diff --git a/.DS_Store b/.DS_Store index fc56969bb..3c164b1a9 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.cargo/audit.toml b/.cargo/audit.toml new file mode 100644 index 000000000..f562d12e0 --- /dev/null +++ b/.cargo/audit.toml @@ -0,0 +1,30 @@ +[advisories] +ignore = [ + # DO NOT ADD ANYTHING TO THIS LIST WITHOUT CAREFUL CONSIDERATION! + + # dotenv being unmaintained is ignored because it is an indirect dependency of cloud-storage, which would be hard to replace. + # In addition, it is most likely not ever going to be on a security-critical path, considering it only parses trusted .env files. + # However, we should probably replace cloud-storage with tame-gcs as soon as possible to remove this ignore. + "RUSTSEC-2021-0141", + + # mach is unmaintained, but seems to be required by wasmtime at its latest version, which we currently cannot do without. + # We should replace it with mach2 in our personal code, but will need to keep it there until wasmtime switches to it. + # Anyway, it cannot be a security liability in production, considering it is bindings to the OS X kernel. + "RUSTSEC-2020-0168", + + # memmap is unmaintained, but is used by wasmer0, which we need to keep alive for replayability reasons. + # We should remove wasmer0 and this ignore as soon as we get limited replayability. + "RUSTSEC-2020-0077", + + # parity-wasm is deprecated, but is used by our runtimes before unc-vm, which we need to keep alive for replayability reasons. + # We should remove them all, as well as this ignore, as soon as we get limited replayability. + "RUSTSEC-2022-0061", + + # borsh is vulnerable, but is used by wasmer0, which we need to keep alive for replayability reasons. + # We should remove it, as well as this ignore, as soon as we get limited replayability. + "RUSTSEC-2023-0033", + + # older versions of parking-lot are vulnerable, but used by wasmer0, which we need to keep alive for replayability reasons. + # We should remove it, as well as this ignore, as soon as we get limited replayability. + "RUSTSEC-2020-0070", +] diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 000000000..5a4d9f339 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,7 @@ +[build] +# We compile with `panic=abort`, so we need `-Cforce-unwind-tables=y` +# to get a useful backtrace on panic. +rustflags = ["-Cforce-unwind-tables=y"] + +[target.'cfg(target_arch = "x86_64")'] +rustflags = ["-Ctarget-feature=+sse4.1,+sse4.2", "-Cforce-unwind-tables=y"] diff --git a/.config/nextest.toml b/.config/nextest.toml new file mode 100644 index 000000000..e449bf728 --- /dev/null +++ b/.config/nextest.toml @@ -0,0 +1,36 @@ +[profile.default] +slow-timeout = { period = "60s", terminate-after = 2, grace-period = "0s" } + +[[profile.default.overrides]] +filter = 'test(test_full_estimator)' +slow-timeout = { period = "10m", terminate-after = 3 } +retries = 0 +threads-required = 2 + +[[profile.default.overrides]] +filter = 'package(style-tests)' +slow-timeout = { period = "120s", terminate-after = 5 } +retries = 0 +threads-required = 4 + +# Unfortunately no support for inheriting profiles yet: +# https://github.com/nextest-rs/nextest/issues/387 +[profile.ci] +slow-timeout = { period = "120s", terminate-after = 5 } +# Try a few times before failing the whole test suite on a potentially spurious tests. +# The hope is that people will fix the spurious tests as they encounter them locally... +retries = { backoff = "fixed", count = 3, delay = "1s" } +failure-output = "final" +fail-fast = false + +[[profile.ci.overrides]] +filter = 'test(test_full_estimator)' +slow-timeout = { period = "10m", terminate-after = 3 } +retries = 0 +threads-required = 2 + +[[profile.ci.overrides]] +filter = 'package(style-tests)' +slow-timeout = { period = "120s", terminate-after = 5 } +retries = 0 +threads-required = 4 diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..48701d105 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +.idea/ +.vscode/ + +docker/ +!docker/scripts/ +ops/ +!ops/run.sh +!ops/tendermint-config.toml + +**/target +storage/ +keystore/ +tmp/ +sandbox/ + +# Ignore Vim swapfiles +*.swp + +# Docker files shouldn't invalidate the cache because Docker itself will use these appropriately +.dockerignore +Dockerfile +Dockerfile.nightly + +# Ignore git internal structure +.git/ diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..214b64ac9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +**/package-lock.json linguist-generated=true -diff +# Disable linguist for WebAssembly files. +# Those are used to test the runtime and should not be indexed. +*.wasm linguist-detectable=false +*.wast linguist-detectable=false +*.wat linguist-detectable=false diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..4ca72b851 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,36 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + + + +**Describe the bug** +Please provide a short description of the bug. + +**To Reproduce** +Steps to reproduce the behavior. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Version (please complete the following information):** +- utility commit/branch +- rust version (if local) +- docker (if using docker) +- mainnet/testnet/local + +**Additional context** +Add any other context about the problem here. diff --git a/.github/PULL_REQUEST_TEMPLATE/copilot_generated.md b/.github/PULL_REQUEST_TEMPLATE/copilot_generated.md new file mode 100644 index 000000000..3cae3853f --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/copilot_generated.md @@ -0,0 +1,10 @@ +### WHAT +copilot:summary +​ +copilot:poem + +### WHY + + +### HOW +copilot:walkthrough diff --git a/.github/PULL_REQUEST_TEMPLATE/feature_stabilization.md b/.github/PULL_REQUEST_TEMPLATE/feature_stabilization.md new file mode 100644 index 000000000..c79ce0a24 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/feature_stabilization.md @@ -0,0 +1,15 @@ +# Feature to stabilize +Describe the protocol feature you want to stabilize in this PR, what it is about, and why do we need it without assuming prior knowledge of the feature. +Feel free to link other pull requests or issues here. + +# Context + +- NEP (if exists): https://github.com/near/NEPs/blob/master/neps/nep-XXXX.md +- Implementation: https://github.com/utnet-org/utility/pull/XXXX + +# Testing and QA +Describe the testing plan for this protocol and why you are confident that it is ready to be stabilized. + +# Checklist +- [ ] Link to nightly nayduck run (`./scripts/nayduck.py`, [docs](https://github.com/utnet-org/utility/blob/master/nightly/README.md#scheduling-a-run)): https://nayduck.near.org/ +- [ ] Update CHANGELOG.md to include this protocol feature in the `Unreleased` section. diff --git a/.github/weekly-digest.yml b/.github/weekly-digest.yml new file mode 100644 index 000000000..af602faff --- /dev/null +++ b/.github/weekly-digest.yml @@ -0,0 +1,8 @@ +# Configuration for weekly-digest - https://github.com/apps/weekly-digest +publishDay: 5 +canPublishIssues: true +canPublishPullRequests: true +canPublishContributors: true +canPublishStargazers: true +canPublishCommits: false + diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml new file mode 100644 index 000000000..f83a02a03 --- /dev/null +++ b/.github/workflows/book.yml @@ -0,0 +1,45 @@ +name: Book + +on: + push: + branches: + - master + pull_request: + paths: + - 'docs/**' + - '.github/workflows/book.yml' + +jobs: + book: + name: Book + runs-on: ubuntu-latest + env: + MDBOOK_VERSION: '0.4.21' + steps: + - uses: actions/checkout@v2 + - name: Install mdbook + run: | + curl -L https://github.com/rust-lang/mdBook/releases/download/v$MDBOOK_VERSION/mdbook-v$MDBOOK_VERSION-x86_64-unknown-linux-gnu.tar.gz | tar xz -C ~/.cargo/bin + - name: Build + run: mdbook build + working-directory: docs + - uses: actions/upload-artifact@v2 + with: + name: book + path: target/book + + deploy: + name: Deploy + runs-on: ubuntu-latest + permissions: + contents: write + needs: book + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + steps: + - uses: actions/download-artifact@v2 + with: + name: book + - uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: . diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..e36254559 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,380 @@ +name: CI + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + pull_request: + merge_group: + +env: + CI_HACKS: 1 + +# BE CAREFUL IF EDITING THIS FILE: +# If you add/remove python tests from here, you should also update `check_pytests.py`’s list of GHA_TESTS +# so that it stays in-sync, to make sure no tests are lost. + +jobs: + cargo_nextest: + name: "Cargo Nextest (${{matrix.name}})" + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - name: Linux + id: linux + cache_id: linux + os: ubuntu-22.04-16core + type: stable + runs_integ_tests: true + - name: Linux Nightly + id: linux-nightly + cache_id: linux + os: ubuntu-22.04-16core + type: nightly + runs_integ_tests: true + - name: MacOS + id: macos + cache_id: macos + os: macos-latest-xlarge + type: stable + runs_integ_tests: false + timeout-minutes: 90 + steps: + - uses: actions/checkout@v4 + + # Install all the required tools + - uses: baptiste0928/cargo-install@21a18ba3bf4a184d1804e8b759930d3471b1c941 + with: + crate: just + - uses: baptiste0928/cargo-install@21a18ba3bf4a184d1804e8b759930d3471b1c941 + with: + crate: cargo-nextest + - uses: baptiste0928/cargo-install@21a18ba3bf4a184d1804e8b759930d3471b1c941 + with: + crate: cargo-deny + - uses: baptiste0928/cargo-install@21a18ba3bf4a184d1804e8b759930d3471b1c941 + with: + crate: cargo-llvm-cov + + # Setup the dependency rust cache and llvm-cov + - uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 + with: + prefix-key: "0" # change this to invalidate CI cache + shared-key: "cargo_nextest-${{ matrix.cache_id }}" + + # Run the tests + - run: just codecov "nextest-unit ${{ matrix.type }}" + - run: mv codecov.json unit-${{ matrix.id }}.json + - run: just codecov "nextest-integration ${{ matrix.type }}" + if: matrix.runs_integ_tests + - run: mv codecov.json integration-${{ matrix.id }}.json + if: matrix.runs_integ_tests + + # Upload the coverage + - uses: actions/upload-artifact@v3 + with: + name: coverage + path: | + unit-${{ matrix.id }}.json + integration-${{ matrix.id }}.json + + protobuf_backward_compat: + name: "Protobuf Backward Compatibility" + runs-on: ubuntu-22.04-8core + steps: + - uses: actions/checkout@v4 + - uses: bufbuild/buf-setup-action@1158f4fa81bc02e1ff62abcca6d516c9e24c77da + - uses: bufbuild/buf-breaking-action@a074e988ee34efcd4927079e79c611f428354c01 + with: + against: "https://github.com/utnet-org/utility.git#${{github.event.pull_request.base.sha && format('ref={0}', github.event.pull_request.base.sha) || 'branch=master' }}" + + py_backward_compat: + name: "Backward Compatibility" + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: 3.11 + cache: pip + - uses: baptiste0928/cargo-install@21a18ba3bf4a184d1804e8b759930d3471b1c941 + with: + crate: cargo-llvm-cov + - uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 + with: + prefix-key: "0" # change this to invalidate CI cache + shared-key: "cargo_nextest-linux" + save-if: "false" # use the cache from nextest, but don’t double-save + - run: pip3 install --user -r pytest/requirements.txt + - run: cargo llvm-cov show-env | grep -v RUSTFLAGS | tr -d "'" >> "$GITHUB_ENV" + - run: echo "RUSTC_WORKSPACE_WRAPPER=$PWD/scripts/rustc-coverage-wrapper.sh" >> "$GITHUB_ENV" + - run: cargo build --locked --profile dev-release -p uncd --bin uncd + - run: echo "CURRENT_NEARD=$PWD/target/dev-release/uncd" >> "$GITHUB_ENV" + - run: cd pytest && python3 tests/sanity/backward_compatible.py + - run: cargo llvm-cov report --profile dev-release --codecov --output-path py-backward-compat.json + - uses: actions/upload-artifact@v3 + with: + name: coverage + path: py-backward-compat.json + + py_db_migration: + name: "Database Migration" + runs-on: ubuntu-22.04-8core + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: 3.11 + cache: pip + - uses: baptiste0928/cargo-install@21a18ba3bf4a184d1804e8b759930d3471b1c941 + with: + crate: cargo-llvm-cov + - uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 + with: + prefix-key: "0" # change this to invalidate CI cache + shared-key: "cargo_nextest-linux" + save-if: "false" # use the cache from nextest, but don’t double-save + - run: pip3 install --user -r pytest/requirements.txt + - run: cargo llvm-cov show-env | grep -v RUSTFLAGS | tr -d "'" >> "$GITHUB_ENV" + - run: echo "RUSTC_WORKSPACE_WRAPPER=$PWD/scripts/rustc-coverage-wrapper.sh" >> "$GITHUB_ENV" + - run: cargo build --locked --profile dev-release -p uncd --bin uncd + - run: echo "CURRENT_NEARD=$PWD/target/dev-release/uncd" >> "$GITHUB_ENV" + - run: echo "unc_ROOT=$PWD" >> "$GITHUB_ENV" + - run: cd pytest && python3 tests/sanity/db_migration.py + - run: cargo llvm-cov report --profile dev-release --codecov --output-path py-db-migration.json + - uses: actions/upload-artifact@v3 + with: + name: coverage + path: py-db-migration.json + + py_sanity_checks: + name: "Sanity Checks" + runs-on: ubuntu-22.04-16core + strategy: + fail-fast: false + timeout-minutes: 90 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: 3.11 + cache: pip + - uses: baptiste0928/cargo-install@21a18ba3bf4a184d1804e8b759930d3471b1c941 + with: + crate: cargo-llvm-cov + - uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 + with: + prefix-key: "0" # change this to invalidate CI cache + shared-key: "cargo_nextest-linux" + save-if: "false" # use the cache from nextest, but don’t double-save + - run: pip3 install --user -r pytest/requirements.txt + # This is the only job that uses `--features nightly` so we build this in-line instead of a + # separate job like done with the regular uncd. + - run: cargo llvm-cov show-env | grep -v RUSTFLAGS | tr -d "'" >> "$GITHUB_ENV" + - run: echo "RUSTC_WORKSPACE_WRAPPER=$PWD/scripts/rustc-coverage-wrapper.sh" >> "$GITHUB_ENV" + - run: cargo build --profile dev-release -p uncd --bin uncd --features nightly + # Note: We're not running spin_up_cluster.py for non-nightly + # because spinning up non-nightly clusters is already covered + # by other steps in the CI, e.g. upgradable. + - run: python3 pytest/tests/sanity/spin_up_cluster.py + env: + unc_ROOT: "target/dev-release" + - run: cargo llvm-cov report --profile dev-release --codecov --output-path py-sanity-checks.json + - uses: actions/upload-artifact@v3 + with: + name: coverage + path: py-sanity-checks.json + + py_genesis_check: + name: "Genesis Changes" + runs-on: ubuntu-22.04-8core + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: 3.11 + cache: pip + - uses: baptiste0928/cargo-install@21a18ba3bf4a184d1804e8b759930d3471b1c941 + with: + crate: cargo-llvm-cov + - uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 + with: + prefix-key: "0" # change this to invalidate CI cache + shared-key: "cargo_nextest-linux" + save-if: "false" # use the cache from nextest, but don’t double-save + - run: pip3 install --user -r pytest/requirements.txt + - run: cargo llvm-cov show-env | grep -v RUSTFLAGS | tr -d "'" >> "$GITHUB_ENV" + - run: echo "RUSTC_WORKSPACE_WRAPPER=$PWD/scripts/rustc-coverage-wrapper.sh" >> "$GITHUB_ENV" + - run: cargo build --locked --profile dev-release -p uncd --bin uncd + - run: echo "CURRENT_NEARD=$PWD/target/dev-release/uncd" >> "$GITHUB_ENV" + - run: python3 scripts/state/update_res.py check + - run: cargo llvm-cov report --profile dev-release --codecov --output-path py-genesis-check.json + - uses: actions/upload-artifact@v3 + with: + name: coverage + path: py-genesis-check.json + + py_style_check: + name: "Style" + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: baptiste0928/cargo-install@21a18ba3bf4a184d1804e8b759930d3471b1c941 + with: + crate: just + - uses: actions/setup-python@v4 + with: + python-version: 3.11 + cache: pip + - run: pip3 install --user -r pytest/requirements.txt + - run: just python-style-checks + + py_upgradability: + name: "Upgradability" + runs-on: ubuntu-22.04-8core + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: 3.11 + cache: pip + - uses: baptiste0928/cargo-install@21a18ba3bf4a184d1804e8b759930d3471b1c941 + with: + crate: cargo-llvm-cov + - uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 + with: + prefix-key: "0" # change this to invalidate CI cache + shared-key: "cargo_nextest-linux" + save-if: "false" # use the cache from nextest, but don’t double-save + - run: pip3 install --user -r pytest/requirements.txt + - run: cargo llvm-cov show-env | grep -v RUSTFLAGS | tr -d "'" >> "$GITHUB_ENV" + - run: echo "RUSTC_WORKSPACE_WRAPPER=$PWD/scripts/rustc-coverage-wrapper.sh" >> "$GITHUB_ENV" + - run: cargo build --locked --profile dev-release -p uncd --bin uncd + - run: echo "CURRENT_NEARD=$PWD/target/dev-release/uncd" >> "$GITHUB_ENV" + - run: cd pytest && python3 tests/sanity/upgradable.py + - run: cargo llvm-cov report --profile dev-release --codecov --output-path py-upgradability.json + - uses: actions/upload-artifact@v3 + with: + name: coverage + path: py-upgradability.json + + rpc_error_schema: + name: "RPC Schema" + runs-on: ubuntu-22.04-8core + steps: + - uses: actions/checkout@v4 + - uses: Swatinem/rust-cache@a95ba195448af2da9b00fb742d14ffaaf3c21f43 + with: + prefix-key: "0" # change this to invalidate CI cache + shared-key: "cargo_nextest-linux" + save-if: "false" # use the cache from nextest, but don’t double-save + - uses: baptiste0928/cargo-install@21a18ba3bf4a184d1804e8b759930d3471b1c941 + with: + crate: just + - run: just check-rpc-errors-schema + + lychee_checks: + name: "Lychee Lints" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: lycheeverse/lychee-action@2ac9f030ccdea0033e2510a23a67da2a2da98492 + with: + fail: true + + cargo_audit: + name: "Cargo Audit" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: baptiste0928/cargo-install@21a18ba3bf4a184d1804e8b759930d3471b1c941 + with: + crate: cargo-audit + - run: cargo audit -D warnings + + upload_coverage: + name: "Upload Coverage" + runs-on: ubuntu-latest + needs: + - cargo_nextest + - py_backward_compat + - py_db_migration + - py_sanity_checks + - py_genesis_check + - py_upgradability + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v3 + with: + name: coverage + # Keep the number of uploads here in sync with codecov.yml’s after_n_build value + # codecov will send a comment only after having receidev this number of uploads. + - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: unit-linux.json + fail_ci_if_error: true + flags: unittests,linux + - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: unit-linux-nightly.json + fail_ci_if_error: true + flags: unittests,linux-nightly + - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: unit-macos.json + fail_ci_if_error: true + flags: unittests,macos + - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: integration-linux.json + fail_ci_if_error: true + flags: integration-tests,linux + - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: integration-linux-nightly.json + fail_ci_if_error: true + flags: integration-tests,linux-nightly + # - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d + # with: + # token: ${{ secrets.CODECOV_TOKEN }} + # files: integration-macos.json + # fail_ci_if_error: true + # flags: integration-tests,macos + - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: py-backward-compat.json + fail_ci_if_error: true + flags: pytests,backward-compatibility,linux + - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: py-db-migration.json + fail_ci_if_error: true + flags: pytests,db-migration,linux + - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: py-sanity-checks.json + fail_ci_if_error: true + flags: pytests,sanity-checks,linux + - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: py-genesis-check.json + fail_ci_if_error: true + flags: pytests,genesis-check,linux + - uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: py-upgradability.json + fail_ci_if_error: true + flags: pytests,upgradability,linux diff --git a/.github/workflows/issue-metrics.yml b/.github/workflows/issue-metrics.yml new file mode 100644 index 000000000..81be7bbe9 --- /dev/null +++ b/.github/workflows/issue-metrics.yml @@ -0,0 +1,42 @@ +name: Monthly issue metrics + +on: + workflow_dispatch: + schedule: + - cron: '3 2 1 * *' + +permissions: + issues: write + pull-requests: read + +jobs: + monthly-issue-metrics: + name: past month issue metrics + runs-on: ubuntu-latest + steps: + - name: Get dates for last month + shell: bash + id: last-month + run: | + # Calculate the first day of the previous month + first_day=$(date -d "last month" +%Y-%m-01) + + # Calculate the last day of the previous month + last_day=$(date -d "$first_day +1 month -1 day" +%Y-%m-%d) + + #Set an environment variable with the date range + echo "$first_day..$last_day" + echo "LAST_MONTH=$first_day..$last_day" >> $GITHUB_OUTPUT + + - name: Run issue-metrics tool + uses: github/issue-metrics@v2 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SEARCH_QUERY: 'repo:utnet-org/utility is:issue created:${{ steps.last-month.outputs.LAST_MONTH }}' + + - name: Create issue + uses: peter-evans/create-issue-from-file@v4 + with: + title: Monthly issue metrics report + token: ${{ secrets.GITHUB_TOKEN }} + content-filepath: ./issue_metrics.md diff --git a/.github/workflows/mac_binary.yml b/.github/workflows/mac_binary.yml new file mode 100644 index 000000000..c2df74dc0 --- /dev/null +++ b/.github/workflows/mac_binary.yml @@ -0,0 +1,26 @@ +name: MacOS binary release +on: + workflow_dispatch: + push: + branches: + - master + tags: + - '[0-9]+.[0-9]+.[0-9]+*' +jobs: + build_binary: + runs-on: macOS-latest + + steps: + - uses: hecrj/setup-rust-action@v1 + with: + rust-version: nightly-2020-03-19 + - uses: actions/checkout@master + - uses: isbang/setup-awscli@v0.1.0 + - run: scripts/mac-release.sh + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + - run: scripts/mac-release.sh nightly-release + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/.github/workflows/mac_m1_binary.yml b/.github/workflows/mac_m1_binary.yml new file mode 100644 index 000000000..2778f3d1b --- /dev/null +++ b/.github/workflows/mac_m1_binary.yml @@ -0,0 +1,23 @@ +name: MacOS-m1 binary release +on: + workflow_dispatch: + push: + branches: + - master + tags: + - '[0-9]+.[0-9]+.[0-9]+*' +jobs: + build_binary: + runs-on: macos-latest-xlarge + + steps: + - uses: actions/checkout@master + - uses: isbang/setup-awscli@v0.1.0 + - run: scripts/mac-release.sh + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + - run: scripts/mac-release.sh nightly-release + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/.github/workflows/master_fuzzer_binaries.yml b/.github/workflows/master_fuzzer_binaries.yml new file mode 100644 index 000000000..4ed29701b --- /dev/null +++ b/.github/workflows/master_fuzzer_binaries.yml @@ -0,0 +1,54 @@ +name: Build fuzz targets from master +on: + push: + branches: + - master + +jobs: + build_fuzzers: + name: Build Fuzzers + runs-on: "ubuntu-20.04-32core" + + permissions: + contents: "read" + id-token: "write" + + steps: + - run: sudo fallocate -l 128G /swap-file + - run: sudo chmod 600 /swap-file + - run: sudo mkswap /swap-file + - run: sudo swapon /swap-file + + - id: "auth" + uses: "google-github-actions/auth@v1" + with: + workload_identity_provider: "projects/968400232856/locations/global/workloadIdentityPools/project-identity-pool/providers/github-provider" + service_account: "unc-fuzzer-service-account@unc-fuzzer.iam.gserviceaccount.com" + + - name: Installing nightly rust + run: | + rustup install nightly + rustup default nightly + + - uses: baptiste0928/cargo-install@21a18ba3bf4a184d1804e8b759930d3471b1c941 + with: + crate: cargo-bolero + # TODO: remove the below once https://github.com/camshaft/bolero/pull/195 is released on crates.io + # and https://github.com/camshaft/bolero/pull/196 has a proper fix + git: https://github.com/Ekleog-NEAR/bolero + rev: 56da8e6d1d018519a30b36d85d3a53fe35a42eaf + + - run: rustup target add --toolchain nightly wasm32-unknown-unknown + + - name: "Set up GCP SDK" + uses: "google-github-actions/setup-gcloud@v1" + with: + version: ">= 416.0.0" + + - uses: actions/checkout@master + + - name: "Compile fuzzers and upload to GCS" + run: | + NAME="framework-${{ github.ref_name }}-$(env TZ=Etc/UTC date +"%Y%m%d%H%M%S")" + RUSTFLAGS="-A warnings --cfg fuzz" cargo +nightly bolero build-clusterfuzz --all-features --profile fuzz + gsutil cp -Z target/fuzz/clusterfuzz.tar "gs://fuzzer_targets/${{ github.ref_name }}/$NAME.tar.gz" diff --git a/.github/workflows/nightly_nayduck.yml b/.github/workflows/nightly_nayduck.yml new file mode 100644 index 000000000..f692428f1 --- /dev/null +++ b/.github/workflows/nightly_nayduck.yml @@ -0,0 +1,21 @@ +name: Nightly Nayduck tests check +on: + merge_group: + +jobs: + nightly_nayduck_tests: + runs-on: "ubuntu-latest" + timeout-minutes: 10 + + steps: + - name: Install JQ json processor + run: sudo apt install jq + + # In this step we get the latest nightly results from the nayduck server + # and check if there are any non-passing tests + - name: Check if there are any non-passing tests + run: | + NIGHTLY_RESULTS=$(curl -s https://nayduck.near.org/api/nightly-events) + UNSUCCESSFUL_TESTS=$(jq -e '.tests | .[][] | select(.[2] != "PASSED" ) ' <<< ${NIGHTLY_RESULTS} ) + if [ -z "$UNSUCCESSFUL_TESTS" ] ; then echo "Nightly Nayduck tests OK"; \ + else echo "Nightly Nayduck tests are failing" && exit 1; fi diff --git a/.github/workflows/ondemand_fuzzer_binaries.yml b/.github/workflows/ondemand_fuzzer_binaries.yml new file mode 100644 index 000000000..557eaf6e7 --- /dev/null +++ b/.github/workflows/ondemand_fuzzer_binaries.yml @@ -0,0 +1,90 @@ +name: Build on demand fuzz targets +on: + # Run when a new release or rc is created + release: + types: [released, prereleased] + + # Run on-demand + workflow_dispatch: + inputs: + branch_type: + type: choice + required: true + options: + - master + - release + - rc + description: Type of branch to build fuzz targets + branch_ref: + type: string + required: true + description: Branch name or tag to build from + +jobs: + build_fuzzers: + name: Build Fuzzers + runs-on: "ubuntu-20.04-32core" + + permissions: + contents: "read" + id-token: "write" + + steps: + - run: sudo fallocate -l 128G /swap-file + - run: sudo chmod 600 /swap-file + - run: sudo mkswap /swap-file + - run: sudo swapon /swap-file + + - id: "auth" + uses: "google-github-actions/auth@v1" + with: + workload_identity_provider: "projects/968400232856/locations/global/workloadIdentityPools/project-identity-pool/providers/github-provider" + service_account: "unc-fuzzer-service-account@unc-fuzzer.iam.gserviceaccount.com" + + - name: Installing nightly rust + run: | + rustup install nightly + rustup default nightly + + - uses: baptiste0928/cargo-install@21a18ba3bf4a184d1804e8b759930d3471b1c941 + with: + crate: cargo-bolero + # TODO: remove the below once https://github.com/camshaft/bolero/pull/195 is released on crates.io + # and https://github.com/camshaft/bolero/pull/196 has a proper fix + git: https://github.com/Ekleog-NEAR/bolero + rev: 56da8e6d1d018519a30b36d85d3a53fe35a42eaf + + - run: rustup target add --toolchain nightly wasm32-unknown-unknown + + - name: "Set up GCP SDK" + uses: "google-github-actions/setup-gcloud@v1" + with: + version: ">= 416.0.0" + + - name: Checkout Release/RC branch + if: contains(fromJSON('["released", "prereleased"]'), github.event.action) + uses: actions/checkout@master + + - name: Checkout ${{ github.event.inputs.branch_ref }} branch + if: ${{ github.event_name == 'workflow_dispatch'}} + uses: actions/checkout@master + with: + ref: ${{ github.event.inputs.branch_ref }} + + - name: Build Release branch fuzz targets + if: ${{ github.event.action == 'released'}} + run: echo "branch_type=release" >> "$GITHUB_ENV" + + - name: Build RC branch fuzz targets + if: ${{ github.event.action == 'prereleased'}} + run: echo "branch_type=rc" >> "$GITHUB_ENV" + + - name: Build fuzz targets from ${{ github.event.inputs.branch_ref }}" branch + if: ${{ github.event_name == 'workflow_dispatch'}} + run: echo "branch_type=${{ github.event.inputs.branch_type }}" >> "$GITHUB_ENV" + + - name: "Compile fuzzers and upload to GCS" + run: | + NAME="framework-$branch_type-$(env TZ=Etc/UTC date +"%Y%m%d%H%M%S")" + RUSTFLAGS="-A warnings --cfg fuzz" cargo +nightly bolero build-clusterfuzz --all-features --profile fuzz + gsutil cp -Z target/fuzz/clusterfuzz.tar "gs://fuzzer_targets/$branch_type/$NAME.tar.gz" diff --git a/.github/workflows/unc_crates_publish.yml b/.github/workflows/unc_crates_publish.yml new file mode 100644 index 000000000..b85548113 --- /dev/null +++ b/.github/workflows/unc_crates_publish.yml @@ -0,0 +1,41 @@ +name: Unc Crates Publish + +on: + schedule: + - cron: '*/30 * * * *' + workflow_dispatch: + +jobs: + publish-cargo-crates: + name: "Publish unc-workspaces on crates.io https://crates.io/crates/unc-workspaces" + runs-on: "ubuntu-22.04-2core" + #environment: deploy + permissions: + contents: write # required for crates push + timeout-minutes: 30 + + steps: + - name: Checkout utnet-org/utility's develop branch + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: develop + - name: Set up git user + uses: fregante/setup-git-user@v2 + - name: Publish unc-workspaces on crates.io + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: | + set -x + cargo install --git https://github.com/miraclx/cargo-workspaces --tag v0.3.0 cargo-workspaces + cargo ws publish --yes --allow-dirty --force '*' \ + --no-git-commit --no-git-push --no-individual-tags --tag-prefix 'crates-' \ + --tag-msg $$'crates.io snapshot\n---%{\n- %n - https://crates.io/crates/%n/%v}' + + - name: Create tag on https://github.com/utnet-org/utility + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git push --no-follow-tags https://github.com/utnet-org/utility.git tag 'crates-*' + + diff --git a/.github/workflows/uncd_assertion_binary.yml b/.github/workflows/uncd_assertion_binary.yml new file mode 100644 index 000000000..4bbb293a6 --- /dev/null +++ b/.github/workflows/uncd_assertion_binary.yml @@ -0,0 +1,47 @@ +name: Uncd Assertion binary release + +on: + schedule: + - cron: '23 */8 * * *' + + workflow_dispatch: + inputs: + branch: + default: 'master' + description: "Nearcore branch to build and publish" + type: string + required: true + +jobs: + binary-release: + name: "Build and publish uncd binary" + runs-on: "ubuntu-20.04-16core" + #environment: deploy + permissions: + id-token: write # required to use OIDC authentication + + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::754641474505:role/GitHubActionsRunner + aws-region: us-west-1 + + - name: Checkout ${{ github.event.inputs.branch }} branch + if: ${{ github.event_name == 'workflow_dispatch'}} + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.branch }} + + - name: Checkout framework repository + if: ${{ github.event_name != 'workflow_dispatch'}} + uses: actions/checkout@v4 + + - name: Uncd binary build and upload to S3 + run: ./scripts/binary_release.sh assertions-release + + - name: Update latest version metadata in S3 + run: | + echo $(git rev-parse HEAD) > latest + BRANCH=$(git branch --show-current) + aws s3 cp --acl public-read latest s3://build.nearprotocol.com/framework/$(uname)/${BRANCH}/latest-assertions diff --git a/.github/workflows/uncd_nightly_binary.yml b/.github/workflows/uncd_nightly_binary.yml new file mode 100644 index 000000000..0ddb33769 --- /dev/null +++ b/.github/workflows/uncd_nightly_binary.yml @@ -0,0 +1,40 @@ +name: Uncd Nightly binary release + +on: + workflow_dispatch: + inputs: + branch: + default: 'master' + description: "Nearcore branch to build and publish" + type: string + required: true + +jobs: + binary-release: + name: "Build and publish uncd binary" + runs-on: "ubuntu-20.04-16core" + #environment: deploy + permissions: + id-token: write # required to use OIDC authentication + + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::754641474505:role/GitHubActionsRunner + aws-region: us-west-1 + + - name: Checkout ${{ github.event.inputs.branch }} branch + if: ${{ github.event_name == 'workflow_dispatch'}} + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.branch }} + + - name: Uncd binary build and upload to S3 + run: ./scripts/binary_release.sh nightly-release + + - name: Update latest version metadata in S3 + run: | + echo $(git rev-parse HEAD) > latest + BRANCH=$(git branch --show-current) + aws s3 cp --acl public-read latest s3://build.nearprotocol.com/framework/$(uname)/${BRANCH}/latest-nightly diff --git a/.github/workflows/uncd_release.yml b/.github/workflows/uncd_release.yml new file mode 100644 index 000000000..0d66e1fbf --- /dev/null +++ b/.github/workflows/uncd_release.yml @@ -0,0 +1,88 @@ +name: Uncd binary and Docker image release + +on: + # Run when a new release or rc is created + release: + types: [released, prereleased] + push: + branches: master + + workflow_dispatch: + inputs: + branch: + default: 'master' + description: "Nearcore branch to build and publish" + type: string + required: true + +jobs: + binary-release: + name: "Build and publish uncd binary" + runs-on: "ubuntu-20.04-16core" + #environment: deploy + permissions: + id-token: write # required to use OIDC authentication + + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::754641474505:role/GitHubActionsRunner + aws-region: us-west-1 + + - name: Checkout ${{ github.event.inputs.branch }} branch + if: ${{ github.event_name == 'workflow_dispatch'}} + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.branch }} + + - name: Checkout unc repository + if: ${{ github.event_name != 'workflow_dispatch'}} + uses: actions/checkout@v4 + + - name: Uncd binary build and upload to S3 + run: ./scripts/binary_release.sh + + - name: Update latest version metadata in S3 + run: | + echo $(git rev-parse HEAD) > latest + BRANCH=$(git branch --show-current) + aws s3 cp --acl public-read latest s3://build.nearprotocol.com/unc/$(uname)/${BRANCH}/latest + + docker-release: + name: "Build and publish unc Docker image" + runs-on: "ubuntu-20.04-16core" + #environment: deploy + steps: + - name: Checkout ${{ github.event.inputs.branch }} branch + if: ${{ github.event_name == 'workflow_dispatch'}} + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.branch }} + + - name: Checkout unc repository + if: ${{ github.event_name != 'workflow_dispatch'}} + uses: actions/checkout@v4 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKER_PAT_TOKEN }} + + - name: Build and push Docker image to Dockerhub + run: | + COMMIT=$(git rev-parse HEAD) + BRANCH=${{ github.ref_name }} + make docker-unc + docker tag unc utnet-org/utility:${BRANCH}-${COMMIT} + docker tag unc utnet-org/utility:${BRANCH} + + docker push utnet-org/utility:${BRANCH}-${COMMIT} + docker push utnet-org/utility:${BRANCH} + + if [[ ${BRANCH} == "master" ]]; + then + docker tag unc utnet-org/utility:latest + docker push utnet-org/utility:latest + fi \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..a931f3010 --- /dev/null +++ b/.gitignore @@ -0,0 +1,65 @@ +# Generated by Cargo +# will have compiled files and executables +target +/target_expensive/ +/sandbox +docker-build +/conf/grafana-dashboard-main-testnet.json +/storage/ +/test-utils/node/storage +/test-utils/ganache/storage +/node/service/storage +/keystore/ +/chain/client/tmp_bench +testdir/ +my_script.sh +# These are backup files generated by rustfmt +**/*.rs.bk + +**/.ipynb_checkpoints/* + +# Editor config files +.vscode +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 +.idea + +# Python env +.env +*.pyc +venv + +# Node +**/node_modules/ + +# Integration tests +create-unc-app/ +unc-api-js/ +nearlib_release_test/ +test-keys/ +tmp/ + +# OS X +.DS_Store + +# Logs +*.log + +# Vim tmp files +*.swp +rusty-tags.vi + +# testnet genesis +/framework/res/testnet.json +/framework/res/testnet_genesis_records*.json + +# logs from mocknet nodes +/pytest/logs/ + +# Estimator generated files +costs-*.txt +names-to-stats.txt +data_dump_*.bin! +*_key.json + + diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile new file mode 100644 index 000000000..f924eb1eb --- /dev/null +++ b/.gitpod.Dockerfile @@ -0,0 +1,4 @@ +FROM gitpod/workspace-full + +RUN git clone https://github.com/unet-org/utility.git --depth 1 /home/gitpod/utility +RUN bash -cl "cd /home/gitpod/utility && cargo build && cargo test" diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 000000000..ea1eca7fe --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,8 @@ +tasks: + - init: cp -R /home/gitpod/utility/target ./target && cargo build && cargo test +image: + file: .gitpod.Dockerfile +github: + prebuilds: + addCheck: true + addComment: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..caacff25e --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,4 @@ +- repo: https://github.com/doublify/pre-commit-rust + rev: master + hooks: + - id: fmt \ No newline at end of file diff --git a/.style.yapf b/.style.yapf new file mode 100644 index 000000000..f34c28059 --- /dev/null +++ b/.style.yapf @@ -0,0 +1,4 @@ +[style] +based_on_style = google +indent_width = 4 +column_limit = 80 diff --git a/ATTRIBUTIONS.md b/ATTRIBUTIONS.md new file mode 100644 index 000000000..eef269417 --- /dev/null +++ b/ATTRIBUTIONS.md @@ -0,0 +1,1073 @@ +# UNC Client Attributions + +We have taken inspiration and few pieces of code from: + * [OpenEthereum](https://github.com/openethereum/openethereum) + * [Parity Substrate](https://github.com/paritytech/substrate) + * [Parity Trie](https://github.com/paritytech/trie) + * [Grin](https://github.com/mimblewimble/grin/) + * [Near](https://github.com/near/nearcore) + +## Licenses + +### OpenEthereum, Parity Substrate + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + +### Parity Trie + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +### Grin + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +### Near + + END OF TERMS AND CONDITIONS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 000000000..4a025b352 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,3 @@ +# CODEOWNERS: https://help.github.com/articles/about-codeowners/ + +* @utnet-org/utility-codeowners diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..1bdba600b --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at social@unc.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..d84d7c589 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,163 @@ +Thank you for your interest in contributing to the NEAR reference client! We +welcome contributions from everyone. Below are various bits of information to +help you get started. If you require additional help, please reach out to us on +our [zulip channel](https://unc.zulipchat.com/). + +## Quick Start + +framework is a fairly standard Rust project, so building is as easy as + +```console +$ cargo build +``` + +Building framework requires a fairly recent Rust compiler (get it +[here](https://rustup.rs)), as well as `clang` and `cmake` to build RocksDB +(`sudo apt install cmake clang`). + +Sadly at the moment framework is only compatible with Linux and MacOS, Windows is +not supported yet. + +To run a local NEAR network with one node, use + +```console +$ cargo run -p uncd -- init # generates various configs in ~/.unc +$ cargo run -p uncd -- run +``` + +You can now use your own node's HTTP RPC API (e.g. +[httpie](https://httpie.io/docs/cli/installation)) + +```console +$ http get http://localhost:3030/status +$ http post http://localhost:3030/ method=query jsonrpc=2.0 id=1 \ + params:='{"request_type": "view_account", "finality": "final", "account_id": "test.unc"}' +``` + +The RPC is documented [here](https://docs.unc.org/api/rpc/introduction), and +can be conveniently accessed from the command line [NEAR +CLI](https://docs.unc.org/tools/unc-cli) utility. + +## Next Steps + +To learn more about how framework works, skim through our guide to framework +development: + +https://unc.github.io/framework/ + +If you are looking for relatively simple tasks to familiarise yourself with +`framework`, please check out issues labeled with the `C-good-first-issue` label +[here](https://github.com/utnet-org/utility/labels/C-good-first-issue). If you see +one that looks interesting and is unassigned or has not been actively worked on +in some time, please ask to have the issue assigned to you and someone from +the team should help you get started. We do not always keep the issue tracker +up-to-date, so if you do not find an interesting task to work on, please ask for +help on our zulip channel. + +If you have an idea for an enhancement to the protocol itself, please make a +proposal by following the [NEAR Enhancement +Proposal](https://github.com/unc/NEPs/blob/master/neps/nep-0001.md) process. + +## Pull Requests + +All the contributions to `framework` happen via Pull Requests. Please follow the +following steps when creating a PR: + +1. Fork the `framework` repository and create a new branch there to do your work. +2. The branch can contain any number of commits. When merged, all commits will + be squashed into a single commit. +3. The changes should be thoroughly tested. Please refer to [this + document](https://github.com/utnet-org/utility/blob/master/docs/practices/testing/README.md) + for our testing guidelines and an overview of the testing infrastructure. +4. When ready, send a pull request against the `master` branch of the `framework` + repository. +5. Feel free to submit draft PRs to get early feedback and to make sure you are + on the right track. +6. The PR name should follow the template: `: `. Where `type` is: + - `fix` for bug fixes; + - `feat` for new features; + - `refactor` for changes that reorganize code without adding new content; + - `doc` for changes that change documentation or comments; + - `test` for changes that introduce new tests; + - `chore` for grunt tasks like updating dependencies. +7. The PR should also contain a description when appropriate to provide + additional information to help the reviewer inspect the proposed change. +8. If your PR introduces a user-observable change (e.g. a new protocol feature, + new configuration option, new Prometheus metric etc.) please document it in + [CHANGELOG.md](CHANGELOG.md) in the `[unreleased]` section. +9. It is important to select the ` Allow edits and access to secrets by + maintainers` checkbox on the PR. Without this option, the merge bot will not + have sufficient rights to be able to merge the PR when it is approved. It + also allows the maintainers to make trivial changes to the PR as necessary. + Please see + [these](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) + [links](https://stackoverflow.com/questions/63341296/github-pull-request-allow-edits-by-maintainers) + for the implications of selecting the checkbox. + +## After the PR is submitted + +1. We have a CI process configured to run various tests on each PR. All tests +need to pass before a PR can be merged. +2. When all the comments from the reviewer(s) have been addressed, they should +approve the PR allowing a PR to be merged. +3. Before merging a PR, the code should be reviewed properly. In particular, the +person who clicks "Merge when ready" has specific duties, exposed in the last +paragraph of the "Code review process" section below. +4. An approved PR can be merged by clicking the "Merge when ready" button. The +button can be clicked by the author if they have the appropriate access, or by a +reviewer otherwise. PR authors can also click the button immediately after filing +a PR; removing an additional round-trip after the PR gets approved. The PR author +will be notified by email by github if the PR fails to land, once it has entered +the merge queue (ie. after it has passed PR CI and gotten an approving review). + +## Code review process + +We have two groups of code reviewers: Super owners and normal owners. When a +PR is created: + +- a super owner will be automatically assigned to review. +- they may choose to review the PR themselves or they may delegate to someone else +who belongs either to the super owners or the normal owners group. +- the delegate will perform the review and as needed engage other reviewers as +well. They will review your tests, and make sure that they can convince +themselves the test coverage is adequate before they even look into the +change, so make sure you tested all the corner cases. +- it is normal to sometimes require multiple rounds of reviews to get a PR + merged. If your PR received some feedback from a reviewer, use the [github + UI](https://stackoverflow.com/questions/40893008/how-to-resume-review-process-after-updating-pull-request-at-github) + to re-request a review. + +The author is also free to directly request reviews from specific persons +[through the github +ui](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/requesting-a-pull-request-review). +In this case, the automatically selected super owner will ensure that the +selected reviewer is sufficient or additional reviewers are needed. + +The process for becoming a code reviewer is relatively straightforward. +The candidate should have a good understanding of the codebase and then +the existing super owners will discuss and approve the addition. These +discussions take place on zulip so if you are interested in becoming a +code reviewer, please reach out to us there. + +The person who clicks the "Merge when ready" button is the one who +guarantees that no unreviewed code was added between the approving review +and the commit that is being landed. They must be especially careful of +commits that might have happened before the review, but not have been +reviewed (eg. because the review was already in progress), as they will +show above the review line on the GitHub UI. In addition, not refreshing +the page might lead to these commits just not displaying, so the reviewer +should refresh the page both before and after clicking "Merge when ready," +and confirm that the commits are as expected. + +## Release Schedule + +Once your change ends up in master, it will be released with the rest of the +changes by other contributors on the regular release schedules. + +On [betanet](https://docs.unc.org/docs/concepts/networks#testnet) we run +nightly build from master with all the nightly protocol features enabled. Every +five weeks, we stabilize some protocol features and make a release candidate for +testnet. The process for feature stabilization can be found in [this +document](docs/practices/protocol_upgrade.md). After the release candidate has been +running on testnet for four weeks and no issues are observed, we stabilize and +publish the release for mainnet. diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..3b50c8a89 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,8775 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb72882332b6d6282f428b77ba0358cb2687e61a6f6df6a6d3871e8a177c2d4f" +dependencies = [ + "actix-macros", + "actix-rt", + "actix_derive", + "bitflags 2.4.2", + "bytes", + "crossbeam-channel", + "futures-core", + "futures-sink", + "futures-task", + "futures-util", + "log", + "once_cell", + "parking_lot 0.12.1", + "pin-project-lite", + "smallvec", + "tokio", + "tokio-util 0.7.10", +] + +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags 2.4.2", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util 0.7.10", + "tracing", +] + +[[package]] +name = "actix-cors" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0346d8c1f762b41b458ed3145eea914966bb9ad20b9be0d6d463b20d45586370" +dependencies = [ + "actix-utils", + "actix-web", + "derive_more", + "futures-util", + "log", + "once_cell", + "smallvec", +] + +[[package]] +name = "actix-http" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d223b13fd481fc0d1f83bb12659ae774d9e3601814c68a0bc539731698cca743" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash 0.8.11", + "base64 0.21.7", + "bitflags 2.4.2", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2", + "http 0.2.12", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand 0.8.5", + "sha1", + "smallvec", + "tokio", + "tokio-util 0.7.10", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn 2.0.52", +] + +[[package]] +name = "actix-router" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22475596539443685426b6bdadb926ad0ecaefdfc5fb05e5e3441f15463c511" +dependencies = [ + "bytestring", + "http 0.2.12", + "regex", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" +dependencies = [ + "actix-macros", + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb13e7eef0423ea6eab0e59f6c72e7cb46d33691ad56a726b3cd07ddec2c2d4" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2 0.5.6", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-tls" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4cce60a2f2b477bc72e5cde0af1812a6e82d8fd85b5570a5dcf2a5bf2c5be5f" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "http 0.2.12", + "http 1.1.0", + "impl-more", + "openssl", + "pin-project-lite", + "tokio", + "tokio-openssl", + "tokio-util 0.7.10", + "tracing", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a6556ddebb638c2358714d853257ed226ece6023ef9364f23f0c70737ea984" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash 0.8.11", + "bytes", + "bytestring", + "cfg-if 1.0.0", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2 0.5.6", + "time", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1f50ebbb30eca122b188319a4398b3f7bb4a8cdf50ecfb73bfc6a3c3ce54f5" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "actix_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c7db3d5a9718568e4cf4a537cfd7070e6e6ff7481510d0237fb529ac850f6d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.12", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if 1.0.0", + "getrandom 0.2.12", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arc-swap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3d0060af21e8d11a926981cc00c6c1541aa91dd64b9f881985c3da1094425f" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "async-recursion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "attohttpc" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "262c3f7f5d61249d8c00e5546e2685cd15ebeeb1bc0f3cc5449350a1cb07319e" +dependencies = [ + "http 0.2.12", + "log", + "native-tls", + "openssl", + "serde", + "serde_json", + "url", + "wildmatch", +] + +[[package]] +name = "autocfg" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "awc" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c09cc97310b926f01621faee652f3d1b0962545a3cec6c9ac07def9ea36c2c" +dependencies = [ + "actix-codec", + "actix-http", + "actix-rt", + "actix-service", + "actix-tls", + "actix-utils", + "base64 0.21.7", + "bytes", + "cfg-if 1.0.0", + "cookie", + "derive_more", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "itoa", + "log", + "mime", + "openssl", + "percent-encoding", + "pin-project-lite", + "rand 0.8.5", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", +] + +[[package]] +name = "aws-creds" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeeee1a5defa63cba39097a510dfe63ef53658fc8995202a610f6a8a4d03639" +dependencies = [ + "attohttpc", + "dirs", + "rust-ini", + "serde", + "serde-xml-rs", + "thiserror", + "time", + "url", +] + +[[package]] +name = "aws-region" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42fed2b9fca70f2908268d057a607f2a906f47edbf856ea8587de9038d264e22" +dependencies = [ + "thiserror", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "basic-toml" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5" +dependencies = [ + "serde", +] + +[[package]] +name = "bencher" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfdb4953a096c551ce9ace855a604d702e6e62d77fac690575ae347571717f5" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.52", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" +dependencies = [ + "crypto-mac", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "blake3" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b64485778c4f16a6a5a9d335e80d449ac6c70cdd6a06d2af18a6f6f775a125b3" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if 0.1.10", + "constant_time_eq", + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block_on_proc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b872f3528eeeb4370ee73b51194dc1cd93680c2d0eb6c7a223889038d2c1a167" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bolero" +version = "0.10.0" +source = "git+https://github.com/Ekleog-NEAR/bolero?rev=56da8e6d1d018519a30b36d85d3a53fe35a42eaf#56da8e6d1d018519a30b36d85d3a53fe35a42eaf" +dependencies = [ + "bolero-afl", + "bolero-engine", + "bolero-generator", + "bolero-honggfuzz", + "bolero-kani", + "bolero-libfuzzer", + "cfg-if 1.0.0", + "rand 0.8.5", +] + +[[package]] +name = "bolero-afl" +version = "0.10.0" +source = "git+https://github.com/Ekleog-NEAR/bolero?rev=56da8e6d1d018519a30b36d85d3a53fe35a42eaf#56da8e6d1d018519a30b36d85d3a53fe35a42eaf" +dependencies = [ + "bolero-engine", + "cc", +] + +[[package]] +name = "bolero-engine" +version = "0.10.0" +source = "git+https://github.com/Ekleog-NEAR/bolero?rev=56da8e6d1d018519a30b36d85d3a53fe35a42eaf#56da8e6d1d018519a30b36d85d3a53fe35a42eaf" +dependencies = [ + "anyhow", + "backtrace", + "bolero-generator", + "lazy_static", + "pretty-hex", + "rand 0.8.5", +] + +[[package]] +name = "bolero-generator" +version = "0.10.0" +source = "git+https://github.com/Ekleog-NEAR/bolero?rev=56da8e6d1d018519a30b36d85d3a53fe35a42eaf#56da8e6d1d018519a30b36d85d3a53fe35a42eaf" +dependencies = [ + "arbitrary", + "bolero-generator-derive", + "either", + "rand_core 0.6.4", +] + +[[package]] +name = "bolero-generator-derive" +version = "0.10.0" +source = "git+https://github.com/Ekleog-NEAR/bolero?rev=56da8e6d1d018519a30b36d85d3a53fe35a42eaf#56da8e6d1d018519a30b36d85d3a53fe35a42eaf" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bolero-honggfuzz" +version = "0.10.0" +source = "git+https://github.com/Ekleog-NEAR/bolero?rev=56da8e6d1d018519a30b36d85d3a53fe35a42eaf#56da8e6d1d018519a30b36d85d3a53fe35a42eaf" +dependencies = [ + "bolero-engine", +] + +[[package]] +name = "bolero-kani" +version = "0.10.0" +source = "git+https://github.com/Ekleog-NEAR/bolero?rev=56da8e6d1d018519a30b36d85d3a53fe35a42eaf#56da8e6d1d018519a30b36d85d3a53fe35a42eaf" +dependencies = [ + "bolero-engine", +] + +[[package]] +name = "bolero-libfuzzer" +version = "0.10.0" +source = "git+https://github.com/Ekleog-NEAR/bolero?rev=56da8e6d1d018519a30b36d85d3a53fe35a42eaf#56da8e6d1d018519a30b36d85d3a53fe35a42eaf" +dependencies = [ + "bolero-engine", + "cc", +] + +[[package]] +name = "borsh" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +dependencies = [ + "borsh-derive 0.9.3", + "hashbrown 0.11.2", +] + +[[package]] +name = "borsh" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58b559fd6448c6e2fd0adb5720cd98a2506594cafa4737ff98c396f3e82f667" +dependencies = [ + "borsh-derive 1.3.1", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate 0.1.5", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aadb5b6ccbd078890f6d7003694e33816e6b784358f18e15e7e6d9f065a57cd" +dependencies = [ + "once_cell", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.52", + "syn_derive", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "brotli" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bumpalo" +version = "3.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "bytesize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" +dependencies = [ + "serde", +] + +[[package]] +name = "bytestring" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" +dependencies = [ + "bytes", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "c2-chacha" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27dae93fe7b1e0424dc57179ac396908c26b035a87234809f5c4dfd1b47dc80" +dependencies = [ + "cipher", + "ppv-lite86", +] + +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.22", + "serde", + "serde_json", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "chainsync-loadtest" +version = "0.1.0" +dependencies = [ + "actix", + "anyhow", + "async-trait", + "clap", + "dirs", + "framework", + "futures", + "log", + "openssl-probe", + "parking_lot 0.12.1", + "rand 0.8.5", + "tokio", + "unc-async", + "unc-chain-configs", + "unc-crypto", + "unc-network", + "unc-o11y", + "unc-primitives", + "unc-store", +] + +[[package]] +name = "chrono" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.4", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.11.0", +] + +[[package]] +name = "clap_derive" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "cloud-storage" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7602ac4363f68ac757d6b87dd5d850549a14d37489902ae639c06ecec06ad275" +dependencies = [ + "async-trait", + "base64 0.13.1", + "bytes", + "chrono", + "dotenv", + "futures-util", + "hex", + "jsonwebtoken", + "lazy_static", + "openssl", + "percent-encoding", + "reqwest", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "cold-store-tool" +version = "0.1.0" +dependencies = [ + "anyhow", + "borsh 1.3.1", + "clap", + "framework", + "rand 0.8.5", + "strum", + "tracing", + "unc-chain-configs", + "unc-epoch-manager", + "unc-primitives", + "unc-store", +] + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "cpu-time" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e393a7668fe1fad3075085b86c781883000b4ede868f43627b34a87c8b7ded" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-bforest" +version = "0.101.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b5bb9245ec7dcc04d03110e538d31f0969d301c9d673145f4b4d5c3478539a3" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.101.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebb18d10e5ddac43ba4ca8fd4e310938569c3e484cc01b6372b27dc5bb4dfd28" +dependencies = [ + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli", + "hashbrown 0.14.3", + "log", + "regalloc2", + "smallvec", + "target-lexicon 0.12.14", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.101.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a3ce6d22982c1b9b6b012654258bab1a13947bb12703518bef06b1a4867c3d6" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.101.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47220fd4f9a0ce23541652b6f16f83868d282602c600d14934b2a4c166b4bd80" + +[[package]] +name = "cranelift-control" +version = "0.101.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5a4c42672aea9b6e820046b52e47a1c05d3394a6cdf4cb3c3c4b702f954bd2" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.101.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b4e9a3296fc827f9d35135dc2c0c8dd8d8359eb1ef904bae2d55d5bcb0c9f94" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "cranelift-frontend" +version = "0.101.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ec537d0f0b8e084517f3e7bfa1d89af343d7c7df455573fca9f272d4e01267" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon 0.12.14", +] + +[[package]] +name = "cranelift-isle" +version = "0.101.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bab6d69919d210a50331d35cc6ce111567bc040aebac63a8ae130d0400a075" + +[[package]] +name = "cranelift-native" +version = "0.101.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f32e81605f352cf37af5463f11cd7deec7b6572741931a8d372f7fdd4a744f5d" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon 0.12.14", +] + +[[package]] +name = "cranelift-wasm" +version = "0.101.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0edaa4cbec1bc787395c074233df2652dd62f3e29d3ee60329514a0a51e6b045" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools", + "log", + "smallvec", + "wasmparser 0.115.0", + "wasmtime-types", +] + +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crossterm" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" +dependencies = [ + "bitflags 1.3.2", + "crossterm_winapi", + "libc", + "mio", + "parking_lot 0.12.1", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.7", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.7", + "subtle", +] + +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "platforms", + "rand_core 0.6.4", + "rustc_version 0.4.0", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "darling" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 2.0.52", +] + +[[package]] +name = "darling_macro" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derive-enum-from-into" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc2a1b7c0031fb651e9bc1fa4255da82747c187b9ac1dc36b3783d71fadd9d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.0", + "syn 1.0.109", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dissimilar" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632" + +[[package]] +name = "dlv-list" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "dynasm" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dynasm" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33dc03612f42465a8ed7f5e354bc2b79ba54cedefa81d5bd3a064f1835adaba8" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dynasmrt" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" +dependencies = [ + "byteorder", + "dynasm 1.2.3", + "memmap2", +] + +[[package]] +name = "dynasmrt" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7dccc31a678058996aef614f6bd418ced384da70f284e83e2b7bf29b27b6a28" +dependencies = [ + "byteorder", + "dynasm 2.0.0", + "fnv", + "memmap2", +] + +[[package]] +name = "easy-ext" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53aff6fdc1b181225acdcb5b14c47106726fd8e486707315b1b138baed68ee31" + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core 0.6.4", + "sha2 0.10.8", + "subtle", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "elastic-array" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d63720ea2bc2e1b79f7aa044d9dc0b825f9ccb6930b32120f8fb9e873aa84bc" +dependencies = [ + "heapsize", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "enumset" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "estimator-warehouse" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "insta", + "nix 0.24.3", + "reqwest", + "rusqlite", + "serde", + "serde_json", + "tempfile", + "xshell", +] + +[[package]] +name = "expect-test" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3" +dependencies = [ + "dissimilar", + "once_cell", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fiat-crypto" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382" + +[[package]] +name = "finite-wasm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d81b511929c2669eaf64e36471cf27c2508133e62ade9d49e608e8d675e7854" +dependencies = [ + "bitvec", + "dissimilar", + "num-traits", + "prefix-sum-vec", + "thiserror", + "wasm-encoder 0.27.0", + "wasmparser 0.105.0", + "wasmprinter", +] + +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "static_assertions", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flagset" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb3aa5e95cf9aabc17f060cfa0ced7b83f042390760ca53bf09df9968acaa1" + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "framework" +version = "0.1.0" +dependencies = [ + "actix", + "actix-rt", + "actix-web", + "anyhow", + "awc", + "bencher", + "borsh 1.3.1", + "chrono", + "cloud-storage", + "dirs", + "easy-ext", + "futures", + "hex", + "hyper", + "hyper-tls", + "indicatif", + "node-runtime", + "num-rational", + "once_cell", + "primitive-types", + "rand 0.8.5", + "rayon", + "regex", + "reqwest", + "rlimit", + "rust-s3", + "serde", + "serde_ignored", + "serde_json", + "smart-default", + "strum", + "tempfile", + "testlib", + "thiserror", + "tokio", + "tracing", + "unc-actix-test-utils", + "unc-async", + "unc-chain", + "unc-chain-configs", + "unc-chunks", + "unc-client", + "unc-client-primitives", + "unc-config-utils", + "unc-crypto", + "unc-dyn-configs", + "unc-epoch-manager", + "unc-jsonrpc", + "unc-jsonrpc-primitives", + "unc-mainnet-res", + "unc-network", + "unc-o11y", + "unc-parameters", + "unc-performance-metrics", + "unc-pool", + "unc-primitives", + "unc-store", + "unc-telemetry", + "unc-vm-runner", + "xz2", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "fxprof-processed-profile" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" +dependencies = [ + "bitflags 2.4.2", + "debugid", + "fxhash", + "serde", + "serde_json", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "genesis-csv-to-json" +version = "0.1.0" +dependencies = [ + "chrono", + "clap", + "csv", + "framework", + "serde", + "serde_json", + "tempfile", + "unc-chain-configs", + "unc-crypto", + "unc-network", + "unc-primitives", +] + +[[package]] +name = "genesis-populate" +version = "0.1.0" +dependencies = [ + "borsh 1.3.1", + "clap", + "framework", + "indicatif", + "tempfile", + "unc-chain", + "unc-chain-configs", + "unc-crypto", + "unc-epoch-manager", + "unc-primitives", + "unc-store", + "unc-test-contracts", + "unc-vm-runner", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +dependencies = [ + "fallible-iterator 0.3.0", + "indexmap 2.2.5", + "stable_deref_trait", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "h2" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.2.5", + "slab", + "tokio", + "tokio-util 0.7.10", + "tracing", +] + +[[package]] +name = "half" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +dependencies = [ + "cfg-if 1.0.0", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.11", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash 0.8.11", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.3", +] + +[[package]] +name = "heapsize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" +dependencies = [ + "winapi", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d70693199b3cf4552f3fa720b54163927a3ebed2aef240efaf556033ab336a11" +dependencies = [ + "hex-literal-impl", + "proc-macro-hack", +] + +[[package]] +name = "hex-literal-impl" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59448fc2f82a5fb6907f78c3d69d843e82ff5b051923313cc4438cb0c7b745a8" +dependencies = [ + "proc-macro-hack", +] + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.6", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "im" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" +dependencies = [ + "bitmaps", + "rand_core 0.6.4", + "rand_xoshiro", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "impl-more" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d" + +[[package]] +name = "indexer-example" +version = "0.1.0" +dependencies = [ + "actix", + "anyhow", + "clap", + "openssl-probe", + "serde_json", + "tokio", + "tracing", + "unc-indexer", + "unc-o11y", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg 1.1.0", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "indicatif" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7baab56125e25686df467fe470785512329883aab42696d661247aca2a2896e4" +dependencies = [ + "console", + "lazy_static", + "number_prefix", + "rayon", + "regex", +] + +[[package]] +name = "insta" +version = "1.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7c22c4d34ef4788c351e971c52bfdfe7ea2766f8c5466bc175dd46e52ac22e" +dependencies = [ + "console", + "lazy_static", + "linked-hash-map", + "pest", + "pest_derive", + "serde", + "similar", + "yaml-rust", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "integration-tests" +version = "0.1.0" +dependencies = [ + "actix", + "actix-rt", + "anyhow", + "assert_matches", + "borsh 1.3.1", + "chrono", + "clap", + "framework", + "futures", + "hex", + "insta", + "itertools", + "node-runtime", + "once_cell", + "parking_lot 0.12.1", + "primitive-types", + "rand 0.8.5", + "rlp", + "serde", + "serde_json", + "smart-default", + "strum", + "tempfile", + "testlib", + "tokio", + "tracing", + "unc-actix-test-utils", + "unc-async", + "unc-chain", + "unc-chain-configs", + "unc-chunks", + "unc-client", + "unc-client-primitives", + "unc-crypto", + "unc-epoch-manager", + "unc-fmt", + "unc-jsonrpc", + "unc-jsonrpc-client", + "unc-jsonrpc-primitives", + "unc-network", + "unc-o11y", + "unc-parameters", + "unc-performance-metrics", + "unc-primitives", + "unc-primitives-core", + "unc-stdx", + "unc-store", + "unc-telemetry", + "unc-test-contracts", + "unc-undo-block", + "unc-vm-runner", + "unc-wallet-contract", + "wat", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json_comments" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbbfed4e59ba9750e15ba154fdfd9329cee16ff3df539c2666b70f58cc32105" + +[[package]] +name = "jsonwebtoken" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afabcc15e437a6484fc4f12d0fd63068fe457bf93f1c148d3d9649c60b103f32" +dependencies = [ + "base64 0.12.3", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1 0.4.1", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keypair-generator" +version = "0.1.0" +dependencies = [ + "clap", + "framework", + "unc-crypto", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + +[[package]] +name = "libloading" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2caa5afb8bf9f3a2652760ce7d4f62d21c4d5a423e68466fca30df82f2330164" +dependencies = [ + "cfg-if 1.0.0", + "windows-targets 0.52.4", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.2", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "librocksdb-sys" +version = "0.11.0+8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" +dependencies = [ + "bindgen", + "bzip2-sys", + "cc", + "glob", + "libc", + "libz-sys", + "lz4-sys", + "tikv-jemalloc-sys", + "zstd-sys", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037731f5d3aaa87a5675e895b63ddff1a87624bc29f77004ea829809654e48f6" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "local-channel" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +dependencies = [ + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg 1.1.0", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "loupe" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6a72dfa44fe15b5e76b94307eeb2ff995a8c5b283b55008940c02e0c5b634d" +dependencies = [ + "loupe-derive", + "rustversion", +] + +[[package]] +name = "loupe-derive" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fbfc88337168279f2e9ae06e157cfed4efd3316e14dc96ed074d4f2e6c5952" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown 0.12.3", +] + +[[package]] +name = "lz4-sys" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "maybe-async" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if 1.0.0", + "digest 0.10.7", +] + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memfd" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +dependencies = [ + "rustix 0.38.31", +] + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minidom" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f45614075738ce1b77a1768912a60c0227525971b03e09122a05b8a34a2a6278" +dependencies = [ + "rxml", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "mock-node" +version = "0.1.0" +dependencies = [ + "actix", + "actix-rt", + "anyhow", + "clap", + "framework", + "futures", + "pin-project", + "rand 0.8.5", + "rayon", + "serde", + "serde_json", + "tempfile", + "tokio", + "tracing", + "unc-actix-test-utils", + "unc-async", + "unc-chain", + "unc-chain-configs", + "unc-chunks", + "unc-client", + "unc-crypto", + "unc-epoch-manager", + "unc-jsonrpc", + "unc-jsonrpc-client", + "unc-network", + "unc-o11y", + "unc-performance-metrics", + "unc-primitives", + "unc-store", + "unc-telemetry", +] + +[[package]] +name = "more-asserts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nix" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if 0.1.10", + "libc", + "void", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.0", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "node-runtime" +version = "0.1.0" +dependencies = [ + "assert_matches", + "borsh 1.3.1", + "enum-map", + "hex", + "indicatif", + "num-bigint 0.3.3", + "num-rational", + "num-traits", + "once_cell", + "rand 0.8.5", + "rayon", + "serde", + "serde_json", + "sha2 0.10.8", + "tempfile", + "testlib", + "thiserror", + "tracing", + "unc-chain-configs", + "unc-crypto", + "unc-o11y", + "unc-parameters", + "unc-primitives", + "unc-primitives-core", + "unc-store", + "unc-test-contracts", + "unc-vm-runner", + "unc-wallet-contract", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "ntapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +dependencies = [ + "winapi", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d51546d704f52ef14b3c962b5776e53d5b862e5790e40a350d366c209bd7f7a" +dependencies = [ + "autocfg 0.1.8", + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.7.3", + "serde", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg 1.1.0", + "num-bigint 0.3.3", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg 1.1.0", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "number_prefix" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a" + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "crc32fast", + "hashbrown 0.14.3", + "indexmap 2.2.5", + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.4.2", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "300.2.3+3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "opentelemetry" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6105e89802af13fdf48c49d7646d3b533a70e536d818aae7e78ba0433d01acb8" +dependencies = [ + "async-trait", + "crossbeam-channel", + "futures-channel", + "futures-executor", + "futures-util", + "js-sys", + "lazy_static", + "percent-encoding", + "pin-project", + "rand 0.8.5", + "thiserror", + "tokio", + "tokio-stream", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1a6ca9de4c8b00aa7f1a153bd76cb263287155cec642680d79d98706f3d28a" +dependencies = [ + "async-trait", + "futures", + "futures-util", + "http 0.2.12", + "opentelemetry", + "prost", + "thiserror", + "tokio", + "tonic", + "tonic-build", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985cc35d832d412224b2cffe2f9194b1b89b6aa5d0bef76d080dce09d90e62bd" +dependencies = [ + "opentelemetry", +] + +[[package]] +name = "ordered-multimap" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" +dependencies = [ + "dlv-list", + "hashbrown 0.12.3", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "page_size" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "parity-wasm" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" + +[[package]] +name = "parity-wasm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" + +[[package]] +name = "parking_lot" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +dependencies = [ + "lock_api 0.3.4", + "parking_lot_core 0.7.3", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api 0.4.11", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api 0.4.11", + "parking_lot_core 0.9.9", +] + +[[package]] +name = "parking_lot_core" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93f386bb233083c799e6e642a9d73db98c24a5deeb95ffc85bf281255dffc98" +dependencies = [ + "cfg-if 0.1.10", + "cloudabi", + "libc", + "redox_syscall 0.1.57", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pem" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +dependencies = [ + "base64 0.13.1", + "once_cell", + "regex", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "pest_meta" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" +dependencies = [ + "once_cell", + "pest", + "sha2 0.10.8", +] + +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap 2.2.5", +] + +[[package]] +name = "pin-project" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "platforms" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prefix-sum-vec" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa06bd51638b6e76ac9ba9b6afb4164fa647bd2916d722f2623fbb6d1ed8bdba" + +[[package]] +name = "pretty-hex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" + +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn 2.0.52", +] + +[[package]] +name = "primitive-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" +dependencies = [ + "fixed-hash", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prometheus" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +dependencies = [ + "cfg-if 1.0.0", + "fnv", + "lazy_static", + "memchr", + "parking_lot 0.12.1", + "protobuf 2.28.0", + "thiserror", +] + +[[package]] +name = "prost" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" +dependencies = [ + "bytes", + "heck 0.3.3", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prost", + "prost-types", + "regex", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-types" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" +dependencies = [ + "bytes", + "prost", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + +[[package]] +name = "protobuf" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58678a64de2fced2bdec6bca052a6716a0efe692d6e3f53d1bda6a1def64cfc0" +dependencies = [ + "once_cell", + "protobuf-support", + "thiserror", +] + +[[package]] +name = "protobuf-codegen" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32777b0b3f6538d9d2e012b3fad85c7e4b9244b5958d04a6415f4333782b7a77" +dependencies = [ + "anyhow", + "once_cell", + "protobuf 3.4.0", + "protobuf-parse", + "regex", + "tempfile", + "thiserror", +] + +[[package]] +name = "protobuf-parse" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96cb37955261126624a25b5e6bda40ae34cf3989d52a783087ca6091b29b5642" +dependencies = [ + "anyhow", + "indexmap 1.9.3", + "log", + "protobuf 3.4.0", + "protobuf-support", + "tempfile", + "thiserror", + "which", +] + +[[package]] +name = "protobuf-support" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ed294a835b0f30810e13616b1cd34943c6d1e84a8f3b0dcfe466d256c3e7e7" +dependencies = [ + "thiserror", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pwasm-utils" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7a12f176deee919f4ba55326ee17491c8b707d0987aed822682c821b660192" +dependencies = [ + "byteorder", + "log", + "parity-wasm 0.41.0", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.12", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b363d4f6370f88d62bf586c80405657bde0f0e1b8945d47d2ad59b906cb4f54" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rayon" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redis" +version = "0.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f49cdc0bb3f412bf8e7d1bd90fe1d9eb10bc5c399ba90973c14662a27b3f8ba" +dependencies = [ + "combine", + "itoa", + "percent-encoding", + "ryu", + "sha1_smol", + "socket2 0.4.10", + "url", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom 0.2.12", + "libredox", + "thiserror", +] + +[[package]] +name = "reed-solomon-erasure" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a415a013dd7c5d4221382329a5a3482566da675737494935cbbbcdec04662f9d" +dependencies = [ + "smallvec", +] + +[[package]] +name = "regalloc2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6" +dependencies = [ + "hashbrown 0.13.2", + "log", + "rustc-hash", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.6", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "region" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" +dependencies = [ + "bitflags 1.3.2", + "libc", + "mach", + "winapi", +] + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.11.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-util 0.7.10", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg", +] + +[[package]] +name = "restaked" +version = "0.1.0" +dependencies = [ + "clap", + "framework", + "integration-tests", + "tokio", + "unc-crypto", + "unc-jsonrpc-client", + "unc-o11y", + "unc-primitives", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rkyv" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rlimit" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "347703a5ae47adf1e693144157be231dde38c72bd485925cae7407ad3e52480b" +dependencies = [ + "libc", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rocksdb" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" +dependencies = [ + "libc", + "librocksdb-sys", +] + +[[package]] +name = "rsa" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3648b669b10afeab18972c105e284a7b953a669b0be3514c27f9b17acab2f9cd" +dependencies = [ + "byteorder", + "digest 0.9.0", + "lazy_static", + "num-bigint-dig 0.6.1", + "num-integer", + "num-iter", + "num-traits", + "pem", + "rand 0.7.3", + "sha2 0.9.9", + "simple_asn1 0.4.1", + "subtle", + "thiserror", + "zeroize", +] + +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest 0.10.7", + "num-bigint-dig 0.8.4", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "sha2 0.10.8", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rsa-export" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce6de48e7cae950d65a62e67da01c1bb44576f0c3ea3e5749088cf0205a7263" +dependencies = [ + "num-bigint-dig 0.6.1", + "pem", + "rsa 0.3.0", + "simple_asn1 0.5.4", +] + +[[package]] +name = "runtime-params-estimator" +version = "0.1.0" +dependencies = [ + "anyhow", + "borsh 1.3.1", + "bs58", + "bytesize", + "cfg-if 1.0.0", + "chrono", + "clap", + "enum-map", + "framework", + "genesis-populate", + "hex", + "indicatif", + "insta", + "libc", + "node-runtime", + "num-rational", + "num-traits", + "rand 0.8.5", + "rand_xorshift", + "rocksdb", + "serde_json", + "tempfile", + "tracing", + "tracing-span-tree", + "tracing-subscriber", + "unc-chain-configs", + "unc-crypto", + "unc-fmt", + "unc-o11y", + "unc-parameters", + "unc-primitives", + "unc-store", + "unc-test-contracts", + "unc-vm-runner", + "wat", +] + +[[package]] +name = "runtime-tester" +version = "0.1.0" +dependencies = [ + "bolero", + "cpu-time", + "framework", + "libfuzzer-sys", + "serde", + "serde_json", + "tempfile", + "testlib", + "tracing", + "unc-chain", + "unc-chain-configs", + "unc-client", + "unc-client-primitives", + "unc-crypto", + "unc-epoch-manager", + "unc-o11y", + "unc-parameters", + "unc-primitives", + "unc-store", + "unc-test-contracts", +] + +[[package]] +name = "runtime-tester-fuzz" +version = "0.1.0" +dependencies = [ + "libfuzzer-sys", + "runtime-tester", + "serde_json", +] + +[[package]] +name = "rusqlite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" +dependencies = [ + "bitflags 2.4.2", + "chrono", + "fallible-iterator 0.2.0", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rust-ini" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" +dependencies = [ + "cfg-if 1.0.0", + "ordered-multimap", +] + +[[package]] +name = "rust-s3" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6009d9d4cf910505534d62d380a0aa305805a2af0b5c3ad59a3024a0715b847" +dependencies = [ + "async-trait", + "aws-creds", + "aws-region", + "base64 0.13.1", + "block_on_proc", + "cfg-if 1.0.0", + "hex", + "hmac", + "http 0.2.12", + "log", + "maybe-async", + "md5", + "minidom", + "percent-encoding", + "reqwest", + "serde", + "serde-xml-rs", + "serde_derive", + "sha2 0.10.8", + "thiserror", + "time", + "tokio", + "tokio-stream", + "url", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.22", +] + +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno 0.3.8", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags 2.4.2", + "errno 0.3.8", + "libc", + "linux-raw-sys 0.4.13", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "rxml" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a98f186c7a2f3abbffb802984b7f1dfd65dac8be1aafdaabbca4137f53f0dff7" +dependencies = [ + "bytes", + "rxml_validation", + "smartstring", +] + +[[package]] +name = "rxml_validation" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a197350ece202f19a166d1ad6d9d6de145e1d2a8ef47db299abe164dbd7530" + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "secp256k1" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" +dependencies = [ + "rand 0.8.5", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" +dependencies = [ + "cc", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +dependencies = [ + "serde", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-bench" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d733da87e79faaac25616e33d26299a41143fd4cd42746cbb0e91d8feea243fd" +dependencies = [ + "byteorder", + "serde", +] + +[[package]] +name = "serde-xml-rs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65162e9059be2f6a3421ebbb4fef3e74b7d9e7c60c50a0e292c6239f19f1edfa" +dependencies = [ + "log", + "serde", + "thiserror", + "xml-rs", +] + +[[package]] +name = "serde_bytes" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "serde_ignored" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e319a36d1b52126a0d608f24e93b2d81297091818cd70625fcf50a15d84ddf" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "indexmap 2.2.5", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15d167997bd841ec232f5b2b8e0e26606df2e7caa4c31b95ea9ca52b200bd270" +dependencies = [ + "base64 0.21.7", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.5", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "865f9743393e638991566a8b7a479043c2c8da94a33e0a31f18214c9cae0a64d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "serde_yaml" +version = "0.9.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" +dependencies = [ + "indexmap 2.2.5", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "serial_test" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d" +dependencies = [ + "lazy_static", + "parking_lot 0.11.2", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shell-escape" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[package]] +name = "similar" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" + +[[package]] +name = "simple_asn1" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b" +dependencies = [ + "chrono", + "num-bigint 0.2.6", + "num-traits", +] + +[[package]] +name = "simple_asn1" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb4ea60fb301dc81dfc113df680571045d375ab7345d171c5dc7d7e13107a80" +dependencies = [ + "chrono", + "num-bigint 0.4.4", + "num-traits", + "thiserror", +] + +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "slice-group-by" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "smart-default" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg 1.1.0", + "static_assertions", + "version_check", +] + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "speedy_sync" +version = "0.1.0" +dependencies = [ + "borsh 1.3.1", + "clap", + "framework", + "serde", + "serde_json", + "unc-chain", + "unc-chain-configs", + "unc-chain-primitives", + "unc-epoch-manager", + "unc-primitives", + "unc-store", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "state-viewer" +version = "0.1.0" +dependencies = [ + "actix", + "anyhow", + "borsh 1.3.1", + "bytesize", + "chrono", + "clap", + "cloud-storage", + "framework", + "insta", + "itertools", + "node-runtime", + "once_cell", + "rand 0.8.5", + "rayon", + "redis", + "regex", + "reqwest", + "rust-s3", + "serde", + "serde_json", + "strum", + "tempfile", + "testlib", + "thiserror", + "tracing", + "unc-chain", + "unc-chain-configs", + "unc-client", + "unc-crypto", + "unc-epoch-manager", + "unc-network", + "unc-o11y", + "unc-primitives", + "unc-primitives-core", + "unc-store", + "unc-test-contracts", + "yansi", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "storage-usage-delta-calculator" +version = "0.1.0" +dependencies = [ + "anyhow", + "node-runtime", + "serde_json", + "tokio", + "tracing", + "unc-chain-configs", + "unc-o11y", + "unc-parameters", + "unc-primitives", + "unc-store", +] + +[[package]] +name = "store-validator" +version = "0.1.0" +dependencies = [ + "clap", + "framework", + "serde_json", + "testlib", + "unc-chain", + "unc-chain-configs", + "unc-client", + "unc-epoch-manager", + "unc-o11y", + "unc-primitives", + "unc-store", + "yansi", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "stun" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25" +dependencies = [ + "base64 0.13.1", + "crc", + "lazy_static", + "md-5", + "rand 0.8.5", + "ring", + "subtle", + "thiserror", + "tokio", + "url", + "webrtc-util", +] + +[[package]] +name = "style-tests" +version = "0.0.0" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sysinfo" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54cb4ebf3d49308b99e6e9dc95e989e2fdbdc210e4f67c39db0bb89ba927001c" +dependencies = [ + "cfg-if 1.0.0", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "winapi", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d" + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "rustix 0.38.31", + "windows-sys 0.52.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "test-log" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b319995299c65d522680decf80f2c108d85b861d81dfe340a10d16cee29d9e6" +dependencies = [ + "test-log-macros", + "tracing-subscriber", +] + +[[package]] +name = "test-log-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8f546451eaa38373f549093fe9fd05e7d2bade739e2ddf834b9968621d60107" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "testlib" +version = "0.1.0" +dependencies = [ + "node-runtime", + "once_cell", + "unc-chain", + "unc-chain-configs", + "unc-crypto", + "unc-parameters", + "unc-primitives", + "unc-test-contracts", +] + +[[package]] +name = "themis" +version = "0.1.0" +dependencies = [ + "anyhow", + "cargo_metadata", + "semver 1.0.22", + "serde", + "toml", +] + +[[package]] +name = "thiserror" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", +] + +[[package]] +name = "tikv-jemalloc-sys" +version = "0.5.4+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9402443cb8fd499b6f327e40565234ff34dbda27460c5b47db0db77443dd85d1" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "tikv-jemallocator" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965fe0c26be5c56c94e38ba547249074803efd52adfb66de62107d95aab3eaca" +dependencies = [ + "libc", + "tikv-jemalloc-sys", +] + +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot 0.12.1", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.6", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-openssl" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ffab79df67727f6acf57f1ff743091873c24c579b1e2ce4d8f53e47ded4d63d" +dependencies = [ + "futures-util", + "openssl", + "openssl-sys", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.5", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.2.5", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tonic" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff08f4649d10a70ffa3522ca559031285d8e421d727ac85c60825761818f5d0a" +dependencies = [ + "async-stream", + "async-trait", + "base64 0.13.1", + "bytes", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "prost-derive", + "tokio", + "tokio-stream", + "tokio-util 0.6.10", + "tower", + "tower-layer", + "tower-service", + "tracing", + "tracing-futures", +] + +[[package]] +name = "tonic-build" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9403f1bafde247186684b230dc6f38b5cd514584e8bec1dd32514be4745fa757" +dependencies = [ + "proc-macro2", + "prost-build", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand 0.8.5", + "slab", + "tokio", + "tokio-util 0.7.10", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tqdm" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f056dcbe6bffd4595b23fba18affc5bd8c36ebd3199a112b1046f14f57b7323" +dependencies = [ + "crossterm", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-appender" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +dependencies = [ + "crossbeam-channel", + "thiserror", + "time", + "tracing-subscriber", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-log" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-opentelemetry" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbbe89715c1dbbb790059e2565353978564924ee85017b5fff365c872ff6721f" +dependencies = [ + "once_cell", + "opentelemetry", + "tracing", + "tracing-core", + "tracing-log 0.1.4", + "tracing-subscriber", +] + +[[package]] +name = "tracing-span-tree" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3809cda0328d505548af1ca24fac329f41c0c213d0a1e78908922d0b43e5c43" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log 0.2.0", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "trybuild" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a9d3ba662913483d6722303f619e75ea10b7855b0f8e0d72799cf8621bb488f" +dependencies = [ + "basic-toml", + "glob", + "once_cell", + "serde", + "serde_derive", + "serde_json", + "termcolor", +] + +[[package]] +name = "turn" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" +dependencies = [ + "async-trait", + "base64 0.13.1", + "futures", + "log", + "md-5", + "rand 0.8.5", + "ring", + "stun", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unc-account-id" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6568505d6185d67606e842f07ff475f0f4a45d862bce80f5d5bd31a7d7058881" +dependencies = [ + "borsh 1.3.1", + "serde", +] + +[[package]] +name = "unc-actix-test-utils" +version = "0.1.0" +dependencies = [ + "actix-rt", + "futures", + "once_cell", + "unc-store", +] + +[[package]] +name = "unc-amend-genesis" +version = "0.1.0" +dependencies = [ + "anyhow", + "borsh 1.3.1", + "clap", + "framework", + "node-runtime", + "num-rational", + "serde", + "serde_json", + "tempfile", + "tracing", + "unc-chain", + "unc-chain-configs", + "unc-crypto", + "unc-epoch-manager", + "unc-network", + "unc-primitives", + "unc-primitives-core", + "unc-store", + "unc-test-contracts", +] + +[[package]] +name = "unc-async" +version = "0.1.0" +dependencies = [ + "actix", + "derive-enum-from-into", + "derive_more", + "futures", + "once_cell", + "serde", + "serde_json", + "time", + "tokio", + "unc-o11y", + "unc-performance-metrics", + "unc-primitives", +] + +[[package]] +name = "unc-cache" +version = "0.1.0" +dependencies = [ + "bencher", + "lru", + "rand 0.8.5", +] + +[[package]] +name = "unc-chain" +version = "0.1.0" +dependencies = [ + "actix", + "assert_matches", + "borsh 1.3.1", + "bytesize", + "chrono", + "crossbeam-channel", + "enum-map", + "insta", + "itertools", + "itoa", + "lru", + "num-rational", + "once_cell", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rayon", + "strum", + "thiserror", + "tracing", + "unc-async", + "unc-cache", + "unc-chain-configs", + "unc-chain-primitives", + "unc-client-primitives", + "unc-crypto", + "unc-epoch-manager", + "unc-network", + "unc-o11y", + "unc-performance-metrics", + "unc-performance-metrics-macros", + "unc-pool", + "unc-primitives", + "unc-store", + "yansi", +] + +[[package]] +name = "unc-chain-configs" +version = "0.1.0" +dependencies = [ + "anyhow", + "bytesize", + "chrono", + "derive_more", + "num-rational", + "once_cell", + "serde", + "serde_json", + "sha2 0.10.8", + "smart-default", + "tracing", + "unc-config-utils", + "unc-crypto", + "unc-o11y", + "unc-parameters", + "unc-primitives", +] + +[[package]] +name = "unc-chain-primitives" +version = "0.1.0" +dependencies = [ + "chrono", + "thiserror", + "tracing", + "unc-crypto", + "unc-primitives", +] + +[[package]] +name = "unc-chunks" +version = "0.1.0" +dependencies = [ + "actix", + "assert_matches", + "borsh 1.3.1", + "chrono", + "derive-enum-from-into", + "derive_more", + "futures", + "itertools", + "lru", + "once_cell", + "rand 0.8.5", + "reed-solomon-erasure", + "strum", + "time", + "tracing", + "unc-async", + "unc-chain", + "unc-chain-configs", + "unc-chunks-primitives", + "unc-crypto", + "unc-epoch-manager", + "unc-network", + "unc-o11y", + "unc-performance-metrics", + "unc-performance-metrics-macros", + "unc-pool", + "unc-primitives", + "unc-store", +] + +[[package]] +name = "unc-chunks-primitives" +version = "0.1.0" +dependencies = [ + "unc-chain-primitives", + "unc-primitives", +] + +[[package]] +name = "unc-client" +version = "0.1.0" +dependencies = [ + "actix", + "actix-rt", + "anyhow", + "assert_matches", + "async-trait", + "borsh 1.3.1", + "chrono", + "cloud-storage", + "derive_more", + "futures", + "itertools", + "lru", + "num-rational", + "once_cell", + "percent-encoding", + "rand 0.8.5", + "rayon", + "reed-solomon-erasure", + "regex", + "reqwest", + "rust-s3", + "serde", + "serde_json", + "strum", + "sysinfo", + "tempfile", + "thiserror", + "tokio", + "tracing", + "unc-actix-test-utils", + "unc-async", + "unc-chain", + "unc-chain-configs", + "unc-chain-primitives", + "unc-chunks", + "unc-client-primitives", + "unc-crypto", + "unc-dyn-configs", + "unc-epoch-manager", + "unc-network", + "unc-o11y", + "unc-parameters", + "unc-performance-metrics", + "unc-performance-metrics-macros", + "unc-pool", + "unc-primitives", + "unc-store", + "unc-telemetry", + "yansi", +] + +[[package]] +name = "unc-client-primitives" +version = "0.1.0" +dependencies = [ + "actix", + "chrono", + "serde", + "serde_json", + "strum", + "thiserror", + "tracing", + "unc-chain-configs", + "unc-chain-primitives", + "unc-chunks-primitives", + "unc-crypto", + "unc-primitives", + "yansi", +] + +[[package]] +name = "unc-config-utils" +version = "0.1.0" +dependencies = [ + "anyhow", + "json_comments", + "thiserror", + "tracing", +] + +[[package]] +name = "unc-crypto" +version = "0.1.0" +dependencies = [ + "blake2", + "bolero", + "borsh 1.3.1", + "bs58", + "c2-chacha", + "curve25519-dalek", + "derive_more", + "ed25519-dalek", + "hex", + "hex-literal", + "once_cell", + "primitive-types", + "rand 0.7.3", + "rsa 0.9.6", + "rsa-export", + "secp256k1", + "serde", + "serde_json", + "sha2 0.10.8", + "subtle", + "tempfile", + "thiserror", + "unc-account-id", + "unc-config-utils", + "unc-stdx", +] + +[[package]] +name = "unc-database-tool" +version = "0.1.0" +dependencies = [ + "anyhow", + "borsh 1.3.1", + "clap", + "framework", + "indicatif", + "rand 0.8.5", + "rayon", + "rocksdb", + "strum", + "tempfile", + "unc-chain", + "unc-chain-configs", + "unc-epoch-manager", + "unc-primitives", + "unc-store", +] + +[[package]] +name = "unc-dyn-configs" +version = "0.1.0" +dependencies = [ + "anyhow", + "once_cell", + "prometheus", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "unc-chain-configs", + "unc-o11y", + "unc-primitives", +] + +[[package]] +name = "unc-epoch-manager" +version = "0.1.0" +dependencies = [ + "borsh 1.3.1", + "chrono", + "itertools", + "num-bigint 0.3.3", + "num-rational", + "num-traits", + "primitive-types", + "protobuf 3.4.0", + "rand 0.8.5", + "rand_hc 0.3.2", + "serde_json", + "smart-default", + "tracing", + "unc-cache", + "unc-chain-configs", + "unc-chain-primitives", + "unc-crypto", + "unc-primitives", + "unc-store", +] + +[[package]] +name = "unc-epoch-sync-tool" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "framework", + "tracing", + "unc-chain", + "unc-chain-configs", + "unc-epoch-manager", + "unc-primitives", + "unc-store", +] + +[[package]] +name = "unc-flat-storage" +version = "0.1.0" +dependencies = [ + "anyhow", + "borsh 1.3.1", + "clap", + "framework", + "rayon", + "tqdm", + "tracing", + "unc-chain", + "unc-chain-configs", + "unc-epoch-manager", + "unc-primitives", + "unc-store", +] + +[[package]] +name = "unc-fmt" +version = "0.1.0" +dependencies = [ + "unc-primitives-core", +] + +[[package]] +name = "unc-fork-network" +version = "0.1.0" +dependencies = [ + "actix", + "anyhow", + "chrono", + "clap", + "framework", + "hex", + "rayon", + "serde", + "serde_json", + "strum", + "tracing", + "unc-chain", + "unc-chain-configs", + "unc-crypto", + "unc-epoch-manager", + "unc-mirror", + "unc-o11y", + "unc-parameters", + "unc-primitives", + "unc-store", +] + +[[package]] +name = "unc-indexer" +version = "0.1.0" +dependencies = [ + "actix", + "anyhow", + "async-recursion", + "framework", + "futures", + "node-runtime", + "once_cell", + "rocksdb", + "serde", + "serde_json", + "tokio", + "tracing", + "unc-chain-configs", + "unc-client", + "unc-crypto", + "unc-dyn-configs", + "unc-indexer-primitives", + "unc-o11y", + "unc-parameters", + "unc-primitives", + "unc-store", +] + +[[package]] +name = "unc-indexer-primitives" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", + "unc-primitives", +] + +[[package]] +name = "unc-jsonrpc" +version = "0.1.0" +dependencies = [ + "actix", + "actix-cors", + "actix-web", + "bs58", + "easy-ext", + "futures", + "hex", + "once_cell", + "serde", + "serde_json", + "serde_with", + "tokio", + "tracing", + "tracing-subscriber", + "unc-chain-configs", + "unc-client", + "unc-client-primitives", + "unc-jsonrpc-adversarial-primitives", + "unc-jsonrpc-client", + "unc-jsonrpc-primitives", + "unc-network", + "unc-o11y", + "unc-primitives", + "unc-rpc-error-macro", +] + +[[package]] +name = "unc-jsonrpc-adversarial-primitives" +version = "0.1.0" +dependencies = [ + "serde", + "unc-network", + "unc-primitives", +] + +[[package]] +name = "unc-jsonrpc-client" +version = "0.1.0" +dependencies = [ + "actix-http", + "awc", + "futures", + "serde", + "serde_json", + "unc-jsonrpc-primitives", + "unc-primitives", +] + +[[package]] +name = "unc-jsonrpc-fuzz" +version = "0.1.0" +dependencies = [ + "actix", + "arbitrary", + "awc", + "libfuzzer-sys", + "once_cell", + "serde", + "serde_json", + "tokio", + "unc-jsonrpc", + "unc-jsonrpc-primitives", + "unc-jsonrpc-tests", + "unc-primitives", +] + +[[package]] +name = "unc-jsonrpc-primitives" +version = "0.1.0" +dependencies = [ + "arbitrary", + "serde", + "serde_json", + "thiserror", + "unc-chain-configs", + "unc-client-primitives", + "unc-crypto", + "unc-primitives", + "unc-rpc-error-macro", +] + +[[package]] +name = "unc-jsonrpc-tests" +version = "0.1.0" +dependencies = [ + "actix", + "awc", + "borsh 1.3.1", + "futures", + "once_cell", + "serde", + "serde_json", + "unc-actix-test-utils", + "unc-chain-configs", + "unc-client", + "unc-crypto", + "unc-jsonrpc", + "unc-jsonrpc-primitives", + "unc-network", + "unc-o11y", + "unc-primitives", + "unc-store", +] + +[[package]] +name = "unc-mainnet-res" +version = "0.1.0" +dependencies = [ + "serde_json", + "unc-account-id", + "unc-chain-configs", + "unc-primitives", +] + +[[package]] +name = "unc-mirror" +version = "0.1.0" +dependencies = [ + "actix", + "anyhow", + "async-trait", + "borsh 1.3.1", + "bs58", + "clap", + "ed25519-dalek", + "framework", + "hex", + "hkdf", + "once_cell", + "openssl-probe", + "rand_core 0.5.1", + "rocksdb", + "secp256k1", + "serde", + "serde_json", + "sha2 0.10.8", + "strum", + "thiserror", + "tokio", + "tracing", + "unc-chain", + "unc-chain-configs", + "unc-chain-primitives", + "unc-client", + "unc-client-primitives", + "unc-crypto", + "unc-epoch-manager", + "unc-indexer", + "unc-indexer-primitives", + "unc-network", + "unc-o11y", + "unc-primitives", + "unc-primitives-core", + "unc-store", +] + +[[package]] +name = "unc-network" +version = "0.1.0" +dependencies = [ + "actix", + "anyhow", + "arc-swap", + "assert_matches", + "async-trait", + "borsh 1.3.1", + "bytes", + "bytesize", + "chrono", + "criterion", + "crossbeam-channel", + "derive_more", + "futures", + "futures-util", + "im", + "itertools", + "lru", + "once_cell", + "opentelemetry", + "parking_lot 0.12.1", + "pin-project", + "pretty_assertions", + "protobuf 3.4.0", + "protobuf-codegen", + "rand 0.8.5", + "rand_xorshift", + "rayon", + "rlimit", + "serde", + "smart-default", + "strum", + "stun", + "tempfile", + "thiserror", + "time", + "tokio", + "tokio-stream", + "tokio-util 0.7.10", + "tracing", + "turn", + "unc-async", + "unc-crypto", + "unc-fmt", + "unc-o11y", + "unc-performance-metrics", + "unc-performance-metrics-macros", + "unc-primitives", + "unc-stable-hasher", + "unc-store", + "webrtc-util", +] + +[[package]] +name = "unc-o11y" +version = "0.1.0" +dependencies = [ + "actix", + "base64 0.21.7", + "bencher", + "clap", + "itoa", + "once_cell", + "opentelemetry", + "opentelemetry-otlp", + "opentelemetry-semantic-conventions", + "prometheus", + "serde", + "serde_json", + "smartstring", + "strum", + "thiserror", + "tokio", + "tracing", + "tracing-appender", + "tracing-opentelemetry", + "tracing-subscriber", + "unc-crypto", + "unc-fmt", + "unc-primitives-core", +] + +[[package]] +name = "unc-parameters" +version = "0.1.0" +dependencies = [ + "assert_matches", + "borsh 1.3.1", + "clap", + "enum-map", + "insta", + "num-rational", + "serde", + "serde_repr", + "serde_yaml", + "strum", + "thiserror", + "unc-account-id", + "unc-primitives-core", +] + +[[package]] +name = "unc-performance-metrics" +version = "0.1.0" +dependencies = [ + "actix", + "bitflags 1.3.2", + "bytes", + "bytesize", + "futures", + "libc", + "once_cell", + "strum", + "tokio", + "tokio-util 0.7.10", + "tracing", +] + +[[package]] +name = "unc-performance-metrics-macros" +version = "0.1.0" +dependencies = [ + "quote", + "syn 2.0.52", +] + +[[package]] +name = "unc-ping" +version = "0.1.0" +dependencies = [ + "actix-web", + "anyhow", + "chrono", + "clap", + "once_cell", + "prometheus", + "tokio", + "tracing", + "unc-async", + "unc-jsonrpc", + "unc-network", + "unc-o11y", + "unc-primitives", +] + +[[package]] +name = "unc-pool" +version = "0.1.0" +dependencies = [ + "borsh 1.3.1", + "once_cell", + "rand 0.8.5", + "unc-crypto", + "unc-o11y", + "unc-primitives", +] + +[[package]] +name = "unc-primitives" +version = "0.1.0" +dependencies = [ + "arbitrary", + "assert_matches", + "base64 0.21.7", + "bencher", + "borsh 1.3.1", + "bytesize", + "cfg-if 1.0.0", + "chrono", + "derive_more", + "easy-ext", + "enum-map", + "hex", + "insta", + "num-rational", + "once_cell", + "primitive-types", + "rand 0.8.5", + "rand_chacha 0.3.1", + "reed-solomon-erasure", + "serde", + "serde_json", + "serde_with", + "serde_yaml", + "sha3", + "smart-default", + "strum", + "thiserror", + "time", + "tracing", + "unc-crypto", + "unc-fmt", + "unc-o11y", + "unc-parameters", + "unc-primitives-core", + "unc-rpc-error-macro", + "unc-stdx", + "unc-vm-runner", +] + +[[package]] +name = "unc-primitives-core" +version = "0.1.0" +dependencies = [ + "arbitrary", + "base64 0.21.7", + "borsh 1.3.1", + "bs58", + "derive_more", + "enum-map", + "insta", + "num-rational", + "serde", + "serde_json", + "serde_repr", + "serde_with", + "sha2 0.10.8", + "strum", + "thiserror", + "unc-account-id", +] + +[[package]] +name = "unc-rpc-error-core" +version = "0.1.0" +dependencies = [ + "quote", + "serde", + "serde_json", + "syn 2.0.52", +] + +[[package]] +name = "unc-rpc-error-macro" +version = "0.1.0" +dependencies = [ + "fs2", + "serde", + "serde_json", + "syn 2.0.52", + "unc-rpc-error-core", +] + +[[package]] +name = "unc-stable-hasher" +version = "0.1.0" + +[[package]] +name = "unc-state-parts" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "once_cell", + "sha2 0.10.8", + "tokio", + "tracing", + "unc-async", + "unc-jsonrpc", + "unc-network", + "unc-o11y", + "unc-ping", + "unc-primitives", +] + +[[package]] +name = "unc-state-parts-dump-check" +version = "0.1.0" +dependencies = [ + "actix", + "actix-web", + "anyhow", + "borsh 1.3.1", + "clap", + "cloud-storage", + "framework", + "once_cell", + "reqwest", + "tokio", + "tracing", + "unc-client", + "unc-jsonrpc", + "unc-o11y", + "unc-primitives", + "unc-primitives-core", + "unc-store", +] + +[[package]] +name = "unc-stdx" +version = "0.1.0" + +[[package]] +name = "unc-store" +version = "0.1.0" +dependencies = [ + "actix", + "actix-rt", + "anyhow", + "assert_matches", + "bencher", + "borsh 1.3.1", + "bytesize", + "crossbeam", + "derive_more", + "elastic-array", + "enum-map", + "fs2", + "hex", + "insta", + "itertools", + "itoa", + "lru", + "num_cpus", + "once_cell", + "rand 0.8.5", + "rayon", + "rlimit", + "rocksdb", + "serde", + "serde_json", + "strum", + "tempfile", + "thiserror", + "tokio", + "tracing", + "unc-chain", + "unc-chain-configs", + "unc-chunks", + "unc-crypto", + "unc-fmt", + "unc-o11y", + "unc-parameters", + "unc-primitives", + "unc-stdx", + "unc-vm-runner", +] + +[[package]] +name = "unc-telemetry" +version = "0.1.0" +dependencies = [ + "actix", + "awc", + "futures", + "once_cell", + "openssl", + "serde", + "serde_json", + "tracing", + "unc-o11y", + "unc-performance-metrics", + "unc-performance-metrics-macros", + "unc-primitives", +] + +[[package]] +name = "unc-test-contracts" +version = "0.1.0" +dependencies = [ + "arbitrary", + "once_cell", + "rand 0.8.5", + "wasm-encoder 0.27.0", + "wasm-smith", + "wat", +] + +[[package]] +name = "unc-undo-block" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "framework", + "tracing", + "unc-chain", + "unc-chain-configs", + "unc-epoch-manager", + "unc-primitives", + "unc-store", +] + +[[package]] +name = "unc-vm-compiler" +version = "0.1.0" +dependencies = [ + "enumset", + "finite-wasm", + "hashbrown 0.14.3", + "rkyv", + "smallvec", + "target-lexicon 0.12.14", + "thiserror", + "tracing", + "unc-vm-types", + "unc-vm-vm", + "wasmparser 0.99.0", +] + +[[package]] +name = "unc-vm-compiler-singlepass" +version = "0.1.0" +dependencies = [ + "dynasm 2.0.0", + "dynasmrt 2.0.0", + "enumset", + "finite-wasm", + "hashbrown 0.14.3", + "lazy_static", + "memoffset 0.8.0", + "more-asserts", + "rayon", + "smallvec", + "strum", + "target-lexicon 0.12.14", + "tracing", + "unc-vm-compiler", + "unc-vm-types", + "unc-vm-vm", +] + +[[package]] +name = "unc-vm-compiler-test-derive" +version = "0.1.0" +dependencies = [ + "pretty_assertions", + "proc-macro2", + "quote", + "syn 2.0.52", + "trybuild", +] + +[[package]] +name = "unc-vm-engine" +version = "0.1.0" +dependencies = [ + "backtrace", + "cfg-if 1.0.0", + "crossbeam-queue", + "enumset", + "finite-wasm", + "lazy_static", + "memmap2", + "more-asserts", + "region", + "rkyv", + "rustc-demangle", + "rustix 0.37.27", + "target-lexicon 0.12.14", + "thiserror", + "tracing", + "unc-vm-compiler", + "unc-vm-types", + "unc-vm-vm", +] + +[[package]] +name = "unc-vm-runner" +version = "0.1.0" +dependencies = [ + "anyhow", + "arbitrary", + "assert_matches", + "base64 0.21.7", + "bolero", + "borsh 1.3.1", + "ed25519-dalek", + "enum-map", + "expect-test", + "finite-wasm", + "hex", + "loupe", + "memoffset 0.8.0", + "num-rational", + "once_cell", + "parity-wasm 0.41.0", + "parity-wasm 0.42.2", + "prefix-sum-vec", + "pwasm-utils", + "rand 0.8.5", + "ripemd", + "serde", + "serde_json", + "serde_repr", + "serde_with", + "sha2 0.10.8", + "sha3", + "strum", + "thiserror", + "tracing", + "unc-crypto", + "unc-parameters", + "unc-primitives-core", + "unc-stdx", + "unc-test-contracts", + "unc-vm-compiler", + "unc-vm-compiler-singlepass", + "unc-vm-engine", + "unc-vm-types", + "unc-vm-vm", + "wasm-encoder 0.27.0", + "wasm-smith", + "wasmer-compiler-near", + "wasmer-compiler-singlepass-near", + "wasmer-engine-near", + "wasmer-engine-universal-near", + "wasmer-runtime-core-near", + "wasmer-runtime-near", + "wasmer-types-near", + "wasmer-vm-near", + "wasmparser 0.78.2", + "wasmprinter", + "wasmtime", + "wat", + "zeropool-bn", +] + +[[package]] +name = "unc-vm-runner-fuzz" +version = "0.1.0" +dependencies = [ + "arbitrary", + "libfuzzer-sys", + "unc-parameters", + "unc-primitives", + "unc-test-contracts", + "unc-vm-runner", + "wasm-smith", + "wasmprinter", +] + +[[package]] +name = "unc-vm-test-api" +version = "0.1.0" +dependencies = [ + "anyhow", + "cfg-if 1.0.0", + "finite-wasm", + "indexmap 1.9.3", + "more-asserts", + "prefix-sum-vec", + "serial_test", + "target-lexicon 0.12.14", + "tempfile", + "test-log", + "thiserror", + "tracing", + "tracing-subscriber", + "unc-vm-compiler", + "unc-vm-compiler-singlepass", + "unc-vm-compiler-test-derive", + "unc-vm-engine", + "unc-vm-test-generator", + "unc-vm-types", + "unc-vm-vm", + "unc-vm-wast", + "wat", + "winapi", +] + +[[package]] +name = "unc-vm-test-generator" +version = "0.1.0" +dependencies = [ + "anyhow", + "target-lexicon 0.12.14", +] + +[[package]] +name = "unc-vm-types" +version = "0.1.0" +dependencies = [ + "bolero", + "indexmap 1.9.3", + "num-traits", + "rkyv", + "thiserror", +] + +[[package]] +name = "unc-vm-vm" +version = "0.1.0" +dependencies = [ + "backtrace", + "cc", + "cfg-if 1.0.0", + "finite-wasm", + "indexmap 1.9.3", + "libc", + "memoffset 0.8.0", + "more-asserts", + "region", + "rkyv", + "thiserror", + "tracing", + "unc-vm-types", + "wasmparser 0.99.0", + "winapi", +] + +[[package]] +name = "unc-vm-wast" +version = "0.1.0" +dependencies = [ + "anyhow", + "tempfile", + "thiserror", + "unc-vm-test-api", + "wast 40.0.0", +] + +[[package]] +name = "unc-wallet-contract" +version = "0.1.0" +dependencies = [ + "anyhow", + "unc-primitives-core", + "unc-vm-runner", +] + +[[package]] +name = "uncd" +version = "0.1.0" +dependencies = [ + "actix", + "anyhow", + "clap", + "cold-store-tool", + "framework", + "futures", + "once_cell", + "openssl-probe", + "opentelemetry", + "rayon", + "rlimit", + "rustc_version 0.4.0", + "serde", + "serde_json", + "shell-escape", + "state-viewer", + "thiserror", + "tikv-jemallocator", + "tokio", + "tracing", + "unc-amend-genesis", + "unc-chain-configs", + "unc-client", + "unc-config-utils", + "unc-crypto", + "unc-database-tool", + "unc-dyn-configs", + "unc-epoch-sync-tool", + "unc-flat-storage", + "unc-fork-network", + "unc-jsonrpc-primitives", + "unc-mirror", + "unc-network", + "unc-o11y", + "unc-performance-metrics", + "unc-ping", + "unc-primitives", + "unc-state-parts", + "unc-state-parts-dump-check", + "unc-store", + "unc-undo-block", + "yansi", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.52", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wasm-encoder" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b47b995b096a689358ca9de6c727b94351b95b390dbbf6b7021c22797d36caa" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e77053dc709db790691d3732cfc458adc5acc881dec524965c608effdcd9c581" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca90ba1b5b0a70d3d49473c5579951f3bddc78d47b59256d2f9d4922b150aca" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-smith" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb87569c81c298836d88405a28a68e8dfcd43b75220a4b4522831a63921b7dbc" +dependencies = [ + "arbitrary", + "flagset", + "indexmap 1.9.3", + "leb128", + "wasm-encoder 0.12.0", + "wasmparser 0.85.0", +] + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmer-compiler-near" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fdae7245128f284476e6db9653ef0a15b011975091bcd7f9d7303132409662" +dependencies = [ + "enumset", + "rkyv", + "smallvec", + "target-lexicon 0.12.14", + "thiserror", + "wasmer-types-near", + "wasmer-vm-near", + "wasmparser 0.78.2", +] + +[[package]] +name = "wasmer-compiler-singlepass-near" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac4af0e438015585eb27b2c6f6869c58c540bfe27408b686b1778470bf301050" +dependencies = [ + "byteorder", + "dynasm 1.2.3", + "dynasmrt 1.2.3", + "lazy_static", + "memoffset 0.6.5", + "more-asserts", + "rayon", + "smallvec", + "wasmer-compiler-near", + "wasmer-types-near", + "wasmer-vm-near", +] + +[[package]] +name = "wasmer-engine-near" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4048411cabb2c94c7d8d11d9d0282cc6b15308b61ebc1e122c40e89865ebb5c5" +dependencies = [ + "backtrace", + "enumset", + "lazy_static", + "memmap2", + "more-asserts", + "rustc-demangle", + "target-lexicon 0.12.14", + "thiserror", + "wasmer-compiler-near", + "wasmer-types-near", + "wasmer-vm-near", +] + +[[package]] +name = "wasmer-engine-universal-near" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f31c3d2850ac7957406d3c9581d9435ea8126a26478709fa7e931b6f562b4d" +dependencies = [ + "cfg-if 1.0.0", + "enumset", + "leb128", + "region", + "rkyv", + "thiserror", + "wasmer-compiler-near", + "wasmer-engine-near", + "wasmer-types-near", + "wasmer-vm-near", + "winapi", +] + +[[package]] +name = "wasmer-runtime-core-near" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3fac37da3c625e98708c5dd92d3f642aaf700fd077168d3d0fff277ec6a165" +dependencies = [ + "bincode", + "blake3", + "borsh 0.9.3", + "cc", + "digest 0.8.1", + "errno 0.2.8", + "hex", + "indexmap 1.9.3", + "lazy_static", + "libc", + "nix 0.15.0", + "page_size", + "parking_lot 0.10.2", + "rustc_version 0.2.3", + "serde", + "serde-bench", + "serde_bytes", + "serde_derive", + "smallvec", + "target-lexicon 0.10.0", + "wasmparser 0.51.4", + "winapi", +] + +[[package]] +name = "wasmer-runtime-near" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "158e6fff11e5e1ef805af50637374d5bd43d92017beafa18992cdf7f3f7ae3e4" +dependencies = [ + "lazy_static", + "memmap", + "serde", + "serde_derive", + "wasmer-runtime-core-near", + "wasmer-singlepass-backend-near", +] + +[[package]] +name = "wasmer-singlepass-backend-near" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6edd0ba6c0bcf9b279186d4dbe81649dda3e5ef38f586865943de4dcd653f8" +dependencies = [ + "bincode", + "borsh 0.9.3", + "byteorder", + "dynasm 1.2.3", + "dynasmrt 1.2.3", + "lazy_static", + "libc", + "nix 0.15.0", + "serde", + "serde_derive", + "smallvec", + "wasmer-runtime-core-near", +] + +[[package]] +name = "wasmer-types-near" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba154adffb0fbd33f5dabd3788a1744d846b43e6e090d44269c7ee8fa5743e4" +dependencies = [ + "indexmap 1.9.3", + "rkyv", + "thiserror", +] + +[[package]] +name = "wasmer-vm-near" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a5585596f6e9915d606de944aece51626736fb1191aefb5b2ef108c6f7604a" +dependencies = [ + "backtrace", + "cc", + "cfg-if 1.0.0", + "indexmap 1.9.3", + "libc", + "memoffset 0.6.5", + "more-asserts", + "region", + "rkyv", + "thiserror", + "wasmer-types-near", + "winapi", +] + +[[package]] +name = "wasmparser" +version = "0.51.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeb1956b19469d1c5e63e459d29e7b5aa0f558d9f16fcef09736f8a265e6c10a" + +[[package]] +name = "wasmparser" +version = "0.78.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52144d4c78e5cf8b055ceab8e5fa22814ce4315d6002ad32cfd914f37c12fd65" + +[[package]] +name = "wasmparser" +version = "0.85.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "570460c58b21e9150d2df0eaaedbb7816c34bcec009ae0dcc976e40ba81463e7" +dependencies = [ + "indexmap 1.9.3", +] + +[[package]] +name = "wasmparser" +version = "0.99.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef3b717afc67f848f412d4f02c127dd3e35a0eecd58c684580414df4fde01d3" +dependencies = [ + "indexmap 1.9.3", + "url", +] + +[[package]] +name = "wasmparser" +version = "0.105.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83be9e0b3f9570dc1979a33ae7b89d032c73211564232b99976553e5c155ec32" +dependencies = [ + "indexmap 1.9.3", + "url", +] + +[[package]] +name = "wasmparser" +version = "0.115.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e06c0641a4add879ba71ccb3a1e4278fd546f76f1eafb21d8f7b07733b547cd5" +dependencies = [ + "indexmap 2.2.5", + "semver 1.0.22", +] + +[[package]] +name = "wasmprinter" +version = "0.2.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b0e5ed7a74a065637f0d7798ce5f29cadb064980d24b0c82af5200122fa0d8" +dependencies = [ + "anyhow", + "wasmparser 0.105.0", +] + +[[package]] +name = "wasmtime" +version = "14.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca54f6090ce46973f33a79f265924b204f248f91aec09229bce53d19d567c1a6" +dependencies = [ + "anyhow", + "bincode", + "bumpalo", + "cfg-if 1.0.0", + "fxprof-processed-profile", + "indexmap 2.2.5", + "libc", + "log", + "object", + "once_cell", + "paste", + "psm", + "serde", + "serde_derive", + "serde_json", + "target-lexicon 0.12.14", + "wasm-encoder 0.35.0", + "wasmparser 0.115.0", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "14.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54984bc0b5689da87a43d7c181d23092b4d5cfcbb7ae3eb6b917dd55865d95e6" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "wasmtime-cranelift" +version = "14.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cf3cee8be02f5006d21b773ffd6802f96a0b7d661ff2ad8a01fb93df458b1aa" +dependencies = [ + "anyhow", + "cfg-if 1.0.0", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli", + "log", + "object", + "target-lexicon 0.12.14", + "thiserror", + "wasmparser 0.115.0", + "wasmtime-cranelift-shared", + "wasmtime-environ", + "wasmtime-versioned-export-macros", +] + +[[package]] +name = "wasmtime-cranelift-shared" +version = "14.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420fd2a69bc162957f4c94f21c7fa08ecf60d916f4e87b56332507c555da381d" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", + "cranelift-native", + "gimli", + "object", + "target-lexicon 0.12.14", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "14.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb6a445ce2b2810127caee6c1b79b8da4ae57712b05556a674592c18b7500a14" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli", + "indexmap 2.2.5", + "log", + "object", + "serde", + "serde_derive", + "target-lexicon 0.12.14", + "thiserror", + "wasmparser 0.115.0", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-jit" +version = "14.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f0f6586c61125fbfc13c3108c3dd565d21f314dd5bac823b9a5b7ab576d21f1" +dependencies = [ + "addr2line", + "anyhow", + "bincode", + "cfg-if 1.0.0", + "cpp_demangle", + "gimli", + "log", + "object", + "rustc-demangle", + "rustix 0.38.31", + "serde", + "serde_derive", + "target-lexicon 0.12.14", + "wasmtime-environ", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "14.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109a9e46afe33580b952b14a4207354355f19bcdf0b47485b397b68409eaf553" +dependencies = [ + "once_cell", + "wasmtime-versioned-export-macros", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "14.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f67e6be36375c39cff57ed3b137ab691afbf2d9ba8ee1c01f77888413f218749" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "14.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07986b2327b5e7f535ed638fbde25990fc8f85400194fda0d26db71c7b685e" +dependencies = [ + "anyhow", + "cc", + "cfg-if 1.0.0", + "indexmap 2.2.5", + "libc", + "log", + "mach", + "memfd", + "memoffset 0.9.0", + "paste", + "rand 0.8.5", + "rustix 0.38.31", + "sptr", + "wasm-encoder 0.35.0", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "wasmtime-versioned-export-macros", + "wasmtime-wmemcheck", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-types" +version = "14.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e810a0d2e869abd1cb42bd232990f6bd211672b3d202d2ae7e70ffb97ed70ea3" +dependencies = [ + "cranelift-entity", + "serde", + "serde_derive", + "thiserror", + "wasmparser 0.115.0", +] + +[[package]] +name = "wasmtime-versioned-export-macros" +version = "14.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b5575a75e711ca6c36bb9ad647c93541cdc8e34218031acba5da3f35919dd3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "wasmtime-wmemcheck" +version = "14.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dafab2db172a53e23940e0fa3078c202f567ee5f13f4b42f66b694fab43c658" + +[[package]] +name = "wast" +version = "40.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb4f48a8b083dbc50e291e430afb8f524092bb00428957bcc63f49f856c64ac" +dependencies = [ + "leb128", + "memchr", + "unicode-width", +] + +[[package]] +name = "wast" +version = "201.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ef6e1ef34d7da3e2b374fd2b1a9c0227aff6cad596e1b24df9b58d0f6222faa" +dependencies = [ + "bumpalo", + "leb128", + "memchr", + "unicode-width", + "wasm-encoder 0.201.0", +] + +[[package]] +name = "wat" +version = "1.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453d5b37a45b98dee4f4cb68015fc73634d7883bbef1c65e6e9c78d454cf3f32" +dependencies = [ + "wast 201.0.0", +] + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webrtc-util" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" +dependencies = [ + "async-trait", + "bitflags 1.3.2", + "bytes", + "cc", + "ipnet", + "lazy_static", + "libc", + "log", + "nix 0.24.3", + "rand 0.8.5", + "thiserror", + "tokio", + "winapi", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.31", +] + +[[package]] +name = "wildmatch" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "495ec47bf3c1345005f40724f0269362c8556cbc43aed0526ed44cae1d35fceb" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if 1.0.0", + "windows-sys 0.48.0", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "xml-rs" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" + +[[package]] +name = "xshell" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce2107fe03e558353b4c71ad7626d58ed82efaf56c54134228608893c77023ad" +dependencies = [ + "xshell-macros", +] + +[[package]] +name = "xshell-macros" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e2c411759b501fb9501aac2b1b2d287a6e93e5bdcf13c25306b23e1b716dd0e" + +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "zeropool-bn" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e61de68ede9ffdd69c01664f65a178c5188b73f78faa21f0936016a888ff7c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand 0.8.5", + "rustc-hex", +] + +[[package]] +name = "zstd" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.9+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..132f558de --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,423 @@ +[workspace.package] +version = "0.1.0" # managed by cargo-workspaces, see below +authors = ["Hello Inc "] +edition = "2021" +rust-version = "1.75.0" +repository = "https://github.com/utnet-org/utility" +license = "MIT OR Apache-2.0" + +[workspace.metadata.workspaces] +# Shared version of all public crates in the workspace. +# This is only used for crates that are not stable. +# Most crates are not stable on purpose, as maintaining API compatibility is a +# significant developer time expense. Please think thoroughly before adding +# anything to the list of stable crates. +version = "0.1.0" +exclude = ["uncd"] + +[workspace] +resolver = "2" +members = [ + "chain/chain", + "chain/chunks", + "chain/client", + "chain/client-primitives", + "chain/epoch-manager", + "chain/indexer", + "chain/indexer-primitives", + "chain/jsonrpc", + "chain/jsonrpc-adversarial-primitives", + "chain/jsonrpc-primitives", + "chain/jsonrpc/client", + "chain/jsonrpc/fuzz", + "chain/jsonrpc/jsonrpc-tests", + "chain/network", + "chain/pool", + "chain/telemetry", + "core/async", + "core/chain-configs", + "core/crypto", + "core/dyn-configs", + "core/o11y", + "core/parameters", + "core/primitives", + "core/primitives-core", + "core/store", + "framework", + "genesis-tools/genesis-csv-to-json", + "genesis-tools/genesis-populate", + "genesis-tools/keypair-generator", + "integration-tests", + "runtime/unc-vm/test-api", + "runtime/unc-vm/compiler", + "runtime/unc-vm/compiler-singlepass", + "runtime/unc-vm/engine", + "runtime/unc-vm/vm", + "runtime/unc-vm/types", + "runtime/unc-vm/wast", + "runtime/unc-vm/compiler-test-derive", + "runtime/unc-vm-runner", + "runtime/unc-vm-runner/fuzz", + "runtime/unc-wallet-contract", + "runtime/runtime", + "runtime/runtime-params-estimator", + "runtime/runtime-params-estimator/estimator-warehouse", + "test-utils/actix-test-utils", + "test-utils/runtime-tester", + "test-utils/runtime-tester/fuzz", + "test-utils/store-validator", + "test-utils/testlib", + "test-utils/style", + "tools/database", + "tools/chainsync-loadtest", + "tools/fork-network", + "tools/indexer/example", + "tools/mirror", + "tools/mock-node", + "tools/ping", + "tools/restaked", + "tools/rpctypegen/core", + "tools/rpctypegen/macro", + "tools/speedy_sync", + "tools/state-parts", + "tools/state-parts-dump-check", + "tools/state-viewer", + "tools/storage-usage-delta-calculator", + "tools/themis", + "tools/undo-block", + "utils/config", + "utils/fmt", + "utils/mainnet-res", + "utils/unc-cache", + "utils/stdx", + "uncd", +] + +[workspace.lints.rust] +warnings = "deny" + +[workspace.lints.clippy] +all = { level = "allow", priority = -100 } +correctness = { level = "deny", priority = -50 } +suspicious = { level = "deny", priority = -50 } +perf = { level = "deny", priority = -50 } +# overrides clippy::perf = "deny": https://github.com/rust-lang/rust-clippy/issues/8111 +single_char_pattern = "allow" +clone_on_copy = "deny" +derivable_impls = "deny" +redundant_clone = "deny" +len_zero = "deny" + + +[workspace.dependencies] +actix = "0.13.0" +actix-cors = "0.6.1" +actix-http = "3.3" +actix-rt = "2" +actix-web = "4.1" +anyhow = "1.0.62" +arbitrary = { version = "1.2.3", features = ["derive"] } +arc-swap = "1.5" +assert_matches = "1.5.0" +async-recursion = "1.0.4" +async-trait = "0.1.58" +awc = { version = "3", features = ["openssl"] } +backtrace = "0.3" +base64 = "0.21" +bencher = "0.1.5" +bitflags = "1.2" +blake2 = "0.9.1" +bn = { package = "zeropool-bn", version = "0.5.11", default-features = false } +bolero = { version = "0.10.0", git = "https://github.com/Ekleog-NEAR/bolero", rev = "56da8e6d1d018519a30b36d85d3a53fe35a42eaf", features = ["arbitrary"] } +borsh = { version = "1.0.0", features = ["derive", "rc"] } +bs58 = "0.4" +bytes = "1" +bytesize = { version = "1.1", features = ["serde"] } +c2-chacha = "0.3" +cargo_metadata = "0.14.1" +cc = "1.0" +cfg-if = "1.0" +chrono = { version = "0.4.19", features = ["serde"] } +clap = { version = "4.2.0", features = ["derive", "env", "string"] } +cloud-storage = "0.11.1" +conqueue = "0.4.0" +cpu-time = "1.0" +criterion = { version = "0.5.1", default_features = false, features = ["html_reports", "cargo_bench_support"] } +crossbeam = "0.8" +crossbeam-channel = "0.5.8" +crossbeam-queue = "0.3.8" +csv = "1.2.1" +curve25519-dalek = { version = "4.1.1", default-features = false, features = ["alloc", "precomputed-tables", "rand_core"] } +derive-enum-from-into = "0.1.1" +derive_more = "0.99.9" +dirs = "4" +dynasm = "2.0" +dynasmrt = "2.0" +easy-ext = "0.2" +ed25519-dalek = { version = "2.1.0", default-features = false, features = ["hazmat", "rand_core"] } +elastic-array = "0.11" +enum-map = "2.1.0" +enumset = "1.0" +expect-test = "1.3.0" +finite-wasm = "0.5.0" +flate2 = "1.0.22" +fs2 = "0.4" +futures = "0.3.5" +futures-util = "0.3" +genesis-populate = { path = "genesis-tools/genesis-populate" } +hashbrown = "0.14.2" +hex = { version = "0.4.2", features = ["serde"] } +hex-literal = "0.2" +hkdf = "0.12.3" +hyper = { version = "0.14", features = ["full"] } +hyper-tls = "0.5.0" +im = "15" +indexmap = "1.6" +indicatif = { version = "0.15.0", features = ["with_rayon"] } +insta = { version = "1.34.0", features = ["json", "yaml", "redactions"] } +integration-tests = { path = "integration-tests" } +itertools = "0.10.0" +itoa = "1.0" +json_comments = "0.2.1" +lazy_static = "1.4" +leb128 = "0.2" +libc = "0.2.81" +libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] } +log = "0.4" +loupe = "0.1" +lru = "0.7.2" +memmap2 = "0.5" +memoffset = "0.8" +more-asserts = "0.2" +unc-account-id = { version = "0.1.0", features = ["internal_unstable", "serde", "borsh"] } +unc-actix-test-utils = { path = "test-utils/actix-test-utils" } +unc-amend-genesis = { path = "tools/amend-genesis" } +unc-database-tool= { path = "tools/database" } +unc-async = { path = "core/async" } +unc-cache = { path = "utils/unc-cache", version = "0.1.0" } +unc-chain = { path = "chain/chain" } +unc-chain-configs = { path = "core/chain-configs", version = "0.1.0" } +unc-chain-primitives = { path = "chain/chain-primitives", version = "0.1.0" } +unc-chunks = { path = "chain/chunks" } +unc-chunks-primitives = { path = "chain/chunks-primitives", version = "0.1.0" } +unc-client = { path = "chain/client" } +unc-client-primitives = { path = "chain/client-primitives", version = "0.1.0" } +unc-cold-store-tool = { path = "tools/cold-store", package = "cold-store-tool" } +unc-config-utils = { path = "utils/config", version = "0.1.0" } +framework = { path = "framework" } +unc-crypto = { path = "core/crypto", version = "0.1.0" } +unc-dyn-configs = { path = "core/dyn-configs", version = "0.1.0" } +unc-epoch-manager = { path = "chain/epoch-manager" } +unc-epoch-sync-tool = { path = "tools/epoch-sync"} +unc-flat-storage = { path = "tools/flat-storage" } +unc-fork-network = { path = "tools/fork-network" } +unc-fmt = { path = "utils/fmt", version = "0.1.0" } +unc-indexer = { path = "chain/indexer" } +unc-indexer-primitives = { path = "chain/indexer-primitives", version = "0.1.0" } +unc-jsonrpc = { path = "chain/jsonrpc" } +unc-jsonrpc-adversarial-primitives = { path = "chain/jsonrpc-adversarial-primitives" } +unc-jsonrpc-client = { path = "chain/jsonrpc/client" } +unc-jsonrpc-primitives = { path = "chain/jsonrpc-primitives", features = ["full"], version = "0.1.0" } +unc-jsonrpc-tests = { path = "chain/jsonrpc/jsonrpc-tests" } +unc-mainnet-res = { path = "utils/mainnet-res" } +unc-mirror = { path = "tools/mirror" } +unc-network = { path = "chain/network" } +unc-o11y = { path = "core/o11y", version = "0.1.0" } +unc-parameters = { path = "core/parameters", version = "0.1.0" } +unc-performance-metrics = { path = "utils/unc-performance-metrics" } +unc-performance-metrics-macros = { path = "utils/unc-performance-metrics-macros" } +unc-ping = { path = "tools/ping" } +unc-pool = { path = "chain/pool" } +unc-primitives = { path = "core/primitives", version = "0.1.0" } +unc-primitives-core = { path = "core/primitives-core", version = "0.1.0" } +unc-rpc-error-core = { path = "tools/rpctypegen/core", version = "0.1.0" } +unc-rpc-error-macro = { path = "tools/rpctypegen/macro", version = "0.1.0" } +unc-stable-hasher = { path = "utils/unc-stable-hasher", version = "0.1.0" } +unc-state-parts = { path = "tools/state-parts" } +unc-state-parts-dump-check = { path = "tools/state-parts-dump-check" } +unc-state-viewer = { path = "tools/state-viewer", package = "state-viewer" } +unc-store = { path = "core/store" } +unc-telemetry = { path = "chain/telemetry" } +unc-test-contracts = { path = "runtime/unc-test-contracts" } +unc-undo-block = { path = "tools/undo-block" } +unc-vm-test-api = { path = "runtime/unc-vm/test-api" } +unc-vm-compiler = { path = "runtime/unc-vm/compiler", version = "0.1.0" } +unc-vm-compiler-singlepass = { path = "runtime/unc-vm/compiler-singlepass", version = "0.1.0" } +unc-vm-compiler-test-derive = { path = "runtime/unc-vm/compiler-test-derive" } +unc-vm-engine = { path = "runtime/unc-vm/engine", version = "0.1.0" } +unc-vm-engine-universal = { path = "runtime/unc-vm/engine-universal" } +unc-vm-runner = { path = "runtime/unc-vm-runner", version = "0.1.0" } +unc-vm-test-generator = { path = "runtime/unc-vm/test-generator" } +unc-vm-types = { path = "runtime/unc-vm/types", version = "0.1.0" } +unc-vm-vm = { path = "runtime/unc-vm/vm", version = "0.1.0" } +unc-vm-wast = { path = "runtime/unc-vm/wast" } +unc-wallet-contract = { path = "runtime/unc-wallet-contract" } +nix = "0.24" +node-runtime = { path = "runtime/runtime" } +num-bigint = "0.3" +num_cpus = "1.11" +num-rational = { version = "0.3.1", features = ["serde"] } +num-traits = "0.2.15" +once_cell = "1.13.1" +openssl = { version = "0.10.60", features = ["vendored"] } +openssl-probe = "0.1.4" +opentelemetry = { version = "0.17.0", features = ["rt-tokio", "trace"] } +opentelemetry-otlp = "0.10.0" +opentelemetry-semantic-conventions = "0.9.0" +paperclip = { version = "0.8.0", features = ["actix4"] } +parity-wasm = { version = "0.42", default-features = false } +parity-wasm_41 = { package = "parity-wasm", version = "0.41" } +parking_lot = "0.12.1" +percent-encoding = "2.2.0" +pin-project = "1.0" +prefix-sum-vec = "0.1.2" +pretty_assertions = "1.2" +primitive-types = { version = "0.10", default-features = false } +proc-macro2 = "1.0.64" +prometheus = "0.13.1" +protobuf = "3.0.1" +protobuf-codegen = "3.0.1" +pwasm-utils_12 = { package = "pwasm-utils", version = "0.12" } +quote = "1.0" +rand = "0.8.5" +rand_chacha = "0.3.1" +rand_core = "0.5" +rand_hc = "0.3.1" +rand_xorshift = "0.3" +rayon = "1.5" +redis = "0.23.0" +reed-solomon-erasure = "4" +regex = "1.7.1" +region = "3.0" +reqwest = { version = "0.11.14", features = ["blocking"] } +ripemd = "0.1.1" +rkyv = "0.7.31" +rlimit = "0.7" +rlp = "0.5.2" +rocksdb = { version = "0.21.0", default-features = false, features = ["snappy", "lz4", "zstd", "zlib", "jemalloc"] } +runtime-tester = { path = "test-utils/runtime-tester" } +rusqlite = { version = "0.29.0", features = ["bundled", "chrono", "functions"] } +rustc-demangle = "0.1" +rust-s3 = { version = "0.32.3", features = ["blocking"] } +rustix = "0.37" +rsa = { version = "0.9.6", features = ["sha2", "getrandom"] } +rsa-export = '0.3.3' +secp256k1 = { version = "0.27.0", features = ["recovery", "rand-std"] } +semver = "1.0.4" +serde = { version = "1.0.136", features = ["alloc", "derive", "rc"] } +serde_ignored = "0.1" +serde_json = "1.0.68" +serde_repr = "0.1.8" +serde_with = { version = "3.0", features = ["base64"] } +serde_yaml = "0.9" +serial_test = "0.5" +sha2 = "0.10" +sha3 = "0.10" +shell-escape = "0.1.5" +smallvec = "1.6" +smart-default = "0.6" +smartstring = "1.0.1" +strum = { version = "0.24", features = ["derive"] } +stun = "0.4" +subtle = "2.2" +syn = { version = "2.0.4", features = ["extra-traits", "full"] } +sysinfo = "0.24.5" +tar = "0.4.38" +target-lexicon = { version = "0.12.2", default-features = false } +tempfile = "3.3" +testlib = { path = "test-utils/testlib" } +test-log = { version = "0.2", default-features = false, features = ["trace"] } +thiserror = "1.0.30" +tikv-jemallocator = "0.5.0" +time = { version = "0.3.9", features = ["parsing", "serde"] } +tokio = { version = "1.28", features = [ + "fs", + "macros", + "net", + "rt-multi-thread", + "sync", + "time", +] } +tokio-stream = { version = "0.1.2", features = ["net"] } +tokio-util = { version = "0.7.1", features = ["codec", "io"] } +toml = "0.5.8" +tqdm = "0.4.4" +tracing = { version = "0.1.36", features = ["std"] } +tracing-appender = "0.2.2" +tracing-opentelemetry = "0.17.0" +tracing-span-tree = "0.1" +tracing-subscriber = { version = "0.3.15", features = ["env-filter", "fmt", "registry", "std"] } +trybuild = "1.0.11" +turn = "0.6" +validator = "0.12" +wasm-encoder = "0.27.0" +wasmer-compiler = { package = "wasmer-compiler-near", version = "=2.4.1" } +wasmer-compiler-singlepass = { package = "wasmer-compiler-singlepass-near", version = "=2.4.1" } +wasmer-engine = { package = "wasmer-engine-near", version = "=2.4.1" } +wasmer-engine-universal = { package = "wasmer-engine-universal-near", version = "=2.4.1", features = ["compiler"] } +wasmer-runtime = { version = "0.18.0", package = "wasmer-runtime-near", features = ["default-backend-singlepass"], default-features = false } +wasmer-runtime-core = { version = "0.18.2", package = "wasmer-runtime-core-near" } +wasmer-types = { package = "wasmer-types-near", version = "=2.4.1" } +wasmer-vm = { package = "wasmer-vm-near", version = "=2.4.1" } +wasmparser = "0.78" # TODO: unify at least the versions of wasmparser we have in our codebase +wasmprinter = "0.2" +wasm-smith = "0.10" +wasmtime = { version = "14.0.4", default-features = false, features = ["cranelift"] } +wast = "40.0" +wat = "1.0.40" +webrtc-util = "0.7" +winapi = { version = "0.3", features = ["winbase", "memoryapi", "errhandlingapi", "winnt", "impl-default"] } +xshell = "0.2.1" +xz2 = "0.1.6" +yansi = "0.5.1" + +stdx = { package = "unc-stdx", path = "utils/stdx", version = "0.1.0" } + +[patch.crates-io] + +# Note that "bench" profile inherits from "release" profile and +# "test" profile inherits from "dev" profile. +# https://doc.rust-lang.org/cargo/reference/profiles.html#test + +[profile.dev] +panic = 'abort' + +[profile.release] +overflow-checks = true +panic = 'abort' +lto = "fat" +codegen-units = 1 + +# A much faster to compile version of `release`, for development use. +[profile.dev-release] +inherits = "release" +lto = false +codegen-units = 16 +debug-assertions = true + +# Used for fuzzing, LTO is ill-supported as of 2023-09 and so should not be enabled. +[profile.fuzz] +inherits = "dev" +opt-level = 3 +incremental = false +codegen-units = 1 + +# Compile some dependencies with optimizations to speed up tests. +[profile.dev.package.hex] +opt-level = 3 +[profile.dev.package.rand] +opt-level = 3 +[profile.dev.package.bs58] +opt-level = 3 +[profile.dev.package.sha2] +opt-level = 3 +[profile.dev.package.curve25519-dalek] +opt-level = 3 +[profile.dev.package.unsafe-libyaml] +opt-level = 3 +[profile.dev.package.hashbrown] +opt-level = 3 +[profile.dev.package.dynasmrt] +opt-level = 3 +[profile.dev.package."*"] +opt-level = 1 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..49c0db065 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,46 @@ +# syntax=docker/dockerfile-upstream:experimental + +FROM ubuntu:22.04 as build + +RUN apt-get update -qq && apt-get install -y \ + git \ + cmake \ + g++ \ + pkg-config \ + libssl-dev \ + curl \ + llvm \ + clang \ + && rm -rf /var/lib/apt/lists/* + +COPY ./rust-toolchain.toml /tmp/rust-toolchain.toml + +ENV RUSTUP_HOME=/usr/local/rustup \ + CARGO_HOME=/usr/local/cargo \ + PATH=/usr/local/cargo/bin:$PATH + +RUN curl https://sh.rustup.rs -sSf | \ + sh -s -- -y --no-modify-path --default-toolchain none + +VOLUME [ /near ] +WORKDIR /near +COPY . . + +ENV PORTABLE=ON +ARG make_target= +RUN make CARGO_TARGET_DIR=/tmp/target \ + "${make_target:?make_target not set}" + +# Docker image +FROM ubuntu:22.04 + +EXPOSE 3030 24567 + +RUN apt-get update -qq && apt-get install -y \ + libssl-dev ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/run_docker.sh /usr/local/bin/run.sh +COPY --from=build /tmp/target/release/uncd /usr/local/bin/ + +CMD ["/usr/local/bin/run.sh"] diff --git a/Justfile b/Justfile new file mode 100644 index 000000000..e4d9b53f9 --- /dev/null +++ b/Justfile @@ -0,0 +1,98 @@ +# FIXME: some of these tests don't work very well on MacOS at the moment. Should fix +# them at earliest convenience :) +# Also in addition to this, the `nextest-integration` test is currently disabled on macos +with_macos_excludes := if os() == "macos" { + "--exclude node-runtime --exclude runtime-params-estimator --exclude unc-network --exclude estimator-warehouse" +} else { + "" +} +nightly_flags := "--features nightly,test_features" + +export RUST_BACKTRACE := env("RUST_BACKTRACE", "short") +ci_hack_nextest_profile := if env("CI_HACKS", "0") == "1" { "--profile ci" } else { "" } + +# all the tests, as close to CI as possible +test: test-ci test-extra + +# only the tests that are exactly the same as the ones in CI +test-ci: (nextest "stable") (nextest "nightly") python-style-checks + +# tests that are as close to CI as possible, but not exactly the same code +test-extra: check-lychee + +# all cargo tests, TYPE is "stable" or "nightly" +nextest TYPE *FLAGS: (nextest-unit TYPE FLAGS) (nextest-integration TYPE FLAGS) + +# cargo unit tests, TYPE is "stable" or "nightly" +nextest-unit TYPE *FLAGS: + cargo nextest run \ + --locked \ + --workspace \ + --exclude integration-tests \ + --cargo-profile dev-release \ + {{ ci_hack_nextest_profile }} \ + {{ with_macos_excludes }} \ + {{ if TYPE == "nightly" { nightly_flags } \ + else if TYPE == "stable" { "" } \ + else { error("TYPE is neighter 'nightly' nor 'stable'") } }} \ + {{ FLAGS }} + +# cargo integration tests, TYPE is "stable" or "nightly" +[linux] +nextest-integration TYPE *FLAGS: + cargo nextest run \ + --locked \ + --package integration-tests \ + --cargo-profile dev-release \ + {{ ci_hack_nextest_profile }} \ + {{ if TYPE == "nightly" { nightly_flags } \ + else if TYPE == "stable" { "" } \ + else { error("TYPE is neither 'nightly' nor 'stable'") } }} \ + {{ FLAGS }} +# Note: when re-enabling this on macos, ci.yml will need to be adjusted to report code coverage again +[macos] +nextest-integration TYPE *FLAGS: + @echo "Nextest integration tests are currently disabled on macos!" + +# generate a codecov report for RULE +codecov RULE: + #!/usr/bin/env bash + set -euxo pipefail + # Note: macos seems to not support `source <()` as a way to set environment variables, but + # this variant seems to work on both linux and macos. + # TODO: remove the RUSTFLAGS hack, see also https://github.com/rust-lang/cargo/issues/13040 + cargo llvm-cov show-env --export-prefix | grep -v RUSTFLAGS > env + source ./env + export RUSTC_WORKSPACE_WRAPPER="{{ absolute_path("scripts/rustc-coverage-wrapper.sh") }}" + {{ just_executable() }} {{ RULE }} + cargo llvm-cov report --profile dev-release --codecov --output-path codecov.json + # See https://github.com/taiki-e/cargo-llvm-cov/issues/292 + find target -name '*.profraw' -delete + +# style checks from python scripts +python-style-checks: + python3 scripts/check_nightly.py + python3 scripts/check_pytests.py + python3 scripts/fix_nightly_feature_flags.py + ./scripts/formatting --check + +# lychee-based url validity checks +check-lychee: + # This is not actually run in CI. GITHUB_TOKEN can still be set locally by people who want + # to reproduce CI behavior in a better way. + git ls-files | grep 'md$\|mkd$\|html\?$' | xargs lychee {{ if env("GITHUB_TOKEN", "") != "" { "" } else { "-a 429" } }} + @echo {{ if env("GITHUB_TOKEN", "") != "" { "" } \ + else { "Note: 'Too Many Requests' errors are allowed here but not in CI, set GITHUB_TOKEN to check them" } }} + +# build target/rpc_errors_schema.json +build-rpc-errors-schema: + rm -f target/rpc_errors_schema.json + cargo check -p unc-jsonrpc --features dump_errors_schema + +# update chain/jsonrpc/res/rpc_errors_schema.json +update-rpc-errors-schema: build-rpc-errors-schema + cp target/rpc_errors_schema.json chain/jsonrpc/res/rpc_errors_schema.json + +# check chain/jsonrpc/res/rpc_errors_schema.json +check-rpc-errors-schema: build-rpc-errors-schema + diff target/rpc_errors_schema.json chain/jsonrpc/res/rpc_errors_schema.json diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 000000000..f49a4e16e --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 000000000..749aa1ecd --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,19 @@ +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..73fb2885d --- /dev/null +++ b/Makefile @@ -0,0 +1,96 @@ +export DOCKER_BUILDKIT = 1 +export CARGO_BUILD_RUSTFLAGS = -D warnings +export UNC_RELEASE_BUILD = no +export CARGO_TARGET_DIR = target + + +# By default, build a regular release +all: release + + +docker-framework: DOCKER_TAG ?= framework +docker-framework: + docker build -t $(DOCKER_TAG) -f Dockerfile --build-arg=make_target=uncd-release --progress=plain . + +docker-framework-sandbox: DOCKER_TAG ?= framework-sandbox +docker-framework-sandbox: + docker build -t $(DOCKER_TAG) -f Dockerfile --build-arg=make_target=uncd-sandbox-release --progress=plain . + +docker-framework-nightly: DOCKER_TAG ?= framework-nightly +docker-framework-nightly: + docker build -t $(DOCKER_TAG) -f Dockerfile --build-arg=make_target=uncd-nightly-release --progress=plain . + + +release: uncd-release + cargo build -p store-validator --release + cargo build -p genesis-populate --release + $(MAKE) sandbox-release + +uncd: uncd-release + @echo 'uncd binary ready in ./target/release/uncd' + +uncd-release: UNC_RELEASE_BUILD=release +uncd-release: + cargo build -p uncd --release + +uncd-debug: + cargo build -p uncd + +debug: uncd-debug + cargo build -p store-validator + cargo build -p genesis-populate + $(MAKE) sandbox + + +perf-release: UNC_RELEASE_BUILD=release +perf-release: + CARGO_PROFILE_RELEASE_DEBUG=true cargo build -p uncd --release --features performance_stats + cargo build -p store-validator --release --features framework/performance_stats + + +perf-debug: + cargo build -p uncd --features performance_stats + cargo build -p store-validator --features framework/performance_stats + + +nightly-release: uncd-nightly-release + cargo build -p store-validator --release --features framework/nightly,framework/performance_stats + cargo build -p genesis-populate --release --features framework/nightly,framework/performance_stats + +uncd-nightly-release: + cargo build -p uncd --release --features nightly,performance_stats + + +nightly-debug: + cargo build -p uncd --features nightly,performance_stats + cargo build -p store-validator --features framework/nightly,framework/performance_stats + cargo build -p genesis-populate --features framework/nightly,framework/performance_stats + + +assertions-release: UNC_RELEASE_BUILD=release +assertions-release: + CARGO_PROFILE_RELEASE_DEBUG=true CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS=true cargo build -p uncd --release --features performance_stats + +sandbox: CARGO_TARGET_DIR=sandbox +sandbox: uncd-sandbox + mkdir -p target/debug + ln -f sandbox/debug/uncd target/debug/uncd-sandbox + @ln -f sandbox/debug/uncd target/debug/unc-sandbox + +uncd-sandbox: + cargo build -p uncd --features sandbox + + +sandbox-release: CARGO_TARGET_DIR=sandbox +sandbox-release: uncd-sandbox-release + mkdir -p target/release + ln -f sandbox/release/uncd target/release/uncd-sandbox + @ln -f sandbox/release/uncd target/release/unc-sandbox + +uncd-sandbox-release: + cargo build -p uncd --features sandbox --release + + +.PHONY: docker-framework docker-framework-nightly release uncd debug +.PHONY: perf-release perf-debug nightly-release nightly-debug assertions-release sandbox +.PHONY: sandbox-release diff --git a/README.md b/README.md new file mode 100644 index 000000000..04721b7c8 --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +
+
+ +

+ +

+ +
+
+ + +## Reference implementation of UNC Protocol + +![Buildkite](https://img.shields.io/buildkite/0eae07525f8e44a19b48fa937813e2c21ee04aa351361cd851) +![Stable Status][stable-release] +![Prerelease Status][prerelease] +[![codecov][codecov-badge]][codecov-url] +[![Discord chat][discord-badge]][discord-url] +[![Telegram Group][telegram-badge]][telegram-url] + +[stable-release]: https://img.shields.io/github/v/release/utnet-org/utility?label=stable +[prerelease]: https://img.shields.io/github/v/release/utnet-org/utility?include_prereleases&label=prerelease +[ci-badge-master]: https://badge.buildkite.com/a81147cb62c585cc434459eedd1d25e521453120ead9ee6c64.svg?branch=master +[ci-url]: https://buildkite.com/utnet-org/utility +[codecov-badge]: https://codecov.io/gh/utnet-org/utility/branch/master/graph/badge.svg +[codecov-url]: https://codecov.io/gh/utnet-org/utility +[discord-badge]: https://img.shields.io/discord/490367152054992913.svg +[discord-url]: https://unc.chat +[telegram-badge]: https://cdn.jsdelivr.net/gh/Patrolavia/telegram-badge@8fe3382b3fd3a1c533ba270e608035a27e430c2e/chat.svg +[telegram-url]: https://t.me/cryptounc + +## About UNC + +UNC's purpose is to enable community-driven innovation to benefit people around the world. + +To achieve this purpose, *UNC* provides a developer platform where developers and entrepreneurs can create apps that put users back in control of their data and assets, which is the foundation of ["Open Web" movement][open-web-url]. + +One of the components of *UNC* is the UNC Protocol, an infrastructure for server-less applications and smart contracts powered by a blockchain. +UNC Protocol is built to deliver usability and scalability of modern PaaS like Firebase at fraction of the prices that blockchains like Ethereum charge. + +Overall, *UNC* provides a wide range of tools for developers to easily build applications: + - [JS Client library][js-api] to connect to UNC Protocol from your applications. + - [Rust][rust-sdk] and [JavaScript/TypeScript][js-sdk] SDKs to write smart contracts and stateful server-less functions. + - [Numerous examples][examples-url] with links to hack on them right inside your browser. + - [Lots of documentation][docs-url], with [Tutorials][tutorials-url] and [API docs][api-docs-url]. + +[open-web-url]: https://techcrunch.com/2016/04/10/1301496/ +[js-api]: https://github.com/utnet-org/utility/unc-api-js +[rust-sdk]: https://github.com/utnet-org/utility/unc-sdk-rs +[js-sdk]: https://github.com/utnet-org/utility/unc-sdk-js +[examples-url]: https://utnet-org/utility.dev +[docs-url]: https://docs.utnet-org/utility.org +[tutorials-url]: https://docs.utnet-org/utility.org/tutorials/welcome +[api-docs-url]: https://docs.utnet-org/utility.org/api/rpc/introduction + +## Join the Network + +The easiest way to join the network, is by using the `make release` command, which you can install as follows: + +```bash +cd utility +make release +``` + +You can join all the active networks: +``` +# mainnet or testnet node init +./target/release/uncd --home ~/.unc init --chain-id [testnet, mainnet] --download-genesis --download-config + +# localnet node init +./target/release/uncd localnet + +# node run +./target/release/uncd --home ~/.unc run +``` + + +To learn how to become validator, checkout [documentation](https://docs.utnet-org/utility.org/docs/develop/node/validator/staking-and-delegation). + +## Contributing + +The workflow and details of setup to contribute are described in [CONTRIBUTING.md](CONTRIBUTING.md), and security policy is described in [SECURITY.md](SECURITY.md). +To propose new protocol changes or standards use [Specification & Standards repository](https://github.com/nearprotocol/NEPs). + +## Getting in Touch + +We use Zulip for semi-synchronous technical discussion, feel free to chime in: + +https://utnet-org/utility.zulipchat.com/ + +For non-technical discussion and overall direction of the project, see our Discourse forum: + +https://gov.utnet-org/utility.org diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..d8f1ed0eb --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,46 @@ +# Security Policy + +Reference client for NEAR is held to the highest security standard. +This document defines the policy on how to report vulnerabilities and receive updates when security patches are released. + +If you have any suggestions or comments about the security policy, please email the [NEAR Security Team](mailto:security@near.org) at security@near.org + +## Reporting a vulnerability + +All security issues should be submitted on [hackenproof](https://hackenproof.com/unc). The team will review the submissions and decide whether they are eligible for bounty payouts. For more details, please check out the program description on the hackenproof website. + +## Handling & disclosure process + +1. Security report is received and assigned to an owner. This person will coordinate the process of evaluating, fixing, releasing and disclosing the issue. +2. After initial report is received, the evaluation process is performed. It's identified if the issue exists, its severity and which version / components of the code is affected. Additional review to identify similar issues also happens. +3. Fixes are implemented for all supported releases. These fixes are not publicly communicated but held in a private repo of the Security Team or locally. +4. A suggested announcement date for this vulnerability is chosen. The notification is drafted and includes patches to all supported versions and affected components. +5. On the announcement date, the [NEAR Security Update newsletter](https://groups.google.com/a/near.org/g/security-updates) is sent an announcement. The changes are fast tracked and merged into the public repository. At least 6 hours after the mailing list is notified, a copy of the advisory will be published across social channels. + +This process may take time, especially when coordinating with network participants and maintainers of other components in the ecosystem. +The goal will be to address issues in as short of a period as possible, but it's important that the process described above to ensure that disclosures are handled in a consistent manner. + +*Note:* If the Security Team identifies that an issue is mission-critical and requires a subset of network participants to update prior to newsletter announcement - this will be done in a manual way by communicating via direct channels. + +## Reward + +The discovery of the security vulnerabilities that include but are not limited to the following categories will be rewarded proportionally to their severity: +* Algorithmic, implementation, and economic issues that violate safety of the blockchain; +* Algorithmic, implementation, and economic issues that stall the blockchain or significantly throttle liveness; +* Algorithmic, implementation, and economic issues in the standard contracts developed by NEAR; +* Issues that expose the private data of the users, the developers, or the validators; + +The following are the necessary conditions for the reward: +* The vulnerability is disclosed to NEAR before it is disclosed publicly and NEAR is given sufficient time to fix it; +* The vulnerability is not disclosed to anyone else except the finder and NEAR before it is fixed; +* The vulnerability is not exploited until it is fixed. + +### Rewards platform + +We are using https://gitcoin.co/ to reward tokens. Meaning that every security vulnerability that you submit to us will be processed like a general work-item by an external contributor. To receive the reward, you would need to register on https://gitcoin.co/ and be able to receive the reward through it. Example of a reward for a security vulnerability finding: https://gitcoin.co/issue/near/community/5/4359 + + +## Receive Security Updates + +If you want to be informed about security vulnerabilities, please subscribe to the [NEAR Security Update newsletter](https://groups.google.com/a/near.org/g/security-updates). +The newsletter is very low traffic and only sent out where public disclosure of a vulnerability happens. diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 000000000..93e819b2f --- /dev/null +++ b/buf.yaml @@ -0,0 +1,4 @@ +version: v1 +breaking: + use: + - WIRE diff --git a/chain/chain-primitives/Cargo.toml b/chain/chain-primitives/Cargo.toml new file mode 100644 index 000000000..3f7ea0fc2 --- /dev/null +++ b/chain/chain-primitives/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "unc-chain-primitives" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "This crate hosts NEAR chain-related error types" +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +chrono.workspace = true +thiserror.workspace = true +tracing.workspace = true + +unc-primitives.workspace = true +unc-crypto.workspace = true + +[features] +new_epoch_sync = [] diff --git a/chain/chain-primitives/README.md b/chain/chain-primitives/README.md new file mode 100644 index 000000000..fd899e852 --- /dev/null +++ b/chain/chain-primitives/README.md @@ -0,0 +1,3 @@ +# unc-chain-primitives + +This crate hosts NEAR chain-related error types. \ No newline at end of file diff --git a/chain/chain-primitives/src/error.rs b/chain/chain-primitives/src/error.rs new file mode 100644 index 000000000..bb2ae0c21 --- /dev/null +++ b/chain/chain-primitives/src/error.rs @@ -0,0 +1,469 @@ +use std::io; + +use chrono::DateTime; +use chrono::Utc; + +use unc_primitives::block::BlockValidityError; +use unc_primitives::challenge::{ChunkProofs, ChunkState}; +use unc_primitives::errors::{BlockError, EpochError, StorageError}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardLayoutError; +use unc_primitives::sharding::{ChunkHash, ShardChunkHeader}; +use unc_primitives::types::{BlockHeight, EpochId, ShardId}; + +#[derive(thiserror::Error, Debug)] +pub enum QueryError { + #[error("Account ID {requested_account_id} is invalid")] + InvalidAccount { + requested_account_id: unc_primitives::types::AccountId, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("Account {requested_account_id} does not exist while viewing")] + UnknownAccount { + requested_account_id: unc_primitives::types::AccountId, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error( + "Contract code for contract ID {contract_account_id} has never been observed on the node" + )] + NoContractCode { + contract_account_id: unc_primitives::types::AccountId, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("Access key for public key {public_key} does not exist while viewing")] + UnknownAccessKey { + public_key: unc_crypto::PublicKey, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("Internal error occurred: {error_message}")] + InternalError { + error_message: String, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("Function call returned an error: {error_message}")] + ContractExecutionError { + error_message: String, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("The state of account {requested_account_id} is too large")] + TooLargeContractState { + requested_account_id: unc_primitives::types::AccountId, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// The block is already known + #[error("Block is known: {0}")] + BlockKnown(#[from] BlockKnownError), + #[error("Too many blocks being processed")] + TooManyProcessingBlocks, + /// Orphan block. + #[error("Orphan")] + Orphan, + /// Chunk is missing. + #[error("Chunk Missing (unavailable on the node): {0:?}")] + ChunkMissing(ChunkHash), + /// Chunks missing with header info. + #[error("Chunks Missing: {0:?}")] + ChunksMissing(Vec), + /// Block time is before parent block time. + #[error("Invalid Block Time: block time {1} before previous {0}")] + InvalidBlockPastTime(DateTime, DateTime), + /// Block time is from too much in the future. + #[error("Invalid Block Time: Too far in the future: {0}")] + InvalidBlockFutureTime(DateTime), + /// Block height is invalid (not previous + 1). + #[error("Invalid Block Height {0}")] + InvalidBlockHeight(BlockHeight), + /// Invalid block proposed signature. + #[error("Invalid Block Proposer Signature")] + InvalidBlockProposer, + /// Invalid state root hash. + #[error("Invalid State Root Hash")] + InvalidStateRoot, + /// Invalid block tx root hash. + #[error("Invalid Block Tx Root Hash")] + InvalidTxRoot, + /// Invalid chunk receipts root hash. + #[error("Invalid Chunk Receipts Root Hash")] + InvalidChunkReceiptsRoot, + /// Invalid chunk headers root hash. + #[error("Invalid Chunk Headers Root Hash")] + InvalidChunkHeadersRoot, + /// Invalid chunk tx root hash. + #[error("Invalid Chunk Tx Root Hash")] + InvalidChunkTxRoot, + /// Invalid receipts proof. + #[error("Invalid Receipts Proof")] + InvalidReceiptsProof, + /// Invalid outcomes proof. + #[error("Invalid Outcomes Proof")] + InvalidOutcomesProof, + /// Invalid state payload on state sync. + #[error("Invalid State Payload")] + InvalidStatePayload, + /// Invalid transactions in the block. + #[error("Invalid Transactions")] + InvalidTransactions, + /// Invalid Challenge Root (doesn't match actual challenge) + #[error("Invalid Challenge Root")] + InvalidChallengeRoot, + /// Invalid challenge (wrong signature or format). + #[error("Invalid Challenge")] + InvalidChallenge, + /// Incorrect (malicious) challenge (slash the sender). + #[error("Malicious Challenge")] + MaliciousChallenge, + /// Incorrect number of chunk headers + #[error("Incorrect Number of Chunk Headers")] + IncorrectNumberOfChunkHeaders, + /// Invalid chunk. + #[error("Invalid Chunk")] + InvalidChunk, + /// One of the chunks has invalid proofs + #[error("Invalid Chunk Proofs")] + InvalidChunkProofs(Box), + /// Invalid chunk state. + #[error("Invalid Chunk State")] + InvalidChunkState(Box), + #[error("Invalid Chunk State Witness")] + InvalidChunkStateWitness(String), + /// Invalid chunk mask + #[error("Invalid Chunk Mask")] + InvalidChunkMask, + /// The chunk height is outside of the horizon + #[error("Invalid Chunk Height")] + InvalidChunkHeight, + /// Invalid epoch hash + #[error("Invalid Epoch Hash")] + InvalidEpochHash, + /// `next_bps_hash` doens't correspond to the actual next block producers set + #[error("Invalid Next BP Hash")] + InvalidNextBPHash, + /// The block has a protocol version that's outdated + #[error("Invalid protocol version")] + InvalidProtocolVersion, + /// The block doesn't have approvals from 50% of the block producers + #[error("Not enough approvals")] + NotEnoughApprovals, + /// The information about the last final block is incorrect + #[error("Invalid finality info")] + InvalidFinalityInfo, + /// Invalid validator proposals in the block. + #[error("Invalid Validator Proposals")] + InvalidValidatorProposals, + /// Invalid Signature + #[error("Invalid Signature")] + InvalidSignature, + /// Invalid Approvals + #[error("Invalid Approvals")] + InvalidApprovals, + /// Invalid Gas Limit + #[error("Invalid Gas Limit")] + InvalidGasLimit, + /// Invalid Gas Price + #[error("Invalid Gas Price")] + InvalidGasPrice, + /// Invalid Gas Used + #[error("Invalid Gas Used")] + InvalidGasUsed, + /// Invalid Balance Burnt + #[error("Invalid Balance Burnt")] + InvalidBalanceBurnt, + /// Invalid shard id + #[error("Shard id {0} does not exist")] + InvalidShardId(ShardId), + /// Invalid shard id + #[error("Invalid state request: {0}")] + InvalidStateRequest(String), + /// Invalid VRF proof, or incorrect random_output in the header + #[error("Invalid Randomness Beacon Output")] + InvalidRandomnessBeaconOutput, + /// Invalid block merkle root. + #[error("Invalid Block Merkle Root")] + InvalidBlockMerkleRoot, + /// Invalid split shard ids. + #[error("Invalid Split Shard Ids when resharding. shard_id: {0}, parent_shard_id: {1}")] + InvalidSplitShardsIds(u64, u64), + /// Someone is not a validator. Usually happens in signature verification + #[error("Not A Validator")] + NotAValidator, + /// Someone is not a chunk validator. Happens if we're asked to validate a chunk we're not + /// supposed to validate, or to verify a chunk approval signed by a validator that isn't + /// supposed to validate the chunk. + #[error("Not A Chunk Validator")] + NotAChunkValidator, + /// Validator error. + #[error("Validator Error: {0}")] + ValidatorError(String), + /// Block out of bounds. Usually if received block is too far in the future or alternative fork. + #[error("Epoch Out Of Bounds: {:?}", _0)] + BlockOutOfBounds(CryptoHash), + /// Epoch out of bounds. Usually if received block is too far in the future or alternative fork. + #[error("Epoch Out Of Bounds: {:?}", _0)] + EpochOutOfBounds(EpochId), + /// A challenged block is on the chain that was attempted to become the head + #[error("Challenged block on chain")] + ChallengedBlockOnChain, + /// Block cannot be finalized. + #[error("Block cannot be finalized")] + CannotBeFinalized, + /// IO Error. + #[error("IO Error: {0}")] + IOErr(#[from] io::Error), + /// Not found record in the DB. + #[error("DB Not Found Error: {0}")] + DBNotFoundErr(String), + /// Storage error. Used for internal passing the error. + #[error("Storage Error: {0}")] + StorageError(#[from] StorageError), + /// GC error. + #[error("GC Error: {0}")] + GCError(String), + /// Anything else + #[error("Other Error: {0}")] + Other(String), +} + +/// For now StorageError can happen at any time from ViewClient because of +/// the used isolation level + running ViewClient in a separate thread. +pub trait LogTransientStorageError { + fn log_storage_error(self, message: &str) -> Self; +} + +impl LogTransientStorageError for Result { + fn log_storage_error(self, message: &str) -> Self { + if let Err(err) = &self { + tracing::error!(target: "client", "Transient storage error: {message}, {err}"); + } + self + } +} + +impl Error { + pub fn is_bad_data(&self) -> bool { + match self { + Error::BlockKnown(_) + | Error::TooManyProcessingBlocks + | Error::Orphan + | Error::ChunkMissing(_) + | Error::ChunksMissing(_) + | Error::InvalidChunkHeight + | Error::IOErr(_) + | Error::Other(_) + | Error::ValidatorError(_) + | Error::EpochOutOfBounds(_) + | Error::ChallengedBlockOnChain + | Error::CannotBeFinalized + | Error::StorageError(_) + | Error::GCError(_) + | Error::DBNotFoundErr(_) => false, + Error::InvalidBlockPastTime(_, _) + | Error::InvalidBlockFutureTime(_) + | Error::InvalidBlockHeight(_) + | Error::InvalidBlockProposer + | Error::InvalidChunk + | Error::InvalidChunkProofs(_) + | Error::InvalidChunkState(_) + | Error::InvalidChunkStateWitness(_) + | Error::InvalidChunkMask + | Error::InvalidStateRoot + | Error::InvalidTxRoot + | Error::InvalidChunkReceiptsRoot + | Error::InvalidOutcomesProof + | Error::InvalidChunkHeadersRoot + | Error::InvalidChunkTxRoot + | Error::InvalidReceiptsProof + | Error::InvalidStatePayload + | Error::InvalidTransactions + | Error::InvalidChallenge + | Error::InvalidSplitShardsIds(_, _) + | Error::MaliciousChallenge + | Error::IncorrectNumberOfChunkHeaders + | Error::InvalidEpochHash + | Error::InvalidNextBPHash + | Error::NotEnoughApprovals + | Error::InvalidFinalityInfo + | Error::InvalidValidatorProposals + | Error::InvalidSignature + | Error::InvalidApprovals + | Error::InvalidGasLimit + | Error::InvalidGasPrice + | Error::InvalidGasUsed + | Error::InvalidBalanceBurnt + | Error::InvalidShardId(_) + | Error::InvalidStateRequest(_) + | Error::InvalidRandomnessBeaconOutput + | Error::InvalidBlockMerkleRoot + | Error::InvalidProtocolVersion + | Error::NotAValidator + | Error::NotAChunkValidator + | Error::BlockOutOfBounds(_) + | Error::InvalidChallengeRoot => true, + } + } + + pub fn is_error(&self) -> bool { + match self { + Error::IOErr(_) | Error::Other(_) | Error::DBNotFoundErr(_) => true, + _ => false, + } + } + + /// Some blockchain errors are reported in the prometheus metrics. In such cases a report might + /// contain a label that specifies the type of error that has occured. For example when the node + /// receives a block with an invalid signature this would be reported as: + /// `unc_num_invalid_blocks{error="invalid_signature"}`. + /// This function returns the value of the error label for a specific instance of Error. + pub fn prometheus_label_value(&self) -> &'static str { + match self { + Error::BlockKnown(_) => "block_known", + Error::TooManyProcessingBlocks => "too_many_processing_blocks", + Error::Orphan => "orphan", + Error::ChunkMissing(_) => "chunk_missing", + Error::ChunksMissing(_) => "chunks_missing", + Error::InvalidChunkHeight => "invalid_chunk_height", + Error::IOErr(_) => "io_err", + Error::Other(_) => "other", + Error::ValidatorError(_) => "validator_error", + Error::EpochOutOfBounds(_) => "epoch_out_of_bounds", + Error::ChallengedBlockOnChain => "challenged_block_on_chain", + Error::CannotBeFinalized => "cannot_be_finalized", + Error::StorageError(_) => "storage_error", + Error::GCError(_) => "gc_error", + Error::DBNotFoundErr(_) => "db_not_found_err", + Error::InvalidBlockPastTime(_, _) => "invalid_block_past_time", + Error::InvalidBlockFutureTime(_) => "invalid_block_future_time", + Error::InvalidBlockHeight(_) => "invalid_block_height", + Error::InvalidBlockProposer => "invalid_block_proposer", + Error::InvalidChunk => "invalid_chunk", + Error::InvalidChunkProofs(_) => "invalid_chunk_proofs", + Error::InvalidChunkState(_) => "invalid_chunk_state", + Error::InvalidChunkStateWitness(_) => "invalid_chunk_state_witness", + Error::InvalidChunkMask => "invalid_chunk_mask", + Error::InvalidStateRoot => "invalid_state_root", + Error::InvalidTxRoot => "invalid_tx_root", + Error::InvalidChunkReceiptsRoot => "invalid_chunk_receipts_root", + Error::InvalidOutcomesProof => "invalid_outcomes_proof", + Error::InvalidChunkHeadersRoot => "invalid_chunk_headers_root", + Error::InvalidChunkTxRoot => "invalid_chunk_tx_root", + Error::InvalidReceiptsProof => "invalid_receipts_proof", + Error::InvalidStatePayload => "invalid_state_payload", + Error::InvalidTransactions => "invalid_transactions", + Error::InvalidChallenge => "invalid_challenge", + Error::InvalidSplitShardsIds(_, _) => "invalid_split_shard_ids", + Error::MaliciousChallenge => "malicious_challenge", + Error::IncorrectNumberOfChunkHeaders => "incorrect_number_of_chunk_headers", + Error::InvalidEpochHash => "invalid_epoch_hash", + Error::InvalidNextBPHash => "invalid_next_bp_hash", + Error::NotEnoughApprovals => "not_enough_approvals", + Error::InvalidFinalityInfo => "invalid_finality_info", + Error::InvalidValidatorProposals => "invalid_validator_proposals", + Error::InvalidSignature => "invalid_signature", + Error::InvalidApprovals => "invalid_approvals", + Error::InvalidGasLimit => "invalid_gas_limit", + Error::InvalidGasPrice => "invalid_gas_price", + Error::InvalidGasUsed => "invalid_gas_used", + Error::InvalidBalanceBurnt => "invalid_balance_burnt", + Error::InvalidShardId(_) => "invalid_shard_id", + Error::InvalidStateRequest(_) => "invalid_state_request", + Error::InvalidRandomnessBeaconOutput => "invalid_randomness_beacon_output", + Error::InvalidBlockMerkleRoot => "invalid_block_merkele_root", + Error::InvalidProtocolVersion => "invalid_protocol_version", + Error::NotAValidator => "not_a_validator", + Error::NotAChunkValidator => "not_a_chunk_validator", + Error::InvalidChallengeRoot => "invalid_challenge_root", + Error::BlockOutOfBounds(_)=> "block_out_of_bounds" + } + } +} + +impl From for Error { + fn from(error: EpochError) -> Self { + match error { + EpochError::EpochOutOfBounds(epoch_id) => Error::EpochOutOfBounds(epoch_id), + EpochError::MissingBlock(h) => Error::DBNotFoundErr(format!("epoch block: {h}")), + EpochError::NotAValidator(_account_id, _epoch_id) => Error::NotAValidator, + err => Error::Other(err.to_string()), + } + } +} + +impl From for Error { + fn from(error: BlockError) -> Self { + match error { + err => Error::ValidatorError(err.to_string()), + } + } +} + +pub trait EpochErrorResultToChainError { + fn into_chain_error(self) -> Result; +} + +impl EpochErrorResultToChainError for Result { + fn into_chain_error(self: Result) -> Result { + self.map_err(|err| err.into()) + } +} + +impl From for Error { + fn from(error: ShardLayoutError) -> Self { + match error { + ShardLayoutError::InvalidShardIdError { shard_id } => Error::InvalidShardId(shard_id), + } + } +} + +impl From for Error { + fn from(error: BlockValidityError) -> Self { + match error { + BlockValidityError::InvalidStateRoot => Error::InvalidStateRoot, + BlockValidityError::InvalidReceiptRoot => Error::InvalidChunkReceiptsRoot, + BlockValidityError::InvalidTransactionRoot => Error::InvalidTxRoot, + BlockValidityError::InvalidChunkHeaderRoot => Error::InvalidChunkHeadersRoot, + BlockValidityError::InvalidChunkMask => Error::InvalidChunkMask, + BlockValidityError::InvalidChallengeRoot => Error::InvalidChallengeRoot, + } + } +} + +#[derive(Clone, Eq, PartialEq, Debug, thiserror::Error)] +pub enum BlockKnownError { + #[error("already known in header")] + KnownInHeader, + #[error("already known in head")] + KnownInHead, + #[error("already known in orphan")] + KnownInOrphan, + #[error("already known in missing chunks")] + KnownInMissingChunks, + #[error("already known in store")] + KnownInStore, + #[error("already known in blocks in processing")] + KnownInProcessing, + #[error("already known in invalid blocks")] + KnownAsInvalid, +} + +#[cfg(feature = "new_epoch_sync")] +pub mod epoch_sync { + #[derive(thiserror::Error, std::fmt::Debug)] + pub enum EpochSyncInfoError { + #[error(transparent)] + EpochSyncInfoErr(#[from] unc_primitives::errors::epoch_sync::EpochSyncInfoError), + #[error(transparent)] + IOErr(#[from] std::io::Error), + #[error(transparent)] + ChainErr(#[from] crate::Error), + } +} diff --git a/chain/chain-primitives/src/lib.rs b/chain/chain-primitives/src/lib.rs new file mode 100644 index 000000000..dd4aeed7c --- /dev/null +++ b/chain/chain-primitives/src/lib.rs @@ -0,0 +1,3 @@ +pub mod error; + +pub use error::Error; diff --git a/chain/chain/Cargo.toml b/chain/chain/Cargo.toml new file mode 100644 index 000000000..40058480b --- /dev/null +++ b/chain/chain/Cargo.toml @@ -0,0 +1,87 @@ +[package] +name = "unc-chain" +version.workspace = true +authors.workspace = true +edition.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix.workspace = true +assert_matches.workspace = true +borsh.workspace = true +bytesize.workspace = true +chrono.workspace = true +crossbeam-channel.workspace = true +enum-map.workspace = true +itertools.workspace = true +itoa.workspace = true +lru.workspace = true +num-rational.workspace = true +once_cell.workspace = true +rand.workspace = true +rand_chacha.workspace = true +rayon.workspace = true +strum.workspace = true +thiserror.workspace = true +tracing.workspace = true +yansi.workspace = true + +unc-async.workspace = true +unc-cache.workspace = true +unc-chain-configs.workspace = true +unc-chain-primitives.workspace = true +unc-client-primitives.workspace = true +unc-crypto.workspace = true +unc-epoch-manager.workspace = true +unc-network.workspace = true +unc-o11y.workspace = true +unc-performance-metrics.workspace = true +unc-performance-metrics-macros.workspace = true +unc-pool.workspace = true +unc-primitives.workspace = true +unc-store.workspace = true + +[dev-dependencies] +insta.workspace = true +assert_matches.workspace = true + +[features] +# if enabled, we assert in most situations that are impossible unless some byzantine behavior is observed. +byzantine_asserts = [] +expensive_tests = [] +test_features = [] +no_cache = ["unc-store/no_cache"] +new_epoch_sync = ["unc-store/new_epoch_sync", "unc-primitives/new_epoch_sync", "unc-epoch-manager/new_epoch_sync", "unc-chain-primitives/new_epoch_sync"] + +protocol_feature_reject_blocks_with_outdated_protocol_version = [ + "unc-primitives/protocol_feature_reject_blocks_with_outdated_protocol_version", +] + +nightly = [ + "nightly_protocol", + "protocol_feature_reject_blocks_with_outdated_protocol_version", + "unc-async/nightly", + "unc-chain-configs/nightly", + "unc-client-primitives/nightly", + "unc-epoch-manager/nightly", + "unc-network/nightly", + "unc-o11y/nightly", + "unc-pool/nightly", + "unc-primitives/nightly", + "unc-store/nightly", +] +nightly_protocol = [ + "unc-async/nightly_protocol", + "unc-chain-configs/nightly_protocol", + "unc-client-primitives/nightly_protocol", + "unc-epoch-manager/nightly_protocol", + "unc-network/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-pool/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", +] +sandbox = ["unc-primitives/sandbox"] diff --git a/chain/chain/src/block_processing_utils.rs b/chain/chain/src/block_processing_utils.rs new file mode 100644 index 000000000..efd150ff7 --- /dev/null +++ b/chain/chain/src/block_processing_utils.rs @@ -0,0 +1,158 @@ +use crate::chain::BlockMissingChunks; +use crate::unc_chain_primitives::error::BlockKnownError::KnownInProcessing; +use crate::orphan::OrphanMissingChunks; +use crate::Provenance; +use unc_primitives::block::Block; +use unc_primitives::challenge::{ChallengeBody, ChallengesResult}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::sharding::{ReceiptProof, ShardChunkHeader, StateSyncInfo}; +use unc_primitives::types::ShardId; +use once_cell::sync::OnceCell; +use std::collections::HashMap; +use std::sync::Arc; +use std::time::Instant; + +/// Max number of blocks that can be in the pool at once. +/// This number will likely never be hit unless there are many forks in the chain. +pub(crate) const MAX_PROCESSING_BLOCKS: usize = 5; + +/// Contains information from preprocessing a block +pub(crate) struct BlockPreprocessInfo { + pub(crate) is_caught_up: bool, + pub(crate) state_sync_info: Option, + pub(crate) incoming_receipts: HashMap>, + pub(crate) challenges_result: ChallengesResult, + pub(crate) challenged_blocks: Vec, + pub(crate) provenance: Provenance, + /// This field will be set when the apply_chunks has finished. + /// This is used to provide a way for caller to wait for the finishing of applying chunks of + /// a block + pub(crate) apply_chunks_done: Arc>, + /// This is used to calculate block processing time metric + pub(crate) block_start_processing_time: Instant, +} + +/// Blocks which finished pre-processing and are now being applied asynchronously +pub(crate) struct BlocksInProcessing { + // A map that stores all blocks in processing + preprocessed_blocks: HashMap, +} + +#[derive(Debug)] +pub(crate) enum AddError { + ExceedingPoolSize, + BlockAlreadyInPool, +} + +impl From for unc_chain_primitives::Error { + fn from(err: AddError) -> Self { + match err { + AddError::ExceedingPoolSize => unc_chain_primitives::Error::TooManyProcessingBlocks, + AddError::BlockAlreadyInPool => { + unc_chain_primitives::Error::BlockKnown(KnownInProcessing) + } + } + } +} + +/// Results from processing a block that are useful for client and client actor to use +/// for steps after a block is processed that can't be finished inside Chain after a block is processed +/// (for example, sending requests for missing chunks or challenges). +/// This struct is passed to Chain::process_block as an argument instead of returned as Result, +/// because the information stored here need to returned whether process_block succeeds or returns an error. +#[derive(Default)] +pub struct BlockProcessingArtifact { + pub orphans_missing_chunks: Vec, + pub blocks_missing_chunks: Vec, + pub challenges: Vec, + pub invalid_chunks: Vec, +} + +/// This struct defines the callback function that will be called after apply chunks are finished +/// for each block. Multiple functions that might trigger the start processing of new blocks has +/// this as an argument. Caller of these functions must note that this callback can be called multiple +/// times, for different blocks, because these functions may trigger the processing of more than +/// one block. +pub type DoneApplyChunkCallback = Arc () + Send + Sync + 'static>; + +#[derive(Debug)] +pub struct BlockNotInPoolError; + +impl BlocksInProcessing { + pub(crate) fn new() -> Self { + BlocksInProcessing { preprocessed_blocks: HashMap::new() } + } + + pub(crate) fn len(&self) -> usize { + self.preprocessed_blocks.len() + } + + /// Add a preprocessed block to the pool. Return Error::ExceedingPoolSize if the pool already + /// reaches its max size. + pub(crate) fn add( + &mut self, + block: Block, + preprocess_info: BlockPreprocessInfo, + ) -> Result<(), AddError> { + self.add_dry_run(block.hash())?; + + self.preprocessed_blocks.insert(*block.hash(), (block, preprocess_info)); + Ok(()) + } + + pub(crate) fn contains(&self, block_hash: &CryptoHash) -> bool { + self.preprocessed_blocks.contains_key(block_hash) + } + + pub(crate) fn remove( + &mut self, + block_hash: &CryptoHash, + ) -> Option<(Block, BlockPreprocessInfo)> { + self.preprocessed_blocks.remove(block_hash) + } + + /// This function does NOT add the block, it simply checks if the block can be added + pub(crate) fn add_dry_run(&self, block_hash: &CryptoHash) -> Result<(), AddError> { + // We set a limit to the max number of blocks that we will be processing at the same time. + // Since processing a block requires that the its previous block is processed, this limit + // is likely never hit, unless there are many forks in the chain. + // In this case, we will simply drop the block. + if self.preprocessed_blocks.len() >= MAX_PROCESSING_BLOCKS { + Err(AddError::ExceedingPoolSize) + } else if self.preprocessed_blocks.contains_key(block_hash) { + Err(AddError::BlockAlreadyInPool) + } else { + Ok(()) + } + } + + pub(crate) fn has_blocks_to_catch_up(&self, prev_hash: &CryptoHash) -> bool { + self.preprocessed_blocks + .iter() + .any(|(_, (block, _))| block.header().prev_hash() == prev_hash) + } + + /// This function waits until apply_chunks_done is marked as true for all blocks in the pool + /// Returns true if new blocks are done applying chunks + pub(crate) fn wait_for_all_blocks(&self) -> bool { + for (_, (_, block_preprocess_info)) in self.preprocessed_blocks.iter() { + let _ = block_preprocess_info.apply_chunks_done.wait(); + } + !self.preprocessed_blocks.is_empty() + } + + /// This function waits until apply_chunks_done is marked as true for block `block_hash` + pub(crate) fn wait_for_block( + &self, + block_hash: &CryptoHash, + ) -> Result<(), BlockNotInPoolError> { + let _ = self + .preprocessed_blocks + .get(block_hash) + .ok_or(BlockNotInPoolError)? + .1 + .apply_chunks_done + .wait(); + Ok(()) + } +} diff --git a/chain/chain/src/blocks_delay_tracker.rs b/chain/chain/src/blocks_delay_tracker.rs new file mode 100644 index 000000000..0c9478bd8 --- /dev/null +++ b/chain/chain/src/blocks_delay_tracker.rs @@ -0,0 +1,484 @@ +use chrono::DateTime; +use unc_epoch_manager::EpochManagerAdapter; +use unc_primitives::block::{Block, Tip}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::sharding::{ChunkHash, ShardChunkHeader}; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::types::{BlockHeight, ShardId}; +use unc_primitives::views::{ + BlockProcessingInfo, BlockProcessingStatus, ChainProcessingInfo, ChunkProcessingInfo, + ChunkProcessingStatus, DroppedReason, +}; +use std::collections::{hash_map::Entry, BTreeMap, HashMap}; +use std::mem; +use std::time::Instant; +use tracing::error; + +use crate::{metrics, Chain, ChainStoreAccess}; + +const BLOCK_DELAY_TRACKING_COUNT: u64 = 50; + +/// A centralized place that records monitoring information about the important timestamps throughout +/// the lifetime of blocks and chunks. It keeps information of recent blocks and chunks +/// (blocks with height > head height - BLOCK_DELAY_TRACKING_HORIZON). +/// A block is added the first time when chain tries to process the block. Note that this means +/// the block already passes a few checks in ClientActor and in Client before it enters the chain +/// code. For example, client actor checks that the block must be within head_height + BLOCK_HORIZON (500), +/// that's why we know tracker at most tracks 550 blocks. +#[derive(Debug, Default)] +pub struct BlocksDelayTracker { + // A block is added at the first time it was received, and + // removed if it is too far from the chain head. + blocks: HashMap, + // Maps block height to block hash. Used for gc. + // Theoretically, each block height should only have one block, if our block processing code + // works correctly. We are storing a vector here just in case. + blocks_height_map: BTreeMap>, + // Chunks that belong to the blocks in the tracker + chunks: HashMap, + // Chunks that we don't know which block it belongs to yet + floating_chunks: HashMap, + head_height: BlockHeight, +} + +#[derive(Debug, Clone)] +pub struct BlockTrackingStats { + /// Timestamp when block was received. + pub received_timestamp: Instant, + pub received_utc_timestamp: DateTime, + /// Timestamp when block was put to the orphan pool, if it ever was + pub orphaned_timestamp: Option, + /// Timestamp when block was put to the missing chunks pool + pub missing_chunks_timestamp: Option, + /// Timestamp when block was moved out of the orphan pool + pub removed_from_orphan_timestamp: Option, + /// Timestamp when block was moved out of the missing chunks pool + pub removed_from_missing_chunks_timestamp: Option, + /// Timestamp when block was done processing + pub processed_timestamp: Option, + /// Whether the block is not processed because of different reasons + pub dropped: Option, + /// Stores the error message encountered during the processing of this block + pub error: Option, + /// Only contains new chunks that belong to this block, if the block doesn't produce a new chunk + /// for a shard, the corresponding item will be None. + pub chunks: Vec>, +} + +/// Records timestamps of requesting and receiving a chunk. Assumes that each chunk is requested +/// before it is received. +#[derive(Debug, Clone)] +pub struct ChunkTrackingStats { + pub height_created: BlockHeight, + pub shard_id: ShardId, + pub prev_block_hash: CryptoHash, + /// Timestamp of first time when we request for this chunk. + pub requested_timestamp: Option>, + /// Timestamp of when the node receives all information it needs for this chunk + pub completed_timestamp: Option>, +} + +impl ChunkTrackingStats { + fn new(chunk_header: &ShardChunkHeader) -> Self { + Self { + height_created: chunk_header.height_created(), + shard_id: chunk_header.shard_id(), + prev_block_hash: *chunk_header.prev_block_hash(), + requested_timestamp: None, + completed_timestamp: None, + } + } + + fn to_chunk_processing_info( + &self, + chunk_hash: ChunkHash, + epoch_manager: &dyn EpochManagerAdapter, + ) -> ChunkProcessingInfo { + let status = if self.completed_timestamp.is_some() { + ChunkProcessingStatus::Completed + } else if self.requested_timestamp.is_some() { + ChunkProcessingStatus::Requested + } else { + ChunkProcessingStatus::NeedToRequest + }; + let created_by = epoch_manager + .get_epoch_id_from_prev_block(&self.prev_block_hash) + .and_then(|epoch_id| { + epoch_manager.get_chunk_producer(&epoch_id, self.height_created, self.shard_id) + }) + .ok(); + let request_duration = if let Some(requested_timestamp) = self.requested_timestamp { + if let Some(completed_timestamp) = self.completed_timestamp { + Some((completed_timestamp - requested_timestamp).num_milliseconds() as u64) + } else { + None + } + } else { + None + }; + ChunkProcessingInfo { + chunk_hash, + height_created: self.height_created, + shard_id: self.shard_id, + prev_block_hash: self.prev_block_hash, + created_by, + status, + requested_timestamp: self.requested_timestamp, + completed_timestamp: self.completed_timestamp, + request_duration, + chunk_parts_collection: vec![], + } + } +} + +impl BlocksDelayTracker { + pub fn mark_block_received( + &mut self, + block: &Block, + timestamp: Instant, + utc_timestamp: DateTime, + ) { + let block_hash = block.header().hash(); + + if let Entry::Vacant(entry) = self.blocks.entry(*block_hash) { + let height = block.header().height(); + let chunks = block + .chunks() + .iter() + .map(|chunk| { + if chunk.height_included() == height { + let chunk_hash = chunk.chunk_hash(); + self.chunks + .entry(chunk_hash.clone()) + .or_insert(ChunkTrackingStats::new(chunk)); + self.floating_chunks.remove(&chunk_hash); + Some(chunk_hash) + } else { + None + } + }) + .collect(); + entry.insert(BlockTrackingStats { + received_timestamp: timestamp, + received_utc_timestamp: utc_timestamp, + orphaned_timestamp: None, + missing_chunks_timestamp: None, + removed_from_orphan_timestamp: None, + removed_from_missing_chunks_timestamp: None, + processed_timestamp: None, + dropped: None, + error: None, + chunks, + }); + self.blocks_height_map.entry(height).or_insert(vec![]).push(*block_hash); + } + } + + pub fn mark_block_dropped(&mut self, block_hash: &CryptoHash, reason: DroppedReason) { + if let Some(block_entry) = self.blocks.get_mut(block_hash) { + block_entry.dropped = Some(reason); + } else { + error!(target:"blocks_delay_tracker", "block {:?} was dropped but was not marked received", block_hash); + } + } + + pub fn mark_block_errored(&mut self, block_hash: &CryptoHash, err: String) { + if let Some(block_entry) = self.blocks.get_mut(block_hash) { + block_entry.error = Some(err); + } else { + error!(target:"blocks_delay_tracker", "block {:?} was errored but was not marked received", block_hash); + } + } + + pub fn mark_block_orphaned(&mut self, block_hash: &CryptoHash, timestamp: Instant) { + if let Some(block_entry) = self.blocks.get_mut(block_hash) { + block_entry.orphaned_timestamp = Some(timestamp); + } else { + error!(target:"blocks_delay_tracker", "block {:?} was orphaned but was not marked received", block_hash); + } + } + + pub fn mark_block_unorphaned(&mut self, block_hash: &CryptoHash, timestamp: Instant) { + if let Some(block_entry) = self.blocks.get_mut(block_hash) { + block_entry.removed_from_orphan_timestamp = Some(timestamp); + } else { + error!(target:"blocks_delay_tracker", "block {:?} was unorphaned but was not marked received", block_hash); + } + } + + pub fn mark_block_has_missing_chunks(&mut self, block_hash: &CryptoHash, timestamp: Instant) { + if let Some(block_entry) = self.blocks.get_mut(block_hash) { + block_entry.missing_chunks_timestamp = Some(timestamp); + } else { + error!(target:"blocks_delay_tracker", "block {:?} was marked as having missing chunks but was not marked received", block_hash); + } + } + + pub fn mark_block_completed_missing_chunks( + &mut self, + block_hash: &CryptoHash, + timestamp: Instant, + ) { + if let Some(block_entry) = self.blocks.get_mut(block_hash) { + block_entry.removed_from_missing_chunks_timestamp = Some(timestamp); + } else { + error!(target:"blocks_delay_tracker", "block {:?} was marked as having no missing chunks but was not marked received", block_hash); + } + } + + pub fn mark_chunk_completed( + &mut self, + chunk_header: &ShardChunkHeader, + timestamp: DateTime, + ) { + let chunk_hash = chunk_header.chunk_hash(); + self.chunks + .entry(chunk_hash.clone()) + .or_insert_with(|| { + self.floating_chunks.insert(chunk_hash, chunk_header.height_created()); + ChunkTrackingStats::new(chunk_header) + }) + .completed_timestamp + .get_or_insert(timestamp); + } + + pub fn mark_chunk_requested( + &mut self, + chunk_header: &ShardChunkHeader, + timestamp: DateTime, + ) { + let chunk_hash = chunk_header.chunk_hash(); + self.chunks + .entry(chunk_hash.clone()) + .or_insert_with(|| { + self.floating_chunks.insert(chunk_hash, chunk_header.height_created()); + ChunkTrackingStats::new(chunk_header) + }) + .requested_timestamp + .get_or_insert(timestamp); + } + + fn update_head(&mut self, head_height: BlockHeight) { + if head_height != self.head_height { + let cutoff_height = head_height.saturating_sub(BLOCK_DELAY_TRACKING_COUNT); + self.head_height = head_height; + let mut blocks_to_remove = self.blocks_height_map.split_off(&cutoff_height); + mem::swap(&mut self.blocks_height_map, &mut blocks_to_remove); + + for block_hash in blocks_to_remove.values().flatten() { + if let Some(block) = self.blocks.remove(&block_hash) { + for chunk_hash in block.chunks { + if let Some(chunk_hash) = chunk_hash { + self.chunks.remove(&chunk_hash); + } + } + } else { + debug_assert!(false); + error!(target:"block_delay_tracker", "block {:?} in height map but not in blocks", block_hash); + } + } + + let chunks_to_remove: Vec<_> = self + .floating_chunks + .iter() + .filter_map(|(chunk_hash, chunk_height)| { + if chunk_height < &cutoff_height { + Some(chunk_hash.clone()) + } else { + None + } + }) + .collect(); + for chunk_hash in chunks_to_remove { + self.chunks.remove(&chunk_hash); + self.floating_chunks.remove(&chunk_hash); + } + } + } + + pub fn finish_block_processing(&mut self, block_hash: &CryptoHash, new_head: Option) { + if let Some(processed_block) = self.blocks.get_mut(&block_hash) { + processed_block.processed_timestamp = Some(StaticClock::instant()); + } + // To get around the rust reference scope check + if let Some(processed_block) = self.blocks.get(&block_hash) { + let chunks = processed_block.chunks.clone(); + self.update_block_metrics(processed_block); + for (shard_id, chunk_hash) in chunks.into_iter().enumerate() { + if let Some(chunk_hash) = chunk_hash { + if let Some(processed_chunk) = self.chunks.get(&chunk_hash) { + self.update_chunk_metrics(processed_chunk, shard_id as ShardId); + } + } + } + } + if let Some(head) = new_head { + self.update_head(head.height); + } + } + + fn update_block_metrics(&self, block: &BlockTrackingStats) { + if let Some(start) = block.orphaned_timestamp { + if let Some(end) = block.removed_from_orphan_timestamp { + metrics::BLOCK_ORPHANED_DELAY + .observe(end.saturating_duration_since(start).as_secs_f64()); + } + } else { + metrics::BLOCK_ORPHANED_DELAY.observe(0.); + } + if let Some(start) = block.missing_chunks_timestamp { + if let Some(end) = block.removed_from_missing_chunks_timestamp { + metrics::BLOCK_MISSING_CHUNKS_DELAY + .observe(end.saturating_duration_since(start).as_secs_f64()); + } + } else { + metrics::BLOCK_MISSING_CHUNKS_DELAY.observe(0.); + } + } + + fn update_chunk_metrics(&self, chunk: &ChunkTrackingStats, shard_id: ShardId) { + if let Some(chunk_requested) = chunk.requested_timestamp { + // Theoretically chunk_received should have been set here because a block being processed + // requires all chunks to be received + if let Some(chunk_received) = chunk.completed_timestamp { + metrics::CHUNK_RECEIVED_DELAY + .with_label_values(&[&shard_id.to_string()]) + .observe((chunk_received - chunk_requested).num_milliseconds() as f64 / 1000.); + } + } + } + + fn get_block_processing_info( + &self, + block_height: BlockHeight, + block_hash: &CryptoHash, + chain: &Chain, + epoch_manager: &dyn EpochManagerAdapter, + ) -> Option { + self.blocks.get(block_hash).map(|block_stats| { + let chunks_info: Vec<_> = block_stats + .chunks + .iter() + .map(|chunk_hash| { + if let Some(chunk_hash) = chunk_hash { + self.chunks + .get(chunk_hash) + .map(|x| x.to_chunk_processing_info(chunk_hash.clone(), epoch_manager)) + } else { + None + } + }) + .collect(); + let now = StaticClock::instant(); + let block_status = chain.get_block_status(block_hash, block_stats); + let in_progress_ms = block_stats + .processed_timestamp + .unwrap_or(now) + .checked_duration_since(block_stats.received_timestamp) + .map(|x| x.as_millis()) + .unwrap_or_default(); + let orphaned_ms = if let Some(orphaned_time) = block_stats.orphaned_timestamp { + block_stats + .removed_from_orphan_timestamp + .unwrap_or(now) + .checked_duration_since(orphaned_time) + .map(|x| x.as_millis()) + } else { + None + }; + let missing_chunks_ms = + if let Some(missing_chunks_time) = block_stats.missing_chunks_timestamp { + block_stats + .removed_from_missing_chunks_timestamp + .unwrap_or(now) + .checked_duration_since(missing_chunks_time) + .map(|x| x.as_millis()) + } else { + None + }; + BlockProcessingInfo { + height: block_height, + hash: *block_hash, + received_timestamp: block_stats.received_utc_timestamp, + in_progress_ms, + orphaned_ms, + block_status, + missing_chunks_ms, + chunks_info, + } + }) + } +} + +impl Chain { + fn get_block_status( + &self, + block_hash: &CryptoHash, + block_info: &BlockTrackingStats, + ) -> BlockProcessingStatus { + if self.is_orphan(block_hash) { + return BlockProcessingStatus::Orphan; + } + if self.is_chunk_orphan(block_hash) { + return BlockProcessingStatus::WaitingForChunks; + } + if self.is_in_processing(block_hash) { + return BlockProcessingStatus::InProcessing; + } + if self.chain_store().block_exists(block_hash).unwrap_or_default() { + return BlockProcessingStatus::Accepted; + } + if let Some(dropped_reason) = &block_info.dropped { + return BlockProcessingStatus::Dropped(dropped_reason.clone()); + } + if let Some(error) = &block_info.error { + return BlockProcessingStatus::Error(error.clone()); + } + return BlockProcessingStatus::Unknown; + } + + pub fn get_chain_processing_info(&self) -> ChainProcessingInfo { + let blocks_info: Vec<_> = self + .blocks_delay_tracker + .blocks_height_map + .iter() + .rev() + .flat_map(|(height, hashes)| { + hashes + .iter() + .flat_map(|hash| { + self.blocks_delay_tracker.get_block_processing_info( + *height, + hash, + self, + self.epoch_manager.as_ref(), + ) + }) + .collect::>() + }) + .collect(); + let mut floating_chunks_info = self + .blocks_delay_tracker + .floating_chunks + .iter() + .flat_map(|(chunk_hash, _)| { + self.blocks_delay_tracker.chunks.get(chunk_hash).map(|chunk_stats| { + chunk_stats + .to_chunk_processing_info(chunk_hash.clone(), self.epoch_manager.as_ref()) + }) + }) + .collect::>(); + floating_chunks_info.sort_by(|chunk1, chunk2| { + (chunk1.height_created, chunk1.shard_id) + .partial_cmp(&(chunk2.height_created, chunk2.shard_id)) + .unwrap() + }); + ChainProcessingInfo { + num_blocks_in_processing: self.blocks_in_processing_len(), + num_orphans: self.orphans_len(), + num_blocks_missing_chunks: self.blocks_with_missing_chunks_len(), + blocks_info, + floating_chunks_info, + } + } +} diff --git a/chain/chain/src/chain.rs b/chain/chain/src/chain.rs new file mode 100644 index 000000000..3b52ff5e4 --- /dev/null +++ b/chain/chain/src/chain.rs @@ -0,0 +1,4730 @@ +use crate::block_processing_utils::{ + BlockPreprocessInfo, BlockProcessingArtifact, BlocksInProcessing, DoneApplyChunkCallback, +}; +use crate::blocks_delay_tracker::BlocksDelayTracker; +use crate::chain_update::ChainUpdate; +use crate::crypto_hash_timer::CryptoHashTimer; +use crate::lightclient::get_epoch_block_producers_view; +use crate::migrations::check_if_block_is_first_with_chunk_of_version; +use crate::missing_chunks::MissingChunksPool; +use crate::orphan::{Orphan, OrphanBlockPool}; +use crate::sharding::shuffle_receipt_proofs; +use crate::state_request_tracker::StateRequestTracker; +use crate::state_snapshot_actor::SnapshotCallbacks; +use crate::store::{ChainStore, ChainStoreAccess, ChainStoreUpdate}; + +use crate::types::{ + AcceptedBlock, ApplyChunkBlockContext, ApplyChunkResult, BlockEconomicsConfig, ChainConfig, + RuntimeAdapter, StorageDataSource, +}; +use crate::update_shard::{ + apply_new_chunk, process_missing_chunks_range, process_shard_update, NewChunkData, + NewChunkResult, OldChunkData, ReshardingData, ShardContext, ShardUpdateReason, + ShardUpdateResult, StorageContext, +}; +use crate::validate::{ + validate_challenge, validate_chunk_proofs, validate_chunk_with_chunk_extra, + validate_transactions_order, +}; +use crate::{ + byzantine_assert, create_light_client_block_view, BlockStatus, ChainGenesis, Doomslug, + Provenance, +}; +use crate::{metrics, DoomslugThresholdMode}; +use borsh::BorshDeserialize; +use chrono::Duration; +use crossbeam_channel::{unbounded, Receiver, Sender}; +use itertools::Itertools; +use lru::LruCache; +use unc_chain_configs::{MutableConfigValue, ReshardingConfig, ReshardingHandle}; +#[cfg(feature = "new_epoch_sync")] +use unc_chain_primitives::error::epoch_sync::EpochSyncInfoError; +use unc_chain_primitives::error::{BlockKnownError, Error, LogTransientStorageError}; +use unc_epoch_manager::shard_tracker::ShardTracker; +use unc_epoch_manager::types::BlockHeaderInfo; +use unc_epoch_manager::EpochManagerAdapter; +use unc_o11y::log_assert; +use unc_primitives::block::{genesis_chunks, Block, BlockValidityError, Tip}; +use unc_primitives::block_header::BlockHeader; +use unc_primitives::challenge::{ + BlockDoubleSign, Challenge, ChallengeBody, ChallengesResult, ChunkProofs, ChunkState, + MaybeEncodedShardChunk, PartialState, SlashedValidator, +}; +use unc_primitives::checked_feature; +#[cfg(feature = "new_epoch_sync")] +use unc_primitives::epoch_manager::epoch_sync::EpochSyncInfo; +#[cfg(feature = "new_epoch_sync")] +use unc_primitives::errors::epoch_sync::EpochSyncHashType; +use unc_primitives::errors::EpochError; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::merkle::{ + combine_hash, merklize, verify_path, Direction, MerklePath, MerklePathItem, PartialMerkleTree, +}; +use unc_primitives::receipt::Receipt; +use unc_primitives::sandbox::state_patch::SandboxStatePatch; +use unc_primitives::shard_layout::{account_id_to_shard_id, ShardLayout, ShardUId}; +use unc_primitives::sharding::{ + ChunkHash, ChunkHashHeight, EncodedShardChunk, ReceiptList, ReceiptProof, ShardChunk, + ShardChunkHeader, ShardInfo, ShardProof, StateSyncInfo, +}; +use unc_primitives::state_part::PartId; +use unc_primitives::state_sync::{ + get_num_state_parts, BitArray, CachedParts, ReceiptProofResponse, RootProof, + ShardStateSyncResponseHeader, ShardStateSyncResponseHeaderV2, StateHeaderKey, StatePartKey, +}; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::transaction::{ExecutionOutcomeWithIdAndProof, SignedTransaction}; +use unc_primitives::types::chunk_extra::ChunkExtra; +use unc_primitives::types::{ + AccountId, Balance, BlockExtra, BlockHeight, BlockHeightDelta, EpochId, MerkleHash, NumBlocks, + ShardId, StateRoot, +}; +use unc_primitives::unwrap_or_return; +#[cfg(feature = "new_epoch_sync")] +use unc_primitives::utils::index_to_bytes; +use unc_primitives::utils::MaybeValidated; +use unc_primitives::version::{ProtocolFeature, PROTOCOL_VERSION}; +use unc_primitives::views::{ + BlockStatusView, DroppedReason, ExecutionOutcomeWithIdView, ExecutionStatusView, + FinalExecutionOutcomeView, FinalExecutionOutcomeWithReceiptView, FinalExecutionStatus, + LightClientBlockView, SignedTransactionView, +}; +use unc_store::config::StateSnapshotType; +use unc_store::flat::{store_helper, FlatStorageReadyStatus, FlatStorageStatus}; +use unc_store::get_genesis_state_roots; +use unc_store::DBCol; +use once_cell::sync::OnceCell; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::fmt::{Debug, Formatter}; +use std::sync::Arc; +use std::time::Instant; +use tracing::{debug, debug_span, error, info, warn, Span}; + +/// The size of the invalid_blocks in-memory pool +pub const INVALID_CHUNKS_POOL_SIZE: usize = 5000; + +/// 10000 years in seconds. Big constant for sandbox to allow time traveling. +#[cfg(feature = "sandbox")] +const ACCEPTABLE_TIME_DIFFERENCE: i64 = 60 * 60 * 24 * 365 * 10000; + +// Number of parent blocks traversed to check if the block can be finalized. +const NUM_PARENTS_TO_CHECK_FINALITY: usize = 20; + +/// Refuse blocks more than this many block intervals in the future (as in bitcoin). +#[cfg(not(feature = "sandbox"))] +const ACCEPTABLE_TIME_DIFFERENCE: i64 = 12 * 10; + +/// Private constant for 1 NEAR (copy from near/config.rs) used for reporting. +const UNC_BASE: Balance = 1_000_000_000_000_000_000_000_000; + +/// apply_chunks may be called in two code paths, through process_block or through catchup_blocks +/// When it is called through process_block, it is possible that the shard state for the next epoch +/// has not been caught up yet, thus the two modes IsCaughtUp and NotCaughtUp. +/// CatchingUp is for when apply_chunks is called through catchup_blocks, this is to catch up the +/// shard states for the next epoch +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +enum ApplyChunksMode { + IsCaughtUp, + CatchingUp, + NotCaughtUp, +} + +/// Contains information for missing chunks in a block +pub struct BlockMissingChunks { + /// previous block hash + pub prev_hash: CryptoHash, + pub missing_chunks: Vec, +} + +impl Debug for BlockMissingChunks { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("BlockMissingChunks") + .field("prev_hash", &self.prev_hash) + .field("num_missing_chunks", &self.missing_chunks.len()) + .finish() + } +} + +/// Check if block header is known +/// Returns Err(Error) if any error occurs when checking store +/// Ok(Err(BlockKnownError)) if the block header is known +/// Ok(Ok()) otherwise +pub fn check_header_known( + chain: &Chain, + header: &BlockHeader, +) -> Result, Error> { + // TODO: Change the return type to Result. + let header_head = chain.chain_store().header_head()?; + if header.hash() == &header_head.last_block_hash + || header.hash() == &header_head.prev_block_hash + { + return Ok(Err(BlockKnownError::KnownInHeader)); + } + check_known_store(chain, header.hash()) +} + +/// Check if this block is in the store already. +/// Returns Err(Error) if any error occurs when checking store +/// Ok(Err(BlockKnownError)) if the block is in the store +/// Ok(Ok()) otherwise +fn check_known_store( + chain: &Chain, + block_hash: &CryptoHash, +) -> Result, Error> { + // TODO: Change the return type to Result. + if chain.chain_store().block_exists(block_hash)? { + Ok(Err(BlockKnownError::KnownInStore)) + } else { + // Not yet processed this block, we can proceed. + Ok(Ok(())) + } +} + +/// Check if block is known: head, orphan, in processing or in store. +/// Returns Err(Error) if any error occurs when checking store +/// Ok(Err(BlockKnownError)) if the block is known +/// Ok(Ok()) otherwise +pub fn check_known( + chain: &Chain, + block_hash: &CryptoHash, +) -> Result, Error> { + // TODO: Change the return type to Result. + let head = chain.chain_store().head()?; + // Quick in-memory check for fast-reject any block handled recently. + if block_hash == &head.last_block_hash || block_hash == &head.prev_block_hash { + return Ok(Err(BlockKnownError::KnownInHead)); + } + if chain.blocks_in_processing.contains(block_hash) { + return Ok(Err(BlockKnownError::KnownInProcessing)); + } + // Check if this block is in the set of known orphans. + if chain.orphans.contains(block_hash) { + return Ok(Err(BlockKnownError::KnownInOrphan)); + } + if chain.blocks_with_missing_chunks.contains(block_hash) { + return Ok(Err(BlockKnownError::KnownInMissingChunks)); + } + if chain.is_block_invalid(block_hash) { + return Ok(Err(BlockKnownError::KnownAsInvalid)); + } + check_known_store(chain, block_hash) +} + +type BlockApplyChunksResult = (CryptoHash, Vec<(ShardId, Result)>); + +/// Facade to the blockchain block processing and storage. +/// Provides current view on the state according to the chain state. +pub struct Chain { + chain_store: ChainStore, + pub epoch_manager: Arc, + pub shard_tracker: ShardTracker, + pub runtime_adapter: Arc, + pub(crate) orphans: OrphanBlockPool, + pub blocks_with_missing_chunks: MissingChunksPool, + genesis: Block, + pub transaction_validity_period: NumBlocks, + pub epoch_length: BlockHeightDelta, + /// Block economics, relevant to changes when new block must be produced. + pub block_economics_config: BlockEconomicsConfig, + pub doomslug_threshold_mode: DoomslugThresholdMode, + pub blocks_delay_tracker: BlocksDelayTracker, + /// Processing a block is done in three stages: preprocess_block, async_apply_chunks and + /// postprocess_block. The async_apply_chunks is done asynchronously from the ClientActor thread. + /// `blocks_in_processing` keeps track of all the blocks that have been preprocessed but are + /// waiting for chunks being applied. + pub(crate) blocks_in_processing: BlocksInProcessing, + /// Used by async_apply_chunks to send apply chunks results back to chain + apply_chunks_sender: Sender, + /// Used to receive apply chunks results + apply_chunks_receiver: Receiver, + /// Time when head was updated most recently. + last_time_head_updated: Instant, + /// Prevents re-application of known-to-be-invalid blocks, so that in case of a + /// protocol issue we can recover faster by focusing on correct blocks. + invalid_blocks: LruCache, + + /// Support for sandbox's patch_state requests. + /// + /// Sandbox needs ability to arbitrary modify the state. Blockchains + /// naturally prevent state tampering, so we can't *just* modify data in + /// place in the database. Instead, we will include this "bonus changes" in + /// the next block we'll be processing, keeping them in this field in the + /// meantime. + /// + /// Note that without `sandbox` feature enabled, `SandboxStatePatch` is + /// a ZST. All methods of the type are no-ops which behave as if the object + /// was empty and could not hold any records (which it cannot). It’s + /// impossible to have non-empty state patch on non-sandbox builds. + pending_state_patch: SandboxStatePatch, + + /// Used to store state parts already requested along with elapsed time + /// to create the parts. This information is used for debugging + pub(crate) requested_state_parts: StateRequestTracker, + + /// A callback to initiate state snapshot. + snapshot_callbacks: Option, + + /// Configuration for resharding. + pub(crate) resharding_config: MutableConfigValue, + + // A handle that allows the main process to interrupt resharding if needed. + // This typically happens when the main process is interrupted. + pub resharding_handle: ReshardingHandle, +} + +impl Drop for Chain { + fn drop(&mut self) { + let _ = self.blocks_in_processing.wait_for_all_blocks(); + } +} + +/// UpdateShardJob is a closure that is responsible for updating a shard for a single block. +/// Execution context (latest blocks/chunks details) are already captured within. +type UpdateShardJob = + (ShardId, Box Result + Send + 'static>); + +/// PreprocessBlockResult is a tuple where the first element is a vector of jobs +/// to update shards, the second element is BlockPreprocessInfo +type PreprocessBlockResult = (Vec, BlockPreprocessInfo); + +// Used only for verify_block_hash_and_signature. See that method. +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum VerifyBlockHashAndSignatureResult { + Correct, + Incorrect, + CannotVerifyBecauseBlockIsOrphan, +} + +impl Chain { + pub fn make_genesis_block( + epoch_manager: &dyn EpochManagerAdapter, + runtime_adapter: &dyn RuntimeAdapter, + chain_genesis: &ChainGenesis, + ) -> Result { + let state_roots = get_genesis_state_roots(runtime_adapter.store())? + .expect("genesis should be initialized."); + let genesis_chunks = genesis_chunks( + state_roots, + &epoch_manager.shard_ids(&EpochId::default())?, + chain_genesis.gas_limit, + chain_genesis.height, + chain_genesis.protocol_version, + ); + Ok(Block::genesis( + chain_genesis.protocol_version, + genesis_chunks.into_iter().map(|chunk: ShardChunk| chunk.take_header()).collect(), + chain_genesis.time, + chain_genesis.height, + chain_genesis.min_gas_price, + chain_genesis.total_supply, + Chain::compute_bp_hash( + epoch_manager, + EpochId::default(), + EpochId::default(), + &CryptoHash::default(), + )?, + )) + } + + pub fn new_for_view_client( + epoch_manager: Arc, + shard_tracker: ShardTracker, + runtime_adapter: Arc, + chain_genesis: &ChainGenesis, + doomslug_threshold_mode: DoomslugThresholdMode, + save_trie_changes: bool, + ) -> Result { + let store = runtime_adapter.store(); + let chain_store = ChainStore::new(store.clone(), chain_genesis.height, save_trie_changes); + let genesis = Self::make_genesis_block( + epoch_manager.as_ref(), + runtime_adapter.as_ref(), + chain_genesis, + )?; + let (sc, rc) = unbounded(); + Ok(Chain { + chain_store, + epoch_manager, + shard_tracker, + runtime_adapter, + orphans: OrphanBlockPool::new(), + blocks_with_missing_chunks: MissingChunksPool::new(), + blocks_in_processing: BlocksInProcessing::new(), + genesis, + transaction_validity_period: chain_genesis.transaction_validity_period, + epoch_length: chain_genesis.epoch_length, + block_economics_config: BlockEconomicsConfig::from(chain_genesis), + doomslug_threshold_mode, + blocks_delay_tracker: BlocksDelayTracker::default(), + apply_chunks_sender: sc, + apply_chunks_receiver: rc, + last_time_head_updated: StaticClock::instant(), + invalid_blocks: LruCache::new(INVALID_CHUNKS_POOL_SIZE), + pending_state_patch: Default::default(), + requested_state_parts: StateRequestTracker::new(), + snapshot_callbacks: None, + resharding_config: MutableConfigValue::new( + ReshardingConfig::default(), + "resharding_config", + ), + resharding_handle: ReshardingHandle::new(), + }) + } + + pub fn new( + epoch_manager: Arc, + shard_tracker: ShardTracker, + runtime_adapter: Arc, + chain_genesis: &ChainGenesis, + doomslug_threshold_mode: DoomslugThresholdMode, + chain_config: ChainConfig, + snapshot_callbacks: Option, + ) -> Result { + // Get runtime initial state and create genesis block out of it. + let state_roots = get_genesis_state_roots(runtime_adapter.store())? + .expect("genesis should be initialized."); + let mut chain_store = ChainStore::new( + runtime_adapter.store().clone(), + chain_genesis.height, + chain_config.save_trie_changes, + ); + let genesis_chunks = genesis_chunks( + state_roots.clone(), + &epoch_manager.shard_ids(&EpochId::default())?, + chain_genesis.gas_limit, + chain_genesis.height, + chain_genesis.protocol_version, + ); + let genesis = Block::genesis( + chain_genesis.protocol_version, + genesis_chunks.iter().map(|chunk| chunk.cloned_header()).collect(), + chain_genesis.time, + chain_genesis.height, + chain_genesis.min_gas_price, + chain_genesis.total_supply, + Chain::compute_bp_hash( + epoch_manager.as_ref(), + EpochId::default(), + EpochId::default(), + &CryptoHash::default(), + )?, + ); + + // Check if we have a head in the store, otherwise pick genesis block. + let mut store_update = chain_store.store_update(); + let (block_head, header_head) = match store_update.head() { + Ok(block_head) => { + // Check that genesis in the store is the same as genesis given in the config. + let genesis_hash = store_update.get_block_hash_by_height(chain_genesis.height)?; + if &genesis_hash != genesis.hash() { + return Err(Error::Other(format!( + "Genesis mismatch between storage and config: {:?} vs {:?}", + genesis_hash, + genesis.hash() + ))); + } + + // Check we have the header corresponding to the header_head. + let mut header_head = store_update.header_head()?; + if store_update.get_block_header(&header_head.last_block_hash).is_err() { + // Reset header head and "sync" head to be consistent with current block head. + store_update.save_header_head_if_not_challenged(&block_head)?; + header_head = block_head.clone(); + } + + // TODO: perform validation that latest state in runtime matches the stored chain. + + (block_head, header_head) + } + Err(Error::DBNotFoundErr(_)) => { + for chunk in genesis_chunks { + store_update.save_chunk(chunk.clone()); + } + store_update.merge(epoch_manager.add_validator_proposals(BlockHeaderInfo::new( + genesis.header(), + // genesis height is considered final + chain_genesis.height, + ))?); + store_update.save_block_header(genesis.header().clone())?; + store_update.save_block(genesis.clone()); + store_update + .save_block_extra(genesis.hash(), BlockExtra { challenges_result: vec![] }); + + for (chunk_header, state_root) in genesis.chunks().iter().zip(state_roots.iter()) { + store_update.save_chunk_extra( + genesis.hash(), + &epoch_manager + .shard_id_to_uid(chunk_header.shard_id(), &EpochId::default())?, + ChunkExtra::new( + state_root, + CryptoHash::default(), + vec![], + vec![], + 0, + chain_genesis.gas_limit, + 0, + ), + ); + } + + let block_head = Tip::from_header(genesis.header()); + let header_head = block_head.clone(); + store_update.save_head(&block_head)?; + store_update.save_final_head(&header_head)?; + + // Set the root block of flat state to be the genesis block. Later, when we + // init FlatStorages, we will read the from this column in storage, so it + // must be set here. + let flat_storage_manager = runtime_adapter.get_flat_storage_manager(); + let genesis_epoch_id = genesis.header().epoch_id(); + let mut tmp_store_update = store_update.store().store_update(); + for shard_uid in epoch_manager.get_shard_layout(genesis_epoch_id)?.shard_uids() { + flat_storage_manager.set_flat_storage_for_genesis( + &mut tmp_store_update, + shard_uid, + genesis.hash(), + genesis.header().height(), + ) + } + store_update.merge(tmp_store_update); + + info!(target: "chain", "Init: saved genesis: #{} {} / {:?}", block_head.height, block_head.last_block_hash, state_roots); + + (block_head, header_head) + } + Err(err) => return Err(err), + }; + store_update.commit()?; + + // We must load in-memory tries here, and not inside runtime, because + // if we were initializing from genesis, the runtime would be + // initialized when no blocks or flat storage were initialized. We + // require flat storage in order to load in-memory tries. + // TODO(#9511): The calculation of shard UIDs is not precise in the case + // of resharding. We need to revisit this. + let tip = chain_store.head()?; + let shard_uids: Vec<_> = + epoch_manager.get_shard_layout(&tip.epoch_id)?.shard_uids().collect(); + runtime_adapter.load_mem_tries_on_startup(&shard_uids)?; + + info!(target: "chain", "Init: header head @ #{} {}; block head @ #{} {}", + header_head.height, header_head.last_block_hash, + block_head.height, block_head.last_block_hash); + metrics::BLOCK_HEIGHT_HEAD.set(block_head.height as i64); + let block_header = chain_store.get_block_header(&block_head.last_block_hash)?; + metrics::BLOCK_ORDINAL_HEAD.set(block_header.block_ordinal() as i64); + metrics::HEADER_HEAD_HEIGHT.set(header_head.height as i64); + metrics::BOOT_TIME_SECONDS.set(StaticClock::utc().timestamp()); + + metrics::TAIL_HEIGHT.set(chain_store.tail()? as i64); + metrics::CHUNK_TAIL_HEIGHT.set(chain_store.chunk_tail()? as i64); + metrics::FORK_TAIL_HEIGHT.set(chain_store.fork_tail()? as i64); + + // Even though the channel is unbounded, the channel size is practically bounded by the size + // of blocks_in_processing, which is set to 5 now. + let (sc, rc) = unbounded(); + Ok(Chain { + chain_store, + epoch_manager, + shard_tracker, + runtime_adapter, + orphans: OrphanBlockPool::new(), + blocks_with_missing_chunks: MissingChunksPool::new(), + blocks_in_processing: BlocksInProcessing::new(), + invalid_blocks: LruCache::new(INVALID_CHUNKS_POOL_SIZE), + genesis: genesis.clone(), + transaction_validity_period: chain_genesis.transaction_validity_period, + epoch_length: chain_genesis.epoch_length, + block_economics_config: BlockEconomicsConfig::from(chain_genesis), + doomslug_threshold_mode, + blocks_delay_tracker: Default::default(), + apply_chunks_sender: sc, + apply_chunks_receiver: rc, + last_time_head_updated: StaticClock::instant(), + pending_state_patch: Default::default(), + requested_state_parts: StateRequestTracker::new(), + snapshot_callbacks, + resharding_config: chain_config.resharding_config, + resharding_handle: ReshardingHandle::new(), + }) + } + + #[cfg(feature = "test_features")] + pub fn adv_disable_doomslug(&mut self) { + self.doomslug_threshold_mode = DoomslugThresholdMode::NoApprovals + } + + pub fn compute_bp_hash( + epoch_manager: &dyn EpochManagerAdapter, + epoch_id: EpochId, + prev_epoch_id: EpochId, + last_known_hash: &CryptoHash, + ) -> Result { + let bps = epoch_manager.get_epoch_block_producers_ordered(&epoch_id, last_known_hash)?; + let protocol_version = epoch_manager.get_epoch_protocol_version(&prev_epoch_id)?; + if checked_feature!("stable", BlockHeaderV3, protocol_version) { + let validator_stakes = bps.into_iter().map(|(bp, _)| bp); + Ok(CryptoHash::hash_borsh_iter(validator_stakes)) + } else { + let validator_stakes = bps.into_iter().map(|(bp, _)| bp.into_v1()); + Ok(CryptoHash::hash_borsh_iter(validator_stakes)) + } + } + + pub fn get_last_time_head_updated(&self) -> Instant { + self.last_time_head_updated + } + + /// Creates a light client block for the last final block from perspective of some other block + /// + /// # Arguments + /// * `header` - the last finalized block seen from `header` (not pushed back) will be used to + /// compute the light client block + pub fn create_light_client_block( + header: &BlockHeader, + epoch_manager: &dyn EpochManagerAdapter, + chain_store: &dyn ChainStoreAccess, + ) -> Result { + let final_block_header = { + let ret = chain_store.get_block_header(header.last_final_block())?; + let two_ahead = chain_store.get_block_header_by_height(ret.height() + 2)?; + if two_ahead.epoch_id() != ret.epoch_id() { + let one_ahead = chain_store.get_block_header_by_height(ret.height() + 1)?; + if one_ahead.epoch_id() != ret.epoch_id() { + let new_final_hash = *ret.last_final_block(); + chain_store.get_block_header(&new_final_hash)? + } else { + let new_final_hash = *one_ahead.last_final_block(); + chain_store.get_block_header(&new_final_hash)? + } + } else { + ret + } + }; + + let next_block_producers = get_epoch_block_producers_view( + final_block_header.next_epoch_id(), + header.prev_hash(), + epoch_manager, + )?; + + create_light_client_block_view(&final_block_header, chain_store, Some(next_block_producers)) + } + + pub fn save_block(&mut self, block: MaybeValidated) -> Result<(), Error> { + if self.chain_store.get_block(block.hash()).is_ok() { + return Ok(()); + } + let mut chain_store_update = ChainStoreUpdate::new(&mut self.chain_store); + + chain_store_update.save_block(block.into_inner()); + // We don't need to increase refcount for `prev_hash` at this point + // because this is the block before State Sync. + + chain_store_update.commit()?; + Ok(()) + } + + fn save_block_height_processed(&mut self, block_height: BlockHeight) -> Result<(), Error> { + let mut chain_store_update = ChainStoreUpdate::new(&mut self.chain_store); + if !chain_store_update.is_height_processed(block_height)? { + chain_store_update.save_block_height_processed(block_height); + } + chain_store_update.commit()?; + Ok(()) + } + + fn maybe_mark_block_invalid(&mut self, block_hash: CryptoHash, error: &Error) { + metrics::NUM_INVALID_BLOCKS.with_label_values(&[error.prometheus_label_value()]).inc(); + // We only mark the block as invalid if the block has bad data (not for other errors that would + // not be the fault of the block itself), except when the block has a bad signature which means + // the block might not have been what the block producer originally produced. Either way, it's + // OK if we miss some cases here because this is just an optimization to avoid reprocessing + // known invalid blocks so the network recovers faster in case of any issues. + if error.is_bad_data() && !matches!(error, Error::InvalidSignature) { + self.invalid_blocks.put(block_hash, ()); + } + } + + /// Return a StateSyncInfo that includes the information needed for syncing state for shards needed + /// in the next epoch. + fn get_state_sync_info( + &self, + me: &Option, + block: &Block, + ) -> Result, Error> { + let prev_hash = *block.header().prev_hash(); + let shards_to_state_sync = Chain::get_shards_to_state_sync( + self.epoch_manager.as_ref(), + &self.shard_tracker, + me, + &prev_hash, + )?; + let prev_block = self.get_block(&prev_hash)?; + + if prev_block.chunks().len() != block.chunks().len() && !shards_to_state_sync.is_empty() { + // Currently, the state sync algorithm assumes that the number of chunks do not change + // between the epoch being synced to and the last epoch. + // For example, if shard layout changes at the beginning of epoch T, validators + // will not be able to sync states at epoch T for epoch T+1 + // Fortunately, since all validators track all shards for now, this error will not be + // triggered in live yet + // Instead of propagating the error, we simply log the error here because the error + // do not affect processing blocks for this epoch. However, when the next epoch comes, + // the validator will not have the states ready so it will halt. + error!( + "Cannot download states for epoch {:?} because sharding just changed. I'm {:?}", + block.header().epoch_id(), + me + ); + debug_assert!(false); + } + if shards_to_state_sync.is_empty() { + Ok(None) + } else { + debug!(target: "chain", "Downloading state for {:?}, I'm {:?}", shards_to_state_sync, me); + + let state_sync_info = StateSyncInfo { + epoch_tail_hash: *block.header().hash(), + shards: shards_to_state_sync + .iter() + .map(|shard_id| { + let chunk = &prev_block.chunks()[*shard_id as usize]; + ShardInfo(*shard_id, chunk.chunk_hash()) + }) + .collect(), + }; + + Ok(Some(state_sync_info)) + } + } + + /// Do basic validation of a block upon receiving it. Check that block is + /// well-formed (various roots match). + pub fn validate_block(&self, block: &MaybeValidated) -> Result<(), Error> { + block + .validate_with(|block| { + Chain::validate_block_impl(self.epoch_manager.as_ref(), self.genesis_block(), block) + .map(|_| true) + }) + .map(|_| ()) + } + + fn validate_block_impl( + epoch_manager: &dyn EpochManagerAdapter, + genesis_block: &Block, + block: &Block, + ) -> Result<(), Error> { + for (shard_id, chunk_header) in block.chunks().iter().enumerate() { + if chunk_header.height_created() == genesis_block.header().height() { + // Special case: genesis chunks can be in non-genesis blocks and don't have a signature + // We must verify that content matches and signature is empty. + // TODO: this code will not work when genesis block has different number of chunks as the current block + // https://github.com/utnet-org/utility/issues/4908 + let chunks = genesis_block.chunks(); + let genesis_chunk = chunks.get(shard_id); + let genesis_chunk = genesis_chunk.ok_or(Error::InvalidChunk)?; + + if genesis_chunk.chunk_hash() != chunk_header.chunk_hash() + || genesis_chunk.signature() != chunk_header.signature() + { + return Err(Error::InvalidChunk); + } + } else if chunk_header.height_created() == block.header().height() { + if chunk_header.shard_id() != shard_id as ShardId { + return Err(Error::InvalidShardId(chunk_header.shard_id())); + } + if !epoch_manager.verify_chunk_header_signature( + &chunk_header.clone(), + block.header().epoch_id(), + block.header().prev_hash(), + )? { + byzantine_assert!(false); + return Err(Error::InvalidChunk); + } + } + } + block.check_validity().map_err(|e| >::into(e))?; + Ok(()) + } + + /// Verify header signature when the epoch is known, but not the whole chain. + /// Same as verify_header_signature except it does not verify that block producer hasn't been slashed + fn partial_verify_orphan_header_signature(&self, header: &BlockHeader) -> Result { + let block_producer = + self.epoch_manager.get_block_producer(header.epoch_id(), header.height())?; + // DEVNOTE: we pass head which is not necessarily on block's chain, but it's only used for + // slashing info which we will ignore + let head = self.head()?; + let (block_producer, _slashed) = self.epoch_manager.get_validator_by_account_id( + header.epoch_id(), + &head.last_block_hash, + &block_producer, + )?; + Ok(header.signature().verify(header.hash().as_ref(), block_producer.public_key())) + } + + /// Optimization which checks if block with the given header can be reached from final head, and thus can be + /// finalized by this node. + /// If this is the case, returns Ok. + /// If we discovered that it is not the case, returns `Error::CannotBeFinalized`. + /// If too many parents were checked, returns Ok to avoid long delays. + fn check_if_finalizable(&self, header: &BlockHeader) -> Result<(), Error> { + let mut header = header.clone(); + let final_head = self.final_head()?; + for _ in 0..NUM_PARENTS_TO_CHECK_FINALITY { + // If we reached final head, then block can be finalized. + if header.hash() == &final_head.last_block_hash { + return Ok(()); + } + // If we went behind final head, then block cannot be finalized on top of final head. + if header.height() < final_head.height { + return Err(Error::CannotBeFinalized); + } + // Otherwise go to parent block. + header = match self.get_previous_header(&header) { + Ok(header) => header, + Err(_) => { + // We couldn't find previous header. Return Ok because it can be an orphaned block which can be + // connected to canonical chain later. + return Ok(()); + } + } + } + + // If we traversed too many blocks, return Ok to avoid long delays. + Ok(()) + } + + /// Validate header. Returns error if the header is invalid. + /// `challenges`: the function will add new challenges generated from validating this header + /// to the vector. You can pass an empty vector here, or a vector with existing + /// challenges already. + fn validate_header( + &self, + header: &BlockHeader, + provenance: &Provenance, + challenges: &mut Vec, + ) -> Result<(), Error> { + // Refuse blocks from the too distant future. + if header.timestamp() > StaticClock::utc() + Duration::seconds(ACCEPTABLE_TIME_DIFFERENCE) { + return Err(Error::InvalidBlockFutureTime(header.timestamp())); + } + + // Check the signature. + if !self.epoch_manager.verify_header_signature(header)? { + return Err(Error::InvalidSignature); + } + + // Check we don't know a block with given height already. + // If we do - send out double sign challenge and keep going as double signed blocks are valid blocks. + // Check if there is already known block of the same height that has the same epoch id + if let Some(block_hashes) = + self.chain_store.get_all_block_hashes_by_height(header.height())?.get(header.epoch_id()) + { + // This should be guaranteed but it doesn't hurt to check again + if !block_hashes.contains(header.hash()) { + let other_header = self.get_block_header(block_hashes.iter().next().unwrap())?; + + challenges.push(ChallengeBody::BlockDoubleSign(BlockDoubleSign { + left_block_header: borsh::to_vec(&header).expect("Failed to serialize"), + right_block_header: borsh::to_vec(&other_header).expect("Failed to serialize"), + })); + } + } + + #[cfg(feature = "protocol_feature_reject_blocks_with_outdated_protocol_version")] + if let Ok(epoch_protocol_version) = + self.epoch_manager.get_epoch_protocol_version(header.epoch_id()) + { + if checked_feature!( + "protocol_feature_reject_blocks_with_outdated_protocol_version", + RejectBlocksWithOutdatedProtocolVersions, + epoch_protocol_version + ) { + if header.latest_protocol_version() < epoch_protocol_version { + error!( + "header protocol version {} smaller than epoch protocol version {}", + header.latest_protocol_version(), + epoch_protocol_version + ); + return Err(Error::InvalidProtocolVersion); + } + } + } + + let prev_header = self.get_previous_header(header)?; + + // Check that epoch_id in the header does match epoch given previous header (only if previous header is present). + let epoch_id_from_prev_block = + &self.epoch_manager.get_epoch_id_from_prev_block(header.prev_hash())?; + let epoch_id_from_header = header.epoch_id(); + if epoch_id_from_prev_block != epoch_id_from_header { + return Err(Error::InvalidEpochHash); + } + + // Check that epoch_id in the header does match epoch given previous header (only if previous header is present). + if &self.epoch_manager.get_next_epoch_id_from_prev_block(header.prev_hash())? + != header.next_epoch_id() + { + return Err(Error::InvalidEpochHash); + } + + if header.epoch_id() == prev_header.epoch_id() { + if header.next_bp_hash() != prev_header.next_bp_hash() { + return Err(Error::InvalidNextBPHash); + } + } else { + if header.next_bp_hash() + != &Chain::compute_bp_hash( + self.epoch_manager.as_ref(), + header.next_epoch_id().clone(), + header.epoch_id().clone(), + header.prev_hash(), + )? + { + return Err(Error::InvalidNextBPHash); + } + } + + if header.chunk_mask().len() != self.epoch_manager.shard_ids(header.epoch_id())?.len() { + return Err(Error::InvalidChunkMask); + } + + if !header.verify_chunks_included() { + return Err(Error::InvalidChunkMask); + } + + if let Some(prev_height) = header.prev_height() { + if prev_height != prev_header.height() { + return Err(Error::Other("Invalid prev_height".to_string())); + } + } + + // Prevent time warp attacks and some timestamp manipulations by forcing strict + // time progression. + if header.raw_timestamp() <= prev_header.raw_timestamp() { + return Err(Error::InvalidBlockPastTime(prev_header.timestamp(), header.timestamp())); + } + // If this is not the block we produced (hence trust in it) - validates block + // producer, confirmation signatures and finality info. + if *provenance != Provenance::PRODUCED { + // first verify aggregated signature + if !self.epoch_manager.verify_approval( + prev_header.hash(), + prev_header.height(), + header.height(), + header.approvals(), + )? { + return Err(Error::InvalidApprovals); + }; + + let stakes = self + .epoch_manager + .get_epoch_block_approvers_ordered(header.prev_hash())? + .iter() + .map(|(x, is_slashed)| (x.frozen_this_epoch, x.frozen_next_epoch, *is_slashed)) + .collect::>(); + if !Doomslug::can_approved_block_be_produced( + self.doomslug_threshold_mode, + header.approvals(), + &stakes, + ) { + return Err(Error::NotEnoughApprovals); + } + + let expected_last_ds_final_block = if prev_header.height() + 1 == header.height() { + prev_header.hash() + } else { + prev_header.last_ds_final_block() + }; + + let expected_last_final_block = if prev_header.height() + 1 == header.height() + && prev_header.last_ds_final_block() == prev_header.prev_hash() + { + prev_header.prev_hash() + } else { + prev_header.last_final_block() + }; + + if header.last_ds_final_block() != expected_last_ds_final_block + || header.last_final_block() != expected_last_final_block + { + return Err(Error::InvalidFinalityInfo); + } + + let block_merkle_tree = self.chain_store.get_block_merkle_tree(header.prev_hash())?; + let mut block_merkle_tree = PartialMerkleTree::clone(&block_merkle_tree); + block_merkle_tree.insert(*header.prev_hash()); + if &block_merkle_tree.root() != header.block_merkle_root() { + return Err(Error::InvalidBlockMerkleRoot); + } + + // Check that challenges root is empty to ensure later that block doesn't contain challenges. + // TODO (#2445): Enable challenges when they are working correctly. + if header.challenges_root() != &MerkleHash::default() { + return Err(Error::InvalidChallengeRoot); + } + if !header.challenges_result().is_empty() { + return Err(Error::InvalidChallenge); + } + } + + Ok(()) + } + + /// Process block header as part of "header first" block propagation. + /// We validate the header but we do not store it or update header head + /// based on this. We will update these once we get the block back after + /// requesting it. + pub fn process_block_header( + &self, + header: &BlockHeader, + challenges: &mut Vec, + ) -> Result<(), Error> { + debug!(target: "chain", "Process block header: {} at {}", header.hash(), header.height()); + + check_known(self, header.hash())?.map_err(|e| Error::BlockKnown(e))?; + self.validate_header(header, &Provenance::NONE, challenges)?; + Ok(()) + } + + /// Verify that the block signature and block body hash matches. It makes sure that the block + /// content is not tampered by a middle man. + /// Returns Correct if the both check succeeds. Returns Incorrect if either check fails. + /// Returns CannotVerifyBecauseBlockIsOrphan, if we could not verify the signature because + /// the parent block is not yet available. + pub fn verify_block_hash_and_signature( + &self, + block: &Block, + ) -> Result { + // skip the verification if we are processing the genesis block + if block.hash() == self.genesis.hash() { + return Ok(VerifyBlockHashAndSignatureResult::Correct); + } + let epoch_id = match self.epoch_manager.get_epoch_id(block.header().prev_hash()) { + Ok(epoch_id) => epoch_id, + Err(EpochError::MissingBlock(missing_block)) + if &missing_block == block.header().prev_hash() => + { + return Ok(VerifyBlockHashAndSignatureResult::CannotVerifyBecauseBlockIsOrphan); + } + Err(err) => return Err(err.into()), + }; + let epoch_protocol_version = self.epoch_manager.get_epoch_protocol_version(&epoch_id)?; + // Check that block body hash matches the block body. This makes sure that the block body + // content is not tampered + if checked_feature!("stable", BlockHeaderV4, epoch_protocol_version) { + let block_body_hash = block.compute_block_body_hash(); + if block_body_hash.is_none() { + tracing::warn!("Block version too old for block: {:?}", block.hash()); + return Ok(VerifyBlockHashAndSignatureResult::Incorrect); + } + if block.header().block_body_hash() != block_body_hash { + tracing::warn!("Invalid block body hash for block: {:?}", block.hash()); + return Ok(VerifyBlockHashAndSignatureResult::Incorrect); + } + } + + // Verify the signature. Since the signature is signed on the hash of block header, this check + // makes sure the block header content is not tampered + if !self.epoch_manager.verify_header_signature(block.header())? { + tracing::error!("wrong signature"); + return Ok(VerifyBlockHashAndSignatureResult::Incorrect); + } + Ok(VerifyBlockHashAndSignatureResult::Correct) + } + + /// Verify that `challenges` are valid + /// If all challenges are valid, returns ChallengesResult, which comprises of the list of + /// validators that need to be slashed and the list of blocks that are challenged. + /// Returns Error if any challenge is invalid. + /// Note: you might be wondering why the list of challenged blocks is not part of ChallengesResult. + /// That's because ChallengesResult is part of BlockHeader, to modify that struct requires protocol + /// upgrade. + pub fn verify_challenges( + &self, + challenges: &[Challenge], + epoch_id: &EpochId, + prev_block_hash: &CryptoHash, + ) -> Result<(ChallengesResult, Vec), Error> { + let _span = tracing::debug_span!( + target: "chain", + "verify_challenges", + ?challenges) + .entered(); + let mut result = vec![]; + let mut challenged_blocks = vec![]; + for challenge in challenges.iter() { + match validate_challenge( + self.epoch_manager.as_ref(), + self.runtime_adapter.as_ref(), + epoch_id, + prev_block_hash, + challenge, + ) { + Ok((hash, account_ids)) => { + let is_double_sign = match challenge.body { + // If it's double signed block, we don't invalidate blocks just slash. + ChallengeBody::BlockDoubleSign(_) => true, + _ => { + challenged_blocks.push(hash); + false + } + }; + let slash_validators: Vec<_> = account_ids + .into_iter() + .map(|id| SlashedValidator::new(id, is_double_sign)) + .collect(); + result.extend(slash_validators); + } + Err(Error::MaliciousChallenge) => { + result.push(SlashedValidator::new(challenge.account_id.clone(), false)); + } + Err(err) => return Err(err), + } + } + Ok((result, challenged_blocks)) + } + + /// Do basic validation of the information that we can get from the chunk headers in `block` + fn validate_chunk_headers(&self, block: &Block, prev_block: &Block) -> Result<(), Error> { + let prev_chunk_headers = + Chain::get_prev_chunk_headers(self.epoch_manager.as_ref(), prev_block)?; + for (chunk_header, prev_chunk_header) in + block.chunks().iter().zip(prev_chunk_headers.iter()) + { + if chunk_header.height_included() == block.header().height() { + if chunk_header.prev_block_hash() != block.header().prev_hash() { + return Err(Error::InvalidChunk); + } + } else { + if prev_chunk_header != chunk_header { + return Err(Error::InvalidChunk); + } + } + } + + // Verify that proposals from chunks match block header proposals. + let block_height = block.header().height(); + for pair in block + .chunks() + .iter() + .filter(|chunk| block_height == chunk.height_included()) + .flat_map(|chunk| chunk.prev_validator_power_proposals()) + .zip_longest(block.header().prev_validator_power_proposals()) + { + match pair { + itertools::EitherOrBoth::Both(cp, hp) => { + if hp != cp { + // Proposals differed! + return Err(Error::InvalidValidatorProposals); + } + } + _ => { + // Can only occur if there were a different number of proposals in the header + // and chunks + return Err(Error::InvalidValidatorProposals); + } + } + } + + for pair in block + .chunks() + .iter() + .filter(|chunk| block_height == chunk.height_included()) + .flat_map(|chunk| chunk.prev_validator_frozen_proposals()) + .zip_longest(block.header().prev_validator_frozen_proposals()) + { + match pair { + itertools::EitherOrBoth::Both(cp, hp) => { + if hp != cp { + // Proposals differed! + return Err(Error::InvalidValidatorProposals); + } + } + _ => { + // Can only occur if there were a different number of proposals in the header + // and chunks + return Err(Error::InvalidValidatorProposals); + } + } + } + + Ok(()) + } + + /// Check if the chain leading to the given block has challenged blocks on it. Returns Ok if the chain + /// does not have challenged blocks, otherwise error ChallengedBlockOnChain. + fn check_if_challenged_block_on_chain(&self, block_header: &BlockHeader) -> Result<(), Error> { + let mut hash = *block_header.hash(); + let mut height = block_header.height(); + let mut prev_hash = *block_header.prev_hash(); + loop { + match self.get_block_hash_by_height(height) { + Ok(cur_hash) if cur_hash == hash => { + // Found common ancestor. + return Ok(()); + } + _ => { + if self.chain_store.is_block_challenged(&hash)? { + return Err(Error::ChallengedBlockOnChain); + } + let prev_header = self.get_block_header(&prev_hash)?; + hash = *prev_header.hash(); + height = prev_header.height(); + prev_hash = *prev_header.prev_hash(); + } + }; + } + } + + pub fn ping_missing_chunks( + &self, + me: &Option, + parent_hash: CryptoHash, + block: &Block, + ) -> Result<(), Error> { + if !self.care_about_any_shard_or_part(me, parent_hash)? { + return Ok(()); + } + let mut missing = vec![]; + let height = block.header().height(); + for (shard_id, chunk_header) in block.chunks().iter().enumerate() { + // Check if any chunks are invalid in this block. + if let Some(encoded_chunk) = + self.chain_store.is_invalid_chunk(&chunk_header.chunk_hash())? + { + let merkle_paths = Block::compute_chunk_headers_root(block.chunks().iter()).1; + let chunk_proof = ChunkProofs { + block_header: borsh::to_vec(&block.header()).expect("Failed to serialize"), + merkle_proof: merkle_paths[shard_id].clone(), + chunk: Box::new(MaybeEncodedShardChunk::Encoded(EncodedShardChunk::clone( + &encoded_chunk, + ))), + }; + return Err(Error::InvalidChunkProofs(Box::new(chunk_proof))); + } + let shard_id = shard_id as ShardId; + if chunk_header.height_included() == height { + let chunk_hash = chunk_header.chunk_hash(); + + if let Err(_) = self.chain_store.get_partial_chunk(&chunk_header.chunk_hash()) { + missing.push(chunk_header.clone()); + } else if self.shard_tracker.care_about_shard( + me.as_ref(), + &parent_hash, + shard_id, + true, + ) || self.shard_tracker.will_care_about_shard( + me.as_ref(), + &parent_hash, + shard_id, + true, + ) { + if let Err(_) = self.chain_store.get_chunk(&chunk_hash) { + missing.push(chunk_header.clone()); + } + } + } + } + if !missing.is_empty() { + return Err(Error::ChunksMissing(missing)); + } + Ok(()) + } + + fn care_about_any_shard_or_part( + &self, + me: &Option, + parent_hash: CryptoHash, + ) -> Result { + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(&parent_hash)?; + for shard_id in self.epoch_manager.shard_ids(&epoch_id)? { + if self.shard_tracker.care_about_shard(me.as_ref(), &parent_hash, shard_id, true) + || self.shard_tracker.will_care_about_shard( + me.as_ref(), + &parent_hash, + shard_id, + true, + ) + { + return Ok(true); + } + } + for part_id in 0..self.epoch_manager.num_total_parts() { + if &Some(self.epoch_manager.get_part_owner(&epoch_id, part_id as u64)?) == me { + return Ok(true); + } + } + Ok(false) + } + + /// Collect all incoming receipts generated in `block`, return a map from target shard id to the + /// list of receipts that the target shard receives. + /// The receipts are sorted by the order that they will be processed. + /// Note that the receipts returned in this function do not equal all receipts that will be + /// processed as incoming receipts in this block, because that may include incoming receipts + /// generated in previous blocks too, if some shards in the previous blocks did not produce + /// new chunks. + pub fn collect_incoming_receipts_from_block( + &self, + me: &Option, + block: &Block, + ) -> Result>, Error> { + if !self.care_about_any_shard_or_part(me, *block.header().prev_hash())? { + return Ok(HashMap::new()); + } + let height = block.header().height(); + let mut receipt_proofs_by_shard_id = HashMap::new(); + + for chunk_header in block.chunks().iter() { + if chunk_header.height_included() != height { + continue; + } + let partial_encoded_chunk = + self.chain_store.get_partial_chunk(&chunk_header.chunk_hash()).unwrap(); + for receipt in partial_encoded_chunk.receipts().iter() { + let ReceiptProof(_, shard_proof) = receipt; + let ShardProof { to_shard_id, .. } = shard_proof; + receipt_proofs_by_shard_id + .entry(*to_shard_id) + .or_insert_with(Vec::new) + .push(receipt.clone()); + } + } + // sort the receipts deterministically so the order that they will be processed is deterministic + for (_, receipt_proofs) in receipt_proofs_by_shard_id.iter_mut() { + shuffle_receipt_proofs(receipt_proofs, block.hash()); + } + + Ok(receipt_proofs_by_shard_id) + } + + /// Start processing a received or produced block. This function will process block asynchronously. + /// It preprocesses the block by verifying that the block is valid and ready to process, then + /// schedules the work of applying chunks in rayon thread pool. The function will return before + /// the block processing is finished. + /// This function is used in conjunction with the function postprocess_ready_blocks, which checks + /// if any of the blocks in processing has finished applying chunks to finish postprocessing + /// these blocks that are ready. + /// `block_processing_artifacts`: Callers can pass an empty object or an existing BlockProcessingArtifact. + /// This function will add the effect from processing this block to there. + /// `apply_chunks_done_callback`: This callback will be called after apply_chunks are finished + /// (so it also happens asynchronously in the rayon thread pool). Callers can + /// use this callback as a way to receive notifications when apply chunks are done + /// so it can call postprocess_ready_blocks. + pub fn start_process_block_async( + &mut self, + me: &Option, + block: MaybeValidated, + provenance: Provenance, + block_processing_artifacts: &mut BlockProcessingArtifact, + apply_chunks_done_callback: DoneApplyChunkCallback, + ) -> Result<(), Error> { + let _span = + debug_span!(target: "chain", "start_process_block_async", ?provenance).entered(); + let block_received_time = StaticClock::instant(); + metrics::BLOCK_PROCESSING_ATTEMPTS_TOTAL.inc(); + + let block_height = block.header().height(); + let hash = *block.hash(); + let res = self.start_process_block_impl( + me, + block, + provenance, + block_processing_artifacts, + apply_chunks_done_callback, + block_received_time, + ); + + if matches!(res, Err(Error::TooManyProcessingBlocks)) { + self.blocks_delay_tracker + .mark_block_dropped(&hash, DroppedReason::TooManyProcessingBlocks); + } + // Save the block as processed even if it failed. This is used to filter out the + // incoming blocks that are not requested on heights which we already processed. + // If there is a new incoming block that we didn't request and we already have height + // processed 'marked as true' - then we'll not even attempt to process it + if let Err(e) = self.save_block_height_processed(block_height) { + warn!(target: "chain", "Failed to save processed height {}: {}", block_height, e); + } + + res + } + + /// Checks if any block has finished applying chunks and postprocesses these blocks to complete + /// their processing. Return a list of blocks that have finished processing. + /// If there are no blocks that are ready to be postprocessed, it returns immediately + /// with an empty list. Even if there are blocks being processed, it does not wait + /// for these blocks to be ready. + pub fn postprocess_ready_blocks( + &mut self, + me: &Option, + block_processing_artifacts: &mut BlockProcessingArtifact, + apply_chunks_done_callback: DoneApplyChunkCallback, + ) -> (Vec, HashMap) { + let _span = debug_span!(target: "chain", "postprocess_ready_blocks_chain").entered(); + let mut accepted_blocks = vec![]; + let mut errors = HashMap::new(); + while let Ok((block_hash, apply_result)) = self.apply_chunks_receiver.try_recv() { + match self.postprocess_block( + me, + block_hash, + apply_result, + block_processing_artifacts, + apply_chunks_done_callback.clone(), + ) { + Err(e) => { + errors.insert(block_hash, e); + } + Ok(accepted_block) => { + accepted_blocks.push(accepted_block); + } + } + } + (accepted_blocks, errors) + } + + /// Process challenge to invalidate chain. This is done between blocks to unroll the chain as + /// soon as possible and allow next block producer to skip invalid blocks. + pub fn process_challenge(&mut self, challenge: &Challenge) { + let head = unwrap_or_return!(self.head()); + match self.verify_challenges(&[challenge.clone()], &head.epoch_id, &head.last_block_hash) { + Ok((_, challenged_blocks)) => { + let mut chain_update = self.chain_update(); + for block_hash in challenged_blocks { + match chain_update.mark_block_as_challenged(&block_hash, None) { + Ok(()) => {} + Err(err) => { + warn!(target: "chain", %block_hash, ?err, "Error saving block as challenged"); + } + } + } + unwrap_or_return!(chain_update.commit()); + } + Err(err) => { + warn!(target: "chain", ?err, "Invalid challenge: {:#?}", challenge); + } + } + } + + /// Processes headers and adds them to store for syncing. + pub fn sync_block_headers( + &mut self, + mut headers: Vec, + challenges: &mut Vec, + ) -> Result<(), Error> { + // Sort headers by heights. + headers.sort_by_key(|left| left.height()); + + if let (Some(first_header), Some(last_header)) = (headers.first(), headers.last()) { + info!( + target: "chain", + num_headers = headers.len(), + first_hash = ?first_header.hash(), + first_height = first_header.height(), + last_hash = ?last_header.hash(), + last_height = ?last_header.height(), + "Sync block headers"); + } else { + // No headers. + return Ok(()); + }; + + // Performance optimization to skip looking up every header in the store. + let all_known = if let Some(last_header) = headers.last() { + // If the last header is known, then the other headers are known too. + self.chain_store.get_block_header(last_header.hash()).is_ok() + } else { + // Empty set of headers, therefore all received headers are known. + true + }; + + if all_known { + return Ok(()); + } + + // Validate header and then add to the chain. + for header in headers.iter() { + match check_header_known(self, header)? { + Ok(_) => {} + Err(_) => continue, + } + + self.validate_header(header, &Provenance::SYNC, challenges)?; + let mut chain_store_update = self.chain_store.store_update(); + chain_store_update.save_block_header(header.clone())?; + + // Add validator proposals for given header. + let last_finalized_height = + chain_store_update.get_block_height(header.last_final_block())?; + let epoch_manager_update = self + .epoch_manager + .add_validator_proposals(BlockHeaderInfo::new(header, last_finalized_height))?; + chain_store_update.merge(epoch_manager_update); + chain_store_update.commit()?; + + #[cfg(feature = "new_epoch_sync")] + { + // At this point BlockInfo for this header should be in DB and in `epoch_manager`s cache because of `add_validator_proposals` call. + let mut chain_update = self.chain_update(); + chain_update.save_epoch_sync_info_if_finalised(header)?; + chain_update.commit()?; + } + } + + let mut chain_update = self.chain_update(); + if let Some(header) = headers.last() { + // Update header_head if it's the new tip + chain_update.update_header_head_if_not_challenged(header)?; + } + chain_update.commit() + } + + /// Returns if given block header is on the current chain. + /// + /// This is done by fetching header by height and checking that it’s the + /// same one as provided. + fn is_on_current_chain(&self, header: &BlockHeader) -> Result { + let chain_header = self.get_block_header_by_height(header.height())?; + Ok(chain_header.hash() == header.hash()) + } + + /// Finds first of the given hashes that is known on the main chain. + pub fn find_common_header(&self, hashes: &[CryptoHash]) -> Option { + for hash in hashes { + if let Ok(header) = self.get_block_header(hash) { + if let Ok(header_at_height) = self.get_block_header_by_height(header.height()) { + if header.hash() == header_at_height.hash() { + return Some(header); + } + } + } + } + None + } + + fn determine_status(&self, head: Option, prev_head: Tip) -> BlockStatus { + let has_head = head.is_some(); + let mut is_next_block = false; + + let old_hash = if let Some(head) = head { + if head.prev_block_hash == prev_head.last_block_hash { + is_next_block = true; + None + } else { + Some(prev_head.last_block_hash) + } + } else { + None + }; + + match (has_head, is_next_block) { + (true, true) => BlockStatus::Next, + (true, false) => BlockStatus::Reorg(old_hash.unwrap()), + (false, _) => BlockStatus::Fork, + } + } + + /// Set the new head after state sync was completed if it is indeed newer. + /// Check for potentially unlocked orphans after this update. + pub fn reset_heads_post_state_sync( + &mut self, + me: &Option, + sync_hash: CryptoHash, + block_processing_artifacts: &mut BlockProcessingArtifact, + apply_chunks_done_callback: DoneApplyChunkCallback, + ) -> Result<(), Error> { + let _span = tracing::debug_span!(target: "sync", "reset_heads_post_state_sync").entered(); + // Get header we were syncing into. + let header = self.get_block_header(&sync_hash)?; + let hash = *header.prev_hash(); + let prev_block = self.get_block(&hash)?; + let new_tail = prev_block.header().height(); + let new_chunk_tail = prev_block.chunks().iter().map(|x| x.height_created()).min().unwrap(); + let tip = Tip::from_header(prev_block.header()); + let final_head = Tip::from_header(self.genesis.header()); + // Update related heads now. + let mut chain_store_update = self.mut_chain_store().store_update(); + chain_store_update.save_body_head(&tip)?; + // Reset final head to genesis since at this point we don't have the last final block. + chain_store_update.save_final_head(&final_head)?; + // New Tail can not be earlier than `prev_block.header.inner_lite.height` + chain_store_update.update_tail(new_tail)?; + // New Chunk Tail can not be earlier than minimum of height_created in Block `prev_block` + chain_store_update.update_chunk_tail(new_chunk_tail); + chain_store_update.commit()?; + + // Check if there are any orphans unlocked by this state sync. + // We can't fail beyond this point because the caller will not process accepted blocks + // and the blocks with missing chunks if this method fails + self.check_orphans(me, hash, block_processing_artifacts, apply_chunks_done_callback); + Ok(()) + } + + // Unlike start_process_block() this function doesn't update metrics for + // successful blocks processing. + fn start_process_block_impl( + &mut self, + me: &Option, + block: MaybeValidated, + provenance: Provenance, + block_processing_artifact: &mut BlockProcessingArtifact, + apply_chunks_done_callback: DoneApplyChunkCallback, + block_received_time: Instant, + ) -> Result<(), Error> { + let block_height = block.header().height(); + let _span = + debug_span!(target: "chain", "start_process_block_impl", block_height).entered(); + // 0) Before we proceed with any further processing, we first check that the block + // hash and signature matches to make sure the block is indeed produced by the assigned + // block producer. If not, we drop the block immediately + // Note that it may appear that we call verify_block_hash_signature twice, once in + // receive_block_impl, once here. The redundancy is because if a block is received as an orphan, + // the check in receive_block_impl will not be complete and the block will be stored in + // the orphan pool. When the orphaned block is ready to be processed, we must perform this check. + // Also note that we purposely separates the check from the rest of the block verification check in + // preprocess_block. + if self.verify_block_hash_and_signature(&block)? + == VerifyBlockHashAndSignatureResult::Incorrect + { + return Err(Error::InvalidSignature); + } + + // 1) preprocess the block where we verify that the block is valid and ready to be processed + // No chain updates are applied at this step. + let state_patch = self.pending_state_patch.take(); + let preprocess_timer = metrics::BLOCK_PREPROCESSING_TIME.start_timer(); + let preprocess_res = self.preprocess_block( + me, + &block, + &provenance, + &mut block_processing_artifact.challenges, + &mut block_processing_artifact.invalid_chunks, + block_received_time, + state_patch, + ); + let preprocess_res = match preprocess_res { + Ok(preprocess_res) => { + preprocess_timer.observe_duration(); + preprocess_res + } + Err(e) => { + self.maybe_mark_block_invalid(*block.hash(), &e); + preprocess_timer.stop_and_discard(); + match &e { + Error::Orphan => { + let tail_height = self.chain_store.tail()?; + // we only add blocks that couldn't have been gc'ed to the orphan pool. + if block_height >= tail_height { + let requested_missing_chunks = if let Some(orphan_missing_chunks) = + self.should_request_chunks_for_orphan(me, &block) + { + block_processing_artifact + .orphans_missing_chunks + .push(orphan_missing_chunks); + true + } else { + false + }; + + let time = StaticClock::instant(); + self.blocks_delay_tracker.mark_block_orphaned(block.hash(), time); + self.save_orphan( + block, + provenance, + Some(time), + requested_missing_chunks, + ); + } + } + Error::ChunksMissing(missing_chunks) => { + let block_hash = *block.hash(); + let missing_chunk_hashes: Vec<_> = + missing_chunks.iter().map(|header| header.chunk_hash()).collect(); + block_processing_artifact.blocks_missing_chunks.push(BlockMissingChunks { + prev_hash: *block.header().prev_hash(), + missing_chunks: missing_chunks.clone(), + }); + let time = StaticClock::instant(); + self.blocks_delay_tracker.mark_block_has_missing_chunks(block.hash(), time); + let orphan = Orphan { block, provenance, added: time }; + self.blocks_with_missing_chunks + .add_block_with_missing_chunks(orphan, missing_chunk_hashes.clone()); + debug!( + target: "chain", + "Process block: missing chunks. Block hash: {:?}. Missing chunks: {:?}", + block_hash, missing_chunk_hashes, + ); + } + Error::EpochOutOfBounds(epoch_id) => { + // Possibly block arrived before we finished processing all of the blocks for epoch before last. + // Or someone is attacking with invalid chain. + debug!(target: "chain", "Received block {}/{} ignored, as epoch {:?} is unknown", block_height, block.hash(), epoch_id); + } + Error::BlockKnown(block_known_error) => { + debug!( + target: "chain", + "Block {} at {} is known at this time: {:?}", + block.hash(), + block_height, + block_known_error); + } + _ => {} + } + return Err(e); + } + }; + let (apply_chunk_work, block_preprocess_info) = preprocess_res; + + // 2) Start creating snapshot if needed. + if let Err(err) = self.process_snapshot() { + tracing::error!(target: "state_snapshot", ?err, "Failed to make a state snapshot"); + } + + let block = block.into_inner(); + let block_hash = *block.hash(); + let block_height = block.header().height(); + let apply_chunks_done_marker = block_preprocess_info.apply_chunks_done.clone(); + self.blocks_in_processing.add(block, block_preprocess_info)?; + + // 3) schedule apply chunks, which will be executed in the rayon thread pool. + self.schedule_apply_chunks( + block_hash, + block_height, + apply_chunk_work, + apply_chunks_done_marker, + apply_chunks_done_callback.clone(), + ); + + Ok(()) + } + + /// Applying chunks async by starting the work at the rayon thread pool + /// `apply_chunks_done_marker`: a marker that will be set to true once applying chunks is finished + /// `apply_chunks_done_callback`: a callback that will be called once applying chunks is finished + fn schedule_apply_chunks( + &self, + block_hash: CryptoHash, + block_height: BlockHeight, + work: Vec, + apply_chunks_done_marker: Arc>, + apply_chunks_done_callback: DoneApplyChunkCallback, + ) { + let sc = self.apply_chunks_sender.clone(); + spawn(move || { + // do_apply_chunks runs `work` in parallel, but still waits for all of them to finish + let res = do_apply_chunks(block_hash, block_height, work); + // If we encounter error here, that means the receiver is deallocated and the client + // thread is already shut down. The node is already crashed, so we can unwrap here + sc.send((block_hash, res)).unwrap(); + if let Err(_) = apply_chunks_done_marker.set(()) { + // This should never happen, if it does, it means there is a bug in our code. + log_assert!(false, "apply chunks are called twice for block {block_hash:?}"); + } + apply_chunks_done_callback(block_hash); + }); + + /// `rayon::spawn` decorated to propagate `tracing` context across + /// threads. + fn spawn(f: impl FnOnce() + Send + 'static) { + let dispatcher = tracing::dispatcher::get_default(|it| it.clone()); + rayon::spawn(move || tracing::dispatcher::with_default(&dispatcher, f)) + } + } + + fn postprocess_block_only( + &mut self, + me: &Option, + block: &Block, + block_preprocess_info: BlockPreprocessInfo, + apply_results: Vec<(ShardId, Result)>, + ) -> Result, Error> { + let mut chain_update = self.chain_update(); + let new_head = + chain_update.postprocess_block(me, &block, block_preprocess_info, apply_results)?; + chain_update.commit()?; + Ok(new_head) + } + + /// Run postprocessing on this block, which stores the block on chain. + /// Check that if accepting the block unlocks any orphans in the orphan pool and start + /// the processing of those blocks. + fn postprocess_block( + &mut self, + me: &Option, + block_hash: CryptoHash, + apply_results: Vec<(ShardId, Result)>, + block_processing_artifacts: &mut BlockProcessingArtifact, + apply_chunks_done_callback: DoneApplyChunkCallback, + ) -> Result { + let timer = metrics::BLOCK_POSTPROCESSING_TIME.start_timer(); + let (block, block_preprocess_info) = + self.blocks_in_processing.remove(&block_hash).unwrap_or_else(|| { + panic!( + "block {:?} finished applying chunks but not in blocks_in_processing pool", + block_hash + ) + }); + // We want to include block height here, so we didn't put this line at the beginning of the + // function. + let _span = tracing::debug_span!( + target: "chain", + "postprocess_block", + height = block.header().height()) + .entered(); + + let prev_head = self.chain_store.head()?; + let is_caught_up = block_preprocess_info.is_caught_up; + let provenance = block_preprocess_info.provenance.clone(); + let block_start_processing_time = block_preprocess_info.block_start_processing_time; + // TODO(#8055): this zip relies on the ordering of the apply_results. + for (shard_id, apply_result) in apply_results.iter() { + if let Err(err) = apply_result { + if err.is_bad_data() { + let chunk = block.chunks()[*shard_id as usize].clone(); + block_processing_artifacts.invalid_chunks.push(chunk); + } + } + } + let new_head = + match self.postprocess_block_only(me, &block, block_preprocess_info, apply_results) { + Err(err) => { + self.maybe_mark_block_invalid(*block.hash(), &err); + self.blocks_delay_tracker.mark_block_errored(&block_hash, err.to_string()); + return Err(err); + } + Ok(new_head) => new_head, + }; + + // Update flat storage head to be the last final block. Note that this update happens + // in a separate db transaction from the update from block processing. This is intentional + // because flat_storage need to be locked during the update of flat head, otherwise + // flat_storage is in an inconsistent state that could be accessed by the other + // apply chunks processes. This means, the flat head is not always the same as + // the last final block on chain, which is OK, because in the flat storage implementation + // we don't assume that. + let epoch_id = block.header().epoch_id(); + for shard_id in self.epoch_manager.shard_ids(epoch_id)? { + let need_flat_storage_update = if is_caught_up { + // If we already caught up this epoch, then flat storage exists for both shards which we already track + // and shards which will be tracked in next epoch, so we can update them. + self.shard_tracker.care_about_shard( + me.as_ref(), + block.header().prev_hash(), + shard_id, + true, + ) || self.shard_tracker.will_care_about_shard( + me.as_ref(), + block.header().prev_hash(), + shard_id, + true, + ) + } else { + // If we didn't catch up, we can update only shards tracked right now. Remaining shards will be updated + // during catchup of this block. + self.shard_tracker.care_about_shard( + me.as_ref(), + block.header().prev_hash(), + shard_id, + true, + ) + }; + + if need_flat_storage_update { + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, epoch_id)?; + let flat_storage_manager = self.runtime_adapter.get_flat_storage_manager(); + flat_storage_manager.update_flat_storage_for_shard(shard_uid, &block)?; + self.garbage_collect_memtrie_roots(&block, shard_uid); + } + } + + self.pending_state_patch.clear(); + + if let Some(tip) = &new_head { + // TODO: move this logic of tracking validators metrics to EpochManager + let mut count = 0; + let mut stake = 0; + if let Ok(producers) = self.epoch_manager.get_epoch_chunk_producers(&tip.epoch_id) { + stake += producers.iter().map(|info| info.frozen()).sum::(); + count += producers.len(); + } + + stake /= UNC_BASE; + metrics::VALIDATOR_AMOUNT_STAKED.set(i64::try_from(stake).unwrap_or(i64::MAX)); + metrics::VALIDATOR_ACTIVE_TOTAL.set(i64::try_from(count).unwrap_or(i64::MAX)); + + self.last_time_head_updated = StaticClock::instant(); + }; + + metrics::BLOCK_PROCESSED_TOTAL.inc(); + metrics::BLOCK_PROCESSING_TIME.observe( + StaticClock::instant() + .saturating_duration_since(block_start_processing_time) + .as_secs_f64(), + ); + self.blocks_delay_tracker.finish_block_processing(&block_hash, new_head.clone()); + + timer.observe_duration(); + let _timer = CryptoHashTimer::new_with_start(*block.hash(), block_start_processing_time); + + self.check_orphans( + me, + *block.hash(), + block_processing_artifacts, + apply_chunks_done_callback, + ); + + // Determine the block status of this block (whether it is a side fork and updates the chain head) + // Block status is needed in Client::on_block_accepted_with_optional_chunk_produce to + // decide to how to update the tx pool. + let block_status = self.determine_status(new_head, prev_head); + Ok(AcceptedBlock { hash: *block.hash(), status: block_status, provenance }) + } + + fn garbage_collect_memtrie_roots(&self, block: &Block, shard_uid: ShardUId) { + let tries = self.runtime_adapter.get_tries(); + let last_final_block = block.header().last_final_block(); + if last_final_block != &CryptoHash::default() { + let header = self.chain_store.get_block_header(last_final_block).unwrap(); + if let Some(prev_height) = header.prev_height() { + tries.delete_memtrie_roots_up_to_height(shard_uid, prev_height); + } + } + } + + /// Preprocess a block before applying chunks, verify that we have the necessary information + /// to process the block an the block is valid. + /// Note that this function does NOT introduce any changes to chain state. + pub(crate) fn preprocess_block( + &self, + me: &Option, + block: &MaybeValidated, + provenance: &Provenance, + challenges: &mut Vec, + invalid_chunks: &mut Vec, + block_received_time: Instant, + state_patch: SandboxStatePatch, + ) -> Result { + let header = block.header(); + + // see if the block is already in processing or if there are too many blocks being processed + self.blocks_in_processing.add_dry_run(block.hash())?; + + debug!(target: "chain", num_approvals = header.num_approvals(), "Preprocess block"); + + // Check that we know the epoch of the block before we try to get the header + // (so that a block from unknown epoch doesn't get marked as an orphan) + if !self.epoch_manager.epoch_exists(header.epoch_id()) { + return Err(Error::EpochOutOfBounds(header.epoch_id().clone())); + } + + if block.chunks().len() != self.epoch_manager.shard_ids(header.epoch_id())?.len() { + return Err(Error::IncorrectNumberOfChunkHeaders); + } + + // Check if we have already processed this block previously. + check_known(self, header.hash())?.map_err(|e| Error::BlockKnown(e))?; + + // Delay hitting the db for current chain head until we know this block is not already known. + let head = self.head()?; + let is_next = header.prev_hash() == &head.last_block_hash; + + // Sandbox allows fast-forwarding, so only enable when not within sandbox + if !cfg!(feature = "sandbox") { + // A heuristic to prevent block height to jump too fast towards BlockHeight::max and cause + // overflow-related problems + let block_height = header.height(); + if block_height > head.height + self.epoch_length * 1000 { + return Err(Error::InvalidBlockHeight(block_height)); + } + } + + // Block is an orphan if we do not know about the previous full block. + if !is_next && !self.block_exists(header.prev_hash())? { + // Before we add the block to the orphan pool, do some checks: + // 1. Block header is signed by the block producer for height. + // 2. Chunk headers in block body match block header. + // 3. Header has enough approvals from epoch block producers. + // Not checked: + // - Block producer could be slashed + // - Chunk header signatures could be wrong + if !self.partial_verify_orphan_header_signature(header)? { + return Err(Error::InvalidSignature); + } + block.check_validity()?; + // TODO: enable after #3729 and #3863 + // self.verify_orphan_header_approvals(&header)?; + return Err(Error::Orphan); + } + + let epoch_protocol_version = + self.epoch_manager.get_epoch_protocol_version(header.epoch_id())?; + if epoch_protocol_version > PROTOCOL_VERSION { + panic!("The client protocol version is older than the protocol version of the network. Please update framework. Client protocol version:{}, network protocol version {}", PROTOCOL_VERSION, epoch_protocol_version); + } + + // First real I/O expense. + let prev = self.get_previous_header(header)?; + let prev_hash = *prev.hash(); + let prev_prev_hash = *prev.prev_hash(); + let gas_price = prev.next_gas_price(); + let prev_random_value = *prev.random_value(); + let prev_height = prev.height(); + + // Do not accept old forks + if prev_height < self.runtime_adapter.get_gc_stop_height(&head.last_block_hash) { + return Err(Error::InvalidBlockHeight(prev_height)); + } + + let (is_caught_up, state_sync_info) = + self.get_catchup_and_state_sync_infos(header, prev_hash, prev_prev_hash, me, block)?; + + self.check_if_challenged_block_on_chain(header)?; + + debug!(target: "chain", block_hash = ?header.hash(), me=?me, is_caught_up=is_caught_up, "Process block"); + + // Check the header is valid before we proceed with the full block. + self.validate_header(header, provenance, challenges)?; + + self.epoch_manager.verify_block_vrf( + header.epoch_id(), + header.height(), + &prev_random_value, + block.vrf_value(), + block.vrf_proof(), + )?; + + if header.random_value() != &hash(block.vrf_value().0.as_ref()) { + return Err(Error::InvalidRandomnessBeaconOutput); + } + + if let Err(e) = self.validate_block(block) { + byzantine_assert!(false); + return Err(e); + } + + let protocol_version = self.epoch_manager.get_epoch_protocol_version(header.epoch_id())?; + if !block.verify_gas_price( + gas_price, + self.block_economics_config.min_gas_price(protocol_version), + self.block_economics_config.max_gas_price(protocol_version), + self.block_economics_config.gas_price_adjustment_rate(protocol_version), + ) { + byzantine_assert!(false); + return Err(Error::InvalidGasPrice); + } + let minted_amount = if self.epoch_manager.is_next_block_epoch_start(&prev_hash)? { + Some(self.epoch_manager.get_epoch_minted_amount(header.next_epoch_id())?) + } else { + None + }; + + if !block.verify_total_supply(prev.total_supply(), minted_amount) { + byzantine_assert!(false); + return Err(Error::InvalidGasPrice); + } + + let (challenges_result, challenged_blocks) = + self.verify_challenges(block.challenges(), header.epoch_id(), header.prev_hash())?; + + let prev_block = self.get_block(&prev_hash)?; + + self.validate_chunk_headers(&block, &prev_block)?; + + self.ping_missing_chunks(me, prev_hash, block)?; + let incoming_receipts = self.collect_incoming_receipts_from_block(me, block)?; + + // Check if block can be finalized and drop it otherwise. + self.check_if_finalizable(header)?; + + let apply_chunk_work = self.apply_chunks_preprocessing( + me, + block, + &prev_block, + &incoming_receipts, + // If we have the state for shards in the next epoch already downloaded, apply the state transition + // for these states as well + // otherwise put the block into the permanent storage, waiting for be caught up + if is_caught_up { ApplyChunksMode::IsCaughtUp } else { ApplyChunksMode::NotCaughtUp }, + state_patch, + invalid_chunks, + )?; + + Ok(( + apply_chunk_work, + BlockPreprocessInfo { + is_caught_up, + state_sync_info, + incoming_receipts, + challenges_result, + challenged_blocks, + provenance: provenance.clone(), + apply_chunks_done: Arc::new(OnceCell::new()), + block_start_processing_time: block_received_time, + }, + )) + } + + fn get_catchup_and_state_sync_infos( + &self, + header: &BlockHeader, + prev_hash: CryptoHash, + prev_prev_hash: CryptoHash, + me: &Option, + block: &MaybeValidated, + ) -> Result<(bool, Option), Error> { + if self.epoch_manager.is_next_block_epoch_start(&prev_hash)? { + debug!(target: "chain", block_hash=?header.hash(), "block is the first block of an epoch"); + if !self.prev_block_is_caught_up(&prev_prev_hash, &prev_hash)? { + // The previous block is not caught up for the next epoch relative to the previous + // block, which is the current epoch for this block, so this block cannot be applied + // at all yet, needs to be orphaned + return Err(Error::Orphan); + } + + // For the first block of the epoch we check if we need to start download states for + // shards that we will care about in the next epoch. If there is no state to be downloaded, + // we consider that we are caught up, otherwise not + let state_sync_info = self.get_state_sync_info(me, block)?; + Ok((state_sync_info.is_none(), state_sync_info)) + } else { + Ok((self.prev_block_is_caught_up(&prev_prev_hash, &prev_hash)?, None)) + } + } + + pub fn prev_block_is_caught_up( + &self, + prev_prev_hash: &CryptoHash, + prev_hash: &CryptoHash, + ) -> Result { + // Needs to be used with care: for the first block of each epoch the semantic is slightly + // different, since the prev_block is in a different epoch. So for all the blocks but the + // first one in each epoch this method returns true if the block is ready to have state + // applied for the next epoch, while for the first block in a particular epoch this method + // returns true if the block is ready to have state applied for the current epoch (and + // otherwise should be orphaned) + Ok(!self.chain_store.get_blocks_to_catchup(prev_prev_hash)?.contains(prev_hash)) + } + + /// Return all shards that whose states need to be caught up + /// That has two cases: + /// 1) Shard layout will change in the next epoch. In this case, the method returns all shards + /// in the current epoch that will be split into a future shard that `me` will track. + /// 2) Shard layout will be the same. In this case, the method returns all shards that `me` will + /// track in the next epoch but not this epoch + fn get_shards_to_state_sync( + epoch_manager: &dyn EpochManagerAdapter, + shard_tracker: &ShardTracker, + me: &Option, + parent_hash: &CryptoHash, + ) -> Result, Error> { + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(parent_hash)?; + Ok((epoch_manager.shard_ids(&epoch_id)?) + .into_iter() + .filter(|shard_id| { + Self::should_catch_up_shard( + epoch_manager, + shard_tracker, + me, + parent_hash, + *shard_id, + ) + }) + .collect()) + } + + fn should_catch_up_shard( + epoch_manager: &dyn EpochManagerAdapter, + shard_tracker: &ShardTracker, + me: &Option, + parent_hash: &CryptoHash, + shard_id: ShardId, + ) -> bool { + let result = epoch_manager.will_shard_layout_change(parent_hash); + let will_shard_layout_change = match result { + Ok(will_shard_layout_change) => will_shard_layout_change, + Err(err) => { + // TODO(resharding) This is a problem, if this happens the node + // will not perform resharding and fall behind the network. + tracing::error!(target: "chain", ?err, "failed to check if shard layout will change"); + false + } + }; + // if shard layout will change the next epoch, we should catch up the shard regardless + // whether we already have the shard's state this epoch, because we need to generate + // new states for shards split from the current shard for the next epoch + let will_care_about_shard = + shard_tracker.will_care_about_shard(me.as_ref(), parent_hash, shard_id, true); + let does_care_about_shard = + shard_tracker.care_about_shard(me.as_ref(), parent_hash, shard_id, true); + + tracing::debug!(target: "chain", does_care_about_shard, will_care_about_shard, will_shard_layout_change, "should catch up shard"); + + will_care_about_shard && (will_shard_layout_change || !does_care_about_shard) + } + + /// Check if any block with missing chunk is ready to be processed and start processing these blocks + pub fn check_blocks_with_missing_chunks( + &mut self, + me: &Option, + block_processing_artifact: &mut BlockProcessingArtifact, + apply_chunks_done_callback: DoneApplyChunkCallback, + ) { + let blocks = self.blocks_with_missing_chunks.ready_blocks(); + if !blocks.is_empty() { + debug!(target:"chain", "Got {} blocks that were missing chunks but now are ready.", blocks.len()); + } + for block in blocks { + let block_hash = *block.block.header().hash(); + let height = block.block.header().height(); + let time = StaticClock::instant(); + let res = self.start_process_block_async( + me, + block.block, + block.provenance, + block_processing_artifact, + apply_chunks_done_callback.clone(), + ); + match res { + Ok(_) => { + debug!(target: "chain", %block_hash, height, "Accepted block with missing chunks"); + self.blocks_delay_tracker + .mark_block_completed_missing_chunks(&block_hash, time); + } + Err(_) => { + debug!(target: "chain", %block_hash, height, "Declined block with missing chunks is declined."); + } + } + } + } + + pub fn get_outgoing_receipts_for_shard( + &self, + prev_block_hash: CryptoHash, + shard_id: ShardId, + last_height_included: BlockHeight, + ) -> Result, Error> { + self.chain_store.get_outgoing_receipts_for_shard( + self.epoch_manager.as_ref(), + prev_block_hash, + shard_id, + last_height_included, + ) + } + + /// Computes ShardStateSyncResponseHeader. + pub fn compute_state_response_header( + &self, + shard_id: ShardId, + sync_hash: CryptoHash, + ) -> Result { + // Consistency rules: + // 1. Everything prefixed with `sync_` indicates new epoch, for which we are syncing. + // 1a. `sync_prev` means the last of the prev epoch. + // 2. Empty prefix means the height where chunk was applied last time in the prev epoch. + // Let's call it `current`. + // 2a. `prev_` means we're working with height before current. + // 3. In inner loops we use all prefixes with no relation to the context described above. + let sync_block = self + .get_block(&sync_hash) + .log_storage_error("block has already been checked for existence")?; + let sync_block_header = sync_block.header(); + let sync_block_epoch_id = sync_block_header.epoch_id(); + let shard_ids = self.epoch_manager.shard_ids(sync_block_epoch_id)?; + if !shard_ids.contains(&shard_id) { + return Err(shard_id_out_of_bounds(shard_id)); + } + + // The chunk was applied at height `chunk_header.height_included`. + // Getting the `current` state. + let sync_prev_block = self.get_block(sync_block_header.prev_hash())?; + if sync_block_epoch_id == sync_prev_block.header().epoch_id() { + return Err(sync_hash_not_first_hash(sync_hash)); + } + // Chunk header here is the same chunk header as at the `current` height. + let sync_prev_hash = sync_prev_block.hash(); + let chunk_header = &sync_prev_block.chunks()[shard_id as usize]; + let (chunk_headers_root, chunk_proofs) = merklize( + &sync_prev_block + .chunks() + .iter() + .map(|shard_chunk| { + ChunkHashHeight(shard_chunk.chunk_hash(), shard_chunk.height_included()) + }) + .collect::>(), + ); + assert_eq!(&chunk_headers_root, sync_prev_block.header().chunk_headers_root()); + + let chunk = self.get_chunk_clone_from_header(chunk_header)?; + let chunk_proof = chunk_proofs[shard_id as usize].clone(); + let block_header = + self.get_block_header_on_chain_by_height(&sync_hash, chunk_header.height_included())?; + + // Collecting the `prev` state. + let (prev_chunk_header, prev_chunk_proof, prev_chunk_height_included) = match self + .get_block(block_header.prev_hash()) + { + Ok(prev_block) => { + let prev_chunk_header = prev_block.chunks()[shard_id as usize].clone(); + let (prev_chunk_headers_root, prev_chunk_proofs) = merklize( + &prev_block + .chunks() + .iter() + .map(|shard_chunk| { + ChunkHashHeight(shard_chunk.chunk_hash(), shard_chunk.height_included()) + }) + .collect::>(), + ); + assert_eq!(&prev_chunk_headers_root, prev_block.header().chunk_headers_root()); + + let prev_chunk_proof = prev_chunk_proofs[shard_id as usize].clone(); + let prev_chunk_height_included = prev_chunk_header.height_included(); + + (Some(prev_chunk_header), Some(prev_chunk_proof), prev_chunk_height_included) + } + Err(e) => match e { + Error::DBNotFoundErr(_) => { + if block_header.prev_hash() == &CryptoHash::default() { + (None, None, 0) + } else { + return Err(e); + } + } + _ => return Err(e), + }, + }; + + // Getting all existing incoming_receipts from prev_chunk height to the + // new epoch. + let incoming_receipts_proofs = self.chain_store.get_incoming_receipts_for_shard( + self.epoch_manager.as_ref(), + shard_id, + sync_hash, + prev_chunk_height_included, + )?; + + // Collecting proofs for incoming receipts. + let mut root_proofs = vec![]; + for receipt_response in incoming_receipts_proofs.iter() { + let ReceiptProofResponse(block_hash, receipt_proofs) = receipt_response; + let block_header = self.get_block_header(block_hash)?.clone(); + let block = self.get_block(block_hash)?; + let (block_receipts_root, block_receipts_proofs) = merklize( + &block + .chunks() + .iter() + .map(|chunk| chunk.prev_outgoing_receipts_root()) + .collect::>(), + ); + + let mut root_proofs_cur = vec![]; + if receipt_proofs.len() != block_header.chunks_included() as usize { + // Happens if a node doesn't track all shards and can't provide + // all incoming receipts to a chunk. + return Err(Error::Other("Not tracking all shards".to_owned())); + } + for receipt_proof in receipt_proofs.iter() { + let ReceiptProof(receipts, shard_proof) = receipt_proof; + let ShardProof { from_shard_id, to_shard_id: _, proof } = shard_proof; + let receipts_hash = CryptoHash::hash_borsh(ReceiptList(shard_id, receipts)); + let from_shard_id = *from_shard_id as usize; + + let root_proof = block.chunks()[from_shard_id].prev_outgoing_receipts_root(); + root_proofs_cur + .push(RootProof(root_proof, block_receipts_proofs[from_shard_id].clone())); + + // Make sure we send something reasonable. + assert_eq!(block_header.prev_chunk_outgoing_receipts_root(), &block_receipts_root); + assert!(verify_path(root_proof, proof, &receipts_hash)); + assert!(verify_path( + block_receipts_root, + &block_receipts_proofs[from_shard_id], + &root_proof, + )); + } + root_proofs.push(root_proofs_cur); + } + + let state_root_node = self.runtime_adapter.get_state_root_node( + shard_id, + sync_prev_hash, + &chunk_header.prev_state_root(), + )?; + + let (chunk, prev_chunk_header) = match chunk { + ShardChunk::V1(chunk) => { + let prev_chunk_header = + prev_chunk_header.and_then(|prev_header| match prev_header { + ShardChunkHeader::V1(header) => Some(ShardChunkHeader::V1(header)), + ShardChunkHeader::V2(_) => None, + ShardChunkHeader::V3(_) => None, + }); + let chunk = ShardChunk::V1(chunk); + (chunk, prev_chunk_header) + } + chunk @ ShardChunk::V2(_) => (chunk, prev_chunk_header), + }; + + let shard_state_header = ShardStateSyncResponseHeaderV2 { + chunk, + chunk_proof, + prev_chunk_header, + prev_chunk_proof, + incoming_receipts_proofs, + root_proofs, + state_root_node, + }; + + Ok(ShardStateSyncResponseHeader::V2(shard_state_header)) + } + + /// Returns ShardStateSyncResponseHeader for the given epoch and shard. + /// If the header is already available in the DB, returns the cached version and doesn't recompute it. + /// If the header was computed then it also gets cached in the DB. + pub fn get_state_response_header( + &self, + shard_id: ShardId, + sync_hash: CryptoHash, + ) -> Result { + // Check cache + let key = borsh::to_vec(&StateHeaderKey(shard_id, sync_hash))?; + if let Ok(Some(header)) = self.chain_store.store().get_ser(DBCol::StateHeaders, &key) { + return Ok(header); + } + + let shard_state_header = self.compute_state_response_header(shard_id, sync_hash)?; + + // Saving the header data + let mut store_update = self.chain_store.store().store_update(); + store_update.set_ser(DBCol::StateHeaders, &key, &shard_state_header)?; + store_update.commit()?; + + Ok(shard_state_header) + } + + pub fn get_state_response_part( + &mut self, + shard_id: ShardId, + part_id: u64, + sync_hash: CryptoHash, + ) -> Result, Error> { + let _span = tracing::debug_span!( + target: "sync", + "get_state_response_part", + shard_id, + part_id, + %sync_hash) + .entered(); + // Check cache + let key = borsh::to_vec(&StatePartKey(sync_hash, shard_id, part_id))?; + if let Ok(Some(state_part)) = self.chain_store.store().get(DBCol::StateParts, &key) { + return Ok(state_part.into()); + } + + let block = self + .get_block(&sync_hash) + .log_storage_error("block has already been checked for existence")?; + let header = block.header(); + let epoch_id = block.header().epoch_id(); + let shard_ids = self.epoch_manager.shard_ids(epoch_id)?; + if !shard_ids.contains(&shard_id) { + return Err(shard_id_out_of_bounds(shard_id)); + } + let prev_block = self.get_block(header.prev_hash())?; + if epoch_id == prev_block.header().epoch_id() { + return Err(sync_hash_not_first_hash(sync_hash)); + } + let state_root = prev_block.chunks()[shard_id as usize].prev_state_root(); + let prev_hash = *prev_block.hash(); + let prev_prev_hash = *prev_block.header().prev_hash(); + let state_root_node = self + .runtime_adapter + .get_state_root_node(shard_id, &prev_hash, &state_root) + .log_storage_error("get_state_root_node fail")?; + let num_parts = get_num_state_parts(state_root_node.memory_usage); + if part_id >= num_parts { + return Err(shard_id_out_of_bounds(shard_id)); + } + let current_time = Instant::now(); + let state_part = self + .runtime_adapter + .obtain_state_part( + shard_id, + &prev_prev_hash, + &state_root, + PartId::new(part_id, num_parts), + ) + .log_storage_error("obtain_state_part fail")?; + + let elapsed_ms = current_time.elapsed().as_millis(); + self.requested_state_parts + .save_state_part_elapsed(&sync_hash, &shard_id, &part_id, elapsed_ms); + + // Before saving State Part data, we need to make sure we can calculate and save State Header + self.get_state_response_header(shard_id, sync_hash)?; + + // Saving the part data + let mut store_update = self.chain_store.store().store_update(); + store_update.set(DBCol::StateParts, &key, &state_part); + store_update.commit()?; + + Ok(state_part) + } + + pub fn set_state_header( + &mut self, + shard_id: ShardId, + sync_hash: CryptoHash, + shard_state_header: ShardStateSyncResponseHeader, + ) -> Result<(), Error> { + let sync_block_header = self.get_block_header(&sync_hash)?; + + let chunk = shard_state_header.cloned_chunk(); + let prev_chunk_header = shard_state_header.cloned_prev_chunk_header(); + + // 1-2. Checking chunk validity + if !validate_chunk_proofs(&chunk, self.epoch_manager.as_ref())? { + byzantine_assert!(false); + return Err(Error::Other( + "set_shard_state failed: chunk header proofs are invalid".into(), + )); + } + + // Consider chunk itself is valid. + + // 3. Checking that chunks `chunk` and `prev_chunk` are included in appropriate blocks + // 3a. Checking that chunk `chunk` is included into block at last height before sync_hash + // 3aa. Also checking chunk.height_included + let sync_prev_block_header = self.get_block_header(sync_block_header.prev_hash())?; + if !verify_path( + *sync_prev_block_header.chunk_headers_root(), + shard_state_header.chunk_proof(), + &ChunkHashHeight(chunk.chunk_hash(), chunk.height_included()), + ) { + byzantine_assert!(false); + return Err(Error::Other( + "set_shard_state failed: chunk isn't included into block".into(), + )); + } + + let block_header = + self.get_block_header_on_chain_by_height(&sync_hash, chunk.height_included())?; + // 3b. Checking that chunk `prev_chunk` is included into block at height before chunk.height_included + // 3ba. Also checking prev_chunk.height_included - it's important for getting correct incoming receipts + match (&prev_chunk_header, shard_state_header.prev_chunk_proof()) { + (Some(prev_chunk_header), Some(prev_chunk_proof)) => { + let prev_block_header = + self.get_block_header(block_header.prev_hash())?; + if !verify_path( + *prev_block_header.chunk_headers_root(), + prev_chunk_proof, + &ChunkHashHeight(prev_chunk_header.chunk_hash(), prev_chunk_header.height_included()), + ) { + byzantine_assert!(false); + return Err(Error::Other( + "set_shard_state failed: prev_chunk isn't included into block".into(), + )); + } + } + (None, None) => { + if chunk.height_included() != 0 { + return Err(Error::Other( + "set_shard_state failed: received empty state response for a chunk that is not at height 0".into() + )); + } + } + _ => + return Err(Error::Other("set_shard_state failed: `prev_chunk_header` and `prev_chunk_proof` must either both be present or both absent".into())) + }; + + // 4. Proving incoming receipts validity + // 4a. Checking len of proofs + if shard_state_header.root_proofs().len() + != shard_state_header.incoming_receipts_proofs().len() + { + byzantine_assert!(false); + return Err(Error::Other("set_shard_state failed: invalid proofs".into())); + } + let mut hash_to_compare = sync_hash; + for (i, receipt_response) in + shard_state_header.incoming_receipts_proofs().iter().enumerate() + { + let ReceiptProofResponse(block_hash, receipt_proofs) = receipt_response; + + // 4b. Checking that there is a valid sequence of continuous blocks + if *block_hash != hash_to_compare { + byzantine_assert!(false); + return Err(Error::Other( + "set_shard_state failed: invalid incoming receipts".into(), + )); + } + let header = self.get_block_header(&hash_to_compare)?; + hash_to_compare = *header.prev_hash(); + + let block_header = self.get_block_header(block_hash)?; + // 4c. Checking len of receipt_proofs for current block + if receipt_proofs.len() != shard_state_header.root_proofs()[i].len() + || receipt_proofs.len() != block_header.chunks_included() as usize + { + byzantine_assert!(false); + return Err(Error::Other("set_shard_state failed: invalid proofs".into())); + } + // We know there were exactly `block_header.chunks_included` chunks included + // on the height of block `block_hash`. + // There were no other proofs except for included chunks. + // According to Pigeonhole principle, it's enough to ensure all receipt_proofs are distinct + // to prove that all receipts were received and no receipts were hidden. + let mut visited_shard_ids = HashSet::::new(); + for (j, receipt_proof) in receipt_proofs.iter().enumerate() { + let ReceiptProof(receipts, shard_proof) = receipt_proof; + let ShardProof { from_shard_id, to_shard_id: _, proof } = shard_proof; + // 4d. Checking uniqueness for set of `from_shard_id` + match visited_shard_ids.get(from_shard_id) { + Some(_) => { + byzantine_assert!(false); + return Err(Error::Other("set_shard_state failed: invalid proofs".into())); + } + _ => visited_shard_ids.insert(*from_shard_id), + }; + let RootProof(root, block_proof) = &shard_state_header.root_proofs()[i][j]; + let receipts_hash = CryptoHash::hash_borsh(ReceiptList(shard_id, receipts)); + // 4e. Proving the set of receipts is the subset of outgoing_receipts of shard `shard_id` + if !verify_path(*root, proof, &receipts_hash) { + byzantine_assert!(false); + return Err(Error::Other("set_shard_state failed: invalid proofs".into())); + } + // 4f. Proving the outgoing_receipts_root matches that in the block + if !verify_path( + *block_header.prev_chunk_outgoing_receipts_root(), + block_proof, + root, + ) { + byzantine_assert!(false); + return Err(Error::Other("set_shard_state failed: invalid proofs".into())); + } + } + } + // 4g. Checking that there are no more heights to get incoming_receipts + let header = self.get_block_header(&hash_to_compare)?; + if header.height() != prev_chunk_header.map_or(0, |h| h.height_included()) { + byzantine_assert!(false); + return Err(Error::Other("set_shard_state failed: invalid incoming receipts".into())); + } + + // 5. Checking that state_root_node is valid + let chunk_inner = chunk.take_header().take_inner(); + if !self.runtime_adapter.validate_state_root_node( + shard_state_header.state_root_node(), + chunk_inner.prev_state_root(), + ) { + byzantine_assert!(false); + return Err(Error::Other("set_shard_state failed: state_root_node is invalid".into())); + } + + // Saving the header data. + let mut store_update = self.chain_store.store().store_update(); + let key = borsh::to_vec(&StateHeaderKey(shard_id, sync_hash))?; + store_update.set_ser(DBCol::StateHeaders, &key, &shard_state_header)?; + store_update.commit()?; + + Ok(()) + } + + pub fn get_state_header( + &self, + shard_id: ShardId, + sync_hash: CryptoHash, + ) -> Result { + self.chain_store.get_state_header(shard_id, sync_hash) + } + + pub fn set_state_part( + &mut self, + shard_id: ShardId, + sync_hash: CryptoHash, + part_id: PartId, + data: &[u8], + ) -> Result<(), Error> { + let shard_state_header = self.get_state_header(shard_id, sync_hash)?; + let chunk = shard_state_header.take_chunk(); + let state_root = *chunk.take_header().take_inner().prev_state_root(); + if !self.runtime_adapter.validate_state_part(&state_root, part_id, data) { + byzantine_assert!(false); + return Err(Error::Other(format!( + "set_state_part failed: validate_state_part failed. state_root={:?}", + state_root + ))); + } + + // Saving the part data. + let mut store_update = self.chain_store.store().store_update(); + let key = borsh::to_vec(&StatePartKey(sync_hash, shard_id, part_id.idx))?; + store_update.set(DBCol::StateParts, &key, data); + store_update.commit()?; + Ok(()) + } + + pub fn schedule_apply_state_parts( + &self, + shard_id: ShardId, + sync_hash: CryptoHash, + num_parts: u64, + state_parts_task_scheduler: &dyn Fn(ApplyStatePartsRequest), + ) -> Result<(), Error> { + let epoch_id = self.get_block_header(&sync_hash)?.epoch_id().clone(); + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, &epoch_id)?; + + let shard_state_header = self.get_state_header(shard_id, sync_hash)?; + let state_root = shard_state_header.chunk_prev_state_root(); + + state_parts_task_scheduler(ApplyStatePartsRequest { + runtime_adapter: self.runtime_adapter.clone(), + shard_uid, + state_root, + num_parts, + epoch_id, + sync_hash, + }); + + Ok(()) + } + + pub fn set_state_finalize( + &mut self, + shard_id: ShardId, + sync_hash: CryptoHash, + apply_result: Result<(), unc_chain_primitives::Error>, + ) -> Result<(), Error> { + let _span = tracing::debug_span!(target: "sync", "set_state_finalize").entered(); + apply_result?; + + let shard_state_header = self.get_state_header(shard_id, sync_hash)?; + let chunk = shard_state_header.cloned_chunk(); + + let block_hash = chunk.prev_block(); + + // We synced shard state on top of _previous_ block for chunk in shard state header and applied state parts to + // flat storage. Now we can set flat head to hash of this block and create flat storage. + // If block_hash is equal to default - this means that we're all the way back at genesis. + // So we don't have to add the storage state for shard in such case. + // TODO(8438) - add additional test scenarios for this case. + if *block_hash != CryptoHash::default() { + let block_header = self.get_block_header(block_hash)?; + let epoch_id = block_header.epoch_id(); + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, epoch_id)?; + + let flat_storage_manager = self.runtime_adapter.get_flat_storage_manager(); + // Flat storage must not exist at this point because leftover keys corrupt its state. + assert!(flat_storage_manager.get_flat_storage_for_shard(shard_uid).is_none()); + + let flat_head_hash = *chunk.prev_block(); + let flat_head_header = self.get_block_header(&flat_head_hash)?; + let flat_head_prev_hash = *flat_head_header.prev_hash(); + let flat_head_height = flat_head_header.height(); + + tracing::debug!(target: "store", ?shard_uid, ?flat_head_hash, flat_head_height, "set_state_finalize - initialized flat storage"); + + let mut store_update = self.runtime_adapter.store().store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { + flat_head: unc_store::flat::BlockInfo { + hash: flat_head_hash, + prev_hash: flat_head_prev_hash, + height: flat_head_height, + }, + }), + ); + store_update.commit()?; + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + } + + let mut height = shard_state_header.chunk_height_included(); + let mut chain_update = self.chain_update(); + chain_update.set_state_finalize(shard_id, sync_hash, shard_state_header)?; + chain_update.commit()?; + + // We restored the state on height `shard_state_header.chunk.header.height_included`. + // Now we should build a chain up to height of `sync_hash` block. + loop { + height += 1; + let mut chain_update = self.chain_update(); + // Result of successful execution of set_state_finalize_on_height is bool, + // should we commit and continue or stop. + if chain_update.set_state_finalize_on_height(height, shard_id, sync_hash)? { + chain_update.commit()?; + } else { + break; + } + } + + Ok(()) + } + + pub fn clear_downloaded_parts( + &mut self, + shard_id: ShardId, + sync_hash: CryptoHash, + num_parts: u64, + ) -> Result<(), Error> { + let mut chain_store_update = self.mut_chain_store().store_update(); + chain_store_update.gc_col_state_parts(sync_hash, shard_id, num_parts)?; + chain_store_update.commit() + } + + pub fn catchup_blocks_step( + &mut self, + me: &Option, + sync_hash: &CryptoHash, + blocks_catch_up_state: &mut BlocksCatchUpState, + block_catch_up_scheduler: &dyn Fn(BlockCatchUpRequest), + ) -> Result<(), Error> { + tracing::debug!( + target: "catchup", + pending_blocks = ?blocks_catch_up_state.pending_blocks, + processed_blocks = ?blocks_catch_up_state.processed_blocks.keys().collect::>(), + scheduled_blocks = ?blocks_catch_up_state.scheduled_blocks, + done_blocks = blocks_catch_up_state.done_blocks.len(), + "catch up blocks"); + let mut processed_blocks = HashMap::new(); + for (queued_block, results) in blocks_catch_up_state.processed_blocks.drain() { + // If this block is parent of some blocks in processing that need to be caught up, + // we can't mark this block as done yet because these blocks haven't been added to + // the store as blocks to be caught up yet. If we mark this block as done right now, + // these blocks will never get caught up. So we add these blocks back to the processed_blocks + // queue. + if self.blocks_in_processing.has_blocks_to_catch_up(&queued_block) { + processed_blocks.insert(queued_block, results); + } else { + match self.block_catch_up_postprocess(me, &queued_block, results) { + Ok(_) => { + let mut saw_one = false; + for next_block_hash in + self.chain_store.get_blocks_to_catchup(&queued_block)?.clone() + { + saw_one = true; + blocks_catch_up_state.pending_blocks.push(next_block_hash); + } + if saw_one { + assert_eq!( + self.epoch_manager.get_epoch_id_from_prev_block(&queued_block)?, + blocks_catch_up_state.epoch_id + ); + } + blocks_catch_up_state.done_blocks.push(queued_block); + } + Err(_) => { + error!("Error processing block during catch up, retrying"); + blocks_catch_up_state.pending_blocks.push(queued_block); + } + } + } + } + blocks_catch_up_state.processed_blocks = processed_blocks; + + for pending_block in blocks_catch_up_state.pending_blocks.drain(..) { + let block = self.chain_store.get_block(&pending_block)?.clone(); + let prev_block = self.chain_store.get_block(block.header().prev_hash())?.clone(); + + let receipts_by_shard = self.collect_incoming_receipts_from_block(me, &block)?; + let work = self.apply_chunks_preprocessing( + me, + &block, + &prev_block, + &receipts_by_shard, + ApplyChunksMode::CatchingUp, + Default::default(), + &mut Vec::new(), + )?; + metrics::SCHEDULED_CATCHUP_BLOCK.set(block.header().height() as i64); + blocks_catch_up_state.scheduled_blocks.insert(pending_block); + block_catch_up_scheduler(BlockCatchUpRequest { + sync_hash: *sync_hash, + block_hash: pending_block, + block_height: block.header().height(), + work, + }); + } + + Ok(()) + } + + /// Validates basic correctness of array of transactions included in chunk. + /// Doesn't require state. + fn validate_chunk_transactions( + &self, + block: &Block, + prev_block_header: &BlockHeader, + chunk: &ShardChunk, + ) -> Result<(), Error> { + if !validate_transactions_order(chunk.transactions()) { + let merkle_paths = Block::compute_chunk_headers_root(block.chunks().iter()).1; + let chunk_proof = ChunkProofs { + block_header: borsh::to_vec(&block.header()).expect("Failed to serialize"), + merkle_proof: merkle_paths[chunk.shard_id() as usize].clone(), + chunk: MaybeEncodedShardChunk::Decoded(chunk.clone()).into(), + }; + return Err(Error::InvalidChunkProofs(Box::new(chunk_proof))); + } + + let protocol_version = + self.epoch_manager.get_epoch_protocol_version(block.header().epoch_id())?; + if checked_feature!("stable", AccessKeyNonceRange, protocol_version) { + let transaction_validity_period = self.transaction_validity_period; + for transaction in chunk.transactions() { + self.chain_store() + .check_transaction_validity_period( + prev_block_header, + &transaction.transaction.block_hash, + transaction_validity_period, + ) + .map_err(|_| Error::from(Error::InvalidTransactions))?; + } + }; + + Ok(()) + } + + /// For given pair of block headers and shard id, return information about + /// block necessary for processing shard update. + fn get_apply_chunk_block_context( + &self, + block_header: &BlockHeader, + prev_block_header: &BlockHeader, + is_new_chunk: bool, + ) -> Result { + let epoch_id = block_header.epoch_id(); + let protocol_version = self.epoch_manager.get_epoch_protocol_version(epoch_id)?; + // Before `FixApplyChunks` feature, gas price was taken from current + // block by mistake. Preserve it for backwards compatibility. + let gas_price = if !is_new_chunk + && protocol_version < ProtocolFeature::FixApplyChunks.protocol_version() + { + block_header.next_gas_price() + } else { + prev_block_header.next_gas_price() + }; + + Ok(ApplyChunkBlockContext::from_header(block_header, gas_price)) + } + + fn block_catch_up_postprocess( + &mut self, + me: &Option, + block_hash: &CryptoHash, + results: Vec>, + ) -> Result<(), Error> { + let block = self.chain_store.get_block(block_hash)?; + let mut chain_update = self.chain_update(); + let results = results.into_iter().collect::, Error>>()?; + chain_update.apply_chunk_postprocessing(&block, results)?; + chain_update.commit()?; + + let epoch_id = block.header().epoch_id(); + for shard_id in self.epoch_manager.shard_ids(epoch_id)? { + // Update flat storage for each shard being caught up. We catch up a shard if it is tracked in the next + // epoch. If it is tracked in this epoch as well, it was updated during regular block processing. + if !self.shard_tracker.care_about_shard( + me.as_ref(), + block.header().prev_hash(), + shard_id, + true, + ) && self.shard_tracker.will_care_about_shard( + me.as_ref(), + block.header().prev_hash(), + shard_id, + true, + ) { + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, epoch_id)?; + let flat_storage_manager = self.runtime_adapter.get_flat_storage_manager(); + flat_storage_manager.update_flat_storage_for_shard(shard_uid, &block)?; + self.garbage_collect_memtrie_roots(&block, shard_uid); + } + } + + Ok(()) + } + + /// Apply transactions in chunks for the next epoch in blocks that were blocked on the state sync + pub fn finish_catchup_blocks( + &mut self, + me: &Option, + epoch_first_block: &CryptoHash, + block_processing_artifacts: &mut BlockProcessingArtifact, + apply_chunks_done_callback: DoneApplyChunkCallback, + affected_blocks: &[CryptoHash], + ) -> Result<(), Error> { + debug!( + "Finishing catching up blocks after syncing pre {:?}, me: {:?}", + epoch_first_block, me + ); + + let first_block = self.chain_store.get_block(epoch_first_block)?; + + let mut chain_store_update = ChainStoreUpdate::new(&mut self.chain_store); + + // `blocks_to_catchup` consists of pairs (`prev_hash`, `hash`). For the block that precedes + // `epoch_first_block` we should only remove the pair with hash = epoch_first_block, while + // for all the blocks in the queue we can remove all the pairs that have them as `prev_hash` + // since we processed all the blocks built on top of them above during the BFS + chain_store_update + .remove_block_to_catchup(*first_block.header().prev_hash(), *epoch_first_block); + + for block_hash in affected_blocks { + debug!(target: "chain", "Catching up: removing prev={:?} from the queue. I'm {:?}", block_hash, me); + chain_store_update.remove_prev_block_to_catchup(*block_hash); + } + chain_store_update.remove_state_sync_info(*epoch_first_block); + + chain_store_update.commit()?; + + for hash in affected_blocks.iter() { + self.check_orphans( + me, + *hash, + block_processing_artifacts, + apply_chunks_done_callback.clone(), + ); + } + + Ok(()) + } + + pub fn get_transaction_execution_result( + &self, + id: &CryptoHash, + ) -> Result, Error> { + Ok(self.chain_store.get_outcomes_by_id(id)?.into_iter().map(Into::into).collect()) + } + + fn get_recursive_transaction_results( + &self, + outcomes: &mut Vec, + id: &CryptoHash, + ) -> Result<(), Error> { + outcomes.push(ExecutionOutcomeWithIdView::from(self.get_execution_outcome(id)?)); + let outcome_idx = outcomes.len() - 1; + for idx in 0..outcomes[outcome_idx].outcome.receipt_ids.len() { + let id = outcomes[outcome_idx].outcome.receipt_ids[idx]; + self.get_recursive_transaction_results(outcomes, &id)?; + } + Ok(()) + } + + pub fn get_final_transaction_result( + &self, + transaction_hash: &CryptoHash, + ) -> Result { + let mut outcomes = Vec::new(); + self.get_recursive_transaction_results(&mut outcomes, transaction_hash)?; + let mut looking_for_id = *transaction_hash; + let num_outcomes = outcomes.len(); + let status = outcomes + .iter() + .find_map(|outcome_with_id| { + if outcome_with_id.id == looking_for_id { + match &outcome_with_id.outcome.status { + ExecutionStatusView::Unknown if num_outcomes == 1 => { + Some(FinalExecutionStatus::NotStarted) + } + ExecutionStatusView::Unknown => Some(FinalExecutionStatus::Started), + ExecutionStatusView::Failure(e) => { + Some(FinalExecutionStatus::Failure(e.clone())) + } + ExecutionStatusView::SuccessValue(v) => { + Some(FinalExecutionStatus::SuccessValue(v.clone())) + } + ExecutionStatusView::SuccessReceiptId(id) => { + looking_for_id = *id; + None + } + } + } else { + None + } + }) + .expect("results should resolve to a final outcome"); + let receipts_outcome = outcomes.split_off(1); + let transaction = self.chain_store.get_transaction(transaction_hash)?.ok_or_else(|| { + Error::DBNotFoundErr(format!("Transaction {} is not found", transaction_hash)) + })?; + let transaction: SignedTransactionView = SignedTransaction::clone(&transaction).into(); + let transaction_outcome = outcomes.pop().unwrap(); + Ok(FinalExecutionOutcomeView { status, transaction, transaction_outcome, receipts_outcome }) + } + + pub fn get_final_transaction_result_with_receipt( + &self, + final_outcome: FinalExecutionOutcomeView, + ) -> Result { + let receipt_id_from_transaction = + final_outcome.transaction_outcome.outcome.receipt_ids.get(0).cloned(); + let is_local_receipt = + final_outcome.transaction.signer_id == final_outcome.transaction.receiver_id; + + let receipts = final_outcome + .receipts_outcome + .iter() + .filter_map(|outcome| { + if Some(outcome.id) == receipt_id_from_transaction && is_local_receipt { + None + } else { + Some(self.chain_store.get_receipt(&outcome.id).and_then(|r| { + r.map(|r| Receipt::clone(&r).into()).ok_or_else(|| { + Error::DBNotFoundErr(format!("Receipt {} is not found", outcome.id)) + }) + })) + } + }) + .collect::, _>>()?; + + Ok(FinalExecutionOutcomeWithReceiptView { final_outcome, receipts }) + } + + pub fn check_blocks_final_and_canonical( + &self, + block_headers: &[BlockHeader], + ) -> Result<(), Error> { + let last_final_block_hash = *self.head_header()?.last_final_block(); + let last_final_height = self.get_block_header(&last_final_block_hash)?.height(); + for hdr in block_headers { + if hdr.height() > last_final_height || !self.is_on_current_chain(&hdr)? { + return Err(Error::Other(format!("{} not on current chain", hdr.hash()))); + } + } + Ok(()) + } + + pub fn create_chunk_state_challenge( + &self, + prev_block: &Block, + block: &Block, + chunk_header: &ShardChunkHeader, + ) -> Result { + let chunk_shard_id = chunk_header.shard_id(); + let prev_merkle_proofs = Block::compute_chunk_headers_root(prev_block.chunks().iter()).1; + let merkle_proofs = Block::compute_chunk_headers_root(block.chunks().iter()).1; + let prev_chunk = self + .get_chunk_clone_from_header(&prev_block.chunks()[chunk_shard_id as usize].clone()) + .unwrap(); + + // TODO (#6316): enable storage proof generation + // let prev_chunk_header = &prev_block.chunks()[chunk_shard_id as usize]; + // let receipt_proof_response: Vec = + // self.chain_store_update.get_incoming_receipts_for_shard( + // chunk_shard_id, + // *prev_block.hash(), + // prev_chunk_header.height_included(), + // )?; + // let receipts = collect_receipts_from_response(&receipt_proof_response); + // + // let challenges_result = self.verify_challenges( + // block.challenges(), + // block.header().epoch_id(), + // block.header().prev_hash(), + // Some(block.hash()), + // )?; + // let prev_chunk_inner = prev_chunk.cloned_header().take_inner(); + // let is_first_block_with_chunk_of_version = check_if_block_is_first_with_chunk_of_version( + // &mut self.chain_store_update, + // self.runtime_adapter.as_ref(), + // prev_block.hash(), + // chunk_shard_id, + // )?; + // let apply_result = self + // .runtime_adapter + // .apply_transactions_with_optional_storage_proof( + // chunk_shard_id, + // prev_chunk_inner.prev_state_root(), + // prev_chunk.height_included(), + // prev_block.header().raw_timestamp(), + // prev_chunk_inner.prev_block_hash(), + // prev_block.hash(), + // &receipts, + // prev_chunk.transactions(), + // prev_chunk_inner.validator_proposals(), + // prev_block.header().gas_price(), + // prev_chunk_inner.gas_limit(), + // &challenges_result, + // *block.header().random_value(), + // true, + // true, + // is_first_block_with_chunk_of_version, + // None, + // ) + // .unwrap(); + // let partial_state = apply_result.proof.unwrap().nodes; + Ok(ChunkState { + prev_block_header: borsh::to_vec(&prev_block.header())?, + block_header: borsh::to_vec(&block.header())?, + prev_merkle_proof: prev_merkle_proofs[chunk_shard_id as usize].clone(), + merkle_proof: merkle_proofs[chunk_shard_id as usize].clone(), + prev_chunk, + chunk_header: chunk_header.clone(), + partial_state: PartialState::TrieValues(vec![]), + }) + } + + fn get_resharding_state_roots( + &self, + block: &Block, + shard_id: ShardId, + ) -> Result, Error> { + let next_shard_layout = + self.epoch_manager.get_shard_layout(block.header().next_epoch_id())?; + let new_shards = + next_shard_layout.get_children_shards_uids(shard_id).unwrap_or_else(|| { + panic!( + "shard layout must contain maps of all shards to its children shards {} {:?}", + shard_id, next_shard_layout, + ); + }); + new_shards + .iter() + .map(|shard_uid| { + self.get_chunk_extra(block.header().prev_hash(), shard_uid) + .map(|chunk_extra| (*shard_uid, *chunk_extra.state_root())) + }) + .collect() + } + + /// Returns sequence of blocks in chain from `last_block_hash` (inclusive) + /// until the block with height `first_block_height` (inclusive if `include_with_height` + /// is true). For each block hash in resulting `Vec`, next entry contains hash of its + /// parent on chain. + /// TODO(logunov): consider uniting with `get_incoming_receipts_for_shard` because it + /// has the same purpose. + fn get_blocks_until_height( + &self, + mut last_block_hash: CryptoHash, + first_block_height: BlockHeight, + include_with_height: bool, + ) -> Result, Error> { + let mut blocks = vec![]; + loop { + let header = self.get_block_header(&last_block_hash)?; + if header.height() < first_block_height { + return Err(Error::InvalidBlockHeight(first_block_height)); + } + + if header.height() == first_block_height { + break; + } + + blocks.push(last_block_hash); + last_block_hash = *header.prev_hash(); + } + if include_with_height { + blocks.push(last_block_hash); + } + Ok(blocks) + } + + /// Creates jobs which will update shards for the given block and incoming + /// receipts aggregated for it. + fn apply_chunks_preprocessing( + &self, + me: &Option, + block: &Block, + prev_block: &Block, + incoming_receipts: &HashMap>, + mode: ApplyChunksMode, + mut state_patch: SandboxStatePatch, + invalid_chunks: &mut Vec, + ) -> Result, Error> { + let _span = tracing::debug_span!(target: "chain", "apply_chunks_preprocessing").entered(); + let prev_chunk_headers = + Chain::get_prev_chunk_headers(self.epoch_manager.as_ref(), prev_block)?; + + let mut maybe_jobs = vec![]; + for (shard_id, (chunk_header, prev_chunk_header)) in + block.chunks().iter().zip(prev_chunk_headers.iter()).enumerate() + { + // XXX: This is a bit questionable -- sandbox state patching works + // only for a single shard. This so far has been enough. + let state_patch = state_patch.take(); + + let stateful_job = self.get_update_shard_job( + me, + block, + prev_block, + chunk_header, + prev_chunk_header, + shard_id as ShardId, + mode, + incoming_receipts, + state_patch, + ); + maybe_jobs.push((shard_id, stateful_job)); + + let protocol_version = + self.epoch_manager.get_epoch_protocol_version(block.header().epoch_id())?; + if checked_feature!("stable", ChunkValidation, protocol_version) { + let stateless_job = self.get_stateless_validation_job( + me, + block, + prev_block, + chunk_header, + prev_chunk_header, + shard_id as ShardId, + mode, + ); + maybe_jobs.push((shard_id, stateless_job)); + } + } + + let mut jobs = vec![]; + for (shard_id, maybe_job) in maybe_jobs { + match maybe_job { + Ok(Some(processor)) => jobs.push(processor), + Ok(None) => {} + Err(err) => { + if err.is_bad_data() { + let chunk_header = block.chunks()[shard_id].clone(); + invalid_chunks.push(chunk_header); + } + return Err(err); + } + } + } + + Ok(jobs) + } + + fn get_shard_context( + &self, + me: &Option, + block_header: &BlockHeader, + shard_id: ShardId, + mode: ApplyChunksMode, + ) -> Result { + let prev_hash = block_header.prev_hash(); + let epoch_id = block_header.epoch_id(); + let cares_about_shard_this_epoch = + self.shard_tracker.care_about_shard(me.as_ref(), prev_hash, shard_id, true); + let cares_about_shard_next_epoch = + self.shard_tracker.will_care_about_shard(me.as_ref(), prev_hash, shard_id, true); + let will_shard_layout_change = self.epoch_manager.will_shard_layout_change(prev_hash)?; + let should_apply_chunk = get_should_apply_chunk( + mode, + cares_about_shard_this_epoch, + cares_about_shard_next_epoch, + ); + let need_to_reshard = will_shard_layout_change && cares_about_shard_next_epoch; + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, epoch_id)?; + Ok(ShardContext { + shard_uid, + cares_about_shard_this_epoch, + will_shard_layout_change, + should_apply_chunk, + need_to_reshard, + }) + } + + /// This method returns the closure that is responsible for updating a shard. + fn get_update_shard_job( + &self, + me: &Option, + block: &Block, + prev_block: &Block, + chunk_header: &ShardChunkHeader, + prev_chunk_header: &ShardChunkHeader, + shard_id: ShardId, + mode: ApplyChunksMode, + incoming_receipts: &HashMap>, + state_patch: SandboxStatePatch, + ) -> Result, Error> { + let _span = tracing::debug_span!(target: "chain", "get_update_shard_job").entered(); + let prev_hash = block.header().prev_hash(); + let shard_context = self.get_shard_context(me, block.header(), shard_id, mode)?; + + // We can only perform resharding when states are ready, i.e., mode != ApplyChunksMode::NotCaughtUp + // 1) if should_apply_chunk == true && resharding_state_roots.is_some(), + // that means children shards are ready. + // `apply_resharding_state_changes` will apply updates to the children shards + // 2) if should_apply_chunk == true && resharding_state_roots.is_none(), + // that means children shards are not ready yet. + // `apply_resharding_state_changes` will return `state_changes_for_resharding`, + // which will be stored to the database in `process_apply_chunks` + // 3) if should_apply_chunk == false && resharding_state_roots.is_some() + // This implies mode == CatchingUp and cares_about_shard_this_epoch == true, + // otherwise should_apply_chunk will be true + // That means transactions have already been applied last time when apply_chunks are + // called with mode NotCaughtUp, therefore `state_changes_for_resharding` have been + // stored in the database. Then we can safely read that and apply that to the split + // states + let resharding_state_roots = + if shard_context.need_to_reshard && mode != ApplyChunksMode::NotCaughtUp { + Some(self.get_resharding_state_roots(block, shard_id)?) + } else { + None + }; + + let is_new_chunk = chunk_header.height_included() == block.header().height(); + let shard_update_reason = if shard_context.should_apply_chunk { + let block_context = self.get_apply_chunk_block_context( + block.header(), + prev_block.header(), + is_new_chunk, + )?; + let storage_context = + StorageContext { storage_data_source: StorageDataSource::Db, state_patch }; + if is_new_chunk { + // Validate new chunk and collect incoming receipts for it. + + let prev_chunk_extra = self.get_chunk_extra(prev_hash, &shard_context.shard_uid)?; + let chunk = self.get_chunk_clone_from_header(&chunk_header)?; + let prev_chunk_height_included = prev_chunk_header.height_included(); + + // Validate that all next chunk information matches previous chunk extra. + validate_chunk_with_chunk_extra( + // It's safe here to use ChainStore instead of ChainStoreUpdate + // because we're asking prev_chunk_header for already committed block + self.chain_store(), + self.epoch_manager.as_ref(), + prev_hash, + prev_chunk_extra.as_ref(), + prev_chunk_height_included, + &chunk_header, + ) + .map_err(|err| { + warn!( + target: "chain", + ?err, + prev_block_hash=?prev_hash, + block_hash=?block.header().hash(), + shard_id, + prev_chunk_height_included, + ?prev_chunk_extra, + ?chunk_header, + "Failed to validate chunk extra" + ); + byzantine_assert!(false); + match self.create_chunk_state_challenge(prev_block, block, &chunk_header) { + Ok(chunk_state) => Error::InvalidChunkState(Box::new(chunk_state)), + Err(err) => err, + } + })?; + + self.validate_chunk_transactions(&block, prev_block.header(), &chunk)?; + + // we can't use hash from the current block here yet because the incoming receipts + // for this block is not stored yet + let new_receipts = collect_receipts(incoming_receipts.get(&shard_id).unwrap()); + let old_receipts = &self.chain_store().get_incoming_receipts_for_shard( + self.epoch_manager.as_ref(), + shard_id, + *prev_hash, + prev_chunk_height_included, + )?; + let old_receipts = collect_receipts_from_response(old_receipts); + let receipts = [new_receipts, old_receipts].concat(); + + // This variable is responsible for checking to which block we can apply receipts previously lost in apply_chunks + // (see https://github.com/utnet-org/utility/pull/4248/) + // We take the first block with existing chunk in the first epoch in which protocol feature + // RestoreReceiptsAfterFixApplyChunks was enabled, and put the restored receipts there. + let is_first_block_with_chunk_of_version = + check_if_block_is_first_with_chunk_of_version( + self.chain_store(), + self.epoch_manager.as_ref(), + block.header().prev_hash(), + shard_id, + )?; + + ShardUpdateReason::NewChunk(NewChunkData { + block: block_context, + is_first_block_with_chunk_of_version, + chunk, + receipts, + resharding_state_roots, + storage_context, + }) + } else { + ShardUpdateReason::OldChunk(OldChunkData { + block: block_context, + prev_chunk_extra: ChunkExtra::clone( + self.get_chunk_extra(prev_hash, &shard_context.shard_uid)?.as_ref(), + ), + resharding_state_roots, + storage_context, + }) + } + } else if let Some(resharding_state_roots) = resharding_state_roots { + assert!( + mode == ApplyChunksMode::CatchingUp && shard_context.cares_about_shard_this_epoch + ); + let state_changes = + self.chain_store().get_state_changes_for_resharding(block.hash(), shard_id)?; + ShardUpdateReason::Resharding(ReshardingData { + block_hash: *block.hash(), + block_height: block.header().height(), + state_changes, + resharding_state_roots, + }) + } else { + return Ok(None); + }; + + let runtime = self.runtime_adapter.clone(); + let epoch_manager = self.epoch_manager.clone(); + Ok(Some(( + shard_id, + Box::new(move |parent_span| -> Result { + Ok(ShardUpdateResult::Stateful(process_shard_update( + parent_span, + runtime.as_ref(), + epoch_manager.as_ref(), + shard_update_reason, + shard_context, + )?)) + }), + ))) + } + + /// Gets range of block and shard contexts for blocks between two + /// consecutive chunks for given `shard_id`. + /// One chunk is represented by `chunk_prev_hash`. + /// Its previous chunk is represented by `prev_chunk_height_included`. + /// Needed to process shard updates between these chunks. + /// If there are no missing chunks between them, resulting vector will be + /// empty. + /// Optionally returns `ChunkExtra` corresponding to block with previous + /// chunk. + fn get_blocks_range_with_missing_chunks( + &self, + me: &Option, + chunk_prev_hash: CryptoHash, + prev_chunk_height_included: BlockHeight, + shard_id: ShardId, + mode: ApplyChunksMode, + return_chunk_extra: bool, + ) -> Result<(Vec<(ApplyChunkBlockContext, ShardContext)>, Option), Error> { + let blocks_to_execute = + self.get_blocks_until_height(chunk_prev_hash, prev_chunk_height_included, false)?; + let mut execution_contexts: Vec<(ApplyChunkBlockContext, ShardContext)> = vec![]; + for block_hash in blocks_to_execute { + let block_header = self.get_block_header(&block_hash)?; + let prev_block_header = self.get_previous_header(&block_header)?; + let shard_context = self.get_shard_context(me, &block_header, shard_id, mode)?; + if shard_context.need_to_reshard { + return Err(Error::Other(String::from( + "Resharding occurred in blocks range, it is not supported yet", + ))); + } + execution_contexts.push(( + self.get_apply_chunk_block_context(&block_header, &prev_block_header, false)?, + shard_context, + )); + } + execution_contexts.reverse(); + + let chunk_extra = if return_chunk_extra { + let chunk_extra_block_hash = match execution_contexts.first() { + // If blocks range is non-trivial, block hash for chunk extra will + // be the **previous hash** for the first block in range of missing + // chunk. + Some((block_context, _)) => &block_context.prev_block_hash, + // Otherwise it is previous block for the chunk. + None => &chunk_prev_hash, + }; + let shard_uid = self.epoch_manager.shard_id_to_uid( + shard_id, + &self.epoch_manager.get_epoch_id(chunk_extra_block_hash)?, + )?; + match self.get_chunk_extra(chunk_extra_block_hash, &shard_uid) { + Ok(chunk_extra) => Some(ChunkExtra::clone(chunk_extra.as_ref())), + Err(e) => { + debug!(target: "client", ?chunk_extra_block_hash, ?shard_uid, "Chunk extra is missing: {e}"); + None + } + } + } else { + None + }; + + Ok((execution_contexts, chunk_extra)) + } + + /// Returns closure which should validate a chunk without state, if chunk is present. + /// TODO(logunov): + /// 1. Currently result of this job is not applied and used only to validate with + /// stateful chunk execution. Later current execution must be deprecated in favour of + /// this one. + /// 2. Use state witness and make this method truly stateless. + /// 3. Use range of blocks between chunk and previous existing chunk to collect + /// incoming receipts. + fn get_stateless_validation_job( + &self, + me: &Option, + block: &Block, + prev_block: &Block, + chunk_header: &ShardChunkHeader, + prev_chunk_header: &ShardChunkHeader, + shard_id: ShardId, + mode: ApplyChunksMode, + ) -> Result, Error> { + let _span = tracing::debug_span!(target: "chain", "get_stateless_validation_job").entered(); + let last_shard_context = self.get_shard_context(me, block.header(), shard_id, mode)?; + let is_new_chunk = chunk_header.height_included() == block.header().height(); + + // If we don't track a shard or there is no chunk, there is nothing to validate. + if !last_shard_context.should_apply_chunk || !is_new_chunk { + return Ok(None); + } + + // Validate transactions in chunk. + let chunk = self.get_chunk_clone_from_header(&chunk_header)?; + self.validate_chunk_transactions(&block, prev_block.header(), &chunk)?; + + let runtime = self.runtime_adapter.clone(); + let epoch_manager = self.epoch_manager.clone(); + let shard_id = prev_chunk_header.shard_id(); + let prev_chunk_height_included = prev_chunk_header.height_included(); + let prev_chunk_prev_hash = *prev_chunk_header.prev_block_hash(); + + // If previous chunk is genesis chunk, its execution is trivial. + // TODO(logunov): check that state witness is empty. + if prev_chunk_prev_hash == CryptoHash::default() { + return Ok(None); + } + + // Find the block at which height previous chunk was created. + // We will apply previous chunk to validate state witness in current chunk. + let mut last_blocks = + self.get_blocks_until_height(*prev_block.hash(), prev_chunk_height_included, true)?; + let prev_chunk_block_hash = last_blocks.pop().unwrap(); + let prev_chunk_block_header = self.get_block_header(&prev_chunk_block_hash)?; + assert_eq!(prev_chunk_block_header.prev_hash(), &prev_chunk_prev_hash); + + // Check that current and previous chunks have the same shard layouts because + // resharding is not supported yet - we need to determine parent shard id otherwise. + let prev_chunk_prev_block = match self.get_block(&prev_chunk_prev_hash) { + Ok(b) => b, + Err(e) => { + let block_height = block.header().height(); + let block_hash = block.hash(); + // TODO(logunov): this is probably happening just after state sync; needs to be + // fixed before stateless validation release. + debug!(target: "client", block_height, ?block_hash, shard_id, ?prev_chunk_prev_hash, "Previous block for previous chunk is missing: {e}"); + return Ok(None); + } + }; + let shard_layout = + epoch_manager.get_shard_layout_from_prev_block(prev_block.header().prev_hash())?; + let prev_shard_layout = epoch_manager + .get_shard_layout_from_prev_block(prev_chunk_prev_block.header().prev_hash())?; + if shard_layout != prev_shard_layout { + return Ok(None); + } + + // Do the same check for previous and previous-previous chunk, because we + // iterate over blocks between them to check shard update, and we will use + // the same shard id again for simplicity. + // TODO(logunov): does `prev_prev_chunk` always exist in DB? + let prev_prev_chunk = &prev_chunk_prev_block.chunks()[shard_id as usize]; + let prev_prev_chunk_prev_hash = prev_prev_chunk.prev_block_hash(); + let prev_prev_chunk_height_included = prev_prev_chunk.height_included(); + let prev_prev_shard_layout = + epoch_manager.get_shard_layout_from_prev_block(prev_prev_chunk_prev_hash)?; + if prev_shard_layout != prev_prev_shard_layout { + return Ok(None); + } + + // Collect receipts appeared on chain between previous and previous-previous chunk. + // Ideally we should collect receipts from current chunk (excluded) to previous chunk + // (included). But now we verify results against current stateful logic. + let receipts_response = &self.chain_store().get_incoming_receipts_for_shard( + self.epoch_manager.as_ref(), + shard_id, + prev_chunk_block_hash, + prev_prev_chunk_height_included, + )?; + let receipts = collect_receipts_from_response(receipts_response); + + // Get execution contexts for blocks with missing chunks between + // previous-previous chunk and previous chunk to execute. + // Also get starting chunk extra. + let (execution_contexts_before, maybe_chunk_extra) = self + .get_blocks_range_with_missing_chunks( + me, + prev_chunk_prev_hash, + prev_prev_chunk_height_included, + shard_id, + mode, + true, + )?; + let mut current_chunk_extra = match maybe_chunk_extra { + Some(c) => c, + None => { + debug!(target: "client", "Warning: chunk extra is missing"); + return Ok(None); + } + }; + + // Get execution context for previous chunk. + let (prev_chunk_block_context, prev_chunk_shard_context) = { + let block_header = self.get_block_header(&prev_chunk_block_hash)?; + let prev_block_header = self.get_previous_header(&block_header)?; + let shard_context = self.get_shard_context(me, &block_header, shard_id, mode)?; + if shard_context.need_to_reshard { + return Ok(None); + } + ( + self.get_apply_chunk_block_context(&block_header, &prev_block_header, true)?, + shard_context, + ) + }; + let prev_chunk = self.get_chunk_clone_from_header(&prev_chunk_header.clone())?; + + // Get execution contexts for blocks with missing chunks between + // previous chunk and the current chunk. + let (execution_contexts_after, _) = self.get_blocks_range_with_missing_chunks( + me, + *prev_block.hash(), + prev_chunk_height_included, + shard_id, + mode, + false, + )?; + + // Create stateless validation job. + // Its initial state corresponds to the block at which `prev_prev_chunk` + // was created, and then it: + // 1. processes updates for missing chunks until a block at which + // `prev_chunk` was created; + // 2. processes update for the `prev_chunk`; + // 3. processes updates for missing chunks until the last chunk + // is reached. + Ok(Some(( + shard_id, + Box::new(move |parent_span| -> Result { + // Process missing chunks before previous chunk. + let mut result = process_missing_chunks_range( + parent_span, + current_chunk_extra.clone(), + runtime.as_ref(), + epoch_manager.as_ref(), + execution_contexts_before, + )?; + current_chunk_extra = match result.last() { + Some((_, _, chunk_extra)) => chunk_extra.clone(), + None => current_chunk_extra, + }; + // TODO(logunov): use `validate_chunk_with_chunk_extra` + assert_eq!(current_chunk_extra.state_root(), &prev_chunk.prev_state_root()); + // Process previous chunk. + let NewChunkResult { gas_limit, shard_uid, apply_result, resharding_results: _ } = + apply_new_chunk( + parent_span, + NewChunkData { + chunk: prev_chunk, + receipts, + resharding_state_roots: None, + block: prev_chunk_block_context.clone(), + is_first_block_with_chunk_of_version: false, + storage_context: StorageContext { + storage_data_source: StorageDataSource::DbTrieOnly, + state_patch: Default::default(), + }, + }, + prev_chunk_shard_context, + runtime.as_ref(), + epoch_manager.as_ref(), + )?; + let (outcome_root, _) = + ApplyChunkResult::compute_outcomes_proof(&apply_result.outcomes); + current_chunk_extra = ChunkExtra::new( + &apply_result.new_root, + outcome_root, + apply_result.validator_power_proposals, + apply_result.validator_frozen_proposals, + apply_result.total_gas_burnt, + gas_limit, + apply_result.total_balance_burnt, + ); + result.push(( + prev_chunk_block_context.block_hash, + shard_uid, + current_chunk_extra.clone(), + )); + // Process missing chunks after previous chunk. + let result_after = process_missing_chunks_range( + parent_span, + current_chunk_extra, + runtime.as_ref(), + epoch_manager.as_ref(), + execution_contexts_after, + )?; + result.extend(result_after.into_iter()); + Ok(ShardUpdateResult::Stateless(result)) + }), + ))) + } + + /// Function to create a new snapshot if needed + fn process_snapshot(&mut self) -> Result<(), Error> { + let (make_snapshot, delete_snapshot) = self.should_make_or_delete_snapshot()?; + if !make_snapshot && !delete_snapshot { + return Ok(()); + } + if let Some(snapshot_callbacks) = &self.snapshot_callbacks { + if make_snapshot { + let head = self.head()?; + let epoch_height = + self.epoch_manager.get_epoch_height_from_prev_block(&head.prev_block_hash)?; + let shard_uids = self + .epoch_manager + .get_shard_layout_from_prev_block(&head.prev_block_hash)? + .shard_uids() + .collect(); + let last_block = self.get_block(&head.last_block_hash)?; + let make_snapshot_callback = &snapshot_callbacks.make_snapshot_callback; + make_snapshot_callback(head.prev_block_hash, epoch_height, shard_uids, last_block); + } else if delete_snapshot { + let delete_snapshot_callback = &snapshot_callbacks.delete_snapshot_callback; + delete_snapshot_callback(); + } + } + Ok(()) + } + + /// Function to check whether we need to create a new snapshot while processing the current block + /// Note that this functions is called as a part of block preprocesing, so the head is not updated to current block + fn should_make_or_delete_snapshot(&mut self) -> Result<(bool, bool), Error> { + // head value is that of the previous block, i.e. curr_block.prev_hash + let head = self.head()?; + if head.prev_block_hash == CryptoHash::default() { + // genesis block, do not snapshot + return Ok((false, false)); + } + + let is_epoch_boundary = + self.epoch_manager.is_next_block_epoch_start(&head.last_block_hash)?; + let will_shard_layout_change = + self.epoch_manager.will_shard_layout_change(&head.last_block_hash)?; + let tries = self.runtime_adapter.get_tries(); + let snapshot_config = tries.state_snapshot_config(); + let make_snapshot = match snapshot_config.state_snapshot_type { + // For every epoch, we snapshot if the next block would be in a different epoch + StateSnapshotType::EveryEpoch => is_epoch_boundary, + // For resharding only, we snapshot if next block would be in a different shard layout + StateSnapshotType::ForReshardingOnly => is_epoch_boundary && will_shard_layout_change, + }; + + // We need to delete the existing snapshot at the epoch boundary if we are not making a new snapshot + // This is useful for the next epoch after resharding where make_snapshot is false but it's an epoch boundary + let delete_snapshot = !make_snapshot && is_epoch_boundary; + + Ok((make_snapshot, delete_snapshot)) + } + + /// Returns a description of state parts cached for the given shard of the given epoch. + pub fn get_cached_state_parts( + &self, + sync_hash: CryptoHash, + shard_id: ShardId, + num_parts: u64, + ) -> Result { + let _span = tracing::debug_span!(target: "chain", "get_cached_state_parts").entered(); + // DBCol::StateParts is keyed by StatePartKey: (BlockHash || ShardId || PartId (u64)). + let lower_bound = StatePartKey(sync_hash, shard_id, 0); + let lower_bound = borsh::to_vec(&lower_bound)?; + let upper_bound = StatePartKey(sync_hash, shard_id + 1, 0); + let upper_bound = borsh::to_vec(&upper_bound)?; + let mut num_cached_parts = 0; + let mut bit_array = BitArray::new(num_parts); + for item in self.chain_store.store().iter_range( + DBCol::StateParts, + Some(&lower_bound), + Some(&upper_bound), + ) { + let key = item?.0; + let key = StatePartKey::try_from_slice(&key)?; + let part_id = key.2; + num_cached_parts += 1; + bit_array.set_bit(part_id); + } + Ok(if num_cached_parts == 0 { + CachedParts::NoParts + } else if num_cached_parts == num_parts { + CachedParts::AllParts + } else { + CachedParts::BitArray(bit_array) + }) + } +} + +fn shard_id_out_of_bounds(shard_id: ShardId) -> Error { + Error::InvalidStateRequest(format!("shard_id {shard_id:?} out of bounds").into()) +} + +fn sync_hash_not_first_hash(sync_hash: CryptoHash) -> Error { + Error::InvalidStateRequest( + format!("sync_hash {sync_hash:?} is not the first hash of the epoch").into(), + ) +} + +/// We want to guarantee that transactions are only applied once for each shard, +/// even though apply_chunks may be called twice, once with +/// ApplyChunksMode::NotCaughtUp once with ApplyChunksMode::CatchingUp. Note +/// that it does not guard whether the children shards are ready or not, see the +/// comments before `need_to_reshard` +fn get_should_apply_chunk( + mode: ApplyChunksMode, + cares_about_shard_this_epoch: bool, + cares_about_shard_next_epoch: bool, +) -> bool { + match mode { + // next epoch's shard states are not ready, only update this epoch's shards + ApplyChunksMode::NotCaughtUp => cares_about_shard_this_epoch, + // update both this epoch and next epoch + ApplyChunksMode::IsCaughtUp => cares_about_shard_this_epoch || cares_about_shard_next_epoch, + // catching up next epoch's shard states, do not update this epoch's shard state + // since it has already been updated through ApplyChunksMode::NotCaughtUp + ApplyChunksMode::CatchingUp => { + !cares_about_shard_this_epoch && cares_about_shard_next_epoch + } + } +} + +/// Implement block merkle proof retrieval. +impl Chain { + fn combine_maybe_hashes( + hash1: Option, + hash2: Option, + ) -> Option { + match (hash1, hash2) { + (Some(h1), Some(h2)) => Some(combine_hash(&h1, &h2)), + (Some(h1), None) => Some(h1), + (None, Some(_)) => { + debug_assert!(false, "Inconsistent state in merkle proof computation: left node is None but right node exists"); + None + } + _ => None, + } + } + + fn chain_update(&mut self) -> ChainUpdate { + ChainUpdate::new( + &mut self.chain_store, + self.epoch_manager.clone(), + self.shard_tracker.clone(), + self.runtime_adapter.clone(), + self.doomslug_threshold_mode, + self.transaction_validity_period, + ) + } + + /// Get node at given position (index, level). If the node does not exist, return `None`. + fn get_merkle_tree_node( + &self, + index: u64, + level: u64, + counter: u64, + tree_size: u64, + tree_nodes: &mut HashMap<(u64, u64), Option>, + ) -> Result, Error> { + if let Some(hash) = tree_nodes.get(&(index, level)) { + Ok(*hash) + } else { + if level == 0 { + let maybe_hash = if index >= tree_size { + None + } else { + Some(self.chain_store().get_block_hash_from_ordinal(index)?) + }; + tree_nodes.insert((index, level), maybe_hash); + Ok(maybe_hash) + } else { + let cur_tree_size = (index + 1) * counter; + let maybe_hash = if cur_tree_size > tree_size { + if index * counter <= tree_size { + let left_hash = self.get_merkle_tree_node( + index * 2, + level - 1, + counter / 2, + tree_size, + tree_nodes, + )?; + let right_hash = self.reconstruct_merkle_tree_node( + index * 2 + 1, + level - 1, + counter / 2, + tree_size, + tree_nodes, + )?; + Self::combine_maybe_hashes(left_hash, right_hash) + } else { + None + } + } else { + Some( + *self + .chain_store() + .get_block_merkle_tree_from_ordinal(cur_tree_size)? + .get_path() + .last() + .ok_or_else(|| Error::Other("Merkle tree node missing".to_string()))?, + ) + }; + tree_nodes.insert((index, level), maybe_hash); + Ok(maybe_hash) + } + } + } + + /// Reconstruct node at given position (index, level). If the node does not exist, return `None`. + fn reconstruct_merkle_tree_node( + &self, + index: u64, + level: u64, + counter: u64, + tree_size: u64, + tree_nodes: &mut HashMap<(u64, u64), Option>, + ) -> Result, Error> { + if let Some(hash) = tree_nodes.get(&(index, level)) { + Ok(*hash) + } else { + if level == 0 { + let maybe_hash = if index >= tree_size { + None + } else { + Some(self.chain_store().get_block_hash_from_ordinal(index)?) + }; + tree_nodes.insert((index, level), maybe_hash); + Ok(maybe_hash) + } else { + let left_hash = self.get_merkle_tree_node( + index * 2, + level - 1, + counter / 2, + tree_size, + tree_nodes, + )?; + let right_hash = self.reconstruct_merkle_tree_node( + index * 2 + 1, + level - 1, + counter / 2, + tree_size, + tree_nodes, + )?; + let maybe_hash = Self::combine_maybe_hashes(left_hash, right_hash); + tree_nodes.insert((index, level), maybe_hash); + + Ok(maybe_hash) + } + } + } + + /// Get merkle proof for block with hash `block_hash` in the merkle tree of `head_block_hash`. + pub fn get_block_proof( + &self, + block_hash: &CryptoHash, + head_block_hash: &CryptoHash, + ) -> Result { + let leaf_index = self.chain_store().get_block_merkle_tree(block_hash)?.size(); + let tree_size = self.chain_store().get_block_merkle_tree(head_block_hash)?.size(); + if leaf_index >= tree_size { + if block_hash == head_block_hash { + // special case if the block to prove is the same as head + return Ok(vec![]); + } + return Err(Error::Other(format!( + "block {} is ahead of head block {}", + block_hash, head_block_hash + ))); + } + let mut level = 0; + let mut counter = 1; + let mut cur_index = leaf_index; + let mut path = vec![]; + let mut tree_nodes = HashMap::new(); + let mut iter = tree_size; + while iter > 1 { + if cur_index % 2 == 0 { + cur_index += 1 + } else { + cur_index -= 1; + } + let direction = if cur_index % 2 == 0 { Direction::Left } else { Direction::Right }; + let maybe_hash = if cur_index % 2 == 1 { + // node not immediately available. Needs to be reconstructed + self.reconstruct_merkle_tree_node( + cur_index, + level, + counter, + tree_size, + &mut tree_nodes, + )? + } else { + self.get_merkle_tree_node(cur_index, level, counter, tree_size, &mut tree_nodes)? + }; + if let Some(hash) = maybe_hash { + path.push(MerklePathItem { hash, direction }); + } + cur_index /= 2; + iter = (iter + 1) / 2; + level += 1; + counter *= 2; + } + Ok(path) + } +} + +/// Various chain getters. +impl Chain { + /// Gets chain head. + #[inline] + pub fn head(&self) -> Result { + self.chain_store.head() + } + + /// Gets chain tail height + #[inline] + pub fn tail(&self) -> Result { + self.chain_store.tail() + } + + /// Gets chain header head. + #[inline] + pub fn header_head(&self) -> Result { + self.chain_store.header_head() + } + + /// Header of the block at the head of the block chain (not the same thing as header_head). + #[inline] + pub fn head_header(&self) -> Result { + self.chain_store.head_header() + } + + /// Get final head of the chain. + #[inline] + pub fn final_head(&self) -> Result { + self.chain_store.final_head() + } + + /// Gets a block by hash. + #[inline] + pub fn get_block(&self, hash: &CryptoHash) -> Result { + self.chain_store.get_block(hash) + } + + /// Gets the block at chain head + pub fn get_head_block(&self) -> Result { + let tip = self.head()?; + self.chain_store.get_block(&tip.last_block_hash) + } + + /// Gets a chunk from hash. + #[inline] + pub fn get_chunk(&self, chunk_hash: &ChunkHash) -> Result, Error> { + self.chain_store.get_chunk(chunk_hash) + } + + /// Gets a chunk from header. + #[inline] + pub fn get_chunk_clone_from_header( + &self, + header: &ShardChunkHeader, + ) -> Result { + self.chain_store.get_chunk_clone_from_header(header) + } + + /// Gets a block from the current chain by height. + #[inline] + pub fn get_block_by_height(&self, height: BlockHeight) -> Result { + let hash = self.chain_store.get_block_hash_by_height(height)?; + self.chain_store.get_block(&hash) + } + + /// Gets block hash from the current chain by height. + #[inline] + pub fn get_block_hash_by_height(&self, height: BlockHeight) -> Result { + self.chain_store.get_block_hash_by_height(height) + } + + /// Gets a block header by hash. + #[inline] + pub fn get_block_header(&self, hash: &CryptoHash) -> Result { + self.chain_store.get_block_header(hash) + } + + /// Returns block header from the canonical chain for given height if present. + #[inline] + pub fn get_block_header_by_height(&self, height: BlockHeight) -> Result { + self.chain_store.get_block_header_by_height(height) + } + + /// Returns block header from the current chain defined by `sync_hash` for given height if present. + #[inline] + pub fn get_block_header_on_chain_by_height( + &self, + sync_hash: &CryptoHash, + height: BlockHeight, + ) -> Result { + self.chain_store.get_block_header_on_chain_by_height(sync_hash, height) + } + + /// Get previous block header. + #[inline] + pub fn get_previous_header(&self, header: &BlockHeader) -> Result { + self.chain_store.get_previous_header(header).map_err(|e| match e { + Error::DBNotFoundErr(_) => Error::Orphan, + other => other, + }) + } + + /// Returns hash of the first available block after genesis. + pub fn get_earliest_block_hash(&self) -> Result, Error> { + self.chain_store.get_earliest_block_hash() + } + + /// Check if block exists. + #[inline] + pub fn block_exists(&self, hash: &CryptoHash) -> Result { + self.chain_store.block_exists(hash) + } + + /// Get block extra that was computer after applying previous block. + #[inline] + pub fn get_block_extra(&self, block_hash: &CryptoHash) -> Result, Error> { + self.chain_store.get_block_extra(block_hash) + } + + /// Get chunk extra that was computed after applying chunk with given hash. + #[inline] + pub fn get_chunk_extra( + &self, + block_hash: &CryptoHash, + shard_uid: &ShardUId, + ) -> Result, Error> { + self.chain_store.get_chunk_extra(block_hash, shard_uid) + } + + /// Get destination shard id for a given receipt id. + #[inline] + pub fn get_shard_id_for_receipt_id(&self, receipt_id: &CryptoHash) -> Result { + self.chain_store.get_shard_id_for_receipt_id(receipt_id) + } + + /// Get next block hash for which there is a new chunk for the shard. + /// If sharding changes before we can find a block with a new chunk for the shard, + /// find the first block that contains a new chunk for any of the shards that split from the + /// original shard + pub fn get_next_block_hash_with_new_chunk( + &self, + block_hash: &CryptoHash, + shard_id: ShardId, + ) -> Result, Error> { + let mut block_hash = *block_hash; + let mut epoch_id = self.get_block_header(&block_hash)?.epoch_id().clone(); + let mut shard_layout = self.epoch_manager.get_shard_layout(&epoch_id)?; + // this corrects all the shard where the original shard will split to if sharding changes + let mut shard_ids = vec![shard_id]; + + while let Ok(next_block_hash) = self.chain_store.get_next_block_hash(&block_hash) { + let next_epoch_id = self.get_block_header(&next_block_hash)?.epoch_id().clone(); + if next_epoch_id != epoch_id { + let next_shard_layout = self.epoch_manager.get_shard_layout(&next_epoch_id)?; + if next_shard_layout != shard_layout { + shard_ids = shard_ids + .into_iter() + .flat_map(|id| { + next_shard_layout.get_children_shards_ids(id).unwrap_or_else(|| { + panic!("invalid shard layout {:?} because it does not contain children shards for parent shard {}", next_shard_layout, id) + }) + }) + .collect(); + + shard_layout = next_shard_layout; + } + epoch_id = next_epoch_id; + } + block_hash = next_block_hash; + + let block = self.get_block(&block_hash)?; + let chunks = block.chunks(); + for &shard_id in shard_ids.iter() { + if chunks[shard_id as usize].height_included() == block.header().height() { + return Ok(Some((block_hash, shard_id))); + } + } + } + + Ok(None) + } + + /// Returns underlying ChainStore. + #[inline] + pub fn chain_store(&self) -> &ChainStore { + &self.chain_store + } + + /// Returns mutable ChainStore. + #[inline] + pub fn mut_chain_store(&mut self) -> &mut ChainStore { + &mut self.chain_store + } + + /// Returns genesis block. + #[inline] + pub fn genesis_block(&self) -> &Block { + &self.genesis + } + + /// Returns genesis block header. + #[inline] + pub fn genesis(&self) -> &BlockHeader { + self.genesis.header() + } + + /// Returns number of orphans currently in the orphan pool. + #[inline] + pub fn blocks_with_missing_chunks_len(&self) -> usize { + self.blocks_with_missing_chunks.len() + } + + #[inline] + pub fn blocks_in_processing_len(&self) -> usize { + self.blocks_in_processing.len() + } + + /// Check if hash is for a known chunk orphan. + #[inline] + pub fn is_chunk_orphan(&self, hash: &CryptoHash) -> bool { + self.blocks_with_missing_chunks.contains(hash) + } + + /// Check if hash is for a block that is being processed + #[inline] + pub fn is_in_processing(&self, hash: &CryptoHash) -> bool { + self.blocks_in_processing.contains(hash) + } + + #[inline] + pub fn is_height_processed(&self, height: BlockHeight) -> Result { + self.chain_store.is_height_processed(height) + } + + #[inline] + pub fn is_block_invalid(&self, hash: &CryptoHash) -> bool { + self.invalid_blocks.contains(hash) + } + + /// Check that sync_hash is the frst block of an epoch. + pub fn check_sync_hash_validity(&self, sync_hash: &CryptoHash) -> Result { + // It's important to check that Block exists because we will sync with it. + // Do not replace with `get_block_header()`. + let sync_block = self.get_block(sync_hash)?; + let prev_hash = *sync_block.header().prev_hash(); + let is_first_block_of_epoch = self.epoch_manager.is_next_block_epoch_start(&prev_hash); + tracing::debug!( + target: "chain", + ?sync_hash, + ?prev_hash, + sync_hash_epoch_id = ?sync_block.header().epoch_id(), + sync_hash_next_epoch_id = ?sync_block.header().next_epoch_id(), + ?is_first_block_of_epoch, + "check_sync_hash_validity"); + + // If sync_hash is not on the Epoch boundary, it's malicious behavior + Ok(is_first_block_of_epoch?) + } + + /// Get transaction result for given hash of transaction or receipt id on the canonical chain + pub fn get_execution_outcome( + &self, + id: &CryptoHash, + ) -> Result { + let outcomes = self.chain_store.get_outcomes_by_id(id)?; + outcomes + .into_iter() + .find(|outcome| match self.get_block_header(&outcome.block_hash) { + Ok(header) => self.is_on_current_chain(&header).unwrap_or(false), + Err(_) => false, + }) + .ok_or_else(|| Error::DBNotFoundErr(format!("EXECUTION OUTCOME: {}", id))) + } + + /// Retrieve the up to `max_headers_returned` headers on the main chain + /// `hashes`: a list of block "locators". `hashes` should be ordered from older blocks to + /// more recent blocks. This function will find the first block in `hashes` + /// that is on the main chain and returns the blocks after this block. If none of the + /// blocks in `hashes` are on the main chain, the function returns an empty vector. + pub fn retrieve_headers( + &self, + hashes: Vec, + max_headers_returned: u64, + max_height: Option, + ) -> Result, Error> { + let header = match self.find_common_header(&hashes) { + Some(header) => header, + None => return Ok(vec![]), + }; + + let mut headers = vec![]; + let header_head_height = self.header_head()?.height; + let max_height = max_height.unwrap_or(header_head_height); + // TODO: this may be inefficient if there are a lot of skipped blocks. + for h in header.height() + 1..=max_height { + if let Ok(header) = self.get_block_header_by_height(h) { + headers.push(header.clone()); + if headers.len() >= max_headers_returned as usize { + break; + } + } + } + Ok(headers) + } + + /// Returns a vector of chunk headers, each of which corresponds to the previous chunk of + /// a chunk in the block after `prev_block` + /// This function is important when the block after `prev_block` has different number of chunks + /// from `prev_block`. + /// In block production and processing, often we need to get the previous chunks of chunks + /// in the current block, this function provides a way to do so while handling sharding changes + /// correctly. + /// For example, if `prev_block` has two shards 0, 1 and the block after `prev_block` will have + /// 4 shards 0, 1, 2, 3, 0 and 1 split from shard 0 and 2 and 3 split from shard 1. + /// `get_prev_chunks(runtime_adapter, prev_block)` will return + /// `[prev_block.chunks()[0], prev_block.chunks()[0], prev_block.chunks()[1], prev_block.chunks()[1]]` + pub fn get_prev_chunk_headers( + epoch_manager: &dyn EpochManagerAdapter, + prev_block: &Block, + ) -> Result, Error> { + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(prev_block.hash())?; + let shard_ids = epoch_manager.shard_ids(&epoch_id)?; + let prev_shard_ids = epoch_manager.get_prev_shard_ids(prev_block.hash(), shard_ids)?; + let chunks = prev_block.chunks(); + Ok(prev_shard_ids + .into_iter() + .map(|shard_id| chunks.get(shard_id as usize).unwrap().clone()) + .collect()) + } + + pub fn get_prev_chunk_header( + epoch_manager: &dyn EpochManagerAdapter, + prev_block: &Block, + shard_id: ShardId, + ) -> Result { + let prev_shard_id = epoch_manager.get_prev_shard_ids(prev_block.hash(), vec![shard_id])?[0]; + Ok(prev_block.chunks().get(prev_shard_id as usize).unwrap().clone()) + } + + pub fn group_receipts_by_shard( + receipts: Vec, + shard_layout: &ShardLayout, + ) -> HashMap> { + let mut result = HashMap::new(); + for receipt in receipts { + let shard_id = account_id_to_shard_id(&receipt.receiver_id, shard_layout); + let entry = result.entry(shard_id).or_insert_with(Vec::new); + entry.push(receipt) + } + result + } + + pub fn build_receipts_hashes( + receipts: &[Receipt], + shard_layout: &ShardLayout, + ) -> Vec { + // Using a BTreeMap instead of HashMap to enable in order iteration + // below. + // + // Pre-populating because even if there are no receipts for a shard, we + // need an empty vector for it. + let mut result: BTreeMap<_, _> = + shard_layout.shard_ids().map(|shard_id| (shard_id, vec![])).collect(); + let mut cache = HashMap::new(); + for receipt in receipts { + let &mut shard_id = cache + .entry(&receipt.receiver_id) + .or_insert_with(|| account_id_to_shard_id(&receipt.receiver_id, shard_layout)); + // This unwrap should be safe as we pre-populated the map with all + // valid shard ids. + result.get_mut(&shard_id).unwrap().push(receipt); + } + result + .into_iter() + .map(|(shard_id, receipts)| { + let bytes = borsh::to_vec(&(shard_id, receipts)).unwrap(); + hash(&bytes) + }) + .collect() + } +} + +/// Sandbox node specific operations +impl Chain { + // NB: `SandboxStatePatch` can only be created in `#[cfg(feature = + // "sandbox")]`, so we don't need extra cfg-gating here. + pub fn patch_state(&mut self, patch: SandboxStatePatch) { + self.pending_state_patch.merge(patch); + } + + pub fn patch_state_in_progress(&self) -> bool { + !self.pending_state_patch.is_empty() + } +} + +/// Epoch sync specific functions. +#[cfg(feature = "new_epoch_sync")] +impl Chain { + /// TODO(posvyatokum): validate `epoch_sync_info` before `store_update` commit. + pub fn validate_and_record_epoch_sync_info( + &mut self, + epoch_sync_info: &EpochSyncInfo, + ) -> Result<(), EpochSyncInfoError> { + let store = self.chain_store().store().clone(); + let epoch_manager = self.epoch_manager.clone(); + let mut chain_store_update = self.chain_store.store_update(); + let mut store_update = store.store_update(); + + let epoch_id = epoch_sync_info.get_epoch_id()?; + // save EpochSyncInfo + + store_update.set_ser(DBCol::EpochSyncInfo, epoch_id.as_ref(), epoch_sync_info)?; + + // save EpochInfo's + + store_update.set_ser(DBCol::EpochInfo, epoch_id.as_ref(), &epoch_sync_info.epoch_info)?; + store_update.set_ser( + DBCol::EpochInfo, + epoch_sync_info.get_next_epoch_id()?.as_ref(), + &epoch_sync_info.next_epoch_info, + )?; + store_update.set_ser( + DBCol::EpochInfo, + epoch_sync_info.get_next_next_epoch_id()?.as_ref(), + &epoch_sync_info.next_next_epoch_info, + )?; + + // construct and save all new BlockMerkleTree's + + let mut cur_block_merkle_tree = (*chain_store_update + .get_block_merkle_tree(epoch_sync_info.get_epoch_first_header()?.prev_hash())?) + .clone(); + let mut prev_hash = epoch_sync_info.get_epoch_first_header()?.prev_hash(); + for hash in &epoch_sync_info.all_block_hashes { + cur_block_merkle_tree.insert(*prev_hash); + chain_store_update.save_block_merkle_tree(*hash, cur_block_merkle_tree.clone()); + prev_hash = hash; + } + + // save all block data in headers_to_save + + for hash in &epoch_sync_info.headers_to_save { + let header = epoch_sync_info.get_header(*hash, EpochSyncHashType::BlockToSave)?; + // check that block is not known already + if store.exists(DBCol::BlockHeader, hash.as_ref())? { + continue; + } + + store_update.insert_ser(DBCol::BlockHeader, header.hash().as_ref(), header)?; + store_update.set_ser( + DBCol::NextBlockHashes, + header.prev_hash().as_ref(), + header.hash(), + )?; + store_update.set_ser( + DBCol::BlockHeight, + &index_to_bytes(header.height()), + header.hash(), + )?; + store_update.set_ser( + DBCol::BlockOrdinal, + &index_to_bytes(header.block_ordinal()), + &header.hash(), + )?; + + store_update.insert_ser( + DBCol::BlockInfo, + hash.as_ref(), + &epoch_sync_info.get_block_info(hash)?, + )?; + } + + // save header head, final head, update epoch_manager aggregator + chain_store_update + .force_save_header_head(&Tip::from_header(epoch_sync_info.get_epoch_last_header()?))?; + chain_store_update.save_final_head(&Tip::from_header( + epoch_sync_info.get_epoch_last_finalised_header()?, + ))?; + epoch_manager + .force_update_aggregator(epoch_id, epoch_sync_info.get_epoch_last_finalised_hash()?); + + // TODO(posvyatokum): add EpochSyncInfo validation. + + chain_store_update.merge(store_update); + chain_store_update.commit()?; + Ok(()) + } +} + +pub fn do_apply_chunks( + block_hash: CryptoHash, + block_height: BlockHeight, + work: Vec, +) -> Vec<(ShardId, Result)> { + let parent_span = + tracing::debug_span!(target: "chain", "do_apply_chunks", block_height, %block_hash) + .entered(); + work.into_par_iter() + .map(|(shard_id, task)| { + // As chunks can be processed in parallel, make sure they are all tracked as children of + // a single span. + (shard_id, task(&parent_span)) + }) + .collect() +} + +pub fn collect_receipts<'a, T>(receipt_proofs: T) -> Vec +where + T: IntoIterator, +{ + receipt_proofs.into_iter().flat_map(|ReceiptProof(receipts, _)| receipts).cloned().collect() +} + +pub fn collect_receipts_from_response( + receipt_proof_response: &[ReceiptProofResponse], +) -> Vec { + collect_receipts( + receipt_proof_response.iter().flat_map(|ReceiptProofResponse(_, proofs)| proofs.iter()), + ) +} + +#[derive(actix::Message)] +#[rtype(result = "()")] +pub struct ApplyStatePartsRequest { + pub runtime_adapter: Arc, + pub shard_uid: ShardUId, + pub state_root: StateRoot, + pub num_parts: u64, + pub epoch_id: EpochId, + pub sync_hash: CryptoHash, +} + +// Skip `runtime_adapter`, because it's a complex object that has complex logic +// and many fields. +impl Debug for ApplyStatePartsRequest { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ApplyStatePartsRequest") + .field("runtime_adapter", &"") + .field("shard_uid", &self.shard_uid) + .field("state_root", &self.state_root) + .field("num_parts", &self.num_parts) + .field("epoch_id", &self.epoch_id) + .field("sync_hash", &self.sync_hash) + .finish() + } +} + +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub struct ApplyStatePartsResponse { + pub apply_result: Result<(), unc_chain_primitives::error::Error>, + pub shard_id: ShardId, + pub sync_hash: CryptoHash, +} + +#[derive(actix::Message)] +#[rtype(result = "()")] +pub struct BlockCatchUpRequest { + pub sync_hash: CryptoHash, + pub block_hash: CryptoHash, + pub block_height: BlockHeight, + pub work: Vec, +} + +// Skip `work`, because displaying functions is not possible. +impl Debug for BlockCatchUpRequest { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("BlockCatchUpRequest") + .field("sync_hash", &self.sync_hash) + .field("block_hash", &self.block_hash) + .field("block_height", &self.block_height) + .field("work", &format!("", self.work.len())) + .finish() + } +} + +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub struct BlockCatchUpResponse { + pub sync_hash: CryptoHash, + pub block_hash: CryptoHash, + pub results: Vec<(ShardId, Result)>, +} + +/// Helper to track blocks catch up +/// Lifetime of a block_hash is as follows: +/// 1. It is added to pending blocks, either as first block of an epoch or because we (post) +/// processed previous block +/// 2. Block is preprocessed and scheduled for processing in sync jobs actor. Block hash +/// and state changes from preprocessing goes to scheduled blocks +/// 3. We've got response from sync jobs actor that block was processed. Block hash, state +/// changes from preprocessing and result of processing block are moved to processed blocks +/// 4. Results are postprocessed. If there is any error block goes back to pending to try again. +/// Otherwise results are commited, block is moved to done blocks and any blocks that +/// have this block as previous are added to pending +pub struct BlocksCatchUpState { + /// Hash of first block of an epoch + pub first_block_hash: CryptoHash, + /// Epoch id + pub epoch_id: EpochId, + /// Collection of block hashes that are yet to be sent for processed + pub pending_blocks: Vec, + /// Map from block hashes that are scheduled for processing to saved store updates from their + /// preprocessing + pub scheduled_blocks: HashSet, + /// Map from block hashes that were processed to (saved store update, process results) + pub processed_blocks: HashMap>>, + /// Collection of block hashes that are fully processed + pub done_blocks: Vec, +} + +impl BlocksCatchUpState { + pub fn new(first_block_hash: CryptoHash, epoch_id: EpochId) -> Self { + Self { + first_block_hash, + epoch_id, + pending_blocks: vec![first_block_hash], + scheduled_blocks: HashSet::new(), + processed_blocks: HashMap::new(), + done_blocks: vec![], + } + } + + pub fn is_finished(&self) -> bool { + self.pending_blocks.is_empty() + && self.scheduled_blocks.is_empty() + && self.processed_blocks.is_empty() + } +} + +impl Chain { + // Get status for debug page + pub fn get_block_catchup_status( + &self, + block_catchup_state: &BlocksCatchUpState, + ) -> Vec { + block_catchup_state + .pending_blocks + .iter() + .chain(block_catchup_state.scheduled_blocks.iter()) + .chain(block_catchup_state.processed_blocks.keys()) + .map(|block_hash| BlockStatusView { + height: self + .get_block_header(block_hash) + .map(|header| header.height()) + .unwrap_or_default(), + hash: *block_hash, + }) + .collect() + } +} diff --git a/chain/chain/src/chain_update.rs b/chain/chain/src/chain_update.rs new file mode 100644 index 000000000..4a6c7b274 --- /dev/null +++ b/chain/chain/src/chain_update.rs @@ -0,0 +1,1072 @@ +use crate::block_processing_utils::BlockPreprocessInfo; +use crate::chain::collect_receipts_from_response; +use crate::metrics::{SHARD_LAYOUT_NUM_SHARDS, SHARD_LAYOUT_VERSION}; +use crate::store::{ChainStore, ChainStoreAccess, ChainStoreUpdate}; + +use crate::types::{ + ApplyChunkBlockContext, ApplyChunkResult, ApplyChunkShardContext, ReshardingResults, + RuntimeAdapter, RuntimeStorageConfig, +}; +use crate::update_shard::{ + NewChunkResult, OldChunkResult, ReshardingResult, ShardBlockUpdateResult, ShardUpdateResult, +}; +use crate::{metrics, DoomslugThresholdMode}; +use crate::{Chain, Doomslug}; +use unc_chain_primitives::error::Error; +use unc_epoch_manager::shard_tracker::ShardTracker; +use unc_epoch_manager::types::BlockHeaderInfo; +use unc_epoch_manager::EpochManagerAdapter; +use unc_primitives::block::{Block, Tip}; +use unc_primitives::block_header::BlockHeader; +#[cfg(feature = "new_epoch_sync")] +use unc_primitives::epoch_manager::{block_info::BlockInfo, epoch_sync::EpochSyncInfo}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::{account_id_to_shard_id, account_id_to_shard_uid, ShardUId}; +use unc_primitives::sharding::ShardChunk; +use unc_primitives::state_sync::{ReceiptProofResponse, ShardStateSyncResponseHeader}; +use unc_primitives::types::chunk_extra::ChunkExtra; +use unc_primitives::types::{ + AccountId, BlockExtra, BlockHeight, BlockHeightDelta, NumShards, ShardId, +}; +use unc_primitives::views::LightClientBlockView; +use std::collections::HashMap; +#[cfg(feature = "new_epoch_sync")] +use std::collections::HashSet; +use std::sync::Arc; +use tracing::{debug, info, warn}; + +/// Chain update helper, contains information that is needed to process block +/// and decide to accept it or reject it. +/// If rejected nothing will be updated in underlying storage. +/// Safe to stop process mid way (Ctrl+C or crash). +pub struct ChainUpdate<'a> { + epoch_manager: Arc, + shard_tracker: ShardTracker, + runtime_adapter: Arc, + chain_store_update: ChainStoreUpdate<'a>, + doomslug_threshold_mode: DoomslugThresholdMode, + #[allow(unused)] + transaction_validity_period: BlockHeightDelta, +} + +impl<'a> ChainUpdate<'a> { + pub fn new( + chain_store: &'a mut ChainStore, + epoch_manager: Arc, + shard_tracker: ShardTracker, + runtime_adapter: Arc, + doomslug_threshold_mode: DoomslugThresholdMode, + transaction_validity_period: BlockHeightDelta, + ) -> Self { + let chain_store_update: ChainStoreUpdate<'_> = chain_store.store_update(); + Self::new_impl( + epoch_manager, + shard_tracker, + runtime_adapter, + doomslug_threshold_mode, + transaction_validity_period, + chain_store_update, + ) + } + + fn new_impl( + epoch_manager: Arc, + shard_tracker: ShardTracker, + runtime_adapter: Arc, + doomslug_threshold_mode: DoomslugThresholdMode, + transaction_validity_period: BlockHeightDelta, + chain_store_update: ChainStoreUpdate<'a>, + ) -> Self { + ChainUpdate { + epoch_manager, + shard_tracker, + runtime_adapter, + chain_store_update, + doomslug_threshold_mode, + transaction_validity_period, + } + } + + /// Commit changes to the chain into the database. + pub fn commit(self) -> Result<(), Error> { + self.chain_store_update.commit() + } + + /// For all the outgoing receipts generated in block `hash` at the shards we + /// are tracking in this epoch, save a mapping from receipt ids to the + /// destination shard ids that the receipt will be sent to in the next + /// block. + /// + /// Note that this function should be called after `save_block` is called on + /// this block because it requires that the block info is available in + /// EpochManager, otherwise it will return an error. + fn save_receipt_id_to_shard_id_for_block( + &mut self, + account_id: Option<&AccountId>, + hash: &CryptoHash, + prev_hash: &CryptoHash, + shard_ids: &[ShardId], + ) -> Result<(), Error> { + let mut list = vec![]; + for &shard_id in shard_ids { + if self.shard_tracker.care_about_shard(account_id, prev_hash, shard_id, true) { + list.push(self.get_receipt_id_to_shard_id(hash, shard_id)?); + } + } + for map in list { + for (receipt_id, shard_id) in map { + self.chain_store_update.save_receipt_id_to_shard_id(receipt_id, shard_id); + } + } + Ok(()) + } + + /// Returns a mapping from the receipt id to the destination shard id. + fn get_receipt_id_to_shard_id( + &mut self, + hash: &CryptoHash, + shard_id: u64, + ) -> Result, Error> { + let outgoing_receipts = self.chain_store_update.get_outgoing_receipts(hash, shard_id); + let outgoing_receipts = if let Ok(outgoing_receipts) = outgoing_receipts { + outgoing_receipts + } else { + return Ok(HashMap::new()); + }; + let shard_layout = self.epoch_manager.get_shard_layout_from_prev_block(hash)?; + let outgoing_receipts = outgoing_receipts + .iter() + .map(|receipt| { + (receipt.receipt_id, account_id_to_shard_id(&receipt.receiver_id, &shard_layout)) + }) + .collect(); + Ok(outgoing_receipts) + } + + pub(crate) fn apply_chunk_postprocessing( + &mut self, + block: &Block, + apply_results: Vec, + ) -> Result<(), Error> { + let _span = tracing::debug_span!(target: "chain", "apply_chunk_postprocessing").entered(); + for result in apply_results { + match result { + ShardUpdateResult::Stateful(result) => { + self.process_apply_chunk_result(block, result)? + } + ShardUpdateResult::Stateless(results) => { + for (block_hash, shard_uid, chunk_extra) in results { + let expected_chunk_extra = + self.chain_store_update.get_chunk_extra(&block_hash, &shard_uid)?; + assert_eq!( + &chunk_extra, + expected_chunk_extra.as_ref(), + "For stateless validation, chunk extras for block {} and shard {} do not match", + block_hash, + shard_uid + ); + } + } + } + } + Ok(()) + } + + /// Postprocess resharding results and do the necessary update on chain for + /// resharding results. + /// - Store the chunk extras and trie changes for the apply results. + /// - Store the state changes to be applided later for the store results. + fn process_resharding_results( + &mut self, + block: &Block, + shard_uid: &ShardUId, + resharding_results: ReshardingResults, + ) -> Result<(), Error> { + let block_hash = block.hash(); + let prev_hash = block.header().prev_hash(); + let height = block.header().height(); + match resharding_results { + ReshardingResults::ApplyReshardingResults(mut results) => { + tracing::debug!(target: "resharding", height, ?shard_uid, "process_resharding_results apply"); + + // Sort the results so that the gas reassignment is deterministic. + results.sort_unstable_by_key(|r| r.shard_uid); + // Drop the mutability as we no longer need it. + let results = results; + + // Split validator_proposals, gas_burnt, balance_burnt to each child shard + // and store the chunk extra for children shards + // Note that here we do not split outcomes by the new shard layout, we simply store + // the outcome_root from the parent shard. This is because outcome proofs are + // generated per shard using the old shard layout and stored in the database. + // For these proofs to work, we must store the outcome root per shard + // using the old shard layout instead of the new shard layout + let chunk_extra = self.chain_store_update.get_chunk_extra(block_hash, shard_uid)?; + let next_epoch_shard_layout = { + let epoch_id = + self.epoch_manager.get_next_epoch_id_from_prev_block(prev_hash)?; + self.epoch_manager.get_shard_layout(&epoch_id)? + }; + + let mut validator_power_proposals_by_shard: HashMap<_, Vec<_>> = HashMap::new(); + for validator_power_proposal in chunk_extra.validator_power_proposals() { + let shard_uid = account_id_to_shard_uid( + validator_power_proposal.account_id(), + &next_epoch_shard_layout, + ); + validator_power_proposals_by_shard + .entry(shard_uid) + .or_default() + .push(validator_power_proposal); + } + + let mut validator_frozen_proposals_by_shard: HashMap<_, Vec<_>> = HashMap::new(); + for validator_frozen_proposal in chunk_extra.validator_frozen_proposals() { + let shard_uid = account_id_to_shard_uid( + validator_frozen_proposal.account_id(), + &next_epoch_shard_layout, + ); + validator_frozen_proposals_by_shard + .entry(shard_uid) + .or_default() + .push(validator_frozen_proposal); + } + + let num_split_shards = next_epoch_shard_layout + .get_children_shards_uids(shard_uid.shard_id()) + .unwrap_or_else(|| panic!("invalid shard layout {:?}", next_epoch_shard_layout)) + .len() as NumShards; + + let total_gas_used = chunk_extra.gas_used(); + let total_balance_burnt = chunk_extra.balance_burnt(); + + // The gas remainder, the children shards will be reassigned one + // unit each until its depleted. + let mut gas_res = total_gas_used % num_split_shards; + // The gas quotient, the children shards will be reassigned the + // full value each. + let gas_split = total_gas_used / num_split_shards; + + // The balance remainder, the children shards will be reassigned one + // unit each until its depleted. + let mut balance_res = (total_balance_burnt % num_split_shards as u128) as NumShards; + // The balance quotient, the children shards will be reassigned the + // full value each. + let balance_split = total_balance_burnt / (num_split_shards as u128); + + let gas_limit = chunk_extra.gas_limit(); + let outcome_root = *chunk_extra.outcome_root(); + + let mut sum_gas_used = 0; + let mut sum_balance_burnt = 0; + + // The gas and balance distribution assumes that we have a result for every split shard. + // TODO(resharding) make sure that is the case. + assert_eq!(num_split_shards, results.len() as u64); + + for result in results { + let gas_burnt = if gas_res > 0 { + gas_res -= 1; + gas_split + 1 + } else { + gas_split + }; + + let balance_burnt = if balance_res > 0 { + balance_res -= 1; + balance_split + 1 + } else { + balance_split + }; + + let new_chunk_extra = ChunkExtra::new( + &result.new_root, + outcome_root, + validator_power_proposals_by_shard.remove(&result.shard_uid).unwrap_or_default(), + validator_frozen_proposals_by_shard.remove(&result.shard_uid).unwrap_or_default(), + gas_burnt, + gas_limit, + balance_burnt, + ); + sum_gas_used += gas_burnt; + sum_balance_burnt += balance_burnt; + + // TODO(#9430): Support manager.save_flat_state_changes and manager.update_flat_storage_for_shard + // functions to be a part of the same chain_store_update + let flat_storage_manager = self.runtime_adapter.get_flat_storage_manager(); + let store_update = flat_storage_manager.save_flat_state_changes( + *block_hash, + *prev_hash, + block.header().height(), + result.shard_uid, + result.trie_changes.state_changes(), + )?; + flat_storage_manager.update_flat_storage_for_shard(*shard_uid, block)?; + self.chain_store_update.merge(store_update); + + self.chain_store_update.save_chunk_extra( + block_hash, + &result.shard_uid, + new_chunk_extra, + ); + self.chain_store_update.save_trie_changes(result.trie_changes); + } + assert_eq!(sum_gas_used, total_gas_used); + assert_eq!(sum_balance_burnt, total_balance_burnt); + } + ReshardingResults::StoreReshardingResults(state_changes) => { + tracing::debug!(target: "resharding", height, ?shard_uid, "process_resharding_results store"); + self.chain_store_update.add_state_changes_for_resharding( + *block_hash, + shard_uid.shard_id(), + state_changes, + ); + } + } + Ok(()) + } + + /// Process results of applying chunk + fn process_apply_chunk_result( + &mut self, + block: &Block, + result: ShardBlockUpdateResult, + ) -> Result<(), Error> { + let block_hash = block.hash(); + let prev_hash = block.header().prev_hash(); + let height = block.header().height(); + match result { + ShardBlockUpdateResult::NewChunk(NewChunkResult { + gas_limit, + shard_uid, + apply_result, + resharding_results, + }) => { + let (outcome_root, outcome_paths) = + ApplyChunkResult::compute_outcomes_proof(&apply_result.outcomes); + let shard_id = shard_uid.shard_id(); + + // Save state root after applying transactions. + self.chain_store_update.save_chunk_extra( + block_hash, + &shard_uid, + ChunkExtra::new( + &apply_result.new_root, + outcome_root, + apply_result.validator_power_proposals, + apply_result.validator_frozen_proposals, + apply_result.total_gas_burnt, + gas_limit, + apply_result.total_balance_burnt, + ), + ); + + let flat_storage_manager = self.runtime_adapter.get_flat_storage_manager(); + let store_update = flat_storage_manager.save_flat_state_changes( + *block_hash, + *prev_hash, + height, + shard_uid, + apply_result.trie_changes.state_changes(), + )?; + self.chain_store_update.merge(store_update); + + self.chain_store_update.save_trie_changes(apply_result.trie_changes); + self.chain_store_update.save_outgoing_receipt( + block_hash, + shard_id, + apply_result.outgoing_receipts, + ); + // Save receipt and transaction results. + self.chain_store_update.save_outcomes_with_proofs( + block_hash, + shard_id, + apply_result.outcomes, + outcome_paths, + ); + if let Some(resharding_results) = resharding_results { + self.process_resharding_results(block, &shard_uid, resharding_results)?; + } + } + ShardBlockUpdateResult::OldChunk(OldChunkResult { + shard_uid, + apply_result, + resharding_results, + }) => { + let old_extra = self.chain_store_update.get_chunk_extra(prev_hash, &shard_uid)?; + + let mut new_extra = ChunkExtra::clone(&old_extra); + *new_extra.state_root_mut() = apply_result.new_root; + + let flat_storage_manager = self.runtime_adapter.get_flat_storage_manager(); + let store_update = flat_storage_manager.save_flat_state_changes( + *block_hash, + *prev_hash, + height, + shard_uid, + apply_result.trie_changes.state_changes(), + )?; + self.chain_store_update.merge(store_update); + + self.chain_store_update.save_chunk_extra(block_hash, &shard_uid, new_extra); + self.chain_store_update.save_trie_changes(apply_result.trie_changes); + + if let Some(resharding_config) = resharding_results { + self.process_resharding_results(block, &shard_uid, resharding_config)?; + } + } + ShardBlockUpdateResult::Resharding(ReshardingResult { shard_uid, results }) => { + self.chain_store_update + .remove_state_changes_for_resharding(*block.hash(), shard_uid.shard_id()); + self.process_resharding_results( + block, + &shard_uid, + ReshardingResults::ApplyReshardingResults(results), + )?; + } + }; + Ok(()) + } + + /// This is the last step of process_block_single, where we take the preprocess block info + /// apply chunk results and store the results on chain. + pub(crate) fn postprocess_block( + &mut self, + me: &Option, + block: &Block, + block_preprocess_info: BlockPreprocessInfo, + apply_chunks_results: Vec<(ShardId, Result)>, + ) -> Result, Error> { + let shard_ids = self.epoch_manager.shard_ids(block.header().epoch_id())?; + let prev_hash = block.header().prev_hash(); + let results = apply_chunks_results.into_iter().map(|(shard_id, x)| { + if let Err(err) = &x { + warn!(target: "chain", shard_id, hash = %block.hash(), %err, "Error in applying chunk for block"); + } + x + }).collect::, Error>>()?; + self.apply_chunk_postprocessing(block, results)?; + + let BlockPreprocessInfo { + is_caught_up, + state_sync_info, + incoming_receipts, + challenges_result, + challenged_blocks, + .. + } = block_preprocess_info; + + if !is_caught_up { + debug!(target: "chain", %prev_hash, hash = %*block.hash(), "Add block to catch up"); + self.chain_store_update.add_block_to_catchup(*prev_hash, *block.hash()); + } + + for (shard_id, receipt_proofs) in incoming_receipts { + self.chain_store_update.save_incoming_receipt( + block.hash(), + shard_id, + Arc::new(receipt_proofs), + ); + } + if let Some(state_sync_info) = state_sync_info { + self.chain_store_update.add_state_sync_info(state_sync_info); + } + + self.chain_store_update.save_block_extra(block.hash(), BlockExtra { challenges_result }); + for block_hash in challenged_blocks { + self.mark_block_as_challenged(&block_hash, Some(block.hash()))?; + } + + self.chain_store_update.save_block_header(block.header().clone())?; + self.update_header_head_if_not_challenged(block.header())?; + + // If block checks out, record validator proposals for given block. + let last_final_block = block.header().last_final_block(); + let last_finalized_height = if last_final_block == &CryptoHash::default() { + self.chain_store_update.get_genesis_height() + } else { + self.chain_store_update.get_block_header(last_final_block)?.height() + }; + + let epoch_manager_update = self + .epoch_manager + .add_validator_proposals(BlockHeaderInfo::new(block.header(), last_finalized_height))?; + self.chain_store_update.merge(epoch_manager_update); + + #[cfg(feature = "new_epoch_sync")] + { + // BlockInfo should be already recorded in epoch_manager cache because of `add_validator_proposals` call + self.save_epoch_sync_info_if_finalised(block.header())?; + } + + // Add validated block to the db, even if it's not the canonical fork. + self.chain_store_update.save_block(block.clone()); + self.chain_store_update.inc_block_refcount(prev_hash)?; + + // Save receipt_id_to_shard_id for all outgoing receipts generated in this block + self.save_receipt_id_to_shard_id_for_block( + me.as_ref(), + block.hash(), + prev_hash, + &shard_ids, + )?; + + // Update the chain head if it's the new tip + let res = self.update_head(block.header())?; + + if res.is_some() { + // On the epoch switch record the epoch light client block + // Note that we only do it if `res.is_some()`, i.e. if the current block is the head. + // This is necessary because the computation of the light client block relies on + // `ColNextBlockHash`-es populated, and they are only populated for the canonical + // chain. We need to be careful to avoid a situation when the first block of the epoch + // never becomes a tip of the canonical chain. + // Presently the epoch boundary is defined by the height, and the fork choice rule + // is also just height, so the very first block to cross the epoch end is guaranteed + // to be the head of the chain, and result in the light client block produced. + let prev = self.chain_store_update.get_previous_header(block.header())?; + let prev_epoch_id = prev.epoch_id().clone(); + if block.header().epoch_id() != &prev_epoch_id { + if prev.last_final_block() != &CryptoHash::default() { + let light_client_block = self.create_light_client_block(&prev)?; + self.chain_store_update + .save_epoch_light_client_block(&prev_epoch_id.0, light_client_block); + } + } + + let shard_layout = self.epoch_manager.get_shard_layout_from_prev_block(prev.hash())?; + SHARD_LAYOUT_VERSION.set(shard_layout.version() as i64); + SHARD_LAYOUT_NUM_SHARDS.set(shard_layout.shard_ids().count() as i64); + } + Ok(res) + } + + pub fn create_light_client_block( + &mut self, + header: &BlockHeader, + ) -> Result { + // First update the last next_block, since it might not be set yet + self.chain_store_update.save_next_block_hash(header.prev_hash(), *header.hash()); + + Chain::create_light_client_block( + header, + self.epoch_manager.as_ref(), + &mut self.chain_store_update, + ) + } + + #[allow(dead_code)] + fn verify_orphan_header_approvals(&mut self, header: &BlockHeader) -> Result<(), Error> { + let prev_hash = header.prev_hash(); + let prev_height = match header.prev_height() { + None => { + // this will accept orphans of V1 and V2 + // TODO: reject header V1 and V2 after a certain height + return Ok(()); + } + Some(prev_height) => prev_height, + }; + let height = header.height(); + let epoch_id = header.epoch_id(); + let approvals = header.approvals(); + self.epoch_manager.verify_approvals_and_threshold_orphan( + epoch_id, + &|approvals, stakes| { + Doomslug::can_approved_block_be_produced( + self.doomslug_threshold_mode, + approvals, + stakes, + ) + }, + prev_hash, + prev_height, + height, + approvals, + ) + } + + /// Update the header head if this header has most work. + pub(crate) fn update_header_head_if_not_challenged( + &mut self, + header: &BlockHeader, + ) -> Result, Error> { + let header_head = self.chain_store_update.header_head()?; + if header.height() > header_head.height { + let tip = Tip::from_header(header); + self.chain_store_update.save_header_head_if_not_challenged(&tip)?; + debug!(target: "chain", "Header head updated to {} at {}", tip.last_block_hash, tip.height); + metrics::HEADER_HEAD_HEIGHT.set(tip.height as i64); + + Ok(Some(tip)) + } else { + Ok(None) + } + } + + fn update_final_head_from_block(&mut self, header: &BlockHeader) -> Result, Error> { + let final_head = self.chain_store_update.final_head()?; + let last_final_block_header = + match self.chain_store_update.get_block_header(header.last_final_block()) { + Ok(final_header) => final_header, + Err(Error::DBNotFoundErr(_)) => return Ok(None), + Err(err) => return Err(err), + }; + if last_final_block_header.height() > final_head.height { + let tip = Tip::from_header(&last_final_block_header); + self.chain_store_update.save_final_head(&tip)?; + Ok(Some(tip)) + } else { + Ok(None) + } + } + + /// Directly updates the head if we've just appended a new block to it or handle + /// the situation where the block has higher height to have a fork + fn update_head(&mut self, header: &BlockHeader) -> Result, Error> { + // if we made a fork with higher height than the head (which should also be true + // when extending the head), update it + self.update_final_head_from_block(header)?; + let head = self.chain_store_update.head()?; + if header.height() > head.height { + let tip = Tip::from_header(header); + + self.chain_store_update.save_body_head(&tip)?; + metrics::BLOCK_HEIGHT_HEAD.set(tip.height as i64); + metrics::BLOCK_ORDINAL_HEAD.set(header.block_ordinal() as i64); + debug!(target: "chain", "Head updated to {} at {}", tip.last_block_hash, tip.height); + Ok(Some(tip)) + } else { + Ok(None) + } + } + + /// Marks a block as invalid, + pub(crate) fn mark_block_as_challenged( + &mut self, + block_hash: &CryptoHash, + challenger_hash: Option<&CryptoHash>, + ) -> Result<(), Error> { + info!(target: "chain", "Marking {} as challenged block (challenged in {:?}) and updating the chain.", block_hash, challenger_hash); + let block_header = match self.chain_store_update.get_block_header(block_hash) { + Ok(block_header) => block_header, + Err(e) => match e { + Error::DBNotFoundErr(_) => { + // The block wasn't seen yet, still challenge is good. + self.chain_store_update.save_challenged_block(*block_hash); + return Ok(()); + } + _ => return Err(e), + }, + }; + + let cur_block_at_same_height = + match self.chain_store_update.get_block_hash_by_height(block_header.height()) { + Ok(bh) => Some(bh), + Err(e) => match e { + Error::DBNotFoundErr(_) => None, + _ => return Err(e), + }, + }; + + self.chain_store_update.save_challenged_block(*block_hash); + + // If the block being invalidated is on the canonical chain, update head + if cur_block_at_same_height == Some(*block_hash) { + // We only consider two candidates for the new head: the challenger and the block + // immediately preceding the block being challenged + // It could be that there is a better chain known. However, it is extremely unlikely, + // and even if there's such chain available, the very next block built on it will + // bring this node's head to that chain. + let prev_header = self.chain_store_update.get_block_header(block_header.prev_hash())?; + let prev_height = prev_header.height(); + let new_head_header = if let Some(hash) = challenger_hash { + let challenger_header = self.chain_store_update.get_block_header(hash)?; + if challenger_header.height() > prev_height { + challenger_header + } else { + prev_header + } + } else { + prev_header + }; + let last_final_block = *new_head_header.last_final_block(); + + let tip = Tip::from_header(&new_head_header); + self.chain_store_update.save_head(&tip)?; + let new_final_header = self.chain_store_update.get_block_header(&last_final_block)?; + self.chain_store_update.save_final_head(&Tip::from_header(&new_final_header))?; + } + + Ok(()) + } + + pub fn set_state_finalize( + &mut self, + shard_id: ShardId, + sync_hash: CryptoHash, + shard_state_header: ShardStateSyncResponseHeader, + ) -> Result<(), Error> { + let _span = + tracing::debug_span!(target: "sync", "chain_update_set_state_finalize").entered(); + let (chunk, incoming_receipts_proofs) = match shard_state_header { + ShardStateSyncResponseHeader::V1(shard_state_header) => ( + ShardChunk::V1(shard_state_header.chunk), + shard_state_header.incoming_receipts_proofs, + ), + ShardStateSyncResponseHeader::V2(shard_state_header) => { + (shard_state_header.chunk, shard_state_header.incoming_receipts_proofs) + } + }; + + let block_header = self + .chain_store_update + .get_block_header_on_chain_by_height(&sync_hash, chunk.height_included())?; + + // Getting actual incoming receipts. + let mut receipt_proof_response: Vec = vec![]; + for incoming_receipt_proof in incoming_receipts_proofs.iter() { + let ReceiptProofResponse(hash, _) = incoming_receipt_proof; + let block_header = self.chain_store_update.get_block_header(hash)?; + if block_header.height() <= chunk.height_included() { + receipt_proof_response.push(incoming_receipt_proof.clone()); + } + } + let receipts = collect_receipts_from_response(&receipt_proof_response); + // Prev block header should be present during state sync, since headers have been synced at this point. + let gas_price = if block_header.height() == self.chain_store_update.get_genesis_height() { + block_header.next_gas_price() + } else { + self.chain_store_update.get_block_header(block_header.prev_hash())?.next_gas_price() + }; + + let chunk_header = chunk.cloned_header(); + let gas_limit = chunk_header.gas_limit(); + // This is set to false because the value is only relevant + // during protocol version RestoreReceiptsAfterFixApplyChunks. + // TODO(nikurt): Determine the value correctly. + let is_first_block_with_chunk_of_version = false; + + let apply_result = self.runtime_adapter.apply_chunk( + RuntimeStorageConfig::new(chunk_header.prev_state_root(), true), + ApplyChunkShardContext { + shard_id, + gas_limit, + last_validator_power_proposals: chunk_header.prev_validator_power_proposals(), + last_validator_frozen_proposals: chunk_header.prev_validator_frozen_proposals(), + is_first_block_with_chunk_of_version, + is_new_chunk: true, + }, + ApplyChunkBlockContext { + height: chunk_header.height_included(), + block_hash: *block_header.hash(), + prev_block_hash: *chunk_header.prev_block_hash(), + block_timestamp: block_header.raw_timestamp(), + gas_price, + challenges_result: block_header.challenges_result().clone(), + random_seed: *block_header.random_value(), + }, + &receipts, + chunk.transactions(), + )?; + + let (outcome_root, outcome_proofs) = + ApplyChunkResult::compute_outcomes_proof(&apply_result.outcomes); + + self.chain_store_update.save_chunk(chunk); + + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, block_header.epoch_id())?; + let flat_storage_manager = self.runtime_adapter.get_flat_storage_manager(); + let store_update = flat_storage_manager.save_flat_state_changes( + *block_header.hash(), + *chunk_header.prev_block_hash(), + chunk_header.height_included(), + shard_uid, + apply_result.trie_changes.state_changes(), + )?; + self.chain_store_update.merge(store_update); + + self.chain_store_update.save_trie_changes(apply_result.trie_changes); + let chunk_extra = ChunkExtra::new( + &apply_result.new_root, + outcome_root, + apply_result.validator_power_proposals, + apply_result.validator_frozen_proposals, + apply_result.total_gas_burnt, + gas_limit, + apply_result.total_balance_burnt, + ); + self.chain_store_update.save_chunk_extra(block_header.hash(), &shard_uid, chunk_extra); + + self.chain_store_update.save_outgoing_receipt( + block_header.hash(), + shard_id, + apply_result.outgoing_receipts, + ); + // Saving transaction results. + self.chain_store_update.save_outcomes_with_proofs( + block_header.hash(), + shard_id, + apply_result.outcomes, + outcome_proofs, + ); + // Saving all incoming receipts. + for receipt_proof_response in incoming_receipts_proofs { + self.chain_store_update.save_incoming_receipt( + &receipt_proof_response.0, + shard_id, + receipt_proof_response.1, + ); + } + Ok(()) + } + + pub fn set_state_finalize_on_height( + &mut self, + height: BlockHeight, + shard_id: ShardId, + sync_hash: CryptoHash, + ) -> Result { + let _span = tracing::debug_span!(target: "sync", "set_state_finalize_on_height").entered(); + let block_header_result = + self.chain_store_update.get_block_header_on_chain_by_height(&sync_hash, height); + if let Err(_) = block_header_result { + // No such height, go ahead. + return Ok(true); + } + let block_header = block_header_result?; + if block_header.hash() == &sync_hash { + // Don't continue + return Ok(false); + } + let prev_block_header = + self.chain_store_update.get_block_header(block_header.prev_hash())?; + + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, block_header.epoch_id())?; + let chunk_extra = + self.chain_store_update.get_chunk_extra(prev_block_header.hash(), &shard_uid)?; + + let apply_result = self.runtime_adapter.apply_chunk( + RuntimeStorageConfig::new(*chunk_extra.state_root(), true), + ApplyChunkShardContext { + shard_id, + last_validator_power_proposals: chunk_extra.validator_power_proposals(), + last_validator_frozen_proposals: chunk_extra.validator_frozen_proposals(), + gas_limit: chunk_extra.gas_limit(), + is_new_chunk: false, + is_first_block_with_chunk_of_version: false, + }, + ApplyChunkBlockContext::from_header(&block_header, prev_block_header.next_gas_price()), + &[], + &[], + )?; + let flat_storage_manager = self.runtime_adapter.get_flat_storage_manager(); + let store_update = flat_storage_manager.save_flat_state_changes( + *block_header.hash(), + *prev_block_header.hash(), + height, + shard_uid, + apply_result.trie_changes.state_changes(), + )?; + self.chain_store_update.merge(store_update); + self.chain_store_update.save_trie_changes(apply_result.trie_changes); + + let mut new_chunk_extra = ChunkExtra::clone(&chunk_extra); + *new_chunk_extra.state_root_mut() = apply_result.new_root; + + self.chain_store_update.save_chunk_extra(block_header.hash(), &shard_uid, new_chunk_extra); + Ok(true) + } +} + +/// Epoch sync specific functions. +#[cfg(feature = "new_epoch_sync")] +impl<'a> ChainUpdate<'a> { + /// This function assumes `BlockInfo` is already retrievable from `epoch_manager`. + /// This can be achieved by calling `add_validator_proposals`. + pub(crate) fn save_epoch_sync_info_if_finalised( + &mut self, + header: &BlockHeader, + ) -> Result<(), Error> { + let block_info = self.epoch_manager.get_block_info(header.hash())?; + let epoch_first_block_hash = block_info.epoch_first_block(); + + if *epoch_first_block_hash == CryptoHash::default() { + // This is the genesis epoch. We don't have any fully finalised epoch yet. + return Ok(()); + } + + let epoch_first_block_info = self.epoch_manager.get_block_info(epoch_first_block_hash)?; + let prev_epoch_last_block_hash = epoch_first_block_info.prev_hash(); + + if *prev_epoch_last_block_hash == CryptoHash::default() { + // This is the genesis epoch. We don't have any fully finalised epoch yet. + return Ok(()); + } + let prev_epoch_last_block_info = + self.epoch_manager.get_block_info(prev_epoch_last_block_hash)?; + + if prev_epoch_last_block_info.epoch_id() == epoch_first_block_info.epoch_id() { + // Previous epoch is the genesis epoch. We don't have any fully finalised epoch yet. + return Ok(()); + } + + // Check that last finalised block is after epoch first block. + // So, that it is in the current epoch. + let last_final_block_hash = header.last_final_block(); + if *last_final_block_hash == CryptoHash::default() { + // We didn't finalise any blocks yet. We don't have any fully finalised epoch yet. + return Ok(()); + } + let last_final_block_info = self.epoch_manager.get_block_info(last_final_block_hash)?; + if last_final_block_info.epoch_id() != epoch_first_block_info.epoch_id() { + // Last finalised block is in the previous epoch. + // We didn't finalise header with `epoch_sync_data_hash` for the previous epoch yet. + return Ok(()); + } + if self.chain_store_update.store().exists( + unc_store::DBCol::EpochSyncInfo, + prev_epoch_last_block_info.epoch_id().as_ref(), + )? { + // We already wrote `EpochSyncInfo` for this epoch. + // Probably during epoch sync. + return Ok(()); + } + self.save_epoch_sync_info_impl(&prev_epoch_last_block_info, epoch_first_block_hash) + } + + /// If the block is the last one in the epoch + /// construct and record `EpochSyncInfo` to `self.chain_store_update`. + fn save_epoch_sync_info_impl( + &mut self, + last_block_info: &BlockInfo, + next_epoch_first_hash: &CryptoHash, + ) -> Result<(), Error> { + let mut store_update = self.chain_store_update.store().store_update(); + store_update + .set_ser( + unc_store::DBCol::EpochSyncInfo, + last_block_info.epoch_id().as_ref(), + &self.create_epoch_sync_info(last_block_info, next_epoch_first_hash, None)?, + ) + .map_err(unc_primitives::errors::EpochError::from)?; + self.chain_store_update.merge(store_update); + Ok(()) + } + + /// Create a pair of `BlockHeader`s necessary to create `BlockInfo` for `block_hash`: + /// - header for `block_hash` + /// - header for `last_final_block` of `block_hash` header + fn get_header_pair( + &self, + block_hash: &CryptoHash, + ) -> Result<(BlockHeader, BlockHeader), Error> { + let header = self.chain_store_update.get_block_header(block_hash)?; + // `block_hash` can correspond to genesis block, for which there is no last final block recorded, + // because `last_final_block` for genesis is `CryptoHash::default()` + // Here we return just the same genesis block header as last known block header + // TODO(posvyatokum) process this case carefully in epoch sync validation + // TODO(posvyatokum) process this carefully in saving the parts of epoch sync data + let last_finalised_header = { + if *header.last_final_block() == CryptoHash::default() { + header.clone() + } else { + self.chain_store_update.get_block_header(header.last_final_block())? + } + }; + Ok((header, last_finalised_header)) + } + + /// For epoch sync we need to save: + /// - (*) first header of the next epoch (contains `epoch_sync_data_hash` for `EpochInfo` validation) + /// - first header of the epoch + /// - last header of the epoch + /// - prev last header of the epoch + /// - every header on chain from `last_final_block` to the end of the epoch + /// - (*) header of the `last_final_block` for each of previously mentioned headers + /// + /// Because headers may repeat between those points, we use one `HashMap` to store them indexed by hash. + /// + /// Headers not marked with (*) need to be saved on the syncing node. + /// Headers marked with (*) only needed for `EpochSyncInfo` validation. + fn get_epoch_sync_info_headers( + &self, + last_block_info: &BlockInfo, + next_epoch_first_hash: &CryptoHash, + ) -> Result<(HashMap, HashSet), Error> { + let mut headers = HashMap::new(); + let mut headers_to_save = HashSet::new(); + + let mut add_header = |block_hash: &CryptoHash| -> Result<(), Error> { + let (header, last_finalised_header) = self.get_header_pair(block_hash)?; + headers.insert(*header.hash(), header); + headers.insert(*last_finalised_header.hash(), last_finalised_header); + headers_to_save.insert(*block_hash); + Ok(()) + }; + + add_header(next_epoch_first_hash)?; + add_header(last_block_info.epoch_first_block())?; + add_header(last_block_info.hash())?; + add_header(last_block_info.prev_hash())?; + + // If we didn't add `last_final_block_hash` yet, go down the chain until we find it. + if last_block_info.hash() != last_block_info.last_final_block_hash() + && last_block_info.prev_hash() != last_block_info.last_final_block_hash() + { + let mut current_header = + self.chain_store_update.get_block_header(last_block_info.prev_hash())?; + while current_header.hash() != last_block_info.last_final_block_hash() { + // This only should happen if BlockInfo data is incorrect. + // Without this assert same BlockInfo will cause infinite loop instead of crash with a message. + assert!( + current_header.height() > last_block_info.last_finalized_height(), + "Reached block at height {:?} with hash {:?} from {:?}", + current_header.height(), + current_header.hash(), + last_block_info + ); + + // current_header was already added, as we start from current_header = prev_header. + current_header = + self.chain_store_update.get_block_header(current_header.prev_hash())?; + add_header(current_header.hash())?; + } + } + + // We don't need to save `next_epoch_first_hash` during `EpochSyncInfo` processing. + // It is only needed for validation. + headers_to_save.remove(next_epoch_first_hash); + + Ok((headers, headers_to_save)) + } + + /// Data that is necessary to prove Epoch in new Epoch Sync. + pub fn create_epoch_sync_info( + &self, + last_block_info: &BlockInfo, + next_epoch_first_hash: &CryptoHash, + hash_to_prev_hash: Option<&HashMap>, + ) -> Result { + let mut all_block_hashes = + self.epoch_manager.get_all_epoch_hashes(last_block_info, hash_to_prev_hash)?; + all_block_hashes.reverse(); + + let (headers, headers_to_save) = + self.get_epoch_sync_info_headers(last_block_info, next_epoch_first_hash)?; + + let epoch_id = last_block_info.epoch_id(); + let next_epoch_id = self.epoch_manager.get_next_epoch_id(last_block_info.hash())?; + let next_next_epoch_id = unc_primitives::types::EpochId(*last_block_info.hash()); + + Ok(EpochSyncInfo { + all_block_hashes, + headers, + headers_to_save, + next_epoch_first_hash: *next_epoch_first_hash, + epoch_info: (*self.epoch_manager.get_epoch_info(epoch_id)?).clone(), + next_epoch_info: (*self.epoch_manager.get_epoch_info(&next_epoch_id)?).clone(), + next_next_epoch_info: (*self.epoch_manager.get_epoch_info(&next_next_epoch_id)?) + .clone(), + }) + } +} diff --git a/chain/chain/src/chunks_store.rs b/chain/chain/src/chunks_store.rs new file mode 100644 index 000000000..229d932d8 --- /dev/null +++ b/chain/chain/src/chunks_store.rs @@ -0,0 +1,60 @@ +use std::sync::Arc; + +use borsh::BorshDeserialize; +use unc_cache::CellLruCache; +use unc_chain_primitives::Error; +use unc_primitives::sharding::{ChunkHash, PartialEncodedChunk, ShardChunk}; +use unc_store::{DBCol, Store}; + +#[cfg(not(feature = "no_cache"))] +const CHUNK_CACHE_SIZE: usize = 1024; +#[cfg(feature = "no_cache")] +const CHUNK_CACHE_SIZE: usize = 1; + +pub struct ReadOnlyChunksStore { + store: Store, + partial_chunks: CellLruCache, Arc>, + chunks: CellLruCache, Arc>, +} + +impl ReadOnlyChunksStore { + pub fn new(store: Store) -> Self { + Self { + store, + partial_chunks: CellLruCache::new(CHUNK_CACHE_SIZE), + chunks: CellLruCache::new(CHUNK_CACHE_SIZE), + } + } + + fn read_with_cache<'a, T: BorshDeserialize + Clone + 'a>( + &self, + col: DBCol, + cache: &'a CellLruCache, T>, + key: &[u8], + ) -> std::io::Result> { + if let Some(value) = cache.get(key) { + return Ok(Some(value)); + } + if let Some(result) = self.store.get_ser::(col, key)? { + cache.put(key.to_vec(), result.clone()); + return Ok(Some(result)); + } + Ok(None) + } + pub fn get_partial_chunk( + &self, + chunk_hash: &ChunkHash, + ) -> Result, Error> { + match self.read_with_cache(DBCol::PartialChunks, &self.partial_chunks, chunk_hash.as_ref()) + { + Ok(Some(shard_chunk)) => Ok(shard_chunk), + _ => Err(Error::ChunkMissing(chunk_hash.clone())), + } + } + pub fn get_chunk(&self, chunk_hash: &ChunkHash) -> Result, Error> { + match self.read_with_cache(DBCol::Chunks, &self.chunks, chunk_hash.as_ref()) { + Ok(Some(shard_chunk)) => Ok(shard_chunk), + _ => Err(Error::ChunkMissing(chunk_hash.clone())), + } + } +} diff --git a/chain/chain/src/crypto_hash_timer.rs b/chain/chain/src/crypto_hash_timer.rs new file mode 100644 index 000000000..681e01b61 --- /dev/null +++ b/chain/chain/src/crypto_hash_timer.rs @@ -0,0 +1,75 @@ +use lru::LruCache; +use unc_primitives::{hash::CryptoHash, static_clock::StaticClock}; +use std::{ + sync::Mutex, + time::{Duration, Instant}, +}; + +use once_cell::sync::Lazy; + +// Cache with the mapping from CryptoHash (blocks, chunks) to the number milliseconds that it took to process them. +// Used only for debugging purposes. +static CRYPTO_HASH_TIMER_RESULTS: Lazy>> = + Lazy::new(|| Mutex::new(LruCache::new(10000))); + +/// Struct to measure computation times related to different CryptoHashes (for example chunk or block computations). +/// It stores the data in the global LRU cache, which allows it to be read afterwards. +/// +/// Example use: +/// ```rust, ignore +/// { +/// let _timer = CryptoHashTime(block.hash); +/// // Do stuff with the block +/// // timer gets released at the end of this block +/// } +/// ``` +#[must_use] +pub struct CryptoHashTimer { + key: CryptoHash, + start: Instant, +} + +impl CryptoHashTimer { + pub fn new(key: CryptoHash) -> Self { + Self::new_with_start(key, StaticClock::instant()) + } + pub fn new_with_start(key: CryptoHash, start: Instant) -> Self { + CryptoHashTimer { key, start } + } + pub fn get_timer_value(key: CryptoHash) -> Option { + CRYPTO_HASH_TIMER_RESULTS.lock().unwrap().get(&key).cloned() + } +} + +impl Drop for CryptoHashTimer { + fn drop(&mut self) { + let time_passed = StaticClock::instant() - self.start; + let mut guard_ = CRYPTO_HASH_TIMER_RESULTS.lock().unwrap(); + let previous = guard_.get(&self.key).copied().unwrap_or_default(); + guard_.put(self.key, time_passed + previous); + } +} + +#[test] +fn test_crypto_hash_timer() { + let crypto_hash: CryptoHash = "s3N6V7CNAN2Eg6vfivMVHR4hbMZeh72fTmYbrC6dXBT".parse().unwrap(); + // Timer should be missing. + assert_eq!(CryptoHashTimer::get_timer_value(crypto_hash), None); + let mock_clock_guard = unc_primitives::static_clock::MockClockGuard::default(); + let start_time = Instant::now(); + mock_clock_guard.add_instant(start_time); + mock_clock_guard.add_instant(start_time + std::time::Duration::from_secs(1)); + { + let _timer = CryptoHashTimer::new(crypto_hash); + } + // Timer should show exactly 1 second (1000 ms). + assert_eq!(CryptoHashTimer::get_timer_value(crypto_hash), Some(Duration::from_millis(1000))); + let start_time = Instant::now(); + mock_clock_guard.add_instant(start_time); + mock_clock_guard.add_instant(start_time + std::time::Duration::from_secs(2)); + { + let _timer = CryptoHashTimer::new(crypto_hash); + } + // Now we run the second time (with the same hash) - so the counter should show 3 seconds. + assert_eq!(CryptoHashTimer::get_timer_value(crypto_hash), Some(Duration::from_millis(3000))); +} diff --git a/chain/chain/src/doomslug.rs b/chain/chain/src/doomslug.rs new file mode 100644 index 000000000..57f66c5ab --- /dev/null +++ b/chain/chain/src/doomslug.rs @@ -0,0 +1,1263 @@ +use std::collections::{HashMap, VecDeque}; +use std::sync::Arc; +use std::time::{Duration, Instant}; + +use crate::doomslug::trackable::TrackableBlockHeightValue; +use crate::metrics; +use unc_client_primitives::debug::{ApprovalAtHeightStatus, ApprovalHistoryEntry}; +use unc_crypto::Signature; +use unc_primitives::block::{Approval, ApprovalInner}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::types::{AccountId, ApprovalFrozen, Balance, BlockHeight, BlockHeightDelta}; +use unc_primitives::validator_signer::ValidatorSigner; +use tracing::{debug, debug_span, field, info}; + +/// Have that many iterations in the timer instead of `loop` to prevent potential bugs from blocking +/// the node +const MAX_TIMER_ITERS: usize = 20; + +/// How many heights ahead to track approvals. This needs to be sufficiently large so that we can +/// recover after rather long network interruption, but not too large to consume too much memory if +/// someone in the network spams with invalid approvals. Note that we will only store approvals for +/// heights that are targeting us, which is once per as many heights as there are block producers, +/// thus 10_000 heights in practice will mean on the order of one hundred entries. +const MAX_HEIGHTS_AHEAD_TO_STORE_APPROVALS: BlockHeight = 10_000; + +// Number of blocks (before head) for which to keep the history of approvals (for debugging). +const MAX_HEIGHTS_BEFORE_TO_STORE_APPROVALS: u64 = 20; + +// Maximum amount of historical approvals that we'd keep for debugging purposes. +const MAX_HISTORY_SIZE: usize = 1000; + +/// The threshold for doomslug to create a block. +/// `TwoThirds` means the block can only be produced if at least 2/3 of the stake is approving it, +/// and is what should be used in production (and what guarantees finality) +/// `NoApprovals` means the block production is not blocked on approvals. This is used +/// in many tests (e.g. `cross_shard_tx`) to create lots of forkfulness. +#[derive(PartialEq, Eq, Debug, Clone, Copy)] +pub enum DoomslugThresholdMode { + NoApprovals, + TwoThirds, +} + +/// The result of processing an approval. +#[derive(PartialEq, Eq, Debug)] +pub enum DoomslugBlockProductionReadiness { + NotReady, + ReadySince(Instant), +} + +struct DoomslugTimer { + started: Instant, + last_endorsement_sent: Instant, + height: BlockHeight, + endorsement_delay: Duration, + min_delay: Duration, + delay_step: Duration, + max_delay: Duration, +} + +struct DoomslugTip { + block_hash: CryptoHash, + height: BlockHeight, +} + +struct DoomslugApprovalsTracker { + witness: HashMap)>, + account_id_to_stakes: HashMap, + total_stake_this_epoch: Balance, + approved_stake_this_epoch: Balance, + total_stake_next_epoch: Balance, + approved_stake_next_epoch: Balance, + time_passed_threshold: Option, + threshold_mode: DoomslugThresholdMode, +} + +mod trackable { + use unc_o11y::metrics::IntGauge; + use unc_primitives::types::BlockHeight; + use once_cell::sync::Lazy; + + pub struct TrackableBlockHeightValue(BlockHeight, &'static Lazy); + + impl TrackableBlockHeightValue { + pub fn new(value: BlockHeight, gauge: &'static Lazy) -> Self { + gauge.set(value as i64); + Self(value, gauge) + } + + pub fn get(&self) -> BlockHeight { + self.0 + } + + pub fn set(&mut self, new: BlockHeight) { + self.0 = new; + self.1.set(self.0 as i64); + } + } +} + +/// Approvals can arrive before the corresponding blocks, and we need a meaningful way to keep as +/// many approvals as possible that can be useful in the future, while not allowing an adversary +/// to spam us with invalid approvals. +/// To that extent, for each `account_id` and each `target_height` we keep exactly one approval, +/// whichever came last. We only maintain those for +/// a) `account_id`s that match the corresponding epoch (and for which we can validate a signature) +/// b) `target_height`s for which we produce blocks +/// c) `target_height`s within a meaningful horizon from the current tip. +/// This class is responsible for maintaining witnesses for the blocks, while also ensuring that +/// only one approval per (`account_id`) is kept. We instantiate one such class per height, thus +/// ensuring that only one approval is kept per (`target_height`, `account_id`). `Doomslug` below +/// ensures that only instances within the horizon are kept, and the user of the `Doomslug` is +/// responsible for ensuring that only approvals for proper account_ids with valid signatures are +/// provided. +struct DoomslugApprovalsTrackersAtHeight { + approval_trackers: HashMap, + last_approval_per_account: HashMap, +} + +/// Contains all the logic for Doomslug, but no integration with chain or storage. The integration +/// happens via `PersistentDoomslug` struct. The split is to simplify testing of the logic separate +/// from the chain. +pub struct Doomslug { + approval_tracking: HashMap, + /// Largest target height for which we issued an approval + largest_target_height: TrackableBlockHeightValue, + /// Largest height for which we saw a block containing 1/2 endorsements in it + largest_final_height: TrackableBlockHeightValue, + /// Largest height for which we saw threshold approvals (and thus can potentially create a block) + largest_threshold_height: TrackableBlockHeightValue, + /// Largest target height of approvals that we've received + largest_approval_height: TrackableBlockHeightValue, + /// Information Doomslug tracks about the chain tip + tip: DoomslugTip, + /// Whether an endorsement (or in general an approval) was sent since updating the tip + endorsement_pending: bool, + /// Information to track the timer (see `start_timer` routine in the paper) + timer: DoomslugTimer, + signer: Option>, + /// How many approvals to have before producing a block. In production should be always `HalfStake`, + /// but for many tests we use `NoApprovals` to invoke more forkfulness + threshold_mode: DoomslugThresholdMode, + + /// Approvals that were created by this doomslug instance (for debugging only). + /// Keeps up to MAX_HISTORY_SIZE entries. + history: VecDeque, +} + +impl DoomslugTimer { + /// Computes the delay to sleep given the number of heights from the last final block + /// This is what `T` represents in the paper. + /// + /// # Arguments + /// * `n` - number of heights since the last block with doomslug finality + /// + /// # Returns + /// Duration to sleep + pub fn get_delay(&self, n: BlockHeightDelta) -> Duration { + let n32 = u32::try_from(n).unwrap_or(u32::MAX); + std::cmp::min(self.max_delay, self.min_delay + self.delay_step * n32.saturating_sub(2)) + } +} + +impl DoomslugApprovalsTracker { + fn new( + account_id_to_stakes: HashMap, + threshold_mode: DoomslugThresholdMode, + ) -> Self { + let total_stake_this_epoch = account_id_to_stakes.values().map(|(x, _)| x).sum::(); + let total_stake_next_epoch = account_id_to_stakes.values().map(|(_, x)| x).sum::(); + + DoomslugApprovalsTracker { + witness: Default::default(), + account_id_to_stakes, + total_stake_this_epoch, + total_stake_next_epoch, + approved_stake_this_epoch: 0, + approved_stake_next_epoch: 0, + time_passed_threshold: None, + threshold_mode, + } + } + + /// Given a single approval (either an endorsement or a skip-message) updates the approved + /// stake on the block that is being approved, and returns whether the block is now ready to be + /// produced. + /// + /// # Arguments + /// * now - the current timestamp + /// * approval - the approval to process + /// + /// # Returns + /// Whether the block is ready to be produced + fn process_approval( + &mut self, + now: Instant, + approval: &Approval, + ) -> DoomslugBlockProductionReadiness { + let mut increment_approved_stake = false; + self.witness.entry(approval.account_id.clone()).or_insert_with(|| { + increment_approved_stake = true; + (approval.clone(), chrono::Utc::now()) + }); + + if increment_approved_stake { + let stakes = self.account_id_to_stakes.get(&approval.account_id).map_or((0, 0), |x| *x); + self.approved_stake_this_epoch += stakes.0; + self.approved_stake_next_epoch += stakes.1; + } + + // We call to `get_block_production_readiness` here so that if the number of approvals crossed + // the threshold, the timer for block production starts. + self.get_block_production_readiness(now) + } + + /// Withdraws an approval. This happens if a newer approval for the same `target_height` comes + /// from the same account. Removes the approval from the `witness` and updates approved and + /// endorsed stakes. + fn withdraw_approval(&mut self, account_id: &AccountId) { + let approval = match self.witness.remove(account_id) { + None => return, + Some(approval) => approval.0, + }; + + let stakes = self.account_id_to_stakes.get(&approval.account_id).map_or((0, 0), |x| *x); + self.approved_stake_this_epoch -= stakes.0; + self.approved_stake_next_epoch -= stakes.1; + } + + /// Returns whether the block has enough approvals, and if yes, since what moment it does. + /// + /// # Arguments + /// * now - the current timestamp + /// + /// # Returns + /// `NotReady` if the block doesn't have enough approvals yet to cross the threshold + /// `ReadySince` if the block has enough approvals to pass the threshold, and since when it + /// does + fn get_block_production_readiness(&mut self, now: Instant) -> DoomslugBlockProductionReadiness { + if (self.approved_stake_this_epoch > self.total_stake_this_epoch * 2 / 3 + && (self.approved_stake_next_epoch > self.total_stake_next_epoch * 2 / 3 + || self.total_stake_next_epoch == 0)) + || self.threshold_mode == DoomslugThresholdMode::NoApprovals + { + if self.time_passed_threshold == None { + self.time_passed_threshold = Some(now); + } + DoomslugBlockProductionReadiness::ReadySince(self.time_passed_threshold.unwrap()) + } else { + DoomslugBlockProductionReadiness::NotReady + } + } + + // Get witnesses together with their arrival time. + fn get_witnesses(&self) -> Vec<(AccountId, chrono::DateTime)> { + self.witness + .iter() + .map(|(key, (_, arrival_time))| (key.clone(), *arrival_time)) + .collect::>() + } +} + +impl DoomslugApprovalsTrackersAtHeight { + fn new() -> Self { + Self { approval_trackers: HashMap::new(), last_approval_per_account: HashMap::new() } + } + + /// This method is a wrapper around `DoomslugApprovalsTracker::process_approval`, see comment + /// above it for more details. + /// This method has an extra logic that ensures that we only track one approval per `account_id`, + /// if we already know some other approval for this account, we first withdraw it from the + /// corresponding tracker, and associate the new approval with the account. + /// + /// # Arguments + /// * `now` - the current timestamp + /// * `approval` - the approval to be processed + /// * `stakes` - all the stakes of all the block producers in the current epoch + /// * `threshold_mode` - how many approvals are needed to produce a block. Is used to compute + /// the return value + /// + /// # Returns + /// Same as `DoomslugApprovalsTracker::process_approval` + fn process_approval( + &mut self, + now: Instant, + approval: &Approval, + stakes: &[(ApprovalFrozen, bool)], + threshold_mode: DoomslugThresholdMode, + ) -> DoomslugBlockProductionReadiness { + if let Some(last_parent) = self.last_approval_per_account.get(&approval.account_id) { + let should_remove = self + .approval_trackers + .get_mut(last_parent) + .map(|x| { + x.withdraw_approval(&approval.account_id); + x.witness.is_empty() + }) + .unwrap_or(false); + + if should_remove { + self.approval_trackers.remove(last_parent); + } + } + + let account_id_to_stakes = stakes + .iter() + .filter_map(|(x, is_slashed)| { + if *is_slashed { + None + } else { + Some((x.account_id.clone(), (x.frozen_this_epoch, x.frozen_next_epoch))) + } + }) + .collect::>(); + + assert_eq!(account_id_to_stakes.len(), stakes.len()); + + if !account_id_to_stakes.contains_key(&approval.account_id) { + return DoomslugBlockProductionReadiness::NotReady; + } + + self.last_approval_per_account.insert(approval.account_id.clone(), approval.inner.clone()); + self.approval_trackers + .entry(approval.inner.clone()) + .or_insert_with(|| DoomslugApprovalsTracker::new(account_id_to_stakes, threshold_mode)) + .process_approval(now, approval) + } + + /// Returns the current approvals status for the trackers at this height. + /// Status contains information about which account voted (and for what) and whether the doomslug voting threshold was reached. + pub fn status(&self) -> ApprovalAtHeightStatus { + let approvals = self + .approval_trackers + .iter() + .flat_map(|(approval, tracker)| { + let witnesses = tracker.get_witnesses(); + witnesses.into_iter().map(|(account_name, approval_time)| { + (account_name, (approval.clone(), approval_time)) + }) + }) + .collect::>(); + + let threshold_approval = self + .approval_trackers + .iter() + .filter_map(|(_, tracker)| tracker.time_passed_threshold) + .min() + .map(|ts| { + chrono::Utc::now() + - chrono::Duration::from_std(ts.elapsed()).unwrap_or(chrono::Duration::days(1)) + }); + ApprovalAtHeightStatus { approvals, ready_at: threshold_approval } + } +} + +impl Doomslug { + pub fn new( + largest_target_height: BlockHeight, + endorsement_delay: Duration, + min_delay: Duration, + delay_step: Duration, + max_delay: Duration, + signer: Option>, + threshold_mode: DoomslugThresholdMode, + ) -> Self { + Doomslug { + approval_tracking: HashMap::new(), + largest_target_height: TrackableBlockHeightValue::new( + largest_target_height, + &metrics::LARGEST_TARGET_HEIGHT, + ), + largest_approval_height: TrackableBlockHeightValue::new( + 0, + &metrics::LARGEST_APPROVAL_HEIGHT, + ), + largest_final_height: TrackableBlockHeightValue::new(0, &metrics::LARGEST_FINAL_HEIGHT), + largest_threshold_height: TrackableBlockHeightValue::new( + 0, + &metrics::LARGEST_THRESHOLD_HEIGHT, + ), + tip: DoomslugTip { block_hash: CryptoHash::default(), height: 0 }, + endorsement_pending: false, + timer: DoomslugTimer { + started: StaticClock::instant(), + last_endorsement_sent: StaticClock::instant(), + height: 0, + endorsement_delay, + min_delay, + delay_step, + max_delay, + }, + signer, + threshold_mode, + history: VecDeque::new(), + } + } + + #[cfg(feature = "test_features")] + pub fn adv_disable(&mut self) { + self.threshold_mode = DoomslugThresholdMode::NoApprovals + } + + /// Returns the `(hash, height)` of the current tip. Currently is only used by tests. + pub fn get_tip(&self) -> (CryptoHash, BlockHeight) { + (self.tip.block_hash, self.tip.height) + } + + /// Returns the largest height for which we have enough approvals to be theoretically able to + /// produce a block (in practice a blocks might not be produceable yet if not enough time + /// passed since it accumulated enough approvals) + pub fn get_largest_height_crossing_threshold(&self) -> BlockHeight { + self.largest_threshold_height.get() + } + + /// Returns the largest height for which we've received an approval + pub fn get_largest_approval_height(&self) -> BlockHeight { + self.largest_approval_height.get() + } + + pub fn get_largest_final_height(&self) -> BlockHeight { + self.largest_final_height.get() + } + + pub fn get_largest_target_height(&self) -> BlockHeight { + self.largest_target_height.get() + } + + pub fn get_timer_height(&self) -> BlockHeight { + self.timer.height + } + + pub fn get_timer_start(&self) -> Instant { + self.timer.started + } + + /// Returns currently available approval history. + pub fn get_approval_history(&self) -> Vec { + self.history.iter().cloned().collect::>() + } + + /// Adds new approval to the history. + fn update_history(&mut self, entry: ApprovalHistoryEntry) { + while self.history.len() >= MAX_HISTORY_SIZE { + self.history.pop_front(); + } + self.history.push_back(entry); + } + + /// Is expected to be called periodically and processed the timer (`start_timer` in the paper) + /// If the `cur_time` way ahead of last time the `process_timer` was called, will only process + /// a bounded number of steps, to avoid an infinite loop in case of some bugs. + /// Processes sending delayed approvals or skip messages + /// A major difference with the paper is that we process endorsement from the `process_timer`, + /// not at the time of receiving a block. It is done to stagger blocks if the network is way + /// too fast (e.g. during tests, or if a large set of validators have connection significantly + /// better between themselves than with the rest of the validators) + /// + /// # Arguments + /// * `cur_time` - is expected to receive `now`. Doesn't directly use `now` to simplify testing + /// + /// # Returns + /// A vector of approvals that need to be sent to other block producers as a result of processing + /// the timers + #[must_use] + pub fn process_timer(&mut self, cur_time: Instant) -> Vec { + let mut ret = vec![]; + for _ in 0..MAX_TIMER_ITERS { + let skip_delay = self + .timer + .get_delay(self.timer.height.saturating_sub(self.largest_final_height.get())); + + // The `endorsement_delay` is time to send approval to the block producer at `timer.height`, + // while the `skip_delay` is the time before sending the approval to BP of `timer_height + 1`, + // so it makes sense for them to be at least 2x apart + debug_assert!(skip_delay >= 2 * self.timer.endorsement_delay); + + let tip_height = self.tip.height; + + if self.endorsement_pending + && cur_time >= self.timer.last_endorsement_sent + self.timer.endorsement_delay + { + if tip_height >= self.largest_target_height.get() { + self.largest_target_height.set(tip_height + 1); + + if let Some(approval) = self.create_approval(tip_height + 1) { + ret.push(approval); + } + self.update_history(ApprovalHistoryEntry { + parent_height: tip_height, + target_height: tip_height + 1, + timer_started_ago_millis: self + .timer + .last_endorsement_sent + .elapsed() + .as_millis() as u64, + expected_delay_millis: self.timer.endorsement_delay.as_millis() as u64, + approval_creation_time: chrono::Utc::now(), + }); + } + + self.timer.last_endorsement_sent = cur_time; + self.endorsement_pending = false; + } + + if cur_time >= self.timer.started + skip_delay { + debug_assert!(!self.endorsement_pending); + + self.largest_target_height + .set(std::cmp::max(self.timer.height + 1, self.largest_target_height.get())); + + if let Some(approval) = self.create_approval(self.timer.height + 1) { + ret.push(approval); + } + self.update_history(ApprovalHistoryEntry { + parent_height: tip_height, + target_height: self.timer.height + 1, + timer_started_ago_millis: self.timer.started.elapsed().as_millis() as u64, + expected_delay_millis: skip_delay.as_millis() as u64, + approval_creation_time: chrono::Utc::now(), + }); + + // Restart the timer + self.timer.started += skip_delay; + self.timer.height += 1; + } else { + break; + } + } + + ret + } + + fn create_approval(&self, target_height: BlockHeight) -> Option { + self.signer.as_ref().map(|signer| { + Approval::new(self.tip.block_hash, self.tip.height, target_height, &**signer) + }) + } + + /// Determines whether a block has enough approvals to be produced. + /// In production (with `mode == HalfStake`) we require the total stake of all the approvals to + /// be strictly more than half of the total stake. For many non-doomslug specific tests + /// (with `mode == NoApprovals`) no approvals are needed. + /// + /// # Arguments + /// * `mode` - whether we want half of the total stake or just a single approval + /// * `approvals` - the set of approvals in the current block + /// * `stakes` - the vector of validator stakes in the current epoch + pub fn can_approved_block_be_produced( + mode: DoomslugThresholdMode, + approvals: &[Option>], + stakes: &[(Balance, Balance, bool)], + ) -> bool { + if mode == DoomslugThresholdMode::NoApprovals { + return true; + } + + let threshold1 = stakes.iter().map(|(x, _, _)| x).sum::() * 2 / 3; + let threshold2 = stakes.iter().map(|(_, x, _)| x).sum::() * 2 / 3; + + let approved_stake1 = approvals + .iter() + .zip(stakes.iter()) + .filter(|(_, (_, _, is_slashed))| !*is_slashed) + .map(|(approval, (stake, _, _))| if approval.is_some() { *stake } else { 0 }) + .sum::(); + + let approved_stake2 = approvals + .iter() + .zip(stakes.iter()) + .filter(|(_, (_, _, is_slashed))| !*is_slashed) + .map(|(approval, (_, stake, _))| if approval.is_some() { *stake } else { 0 }) + .sum::(); + + (approved_stake1 > threshold1 || threshold1 == 0) + && (approved_stake2 > threshold2 || threshold2 == 0) + } + + pub fn get_witness( + &self, + prev_hash: &CryptoHash, + parent_height: BlockHeight, + target_height: BlockHeight, + ) -> HashMap)> { + let hash_or_height = ApprovalInner::new(prev_hash, parent_height, target_height); + if let Some(approval_trackers_at_height) = self.approval_tracking.get(&target_height) { + let approvals_tracker = + approval_trackers_at_height.approval_trackers.get(&hash_or_height); + match approvals_tracker { + None => HashMap::new(), + Some(approvals_tracker) => approvals_tracker.witness.clone(), + } + } else { + HashMap::new() + } + } + + /// Updates the current tip of the chain. Restarts the timer accordingly. + /// + /// # Arguments + /// * `now` - current time. Doesn't call to `Utc::now()` directly to simplify testing + /// * `block_hash` - the hash of the new tip + /// * `height` - the height of the tip + /// * `last_ds_final_height` - last height at which a block in this chain has doomslug finality + pub fn set_tip( + &mut self, + now: Instant, + block_hash: CryptoHash, + height: BlockHeight, + last_final_height: BlockHeight, + ) { + debug_assert!(height > self.tip.height || self.tip.height == 0); + self.tip = DoomslugTip { block_hash, height }; + + self.largest_final_height.set(last_final_height); + self.timer.height = height + 1; + self.timer.started = now; + + self.approval_tracking.retain(|h, _| { + *h > height.saturating_sub(MAX_HEIGHTS_BEFORE_TO_STORE_APPROVALS) + && *h <= height + MAX_HEIGHTS_AHEAD_TO_STORE_APPROVALS + }); + + self.endorsement_pending = true; + } + + /// Records an approval message, and return whether the block has passed the threshold / ready + /// to be produced without waiting any further. See the comment for `DoomslugApprovalTracker::process_approval` + /// for details + #[must_use] + fn on_approval_message_internal( + &mut self, + now: Instant, + approval: &Approval, + all_frozen: &[(ApprovalFrozen, bool)], + ) -> DoomslugBlockProductionReadiness { + let threshold_mode = self.threshold_mode; + let ret = self + .approval_tracking + .entry(approval.target_height) + .or_insert_with(|| DoomslugApprovalsTrackersAtHeight::new()) + .process_approval(now, approval, all_frozen, threshold_mode); + + if approval.target_height > self.largest_approval_height.get() { + self.largest_approval_height.set(approval.target_height); + } + + if ret != DoomslugBlockProductionReadiness::NotReady { + if approval.target_height > self.largest_threshold_height.get() { + self.largest_threshold_height.set(approval.target_height); + } + } + + ret + } + + /// Processes single approval + pub fn on_approval_message( + &mut self, + now: Instant, + approval: &Approval, + all_frozen: &[(ApprovalFrozen, bool)], + ) { + if approval.target_height < self.tip.height + || approval.target_height > self.tip.height + MAX_HEIGHTS_AHEAD_TO_STORE_APPROVALS + { + return; + } + + let _ = self.on_approval_message_internal(now, approval, all_frozen); + } + + /// Gets the current status of approvals for a given height. + /// It will only work for heights that we have in memory, that is that are not older than MAX_HEIGHTS_BEFORE_TO_STORE_APPROVALS + /// blocks from the head. + pub fn approval_status_at_height(&self, height: &BlockHeight) -> ApprovalAtHeightStatus { + self.approval_tracking.get(height).map(|it| it.status()).unwrap_or_default() + } + + /// Returns whether we can produce a block for this height. The check for whether `me` is the + /// block producer for the height needs to be done by the caller. + /// We can produce a block if: + /// - The block has 2/3 of approvals, doomslug-finalizing the previous block, and we have + /// enough chunks, or + /// - The block has 1/2 of approvals, and T(h' / 6) has passed since the block has had 1/2 of + /// approvals for the first time, where h' is time since the last ds-final block. + /// Only the height is passed into the function, we use the tip known to `Doomslug` as the + /// parent hash. + /// + /// # Arguments: + /// * `now` - current timestamp + /// * `target_height` - the height for which the readiness is checked + /// * `has_enough_chunks` - if not, we will wait for T(h' / 6) even if we have 2/3 approvals & + /// have the previous block ds-final. + #[must_use] + pub fn ready_to_produce_block( + &mut self, + now: Instant, + target_height: BlockHeight, + has_enough_chunks: bool, + log_block_production_info: bool, + ) -> bool { + let span = debug_span!( + target: "doomslug", + "ready_to_produce_block", + has_enough_chunks, + target_height, + enough_approvals_for = field::Empty, + ready_to_produce_block = field::Empty, + need_to_wait = field::Empty) + .entered(); + let hash_or_height = + ApprovalInner::new(&self.tip.block_hash, self.tip.height, target_height); + if let Some(approval_trackers_at_height) = self.approval_tracking.get_mut(&target_height) { + if let Some(approval_tracker) = + approval_trackers_at_height.approval_trackers.get_mut(&hash_or_height) + { + let block_production_readiness = + approval_tracker.get_block_production_readiness(now); + match block_production_readiness { + DoomslugBlockProductionReadiness::NotReady => false, + DoomslugBlockProductionReadiness::ReadySince(when) => { + let enough_approvals_for = now.saturating_duration_since(when); + span.record("enough_approvals_for", enough_approvals_for.as_secs_f64()); + span.record("ready_to_produce_block", true); + if has_enough_chunks { + if log_block_production_info { + info!( + target: "doomslug", + target_height, + ?enough_approvals_for, + "ready to produce block, has enough approvals, has enough chunks"); + } + true + } else { + let delay = self.timer.get_delay( + self.timer.height.saturating_sub(self.largest_final_height.get()), + ) / 6; + + let ready = now > when + delay; + span.record("need_to_wait", !ready); + if log_block_production_info { + if ready { + info!( + target: "doomslug", + target_height, + ?enough_approvals_for, + "ready to produce block, but does not have enough chunks"); + } else { + info!( + target: "doomslug", + target_height, + need_to_wait_for = ?(when + delay).saturating_duration_since(now), + ?enough_approvals_for, + "not ready to produce block, need to wait"); + } + } + ready + } + } + } + } else { + debug!(target: "doomslug", target_height, ?hash_or_height, "No approval tracker"); + false + } + } else { + debug!(target: "doomslug", target_height, "No approval trackers at height"); + false + } + } +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + use std::time::Duration; + + use unc_crypto::{KeyType, SecretKey}; + use unc_primitives::block::{Approval, ApprovalInner}; + use unc_primitives::hash::hash; + use unc_primitives::static_clock::StaticClock; + use unc_primitives::test_utils::create_test_signer; + use unc_primitives::types::ApprovalStake; + + use crate::doomslug::{ + DoomslugApprovalsTrackersAtHeight, DoomslugBlockProductionReadiness, DoomslugThresholdMode, + }; + use crate::Doomslug; + + #[test] + fn test_endorsements_and_skips_basic() { + let mut ds = Doomslug::new( + 0, + Duration::from_millis(400), + Duration::from_millis(1000), + Duration::from_millis(100), + Duration::from_millis(3000), + Some(Arc::new(create_test_signer("test"))), + DoomslugThresholdMode::TwoThirds, + ); + + let mut now = StaticClock::instant(); // For the test purposes the absolute value of the initial instant doesn't matter + + // Set a new tip, must produce an endorsement + ds.set_tip(now, hash(&[1]), 1, 1); + assert_eq!(ds.process_timer(now + Duration::from_millis(399)).len(), 0); + let approval = + ds.process_timer(now + Duration::from_millis(400)).into_iter().nth(0).unwrap(); + assert_eq!(approval.inner, ApprovalInner::Endorsement(hash(&[1]))); + assert_eq!(approval.target_height, 2); + + // Same tip => no approval + assert_eq!(ds.process_timer(now + Duration::from_millis(400)), vec![]); + + // The block was `ds_final` and therefore started the timer. Try checking before one second expires + assert_eq!(ds.process_timer(now + Duration::from_millis(999)), vec![]); + + // But one second should trigger the skip + match ds.process_timer(now + Duration::from_millis(1000)) { + approvals if approvals.is_empty() => assert!(false), + approvals => { + assert_eq!(approvals[0].inner, ApprovalInner::Skip(1)); + assert_eq!(approvals[0].target_height, 3); + } + } + + // Shift now 1 second forward + now += Duration::from_millis(1000); + + // Not processing a block at height 2 should not produce an appoval + ds.set_tip(now, hash(&[2]), 2, 0); + assert_eq!(ds.process_timer(now + Duration::from_millis(400)), vec![]); + + // Shift now 1 second forward + now += Duration::from_millis(1000); + + // But at height 3 should (also neither block has finality set, keep last final at 0 for now) + ds.set_tip(now, hash(&[3]), 3, 0); + let approval = + ds.process_timer(now + Duration::from_millis(400)).into_iter().nth(0).unwrap(); + assert_eq!(approval.inner, ApprovalInner::Endorsement(hash(&[3]))); + assert_eq!(approval.target_height, 4); + + // Move 1 second further + now += Duration::from_millis(1000); + + assert_eq!(ds.process_timer(now + Duration::from_millis(199)), vec![]); + + match ds.process_timer(now + Duration::from_millis(200)) { + approvals if approvals.is_empty() => assert!(false), + approvals if approvals.len() == 1 => { + assert_eq!(approvals[0].inner, ApprovalInner::Skip(3)); + assert_eq!(approvals[0].target_height, 5); + } + _ => assert!(false), + } + + // Move 1 second further + now += Duration::from_millis(1000); + + // Now skip 5 (the extra delay is 200+300 = 500) + assert_eq!(ds.process_timer(now + Duration::from_millis(499)), vec![]); + + match ds.process_timer(now + Duration::from_millis(500)) { + approvals if approvals.is_empty() => assert!(false), + approvals => { + assert_eq!(approvals[0].inner, ApprovalInner::Skip(3)); + assert_eq!(approvals[0].target_height, 6); + } + } + + // Move 1 second further + now += Duration::from_millis(1000); + + // Skip 6 (the extra delay is 0+200+300+400 = 900) + assert_eq!(ds.process_timer(now + Duration::from_millis(899)), vec![]); + + match ds.process_timer(now + Duration::from_millis(900)) { + approvals if approvals.is_empty() => assert!(false), + approvals => { + assert_eq!(approvals[0].inner, ApprovalInner::Skip(3)); + assert_eq!(approvals[0].target_height, 7); + } + } + + // Move 1 second further + now += Duration::from_millis(1000); + + // Accept block at 5 with finality on the prev block, expect it to not produce an approval + ds.set_tip(now, hash(&[5]), 5, 4); + assert_eq!(ds.process_timer(now + Duration::from_millis(400)), vec![]); + + // Skip a whole bunch of heights by moving 100 seconds ahead + now += Duration::from_millis(100_000); + assert!(ds.process_timer(now).len() > 10); + + // Add some random small number of milliseconds to test that when the next block is added, the + // timer is reset + now += Duration::from_millis(17); + + // No approval, since we skipped 6 + ds.set_tip(now, hash(&[6]), 6, 4); + assert_eq!(ds.process_timer(now + Duration::from_millis(400)), vec![]); + + // The block height was less than the timer height, and thus the timer was reset. + // The wait time for height 7 with last ds final block at 5 is 1100 + assert_eq!(ds.process_timer(now + Duration::from_millis(1099)), vec![]); + + match ds.process_timer(now + Duration::from_millis(1100)) { + approvals if approvals.is_empty() => assert!(false), + approvals => { + assert_eq!(approvals[0].inner, ApprovalInner::Skip(6)); + assert_eq!(approvals[0].target_height, 8); + } + } + } + + #[test] + fn test_doomslug_approvals() { + let accounts: Vec<(&str, u128, u128)> = + vec![("test1", 2, 0), ("test2", 1, 0), ("test3", 3, 0), ("test4", 1, 0)]; + let stakes = accounts + .iter() + .map(|(account_id, stake_this_epoch, stake_next_epoch)| ApprovalStake { + account_id: account_id.parse().unwrap(), + stake_this_epoch: *stake_this_epoch, + stake_next_epoch: *stake_next_epoch, + public_key: SecretKey::from_seed(KeyType::ED25519, account_id).public_key(), + }) + .map(|stake| (stake, false)) + .collect::>(); + let signers = accounts + .iter() + .map(|(account_id, _, _)| create_test_signer(account_id)) + .collect::>(); + + let signer = Arc::new(create_test_signer("test")); + let mut ds = Doomslug::new( + 0, + Duration::from_millis(400), + Duration::from_millis(1000), + Duration::from_millis(100), + Duration::from_millis(3000), + Some(signer), + DoomslugThresholdMode::TwoThirds, + ); + + let mut now = StaticClock::instant(); + + // In the comments below the format is + // account, height -> approved stake + // The total stake is 7, so the threshold is 5 + + // "test1", 2 -> 2 + assert_eq!( + ds.on_approval_message_internal( + now, + &Approval::new(hash(&[1]), 1, 2, &signers[0]), + &stakes, + ), + DoomslugBlockProductionReadiness::NotReady, + ); + + // "test3", 4 -> 3 + assert_eq!( + ds.on_approval_message_internal( + now, + &Approval::new(hash(&[1]), 1, 4, &signers[2]), + &stakes, + ), + DoomslugBlockProductionReadiness::NotReady, + ); + + // "test4", 4 -> 4 + assert_eq!( + ds.on_approval_message_internal( + now, + &Approval::new(hash(&[1]), 1, 4, &signers[3]), + &stakes, + ), + DoomslugBlockProductionReadiness::NotReady, + ); + + // "test1", 4 -> same account, still 5 + assert_eq!( + ds.on_approval_message_internal( + now + Duration::from_millis(100), + &Approval::new(hash(&[1]), 1, 4, &signers[3]), + &stakes, + ), + DoomslugBlockProductionReadiness::NotReady, + ); + + // "test2", 4 -> 5 + assert_eq!( + ds.on_approval_message_internal( + now, + &Approval::new(hash(&[1]), 1, 4, &signers[1]), + &stakes, + ), + DoomslugBlockProductionReadiness::ReadySince(now), + ); + + // "test1", 4 -> 7 + assert_eq!( + ds.on_approval_message_internal( + now, + &Approval::new(hash(&[1]), 1, 4, &signers[0]), + &stakes, + ), + DoomslugBlockProductionReadiness::ReadySince(now), + ); + + // "test4", 2 -> 3 + assert_eq!( + ds.on_approval_message_internal( + now, + &Approval::new(hash(&[1]), 1, 2, &signers[3]), + &stakes, + ), + DoomslugBlockProductionReadiness::NotReady, + ); + + now += Duration::from_millis(200); + + // "test3", 2 -> 6 + assert_eq!( + ds.on_approval_message_internal( + now, + &Approval::new(hash(&[1]), 1, 2, &signers[2]), + &stakes, + ), + DoomslugBlockProductionReadiness::ReadySince(now), + ); + + // A different parent hash + assert_eq!( + ds.on_approval_message_internal( + now, + &Approval::new(hash(&[2]), 2, 4, &signers[1]), + &stakes, + ), + DoomslugBlockProductionReadiness::NotReady, + ); + } + + #[test] + fn test_doomslug_one_approval_per_target_height() { + let accounts = vec![("test1", 2, 0), ("test2", 1, 2), ("test3", 3, 3), ("test4", 2, 2)]; + let signers = accounts + .iter() + .map(|(account_id, _, _)| create_test_signer(account_id)) + .collect::>(); + let stakes = accounts + .into_iter() + .map(|(account_id, stake_this_epoch, stake_next_epoch)| ApprovalStake { + account_id: account_id.parse().unwrap(), + stake_this_epoch, + stake_next_epoch, + public_key: SecretKey::from_seed(KeyType::ED25519, account_id).public_key(), + }) + .map(|stake| (stake, false)) + .collect::>(); + let mut tracker = DoomslugApprovalsTrackersAtHeight::new(); + + let a1_1 = Approval::new(hash(&[1]), 1, 4, &signers[0]); + let a1_2 = Approval::new(hash(&[1]), 1, 4, &signers[1]); + let a1_3 = Approval::new(hash(&[1]), 1, 4, &signers[2]); + + let a2_1 = Approval::new(hash(&[3]), 3, 4, &signers[0]); + let a2_2 = Approval::new(hash(&[3]), 3, 4, &signers[1]); + let a2_3 = Approval::new(hash(&[3]), 3, 4, &signers[2]); + + // Process first approval, and then process it again and make sure it works + tracker.process_approval( + StaticClock::instant(), + &a1_1, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); + + assert_eq!( + tracker + .approval_trackers + .get(&ApprovalInner::Skip(1)) + .unwrap() + .approved_stake_this_epoch, + 2 + ); + + assert_eq!( + tracker + .approval_trackers + .get(&ApprovalInner::Skip(1)) + .unwrap() + .approved_stake_next_epoch, + 0 + ); + + tracker.process_approval( + StaticClock::instant(), + &a1_1, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); + + assert_eq!( + tracker + .approval_trackers + .get(&ApprovalInner::Skip(1)) + .unwrap() + .approved_stake_this_epoch, + 2 + ); + + assert_eq!( + tracker + .approval_trackers + .get(&ApprovalInner::Skip(1)) + .unwrap() + .approved_stake_next_epoch, + 0 + ); + + // Process the remaining two approvals on the first block + tracker.process_approval( + StaticClock::instant(), + &a1_2, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); + tracker.process_approval( + StaticClock::instant(), + &a1_3, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); + + assert_eq!( + tracker + .approval_trackers + .get(&ApprovalInner::Skip(1)) + .unwrap() + .approved_stake_this_epoch, + 6 + ); + + assert_eq!( + tracker + .approval_trackers + .get(&ApprovalInner::Skip(1)) + .unwrap() + .approved_stake_next_epoch, + 5 + ); + + // Process new approvals one by one, expect the approved and endorsed stake to slowly decrease + tracker.process_approval( + StaticClock::instant(), + &a2_1, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); + + assert_eq!( + tracker + .approval_trackers + .get(&ApprovalInner::Skip(1)) + .unwrap() + .approved_stake_this_epoch, + 4 + ); + + assert_eq!( + tracker + .approval_trackers + .get(&ApprovalInner::Skip(1)) + .unwrap() + .approved_stake_next_epoch, + 5 + ); + + tracker.process_approval( + StaticClock::instant(), + &a2_2, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); + + assert_eq!( + tracker + .approval_trackers + .get(&ApprovalInner::Skip(1)) + .unwrap() + .approved_stake_this_epoch, + 3 + ); + + assert_eq!( + tracker + .approval_trackers + .get(&ApprovalInner::Skip(1)) + .unwrap() + .approved_stake_next_epoch, + 3 + ); + + // As we update the last of the three approvals, the tracker for the first block should be completely removed + tracker.process_approval( + StaticClock::instant(), + &a2_3, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); + + assert!(tracker.approval_trackers.get(&ApprovalInner::Skip(1)).is_none()); + + // Check the approved and endorsed stake for the new block, and also ensure that processing one of the same approvals + // again works fine + + assert_eq!( + tracker + .approval_trackers + .get(&ApprovalInner::Endorsement(hash(&[3]))) + .unwrap() + .approved_stake_this_epoch, + 6 + ); + + assert_eq!( + tracker + .approval_trackers + .get(&ApprovalInner::Endorsement(hash(&[3]))) + .unwrap() + .approved_stake_next_epoch, + 5 + ); + + tracker.process_approval( + StaticClock::instant(), + &a2_3, + &stakes, + DoomslugThresholdMode::TwoThirds, + ); + + assert_eq!( + tracker + .approval_trackers + .get(&ApprovalInner::Endorsement(hash(&[3]))) + .unwrap() + .approved_stake_this_epoch, + 6 + ); + + assert_eq!( + tracker + .approval_trackers + .get(&ApprovalInner::Endorsement(hash(&[3]))) + .unwrap() + .approved_stake_next_epoch, + 5 + ); + } +} diff --git a/chain/chain/src/flat_storage_creator.rs b/chain/chain/src/flat_storage_creator.rs new file mode 100644 index 000000000..fed4bc0b3 --- /dev/null +++ b/chain/chain/src/flat_storage_creator.rs @@ -0,0 +1,492 @@ +//! Logic for creating flat storage in parallel to chain processing. +//! +//! The main struct responsible is `FlatStorageShardCreator`. +//! After its creation, `update_status` is called periodically, which executes some part of flat storage creation +//! depending on what the current status is: +//! `SavingDeltas`: checks if we moved chain final head forward enough to have all flat storage deltas written on disk. +//! `FetchingState`: spawns threads for fetching some range state parts, waits for receiving results, writes key-value +//! parts to flat storage column on disk and spawns threads for new range once current range is finished. +//! `CatchingUp`: moves flat storage head forward, so it may reach chain final head. +//! `Ready`: flat storage is created and it is up-to-date. + +use crate::types::RuntimeAdapter; +use crate::{ChainStore, ChainStoreAccess}; +use assert_matches::assert_matches; +use crossbeam_channel::{unbounded, Receiver, Sender}; +use unc_chain_primitives::Error; +use unc_epoch_manager::EpochManagerAdapter; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::state::FlatStateValue; +use unc_primitives::state_part::PartId; +use unc_primitives::types::{BlockHeight, StateRoot}; +use unc_store::flat::{ + store_helper, BlockInfo, FetchingStateStatus, FlatStateChanges, FlatStorageCreationMetrics, + FlatStorageCreationStatus, FlatStorageReadyStatus, FlatStorageStatus, NUM_PARTS_IN_ONE_STEP, + STATE_PART_MEMORY_LIMIT, +}; +use unc_store::Store; +use unc_store::{Trie, TrieDBStorage, TrieTraversalItem}; +use std::collections::HashMap; +use std::rc::Rc; +use std::sync::atomic::AtomicU64; +use std::sync::Arc; +use tracing::{debug, info}; + +/// If we launched a node with enabled flat storage but it doesn't have flat storage data on disk, we have to create it. +/// This struct is responsible for this process for the given shard. +/// See doc comment on [`FlatStorageCreationStatus`] for the details of the process. +pub struct FlatStorageShardCreator { + shard_uid: ShardUId, + /// Height on top of which this struct was created. + start_height: BlockHeight, + epoch_manager: Arc, + runtime: Arc, + /// Tracks number of state parts which are not fetched yet during a single step. + /// Stores Some(parts) if threads for fetching state were spawned and None otherwise. + remaining_state_parts: Option, + /// Used by threads which traverse state parts to tell that traversal is finished. + fetched_parts_sender: Sender, + /// Used by main thread to update the number of traversed state parts. + fetched_parts_receiver: Receiver, + metrics: FlatStorageCreationMetrics, +} + +impl FlatStorageShardCreator { + /// Maximal number of blocks which can be caught up during one step. + const CATCH_UP_BLOCKS: usize = 50; + + pub fn new( + shard_uid: ShardUId, + start_height: BlockHeight, + epoch_manager: Arc, + runtime: Arc, + ) -> Self { + let (fetched_parts_sender, fetched_parts_receiver) = unbounded(); + Self { + shard_uid, + start_height, + epoch_manager, + runtime, + remaining_state_parts: None, + fetched_parts_sender, + fetched_parts_receiver, + metrics: FlatStorageCreationMetrics::new(shard_uid.shard_id()), + } + } + + #[allow(unused)] + fn nibbles_to_hex(key_nibbles: &[u8]) -> String { + let path_prefix = match key_nibbles.last() { + Some(16) => &key_nibbles[..key_nibbles.len() - 1], + _ => &key_nibbles, + }; + path_prefix + .iter() + .map(|&n| char::from_digit(n as u32, 16).expect("nibble should be <16")) + .collect() + } + + /// Fetch state part, write all state items to flat storage and send the number of items to the given channel. + fn fetch_state_part( + store: Store, + shard_uid: ShardUId, + state_root: StateRoot, + part_id: PartId, + progress: Arc, + result_sender: Sender, + ) { + let trie_storage = TrieDBStorage::new(store.clone(), shard_uid); + let trie = Trie::new(Rc::new(trie_storage), state_root, None); + let path_begin = trie.find_state_part_boundary(part_id.idx, part_id.total).unwrap(); + let path_end = trie.find_state_part_boundary(part_id.idx + 1, part_id.total).unwrap(); + let hex_path_begin = Self::nibbles_to_hex(&path_begin); + debug!(target: "store", "Preload state part from {hex_path_begin}"); + let mut trie_iter = trie.iter().unwrap(); + + let mut store_update = store.store_update(); + let mut num_items = 0; + for TrieTraversalItem { hash, key } in + trie_iter.visit_nodes_interval(&path_begin, &path_end).unwrap() + { + if let Some(key) = key { + let value = trie.retrieve_value(&hash).unwrap(); + store_helper::set_flat_state_value( + &mut store_update, + shard_uid, + key, + Some(FlatStateValue::value_ref(&value)), + ); + num_items += 1; + } + } + store_update.commit().unwrap(); + + let processed_parts = progress.fetch_add(1, std::sync::atomic::Ordering::Relaxed) + 1; + + debug!(target: "store", + "Preload subtrie at {hex_path_begin} done, \ + loaded {num_items} state items, \ + proccessed parts: {processed_parts}" + ); + + if let Err(e) = result_sender.send(num_items) { + // Log error with `eprintln` because `tracing` stops working after + // SIGTERM. + eprintln!( + "During flat storage creation, state part sender channel \ + was disconnected: {e}. Results of fetching state part \ + {part_id:?} for shard {shard_uid} will not be recorded \ + and flat storage creation will stall. Please restart the node." + ); + } + } + + /// Checks current flat storage creation status, execute work related to it and possibly switch to next status. + /// Creates flat storage when all intermediate steps are finished. + /// Returns boolean indicating if flat storage was created. + pub fn update_status( + &mut self, + chain_store: &ChainStore, + thread_pool: &rayon::ThreadPool, + ) -> Result { + let shard_id = self.shard_uid.shard_id(); + let current_status = + store_helper::get_flat_storage_status(chain_store.store(), self.shard_uid) + .expect("failed to read flat storage status"); + self.metrics.set_status(¤t_status); + match ¤t_status { + FlatStorageStatus::Empty => { + let mut store_update = chain_store.store().store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + self.shard_uid, + FlatStorageStatus::Creation(FlatStorageCreationStatus::SavingDeltas), + ); + store_update.commit()?; + } + FlatStorageStatus::Creation(FlatStorageCreationStatus::SavingDeltas) => { + let final_head = chain_store.final_head()?; + let final_height = final_head.height; + + if final_height > self.start_height { + // If it holds, deltas for all blocks after final head are saved to disk, because they have bigger + // heights than one on which we launched a node. Check that it is true: + for height in final_height + 1..=chain_store.head()?.height { + // We skip heights for which there are no blocks, because certain heights can be skipped. + for (_, hashes) in + chain_store.get_all_block_hashes_by_height(height)?.iter() + { + for hash in hashes { + debug!(target: "store", %shard_id, %height, %hash, "Checking delta existence"); + assert_matches!( + store_helper::get_delta_changes( + chain_store.store(), + self.shard_uid, + *hash + ), + Ok(Some(_)) + ); + } + } + } + + // We continue saving deltas, and also start fetching state. + let block_hash = final_head.last_block_hash; + let store = self.runtime.store().clone(); + let epoch_id = self.epoch_manager.get_epoch_id(&block_hash)?; + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, &epoch_id)?; + let trie_storage = TrieDBStorage::new(store, shard_uid); + let state_root = + *chain_store.get_chunk_extra(&block_hash, &shard_uid)?.state_root(); + let trie = Trie::new(Rc::new(trie_storage), state_root, None); + let root_node = trie.retrieve_root_node().unwrap(); + let num_state_parts = + root_node.memory_usage / STATE_PART_MEMORY_LIMIT.as_u64() + 1; + let status = FetchingStateStatus { + block_hash, + part_id: 0, + num_parts_in_step: NUM_PARTS_IN_ONE_STEP, + num_parts: num_state_parts, + }; + info!(target: "store", %shard_id, %final_height, ?status, "Switching status to fetching state"); + + let mut store_update = chain_store.store().store_update(); + self.metrics.set_flat_head_height(final_head.height); + store_helper::set_flat_storage_status( + &mut store_update, + self.shard_uid, + FlatStorageStatus::Creation(FlatStorageCreationStatus::FetchingState( + status, + )), + ); + store_update.commit()?; + } + } + FlatStorageStatus::Creation(FlatStorageCreationStatus::FetchingState( + fetching_state_status, + )) => { + let store = self.runtime.store().clone(); + let block_hash = fetching_state_status.block_hash; + let start_part_id = fetching_state_status.part_id; + let num_parts_in_step = fetching_state_status.num_parts_in_step; + let num_parts = fetching_state_status.num_parts; + let next_start_part_id = num_parts.min(start_part_id + num_parts_in_step); + self.metrics.set_remaining_state_parts(num_parts - start_part_id); + + match self.remaining_state_parts { + None => { + // We need to spawn threads to fetch state parts and fill flat storage data. + let epoch_id = self.epoch_manager.get_epoch_id(&block_hash)?; + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, &epoch_id)?; + let state_root = + *chain_store.get_chunk_extra(&block_hash, &shard_uid)?.state_root(); + let progress = Arc::new(std::sync::atomic::AtomicU64::new(0)); + debug!( + target: "store", %shard_id, %block_hash, %start_part_id, %next_start_part_id, %num_parts, + "Spawning threads to fetch state parts for flat storage" + ); + + for part_id in start_part_id..next_start_part_id { + let inner_store = store.clone(); + let inner_progress = progress.clone(); + let inner_sender = self.fetched_parts_sender.clone(); + let inner_threads_used = self.metrics.threads_used(); + thread_pool.spawn(move || { + inner_threads_used.inc(); + Self::fetch_state_part( + inner_store, + shard_uid, + state_root, + PartId::new(part_id, num_parts), + inner_progress, + inner_sender, + ); + inner_threads_used.dec(); + }) + } + + self.remaining_state_parts = Some(next_start_part_id - start_part_id); + } + Some(state_parts) if state_parts > 0 => { + // If not all state parts were fetched, try receiving new results. + let mut updated_state_parts = state_parts; + while let Ok(num_items) = self.fetched_parts_receiver.try_recv() { + updated_state_parts -= 1; + self.metrics.inc_fetched_state(num_items); + } + self.remaining_state_parts = Some(updated_state_parts); + } + Some(_) => { + // Mark that we don't wait for new state parts. + self.remaining_state_parts = None; + + let mut store_update = chain_store.store().store_update(); + if next_start_part_id < num_parts { + // If there are still remaining state parts, switch status to the new range of state parts. + // We will spawn new rayon tasks on the next status update. + let new_status = FetchingStateStatus { + block_hash, + part_id: next_start_part_id, + num_parts_in_step, + num_parts, + }; + debug!(target: "chain", %shard_id, %block_hash, ?new_status); + store_helper::set_flat_storage_status( + &mut store_update, + self.shard_uid, + FlatStorageStatus::Creation( + FlatStorageCreationStatus::FetchingState(new_status), + ), + ); + } else { + // If all parts were fetched, we can start catchup. + info!(target: "chain", %shard_id, %block_hash, "Finished fetching state"); + self.metrics.set_remaining_state_parts(0); + store_helper::remove_delta( + &mut store_update, + self.shard_uid, + block_hash, + ); + store_helper::set_flat_storage_status( + &mut store_update, + self.shard_uid, + FlatStorageStatus::Creation(FlatStorageCreationStatus::CatchingUp( + block_hash, + )), + ); + } + store_update.commit()?; + } + } + } + FlatStorageStatus::Creation(FlatStorageCreationStatus::CatchingUp(old_flat_head)) => { + let store = self.runtime.store(); + let mut flat_head = *old_flat_head; + let chain_final_head = chain_store.final_head()?; + let mut merged_changes = FlatStateChanges::default(); + let mut store_update = self.runtime.store().store_update(); + + // Merge up to 50 deltas of the next blocks until we reach chain final head. + // TODO: consider merging 10 deltas at once to limit memory usage + for _ in 0..Self::CATCH_UP_BLOCKS { + let height = chain_store.get_block_height(&flat_head).unwrap(); + if height > chain_final_head.height { + panic!("New flat head moved too far: new head = {flat_head}, height = {height}, final block height = {}", chain_final_head.height); + } + // Stop if we reached chain final head. + if flat_head == chain_final_head.last_block_hash { + break; + } + flat_head = chain_store.get_next_block_hash(&flat_head).unwrap(); + let changes = store_helper::get_delta_changes(store, self.shard_uid, flat_head) + .unwrap() + .unwrap(); + merged_changes.merge(changes); + store_helper::remove_delta(&mut store_update, self.shard_uid, flat_head); + } + + if (old_flat_head != &flat_head) || (flat_head == chain_final_head.last_block_hash) + { + // If flat head changes, save all changes to store. + let epoch_id = self.epoch_manager.get_epoch_id(&flat_head)?; + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, &epoch_id)?; + let old_height = chain_store.get_block_height(&old_flat_head).unwrap(); + let flat_head_block_header = chain_store.get_block_header(&flat_head).unwrap(); + let height = flat_head_block_header.height(); + debug!(target: "chain", %shard_id, %old_flat_head, %old_height, %flat_head, %height, "Catching up flat head"); + self.metrics.set_flat_head_height(height); + merged_changes.apply_to_flat_state(&mut store_update, shard_uid); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Creation(FlatStorageCreationStatus::CatchingUp( + flat_head, + )), + ); + store_update.commit()?; + + // If we reached chain final head, we can finish catchup and finally create flat storage. + if flat_head == chain_final_head.last_block_hash { + // GC deltas from forks which could have appeared on chain during catchup. + // Assuming that flat storage creation finishes in < 2 days, all deltas metadata cannot occupy + // more than 2 * (Blocks per day = 48 * 60 * 60) * (BlockInfo size = 72) ~= 12.4 MB. + let mut store_update = self.runtime.store().store_update(); + let deltas_metadata = store_helper::get_all_deltas_metadata(&store, shard_uid) + .unwrap_or_else(|_| { + panic!("Cannot read flat state deltas metadata for shard {shard_id} from storage") + }); + let mut gc_count = 0; + for delta_metadata in deltas_metadata { + if delta_metadata.block.height <= chain_final_head.height { + store_helper::remove_delta( + &mut store_update, + self.shard_uid, + delta_metadata.block.hash, + ); + gc_count += 1; + } + } + + // If we reached chain final head, we can finish catchup and finally create flat storage. + store_helper::set_flat_storage_status( + &mut store_update, + self.shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { + flat_head: BlockInfo { + hash: flat_head, + prev_hash: *flat_head_block_header.prev_hash(), + height, + }, + }), + ); + store_update.commit()?; + info!(target: "chain", %shard_id, %flat_head, %height, "Garbage collected {gc_count} deltas"); + self.runtime + .get_flat_storage_manager() + .create_flat_storage_for_shard(shard_uid) + .unwrap(); + info!(target: "chain", %shard_id, %flat_head, %height, "Flat storage creation done"); + } + } + } + FlatStorageStatus::Ready(_) => return Ok(true), + FlatStorageStatus::Disabled => { + panic!("initiated flat storage creation for shard {shard_id} while it is disabled"); + } + }; + Ok(false) + } +} + +/// Creates flat storages for all shards. +pub struct FlatStorageCreator { + pub shard_creators: HashMap, + /// Used to spawn threads for traversing state parts. + pub pool: rayon::ThreadPool, +} + +impl FlatStorageCreator { + /// For each of tracked shards, either creates flat storage if it is already stored on DB, + /// or starts migration to flat storage which updates DB in background and creates flat storage afterwards. + pub fn new( + epoch_manager: Arc, + runtime: Arc, + chain_store: &ChainStore, + num_threads: usize, + ) -> Result, Error> { + let chain_head = chain_store.head()?; + let shard_ids = epoch_manager.shard_ids(&chain_head.epoch_id)?; + let mut shard_creators: HashMap = HashMap::new(); + let mut creation_needed = false; + let flat_storage_manager = runtime.get_flat_storage_manager(); + // Create flat storage for all shards. + // TODO(nikurt): Choose which shards need to open the flat storage. + for shard_id in shard_ids { + // The node applies transactions from the shards it cares about this and the next epoch. + let shard_uid = epoch_manager.shard_id_to_uid(shard_id, &chain_head.epoch_id)?; + let status = flat_storage_manager.get_flat_storage_status(shard_uid); + + match status { + FlatStorageStatus::Ready(_) => { + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + } + FlatStorageStatus::Empty | FlatStorageStatus::Creation(_) => { + creation_needed = true; + shard_creators.insert( + shard_uid, + FlatStorageShardCreator::new( + shard_uid, + chain_head.height, + epoch_manager.clone(), + runtime.clone(), + ), + ); + } + FlatStorageStatus::Disabled => {} + } + } + + let flat_storage_creator = if creation_needed { + Some(Self { + shard_creators, + pool: rayon::ThreadPoolBuilder::new().num_threads(num_threads).build().unwrap(), + }) + } else { + None + }; + Ok(flat_storage_creator) + } + + /// Updates statuses of underlying flat storage creation processes. Returns boolean + /// indicating if all flat storages are created. + pub fn update_status(&mut self, chain_store: &ChainStore) -> Result { + // TODO (#7327): If resharding happens, we may want to throw an error here. + // TODO (#7327): If flat storage is created, the creator probably should be removed. + + let mut all_created = true; + for shard_creator in self.shard_creators.values_mut() { + all_created &= shard_creator.update_status(chain_store, &self.pool)?; + } + Ok(all_created) + } +} diff --git a/chain/chain/src/garbage_collection.rs b/chain/chain/src/garbage_collection.rs new file mode 100644 index 000000000..40ab7a380 --- /dev/null +++ b/chain/chain/src/garbage_collection.rs @@ -0,0 +1,1011 @@ +use std::collections::HashMap; +use std::sync::Arc; +use std::{fmt, io}; + +use unc_chain_configs::GCConfig; +use unc_chain_primitives::Error; +use unc_epoch_manager::EpochManagerAdapter; +use unc_primitives::block::Block; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::get_block_shard_uid; +use unc_primitives::state_sync::{StateHeaderKey, StatePartKey}; +use unc_primitives::types::{BlockHeight, BlockHeightDelta, EpochId, NumBlocks, ShardId}; +use unc_primitives::utils::{get_block_shard_id, get_outcome_id_block_hash, index_to_bytes}; +use unc_store::flat::store_helper; +use unc_store::{DBCol, KeyForStateChanges, ShardTries, ShardUId}; + +use crate::types::RuntimeAdapter; +use crate::{metrics, Chain, ChainStoreAccess, ChainStoreUpdate}; + +#[derive(Clone)] +pub enum GCMode { + Fork(ShardTries), + Canonical(ShardTries), + StateSync { clear_block_info: bool }, +} + +impl fmt::Debug for GCMode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + GCMode::Fork(_) => write!(f, "GCMode::Fork"), + GCMode::Canonical(_) => write!(f, "GCMode::Canonical"), + GCMode::StateSync { .. } => write!(f, "GCMode::StateSync"), + } + } +} + +impl Chain { + // GC CONTRACT + // === + // + // Prerequisites, guaranteed by the System: + // 1. Genesis block is available and should not be removed by GC. + // 2. No block in storage except Genesis has height lower or equal to `genesis_height`. + // 3. There is known lowest block height (Tail) came from Genesis or State Sync. + // a. Tail is always on the Canonical Chain. + // b. Only one Tail exists. + // c. Tail's height is higher than or equal to `genesis_height`, + // 4. There is a known highest block height (Head). + // a. Head is always on the Canonical Chain. + // 5. All blocks in the storage have heights in range [Tail; Head]. + // a. All forks end up on height of Head or lower. + // 6. If block A is ancestor of block B, height of A is strictly less then height of B. + // 7. (Property 1). A block with the lowest height among all the blocks at which the fork has started, + // i.e. all the blocks with the outgoing degree 2 or more, + // has the least height among all blocks on the fork. + // 8. (Property 2). The oldest block where the fork happened is never affected + // by Canonical Chain Switching and always stays on Canonical Chain. + // + // Overall: + // 1. GC procedure is handled by `clear_data()` function. + // 2. `clear_data()` runs GC process for all blocks from the Tail to GC Stop Height provided by Epoch Manager. + // 3. `clear_data()` executes separately: + // a. Forks Clearing runs for each height from Tail up to GC Stop Height. + // b. Canonical Chain Clearing from (Tail + 1) up to GC Stop Height. + // 4. Before actual clearing is started, Block Reference Map should be built. + // 5. `clear_data()` executes every time when block at new height is added. + // 6. In case of State Sync, State Sync Clearing happens. + // + // Forks Clearing: + // 1. Any fork which ends up on height `height` INCLUSIVELY and earlier will be completely deleted + // from the Store with all its ancestors up to the ancestor block where fork is happened + // EXCLUDING the ancestor block where fork is happened. + // 2. The oldest ancestor block always remains on the Canonical Chain by property 2. + // 3. All forks which end up on height `height + 1` and further are protected from deletion and + // no their ancestor will be deleted (even with lowest heights). + // 4. `clear_forks_data()` handles forks clearing for fixed height `height`. + // + // Canonical Chain Clearing: + // 1. Blocks on the Canonical Chain with the only descendant (if no forks started from them) + // are unlocked for Canonical Chain Clearing. + // 2. If Forks Clearing ended up on the Canonical Chain, the block may be unlocked + // for the Canonical Chain Clearing. There is no other reason to unlock the block exists. + // 3. All the unlocked blocks will be completely deleted + // from the Tail up to GC Stop Height EXCLUSIVELY. + // 4. (Property 3, GC invariant). Tail can be shifted safely to the height of the + // earliest existing block. There is always only one Tail (based on property 1) + // and it's always on the Canonical Chain (based on property 2). + // + // Example: + // + // height: 101 102 103 104 + // --------[A]---[B]---[C]---[D] + // \ \ + // \ \---[E] + // \ + // \-[F]---[G] + // + // 1. Let's define clearing height = 102. It this case fork A-F-G is protected from deletion + // because of G which is on height 103. Nothing will be deleted. + // 2. Let's define clearing height = 103. It this case Fork Clearing will be executed for A + // to delete blocks G and F, then Fork Clearing will be executed for B to delete block E. + // Then Canonical Chain Clearing will delete blocks A and B as unlocked. + // Block C is the only block of height 103 remains on the Canonical Chain (invariant). + // + // State Sync Clearing: + // 1. Executing State Sync means that no data in the storage is useful for block processing + // and should be removed completely. + // 2. The Tail should be set to the block preceding Sync Block. + // 3. All the data preceding new Tail is deleted in State Sync Clearing + // and the Trie is updated with having only Genesis data. + // 4. State Sync Clearing happens in `reset_data_pre_state_sync()`. + // + pub fn clear_data(&mut self, tries: ShardTries, gc_config: &GCConfig) -> Result<(), Error> { + let _span = tracing::debug_span!(target: "garbage_collection", "clear_data").entered(); + + let head = self.chain_store().head()?; + let tail = self.chain_store().tail()?; + let gc_stop_height = self.runtime_adapter.get_gc_stop_height(&head.last_block_hash); + if gc_stop_height > head.height { + return Err(Error::GCError("gc_stop_height cannot be larger than head.height".into())); + } + let prev_epoch_id = self.get_block_header(&head.prev_block_hash)?.epoch_id().clone(); + let epoch_change = prev_epoch_id != head.epoch_id; + let mut fork_tail = self.chain_store().fork_tail()?; + metrics::TAIL_HEIGHT.set(tail as i64); + metrics::FORK_TAIL_HEIGHT.set(fork_tail as i64); + metrics::CHUNK_TAIL_HEIGHT.set(self.chain_store().chunk_tail()? as i64); + metrics::GC_STOP_HEIGHT.set(gc_stop_height as i64); + if epoch_change && fork_tail < gc_stop_height { + // if head doesn't change on the epoch boundary, we may update fork tail several times + // but that is fine since it doesn't affect correctness and also we limit the number of + // heights that fork cleaning goes through so it doesn't slow down client either. + let mut chain_store_update = self.mut_chain_store().store_update(); + chain_store_update.update_fork_tail(gc_stop_height); + chain_store_update.commit()?; + fork_tail = gc_stop_height; + } + let mut gc_blocks_remaining = gc_config.gc_blocks_limit; + + // Forks Cleaning + let gc_fork_clean_step = gc_config.gc_fork_clean_step; + let stop_height = tail.max(fork_tail.saturating_sub(gc_fork_clean_step)); + for height in (stop_height..fork_tail).rev() { + self.clear_forks_data(tries.clone(), height, &mut gc_blocks_remaining)?; + if gc_blocks_remaining == 0 { + return Ok(()); + } + let mut chain_store_update = self.mut_chain_store().store_update(); + chain_store_update.update_fork_tail(height); + chain_store_update.commit()?; + } + + // Canonical Chain Clearing + for height in tail + 1..gc_stop_height { + if gc_blocks_remaining == 0 { + return Ok(()); + } + let blocks_current_height = self + .chain_store() + .get_all_block_hashes_by_height(height)? + .values() + .flatten() + .cloned() + .collect::>(); + let epoch_manager = self.epoch_manager.clone(); + let runtime = self.runtime_adapter.clone(); + let mut chain_store_update = self.mut_chain_store().store_update(); + if let Some(block_hash) = blocks_current_height.first() { + let prev_hash = *chain_store_update.get_block_header(block_hash)?.prev_hash(); + let prev_block_refcount = chain_store_update.get_block_refcount(&prev_hash)?; + if prev_block_refcount > 1 { + // Block of `prev_hash` starts a Fork, stopping + break; + } else if prev_block_refcount == 1 { + debug_assert_eq!(blocks_current_height.len(), 1); + chain_store_update.clear_block_data( + epoch_manager.as_ref(), + *block_hash, + GCMode::Canonical(tries.clone()), + )?; + chain_store_update.clear_resharding_data( + runtime.as_ref(), + epoch_manager.as_ref(), + *block_hash, + )?; + gc_blocks_remaining -= 1; + } else { + return Err(Error::GCError( + "block on canonical chain shouldn't have refcount 0".into(), + )); + } + } + chain_store_update.update_tail(height)?; + chain_store_update.commit()?; + } + Ok(()) + } + + /// Garbage collect data which archival node doesn’t need to keep. + /// + /// Normally, archival nodes keep all the data from the genesis block and + /// don’t run garbage collection. On the other hand, for better performance + /// the storage contains some data duplication, i.e. values in some of the + /// columns can be recomputed from data in different columns. To save on + /// storage, archival nodes do garbage collect that data. + /// + /// `gc_height_limit` limits how many heights will the function process. + pub fn clear_archive_data(&mut self, gc_height_limit: BlockHeightDelta) -> Result<(), Error> { + let _span = tracing::debug_span!(target: "chain", "clear_archive_data").entered(); + + let head = self.chain_store().head()?; + let gc_stop_height = self.runtime_adapter.get_gc_stop_height(&head.last_block_hash); + if gc_stop_height > head.height { + return Err(Error::GCError("gc_stop_height cannot be larger than head.height".into())); + } + + let mut chain_store_update = self.mut_chain_store().store_update(); + chain_store_update.clear_redundant_chunk_data(gc_stop_height, gc_height_limit)?; + metrics::CHUNK_TAIL_HEIGHT.set(chain_store_update.chunk_tail()? as i64); + metrics::GC_STOP_HEIGHT.set(gc_stop_height as i64); + chain_store_update.commit() + } + + fn clear_forks_data( + &mut self, + tries: ShardTries, + height: BlockHeight, + gc_blocks_remaining: &mut NumBlocks, + ) -> Result<(), Error> { + let blocks_current_height = self + .chain_store() + .get_all_block_hashes_by_height(height)? + .values() + .flatten() + .cloned() + .collect::>(); + for block_hash in blocks_current_height.iter() { + let mut current_hash = *block_hash; + loop { + if *gc_blocks_remaining == 0 { + return Ok(()); + } + // Block `block_hash` is not on the Canonical Chain + // because shorter chain cannot be Canonical one + // and it may be safely deleted + // and all its ancestors while there are no other sibling blocks rely on it. + let epoch_manager = self.epoch_manager.clone(); + let mut chain_store_update = self.mut_chain_store().store_update(); + if chain_store_update.get_block_refcount(¤t_hash)? == 0 { + let prev_hash = + *chain_store_update.get_block_header(¤t_hash)?.prev_hash(); + + // It's safe to call `clear_block_data` for prev data because it clears fork only here + chain_store_update.clear_block_data( + epoch_manager.as_ref(), + current_hash, + GCMode::Fork(tries.clone()), + )?; + chain_store_update.commit()?; + *gc_blocks_remaining -= 1; + + current_hash = prev_hash; + } else { + // Block of `current_hash` is an ancestor for some other blocks, stopping + break; + } + } + } + + Ok(()) + } + + pub fn reset_data_pre_state_sync(&mut self, sync_hash: CryptoHash) -> Result<(), Error> { + let _span = tracing::debug_span!(target: "sync", "reset_data_pre_state_sync").entered(); + let head = self.head()?; + // Get header we were syncing into. + let header = self.get_block_header(&sync_hash)?; + let prev_hash = *header.prev_hash(); + let sync_height = header.height(); + let gc_height = std::cmp::min(head.height + 1, sync_height); + + // GC all the data from current tail up to `gc_height`. In case tail points to a height where + // there is no block, we need to make sure that the last block before tail is cleaned. + let tail = self.chain_store().tail()?; + let mut tail_prev_block_cleaned = false; + for height in tail..gc_height { + let blocks_current_height = self + .chain_store() + .get_all_block_hashes_by_height(height)? + .values() + .flatten() + .cloned() + .collect::>(); + for block_hash in blocks_current_height { + let epoch_manager = self.epoch_manager.clone(); + let mut chain_store_update = self.mut_chain_store().store_update(); + if !tail_prev_block_cleaned { + let prev_block_hash = + *chain_store_update.get_block_header(&block_hash)?.prev_hash(); + if chain_store_update.get_block(&prev_block_hash).is_ok() { + chain_store_update.clear_block_data( + epoch_manager.as_ref(), + prev_block_hash, + GCMode::StateSync { clear_block_info: true }, + )?; + } + tail_prev_block_cleaned = true; + } + chain_store_update.clear_block_data( + epoch_manager.as_ref(), + block_hash, + GCMode::StateSync { clear_block_info: block_hash != prev_hash }, + )?; + chain_store_update.commit()?; + } + } + + // Clear Chunks data + let mut chain_store_update = self.mut_chain_store().store_update(); + // The largest height of chunk we have in storage is head.height + 1 + let chunk_height = std::cmp::min(head.height + 2, sync_height); + chain_store_update.clear_chunk_data_and_headers(chunk_height)?; + chain_store_update.commit()?; + + // clear all trie data + + let tries = self.runtime_adapter.get_tries(); + let mut chain_store_update = self.mut_chain_store().store_update(); + let mut store_update = tries.store_update(); + store_update.delete_all(DBCol::State); + chain_store_update.merge(store_update); + + // The reason to reset tail here is not to allow Tail be greater than Head + chain_store_update.reset_tail(); + chain_store_update.commit()?; + Ok(()) + } +} + +impl<'a> ChainStoreUpdate<'a> { + fn clear_header_data_for_heights( + &mut self, + start: BlockHeight, + end: BlockHeight, + ) -> Result<(), Error> { + for height in start..=end { + let header_hashes = self.chain_store().get_all_header_hashes_by_height(height)?; + for header_hash in header_hashes { + // Delete header_hash-indexed data: block header + let mut store_update = self.store().store_update(); + let key: &[u8] = header_hash.as_bytes(); + store_update.delete(DBCol::BlockHeader, key); + self.chain_store().headers.pop(key); + self.merge(store_update); + } + let key = index_to_bytes(height); + self.gc_col(DBCol::HeaderHashesByHeight, &key); + } + Ok(()) + } + + fn clear_chunk_data_and_headers(&mut self, min_chunk_height: BlockHeight) -> Result<(), Error> { + let chunk_tail = self.chunk_tail()?; + for height in chunk_tail..min_chunk_height { + let chunk_hashes = self.chain_store().get_all_chunk_hashes_by_height(height)?; + for chunk_hash in chunk_hashes { + // 1. Delete chunk-related data + let chunk = self.get_chunk(&chunk_hash)?.clone(); + debug_assert_eq!(chunk.cloned_header().height_created(), height); + for transaction in chunk.transactions() { + self.gc_col(DBCol::Transactions, transaction.get_hash().as_bytes()); + } + for receipt in chunk.prev_outgoing_receipts() { + self.gc_col(DBCol::Receipts, receipt.get_hash().as_bytes()); + } + + // 2. Delete chunk_hash-indexed data + let chunk_hash = chunk_hash.as_bytes(); + self.gc_col(DBCol::Chunks, chunk_hash); + self.gc_col(DBCol::PartialChunks, chunk_hash); + self.gc_col(DBCol::InvalidChunks, chunk_hash); + } + + let header_hashes = self.chain_store().get_all_header_hashes_by_height(height)?; + for _header_hash in header_hashes { + // 3. Delete header_hash-indexed data + // TODO #3488: enable + //self.gc_col(DBCol::BlockHeader, header_hash.as_bytes()); + } + + // 4. Delete chunks_tail-related data + let key = index_to_bytes(height); + self.gc_col(DBCol::ChunkHashesByHeight, &key); + self.gc_col(DBCol::HeaderHashesByHeight, &key); + } + self.update_chunk_tail(min_chunk_height); + Ok(()) + } + + /// Clears chunk data which can be computed from other data in the storage. + /// + /// We are storing PartialEncodedChunk objects in the DBCol::PartialChunks in + /// the storage. However, those objects can be computed from data in + /// DBCol::Chunks and as such are redundant. For performance reasons we want to + /// keep that data when operating at head of the chain but the data can be + /// safely removed from archival storage. + /// + /// `gc_stop_height` indicates height starting from which no data should be + /// garbage collected. Roughly speaking this represents start of the ‘hot’ + /// data that we want to keep. + /// + /// `gt_height_limit` indicates limit of how many non-empty heights to + /// process. This limit means that the method may stop garbage collection + /// before reaching `gc_stop_height`. + fn clear_redundant_chunk_data( + &mut self, + gc_stop_height: BlockHeight, + gc_height_limit: BlockHeightDelta, + ) -> Result<(), Error> { + let mut height = self.chunk_tail()?; + let mut remaining = gc_height_limit; + while height < gc_stop_height && remaining > 0 { + let chunk_hashes = self.chain_store().get_all_chunk_hashes_by_height(height)?; + height += 1; + if !chunk_hashes.is_empty() { + remaining -= 1; + for chunk_hash in chunk_hashes { + let chunk_hash = chunk_hash.as_bytes(); + self.gc_col(DBCol::PartialChunks, chunk_hash); + // Data in DBCol::InvalidChunks isn’t technically redundant (it + // cannot be calculated from other data) but it is data we + // don’t need for anything so it can be deleted as well. + self.gc_col(DBCol::InvalidChunks, chunk_hash); + } + } + } + self.update_chunk_tail(height); + Ok(()) + } + + fn get_shard_uids_to_gc( + &mut self, + epoch_manager: &dyn EpochManagerAdapter, + block_hash: &CryptoHash, + ) -> Vec { + let block_header = self.get_block_header(block_hash).expect("block header must exist"); + let shard_layout = + epoch_manager.get_shard_layout(block_header.epoch_id()).expect("epoch info must exist"); + // gc shards in this epoch + let mut shard_uids_to_gc: Vec<_> = shard_layout.shard_uids().collect(); + // gc shards in the shard layout in the next epoch if shards will change in the next epoch + // Suppose shard changes at epoch T, we need to garbage collect the new shard layout + // from the last block in epoch T-2 to the last block in epoch T-1 + // Because we need to gc the last block in epoch T-2, we can't simply use + // block_header.epoch_id() as next_epoch_id + let next_epoch_id = block_header.next_epoch_id(); + let next_shard_layout = + epoch_manager.get_shard_layout(next_epoch_id).expect("epoch info must exist"); + if shard_layout != next_shard_layout { + shard_uids_to_gc.extend(next_shard_layout.shard_uids()); + } + shard_uids_to_gc + } + + /// GC trie state and flat state data after a resharding event + /// Most of the work happens on the last block of the epoch when resharding is COMPLETED + /// During GC, when we detect a change in shard layout, we can clear off all entries from + /// the parent shards + /// TODO(resharding): Need to clean remaining columns after resharding + fn clear_resharding_data( + &mut self, + runtime: &dyn RuntimeAdapter, + epoch_manager: &dyn EpochManagerAdapter, + block_hash: CryptoHash, + ) -> Result<(), Error> { + // Need to check if this is the last block of the epoch where resharding is completed + // which means shard layout changed in the previous epoch + if !epoch_manager.is_last_block_in_finished_epoch(&block_hash)? { + return Ok(()); + } + + // Since this code is related to GC, we need to be careful about accessing block_infos. Note + // that the BlockInfo exists for the current block_hash as it's not been GC'd yet. + // However, we need to use the block header to get the epoch_id and shard_layout for + // first_block_epoch_header and last_block_prev_epoch_hash as BlockInfo for these blocks is + // already GC'd while BlockHeader isn't GC'd. + let block_info = epoch_manager.get_block_info(&block_hash)?; + let first_block_epoch_hash = block_info.epoch_first_block(); + if first_block_epoch_hash == &CryptoHash::default() { + return Ok(()); + } + let first_block_epoch_header = self.get_block_header(first_block_epoch_hash)?; + let last_block_prev_epoch_header = + self.get_block_header(first_block_epoch_header.prev_hash())?; + + let epoch_id = first_block_epoch_header.epoch_id(); + let shard_layout = epoch_manager.get_shard_layout(epoch_id)?; + let prev_epoch_id = last_block_prev_epoch_header.epoch_id(); + let prev_shard_layout = epoch_manager.get_shard_layout(prev_epoch_id)?; + if shard_layout == prev_shard_layout { + return Ok(()); + } + + // Now we can proceed to removing the trie state and flat state + let mut store_update = self.store().store_update(); + for shard_uid in prev_shard_layout.shard_uids() { + tracing::info!(target: "garbage_collection", ?block_hash, ?shard_uid, "GC resharding"); + runtime.get_tries().delete_trie_for_shard(shard_uid, &mut store_update); + runtime + .get_flat_storage_manager() + .remove_flat_storage_for_shard(shard_uid, &mut store_update)?; + } + + self.merge(store_update); + Ok(()) + } + + // Clearing block data of `block_hash`, if on a fork. + // Clearing block data of `block_hash.prev`, if on the Canonical Chain. + pub fn clear_block_data( + &mut self, + epoch_manager: &dyn EpochManagerAdapter, + mut block_hash: CryptoHash, + gc_mode: GCMode, + ) -> Result<(), Error> { + let mut store_update = self.store().store_update(); + + tracing::info!(target: "garbage_collection", ?gc_mode, ?block_hash, "GC block_hash"); + + // 1. Apply revert insertions or deletions from DBCol::TrieChanges for Trie + { + let shard_uids_to_gc: Vec<_> = self.get_shard_uids_to_gc(epoch_manager, &block_hash); + match gc_mode.clone() { + GCMode::Fork(tries) => { + // If the block is on a fork, we delete the state that's the result of applying this block + for shard_uid in shard_uids_to_gc { + let trie_changes = self.store().get_ser( + DBCol::TrieChanges, + &get_block_shard_uid(&block_hash, &shard_uid), + )?; + if let Some(trie_changes) = trie_changes { + tries.revert_insertions(&trie_changes, shard_uid, &mut store_update); + self.gc_col( + DBCol::TrieChanges, + &get_block_shard_uid(&block_hash, &shard_uid), + ); + } + } + } + GCMode::Canonical(tries) => { + // If the block is on canonical chain, we delete the state that's before applying this block + for shard_uid in shard_uids_to_gc { + let trie_changes = self.store().get_ser( + DBCol::TrieChanges, + &get_block_shard_uid(&block_hash, &shard_uid), + )?; + if let Some(trie_changes) = trie_changes { + tries.apply_deletions(&trie_changes, shard_uid, &mut store_update); + self.gc_col( + DBCol::TrieChanges, + &get_block_shard_uid(&block_hash, &shard_uid), + ); + } + } + // Set `block_hash` on previous one + block_hash = *self.get_block_header(&block_hash)?.prev_hash(); + } + GCMode::StateSync { .. } => { + // Not apply the data from DBCol::TrieChanges + for shard_uid in shard_uids_to_gc { + self.gc_col( + DBCol::TrieChanges, + &get_block_shard_uid(&block_hash, &shard_uid), + ); + } + } + } + } + + let block = + self.get_block(&block_hash).expect("block data is not expected to be already cleaned"); + let height = block.header().height(); + + // 2. Delete shard_id-indexed data (Receipts, State Headers and Parts, etc.) + for shard_id in 0..block.header().chunk_mask().len() as ShardId { + let block_shard_id = get_block_shard_id(&block_hash, shard_id); + self.gc_outgoing_receipts(&block_hash, shard_id); + self.gc_col(DBCol::IncomingReceipts, &block_shard_id); + + // For incoming State Parts it's done in chain.clear_downloaded_parts() + // The following code is mostly for outgoing State Parts. + // However, if node crashes while State Syncing, it may never clear + // downloaded State parts in `clear_downloaded_parts`. + // We need to make sure all State Parts are removed. + if let Ok(shard_state_header) = + self.chain_store().get_state_header(shard_id, block_hash) + { + let state_num_parts = shard_state_header.num_state_parts(); + self.gc_col_state_parts(block_hash, shard_id, state_num_parts)?; + let key = borsh::to_vec(&StateHeaderKey(shard_id, block_hash))?; + self.gc_col(DBCol::StateHeaders, &key); + } + } + // gc DBCol::ChunkExtra based on shard_uid since it's indexed by shard_uid in the storage + for shard_uid in self.get_shard_uids_to_gc(epoch_manager, &block_hash) { + let block_shard_uid = get_block_shard_uid(&block_hash, &shard_uid); + self.gc_col(DBCol::ChunkExtra, &block_shard_uid); + } + + // 3. Delete block_hash-indexed data + self.gc_col(DBCol::Block, block_hash.as_bytes()); + self.gc_col(DBCol::BlockExtra, block_hash.as_bytes()); + self.gc_col(DBCol::NextBlockHashes, block_hash.as_bytes()); + self.gc_col(DBCol::ChallengedBlocks, block_hash.as_bytes()); + self.gc_col(DBCol::BlocksToCatchup, block_hash.as_bytes()); + let storage_key = KeyForStateChanges::for_block(&block_hash); + let stored_state_changes: Vec> = self + .store() + .iter_prefix(DBCol::StateChanges, storage_key.as_ref()) + .map(|item| item.map(|(key, _)| key)) + .collect::>>()?; + for key in stored_state_changes { + self.gc_col(DBCol::StateChanges, &key); + } + self.gc_col(DBCol::BlockRefCount, block_hash.as_bytes()); + self.gc_outcomes(&block)?; + match gc_mode { + GCMode::StateSync { clear_block_info: false } => {} + _ => self.gc_col(DBCol::BlockInfo, block_hash.as_bytes()), + } + self.gc_col(DBCol::StateDlInfos, block_hash.as_bytes()); + + // 4. Update or delete block_hash_per_height + self.gc_col_block_per_height(&block_hash, height, block.header().epoch_id())?; + + match gc_mode { + GCMode::Fork(_) => { + // 5. Forks only clearing + self.dec_block_refcount(block.header().prev_hash())?; + } + GCMode::Canonical(_) => { + // 6. Canonical Chain only clearing + // Delete chunks, chunk-indexed data and block headers + let mut min_chunk_height = self.tail()?; + for chunk_header in block.chunks().iter() { + if min_chunk_height > chunk_header.height_created() { + min_chunk_height = chunk_header.height_created(); + } + } + self.clear_chunk_data_and_headers(min_chunk_height)?; + } + GCMode::StateSync { .. } => { + // 7. State Sync clearing + // Chunks deleted separately + } + }; + self.merge(store_update); + Ok(()) + } + + // Delete all data in rocksdb that are partially or wholly indexed and can be looked up by hash of the current head of the chain + // and that indicates a link between current head and its prev block + pub fn clear_head_block_data( + &mut self, + epoch_manager: &dyn EpochManagerAdapter, + ) -> Result<(), Error> { + let header_head = self.header_head().unwrap(); + let header_head_height = header_head.height; + let block_hash = self.head().unwrap().last_block_hash; + + let block = + self.get_block(&block_hash).expect("block data is not expected to be already cleaned"); + + let epoch_id = block.header().epoch_id(); + + let head_height = block.header().height(); + + // 1. Delete shard_id-indexed data (TrieChanges, Receipts, ChunkExtra, State Headers and Parts, FlatStorage data) + for shard_id in 0..block.header().chunk_mask().len() as ShardId { + let shard_uid = epoch_manager.shard_id_to_uid(shard_id, epoch_id).unwrap(); + let block_shard_id = get_block_shard_uid(&block_hash, &shard_uid); + + // delete TrieChanges + self.gc_col(DBCol::TrieChanges, &block_shard_id); + + // delete Receipts + self.gc_outgoing_receipts(&block_hash, shard_id); + self.gc_col(DBCol::IncomingReceipts, &block_shard_id); + + // delete DBCol::ChunkExtra based on shard_uid since it's indexed by shard_uid in the storage + self.gc_col(DBCol::ChunkExtra, &block_shard_id); + + // delete state parts and state headers + if let Ok(shard_state_header) = + self.chain_store().get_state_header(shard_id, block_hash) + { + let state_num_parts = shard_state_header.num_state_parts(); + self.gc_col_state_parts(block_hash, shard_id, state_num_parts)?; + let state_header_key = borsh::to_vec(&StateHeaderKey(shard_id, block_hash))?; + self.gc_col(DBCol::StateHeaders, &state_header_key); + } + + // delete flat storage columns: FlatStateChanges and FlatStateDeltaMetadata + let mut store_update = self.store().store_update(); + store_helper::remove_delta(&mut store_update, shard_uid, block_hash); + self.merge(store_update); + } + + // 2. Delete block_hash-indexed data + self.gc_col(DBCol::Block, block_hash.as_bytes()); + self.gc_col(DBCol::BlockExtra, block_hash.as_bytes()); + self.gc_col(DBCol::NextBlockHashes, block_hash.as_bytes()); + self.gc_col(DBCol::ChallengedBlocks, block_hash.as_bytes()); + self.gc_col(DBCol::BlocksToCatchup, block_hash.as_bytes()); + let storage_key = KeyForStateChanges::for_block(&block_hash); + let stored_state_changes: Vec> = self + .store() + .iter_prefix(DBCol::StateChanges, storage_key.as_ref()) + .map(|item| item.map(|(key, _)| key)) + .collect::>>()?; + for key in stored_state_changes { + self.gc_col(DBCol::StateChanges, &key); + } + self.gc_col(DBCol::BlockRefCount, block_hash.as_bytes()); + self.gc_outcomes(&block)?; + self.gc_col(DBCol::BlockInfo, block_hash.as_bytes()); + self.gc_col(DBCol::StateDlInfos, block_hash.as_bytes()); + + // 3. update columns related to prev block (block refcount and NextBlockHashes) + self.dec_block_refcount(block.header().prev_hash())?; + self.gc_col(DBCol::NextBlockHashes, block.header().prev_hash().as_bytes()); + + // 4. Update or delete block_hash_per_height + self.gc_col_block_per_height(&block_hash, head_height, block.header().epoch_id())?; + + self.clear_chunk_data_at_height(head_height)?; + + self.clear_header_data_for_heights(head_height, header_head_height)?; + + Ok(()) + } + + fn clear_chunk_data_at_height(&mut self, height: BlockHeight) -> Result<(), Error> { + let chunk_hashes = self.chain_store().get_all_chunk_hashes_by_height(height)?; + for chunk_hash in chunk_hashes { + // 1. Delete chunk-related data + let chunk = self.get_chunk(&chunk_hash)?.clone(); + debug_assert_eq!(chunk.cloned_header().height_created(), height); + for transaction in chunk.transactions() { + self.gc_col(DBCol::Transactions, transaction.get_hash().as_bytes()); + } + for receipt in chunk.prev_outgoing_receipts() { + self.gc_col(DBCol::Receipts, receipt.get_hash().as_bytes()); + } + + // 2. Delete chunk_hash-indexed data + let chunk_hash = chunk_hash.as_bytes(); + self.gc_col(DBCol::Chunks, chunk_hash); + self.gc_col(DBCol::PartialChunks, chunk_hash); + self.gc_col(DBCol::InvalidChunks, chunk_hash); + } + + // 4. Delete chunk hashes per height + let key = index_to_bytes(height); + self.gc_col(DBCol::ChunkHashesByHeight, &key); + + Ok(()) + } + + fn gc_col_block_per_height( + &mut self, + block_hash: &CryptoHash, + height: BlockHeight, + epoch_id: &EpochId, + ) -> Result<(), Error> { + let mut store_update = self.store().store_update(); + let mut epoch_to_hashes = + HashMap::clone(self.chain_store().get_all_block_hashes_by_height(height)?.as_ref()); + let hashes = epoch_to_hashes.get_mut(epoch_id).ok_or_else(|| { + unc_chain_primitives::Error::Other("current epoch id should exist".into()) + })?; + hashes.remove(block_hash); + if hashes.is_empty() { + epoch_to_hashes.remove(epoch_id); + } + let key = &index_to_bytes(height)[..]; + if epoch_to_hashes.is_empty() { + store_update.delete(DBCol::BlockPerHeight, key); + self.chain_store().block_hash_per_height.pop(key); + } else { + store_update.set_ser(DBCol::BlockPerHeight, key, &epoch_to_hashes)?; + self.chain_store().block_hash_per_height.put(key.to_vec(), Arc::new(epoch_to_hashes)); + } + if self.is_height_processed(height)? { + self.gc_col(DBCol::ProcessedBlockHeights, key); + } + self.merge(store_update); + Ok(()) + } + + pub fn gc_col_state_parts( + &mut self, + sync_hash: CryptoHash, + shard_id: ShardId, + num_parts: u64, + ) -> Result<(), Error> { + for part_id in 0..num_parts { + let key = borsh::to_vec(&StatePartKey(sync_hash, shard_id, part_id))?; + self.gc_col(DBCol::StateParts, &key); + } + Ok(()) + } + + fn gc_outgoing_receipts(&mut self, block_hash: &CryptoHash, shard_id: ShardId) { + let mut store_update = self.store().store_update(); + match self + .get_outgoing_receipts(block_hash, shard_id) + .map(|receipts| receipts.iter().map(|receipt| receipt.receipt_id).collect::>()) + { + Ok(receipt_ids) => { + for receipt_id in receipt_ids { + let key: Vec = receipt_id.into(); + store_update.decrement_refcount(DBCol::ReceiptIdToShardId, &key); + self.chain_store().receipt_id_to_shard_id.pop(&key); + } + } + Err(error) => { + match error { + Error::DBNotFoundErr(_) => { + // Sometimes we don't save outgoing receipts. See the usages of save_outgoing_receipt. + // The invariant is that DBCol::OutgoingReceipts has same receipts as DBCol::ReceiptIdToShardId. + } + _ => { + tracing::error!(target: "chain", "Error getting outgoing receipts for block {}, shard {}: {:?}", block_hash, shard_id, error); + } + } + } + } + + let key = get_block_shard_id(block_hash, shard_id); + store_update.delete(DBCol::OutgoingReceipts, &key); + self.chain_store().outgoing_receipts.pop(&key); + self.merge(store_update); + } + + fn gc_outcomes(&mut self, block: &Block) -> Result<(), Error> { + let block_hash = block.hash(); + let store_update = self.store().store_update(); + for chunk_header in + block.chunks().iter().filter(|h| h.height_included() == block.header().height()) + { + let shard_id = chunk_header.shard_id(); + let outcome_ids = + self.chain_store().get_outcomes_by_block_hash_and_shard_id(block_hash, shard_id)?; + for outcome_id in outcome_ids { + self.gc_col( + DBCol::TransactionResultForBlock, + &get_outcome_id_block_hash(&outcome_id, block_hash), + ); + } + self.gc_col(DBCol::OutcomeIds, &get_block_shard_id(block_hash, shard_id)); + } + self.merge(store_update); + Ok(()) + } + + fn gc_col(&mut self, col: DBCol, key: &[u8]) { + let mut store_update = self.store().store_update(); + match col { + DBCol::OutgoingReceipts => { + panic!("Must use gc_outgoing_receipts"); + } + DBCol::IncomingReceipts => { + store_update.delete(col, key); + self.chain_store().incoming_receipts.pop(key); + } + DBCol::StateHeaders => { + store_update.delete(col, key); + } + DBCol::BlockHeader => { + // TODO(#3488) At the moment header sync needs block headers. + // However, we want to eventually garbage collect headers. + // When that happens we should make sure that block headers is + // copied to the cold storage. + store_update.delete(col, key); + self.chain_store().headers.pop(key); + unreachable!(); + } + DBCol::Block => { + store_update.delete(col, key); + self.chain_store().blocks.pop(key); + } + DBCol::BlockExtra => { + store_update.delete(col, key); + self.chain_store().block_extras.pop(key); + } + DBCol::NextBlockHashes => { + store_update.delete(col, key); + self.chain_store().next_block_hashes.pop(key); + } + DBCol::ChallengedBlocks => { + store_update.delete(col, key); + } + DBCol::BlocksToCatchup => { + store_update.delete(col, key); + } + DBCol::StateChanges => { + store_update.delete(col, key); + } + DBCol::BlockRefCount => { + store_update.delete(col, key); + self.chain_store().block_refcounts.pop(key); + } + DBCol::ReceiptIdToShardId => { + panic!("Must use gc_outgoing_receipts"); + } + DBCol::Transactions => { + store_update.decrement_refcount(col, key); + self.chain_store().transactions.pop(key); + } + DBCol::Receipts => { + store_update.decrement_refcount(col, key); + self.chain_store().receipts.pop(key); + } + DBCol::Chunks => { + store_update.delete(col, key); + self.chain_store().chunks.pop(key); + } + DBCol::ChunkExtra => { + store_update.delete(col, key); + self.chain_store().chunk_extras.pop(key); + } + DBCol::PartialChunks => { + store_update.delete(col, key); + self.chain_store().partial_chunks.pop(key); + } + DBCol::InvalidChunks => { + store_update.delete(col, key); + self.chain_store().invalid_chunks.pop(key); + } + DBCol::ChunkHashesByHeight => { + store_update.delete(col, key); + } + DBCol::StateParts => { + store_update.delete(col, key); + } + DBCol::State => { + panic!("Actual gc happens elsewhere, call inc_gc_col_state to increase gc count"); + } + DBCol::TrieChanges => { + store_update.delete(col, key); + } + DBCol::BlockPerHeight => { + panic!("Must use gc_col_glock_per_height method to gc DBCol::BlockPerHeight"); + } + DBCol::TransactionResultForBlock => { + store_update.delete(col, key); + } + DBCol::OutcomeIds => { + store_update.delete(col, key); + } + DBCol::StateDlInfos => { + store_update.delete(col, key); + } + DBCol::BlockInfo => { + store_update.delete(col, key); + } + DBCol::ProcessedBlockHeights => { + store_update.delete(col, key); + self.chain_store().processed_block_heights.pop(key); + } + DBCol::HeaderHashesByHeight => { + store_update.delete(col, key); + } + DBCol::DbVersion + | DBCol::BlockMisc + | DBCol::_GCCount + | DBCol::BlockHeight // block sync needs it + genesis should be accessible + | DBCol::_Peers + | DBCol::RecentOutboundConnections + | DBCol::BlockMerkleTree + | DBCol::AccountAnnouncements + | DBCol::EpochLightClientBlocks + | DBCol::PeerComponent + | DBCol::LastComponentNonce + | DBCol::ComponentEdges + // https://github.com/utnet-org/utility/pull/2952 + | DBCol::EpochInfo + | DBCol::EpochStart + | DBCol::EpochValidatorInfo + | DBCol::BlockOrdinal + | DBCol::_ChunkPerHeightShard + | DBCol::_NextBlockWithNewChunk + | DBCol::_LastBlockWithNewChunk + | DBCol::_TransactionRefCount + | DBCol::_TransactionResult + | DBCol::StateChangesForSplitStates + | DBCol::CachedContractCode + | DBCol::FlatState + | DBCol::FlatStateChanges + | DBCol::FlatStateDeltaMetadata + | DBCol::FlatStorageStatus + | DBCol::Misc + | DBCol::BadValidator + => unreachable!(), + #[cfg(feature = "new_epoch_sync")] + DBCol::EpochSyncInfo => unreachable!(), + } + self.merge(store_update); + } +} diff --git a/chain/chain/src/lib.rs b/chain/chain/src/lib.rs new file mode 100644 index 000000000..4d7f23af8 --- /dev/null +++ b/chain/chain/src/lib.rs @@ -0,0 +1,52 @@ +pub use block_processing_utils::{BlockProcessingArtifact, DoneApplyChunkCallback}; +pub use chain::{check_known, collect_receipts, Chain}; +pub use chain_update::ChainUpdate; +pub use doomslug::{Doomslug, DoomslugBlockProductionReadiness, DoomslugThresholdMode}; +pub use lightclient::{create_light_client_block_view, get_epoch_block_producers_view}; +pub use unc_chain_primitives::{self, Error}; +pub use unc_primitives::receipt::ReceiptResult; +pub use store::{ChainStore, ChainStoreAccess, ChainStoreUpdate}; +pub use store_validator::{ErrorMessage, StoreValidator}; +pub use types::{Block, BlockHeader, BlockStatus, ChainGenesis, Provenance}; + +mod block_processing_utils; +pub mod blocks_delay_tracker; +pub mod chain; +mod chain_update; +pub mod chunks_store; +pub mod crypto_hash_timer; +mod doomslug; +pub mod flat_storage_creator; +mod garbage_collection; +mod lightclient; +mod metrics; +pub mod migrations; +pub mod missing_chunks; +pub mod orphan; +pub mod resharding; +mod state_request_tracker; +pub mod state_snapshot_actor; +mod store; +pub mod store_validator; +pub mod test_utils; +pub mod types; +pub mod validate; + +pub mod sharding; +#[cfg(test)] +mod tests; +mod update_shard; + +#[cfg(feature = "byzantine_asserts")] +#[macro_export] +macro_rules! byzantine_assert { + ($cond: expr) => { + assert!($cond) + }; +} + +#[cfg(not(feature = "byzantine_asserts"))] +#[macro_export] +macro_rules! byzantine_assert { + ($cond: expr) => {}; +} diff --git a/chain/chain/src/lightclient.rs b/chain/chain/src/lightclient.rs new file mode 100644 index 000000000..a763e36b3 --- /dev/null +++ b/chain/chain/src/lightclient.rs @@ -0,0 +1,73 @@ +use unc_chain_primitives::Error; +use unc_epoch_manager::EpochManagerAdapter; +use unc_primitives::block::BlockHeader; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::types::EpochId; +use unc_primitives::views::{BlockHeaderInnerLiteView, LightClientBlockView}; +use unc_primitives::views::validator_power_and_frozen_view::ValidatorPowerAndFrozenView; + +use crate::ChainStoreAccess; + +pub fn get_epoch_block_producers_view( + epoch_id: &EpochId, + prev_hash: &CryptoHash, + epoch_manager: &dyn EpochManagerAdapter, +) -> Result, Error> { + Ok(epoch_manager + .get_epoch_block_producers_ordered(epoch_id, prev_hash)? + .iter() + .map(|x| x.0.clone().into()) + .collect::>()) +} + +/// Creates the `LightClientBlock` from the information in the chain store for a given block. +/// +/// # Arguments +/// * `block_header` - block header of the block for which the `LightClientBlock` is computed +/// * `chain_store` +/// * `block_producers` - the ordered list of block producers in the epoch of the block that +/// corresponds to `block_header` +/// * `next_block_producers` - the ordered list of block producers in the next epoch, if the light +/// client should contain them (otherwise `None`) +/// +/// # Requirements: +/// `get_next_final_block_hash` in the chain is set for the block that `block_header` corresponds +/// to and for the next block, and the three blocks must have sequential heights. +pub fn create_light_client_block_view( + block_header: &BlockHeader, + chain_store: &dyn ChainStoreAccess, + next_block_producers: Option>, +) -> Result { + let inner_lite_view = BlockHeaderInnerLiteView { + height: block_header.height(), + epoch_id: block_header.epoch_id().0, + next_epoch_id: block_header.next_epoch_id().0, + prev_state_root: *block_header.prev_state_root(), + outcome_root: *block_header.outcome_root(), + timestamp: block_header.raw_timestamp(), + timestamp_nanosec: block_header.raw_timestamp(), + next_bp_hash: *block_header.next_bp_hash(), + block_merkle_root: *block_header.block_merkle_root(), + }; + let inner_rest_hash = hash(&block_header.inner_rest_bytes()); + + let next_block_hash = chain_store.get_next_block_hash(block_header.hash())?; + let next_block_header = chain_store.get_block_header(&next_block_hash)?; + let next_block_inner_hash = BlockHeader::compute_inner_hash( + &next_block_header.inner_lite_bytes(), + &next_block_header.inner_rest_bytes(), + ); + + let after_next_block_hash = chain_store.get_next_block_hash(&next_block_hash)?; + let after_next_block_header = chain_store.get_block_header(&after_next_block_hash)?; + let approvals_after_next = after_next_block_header.approvals().to_vec(); + + Ok(LightClientBlockView { + prev_block_hash: *block_header.prev_hash(), + next_block_inner_hash, + inner_lite: inner_lite_view, + inner_rest_hash, + next_bps: next_block_producers, + approvals_after_next, + }) +} diff --git a/chain/chain/src/metrics.rs b/chain/chain/src/metrics.rs new file mode 100644 index 000000000..f572ae84e --- /dev/null +++ b/chain/chain/src/metrics.rs @@ -0,0 +1,240 @@ +use unc_o11y::metrics::{ + exponential_buckets, processing_time_buckets, try_create_histogram, try_create_histogram_vec, + try_create_histogram_with_buckets, try_create_int_counter, try_create_int_gauge, + try_create_int_gauge_vec, Histogram, HistogramVec, IntCounter, IntGauge, IntGaugeVec, +}; +use once_cell::sync::Lazy; + +pub static BLOCK_PROCESSING_ATTEMPTS_TOTAL: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_block_processing_attempts_total", + "Total number of block processing attempts. The most common reason for aborting block processing is missing chunks", + ) + .unwrap() +}); +pub static BLOCK_PROCESSED_TOTAL: Lazy = Lazy::new(|| { + try_create_int_counter("unc_block_processed_total", "Total number of blocks processed") + .unwrap() +}); +pub static BLOCK_PROCESSING_TIME: Lazy = Lazy::new(|| { + try_create_histogram_with_buckets( + "unc_block_processing_time", + "Time taken to process blocks successfully, from when a block is ready to be processed till when the processing is finished. Measures only the time taken by the successful attempts of block processing", + processing_time_buckets() + ).unwrap() +}); +pub static BLOCK_PREPROCESSING_TIME: Lazy = Lazy::new(|| { + try_create_histogram("unc_block_preprocessing_time", "Time taken to preprocess blocks, only include the time when the preprocessing is successful") + .unwrap() +}); +pub static BLOCK_POSTPROCESSING_TIME: Lazy = Lazy::new(|| { + try_create_histogram("unc_block_postprocessing_time", "Time taken to postprocess blocks") + .unwrap() +}); +pub static BLOCK_HEIGHT_HEAD: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_block_height_head", "Height of the current head of the blockchain") + .unwrap() +}); +pub static BLOCK_ORDINAL_HEAD: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_block_ordinal_head", "Ordinal of the current head of the blockchain") + .unwrap() +}); +pub static VALIDATOR_AMOUNT_STAKED: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_validators_stake_total", + "The total stake of all active validators during the last block", + ) + .unwrap() +}); +pub static VALIDATOR_ACTIVE_TOTAL: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_validator_active_total", + "The total number of validators active after last block", + ) + .unwrap() +}); +pub static NUM_ORPHANS: Lazy = + Lazy::new(|| try_create_int_gauge("unc_num_orphans", "Number of orphan blocks.").unwrap()); +pub static HEADER_HEAD_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_header_head_height", "Height of the header head").unwrap() +}); +pub static BOOT_TIME_SECONDS: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_boot_time_seconds", + "Unix timestamp in seconds of the moment the client was started", + ) + .unwrap() +}); +pub static TAIL_HEIGHT: Lazy = + Lazy::new(|| try_create_int_gauge("unc_tail_height", "Height of tail").unwrap()); +pub static CHUNK_TAIL_HEIGHT: Lazy = + Lazy::new(|| try_create_int_gauge("unc_chunk_tail_height", "Height of chunk tail").unwrap()); +pub static FORK_TAIL_HEIGHT: Lazy = + Lazy::new(|| try_create_int_gauge("unc_fork_tail_height", "Height of fork tail").unwrap()); +pub static GC_STOP_HEIGHT: Lazy = + Lazy::new(|| try_create_int_gauge("unc_gc_stop_height", "Target height of gc").unwrap()); +pub static CHUNK_RECEIVED_DELAY: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_chunk_receive_delay_seconds", + "Delay between requesting and receiving a chunk.", + &["shard_id"], + Some(exponential_buckets(0.001, 1.6, 20).unwrap()), + ) + .unwrap() +}); +pub static BLOCK_ORPHANED_DELAY: Lazy = Lazy::new(|| { + try_create_histogram("unc_block_orphaned_delay", "How long blocks stay in the orphan pool") + .unwrap() +}); +pub static BLOCK_MISSING_CHUNKS_DELAY: Lazy = Lazy::new(|| { + try_create_histogram( + "unc_block_missing_chunks_delay", + "How long blocks stay in the missing chunks pool", + ) + .unwrap() +}); +pub static STATE_PART_ELAPSED: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_state_part_elapsed_sec", + "Time needed to create a state part", + &["shard_id"], + Some(exponential_buckets(0.001, 1.6, 20).unwrap()), + ) + .unwrap() +}); +pub static NUM_INVALID_BLOCKS: Lazy = Lazy::new(|| { + try_create_int_gauge_vec("unc_num_invalid_blocks", "Number of invalid blocks", &["error"]) + .unwrap() +}); +pub(crate) static SCHEDULED_CATCHUP_BLOCK: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_catchup_scheduled_block_height", + "Tracks the progress of blocks catching up", + ) + .unwrap() +}); +pub(crate) static LARGEST_TARGET_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_largest_target_height", + "The largest height for which we sent an approval (or skip)", + ) + .unwrap() +}); +pub(crate) static LARGEST_THRESHOLD_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_largest_threshold_height", + "The largest height where we got enough approvals", + ) + .unwrap() +}); +pub(crate) static LARGEST_APPROVAL_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_largest_approval_height", + "The largest height for which we've got at least one approval", + ) + .unwrap() +}); +pub(crate) static LARGEST_FINAL_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_largest_final_height", + "Largest height for which we saw a block containing 1/2 endorsements in it", + ) + .unwrap() +}); + +pub(crate) enum ReshardingStatus { + /// The ReshardingRequest was send to the SyncJobsActor. + Scheduled, + /// The SyncJobsActor is performing the resharding. + BuildingState, + /// The resharding is finished. + Finished, + /// The resharding failed. Manual recovery is necessary! + Failed, +} + +impl From for i64 { + /// Converts status to integer to export to prometheus later. + /// Cast inside enum does not work because it is not fieldless. + fn from(value: ReshardingStatus) -> Self { + match value { + ReshardingStatus::Scheduled => 0, + ReshardingStatus::BuildingState => 1, + ReshardingStatus::Finished => 2, + ReshardingStatus::Failed => -1, + } + } +} + +pub(crate) static SHARD_LAYOUT_VERSION: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_shard_layout_version", + "The version of the shard layout of the current head.", + ) + .unwrap() +}); + +pub(crate) static SHARD_LAYOUT_NUM_SHARDS: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_shard_layout_num_shards", + "The number of shards in the shard layout of the current head.", + ) + .unwrap() +}); + +pub(crate) static RESHARDING_BATCH_COUNT: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_resharding_batch_count", + "The number of batches committed to the db.", + &["shard_uid"], + ) + .unwrap() +}); + +pub(crate) static RESHARDING_BATCH_SIZE: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_resharding_batch_size", + "The size of batches committed to the db.", + &["shard_uid"], + ) + .unwrap() +}); + +pub static RESHARDING_BATCH_PREPARE_TIME: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_resharding_batch_prepare_time", + "Time needed to prepare a batch in resharding.", + &["shard_uid"], + Some(exponential_buckets(0.001, 1.6, 20).unwrap()), + ) + .unwrap() +}); + +pub static RESHARDING_BATCH_APPLY_TIME: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_resharding_batch_apply_time", + "Time needed to apply a batch in resharding.", + &["shard_uid"], + Some(exponential_buckets(0.001, 1.6, 20).unwrap()), + ) + .unwrap() +}); + +pub static RESHARDING_BATCH_COMMIT_TIME: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_resharding_batch_commit_time", + "Time needed to commit a batch in resharding.", + &["shard_uid"], + Some(exponential_buckets(0.001, 1.6, 20).unwrap()), + ) + .unwrap() +}); + +pub(crate) static RESHARDING_STATUS: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_resharding_status", + "The status of the resharding process.", + &["shard_uid"], + ) + .unwrap() +}); diff --git a/chain/chain/src/migrations.rs b/chain/chain/src/migrations.rs new file mode 100644 index 000000000..230ddee5c --- /dev/null +++ b/chain/chain/src/migrations.rs @@ -0,0 +1,43 @@ +use crate::store::ChainStoreAccess; +use unc_chain_primitives::error::Error; +use unc_epoch_manager::EpochManagerAdapter; +use unc_primitives::hash::CryptoHash; +use unc_primitives::types::ShardId; + +/// Check that epoch of block with given prev_block_hash is the first one with current protocol version. +fn is_first_epoch_with_protocol_version( + epoch_manager: &dyn EpochManagerAdapter, + prev_block_hash: &CryptoHash, +) -> Result { + let prev_epoch_id = epoch_manager.get_prev_epoch_id_from_prev_block(prev_block_hash)?; + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(prev_block_hash)?; + let protocol_version = epoch_manager.get_epoch_protocol_version(&epoch_id)?; + let prev_epoch_protocol_version = epoch_manager.get_epoch_protocol_version(&prev_epoch_id)?; + Ok(protocol_version != prev_epoch_protocol_version) +} + +/// Check that block is the first one with existing chunk for the given shard in the chain with its protocol version. +/// We assume that current block contain the chunk for shard with the given id. +pub fn check_if_block_is_first_with_chunk_of_version( + chain_store: &dyn ChainStoreAccess, + epoch_manager: &dyn EpochManagerAdapter, + prev_block_hash: &CryptoHash, + shard_id: ShardId, +) -> Result { + // Check that block belongs to the first epoch with current protocol version + // to avoid get_epoch_id_of_last_block_with_chunk call in the opposite case + if is_first_epoch_with_protocol_version(epoch_manager, prev_block_hash)? { + // Compare only epochs because we already know that current epoch is the first one with current protocol version + // convert shard id to shard id of previous epoch because number of shards may change + let shard_id = epoch_manager.get_prev_shard_ids(prev_block_hash, vec![shard_id])?[0]; + let prev_epoch_id = chain_store.get_epoch_id_of_last_block_with_chunk( + epoch_manager, + prev_block_hash, + shard_id, + )?; + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(prev_block_hash)?; + Ok(prev_epoch_id != epoch_id) + } else { + Ok(false) + } +} diff --git a/chain/chain/src/missing_chunks.rs b/chain/chain/src/missing_chunks.rs new file mode 100644 index 000000000..d275809c3 --- /dev/null +++ b/chain/chain/src/missing_chunks.rs @@ -0,0 +1,296 @@ +use unc_primitives::hash::CryptoHash; +use unc_primitives::sharding::ChunkHash; +use unc_primitives::types::BlockHeight; +use std::cmp::Ordering; +use std::collections::{ + btree_map::{self, BTreeMap}, + hash_map::{self, HashMap}, + BinaryHeap, HashSet, +}; +use tracing::{debug, warn}; + +type BlockHash = CryptoHash; + +const MAX_BLOCKS_MISSING_CHUNKS: usize = 1024; + +pub trait BlockLike { + fn hash(&self) -> BlockHash; + fn height(&self) -> BlockHeight; +} + +#[derive(Debug)] +struct HeightOrdered(T); + +impl PartialEq for HeightOrdered { + fn eq(&self, other: &Self) -> bool { + self.0.height() == other.0.height() + } +} +impl Eq for HeightOrdered {} +impl PartialOrd for HeightOrdered { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.0.height().cmp(&other.0.height())) + } +} +impl Ord for HeightOrdered { + fn cmp(&self, other: &Self) -> Ordering { + self.0.height().cmp(&other.0.height()) + } +} + +/// Structure for keeping track of missing chunks. +/// The reason to have a Block type parameter instead of using the +/// `block::Block` type is to make testing easier (`block::Block` is a complex structure and I +/// don't care about most of it). +#[derive(Debug, Default)] +pub struct MissingChunksPool { + missing_chunks: HashMap>, + blocks_missing_chunks: HashMap>, + blocks_waiting_for_chunks: HashMap, + blocks_ready_to_process: BinaryHeap>, + height_idx: BTreeMap>, +} + +impl MissingChunksPool { + pub fn new() -> Self { + Self { + missing_chunks: Default::default(), + blocks_missing_chunks: Default::default(), + blocks_waiting_for_chunks: Default::default(), + blocks_ready_to_process: BinaryHeap::new(), + height_idx: Default::default(), + } + } + + pub fn contains(&self, block_hash: &BlockHash) -> bool { + self.blocks_waiting_for_chunks.contains_key(block_hash) + } + + pub fn get(&self, block_hash: &BlockHash) -> Option<&Block> { + self.blocks_waiting_for_chunks.get(block_hash) + } + + pub fn len(&self) -> usize { + self.blocks_waiting_for_chunks.len() + } + + pub fn ready_blocks(&mut self) -> Vec { + if self.blocks_ready_to_process.is_empty() { + return Vec::new(); + } + let heap = std::mem::replace(&mut self.blocks_ready_to_process, BinaryHeap::new()); + heap.into_sorted_vec().into_iter().map(|x| x.0).collect() + } + + pub fn add_block_with_missing_chunks(&mut self, block: Block, missing_chunks: Vec) { + let block_hash = block.hash(); + // This case can only happen when missing chunks are not being eventually received and + // thus removing blocks from the HashMap. It means the this node has severely stalled out. + // It is ok to ignore further blocks because either (a) we will start receiving chunks + // again, work through the backlog of the pool, then naturally sync the later blocks + // which were not added initially, or (b) someone will restart the node because something + // has gone horribly wrong, in which case these HashMaps will be lost anyways. + if self.blocks_missing_chunks.len() >= MAX_BLOCKS_MISSING_CHUNKS { + warn!(target: "chunks", "Not recording block with hash {} even though it is missing chunks. The missing chunks pool is full.", block_hash); + return; + } + + for chunk_hash in missing_chunks.iter().cloned() { + let blocks_for_chunk = + self.missing_chunks.entry(chunk_hash).or_insert_with(HashSet::new); + blocks_for_chunk.insert(block_hash); + } + + // Convert to HashSet + let missing_chunks = missing_chunks.into_iter().collect(); + match self.blocks_missing_chunks.entry(block_hash) { + hash_map::Entry::Vacant(entry) => { + entry.insert(missing_chunks); + } + // The Occupied case should never happen since we know + // all the missing chunks for a block the first time we receive it, + // and we should not call `add_block_with_missing_chunks` again after + // we know a block is missing chunks. + hash_map::Entry::Occupied(mut entry) => { + let previous_chunks = entry.insert(missing_chunks); + warn!(target: "chunks", "Block with hash {} was already missing chunks {:?}.", block_hash, previous_chunks); + } + } + + let height = block.height(); + let blocks_at_height = self.height_idx.entry(height).or_insert_with(HashSet::new); + blocks_at_height.insert(block_hash); + self.blocks_waiting_for_chunks.insert(block_hash, block); + } + + pub fn accept_chunk(&mut self, chunk_hash: &ChunkHash) { + let block_hashes = self.missing_chunks.remove(chunk_hash).unwrap_or_else(HashSet::new); + debug!(target: "chunks", ?chunk_hash, "Chunk accepted, {} blocks were waiting for it.", block_hashes.len()); + for block_hash in block_hashes { + match self.blocks_missing_chunks.entry(block_hash) { + hash_map::Entry::Occupied(mut missing_chunks_entry) => { + let missing_chunks = missing_chunks_entry.get_mut(); + missing_chunks.remove(chunk_hash); + if missing_chunks.is_empty() { + // No more missing chunks! + missing_chunks_entry.remove_entry(); + debug!(target: "chunks", %block_hash, "Block is ready - last chunk received."); + self.mark_block_as_ready(&block_hash); + } else { + debug!(target: "chunks", %block_hash, "Block is still waiting for {} chunks.", missing_chunks.len()); + } + } + hash_map::Entry::Vacant(_) => { + warn!(target: "chunks", "Invalid MissingChunksPool state. Block with hash {} was still a value of the missing_chunks map, but not present in the blocks_missing_chunks map", block_hash); + self.mark_block_as_ready(&block_hash); + } + } + } + } + + fn mark_block_as_ready(&mut self, block_hash: &BlockHash) { + if let Some(block) = self.blocks_waiting_for_chunks.remove(block_hash) { + let height = block.height(); + if let btree_map::Entry::Occupied(mut entry) = self.height_idx.entry(height) { + let blocks_at_height = entry.get_mut(); + blocks_at_height.remove(block_hash); + if blocks_at_height.is_empty() { + entry.remove_entry(); + } + } + self.blocks_ready_to_process.push(HeightOrdered(block)); + } + } + + pub fn prune_blocks_below_height(&mut self, height: BlockHeight) { + let heights_to_remove: Vec = + self.height_idx.keys().copied().take_while(|h| *h < height).collect(); + for h in heights_to_remove { + if let Some(block_hashes) = self.height_idx.remove(&h) { + for block_hash in block_hashes { + self.blocks_waiting_for_chunks.remove(&block_hash); + if let Some(chunk_hashes) = self.blocks_missing_chunks.remove(&block_hash) { + for chunk_hash in chunk_hashes { + if let hash_map::Entry::Occupied(mut entry) = + self.missing_chunks.entry(chunk_hash) + { + let blocks_for_chunk = entry.get_mut(); + blocks_for_chunk.remove(&block_hash); + if blocks_for_chunk.is_empty() { + entry.remove_entry(); + } + } + } + } + } + } + } + } +} + +#[cfg(test)] +mod test { + use super::{BlockHash, BlockLike, MissingChunksPool, MAX_BLOCKS_MISSING_CHUNKS}; + use unc_primitives::hash::{hash, CryptoHash}; + use unc_primitives::sharding::ChunkHash; + use unc_primitives::types::BlockHeight; + + fn get_hash(idx: u64) -> CryptoHash { + hash(&idx.to_le_bytes()) + } + + fn get_chunk_hash(idx: u64) -> ChunkHash { + ChunkHash(get_hash(idx)) + } + + #[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] + struct MockBlock { + hash: BlockHash, + height: BlockHeight, + } + impl MockBlock { + fn new(height: BlockHeight) -> Self { + Self { hash: get_hash(height), height } + } + } + impl BlockLike for MockBlock { + fn hash(&self) -> BlockHash { + self.hash + } + + fn height(&self) -> u64 { + self.height + } + } + + #[test] + fn should_mark_blocks_as_ready_after_all_chunks_accepted() { + let mut pool: MissingChunksPool = MissingChunksPool::default(); + + let block_height = 0; + let block = MockBlock::new(block_height); + let chunk_hashes: Vec = (101..105).map(get_chunk_hash).collect(); + + pool.add_block_with_missing_chunks(block, chunk_hashes.clone()); + assert!(pool.contains(&block.hash)); + + for chunk_hash in chunk_hashes.iter().skip(1) { + pool.accept_chunk(chunk_hash); + assert!(pool.contains(&block.hash)); + } + + // after the last chunk is accepted the block is ready to process + pool.accept_chunk(&chunk_hashes[0]); + assert!(!pool.contains(&block.hash)); + assert_eq!(pool.ready_blocks(), vec![block]); + } + + #[test] + fn should_not_add_new_blocks_after_size_limit() { + let mut pool: MissingChunksPool = MissingChunksPool::default(); + let mut chunk_hash_idx = MAX_BLOCKS_MISSING_CHUNKS as BlockHeight; + + for block_height in 0..MAX_BLOCKS_MISSING_CHUNKS { + let block_height = block_height as BlockHeight; + let block = MockBlock::new(block_height); + chunk_hash_idx += 1; + let missing_chunk_hash = get_chunk_hash(chunk_hash_idx); + let block_hash = block.hash; + pool.add_block_with_missing_chunks(block, vec![missing_chunk_hash]); + assert!(pool.contains(&block_hash)); + } + + let block_height = MAX_BLOCKS_MISSING_CHUNKS as BlockHeight; + let block = MockBlock::new(block_height); + chunk_hash_idx += 1; + let missing_chunk_hash = get_chunk_hash(chunk_hash_idx); + let block_hash = block.hash; + pool.add_block_with_missing_chunks(block, vec![missing_chunk_hash]); + assert!(!pool.contains(&block_hash)); + } + + #[test] + fn should_remove_old_blocks_when_prune_called() { + let mut pool: MissingChunksPool = MissingChunksPool::default(); + + let block = MockBlock::new(0); + let early_block_hash = block.hash; + let missing_chunk_hash = get_chunk_hash(100); + pool.add_block_with_missing_chunks(block, vec![missing_chunk_hash]); + + let block_height = 1; + let block = MockBlock::new(block_height); + let missing_chunk_hash = get_chunk_hash(200); + pool.add_block_with_missing_chunks(block, vec![missing_chunk_hash.clone()]); + + let later_block = MockBlock::new(block_height + 1); + let later_block_hash = later_block.hash; + pool.add_block_with_missing_chunks(later_block, vec![get_chunk_hash(300)]); + + pool.accept_chunk(&missing_chunk_hash); + pool.prune_blocks_below_height(block_height); + assert_eq!(pool.ready_blocks(), vec![block]); + assert!(!pool.contains(&early_block_hash)); + assert!(pool.contains(&later_block_hash)); + } +} diff --git a/chain/chain/src/orphan.rs b/chain/chain/src/orphan.rs new file mode 100644 index 000000000..9515fc864 --- /dev/null +++ b/chain/chain/src/orphan.rs @@ -0,0 +1,431 @@ +use std::collections::{HashMap, HashSet}; +use std::fmt::{Debug, Formatter}; +use std::time::{Duration, Instant}; + +use unc_chain_primitives::Error; +use unc_primitives::block::Block; +use unc_primitives::hash::CryptoHash; +use unc_primitives::sharding::ShardChunkHeader; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::types::{AccountId, BlockHeight, EpochId}; +use unc_primitives::utils::MaybeValidated; +use tracing::{debug, debug_span}; + +use crate::missing_chunks::BlockLike; +use crate::{metrics, BlockProcessingArtifact, Chain, DoneApplyChunkCallback, Provenance}; + +/// Maximum number of orphans chain can store. +const MAX_ORPHAN_SIZE: usize = 1024; + +/// Maximum age of orphan to store in the chain. +const MAX_ORPHAN_AGE_SECS: u64 = 300; + +// Number of orphan ancestors should be checked to request chunks +// Orphans for which we will request for missing chunks must satisfy, +// its NUM_ORPHAN_ANCESTORS_CHECK'th ancestor has been accepted +pub const NUM_ORPHAN_ANCESTORS_CHECK: u64 = 3; + +// Maximum number of orphans that we can request missing chunks +// Note that if there are no forks, the maximum number of orphans we would +// request missing chunks will not exceed NUM_ORPHAN_ANCESTORS_CHECK, +// this number only adds another restriction when there are multiple forks. +// It should almost never be hit +const MAX_ORPHAN_MISSING_CHUNKS: usize = 5; + +/// Orphan is a block whose previous block is not accepted (in store) yet. +/// Therefore, they are not ready to be processed yet. +/// We save these blocks in an in-memory orphan pool to be processed later +/// after their previous block is accepted. +pub struct Orphan { + pub(crate) block: MaybeValidated, + pub(crate) provenance: Provenance, + pub(crate) added: Instant, +} + +impl BlockLike for Orphan { + fn hash(&self) -> CryptoHash { + *self.block.hash() + } + + fn height(&self) -> u64 { + self.block.header().height() + } +} + +impl Orphan { + fn prev_hash(&self) -> &CryptoHash { + self.block.header().prev_hash() + } +} + +/// OrphanBlockPool stores information of all orphans that are waiting to be processed +/// A block is added to the orphan pool when process_block failed because the block is an orphan +/// A block is removed from the pool if +/// 1) it is ready to be processed +/// or +/// 2) size of the pool exceeds MAX_ORPHAN_SIZE and the orphan was added a long time ago +/// or the height is high +pub struct OrphanBlockPool { + /// A map from block hash to a orphan block + orphans: HashMap, + /// A set that contains all orphans for which we have requested missing chunks for them + /// An orphan can be added to this set when it was first added to the pool, or later + /// when certain requirements are satisfied (see check_orphans) + /// It can only be removed from this set when the orphan is removed from the pool + orphans_requested_missing_chunks: HashSet, + /// A map from block heights to orphan blocks at the height + /// It's used to evict orphans when the pool is saturated + height_idx: HashMap>, + /// A map from block hashes to orphan blocks whose prev block is the block + /// It's used to check which orphan blocks are ready to be processed when a block is accepted + prev_hash_idx: HashMap>, + /// number of orphans that were evicted + evicted: usize, +} + +impl OrphanBlockPool { + pub fn new() -> OrphanBlockPool { + OrphanBlockPool { + orphans: HashMap::default(), + orphans_requested_missing_chunks: HashSet::default(), + height_idx: HashMap::default(), + prev_hash_idx: HashMap::default(), + evicted: 0, + } + } + + pub fn len(&self) -> usize { + self.orphans.len() + } + + fn len_evicted(&self) -> usize { + self.evicted + } + + /// Add a block to the orphan pool + /// `requested_missing_chunks`: whether missing chunks has been requested for the orphan + fn add(&mut self, orphan: Orphan, requested_missing_chunks: bool) { + let block_hash = *orphan.block.hash(); + let height_hashes = self.height_idx.entry(orphan.block.header().height()).or_default(); + height_hashes.push(*orphan.block.hash()); + let prev_hash_entries = + self.prev_hash_idx.entry(*orphan.block.header().prev_hash()).or_default(); + prev_hash_entries.push(block_hash); + self.orphans.insert(block_hash, orphan); + if requested_missing_chunks { + self.orphans_requested_missing_chunks.insert(block_hash); + } + + if self.orphans.len() > MAX_ORPHAN_SIZE { + let old_len = self.orphans.len(); + + let mut removed_hashes: HashSet = HashSet::default(); + self.orphans.retain(|_, ref mut x| { + let keep = x.added.elapsed() < Duration::from_secs(MAX_ORPHAN_AGE_SECS); + if !keep { + removed_hashes.insert(*x.block.hash()); + } + keep + }); + let mut heights = self.height_idx.keys().cloned().collect::>(); + heights.sort_unstable(); + for h in heights.iter().rev() { + if let Some(hash) = self.height_idx.remove(h) { + for h in hash { + let _ = self.orphans.remove(&h); + removed_hashes.insert(h); + } + } + if self.orphans.len() < MAX_ORPHAN_SIZE { + break; + } + } + self.height_idx.retain(|_, ref mut xs| xs.iter().any(|x| !removed_hashes.contains(x))); + self.prev_hash_idx + .retain(|_, ref mut xs| xs.iter().any(|x| !removed_hashes.contains(x))); + self.orphans_requested_missing_chunks.retain(|x| !removed_hashes.contains(x)); + + self.evicted += old_len - self.orphans.len(); + } + metrics::NUM_ORPHANS.set(self.orphans.len() as i64); + } + + pub fn contains(&self, hash: &CryptoHash) -> bool { + self.orphans.contains_key(hash) + } + + pub fn get(&self, hash: &CryptoHash) -> Option<&Orphan> { + self.orphans.get(hash) + } + + // // Iterates over existing orphans. + // pub fn map(&self, orphan_fn: &mut dyn FnMut(&CryptoHash, &Block, &Instant)) { + // self.orphans + // .iter() + // .map(|it| orphan_fn(it.0, it.1.block.get_inner(), &it.1.added)) + // .collect_vec(); + // } + + /// Remove all orphans in the pool that can be "adopted" by block `prev_hash`, i.e., children + /// of `prev_hash` and return the list. + /// This function is called when `prev_hash` is accepted, thus its children can be removed + /// from the orphan pool and be processed. + pub fn remove_by_prev_hash(&mut self, prev_hash: CryptoHash) -> Option> { + let mut removed_hashes: HashSet = HashSet::default(); + let ret = self.prev_hash_idx.remove(&prev_hash).map(|hs| { + hs.iter() + .filter_map(|h| { + removed_hashes.insert(*h); + self.orphans_requested_missing_chunks.remove(h); + self.orphans.remove(h) + }) + .collect() + }); + + self.height_idx.retain(|_, ref mut xs| xs.iter().any(|x| !removed_hashes.contains(x))); + + metrics::NUM_ORPHANS.set(self.orphans.len() as i64); + ret + } + + /// Return a list of orphans that are among the `target_depth` immediate descendants of + /// the block `parent_hash` + pub fn get_orphans_within_depth( + &self, + parent_hash: CryptoHash, + target_depth: u64, + ) -> Vec { + let mut _visited = HashSet::new(); + + let mut res = vec![]; + let mut queue = vec![(parent_hash, 0)]; + while let Some((prev_hash, depth)) = queue.pop() { + if depth == target_depth { + break; + } + if let Some(block_hashes) = self.prev_hash_idx.get(&prev_hash) { + for hash in block_hashes { + queue.push((*hash, depth + 1)); + res.push(*hash); + // there should be no loop + debug_assert!(_visited.insert(*hash)); + } + } + + // probably something serious went wrong here because there shouldn't be so many forks + assert!( + res.len() <= 100 * target_depth as usize, + "found too many orphans {:?}, probably something is wrong with the chain", + res + ); + } + res + } + + /// Returns true if the block has not been requested yet and the number of orphans + /// for which we have requested missing chunks have not exceeded MAX_ORPHAN_MISSING_CHUNKS + fn can_request_missing_chunks_for_orphan(&self, block_hash: &CryptoHash) -> bool { + self.orphans_requested_missing_chunks.len() < MAX_ORPHAN_MISSING_CHUNKS + && !self.orphans_requested_missing_chunks.contains(block_hash) + } + + fn mark_missing_chunks_requested_for_orphan(&mut self, block_hash: CryptoHash) { + self.orphans_requested_missing_chunks.insert(block_hash); + } +} + +/// Contains information needed to request chunks for orphans +/// Fields will be used as arguments for `request_chunks_for_orphan` +pub struct OrphanMissingChunks { + pub missing_chunks: Vec, + /// epoch id for the block that has missing chunks + pub epoch_id: EpochId, + /// hash of an ancestor block of the block that has missing chunks + /// this is used as an argument for `request_chunks_for_orphan` + /// see comments in `request_chunks_for_orphan` for what `ancestor_hash` is used for + pub ancestor_hash: CryptoHash, +} + +impl Debug for OrphanMissingChunks { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("OrphanMissingChunks") + .field("epoch_id", &self.epoch_id) + .field("ancestor_hash", &self.ancestor_hash) + .field("num_missing_chunks", &self.missing_chunks.len()) + .finish() + } +} + +impl Chain { + pub fn save_orphan( + &mut self, + block: MaybeValidated, + provenance: Provenance, + added: Option, + requested_missing_chunks: bool, + ) { + let block_hash = *block.hash(); + if !self.orphans.contains(block.hash()) { + self.orphans.add( + Orphan { block, provenance, added: added.unwrap_or(StaticClock::instant()) }, + requested_missing_chunks, + ); + } + + debug!( + target: "chain", + "Process block: orphan: {:?}, # orphans {}{}", + block_hash, + self.orphans.len(), + if self.orphans.len_evicted() > 0 { + format!(", # evicted {}", self.orphans.len_evicted()) + } else { + String::new() + }, + ); + } + + /// Check if we can request chunks for this orphan. Conditions are + /// 1) Orphans that with outstanding missing chunks request has not exceed `MAX_ORPHAN_MISSING_CHUNKS` + /// 2) we haven't already requested missing chunks for the orphan + /// 3) All the `NUM_ORPHAN_ANCESTORS_CHECK` immediate parents of the block are either accepted, + /// or orphans or in `blocks_with_missing_chunks` + /// 4) Among the `NUM_ORPHAN_ANCESTORS_CHECK` immediate parents of the block at least one is + /// accepted(is in store), call it `ancestor` + /// 5) The next block of `ancestor` has the same epoch_id as the orphan block + /// (This is because when requesting chunks, we will use `ancestor` hash instead of the + /// previous block hash of the orphan to decide epoch id) + /// 6) The orphan has missing chunks + pub fn should_request_chunks_for_orphan( + &mut self, + me: &Option, + orphan: &Block, + ) -> Option { + // 1) Orphans that with outstanding missing chunks request has not exceed `MAX_ORPHAN_MISSING_CHUNKS` + // 2) we haven't already requested missing chunks for the orphan + if !self.orphans.can_request_missing_chunks_for_orphan(orphan.hash()) { + return None; + } + let mut block_hash = *orphan.header().prev_hash(); + for _ in 0..NUM_ORPHAN_ANCESTORS_CHECK { + // 3) All the `NUM_ORPHAN_ANCESTORS_CHECK` immediate parents of the block are either accepted, + // or orphans or in `blocks_with_missing_chunks` + if let Some(block) = self.blocks_with_missing_chunks.get(&block_hash) { + block_hash = *block.prev_hash(); + continue; + } + if let Some(orphan) = self.orphans.get(&block_hash) { + block_hash = *orphan.prev_hash(); + continue; + } + // 4) Among the `NUM_ORPHAN_ANCESTORS_CHECK` immediate parents of the block at least one is + // accepted(is in store), call it `ancestor` + if self.get_block(&block_hash).is_ok() { + if let Ok(epoch_id) = self.epoch_manager.get_epoch_id_from_prev_block(&block_hash) { + // 5) The next block of `ancestor` has the same epoch_id as the orphan block + if &epoch_id == orphan.header().epoch_id() { + // 6) The orphan has missing chunks + if let Err(e) = self.ping_missing_chunks(me, block_hash, orphan) { + return match e { + Error::ChunksMissing(missing_chunks) => { + debug!(target:"chain", "Request missing chunks for orphan {:?} {:?}", orphan.hash(), missing_chunks.iter().map(|chunk|{(chunk.shard_id(), chunk.chunk_hash())}).collect::>()); + Some(OrphanMissingChunks { + missing_chunks, + epoch_id, + ancestor_hash: block_hash, + }) + } + _ => None, + }; + } + } + } + return None; + } + return None; + } + None + } + + /// only used for test + pub fn check_orphan_partial_chunks_requested(&self, block_hash: &CryptoHash) -> bool { + self.orphans.orphans_requested_missing_chunks.contains(block_hash) + } + + /// Check for orphans that are ready to be processed or request missing chunks, process these blocks. + /// `prev_hash`: hash of the block that is just accepted + /// `block_accepted`: callback to be called when an orphan is accepted + /// `block_misses_chunks`: callback to be called when an orphan is added to the pool of blocks + /// that have missing chunks + /// `orphan_misses_chunks`: callback to be called when it is ready to request missing chunks for + /// an orphan + /// `on_challenge`: callback to be called when an orphan should be challenged + pub fn check_orphans( + &mut self, + me: &Option, + prev_hash: CryptoHash, + block_processing_artifacts: &mut BlockProcessingArtifact, + apply_chunks_done_callback: DoneApplyChunkCallback, + ) { + let _span = debug_span!( + target: "chain", + "check_orphans", + ?prev_hash, + num_orphans = self.orphans.len()) + .entered(); + // Check if there are orphans we can process. + // check within the descendents of `prev_hash` to see if there are orphans there that + // are ready to request missing chunks for + let orphans_to_check = + self.orphans.get_orphans_within_depth(prev_hash, NUM_ORPHAN_ANCESTORS_CHECK); + for orphan_hash in orphans_to_check { + let orphan = self.orphans.get(&orphan_hash).unwrap().block.clone(); + if let Some(orphan_missing_chunks) = self.should_request_chunks_for_orphan(me, &orphan) + { + block_processing_artifacts.orphans_missing_chunks.push(orphan_missing_chunks); + self.orphans.mark_missing_chunks_requested_for_orphan(orphan_hash); + } + } + if let Some(orphans) = self.orphans.remove_by_prev_hash(prev_hash) { + debug!(target: "chain", found_orphans = orphans.len(), "Check orphans"); + for orphan in orphans.into_iter() { + let block_hash = orphan.hash(); + self.blocks_delay_tracker + .mark_block_unorphaned(&block_hash, StaticClock::instant()); + let res = self.start_process_block_async( + me, + orphan.block, + orphan.provenance, + block_processing_artifacts, + apply_chunks_done_callback.clone(), + ); + if let Err(err) = res { + debug!(target: "chain", "Orphan {:?} declined, error: {:?}", block_hash, err); + } + } + debug!( + target: "chain", + remaining_orphans=self.orphans.len(), + "Check orphans", + ); + } + } + + /// Returns number of orphans currently in the orphan pool. + #[inline] + pub fn orphans_len(&self) -> usize { + self.orphans.len() + } + + /// Returns number of evicted orphans. + #[inline] + pub fn orphans_evicted_len(&self) -> usize { + self.orphans.len_evicted() + } + + /// Check if hash is for a known orphan. + #[inline] + pub fn is_orphan(&self, hash: &CryptoHash) -> bool { + self.orphans.contains(hash) + } +} diff --git a/chain/chain/src/resharding.rs b/chain/chain/src/resharding.rs new file mode 100644 index 000000000..ad201ac31 --- /dev/null +++ b/chain/chain/src/resharding.rs @@ -0,0 +1,475 @@ +/// Implementation for all resharding logic. +/// ReshardingRequest and ReshardingResponse are exchanged across the client_actor and SyncJobsActor. +/// build_state_for_split_shards_preprocessing and build_state_for_split_shards_postprocessing are handled +/// by the client_actor while the heavy resharding build_state_for_split_shards is done by SyncJobsActor +/// so as to not affect client. +use crate::metrics::{ + ReshardingStatus, RESHARDING_BATCH_APPLY_TIME, RESHARDING_BATCH_COMMIT_TIME, + RESHARDING_BATCH_COUNT, RESHARDING_BATCH_PREPARE_TIME, RESHARDING_BATCH_SIZE, + RESHARDING_STATUS, +}; +use crate::Chain; +use itertools::Itertools; +use unc_chain_configs::{MutableConfigValue, ReshardingConfig, ReshardingHandle}; +use unc_chain_primitives::error::Error; +use unc_primitives::errors::StorageError::StorageInconsistentState; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::{account_id_to_shard_uid, ShardLayout}; +use unc_primitives::state::FlatStateValue; +use unc_primitives::types::chunk_extra::ChunkExtra; +use unc_primitives::types::{AccountId, ShardId, StateRoot}; +use unc_store::flat::{ + store_helper, BlockInfo, FlatStorageError, FlatStorageManager, FlatStorageReadyStatus, + FlatStorageStatus, +}; +use unc_store::resharding::get_delayed_receipts; +use unc_store::trie::SnapshotError; +use unc_store::{ShardTries, ShardUId, StorageError, Store, Trie, TrieDBStorage, TrieStorage}; +use std::collections::{HashMap, HashSet}; +use std::fmt::{Debug, Formatter}; +use std::sync::Arc; +use std::time::Duration; +use tracing::debug; + +/// ReshardingRequest has all the information needed to start a resharding job. This message is sent +/// from ClientActor to SyncJobsActor. We do not want to stall the ClientActor with a long running +/// resharding job. The SyncJobsActor is helpful for handling such long running jobs. +#[derive(actix::Message)] +#[rtype(result = "()")] +pub struct ReshardingRequest { + pub tries: Arc, + // The block hash of the first block of the epoch. + pub sync_hash: CryptoHash, + // The prev hash of the sync_hash. We want the state at that block hash. + pub prev_hash: CryptoHash, + // The prev prev hash of the sync_hash. The state snapshot should be saved at that block hash. + pub prev_prev_hash: CryptoHash, + // Parent shardUId to be split into child shards. + pub shard_uid: ShardUId, + // The state root of the parent ShardUId. This is different from block sync_hash + pub state_root: StateRoot, + // The shard layout in the next epoch. + pub next_epoch_shard_layout: ShardLayout, + // Time we've spent polling for the state snapshot to be ready. We autofail after a certain time. + pub curr_poll_time: Duration, + // Configuration for resharding. Can be used to throttle resharding if needed. + pub config: MutableConfigValue, + // A handle that allows the main process to interrupt resharding if needed. + // This typically happens when the main process is interrupted. + pub handle: ReshardingHandle, +} + +// Skip `runtime_adapter`, because it's a complex object that has complex logic +// and many fields. +impl Debug for ReshardingRequest { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ReshardingRequest") + .field("tries", &"") + .field("sync_hash", &self.sync_hash) + .field("prev_hash", &self.prev_hash) + .field("prev_prev_hash", &self.prev_prev_hash) + .field("shard_uid", &self.shard_uid) + .field("state_root", &self.state_root) + .field("next_epoch_shard_layout_version", &self.next_epoch_shard_layout.version()) + .field("curr_poll_time", &self.curr_poll_time) + .finish() + } +} + +// ReshardingResponse is the response sent from SyncJobsActor to ClientActor once resharding is completed. +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub struct ReshardingResponse { + pub sync_hash: CryptoHash, + pub shard_id: ShardId, + pub new_state_roots: Result, Error>, +} + +fn get_checked_account_id_to_shard_uid_fn( + shard_uid: ShardUId, + new_shards: Vec, + next_epoch_shard_layout: ShardLayout, +) -> impl Fn(&AccountId) -> ShardUId { + let split_shard_ids: HashSet<_> = new_shards.into_iter().collect(); + move |account_id: &AccountId| { + let new_shard_uid = account_id_to_shard_uid(account_id, &next_epoch_shard_layout); + // check that all accounts in the shard are mapped the shards that this shard will split + // to according to shard layout + assert!( + split_shard_ids.contains(&new_shard_uid), + "Inconsistent shard_layout specs. Account {:?} in shard {:?} and in shard {:?}, but the former is not parent shard for the latter", + account_id, + shard_uid, + new_shard_uid, + ); + new_shard_uid + } +} + +// Format of the trie key, value pair that is used in tries.add_values_to_children_states() function +type TrieEntry = (Vec, Option>); + +struct TrieUpdateBatch { + entries: Vec, + size: u64, +} + +// Function to return batches of trie key, value pairs from flat storage iter. We return None at the end of iter. +// The batch size is roughly batch_memory_limit. +fn get_trie_update_batch( + config: &ReshardingConfig, + iter: &mut impl Iterator, Option>), FlatStorageError>>, +) -> Result, FlatStorageError> { + let mut size: u64 = 0; + let mut entries = Vec::new(); + while let Some(item) = iter.next() { + let (key, value) = item?; + size += key.len() as u64 + value.as_ref().map_or(0, |v| v.len() as u64); + entries.push((key, value)); + if size > config.batch_size.as_u64() { + break; + } + } + if entries.is_empty() { + Ok(None) + } else { + Ok(Some(TrieUpdateBatch { entries, size })) + } +} + +fn apply_delayed_receipts<'a>( + config: &ReshardingConfig, + tries: &ShardTries, + orig_shard_uid: ShardUId, + orig_state_root: StateRoot, + state_roots: HashMap, + account_id_to_shard_uid: &(dyn Fn(&AccountId) -> ShardUId + 'a), +) -> Result, Error> { + let orig_trie_update = tries.new_trie_update_view(orig_shard_uid, orig_state_root); + + let mut start_index = None; + let mut new_state_roots = state_roots; + while let Some((next_index, receipts)) = + get_delayed_receipts(&orig_trie_update, start_index, config.batch_size)? + { + let (store_update, updated_state_roots) = tries.apply_delayed_receipts_to_children_states( + &new_state_roots, + &receipts, + account_id_to_shard_uid, + )?; + new_state_roots = updated_state_roots; + start_index = Some(next_index); + store_update.commit()?; + } + + Ok(new_state_roots) +} + +// function to set up flat storage status to Ready after a resharding event +// TODO(resharding) : Consolidate this with setting up flat storage during state sync logic +fn set_flat_storage_state( + store: Store, + flat_storage_manager: &FlatStorageManager, + shard_uid: ShardUId, + block_info: BlockInfo, +) -> Result<(), Error> { + let mut store_update = store.store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head: block_info }), + ); + store_update.commit()?; + flat_storage_manager.create_flat_storage_for_shard(shard_uid)?; + Ok(()) +} + +/// Helper function to read the value from flat storage. +/// It either returns the inlined value or reads ref value from the storage. +fn read_flat_state_value( + trie_storage: &TrieDBStorage, + flat_state_value: FlatStateValue, +) -> Vec { + match flat_state_value { + FlatStateValue::Ref(val) => trie_storage.retrieve_raw_bytes(&val.hash).unwrap().to_vec(), + FlatStateValue::Inlined(val) => val, + } +} + +impl Chain { + pub fn build_state_for_resharding_preprocessing( + &self, + sync_hash: &CryptoHash, + shard_id: ShardId, + resharding_scheduler: &dyn Fn(ReshardingRequest), + ) -> Result<(), Error> { + tracing::debug!(target: "resharding", ?shard_id, ?sync_hash, "preprocessing started"); + let block_header = self.get_block_header(sync_hash)?; + let shard_layout = self.epoch_manager.get_shard_layout(block_header.epoch_id())?; + let next_epoch_shard_layout = + self.epoch_manager.get_shard_layout(block_header.next_epoch_id())?; + assert_ne!(shard_layout, next_epoch_shard_layout); + + let shard_uid = ShardUId::from_shard_id_and_layout(shard_id, &shard_layout); + let prev_hash = block_header.prev_hash(); + let prev_block_header = self.get_block_header(prev_hash)?; + let prev_prev_hash = prev_block_header.prev_hash(); + let state_root = *self.get_chunk_extra(&prev_hash, &shard_uid)?.state_root(); + + resharding_scheduler(ReshardingRequest { + tries: Arc::new(self.runtime_adapter.get_tries()), + sync_hash: *sync_hash, + prev_hash: *prev_hash, + prev_prev_hash: *prev_prev_hash, + shard_uid, + state_root, + next_epoch_shard_layout, + curr_poll_time: Duration::ZERO, + config: self.resharding_config.clone(), + handle: self.resharding_handle.clone(), + }); + + RESHARDING_STATUS + .with_label_values(&[&shard_uid.to_string()]) + .set(ReshardingStatus::Scheduled.into()); + Ok(()) + } + + /// Function to check whether the snapshot is ready for resharding or not. We return true if the snapshot is not + /// ready and we need to retry/reschedule the resharding job. + pub fn retry_build_state_for_split_shards(resharding_request: &ReshardingRequest) -> bool { + let ReshardingRequest { tries, prev_prev_hash, curr_poll_time, config, .. } = + resharding_request; + let config = config.get(); + + // Do not retry if we have spent more than max_poll_time + // The error would be caught in build_state_for_split_shards and propagated to client actor + if curr_poll_time > &config.max_poll_time { + tracing::warn!(target: "resharding", ?curr_poll_time, ?config.max_poll_time, "exceeded max poll time while waiting for snapshot"); + return false; + } + + let state_snapshot = tries.get_state_snapshot(prev_prev_hash); + if let Err(err) = state_snapshot { + tracing::debug!(target: "resharding", ?err, "state snapshot is not ready"); + return match err { + SnapshotError::SnapshotNotFound(_) => true, + SnapshotError::LockWouldBlock => true, + SnapshotError::IncorrectSnapshotRequested(_, _) => false, + SnapshotError::Other(_) => false, + }; + } + + // The snapshot is Ok, no need to retry. + return false; + } + + pub fn build_state_for_split_shards( + resharding_request: ReshardingRequest, + ) -> ReshardingResponse { + let shard_uid = resharding_request.shard_uid; + let shard_id = shard_uid.shard_id(); + let sync_hash = resharding_request.sync_hash; + let new_state_roots = Self::build_state_for_split_shards_impl(resharding_request); + match &new_state_roots { + Ok(_) => {} + Err(err) => { + tracing::error!(target: "resharding", ?shard_uid, ?err, "Resharding failed, manual recovery is necessary!"); + RESHARDING_STATUS + .with_label_values(&[&shard_uid.to_string()]) + .set(ReshardingStatus::Failed.into()); + } + } + ReshardingResponse { shard_id, sync_hash, new_state_roots } + } + + fn build_state_for_split_shards_impl( + resharding_request: ReshardingRequest, + ) -> Result, Error> { + let ReshardingRequest { + tries, + prev_hash, + prev_prev_hash, + shard_uid, + state_root, + next_epoch_shard_layout, + config, + handle, + .. + } = resharding_request; + tracing::debug!(target: "resharding", config=?config.get(), ?shard_uid, "build_state_for_split_shards_impl starting"); + + let shard_id = shard_uid.shard_id(); + let new_shards = next_epoch_shard_layout + .get_children_shards_uids(shard_id) + .ok_or(Error::InvalidShardId(shard_id))?; + let mut state_roots: HashMap<_, _> = + new_shards.iter().map(|shard_uid| (*shard_uid, Trie::EMPTY_ROOT)).collect(); + + RESHARDING_STATUS + .with_label_values(&[&shard_uid.to_string()]) + .set(ReshardingStatus::BuildingState.into()); + + // Build the required iterator from flat storage and delta changes. Note that we are + // working with iterators as we don't want to have all the state in memory at once. + // + // Iterator is built by chaining the following: + // 1. Flat storage iterator from the snapshot state as of `prev_prev_hash`. + // 2. Delta changes iterator from the snapshot state as of `prev_hash`. + // + // The snapshot when created has the flat head as of `prev_prev_hash`, i.e. the hash as + // of the second last block of the previous epoch. Hence we need to append the detla + // changes on top of it. + let (snapshot_store, flat_storage_manager) = tries + .get_state_snapshot(&prev_prev_hash) + .map_err(|err| StorageInconsistentState(err.to_string()))?; + let flat_storage_chunk_view = flat_storage_manager.chunk_view(shard_uid, prev_prev_hash); + let flat_storage_chunk_view = flat_storage_chunk_view.ok_or_else(|| { + StorageInconsistentState("Chunk view missing for snapshot flat storage".to_string()) + })?; + // Get the flat storage iter and wrap the value in Optional::Some to + // match the delta iterator so that they can be chained. + let flat_storage_iter = flat_storage_chunk_view.iter_flat_state_entries(None, None); + let flat_storage_iter = flat_storage_iter.map_ok(|(key, value)| (key, Some(value))); + + // Get the delta iter and wrap the items in Result to match the flat + // storage iter so that they can be chained. + let delta = store_helper::get_delta_changes(&snapshot_store, shard_uid, prev_hash) + .map_err(|err| StorageInconsistentState(err.to_string()))?; + let delta = delta.ok_or_else(|| { + StorageInconsistentState("Delta missing for snapshot flat storage".to_string()) + })?; + let delta_iter = delta.0.into_iter(); + let delta_iter = delta_iter.map(|item| Ok(item)); + + // chain the flat storage and flat storage delta iterators + let iter = flat_storage_iter.chain(delta_iter); + + // map the iterator to read the values + let trie_storage = TrieDBStorage::new(tries.get_store(), shard_uid); + let iter = iter.map_ok(move |(key, value)| { + (key, value.map(|value| read_flat_state_value(&trie_storage, value))) + }); + + // function to map account id to shard uid in range of child shards + let checked_account_id_to_shard_uid = + get_checked_account_id_to_shard_uid_fn(shard_uid, new_shards, next_epoch_shard_layout); + + let shard_uid_string = shard_uid.to_string(); + let metrics_labels = [shard_uid_string.as_str()]; + + // Once we build the iterator, we break it into batches using the get_trie_update_batch function. + let mut iter = iter; + loop { + if !handle.get() { + // The keep_going is set to false, interrupt processing. + tracing::info!(target: "resharding", ?shard_uid, "build_state_for_split_shards_impl interrupted"); + return Err(Error::Other("Resharding interrupted.".to_string())); + } + // Prepare the batch. + let batch = { + let histogram = RESHARDING_BATCH_PREPARE_TIME.with_label_values(&metrics_labels); + let _timer = histogram.start_timer(); + let batch = get_trie_update_batch(&config.get(), &mut iter); + let batch = batch.map_err(Into::::into)?; + let Some(batch) = batch else { break }; + batch + }; + + // Apply the batch - add values to the children shards. + let TrieUpdateBatch { entries, size } = batch; + let store_update = { + let histogram = RESHARDING_BATCH_APPLY_TIME.with_label_values(&metrics_labels); + let _timer = histogram.start_timer(); + // TODO(#9435): This is highly inefficient as for each key in the batch, we are parsing the account_id + // A better way would be to use the boundary account to construct the from and to key range for flat storage iterator + let (store_update, new_state_roots) = tries.add_values_to_children_states( + &state_roots, + entries, + &checked_account_id_to_shard_uid, + )?; + state_roots = new_state_roots; + store_update + }; + + // Commit the store update. + { + let histogram = RESHARDING_BATCH_COMMIT_TIME.with_label_values(&metrics_labels); + let _timer = histogram.start_timer(); + store_update.commit()?; + } + + RESHARDING_BATCH_COUNT.with_label_values(&metrics_labels).inc(); + RESHARDING_BATCH_SIZE.with_label_values(&metrics_labels).add(size as i64); + + // sleep between batches in order to throttle resharding and leave + // some resource for the regular node operation + std::thread::sleep(config.get().batch_delay); + } + + state_roots = apply_delayed_receipts( + &config.get(), + &tries, + shard_uid, + state_root, + state_roots, + &checked_account_id_to_shard_uid, + )?; + + tracing::debug!(target: "resharding", ?shard_uid, "build_state_for_split_shards_impl finished"); + Ok(state_roots) + } + + pub fn build_state_for_split_shards_postprocessing( + &mut self, + shard_uid: ShardUId, + sync_hash: &CryptoHash, + state_roots: HashMap, + ) -> Result<(), Error> { + let block_header = self.get_block_header(sync_hash)?; + let prev_hash = block_header.prev_hash(); + + let child_shard_uids = state_roots.keys().cloned().collect_vec(); + self.initialize_flat_storage(&prev_hash, &child_shard_uids)?; + + let mut chain_store_update = self.mut_chain_store().store_update(); + for (shard_uid, state_root) in state_roots { + // here we store the state roots in chunk_extra in the database for later use + let chunk_extra = ChunkExtra::new_with_only_state_root(&state_root); + chain_store_update.save_chunk_extra(&prev_hash, &shard_uid, chunk_extra); + debug!(target:"resharding", "Finish building resharding for shard {:?} {:?} {:?} ", shard_uid, prev_hash, state_root); + } + chain_store_update.commit()?; + + RESHARDING_STATUS + .with_label_values(&[&shard_uid.to_string()]) + .set(ReshardingStatus::Finished.into()); + + Ok(()) + } + + // Here we iterate over all the child shards and initialize flat storage for them by calling set_flat_storage_state + // Note that this function is called on the current_block which is the first block the next epoch. + // We set the flat_head as the prev_block as after resharding, the state written to flat storage corresponds to the + // state as of prev_block, and that's the convention that we follow. + fn initialize_flat_storage( + &self, + prev_hash: &CryptoHash, + child_shard_uids: &[ShardUId], + ) -> Result<(), Error> { + let prev_block_header = self.get_block_header(prev_hash)?; + let prev_block_info = BlockInfo { + hash: *prev_block_header.hash(), + prev_hash: *prev_block_header.prev_hash(), + height: prev_block_header.height(), + }; + + // create flat storage for child shards + let flat_storage_manager = self.runtime_adapter.get_flat_storage_manager(); + for shard_uid in child_shard_uids { + let store = self.runtime_adapter.store().clone(); + set_flat_storage_state(store, &flat_storage_manager, *shard_uid, prev_block_info)?; + } + Ok(()) + } +} diff --git a/chain/chain/src/sharding.rs b/chain/chain/src/sharding.rs new file mode 100644 index 000000000..396cb5cdb --- /dev/null +++ b/chain/chain/src/sharding.rs @@ -0,0 +1,28 @@ +use unc_primitives::hash::CryptoHash; +use rand::seq::SliceRandom; +use rand::SeedableRng; +use rand_chacha::ChaCha20Rng; + +pub fn shuffle_receipt_proofs( + receipt_proofs: &mut Vec, + block_hash: &CryptoHash, +) { + let mut slice = [0u8; 32]; + slice.copy_from_slice(block_hash.as_ref()); + let mut rng: ChaCha20Rng = SeedableRng::from_seed(slice); + receipt_proofs.shuffle(&mut rng); +} + +#[cfg(test)] +mod tests { + use crate::sharding::shuffle_receipt_proofs; + use unc_primitives::hash::CryptoHash; + + #[test] + pub fn receipt_randomness_reproducibility() { + // Sanity check that the receipt shuffling implementation does not change. + let mut receipt_proofs = vec![0, 1, 2, 3, 4, 5, 6]; + shuffle_receipt_proofs(&mut receipt_proofs, &CryptoHash::hash_bytes(&[1, 2, 3, 4, 5])); + assert_eq!(receipt_proofs, vec![2, 3, 1, 4, 0, 5, 6],); + } +} diff --git a/chain/chain/src/state_request_tracker.rs b/chain/chain/src/state_request_tracker.rs new file mode 100644 index 000000000..69fc304f4 --- /dev/null +++ b/chain/chain/src/state_request_tracker.rs @@ -0,0 +1,59 @@ +use crate::metrics; +use crate::Chain; +use lru::LruCache; +use unc_primitives::{ + hash::CryptoHash, + types::ShardId, + views::{PartElapsedTimeView, RequestedStatePartsView}, +}; +use std::collections::HashMap; + +const REQUESTED_STATE_PARTS_CACHE_SIZE: usize = 4; + +//Track the state parts that are already requested for debugging +//This struct provides methods to save and retrieve the information +//along with time elapsed to create state part. +//See https://github.com/utnet-org/utility/issues/7991 for more details +#[derive(Debug)] +pub(crate) struct StateRequestTracker { + requested_state_parts: LruCache>>, +} + +impl StateRequestTracker { + pub(crate) fn new() -> Self { + StateRequestTracker { + requested_state_parts: LruCache::new(REQUESTED_STATE_PARTS_CACHE_SIZE), + } + } + + pub(crate) fn save_state_part_elapsed( + &mut self, + crypto_hash: &CryptoHash, + shard_id: &ShardId, + part_id: &u64, + elapsed_ms: u128, + ) { + self.requested_state_parts.get_or_insert(*crypto_hash, || HashMap::new()); + let parts_per_shard = self.requested_state_parts.get_mut(crypto_hash).unwrap(); + let elapsed = parts_per_shard.entry(*shard_id).or_insert_with(|| vec![]); + elapsed.push(PartElapsedTimeView::new(part_id, elapsed_ms)); + metrics::STATE_PART_ELAPSED + .with_label_values(&[&shard_id.to_string()]) + .observe(elapsed_ms as f64 / 1000.); + } +} + +impl Chain { + pub fn get_requested_state_parts(&self) -> Vec { + let result: Vec = self + .requested_state_parts + .requested_state_parts + .iter() + .map(|(crypto_hash, parts_per_shard)| RequestedStatePartsView { + block_hash: *crypto_hash, + shard_requested_parts: parts_per_shard.clone(), + }) + .collect(); + return result; + } +} diff --git a/chain/chain/src/state_snapshot_actor.rs b/chain/chain/src/state_snapshot_actor.rs new file mode 100644 index 000000000..d58c0a54b --- /dev/null +++ b/chain/chain/src/state_snapshot_actor.rs @@ -0,0 +1,198 @@ +use actix::{AsyncContext, Context}; +use unc_async::messaging::CanSend; +use unc_network::types::{NetworkRequests, PeerManagerAdapter, PeerManagerMessageRequest}; +use unc_o11y::{handler_debug_span, OpenTelemetrySpanExt, WithSpanContext, WithSpanContextExt}; +use unc_performance_metrics_macros::perf; +use unc_primitives::block::Block; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::types::{EpochHeight, ShardId}; +use unc_store::flat::FlatStorageManager; +use unc_store::ShardTries; +use std::sync::Arc; + +/// Runs tasks related to state snapshots. +/// There are three main handlers in StateSnapshotActor and they are called in sequence +/// 1. DeleteSnapshotRequest: deletes a snapshot and optionally calls CreateSnapshotRequest. +/// 2. CreateSnapshotRequest: creates a new snapshot and optionally calls CompactSnapshotRequest based on config. +/// 3. CompactSnapshotRequest: compacts a snapshot store. +pub struct StateSnapshotActor { + flat_storage_manager: FlatStorageManager, + network_adapter: PeerManagerAdapter, + tries: ShardTries, +} + +impl StateSnapshotActor { + pub fn new( + flat_storage_manager: FlatStorageManager, + network_adapter: PeerManagerAdapter, + tries: ShardTries, + ) -> Self { + Self { flat_storage_manager, network_adapter, tries } + } +} + +impl actix::Actor for StateSnapshotActor { + type Context = Context; +} + +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +struct DeleteAndMaybeCreateSnapshotRequest { + /// Optionally send request to create a new snapshot after deleting any existing snapshots. + create_snapshot_request: Option, +} + +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +struct CreateSnapshotRequest { + /// prev_hash of the last processed block. + prev_block_hash: CryptoHash, + /// epoch height associated with prev_block_hash + epoch_height: EpochHeight, + /// Shards that need to be present in the snapshot. + shard_uids: Vec, + /// Last block of the prev epoch. + block: Block, +} + +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +struct CompactSnapshotRequest {} + +impl actix::Handler> for StateSnapshotActor { + type Result = (); + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + context: &mut Context, + ) { + let (_span, msg) = handler_debug_span!(target: "state_snapshot", msg); + tracing::debug!(target: "state_snapshot", ?msg); + + // We don't need to acquire any locks on flat storage or snapshot. + let DeleteAndMaybeCreateSnapshotRequest { create_snapshot_request } = msg; + self.tries.delete_state_snapshot(); + + // Optionally send a create_snapshot_request after deletion + if let Some(create_snapshot_request) = create_snapshot_request { + context.address().do_send(create_snapshot_request.with_span_context()); + } + } +} + +impl actix::Handler> for StateSnapshotActor { + type Result = (); + + #[perf] + fn handle(&mut self, msg: WithSpanContext, context: &mut Context) { + let (_span, msg) = handler_debug_span!(target: "state_snapshot", msg); + tracing::debug!(target: "state_snapshot", ?msg); + + let CreateSnapshotRequest { prev_block_hash, epoch_height, shard_uids, block } = msg; + let res = self.tries.create_state_snapshot(prev_block_hash, &shard_uids, &block); + + // Unlocking flat state head can be done asynchronously in state_snapshot_actor. + // The next flat storage update will bring flat storage to latest head. + if !self.flat_storage_manager.set_flat_state_updates_mode(true) { + tracing::error!(target: "state_snapshot", ?prev_block_hash, ?shard_uids, "Failed to unlock flat state updates"); + } + match res { + Ok(res_shard_uids) => { + if let Some(res_shard_uids) = res_shard_uids { + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::SnapshotHostInfo { + sync_hash: prev_block_hash, + epoch_height, + shards: res_shard_uids + .iter() + .map(|uid| uid.shard_id as ShardId) + .collect(), + }, + )); + } + + if self.tries.state_snapshot_config().compaction_enabled { + context.address().do_send(CompactSnapshotRequest {}.with_span_context()); + } else { + tracing::info!(target: "state_snapshot", "State snapshot ready, not running compaction."); + } + } + Err(err) => { + tracing::error!(target: "state_snapshot", ?err, "State snapshot creation failed") + } + } + } +} + +/// Runs compaction of the snapshot store. +impl actix::Handler> for StateSnapshotActor { + type Result = (); + + #[perf] + fn handle(&mut self, msg: WithSpanContext, _: &mut Context) { + let (_span, msg) = handler_debug_span!(target: "state_snapshot", msg); + tracing::debug!(target: "state_snapshot", ?msg); + + if let Err(err) = self.tries.compact_state_snapshot() { + tracing::error!(target: "state_snapshot", ?err, "State snapshot compaction failed"); + } else { + tracing::info!(target: "state_snapshot", "State snapshot compaction succeeded"); + } + } +} + +type MakeSnapshotCallback = + Arc, Block) -> () + Send + Sync + 'static>; + +type DeleteSnapshotCallback = Arc () + Send + Sync + 'static>; + +pub struct SnapshotCallbacks { + pub make_snapshot_callback: MakeSnapshotCallback, + pub delete_snapshot_callback: DeleteSnapshotCallback, +} + +/// Sends a request to make a state snapshot. +pub fn get_make_snapshot_callback( + state_snapshot_addr: Arc>, + flat_storage_manager: FlatStorageManager, +) -> MakeSnapshotCallback { + Arc::new(move |prev_block_hash, epoch_height, shard_uids, block| { + tracing::info!( + target: "state_snapshot", + ?prev_block_hash, + ?shard_uids, + "make_snapshot_callback sends `DeleteAndMaybeCreateSnapshotRequest` to state_snapshot_addr"); + // We need to stop flat head updates synchronously in the client thread. + // Async update in state_snapshot_actor and potentially lead to flat head progressing beyond prev_block_hash + if !flat_storage_manager.set_flat_state_updates_mode(false) { + tracing::error!(target: "state_snapshot", ?prev_block_hash, ?shard_uids, "Failed to lock flat state updates"); + return; + } + let create_snapshot_request = + CreateSnapshotRequest { prev_block_hash, epoch_height, shard_uids, block }; + state_snapshot_addr.do_send( + DeleteAndMaybeCreateSnapshotRequest { + create_snapshot_request: Some(create_snapshot_request), + } + .with_span_context(), + ); + }) +} + +/// Sends a request to delete a state snapshot. +pub fn get_delete_snapshot_callback( + state_snapshot_addr: Arc>, +) -> DeleteSnapshotCallback { + Arc::new(move || { + tracing::info!( + target: "state_snapshot", + "delete_snapshot_callback sends `DeleteAndMaybeCreateSnapshotRequest` to state_snapshot_addr"); + state_snapshot_addr.do_send( + DeleteAndMaybeCreateSnapshotRequest { create_snapshot_request: None } + .with_span_context(), + ); + }) +} diff --git a/chain/chain/src/store.rs b/chain/chain/src/store.rs new file mode 100644 index 000000000..dfef8cfbb --- /dev/null +++ b/chain/chain/src/store.rs @@ -0,0 +1,2934 @@ +use std::collections::hash_map::Entry; +use std::collections::{HashMap, HashSet}; +use std::io; + +use borsh::{BorshDeserialize, BorshSerialize}; +use chrono::Utc; +use unc_cache::CellLruCache; + +use unc_chain_primitives::error::Error; +use unc_epoch_manager::EpochManagerAdapter; +use unc_primitives::block::Tip; +use unc_primitives::checked_feature; +#[cfg(feature = "new_epoch_sync")] +use unc_primitives::epoch_manager::epoch_sync::EpochSyncInfo; +use unc_primitives::errors::InvalidTxError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::{MerklePath, PartialMerkleTree}; +use unc_primitives::receipt::Receipt; +use unc_primitives::shard_layout::account_id_to_shard_id; +use unc_primitives::shard_layout::{get_block_shard_uid, ShardLayout, ShardUId}; +use unc_primitives::sharding::{ + ChunkHash, EncodedShardChunk, PartialEncodedChunk, ReceiptProof, ShardChunk, ShardChunkHeader, + StateSyncInfo, +}; +use unc_primitives::state_sync::{ + ReceiptProofResponse, ShardStateSyncResponseHeader, StateHeaderKey, StateSyncDumpProgress, +}; +use unc_primitives::transaction::{ + ExecutionOutcomeWithId, ExecutionOutcomeWithIdAndProof, ExecutionOutcomeWithProof, + SignedTransaction, +}; +use unc_primitives::trie_key::{trie_key_parsers, TrieKey}; +use unc_primitives::types::chunk_extra::ChunkExtra; +use unc_primitives::types::{ + BlockExtra, BlockHeight, EpochId, NumBlocks, ShardId, StateChanges, StateChangesExt, + StateChangesForResharding, StateChangesKinds, StateChangesKindsExt, StateChangesRequest, +}; +use unc_primitives::utils::{ + get_block_shard_id, get_outcome_id_block_hash, get_outcome_id_block_hash_rev, index_to_bytes, + to_timestamp, +}; +use unc_primitives::version::ProtocolVersion; +use unc_primitives::views::LightClientBlockView; +use unc_store::{ + DBCol, KeyForStateChanges, Store, StoreUpdate, WrappedTrieChanges, CHUNK_TAIL_KEY, + FINAL_HEAD_KEY, FORK_TAIL_KEY, HEADER_HEAD_KEY, HEAD_KEY, LARGEST_TARGET_HEIGHT_KEY, + LATEST_KNOWN_KEY, TAIL_KEY, +}; + +use crate::byzantine_assert; +use crate::chunks_store::ReadOnlyChunksStore; +use crate::types::{Block, BlockHeader, LatestKnown}; +use unc_store::db::{StoreStatistics, STATE_SYNC_DUMP_KEY}; +use std::sync::Arc; + +/// lru cache size +#[cfg(not(feature = "no_cache"))] +const CACHE_SIZE: usize = 100; +#[cfg(not(feature = "no_cache"))] +const CHUNK_CACHE_SIZE: usize = 1024; + +#[cfg(feature = "no_cache")] +const CACHE_SIZE: usize = 1; +#[cfg(feature = "no_cache")] +const CHUNK_CACHE_SIZE: usize = 1; + +/// Accesses the chain store. Used to create atomic editable views that can be reverted. +pub trait ChainStoreAccess { + /// Returns underlying chain store + fn chain_store(&self) -> &ChainStore; + /// Returns underlaying store. + fn store(&self) -> &Store; + /// The chain head. + fn head(&self) -> Result; + /// The chain Blocks Tail height. + fn tail(&self) -> Result; + /// The chain Chunks Tail height. + fn chunk_tail(&self) -> Result; + /// Tail height of the fork cleaning process. + fn fork_tail(&self) -> Result; + /// Head of the header chain (not the same thing as head_header). + fn header_head(&self) -> Result; + /// Header of the block at the head of the block chain (not the same thing as header_head). + fn head_header(&self) -> Result; + /// The chain final head. It is guaranteed to be monotonically increasing. + fn final_head(&self) -> Result; + /// Largest approval target height sent by us + fn largest_target_height(&self) -> Result; + /// Get full block. + fn get_block(&self, h: &CryptoHash) -> Result; + /// Get full chunk. + fn get_chunk(&self, chunk_hash: &ChunkHash) -> Result, Error>; + /// Get partial chunk. + fn get_partial_chunk(&self, chunk_hash: &ChunkHash) -> Result, Error>; + /// Get full chunk from header, with possible error that contains the header for further retrieval. + fn get_chunk_clone_from_header(&self, header: &ShardChunkHeader) -> Result { + let shard_chunk_result = self.get_chunk(&header.chunk_hash()); + match shard_chunk_result { + Err(_) => { + return Err(Error::ChunksMissing(vec![header.clone()])); + } + Ok(shard_chunk) => { + byzantine_assert!(header.height_included() > 0 || header.height_created() == 0); + if header.height_included() == 0 && header.height_created() > 0 { + return Err(Error::Other(format!( + "Invalid header: {:?} for chunk {:?}", + header, shard_chunk + ))); + } + let mut shard_chunk_clone = ShardChunk::clone(&shard_chunk); + shard_chunk_clone.set_height_included(header.height_included()); + Ok(shard_chunk_clone) + } + } + } + /// Does this full block exist? + fn block_exists(&self, h: &CryptoHash) -> Result; + /// Does this chunk exist? + fn chunk_exists(&self, h: &ChunkHash) -> Result; + /// Get previous header. + fn get_previous_header(&self, header: &BlockHeader) -> Result; + /// GEt block extra for given block. + fn get_block_extra(&self, block_hash: &CryptoHash) -> Result, Error>; + /// Get chunk extra info for given block hash + shard id. + fn get_chunk_extra( + &self, + block_hash: &CryptoHash, + shard_uid: &ShardUId, + ) -> Result, Error>; + /// Get block header. + fn get_block_header(&self, h: &CryptoHash) -> Result; + /// Returns hash of the block on the main chain for given height. + fn get_block_hash_by_height(&self, height: BlockHeight) -> Result; + /// Returns hash of the first available block after genesis. + fn get_earliest_block_hash(&self) -> Result, Error> { + // To find the earliest available block we use the `tail` marker primarily + // used by garbage collection system. + // NOTE: `tail` is the block height at which we can say that there is + // at most 1 block available in the range from the genesis height to + // the tail. Thus, the strategy is to find the first block AFTER the tail + // height, and use the `prev_hash` to get the reference to the earliest + // block. + // The earliest block can be the genesis block. + let head_header_height = self.head_header()?.height(); + let tail = self.tail()?; + + // There is a corner case when there are no blocks after the tail, and + // the tail is in fact the earliest block available on the chain. + if let Ok(block_hash) = self.get_block_hash_by_height(tail) { + return Ok(Some(block_hash)); + } + for height in tail + 1..=head_header_height { + if let Ok(block_hash) = self.get_block_hash_by_height(height) { + let earliest_block_hash = *self.get_block_header(&block_hash)?.prev_hash(); + debug_assert!(matches!(self.block_exists(&earliest_block_hash), Ok(true))); + return Ok(Some(earliest_block_hash)); + } + } + Ok(None) + } + /// Returns block header from the current chain for given height if present. + fn get_block_header_by_height(&self, height: BlockHeight) -> Result { + let hash = self.get_block_hash_by_height(height)?; + self.get_block_header(&hash) + } + fn get_next_block_hash(&self, hash: &CryptoHash) -> Result; + fn get_epoch_light_client_block( + &self, + hash: &CryptoHash, + ) -> Result, Error>; + /// Returns a number of references for Block with `block_hash` + fn get_block_refcount(&self, block_hash: &CryptoHash) -> Result; + /// Returns block header from the current chain defined by `sync_hash` for given height if present. + fn get_block_header_on_chain_by_height( + &self, + sync_hash: &CryptoHash, + height: BlockHeight, + ) -> Result { + let mut header = self.get_block_header(sync_hash)?; + let mut hash = *sync_hash; + while header.height() > height { + hash = *header.prev_hash(); + header = self.get_block_header(&hash)?; + } + let header_height = header.height(); + if header_height < height { + return Err(Error::InvalidBlockHeight(header_height)); + } + self.get_block_header(&hash) + } + /// Returns resulting receipt for given block. + fn get_outgoing_receipts( + &self, + hash: &CryptoHash, + shard_id: ShardId, + ) -> Result>, Error>; + + fn get_incoming_receipts( + &self, + hash: &CryptoHash, + shard_id: ShardId, + ) -> Result>, Error>; + + /// Collect incoming receipts for shard `shard_id` from + /// the block at height `last_chunk_height_included` (non-inclusive) to the block `block_hash` (inclusive) + /// This is because the chunks for the shard are empty for the blocks in between, + /// so the receipts from these blocks are propagated + fn get_incoming_receipts_for_shard( + &self, + epoch_manager: &dyn EpochManagerAdapter, + mut shard_id: ShardId, + mut block_hash: CryptoHash, + last_chunk_height_included: BlockHeight, + ) -> Result, Error> { + let _span = + tracing::debug_span!(target: "chain", "get_incoming_receipts_for_shard", ?shard_id, ?block_hash, last_chunk_height_included).entered(); + + let mut ret = vec![]; + + let target_shard_id = shard_id; + let target_shard_layout = epoch_manager.get_shard_layout_from_prev_block(&block_hash)?; + + loop { + let header = self.get_block_header(&block_hash)?; + + if header.height() < last_chunk_height_included { + panic!("get_incoming_receipts_for_shard failed"); + } + + if header.height() == last_chunk_height_included { + break; + } + + let prev_hash = header.prev_hash(); + let shard_layout = epoch_manager.get_shard_layout_from_prev_block(&block_hash)?; + let prev_shard_layout = epoch_manager.get_shard_layout_from_prev_block(prev_hash)?; + + if shard_layout != prev_shard_layout { + let parent_shard_id = shard_layout.get_parent_shard_id(shard_id)?; + tracing::debug!( + target: "chain", + version = shard_layout.version(), + prev_version = prev_shard_layout.version(), + shard_id, + parent_shard_id, + "crossing epoch boundary with shard layout change, updating shard id" + ); + shard_id = parent_shard_id; + } + + let receipts_proofs = self.get_incoming_receipts(&block_hash, shard_id); + match receipts_proofs { + Ok(receipt_proofs) => { + tracing::debug!( + target: "chain", + "found receipts from block with missing chunks", + ); + + // If the shard layout changed we need to filter receipts to + // make sure we only include receipts where receiver belongs + // to the target shard id in the target shard layout. + let filtered_receipt_proofs = filter_incoming_receipts_for_shard( + &target_shard_layout, + target_shard_id, + receipt_proofs, + ); + + ret.push(ReceiptProofResponse(block_hash, filtered_receipt_proofs.into())); + } + Err(err) => { + tracing::debug!( + target: "chain", + ?err, + "could not find receipts from block with missing chunks" + ); + + // This can happen when all chunks are missing in a block + // and then we can safely assume that there aren't any + // incoming receipts. It would be nicer to explicitly check + // that condition rather than relying on errors when reading + // from the db. + ret.push(ReceiptProofResponse(block_hash, Arc::new(vec![]))); + } + } + + block_hash = *prev_hash; + } + + Ok(ret) + } + + /// Returns whether the block with the given hash was challenged + fn is_block_challenged(&self, hash: &CryptoHash) -> Result; + + fn get_blocks_to_catchup(&self, prev_hash: &CryptoHash) -> Result, Error>; + + /// Returns encoded chunk if it's invalid otherwise None. + fn is_invalid_chunk( + &self, + chunk_hash: &ChunkHash, + ) -> Result>, Error>; + + /// Get destination shard id for receipt id. + fn get_shard_id_for_receipt_id(&self, receipt_id: &CryptoHash) -> Result; + + fn get_transaction( + &self, + tx_hash: &CryptoHash, + ) -> Result>, Error>; + + /// Fetch a receipt by id, if it is stored in the store. + /// + /// Note that not _all_ receipts are persisted. Some receipts are ephemeral, + /// get processed immediately after creation and don't even get to the + /// database. + fn get_receipt(&self, receipt_id: &CryptoHash) -> Result>, Error>; + + fn get_genesis_height(&self) -> BlockHeight; + + fn get_block_merkle_tree( + &self, + block_hash: &CryptoHash, + ) -> Result, Error>; + + fn get_block_hash_from_ordinal(&self, block_ordinal: NumBlocks) -> Result; + + fn get_block_merkle_tree_from_ordinal( + &self, + block_ordinal: NumBlocks, + ) -> Result, Error> { + let block_hash = self.get_block_hash_from_ordinal(block_ordinal)?; + self.get_block_merkle_tree(&block_hash) + } + + fn is_height_processed(&self, height: BlockHeight) -> Result; + + fn get_block_height(&self, hash: &CryptoHash) -> Result { + if hash == &CryptoHash::default() { + Ok(self.get_genesis_height()) + } else { + Ok(self.get_block_header(hash)?.height()) + } + } + + /// Get epoch id of the last block with existing chunk for the given shard id. + fn get_epoch_id_of_last_block_with_chunk( + &self, + epoch_manager: &dyn EpochManagerAdapter, + hash: &CryptoHash, + shard_id: ShardId, + ) -> Result { + let mut candidate_hash = *hash; + let mut shard_id = shard_id; + loop { + let block_header = self.get_block_header(&candidate_hash)?; + if block_header.chunk_mask()[shard_id as usize] { + break Ok(block_header.epoch_id().clone()); + } + candidate_hash = *block_header.prev_hash(); + shard_id = epoch_manager.get_prev_shard_ids(&candidate_hash, vec![shard_id])?[0]; + } + } +} + +/// Given a vector of receipts return only the receipts that should be assigned +/// to the target shard id in the target shard layout. Used when collecting the +/// incoming receipts and the shard layout changed. +fn filter_incoming_receipts_for_shard( + target_shard_layout: &ShardLayout, + target_shard_id: u64, + receipt_proofs: Arc>, +) -> Vec { + let mut filtered_receipt_proofs = vec![]; + for receipt_proof in receipt_proofs.iter() { + let mut filtered_receipts = vec![]; + let ReceiptProof(receipts, shard_proof) = receipt_proof.clone(); + for receipt in receipts { + let receiver_shard_id = + account_id_to_shard_id(&receipt.receiver_id, target_shard_layout); + if receiver_shard_id == target_shard_id { + tracing::trace!(target: "chain", receipt_id=?receipt.receipt_id, "including receipt"); + filtered_receipts.push(receipt); + } else { + tracing::trace!(target: "chain", receipt_id=?receipt.receipt_id, "excluding receipt"); + } + } + // TODO(resharding) adjust the shard proof accordingly + // currently this only matters for state sync + let receipt_proof = ReceiptProof(filtered_receipts, shard_proof); + filtered_receipt_proofs.push(receipt_proof); + } + filtered_receipt_proofs +} + +/// All chain-related database operations. +pub struct ChainStore { + store: Store, + /// Genesis block height. + genesis_height: BlockHeight, + /// Latest known. + latest_known: once_cell::unsync::OnceCell, + /// Current head of the chain + head: Option, + /// Tail height of the chain, + tail: Option, + /// Cache with headers. + pub(crate) headers: CellLruCache, BlockHeader>, + /// Cache with blocks. + pub(crate) blocks: CellLruCache, Block>, + /// Cache with chunks + pub(crate) chunks: CellLruCache, Arc>, + /// Cache with partial chunks + pub(crate) partial_chunks: CellLruCache, Arc>, + /// Cache with block extra. + pub(crate) block_extras: CellLruCache, Arc>, + /// Cache with chunk extra. + pub(crate) chunk_extras: CellLruCache, Arc>, + /// Cache with height to hash on the main chain. + height: CellLruCache, CryptoHash>, + /// Cache with height to block hash on any chain. + pub(crate) block_hash_per_height: + CellLruCache, Arc>>>, + /// Next block hashes for each block on the canonical chain + pub(crate) next_block_hashes: CellLruCache, CryptoHash>, + /// Light client blocks corresponding to the last finalized block of each epoch + epoch_light_client_blocks: CellLruCache, Arc>, + /// Cache with outgoing receipts. + pub(crate) outgoing_receipts: CellLruCache, Arc>>, + /// Cache with incoming receipts. + pub(crate) incoming_receipts: CellLruCache, Arc>>, + /// Invalid chunks. + pub(crate) invalid_chunks: CellLruCache, Arc>, + /// Mapping from receipt id to destination shard id + pub(crate) receipt_id_to_shard_id: CellLruCache, ShardId>, + /// Transactions + pub(crate) transactions: CellLruCache, Arc>, + /// Receipts + pub(crate) receipts: CellLruCache, Arc>, + /// Cache with Block Refcounts + pub(crate) block_refcounts: CellLruCache, u64>, + /// Cache of block hash -> block merkle tree at the current block + block_merkle_tree: CellLruCache, Arc>, + /// Cache of block ordinal to block hash. + block_ordinal_to_hash: CellLruCache, CryptoHash>, + /// Processed block heights. + pub(crate) processed_block_heights: CellLruCache, ()>, + /// save_trie_changes should be set to true iff + /// - archive if false - non-archival nodes need trie changes to perform garbage collection + /// - archive is true, cold_store is configured and migration to split_storage is finished - node + /// working in split storage mode needs trie changes in order to do garbage collection on hot. + save_trie_changes: bool, +} + +fn option_to_not_found(res: io::Result>, field_name: F) -> Result +where + F: std::string::ToString, +{ + match res { + Ok(Some(o)) => Ok(o), + Ok(None) => Err(Error::DBNotFoundErr(field_name.to_string())), + Err(e) => Err(e.into()), + } +} + +impl ChainStore { + pub fn new(store: Store, genesis_height: BlockHeight, save_trie_changes: bool) -> ChainStore { + ChainStore { + store, + genesis_height, + latest_known: once_cell::unsync::OnceCell::new(), + head: None, + tail: None, + blocks: CellLruCache::new(CACHE_SIZE), + headers: CellLruCache::new(CACHE_SIZE), + chunks: CellLruCache::new(CHUNK_CACHE_SIZE), + partial_chunks: CellLruCache::new(CHUNK_CACHE_SIZE), + block_extras: CellLruCache::new(CACHE_SIZE), + chunk_extras: CellLruCache::new(CACHE_SIZE), + height: CellLruCache::new(CACHE_SIZE), + block_hash_per_height: CellLruCache::new(CACHE_SIZE), + block_refcounts: CellLruCache::new(CACHE_SIZE), + next_block_hashes: CellLruCache::new(CACHE_SIZE), + epoch_light_client_blocks: CellLruCache::new(CACHE_SIZE), + outgoing_receipts: CellLruCache::new(CACHE_SIZE), + incoming_receipts: CellLruCache::new(CACHE_SIZE), + invalid_chunks: CellLruCache::new(CACHE_SIZE), + receipt_id_to_shard_id: CellLruCache::new(CHUNK_CACHE_SIZE), + transactions: CellLruCache::new(CHUNK_CACHE_SIZE), + receipts: CellLruCache::new(CHUNK_CACHE_SIZE), + block_merkle_tree: CellLruCache::new(CACHE_SIZE), + block_ordinal_to_hash: CellLruCache::new(CACHE_SIZE), + processed_block_heights: CellLruCache::new(CACHE_SIZE), + save_trie_changes, + } + } + + pub fn new_read_only_chunks_store(&self) -> ReadOnlyChunksStore { + ReadOnlyChunksStore::new(self.store.clone()) + } + + pub fn store_update(&mut self) -> ChainStoreUpdate<'_> { + ChainStoreUpdate::new(self) + } + + pub fn iterate_state_sync_infos(&self) -> Result, Error> { + self.store + .iter(DBCol::StateDlInfos) + .map(|item| match item { + Ok((k, v)) => Ok(( + CryptoHash::try_from(k.as_ref()).map_err(|_| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!("wrong key length: {k:?}"), + ) + })?, + StateSyncInfo::try_from_slice(v.as_ref())?, + )), + Err(err) => Err(err.into()), + }) + .collect() + } + + pub fn get_state_changes_for_resharding( + &self, + block_hash: &CryptoHash, + shard_id: ShardId, + ) -> Result { + let key = &get_block_shard_id(block_hash, shard_id); + option_to_not_found( + self.store.get_ser(DBCol::StateChangesForSplitStates, key), + format_args!("CONSOLIDATED STATE CHANGES: {}:{}", block_hash, shard_id), + ) + } + + /// Get outgoing receipts that will be *sent* from shard `shard_id` from block whose prev block + /// is `prev_block_hash` + /// Note that the meaning of outgoing receipts here are slightly different from + /// `save_outgoing_receipts` or `get_outgoing_receipts`. + /// There, outgoing receipts for a shard refers to receipts that are generated + /// from the shard from block `prev_block_hash`. + /// Here, outgoing receipts for a shard refers to receipts that will be sent from this shard + /// to other shards in the block after `prev_block_hash` + /// The difference of one block is important because shard layout may change between the previous + /// block and the current block and the meaning of `shard_id` will change. + /// + /// Note, the current way of implementation assumes that at least one chunk is generated before + /// shard layout are changed twice. This is not a problem right now because we are changing shard + /// layout for the first time for simple nightshade and generally not a problem if shard layout + /// changes very rarely. + /// But we need to implement a more theoretically correct algorithm if shard layouts will change + /// more often in the future + /// + pub fn get_outgoing_receipts_for_shard( + &self, + epoch_manager: &dyn EpochManagerAdapter, + prev_block_hash: CryptoHash, + shard_id: ShardId, + last_included_height: BlockHeight, + ) -> Result, Error> { + let shard_layout = epoch_manager.get_shard_layout_from_prev_block(&prev_block_hash)?; + let mut receipts_block_hash = prev_block_hash; + loop { + let block_header = self.get_block_header(&receipts_block_hash)?; + + if block_header.height() != last_included_height { + receipts_block_hash = *block_header.prev_hash(); + continue; + } + let receipts_shard_layout = epoch_manager.get_shard_layout(block_header.epoch_id())?; + + // get the shard from which the outgoing receipt were generated + let receipts_shard_id = if shard_layout != receipts_shard_layout { + shard_layout.get_parent_shard_id(shard_id)? + } else { + shard_id + }; + + let mut receipts = self + .get_outgoing_receipts(&receipts_block_hash, receipts_shard_id) + .map(|v| v.to_vec()) + .unwrap_or_default(); + + if shard_layout != receipts_shard_layout { + // the shard layout has changed so we need to reassign the outgoing receipts + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(&prev_block_hash)?; + let protocol_version = epoch_manager.get_epoch_protocol_version(&epoch_id)?; + Self::reassign_outgoing_receipts_for_resharding( + &mut receipts, + protocol_version, + &shard_layout, + shard_id, + receipts_shard_id, + )?; + } + + return Ok(receipts); + } + } + + fn reassign_outgoing_receipts_for_resharding( + receipts: &mut Vec, + protocol_version: ProtocolVersion, + shard_layout: &ShardLayout, + shard_id: u64, + receipts_shard_id: u64, + ) -> Result<(), Error> { + tracing::trace!(target: "resharding", ?protocol_version, shard_id, receipts_shard_id, "reassign_outgoing_receipts_for_resharding"); + // If simple nightshade v2 is enabled and stable use that. + if checked_feature!("stable", SimpleNightshadeV2, protocol_version) { + Self::reassign_outgoing_receipts_for_resharding_v2( + receipts, + shard_layout, + shard_id, + receipts_shard_id, + )?; + return Ok(()); + } + + // Otherwise use the old reassignment. Keep in mind it only works for + // 1 shard -> n shards reshardings, otherwise receipts get lost. + Self::reassign_outgoing_receipts_for_resharding_v1(receipts, shard_layout, shard_id)?; + Ok(()) + } + + /// Reassign the outgoing receipts from the parent shard to the children + /// shards. + /// + /// This method does it based on the "lowest child index" approach where it + /// assigns all the receipts from parent to the child shard with the lowest + /// index. It's meant to be used for the resharding from simple nightshade + /// with 4 shards to simple nightshade v2 with 5 shards and subsequent + /// reshardings. + /// + /// e.g. in the following resharding + /// 0->0', 1->1', 2->2', 3->3',4' + /// 0' will get all outgoing receipts from its parent 0 + /// 1' will get all outgoing receipts from its parent 1 + /// 2' will get all outgoing receipts from its parent 2 + /// 3' will get all outgoing receipts from its parent 3 + /// 4' will get no outgoing receipts from its parent 3 + /// All receipts are distributed to children, each exactly once. + fn reassign_outgoing_receipts_for_resharding_v2( + receipts: &mut Vec, + shard_layout: &ShardLayout, + shard_id: ShardId, + receipts_shard_id: ShardId, + ) -> Result<(), Error> { + let split_shard_ids = shard_layout.get_children_shards_ids(receipts_shard_id); + let split_shard_ids = + split_shard_ids.ok_or(Error::InvalidSplitShardsIds(shard_id, receipts_shard_id))?; + + // The target shard id is the split shard with the lowest shard id. + let target_shard_id = split_shard_ids.iter().min(); + let target_shard_id = + *target_shard_id.ok_or(Error::InvalidSplitShardsIds(shard_id, receipts_shard_id))?; + + if shard_id == target_shard_id { + // This shard_id is the lowest index child, it gets all the receipts. + Ok(()) + } else { + // This shard_id is not the lowest index child, it gets no receipts. + receipts.clear(); + Ok(()) + } + } + + /// Reassign the outgoing receipts from the parent shard to the children + /// shards. + /// + /// This method does it based on the "receipt receiver" approach where the + /// receipt is assigned to the shard of the receiver. + /// + /// This approach worked well for the 1->4 shards resharding but it doesn't + /// work for following reshardings. The reason is that it's only the child + /// shards that look at parents shard's outgoing receipts. If the receipt + /// receiver happens to not fall within one of the children shards then the + /// receipt is lost. + fn reassign_outgoing_receipts_for_resharding_v1( + receipts: &mut Vec, + shard_layout: &ShardLayout, + shard_id: ShardId, + ) -> Result<(), Error> { + receipts.retain(|receipt| { + account_id_to_shard_id(&receipt.receiver_id, &shard_layout) == shard_id + }); + Ok(()) + } + + /// For a given transaction, it expires if the block that the chunk points to is more than `validity_period` + /// ahead of the block that has `base_block_hash`. + pub fn check_transaction_validity_period( + &self, + prev_block_header: &BlockHeader, + base_block_hash: &CryptoHash, + validity_period: BlockHeight, + ) -> Result<(), InvalidTxError> { + // if both are on the canonical chain, comparing height is sufficient + // we special case this because it is expected that this scenario will happen in most cases. + let base_height = + self.get_block_header(base_block_hash).map_err(|_| InvalidTxError::Expired)?.height(); + let prev_height = prev_block_header.height(); + if let Ok(base_block_hash_by_height) = self.get_block_hash_by_height(base_height) { + if &base_block_hash_by_height == base_block_hash { + if let Ok(prev_hash) = self.get_block_hash_by_height(prev_height) { + if &prev_hash == prev_block_header.hash() { + if prev_height <= base_height + validity_period { + return Ok(()); + } else { + return Err(InvalidTxError::Expired); + } + } + } + } + } + + // if the base block height is smaller than `last_final_height` we only need to check + // whether the base block is the same as the one with that height on the canonical fork. + // Otherwise we walk back the chain to check whether base block is on the same chain. + let last_final_height = self + .get_block_height(prev_block_header.last_final_block()) + .map_err(|_| InvalidTxError::InvalidChain)?; + + if prev_height > base_height + validity_period { + Err(InvalidTxError::Expired) + } else if last_final_height >= base_height { + let base_block_hash_by_height = self + .get_block_hash_by_height(base_height) + .map_err(|_| InvalidTxError::InvalidChain)?; + if &base_block_hash_by_height == base_block_hash { + if prev_height <= base_height + validity_period { + Ok(()) + } else { + Err(InvalidTxError::Expired) + } + } else { + Err(InvalidTxError::InvalidChain) + } + } else { + let header = self + .get_block_header_on_chain_by_height(prev_block_header.hash(), base_height) + .map_err(|_| InvalidTxError::InvalidChain)?; + if header.hash() == base_block_hash { + Ok(()) + } else { + Err(InvalidTxError::InvalidChain) + } + } + } +} + +impl ChainStore { + /// Returns outcomes on all forks generated by applying transaction or + /// receipt with the given id. + pub fn get_outcomes_by_id( + &self, + id: &CryptoHash, + ) -> Result, Error> { + self.store + .iter_prefix_ser::( + DBCol::TransactionResultForBlock, + id.as_ref(), + ) + .map(|item| { + let (key, outcome_with_proof) = item?; + let (_, block_hash) = get_outcome_id_block_hash_rev(key.as_ref())?; + Ok(ExecutionOutcomeWithIdAndProof { + proof: outcome_with_proof.proof, + block_hash, + outcome_with_id: ExecutionOutcomeWithId { + id: *id, + outcome: outcome_with_proof.outcome, + }, + }) + }) + .collect() + } + + pub fn get_outcome_by_id_and_block_hash( + &self, + id: &CryptoHash, + block_hash: &CryptoHash, + ) -> Result, Error> { + Ok(self.store.get_ser( + DBCol::TransactionResultForBlock, + &get_outcome_id_block_hash(id, block_hash), + )?) + } + + /// Returns a vector of Outcome ids for given block and shard id + pub fn get_outcomes_by_block_hash_and_shard_id( + &self, + block_hash: &CryptoHash, + shard_id: ShardId, + ) -> Result, Error> { + Ok(self + .store + .get_ser(DBCol::OutcomeIds, &get_block_shard_id(block_hash, shard_id))? + .unwrap_or_default()) + } + + /// Get all execution outcomes generated when the chunk are applied + pub fn get_block_execution_outcomes( + &self, + block_hash: &CryptoHash, + ) -> Result>, Error> { + let block = self.get_block(block_hash)?; + let chunk_headers = block.chunks().iter().cloned().collect::>(); + + let mut res = HashMap::new(); + for chunk_header in chunk_headers { + let shard_id = chunk_header.shard_id(); + let outcomes = self + .get_outcomes_by_block_hash_and_shard_id(block_hash, shard_id)? + .into_iter() + .filter_map(|id| { + let outcome_with_proof = + self.get_outcome_by_id_and_block_hash(&id, block_hash).ok()??; + Some(ExecutionOutcomeWithIdAndProof { + proof: outcome_with_proof.proof, + block_hash: *block_hash, + outcome_with_id: ExecutionOutcomeWithId { + id, + outcome: outcome_with_proof.outcome, + }, + }) + }) + .collect::>(); + res.insert(shard_id, outcomes); + } + Ok(res) + } + + /// Returns a hashmap of epoch id -> set of all blocks got for current (height, epoch_id) + pub fn get_all_block_hashes_by_height( + &self, + height: BlockHeight, + ) -> Result>>, Error> { + Ok(self + .read_with_cache( + DBCol::BlockPerHeight, + &self.block_hash_per_height, + &index_to_bytes(height), + )? + .unwrap_or_default()) + } + + /// Returns a HashSet of Chunk Hashes for current Height + pub fn get_all_chunk_hashes_by_height( + &self, + height: BlockHeight, + ) -> Result, Error> { + Ok(self + .store + .get_ser(DBCol::ChunkHashesByHeight, &index_to_bytes(height))? + .unwrap_or_default()) + } + + /// Returns a HashSet of Header Hashes for current Height + pub fn get_all_header_hashes_by_height( + &self, + height: BlockHeight, + ) -> Result, Error> { + Ok(self + .store + .get_ser(DBCol::HeaderHashesByHeight, &index_to_bytes(height))? + .unwrap_or_default()) + } + + pub fn get_state_header( + &self, + shard_id: ShardId, + block_hash: CryptoHash, + ) -> Result { + let key = borsh::to_vec(&StateHeaderKey(shard_id, block_hash))?; + match self.store.get_ser(DBCol::StateHeaders, &key) { + Ok(Some(header)) => Ok(header), + _ => Err(Error::Other("Cannot get shard_state_header".into())), + } + } + + /// Returns latest known height and time it was seen. + pub fn get_latest_known(&self) -> Result { + self.latest_known + .get_or_try_init(|| { + option_to_not_found( + self.store.get_ser(DBCol::BlockMisc, LATEST_KNOWN_KEY), + "LATEST_KNOWN_KEY", + ) + }) + .cloned() + } + + /// Save the latest known. + pub fn save_latest_known(&mut self, latest_known: LatestKnown) -> Result<(), Error> { + let mut store_update = self.store.store_update(); + store_update.set_ser(DBCol::BlockMisc, LATEST_KNOWN_KEY, &latest_known)?; + self.latest_known = once_cell::unsync::OnceCell::from(latest_known); + store_update.commit().map_err(|err| err.into()) + } + + /// Save epoch sync info + #[cfg(feature = "new_epoch_sync")] + pub fn get_epoch_sync_info(&self, epoch_id: &EpochId) -> Result { + option_to_not_found( + self.store.get_ser(DBCol::EpochSyncInfo, epoch_id.as_ref()), + "EpochSyncInfo", + ) + } + + /// Retrieve the kinds of state changes occurred in a given block. + /// + /// We store different types of data, so we prefer to only expose minimal information about the + /// changes (i.e. a kind of the change and an account id). + pub fn get_state_changes_in_block( + &self, + block_hash: &CryptoHash, + ) -> Result { + // We store the trie changes under a compound key: `block_hash + trie_key`, so when we + // query the changes, we reverse the process by splitting the key using simple slicing of an + // array of bytes, essentially, extracting `trie_key`. + // + // Example: data changes are stored under a key: + // + // block_hash + (col::ACCOUNT + account_id + ACCOUNT_DATA_SEPARATOR + user_specified_key) + // + // Thus, to query the list of touched accounts we do the following: + // 1. Query RocksDB for `block_hash` prefix. + // 2. Extract the original Trie key out of the keys returned by RocksDB + // 3. Try extracting `account_id` from the key using KeyFor* implementations + + let storage_key = KeyForStateChanges::for_block(block_hash); + + let mut block_changes = storage_key.find_iter(&self.store); + + Ok(StateChangesKinds::from_changes(&mut block_changes)?) + } + + pub fn get_state_changes_with_cause_in_block( + &self, + block_hash: &CryptoHash, + ) -> Result { + let storage_key = KeyForStateChanges::for_block(block_hash); + + let mut block_changes = storage_key.find_iter(&self.store); + + Ok(StateChanges::from_changes(&mut block_changes)?) + } + + /// Retrieve the key-value changes from the store and decode them appropriately. + /// + /// We store different types of data, so we need to take care of all the types. That is, the + /// account data and the access keys are internally-serialized and we have to deserialize those + /// values appropriately. Code and data changes are simple blobs of data, so we return them as + /// base64-encoded blobs. + pub fn get_state_changes( + &self, + block_hash: &CryptoHash, + state_changes_request: &StateChangesRequest, + ) -> Result { + // We store the trie changes under a compound key: `block_hash + trie_key`, so when we + // query the changes, we reverse the process by splitting the key using simple slicing of an + // array of bytes, essentially, extracting `trie_key`. + // + // Example: data changes are stored under a key: + // + // block_hash + (col::ACCOUNT + account_id + ACCOUNT_DATA_SEPARATOR + user_specified_key) + // + // Thus, to query all the changes by a user-specified key prefix, we do the following: + // 1. Query RocksDB for + // block_hash + (col::ACCOUNT + account_id + ACCOUNT_DATA_SEPARATOR + user_specified_key_prefix) + // + // 2. In the simplest case, to extract the full key we need to slice the RocksDB key by a length of + // block_hash + (col::ACCOUNT + account_id + ACCOUNT_DATA_SEPARATOR) + // + // In this implementation, however, we decoupled this process into two steps: + // + // 2.1. Split off the `block_hash` (internally in `KeyForStateChanges`), thus we are + // left working with a key that was used in the trie. + // 2.2. Parse the trie key with a relevant KeyFor* implementation to ensure consistency + + Ok(match state_changes_request { + StateChangesRequest::AccountChanges { account_ids } => { + let mut changes = StateChanges::new(); + for account_id in account_ids { + let data_key = TrieKey::Account { account_id: account_id.clone() }; + let storage_key = KeyForStateChanges::from_trie_key(block_hash, &data_key); + let changes_per_key = storage_key.find_exact_iter(&self.store); + changes.extend(StateChanges::from_account_changes(changes_per_key)?); + } + changes + } + StateChangesRequest::SingleAccessKeyChanges { keys } => { + let mut changes = StateChanges::new(); + for key in keys { + let data_key = TrieKey::AccessKey { + account_id: key.account_id.clone(), + public_key: key.public_key.clone(), + }; + let storage_key = KeyForStateChanges::from_trie_key(block_hash, &data_key); + let changes_per_key = storage_key.find_exact_iter(&self.store); + changes.extend(StateChanges::from_access_key_changes(changes_per_key)?); + } + changes + } + StateChangesRequest::AllAccessKeyChanges { account_ids } => { + let mut changes = StateChanges::new(); + for account_id in account_ids { + let data_key = trie_key_parsers::get_raw_prefix_for_access_keys(account_id); + let storage_key = KeyForStateChanges::from_raw_key(block_hash, &data_key); + let changes_per_key_prefix = storage_key.find_iter(&self.store); + changes.extend(StateChanges::from_access_key_changes(changes_per_key_prefix)?); + } + changes + } + StateChangesRequest::ContractCodeChanges { account_ids } => { + let mut changes = StateChanges::new(); + for account_id in account_ids { + let data_key = TrieKey::ContractCode { account_id: account_id.clone() }; + let storage_key = KeyForStateChanges::from_trie_key(block_hash, &data_key); + let changes_per_key = storage_key.find_exact_iter(&self.store); + changes.extend(StateChanges::from_contract_code_changes(changes_per_key)?); + } + changes + } + StateChangesRequest::DataChanges { account_ids, key_prefix } => { + let mut changes = StateChanges::new(); + for account_id in account_ids { + let data_key = trie_key_parsers::get_raw_prefix_for_contract_data( + account_id, + key_prefix.as_ref(), + ); + let storage_key = KeyForStateChanges::from_raw_key(block_hash, &data_key); + let changes_per_key_prefix = storage_key.find_iter(&self.store); + changes.extend(StateChanges::from_data_changes(changes_per_key_prefix)?); + } + changes + } + }) + } + + pub fn get_store_statistics(&self) -> Option { + self.store.get_store_statistics() + } + + fn read_with_cache<'a, T: BorshDeserialize + Clone + 'a>( + &self, + col: DBCol, + cache: &'a CellLruCache, T>, + key: &[u8], + ) -> io::Result> { + if let Some(value) = cache.get(key) { + return Ok(Some(value)); + } + if let Some(result) = self.store.get_ser::(col, key)? { + cache.put(key.to_vec(), result.clone()); + return Ok(Some(result)); + } + Ok(None) + } + + /// Constructs key 'STATE_SYNC_DUMP:', + /// for example 'STATE_SYNC_DUMP:2' for shard_id=2. + /// Doesn't contain epoch_id, because only one dump process per shard is allowed. + fn state_sync_dump_progress_key(shard_id: ShardId) -> Vec { + let mut key = STATE_SYNC_DUMP_KEY.to_vec(); + key.extend(b":".to_vec()); + key.extend(shard_id.to_le_bytes()); + key + } + + /// Retrieves STATE_SYNC_DUMP for the given shard. + pub fn get_state_sync_dump_progress( + &self, + shard_id: ShardId, + ) -> Result { + option_to_not_found( + self.store + .get_ser(DBCol::BlockMisc, &ChainStore::state_sync_dump_progress_key(shard_id)), + format!("STATE_SYNC_DUMP:{}", shard_id), + ) + } + + /// Updates STATE_SYNC_DUMP for the given shard. + pub fn set_state_sync_dump_progress( + &self, + shard_id: ShardId, + value: Option, + ) -> Result<(), Error> { + let mut store_update = self.store.store_update(); + let key = ChainStore::state_sync_dump_progress_key(shard_id); + match value { + None => store_update.delete(DBCol::BlockMisc, &key), + Some(value) => store_update.set_ser(DBCol::BlockMisc, &key, &value)?, + } + store_update.commit().map_err(|err| err.into()) + } +} + +impl ChainStoreAccess for ChainStore { + fn chain_store(&self) -> &ChainStore { + &self + } + + fn store(&self) -> &Store { + &self.store + } + /// The chain head. + fn head(&self) -> Result { + if let Some(ref tip) = self.head { + Ok(tip.clone()) + } else { + option_to_not_found(self.store.get_ser(DBCol::BlockMisc, HEAD_KEY), "HEAD") + } + } + + /// The chain Blocks Tail height, used by GC. + fn tail(&self) -> Result { + if let Some(tail) = self.tail.as_ref() { + Ok(*tail) + } else { + self.store + .get_ser(DBCol::BlockMisc, TAIL_KEY) + .map(|option| option.unwrap_or_else(|| self.genesis_height)) + .map_err(|e| e.into()) + } + } + + /// The chain Chunks Tail height, used by GC. + fn chunk_tail(&self) -> Result { + self.store + .get_ser(DBCol::BlockMisc, CHUNK_TAIL_KEY) + .map(|option| option.unwrap_or_else(|| self.genesis_height)) + .map_err(|e| e.into()) + } + + fn fork_tail(&self) -> Result { + self.store + .get_ser(DBCol::BlockMisc, FORK_TAIL_KEY) + .map(|option| option.unwrap_or_else(|| self.genesis_height)) + .map_err(|e| e.into()) + } + + /// Header of the block at the head of the block chain (not the same thing as header_head). + fn head_header(&self) -> Result { + self.get_block_header(&self.head()?.last_block_hash) + } + + /// Largest height for which we created a doomslug endorsement + fn largest_target_height(&self) -> Result { + match self.store.get_ser(DBCol::BlockMisc, LARGEST_TARGET_HEIGHT_KEY) { + Ok(Some(o)) => Ok(o), + Ok(None) => Ok(0), + Err(e) => Err(e.into()), + } + } + + /// Head of the header chain (not the same thing as head_header). + fn header_head(&self) -> Result { + option_to_not_found(self.store.get_ser(DBCol::BlockMisc, HEADER_HEAD_KEY), "HEADER_HEAD") + } + + /// Final head of the chain. + fn final_head(&self) -> Result { + option_to_not_found(self.store.get_ser(DBCol::BlockMisc, FINAL_HEAD_KEY), "FINAL HEAD") + } + + /// Get full block. + fn get_block(&self, h: &CryptoHash) -> Result { + option_to_not_found( + self.read_with_cache(DBCol::Block, &self.blocks, h.as_ref()), + format_args!("BLOCK: {}", h), + ) + } + + /// Get full chunk. + fn get_chunk(&self, chunk_hash: &ChunkHash) -> Result, Error> { + match self.read_with_cache(DBCol::Chunks, &self.chunks, chunk_hash.as_ref()) { + Ok(Some(shard_chunk)) => Ok(shard_chunk), + _ => Err(Error::ChunkMissing(chunk_hash.clone())), + } + } + + /// Get partial chunk. + fn get_partial_chunk(&self, chunk_hash: &ChunkHash) -> Result, Error> { + match self.read_with_cache(DBCol::PartialChunks, &self.partial_chunks, chunk_hash.as_ref()) + { + Ok(Some(shard_chunk)) => Ok(shard_chunk), + _ => Err(Error::ChunkMissing(chunk_hash.clone())), + } + } + + /// Does this full block exist? + fn block_exists(&self, h: &CryptoHash) -> Result { + self.store.exists(DBCol::Block, h.as_ref()).map_err(|e| e.into()) + } + + fn chunk_exists(&self, h: &ChunkHash) -> Result { + if self.chunks.get(h.as_ref()).is_some() { + Ok(true) + } else { + self.store.exists(DBCol::Chunks, h.as_ref()).map_err(|e| e.into()) + } + } + + /// Get previous header. + fn get_previous_header(&self, header: &BlockHeader) -> Result { + self.get_block_header(header.prev_hash()) + } + + /// Information from applying block. + fn get_block_extra(&self, block_hash: &CryptoHash) -> Result, Error> { + option_to_not_found( + self.read_with_cache(DBCol::BlockExtra, &self.block_extras, block_hash.as_ref()), + format_args!("BLOCK EXTRA: {}", block_hash), + ) + } + + /// Information from applying chunk. + fn get_chunk_extra( + &self, + block_hash: &CryptoHash, + shard_uid: &ShardUId, + ) -> Result, Error> { + option_to_not_found( + self.read_with_cache( + DBCol::ChunkExtra, + &self.chunk_extras, + &get_block_shard_uid(block_hash, shard_uid), + ), + format_args!("CHUNK EXTRA: {}:{:?}", block_hash, shard_uid), + ) + } + + /// Get block header. + fn get_block_header(&self, h: &CryptoHash) -> Result { + option_to_not_found( + self.read_with_cache(DBCol::BlockHeader, &self.headers, h.as_ref()), + format_args!("BLOCK HEADER: {}", h), + ) + } + + /// Returns hash of the block on the main chain for given height. + fn get_block_hash_by_height(&self, height: BlockHeight) -> Result { + option_to_not_found( + self.store.get_ser(DBCol::BlockHeight, &index_to_bytes(height)), + format_args!("BLOCK HEIGHT: {}", height), + ) + // TODO: cache needs to be deleted when things get updated. + // option_to_not_found( + // self.read_with_cache( + // DBCol::BlockHeight, + // &mut self.height, + // &index_to_bytes(height), + // ), + // format_args!("BLOCK HEIGHT: {}", height), + // ) + } + + fn get_next_block_hash(&self, hash: &CryptoHash) -> Result { + option_to_not_found( + self.read_with_cache(DBCol::NextBlockHashes, &self.next_block_hashes, hash.as_ref()), + format_args!("NEXT BLOCK HASH: {}", hash), + ) + } + + fn get_epoch_light_client_block( + &self, + hash: &CryptoHash, + ) -> Result, Error> { + option_to_not_found( + self.read_with_cache( + DBCol::EpochLightClientBlocks, + &self.epoch_light_client_blocks, + hash.as_ref(), + ), + format_args!("EPOCH LIGHT CLIENT BLOCK: {}", hash), + ) + } + + fn get_block_refcount(&self, block_hash: &CryptoHash) -> Result { + option_to_not_found( + self.read_with_cache(DBCol::BlockRefCount, &self.block_refcounts, block_hash.as_ref()), + format_args!("BLOCK REFCOUNT: {}", block_hash), + ) + } + + /// Get outgoing receipts *generated* from shard `shard_id` in block `prev_hash` + /// Note that this function is different from get_outgoing_receipts_for_shard, see comments there + fn get_outgoing_receipts( + &self, + prev_block_hash: &CryptoHash, + shard_id: ShardId, + ) -> Result>, Error> { + option_to_not_found( + self.read_with_cache( + DBCol::OutgoingReceipts, + &self.outgoing_receipts, + &get_block_shard_id(prev_block_hash, shard_id), + ), + format_args!("OUTGOING RECEIPT: {} {}", prev_block_hash, shard_id), + ) + } + + fn get_incoming_receipts( + &self, + block_hash: &CryptoHash, + shard_id: ShardId, + ) -> Result>, Error> { + option_to_not_found( + self.read_with_cache( + DBCol::IncomingReceipts, + &self.incoming_receipts, + &get_block_shard_id(block_hash, shard_id), + ), + format_args!("INCOMING RECEIPT: {} {}", block_hash, shard_id), + ) + } + + fn get_blocks_to_catchup(&self, hash: &CryptoHash) -> Result, Error> { + Ok(self.store.get_ser(DBCol::BlocksToCatchup, hash.as_ref())?.unwrap_or_default()) + } + + fn is_block_challenged(&self, hash: &CryptoHash) -> Result { + Ok(self.store.get_ser(DBCol::ChallengedBlocks, hash.as_ref())?.unwrap_or_default()) + } + + fn is_invalid_chunk( + &self, + chunk_hash: &ChunkHash, + ) -> Result>, Error> { + self.read_with_cache(DBCol::InvalidChunks, &self.invalid_chunks, chunk_hash.as_ref()) + .map_err(|err| err.into()) + } + + fn get_shard_id_for_receipt_id(&self, receipt_id: &CryptoHash) -> Result { + option_to_not_found( + self.read_with_cache( + DBCol::ReceiptIdToShardId, + &self.receipt_id_to_shard_id, + receipt_id.as_ref(), + ), + format_args!("RECEIPT ID: {}", receipt_id), + ) + } + + fn get_transaction( + &self, + tx_hash: &CryptoHash, + ) -> Result>, Error> { + self.read_with_cache(DBCol::Transactions, &self.transactions, tx_hash.as_ref()) + .map_err(|e| e.into()) + } + + fn get_receipt(&self, receipt_id: &CryptoHash) -> Result>, Error> { + self.read_with_cache(DBCol::Receipts, &self.receipts, receipt_id.as_ref()) + .map_err(|e| e.into()) + } + + fn get_genesis_height(&self) -> BlockHeight { + self.genesis_height + } + + fn get_block_merkle_tree( + &self, + block_hash: &CryptoHash, + ) -> Result, Error> { + option_to_not_found( + self.read_with_cache( + DBCol::BlockMerkleTree, + &self.block_merkle_tree, + block_hash.as_ref(), + ), + format_args!("BLOCK MERKLE TREE: {}", block_hash), + ) + } + + fn get_block_hash_from_ordinal(&self, block_ordinal: NumBlocks) -> Result { + option_to_not_found( + self.read_with_cache( + DBCol::BlockOrdinal, + &self.block_ordinal_to_hash, + &index_to_bytes(block_ordinal), + ), + format_args!("BLOCK ORDINAL: {}", block_ordinal), + ) + } + + fn is_height_processed(&self, height: BlockHeight) -> Result { + self.read_with_cache( + DBCol::ProcessedBlockHeights, + &self.processed_block_heights, + &index_to_bytes(height), + ) + .map(|r| r.is_some()) + .map_err(|e| e.into()) + } +} + +/// Cache update for ChainStore +#[derive(Default)] +pub(crate) struct ChainStoreCacheUpdate { + blocks: HashMap, + headers: HashMap, + block_extras: HashMap>, + chunk_extras: HashMap<(CryptoHash, ShardUId), Arc>, + chunks: HashMap>, + partial_chunks: HashMap>, + block_hash_per_height: HashMap>>, + pub(crate) height_to_hashes: HashMap>, + next_block_hashes: HashMap, + epoch_light_client_blocks: HashMap>, + outgoing_receipts: HashMap<(CryptoHash, ShardId), Arc>>, + incoming_receipts: HashMap<(CryptoHash, ShardId), Arc>>, + outcomes: HashMap<(CryptoHash, CryptoHash), ExecutionOutcomeWithProof>, + outcome_ids: HashMap<(CryptoHash, ShardId), Vec>, + invalid_chunks: HashMap>, + receipt_id_to_shard_id: HashMap, + transactions: HashMap>, + receipts: HashMap>, + block_refcounts: HashMap, + block_merkle_tree: HashMap>, + block_ordinal_to_hash: HashMap, + processed_block_heights: HashSet, +} + +/// Provides layer to update chain without touching the underlying database. +/// This serves few purposes, main one is that even if executable exists/fails during update the database is in consistent state. +pub struct ChainStoreUpdate<'a> { + chain_store: &'a mut ChainStore, + store_updates: Vec, + /// Blocks added during this update. Takes ownership (unclear how to not do it because of failure exists). + pub(crate) chain_store_cache_update: ChainStoreCacheUpdate, + head: Option, + tail: Option, + chunk_tail: Option, + fork_tail: Option, + header_head: Option, + final_head: Option, + largest_target_height: Option, + trie_changes: Vec, + + // All state changes made by a chunk, this is only used for resharding. + add_state_changes_for_resharding: HashMap<(CryptoHash, ShardId), StateChangesForResharding>, + remove_state_changes_for_resharding: HashSet<(CryptoHash, ShardId)>, + + add_blocks_to_catchup: Vec<(CryptoHash, CryptoHash)>, + // A pair (prev_hash, hash) to be removed from blocks to catchup + remove_blocks_to_catchup: Vec<(CryptoHash, CryptoHash)>, + // A prev_hash to be removed with all the hashes associated with it + remove_prev_blocks_to_catchup: Vec, + add_state_sync_infos: Vec, + remove_state_sync_infos: Vec, + challenged_blocks: HashSet, +} + +impl<'a> ChainStoreUpdate<'a> { + pub fn new(chain_store: &'a mut ChainStore) -> Self { + ChainStoreUpdate { + chain_store, + store_updates: vec![], + chain_store_cache_update: ChainStoreCacheUpdate::default(), + head: None, + tail: None, + chunk_tail: None, + fork_tail: None, + header_head: None, + final_head: None, + largest_target_height: None, + trie_changes: vec![], + add_state_changes_for_resharding: HashMap::new(), + remove_state_changes_for_resharding: HashSet::new(), + add_blocks_to_catchup: vec![], + remove_blocks_to_catchup: vec![], + remove_prev_blocks_to_catchup: vec![], + add_state_sync_infos: vec![], + remove_state_sync_infos: vec![], + challenged_blocks: HashSet::default(), + } + } +} + +impl<'a> ChainStoreAccess for ChainStoreUpdate<'a> { + fn chain_store(&self) -> &ChainStore { + &self.chain_store + } + + fn store(&self) -> &Store { + &self.chain_store.store + } + + /// The chain head. + fn head(&self) -> Result { + if let Some(head) = &self.head { + Ok(head.clone()) + } else { + self.chain_store.head() + } + } + + /// The chain Block Tail height, used by GC. + fn tail(&self) -> Result { + if let Some(tail) = &self.tail { + Ok(*tail) + } else { + self.chain_store.tail() + } + } + + /// The chain Chunks Tail height, used by GC. + fn chunk_tail(&self) -> Result { + if let Some(chunk_tail) = &self.chunk_tail { + Ok(*chunk_tail) + } else { + self.chain_store.chunk_tail() + } + } + + /// Fork tail used by GC + fn fork_tail(&self) -> Result { + if let Some(fork_tail) = &self.fork_tail { + Ok(*fork_tail) + } else { + self.chain_store.fork_tail() + } + } + + /// Head of the header chain (not the same thing as head_header). + fn header_head(&self) -> Result { + if let Some(header_head) = &self.header_head { + Ok(header_head.clone()) + } else { + self.chain_store.header_head() + } + } + + fn final_head(&self) -> Result { + if let Some(final_head) = self.final_head.as_ref() { + Ok(final_head.clone()) + } else { + self.chain_store.final_head() + } + } + + fn largest_target_height(&self) -> Result { + if let Some(largest_target_height) = &self.largest_target_height { + Ok(*largest_target_height) + } else { + self.chain_store.largest_target_height() + } + } + + /// Header of the block at the head of the block chain (not the same thing as header_head). + fn head_header(&self) -> Result { + self.get_block_header(&(self.head()?.last_block_hash)) + } + + /// Get full block. + fn get_block(&self, h: &CryptoHash) -> Result { + if let Some(block) = self.chain_store_cache_update.blocks.get(h) { + Ok(block.clone()) + } else { + self.chain_store.get_block(h) + } + } + + /// Does this full block exist? + fn block_exists(&self, h: &CryptoHash) -> Result { + Ok(self.chain_store_cache_update.blocks.contains_key(h) + || self.chain_store.block_exists(h)?) + } + + fn chunk_exists(&self, h: &ChunkHash) -> Result { + Ok(self.chain_store_cache_update.chunks.contains_key(h) + || self.chain_store.chunk_exists(h)?) + } + + /// Get previous header. + fn get_previous_header(&self, header: &BlockHeader) -> Result { + self.get_block_header(header.prev_hash()) + } + + fn get_block_extra(&self, block_hash: &CryptoHash) -> Result, Error> { + if let Some(block_extra) = self.chain_store_cache_update.block_extras.get(block_hash) { + Ok(Arc::clone(block_extra)) + } else { + self.chain_store.get_block_extra(block_hash) + } + } + + /// Get state root hash after applying header with given hash. + fn get_chunk_extra( + &self, + block_hash: &CryptoHash, + shard_uid: &ShardUId, + ) -> Result, Error> { + if let Some(chunk_extra) = + self.chain_store_cache_update.chunk_extras.get(&(*block_hash, *shard_uid)) + { + Ok(Arc::clone(chunk_extra)) + } else { + self.chain_store.get_chunk_extra(block_hash, shard_uid) + } + } + + /// Get block header. + fn get_block_header(&self, hash: &CryptoHash) -> Result { + if let Some(header) = self.chain_store_cache_update.headers.get(hash).cloned() { + Ok(header) + } else { + self.chain_store.get_block_header(hash) + } + } + + /// Get block header from the current chain by height. + fn get_block_hash_by_height(&self, height: BlockHeight) -> Result { + match self.chain_store_cache_update.height_to_hashes.get(&height) { + Some(Some(hash)) => Ok(*hash), + Some(None) => Err(Error::DBNotFoundErr(format!("BLOCK HEIGHT: {}", height))), + None => self.chain_store.get_block_hash_by_height(height), + } + } + + fn get_block_refcount(&self, block_hash: &CryptoHash) -> Result { + if let Some(refcount) = self.chain_store_cache_update.block_refcounts.get(block_hash) { + Ok(*refcount) + } else { + let refcount = match self.chain_store.get_block_refcount(block_hash) { + Ok(refcount) => refcount, + Err(e) => match e { + Error::DBNotFoundErr(_) => 0, + _ => return Err(e), + }, + }; + Ok(refcount) + } + } + + fn get_next_block_hash(&self, hash: &CryptoHash) -> Result { + if let Some(next_hash) = self.chain_store_cache_update.next_block_hashes.get(hash) { + Ok(*next_hash) + } else { + self.chain_store.get_next_block_hash(hash) + } + } + + fn get_epoch_light_client_block( + &self, + hash: &CryptoHash, + ) -> Result, Error> { + if let Some(light_client_block) = + self.chain_store_cache_update.epoch_light_client_blocks.get(hash) + { + Ok(Arc::clone(light_client_block)) + } else { + self.chain_store.get_epoch_light_client_block(hash) + } + } + + /// Get receipts produced for block with given hash. + fn get_outgoing_receipts( + &self, + hash: &CryptoHash, + shard_id: ShardId, + ) -> Result>, Error> { + if let Some(receipts) = + self.chain_store_cache_update.outgoing_receipts.get(&(*hash, shard_id)) + { + Ok(Arc::clone(receipts)) + } else { + self.chain_store.get_outgoing_receipts(hash, shard_id) + } + } + + /// Get receipts produced for block with given hash. + fn get_incoming_receipts( + &self, + hash: &CryptoHash, + shard_id: ShardId, + ) -> Result>, Error> { + if let Some(receipt_proofs) = + self.chain_store_cache_update.incoming_receipts.get(&(*hash, shard_id)) + { + Ok(Arc::clone(receipt_proofs)) + } else { + self.chain_store.get_incoming_receipts(hash, shard_id) + } + } + + fn get_chunk(&self, chunk_hash: &ChunkHash) -> Result, Error> { + if let Some(chunk) = self.chain_store_cache_update.chunks.get(chunk_hash) { + Ok(Arc::clone(chunk)) + } else { + self.chain_store.get_chunk(chunk_hash) + } + } + + fn get_partial_chunk(&self, chunk_hash: &ChunkHash) -> Result, Error> { + if let Some(partial_chunk) = self.chain_store_cache_update.partial_chunks.get(chunk_hash) { + Ok(Arc::clone(partial_chunk)) + } else { + self.chain_store.get_partial_chunk(chunk_hash) + } + } + + fn get_chunk_clone_from_header(&self, header: &ShardChunkHeader) -> Result { + if let Some(chunk) = self.chain_store_cache_update.chunks.get(&header.chunk_hash()) { + Ok(ShardChunk::clone(chunk)) + } else { + self.chain_store.get_chunk_clone_from_header(header) + } + } + + fn get_blocks_to_catchup(&self, prev_hash: &CryptoHash) -> Result, Error> { + // Make sure we never request a block to catchup after altering the data structure + assert_eq!(self.add_blocks_to_catchup.len(), 0); + assert_eq!(self.remove_blocks_to_catchup.len(), 0); + assert_eq!(self.remove_prev_blocks_to_catchup.len(), 0); + + self.chain_store.get_blocks_to_catchup(prev_hash) + } + + fn is_block_challenged(&self, hash: &CryptoHash) -> Result { + if self.challenged_blocks.contains(hash) { + return Ok(true); + } + self.chain_store.is_block_challenged(hash) + } + + fn is_invalid_chunk( + &self, + chunk_hash: &ChunkHash, + ) -> Result>, Error> { + if let Some(chunk) = self.chain_store_cache_update.invalid_chunks.get(chunk_hash) { + Ok(Some(Arc::clone(chunk))) + } else { + self.chain_store.is_invalid_chunk(chunk_hash) + } + } + + fn get_shard_id_for_receipt_id(&self, receipt_id: &CryptoHash) -> Result { + if let Some(shard_id) = self.chain_store_cache_update.receipt_id_to_shard_id.get(receipt_id) + { + Ok(*shard_id) + } else { + self.chain_store.get_shard_id_for_receipt_id(receipt_id) + } + } + + fn get_transaction( + &self, + tx_hash: &CryptoHash, + ) -> Result>, Error> { + if let Some(tx) = self.chain_store_cache_update.transactions.get(tx_hash) { + Ok(Some(Arc::clone(tx))) + } else { + self.chain_store.get_transaction(tx_hash) + } + } + + fn get_receipt(&self, receipt_id: &CryptoHash) -> Result>, Error> { + if let Some(receipt) = self.chain_store_cache_update.receipts.get(receipt_id) { + Ok(Some(Arc::clone(receipt))) + } else { + self.chain_store.get_receipt(receipt_id) + } + } + + fn get_genesis_height(&self) -> BlockHeight { + self.chain_store.genesis_height + } + + fn get_block_merkle_tree( + &self, + block_hash: &CryptoHash, + ) -> Result, Error> { + if let Some(merkle_tree) = self.chain_store_cache_update.block_merkle_tree.get(block_hash) { + Ok(Arc::clone(&merkle_tree)) + } else { + self.chain_store.get_block_merkle_tree(block_hash) + } + } + + fn get_block_hash_from_ordinal(&self, block_ordinal: NumBlocks) -> Result { + if let Some(block_hash) = + self.chain_store_cache_update.block_ordinal_to_hash.get(&block_ordinal) + { + Ok(*block_hash) + } else { + self.chain_store.get_block_hash_from_ordinal(block_ordinal) + } + } + + fn is_height_processed(&self, height: BlockHeight) -> Result { + if self.chain_store_cache_update.processed_block_heights.contains(&height) { + Ok(true) + } else { + self.chain_store.is_height_processed(height) + } + } +} + +impl<'a> ChainStoreUpdate<'a> { + /// Update both header and block body head. + pub fn save_head(&mut self, t: &Tip) -> Result<(), Error> { + self.save_body_head(t)?; + self.save_header_head_if_not_challenged(t) + } + + /// Update block body head and latest known height. + pub fn save_body_head(&mut self, t: &Tip) -> Result<(), Error> { + self.try_save_latest_known(t.height)?; + self.head = Some(t.clone()); + Ok(()) + } + + pub fn save_final_head(&mut self, t: &Tip) -> Result<(), Error> { + self.final_head = Some(t.clone()); + Ok(()) + } + + /// This function checks that the block is not on a chain with challenged blocks and updates + /// fields in ChainStore that stores information of the canonical chain + fn update_height_if_not_challenged( + &mut self, + height: BlockHeight, + hash: CryptoHash, + ) -> Result<(), Error> { + let mut prev_hash = hash; + let mut prev_height = height; + loop { + let header = self.get_block_header(&prev_hash)?; + let (header_height, header_hash, header_prev_hash) = + (header.height(), *header.hash(), *header.prev_hash()); + // Clean up block indices between blocks. + for height in (header_height + 1)..prev_height { + self.chain_store_cache_update.height_to_hashes.insert(height, None); + } + // Override block ordinal to hash mapping for blocks in between. + // At this point block_merkle_tree for header is already saved. + let block_ordinal = self.get_block_merkle_tree(&header_hash)?.size(); + self.chain_store_cache_update.block_ordinal_to_hash.insert(block_ordinal, header_hash); + match self.get_block_hash_by_height(header_height) { + Ok(cur_hash) if cur_hash == header_hash => { + // Found common ancestor. + return Ok(()); + } + _ => { + // TODO: remove this check from this function and use Chain::check_if_challenged_block_on_chain + // I'm not doing that now because I'm afraid that this will make header sync take + // even longer. + if self.is_block_challenged(&header_hash)? { + return Err(Error::ChallengedBlockOnChain); + } + self.chain_store_cache_update + .height_to_hashes + .insert(header_height, Some(header_hash)); + self.chain_store_cache_update + .next_block_hashes + .insert(header_prev_hash, header_hash); + prev_hash = header_prev_hash; + prev_height = header_height; + } + }; + } + } + + /// Save header head in Epoch Sync + /// Checking validity of header head is delegated to Epoch Sync methods + pub fn force_save_header_head(&mut self, t: &Tip) -> Result<(), Error> { + self.try_save_latest_known(t.height)?; + + // TODO #3488 + // Bowen: It seems that height_to_hashes is used to update DBCol::BlockHeight, which stores blocks, + // not block headers, by height. Therefore I wonder whether this line here breaks some invariant + // since now we potentially don't have the corresponding block in storage. + + //self.chain_tore_cache_update.height_to_hashes.insert(t.height, Some(t.last_block_hash)); + //self.chain_store_cache_update.next_block_hashes.insert(t.prev_block_hash, t.last_block_hash); + self.header_head = Some(t.clone()); + Ok(()) + } + + /// Update header head and height to hash index for this branch. + pub fn save_header_head_if_not_challenged(&mut self, t: &Tip) -> Result<(), Error> { + if t.height > self.chain_store.genesis_height { + self.update_height_if_not_challenged(t.height, t.prev_block_hash)?; + } + self.try_save_latest_known(t.height)?; + + match self.header_head() { + Ok(prev_tip) => { + if prev_tip.height > t.height { + for height in (t.height + 1)..=prev_tip.height { + self.chain_store_cache_update.height_to_hashes.insert(height, None); + } + } + } + Err(err) => match err { + Error::DBNotFoundErr(_) => {} + e => return Err(e), + }, + } + + // save block ordinal and height if we need to update header head + let block_ordinal = self.get_block_merkle_tree(&t.last_block_hash)?.size(); + self.chain_store_cache_update + .block_ordinal_to_hash + .insert(block_ordinal, t.last_block_hash); + self.chain_store_cache_update.height_to_hashes.insert(t.height, Some(t.last_block_hash)); + self.chain_store_cache_update + .next_block_hashes + .insert(t.prev_block_hash, t.last_block_hash); + self.header_head = Some(t.clone()); + Ok(()) + } + + pub fn save_largest_target_height(&mut self, height: BlockHeight) { + self.largest_target_height = Some(height); + } + + /// Save new height if it's above currently latest known. + pub fn try_save_latest_known(&mut self, height: BlockHeight) -> Result<(), Error> { + let latest_known = self.chain_store.get_latest_known().ok(); + if latest_known.is_none() || height > latest_known.unwrap().height { + self.chain_store + .save_latest_known(LatestKnown { height, seen: to_timestamp(Utc::now()) })?; + } + Ok(()) + } + + #[cfg(feature = "test_features")] + pub fn adv_save_latest_known(&mut self, height: BlockHeight) -> Result<(), Error> { + let header = self.get_block_header_by_height(height)?; + let tip = Tip::from_header(&header); + self.chain_store + .save_latest_known(LatestKnown { height, seen: to_timestamp(Utc::now()) })?; + self.save_head(&tip)?; + Ok(()) + } + + /// Save block. + pub fn save_block(&mut self, block: Block) { + self.chain_store_cache_update.blocks.insert(*block.hash(), block); + } + + /// Save post applying block extra info. + pub fn save_block_extra(&mut self, block_hash: &CryptoHash, block_extra: BlockExtra) { + self.chain_store_cache_update.block_extras.insert(*block_hash, Arc::new(block_extra)); + } + + /// Save post applying chunk extra info. + pub fn save_chunk_extra( + &mut self, + block_hash: &CryptoHash, + shard_uid: &ShardUId, + chunk_extra: ChunkExtra, + ) { + self.chain_store_cache_update + .chunk_extras + .insert((*block_hash, *shard_uid), Arc::new(chunk_extra)); + } + + pub fn save_chunk(&mut self, chunk: ShardChunk) { + for transaction in chunk.transactions() { + self.chain_store_cache_update + .transactions + .insert(transaction.get_hash(), Arc::new(transaction.clone())); + } + for receipt in chunk.prev_outgoing_receipts() { + self.chain_store_cache_update + .receipts + .insert(receipt.receipt_id, Arc::new(receipt.clone())); + } + self.chain_store_cache_update.chunks.insert(chunk.chunk_hash(), Arc::new(chunk)); + } + + pub fn save_partial_chunk(&mut self, partial_chunk: PartialEncodedChunk) { + self.chain_store_cache_update + .partial_chunks + .insert(partial_chunk.chunk_hash(), Arc::new(partial_chunk)); + } + + pub fn save_block_merkle_tree( + &mut self, + block_hash: CryptoHash, + block_merkle_tree: PartialMerkleTree, + ) { + self.chain_store_cache_update + .block_merkle_tree + .insert(block_hash, Arc::new(block_merkle_tree)); + } + + fn update_and_save_block_merkle_tree(&mut self, header: &BlockHeader) -> Result<(), Error> { + let prev_hash = *header.prev_hash(); + if prev_hash == CryptoHash::default() { + self.save_block_merkle_tree(*header.hash(), PartialMerkleTree::default()); + } else { + let old_merkle_tree = self.get_block_merkle_tree(&prev_hash)?; + let mut new_merkle_tree = PartialMerkleTree::clone(&old_merkle_tree); + new_merkle_tree.insert(prev_hash); + self.save_block_merkle_tree(*header.hash(), new_merkle_tree); + } + Ok(()) + } + + /// Used only in Epoch Sync finalization + /// Validity of Header is checked by Epoch Sync methods + pub fn save_block_header_no_update_tree(&mut self, header: BlockHeader) -> Result<(), Error> { + self.chain_store_cache_update.headers.insert(*header.hash(), header); + Ok(()) + } + + pub fn save_block_header(&mut self, header: BlockHeader) -> Result<(), Error> { + self.update_and_save_block_merkle_tree(&header)?; + self.chain_store_cache_update.headers.insert(*header.hash(), header); + Ok(()) + } + + pub fn save_next_block_hash(&mut self, hash: &CryptoHash, next_hash: CryptoHash) { + self.chain_store_cache_update.next_block_hashes.insert(*hash, next_hash); + } + + pub fn save_epoch_light_client_block( + &mut self, + epoch_hash: &CryptoHash, + light_client_block: LightClientBlockView, + ) { + self.chain_store_cache_update + .epoch_light_client_blocks + .insert(*epoch_hash, Arc::new(light_client_block)); + } + + // save the outgoing receipts generated by chunk from block `hash` for shard `shard_id` + pub fn save_outgoing_receipt( + &mut self, + hash: &CryptoHash, + shard_id: ShardId, + outgoing_receipts: Vec, + ) { + self.chain_store_cache_update + .outgoing_receipts + .insert((*hash, shard_id), Arc::new(outgoing_receipts)); + } + + pub fn save_receipt_id_to_shard_id(&mut self, receipt_id: CryptoHash, shard_id: ShardId) { + self.chain_store_cache_update.receipt_id_to_shard_id.insert(receipt_id, shard_id); + } + + pub fn save_incoming_receipt( + &mut self, + hash: &CryptoHash, + shard_id: ShardId, + receipt_proof: Arc>, + ) { + self.chain_store_cache_update.incoming_receipts.insert((*hash, shard_id), receipt_proof); + } + + pub fn save_outcomes_with_proofs( + &mut self, + block_hash: &CryptoHash, + shard_id: ShardId, + outcomes: Vec, + proofs: Vec, + ) { + let mut outcome_ids = Vec::with_capacity(outcomes.len()); + for (outcome_with_id, proof) in outcomes.into_iter().zip(proofs.into_iter()) { + outcome_ids.push(outcome_with_id.id); + self.chain_store_cache_update.outcomes.insert( + (outcome_with_id.id, *block_hash), + ExecutionOutcomeWithProof { outcome: outcome_with_id.outcome, proof }, + ); + } + self.chain_store_cache_update.outcome_ids.insert((*block_hash, shard_id), outcome_ids); + } + + pub fn save_trie_changes(&mut self, trie_changes: WrappedTrieChanges) { + self.trie_changes.push(trie_changes); + } + + pub fn add_state_changes_for_resharding( + &mut self, + block_hash: CryptoHash, + shard_id: ShardId, + state_changes: StateChangesForResharding, + ) { + let prev = + self.add_state_changes_for_resharding.insert((block_hash, shard_id), state_changes); + // We should not save state changes for the same chunk twice + assert!(prev.is_none()); + } + + pub fn remove_state_changes_for_resharding( + &mut self, + block_hash: CryptoHash, + shard_id: ShardId, + ) { + // We should not remove state changes for the same chunk twice + let value_not_present = + self.remove_state_changes_for_resharding.insert((block_hash, shard_id)); + assert!(value_not_present); + } + + pub fn add_block_to_catchup(&mut self, prev_hash: CryptoHash, block_hash: CryptoHash) { + self.add_blocks_to_catchup.push((prev_hash, block_hash)); + } + + pub fn remove_block_to_catchup(&mut self, prev_hash: CryptoHash, hash: CryptoHash) { + self.remove_blocks_to_catchup.push((prev_hash, hash)); + } + + pub fn remove_prev_block_to_catchup(&mut self, hash: CryptoHash) { + self.remove_prev_blocks_to_catchup.push(hash); + } + + pub fn add_state_sync_info(&mut self, info: StateSyncInfo) { + self.add_state_sync_infos.push(info); + } + + pub fn remove_state_sync_info(&mut self, hash: CryptoHash) { + self.remove_state_sync_infos.push(hash); + } + + pub fn save_challenged_block(&mut self, hash: CryptoHash) { + self.challenged_blocks.insert(hash); + } + + pub fn save_invalid_chunk(&mut self, chunk: EncodedShardChunk) { + self.chain_store_cache_update.invalid_chunks.insert(chunk.chunk_hash(), Arc::new(chunk)); + } + + pub fn save_block_height_processed(&mut self, height: BlockHeight) { + self.chain_store_cache_update.processed_block_heights.insert(height); + } + + pub fn inc_block_refcount(&mut self, block_hash: &CryptoHash) -> Result<(), Error> { + let refcount = match self.get_block_refcount(block_hash) { + Ok(refcount) => refcount, + Err(e) => match e { + Error::DBNotFoundErr(_) => 0, + _ => return Err(e), + }, + }; + self.chain_store_cache_update.block_refcounts.insert(*block_hash, refcount + 1); + Ok(()) + } + + pub fn dec_block_refcount(&mut self, block_hash: &CryptoHash) -> Result<(), Error> { + let refcount = self.get_block_refcount(block_hash)?; + if refcount > 0 { + self.chain_store_cache_update.block_refcounts.insert(*block_hash, refcount - 1); + Ok(()) + } else { + debug_assert!(false, "refcount can not be negative"); + Err(Error::Other(format!("cannot decrease refcount for {:?}", block_hash))) + } + } + + pub fn reset_tail(&mut self) { + self.tail = None; + self.chunk_tail = None; + self.fork_tail = None; + } + + pub fn update_tail(&mut self, height: BlockHeight) -> Result<(), Error> { + self.tail = Some(height); + let genesis_height = self.get_genesis_height(); + // When fork tail is behind tail, it doesn't hurt to set it to tail for consistency. + if self.fork_tail()? < height { + self.fork_tail = Some(height); + } + + let chunk_tail = self.chunk_tail()?; + if chunk_tail == genesis_height { + // For consistency, Chunk Tail should be set if Tail is set + self.chunk_tail = Some(self.get_genesis_height()); + } + Ok(()) + } + + pub fn update_fork_tail(&mut self, height: BlockHeight) { + self.fork_tail = Some(height); + } + + pub fn update_chunk_tail(&mut self, height: BlockHeight) { + self.chunk_tail = Some(height); + } + + /// Merge another StoreUpdate into this one + pub fn merge(&mut self, store_update: StoreUpdate) { + self.store_updates.push(store_update); + } + + fn write_col_misc( + store_update: &mut StoreUpdate, + key: &[u8], + value: &mut Option, + ) -> Result<(), Error> { + if let Some(t) = value.take() { + store_update.set_ser(DBCol::BlockMisc, key, &t)?; + } + Ok(()) + } + + /// Only used in mock network + /// Create a new ChainStoreUpdate that copies the necessary chain state related to `block_hash` + /// from `source_store` to the current store. + pub fn copy_chain_state_as_of_block( + chain_store: &'a mut ChainStore, + block_hash: &CryptoHash, + source_epoch_manager: &dyn EpochManagerAdapter, + source_store: &ChainStore, + ) -> Result, Error> { + let mut chain_store_update = ChainStoreUpdate::new(chain_store); + let block = source_store.get_block(block_hash)?; + let header = block.header().clone(); + let height = header.height(); + let tip = Tip { + height, + last_block_hash: *block_hash, + prev_block_hash: *header.prev_hash(), + epoch_id: header.epoch_id().clone(), + next_epoch_id: header.next_epoch_id().clone(), + }; + chain_store_update.head = Some(tip.clone()); + chain_store_update.tail = Some(height); + chain_store_update.chunk_tail = Some(height); + chain_store_update.fork_tail = Some(height); + chain_store_update.header_head = Some(tip.clone()); + chain_store_update.final_head = Some(tip); + chain_store_update.chain_store_cache_update.blocks.insert(*block_hash, block.clone()); + chain_store_update.chain_store_cache_update.headers.insert(*block_hash, header.clone()); + // store all headers until header.last_final_block + // needed to light client + let mut prev_hash = *header.prev_hash(); + let last_final_hash = header.last_final_block(); + loop { + let header = source_store.get_block_header(&prev_hash)?; + chain_store_update.chain_store_cache_update.headers.insert(prev_hash, header.clone()); + if &prev_hash == last_final_hash { + break; + } else { + chain_store_update + .chain_store_cache_update + .next_block_hashes + .insert(*header.prev_hash(), prev_hash); + prev_hash = *header.prev_hash(); + } + } + chain_store_update + .chain_store_cache_update + .block_extras + .insert(*block_hash, source_store.get_block_extra(block_hash)?); + let shard_layout = source_epoch_manager.get_shard_layout(&header.epoch_id())?; + for shard_uid in shard_layout.shard_uids() { + chain_store_update.chain_store_cache_update.chunk_extras.insert( + (*block_hash, shard_uid), + source_store.get_chunk_extra(block_hash, &shard_uid)?.clone(), + ); + } + for (shard_id, chunk_header) in block.chunks().iter().enumerate() { + let chunk_hash = chunk_header.chunk_hash(); + let shard_id = shard_id as u64; + chain_store_update + .chain_store_cache_update + .chunks + .insert(chunk_hash.clone(), source_store.get_chunk(&chunk_hash)?.clone()); + chain_store_update.chain_store_cache_update.outgoing_receipts.insert( + (*block_hash, shard_id), + source_store.get_outgoing_receipts(block_hash, shard_id)?.clone(), + ); + chain_store_update.chain_store_cache_update.incoming_receipts.insert( + (*block_hash, shard_id), + source_store.get_incoming_receipts(block_hash, shard_id)?.clone(), + ); + let outcome_ids = + source_store.get_outcomes_by_block_hash_and_shard_id(block_hash, shard_id)?; + for id in outcome_ids.iter() { + if let Some(existing_outcome) = + source_store.get_outcome_by_id_and_block_hash(id, block_hash)? + { + chain_store_update + .chain_store_cache_update + .outcomes + .insert((*id, *block_hash), existing_outcome); + } + } + chain_store_update + .chain_store_cache_update + .outcome_ids + .insert((*block_hash, shard_id), outcome_ids); + } + chain_store_update + .chain_store_cache_update + .height_to_hashes + .insert(height, Some(*block_hash)); + chain_store_update + .chain_store_cache_update + .next_block_hashes + .insert(*header.prev_hash(), *block_hash); + let block_merkle_tree = source_store.get_block_merkle_tree(block_hash)?; + chain_store_update + .chain_store_cache_update + .block_merkle_tree + .insert(*block_hash, block_merkle_tree.clone()); + chain_store_update + .chain_store_cache_update + .block_ordinal_to_hash + .insert(block_merkle_tree.size(), *block_hash); + chain_store_update.chain_store_cache_update.processed_block_heights.insert(height); + + // other information not directly related to this block + chain_store_update.chain_store_cache_update.height_to_hashes.insert( + source_store.genesis_height, + Some(source_store.get_block_hash_by_height(source_store.genesis_height)?), + ); + Ok(chain_store_update) + } + + fn finalize(&mut self) -> Result { + let mut store_update = self.store().store_update(); + Self::write_col_misc(&mut store_update, HEAD_KEY, &mut self.head)?; + Self::write_col_misc(&mut store_update, TAIL_KEY, &mut self.tail)?; + Self::write_col_misc(&mut store_update, CHUNK_TAIL_KEY, &mut self.chunk_tail)?; + Self::write_col_misc(&mut store_update, FORK_TAIL_KEY, &mut self.fork_tail)?; + Self::write_col_misc(&mut store_update, HEADER_HEAD_KEY, &mut self.header_head)?; + Self::write_col_misc(&mut store_update, FINAL_HEAD_KEY, &mut self.final_head)?; + Self::write_col_misc( + &mut store_update, + LARGEST_TARGET_HEIGHT_KEY, + &mut self.largest_target_height, + )?; + debug_assert!(self.chain_store_cache_update.blocks.len() <= 1); + for (hash, block) in self.chain_store_cache_update.blocks.iter() { + let mut map = HashMap::clone( + self.chain_store.get_all_block_hashes_by_height(block.header().height())?.as_ref(), + ); + map.entry(block.header().epoch_id().clone()) + .or_insert_with(|| HashSet::new()) + .insert(*hash); + store_update.set_ser( + DBCol::BlockPerHeight, + &index_to_bytes(block.header().height()), + &map, + )?; + self.chain_store_cache_update + .block_hash_per_height + .insert(block.header().height(), map); + store_update.insert_ser(DBCol::Block, hash.as_ref(), block)?; + } + let mut header_hashes_by_height: HashMap> = HashMap::new(); + for (hash, header) in self.chain_store_cache_update.headers.iter() { + if self.chain_store.get_block_header(hash).is_ok() { + // No need to add same Header once again + continue; + } + + header_hashes_by_height + .entry(header.height()) + .or_insert_with(|| { + self.chain_store + .get_all_header_hashes_by_height(header.height()) + .unwrap_or_default() + }) + .insert(*hash); + store_update.insert_ser(DBCol::BlockHeader, hash.as_ref(), header)?; + } + for (height, hash_set) in header_hashes_by_height { + store_update.set_ser( + DBCol::HeaderHashesByHeight, + &index_to_bytes(height), + &hash_set, + )?; + } + for ((block_hash, shard_uid), chunk_extra) in + self.chain_store_cache_update.chunk_extras.iter() + { + store_update.set_ser( + DBCol::ChunkExtra, + &get_block_shard_uid(block_hash, shard_uid), + chunk_extra, + )?; + } + for (block_hash, block_extra) in self.chain_store_cache_update.block_extras.iter() { + store_update.insert_ser(DBCol::BlockExtra, block_hash.as_ref(), block_extra)?; + } + let mut chunk_hashes_by_height: HashMap> = HashMap::new(); + for (chunk_hash, chunk) in self.chain_store_cache_update.chunks.iter() { + if self.chain_store.chunk_exists(chunk_hash)? { + // No need to add same Chunk once again + continue; + } + + let height_created = chunk.height_created(); + match chunk_hashes_by_height.entry(height_created) { + Entry::Occupied(mut entry) => { + entry.get_mut().insert(chunk_hash.clone()); + } + Entry::Vacant(entry) => { + let mut hash_set = + match self.chain_store.get_all_chunk_hashes_by_height(height_created) { + Ok(hash_set) => hash_set.clone(), + Err(_) => HashSet::new(), + }; + hash_set.insert(chunk_hash.clone()); + entry.insert(hash_set); + } + }; + + // Increase transaction refcounts for all included txs + for tx in chunk.transactions().iter() { + let bytes = borsh::to_vec(&tx).expect("Borsh cannot fail"); + store_update.increment_refcount( + DBCol::Transactions, + tx.get_hash().as_ref(), + &bytes, + ); + } + + // Increase receipt refcounts for all included receipts + for receipt in chunk.prev_outgoing_receipts().iter() { + let bytes = borsh::to_vec(&receipt).expect("Borsh cannot fail"); + store_update.increment_refcount( + DBCol::Receipts, + receipt.get_hash().as_ref(), + &bytes, + ); + } + + store_update.insert_ser(DBCol::Chunks, chunk_hash.as_ref(), chunk)?; + } + for (height, hash_set) in chunk_hashes_by_height { + store_update.set_ser(DBCol::ChunkHashesByHeight, &index_to_bytes(height), &hash_set)?; + } + for (chunk_hash, partial_chunk) in self.chain_store_cache_update.partial_chunks.iter() { + store_update.insert_ser(DBCol::PartialChunks, chunk_hash.as_ref(), partial_chunk)?; + } + for (height, hash) in self.chain_store_cache_update.height_to_hashes.iter() { + if let Some(hash) = hash { + store_update.set_ser(DBCol::BlockHeight, &index_to_bytes(*height), hash)?; + } else { + store_update.delete(DBCol::BlockHeight, &index_to_bytes(*height)); + } + } + for (block_hash, next_hash) in self.chain_store_cache_update.next_block_hashes.iter() { + store_update.set_ser(DBCol::NextBlockHashes, block_hash.as_ref(), next_hash)?; + } + for (epoch_hash, light_client_block) in + self.chain_store_cache_update.epoch_light_client_blocks.iter() + { + store_update.set_ser( + DBCol::EpochLightClientBlocks, + epoch_hash.as_ref(), + light_client_block, + )?; + } + for ((block_hash, shard_id), receipt) in + self.chain_store_cache_update.outgoing_receipts.iter() + { + store_update.set_ser( + DBCol::OutgoingReceipts, + &get_block_shard_id(block_hash, *shard_id), + receipt, + )?; + } + for ((block_hash, shard_id), receipt) in + self.chain_store_cache_update.incoming_receipts.iter() + { + store_update.set_ser( + DBCol::IncomingReceipts, + &get_block_shard_id(block_hash, *shard_id), + receipt, + )?; + } + for ((outcome_id, block_hash), outcome_with_proof) in + self.chain_store_cache_update.outcomes.iter() + { + store_update.insert_ser( + DBCol::TransactionResultForBlock, + &get_outcome_id_block_hash(outcome_id, block_hash), + &outcome_with_proof, + )?; + } + for ((block_hash, shard_id), ids) in self.chain_store_cache_update.outcome_ids.iter() { + store_update.set_ser( + DBCol::OutcomeIds, + &get_block_shard_id(block_hash, *shard_id), + &ids, + )?; + } + for (receipt_id, shard_id) in self.chain_store_cache_update.receipt_id_to_shard_id.iter() { + let data = borsh::to_vec(&shard_id)?; + store_update.increment_refcount(DBCol::ReceiptIdToShardId, receipt_id.as_ref(), &data); + } + for (block_hash, refcount) in self.chain_store_cache_update.block_refcounts.iter() { + store_update.set_ser(DBCol::BlockRefCount, block_hash.as_ref(), refcount)?; + } + for (block_hash, block_merkle_tree) in + self.chain_store_cache_update.block_merkle_tree.iter() + { + store_update.set_ser(DBCol::BlockMerkleTree, block_hash.as_ref(), block_merkle_tree)?; + } + for (block_ordinal, block_hash) in + self.chain_store_cache_update.block_ordinal_to_hash.iter() + { + store_update.set_ser( + DBCol::BlockOrdinal, + &index_to_bytes(*block_ordinal), + block_hash, + )?; + } + + // Convert trie changes to database ops for trie nodes. + // Create separate store update for deletions, because we want to update cache and don't want to remove nodes + // from the store. + let mut deletions_store_update = self.store().store_update(); + for mut wrapped_trie_changes in self.trie_changes.drain(..) { + wrapped_trie_changes.apply_mem_changes(); + wrapped_trie_changes.insertions_into(&mut store_update); + wrapped_trie_changes.deletions_into(&mut deletions_store_update); + wrapped_trie_changes.state_changes_into(&mut store_update); + + if self.chain_store.save_trie_changes { + wrapped_trie_changes + .trie_changes_into(&mut store_update) + .map_err(|err| Error::Other(err.to_string()))?; + } + } + + for ((block_hash, shard_id), state_changes) in self.add_state_changes_for_resharding.drain() + { + store_update.set_ser( + DBCol::StateChangesForSplitStates, + &get_block_shard_id(&block_hash, shard_id), + &state_changes, + )?; + } + for (block_hash, shard_id) in self.remove_state_changes_for_resharding.drain() { + store_update.delete( + DBCol::StateChangesForSplitStates, + &get_block_shard_id(&block_hash, shard_id), + ); + } + + let mut affected_catchup_blocks = HashSet::new(); + for (prev_hash, hash) in self.remove_blocks_to_catchup.drain(..) { + assert!(!affected_catchup_blocks.contains(&prev_hash)); + if affected_catchup_blocks.contains(&prev_hash) { + return Err(Error::Other( + "Multiple changes to the store affect the same catchup block".to_string(), + )); + } + affected_catchup_blocks.insert(prev_hash); + + let mut prev_table = + self.chain_store.get_blocks_to_catchup(&prev_hash).unwrap_or_else(|_| vec![]); + + let mut remove_idx = prev_table.len(); + for (i, val) in prev_table.iter().enumerate() { + if *val == hash { + remove_idx = i; + } + } + + assert_ne!(remove_idx, prev_table.len()); + prev_table.swap_remove(remove_idx); + + if !prev_table.is_empty() { + store_update.set_ser(DBCol::BlocksToCatchup, prev_hash.as_ref(), &prev_table)?; + } else { + store_update.delete(DBCol::BlocksToCatchup, prev_hash.as_ref()); + } + } + for prev_hash in self.remove_prev_blocks_to_catchup.drain(..) { + assert!(!affected_catchup_blocks.contains(&prev_hash)); + if affected_catchup_blocks.contains(&prev_hash) { + return Err(Error::Other( + "Multiple changes to the store affect the same catchup block".to_string(), + )); + } + affected_catchup_blocks.insert(prev_hash); + + store_update.delete(DBCol::BlocksToCatchup, prev_hash.as_ref()); + } + for (prev_hash, new_hash) in self.add_blocks_to_catchup.drain(..) { + assert!(!affected_catchup_blocks.contains(&prev_hash)); + if affected_catchup_blocks.contains(&prev_hash) { + return Err(Error::Other( + "Multiple changes to the store affect the same catchup block".to_string(), + )); + } + affected_catchup_blocks.insert(prev_hash); + + let mut prev_table = + self.chain_store.get_blocks_to_catchup(&prev_hash).unwrap_or_else(|_| vec![]); + prev_table.push(new_hash); + store_update.set_ser(DBCol::BlocksToCatchup, prev_hash.as_ref(), &prev_table)?; + } + for state_sync_info in self.add_state_sync_infos.drain(..) { + store_update.set_ser( + DBCol::StateDlInfos, + state_sync_info.epoch_tail_hash.as_ref(), + &state_sync_info, + )?; + } + for hash in self.remove_state_sync_infos.drain(..) { + store_update.delete(DBCol::StateDlInfos, hash.as_ref()); + } + for hash in self.challenged_blocks.drain() { + store_update.set_ser(DBCol::ChallengedBlocks, hash.as_ref(), &true)?; + } + for (chunk_hash, chunk) in self.chain_store_cache_update.invalid_chunks.iter() { + store_update.insert_ser(DBCol::InvalidChunks, chunk_hash.as_ref(), chunk)?; + } + for block_height in self.chain_store_cache_update.processed_block_heights.iter() { + store_update.set_ser( + DBCol::ProcessedBlockHeights, + &index_to_bytes(*block_height), + &(), + )?; + } + for other in self.store_updates.drain(..) { + store_update.merge(other); + } + Ok(store_update) + } + + pub fn commit(mut self) -> Result<(), Error> { + let store_update = self.finalize()?; + store_update.commit()?; + let ChainStoreCacheUpdate { + blocks, + headers, + block_extras, + chunk_extras, + chunks, + partial_chunks, + block_hash_per_height, + height_to_hashes, + next_block_hashes, + epoch_light_client_blocks, + outgoing_receipts, + incoming_receipts, + invalid_chunks, + receipt_id_to_shard_id, + transactions, + receipts, + block_refcounts, + block_merkle_tree, + block_ordinal_to_hash, + processed_block_heights, + + outcomes: _, + outcome_ids: _, + } = self.chain_store_cache_update; + for (hash, block) in blocks { + self.chain_store.blocks.put(hash.into(), block); + } + for (hash, header) in headers { + self.chain_store.headers.put(hash.into(), header); + } + for (hash, block_extra) in block_extras { + self.chain_store.block_extras.put(hash.into(), block_extra); + } + for ((block_hash, shard_uid), chunk_extra) in chunk_extras { + let key = get_block_shard_uid(&block_hash, &shard_uid); + self.chain_store.chunk_extras.put(key, chunk_extra); + } + for (hash, chunk) in chunks { + self.chain_store.chunks.put(hash.into(), chunk); + } + for (hash, partial_chunk) in partial_chunks { + self.chain_store.partial_chunks.put(hash.into(), partial_chunk); + } + for (height, epoch_id_to_hash) in block_hash_per_height { + self.chain_store + .block_hash_per_height + .put(index_to_bytes(height).to_vec(), Arc::new(epoch_id_to_hash)); + } + for (height, block_hash) in height_to_hashes { + let bytes = index_to_bytes(height); + if let Some(hash) = block_hash { + self.chain_store.height.put(bytes.to_vec(), hash); + } else { + self.chain_store.height.pop(&bytes.to_vec()); + } + } + for (block_hash, next_hash) in next_block_hashes { + self.chain_store.next_block_hashes.put(block_hash.into(), next_hash); + } + for (epoch_hash, light_client_block) in epoch_light_client_blocks { + self.chain_store.epoch_light_client_blocks.put(epoch_hash.into(), light_client_block); + } + for ((block_hash, shard_id), shard_outgoing_receipts) in outgoing_receipts { + let key = get_block_shard_id(&block_hash, shard_id); + self.chain_store.outgoing_receipts.put(key, shard_outgoing_receipts); + } + for ((block_hash, shard_id), shard_incoming_receipts) in incoming_receipts { + let key = get_block_shard_id(&block_hash, shard_id); + self.chain_store.incoming_receipts.put(key, shard_incoming_receipts); + } + for (hash, invalid_chunk) in invalid_chunks { + self.chain_store.invalid_chunks.put(hash.into(), invalid_chunk); + } + for (receipt_id, shard_id) in receipt_id_to_shard_id { + self.chain_store.receipt_id_to_shard_id.put(receipt_id.into(), shard_id); + } + for (hash, transaction) in transactions { + self.chain_store.transactions.put(hash.into(), transaction); + } + for (receipt_id, receipt) in receipts { + self.chain_store.receipts.put(receipt_id.into(), receipt); + } + for (block_hash, refcount) in block_refcounts { + self.chain_store.block_refcounts.put(block_hash.into(), refcount); + } + for (block_hash, merkle_tree) in block_merkle_tree { + self.chain_store.block_merkle_tree.put(block_hash.into(), merkle_tree); + } + for (block_ordinal, block_hash) in block_ordinal_to_hash { + self.chain_store + .block_ordinal_to_hash + .put(index_to_bytes(block_ordinal).to_vec(), block_hash); + } + for block_height in processed_block_heights { + self.chain_store.processed_block_heights.put(index_to_bytes(block_height).to_vec(), ()); + } + self.chain_store.head = self.head; + self.chain_store.tail = self.tail; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use crate::test_utils::get_chain; + use unc_primitives::errors::InvalidTxError; + use unc_primitives::hash::hash; + use unc_primitives::test_utils::create_test_signer; + use unc_primitives::test_utils::TestBlockBuilder; + use unc_primitives::types::EpochId; + use unc_primitives::utils::index_to_bytes; + + #[test] + fn test_tx_validity_long_fork() { + let transaction_validity_period = 5; + let mut chain = get_chain(); + let genesis = chain.get_block_by_height(0).unwrap(); + let signer = Arc::new(create_test_signer("test1")); + let short_fork = vec![TestBlockBuilder::new(&genesis, signer.clone()).build()]; + let mut store_update = chain.mut_chain_store().store_update(); + store_update.save_block_header(short_fork[0].header().clone()).unwrap(); + store_update.commit().unwrap(); + + let short_fork_head = short_fork[0].header().clone(); + assert!(chain + .mut_chain_store() + .check_transaction_validity_period( + &short_fork_head, + genesis.hash(), + transaction_validity_period + ) + .is_ok()); + let mut long_fork = vec![]; + let mut prev_block = genesis; + for i in 1..(transaction_validity_period + 3) { + let mut store_update = chain.mut_chain_store().store_update(); + let block = TestBlockBuilder::new(&prev_block, signer.clone()).height(i).build(); + prev_block = block.clone(); + store_update.save_block_header(block.header().clone()).unwrap(); + store_update + .update_height_if_not_challenged(block.header().height(), *block.hash()) + .unwrap(); + long_fork.push(block); + store_update.commit().unwrap(); + } + let valid_base_hash = long_fork[1].hash(); + let cur_header = &long_fork.last().unwrap().header(); + assert!(chain + .mut_chain_store() + .check_transaction_validity_period( + cur_header, + valid_base_hash, + transaction_validity_period + ) + .is_ok()); + let invalid_base_hash = long_fork[0].hash(); + assert_eq!( + chain.mut_chain_store().check_transaction_validity_period( + cur_header, + invalid_base_hash, + transaction_validity_period + ), + Err(InvalidTxError::Expired) + ); + } + + #[test] + fn test_tx_validity_normal_case() { + let transaction_validity_period = 5; + let mut chain = get_chain(); + let genesis = chain.get_block_by_height(0).unwrap(); + let signer = Arc::new(create_test_signer("test1")); + let mut blocks = vec![]; + let mut prev_block = genesis; + for i in 1..(transaction_validity_period + 2) { + let mut store_update = chain.mut_chain_store().store_update(); + let block = TestBlockBuilder::new(&prev_block, signer.clone()).height(i).build(); + prev_block = block.clone(); + store_update.save_block_header(block.header().clone()).unwrap(); + store_update + .update_height_if_not_challenged(block.header().height(), *block.hash()) + .unwrap(); + blocks.push(block); + store_update.commit().unwrap(); + } + let valid_base_hash = blocks[1].hash(); + let cur_header = &blocks.last().unwrap().header(); + assert!(chain + .mut_chain_store() + .check_transaction_validity_period( + cur_header, + valid_base_hash, + transaction_validity_period + ) + .is_ok()); + let new_block = TestBlockBuilder::new(&blocks.last().unwrap(), signer) + .height(transaction_validity_period + 3) + .build(); + + let mut store_update = chain.mut_chain_store().store_update(); + store_update.save_block_header(new_block.header().clone()).unwrap(); + store_update + .update_height_if_not_challenged(new_block.header().height(), *new_block.hash()) + .unwrap(); + store_update.commit().unwrap(); + assert_eq!( + chain.mut_chain_store().check_transaction_validity_period( + new_block.header(), + valid_base_hash, + transaction_validity_period + ), + Err(InvalidTxError::Expired) + ); + } + + #[test] + fn test_tx_validity_off_by_one() { + let transaction_validity_period = 5; + let mut chain = get_chain(); + let genesis = chain.get_block_by_height(0).unwrap(); + let genesis_hash = *genesis.hash(); + let signer = Arc::new(create_test_signer("test1")); + let mut short_fork = vec![]; + let mut prev_block = genesis.clone(); + for i in 1..(transaction_validity_period + 2) { + let mut store_update = chain.mut_chain_store().store_update(); + let block = TestBlockBuilder::new(&prev_block, signer.clone()).height(i).build(); + prev_block = block.clone(); + store_update.save_block_header(block.header().clone()).unwrap(); + short_fork.push(block); + store_update.commit().unwrap(); + } + + let short_fork_head = short_fork.last().unwrap().header().clone(); + assert_eq!( + chain.mut_chain_store().check_transaction_validity_period( + &short_fork_head, + &genesis_hash, + transaction_validity_period + ), + Err(InvalidTxError::Expired) + ); + let mut long_fork = vec![]; + let mut prev_block = genesis; + for i in 1..(transaction_validity_period * 5) { + let mut store_update = chain.mut_chain_store().store_update(); + let block = TestBlockBuilder::new(&prev_block, signer.clone()).height(i).build(); + prev_block = block.clone(); + store_update.save_block_header(block.header().clone()).unwrap(); + long_fork.push(block); + store_update.commit().unwrap(); + } + let long_fork_head = &long_fork.last().unwrap().header(); + assert_eq!( + chain.mut_chain_store().check_transaction_validity_period( + long_fork_head, + &genesis_hash, + transaction_validity_period + ), + Err(InvalidTxError::Expired) + ); + } + + #[test] + fn test_cache_invalidation() { + let mut chain = get_chain(); + let genesis = chain.get_block_by_height(0).unwrap(); + let signer = Arc::new(create_test_signer("test1")); + let block1 = TestBlockBuilder::new(&genesis, signer.clone()).build(); + let mut block2 = block1.clone(); + block2.mut_header().get_mut().inner_lite.epoch_id = EpochId(hash(&[1, 2, 3])); + block2.mut_header().resign(&*signer); + + let mut store_update = chain.mut_chain_store().store_update(); + store_update.chain_store_cache_update.height_to_hashes.insert(1, Some(hash(&[1]))); + store_update + .chain_store_cache_update + .blocks + .insert(*block1.header().hash(), block1.clone()); + store_update.commit().unwrap(); + + let block_hash = chain.mut_chain_store().height.get(&index_to_bytes(1).to_vec()); + let epoch_id_to_hash = + chain.mut_chain_store().block_hash_per_height.get(&index_to_bytes(1).to_vec()); + + let mut store_update = chain.mut_chain_store().store_update(); + store_update.chain_store_cache_update.height_to_hashes.insert(1, Some(hash(&[2]))); + store_update + .chain_store_cache_update + .blocks + .insert(*block2.header().hash(), block2.clone()); + store_update.commit().unwrap(); + + let block_hash1 = chain.mut_chain_store().height.get(&index_to_bytes(1).to_vec()); + let epoch_id_to_hash1 = + chain.mut_chain_store().block_hash_per_height.get(&index_to_bytes(1).to_vec()); + + assert_ne!(block_hash, block_hash1); + assert_ne!(epoch_id_to_hash, epoch_id_to_hash1); + } +} diff --git a/chain/chain/src/store_validator.rs b/chain/chain/src/store_validator.rs new file mode 100644 index 000000000..96ea9f025 --- /dev/null +++ b/chain/chain/src/store_validator.rs @@ -0,0 +1,487 @@ +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; +use std::time::{Duration, Instant}; + +use borsh::BorshDeserialize; +use enum_map::Enum; +use unc_epoch_manager::shard_tracker::ShardTracker; +use unc_epoch_manager::EpochManagerAdapter; +use strum::IntoEnumIterator; +use tracing::warn; + +use unc_chain_configs::GenesisConfig; +use unc_primitives::block::{Block, BlockHeader}; +use unc_primitives::borsh; +use unc_primitives::epoch_manager::block_info::BlockInfo; +use unc_primitives::epoch_manager::epoch_info::EpochInfo; +use unc_primitives::epoch_manager::AGGREGATOR_KEY; +use unc_primitives::hash::CryptoHash; +use unc_primitives::sharding::{ChunkHash, ShardChunk, StateSyncInfo}; +use unc_primitives::state_sync::{ShardStateSyncResponseHeader, StateHeaderKey, StatePartKey}; +use unc_primitives::transaction::ExecutionOutcomeWithProof; +use unc_primitives::types::chunk_extra::ChunkExtra; +use unc_primitives::types::{AccountId, BlockHeight, EpochId}; +use unc_primitives::utils::{get_block_shard_id_rev, get_outcome_id_block_hash_rev}; +use unc_store::db::refcount; +use unc_store::{DBCol, Store, TrieChanges}; +use validate::StoreValidatorError; + +use crate::types::RuntimeAdapter; +use unc_primitives::shard_layout::get_block_shard_uid_rev; +use unc_primitives::static_clock::StaticClock; + +mod validate; + +pub struct StoreValidatorCache { + head: BlockHeight, + header_head: BlockHeight, + tail: BlockHeight, + chunk_tail: BlockHeight, + block_heights_less_tail: Vec, + + tx_refcount: HashMap, + receipt_refcount: HashMap, + block_refcount: HashMap, + genesis_blocks: Vec, +} + +impl StoreValidatorCache { + fn new() -> Self { + Self { + head: 0, + header_head: 0, + tail: 0, + chunk_tail: 0, + block_heights_less_tail: vec![], + tx_refcount: HashMap::new(), + receipt_refcount: HashMap::new(), + block_refcount: HashMap::new(), + genesis_blocks: vec![], + } + } +} + +#[derive(Debug)] +pub struct ErrorMessage { + pub col: String, + pub key: String, + pub err: StoreValidatorError, +} + +pub struct StoreValidator { + me: Option, + config: GenesisConfig, + epoch_manager: Arc, + shard_tracker: ShardTracker, + runtime: Arc, + store: Store, + inner: StoreValidatorCache, + timeout: Option, + start_time: Instant, + pub is_archival: bool, + + pub errors: Vec, + tests: u64, +} + +impl StoreValidator { + pub fn new( + me: Option, + config: GenesisConfig, + epoch_manager: Arc, + shard_tracker: ShardTracker, + runtime: Arc, + store: Store, + is_archival: bool, + ) -> Self { + StoreValidator { + me, + config, + epoch_manager, + shard_tracker, + runtime, + store: store, + inner: StoreValidatorCache::new(), + timeout: None, + start_time: StaticClock::instant(), + is_archival, + errors: vec![], + tests: 0, + } + } + pub fn set_timeout(&mut self, timeout: u64) { + self.timeout = Some(timeout) + } + pub fn is_failed(&self) -> bool { + self.tests == 0 || !self.errors.is_empty() + } + pub fn num_failed(&self) -> u64 { + self.errors.len() as u64 + } + pub fn tests_done(&self) -> u64 { + self.tests + } + fn process_error(&mut self, err: StoreValidatorError, key: K, col: DBCol) { + self.errors.push(ErrorMessage { key: format!("{key:?}"), col: col.to_string(), err }) + } + fn validate_col(&mut self, col: DBCol) -> Result<(), StoreValidatorError> { + for item in self.store.clone().iter_raw_bytes(col) { + let (key, value) = item?; + let key_ref = key.as_ref(); + let value_ref = value.as_ref(); + match col { + DBCol::BlockHeader => { + let block_hash = CryptoHash::try_from(key_ref)?; + let header = BlockHeader::try_from_slice(value_ref)?; + // Block Header Hash is valid + self.check(&validate::block_header_hash_validity, &block_hash, &header, col); + // Block Header Height is valid + self.check(&validate::block_header_height_validity, &block_hash, &header, col); + // Block Header can be indexed by Height + self.check(&validate::header_hash_indexed_by_height, &block_hash, &header, col); + } + DBCol::Block => { + let block_hash = CryptoHash::try_from(key_ref)?; + let block = Block::try_from_slice(value_ref)?; + // Block Hash is valid + self.check(&validate::block_hash_validity, &block_hash, &block, col); + // Block Height is valid + self.check(&validate::block_height_validity, &block_hash, &block, col); + // Block can be indexed by its Height + self.check(&validate::block_indexed_by_height, &block_hash, &block, col); + // Block Header for current Block exists + self.check(&validate::block_header_exists, &block_hash, &block, col); + // Chunks for current Block exist + self.check(&validate::block_chunks_exist, &block_hash, &block, col); + // Chunks for current Block have Height Created not higher than Block Height + self.check(&validate::block_chunks_height_validity, &block_hash, &block, col); + // BlockInfo for current Block exists + self.check(&validate::block_info_exists, &block_hash, &block, col); + // EpochInfo for current Epoch id of Block exists + self.check(&validate::block_epoch_exists, &block_hash, &block, col); + // Increase Block Refcount + self.check(&validate::block_increment_refcount, &block_hash, &block, col); + } + DBCol::BlockHeight => { + let height = BlockHeight::try_from_slice(key_ref)?; + let hash = CryptoHash::try_from(value_ref)?; + // Block on the Canonical Chain is stored properly + self.check(&validate::canonical_header_validity, &height, &hash, col); + // If prev Block exists, it's also on the Canonical Chain and + // there are no Blocks in range (prev_height, height) on the Canonical Chain + self.check(&validate::canonical_prev_block_validity, &height, &hash, col); + } + DBCol::Chunks => { + let chunk_hash = ChunkHash::try_from_slice(key_ref)?; + let shard_chunk = ShardChunk::try_from_slice(value_ref)?; + // Chunk Hash is valid + self.check(&validate::chunk_hash_validity, &chunk_hash, &shard_chunk, col); + // Chunk Height Created is not lower than Chunk Tail + self.check(&validate::chunk_tail_validity, &chunk_hash, &shard_chunk, col); + // ShardChunk can be indexed by Height + self.check( + &validate::chunk_indexed_by_height_created, + &chunk_hash, + &shard_chunk, + col, + ); + // Check that all Txs in Chunk exist + self.check(&validate::chunk_tx_exists, &chunk_hash, &shard_chunk, col); + } + DBCol::ChunkExtra => { + let (block_hash, shard_uid) = get_block_shard_uid_rev(key_ref)?; + let chunk_extra = ChunkExtra::try_from_slice(value_ref)?; + self.check( + &validate::chunk_extra_block_exists, + &(block_hash, shard_uid), + &chunk_extra, + col, + ); + } + DBCol::TrieChanges => { + let (block_hash, shard_uid) = get_block_shard_uid_rev(key_ref)?; + let trie_changes = TrieChanges::try_from_slice(value_ref)?; + // ShardChunk should exist for current TrieChanges + self.check( + &validate::trie_changes_chunk_extra_exists, + &(block_hash, shard_uid), + &trie_changes, + col, + ); + } + DBCol::ChunkHashesByHeight => { + let height = BlockHeight::try_from_slice(key_ref)?; + let chunk_hashes = HashSet::::try_from_slice(value_ref)?; + // ShardChunk which can be indexed by Height exists + self.check(&validate::chunk_of_height_exists, &height, &chunk_hashes, col); + } + DBCol::HeaderHashesByHeight => { + let height = BlockHeight::try_from_slice(key_ref)?; + let header_hashes = HashSet::::try_from_slice(value_ref)?; + // Headers which can be indexed by Height exists + self.check( + &validate::header_hash_of_height_exists, + &height, + &header_hashes, + col, + ); + } + DBCol::OutcomeIds => { + let (block_hash, _) = get_block_shard_id_rev(key_ref)?; + let outcome_ids = Vec::::try_from_slice(value_ref)?; + // TransactionResultForBlock should exist for outcome ID and block hash + self.check( + &validate::outcome_by_outcome_id_exists, + &block_hash, + &outcome_ids, + col, + ); + // Block which can be indexed by Outcome block_hash exists + self.check(&validate::outcome_id_block_exists, &block_hash, &outcome_ids, col); + } + DBCol::TransactionResultForBlock => { + let (outcome_id, block_hash) = get_outcome_id_block_hash_rev(key_ref)?; + let outcome = ::try_from_slice(value_ref)?; + // Outcome is reachable in ColOutcomesByBlockHash + self.check( + &validate::outcome_indexed_by_block_hash, + &(outcome_id, block_hash), + &outcome, + col, + ); + } + DBCol::StateDlInfos => { + let block_hash = CryptoHash::try_from(key_ref)?; + let state_sync_info = StateSyncInfo::try_from_slice(value_ref)?; + // StateSyncInfo is valid + self.check( + &validate::state_sync_info_valid, + &block_hash, + &state_sync_info, + col, + ); + // Block which can be indexed by StateSyncInfo exists + self.check( + &validate::state_sync_info_block_exists, + &block_hash, + &state_sync_info, + col, + ); + } + DBCol::BlockInfo => { + let block_hash = CryptoHash::try_from(key_ref)?; + let block_info = BlockInfo::try_from_slice(value_ref)?; + // Block which can be indexed by BlockInfo exists + self.check( + &validate::block_info_block_header_exists, + &block_hash, + &block_info, + col, + ); + } + DBCol::EpochInfo => { + if key_ref != AGGREGATOR_KEY { + let epoch_id = EpochId::try_from_slice(key_ref)?; + let epoch_info = EpochInfo::try_from_slice(value_ref)?; + // Epoch should exist + self.check(&validate::epoch_validity, &epoch_id, &epoch_info, col); + } + } + DBCol::Transactions => { + let (_value, rc) = refcount::decode_value_with_rc(value_ref); + let tx_hash = CryptoHash::try_from(key_ref)?; + self.check(&validate::tx_refcount, &tx_hash, &(rc as u64), col); + } + DBCol::Receipts => { + let (_value, rc) = refcount::decode_value_with_rc(value_ref); + let receipt_id = CryptoHash::try_from(key_ref)?; + self.check(&validate::receipt_refcount, &receipt_id, &(rc as u64), col); + } + DBCol::BlockRefCount => { + let block_hash = CryptoHash::try_from(key_ref)?; + let refcount = u64::try_from_slice(value_ref)?; + self.check(&validate::block_refcount, &block_hash, &refcount, col); + } + DBCol::StateHeaders => { + let key = StateHeaderKey::try_from_slice(key_ref)?; + let header = ShardStateSyncResponseHeader::try_from_slice(value_ref)?; + self.check(&validate::state_header_block_exists, &key, &header, col); + } + DBCol::StateParts => { + let key = StatePartKey::try_from_slice(key_ref)?; + self.check(&validate::state_part_header_exists, &key, value_ref, col); + } + _ => {} + } + if let Some(timeout) = self.timeout { + if self.start_time.elapsed() > Duration::from_millis(timeout) { + return Ok(()); + } + } + } + Ok(()) + } + + pub fn validate(&mut self) { + self.start_time = StaticClock::instant(); + + // Init checks + // Check Head-Tail validity and fill cache with their values + if let Err(e) = validate::head_tail_validity(self) { + self.process_error(e, "HEAD / HEADER_HEAD / TAIL / CHUNK_TAIL", DBCol::BlockMisc) + } + + // Main loop + for col in DBCol::iter() { + if let Err(e) = self.validate_col(col) { + self.process_error(e, col.to_string(), col) + } + if let Some(timeout) = self.timeout { + if self.start_time.elapsed() > Duration::from_millis(timeout) { + warn!(target: "adversary", "Store validator hit timeout at {col} ({}/{})", col.into_usize(), DBCol::LENGTH); + return; + } + } + } + if let Some(timeout) = self.timeout { + // We didn't complete all Column checks and cannot do final checks, returning here + if self.start_time.elapsed() > Duration::from_millis(timeout) { + warn!(target: "adversary", "Store validator hit timeout before final checks"); + return; + } + } + + // Final checks + // There is no more than one Block which Height is lower than Tail and not equal to Genesis + if let Err(e) = validate::block_height_cmp_tail_final(self) { + self.process_error(e, "TAIL", DBCol::BlockMisc) + } + // Check that all refs are counted + if let Err(e) = validate::tx_refcount_final(self) { + self.process_error(e, "TX_REFCOUNT", DBCol::Transactions) + } + if let Err(e) = validate::receipt_refcount_final(self) { + self.process_error(e, "RECEIPT_REFCOUNT", DBCol::Receipts) + } + // Check that all Block Refcounts are counted + if let Err(e) = validate::block_refcount_final(self) { + self.process_error(e, "BLOCK_REFCOUNT", DBCol::BlockRefCount) + } + } + + fn check( + &mut self, + f: &dyn Fn(&mut StoreValidator, &K, &V) -> Result<(), StoreValidatorError>, + key: &K, + value: &V, + col: DBCol, + ) { + self.tests += 1; + if let Err(e) = f(self, key, value) { + self.process_error(e, key, col); + } + } +} + +#[cfg(test)] +mod tests { + use unc_store::test_utils::create_test_store; + + use crate::test_utils::{KeyValueRuntime, MockEpochManager}; + use crate::types::ChainConfig; + use crate::{Chain, ChainGenesis, ChainStoreAccess, DoomslugThresholdMode}; + + use super::*; + + fn init() -> (Chain, StoreValidator) { + let store = create_test_store(); + let chain_genesis = ChainGenesis::test(); + let epoch_manager = MockEpochManager::new(store.clone(), chain_genesis.epoch_length); + let shard_tracker = ShardTracker::new_empty(epoch_manager.clone()); + let runtime = KeyValueRuntime::new(store.clone(), epoch_manager.as_ref()); + let mut genesis = GenesisConfig::default(); + genesis.genesis_height = 0; + let chain = Chain::new( + epoch_manager.clone(), + shard_tracker.clone(), + runtime.clone(), + &chain_genesis, + DoomslugThresholdMode::NoApprovals, + ChainConfig::test(), + None, + ) + .unwrap(); + ( + chain, + StoreValidator::new(None, genesis, epoch_manager, shard_tracker, runtime, store, false), + ) + } + + #[test] + fn test_io_error() { + let (chain, mut sv) = init(); + let mut store_update = chain.chain_store().store().store_update(); + assert!(sv.validate_col(DBCol::Block).is_ok()); + // Use `set_raw` to ruthlessly override block data with some garbage, + // simulating IO error. + store_update.set_raw_bytes( + DBCol::Block, + chain.get_block_by_height(0).unwrap().hash().as_ref(), + &[123], + ); + store_update.commit().unwrap(); + match sv.validate_col(DBCol::Block) { + Err(StoreValidatorError::IOError(_)) => {} + _ => assert!(false), + } + } + + #[test] + fn test_db_corruption() { + let (chain, mut sv) = init(); + let mut store_update = chain.chain_store().store().store_update(); + assert!(sv.validate_col(DBCol::TrieChanges).is_ok()); + store_update.set_ser::<[u8]>(DBCol::TrieChanges, "567".as_ref(), &[123]).unwrap(); + store_update.commit().unwrap(); + match sv.validate_col(DBCol::TrieChanges) { + Err(StoreValidatorError::DBCorruption(_)) => {} + _ => assert!(false), + } + } + + #[test] + fn test_db_not_found() { + let (chain, mut sv) = init(); + let block = chain.get_block_by_height(0).unwrap(); + assert!(validate::block_header_exists(&mut sv, block.hash(), &block).is_ok()); + match validate::block_header_exists(&mut sv, &CryptoHash::default(), &block) { + Err(StoreValidatorError::DBNotFound { .. }) => {} + _ => assert!(false), + } + } + + #[test] + fn test_discrepancy() { + let (chain, mut sv) = init(); + let block_header = chain.get_block_header_by_height(0).unwrap(); + assert!(validate::block_header_hash_validity(&mut sv, block_header.hash(), &block_header) + .is_ok()); + match validate::block_header_hash_validity(&mut sv, &CryptoHash::default(), &block_header) { + Err(StoreValidatorError::Discrepancy { .. }) => {} + _ => assert!(false), + } + } + + #[test] + fn test_validation_failed() { + let (_chain, mut sv) = init(); + assert!(validate::block_height_cmp_tail_final(&mut sv).is_ok()); + sv.inner.block_heights_less_tail.push(CryptoHash::default()); + assert!(validate::block_height_cmp_tail_final(&mut sv).is_ok()); + sv.inner.block_heights_less_tail.push(CryptoHash::default()); + match validate::block_height_cmp_tail_final(&mut sv) { + Err(StoreValidatorError::ValidationFailed { .. }) => {} + _ => assert!(false), + } + } +} diff --git a/chain/chain/src/store_validator/validate.rs b/chain/chain/src/store_validator/validate.rs new file mode 100644 index 000000000..d66e2e50f --- /dev/null +++ b/chain/chain/src/store_validator/validate.rs @@ -0,0 +1,920 @@ +use crate::StoreValidator; + +use unc_primitives::block::{Block, BlockHeader, Tip}; +use unc_primitives::epoch_manager::block_info::BlockInfo; +use unc_primitives::epoch_manager::epoch_info::EpochInfo; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::{get_block_shard_uid, ShardUId}; +use unc_primitives::sharding::{ChunkHash, ShardChunk, StateSyncInfo}; +use unc_primitives::state_sync::{ShardStateSyncResponseHeader, StateHeaderKey, StatePartKey}; +use unc_primitives::transaction::{ExecutionOutcomeWithProof, SignedTransaction}; +use unc_primitives::types::chunk_extra::ChunkExtra; +use unc_primitives::types::{BlockHeight, EpochId}; +use unc_primitives::utils::{get_block_shard_id, get_outcome_id_block_hash, index_to_bytes}; +use unc_store::{ + DBCol, TrieChanges, CHUNK_TAIL_KEY, FORK_TAIL_KEY, HEADER_HEAD_KEY, HEAD_KEY, TAIL_KEY, +}; +use std::collections::{HashMap, HashSet}; + +#[derive(thiserror::Error, Debug)] +pub enum StoreValidatorError { + #[error(transparent)] + IOError(#[from] std::io::Error), + #[error("DB is corrupted")] + DBCorruption(#[from] Box), + #[error("Function {func_name:?}: data is invalid, {reason:?}")] + InvalidData { func_name: &'static str, reason: String }, + #[error("Function {func_name:?}: data that expected to exist in DB is not found, {reason:?}")] + DBNotFound { func_name: &'static str, reason: String }, + #[error("Function {func_name:?}: {reason:?}, expected {expected:?}, found {found:?}")] + Discrepancy { func_name: &'static str, reason: String, expected: String, found: String }, + #[error("Function {func_name:?}: validation failed, {error:?}")] + ValidationFailed { func_name: &'static str, error: String }, +} + +macro_rules! get_parent_function_name { + () => {{ + fn f() {} + fn type_name_of(_: T) -> &'static str { + std::any::type_name::() + } + let name = type_name_of(f); + name.rsplit_once("::").map_or(name, |(_, name)| name) + }}; +} + +macro_rules! err { + ($($x: expr),*) => ( + return Err(StoreValidatorError::ValidationFailed { + func_name: get_parent_function_name!(), + error: format!($($x),*) + }) + ) +} + +macro_rules! check_discrepancy { + ($arg1: expr, $arg2: expr, $($x: tt),*) => { + if $arg1 != $arg2 { + return Err(StoreValidatorError::Discrepancy { + func_name: get_parent_function_name!(), + reason: format!($($x),*), + expected: format!("{:?}", $arg1), + found: format!("{:?}", $arg2), + }); + } + }; +} + +macro_rules! unwrap_or_err { + ($obj: expr, $($x: tt),*) => { + match $obj { + Ok(value) => value, + Err(e) => { + return Err(StoreValidatorError::InvalidData { + func_name: get_parent_function_name!(), + reason: format!("{}, error: {}", format!($($x),*), e) + }) + } + } + }; +} + +macro_rules! unwrap_or_err_db { + ($obj: expr, $($x: expr),*) => { + match $obj { + Ok(Some(value)) => value, + Err(e) => { + return Err(StoreValidatorError::DBNotFound { + func_name: get_parent_function_name!(), + reason: format!("{}, error: {}", format!($($x),*), e) + }) + } + _ => { + return Err(StoreValidatorError::DBNotFound { + func_name: get_parent_function_name!(), + reason: format!($($x),*) + }) + } + } + }; +} + +// All validations start here + +pub(crate) fn head_tail_validity(sv: &mut StoreValidator) -> Result<(), StoreValidatorError> { + let mut tail = sv.config.genesis_height; + let mut chunk_tail = sv.config.genesis_height; + let mut fork_tail = sv.config.genesis_height; + let tail_db = unwrap_or_err!( + sv.store.get_ser::(DBCol::BlockMisc, TAIL_KEY), + "Can't get Tail from storage" + ); + let chunk_tail_db = unwrap_or_err!( + sv.store.get_ser::(DBCol::BlockMisc, CHUNK_TAIL_KEY), + "Can't get Chunk Tail from storage" + ); + let fork_tail_db = unwrap_or_err!( + sv.store.get_ser::(DBCol::BlockMisc, FORK_TAIL_KEY), + "Can't get Chunk Tail from storage" + ); + if tail_db.is_none() != chunk_tail_db.is_none() { + // Archival nodes can have chunk_tail set without tail being set. + if !sv.is_archival || chunk_tail_db.is_none() { + err!("Tail is {:?} and Chunk Tail is {:?}", tail_db, chunk_tail_db); + } + } + if tail_db.is_some() && fork_tail_db.is_none() { + err!("Tail is {:?} but fork tail is None", tail_db); + } + if tail_db.is_some() { + tail = tail_db.unwrap(); + chunk_tail = chunk_tail_db.unwrap(); + fork_tail = fork_tail_db.unwrap(); + } + let head = unwrap_or_err_db!( + sv.store.get_ser::(DBCol::BlockMisc, HEAD_KEY), + "Can't get Head from storage" + ); + let header_head = unwrap_or_err_db!( + sv.store.get_ser::(DBCol::BlockMisc, HEADER_HEAD_KEY), + "Can't get Header Head from storage" + ); + sv.inner.head = head.height; + sv.inner.header_head = header_head.height; + sv.inner.tail = tail; + sv.inner.chunk_tail = chunk_tail; + if chunk_tail > tail { + err!("chunk_tail > tail, {:?} > {:?}", chunk_tail, tail); + } + if tail > head.height { + err!("tail > head.height, {:?} > {:?}", tail, head); + } + if tail > fork_tail { + err!("tail > fork_tail, {} > {}", tail, fork_tail); + } + if fork_tail > head.height { + err!("fork tail > head.height, {} > {:?}", fork_tail, head); + } + if head.height > header_head.height { + err!("head.height > header_head.height, {:?} > {:?}", tail, head); + } + Ok(()) +} + +pub(crate) fn block_header_hash_validity( + _sv: &mut StoreValidator, + block_hash: &CryptoHash, + header: &BlockHeader, +) -> Result<(), StoreValidatorError> { + check_discrepancy!(header.hash(), block_hash, "Invalid Block Header stored"); + Ok(()) +} + +pub(crate) fn block_header_height_validity( + sv: &mut StoreValidator, + _block_hash: &CryptoHash, + header: &BlockHeader, +) -> Result<(), StoreValidatorError> { + let height = header.height(); + let head = sv.inner.header_head; + if height > head { + err!("Invalid Block Header stored, Head = {:?}, header = {:?}", head, header); + } + Ok(()) +} + +pub(crate) fn block_hash_validity( + _sv: &mut StoreValidator, + block_hash: &CryptoHash, + block: &Block, +) -> Result<(), StoreValidatorError> { + check_discrepancy!(block.hash(), block_hash, "Invalid Block stored"); + Ok(()) +} + +pub(crate) fn block_height_validity( + sv: &mut StoreValidator, + _block_hash: &CryptoHash, + block: &Block, +) -> Result<(), StoreValidatorError> { + let height = block.header().height(); + let tail = sv.inner.tail; + if height <= tail && height != sv.config.genesis_height { + sv.inner.block_heights_less_tail.push(*block.hash()); + } + let head = sv.inner.head; + if height > head { + err!("Invalid Block stored, Head = {:?}, block = {:?}", head, block); + } + Ok(()) +} + +pub(crate) fn block_indexed_by_height( + sv: &mut StoreValidator, + block_hash: &CryptoHash, + block: &Block, +) -> Result<(), StoreValidatorError> { + let height = block.header().height(); + let block_hashes: HashSet = unwrap_or_err_db!( + sv.store.get_ser::>>( + DBCol::BlockPerHeight, + &index_to_bytes(height) + ), + "Can't get HashMap for Height {:?} from DBCol::BlockPerHeight", + height + ) + .values() + .flatten() + .cloned() + .collect(); + if !block_hashes.contains(block_hash) { + err!("Block {:?} is not found in DBCol::BlockPerHeight", block); + } + Ok(()) +} + +pub(crate) fn block_header_exists( + sv: &mut StoreValidator, + block_hash: &CryptoHash, + _block: &Block, +) -> Result<(), StoreValidatorError> { + unwrap_or_err_db!( + sv.store.get_ser::(DBCol::BlockHeader, block_hash.as_ref()), + "Can't get Block Header from storage" + ); + Ok(()) +} + +pub(crate) fn chunk_hash_validity( + _sv: &mut StoreValidator, + chunk_hash: &ChunkHash, + shard_chunk: &ShardChunk, +) -> Result<(), StoreValidatorError> { + check_discrepancy!( + shard_chunk.chunk_hash(), + *chunk_hash, + "Invalid ShardChunk {:?} stored", + shard_chunk + ); + Ok(()) +} + +pub(crate) fn chunk_tail_validity( + sv: &mut StoreValidator, + _chunk_hash: &ChunkHash, + shard_chunk: &ShardChunk, +) -> Result<(), StoreValidatorError> { + let chunk_tail = sv.inner.chunk_tail; + let height = shard_chunk.height_created(); + if height != sv.config.genesis_height && height < chunk_tail { + err!( + "Invalid ShardChunk stored, chunk_tail = {:?}, ShardChunk = {:?}", + chunk_tail, + shard_chunk + ); + } + Ok(()) +} + +pub(crate) fn chunk_indexed_by_height_created( + sv: &mut StoreValidator, + _chunk_hash: &ChunkHash, + shard_chunk: &ShardChunk, +) -> Result<(), StoreValidatorError> { + let height = shard_chunk.height_created(); + let chunk_hashes = unwrap_or_err_db!( + sv.store.get_ser::>(DBCol::ChunkHashesByHeight, &index_to_bytes(height)), + "Can't get Chunks Set from storage on Height {:?}, no one is responsible for ShardChunk {:?}", + height, + shard_chunk + ); + if !chunk_hashes.contains(&shard_chunk.chunk_hash()) { + err!("Can't find ShardChunk {:?} on Height {:?}", shard_chunk, height); + } + Ok(()) +} + +pub(crate) fn header_hash_indexed_by_height( + sv: &mut StoreValidator, + _hash: &CryptoHash, + header: &BlockHeader, +) -> Result<(), StoreValidatorError> { + let height = header.height(); + let _hashes = match sv + .store + .get_ser::>(DBCol::HeaderHashesByHeight, &index_to_bytes(height)) + { + Ok(hashes) => hashes, + Err(e) => err!("Storage error, {:?}", e), + }; + // TODO #3488: enable + // This check is disabled because currently we can accept Headers that below chunk_tail. + // It creates a mess which records for DBCol::HeaderHashesByHeight exist. + // It will be resolved after #3488 is introduced by migration + // that is removing Block Headers forcibly from the DB. + + /*if height < sv.inner.chunk_tail { + // The data must be GCed + if hashes.is_some() { + err!( + "DBCol::HeaderHashesByHeight should be GCed, however for height {:?}, values {:?}", + height, + hashes + ) + } + } else { + if hashes.is_none() || !hashes.unwrap().contains(&header.hash()) { + err!("Can't find Header {:?} on Height {:?}", header, height); + } + }*/ + Ok(()) +} + +pub(crate) fn chunk_tx_exists( + sv: &mut StoreValidator, + _chunk_hash: &ChunkHash, + shard_chunk: &ShardChunk, +) -> Result<(), StoreValidatorError> { + for tx in shard_chunk.transactions().iter() { + let tx_hash = tx.get_hash(); + sv.inner.tx_refcount.entry(tx_hash).and_modify(|x| *x += 1).or_insert(1); + } + for receipt in shard_chunk.prev_outgoing_receipts().iter() { + sv.inner.receipt_refcount.entry(receipt.get_hash()).and_modify(|x| *x += 1).or_insert(1); + } + for tx in shard_chunk.transactions().iter() { + let tx_hash = tx.get_hash(); + unwrap_or_err_db!( + sv.store.get_ser::(DBCol::Transactions, tx_hash.as_ref()), + "Can't get Tx from storage for Tx Hash {:?}", + tx_hash + ); + } + Ok(()) +} + +pub(crate) fn block_chunks_exist( + sv: &mut StoreValidator, + _block_hash: &CryptoHash, + block: &Block, +) -> Result<(), StoreValidatorError> { + for chunk_header in block.chunks().iter() { + if chunk_header.height_included() == block.header().height() { + if let Some(me) = &sv.me { + let cares_about_shard = sv.shard_tracker.care_about_shard( + Some(me), + block.header().prev_hash(), + chunk_header.shard_id(), + true, + ); + let will_care_about_shard = sv.shard_tracker.will_care_about_shard( + Some(me), + block.header().prev_hash(), + chunk_header.shard_id(), + true, + ); + if cares_about_shard || will_care_about_shard { + unwrap_or_err_db!( + sv.store.get_ser::( + DBCol::Chunks, + chunk_header.chunk_hash().as_ref() + ), + "Can't get Chunk {:?} from storage", + chunk_header + ); + if cares_about_shard { + let shard_uid = sv + .epoch_manager + .shard_id_to_uid(chunk_header.shard_id(), block.header().epoch_id()) + .map_err(|err| StoreValidatorError::DBNotFound { + func_name: "get_shard_layout", + reason: err.to_string(), + })?; + let block_shard_uid = get_block_shard_uid(block.hash(), &shard_uid); + unwrap_or_err_db!( + sv.store + .get_ser::(DBCol::ChunkExtra, block_shard_uid.as_ref()), + "Can't get chunk extra for chunk {:?} from storage", + chunk_header + ); + } + } + } + } + } + Ok(()) +} + +pub(crate) fn block_chunks_height_validity( + _sv: &mut StoreValidator, + _block_hash: &CryptoHash, + block: &Block, +) -> Result<(), StoreValidatorError> { + for chunk_header in block.chunks().iter() { + if chunk_header.height_created() > block.header().height() { + err!( + "Invalid ShardChunk included, chunk_header = {:?}, block = {:?}", + chunk_header, + block + ); + } + } + Ok(()) +} + +pub(crate) fn block_info_exists( + sv: &mut StoreValidator, + block_hash: &CryptoHash, + _block: &Block, +) -> Result<(), StoreValidatorError> { + unwrap_or_err_db!( + sv.store.get_ser::(DBCol::BlockInfo, block_hash.as_ref()), + "Can't get BlockInfo from storage" + ); + Ok(()) +} + +pub(crate) fn block_epoch_exists( + _sv: &mut StoreValidator, + _block_hash: &CryptoHash, + _block: &Block, +) -> Result<(), StoreValidatorError> { + // TODO #2893: why? + /* + unwrap_or_err_db!( + sv.store.get_ser::(DBCol::EpochInfo, block.header().epoch_id().as_ref()), + "Can't get EpochInfo from storage" + ); + */ + Ok(()) +} + +pub(crate) fn block_increment_refcount( + sv: &mut StoreValidator, + _block_hash: &CryptoHash, + block: &Block, +) -> Result<(), StoreValidatorError> { + if block.header().height() != sv.config.genesis_height { + let prev_hash = block.header().prev_hash(); + sv.inner.block_refcount.entry(*prev_hash).and_modify(|x| *x += 1).or_insert(1); + } + Ok(()) +} + +pub(crate) fn canonical_header_validity( + sv: &mut StoreValidator, + height: &BlockHeight, + hash: &CryptoHash, +) -> Result<(), StoreValidatorError> { + let header = unwrap_or_err_db!( + sv.store.get_ser::(DBCol::BlockHeader, hash.as_ref()), + "Can't get Block Header {:?} from DBCol::BlockHeader", + hash + ); + if header.height() != *height { + err!("Block on Height {:?} doesn't have required Height, {:?}", height, header); + } + Ok(()) +} + +pub(crate) fn canonical_prev_block_validity( + sv: &mut StoreValidator, + height: &BlockHeight, + hash: &CryptoHash, +) -> Result<(), StoreValidatorError> { + if *height != sv.config.genesis_height { + let header = unwrap_or_err_db!( + sv.store.get_ser::(DBCol::BlockHeader, hash.as_ref()), + "Can't get Block Header {:?} from DBCol::BlockHeader", + hash + ); + let prev_hash = *header.prev_hash(); + let prev_header = unwrap_or_err_db!( + sv.store.get_ser::(DBCol::BlockHeader, prev_hash.as_ref()), + "Can't get prev Block Header {:?} from DBCol::BlockHeader", + prev_hash + ); + let prev_height = prev_header.height(); + let same_prev_hash = unwrap_or_err_db!( + sv.store.get_ser::(DBCol::BlockHeight, &index_to_bytes(prev_height)), + "Can't get prev Block Hash from DBCol::BlockHeight by Height, {:?}, {:?}", + prev_height, + prev_header + ); + check_discrepancy!( + prev_hash, + same_prev_hash, + "Prev Block Hashes in DBCol::BlockHeight and DBCol::BlockHeader at height {:?} are different", + prev_height + ); + + for cur_height in prev_height + 1..*height { + let cur_hash = unwrap_or_err!( + sv.store.get_ser::(DBCol::BlockHeight, &index_to_bytes(cur_height)), + "DB error while getting Block Hash from DBCol::BlockHeight by Height {:?}", + cur_height + ); + if cur_hash.is_some() { + err!("Unexpected Block on the Canonical Chain is found between Heights {:?} and {:?}, {:?}", prev_height, height, cur_hash); + } + } + } + Ok(()) +} + +pub(crate) fn trie_changes_chunk_extra_exists( + sv: &mut StoreValidator, + (block_hash, shard_uid): &(CryptoHash, ShardUId), + trie_changes: &TrieChanges, +) -> Result<(), StoreValidatorError> { + let new_root = trie_changes.new_root; + // 1. Block with `block_hash` should be available + let block = unwrap_or_err_db!( + sv.store.get_ser::(DBCol::Block, block_hash.as_ref()), + "Can't get Block from DB" + ); + // 2) Chunk Extra with `block_hash` and `shard_uid` should be available and match with the new root + let chunk_extra = unwrap_or_err_db!( + sv.store + .get_ser::(DBCol::ChunkExtra, &get_block_shard_uid(block_hash, shard_uid)), + "Can't get Chunk Extra from storage with key {:?} {:?}", + block_hash, + shard_uid + ); + check_discrepancy!(chunk_extra.state_root(), &new_root, "State Root discrepancy"); + // 3) Chunk Extra with `prev_block_hash` and `shard_uid` should match with the old root if available + if let Ok(Some(prev_chunk_extra)) = sv.store.get_ser::( + DBCol::ChunkExtra, + &get_block_shard_uid(block.header().prev_hash(), shard_uid), + ) { + check_discrepancy!( + prev_chunk_extra.state_root(), + &trie_changes.old_root, + "Prev State Root discrepancy, previous ChunkExtra {:?}", + prev_chunk_extra + ); + } + // 4) Trie should exist for `shard_uid` and the root + let trie = sv.runtime.get_tries().get_trie_for_shard(*shard_uid, new_root); + let trie_iterator = unwrap_or_err!( + trie.iter(), + "Trie Node Missing for shard {:?} root {:?}", + shard_uid, + new_root + ); + for item in trie_iterator { + unwrap_or_err!(item, "Error iterating Trie {:?} {:?}", shard_uid, new_root); + } + + // If the trie_changes we are checking are for the next epoch during sharding upgrade, + // skip the checks about ShardChunk because there is no corresponding chunk for this shard_uid + let shard_layout = unwrap_or_err!( + sv.epoch_manager.get_shard_layout(block.header().epoch_id()), + "Error getting shard layout" + ); + if shard_layout.version() != shard_uid.version { + return Ok(()); + } + + // 5. There should be ShardChunk with ShardId `shard_id` + let shard_id = shard_uid.shard_id(); + let chunks = block.chunks(); + if let Some(chunk_header) = chunks.get(shard_id as usize) { + // if the chunk is not a new chunk, skip the check + if chunk_header.height_included() != block.header().height() { + return Ok(()); + } + let chunk_hash = chunk_header.chunk_hash(); + // 6. ShardChunk with `chunk_hash` should be available + unwrap_or_err_db!( + sv.store.get_ser::(DBCol::Chunks, chunk_hash.as_ref()), + "Can't get Chunk from storage with ChunkHash {:?}", + chunk_hash + ); + + // 7. Prev State Roots should be equal + if chunk_header.height_included() == block.header().height() { + check_discrepancy!( + chunk_header.prev_state_root(), + trie_changes.old_root, + "Prev State Root discrepancy, ShardChunk {:?}", + chunk_header + ); + } + + Ok(()) + } else { + err!("Block {:?} does not have chunk {}", block, shard_id) + } +} + +pub(crate) fn chunk_of_height_exists( + sv: &mut StoreValidator, + height: &BlockHeight, + chunk_hashes: &HashSet, +) -> Result<(), StoreValidatorError> { + for chunk_hash in chunk_hashes { + let shard_chunk = unwrap_or_err_db!( + sv.store.get_ser::(DBCol::Chunks, chunk_hash.as_ref()), + "Can't get Chunk from storage with ChunkHash {:?}", + chunk_hash + ); + check_discrepancy!( + shard_chunk.height_created(), + *height, + "Invalid ShardChunk {:?} stored", + shard_chunk + ); + } + Ok(()) +} + +pub(crate) fn header_hash_of_height_exists( + sv: &mut StoreValidator, + height: &BlockHeight, + header_hashes: &HashSet, +) -> Result<(), StoreValidatorError> { + for hash in header_hashes { + let header = unwrap_or_err_db!( + sv.store.get_ser::(DBCol::BlockHeader, hash.as_ref()), + "Can't get Header from storage with Hash {:?}", + hash + ); + check_discrepancy!(header.height(), *height, "Invalid Header {:?} stored", header); + } + Ok(()) +} + +pub(crate) fn outcome_by_outcome_id_exists( + sv: &mut StoreValidator, + block_hash: &CryptoHash, + outcome_ids: &[CryptoHash], +) -> Result<(), StoreValidatorError> { + for outcome_id in outcome_ids { + let _outcome = unwrap_or_err_db!( + sv.store.get_ser::( + DBCol::TransactionResultForBlock, + &get_outcome_id_block_hash(outcome_id, block_hash) + ), + "Can't get TransactionResultForBlock from storage with Outcome id {:?} block hash {:?}", + outcome_id, + block_hash + ); + } + Ok(()) +} + +pub(crate) fn outcome_id_block_exists( + sv: &mut StoreValidator, + block_hash: &CryptoHash, + _outcome_ids: &[CryptoHash], +) -> Result<(), StoreValidatorError> { + unwrap_or_err_db!( + sv.store.get_ser::(DBCol::Block, block_hash.as_ref()), + "Can't get Block from DB" + ); + Ok(()) +} + +pub(crate) fn outcome_indexed_by_block_hash( + sv: &mut StoreValidator, + (outcome_id, block_hash): &(CryptoHash, CryptoHash), + _outcome: &ExecutionOutcomeWithProof, +) -> Result<(), StoreValidatorError> { + let block = unwrap_or_err_db!( + sv.store.get_ser::(DBCol::Block, block_hash.as_ref()), + "Can't get Block {} from DB", + block_hash + ); + for chunk_header in block.chunks().iter() { + if chunk_header.height_included() == block.header().height() { + let shard_uid = sv + .epoch_manager + .shard_id_to_uid(chunk_header.shard_id(), block.header().epoch_id()) + .map_err(|err| StoreValidatorError::DBNotFound { + func_name: "get_shard_layout", + reason: err.to_string(), + })?; + if let Ok(Some(_)) = sv.store.get_ser::( + DBCol::ChunkExtra, + &get_block_shard_uid(block.hash(), &shard_uid), + ) { + let outcome_ids = unwrap_or_err_db!( + sv.store.get_ser::>( + DBCol::OutcomeIds, + &get_block_shard_id(block.hash(), chunk_header.shard_id()) + ), + "Can't get Outcome ids by Block Hash" + ); + if outcome_ids.contains(outcome_id) { + return Ok(()); + } + } + } + } + err!("Outcome id {:?} is not found in DBCol::OutcomeIds", outcome_id) +} + +pub(crate) fn state_sync_info_valid( + _sv: &mut StoreValidator, + block_hash: &CryptoHash, + state_sync_info: &StateSyncInfo, +) -> Result<(), StoreValidatorError> { + check_discrepancy!( + state_sync_info.epoch_tail_hash, + *block_hash, + "Invalid StateSyncInfo stored" + ); + Ok(()) +} + +pub(crate) fn state_sync_info_block_exists( + sv: &mut StoreValidator, + block_hash: &CryptoHash, + _state_sync_info: &StateSyncInfo, +) -> Result<(), StoreValidatorError> { + unwrap_or_err_db!( + sv.store.get_ser::(DBCol::Block, block_hash.as_ref()), + "Can't get Block from DB" + ); + Ok(()) +} + +pub(crate) fn chunk_extra_block_exists( + sv: &mut StoreValidator, + (block_hash, _shard_uid): &(CryptoHash, ShardUId), + _chunk_extra: &ChunkExtra, +) -> Result<(), StoreValidatorError> { + unwrap_or_err_db!( + sv.store.get_ser::(DBCol::Block, block_hash.as_ref()), + "Can't get Block from DB" + ); + Ok(()) +} + +pub(crate) fn block_info_block_header_exists( + sv: &mut StoreValidator, + block_hash: &CryptoHash, + _block_info: &BlockInfo, +) -> Result<(), StoreValidatorError> { + // fake block info for pre-genesis block + if *block_hash == CryptoHash::default() { + return Ok(()); + } + unwrap_or_err_db!( + sv.store.get_ser::(DBCol::BlockHeader, block_hash.as_ref()), + "Can't get Block Header from DB" + ); + Ok(()) +} + +pub(crate) fn epoch_validity( + sv: &mut StoreValidator, + epoch_id: &EpochId, + _epoch_info: &EpochInfo, +) -> Result<(), StoreValidatorError> { + check_discrepancy!(sv.epoch_manager.epoch_exists(epoch_id), true, "Invalid EpochInfo stored"); + Ok(()) +} + +pub(crate) fn tx_refcount( + sv: &mut StoreValidator, + tx_hash: &CryptoHash, + refcount: &u64, +) -> Result<(), StoreValidatorError> { + let expected = sv.inner.tx_refcount.get(tx_hash).map(|&rc| rc).unwrap_or_default(); + if *refcount != expected { + err!("Invalid tx refcount, expected {:?}, found {:?}", expected, refcount) + } else { + sv.inner.tx_refcount.remove(tx_hash); + return Ok(()); + } +} + +pub(crate) fn receipt_refcount( + sv: &mut StoreValidator, + receipt_id: &CryptoHash, + refcount: &u64, +) -> Result<(), StoreValidatorError> { + let expected = sv.inner.receipt_refcount.get(receipt_id).map(|&rc| rc).unwrap_or_default(); + if *refcount != expected { + err!("Invalid receipt refcount, expected {:?}, found {:?}", expected, refcount) + } else { + sv.inner.receipt_refcount.remove(receipt_id); + return Ok(()); + } +} + +pub(crate) fn block_refcount( + sv: &mut StoreValidator, + block_hash: &CryptoHash, + refcount: &u64, +) -> Result<(), StoreValidatorError> { + if let Some(found) = sv.inner.block_refcount.get(block_hash) { + if refcount != found { + err!("Invalid Block Refcount, expected {:?}, found {:?}", refcount, found) + } else { + sv.inner.block_refcount.remove(block_hash); + return Ok(()); + } + } + let header = unwrap_or_err_db!( + sv.store.get_ser::(DBCol::BlockHeader, block_hash.as_ref()), + "Can't get Block Header from DB" + ); + check_discrepancy!( + header.height(), + sv.config.genesis_height, + "Unexpected Block Refcount found" + ); + // This is Genesis Block + check_discrepancy!(*refcount, 1, "Invalid Genesis Block Refcount {:?}", refcount); + sv.inner.genesis_blocks.push(*block_hash); + Ok(()) +} + +pub(crate) fn state_header_block_exists( + sv: &mut StoreValidator, + key: &StateHeaderKey, + _header: &ShardStateSyncResponseHeader, +) -> Result<(), StoreValidatorError> { + unwrap_or_err_db!( + sv.store.get_ser::(DBCol::Block, key.1.as_ref()), + "Can't get Block from DB" + ); + Ok(()) +} + +pub(crate) fn state_part_header_exists( + sv: &mut StoreValidator, + key: &StatePartKey, + _part: &[u8], +) -> Result<(), StoreValidatorError> { + let StatePartKey(block_hash, shard_id, part_id) = *key; + let state_header_key = unwrap_or_err!( + borsh::to_vec(&StateHeaderKey(shard_id, block_hash)), + "Can't serialize StateHeaderKey" + ); + let header = unwrap_or_err_db!( + sv.store.get_ser::(DBCol::StateHeaders, &state_header_key), + "Can't get StateHeaderKey from DB" + ); + let num_parts = header.num_state_parts(); + if part_id >= num_parts { + err!("Invalid part_id {:?}, num_parts {:?}", part_id, num_parts) + } + Ok(()) +} + +// Final checks + +pub(crate) fn block_height_cmp_tail_final(sv: &StoreValidator) -> Result<(), StoreValidatorError> { + let len = sv.inner.block_heights_less_tail.len(); + if len >= 2 { + let blocks = &sv.inner.block_heights_less_tail; + err!("Found {:?} Blocks with height lower than Tail, {:?}", len, blocks) + } + Ok(()) +} + +pub(crate) fn tx_refcount_final(sv: &StoreValidator) -> Result<(), StoreValidatorError> { + if let Some(tx_refcount) = sv.inner.tx_refcount.iter().next() { + err!( + "Found {:?} Txs that are not counted, e.g. {:?}", + sv.inner.tx_refcount.len(), + tx_refcount + ); + } + Ok(()) +} + +pub(crate) fn receipt_refcount_final(sv: &StoreValidator) -> Result<(), StoreValidatorError> { + if let Some(receipt_refcount) = sv.inner.receipt_refcount.iter().next() { + err!( + "Found {:?} receipts that are not counted, e.g. {:?}", + sv.inner.receipt_refcount.len(), + receipt_refcount + ); + } + Ok(()) +} + +pub(crate) fn block_refcount_final(sv: &StoreValidator) -> Result<(), StoreValidatorError> { + let block_refcount_len = sv.inner.block_refcount.len(); + if block_refcount_len >= 2 { + err!( + "Found {:?} Blocks that are not counted, e.g. {:?}", + block_refcount_len, + sv.inner.block_refcount.iter().next() + ); + } + let genesis_blocks_len = sv.inner.genesis_blocks.len(); + if genesis_blocks_len >= 2 { + err!( + "Found {:?} Genesis Blocks, e.g. {:?}", + genesis_blocks_len, + sv.inner.genesis_blocks.first() + ); + } + Ok(()) +} diff --git a/chain/chain/src/test_utils.rs b/chain/chain/src/test_utils.rs new file mode 100644 index 000000000..7f82aa231 --- /dev/null +++ b/chain/chain/src/test_utils.rs @@ -0,0 +1,410 @@ +mod kv_runtime; +mod validator_schedule; + +use chrono::{DateTime, Utc}; +use num_rational::Ratio; +use std::cmp::Ordering; +use std::sync::Arc; + +pub use self::kv_runtime::account_id_to_shard_id; +pub use self::kv_runtime::KeyValueRuntime; +pub use self::kv_runtime::MockEpochManager; +pub use self::validator_schedule::ValidatorSchedule; +use crate::block_processing_utils::BlockNotInPoolError; +use crate::chain::Chain; +use crate::store::ChainStoreAccess; +use crate::types::{AcceptedBlock, ChainConfig, ChainGenesis}; +use crate::DoomslugThresholdMode; +use crate::{BlockProcessingArtifact, Provenance}; +use unc_chain_primitives::Error; +use unc_epoch_manager::shard_tracker::ShardTracker; +use unc_primitives::block::Block; +use unc_primitives::hash::CryptoHash; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::test_utils::create_test_signer; +use unc_primitives::types::{AccountId, NumBlocks, NumShards}; +use unc_primitives::utils::MaybeValidated; +use unc_primitives::validator_signer::InMemoryValidatorSigner; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_store::test_utils::create_test_store; +use unc_store::DBCol; +use tracing::debug; + +pub fn get_chain() -> Chain { + get_chain_with_epoch_length_and_num_shards(10, 1) +} + +pub fn get_chain_with_num_shards(num_shards: NumShards) -> Chain { + get_chain_with_epoch_length_and_num_shards(10, num_shards) +} + +pub fn get_chain_with_epoch_length(epoch_length: NumBlocks) -> Chain { + get_chain_with_epoch_length_and_num_shards(epoch_length, 1) +} + +pub fn get_chain_with_epoch_length_and_num_shards( + epoch_length: NumBlocks, + num_shards: NumShards, +) -> Chain { + let store = create_test_store(); + let chain_genesis = ChainGenesis::test(); + let vs = ValidatorSchedule::new() + .block_producers_per_epoch(vec![vec!["test1".parse().unwrap()]]) + .num_shards(num_shards); + let epoch_manager = MockEpochManager::new_with_validators(store.clone(), vs, epoch_length); + let shard_tracker = ShardTracker::new_empty(epoch_manager.clone()); + let runtime = KeyValueRuntime::new(store, epoch_manager.as_ref()); + Chain::new( + epoch_manager, + shard_tracker, + runtime, + &chain_genesis, + DoomslugThresholdMode::NoApprovals, + ChainConfig::test(), + None, + ) + .unwrap() +} + +/// Wait for all blocks that started processing to be ready for postprocessing +/// Returns true if there are new blocks that are ready +pub fn wait_for_all_blocks_in_processing(chain: &Chain) -> bool { + chain.blocks_in_processing.wait_for_all_blocks() +} + +pub fn is_block_in_processing(chain: &Chain, block_hash: &CryptoHash) -> bool { + chain.blocks_in_processing.contains(block_hash) +} + +pub fn wait_for_block_in_processing( + chain: &Chain, + hash: &CryptoHash, +) -> Result<(), BlockNotInPoolError> { + chain.blocks_in_processing.wait_for_block(hash) +} + +/// Unlike Chain::start_process_block_async, this function blocks until the processing of this block +/// finishes +pub fn process_block_sync( + chain: &mut Chain, + me: &Option, + block: MaybeValidated, + provenance: Provenance, + block_processing_artifacts: &mut BlockProcessingArtifact, +) -> Result, Error> { + let block_hash = *block.hash(); + chain.start_process_block_async( + me, + block, + provenance, + block_processing_artifacts, + Arc::new(|_| {}), + )?; + wait_for_block_in_processing(chain, &block_hash).unwrap(); + let (accepted_blocks, errors) = + chain.postprocess_ready_blocks(me, block_processing_artifacts, Arc::new(|_| {})); + // This is in test, we should never get errors when postprocessing blocks + debug_assert!(errors.is_empty()); + Ok(accepted_blocks) +} + +// TODO(#8190) Improve this testing API. +pub fn setup() -> (Chain, Arc, Arc, Arc) +{ + setup_with_tx_validity_period(100) +} + +fn setup_with_tx_validity_period( + tx_validity_period: NumBlocks, +) -> (Chain, Arc, Arc, Arc) { + let store = create_test_store(); + let epoch_length = 1000; + let epoch_manager = MockEpochManager::new(store.clone(), epoch_length); + let shard_tracker = ShardTracker::new_empty(epoch_manager.clone()); + let runtime = KeyValueRuntime::new(store, epoch_manager.as_ref()); + let chain = Chain::new( + epoch_manager.clone(), + shard_tracker, + runtime.clone(), + &ChainGenesis { + time: StaticClock::utc(), + height: 0, + gas_limit: 1_000_000, + min_gas_price: 100, + max_gas_price: 1_000_000_000, + total_supply: 1_000_000_000, + gas_price_adjustment_rate: Ratio::from_integer(0), + transaction_validity_period: tx_validity_period, + epoch_length, + protocol_version: PROTOCOL_VERSION, + }, + DoomslugThresholdMode::NoApprovals, + ChainConfig::test(), + None, + ) + .unwrap(); + + let signer = Arc::new(create_test_signer("test")); + (chain, epoch_manager, runtime, signer) +} + +pub fn setup_with_validators( + vs: ValidatorSchedule, + epoch_length: u64, + tx_validity_period: NumBlocks, +) -> (Chain, Arc, Arc, Vec>) { + let store = create_test_store(); + let signers = + vs.all_block_producers().map(|x| Arc::new(create_test_signer(x.as_str()))).collect(); + let epoch_manager = MockEpochManager::new_with_validators(store.clone(), vs, epoch_length); + let shard_tracker = ShardTracker::new_empty(epoch_manager.clone()); + let runtime = KeyValueRuntime::new(store, epoch_manager.as_ref()); + let chain = Chain::new( + epoch_manager.clone(), + shard_tracker, + runtime.clone(), + &ChainGenesis { + time: StaticClock::utc(), + height: 0, + gas_limit: 1_000_000, + min_gas_price: 100, + max_gas_price: 1_000_000_000, + total_supply: 1_000_000_000, + gas_price_adjustment_rate: Ratio::from_integer(0), + transaction_validity_period: tx_validity_period, + epoch_length, + protocol_version: PROTOCOL_VERSION, + }, + DoomslugThresholdMode::NoApprovals, + ChainConfig::test(), + None, + ) + .unwrap(); + (chain, epoch_manager, runtime, signers) +} + +pub fn setup_with_validators_and_start_time( + vs: ValidatorSchedule, + epoch_length: u64, + tx_validity_period: NumBlocks, + start_time: DateTime, +) -> (Chain, Arc, Arc, Vec>) { + let store = create_test_store(); + let signers = + vs.all_block_producers().map(|x| Arc::new(create_test_signer(x.as_str()))).collect(); + let epoch_manager = MockEpochManager::new_with_validators(store.clone(), vs, epoch_length); + let shard_tracker = ShardTracker::new_empty(epoch_manager.clone()); + let runtime = KeyValueRuntime::new(store, epoch_manager.as_ref()); + let chain = Chain::new( + epoch_manager.clone(), + shard_tracker, + runtime.clone(), + &ChainGenesis { + time: start_time, + height: 0, + gas_limit: 1_000_000, + min_gas_price: 100, + max_gas_price: 1_000_000_000, + total_supply: 1_000_000_000, + gas_price_adjustment_rate: Ratio::from_integer(0), + transaction_validity_period: tx_validity_period, + epoch_length, + protocol_version: PROTOCOL_VERSION, + }, + DoomslugThresholdMode::NoApprovals, + ChainConfig::test(), + None, + ) + .unwrap(); + (chain, epoch_manager, runtime, signers) +} + +pub fn format_hash(hash: CryptoHash) -> String { + let mut hash = hash.to_string(); + hash.truncate(6); + hash +} + +/// Displays chain from given store. +pub fn display_chain(me: &Option, chain: &mut Chain, tail: bool) { + let epoch_manager = chain.epoch_manager.clone(); + let chain_store = chain.mut_chain_store(); + let head = chain_store.head().unwrap(); + debug!( + "{:?} Chain head ({}): {} / {}", + me, + if tail { "tail" } else { "full" }, + head.height, + head.last_block_hash + ); + let mut headers = vec![]; + for (key, _) in chain_store.store().clone().iter(DBCol::BlockHeader).map(Result::unwrap) { + let header = chain_store + .get_block_header(&CryptoHash::try_from(key.as_ref()).unwrap()) + .unwrap() + .clone(); + if !tail || header.height() + 10 > head.height { + headers.push(header); + } + } + headers.sort_by(|h_left, h_right| { + if h_left.height() > h_right.height() { + Ordering::Greater + } else { + Ordering::Less + } + }); + for header in headers { + if header.prev_hash() == &CryptoHash::default() { + // Genesis block. + debug!("{: >3} {}", header.height(), format_hash(*header.hash())); + } else { + let parent_header = chain_store.get_block_header(header.prev_hash()).unwrap().clone(); + let maybe_block = chain_store.get_block(header.hash()).ok(); + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(header.prev_hash()).unwrap(); + let block_producer = + epoch_manager.get_block_producer(&epoch_id, header.height()).unwrap(); + debug!( + "{: >3} {} | {: >10} | parent: {: >3} {} | {}", + header.height(), + format_hash(*header.hash()), + block_producer, + parent_header.height(), + format_hash(*parent_header.hash()), + if let Some(block) = &maybe_block { + format!("chunks: {}", block.chunks().len()) + } else { + "-".to_string() + } + ); + if let Some(block) = maybe_block { + for chunk_header in block.chunks().iter() { + let chunk_producer = epoch_manager + .get_chunk_producer( + &epoch_id, + chunk_header.height_created(), + chunk_header.shard_id(), + ) + .unwrap(); + if let Ok(chunk) = chain_store.get_chunk(&chunk_header.chunk_hash()) { + debug!( + " {: >3} {} | {} | {: >10} | tx = {: >2}, receipts = {: >2}", + chunk_header.height_created(), + format_hash(chunk_header.chunk_hash().0), + chunk_header.shard_id(), + chunk_producer, + chunk.transactions().len(), + chunk.prev_outgoing_receipts().len() + ); + } else if let Ok(partial_chunk) = + chain_store.get_partial_chunk(&chunk_header.chunk_hash()) + { + debug!( + " {: >3} {} | {} | {: >10} | parts = {:?} receipts = {:?}", + chunk_header.height_created(), + format_hash(chunk_header.chunk_hash().0), + chunk_header.shard_id(), + chunk_producer, + partial_chunk.parts().iter().map(|x| x.part_ord).collect::>(), + partial_chunk + .receipts() + .iter() + .map(|x| format!("{} => {}", x.0.len(), x.1.to_shard_id)) + .collect::>(), + ); + } + } + } + } + } +} + +impl ChainGenesis { + pub fn test() -> Self { + ChainGenesis { + time: StaticClock::utc(), + height: 0, + gas_limit: 10u64.pow(15), + min_gas_price: 0, + max_gas_price: 1_000_000_000, + total_supply: 1_000_000_000, + gas_price_adjustment_rate: Ratio::from_integer(0), + transaction_validity_period: 100, + epoch_length: 5, + protocol_version: PROTOCOL_VERSION, + } + } +} + +#[cfg(test)] +mod test { + use std::convert::TryFrom; + + use rand::Rng; + + use unc_primitives::hash::CryptoHash; + use unc_primitives::receipt::Receipt; + use unc_primitives::sharding::ReceiptList; + use unc_primitives::static_clock::StaticClock; + use unc_primitives::types::{AccountId, NumShards}; + + use crate::Chain; + + use unc_primitives::shard_layout::{account_id_to_shard_id, ShardLayout}; + + fn naive_build_receipt_hashes( + receipts: &[Receipt], + shard_layout: &ShardLayout, + ) -> Vec { + let mut receipts_hashes = vec![]; + for shard_id in shard_layout.shard_ids() { + let shard_receipts: Vec = receipts + .iter() + .filter(|&receipt| { + account_id_to_shard_id(&receipt.receiver_id, shard_layout) == shard_id + }) + .cloned() + .collect(); + receipts_hashes.push(CryptoHash::hash_borsh(ReceiptList(shard_id, &shard_receipts))); + } + receipts_hashes + } + + fn test_build_receipt_hashes_with_num_shard(num_shards: NumShards) { + let shard_layout = ShardLayout::v0(num_shards, 0); + let create_receipt_from_receiver_id = + |receiver_id| Receipt::new_balance_refund(&receiver_id, 0); + let mut rng = rand::thread_rng(); + let receipts = (0..3000) + .map(|_| { + let random_number = rng.gen_range(0..1000); + create_receipt_from_receiver_id( + AccountId::try_from(format!("test{}", random_number)).unwrap(), + ) + }) + .collect::>(); + let start = StaticClock::instant(); + let naive_result = naive_build_receipt_hashes(&receipts, &shard_layout); + let naive_duration = start.elapsed(); + let start = StaticClock::instant(); + let prod_result = Chain::build_receipts_hashes(&receipts, &shard_layout); + let prod_duration = start.elapsed(); + assert_eq!(naive_result, prod_result); + // production implementation is at least 50% faster + assert!( + 2 * naive_duration > 3 * prod_duration, + "naive duration vs production {:?} {:?}", + naive_duration, + prod_duration + ); + } + + #[test] + #[ignore] + /// Disabled, see more details in #5836 + fn test_build_receipt_hashes() { + for num_shards in 1..10 { + test_build_receipt_hashes_with_num_shard(num_shards); + } + } +} diff --git a/chain/chain/src/test_utils/kv_runtime.rs b/chain/chain/src/test_utils/kv_runtime.rs new file mode 100644 index 000000000..90d8d41ee --- /dev/null +++ b/chain/chain/src/test_utils/kv_runtime.rs @@ -0,0 +1,1398 @@ +use super::ValidatorSchedule; +use crate::types::{ + ApplyChunkBlockContext, ApplyChunkResult, ApplyChunkShardContext, ApplyResultForResharding, + RuntimeAdapter, RuntimeStorageConfig, +}; +use crate::BlockHeader; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_chain_configs::{ProtocolConfig, DEFAULT_GC_NUM_EPOCHS_TO_KEEP}; +use unc_chain_primitives::Error; +use unc_crypto::{KeyType, PublicKey, SecretKey, Signature}; +use unc_epoch_manager::types::BlockHeaderInfo; +use unc_epoch_manager::{EpochManagerAdapter, RngSeed}; +use unc_pool::types::PoolIterator; +use unc_primitives::account::{AccessKey, Account}; +use unc_primitives::block_header::{Approval, ApprovalInner}; +use unc_primitives::epoch_manager::block_info::BlockInfo; +use unc_primitives::epoch_manager::epoch_info::EpochInfo; +use unc_primitives::epoch_manager::EpochConfig; +use unc_primitives::epoch_manager::ShardConfig; +use unc_primitives::epoch_manager::ValidatorSelectionConfig; +use unc_primitives::errors::{EpochError, InvalidTxError}; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum}; +use unc_primitives::shard_layout; +use unc_primitives::shard_layout::{ShardLayout, ShardUId}; +use unc_primitives::sharding::ChunkHash; +use unc_primitives::state_part::PartId; +use unc_primitives::transaction::{ + Action, ExecutionMetadata, ExecutionOutcome, ExecutionOutcomeWithId, ExecutionStatus, + SignedTransaction, TransferAction, +}; +use unc_primitives::types::{AccountId, ApprovalFrozen, Balance, BlockHeight, EpochHeight, EpochId, Gas, Nonce, NumShards, ShardId, StateChangesForResharding, StateRoot, StateRootNode, ValidatorInfoIdentifier}; +use unc_primitives::validator_mandates::AssignmentWeight; +use unc_primitives::version::{ProtocolVersion, PROTOCOL_VERSION}; +use unc_primitives::views::{ + AccessKeyInfoView, AccessKeyList, CallResult, ContractCodeView, EpochValidatorInfo, + QueryRequest, QueryResponse, QueryResponseKind, ViewStateResult, +}; +use unc_store::test_utils::TestTriesBuilder; +use unc_store::{ + set_genesis_hash, set_genesis_state_roots, DBCol, ShardTries, StorageError, Store, StoreUpdate, + Trie, TrieChanges, WrappedTrieChanges, +}; +use num_rational::Ratio; +use std::cmp::Ordering; +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::sync::{Arc, RwLock}; +use std::time::Duration; +use unc_primitives::types::validator_power_and_frozen::ValidatorPowerAndFrozen; + +/// Simple key value runtime for tests. +/// +/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +/// WARNING: If you choose to use KeyValueRuntime for your tests, BE PREPARED TO +/// HAVE A BAD TIME. Use it only if you understand it to its entirety. It has +/// implicit behavior, very specific partially implemented logic, and is generally +/// incorrect. USE NightshadeRuntime WHENEVER POSSIBLE. YOU HAVE BEEN WARNED. +/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +/// +/// Major differences with production `NightshadeRuntime`: +/// * Uses in-memory storage +/// * Doesn't have WASM runtime, so can only process simple transfer +/// transaction +/// * Uses hard-coded validator schedule instead of using `EpochManager` and +/// staking to assign block and chunk producers. +pub struct KeyValueRuntime { + store: Store, + tries: ShardTries, + num_shards: NumShards, + epoch_length: u64, + no_gc: bool, + + // A mapping state_root => {account id => amounts}, for transactions and receipts + state: RwLock>, + state_size: RwLock>, + headers_cache: RwLock>, +} + +/// DEPRECATED. DO NOT USE for new tests. Use the real EpochManager, familiarize +/// yourself with how block producers, chunk producers, epoch transitions, etc. +/// work, and write your test to be compatible with what's in production. +/// MockEpochManager is simpler, but it deviates considerably from the production +/// validator selection and epoch management behavior. +pub struct MockEpochManager { + store: Store, + num_shards: NumShards, + epoch_length: u64, + /// A pre determined list of validator sets. We rotate validator set in this list. + /// Epoch i uses validators from `validators_by_valset[i % validators_by_valset.len()]`. + validators_by_valset: Vec, + /// Maps from account id to validator stake for all validators, both block producers and + /// chunk producers + validators: HashMap, + + headers_cache: RwLock>, + hash_to_epoch: RwLock>, + hash_to_next_epoch_approvals_req: RwLock>, + hash_to_next_epoch: RwLock>, + /// Maps EpochId to index of `validators_by_valset` to determine validators for an epoch + hash_to_valset: RwLock>, + epoch_start: RwLock>, +} + +/// Stores the validator information in an epoch. +/// Block producers are specified by `block_producers` +/// Chunk producers have two types, validators who are also block producers and chunk only producers. +/// Block producers are assigned to shards via `validator_groups`. +/// Each shard will have `block_producers.len() / validator_groups` of validators who are also block +/// producers +struct EpochValidatorSet { + block_producers: Vec, + /// index of this list is shard_id + chunk_producers: Vec>, +} + +#[derive(BorshSerialize, BorshDeserialize, Hash, PartialEq, Eq, Ord, PartialOrd, Clone, Debug)] +struct AccountNonce(AccountId, Nonce); + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug)] +struct KVState { + amounts: HashMap, + receipt_nonces: HashSet, + tx_nonces: HashSet, +} + +impl MockEpochManager { + pub fn new(store: Store, epoch_length: u64) -> Arc { + let vs = + ValidatorSchedule::new().block_producers_per_epoch(vec![vec!["test".parse().unwrap()]]); + Self::new_with_validators(store, vs, epoch_length) + } + + pub fn new_with_validators( + store: Store, + vs: ValidatorSchedule, + epoch_length: u64, + ) -> Arc { + let map_with_default_hash1 = HashMap::from([(CryptoHash::default(), EpochId::default())]); + let map_with_default_hash2 = HashMap::from([(CryptoHash::default(), 0)]); + let map_with_default_hash3 = HashMap::from([(EpochId::default(), 0)]); + + let mut validators = HashMap::new(); + let mut validators_by_valset: Vec = vs + .block_producers + .iter() + .map(|account_ids| { + let block_producers: Vec = account_ids + .iter() + .map(|account_id| { + let stake = ValidatorPowerAndFrozen::new( + account_id.clone(), + SecretKey::from_seed(KeyType::ED25519, account_id.as_ref()) + .public_key(), + 1_000_000, + 1_000_000, + ); + validators.insert(account_id.clone(), stake.clone()); + stake + }) + .collect(); + + let validators_per_shard = block_producers.len() as ShardId / vs.validator_groups; + let coef = block_producers.len() as ShardId / vs.num_shards; + + let chunk_producers: Vec> = (0..vs.num_shards) + .map(|shard_id| { + let offset = (shard_id * coef / validators_per_shard * validators_per_shard) + as usize; + block_producers[offset..offset + validators_per_shard as usize].to_vec() + }) + .collect(); + + EpochValidatorSet { block_producers, chunk_producers } + }) + .collect(); + + if !vs.chunk_only_producers.is_empty() { + assert_eq!(validators_by_valset.len(), vs.chunk_only_producers.len()); + for (epoch_idx, epoch_cops) in vs.chunk_only_producers.into_iter().enumerate() { + assert_eq!(epoch_cops.len() as u64, vs.num_shards); + for (shard_idx, shard_cops) in epoch_cops.into_iter().enumerate() { + for account_id in shard_cops { + let stake = ValidatorPowerAndFrozen::new( + account_id.clone(), + SecretKey::from_seed(KeyType::ED25519, account_id.as_ref()) + .public_key(), + 1_000_000, + 1_000_000, + ); + let prev = validators.insert(account_id, stake.clone()); + assert!(prev.is_none(), "chunk only produced is also a block producer"); + validators_by_valset[epoch_idx].chunk_producers[shard_idx].push(stake) + } + } + } + } + + Arc::new(MockEpochManager { + store, + num_shards: vs.num_shards, + epoch_length, + validators, + validators_by_valset, + headers_cache: RwLock::new(HashMap::new()), + hash_to_epoch: RwLock::new(HashMap::new()), + hash_to_next_epoch_approvals_req: RwLock::new(HashMap::new()), + hash_to_next_epoch: RwLock::new(map_with_default_hash1), + hash_to_valset: RwLock::new(map_with_default_hash3), + epoch_start: RwLock::new(map_with_default_hash2), + }) + } + + /// Get epoch and index of validator set by the hash of previous block. + /// Note that it also fills in-memory chain info and there is some + /// assumption that it is called for all previous blocks. + /// TODO (#8269): should we call it recursively for previous blocks if info is not found? + fn get_epoch_and_valset( + &self, + prev_hash: CryptoHash, + ) -> Result<(EpochId, usize, EpochId), EpochError> { + if prev_hash == CryptoHash::default() { + return Ok((EpochId(prev_hash), 0, EpochId(prev_hash))); + } + let prev_block_header = self + .get_block_header(&prev_hash)? + .ok_or_else(|| EpochError::MissingBlock(prev_hash))?; + + let mut hash_to_epoch = self.hash_to_epoch.write().unwrap(); + let mut hash_to_next_epoch_approvals_req = + self.hash_to_next_epoch_approvals_req.write().unwrap(); + let mut hash_to_next_epoch = self.hash_to_next_epoch.write().unwrap(); + let mut hash_to_valset = self.hash_to_valset.write().unwrap(); + let mut epoch_start_map = self.epoch_start.write().unwrap(); + + let prev_prev_hash = *prev_block_header.prev_hash(); + let prev_epoch = hash_to_epoch.get(&prev_prev_hash); + let prev_next_epoch = hash_to_next_epoch.get(&prev_prev_hash).unwrap(); + let prev_valset = match prev_epoch { + Some(prev_epoch) => Some(*hash_to_valset.get(prev_epoch).unwrap()), + None => None, + }; + + let prev_epoch_start = *epoch_start_map.get(&prev_prev_hash).unwrap(); + + let last_final_height = if prev_block_header.last_final_block() == &CryptoHash::default() { + 0 + } else { + self.get_block_header(prev_block_header.last_final_block()).unwrap().unwrap().height() + }; + + let increment_epoch = prev_prev_hash == CryptoHash::default() // genesis is in its own epoch + || last_final_height + 3 >= prev_epoch_start + self.epoch_length; + + let needs_next_epoch_approvals = !increment_epoch + && last_final_height + 3 < prev_epoch_start + self.epoch_length + && prev_block_header.height() + 3 >= prev_epoch_start + self.epoch_length; + + let (epoch, next_epoch, valset, epoch_start) = if increment_epoch { + let new_valset = match prev_valset { + None => 0, + Some(prev_valset) => prev_valset + 1, + }; + ( + prev_next_epoch.clone(), + EpochId(prev_hash), + new_valset, + prev_block_header.height() + 1, + ) + } else { + ( + prev_epoch.unwrap().clone(), + prev_next_epoch.clone(), + prev_valset.unwrap(), + prev_epoch_start, + ) + }; + + hash_to_next_epoch.insert(prev_hash, next_epoch.clone()); + hash_to_epoch.insert(prev_hash, epoch.clone()); + hash_to_next_epoch_approvals_req.insert(prev_hash, needs_next_epoch_approvals); + hash_to_valset.insert(epoch.clone(), valset); + hash_to_valset.insert(next_epoch.clone(), valset + 1); + epoch_start_map.insert(prev_hash, epoch_start); + + Ok((epoch, valset as usize % self.validators_by_valset.len(), next_epoch)) + } + + fn get_block_producers(&self, valset: usize) -> &[ValidatorPowerAndFrozen] { + &self.validators_by_valset[valset].block_producers + } + + fn get_chunk_producers(&self, valset: usize, shard_id: ShardId) -> Vec { + self.validators_by_valset[valset].chunk_producers[shard_id as usize].clone() + } + + fn get_valset_for_epoch(&self, epoch_id: &EpochId) -> Result { + // conveniently here if the prev_hash is passed mistakenly instead of the epoch_hash, + // the `unwrap` will trigger + Ok(*self + .hash_to_valset + .read() + .unwrap() + .get(epoch_id) + .ok_or_else(|| EpochError::EpochOutOfBounds(epoch_id.clone()))? as usize + % self.validators_by_valset.len()) + } + + fn get_block_header(&self, hash: &CryptoHash) -> Result, EpochError> { + let mut headers_cache = self.headers_cache.write().unwrap(); + if headers_cache.get(hash).is_some() { + return Ok(Some(headers_cache.get(hash).unwrap().clone())); + } + if let Some(result) = self.store.get_ser(DBCol::BlockHeader, hash.as_ref())? { + headers_cache.insert(*hash, result); + return Ok(Some(headers_cache.get(hash).unwrap().clone())); + } + Ok(None) + } +} + +impl KeyValueRuntime { + pub fn new(store: Store, epoch_manager: &MockEpochManager) -> Arc { + Self::new_with_no_gc(store, epoch_manager, false) + } + pub fn new_with_no_gc( + store: Store, + epoch_manager: &MockEpochManager, + no_gc: bool, + ) -> Arc { + let num_shards = epoch_manager.shard_ids(&EpochId::default()).unwrap().len() as NumShards; + let epoch_length = + epoch_manager.get_epoch_config(&EpochId::default()).unwrap().epoch_length; + let tries = TestTriesBuilder::new() + .with_store(store.clone()) + .with_shard_layout(0, num_shards) + .build(); + let mut initial_amounts = HashMap::new(); + for (i, validator_stake) in epoch_manager + .validators_by_valset + .iter() + .flat_map(|set| set.block_producers.iter()) + .enumerate() + { + initial_amounts.insert(validator_stake.account_id().clone(), (1000 + 100 * i) as u128); + } + + let kv_state = KVState { + amounts: initial_amounts, + receipt_nonces: HashSet::default(), + tx_nonces: HashSet::default(), + }; + let data = borsh::to_vec(&kv_state).unwrap(); + let data_len = data.len() as u64; + // StateRoot is actually faked here. + // We cannot do any reasonable validations of it in test_utils. + let state = HashMap::from([(Trie::EMPTY_ROOT, kv_state)]); + let state_size = HashMap::from([(Trie::EMPTY_ROOT, data_len)]); + + let mut store_update = store.store_update(); + let genesis_roots: Vec = (0..num_shards).map(|_| Trie::EMPTY_ROOT).collect(); + set_genesis_state_roots(&mut store_update, &genesis_roots); + set_genesis_hash(&mut store_update, &CryptoHash::default()); + store_update.commit().expect("Store failed on genesis intialization"); + + Arc::new(KeyValueRuntime { + store, + tries, + no_gc, + num_shards, + epoch_length, + headers_cache: RwLock::new(HashMap::new()), + state: RwLock::new(state), + state_size: RwLock::new(state_size), + }) + } + + fn get_block_header(&self, hash: &CryptoHash) -> Result, EpochError> { + let mut headers_cache = self.headers_cache.write().unwrap(); + if headers_cache.get(hash).is_some() { + return Ok(Some(headers_cache.get(hash).unwrap().clone())); + } + if let Some(result) = self.store.get_ser(DBCol::BlockHeader, hash.as_ref())? { + headers_cache.insert(*hash, result); + return Ok(Some(headers_cache.get(hash).unwrap().clone())); + } + Ok(None) + } +} + +pub fn account_id_to_shard_id(account_id: &AccountId, num_shards: NumShards) -> ShardId { + let shard_layout = ShardLayout::v0(num_shards, 0); + shard_layout::account_id_to_shard_id(account_id, &shard_layout) +} + +#[derive(BorshSerialize, BorshDeserialize)] +struct ReceiptNonce { + from: AccountId, + to: AccountId, + amount: Balance, + nonce: Nonce, +} + +fn create_receipt_nonce( + from: AccountId, + to: AccountId, + amount: Balance, + nonce: Nonce, +) -> CryptoHash { + CryptoHash::hash_borsh(ReceiptNonce { from, to, amount, nonce }) +} + +impl EpochManagerAdapter for MockEpochManager { + fn epoch_exists(&self, epoch_id: &EpochId) -> bool { + self.hash_to_valset.write().unwrap().contains_key(epoch_id) + } + + fn shard_ids(&self, _epoch_id: &EpochId) -> Result, EpochError> { + Ok((0..self.num_shards).collect()) + } + + fn num_total_parts(&self) -> usize { + 2 + } + + fn num_data_parts(&self) -> usize { + // Same as in Nightshade Runtime + 1 + } + + fn get_part_owner(&self, epoch_id: &EpochId, part_id: u64) -> Result { + let validators = + &self.get_epoch_block_producers_ordered(epoch_id, &CryptoHash::default())?; + // if we don't use data_parts and total_parts as part of the formula here, the part owner + // would not depend on height, and tests wouldn't catch passing wrong height here + let idx = part_id as usize + self.num_data_parts() + self.num_total_parts(); + Ok(validators[idx as usize % validators.len()].0.account_id().clone()) + } + + fn account_id_to_shard_id( + &self, + account_id: &AccountId, + _epoch_id: &EpochId, + ) -> Result { + Ok(account_id_to_shard_id(account_id, self.num_shards)) + } + + fn shard_id_to_uid( + &self, + shard_id: ShardId, + _epoch_id: &EpochId, + ) -> Result { + Ok(ShardUId { version: 0, shard_id: shard_id as u32 }) + } + + fn get_block_info(&self, _hash: &CryptoHash) -> Result, EpochError> { + Ok(Default::default()) + } + + fn get_epoch_config(&self, _epoch_id: &EpochId) -> Result { + Ok(EpochConfig { + epoch_length: self.epoch_length, + num_block_producer_seats: 2, + num_block_producer_seats_per_shard: vec![1, 1], + avg_hidden_validator_seats_per_shard: vec![1, 1], + block_producer_kickout_threshold: 0, + chunk_producer_kickout_threshold: 0, + validator_max_kickout_stake_perc: 0, + online_min_threshold: Ratio::new(1i32, 4i32), + online_max_threshold: Ratio::new(3i32, 4i32), + fishermen_threshold: 1, + minimum_stake_divisor: 1, + protocol_upgrade_stake_threshold: Ratio::new(3i32, 4i32), + shard_layout: ShardLayout::v1_test(), + validator_selection_config: ValidatorSelectionConfig::default(), + }) + } + + /// Return the epoch info containing the mocked data. + /// Epoch id is unused. + /// Available mocked data: + /// - validators + /// - block producers + /// - chunk producers + /// All the other fields have a hardcoded value or left empty. + fn get_epoch_info(&self, _epoch_id: &EpochId) -> Result, EpochError> { + let validators = self.validators.iter().map(|(_, stake)| stake.clone()).collect(); + let mut validator_to_index = HashMap::new(); + for (i, (account_id, _)) in self.validators.iter().enumerate() { + validator_to_index.insert(account_id.clone(), i as u64); + } + let bp_settlement = self.validators_by_valset[0] + .block_producers + .iter() + .map(|stake| *validator_to_index.get(stake.account_id()).unwrap()) + .collect(); + let cp_settlement = self.validators_by_valset[0] + .chunk_producers + .iter() + .map(|vec| { + vec.iter() + .map(|stake| *validator_to_index.get(stake.account_id()).unwrap()) + .collect() + }) + .collect(); + Ok(Arc::new(EpochInfo::new( + 10, + validators, + validator_to_index, + bp_settlement, + cp_settlement, + vec![], + vec![], + HashMap::new(), + BTreeMap::new(), + BTreeMap::new(), + HashMap::new(), + HashMap::new(), + 1, + 1, + 1, + RngSeed::default(), + Default::default(), + ))) + } + + fn get_shard_layout(&self, _epoch_id: &EpochId) -> Result { + Ok(ShardLayout::v0(self.num_shards, 0)) + } + + fn get_shard_config(&self, _epoch_id: &EpochId) -> Result { + panic!("get_shard_config not implemented for KeyValueRuntime"); + } + + fn is_next_block_epoch_start(&self, parent_hash: &CryptoHash) -> Result { + if parent_hash == &CryptoHash::default() { + return Ok(true); + } + let prev_block_header = self + .get_block_header(parent_hash)? + .ok_or_else(|| EpochError::MissingBlock(*parent_hash))?; + let prev_prev_hash = *prev_block_header.prev_hash(); + Ok(self.get_epoch_and_valset(*parent_hash)?.0 + != self.get_epoch_and_valset(prev_prev_hash)?.0) + } + + fn is_last_block_in_finished_epoch(&self, hash: &CryptoHash) -> Result { + self.is_next_block_epoch_start(hash) + } + + fn get_epoch_id_from_prev_block( + &self, + parent_hash: &CryptoHash, + ) -> Result { + Ok(self.get_epoch_and_valset(*parent_hash)?.0) + } + + fn get_epoch_height_from_prev_block( + &self, + _prev_block_hash: &CryptoHash, + ) -> Result { + Ok(0) + } + + fn get_next_epoch_id(&self, block_hash: &CryptoHash) -> Result { + let (_, _, next_epoch_id) = self.get_epoch_and_valset(*block_hash)?; + Ok(next_epoch_id) + } + + fn get_next_epoch_id_from_prev_block( + &self, + parent_hash: &CryptoHash, + ) -> Result { + Ok(self.get_epoch_and_valset(*parent_hash)?.2) + } + + fn get_prev_shard_ids( + &self, + _prev_hash: &CryptoHash, + shard_ids: Vec, + ) -> Result, Error> { + Ok(shard_ids) + } + + fn get_shard_layout_from_prev_block( + &self, + _parent_hash: &CryptoHash, + ) -> Result { + Ok(ShardLayout::v0(self.num_shards, 0)) + } + + fn get_epoch_id(&self, block_hash: &CryptoHash) -> Result { + let (epoch_id, _, _) = self.get_epoch_and_valset(*block_hash)?; + Ok(epoch_id) + } + + fn compare_epoch_id( + &self, + epoch_id: &EpochId, + other_epoch_id: &EpochId, + ) -> Result { + if epoch_id.0 == other_epoch_id.0 { + return Ok(Ordering::Equal); + } + match (self.get_valset_for_epoch(epoch_id), self.get_valset_for_epoch(other_epoch_id)) { + (Ok(index1), Ok(index2)) => Ok(index1.cmp(&index2)), + _ => Err(EpochError::EpochOutOfBounds(epoch_id.clone())), + } + } + + fn get_epoch_start_height(&self, block_hash: &CryptoHash) -> Result { + let epoch_id = self.get_epoch_id(block_hash)?; + match self.get_block_header(&epoch_id.0)? { + Some(block_header) => Ok(block_header.height()), + None => Ok(0), + } + } + + fn get_prev_epoch_id_from_prev_block( + &self, + prev_block_hash: &CryptoHash, + ) -> Result { + let mut candidate_hash = *prev_block_hash; + loop { + let header = self + .get_block_header(&candidate_hash)? + .ok_or_else(|| EpochError::MissingBlock(candidate_hash))?; + candidate_hash = *header.prev_hash(); + if self.is_next_block_epoch_start(&candidate_hash)? { + break Ok(self.get_epoch_and_valset(candidate_hash)?.0); + } + } + } + + fn get_estimated_protocol_upgrade_block_height( + &self, + _block_hash: CryptoHash, + ) -> Result, EpochError> { + Ok(None) + } + + fn get_epoch_block_producers_ordered( + &self, + epoch_id: &EpochId, + _last_known_block_hash: &CryptoHash, + ) -> Result, EpochError> { + let validators = self.get_block_producers(self.get_valset_for_epoch(epoch_id)?); + Ok(validators.iter().map(|x| (x.clone(), false)).collect()) + } + + fn get_epoch_block_approvers_ordered( + &self, + parent_hash: &CryptoHash, + ) -> Result, EpochError> { + let (_cur_epoch, cur_valset, next_epoch) = self.get_epoch_and_valset(*parent_hash)?; + let mut validators = self + .get_block_producers(cur_valset) + .iter() + .map(|x| x.get_approval_frozen(false)) + .collect::>(); + if *self.hash_to_next_epoch_approvals_req.write().unwrap().get(parent_hash).unwrap() { + let validators_copy = validators.clone(); + validators.extend( + self.get_block_producers(self.get_valset_for_epoch(&next_epoch)?) + .iter() + .filter(|x| { + !validators_copy.iter().any(|entry| &entry.account_id == x.account_id()) + }) + .map(|x| x.get_approval_frozen(true)), + ); + } + let validators = validators.into_iter().map(|stake| (stake, false)).collect::>(); + Ok(validators) + } + + fn get_epoch_chunk_producers( + &self, + _epoch_id: &EpochId, + ) -> Result, EpochError> { + tracing::warn!("not implemented, returning a dummy value"); + Ok(vec![]) + } + + fn get_block_producer( + &self, + epoch_id: &EpochId, + height: BlockHeight, + ) -> Result { + let validators = self.get_block_producers(self.get_valset_for_epoch(epoch_id)?); + Ok(validators[(height as usize) % validators.len()].account_id().clone()) + } + + fn get_block_producer_by_hash(&self, _block_hash: &CryptoHash) -> Result { + todo!() + } + + + fn get_chunk_producer( + &self, + epoch_id: &EpochId, + height: BlockHeight, + shard_id: ShardId, + ) -> Result { + let valset = self.get_valset_for_epoch(epoch_id)?; + let chunk_producers = self.get_chunk_producers(valset, shard_id); + let index = (shard_id + height + 1) as usize % chunk_producers.len(); + Ok(chunk_producers[index].account_id().clone()) + } + + fn get_chunk_validators( + &self, + _epoch_id: &EpochId, + _shard_id: ShardId, + _height: BlockHeight, + ) -> Result, EpochError> { + Ok(HashMap::new()) + } + + fn get_validator_by_account_id( + &self, + epoch_id: &EpochId, + _last_known_block_hash: &CryptoHash, + account_id: &AccountId, + ) -> Result<(ValidatorPowerAndFrozen, bool), EpochError> { + let validators = &self.validators_by_valset[self.get_valset_for_epoch(epoch_id)?]; + for validator_stake in validators.block_producers.iter() { + if validator_stake.account_id() == account_id { + return Ok((validator_stake.clone(), false)); + } + } + for validator_stake in validators.chunk_producers.iter().flatten() { + if validator_stake.account_id() == account_id { + return Ok((validator_stake.clone(), false)); + } + } + Err(EpochError::NotAValidator(account_id.clone(), epoch_id.clone())) + } + + fn get_fisherman_by_account_id( + &self, + epoch_id: &EpochId, + _last_known_block_hash: &CryptoHash, + account_id: &AccountId, + ) -> Result<(ValidatorPowerAndFrozen, bool), EpochError> { + Err(EpochError::NotAValidator(account_id.clone(), epoch_id.clone())) + } + + fn get_validator_info( + &self, + _epoch_id: ValidatorInfoIdentifier, + ) -> Result { + Ok(EpochValidatorInfo { + current_validators: vec![], + next_validators: vec![], + current_fishermen: vec![], + next_fishermen: vec![], + current_power_proposals: vec![], + current_frozen_proposals: vec![], + prev_epoch_kickout: vec![], + epoch_start_height: 0, + epoch_height: 1, + }) + } + + fn add_validator_proposals( + &self, + _block_header_info: BlockHeaderInfo, + ) -> Result { + Ok(self.store.store_update()) + } + + fn get_epoch_minted_amount(&self, _epoch_id: &EpochId) -> Result { + Ok(0) + } + + fn get_epoch_protocol_version( + &self, + _epoch_id: &EpochId, + ) -> Result { + Ok(PROTOCOL_VERSION) + } + + fn get_epoch_sync_data( + &self, + _prev_epoch_last_block_hash: &CryptoHash, + _epoch_id: &EpochId, + _next_epoch_id: &EpochId, + ) -> Result< + ( + Arc, + Arc, + Arc, + Arc, + Arc, + Arc, + ), + EpochError, + > { + Ok(Default::default()) + } + + fn epoch_sync_init_epoch_manager( + &self, + _prev_epoch_first_block_info: BlockInfo, + _prev_epoch_last_block_info: BlockInfo, + _prev_epoch_prev_last_block_info: BlockInfo, + _prev_epoch_id: &EpochId, + _prev_epoch_info: EpochInfo, + _epoch_id: &EpochId, + _epoch_info: EpochInfo, + _next_epoch_id: &EpochId, + _next_epoch_info: EpochInfo, + ) -> Result<(), EpochError> { + Ok(()) + } + + fn verify_block_vrf( + &self, + _epoch_id: &EpochId, + _block_height: BlockHeight, + _prev_random_value: &CryptoHash, + _vrf_value: &unc_crypto::vrf::Value, + _vrf_proof: &unc_crypto::vrf::Proof, + ) -> Result<(), Error> { + Ok(()) + } + + fn verify_validator_signature( + &self, + _epoch_id: &EpochId, + _last_known_block_hash: &CryptoHash, + _account_id: &AccountId, + _data: &[u8], + _signature: &Signature, + ) -> Result { + Ok(true) + } + + fn verify_validator_or_fisherman_signature( + &self, + _epoch_id: &EpochId, + _last_known_block_hash: &CryptoHash, + _account_id: &AccountId, + _data: &[u8], + _signature: &Signature, + ) -> Result { + Ok(true) + } + + fn verify_header_signature(&self, header: &BlockHeader) -> Result { + let validator = self.get_block_producer(&header.epoch_id(), header.height())?; + let validator_stake = &self.validators[&validator]; + Ok(header.verify_block_producer(validator_stake.public_key())) + } + + fn verify_chunk_signature_with_header_parts( + &self, + _chunk_hash: &ChunkHash, + _signature: &Signature, + _epoch_id: &EpochId, + _last_kown_hash: &CryptoHash, + _height_created: BlockHeight, + _shard_id: ShardId, + ) -> Result { + Ok(true) + } + + fn verify_approval( + &self, + _prev_block_hash: &CryptoHash, + _prev_block_height: BlockHeight, + _block_height: BlockHeight, + _approvals: &[Option>], + ) -> Result { + Ok(true) + } + + fn verify_approvals_and_threshold_orphan( + &self, + epoch_id: &EpochId, + can_approved_block_be_produced: &dyn Fn( + &[Option>], + &[(Balance, Balance, bool)], + ) -> bool, + prev_block_hash: &CryptoHash, + prev_block_height: BlockHeight, + block_height: BlockHeight, + approvals: &[Option>], + ) -> Result<(), Error> { + let validators = self.get_block_producers(self.get_valset_for_epoch(epoch_id)?); + let message_to_sign = Approval::get_data_for_sig( + &if prev_block_height + 1 == block_height { + ApprovalInner::Endorsement(*prev_block_hash) + } else { + ApprovalInner::Skip(prev_block_height) + }, + block_height, + ); + + for (validator, may_be_signature) in validators.iter().zip(approvals.iter()) { + if let Some(signature) = may_be_signature { + if !signature.verify(message_to_sign.as_ref(), validator.public_key()) { + return Err(Error::InvalidApprovals); + } + } + } + let all_frozen = validators.iter().map(|frozen| (frozen.frozen(), 0, false)).collect::>(); + if !can_approved_block_be_produced(approvals, &all_frozen) { + Err(Error::NotEnoughApprovals) + } else { + Ok(()) + } + } + + fn cares_about_shard_from_prev_block( + &self, + parent_hash: &CryptoHash, + account_id: &AccountId, + shard_id: ShardId, + ) -> Result { + // This `unwrap` here tests that in all code paths we check that the epoch exists before + // we check if we care about a shard. Please do not remove the unwrap, fix the logic of + // the calling function. + let epoch_valset = self.get_epoch_and_valset(*parent_hash).unwrap(); + let chunk_producers = self.get_chunk_producers(epoch_valset.1, shard_id); + for validator in chunk_producers { + if validator.account_id() == account_id { + return Ok(true); + } + } + Ok(false) + } + + fn cares_about_shard_next_epoch_from_prev_block( + &self, + parent_hash: &CryptoHash, + account_id: &AccountId, + shard_id: ShardId, + ) -> Result { + // This `unwrap` here tests that in all code paths we check that the epoch exists before + // we check if we care about a shard. Please do not remove the unwrap, fix the logic of + // the calling function. + let epoch_valset = self.get_epoch_and_valset(*parent_hash).unwrap(); + let chunk_producers = self + .get_chunk_producers((epoch_valset.1 + 1) % self.validators_by_valset.len(), shard_id); + for validator in chunk_producers { + if validator.account_id() == account_id { + return Ok(true); + } + } + Ok(false) + } + + fn will_shard_layout_change(&self, parent_hash: &CryptoHash) -> Result { + // Copied from EpochManager (KeyValueRuntime is deprecated anyway). + let epoch_id = self.get_epoch_id_from_prev_block(parent_hash)?; + let next_epoch_id = self.get_next_epoch_id_from_prev_block(parent_hash)?; + let shard_layout = self.get_shard_layout(&epoch_id)?; + let next_shard_layout = self.get_shard_layout(&next_epoch_id)?; + Ok(shard_layout != next_shard_layout) + } + + #[cfg(feature = "new_epoch_sync")] + fn get_all_epoch_hashes( + &self, + _last_block_info: &BlockInfo, + _hash_to_prev_hash: Option<&HashMap>, + ) -> Result, EpochError> { + Ok(vec![]) + } + + #[cfg(feature = "new_epoch_sync")] + fn force_update_aggregator(&self, _epoch_id: &EpochId, _hash: &CryptoHash) {} + +} + +impl RuntimeAdapter for KeyValueRuntime { + fn store(&self) -> &Store { + &self.store + } + + fn get_tries(&self) -> ShardTries { + self.tries.clone() + } + + fn get_trie_for_shard( + &self, + shard_id: ShardId, + _block_hash: &CryptoHash, + state_root: StateRoot, + _use_flat_storage: bool, + ) -> Result { + Ok(self + .tries + .get_trie_for_shard(ShardUId { version: 0, shard_id: shard_id as u32 }, state_root)) + } + + fn get_flat_storage_manager(&self) -> unc_store::flat::FlatStorageManager { + self.tries.get_flat_storage_manager() + } + + fn get_view_trie_for_shard( + &self, + shard_id: ShardId, + _block_hash: &CryptoHash, + state_root: StateRoot, + ) -> Result { + Ok(self.tries.get_view_trie_for_shard( + ShardUId { version: 0, shard_id: shard_id as u32 }, + state_root, + )) + } + + fn validate_tx( + &self, + _gas_price: Balance, + _state_update: Option, + _transaction: &SignedTransaction, + _verify_signature: bool, + _epoch_id: &EpochId, + _current_protocol_version: ProtocolVersion, + ) -> Result, Error> { + Ok(None) + } + + fn prepare_transactions( + &self, + _gas_price: Balance, + _gas_limit: Gas, + _epoch_id: &EpochId, + _shard_id: ShardId, + _state_root: StateRoot, + _next_block_height: BlockHeight, + transactions: &mut dyn PoolIterator, + _chain_validate: &mut dyn FnMut(&SignedTransaction) -> bool, + _current_protocol_version: ProtocolVersion, + _time_limit: Option, + ) -> Result, Error> { + let mut res = vec![]; + while let Some(iter) = transactions.next() { + res.push(iter.next().unwrap()); + } + Ok(res) + } + + fn apply_chunk( + &self, + storage_config: RuntimeStorageConfig, + chunk: ApplyChunkShardContext, + block: ApplyChunkBlockContext, + receipts: &[Receipt], + transactions: &[SignedTransaction], + ) -> Result { + assert!(!storage_config.record_storage); + let mut tx_results = vec![]; + let shard_id = chunk.shard_id; + + let mut state = + self.state.read().unwrap().get(&storage_config.state_root).cloned().unwrap(); + + let mut balance_transfers = vec![]; + + for receipt in receipts.iter() { + if let ReceiptEnum::Action(action) = &receipt.receipt { + assert_eq!(account_id_to_shard_id(&receipt.receiver_id, self.num_shards), shard_id); + if !state.receipt_nonces.contains(&receipt.receipt_id) { + state.receipt_nonces.insert(receipt.receipt_id); + if let Action::Transfer(TransferAction { deposit }) = action.actions[0] { + balance_transfers.push(( + receipt.get_hash(), + receipt.predecessor_id.clone(), + receipt.receiver_id.clone(), + deposit, + 0, + )); + } + } else { + panic!("receipts should never be applied twice"); + } + } else { + unreachable!(); + } + } + + for transaction in transactions { + assert_eq!( + account_id_to_shard_id(&transaction.transaction.signer_id, self.num_shards), + shard_id + ); + if transaction.transaction.actions.is_empty() { + continue; + } + if let Action::Transfer(TransferAction { deposit }) = transaction.transaction.actions[0] + { + if !state.tx_nonces.contains(&AccountNonce( + transaction.transaction.receiver_id.clone(), + transaction.transaction.nonce, + )) { + state.tx_nonces.insert(AccountNonce( + transaction.transaction.receiver_id.clone(), + transaction.transaction.nonce, + )); + balance_transfers.push(( + transaction.get_hash(), + transaction.transaction.signer_id.clone(), + transaction.transaction.receiver_id.clone(), + deposit, + transaction.transaction.nonce, + )); + } else { + balance_transfers.push(( + transaction.get_hash(), + transaction.transaction.signer_id.clone(), + transaction.transaction.receiver_id.clone(), + 0, + transaction.transaction.nonce, + )); + } + } else { + unreachable!(); + } + } + + let mut outgoing_receipts = vec![]; + + for (hash, from, to, amount, nonce) in balance_transfers { + let mut good_to_go = false; + + if account_id_to_shard_id(&from, self.num_shards) != shard_id { + // This is a receipt, was already debited + good_to_go = true; + } else if let Some(balance) = state.amounts.get(&from) { + if *balance >= amount { + let new_balance = balance - amount; + state.amounts.insert(from.clone(), new_balance); + good_to_go = true; + } + } + + if good_to_go { + let new_receipt_hashes = if account_id_to_shard_id(&to, self.num_shards) == shard_id + { + state.amounts.insert(to.clone(), state.amounts.get(&to).unwrap_or(&0) + amount); + vec![] + } else { + assert_ne!(nonce, 0); + let receipt = Receipt { + predecessor_id: from.clone(), + receiver_id: to.clone(), + receipt_id: create_receipt_nonce(from.clone(), to.clone(), amount, nonce), + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: from.clone(), + signer_public_key: PublicKey::empty(KeyType::ED25519), + gas_price: block.gas_price, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: vec![Action::Transfer(TransferAction { deposit: amount })], + }), + }; + let receipt_hash = receipt.get_hash(); + outgoing_receipts.push(receipt); + vec![receipt_hash] + }; + + tx_results.push(ExecutionOutcomeWithId { + id: hash, + outcome: ExecutionOutcome { + status: ExecutionStatus::SuccessValue(vec![]), + logs: vec![], + receipt_ids: new_receipt_hashes, + gas_burnt: 0, + compute_usage: Some(0), + tokens_burnt: 0, + executor_id: to.clone(), + metadata: ExecutionMetadata::V1, + }, + }); + } + } + + let data = borsh::to_vec(&state)?; + let state_size = data.len() as u64; + let state_root = hash(&data); + self.state.write().unwrap().insert(state_root, state); + self.state_size.write().unwrap().insert(state_root, state_size); + + Ok(ApplyChunkResult { + trie_changes: WrappedTrieChanges::new( + self.get_tries(), + ShardUId { version: 0, shard_id: shard_id as u32 }, + TrieChanges::empty(state_root), + Default::default(), + block.block_hash, + block.height, + ), + new_root: state_root, + outcomes: tx_results, + outgoing_receipts, + validator_power_proposals: vec![], + validator_frozen_proposals: vec![], + total_gas_burnt: 0, + total_balance_burnt: 0, + proof: None, + processed_delayed_receipts: vec![], + }) + } + + fn query( + &self, + _shard_id: ShardUId, + state_root: &StateRoot, + block_height: BlockHeight, + _block_timestamp: u64, + _prev_block_hash: &CryptoHash, + block_hash: &CryptoHash, + _epoch_id: &EpochId, + request: &QueryRequest, + ) -> Result { + match request { + QueryRequest::ViewAccount { account_id, .. } => Ok(QueryResponse { + kind: QueryResponseKind::ViewAccount( + Account::new( + self.state.read().unwrap().get(state_root).map_or_else( + || 0, + |state| *state.amounts.get(account_id).unwrap_or(&0), + ), + 0, + 0, + CryptoHash::default(), + 0, + ) + .into(), + ), + block_height, + block_hash: *block_hash, + }), + QueryRequest::ViewCode { .. } => Ok(QueryResponse { + kind: QueryResponseKind::ViewCode(ContractCodeView { + code: vec![], + hash: CryptoHash::default(), + }), + block_height, + block_hash: *block_hash, + }), + QueryRequest::ViewAccessKeyList { .. } => Ok(QueryResponse { + kind: QueryResponseKind::AccessKeyList(AccessKeyList { + keys: vec![AccessKeyInfoView { + public_key: PublicKey::empty(KeyType::ED25519), + access_key: AccessKey::full_access().into(), + }], + }), + block_height, + block_hash: *block_hash, + }), + QueryRequest::ViewAccessKey { .. } => Ok(QueryResponse { + kind: QueryResponseKind::AccessKey(AccessKey::full_access().into()), + block_height, + block_hash: *block_hash, + }), + QueryRequest::ViewState { .. } => Ok(QueryResponse { + kind: QueryResponseKind::ViewState(ViewStateResult { + values: Default::default(), + proof: vec![], + }), + block_height, + block_hash: *block_hash, + }), + QueryRequest::CallFunction { .. } => Ok(QueryResponse { + kind: QueryResponseKind::CallResult(CallResult { + result: Default::default(), + logs: Default::default(), + }), + block_height, + block_hash: *block_hash, + }), + } + } + + fn obtain_state_part( + &self, + _shard_id: ShardId, + _block_hash: &CryptoHash, + state_root: &StateRoot, + part_id: PartId, + ) -> Result, Error> { + if part_id.idx != 0 { + return Ok(vec![]); + } + let state = self.state.read().unwrap().get(state_root).unwrap().clone(); + let data = borsh::to_vec(&state).expect("should never fall"); + Ok(data) + } + + fn validate_state_part(&self, _state_root: &StateRoot, _part_id: PartId, _data: &[u8]) -> bool { + // We do not care about deeper validation in test_utils + true + } + + fn apply_state_part( + &self, + _shard_id: ShardId, + state_root: &StateRoot, + part_id: PartId, + data: &[u8], + _epoch_id: &EpochId, + ) -> Result<(), Error> { + if part_id.idx != 0 { + return Ok(()); + } + let state = KVState::try_from_slice(data).unwrap(); + self.state.write().unwrap().insert(*state_root, state.clone()); + let data = borsh::to_vec(&state)?; + let state_size = data.len() as u64; + self.state_size.write().unwrap().insert(*state_root, state_size); + Ok(()) + } + + fn get_state_root_node( + &self, + _shard_id: ShardId, + _block_hash: &CryptoHash, + state_root: &StateRoot, + ) -> Result { + let data = borsh::to_vec(&self.state.read().unwrap().get(state_root).unwrap().clone()) + .expect("should never fall") + .into(); + let memory_usage = *self.state_size.read().unwrap().get(state_root).unwrap(); + Ok(StateRootNode { data, memory_usage }) + } + + fn validate_state_root_node( + &self, + _state_root_node: &StateRootNode, + _state_root: &StateRoot, + ) -> bool { + // We do not care about deeper validation in test_utils + true + } + + fn get_gc_stop_height(&self, block_hash: &CryptoHash) -> BlockHeight { + if !self.no_gc { + // This code is 'incorrect' - as production one is always setting the GC to the + // first block of the epoch. + // Unfortunately many tests are depending on this and not setting epochs when + // they produce blocks. + let block_height = self + .get_block_header(block_hash) + .unwrap_or_default() + .map(|h| h.height()) + .unwrap_or_default(); + block_height.saturating_sub(DEFAULT_GC_NUM_EPOCHS_TO_KEEP * self.epoch_length) + /* // TODO: use this version of the code instead - after we fix the block creation + // issue in multiple tests. + // We have to return the first block of the epoch T-DEFAULT_GC_NUM_EPOCHS_TO_KEEP. + let mut current_header = self.get_block_header(block_hash).unwrap().unwrap(); + for _ in 0..DEFAULT_GC_NUM_EPOCHS_TO_KEEP { + let last_block_of_prev_epoch = current_header.next_epoch_id(); + current_header = + self.get_block_header(&last_block_of_prev_epoch.0).unwrap().unwrap(); + } + loop { + if current_header.next_epoch_id().0 == *current_header.prev_hash() { + break; + } + current_header = + self.get_block_header(current_header.prev_hash()).unwrap().unwrap(); + } + current_header.height()*/ + } else { + 0 + } + } + + fn get_protocol_config(&self, _epoch_id: &EpochId) -> Result { + unreachable!("get_protocol_config should not be called in KeyValueRuntime"); + } + + fn will_shard_layout_change_next_epoch( + &self, + _parent_hash: &CryptoHash, + ) -> Result { + Ok(false) + } + + fn apply_update_to_children_states( + &self, + _block_hash: &CryptoHash, + _block_height: BlockHeight, + _state_roots: HashMap, + _next_shard_layout: &ShardLayout, + _state_changes: StateChangesForResharding, + ) -> Result, Error> { + Ok(vec![]) + } + + fn load_mem_tries_on_startup(&self, _shard_uids: &[ShardUId]) -> Result<(), StorageError> { + Ok(()) + } +} diff --git a/chain/chain/src/test_utils/validator_schedule.rs b/chain/chain/src/test_utils/validator_schedule.rs new file mode 100644 index 000000000..9aa76289a --- /dev/null +++ b/chain/chain/src/test_utils/validator_schedule.rs @@ -0,0 +1,104 @@ +use unc_primitives::types::{AccountId, NumShards}; +use std::collections::HashSet; + +/// Validator schedule describes how block and chunk producers are selected by +/// the KeyValue runtime. +/// +/// In the real runtime, we use complex algorithm based on randomness and stake +/// to select the validators. For for tests though, we just want to select them +/// by fiat. +/// +/// Conventional short name for `ValidatorSchedule` is `vs`. +#[derive(Clone)] +pub struct ValidatorSchedule { + pub(super) block_producers: Vec>, + pub(super) chunk_only_producers: Vec>>, + pub(super) validator_groups: u64, + pub(super) num_shards: NumShards, +} + +impl ValidatorSchedule { + pub fn new() -> Self { + Self::new_with_shards(1) + } + + pub fn new_with_shards(num_shards: NumShards) -> Self { + Self { + block_producers: Vec::new(), + chunk_only_producers: Vec::new(), + validator_groups: 1, + num_shards, + } + } + + /// Specifies, for each epoch, the set of block producers for this epoch. + /// + /// Conceptually, this "loops around" when `epoch_id >= block_producers.len()` + pub fn block_producers_per_epoch(mut self, block_producers: Vec>) -> Self { + self.block_producers = block_producers; + self.sanity_check(); + self + } + + /// Specifies, for each shard in each epoch, the set of chunk-only producers + /// for the shard. + /// + /// The full set of chunk-producers is composed from chunk_only producers + /// and some subset of block_producers (this is controlled by + /// `validator_groups`). + pub fn chunk_only_producers_per_epoch_per_shard( + mut self, + chunk_only_producers: Vec>>, + ) -> Self { + self.chunk_only_producers = chunk_only_producers; + self.sanity_check(); + self + } + + /// Controls how chunk_producers are selected from the block producers. + /// + /// This parameter splits the validators in each shard into that many + /// groups, and assigns a separate set of shards to each group. E.g. if + /// there are 8 validators in a particular epoch, and 4 shards, and 2 + /// validator groups, 4 validators will be assigned to two shards, and 4 + /// remaining validators will be assigned to 2 remaining shards. With 8 + /// validators, 4 shards and 4 validator groups each shard will be assigned + /// to two validators. + /// + /// See how `EpochValidatorSet` is constructed for details. + pub fn validator_groups(mut self, validator_groups: u64) -> Self { + self.validator_groups = validator_groups; + self + } + + pub fn num_shards(mut self, num_shards: NumShards) -> Self { + self.num_shards = num_shards; + self + } + + pub fn all_block_producers(&self) -> impl Iterator { + self.block_producers.iter().flatten() + } + + pub fn all_validators(&self) -> impl Iterator { + self.all_block_producers().chain(self.chunk_only_producers.iter().flatten().flatten()) + } + + fn sanity_check(&self) { + let mut block_producers = HashSet::new(); + for bp in self.block_producers.iter().flatten() { + if !block_producers.insert(bp) { + panic!("block producer {bp} is specified twice") + } + } + let mut chunk_only_producers = HashSet::new(); + for cop in self.chunk_only_producers.iter().flatten().flatten() { + if !chunk_only_producers.insert(cop) { + panic!("chunk only producer {cop} is specified twice") + } + } + if let Some(v) = block_producers.intersection(&chunk_only_producers).next() { + panic!("{v} is both a block and a chunk only producer") + } + } +} diff --git a/chain/chain/src/tests/challenges.rs b/chain/chain/src/tests/challenges.rs new file mode 100644 index 000000000..c58e90338 --- /dev/null +++ b/chain/chain/src/tests/challenges.rs @@ -0,0 +1,25 @@ +use crate::test_utils::setup; +use crate::Error; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::test_utils::TestBlockBuilder; + +#[test] +fn test_no_challenge_on_same_header() { + init_test_logger(); + let (mut chain, _, _, signer) = setup(); + let prev_hash = *chain.head_header().unwrap().hash(); + let prev = chain.get_block(&prev_hash).unwrap(); + let block = TestBlockBuilder::new(&prev, signer).build(); + chain.process_block_test(&None, block.clone()).unwrap(); + assert_eq!(chain.head().unwrap().height, 1); + let mut challenges = vec![]; + if let Err(e) = chain.process_block_header(block.header(), &mut challenges) { + match e { + Error::BlockKnown(_) => {} + _ => panic!("Wrong error kind {}", e), + } + assert!(challenges.is_empty()); + } else { + panic!("Process the same header twice should produce error"); + } +} diff --git a/chain/chain/src/tests/doomslug.rs b/chain/chain/src/tests/doomslug.rs new file mode 100644 index 000000000..8df9e4315 --- /dev/null +++ b/chain/chain/src/tests/doomslug.rs @@ -0,0 +1,320 @@ +use unc_primitives::static_clock::StaticClock; +use unc_primitives::test_utils::create_test_signer; +use rand::{thread_rng, Rng}; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; +use std::time::{Duration, Instant}; + +use crate::{Doomslug, DoomslugThresholdMode}; +use unc_crypto::{KeyType, SecretKey}; +use unc_primitives::block::Approval; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::types::{ApprovalStake, BlockHeight}; + +fn block_hash(height: BlockHeight, ord: usize) -> CryptoHash { + hash(([height.to_le_bytes(), ord.to_le_bytes()].concat()).as_ref()) +} + +fn get_msg_delivery_time(now: Instant, gst: Instant, delta: Duration) -> Instant { + std::cmp::max(now, gst) + + Duration::from_millis(thread_rng().gen_range(0..delta.as_millis()) as u64) +} + +/// Runs a single iteration of a fuzz test given specific time until global stabilization and +/// the max delay on messages. +/// Returns amount of time it took to produce a doomslug final block at height 50, as well as the +/// largest height encountered +/// +/// # Arguments +/// * `time_to_gst` - number of milliseconds before global stabilization time +/// * `delta` - max message delay +/// * `height_goal` - the appearance of a block at this (or higher) height with finality +/// will end the test +fn one_iter( + time_to_gst: Duration, + delta: Duration, + height_goal: BlockHeight, +) -> (Duration, BlockHeight) { + let account_ids = ["test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8"]; + let stakes = account_ids + .iter() + .map(|account_id| ApprovalStake { + account_id: account_id.parse().unwrap(), + stake_this_epoch: 1, + stake_next_epoch: 1, + public_key: SecretKey::from_seed(KeyType::ED25519, account_id).public_key(), + }) + .map(|stake| (stake, false)) + .collect::>(); + let signers = account_ids + .iter() + .map(|account_id| Arc::new(create_test_signer(account_id))) + .collect::>(); + let mut doomslugs = signers + .iter() + .map(|signer| { + Doomslug::new( + 0, + Duration::from_millis(200), + Duration::from_millis(1000), + Duration::from_millis(100), + delta * 20, // some arbitrary number larger than delta * 6 + Some(signer.clone()), + DoomslugThresholdMode::TwoThirds, + ) + }) + .collect::>(); + + let mut now = StaticClock::instant(); + let started = now; + + let gst = now + time_to_gst; + let mut approval_queue: Vec<(Approval, Instant)> = vec![]; + let mut block_queue: Vec<(BlockHeight, usize, BlockHeight, Instant, CryptoHash)> = vec![]; + let mut largest_produced_height: BlockHeight = 1; + let mut chain_lengths = HashMap::new(); + let mut hash_to_block_info: HashMap = + HashMap::new(); + let mut hash_to_prev_hash: HashMap = HashMap::new(); + + let mut blocks_with_finality: Vec<(CryptoHash, BlockHeight)> = vec![]; + + chain_lengths.insert(block_hash(1, 0), 1); + + for ds in doomslugs.iter_mut() { + ds.set_tip(now, block_hash(1, 0), 1, 1); + hash_to_block_info.insert(block_hash(1, 0), (1, 1, block_hash(1, 0))); + } + + let mut is_done = false; + while !is_done { + now = now + Duration::from_millis(25); + let mut new_approval_queue = vec![]; + let mut new_block_queue = vec![]; + + // 1. Process approvals + for approval in approval_queue.into_iter() { + if approval.1 > now { + new_approval_queue.push(approval); + } else { + let me = (approval.0.target_height % 8) as usize; + + // Make test1 and test2 be offline and never send approvals + if matches!(approval.0.account_id.as_ref(), "test1" | "test2") { + continue; + } + + // Generally make 20% of the remaining approvals to drop + if thread_rng().gen_range(0..10) < 2 { + continue; + } + + doomslugs[me].on_approval_message(now, &approval.0, &stakes); + } + } + approval_queue = new_approval_queue; + + // 2. Process blocks + for block in block_queue.into_iter() { + if block.3 > now { + new_block_queue.push(block); + } else { + let ds = &mut doomslugs[block.1 as usize]; + if block.0 as BlockHeight > ds.get_tip().1 { + // Accept all the blocks from the tip till this block + let mut block_infos = vec![(block.0, block.2, block.4)]; + for block_index in 0..50 { + if block_index == 49 { + assert!(false); + } + + let last_block = block_infos.last().unwrap(); + let prev_block_info = hash_to_block_info + .get(&hash_to_prev_hash.get(&last_block.2).unwrap()) + .unwrap(); + + if prev_block_info.0 as BlockHeight <= ds.get_tip().1 { + break; + } + block_infos.push(*prev_block_info); + } + + for block_info in block_infos.into_iter().rev() { + if block_info.0 > ds.get_tip().1 { + ds.set_tip( + now, + block_info.2, + block_info.0 as BlockHeight, + block_info.1, + ); + } + } + } + } + } + block_queue = new_block_queue; + + // 3. Process timers + for ds in doomslugs.iter_mut() { + for approval in ds.process_timer(now) { + approval_queue.push((approval, get_msg_delivery_time(now, gst, delta))); + } + } + + // 4. Produce blocks + 'outer: for (bp_ord, ds) in doomslugs.iter_mut().enumerate() { + for target_height in (ds.get_tip().1 + 1)..=ds.get_largest_height_crossing_threshold() { + if ds.ready_to_produce_block(now, target_height, true, false) { + let num_blocks_to_produce = if bp_ord < 3 { 2 } else { 1 }; + + for block_ord in 0..num_blocks_to_produce { + let parent_hash = ds.get_tip().0; + + let prev_height = hash_to_block_info.get(&parent_hash).unwrap().0; + let prev_prev_height = if prev_height <= 1 { + 0 + } else { + let prev_prev_hash = hash_to_prev_hash.get(&parent_hash).unwrap(); + hash_to_block_info.get(&prev_prev_hash).unwrap().0 + }; + + let is_final = + target_height == prev_height + 1 && prev_height == prev_prev_height + 1; + + let last_final_height = if is_final { + target_height - 2 + } else { + hash_to_block_info.get(&parent_hash).unwrap().1 + }; + + if target_height >= 2048 { + println!("Largest produced_height: {}", largest_produced_height); + for ds in doomslugs.iter() { + println!( + " - tip: ({:?}), final_height: {}, timer height: {}", + ds.get_tip(), + ds.get_largest_final_height(), + ds.get_timer_height() + ); + } + assert!(false); + break 'outer; + } + let block_hash = block_hash(target_height, block_ord); + for whom in 0..8 { + let block_info = ( + target_height, + whom, + last_final_height, + get_msg_delivery_time(now, gst, delta), + block_hash, + ); + block_queue.push(block_info); + } + + hash_to_block_info + .insert(block_hash, (target_height, last_final_height, block_hash)); + hash_to_prev_hash.insert(block_hash, parent_hash); + + assert!(chain_lengths.get(&block_hash).is_none()); + let prev_length = *chain_lengths.get(&ds.get_tip().0).unwrap(); + chain_lengths.insert(block_hash, prev_length + 1); + + if is_final && target_height != 2 { + blocks_with_finality.push(( + *hash_to_prev_hash.get(&parent_hash).unwrap(), + target_height - 2, + )); + } + + if target_height > largest_produced_height { + largest_produced_height = target_height; + } + if target_height >= height_goal && is_final { + assert!(prev_length + 1 > 20); // make sure we actually built some chain + is_done = true; + } + + // Accept our own block (only accept the last if are maliciously producing multiple, + // so that `ds.get_tip(...)` doesn't return the new block on the next iteration) + if block_ord + 1 == num_blocks_to_produce { + ds.set_tip( + now, + block_hash, + target_height as BlockHeight, + last_final_height, + ); + } + } + } + } + } + + // 5. In the liveness proof we rely on timers always being within delta from each other + // Validate that assumption + for i in 0..8 { + for j in (i + 1)..8 { + let ith_timer_start = doomslugs[i].get_timer_start(); + let jth_timer_start = doomslugs[j].get_timer_start(); + + // Only makes sense for timers that are more than delta in the past, since for more + // recent timers the other participant's start time might be in the future + if now - ith_timer_start >= delta && now - jth_timer_start >= delta { + if ith_timer_start > jth_timer_start { + assert!(ith_timer_start - jth_timer_start <= delta); + } else { + assert!(jth_timer_start - ith_timer_start <= delta); + } + } + } + } + } + + // We successfully got to the `height_goal`. Check that all the blocks are building only on + // doomslug final blocks + for (block_hash, (block_height, _, _)) in hash_to_block_info.iter() { + let mut seen_hashes = HashSet::new(); + let mut block_hash = *block_hash; + seen_hashes.insert(block_hash); + + loop { + match hash_to_prev_hash.get(&block_hash) { + None => break, + Some(prev_block_hash) => { + block_hash = *prev_block_hash; + seen_hashes.insert(block_hash); + } + } + } + + for (block_hash, height) in blocks_with_finality.iter() { + assert!(*height >= *block_height || seen_hashes.contains(block_hash)); + } + } + + (now - started, largest_produced_height) +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_fuzzy_doomslug_liveness_and_safety() { + for (time_to_gst_millis, height_goal) in + &[(0, 200), (1000, 200), (10000, 300), (100000, 400), (500000, 500)] + { + for delta in &[100, 300, 500, 1000, 2000, 4000] { + println!("Staring set of tests. Time to GST: {}, delta: {}", time_to_gst_millis, delta); + for _iter in 0..10 { + let (took, height) = one_iter( + Duration::from_millis(*time_to_gst_millis), + Duration::from_millis(*delta), + *height_goal, + ); + println!( + " --> Took {} (simulated) milliseconds and {} heights", + took.as_millis(), + height + ); + } + } + } +} diff --git a/chain/chain/src/tests/garbage_collection.rs b/chain/chain/src/tests/garbage_collection.rs new file mode 100644 index 000000000..ea5fe2d60 --- /dev/null +++ b/chain/chain/src/tests/garbage_collection.rs @@ -0,0 +1,991 @@ +use rand::Rng; +use std::sync::Arc; + +use crate::chain::Chain; +use crate::garbage_collection::GCMode; +use crate::test_utils::{ + get_chain, get_chain_with_epoch_length, get_chain_with_epoch_length_and_num_shards, + get_chain_with_num_shards, +}; +use crate::types::Tip; +use crate::{ChainStoreAccess, StoreValidator}; + +use unc_chain_configs::{GCConfig, GenesisConfig}; +use unc_epoch_manager::EpochManagerAdapter; +use unc_primitives::block::Block; +use unc_primitives::epoch_manager::block_info::BlockInfo; +use unc_primitives::merkle::PartialMerkleTree; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::test_utils::{create_test_signer, TestBlockBuilder}; +use unc_primitives::types::{BlockHeight, NumBlocks, StateRoot}; +use unc_primitives::validator_signer::InMemoryValidatorSigner; +use unc_store::test_utils::gen_changes; +use unc_store::{DBCol, ShardTries, Trie, WrappedTrieChanges}; + +// Build a chain of num_blocks on top of prev_block +fn do_fork( + mut prev_block: Block, + mut prev_state_roots: Vec, + tries: ShardTries, + chain: &mut Chain, + num_blocks: u64, + states: &mut Vec<(Block, Vec, Vec, Option>)>>)>, + max_changes: usize, + verbose: bool, +) { + if verbose { + println!( + "Prev Block: @{} {:?} {:?} {:?}", + prev_block.header().height(), + prev_block.header().epoch_id(), + prev_block.header().next_epoch_id(), + prev_block.hash() + ); + } + let mut rng = rand::thread_rng(); + let signer = Arc::new(create_test_signer("test1")); + let num_shards = prev_state_roots.len() as u64; + for i in 0..num_blocks { + let next_epoch_id = chain + .epoch_manager + .get_next_epoch_id_from_prev_block(prev_block.hash()) + .expect("block must exist"); + let block = if next_epoch_id == *prev_block.header().next_epoch_id() { + TestBlockBuilder::new(&prev_block, signer.clone()).build() + } else { + let prev_hash = prev_block.hash(); + let epoch_id = prev_block.header().next_epoch_id().clone(); + if verbose { + println!( + "Creating block with new epoch id {:?} @{}", + next_epoch_id, + prev_block.header().height() + 1 + ); + } + let next_bp_hash = Chain::compute_bp_hash( + chain.epoch_manager.as_ref(), + next_epoch_id.clone(), + epoch_id.clone(), + &prev_hash, + ) + .unwrap(); + TestBlockBuilder::new(&prev_block, signer.clone()) + .epoch_id(epoch_id) + .next_epoch_id(next_epoch_id) + .next_bp_hash(next_bp_hash) + .build() + }; + + if verbose { + println!( + "Block: @{} {:?} {:?} {:?}", + block.header().height(), + block.header().epoch_id(), + block.header().next_epoch_id(), + block.hash() + ); + } + + let head = chain.head().unwrap(); + let mut store_update = chain.mut_chain_store().store_update(); + if i == 0 { + store_update.save_block_merkle_tree(*prev_block.hash(), PartialMerkleTree::default()); + } + store_update.save_block(block.clone()); + store_update.inc_block_refcount(block.header().prev_hash()).unwrap(); + store_update.save_block_header(block.header().clone()).unwrap(); + let tip = Tip::from_header(block.header()); + if head.height < tip.height { + store_update.save_head(&tip).unwrap(); + } + + let mut trie_changes_shards = Vec::new(); + for shard_id in 0..num_shards { + let shard_uid = ShardUId { version: 0, shard_id: shard_id as u32 }; + let trie_changes_data = gen_changes(&mut rng, max_changes); + let state_root = prev_state_roots[shard_id as usize]; + let trie = tries.get_trie_for_shard(shard_uid, state_root); + let trie_changes = trie.update(trie_changes_data.iter().cloned()).unwrap(); + if verbose { + println!("state new {:?} {:?}", block.header().height(), trie_changes_data); + } + + let new_root = trie_changes.new_root; + let wrapped_trie_changes = WrappedTrieChanges::new( + tries.clone(), + shard_uid, + trie_changes, + Default::default(), + *block.hash(), + block.header().height(), + ); + store_update.save_trie_changes(wrapped_trie_changes); + + prev_state_roots[shard_id as usize] = new_root; + trie_changes_shards.push(trie_changes_data); + } + store_update.commit().unwrap(); + states.push((block.clone(), prev_state_roots.clone(), trie_changes_shards)); + prev_block = block.clone(); + } +} + +// This test infrastructure do the following: +// Build Chain 1 from the full data, then GC it. +// Build Chain 2 from the data removing everything that should be removed after GC. +// Make sure that Chain 1 == Chain 2. +fn gc_fork_common(simple_chains: Vec, max_changes: usize) { + // Running the test + println!( + "Running gc test: max_changes per block = {:?}, simple_chains_len = {:?} simple_chains = {:?}", + max_changes, + simple_chains.len(), + simple_chains + ); + let verbose = false; + + let num_shards = rand::thread_rng().gen_range(1..3); + + // Init Chain 1 + let mut chain1 = get_chain_with_num_shards(num_shards); + let tries1 = chain1.runtime_adapter.get_tries(); + let mut rng = rand::thread_rng(); + let shard_to_check_trie = rng.gen_range(0..num_shards); + let shard_uid = ShardUId { version: 0, shard_id: shard_to_check_trie as u32 }; + let genesis1 = chain1.get_block_by_height(0).unwrap(); + let mut states1 = vec![]; + states1.push(( + genesis1, + vec![Trie::EMPTY_ROOT; num_shards as usize], + vec![Vec::new(); num_shards as usize], + )); + + for simple_chain in simple_chains.iter() { + let (source_block1, state_root1, _) = states1[simple_chain.from as usize].clone(); + do_fork( + source_block1.clone(), + state_root1, + tries1.clone(), + &mut chain1, + simple_chain.length, + &mut states1, + max_changes, + verbose, + ); + } + + // GC execution + chain1 + .clear_data(tries1.clone(), &GCConfig { gc_blocks_limit: 1000, ..GCConfig::default() }) + .unwrap(); + + let tries2 = get_chain_with_num_shards(num_shards).runtime_adapter.get_tries(); + + // Find gc_height + let mut gc_height = simple_chains[0].length - 51; + for (i, simple_chain) in simple_chains.iter().enumerate() { + if (!simple_chain.is_removed) && gc_height > simple_chain.from && i > 0 { + gc_height = simple_chain.from + } + } + if verbose { + println!("GC height = {:?}", gc_height); + } + + let mut start_index = 1; // zero is for genesis + let mut state_roots2 = vec![]; + state_roots2.push(Trie::EMPTY_ROOT); + + for simple_chain in simple_chains.iter() { + if simple_chain.is_removed { + for _ in 0..simple_chain.length { + // This chain is deleted in Chain1 + state_roots2.push(Trie::EMPTY_ROOT); + } + start_index += simple_chain.length; + continue; + } + + let mut state_root2 = state_roots2[simple_chain.from as usize]; + let state_root1 = states1[simple_chain.from as usize].1[shard_to_check_trie as usize]; + tries1.get_trie_for_shard(shard_uid, state_root1).iter().unwrap(); + assert_eq!(state_root1, state_root2); + + for i in start_index..start_index + simple_chain.length { + let (block1, state_root1, changes1) = states1[i as usize].clone(); + // Apply to Trie 2 the same changes (changes1) as applied to Trie 1 + let trie_changes2 = tries2 + .get_trie_for_shard(shard_uid, state_root2) + .update(changes1[shard_to_check_trie as usize].iter().cloned()) + .unwrap(); + // i == gc_height is the only height should be processed here + let mut update2 = tries2.store_update(); + if block1.header().height() > gc_height || i == gc_height { + tries2.apply_insertions(&trie_changes2, shard_uid, &mut update2); + state_root2 = trie_changes2.new_root; + assert_eq!(state_root1[shard_to_check_trie as usize], state_root2); + } else { + let new_root2 = tries2.apply_all(&trie_changes2, shard_uid, &mut update2); + state_root2 = new_root2; + } + state_roots2.push(state_root2); + update2.commit().unwrap(); + } + start_index += simple_chain.length; + } + + let mut start_index = 1; // zero is for genesis + for simple_chain in simple_chains.iter() { + if simple_chain.is_removed { + for i in start_index..start_index + simple_chain.length { + let (block1, _, _) = states1[i as usize].clone(); + // Make sure that blocks were removed. + assert_eq!( + chain1.block_exists(block1.hash()).unwrap(), + false, + "Block {:?}@{} should have been removed - as it belongs to removed fork.", + block1.hash(), + block1.header().height() + ); + } + start_index += simple_chain.length; + continue; + } + for i in start_index..start_index + simple_chain.length { + let (block1, state_root1, _) = states1[i as usize].clone(); + let state_root1 = state_root1[shard_to_check_trie as usize]; + if block1.header().height() > gc_height || i == gc_height { + assert_eq!( + chain1.block_exists(block1.hash()).unwrap(), + true, + "Block {:?}@{} should exist", + block1.hash(), + block1.header().height() + ); + let a = tries1 + .get_trie_for_shard(shard_uid, state_root1) + .iter() + .unwrap() + .map(|item| item.unwrap().0) + .collect::>(); + let b = tries2 + .get_trie_for_shard(shard_uid, state_root1) + .iter() + .unwrap() + .map(|item| item.unwrap().0) + .collect::>(); + assert_eq!(a, b); + } else { + // Make sure that blocks were removed. + assert_eq!( + chain1.block_exists(block1.hash()).unwrap(), + false, + "Block {:?}@{} should have been removed.", + block1.hash(), + block1.header().height() + ); + } + } + start_index += simple_chain.length; + } +} + +// from is an index in blocks array, length is the number of blocks in a chain on top of blocks[from], +// is_removed = should this chain be removed after GC +#[derive(Debug, Clone)] +pub struct SimpleChain { + from: u64, + length: u64, + is_removed: bool, +} + +// This test creates only chain +#[test] +fn test_gc_remove_simple_chain_sanity() { + for max_changes in 1..=20 { + let chains = vec![SimpleChain { from: 0, length: 101, is_removed: false }]; + // remove 50 blocks, height = 50 will be the earliest one which exists + gc_fork_common(chains, max_changes); + } +} + +// Creates simple shorter fork and GCs it +fn test_gc_remove_fork_common(max_changes_limit: usize) { + for max_changes in 1..=max_changes_limit { + for fork_length in 1..=10 { + let chains = vec![ + SimpleChain { from: 0, length: 101, is_removed: false }, + SimpleChain { from: 10, length: fork_length, is_removed: true }, + ]; + gc_fork_common(chains, max_changes); + } + for fork_length in 30..=40 { + let chains = vec![ + SimpleChain { from: 0, length: 101, is_removed: false }, + SimpleChain { from: 10, length: fork_length, is_removed: true }, + ]; + gc_fork_common(chains, max_changes); + } + } +} + +#[test] +fn test_gc_remove_fork_small() { + test_gc_remove_fork_common(1) +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_gc_remove_fork_large() { + test_gc_remove_fork_common(20) +} + +#[test] +fn test_gc_remove_fork_fail_often() { + for _tries in 0..10 { + let chains = vec![ + SimpleChain { from: 0, length: 101, is_removed: false }, + SimpleChain { from: 10, length: 35, is_removed: true }, + ]; + gc_fork_common(chains, 1); + let chains = vec![ + SimpleChain { from: 0, length: 101, is_removed: false }, + SimpleChain { from: 10, length: 80, is_removed: false }, + ]; + gc_fork_common(chains, 6); + } +} + +// Creates simple shorter fork and NOT GCs it +fn test_gc_not_remove_fork_common(max_changes_limit: usize) { + for max_changes in 1..=max_changes_limit { + for fork_length in 41..=50 { + let chains = vec![ + SimpleChain { from: 0, length: 101, is_removed: false }, + SimpleChain { from: 10, length: fork_length, is_removed: false }, + ]; + gc_fork_common(chains, max_changes); + } + for fork_length in 80..=90 { + let chains = vec![ + SimpleChain { from: 0, length: 101, is_removed: false }, + SimpleChain { from: 10, length: fork_length, is_removed: false }, + ]; + gc_fork_common(chains, max_changes); + } + } +} + +#[test] +fn test_gc_not_remove_fork_small() { + test_gc_not_remove_fork_common(1) +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_gc_not_remove_fork_large() { + test_gc_not_remove_fork_common(20) +} + +// Creates simple longer fork and NOT GCs it +#[test] +fn test_gc_not_remove_longer_fork() { + for fork_length in 91..=100 { + let chains = vec![ + SimpleChain { from: 0, length: 101, is_removed: false }, + SimpleChain { from: 10, length: fork_length, is_removed: false }, + ]; + gc_fork_common(chains, 1); + } +} + +// This test creates forks from genesis +#[test] +fn test_gc_forks_from_genesis() { + for fork_length in 1..=10 { + let chains = vec![ + SimpleChain { from: 0, length: 101, is_removed: false }, + SimpleChain { from: 0, length: fork_length, is_removed: true }, + ]; + gc_fork_common(chains, 1); + } + for fork_length in 45..=50 { + let chains = vec![ + SimpleChain { from: 0, length: 101, is_removed: false }, + SimpleChain { from: 0, length: fork_length, is_removed: true }, + ]; + gc_fork_common(chains, 1); + } + for fork_length in 51..=55 { + let chains = vec![ + SimpleChain { from: 0, length: 101, is_removed: false }, + SimpleChain { from: 0, length: fork_length, is_removed: false }, + ]; + gc_fork_common(chains, 1); + } + for fork_length in 0..=10 { + let chains = vec![ + SimpleChain { from: 0, length: 101, is_removed: false }, + SimpleChain { from: 0, length: 51 + fork_length, is_removed: false }, + SimpleChain { from: 0, length: fork_length, is_removed: true }, + SimpleChain { from: 0, length: 50 - fork_length, is_removed: true }, + ]; + gc_fork_common(chains, 1); + } +} + +#[test] +fn test_gc_overlap() { + for max_changes in 1..=20 { + let chains = vec![ + SimpleChain { from: 0, length: 101, is_removed: false }, + SimpleChain { from: 10, length: 70, is_removed: false }, + SimpleChain { from: 20, length: 25, is_removed: true }, + SimpleChain { from: 30, length: 30, is_removed: false }, + SimpleChain { from: 40, length: 1, is_removed: true }, + ]; + gc_fork_common(chains, max_changes); + } +} + +fn test_gc_boundaries_common(max_changes_limit: usize) { + for max_changes in 1..=max_changes_limit { + for i in 45..=51 { + for len in 1..=5 { + let chains = vec![ + SimpleChain { from: 0, length: 101, is_removed: false }, + SimpleChain { from: i, length: len, is_removed: i + len <= 50 }, + ]; + gc_fork_common(chains, max_changes); + } + } + } +} + +#[test] +fn test_gc_boundaries_small() { + test_gc_boundaries_common(1) +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_gc_boundaries_large() { + test_gc_boundaries_common(20) +} + +fn test_gc_random_common(runs: u64) { + let mut rng = rand::thread_rng(); + for _tests in 0..runs { + let canonical_len = 101; + let mut chains = vec![SimpleChain { from: 0, length: canonical_len, is_removed: false }]; + for _num_chains in 1..10 { + let from = rng.gen_range(0..50); + let len = rng.gen_range(0..50) + 1; + chains.push(SimpleChain { + from, + length: len, + is_removed: from + len < canonical_len - 50, + }); + gc_fork_common(chains.clone(), rng.gen_range(0..20) + 1); + } + } +} + +#[test] +fn test_gc_random_small() { + test_gc_random_common(3); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_gc_random_large() { + test_gc_random_common(25); +} + +#[test] +fn test_gc_pine_small() { + let mut chains = vec![SimpleChain { from: 0, length: 101, is_removed: false }]; + for i in 1..50 { + chains.push(SimpleChain { from: i, length: 1, is_removed: true }); + } + for i in 50..100 { + chains.push(SimpleChain { from: i, length: 1, is_removed: false }); + } + gc_fork_common(chains, 3); + + let mut chains = vec![SimpleChain { from: 0, length: 101, is_removed: false }]; + for i in 1..49 { + chains.push(SimpleChain { from: i, length: 2, is_removed: true }); + } + for i in 49..99 { + chains.push(SimpleChain { from: i, length: 2, is_removed: false }); + } + gc_fork_common(chains, 2); + + let mut chains = vec![SimpleChain { from: 0, length: 101, is_removed: false }]; + for i in 1..48 { + chains.push(SimpleChain { from: i, length: 3, is_removed: true }); + } + for i in 48..98 { + chains.push(SimpleChain { from: i, length: 3, is_removed: false }); + } + gc_fork_common(chains, 1); + + let mut chains = vec![SimpleChain { from: 0, length: 101, is_removed: false }]; + for i in 1..40 { + chains.push(SimpleChain { from: i, length: 11, is_removed: true }); + } + for i in 40..90 { + chains.push(SimpleChain { from: i, length: 11, is_removed: false }); + } + gc_fork_common(chains, 1); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_gc_pine() { + for max_changes in 1..=20 { + let mut chains = vec![SimpleChain { from: 0, length: 101, is_removed: false }]; + for i in 1..50 { + chains.push(SimpleChain { from: i, length: 1, is_removed: true }); + } + for i in 50..100 { + chains.push(SimpleChain { from: i, length: 1, is_removed: false }); + } + gc_fork_common(chains, max_changes); + + let mut chains = vec![SimpleChain { from: 0, length: 101, is_removed: false }]; + for i in 1..40 { + chains.push(SimpleChain { from: i, length: 11, is_removed: true }); + } + for i in 40..90 { + chains.push(SimpleChain { from: i, length: 11, is_removed: false }); + } + gc_fork_common(chains, max_changes); + } +} + +fn test_gc_star_common(max_changes_limit: usize) { + for max_changes in 1..=max_changes_limit { + let mut chains = vec![SimpleChain { from: 0, length: 101, is_removed: false }]; + for i in 1..=17 { + chains.push(SimpleChain { from: 33, length: i, is_removed: true }); + } + for i in 18..67 { + chains.push(SimpleChain { from: 33, length: i, is_removed: false }); + } + gc_fork_common(chains, max_changes); + } +} + +#[test] +fn test_gc_star_small() { + test_gc_star_common(1) +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_gc_star_large() { + test_gc_star_common(20) +} + +#[test] +// This test covers the situation when fork happens far away from the end of the epoch. +// Currently, we start fork cleanup, once we are at the epoch boundary, by setting 'fork_tail'. +// Then in each step, we move the fork_tail backwards, cleaning up any forks that we might encounter. +// But in order not to do too much work in one go, we check up to 1000 blocks (and we continue during the next execution of clean). +// This test checks what happens when fork is more than 1k blocks from the epoch end - and checks that it gets properly cleaned +// during the second run. +fn test_fork_far_away_from_epoch_end() { + let verbose = false; + let max_changes = 1; + let fork_clean_step = 100; + let epoch_length = fork_clean_step + 10; + let simple_chains = vec![ + SimpleChain { from: 0, length: 5, is_removed: false }, + SimpleChain { from: 5, length: 2, is_removed: true }, + // We want the chain to end up exactly at the new epoch start. + SimpleChain { from: 5, length: 6 * epoch_length - 5 + 1, is_removed: false }, + ]; + + let num_shards = 1; + let mut chain1 = get_chain_with_epoch_length_and_num_shards(epoch_length, num_shards); + let tries1 = chain1.runtime_adapter.get_tries(); + let genesis1 = chain1.get_block_by_height(0).unwrap(); + let mut states1 = vec![( + genesis1, + vec![Trie::EMPTY_ROOT; num_shards as usize], + vec![Vec::new(); num_shards as usize], + )]; + + for simple_chain in simple_chains.iter() { + let (source_block1, state_root1, _) = states1[simple_chain.from as usize].clone(); + do_fork( + source_block1.clone(), + state_root1, + tries1.clone(), + &mut chain1, + simple_chain.length, + &mut states1, + max_changes, + verbose, + ); + } + + // GC execution + chain1 + .clear_data( + tries1.clone(), + &GCConfig { + gc_blocks_limit: 100, + gc_fork_clean_step: fork_clean_step, + ..GCConfig::default() + }, + ) + .expect("Clear data failed"); + + // The run above would clear just the first 5 blocks from the beginning, but shouldn't clear any forks + // yet - as fork_tail only clears the 'last' 1k blocks. + for i in 1..5 { + let (block, _, _) = states1[i as usize].clone(); + assert_eq!( + chain1.block_exists(block.hash()).unwrap(), + false, + "Block {:?}@{} should have been removed.", + block.hash(), + block.header().height() + ); + } + // But blocks from the fork - shouldn't be removed yet. + for i in 6..7 { + let (block, _, _) = states1[i as usize].clone(); + assert_eq!( + chain1.block_exists(block.hash()).unwrap(), + true, + "Block {:?}@{} should NOT be removed.", + block.hash(), + block.header().height() + ); + } + // Now let's add one more block - and now the fork (and the rest) should be successfully removed. + { + let (source_block1, state_root1, _) = states1.last().unwrap().clone(); + do_fork( + source_block1, + state_root1, + tries1.clone(), + &mut chain1, + 1, + &mut states1, + max_changes, + verbose, + ); + } + chain1 + .clear_data(tries1, &GCConfig { gc_blocks_limit: 100, ..GCConfig::default() }) + .expect("Clear data failed"); + // And now all these blocks should be safely removed. + for i in 6..50 { + let (block, _, _) = states1[i as usize].clone(); + assert_eq!( + chain1.block_exists(block.hash()).unwrap(), + false, + "Block {:?}@{} should have been removed.", + block.hash(), + block.header().height() + ); + } +} + +/// Test that garbage collection works properly. The blocks behind gc head should be garbage +/// collected while the blocks that are ahead of it should not. +#[test] +fn test_clear_old_data() { + let mut chain = get_chain_with_epoch_length(1); + let epoch_manager = chain.epoch_manager.clone(); + let genesis = chain.get_block_by_height(0).unwrap(); + let signer = Arc::new(create_test_signer("test1")); + let mut prev_block = genesis; + let mut blocks = vec![prev_block.clone()]; + for i in 1..15 { + add_block( + &mut chain, + epoch_manager.as_ref(), + &mut prev_block, + &mut blocks, + signer.clone(), + i, + ); + } + + let trie = chain.runtime_adapter.get_tries(); + chain.clear_data(trie, &GCConfig { gc_blocks_limit: 100, ..GCConfig::default() }).unwrap(); + + // epoch didn't change so no data is garbage collected. + for i in 0..15 { + println!("height = {} hash = {}", i, blocks[i].hash()); + if i < 8 { + assert!(chain.get_block(blocks[i].hash()).is_err()); + assert!(chain + .mut_chain_store() + .get_all_block_hashes_by_height(i as BlockHeight) + .unwrap() + .is_empty()); + } else { + assert!(chain.get_block(blocks[i].hash()).is_ok()); + assert!(!chain + .mut_chain_store() + .get_all_block_hashes_by_height(i as BlockHeight) + .unwrap() + .is_empty()); + } + } +} + +// Adds block to the chain at given height after prev_block. +fn add_block( + chain: &mut Chain, + epoch_manager: &dyn EpochManagerAdapter, + prev_block: &mut Block, + blocks: &mut Vec, + signer: Arc, + height: u64, +) { + let next_epoch_id = epoch_manager + .get_next_epoch_id_from_prev_block(prev_block.hash()) + .expect("block must exist"); + let mut store_update = chain.mut_chain_store().store_update(); + + let block = if next_epoch_id == *prev_block.header().next_epoch_id() { + TestBlockBuilder::new(&prev_block, signer).height(height).build() + } else { + let prev_hash = prev_block.hash(); + let epoch_id = prev_block.header().next_epoch_id().clone(); + let next_bp_hash = Chain::compute_bp_hash( + epoch_manager, + next_epoch_id.clone(), + epoch_id.clone(), + &prev_hash, + ) + .unwrap(); + TestBlockBuilder::new(&prev_block, signer) + .height(height) + .epoch_id(epoch_id) + .next_epoch_id(next_epoch_id) + .next_bp_hash(next_bp_hash) + .build() + }; + blocks.push(block.clone()); + store_update.save_block(block.clone()); + store_update.inc_block_refcount(block.header().prev_hash()).unwrap(); + store_update.save_block_header(block.header().clone()).unwrap(); + store_update.save_head(&Tip::from_header(block.header())).unwrap(); + store_update + .chain_store_cache_update + .height_to_hashes + .insert(height, Some(*block.header().hash())); + store_update.save_next_block_hash(prev_block.hash(), *block.hash()); + store_update.commit().unwrap(); + *prev_block = block.clone(); +} + +#[test] +fn test_clear_old_data_fixed_height() { + let mut chain = get_chain(); + let epoch_manager = chain.epoch_manager.clone(); + let genesis = chain.get_block_by_height(0).unwrap(); + let signer = Arc::new(create_test_signer("test1")); + let mut prev_block = genesis; + let mut blocks = vec![prev_block.clone()]; + for i in 1..10 { + add_block( + &mut chain, + epoch_manager.as_ref(), + &mut prev_block, + &mut blocks, + signer.clone(), + i, + ); + } + + assert!(chain.get_block(blocks[4].hash()).is_ok()); + assert!(chain.get_block(blocks[5].hash()).is_ok()); + assert!(chain.get_block(blocks[6].hash()).is_ok()); + assert!(chain.get_block_header(blocks[5].hash()).is_ok()); + assert_eq!( + chain + .mut_chain_store() + .get_all_block_hashes_by_height(5) + .unwrap() + .values() + .flatten() + .collect::>(), + vec![blocks[5].hash()] + ); + assert!(chain.mut_chain_store().get_next_block_hash(blocks[5].hash()).is_ok()); + + let trie = chain.runtime_adapter.get_tries(); + let mut store_update = chain.mut_chain_store().store_update(); + assert!(store_update + .clear_block_data(epoch_manager.as_ref(), *blocks[5].hash(), GCMode::Canonical(trie)) + .is_ok()); + store_update.commit().unwrap(); + + assert!(chain.get_block(blocks[4].hash()).is_err()); + assert!(chain.get_block(blocks[5].hash()).is_ok()); + assert!(chain.get_block(blocks[6].hash()).is_ok()); + // block header should be available + assert!(chain.get_block_header(blocks[4].hash()).is_ok()); + assert!(chain.get_block_header(blocks[5].hash()).is_ok()); + assert!(chain.get_block_header(blocks[6].hash()).is_ok()); + assert!(chain.mut_chain_store().get_all_block_hashes_by_height(4).unwrap().is_empty()); + assert!(!chain.mut_chain_store().get_all_block_hashes_by_height(5).unwrap().is_empty()); + assert!(!chain.mut_chain_store().get_all_block_hashes_by_height(6).unwrap().is_empty()); + assert!(chain.mut_chain_store().get_next_block_hash(blocks[4].hash()).is_err()); + assert!(chain.mut_chain_store().get_next_block_hash(blocks[5].hash()).is_ok()); + assert!(chain.mut_chain_store().get_next_block_hash(blocks[6].hash()).is_ok()); +} + +/// Test that `gc_blocks_limit` works properly +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_clear_old_data_too_many_heights() { + for i in 1..5 { + println!("gc_blocks_limit == {:?}", i); + test_clear_old_data_too_many_heights_common(i); + } + test_clear_old_data_too_many_heights_common(25); + test_clear_old_data_too_many_heights_common(50); + test_clear_old_data_too_many_heights_common(87); +} + +fn test_clear_old_data_too_many_heights_common(gc_blocks_limit: NumBlocks) { + let mut chain = get_chain_with_epoch_length(1); + let genesis = chain.get_block_by_height(0).unwrap(); + let signer = Arc::new(create_test_signer("test1")); + let mut prev_block = genesis; + let mut blocks = vec![prev_block.clone()]; + { + let mut store_update = chain.chain_store().store().store_update(); + let block_info = BlockInfo::default(); + store_update.insert_ser(DBCol::BlockInfo, prev_block.hash().as_ref(), &block_info).unwrap(); + store_update.commit().unwrap(); + } + for i in 1..1000 { + let block = TestBlockBuilder::new(&prev_block, signer.clone()).height(i).build(); + blocks.push(block.clone()); + + let mut store_update = chain.mut_chain_store().store_update(); + store_update.save_block(block.clone()); + store_update.inc_block_refcount(block.header().prev_hash()).unwrap(); + store_update.save_block_header(block.header().clone()).unwrap(); + store_update.save_head(&Tip::from_header(&block.header())).unwrap(); + { + let mut store_update = store_update.store().store_update(); + let block_info = BlockInfo::default(); + store_update.insert_ser(DBCol::BlockInfo, block.hash().as_ref(), &block_info).unwrap(); + store_update.commit().unwrap(); + } + store_update + .chain_store_cache_update + .height_to_hashes + .insert(i, Some(*block.header().hash())); + store_update.save_next_block_hash(&prev_block.hash(), *block.hash()); + store_update.commit().unwrap(); + + prev_block = block.clone(); + } + + let trie = chain.runtime_adapter.get_tries(); + + for iter in 0..10 { + println!("ITERATION #{:?}", iter); + assert!(chain + .clear_data(trie.clone(), &GCConfig { gc_blocks_limit, ..GCConfig::default() }) + .is_ok()); + + // epoch didn't change so no data is garbage collected. + for i in 0..1000 { + if i < (iter + 1) * gc_blocks_limit as usize { + assert!(chain.get_block(&blocks[i].hash()).is_err()); + assert!(chain + .mut_chain_store() + .get_all_block_hashes_by_height(i as BlockHeight) + .unwrap() + .is_empty()); + } else { + assert!(chain.get_block(&blocks[i].hash()).is_ok()); + assert!(!chain + .mut_chain_store() + .get_all_block_hashes_by_height(i as BlockHeight) + .unwrap() + .is_empty()); + } + } + let mut genesis = GenesisConfig::default(); + genesis.genesis_height = 0; + let mut store_validator = StoreValidator::new( + None, + genesis.clone(), + chain.epoch_manager.clone(), + chain.shard_tracker.clone(), + chain.runtime_adapter.clone(), + chain.chain_store().store().clone(), + false, + ); + store_validator.validate(); + println!("errors = {:?}", store_validator.errors); + assert!(!store_validator.is_failed()); + } +} + +#[test] +fn test_fork_chunk_tail_updates() { + let mut chain = get_chain(); + let epoch_manager = chain.epoch_manager.clone(); + let genesis = chain.get_block_by_height(0).unwrap(); + let signer = Arc::new(create_test_signer("test1")); + let mut prev_block = genesis; + let mut blocks = vec![prev_block.clone()]; + for i in 1..10 { + add_block( + &mut chain, + epoch_manager.as_ref(), + &mut prev_block, + &mut blocks, + signer.clone(), + i, + ); + } + assert_eq!(chain.tail().unwrap(), 0); + + { + let mut store_update = chain.mut_chain_store().store_update(); + assert_eq!(store_update.tail().unwrap(), 0); + store_update.update_tail(1).unwrap(); + store_update.commit().unwrap(); + } + // Chunk tail should be auto updated to genesis (if not set) and fork_tail to the tail. + { + let store_update = chain.mut_chain_store().store_update(); + assert_eq!(store_update.tail().unwrap(), 1); + assert_eq!(store_update.fork_tail().unwrap(), 1); + assert_eq!(store_update.chunk_tail().unwrap(), 0); + } + { + let mut store_update = chain.mut_chain_store().store_update(); + store_update.update_fork_tail(3); + store_update.commit().unwrap(); + } + { + let mut store_update = chain.mut_chain_store().store_update(); + store_update.update_tail(2).unwrap(); + store_update.commit().unwrap(); + } + { + let store_update = chain.mut_chain_store().store_update(); + assert_eq!(store_update.tail().unwrap(), 2); + assert_eq!(store_update.fork_tail().unwrap(), 3); + assert_eq!(store_update.chunk_tail().unwrap(), 0); + } +} diff --git a/chain/chain/src/tests/mod.rs b/chain/chain/src/tests/mod.rs new file mode 100644 index 000000000..508acadec --- /dev/null +++ b/chain/chain/src/tests/mod.rs @@ -0,0 +1,31 @@ +mod challenges; +mod doomslug; +mod garbage_collection; +mod simple_chain; +mod sync_chain; + +use crate::block_processing_utils::BlockProcessingArtifact; +use crate::test_utils::process_block_sync; +use crate::{Block, Chain, Error, Provenance}; +use unc_primitives::account::id::AccountId; +use unc_primitives::utils::MaybeValidated; + +impl Chain { + /// A wrapper function around process_block that doesn't trigger all the callbacks + /// Only used in tests + pub(crate) fn process_block_test( + &mut self, + me: &Option, + block: Block, + ) -> Result<(), Error> { + let mut block_processing_artifacts = BlockProcessingArtifact::default(); + process_block_sync( + self, + me, + MaybeValidated::from(block), + Provenance::PRODUCED, + &mut block_processing_artifacts, + ) + .map(|_| {}) + } +} diff --git a/chain/chain/src/tests/simple_chain.rs b/chain/chain/src/tests/simple_chain.rs new file mode 100644 index 000000000..ca4e7e2d3 --- /dev/null +++ b/chain/chain/src/tests/simple_chain.rs @@ -0,0 +1,282 @@ +use crate::unc_chain_primitives::error::BlockKnownError; +use crate::test_utils::{setup, wait_for_all_blocks_in_processing}; +use crate::{Block, BlockProcessingArtifact, ChainStoreAccess, Error}; +use assert_matches::assert_matches; +use chrono; +use chrono::TimeZone; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::hash::CryptoHash; +use unc_primitives::static_clock::MockClockGuard; +use unc_primitives::test_utils::TestBlockBuilder; +use unc_primitives::version::PROTOCOL_VERSION; +use num_rational::Ratio; +use std::sync::Arc; +use std::time::Instant; + +fn timestamp(hour: u32, min: u32, sec: u32, millis: u32) -> chrono::DateTime { + chrono::Utc.with_ymd_and_hms(2020, 10, 1, hour, min, sec).single().unwrap() + + chrono::Duration::milliseconds(i64::from(millis)) +} + +#[test] +fn build_chain() { + init_test_logger(); + let mock_clock_guard = MockClockGuard::default(); + + mock_clock_guard.add_utc(timestamp(0, 0, 3, 444)); + mock_clock_guard.add_utc(timestamp(0, 0, 0, 0)); // Client startup timestamp. + mock_clock_guard.add_utc(timestamp(0, 0, 0, 0)); // Client startup timestamp. + mock_clock_guard.add_instant(Instant::now()); + + // this step may fail when adding a new dynamic config + // the dynamic config uses the static clock to update metrics + // for every new field one extra utc timestamp should be added + let (mut chain, _, _, signer) = setup(); + + assert_eq!(mock_clock_guard.utc_call_count(), 3); + assert_eq!(mock_clock_guard.instant_call_count(), 1); + assert_eq!(chain.head().unwrap().height, 0); + + // The hashes here will have to be modified after changes to the protocol. + // In particular if you update protocol version or add new protocol + // features. If this assert is failing without you adding any new or + // stabilising any existing protocol features, this indicates bug in your + // code which unexpectedly changes the protocol. + // + // To update the hashes you can use cargo-insta. Note that you’ll need to + // run the test twice: once with default features and once with + // ‘nightly’ feature enabled: + // + // cargo install cargo-insta + // cargo insta test --accept -p unc-chain -- tests::simple_chain::build_chain + // cargo insta test --accept -p unc-chain --features nightly -- tests::simple_chain::build_chain + let hash = chain.head().unwrap().last_block_hash; + if cfg!(feature = "nightly") { + insta::assert_display_snapshot!(hash, @"CwaiZ4AmfJSnMN9rytYwwYHCTzLioC5xcjHzNkDex1HH"); + } else { + insta::assert_display_snapshot!(hash, @"HJmRPXT4JM9tt6mXw2gM75YaSoqeDCphhFK26uRpd1vw"); + } + + for i in 1..5 { + // two entries, because the clock is called 2 times per block + // - one time for creation of the block + // - one time for validating block header + mock_clock_guard.add_utc(timestamp(0, 0, 3, 444 + i)); + mock_clock_guard.add_utc(timestamp(0, 0, 3, 444 + i)); + // Instant calls for CryptoHashTimer. + mock_clock_guard.add_instant(Instant::now()); + mock_clock_guard.add_instant(Instant::now()); + mock_clock_guard.add_instant(Instant::now()); + mock_clock_guard.add_instant(Instant::now()); + + let prev_hash = *chain.head_header().unwrap().hash(); + let prev = chain.get_block(&prev_hash).unwrap(); + let block = TestBlockBuilder::new(&prev, signer.clone()).build(); + chain.process_block_test(&None, block).unwrap(); + assert_eq!(chain.head().unwrap().height, i as u64); + } + + assert_eq!(mock_clock_guard.utc_call_count(), 11); + assert_eq!(mock_clock_guard.instant_call_count(), 17); + assert_eq!(chain.head().unwrap().height, 4); + + let hash = chain.head().unwrap().last_block_hash; + if cfg!(feature = "nightly") { + insta::assert_display_snapshot!(hash, @"Dn18HUFm149fojXpwV1dYCfjdPh56S1k233kp7vmnFeE"); + } else { + insta::assert_display_snapshot!(hash, @"HbQVGVZ3WGxsNqeM3GfSwDoxwYZ2RBP1SinAze9SYR3C"); + } +} + +#[test] +fn build_chain_with_orphans() { + init_test_logger(); + let (mut chain, _, _, signer) = setup(); + let mut blocks = vec![chain.get_block(&chain.genesis().hash().clone()).unwrap()]; + for i in 1..4 { + let block = TestBlockBuilder::new(&blocks[i - 1], signer.clone()).build(); + blocks.push(block); + } + let last_block = &blocks[blocks.len() - 1]; + let block = Block::produce( + PROTOCOL_VERSION, + PROTOCOL_VERSION, + last_block.header(), + 10, + last_block.header().block_ordinal() + 1, + last_block.chunks().iter().cloned().collect(), + last_block.header().epoch_id().clone(), + last_block.header().next_epoch_id().clone(), + None, + vec![], + Ratio::from_integer(0), + 0, + 100, + Some(0), + vec![], + vec![], + &*signer, + *last_block.header().next_bp_hash(), + CryptoHash::default(), + None, + ); + assert_matches!(chain.process_block_test(&None, block).unwrap_err(), Error::Orphan); + assert_matches!( + chain.process_block_test(&None, blocks.pop().unwrap()).unwrap_err(), + Error::Orphan + ); + assert_matches!( + chain.process_block_test(&None, blocks.pop().unwrap()).unwrap_err(), + Error::Orphan + ); + chain.process_block_test(&None, blocks.pop().unwrap()).unwrap(); + while wait_for_all_blocks_in_processing(&mut chain) { + chain.postprocess_ready_blocks( + &None, + &mut BlockProcessingArtifact::default(), + Arc::new(|_| {}), + ); + } + assert_eq!(chain.head().unwrap().height, 10); + assert_matches!( + chain.process_block_test(&None, blocks.pop().unwrap(),).unwrap_err(), + Error::BlockKnown(BlockKnownError::KnownInStore) + ); +} + +/// Checks that chain successfully processes blocks with skipped blocks and forks, but doesn't process block behind +/// final head. +#[test] +fn build_chain_with_skips_and_forks() { + init_test_logger(); + let (mut chain, _, _, signer) = setup(); + let genesis = chain.get_block(&chain.genesis().hash().clone()).unwrap(); + let b1 = TestBlockBuilder::new(&genesis, signer.clone()).build(); + let b2 = TestBlockBuilder::new(&genesis, signer.clone()).height(2).build(); + let b3 = TestBlockBuilder::new(&b1, signer.clone()).height(3).build(); + let b4 = TestBlockBuilder::new(&b2, signer.clone()).height(4).build(); + let b5 = TestBlockBuilder::new(&b4, signer.clone()).build(); + let b6 = TestBlockBuilder::new(&b5, signer.clone()).build(); + assert!(chain.process_block_test(&None, b1).is_ok()); + assert!(chain.process_block_test(&None, b2).is_ok()); + assert!(chain.process_block_test(&None, b3.clone()).is_ok()); + assert!(chain.process_block_test(&None, b4).is_ok()); + assert!(chain.process_block_test(&None, b5).is_ok()); + assert!(chain.process_block_test(&None, b6).is_ok()); + assert!(chain.get_block_header_by_height(1).is_err()); + assert_eq!(chain.get_block_header_by_height(5).unwrap().height(), 5); + assert_eq!(chain.get_block_header_by_height(6).unwrap().height(), 6); + + let c4 = TestBlockBuilder::new(&b3, signer).height(4).build(); + assert_eq!(chain.final_head().unwrap().height, 4); + assert_matches!(chain.process_block_test(&None, c4), Err(Error::CannotBeFinalized)); +} + +/// Verifies that getting block by its height are updated correctly when blocks from different forks are +/// processed, especially when certain heights are skipped. +/// Chain looks as follows (variable name + height): +/// +/// 0 -> b1 (c1) -> b2 +/// | \ \ +/// | \ -> d3 -------> d5 -> d6 +/// | \ +/// | ------> c3 -> c4 +/// | +/// ------------------------------------> e7 +/// +/// Note that only block b1 is finalized, so all blocks here can be processed. But getting block by height should +/// return only blocks from the canonical chain. +#[test] +fn blocks_at_height() { + init_test_logger(); + let (mut chain, _, _, signer) = setup(); + let genesis = chain.get_block_by_height(0).unwrap(); + let b_1 = TestBlockBuilder::new(&genesis, signer.clone()).height(1).build(); + + let b_2 = TestBlockBuilder::new(&b_1, signer.clone()).height(2).build(); + + let c_1 = TestBlockBuilder::new(&genesis, signer.clone()).height(1).build(); + let c_3 = TestBlockBuilder::new(&c_1, signer.clone()).height(3).build(); + let c_4 = TestBlockBuilder::new(&c_3, signer.clone()).height(4).build(); + + let d_3 = TestBlockBuilder::new(&b_2, signer.clone()).height(3).build(); + + let d_5 = TestBlockBuilder::new(&d_3, signer.clone()).height(5).build(); + let d_6 = TestBlockBuilder::new(&d_5, signer.clone()).height(6).build(); + + let e_7 = TestBlockBuilder::new(&b_1, signer).height(7).build(); + + let b_1_hash = *b_1.hash(); + let b_2_hash = *b_2.hash(); + + let c_1_hash = *c_1.hash(); + let c_3_hash = *c_3.hash(); + let c_4_hash = *c_4.hash(); + + let d_3_hash = *d_3.hash(); + let d_5_hash = *d_5.hash(); + let d_6_hash = *d_6.hash(); + + let e_7_hash = *e_7.hash(); + + assert_ne!(c_3_hash, d_3_hash); + + chain.process_block_test(&None, b_1).unwrap(); + chain.process_block_test(&None, b_2).unwrap(); + assert_eq!(chain.header_head().unwrap().height, 2); + + assert_eq!(chain.get_block_header_by_height(1).unwrap().hash(), &b_1_hash); + assert_eq!(chain.get_block_header_by_height(2).unwrap().hash(), &b_2_hash); + + chain.process_block_test(&None, c_1).unwrap(); + chain.process_block_test(&None, c_3).unwrap(); + chain.process_block_test(&None, c_4).unwrap(); + assert_eq!(chain.header_head().unwrap().height, 4); + + assert_eq!(chain.get_block_header_by_height(1).unwrap().hash(), &c_1_hash); + assert!(chain.get_block_header_by_height(2).is_err()); + assert_eq!(chain.get_block_header_by_height(3).unwrap().hash(), &c_3_hash); + assert_eq!(chain.get_block_header_by_height(4).unwrap().hash(), &c_4_hash); + + chain.process_block_test(&None, d_3).unwrap(); + chain.process_block_test(&None, d_5).unwrap(); + chain.process_block_test(&None, d_6).unwrap(); + assert_eq!(chain.header_head().unwrap().height, 6); + + assert_eq!(chain.get_block_header_by_height(1).unwrap().hash(), &b_1_hash); + assert_eq!(chain.get_block_header_by_height(2).unwrap().hash(), &b_2_hash); + assert_eq!(chain.get_block_header_by_height(3).unwrap().hash(), &d_3_hash); + assert!(chain.get_block_header_by_height(4).is_err()); + assert_eq!(chain.get_block_header_by_height(5).unwrap().hash(), &d_5_hash); + assert_eq!(chain.get_block_header_by_height(6).unwrap().hash(), &d_6_hash); + + chain.process_block_test(&None, e_7).unwrap(); + + assert_eq!(chain.get_block_header_by_height(1).unwrap().hash(), &b_1_hash); + for h in 2..=5 { + assert!(chain.get_block_header_by_height(h).is_err()); + } + assert_eq!(chain.get_block_header_by_height(7).unwrap().hash(), &e_7_hash); +} + +#[test] +fn next_blocks() { + init_test_logger(); + let (mut chain, _, _, signer) = setup(); + let genesis = chain.get_block(&chain.genesis().hash().clone()).unwrap(); + let b1 = TestBlockBuilder::new(&genesis, signer.clone()).build(); + let b2 = TestBlockBuilder::new(&b1, signer.clone()).height(2).build(); + let b3 = TestBlockBuilder::new(&b1, signer.clone()).height(3).build(); + let b4 = TestBlockBuilder::new(&b3, signer).height(4).build(); + let b1_hash = *b1.hash(); + let b2_hash = *b2.hash(); + let b3_hash = *b3.hash(); + let b4_hash = *b4.hash(); + assert!(chain.process_block_test(&None, b1).is_ok()); + assert!(chain.process_block_test(&None, b2).is_ok()); + assert_eq!(chain.mut_chain_store().get_next_block_hash(&b1_hash).unwrap(), b2_hash); + assert!(chain.process_block_test(&None, b3).is_ok()); + assert!(chain.process_block_test(&None, b4).is_ok()); + assert_eq!(chain.mut_chain_store().get_next_block_hash(&b1_hash).unwrap(), b3_hash); + assert_eq!(chain.mut_chain_store().get_next_block_hash(&b3_hash).unwrap(), b4_hash); +} diff --git a/chain/chain/src/tests/sync_chain.rs b/chain/chain/src/tests/sync_chain.rs new file mode 100644 index 000000000..e60519a9a --- /dev/null +++ b/chain/chain/src/tests/sync_chain.rs @@ -0,0 +1,30 @@ +use crate::test_utils::setup; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::merkle::PartialMerkleTree; +use unc_primitives::test_utils::TestBlockBuilder; + +#[test] +fn chain_sync_headers() { + init_test_logger(); + let (mut chain, _, _, bls_signer) = setup(); + assert_eq!(chain.header_head().unwrap().height, 0); + let mut blocks = vec![chain.get_block(&chain.genesis().hash().clone()).unwrap()]; + let mut block_merkle_tree = PartialMerkleTree::default(); + for i in 0..4 { + blocks.push( + TestBlockBuilder::new(&blocks[i], bls_signer.clone()) + .block_merkle_tree(&mut block_merkle_tree) + .build(), + ) + } + + let mut challenges = vec![]; + chain + .sync_block_headers( + blocks.drain(1..).map(|block| block.header().clone()).collect(), + &mut challenges, + ) + .unwrap(); + assert_eq!(chain.header_head().unwrap().height, 4); + assert!(challenges.is_empty()); +} diff --git a/chain/chain/src/types.rs b/chain/chain/src/types.rs new file mode 100644 index 000000000..9f394a33e --- /dev/null +++ b/chain/chain/src/types.rs @@ -0,0 +1,560 @@ +use std::collections::HashMap; +use std::time::Duration; + +use borsh::{BorshDeserialize, BorshSerialize}; +use chrono::DateTime; +use chrono::Utc; +use unc_chain_configs::MutableConfigValue; +use unc_chain_configs::ReshardingConfig; +use unc_primitives::sandbox::state_patch::SandboxStatePatch; +use unc_store::flat::FlatStorageManager; +use unc_store::StorageError; +use num_rational::Rational32; + +use unc_chain_configs::{Genesis, ProtocolConfig}; +use unc_chain_primitives::Error; +use unc_pool::types::PoolIterator; +use unc_primitives::challenge::ChallengesResult; +use unc_primitives::checked_feature; +use unc_primitives::errors::InvalidTxError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::{merklize, MerklePath}; +use unc_primitives::receipt::Receipt; +use unc_primitives::shard_layout::{ShardLayout, ShardUId}; +use unc_primitives::state_part::PartId; +use unc_primitives::transaction::{ExecutionOutcomeWithId, SignedTransaction}; +use unc_primitives::types::validator_power::{ValidatorPower, ValidatorPowerIter}; +use unc_primitives::types::{ + Balance, BlockHeight, BlockHeightDelta, EpochId, Gas, MerkleHash, NumBlocks, ShardId, + StateChangesForResharding, StateRoot, StateRootNode, +}; +use unc_primitives::version::{ + ProtocolVersion, MIN_GAS_PRICE_NEP_92, MIN_GAS_PRICE_NEP_92_FIX, MIN_PROTOCOL_VERSION_NEP_92, + MIN_PROTOCOL_VERSION_NEP_92_FIX, +}; +use unc_primitives::views::{QueryRequest, QueryResponse}; +use unc_store::{PartialStorage, ShardTries, Store, Trie, WrappedTrieChanges}; + +pub use unc_epoch_manager::EpochManagerAdapter; +pub use unc_primitives::block::{Block, BlockHeader, Tip}; +use unc_primitives::types::validator_frozen::{ValidatorFrozen, ValidatorFrozenIter}; + +#[derive(Eq, PartialEq, Debug, Clone)] +pub enum BlockStatus { + /// Block is the "next" block, updating the chain head. + Next, + /// Block does not update the chain head and is a fork. + Fork, + /// Block updates the chain head via a (potentially disruptive) "reorg". + /// Previous block was not our previous chain head. + Reorg(CryptoHash), +} + +impl BlockStatus { + pub fn is_new_head(&self) -> bool { + match self { + BlockStatus::Next => true, + BlockStatus::Fork => false, + BlockStatus::Reorg(_) => true, + } + } +} + +/// Options for block origin. +#[derive(Eq, PartialEq, Clone, Debug)] +pub enum Provenance { + /// No provenance. + NONE, + /// Adds block while in syncing mode. + SYNC, + /// Block we produced ourselves. + PRODUCED, +} + +/// Information about processed block. +#[derive(Debug, Clone)] +pub struct AcceptedBlock { + pub hash: CryptoHash, + pub status: BlockStatus, + pub provenance: Provenance, +} + +#[derive(Debug)] +pub struct ApplyResultForResharding { + pub shard_uid: ShardUId, + pub trie_changes: WrappedTrieChanges, + pub new_root: StateRoot, +} + +// ReshardingResults contains the results of applying depending on whether +// resharding is finished. +// If resharding is finished the results should be applied immediately. +// If resharding is not finished the results should be stored and applied later. +#[derive(Debug)] +pub enum ReshardingResults { + /// Immediately apply the resharding result. + /// Happens during IsCaughtUp and CatchingUp + ApplyReshardingResults(Vec), + /// Store the resharding results so that they can be applied later. + /// Happens during NotCaughtUp. + StoreReshardingResults(StateChangesForResharding), +} + +#[derive(Debug)] +pub struct ApplyChunkResult { + pub trie_changes: WrappedTrieChanges, + pub new_root: StateRoot, + pub outcomes: Vec, + pub outgoing_receipts: Vec, + pub validator_power_proposals: Vec, + pub validator_frozen_proposals: Vec, + pub total_gas_burnt: Gas, + pub total_balance_burnt: Balance, + pub proof: Option, + pub processed_delayed_receipts: Vec, +} + +impl ApplyChunkResult { + /// Returns root and paths for all the outcomes in the result. + pub fn compute_outcomes_proof( + outcomes: &[ExecutionOutcomeWithId], + ) -> (MerkleHash, Vec) { + let mut result = vec![]; + for outcome_with_id in outcomes.iter() { + result.push(outcome_with_id.to_hashes()); + } + merklize(&result) + } +} + +/// Block economics config taken from genesis config +pub struct BlockEconomicsConfig { + gas_price_adjustment_rate: Rational32, + genesis_min_gas_price: Balance, + genesis_max_gas_price: Balance, + genesis_protocol_version: ProtocolVersion, +} + +impl BlockEconomicsConfig { + /// Set max gas price to be this multiplier * min_gas_price + const MAX_GAS_MULTIPLIER: u128 = 20; + /// Compute min gas price according to protocol version and genesis protocol version. + /// + /// This returns the effective minimum gas price for a block with the given + /// protocol version. The base value is defined in genesis.config but has + /// been overwritten at specific protocol versions. Chains with a genesis + /// version higher than those changes are not overwritten and will instead + /// respect the value defined in genesis. + pub fn min_gas_price(&self, protocol_version: ProtocolVersion) -> Balance { + if self.genesis_protocol_version < MIN_PROTOCOL_VERSION_NEP_92 { + if protocol_version >= MIN_PROTOCOL_VERSION_NEP_92_FIX { + MIN_GAS_PRICE_NEP_92_FIX + } else if protocol_version >= MIN_PROTOCOL_VERSION_NEP_92 { + MIN_GAS_PRICE_NEP_92 + } else { + self.genesis_min_gas_price + } + } else if self.genesis_protocol_version < MIN_PROTOCOL_VERSION_NEP_92_FIX { + if protocol_version >= MIN_PROTOCOL_VERSION_NEP_92_FIX { + MIN_GAS_PRICE_NEP_92_FIX + } else { + MIN_GAS_PRICE_NEP_92 + } + } else { + self.genesis_min_gas_price + } + } + + pub fn max_gas_price(&self, protocol_version: ProtocolVersion) -> Balance { + if checked_feature!("stable", CapMaxGasPrice, protocol_version) { + std::cmp::min( + self.genesis_max_gas_price, + Self::MAX_GAS_MULTIPLIER * self.min_gas_price(protocol_version), + ) + } else { + self.genesis_max_gas_price + } + } + + pub fn gas_price_adjustment_rate(&self, _protocol_version: ProtocolVersion) -> Rational32 { + self.gas_price_adjustment_rate + } +} + +impl From<&ChainGenesis> for BlockEconomicsConfig { + fn from(chain_genesis: &ChainGenesis) -> Self { + BlockEconomicsConfig { + gas_price_adjustment_rate: chain_genesis.gas_price_adjustment_rate, + genesis_min_gas_price: chain_genesis.min_gas_price, + genesis_max_gas_price: chain_genesis.max_gas_price, + genesis_protocol_version: chain_genesis.protocol_version, + } + } +} + +/// Chain genesis configuration. +#[derive(Clone)] +pub struct ChainGenesis { + pub time: DateTime, + pub height: BlockHeight, + pub gas_limit: Gas, + pub min_gas_price: Balance, + pub max_gas_price: Balance, + pub total_supply: Balance, + pub gas_price_adjustment_rate: Rational32, + pub transaction_validity_period: NumBlocks, + pub epoch_length: BlockHeightDelta, + pub protocol_version: ProtocolVersion, +} + +#[derive(Clone)] +pub struct ChainConfig { + /// Whether to save `TrieChanges` on disk or not. + pub save_trie_changes: bool, + /// Number of threads to execute background migration work. + /// Currently used for flat storage background creation. + pub background_migration_threads: usize, + /// The resharding configuration. + pub resharding_config: MutableConfigValue, +} + +impl ChainConfig { + pub fn test() -> Self { + Self { + save_trie_changes: true, + background_migration_threads: 1, + resharding_config: MutableConfigValue::new( + ReshardingConfig::default(), + "resharding_config", + ), + } + } +} + +impl ChainGenesis { + pub fn new(genesis: &Genesis) -> Self { + Self { + time: genesis.config.genesis_time, + height: genesis.config.genesis_height, + gas_limit: genesis.config.gas_limit, + min_gas_price: genesis.config.min_gas_price, + max_gas_price: genesis.config.max_gas_price, + total_supply: genesis.config.total_supply, + gas_price_adjustment_rate: genesis.config.gas_price_adjustment_rate, + transaction_validity_period: genesis.config.transaction_validity_period, + epoch_length: genesis.config.epoch_length, + protocol_version: genesis.config.protocol_version, + } + } +} + +pub enum StorageDataSource { + /// Full state data is present in DB. + Db, + /// Trie is present in DB and flat storage is not. + /// Used for testing stateless validation jobs, should be removed after + /// stateless validation release. + DbTrieOnly, + /// State data is supplied from state witness, there is no state data + /// stored on disk. + Recorded(PartialStorage), +} + +pub struct RuntimeStorageConfig { + pub state_root: StateRoot, + pub use_flat_storage: bool, + pub source: StorageDataSource, + pub state_patch: SandboxStatePatch, + pub record_storage: bool, +} + +impl RuntimeStorageConfig { + pub fn new(state_root: StateRoot, use_flat_storage: bool) -> Self { + Self { + state_root, + use_flat_storage, + source: StorageDataSource::Db, + state_patch: Default::default(), + record_storage: false, + } + } +} + +#[derive(Clone)] +pub struct ApplyChunkBlockContext { + pub height: BlockHeight, + pub block_hash: CryptoHash, + pub prev_block_hash: CryptoHash, + pub block_timestamp: u64, + pub gas_price: Balance, + pub challenges_result: ChallengesResult, + pub random_seed: CryptoHash, +} + +impl ApplyChunkBlockContext { + pub fn from_header(header: &BlockHeader, gas_price: Balance) -> Self { + Self { + height: header.height(), + block_hash: *header.hash(), + prev_block_hash: *header.prev_hash(), + block_timestamp: header.raw_timestamp(), + gas_price, + challenges_result: header.challenges_result().clone(), + random_seed: *header.random_value(), + } + } +} + +pub struct ApplyChunkShardContext<'a> { + pub shard_id: ShardId, + pub last_validator_power_proposals: ValidatorPowerIter<'a>, + pub last_validator_frozen_proposals: ValidatorFrozenIter<'a>, + pub gas_limit: Gas, + pub is_new_chunk: bool, + pub is_first_block_with_chunk_of_version: bool, +} + +/// Bridge between the chain and the runtime. +/// Main function is to update state given transactions. +/// Additionally handles validators. +/// Naming note: `state_root` is a pre state root for block `block_hash` and a +/// post state root for block `prev_hash`. +pub trait RuntimeAdapter: Send + Sync { + fn get_tries(&self) -> ShardTries; + + fn store(&self) -> &Store; + + /// Returns trie with non-view cache for given `state_root`. + /// `prev_hash` is a block whose post state root is `state_root`, used to + /// access flat storage and to identify the epoch the given `shard_id` is at. + fn get_trie_for_shard( + &self, + shard_id: ShardId, + prev_hash: &CryptoHash, + state_root: StateRoot, + use_flat_storage: bool, + ) -> Result; + + /// Same as `get_trie_for_shard` but returns trie with view cache. + fn get_view_trie_for_shard( + &self, + shard_id: ShardId, + prev_hash: &CryptoHash, + state_root: StateRoot, + ) -> Result; + + fn get_flat_storage_manager(&self) -> FlatStorageManager; + + /// Validates a given signed transaction. + /// If the state root is given, then the verification will use the account. Otherwise it will + /// only validate the transaction math, limits and signatures. + /// Returns an option of `InvalidTxError`, it contains `Some(InvalidTxError)` if there is + /// a validation error, or `None` in case the transaction succeeded. + /// Throws an `Error` with `ErrorKind::StorageError` in case the runtime throws + /// `RuntimeError::StorageError`. + fn validate_tx( + &self, + gas_price: Balance, + state_root: Option, + transaction: &SignedTransaction, + verify_signature: bool, + epoch_id: &EpochId, + current_protocol_version: ProtocolVersion, + ) -> Result, Error>; + + /// Returns an ordered list of valid transactions from the pool up the given limits. + /// Pulls transactions from the given pool iterators one by one. Validates each transaction + /// against the given `chain_validate` closure and runtime's transaction verifier. + /// If the transaction is valid for both, it's added to the result and the temporary state + /// update is preserved for validation of next transactions. + /// Throws an `Error` with `ErrorKind::StorageError` in case the runtime throws + /// `RuntimeError::StorageError`. + fn prepare_transactions( + &self, + gas_price: Balance, + gas_limit: Gas, + epoch_id: &EpochId, + shard_id: ShardId, + state_root: StateRoot, + next_block_height: BlockHeight, + pool_iterator: &mut dyn PoolIterator, + chain_validate: &mut dyn FnMut(&SignedTransaction) -> bool, + current_protocol_version: ProtocolVersion, + time_limit: Option, + ) -> Result, Error>; + + /// Returns true if the shard layout will change in the next epoch + /// Current epoch is the epoch of the block after `parent_hash` + fn will_shard_layout_change_next_epoch(&self, parent_hash: &CryptoHash) -> Result; + + /// Get the block height for which garbage collection should not go over + fn get_gc_stop_height(&self, block_hash: &CryptoHash) -> BlockHeight; + + /// Apply transactions and receipts to given state root and return store update + /// and new state root. + /// Also returns transaction result for each transaction and new receipts. + fn apply_chunk( + &self, + storage: RuntimeStorageConfig, + chunk: ApplyChunkShardContext, + block: ApplyChunkBlockContext, + receipts: &[Receipt], + transactions: &[SignedTransaction], + ) -> Result; + + /// Query runtime with given `path` and `data`. + fn query( + &self, + shard_uid: ShardUId, + state_root: &StateRoot, + block_height: BlockHeight, + block_timestamp: u64, + prev_block_hash: &CryptoHash, + block_hash: &CryptoHash, + epoch_id: &EpochId, + request: &QueryRequest, + ) -> Result; + + /// Get part of the state corresponding to the given state root. + /// `prev_hash` is a block whose post state root is `state_root`. + /// Returns error when storage is inconsistent. + fn obtain_state_part( + &self, + shard_id: ShardId, + prev_hash: &CryptoHash, + state_root: &StateRoot, + part_id: PartId, + ) -> Result, Error>; + + /// Validate state part that expected to be given state root with provided data. + /// Returns false if the resulting part doesn't match the expected one. + fn validate_state_part(&self, state_root: &StateRoot, part_id: PartId, data: &[u8]) -> bool; + + fn apply_update_to_children_states( + &self, + block_hash: &CryptoHash, + block_height: BlockHeight, + state_roots: HashMap, + next_shard_layout: &ShardLayout, + state_changes: StateChangesForResharding, + ) -> Result, Error>; + + /// Should be executed after accepting all the parts to set up a new state. + fn apply_state_part( + &self, + shard_id: ShardId, + state_root: &StateRoot, + part_id: PartId, + part: &[u8], + epoch_id: &EpochId, + ) -> Result<(), Error>; + + /// Returns StateRootNode of a state. + /// `block_hash` is a block whose `prev_state_root` is `state_root` + /// Panics if requested hash is not in storage. + /// Never returns Error + fn get_state_root_node( + &self, + shard_id: ShardId, + block_hash: &CryptoHash, + state_root: &StateRoot, + ) -> Result; + + /// Validate StateRootNode of a state. + fn validate_state_root_node( + &self, + state_root_node: &StateRootNode, + state_root: &StateRoot, + ) -> bool; + + fn get_protocol_config(&self, epoch_id: &EpochId) -> Result; + + /// Loads in-memory tries upon startup. The given shard_uids are possible candidates to load, + /// but which exact shards to load depends on configuration. This may only be called when flat + /// storage is ready. + fn load_mem_tries_on_startup(&self, shard_uids: &[ShardUId]) -> Result<(), StorageError>; +} + +/// The last known / checked height and time when we have processed it. +/// Required to keep track of skipped blocks and not fallback to produce blocks at lower height. +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Default)] +pub struct LatestKnown { + pub height: BlockHeight, + pub seen: u64, +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use chrono::Utc; + use unc_primitives::test_utils::{create_test_signer, TestBlockBuilder}; + + use unc_primitives::block::{genesis_chunks, Approval}; + use unc_primitives::hash::hash; + use unc_primitives::merkle::verify_path; + use unc_primitives::transaction::{ExecutionMetadata, ExecutionOutcome, ExecutionStatus}; + use unc_primitives::version::PROTOCOL_VERSION; + + use super::*; + + #[test] + fn test_block_produce() { + let shard_ids: Vec<_> = (0..32).collect(); + let genesis_chunks = + genesis_chunks(vec![Trie::EMPTY_ROOT], &shard_ids, 1_000_000, 0, PROTOCOL_VERSION); + let genesis_bps: Vec = Vec::new(); + let genesis = Block::genesis( + PROTOCOL_VERSION, + genesis_chunks.into_iter().map(|chunk| chunk.take_header()).collect(), + Utc::now(), + 0, + 100, + 1_000_000_000, + CryptoHash::hash_borsh(genesis_bps), + ); + let signer = Arc::new(create_test_signer("other")); + let b1 = TestBlockBuilder::new(&genesis, signer.clone()).build(); + assert!(b1.header().verify_block_producer(&signer.public_key())); + let other_signer = create_test_signer("other2"); + let approvals = + vec![Some(Box::new(Approval::new(*b1.hash(), 1, 2, &other_signer).signature))]; + let b2 = TestBlockBuilder::new(&b1, signer.clone()).approvals(approvals).build(); + b2.header().verify_block_producer(&signer.public_key()); + } + + #[test] + fn test_execution_outcome_merklization() { + let outcome1 = ExecutionOutcomeWithId { + id: Default::default(), + outcome: ExecutionOutcome { + status: ExecutionStatus::Unknown, + logs: vec!["outcome1".to_string()], + receipt_ids: vec![hash(&[1])], + gas_burnt: 100, + compute_usage: Some(200), + tokens_burnt: 10000, + executor_id: "alice".parse().unwrap(), + metadata: ExecutionMetadata::V1, + }, + }; + let outcome2 = ExecutionOutcomeWithId { + id: Default::default(), + outcome: ExecutionOutcome { + status: ExecutionStatus::SuccessValue(vec![1]), + logs: vec!["outcome2".to_string()], + receipt_ids: vec![], + gas_burnt: 0, + compute_usage: Some(0), + tokens_burnt: 0, + executor_id: "bob".parse().unwrap(), + metadata: ExecutionMetadata::V1, + }, + }; + let outcomes = vec![outcome1, outcome2]; + let (outcome_root, paths) = ApplyChunkResult::compute_outcomes_proof(&outcomes); + for (outcome_with_id, path) in outcomes.into_iter().zip(paths.into_iter()) { + assert!(verify_path(outcome_root, &path, &outcome_with_id.to_hashes())); + } + } +} diff --git a/chain/chain/src/update_shard.rs b/chain/chain/src/update_shard.rs new file mode 100644 index 000000000..bb8f63b76 --- /dev/null +++ b/chain/chain/src/update_shard.rs @@ -0,0 +1,391 @@ +use crate::crypto_hash_timer::CryptoHashTimer; +use crate::types::{ + ApplyChunkBlockContext, ApplyChunkResult, ApplyChunkShardContext, ApplyResultForResharding, + ReshardingResults, RuntimeAdapter, RuntimeStorageConfig, StorageDataSource, +}; +use unc_chain_primitives::Error; +use unc_epoch_manager::EpochManagerAdapter; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::Receipt; +use unc_primitives::sandbox::state_patch::SandboxStatePatch; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::sharding::ShardChunk; +use unc_primitives::types::chunk_extra::ChunkExtra; +use unc_primitives::types::{BlockHeight, Gas, StateChangesForResharding, StateRoot}; +use std::collections::HashMap; + +/// Result of updating a shard for some block when it has a new chunk for this +/// shard. +#[derive(Debug)] +pub struct NewChunkResult { + pub(crate) shard_uid: ShardUId, + pub(crate) gas_limit: Gas, + pub(crate) apply_result: ApplyChunkResult, + pub(crate) resharding_results: Option, +} + +/// Result of updating a shard for some block when it doesn't have a new chunk +/// for this shard, so previous chunk header is copied. +#[derive(Debug)] +pub struct OldChunkResult { + pub(crate) shard_uid: ShardUId, + /// Note that despite the naming, no transactions are applied in this case. + /// TODO(logunov): exclude receipts/txs context from all related types. + pub(crate) apply_result: ApplyChunkResult, + pub(crate) resharding_results: Option, +} + +/// Result of updating a shard for some block when we apply only resharding +/// changes due to resharding. +#[derive(Debug)] +pub struct ReshardingResult { + // parent shard of the + pub(crate) shard_uid: ShardUId, + pub(crate) results: Vec, +} + +/// Result of processing shard update, covering both stateful and stateless scenarios. +#[derive(Debug)] +pub enum ShardUpdateResult { + /// Stateful scenario - processed update for a single block. + Stateful(ShardBlockUpdateResult), + /// Stateless scenario - processed update based on state witness in a chunk. + /// Contains `ChunkExtra`s - results for processing updates corresponding + /// to state witness. + Stateless(Vec<(CryptoHash, ShardUId, ChunkExtra)>), +} + +/// Result for a shard update for a single block. +#[derive(Debug)] +pub enum ShardBlockUpdateResult { + NewChunk(NewChunkResult), + OldChunk(OldChunkResult), + Resharding(ReshardingResult), +} + +/// State roots of children shards which are ready. +type ReshardingStateRoots = HashMap; + +pub(crate) struct NewChunkData { + pub chunk: ShardChunk, + pub receipts: Vec, + pub resharding_state_roots: Option, + pub block: ApplyChunkBlockContext, + pub is_first_block_with_chunk_of_version: bool, + pub storage_context: StorageContext, +} + +pub(crate) struct OldChunkData { + pub prev_chunk_extra: ChunkExtra, + pub resharding_state_roots: Option, + pub block: ApplyChunkBlockContext, + pub storage_context: StorageContext, +} + +pub(crate) struct ReshardingData { + pub resharding_state_roots: ReshardingStateRoots, + pub state_changes: StateChangesForResharding, + pub block_height: BlockHeight, + pub block_hash: CryptoHash, +} + +/// Reason to update a shard when new block appears on chain. +/// All types include state roots for children shards in case of resharding. +#[allow(clippy::large_enum_variant)] +pub(crate) enum ShardUpdateReason { + /// Block has a new chunk for the shard. + /// Contains chunk itself and all new incoming receipts to the shard. + NewChunk(NewChunkData), + /// Block doesn't have a new chunk for the shard. + /// Instead, previous chunk header is copied. + /// Contains result of shard update for previous block. + OldChunk(OldChunkData), + /// See comment to `resharding_state_roots` in `Chain::get_update_shard_job`. + /// Process only state changes caused by resharding. + Resharding(ReshardingData), +} + +/// Information about shard to update. +pub(crate) struct ShardContext { + pub shard_uid: ShardUId, + /// Whether node cares about shard in this epoch. + pub cares_about_shard_this_epoch: bool, + /// Whether shard layout changes in the next epoch. + pub will_shard_layout_change: bool, + /// Whether transactions should be applied. + pub should_apply_chunk: bool, + /// See comment in `get_update_shard_job`. + pub need_to_reshard: bool, +} + +/// Information about storage used for applying txs and receipts. +pub(crate) struct StorageContext { + /// Data source used for processing shard update. + pub storage_data_source: StorageDataSource, + pub state_patch: SandboxStatePatch, +} + +/// Processes shard update with given block and shard. +/// Doesn't modify chain, only produces result to be applied later. +pub(crate) fn process_shard_update( + parent_span: &tracing::Span, + runtime: &dyn RuntimeAdapter, + epoch_manager: &dyn EpochManagerAdapter, + shard_update_reason: ShardUpdateReason, + shard_context: ShardContext, +) -> Result { + Ok(match shard_update_reason { + ShardUpdateReason::NewChunk(data) => ShardBlockUpdateResult::NewChunk(apply_new_chunk( + parent_span, + data, + shard_context, + runtime, + epoch_manager, + )?), + ShardUpdateReason::OldChunk(data) => ShardBlockUpdateResult::OldChunk(apply_old_chunk( + parent_span, + data, + shard_context, + runtime, + epoch_manager, + )?), + ShardUpdateReason::Resharding(data) => ShardBlockUpdateResult::Resharding( + apply_resharding(parent_span, data, shard_context.shard_uid, runtime, epoch_manager)?, + ), + }) +} + +/// Processes shard updates for the execution contexts range which must +/// correspond to missing chunks for some shard. +/// `current_chunk_extra` must correspond to `ChunkExtra` just before +/// execution; in the end it will correspond to the latest execution +/// result. +pub(crate) fn process_missing_chunks_range( + parent_span: &tracing::Span, + mut current_chunk_extra: ChunkExtra, + runtime: &dyn RuntimeAdapter, + epoch_manager: &dyn EpochManagerAdapter, + execution_contexts: Vec<(ApplyChunkBlockContext, ShardContext)>, +) -> Result, Error> { + let mut result = vec![]; + for (block_context, shard_context) in execution_contexts { + let OldChunkResult { shard_uid, apply_result, resharding_results: _ } = apply_old_chunk( + parent_span, + OldChunkData { + block: block_context.clone(), + resharding_state_roots: None, + prev_chunk_extra: current_chunk_extra.clone(), + storage_context: StorageContext { + storage_data_source: StorageDataSource::DbTrieOnly, + state_patch: Default::default(), + }, + }, + shard_context, + runtime, + epoch_manager, + )?; + *current_chunk_extra.state_root_mut() = apply_result.new_root; + result.push((block_context.block_hash, shard_uid, current_chunk_extra.clone())); + } + Ok(result) +} +/// Applies new chunk, which includes applying transactions from chunk and +/// receipts filtered from outgoing receipts from previous chunks. +pub(crate) fn apply_new_chunk( + parent_span: &tracing::Span, + data: NewChunkData, + shard_context: ShardContext, + runtime: &dyn RuntimeAdapter, + epoch_manager: &dyn EpochManagerAdapter, +) -> Result { + let NewChunkData { + block, + chunk, + receipts, + resharding_state_roots, + is_first_block_with_chunk_of_version, + storage_context, + } = data; + let shard_id = shard_context.shard_uid.shard_id(); + let _span = tracing::debug_span!( + target: "chain", + parent: parent_span, + "new_chunk", + shard_id) + .entered(); + let chunk_inner = chunk.cloned_header().take_inner(); + let gas_limit = chunk_inner.gas_limit(); + + let _timer = CryptoHashTimer::new(chunk.chunk_hash().0); + let storage_config = RuntimeStorageConfig { + state_root: *chunk_inner.prev_state_root(), + use_flat_storage: true, + source: storage_context.storage_data_source, + state_patch: storage_context.state_patch, + record_storage: false, + }; + match runtime.apply_chunk( + storage_config, + ApplyChunkShardContext { + shard_id, + last_validator_power_proposals: chunk_inner.prev_validator_power_proposals(), + last_validator_frozen_proposals: chunk_inner.prev_validator_frozen_proposals(), + gas_limit, + is_new_chunk: true, + is_first_block_with_chunk_of_version, + }, + block.clone(), + &receipts, + chunk.transactions(), + ) { + Ok(apply_result) => { + let apply_split_result_or_state_changes = if shard_context.will_shard_layout_change { + Some(apply_resharding_state_changes( + epoch_manager, + runtime, + block, + &apply_result, + resharding_state_roots, + )?) + } else { + None + }; + Ok(NewChunkResult { + gas_limit, + shard_uid: shard_context.shard_uid, + apply_result, + resharding_results: apply_split_result_or_state_changes, + }) + } + Err(err) => Err(err), + } +} + +/// Applies shard update corresponding to missing chunk. +/// (logunov) From what I know, the state update may include only validator +/// accounts update on epoch start. +fn apply_old_chunk( + parent_span: &tracing::Span, + data: OldChunkData, + shard_context: ShardContext, + runtime: &dyn RuntimeAdapter, + epoch_manager: &dyn EpochManagerAdapter, +) -> Result { + let OldChunkData { prev_chunk_extra, resharding_state_roots, block, storage_context } = data; + let shard_id = shard_context.shard_uid.shard_id(); + let _span = tracing::debug_span!( + target: "chain", + parent: parent_span, + "existing_chunk", + shard_id) + .entered(); + + let storage_config = RuntimeStorageConfig { + state_root: *prev_chunk_extra.state_root(), + use_flat_storage: true, + source: storage_context.storage_data_source, + state_patch: storage_context.state_patch, + record_storage: false, + }; + match runtime.apply_chunk( + storage_config, + ApplyChunkShardContext { + shard_id, + last_validator_power_proposals: prev_chunk_extra.validator_power_proposals(), + last_validator_frozen_proposals: prev_chunk_extra.validator_frozen_proposals(), + gas_limit: prev_chunk_extra.gas_limit(), + is_new_chunk: false, + is_first_block_with_chunk_of_version: false, + }, + block.clone(), + &[], + &[], + ) { + Ok(apply_result) => { + let apply_split_result_or_state_changes = if shard_context.will_shard_layout_change { + Some(apply_resharding_state_changes( + epoch_manager, + runtime, + block, + &apply_result, + resharding_state_roots, + )?) + } else { + None + }; + Ok(OldChunkResult { + shard_uid: shard_context.shard_uid, + apply_result, + resharding_results: apply_split_result_or_state_changes, + }) + } + Err(err) => Err(err), + } +} + +/// Applies only resharding changes but not applies any transactions. +fn apply_resharding( + parent_span: &tracing::Span, + data: ReshardingData, + shard_uid: ShardUId, + runtime: &dyn RuntimeAdapter, + epoch_manager: &dyn EpochManagerAdapter, +) -> Result { + let ReshardingData { resharding_state_roots, state_changes, block_height: height, block_hash } = + data; + let shard_id = shard_uid.shard_id(); + let _span = tracing::debug_span!( + target: "chain", + parent: parent_span, + "resharding", + shard_id, + ?shard_uid) + .entered(); + let next_epoch_id = epoch_manager.get_next_epoch_id(&block_hash)?; + let next_epoch_shard_layout = epoch_manager.get_shard_layout(&next_epoch_id)?; + let results = runtime.apply_update_to_children_states( + &block_hash, + height, + resharding_state_roots, + &next_epoch_shard_layout, + state_changes, + )?; + Ok(ReshardingResult { shard_uid, results }) +} + +/// Process ApplyChunkResult to apply changes to children shards. When +/// shards will change next epoch, +/// - if `resharding_state_roots` is not None, that means states for the +/// children shards are ready this function updates these states and returns +/// apply results for these states +/// - otherwise, this function returns state changes needed to be applied to +/// children shards. These state changes will be stored in the database by +/// `process_resharding_results` +fn apply_resharding_state_changes( + epoch_manager: &dyn EpochManagerAdapter, + runtime_adapter: &dyn RuntimeAdapter, + block: ApplyChunkBlockContext, + apply_result: &ApplyChunkResult, + resharding_state_roots: Option, +) -> Result { + let state_changes = StateChangesForResharding::from_raw_state_changes( + apply_result.trie_changes.state_changes(), + apply_result.processed_delayed_receipts.clone(), + ); + let next_epoch_id = epoch_manager.get_next_epoch_id_from_prev_block(&block.prev_block_hash)?; + let next_shard_layout = epoch_manager.get_shard_layout(&next_epoch_id)?; + if let Some(state_roots) = resharding_state_roots { + // children states are ready, apply update to them now + let resharding_results = runtime_adapter.apply_update_to_children_states( + &block.block_hash, + block.height, + state_roots, + &next_shard_layout, + state_changes, + )?; + Ok(ReshardingResults::ApplyReshardingResults(resharding_results)) + } else { + // children states are not ready yet, store state changes in consolidated_state_changes + Ok(ReshardingResults::StoreReshardingResults(state_changes)) + } +} diff --git a/chain/chain/src/validate.rs b/chain/chain/src/validate.rs new file mode 100644 index 000000000..05a95b620 --- /dev/null +++ b/chain/chain/src/validate.rs @@ -0,0 +1,505 @@ +use std::collections::HashMap; + +use borsh::BorshDeserialize; + +use unc_crypto::PublicKey; +use unc_epoch_manager::EpochManagerAdapter; +use unc_primitives::block::{Block, BlockHeader}; +use unc_primitives::challenge::{ + BlockDoubleSign, Challenge, ChallengeBody, ChunkProofs, ChunkState, MaybeEncodedShardChunk, +}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::merklize; +use unc_primitives::sharding::{ShardChunk, ShardChunkHeader}; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::chunk_extra::ChunkExtra; +use unc_primitives::types::{AccountId, BlockHeight, EpochId, Nonce}; + +use crate::types::RuntimeAdapter; +use crate::{byzantine_assert, Chain}; +use crate::{ChainStore, Error}; + +/// Gas limit cannot be adjusted for more than 0.1% at a time. +const GAS_LIMIT_ADJUSTMENT_FACTOR: u64 = 1000; + +/// Verifies that chunk's proofs in the header match the body. +pub fn validate_chunk_proofs( + chunk: &ShardChunk, + epoch_manager: &dyn EpochManagerAdapter, +) -> Result { + let correct_chunk_hash = chunk.compute_header_hash(); + + // 1. Checking chunk.header.hash + let header_hash = chunk.header_hash(); + if header_hash != correct_chunk_hash { + byzantine_assert!(false); + return Ok(false); + } + + // 2. Checking that chunk body is valid + // 2a. Checking chunk hash + if chunk.chunk_hash() != correct_chunk_hash { + byzantine_assert!(false); + return Ok(false); + } + let height_created = chunk.height_created(); + let outgoing_receipts_root = chunk.prev_outgoing_receipts_root(); + let (transactions, receipts) = (chunk.transactions(), chunk.prev_outgoing_receipts()); + + // 2b. Checking that chunk transactions are valid + let (tx_root, _) = merklize(transactions); + if tx_root != chunk.tx_root() { + byzantine_assert!(false); + return Ok(false); + } + // 2c. Checking that chunk receipts are valid + if height_created == 0 { + return Ok(receipts.is_empty() && outgoing_receipts_root == CryptoHash::default()); + } else { + let shard_layout = { + let prev_block_hash = chunk.prev_block_hash(); + epoch_manager.get_shard_layout_from_prev_block(&prev_block_hash)? + }; + let outgoing_receipts_hashes = Chain::build_receipts_hashes(receipts, &shard_layout); + let (receipts_root, _) = merklize(&outgoing_receipts_hashes); + if receipts_root != outgoing_receipts_root { + byzantine_assert!(false); + return Ok(false); + } + } + Ok(true) +} + +/// Validates that the given transactions are in proper valid order. +/// See +pub fn validate_transactions_order(transactions: &[SignedTransaction]) -> bool { + let mut nonces: HashMap<(&AccountId, &PublicKey), Nonce> = HashMap::new(); + let mut batches: HashMap<(&AccountId, &PublicKey), usize> = HashMap::new(); + let mut current_batch = 1; + + for tx in transactions { + let key = (&tx.transaction.signer_id, &tx.transaction.public_key); + + // Verifying nonce + let nonce = tx.transaction.nonce; + if let Some(last_nonce) = nonces.get(&key) { + if nonce <= *last_nonce { + // Nonces should increase. + return false; + } + } + nonces.insert(key, nonce); + + // Verifying batch + let last_batch = *batches.get(&key).unwrap_or(&0); + if last_batch == current_batch { + current_batch += 1; + } else if last_batch < current_batch - 1 { + // The key was skipped in the previous batch + return false; + } + batches.insert(key, current_batch); + } + true +} + +/// Validate that all next chunk information matches previous chunk extra. +pub fn validate_chunk_with_chunk_extra( + chain_store: &ChainStore, + epoch_manager: &dyn EpochManagerAdapter, + prev_block_hash: &CryptoHash, + prev_chunk_extra: &ChunkExtra, + prev_chunk_height_included: BlockHeight, + chunk_header: &ShardChunkHeader, +) -> Result<(), Error> { + let outgoing_receipts = chain_store.get_outgoing_receipts_for_shard( + epoch_manager, + *prev_block_hash, + chunk_header.shard_id(), + prev_chunk_height_included, + )?; + let outgoing_receipts_hashes = { + let shard_layout = epoch_manager.get_shard_layout_from_prev_block(prev_block_hash)?; + Chain::build_receipts_hashes(&outgoing_receipts, &shard_layout) + }; + let (outgoing_receipts_root, _) = merklize(&outgoing_receipts_hashes); + + validate_chunk_with_chunk_extra_and_receipts_root( + prev_chunk_extra, + chunk_header, + &outgoing_receipts_root, + ) +} + +/// Validate that all next chunk information matches previous chunk extra. +pub fn validate_chunk_with_chunk_extra_and_receipts_root( + prev_chunk_extra: &ChunkExtra, + chunk_header: &ShardChunkHeader, + outgoing_receipts_root: &CryptoHash, +) -> Result<(), Error> { + if *prev_chunk_extra.state_root() != chunk_header.prev_state_root() { + return Err(Error::InvalidStateRoot); + } + + if *prev_chunk_extra.outcome_root() != chunk_header.prev_outcome_root() { + return Err(Error::InvalidOutcomesProof); + } + + let chunk_extra_power_proposals = prev_chunk_extra.validator_power_proposals(); + let chunk_extra_frozen_proposals = prev_chunk_extra.validator_frozen_proposals(); + + let chunk_header_power_proposals = chunk_header.prev_validator_power_proposals(); + let chunk_header_frozen_proposals = chunk_header.prev_validator_frozen_proposals(); + + if chunk_header_power_proposals.len() != chunk_extra_power_proposals.len() + || !chunk_extra_power_proposals.eq(chunk_header_power_proposals) + || chunk_header_frozen_proposals.len() != chunk_extra_frozen_proposals.len() + || !chunk_extra_frozen_proposals.eq(chunk_header_frozen_proposals) + { + return Err(Error::InvalidValidatorProposals); + } + + if prev_chunk_extra.gas_limit() != chunk_header.gas_limit() { + return Err(Error::InvalidGasLimit); + } + + if prev_chunk_extra.gas_used() != chunk_header.prev_gas_used() { + return Err(Error::InvalidGasUsed); + } + + if prev_chunk_extra.balance_burnt() != chunk_header.prev_balance_burnt() { + return Err(Error::InvalidBalanceBurnt); + } + + if outgoing_receipts_root != &chunk_header.prev_outgoing_receipts_root() { + return Err(Error::InvalidReceiptsProof); + } + + let gas_limit = prev_chunk_extra.gas_limit(); + if chunk_header.gas_limit() < gas_limit - gas_limit / GAS_LIMIT_ADJUSTMENT_FACTOR + || chunk_header.gas_limit() > gas_limit + gas_limit / GAS_LIMIT_ADJUSTMENT_FACTOR + { + return Err(Error::InvalidGasLimit); + } + + Ok(()) +} + +/// Validates a double sign challenge. +/// Only valid if ancestors of both blocks are present in the chain. +fn validate_double_sign( + epoch_manager: &dyn EpochManagerAdapter, + block_double_sign: &BlockDoubleSign, +) -> Result<(CryptoHash, Vec), Error> { + let left_block_header = BlockHeader::try_from_slice(&block_double_sign.left_block_header)?; + let right_block_header = BlockHeader::try_from_slice(&block_double_sign.right_block_header)?; + let block_producer = epoch_manager + .get_block_producer(left_block_header.epoch_id(), left_block_header.height())?; + if left_block_header.hash() != right_block_header.hash() + && left_block_header.height() == right_block_header.height() + && epoch_manager.verify_validator_signature( + left_block_header.epoch_id(), + left_block_header.prev_hash(), + &block_producer, + left_block_header.hash().as_ref(), + left_block_header.signature(), + )? + && epoch_manager.verify_validator_signature( + right_block_header.epoch_id(), + right_block_header.prev_hash(), + &block_producer, + right_block_header.hash().as_ref(), + right_block_header.signature(), + )? + { + // Deterministically return header with higher hash. + Ok(if left_block_header.hash() > right_block_header.hash() { + (*left_block_header.hash(), vec![block_producer]) + } else { + (*right_block_header.hash(), vec![block_producer]) + }) + } else { + Err(Error::MaliciousChallenge) + } +} + +fn validate_header_authorship( + epoch_manager: &dyn EpochManagerAdapter, + block_header: &BlockHeader, +) -> Result<(), Error> { + if epoch_manager.verify_header_signature(block_header)? { + Ok(()) + } else { + Err(Error::InvalidChallenge) + } +} + +fn validate_chunk_authorship( + epoch_manager: &dyn EpochManagerAdapter, + chunk_header: &ShardChunkHeader, +) -> Result { + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(&chunk_header.prev_block_hash())?; + if epoch_manager.verify_chunk_header_signature( + chunk_header, + &epoch_id, + &chunk_header.prev_block_hash(), + )? { + let chunk_producer = epoch_manager.get_chunk_producer( + &epoch_id, + chunk_header.height_created(), + chunk_header.shard_id(), + )?; + Ok(chunk_producer) + } else { + Err(Error::InvalidChallenge) + } +} + +fn validate_chunk_proofs_challenge( + epoch_manager: &dyn EpochManagerAdapter, + chunk_proofs: &ChunkProofs, +) -> Result<(CryptoHash, Vec), Error> { + let block_header = BlockHeader::try_from_slice(&chunk_proofs.block_header)?; + validate_header_authorship(epoch_manager, &block_header)?; + let chunk_header = match &*chunk_proofs.chunk { + MaybeEncodedShardChunk::Encoded(encoded_chunk) => encoded_chunk.cloned_header(), + MaybeEncodedShardChunk::Decoded(chunk) => chunk.cloned_header(), + }; + let chunk_producer = validate_chunk_authorship(epoch_manager, &chunk_header)?; + let account_to_slash_for_valid_challenge = Ok((*block_header.hash(), vec![chunk_producer])); + if !Block::validate_chunk_header_proof( + &chunk_header, + block_header.chunk_headers_root(), + &chunk_proofs.merkle_proof, + ) { + // Merkle proof is invalid. It's a malicious challenge. + return Err(Error::MaliciousChallenge); + } + // Temporary holds the decoded chunk, since we use a reference below to avoid cloning it. + let tmp_chunk; + let chunk_ref = match &*chunk_proofs.chunk { + MaybeEncodedShardChunk::Encoded(encoded_chunk) => { + match encoded_chunk.decode_chunk(epoch_manager.num_data_parts()) { + Ok(chunk) => { + tmp_chunk = Some(chunk); + tmp_chunk.as_ref().unwrap() + } + Err(_) => { + // Chunk can't be decoded. Good challenge. + return account_to_slash_for_valid_challenge; + } + } + } + MaybeEncodedShardChunk::Decoded(chunk) => chunk, + }; + + if !validate_chunk_proofs(chunk_ref, epoch_manager)? { + // Chunk proofs are invalid. Good challenge. + return account_to_slash_for_valid_challenge; + } + + if !validate_transactions_order(chunk_ref.transactions()) { + // Chunk transactions are invalid. Good challenge. + return account_to_slash_for_valid_challenge; + } + + // The chunk is fine. It's a malicious challenge. + return Err(Error::MaliciousChallenge); +} + +fn validate_chunk_state_challenge( + _runtime: &dyn RuntimeAdapter, + _chunk_state: &ChunkState, +) -> Result<(CryptoHash, Vec), Error> { + // TODO (#2445): Enable challenges when they are working correctly. + // let prev_block_header = BlockHeader::try_from_slice(&chunk_state.prev_block_header)?; + // let block_header = BlockHeader::try_from_slice(&chunk_state.block_header)?; + + // // Validate previous chunk and block header. + // validate_header_authorship(runtime_adapter, &prev_block_header)?; + // let prev_chunk_header = chunk_state.prev_chunk.cloned_header(); + // let _ = validate_chunk_authorship(runtime_adapter, &prev_chunk_header)?; + // if !Block::validate_chunk_header_proof( + // &prev_chunk_header, + // prev_block_header.chunk_headers_root(), + // &chunk_state.prev_merkle_proof, + // ) { + // return Err(ErrorKind::MaliciousChallenge.into()); + // } + // + // // Validate current chunk and block header. + // validate_header_authorship(runtime_adapter, &block_header)?; + // let chunk_producer = validate_chunk_authorship(runtime_adapter, &chunk_state.chunk_header)?; + // if !Block::validate_chunk_header_proof( + // &chunk_state.chunk_header, + // block_header.chunk_headers_root(), + // &chunk_state.merkle_proof, + // ) { + // return Err(ErrorKind::MaliciousChallenge.into()); + // } + + // Apply state transition and check that the result state and other data doesn't match. + // TODO (#6316): enable storage proof generation + // let partial_storage = PartialStorage { nodes: chunk_state.partial_state.clone() }; + // let result = runtime + // .check_state_transition( + // partial_storage, + // prev_chunk_header.shard_id(), + // &prev_chunk_header.prev_state_root(), + // block_header.height(), + // block_header.raw_timestamp(), + // block_header.prev_hash(), + // block_header.hash(), + // chunk_state.prev_chunk.receipts(), + // chunk_state.prev_chunk.transactions(), + // ValidatorStakeIter::empty(), + // prev_block_header.gas_price(), + // prev_chunk_header.gas_limit(), + // &ChallengesResult::default(), + // *block_header.random_value(), + // // TODO: set it properly when challenges are enabled + // true, + // false, + // ) + // .map_err(|_| Error::from(ErrorKind::MaliciousChallenge))?; + // let outcome_root = ApplyTransactionResult::compute_outcomes_proof(&result.outcomes).0; + // let proposals_match = result.validator_proposals.len() + // == chunk_state.chunk_header.validator_proposals().len() + // && result + // .validator_proposals + // .iter() + // .zip(chunk_state.chunk_header.validator_proposals()) + // .all(|(x, y)| x == &y); + // if result.new_root != chunk_state.chunk_header.prev_state_root() + // || outcome_root != chunk_state.chunk_header.outcome_root() + // || !proposals_match + // || result.total_gas_burnt != chunk_state.chunk_header.gas_used() + // { + // Ok((*block_header.hash(), vec![chunk_producer])) + // } else { + // // If all the data matches, this is actually valid chunk and challenge is malicious. + // Err(ErrorKind::MaliciousChallenge.into()) + // } + // Ok((*block_header.hash(), vec![chunk_producer])) + + Err(Error::MaliciousChallenge) +} + +/// Returns `Some(block_hash, vec![account_id])` of invalid block and who to +/// slash if challenge is correct and None if incorrect. +pub fn validate_challenge( + epoch_manager: &dyn EpochManagerAdapter, + runtime: &dyn RuntimeAdapter, + epoch_id: &EpochId, + last_block_hash: &CryptoHash, + challenge: &Challenge, +) -> Result<(CryptoHash, Vec), Error> { + // Check signature is correct on the challenge. + if !epoch_manager.verify_validator_or_fisherman_signature( + epoch_id, + last_block_hash, + &challenge.account_id, + challenge.hash.as_ref(), + &challenge.signature, + )? { + return Err(Error::InvalidChallenge); + } + match &challenge.body { + ChallengeBody::BlockDoubleSign(block_double_sign) => { + validate_double_sign(epoch_manager, block_double_sign) + } + ChallengeBody::ChunkProofs(chunk_proofs) => { + validate_chunk_proofs_challenge(epoch_manager, chunk_proofs) + } + ChallengeBody::ChunkState(chunk_state) => { + validate_chunk_state_challenge(runtime, chunk_state) + } + } +} + +#[cfg(test)] +mod tests { + use unc_crypto::{InMemorySigner, KeyType}; + + use super::*; + + fn make_tx(account_id: &str, seed: &str, nonce: Nonce) -> SignedTransaction { + let account_id: AccountId = account_id.parse().unwrap(); + let signer = InMemorySigner::from_seed(account_id.clone(), KeyType::ED25519, seed); + SignedTransaction::send_money( + nonce, + account_id, + "bob".parse().unwrap(), + &signer, + 10, + CryptoHash::default(), + ) + } + + #[test] + pub fn test_transaction_order_empty() { + let transactions = vec![]; + assert!(validate_transactions_order(&transactions)); + } + + #[test] + pub fn test_transaction_order_one_tx() { + let transactions = vec![make_tx("test_a", "test_A", 1)]; + assert!(validate_transactions_order(&transactions)); + } + + #[test] + pub fn test_transaction_order_simple() { + let transactions = vec![ + make_tx("test_a", "test_A", 1), + make_tx("test_b", "test_A", 3), + make_tx("test_a", "test_B", 4), + make_tx("test_c", "test_A", 2), + make_tx("test_b", "test_A", 6), // 2nd batch + make_tx("test_c", "test_A", 5), + make_tx("test_c", "test_A", 6), // 3rd batch + ]; + assert!(validate_transactions_order(&transactions)); + } + + #[test] + pub fn test_transaction_order_bad_nonce() { + let transactions = vec![ + make_tx("test_a", "test_A", 2), + make_tx("test_b", "test_A", 3), + make_tx("test_c", "test_A", 2), + make_tx("test_a", "test_A", 1), // 2nd batch, nonce 1 < 2 + make_tx("test_c", "test_A", 6), + ]; + assert!(!validate_transactions_order(&transactions)); + } + + #[test] + pub fn test_transaction_order_same_tx() { + let transactions = vec![make_tx("test_a", "test_A", 1), make_tx("test_a", "test_A", 1)]; + assert!(!validate_transactions_order(&transactions)); + } + + #[test] + pub fn test_transaction_order_skipped_in_first_batch() { + let transactions = vec![ + make_tx("test_a", "test_A", 2), + make_tx("test_c", "test_A", 2), + make_tx("test_a", "test_A", 4), // 2nd batch starts + make_tx("test_b", "test_A", 6), // Missing in the first batch + ]; + assert!(!validate_transactions_order(&transactions)); + } + + #[test] + pub fn test_transaction_order_skipped_in_2nd_batch() { + let transactions = vec![ + make_tx("test_a", "test_A", 2), + make_tx("test_c", "test_A", 2), + make_tx("test_a", "test_A", 4), // 2nd batch starts + make_tx("test_a", "test_A", 6), // 3rd batch starts + make_tx("test_c", "test_A", 6), // Not in the 2nd batch + ]; + assert!(!validate_transactions_order(&transactions)); + } +} diff --git a/chain/chunks-primitives/Cargo.toml b/chain/chunks-primitives/Cargo.toml new file mode 100644 index 000000000..0c2b4221c --- /dev/null +++ b/chain/chunks-primitives/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "unc-chunks-primitives" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "This crate hosts NEAR chunks-related error types" +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +unc-chain-primitives.workspace = true +unc-primitives.workspace = true diff --git a/chain/chunks-primitives/README.md b/chain/chunks-primitives/README.md new file mode 100644 index 000000000..6040d3183 --- /dev/null +++ b/chain/chunks-primitives/README.md @@ -0,0 +1,3 @@ +# unc-chunk-primitives + +This crate hosts NEAR chunks-related error types. \ No newline at end of file diff --git a/chain/chunks-primitives/src/error.rs b/chain/chunks-primitives/src/error.rs new file mode 100644 index 000000000..b13840781 --- /dev/null +++ b/chain/chunks-primitives/src/error.rs @@ -0,0 +1,45 @@ +use std::fmt; + +use unc_primitives::errors::EpochError; + +#[derive(Debug)] +pub enum Error { + InvalidPartMessage, + InvalidChunkPartId, + InvalidChunkShardId, + InvalidMerkleProof, + InvalidChunkSignature, + InvalidChunkHeader, + InvalidChunk, + DuplicateChunkHeight, + UnknownChunk, + KnownPart, + ChainError(unc_chain_primitives::Error), + IOError(std::io::Error), +} + +impl std::error::Error for Error {} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{:?}", self) + } +} + +impl From for Error { + fn from(err: std::io::Error) -> Self { + Error::IOError(err) + } +} + +impl From for Error { + fn from(err: unc_chain_primitives::Error) -> Self { + Error::ChainError(err) + } +} + +impl From for Error { + fn from(err: EpochError) -> Self { + Error::ChainError(err.into()) + } +} diff --git a/chain/chunks-primitives/src/lib.rs b/chain/chunks-primitives/src/lib.rs new file mode 100644 index 000000000..8894df6a2 --- /dev/null +++ b/chain/chunks-primitives/src/lib.rs @@ -0,0 +1,3 @@ +mod error; + +pub use error::Error; diff --git a/chain/chunks/Cargo.toml b/chain/chunks/Cargo.toml new file mode 100644 index 000000000..7aa563687 --- /dev/null +++ b/chain/chunks/Cargo.toml @@ -0,0 +1,74 @@ +[package] +name = "unc-chunks" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix.workspace = true +borsh.workspace = true +chrono.workspace = true +derive_more.workspace = true +derive-enum-from-into.workspace = true +futures.workspace = true +lru.workspace = true +once_cell.workspace = true +rand.workspace = true +reed-solomon-erasure.workspace = true +strum.workspace = true +time.workspace = true +tracing.workspace = true +# itertools has collect_vec which is useful in quick debugging prints +itertools.workspace = true + +unc-async.workspace = true +unc-chain-configs.workspace = true +unc-crypto.workspace = true +unc-epoch-manager.workspace = true +unc-primitives.workspace = true +unc-chunks-primitives.workspace = true +unc-store.workspace = true +unc-network.workspace = true +unc-o11y.workspace = true +unc-chain.workspace = true +unc-pool.workspace = true +unc-performance-metrics.workspace = true +unc-performance-metrics-macros.workspace = true + +[dev-dependencies] +assert_matches.workspace = true + +[features] +nightly_protocol = [ + "unc-async/nightly_protocol", + "unc-chain-configs/nightly_protocol", + "unc-chain/nightly_protocol", + "unc-epoch-manager/nightly_protocol", + "unc-network/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-pool/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", +] +nightly = [ + "nightly_protocol", + "unc-async/nightly", + "unc-chain-configs/nightly", + "unc-chain/nightly", + "unc-epoch-manager/nightly", + "unc-network/nightly", + "unc-o11y/nightly", + "unc-pool/nightly", + "unc-primitives/nightly", + "unc-store/nightly", +] +byzantine_asserts = ["unc-chain/byzantine_asserts"] +expensive_tests = [] +test_features = [] diff --git a/chain/chunks/README.md b/chain/chunks/README.md new file mode 100644 index 000000000..48385ff6d --- /dev/null +++ b/chain/chunks/README.md @@ -0,0 +1,35 @@ +# unc-chunks + +This crate cotains functions to handle chunks. In NEAR - the block consists of multiple chunks - at most one per shard. + +When a chunk is created, the creator encodes its contents using Reed Solomon encoding (ErasureCoding) and adds cross-shard receipts - creating PartialEncodedChunks that are later sent to all the validators (each validator gets a subset of them). This is done for data availability reasons (so that we need only a part of the validators to reconstruct the whole chunk). You can read more about it in [the Nightshade paper](https://near.org/papers/nightshade). + +A honest validator will only approve a block if it receives its assigned parts for all chunks in the block - which means that for each chunk, it has `has_all_parts()` returning true. + +For all nodes (validators and non-validators), they will only accept/process a block if the following requirements are satisfied: + +* for every shard it tracks, a node has to have the full chunk, +* for every shard it doesn't track, a node has have the receipts from this shard to all shards + +If node tracks given shard (that is - it wants to have a whole content of the shard present in the local memory) - it will want to receive the necessary amount of PartialChunks to be able to reconstruct the whole chunk. As we use ReedSolomon, this means that they need `data_shard_count` PartialChunks (which is lower than `total_shard_count`). Afterwards, it will reconstruct the chunk and persist it in the local storage (via chain/client). + +When the PartialEncodedChunk is received (in chain/client) - it will try to validate it immediately, if the previous block is already available (via `process_partial_encoded_chunk()`) or store it for future validation (via `store_partial_encoded_chunk()`). + +## ShardsManager + +Shard Manager is responsible for: + +* **fetching partial chunks** - it can ask other nodes for the partial chunks that it is missing. It keeps the track of the requests via RequestPool and can be asked to resend them when someone calls `resend_chunk_requests` (this is done periodically by the client actor). +* **storing partial chunks** - it stores partial chunks within local cache before they can be used to reconstruct for the full chunk. + `stored_partial_encoded_chunks` stores non-validated partial chunks while `ChunkCache` stores validated partial chunks. (we need the previous block in order to validate partial chunks). This data is also used when other nodes are requesting a partial encoded chunk (see below). +* **handling partial chunks requests** - when request for the partial chunk arrives, it handles reading it from store and returning to the requesting node +* **validating chunks** - once it receives correct set of partial chunks for the chunk, it can 'accept the chunk' (which means that validator can sign the block if all chunks are accepted) +* **storing full chunks** - it stores the full chunk into our storage via `decode_and_persist_encoded_chunk()`, which calls store_update's `save_chunk()` + +## ChunkCache + +Cache for validated partial chunks that we've seen - also stores the mapping from the blocks to the chunk headers. + +## RequestPool + +Tracks the requests for chunks that were sent - so that we don't resend them unless enough time elapsed since the last attempt. \ No newline at end of file diff --git a/chain/chunks/src/adapter.rs b/chain/chunks/src/adapter.rs new file mode 100644 index 000000000..f1b73b46b --- /dev/null +++ b/chain/chunks/src/adapter.rs @@ -0,0 +1,53 @@ +use actix::Message; +use unc_chain::types::Tip; +use unc_primitives::{ + hash::CryptoHash, + merkle::MerklePath, + receipt::Receipt, + sharding::{EncodedShardChunk, PartialEncodedChunk, ShardChunkHeader}, + types::EpochId, +}; + +#[derive(Message, Debug, strum::IntoStaticStr)] +#[rtype(result = "()")] +pub enum ShardsManagerRequestFromClient { + /// Processes the header seen from a block we received, if we have not already received the + /// header earlier from the chunk producer (via PartialEncodedChunk). + /// This can happen if we are not a validator, or if we are a validator but somehow missed + /// the chunk producer's message. + ProcessChunkHeaderFromBlock(ShardChunkHeader), + /// Lets the ShardsManager know that the chain heads have been updated. + /// For a discussion of head vs header_head, see #8154. + UpdateChainHeads { head: Tip, header_head: Tip }, + /// As a chunk producer, distributes the given chunk to the other validators (by sending + /// PartialEncodedChunk messages to them). + /// The partial_chunk and encoded_chunk represent the same data, just in different formats. + /// + /// TODO(#8422): the arguments contain redundant information. + DistributeEncodedChunk { + partial_chunk: PartialEncodedChunk, + encoded_chunk: EncodedShardChunk, + merkle_paths: Vec, + outgoing_receipts: Vec, + }, + /// Requests the given chunks to be fetched from other nodes. + /// Only the parts and receipt proofs that this node cares about will be fetched; when + /// the fetching is complete, a response of ClientAdapterForShardsManager::did_complete_chunk + /// will be sent back to the client. + RequestChunks { chunks_to_request: Vec, prev_hash: CryptoHash }, + /// Similar to request_chunks, but for orphan chunks. Since the chunk belongs to an orphan + /// block, the previous block is not known and thus we cannot derive epoch information from + /// that block. Therefore, an ancestor_hash must be provided which must correspond to a + /// block that is an ancestor of these chunks' blocks, and which must be in the same epoch. + RequestChunksForOrphan { + chunks_to_request: Vec, + epoch_id: EpochId, + ancestor_hash: CryptoHash, + }, + /// In response to processing a block, checks if there are any chunks that should have been + /// complete but are just waiting on the previous block to become available (e.g. a chunk + /// requested by request_chunks_for_orphan, which then received all needed parts and receipt + /// proofs, but cannot be marked as complete because the previous block isn't available), + /// and completes them if so. + CheckIncompleteChunks(CryptoHash), +} diff --git a/chain/chunks/src/chunk_cache.rs b/chain/chunks/src/chunk_cache.rs new file mode 100644 index 000000000..488a6712c --- /dev/null +++ b/chain/chunks/src/chunk_cache.rs @@ -0,0 +1,340 @@ +use std::collections::{HashMap, HashSet}; + +use unc_primitives::hash::CryptoHash; +use unc_primitives::sharding::{ + ChunkHash, PartialEncodedChunkPart, PartialEncodedChunkV2, ReceiptProof, ShardChunkHeader, +}; +use unc_primitives::types::{BlockHeight, BlockHeightDelta, ShardId}; +use std::collections::hash_map::Entry::Occupied; +use tracing::warn; + +// This file implements EncodedChunksCache, which provides three main functionalities: +// 1) It stores a map from a chunk hash to all the parts and receipts received so far for the chunk. +// This map is used to aggregate chunk parts and receipts before the full chunk can be reconstructed +// or the necessary parts and receipts are received. +// When a PartialEncodedChunk is received, the parts and receipts it contains are merged to the +// corresponding chunk entry in the map. +// Entries in the map are removed if the chunk is found to be invalid or the chunk goes out of +// horizon [chain_head_height - HEIGHT_HORIZON, chain_head_height + MAX_HEIGHTS_AHEAD] +// 2) It stores the set of incomplete chunks, indexed by the block hash of the previous block. +// A chunk always starts incomplete. It can be marked as complete through +// `mark_entry_complete`. A complete entry means the chunk has all parts and receipts needed. +// 3) It stores a map from block hash to chunk headers that are ready to be included in a block. +// This functionality is meant for block producers. When producing a block, the block producer +// will only include chunks in the block for which it has received the part it owns. +// Users of the data structure are responsible for adding chunk to this map at the right time. + +/// A chunk is out of horizon if its height + HEIGHT_HORIZON < largest_seen_height +const HEIGHT_HORIZON: BlockHeightDelta = 1024; +/// A chunk is out of horizon if its height > HEIGHT_HORIZON + largest_seen_height +const MAX_HEIGHTS_AHEAD: BlockHeightDelta = 5; + +/// EncodedChunksCacheEntry stores the consolidated parts and receipts received for a chunk +/// When a PartialEncodedChunk is received, it can be merged to the existing EncodedChunksCacheEntry +/// for the chunk +pub struct EncodedChunksCacheEntry { + pub header: ShardChunkHeader, + pub parts: HashMap, + pub receipts: HashMap, + /// whether this entry has all parts and receipts + pub complete: bool, + /// whether this chunk is ready for inclusion for producing a block + pub ready_for_inclusion: bool, + /// Whether the header has been **fully** validated. + /// Every entry added to the cache already has their header "partially" validated + /// by validate_chunk_header. When the previous block is accepted, they must be + /// validated again to make sure they are fully validated. + /// See comments in `validate_chunk_header` for more context on partial vs full validation + pub header_fully_validated: bool, +} + +pub struct EncodedChunksCache { + /// Largest seen height from the head of the chain + largest_seen_height: BlockHeight, + + /// A map from a chunk hash to the corresponding EncodedChunksCacheEntry of the chunk + /// Entries in this map have height in + /// [chain_head_height - HEIGHT_HORIZON, chain_head_height + MAX_HEIGHTS_AHEAD] + encoded_chunks: HashMap, + /// A map from a block height to chunk hashes at this height for all chunk stored in the cache + /// This is used to gc chunks that are out of horizon + height_map: HashMap>, + /// A map from block height to shard ID to the chunk hash we've received, so we only process + /// one chunk per shard per height. + height_to_shard_to_chunk: HashMap>, + /// A map from a block hash to a set of incomplete chunks (does not have all parts and receipts yet) + /// whose previous block is the block hash. + incomplete_chunks: HashMap>, +} + +impl EncodedChunksCacheEntry { + pub fn from_chunk_header(header: ShardChunkHeader) -> Self { + EncodedChunksCacheEntry { + header, + parts: HashMap::new(), + receipts: HashMap::new(), + complete: false, + ready_for_inclusion: false, + header_fully_validated: false, + } + } + + /// Inserts previously unknown chunks and receipts, returning the part ords that were + /// previously unknown. + pub fn merge_in_partial_encoded_chunk( + &mut self, + partial_encoded_chunk: &PartialEncodedChunkV2, + ) -> HashSet { + let mut previously_missing_part_ords = HashSet::new(); + for part_info in partial_encoded_chunk.parts.iter() { + let part_ord = part_info.part_ord; + self.parts.entry(part_ord).or_insert_with(|| { + previously_missing_part_ords.insert(part_ord); + part_info.clone() + }); + } + + for receipt in partial_encoded_chunk.receipts.iter() { + let shard_id = receipt.1.to_shard_id; + self.receipts.entry(shard_id).or_insert_with(|| receipt.clone()); + } + previously_missing_part_ords + } +} + +impl EncodedChunksCache { + pub fn new() -> Self { + EncodedChunksCache { + largest_seen_height: 0, + encoded_chunks: HashMap::new(), + height_map: HashMap::new(), + height_to_shard_to_chunk: HashMap::new(), + incomplete_chunks: HashMap::new(), + } + } + + pub fn get(&self, chunk_hash: &ChunkHash) -> Option<&EncodedChunksCacheEntry> { + self.encoded_chunks.get(chunk_hash) + } + + /// Mark an entry as complete, which means it has all parts and receipts needed + pub fn mark_entry_complete(&mut self, chunk_hash: &ChunkHash) { + if let Some(entry) = self.encoded_chunks.get_mut(chunk_hash) { + entry.complete = true; + let previous_block_hash = &entry.header.prev_block_hash().clone(); + self.remove_chunk_from_incomplete_chunks(previous_block_hash, chunk_hash); + } else { + warn!(target:"chunks", "cannot mark non-existent entry as complete {:?}", chunk_hash); + } + } + + pub fn mark_entry_validated(&mut self, chunk_hash: &ChunkHash) { + if let Some(entry) = self.encoded_chunks.get_mut(chunk_hash) { + entry.header_fully_validated = true; + } else { + warn!("no entry exist {:?}", chunk_hash); + } + } + + /// Get a list of incomplete chunks whose previous block hash is `prev_block_hash` + pub fn get_incomplete_chunks( + &self, + prev_block_hash: &CryptoHash, + ) -> Option<&HashSet> { + self.incomplete_chunks.get(prev_block_hash) + } + + pub fn remove(&mut self, chunk_hash: &ChunkHash) -> Option { + if let Some(entry) = self.encoded_chunks.remove(chunk_hash) { + self.remove_chunk_from_incomplete_chunks(entry.header.prev_block_hash(), chunk_hash); + Some(entry) + } else { + None + } + } + + // Remove the chunk from the `incomplete_chunks` map. This is an internal function. + // Use `mark_entry_complete` instead for outside calls + fn remove_chunk_from_incomplete_chunks( + &mut self, + prev_block_hash: &CryptoHash, + chunk_hash: &ChunkHash, + ) { + if let Occupied(mut entry) = self.incomplete_chunks.entry(*prev_block_hash) { + entry.get_mut().remove(chunk_hash); + if entry.get().is_empty() { + entry.remove(); + } + } + } + + // Create an empty entry from the header and insert it if there is no entry for the chunk already + // Return a mutable reference to the entry + pub fn get_or_insert_from_header( + &mut self, + chunk_header: &ShardChunkHeader, + ) -> &mut EncodedChunksCacheEntry { + let chunk_hash = chunk_header.chunk_hash(); + self.encoded_chunks.entry(chunk_hash).or_insert_with_key(|chunk_hash| { + self.height_map + .entry(chunk_header.height_created()) + .or_default() + .insert(chunk_hash.clone()); + self.height_to_shard_to_chunk + .entry(chunk_header.height_created()) + .or_default() + .insert(chunk_header.shard_id(), chunk_hash.clone()); + self.incomplete_chunks + .entry(*chunk_header.prev_block_hash()) + .or_default() + .insert(chunk_hash.clone()); + EncodedChunksCacheEntry::from_chunk_header(chunk_header.clone()) + }) + } + + pub fn height_within_front_horizon(&self, height: BlockHeight) -> bool { + height >= self.largest_seen_height && height <= self.largest_seen_height + MAX_HEIGHTS_AHEAD + } + + pub fn height_within_rear_horizon(&self, height: BlockHeight) -> bool { + height + HEIGHT_HORIZON >= self.largest_seen_height && height <= self.largest_seen_height + } + + pub fn height_within_horizon(&self, height: BlockHeight) -> bool { + self.height_within_front_horizon(height) || self.height_within_rear_horizon(height) + } + + pub fn get_chunk_hash_by_height_and_shard( + &self, + height: BlockHeight, + shard_id: ShardId, + ) -> Option<&ChunkHash> { + self.height_to_shard_to_chunk.get(&height)?.get(&shard_id) + } + + /// Add parts and receipts stored in a partial encoded chunk to the corresponding chunk entry, + /// returning the set of part ords that were previously unknown. + pub fn merge_in_partial_encoded_chunk( + &mut self, + partial_encoded_chunk: &PartialEncodedChunkV2, + ) -> HashSet { + let entry = self.get_or_insert_from_header(&partial_encoded_chunk.header); + entry.merge_in_partial_encoded_chunk(partial_encoded_chunk) + } + + /// Remove a chunk from the cache if it is outside of horizon + pub fn remove_from_cache_if_outside_horizon(&mut self, chunk_hash: &ChunkHash) { + if let Some(entry) = self.encoded_chunks.get(chunk_hash) { + let height = entry.header.height_created(); + if !self.height_within_horizon(height) { + self.remove(&chunk_hash); + } + } + } + + /// Update largest seen height and removes chunks from the cache that are outside of horizon + pub fn update_largest_seen_height( + &mut self, + new_height: BlockHeight, + requested_chunks: &HashMap, + ) { + let old_largest_seen_height = self.largest_seen_height; + self.largest_seen_height = new_height; + for height in old_largest_seen_height.saturating_sub(HEIGHT_HORIZON) + ..self.largest_seen_height.saturating_sub(HEIGHT_HORIZON) + { + if let Some(chunks_to_remove) = self.height_map.remove(&height) { + for chunk_hash in chunks_to_remove { + if !requested_chunks.contains_key(&chunk_hash) { + self.remove(&chunk_hash); + } + } + } + self.height_to_shard_to_chunk.remove(&height); + } + } + + /// Marks the chunk for inclusion in a block; returns true if we haven't already + /// called for this chunk. Requires that the chunk is already in the cache. + pub fn mark_chunk_for_inclusion(&mut self, chunk_hash: &ChunkHash) -> bool { + let entry = self.encoded_chunks.get_mut(chunk_hash).unwrap(); + if entry.ready_for_inclusion { + false + } else { + entry.ready_for_inclusion = true; + true + } + } +} + +#[cfg(test)] +mod tests { + use std::collections::{HashMap, HashSet}; + + use unc_crypto::KeyType; + use unc_primitives::hash::CryptoHash; + use unc_primitives::sharding::{PartialEncodedChunkV2, ShardChunkHeader, ShardChunkHeaderV2}; + use unc_primitives::validator_signer::InMemoryValidatorSigner; + + use crate::chunk_cache::EncodedChunksCache; + use crate::ChunkRequestInfo; + + fn create_chunk_header(height: u64, shard_id: u64) -> ShardChunkHeader { + let signer = + InMemoryValidatorSigner::from_random("test".parse().unwrap(), KeyType::ED25519); + ShardChunkHeader::V2(ShardChunkHeaderV2::new( + CryptoHash::default(), + CryptoHash::default(), + CryptoHash::default(), + CryptoHash::default(), + 1, + height, + shard_id, + 0, + 0, + 0, + CryptoHash::default(), + CryptoHash::default(), + vec![], + &signer, + )) + } + + #[test] + fn test_incomplete_chunks() { + let mut cache = EncodedChunksCache::new(); + let header0 = create_chunk_header(1, 0); + let header1 = create_chunk_header(1, 1); + cache.get_or_insert_from_header(&header0); + cache.merge_in_partial_encoded_chunk(&PartialEncodedChunkV2 { + header: header1.clone(), + parts: vec![], + receipts: vec![], + }); + assert_eq!( + cache.get_incomplete_chunks(&CryptoHash::default()).unwrap(), + &HashSet::from([header0.chunk_hash(), header1.chunk_hash()]) + ); + cache.mark_entry_complete(&header0.chunk_hash()); + assert_eq!( + cache.get_incomplete_chunks(&CryptoHash::default()).unwrap(), + &[header1.chunk_hash()].into_iter().collect::>() + ); + cache.mark_entry_complete(&header1.chunk_hash()); + assert_eq!(cache.get_incomplete_chunks(&CryptoHash::default()), None); + } + + #[test] + fn test_cache_removal() { + let mut cache = EncodedChunksCache::new(); + let header = create_chunk_header(1, 0); + let partial_encoded_chunk = + PartialEncodedChunkV2 { header: header, parts: vec![], receipts: vec![] }; + cache.merge_in_partial_encoded_chunk(&partial_encoded_chunk); + assert!(!cache.height_map.is_empty()); + + cache.update_largest_seen_height::(2000, &HashMap::default()); + assert!(cache.encoded_chunks.is_empty()); + assert!(cache.height_map.is_empty()); + } +} diff --git a/chain/chunks/src/client.rs b/chain/chunks/src/client.rs new file mode 100644 index 000000000..83673d4fe --- /dev/null +++ b/chain/chunks/src/client.rs @@ -0,0 +1,272 @@ +use std::collections::HashMap; + +use actix::Message; + +use unc_pool::types::PoolIterator; +use unc_pool::{InsertTransactionResult, PoolIteratorWrapper, TransactionPool}; +use unc_primitives::shard_layout::{account_id_to_shard_uid, ShardLayout, ShardUId}; +use unc_primitives::{ + epoch_manager::RngSeed, + sharding::{EncodedShardChunk, PartialEncodedChunk, ShardChunk, ShardChunkHeader}, + transaction::SignedTransaction, + types::{AccountId, ShardId}, +}; + +#[derive(Message, Debug)] +#[rtype(result = "()")] +pub enum ShardsManagerResponse { + /// Notifies the client that the ShardsManager has collected a complete chunk. + /// Note that this does NOT mean that the chunk is fully constructed. If we are + /// not tracking the shard this chunk is in, then being complete only means that + /// we have received the parts we own, and the receipt proofs corresponding to + /// shards that we do track. On the other hand if we are tracking the shard this + /// chunk is in, then being complete does mean having the full chunk, in which + /// case the shard_chunk is also provided. + ChunkCompleted { partial_chunk: PartialEncodedChunk, shard_chunk: Option }, + /// Notifies the client that we have collected a full chunk but the chunk cannot + /// be properly decoded. + InvalidChunk(EncodedShardChunk), + /// Notifies the client that the chunk header is ready for inclusion into a new + /// block, so that if we are a block producer, we may create a block that contains + /// this chunk now. The producer of this chunk is also provided. + ChunkHeaderReadyForInclusion { chunk_header: ShardChunkHeader, chunk_producer: AccountId }, +} + +pub struct ShardedTransactionPool { + tx_pools: HashMap, + + /// Useful to make tests deterministic and reproducible, + /// while keeping the security of randomization of transactions in pool + rng_seed: RngSeed, + + /// If set, new transactions that bring the size of the pool over this limit will be rejected. + /// The size is tracked and enforced separately for each shard. + pool_size_limit: Option, +} + +impl ShardedTransactionPool { + pub fn new(rng_seed: RngSeed, pool_size_limit: Option) -> Self { + Self { tx_pools: HashMap::new(), rng_seed, pool_size_limit } + } + + pub fn get_pool_iterator(&mut self, shard_uid: ShardUId) -> Option> { + self.tx_pools.get_mut(&shard_uid).map(|pool| pool.pool_iterator()) + } + + /// Tries to insert the transaction into the pool for a given shard. + pub fn insert_transaction( + &mut self, + shard_uid: ShardUId, + tx: SignedTransaction, + ) -> InsertTransactionResult { + self.pool_for_shard(shard_uid).insert_transaction(tx) + } + + pub fn remove_transactions(&mut self, shard_uid: ShardUId, transactions: &[SignedTransaction]) { + if let Some(pool) = self.tx_pools.get_mut(&shard_uid) { + pool.remove_transactions(transactions) + } + } + + /// Computes a deterministic random seed for given `shard_id`. + /// This seed is used to randomize the transaction pool. + /// For better security we want the seed to different in each shard. + /// For testing purposes we want it to be the reproducible and derived from the `self.rng_seed` and `shard_id` + fn random_seed(base_seed: &RngSeed, shard_id: ShardId) -> RngSeed { + let mut res = *base_seed; + res[0] = shard_id as u8; + res[1] = (shard_id / 256) as u8; + res + } + + fn pool_for_shard(&mut self, shard_uid: ShardUId) -> &mut TransactionPool { + self.tx_pools.entry(shard_uid).or_insert_with(|| { + TransactionPool::new( + Self::random_seed(&self.rng_seed, shard_uid.shard_id()), + self.pool_size_limit, + &shard_uid.to_string(), + ) + }) + } + + /// Reintroduces transactions back during the chain reorg. Returns the number of transactions + /// that were added or are already present in the pool. + pub fn reintroduce_transactions( + &mut self, + shard_uid: ShardUId, + transactions: &[SignedTransaction], + ) -> usize { + let mut reintroduced_count = 0; + let pool = self.pool_for_shard(shard_uid); + for tx in transactions { + reintroduced_count += match pool.insert_transaction(tx.clone()) { + InsertTransactionResult::Success | InsertTransactionResult::Duplicate => 1, + InsertTransactionResult::NoSpaceLeft => 0, + } + } + reintroduced_count + } + + /// Migrate all of the transactions in the pool from the old shard layout to + /// the new shard layout. + /// It works by emptying the pools for old shard uids and re-inserting the + /// transactions back to the pool with the new shard uids. + pub fn reshard(&mut self, old_shard_layout: &ShardLayout, new_shard_layout: &ShardLayout) { + tracing::debug!( + target: "client", + old_shard_layout_version = old_shard_layout.version(), + new_shard_layout_version = new_shard_layout.version(), + "resharding the transaction pool" + ); + debug_assert!(old_shard_layout != new_shard_layout); + debug_assert!(old_shard_layout.version() + 1 == new_shard_layout.version()); + + let mut transactions = vec![]; + + for old_shard_uid in old_shard_layout.shard_uids() { + if let Some(mut iter) = self.get_pool_iterator(old_shard_uid) { + while let Some(group) = iter.next() { + while let Some(tx) = group.next() { + transactions.push(tx); + } + } + } + } + + for tx in transactions { + let signer_id = &tx.transaction.signer_id; + let new_shard_uid = account_id_to_shard_uid(&signer_id, new_shard_layout); + self.insert_transaction(new_shard_uid, tx); + } + } +} + +#[cfg(test)] +mod tests { + use crate::client::ShardedTransactionPool; + use unc_crypto::{InMemorySigner, KeyType}; + use unc_o11y::testonly::init_test_logger; + use unc_pool::types::PoolIterator; + use unc_primitives::{ + epoch_manager::RngSeed, + hash::CryptoHash, + shard_layout::{account_id_to_shard_uid, ShardLayout}, + transaction::SignedTransaction, + types::AccountId, + }; + use unc_store::ShardUId; + use rand::{rngs::StdRng, seq::SliceRandom, SeedableRng}; + use std::{collections::HashMap, str::FromStr}; + + const TEST_SEED: RngSeed = [3; 32]; + + #[test] + fn test_random_seed_with_shard_id() { + let seed0 = ShardedTransactionPool::random_seed(&TEST_SEED, 0); + let seed10 = ShardedTransactionPool::random_seed(&TEST_SEED, 10); + let seed256 = ShardedTransactionPool::random_seed(&TEST_SEED, 256); + let seed1000 = ShardedTransactionPool::random_seed(&TEST_SEED, 1000); + let seed1000000 = ShardedTransactionPool::random_seed(&TEST_SEED, 1_000_000); + assert_ne!(seed0, seed10); + assert_ne!(seed0, seed256); + assert_ne!(seed0, seed1000); + assert_ne!(seed0, seed1000000); + assert_ne!(seed10, seed256); + assert_ne!(seed10, seed1000); + assert_ne!(seed10, seed1000000); + assert_ne!(seed256, seed1000); + assert_ne!(seed256, seed1000000); + assert_ne!(seed1000, seed1000000); + } + + #[test] + fn test_transaction_pool_resharding() { + init_test_logger(); + let old_shard_layout = ShardLayout::get_simple_nightshade_layout(); + let new_shard_layout = ShardLayout::get_simple_nightshade_layout_v2(); + + let mut pool = ShardedTransactionPool::new(TEST_SEED, None); + + let mut shard_id_to_accounts = HashMap::new(); + shard_id_to_accounts.insert(0, vec!["aaa", "abcd", "a-a-a-a-a"]); + shard_id_to_accounts.insert(1, vec!["aurora"]); + shard_id_to_accounts.insert(2, vec!["aurora-0", "bob", "kkk"]); + // this shard is split, make sure there are accounts for both shards 3' and 4' + shard_id_to_accounts.insert(3, vec!["mmm", "rrr", "sweat", "ttt", "www", "zzz"]); + + let deposit = 222; + + let mut rng: StdRng = SeedableRng::seed_from_u64(42); + + // insert some transactions using the old shard layout + + let n = 100; + tracing::info!("inserting {n} transactions into the pool using the old shard layout"); + for i in 0..n { + let shard_ids: Vec<_> = old_shard_layout.shard_ids().collect(); + let &signer_shard_id = shard_ids.choose(&mut rng).unwrap(); + let &receiver_shard_id = shard_ids.choose(&mut rng).unwrap(); + let nonce = i as u64; + + let signer_id = *shard_id_to_accounts[&signer_shard_id].choose(&mut rng).unwrap(); + let signer_id = AccountId::from_str(signer_id).unwrap(); + + let receiver_id = *shard_id_to_accounts[&receiver_shard_id].choose(&mut rng).unwrap(); + let receiver_id = AccountId::from_str(receiver_id).unwrap(); + + let signer = InMemorySigner::from_seed(signer_id.clone(), KeyType::ED25519, "seed"); + + let tx = SignedTransaction::send_money( + nonce, + signer_id.clone(), + receiver_id.clone(), + &signer, + deposit, + CryptoHash::default(), + ); + + let shard_uid = + ShardUId { shard_id: signer_shard_id as u32, version: old_shard_layout.version() }; + pool.insert_transaction(shard_uid, tx); + } + + // reshard + + tracing::info!("resharding the pool"); + pool.reshard(&old_shard_layout, &new_shard_layout); + + // check the pool is correctly resharded + + tracing::info!("checking the pool after resharding"); + { + let shard_ids: Vec<_> = new_shard_layout.shard_ids().collect(); + for &shard_id in shard_ids.iter() { + let shard_id = shard_id as u32; + let shard_uid = ShardUId { shard_id, version: new_shard_layout.version() }; + let pool = pool.pool_for_shard(shard_uid); + let pool_len = pool.len(); + tracing::debug!("checking shard_uid {shard_uid:?}, the pool len is {pool_len}"); + assert_ne!(pool.len(), 0); + } + + let mut total = 0; + for shard_id in shard_ids { + let shard_id = shard_id as u32; + let shard_uid = ShardUId { shard_id, version: new_shard_layout.version() }; + let mut pool_iter = pool.get_pool_iterator(shard_uid).unwrap(); + while let Some(group) = pool_iter.next() { + while let Some(tx) = group.next() { + total += 1; + let account_id = tx.transaction.signer_id; + let tx_shard_uid = account_id_to_shard_uid(&account_id, &new_shard_layout); + tracing::debug!("checking {account_id:?}:{tx_shard_uid} in {shard_uid}"); + assert_eq!(shard_uid, tx_shard_uid); + } + } + } + + assert_eq!(total, n); + } + tracing::info!("finished"); + } +} diff --git a/chain/chunks/src/lib.rs b/chain/chunks/src/lib.rs new file mode 100644 index 000000000..e5266e3ae --- /dev/null +++ b/chain/chunks/src/lib.rs @@ -0,0 +1,2964 @@ +//! This module implements ShardManager, which handles chunks requesting and processing. +//! Since blocks only contain chunk headers, full chunks must be communicated separately. +//! For data availability, information in a chunk is divided into parts by Reed Solomon encoding, +//! and each validator holds a subset of parts of each chunk (a validator is called the owner +//! of the parts that they hold). This way, a chunk can be retrieved from any X validators +//! where X is the threshold for the Reed Solomon encoding for retrieving the full information. +//! Currently, X is set to be 1/3 of total validator seats (num_data_parts). +//! +//! **How chunks are propagated in the network +//! Instead sending the full chunk, chunk content is communicated between nodes +//! through PartialEncodedChunk, which includes the chunk header, some parts and +//! receipts of the chunk. Full chunk can be reconstructed if a node receives +//! enough chunk parts. +//! A node receives partial encoded chunks in three ways, +//! - by requesting it and receiving a PartialEncodedChunkResponse, +//! - by receiving a PartialEncodedChunk, which is sent from the original chunk +//! producer to the part owners after the chunk is produced +//! - by receiving a PartialEncodedChunkForward, which is sent from part owners +//! to validators who track the shard, when a validator first receives a part +//! it owns. TODO(#5886): this is actually not the current behavior. +//! Note that last two messages can only be sent from validators to validators, +//! so the only way a non-validator receives a partial encoded chunk is by +//! requesting it. +//! +//! ** Requesting for chunks +//! `ShardManager` keeps a request pool that stores all requests for chunks that are not completed +//! yet. The requests are managed at the chunk level, instead of individual parts and receipts. +//! A new request can be added by sending it a message RequestChunks or RequestChunksForOrphan +//! (depending on whether the parent block is known). If it is not in the pool yet, +//! `request_partial_encoded_chunk` will be called, which checks which parts or +//! receipts are still needed for the chunk by checking `encoded_chunks` (see the section on +//! ** Storing chunks). This way, the node won't send requests for parts and receipts they already have. +//! It then figures out where to request them, either from the original +//! chunk producer, or a block producer or peer who tracks the shard, and sends out the network +//! requests. Check the logic there for details regarding how targets of requests are chosen. +//! +//! Once a request is added the pool, it can be resent through `resend_chunk_requests`, +//! which is done periodically through the ShardsManagerActor. A request is only removed from +//! the pool when all needed parts and receipts in the requested chunk are received. +//! +//! ** Storing chunks +//! Before a chunk can be reconstructed fully, parts and receipts in the chunk are stored in +//! `encoded_chunks`. Full chunks will be persisted in the database storage after they are +//! reconstructed. +//! +//! ** Forwarding chunks +//! To save messages and time for chunks to propagate among validators, we implemented chunk part +//! forwarding. When a validator receives a part it owns, it forwards the part to +//! other validators who are assigned to track the shard through a PartialEncodedChunkForward message. +//! This saves the number of requests validators need to send to get all parts they need. A forwarded +//! part can only be processed after the node has the corresponding chunk header, either from blocks +//! or partial chunk requests. Before that, they are temporarily stored in `chunk_forwards_cache`. +//! After that, they are processed as a PartialEncodedChunk message containing the cached parts. +//! +//! ** Processing chunks +//! Function `process_partial_encoded_chunk` processes a partial encoded chunk message. +//! 1) validates the parts and receipts in the message +//! 2) merges the parts and receipts are into `encoded_chunks`. +//! 3) forwards newly received owned parts to other validators, if any. +//! 4) checks if there are any forwarded chunk parts in `chunk_forwards_cache` that can be processed. +//! 5) checks if all needed parts and receipts are received and tries to reconstruct the full chunk. +//! If successful, removes request for the chunk from the request pool. Note that a prerequisite +//! of this is that the previous block is accepted. If missing the previous block is the only +//! blocker, there's another chance to trigger this processing again in check_incomplete_chunks, +//! which is triggered by sending the CheckIncompleteChunks message from the client. +//! +//! ** Validating chunks +//! Before `process_partial_encoded_chunk` returns HaveAllPartsAndReceipts, it will perform +//! the validation steps and return error if validation fails. +//! 1) validate the chunk header is signed by the correct chunk producer and the chunk producer +//! is not slashed (see `validate_chunk_header`) +//! 2) validate the merkle proofs of the parts and receipts with regarding to the parts root and +//! receipts root in the chunk header (see the beginning of `process_partial_encoded_chunk`) +//! 3) after the full chunk is reconstructed, validate chunk's proofs in the header matches the body +//! (see validate_chunk_proofs) +//! +//! We also guarantee that all entries stored inside ShardsManager::encoded_chunks have the chunk header +//! at least "partially" validated by `validate_chunk_header` (see the comments there for what "partial" +//! validation means). + +use crate::chunk_cache::{EncodedChunksCache, EncodedChunksCacheEntry}; +use crate::logic::{cares_about_shard_this_or_next_epoch, chunk_needs_to_be_fetched_from_archival}; +use adapter::ShardsManagerRequestFromClient; +use client::ShardsManagerResponse; +use logic::{ + decode_encoded_chunk, make_outgoing_receipts_proofs, + make_partial_encoded_chunk_from_owned_parts_and_needed_receipts, need_part, need_receipt, +}; +use metrics::{ + PARTIAL_ENCODED_CHUNK_FORWARD_CACHED_WITHOUT_HEADER, + PARTIAL_ENCODED_CHUNK_FORWARD_CACHED_WITHOUT_PREV_BLOCK, PARTIAL_ENCODED_CHUNK_RESPONSE_DELAY, +}; +use unc_async::messaging::Sender; +use unc_async::time; +use unc_chain::byzantine_assert; +use unc_chain::chunks_store::ReadOnlyChunksStore; +use unc_chain::unc_chain_primitives::error::Error::DBNotFoundErr; +use unc_chain::types::EpochManagerAdapter; +pub use unc_chunks_primitives::Error; +use unc_epoch_manager::shard_tracker::ShardTracker; +use unc_network::shards_manager::ShardsManagerRequestFromNetwork; +use unc_network::types::{ + AccountIdOrPeerTrackingShard, PartialEncodedChunkForwardMsg, PartialEncodedChunkRequestMsg, + PartialEncodedChunkResponseMsg, +}; +use unc_network::types::{NetworkRequests, PeerManagerMessageRequest}; +use unc_primitives::block::Tip; +use unc_primitives::errors::EpochError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::{verify_path, MerklePath}; +use unc_primitives::receipt::Receipt; +use unc_primitives::sharding::{ + ChunkHash, EncodedShardChunk, EncodedShardChunkBody, PartialEncodedChunk, + PartialEncodedChunkPart, PartialEncodedChunkV2, ReceiptProof, ReedSolomonWrapper, ShardChunk, + ShardChunkHeader, ShardProof, +}; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::validator_power::ValidatorPower; +use unc_primitives::types::{ + AccountId, Balance, BlockHeight, BlockHeightDelta, EpochId, Gas, MerkleHash, ShardId, StateRoot, +}; +use unc_primitives::unwrap_or_return; +use unc_primitives::utils::MaybeValidated; +use unc_primitives::validator_signer::ValidatorSigner; +use unc_primitives::version::ProtocolVersion; +use rand::seq::IteratorRandom; +use rand::Rng; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; +use tracing::{debug, debug_span, error, warn}; +use unc_primitives::types::validator_frozen::ValidatorFrozen; + +pub mod adapter; +mod chunk_cache; +pub mod client; +pub mod logic; +pub mod metrics; +pub mod shards_manager_actor; +pub mod test_loop; +pub mod test_utils; + +pub const CHUNK_REQUEST_RETRY: time::Duration = time::Duration::milliseconds(100); +pub const CHUNK_REQUEST_SWITCH_TO_OTHERS: time::Duration = time::Duration::milliseconds(400); +pub const CHUNK_REQUEST_SWITCH_TO_FULL_FETCH: time::Duration = time::Duration::seconds(3); +const CHUNK_REQUEST_RETRY_MAX: time::Duration = time::Duration::seconds(1000); +const CHUNK_FORWARD_CACHE_SIZE: usize = 1000; +// Only request chunks from peers whose latest height >= chunk_height - CHUNK_REQUEST_PEER_HORIZON +const CHUNK_REQUEST_PEER_HORIZON: BlockHeightDelta = 5; + +#[derive(PartialEq, Eq)] +pub enum ChunkStatus { + Complete(Vec), + Incomplete, + Invalid, +} + +#[derive(Debug)] +pub enum ProcessPartialEncodedChunkResult { + /// The information included in the partial encoded chunk is already known, no processing is needed + Known, + /// All parts and receipts in the chunk are received and the chunk has been processed + HaveAllPartsAndReceipts, + /// More parts and receipts are needed for processing the full chunk + NeedMorePartsOrReceipts, + /// PartialEncodedChunkMessage is received earlier than Block for the same height. + /// Without the block we cannot restore the epoch and save encoded chunk data. + NeedBlock, +} + +#[derive(Clone, Debug)] +struct ChunkRequestInfo { + height: BlockHeight, + // hash of the ancestor hash used for the request, i.e., the first block up the + // parent chain of the block that has missing chunks that is approved + ancestor_hash: CryptoHash, + // previous block hash of the chunk + prev_block_hash: CryptoHash, + shard_id: ShardId, + added: time::Instant, + last_requested: time::Instant, +} + +struct RequestPool { + retry_duration: time::Duration, + switch_to_others_duration: time::Duration, + switch_to_full_fetch_duration: time::Duration, + max_duration: time::Duration, + requests: HashMap, +} + +impl RequestPool { + pub fn new( + retry_duration: time::Duration, + switch_to_others_duration: time::Duration, + switch_to_full_fetch_duration: time::Duration, + max_duration: time::Duration, + ) -> Self { + Self { + retry_duration, + switch_to_others_duration, + switch_to_full_fetch_duration, + max_duration, + requests: HashMap::default(), + } + } + pub fn contains_key(&self, chunk_hash: &ChunkHash) -> bool { + self.requests.contains_key(chunk_hash) + } + pub fn len(&self) -> usize { + self.requests.len() + } + + pub fn insert(&mut self, chunk_hash: ChunkHash, chunk_request: ChunkRequestInfo) { + self.requests.insert(chunk_hash, chunk_request); + } + + pub fn get_request_info(&self, chunk_hash: &ChunkHash) -> Option<&ChunkRequestInfo> { + self.requests.get(chunk_hash) + } + + pub fn remove(&mut self, chunk_hash: &ChunkHash) { + self.requests.remove(chunk_hash); + } + + pub fn fetch(&mut self, current_time: time::Instant) -> Vec<(ChunkHash, ChunkRequestInfo)> { + let mut removed_requests = HashSet::::default(); + let mut requests = Vec::new(); + for (chunk_hash, chunk_request) in self.requests.iter_mut() { + if current_time - chunk_request.added >= self.max_duration { + debug!(target: "chunks", "Evicted chunk requested that was never fetched {} (shard_id: {})", chunk_hash.0, chunk_request.shard_id); + removed_requests.insert(chunk_hash.clone()); + continue; + } + if current_time - chunk_request.last_requested >= self.retry_duration { + chunk_request.last_requested = current_time; + requests.push((chunk_hash.clone(), chunk_request.clone())); + } + } + for chunk_hash in removed_requests { + self.requests.remove(&chunk_hash); + } + requests + } +} + +pub struct ShardsManager { + clock: time::Clock, + me: Option, + store: ReadOnlyChunksStore, + + epoch_manager: Arc, + shard_tracker: ShardTracker, + peer_manager_adapter: Sender, + client_adapter: Sender, + rs: ReedSolomonWrapper, + + encoded_chunks: EncodedChunksCache, + requested_partial_encoded_chunks: RequestPool, + chunk_forwards_cache: lru::LruCache>, + + // This is a best-effort cache of the chain's head, not the source of truth. The source + // of truth is in the chain store and written to by the Client. + chain_head: Tip, + // Similarly, this is the best-effort cache of the chain's header_head. This is used to + // determine how old a chunk is. We don't use the chain_head for that, because if we just + // ran header sync and are downloading the chunks for older blocks, head is behind while + // header_head is new, but we would only know that the older chunks are old because + // header_head is much newer. + chain_header_head: Tip, +} + +impl ShardsManager { + pub fn new( + clock: time::Clock, + me: Option, + epoch_manager: Arc, + shard_tracker: ShardTracker, + network_adapter: Sender, + client_adapter: Sender, + store: ReadOnlyChunksStore, + initial_chain_head: Tip, + initial_chain_header_head: Tip, + ) -> Self { + Self { + clock, + me, + store, + epoch_manager: epoch_manager.clone(), + shard_tracker, + peer_manager_adapter: network_adapter, + client_adapter, + rs: ReedSolomonWrapper::new( + epoch_manager.num_data_parts(), + epoch_manager.num_total_parts() - epoch_manager.num_data_parts(), + ), + encoded_chunks: EncodedChunksCache::new(), + requested_partial_encoded_chunks: RequestPool::new( + CHUNK_REQUEST_RETRY, + CHUNK_REQUEST_SWITCH_TO_OTHERS, + CHUNK_REQUEST_SWITCH_TO_FULL_FETCH, + CHUNK_REQUEST_RETRY_MAX, + ), + chunk_forwards_cache: lru::LruCache::new(CHUNK_FORWARD_CACHE_SIZE), + chain_head: initial_chain_head, + chain_header_head: initial_chain_header_head, + } + } + + pub fn update_chain_heads(&mut self, head: Tip, header_head: Tip) { + self.encoded_chunks.update_largest_seen_height( + head.height, + &self.requested_partial_encoded_chunks.requests, + ); + self.chain_head = head; + self.chain_header_head = header_head; + } + + fn request_partial_encoded_chunk( + &mut self, + height: BlockHeight, + ancestor_hash: &CryptoHash, + shard_id: ShardId, + chunk_hash: &ChunkHash, + force_request_full: bool, + request_own_parts_from_others: bool, + request_from_archival: bool, + ) -> Result<(), unc_chain::Error> { + let _span = tracing::debug_span!( + target: "chunks", + "request_partial_encoded_chunk", + ?chunk_hash, + ?height, + ?shard_id, + ?request_from_archival) + .entered(); + let mut bp_to_parts = HashMap::<_, Vec>::new(); + + let cache_entry = self.encoded_chunks.get(chunk_hash); + + let request_full = force_request_full + || cares_about_shard_this_or_next_epoch( + self.me.as_ref(), + ancestor_hash, + shard_id, + true, + &self.shard_tracker, + ); + + let chunk_producer_account_id = self.epoch_manager.as_ref().get_chunk_producer( + &self.epoch_manager.get_epoch_id_from_prev_block(ancestor_hash)?, + height, + shard_id, + )?; + + // In the following we compute which target accounts we should request parts and receipts from + // First we choose a shard representative target which is either the original chunk producer + // or a random block producer tracks the shard. + // If request_from_archival is true (indicating we are requesting a chunk not from the current + // or the last epoch), request all parts and receipts from the shard representative target + // For each part, if we are the part owner, we request the part from the shard representative + // target, otherwise, the part owner + // For receipts, request them from the shard representative target + // + // Also note that the target accounts decided is not necessarily the final destination + // where requests are sent. We use them to construct AccountIdOrPeerTrackingShard struct, + // which will be passed to PeerManagerActor. PeerManagerActor will try to request either + // from the target account or any eligible peer of the node (See comments in + // AccountIdOrPeerTrackingShard for when target account is used or peer is used) + + let me = self.me.as_ref(); + // A account that is either the original chunk producer or a random block producer tracking + // the shard + let shard_representative_target = if !request_own_parts_from_others + && !request_from_archival + && Some(&chunk_producer_account_id) != me + { + Some(chunk_producer_account_id) + } else { + self.get_random_target_tracking_shard(ancestor_hash, shard_id)? + }; + + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(ancestor_hash)?; + + for part_ord in 0..self.rs.total_shard_count() { + let part_ord = part_ord as u64; + if cache_entry.map_or(false, |cache_entry| cache_entry.parts.contains_key(&part_ord)) { + continue; + } + + // Note: If request_from_archival is true, we potentially call + // get_part_owner unnecessarily. It’s probably not worth optimising + // though unless you can think of a concise way to do it. + let part_owner = self.epoch_manager.get_part_owner(&epoch_id, part_ord)?; + let we_own_part = Some(&part_owner) == me; + if !request_full && !we_own_part { + continue; + } + + // This is false positive, similar to what was reported here: + // https://github.com/rust-lang/rust-clippy/issues/5940 + #[allow(clippy::if_same_then_else)] + let fetch_from = if request_from_archival { + shard_representative_target.clone() + } else if we_own_part { + // If missing own part, request it from the chunk producer / node tracking shard + shard_representative_target.clone() + } else { + Some(part_owner) + }; + + bp_to_parts.entry(fetch_from).or_default().push(part_ord); + } + + let shards_to_fetch_receipts = + // TODO: only keep shards for which we don't have receipts yet + if request_full { HashSet::new() } else { self.get_tracking_shards(ancestor_hash) }; + + // The loop below will be sending PartialEncodedChunkRequestMsg to various block producers. + // We need to send such a message to the original chunk producer if we do not have the receipts + // for some subset of shards, even if we don't need to request any parts from the original + // chunk producer. + if !shards_to_fetch_receipts.is_empty() { + bp_to_parts.entry(shard_representative_target.clone()).or_default(); + } + + let no_account_id = me.is_none(); + debug!(target: "chunks", "Will send {} requests to fetch chunk parts.", bp_to_parts.len()); + for (target_account, part_ords) in bp_to_parts { + // extra check that we are not sending request to ourselves. + if no_account_id || me != target_account.as_ref() { + let prefer_peer = request_from_archival || rand::thread_rng().gen::(); + debug!( + target: "chunks", + ?part_ords, + shard_id, + ?target_account, + prefer_peer, + "Requesting parts", + ); + + let request = PartialEncodedChunkRequestMsg { + chunk_hash: chunk_hash.clone(), + part_ords, + tracking_shards: if target_account == shard_representative_target { + shards_to_fetch_receipts.clone() + } else { + HashSet::new() + }, + }; + let target = AccountIdOrPeerTrackingShard { + account_id: target_account, + prefer_peer, + shard_id, + only_archival: request_from_archival, + min_height: height.saturating_sub(CHUNK_REQUEST_PEER_HORIZON), + }; + + self.peer_manager_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::PartialEncodedChunkRequest { + target, + request, + create_time: self.clock.now(), + }, + )); + } else { + warn!(target: "client", "{:?} requests parts {:?} for chunk {:?} from self", + me, part_ords, chunk_hash + ); + } + } + + Ok(()) + } + + /// Get a random shard block producer that is not me. + fn get_random_target_tracking_shard( + &self, + parent_hash: &CryptoHash, + shard_id: ShardId, + ) -> Result, unc_chain::Error> { + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(parent_hash).unwrap(); + let block_producers = self + .epoch_manager + .get_epoch_block_producers_ordered(&epoch_id, parent_hash)? + .into_iter() + .filter_map(|(validator_stake, is_slashed)| { + let account_id = validator_stake.take_account_id(); + if !is_slashed + && cares_about_shard_this_or_next_epoch( + Some(&account_id), + parent_hash, + shard_id, + false, + &self.shard_tracker, + ) + && self.me.as_ref() != Some(&account_id) + { + Some(account_id) + } else { + None + } + }); + + Ok(block_producers.choose(&mut rand::thread_rng())) + } + + fn get_tracking_shards(&self, parent_hash: &CryptoHash) -> HashSet { + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(parent_hash).unwrap(); + self.epoch_manager + .shard_ids(&epoch_id) + .unwrap() + .into_iter() + .filter(|chunk_shard_id| { + cares_about_shard_this_or_next_epoch( + self.me.as_ref(), + parent_hash, + *chunk_shard_id, + true, + &self.shard_tracker, + ) + }) + .collect::>() + } + + /// Check whether the node should wait for chunk parts being forwarded to it + /// The node will wait if it's a block producer or a chunk producer that is responsible + /// for producing the next chunk in this shard. + /// `prev_hash`: previous block hash of the chunk that we are requesting + /// `next_chunk_height`: height of the next chunk of the chunk that we are requesting + fn should_wait_for_chunk_forwarding( + &self, + prev_hash: &CryptoHash, + shard_id: ShardId, + next_chunk_height: BlockHeight, + ) -> Result { + // chunks will not be forwarded to non-validators + let me = match self.me.as_ref() { + None => return Ok(false), + Some(it) => it, + }; + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(prev_hash)?; + let block_producers = + self.epoch_manager.get_epoch_block_producers_ordered(&epoch_id, prev_hash)?; + for (bp, _) in block_producers { + if bp.account_id() == me { + return Ok(true); + } + } + let chunk_producer = + self.epoch_manager.get_chunk_producer(&epoch_id, next_chunk_height, shard_id)?; + if &chunk_producer == me { + return Ok(true); + } + Ok(false) + } + + /// Only marks this chunk as being requested + /// Note no requests are actually sent at this point. + fn request_chunk_single_mark_only(&mut self, chunk_header: &ShardChunkHeader) { + self.request_chunk_single(chunk_header, *chunk_header.prev_block_hash(), true) + } + + /// send partial chunk requests for one chunk + /// `chunk_header`: the chunk being requested + /// `ancestor_hash`: hash of an ancestor block of the requested chunk. + /// It must satisfy + /// 1) it is from the same epoch than `epoch_id` + /// 2) it is processed + /// If the above conditions are not met, the request will be dropped + /// `mark_only`: if true, only add the request to the pool, but do not send it. + fn request_chunk_single( + &mut self, + chunk_header: &ShardChunkHeader, + ancestor_hash: CryptoHash, + mark_only: bool, + ) { + let height = chunk_header.height_created(); + let shard_id = chunk_header.shard_id(); + let chunk_hash = chunk_header.chunk_hash(); + let _span = debug_span!( + target: "chunks", + "request_chunk_single", + ?chunk_hash, + shard_id, + height_created = height) + .entered(); + + if self.requested_partial_encoded_chunks.contains_key(&chunk_hash) { + debug!(target: "chunks", height, shard_id, ?chunk_hash, "Not requesting chunk, already being requested."); + return; + } + + if let Some(entry) = self.encoded_chunks.get(&chunk_header.chunk_hash()) { + if entry.complete { + debug!(target: "chunks", height, shard_id, ?chunk_hash, "Not requesting chunk, already complete."); + return; + } + } else { + // In all code paths that lead to this function, we have already inserted the header. + // However, if the chunk had just been processed and marked as complete, it might have + // been removed from the cache if it is out of horizon. So in this case, the chunk is + // already complete and we don't need to request anything. + debug!(target: "chunks", height, shard_id, ?chunk_hash, "Not requesting chunk, already complete and GC-ed."); + return; + } + + let prev_block_hash = *chunk_header.prev_block_hash(); + self.requested_partial_encoded_chunks.insert( + chunk_hash.clone(), + ChunkRequestInfo { + height, + prev_block_hash, + ancestor_hash, + shard_id, + last_requested: self.clock.now().into(), + added: self.clock.now().into(), + }, + ); + + if mark_only { + debug!(target: "chunks", height, shard_id, ?chunk_hash, "Marked the chunk as being requested but did not send the request yet."); + return; + } + + let fetch_from_archival = chunk_needs_to_be_fetched_from_archival( + &ancestor_hash, &self.chain_header_head.last_block_hash, + self.epoch_manager.as_ref()).unwrap_or_else(|err| { + error!(target: "chunks", "Error during requesting partial encoded chunk. Cannot determine whether to request from an archival node, defaulting to not: {}", err); + false + }); + let old_block = self.chain_header_head.last_block_hash != prev_block_hash + && self.chain_header_head.prev_block_hash != prev_block_hash; + + let should_wait_for_chunk_forwarding = + self.should_wait_for_chunk_forwarding(&ancestor_hash, chunk_header.shard_id(), chunk_header.height_created()+1).unwrap_or_else(|_| { + // ancestor_hash must be accepted because we don't request missing chunks through this + // this function for orphans + debug_assert!(false, "{:?} must be accepted", ancestor_hash); + error!(target:"chunks", "requesting chunk whose ancestor_hash {:?} is not accepted", ancestor_hash); + false + }); + + // If chunks forwarding is enabled, + // we purposely do not send chunk request messages right away for new blocks. Such requests + // will eventually be sent because of the `resend_chunk_requests` loop. However, + // we want to give some time for any `PartialEncodedChunkForward` messages to arrive + // before we send requests. + if !should_wait_for_chunk_forwarding || fetch_from_archival || old_block { + debug!(target: "chunks", height, shard_id, ?chunk_hash, "Requesting."); + let request_result = self.request_partial_encoded_chunk( + height, + &ancestor_hash, + shard_id, + &chunk_hash, + false, + old_block, + fetch_from_archival, + ); + if let Err(err) = request_result { + error!(target: "chunks", "Error during requesting partial encoded chunk: {}", err); + } + } else { + debug!(target: "chunks",should_wait_for_chunk_forwarding, fetch_from_archival, old_block, "Delaying the chunk request."); + } + } + + /// send chunk requests for some chunks in a block + /// `chunks_to_request`: chunks to request + /// `prev_hash`: hash of prev block of the block we are requesting missing chunks for + /// The function assumes the prev block is accepted + pub fn request_chunks( + &mut self, + chunks_to_request: Vec, + prev_hash: CryptoHash, + ) { + let _span = debug_span!( + target: "chunks", + "request_chunks", + ?prev_hash, + num_chunks_to_request = chunks_to_request.len()) + .entered(); + for chunk_header in chunks_to_request { + self.request_chunk_single(&chunk_header, prev_hash, false); + } + } + + /// Request chunks for an orphan block. + /// `epoch_id`: epoch_id of the orphan block + /// `ancestor_hash`: because BlockInfo for the immediate parent of an orphan block is not ready, + /// we use hash of an ancestor block of this orphan to request chunks. It must + /// satisfy + /// 1) it is from the same epoch than `epoch_id` + /// 2) it is processed + /// If the above conditions are not met, the request will be dropped + pub fn request_chunks_for_orphan( + &mut self, + chunks_to_request: Vec, + epoch_id: &EpochId, + ancestor_hash: CryptoHash, + ) { + let _span = debug_span!( + target: "chunks", + "request_chunks_for_orphan", + ?ancestor_hash, + ?epoch_id, + num_chunks_to_request = chunks_to_request.len()) + .entered(); + let ancestor_epoch_id = + unwrap_or_return!(self.epoch_manager.get_epoch_id_from_prev_block(&ancestor_hash)); + if epoch_id != &ancestor_epoch_id { + return; + } + + for chunk_header in chunks_to_request { + self.request_chunk_single(&chunk_header, ancestor_hash, false) + } + } + + /// Resends chunk requests if haven't received it within expected time. + pub fn resend_chunk_requests(&mut self) { + let _span = tracing::debug_span!( + target: "client", + "resend_chunk_requests", + header_head_height = self.chain_header_head.height, + pool_size = self.requested_partial_encoded_chunks.len()) + .entered(); + // Process chunk one part requests. + let requests = self.requested_partial_encoded_chunks.fetch(self.clock.now().into()); + for (chunk_hash, chunk_request) in requests { + let fetch_from_archival = + chunk_needs_to_be_fetched_from_archival(&chunk_request.ancestor_hash, &self.chain_header_head.last_block_hash, + self.epoch_manager.as_ref()).unwrap_or_else(|err| { + debug_assert!(false); + error!(target: "chunks", "Error during re-requesting partial encoded chunk. Cannot determine whether to request from an archival node, defaulting to not: {}", err); + false + }); + let old_block = self.chain_header_head.last_block_hash != chunk_request.prev_block_hash + && self.chain_header_head.prev_block_hash != chunk_request.prev_block_hash; + + match self.request_partial_encoded_chunk( + chunk_request.height, + &chunk_request.ancestor_hash, + chunk_request.shard_id, + &chunk_hash, + self.clock.now() - chunk_request.added + >= self.requested_partial_encoded_chunks.switch_to_full_fetch_duration, + old_block + || self.clock.now() - chunk_request.added + >= self.requested_partial_encoded_chunks.switch_to_others_duration, + fetch_from_archival, + ) { + Ok(()) => {} + Err(err) => { + debug_assert!(false); + error!(target: "chunks", "Error during requesting partial encoded chunk: {}", err); + } + } + } + } + + pub fn receipts_recipient_filter( + from_shard_id: ShardId, + tracking_shards: T, + receipts_by_shard: &HashMap>, + proofs: &[MerklePath], + ) -> Vec + where + T: IntoIterator, + { + tracking_shards + .into_iter() + .map(|to_shard_id| { + let receipts = + receipts_by_shard.get(&to_shard_id).cloned().unwrap_or_else(Vec::new); + let shard_proof = ShardProof { + from_shard_id, + to_shard_id, + proof: proofs[to_shard_id as usize].clone(), + }; + ReceiptProof(receipts, shard_proof) + }) + .collect() + } + + pub fn process_partial_encoded_chunk_request( + &mut self, + request: PartialEncodedChunkRequestMsg, + route_back: CryptoHash, + ) { + let _span = tracing::debug_span!( + target: "chunks", + "process_partial_encoded_chunk_request", + chunk_hash = %request.chunk_hash.0) + .entered(); + debug!(target: "chunks", + chunk_hash = %request.chunk_hash.0, + part_ords = ?request.part_ords, + shards = ?request.tracking_shards, + account = ?self.me.as_ref()); + + let started = self.clock.now(); + let (source, response) = self.prepare_partial_encoded_chunk_response(request); + let elapsed = (self.clock.now() - started).as_seconds_f64(); + let labels = [ + source.name_for_metrics(), + if response.parts.is_empty() && response.receipts.is_empty() { "failed" } else { "ok" }, + ]; + metrics::PARTIAL_ENCODED_CHUNK_REQUEST_PROCESSING_TIME + .with_label_values(&labels) + .observe(elapsed); + + self.peer_manager_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::PartialEncodedChunkResponse { route_back, response }, + )); + } + + /// Finds the parts and receipt proofs asked for in the request, and returns a response + /// containing whatever was found. See comment for PartialEncodedChunkResponseSource for + /// an explanation of that part of the return value. + /// Ensures the receipts in the response are in a deterministic order. + fn prepare_partial_encoded_chunk_response( + &mut self, + request: PartialEncodedChunkRequestMsg, + ) -> (PartialEncodedChunkResponseSource, PartialEncodedChunkResponseMsg) { + let (src, mut response_msg) = self.prepare_partial_encoded_chunk_response_unsorted(request); + // Note that the PartialChunks column is a write-once column, and needs + // the values to be deterministic. + response_msg.receipts.sort(); + (src, response_msg) + } + + fn prepare_partial_encoded_chunk_response_unsorted( + &mut self, + request: PartialEncodedChunkRequestMsg, + ) -> (PartialEncodedChunkResponseSource, PartialEncodedChunkResponseMsg) { + let PartialEncodedChunkRequestMsg { chunk_hash, part_ords, mut tracking_shards } = request; + let mut response = PartialEncodedChunkResponseMsg { + chunk_hash: chunk_hash.clone(), + parts: vec![], + receipts: vec![], + }; + let mut part_ords = part_ords.into_iter().collect::>(); + if part_ords.is_empty() && tracking_shards.is_empty() { + // Don't bother looking up anything if none was requested at all. + return (PartialEncodedChunkResponseSource::None, response); + } + // Try getting data from in-memory cache. + if let Some(entry) = self.encoded_chunks.get(&chunk_hash) { + Self::lookup_partial_encoded_chunk_from_cache( + &mut part_ords, + &mut tracking_shards, + &mut response, + entry, + ); + } + if part_ords.is_empty() && tracking_shards.is_empty() { + // If we found all parts and receipts, return now. + return (PartialEncodedChunkResponseSource::InMemoryCache, response); + } + + // Try fetching partial encoded chunk from storage. + if let Ok(partial_chunk) = self.store.get_partial_chunk(&chunk_hash) { + Self::lookup_partial_encoded_chunk_from_partial_chunk_storage( + part_ords, + tracking_shards, + &mut response, + &partial_chunk, + ); + // If we have found the partial chunk, then the shard chunk would not have + // anything extra (since we populate the shard chunk iff we track the shard + // but if that's the case we would've populated all parts and receipts to + // the partial chunk as well). + return (PartialEncodedChunkResponseSource::PartialChunkOnDisk, response); + } + + // If we are an archival node we might have garbage collected the + // partial chunk while we still keep the full chunk. We can get the + // chunk, recalculate the parts and respond to the request. + if let Ok(chunk) = self.store.get_chunk(&chunk_hash) { + self.lookup_partial_encoded_chunk_from_chunk_storage( + part_ords, + tracking_shards, + &mut response, + &chunk, + ); + } + + // Note that we return this source even if we didn't find anything on disk, + // because we still spent the time to look for it on disk. + (PartialEncodedChunkResponseSource::ShardChunkOnDisk, response) + } + + /// Looks up the given part_ords and tracking_shards from the cache, appending + /// any we have found into the response, and deleting those we have found from + /// part_ords and tracking_shards. + fn lookup_partial_encoded_chunk_from_cache( + part_ords: &mut HashSet, + tracking_shards: &mut HashSet, + response: &mut PartialEncodedChunkResponseMsg, + entry: &EncodedChunksCacheEntry, + ) { + part_ords.retain(|part_ord| { + if let Some(part) = entry.parts.get(part_ord) { + response.parts.push(part.clone()); + false + } else { + true + } + }); + tracking_shards.retain(|shard_id| { + if let Some(receipt_proof) = entry.receipts.get(shard_id) { + response.receipts.push(receipt_proof.clone()); + false + } else { + true + } + }); + } + + /// Looks up the given part_ords and tracking_shards from the partial chunks + /// storage, appending any we have found into the response, and deleting those we + /// have found from part_ords and tracking_shards. + pub fn lookup_partial_encoded_chunk_from_partial_chunk_storage( + part_ords: HashSet, + tracking_shards: HashSet, + response: &mut PartialEncodedChunkResponseMsg, + partial_chunk: &PartialEncodedChunk, + ) { + for part in partial_chunk.parts() { + if part_ords.contains(&part.part_ord) { + response.parts.push(part.clone()); + } + } + for receipt in partial_chunk.receipts() { + if tracking_shards.contains(&receipt.1.to_shard_id) { + response.receipts.push(receipt.clone()); + } + } + } + + /// Looks up the given part_ords and tracking_shards from the cache, appending + /// any we have found into the response. + /// + /// This requires encoding the chunk and as such is computationally + /// expensive operation. If possible, the request should be served from + /// EncodedChunksCacheEntry or PartialEncodedChunk instead. + // pub for testing + pub fn lookup_partial_encoded_chunk_from_chunk_storage( + &mut self, + part_ords: HashSet, + tracking_shards: HashSet, + response: &mut PartialEncodedChunkResponseMsg, + chunk: &ShardChunk, + ) { + let header = chunk.cloned_header(); + + // Get outgoing receipts for the chunk and construct vector of their + // proofs. + let outgoing_receipts = chunk.prev_outgoing_receipts(); + let present_receipts: HashMap = match make_outgoing_receipts_proofs( + &header, + &outgoing_receipts, + self.epoch_manager.as_ref(), + ) { + Ok(receipts) => receipts.map(|receipt| (receipt.1.to_shard_id, receipt)).collect(), + Err(e) => { + warn!(target: "chunks", "Not sending {:?}, failed to make outgoing receipts proofs: {}", chunk.chunk_hash(), e); + return; + } + }; + + // Construct EncodedShardChunk. If we earlier determined that we will + // need parity parts, instruct the constructor to calculate them as + // well. Otherwise we won’t bother. + let (parts, encoded_length) = match EncodedShardChunk::encode_transaction_receipts( + &mut self.rs, + chunk.transactions().to_vec(), + &outgoing_receipts, + ) { + Ok(result) => result, + Err(err) => { + warn!(target: "chunks", + "Not sending {:?}, failed to encode transactions and receipts: {}", + chunk.chunk_hash(), err); + return; + } + }; + if header.encoded_length() != encoded_length { + warn!(target: "chunks", + "Not sending {:?}, expected encoded length doesn’t match calculated: {} != {}", + chunk.chunk_hash(), header.encoded_length(), encoded_length); + return; + } + + let mut content = EncodedShardChunkBody { parts }; + if let Err(err) = content.reconstruct(&mut self.rs) { + warn!(target: "chunks", + "Not sending {:?}, failed to reconstruct RS parity parts: {}", + chunk.chunk_hash(), err); + return; + } + + let (encoded_merkle_root, merkle_paths) = content.get_merkle_hash_and_paths(); + if header.encoded_merkle_root() != encoded_merkle_root { + warn!(target: "chunks", + "Not sending {:?}, expected encoded Merkle root doesn’t match calculated: {} != {}", + chunk.chunk_hash(), header.encoded_merkle_root(), encoded_merkle_root); + return; + } + if merkle_paths.len() != content.parts.len() { + warn!(target: "chunks", + "Not sending {:?}, expected number of Merkle paths doesn’t match calculated: {} != {}", + chunk.chunk_hash(), merkle_paths.len(), content.parts.len()); + return; + } + for (part_ord, (part, merkle_proof)) in + content.parts.into_iter().zip(merkle_paths.into_iter()).enumerate() + { + if let Some(part) = part { + if part_ords.contains(&(part_ord as u64)) { + response.parts.push(PartialEncodedChunkPart { + part_ord: part_ord as u64, + part, + merkle_proof, + }); + } + } + } + for (shard_id, receipt_proof) in present_receipts { + if tracking_shards.contains(&shard_id) { + response.receipts.push(receipt_proof); + } + } + } + + // pub for testing + pub fn check_chunk_complete( + chunk: &mut EncodedShardChunk, + rs: &mut ReedSolomonWrapper, + ) -> ChunkStatus { + let _span = debug_span!( + target: "chunks", + "check_chunk_complete", + height_included = chunk.cloned_header().height_included(), + shard_id = chunk.cloned_header().shard_id(), + chunk_hash = ?chunk.chunk_hash()) + .entered(); + let data_parts = rs.data_shard_count(); + if chunk.content().num_fetched_parts() >= data_parts { + if let Ok(_) = chunk.content_mut().reconstruct(rs) { + let (merkle_root, merkle_paths) = chunk.content().get_merkle_hash_and_paths(); + if merkle_root == chunk.encoded_merkle_root() { + debug!(target: "chunks", "Complete"); + ChunkStatus::Complete(merkle_paths) + } else { + debug!( + target: "chunks", + ?merkle_root, + chunk_encoded_merkle_root = ?chunk.encoded_merkle_root(), + "Invalid: Wrong merkle root"); + ChunkStatus::Invalid + } + } else { + debug!(target: "chunks", "Invalid: Failed to reconstruct"); + ChunkStatus::Invalid + } + } else { + debug!(target: "chunks", num_fetched_parts = chunk.content().num_fetched_parts(), data_parts, "Incomplete"); + ChunkStatus::Incomplete + } + } + + /// Add a part to current encoded chunk stored in memory. It's present only if One Part was present and signed correctly. + fn validate_part( + &mut self, + merkle_root: MerkleHash, + part: &PartialEncodedChunkPart, + num_total_parts: usize, + ) -> Result<(), Error> { + if (part.part_ord as usize) < num_total_parts { + if !verify_path(merkle_root, &part.merkle_proof, &part.part) { + return Err(Error::InvalidMerkleProof); + } + + Ok(()) + } else { + Err(Error::InvalidChunkPartId) + } + } + + fn decode_encoded_chunk_if_complete( + &mut self, + mut encoded_chunk: EncodedShardChunk, + ) -> Result, Error> { + match ShardsManager::check_chunk_complete(&mut encoded_chunk, &mut self.rs) { + ChunkStatus::Complete(merkle_paths) => { + self.requested_partial_encoded_chunks.remove(&encoded_chunk.chunk_hash()); + match decode_encoded_chunk( + &encoded_chunk, + merkle_paths, + self.me.as_ref(), + self.epoch_manager.as_ref(), + &self.shard_tracker, + ) { + Ok(chunk) => Ok(Some(chunk)), + Err(err) => { + self.encoded_chunks.remove(&encoded_chunk.chunk_hash()); + Err(err) + } + } + } + ChunkStatus::Incomplete => Ok(None), + ChunkStatus::Invalid => { + let chunk_hash = encoded_chunk.chunk_hash(); + self.encoded_chunks.remove(&chunk_hash); + Err(Error::InvalidChunk) + } + } + } + + fn validate_partial_encoded_chunk_forward( + &mut self, + forward: &PartialEncodedChunkForwardMsg, + ) -> Result<(), Error> { + let valid_hash = forward.is_valid_hash(); // check hash + + if !valid_hash { + return Err(Error::InvalidPartMessage); + } + + // check part merkle proofs + let num_total_parts = self.rs.total_shard_count(); + for part_info in forward.parts.iter() { + self.validate_part(forward.merkle_root, part_info, num_total_parts)?; + } + + // check signature + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(&forward.prev_block_hash)?; + let valid_signature = self.epoch_manager.verify_chunk_signature_with_header_parts( + &forward.chunk_hash, + &forward.signature, + &epoch_id, + &forward.prev_block_hash, + forward.height_created, + forward.shard_id, + )?; + + if !valid_signature { + return Err(Error::InvalidChunkSignature); + } + + Ok(()) + } + + /// Gets the header associated with the chunk hash from the `encoded_chunks` cache. + /// An error is returned if the chunk is not present or the hash in the associated + /// header does not match the given hash. + fn get_partial_encoded_chunk_header( + &self, + chunk_hash: &ChunkHash, + ) -> Result { + let header = self + .encoded_chunks + .get(chunk_hash) + .map(|encoded_chunk| encoded_chunk.header.clone()) + .ok_or(Error::UnknownChunk)?; + + // Check the hashes match + if header.chunk_hash() != *chunk_hash { + byzantine_assert!(false); + return Err(Error::InvalidChunkHeader); + } + + Ok(header) + } + + fn insert_forwarded_chunk(&mut self, forward: PartialEncodedChunkForwardMsg) { + let chunk_hash = forward.chunk_hash.clone(); + let num_total_parts = self.rs.total_shard_count() as u64; + match self.chunk_forwards_cache.get_mut(&chunk_hash) { + None => { + // Never seen this chunk hash before, collect the parts and cache them + let parts = forward.parts.into_iter().filter_map(|part| { + let part_ord = part.part_ord; + if part_ord > num_total_parts { + warn!(target: "chunks", "Received chunk part with part_ord greater than the the total number of chunks"); + None + } else { + Some((part_ord, part)) + } + }).collect(); + self.chunk_forwards_cache.put(chunk_hash, parts); + } + + Some(existing_parts) => { + for part in forward.parts { + let part_ord = part.part_ord; + if part_ord > num_total_parts { + warn!(target: "chunks", "Received chunk part with part_ord greater than the the total number of chunks"); + continue; + } + existing_parts.insert(part_ord, part); + } + } + } + } + + pub fn process_partial_encoded_chunk_forward( + &mut self, + forward: PartialEncodedChunkForwardMsg, + ) -> Result<(), Error> { + let maybe_header = self + .validate_partial_encoded_chunk_forward(&forward) + .and_then(|_| self.get_partial_encoded_chunk_header(&forward.chunk_hash)); + + let header = match maybe_header { + Ok(header) => Ok(header), + Err(Error::UnknownChunk) => { + // We don't know this chunk yet; cache the forwarded part + // to be used after we get the header. + self.insert_forwarded_chunk(forward); + PARTIAL_ENCODED_CHUNK_FORWARD_CACHED_WITHOUT_HEADER.inc(); + return Ok(()); // a normal and expected case, not error + } + Err(Error::ChainError(chain_error)) => { + match chain_error { + unc_chain::Error::DBNotFoundErr(_) => { + // We can't check if this chunk came from a valid chunk producer because + // we don't know `prev_block`, however the signature is checked when + // forwarded parts are later processed as partial encoded chunks, so we + // can mark it as unknown for now. + self.insert_forwarded_chunk(forward); + PARTIAL_ENCODED_CHUNK_FORWARD_CACHED_WITHOUT_PREV_BLOCK.inc(); + return Ok(()); // a normal and expected case, not error + } + // Some other error occurred, we don't know how to handle it + _ => Err(Error::ChainError(chain_error)), + } + } + Err(err) => Err(err), + }?; + let partial_chunk = PartialEncodedChunk::V2(PartialEncodedChunkV2 { + header, + parts: forward.parts, + receipts: Vec::new(), + }); + self.process_partial_encoded_chunk(MaybeValidated::from_validated(partial_chunk))?; + Ok(()) + } + + /// Validate a chunk header + /// 1) check that the chunk header is signed by the correct chunk producer for the chunk at + /// the height for the shard + /// 2) check that the chunk header is compatible with the current protocol version + /// + // Note that this function only does partial validation. Full validation is only possible + // after the previous block of the chunk is processed. To be able to process partial encoded + // chunk messages in advance, this function tries to verify with other accepted block hash in + // the chain if the previous block hash is not accepted. Validation is only partially done + // in those cases. Full validation can be achieved by calling this function after + // the previous block hash is accepted. + // + // To achieve full validation, this function is called twice for each chunk entry + // first when the chunk entry is inserted in `encoded_chunks` + // then in `process_partial_encoded_chunk` after checking the previous block is ready + fn validate_chunk_header(&self, header: &ShardChunkHeader) -> Result<(), Error> { + let chunk_hash = header.chunk_hash(); + let _span = debug_span!(target: "chunks", "validate_chunk_header", ?chunk_hash).entered(); + // 1. check signature + // Ideally, validating the chunk header needs the previous block to be accepted already. + // However, we want to be able to validate chunk header in advance so we can save + // the corresponding parts and receipts before the previous block is processed + // We do this three layered check + // 1) if prev_block_hash is processed, we use that + // 2) if we have sent request for the chunk, we know the `ancestor_hash` from the original + // request and we know that get_epoch_id_from_prev_block(ancestor_hash) = + // get_epoch_id_from_prev_block(prev_block_hash). Thus, we can calculate epoch_id + // from ancestor_hash + // 3) otherwise, we use the current chain_head to calculate epoch id. In this case, + // we are not sure if we are using the correct epoch id, thus `epoch_id_confirmed` is false. + // And if the validation fails in this case, we actually can't say if the chunk is actually + // invalid. So we must return chain_error instead of return error + let (ancestor_hash, epoch_id, epoch_id_confirmed) = { + let prev_block_hash = *header.prev_block_hash(); + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(&prev_block_hash); + if let Ok(epoch_id) = epoch_id { + (prev_block_hash, epoch_id, true) + } else if let Some(request_info) = + self.requested_partial_encoded_chunks.get_request_info(&chunk_hash) + { + let ancestor_hash = request_info.ancestor_hash; + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(&ancestor_hash)?; + (ancestor_hash, epoch_id, true) + } else { + // we can safely unwrap here because chain head must already be accepted + let epoch_id = self + .epoch_manager + .get_epoch_id_from_prev_block(&self.chain_head.last_block_hash) + .unwrap(); + (self.chain_head.last_block_hash, epoch_id, false) + } + }; + + if !self.epoch_manager.verify_chunk_header_signature(header, &epoch_id, &ancestor_hash)? { + return if epoch_id_confirmed { + byzantine_assert!(false); + Err(Error::InvalidChunkSignature) + } else { + // we are not sure if we are using the correct epoch id for validation, so + // we can't be sure if the chunk header is actually invalid. Let's return + // DbNotFoundError for now, which means we don't have all needed information yet + Err(DBNotFoundErr(format!("block {:?}", header.prev_block_hash())).into()) + }; + } + + if !self.epoch_manager.shard_ids(&epoch_id)?.contains(&header.shard_id()) { + return if epoch_id_confirmed { + byzantine_assert!(false); + Err(Error::InvalidChunkShardId) + } else { + // we are not sure if we are using the correct epoch id for validation, so + // we can't be sure if the chunk header is actually invalid. Let's return + // DbNotFoundError for now, which means we don't have all needed information yet + Err(DBNotFoundErr(format!("block {:?}", header.prev_block_hash())).into()) + }; + } + + // 2. check protocol version + let protocol_version = self.epoch_manager.get_epoch_protocol_version(&epoch_id)?; + if header.valid_for(protocol_version) { + Ok(()) + } else if epoch_id_confirmed { + Err(Error::InvalidChunkHeader) + } else { + Err(DBNotFoundErr(format!("block {:?}", header.prev_block_hash())).into()) + } + } + + /// Inserts the header if it is not already known, and process the forwarded chunk parts cached + /// for this chunk, if any. Returns true if the header was newly inserted or forwarded parts + /// were newly processed. + fn insert_header_if_not_exists_and_process_cached_chunk_forwards( + &mut self, + header: &ShardChunkHeader, + ) -> bool { + let header_known_before = self.encoded_chunks.get(&header.chunk_hash()).is_some(); + if self.encoded_chunks.get_or_insert_from_header(header).complete { + return false; + } + if let Some(parts) = self.chunk_forwards_cache.pop(&header.chunk_hash()) { + // Note that we don't need any further validation for the forwarded part. + // The forwarded part was earlier validated via validate_partial_encoded_chunk_forward, + // which checks the part against the merkle root in the forward message, and the merkle + // root is checked against the chunk hash in the forward message, and that chunk hash + // is used to identify the chunk. Furthermore, it's OK to directly use the header if + // it is the first time we learn of the header here, because later when we call + // try_process_chunk_parts_and_receipts, we will perform a header validation if we + // didn't already. + self.encoded_chunks.merge_in_partial_encoded_chunk(&PartialEncodedChunkV2 { + header: header.clone(), + parts: parts.into_values().collect(), + receipts: vec![], + }); + return true; + } + !header_known_before + } + + /// Processes a partial encoded chunk message, which means + /// 1) Checks that the partial encoded chunk message is valid, including checking + /// header, parts and receipts + /// 2) If the chunk message is valid, save the parts and receipts in cache + /// 3) Forwards newly received owned parts to other validators, if any + /// 4) Processes forwarded chunk parts that haven't been processed yet + /// 5) Checks if the chunk has all parts and receipts, if so and if the node cares about the shard, + /// decodes and persists the full chunk + /// + /// Params + /// `partial_encoded_chunk`: the partial encoded chunk needs to be processed. `MaybeValidated` + /// denotes whether the chunk header has been validated or not + /// + /// Returns + /// ProcessPartialEncodedChunkResult::Known: if all information in + /// the `partial_encoded_chunk` is already known and no further processing is needed + /// ProcessPartialEncodedChunkResult::NeedBlock: if the previous block is needed + /// to finish processing + /// ProcessPartialEncodedChunkResult::NeedMorePartsOrReceipts: if more parts and receipts + /// are needed for processing the full chunk + /// ProcessPartialEncodedChunkResult::HaveAllPartsAndReceipts: if all parts and + /// receipts in the chunk are received and the chunk has been processed. + pub fn process_partial_encoded_chunk( + &mut self, + partial_encoded_chunk: MaybeValidated, + ) -> Result { + let partial_encoded_chunk = + partial_encoded_chunk.map(|chunk| PartialEncodedChunkV2::from(chunk)); + let header = &partial_encoded_chunk.header; + let chunk_hash = header.chunk_hash(); + let _span = debug_span!( + target: "chunks", + "process_partial_encoded_chunk", + ?chunk_hash, + shard_id = header.shard_id(), + height_created = header.height_created(), + height_included = header.height_included()) + .entered(); + debug!( + target: "chunks", + parts = ?partial_encoded_chunk.get_inner().parts.iter().map(|p| p.part_ord).collect::>(), + "Process partial encoded chunk"); + // Verify the partial encoded chunk is valid and worth processing + // 1.a Leave if we received known chunk + if let Some(entry) = self.encoded_chunks.get(&chunk_hash) { + if entry.complete { + return Ok(ProcessPartialEncodedChunkResult::Known); + } + debug!(target: "chunks", num_parts_in_cache = entry.parts.len(), total_needed = self.rs.data_shard_count()); + } else { + debug!(target: "chunks", num_parts_in_cache = 0, total_needed = self.rs.data_shard_count()); + } + + // 1.b Checking chunk height + let chunk_requested = self.requested_partial_encoded_chunks.contains_key(&chunk_hash); + if !chunk_requested { + if !self.encoded_chunks.height_within_horizon(header.height_created()) { + return Err(Error::ChainError(unc_chain::Error::InvalidChunkHeight)); + } + // We shouldn't process unrequested chunk if we have seen one with same (height_created + shard_id) but different chunk_hash + if let Some(hash) = self + .encoded_chunks + .get_chunk_hash_by_height_and_shard(header.height_created(), header.shard_id()) + { + if hash != &chunk_hash { + warn!(target: "client", "Rejecting unrequested chunk {:?}, height {}, shard_id {}, because of having {:?}", chunk_hash, header.height_created(), header.shard_id(), hash); + return Err(Error::DuplicateChunkHeight); + } + } + } + + // 1.c checking header validity + match partial_encoded_chunk + .validate_with(|pec| self.validate_chunk_header(&pec.header).map(|()| true)) + { + Err(Error::ChainError(chain_error)) => match chain_error { + // validate_chunk_header returns DBNotFoundError if the previous block is not ready + // in this case, we return NeedBlock instead of error + unc_chain::Error::DBNotFoundErr(_) => { + debug!(target:"client", "Dropping partial encoded chunk {:?} height {}, shard_id {} because we don't have enough information to validate it", + header.chunk_hash(), header.height_created(), header.shard_id()); + return Ok(ProcessPartialEncodedChunkResult::NeedBlock); + } + _ => return Err(chain_error.into()), + }, + Err(err) => return Err(err), + Ok(_) => (), + } + let partial_encoded_chunk = partial_encoded_chunk.as_ref().into_inner(); + + // 1.d Checking part_ords' validity + let num_total_parts = self.rs.total_shard_count(); + for part_info in partial_encoded_chunk.parts.iter() { + // TODO: only validate parts we care about + // https://github.com/utnet-org/utility/issues/5885 + self.validate_part(header.encoded_merkle_root(), part_info, num_total_parts)?; + } + + // 1.e Checking receipts validity + for proof in partial_encoded_chunk.receipts.iter() { + // TODO: only validate receipts we care about + // https://github.com/utnet-org/utility/issues/5885 + // we can't simply use prev_block_hash to check if the node tracks this shard or not + // because prev_block_hash may not be ready + if !proof.verify_against_receipt_root(header.prev_outgoing_receipts_root()) { + byzantine_assert!(false); + return Err(Error::ChainError(unc_chain::Error::InvalidReceiptsProof)); + } + } + + // 2. Consider it valid; merge parts and receipts included in the partial encoded chunk + // into chunk cache + let new_part_ords = + self.encoded_chunks.merge_in_partial_encoded_chunk(partial_encoded_chunk); + + // 3. Forward my parts to others tracking this chunk's shard + // It's possible that the previous block has not been processed yet. We will want to + // forward the chunk parts in this case, so we try our best to estimate current epoch id + // using the chain head. At epoch boundary, it could happen that this epoch id is not the + // actual epoch of the block, which is ok. In the worst case, chunk parts are not forwarded to the + // the right block producers, which may make validators wait for chunks for a little longer, + // but it doesn't affect the correctness of the protocol. + if let Ok(epoch_id) = self + .epoch_manager + .get_epoch_id_from_prev_block(&partial_encoded_chunk.header.prev_block_hash()) + { + self.send_partial_encoded_chunk_to_chunk_trackers( + partial_encoded_chunk, + new_part_ords, + &epoch_id, + &partial_encoded_chunk.header.prev_block_hash(), + )?; + } else { + let epoch_id = self + .epoch_manager + .get_epoch_id_from_prev_block(&self.chain_head.last_block_hash)?; + self.send_partial_encoded_chunk_to_chunk_trackers( + partial_encoded_chunk, + new_part_ords, + &epoch_id, + &self.chain_head.last_block_hash.clone(), + )?; + }; + + // 4. Process the forwarded parts in chunk_forwards_cache. + self.insert_header_if_not_exists_and_process_cached_chunk_forwards(header); + + // 5. Check if the chunk is complete; requesting more if not. + let result = self.try_process_chunk_parts_and_receipts(header)?; + match result { + ProcessPartialEncodedChunkResult::NeedMorePartsOrReceipts => { + // This may be the first time we see this chunk, so mark it in the request pool. + // If it's not already requested for, next time we resend requests we would + // request the chunk. + self.request_chunk_single_mark_only(header); + } + _ => {} + } + Ok(result) + } + + pub fn process_partial_encoded_chunk_response( + &mut self, + response: PartialEncodedChunkResponseMsg, + ) -> Result<(), Error> { + let header = self.get_partial_encoded_chunk_header(&response.chunk_hash)?; + let partial_chunk = PartialEncodedChunk::new(header, response.parts, response.receipts); + // We already know the header signature is valid because we read it from the + // shard manager. + self.process_partial_encoded_chunk(MaybeValidated::from_validated(partial_chunk))?; + Ok(()) + } + + /// Let the ShardsManager know about the chunk header, when encountering that chunk header + /// from the block and the chunk is possibly not yet known to the ShardsManager. + pub fn process_chunk_header_from_block( + &mut self, + header: &ShardChunkHeader, + ) -> Result<(), Error> { + if self.insert_header_if_not_exists_and_process_cached_chunk_forwards(header) { + self.try_process_chunk_parts_and_receipts(header)?; + } + Ok(()) + } + + /// Checks if the chunk has all parts and receipts, if so and if the node cares about the shard, + /// decodes and persists the full chunk + /// `header`: header of the chunk. It must be known by `ShardsManager`, either + /// by previous call to `process_partial_encoded_chunk` or `request_partial_encoded_chunk` + fn try_process_chunk_parts_and_receipts( + &mut self, + header: &ShardChunkHeader, + ) -> Result { + let chunk_hash = header.chunk_hash(); + let _span = debug_span!( + target: "chunks", + "try_process_chunk_parts_and_receipts", + height_included = header.height_included(), + ?chunk_hash) + .entered(); + // The logic from now on requires previous block is processed because + // calculating owner parts requires that, so we first check + // whether prev_block_hash is in the chain, if not, returns NeedBlock + let prev_block_hash = header.prev_block_hash(); + let epoch_id = match self.epoch_manager.get_epoch_id_from_prev_block(&prev_block_hash) { + Ok(epoch_id) => epoch_id, + Err(_) => { + debug!(target: "chunks", ?prev_block_hash, "NeedBlock"); + return Ok(ProcessPartialEncodedChunkResult::NeedBlock); + } + }; + // check the header exists in encoded_chunks and validate it again (full validation) + // now that prev_block is processed + if let Some(chunk_entry) = self.encoded_chunks.get(&chunk_hash) { + if !chunk_entry.header_fully_validated { + let res = self.validate_chunk_header(header); + match res { + Ok(()) => { + self.encoded_chunks.mark_entry_validated(&chunk_hash); + } + Err(err) => { + return match err { + Error::ChainError(chain_error) => { + debug!(target: "chunks", ?chain_error); + Err(chain_error.into()) + } + err => { + // the chunk header is invalid + // remove this entry from the cache and remove the request from the request pool + debug!(target: "chunks", ?err, "Chunk header is invalid"); + self.encoded_chunks.remove(&chunk_hash); + self.requested_partial_encoded_chunks.remove(&chunk_hash); + Err(err) + } + }; + } + } + } + } else { + debug!(target: "chunks", "UnknownChunk"); + return Err(Error::UnknownChunk); + } + + // Now check whether we have all parts and receipts needed for the given chunk + // Note that have_all_parts and have_all_receipts don't mean that we have every part and receipt + // in this chunk, it simply means that we have the parts and receipts that we need for this + // chunk. See comments in has_all_parts and has_all_receipts to see the conditions. + // we can safely unwrap here because we already checked that chunk_hash exist in encoded_chunks + let entry = self.encoded_chunks.get(&chunk_hash).unwrap(); + let have_all_parts = self.has_all_parts(&prev_block_hash, entry)?; + let have_all_receipts = self.has_all_receipts(&prev_block_hash, entry)?; + + let can_reconstruct = entry.parts.len() >= self.epoch_manager.num_data_parts(); + let chunk_producer = self.epoch_manager.get_chunk_producer( + &epoch_id, + header.height_created(), + header.shard_id(), + )?; + + if have_all_parts { + if self.encoded_chunks.mark_chunk_for_inclusion(&chunk_hash) { + self.client_adapter.send(ShardsManagerResponse::ChunkHeaderReadyForInclusion { + chunk_header: header.clone(), + chunk_producer, + }); + } + } + // we can safely unwrap here because we already checked that chunk_hash exist in encoded_chunks + let entry = self.encoded_chunks.get(&chunk_hash).unwrap(); + + let cares_about_shard = cares_about_shard_this_or_next_epoch( + self.me.as_ref(), + &prev_block_hash, + header.shard_id(), + true, + &self.shard_tracker, + ); + + debug!(target: "chunks", cares_about_shard, can_reconstruct, have_all_parts, have_all_receipts); + if !cares_about_shard && have_all_parts && have_all_receipts { + // If we don't care about the shard, we only need the parts and the receipts that we + // own, before marking the chunk as completed. + let partial_chunk = make_partial_encoded_chunk_from_owned_parts_and_needed_receipts( + header, + entry.parts.values(), + entry.receipts.values(), + self.me.as_ref(), + self.epoch_manager.as_ref(), + &self.shard_tracker, + ); + + self.complete_chunk(partial_chunk, None); + return Ok(ProcessPartialEncodedChunkResult::HaveAllPartsAndReceipts); + } + + // If we can reconstruct the chunk, then all parts and receipts are available so we can + // always complete the chunk. + if can_reconstruct { + let protocol_version = self.epoch_manager.get_epoch_protocol_version(&epoch_id)?; + let mut encoded_chunk = EncodedShardChunk::from_header( + header.clone(), + self.rs.total_shard_count(), + protocol_version, + ); + + for (part_ord, part_entry) in entry.parts.iter() { + encoded_chunk.content_mut().parts[*part_ord as usize] = + Some(part_entry.part.clone()); + } + + let (shard_chunk, partial_chunk) = self + .decode_encoded_chunk_if_complete(encoded_chunk)? + .expect("decoding shouldn't fail"); + + // For consistency, only persist shard_chunk if we actually care about the shard. + // Don't persist if we don't care about the shard, even if we accidentally got enough + // parts to reconstruct the full shard. + if cares_about_shard { + self.complete_chunk(partial_chunk, Some(shard_chunk)); + } else { + self.complete_chunk(partial_chunk, None); + } + return Ok(ProcessPartialEncodedChunkResult::HaveAllPartsAndReceipts); + } + Ok(ProcessPartialEncodedChunkResult::NeedMorePartsOrReceipts) + } + + /// A helper function to be called after a chunk is considered complete + fn complete_chunk( + &mut self, + partial_chunk: PartialEncodedChunk, + shard_chunk: Option, + ) { + let _span = debug_span!(target: "chunks", "complete_chunk").entered(); + let chunk_hash = partial_chunk.chunk_hash(); + self.encoded_chunks.mark_entry_complete(&chunk_hash); + self.encoded_chunks.remove_from_cache_if_outside_horizon(&chunk_hash); + self.requested_partial_encoded_chunks.remove(&chunk_hash); + debug!(target: "chunks", "Completed chunk {:?}", chunk_hash); + self.client_adapter + .send(ShardsManagerResponse::ChunkCompleted { partial_chunk, shard_chunk }); + } + + /// Try to process chunks in the chunk cache whose previous block hash is `prev_block_hash` and + /// who are not marked as complete yet + /// This function is needed because chunks in chunk cache will only be marked as complete after + /// the previous block is accepted. So we need to check if there are any chunks can be marked as + /// complete when a new block is accepted. + pub fn check_incomplete_chunks(&mut self, prev_block_hash: &CryptoHash) { + let _span = + debug_span!(target: "chunks", "check_incomplete_chunks", ?prev_block_hash).entered(); + let mut chunks_to_process = vec![]; + if let Some(chunk_hashes) = self.encoded_chunks.get_incomplete_chunks(prev_block_hash) { + for chunk_hash in chunk_hashes { + debug!(target: "chunks", ?chunk_hash, "incomplete chunk"); + if let Some(entry) = self.encoded_chunks.get(chunk_hash) { + chunks_to_process.push(entry.header.clone()); + } + } + } + for header in chunks_to_process { + debug!(target: "chunks", + chunk_hash = ?header.chunk_hash(), + ?prev_block_hash, + "try to process incomplete chunk"); + if let Err(err) = self.try_process_chunk_parts_and_receipts(&header) { + error!(target:"chunks", "unexpected error processing orphan chunk {:?}", err) + } + } + } + + /// Send the parts of the partial_encoded_chunk that are owned by `self.me` to the + /// other validators that are tracking the shard. + fn send_partial_encoded_chunk_to_chunk_trackers( + &mut self, + partial_encoded_chunk: &PartialEncodedChunkV2, + part_ords: HashSet, + epoch_id: &EpochId, + lastest_block_hash: &CryptoHash, + ) -> Result<(), Error> { + let me = match self.me.as_ref() { + Some(me) => me, + None => return Ok(()), + }; + let owned_parts: Vec<_> = partial_encoded_chunk + .parts + .iter() + .filter(|part| { + part_ords.contains(&part.part_ord) + && self + .epoch_manager + .get_part_owner(epoch_id, part.part_ord) + .map_or(false, |owner| &owner == me) + }) + .cloned() + .collect(); + + if owned_parts.is_empty() { + return Ok(()); + } + + let forward = PartialEncodedChunkForwardMsg::from_header_and_parts( + &partial_encoded_chunk.header, + owned_parts, + ); + + let block_producers = + self.epoch_manager.get_epoch_block_producers_ordered(&epoch_id, lastest_block_hash)?; + let current_chunk_height = partial_encoded_chunk.header.height_created(); + let mut next_chunk_producers = self + .epoch_manager + .shard_ids(&epoch_id)? + .into_iter() + .map(|shard_id| { + self.epoch_manager.get_chunk_producer(&epoch_id, current_chunk_height + 1, shard_id) + }) + .collect::, _>>()?; + next_chunk_producers.remove(me); + for (bp, _) in block_producers { + let bp_account_id = bp.take_account_id(); + // no need to send anything to myself + if me == &bp_account_id { + continue; + } + next_chunk_producers.remove(&bp_account_id); + + // Technically, here we should check if the block producer actually cares about the shard. + // We don't because with the current implementation, we force all validators to track all + // shards by making their config tracking all shards. + // See https://github.com/utnet-org/utility/issues/7388 + self.peer_manager_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::PartialEncodedChunkForward { + account_id: bp_account_id, + forward: forward.clone(), + }, + )); + } + + // We also forward chunk parts to incoming chunk producers because we want them to be able + // to produce the next chunk without delays. For the same reason as above, we don't check if they + // actually track this shard. + for next_chunk_producer in next_chunk_producers { + self.peer_manager_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::PartialEncodedChunkForward { + account_id: next_chunk_producer, + forward: forward.clone(), + }, + )); + } + + Ok(()) + } + + /// Returns true if we have all the necessary receipts for this chunk entry to process it. + /// NOTE: this doesn't mean that we got *all* the receipts. + /// It means that we have all receipts included in this chunk sending to the shards we track. + fn has_all_receipts( + &self, + prev_block_hash: &CryptoHash, + chunk_entry: &EncodedChunksCacheEntry, + ) -> Result { + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(prev_block_hash)?; + for shard_id in self.epoch_manager.shard_ids(&epoch_id)? { + if !chunk_entry.receipts.contains_key(&shard_id) { + if need_receipt(prev_block_hash, shard_id, self.me.as_ref(), &self.shard_tracker) { + return Ok(false); + } + } + } + Ok(true) + } + + /// Returns true if we have all the parts that are needed to validate the block. + /// NOTE: this doesn't mean that we got *all* the parts (as given verifier only needs the ones + /// for which it is the 'owner'). + fn has_all_parts( + &self, + prev_block_hash: &CryptoHash, + chunk_entry: &EncodedChunksCacheEntry, + ) -> Result { + for part_ord in 0..self.rs.total_shard_count() { + let part_ord = part_ord as u64; + if !chunk_entry.parts.contains_key(&part_ord) { + if need_part( + prev_block_hash, + part_ord, + self.me.as_ref(), + self.epoch_manager.as_ref(), + )? { + return Ok(false); + } + } + } + Ok(true) + } + + pub fn create_encoded_shard_chunk( + prev_block_hash: CryptoHash, + prev_state_root: StateRoot, + prev_outcome_root: CryptoHash, + height: u64, + shard_id: ShardId, + prev_gas_used: Gas, + gas_limit: Gas, + prev_balance_burnt: Balance, + prev_validator_power_proposals: Vec, + prev_validator_frozen_proposals: Vec, + transactions: Vec, + prev_outgoing_receipts: &[Receipt], + prev_outgoing_receipts_root: CryptoHash, + tx_root: CryptoHash, + signer: &dyn ValidatorSigner, + rs: &mut ReedSolomonWrapper, + protocol_version: ProtocolVersion, + ) -> Result<(EncodedShardChunk, Vec), Error> { + EncodedShardChunk::new( + prev_block_hash, + prev_state_root, + prev_outcome_root, + height, + shard_id, + rs, + prev_gas_used, + gas_limit, + prev_balance_burnt, + tx_root, + prev_validator_power_proposals, + prev_validator_frozen_proposals, + transactions, + prev_outgoing_receipts, + prev_outgoing_receipts_root, + signer, + protocol_version, + ) + .map_err(|err| err.into()) + } + + pub fn distribute_encoded_chunk( + &mut self, + partial_chunk: PartialEncodedChunk, + encoded_chunk: EncodedShardChunk, + merkle_paths: &Vec, + outgoing_receipts: Vec, + ) -> Result<(), Error> { + let shard_id = encoded_chunk.shard_id(); + let _timer = metrics::DISTRIBUTE_ENCODED_CHUNK_TIME + .with_label_values(&[&shard_id.to_string()]) + .start_timer(); + // TODO: if the number of validators exceeds the number of parts, this logic must be changed + let chunk_header = encoded_chunk.cloned_header(); + let prev_block_hash = chunk_header.prev_block_hash(); + let _span = tracing::debug_span!( + target: "client", + "distribute_encoded_chunk", + ?prev_block_hash, + ?shard_id) + .entered(); + + let mut block_producer_mapping = HashMap::new(); + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(&prev_block_hash)?; + for part_ord in 0..self.rs.total_shard_count() { + let part_ord = part_ord as u64; + let to_whom = self.epoch_manager.get_part_owner(&epoch_id, part_ord).unwrap(); + + let entry = block_producer_mapping.entry(to_whom).or_insert_with(Vec::new); + entry.push(part_ord); + } + + let receipt_proofs = make_outgoing_receipts_proofs( + &chunk_header, + &outgoing_receipts, + self.epoch_manager.as_ref(), + )? + .map(Arc::new) + .collect::>(); + for (to_whom, part_ords) in block_producer_mapping { + let part_receipt_proofs = receipt_proofs + .iter() + .filter(|proof| { + let proof_shard_id = proof.1.to_shard_id; + cares_about_shard_this_or_next_epoch( + Some(&to_whom), + &prev_block_hash, + proof_shard_id, + false, + &self.shard_tracker, + ) + }) + .cloned() + .collect(); + + let partial_encoded_chunk = encoded_chunk + .create_partial_encoded_chunk_with_arc_receipts( + part_ords, + part_receipt_proofs, + &merkle_paths, + ); + + if Some(&to_whom) != self.me.as_ref() { + self.peer_manager_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::PartialEncodedChunkMessage { + account_id: to_whom.clone(), + partial_encoded_chunk, + }, + )); + } + } + + // Add it to the set of chunks to be included in the next block + self.encoded_chunks.merge_in_partial_encoded_chunk(&partial_chunk.into()); + self.encoded_chunks.mark_chunk_for_inclusion(&chunk_header.chunk_hash()); + + Ok(()) + } + + pub fn handle_client_request(&mut self, request: ShardsManagerRequestFromClient) { + let _span = tracing::debug_span!( + target: "chunks", + "shards_manager_request_from_client", + "type" = <&'static str>::from(&request) + ) + .entered(); + match request { + ShardsManagerRequestFromClient::ProcessChunkHeaderFromBlock(chunk_header) => { + if let Err(e) = self.process_chunk_header_from_block(&chunk_header) { + warn!(target: "chunks", "Error processing chunk header from block: {:?}", e); + } + } + ShardsManagerRequestFromClient::UpdateChainHeads { head, header_head } => { + self.update_chain_heads(head, header_head) + } + ShardsManagerRequestFromClient::DistributeEncodedChunk { + partial_chunk, + encoded_chunk, + merkle_paths, + outgoing_receipts, + } => { + if let Err(e) = self.distribute_encoded_chunk( + partial_chunk, + encoded_chunk, + &merkle_paths, + outgoing_receipts, + ) { + warn!(target: "chunks", "Error distributing encoded chunk: {:?}", e); + } + } + ShardsManagerRequestFromClient::RequestChunks { chunks_to_request, prev_hash } => { + self.request_chunks(chunks_to_request, prev_hash) + } + ShardsManagerRequestFromClient::RequestChunksForOrphan { + chunks_to_request, + epoch_id, + ancestor_hash, + } => self.request_chunks_for_orphan(chunks_to_request, &epoch_id, ancestor_hash), + ShardsManagerRequestFromClient::CheckIncompleteChunks(prev_block_hash) => { + self.check_incomplete_chunks(&prev_block_hash) + } + } + } + + pub fn handle_network_request(&mut self, request: ShardsManagerRequestFromNetwork) { + let _span = tracing::debug_span!( + target: "chunks", + "shards_manager_request_from_network", + "type" = <&'static str>::from(&request) + ) + .entered(); + match request { + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunk(partial_encoded_chunk) => { + if let Err(e) = self.process_partial_encoded_chunk(partial_encoded_chunk.into()) { + warn!(target: "chunks", "Error processing partial encoded chunk: {:?}", e); + } + } + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkForward( + partial_encoded_chunk_forward, + ) => { + if let Err(e) = + self.process_partial_encoded_chunk_forward(partial_encoded_chunk_forward) + { + warn!(target: "chunks", "Error processing partial encoded chunk forward: {:?}", e); + } + } + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkResponse { + partial_encoded_chunk_response, + received_time, + } => { + PARTIAL_ENCODED_CHUNK_RESPONSE_DELAY + .observe((self.clock.now() - received_time).as_seconds_f64()); + if let Err(e) = + self.process_partial_encoded_chunk_response(partial_encoded_chunk_response) + { + warn!(target: "chunks", "Error processing partial encoded chunk response: {:?}", e); + } + } + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkRequest { + partial_encoded_chunk_request, + route_back, + } => { + self.process_partial_encoded_chunk_request( + partial_encoded_chunk_request, + route_back, + ); + } + } + } +} + +/// Indicates where we fetched the response to a PartialEncodedChunkRequest. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PartialEncodedChunkResponseSource { + /// No lookup was performed. + None, + /// We only had to look into the in-memory partial chunk cache. + InMemoryCache, + /// We had to look at the PartialChunk column on disk. + PartialChunkOnDisk, + /// We had to look at the ShardChunk column on disk, and if we found it, + /// reconstructed the partial chunk from it. + ShardChunkOnDisk, +} + +impl PartialEncodedChunkResponseSource { + /// Returns the name to be used for exporting prometheus metrics. + pub fn name_for_metrics(&self) -> &'static str { + match self { + PartialEncodedChunkResponseSource::None => "none", + PartialEncodedChunkResponseSource::InMemoryCache => "cache", + PartialEncodedChunkResponseSource::PartialChunkOnDisk => "partial", + PartialEncodedChunkResponseSource::ShardChunkOnDisk => "chunk", + } + } +} + +#[cfg(test)] +mod test { + mod basic; + mod multi; + + use assert_matches::assert_matches; + use unc_async::messaging::IntoSender; + use unc_async::time::FakeClock; + use unc_epoch_manager::shard_tracker::TrackedConfig; + use unc_epoch_manager::test_utils::setup_epoch_manager_with_block_and_chunk_producers; + use unc_network::test_utils::MockPeerManagerAdapter; + use unc_network::types::NetworkRequests; + use unc_primitives::block::Tip; + use unc_primitives::hash::{hash, CryptoHash}; + use unc_primitives::types::EpochId; + use unc_store::test_utils::create_test_store; + use std::sync::Arc; + + use super::*; + use crate::logic::persist_chunk; + use crate::test_utils::*; + + /// should not request partial encoded chunk from self + #[test] + fn test_request_partial_encoded_chunk_from_self() { + let mock_tip = Tip { + height: 0, + last_block_hash: CryptoHash::default(), + prev_block_hash: CryptoHash::default(), + epoch_id: EpochId::default(), + next_epoch_id: EpochId::default(), + }; + let store = create_test_store(); + let epoch_manager = setup_epoch_manager_with_block_and_chunk_producers( + store.clone(), + vec!["test".parse().unwrap()], + vec![], + 1, + 2, + ); + let epoch_manager = Arc::new(epoch_manager.into_handle()); + let shard_tracker = ShardTracker::new(TrackedConfig::AllShards, epoch_manager.clone()); + let network_adapter = Arc::new(MockPeerManagerAdapter::default()); + let client_adapter = Arc::new(MockClientAdapterForShardsManager::default()); + let clock = FakeClock::default(); + let mut shards_manager = ShardsManager::new( + clock.clock(), + Some("test".parse().unwrap()), + epoch_manager, + shard_tracker, + network_adapter.as_sender(), + client_adapter.as_sender(), + ReadOnlyChunksStore::new(store), + mock_tip.clone(), + mock_tip, + ); + let added = clock.now().into(); + shards_manager.requested_partial_encoded_chunks.insert( + ChunkHash(hash(&[1])), + ChunkRequestInfo { + height: 0, + ancestor_hash: Default::default(), + prev_block_hash: Default::default(), + shard_id: 0, + added, + last_requested: added, + }, + ); + clock.advance(CHUNK_REQUEST_RETRY * 2); + shards_manager.resend_chunk_requests(); + + // For the chunks that would otherwise be requested from self we expect a request to be + // sent to any peer tracking shard + + let msg = network_adapter.requests.read().unwrap()[0].as_network_requests_ref().clone(); + if let NetworkRequests::PartialEncodedChunkRequest { target, .. } = msg { + assert!(target.account_id == None); + } else { + println!("{:?}", network_adapter.requests.read().unwrap()); + assert!(false); + }; + } + + #[test] + fn test_resend_chunk_requests() { + // Test that resending chunk requests won't request for parts the node already received + let mut fixture = ChunkTestFixture::new(true, 3, 6, 1, true); + let clock = FakeClock::default(); + let mut shards_manager = ShardsManager::new( + clock.clock(), + Some(fixture.mock_shard_tracker.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + // process chunk part 0 + let partial_encoded_chunk = fixture.make_partial_encoded_chunk(&[0]); + let result = shards_manager + .process_partial_encoded_chunk(MaybeValidated::from(partial_encoded_chunk)) + .unwrap(); + assert_matches!(result, ProcessPartialEncodedChunkResult::NeedBlock); + + // should not request part 0 + shards_manager.request_chunk_single( + &fixture.mock_chunk_header, + CryptoHash::default(), + false, + ); + let collect_request_parts = |fixture: &mut ChunkTestFixture| -> HashSet { + let mut parts = HashSet::new(); + while let Some(r) = fixture.mock_network.pop() { + match r.as_network_requests_ref() { + NetworkRequests::PartialEncodedChunkRequest { request, .. } => { + for part_ord in &request.part_ords { + parts.insert(*part_ord); + } + } + _ => {} + } + } + parts + }; + let requested_parts = collect_request_parts(&mut fixture); + assert_eq!( + requested_parts, + (1..fixture.mock_chunk_parts.len() as u64).collect::>() + ); + + // process chunk part 1 + let partial_encoded_chunk = fixture.make_partial_encoded_chunk(&[1]); + let result = shards_manager + .process_partial_encoded_chunk(MaybeValidated::from(partial_encoded_chunk)) + .unwrap(); + assert_matches!(result, ProcessPartialEncodedChunkResult::NeedBlock); + + // resend request and check chunk part 0 and 1 are not requested again + clock.advance(CHUNK_REQUEST_RETRY * 2); + shards_manager.resend_chunk_requests(); + + let requested_parts = collect_request_parts(&mut fixture); + assert_eq!( + requested_parts, + (2..fixture.mock_chunk_parts.len() as u64).collect::>() + ); + + // immediately resend chunk requests + // this should not send any new requests because it doesn't pass the time check + shards_manager.resend_chunk_requests(); + let requested_parts = collect_request_parts(&mut fixture); + assert_eq!(requested_parts, HashSet::new()); + } + + #[test] + fn test_invalid_chunk() { + // Test that process_partial_encoded_chunk will reject invalid chunk + let fixture = ChunkTestFixture::default(); + let mut shards_manager = ShardsManager::new( + FakeClock::default().clock(), + Some(fixture.mock_shard_tracker.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + + // part id > num parts + let mut partial_encoded_chunk = fixture.make_partial_encoded_chunk(&[0]); + if let PartialEncodedChunk::V2(ref mut chunk) = partial_encoded_chunk { + chunk.parts[0].part_ord = fixture.mock_chunk_parts.len() as u64; + } + let result = shards_manager + .process_partial_encoded_chunk(MaybeValidated::from(partial_encoded_chunk)); + assert_matches!(result, Err(Error::InvalidChunkPartId)); + + // TODO: add more test cases + } + + #[test] + fn test_chunk_forwarding_dedup() { + // Tests that we only forward a chunk if it's the first time we receive it. + let fixture = ChunkTestFixture::default(); + let mut shards_manager = ShardsManager::new( + FakeClock::default().clock(), + Some(fixture.mock_chunk_part_owner.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + let count_num_forward_msgs = |fixture: &ChunkTestFixture| { + fixture + .mock_network + .requests + .read() + .unwrap() + .iter() + .filter(|request| match request.as_network_requests_ref() { + NetworkRequests::PartialEncodedChunkForward { .. } => true, + _ => false, + }) + .count() + }; + let non_owned_part_ords: Vec = (0..(fixture.epoch_manager.num_total_parts() as u64)) + .filter(|ord| !fixture.mock_part_ords.contains(ord)) + .collect(); + // Received 3 partial encoded chunks; the owned part is received 3 times, but should + // only forward the part on receiving it the first time. + let mut partial_encoded_chunks = vec![ + fixture + .make_partial_encoded_chunk(&[non_owned_part_ords[0], fixture.mock_part_ords[0]]), + fixture + .make_partial_encoded_chunk(&[non_owned_part_ords[1], fixture.mock_part_ords[0]]), + fixture + .make_partial_encoded_chunk(&[non_owned_part_ords[2], fixture.mock_part_ords[0]]), + ]; + shards_manager + .process_partial_encoded_chunk(MaybeValidated::from(partial_encoded_chunks.remove(0))) + .unwrap(); + let num_forward_msgs_after_first_receiving = count_num_forward_msgs(&fixture); + assert!(num_forward_msgs_after_first_receiving > 0); + shards_manager + .process_partial_encoded_chunk(MaybeValidated::from(partial_encoded_chunks.remove(0))) + .unwrap(); + shards_manager + .process_partial_encoded_chunk(MaybeValidated::from(partial_encoded_chunks.remove(0))) + .unwrap(); + let num_forward_msgs_after_receiving_duplicates = count_num_forward_msgs(&fixture); + assert_eq!( + num_forward_msgs_after_receiving_duplicates, + num_forward_msgs_after_first_receiving + ); + } + + #[derive(PartialEq, Eq, Debug)] + struct RequestChunksResult { + marked_as_requested: bool, + sent_request_message_immediately: bool, + sent_request_message_after_timeout: bool, + } + + /// Make ShardsManager request a chunk when run as the given account ID. + fn run_request_chunks_with_account( + fixture: &ChunkTestFixture, + account_id: Option, + ) -> RequestChunksResult { + let clock = FakeClock::default(); + let mut shards_manager = ShardsManager::new( + clock.clock(), + account_id, + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + shards_manager.insert_header_if_not_exists_and_process_cached_chunk_forwards( + &fixture.mock_chunk_header, + ); + shards_manager.request_chunks( + vec![fixture.mock_chunk_header.clone()], + *fixture.mock_chunk_header.prev_block_hash(), + ); + let marked_as_requested = shards_manager + .requested_partial_encoded_chunks + .contains_key(&fixture.mock_chunk_header.chunk_hash()); + + let mut sent_request_message_immediately = false; + while let Some(_) = fixture.mock_network.pop() { + sent_request_message_immediately = true; + } + clock.advance(CHUNK_REQUEST_RETRY * 2); + shards_manager.resend_chunk_requests(); + let mut sent_request_message_after_timeout = false; + while let Some(_) = fixture.mock_network.pop() { + sent_request_message_after_timeout = true; + } + RequestChunksResult { + marked_as_requested, + sent_request_message_immediately, + sent_request_message_after_timeout, + } + } + + #[test] + // test that + // when a non validator requests chunks, the request is sent immediately + // when a validator requests chunks, the request is recorded but not sent, because it + // will wait for chunks being forwarded + fn test_chunk_forward_non_validator() { + // A non-validator that tracks all shards should request immediately. + let mut fixture = ChunkTestFixture::new(false, 3, 12, 12, true); + assert_eq!( + run_request_chunks_with_account(&mut fixture, None), + RequestChunksResult { + marked_as_requested: true, + sent_request_message_immediately: true, + sent_request_message_after_timeout: true, + } + ); + + // still a non-validator because the account id is not a validator account id + assert_eq!( + run_request_chunks_with_account(&mut fixture, None), + RequestChunksResult { + marked_as_requested: true, + sent_request_message_immediately: true, + sent_request_message_after_timeout: true, + } + ); + + // when a tracking chunk producer request chunks, the request should not be send + // immediately. + let account_id = Some(fixture.mock_shard_tracker.clone()); + assert_eq!( + run_request_chunks_with_account(&mut fixture, account_id), + RequestChunksResult { + marked_as_requested: true, + sent_request_message_immediately: false, + sent_request_message_after_timeout: true, + } + ); + } + + #[test] + // Test that when a validator receives a chunk before the chunk header, it should store + // the forward and use it when it receives the header + fn test_receive_forward_before_header() { + // Here we test the case when the chunk is received, its previous block is not processed yet + // We want to verify that the chunk forward can be stored and wait to be processed in this + // case too + let fixture = ChunkTestFixture::new(true, 2, 4, 4, false); + let clock = FakeClock::default(); + let mut shards_manager = ShardsManager::new( + clock.clock(), + Some(fixture.mock_shard_tracker.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + let (most_parts, other_parts) = { + let mut most_parts = fixture.mock_chunk_parts.clone(); + let n = most_parts.len(); + let other_parts = most_parts.split_off(n - (n / 4)); + (most_parts, other_parts) + }; + let forward = PartialEncodedChunkForwardMsg::from_header_and_parts( + &fixture.mock_chunk_header, + most_parts, + ); + // The validator receives the chunk forward + assert!(shards_manager.process_partial_encoded_chunk_forward(forward).is_ok()); + let partial_encoded_chunk = PartialEncodedChunk::V2(PartialEncodedChunkV2 { + header: fixture.mock_chunk_header.clone(), + parts: other_parts, + receipts: Vec::new(), + }); + // The validator receives a chunk header with the rest of the parts it needed + let result = shards_manager + .process_partial_encoded_chunk(MaybeValidated::from(partial_encoded_chunk)) + .unwrap(); + + match result { + ProcessPartialEncodedChunkResult::NeedBlock => (), + other_result => panic!("Expected NeedBlock, but got {:?}", other_result), + } + // Now try to request for this chunk, first explicitly, and then through resend_chunk_requests. + // No requests should have been sent since all the required parts were contained in the + // forwarded parts. + shards_manager.request_chunks_for_orphan( + vec![fixture.mock_chunk_header.clone()], + &EpochId::default(), + CryptoHash::default(), + ); + clock.advance(CHUNK_REQUEST_RETRY * 2); + shards_manager.resend_chunk_requests(); + assert!(fixture + .mock_network + .requests + .read() + .unwrap() + .iter() + .find(|r| { + match r.as_network_requests_ref() { + NetworkRequests::PartialEncodedChunkRequest { .. } => true, + _ => false, + } + }) + .is_none()); + } + + #[test] + // Test that when a validator receives a chunk forward before the chunk header, and that the + // chunk header first arrives as part of a block, it should store the the forward and use it + // when it receives the header. + fn test_receive_forward_before_chunk_header_from_block() { + let fixture = ChunkTestFixture::default(); + let clock = FakeClock::default(); + let mut shards_manager = ShardsManager::new( + clock.clock(), + Some(fixture.mock_shard_tracker.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + let forward = PartialEncodedChunkForwardMsg::from_header_and_parts( + &fixture.mock_chunk_header, + fixture.mock_chunk_parts.clone(), + ); + // The validator receives the chunk forward + assert!(shards_manager.process_partial_encoded_chunk_forward(forward).is_ok(),); + // The validator then receives the block, which is missing chunks; it notifies the + // ShardsManager of the chunk header, and ShardsManager is able to complete the chunk + // because of the forwarded parts.shards_manager + shards_manager.insert_header_if_not_exists_and_process_cached_chunk_forwards( + &fixture.mock_chunk_header, + ); + let process_result = shards_manager + .try_process_chunk_parts_and_receipts(&fixture.mock_chunk_header) + .unwrap(); + match process_result { + ProcessPartialEncodedChunkResult::HaveAllPartsAndReceipts => {} + _ => { + panic!("Unexpected process_result: {:?}", process_result); + } + } + assert_eq!(fixture.count_chunk_completion_messages(), 1); + // Requesting it again should not send any actual requests as the chunk is already + // complete. Sleeping and resending later should also not send any requests. + shards_manager.request_chunk_single( + &fixture.mock_chunk_header, + *fixture.mock_chunk_header.prev_block_hash(), + false, + ); + + clock.advance(CHUNK_REQUEST_RETRY * 2); + shards_manager.resend_chunk_requests(); + assert!(fixture + .mock_network + .requests + .read() + .unwrap() + .iter() + .find(|r| { + match r.as_network_requests_ref() { + NetworkRequests::PartialEncodedChunkRequest { .. } => true, + _ => false, + } + }) + .is_none()); + } + + #[test] + fn test_chunk_cache_hit_for_produced_chunk() { + let fixture = ChunkTestFixture::default(); + let mut shards_manager = ShardsManager::new( + FakeClock::default().clock(), + Some(fixture.mock_shard_tracker.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + + shards_manager + .distribute_encoded_chunk( + fixture.make_partial_encoded_chunk(&fixture.all_part_ords), + fixture.mock_encoded_chunk.clone(), + &fixture.mock_merkle_paths, + fixture.mock_outgoing_receipts.clone(), + ) + .unwrap(); + + let (source, response) = + shards_manager.prepare_partial_encoded_chunk_response(PartialEncodedChunkRequestMsg { + chunk_hash: fixture.mock_chunk_header.chunk_hash(), + part_ords: fixture.all_part_ords.clone(), + tracking_shards: HashSet::new(), + }); + assert_eq!(source, PartialEncodedChunkResponseSource::InMemoryCache); + assert_eq!(response.parts.len(), fixture.all_part_ords.len()); + } + + #[test] + fn test_chunk_cache_hit_for_received_chunk() { + let fixture = ChunkTestFixture::default(); + let mut shards_manager = ShardsManager::new( + FakeClock::default().clock(), + Some(fixture.mock_shard_tracker.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + + shards_manager + .process_partial_encoded_chunk( + fixture.make_partial_encoded_chunk(&fixture.all_part_ords).into(), + ) + .unwrap(); + + let (source, response) = + shards_manager.prepare_partial_encoded_chunk_response(PartialEncodedChunkRequestMsg { + chunk_hash: fixture.mock_chunk_header.chunk_hash(), + part_ords: fixture.all_part_ords.clone(), + tracking_shards: HashSet::new(), + }); + assert_eq!(source, PartialEncodedChunkResponseSource::InMemoryCache); + assert_eq!(response.parts.len(), fixture.all_part_ords.len()); + } + + #[test] + fn test_chunk_response_for_uncached_partial_chunk() { + let mut fixture = ChunkTestFixture::default(); + let mut shards_manager = ShardsManager::new( + FakeClock::default().clock(), + Some(fixture.mock_shard_tracker.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + + persist_chunk( + fixture.make_partial_encoded_chunk(&fixture.all_part_ords), + None, + &mut fixture.chain_store, + ) + .unwrap(); + + let (source, response) = + shards_manager.prepare_partial_encoded_chunk_response(PartialEncodedChunkRequestMsg { + chunk_hash: fixture.mock_chunk_header.chunk_hash(), + part_ords: fixture.all_part_ords.clone(), + tracking_shards: HashSet::new(), + }); + assert_eq!(source, PartialEncodedChunkResponseSource::PartialChunkOnDisk); + assert_eq!(response.parts.len(), fixture.all_part_ords.len()); + } + + #[test] + fn test_chunk_response_for_uncached_shard_chunk() { + let mut fixture = ChunkTestFixture::default(); + let mut shards_manager = ShardsManager::new( + FakeClock::default().clock(), + Some(fixture.mock_shard_tracker.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + + let mut update = fixture.chain_store.store_update(); + let shard_chunk = fixture + .mock_encoded_chunk + .decode_chunk(fixture.epoch_manager.num_data_parts()) + .unwrap(); + update.save_chunk(shard_chunk); + update.commit().unwrap(); + + let (source, response) = + shards_manager.prepare_partial_encoded_chunk_response(PartialEncodedChunkRequestMsg { + chunk_hash: fixture.mock_chunk_header.chunk_hash(), + part_ords: fixture.all_part_ords.clone(), + tracking_shards: HashSet::new(), + }); + assert_eq!(source, PartialEncodedChunkResponseSource::ShardChunkOnDisk); + assert_eq!(response.parts.len(), fixture.all_part_ords.len()); + } + + #[test] + fn test_chunk_response_combining_cache_and_partial_chunks() { + let mut fixture = ChunkTestFixture::default(); + let mut shards_manager = ShardsManager::new( + FakeClock::default().clock(), + Some(fixture.mock_shard_tracker.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + // Split the part ords into two groups. + assert!(fixture.all_part_ords.len() >= 2); + let (cache_ords, partial_ords) = + fixture.all_part_ords.split_at(fixture.all_part_ords.len() / 2); + + shards_manager + .process_partial_encoded_chunk(fixture.make_partial_encoded_chunk(cache_ords).into()) + .unwrap(); + + persist_chunk( + fixture.make_partial_encoded_chunk(partial_ords), + None, + &mut fixture.chain_store, + ) + .unwrap(); + let (source, response) = + shards_manager.prepare_partial_encoded_chunk_response(PartialEncodedChunkRequestMsg { + chunk_hash: fixture.mock_chunk_header.chunk_hash(), + part_ords: fixture.all_part_ords.clone(), + tracking_shards: HashSet::new(), + }); + assert_eq!(source, PartialEncodedChunkResponseSource::PartialChunkOnDisk); + assert_eq!(response.parts.len(), fixture.all_part_ords.len()); + } + + #[test] + fn test_chunk_response_combining_cache_and_shard_chunks() { + let mut fixture = ChunkTestFixture::default(); + let mut shards_manager = ShardsManager::new( + FakeClock::default().clock(), + Some(fixture.mock_shard_tracker.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + // Only add half of the parts to the cache. + assert!(fixture.all_part_ords.len() >= 2); + + shards_manager + .process_partial_encoded_chunk( + fixture + .make_partial_encoded_chunk( + &fixture.all_part_ords[0..fixture.all_part_ords.len() / 2], + ) + .into(), + ) + .unwrap(); + + let mut update = fixture.chain_store.store_update(); + let shard_chunk = fixture + .mock_encoded_chunk + .decode_chunk(fixture.epoch_manager.num_data_parts()) + .unwrap(); + update.save_chunk(shard_chunk); + update.commit().unwrap(); + + let (source, response) = + shards_manager.prepare_partial_encoded_chunk_response(PartialEncodedChunkRequestMsg { + chunk_hash: fixture.mock_chunk_header.chunk_hash(), + part_ords: fixture.all_part_ords.clone(), + tracking_shards: HashSet::new(), + }); + assert_eq!(source, PartialEncodedChunkResponseSource::ShardChunkOnDisk); + assert_eq!(response.parts.len(), fixture.all_part_ords.len()); + } + + #[test] + fn test_chunk_response_combining_cache_and_partial_chunks_with_some_missing() { + let mut fixture = ChunkTestFixture::default(); + let mut shards_manager = ShardsManager::new( + FakeClock::default().clock(), + Some(fixture.mock_shard_tracker.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + // Split the part ords into three groups; put one in cache, the second in partial + // and the third is missing. We should return the first two groups. + assert!(fixture.all_part_ords.len() >= 3); + let n = fixture.all_part_ords.len(); + let cache_ords = &fixture.all_part_ords.as_slice()[0..n / 3]; + let partial_ords = &fixture.all_part_ords.as_slice()[n / 3..(n * 2 / 3)]; + + shards_manager + .process_partial_encoded_chunk(fixture.make_partial_encoded_chunk(cache_ords).into()) + .unwrap(); + + persist_chunk( + fixture.make_partial_encoded_chunk(partial_ords), + None, + &mut fixture.chain_store, + ) + .unwrap(); + let (source, response) = + shards_manager.prepare_partial_encoded_chunk_response(PartialEncodedChunkRequestMsg { + chunk_hash: fixture.mock_chunk_header.chunk_hash(), + part_ords: fixture.all_part_ords.clone(), + tracking_shards: HashSet::new(), + }); + assert_eq!(source, PartialEncodedChunkResponseSource::PartialChunkOnDisk); + assert_eq!(response.parts.len(), n * 2 / 3); + } + + #[test] + fn test_chunk_response_empty_request() { + let fixture = ChunkTestFixture::default(); + let mut shards_manager = ShardsManager::new( + FakeClock::default().clock(), + Some(fixture.mock_shard_tracker.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + let (source, response) = + shards_manager.prepare_partial_encoded_chunk_response(PartialEncodedChunkRequestMsg { + chunk_hash: fixture.mock_chunk_header.chunk_hash(), + part_ords: Vec::new(), + tracking_shards: HashSet::new(), + }); + assert_eq!(source, PartialEncodedChunkResponseSource::None); + assert!(response.parts.is_empty()); + } + + #[test] + fn test_chunk_response_for_nonexistent_chunk() { + let fixture = ChunkTestFixture::default(); + let mut shards_manager = ShardsManager::new( + FakeClock::default().clock(), + Some(fixture.mock_shard_tracker.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + let (source, response) = + shards_manager.prepare_partial_encoded_chunk_response(PartialEncodedChunkRequestMsg { + chunk_hash: fixture.mock_chunk_header.chunk_hash(), + part_ords: vec![0], + tracking_shards: HashSet::new(), + }); + assert_eq!(source, PartialEncodedChunkResponseSource::ShardChunkOnDisk); + assert!(response.parts.is_empty()); + } + + #[test] + fn test_chunk_response_for_request_including_invalid_part_ord() { + let mut fixture = ChunkTestFixture::default(); + let mut shards_manager = ShardsManager::new( + FakeClock::default().clock(), + Some(fixture.mock_shard_tracker.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + let mut update = fixture.chain_store.store_update(); + let shard_chunk = fixture + .mock_encoded_chunk + .decode_chunk(fixture.epoch_manager.num_data_parts()) + .unwrap(); + update.save_chunk(shard_chunk); + update.commit().unwrap(); + + let (source, response) = + shards_manager.prepare_partial_encoded_chunk_response(PartialEncodedChunkRequestMsg { + chunk_hash: fixture.mock_chunk_header.chunk_hash(), + part_ords: vec![0, fixture.epoch_manager.num_total_parts() as u64], + tracking_shards: HashSet::new(), + }); + assert_eq!(source, PartialEncodedChunkResponseSource::ShardChunkOnDisk); + // part 0 should be returned; part N should be ignored. + assert_eq!(response.parts.len(), 1); + } + + #[test] + fn test_chunk_response_for_request_with_duplicate_part_ords() { + // We should not return any duplicates. + let mut fixture = ChunkTestFixture::default(); + let mut shards_manager = ShardsManager::new( + FakeClock::default().clock(), + Some(fixture.mock_shard_tracker.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + let mut update = fixture.chain_store.store_update(); + let shard_chunk = fixture + .mock_encoded_chunk + .decode_chunk(fixture.epoch_manager.num_data_parts()) + .unwrap(); + update.save_chunk(shard_chunk); + update.commit().unwrap(); + + let (source, response) = + shards_manager.prepare_partial_encoded_chunk_response(PartialEncodedChunkRequestMsg { + chunk_hash: fixture.mock_chunk_header.chunk_hash(), + part_ords: vec![0, 1, 0, 1, 0, 1, 0, 1], + tracking_shards: HashSet::new(), + }); + assert_eq!(source, PartialEncodedChunkResponseSource::ShardChunkOnDisk); + assert_eq!(response.parts.len(), 2); + } + + #[test] + fn test_report_chunk_for_inclusion_to_client() { + let fixture = ChunkTestFixture::default(); + let mut shards_manager = ShardsManager::new( + FakeClock::default().clock(), + Some(fixture.mock_chunk_part_owner.clone()), + Arc::new(fixture.epoch_manager.clone()), + fixture.shard_tracker.clone(), + fixture.mock_network.as_sender(), + fixture.mock_client_adapter.as_sender(), + fixture.chain_store.new_read_only_chunks_store(), + fixture.mock_chain_head.clone(), + fixture.mock_chain_head.clone(), + ); + let part = fixture.make_partial_encoded_chunk(&fixture.mock_part_ords); + shards_manager.process_partial_encoded_chunk(part.clone().into()).unwrap(); + assert_eq!(fixture.count_chunk_ready_for_inclusion_messages(), 1); + + // test that chunk inclusion message is only sent once. + shards_manager.process_partial_encoded_chunk(part.into()).unwrap(); + assert_eq!(fixture.count_chunk_ready_for_inclusion_messages(), 0); + } +} diff --git a/chain/chunks/src/logic.rs b/chain/chunks/src/logic.rs new file mode 100644 index 000000000..d3a2335ea --- /dev/null +++ b/chain/chunks/src/logic.rs @@ -0,0 +1,240 @@ +use unc_chain::{types::EpochManagerAdapter, validate::validate_chunk_proofs, Chain, ChainStore}; +use unc_chunks_primitives::Error; +use unc_epoch_manager::shard_tracker::ShardTracker; +use unc_primitives::{ + errors::EpochError, + hash::CryptoHash, + merkle::{merklize, MerklePath}, + receipt::Receipt, + sharding::{ + EncodedShardChunk, PartialEncodedChunk, PartialEncodedChunkPart, PartialEncodedChunkV1, + PartialEncodedChunkV2, ReceiptProof, ShardChunk, ShardChunkHeader, ShardProof, + }, + types::{AccountId, ShardId}, +}; +use tracing::{debug, debug_span, error}; + +pub fn need_receipt( + prev_block_hash: &CryptoHash, + shard_id: ShardId, + me: Option<&AccountId>, + shard_tracker: &ShardTracker, +) -> bool { + cares_about_shard_this_or_next_epoch(me, prev_block_hash, shard_id, true, shard_tracker) +} + +/// Returns true if we need this part to sign the block. +pub fn need_part( + prev_block_hash: &CryptoHash, + part_ord: u64, + me: Option<&AccountId>, + epoch_manager: &dyn EpochManagerAdapter, +) -> Result { + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(prev_block_hash)?; + Ok(Some(&epoch_manager.get_part_owner(&epoch_id, part_ord)?) == me) +} + +pub fn cares_about_shard_this_or_next_epoch( + account_id: Option<&AccountId>, + parent_hash: &CryptoHash, + shard_id: ShardId, + is_me: bool, + shard_tracker: &ShardTracker, +) -> bool { + // TODO(robin-near): I think we only need the shard_tracker if is_me is false. + shard_tracker.care_about_shard(account_id, parent_hash, shard_id, is_me) + || shard_tracker.will_care_about_shard(account_id, parent_hash, shard_id, is_me) +} + +pub fn chunk_needs_to_be_fetched_from_archival( + chunk_prev_block_hash: &CryptoHash, + header_head: &CryptoHash, + epoch_manager: &dyn EpochManagerAdapter, +) -> Result { + let head_epoch_id = epoch_manager.get_epoch_id(header_head)?; + let head_next_epoch_id = epoch_manager.get_next_epoch_id(header_head)?; + let chunk_epoch_id = epoch_manager.get_epoch_id_from_prev_block(chunk_prev_block_hash)?; + let chunk_next_epoch_id = + epoch_manager.get_next_epoch_id_from_prev_block(chunk_prev_block_hash)?; + + // `chunk_epoch_id != head_epoch_id && chunk_next_epoch_id != head_epoch_id` covers the + // common case: the chunk is in the current epoch, or in the previous epoch, relative to the + // header head. The third condition (`chunk_epoch_id != head_next_epoch_id`) covers a + // corner case, in which the `header_head` is the last block of an epoch, and the chunk is + // for the next block. In this case the `chunk_epoch_id` will be one epoch ahead of the + // `header_head`. + Ok(chunk_epoch_id != head_epoch_id + && chunk_next_epoch_id != head_epoch_id + && chunk_epoch_id != head_next_epoch_id) +} + +/// Constructs receipt proofs for specified chunk and returns them in an +/// iterator. +pub fn make_outgoing_receipts_proofs( + chunk_header: &ShardChunkHeader, + outgoing_receipts: &[Receipt], + epoch_manager: &dyn EpochManagerAdapter, +) -> Result, EpochError> { + let shard_id = chunk_header.shard_id(); + let shard_layout = + epoch_manager.get_shard_layout_from_prev_block(chunk_header.prev_block_hash())?; + + let hashes = Chain::build_receipts_hashes(&outgoing_receipts, &shard_layout); + let (root, proofs) = merklize(&hashes); + assert_eq!(chunk_header.prev_outgoing_receipts_root(), root); + + let mut receipts_by_shard = + Chain::group_receipts_by_shard(outgoing_receipts.to_vec(), &shard_layout); + let it = proofs.into_iter().enumerate().map(move |(proof_shard_id, proof)| { + let proof_shard_id = proof_shard_id as u64; + let receipts = receipts_by_shard.remove(&proof_shard_id).unwrap_or_else(Vec::new); + let shard_proof = + ShardProof { from_shard_id: shard_id, to_shard_id: proof_shard_id, proof }; + ReceiptProof(receipts, shard_proof) + }); + Ok(it) +} + +pub fn make_partial_encoded_chunk_from_owned_parts_and_needed_receipts<'a>( + header: &'a ShardChunkHeader, + parts: impl Iterator, + receipts: impl Iterator, + me: Option<&AccountId>, + epoch_manager: &dyn EpochManagerAdapter, + shard_tracker: &ShardTracker, +) -> PartialEncodedChunk { + let prev_block_hash = header.prev_block_hash(); + let cares_about_shard = cares_about_shard_this_or_next_epoch( + me, + prev_block_hash, + header.shard_id(), + true, + shard_tracker, + ); + let parts = parts + .filter(|entry| { + cares_about_shard + || need_part(prev_block_hash, entry.part_ord, me, epoch_manager).unwrap_or(false) + }) + .cloned() + .collect(); + let mut receipts: Vec<_> = receipts + .filter(|receipt| { + cares_about_shard + || need_receipt(prev_block_hash, receipt.1.to_shard_id, me, shard_tracker) + }) + .cloned() + .collect(); + // Make sure the receipts are in a deterministic order. + receipts.sort(); + match header.clone() { + ShardChunkHeader::V1(header) => { + PartialEncodedChunk::V1(PartialEncodedChunkV1 { header, parts, receipts }) + } + header => PartialEncodedChunk::V2(PartialEncodedChunkV2 { header, parts, receipts }), + } +} + +pub fn decode_encoded_chunk( + encoded_chunk: &EncodedShardChunk, + merkle_paths: Vec, + me: Option<&AccountId>, + epoch_manager: &dyn EpochManagerAdapter, + shard_tracker: &ShardTracker, +) -> Result<(ShardChunk, PartialEncodedChunk), Error> { + let chunk_hash = encoded_chunk.chunk_hash(); + let _span = debug_span!( + target: "chunks", + "decode_encoded_chunk", + height_included = encoded_chunk.cloned_header().height_included(), + shard_id = encoded_chunk.cloned_header().shard_id(), + ?chunk_hash) + .entered(); + + if let Ok(shard_chunk) = encoded_chunk + .decode_chunk(epoch_manager.num_data_parts()) + .map_err(|err| Error::from(err)) + .and_then(|shard_chunk| { + if !validate_chunk_proofs(&shard_chunk, epoch_manager)? { + return Err(Error::InvalidChunk); + } + Ok(shard_chunk) + }) + { + debug!( + target: "chunks", + ?chunk_hash, + encoded_length = encoded_chunk.encoded_length(), + num_tx = shard_chunk.transactions().len(), + ?me, + "Reconstructed and decoded"); + let partial_chunk = create_partial_chunk( + encoded_chunk, + merkle_paths, + shard_chunk.prev_outgoing_receipts().to_vec(), + me, + epoch_manager, + shard_tracker, + ) + .map_err(|err| Error::ChainError(err.into()))?; + + Ok((shard_chunk, partial_chunk)) + } else { + // Can't decode chunk or has invalid proofs, ignore it + error!(target: "chunks", ?chunk_hash, ?me, "Reconstructed, but failed to decoded chunk"); + Err(Error::InvalidChunk) + } +} + +fn create_partial_chunk( + encoded_chunk: &EncodedShardChunk, + merkle_paths: Vec, + outgoing_receipts: Vec, + me: Option<&AccountId>, + epoch_manager: &dyn EpochManagerAdapter, + shard_tracker: &ShardTracker, +) -> Result { + let header = encoded_chunk.cloned_header(); + let receipts = + make_outgoing_receipts_proofs(&header, &outgoing_receipts, epoch_manager)?.collect(); + let partial_chunk = PartialEncodedChunkV2 { + header, + parts: encoded_chunk + .content() + .parts + .clone() + .into_iter() + .zip(merkle_paths) + .enumerate() + .map(|(part_ord, (part, merkle_proof))| { + let part_ord = part_ord as u64; + let part = part.unwrap(); + PartialEncodedChunkPart { part_ord, part, merkle_proof } + }) + .collect(), + receipts, + }; + + Ok(make_partial_encoded_chunk_from_owned_parts_and_needed_receipts( + &partial_chunk.header, + partial_chunk.parts.iter(), + partial_chunk.receipts.iter(), + me, + epoch_manager, + shard_tracker, + )) +} + +pub fn persist_chunk( + partial_chunk: PartialEncodedChunk, + shard_chunk: Option, + store: &mut ChainStore, +) -> Result<(), Error> { + let mut update = store.store_update(); + update.save_partial_chunk(partial_chunk); + if let Some(shard_chunk) = shard_chunk { + update.save_chunk(shard_chunk); + } + update.commit()?; + Ok(()) +} diff --git a/chain/chunks/src/metrics.rs b/chain/chunks/src/metrics.rs new file mode 100644 index 000000000..e6e7bc6b1 --- /dev/null +++ b/chain/chunks/src/metrics.rs @@ -0,0 +1,65 @@ +use unc_o11y::metrics::{exponential_buckets, try_create_histogram, Counter, Histogram}; +use once_cell::sync::Lazy; + +pub static PARTIAL_ENCODED_CHUNK_REQUEST_PROCESSING_TIME: Lazy = + Lazy::new(|| { + unc_o11y::metrics::try_create_histogram_vec( + "unc_partial_encoded_chunk_request_processing_time", + concat!( + "Time taken to prepare responses to partial encoded chuck ", + "requests. The ‘method’ key describes how we tried to fulfil ", + "the request and ‘success’ describes whether we managed to ", + "create a response (‘ok’) or not (‘failed’). Note that ", + "success does not mean that we managed to send the response ", + "over network; the count is taken before we attempt to send ", + "the data out." + ), + &["method", "success"], + Some(exponential_buckets(0.001, 2.0, 16).unwrap()), + ) + .unwrap() + }); + +pub static DISTRIBUTE_ENCODED_CHUNK_TIME: Lazy = + Lazy::new(|| { + unc_o11y::metrics::try_create_histogram_vec( + "unc_distribute_encoded_chunk_time", + concat!( + "Time to distribute data about a produced chunk: Preparation of network messages ", + "and passing it to peer manager", + ), + &["shard_id"], + Some(exponential_buckets(0.001, 2.0, 16).unwrap()), + ) + .unwrap() + }); + +pub(crate) static PARTIAL_ENCODED_CHUNK_RESPONSE_DELAY: Lazy = Lazy::new(|| { + try_create_histogram( + "unc_partial_encoded_chunk_response_delay", + "Delay between when a partial encoded chunk response is sent from PeerActor and when it is received by ShardsManagerActor", + ) + .unwrap() +}); + +pub static PARTIAL_ENCODED_CHUNK_FORWARD_CACHED_WITHOUT_HEADER: Lazy = Lazy::new(|| { + unc_o11y::metrics::try_create_counter( + "unc_partial_encoded_chunk_forward_cached_without_header", + concat!( + "Number of times we received a valid partial encoded chunk forward without having the corresponding chunk header so we cached it" + ), + ) + .unwrap() +}); + +pub static PARTIAL_ENCODED_CHUNK_FORWARD_CACHED_WITHOUT_PREV_BLOCK: Lazy = Lazy::new( + || { + unc_o11y::metrics::try_create_counter( + "unc_partial_encoded_chunk_forward_cached_without_prev_block", + concat!( + "Number of times we received a partial encoded chunk forward without having the previous block to fully validate it, so we cached it" + ), + ) + .unwrap() + }, +); diff --git a/chain/chunks/src/shards_manager_actor.rs b/chain/chunks/src/shards_manager_actor.rs new file mode 100644 index 000000000..77519436a --- /dev/null +++ b/chain/chunks/src/shards_manager_actor.rs @@ -0,0 +1,114 @@ +use std::{sync::Arc, time::Duration}; + +use actix::{Actor, Addr, Arbiter, ArbiterHandle, Context, Handler}; +use unc_async::messaging::Sender; +use unc_async::time; +use unc_chain::{chunks_store::ReadOnlyChunksStore, types::Tip}; +use unc_epoch_manager::{shard_tracker::ShardTracker, EpochManagerAdapter}; +use unc_network::{ + shards_manager::ShardsManagerRequestFromNetwork, types::PeerManagerMessageRequest, +}; +use unc_o11y::WithSpanContext; +use unc_performance_metrics_macros::perf; +use unc_primitives::types::AccountId; +use unc_store::{DBCol, Store, HEADER_HEAD_KEY, HEAD_KEY}; + +use crate::{ + adapter::ShardsManagerRequestFromClient, client::ShardsManagerResponse, ShardsManager, +}; + +pub struct ShardsManagerActor { + shards_mgr: ShardsManager, + chunk_request_retry_period: Duration, +} + +impl ShardsManagerActor { + fn new(shards_mgr: ShardsManager, chunk_request_retry_period: Duration) -> Self { + Self { shards_mgr, chunk_request_retry_period } + } + + fn periodically_resend_chunk_requests(&mut self, ctx: &mut Context) { + self.shards_mgr.resend_chunk_requests(); + + unc_performance_metrics::actix::run_later( + ctx, + self.chunk_request_retry_period, + move |act, ctx| { + act.periodically_resend_chunk_requests(ctx); + }, + ); + } +} + +impl Actor for ShardsManagerActor { + type Context = Context; + + fn started(&mut self, ctx: &mut Self::Context) { + self.periodically_resend_chunk_requests(ctx); + } +} + +impl Handler> for ShardsManagerActor { + type Result = (); + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Context, + ) { + self.shards_mgr.handle_client_request(msg.msg); + } +} + +impl Handler> for ShardsManagerActor { + type Result = (); + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Context, + ) { + self.shards_mgr.handle_network_request(msg.msg); + } +} + +pub fn start_shards_manager( + epoch_manager: Arc, + shard_tracker: ShardTracker, + network_adapter: Sender, + client_adapter_for_shards_manager: Sender, + me: Option, + store: Store, + chunk_request_retry_period: Duration, +) -> (Addr, ArbiterHandle) { + let shards_manager_arbiter = Arbiter::new(); + let shards_manager_arbiter_handle = shards_manager_arbiter.handle(); + // TODO: make some better API for accessing chain properties like head. + let chain_head = store + .get_ser::(DBCol::BlockMisc, HEAD_KEY) + .unwrap() + .expect("ShardsManager must be initialized after the chain is initialized"); + let chain_header_head = store + .get_ser::(DBCol::BlockMisc, HEADER_HEAD_KEY) + .unwrap() + .expect("ShardsManager must be initialized after the chain is initialized"); + let chunks_store = ReadOnlyChunksStore::new(store); + let shards_manager = ShardsManager::new( + time::Clock::real(), + me, + epoch_manager, + shard_tracker, + network_adapter, + client_adapter_for_shards_manager, + chunks_store, + chain_head, + chain_header_head, + ); + let shards_manager_addr = + ShardsManagerActor::start_in_arbiter(&shards_manager_arbiter_handle, move |_| { + ShardsManagerActor::new(shards_manager, chunk_request_retry_period) + }); + (shards_manager_addr, shards_manager_arbiter_handle) +} diff --git a/chain/chunks/src/test/basic.rs b/chain/chunks/src/test/basic.rs new file mode 100644 index 000000000..8efa4f38d --- /dev/null +++ b/chain/chunks/src/test/basic.rs @@ -0,0 +1,256 @@ +use std::collections::HashSet; + +use derive_enum_from_into::{EnumFrom, EnumTryInto}; +use unc_async::time; +use unc_async::{ + messaging::{CanSend, IntoSender, Sender}, + test_loop::{ + adhoc::{handle_adhoc_events, AdhocEvent, AdhocEventSender}, + event_handler::capture_events, + }, +}; +use unc_chain::chunks_store::ReadOnlyChunksStore; +use unc_epoch_manager::test_utils::hash_range; +use unc_network::{ + shards_manager::ShardsManagerRequestFromNetwork, + types::{NetworkRequests, PeerManagerMessageRequest}, +}; +use unc_primitives::types::{AccountId, BlockHeight}; +use unc_store::test_utils::create_test_store; +use tracing::log::info; + +use crate::{ + adapter::ShardsManagerRequestFromClient, + client::ShardsManagerResponse, + test_loop::{ + forward_client_request_to_shards_manager, forward_network_request_to_shards_manager, + periodically_resend_chunk_requests, MockChainForShardsManager, + MockChainForShardsManagerConfig, ShardsManagerResendChunkRequests, + }, + test_utils::default_tip, + ShardsManager, CHUNK_REQUEST_RETRY, +}; + +#[derive(derive_more::AsMut)] +struct TestData { + shards_manager: ShardsManager, + chain: MockChainForShardsManager, + /// Captured events sent to the client. + client_events: Vec, + /// Captured events sent to the network. + network_events: Vec, +} + +impl TestData { + fn new(shards_manager: ShardsManager, chain: MockChainForShardsManager) -> Self { + Self { shards_manager, chain, client_events: vec![], network_events: vec![] } + } +} + +#[derive(EnumTryInto, Debug, EnumFrom)] +enum TestEvent { + ClientToShardsManager(ShardsManagerRequestFromClient), + NetworkToShardsManager(ShardsManagerRequestFromNetwork), + ShardsManagerToClient(ShardsManagerResponse), + ShardsManagerToNetwork(PeerManagerMessageRequest), + ShardsManagerResendRequests(ShardsManagerResendChunkRequests), + Adhoc(AdhocEvent), +} + +type ShardsManagerTestLoopBuilder = unc_async::test_loop::TestLoopBuilder; + +/// Basic test that sends a full chunk to one ShardsManager and checks that it +/// reports the complete chunk to the client. +#[test] +fn test_basic_receive_complete_chunk() { + let builder = ShardsManagerTestLoopBuilder::new(); + let validators = (0..5) + .map(|i| format!("validator_{}", i).parse::().unwrap()) + .collect::>(); + + let store = create_test_store(); + let chain = MockChainForShardsManager::new( + store.clone(), + MockChainForShardsManagerConfig { + account_id: validators[0].clone(), + block_producers: validators.clone(), + chunk_only_producers: vec![], + epoch_length: 2, + num_shards: 3, + track_all_shards: true, + shards_manager: builder.sender().into_sender(), + }, + ); + + let shards_manager = ShardsManager::new( + builder.clock(), + Some(validators[0].clone()), + chain.epoch_manager.clone(), + chain.shard_tracker.clone(), + Sender::noop(), + builder.sender().into_sender(), + ReadOnlyChunksStore::new(store), + default_tip(), + default_tip(), + ); + let test_data = TestData::new(shards_manager, chain); + let mut test = builder.build(test_data); + test.register_handler(forward_client_request_to_shards_manager().widen()); + test.register_handler(forward_network_request_to_shards_manager().widen()); + test.register_handler(capture_events::().widen()); + + // Have the ShardsManager receive a PartialEncodedChunk with all parts. + let chunk = test.data.chain.produce_chunk_signed_by_chunk_producer(2); + test.sender().send(TestEvent::NetworkToShardsManager( + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunk( + chunk.make_partial_encoded_chunk(&chunk.part_ords(), &[]), + ), + )); + test.run_for(time::Duration::seconds(1)); + + assert_eq!(test.data.client_events.len(), 2); + match &test.data.client_events[0] { + ShardsManagerResponse::ChunkHeaderReadyForInclusion { .. } => {} + _ => panic!(), + } + match &test.data.client_events[1] { + ShardsManagerResponse::ChunkCompleted { partial_chunk, shard_chunk } => { + assert_eq!(partial_chunk.parts().len(), chunk.part_ords().len()); + assert!(shard_chunk.is_some()); + } + _ => panic!(), + } +} + +/// Tests that one node forwards chunk parts correctly to other nodes. +/// When a validator V receives a PartialEncodedChunk from the chunk producer, +/// containing the parts that the V owns, it is responsible for forwarding +/// these parts to the next chunk producer as well as the any block producer +/// that tracks the shard, as an optimization so that they don't have to +/// request what we already know they will request. This test checks that +/// behavior. +/// +/// Note that before Phase 2 of sharding is launched, we actually forward to +/// all block producers, not just those tracking the shard, because all +/// block producers are required to track all shards. +/// +/// See the comment on top of chain/chunks/src/lib.rs for more details. +#[test] +fn test_chunk_forward() { + let builder = ShardsManagerTestLoopBuilder::new(); + // For this test we need to ensure that 1 part is not enough to decode + // the whole chunk. For that, we need at least 7 block producers, so that + // the total number of parts is 7, and the number of parts needed to + // decode the chunk is 2, since the formula is (num_block_producers - 1)/3. + let block_producers: Vec = + (0..10).map(|idx| format!("bp_{}", idx).parse().unwrap()).collect(); + let chunk_only_producers: Vec = + (0..10).map(|idx| format!("cp_{}", idx).parse().unwrap()).collect(); + let store = create_test_store(); + let chain = MockChainForShardsManager::new( + store.clone(), + MockChainForShardsManagerConfig { + account_id: block_producers[0].clone(), + block_producers: block_producers.clone(), + chunk_only_producers: chunk_only_producers.clone(), + num_shards: 1, + epoch_length: 5, + track_all_shards: true, + shards_manager: builder.sender().into_sender(), + }, + ); + let shards_manager = ShardsManager::new( + builder.clock(), + Some(block_producers[0].clone()), + chain.epoch_manager.clone(), + chain.shard_tracker.clone(), + builder.sender().into_sender(), + builder.sender().into_sender(), + ReadOnlyChunksStore::new(store), + default_tip(), + default_tip(), + ); + let mut test = builder.build(TestData::new(shards_manager, chain)); + test.register_handler(capture_events::().widen()); + test.register_handler(capture_events::().widen()); + test.register_handler(forward_client_request_to_shards_manager().widen()); + test.register_handler(forward_network_request_to_shards_manager().widen()); + test.register_handler(periodically_resend_chunk_requests(CHUNK_REQUEST_RETRY).widen()); + test.register_handler(handle_adhoc_events()); + + // We'll produce a single chunk whose next chunk producer is a chunk-only + // producer, so that we can test that the chunk is forwarded to the next + // chunk producer (since we forward it to block producers anyway). + // We do that by producing *blocks* until we get to a height where the + // next chunk producer is a chunk-only producer. + test.sender().send_adhoc_event("produce chunk", { + let sender = test.sender(); + let chunk_only_producers = chunk_only_producers.clone(); + move |data| { + let hashes = hash_range(100); // 100 blocks should be more than enough to get lucky. + for (i, hash) in hashes.iter().enumerate() { + let partial_encoded_chunk = data.chain.produce_chunk_signed_by_chunk_producer(0); + data.chain.record_block(*hash, i as BlockHeight + 1); + let next_chunk_producer = data.chain.next_chunk_producer(0); + if !chunk_only_producers.contains(&next_chunk_producer) { + info!(target: "test", "Trying again at height {} which has chunk producer {}, we want the next chunk producer to be a chunk only producer", + i + 1, next_chunk_producer); + continue; + } + sender.send(TestEvent::NetworkToShardsManager( + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunk( + partial_encoded_chunk.make_partial_encoded_chunk(&[0], &[]), + ), + )); + break; + } + } + }); + test.run_instant(); + + // The logic implemented right now is that all block producers will get the + // forwarding (due to tracking all shards), and for chunk only producers, + // they will only get the forwarding if they are the next chunk producer. + let mut next_chunk_producer_forwarded = false; + let mut block_producer_forwarded = HashSet::new(); + while let Some(r) = test.data.network_events.pop() { + match r.as_network_requests_ref() { + NetworkRequests::PartialEncodedChunkForward { account_id, .. } => { + if account_id == &test.data.chain.next_chunk_producer(0) { + next_chunk_producer_forwarded = true; + } else { + assert!( + !chunk_only_producers.contains(&account_id), + "shouldn't forward to {:?}", + account_id + ); + block_producer_forwarded.insert(account_id.clone()); + } + } + NetworkRequests::PartialEncodedChunkRequest { .. } => { + panic!("Shouldn't request chunk part yet; should wait for forwarding"); + } + _ => { + panic!("Unexpected network request: {:?}", r); + } + } + } + assert!(next_chunk_producer_forwarded); + assert_eq!(block_producer_forwarded.len(), block_producers.len() - 1); + + // Now run for a bit longer to trigger resend. The validator should now + // request the missing parts. + test.run_for(CHUNK_REQUEST_RETRY); + let mut seen_part_request = false; + while let Some(r) = test.data.network_events.pop() { + match r.as_network_requests_ref() { + NetworkRequests::PartialEncodedChunkRequest { .. } => { + seen_part_request = true; + } + _ => { + panic!("Unexpected network request: {:?}", r); + } + } + } + assert!(seen_part_request); +} diff --git a/chain/chunks/src/test/multi.rs b/chain/chunks/src/test/multi.rs new file mode 100644 index 000000000..5a4c9688a --- /dev/null +++ b/chain/chunks/src/test/multi.rs @@ -0,0 +1,327 @@ +use derive_enum_from_into::{EnumFrom, EnumTryInto}; +use unc_async::{ + messaging::IntoSender, + test_loop::{ + adhoc::{handle_adhoc_events, AdhocEvent, AdhocEventSender}, + event_handler::capture_events, + }, + time, +}; +use unc_chain::chunks_store::ReadOnlyChunksStore; +use unc_epoch_manager::EpochManagerAdapter; +use unc_network::{ + shards_manager::ShardsManagerRequestFromNetwork, test_loop::SupportsRoutingLookup, + types::PeerManagerMessageRequest, +}; +use unc_primitives::types::{AccountId, NumShards}; +use unc_store::test_utils::create_test_store; + +use crate::{ + adapter::ShardsManagerRequestFromClient, + client::ShardsManagerResponse, + test_loop::{ + forward_client_request_to_shards_manager, forward_network_request_to_shards_manager, + route_shards_manager_network_messages, MockChainForShardsManager, + MockChainForShardsManagerConfig, + }, + test_utils::default_tip, + ShardsManager, +}; + +#[derive(derive_more::AsMut, derive_more::AsRef)] +struct TestData { + shards_manager: ShardsManager, + chain: MockChainForShardsManager, + client_events: Vec, + account_id: AccountId, +} + +#[derive(EnumTryInto, Debug, EnumFrom)] +enum TestEvent { + Adhoc(AdhocEvent), + ClientToShardsManager(ShardsManagerRequestFromClient), + NetworkToShardsManager(ShardsManagerRequestFromNetwork), + ShardsManagerToClient(ShardsManagerResponse), + OutboundNetwork(PeerManagerMessageRequest), +} + +type ShardsManagerTestLoop = unc_async::test_loop::TestLoop, (usize, TestEvent)>; +type ShardsManagerTestLoopBuilder = unc_async::test_loop::TestLoopBuilder<(usize, TestEvent)>; + +struct BasicSetupConfig { + block_producers: Vec, + chunk_only_producers: Vec, + epoch_length: u64, + num_shards: NumShards, + track_all_shards: bool, +} + +const NETWORK_DELAY: time::Duration = time::Duration::milliseconds(10); + +fn basic_setup(config: BasicSetupConfig) -> ShardsManagerTestLoop { + let builder = ShardsManagerTestLoopBuilder::new(); + let all_accounts = config.block_producers.iter().chain(config.chunk_only_producers.iter()); + let data = all_accounts + .enumerate() + .map(|(idx, account)| { + let store = create_test_store(); + let chain = MockChainForShardsManager::new( + store.clone(), + MockChainForShardsManagerConfig { + account_id: account.clone(), + block_producers: config.block_producers.clone(), + chunk_only_producers: config.chunk_only_producers.clone(), + epoch_length: config.epoch_length, + num_shards: config.num_shards, + track_all_shards: config.track_all_shards, + shards_manager: builder.sender().for_index(idx).into_sender(), + }, + ); + let shards_manager = ShardsManager::new( + builder.clock(), + Some(account.clone()), + chain.epoch_manager.clone(), + chain.shard_tracker.clone(), + builder.sender().for_index(idx).into_sender(), + builder.sender().for_index(idx).into_sender(), + ReadOnlyChunksStore::new(store), + default_tip(), + default_tip(), + ); + TestData { shards_manager, chain, client_events: vec![], account_id: account.clone() } + }) + .collect::>(); + let mut test = builder.build(data); + for idx in 0..test.data.len() { + test.register_handler(handle_adhoc_events().for_index(idx)); + test.register_handler(forward_client_request_to_shards_manager().widen().for_index(idx)); + test.register_handler(forward_network_request_to_shards_manager().widen().for_index(idx)); + test.register_handler(capture_events::().widen().for_index(idx)); + test.register_handler(route_shards_manager_network_messages(NETWORK_DELAY)); + // Note that we don't have the periodically resending requests handler, because + // our forwarding logic means that we don't need to resend requests, unless + // there is unreliable network, which is tested separately. + } + test +} + +/// Tests that when we have some block producers (validators) in the network, +/// and one block producer produces a chunk, the chunk is distributed to the +/// other block producers properly and all other block producers report the +/// completion of the chunk to the client. +#[test] +fn test_distribute_chunk_basic() { + let mut test = basic_setup(BasicSetupConfig { + block_producers: (0..10).map(|idx| format!("validator_{}", idx).parse().unwrap()).collect(), + chunk_only_producers: Vec::new(), + epoch_length: 4, // arbitrary + num_shards: 3, // arbitrary + track_all_shards: false, + }); + let chunk_producer = test.data[0].chain.next_chunk_producer(1); + let chunk_producer_idx = test.data.index_for_account(&chunk_producer); + test.sender().for_index(chunk_producer_idx).send_adhoc_event("produce chunk", |data| { + let chunk = data.chain.produce_chunk(1); + data.chain.distribute_chunk(&chunk); + }); + // Two network rounds is enough because each node should have + // forwarded the parts to those block producers that need them. + test.run_for(NETWORK_DELAY * 2); + + // All other nodes should have received the chunk header and their owned + // parts, but only those that track the shard should have persisted the + // entire chunk. + for idx in 0..test.data.len() { + if idx == chunk_producer_idx { + continue; + } + let data = test.data.get(idx).unwrap(); + assert_eq!(data.client_events.len(), 2); + match &data.client_events[0] { + ShardsManagerResponse::ChunkHeaderReadyForInclusion { + chunk_producer: producer, + .. + } => { + assert_eq!(producer, &chunk_producer); + } + _ => panic!("Unexpected event"), + } + match &data.client_events[1] { + ShardsManagerResponse::ChunkCompleted { partial_chunk, shard_chunk } => { + if data.chain.cares_about_shard_this_or_next_epoch(1) { + assert_eq!( + partial_chunk.parts().len(), + data.chain.epoch_manager.num_total_parts() + ); + assert!(shard_chunk.is_some()); + } else { + assert_eq!(partial_chunk.parts().len(), 1); + assert_eq!(partial_chunk.parts()[0].part_ord as usize, idx); + assert!(shard_chunk.is_none()); + } + } + _ => panic!("Unexpected event"), + } + } +} + +/// Tests that when we have some block producers (validators) in the network, +/// and one block producer produces a chunk, the chunk is distributed to the +/// other block producers properly and all other block producers report the +/// completion of the chunk to the client. Unlike test_distribute_chunk_basic, +/// this test has all block producers track all shards. +#[test] +fn test_distribute_chunk_track_all_shards() { + let mut test = basic_setup(BasicSetupConfig { + block_producers: (0..10).map(|idx| format!("validator_{}", idx).parse().unwrap()).collect(), + chunk_only_producers: Vec::new(), + epoch_length: 4, // arbitrary + num_shards: 3, // arbitrary + track_all_shards: true, + }); + let chunk_producer = test.data[0].chain.next_chunk_producer(1); + let chunk_producer_idx = test.data.index_for_account(&chunk_producer); + test.sender().for_index(chunk_producer_idx).send_adhoc_event("produce chunk", |data| { + let chunk = data.chain.produce_chunk(1); + data.chain.distribute_chunk(&chunk); + }); + // Two network rounds is enough because each node should have + // forwarded the parts to those block producers that need them. + // TODO: after phase 2, we will need a longer delay because validators + // that don't track the shard will not get forwards. We may also need + // to add the periodic resending handler. + test.run_for(NETWORK_DELAY * 2); + + // All other nodes should have received the complete chunk. + for idx in 0..test.data.len() { + if idx == chunk_producer_idx { + continue; + } + let data = test.data.get(idx).unwrap(); + assert_eq!(data.client_events.len(), 2); + match &data.client_events[0] { + ShardsManagerResponse::ChunkHeaderReadyForInclusion { + chunk_producer: producer, + .. + } => { + assert_eq!(producer, &chunk_producer); + } + _ => panic!("Unexpected event"), + } + match &data.client_events[1] { + ShardsManagerResponse::ChunkCompleted { partial_chunk, shard_chunk } => { + assert_eq!(partial_chunk.parts().len(), data.chain.epoch_manager.num_total_parts()); + assert!(shard_chunk.is_some()); + } + _ => panic!("Unexpected event"), + } + } +} + +/// Tests that when the network has some block producers and also some chunk- +/// only producers, the chunk-only producers are also able to request and +/// complete chunks. +#[test] +fn test_distribute_chunk_with_chunk_only_producers() { + const NUM_BLOCK_PRODUCERS: usize = 8; // arbitrary + const NUM_CHUNK_ONLY_PRODUCERS: usize = 6; // arbitrary + let mut test = basic_setup(BasicSetupConfig { + block_producers: (0..NUM_BLOCK_PRODUCERS) + .map(|idx| format!("bp_{}", idx).parse().unwrap()) + .collect(), + chunk_only_producers: (0..NUM_CHUNK_ONLY_PRODUCERS) + .map(|idx| format!("cp_{}", idx).parse().unwrap()) + .collect(), + epoch_length: 4, // arbitrary + num_shards: 3, // arbitrary + track_all_shards: false, + }); + + let chunk_producer = test.data[0].chain.next_chunk_producer(1); + let chunk_producer_idx = test.data.index_for_account(&chunk_producer); + + let sender = test.sender(); + // Typically the chunk-only producer would receive a block later because the block + // producer would need to first produce a block. So we'll just have some arbitrary + // delay here. + let chunk_producer_receive_block_at: time::Duration = time::Duration::milliseconds(50); + test.sender().for_index(chunk_producer_idx).send_adhoc_event("produce chunk", move |data| { + let chunk = data.chain.produce_chunk(1); + data.chain.distribute_chunk(&chunk); + + // The chunk producers may not be forwarded the chunk header, so we + // trigger their processing of it manually, as if they had received it + // via a produced block. + for idx in NUM_BLOCK_PRODUCERS..NUM_BLOCK_PRODUCERS + NUM_CHUNK_ONLY_PRODUCERS { + let chunk_header = chunk.header(); + sender.clone().for_index(idx).schedule_adhoc_event( + "request chunk from block", + move |data| data.chain.request_chunk_for_block(chunk_header), + chunk_producer_receive_block_at, + ); + } + }); + // Run for a network roundtrip after the chunk producers receive the block. + // This should be enough time for the chunk producers to request and + // receive the missing chunk parts. + test.run_for(chunk_producer_receive_block_at + NETWORK_DELAY * 2); + + for idx in 0..test.data.len() { + if idx == chunk_producer_idx { + continue; + } + let chunk_producer = chunk_producer.clone(); + // Run assertions in the test loop so if something fails we know which + // instance failed. + test.sender().for_index(idx).send_adhoc_event("assertions", move |data| { + assert_eq!(data.client_events.len(), 2); + match &data.client_events[0] { + ShardsManagerResponse::ChunkHeaderReadyForInclusion { + chunk_producer: producer, + .. + } => { + assert_eq!(producer, &chunk_producer); + } + _ => panic!("Unexpected event"), + } + match &data.client_events[1] { + ShardsManagerResponse::ChunkCompleted { partial_chunk, shard_chunk } => { + if data.chain.cares_about_shard_this_or_next_epoch(1) { + // If we track this shard we should have all parts. + assert_eq!( + partial_chunk.parts().len(), + data.chain.epoch_manager.num_total_parts() + ); + assert!(shard_chunk.is_some()); + } else { + assert!(shard_chunk.is_none()); + if idx < NUM_BLOCK_PRODUCERS { + // Block producers own one part each. + assert_eq!(partial_chunk.parts().len(), 1); + assert_eq!(partial_chunk.parts()[0].part_ord as usize, idx); + } else { + // Chunk producers don't own any parts. + assert_eq!(partial_chunk.parts().len(), 0); + } + } + for shard in 0..3 { + if data.chain.cares_about_shard_this_or_next_epoch(shard) { + if data.chain.cares_about_shard_this_or_next_epoch(1) { + // If we track shard 1, we should have the full chunk + // and thus have every receipt proof. + assert_eq!(partial_chunk.receipts().len(), 3); + } else { + // Otherwise, we should only have the receipt proof for the + // shard we care about. + assert_eq!(partial_chunk.receipts().len(), 1); + assert_eq!(partial_chunk.receipts()[0].1.to_shard_id, shard); + } + } + } + } + _ => panic!("Unexpected event"), + } + }); + } + test.run_instant(); +} diff --git a/chain/chunks/src/test_loop.rs b/chain/chunks/src/test_loop.rs new file mode 100644 index 000000000..bcecd3b62 --- /dev/null +++ b/chain/chunks/src/test_loop.rs @@ -0,0 +1,385 @@ +use std::{collections::HashMap, sync::Arc}; + +use unc_async::time; +use unc_async::{ + messaging::Sender, + test_loop::event_handler::{interval, LoopEventHandler, LoopHandlerContext, TryIntoOrSelf}, +}; +use unc_chain::{types::Tip, Chain}; +use unc_epoch_manager::{ + shard_tracker::{ShardTracker, TrackedConfig}, + test_utils::{record_block, setup_epoch_manager_with_block_and_chunk_producers}, + EpochManagerAdapter, EpochManagerHandle, +}; +use unc_network::{ + shards_manager::ShardsManagerRequestFromNetwork, + test_loop::SupportsRoutingLookup, + types::{NetworkRequests, PeerManagerMessageRequest}, +}; +use unc_primitives::{ + hash::CryptoHash, + merkle::{self, MerklePath}, + sharding::{ + EncodedShardChunk, PartialEncodedChunk, PartialEncodedChunkV2, ReceiptProof, + ReedSolomonWrapper, ShardChunkHeader, + }, + test_utils::create_test_signer, + types::{AccountId, BlockHeight, BlockHeightDelta, MerkleHash, NumShards, ShardId}, + version::PROTOCOL_VERSION, +}; +use unc_store::Store; + +use crate::{ + adapter::ShardsManagerRequestFromClient, + logic::{cares_about_shard_this_or_next_epoch, make_outgoing_receipts_proofs}, + test_utils::{default_tip, tip}, + ShardsManager, +}; + +pub fn forward_client_request_to_shards_manager( +) -> LoopEventHandler { + LoopEventHandler::new_simple(|event, data: &mut ShardsManager| { + data.handle_client_request(event); + }) +} + +pub fn forward_network_request_to_shards_manager( +) -> LoopEventHandler { + LoopEventHandler::new_simple(|event, data: &mut ShardsManager| { + data.handle_network_request(event); + }) +} + +/// Routes network messages that are issued by ShardsManager to other instances +/// in a multi-instance test. +/// +/// TODO: This logic should ideally not be duplicated from the real +/// PeerManagerActor and PeerActor. +pub fn route_shards_manager_network_messages< + Data: SupportsRoutingLookup, + Event: TryIntoOrSelf + + From + + From, +>( + network_delay: time::Duration, +) -> LoopEventHandler { + let mut route_back_lookup: HashMap = HashMap::new(); + let mut next_hash: u64 = 0; + LoopEventHandler::new( + move |event: (usize, Event), + data: &mut Data, + context: &LoopHandlerContext<(usize, Event)>| { + let (idx, event) = event; + let message = event.try_into_or_self().map_err(|e| (idx, e.into()))?; + match message { + PeerManagerMessageRequest::NetworkRequests(request) => { + match request { + NetworkRequests::PartialEncodedChunkRequest { target, request, .. } => { + let target_idx = data.index_for_account(&target.account_id.unwrap()); + let route_back = CryptoHash::hash_borsh(next_hash); + route_back_lookup.insert(route_back, idx); + next_hash += 1; + context.sender.send_with_delay( + (target_idx, + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkRequest { + partial_encoded_chunk_request: request, + route_back, + }.into()), + network_delay, + ); + Ok(()) + } + NetworkRequests::PartialEncodedChunkResponse { route_back, response } => { + let target_idx = + *route_back_lookup.get(&route_back).expect("Route back not found"); + context.sender.send_with_delay( + (target_idx, + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkResponse { + partial_encoded_chunk_response: response, + received_time: context.clock.now().into(), // TODO: use clock + }.into()), + network_delay, + ); + Ok(()) + } + NetworkRequests::PartialEncodedChunkMessage { + account_id, + partial_encoded_chunk, + } => { + let target_idx = data.index_for_account(&account_id); + context.sender.send_with_delay( + ( + target_idx, + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunk( + partial_encoded_chunk.into(), + ) + .into(), + ), + network_delay, + ); + Ok(()) + } + NetworkRequests::PartialEncodedChunkForward { account_id, forward } => { + let target_idx = data.index_for_account(&account_id); + context.sender.send_with_delay( + (target_idx, + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkForward( + forward, + ).into()), + network_delay, + ); + Ok(()) + } + other_message => Err(( + idx, + PeerManagerMessageRequest::NetworkRequests(other_message).into(), + )), + } + } + message => Err((idx, message.into())), + } + }, + ) +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ShardsManagerResendChunkRequests; + +/// Periodically call resend_chunk_requests. +pub fn periodically_resend_chunk_requests( + every: time::Duration, +) -> LoopEventHandler { + interval(every, ShardsManagerResendChunkRequests, |data: &mut ShardsManager| { + data.resend_chunk_requests() + }) +} + +/// A simple implementation of the chain side that interacts with +/// ShardsManager. +pub struct MockChainForShardsManager { + pub account_id: AccountId, + pub epoch_manager: Arc, + pub shard_tracker: ShardTracker, + pub shards_manager: Sender, + pub tip: Tip, +} + +pub struct MockChainForShardsManagerConfig { + pub account_id: AccountId, + pub num_shards: NumShards, + pub epoch_length: BlockHeightDelta, + pub block_producers: Vec, + pub chunk_only_producers: Vec, + pub track_all_shards: bool, + pub shards_manager: Sender, +} + +impl MockChainForShardsManager { + pub fn new(store: Store, config: MockChainForShardsManagerConfig) -> Self { + let epoch_manager = Arc::new( + setup_epoch_manager_with_block_and_chunk_producers( + store, + config.block_producers, + config.chunk_only_producers, + config.num_shards, + config.epoch_length, + ) + .into_handle(), + ); + let tracking = if config.track_all_shards { + TrackedConfig::AllShards + } else { + TrackedConfig::new_empty() + }; + let shard_tracker = ShardTracker::new(tracking, epoch_manager.clone()); + Self { + account_id: config.account_id, + epoch_manager, + shard_tracker, + shards_manager: config.shards_manager, + tip: default_tip(), + } + } + + /// Adds a new block to the chain. Automatically takes care of Epoch + /// transitions, and automatically updates the tip, and notifies the + /// ShardsManager about the new tip. + pub fn record_block(&mut self, last: CryptoHash, height: BlockHeight) { + record_block( + &mut *self.epoch_manager.write(), + self.tip.last_block_hash, + last, + height, + vec![], + vec![], + ); + self.tip = tip(self.epoch_manager.as_ref(), last); + self.shards_manager.send(ShardsManagerRequestFromClient::UpdateChainHeads { + head: self.tip.clone(), + header_head: self.tip.clone(), + }); + } + + /// Makes a request to the ShardsManager to fetch this chunk. + pub fn request_chunk_for_block(&mut self, chunk_header: ShardChunkHeader) { + // TODO: this request and the next request are somewhat redundant, and the next + // request does not work without the first one. We should consolidate the two + // requests. + self.shards_manager.send(ShardsManagerRequestFromClient::ProcessChunkHeaderFromBlock( + chunk_header.clone(), + )); + if &self.tip.last_block_hash == chunk_header.prev_block_hash() { + self.shards_manager.send(ShardsManagerRequestFromClient::RequestChunks { + chunks_to_request: vec![chunk_header.clone()], + prev_hash: *chunk_header.prev_block_hash(), + }); + } else { + self.shards_manager.send(ShardsManagerRequestFromClient::RequestChunksForOrphan { + chunks_to_request: vec![chunk_header], + epoch_id: self + .epoch_manager + .get_epoch_id_from_prev_block(&self.tip.last_block_hash) + .unwrap(), + ancestor_hash: self.tip.last_block_hash, + }); + } + } + + /// Calculates the next chunk producer (for tip.height + 1) + pub fn next_chunk_producer(&mut self, shard_id: ShardId) -> AccountId { + let epoch_id = + self.epoch_manager.get_epoch_id_from_prev_block(&self.tip.last_block_hash).unwrap(); + self.epoch_manager.get_chunk_producer(&epoch_id, self.tip.height + 1, shard_id).unwrap() + } + + /// Produces the next chunk for the given shard, signed by the chunk + /// producer who is supposed to produce it. + pub fn produce_chunk_signed_by_chunk_producer( + &mut self, + shard_id: ShardId, + ) -> TestChunkEncoder { + let receipts = Vec::new(); + let epoch_id = + self.epoch_manager.get_epoch_id_from_prev_block(&self.tip.last_block_hash).unwrap(); + let shard_layout = self.epoch_manager.get_shard_layout(&epoch_id).unwrap(); + let receipts_hashes = Chain::build_receipts_hashes(&receipts, &shard_layout); + let (receipts_root, _) = merkle::merklize(&receipts_hashes); + let chunk_producer = self.next_chunk_producer(shard_id); + let signer = create_test_signer(chunk_producer.as_str()); + let data_parts = self.epoch_manager.num_data_parts(); + let parity_parts = self.epoch_manager.num_total_parts() - data_parts; + let mut rs = ReedSolomonWrapper::new(data_parts, parity_parts); + let (chunk, merkle_paths) = ShardsManager::create_encoded_shard_chunk( + self.tip.last_block_hash, + CryptoHash::default(), + CryptoHash::default(), + self.tip.height + 1, + shard_id, + 0, + 1000, + 0, + Vec::new(), + Vec::new(), + Vec::new(), + &receipts, + receipts_root, + MerkleHash::default(), + &signer, + &mut rs, + PROTOCOL_VERSION, + ) + .unwrap(); + let receipt_proofs = + make_outgoing_receipts_proofs(&chunk.cloned_header(), &[], self.epoch_manager.as_ref()) + .unwrap() + .collect(); + TestChunkEncoder::new(chunk, merkle_paths, receipt_proofs) + } + + /// Produces the next chunk, asserting that we are the chunk producer. + pub fn produce_chunk(&mut self, shard_id: ShardId) -> TestChunkEncoder { + assert!(self.next_chunk_producer(shard_id) == self.account_id, "Cannot use produce_chunk if we are not the chunk producer; try produce_chunk_signed_by_chunk_producer."); + self.produce_chunk_signed_by_chunk_producer(shard_id) + } + + /// Distributes the produced chunk via the ShardsManager. + pub fn distribute_chunk(&mut self, chunk: &TestChunkEncoder) { + self.shards_manager.send(ShardsManagerRequestFromClient::DistributeEncodedChunk { + encoded_chunk: chunk.encoded_chunk.clone(), + merkle_paths: chunk.merkle_paths.clone(), + outgoing_receipts: Vec::new(), + partial_chunk: chunk.full_partial_chunk.clone(), + }); + } + + /// Whether we care about this shard this or next epoch, + /// as of the current tip. + pub fn cares_about_shard_this_or_next_epoch(&self, shard_id: ShardId) -> bool { + cares_about_shard_this_or_next_epoch( + Some(&self.account_id), + &self.tip.last_block_hash, + shard_id, + true, + &self.shard_tracker, + ) + } +} + +/// A helper struct for encoding partial chunks, for a specific chunk. +pub struct TestChunkEncoder { + encoded_chunk: EncodedShardChunk, + full_partial_chunk: PartialEncodedChunk, + merkle_paths: Vec, +} + +impl TestChunkEncoder { + pub fn new( + encoded_chunk: EncodedShardChunk, + merkle_paths: Vec, + receipt_proofs: Vec, + ) -> Self { + let all_part_ords = + encoded_chunk.content().parts.iter().enumerate().map(|(i, _)| i as u64).collect(); + let full_partial_chunk = encoded_chunk.create_partial_encoded_chunk( + all_part_ords, + receipt_proofs, + &merkle_paths, + ); + Self { encoded_chunk, full_partial_chunk, merkle_paths } + } + + pub fn part_ords(&self) -> Vec { + self.full_partial_chunk.parts().iter().map(|part| part.part_ord).collect() + } + + pub fn make_partial_encoded_chunk( + &self, + part_ords: &[u64], + receipt_shards: &[ShardId], + ) -> PartialEncodedChunk { + let parts = part_ords + .iter() + .copied() + .flat_map(|ord| { + self.full_partial_chunk.parts().iter().find(|part| part.part_ord == ord) + }) + .cloned() + .collect(); + PartialEncodedChunk::V2(PartialEncodedChunkV2 { + header: self.encoded_chunk.cloned_header(), + parts, + receipts: self + .full_partial_chunk + .receipts() + .iter() + .enumerate() + .filter(|(i, _)| receipt_shards.contains(&(*i as ShardId))) + .map(|(_, receipt)| receipt.clone()) + .collect(), + }) + } + + pub fn header(&self) -> ShardChunkHeader { + self.encoded_chunk.cloned_header() + } +} diff --git a/chain/chunks/src/test_utils.rs b/chain/chunks/src/test_utils.rs new file mode 100644 index 000000000..40802b7bd --- /dev/null +++ b/chain/chunks/src/test_utils.rs @@ -0,0 +1,310 @@ +use unc_async::messaging::CanSend; +use unc_chain::types::{EpochManagerAdapter, Tip}; +use unc_chain::{Chain, ChainStore}; +use unc_epoch_manager::shard_tracker::{ShardTracker, TrackedConfig}; +use unc_epoch_manager::test_utils::setup_epoch_manager_with_block_and_chunk_producers; +use unc_epoch_manager::EpochManagerHandle; +use unc_network::shards_manager::ShardsManagerRequestFromNetwork; +use unc_network::test_utils::MockPeerManagerAdapter; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::{self, MerklePath}; +use unc_primitives::receipt::Receipt; +use unc_primitives::sharding::{ + EncodedShardChunk, PartialEncodedChunk, PartialEncodedChunkPart, PartialEncodedChunkV2, + ReedSolomonWrapper, ShardChunkHeader, +}; +use unc_primitives::test_utils::create_test_signer; +use unc_primitives::types::MerkleHash; +use unc_primitives::types::{AccountId, EpochId, ShardId}; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_store::test_utils::create_test_store; +use unc_store::Store; +use std::collections::VecDeque; +use std::sync::{Arc, Mutex, RwLock}; + +use crate::adapter::ShardsManagerRequestFromClient; +use crate::client::ShardsManagerResponse; +use crate::ShardsManager; + +/// Deprecated. Use `MockChainForShardsManager`. +pub struct ChunkTestFixture { + pub store: Store, + pub epoch_manager: EpochManagerHandle, + pub shard_tracker: ShardTracker, + pub mock_network: Arc, + pub mock_client_adapter: Arc, + pub chain_store: ChainStore, + pub all_part_ords: Vec, + pub mock_part_ords: Vec, + pub mock_merkle_paths: Vec, + pub mock_outgoing_receipts: Vec, + pub mock_encoded_chunk: EncodedShardChunk, + pub mock_chunk_part_owner: AccountId, + pub mock_shard_tracker: AccountId, + pub mock_chunk_header: ShardChunkHeader, + pub mock_chunk_parts: Vec, + pub mock_chain_head: Tip, + pub rs: ReedSolomonWrapper, +} + +impl Default for ChunkTestFixture { + fn default() -> Self { + Self::new(false, 3, 6, 6, true) + } +} + +impl ChunkTestFixture { + pub fn new( + orphan_chunk: bool, + num_shards: u64, + num_block_producers: usize, + num_chunk_only_producers: usize, + track_all_shards: bool, + ) -> Self { + if num_shards > num_block_producers as u64 { + panic!("Invalid setup: there must be at least as many block producers as shards"); + } + let store = create_test_store(); + let epoch_manager = setup_epoch_manager_with_block_and_chunk_producers( + store.clone(), + (0..num_block_producers).map(|i| format!("test_bp_{}", i).parse().unwrap()).collect(), + (0..num_chunk_only_producers) + .map(|i| format!("test_cp_{}", i).parse().unwrap()) + .collect(), + num_shards, + 2, + ); + let epoch_manager = epoch_manager.into_handle(); + let shard_tracker = ShardTracker::new( + if track_all_shards { TrackedConfig::AllShards } else { TrackedConfig::new_empty() }, + Arc::new(epoch_manager.clone()), + ); + let mock_network = Arc::new(MockPeerManagerAdapter::default()); + let mock_client_adapter = Arc::new(MockClientAdapterForShardsManager::default()); + + let data_parts = epoch_manager.num_data_parts(); + let parity_parts = epoch_manager.num_total_parts() - data_parts; + let mut rs = ReedSolomonWrapper::new(data_parts, parity_parts); + let mock_ancestor_hash = CryptoHash::default(); + // generate a random block hash for the block at height 1 + let (mock_parent_hash, mock_height) = + if orphan_chunk { (CryptoHash::hash_bytes(&[]), 2) } else { (mock_ancestor_hash, 1) }; + // setting this to 2 instead of 0 so that when chunk producers + let mock_shard_id: ShardId = 0; + let mock_epoch_id = + epoch_manager.get_epoch_id_from_prev_block(&mock_ancestor_hash).unwrap(); + let mock_chunk_producer = + epoch_manager.get_chunk_producer(&mock_epoch_id, mock_height, mock_shard_id).unwrap(); + let signer = create_test_signer(mock_chunk_producer.as_str()); + let validators: Vec<_> = epoch_manager + .get_epoch_block_producers_ordered(&EpochId::default(), &CryptoHash::default()) + .unwrap() + .into_iter() + .map(|v| v.0.account_id().clone()) + .collect(); + let mock_shard_tracker = validators + .iter() + .find(|v| { + if v == &&mock_chunk_producer { + false + } else { + let tracks_shard = shard_tracker.care_about_shard( + Some(*v), + &mock_ancestor_hash, + mock_shard_id, + false, + ) || shard_tracker.will_care_about_shard( + Some(*v), + &mock_ancestor_hash, + mock_shard_id, + false, + ); + tracks_shard + } + }) + .cloned() + .unwrap(); + let mock_chunk_part_owner = validators + .into_iter() + .find(|v| v != &mock_chunk_producer && v != &mock_shard_tracker) + .unwrap(); + + let receipts = Vec::new(); + let shard_layout = epoch_manager.get_shard_layout(&EpochId::default()).unwrap(); + let receipts_hashes = Chain::build_receipts_hashes(&receipts, &shard_layout); + let (receipts_root, _) = merkle::merklize(&receipts_hashes); + let (mock_chunk, mock_merkle_paths) = ShardsManager::create_encoded_shard_chunk( + mock_parent_hash, + Default::default(), + Default::default(), + mock_height, + mock_shard_id, + 0, + 1000, + 0, + Vec::new(), + Vec::new(), + Vec::new(), + &receipts, + receipts_root, + MerkleHash::default(), + &signer, + &mut rs, + PROTOCOL_VERSION, + ) + .unwrap(); + + let all_part_ords: Vec = + (0..mock_chunk.content().parts.len()).map(|p| p as u64).collect(); + let mock_part_ords = all_part_ords + .iter() + .copied() + .filter(|p| { + epoch_manager.get_part_owner(&mock_epoch_id, *p).unwrap() == mock_chunk_part_owner + }) + .collect(); + let encoded_chunk = mock_chunk.create_partial_encoded_chunk( + all_part_ords.clone(), + Vec::new(), + &mock_merkle_paths, + ); + let chain_store = ChainStore::new(store.clone(), 0, true); + + ChunkTestFixture { + store, + epoch_manager, + shard_tracker, + mock_network, + mock_client_adapter, + chain_store, + all_part_ords, + mock_part_ords, + mock_encoded_chunk: mock_chunk, + mock_merkle_paths, + mock_outgoing_receipts: receipts, + mock_chunk_part_owner, + mock_shard_tracker, + mock_chunk_header: encoded_chunk.cloned_header(), + mock_chunk_parts: encoded_chunk.parts().to_vec(), + mock_chain_head: Tip { + height: 0, + last_block_hash: CryptoHash::default(), + prev_block_hash: CryptoHash::default(), + epoch_id: EpochId::default(), + next_epoch_id: EpochId::default(), + }, + rs, + } + } + + pub fn make_partial_encoded_chunk(&self, part_ords: &[u64]) -> PartialEncodedChunk { + let parts = part_ords + .iter() + .copied() + .flat_map(|ord| self.mock_chunk_parts.iter().find(|part| part.part_ord == ord)) + .cloned() + .collect(); + PartialEncodedChunk::V2(PartialEncodedChunkV2 { + header: self.mock_chunk_header.clone(), + parts, + receipts: Vec::new(), + }) + } + + pub fn count_chunk_completion_messages(&self) -> usize { + let mut chunks_completed = 0; + while let Some(message) = self.mock_client_adapter.pop() { + if let ShardsManagerResponse::ChunkCompleted { .. } = message { + chunks_completed += 1; + } + } + chunks_completed + } + + pub fn count_chunk_ready_for_inclusion_messages(&self) -> usize { + let mut chunks_ready = 0; + while let Some(message) = self.mock_client_adapter.pop() { + if let ShardsManagerResponse::ChunkHeaderReadyForInclusion { .. } = message { + chunks_ready += 1; + } + } + chunks_ready + } +} + +/// Gets the Tip from the block hash and the EpochManager. +pub fn tip(epoch_manager: &dyn EpochManagerAdapter, last_block_hash: CryptoHash) -> Tip { + let block_info = epoch_manager.get_block_info(&last_block_hash).unwrap(); + let epoch_id = epoch_manager.get_epoch_id(&last_block_hash).unwrap(); + let next_epoch_id = epoch_manager.get_next_epoch_id(&last_block_hash).unwrap(); + Tip { + height: block_info.height(), + last_block_hash, + prev_block_hash: *block_info.prev_hash(), + epoch_id, + next_epoch_id, + } +} + +/// Returns the tip representing the genesis block for testing. +pub fn default_tip() -> Tip { + Tip { + height: 0, + last_block_hash: CryptoHash::default(), + prev_block_hash: CryptoHash::default(), + epoch_id: EpochId::default(), + next_epoch_id: EpochId::default(), + } +} + +// Mocked `PeerManager` adapter, has a queue of `PeerManagerMessageRequest` messages. +#[derive(Default)] +pub struct MockClientAdapterForShardsManager { + pub requests: Arc>>, +} + +impl CanSend for MockClientAdapterForShardsManager { + fn send(&self, msg: ShardsManagerResponse) { + self.requests.write().unwrap().push_back(msg); + } +} + +impl MockClientAdapterForShardsManager { + pub fn pop(&self) -> Option { + self.requests.write().unwrap().pop_front() + } + pub fn pop_most_recent(&self) -> Option { + self.requests.write().unwrap().pop_back() + } +} + +// Allows ShardsManagerActor-like behavior, except without having to spawn an actor, +// and without having to manually route ShardsManagerRequest messages. This only works +// for single-threaded (synchronous) tests. The ShardsManager is immediately called +// upon receiving a ShardsManagerRequest message. +#[derive(Clone)] +pub struct SynchronousShardsManagerAdapter { + // Need a mutex here even though we only support single-threaded tests, because + // MsgRecipient requires Sync. + pub shards_manager: Arc>, +} + +impl CanSend for SynchronousShardsManagerAdapter { + fn send(&self, msg: ShardsManagerRequestFromClient) { + let mut shards_manager = self.shards_manager.lock().unwrap(); + shards_manager.handle_client_request(msg); + } +} + +impl CanSend for SynchronousShardsManagerAdapter { + fn send(&self, msg: ShardsManagerRequestFromNetwork) { + let mut shards_manager = self.shards_manager.lock().unwrap(); + shards_manager.handle_network_request(msg); + } +} + +impl SynchronousShardsManagerAdapter { + pub fn new(shards_manager: ShardsManager) -> Self { + Self { shards_manager: Arc::new(Mutex::new(shards_manager)) } + } +} diff --git a/chain/client-primitives/Cargo.toml b/chain/client-primitives/Cargo.toml new file mode 100644 index 000000000..442644fb2 --- /dev/null +++ b/chain/client-primitives/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "unc-client-primitives" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "This crate hosts NEAR client-related error types" +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +actix.workspace = true +chrono.workspace = true +serde.workspace = true +serde_json.workspace = true +strum.workspace = true +thiserror.workspace = true +tracing.workspace = true +yansi.workspace = true + +unc-chain-primitives.workspace = true +unc-chain-configs.workspace = true +unc-chunks-primitives.workspace = true +unc-crypto.workspace = true +unc-primitives.workspace = true + +[features] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-primitives/nightly_protocol", +] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-primitives/nightly", +] +sandbox = [] diff --git a/chain/client-primitives/src/debug.rs b/chain/client-primitives/src/debug.rs new file mode 100644 index 000000000..a5d2884cc --- /dev/null +++ b/chain/client-primitives/src/debug.rs @@ -0,0 +1,209 @@ +//! Structs in this module are used for debug purposes, and might change at any time +//! without backwards compatibility of JSON encoding. +use crate::types::StatusError; +use chrono::DateTime; +use unc_primitives::types::EpochId; +use unc_primitives::views::{ + CatchupStatusView, ChainProcessingInfo, EpochValidatorInfo, RequestedStatePartsView, + SyncStatusView, +}; +use unc_primitives::{ + block_header::ApprovalInner, + hash::CryptoHash, + sharding::ChunkHash, + types::{AccountId, BlockHeight}, + views::ValidatorInfo, +}; +use std::collections::HashMap; + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct TrackedShardsView { + pub shards_tracked_this_epoch: Vec, + pub shards_tracked_next_epoch: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct EpochInfoView { + pub epoch_id: CryptoHash, + pub height: BlockHeight, + pub first_block: Option<(CryptoHash, DateTime)>, + pub block_producers: Vec, + pub chunk_only_producers: Vec, + pub validator_info: Option, + pub protocol_version: u32, + pub shards_size_and_parts: Vec<(u64, u64, bool)>, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct DebugChunkStatus { + pub shard_id: u64, + pub chunk_hash: ChunkHash, + pub chunk_producer: Option, + pub gas_used: u64, + #[serde(skip_serializing_if = "Option::is_none")] + pub processing_time_ms: Option, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct DebugBlockStatus { + pub block_hash: CryptoHash, + pub prev_block_hash: CryptoHash, + pub block_height: u64, + pub block_timestamp: u64, + pub block_producer: Option, + pub full_block_missing: bool, // only header available + pub is_on_canonical_chain: bool, + pub chunks: Vec, + // Time that was spent processing a given block. + #[serde(skip_serializing_if = "Option::is_none")] + pub processing_time_ms: Option, + pub gas_price_ratio: f64, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct MissedHeightInfo { + pub block_height: u64, + pub block_producer: Option, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct DebugBlockStatusData { + pub blocks: Vec, + pub missed_heights: Vec, + pub head: CryptoHash, + pub header_head: CryptoHash, +} + +// Information about the approval created by this node. +// Used for debug purposes only. +#[derive(serde::Serialize, Debug, Clone)] +pub struct ApprovalHistoryEntry { + // If target_height == base_height + 1 - this is endorsement. + // Otherwise this is a skip. + pub parent_height: BlockHeight, + pub target_height: BlockHeight, + // Time when we actually created the approval and sent it out. + pub approval_creation_time: DateTime, + // The moment when we were ready to send this approval (or skip) + pub timer_started_ago_millis: u64, + // But we had to wait at least this long before doing it. + pub expected_delay_millis: u64, +} + +// Information about chunk produced by this node. +// For debug purposes only. +#[derive(serde::Serialize, Debug, Default, Clone)] +pub struct ChunkProduction { + // Time when we produced the chunk. + pub chunk_production_time: Option>, + // How long did the chunk production take (reed solomon encoding, preparing fragments etc.) + // Doesn't include network latency. + pub chunk_production_duration_millis: Option, +} +// Information about the block produced by this node. +// For debug purposes only. +#[derive(serde::Serialize, Debug, Clone, Default)] +pub struct BlockProduction { + // Approvals that we received. + pub approvals: ApprovalAtHeightStatus, + // Chunk producer and time at which we received chunk for given shard. This field will not be + // set if we didn't produce the block. + pub chunks_collection_time: Vec, + // Time when we produced the block, None if we didn't produce the block. + pub block_production_time: Option>, + // Whether this block is included on the canonical chain. + pub block_included: bool, +} + +#[derive(serde::Serialize, Debug, Clone)] +pub struct ChunkCollection { + // Chunk producer of the chunk + pub chunk_producer: AccountId, + // Time when the chunk was received. Note that this field can be filled even if the block doesn't + // include a chunk for the shard, if a chunk at this height was received after the block was produced. + pub received_time: Option>, + // Whether the block included a chunk for this shard + pub chunk_included: bool, +} + +// Information about things related to block/chunk production +// at given height. +// For debug purposes only. +#[derive(serde::Serialize, Debug, Default)] +pub struct ProductionAtHeight { + // Stores information about block production is we are responsible for producing this block, + // None if we are not responsible for producing this block. + pub block_production: Option, + // Map from shard_id to chunk that we are responsible to produce at this height + pub chunk_production: HashMap, +} + +// Infromation about the approvals that we received. +#[derive(serde::Serialize, Debug, Default, Clone)] +pub struct ApprovalAtHeightStatus { + // Map from validator id to the type of approval that they sent and timestamp. + pub approvals: HashMap)>, + // Time at which we received 2/3 approvals (doomslug threshold). + pub ready_at: Option>, +} + +#[derive(serde::Serialize, Debug)] +pub struct ValidatorStatus { + pub validator_name: Option, + // Current number of shards + pub shards: u64, + // Current height. + pub head_height: u64, + // Current validators with their stake (stake is in NEAR - not yoctonear). + pub validators: Option>, + // All approvals that we've sent. + pub approval_history: Vec, + // Blocks & chunks that we've produced or about to produce. + // Sorted by block height inversely (high to low) + // The range of heights are controlled by constants in client_actor.rs + pub production: Vec<(BlockHeight, ProductionAtHeight)>, + // Chunk producers that this node has banned. + pub banned_chunk_producers: Vec<(EpochId, Vec)>, +} + +// Different debug requests that can be sent by HTML pages, via GET. +#[derive(Debug)] +pub enum DebugStatus { + // Request for the current sync status + SyncStatus, + // Request currently tracked shards + TrackedShards, + // Detailed information about last couple epochs. + EpochInfo, + // Detailed information about last couple blocks. + BlockStatus(Option), + // Consensus related information. + ValidatorStatus, + // Request for the current catchup status + CatchupStatus, + // Request for the current state of chain processing (blocks in progress etc). + ChainProcessingStatus, + // The state parts already requested. + RequestedStateParts, +} + +impl actix::Message for DebugStatus { + type Result = Result; +} + +#[derive(serde::Serialize, Debug)] +pub enum DebugStatusResponse { + SyncStatus(SyncStatusView), + CatchupStatus(Vec), + TrackedShards(TrackedShardsView), + // List of epochs - in descending order (next epoch is first). + EpochInfo(Vec), + // Detailed information about blocks. + BlockStatus(DebugBlockStatusData), + // Detailed information about the validator (approvals, block & chunk production etc.) + ValidatorStatus(ValidatorStatus), + // Detailed information about chain processing (blocks in progress etc). + ChainProcessingStatus(ChainProcessingInfo), + // The state parts already requested. + RequestedStateParts(Vec), +} diff --git a/chain/client-primitives/src/lib.rs b/chain/client-primitives/src/lib.rs new file mode 100644 index 000000000..67cacc936 --- /dev/null +++ b/chain/client-primitives/src/lib.rs @@ -0,0 +1,2 @@ +pub mod debug; +pub mod types; diff --git a/chain/client-primitives/src/types.rs b/chain/client-primitives/src/types.rs new file mode 100644 index 000000000..54b12f8f2 --- /dev/null +++ b/chain/client-primitives/src/types.rs @@ -0,0 +1,1262 @@ +use actix::Message; +use chrono::DateTime; +use chrono::Utc; +use unc_chain_configs::{ClientConfig, ProtocolConfigView}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::{MerklePath, PartialMerkleTree}; +use unc_primitives::network::PeerId; +use unc_primitives::sharding::ChunkHash; +use unc_primitives::types::{ + AccountId, BlockHeight, BlockReference, EpochId, EpochReference, MaybeBlockId, ShardId, + TransactionOrReceiptId, +}; +use unc_primitives::views::{BlockView, ChunkView, DownloadStatusView, EpochValidatorInfo, ExecutionOutcomeWithIdView, GasPriceView, LightClientBlockLiteView, LightClientBlockView, MaintenanceWindowsView, MinerChipsListView, QueryRequest, QueryResponse, ReceiptView, ShardSyncDownloadView, SplitStorageInfoView, StateChangesKindsView, StateChangesRequestView, StateChangesView, SyncStatusView, TxStatusView}; +pub use unc_primitives::views::{StatusResponse, StatusSyncInfo}; +use std::collections::HashMap; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use tracing::debug_span; +use yansi::Color::Magenta; +use yansi::Style; +use unc_primitives::errors::EpochError; +use unc_primitives::views::validator_power_and_frozen_view::ValidatorPowerAndFrozenView; + +/// Combines errors coming from chain, tx pool and block producer. +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Chain: {0}")] + Chain(#[from] unc_chain_primitives::Error), + #[error("Chunk: {0}")] + Chunk(#[from] unc_chunks_primitives::Error), + #[error("Block Producer: {0}")] + BlockProducer(String), + #[error("Chunk Producer: {0}")] + ChunkProducer(String), + #[error("Other: {0}")] + Other(String), +} + +impl From for Error { + fn from(err: unc_primitives::errors::EpochError) -> Self { + Error::Chain(err.into()) + } +} + +#[derive(Debug, serde::Serialize)] +pub struct DownloadStatus { + pub start_time: DateTime, + pub prev_update_time: DateTime, + pub run_me: Arc, + pub error: bool, + pub done: bool, + pub state_requests_count: u64, + pub last_target: Option, +} + +impl DownloadStatus { + pub fn new(now: DateTime) -> Self { + Self { + start_time: now, + prev_update_time: now, + run_me: Arc::new(AtomicBool::new(true)), + error: false, + done: false, + state_requests_count: 0, + last_target: None, + } + } +} + +impl Clone for DownloadStatus { + /// Clones an object, but it clones the value of `run_me` instead of the + /// `Arc` that wraps that value. + fn clone(&self) -> Self { + DownloadStatus { + start_time: self.start_time, + prev_update_time: self.prev_update_time, + // Creates a new `Arc` holding the same value. + run_me: Arc::new(AtomicBool::new(self.run_me.load(Ordering::SeqCst))), + error: self.error, + done: self.done, + state_requests_count: self.state_requests_count, + last_target: self.last_target.clone(), + } + } +} + +/// Various status of syncing a specific shard. +#[derive(Clone, Debug)] +pub enum ShardSyncStatus { + StateDownloadHeader, + StateDownloadParts, + StateDownloadScheduling, + StateDownloadApplying, + StateDownloadComplete, + ReshardingScheduling, + ReshardingApplying, + StateSyncDone, +} + +impl ShardSyncStatus { + pub fn repr(&self) -> u8 { + match self { + ShardSyncStatus::StateDownloadHeader => 0, + ShardSyncStatus::StateDownloadParts => 1, + ShardSyncStatus::StateDownloadScheduling => 2, + ShardSyncStatus::StateDownloadApplying => 3, + ShardSyncStatus::StateDownloadComplete => 4, + ShardSyncStatus::ReshardingScheduling => 5, + ShardSyncStatus::ReshardingApplying => 6, + ShardSyncStatus::StateSyncDone => 7, + } + } +} + +/// Manually implement compare for ShardSyncStatus to compare only based on variant name +impl PartialEq for ShardSyncStatus { + fn eq(&self, other: &Self) -> bool { + std::mem::discriminant(self) == std::mem::discriminant(other) + } +} + +impl Eq for ShardSyncStatus {} + +impl ToString for ShardSyncStatus { + fn to_string(&self) -> String { + match self { + ShardSyncStatus::StateDownloadHeader => "header".to_string(), + ShardSyncStatus::StateDownloadParts => "parts".to_string(), + ShardSyncStatus::StateDownloadScheduling => "scheduling".to_string(), + ShardSyncStatus::StateDownloadApplying => "applying".to_string(), + ShardSyncStatus::StateDownloadComplete => "download complete".to_string(), + ShardSyncStatus::ReshardingScheduling => "resharding scheduling".to_string(), + ShardSyncStatus::ReshardingApplying => "resharding applying".to_string(), + ShardSyncStatus::StateSyncDone => "done".to_string(), + } + } +} + +impl From<&DownloadStatus> for DownloadStatusView { + fn from(status: &DownloadStatus) -> Self { + DownloadStatusView { done: status.done, error: status.error } + } +} + +impl From for ShardSyncDownloadView { + fn from(download: ShardSyncDownload) -> Self { + ShardSyncDownloadView { + downloads: download.downloads.iter().map(|x| x.into()).collect(), + status: download.status.to_string(), + } + } +} + +/// Stores status of shard sync and statuses of downloading shards. +#[derive(Clone, Debug)] +pub struct ShardSyncDownload { + /// Stores all download statuses. If we are downloading state parts, its + /// length equals the number of state parts. Otherwise it is 1, since we + /// have only one piece of data to download, like shard state header. It + /// could be 0 when we are not downloading anything but rather splitting a + /// shard as part of resharding. + pub downloads: Vec, + pub status: ShardSyncStatus, +} + +impl ShardSyncDownload { + /// Creates a instance of self which includes initial statuses for shard state header download at the given time. + pub fn new_download_state_header(now: DateTime) -> Self { + Self { + downloads: vec![DownloadStatus::new(now)], + status: ShardSyncStatus::StateDownloadHeader, + } + } + + /// Creates a instance of self which includes initial statuses for shard state parts download at the given time. + pub fn new_download_state_parts(now: DateTime, num_parts: u64) -> Self { + // Avoid using `vec![x; num_parts]`, because each element needs to have + // its own independent value of `response`. + let mut downloads = Vec::with_capacity(num_parts as usize); + for _ in 0..num_parts { + downloads.push(DownloadStatus::new(now)); + } + Self { downloads, status: ShardSyncStatus::StateDownloadParts } + } +} + +pub fn format_shard_sync_phase_per_shard( + new_shard_sync: &HashMap, + use_colour: bool, +) -> Vec<(ShardId, String)> { + new_shard_sync + .iter() + .map(|(&shard_id, shard_progress)| { + (shard_id, format_shard_sync_phase(shard_progress, use_colour)) + }) + .collect::>() +} + +/// Applies style if `use_colour` is enabled. +fn paint(s: &str, style: Style, use_style: bool) -> String { + if use_style { + style.paint(s).to_string() + } else { + s.to_string() + } +} + +/// Formats the given ShardSyncDownload for logging. +pub fn format_shard_sync_phase( + shard_sync_download: &ShardSyncDownload, + use_colour: bool, +) -> String { + match &shard_sync_download.status { + ShardSyncStatus::StateDownloadHeader => format!( + "{} requests sent {}, last target {:?}", + paint("HEADER", Magenta.style().bold(), use_colour), + shard_sync_download.downloads.get(0).map_or(0, |x| x.state_requests_count), + shard_sync_download.downloads.get(0).map_or(None, |x| x.last_target.as_ref()), + ), + ShardSyncStatus::StateDownloadParts => { + let mut num_parts_done = 0; + let mut num_parts_not_done = 0; + for download in shard_sync_download.downloads.iter() { + if download.done { + num_parts_done += 1; + } else { + num_parts_not_done += 1; + } + } + format!("num_parts_done={num_parts_done} num_parts_not_done={num_parts_not_done}") + } + status => format!("{status:?}"), + } +} + +#[derive(Clone)] +pub struct StateSyncStatus { + pub sync_hash: CryptoHash, + pub sync_status: HashMap, +} + +/// If alternate flag was specified, write formatted sync_status per shard. +impl std::fmt::Debug for StateSyncStatus { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if f.alternate() { + write!( + f, + "StateSyncStatus {{ sync_hash: {:?}, shard_sync: {:?} }}", + self.sync_hash, + format_shard_sync_phase_per_shard(&self.sync_status, false) + ) + } else { + write!( + f, + "StateSyncStatus {{ sync_hash: {:?}, sync_status: {:?} }}", + self.sync_hash, self.sync_status + ) + } + } +} + +/// Various status sync can be in, whether it's fast sync or archival. +#[derive(Clone, Debug, strum::AsRefStr)] +pub enum SyncStatus { + /// Initial state. Not enough peers to do anything yet. + AwaitingPeers, + /// Not syncing / Done syncing. + NoSync, + /// Syncing using light-client headers to a recent epoch + // TODO #3488 + // Bowen: why do we use epoch ordinal instead of epoch id? + EpochSync { epoch_ord: u64 }, + /// Downloading block headers for fast sync. + HeaderSync { + /// Head height at the beginning. Not the header head height! + /// Used only for reporting the progress of the sync. + start_height: BlockHeight, + /// Current header head height. + current_height: BlockHeight, + /// Highest height of our peers. + highest_height: BlockHeight, + }, + /// State sync, with different states of state sync for different shards. + StateSync(StateSyncStatus), + /// Sync state across all shards is done. + StateSyncDone, + /// Download and process blocks until the head reaches the head of the network. + BlockSync { + /// Header head height at the beginning. + /// Used only for reporting the progress of the sync. + start_height: BlockHeight, + /// Current head height. + current_height: BlockHeight, + /// Highest height of our peers. + highest_height: BlockHeight, + }, +} + +impl SyncStatus { + /// Get a string representation of the status variant + pub fn as_variant_name(&self) -> &str { + self.as_ref() + } + + /// True if currently engaged in syncing the chain. + pub fn is_syncing(&self) -> bool { + match self { + SyncStatus::NoSync => false, + _ => true, + } + } + + pub fn repr(&self) -> u8 { + match self { + // Represent NoSync as 0 because it is the state of a normal well-behaving node. + SyncStatus::NoSync => 0, + SyncStatus::AwaitingPeers => 1, + SyncStatus::EpochSync { .. } => 2, + SyncStatus::HeaderSync { .. } => 3, + SyncStatus::StateSync(_) => 4, + SyncStatus::StateSyncDone => 5, + SyncStatus::BlockSync { .. } => 6, + } + } + + pub fn start_height(&self) -> Option { + match self { + SyncStatus::HeaderSync { start_height, .. } => Some(*start_height), + SyncStatus::BlockSync { start_height, .. } => Some(*start_height), + _ => None, + } + } + + pub fn update(&mut self, new_value: Self) { + let _span = + debug_span!(target: "sync", "update_sync_status", old_value = ?self, ?new_value) + .entered(); + *self = new_value; + } +} + +impl From for SyncStatusView { + fn from(status: SyncStatus) -> Self { + match status { + SyncStatus::AwaitingPeers => SyncStatusView::AwaitingPeers, + SyncStatus::NoSync => SyncStatusView::NoSync, + SyncStatus::EpochSync { epoch_ord } => SyncStatusView::EpochSync { epoch_ord }, + SyncStatus::HeaderSync { start_height, current_height, highest_height } => { + SyncStatusView::HeaderSync { start_height, current_height, highest_height } + } + SyncStatus::StateSync(state_sync_status) => SyncStatusView::StateSync( + state_sync_status.sync_hash, + state_sync_status + .sync_status + .into_iter() + .map(|(shard_id, shard_sync)| (shard_id, shard_sync.into())) + .collect(), + ), + SyncStatus::StateSyncDone => SyncStatusView::StateSyncDone, + SyncStatus::BlockSync { start_height, current_height, highest_height } => { + SyncStatusView::BlockSync { start_height, current_height, highest_height } + } + } + } +} + +/// Actor message requesting block provider by EpochId and BlockHeight. +#[derive(Debug)] +pub struct GetProvider(pub EpochId, pub BlockHeight); + +#[derive(thiserror::Error, Debug)] +pub enum GetProviderError { + #[error("IO Error: {error_message}")] + IOError { error_message: String }, + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { error_message: String }, + #[error("There are no fully synchronized blocks yet")] + NotSyncedYet, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for crate::types::GetProviderError { + fn from(error: unc_chain_primitives::Error) -> Self { + match error { + unc_chain_primitives::Error::IOErr(error) => { + Self::IOError { error_message: error.to_string() } + } + unc_chain_primitives::Error::DBNotFoundErr(error_message) => { + Self::UnknownBlock { error_message } + } + _ => Self::Unreachable { error_message: error.to_string() }, + } + } +} + +impl From for GetProviderError { + fn from(error: EpochError) -> Self { + Self::IOError { error_message: error.to_string() } + } +} + + +impl Message for crate::types::GetProvider { + type Result = Result; +} + +/// Actor message requesting all chips owned by AccountId +#[derive(Debug)] +pub struct MinerChipsList(pub AccountId); + +#[derive(thiserror::Error, Debug)] +pub enum MinerChipsListError { + #[error("IO Error: {error_message}")] + IOError { error_message: String }, + #[error("Account either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownAccount { error_message: String }, + #[error("Chips info unavailable")] + ChipsInfoUnavailable, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for crate::types::MinerChipsListError { + fn from(error: unc_chain_primitives::Error) -> Self { + match error { + unc_chain_primitives::Error::IOErr(error) => { + Self::IOError { error_message: error.to_string() } + } + unc_chain_primitives::Error::DBNotFoundErr(error_message) => { + Self::UnknownAccount { error_message } + } + _ => Self::Unreachable { error_message: error.to_string() }, + } + } +} + +impl From for crate::types::MinerChipsListError { + fn from(error: EpochError) -> Self { + Self::IOError { error_message: error.to_string() } + } +} + + +impl Message for crate::types::MinerChipsList { + type Result = Result; +} + +/// Actor message requesting block by id, hash or sync state. +#[derive(Debug)] +pub struct GetBlock(pub BlockReference); + +#[derive(thiserror::Error, Debug)] +pub enum GetBlockError { + #[error("IO Error: {error_message}")] + IOError { error_message: String }, + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { error_message: String }, + #[error("There are no fully synchronized blocks yet")] + NotSyncedYet, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for GetBlockError { + fn from(error: unc_chain_primitives::Error) -> Self { + match error { + unc_chain_primitives::Error::IOErr(error) => { + Self::IOError { error_message: error.to_string() } + } + unc_chain_primitives::Error::DBNotFoundErr(error_message) => { + Self::UnknownBlock { error_message } + } + _ => Self::Unreachable { error_message: error.to_string() }, + } + } +} + +impl GetBlock { + pub fn latest() -> Self { + Self(BlockReference::latest()) + } +} + +impl Message for GetBlock { + type Result = Result; +} + +/// Get block with the block merkle tree. Used for testing +#[derive(Debug)] +pub struct GetBlockWithMerkleTree(pub BlockReference); + +impl GetBlockWithMerkleTree { + pub fn latest() -> Self { + Self(BlockReference::latest()) + } +} + +impl Message for GetBlockWithMerkleTree { + type Result = Result<(BlockView, Arc), GetBlockError>; +} + +/// Actor message requesting a chunk by chunk hash and block hash + shard id. +#[derive(Debug)] +pub enum GetChunk { + Height(BlockHeight, ShardId), + BlockHash(CryptoHash, ShardId), + ChunkHash(ChunkHash), +} + +impl Message for GetChunk { + type Result = Result; +} + +#[derive(thiserror::Error, Debug)] +pub enum GetChunkError { + #[error("IO Error: {error_message}")] + IOError { error_message: String }, + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { error_message: String }, + #[error("Shard ID {shard_id} is invalid")] + InvalidShardId { shard_id: u64 }, + #[error("Chunk with hash {chunk_hash:?} has never been observed on this node")] + UnknownChunk { chunk_hash: ChunkHash }, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for GetChunkError { + fn from(error: unc_chain_primitives::Error) -> Self { + match error { + unc_chain_primitives::Error::IOErr(error) => { + Self::IOError { error_message: error.to_string() } + } + unc_chain_primitives::Error::DBNotFoundErr(error_message) => { + Self::UnknownBlock { error_message } + } + unc_chain_primitives::Error::InvalidShardId(shard_id) => { + Self::InvalidShardId { shard_id } + } + unc_chain_primitives::Error::ChunkMissing(chunk_hash) => { + Self::UnknownChunk { chunk_hash } + } + _ => Self::Unreachable { error_message: error.to_string() }, + } + } +} + +/// Queries client for given path / data. +#[derive(Clone, Debug)] +pub struct Query { + pub block_reference: BlockReference, + pub request: QueryRequest, +} + +impl Query { + pub fn new(block_reference: BlockReference, request: QueryRequest) -> Self { + Query { block_reference, request } + } +} + +impl Message for Query { + type Result = Result; +} + +#[derive(thiserror::Error, Debug)] +pub enum QueryError { + #[error("There are no fully synchronized blocks on the node yet")] + NoSyncedBlocks, + #[error("The node does not track the shard ID {requested_shard_id}")] + UnavailableShard { requested_shard_id: unc_primitives::types::ShardId }, + #[error("Account ID {requested_account_id} is invalid")] + InvalidAccount { + requested_account_id: unc_primitives::types::AccountId, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error( + "Account {requested_account_id} does not exist while viewing at block #{block_height}" + )] + UnknownAccount { + requested_account_id: unc_primitives::types::AccountId, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error( + "Contract code for contract ID {contract_account_id} has never been observed on the node at block #{block_height}" + )] + NoContractCode { + contract_account_id: unc_primitives::types::AccountId, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("State of contract {contract_account_id} is too large to be viewed")] + TooLargeContractState { + contract_account_id: unc_primitives::types::AccountId, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("Access key for public key {public_key} has never been observed on the node at block #{block_height}")] + UnknownAccessKey { + public_key: unc_crypto::PublicKey, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("Function call returned an error: {vm_error}")] + ContractExecutionError { + vm_error: String, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, + #[error( + "The data for block #{block_height} is garbage collected on this node, use an archival node to fetch historical data" + )] + GarbageCollectedBlock { + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("Block either has never been observed on the node or has been garbage collected: {block_reference:?}")] + UnknownBlock { block_reference: unc_primitives::types::BlockReference }, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +#[derive(Debug)] +pub struct Status { + pub is_health_check: bool, + // If true - return more detailed information about the current status (recent blocks etc). + pub detailed: bool, +} + +#[derive(thiserror::Error, Debug)] +pub enum StatusError { + #[error("Node is syncing")] + NodeIsSyncing, + #[error("No blocks for {elapsed:?}")] + NoNewBlocks { elapsed: std::time::Duration }, + #[error("Epoch Out Of Bounds {epoch_id:?}")] + EpochOutOfBounds { epoch_id: unc_primitives::types::EpochId }, + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for StatusError { + fn from(error: unc_chain_primitives::error::Error) -> Self { + match error { + unc_chain_primitives::error::Error::IOErr(error) => { + Self::InternalError { error_message: error.to_string() } + } + unc_chain_primitives::error::Error::DBNotFoundErr(error_message) + | unc_chain_primitives::error::Error::ValidatorError(error_message) => { + Self::InternalError { error_message } + } + unc_chain_primitives::error::Error::EpochOutOfBounds(epoch_id) => { + Self::EpochOutOfBounds { epoch_id } + } + _ => Self::Unreachable { error_message: error.to_string() }, + } + } +} + +impl Message for Status { + type Result = Result; +} + +#[derive(Debug)] +pub struct GetNextLightClientBlock { + pub last_block_hash: CryptoHash, +} + +#[derive(thiserror::Error, Debug)] +pub enum GetNextLightClientBlockError { + #[error("Internal error: {error_message}")] + InternalError { error_message: String }, + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { error_message: String }, + #[error("Epoch Out Of Bounds {epoch_id:?}")] + EpochOutOfBounds { epoch_id: unc_primitives::types::EpochId }, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for GetNextLightClientBlockError { + fn from(error: unc_chain_primitives::error::Error) -> Self { + match error { + unc_chain_primitives::error::Error::DBNotFoundErr(error_message) => { + Self::UnknownBlock { error_message } + } + unc_chain_primitives::error::Error::IOErr(error) => { + Self::InternalError { error_message: error.to_string() } + } + unc_chain_primitives::error::Error::EpochOutOfBounds(epoch_id) => { + Self::EpochOutOfBounds { epoch_id } + } + _ => Self::Unreachable { error_message: error.to_string() }, + } + } +} + +impl Message for GetNextLightClientBlock { + type Result = Result>, GetNextLightClientBlockError>; +} + +#[derive(Debug)] +pub struct GetNetworkInfo {} + +impl Message for GetNetworkInfo { + type Result = Result; +} + +#[derive(Debug)] +pub struct GetGasPrice { + pub block_id: MaybeBlockId, +} + +impl Message for GetGasPrice { + type Result = Result; +} + +#[derive(thiserror::Error, Debug)] +pub enum GetGasPriceError { + #[error("Internal error: {error_message}")] + InternalError { error_message: String }, + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { error_message: String }, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for GetGasPriceError { + fn from(error: unc_chain_primitives::Error) -> Self { + match error { + unc_chain_primitives::Error::IOErr(error) => { + Self::InternalError { error_message: error.to_string() } + } + unc_chain_primitives::Error::DBNotFoundErr(error_message) => { + Self::UnknownBlock { error_message } + } + _ => Self::Unreachable { error_message: error.to_string() }, + } + } +} + +#[derive(Clone, Debug)] +pub struct PeerInfo { + pub id: PeerId, + pub addr: Option, + pub account_id: Option, +} + +#[derive(Clone, Debug)] +pub struct KnownProducer { + pub account_id: AccountId, + pub addr: Option, + pub peer_id: PeerId, + pub next_hops: Option>, +} + +#[derive(Debug)] +pub struct NetworkInfoResponse { + pub connected_peers: Vec, + pub num_connected_peers: usize, + pub peer_max_count: u32, + pub sent_bytes_per_sec: u64, + pub received_bytes_per_sec: u64, + /// Accounts of known block and chunk producers from routing table. + pub known_producers: Vec, +} + +/// Status of given transaction including all the subsequent receipts. +#[derive(Debug)] +pub struct TxStatus { + pub tx_hash: CryptoHash, + pub signer_account_id: AccountId, + pub fetch_receipt: bool, +} + +#[derive(Debug)] +pub enum TxStatusError { + ChainError(unc_chain_primitives::Error), + MissingTransaction(CryptoHash), + InternalError(String), + TimeoutError, +} + +impl From for TxStatusError { + fn from(error: unc_chain_primitives::Error) -> Self { + Self::ChainError(error) + } +} + +impl Message for TxStatus { + type Result = Result; +} + +#[derive(Debug)] +pub struct GetValidatorInfo { + pub epoch_reference: EpochReference, +} + +impl Message for GetValidatorInfo { + type Result = Result; +} + + +#[derive(thiserror::Error, Debug)] +pub enum GetProviderInfoError { + #[error("IO Error: {0}")] + IOError(String), + #[error("Unknown block")] + UnknownBlock, + #[error("Provider info unavailable")] + ProviderInfoUnavailable, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {0}")] + Unreachable(String), +} + +impl From for crate::types::GetProviderInfoError { + fn from(error: unc_chain_primitives::Error) -> Self { + match error { + unc_chain_primitives::Error::DBNotFoundErr(_) + | unc_chain_primitives::Error::BlockOutOfBounds(_) => Self::UnknownBlock, + unc_chain_primitives::Error::IOErr(s) => Self::IOError(s.to_string()), + _ => Self::Unreachable(error.to_string()), + } + } +} +#[derive(thiserror::Error, Debug)] +pub enum GetValidatorInfoError { + #[error("IO Error: {0}")] + IOError(String), + #[error("Unknown epoch")] + UnknownEpoch, + #[error("Validator info unavailable")] + ValidatorInfoUnavailable, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {0}")] + Unreachable(String), +} + +impl From for GetValidatorInfoError { + fn from(error: unc_chain_primitives::Error) -> Self { + match error { + unc_chain_primitives::Error::DBNotFoundErr(_) + | unc_chain_primitives::Error::EpochOutOfBounds(_) => Self::UnknownEpoch, + unc_chain_primitives::Error::IOErr(s) => Self::IOError(s.to_string()), + _ => Self::Unreachable(error.to_string()), + } + } +} + +#[derive(Debug)] +pub struct GetValidatorOrdered { + pub block_id: MaybeBlockId, +} + +impl Message for GetValidatorOrdered { + type Result = Result, GetValidatorInfoError>; +} + +#[derive(Debug)] +pub struct GetStateChanges { + pub block_hash: CryptoHash, + pub state_changes_request: StateChangesRequestView, +} + +#[derive(thiserror::Error, Debug)] +pub enum GetStateChangesError { + #[error("IO Error: {error_message}")] + IOError { error_message: String }, + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { error_message: String }, + #[error("There are no fully synchronized blocks yet")] + NotSyncedYet, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for GetStateChangesError { + fn from(error: unc_chain_primitives::Error) -> Self { + match error { + unc_chain_primitives::Error::IOErr(error) => { + Self::IOError { error_message: error.to_string() } + } + unc_chain_primitives::Error::DBNotFoundErr(error_message) => { + Self::UnknownBlock { error_message } + } + _ => Self::Unreachable { error_message: error.to_string() }, + } + } +} + +impl Message for GetStateChanges { + type Result = Result; +} + +#[derive(Debug)] +pub struct GetStateChangesInBlock { + pub block_hash: CryptoHash, +} + +impl Message for GetStateChangesInBlock { + type Result = Result; +} + +#[derive(Debug)] +pub struct GetStateChangesWithCauseInBlock { + pub block_hash: CryptoHash, +} + +impl Message for GetStateChangesWithCauseInBlock { + type Result = Result; +} + +#[derive(Debug)] +pub struct GetStateChangesWithCauseInBlockForTrackedShards { + pub block_hash: CryptoHash, + pub epoch_id: EpochId, +} + +impl Message for GetStateChangesWithCauseInBlockForTrackedShards { + type Result = Result, GetStateChangesError>; +} + +#[derive(Debug)] +pub struct GetExecutionOutcome { + pub id: TransactionOrReceiptId, +} + +#[derive(thiserror::Error, Debug)] +pub enum GetExecutionOutcomeError { + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { error_message: String }, + #[error("Inconsistent state. Total number of shards is {number_or_shards} but the execution outcome is in shard {execution_outcome_shard_id}")] + InconsistentState { + number_or_shards: usize, + execution_outcome_shard_id: unc_primitives::types::ShardId, + }, + #[error("{transaction_or_receipt_id} has not been confirmed")] + NotConfirmed { transaction_or_receipt_id: unc_primitives::hash::CryptoHash }, + #[error("{transaction_or_receipt_id} does not exist")] + UnknownTransactionOrReceipt { transaction_or_receipt_id: unc_primitives::hash::CryptoHash }, + #[error("Node doesn't track the shard where {transaction_or_receipt_id} is executed")] + UnavailableShard { + transaction_or_receipt_id: unc_primitives::hash::CryptoHash, + shard_id: unc_primitives::types::ShardId, + }, + #[error("Internal error: {error_message}")] + InternalError { error_message: String }, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for GetExecutionOutcomeError { + fn from(error: TxStatusError) -> Self { + match error { + TxStatusError::ChainError(err) => { + Self::InternalError { error_message: err.to_string() } + } + _ => Self::Unreachable { error_message: format!("{:?}", error) }, + } + } +} + +impl From for GetExecutionOutcomeError { + fn from(error: unc_chain_primitives::error::Error) -> Self { + match error { + unc_chain_primitives::Error::IOErr(error) => { + Self::InternalError { error_message: error.to_string() } + } + unc_chain_primitives::Error::DBNotFoundErr(error_message) => { + Self::UnknownBlock { error_message } + } + _ => Self::Unreachable { error_message: error.to_string() }, + } + } +} + +pub struct GetExecutionOutcomeResponse { + pub outcome_proof: ExecutionOutcomeWithIdView, + pub outcome_root_proof: MerklePath, +} + +impl Message for GetExecutionOutcome { + type Result = Result; +} + +#[derive(Debug)] +pub struct GetExecutionOutcomesForBlock { + pub block_hash: CryptoHash, +} + +impl Message for GetExecutionOutcomesForBlock { + type Result = Result>, String>; +} + +#[derive(Debug)] +pub struct GetBlockProof { + pub block_hash: CryptoHash, + pub head_block_hash: CryptoHash, +} + +pub struct GetBlockProofResponse { + pub block_header_lite: LightClientBlockLiteView, + pub proof: MerklePath, +} + +#[derive(thiserror::Error, Debug)] +pub enum GetBlockProofError { + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { error_message: String }, + #[error("Internal error: {error_message}")] + InternalError { error_message: String }, + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {error_message}")] + Unreachable { error_message: String }, +} + +impl From for GetBlockProofError { + fn from(error: unc_chain_primitives::error::Error) -> Self { + match error { + unc_chain_primitives::error::Error::DBNotFoundErr(error_message) => { + Self::UnknownBlock { error_message } + } + unc_chain_primitives::error::Error::Other(error_message) => { + Self::InternalError { error_message } + } + err => Self::Unreachable { error_message: err.to_string() }, + } + } +} + +impl Message for GetBlockProof { + type Result = Result; +} + +#[derive(Debug)] +pub struct GetReceipt { + pub receipt_id: CryptoHash, +} + +#[derive(thiserror::Error, Debug)] +pub enum GetReceiptError { + #[error("IO Error: {0}")] + IOError(String), + #[error("Receipt with id {0} has never been observed on this node")] + UnknownReceipt(unc_primitives::hash::CryptoHash), + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {0}")] + Unreachable(String), +} + +impl From for GetReceiptError { + fn from(error: unc_chain_primitives::Error) -> Self { + match error { + unc_chain_primitives::Error::IOErr(error) => Self::IOError(error.to_string()), + _ => Self::Unreachable(error.to_string()), + } + } +} + +impl Message for GetReceipt { + type Result = Result, GetReceiptError>; +} + +#[derive(Debug)] +pub struct GetProtocolConfig(pub BlockReference); + +impl Message for GetProtocolConfig { + type Result = Result; +} + +#[derive(thiserror::Error, Debug)] +pub enum GetProtocolConfigError { + #[error("IO Error: {0}")] + IOError(String), + #[error("Block has never been observed: {0}")] + UnknownBlock(String), + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {0}")] + Unreachable(String), +} + +impl From for GetProtocolConfigError { + fn from(error: unc_chain_primitives::Error) -> Self { + match error { + unc_chain_primitives::Error::IOErr(error) => Self::IOError(error.to_string()), + unc_chain_primitives::Error::DBNotFoundErr(s) => Self::UnknownBlock(s), + _ => Self::Unreachable(error.to_string()), + } + } +} + +#[derive(Debug)] +pub struct GetMaintenanceWindows { + pub account_id: AccountId, +} + +impl Message for GetMaintenanceWindows { + type Result = Result; +} + +#[derive(thiserror::Error, Debug)] +pub enum GetMaintenanceWindowsError { + #[error("IO Error: {0}")] + IOError(String), + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {0}")] + Unreachable(String), +} + +impl From for GetMaintenanceWindowsError { + fn from(error: unc_chain_primitives::Error) -> Self { + match error { + unc_chain_primitives::Error::IOErr(error) => Self::IOError(error.to_string()), + _ => Self::Unreachable(error.to_string()), + } + } +} + +#[derive(Debug)] +pub struct GetClientConfig {} + +impl Message for GetClientConfig { + type Result = Result; +} + +#[derive(thiserror::Error, Debug)] +pub enum GetClientConfigError { + #[error("IO Error: {0}")] + IOError(String), + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {0}")] + Unreachable(String), +} + +impl From for GetClientConfigError { + fn from(error: unc_chain_primitives::Error) -> Self { + match error { + unc_chain_primitives::Error::IOErr(error) => Self::IOError(error.to_string()), + _ => Self::Unreachable(error.to_string()), + } + } +} + +#[derive(Debug)] +pub struct GetSplitStorageInfo {} + +impl Message for GetSplitStorageInfo { + type Result = Result; +} + +#[derive(thiserror::Error, Debug)] +pub enum GetSplitStorageInfoError { + #[error("IO Error: {0}")] + IOError(String), + // NOTE: Currently, the underlying errors are too broad, and while we tried to handle + // expected cases, we cannot statically guarantee that no other errors will be returned + // in the future. + // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors + #[error("It is a bug if you receive this error type, please, report this incident: https://github.com/utnet-org/utility/issues/new/choose. Details: {0}")] + Unreachable(String), +} + +impl From for GetSplitStorageInfoError { + fn from(error: unc_chain_primitives::Error) -> Self { + match error { + unc_chain_primitives::Error::IOErr(error) => Self::IOError(error.to_string()), + _ => Self::Unreachable(error.to_string()), + } + } +} + +impl From for GetSplitStorageInfoError { + fn from(error: std::io::Error) -> Self { + Self::IOError(error.to_string()) + } +} + +#[cfg(feature = "sandbox")] +#[derive(Debug)] +pub enum SandboxMessage { + SandboxPatchState(Vec), + SandboxPatchStateStatus, + SandboxFastForward(unc_primitives::types::BlockHeightDelta), + SandboxFastForwardStatus, +} + +#[cfg(feature = "sandbox")] +#[derive(Eq, PartialEq, Debug, actix::MessageResponse)] +pub enum SandboxResponse { + SandboxPatchStateFinished(bool), + SandboxFastForwardFinished(bool), + SandboxFastForwardFailed(String), + SandboxNoResponse, +} +#[cfg(feature = "sandbox")] +impl Message for SandboxMessage { + type Result = SandboxResponse; +} diff --git a/chain/client/Cargo.toml b/chain/client/Cargo.toml new file mode 100644 index 000000000..0055eae16 --- /dev/null +++ b/chain/client/Cargo.toml @@ -0,0 +1,116 @@ +[package] +name = "unc-client" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix-rt.workspace = true +actix.workspace = true +anyhow.workspace = true +async-trait.workspace = true +borsh.workspace = true +chrono.workspace = true +cloud-storage.workspace = true +derive_more.workspace = true +futures.workspace = true +itertools.workspace = true +lru.workspace = true +num-rational.workspace = true +once_cell.workspace = true +percent-encoding.workspace = true +rand.workspace = true +rayon.workspace = true +reed-solomon-erasure.workspace = true +regex.workspace = true +reqwest.workspace = true +rust-s3.workspace = true +serde.workspace = true +serde_json.workspace = true +strum.workspace = true +sysinfo.workspace = true +tempfile.workspace = true +thiserror.workspace = true +tokio.workspace = true +tracing.workspace = true +yansi.workspace = true + +unc-async.workspace = true +unc-chain-configs.workspace = true +unc-chain-primitives.workspace = true +unc-chain.workspace = true +unc-chunks.workspace = true +unc-client-primitives.workspace = true +unc-crypto.workspace = true +unc-dyn-configs.workspace = true +unc-epoch-manager.workspace = true +unc-network.workspace = true +unc-o11y.workspace = true +unc-performance-metrics-macros.workspace = true +unc-performance-metrics.workspace = true +unc-pool.workspace = true +unc-parameters.workspace = true +unc-primitives.workspace = true +unc-store.workspace = true +unc-telemetry.workspace = true + +[dev-dependencies] +assert_matches.workspace = true +unc-actix-test-utils.workspace = true + +[features] +# if enabled, we assert in most situations that are impossible unless some byzantine behavior is observed. +byzantine_asserts = ["unc-chain/byzantine_asserts"] +expensive_tests = [] +test_features = [ + "unc-network/test_features", + "unc-chain/test_features", + "unc-chunks/test_features", +] +nightly_protocol = [ + "unc-async/nightly_protocol", + "unc-chain-configs/nightly_protocol", + "unc-chain/nightly_protocol", + "unc-chunks/nightly_protocol", + "unc-client-primitives/nightly_protocol", + "unc-dyn-configs/nightly_protocol", + "unc-epoch-manager/nightly_protocol", + "unc-network/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-parameters/nightly_protocol", + "unc-pool/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", + "unc-telemetry/nightly_protocol", +] +nightly = [ + "nightly_protocol", + "unc-async/nightly", + "unc-chain-configs/nightly", + "unc-chain/nightly", + "unc-chunks/nightly", + "unc-client-primitives/nightly", + "unc-dyn-configs/nightly", + "unc-epoch-manager/nightly", + "unc-network/nightly", + "unc-o11y/nightly", + "unc-parameters/nightly", + "unc-pool/nightly", + "unc-primitives/nightly", + "unc-store/nightly", + "unc-telemetry/nightly", +] +sandbox = [ + "unc-client-primitives/sandbox", + "unc-chain/sandbox", +] +new_epoch_sync = [ + "unc-chain/new_epoch_sync" +] diff --git a/chain/client/src/adapter.rs b/chain/client/src/adapter.rs new file mode 100644 index 000000000..049e8d255 --- /dev/null +++ b/chain/client/src/adapter.rs @@ -0,0 +1,372 @@ +use crate::client_actor::ClientActor; +use crate::view_client::ViewClientActor; +use unc_network::types::{ + NetworkInfo, PartialEncodedChunkForwardMsg, PartialEncodedChunkRequestMsg, + PartialEncodedChunkResponseMsg, ReasonForBan, StateResponseInfo, +}; +use unc_o11y::WithSpanContextExt; +use unc_primitives::block::{Approval, Block, BlockHeader}; +use unc_primitives::challenge::Challenge; +use unc_primitives::chunk_validation::{ChunkEndorsement, ChunkStateWitness}; +use unc_primitives::errors::InvalidTxError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::sharding::PartialEncodedChunk; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{AccountId, BlockHeight, EpochId, ShardId}; +use unc_primitives::views::FinalExecutionOutcomeView; + +/// Transaction status query +#[derive(actix::Message, Debug)] +#[rtype(result = "Option>")] +pub(crate) struct TxStatusRequest { + pub tx_hash: CryptoHash, + pub signer_account_id: AccountId, +} + +/// Transaction status response +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub(crate) struct TxStatusResponse(pub Box); + +/// Request a provider. +#[derive(actix::Message, Debug)] +#[rtype(result = "Option")] +pub(crate) struct ProviderRequest(pub EpochId, pub BlockHeight); + +/// Provider response. +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub struct ProviderResponse { + pub provider: AccountId, +} + +/// Request a block. +#[derive(actix::Message, Debug)] +#[rtype(result = "Option>")] +pub(crate) struct BlockRequest(pub CryptoHash); + +/// Block response. +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub struct BlockResponse { + pub block: Block, + pub peer_id: PeerId, + pub was_requested: bool, +} + +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub struct BlockApproval(pub Approval, pub PeerId); + +/// Request headers. +#[derive(actix::Message, Debug)] +#[rtype(result = "Option>")] +pub(crate) struct BlockHeadersRequest(pub Vec); + +/// Headers response. +#[derive(actix::Message, Debug)] +#[rtype(result = "Result<(),ReasonForBan>")] +pub(crate) struct BlockHeadersResponse(pub Vec, pub PeerId); + +/// State request header. +#[derive(actix::Message, Debug)] +#[rtype(result = "Option")] +pub struct StateRequestHeader { + pub shard_id: ShardId, + pub sync_hash: CryptoHash, +} + +/// State request part. +#[derive(actix::Message, Debug)] +#[rtype(result = "Option")] +pub struct StateRequestPart { + pub shard_id: ShardId, + pub sync_hash: CryptoHash, + pub part_id: u64, +} + +/// Response to state request. +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub struct StateResponse(pub Box); + +/// Account announcements that needs to be validated before being processed. +/// They are paired with last epoch id known to this announcement, in order to accept only +/// newer announcements. +#[derive(actix::Message, Debug)] +#[rtype(result = "Result,ReasonForBan>")] +pub(crate) struct AnnounceAccountRequest(pub Vec<(AnnounceAccount, Option)>); + +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub struct SetNetworkInfo(pub NetworkInfo); + +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub(crate) struct RecvChallenge(pub Challenge); + +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub(crate) struct RecvPartialEncodedChunkForward(pub PartialEncodedChunkForwardMsg); + +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub(crate) struct RecvPartialEncodedChunk(pub PartialEncodedChunk); + +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub(crate) struct RecvPartialEncodedChunkResponse( + pub PartialEncodedChunkResponseMsg, + pub std::time::Instant, +); + +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub(crate) struct RecvPartialEncodedChunkRequest(pub PartialEncodedChunkRequestMsg, pub CryptoHash); + +#[derive(actix::Message, Debug)] +#[rtype(result = "ProcessTxResponse")] +pub struct ProcessTxRequest { + pub transaction: SignedTransaction, + pub is_forwarded: bool, + pub check_only: bool, +} + +#[derive(actix::MessageResponse, Debug, PartialEq, Eq)] +pub enum ProcessTxResponse { + /// No response. + NoResponse, + /// Valid transaction inserted into mempool as response to Transaction. + ValidTx, + /// Invalid transaction inserted into mempool as response to Transaction. + InvalidTx(InvalidTxError), + /// The request is routed to other shards + RequestRouted, + /// The node being queried does not track the shard needed and therefore cannot provide userful + /// response. + DoesNotTrackShard, +} + +#[derive(actix::Message, Debug, PartialEq, Eq)] +#[rtype(result = "()")] +pub struct ChunkStateWitnessMessage(pub ChunkStateWitness); + +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub struct ChunkEndorsementMessage(pub ChunkEndorsement); + +pub struct Adapter { + /// Address of the client actor. + client_addr: actix::Addr, + /// Address of the view client actor. + view_client_addr: actix::Addr, +} + +impl Adapter { + pub fn new( + client_addr: actix::Addr, + view_client_addr: actix::Addr, + ) -> Self { + Self { client_addr, view_client_addr } + } +} + +#[async_trait::async_trait] +impl unc_network::client::Client for Adapter { + async fn tx_status_request( + &self, + account_id: AccountId, + tx_hash: CryptoHash, + ) -> Option> { + match self + .view_client_addr + .send(TxStatusRequest { tx_hash, signer_account_id: account_id }.with_span_context()) + .await + { + Ok(res) => res, + Err(err) => { + tracing::error!("mailbox error: {err}"); + None + } + } + } + + async fn tx_status_response(&self, tx_result: FinalExecutionOutcomeView) { + match self + .view_client_addr + .send(TxStatusResponse(Box::new(tx_result.clone())).with_span_context()) + .await + { + Ok(()) => {} + Err(err) => { + tracing::error!("mailbox error: {err}"); + } + } + } + + async fn state_request_header( + &self, + shard_id: ShardId, + sync_hash: CryptoHash, + ) -> Result, ReasonForBan> { + match self + .view_client_addr + .send(StateRequestHeader { shard_id, sync_hash }.with_span_context()) + .await + { + Ok(Some(StateResponse(resp))) => Ok(Some(*resp)), + Ok(None) => Ok(None), + Err(err) => { + tracing::error!("mailbox error: {err}"); + Ok(None) + } + } + } + + async fn state_request_part( + &self, + shard_id: ShardId, + sync_hash: CryptoHash, + part_id: u64, + ) -> Result, ReasonForBan> { + match self + .view_client_addr + .send(StateRequestPart { shard_id, sync_hash, part_id }.with_span_context()) + .await + { + Ok(Some(StateResponse(resp))) => Ok(Some(*resp)), + Ok(None) => Ok(None), + Err(err) => { + tracing::error!("mailbox error: {err}"); + Ok(None) + } + } + } + + async fn state_response(&self, info: StateResponseInfo) { + match self.client_addr.send(StateResponse(Box::new(info)).with_span_context()).await { + Ok(()) => {} + Err(err) => tracing::error!("mailbox error: {err}"), + } + } + + async fn block_approval(&self, approval: Approval, peer_id: PeerId) { + match self.client_addr.send(BlockApproval(approval, peer_id).with_span_context()).await { + Ok(()) => {} + Err(err) => tracing::error!("mailbox error: {err}"), + } + } + + async fn transaction(&self, transaction: SignedTransaction, is_forwarded: bool) { + match self + .client_addr + .send( + ProcessTxRequest { transaction, is_forwarded, check_only: false } + .with_span_context(), + ) + .await + { + Ok(ProcessTxResponse::InvalidTx(err)) => { + tracing::warn!(target: "network", ?err, "Received invalid tx"); + // TODO: count as malicious behavior? + } + Ok(_) => {} + Err(err) => { + tracing::error!("mailbox error: {err}"); + } + } + } + + async fn block_request(&self, hash: CryptoHash) -> Option> { + match self.view_client_addr.send(BlockRequest(hash).with_span_context()).await { + Ok(res) => res, + Err(err) => { + tracing::error!("mailbox error: {err}"); + None + } + } + } + + async fn block_headers_request(&self, hashes: Vec) -> Option> { + match self.view_client_addr.send(BlockHeadersRequest(hashes).with_span_context()).await { + Ok(headers) => headers, + Err(err) => { + tracing::error!("mailbox error: {err}"); + None + } + } + } + + async fn block(&self, block: Block, peer_id: PeerId, was_requested: bool) { + match self + .client_addr + .send(BlockResponse { block, peer_id, was_requested }.with_span_context()) + .await + { + Ok(()) => {} + Err(err) => tracing::error!("mailbox error: {err}"), + } + } + + async fn block_headers( + &self, + headers: Vec, + peer_id: PeerId, + ) -> Result<(), ReasonForBan> { + match self + .client_addr + .send(BlockHeadersResponse(headers, peer_id).with_span_context()) + .await + { + Ok(res) => res, + Err(err) => { + tracing::error!("mailbox error: {err}"); + Ok(()) + } + } + } + + async fn challenge(&self, challenge: Challenge) { + match self.client_addr.send(RecvChallenge(challenge).with_span_context()).await { + Ok(()) => {} + Err(err) => tracing::error!("mailbox error: {err}"), + } + } + + async fn network_info(&self, info: NetworkInfo) { + match self.client_addr.send(SetNetworkInfo(info).with_span_context()).await { + Ok(()) => {} + Err(err) => tracing::error!("mailbox error: {err}"), + } + } + + async fn announce_account( + &self, + accounts: Vec<(AnnounceAccount, Option)>, + ) -> Result, ReasonForBan> { + match self.view_client_addr.send(AnnounceAccountRequest(accounts).with_span_context()).await + { + Ok(res) => res, + Err(err) => { + tracing::error!("mailbox error: {err}"); + Ok(vec![]) + } + } + } + + async fn chunk_state_witness(&self, witness: ChunkStateWitness) { + match self.client_addr.send(ChunkStateWitnessMessage(witness).with_span_context()).await { + Ok(()) => {} + Err(err) => tracing::error!("mailbox error: {err}"), + } + } + + async fn chunk_endorsement(&self, endorsement: ChunkEndorsement) { + match self.client_addr.send(ChunkEndorsementMessage(endorsement).with_span_context()).await + { + Ok(()) => {} + Err(err) => tracing::error!("mailbox error: {err}"), + } + } +} diff --git a/chain/client/src/adversarial.rs b/chain/client/src/adversarial.rs new file mode 100644 index 000000000..d2a8d3475 --- /dev/null +++ b/chain/client/src/adversarial.rs @@ -0,0 +1,62 @@ +#[cfg(feature = "test_features")] +mod adv { + use std::sync::atomic::Ordering; + + #[derive(Default)] + struct Inner { + disable_header_sync: std::sync::atomic::AtomicBool, + disable_doomslug: std::sync::atomic::AtomicBool, + is_archival: bool, + } + + #[derive(Default, Clone)] + pub struct Controls(std::sync::Arc); + + impl Controls { + pub fn new(is_archival: bool) -> Self { + Self(std::sync::Arc::new(Inner { is_archival, ..Inner::default() })) + } + + pub fn disable_header_sync(&self) -> bool { + self.0.disable_header_sync.load(Ordering::SeqCst) + } + + pub fn set_disable_header_sync(&mut self, value: bool) { + self.0.disable_header_sync.store(value, Ordering::SeqCst); + } + + pub fn disable_doomslug(&self) -> bool { + self.0.disable_doomslug.load(Ordering::SeqCst) + } + + pub fn set_disable_doomslug(&self, value: bool) { + self.0.disable_doomslug.store(value, Ordering::SeqCst); + } + + pub fn is_archival(&self) -> bool { + self.0.is_archival + } + } +} + +#[cfg(not(feature = "test_features"))] +mod adv { + #[derive(Default, Clone)] + pub struct Controls; + + impl Controls { + pub const fn new(_is_archival: bool) -> Self { + Self + } + + pub const fn disable_header_sync(&self) -> bool { + false + } + + pub const fn disable_doomslug(&self) -> bool { + false + } + } +} + +pub use adv::Controls; diff --git a/chain/client/src/chunk_validation.rs b/chain/client/src/chunk_validation.rs new file mode 100644 index 000000000..ac0cbc520 --- /dev/null +++ b/chain/client/src/chunk_validation.rs @@ -0,0 +1,485 @@ +use unc_async::messaging::{CanSend, Sender}; +use unc_chain::migrations::check_if_block_is_first_with_chunk_of_version; +use unc_chain::sharding::shuffle_receipt_proofs; +use unc_chain::types::{ + ApplyChunkBlockContext, ApplyChunkResult, ApplyChunkShardContext, RuntimeAdapter, + RuntimeStorageConfig, StorageDataSource, +}; +use unc_chain::validate::validate_chunk_with_chunk_extra_and_receipts_root; +use unc_chain::{Block, BlockHeader, Chain, ChainStore, ChainStoreAccess}; +use unc_chain_primitives::Error; +use unc_epoch_manager::EpochManagerAdapter; +use unc_network::types::{NetworkRequests, PeerManagerMessageRequest}; +use unc_primitives::challenge::PartialState; +use unc_primitives::checked_feature; +use unc_primitives::chunk_validation::{ + ChunkEndorsement, ChunkEndorsementInner, ChunkStateTransition, ChunkStateWitness, +}; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::merkle::merklize; +use unc_primitives::receipt::Receipt; +use unc_primitives::sharding::{ShardChunk, ShardChunkHeader}; +use unc_primitives::types::chunk_extra::ChunkExtra; +use unc_primitives::types::{EpochId, ShardId}; +use unc_primitives::validator_signer::ValidatorSigner; +use unc_store::PartialStorage; +use std::collections::HashMap; +use std::sync::Arc; + +use crate::Client; + +/// A module that handles chunk validation logic. Chunk validation refers to a +/// critical process of stateless validation, where chunk validators (certain +/// validators selected to validate the chunk) verify that the chunk's state +/// witness is correct, and then send chunk endorsements to the block producer +/// so that the chunk can be included in the block. +pub struct ChunkValidator { + /// The signer for our own node, if we are a validator. If not, this is None. + my_signer: Option>, + epoch_manager: Arc, + network_sender: Sender, + runtime_adapter: Arc, +} + +impl ChunkValidator { + pub fn new( + my_signer: Option>, + epoch_manager: Arc, + network_sender: Sender, + runtime_adapter: Arc, + ) -> Self { + Self { my_signer, epoch_manager, network_sender, runtime_adapter } + } + + /// Performs the chunk validation logic. When done, it will send the chunk + /// endorsement message to the block producer. The actual validation logic + /// happens in a separate thread. + pub fn start_validating_chunk( + &self, + state_witness: ChunkStateWitness, + chain_store: &ChainStore, + ) -> Result<(), Error> { + let Some(my_signer) = self.my_signer.as_ref() else { + return Err(Error::NotAValidator); + }; + let chunk_header = state_witness.chunk_header.clone(); + let epoch_id = + self.epoch_manager.get_epoch_id_from_prev_block(chunk_header.prev_block_hash())?; + // We will only validate something if we are a chunk validator for this chunk. + // Note this also covers the case before the protocol upgrade for chunk validators, + // because the chunk validators will be empty. + let chunk_validators = self.epoch_manager.get_chunk_validators( + &epoch_id, + chunk_header.shard_id(), + chunk_header.height_created(), + )?; + if !chunk_validators.contains_key(my_signer.validator_id()) { + return Err(Error::NotAChunkValidator); + } + + let pre_validation_result = pre_validate_chunk_state_witness( + &state_witness, + chain_store, + self.epoch_manager.as_ref(), + )?; + + let block_producer = + self.epoch_manager.get_block_producer(&epoch_id, chunk_header.height_created())?; + + let network_sender = self.network_sender.clone(); + let signer = self.my_signer.clone().unwrap(); + let epoch_manager = self.epoch_manager.clone(); + let runtime_adapter = self.runtime_adapter.clone(); + rayon::spawn(move || { + match validate_chunk_state_witness( + state_witness, + pre_validation_result, + epoch_manager.as_ref(), + runtime_adapter.as_ref(), + ) { + Ok(()) => { + tracing::debug!( + target: "chunk_validation", + chunk_hash=?chunk_header.chunk_hash(), + block_producer=%block_producer, + "Chunk validated successfully, sending endorsement", + ); + let endorsement_to_sign = ChunkEndorsementInner::new(chunk_header.chunk_hash()); + let endorsement = ChunkEndorsement { + account_id: signer.validator_id().clone(), + signature: signer.sign_chunk_endorsement(&endorsement_to_sign), + inner: endorsement_to_sign, + }; + network_sender.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::ChunkEndorsement(block_producer, endorsement), + )); + } + Err(err) => { + tracing::error!("Failed to validate chunk: {:?}", err); + } + } + }); + Ok(()) + } +} + +/// Pre-validates the chunk's receipts and transactions against the chain. +/// We do this before handing off the computationally intensive part to a +/// validation thread. +fn pre_validate_chunk_state_witness( + state_witness: &ChunkStateWitness, + store: &ChainStore, + epoch_manager: &dyn EpochManagerAdapter, +) -> Result { + let shard_id = state_witness.chunk_header.shard_id(); + + // First, go back through the blockchain history to locate the last new chunk + // and last last new chunk for the shard. + + // Blocks from the last new chunk (exclusive) to the parent block (inclusive). + let mut blocks_after_last_chunk = Vec::new(); + // Blocks from the last last new chunk (exclusive) to the last new chunk (inclusive). + let mut blocks_after_last_last_chunk = Vec::new(); + + { + let mut block_hash = *state_witness.chunk_header.prev_block_hash(); + let mut prev_chunks_seen = 0; + loop { + let block = store.get_block(&block_hash)?; + let chunks = block.chunks(); + let Some(chunk) = chunks.get(shard_id as usize) else { + return Err(Error::InvalidChunkStateWitness(format!( + "Shard {} does not exist in block {:?}", + shard_id, block_hash + ))); + }; + block_hash = *block.header().prev_hash(); + if chunk.is_new_chunk() { + prev_chunks_seen += 1; + } + if prev_chunks_seen == 0 { + blocks_after_last_chunk.push(block); + } else if prev_chunks_seen == 1 { + blocks_after_last_last_chunk.push(block); + } + if prev_chunks_seen == 2 { + break; + } + } + } + + // Compute the chunks from which receipts should be collected. + let mut chunks_to_collect_receipts_from = Vec::new(); + for block in blocks_after_last_last_chunk.iter().rev() { + // To stay consistent with the order in which receipts are applied, + // blocks are iterated in reverse order (from new to old), and + // chunks are shuffled for each block. + let mut chunks_in_block = block + .chunks() + .iter() + .map(|chunk| (chunk.chunk_hash(), chunk.prev_outgoing_receipts_root())) + .collect::>(); + shuffle_receipt_proofs(&mut chunks_in_block, block.hash()); + chunks_to_collect_receipts_from.extend(chunks_in_block); + } + + // Verify that for each chunk, the receipts that have been provided match + // the receipts that we are expecting. + let mut receipts_to_apply = Vec::new(); + for (chunk_hash, receipt_root) in chunks_to_collect_receipts_from { + let Some(receipt_proof) = state_witness.source_receipt_proofs.get(&chunk_hash) else { + return Err(Error::InvalidChunkStateWitness(format!( + "Missing source receipt proof for chunk {:?}", + chunk_hash + ))); + }; + if !receipt_proof.verify_against_receipt_root(receipt_root) { + return Err(Error::InvalidChunkStateWitness(format!( + "Provided receipt proof failed verification against receipt root for chunk {:?}", + chunk_hash + ))); + } + // TODO(#10265): This does not currently handle shard layout change. + if receipt_proof.1.to_shard_id != shard_id { + return Err(Error::InvalidChunkStateWitness(format!( + "Receipt proof for chunk {:?} is for shard {}, expected shard {}", + chunk_hash, receipt_proof.1.to_shard_id, shard_id + ))); + } + receipts_to_apply.extend(receipt_proof.0.iter().cloned()); + } + let exact_receipts_hash = hash(&borsh::to_vec(&receipts_to_apply).unwrap()); + if exact_receipts_hash != state_witness.exact_receipts_hash { + return Err(Error::InvalidChunkStateWitness(format!( + "Receipts hash {:?} does not match expected receipts hash {:?}", + exact_receipts_hash, state_witness.exact_receipts_hash + ))); + } + let tx_root_from_state_witness = hash(&borsh::to_vec(&state_witness.transactions).unwrap()); + let block_of_last_new_chunk = blocks_after_last_chunk.last().unwrap(); + let last_new_chunk_tx_root = + block_of_last_new_chunk.chunks().get(shard_id as usize).unwrap().tx_root(); + if last_new_chunk_tx_root != tx_root_from_state_witness { + return Err(Error::InvalidChunkStateWitness(format!( + "Transaction root {:?} does not match expected transaction root {:?}", + tx_root_from_state_witness, last_new_chunk_tx_root + ))); + } + + Ok(PreValidationOutput { + receipts_to_apply, + main_transition_params: get_state_transition_validation_params( + store, + epoch_manager, + block_of_last_new_chunk, + shard_id, + )?, + implicit_transition_params: blocks_after_last_chunk + .into_iter() + .map(|block| { + get_state_transition_validation_params(store, epoch_manager, &block, shard_id) + }) + .collect::>()?, + }) +} + +fn get_state_transition_validation_params( + store: &ChainStore, + epoch_manager: &dyn EpochManagerAdapter, + block: &Block, + shard_id: ShardId, +) -> Result { + let chunks = block.chunks(); + let chunk = chunks.get(shard_id as usize).ok_or_else(|| { + Error::InvalidChunkStateWitness(format!( + "Shard {} does not exist in block {:?}", + shard_id, + block.hash() + )) + })?; + let is_first_block_with_chunk_of_version = check_if_block_is_first_with_chunk_of_version( + store, + epoch_manager, + block.header().prev_hash(), + shard_id, + )?; + let prev_block_header = store.get_block_header(&block.header().prev_hash())?; + Ok(ChunkStateTransitionValidationParams { + chunk: chunk.clone(), + block: block.header().clone(), + gas_price: prev_block_header.next_gas_price(), + is_first_block_with_chunk_of_version, + }) +} + +struct ChunkStateTransitionValidationParams { + chunk: ShardChunkHeader, + block: BlockHeader, + gas_price: u128, + is_first_block_with_chunk_of_version: bool, +} + +struct PreValidationOutput { + receipts_to_apply: Vec, + main_transition_params: ChunkStateTransitionValidationParams, + implicit_transition_params: Vec, +} + +fn validate_chunk_state_witness( + state_witness: ChunkStateWitness, + pre_validation_output: PreValidationOutput, + epoch_manager: &dyn EpochManagerAdapter, + runtime_adapter: &dyn RuntimeAdapter, +) -> Result<(), Error> { + let main_transition = pre_validation_output.main_transition_params; + let runtime_storage_config = RuntimeStorageConfig { + record_storage: false, + source: StorageDataSource::Recorded(PartialStorage { + nodes: state_witness.main_state_transition.base_state, + }), + state_patch: Default::default(), + state_root: main_transition.chunk.prev_state_root(), + use_flat_storage: true, + }; + let mut main_apply_result = runtime_adapter.apply_chunk( + runtime_storage_config, + ApplyChunkShardContext { + gas_limit: main_transition.chunk.gas_limit(), + is_first_block_with_chunk_of_version: main_transition + .is_first_block_with_chunk_of_version, + is_new_chunk: true, + last_validator_power_proposals: main_transition.chunk.prev_validator_power_proposals(), + last_validator_frozen_proposals: main_transition.chunk.prev_validator_frozen_proposals(), + shard_id: main_transition.chunk.shard_id(), + }, + ApplyChunkBlockContext::from_header(&main_transition.block, main_transition.gas_price), + &pre_validation_output.receipts_to_apply, + &state_witness.transactions, + )?; + let outgoing_receipts = std::mem::take(&mut main_apply_result.outgoing_receipts); + let mut chunk_extra = apply_result_to_chunk_extra(main_apply_result, &main_transition.chunk); + if chunk_extra.state_root() != &state_witness.main_state_transition.post_state_root { + // This is an early check, it's not for correctness, only for better + // error reporting in case of an invalid state witness due to a bug. + // Only the final state root check against the chunk header is required. + return Err(Error::InvalidChunkStateWitness(format!( + "Post state root {:?} for main transition does not match expected post state root {:?}", + chunk_extra.state_root(), + state_witness.main_state_transition.post_state_root, + ))); + } + + for (transition_params, transition) in pre_validation_output + .implicit_transition_params + .into_iter() + .zip(state_witness.implicit_transitions.into_iter()) + { + let runtime_storage_config = RuntimeStorageConfig { + record_storage: false, + source: StorageDataSource::Recorded(PartialStorage { nodes: transition.base_state }), + state_patch: Default::default(), + state_root: *chunk_extra.state_root(), + use_flat_storage: true, + }; + let apply_result = runtime_adapter.apply_chunk( + runtime_storage_config, + ApplyChunkShardContext { + gas_limit: transition_params.chunk.gas_limit(), + is_first_block_with_chunk_of_version: transition_params + .is_first_block_with_chunk_of_version, + is_new_chunk: false, + last_validator_power_proposals: transition_params.chunk.prev_validator_power_proposals(), + last_validator_frozen_proposals: transition_params.chunk.prev_validator_frozen_proposals(), + shard_id: transition_params.chunk.shard_id(), + }, + ApplyChunkBlockContext::from_header( + &transition_params.block, + transition_params.gas_price, + ), + &[], + &[], + )?; + *chunk_extra.state_root_mut() = apply_result.new_root; + if chunk_extra.state_root() != &transition.post_state_root { + // This is an early check, it's not for correctness, only for better + // error reporting in case of an invalid state witness due to a bug. + // Only the final state root check against the chunk header is required. + return Err(Error::InvalidChunkStateWitness(format!( + "Post state root {:?} for implicit transition at chunk {:?}, does not match expected state root {:?}", + chunk_extra.state_root(), transition_params.chunk.chunk_hash(), transition.post_state_root + ))); + } + } + + // Finally, verify that the newly proposed chunk matches everything we have computed. + let outgoing_receipts_hashes = { + let shard_layout = epoch_manager + .get_shard_layout_from_prev_block(state_witness.chunk_header.prev_block_hash())?; + Chain::build_receipts_hashes(&outgoing_receipts, &shard_layout) + }; + let (outgoing_receipts_root, _) = merklize(&outgoing_receipts_hashes); + validate_chunk_with_chunk_extra_and_receipts_root( + &chunk_extra, + &state_witness.chunk_header, + &outgoing_receipts_root, + )?; + + // Before we're done we have one last thing to do: verify that the proposed transactions + // are valid. + // TODO(#9292): Not sure how to do this. + + Ok(()) +} + +fn apply_result_to_chunk_extra( + apply_result: ApplyChunkResult, + chunk: &ShardChunkHeader, +) -> ChunkExtra { + let (outcome_root, _) = ApplyChunkResult::compute_outcomes_proof(&apply_result.outcomes); + ChunkExtra::new( + &apply_result.new_root, + outcome_root, + apply_result.validator_power_proposals, + apply_result.validator_frozen_proposals, + apply_result.total_gas_burnt, + chunk.gas_limit(), + apply_result.total_balance_burnt, + ) +} + +impl Client { + /// Responds to a network request to verify a `ChunkStateWitness`, which is + /// sent by chunk producers after they produce a chunk. + pub fn process_chunk_state_witness(&mut self, witness: ChunkStateWitness) -> Result<(), Error> { + // TODO(#10265): If the previous block does not exist, we should + // queue this (similar to orphans) to retry later. + self.chunk_validator.start_validating_chunk(witness, self.chain.chain_store()) + } + + /// Distributes the chunk state witness to chunk validators that are + /// selected to validate this chunk. + pub fn send_chunk_state_witness_to_chunk_validators( + &mut self, + epoch_id: &EpochId, + chunk_header: &ShardChunkHeader, + chunk: &ShardChunk, + ) -> Result<(), Error> { + let protocol_version = self.epoch_manager.get_epoch_protocol_version(epoch_id)?; + if !checked_feature!("stable", ChunkValidation, protocol_version) { + return Ok(()); + } + let chunk_validators = self.epoch_manager.get_chunk_validators( + epoch_id, + chunk_header.shard_id(), + chunk_header.height_created(), + )?; + let witness = ChunkStateWitness { + chunk_header: chunk_header.clone(), + main_state_transition: ChunkStateTransition { + // TODO(#9292): Fetch from StoredChunkStateTransitionData. + base_state: PartialState::default(), + // TODO(#9292): Block hash of the last new chunk. + block_hash: CryptoHash::default(), + // TODO(#9292): Fetch from ChunkExtra of the last new chunk. + post_state_root: CryptoHash::default(), + }, + // TODO(#9292): Iterate through the chain to derive this. + source_receipt_proofs: HashMap::new(), + // TODO(#9292): Include transactions from the last new chunk. + transactions: Vec::new(), + // TODO(#9292): Fetch from StoredChunkStateTransitionData. + // (Could also be derived from iterating through the receipts, but + // that defeats the purpose of this check being a debugging + // mechanism.) + exact_receipts_hash: CryptoHash::default(), + // TODO(#9292): Fetch from StoredChunkStateTransitionData for each missing + // chunk. + implicit_transitions: Vec::new(), + new_transactions: chunk.transactions().to_vec(), + // TODO(#9292): Derive this during chunk production, during + // prepare_transactions or the like. + new_transactions_validation_state: PartialState::default(), + }; + tracing::debug!( + target: "chunk_validation", + "Sending chunk state witness for chunk {:?} to chunk validators {:?}", + chunk_header.chunk_hash(), + chunk_validators.keys(), + ); + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::ChunkStateWitness(chunk_validators.into_keys().collect(), witness), + )); + Ok(()) + } + + /// Function to process an incoming chunk endorsement from chunk validators. + pub fn process_chunk_endorsement( + &mut self, + _endorsement: ChunkEndorsement, + ) -> Result<(), Error> { + // TODO(10265): Here if we are the current block producer, we would store the chunk endorsement + // for each chunk which would later be used during block production to check whether to include the + // chunk or not. + Ok(()) + } +} diff --git a/chain/client/src/client.rs b/chain/client/src/client.rs new file mode 100644 index 000000000..22ba4b2cb --- /dev/null +++ b/chain/client/src/client.rs @@ -0,0 +1,2676 @@ +//! Client is responsible for tracking the chain, chunks, and producing them when needed. +//! This client works completely synchronously and must be operated by some async actor outside. + +use crate::adapter::ProcessTxResponse; +use crate::chunk_validation::ChunkValidator; +use crate::debug::BlockProductionTracker; +use crate::debug::PRODUCTION_TIMES_CACHE_SIZE; +use crate::sync::adapter::SyncShardInfo; +use crate::sync::block::BlockSync; +use crate::sync::epoch::EpochSync; +use crate::sync::header::HeaderSync; +use crate::sync::state::{StateSync, StateSyncResult}; +use crate::SyncAdapter; +use crate::SyncMessage; +use crate::{metrics, SyncStatus}; +use actix_rt::ArbiterHandle; +use chrono::DateTime; +use chrono::Utc; +use itertools::Itertools; +use lru::LruCache; +use unc_async::messaging::IntoSender; +use unc_async::messaging::{CanSend, Sender}; +use unc_chain::chain::VerifyBlockHashAndSignatureResult; +use unc_chain::chain::{ + ApplyStatePartsRequest, BlockCatchUpRequest, BlockMissingChunks, BlocksCatchUpState, +}; +use unc_chain::flat_storage_creator::FlatStorageCreator; +use unc_chain::orphan::OrphanMissingChunks; +use unc_chain::resharding::ReshardingRequest; +use unc_chain::state_snapshot_actor::SnapshotCallbacks; +use unc_chain::test_utils::format_hash; +use unc_chain::types::RuntimeAdapter; +use unc_chain::types::{ChainConfig, LatestKnown}; +use unc_chain::{ + BlockProcessingArtifact, BlockStatus, Chain, ChainGenesis, ChainStoreAccess, + DoneApplyChunkCallback, Doomslug, DoomslugThresholdMode, Provenance, +}; +use unc_chain_configs::{ClientConfig, LogSummaryStyle, UpdateableClientConfig}; +use unc_chunks::adapter::ShardsManagerRequestFromClient; +use unc_chunks::client::ShardedTransactionPool; +use unc_chunks::logic::{ + cares_about_shard_this_or_next_epoch, decode_encoded_chunk, persist_chunk, +}; +use unc_chunks::ShardsManager; +use unc_client_primitives::debug::ChunkProduction; +use unc_client_primitives::types::{ + format_shard_sync_phase_per_shard, Error, ShardSyncDownload, ShardSyncStatus, +}; +use unc_epoch_manager::shard_tracker::ShardTracker; +use unc_epoch_manager::EpochManagerAdapter; +use unc_network::types::{AccountKeys, ChainInfo, PeerManagerMessageRequest, SetChainInfo}; +use unc_network::types::{ + HighestHeightPeerInfo, NetworkRequests, PeerManagerAdapter, ReasonForBan, +}; +use unc_o11y::log_assert; +use unc_o11y::WithSpanContextExt; +use unc_pool::InsertTransactionResult; +use unc_primitives::block::{Approval, ApprovalInner, ApprovalMessage, Block, BlockHeader, Tip}; +use unc_primitives::block_header::ApprovalType; +use unc_primitives::challenge::{Challenge, ChallengeBody}; +use unc_primitives::epoch_manager::RngSeed; +use unc_primitives::errors::EpochError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::{merklize, MerklePath, PartialMerkleTree}; +use unc_primitives::network::PeerId; +use unc_primitives::receipt::Receipt; +use unc_primitives::sharding::StateSyncInfo; +use unc_primitives::sharding::{ + ChunkHash, EncodedShardChunk, PartialEncodedChunk, ReedSolomonWrapper, ShardChunk, + ShardChunkHeader, ShardInfo, +}; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{ApprovalFrozen, Gas}; +use unc_primitives::types::StateRoot; +use unc_primitives::types::{AccountId, BlockHeight, EpochId, NumBlocks, ShardId}; +use unc_primitives::unwrap_or_return; +use unc_primitives::utils::MaybeValidated; +use unc_primitives::validator_signer::ValidatorSigner; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_primitives::views::{CatchupStatusView, DroppedReason}; +use unc_store::metadata::DbKind; +use unc_store::ShardUId; +use std::cmp::max; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; +use std::sync::RwLock; +use std::time::{Duration, Instant}; +use tracing::{debug, debug_span, error, info, trace, warn}; + +const NUM_REBROADCAST_BLOCKS: usize = 30; +const CHUNK_HEADERS_FOR_INCLUSION_CACHE_SIZE: usize = 2048; +const NUM_EPOCH_CHUNK_PRODUCERS_TO_KEEP_IN_BLOCKLIST: usize = 1000; + +/// The time we wait for the response to a Epoch Sync request before retrying +// TODO #3488 set 30_000 +pub const EPOCH_SYNC_REQUEST_TIMEOUT: Duration = Duration::from_millis(1_000); +/// How frequently a Epoch Sync response can be sent to a particular peer +// TODO #3488 set 60_000 +pub const EPOCH_SYNC_PEER_TIMEOUT: Duration = Duration::from_millis(10); +/// Drop blocks whose height are beyond head + horizon if it is not in the current epoch. +const BLOCK_HORIZON: u64 = 500; + +/// number of blocks at the epoch start for which we will log more detailed info +pub const EPOCH_START_INFO_BLOCKS: u64 = 500; + +/// Defines whether in case of adversarial block production invalid blocks can +/// be produced. +#[cfg(feature = "test_features")] +#[derive(PartialEq, Eq)] +pub enum AdvProduceBlocksMode { + All, + OnlyValid, +} + +pub struct Client { + /// Adversarial controls - should be enabled only to test disruptive + /// behaviour on chain. + #[cfg(feature = "test_features")] + pub adv_produce_blocks: Option, + #[cfg(feature = "test_features")] + pub produce_invalid_chunks: bool, + #[cfg(feature = "test_features")] + pub produce_invalid_tx_in_chunks: bool, + + /// Fast Forward accrued delta height used to calculate fast forwarded timestamps for each block. + #[cfg(feature = "sandbox")] + pub(crate) accrued_fastforward_delta: unc_primitives::types::BlockHeightDelta, + + /// count for not producing block times + pub config: ClientConfig, + pub sync_status: SyncStatus, + pub state_sync_adapter: Arc>, + pub chain: Chain, + pub doomslug: Doomslug, + pub epoch_manager: Arc, + pub shard_tracker: ShardTracker, + pub runtime_adapter: Arc, + pub shards_manager_adapter: Sender, + pub sharded_tx_pool: ShardedTransactionPool, + prev_block_to_chunk_headers_ready_for_inclusion: LruCache< + CryptoHash, + HashMap, AccountId)>, + >, + pub do_not_include_chunks_from: LruCache<(EpochId, AccountId), ()>, + /// Network adapter. + pub network_adapter: PeerManagerAdapter, + /// Signer for block producer (if present). + pub validator_signer: Option>, + /// Approvals for which we do not have the block yet + pub pending_approvals: + lru::LruCache>, + /// A mapping from a block for which a state sync is underway for the next epoch, and the object + /// storing the current status of the state sync and blocks catch up + pub catchup_state_syncs: + HashMap, BlocksCatchUpState)>, + /// Keeps track of information needed to perform the initial Epoch Sync + pub epoch_sync: EpochSync, + /// Keeps track of syncing headers. + pub header_sync: HeaderSync, + /// Keeps track of syncing block. + pub block_sync: BlockSync, + /// Keeps track of syncing state. + pub state_sync: StateSync, + /// List of currently accumulated challenges. + pub challenges: HashMap, + /// A ReedSolomon instance to reconstruct shard. + pub rs_for_chunk_production: ReedSolomonWrapper, + /// Blocks that have been re-broadcast recently. They should not be broadcast again. + rebroadcasted_blocks: lru::LruCache, + /// Last time the head was updated, or our head was rebroadcasted. Used to re-broadcast the head + /// again to prevent network from stalling if a large percentage of the network missed a block + last_time_head_progress_made: Instant, + + /// Block production timing information. Used only for debug purposes. + /// Stores approval information and production time of the block + pub block_production_info: BlockProductionTracker, + /// Chunk production timing information. Used only for debug purposes. + pub chunk_production_info: lru::LruCache<(BlockHeight, ShardId), ChunkProduction>, + + /// Cached precomputed set of TIER1 accounts. + /// See send_network_chain_info(). + tier1_accounts_cache: Option<(EpochId, Arc)>, + /// Used when it is needed to create flat storage in background for some shards. + flat_storage_creator: Option, + + /// When the "sync block" was requested. + /// The "sync block" is the last block of the previous epoch, i.e. `prev_hash` of the `sync_hash` block. + pub last_time_sync_block_requested: Option>, + + pub chunk_validator: ChunkValidator, +} + +impl Client { + pub(crate) fn update_client_config(&self, update_client_config: UpdateableClientConfig) { + self.config.expected_shutdown.update(update_client_config.expected_shutdown); + self.config.resharding_config.update(update_client_config.resharding_config); + self.config + .produce_chunk_add_transactions_time_limit + .update(update_client_config.produce_chunk_add_transactions_time_limit); + } +} + +// Debug information about the upcoming block. +#[derive(Default)] +pub struct BlockDebugStatus { + // How long is this block 'in progress' (time since we first saw it). + pub in_progress_for: Option, + // How long is this block in orphan pool. + pub in_orphan_for: Option, + // List of chunk hashes that belong to this block. + pub chunk_hashes: Vec, + + // Chunk statuses are below: + // We first sent the request to fetch the chunk + // Later we get the response from the peer and we try to reconstruct it. + // If reconstructions suceeds, the chunk will be marked as complete. + // If it fails (or fragments are missing) - we're going to re-request the chunk again. + + // Chunks that we reqeusted (sent the request to peers). + pub chunks_requested: HashSet, + // Chunks for which we've received the response. + pub chunks_received: HashSet, + // Chunks completed - fully rebuild and present in database. + pub chunks_completed: HashSet, +} + +impl Client { + pub fn new( + config: ClientConfig, + chain_genesis: ChainGenesis, + epoch_manager: Arc, + shard_tracker: ShardTracker, + state_sync_adapter: Arc>, + runtime_adapter: Arc, + network_adapter: PeerManagerAdapter, + shards_manager_adapter: Sender, + validator_signer: Option>, + enable_doomslug: bool, + rng_seed: RngSeed, + snapshot_callbacks: Option, + ) -> Result { + let doomslug_threshold_mode = if enable_doomslug { + DoomslugThresholdMode::TwoThirds + } else { + DoomslugThresholdMode::NoApprovals + }; + let chain_config = ChainConfig { + save_trie_changes: config.save_trie_changes, + background_migration_threads: config.client_background_migration_threads, + resharding_config: config.resharding_config.clone(), + }; + let chain = Chain::new( + epoch_manager.clone(), + shard_tracker.clone(), + runtime_adapter.clone(), + &chain_genesis, + doomslug_threshold_mode, + chain_config.clone(), + snapshot_callbacks, + )?; + // Create flat storage or initiate migration to flat storage. + let flat_storage_creator = FlatStorageCreator::new( + epoch_manager.clone(), + runtime_adapter.clone(), + chain.chain_store(), + chain_config.background_migration_threads, + )?; + let sharded_tx_pool = + ShardedTransactionPool::new(rng_seed, config.transaction_pool_size_limit); + let sync_status = SyncStatus::AwaitingPeers; + let genesis_block = chain.genesis_block(); + let epoch_sync = EpochSync::new( + network_adapter.clone(), + genesis_block.header().epoch_id().clone(), + genesis_block.header().next_epoch_id().clone(), + epoch_manager + .get_epoch_block_producers_ordered( + genesis_block.header().epoch_id(), + genesis_block.hash(), + )? + .iter() + .map(|x| x.0.clone()) + .collect(), + EPOCH_SYNC_REQUEST_TIMEOUT, + EPOCH_SYNC_PEER_TIMEOUT, + ); + let header_sync = HeaderSync::new( + network_adapter.clone(), + config.header_sync_initial_timeout, + config.header_sync_progress_timeout, + config.header_sync_stall_ban_timeout, + config.header_sync_expected_height_per_second, + ); + let block_sync = BlockSync::new( + network_adapter.clone(), + config.block_fetch_horizon, + config.archive, + config.state_sync_enabled, + ); + // Start one actor per shard. + if config.state_sync_enabled { + let epoch_id = chain.chain_store().head().expect("Cannot get chain head.").epoch_id; + let shard_layout = + epoch_manager.get_shard_layout(&epoch_id).expect("Cannot get shard layout."); + match state_sync_adapter.write() { + Ok(mut state_sync_adapter) => { + for shard_uid in shard_layout.shard_uids() { + state_sync_adapter.start(shard_uid); + } + } + Err(_) => panic!("Cannot acquire write lock on sync adapter. Lock poisoned."), + } + } + + let state_sync = StateSync::new( + network_adapter.clone(), + config.state_sync_timeout, + &config.chain_id, + &config.state_sync.sync, + false, + ); + let num_block_producer_seats: usize = config.num_block_producer_seats as usize; + let data_parts = epoch_manager.num_data_parts(); + let parity_parts = epoch_manager.num_total_parts() - data_parts; + + let doomslug = Doomslug::new( + chain.chain_store().largest_target_height()?, + config.min_block_production_delay, + config.max_block_production_delay, + config.max_block_production_delay / 10, + config.max_block_wait_delay, + validator_signer.clone(), + doomslug_threshold_mode, + ); + let chunk_validator = ChunkValidator::new( + validator_signer.clone(), + epoch_manager.clone(), + network_adapter.clone().into_sender(), + runtime_adapter.clone(), + ); + Ok(Self { + #[cfg(feature = "test_features")] + adv_produce_blocks: None, + #[cfg(feature = "test_features")] + produce_invalid_chunks: false, + #[cfg(feature = "test_features")] + produce_invalid_tx_in_chunks: false, + #[cfg(feature = "sandbox")] + accrued_fastforward_delta: 0, + config, + sync_status, + state_sync_adapter, + chain, + doomslug, + epoch_manager, + shard_tracker, + runtime_adapter, + shards_manager_adapter, + sharded_tx_pool, + prev_block_to_chunk_headers_ready_for_inclusion: LruCache::new( + CHUNK_HEADERS_FOR_INCLUSION_CACHE_SIZE, + ), + do_not_include_chunks_from: LruCache::new( + NUM_EPOCH_CHUNK_PRODUCERS_TO_KEEP_IN_BLOCKLIST, + ), + network_adapter, + validator_signer, + pending_approvals: lru::LruCache::new(num_block_producer_seats), + catchup_state_syncs: HashMap::new(), + epoch_sync, + header_sync, + block_sync, + state_sync, + challenges: Default::default(), + rs_for_chunk_production: ReedSolomonWrapper::new(data_parts, parity_parts), + rebroadcasted_blocks: lru::LruCache::new(NUM_REBROADCAST_BLOCKS), + last_time_head_progress_made: StaticClock::instant(), + block_production_info: BlockProductionTracker::new(), + chunk_production_info: lru::LruCache::new(PRODUCTION_TIMES_CACHE_SIZE), + tier1_accounts_cache: None, + flat_storage_creator, + last_time_sync_block_requested: None, + chunk_validator, + }) + } + + // Checks if it's been at least `stall_timeout` since the last time the head was updated, or + // this method was called. If yes, rebroadcasts the current head. + pub fn check_head_progress_stalled(&mut self, stall_timeout: Duration) -> Result<(), Error> { + if StaticClock::instant() > self.last_time_head_progress_made + stall_timeout + && !self.sync_status.is_syncing() + { + let block = self.chain.get_block(&self.chain.head()?.last_block_hash)?; + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::Block { block: block }, + )); + self.last_time_head_progress_made = StaticClock::instant(); + } + Ok(()) + } + + pub fn remove_transactions_for_block( + &mut self, + me: AccountId, + block: &Block, + ) -> Result<(), Error> { + let epoch_id = self.epoch_manager.get_epoch_id(block.hash())?; + for (shard_id, chunk_header) in block.chunks().iter().enumerate() { + let shard_id = shard_id as ShardId; + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, &epoch_id)?; + if block.header().height() == chunk_header.height_included() { + if cares_about_shard_this_or_next_epoch( + Some(&me), + block.header().prev_hash(), + shard_id, + true, + &self.shard_tracker, + ) { + // By now the chunk must be in store, otherwise the block would have been orphaned + let chunk = self.chain.get_chunk(&chunk_header.chunk_hash()).unwrap(); + let transactions = chunk.transactions(); + self.sharded_tx_pool.remove_transactions(shard_uid, transactions); + } + } + } + for challenge in block.challenges().iter() { + self.challenges.remove(&challenge.hash); + } + Ok(()) + } + + pub fn reintroduce_transactions_for_block( + &mut self, + me: AccountId, + block: &Block, + ) -> Result<(), Error> { + let epoch_id = self.epoch_manager.get_epoch_id(block.hash())?; + for (shard_id, chunk_header) in block.chunks().iter().enumerate() { + let shard_id = shard_id as ShardId; + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, &epoch_id)?; + + if block.header().height() == chunk_header.height_included() { + if cares_about_shard_this_or_next_epoch( + Some(&me), + block.header().prev_hash(), + shard_id, + false, + &self.shard_tracker, + ) { + // By now the chunk must be in store, otherwise the block would have been orphaned + let chunk = self.chain.get_chunk(&chunk_header.chunk_hash()).unwrap(); + let reintroduced_count = self + .sharded_tx_pool + .reintroduce_transactions(shard_uid, &chunk.transactions()); + if reintroduced_count < chunk.transactions().len() { + debug!(target: "client", + reintroduced_count, + num_tx = chunk.transactions().len(), + "Reintroduced transactions"); + } + } + } + } + for challenge in block.challenges().iter() { + self.challenges.insert(challenge.hash, challenge.clone()); + } + Ok(()) + } + + /// Checks couple conditions whether Client can produce new block on height + /// `height` on top of block with `prev_header`. + /// Needed to skip several checks in case of adversarial controls enabled. + /// TODO: consider returning `Result<(), Error>` as `Ok(false)` looks like + /// faulty logic. + fn can_produce_block( + &self, + prev_header: &BlockHeader, + height: BlockHeight, + account_id: &AccountId, + next_block_proposer: &AccountId, + ) -> Result { + #[cfg(feature = "test_features")] + { + if self.adv_produce_blocks == Some(AdvProduceBlocksMode::All) { + return Ok(true); + } + } + + // If we are not block proposer, skip block production. + if account_id != next_block_proposer { + info!(target: "client", height, "Skipping block production, not block producer for next block."); + return Ok(false); + } + + #[cfg(feature = "test_features")] + { + if self.adv_produce_blocks == Some(AdvProduceBlocksMode::OnlyValid) { + return Ok(true); + } + } + + // If height is known already, don't produce new block for this height. + let known_height = self.chain.chain_store().get_latest_known()?.height; + if height <= known_height { + return Ok(false); + } + + // If we are to start new epoch with this block, check if the previous + // block is caught up. If it is not the case, we wouldn't be able to + // apply the following block, so we also skip block production. + let prev_hash = prev_header.hash(); + if self.epoch_manager.is_next_block_epoch_start(prev_hash)? { + let prev_prev_hash = prev_header.prev_hash(); + if !self.chain.prev_block_is_caught_up(prev_prev_hash, prev_hash)? { + debug!(target: "client", height, "Skipping block production, prev block is not caught up"); + return Ok(false); + } + } + + Ok(true) + } + + pub fn get_chunk_headers_ready_for_inclusion( + &self, + epoch_id: &EpochId, + prev_block_hash: &CryptoHash, + ) -> HashMap, AccountId)> { + self.prev_block_to_chunk_headers_ready_for_inclusion + .peek(prev_block_hash) + .cloned() + .unwrap_or_default() + .into_iter() + .filter(|(_, (chunk_header, _, chunk_producer))| { + let banned = self + .do_not_include_chunks_from + .contains(&(epoch_id.clone(), chunk_producer.clone())); + if banned { + warn!( + target: "client", + chunk_hash = ?chunk_header.chunk_hash(), + ?chunk_producer, + "Not including chunk from a banned validator"); + metrics::CHUNK_DROPPED_BECAUSE_OF_BANNED_CHUNK_PRODUCER.inc(); + } + !banned + }) + .collect() + } + + pub fn num_chunk_headers_ready_for_inclusion( + &self, + epoch_id: &EpochId, + prev_block_hash: &CryptoHash, + ) -> usize { + let entries = + match self.prev_block_to_chunk_headers_ready_for_inclusion.peek(prev_block_hash) { + Some(entries) => entries, + None => return 0, + }; + entries + .values() + .filter(|(_, _, chunk_producer)| { + !self + .do_not_include_chunks_from + .contains(&(epoch_id.clone(), chunk_producer.clone())) + }) + .count() + } + + /// Produce block if we are block producer for given block `height`. + /// Either returns produced block (not applied) or error. + pub fn produce_block(&mut self, height: BlockHeight) -> Result, Error> { + let _span = tracing::debug_span!(target: "client", "produce_block", height).entered(); + + let head = self.chain.head()?; + assert_eq!( + head.epoch_id, + self.epoch_manager.get_epoch_id_from_prev_block(&head.prev_block_hash).unwrap() + ); + + self.produce_block_on(height, head.last_block_hash) + } + + /// Produce block for given `height` on top of block `prev_hash`. + /// Should be called either from `produce_block` or in tests. + pub fn produce_block_on( + &mut self, + height: BlockHeight, + prev_hash: CryptoHash, + ) -> Result, Error> { + let validator_signer = self + .validator_signer + .as_ref() + .ok_or_else(|| Error::BlockProducer("Called without block producer info.".to_string()))? + .clone(); + + // Check that we are were called at the block that we are producer for. + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(&prev_hash).unwrap(); + let next_block_proposer = self.epoch_manager.get_block_producer(&epoch_id, height)?; + + let prev = self.chain.get_block_header(&prev_hash)?; + let prev_height = prev.height(); + let prev_epoch_id = prev.epoch_id().clone(); + let prev_next_bp_hash = *prev.next_bp_hash(); + + // Check and update the doomslug tip here. This guarantees that our endorsement will be in the + // doomslug witness. Have to do it before checking the ability to produce a block. + let _ = self.check_and_update_doomslug_tip()?; + + if !self.can_produce_block( + &prev, + height, + validator_signer.validator_id(), + &next_block_proposer, + )? { + debug!(target: "client", "Should reschedule block"); + return Ok(None); + } + let (validator_stake, _) = self.epoch_manager.get_validator_by_account_id( + &epoch_id, + &prev_hash, + &next_block_proposer, + )?; + + let validator_pk = validator_stake.take_public_key(); + if validator_pk != validator_signer.public_key() { + debug!(target: "client", + local_validator_key = ?validator_signer.public_key(), + ?validator_pk, + "Local validator key does not match expected validator key, skipping block production"); + #[cfg(not(feature = "test_features"))] + return Ok(None); + #[cfg(feature = "test_features")] + match self.adv_produce_blocks { + None | Some(AdvProduceBlocksMode::OnlyValid) => return Ok(None), + Some(AdvProduceBlocksMode::All) => {} + } + } + + let new_chunks = self.get_chunk_headers_ready_for_inclusion(&epoch_id, &prev_hash); + debug!( + target: "client", + validator=?validator_signer.validator_id(), + height=height, + prev_height=prev.height(), + prev_hash=format_hash(prev_hash), + new_chunks_count=new_chunks.len(), + new_chunks=?new_chunks.keys().sorted().collect_vec(), + "Producing block", + ); + + // If we are producing empty blocks and there are no transactions. + if !self.config.produce_empty_blocks && new_chunks.is_empty() { + debug!(target: "client", "Empty blocks, skipping block production"); + return Ok(None); + } + + let mut approvals_map = self.doomslug.get_witness(&prev_hash, prev_height, height); + + // At this point, the previous epoch hash must be available + let epoch_id = self + .epoch_manager + .get_epoch_id_from_prev_block(&prev_hash) + .expect("Epoch hash should exist at this point"); + let protocol_version = self + .epoch_manager + .get_epoch_protocol_version(&epoch_id) + .expect("Epoch info should be ready at this point"); + if protocol_version > PROTOCOL_VERSION { + panic!("The client protocol version is older than the protocol version of the network. Please update framework. Client protocol version:{}, network protocol version {}", PROTOCOL_VERSION, protocol_version); + } + + let approvals = self + .epoch_manager + .get_epoch_block_approvers_ordered(&prev_hash)? + .into_iter() + .map(|(ApprovalFrozen { account_id, .. }, is_slashed)| { + if is_slashed { + None + } else { + approvals_map.remove(&account_id).map(|x| x.0.signature.into()) + } + }) + .collect(); + + debug_assert_eq!(approvals_map.len(), 0); + + let next_epoch_id = self + .epoch_manager + .get_next_epoch_id_from_prev_block(&prev_hash) + .expect("Epoch hash should exist at this point"); + + let protocol_version = self.epoch_manager.get_epoch_protocol_version(&epoch_id)?; + let gas_price_adjustment_rate = + self.chain.block_economics_config.gas_price_adjustment_rate(protocol_version); + let min_gas_price = self.chain.block_economics_config.min_gas_price(protocol_version); + let max_gas_price = self.chain.block_economics_config.max_gas_price(protocol_version); + + let next_bp_hash = if prev_epoch_id != epoch_id { + Chain::compute_bp_hash( + self.epoch_manager.as_ref(), + next_epoch_id, + epoch_id.clone(), + &prev_hash, + )? + } else { + prev_next_bp_hash + }; + + #[cfg(feature = "sandbox")] + let timestamp_override = Some(StaticClock::utc() + self.sandbox_delta_time()); + #[cfg(not(feature = "sandbox"))] + let timestamp_override = None; + + // Get block extra from previous block. + let block_merkle_tree = self.chain.chain_store().get_block_merkle_tree(&prev_hash)?; + let mut block_merkle_tree = PartialMerkleTree::clone(&block_merkle_tree); + block_merkle_tree.insert(prev_hash); + let block_merkle_root = block_merkle_tree.root(); + // The number of leaves in Block Merkle Tree is the amount of Blocks on the Canonical Chain by construction. + // The ordinal of the next Block will be equal to this amount plus one. + let block_ordinal: NumBlocks = block_merkle_tree.size() + 1; + let prev_block_extra = self.chain.get_block_extra(&prev_hash)?; + let prev_block = self.chain.get_block(&prev_hash)?; + let mut chunks = Chain::get_prev_chunk_headers(self.epoch_manager.as_ref(), &prev_block)?; + + // Add debug information about the block production (and info on when did the chunks arrive). + self.block_production_info.record_block_production( + height, + BlockProductionTracker::construct_chunk_collection_info( + height, + &epoch_id, + chunks.len() as ShardId, + &new_chunks, + self.epoch_manager.as_ref(), + )?, + ); + + // Collect new chunks. + for (shard_id, (mut chunk_header, _, _)) in new_chunks { + *chunk_header.height_included_mut() = height; + chunks[shard_id as usize] = chunk_header; + } + + let prev_header = &prev_block.header(); + + let next_epoch_id = self.epoch_manager.get_next_epoch_id_from_prev_block(&prev_hash)?; + + let minted_amount = if self.epoch_manager.is_next_block_epoch_start(&prev_hash)? { + Some(self.epoch_manager.get_epoch_minted_amount(&next_epoch_id)?) + } else { + None + }; + + let epoch_sync_data_hash = if self.epoch_manager.is_next_block_epoch_start(&prev_hash)? { + Some(self.epoch_manager.get_epoch_sync_data_hash( + prev_block.hash(), + &epoch_id, + &next_epoch_id, + )?) + } else { + None + }; + + // Get all the current challenges. + // TODO(2445): Enable challenges when they are working correctly. + // let challenges = self.challenges.drain().map(|(_, challenge)| challenge).collect(); + let this_epoch_protocol_version = + self.epoch_manager.get_epoch_protocol_version(&epoch_id)?; + let next_epoch_protocol_version = + self.epoch_manager.get_epoch_protocol_version(&next_epoch_id)?; + + let block = Block::produce( + this_epoch_protocol_version, + next_epoch_protocol_version, + prev_header, + height, + block_ordinal, + chunks, + epoch_id, + next_epoch_id, + epoch_sync_data_hash, + approvals, + gas_price_adjustment_rate, + min_gas_price, + max_gas_price, + minted_amount, + prev_block_extra.challenges_result.clone(), + vec![], + &*validator_signer, + next_bp_hash, + block_merkle_root, + timestamp_override, + ); + + // Update latest known even before returning block out, to prevent race conditions. + self.chain + .mut_chain_store() + .save_latest_known(LatestKnown { height, seen: block.header().raw_timestamp() })?; + + metrics::BLOCK_PRODUCED_TOTAL.inc(); + + Ok(Some(block)) + } + + pub fn produce_chunk( + &mut self, + prev_block_hash: CryptoHash, + epoch_id: &EpochId, + last_header: ShardChunkHeader, + next_height: BlockHeight, + shard_id: ShardId, + ) -> Result, Vec)>, Error> { + let timer = Instant::now(); + let _timer = + metrics::PRODUCE_CHUNK_TIME.with_label_values(&[&shard_id.to_string()]).start_timer(); + let _span = tracing::debug_span!(target: "client", "produce_chunk", next_height, shard_id, ?epoch_id).entered(); + let validator_signer = self + .validator_signer + .as_ref() + .ok_or_else(|| Error::ChunkProducer("Called without block producer info.".to_string()))? + .clone(); + + let chunk_proposer = + self.epoch_manager.get_chunk_producer(epoch_id, next_height, shard_id).unwrap(); + if validator_signer.validator_id() != &chunk_proposer { + debug!(target: "client", + me = ?validator_signer.validator_id(), + ?chunk_proposer, + next_height, + shard_id, + "Not producing chunk. Not chunk producer for next chunk."); + return Ok(None); + } + if self.epoch_manager.is_next_block_epoch_start(&prev_block_hash)? { + let prev_prev_hash = *self.chain.get_block_header(&prev_block_hash)?.prev_hash(); + if !self.chain.prev_block_is_caught_up(&prev_prev_hash, &prev_block_hash)? { + // See comment in similar snipped in `produce_block` + debug!(target: "client", shard_id, next_height, "Produce chunk: prev block is not caught up"); + return Err(Error::ChunkProducer( + "State for the epoch is not downloaded yet, skipping chunk production" + .to_string(), + )); + } + } + + debug!(target: "client", me = ?validator_signer.validator_id(), next_height, shard_id, "Producing chunk"); + + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, epoch_id)?; + let chunk_extra = self + .chain + .get_chunk_extra(&prev_block_hash, &shard_uid) + .map_err(|err| Error::ChunkProducer(format!("No chunk extra available: {}", err)))?; + + let prev_block_header = self.chain.get_block_header(&prev_block_hash)?; + let transactions = self.prepare_transactions( + shard_uid, + chunk_extra.gas_limit(), + *chunk_extra.state_root(), + &prev_block_header, + )?; + #[cfg(feature = "test_features")] + let transactions = Self::maybe_insert_invalid_transaction( + transactions, + prev_block_hash, + self.produce_invalid_tx_in_chunks, + ); + let num_filtered_transactions = transactions.len(); + let (tx_root, _) = merklize(&transactions); + let outgoing_receipts = self.chain.get_outgoing_receipts_for_shard( + prev_block_hash, + shard_id, + last_header.height_included(), + )?; + + let outgoing_receipts_root = self.calculate_receipts_root(epoch_id, &outgoing_receipts)?; + let protocol_version = self.epoch_manager.get_epoch_protocol_version(epoch_id)?; + let gas_used = chunk_extra.gas_used(); + #[cfg(feature = "test_features")] + let gas_used = if self.produce_invalid_chunks { gas_used + 1 } else { gas_used }; + let (encoded_chunk, merkle_paths) = ShardsManager::create_encoded_shard_chunk( + prev_block_hash, + *chunk_extra.state_root(), + *chunk_extra.outcome_root(), + next_height, + shard_id, + gas_used, + chunk_extra.gas_limit(), + chunk_extra.balance_burnt(), + chunk_extra.validator_power_proposals().collect(), + chunk_extra.validator_frozen_proposals().collect(), + transactions, + &outgoing_receipts, + outgoing_receipts_root, + tx_root, + &*validator_signer, + &mut self.rs_for_chunk_production, + protocol_version, + )?; + + debug!(target: "client", + me = %validator_signer.validator_id(), + chunk_hash = ?encoded_chunk.chunk_hash(), + %prev_block_hash, + num_filtered_transactions, + num_outgoing_receipts = outgoing_receipts.len(), + "Produced chunk"); + + metrics::CHUNK_PRODUCED_TOTAL.inc(); + self.chunk_production_info.put( + (next_height, shard_id), + ChunkProduction { + chunk_production_time: Some(StaticClock::utc()), + chunk_production_duration_millis: Some(timer.elapsed().as_millis() as u64), + }, + ); + + Ok(Some((encoded_chunk, merkle_paths, outgoing_receipts))) + } + + /// Calculates the root of receipt proofs. + /// All receipts are groupped by receiver_id and hash is calculated + /// for each such group. Then we merkalize these hashes to calculate + /// the receipts root. + /// + /// Receipts root is used in the following ways: + /// 1. Someone who cares about shard will download all the receipts + /// and checks if those correspond to receipts_root. + /// 2. Anyone who asks for one's incoming receipts will receive a piece + /// of incoming receipts only with merkle receipts proofs which can + /// be checked locally. + fn calculate_receipts_root( + &self, + epoch_id: &EpochId, + receipts: &[Receipt], + ) -> Result { + let shard_layout = self.epoch_manager.get_shard_layout(epoch_id)?; + let receipts_hashes = Chain::build_receipts_hashes(&receipts, &shard_layout); + let (receipts_root, _) = merklize(&receipts_hashes); + Ok(receipts_root) + } + + #[cfg(feature = "test_features")] + fn maybe_insert_invalid_transaction( + mut txs: Vec, + prev_block_hash: CryptoHash, + insert: bool, + ) -> Vec { + if insert { + txs.push(SignedTransaction::new( + unc_crypto::Signature::empty(unc_crypto::KeyType::ED25519), + unc_primitives::transaction::Transaction::new( + "test".parse().unwrap(), + unc_crypto::PublicKey::empty(unc_crypto::KeyType::SECP256K1), + "other".parse().unwrap(), + 3, + prev_block_hash, + ), + )); + } + txs + } + + /// Prepares an ordered list of valid transactions from the pool up the limits. + fn prepare_transactions( + &mut self, + shard_uid: ShardUId, + gas_limit: Gas, + state_root: StateRoot, + prev_block_header: &BlockHeader, + ) -> Result, Error> { + let Self { chain, sharded_tx_pool, epoch_manager, runtime_adapter: runtime, .. } = self; + + let shard_id = shard_uid.shard_id as ShardId; + let next_epoch_id = epoch_manager.get_epoch_id_from_prev_block(prev_block_header.hash())?; + let protocol_version = epoch_manager.get_epoch_protocol_version(&next_epoch_id)?; + + let transactions = if let Some(mut iter) = sharded_tx_pool.get_pool_iterator(shard_uid) { + let transaction_validity_period = chain.transaction_validity_period; + runtime.prepare_transactions( + prev_block_header.next_gas_price(), + gas_limit, + &next_epoch_id, + shard_id, + state_root, + // while the height of the next block that includes the chunk might not be prev_height + 1, + // passing it will result in a more conservative check and will not accidentally allow + // invalid transactions to be included. + prev_block_header.height() + 1, + &mut iter, + &mut |tx: &SignedTransaction| -> bool { + chain + .chain_store() + .check_transaction_validity_period( + prev_block_header, + &tx.transaction.block_hash, + transaction_validity_period, + ) + .is_ok() + }, + protocol_version, + self.config.produce_chunk_add_transactions_time_limit.get(), + )? + } else { + vec![] + }; + // Reintroduce valid transactions back to the pool. They will be removed when the chunk is + // included into the block. + let reintroduced_count = sharded_tx_pool.reintroduce_transactions(shard_uid, &transactions); + if reintroduced_count < transactions.len() { + debug!(target: "client", reintroduced_count, num_tx = transactions.len(), "Reintroduced transactions"); + } + Ok(transactions) + } + + pub fn send_challenges(&mut self, challenges: Vec) { + if let Some(validator_signer) = &self.validator_signer { + for body in challenges { + let challenge = Challenge::produce(body, &**validator_signer); + self.challenges.insert(challenge.hash, challenge.clone()); + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::Challenge(challenge), + )); + } + } + } + + /// Processes received block. Ban peer if the block header is invalid or the block is ill-formed. + // This function is just a wrapper for process_block_impl that makes error propagation easier. + pub fn receive_block( + &mut self, + block: Block, + peer_id: PeerId, + was_requested: bool, + apply_chunks_done_callback: DoneApplyChunkCallback, + ) { + let hash = *block.hash(); + let prev_hash = *block.header().prev_hash(); + let _span = tracing::debug_span!( + target: "client", + "receive_block", + me = ?self.validator_signer.as_ref().map(|vs| vs.validator_id()), + %prev_hash, + %hash, + height = block.header().height(), + %peer_id, + was_requested) + .entered(); + + let res = + self.receive_block_impl(block, peer_id, was_requested, apply_chunks_done_callback); + // Log the errors here. Note that the real error handling logic is already + // done within process_block_impl, this is just for logging. + if let Err(err) = res { + if err.is_bad_data() { + warn!(target: "client", ?err, "Receive bad block"); + } else if err.is_error() { + if let unc_chain::Error::DBNotFoundErr(msg) = &err { + debug_assert!(!msg.starts_with("BLOCK HEIGHT"), "{:?}", err); + } + if self.sync_status.is_syncing() { + // While syncing, we may receive blocks that are older or from next epochs. + // This leads to Old Block or EpochOutOfBounds errors. + debug!(target: "client", ?err, sync_status = ?self.sync_status, "Error receiving a block. is syncing"); + } else { + error!(target: "client", ?err, "Error on receiving a block. Not syncing"); + } + } else { + debug!(target: "client", ?err, "Process block: refused by chain"); + } + self.chain.blocks_delay_tracker.mark_block_errored(&hash, err.to_string()); + } + } + + /// Processes received block. + /// This function first does some pre-check based on block height to avoid processing + /// blocks multiple times. + /// Then it process the block header. If the header if valid, broadcast the block to its peers + /// Then it starts the block processing process to process the full block. + pub(crate) fn receive_block_impl( + &mut self, + block: Block, + peer_id: PeerId, + was_requested: bool, + apply_chunks_done_callback: DoneApplyChunkCallback, + ) -> Result<(), unc_chain::Error> { + let _span = + debug_span!(target: "chain", "receive_block_impl", was_requested, ?peer_id).entered(); + self.chain.blocks_delay_tracker.mark_block_received( + &block, + StaticClock::instant(), + StaticClock::utc(), + ); + // To protect ourselves from spamming, we do some pre-check on block height before we do any + // real processing. + if !self.check_block_height(&block, was_requested)? { + self.chain + .blocks_delay_tracker + .mark_block_dropped(block.hash(), DroppedReason::HeightProcessed); + return Ok(()); + } + + // Before we proceed with any further processing, we first check that the block + // hash and signature matches to make sure the block is indeed produced by the assigned + // block producer. If not, we drop the block immediately and ban the peer + if self.chain.verify_block_hash_and_signature(&block)? + == VerifyBlockHashAndSignatureResult::Incorrect + { + self.ban_peer(peer_id, ReasonForBan::BadBlockHeader); + return Err(unc_chain::Error::InvalidSignature); + } + + let prev_hash = *block.header().prev_hash(); + let block = block.into(); + self.verify_and_rebroadcast_block(&block, was_requested, &peer_id)?; + let provenance = + if was_requested { unc_chain::Provenance::SYNC } else { unc_chain::Provenance::NONE }; + let res = self.start_process_block(block, provenance, apply_chunks_done_callback); + match &res { + Err(unc_chain::Error::Orphan) => { + debug!(target: "chain", ?prev_hash, "Orphan error"); + if !self.chain.is_orphan(&prev_hash) { + debug!(target: "chain", "not orphan"); + self.request_block(prev_hash, peer_id) + } + } + err => { + debug!(target: "chain", ?err, "some other error"); + } + } + res + } + + /// To protect ourselves from spamming, we do some pre-check on block height before we do any + /// processing. This function returns true if the block height is valid. + fn check_block_height( + &self, + block: &Block, + was_requested: bool, + ) -> Result { + let head = self.chain.head()?; + let is_syncing = self.sync_status.is_syncing(); + if block.header().height() >= head.height + BLOCK_HORIZON && is_syncing && !was_requested { + debug!(target: "client", head_height = head.height, "Dropping a block that is too far ahead."); + return Ok(false); + } + let tail = self.chain.tail()?; + if block.header().height() < tail { + debug!(target: "client", tail_height = tail, "Dropping a block that is too far behind."); + return Ok(false); + } + // drop the block if a) it is not requested, b) we already processed this height, + //est-utils/actix-test-utils/src/lib.rs c) it is not building on top of current head + if !was_requested + && block.header().prev_hash() + != &self + .chain + .head() + .map_or_else(|_| CryptoHash::default(), |tip| tip.last_block_hash) + { + if self.chain.is_height_processed(block.header().height())? { + debug!(target: "client", height = block.header().height(), "Dropping a block because we've seen this height before and we didn't request it"); + return Ok(false); + } + } + Ok(true) + } + + /// Verify the block and rebroadcast it if it is valid, ban the peer if it's invalid. + /// Ignore all other errors because the full block will be processed later. + /// Note that this happens before the full block processing logic because we want blocks to be + /// propagated in the network fast. + fn verify_and_rebroadcast_block( + &mut self, + block: &MaybeValidated, + was_requested: bool, + peer_id: &PeerId, + ) -> Result<(), unc_chain::Error> { + let res = self.chain.process_block_header(block.header(), &mut vec![]); + let res = res.and_then(|_| self.chain.validate_block(block)); + match res { + Ok(_) => { + let head = self.chain.head()?; + // do not broadcast blocks that are too far back. + if (head.height < block.header().height() + || &head.epoch_id == block.header().epoch_id()) + && !was_requested + && !self.sync_status.is_syncing() + { + self.rebroadcast_block(block.as_ref().into_inner()); + } + Ok(()) + } + Err(e) if e.is_bad_data() => { + // We don't ban a peer if the block timestamp is too much in the future since it's possible + // that a block is considered valid in one machine and invalid in another machine when their + // clocks are not synced. + if !matches!(e, unc_chain::Error::InvalidBlockFutureTime(_)) { + self.ban_peer(peer_id.clone(), ReasonForBan::BadBlockHeader); + } + Err(e) + } + Err(_) => { + // We are ignoring all other errors and proceeding with the + // block. If it is an orphan (i.e. we haven’t processed its + // previous block) than we will get MissingBlock errors. In + // those cases we shouldn’t reject the block instead passing + // it along. Eventually, it’ll get saved as an orphan. + Ok(()) + } + } + } + + /// Start the processing of a block. Note that this function will return before + /// the full processing is finished because applying chunks is done asynchronously + /// in the rayon thread pool. + /// `apply_chunks_done_callback`: a callback that will be called when applying chunks is finished. + pub fn start_process_block( + &mut self, + block: MaybeValidated, + provenance: Provenance, + apply_chunks_done_callback: DoneApplyChunkCallback, + ) -> Result<(), unc_chain::Error> { + let _span = debug_span!( + target: "chain", + "start_process_block", + ?provenance, + block_height = block.header().height()) + .entered(); + let mut block_processing_artifacts = BlockProcessingArtifact::default(); + + let result = { + let me = self + .validator_signer + .as_ref() + .map(|validator_signer| validator_signer.validator_id().clone()); + self.chain.start_process_block_async( + &me, + block, + provenance, + &mut block_processing_artifacts, + apply_chunks_done_callback, + ) + }; + + self.process_block_processing_artifact(block_processing_artifacts); + + // Send out challenge if the block was found to be invalid. + if let Some(validator_signer) = self.validator_signer.as_ref() { + if let Err(e) = &result { + match e { + unc_chain::Error::InvalidChunkProofs(chunk_proofs) => { + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::Challenge(Challenge::produce( + ChallengeBody::ChunkProofs(*chunk_proofs.clone()), + &**validator_signer, + )), + )); + } + unc_chain::Error::InvalidChunkState(chunk_state) => { + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::Challenge(Challenge::produce( + ChallengeBody::ChunkState(*chunk_state.clone()), + &**validator_signer, + )), + )); + } + _ => {} + } + } + } + + result + } + + /// Check if there are any blocks that has finished applying chunks, run post processing on these + /// blocks. + pub fn postprocess_ready_blocks( + &mut self, + apply_chunks_done_callback: DoneApplyChunkCallback, + should_produce_chunk: bool, + ) -> (Vec, HashMap) { + let _span = debug_span!(target: "client", "postprocess_ready_blocks", should_produce_chunk) + .entered(); + let me = self + .validator_signer + .as_ref() + .map(|validator_signer| validator_signer.validator_id().clone()); + let mut block_processing_artifacts = BlockProcessingArtifact::default(); + let (accepted_blocks, errors) = self.chain.postprocess_ready_blocks( + &me, + &mut block_processing_artifacts, + apply_chunks_done_callback, + ); + if accepted_blocks.iter().any(|accepted_block| accepted_block.status.is_new_head()) { + self.shards_manager_adapter.send(ShardsManagerRequestFromClient::UpdateChainHeads { + head: self.chain.head().unwrap(), + header_head: self.chain.header_head().unwrap(), + }); + } + self.process_block_processing_artifact(block_processing_artifacts); + let accepted_blocks_hashes = + accepted_blocks.iter().map(|accepted_block| accepted_block.hash).collect(); + for accepted_block in accepted_blocks { + self.on_block_accepted_with_optional_chunk_produce( + accepted_block.hash, + accepted_block.status, + accepted_block.provenance, + !should_produce_chunk, + ); + } + self.last_time_head_progress_made = + max(self.chain.get_last_time_head_updated(), self.last_time_head_progress_made); + (accepted_blocks_hashes, errors) + } + + /// Process the result of block processing from chain, finish the steps that can't be done + /// in chain, including + /// - sending challenges + /// - requesting missing chunks + pub(crate) fn process_block_processing_artifact( + &mut self, + block_processing_artifacts: BlockProcessingArtifact, + ) { + let BlockProcessingArtifact { + orphans_missing_chunks, + blocks_missing_chunks, + challenges, + invalid_chunks, + } = block_processing_artifacts; + // Send out challenges that accumulated via on_challenge. + self.send_challenges(challenges); + // For any missing chunk, let the ShardsManager know of the chunk header so that it may + // apply forwarded parts. This may end up completing the chunk. + let missing_chunks = blocks_missing_chunks + .iter() + .flat_map(|block| block.missing_chunks.iter()) + .chain(orphans_missing_chunks.iter().flat_map(|block| block.missing_chunks.iter())); + for chunk in missing_chunks { + self.shards_manager_adapter + .send(ShardsManagerRequestFromClient::ProcessChunkHeaderFromBlock(chunk.clone())); + } + // Request any missing chunks (which may be completed by the + // process_chunk_header_from_block call, but that is OK as it would be noop). + self.request_missing_chunks(blocks_missing_chunks, orphans_missing_chunks); + + for chunk_header in invalid_chunks { + if let Err(err) = self.ban_chunk_producer_for_producing_invalid_chunk(chunk_header) { + error!(target: "client", ?err, "Failed to ban chunk producer for producing invalid chunk"); + } + } + } + + fn ban_chunk_producer_for_producing_invalid_chunk( + &mut self, + chunk_header: ShardChunkHeader, + ) -> Result<(), Error> { + let epoch_id = + self.epoch_manager.get_epoch_id_from_prev_block(chunk_header.prev_block_hash())?; + let chunk_producer = self.epoch_manager.get_chunk_producer( + &epoch_id, + chunk_header.height_created(), + chunk_header.shard_id(), + )?; + error!( + target: "client", + ?chunk_producer, + ?epoch_id, + chunk_hash = ?chunk_header.chunk_hash(), + "Banning chunk producer for producing invalid chunk"); + metrics::CHUNK_PRODUCER_BANNED_FOR_EPOCH.inc(); + self.do_not_include_chunks_from.put((epoch_id, chunk_producer), ()); + Ok(()) + } + + fn rebroadcast_block(&mut self, block: &Block) { + if self.rebroadcasted_blocks.get(block.hash()).is_none() { + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::Block { block: block.clone() }, + )); + self.rebroadcasted_blocks.put(*block.hash(), ()); + } + } + + /// Called asynchronously when the ShardsManager finishes processing a chunk. + pub fn on_chunk_completed( + &mut self, + partial_chunk: PartialEncodedChunk, + shard_chunk: Option, + apply_chunks_done_callback: DoneApplyChunkCallback, + ) { + let chunk_header = partial_chunk.cloned_header(); + self.chain.blocks_delay_tracker.mark_chunk_completed(&chunk_header, StaticClock::utc()); + self.block_production_info + .record_chunk_collected(partial_chunk.height_created(), partial_chunk.shard_id()); + persist_chunk(partial_chunk, shard_chunk, self.chain.mut_chain_store()) + .expect("Could not persist chunk"); + // We're marking chunk as accepted. + self.chain.blocks_with_missing_chunks.accept_chunk(&chunk_header.chunk_hash()); + // If this was the last chunk that was missing for a block, it will be processed now. + self.process_blocks_with_missing_chunks(apply_chunks_done_callback) + } + + /// Called asynchronously when the ShardsManager finishes processing a chunk but the chunk + /// is invalid. + pub fn on_invalid_chunk(&mut self, encoded_chunk: EncodedShardChunk) { + let mut update = self.chain.mut_chain_store().store_update(); + update.save_invalid_chunk(encoded_chunk); + if let Err(err) = update.commit() { + error!(target: "client", ?err, "Error saving invalid chunk"); + } + } + + pub fn on_chunk_header_ready_for_inclusion( + &mut self, + chunk_header: ShardChunkHeader, + chunk_producer: AccountId, + ) { + let prev_block_hash = chunk_header.prev_block_hash(); + self.prev_block_to_chunk_headers_ready_for_inclusion + .get_or_insert(*prev_block_hash, || HashMap::new()); + self.prev_block_to_chunk_headers_ready_for_inclusion + .get_mut(prev_block_hash) + .unwrap() + .insert(chunk_header.shard_id(), (chunk_header, chrono::Utc::now(), chunk_producer)); + } + + pub fn sync_block_headers( + &mut self, + headers: Vec, + ) -> Result<(), unc_chain::Error> { + let mut challenges = vec![]; + self.chain.sync_block_headers(headers, &mut challenges)?; + self.send_challenges(challenges); + self.shards_manager_adapter.send(ShardsManagerRequestFromClient::UpdateChainHeads { + head: self.chain.head().unwrap(), + header_head: self.chain.header_head().unwrap(), + }); + Ok(()) + } + + /// Checks if the latest hash known to Doomslug matches the current head, and updates it if not. + pub fn check_and_update_doomslug_tip(&mut self) -> Result<(), Error> { + let tip = self.chain.head()?; + + if tip.last_block_hash != self.doomslug.get_tip().0 { + // We need to update the doomslug tip + let last_final_hash = + *self.chain.get_block_header(&tip.last_block_hash)?.last_final_block(); + let last_final_height = if last_final_hash == CryptoHash::default() { + self.chain.genesis().height() + } else { + self.chain.get_block_header(&last_final_hash)?.height() + }; + self.doomslug.set_tip( + StaticClock::instant(), + tip.last_block_hash, + tip.height, + last_final_height, + ); + } + + Ok(()) + } + + #[cfg(feature = "sandbox")] + pub fn sandbox_update_tip(&mut self, height: BlockHeight) -> Result<(), Error> { + let tip = self.chain.head()?; + + let last_final_hash = + *self.chain.get_block_header(&tip.last_block_hash)?.last_final_block(); + let last_final_height = if last_final_hash == CryptoHash::default() { + self.chain.genesis().height() + } else { + self.chain.get_block_header(&last_final_hash)?.height() + }; + self.doomslug.set_tip( + StaticClock::instant(), + tip.last_block_hash, + height, + last_final_height, + ); + + Ok(()) + } + + /// Gets the advanced timestamp delta in nanoseconds for sandbox once it has been fast-forwarded + #[cfg(feature = "sandbox")] + pub fn sandbox_delta_time(&self) -> chrono::Duration { + let avg_block_prod_time = (self.config.min_block_production_delay.as_nanos() + + self.config.max_block_production_delay.as_nanos()) + / 2; + + let ns = (self.accrued_fastforward_delta as u128 * avg_block_prod_time) + .try_into() + .unwrap_or_else(|_| { + panic!( + "Too high of a delta_height {} to convert into u64", + self.accrued_fastforward_delta + ) + }); + + chrono::Duration::nanoseconds(ns) + } + + pub fn send_approval( + &mut self, + parent_hash: &CryptoHash, + approval: Approval, + ) -> Result<(), Error> { + let next_epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(parent_hash)?; + let next_block_producer = + self.epoch_manager.get_block_producer(&next_epoch_id, approval.target_height)?; + if Some(&next_block_producer) == self.validator_signer.as_ref().map(|x| x.validator_id()) { + self.collect_block_approval(&approval, ApprovalType::SelfApproval); + } else { + debug!(target: "client", + approval_inner = ?approval.inner, + account_id = ?approval.account_id, + next_bp = ?next_block_producer, + target_height = approval.target_height, + "Sending an approval"); + let approval_message = ApprovalMessage::new(approval, next_block_producer); + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::Approval { approval_message }, + )); + } + + Ok(()) + } + + /// Gets called when block got accepted. + /// Only produce chunk if `skip_produce_chunk` is false. + /// `skip_produce_chunk` is set to true to simulate when there are missing chunks in a block + pub fn on_block_accepted_with_optional_chunk_produce( + &mut self, + block_hash: CryptoHash, + status: BlockStatus, + provenance: Provenance, + skip_produce_chunk: bool, + ) { + let _span = tracing::debug_span!( + target: "client", + "on_block_accepted_with_optional_chunk_produce", + ?block_hash, + ?status, + ?provenance, + skip_produce_chunk, + is_syncing = self.sync_status.is_syncing(), + sync_status = ?self.sync_status) + .entered(); + let block = match self.chain.get_block(&block_hash) { + Ok(block) => block, + Err(err) => { + error!(target: "client", ?err, ?block_hash, "Failed to find block that was just accepted"); + return; + } + }; + + let _ = self.check_and_update_doomslug_tip(); + + // If we produced the block, then it should have already been broadcasted. + // If received the block from another node then broadcast "header first" to minimize network traffic. + if provenance == Provenance::NONE { + let endorsements = self + .pending_approvals + .pop(&ApprovalInner::Endorsement(block_hash)) + .unwrap_or_default(); + let skips = self + .pending_approvals + .pop(&ApprovalInner::Skip(block.header().height())) + .unwrap_or_default(); + + for (_account_id, (approval, approval_type)) in + endorsements.into_iter().chain(skips.into_iter()) + { + self.collect_block_approval(&approval, approval_type); + } + } + + if status.is_new_head() { + let last_final_block = block.header().last_final_block(); + let last_finalized_height = if last_final_block == &CryptoHash::default() { + self.chain.genesis().height() + } else { + self.chain.get_block_header(last_final_block).map_or(0, |header| header.height()) + }; + self.chain.blocks_with_missing_chunks.prune_blocks_below_height(last_finalized_height); + + { + let _span = tracing::debug_span!( + target: "client", + "garbage_collection", + block_hash = ?block.hash(), + height = block.header().height()) + .entered(); + let _gc_timer = metrics::GC_TIME.start_timer(); + let result = self.clear_data(); + log_assert!(result.is_ok(), "Can't clear old data, {:?}", result); + } + + // send_network_chain_info should be called whenever the chain head changes. + // See send_network_chain_info() for more details. + if let Err(err) = self.send_network_chain_info() { + error!(target: "client", ?err, "Failed to update network chain info"); + } + + // If the next block is the first of the next epoch and the shard + // layout is changing we need to reshard the transaction pool. + // TODO make sure transactions don't get added for the old shard + // layout after the pool resharding + if self.epoch_manager.is_next_block_epoch_start(&block_hash).unwrap_or(false) { + let new_shard_layout = + self.epoch_manager.get_shard_layout_from_prev_block(&block_hash); + let old_shard_layout = + self.epoch_manager.get_shard_layout_from_prev_block(block.header().prev_hash()); + match (old_shard_layout, new_shard_layout) { + (Ok(old_shard_layout), Ok(new_shard_layout)) => { + if old_shard_layout != new_shard_layout { + self.sharded_tx_pool.reshard(&old_shard_layout, &new_shard_layout); + } + } + (old_shard_layout, new_shard_layout) => { + tracing::warn!(target: "client", ?old_shard_layout, ?new_shard_layout, "failed to check if shard layout is changing"); + } + } + } + } + + if let Some(validator_signer) = self.validator_signer.clone() { + let validator_id = validator_signer.validator_id().clone(); + + if !self.reconcile_transaction_pool(validator_id.clone(), status, &block) { + return; + } + + if provenance != Provenance::SYNC + && !self.sync_status.is_syncing() + && !skip_produce_chunk + { + self.produce_chunks(&block, validator_id); + } else { + info!(target: "client", "not producing a chunk"); + } + } + + self.shards_manager_adapter + .send(ShardsManagerRequestFromClient::CheckIncompleteChunks(*block.hash())); + } + + /// Reconcile the transaction pool after processing a block. + /// returns true if it's ok to proceed to produce chunks + /// returns false when handling a fork and there is no need to produce chunks + fn reconcile_transaction_pool( + &mut self, + validator_id: AccountId, + status: BlockStatus, + block: &Block, + ) -> bool { + match status { + BlockStatus::Next => { + // If this block immediately follows the current tip, remove + // transactions from the txpool. + self.remove_transactions_for_block(validator_id, block).unwrap_or_default(); + } + BlockStatus::Fork => { + // If it's a fork, no need to reconcile transactions or produce chunks. + return false; + } + BlockStatus::Reorg(prev_head) => { + // If a reorg happened, reintroduce transactions from the + // previous chain and remove transactions from the new chain. + let mut reintroduce_head = self.chain.get_block_header(&prev_head).unwrap(); + let mut remove_head = block.header().clone(); + assert_ne!(remove_head.hash(), reintroduce_head.hash()); + + let mut to_remove = vec![]; + let mut to_reintroduce = vec![]; + + while remove_head.hash() != reintroduce_head.hash() { + while remove_head.height() > reintroduce_head.height() { + to_remove.push(*remove_head.hash()); + remove_head = + self.chain.get_block_header(remove_head.prev_hash()).unwrap().clone(); + } + while reintroduce_head.height() > remove_head.height() + || reintroduce_head.height() == remove_head.height() + && reintroduce_head.hash() != remove_head.hash() + { + to_reintroduce.push(*reintroduce_head.hash()); + reintroduce_head = self + .chain + .get_block_header(reintroduce_head.prev_hash()) + .unwrap() + .clone(); + } + } + + for to_reintroduce_hash in to_reintroduce { + if let Ok(block) = self.chain.get_block(&to_reintroduce_hash) { + let block = block.clone(); + self.reintroduce_transactions_for_block(validator_id.clone(), &block) + .unwrap_or_default(); + } + } + + for to_remove_hash in to_remove { + if let Ok(block) = self.chain.get_block(&to_remove_hash) { + let block = block.clone(); + self.remove_transactions_for_block(validator_id.clone(), &block) + .unwrap_or_default(); + } + } + } + }; + true + } + + // Produce new chunks + fn produce_chunks(&mut self, block: &Block, validator_id: AccountId) { + let _span = debug_span!( + target: "client", + "produce_chunks", + ?validator_id, + block_height = block.header().height()) + .entered(); + let epoch_id = + self.epoch_manager.get_epoch_id_from_prev_block(block.header().hash()).unwrap(); + for shard_id in self.epoch_manager.shard_ids(&epoch_id).unwrap() { + let next_height = block.header().height() + 1; + let epoch_manager = self.epoch_manager.as_ref(); + let chunk_proposer = + epoch_manager.get_chunk_producer(&epoch_id, next_height, shard_id).unwrap(); + if &chunk_proposer != &validator_id { + continue; + } + + let _span = debug_span!( + target: "client", + "on_block_accepted", + prev_block_hash = ?*block.hash(), + ?shard_id) + .entered(); + let _timer = metrics::PRODUCE_AND_DISTRIBUTE_CHUNK_TIME + .with_label_values(&[&shard_id.to_string()]) + .start_timer(); + let last_header = Chain::get_prev_chunk_header(epoch_manager, block, shard_id).unwrap(); + match self.produce_chunk(*block.hash(), &epoch_id, last_header, next_height, shard_id) { + Ok(Some((encoded_chunk, merkle_paths, receipts))) => { + let chunk_header = encoded_chunk.cloned_header(); + let shard_chunk = self + .persist_and_distribute_encoded_chunk( + encoded_chunk, + merkle_paths, + receipts, + validator_id.clone(), + ) + .expect("Failed to process produced chunk"); + if let Err(err) = self.send_chunk_state_witness_to_chunk_validators( + &epoch_id, + &chunk_header, + &shard_chunk, + ) { + tracing::error!(target: "client", ?err, "Failed to send chunk state witness to chunk validators"); + } + } + Ok(None) => {} + Err(err) => { + error!(target: "client", ?err, "Error producing chunk"); + } + } + } + } + + pub fn persist_and_distribute_encoded_chunk( + &mut self, + encoded_chunk: EncodedShardChunk, + merkle_paths: Vec, + receipts: Vec, + validator_id: AccountId, + ) -> Result { + let (shard_chunk, partial_chunk) = decode_encoded_chunk( + &encoded_chunk, + merkle_paths.clone(), + Some(&validator_id), + self.epoch_manager.as_ref(), + &self.shard_tracker, + )?; + persist_chunk( + partial_chunk.clone(), + Some(shard_chunk.clone()), + self.chain.mut_chain_store(), + )?; + self.on_chunk_header_ready_for_inclusion(encoded_chunk.cloned_header(), validator_id); + self.shards_manager_adapter.send(ShardsManagerRequestFromClient::DistributeEncodedChunk { + partial_chunk, + encoded_chunk, + merkle_paths, + outgoing_receipts: receipts, + }); + Ok(shard_chunk) + } + + pub fn request_missing_chunks( + &mut self, + blocks_missing_chunks: Vec, + orphans_missing_chunks: Vec, + ) { + let _span = debug_span!( + target: "client", + "request_missing_chunks", + ?blocks_missing_chunks, + ?orphans_missing_chunks) + .entered(); + let now = StaticClock::utc(); + for BlockMissingChunks { prev_hash, missing_chunks } in blocks_missing_chunks { + for chunk in &missing_chunks { + self.chain.blocks_delay_tracker.mark_chunk_requested(chunk, now); + } + self.shards_manager_adapter.send(ShardsManagerRequestFromClient::RequestChunks { + chunks_to_request: missing_chunks, + prev_hash, + }); + } + + for OrphanMissingChunks { missing_chunks, epoch_id, ancestor_hash } in + orphans_missing_chunks + { + for chunk in &missing_chunks { + self.chain.blocks_delay_tracker.mark_chunk_requested(chunk, now); + } + self.shards_manager_adapter.send( + ShardsManagerRequestFromClient::RequestChunksForOrphan { + chunks_to_request: missing_chunks, + epoch_id, + ancestor_hash, + }, + ); + } + } + + /// Check if any block with missing chunks is ready to be processed + pub fn process_blocks_with_missing_chunks( + &mut self, + apply_chunks_done_callback: DoneApplyChunkCallback, + ) { + let _span = debug_span!(target: "client", "process_blocks_with_missing_chunks").entered(); + let me = + self.validator_signer.as_ref().map(|validator_signer| validator_signer.validator_id()); + let mut blocks_processing_artifacts = BlockProcessingArtifact::default(); + self.chain.check_blocks_with_missing_chunks( + &me.map(|x| x.clone()), + &mut blocks_processing_artifacts, + apply_chunks_done_callback, + ); + self.process_block_processing_artifact(blocks_processing_artifacts); + } + + pub fn is_validator(&self, epoch_id: &EpochId, block_hash: &CryptoHash) -> bool { + match self.validator_signer.as_ref() { + None => false, + Some(signer) => { + let account_id = signer.validator_id(); + match self + .epoch_manager + .get_validator_by_account_id(epoch_id, block_hash, account_id) + { + Ok((validator_stake, is_slashed)) => { + !is_slashed && validator_stake.take_public_key() == signer.public_key() + } + Err(_) => false, + } + } + } + } + + fn handle_process_approval_error( + &mut self, + approval: &Approval, + approval_type: ApprovalType, + check_validator: bool, + error: unc_chain::Error, + ) { + let is_validator = + |epoch_id, block_hash, account_id, epoch_manager: &dyn EpochManagerAdapter| { + match epoch_manager.get_validator_by_account_id(epoch_id, block_hash, account_id) { + Ok((_, is_slashed)) => !is_slashed, + Err(_) => false, + } + }; + if let unc_chain::Error::DBNotFoundErr(_) = error { + if check_validator { + let head = unwrap_or_return!(self.chain.head()); + if !is_validator( + &head.epoch_id, + &head.last_block_hash, + &approval.account_id, + self.epoch_manager.as_ref(), + ) && !is_validator( + &head.next_epoch_id, + &head.last_block_hash, + &approval.account_id, + self.epoch_manager.as_ref(), + ) { + return; + } + } + let mut entry = + self.pending_approvals.pop(&approval.inner).unwrap_or_else(|| HashMap::new()); + entry.insert(approval.account_id.clone(), (approval.clone(), approval_type)); + self.pending_approvals.put(approval.inner.clone(), entry); + } + } + + /// Collects block approvals. + /// + /// We send the approval to doomslug given the epoch of the current tip iff: + /// 1. We are the block producer for the target height in the tip's epoch; + /// 2. The signature matches that of the account; + /// If we are not the block producer, but we also don't know the previous block, we add the + /// approval to `pending_approvals`, since it could be that the approval is from the next epoch. + /// + /// # Arguments + /// * `approval` - the approval to be collected + /// * `approval_type` - whether the approval was just produced by us (in which case skip validation, + /// only check whether we are the next block producer and store in Doomslug) + pub fn collect_block_approval(&mut self, approval: &Approval, approval_type: ApprovalType) { + let Approval { inner, account_id, target_height, signature } = approval; + + let parent_hash = match inner { + ApprovalInner::Endorsement(parent_hash) => *parent_hash, + ApprovalInner::Skip(parent_height) => { + match self.chain.chain_store().get_all_block_hashes_by_height(*parent_height) { + Ok(hashes) => { + // If there is more than one block at the height, all of them will be + // eligible to build the next block on, so we just pick one. + let hash = hashes.values().flatten().next(); + match hash { + Some(hash) => *hash, + None => { + self.handle_process_approval_error( + approval, + approval_type, + true, + unc_chain::Error::DBNotFoundErr(format!( + "Cannot find any block on height {}", + parent_height + )), + ); + return; + } + } + } + Err(e) => { + self.handle_process_approval_error(approval, approval_type, true, e); + return; + } + } + } + }; + + let next_block_epoch_id = + match self.epoch_manager.get_epoch_id_from_prev_block(&parent_hash) { + Err(e) => { + self.handle_process_approval_error(approval, approval_type, true, e.into()); + return; + } + Ok(next_epoch_id) => next_epoch_id, + }; + + if let ApprovalType::PeerApproval(_) = approval_type { + // Check signature is correct for given validator. + // Note that on the epoch boundary the blocks contain approvals from both the current + // and the next epoch. Here we try to fetch the validator for the epoch of the next block, + // if we succeed, it must use the key from that epoch, and thus we use the epoch of the + // next block below when verifying the signature. Otherwise, if the block producer doesn't + // exist in the epoch of the next block, we use the epoch after next to validate the + // signature. We don't care here if the block is actually on the epochs boundary yet, + // `Doomslug::on_approval_message` below will handle it. + let validator_epoch_id = match self.epoch_manager.get_validator_by_account_id( + &next_block_epoch_id, + &parent_hash, + account_id, + ) { + Ok(_) => next_block_epoch_id.clone(), + Err(EpochError::NotAValidator(_, _)) => { + match self.epoch_manager.get_next_epoch_id_from_prev_block(&parent_hash) { + Ok(next_block_next_epoch_id) => next_block_next_epoch_id, + Err(_) => return, + } + } + _ => return, + }; + match self.epoch_manager.verify_validator_signature( + &validator_epoch_id, + &parent_hash, + account_id, + Approval::get_data_for_sig(inner, *target_height).as_ref(), + signature, + ) { + Ok(true) => {} + _ => return, + } + } + + let is_block_producer = + match self.epoch_manager.get_block_producer(&next_block_epoch_id, *target_height) { + Err(_) => false, + Ok(target_block_producer) => { + Some(&target_block_producer) + == self.validator_signer.as_ref().map(|x| x.validator_id()) + } + }; + + if !is_block_producer { + match self.chain.get_block_header(&parent_hash) { + Ok(_) => { + // If we know the header, then either the parent_hash is the tip, and we are + // not the block producer for the corresponding height on top of the tip, or + // the parent_hash is not the tip, and then we will never build on top of it. + // Either way, this approval is of no use for us. + return; + } + Err(e) => { + self.handle_process_approval_error(approval, approval_type, false, e); + return; + } + }; + } + + let block_producer_stakes = + match self.epoch_manager.get_epoch_block_approvers_ordered(&parent_hash) { + Ok(block_producer_stakes) => block_producer_stakes, + Err(err) => { + error!(target: "client", ?err, "Block approval error"); + return; + } + }; + self.doomslug.on_approval_message(StaticClock::instant(), approval, &block_producer_stakes); + } + + /// Forwards given transaction to upcoming validators. + fn forward_tx(&self, epoch_id: &EpochId, tx: &SignedTransaction) -> Result<(), Error> { + let shard_id = + self.epoch_manager.account_id_to_shard_id(&tx.transaction.signer_id, epoch_id)?; + // Use the header head to make sure the list of validators is as + // up-to-date as possible. + let head = self.chain.header_head()?; + let maybe_next_epoch_id = self.get_next_epoch_id_if_at_boundary(&head)?; + + let mut validators = HashSet::new(); + for horizon in (2..=self.config.tx_routing_height_horizon) + .chain(vec![self.config.tx_routing_height_horizon * 2].into_iter()) + { + let target_height = head.height + horizon - 1; + let validator = + self.epoch_manager.get_chunk_producer(epoch_id, target_height, shard_id)?; + validators.insert(validator); + if let Some(next_epoch_id) = &maybe_next_epoch_id { + let next_shard_id = self + .epoch_manager + .account_id_to_shard_id(&tx.transaction.signer_id, next_epoch_id)?; + let validator = self.epoch_manager.get_chunk_producer( + next_epoch_id, + target_height, + next_shard_id, + )?; + validators.insert(validator); + } + } + + if let Some(account_id) = self.validator_signer.as_ref().map(|bp| bp.validator_id()) { + validators.remove(account_id); + } + for validator in validators { + trace!(target: "client", me = ?self.validator_signer.as_ref().map(|bp| bp.validator_id()), ?tx, ?validator, shard_id, "Routing a transaction"); + + // Send message to network to actually forward transaction. + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::ForwardTx(validator, tx.clone()), + )); + } + + Ok(()) + } + + /// Submits the transaction for future inclusion into the chain. + /// + /// If accepted, it will be added to the transaction pool and possibly forwarded to another + /// validator. + #[must_use] + pub fn process_tx( + &mut self, + tx: SignedTransaction, + is_forwarded: bool, + check_only: bool, + ) -> ProcessTxResponse { + unwrap_or_return!(self.process_tx_internal(&tx, is_forwarded, check_only), { + let me = self.validator_signer.as_ref().map(|vs| vs.validator_id()); + warn!(target: "client", ?me, ?tx, "Dropping tx"); + ProcessTxResponse::NoResponse + }) + } + + /// If we are close to epoch boundary, return next epoch id, otherwise return None. + fn get_next_epoch_id_if_at_boundary(&self, head: &Tip) -> Result, Error> { + let next_epoch_started = + self.epoch_manager.is_next_block_epoch_start(&head.last_block_hash)?; + if next_epoch_started { + return Ok(None); + } + let next_epoch_estimated_height = + self.epoch_manager.get_epoch_start_height(&head.last_block_hash)? + + self.config.epoch_length; + + let epoch_boundary_possible = + head.height + self.config.tx_routing_height_horizon >= next_epoch_estimated_height; + if epoch_boundary_possible { + Ok(Some(self.epoch_manager.get_next_epoch_id_from_prev_block(&head.last_block_hash)?)) + } else { + Ok(None) + } + } + + /// If we're a validator in one of the next few chunks, but epoch switch could happen soon, + /// we forward to a validator from next epoch. + fn possibly_forward_tx_to_next_epoch(&mut self, tx: &SignedTransaction) -> Result<(), Error> { + let head = self.chain.head()?; + if let Some(next_epoch_id) = self.get_next_epoch_id_if_at_boundary(&head)? { + self.forward_tx(&next_epoch_id, tx)?; + } else { + self.forward_tx(&head.epoch_id, tx)?; + } + Ok(()) + } + + /// Process transaction and either add it to the mempool or return to redirect to another validator. + fn process_tx_internal( + &mut self, + tx: &SignedTransaction, + is_forwarded: bool, + check_only: bool, + ) -> Result { + let head = self.chain.head()?; + let me = self.validator_signer.as_ref().map(|vs| vs.validator_id()); + let cur_block_header = self.chain.head_header()?; + let transaction_validity_period = self.chain.transaction_validity_period; + // here it is fine to use `cur_block_header` as it is a best effort estimate. If the transaction + // were to be included, the block that the chunk points to will have height >= height of + // `cur_block_header`. + if let Err(e) = self.chain.chain_store().check_transaction_validity_period( + &cur_block_header, + &tx.transaction.block_hash, + transaction_validity_period, + ) { + debug!(target: "client", ?tx, "Invalid tx: expired or from a different fork"); + return Ok(ProcessTxResponse::InvalidTx(e)); + } + let gas_price = cur_block_header.next_gas_price(); + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(&head.last_block_hash)?; + + let protocol_version = self.epoch_manager.get_epoch_protocol_version(&epoch_id)?; + + if let Some(err) = self + .runtime_adapter + .validate_tx(gas_price, None, tx, true, &epoch_id, protocol_version) + .expect("no storage errors") + { + debug!(target: "client", tx_hash = ?tx.get_hash(), ?err, "Invalid tx during basic validation"); + return Ok(ProcessTxResponse::InvalidTx(err)); + } + + let shard_id = + self.epoch_manager.account_id_to_shard_id(&tx.transaction.signer_id, &epoch_id)?; + let care_about_shard = + self.shard_tracker.care_about_shard(me, &head.last_block_hash, shard_id, true); + let will_care_about_shard = + self.shard_tracker.will_care_about_shard(me, &head.last_block_hash, shard_id, true); + // TODO(resharding) will_care_about_shard should be called with the + // account shard id from the next epoch, in case shard layout changes + if care_about_shard || will_care_about_shard { + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, &epoch_id)?; + let state_root = match self.chain.get_chunk_extra(&head.last_block_hash, &shard_uid) { + Ok(chunk_extra) => *chunk_extra.state_root(), + Err(_) => { + // Not being able to fetch a state root most likely implies that we haven't + // caught up with the next epoch yet. + if is_forwarded { + return Err(Error::Other("Node has not caught up yet".to_string())); + } else { + self.forward_tx(&epoch_id, tx)?; + return Ok(ProcessTxResponse::RequestRouted); + } + } + }; + if let Some(err) = self + .runtime_adapter + .validate_tx(gas_price, Some(state_root), tx, false, &epoch_id, protocol_version) + .expect("no storage errors") + { + debug!(target: "client", ?err, "Invalid tx"); + Ok(ProcessTxResponse::InvalidTx(err)) + } else if check_only { + Ok(ProcessTxResponse::ValidTx) + } else { + // Transactions only need to be recorded if the node is a validator. + if me.is_some() { + match self.sharded_tx_pool.insert_transaction(shard_uid, tx.clone()) { + InsertTransactionResult::Success => { + trace!(target: "client", ?shard_uid, tx_hash = ?tx.get_hash(), "Recorded a transaction."); + } + InsertTransactionResult::Duplicate => { + trace!(target: "client", ?shard_uid, tx_hash = ?tx.get_hash(), "Duplicate transaction, not forwarding it."); + return Ok(ProcessTxResponse::ValidTx); + } + InsertTransactionResult::NoSpaceLeft => { + if is_forwarded { + trace!(target: "client", ?shard_uid, tx_hash = ?tx.get_hash(), "Transaction pool is full, dropping the transaction."); + } else { + trace!(target: "client", ?shard_uid, tx_hash = ?tx.get_hash(), "Transaction pool is full, trying to forward the transaction."); + } + } + } + } + + // Active validator: + // possibly forward to next epoch validators + // Not active validator: + // forward to current epoch validators, + // possibly forward to next epoch validators + if self.active_validator(shard_id)? { + trace!(target: "client", account = ?me, shard_id, tx_hash = ?tx.get_hash(), is_forwarded, "Recording a transaction."); + metrics::TRANSACTION_RECEIVED_VALIDATOR.inc(); + + if !is_forwarded { + self.possibly_forward_tx_to_next_epoch(tx)?; + } + Ok(ProcessTxResponse::ValidTx) + } else if !is_forwarded { + trace!(target: "client", shard_id, tx_hash = ?tx.get_hash(), "Forwarding a transaction."); + metrics::TRANSACTION_RECEIVED_NON_VALIDATOR.inc(); + self.forward_tx(&epoch_id, tx)?; + Ok(ProcessTxResponse::RequestRouted) + } else { + trace!(target: "client", shard_id, tx_hash = ?tx.get_hash(), "Non-validator received a forwarded transaction, dropping it."); + metrics::TRANSACTION_RECEIVED_NON_VALIDATOR_FORWARDED.inc(); + Ok(ProcessTxResponse::NoResponse) + } + } + } else if check_only { + Ok(ProcessTxResponse::DoesNotTrackShard) + } else if is_forwarded { + // Received forwarded transaction but we are not tracking the shard + debug!(target: "client", ?me, shard_id, tx_hash = ?tx.get_hash(), "Received forwarded transaction but no tracking shard"); + Ok(ProcessTxResponse::NoResponse) + } else { + // We are not tracking this shard, so there is no way to validate this tx. Just rerouting. + self.forward_tx(&epoch_id, tx)?; + Ok(ProcessTxResponse::RequestRouted) + } + } + + /// Determine if I am a validator in next few blocks for specified shard, assuming epoch doesn't change. + fn active_validator(&self, shard_id: ShardId) -> Result { + let head = self.chain.head()?; + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(&head.last_block_hash)?; + + let account_id = if let Some(vs) = self.validator_signer.as_ref() { + vs.validator_id() + } else { + return Ok(false); + }; + + for i in 1..=self.config.tx_routing_height_horizon { + let chunk_producer = + self.epoch_manager.get_chunk_producer(&epoch_id, head.height + i, shard_id)?; + if &chunk_producer == account_id { + return Ok(true); + } + } + Ok(false) + } + + /// Walks through all the ongoing state syncs for future epochs and processes them + pub fn run_catchup( + &mut self, + highest_height_peers: &[HighestHeightPeerInfo], + state_parts_task_scheduler: &dyn Fn(ApplyStatePartsRequest), + block_catch_up_task_scheduler: &dyn Fn(BlockCatchUpRequest), + resharding_scheduler: &dyn Fn(ReshardingRequest), + apply_chunks_done_callback: DoneApplyChunkCallback, + state_parts_arbiter_handle: &ArbiterHandle, + ) -> Result<(), Error> { + let _span = debug_span!(target: "sync", "run_catchup").entered(); + let mut notify_state_sync = false; + let me = &self.validator_signer.as_ref().map(|x| x.validator_id().clone()); + for (sync_hash, state_sync_info) in self.chain.chain_store().iterate_state_sync_infos()? { + assert_eq!(sync_hash, state_sync_info.epoch_tail_hash); + let network_adapter = self.network_adapter.clone(); + + let shards_to_split = self.get_shards_to_split(sync_hash, &state_sync_info, me)?; + let state_sync_timeout = self.config.state_sync_timeout; + let epoch_id = self.chain.get_block(&sync_hash)?.header().epoch_id().clone(); + + let (state_sync, shards_to_split, blocks_catch_up_state) = + self.catchup_state_syncs.entry(sync_hash).or_insert_with(|| { + tracing::debug!(target: "client", ?sync_hash, "inserting new state sync"); + notify_state_sync = true; + ( + StateSync::new( + network_adapter, + state_sync_timeout, + &self.config.chain_id, + &self.config.state_sync.sync, + true, + ), + shards_to_split, + BlocksCatchUpState::new(sync_hash, epoch_id.clone()), + ) + }); + + // For colour decorators to work, they need to printed directly. Otherwise the decorators get escaped, garble output and don't add colours. + debug!(target: "catchup", ?me, ?sync_hash, progress_per_shard = ?format_shard_sync_phase_per_shard(&shards_to_split, false), "Catchup"); + let use_colour = matches!(self.config.log_summary_style, LogSummaryStyle::Colored); + + let tracking_shards: Vec = + state_sync_info.shards.iter().map(|tuple| tuple.0).collect(); + // Notify each shard to sync. + if notify_state_sync { + let shard_layout = self + .epoch_manager + .get_shard_layout(&epoch_id) + .expect("Cannot get shard layout"); + for &shard_id in &tracking_shards { + let shard_uid = ShardUId::from_shard_id_and_layout(shard_id, &shard_layout); + match self.state_sync_adapter.clone().read() { + Ok(sync_adapter) => sync_adapter.send( + shard_uid, + (SyncMessage::StartSync(SyncShardInfo { shard_uid, sync_hash })) + .with_span_context(), + ), + Err(_) => { + error!(target:"catchup", "State sync adapter lock is poisoned.") + } + } + } + } + + // Initialize the new shard sync to contain the shards to split at + // first. It will get updated with the shard sync download status + // for other shards later. + let new_shard_sync = shards_to_split; + match state_sync.run( + me, + sync_hash, + new_shard_sync, + &mut self.chain, + self.epoch_manager.as_ref(), + highest_height_peers, + tracking_shards, + state_parts_task_scheduler, + resharding_scheduler, + state_parts_arbiter_handle, + use_colour, + self.runtime_adapter.clone(), + )? { + StateSyncResult::InProgress => {} + StateSyncResult::Completed => { + debug!(target: "catchup", "state sync completed now catch up blocks"); + self.chain.catchup_blocks_step( + me, + &sync_hash, + blocks_catch_up_state, + block_catch_up_task_scheduler, + )?; + + if blocks_catch_up_state.is_finished() { + let mut block_processing_artifacts = BlockProcessingArtifact::default(); + + self.chain.finish_catchup_blocks( + me, + &sync_hash, + &mut block_processing_artifacts, + apply_chunks_done_callback.clone(), + &blocks_catch_up_state.done_blocks, + )?; + + self.process_block_processing_artifact(block_processing_artifacts); + } + } + } + } + + Ok(()) + } + + /// This method checks which of the shards requested for state sync are already present. + /// Any shard that is currently tracked needs not to be downloaded again. + /// + /// The hidden logic here is that shards that are marked for state sync but + /// are currently tracked are actually marked for splitting. Please see the + /// comment on [`Chain::get_shards_to_state_sync`] for further explanation. + /// + /// Returns a map from the shard_id to ShardSyncDownload only for those + /// shards that need to be split. + fn get_shards_to_split( + &mut self, + sync_hash: CryptoHash, + state_sync_info: &StateSyncInfo, + me: &Option, + ) -> Result, Error> { + let prev_hash = *self.chain.get_block(&sync_hash)?.header().prev_hash(); + let need_to_reshard = self.epoch_manager.will_shard_layout_change(&prev_hash)?; + + if !need_to_reshard { + debug!(target: "catchup", "do not need to reshard"); + return Ok(HashMap::new()); + } + + // If the client already has the state for this epoch, skip the downloading phase + let shards_to_split = state_sync_info + .shards + .iter() + .filter_map(|ShardInfo(shard_id, _)| self.should_split_shard(shard_id, me, prev_hash)) + .collect(); + Ok(shards_to_split) + } + + /// Shard should be split if state sync was requested for it but we already + /// track it. + fn should_split_shard( + &mut self, + shard_id: &u64, + me: &Option, + prev_hash: CryptoHash, + ) -> Option<(u64, ShardSyncDownload)> { + let shard_id = *shard_id; + if self.shard_tracker.care_about_shard(me.as_ref(), &prev_hash, shard_id, true) { + let shard_sync_download = ShardSyncDownload { + downloads: vec![], + status: ShardSyncStatus::ReshardingScheduling, + }; + Some((shard_id, shard_sync_download)) + } else { + None + } + } + + /// When accepting challenge, we verify that it's valid given signature with current validators. + pub fn process_challenge(&mut self, _challenge: Challenge) -> Result<(), Error> { + // TODO(2445): Enable challenges when they are working correctly. + // if self.challenges.contains_key(&challenge.hash) { + // return Ok(()); + // } + // debug!(target: "client", "Received challenge: {:?}", challenge); + // let head = self.chain.head()?; + // if self.runtime_adapter.verify_validator_or_fisherman_signature( + // &head.epoch_id, + // &head.prev_block_hash, + // &challenge.account_id, + // challenge.hash.as_ref(), + // &challenge.signature, + // )? { + // // If challenge is not double sign, we should process it right away to invalidate the chain. + // match challenge.body { + // ChallengeBody::BlockDoubleSign(_) => {} + // _ => { + // self.chain.process_challenge(&challenge); + // } + // } + // self.challenges.insert(challenge.hash, challenge); + // } + Ok(()) + } + + /// Check updates from background flat storage creation processes and possibly update + /// creation statuses. Returns boolean indicating if all flat storages are created or + /// creation is not needed. + pub fn run_flat_storage_creation_step(&mut self) -> Result { + let result = match &mut self.flat_storage_creator { + Some(flat_storage_creator) => { + flat_storage_creator.update_status(self.chain.chain_store())? + } + None => true, + }; + Ok(result) + } + + fn clear_data(&mut self) -> Result<(), unc_chain::Error> { + // A RPC node should do regular garbage collection. + if !self.config.archive { + let tries = self.runtime_adapter.get_tries(); + return self.chain.clear_data(tries, &self.config.gc); + } + + // An archival node with split storage should perform garbage collection + // on the hot storage. In order to determine if split storage is enabled + // *and* that the migration to split storage is finished we can check + // the store kind. It's only set to hot after the migration is finished. + let store = self.chain.chain_store().store(); + let kind = store.get_db_kind()?; + if kind == Some(DbKind::Hot) { + let tries = self.runtime_adapter.get_tries(); + return self.chain.clear_data(tries, &self.config.gc); + } + + // An archival node with legacy storage or in the midst of migration to split + // storage should do the legacy clear_archive_data. + self.chain.clear_archive_data(self.config.gc.gc_blocks_limit) + } +} + +/* implements functions used to communicate with network */ +impl Client { + pub fn request_block(&self, hash: CryptoHash, peer_id: PeerId) { + let _span = debug_span!(target: "client", "request_block", ?hash, ?peer_id).entered(); + match self.chain.block_exists(&hash) { + Ok(false) => { + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::BlockRequest { hash, peer_id }, + )); + } + Ok(true) => { + debug!(target: "client", ?hash, "send_block_request_to_peer: block already known") + } + Err(err) => { + error!(target: "client", ?err, "send_block_request_to_peer: failed to check block exists") + } + } + } + + pub fn ban_peer(&self, peer_id: PeerId, ban_reason: ReasonForBan) { + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::BanPeer { peer_id, ban_reason }, + )); + } +} + +impl Client { + /// Each epoch defines a set of important accounts: block producers, chunk producers, + /// approvers. Low-latency reliable communication between those accounts is critical, + /// so that the blocks can be produced on time. This function computes the set of + /// important accounts (aka TIER1 accounts) so that it can be fed to PeerManager, which + /// will take care of the traffic prioritization. + /// + /// It returns both TIER1 accounts for both current epoch (according to the `tip`) + /// and the next epoch, so that the PeerManager can establish the priority connections + /// in advance (before the epoch starts and they are actually needed). + /// + /// The result of the last call to get_tier1_accounts() is cached, so that it is not recomputed + /// if the current epoch didn't change since the last call. In particular SetChainInfo is being + /// send after processing each block (order of seconds), while the epoch changes way less + /// frequently (order of hours). + fn get_tier1_accounts(&mut self, tip: &Tip) -> Result, Error> { + match &self.tier1_accounts_cache { + Some(it) if it.0 == tip.epoch_id => return Ok(it.1.clone()), + _ => {} + } + + let _guard = + tracing::debug_span!(target: "client", "get_tier1_accounts(): recomputing").entered(); + + // What we really need are: chunk producers, block producers and block approvers for + // this epoch and the beginnig of the next epoch (so that all required connections are + // established in advance). Note that block producers and block approvers are not + // exactly the same - last blocks of this epoch will also need to be signed by the + // block producers of the next epoch. On the other hand, block approvers + // of the next epoch will also include block producers of the N+2 epoch (which we + // definitely don't need to connect to right now). Still, as long as there is no big churn + // in the set of block producers, it doesn't make much difference. + // + // With the current implementation we just fetch chunk producers and block producers + // of this and the next epoch (which covers what we need, as described above), but may + // require some tuning in the future. In particular, if we decide that connecting to + // block & chunk producers of the next expoch is too expensive, we can postpone it + // till almost the end of this epoch. + let mut account_keys = AccountKeys::new(); + for epoch_id in [&tip.epoch_id, &tip.next_epoch_id] { + // We assume here that calls to get_epoch_chunk_producers and get_epoch_block_producers_ordered + // are cheaper than block processing (and that they will work with both this and + // the next epoch). The caching on top of that (in tier1_accounts_cache field) is just + // a defence in depth, based on the previous experience with expensive + // EpochManagerAdapter::get_validators_info call. + for cp in self.epoch_manager.get_epoch_chunk_producers(epoch_id)? { + account_keys + .entry(cp.account_id().clone()) + .or_default() + .insert(cp.public_key().clone()); + } + for (bp, _) in self + .epoch_manager + .get_epoch_block_producers_ordered(epoch_id, &tip.last_block_hash)? + { + account_keys + .entry(bp.account_id().clone()) + .or_default() + .insert(bp.public_key().clone()); + } + } + let account_keys = Arc::new(account_keys); + self.tier1_accounts_cache = Some((tip.epoch_id.clone(), account_keys.clone())); + Ok(account_keys) + } + + /// send_network_chain_info sends ChainInfo to PeerManagerActor. + /// ChainInfo contains chain information relevant to p2p networking. + /// It is expected to be called every time the head of the chain changes (or more often). + /// Subsequent calls will probably re-send to PeerManagerActor a lot of redundant + /// information (for example epoch-related data changes way less often than chain head + /// changes), but that's fine - we avoid recomputing rarely changing data in ChainInfo by caching it. + /// The condition to call this function is simple - every time chain head changes - + /// which hopefully will make it hard to forget to call it. And even if there is some + /// corner case not covered - since blocks are sent frequently (every few seconds), + /// the POV of Client and PeerManagerActor will be desynchronized only for a short time. + /// + /// TODO(gprusak): consider making send_network_chain_info accept chain Tip as an argument + /// to underline that it is expected to be called whenever Tip changes. Currently + /// self.chain.head() is fallible for some reason, so calling it at the + /// send_network_chain_info() call site would be ugly (we just log the error). + /// In theory we should already have the tip at the call-site, eg from + /// check_And_update_doomslug_tip, but that would require a bigger refactor. + pub(crate) fn send_network_chain_info(&mut self) -> Result<(), Error> { + let tip = self.chain.head()?; + // convert config tracked shards + // runtime will track all shards if config tracked shards is not empty + // https://github.com/utnet-org/utility/issues/4930 + let tracked_shards = if self.config.tracked_shards.is_empty() { + vec![] + } else { + self.epoch_manager.shard_ids(&tip.epoch_id)? + }; + let tier1_accounts = self.get_tier1_accounts(&tip)?; + let block = self.chain.get_block(&tip.last_block_hash)?; + self.network_adapter.send(SetChainInfo(ChainInfo { + block, + tracked_shards, + tier1_accounts, + })); + Ok(()) + } +} + +impl Client { + pub fn get_catchup_status(&self) -> Result, unc_chain::Error> { + let mut ret = vec![]; + for (sync_hash, (_, shard_sync_state, block_catchup_state)) in + self.catchup_state_syncs.iter() + { + let sync_block_height = self.chain.get_block_header(sync_hash)?.height(); + let shard_sync_status: HashMap<_, _> = shard_sync_state + .iter() + .map(|(shard_id, state)| (*shard_id, state.status.to_string())) + .collect(); + ret.push(CatchupStatusView { + sync_block_hash: *sync_hash, + sync_block_height, + shard_sync_status, + blocks_to_catchup: self.chain.get_block_catchup_status(block_catchup_state), + }); + } + Ok(ret) + } +} + +impl Drop for Client { + fn drop(&mut self) { + // State sync is tied to the client logic. When the client goes out of scope or it is restarted, + // the running sync actors should also stop. + self.state_sync_adapter + .to_owned() + .write() + .expect("Cannot acquire write lock on sync adapter. Lock poisoned.") + .stop_all(); + } +} diff --git a/chain/client/src/client_actor.rs b/chain/client/src/client_actor.rs new file mode 100644 index 000000000..0debe3862 --- /dev/null +++ b/chain/client/src/client_actor.rs @@ -0,0 +1,2089 @@ +//! Client actor orchestrates Client and facilitates network connection. +//! It should just serve as a coordinator class to handle messages and check triggers but immediately +//! pass the control to Client. This means, any real block processing or production logic should +//! be put in Client. +//! Unfortunately, this is not the case today. We are in the process of refactoring ClientActor +//! https://github.com/utnet-org/utility/issues/7899 + +use crate::adapter::{ + BlockApproval, BlockHeadersResponse, BlockResponse, ChunkEndorsementMessage, + ChunkStateWitnessMessage, ProcessTxRequest, ProcessTxResponse, RecvChallenge, SetNetworkInfo, + StateResponse, +}; +#[cfg(feature = "test_features")] +use crate::client::AdvProduceBlocksMode; +use crate::client::{Client, EPOCH_START_INFO_BLOCKS}; +use crate::config_updater::ConfigUpdater; +use crate::debug::new_network_info_view; +use crate::info::{display_sync_status, InfoHelper}; +use crate::sync::adapter::{SyncMessage, SyncShardInfo}; +use crate::sync::state::{StateSync, StateSyncResult}; +use crate::sync_jobs_actor::{create_sync_job_scheduler, SyncJobsActor}; +use crate::{metrics, StatusResponse, SyncAdapter}; +use actix::{Actor, Addr, Arbiter, AsyncContext, Context, Handler}; +use actix_rt::ArbiterHandle; +use chrono::{DateTime, Utc}; +use itertools::Itertools; +use unc_async::messaging::{CanSend, Sender}; +use unc_chain::chain::{ + ApplyStatePartsRequest, ApplyStatePartsResponse, BlockCatchUpRequest, BlockCatchUpResponse, +}; +use unc_chain::resharding::{ReshardingRequest, ReshardingResponse}; +use unc_chain::state_snapshot_actor::SnapshotCallbacks; +use unc_chain::test_utils::format_hash; +use unc_chain::types::RuntimeAdapter; +#[cfg(feature = "test_features")] +use unc_chain::ChainStoreAccess; +use unc_chain::{ + byzantine_assert, unc_chain_primitives, Block, BlockHeader, BlockProcessingArtifact, + ChainGenesis, DoneApplyChunkCallback, Provenance, +}; +use unc_chain_configs::{ClientConfig, LogSummaryStyle, ReshardingHandle}; +use unc_chain_primitives::error::EpochErrorResultToChainError; +use unc_chunks::adapter::ShardsManagerRequestFromClient; +use unc_chunks::client::ShardsManagerResponse; +use unc_chunks::logic::cares_about_shard_this_or_next_epoch; +use unc_client_primitives::types::{ + Error, GetClientConfig, GetClientConfigError, GetNetworkInfo, NetworkInfoResponse, + StateSyncStatus, Status, StatusError, StatusSyncInfo, SyncStatus, +}; +use unc_epoch_manager::shard_tracker::ShardTracker; +use unc_epoch_manager::EpochManagerAdapter; +use unc_network::types::ReasonForBan; +use unc_network::types::{ + NetworkInfo, NetworkRequests, PeerManagerAdapter, PeerManagerMessageRequest, +}; +use unc_o11y::{handler_debug_span, OpenTelemetrySpanExt, WithSpanContext, WithSpanContextExt}; +use unc_performance_metrics; +use unc_performance_metrics_macros::perf; +use unc_primitives::block::Tip; +use unc_primitives::block_header::ApprovalType; +use unc_primitives::epoch_manager::RngSeed; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::types::BlockHeight; +use unc_primitives::unwrap_or_return; +use unc_primitives::utils::{from_timestamp, MaybeValidated}; +use unc_primitives::validator_signer::ValidatorSigner; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_primitives::views::{DetailedDebugStatus, ValidatorInfo}; +#[cfg(feature = "test_features")] +use unc_store::DBCol; +use unc_store::ShardUId; +use unc_telemetry::TelemetryActor; +use rand::seq::SliceRandom; +use rand::{thread_rng, Rng}; +use std::collections::HashMap; +use std::fmt; +use std::sync::{Arc, RwLock}; +use std::thread; +use std::time::{Duration, Instant}; +use tokio::sync::broadcast; +use tracing::{debug, debug_span, error, info, trace, warn}; + +/// Multiplier on `max_block_time` to wait until deciding that chain stalled. +const STATUS_WAIT_TIME_MULTIPLIER: u64 = 10; +/// `max_block_production_time` times this multiplier is how long we wait before rebroadcasting +/// the current `head` +const HEAD_STALL_MULTIPLIER: u32 = 4; + +pub struct ClientActor { + /// Adversarial controls + pub adv: crate::adversarial::Controls, + + // Address of this ClientActor. Can be used to send messages to self. + my_address: Addr, + pub(crate) client: Client, + network_adapter: PeerManagerAdapter, + network_info: NetworkInfo, + /// Identity that represents this Client at the network level. + /// It is used as part of the messages that identify this client. + node_id: PeerId, + /// Last time we announced our accounts as validators. + last_validator_announce_time: Option, + /// Info helper. + info_helper: InfoHelper, + + /// Last time handle_block_production method was called + block_production_next_attempt: DateTime, + + // Last time when log_summary method was called. + log_summary_timer_next_attempt: DateTime, + + block_production_started: bool, + doomslug_timer_next_attempt: DateTime, + sync_timer_next_attempt: DateTime, + sync_started: bool, + state_parts_task_scheduler: Box, + block_catch_up_scheduler: Box, + resharding_scheduler: Box, + state_parts_client_arbiter: Arbiter, + + #[cfg(feature = "sandbox")] + fastforward_delta: unc_primitives::types::BlockHeightDelta, + + /// Synchronization measure to allow graceful shutdown. + /// Informs the system when a ClientActor gets dropped. + shutdown_signal: Option>, + + /// Manages updating the config. + config_updater: Option, +} + +/// Blocks the program until given genesis time arrives. +fn wait_until_genesis(genesis_time: &DateTime) { + loop { + // Get chrono::Duration::num_seconds() by deducting genesis_time from now. + let duration = genesis_time.signed_duration_since(StaticClock::utc()); + let chrono_seconds = duration.num_seconds(); + // Check if number of seconds in chrono::Duration larger than zero. + if chrono_seconds <= 0 { + break; + } + info!(target: "near", "Waiting until genesis: {}d {}h {}m {}s", duration.num_days(), + (duration.num_hours() % 24), + (duration.num_minutes() % 60), + (duration.num_seconds() % 60)); + let wait = + std::cmp::min(Duration::from_secs(10), Duration::from_secs(chrono_seconds as u64)); + thread::sleep(wait); + } +} + +impl ClientActor { + pub fn new( + client: Client, + address: Addr, + config: ClientConfig, + node_id: PeerId, + network_adapter: PeerManagerAdapter, + validator_signer: Option>, + telemetry_actor: Addr, + ctx: &Context, + shutdown_signal: Option>, + adv: crate::adversarial::Controls, + config_updater: Option, + ) -> Result { + let state_parts_arbiter = Arbiter::new(); + let self_addr = ctx.address(); + let self_addr_clone = self_addr; + let sync_jobs_actor_addr = SyncJobsActor::start_in_arbiter( + &state_parts_arbiter.handle(), + move |ctx: &mut Context| -> SyncJobsActor { + ctx.set_mailbox_capacity(SyncJobsActor::MAILBOX_CAPACITY); + SyncJobsActor { client_addr: self_addr_clone } + }, + ); + if let Some(vs) = &validator_signer { + info!(target: "client", "Starting validator node: {}", vs.validator_id()); + } + let info_helper = InfoHelper::new(Some(telemetry_actor), &config, validator_signer.clone()); + + let now = Utc::now(); + Ok(ClientActor { + adv, + my_address: address, + client, + network_adapter, + node_id, + network_info: NetworkInfo { + connected_peers: vec![], + tier1_connections: vec![], + num_connected_peers: 0, + peer_max_count: 0, + highest_height_peers: vec![], + received_bytes_per_sec: 0, + sent_bytes_per_sec: 0, + known_producers: vec![], + tier1_accounts_keys: vec![], + tier1_accounts_data: vec![], + }, + last_validator_announce_time: None, + info_helper, + block_production_next_attempt: now, + log_summary_timer_next_attempt: now, + block_production_started: false, + doomslug_timer_next_attempt: now, + sync_timer_next_attempt: now, + sync_started: false, + state_parts_task_scheduler: create_sync_job_scheduler::( + sync_jobs_actor_addr.clone(), + ), + block_catch_up_scheduler: create_sync_job_scheduler::( + sync_jobs_actor_addr.clone(), + ), + resharding_scheduler: create_sync_job_scheduler::( + sync_jobs_actor_addr, + ), + state_parts_client_arbiter: state_parts_arbiter, + + #[cfg(feature = "sandbox")] + fastforward_delta: 0, + shutdown_signal, + config_updater, + }) + } +} + +impl Actor for ClientActor { + type Context = Context; + + fn started(&mut self, ctx: &mut Self::Context) { + self.start_flat_storage_creation(ctx); + + // Start syncing job. + self.start_sync(ctx); + + // Start block production tracking if have block producer info. + if self.client.validator_signer.is_some() { + self.block_production_started = true; + } + + // Start triggers + self.schedule_triggers(ctx); + + // Start catchup job. + self.catchup(ctx); + + if let Err(err) = self.client.send_network_chain_info() { + error!(target: "client", ?err, "Failed to update network chain info"); + } + } +} + +impl ClientActor { + /// Wrapper for processing actix message which must be called after receiving it. + /// + /// Due to a bug in Actix library, while there are messages in mailbox, Actix + /// will prioritize processing messages until mailbox is empty. In such case execution + /// of any other task scheduled with `run_later` will be delayed. At the same time, + /// we have several important functions which have to be called regularly, so we put + /// these calls into `check_triggers` and call it here as a quick hack. + fn wrap( + &mut self, + msg: WithSpanContext, + ctx: &mut Context, + msg_type: &str, + f: impl FnOnce(&mut Self, Req) -> Res, + ) -> Res { + let (_span, msg) = handler_debug_span!(target: "client", msg, msg_type); + self.check_triggers(ctx); + let _span_inner = tracing::debug_span!(target: "client", "NetworkClientMessage").entered(); + metrics::CLIENT_MESSAGES_COUNT.with_label_values(&[msg_type]).inc(); + let timer = + metrics::CLIENT_MESSAGES_PROCESSING_TIME.with_label_values(&[msg_type]).start_timer(); + let res = f(self, msg); + timer.observe_duration(); + res + } +} + +#[cfg(feature = "test_features")] +#[derive(actix::Message, Debug)] +#[rtype(result = "Option")] +pub enum NetworkAdversarialMessage { + AdvProduceBlocks(u64, bool), + AdvSwitchToHeight(u64), + AdvDisableHeaderSync, + AdvDisableDoomslug, + AdvGetSavedBlocks, + AdvCheckStorageConsistency, +} + +#[cfg(feature = "test_features")] +impl Handler> for ClientActor { + type Result = Option; + + fn handle( + &mut self, + msg: WithSpanContext, + ctx: &mut Context, + ) -> Self::Result { + self.wrap(msg, ctx, "NetworkAdversarialMessage", |this, msg| match msg { + NetworkAdversarialMessage::AdvDisableDoomslug => { + info!(target: "adversary", "Turning Doomslug off"); + this.adv.set_disable_doomslug(true); + this.client.doomslug.adv_disable(); + this.client.chain.adv_disable_doomslug(); + None + } + NetworkAdversarialMessage::AdvDisableHeaderSync => { + info!(target: "adversary", "Blocking header sync"); + this.adv.set_disable_header_sync(true); + None + } + NetworkAdversarialMessage::AdvProduceBlocks( + num_blocks, + only_valid, + ) => { + info!(target: "adversary", num_blocks, "Starting adversary blocks production"); + if only_valid { + this.client.adv_produce_blocks = Some(AdvProduceBlocksMode::OnlyValid); + } else { + this.client.adv_produce_blocks = Some(AdvProduceBlocksMode::All); + } + let start_height = + this.client.chain.mut_chain_store().get_latest_known().unwrap().height + 1; + let mut blocks_produced = 0; + for height in start_height.. { + let block = this + .client + .produce_block(height) + .expect("block should be produced"); + if only_valid && block == None { + continue; + } + let block = block.expect("block should exist after produced"); + info!(target: "adversary", blocks_produced, num_blocks, height, "Producing adversary block"); + this.network_adapter.send( + PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::Block { block: block.clone() }, + ) + ); + let _ = this.client.start_process_block( + block.into(), + Provenance::PRODUCED, + this.get_apply_chunks_done_callback(), + ); + blocks_produced += 1; + if blocks_produced == num_blocks { + break; + } + } + None + } + NetworkAdversarialMessage::AdvSwitchToHeight(height) => { + info!(target: "adversary", "Switching to height {:?}", height); + let mut chain_store_update = this.client.chain.mut_chain_store().store_update(); + chain_store_update.save_largest_target_height(height); + chain_store_update + .adv_save_latest_known(height) + .expect("adv method should not fail"); + chain_store_update.commit().expect("adv method should not fail"); + None + } + NetworkAdversarialMessage::AdvGetSavedBlocks => { + info!(target: "adversary", "Requested number of saved blocks"); + let store = this.client.chain.chain_store().store(); + let mut num_blocks = 0; + for _ in store.iter(DBCol::Block) { + num_blocks += 1; + } + Some(num_blocks) + } + NetworkAdversarialMessage::AdvCheckStorageConsistency => { + // timeout is set to 1.5 seconds to give some room as we wait in Nightly for 2 seconds + let timeout = 1500; + info!(target: "adversary", "Check Storage Consistency, timeout set to {:?} milliseconds", timeout); + let mut genesis = unc_chain_configs::GenesisConfig::default(); + genesis.genesis_height = this.client.chain.chain_store().get_genesis_height(); + let mut store_validator = unc_chain::store_validator::StoreValidator::new( + this.client.validator_signer.as_ref().map(|x| x.validator_id().clone()), + genesis, + this.client.epoch_manager.clone(), + this.client.shard_tracker.clone(), + this.client.runtime_adapter.clone(), + this.client.chain.chain_store().store().clone(), + this.adv.is_archival(), + ); + store_validator.set_timeout(timeout); + store_validator.validate(); + if store_validator.is_failed() { + error!(target: "client", "Storage Validation failed, {:?}", store_validator.errors); + Some(0) + } else { + Some(store_validator.tests_done()) + } + } + }) + } +} + +impl Handler> for ClientActor { + type Result = ProcessTxResponse; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + ctx: &mut Context, + ) -> Self::Result { + self.wrap(msg, ctx, "ProcessTxRequest", |this: &mut Self, msg| { + let ProcessTxRequest { transaction, is_forwarded, check_only } = msg; + this.client.process_tx(transaction, is_forwarded, check_only) + }) + } +} + +impl Handler> for ClientActor { + type Result = (); + + #[perf] + fn handle(&mut self, msg: WithSpanContext, ctx: &mut Context) { + self.wrap(msg, ctx, "BlockResponse", |this: &mut Self, msg|{ + let BlockResponse{ block, peer_id, was_requested } = msg; + info!(target: "client", block_height = block.header().height(), block_hash = ?block.header().hash(), "BlockResponse"); + let blocks_at_height = this + .client + .chain + .chain_store() + .get_all_block_hashes_by_height(block.header().height()); + if was_requested || blocks_at_height.is_err() || blocks_at_height.as_ref().unwrap().is_empty() { + // This is a very sneaky piece of logic. + if this.maybe_receive_state_sync_blocks(&block) { + // A node is syncing its state. Don't consider receiving + // blocks other than the few special ones that State Sync expects. + return; + } + this.client.receive_block( + block, + peer_id, + was_requested, + this.get_apply_chunks_done_callback(), + ); + } else { + match this + .client + .epoch_manager + .get_epoch_id_from_prev_block(block.header().prev_hash()) + { + Ok(epoch_id) => { + if let Some(hashes) = blocks_at_height.unwrap().get(&epoch_id) { + if !hashes.contains(block.header().hash()) { + warn!(target: "client", "Rejecting unrequested block {}, height {}", block.header().hash(), block.header().height()); + } + } + } + _ => {} + } + } + }) + } +} + +impl Handler> for ClientActor { + type Result = Result<(), ReasonForBan>; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + ctx: &mut Context, + ) -> Self::Result { + self.wrap(msg, ctx, "BlockHeadersResponse", |this, msg| { + let BlockHeadersResponse(headers, peer_id) = msg; + if this.receive_headers(headers, peer_id) { + Ok(()) + } else { + warn!(target: "client", "Banning node for sending invalid block headers"); + Err(ReasonForBan::BadBlockHeader) + } + }) + } +} + +impl Handler> for ClientActor { + type Result = (); + + #[perf] + fn handle(&mut self, msg: WithSpanContext, ctx: &mut Context) { + self.wrap(msg, ctx, "BlockApproval", |this, msg| { + let BlockApproval(approval, peer_id) = msg; + debug!(target: "client", "Receive approval {:?} from peer {:?}", approval, peer_id); + this.client.collect_block_approval(&approval, ApprovalType::PeerApproval(peer_id)); + }) + } +} + +/// StateResponse is used during StateSync and catchup. +/// It contains either StateSync header information (that tells us how many parts there are etc) or a single part. +impl Handler> for ClientActor { + type Result = (); + + #[perf] + fn handle(&mut self, msg: WithSpanContext, ctx: &mut Context) { + self.wrap(msg, ctx, "StateResponse", |this, msg| { + let StateResponse(state_response_info) = msg; + let shard_id = state_response_info.shard_id(); + let hash = state_response_info.sync_hash(); + let state_response = state_response_info.take_state_response(); + + trace!(target: "sync", "Received state response shard_id: {} sync_hash: {:?} part(id/size): {:?}", + shard_id, + hash, + state_response.part().as_ref().map(|(part_id, data)| (part_id, data.len())) + ); + // Get the download that matches the shard_id and hash + + // ... It could be that the state was requested by the state sync + if let SyncStatus::StateSync(StateSyncStatus{ sync_hash, sync_status: shards_to_download }) = + &mut this.client.sync_status + { + if hash == *sync_hash { + if let Some(shard_download) = shards_to_download.get_mut(&shard_id) { + this.client.state_sync.update_download_on_state_response_message(shard_download, hash, shard_id, state_response, &mut this.client.chain); + return; + } + } + } + + // ... Or one of the catchups + if let Some((state_sync, shards_to_download, _)) = + this.client.catchup_state_syncs.get_mut(&hash) + { + if let Some(shard_download) = shards_to_download.get_mut(&shard_id) { + state_sync.update_download_on_state_response_message(shard_download, hash, shard_id, state_response, &mut this.client.chain); + return; + } + } + + error!(target: "sync", "State sync received hash {} that we're not expecting, potential malicious peer or a very delayed response.", hash); + }) + } +} + +impl Handler> for ClientActor { + type Result = (); + + #[perf] + fn handle(&mut self, msg: WithSpanContext, ctx: &mut Context) { + self.wrap(msg, ctx, "RecvChallenge", |this, msg| { + let RecvChallenge(challenge) = msg; + match this.client.process_challenge(challenge) { + Ok(_) => {} + Err(err) => error!(target: "client", "Error processing challenge: {}", err), + } + }); + } +} + +impl Handler> for ClientActor { + type Result = (); + + #[perf] + fn handle(&mut self, msg: WithSpanContext, ctx: &mut Context) { + // SetNetworkInfo is a large message. Avoid printing it at the `debug` verbosity. + self.wrap(msg, ctx, "SetNetworkInfo", |this, msg| { + let SetNetworkInfo(network_info) = msg; + this.network_info = network_info; + }) + } +} + +#[cfg(feature = "sandbox")] +impl Handler> for ClientActor { + type Result = unc_client_primitives::types::SandboxResponse; + + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Context, + ) -> unc_client_primitives::types::SandboxResponse { + let (_span, msg) = handler_debug_span!(target: "client", msg); + match msg { + unc_client_primitives::types::SandboxMessage::SandboxPatchState(state) => { + self.client.chain.patch_state( + unc_primitives::sandbox::state_patch::SandboxStatePatch::new(state), + ); + unc_client_primitives::types::SandboxResponse::SandboxNoResponse + } + unc_client_primitives::types::SandboxMessage::SandboxPatchStateStatus => { + unc_client_primitives::types::SandboxResponse::SandboxPatchStateFinished( + !self.client.chain.patch_state_in_progress(), + ) + } + unc_client_primitives::types::SandboxMessage::SandboxFastForward(delta_height) => { + if self.fastforward_delta > 0 { + return unc_client_primitives::types::SandboxResponse::SandboxFastForwardFailed( + "Consecutive fast_forward requests cannot be made while a current one is going on.".to_string()); + } + + self.fastforward_delta = delta_height; + unc_client_primitives::types::SandboxResponse::SandboxNoResponse + } + unc_client_primitives::types::SandboxMessage::SandboxFastForwardStatus => { + unc_client_primitives::types::SandboxResponse::SandboxFastForwardFinished( + self.fastforward_delta == 0, + ) + } + } + } +} + +impl Handler> for ClientActor { + type Result = Result; + + #[perf] + fn handle(&mut self, msg: WithSpanContext, ctx: &mut Context) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + self.check_triggers(ctx); + + let head = self.client.chain.head()?; + let head_header = self.client.chain.get_block_header(&head.last_block_hash)?; + let latest_block_time = head_header.raw_timestamp(); + let latest_state_root = *head_header.prev_state_root(); + if msg.is_health_check { + let now = Utc::now(); + let block_timestamp = from_timestamp(latest_block_time); + if now > block_timestamp { + let elapsed = (now - block_timestamp).to_std().unwrap(); + if elapsed + > Duration::from_millis( + self.client.config.max_block_production_delay.as_millis() as u64 + * STATUS_WAIT_TIME_MULTIPLIER, + ) + { + return Err(StatusError::NoNewBlocks { elapsed }); + } + } + + if self.client.sync_status.is_syncing() { + return Err(StatusError::NodeIsSyncing); + } + } + let validators: Vec = self + .client + .epoch_manager + .get_epoch_block_producers_ordered(&head.epoch_id, &head.last_block_hash) + .into_chain_error()? + .into_iter() + .map(|(validator_stake, is_slashed)| ValidatorInfo { + account_id: validator_stake.take_account_id(), + is_slashed, + }) + .collect(); + + let epoch_start_height = + self.client.epoch_manager.get_epoch_start_height(&head.last_block_hash).ok(); + + let protocol_version = self + .client + .epoch_manager + .get_epoch_protocol_version(&head.epoch_id) + .into_chain_error()?; + + let node_public_key = self.node_id.public_key().clone(); + let (validator_account_id, validator_public_key) = match &self.client.validator_signer { + Some(vs) => (Some(vs.validator_id().clone()), Some(vs.public_key())), + None => (None, None), + }; + let node_key = validator_public_key.clone(); + + let mut earliest_block_hash = None; + let mut earliest_block_height = None; + let mut earliest_block_time = None; + if let Some(earliest_block_hash_value) = self.client.chain.get_earliest_block_hash()? { + earliest_block_hash = Some(earliest_block_hash_value); + if let Ok(earliest_block) = + self.client.chain.get_block_header(&earliest_block_hash_value) + { + earliest_block_height = Some(earliest_block.height()); + earliest_block_time = Some(earliest_block.timestamp()); + } + } + // Provide more detailed information about the current state of chain. + // For now - provide info about last 50 blocks. + let detailed_debug_status = if msg.detailed { + Some(DetailedDebugStatus { + network_info: new_network_info_view(&self.client.chain, &self.network_info), + sync_status: format!( + "{} ({})", + self.client.sync_status.as_variant_name(), + display_sync_status( + &self.client.sync_status, + &self.client.chain.head()?, + &self.client.config.state_sync.sync, + ), + ), + catchup_status: self.client.get_catchup_status()?, + current_head_status: head.clone().into(), + current_header_head_status: self.client.chain.header_head()?.into(), + block_production_delay_millis: self + .client + .config + .min_block_production_delay + .as_millis() as u64, + }) + } else { + None + }; + let uptime_sec = StaticClock::utc().timestamp() - self.info_helper.boot_time_seconds; + Ok(StatusResponse { + version: self.client.config.version.clone(), + protocol_version, + latest_protocol_version: PROTOCOL_VERSION, + chain_id: self.client.config.chain_id.clone(), + rpc_addr: self.client.config.rpc_addr.clone(), + validators, + sync_info: StatusSyncInfo { + latest_block_hash: head.last_block_hash, + latest_block_height: head.height, + latest_state_root, + latest_block_time: from_timestamp(latest_block_time), + syncing: self.client.sync_status.is_syncing(), + earliest_block_hash, + earliest_block_height, + earliest_block_time, + epoch_id: Some(head.epoch_id), + epoch_start_height, + }, + validator_account_id, + validator_public_key, + node_public_key, + node_key, + uptime_sec, + detailed_debug_status, + }) + } +} + +/// Private to public API conversion. +fn make_peer_info(from: unc_network::types::PeerInfo) -> unc_client_primitives::types::PeerInfo { + unc_client_primitives::types::PeerInfo { + id: from.id, + addr: from.addr, + account_id: from.account_id, + } +} + +/// Private to public API conversion. +fn make_known_producer( + from: unc_network::types::KnownProducer, +) -> unc_client_primitives::types::KnownProducer { + unc_client_primitives::types::KnownProducer { + peer_id: from.peer_id, + account_id: from.account_id, + addr: from.addr, + next_hops: from.next_hops, + } +} + +impl Handler> for ClientActor { + type Result = Result; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + ctx: &mut Context, + ) -> Self::Result { + let (_span, _msg) = handler_debug_span!(target: "client", msg); + self.check_triggers(ctx); + + Ok(NetworkInfoResponse { + connected_peers: (self.network_info.connected_peers.iter()) + .map(|fpi| make_peer_info(fpi.full_peer_info.peer_info.clone())) + .collect(), + num_connected_peers: self.network_info.num_connected_peers, + peer_max_count: self.network_info.peer_max_count, + sent_bytes_per_sec: self.network_info.sent_bytes_per_sec, + received_bytes_per_sec: self.network_info.received_bytes_per_sec, + known_producers: self + .network_info + .known_producers + .iter() + .map(|p| make_known_producer(p.clone())) + .collect(), + }) + } +} + +/// `ApplyChunksDoneMessage` is a message that signals the finishing of applying chunks of a block. +/// Upon receiving this message, ClientActors knows that it's time to finish processing the blocks that +/// just finished applying chunks. +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub struct ApplyChunksDoneMessage; + +impl Handler> for ClientActor { + type Result = (); + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, _msg) = handler_debug_span!(target: "client", msg); + self.try_process_unfinished_blocks(); + } +} + +#[derive(Debug)] +enum SyncRequirement { + SyncNeeded { peer_id: PeerId, highest_height: BlockHeight, head: Tip }, + AlreadyCaughtUp { peer_id: PeerId, highest_height: BlockHeight, head: Tip }, + NoPeers, + AdvHeaderSyncDisabled, +} + +impl SyncRequirement { + fn sync_needed(&self) -> bool { + matches!(self, Self::SyncNeeded { .. }) + } + + fn to_metrics_string(&self) -> String { + match self { + Self::SyncNeeded { .. } => "SyncNeeded", + Self::AlreadyCaughtUp { .. } => "AlreadyCaughtUp", + Self::NoPeers => "NoPeers", + Self::AdvHeaderSyncDisabled { .. } => "AdvHeaderSyncDisabled", + } + .to_string() + } +} + +impl fmt::Display for SyncRequirement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::SyncNeeded { peer_id, highest_height, head: my_head } => write!( + f, + "sync needed at #{} [{}]. highest height peer: {} at #{}", + my_head.height, + format_hash(my_head.last_block_hash), + peer_id, + highest_height + ), + Self::AlreadyCaughtUp { peer_id, highest_height, head: my_head } => write!( + f, + "synced at #{} [{}]. highest height peer: {} at #{}", + my_head.height, + format_hash(my_head.last_block_hash), + peer_id, + highest_height + ), + Self::NoPeers => write!(f, "no available peers"), + Self::AdvHeaderSyncDisabled => { + write!(f, "syncing disabled via adv_disable_header_sync") + } + } + } +} + +impl ClientActor { + /// Check if client Account Id should be sent and send it. + /// Account Id is sent when is not current a validator but are becoming a validator soon. + fn check_send_announce_account(&mut self, prev_block_hash: CryptoHash) { + // If no peers, there is no one to announce to. + if self.network_info.num_connected_peers == 0 { + debug!(target: "client", "No peers: skip account announce"); + return; + } + + // First check that we currently have an AccountId + let validator_signer = match self.client.validator_signer.as_ref() { + None => return, + Some(signer) => signer, + }; + + let now = StaticClock::instant(); + // Check that we haven't announced it too recently + if let Some(last_validator_announce_time) = self.last_validator_announce_time { + // Don't make announcement if have passed less than half of the time in which other peers + // should remove our Account Id from their Routing Tables. + if 2 * (now - last_validator_announce_time) < self.client.config.ttl_account_id_router { + return; + } + } + + debug!(target: "client", "Check announce account for {}, last announce time {:?}", validator_signer.validator_id(), self.last_validator_announce_time); + + // Announce AccountId if client is becoming a validator soon. + let next_epoch_id = unwrap_or_return!(self + .client + .epoch_manager + .get_next_epoch_id_from_prev_block(&prev_block_hash)); + + // Check client is part of the futures validators + if self.client.is_validator(&next_epoch_id, &prev_block_hash) { + debug!(target: "client", "Sending announce account for {}", validator_signer.validator_id()); + self.last_validator_announce_time = Some(now); + + let signature = validator_signer.sign_account_announce( + validator_signer.validator_id(), + &self.node_id, + &next_epoch_id, + ); + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::AnnounceAccount(AnnounceAccount { + account_id: validator_signer.validator_id().clone(), + peer_id: self.node_id.clone(), + epoch_id: next_epoch_id, + signature, + }), + )); + } + } + + /// Process the sandbox fast forward request. If the change in block height is past an epoch, + /// we fast forward to just right before the epoch, produce some blocks to get past and into + /// a new epoch, then we continue on with the residual amount to fast forward. + #[cfg(feature = "sandbox")] + fn sandbox_process_fast_forward( + &mut self, + block_height: BlockHeight, + ) -> Result, Error> { + let mut delta_height = std::mem::replace(&mut self.fastforward_delta, 0); + if delta_height == 0 { + return Ok(None); + } + + let epoch_length = self.client.config.epoch_length; + if epoch_length <= 3 { + return Err(Error::Other( + "Unsupported: fast_forward with an epoch length of 3 or less".to_string(), + )); + } + + // Check if we are at epoch boundary. If we are, do not fast forward until new + // epoch is here. Decrement the fast_forward count by 1 when a block is produced + // during this period of waiting + let block_height_wrt_epoch = block_height % epoch_length; + if epoch_length - block_height_wrt_epoch <= 3 || block_height_wrt_epoch == 0 { + // wait for doomslug to call into produce block + self.fastforward_delta = delta_height; + return Ok(None); + } + + let delta_height = if block_height_wrt_epoch + delta_height >= epoch_length { + // fast forward to just right before epoch boundary to have epoch_manager + // handle the epoch_height updates as normal. `- 3` since this is being + // done 3 blocks before the epoch ends. + let right_before_epoch_update = epoch_length - block_height_wrt_epoch - 3; + + delta_height -= right_before_epoch_update; + self.fastforward_delta = delta_height; + right_before_epoch_update + } else { + delta_height + }; + + self.client.accrued_fastforward_delta += delta_height; + let delta_time = self.client.sandbox_delta_time(); + let new_latest_known = unc_chain::types::LatestKnown { + height: block_height + delta_height, + seen: unc_primitives::utils::to_timestamp(StaticClock::utc() + delta_time), + }; + + Ok(Some(new_latest_known)) + } + + fn pre_block_production(&mut self) -> Result<(), Error> { + #[cfg(feature = "sandbox")] + { + let latest_known = self.client.chain.mut_chain_store().get_latest_known()?; + if let Some(new_latest_known) = + self.sandbox_process_fast_forward(latest_known.height)? + { + self.client.chain.mut_chain_store().save_latest_known(new_latest_known.clone())?; + self.client.sandbox_update_tip(new_latest_known.height)?; + } + } + Ok(()) + } + + fn post_block_production(&mut self) { + #[cfg(feature = "sandbox")] + if self.fastforward_delta > 0 { + // Decrease the delta_height by 1 since we've produced a single block. This + // ensures that we advanced the right amount of blocks when fast forwarding + // and fast forwarding triggers regular block production in the case of + // stepping between epoch boundaries. + self.fastforward_delta -= 1; + } + } + + /// Retrieves latest height, and checks if must produce next block. + /// Otherwise wait for block arrival or suggest to skip after timeout. + fn handle_block_production(&mut self) -> Result<(), Error> { + let _span = tracing::debug_span!(target: "client", "handle_block_production").entered(); + // If syncing, don't try to produce blocks. + if self.client.sync_status.is_syncing() { + debug!(target:"client", sync_status=format!("{:#?}", self.client.sync_status), "Syncing - block production disabled"); + return Ok(()); + } + + let _ = self.client.check_and_update_doomslug_tip(); + + self.pre_block_production()?; + let head = self.client.chain.head()?; + let latest_known = self.client.chain.chain_store().get_latest_known()?; + + assert!( + head.height <= latest_known.height, + "Latest known height is invalid {} vs {}", + head.height, + latest_known.height + ); + + let epoch_id = + self.client.epoch_manager.get_epoch_id_from_prev_block(&head.last_block_hash)?; + let log_block_production_info = + if self.client.epoch_manager.is_next_block_epoch_start(&head.last_block_hash)? { + true + } else { + // the next block is still the same epoch + let epoch_start_height = + self.client.epoch_manager.get_epoch_start_height(&head.last_block_hash)?; + latest_known.height - epoch_start_height < EPOCH_START_INFO_BLOCKS + }; + + // We try to produce block for multiple heights (up to the highest height for which we've seen 2/3 of approvals). + if latest_known.height + 1 <= self.client.doomslug.get_largest_height_crossing_threshold() { + debug!(target: "client", "Considering blocks for production between {} and {} ", latest_known.height + 1, self.client.doomslug.get_largest_height_crossing_threshold()); + } else { + debug!(target: "client", "Cannot produce any block: not enough approvals beyond {}", latest_known.height); + } + + let me = if let Some(me) = &self.client.validator_signer { + me.validator_id().clone() + } else { + return Ok(()); + }; + + // For debug purpose, we record the approvals we have seen so far to the future blocks + for height in latest_known.height + 1..=self.client.doomslug.get_largest_approval_height() { + let next_block_producer_account = + self.client.epoch_manager.get_block_producer(&epoch_id, height)?; + + if me == next_block_producer_account { + self.client.block_production_info.record_approvals( + height, + self.client.doomslug.approval_status_at_height(&height), + ); + } + } + + for height in + latest_known.height + 1..=self.client.doomslug.get_largest_height_crossing_threshold() + { + let next_block_producer_account = + self.client.epoch_manager.get_block_producer(&epoch_id, height)?; + + if me == next_block_producer_account { + let num_chunks = self + .client + .num_chunk_headers_ready_for_inclusion(&epoch_id, &head.last_block_hash); + let have_all_chunks = head.height == 0 + || num_chunks == self.client.epoch_manager.shard_ids(&epoch_id).unwrap().len(); + + if self.client.doomslug.ready_to_produce_block( + StaticClock::instant(), + height, + have_all_chunks, + log_block_production_info, + ) { + if let Err(err) = self.produce_block(height) { + // If there is an error, report it and let it retry on the next loop step. + error!(target: "client", height, "Block production failed: {}", err); + } else { + self.post_block_production(); + } + } + } + } + + Ok(()) + } + + fn schedule_triggers(&mut self, ctx: &mut Context) { + let wait = self.check_triggers(ctx); + + unc_performance_metrics::actix::run_later(ctx, wait, move |act, ctx| { + act.schedule_triggers(ctx); + }); + } + + /// Check if the scheduled time of any "triggers" has passed, and if so, call the trigger. + /// Triggers are important functions of client, like running single step of state sync or + /// checking if we can produce a block. + /// + /// It is called before processing Actix message and also in schedule_triggers. + /// This is to ensure all triggers enjoy higher priority than any actix message. + /// Otherwise due to a bug in Actix library Actix prioritizes processing messages + /// while there are messages in mailbox. Because of that we handle scheduling + /// triggers with custom `run_timer` function instead of `run_later` in Actix. + /// + /// Returns the delay before the next time `check_triggers` should be called, which is + /// min(time until the closest trigger, 1 second). + fn check_triggers(&mut self, ctx: &mut Context) -> Duration { + let _span = tracing::debug_span!(target: "client", "check_triggers").entered(); + if let Some(config_updater) = &mut self.config_updater { + config_updater.try_update(&|updateable_client_config| { + self.client.update_client_config(updateable_client_config) + }); + } + + // Check block height to trigger expected shutdown + if let Ok(head) = self.client.chain.head() { + if let Some(block_height_to_shutdown) = self.client.config.expected_shutdown.get() { + if head.height >= block_height_to_shutdown { + info!(target: "client", "Expected shutdown triggered: head block({}) >= ({:?})", head.height, block_height_to_shutdown); + if let Some(tx) = self.shutdown_signal.take() { + let _ = tx.send(()); // Ignore send signal fail, it will send again in next trigger + } + } + } + } + + self.try_process_unfinished_blocks(); + + let mut delay = Duration::from_secs(1); + let now = Utc::now(); + + let timer = metrics::CHECK_TRIGGERS_TIME.start_timer(); + if self.sync_started { + self.sync_timer_next_attempt = self.run_timer( + self.sync_wait_period(), + self.sync_timer_next_attempt, + ctx, + |act, _| act.run_sync_step(), + "sync", + ); + + delay = std::cmp::min( + delay, + self.sync_timer_next_attempt.signed_duration_since(now).to_std().unwrap_or(delay), + ); + + self.doomslug_timer_next_attempt = self.run_timer( + self.client.config.doosmslug_step_period, + self.doomslug_timer_next_attempt, + ctx, + |act, ctx| act.try_doomslug_timer(ctx), + "doomslug", + ); + delay = core::cmp::min( + delay, + self.doomslug_timer_next_attempt + .signed_duration_since(now) + .to_std() + .unwrap_or(delay), + ) + } + if self.block_production_started { + self.block_production_next_attempt = self.run_timer( + self.client.config.block_production_tracking_delay, + self.block_production_next_attempt, + ctx, + |act, _ctx| act.try_handle_block_production(), + "block_production", + ); + + let _ = self.client.check_head_progress_stalled( + self.client.config.max_block_production_delay * HEAD_STALL_MULTIPLIER, + ); + + delay = core::cmp::min( + delay, + self.block_production_next_attempt + .signed_duration_since(now) + .to_std() + .unwrap_or(delay), + ) + } + + self.log_summary_timer_next_attempt = self.run_timer( + self.client.config.log_summary_period, + self.log_summary_timer_next_attempt, + ctx, + |act, _ctx| act.log_summary(), + "log_summary", + ); + delay = core::cmp::min( + delay, + self.log_summary_timer_next_attempt + .signed_duration_since(now) + .to_std() + .unwrap_or(delay), + ); + timer.observe_duration(); + delay + } + + /// "Unfinished" blocks means that blocks that client has started the processing and haven't + /// finished because it was waiting for applying chunks to be done. This function checks + /// if there are any "unfinished" blocks that are ready to be processed again and finish processing + /// these blocks. + /// This function is called at two places, upon receiving ApplyChunkDoneMessage and `check_triggers`. + /// The job that executes applying chunks will send an ApplyChunkDoneMessage to ClientActor after + /// applying chunks is done, so when receiving ApplyChunkDoneMessage messages, ClientActor + /// calls this function to finish processing the unfinished blocks. ClientActor also calls + /// this function in `check_triggers`, because the actix queue may be blocked by other messages + /// and we want to prioritize block processing. + fn try_process_unfinished_blocks(&mut self) { + let _span = debug_span!(target: "client", "try_process_unfinished_blocks").entered(); + let (accepted_blocks, errors) = + self.client.postprocess_ready_blocks(self.get_apply_chunks_done_callback(), true); + if !errors.is_empty() { + error!(target: "client", ?errors, "try_process_unfinished_blocks got errors"); + } + self.process_accepted_blocks(accepted_blocks); + } + + fn try_handle_block_production(&mut self) { + let _span = debug_span!(target: "client", "try_handle_block_production").entered(); + if let Err(err) = self.handle_block_production() { + tracing::error!(target: "client", ?err, "Handle block production failed") + } + } + + fn try_doomslug_timer(&mut self, _: &mut Context) { + let _span = tracing::debug_span!(target: "client", "try_doomslug_timer").entered(); + let _ = self.client.check_and_update_doomslug_tip(); + let approvals = self.client.doomslug.process_timer(StaticClock::instant()); + + // Important to save the largest approval target height before sending approvals, so + // that if the node crashes in the meantime, we cannot get slashed on recovery + let mut chain_store_update = self.client.chain.mut_chain_store().store_update(); + chain_store_update + .save_largest_target_height(self.client.doomslug.get_largest_target_height()); + + match chain_store_update.commit() { + Ok(_) => { + let head = unwrap_or_return!(self.client.chain.head()); + if self.client.is_validator(&head.epoch_id, &head.last_block_hash) + || self.client.is_validator(&head.next_epoch_id, &head.last_block_hash) + { + for approval in approvals { + if let Err(e) = + self.client.send_approval(&self.client.doomslug.get_tip().0, approval) + { + error!("Error while sending an approval {:?}", e); + } + } + } + } + Err(e) => error!("Error while committing largest skipped height {:?}", e), + }; + } + + /// Produce block if we are block producer for given `next_height` height. + /// Can return error, should be called with `produce_block` to handle errors and reschedule. + fn produce_block(&mut self, next_height: BlockHeight) -> Result<(), Error> { + let _span = tracing::debug_span!(target: "client", "produce_block", next_height).entered(); + if let Some(block) = self.client.produce_block(next_height)? { + // If we produced the block, send it out before we apply the block. + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::Block { block: block.clone() }, + )); + // We’ve produced the block so that counts as validated block. + let block = MaybeValidated::from_validated(block); + let res = self.client.start_process_block( + block, + Provenance::PRODUCED, + self.get_apply_chunks_done_callback(), + ); + if let Err(e) = &res { + match e { + unc_chain::Error::ChunksMissing(_) => { + debug!(target: "client", "chunks missing"); + // missing chunks were already handled in Client::process_block, we don't need to + // do anything here + return Ok(()); + } + _ => { + error!(target: "client", ?res, "Failed to process freshly produced block"); + byzantine_assert!(false); + return res.map_err(|err| err.into()); + } + } + } + } + Ok(()) + } + + fn send_chunks_metrics(&mut self, block: &Block) { + let chunks = block.chunks(); + for (chunk, &included) in chunks.iter().zip(block.header().chunk_mask().iter()) { + if included { + self.info_helper.chunk_processed( + chunk.shard_id(), + chunk.prev_gas_used(), + chunk.prev_balance_burnt(), + ); + } else { + self.info_helper.chunk_skipped(chunk.shard_id()); + } + } + } + + fn send_block_metrics(&mut self, block: &Block) { + let chunks_in_block = block.header().chunk_mask().iter().filter(|&&m| m).count(); + let gas_used = Block::compute_gas_used(block.chunks().iter(), block.header().height()); + + let last_final_hash = block.header().last_final_block(); + let last_final_ds_hash = block.header().last_ds_final_block(); + let last_final_block_height = self + .client + .chain + .get_block(&last_final_hash) + .map_or(0, |block| block.header().height()); + let last_final_ds_block_height = self + .client + .chain + .get_block(&last_final_ds_hash) + .map_or(0, |block| block.header().height()); + + let epoch_height = + self.client.epoch_manager.get_epoch_height_from_prev_block(block.hash()).unwrap_or(0); + let epoch_start_height = self + .client + .epoch_manager + .get_epoch_start_height(&last_final_hash) + .unwrap_or(last_final_block_height); + let last_final_block_height_in_epoch = + last_final_block_height.checked_sub(epoch_start_height); + + self.info_helper.block_processed( + gas_used, + chunks_in_block as u64, + block.header().next_gas_price(), + block.header().total_supply(), + last_final_block_height, + last_final_ds_block_height, + epoch_height, + last_final_block_height_in_epoch, + ); + } + + /// Process all blocks that were accepted by calling other relevant services. + fn process_accepted_blocks(&mut self, accepted_blocks: Vec) { + let _span = tracing::debug_span!( + target: "client", + "process_accepted_blocks", + num_blocks = accepted_blocks.len()) + .entered(); + for accepted_block in accepted_blocks { + let block = self.client.chain.get_block(&accepted_block).unwrap().clone(); + self.send_chunks_metrics(&block); + self.send_block_metrics(&block); + self.check_send_announce_account(*block.header().last_final_block()); + } + } + + /// Returns the callback function that will be passed to various functions that may trigger + /// the processing of new blocks. This callback will be called at the end of applying chunks + /// for every block. + fn get_apply_chunks_done_callback(&self) -> DoneApplyChunkCallback { + let addr = self.my_address.clone(); + Arc::new(move |_| { + addr.do_send(ApplyChunksDoneMessage {}.with_span_context()); + }) + } + + fn receive_headers(&mut self, headers: Vec, peer_id: PeerId) -> bool { + let _span = + debug_span!(target: "client", "receive_headers", num_headers = headers.len(), ?peer_id) + .entered(); + if headers.is_empty() { + info!(target: "client", "Received an empty set of block headers"); + return true; + } + match self.client.sync_block_headers(headers) { + Ok(_) => true, + Err(err) => { + if err.is_bad_data() { + error!(target: "client", ?err, "Error processing sync blocks"); + false + } else { + debug!(target: "client", ?err, "Block headers refused by chain"); + true + } + } + } + } + + /// Check whether need to (continue) sync. + /// Also return higher height with known peers at that height. + fn syncing_info(&self) -> Result { + if self.adv.disable_header_sync() { + return Ok(SyncRequirement::AdvHeaderSyncDisabled); + } + + let head = self.client.chain.head()?; + let is_syncing = self.client.sync_status.is_syncing(); + + // Only consider peers whose latest block is not invalid blocks + let eligible_peers: Vec<_> = self + .network_info + .highest_height_peers + .iter() + .filter(|p| !self.client.chain.is_block_invalid(&p.highest_block_hash)) + .collect(); + metrics::PEERS_WITH_INVALID_HASH + .set(self.network_info.highest_height_peers.len() as i64 - eligible_peers.len() as i64); + let peer_info = if let Some(peer_info) = eligible_peers.choose(&mut thread_rng()) { + peer_info + } else { + return Ok(SyncRequirement::NoPeers); + }; + + let peer_id = peer_info.peer_info.id.clone(); + let highest_height = peer_info.highest_block_height; + + if is_syncing { + if highest_height <= head.height { + Ok(SyncRequirement::AlreadyCaughtUp { peer_id, highest_height, head }) + } else { + Ok(SyncRequirement::SyncNeeded { peer_id, highest_height, head }) + } + } else { + if highest_height > head.height + self.client.config.sync_height_threshold { + Ok(SyncRequirement::SyncNeeded { peer_id, highest_height, head }) + } else { + Ok(SyncRequirement::AlreadyCaughtUp { peer_id, highest_height, head }) + } + } + } + + fn start_flat_storage_creation(&mut self, ctx: &mut Context) { + if !self.client.config.flat_storage_creation_enabled { + return; + } + match self.client.run_flat_storage_creation_step() { + Ok(false) => {} + Ok(true) => { + return; + } + Err(err) => { + error!(target: "client", "Error occurred during flat storage creation step: {:?}", err); + } + } + + unc_performance_metrics::actix::run_later( + ctx, + self.client.config.flat_storage_creation_period, + move |act, ctx| { + act.start_flat_storage_creation(ctx); + }, + ); + } + + /// Starts syncing and then switches to either syncing or regular mode. + fn start_sync(&mut self, ctx: &mut Context) { + // Wait for connections reach at least minimum peers unless skipping sync. + if self.network_info.num_connected_peers < self.client.config.min_num_peers + && !self.client.config.skip_sync_wait + { + unc_performance_metrics::actix::run_later( + ctx, + self.client.config.sync_step_period, + move |act, ctx| { + act.start_sync(ctx); + }, + ); + return; + } + self.sync_started = true; + + // Sync loop will be started by check_triggers. + } + + /// Select the block hash we are using to sync state. It will sync with the state before applying the + /// content of such block. + /// + /// The selected block will always be the first block on a new epoch: + /// . + fn find_sync_hash(&mut self) -> Result { + let header_head = self.client.chain.header_head()?; + let sync_hash = header_head.last_block_hash; + let epoch_start_sync_hash = + StateSync::get_epoch_start_sync_hash(&mut self.client.chain, &sync_hash)?; + + let genesis_hash = self.client.chain.genesis().hash(); + tracing::debug!( + target: "sync", + ?header_head, + ?sync_hash, + ?epoch_start_sync_hash, + ?genesis_hash, + "find_sync_hash"); + assert_ne!(&epoch_start_sync_hash, genesis_hash); + Ok(epoch_start_sync_hash) + } + + /// Runs catchup on repeat, if this client is a validator. + /// Schedules itself again if it was not ran as response to state parts job result + fn catchup(&mut self, ctx: &mut Context) { + { + // An extra scope to limit the lifetime of the span. + let _span = tracing::debug_span!(target: "client", "catchup").entered(); + if let Err(err) = self.client.run_catchup( + &self.network_info.highest_height_peers, + &self.state_parts_task_scheduler, + &self.block_catch_up_scheduler, + &self.resharding_scheduler, + self.get_apply_chunks_done_callback(), + &self.state_parts_client_arbiter.handle(), + ) { + error!(target: "client", "{:?} Error occurred during catchup for the next epoch: {:?}", self.client.validator_signer.as_ref().map(|vs| vs.validator_id()), err); + } + } + + unc_performance_metrics::actix::run_later( + ctx, + self.client.config.catchup_step_period, + move |act, ctx| { + act.catchup(ctx); + }, + ); + } + + /// Runs given callback if the time now is at least `next_attempt`. + /// Returns time for next run which should be made based on given `delay` between runs. + fn run_timer( + &mut self, + delay: Duration, + next_attempt: DateTime, + ctx: &mut Context, + f: F, + timer_label: &str, + ) -> DateTime + where + F: FnOnce(&mut Self, &mut ::Context) + 'static, + { + let now = Utc::now(); + if now < next_attempt { + return next_attempt; + } + + let timer = + metrics::CLIENT_TRIGGER_TIME_BY_TYPE.with_label_values(&[timer_label]).start_timer(); + f(self, ctx); + timer.observe_duration(); + + now.checked_add_signed(chrono::Duration::from_std(delay).unwrap()).unwrap() + } + + fn sync_wait_period(&self) -> Duration { + if let Ok(sync) = self.syncing_info() { + if !sync.sync_needed() { + // If we don't need syncing - retry the sync call rarely. + self.client.config.sync_check_period + } else { + // If we need syncing - retry the sync call often. + self.client.config.sync_step_period + } + } else { + self.client.config.sync_step_period + } + } + + /// Main syncing job responsible for syncing client with other peers. + /// Runs itself iff it was not ran as reaction for message with results of + /// finishing state part job + fn run_sync_step(&mut self) { + let _span = tracing::debug_span!(target: "client", "run_sync_step").entered(); + + macro_rules! unwrap_and_report (($obj: expr) => (match $obj { + Ok(v) => v, + Err(err) => { + error!(target: "sync", "Sync: Unexpected error: {}", err); + return; + } + })); + + let currently_syncing = self.client.sync_status.is_syncing(); + let sync = unwrap_and_report!(self.syncing_info()); + self.info_helper.update_sync_requirements_metrics(sync.to_metrics_string()); + + match sync { + SyncRequirement::AlreadyCaughtUp { .. } + | SyncRequirement::NoPeers + | SyncRequirement::AdvHeaderSyncDisabled => { + if currently_syncing { + // Initial transition out of "syncing" state. + debug!(target: "sync", prev_sync_status = ?self.client.sync_status, "disabling sync"); + self.client.sync_status.update(SyncStatus::NoSync); + // Announce this client's account id if their epoch is coming up. + let head = unwrap_and_report!(self.client.chain.head()); + self.check_send_announce_account(head.prev_block_hash); + } + } + + SyncRequirement::SyncNeeded { highest_height, .. } => { + let mut notify_start_sync = false; + if !currently_syncing { + info!(target: "client", ?sync, "enabling sync"); + } + // Run each step of syncing separately. + unwrap_and_report!(self.client.header_sync.run( + &mut self.client.sync_status, + &mut self.client.chain, + highest_height, + &self.network_info.highest_height_peers + )); + // Only body / state sync if header height is close to the latest. + let header_head = unwrap_and_report!(self.client.chain.header_head()); + + // Sync state if already running sync state or if block sync is too far. + let sync_state = match self.client.sync_status { + SyncStatus::StateSync(_) => true, + _ if header_head.height + >= highest_height + .saturating_sub(self.client.config.block_header_fetch_horizon) => + { + unwrap_and_report!(self.client.block_sync.run( + &mut self.client.sync_status, + &self.client.chain, + highest_height, + &self.network_info.highest_height_peers + )) + } + _ => false, + }; + if sync_state { + match self.client.sync_status { + SyncStatus::StateSync(_) => (), + _ => { + let sync_hash = unwrap_and_report!(self.find_sync_hash()); + if !self.client.config.archive { + unwrap_and_report!(self + .client + .chain + .reset_data_pre_state_sync(sync_hash)); + } + self.client.sync_status.update(SyncStatus::StateSync( + StateSyncStatus { sync_hash, sync_status: HashMap::default() }, + )); + // This is the first time we run state sync. + notify_start_sync = true; + } + }; + let sync_hash = match &self.client.sync_status { + SyncStatus::StateSync(s) => s.sync_hash, + _ => unreachable!("Sync status should have been StateSync!"), + }; + + let me = + self.client.validator_signer.as_ref().map(|x| x.validator_id().clone()); + let block_header = + unwrap_and_report!(self.client.chain.get_block_header(&sync_hash)); + let prev_hash = *block_header.prev_hash(); + let epoch_id = block_header.epoch_id().clone(); + let shards_to_sync: Vec<_> = self + .client + .epoch_manager + .shard_ids(&epoch_id) + .unwrap() + .into_iter() + .filter(|&shard_id| { + cares_about_shard_this_or_next_epoch( + me.as_ref(), + &prev_hash, + shard_id, + true, + &self.client.shard_tracker, + ) + }) + .collect(); + + let use_colour = + matches!(self.client.config.log_summary_style, LogSummaryStyle::Colored); + + // Notify each shard to sync. + if notify_start_sync { + let shard_layout = self + .client + .epoch_manager + .get_shard_layout(&epoch_id) + .expect("Cannot get shard layout"); + for &shard_id in &shards_to_sync { + let shard_uid = + ShardUId::from_shard_id_and_layout(shard_id, &shard_layout); + match self.client.state_sync_adapter.clone().read() { + Ok(sync_adapter) => sync_adapter.send( + shard_uid, + (SyncMessage::StartSync(SyncShardInfo { + shard_uid, + sync_hash, + })) + .with_span_context(), + ), + Err(_) => { + error!(target:"client", "State sync adapter lock is poisoned.") + } + } + } + } + + let now = StaticClock::utc(); + + // FIXME: it checks if the block exists.. but I have no idea why.. + // seems that we don't really use this block in case of catchup - we use it only for state sync. + // Seems it is related to some bug with block getting orphaned after state sync? but not sure. + let (request_block, have_block) = + unwrap_and_report!(self.sync_block_status(&prev_hash, now)); + + if request_block { + self.client.last_time_sync_block_requested = Some(now); + if let Some(peer_info) = + self.network_info.highest_height_peers.choose(&mut thread_rng()) + { + let id = peer_info.peer_info.id.clone(); + + for hash in + vec![*block_header.prev_hash(), *block_header.hash()].into_iter() + { + self.client.request_block(hash, id.clone()); + } + } + } + if have_block { + self.client.last_time_sync_block_requested = None; + } + + let state_sync_status = match &mut self.client.sync_status { + SyncStatus::StateSync(s) => s, + _ => unreachable!("Sync status should have been StateSync!"), + }; + match unwrap_and_report!(self.client.state_sync.run( + &me, + sync_hash, + &mut state_sync_status.sync_status, + &mut self.client.chain, + self.client.epoch_manager.as_ref(), + &self.network_info.highest_height_peers, + shards_to_sync, + &self.state_parts_task_scheduler, + &self.resharding_scheduler, + &self.state_parts_client_arbiter.handle(), + use_colour, + self.client.runtime_adapter.clone(), + )) { + StateSyncResult::InProgress => (), + StateSyncResult::Completed => { + if !have_block { + trace!(target: "sync", "Sync done. Waiting for sync block."); + return; + } + info!(target: "sync", "State sync: all shards are done"); + + let mut block_processing_artifacts = BlockProcessingArtifact::default(); + + unwrap_and_report!(self.client.chain.reset_heads_post_state_sync( + &me, + sync_hash, + &mut block_processing_artifacts, + self.get_apply_chunks_done_callback(), + )); + + self.client + .process_block_processing_artifact(block_processing_artifacts); + self.client.sync_status.update(SyncStatus::BlockSync { + start_height: 0, + current_height: 0, + highest_height: 0, + }); + } + } + } + } + } + } + + /// Verifies if the node possesses sync block. + /// It is the last block of the previous epoch. + /// If the block is absent, the node requests it from peers. + fn sync_block_status( + &self, + prev_hash: &CryptoHash, + now: DateTime, + ) -> Result<(bool, bool), unc_chain::Error> { + let (request_block, have_block) = if !self.client.chain.block_exists(prev_hash)? { + let timeout = + chrono::Duration::from_std(self.client.config.state_sync_timeout).unwrap(); + match self.client.last_time_sync_block_requested { + None => (true, false), + Some(last_time) => { + if (now - last_time) >= timeout { + tracing::error!( + target: "sync", + %prev_hash, + ?timeout, + "State sync: block request timed out"); + (true, false) + } else { + (false, false) + } + } + } + } else { + (false, true) + }; + Ok((request_block, have_block)) + } + + /// Print current summary. + fn log_summary(&mut self) { + let _span = tracing::debug_span!(target: "client", "log_summary").entered(); + self.info_helper.log_summary( + &self.client, + &self.node_id, + &self.network_info, + &self.config_updater, + ) + } + + /// Checks if the node is syncing its State and applies special logic in that case. + /// A node usually ignores blocks that are too far ahead, but in case of a node syncing its state it is looking for 2 specific blocks: + /// * The first block of the new epoch + /// * The last block of the prev epoch + /// Returns whether the node is syncing its state. + fn maybe_receive_state_sync_blocks(&mut self, block: &Block) -> bool { + let SyncStatus::StateSync(StateSyncStatus { sync_hash, .. }) = self.client.sync_status + else { + return false; + }; + if let Ok(header) = self.client.chain.get_block_header(&sync_hash) { + let block: MaybeValidated = (*block).clone().into(); + let block_hash = *block.hash(); + if let Err(err) = self.client.chain.validate_block(&block) { + byzantine_assert!(false); + error!(target: "client", ?err, ?block_hash, "Received an invalid block during state sync"); + } + // Notice that two blocks are saved differently: + // * save_block() for one block. + // * save_orphan() for another block. + if &block_hash == header.prev_hash() { + // The last block of the previous epoch. + if let Err(err) = self.client.chain.save_block(block) { + error!(target: "client", ?err, ?block_hash, "Failed to save a block during state sync"); + } + } else if block_hash == sync_hash { + // The first block of the new epoch. + self.client.chain.save_orphan(block, Provenance::NONE, None, false); + } + } + true + } +} + +impl Drop for ClientActor { + fn drop(&mut self) { + let _span = tracing::debug_span!(target: "client", "drop").entered(); + self.state_parts_client_arbiter.stop(); + } +} + +impl Handler> for ClientActor { + type Result = (); + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + if let Some((sync, _, _)) = self.client.catchup_state_syncs.get_mut(&msg.sync_hash) { + // We are doing catchup + sync.set_apply_result(msg.shard_id, msg.apply_result); + } else { + self.client.state_sync.set_apply_result(msg.shard_id, msg.apply_result); + } + } +} + +impl Handler> for ClientActor { + type Result = (); + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + if let Some((_, _, blocks_catch_up_state)) = + self.client.catchup_state_syncs.get_mut(&msg.sync_hash) + { + assert!(blocks_catch_up_state.scheduled_blocks.remove(&msg.block_hash)); + blocks_catch_up_state + .processed_blocks + .insert(msg.block_hash, msg.results.into_iter().map(|res| res.1).collect_vec()); + } else { + panic!("block catch up processing result from unknown sync hash"); + } + } +} + +impl Handler> for ClientActor { + type Result = (); + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + if let Some((sync, _, _)) = self.client.catchup_state_syncs.get_mut(&msg.sync_hash) { + // We are doing catchup + sync.set_resharding_result(msg.shard_id, msg.new_state_roots); + } else { + self.client.state_sync.set_resharding_result(msg.shard_id, msg.new_state_roots); + } + } +} + +impl Handler> for ClientActor { + type Result = (); + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + match msg { + ShardsManagerResponse::ChunkCompleted { partial_chunk, shard_chunk } => { + self.client.on_chunk_completed( + partial_chunk, + shard_chunk, + self.get_apply_chunks_done_callback(), + ); + } + ShardsManagerResponse::InvalidChunk(encoded_chunk) => { + self.client.on_invalid_chunk(encoded_chunk); + } + ShardsManagerResponse::ChunkHeaderReadyForInclusion { + chunk_header, + chunk_producer, + } => { + self.client.on_chunk_header_ready_for_inclusion(chunk_header, chunk_producer); + } + } + } +} + +impl Handler> for ClientActor { + type Result = Result; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + + Ok(self.client.config.clone()) + } +} + +impl Handler> for ClientActor { + type Result = (); + + #[perf] + fn handle(&mut self, msg: WithSpanContext, _: &mut Context) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + // TODO + // process messages from SyncActors + } +} + +impl Handler> for ClientActor { + type Result = (); + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + if let Err(err) = self.client.process_chunk_state_witness(msg.0) { + tracing::error!(target: "client", ?err, "Error processing chunk state witness"); + } + } +} + +impl Handler> for ClientActor { + type Result = (); + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + if let Err(err) = self.client.process_chunk_endorsement(msg.0) { + tracing::error!(target: "client", ?err, "Error processing chunk endorsement"); + } + } +} + +/// Returns random seed sampled from the current thread +pub fn random_seed_from_thread() -> RngSeed { + let mut rng_seed: RngSeed = [0; 32]; + rand::thread_rng().fill(&mut rng_seed); + rng_seed +} + +/// Starts client in a separate Arbiter (thread). +pub fn start_client( + client_config: ClientConfig, + chain_genesis: ChainGenesis, + epoch_manager: Arc, + shard_tracker: ShardTracker, + runtime: Arc, + node_id: PeerId, + state_sync_adapter: Arc>, + network_adapter: PeerManagerAdapter, + shards_manager_adapter: Sender, + validator_signer: Option>, + telemetry_actor: Addr, + snapshot_callbacks: Option, + sender: Option>, + adv: crate::adversarial::Controls, + config_updater: Option, +) -> (Addr, ArbiterHandle, ReshardingHandle) { + let client_arbiter = Arbiter::new(); + let client_arbiter_handle = client_arbiter.handle(); + + wait_until_genesis(&chain_genesis.time); + let client = Client::new( + client_config.clone(), + chain_genesis, + epoch_manager, + shard_tracker, + state_sync_adapter, + runtime, + network_adapter.clone(), + shards_manager_adapter, + validator_signer.clone(), + true, + random_seed_from_thread(), + snapshot_callbacks, + ) + .unwrap(); + let resharding_handle = client.chain.resharding_handle.clone(); + let client_addr = ClientActor::start_in_arbiter(&client_arbiter_handle, move |ctx| { + ClientActor::new( + client, + ctx.address(), + client_config, + node_id, + network_adapter, + validator_signer, + telemetry_actor, + ctx, + sender, + adv, + config_updater, + ) + .unwrap() + }); + (client_addr, client_arbiter_handle, resharding_handle) +} diff --git a/chain/client/src/config_updater.rs b/chain/client/src/config_updater.rs new file mode 100644 index 000000000..45079e671 --- /dev/null +++ b/chain/client/src/config_updater.rs @@ -0,0 +1,53 @@ +use unc_chain_configs::UpdateableClientConfig; +use unc_dyn_configs::{UpdateableConfigLoaderError, UpdateableConfigs}; +use std::sync::Arc; +use tokio::sync::broadcast::Receiver; + +#[derive(Debug)] +pub enum ClientConfigUpdateError {} + +/// Manages updating the config encapsulating. +pub struct ConfigUpdater { + /// Receives config updates while the node is running. + rx_config_update: Receiver>>, + + /// Represents the latest Error of reading the dynamically reloadable configs. + updateable_configs_error: Option>, +} + +impl ConfigUpdater { + pub fn new( + rx_config_update: Receiver>>, + ) -> Self { + Self { rx_config_update, updateable_configs_error: None } + } + + /// Check if any of the configs were updated. + /// If they did, the receiver (rx_config_update) will contain a clone of the new configs. + pub fn try_update(&mut self, update_client_config_fn: &dyn Fn(UpdateableClientConfig)) { + while let Ok(maybe_updateable_configs) = self.rx_config_update.try_recv() { + match maybe_updateable_configs { + Ok(updateable_configs) => { + if let Some(client_config) = updateable_configs.client_config { + update_client_config_fn(client_config); + tracing::info!(target: "config", "Updated ClientConfig"); + } + self.updateable_configs_error = None; + } + Err(err) => { + self.updateable_configs_error = Some(err.clone()); + } + } + } + } + + /// Prints an error if it's present. + pub fn report_status(&self) { + if let Some(updateable_configs_error) = &self.updateable_configs_error { + tracing::warn!( + target: "stats", + "Dynamically updateable configs are not valid. Please fix this ASAP otherwise the node will probably crash after restart: {}", + *updateable_configs_error); + } + } +} diff --git a/chain/client/src/debug.rs b/chain/client/src/debug.rs new file mode 100644 index 000000000..191a13f02 --- /dev/null +++ b/chain/client/src/debug.rs @@ -0,0 +1,724 @@ +//! Structs in this file are used for debug purposes, and might change at any time +//! without backwards compatibility. +use crate::ClientActor; +use actix::{Context, Handler}; + +use itertools::Itertools; +use unc_chain::crypto_hash_timer::CryptoHashTimer; +use unc_chain::{unc_chain_primitives, Chain, ChainStoreAccess}; +use unc_client_primitives::debug::{ + ApprovalAtHeightStatus, BlockProduction, ChunkCollection, DebugBlockStatusData, DebugStatus, + DebugStatusResponse, MissedHeightInfo, ProductionAtHeight, ValidatorStatus, +}; +use unc_client_primitives::types::Error; +use unc_client_primitives::{ + debug::{EpochInfoView, TrackedShardsView}, + types::StatusError, +}; +use unc_epoch_manager::EpochManagerAdapter; +use unc_o11y::{handler_debug_span, log_assert, OpenTelemetrySpanExt, WithSpanContext}; +use unc_performance_metrics_macros::perf; +use unc_primitives::state_sync::get_num_state_parts; +use unc_primitives::types::{AccountId, BlockHeight, NumShards, ShardId, ValidatorInfoIdentifier}; +use unc_primitives::{ + hash::CryptoHash, + state_sync::{ShardStateSyncResponseHeader, StateHeaderKey}, + types::EpochId, + views::ValidatorInfo, +}; +use unc_store::DBCol; +use std::cmp::{max, min}; +use std::collections::{HashMap, HashSet}; + +use unc_client_primitives::debug::{DebugBlockStatus, DebugChunkStatus}; +use unc_network::types::{ConnectedPeerInfo, NetworkInfo, PeerType}; +use unc_primitives::sharding::ShardChunkHeader; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::views::{ + AccountDataView, KnownProducerView, NetworkInfoView, PeerInfoView, Tier1ProxyView, +}; + +// Constants for debug requests. +const DEBUG_BLOCKS_TO_FETCH: u32 = 50; +const DEBUG_EPOCHS_TO_FETCH: u32 = 5; + +// How many old blocks (before HEAD) should be shown in debug page. +const DEBUG_PRODUCTION_OLD_BLOCKS_TO_SHOW: u64 = 50; + +// Maximum number of blocks to show. +const DEBUG_MAX_PRODUCTION_BLOCKS_TO_SHOW: u64 = 1000; + +/// Number of blocks (and chunks) for which to keep the detailed timing information for debug purposes. +pub const PRODUCTION_TIMES_CACHE_SIZE: usize = 1000; + +pub struct BlockProductionTracker(lru::LruCache); + +impl BlockProductionTracker { + pub(crate) fn new() -> Self { + Self(lru::LruCache::new(PRODUCTION_TIMES_CACHE_SIZE)) + } + + pub(crate) fn get(&mut self, height: BlockHeight) -> BlockProduction { + self.0.get(&height).cloned().unwrap_or_default() + } + + /// Record approvals received so far for this block. Must be called before block is produced. + pub(crate) fn record_approvals( + &mut self, + height: BlockHeight, + approvals: ApprovalAtHeightStatus, + ) { + // This function will only be called before block production, so it is ok to overwrite + // the previous value + if let Some(prev_block_production) = self.0.put( + height, + BlockProduction { + approvals, + chunks_collection_time: vec![], + block_production_time: None, + block_included: false, + }, + ) { + log_assert!( + prev_block_production.block_production_time.is_none(), + "record approvals called after block {} produced", + height + ); + } + } + + /// Record block production info + pub(crate) fn record_block_production( + &mut self, + height: BlockHeight, + chunk_collections: Vec, + ) { + if let Some(block_production) = self.0.get_mut(&height) { + block_production.block_production_time = Some(StaticClock::utc()); + block_production.chunks_collection_time = chunk_collections; + } + } + + /// Record chunk collected after a block is produced if the block didn't include a chunk for the shard. + /// If called before the block was produced, nothing happens. + pub(crate) fn record_chunk_collected(&mut self, height: BlockHeight, shard_id: ShardId) { + if let Some(block_production) = self.0.get_mut(&height) { + let chunk_collections = &mut block_production.chunks_collection_time; + // Check that chunk_collection is set and we haven't received this chunk yet. + if let Some(chunk_collection) = chunk_collections.get_mut(shard_id as usize) { + if chunk_collection.received_time.is_none() { + chunk_collection.received_time = Some(StaticClock::utc()); + } + } + // Otherwise, it means chunk_collections is not set yet, which means the block wasn't produced. + // And we do nothing in this case. + } + } + + pub(crate) fn construct_chunk_collection_info( + block_height: BlockHeight, + epoch_id: &EpochId, + num_shards: ShardId, + new_chunks: &HashMap, AccountId)>, + epoch_manager: &dyn EpochManagerAdapter, + ) -> Result, Error> { + let mut chunk_collection_info = vec![]; + for shard_id in 0..num_shards { + if let Some((_, chunk_time, chunk_producer)) = new_chunks.get(&shard_id) { + chunk_collection_info.push(ChunkCollection { + chunk_producer: chunk_producer.clone(), + received_time: Some(*chunk_time), + chunk_included: true, + }); + } else { + let chunk_producer = + epoch_manager.get_chunk_producer(epoch_id, block_height, shard_id)?; + chunk_collection_info.push(ChunkCollection { + chunk_producer, + received_time: None, + chunk_included: false, + }); + } + } + Ok(chunk_collection_info) + } +} + +impl Handler> for ClientActor { + type Result = Result; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + match msg { + DebugStatus::SyncStatus => { + Ok(DebugStatusResponse::SyncStatus(self.client.sync_status.clone().into())) + } + DebugStatus::TrackedShards => { + Ok(DebugStatusResponse::TrackedShards(self.get_tracked_shards_view()?)) + } + DebugStatus::EpochInfo => { + Ok(DebugStatusResponse::EpochInfo(self.get_recent_epoch_info()?)) + } + DebugStatus::BlockStatus(height) => { + Ok(DebugStatusResponse::BlockStatus(self.get_last_blocks_info(height)?)) + } + DebugStatus::ValidatorStatus => { + Ok(DebugStatusResponse::ValidatorStatus(self.get_validator_status()?)) + } + DebugStatus::CatchupStatus => { + Ok(DebugStatusResponse::CatchupStatus(self.client.get_catchup_status()?)) + } + DebugStatus::RequestedStateParts => Ok(DebugStatusResponse::RequestedStateParts( + self.client.chain.get_requested_state_parts(), + )), + DebugStatus::ChainProcessingStatus => Ok(DebugStatusResponse::ChainProcessingStatus( + self.client.chain.get_chain_processing_info(), + )), + } + } +} + +impl ClientActor { + // Gets a list of block producers and chunk-only producers for a given epoch. + fn get_producers_for_epoch( + &self, + epoch_id: &EpochId, + last_known_block_hash: &CryptoHash, + ) -> Result<(Vec, Vec), Error> { + let mut block_producers_set = HashSet::new(); + let block_producers: Vec = self + .client + .epoch_manager + .get_epoch_block_producers_ordered(epoch_id, last_known_block_hash)? + .into_iter() + .map(|(validator_stake, is_slashed)| { + block_producers_set.insert(validator_stake.account_id().as_str().to_owned()); + ValidatorInfo { account_id: validator_stake.take_account_id(), is_slashed } + }) + .collect(); + let chunk_only_producers = self + .client + .epoch_manager + .get_epoch_chunk_producers(&epoch_id)? + .iter() + .filter_map(|producer| { + if block_producers_set.contains(&producer.account_id().to_string()) { + None + } else { + Some(producer.account_id().to_string()) + } + }) + .collect::>(); + Ok((block_producers, chunk_only_producers)) + } + + /// Gets the information about the epoch that contains a given block. + /// Also returns the hash of the last block of the previous epoch. + fn get_epoch_info_view( + &mut self, + current_block: CryptoHash, + is_current_block_head: bool, + ) -> Result<(EpochInfoView, CryptoHash), Error> { + let epoch_start_height = + self.client.epoch_manager.get_epoch_start_height(¤t_block)?; + + let block = self.client.chain.get_block_by_height(epoch_start_height)?; + let epoch_id = block.header().epoch_id(); + let (validators, chunk_only_producers) = + self.get_producers_for_epoch(&epoch_id, ¤t_block)?; + + let shards_size_and_parts: Vec<(u64, u64)> = block + .chunks() + .iter() + .enumerate() + .map(|(shard_id, chunk)| { + let state_root_node = self.client.runtime_adapter.get_state_root_node( + shard_id as u64, + block.hash(), + &chunk.prev_state_root(), + ); + if let Ok(state_root_node) = state_root_node { + ( + state_root_node.memory_usage, + get_num_state_parts(state_root_node.memory_usage), + ) + } else { + (0, 0) + } + }) + .collect(); + + let state_header_exists: Vec = (0..block.chunks().len()) + .map(|shard_id| { + let key = borsh::to_vec(&StateHeaderKey(shard_id as u64, *block.hash())); + match key { + Ok(key) => { + matches!( + self.client + .chain + .chain_store() + .store() + .get_ser::(DBCol::StateHeaders, &key), + Ok(Some(_)) + ) + } + Err(_) => false, + } + }) + .collect(); + + let shards_size_and_parts = shards_size_and_parts + .iter() + .zip(state_header_exists.iter()) + .map(|((a, b), c)| (*a, *b, *c)) + .collect(); + + let validator_info = if is_current_block_head { + self.client + .epoch_manager + .get_validator_info(ValidatorInfoIdentifier::BlockHash(current_block))? + } else { + self.client + .epoch_manager + .get_validator_info(ValidatorInfoIdentifier::EpochId(epoch_id.clone()))? + }; + return Ok(( + EpochInfoView { + epoch_id: epoch_id.0, + height: block.header().height(), + first_block: Some((*block.header().hash(), block.header().timestamp())), + block_producers: validators.to_vec(), + chunk_only_producers, + validator_info: Some(validator_info), + protocol_version: self + .client + .epoch_manager + .get_epoch_protocol_version(epoch_id) + .unwrap_or(0), + shards_size_and_parts, + }, + // Last block of the previous epoch. + *block.header().prev_hash(), + )); + } + + fn get_next_epoch_view(&self) -> Result { + let head = self.client.chain.head()?; + let epoch_start_height = + self.client.epoch_manager.get_epoch_start_height(&head.last_block_hash)?; + let (validators, chunk_only_producers) = + self.get_producers_for_epoch(&head.next_epoch_id, &head.last_block_hash)?; + + Ok(EpochInfoView { + epoch_id: head.next_epoch_id.0, + // Expected height of the next epoch. + height: epoch_start_height + self.client.config.epoch_length, + first_block: None, + block_producers: validators, + chunk_only_producers, + validator_info: None, + protocol_version: self + .client + .epoch_manager + .get_epoch_protocol_version(&head.next_epoch_id)?, + shards_size_and_parts: vec![], + }) + } + + fn get_tracked_shards_view(&self) -> Result { + let epoch_id = self.client.chain.header_head()?.epoch_id; + let fetch_hash = self.client.chain.header_head()?.last_block_hash; + let me = self.client.validator_signer.as_ref().map(|x| x.validator_id().clone()); + let shard_ids = self.client.epoch_manager.shard_ids(&epoch_id).unwrap(); + let shards_tracked_this_epoch = shard_ids + .iter() + .map(|&shard_id| { + self.client.shard_tracker.care_about_shard(me.as_ref(), &fetch_hash, shard_id, true) + }) + .collect(); + let shards_tracked_next_epoch = shard_ids + .into_iter() + .map(|shard_id| { + self.client.shard_tracker.will_care_about_shard( + me.as_ref(), + &fetch_hash, + shard_id, + true, + ) + }) + .collect(); + Ok(TrackedShardsView { shards_tracked_this_epoch, shards_tracked_next_epoch }) + } + + fn get_recent_epoch_info( + &mut self, + ) -> Result, unc_chain_primitives::Error> { + // Next epoch id + let mut epochs_info: Vec = Vec::new(); + + if let Ok(next_epoch) = self.get_next_epoch_view() { + epochs_info.push(next_epoch); + } + let head = self.client.chain.head()?; + let mut current_block = head.last_block_hash; + for i in 0..DEBUG_EPOCHS_TO_FETCH { + if let Ok((epoch_view, block_previous_epoch)) = + self.get_epoch_info_view(current_block, i == 0) + { + current_block = block_previous_epoch; + epochs_info.push(epoch_view); + } else { + break; + } + } + Ok(epochs_info) + } + + fn get_last_blocks_info( + &mut self, + starting_height: Option, + ) -> Result { + let head = self.client.chain.head()?; + let header_head = self.client.chain.header_head()?; + + let mut blocks: HashMap = HashMap::new(); + let mut missed_heights: Vec = Vec::new(); + let mut last_epoch_id = head.epoch_id; + let initial_gas_price = self.client.chain.genesis_block().header().next_gas_price(); + + let mut height_to_fetch = starting_height.unwrap_or(header_head.height); + let min_height_to_fetch = + max(height_to_fetch as i64 - DEBUG_BLOCKS_TO_FETCH as i64, 0) as u64; + let mut block_hashes_to_force_fetch = HashSet::new(); + while height_to_fetch > min_height_to_fetch || !block_hashes_to_force_fetch.is_empty() { + let block_hashes = if height_to_fetch > min_height_to_fetch { + let block_hashes: Vec = self + .client + .chain + .chain_store() + .get_all_header_hashes_by_height(height_to_fetch)? + .into_iter() + .collect(); + if block_hashes.is_empty() { + missed_heights.push(MissedHeightInfo { + block_height: height_to_fetch, + block_producer: self + .client + .epoch_manager + .get_block_producer(&last_epoch_id, height_to_fetch) + .ok(), + }); + } + height_to_fetch -= 1; + block_hashes + } else { + let block_hashes = block_hashes_to_force_fetch.iter().cloned().collect(); + block_hashes_to_force_fetch.clear(); + block_hashes + }; + for block_hash in block_hashes { + if blocks.contains_key(&block_hash) { + continue; + } + let block_header = if block_hash == CryptoHash::default() { + self.client.chain.genesis().clone() + } else { + self.client.chain.get_block_header(&block_hash)? + }; + let block = if block_hash == CryptoHash::default() { + Some(self.client.chain.genesis_block().clone()) + } else { + self.client.chain.get_block(&block_hash).ok() + }; + let is_on_canonical_chain = + match self.client.chain.get_block_by_height(block_header.height()) { + Ok(block) => block.hash() == &block_hash, + Err(_) => false, + }; + + let block_producer = self + .client + .epoch_manager + .get_block_producer(block_header.epoch_id(), block_header.height()) + .ok(); + + let chunks = match &block { + Some(block) => block + .chunks() + .iter() + .map(|chunk| DebugChunkStatus { + shard_id: chunk.shard_id(), + chunk_hash: chunk.chunk_hash(), + chunk_producer: self + .client + .epoch_manager + .get_chunk_producer( + block_header.epoch_id(), + block_header.height(), + chunk.shard_id(), + ) + .ok(), + gas_used: chunk.prev_gas_used(), + processing_time_ms: CryptoHashTimer::get_timer_value( + chunk.chunk_hash().0, + ) + .map(|s| s.as_millis() as u64), + }) + .collect(), + None => vec![], + }; + + blocks.insert( + block_hash, + DebugBlockStatus { + block_hash, + prev_block_hash: *block_header.prev_hash(), + block_height: block_header.height(), + block_producer, + full_block_missing: block.is_none(), + is_on_canonical_chain, + chunks, + processing_time_ms: CryptoHashTimer::get_timer_value(block_hash) + .map(|s| s.as_millis() as u64), + block_timestamp: block_header.raw_timestamp(), + gas_price_ratio: block_header.next_gas_price() as f64 + / initial_gas_price as f64, + }, + ); + // TODO(robin): using last epoch id when iterating in reverse height direction is + // not a good idea for calculating producer of missing heights. Revisit this. + last_epoch_id = block_header.epoch_id().clone(); + if let Some(prev_height) = block_header.prev_height() { + if block_header.height() != prev_height + 1 { + // This block was produced using a Skip approval; make sure to fetch the + // previous block even if it's very far back so we can better understand + // the skip. + // TODO(robin): A better heuristic can be used to determine how far back + // to fetch additional blocks. + block_hashes_to_force_fetch.insert(*block_header.prev_hash()); + } + } + } + } + Ok(DebugBlockStatusData { + head: head.last_block_hash, + header_head: header_head.last_block_hash, + missed_heights, + blocks: blocks.into_values().collect(), + }) + } + + /// Returns debugging information about the validator - including things like which approvals were received, which blocks/chunks will be + /// produced and some detailed timing information. + fn get_validator_status(&mut self) -> Result { + let head = self.client.chain.head()?; + let mut productions = vec![]; + + if let Some(signer) = &self.client.validator_signer { + let validator_id = signer.validator_id().to_string(); + + // We want to show some older blocks (up to DEBUG_PRODUCTION_OLD_BLOCKS_TO_SHOW in the past) + // and new blocks (up to the current height for which we've sent approval). + + let estimated_epoch_end = max( + head.height, + self.client.epoch_manager.get_epoch_start_height(&head.last_block_hash)? + + self.client.chain.epoch_length, + ); + let max_height = self.client.doomslug.get_largest_approval_height().clamp( + head.height, + min(head.height + DEBUG_MAX_PRODUCTION_BLOCKS_TO_SHOW, estimated_epoch_end), + ); + + #[allow(clippy::redundant_clone)] + let mut epoch_id = head.epoch_id.clone(); + for height in + head.height.saturating_sub(DEBUG_PRODUCTION_OLD_BLOCKS_TO_SHOW)..=max_height + { + let mut has_block_or_chunks_to_produce = false; + let mut production = ProductionAtHeight::default(); + + // The block may be in the last epoch from head, we need to account for that. + if let Ok(header) = self.client.chain.get_block_header_by_height(height) { + epoch_id = header.epoch_id().clone(); + } + + // And if we are the block (or chunk) producer for this height - collect some timing info. + let block_producer = self + .client + .epoch_manager + .get_block_producer(&epoch_id, height) + .map(|f| f.to_string()) + .unwrap_or_default(); + + let shard_ids = self.client.epoch_manager.shard_ids(&epoch_id)?; + + if block_producer == validator_id { + // For each height - we want to collect information about received approvals. + let mut block_production = self.client.block_production_info.get(height); + block_production.block_included = + self.client.chain.get_block_hash_by_height(height).is_ok(); + production.block_production = Some(block_production); + has_block_or_chunks_to_produce = true; + } + + for shard_id in shard_ids { + let chunk_producer = self + .client + .epoch_manager + .get_chunk_producer(&epoch_id, height, shard_id) + .map(|f| f.to_string()) + .unwrap_or_default(); + if chunk_producer == validator_id { + production.chunk_production.insert( + shard_id, + self.client + .chunk_production_info + .get(&(height, shard_id)) + .cloned() + .unwrap_or_default(), + ); + has_block_or_chunks_to_produce = true; + } + } + if has_block_or_chunks_to_produce { + productions.push((height, production)); + } + } + } + productions.reverse(); + + Ok(ValidatorStatus { + validator_name: self + .client + .validator_signer + .as_ref() + .map(|signer| signer.validator_id().clone()), + // TODO: this might not work correctly when we're at the epoch boundary (as it will just return the validators for the current epoch). + // We can fix it in the future, if we see that this debug page is useful. + validators: self + .client + .epoch_manager + .get_epoch_block_approvers_ordered(&head.last_block_hash) + .map(|validators| { + validators + .iter() + .map(|validator| { + ( + validator.0.account_id.clone(), + (validator.0.frozen_this_epoch / 10u128.pow(12)) as u64, + ) + }) + .collect::>() + }) + .ok(), + head_height: head.height, + shards: self.client.epoch_manager.shard_ids(&head.epoch_id).unwrap_or_default().len() + as NumShards, + approval_history: self.client.doomslug.get_approval_history(), + production: productions, + banned_chunk_producers: self + .client + .do_not_include_chunks_from + .iter() + .map(|(k, _)| k.clone()) + .sorted() + .group_by(|(k, _)| k.clone()) + .into_iter() + .map(|(k, vs)| (k, vs.map(|(_, v)| v).collect())) + .collect(), + }) + } +} +fn new_peer_info_view(chain: &Chain, connected_peer_info: &ConnectedPeerInfo) -> PeerInfoView { + let full_peer_info = &connected_peer_info.full_peer_info; + PeerInfoView { + addr: match full_peer_info.peer_info.addr { + Some(socket_addr) => socket_addr.to_string(), + None => "N/A".to_string(), + }, + account_id: full_peer_info.peer_info.account_id.clone(), + height: full_peer_info.chain_info.last_block.map(|x| x.height), + block_hash: full_peer_info.chain_info.last_block.map(|x| x.hash), + is_highest_block_invalid: full_peer_info + .chain_info + .last_block + .map(|x| chain.is_block_invalid(&x.hash)) + .unwrap_or_default(), + tracked_shards: full_peer_info.chain_info.tracked_shards.clone(), + archival: full_peer_info.chain_info.archival, + peer_id: full_peer_info.peer_info.id.public_key().clone(), + received_bytes_per_sec: connected_peer_info.received_bytes_per_sec, + sent_bytes_per_sec: connected_peer_info.sent_bytes_per_sec, + last_time_peer_requested_millis: connected_peer_info + .last_time_peer_requested + .elapsed() + .whole_milliseconds() as u64, + last_time_received_message_millis: connected_peer_info + .last_time_received_message + .elapsed() + .whole_milliseconds() as u64, + connection_established_time_millis: connected_peer_info + .connection_established_time + .elapsed() + .whole_milliseconds() as u64, + is_outbound_peer: connected_peer_info.peer_type == PeerType::Outbound, + nonce: connected_peer_info.nonce, + } +} + +pub(crate) fn new_network_info_view(chain: &Chain, network_info: &NetworkInfo) -> NetworkInfoView { + NetworkInfoView { + peer_max_count: network_info.peer_max_count, + num_connected_peers: network_info.num_connected_peers, + connected_peers: network_info + .connected_peers + .iter() + .map(|full_peer_info| new_peer_info_view(chain, full_peer_info)) + .collect::>(), + known_producers: network_info + .known_producers + .iter() + .map(|it| KnownProducerView { + account_id: it.account_id.clone(), + peer_id: it.peer_id.public_key().clone(), + next_hops: it + .next_hops + .as_ref() + .map(|it| it.iter().map(|peer_id| peer_id.public_key().clone()).collect()), + }) + .collect(), + tier1_accounts_keys: network_info.tier1_accounts_keys.clone(), + tier1_accounts_data: network_info + .tier1_accounts_data + .iter() + .map(|d| AccountDataView { + peer_id: d.peer_id.public_key().clone(), + proxies: d + .proxies + .iter() + .map(|p| Tier1ProxyView { + addr: p.addr, + peer_id: p.peer_id.public_key().clone(), + }) + .collect(), + account_key: d.account_key.clone(), + timestamp: chrono::DateTime::from_naive_utc_and_offset( + chrono::NaiveDateTime::from_timestamp_opt(d.timestamp.unix_timestamp(), 0) + .unwrap(), + chrono::Utc, + ), + }) + .collect(), + tier1_connections: network_info + .tier1_connections + .iter() + .map(|full_peer_info| new_peer_info_view(chain, full_peer_info)) + .collect::>(), + } +} diff --git a/chain/client/src/info.rs b/chain/client/src/info.rs new file mode 100644 index 000000000..4e342bd43 --- /dev/null +++ b/chain/client/src/info.rs @@ -0,0 +1,953 @@ +use crate::config_updater::ConfigUpdater; +use crate::{metrics, SyncStatus}; +use actix::Addr; +use itertools::Itertools; +use unc_chain_configs::{ClientConfig, LogSummaryStyle, SyncConfig}; +use unc_client_primitives::types::StateSyncStatus; +use unc_network::types::NetworkInfo; +use unc_primitives::block::Tip; +use unc_primitives::network::PeerId; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::telemetry::{ + TelemetryAgentInfo, TelemetryChainInfo, TelemetryInfo, TelemetrySystemInfo, +}; +use unc_primitives::types::{ + AccountId, Balance, BlockHeight, EpochHeight, EpochId, Gas, NumBlocks, ShardId, ValidatorId, + ValidatorInfoIdentifier, +}; +use unc_primitives::unwrap_or_return; +use unc_primitives::validator_signer::ValidatorSigner; +use unc_primitives::version::Version; +use unc_primitives::views::{ + CatchupStatusView, ChunkProcessingStatus, CurrentEpochValidatorInfo, EpochValidatorInfo, + ValidatorKickoutView, +}; +use unc_telemetry::{telemetry, TelemetryActor}; +use std::cmp::min; +use std::collections::HashMap; +use std::fmt::Write; +use std::sync::Arc; +use std::time::Instant; +use sysinfo::{get_current_pid, set_open_files_limit, Pid, ProcessExt, System, SystemExt}; +use tracing::info; + +const TERAGAS: f64 = 1_000_000_000_000_f64; + +struct ValidatorInfoHelper { + pub is_validator: bool, + pub num_validators: usize, +} + +/// A helper that prints information about current chain and reports to telemetry. +pub struct InfoHelper { + /// Nearcore agent (executable) version + nearcore_version: Version, + /// System reference. + sys: System, + /// Process id to query resources. + pid: Option, + /// Timestamp when client was started. + started: Instant, + /// Total number of blocks processed. + num_blocks_processed: u64, + /// Total number of blocks processed. + num_chunks_in_blocks_processed: u64, + /// Total gas used during period. + gas_used: u64, + /// Sign telemetry with block producer key if available. + validator_signer: Option>, + /// Telemetry actor. + // The field can be None for testing. This allows avoiding running actix in tests. + telemetry_actor: Option>, + /// Log coloring enabled. + log_summary_style: LogSummaryStyle, + /// Epoch id. + epoch_id: Option, + /// Timestamp of starting the client. + pub boot_time_seconds: i64, + // Allows more detailed logging, for example a list of orphaned blocks. + enable_multiline_logging: bool, + // Keeps track of the previous SyncRequirement for updating metrics. + prev_sync_requirement: Option, +} + +impl InfoHelper { + pub fn new( + telemetry_actor: Option>, + client_config: &ClientConfig, + validator_signer: Option>, + ) -> Self { + set_open_files_limit(0); + metrics::export_version(&client_config.version); + InfoHelper { + nearcore_version: client_config.version.clone(), + sys: System::new(), + pid: get_current_pid().ok(), + started: StaticClock::instant(), + num_blocks_processed: 0, + num_chunks_in_blocks_processed: 0, + gas_used: 0, + telemetry_actor, + validator_signer, + log_summary_style: client_config.log_summary_style, + boot_time_seconds: StaticClock::utc().timestamp(), + epoch_id: None, + enable_multiline_logging: client_config.enable_multiline_logging, + prev_sync_requirement: None, + } + } + + pub fn chunk_processed(&mut self, shard_id: ShardId, gas_used: Gas, balance_burnt: Balance) { + metrics::TGAS_USAGE_HIST + .with_label_values(&[&shard_id.to_string()]) + .observe(gas_used as f64 / TERAGAS); + metrics::BALANCE_BURNT.inc_by(balance_burnt as f64); + } + + pub fn chunk_skipped(&mut self, shard_id: ShardId) { + metrics::CHUNK_SKIPPED_TOTAL.with_label_values(&[&shard_id.to_string()]).inc(); + } + + pub fn block_processed( + &mut self, + gas_used: Gas, + num_chunks: u64, + gas_price: Balance, + total_supply: Balance, + last_final_block_height: BlockHeight, + last_final_ds_block_height: BlockHeight, + epoch_height: EpochHeight, + last_final_block_height_in_epoch: Option, + ) { + self.num_blocks_processed += 1; + self.num_chunks_in_blocks_processed += num_chunks; + self.gas_used += gas_used; + metrics::GAS_USED.inc_by(gas_used as f64); + metrics::BLOCKS_PROCESSED.inc(); + metrics::CHUNKS_PROCESSED.inc_by(num_chunks); + metrics::GAS_PRICE.set(gas_price as f64); + metrics::TOTAL_SUPPLY.set(total_supply as f64); + metrics::FINAL_BLOCK_HEIGHT.set(last_final_block_height as i64); + metrics::FINAL_DOOMSLUG_BLOCK_HEIGHT.set(last_final_ds_block_height as i64); + metrics::EPOCH_HEIGHT.set(epoch_height as i64); + if let Some(last_final_block_height_in_epoch) = last_final_block_height_in_epoch { + // In rare cases cases the final height isn't updated, for example right after a state sync. + // Don't update the metric in such cases. + metrics::FINAL_BLOCK_HEIGHT_IN_EPOCH.set(last_final_block_height_in_epoch as i64); + } + } + + /// Count which shards are tracked by the node in the epoch indicated by head parameter. + fn record_tracked_shards(head: &Tip, client: &crate::client::Client) { + let me = client.validator_signer.as_ref().map(|x| x.validator_id()); + if let Ok(shard_ids) = client.epoch_manager.shard_ids(&head.epoch_id) { + for shard_id in shard_ids { + let tracked = client.shard_tracker.care_about_shard( + me, + &head.last_block_hash, + shard_id, + true, + ); + metrics::TRACKED_SHARDS + .with_label_values(&[&shard_id.to_string()]) + .set(if tracked { 1 } else { 0 }); + } + } + } + + fn record_block_producers(head: &Tip, client: &crate::client::Client) { + let me = client.validator_signer.as_ref().map(|x| x.validator_id().clone()); + if let Some(is_bp) = me.map_or(Some(false), |account_id| { + // In rare cases block producer information isn't available. + // Don't set the metric in this case. + client + .epoch_manager + .get_epoch_block_producers_ordered(&head.epoch_id, &head.last_block_hash) + .map_or(None, |bp| Some(bp.iter().any(|bp| bp.0.account_id() == &account_id))) + }) { + metrics::IS_BLOCK_PRODUCER.set(if is_bp { 1 } else { 0 }); + } + } + + fn record_chunk_producers(head: &Tip, client: &crate::client::Client) { + if let (Some(account_id), Ok(epoch_info)) = ( + client.validator_signer.as_ref().map(|x| x.validator_id().clone()), + client.epoch_manager.get_epoch_info(&head.epoch_id), + ) { + for (shard_id, validators) in epoch_info.chunk_producers_settlement().iter().enumerate() + { + let is_chunk_producer_for_shard = validators.iter().any(|&validator_id| { + *epoch_info.validator_account_id(validator_id) == account_id + }); + metrics::IS_CHUNK_PRODUCER_FOR_SHARD + .with_label_values(&[&shard_id.to_string()]) + .set(if is_chunk_producer_for_shard { 1 } else { 0 }); + } + } else if let Ok(shard_ids) = client.epoch_manager.shard_ids(&head.epoch_id) { + for shard_id in shard_ids { + metrics::IS_CHUNK_PRODUCER_FOR_SHARD + .with_label_values(&[&shard_id.to_string()]) + .set(0); + } + } + } + + /// The value obtained by multiplying the stake fraction with the expected number of blocks in an epoch + /// is an estimation, and not an exact value. To obtain a more precise result, it is necessary to examine + /// all the blocks in the epoch. However, even this method may not be completely accurate because additional + /// blocks could potentially be added at the end of the epoch. + fn record_epoch_settlement_info(head: &Tip, client: &crate::client::Client) { + let epoch_info = client.epoch_manager.get_epoch_info(&head.epoch_id); + let blocks_in_epoch = client.config.epoch_length; + let shard_ids = client.epoch_manager.shard_ids(&head.epoch_id).unwrap_or_default(); + if let Ok(epoch_info) = epoch_info { + metrics::VALIDATORS_CHUNKS_EXPECTED_IN_EPOCH.reset(); + metrics::VALIDATORS_BLOCKS_EXPECTED_IN_EPOCH.reset(); + metrics::BLOCK_PRODUCER_STAKE.reset(); + + let epoch_height = epoch_info.epoch_height().to_string(); + + let mut stake_per_bp = HashMap::::new(); + + let stake_to_blocks = |stake: Balance, stake_sum: Balance| -> i64 { + if stake == 0 { + 0 + } else { + (((stake as f64) / (stake_sum as f64)) * (blocks_in_epoch as f64)) as i64 + } + }; + + let mut stake_sum = 0; + for &id in epoch_info.block_producers_settlement() { + let stake = epoch_info.validator_frozen(id); + stake_per_bp.insert(id, stake); + stake_sum += stake; + } + + stake_per_bp.iter().for_each(|(&id, &stake)| { + metrics::BLOCK_PRODUCER_STAKE + .with_label_values(&[ + epoch_info.get_validator(id).account_id().as_str(), + &epoch_height, + ]) + .set((stake / 1e24 as u128) as i64); + metrics::VALIDATORS_BLOCKS_EXPECTED_IN_EPOCH + .with_label_values(&[ + epoch_info.get_validator(id).account_id().as_str(), + &epoch_height, + ]) + .set(stake_to_blocks(stake, stake_sum)) + }); + + for shard_id in shard_ids { + let mut stake_per_cp = HashMap::::new(); + stake_sum = 0; + for &id in &epoch_info.chunk_producers_settlement()[shard_id as usize] { + let stake = epoch_info.validator_frozen(id); + stake_per_cp.insert(id, stake); + stake_sum += stake; + } + + stake_per_cp.iter().for_each(|(&id, &stake)| { + metrics::VALIDATORS_CHUNKS_EXPECTED_IN_EPOCH + .with_label_values(&[ + epoch_info.get_validator(id).account_id().as_str(), + &shard_id.to_string(), + &epoch_height, + ]) + .set(stake_to_blocks(stake, stake_sum)) + }); + } + } + } + + /// Records protocol version of the current epoch. + fn record_protocol_version(head: &Tip, client: &crate::client::Client) { + if let Ok(version) = client.epoch_manager.get_epoch_protocol_version(&head.epoch_id) { + metrics::CURRENT_PROTOCOL_VERSION.set(version as i64); + } + } + + /// Print current summary. + pub fn log_summary( + &mut self, + client: &crate::client::Client, + node_id: &PeerId, + network_info: &NetworkInfo, + config_updater: &Option, + ) { + let is_syncing = client.sync_status.is_syncing(); + let head = unwrap_or_return!(client.chain.head()); + let validator_info = if !is_syncing { + let validators = unwrap_or_return!(client + .epoch_manager + .get_epoch_block_producers_ordered(&head.epoch_id, &head.last_block_hash)); + let num_validators = validators.len(); + let account_id = client.validator_signer.as_ref().map(|x| x.validator_id()); + let is_validator = if let Some(account_id) = account_id { + match client.epoch_manager.get_validator_by_account_id( + &head.epoch_id, + &head.last_block_hash, + account_id, + ) { + Ok((_, is_slashed)) => !is_slashed, + Err(_) => false, + } + } else { + false + }; + Some(ValidatorInfoHelper { is_validator, num_validators }) + } else { + None + }; + + let header_head = unwrap_or_return!(client.chain.header_head()); + let validator_epoch_stats = if is_syncing { + // EpochManager::get_validator_info method (which is what runtime + // adapter calls) is expensive when node is syncing so we’re simply + // not collecting the statistics. The statistics are used to update + // a few Prometheus metrics only so we prefer to leave the metrics + // unset until node finishes synchronising. TODO(#6763): If we + // manage to get get_validator_info fasts again (or return an error + // if computation would be too slow), remove the ‘if is_syncing’ + // check. + Default::default() + } else { + let epoch_identifier = ValidatorInfoIdentifier::BlockHash(header_head.last_block_hash); + client + .epoch_manager + .get_validator_info(epoch_identifier) + .map(get_validator_epoch_stats) + .unwrap_or_default() + }; + + InfoHelper::record_tracked_shards(&head, &client); + InfoHelper::record_block_producers(&head, &client); + InfoHelper::record_chunk_producers(&head, &client); + let next_epoch_id = Some(head.epoch_id.clone()); + if self.epoch_id.ne(&next_epoch_id) { + // We only want to compute this once per epoch to avoid heavy computational work, that can last up to 100ms. + InfoHelper::record_epoch_settlement_info(&head, &client); + // This isn't heavy computationally. + InfoHelper::record_protocol_version(&head, &client); + + self.epoch_id = next_epoch_id; + } + + self.info( + &head, + &client.sync_status, + client.get_catchup_status().unwrap_or_default(), + node_id, + network_info, + validator_info, + validator_epoch_stats, + client + .epoch_manager + .get_estimated_protocol_upgrade_block_height(head.last_block_hash) + .unwrap_or(None) + .unwrap_or(0), + &client.config, + config_updater, + ); + self.log_chain_processing_info(client, &head.epoch_id); + } + + fn info( + &mut self, + head: &Tip, + sync_status: &SyncStatus, + catchup_status: Vec, + node_id: &PeerId, + network_info: &NetworkInfo, + validator_info: Option, + validator_epoch_stats: Vec, + protocol_upgrade_block_height: BlockHeight, + client_config: &ClientConfig, + config_updater: &Option, + ) { + let use_color = matches!(self.log_summary_style, LogSummaryStyle::Colored); + let paint = |color: yansi::Color, text: Option| match text { + None => yansi::Paint::default(String::new()), + Some(text) if use_color => yansi::Paint::default(text).fg(color).bold(), + Some(text) => yansi::Paint::default(text), + }; + + let s = |num| if num == 1 { "" } else { "s" }; + + let sync_status_log = + Some(display_sync_status(sync_status, head, &client_config.state_sync.sync)); + let catchup_status_log = display_catchup_status(catchup_status); + let validator_info_log = validator_info.as_ref().map(|info| { + format!( + " {}{} validator{}", + if info.is_validator { "Validator | " } else { "" }, + info.num_validators, + s(info.num_validators) + ) + }); + + let network_info_log = Some(format!( + " {} peer{} ⬇ {} ⬆ {}", + network_info.num_connected_peers, + s(network_info.num_connected_peers), + PrettyNumber::bytes_per_sec(network_info.received_bytes_per_sec), + PrettyNumber::bytes_per_sec(network_info.sent_bytes_per_sec) + )); + + let avg_bls = (self.num_blocks_processed as f64) + / (self.started.elapsed().as_millis() as f64) + * 1000.0; + let avg_gas_used = + ((self.gas_used as f64) / (self.started.elapsed().as_millis() as f64) * 1000.0) as u64; + let blocks_info_log = + Some(format!(" {:.2} bps {}", avg_bls, PrettyNumber::gas_per_sec(avg_gas_used))); + + let proc_info = self.pid.filter(|pid| self.sys.refresh_process(*pid)).map(|pid| { + let proc = + self.sys.process(pid).expect("refresh_process succeeds, this should be not None"); + (proc.cpu_usage(), proc.memory()) + }); + let machine_info_log = proc_info.as_ref().map(|(cpu, mem)| { + format!(" CPU: {:.0}%, Mem: {}", cpu, PrettyNumber::bytes(mem * 1024)) + }); + + info!( + target: "stats", "{}{}{}{}{}", + paint(yansi::Color::Yellow, sync_status_log), + paint(yansi::Color::White, validator_info_log), + paint(yansi::Color::Cyan, network_info_log), + paint(yansi::Color::Green, blocks_info_log), + paint(yansi::Color::Blue, machine_info_log), + ); + if !catchup_status_log.is_empty() { + info!(target: "stats", "Catchups\n{}", catchup_status_log); + } + if let Some(config_updater) = &config_updater { + config_updater.report_status(); + } + let (cpu_usage, memory_usage) = proc_info.unwrap_or_default(); + let is_validator = validator_info.map(|v| v.is_validator).unwrap_or_default(); + (metrics::IS_VALIDATOR.set(is_validator as i64)); + (metrics::RECEIVED_BYTES_PER_SECOND.set(network_info.received_bytes_per_sec as i64)); + (metrics::SENT_BYTES_PER_SECOND.set(network_info.sent_bytes_per_sec as i64)); + (metrics::CPU_USAGE.set(cpu_usage as i64)); + (metrics::MEMORY_USAGE.set((memory_usage * 1024) as i64)); + (metrics::PROTOCOL_UPGRADE_BLOCK_HEIGHT.set(protocol_upgrade_block_height as i64)); + + // In case we can't get the list of validators for the current and the previous epoch, + // skip updating the per-validator metrics. + // Note that the metrics are set to 0 for previous epoch validators who are no longer + // validators. + for stats in validator_epoch_stats { + (metrics::VALIDATORS_BLOCKS_PRODUCED + .with_label_values(&[stats.account_id.as_str()]) + .set(stats.num_produced_blocks as i64)); + (metrics::VALIDATORS_BLOCKS_EXPECTED + .with_label_values(&[stats.account_id.as_str()]) + .set(stats.num_expected_blocks as i64)); + (metrics::VALIDATORS_CHUNKS_PRODUCED + .with_label_values(&[stats.account_id.as_str()]) + .set(stats.num_produced_chunks as i64)); + (metrics::VALIDATORS_CHUNKS_EXPECTED + .with_label_values(&[stats.account_id.as_str()]) + .set(stats.num_expected_chunks as i64)); + for ((shard, expected), produced) in stats + .shards + .iter() + .zip(stats.num_expected_chunks_per_shard.iter()) + .zip(stats.num_produced_chunks_per_shard.iter()) + { + (metrics::VALIDATORS_CHUNKS_EXPECTED_BY_SHARD + .with_label_values(&[stats.account_id.as_str(), &shard.to_string()]) + .set(*expected as i64)); + (metrics::VALIDATORS_CHUNKS_PRODUCED_BY_SHARD + .with_label_values(&[stats.account_id.as_str(), &shard.to_string()]) + .set(*produced as i64)); + } + } + + self.started = StaticClock::instant(); + self.num_blocks_processed = 0; + self.num_chunks_in_blocks_processed = 0; + self.gas_used = 0; + + // In production `telemetry_actor` should always be available. + if let Some(telemetry_actor) = &self.telemetry_actor { + telemetry( + telemetry_actor, + self.telemetry_info( + head, + sync_status, + node_id, + network_info, + client_config, + cpu_usage, + memory_usage, + is_validator, + ), + ); + } + } + + fn telemetry_info( + &self, + head: &Tip, + sync_status: &SyncStatus, + node_id: &PeerId, + network_info: &NetworkInfo, + client_config: &ClientConfig, + cpu_usage: f32, + memory_usage: u64, + is_validator: bool, + ) -> serde_json::Value { + let info = TelemetryInfo { + agent: TelemetryAgentInfo { + name: "unc-rs".to_string(), + version: self.nearcore_version.version.clone(), + build: self.nearcore_version.build.clone(), + }, + system: TelemetrySystemInfo { + bandwidth_download: network_info.received_bytes_per_sec, + bandwidth_upload: network_info.sent_bytes_per_sec, + cpu_usage, + memory_usage, + boot_time_seconds: self.boot_time_seconds, + }, + chain: TelemetryChainInfo { + node_id: node_id.to_string(), + account_id: self.validator_signer.as_ref().map(|bp| bp.validator_id().clone()), + is_validator, + status: sync_status.as_variant_name().to_string(), + latest_block_hash: head.last_block_hash, + latest_block_height: head.height, + num_peers: network_info.num_connected_peers, + block_production_tracking_delay: client_config + .block_production_tracking_delay + .as_secs_f64(), + min_block_production_delay: client_config.min_block_production_delay.as_secs_f64(), + max_block_production_delay: client_config.max_block_production_delay.as_secs_f64(), + max_block_wait_delay: client_config.max_block_wait_delay.as_secs_f64(), + }, + extra_info: serde_json::to_string(&extra_telemetry_info(client_config)).unwrap(), + }; + // Sign telemetry if there is a signer present. + if let Some(vs) = self.validator_signer.as_ref() { + vs.sign_telemetry(&info) + } else { + serde_json::to_value(&info).expect("Telemetry must serialize to json") + } + } + + fn log_chain_processing_info(&mut self, client: &crate::Client, epoch_id: &EpochId) { + let chain = &client.chain; + let use_color = matches!(self.log_summary_style, LogSummaryStyle::Colored); + let info = chain.get_chain_processing_info(); + let blocks_info = BlocksInfo { blocks_info: info.blocks_info, use_color }; + tracing::debug!( + target: "stats", + "{:?} Orphans: {} With missing chunks: {} In processing {}{}", + epoch_id, + info.num_orphans, + info.num_blocks_missing_chunks, + info.num_blocks_in_processing, + if self.enable_multiline_logging { blocks_info.to_string() } else { "".to_owned() }, + ); + } + + // If the `new_sync_requirement` differs from `self.prev_sync_requirement`, + // then increments a corresponding metric. + // Uses `String` instead of `SyncRequirement` to avoid circular dependencies. + pub(crate) fn update_sync_requirements_metrics(&mut self, new_sync_requirement: String) { + // Compare the new SyncRequirement with the previously seen SyncRequirement. + let change = match &self.prev_sync_requirement { + None => Some(new_sync_requirement), + Some(prev_sync) => { + let prev_sync_requirement = format!("{prev_sync}"); + if prev_sync_requirement == new_sync_requirement { + None + } else { + Some(new_sync_requirement) + } + } + }; + if let Some(new_sync_requirement) = change { + // Something change, update the metrics and record it. + metrics::SYNC_REQUIREMENT.with_label_values(&[&new_sync_requirement]).inc(); + metrics::SYNC_REQUIREMENT_CURRENT.with_label_values(&[&new_sync_requirement]).set(1); + if let Some(prev_sync_requirement) = &self.prev_sync_requirement { + metrics::SYNC_REQUIREMENT_CURRENT + .with_label_values(&[&prev_sync_requirement]) + .set(0); + } + metrics::SYNC_REQUIREMENT_CURRENT.with_label_values(&[&new_sync_requirement]).set(1); + self.prev_sync_requirement = Some(new_sync_requirement); + } + } +} + +fn extra_telemetry_info(client_config: &ClientConfig) -> serde_json::Value { + serde_json::json!({ + "block_production_tracking_delay": client_config.block_production_tracking_delay.as_secs_f64(), + "min_block_production_delay": client_config.min_block_production_delay.as_secs_f64(), + "max_block_production_delay": client_config.max_block_production_delay.as_secs_f64(), + "max_block_wait_delay": client_config.max_block_wait_delay.as_secs_f64(), + }) +} + +pub fn display_catchup_status(catchup_status: Vec) -> String { + catchup_status + .into_iter() + .map(|catchup_status| { + let shard_sync_string = catchup_status + .shard_sync_status + .iter() + .sorted_by_key(|x| x.0) + .map(|(shard_id, status_string)| format!("Shard {} {}", shard_id, status_string)) + .join(", "); + let block_catchup_string = if !catchup_status.blocks_to_catchup.is_empty() { + "done".to_string() + } else { + catchup_status + .blocks_to_catchup + .iter() + .map(|block_view| format!("{:?}@{:?}", block_view.hash, block_view.height)) + .join(", ") + }; + format!( + "Sync block {:?}@{:?} \nShard sync status: {}\nNext blocks to catch up: {}", + catchup_status.sync_block_hash, + catchup_status.sync_block_height, + shard_sync_string, + block_catchup_string, + ) + }) + .join("\n") +} + +pub fn display_sync_status( + sync_status: &SyncStatus, + head: &Tip, + state_sync_config: &SyncConfig, +) -> String { + metrics::SYNC_STATUS.set(sync_status.repr() as i64); + match sync_status { + SyncStatus::AwaitingPeers => format!("#{:>8} Waiting for peers", head.height), + SyncStatus::NoSync => format!("#{:>8} {:>44}", head.height, head.last_block_hash), + SyncStatus::EpochSync { epoch_ord } => { + format!("[EPOCH: {:>5}] Getting to a recent epoch", epoch_ord) + } + SyncStatus::HeaderSync { start_height, current_height, highest_height } => { + let percent = if highest_height <= start_height { + 0.0 + } else { + (((min(current_height, highest_height) - start_height) * 100) as f64) + / ((highest_height - start_height) as f64) + }; + format!( + "#{:>8} Downloading headers {:.2}% ({} left; at {})", + head.height, + percent, + highest_height - current_height, + current_height + ) + } + SyncStatus::BlockSync { start_height, current_height, highest_height } => { + let percent = if highest_height <= start_height { + 0.0 + } else { + ((current_height - start_height) * 100) as f64 + / ((highest_height - start_height) as f64) + }; + format!( + "#{:>8} Downloading blocks {:.2}% ({} left; at {})", + head.height, + percent, + highest_height - current_height, + current_height + ) + } + SyncStatus::StateSync(StateSyncStatus { sync_hash, sync_status: shard_statuses }) => { + let mut res = format!("State {:?}", sync_hash); + let mut shard_statuses: Vec<_> = shard_statuses.iter().collect(); + shard_statuses.sort_by_key(|(shard_id, _)| *shard_id); + for (shard_id, shard_status) in shard_statuses { + write!(res, "[{}: {}]", shard_id, shard_status.status.to_string(),).unwrap(); + } + if let SyncConfig::Peers = state_sync_config { + tracing::warn!( + target: "stats", + "The node is trying to sync its State from its peers. The current implementation of this mechanism is known to be unreliable. It may never complete, or fail randomly and corrupt the DB.\n\ + Suggestions:\n\ + * Try to state sync from GCS. See `\"state_sync\"` and `\"state_sync_enabled\"` options in the reference `config.json` file. + or + * Disable state sync in the config. Add `\"state_sync_enabled\": false` to `config.json`, then download a recent data snapshot and restart the node."); + }; + res + } + SyncStatus::StateSyncDone => "State sync done".to_string(), + } +} + +/// Displays ` {} for {}ms` if second item is `Some`. +struct FormatMillis(&'static str, Option); + +impl std::fmt::Display for FormatMillis { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.1.map_or(Ok(()), |ms| write!(fmt, " {} for {ms}ms", self.0)) + } +} + +/// Formats information about each block. Each information line is *preceded* +/// by a new line character. There’s no final new line character. This is +/// meant to be used in logging where final new line is not desired. +struct BlocksInfo { + blocks_info: Vec, + use_color: bool, +} + +impl std::fmt::Display for BlocksInfo { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let paint = |color: yansi::Color, text: String| { + if self.use_color { + yansi::Paint::default(text).fg(color).bold() + } else { + yansi::Paint::default(text) + } + }; + + for block_info in self.blocks_info.iter() { + let mut all_chunks_received = true; + let chunk_status = block_info + .chunks_info + .iter() + .map(|chunk_info| { + if let Some(chunk_info) = chunk_info { + all_chunks_received &= + matches!(chunk_info.status, ChunkProcessingStatus::Completed); + match chunk_info.status { + ChunkProcessingStatus::Completed => '✔', + ChunkProcessingStatus::Requested => '⬇', + ChunkProcessingStatus::NeedToRequest => '.', + } + } else { + 'X' + } + }) + .collect::(); + + let chunk_status_color = + if all_chunks_received { yansi::Color::Green } else { yansi::Color::White }; + + let chunk_status = paint(chunk_status_color, chunk_status); + let in_progress = FormatMillis("in progress", Some(block_info.in_progress_ms)); + let in_orphan = FormatMillis("orphan", block_info.orphaned_ms); + let missing_chunks = FormatMillis("missing chunks", block_info.missing_chunks_ms); + + write!( + fmt, + "\n {} {} {:?}{in_progress}{in_orphan}{missing_chunks} Chunks:({chunk_status}))", + block_info.height, block_info.hash, block_info.block_status, + )?; + } + + Ok(()) + } +} + +/// Format number using SI prefixes. +struct PrettyNumber(u64, &'static str); + +impl PrettyNumber { + fn bytes_per_sec(bps: u64) -> Self { + Self(bps, "B/s") + } + + fn bytes(bytes: u64) -> Self { + Self(bytes, "B") + } + + fn gas_per_sec(gps: u64) -> Self { + Self(gps, "gas/s") + } +} + +impl std::fmt::Display for PrettyNumber { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self(mut num, unit) = self; + if num < 1_000 { + return write!(f, "{} {}", num, unit); + } + for prefix in b"kMGTPE" { + if num < 1_000_000 { + let precision = if num < 10_000 { + 2 + } else if num < 100_000 { + 1 + } else { + 0 + }; + return write!( + f, + "{:.*} {}{}", + precision, + num as f64 / 1_000.0, + *prefix as char, + unit + ); + } + num /= 1000; + } + unreachable!() + } +} + +/// Number of blocks and chunks produced and expected by a certain validator. +pub struct ValidatorProductionStats { + pub account_id: AccountId, + pub num_produced_blocks: NumBlocks, + pub num_expected_blocks: NumBlocks, + pub num_produced_chunks: NumBlocks, + pub num_expected_chunks: NumBlocks, + pub shards: Vec, + pub num_produced_chunks_per_shard: Vec, + pub num_expected_chunks_per_shard: Vec, +} + +impl ValidatorProductionStats { + pub fn kickout(kickout: ValidatorKickoutView) -> Self { + Self { + account_id: kickout.account_id, + num_produced_blocks: 0, + num_expected_blocks: 0, + num_produced_chunks: 0, + num_expected_chunks: 0, + shards: vec![], + num_produced_chunks_per_shard: vec![], + num_expected_chunks_per_shard: vec![], + } + } + pub fn validator(info: CurrentEpochValidatorInfo) -> Self { + Self { + account_id: info.account_id, + num_produced_blocks: info.num_produced_blocks, + num_expected_blocks: info.num_expected_blocks, + num_produced_chunks: info.num_produced_chunks, + num_expected_chunks: info.num_expected_chunks, + shards: info.shards, + num_produced_chunks_per_shard: info.num_produced_chunks_per_shard, + num_expected_chunks_per_shard: info.num_expected_chunks_per_shard, + } + } +} + +/// Converts EpochValidatorInfo into a vector of ValidatorProductionStats. +fn get_validator_epoch_stats( + current_validator_epoch_info: EpochValidatorInfo, +) -> Vec { + let mut stats = vec![]; + // Record kickouts to replace latest stats of kicked out validators with zeros. + for kickout in current_validator_epoch_info.prev_epoch_kickout { + stats.push(ValidatorProductionStats::kickout(kickout)); + } + for validator in current_validator_epoch_info.current_validators { + stats.push(ValidatorProductionStats::validator(validator)); + } + stats +} + +#[cfg(test)] +mod tests { + use super::*; + use assert_matches::assert_matches; + use unc_chain::test_utils::{KeyValueRuntime, MockEpochManager, ValidatorSchedule}; + use unc_chain::types::ChainConfig; + use unc_chain::{Chain, ChainGenesis, DoomslugThresholdMode}; + use unc_epoch_manager::shard_tracker::ShardTracker; + use unc_network::test_utils::peer_id_from_seed; + use unc_primitives::version::PROTOCOL_VERSION; + use num_rational::Ratio; + + #[test] + fn test_pretty_number() { + for (want, num) in [ + ("0 U", 0), + ("1 U", 1), + ("10 U", 10), + ("100 U", 100), + ("1.00 kU", 1_000), + ("10.0 kU", 10_000), + ("100 kU", 100_000), + ("1.00 MU", 1_000_000), + ("10.0 MU", 10_000_000), + ("100 MU", 100_000_000), + ("18.4 EU", u64::MAX), + ] { + let got = PrettyNumber(num, "U").to_string(); + assert_eq!(want, &got, "num={}", num); + } + } + + #[test] + fn telemetry_info() { + let config = ClientConfig::test(false, 1230, 2340, 50, false, true, true, true); + let info_helper = InfoHelper::new(None, &config, None); + + let store = unc_store::test_utils::create_test_store(); + let vs = + ValidatorSchedule::new().block_producers_per_epoch(vec![vec!["test".parse().unwrap()]]); + let epoch_manager = MockEpochManager::new_with_validators(store.clone(), vs, 123); + let shard_tracker = ShardTracker::new_empty(epoch_manager.clone()); + let runtime = KeyValueRuntime::new(store, epoch_manager.as_ref()); + let chain_genesis = ChainGenesis { + time: StaticClock::utc(), + height: 0, + gas_limit: 1_000_000, + min_gas_price: 100, + max_gas_price: 1_000_000_000, + total_supply: 3_000_000_000_000_000_000_000_000_000_000_000, + gas_price_adjustment_rate: Ratio::from_integer(0), + transaction_validity_period: 123123, + epoch_length: 123, + protocol_version: PROTOCOL_VERSION, + }; + let doomslug_threshold_mode = DoomslugThresholdMode::TwoThirds; + let chain = Chain::new( + epoch_manager, + shard_tracker, + runtime, + &chain_genesis, + doomslug_threshold_mode, + ChainConfig::test(), + None, + ) + .unwrap(); + + let telemetry = info_helper.telemetry_info( + &chain.head().unwrap(), + &SyncStatus::AwaitingPeers, + &peer_id_from_seed("zxc"), + &NetworkInfo { + connected_peers: vec![], + num_connected_peers: 0, + peer_max_count: 0, + highest_height_peers: vec![], + sent_bytes_per_sec: 0, + received_bytes_per_sec: 0, + known_producers: vec![], + tier1_connections: vec![], + tier1_accounts_keys: vec![], + tier1_accounts_data: vec![], + }, + &config, + 0.0, + 0, + false, + ); + println!("Got telemetry info: {:?}", telemetry); + assert_matches!( + telemetry["extra_info"].as_str().unwrap().find("\"max_block_production_delay\":2.34,"), + Some(_) + ); + } +} diff --git a/chain/client/src/lib.rs b/chain/client/src/lib.rs new file mode 100644 index 000000000..6f3c540bd --- /dev/null +++ b/chain/client/src/lib.rs @@ -0,0 +1,37 @@ +pub use unc_client_primitives::types::{ + Error, GetBlock, GetBlockProof, GetBlockProofResponse, GetBlockWithMerkleTree, GetChunk, + GetClientConfig, GetExecutionOutcome, GetExecutionOutcomeResponse, + GetExecutionOutcomesForBlock, GetGasPrice, GetMaintenanceWindows, GetNetworkInfo, + GetNextLightClientBlock, GetProtocolConfig, GetReceipt, GetSplitStorageInfo, GetStateChanges, + GetStateChangesInBlock, GetStateChangesWithCauseInBlock, + GetStateChangesWithCauseInBlockForTrackedShards, GetValidatorInfo, GetValidatorOrdered, Query, + QueryError, Status, StatusResponse, SyncStatus, TxStatus, TxStatusError, +}; + +pub use crate::adapter::{ + BlockApproval, BlockResponse, ProcessTxRequest, ProcessTxResponse, SetNetworkInfo, +}; +pub use crate::client::Client; +#[cfg(feature = "test_features")] +pub use crate::client_actor::NetworkAdversarialMessage; +pub use crate::client_actor::{start_client, ClientActor}; +pub use crate::config_updater::ConfigUpdater; +pub use crate::sync::adapter::{SyncAdapter, SyncMessage}; +pub use crate::view_client::{start_view_client, ViewClientActor}; +pub use unc_client_primitives::debug::DebugStatus; + +pub mod adapter; +pub mod adversarial; +mod chunk_validation; +mod client; +mod client_actor; +mod config_updater; +pub mod debug; +mod info; +mod metrics; +pub mod sync; +mod sync_jobs_actor; +pub mod test_utils; +#[cfg(test)] +mod tests; +mod view_client; diff --git a/chain/client/src/metrics.rs b/chain/client/src/metrics.rs new file mode 100644 index 000000000..b6e7d205d --- /dev/null +++ b/chain/client/src/metrics.rs @@ -0,0 +1,544 @@ +use unc_o11y::metrics::{ + exponential_buckets, try_create_counter, try_create_gauge, try_create_histogram, + try_create_histogram_vec, try_create_int_counter, try_create_int_counter_vec, + try_create_int_gauge, try_create_int_gauge_vec, Counter, Gauge, Histogram, HistogramVec, + IntCounter, IntCounterVec, IntGauge, IntGaugeVec, +}; +use once_cell::sync::Lazy; + +pub(crate) static BLOCK_PRODUCED_TOTAL: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_block_produced_total", + "Total number of blocks produced since starting this node", + ) + .unwrap() +}); + +pub(crate) static CHUNK_PRODUCED_TOTAL: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_chunk_produced_total", + "Total number of chunks produced since starting this node", + ) + .unwrap() +}); + +pub(crate) static IS_VALIDATOR: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_is_validator", + "Bool to denote if it is validating in the current epoch", + ) + .unwrap() +}); + +pub(crate) static IS_BLOCK_PRODUCER: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_is_block_producer", + "Bool to denote if the node is a block producer in the current epoch", + ) + .unwrap() +}); + +pub(crate) static IS_CHUNK_PRODUCER_FOR_SHARD: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_is_chunk_producer_for_shard", + "Bool to denote if the node is a chunk producer for a shard in the current epoch", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static RECEIVED_BYTES_PER_SECOND: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_received_bytes_per_second", + "Number of bytes per second received over the network overall", + ) + .unwrap() +}); + +pub(crate) static SENT_BYTES_PER_SECOND: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_sent_bytes_per_second", + "Number of bytes per second sent over the network overall", + ) + .unwrap() +}); + +pub(crate) static CPU_USAGE: Lazy = + Lazy::new(|| try_create_int_gauge("unc_cpu_usage_ratio", "Percent of CPU usage").unwrap()); + +pub(crate) static MEMORY_USAGE: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_memory_usage_bytes", "Amount of RAM memory usage").unwrap() +}); + +pub(crate) static GC_TIME: Lazy = Lazy::new(|| { + try_create_histogram("unc_gc_time", "Time taken to do garbage collection").unwrap() +}); + +pub(crate) static TGAS_USAGE_HIST: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_chunk_tgas_used_hist", + "Number of Tgas (10^12 of gas) used by processed chunks, as a histogram", + &["shard"], + Some(vec![ + 50., 100., 300., 500., 700., 800., 900., 950., 1000., 1050., 1100., 1150., 1200., + 1250., 1300., + ]), + ) + .unwrap() +}); + +pub(crate) static VALIDATORS_CHUNKS_PRODUCED: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_validators_chunks_produced", + "Number of chunks produced by a validator", + &["account_id"], + ) + .unwrap() +}); + +pub(crate) static VALIDATORS_CHUNKS_EXPECTED: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_validators_chunks_expected", + "Number of chunks expected to be produced by a validator", + &["account_id"], + ) + .unwrap() +}); + +pub(crate) static VALIDATORS_CHUNKS_PRODUCED_BY_SHARD: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_validators_chunks_produced_by_shard", + "Number of chunks produced by a validator", + &["account_id", "shard_id"], + ) + .unwrap() +}); + +pub(crate) static VALIDATORS_CHUNKS_EXPECTED_BY_SHARD: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_validators_chunks_expected_by_shard", + "Number of chunks expected to be produced by a validator", + &["account_id", "shard_id"], + ) + .unwrap() +}); + +pub(crate) static VALIDATORS_CHUNKS_EXPECTED_IN_EPOCH: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_validators_chunks_expected_in_epoch", + "Number of chunks expected to be produced by a validator within current epoch", + &["account_id", "shard_id", "epoch_height"], + ) + .unwrap() +}); + +pub(crate) static VALIDATORS_BLOCKS_PRODUCED: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_validators_blocks_produced", + "Number of blocks produced by a validator", + &["account_id"], + ) + .unwrap() +}); + +pub(crate) static VALIDATORS_BLOCKS_EXPECTED: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_validators_blocks_expected", + "Number of blocks expected to be produced by a validator", + &["account_id"], + ) + .unwrap() +}); + +pub(crate) static VALIDATORS_BLOCKS_EXPECTED_IN_EPOCH: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_validators_blocks_expected_in_epoch", + "Number of blocks expected to be produced by a validator within current epoch", + &["account_id", "epoch_height"], + ) + .unwrap() +}); + +pub(crate) static BLOCK_PRODUCER_STAKE: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_block_producer_stake", + "Stake of each block producer in the network", + &["account_id", "epoch_height"], + ) + .unwrap() +}); + +pub(crate) static TRACKED_SHARDS: Lazy = Lazy::new(|| { + try_create_int_gauge_vec("unc_client_tracked_shards", "Tracked shards", &["shard_id"]).unwrap() +}); + +pub(crate) static SYNC_STATUS: Lazy = + Lazy::new(|| try_create_int_gauge("unc_sync_status", "Node sync status").unwrap()); + +pub(crate) static EPOCH_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_epoch_height", "Height of the epoch at the head of the blockchain") + .unwrap() +}); + +pub(crate) static FINAL_BLOCK_HEIGHT_IN_EPOCH: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_final_block_height_in_epoch", + "Height of the last block within the epoch.", + ) + .unwrap() +}); + +pub(crate) static PROTOCOL_UPGRADE_BLOCK_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_protocol_upgrade_block_height", + "Estimated block height of the protocol upgrade", + ) + .unwrap() +}); + +pub(crate) static PEERS_WITH_INVALID_HASH: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_peers_with_invalid_hash", "Number of peers that are on invalid hash") + .unwrap() +}); + +pub(crate) static CHUNK_SKIPPED_TOTAL: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_chunk_skipped_total", + "Number of skipped chunks", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static CHUNK_PRODUCER_BANNED_FOR_EPOCH: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_chunk_producer_banned_for_epoch", + "Number of times we have banned a chunk producer for an epoch", + ) + .unwrap() +}); + +pub(crate) static CHUNK_DROPPED_BECAUSE_OF_BANNED_CHUNK_PRODUCER: Lazy = + Lazy::new(|| { + try_create_int_counter( + "unc_chunk_dropped_because_of_banned_chunk_producer", + "Number of chunks we, as a block producer, + dropped, because the chunk is produced by a banned chunk producer", + ) + .unwrap() + }); + +pub(crate) static CLIENT_MESSAGES_COUNT: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_client_messages_count", + "Number of messages client actor received by message type", + &["type"], + ) + .unwrap() +}); + +pub(crate) static CLIENT_MESSAGES_PROCESSING_TIME: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_client_messages_processing_time", + "Processing time of messages that client actor received, sorted by message type", + &["type"], + Some(exponential_buckets(0.0001, 1.6, 20).unwrap()), + ) + .unwrap() +}); + +pub(crate) static CHECK_TRIGGERS_TIME: Lazy = Lazy::new(|| { + try_create_histogram( + "unc_client_triggers_time", + "Processing time of the check_triggers function in client", + ) + .unwrap() +}); + +pub(crate) static CLIENT_TRIGGER_TIME_BY_TYPE: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_client_triggers_time_by_type", + "Time spent on the different triggers in client", + &["trigger"], + Some(exponential_buckets(0.0001, 1.6, 20).unwrap()), + ) + .unwrap() +}); + +pub(crate) static GAS_USED: Lazy = Lazy::new(|| { + try_create_counter("unc_gas_used", "Gas used by processed blocks, measured in gas").unwrap() +}); + +pub(crate) static BLOCKS_PROCESSED: Lazy = Lazy::new(|| { + try_create_int_counter("unc_blocks_processed", "Number of processed blocks").unwrap() +}); + +pub(crate) static CHUNKS_PROCESSED: Lazy = Lazy::new(|| { + try_create_int_counter("unc_chunks_processed", "Number of processed chunks").unwrap() +}); + +pub(crate) static GAS_PRICE: Lazy = Lazy::new(|| { + try_create_gauge("unc_gas_price", "Gas price of the latest processed block").unwrap() +}); + +pub(crate) static BALANCE_BURNT: Lazy = Lazy::new(|| { + try_create_counter("unc_balance_burnt", "Balance burnt by processed blocks in NEAR tokens") + .unwrap() +}); + +pub(crate) static TOTAL_SUPPLY: Lazy = Lazy::new(|| { + try_create_gauge("unc_total_supply", "Gas price of the latest processed block").unwrap() +}); + +pub(crate) static FINAL_BLOCK_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_final_block_height", "Last block that has full BFT finality") + .unwrap() +}); + +pub(crate) static FINAL_DOOMSLUG_BLOCK_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_final_doomslug_block_height", + "Last block that has Doomslug finality", + ) + .unwrap() +}); + +static NODE_DB_VERSION: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_node_db_version", "DB version used by the node").unwrap() +}); + +static NODE_BUILD_INFO: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_build_info", + "Metric whose labels indicate node’s version; see \ + .", + &["release", "build", "rustc_version"], + ) + .unwrap() +}); + +pub(crate) static TRANSACTION_RECEIVED_VALIDATOR: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_transaction_received_validator", "Validator received a transaction") + .unwrap() +}); + +pub(crate) static TRANSACTION_RECEIVED_NON_VALIDATOR: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_transaction_received_non_validator", + "Non-validator received a transaction", + ) + .unwrap() +}); + +pub(crate) static TRANSACTION_RECEIVED_NON_VALIDATOR_FORWARDED: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_transaction_received_non_validator_forwarded", + "Non-validator received a forwarded transaction", + ) + .unwrap() +}); + +pub(crate) static NODE_PROTOCOL_VERSION: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_node_protocol_version", "Max protocol version supported by the node") + .unwrap() +}); + +pub(crate) static CURRENT_PROTOCOL_VERSION: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_current_protocol_version", "Protocol version of the current epoch") + .unwrap() +}); + +pub(crate) static NODE_PROTOCOL_UPGRADE_VOTING_START: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_node_protocol_upgrade_voting_start", + "Time in seconds since Unix epoch determining when node will start voting for the protocol upgrade; zero if there is no schedule for the voting") + .unwrap() +}); + +pub(crate) static PRODUCE_CHUNK_TIME: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_produce_chunk_time", + "Time taken to produce a chunk", + &["shard_id"], + Some(exponential_buckets(0.001, 2.0, 16).unwrap()), + ) + .unwrap() +}); + +pub(crate) static VIEW_CLIENT_MESSAGE_TIME: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_view_client_messages_processing_time", + "Time that view client takes to handle different messages", + &["message"], + Some(exponential_buckets(0.001, 2.0, 16).unwrap()), + ) + .unwrap() +}); + +pub(crate) static PRODUCE_AND_DISTRIBUTE_CHUNK_TIME: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_produce_and_distribute_chunk_time", + "Time to produce a chunk and distribute it to peers", + &["shard_id"], + Some(exponential_buckets(0.001, 2.0, 16).unwrap()), + ) + .unwrap() +}); +/// Exports uncd, protocol and database versions via Prometheus metrics. +/// +/// Sets metrics which export node’s max supported protocol version, used +/// database version and build information. The latter is taken from +/// `neard_version` argument. +pub(crate) fn export_version(neard_version: &unc_primitives::version::Version) { + NODE_PROTOCOL_VERSION.set(unc_primitives::version::PROTOCOL_VERSION.into()); + NODE_PROTOCOL_UPGRADE_VOTING_START + .set(unc_primitives::version::PROTOCOL_UPGRADE_SCHEDULE.timestamp()); + NODE_DB_VERSION.set(unc_store::metadata::DB_VERSION.into()); + NODE_BUILD_INFO.reset(); + NODE_BUILD_INFO + .with_label_values(&[ + &neard_version.version, + &neard_version.build, + &neard_version.rustc_version, + ]) + .inc(); +} + +pub(crate) static STATE_SYNC_STAGE: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_state_sync_stage", + "Stage of state sync per shard", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_RETRY_PART: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_state_sync_retry_part_total", + "Number of part requests retried", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_HEADER_ERROR: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_state_sync_header_error_total", + "Number of state sync header requests resulting in an error", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_HEADER_TIMEOUT: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_state_sync_header_timeout_total", + "Number of state sync header requests timing out", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_PARTS_DONE: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_state_sync_parts_done", + "Number of parts downloaded", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_PARTS_TOTAL: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_state_sync_parts_per_shard", + "Number of parts in the shard", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_DISCARD_PARTS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_state_sync_discard_parts_total", + "Number of times all downloaded parts were discarded to try again", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_EXTERNAL_PARTS_DONE: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_state_sync_external_parts_done_total", + "Number of parts retrieved from external storage", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_EXTERNAL_PARTS_FAILED: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_state_sync_external_parts_failed_total", + "Failed retrieval attempts from external storage", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_EXTERNAL_PARTS_REQUEST_DELAY: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_state_sync_external_parts_request_delay_sec", + "Latency of state part requests to external storage", + &["shard_id"], + Some(exponential_buckets(0.001, 2.0, 20).unwrap()), + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_EXTERNAL_PARTS_SIZE_DOWNLOADED: Lazy = + Lazy::new(|| { + try_create_int_counter_vec( + "unc_state_sync_external_parts_size_downloaded_bytes_total", + "Bytes downloaded from an external storage", + &["shard_id"], + ) + .unwrap() + }); + +pub(crate) static STATE_SYNC_DUMP_PUT_OBJECT_ELAPSED: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_state_sync_dump_put_object_elapsed_sec", + "Latency of writes to external storage", + &["shard_id", "result"], + Some(exponential_buckets(0.001, 1.6, 25).unwrap()), + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_DUMP_LIST_OBJECT_ELAPSED: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_state_sync_dump_list_object_elapsed_sec", + "Latency of ls in external storage", + &["shard_id"], + Some(exponential_buckets(0.001, 1.6, 25).unwrap()), + ) + .unwrap() +}); + +pub(crate) static SYNC_REQUIREMENT: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_sync_requirements_total", + "Number of sync was required", + &["state"], + ) + .unwrap() +}); + +pub(crate) static SYNC_REQUIREMENT_CURRENT: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_sync_requirements_current", + "The latest SyncRequirement", + &["state"], + ) + .unwrap() +}); diff --git a/chain/client/src/sync/adapter.rs b/chain/client/src/sync/adapter.rs new file mode 100644 index 000000000..541bd2871 --- /dev/null +++ b/chain/client/src/sync/adapter.rs @@ -0,0 +1,116 @@ +use super::sync_actor::SyncActor; +use actix::dev::ToEnvelope; +use actix::prelude::SendError; +use actix::{Actor, Message}; +use actix_rt::Arbiter; +use core::fmt::Debug; +use unc_async::messaging::Sender; +use unc_network::types::{ + PeerManagerMessageRequest, StateSync as NetworkStateSync, StateSyncResponse, +}; +use unc_o11y::WithSpanContextExt; +use unc_primitives::hash::CryptoHash; +use unc_store::ShardUId; +use std::collections::HashMap; +use tracing::warn; + +/// Information about the shard being synced +#[derive(Debug)] +pub struct SyncShardInfo { + pub shard_uid: ShardUId, + pub sync_hash: CryptoHash, +} + +/// Messages between Client and Sync Actor +#[derive(Message, Debug)] +#[rtype(result = "()")] +pub enum SyncMessage { + /// Notify an active actor to start syncing + StartSync(SyncShardInfo), + /// Notify the client that the work is done + SyncDone(SyncShardInfo), +} + +struct ActorHandler { + /// Address of actor mailbox + addr: actix::Addr, + /// Thread handler of actor + arbiter: actix::Arbiter, +} + +/// Manager for state sync threads. +/// Offers functions to interact with the sync actors, such as start, stop and send messages. +pub struct SyncAdapter { + /// Address of the sync actors indexed by the SharUid + actor_handler_map: HashMap, + /// Message channel for client + client_adapter: Sender, + /// Message channel with network + network_adapter: Sender, +} + +impl SyncAdapter { + pub fn new( + client_adapter: Sender, + network_adapter: Sender, + ) -> Self { + Self { actor_handler_map: [].into(), client_adapter, network_adapter } + } + + /// Starts a new arbiter and runs the actor on it + pub fn start(&mut self, shard_uid: ShardUId) { + assert!(!self.actor_handler_map.contains_key(&shard_uid), "Actor already started."); + let arbiter = Arbiter::new(); + let arbiter_handle = arbiter.handle(); + let client = self.client_adapter.clone(); + let network = self.network_adapter.clone(); + let addr = SyncActor::start_in_arbiter(&arbiter_handle, move |_ctx| { + SyncActor::new(shard_uid, client, network) + }); + self.actor_handler_map.insert(shard_uid, ActorHandler { addr, arbiter }); + } + + /// Stop the actor and remove it + pub fn stop(&mut self, shard_uid: ShardUId) { + self.actor_handler_map.remove(&shard_uid).expect("Actor not started.").arbiter.stop(); + } + + pub fn stop_all(&mut self) { + self.actor_handler_map.drain().for_each(|(_shard_uid, handler)| { + handler.arbiter.stop(); + }); + } + + /// Forward message to the right shard + pub fn send(&self, shard_uid: ShardUId, msg: M) + where + M: Message + Send + 'static, + M::Result: Send, + SyncActor: actix::Handler, + ::Context: ToEnvelope, + { + let handler = self.actor_handler_map.get(&shard_uid); + match handler { + None => { + warn!(target: "sync", ?shard_uid, "Tried sending message to non existing actor.") + } + Some(handler) => match handler.addr.try_send(msg) { + Ok(_) => {} + Err(SendError::Closed(_)) => { + warn!(target: "sync", ?shard_uid, "Error sending message, mailbox is closed.") + } + Err(SendError::Full(_)) => { + warn!(target: "sync", ?shard_uid, "Error sending message, mailbox is full.") + } + }, + } + } +} + +/// Interface for network +#[async_trait::async_trait] +impl NetworkStateSync for SyncAdapter { + async fn send(&self, shard_uid: ShardUId, msg: StateSyncResponse) { + self.send(shard_uid, msg.with_span_context()); + } +} diff --git a/chain/client/src/sync/block.rs b/chain/client/src/sync/block.rs new file mode 100644 index 000000000..8ebf4fbf9 --- /dev/null +++ b/chain/client/src/sync/block.rs @@ -0,0 +1,480 @@ +use chrono::{DateTime, Duration, Utc}; +use unc_async::messaging::CanSend; +use unc_chain::Chain; +use unc_chain::{check_known, ChainStoreAccess}; +use unc_client_primitives::types::SyncStatus; +use unc_network::types::PeerManagerMessageRequest; +use unc_network::types::{HighestHeightPeerInfo, NetworkRequests, PeerManagerAdapter}; +use unc_o11y::log_assert; +use unc_primitives::block::Tip; +use unc_primitives::hash::CryptoHash; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::types::{BlockHeight, BlockHeightDelta}; +use rand::seq::IteratorRandom; +use tracing::{debug, warn}; + +/// Maximum number of block requested at once in BlockSync +const MAX_BLOCK_REQUESTS: usize = 5; + +/// Expect to receive the requested block in this time. +const BLOCK_REQUEST_TIMEOUT_MS: i64 = 2_000; + +#[derive(Clone)] +pub struct BlockSyncRequest { + // Head of the chain at the time of the last requests. + head: CryptoHash, + // When the last requests were made. + when: DateTime, +} + +/// Helper to track block syncing. +pub struct BlockSync { + network_adapter: PeerManagerAdapter, + + // When the last block requests were made. + last_request: Option, + + /// How far to fetch blocks vs fetch state. + block_fetch_horizon: BlockHeightDelta, + + /// Archival nodes are not allowed to do State Sync, as they need all state from all blocks. + archive: bool, + + /// Whether State Sync should be enabled when a node falls far enough behind. + state_sync_enabled: bool, +} + +impl BlockSync { + pub fn new( + network_adapter: PeerManagerAdapter, + block_fetch_horizon: BlockHeightDelta, + archive: bool, + state_sync_enabled: bool, + ) -> Self { + BlockSync { + network_adapter, + last_request: None, + block_fetch_horizon, + archive, + state_sync_enabled, + } + } + + /// Returns true if State Sync is needed. + /// Returns false is Block Sync is needed. Maybe requests a few blocks from peers. + pub fn run( + &mut self, + sync_status: &mut SyncStatus, + chain: &Chain, + highest_height: BlockHeight, + highest_height_peers: &[HighestHeightPeerInfo], + ) -> Result { + let _span = tracing::debug_span!(target: "sync", "run", sync = "BlockSync").entered(); + let head = chain.head()?; + let header_head = chain.header_head()?; + + match self.block_sync_due(&head, &header_head) { + BlockSyncDue::StateSync => { + debug!(target: "sync", "Sync: transition to State Sync."); + return Ok(true); + } + BlockSyncDue::RequestBlock => { + self.block_sync(chain, highest_height_peers)?; + } + BlockSyncDue::WaitForBlock => { + // Do nothing. + } + } + + // start_height is used to report the progress of state sync, e.g. to say that it's 50% complete. + // This number has no other functional value. + let start_height = sync_status.start_height().unwrap_or(head.height); + + sync_status.update(SyncStatus::BlockSync { + start_height, + current_height: head.height, + highest_height, + }); + Ok(false) + } + + /// Check if state download is required + fn check_state_needed(&self, head: &Tip, header_head: &Tip) -> bool { + if self.archive || !self.state_sync_enabled { + return false; + } + + log_assert!(head.height <= header_head.height); + + // Only if the header head is more than one epoch ahead, then consider State Sync. + // block_fetch_horizon is used for testing to prevent test nodes from switching to State Sync too eagerly. + let prefer_state_sync = head.epoch_id != header_head.epoch_id + && head.next_epoch_id != header_head.epoch_id + && head.height.saturating_add(self.block_fetch_horizon) < header_head.height; + if prefer_state_sync { + debug!( + target: "sync", + head_epoch_id = ?head.epoch_id, + header_head_epoch_id = ?header_head.epoch_id, + head_next_epoch_id = ?head.next_epoch_id, + head_height = head.height, + header_head_height = header_head.height, + block_fetch_horizon = self.block_fetch_horizon, + "Switched from block sync to state sync"); + } + prefer_state_sync + } + + // Finds the last block on the canonical chain that is in store (processed). + fn get_last_processed_block(&self, chain: &Chain) -> Result { + // TODO: Can this function be replaced with `Chain::get_latest_known()`? + // The current chain head may not be on the canonical chain. + // Now we find the most recent block we know on the canonical chain. + // In practice the forks from the last final block are very short, so it is + // acceptable to perform this on each request. + + let head = chain.head()?; + let mut header = chain.get_block_header(&head.last_block_hash)?; + // First go back until we find the common block + while match chain.get_block_header_by_height(header.height()) { + Ok(got_header) => got_header.hash() != header.hash(), + Err(e) => match e { + unc_chain::Error::DBNotFoundErr(_) => true, + _ => return Err(e), + }, + } { + header = chain.get_block_header(header.prev_hash())?; + } + + // Then go forward for as long as we know the next block + let mut hash = *header.hash(); + loop { + match chain.chain_store().get_next_block_hash(&hash) { + Ok(got_hash) => { + if chain.block_exists(&got_hash)? { + hash = got_hash; + } else { + break; + } + } + Err(e) => match e { + unc_chain::Error::DBNotFoundErr(_) => break, + _ => return Err(e), + }, + } + } + + Ok(hash) + } + + /// Request recent blocks from a randomly chosen peer. + fn block_sync( + &mut self, + chain: &Chain, + highest_height_peers: &[HighestHeightPeerInfo], + ) -> Result<(), unc_chain::Error> { + // Update last request now because we want to update it whether or not + // the rest of the logic succeeds. + // TODO: If this code fails we should retry ASAP. Shouldn't we? + let chain_head = chain.head()?; + self.last_request = + Some(BlockSyncRequest { head: chain_head.last_block_hash, when: StaticClock::utc() }); + + // The last block on the canonical chain that is processed (is in store). + let reference_hash = self.get_last_processed_block(chain)?; + + // Look ahead for MAX_BLOCK_REQUESTS block headers and add requests for + // blocks that we don't have yet. + let mut requests = vec![]; + let mut next_hash = reference_hash; + for _ in 0..MAX_BLOCK_REQUESTS { + match chain.chain_store().get_next_block_hash(&next_hash) { + Ok(hash) => next_hash = hash, + Err(e) => match e { + unc_chain::Error::DBNotFoundErr(_) => break, + _ => return Err(e), + }, + } + if let Ok(_) = check_known(chain, &next_hash)? { + let next_height = chain.get_block_header(&next_hash)?.height(); + requests.push((next_height, next_hash)); + } + } + + let header_head = chain.header_head()?; + // Assume that peers are configured to keep as many epochs does this + // node and expect peers to have blocks in the range + // [gc_stop_height, header_head.last_block_hash]. + let gc_stop_height = chain.runtime_adapter.get_gc_stop_height(&header_head.last_block_hash); + + let mut num_requests = 0; + for (height, hash) in requests { + let request_from_archival = self.archive && height < gc_stop_height; + // Assume that heads of `highest_height_peers` are are ahead of the blocks we're requesting. + let peer = if request_from_archival { + // Normal peers are unlikely to have old blocks, request from an archival node. + let archival_peer_iter = highest_height_peers.iter().filter(|p| p.archival); + archival_peer_iter.choose(&mut rand::thread_rng()) + } else { + // All peers are likely to have this block. + let peer_iter = highest_height_peers.iter(); + peer_iter.choose(&mut rand::thread_rng()) + }; + + if let Some(peer) = peer { + debug!( + target: "sync", + head_height = chain_head.height, + header_head_height = header_head.height, + block_hash = ?hash, + block_height = height, + request_from_archival, + peer = ?peer.peer_info.id, + num_peers = highest_height_peers.len(), + "Block sync: requested block"); + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::BlockRequest { hash, peer_id: peer.peer_info.id.clone() }, + )); + num_requests += 1; + } else { + warn!( + target: "sync", + head_height = chain_head.height, + header_head_height = header_head.height, + block_hash = ?hash, + block_height = height, + request_from_archival, + "Block sync: No available peers to request a block from"); + } + } + debug!( + target: "sync", + head_height = chain_head.height, + header_head_height = header_head.height, + num_requests, + "Block sync: requested blocks"); + Ok(()) + } + + /// Checks if we should run block sync and ask for more full blocks. + /// Block sync is due either if the chain head has changed since the last request + /// or if time since the last request is > BLOCK_REQUEST_TIMEOUT_MS + fn block_sync_due(&mut self, head: &Tip, header_head: &Tip) -> BlockSyncDue { + if self.check_state_needed(head, header_head) { + return BlockSyncDue::StateSync; + } + match &self.last_request { + None => { + // Request the next block. + BlockSyncDue::RequestBlock + } + Some(request) => { + // Head got updated, no need to continue waiting for the requested block. + // TODO: This doesn't work nicely with a node requesting MAX_BLOCK_REQUESTS blocks at a time. + // TODO: Does receiving a response to one of those requests cancel and restart the other requests? + let head_got_updated = head.last_block_hash != request.head; + // Timeout elapsed + let timeout = StaticClock::utc() - request.when + > Duration::milliseconds(BLOCK_REQUEST_TIMEOUT_MS); + if head_got_updated || timeout { + // Request the next block. + BlockSyncDue::RequestBlock + } else { + // Continue waiting for the currently requested block. + BlockSyncDue::WaitForBlock + } + } + } + } +} + +/// Whether a new set of blocks needs to be requested. +enum BlockSyncDue { + /// Request the next block. + RequestBlock, + /// The block is already requested, wait for it. + WaitForBlock, + /// Too far behind, drop BlockSync and do StateSync instead. + StateSync, +} + +#[cfg(test)] +mod test { + use std::sync::Arc; + + use unc_chain::test_utils::wait_for_all_blocks_in_processing; + use unc_chain::{ChainGenesis, Provenance}; + use unc_crypto::{KeyType, PublicKey}; + use unc_network::test_utils::MockPeerManagerAdapter; + use unc_o11y::testonly::TracingCapture; + + use unc_primitives::network::PeerId; + use unc_primitives::utils::MaybeValidated; + + use super::*; + use crate::test_utils::TestEnv; + use unc_network::types::PeerInfo; + + use std::collections::HashSet; + + /// Helper function for block sync tests + fn collect_hashes_from_network_adapter( + network_adapter: &MockPeerManagerAdapter, + ) -> HashSet { + let mut network_request = network_adapter.requests.write().unwrap(); + network_request + .drain(..) + .map(|request| match request { + PeerManagerMessageRequest::NetworkRequests(NetworkRequests::BlockRequest { + hash, + .. + }) => hash, + _ => panic!("unexpected network request {:?}", request), + }) + .collect() + } + + fn check_hashes_from_network_adapter( + network_adapter: &MockPeerManagerAdapter, + expected_hashes: Vec, + ) { + let collected_hashes = collect_hashes_from_network_adapter(network_adapter); + assert_eq!(collected_hashes, expected_hashes.into_iter().collect::>()); + } + + fn create_highest_height_peer_infos(num_peers: usize) -> Vec { + (0..num_peers) + .map(|_| HighestHeightPeerInfo { + peer_info: PeerInfo { + id: PeerId::new(PublicKey::empty(KeyType::ED25519)), + addr: None, + account_id: None, + }, + genesis_id: Default::default(), + highest_block_height: 0, + highest_block_hash: Default::default(), + tracked_shards: vec![], + archival: false, + }) + .collect() + } + + #[test] + fn test_block_sync() { + let mut capture = TracingCapture::enable(); + let network_adapter = Arc::new(MockPeerManagerAdapter::default()); + let block_fetch_horizon = 10; + let mut block_sync = + BlockSync::new(network_adapter.clone().into(), block_fetch_horizon, false, true); + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = 100; + let mut env = TestEnv::builder(chain_genesis).clients_count(2).build(); + let mut blocks = vec![]; + for i in 1..5 * MAX_BLOCK_REQUESTS + 1 { + let block = env.clients[0].produce_block(i as u64).unwrap().unwrap(); + blocks.push(block.clone()); + env.process_block(0, block, Provenance::PRODUCED); + } + let block_headers = blocks.iter().map(|b| b.header().clone()).collect::>(); + let peer_infos = create_highest_height_peer_infos(2); + let mut challenges = vec![]; + env.clients[1].chain.sync_block_headers(block_headers, &mut challenges).unwrap(); + assert!(challenges.is_empty()); + + // fetch three blocks at a time + for i in 0..3 { + block_sync.block_sync(&env.clients[1].chain, &peer_infos).unwrap(); + + let expected_blocks: Vec<_> = + blocks[i * MAX_BLOCK_REQUESTS..(i + 1) * MAX_BLOCK_REQUESTS].to_vec(); + check_hashes_from_network_adapter( + &network_adapter, + expected_blocks.iter().map(|b| *b.hash()).collect(), + ); + + for block in expected_blocks { + env.process_block(1, block, Provenance::NONE); + } + } + + // Now test when the node receives the block out of order + // fetch the next three blocks + block_sync.block_sync(&env.clients[1].chain, &peer_infos).unwrap(); + check_hashes_from_network_adapter( + &network_adapter, + (3 * MAX_BLOCK_REQUESTS..4 * MAX_BLOCK_REQUESTS).map(|h| *blocks[h].hash()).collect(), + ); + // assumes that we only get block[4*MAX_BLOCK_REQUESTS-1] + let _ = env.clients[1].process_block_test( + MaybeValidated::from(blocks[4 * MAX_BLOCK_REQUESTS - 1].clone()), + Provenance::NONE, + ); + + // the next block sync should not request block[4*MAX_BLOCK_REQUESTS-1] again + block_sync.block_sync(&env.clients[1].chain, &peer_infos).unwrap(); + check_hashes_from_network_adapter( + &network_adapter, + (3 * MAX_BLOCK_REQUESTS..4 * MAX_BLOCK_REQUESTS - 1) + .map(|h| *blocks[h].hash()) + .collect(), + ); + + // Receive all blocks. Should not request more. As an extra + // complication, pause the processing of one block. + env.pause_block_processing(&mut capture, blocks[4 * MAX_BLOCK_REQUESTS - 1].hash()); + for i in 3 * MAX_BLOCK_REQUESTS..5 * MAX_BLOCK_REQUESTS { + let _ = env.clients[1] + .process_block_test(MaybeValidated::from(blocks[i].clone()), Provenance::NONE); + } + + block_sync.block_sync(&env.clients[1].chain, &peer_infos).unwrap(); + let requested_block_hashes = collect_hashes_from_network_adapter(&network_adapter); + assert!(requested_block_hashes.is_empty(), "{:?}", requested_block_hashes); + + // Now finish paused processing processing and sanity check that we + // still are fully synced. + env.resume_block_processing(blocks[4 * MAX_BLOCK_REQUESTS - 1].hash()); + wait_for_all_blocks_in_processing(&mut env.clients[1].chain); + let requested_block_hashes = collect_hashes_from_network_adapter(&network_adapter); + assert!(requested_block_hashes.is_empty(), "{:?}", requested_block_hashes); + } + + #[test] + fn test_block_sync_archival() { + let network_adapter = Arc::new(MockPeerManagerAdapter::default()); + let block_fetch_horizon = 10; + let mut block_sync = + BlockSync::new(network_adapter.clone().into(), block_fetch_horizon, true, true); + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = 5; + let mut env = TestEnv::builder(chain_genesis).clients_count(2).build(); + let mut blocks = vec![]; + for i in 1..41 { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + blocks.push(block.clone()); + env.process_block(0, block, Provenance::PRODUCED); + } + let block_headers = blocks.iter().map(|b| b.header().clone()).collect::>(); + let peer_infos = create_highest_height_peer_infos(2); + let mut challenges = vec![]; + env.clients[1].chain.sync_block_headers(block_headers, &mut challenges).unwrap(); + assert!(challenges.is_empty()); + + block_sync.block_sync(&env.clients[1].chain, &peer_infos).unwrap(); + let requested_block_hashes = collect_hashes_from_network_adapter(&network_adapter); + // We don't have archival peers, and thus cannot request any blocks + assert_eq!(requested_block_hashes, HashSet::new()); + + let mut peer_infos = create_highest_height_peer_infos(2); + for peer in peer_infos.iter_mut() { + peer.archival = true; + } + + block_sync.block_sync(&env.clients[1].chain, &peer_infos).unwrap(); + let requested_block_hashes = collect_hashes_from_network_adapter(&network_adapter); + assert_eq!( + requested_block_hashes, + blocks.iter().take(MAX_BLOCK_REQUESTS).map(|b| *b.hash()).collect::>() + ); + } +} diff --git a/chain/client/src/sync/epoch.rs b/chain/client/src/sync/epoch.rs new file mode 100644 index 000000000..12e38b2c4 --- /dev/null +++ b/chain/client/src/sync/epoch.rs @@ -0,0 +1,81 @@ +use chrono::{DateTime, Duration, Utc}; +use unc_network::types::PeerManagerAdapter; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::PeerId; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::types::EpochId; +use std::collections::{HashMap, HashSet}; +use std::time::Duration as TimeDuration; +use unc_primitives::types::validator_power_and_frozen::ValidatorPowerAndFrozen; + +/// Helper to keep track of the Epoch Sync +// TODO #3488 +#[allow(dead_code)] +pub struct EpochSync { + network_adapter: PeerManagerAdapter, + /// Datastructure to keep track of when the last request to each peer was made. + /// Peers do not respond to Epoch Sync requests more frequently than once per a certain time + /// interval, thus there's no point in requesting more frequently. + peer_to_last_request_time: HashMap>, + /// Tracks all the peers who have reported that we are already up to date + peers_reporting_up_to_date: HashSet, + /// The last epoch we are synced to + current_epoch_id: EpochId, + /// The next epoch id we need to sync + next_epoch_id: EpochId, + /// The block producers set to validate the light client block view for the next epoch + next_block_producers: Vec, + /// The last epoch id that we have requested + requested_epoch_id: EpochId, + /// When and to whom was the last request made + last_request_time: DateTime, + last_request_peer_id: Option, + + /// How long to wait for a response before re-requesting the same light client block view + request_timeout: Duration, + /// How frequently to send request to the same peer + peer_timeout: Duration, + + /// True, if all peers agreed that we're at the last Epoch. + /// Only finalization is needed. + have_all_epochs: bool, + /// Whether the Epoch Sync was performed to completion previously. + /// Current state machine allows for only one Epoch Sync. + pub done: bool, + + pub sync_hash: CryptoHash, + + received_epoch: bool, + + is_just_started: bool, +} + +impl EpochSync { + pub fn new( + network_adapter: PeerManagerAdapter, + genesis_epoch_id: EpochId, + genesis_next_epoch_id: EpochId, + first_epoch_block_producers: Vec, + request_timeout: TimeDuration, + peer_timeout: TimeDuration, + ) -> Self { + Self { + network_adapter, + peer_to_last_request_time: HashMap::new(), + peers_reporting_up_to_date: HashSet::new(), + current_epoch_id: genesis_epoch_id.clone(), + next_epoch_id: genesis_next_epoch_id, + next_block_producers: first_epoch_block_producers, + requested_epoch_id: genesis_epoch_id, + last_request_time: StaticClock::utc(), + last_request_peer_id: None, + request_timeout: Duration::from_std(request_timeout).unwrap(), + peer_timeout: Duration::from_std(peer_timeout).unwrap(), + received_epoch: false, + have_all_epochs: false, + done: false, + sync_hash: CryptoHash::default(), + is_just_started: true, + } + } +} diff --git a/chain/client/src/sync/external.rs b/chain/client/src/sync/external.rs new file mode 100644 index 000000000..939067f50 --- /dev/null +++ b/chain/client/src/sync/external.rs @@ -0,0 +1,408 @@ +use crate::metrics; +use futures::TryStreamExt; +use unc_primitives::types::{EpochId, ShardId}; +use std::io::{Read, Write}; +use std::path::PathBuf; +use std::sync::Arc; +use std::time::Duration; +use std::time::Instant; + +/// Connection to the external storage. +#[derive(Clone)] +pub enum ExternalConnection { + S3 { + bucket: Arc, + }, + Filesystem { + root_dir: PathBuf, + }, + GCS { + // Used for uploading and listing state parts. + // Requires valid credentials to be specified through env variable. + gcs_client: Arc, + // Used for anonymously downloading state parts. + reqwest_client: Arc, + bucket: String, + }, +} + +const GCS_ENCODE_SET: &percent_encoding::AsciiSet = + &percent_encoding::NON_ALPHANUMERIC.remove(b'-').remove(b'.').remove(b'_'); + +impl ExternalConnection { + pub async fn get_part( + &self, + shard_id: ShardId, + location: &str, + ) -> Result, anyhow::Error> { + let _timer = metrics::STATE_SYNC_EXTERNAL_PARTS_REQUEST_DELAY + .with_label_values(&[&shard_id.to_string()]) + .start_timer(); + match self { + ExternalConnection::S3 { bucket } => { + let response = bucket.get_object(location).await?; + tracing::debug!(target: "sync", %shard_id, location, response_code = response.status_code(), num_bytes = response.bytes().len(), "S3 request finished"); + if response.status_code() == 200 { + Ok(response.bytes().to_vec()) + } else { + Err(anyhow::anyhow!("Bad response status code: {}", response.status_code())) + } + } + ExternalConnection::Filesystem { root_dir } => { + let path = root_dir.join(location); + tracing::debug!(target: "sync", %shard_id, ?path, "Reading a file"); + let data = std::fs::read(&path)?; + Ok(data) + } + ExternalConnection::GCS { reqwest_client, bucket, .. } => { + // Download should be handled anonymously, therefore we are not using cloud-storage crate. + let url = format!( + "https://storage.googleapis.com/storage/v1/b/{}/o/{}?alt=media", + percent_encoding::percent_encode(bucket.as_bytes(), GCS_ENCODE_SET), + percent_encoding::percent_encode(location.as_bytes(), GCS_ENCODE_SET), + ); + let response = reqwest_client.get(&url).send().await?.error_for_status(); + + match response { + Err(e) => { + tracing::debug!(target: "sync", %shard_id, location, error = ?e, "GCS state_part request failed"); + Err(e.into()) + } + Ok(r) => { + let bytes = r.bytes().await?.to_vec(); + tracing::debug!(target: "sync", %shard_id, location, num_bytes = bytes.len(), "GCS state_part request finished"); + Ok(bytes) + } + } + } + } + } + + /// Uploads the given state part to external storage. + // Wrapper for adding is_ok to the metric labels. + pub async fn put_state_part( + &self, + state_part: &[u8], + shard_id: ShardId, + location: &str, + ) -> Result<(), anyhow::Error> { + let instant = Instant::now(); + let res = self.put_state_part_impl(state_part, shard_id, location).await; + let is_ok = if res.is_ok() { "ok" } else { "error" }; + let elapsed = instant.elapsed(); + metrics::STATE_SYNC_DUMP_PUT_OBJECT_ELAPSED + .with_label_values(&[&shard_id.to_string(), is_ok]) + .observe(elapsed.as_secs_f64()); + res + } + + // Actual implementation. + async fn put_state_part_impl( + &self, + state_part: &[u8], + shard_id: ShardId, + location: &str, + ) -> Result<(), anyhow::Error> { + match self { + ExternalConnection::S3 { bucket } => { + bucket.put_object(&location, state_part).await?; + tracing::debug!(target: "state_sync_dump", shard_id, part_length = state_part.len(), ?location, "Wrote a state part to S3"); + Ok(()) + } + ExternalConnection::Filesystem { root_dir } => { + let path = root_dir.join(location); + if let Some(parent_dir) = path.parent() { + std::fs::create_dir_all(parent_dir)?; + } + let mut file = std::fs::OpenOptions::new().write(true).create(true).open(&path)?; + file.write_all(state_part)?; + tracing::debug!(target: "state_sync_dump", shard_id, part_length = state_part.len(), ?location, "Wrote a state part to a file"); + Ok(()) + } + ExternalConnection::GCS { gcs_client, bucket, .. } => { + gcs_client + .object() + .create(bucket, state_part.to_vec(), location, "application/octet-stream") + .await?; + tracing::debug!(target: "state_sync_dump", shard_id, part_length = state_part.len(), ?location, "Wrote a state part to GCS"); + Ok(()) + } + } + } + + fn extract_file_name_from_full_path(full_path: String) -> String { + return Self::extract_file_name_from_path_buf(PathBuf::from(full_path)); + } + + fn extract_file_name_from_path_buf(path_buf: PathBuf) -> String { + return path_buf.file_name().unwrap().to_str().unwrap().to_string(); + } + + /// When using GCS external connection, this function requires credentials. + /// Thus, this function shouldn't be used for sync node that is expected to operate anonymously. + /// Only dump nodes should use this function. + pub async fn list_state_parts( + &self, + shard_id: ShardId, + directory_path: &str, + ) -> Result, anyhow::Error> { + let _timer = metrics::STATE_SYNC_DUMP_LIST_OBJECT_ELAPSED + .with_label_values(&[&shard_id.to_string()]) + .start_timer(); + match self { + ExternalConnection::S3 { bucket } => { + let prefix = format!("{}/", directory_path); + let list_results = bucket.list(prefix.clone(), Some("/".to_string())).await?; + tracing::debug!(target: "state_sync_dump", shard_id, ?directory_path, "List state parts in s3"); + let mut file_names = vec![]; + for res in list_results { + for obj in res.contents { + file_names.push(Self::extract_file_name_from_full_path(obj.key)) + } + } + Ok(file_names) + } + ExternalConnection::Filesystem { root_dir } => { + let path = root_dir.join(directory_path); + tracing::debug!(target: "state_sync_dump", shard_id, ?path, "List state parts in local directory"); + std::fs::create_dir_all(&path)?; + let mut file_names = vec![]; + let files = std::fs::read_dir(&path)?; + for file in files { + let file_name = Self::extract_file_name_from_path_buf(file?.path()); + file_names.push(file_name); + } + Ok(file_names) + } + ExternalConnection::GCS { gcs_client, bucket, .. } => { + let prefix = format!("{}/", directory_path); + tracing::debug!(target: "state_sync_dump", shard_id, ?directory_path, "List state parts in GCS"); + Ok(gcs_client + .object() + .list( + bucket, + cloud_storage::ListRequest { prefix: Some(prefix), ..Default::default() }, + ) + .await? + .try_collect::>() + .await? + .into_iter() + .map(|object_list| { + object_list + .items + .into_iter() + .map(|obj| Self::extract_file_name_from_full_path(obj.name)) + .collect::>() + }) + .flatten() + .collect()) + } + } + } +} + +/// Construct a location on the external storage. +pub fn external_storage_location( + chain_id: &str, + epoch_id: &EpochId, + epoch_height: u64, + shard_id: u64, + part_id: u64, + num_parts: u64, +) -> String { + format!( + "{}/{}", + location_prefix(chain_id, epoch_height, epoch_id, shard_id), + part_filename(part_id, num_parts) + ) +} + +pub fn external_storage_location_directory( + chain_id: &str, + epoch_id: &EpochId, + epoch_height: u64, + shard_id: u64, +) -> String { + location_prefix(chain_id, epoch_height, epoch_id, shard_id) +} + +pub fn location_prefix( + chain_id: &str, + epoch_height: u64, + epoch_id: &EpochId, + shard_id: u64, +) -> String { + format!( + "chain_id={}/epoch_height={}/epoch_id={}/shard_id={}", + chain_id, epoch_height, epoch_id.0, shard_id + ) +} + +pub fn part_filename(part_id: u64, num_parts: u64) -> String { + format!("state_part_{:06}_of_{:06}", part_id, num_parts) +} + +pub fn match_filename(s: &str) -> Option { + let re = regex::Regex::new(r"^state_part_(\d{6})_of_(\d{6})$").unwrap(); + re.captures(s) +} + +pub fn is_part_filename(s: &str) -> bool { + match_filename(s).is_some() +} + +pub fn get_num_parts_from_filename(s: &str) -> Option { + if let Some(captures) = match_filename(s) { + if let Some(num_parts) = captures.get(2) { + if let Ok(num_parts) = num_parts.as_str().parse::() { + return Some(num_parts); + } + } + } + None +} + +pub fn get_part_id_from_filename(s: &str) -> Option { + if let Some(captures) = match_filename(s) { + if let Some(part_id) = captures.get(1) { + if let Ok(part_id) = part_id.as_str().parse::() { + return Some(part_id); + } + } + } + None +} + +pub fn create_bucket_readonly( + bucket: &str, + region: &str, + timeout: Duration, +) -> Result { + let creds = s3::creds::Credentials::anonymous()?; + create_bucket(bucket, region, timeout, creds) +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +struct S3CredentialsConfig { + access_key: String, + secret_key: String, +} + +pub fn create_bucket_readwrite( + bucket: &str, + region: &str, + timeout: Duration, + credentials_file: Option, +) -> Result { + let creds = match credentials_file { + Some(credentials_file) => { + let mut file = std::fs::File::open(credentials_file)?; + let mut json_config_str = String::new(); + file.read_to_string(&mut json_config_str)?; + let credentials_config: S3CredentialsConfig = serde_json::from_str(&json_config_str)?; + s3::creds::Credentials::new( + Some(&credentials_config.access_key), + Some(&credentials_config.secret_key), + None, + None, + None, + ) + } + None => s3::creds::Credentials::default(), + }?; + create_bucket(bucket, region, timeout, creds) +} + +fn create_bucket( + bucket: &str, + region: &str, + timeout: Duration, + creds: s3::creds::Credentials, +) -> Result { + let mut bucket = s3::Bucket::new(bucket, region.parse::()?, creds)?; + // Ensure requests finish in finite amount of time. + bucket.set_request_timeout(Some(timeout)); + Ok(bucket) +} + +#[cfg(test)] +mod test { + use crate::sync::external::{ + get_num_parts_from_filename, get_part_id_from_filename, is_part_filename, part_filename, + ExternalConnection, + }; + use unc_o11y::testonly::init_test_logger; + use rand::distributions::{Alphanumeric, DistString}; + + fn random_string(rand_len: usize) -> String { + Alphanumeric.sample_string(&mut rand::thread_rng(), rand_len) + } + + #[test] + fn test_match_filename() { + let filename = part_filename(5, 15); + assert!(is_part_filename(&filename)); + assert!(!is_part_filename("123123")); + + assert_eq!(get_num_parts_from_filename(&filename), Some(15)); + assert_eq!(get_num_parts_from_filename("123123"), None); + + assert_eq!(get_part_id_from_filename(&filename), Some(5)); + assert_eq!(get_part_id_from_filename("123123"), None); + } + + /// This test should be ignored by default, as it requires gcloud credentials to run. + /// Specify the path to service account json in `SERVICE_ACCOUNT` variable to run the test. + #[test] + #[cfg_attr(not(feature = "gcs_credentials"), ignore)] + fn test_gcs_upload_list_download() { + init_test_logger(); + let rt = tokio::runtime::Runtime::new().unwrap(); + + // Generate random filename. + let filename = random_string(8); + tracing::debug!("Filename: {:?}", filename); + + // Define bucket. + let connection = ExternalConnection::GCS { + gcs_client: std::sync::Arc::new(cloud_storage::Client::default()), + reqwest_client: std::sync::Arc::new(reqwest::Client::default()), + bucket: "state-parts".to_string(), + }; + + // Generate random data. + let data = random_string(1000); + tracing::debug!("Data as string: {:?}", data); + let data: Vec = data.into(); + tracing::debug!("Data: {:?}", data); + + // Directory resembles real usecase. + let dir = "test_folder/chain_id=test/epoch_height=1/epoch_id=test/shard_id=0".to_string(); + let full_filename = format!("{}/{}", dir, filename); + + // Before uploading we shouldn't see filename in the list of files. + let files = rt.block_on(async { connection.list_state_parts(0, &dir).await.unwrap() }); + tracing::debug!("Files before upload: {:?}", files); + assert_eq!(files.into_iter().filter(|x| *x == filename).collect::>().len(), 0); + + // Uploading the file. + rt.block_on(async { connection.put_state_part(&data, 0, &full_filename).await.unwrap() }); + + // After uploading we should see filename in the list of files. + let files = rt.block_on(async { connection.list_state_parts(0, &dir).await.unwrap() }); + tracing::debug!("Files after upload: {:?}", files); + assert_eq!(files.into_iter().filter(|x| *x == filename).collect::>().len(), 1); + + // And the data should match generates data. + let download_data = + rt.block_on(async { connection.get_part(0, &full_filename).await.unwrap() }); + assert_eq!(download_data, data); + + // Also try to download some data at nonexistent location and expect to fail. + let filename = random_string(8); + let full_filename = format!("{}/{}", dir, filename); + + let download_data = rt.block_on(async { connection.get_part(0, &full_filename).await }); + assert!(download_data.is_err(), "{:?}", download_data); + } +} diff --git a/chain/client/src/sync/header.rs b/chain/client/src/sync/header.rs new file mode 100644 index 000000000..f3414e166 --- /dev/null +++ b/chain/client/src/sync/header.rs @@ -0,0 +1,871 @@ +use chrono::{DateTime, Duration, Utc}; +use unc_async::messaging::CanSend; +use unc_chain::{Chain, ChainStoreAccess}; +use unc_client_primitives::types::SyncStatus; +use unc_network::types::PeerManagerMessageRequest; +use unc_network::types::{HighestHeightPeerInfo, NetworkRequests, PeerManagerAdapter}; +use unc_primitives::block::Tip; +use unc_primitives::hash::CryptoHash; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::types::BlockHeight; +use rand::seq::SliceRandom; +use rand::thread_rng; +use std::cmp::min; +use std::time::Duration as TimeDuration; +use tracing::{debug, warn}; + +/// Maximum number of block headers send over the network. +pub const MAX_BLOCK_HEADERS: u64 = 512; + +/// Maximum number of block header hashes to send as part of a locator. +pub const MAX_BLOCK_HEADER_HASHES: usize = 20; + +pub const NS_PER_SECOND: u128 = 1_000_000_000; + +/// Progress of downloading the currently requested batch of headers. +struct BatchProgress { + /// An intermediate timeout by which a certain number of headers is expected. + timeout: DateTime, + /// Height expected at the moment of `timeout`. + expected_height: BlockHeight, + /// Header head height at the moment this batch was requested. + header_head_height: BlockHeight, + highest_height_of_peers: BlockHeight, +} + +/// Helper to keep track of sync headers. +/// Handles major re-orgs by finding closest header that matches and re-downloading headers from that point. +pub struct HeaderSync { + network_adapter: PeerManagerAdapter, + + /// Progress of downloading the currently requested batch of headers. + // TODO: Change type to Option. + batch_progress: BatchProgress, + + /// Peer from which the next batch of headers was requested. + syncing_peer: Option, + + /// When the stalling was first detected. + stalling_ts: Option>, + + /// How much time to wait after initial header sync. + initial_timeout: Duration, + + /// How much time to wait after some progress is made in header sync. + progress_timeout: Duration, + + /// How much time to wait before banning a peer in header sync if sync is too slow. + stall_ban_timeout: Duration, + + /// Expected increase of header head height per second during header sync + expected_height_per_second: u64, +} + +impl HeaderSync { + pub fn new( + network_adapter: PeerManagerAdapter, + initial_timeout: TimeDuration, + progress_timeout: TimeDuration, + stall_ban_timeout: TimeDuration, + expected_height_per_second: u64, + ) -> Self { + HeaderSync { + network_adapter, + batch_progress: BatchProgress { + timeout: StaticClock::utc(), + expected_height: 0, + header_head_height: 0, + highest_height_of_peers: 0, + }, + syncing_peer: None, + stalling_ts: None, + initial_timeout: Duration::from_std(initial_timeout).unwrap(), + progress_timeout: Duration::from_std(progress_timeout).unwrap(), + stall_ban_timeout: Duration::from_std(stall_ban_timeout).unwrap(), + expected_height_per_second, + } + } + + /// Can update `sync_status` to `HeaderSync`. + /// Can request a new batch of headers from a peer. + /// This function won't tell you that header sync is complete. + pub fn run( + &mut self, + sync_status: &mut SyncStatus, + chain: &Chain, + highest_height: BlockHeight, + highest_height_peers: &[HighestHeightPeerInfo], + ) -> Result<(), unc_chain::Error> { + let _span = tracing::debug_span!(target: "sync", "run", sync = "HeaderSync").entered(); + let head = chain.head()?; + let header_head = chain.header_head()?; + + // Check if we need to start a new request for a batch of header. + if !self.header_sync_due(sync_status, &header_head, highest_height) { + // Either + // * header sync is not needed, or + // * a request is already in-flight and more progress is expected. + return Ok(()); + } + + // TODO: Why call `header_sync_due()` if that decision can be overridden here? + let enable_header_sync = match sync_status { + SyncStatus::HeaderSync { .. } + | SyncStatus::BlockSync { .. } + | SyncStatus::StateSyncDone => { + // TODO: Transitioning from BlockSync to HeaderSync is fine if the highest height of peers gets too far from our header_head_height. However it's currently unconditional. + true + } + SyncStatus::NoSync | SyncStatus::AwaitingPeers | SyncStatus::EpochSync { .. } => { + // TODO: How can it get to EpochSync if it's hardcoded to go from NoSync to HeaderSync? + debug!(target: "sync", "Sync: initial transition to Header sync. Header head {} at {}", + header_head.last_block_hash, header_head.height, + ); + true + } + SyncStatus::StateSync { .. } => false, + }; + + if !enable_header_sync { + // Header sync is blocked for whatever reason. + return Ok(()); + } + + // start_height is used to report the progress of header sync, e.g. to say that it's 50% complete. + // This number has no other functional value. + let start_height = sync_status.start_height().unwrap_or(head.height); + + sync_status.update(SyncStatus::HeaderSync { + start_height, + current_height: header_head.height, + highest_height, + }); + + self.syncing_peer = None; + // Pick a new random peer to request the next batch of headers. + if let Some(peer) = highest_height_peers.choose(&mut thread_rng()).cloned() { + // TODO: This condition should always be true, otherwise we can already complete header sync. + if peer.highest_block_height > header_head.height { + self.syncing_peer = self.request_headers(chain, peer); + } + } + Ok(()) + } + + /// Returns the height that we expect to reach starting from `old_height` after `time_delta`. + fn compute_expected_height( + &self, + old_height: BlockHeight, + time_delta: Duration, + ) -> BlockHeight { + (old_height as u128 + + (time_delta.num_nanoseconds().unwrap() as u128 + * self.expected_height_per_second as u128 + / NS_PER_SECOND)) as u64 + } + + /// Returns whether a new batch of headers needs to be requested. + // Checks whether the batch of headers is completely downloaded, or if the peer failed to satisfy our expectations for long enough. + // If yes, then returns true to request a new batch of headers. Maybe bans a peer. + // Otherwise, returns false to indicate that we're expecting more headers from the same requested batch. + // TODO: This function should check the difference between the current header_head height and the highest height of the peers. + // TODO: Triggering header sync to get 1 header (or even 0 headers) makes little sense. + pub(crate) fn header_sync_due( + &mut self, + sync_status: &SyncStatus, + header_head: &Tip, + highest_height: BlockHeight, + ) -> bool { + let now = StaticClock::utc(); + let BatchProgress { + timeout, + expected_height: old_expected_height, + header_head_height: prev_height, + highest_height_of_peers: prev_highest_height, + } = self.batch_progress; + // Received all headers from a batch requested on the previous iteration. + // Can proceed to the next iteration. + let all_headers_received = + header_head.height >= min(prev_height + MAX_BLOCK_HEADERS - 4, prev_highest_height); + + // Did we receive as many headers as we expected from the peer? + // If not, consider the peer stalling. + // This can be either the initial timeout, or any of the progress timeouts after the initial timeout. + let stalling = header_head.height <= old_expected_height && now > timeout; + + // Always enable header sync on initial state transition from + // * NoSync + // * AwaitingPeers. + // TODO: Will this remain correct with the introduction of EpochSync? + // TODO: Shouldn't a node transition to EpochSync from these states? + let force_sync = match sync_status { + SyncStatus::NoSync | SyncStatus::AwaitingPeers => true, + _ => false, + }; + + if force_sync || all_headers_received || stalling { + // Request a new batch of headers. + + self.batch_progress = BatchProgress { + timeout: now + self.initial_timeout, + expected_height: self + .compute_expected_height(header_head.height, self.initial_timeout), + header_head_height: header_head.height, + highest_height_of_peers: highest_height, + }; + + // Record the timestamp when the stalling was first noticed. + if stalling { + if self.stalling_ts.is_none() { + self.stalling_ts = Some(now); + } + } else { + self.stalling_ts = None; + } + + if all_headers_received { + // As the batch of headers is received completely, reset the stalling timestamp. + self.stalling_ts = None; + } else { + if let Some(ref stalling_ts) = self.stalling_ts { + // syncing_peer is expected to be present. + if let Some(ref peer) = self.syncing_peer { + match sync_status { + SyncStatus::HeaderSync { highest_height, .. } => { + if now > *stalling_ts + self.stall_ban_timeout + && *highest_height == peer.highest_block_height + { + // This message is used in sync_ban.py test. Consider checking there as well if you change it. + // The peer is one of the peers with the highest height, but we consider the peer stalling. + warn!(target: "sync", "Sync: ban a peer: {}, for not providing enough headers. Peer's height: {}", peer.peer_info, peer.highest_block_height); + // Ban the peer, which blocks all interactions with the peer for some time. + // TODO: Consider not banning straightaway, but give a node a few attempts before banning it. + // TODO: Prefer not to request the next batch of headers from the same peer. + self.network_adapter.send( + PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::BanPeer { + peer_id: peer.peer_info.id.clone(), + ban_reason: unc_network::types::ReasonForBan::ProvidedNotEnoughHeaders, + }, + ), + ); + // Will retry without this peer. + self.syncing_peer = None; + return false; + } + } + _ => { + // Unexpected + } + } + } + } + } + self.syncing_peer = None; + // Return true to request a new batch of headers. + true + } else { + // Manage the currently requested batch of headers. + // Note that it is guaranteed that `now < timeout`, because otherwise it will be `stalling` or `all_headers_received`. + + // Resetting the timeout as long as we make progress. + if self.made_enough_progress(header_head.height, old_expected_height, now, timeout) { + // Update our expectation. + // `new_expected_height` can be beyond the requested batch of header, but that is fine. + let new_expected_height = + self.compute_expected_height(header_head.height, self.progress_timeout); + self.batch_progress = BatchProgress { + timeout: now + self.progress_timeout, + expected_height: new_expected_height, + header_head_height: prev_height, + highest_height_of_peers: prev_highest_height, + }; + } + // Keep getting headers from the same batch. + // Don't request a new batch of headers. + false + } + } + + /// Checks whether the node made enough progress. + /// Returns true iff it needs less time than (timeout-now) to get (expected_height - current_height) headers at the rate of `expected_height_per_second` headers per second. + fn made_enough_progress( + &self, + current_height: BlockHeight, + expected_height: BlockHeight, + now: DateTime, + timeout: DateTime, + ) -> bool { + if now <= timeout { + self.compute_expected_height(current_height, timeout - now) >= expected_height + } else { + current_height >= expected_height + } + } + + /// Request headers from a given peer to advance the chain. + fn request_headers( + &mut self, + chain: &Chain, + peer: HighestHeightPeerInfo, + ) -> Option { + if let Ok(locator) = self.get_locator(chain) { + debug!(target: "sync", "Sync: request headers: asking {} for headers, {:?}", peer.peer_info.id, locator); + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::BlockHeadersRequest { + hashes: locator, + peer_id: peer.peer_info.id.clone(), + }, + )); + return Some(peer); + } + None + } + + // The remote side will return MAX_BLOCK_HEADERS headers, starting from the first hash in + // the returned "locator" list that is on their canonical chain. + // + // The locator allows us to start syncing from a reasonably recent common ancestor. Since + // we don't know which fork the remote side is on, we include a few hashes. The first one + // we include is the tip of our chain, and the next one is 2 blocks back (on the same chain, + // by number of blocks (or in other words, by ordinals), not by height), then 4 blocks + // back, then 8 blocks back, etc, until we reach the most recent final block. The reason + // why we stop at the final block is because the consensus guarantees us that the final + // blocks observed by all nodes are on the same fork. + fn get_locator(&mut self, chain: &Chain) -> Result, unc_chain::Error> { + let store = chain.chain_store(); + let tip = store.header_head()?; + // We could just get the ordinal from the header, but it's off by one: #8177. + let tip_ordinal = store.get_block_merkle_tree(&tip.last_block_hash)?.size(); + let final_head = store.final_head()?; + let final_head_ordinal = store.get_block_merkle_tree(&final_head.last_block_hash)?.size(); + let ordinals = get_locator_ordinals(final_head_ordinal, tip_ordinal); + let mut locator: Vec = vec![]; + for ordinal in &ordinals { + let block_hash = store.get_block_hash_from_ordinal(*ordinal)?; + locator.push(block_hash); + } + debug!(target: "sync", "Sync: locator: {:?} ordinals: {:?}", locator, ordinals); + Ok(locator) + } +} + +/// Step back from highest to lowest ordinal, in powers of 2 steps, limited by MAX_BLOCK_HEADERS +/// heights per step, and limited by MAX_BLOCK_HEADER_HASHES steps in total. +fn get_locator_ordinals(lowest_ordinal: u64, highest_ordinal: u64) -> Vec { + let mut current = highest_ordinal; + let mut ordinals = vec![]; + let mut step = 2; + while current > lowest_ordinal && ordinals.len() < MAX_BLOCK_HEADER_HASHES as usize - 1 { + ordinals.push(current); + if current <= lowest_ordinal + step { + break; + } + current -= step; + // Do not step back more than MAX_BLOCK_HEADERS, as the gap in between would not + // allow us to sync to a more recent block. + step = min(step * 2, MAX_BLOCK_HEADERS); + } + ordinals.push(lowest_ordinal); + ordinals +} + +#[cfg(test)] +mod test { + use std::sync::Arc; + use std::thread; + + use unc_chain::test_utils::{ + process_block_sync, setup, setup_with_validators_and_start_time, ValidatorSchedule, + }; + use unc_chain::{BlockProcessingArtifact, Provenance}; + use unc_crypto::{KeyType, PublicKey}; + use unc_network::test_utils::MockPeerManagerAdapter; + use unc_primitives::block::{Approval, Block, GenesisId}; + use unc_primitives::network::PeerId; + use unc_primitives::test_utils::TestBlockBuilder; + + use super::*; + use unc_network::types::{BlockInfo, FullPeerInfo, PeerInfo}; + use unc_primitives::merkle::PartialMerkleTree; + use unc_primitives::types::EpochId; + use unc_primitives::version::PROTOCOL_VERSION; + use num_rational::Ratio; + + #[test] + fn test_get_locator_ordinals() { + assert_eq!(get_locator_ordinals(0, 0), vec![0]); + assert_eq!(get_locator_ordinals(0, 1), vec![1, 0]); + assert_eq!(get_locator_ordinals(0, 2), vec![2, 0]); + assert_eq!(get_locator_ordinals(0, 3), vec![3, 1, 0]); + assert_eq!(get_locator_ordinals(0, 10), vec![10, 8, 4, 0]); + assert_eq!(get_locator_ordinals(0, 100), vec![100, 98, 94, 86, 70, 38, 0]); + assert_eq!( + get_locator_ordinals(0, 1000), + vec![1000, 998, 994, 986, 970, 938, 874, 746, 490, 0] + ); + // Locator is still reasonable size even given large height. + assert_eq!( + get_locator_ordinals(0, 10000), + vec![ + 10000, 9998, 9994, 9986, 9970, 9938, 9874, 9746, 9490, 8978, 8466, 7954, 7442, + 6930, 6418, 5906, 5394, 4882, 4370, 0 + ] + ); + assert_eq!(get_locator_ordinals(100, 100), vec![100]); + assert_eq!(get_locator_ordinals(100, 101), vec![101, 100]); + assert_eq!(get_locator_ordinals(100, 102), vec![102, 100]); + assert_eq!(get_locator_ordinals(100, 103), vec![103, 101, 100]); + assert_eq!(get_locator_ordinals(100, 110), vec![110, 108, 104, 100]); + assert_eq!(get_locator_ordinals(100, 200), vec![200, 198, 194, 186, 170, 138, 100]); + assert_eq!( + get_locator_ordinals(20000, 21000), + vec![21000, 20998, 20994, 20986, 20970, 20938, 20874, 20746, 20490, 20000] + ); + assert_eq!( + get_locator_ordinals(20000, 30000), + vec![ + 30000, 29998, 29994, 29986, 29970, 29938, 29874, 29746, 29490, 28978, 28466, 27954, + 27442, 26930, 26418, 25906, 25394, 24882, 24370, 20000 + ] + ); + } + + /// Starts two chains that fork of genesis and checks that they can sync headers to the longest. + #[test] + fn test_sync_headers_fork() { + let mock_adapter = Arc::new(MockPeerManagerAdapter::default()); + let mut header_sync = HeaderSync::new( + mock_adapter.clone().into(), + TimeDuration::from_secs(10), + TimeDuration::from_secs(2), + TimeDuration::from_secs(120), + 1_000_000_000, + ); + let (mut chain, _, _, signer) = setup(); + for _ in 0..3 { + let prev = chain.get_block(&chain.head().unwrap().last_block_hash).unwrap(); + // Have gaps in the chain, so we don't have final blocks (i.e. last final block is + // genesis). Otherwise we violate consensus invariants. + let block = TestBlockBuilder::new(&prev, signer.clone()) + .height(prev.header().height() + 2) + .build(); + process_block_sync( + &mut chain, + &None, + block.into(), + Provenance::PRODUCED, + &mut BlockProcessingArtifact::default(), + ) + .unwrap(); + } + let (mut chain2, _, _, signer2) = setup(); + for _ in 0..5 { + let prev = chain2.get_block(&chain2.head().unwrap().last_block_hash).unwrap(); + // Have gaps in the chain, so we don't have final blocks (i.e. last final block is + // genesis). Otherwise we violate consensus invariants. + let block = TestBlockBuilder::new(&prev, signer2.clone()) + .height(prev.header().height() + 2) + .build(); + process_block_sync( + &mut chain2, + &None, + block.into(), + Provenance::PRODUCED, + &mut BlockProcessingArtifact::default(), + ) + .unwrap(); + } + let mut sync_status = SyncStatus::NoSync; + let peer1 = FullPeerInfo { + peer_info: PeerInfo::random(), + chain_info: unc_network::types::PeerChainInfo { + genesis_id: GenesisId { + chain_id: "unittest".to_string(), + hash: *chain.genesis().hash(), + }, + tracked_shards: vec![], + archival: false, + last_block: Some(BlockInfo { + height: chain2.head().unwrap().height, + hash: chain2.head().unwrap().last_block_hash, + }), + }, + }; + let head = chain.head().unwrap(); + assert!(header_sync + .run( + &mut sync_status, + &mut chain, + head.height, + &[>>::into(peer1.clone()).unwrap()] + ) + .is_ok()); + assert!(sync_status.is_syncing()); + // Check that it queried last block, and then stepped down to genesis block to find common block with the peer. + + let item = mock_adapter.pop().unwrap().as_network_requests(); + assert_eq!( + item, + NetworkRequests::BlockHeadersRequest { + // chain is 6 -> 4 -> 2 -> 0. + hashes: [6, 2, 0] + .iter() + .map(|i| *chain.get_block_by_height(*i).unwrap().hash()) + .collect(), + peer_id: peer1.peer_info.id + } + ); + } + + #[test] + fn test_sync_headers_fork_from_final_block() { + let mock_adapter = Arc::new(MockPeerManagerAdapter::default()); + let mut header_sync = HeaderSync::new( + mock_adapter.clone().into(), + TimeDuration::from_secs(10), + TimeDuration::from_secs(2), + TimeDuration::from_secs(120), + 1_000_000_000, + ); + let (mut chain, _, _, signer) = setup(); + let (mut chain2, _, _, signer2) = setup(); + for chain in [&mut chain, &mut chain2] { + // Both chains share a common final block at height 3. + for _ in 0..5 { + let prev = chain.get_block(&chain.head().unwrap().last_block_hash).unwrap(); + let block = TestBlockBuilder::new(&prev, signer.clone()).build(); + process_block_sync( + chain, + &None, + block.into(), + Provenance::PRODUCED, + &mut BlockProcessingArtifact::default(), + ) + .unwrap(); + } + } + for _ in 0..7 { + let prev = chain.get_block(&chain.head().unwrap().last_block_hash).unwrap(); + // Test with huge gaps to make sure we are still able to find locators. + let block = TestBlockBuilder::new(&prev, signer.clone()) + .height(prev.header().height() + 1000) + .build(); + process_block_sync( + &mut chain, + &None, + block.into(), + Provenance::PRODUCED, + &mut BlockProcessingArtifact::default(), + ) + .unwrap(); + } + for _ in 0..3 { + let prev = chain2.get_block(&chain2.head().unwrap().last_block_hash).unwrap(); + // Test with huge gaps, but 3 blocks here produce a higher height than the 7 blocks + // above. + let block = TestBlockBuilder::new(&prev, signer2.clone()) + .height(prev.header().height() + 3100) + .build(); + process_block_sync( + &mut chain2, + &None, + block.into(), + Provenance::PRODUCED, + &mut BlockProcessingArtifact::default(), + ) + .unwrap(); + } + let mut sync_status = SyncStatus::NoSync; + let peer1 = FullPeerInfo { + peer_info: PeerInfo::random(), + chain_info: unc_network::types::PeerChainInfo { + genesis_id: GenesisId { + chain_id: "unittest".to_string(), + hash: *chain.genesis().hash(), + }, + tracked_shards: vec![], + archival: false, + last_block: Some(BlockInfo { + height: chain2.head().unwrap().height, + hash: chain2.head().unwrap().last_block_hash, + }), + }, + }; + let head = chain.head().unwrap(); + assert!(header_sync + .run( + &mut sync_status, + &mut chain, + head.height, + &[>>::into(peer1.clone()).unwrap()] + ) + .is_ok()); + assert!(sync_status.is_syncing()); + // Check that it queried last block, and then stepped down to genesis block to find common block with the peer. + + let item = mock_adapter.pop().unwrap().as_network_requests(); + assert_eq!( + item, + NetworkRequests::BlockHeadersRequest { + // chain is 7005 -> 6005 -> 5005 -> 4005 -> 3005 -> 2005 -> 1005 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 + // where 3 is final. + hashes: [7005, 5005, 1005, 3] + .iter() + .map(|i| *chain.get_block_by_height(*i).unwrap().hash()) + .collect(), + peer_id: peer1.peer_info.id + } + ); + } + + /// Sets up `HeaderSync` with particular tolerance for slowness, and makes sure that a peer that + /// sends headers below the threshold gets banned, and the peer that sends them faster doesn't get + /// banned. + /// Also makes sure that if `header_sync_due` is checked more frequently than the `progress_timeout` + /// the peer doesn't get banned. (specifically, that the expected height downloaded gets properly + /// adjusted for time passed) + #[test] + fn test_slow_header_sync() { + let network_adapter = Arc::new(MockPeerManagerAdapter::default()); + let highest_height = 1000; + + // Setup header_sync with expectation of 25 headers/second + let mut header_sync = HeaderSync::new( + network_adapter.clone().into(), + TimeDuration::from_secs(1), + TimeDuration::from_secs(1), + TimeDuration::from_secs(3), + 25, + ); + + let set_syncing_peer = |header_sync: &mut HeaderSync| { + header_sync.syncing_peer = Some(HighestHeightPeerInfo { + peer_info: PeerInfo { + id: PeerId::new(PublicKey::empty(KeyType::ED25519)), + addr: None, + account_id: None, + }, + genesis_id: Default::default(), + highest_block_height: 0, + highest_block_hash: Default::default(), + tracked_shards: vec![], + archival: false, + }); + header_sync.syncing_peer.as_mut().unwrap().highest_block_height = highest_height; + }; + set_syncing_peer(&mut header_sync); + + let (chain, _, _, signer) = setup(); + let genesis = chain.get_block(&chain.genesis().hash().clone()).unwrap(); + + let mut last_block = &genesis; + let mut all_blocks = vec![]; + for i in 0..61 { + let current_height = 3 + i * 5; + let block = + TestBlockBuilder::new(last_block, signer.clone()).height(current_height).build(); + all_blocks.push(block); + last_block = &all_blocks[all_blocks.len() - 1]; + } + + let mut last_added_block_ord = 0; + // First send 30 heights every second for a while and make sure it doesn't get + // banned + for _iter in 0..12 { + let block = &all_blocks[last_added_block_ord]; + let current_height = block.header().height(); + set_syncing_peer(&mut header_sync); + header_sync.header_sync_due( + &SyncStatus::HeaderSync { + start_height: current_height, + current_height, + highest_height, + }, + &Tip::from_header(block.header()), + highest_height, + ); + + last_added_block_ord += 3; + + thread::sleep(TimeDuration::from_millis(500)); + } + // 6 blocks / second is fast enough, we should not have banned the peer + assert!(network_adapter.requests.read().unwrap().is_empty()); + + // Now the same, but only 20 heights / sec + for _iter in 0..12 { + let block = &all_blocks[last_added_block_ord]; + let current_height = block.header().height(); + set_syncing_peer(&mut header_sync); + header_sync.header_sync_due( + &SyncStatus::HeaderSync { + start_height: current_height, + current_height, + highest_height, + }, + &Tip::from_header(block.header()), + highest_height, + ); + + last_added_block_ord += 2; + + thread::sleep(TimeDuration::from_millis(500)); + } + // This time the peer should be banned, because 4 blocks/s is not fast enough + let ban_peer = network_adapter.requests.write().unwrap().pop_back().unwrap(); + + if let NetworkRequests::BanPeer { .. } = ban_peer.as_network_requests() { + /* expected */ + } else { + assert!(false); + } + } + + #[test] + fn test_sync_from_very_behind() { + let mock_adapter = Arc::new(MockPeerManagerAdapter::default()); + let mut header_sync = HeaderSync::new( + mock_adapter.clone().into(), + TimeDuration::from_secs(10), + TimeDuration::from_secs(2), + TimeDuration::from_secs(120), + 1_000_000_000, + ); + + let vs = ValidatorSchedule::new() + .block_producers_per_epoch(vec![vec!["test0".parse().unwrap()]]); + let genesis_time = StaticClock::utc(); + // Don't bother with epoch switches. It's not relevant. + let (mut chain, _, _, _) = + setup_with_validators_and_start_time(vs.clone(), 10000, 100, genesis_time); + let (mut chain2, _, _, signers2) = + setup_with_validators_and_start_time(vs, 10000, 100, genesis_time); + // Set up the second chain with 2000+ blocks. + let mut block_merkle_tree = PartialMerkleTree::default(); + block_merkle_tree.insert(*chain.genesis().hash()); // for genesis block + for _ in 0..(4 * MAX_BLOCK_HEADERS + 10) { + let last_block = chain2.get_block(&chain2.head().unwrap().last_block_hash).unwrap(); + let this_height = last_block.header().height() + 1; + let (epoch_id, next_epoch_id) = + if last_block.header().prev_hash() == &CryptoHash::default() { + (last_block.header().next_epoch_id().clone(), EpochId(*last_block.hash())) + } else { + ( + last_block.header().epoch_id().clone(), + last_block.header().next_epoch_id().clone(), + ) + }; + let block = Block::produce( + PROTOCOL_VERSION, + PROTOCOL_VERSION, + last_block.header(), + this_height, + last_block.header().block_ordinal() + 1, + last_block.chunks().iter().cloned().collect(), + epoch_id, + next_epoch_id, + None, + signers2 + .iter() + .map(|signer| { + Some(Box::new( + Approval::new( + *last_block.hash(), + last_block.header().height(), + this_height, + signer.as_ref(), + ) + .signature, + )) + }) + .collect(), + Ratio::new(0, 1), + 0, + 100, + Some(0), + vec![], + vec![], + &*signers2[0], + *last_block.header().next_bp_hash(), + block_merkle_tree.root(), + None, + ); + block_merkle_tree.insert(*block.hash()); + chain2.process_block_header(block.header(), &mut Vec::new()).unwrap(); // just to validate + process_block_sync( + &mut chain2, + &None, + block.into(), + Provenance::PRODUCED, + &mut BlockProcessingArtifact::default(), + ) + .unwrap(); + } + let mut sync_status = SyncStatus::NoSync; + let peer1 = FullPeerInfo { + peer_info: PeerInfo::random(), + chain_info: unc_network::types::PeerChainInfo { + genesis_id: GenesisId { + chain_id: "unittest".to_string(), + hash: *chain.genesis().hash(), + }, + tracked_shards: vec![], + archival: false, + last_block: Some(BlockInfo { + height: chain2.head().unwrap().height, + hash: chain2.head().unwrap().last_block_hash, + }), + }, + }; + // It should be done in 5 iterations, but give it 10 iterations just in case it would + // get into an infinite loop because of some bug and cause the test to hang. + for _ in 0..10 { + let header_head = chain.header_head().unwrap(); + if header_head.last_block_hash == chain2.header_head().unwrap().last_block_hash { + // sync is done. + break; + } + assert!(header_sync + .run( + &mut sync_status, + &mut chain, + header_head.height, + &[>>::into(peer1.clone()).unwrap()] + ) + .is_ok()); + match sync_status { + SyncStatus::HeaderSync { .. } => {} + _ => panic!("Unexpected sync status: {:?}", sync_status), + } + let message = match mock_adapter.pop() { + Some(message) => message.as_network_requests(), + None => { + panic!("No message was sent; current height: {}", header_head.height); + } + }; + match message { + NetworkRequests::BlockHeadersRequest { hashes, peer_id } => { + assert_eq!(peer_id, peer1.peer_info.id); + let headers = chain2.retrieve_headers(hashes, MAX_BLOCK_HEADERS, None).unwrap(); + assert!(!headers.is_empty(), "No headers were returned"); + match chain.sync_block_headers(headers, &mut Vec::new()) { + Ok(_) => {} + Err(e) => { + panic!("Error inserting headers: {:?}", e); + } + } + } + _ => panic!("Unexpected network message: {:?}", message), + } + if chain.header_head().unwrap().height <= header_head.height { + panic!( + "Syncing is not making progress. Head was not updated from {}", + header_head.height + ); + } + } + let new_tip = chain.header_head().unwrap(); + assert_eq!(new_tip.last_block_hash, chain2.head().unwrap().last_block_hash); + } +} diff --git a/chain/client/src/sync/mod.rs b/chain/client/src/sync/mod.rs new file mode 100644 index 000000000..462344f19 --- /dev/null +++ b/chain/client/src/sync/mod.rs @@ -0,0 +1,7 @@ +pub mod adapter; +pub mod block; +pub mod epoch; +pub mod external; +pub mod header; +pub mod state; +pub mod sync_actor; diff --git a/chain/client/src/sync/state.rs b/chain/client/src/sync/state.rs new file mode 100644 index 000000000..a0afc8876 --- /dev/null +++ b/chain/client/src/sync/state.rs @@ -0,0 +1,1406 @@ +//! State sync is trying to fetch the 'full state' from the peers (which can be multiple GB). +//! It happens after HeaderSync and before BlockSync (but only if the node sees that it is 'too much behind'). +//! See https://near.github.io/framework/architecture/how/sync.html for more detailed information. +//! Such state can be downloaded only at special heights (currently - at the beginning of the current and previous +//! epochs). +//! +//! You can do the state sync for each shard independently. +//! It starts by fetching a 'header' - that contains basic information about the state (for example its size, how +//! many parts it consists of, hash of the root etc). +//! Then it tries downloading the rest of the data in 'parts' (usually the part is around 1MB in size). +//! +//! For downloading - the code is picking the potential target nodes (all direct peers that are tracking the shard +//! (and are high enough) + validators from that epoch that were tracking the shard) +//! Then for each part that we're missing, we're 'randomly' picking a target from whom we'll request it - but we make +//! sure to not request more than MAX_STATE_PART_REQUESTS from each. +//! +//! WARNING: with the current design, we're putting quite a load on the validators - as we request a lot of data from +//! them (if you assume that we have 100 validators and 30 peers - we send 100/130 of requests to validators). +//! Currently validators defend against it, by having a rate limiters - but we should improve the algorithm +//! here to depend more on local peers instead. +//! + +use crate::metrics; +use crate::sync::external::{ + create_bucket_readonly, external_storage_location, ExternalConnection, +}; +use actix_rt::ArbiterHandle; +use chrono::{DateTime, Duration, Utc}; +use futures::{future, FutureExt}; +use unc_async::messaging::CanSendAsync; +use unc_chain::chain::ApplyStatePartsRequest; +use unc_chain::unc_chain_primitives; +use unc_chain::resharding::ReshardingRequest; +use unc_chain::types::RuntimeAdapter; +use unc_chain::Chain; +use unc_chain_configs::{ExternalStorageConfig, ExternalStorageLocation, SyncConfig}; +use unc_client_primitives::types::{ + format_shard_sync_phase, DownloadStatus, ShardSyncDownload, ShardSyncStatus, +}; +use unc_epoch_manager::EpochManagerAdapter; +use unc_network::types::PeerManagerMessageRequest; +use unc_network::types::{ + HighestHeightPeerInfo, NetworkRequests, NetworkResponses, PeerManagerAdapter, +}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::PeerId; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::state_part::PartId; +use unc_primitives::state_sync::{ShardStateSyncResponse, StatePartKey}; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::types::{AccountId, EpochHeight, EpochId, ShardId, StateRoot}; +use unc_store::DBCol; +use rand::seq::SliceRandom; +use rand::{thread_rng, Rng}; +use std::collections::HashMap; +use std::ops::Add; +use std::sync::atomic::Ordering; +use std::sync::mpsc::{channel, Receiver, Sender}; +use std::sync::Arc; +use std::time::Duration as TimeDuration; +use tokio::sync::{Semaphore, TryAcquireError}; +use tracing::info; + +/// Maximum number of state parts to request per peer on each round when node is trying to download the state. +pub const MAX_STATE_PART_REQUEST: u64 = 16; +/// Number of state parts already requested stored as pending. +/// This number should not exceed MAX_STATE_PART_REQUEST times (number of peers in the network). +pub const MAX_PENDING_PART: u64 = MAX_STATE_PART_REQUEST * 10000; +/// Time limit per state dump iteration. +/// A node must check external storage for parts to dump again once time is up. +pub const STATE_DUMP_ITERATION_TIME_LIMIT_SECS: u64 = 300; + +pub enum StateSyncResult { + /// State sync still in progress. No action needed by the caller. + InProgress, + /// The state for all shards was downloaded. + Completed, +} + +struct PendingRequestStatus { + /// Number of parts that are in progress (we requested them from a given peer but didn't get the answer yet). + missing_parts: usize, + wait_until: DateTime, +} + +impl PendingRequestStatus { + fn new(timeout: Duration) -> Self { + Self { missing_parts: 1, wait_until: StaticClock::utc().add(timeout) } + } + fn expired(&self) -> bool { + StaticClock::utc() > self.wait_until + } +} + +/// Signals that a state part was downloaded and saved to RocksDB. +/// Or failed to do so. +pub struct StateSyncGetPartResult { + sync_hash: CryptoHash, + shard_id: ShardId, + part_id: PartId, + /// Either the length of state part, or an error describing where the + /// process failed. + part_result: Result, +} + +/// How to retrieve the state data. +enum StateSyncInner { + /// Request both the state header and state parts from the peers. + Peers { + /// Which parts were requested from which peer and when. + last_part_id_requested: HashMap<(PeerId, ShardId), PendingRequestStatus>, + /// Map from which part we requested to whom. + requested_target: lru::LruCache<(u64, CryptoHash), PeerId>, + }, + /// Requests the state header from peers but gets the state parts from an + /// external storage. + PartsFromExternal { + /// Chain ID. + chain_id: String, + /// This semaphore imposes a restriction on the maximum number of simultaneous downloads + semaphore: Arc, + /// Connection to the external storage. + external: ExternalConnection, + }, +} + +/// Helper to track state sync. +pub struct StateSync { + /// How to retrieve the state data. + inner: StateSyncInner, + + /// Is used for communication with the peers. + network_adapter: PeerManagerAdapter, + + /// Timeout (set in config - by default to 60 seconds) is used to figure out how long we should wait + /// for the answer from the other node before giving up. + timeout: Duration, + + /// Maps shard_id to result of applying downloaded state. + state_parts_apply_results: HashMap>, + + /// Maps shard_id to result of splitting state for resharding. + resharding_state_roots: + HashMap, unc_chain::Error>>, + + /// Message queue to process the received state parts. + state_parts_mpsc_tx: Sender, + state_parts_mpsc_rx: Receiver, +} + +impl StateSync { + pub fn new( + network_adapter: PeerManagerAdapter, + timeout: TimeDuration, + chain_id: &str, + sync_config: &SyncConfig, + catchup: bool, + ) -> Self { + let inner = match sync_config { + SyncConfig::Peers => StateSyncInner::Peers { + last_part_id_requested: Default::default(), + requested_target: lru::LruCache::new(MAX_PENDING_PART as usize), + }, + SyncConfig::ExternalStorage(ExternalStorageConfig { + location, + num_concurrent_requests, + num_concurrent_requests_during_catchup, + }) => { + let external = match location { + ExternalStorageLocation::S3 { bucket, region, .. } => { + let bucket = create_bucket_readonly(&bucket, ®ion, timeout); + if let Err(err) = bucket { + panic!("Failed to create an S3 bucket: {}", err); + } + ExternalConnection::S3 { bucket: Arc::new(bucket.unwrap()) } + } + ExternalStorageLocation::Filesystem { root_dir } => { + ExternalConnection::Filesystem { root_dir: root_dir.clone() } + } + ExternalStorageLocation::GCS { bucket, .. } => ExternalConnection::GCS { + gcs_client: Arc::new(cloud_storage::Client::default()), + reqwest_client: Arc::new(reqwest::Client::default()), + bucket: bucket.clone(), + }, + }; + let num_permits = if catchup { + *num_concurrent_requests_during_catchup + } else { + *num_concurrent_requests + } as usize; + StateSyncInner::PartsFromExternal { + chain_id: chain_id.to_string(), + semaphore: Arc::new(tokio::sync::Semaphore::new(num_permits)), + external, + } + } + }; + let timeout = Duration::from_std(timeout).unwrap(); + let (tx, rx) = channel::(); + StateSync { + inner, + network_adapter, + timeout, + state_parts_apply_results: HashMap::new(), + resharding_state_roots: HashMap::new(), + state_parts_mpsc_rx: rx, + state_parts_mpsc_tx: tx, + } + } + + // The return value indicates whether state sync is + // finished, in which case the client will transition to block sync + fn sync_shards_status( + &mut self, + me: &Option, + sync_hash: CryptoHash, + sync_status: &mut HashMap, + chain: &mut Chain, + epoch_manager: &dyn EpochManagerAdapter, + highest_height_peers: &[HighestHeightPeerInfo], + tracking_shards: Vec, + now: DateTime, + state_parts_task_scheduler: &dyn Fn(ApplyStatePartsRequest), + resharding_scheduler: &dyn Fn(ReshardingRequest), + state_parts_arbiter_handle: &ArbiterHandle, + use_colour: bool, + runtime_adapter: Arc, + ) -> Result { + let mut all_done = true; + + let prev_hash = *chain.get_block_header(&sync_hash)?.prev_hash(); + let prev_epoch_id = chain.get_block_header(&prev_hash)?.epoch_id().clone(); + let epoch_id = chain.get_block_header(&sync_hash)?.epoch_id().clone(); + let prev_shard_layout = epoch_manager.get_shard_layout(&prev_epoch_id)?; + let shard_layout = epoch_manager.get_shard_layout(&epoch_id)?; + if prev_shard_layout != shard_layout { + // This error message is used in tests to ensure node exists for the + // correct reason. When changing it please also update the tests. + panic!("cannot sync to the first epoch after sharding upgrade. Please wait for the next epoch or find peers that are more up to date"); + } + let need_to_reshard = epoch_manager.will_shard_layout_change(&prev_hash)?; + + for shard_id in tracking_shards { + let version = prev_shard_layout.version(); + let shard_uid = ShardUId { version, shard_id: shard_id as u32 }; + let mut download_timeout = false; + let mut run_shard_state_download = false; + let shard_sync_download = sync_status.entry(shard_id).or_insert_with(|| { + run_shard_state_download = true; + ShardSyncDownload::new_download_state_header(now) + }); + + let mut shard_sync_done = false; + match &shard_sync_download.status { + ShardSyncStatus::StateDownloadHeader => { + (download_timeout, run_shard_state_download) = self + .sync_shards_download_header_status( + shard_id, + shard_sync_download, + sync_hash, + chain, + now, + )?; + } + ShardSyncStatus::StateDownloadParts => { + let res = + self.sync_shards_download_parts_status(shard_id, shard_sync_download, now); + download_timeout = res.0; + run_shard_state_download = res.1; + } + ShardSyncStatus::StateDownloadScheduling => { + self.sync_shards_download_scheduling_status( + shard_id, + shard_sync_download, + sync_hash, + chain, + now, + state_parts_task_scheduler, + )?; + } + ShardSyncStatus::StateDownloadApplying => { + self.sync_shards_download_applying_status( + shard_id, + shard_sync_download, + sync_hash, + chain, + now, + )?; + } + ShardSyncStatus::StateDownloadComplete => { + shard_sync_done = self + .sync_shards_download_complete_status(need_to_reshard, shard_sync_download); + } + ShardSyncStatus::ReshardingScheduling => { + debug_assert!(need_to_reshard); + self.sync_shards_resharding_scheduling_status( + shard_id, + shard_sync_download, + sync_hash, + chain, + resharding_scheduler, + me, + )?; + } + ShardSyncStatus::ReshardingApplying => { + debug_assert!(need_to_reshard); + shard_sync_done = self.sync_shards_resharding_applying_status( + shard_uid, + shard_sync_download, + sync_hash, + chain, + )?; + } + ShardSyncStatus::StateSyncDone => { + shard_sync_done = true; + } + } + let stage = if shard_sync_done { + // Update the state sync stage metric, because maybe we'll not + // enter this function again. + ShardSyncStatus::StateSyncDone.repr() + } else { + shard_sync_download.status.repr() + }; + metrics::STATE_SYNC_STAGE.with_label_values(&[&shard_id.to_string()]).set(stage as i64); + all_done &= shard_sync_done; + + if download_timeout { + tracing::warn!( + target: "sync", + %shard_id, + timeout_sec = self.timeout.num_seconds(), + "State sync didn't download the state, sending StateRequest again"); + tracing::debug!( + target: "sync", + %shard_id, + %sync_hash, + ?me, + phase = format_shard_sync_phase(shard_sync_download, use_colour), + "State sync status"); + } + + // Execute syncing for shard `shard_id` + if run_shard_state_download { + self.request_shard( + shard_id, + chain, + sync_hash, + shard_sync_download, + highest_height_peers, + runtime_adapter.clone(), + state_parts_arbiter_handle, + )?; + } + } + + Ok(all_done) + } + + // Checks the message queue for new downloaded parts and writes them. + fn process_downloaded_parts( + &mut self, + sync_hash: CryptoHash, + shard_sync: &mut HashMap, + ) { + for StateSyncGetPartResult { sync_hash: msg_sync_hash, shard_id, part_id, part_result } in + self.state_parts_mpsc_rx.try_iter() + { + if msg_sync_hash != sync_hash { + tracing::debug!(target: "sync", + ?shard_id, + ?part_id, + ?sync_hash, + ?msg_sync_hash, + "Received message for other sync.", + ); + continue; + } + info!(target: "sync", ?part_result, ?part_id, ?shard_id, "downloaded a state part"); + if let Some(shard_sync_download) = shard_sync.get_mut(&shard_id) { + if shard_sync_download.status != ShardSyncStatus::StateDownloadParts { + continue; + } + if let Some(part_download) = + shard_sync_download.downloads.get_mut(part_id.idx as usize) + { + process_part_response( + part_id.idx, + shard_id, + sync_hash, + part_download, + part_result, + ); + } + } + } + } + + // Called by the client actor, when it finished applying all the downloaded parts. + pub fn set_apply_result( + &mut self, + shard_id: ShardId, + apply_result: Result<(), unc_chain::Error>, + ) { + self.state_parts_apply_results.insert(shard_id, apply_result); + } + + // Called by the client actor, when it finished resharding. + pub fn set_resharding_result( + &mut self, + shard_id: ShardId, + result: Result, unc_chain::Error>, + ) { + self.resharding_state_roots.insert(shard_id, result); + } + + /// Find the hash of the first block on the same epoch (and chain) of block with hash `sync_hash`. + pub fn get_epoch_start_sync_hash( + chain: &Chain, + sync_hash: &CryptoHash, + ) -> Result { + let mut header = chain.get_block_header(sync_hash)?; + let mut epoch_id = header.epoch_id().clone(); + let mut hash = *header.hash(); + let mut prev_hash = *header.prev_hash(); + loop { + if prev_hash == CryptoHash::default() { + return Ok(hash); + } + header = chain.get_block_header(&prev_hash)?; + if &epoch_id != header.epoch_id() { + return Ok(hash); + } + epoch_id = header.epoch_id().clone(); + hash = *header.hash(); + prev_hash = *header.prev_hash(); + } + } + + // Function called when our node receives the network response with a part. + pub fn received_requested_part( + &mut self, + part_id: u64, + shard_id: ShardId, + sync_hash: CryptoHash, + ) { + match &mut self.inner { + StateSyncInner::Peers { last_part_id_requested, requested_target } => { + let key = (part_id, sync_hash); + // Check that it came from the target that we requested it from. + if let Some(target) = requested_target.get(&key) { + if last_part_id_requested.get_mut(&(target.clone(), shard_id)).map_or( + false, + |request| { + request.missing_parts = request.missing_parts.saturating_sub(1); + request.missing_parts == 0 + }, + ) { + last_part_id_requested.remove(&(target.clone(), shard_id)); + } + } + } + StateSyncInner::PartsFromExternal { .. } => { + // Do nothing. + } + } + } + + /// Avoids peers that already have outstanding requests for parts. + fn select_peers( + &mut self, + highest_height_peers: &[HighestHeightPeerInfo], + shard_id: ShardId, + ) -> Result, unc_chain::Error> { + let peers: Vec = + highest_height_peers.iter().map(|peer| peer.peer_info.id.clone()).collect(); + let res = match &mut self.inner { + StateSyncInner::Peers { last_part_id_requested, .. } => { + last_part_id_requested.retain(|_, request| !request.expired()); + peers + .into_iter() + .filter(|peer| { + // If we still have a pending request from this node - don't add another one. + !last_part_id_requested.contains_key(&(peer.clone(), shard_id)) + }) + .collect::>() + } + StateSyncInner::PartsFromExternal { .. } => peers, + }; + Ok(res) + } + + /// Returns new ShardSyncDownload if successful, otherwise returns given shard_sync_download + fn request_shard( + &mut self, + shard_id: ShardId, + chain: &Chain, + sync_hash: CryptoHash, + shard_sync_download: &mut ShardSyncDownload, + highest_height_peers: &[HighestHeightPeerInfo], + runtime_adapter: Arc, + state_parts_arbiter_handle: &ArbiterHandle, + ) -> Result<(), unc_chain::Error> { + let possible_targets = self.select_peers(highest_height_peers, shard_id)?; + + if possible_targets.is_empty() { + tracing::debug!(target: "sync", "Can't request a state header: No possible targets"); + // In most cases it means that all the targets are currently busy (that we have a pending request with them). + return Ok(()); + } + + // Downloading strategy starts here + match shard_sync_download.status { + ShardSyncStatus::StateDownloadHeader => { + self.request_shard_header( + shard_id, + sync_hash, + &possible_targets, + shard_sync_download, + ); + } + ShardSyncStatus::StateDownloadParts => { + self.request_shard_parts( + shard_id, + sync_hash, + possible_targets, + shard_sync_download, + chain, + runtime_adapter, + state_parts_arbiter_handle, + ); + } + _ => {} + } + + Ok(()) + } + + /// Makes a StateRequestHeader header to one of the peers. + fn request_shard_header( + &mut self, + shard_id: ShardId, + sync_hash: CryptoHash, + possible_targets: &[PeerId], + new_shard_sync_download: &mut ShardSyncDownload, + ) { + let peer_id = possible_targets.choose(&mut thread_rng()).cloned().unwrap(); + tracing::debug!(target: "sync", ?peer_id, shard_id, ?sync_hash, ?possible_targets, "request_shard_header"); + assert!(new_shard_sync_download.downloads[0].run_me.load(Ordering::SeqCst)); + new_shard_sync_download.downloads[0].run_me.store(false, Ordering::SeqCst); + new_shard_sync_download.downloads[0].state_requests_count += 1; + new_shard_sync_download.downloads[0].last_target = Some(peer_id.clone()); + let run_me = new_shard_sync_download.downloads[0].run_me.clone(); + unc_performance_metrics::actix::spawn( + std::any::type_name::(), + self.network_adapter + .send_async(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::StateRequestHeader { shard_id, sync_hash, peer_id }, + )) + .then(move |result| { + if let Ok(NetworkResponses::RouteNotFound) = + result.map(|f| f.as_network_response()) + { + // Send a StateRequestHeader on the next iteration + run_me.store(true, Ordering::SeqCst); + } + future::ready(()) + }), + ); + } + + /// Makes requests to download state parts for the given epoch of the given shard. + fn request_shard_parts( + &mut self, + shard_id: ShardId, + sync_hash: CryptoHash, + possible_targets: Vec, + new_shard_sync_download: &mut ShardSyncDownload, + chain: &Chain, + runtime_adapter: Arc, + state_parts_arbiter_handle: &ArbiterHandle, + ) { + // Iterate over all parts that needs to be requested (i.e. download.run_me is true). + // Parts are ordered such that its index match its part_id. + match &mut self.inner { + StateSyncInner::Peers { last_part_id_requested, requested_target } => { + // We'll select all the 'highest' peers + validators as candidates (excluding those that gave us timeout in the past). + // And for each one of them, we'll ask for up to 16 (MAX_STATE_PART_REQUEST) parts. + let possible_targets_sampler = + SamplerLimited::new(possible_targets, MAX_STATE_PART_REQUEST); + + // For every part that needs to be requested it is selected one + // peer (target) randomly to request the part from. + // IMPORTANT: here we use 'zip' with possible_target_sampler - + // which is limited. So at any moment we'll not request more + // than possible_targets.len() * MAX_STATE_PART_REQUEST parts. + for ((part_id, download), target) in + parts_to_fetch(new_shard_sync_download).zip(possible_targets_sampler) + { + sent_request_part( + target.clone(), + part_id, + shard_id, + sync_hash, + last_part_id_requested, + requested_target, + self.timeout, + ); + request_part_from_peers( + part_id, + target, + download, + shard_id, + sync_hash, + &self.network_adapter, + ); + } + } + StateSyncInner::PartsFromExternal { chain_id, semaphore, external } => { + let sync_block_header = chain.get_block_header(&sync_hash).unwrap(); + let epoch_id = sync_block_header.epoch_id(); + let epoch_info = chain.epoch_manager.get_epoch_info(epoch_id).unwrap(); + let epoch_height = epoch_info.epoch_height(); + + let shard_state_header = chain.get_state_header(shard_id, sync_hash).unwrap(); + let state_root = shard_state_header.chunk_prev_state_root(); + let state_num_parts = shard_state_header.num_state_parts(); + + for (part_id, download) in parts_to_fetch(new_shard_sync_download) { + request_part_from_external_storage( + part_id, + download, + shard_id, + sync_hash, + epoch_id, + epoch_height, + state_num_parts, + &chain_id.clone(), + state_root, + semaphore.clone(), + external.clone(), + runtime_adapter.clone(), + state_parts_arbiter_handle, + self.state_parts_mpsc_tx.clone(), + ); + if semaphore.available_permits() == 0 { + break; + } + } + } + } + } + + /// The main 'step' function that should be called periodically to check and update the sync process. + /// The current state/progress information is mostly kept within 'new_shard_sync' object. + /// + /// Returns the state of the sync. + pub fn run( + &mut self, + me: &Option, + sync_hash: CryptoHash, + sync_status: &mut HashMap, + chain: &mut Chain, + epoch_manager: &dyn EpochManagerAdapter, + highest_height_peers: &[HighestHeightPeerInfo], + // Shards to sync. + tracking_shards: Vec, + state_parts_task_scheduler: &dyn Fn(ApplyStatePartsRequest), + resharding_scheduler: &dyn Fn(ReshardingRequest), + state_parts_arbiter_handle: &ArbiterHandle, + use_colour: bool, + runtime_adapter: Arc, + ) -> Result { + let _span = tracing::debug_span!(target: "sync", "run", sync = "StateSync").entered(); + tracing::trace!(target: "sync", %sync_hash, ?tracking_shards, "syncing state"); + let now = StaticClock::utc(); + + if tracking_shards.is_empty() { + // This case is possible if a validator cares about the same shards in the new epoch as + // in the previous (or about a subset of them), return success right away + + return Ok(StateSyncResult::Completed); + } + // The downloaded parts are from all shards. This function takes all downloaded parts and + // saves them to the DB. + // TODO: Ideally, we want to process the downloads on a different thread than the one that runs the Client. + self.process_downloaded_parts(sync_hash, sync_status); + let all_done = self.sync_shards_status( + me, + sync_hash, + sync_status, + chain, + epoch_manager, + highest_height_peers, + tracking_shards, + now, + state_parts_task_scheduler, + resharding_scheduler, + state_parts_arbiter_handle, + use_colour, + runtime_adapter, + )?; + + if all_done { + Ok(StateSyncResult::Completed) + } else { + Ok(StateSyncResult::InProgress) + } + } + + pub fn update_download_on_state_response_message( + &mut self, + shard_sync_download: &mut ShardSyncDownload, + hash: CryptoHash, + shard_id: u64, + state_response: ShardStateSyncResponse, + chain: &mut Chain, + ) { + if let Some(part_id) = state_response.part_id() { + // Mark that we have received this part (this will update info on pending parts from peers etc). + self.received_requested_part(part_id, shard_id, hash); + } + match shard_sync_download.status { + ShardSyncStatus::StateDownloadHeader => { + if let Some(header) = state_response.take_header() { + if !shard_sync_download.downloads[0].done { + match chain.set_state_header(shard_id, hash, header) { + Ok(()) => { + shard_sync_download.downloads[0].done = true; + } + Err(err) => { + tracing::error!(target: "sync", %shard_id, %hash, ?err, "State sync set_state_header error"); + shard_sync_download.downloads[0].error = true; + } + } + } + } else { + // No header found. + // It may happen because requested node couldn't build state response. + if !shard_sync_download.downloads[0].done { + tracing::info!(target: "sync", %shard_id, %hash, "state_response doesn't have header, should be re-requested"); + shard_sync_download.downloads[0].error = true; + } + } + } + ShardSyncStatus::StateDownloadParts => { + if let Some(part) = state_response.take_part() { + let num_parts = shard_sync_download.downloads.len() as u64; + let (part_id, data) = part; + if part_id >= num_parts { + tracing::error!(target: "sync", %shard_id, %hash, part_id, "State sync received incorrect part_id, potential malicious peer"); + return; + } + if !shard_sync_download.downloads[part_id as usize].done { + match chain.set_state_part( + shard_id, + hash, + PartId::new(part_id, num_parts), + &data, + ) { + Ok(()) => { + shard_sync_download.downloads[part_id as usize].done = true; + } + Err(err) => { + tracing::error!(target: "sync", %shard_id, %hash, part_id, ?err, "State sync set_state_part error"); + shard_sync_download.downloads[part_id as usize].error = true; + } + } + } + } + } + _ => {} + } + } + + /// Checks if the header is downloaded. + /// If the download is complete, then moves forward to `StateDownloadParts`, + /// otherwise retries the header request. + /// Returns `(download_timeout, run_shard_state_download)` where: + /// * `download_timeout` means that the state header request timed out (and needs to be retried). + /// * `run_shard_state_download` means that header or part download requests need to run for this shard. + fn sync_shards_download_header_status( + &mut self, + shard_id: ShardId, + shard_sync_download: &mut ShardSyncDownload, + sync_hash: CryptoHash, + chain: &Chain, + now: DateTime, + ) -> Result<(bool, bool), unc_chain::Error> { + let download = &mut shard_sync_download.downloads[0]; + // StateDownloadHeader is the first step. We want to fetch the basic information about the state (its size, hash etc). + if download.done { + let shard_state_header = chain.get_state_header(shard_id, sync_hash)?; + let state_num_parts = shard_state_header.num_state_parts(); + // If the header was downloaded successfully - move to phase 2 (downloading parts). + // Create the vector with entry for each part. + *shard_sync_download = + ShardSyncDownload::new_download_state_parts(now, state_num_parts); + Ok((false, true)) + } else { + let download_timeout = now - download.prev_update_time > self.timeout; + if download_timeout { + tracing::debug!(target: "sync", last_target = ?download.last_target, start_time = ?download.start_time, prev_update_time = ?download.prev_update_time, state_requests_count = download.state_requests_count, "header request timed out"); + metrics::STATE_SYNC_HEADER_TIMEOUT + .with_label_values(&[&shard_id.to_string()]) + .inc(); + } + if download.error { + tracing::debug!(target: "sync", last_target = ?download.last_target, start_time = ?download.start_time, prev_update_time = ?download.prev_update_time, state_requests_count = download.state_requests_count, "header request error"); + metrics::STATE_SYNC_HEADER_ERROR.with_label_values(&[&shard_id.to_string()]).inc(); + } + // Retry in case of timeout or failure. + if download_timeout || download.error { + download.run_me.store(true, Ordering::SeqCst); + download.error = false; + download.prev_update_time = now; + } + let run_me = download.run_me.load(Ordering::SeqCst); + Ok((download_timeout, run_me)) + } + } + + /// Checks if the parts are downloaded. + /// If download of all parts is complete, then moves forward to `StateDownloadScheduling`. + /// Returns `(download_timeout, run_shard_state_download)` where: + /// * `download_timeout` means that the state header request timed out (and needs to be retried). + /// * `run_shard_state_download` means that header or part download requests need to run for this shard. + fn sync_shards_download_parts_status( + &mut self, + shard_id: ShardId, + shard_sync_download: &mut ShardSyncDownload, + now: DateTime, + ) -> (bool, bool) { + // Step 2 - download all the parts (each part is usually around 1MB). + let mut download_timeout = false; + let mut run_shard_state_download = false; + + let mut parts_done = true; + let num_parts = shard_sync_download.downloads.len(); + let mut num_parts_done = 0; + for part_download in shard_sync_download.downloads.iter_mut() { + if !part_download.done { + parts_done = false; + let prev = part_download.prev_update_time; + let part_timeout = now - prev > self.timeout; // Retry parts that failed. + if part_timeout || part_download.error { + download_timeout |= part_timeout; + if part_timeout || part_download.last_target.is_some() { + // Don't immediately retry failed requests from external + // storage. Most often error is a state part not + // available. That error doesn't get fixed by retrying, + // but rather by waiting. + metrics::STATE_SYNC_RETRY_PART + .with_label_values(&[&shard_id.to_string()]) + .inc(); + part_download.run_me.store(true, Ordering::SeqCst); + part_download.error = false; + part_download.prev_update_time = now; + } + } + if part_download.run_me.load(Ordering::SeqCst) { + run_shard_state_download = true; + } + } + if part_download.done { + num_parts_done += 1; + } + } + metrics::STATE_SYNC_PARTS_DONE + .with_label_values(&[&shard_id.to_string()]) + .set(num_parts_done); + metrics::STATE_SYNC_PARTS_TOTAL + .with_label_values(&[&shard_id.to_string()]) + .set(num_parts as i64); + // If all parts are done - we can move towards scheduling. + if parts_done { + *shard_sync_download = ShardSyncDownload { + downloads: vec![], + status: ShardSyncStatus::StateDownloadScheduling, + }; + } + (download_timeout, run_shard_state_download) + } + + fn sync_shards_download_scheduling_status( + &mut self, + shard_id: ShardId, + shard_sync_download: &mut ShardSyncDownload, + sync_hash: CryptoHash, + chain: &mut Chain, + now: DateTime, + state_parts_task_scheduler: &dyn Fn(ApplyStatePartsRequest), + ) -> Result<(), unc_chain::Error> { + let shard_state_header = chain.get_state_header(shard_id, sync_hash)?; + let state_num_parts = shard_state_header.num_state_parts(); + // Now apply all the parts to the chain / runtime. + // TODO: not sure why this has to happen only after all the parts were downloaded - + // as we could have done this in parallel after getting each part. + match chain.schedule_apply_state_parts( + shard_id, + sync_hash, + state_num_parts, + state_parts_task_scheduler, + ) { + Ok(()) => { + *shard_sync_download = ShardSyncDownload { + downloads: vec![], + status: ShardSyncStatus::StateDownloadApplying, + } + } + Err(err) => { + // Cannot finalize the downloaded state. + // The reasonable behavior here is to start from the very beginning. + metrics::STATE_SYNC_DISCARD_PARTS.with_label_values(&[&shard_id.to_string()]).inc(); + tracing::error!(target: "sync", %shard_id, %sync_hash, ?err, "State sync finalizing error"); + *shard_sync_download = ShardSyncDownload::new_download_state_header(now); + chain.clear_downloaded_parts(shard_id, sync_hash, state_num_parts)?; + } + } + Ok(()) + } + + fn sync_shards_download_applying_status( + &mut self, + shard_id: ShardId, + shard_sync_download: &mut ShardSyncDownload, + sync_hash: CryptoHash, + chain: &mut Chain, + now: DateTime, + ) -> Result<(), unc_chain::Error> { + // Keep waiting until our shard is on the list of results + // (these are set via callback from ClientActor - both for sync and catchup). + if let Some(result) = self.state_parts_apply_results.remove(&shard_id) { + match chain.set_state_finalize(shard_id, sync_hash, result) { + Ok(()) => { + *shard_sync_download = ShardSyncDownload { + downloads: vec![], + status: ShardSyncStatus::StateDownloadComplete, + } + } + Err(err) => { + // Cannot finalize the downloaded state. + // The reasonable behavior here is to start from the very beginning. + metrics::STATE_SYNC_DISCARD_PARTS + .with_label_values(&[&shard_id.to_string()]) + .inc(); + tracing::error!(target: "sync", %shard_id, %sync_hash, ?err, "State sync finalizing error"); + *shard_sync_download = ShardSyncDownload::new_download_state_header(now); + let shard_state_header = chain.get_state_header(shard_id, sync_hash)?; + let state_num_parts = shard_state_header.num_state_parts(); + chain.clear_downloaded_parts(shard_id, sync_hash, state_num_parts)?; + } + } + } + Ok(()) + } + + fn sync_shards_download_complete_status( + &mut self, + need_to_reshard: bool, + shard_sync_download: &mut ShardSyncDownload, + ) -> bool { + // If the shard layout is changing in this epoch - we have to apply it right now. + if need_to_reshard { + *shard_sync_download = ShardSyncDownload { + downloads: vec![], + status: ShardSyncStatus::ReshardingScheduling, + }; + false + } else { + // If there is no layout change - we're done. + *shard_sync_download = + ShardSyncDownload { downloads: vec![], status: ShardSyncStatus::StateSyncDone }; + true + } + } + + fn sync_shards_resharding_scheduling_status( + &mut self, + shard_id: ShardId, + shard_sync_download: &mut ShardSyncDownload, + sync_hash: CryptoHash, + chain: &Chain, + resharding_scheduler: &dyn Fn(ReshardingRequest), + me: &Option, + ) -> Result<(), unc_chain::Error> { + chain.build_state_for_resharding_preprocessing( + &sync_hash, + shard_id, + resharding_scheduler, + )?; + tracing::debug!(target: "sync", %shard_id, %sync_hash, ?me, "resharding scheduled"); + *shard_sync_download = + ShardSyncDownload { downloads: vec![], status: ShardSyncStatus::ReshardingApplying }; + Ok(()) + } + + /// Returns whether the State Sync for the given shard is complete. + fn sync_shards_resharding_applying_status( + &mut self, + shard_uid: ShardUId, + shard_sync_download: &mut ShardSyncDownload, + sync_hash: CryptoHash, + chain: &mut Chain, + ) -> Result { + let result = self.resharding_state_roots.remove(&shard_uid.shard_id()); + let mut shard_sync_done = false; + if let Some(state_roots) = result { + chain.build_state_for_split_shards_postprocessing( + shard_uid, + &sync_hash, + state_roots?, + )?; + *shard_sync_download = + ShardSyncDownload { downloads: vec![], status: ShardSyncStatus::StateSyncDone }; + shard_sync_done = true; + } + Ok(shard_sync_done) + } +} + +/// Returns parts that still need to be fetched. +fn parts_to_fetch( + new_shard_sync_download: &mut ShardSyncDownload, +) -> impl Iterator { + new_shard_sync_download + .downloads + .iter_mut() + .enumerate() + .filter(|(_, download)| download.run_me.load(Ordering::SeqCst)) + .map(|(part_id, download)| (part_id as u64, download)) +} + +/// Starts an asynchronous network request to external storage to fetch the given state part. +fn request_part_from_external_storage( + part_id: u64, + download: &mut DownloadStatus, + shard_id: ShardId, + sync_hash: CryptoHash, + epoch_id: &EpochId, + epoch_height: EpochHeight, + num_parts: u64, + chain_id: &str, + state_root: StateRoot, + semaphore: Arc, + external: ExternalConnection, + runtime_adapter: Arc, + state_parts_arbiter_handle: &ArbiterHandle, + state_parts_mpsc_tx: Sender, +) { + if !download.run_me.swap(false, Ordering::SeqCst) { + tracing::info!(target: "sync", %shard_id, part_id, "run_me is already false"); + return; + } + download.state_requests_count += 1; + download.last_target = None; + + let location = + external_storage_location(chain_id, epoch_id, epoch_height, shard_id, part_id, num_parts); + + match semaphore.try_acquire_owned() { + Ok(permit) => { + if state_parts_arbiter_handle.spawn({ + async move { + let result = external.get_part(shard_id, &location).await; + let part_id = PartId{ idx: part_id, total: num_parts }; + let part_result = match result { + Ok(data) => { + info!(target: "sync", ?shard_id, ?part_id, "downloaded state part"); + if runtime_adapter.validate_state_part(&state_root, part_id, &data) { + let mut store_update = runtime_adapter.store().store_update(); + let part_result = borsh::to_vec(&StatePartKey(sync_hash, shard_id, part_id.idx)).and_then(|key|{ + store_update.set(DBCol::StateParts, &key, &data); + store_update.commit() + }).and_then(|_|Ok(data.len() as u64)).map_err(|err|format!("Failed to store a state part. err={err:?}, state_root={state_root:?}, part_id={part_id:?}, shard_id={shard_id:?}")); + part_result + } else { + Err(format!("validate_state_part failed. state_root={state_root:?}, part_id={part_id:?}, shard_id={shard_id}")) + } + }, + Err(err) => Err(err.to_string()), + }; + match state_parts_mpsc_tx.send(StateSyncGetPartResult { + sync_hash, + shard_id, + part_id, + part_result, + }) { + Ok(_) => tracing::debug!(target: "sync", %shard_id, ?part_id, "Download response sent to processing thread."), + Err(err) => { + tracing::error!(target: "sync", ?err, %shard_id, ?part_id, "Unable to send part download response to processing thread."); + }, + } + drop(permit) + } + }) == false + { + tracing::error!(target: "sync", %shard_id, part_id, "Unable to spawn download. state_parts_arbiter has died."); + } + }, + Err(TryAcquireError::NoPermits) => { + download.run_me.store(true, Ordering::SeqCst); + }, + Err(TryAcquireError::Closed) => { + download.run_me.store(true, Ordering::SeqCst); + tracing::warn!(target: "sync", %shard_id, part_id, "Failed to schedule download. Semaphore closed."); + } + } +} + +/// Asynchronously requests a state part from a suitable peer. +fn request_part_from_peers( + part_id: u64, + peer_id: PeerId, + download: &mut DownloadStatus, + shard_id: ShardId, + sync_hash: CryptoHash, + network_adapter: &PeerManagerAdapter, +) { + download.run_me.store(false, Ordering::SeqCst); + download.state_requests_count += 1; + download.last_target = Some(peer_id.clone()); + let run_me = download.run_me.clone(); + + unc_performance_metrics::actix::spawn( + "StateSync", + network_adapter + .send_async(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::StateRequestPart { shard_id, sync_hash, part_id, peer_id }, + )) + .then(move |result| { + // TODO: possible optimization - in the current code, even if one of the targets it not present in the network graph + // (so we keep getting RouteNotFound) - we'll still keep trying to assign parts to it. + // Fortunately only once every 60 seconds (timeout value). + if let Ok(NetworkResponses::RouteNotFound) = result.map(|f| f.as_network_response()) + { + // Send a StateRequestPart on the next iteration + run_me.store(true, Ordering::SeqCst); + } + future::ready(()) + }), + ); +} + +fn sent_request_part( + peer_id: PeerId, + part_id: u64, + shard_id: ShardId, + sync_hash: CryptoHash, + last_part_id_requested: &mut HashMap<(PeerId, ShardId), PendingRequestStatus>, + requested_target: &mut lru::LruCache<(u64, CryptoHash), PeerId>, + timeout: Duration, +) { + // FIXME: something is wrong - the index should have a shard_id too. + requested_target.put((part_id, sync_hash), peer_id.clone()); + last_part_id_requested + .entry((peer_id, shard_id)) + .and_modify(|pending_request| { + pending_request.missing_parts += 1; + }) + .or_insert_with(|| PendingRequestStatus::new(timeout)); +} + +/// Works around how data requests to external storage are done. +/// This function investigates if the response is valid and updates `done` and `error` appropriately. +/// If the response is successful, then also writes the state part to the DB. +fn process_part_response( + part_id: u64, + shard_id: ShardId, + sync_hash: CryptoHash, + part_download: &mut DownloadStatus, + part_result: Result, +) { + match part_result { + Ok(data_len) => { + // No error, aka Success. + metrics::STATE_SYNC_EXTERNAL_PARTS_DONE + .with_label_values(&[&shard_id.to_string()]) + .inc(); + metrics::STATE_SYNC_EXTERNAL_PARTS_SIZE_DOWNLOADED + .with_label_values(&[&shard_id.to_string()]) + .inc_by(data_len); + part_download.done = true; + } + // The request failed without reaching the external storage. + Err(err) => { + metrics::STATE_SYNC_EXTERNAL_PARTS_FAILED + .with_label_values(&[&shard_id.to_string()]) + .inc(); + tracing::debug!(target: "sync", ?err, %shard_id, %sync_hash, part_id, "Failed to get a part from external storage, will retry"); + part_download.error = true; + } + } +} + +/// Create an abstract collection of elements to be shuffled. +/// Each element will appear in the shuffled output exactly `limit` times. +/// Use it as an iterator to access the shuffled collection. +/// +/// ```rust,ignore +/// let sampler = SamplerLimited::new(vec![1, 2, 3], 2); +/// +/// let res = sampler.collect::>(); +/// +/// assert!(res.len() == 6); +/// assert!(res.iter().filter(|v| v == 1).count() == 2); +/// assert!(res.iter().filter(|v| v == 2).count() == 2); +/// assert!(res.iter().filter(|v| v == 3).count() == 2); +/// ``` +/// +/// Out of the 90 possible values of `res` in the code above on of them is: +/// +/// ``` +/// vec![1, 2, 1, 3, 3, 2]; +/// ``` +struct SamplerLimited { + data: Vec, + limit: Vec, +} + +impl SamplerLimited { + fn new(data: Vec, limit: u64) -> Self { + if limit == 0 { + Self { data: vec![], limit: vec![] } + } else { + let len = data.len(); + Self { data, limit: vec![limit; len] } + } + } +} + +impl Iterator for SamplerLimited { + type Item = T; + + fn next(&mut self) -> Option { + if self.limit.is_empty() { + None + } else { + let len = self.limit.len(); + let ix = thread_rng().gen_range(0..len); + self.limit[ix] -= 1; + + if self.limit[ix] == 0 { + if ix + 1 != len { + self.limit[ix] = self.limit[len - 1]; + self.data.swap(ix, len - 1); + } + + self.limit.pop(); + self.data.pop() + } else { + Some(self.data[ix].clone()) + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use actix::System; + use actix_rt::Arbiter; + use unc_actix_test_utils::run_actix; + use unc_chain::test_utils; + use unc_chain::{test_utils::process_block_sync, BlockProcessingArtifact, Provenance}; + use unc_crypto::SecretKey; + use unc_epoch_manager::EpochManagerAdapter; + use unc_network::test_utils::MockPeerManagerAdapter; + use unc_network::types::PeerInfo; + use unc_primitives::state_sync::{ + CachedParts, ShardStateSyncResponseHeader, ShardStateSyncResponseV3, + }; + use unc_primitives::{test_utils::TestBlockBuilder, types::EpochId}; + + #[test] + // Start a new state sync - and check that it asks for a header. + fn test_ask_for_header() { + let mock_peer_manager = Arc::new(MockPeerManagerAdapter::default()); + let mut state_sync = StateSync::new( + mock_peer_manager.clone().into(), + TimeDuration::from_secs(1), + "chain_id", + &SyncConfig::Peers, + false, + ); + let mut new_shard_sync = HashMap::new(); + + let (mut chain, kv, runtime, signer) = test_utils::setup(); + + // TODO: lower the epoch length + for _ in 0..(chain.epoch_length + 1) { + let prev = chain.get_block(&chain.head().unwrap().last_block_hash).unwrap(); + let block = if kv.is_next_block_epoch_start(prev.hash()).unwrap() { + TestBlockBuilder::new(&prev, signer.clone()) + .epoch_id(prev.header().next_epoch_id().clone()) + .next_epoch_id(EpochId { 0: *prev.hash() }) + .next_bp_hash(*prev.header().next_bp_hash()) + .build() + } else { + TestBlockBuilder::new(&prev, signer.clone()).build() + }; + + process_block_sync( + &mut chain, + &None, + block.into(), + Provenance::PRODUCED, + &mut BlockProcessingArtifact::default(), + ) + .unwrap(); + } + + let request_hash = &chain.head().unwrap().last_block_hash; + let state_sync_header = chain.get_state_response_header(0, *request_hash).unwrap(); + let state_sync_header = match state_sync_header { + ShardStateSyncResponseHeader::V1(_) => panic!("Invalid header"), + ShardStateSyncResponseHeader::V2(internal) => internal, + }; + + let apply_parts_fn = move |_: ApplyStatePartsRequest| {}; + let resharding_fn = move |_: ReshardingRequest| {}; + + let secret_key = SecretKey::from_random(unc_crypto::KeyType::ED25519); + let public_key = secret_key.public_key(); + let peer_id = PeerId::new(public_key); + let highest_height_peer_info = HighestHeightPeerInfo { + peer_info: PeerInfo { id: peer_id.clone(), addr: None, account_id: None }, + genesis_id: Default::default(), + highest_block_height: chain.epoch_length + 10, + highest_block_hash: Default::default(), + tracked_shards: vec![0], + archival: false, + }; + + run_actix(async { + state_sync + .run( + &None, + *request_hash, + &mut new_shard_sync, + &mut chain, + kv.as_ref(), + &[highest_height_peer_info], + vec![0], + &apply_parts_fn, + &resharding_fn, + &Arbiter::new().handle(), + false, + runtime, + ) + .unwrap(); + + // Wait for the message that is sent to peer manager. + mock_peer_manager.notify.notified().await; + let request = mock_peer_manager.pop().unwrap(); + + assert_eq!( + NetworkRequests::StateRequestHeader { + shard_id: 0, + sync_hash: *request_hash, + peer_id: peer_id.clone(), + }, + request.as_network_requests() + ); + + assert_eq!(1, new_shard_sync.len()); + let download = new_shard_sync.get(&0).unwrap(); + + assert_eq!(download.status, ShardSyncStatus::StateDownloadHeader); + + assert_eq!(download.downloads.len(), 1); + let download_status = &download.downloads[0]; + + // 'run me' is false - as we've just executed this peer manager request. + assert_eq!(download_status.run_me.load(Ordering::SeqCst), false); + assert_eq!(download_status.error, false); + assert_eq!(download_status.done, false); + assert_eq!(download_status.state_requests_count, 1); + assert_eq!(download_status.last_target, Some(peer_id),); + + // Now let's simulate header return message. + + let state_response = ShardStateSyncResponse::V3(ShardStateSyncResponseV3 { + header: Some(state_sync_header), + part: None, + cached_parts: Some(CachedParts::AllParts), + can_generate: true, + }); + + state_sync.update_download_on_state_response_message( + &mut new_shard_sync.get_mut(&0).unwrap(), + *request_hash, + 0, + state_response, + &mut chain, + ); + + let download = new_shard_sync.get(&0).unwrap(); + assert_eq!(download.status, ShardSyncStatus::StateDownloadHeader); + // Download should be marked as done. + assert_eq!(download.downloads[0].done, true); + + System::current().stop() + }); + } +} diff --git a/chain/client/src/sync/sync_actor.rs b/chain/client/src/sync/sync_actor.rs new file mode 100644 index 000000000..0f3b38126 --- /dev/null +++ b/chain/client/src/sync/sync_actor.rs @@ -0,0 +1,113 @@ +use super::adapter::{SyncMessage as ClientSyncMessage, SyncShardInfo}; +use unc_async::messaging::Sender; +use unc_network::types::{PeerManagerMessageRequest, StateSyncResponse}; +use unc_o11y::{handler_debug_span, OpenTelemetrySpanExt, WithSpanContext}; +use unc_performance_metrics_macros::perf; +use unc_primitives::hash::CryptoHash; +use unc_store::ShardUId; +use tracing::{debug, info, warn}; + +/// Message channels +#[allow(dead_code)] +struct MessageSenders { + /// Used to send messages to client + client_adapter: Sender, + /// Used to send messages to peer manager + network_adapter: Sender, +} + +/// Actor that runs state sync for a shard +#[allow(dead_code)] +pub struct SyncActor { + /// Shard being synced + shard_uid: ShardUId, + /// Hash of the state that is downloaded + sync_hash: CryptoHash, + /// Channels used to communicate with other actors + senders: MessageSenders, +} + +impl SyncActor { + pub fn new( + shard_uid: ShardUId, + client_adapter: Sender, + network_adapter: Sender, + ) -> Self { + Self { + shard_uid, + sync_hash: CryptoHash::new(), + senders: MessageSenders { client_adapter, network_adapter }, + } + } + + fn handle_client_sync_message(&mut self, msg: ClientSyncMessage) { + match msg { + ClientSyncMessage::StartSync(SyncShardInfo { sync_hash, shard_uid }) => { + assert_eq!(shard_uid, self.shard_uid, "Message is not for this shard SyncActor"); + // Start syncing the shard. + if self.sync_hash == sync_hash { + debug!(target: "sync", shard_id = ?self.shard_uid.shard_id, "Sync already running."); + return; + } + info!(target: "sync", shard_id = ?self.shard_uid.shard_id, "Starting sync on shard"); + // TODO: Add logic to commence state sync. + self.sync_hash = sync_hash; + } + ClientSyncMessage::SyncDone(_) => { + warn!(target: "sync", "Unsupported message received by SyncActor: SyncDone."); + } + } + } + + fn handle_network_sync_message(&mut self, msg: StateSyncResponse) { + match msg { + StateSyncResponse::HeaderResponse => { + debug!(target: "sync", shard_id = ?self.shard_uid.shard_id, "Got header response"); + } + StateSyncResponse::PartResponse => { + warn!(target: "sync", "Unsupported message received by SyncActor: SyncDone."); + } + } + } +} + +/// Control the flow of the state sync actor +impl actix::Actor for SyncActor { + type Context = actix::Context; + + fn started(&mut self, _ctx: &mut Self::Context) { + info!(target: "sync", shard_id = ?self.shard_uid.shard_id, "Sync actor started."); + } + + fn stopped(&mut self, _ctx: &mut Self::Context) { + info!(target: "sync", shard_id = ?self.shard_uid.shard_id, "Sync actor stopped."); + } +} + +/// Process messages from client +impl actix::Handler> for SyncActor { + type Result = (); + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "sync", msg); + self.handle_client_sync_message(msg); + } +} + +/// Process messages from network +impl actix::Handler> for SyncActor { + type Result = (); + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "sync", msg); + self.handle_network_sync_message(msg); + } +} diff --git a/chain/client/src/sync_jobs_actor.rs b/chain/client/src/sync_jobs_actor.rs new file mode 100644 index 000000000..499a921d0 --- /dev/null +++ b/chain/client/src/sync_jobs_actor.rs @@ -0,0 +1,185 @@ +use crate::ClientActor; +use actix::AsyncContext; +use std::time::Duration; + +use unc_chain::chain::{ + do_apply_chunks, ApplyStatePartsRequest, ApplyStatePartsResponse, BlockCatchUpRequest, + BlockCatchUpResponse, +}; +use unc_chain::resharding::ReshardingRequest; +use unc_chain::Chain; +use unc_o11y::{handler_debug_span, OpenTelemetrySpanExt, WithSpanContext, WithSpanContextExt}; +use unc_performance_metrics_macros::perf; +use unc_primitives::state_part::PartId; +use unc_primitives::state_sync::StatePartKey; +use unc_primitives::types::ShardId; +use unc_store::DBCol; + +pub(crate) struct SyncJobsActor { + pub(crate) client_addr: actix::Addr, +} + +pub(crate) fn create_sync_job_scheduler(address: actix::Addr) -> Box +where + M: actix::Message + Send + 'static, + M::Result: Send, + SyncJobsActor: actix::Handler>, +{ + Box::new(move |msg: M| { + if let Err(err) = address.try_send(msg.with_span_context()) { + match err { + actix::dev::SendError::Full(request) => { + address.do_send(request); + } + actix::dev::SendError::Closed(_) => { + tracing::error!("Can't send message to SyncJobsActor, mailbox is closed"); + } + } + } + }) +} + +impl SyncJobsActor { + pub(crate) const MAILBOX_CAPACITY: usize = 100; + + fn apply_parts( + &mut self, + msg: &ApplyStatePartsRequest, + ) -> Result<(), unc_chain_primitives::error::Error> { + let _span = tracing::debug_span!(target: "client", "apply_parts").entered(); + let store = msg.runtime_adapter.store(); + + let shard_id = msg.shard_uid.shard_id as ShardId; + for part_id in 0..msg.num_parts { + let key = borsh::to_vec(&StatePartKey(msg.sync_hash, shard_id, part_id))?; + let part = store.get(DBCol::StateParts, &key)?.unwrap(); + + msg.runtime_adapter.apply_state_part( + shard_id, + &msg.state_root, + PartId::new(part_id, msg.num_parts), + &part, + &msg.epoch_id, + )?; + } + + Ok(()) + } + + /// Clears flat storage before applying state parts. + /// Returns whether the flat storage state was cleared. + fn clear_flat_state( + &mut self, + msg: &ApplyStatePartsRequest, + ) -> Result { + let _span = tracing::debug_span!(target: "client", "clear_flat_state").entered(); + let mut store_update = msg.runtime_adapter.store().store_update(); + let success = msg + .runtime_adapter + .get_flat_storage_manager() + .remove_flat_storage_for_shard(msg.shard_uid, &mut store_update)?; + store_update.commit()?; + Ok(success) + } +} + +impl actix::Actor for SyncJobsActor { + type Context = actix::Context; +} + +impl actix::Handler> for SyncJobsActor { + type Result = (); + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + let shard_id = msg.shard_uid.shard_id as ShardId; + match self.clear_flat_state(&msg) { + Err(err) => { + self.client_addr.do_send( + ApplyStatePartsResponse { + apply_result: Err(err), + shard_id, + sync_hash: msg.sync_hash, + } + .with_span_context(), + ); + return; + } + Ok(false) => { + // Can't panic here, because that breaks many KvRuntime tests. + tracing::error!(target: "client", shard_uid = ?msg.shard_uid, "Failed to delete Flat State, but proceeding with applying state parts."); + } + Ok(true) => { + tracing::debug!(target: "client", shard_uid = ?msg.shard_uid, "Deleted all Flat State"); + } + } + + let result = self.apply_parts(&msg); + self.client_addr.do_send( + ApplyStatePartsResponse { apply_result: result, shard_id, sync_hash: msg.sync_hash } + .with_span_context(), + ); + } +} + +impl actix::Handler> for SyncJobsActor { + type Result = (); + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let results = do_apply_chunks(msg.block_hash, msg.block_height, msg.work); + + self.client_addr.do_send( + BlockCatchUpResponse { sync_hash: msg.sync_hash, block_hash: msg.block_hash, results } + .with_span_context(), + ); + } +} + +impl actix::Handler> for SyncJobsActor { + type Result = (); + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + context: &mut Self::Context, + ) -> Self::Result { + let (_span, mut resharding_request) = handler_debug_span!(target: "resharding", msg); + let config = resharding_request.config.get(); + + // Wait for the initial delay. It should only be used in tests. + let initial_delay = config.initial_delay; + if resharding_request.curr_poll_time == Duration::ZERO && initial_delay > Duration::ZERO { + tracing::debug!(target: "resharding", ?resharding_request, ?initial_delay, "Waiting for the initial delay"); + resharding_request.curr_poll_time += initial_delay; + context.notify_later(resharding_request.with_span_context(), initial_delay); + return; + } + + if Chain::retry_build_state_for_split_shards(&resharding_request) { + // Actix implementation let's us send message to ourselves with a delay. + // In case snapshots are not ready yet, we will retry resharding later. + let retry_delay = config.retry_delay; + tracing::debug!(target: "resharding", ?resharding_request, ?retry_delay, "Snapshot missing, retrying resharding later"); + resharding_request.curr_poll_time += retry_delay; + context.notify_later(resharding_request.with_span_context(), retry_delay); + return; + } + + tracing::debug!(target: "resharding", ?resharding_request, "Starting resharding"); + let response = Chain::build_state_for_split_shards(resharding_request); + self.client_addr.do_send(response.with_span_context()); + } +} diff --git a/chain/client/src/test_utils/block_stats.rs b/chain/client/src/test_utils/block_stats.rs new file mode 100644 index 000000000..5e537c19c --- /dev/null +++ b/chain/client/src/test_utils/block_stats.rs @@ -0,0 +1,107 @@ +use std::cmp::max; +use std::collections::HashMap; +use std::time::{Duration, Instant}; + +use unc_primitives::block::Block; +use unc_primitives::hash::CryptoHash; +use unc_primitives::static_clock::StaticClock; +use tracing::info; + +pub struct BlockStats { + hash2depth: HashMap, + num_blocks: u64, + max_chain_length: u64, + last_check: Instant, + max_divergence: u64, + last_hash: Option, + parent: HashMap, +} + +impl BlockStats { + pub(crate) fn new() -> BlockStats { + BlockStats { + hash2depth: HashMap::new(), + num_blocks: 0, + max_chain_length: 0, + last_check: StaticClock::instant(), + max_divergence: 0, + last_hash: None, + parent: HashMap::new(), + } + } + + fn calculate_distance(&mut self, mut lhs: CryptoHash, mut rhs: CryptoHash) -> u64 { + let mut dlhs = *self.hash2depth.get(&lhs).unwrap(); + let mut drhs = *self.hash2depth.get(&rhs).unwrap(); + + let mut result: u64 = 0; + while dlhs > drhs { + lhs = *self.parent.get(&lhs).unwrap(); + dlhs -= 1; + result += 1; + } + while dlhs < drhs { + rhs = *self.parent.get(&rhs).unwrap(); + drhs -= 1; + result += 1; + } + while lhs != rhs { + lhs = *self.parent.get(&lhs).unwrap(); + rhs = *self.parent.get(&rhs).unwrap(); + result += 2; + } + result + } + + pub(crate) fn add_block(&mut self, block: &Block) { + if self.hash2depth.contains_key(block.hash()) { + return; + } + let prev_height = self.hash2depth.get(block.header().prev_hash()).map(|v| *v).unwrap_or(0); + self.hash2depth.insert(*block.hash(), prev_height + 1); + self.num_blocks += 1; + self.max_chain_length = max(self.max_chain_length, prev_height + 1); + self.parent.insert(*block.hash(), *block.header().prev_hash()); + + if let Some(last_hash2) = self.last_hash { + self.max_divergence = + max(self.max_divergence, self.calculate_distance(last_hash2, *block.hash())); + } + + self.last_hash = Some(*block.hash()); + } + + pub fn check_stats(&mut self, force: bool) { + let now = StaticClock::instant(); + let diff = now.duration_since(self.last_check); + if !force && diff.lt(&Duration::from_secs(60)) { + return; + } + self.last_check = now; + let cur_ratio = (self.num_blocks as f64) / (max(1, self.max_chain_length) as f64); + info!( + "Block stats: ratio: {:.2}, num_blocks: {} max_chain_length: {} max_divergence: {}", + cur_ratio, self.num_blocks, self.max_chain_length, self.max_divergence + ); + } + + pub fn check_block_ratio(&mut self, min_ratio: Option, max_ratio: Option) { + let cur_ratio = (self.num_blocks as f64) / (max(1, self.max_chain_length) as f64); + if let Some(min_ratio2) = min_ratio { + if cur_ratio < min_ratio2 { + panic!( + "ratio of blocks to longest chain is too low got: {:.2} expected: {:.2}", + cur_ratio, min_ratio2 + ); + } + } + if let Some(max_ratio2) = max_ratio { + if cur_ratio > max_ratio2 { + panic!( + "ratio of blocks to longest chain is too high got: {:.2} expected: {:.2}", + cur_ratio, max_ratio2 + ); + } + } + } +} diff --git a/chain/client/src/test_utils/client.rs b/chain/client/src/test_utils/client.rs new file mode 100644 index 000000000..bf94acd78 --- /dev/null +++ b/chain/client/src/test_utils/client.rs @@ -0,0 +1,279 @@ +// FIXME(nagisa): Is there a good reason we're triggering this? Luckily though this is just test +// code so we're in the clear. +#![allow(clippy::arc_with_non_send_sync)] + +use std::mem::swap; +use std::sync::{Arc, RwLock}; + +use crate::Client; +use actix_rt::{Arbiter, System}; +use itertools::Itertools; +use unc_chain::chain::{do_apply_chunks, BlockCatchUpRequest}; +use unc_chain::resharding::ReshardingRequest; +use unc_chain::test_utils::{wait_for_all_blocks_in_processing, wait_for_block_in_processing}; +use unc_chain::{Chain, ChainStoreAccess, Provenance}; +use unc_client_primitives::types::Error; +use unc_network::types::HighestHeightPeerInfo; +use unc_primitives::block::Block; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::{merklize, MerklePath, PartialMerkleTree}; +use unc_primitives::receipt::Receipt; +use unc_primitives::sharding::{EncodedShardChunk, ReedSolomonWrapper}; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{BlockHeight, ShardId}; +use unc_primitives::utils::MaybeValidated; +use unc_primitives::version::PROTOCOL_VERSION; +use num_rational::Ratio; + +impl Client { + /// Unlike Client::start_process_block, which returns before the block finishes processing + /// This function waits until the block is processed. + /// `should_produce_chunk`: Normally, if a block is accepted, client will try to produce + /// chunks for the next block if it is the chunk producer. + /// If `should_produce_chunk` is set to false, client will skip the + /// chunk production. This is useful in tests that need to tweak + /// the produced chunk content. + fn process_block_sync_with_produce_chunk_options( + &mut self, + block: MaybeValidated, + provenance: Provenance, + should_produce_chunk: bool, + ) -> Result, unc_chain::Error> { + self.start_process_block(block, provenance, Arc::new(|_| {}))?; + wait_for_all_blocks_in_processing(&mut self.chain); + let (accepted_blocks, errors) = + self.postprocess_ready_blocks(Arc::new(|_| {}), should_produce_chunk); + assert!(errors.is_empty(), "unexpected errors when processing blocks: {errors:#?}"); + Ok(accepted_blocks) + } + + pub fn process_block_test( + &mut self, + block: MaybeValidated, + provenance: Provenance, + ) -> Result, unc_chain::Error> { + self.process_block_sync_with_produce_chunk_options(block, provenance, true) + } + + pub fn process_block_test_no_produce_chunk( + &mut self, + block: MaybeValidated, + provenance: Provenance, + ) -> Result, unc_chain::Error> { + self.process_block_sync_with_produce_chunk_options(block, provenance, false) + } + + /// This function finishes processing all blocks that started being processed. + pub fn finish_blocks_in_processing(&mut self) -> Vec { + let mut accepted_blocks = vec![]; + while wait_for_all_blocks_in_processing(&mut self.chain) { + accepted_blocks.extend(self.postprocess_ready_blocks(Arc::new(|_| {}), true).0); + } + accepted_blocks + } + + /// This function finishes processing block with hash `hash`, if the processing of that block + /// has started. + pub fn finish_block_in_processing(&mut self, hash: &CryptoHash) -> Vec { + if let Ok(()) = wait_for_block_in_processing(&mut self.chain, hash) { + let (accepted_blocks, _) = self.postprocess_ready_blocks(Arc::new(|_| {}), true); + return accepted_blocks; + } + vec![] + } +} + +fn create_chunk_on_height_for_shard( + client: &mut Client, + next_height: BlockHeight, + shard_id: ShardId, +) -> (EncodedShardChunk, Vec, Vec) { + let last_block_hash = client.chain.head().unwrap().last_block_hash; + let last_block = client.chain.get_block(&last_block_hash).unwrap(); + client + .produce_chunk( + last_block_hash, + &client.epoch_manager.get_epoch_id_from_prev_block(&last_block_hash).unwrap(), + Chain::get_prev_chunk_header(client.epoch_manager.as_ref(), &last_block, shard_id) + .unwrap(), + next_height, + shard_id, + ) + .unwrap() + .unwrap() +} + +pub fn create_chunk_on_height( + client: &mut Client, + next_height: BlockHeight, +) -> (EncodedShardChunk, Vec, Vec) { + create_chunk_on_height_for_shard(client, next_height, 0) +} + +pub fn create_chunk_with_transactions( + client: &mut Client, + transactions: Vec, +) -> (EncodedShardChunk, Vec, Vec, Block) { + create_chunk(client, Some(transactions), None) +} + +/// Create a chunk with specified transactions and possibly a new state root. +/// Useful for writing tests with challenges. +pub fn create_chunk( + client: &mut Client, + replace_transactions: Option>, + replace_tx_root: Option, +) -> (EncodedShardChunk, Vec, Vec, Block) { + let last_block = client.chain.get_block_by_height(client.chain.head().unwrap().height).unwrap(); + let next_height = last_block.header().height() + 1; + let (mut chunk, mut merkle_paths, receipts) = client + .produce_chunk( + *last_block.hash(), + last_block.header().epoch_id(), + last_block.chunks()[0].clone(), + next_height, + 0, + ) + .unwrap() + .unwrap(); + let should_replace = replace_transactions.is_some() || replace_tx_root.is_some(); + let transactions = replace_transactions.unwrap_or_else(Vec::new); + let tx_root = match replace_tx_root { + Some(root) => root, + None => merklize(&transactions).0, + }; + // reconstruct the chunk with changes (if any) + if should_replace { + // The best way it to decode chunk, replace transactions and then recreate encoded chunk. + let total_parts = client.chain.epoch_manager.num_total_parts(); + let data_parts = client.chain.epoch_manager.num_data_parts(); + let decoded_chunk = chunk.decode_chunk(data_parts).unwrap(); + let parity_parts = total_parts - data_parts; + let mut rs = ReedSolomonWrapper::new(data_parts, parity_parts); + + let signer = client.validator_signer.as_ref().unwrap().clone(); + let header = chunk.cloned_header(); + let (mut encoded_chunk, mut new_merkle_paths) = EncodedShardChunk::new( + *header.prev_block_hash(), + header.prev_state_root(), + header.prev_outcome_root(), + header.height_created(), + header.shard_id(), + &mut rs, + header.prev_gas_used(), + header.gas_limit(), + header.prev_balance_burnt(), + tx_root, + header.prev_validator_power_proposals().collect(), + header.prev_validator_frozen_proposals().collect(), + transactions, + decoded_chunk.prev_outgoing_receipts(), + header.prev_outgoing_receipts_root(), + &*signer, + PROTOCOL_VERSION, + ) + .unwrap(); + swap(&mut chunk, &mut encoded_chunk); + swap(&mut merkle_paths, &mut new_merkle_paths); + } + match &mut chunk { + EncodedShardChunk::V1(chunk) => { + chunk.header.height_included = next_height; + } + EncodedShardChunk::V2(chunk) => { + *chunk.header.height_included_mut() = next_height; + } + } + let block_merkle_tree = + client.chain.chain_store().get_block_merkle_tree(last_block.hash()).unwrap(); + let mut block_merkle_tree = PartialMerkleTree::clone(&block_merkle_tree); + block_merkle_tree.insert(*last_block.hash()); + let block = Block::produce( + PROTOCOL_VERSION, + PROTOCOL_VERSION, + last_block.header(), + next_height, + last_block.header().block_ordinal() + 1, + vec![chunk.cloned_header()], + last_block.header().epoch_id().clone(), + last_block.header().next_epoch_id().clone(), + None, + vec![], + Ratio::new(0, 1), + 0, + 100, + None, + vec![], + vec![], + &*client.validator_signer.as_ref().unwrap().clone(), + *last_block.header().next_bp_hash(), + block_merkle_tree.root(), + None, + ); + (chunk, merkle_paths, receipts, block) +} + +/// Keep running catchup until there is no more catchup work that can be done +/// Note that this function does not necessarily mean that all blocks are caught up. +/// It's possible that some blocks that need to be caught up are still being processed +/// and the catchup process can't catch up on these blocks yet. +pub fn run_catchup( + client: &mut Client, + highest_height_peers: &[HighestHeightPeerInfo], +) -> Result<(), Error> { + let f = |_| {}; + let block_messages = Arc::new(RwLock::new(vec![])); + let block_inside_messages = block_messages.clone(); + let block_catch_up = move |msg: BlockCatchUpRequest| { + block_inside_messages.write().unwrap().push(msg); + }; + let resharding_messages = Arc::new(RwLock::new(vec![])); + let resharding_inside_messages = resharding_messages.clone(); + let resharding = move |msg: ReshardingRequest| { + resharding_inside_messages.write().unwrap().push(msg); + }; + let _ = System::new(); + let state_parts_arbiter_handle = Arbiter::new().handle(); + loop { + client.run_catchup( + highest_height_peers, + &f, + &block_catch_up, + &resharding, + Arc::new(|_| {}), + &state_parts_arbiter_handle, + )?; + let mut catchup_done = true; + for msg in block_messages.write().unwrap().drain(..) { + let results = do_apply_chunks(msg.block_hash, msg.block_height, msg.work) + .into_iter() + .map(|res| res.1) + .collect_vec(); + if let Some((_, _, blocks_catch_up_state)) = + client.catchup_state_syncs.get_mut(&msg.sync_hash) + { + assert!(blocks_catch_up_state.scheduled_blocks.remove(&msg.block_hash)); + blocks_catch_up_state.processed_blocks.insert(msg.block_hash, results); + } else { + panic!("block catch up processing result from unknown sync hash"); + } + catchup_done = false; + } + for msg in resharding_messages.write().unwrap().drain(..) { + let response = Chain::build_state_for_split_shards(msg); + if let Some((sync, _, _)) = client.catchup_state_syncs.get_mut(&response.sync_hash) { + // We are doing catchup + sync.set_resharding_result(response.shard_id, response.new_state_roots); + } else { + client + .state_sync + .set_resharding_result(response.shard_id, response.new_state_roots); + } + catchup_done = false; + } + if catchup_done { + break; + } + } + Ok(()) +} diff --git a/chain/client/src/test_utils/mod.rs b/chain/client/src/test_utils/mod.rs new file mode 100644 index 000000000..e2c3081bf --- /dev/null +++ b/chain/client/src/test_utils/mod.rs @@ -0,0 +1,13 @@ +pub mod block_stats; +pub mod client; +pub mod peer_manager_mock; +pub mod setup; +pub mod test_env; +pub mod test_env_builder; + +pub use block_stats::*; +pub use client::*; +pub use peer_manager_mock::*; +pub use setup::*; +pub use test_env::*; +pub use test_env_builder::*; diff --git a/chain/client/src/test_utils/peer_manager_mock.rs b/chain/client/src/test_utils/peer_manager_mock.rs new file mode 100644 index 000000000..039133d0d --- /dev/null +++ b/chain/client/src/test_utils/peer_manager_mock.rs @@ -0,0 +1,39 @@ +use unc_network::types::SetChainInfo; +use unc_network::types::{PeerManagerMessageRequest, PeerManagerMessageResponse}; + +pub struct PeerManagerMock { + handle: Box< + dyn FnMut( + PeerManagerMessageRequest, + &mut actix::Context, + ) -> PeerManagerMessageResponse, + >, +} + +impl PeerManagerMock { + pub(crate) fn new( + f: impl 'static + + FnMut( + PeerManagerMessageRequest, + &mut actix::Context, + ) -> PeerManagerMessageResponse, + ) -> Self { + Self { handle: Box::new(f) } + } +} + +impl actix::Actor for PeerManagerMock { + type Context = actix::Context; +} + +impl actix::Handler for PeerManagerMock { + type Result = PeerManagerMessageResponse; + fn handle(&mut self, msg: PeerManagerMessageRequest, ctx: &mut Self::Context) -> Self::Result { + (self.handle)(msg, ctx) + } +} + +impl actix::Handler for PeerManagerMock { + type Result = (); + fn handle(&mut self, _msg: SetChainInfo, _ctx: &mut Self::Context) {} +} diff --git a/chain/client/src/test_utils/setup.rs b/chain/client/src/test_utils/setup.rs new file mode 100644 index 000000000..8fa5844c4 --- /dev/null +++ b/chain/client/src/test_utils/setup.rs @@ -0,0 +1,1109 @@ +// FIXME(nagisa): Is there a good reason we're triggering this? Luckily though this is just test +// code so we're in the clear. +#![allow(clippy::arc_with_non_send_sync)] + +use super::block_stats::BlockStats; +use super::peer_manager_mock::PeerManagerMock; +use crate::adapter::{ + AnnounceAccountRequest, BlockApproval, BlockHeadersRequest, BlockHeadersResponse, BlockRequest, + BlockResponse, SetNetworkInfo, StateRequestHeader, StateRequestPart, +}; +use crate::{start_view_client, Client, ClientActor, SyncAdapter, SyncStatus, ViewClientActor}; +use actix::{Actor, Addr, AsyncContext, Context}; +use actix_rt::System; +use chrono::DateTime; +use chrono::Utc; +use futures::{future, FutureExt}; +use unc_async::actix::AddrWithAutoSpanContextExt; +use unc_async::messaging::{CanSend, IntoSender, LateBoundSender, Sender}; +use unc_async::time; +use unc_chain::state_snapshot_actor::SnapshotCallbacks; +use unc_chain::test_utils::{KeyValueRuntime, MockEpochManager, ValidatorSchedule}; +use unc_chain::types::{ChainConfig, RuntimeAdapter}; +use unc_chain::{Chain, ChainGenesis, DoomslugThresholdMode}; +use unc_chain_configs::{ClientConfig, MutableConfigValue, ReshardingConfig}; +use unc_chunks::adapter::ShardsManagerRequestFromClient; +use unc_chunks::client::ShardsManagerResponse; +use unc_chunks::shards_manager_actor::start_shards_manager; +use unc_chunks::test_utils::SynchronousShardsManagerAdapter; +use unc_chunks::ShardsManager; +use unc_crypto::{KeyType, PublicKey}; +use unc_epoch_manager::shard_tracker::{ShardTracker, TrackedConfig}; +use unc_epoch_manager::EpochManagerAdapter; +use unc_network::shards_manager::ShardsManagerRequestFromNetwork; +use unc_network::types::{BlockInfo, PeerChainInfo}; +use unc_network::types::{ + ConnectedPeerInfo, FullPeerInfo, NetworkRequests, NetworkResponses, PeerManagerAdapter, +}; +use unc_network::types::{NetworkInfo, PeerManagerMessageRequest, PeerManagerMessageResponse}; +use unc_network::types::{PeerInfo, PeerType}; +use unc_o11y::WithSpanContextExt; +use unc_primitives::block::{ApprovalInner, Block, GenesisId}; +use unc_primitives::epoch_manager::RngSeed; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::network::PeerId; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::test_utils::create_test_signer; +use unc_primitives::types::{AccountId, BlockHeightDelta, NumBlocks, NumSeats}; +use unc_primitives::validator_signer::ValidatorSigner; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_store::test_utils::create_test_store; +use unc_store::Store; +use unc_telemetry::TelemetryActor; +use num_rational::Ratio; +use once_cell::sync::OnceCell; +use rand::{thread_rng, Rng}; +use std::cmp::max; +use std::collections::{HashMap, HashSet}; +use std::ops::DerefMut; +use std::sync::{Arc, RwLock}; +use std::time::{Duration, Instant}; + +pub const TEST_SEED: RngSeed = [3; 32]; + +/// min block production time in milliseconds +pub const MIN_BLOCK_PROD_TIME: Duration = Duration::from_millis(100); +/// max block production time in milliseconds +pub const MAX_BLOCK_PROD_TIME: Duration = Duration::from_millis(200); + +/// Sets up ClientActor and ViewClientActor viewing the same store/runtime. +pub fn setup( + vs: ValidatorSchedule, + epoch_length: BlockHeightDelta, + account_id: AccountId, + skip_sync_wait: bool, + min_block_prod_time: u64, + max_block_prod_time: u64, + enable_doomslug: bool, + archive: bool, + epoch_sync_enabled: bool, + state_sync_enabled: bool, + network_adapter: PeerManagerAdapter, + transaction_validity_period: NumBlocks, + genesis_time: DateTime, + ctx: &Context, +) -> (Block, ClientActor, Addr, ShardsManagerAdapterForTest) { + let store = create_test_store(); + let num_validator_seats = vs.all_block_producers().count() as NumSeats; + let epoch_manager = MockEpochManager::new_with_validators(store.clone(), vs, epoch_length); + let shard_tracker = ShardTracker::new(TrackedConfig::AllShards, epoch_manager.clone()); + let runtime = KeyValueRuntime::new_with_no_gc(store.clone(), epoch_manager.as_ref(), archive); + let chain_genesis = ChainGenesis { + time: genesis_time, + height: 0, + gas_limit: 1_000_000, + min_gas_price: 100, + max_gas_price: 1_000_000_000, + total_supply: 3_000_000_000_000_000_000_000_000_000_000_000, + gas_price_adjustment_rate: Ratio::from_integer(0), + transaction_validity_period, + epoch_length, + protocol_version: PROTOCOL_VERSION, + }; + let doomslug_threshold_mode = if enable_doomslug { + DoomslugThresholdMode::TwoThirds + } else { + DoomslugThresholdMode::NoApprovals + }; + let chain = Chain::new( + epoch_manager.clone(), + shard_tracker.clone(), + runtime.clone(), + &chain_genesis, + doomslug_threshold_mode, + ChainConfig { + save_trie_changes: true, + background_migration_threads: 1, + resharding_config: MutableConfigValue::new( + ReshardingConfig::default(), + "resharding_config", + ), + }, + None, + ) + .unwrap(); + let genesis_block = chain.get_block(&chain.genesis().hash().clone()).unwrap(); + + let signer = Arc::new(create_test_signer(account_id.as_str())); + let telemetry = TelemetryActor::default().start(); + let config = ClientConfig::test( + skip_sync_wait, + min_block_prod_time, + max_block_prod_time, + num_validator_seats, + archive, + true, + epoch_sync_enabled, + state_sync_enabled, + ); + + let adv = crate::adversarial::Controls::default(); + + let view_client_addr = start_view_client( + Some(signer.validator_id().clone()), + chain_genesis.clone(), + epoch_manager.clone(), + shard_tracker.clone(), + runtime.clone(), + network_adapter.clone(), + config.clone(), + adv.clone(), + ); + + let (shards_manager_addr, _) = start_shards_manager( + epoch_manager.clone(), + shard_tracker.clone(), + network_adapter.clone().into_sender(), + ctx.address().with_auto_span_context().into_sender(), + Some(account_id), + store, + config.chunk_request_retry_period, + ); + let shards_manager_adapter = Arc::new(shards_manager_addr.with_auto_span_context()); + + let state_sync_adapter = + Arc::new(RwLock::new(SyncAdapter::new(Sender::noop(), Sender::noop()))); + let client = Client::new( + config.clone(), + chain_genesis, + epoch_manager, + shard_tracker, + state_sync_adapter, + runtime, + network_adapter.clone(), + shards_manager_adapter.as_sender(), + Some(signer.clone()), + enable_doomslug, + TEST_SEED, + None, + ) + .unwrap(); + let client_actor = ClientActor::new( + client, + ctx.address(), + config, + PeerId::new(PublicKey::empty(KeyType::ED25519)), + network_adapter, + Some(signer), + telemetry, + ctx, + None, + adv, + None, + ) + .unwrap(); + (genesis_block, client_actor, view_client_addr, shards_manager_adapter.into()) +} + +pub fn setup_only_view( + vs: ValidatorSchedule, + epoch_length: BlockHeightDelta, + account_id: AccountId, + skip_sync_wait: bool, + min_block_prod_time: u64, + max_block_prod_time: u64, + enable_doomslug: bool, + archive: bool, + epoch_sync_enabled: bool, + state_sync_enabled: bool, + network_adapter: PeerManagerAdapter, + transaction_validity_period: NumBlocks, + genesis_time: DateTime, +) -> Addr { + let store = create_test_store(); + let num_validator_seats = vs.all_block_producers().count() as NumSeats; + let epoch_manager = MockEpochManager::new_with_validators(store.clone(), vs, epoch_length); + let shard_tracker = ShardTracker::new_empty(epoch_manager.clone()); + let runtime = KeyValueRuntime::new_with_no_gc(store, epoch_manager.as_ref(), archive); + let chain_genesis = ChainGenesis { + time: genesis_time, + height: 0, + gas_limit: 1_000_000, + min_gas_price: 100, + max_gas_price: 1_000_000_000, + total_supply: 3_000_000_000_000_000_000_000_000_000_000_000, + gas_price_adjustment_rate: Ratio::from_integer(0), + transaction_validity_period, + epoch_length, + protocol_version: PROTOCOL_VERSION, + }; + + let doomslug_threshold_mode = if enable_doomslug { + DoomslugThresholdMode::TwoThirds + } else { + DoomslugThresholdMode::NoApprovals + }; + Chain::new( + epoch_manager.clone(), + shard_tracker.clone(), + runtime.clone(), + &chain_genesis, + doomslug_threshold_mode, + ChainConfig { + save_trie_changes: true, + background_migration_threads: 1, + resharding_config: MutableConfigValue::new( + ReshardingConfig::default(), + "resharding_config", + ), + }, + None, + ) + .unwrap(); + + let signer = Arc::new(create_test_signer(account_id.as_str())); + TelemetryActor::default().start(); + let config = ClientConfig::test( + skip_sync_wait, + min_block_prod_time, + max_block_prod_time, + num_validator_seats, + archive, + true, + epoch_sync_enabled, + state_sync_enabled, + ); + + let adv = crate::adversarial::Controls::default(); + + start_view_client( + Some(signer.validator_id().clone()), + chain_genesis, + epoch_manager, + shard_tracker, + runtime, + network_adapter, + config, + adv, + ) +} + +/// Sets up ClientActor and ViewClientActor with mock PeerManager. +pub fn setup_mock( + validators: Vec, + account_id: AccountId, + skip_sync_wait: bool, + enable_doomslug: bool, + peer_manager_mock: Box< + dyn FnMut( + &PeerManagerMessageRequest, + &mut Context, + Addr, + ) -> PeerManagerMessageResponse, + >, +) -> ActorHandlesForTesting { + setup_mock_with_validity_period_and_no_epoch_sync( + validators, + account_id, + skip_sync_wait, + enable_doomslug, + peer_manager_mock, + 100, + ) +} + +pub fn setup_mock_with_validity_period_and_no_epoch_sync( + validators: Vec, + account_id: AccountId, + skip_sync_wait: bool, + enable_doomslug: bool, + mut peermanager_mock: Box< + dyn FnMut( + &PeerManagerMessageRequest, + &mut Context, + Addr, + ) -> PeerManagerMessageResponse, + >, + transaction_validity_period: NumBlocks, +) -> ActorHandlesForTesting { + let network_adapter = Arc::new(LateBoundSender::default()); + let mut vca: Option> = None; + let mut sma: Option = None; + let client_addr = ClientActor::create(|ctx: &mut Context| { + let vs = ValidatorSchedule::new().block_producers_per_epoch(vec![validators]); + let (_, client, view_client_addr, shards_manager_adapter) = setup( + vs, + 10, + account_id, + skip_sync_wait, + MIN_BLOCK_PROD_TIME.as_millis() as u64, + MAX_BLOCK_PROD_TIME.as_millis() as u64, + enable_doomslug, + false, + false, + true, + network_adapter.clone().into(), + transaction_validity_period, + StaticClock::utc(), + ctx, + ); + vca = Some(view_client_addr); + sma = Some(shards_manager_adapter); + client + }); + let client_addr1 = client_addr.clone(); + + let network_actor = + PeerManagerMock::new(move |msg, ctx| peermanager_mock(&msg, ctx, client_addr1.clone())) + .start(); + + network_adapter.bind(network_actor); + + ActorHandlesForTesting { + client_actor: client_addr, + view_client_actor: vca.unwrap(), + shards_manager_adapter: sma.unwrap(), + } +} + +#[derive(Clone)] +pub struct ActorHandlesForTesting { + pub client_actor: Addr, + pub view_client_actor: Addr, + pub shards_manager_adapter: ShardsManagerAdapterForTest, +} + +fn send_chunks( + connectors: &[ActorHandlesForTesting], + recipients: I, + target: T, + drop_chunks: bool, + send_to: F, +) where + T: Eq, + I: Iterator, + F: Fn(&ShardsManagerAdapterForTest), +{ + for (i, name) in recipients { + if name == target { + if !drop_chunks || !thread_rng().gen_ratio(1, 5) { + send_to(&connectors[i].shards_manager_adapter); + } + } + } +} + +/// Setup multiple clients talking to each other via a mock network. +/// +/// # Arguments +/// +/// `vs` - the set of validators and how they are assigned to shards in different epochs. +/// +/// `key_pairs` - keys for `validators` +/// +/// `skip_sync_wait` +/// +/// `block_prod_time` - Minimum block production time, assuming there is enough approvals. The +/// maximum block production time depends on the value of `tamper_with_fg`, and is +/// equal to `block_prod_time` if `tamper_with_fg` is `true`, otherwise it is +/// `block_prod_time * 2` +/// +/// `drop_chunks` - if set to true, 10% of all the chunk messages / requests will be dropped +/// +/// `tamper_with_fg` - if set to true, will split the heights into groups of 100. For some groups +/// all the approvals will be dropped (thus completely disabling the finality gadget +/// and introducing severe forkfulness if `block_prod_time` is sufficiently small), +/// for some groups will keep all the approvals (and test the fg invariants), and +/// for some will drop 50% of the approvals. +/// This was designed to tamper with the finality gadget when we +/// had it, unclear if has much effect today. Must be disabled if doomslug is +/// enabled (see below), because doomslug will stall if approvals are not delivered. +/// +/// `epoch_length` - approximate length of the epoch as measured +/// by the block heights difference of it's last and first block. +/// +/// `enable_doomslug` - If false, blocks will be created when at least one approval is present, without +/// waiting for 2/3. This allows for more forkfulness. `cross_shard_tx` has modes +/// both with enabled doomslug (to test "production" setting) and with disabled +/// doomslug (to test higher forkfullness) +/// +/// `network_mock` - the callback that is called for each message sent. The `mock` is called before +/// the default processing. `mock` returns `(response, perform_default)`. If +/// `perform_default` is false, then the message is not processed or broadcasted +/// further and `response` is returned to the requester immediately. Otherwise +/// the default action is performed, that might (and likely will) overwrite the +/// `response` before it is sent back to the requester. +pub fn setup_mock_all_validators( + vs: ValidatorSchedule, + key_pairs: Vec, + skip_sync_wait: bool, + block_prod_time: u64, + drop_chunks: bool, + tamper_with_fg: bool, + epoch_length: BlockHeightDelta, + enable_doomslug: bool, + archive: Vec, + epoch_sync_enabled: Vec, + check_block_stats: bool, + peer_manager_mock: Box< + dyn FnMut( + // Peer validators + &[ActorHandlesForTesting], + // Validator that sends the message + AccountId, + // The message itself + &PeerManagerMessageRequest, + ) -> (PeerManagerMessageResponse, /* perform default */ bool), + >, +) -> (Block, Vec, Arc>) { + let peer_manager_mock = Arc::new(RwLock::new(peer_manager_mock)); + let validators = vs.all_validators().cloned().collect::>(); + let key_pairs = key_pairs; + + let addresses: Vec<_> = (0..key_pairs.len()).map(|i| hash(vec![i as u8].as_ref())).collect(); + let genesis_time = StaticClock::utc(); + let mut ret = vec![]; + + let connectors: Arc>> = Default::default(); + + let announced_accounts = Arc::new(RwLock::new(HashSet::new())); + let genesis_block = Arc::new(RwLock::new(None)); + + let last_height = Arc::new(RwLock::new(vec![0; key_pairs.len()])); + let largest_endorsed_height = Arc::new(RwLock::new(vec![0u64; key_pairs.len()])); + let largest_skipped_height = Arc::new(RwLock::new(vec![0u64; key_pairs.len()])); + let hash_to_height = Arc::new(RwLock::new(HashMap::new())); + let block_stats = Arc::new(RwLock::new(BlockStats::new())); + + for (index, account_id) in validators.clone().into_iter().enumerate() { + let vs = vs.clone(); + let block_stats1 = block_stats.clone(); + let mut view_client_addr_slot = None; + let mut shards_manager_adapter_slot = None; + let validators_clone2 = validators.clone(); + let genesis_block1 = genesis_block.clone(); + let key_pairs = key_pairs.clone(); + let key_pairs1 = key_pairs.clone(); + let addresses = addresses.clone(); + let connectors1 = connectors.clone(); + let network_mock1 = peer_manager_mock.clone(); + let announced_accounts1 = announced_accounts.clone(); + let last_height1 = last_height.clone(); + let last_height2 = last_height.clone(); + let largest_endorsed_height1 = largest_endorsed_height.clone(); + let largest_skipped_height1 = largest_skipped_height.clone(); + let hash_to_height1 = hash_to_height.clone(); + let archive1 = archive.clone(); + let epoch_sync_enabled1 = epoch_sync_enabled.clone(); + let client_addr = ClientActor::create(|ctx| { + let client_addr = ctx.address(); + let _account_id = account_id.clone(); + let pm = PeerManagerMock::new(move |msg, _ctx| { + // Note: this `.wait` will block until all `ClientActors` are created. + let connectors1 = connectors1.wait(); + let mut guard = network_mock1.write().unwrap(); + let (resp, perform_default) = + guard.deref_mut()(connectors1.as_slice(), account_id.clone(), &msg); + drop(guard); + + if perform_default { + let my_ord = validators_clone2.iter().position(|it| it == &account_id).unwrap(); + let my_key_pair = key_pairs[my_ord].clone(); + let my_address = addresses[my_ord]; + + { + let last_height2 = last_height2.read().unwrap(); + let peers: Vec<_> = key_pairs1 + .iter() + .take(connectors1.len()) + .enumerate() + .map(|(i, peer_info)| ConnectedPeerInfo { + full_peer_info: FullPeerInfo { + peer_info: peer_info.clone(), + chain_info: PeerChainInfo { + genesis_id: GenesisId { + chain_id: "unittest".to_string(), + hash: Default::default(), + }, + // TODO: add the correct hash here + last_block: Some(BlockInfo { + height: last_height2[i], + hash: CryptoHash::default(), + }), + tracked_shards: vec![0, 1, 2, 3], + archival: true, + }, + }, + received_bytes_per_sec: 0, + sent_bytes_per_sec: 0, + last_time_peer_requested: unc_async::time::Instant::now(), + last_time_received_message: unc_async::time::Instant::now(), + connection_established_time: unc_async::time::Instant::now(), + peer_type: PeerType::Outbound, + nonce: 3, + }) + .collect(); + let peers2 = peers + .iter() + .filter_map(|it| it.full_peer_info.clone().into()) + .collect(); + let info = NetworkInfo { + connected_peers: peers, + tier1_connections: vec![], + num_connected_peers: key_pairs1.len(), + peer_max_count: key_pairs1.len() as u32, + highest_height_peers: peers2, + sent_bytes_per_sec: 0, + received_bytes_per_sec: 0, + known_producers: vec![], + tier1_accounts_keys: vec![], + tier1_accounts_data: vec![], + }; + client_addr.do_send(SetNetworkInfo(info).with_span_context()); + } + + match msg.as_network_requests_ref() { + NetworkRequests::Block { block } => { + if check_block_stats { + let block_stats2 = &mut *block_stats1.write().unwrap(); + block_stats2.add_block(block); + block_stats2.check_stats(false); + } + + for actor_handles in connectors1 { + actor_handles.client_actor.do_send( + BlockResponse { + block: block.clone(), + peer_id: PeerInfo::random().id, + was_requested: false, + } + .with_span_context(), + ); + } + + let mut last_height1 = last_height1.write().unwrap(); + + let my_height = &mut last_height1[my_ord]; + + *my_height = max(*my_height, block.header().height()); + + hash_to_height1 + .write() + .unwrap() + .insert(*block.header().hash(), block.header().height()); + } + NetworkRequests::PartialEncodedChunkRequest { target, request, .. } => { + send_chunks( + connectors1, + validators_clone2.iter().map(|s| Some(s.clone())).enumerate(), + target.account_id.as_ref().map(|s| s.clone()), + drop_chunks, + |c| { + c.send(ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkRequest { partial_encoded_chunk_request: request.clone(), route_back: my_address }); + }, + ); + } + NetworkRequests::PartialEncodedChunkResponse { route_back, response } => { + send_chunks( + connectors1, + addresses.iter().enumerate(), + route_back, + drop_chunks, + |c| { + c.send(ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkResponse { partial_encoded_chunk_response: response.clone(), received_time: Instant::now() }); + }, + ); + } + NetworkRequests::PartialEncodedChunkMessage { + account_id, + partial_encoded_chunk, + } => { + send_chunks( + connectors1, + validators_clone2.iter().cloned().enumerate(), + account_id.clone(), + drop_chunks, + |c| { + c.send(ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunk(partial_encoded_chunk.clone().into())); + }, + ); + } + NetworkRequests::PartialEncodedChunkForward { account_id, forward } => { + send_chunks( + connectors1, + validators_clone2.iter().cloned().enumerate(), + account_id.clone(), + drop_chunks, + |c| { + c.send(ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkForward(forward.clone())); + } + ); + } + NetworkRequests::BlockRequest { hash, peer_id } => { + for (i, peer_info) in key_pairs.iter().enumerate() { + let peer_id = peer_id.clone(); + if peer_info.id == peer_id { + let me = connectors1[my_ord].client_actor.clone(); + actix::spawn( + connectors1[i] + .view_client_actor + .send(BlockRequest(*hash).with_span_context()) + .then(move |response| { + let response = response.unwrap(); + match response { + Some(block) => { + me.do_send( + BlockResponse { + block: *block, + peer_id, + was_requested: true, + } + .with_span_context(), + ); + } + None => {} + } + future::ready(()) + }), + ); + } + } + } + NetworkRequests::BlockHeadersRequest { hashes, peer_id } => { + for (i, peer_info) in key_pairs.iter().enumerate() { + let peer_id = peer_id.clone(); + if peer_info.id == peer_id { + let me = connectors1[my_ord].client_actor.clone(); + actix::spawn( + connectors1[i] + .view_client_actor + .send( + BlockHeadersRequest(hashes.clone()) + .with_span_context(), + ) + .then(move |response| { + let response = response.unwrap(); + match response { + Some(headers) => { + me.do_send( + BlockHeadersResponse(headers, peer_id) + .with_span_context(), + ); + } + None => {} + } + future::ready(()) + }), + ); + } + } + } + NetworkRequests::StateRequestHeader { + shard_id, + sync_hash, .. + } => { + for (i, _) in validators_clone2.iter().enumerate() { + let me = connectors1[my_ord].client_actor.clone(); + actix::spawn( + connectors1[i] + .view_client_actor + .send( + StateRequestHeader { + shard_id: *shard_id, + sync_hash: *sync_hash, + } + .with_span_context(), + ) + .then(move |response| { + let response = response.unwrap(); + match response { + Some(response) => { + me.do_send(response.with_span_context()); + } + None => {} + } + future::ready(()) + }), + ); + } + } + NetworkRequests::StateRequestPart { + shard_id, + sync_hash, + part_id, .. + } => { + for (i, _) in validators_clone2.iter().enumerate() { + let me = connectors1[my_ord].client_actor.clone(); + actix::spawn( + connectors1[i] + .view_client_actor + .send( + StateRequestPart { + shard_id: *shard_id, + sync_hash: *sync_hash, + part_id: *part_id, + } + .with_span_context(), + ) + .then(move |response| { + let response = response.unwrap(); + match response { + Some(response) => { + me.do_send(response.with_span_context()); + } + None => {} + } + future::ready(()) + }), + ); + } + } + NetworkRequests::AnnounceAccount(announce_account) => { + let mut aa = announced_accounts1.write().unwrap(); + let key = ( + announce_account.account_id.clone(), + announce_account.epoch_id.clone(), + ); + if aa.get(&key).is_none() { + aa.insert(key); + for actor_handles in connectors1 { + actor_handles.view_client_actor.do_send( + AnnounceAccountRequest(vec![( + announce_account.clone(), + None, + )]) + .with_span_context(), + ) + } + } + } + NetworkRequests::Approval { approval_message } => { + let height_mod = approval_message.approval.target_height % 300; + + let do_propagate = if tamper_with_fg { + if height_mod < 100 { + false + } else if height_mod < 200 { + let mut rng = rand::thread_rng(); + rng.gen() + } else { + true + } + } else { + true + }; + + let approval = approval_message.approval.clone(); + + if do_propagate { + for (i, name) in validators_clone2.iter().enumerate() { + if name == &approval_message.target { + connectors1[i].client_actor.do_send( + BlockApproval(approval.clone(), my_key_pair.id.clone()) + .with_span_context(), + ); + } + } + } + + // Verify doomslug invariant + match approval.inner { + ApprovalInner::Endorsement(parent_hash) => { + assert!( + approval.target_height + > largest_skipped_height1.read().unwrap()[my_ord] + ); + largest_endorsed_height1.write().unwrap()[my_ord] = + approval.target_height; + + if let Some(prev_height) = + hash_to_height1.read().unwrap().get(&parent_hash) + { + assert_eq!(prev_height + 1, approval.target_height); + } + } + ApprovalInner::Skip(prev_height) => { + largest_skipped_height1.write().unwrap()[my_ord] = + approval.target_height; + let e = largest_endorsed_height1.read().unwrap()[my_ord]; + // `e` is the *target* height of the last endorsement. `prev_height` + // is allowed to be anything >= to the source height, which is e-1. + assert!( + prev_height + 1 >= e, + "New: {}->{}, Old: {}->{}", + prev_height, + approval.target_height, + e - 1, + e + ); + } + }; + } + NetworkRequests::ForwardTx(_, _) + | NetworkRequests::BanPeer { .. } + | NetworkRequests::TxStatus(_, _, _) + | NetworkRequests::SnapshotHostInfo { .. } + | NetworkRequests::Challenge(_) => {} + NetworkRequests::ChunkStateWitness(_, _) => { + // TODO(#10265): Implement for integration tests. + }, + NetworkRequests::ChunkEndorsement(_, _) => { + // TODO(#10265): Implement for integration tests. + }, + }; + } + resp + }) + .start(); + let (block, client, view_client_addr, shards_manager_adapter) = setup( + vs, + epoch_length, + _account_id, + skip_sync_wait, + block_prod_time, + block_prod_time * 3, + enable_doomslug, + archive1[index], + epoch_sync_enabled1[index], + false, + Arc::new(pm).into(), + 10000, + genesis_time, + ctx, + ); + view_client_addr_slot = Some(view_client_addr); + shards_manager_adapter_slot = Some(shards_manager_adapter); + *genesis_block1.write().unwrap() = Some(block); + client + }); + ret.push(ActorHandlesForTesting { + client_actor: client_addr, + view_client_actor: view_client_addr_slot.unwrap(), + shards_manager_adapter: shards_manager_adapter_slot.unwrap(), + }); + } + hash_to_height.write().unwrap().insert(CryptoHash::default(), 0); + hash_to_height + .write() + .unwrap() + .insert(*genesis_block.read().unwrap().as_ref().unwrap().header().clone().hash(), 0); + connectors.set(ret.clone()).ok().unwrap(); + let value = genesis_block.read().unwrap(); + (value.clone().unwrap(), ret, block_stats) +} + +/// Sets up ClientActor and ViewClientActor without network. +pub fn setup_no_network( + validators: Vec, + account_id: AccountId, + skip_sync_wait: bool, + enable_doomslug: bool, +) -> ActorHandlesForTesting { + setup_no_network_with_validity_period_and_no_epoch_sync( + validators, + account_id, + skip_sync_wait, + 100, + enable_doomslug, + ) +} + +pub fn setup_no_network_with_validity_period_and_no_epoch_sync( + validators: Vec, + account_id: AccountId, + skip_sync_wait: bool, + transaction_validity_period: NumBlocks, + enable_doomslug: bool, +) -> ActorHandlesForTesting { + setup_mock_with_validity_period_and_no_epoch_sync( + validators, + account_id, + skip_sync_wait, + enable_doomslug, + Box::new(|_, _, _| { + PeerManagerMessageResponse::NetworkResponses(NetworkResponses::NoResponse) + }), + transaction_validity_period, + ) +} + +pub fn setup_client_with_runtime( + num_validator_seats: NumSeats, + account_id: Option, + enable_doomslug: bool, + network_adapter: PeerManagerAdapter, + shards_manager_adapter: ShardsManagerAdapterForTest, + chain_genesis: ChainGenesis, + epoch_manager: Arc, + shard_tracker: ShardTracker, + runtime: Arc, + rng_seed: RngSeed, + archive: bool, + save_trie_changes: bool, + snapshot_callbacks: Option, +) -> Client { + let validator_signer = + account_id.map(|x| Arc::new(create_test_signer(x.as_str())) as Arc); + let mut config = ClientConfig::test( + true, + 10, + 20, + num_validator_seats, + archive, + save_trie_changes, + true, + true, + ); + config.epoch_length = chain_genesis.epoch_length; + let state_sync_adapter = + Arc::new(RwLock::new(SyncAdapter::new(Sender::noop(), Sender::noop()))); + let mut client = Client::new( + config, + chain_genesis, + epoch_manager, + shard_tracker, + state_sync_adapter, + runtime, + network_adapter, + shards_manager_adapter.client.into(), + validator_signer, + enable_doomslug, + rng_seed, + snapshot_callbacks, + ) + .unwrap(); + client.sync_status = SyncStatus::NoSync; + client +} + +pub fn setup_client( + store: Store, + vs: ValidatorSchedule, + account_id: Option, + enable_doomslug: bool, + network_adapter: PeerManagerAdapter, + shards_manager_adapter: ShardsManagerAdapterForTest, + chain_genesis: ChainGenesis, + rng_seed: RngSeed, + archive: bool, + save_trie_changes: bool, +) -> Client { + let num_validator_seats = vs.all_block_producers().count() as NumSeats; + let epoch_manager = + MockEpochManager::new_with_validators(store.clone(), vs, chain_genesis.epoch_length); + let shard_tracker = ShardTracker::new_empty(epoch_manager.clone()); + let runtime = KeyValueRuntime::new(store, epoch_manager.as_ref()); + setup_client_with_runtime( + num_validator_seats, + account_id, + enable_doomslug, + network_adapter, + shards_manager_adapter, + chain_genesis, + epoch_manager, + shard_tracker, + runtime, + rng_seed, + archive, + save_trie_changes, + None, + ) +} + +pub fn setup_synchronous_shards_manager( + account_id: Option, + client_adapter: Sender, + network_adapter: PeerManagerAdapter, + epoch_manager: Arc, + shard_tracker: ShardTracker, + runtime: Arc, + chain_genesis: &ChainGenesis, +) -> ShardsManagerAdapterForTest { + // Initialize the chain, to make sure that if the store is empty, we write the genesis + // into the store, and as a short cut to get the parameters needed to instantiate + // ShardsManager. This way we don't have to wait to construct the Client first. + // TODO(#8324): This should just be refactored so that we can construct Chain first + // before anything else. + let chain = Chain::new( + epoch_manager.clone(), + shard_tracker.clone(), + runtime, + chain_genesis, + DoomslugThresholdMode::TwoThirds, // irrelevant + ChainConfig { + save_trie_changes: true, + background_migration_threads: 1, + resharding_config: MutableConfigValue::new( + ReshardingConfig::default(), + "resharding_config", + ), + }, // irrelevant + None, + ) + .unwrap(); + let chain_head = chain.head().unwrap(); + let chain_header_head = chain.header_head().unwrap(); + let shards_manager = ShardsManager::new( + time::Clock::real(), + account_id, + epoch_manager, + shard_tracker, + network_adapter.request_sender, + client_adapter, + chain.chain_store().new_read_only_chunks_store(), + chain_head, + chain_header_head, + ); + Arc::new(SynchronousShardsManagerAdapter::new(shards_manager)).into() +} + +pub fn setup_client_with_synchronous_shards_manager( + store: Store, + vs: ValidatorSchedule, + account_id: Option, + enable_doomslug: bool, + network_adapter: PeerManagerAdapter, + client_adapter: Sender, + chain_genesis: ChainGenesis, + rng_seed: RngSeed, + archive: bool, + save_trie_changes: bool, +) -> Client { + if let None = System::try_current() { + let _ = System::new(); + } + let num_validator_seats = vs.all_block_producers().count() as NumSeats; + let epoch_manager = + MockEpochManager::new_with_validators(store.clone(), vs, chain_genesis.epoch_length); + let shard_tracker = ShardTracker::new_empty(epoch_manager.clone()); + let runtime = KeyValueRuntime::new(store, epoch_manager.as_ref()); + let shards_manager_adapter = setup_synchronous_shards_manager( + account_id.clone(), + client_adapter, + network_adapter.clone(), + epoch_manager.clone(), + shard_tracker.clone(), + runtime.clone(), + &chain_genesis, + ); + setup_client_with_runtime( + num_validator_seats, + account_id, + enable_doomslug, + network_adapter, + shards_manager_adapter, + chain_genesis, + epoch_manager, + shard_tracker, + runtime, + rng_seed, + archive, + save_trie_changes, + None, + ) +} + +/// A combined trait bound for both the client side and network side of the ShardsManager API. +#[derive(Clone, derive_more::AsRef)] +pub struct ShardsManagerAdapterForTest { + pub client: Sender, + pub network: Sender, +} + +impl + CanSend> + From> for ShardsManagerAdapterForTest +{ + fn from(arc: Arc) -> Self { + Self { client: arc.as_sender(), network: arc.as_sender() } + } +} diff --git a/chain/client/src/test_utils/test_env.rs b/chain/client/src/test_utils/test_env.rs new file mode 100644 index 000000000..6557a3637 --- /dev/null +++ b/chain/client/src/test_utils/test_env.rs @@ -0,0 +1,583 @@ +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; +use std::time::Instant; + +use crate::adapter::ProcessTxResponse; +use crate::Client; +use unc_async::messaging::CanSend; +use unc_chain::test_utils::ValidatorSchedule; +use unc_chain::{ChainGenesis, Provenance}; +use unc_chunks::client::ShardsManagerResponse; +use unc_chunks::test_utils::MockClientAdapterForShardsManager; +use unc_crypto::{InMemorySigner, KeyType, Signer}; +use unc_network::shards_manager::ShardsManagerRequestFromNetwork; +use unc_network::test_utils::MockPeerManagerAdapter; +use unc_network::types::NetworkRequests; +use unc_network::types::PeerManagerMessageRequest; +use unc_network::types::{PartialEncodedChunkRequestMsg, PartialEncodedChunkResponseMsg}; +use unc_o11y::testonly::TracingCapture; +use unc_parameters::RuntimeConfig; +use unc_primitives::action::delegate::{DelegateAction, NonDelegateAction, SignedDelegateAction}; +use unc_primitives::block::Block; +use unc_primitives::chunk_validation::ChunkEndorsement; +use unc_primitives::epoch_manager::RngSeed; +use unc_primitives::errors::InvalidTxError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::sharding::PartialEncodedChunk; +use unc_primitives::test_utils::create_test_signer; +use unc_primitives::transaction::{Action, FunctionCallAction, SignedTransaction}; +use unc_primitives::types::{AccountId, Balance, BlockHeight, EpochId, NumSeats}; +use unc_primitives::utils::MaybeValidated; +use unc_primitives::version::ProtocolVersion; +use unc_primitives::views::{ + AccountView, FinalExecutionOutcomeView, QueryRequest, QueryResponseKind, StateItem, +}; +use once_cell::sync::OnceCell; + +use super::setup::{setup_client_with_runtime, ShardsManagerAdapterForTest}; +use super::test_env_builder::TestEnvBuilder; +use super::TEST_SEED; + +/// An environment for writing integration tests with multiple clients. +/// This environment can simulate near nodes without network and it can be configured to use different runtimes. +pub struct TestEnv { + pub chain_genesis: ChainGenesis, + pub validators: Vec, + pub network_adapters: Vec>, + pub client_adapters: Vec>, + pub shards_manager_adapters: Vec, + pub clients: Vec, + pub(crate) account_indices: AccountIndices, + pub(crate) paused_blocks: Arc>>>>, + // random seed to be inject in each client according to AccountId + // if not set, a default constant TEST_SEED will be injected + pub(crate) seeds: HashMap, + pub(crate) archive: bool, + pub(crate) save_trie_changes: bool, +} + +impl TestEnv { + pub fn builder(chain_genesis: ChainGenesis) -> TestEnvBuilder { + TestEnvBuilder::new(chain_genesis) + } + + /// Process a given block in the client with index `id`. + /// Simulate the block processing logic in `Client`, i.e, it would run catchup and then process accepted blocks and possibly produce chunks. + pub fn process_block(&mut self, id: usize, block: Block, provenance: Provenance) { + self.clients[id].process_block_test(MaybeValidated::from(block), provenance).unwrap(); + } + + /// Produces block by given client, which may kick off chunk production. + /// This means that transactions added before this call will be included in the next block produced by this validator. + pub fn produce_block(&mut self, id: usize, height: BlockHeight) { + let block = self.clients[id].produce_block(height).unwrap(); + self.process_block(id, block.unwrap(), Provenance::PRODUCED); + } + + /// Pause processing of the given block, which means that the background + /// thread which applies the chunks on the block will get blocked until + /// `resume_block_processing` is called. + /// + /// Note that you must call `resume_block_processing` at some later point to + /// unstuck the block. + /// + /// Implementation is rather crude and just hijacks our logging + /// infrastructure. Hopefully this is good enough, but, if it isn't, we can + /// add something more robust. + pub fn pause_block_processing(&mut self, capture: &mut TracingCapture, block: &CryptoHash) { + let paused_blocks = Arc::clone(&self.paused_blocks); + paused_blocks.lock().unwrap().insert(*block, Arc::new(OnceCell::new())); + capture.set_callback(move |msg| { + if msg.starts_with("do_apply_chunks") { + let cell = paused_blocks.lock().unwrap().iter().find_map(|(block_hash, cell)| { + if msg.contains(&format!("block_hash={block_hash}")) { + Some(Arc::clone(cell)) + } else { + None + } + }); + if let Some(cell) = cell { + cell.wait(); + } + } + }); + } + + /// See `pause_block_processing`. + pub fn resume_block_processing(&mut self, block: &CryptoHash) { + let mut paused_blocks = self.paused_blocks.lock().unwrap(); + let cell = paused_blocks.remove(block).unwrap(); + let _ = cell.set(()); + } + + pub fn client(&mut self, account_id: &AccountId) -> &mut Client { + self.account_indices.lookup_mut(&mut self.clients, account_id) + } + + pub fn shards_manager(&self, account: &AccountId) -> &ShardsManagerAdapterForTest { + self.account_indices.lookup(&self.shards_manager_adapters, account) + } + + pub fn process_partial_encoded_chunks(&mut self) { + let network_adapters = self.network_adapters.clone(); + + let mut keep_going = true; + while keep_going { + keep_going = false; + // for network_adapter in network_adapters.iter() { + for i in 0..network_adapters.len() { + let network_adapter = network_adapters.get(i).unwrap(); + let _span = + tracing::debug_span!(target: "test", "process_partial_encoded_chunks", client=i).entered(); + + keep_going |= network_adapter.handle_filtered(|request| match request { + PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::PartialEncodedChunkMessage { + account_id, + partial_encoded_chunk, + }, + ) => { + let partial_encoded_chunk = + PartialEncodedChunk::from(partial_encoded_chunk); + let message = ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunk( + partial_encoded_chunk, + ); + self.shards_manager(&account_id).send(message); + None + } + PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::PartialEncodedChunkForward { account_id, forward }, + ) => { + let message = + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkForward( + forward, + ); + self.shards_manager(&account_id).send(message); + None + } + _ => Some(request), + }); + } + } + } + + /// Process all PartialEncodedChunkRequests in the network queue for a client + /// `id`: id for the client + pub fn process_partial_encoded_chunks_requests(&mut self, id: usize) { + while let Some(request) = self.network_adapters[id].pop() { + self.process_partial_encoded_chunk_request(id, request); + } + } + + /// Send the PartialEncodedChunkRequest to the target client, get response and process the response + pub fn process_partial_encoded_chunk_request( + &mut self, + id: usize, + request: PeerManagerMessageRequest, + ) { + if let PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::PartialEncodedChunkRequest { target, request, .. }, + ) = request + { + let target_id = self.account_indices.index(&target.account_id.unwrap()); + let response = self.get_partial_encoded_chunk_response(target_id, request); + if let Some(response) = response { + self.shards_manager_adapters[id].send( + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkResponse { + partial_encoded_chunk_response: response, + received_time: Instant::now(), + }, + ); + } + } else { + panic!("The request is not a PartialEncodedChunk request {:?}", request); + } + } + + pub fn get_partial_encoded_chunk_response( + &mut self, + id: usize, + request: PartialEncodedChunkRequestMsg, + ) -> Option { + self.shards_manager_adapters[id].send( + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkRequest { + partial_encoded_chunk_request: request.clone(), + route_back: CryptoHash::default(), + }, + ); + let response = self.network_adapters[id].pop_most_recent(); + match response { + Some(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::PartialEncodedChunkResponse { route_back: _, response }, + )) => return Some(response), + Some(response) => { + self.network_adapters[id].put_back_most_recent(response); + } + None => {} + } + + panic!( + "Failed to process PartialEncodedChunkRequest from shards manager {}: {:?}", + id, request + ); + } + + pub fn process_shards_manager_responses(&mut self, id: usize) -> bool { + let mut any_processed = false; + while let Some(msg) = self.client_adapters[id].pop() { + match msg { + ShardsManagerResponse::ChunkCompleted { partial_chunk, shard_chunk } => { + self.clients[id].on_chunk_completed( + partial_chunk, + shard_chunk, + Arc::new(|_| {}), + ); + } + ShardsManagerResponse::InvalidChunk(encoded_chunk) => { + self.clients[id].on_invalid_chunk(encoded_chunk); + } + ShardsManagerResponse::ChunkHeaderReadyForInclusion { + chunk_header, + chunk_producer, + } => { + self.clients[id] + .on_chunk_header_ready_for_inclusion(chunk_header, chunk_producer); + } + } + any_processed = true; + } + any_processed + } + + pub fn process_shards_manager_responses_and_finish_processing_blocks(&mut self, idx: usize) { + let _span = + tracing::debug_span!(target: "test", "process_shards_manager", client=idx).entered(); + + loop { + self.process_shards_manager_responses(idx); + if self.clients[idx].finish_blocks_in_processing().is_empty() { + return; + } + } + } + + pub fn propagate_chunk_state_witnesses(&mut self) { + for idx in 0..self.clients.len() { + let _span = + tracing::debug_span!(target: "test", "propagate_chunk_state_witnesses", client=idx) + .entered(); + + self.network_adapters[idx].handle_filtered(|msg| { + if let PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::ChunkStateWitness(accounts, chunk_state_witness), + ) = msg + { + for account in accounts { + self.account_indices + .lookup_mut(&mut self.clients, &account) + .process_chunk_state_witness(chunk_state_witness.clone()) + .unwrap(); + } + None + } else { + Some(msg) + } + }); + } + } + + pub fn get_all_chunk_endorsements(&mut self) -> Vec { + let mut approvals = Vec::new(); + for idx in 0..self.clients.len() { + let _span = + tracing::debug_span!(target: "test", "get_all_chunk_endorsements", client=idx) + .entered(); + + self.network_adapters[idx].handle_filtered(|msg| { + if let PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::ChunkEndorsement(_, endorsement), + ) = msg + { + approvals.push(endorsement); + None + } else { + Some(msg) + } + }); + } + approvals + } + + pub fn send_money(&mut self, id: usize) -> ProcessTxResponse { + let account_id = self.get_client_id(0); + let signer = + InMemorySigner::from_seed(account_id.clone(), KeyType::ED25519, account_id.as_ref()); + let tx = SignedTransaction::send_money( + 1, + account_id.clone(), + account_id.clone(), + &signer, + 100, + self.clients[id].chain.head().unwrap().last_block_hash, + ); + self.clients[id].process_tx(tx, false, false) + } + + /// This function will actually bump to the latest protocol version instead of the provided one. + /// See https://github.com/utnet-org/utility/issues/8590 for details. + pub fn upgrade_protocol(&mut self, protocol_version: ProtocolVersion) { + assert_eq!(self.clients.len(), 1, "at the moment, this support only a single client"); + + let tip = self.clients[0].chain.head().unwrap(); + let epoch_id = self.clients[0] + .epoch_manager + .get_epoch_id_from_prev_block(&tip.last_block_hash) + .unwrap(); + let block_producer = + self.clients[0].epoch_manager.get_block_producer(&epoch_id, tip.height).unwrap(); + + let mut block = self.clients[0].produce_block(tip.height + 1).unwrap().unwrap(); + eprintln!("Producing block with version {protocol_version}"); + block.mut_header().set_latest_protocol_version(protocol_version); + block.mut_header().resign(&create_test_signer(block_producer.as_str())); + + let _ = self.clients[0] + .process_block_test_no_produce_chunk(block.into(), Provenance::NONE) + .unwrap(); + + for i in 0..self.clients[0].chain.epoch_length * 2 { + self.produce_block(0, tip.height + i + 2); + } + } + + pub fn query_account(&mut self, account_id: AccountId) -> AccountView { + let client = &self.clients[0]; + let head = client.chain.head().unwrap(); + let last_block = client.chain.get_block(&head.last_block_hash).unwrap(); + let shard_id = + client.epoch_manager.account_id_to_shard_id(&account_id, &head.epoch_id).unwrap(); + let shard_uid = client.epoch_manager.shard_id_to_uid(shard_id, &head.epoch_id).unwrap(); + let last_chunk_header = &last_block.chunks()[shard_id as usize]; + let response = client + .runtime_adapter + .query( + shard_uid, + &last_chunk_header.prev_state_root(), + last_block.header().height(), + last_block.header().raw_timestamp(), + last_block.header().prev_hash(), + last_block.header().hash(), + last_block.header().epoch_id(), + &QueryRequest::ViewAccount { account_id }, + ) + .unwrap(); + match response.kind { + QueryResponseKind::ViewAccount(account_view) => account_view, + _ => panic!("Wrong return value"), + } + } + + pub fn query_state(&mut self, account_id: AccountId) -> Vec { + let client = &self.clients[0]; + let head = client.chain.head().unwrap(); + let last_block = client.chain.get_block(&head.last_block_hash).unwrap(); + let shard_id = + client.epoch_manager.account_id_to_shard_id(&account_id, &head.epoch_id).unwrap(); + let shard_uid = client.epoch_manager.shard_id_to_uid(shard_id, &head.epoch_id).unwrap(); + let last_chunk_header = &last_block.chunks()[shard_id as usize]; + let response = client + .runtime_adapter + .query( + shard_uid, + &last_chunk_header.prev_state_root(), + last_block.header().height(), + last_block.header().raw_timestamp(), + last_block.header().prev_hash(), + last_block.header().hash(), + last_block.header().epoch_id(), + &QueryRequest::ViewState { + account_id, + prefix: vec![].into(), + include_proof: false, + }, + ) + .unwrap(); + match response.kind { + QueryResponseKind::ViewState(view_state_result) => view_state_result.values, + _ => panic!("Wrong return value"), + } + } + + pub fn query_balance(&mut self, account_id: AccountId) -> Balance { + self.query_account(account_id).amount + } + + /// Restarts client at given index. Note that the new client reuses runtime + /// adapter of old client. + /// TODO (#8269): create new `KeyValueRuntime` for new client. Currently it + /// doesn't work because `KeyValueRuntime` misses info about new epochs in + /// memory caches. + /// Though, it seems that it is not necessary for current use cases. + pub fn restart(&mut self, idx: usize) { + let account_id = self.get_client_id(idx).clone(); + let rng_seed = match self.seeds.get(&account_id) { + Some(seed) => *seed, + None => TEST_SEED, + }; + let vs = ValidatorSchedule::new().block_producers_per_epoch(vec![self.validators.clone()]); + let num_validator_seats = vs.all_block_producers().count() as NumSeats; + self.clients[idx] = setup_client_with_runtime( + num_validator_seats, + Some(self.get_client_id(idx).clone()), + false, + self.network_adapters[idx].clone().into(), + self.shards_manager_adapters[idx].clone(), + self.chain_genesis.clone(), + self.clients[idx].epoch_manager.clone(), + self.clients[idx].shard_tracker.clone(), + self.clients[idx].runtime_adapter.clone(), + rng_seed, + self.archive, + self.save_trie_changes, + None, + ) + } + + /// Returns an [`AccountId`] used by a client at given index. More + /// specifically, returns validator id of the client’s validator signer. + pub fn get_client_id(&self, idx: usize) -> &AccountId { + self.clients[idx].validator_signer.as_ref().unwrap().validator_id() + } + + pub fn get_runtime_config(&self, idx: usize, epoch_id: EpochId) -> RuntimeConfig { + self.clients[idx].runtime_adapter.get_protocol_config(&epoch_id).unwrap().runtime_config + } + + /// Create and sign transaction ready for execution. + pub fn tx_from_actions( + &mut self, + actions: Vec, + signer: &InMemorySigner, + receiver: AccountId, + ) -> SignedTransaction { + let tip = self.clients[0].chain.head().unwrap(); + SignedTransaction::from_actions( + tip.height + 1, + signer.account_id.clone(), + receiver, + signer, + actions, + tip.last_block_hash, + ) + } + + /// Wrap actions in a delegate action, put it in a transaction, sign. + pub fn meta_tx_from_actions( + &mut self, + actions: Vec, + sender: AccountId, + relayer: AccountId, + receiver_id: AccountId, + ) -> SignedTransaction { + let inner_signer = + InMemorySigner::from_seed(sender.clone(), KeyType::ED25519, sender.as_str()); + let relayer_signer = + InMemorySigner::from_seed(relayer.clone(), KeyType::ED25519, relayer.as_str()); + let tip = self.clients[0].chain.head().unwrap(); + let user_nonce = tip.height + 1; + let relayer_nonce = tip.height + 1; + let delegate_action = DelegateAction { + sender_id: inner_signer.account_id.clone(), + receiver_id, + actions: actions + .into_iter() + .map(|action| NonDelegateAction::try_from(action).unwrap()) + .collect(), + nonce: user_nonce, + max_block_height: tip.height + 100, + public_key: inner_signer.public_key(), + }; + let signature = inner_signer.sign(delegate_action.get_nep461_hash().as_bytes()); + let signed_delegate_action = SignedDelegateAction { delegate_action, signature }; + SignedTransaction::from_actions( + relayer_nonce, + relayer, + sender, + &relayer_signer, + vec![Action::Delegate(Box::new(signed_delegate_action))], + tip.last_block_hash, + ) + } + + /// Process a tx and its receipts, then return the execution outcome. + pub fn execute_tx( + &mut self, + tx: SignedTransaction, + ) -> Result { + let tx_hash = tx.get_hash(); + let response = self.clients[0].process_tx(tx, false, false); + // Check if the transaction got rejected + match response { + ProcessTxResponse::NoResponse + | ProcessTxResponse::RequestRouted + | ProcessTxResponse::ValidTx => (), + ProcessTxResponse::InvalidTx(e) => return Err(e), + ProcessTxResponse::DoesNotTrackShard => panic!("test setup is buggy"), + } + let max_iters = 100; + let tip = self.clients[0].chain.head().unwrap(); + for i in 0..max_iters { + let block = self.clients[0].produce_block(tip.height + i + 1).unwrap().unwrap(); + self.process_block(0, block.clone(), Provenance::PRODUCED); + if let Ok(outcome) = self.clients[0].chain.get_final_transaction_result(&tx_hash) { + return Ok(outcome); + } + } + panic!("No transaction outcome found after {max_iters} blocks.") + } + + /// Execute a function call transaction that calls main on the `TestEnv`. + /// + /// This function assumes that account has been deployed and that + /// `InMemorySigner::from_seed` produces a valid signer that has it's key + /// deployed already. + pub fn call_main(&mut self, account: &AccountId) -> FinalExecutionOutcomeView { + let signer = InMemorySigner::from_seed(account.clone(), KeyType::ED25519, account.as_str()); + let actions = vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "main".to_string(), + args: vec![], + gas: 3 * 10u64.pow(14), + deposit: 0, + }))]; + let tx = self.tx_from_actions(actions, &signer, signer.account_id.clone()); + self.execute_tx(tx).unwrap() + } +} + +impl Drop for TestEnv { + fn drop(&mut self) { + let paused_blocks = self.paused_blocks.lock().unwrap(); + for cell in paused_blocks.values() { + let _ = cell.set(()); + } + if !paused_blocks.is_empty() && !std::thread::panicking() { + panic!("some blocks are still paused, did you call `resume_block_processing`?") + } + } +} + +pub(crate) struct AccountIndices(pub(crate) HashMap); + +impl AccountIndices { + pub fn index(&self, account_id: &AccountId) -> usize { + self.0[account_id] + } + + pub fn lookup<'a, T>(&self, container: &'a [T], account_id: &AccountId) -> &'a T { + &container[self.0[account_id]] + } + + pub fn lookup_mut<'a, T>(&self, container: &'a mut [T], account_id: &AccountId) -> &'a mut T { + &mut container[self.0[account_id]] + } +} diff --git a/chain/client/src/test_utils/test_env_builder.rs b/chain/client/src/test_utils/test_env_builder.rs new file mode 100644 index 000000000..3035559f3 --- /dev/null +++ b/chain/client/src/test_utils/test_env_builder.rs @@ -0,0 +1,576 @@ +use super::setup::{setup_client_with_runtime, setup_synchronous_shards_manager}; +use super::test_env::TestEnv; +use super::{AccountIndices, TEST_SEED}; +use actix_rt::System; +use itertools::{multizip, Itertools}; +use unc_async::messaging::IntoSender; +use unc_chain::state_snapshot_actor::SnapshotCallbacks; +use unc_chain::test_utils::{KeyValueRuntime, MockEpochManager, ValidatorSchedule}; +use unc_chain::types::RuntimeAdapter; +use unc_chain::ChainGenesis; +use unc_chain_configs::GenesisConfig; +use unc_chunks::test_utils::MockClientAdapterForShardsManager; +use unc_epoch_manager::shard_tracker::{ShardTracker, TrackedConfig}; +use unc_epoch_manager::{EpochManager, EpochManagerAdapter, EpochManagerHandle}; +use unc_network::test_utils::MockPeerManagerAdapter; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::epoch_manager::{AllEpochConfigTestOverrides, RngSeed}; +use unc_primitives::types::{AccountId, NumShards}; +use unc_store::config::StateSnapshotType; +use unc_store::test_utils::create_test_store; +use unc_store::{NodeStorage, ShardUId, Store, StoreConfig, TrieConfig}; +use std::collections::HashMap; +use std::path::PathBuf; +use std::sync::Arc; + +#[derive(derive_more::From, Clone)] +enum EpochManagerKind { + Mock(Arc), + Handle(Arc), +} + +impl EpochManagerKind { + pub fn into_adapter(self) -> Arc { + match self { + Self::Mock(mock) => mock, + Self::Handle(handle) => handle, + } + } +} + +/// A builder for the TestEnv structure. +pub struct TestEnvBuilder { + chain_genesis: ChainGenesis, + clients: Vec, + validators: Vec, + home_dirs: Option>, + stores: Option>, + epoch_managers: Option>, + shard_trackers: Option>, + runtimes: Option>>, + network_adapters: Option>>, + num_shards: Option, + // random seed to be inject in each client according to AccountId + // if not set, a default constant TEST_SEED will be injected + seeds: HashMap, + archive: bool, + save_trie_changes: bool, + state_snapshot_enabled: bool, +} + +/// Builder for the [`TestEnv`] structure. +impl TestEnvBuilder { + /// Constructs a new builder. + pub(crate) fn new(chain_genesis: ChainGenesis) -> Self { + if let None = System::try_current() { + let _ = System::new(); + } + let clients = Self::make_accounts(1); + let validators = clients.clone(); + let seeds: HashMap = HashMap::with_capacity(1); + Self { + chain_genesis, + clients, + validators, + home_dirs: None, + stores: None, + epoch_managers: None, + shard_trackers: None, + runtimes: None, + network_adapters: None, + num_shards: None, + seeds, + archive: false, + save_trie_changes: true, + state_snapshot_enabled: false, + } + } + + /// Sets list of client [`AccountId`]s to the one provided. Panics if the + /// vector is empty. + pub fn clients(mut self, clients: Vec) -> Self { + assert!(!clients.is_empty()); + assert!(self.stores.is_none(), "Cannot set clients after stores"); + assert!(self.epoch_managers.is_none(), "Cannot set clients after epoch_managers"); + assert!(self.shard_trackers.is_none(), "Cannot set clients after shard_trackers"); + assert!(self.runtimes.is_none(), "Cannot set clients after runtimes"); + assert!(self.network_adapters.is_none(), "Cannot set clients after network_adapters"); + self.clients = clients; + self + } + + /// Sets random seed for each client according to the provided HashMap. + pub fn clients_random_seeds(mut self, seeds: HashMap) -> Self { + self.seeds = seeds; + self + } + + /// Sets number of clients to given one. To get [`AccountId`] used by the + /// validator associated with the client the [`TestEnv::get_client_id`] + /// method can be used. Tests should not rely on any particular format of + /// account identifiers used by the builder. Panics if `num` is zero. + pub fn clients_count(self, num: usize) -> Self { + self.clients(Self::make_accounts(num)) + } + + pub fn num_clients(&self) -> usize { + self.clients.len() + } + + /// Sets list of validator [`AccountId`]s to the one provided. Panics if + /// the vector is empty. + pub fn validators(mut self, validators: Vec) -> Self { + assert!(!validators.is_empty()); + assert!(self.epoch_managers.is_none(), "Cannot set validators after epoch_managers"); + self.validators = validators; + self + } + + /// Sets number of validator seats to given one. To get [`AccountId`] used + /// in the test environment the `validators` field of the built [`TestEnv`] + /// object can be used. Tests should not rely on any particular format of + /// account identifiers used by the builder. Panics if `num` is zero. + pub fn validator_seats(self, num: usize) -> Self { + self.validators(Self::make_accounts(num)) + } + + fn ensure_home_dirs(mut self) -> Self { + if self.home_dirs.is_none() { + let home_dirs = (0..self.clients.len()) + .map(|_| { + let temp_dir = tempfile::tempdir().unwrap(); + temp_dir.into_path() + }) + .collect_vec(); + self.home_dirs = Some(home_dirs) + } + self + } + + /// Overrides the stores that are used to create epoch managers and runtimes. + pub fn stores(mut self, stores: Vec) -> Self { + assert_eq!(stores.len(), self.clients.len()); + assert!(self.stores.is_none(), "Cannot override twice"); + assert!(self.epoch_managers.is_none(), "Cannot override store after epoch_managers"); + assert!(self.runtimes.is_none(), "Cannot override store after runtimes"); + self.stores = Some(stores); + self + } + + pub fn real_stores(self) -> Self { + let ret = self.ensure_home_dirs(); + let stores = ret + .home_dirs + .as_ref() + .unwrap() + .iter() + .map(|home_dir| { + // The max number of open files across all RocksDB instances is INT_MAX i.e. 65,535 + // The default value of max_open_files is 10,000 which only allows upto 6 RocksDB + // instance to open at a time. This is problematic in testing resharding. To overcome + // this limit, we set the max_open_files config to 1000. + let mut store_config = StoreConfig::default(); + store_config.max_open_files = 1000; + NodeStorage::opener(home_dir.as_path(), false, &store_config, None) + .open() + .unwrap() + .get_hot_store() + }) + .collect_vec(); + ret.stores(stores) + } + + /// Internal impl to make sure the stores are initialized. + fn ensure_stores(self) -> Self { + if self.stores.is_some() { + self + } else { + let num_clients = self.clients.len(); + self.stores((0..num_clients).map(|_| create_test_store()).collect()) + } + } + + /// Specifies custom MockEpochManager for each client. This allows us to + /// construct [`TestEnv`] with a custom implementation. + /// + /// The vector must have the same number of elements as they are clients + /// (one by default). If that does not hold, [`Self::build`] method will + /// panic. + pub fn mock_epoch_managers(mut self, epoch_managers: Vec>) -> Self { + assert_eq!(epoch_managers.len(), self.clients.len()); + assert!(self.epoch_managers.is_none(), "Cannot override twice"); + assert!( + self.num_shards.is_none(), + "Cannot set both num_shards and epoch_managers at the same time" + ); + assert!( + self.shard_trackers.is_none(), + "Cannot override epoch_managers after shard_trackers" + ); + assert!(self.runtimes.is_none(), "Cannot override epoch_managers after runtimes"); + self.epoch_managers = + Some(epoch_managers.into_iter().map(|epoch_manager| epoch_manager.into()).collect()); + self + } + + /// Specifies custom EpochManagerHandle for each client. This allows us to + /// construct [`TestEnv`] with a custom implementation. + /// + /// The vector must have the same number of elements as they are clients + /// (one by default). If that does not hold, [`Self::build`] method will + /// panic. + pub fn epoch_managers(mut self, epoch_managers: Vec>) -> Self { + assert_eq!(epoch_managers.len(), self.clients.len()); + assert!(self.epoch_managers.is_none(), "Cannot override twice"); + assert!( + self.num_shards.is_none(), + "Cannot set both num_shards and epoch_managers at the same time" + ); + assert!( + self.shard_trackers.is_none(), + "Cannot override epoch_managers after shard_trackers" + ); + assert!(self.runtimes.is_none(), "Cannot override epoch_managers after runtimes"); + self.epoch_managers = + Some(epoch_managers.into_iter().map(|epoch_manager| epoch_manager.into()).collect()); + self + } + + pub fn real_epoch_managers(self, genesis_config: &GenesisConfig) -> Self { + self.real_epoch_managers_with_test_overrides(genesis_config, None) + } + + /// Constructs real EpochManager implementations for each instance. + pub fn real_epoch_managers_with_test_overrides( + self, + genesis_config: &GenesisConfig, + test_overrides: Option, + ) -> Self { + assert!( + self.num_shards.is_none(), + "Cannot set both num_shards and epoch_managers at the same time" + ); + let ret = self.ensure_stores(); + let epoch_managers = (0..ret.clients.len()) + .map(|i| { + EpochManager::new_arc_handle_with_test_overrides( + ret.stores.as_ref().unwrap()[i].clone(), + genesis_config, + test_overrides.clone(), + ) + }) + .collect(); + ret.epoch_managers(epoch_managers) + } + + /// Internal impl to make sure EpochManagers are initialized. + fn ensure_epoch_managers(self) -> Self { + let mut ret = self.ensure_stores(); + if ret.epoch_managers.is_some() { + return ret; + } + let epoch_managers: Vec = (0..ret.clients.len()) + .map(|i| { + let vs = ValidatorSchedule::new_with_shards(ret.num_shards.unwrap_or(1)) + .block_producers_per_epoch(vec![ret.validators.clone()]); + MockEpochManager::new_with_validators( + ret.stores.as_ref().unwrap()[i].clone(), + vs, + ret.chain_genesis.epoch_length, + ) + .into() + }) + .collect(); + assert!( + ret.shard_trackers.is_none(), + "Cannot override shard_trackers without overriding epoch_managers" + ); + assert!( + ret.runtimes.is_none(), + "Cannot override runtimes without overriding epoch_managers" + ); + ret.epoch_managers = Some(epoch_managers); + ret + } + + /// Visible for extension methods in integration-tests. + pub fn internal_initialize_nightshade_runtimes( + self, + runtime_configs: Vec, + trie_configs: Vec, + nightshade_runtime_creator: impl Fn( + PathBuf, + Store, + Arc, + RuntimeConfigStore, + TrieConfig, + ) -> Arc, + ) -> Self { + let builder = self.ensure_home_dirs().ensure_epoch_managers().ensure_stores(); + let runtimes = multizip(( + builder.home_dirs.clone().unwrap(), + builder.stores.clone().unwrap(), + builder.epoch_managers.clone().unwrap(), + runtime_configs, + trie_configs, + )) + .map(|(home_dir, store, epoch_manager, runtime_config, trie_config)| { + let epoch_manager = match epoch_manager { + EpochManagerKind::Mock(_) => { + panic!("NightshadeRuntime can only be instantiated with EpochManagerHandle") + } + EpochManagerKind::Handle(handle) => handle, + }; + nightshade_runtime_creator(home_dir, store, epoch_manager, runtime_config, trie_config) + }) + .collect(); + builder.runtimes(runtimes) + } + + /// Specifies custom ShardTracker for each client. This allows us to + /// construct [`TestEnv`] with a custom implementation. + pub fn shard_trackers(mut self, shard_trackers: Vec) -> Self { + assert_eq!(shard_trackers.len(), self.clients.len()); + assert!(self.shard_trackers.is_none(), "Cannot override twice"); + self.shard_trackers = Some(shard_trackers); + self + } + + /// Constructs ShardTracker that tracks all shards for each instance. + /// + /// Note that in order to track *NO* shards, just don't override shard_trackers. + pub fn track_all_shards(self) -> Self { + let ret = self.ensure_epoch_managers(); + let shard_trackers = ret + .epoch_managers + .as_ref() + .unwrap() + .iter() + .map(|epoch_manager| { + ShardTracker::new(TrackedConfig::AllShards, epoch_manager.clone().into_adapter()) + }) + .collect(); + ret.shard_trackers(shard_trackers) + } + + /// Internal impl to make sure ShardTrackers are initialized. + fn ensure_shard_trackers(self) -> Self { + let ret = self.ensure_epoch_managers(); + if ret.shard_trackers.is_some() { + return ret; + } + let shard_trackers = ret + .epoch_managers + .as_ref() + .unwrap() + .iter() + .map(|epoch_manager| { + ShardTracker::new(TrackedConfig::new_empty(), epoch_manager.clone().into_adapter()) + }) + .collect(); + ret.shard_trackers(shard_trackers) + } + + /// Specifies custom RuntimeAdapter for each client. This allows us to + /// construct [`TestEnv`] with a custom implementation. + pub fn runtimes(mut self, runtimes: Vec>) -> Self { + assert_eq!(runtimes.len(), self.clients.len()); + assert!(self.runtimes.is_none(), "Cannot override twice"); + self.runtimes = Some(runtimes); + self + } + + /// Internal impl to make sure runtimes are initialized. + fn ensure_runtimes(self) -> Self { + let state_snapshot_enabled = self.state_snapshot_enabled; + let ret = self.ensure_epoch_managers(); + if ret.runtimes.is_some() { + return ret; + } + assert!( + !state_snapshot_enabled, + "State snapshot is not supported with KeyValueRuntime. Consider adding nightshade_runtimes" + ); + let runtimes = (0..ret.clients.len()) + .map(|i| { + let epoch_manager = match &ret.epoch_managers.as_ref().unwrap()[i] { + EpochManagerKind::Mock(mock) => mock.as_ref(), + EpochManagerKind::Handle(_) => { + panic!("Can only default construct KeyValueRuntime with MockEpochManager") + } + }; + KeyValueRuntime::new(ret.stores.as_ref().unwrap()[i].clone(), epoch_manager) + as Arc + }) + .collect(); + ret.runtimes(runtimes) + } + + /// Specifies custom network adaptors for each client. + /// + /// The vector must have the same number of elements as they are clients + /// (one by default). If that does not hold, [`Self::build`] method will + /// panic. + pub fn network_adapters(mut self, adapters: Vec>) -> Self { + self.network_adapters = Some(adapters); + self + } + + /// Internal impl to make sure network adapters are initialized. + fn ensure_network_adapters(self) -> Self { + if self.network_adapters.is_some() { + self + } else { + let num_clients = self.clients.len(); + self.network_adapters((0..num_clients).map(|_| Arc::new(Default::default())).collect()) + } + } + + pub fn num_shards(mut self, num_shards: NumShards) -> Self { + assert!( + self.epoch_managers.is_none(), + "Cannot set both num_shards and epoch_managers at the same time" + ); + self.num_shards = Some(num_shards); + self + } + + pub fn archive(mut self, archive: bool) -> Self { + self.archive = archive; + self + } + + pub fn save_trie_changes(mut self, save_trie_changes: bool) -> Self { + self.save_trie_changes = save_trie_changes; + self + } + + /// Constructs new `TestEnv` structure. + /// + /// If no clients were configured (either through count or vector) one + /// client is created. Similarly, if no validator seats were configured, + /// one seat is configured. + /// + /// Panics if `runtime_adapters` or `network_adapters` methods were used and + /// the length of the vectors passed to them did not equal number of + /// configured clients. + pub fn build(self) -> TestEnv { + self.ensure_shard_trackers().ensure_runtimes().ensure_network_adapters().build_impl() + } + + fn build_impl(self) -> TestEnv { + let chain_genesis = self.chain_genesis; + let clients = self.clients.clone(); + let num_clients = clients.len(); + let validators = self.validators; + let num_validators = validators.len(); + let seeds = self.seeds; + let epoch_managers = self.epoch_managers.unwrap(); + let shard_trackers = self.shard_trackers.unwrap(); + let runtimes = self.runtimes.unwrap(); + let network_adapters = self.network_adapters.unwrap(); + let client_adapters = (0..num_clients) + .map(|_| Arc::new(MockClientAdapterForShardsManager::default())) + .collect::>(); + let shards_manager_adapters = (0..num_clients) + .map(|i| { + let epoch_manager = epoch_managers[i].clone(); + let shard_tracker = shard_trackers[i].clone(); + let runtime = runtimes[i].clone(); + let network_adapter = network_adapters[i].clone(); + let client_adapter = client_adapters[i].clone(); + setup_synchronous_shards_manager( + Some(clients[i].clone()), + client_adapter.as_sender(), + network_adapter.into(), + epoch_manager.into_adapter(), + shard_tracker, + runtime, + &chain_genesis, + ) + }) + .collect::>(); + let clients = (0..num_clients) + .map(|i| { + let account_id = clients[i].clone(); + let network_adapter = network_adapters[i].clone(); + let shards_manager_adapter = shards_manager_adapters[i].clone(); + let epoch_manager = epoch_managers[i].clone(); + let shard_tracker = shard_trackers[i].clone(); + let runtime = runtimes[i].clone(); + let rng_seed = match seeds.get(&account_id) { + Some(seed) => *seed, + None => TEST_SEED, + }; + let tries = runtime.get_tries(); + let make_snapshot_callback = Arc::new(move |prev_block_hash, _epoch_height, shard_uids: Vec, block| { + tracing::info!(target: "state_snapshot", ?prev_block_hash, "make_snapshot_callback"); + tries.delete_state_snapshot(); + tries.create_state_snapshot(prev_block_hash, &shard_uids, &block).unwrap(); + }); + let tries = runtime.get_tries(); + let delete_snapshot_callback = Arc::new(move || { + tracing::info!(target: "state_snapshot", "delete_snapshot_callback"); + tries.delete_state_snapshot(); + }); + let snapshot_callbacks = SnapshotCallbacks { + make_snapshot_callback, + delete_snapshot_callback, + }; + setup_client_with_runtime( + u64::try_from(num_validators).unwrap(), + Some(account_id), + false, + network_adapter.into(), + shards_manager_adapter, + chain_genesis.clone(), + epoch_manager.into_adapter(), + shard_tracker, + runtime, + rng_seed, + self.archive, + self.save_trie_changes, + Some(snapshot_callbacks), + ) + }) + .collect(); + + TestEnv { + chain_genesis, + validators, + network_adapters, + client_adapters, + shards_manager_adapters, + clients, + account_indices: AccountIndices( + self.clients + .into_iter() + .enumerate() + .map(|(index, client)| (client, index)) + .collect(), + ), + paused_blocks: Default::default(), + seeds, + archive: self.archive, + save_trie_changes: self.save_trie_changes, + } + } + + fn make_accounts(count: usize) -> Vec { + (0..count).map(|i| format!("test{}", i).parse().unwrap()).collect() + } + + pub fn use_state_snapshots(mut self) -> Self { + assert!(self.runtimes.is_none(), "Set up snapshot config before runtimes"); + self.state_snapshot_enabled = true; + self + } + + pub fn state_snapshot_type(&self) -> StateSnapshotType { + if self.state_snapshot_enabled { + StateSnapshotType::EveryEpoch + } else { + StateSnapshotType::ForReshardingOnly + } + } +} diff --git a/chain/client/src/tests/bug_repros.rs b/chain/client/src/tests/bug_repros.rs new file mode 100644 index 000000000..c7ec825fa --- /dev/null +++ b/chain/client/src/tests/bug_repros.rs @@ -0,0 +1,333 @@ +// This test tracks tests that reproduce previously fixed bugs to make sure the regressions we +// fix do not resurface + +use std::cmp::max; +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; + +use actix::System; +use futures::FutureExt; +use unc_async::messaging::CanSend; +use unc_network::shards_manager::ShardsManagerRequestFromNetwork; +use rand::{thread_rng, Rng}; + +use crate::adapter::{BlockApproval, BlockResponse, ProcessTxRequest}; +use crate::test_utils::{setup_mock_all_validators, ActorHandlesForTesting}; +use crate::GetBlock; +use unc_actix_test_utils::run_actix; +use unc_chain::test_utils::{account_id_to_shard_id, ValidatorSchedule}; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_network::types::NetworkRequests::PartialEncodedChunkMessage; +use unc_network::types::PeerInfo; +use unc_network::types::{ + NetworkRequests, NetworkResponses, PeerManagerMessageRequest, PeerManagerMessageResponse, +}; +use unc_o11y::testonly::init_test_logger; +use unc_o11y::WithSpanContextExt; +use unc_primitives::block::Block; +use unc_primitives::transaction::SignedTransaction; + +#[test] +fn repro_1183() { + init_test_logger(); + run_actix(async { + let connectors: Arc>> = Arc::new(RwLock::new(vec![])); + + let vs = ValidatorSchedule::new() + .num_shards(4) + .block_producers_per_epoch(vec![vec![ + "test1".parse().unwrap(), + "test2".parse().unwrap(), + "test3".parse().unwrap(), + "test4".parse().unwrap(), + ]]) + .validator_groups(2); + let validators = vs.all_block_producers().cloned().collect::>(); + let key_pairs = vec![ + PeerInfo::random(), + PeerInfo::random(), + PeerInfo::random(), + PeerInfo::random(), // 4 + ]; + + let connectors1 = connectors.clone(); + let last_block: Arc>> = Arc::new(RwLock::new(None)); + let delayed_one_parts: Arc>> = Arc::new(RwLock::new(vec![])); + let (_, conn, _) = setup_mock_all_validators( + vs, + key_pairs, + true, + 200, + false, + false, + 5, + false, + vec![false; validators.len()], + vec![true; validators.len()], + false, + Box::new(move |_, _account_id: _, msg: &PeerManagerMessageRequest| { + if let NetworkRequests::Block { block } = msg.as_network_requests_ref() { + let mut last_block = last_block.write().unwrap(); + let mut delayed_one_parts = delayed_one_parts.write().unwrap(); + + if let Some(last_block) = last_block.clone() { + for actor_handles in connectors1.write().unwrap().iter() { + actor_handles.client_actor.do_send( + BlockResponse { + block: last_block.clone(), + peer_id: PeerInfo::random().id, + was_requested: false, + } + .with_span_context(), + ) + } + } + for delayed_message in delayed_one_parts.iter() { + if let PartialEncodedChunkMessage { + account_id, + partial_encoded_chunk, + .. + } = delayed_message + { + for (i, name) in validators.iter().enumerate() { + if name == account_id { + connectors1.write().unwrap()[i].shards_manager_adapter.send( + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunk( + partial_encoded_chunk.clone().into(), + ), + ); + } + } + } else { + assert!(false); + } + } + + let mut nonce_delta = 0; + for from in ["test1", "test2", "test3", "test4"].iter() { + for to in ["test1", "test2", "test3", "test4"].iter() { + let (from, to) = (from.parse().unwrap(), to.parse().unwrap()); + connectors1.write().unwrap()[account_id_to_shard_id(&from, 4) as usize] + .client_actor + .do_send( + ProcessTxRequest { + transaction: SignedTransaction::send_money( + block.header().height() * 16 + nonce_delta, + from.clone(), + to, + &InMemorySigner::from_seed( + from.clone(), + KeyType::ED25519, + from.as_ref(), + ), + 1, + *block.header().prev_hash(), + ), + is_forwarded: false, + check_only: false, + } + .with_span_context(), + ); + nonce_delta += 1 + } + } + + *last_block = Some(block.clone()); + *delayed_one_parts = vec![]; + + if block.header().height() >= 25 { + System::current().stop(); + } + (NetworkResponses::NoResponse.into(), false) + } else if let NetworkRequests::PartialEncodedChunkMessage { .. } = + msg.as_network_requests_ref() + { + if thread_rng().gen_bool(0.5) { + (NetworkResponses::NoResponse.into(), true) + } else { + delayed_one_parts + .write() + .unwrap() + .push(msg.as_network_requests_ref().clone()); + (NetworkResponses::NoResponse.into(), false) + } + } else { + (NetworkResponses::NoResponse.into(), true) + } + }), + ); + *connectors.write().unwrap() = conn; + + unc_network::test_utils::wait_or_panic(60000); + }); +} + +#[test] +fn test_sync_from_archival_node() { + init_test_logger(); + let vs = ValidatorSchedule::new().num_shards(4).block_producers_per_epoch(vec![vec![ + "test1".parse().unwrap(), + "test2".parse().unwrap(), + "test3".parse().unwrap(), + "test4".parse().unwrap(), + ]]); + let key_pairs = + vec![PeerInfo::random(), PeerInfo::random(), PeerInfo::random(), PeerInfo::random()]; + let largest_height = Arc::new(RwLock::new(0)); + let blocks = Arc::new(RwLock::new(HashMap::new())); + let epoch_length = 4; + + run_actix(async move { + let mut block_counter = 0; + setup_mock_all_validators( + vs, + key_pairs, + true, + 100, + false, + false, + epoch_length, + false, + vec![true, false, false, false], + vec![false, true, true, true], + false, + Box::new( + move |conns, + _, + msg: &PeerManagerMessageRequest| + -> (PeerManagerMessageResponse, bool) { + let msg = msg.as_network_requests_ref(); + if let NetworkRequests::Block { block } = msg { + let mut largest_height = largest_height.write().unwrap(); + *largest_height = max(block.header().height(), *largest_height); + } + if *largest_height.read().unwrap() >= 50 { + System::current().stop(); + } + if *largest_height.read().unwrap() <= 30 { + match msg { + NetworkRequests::Block { block } => { + for (i, actor_handles) in conns.iter().enumerate() { + if i != 3 { + actor_handles.client_actor.do_send( + BlockResponse { + block: block.clone(), + peer_id: PeerInfo::random().id, + was_requested: false, + } + .with_span_context(), + ) + } + } + if block.header().height() <= 10 { + blocks.write().unwrap().insert(*block.hash(), block.clone()); + } + (NetworkResponses::NoResponse.into(), false) + } + NetworkRequests::Approval { approval_message } => { + for (i, actor_handles) in conns.into_iter().enumerate() { + if i != 3 { + actor_handles.client_actor.do_send( + BlockApproval( + approval_message.approval.clone(), + PeerInfo::random().id, + ) + .with_span_context(), + ) + } + } + (NetworkResponses::NoResponse.into(), false) + } + _ => (NetworkResponses::NoResponse.into(), true), + } + } else { + if block_counter > 10 { + panic!("incorrect rebroadcasting of blocks"); + } + for (_, block) in blocks.write().unwrap().drain() { + conns[3].client_actor.do_send( + BlockResponse { + block, + peer_id: PeerInfo::random().id, + was_requested: false, + } + .with_span_context(), + ); + } + match msg { + NetworkRequests::Block { block } => { + if block.header().height() <= 10 { + block_counter += 1; + } + (NetworkResponses::NoResponse.into(), true) + } + _ => (NetworkResponses::NoResponse.into(), true), + } + } + }, + ), + ); + unc_network::test_utils::wait_or_panic(20000); + }); +} + +#[test] +fn test_long_gap_between_blocks() { + init_test_logger(); + let vs = ValidatorSchedule::new() + .num_shards(2) + .block_producers_per_epoch(vec![vec!["test1".parse().unwrap(), "test2".parse().unwrap()]]); + let key_pairs = vec![PeerInfo::random(), PeerInfo::random()]; + let epoch_length = 1000; + let target_height = 600; + + run_actix(async move { + setup_mock_all_validators( + vs, + key_pairs, + true, + 10, + false, + false, + epoch_length, + true, + vec![false, false], + vec![true, true], + false, + Box::new( + move |conns, + _, + msg: &PeerManagerMessageRequest| + -> (PeerManagerMessageResponse, bool) { + match msg.as_network_requests_ref() { + NetworkRequests::Approval { approval_message } => { + let actor = conns[1] + .view_client_actor + .send(GetBlock::latest().with_span_context()); + let actor = actor.then(move |res| { + let res = res.unwrap().unwrap(); + if res.header.height > target_height { + System::current().stop(); + } + futures::future::ready(()) + }); + actix::spawn(actor); + if approval_message.approval.target_height < target_height { + (NetworkResponses::NoResponse.into(), false) + } else { + if approval_message.target == "test1" { + (NetworkResponses::NoResponse.into(), true) + } else { + (NetworkResponses::NoResponse.into(), false) + } + } + } + _ => (NetworkResponses::NoResponse.into(), true), + } + }, + ), + ); + + unc_network::test_utils::wait_or_panic(60000); + }); +} diff --git a/chain/client/src/tests/catching_up.rs b/chain/client/src/tests/catching_up.rs new file mode 100644 index 000000000..d44e6b54e --- /dev/null +++ b/chain/client/src/tests/catching_up.rs @@ -0,0 +1,807 @@ +use std::collections::hash_map::Entry; +use std::collections::{HashMap, HashSet}; +use std::sync::{Arc, RwLock}; + +use actix::{Addr, System}; +use borsh::{BorshDeserialize, BorshSerialize}; +use futures::{future, FutureExt}; + +use crate::adapter::ProcessTxRequest; +use crate::test_utils::{setup_mock_all_validators, ActorHandlesForTesting}; +use crate::{ClientActor, Query}; +use unc_actix_test_utils::run_actix; +use unc_chain::test_utils::{account_id_to_shard_id, ValidatorSchedule}; +use unc_chain_configs::TEST_STATE_SYNC_TIMEOUT; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_network::types::PeerInfo; +use unc_network::types::{NetworkRequests, NetworkResponses, PeerManagerMessageRequest}; +use unc_o11y::testonly::init_integration_logger; +use unc_o11y::WithSpanContextExt; +use unc_primitives::hash::{hash as hash_func, CryptoHash}; +use unc_primitives::network::PeerId; +use unc_primitives::receipt::Receipt; +use unc_primitives::sharding::ChunkHash; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{AccountId, BlockHeight, BlockHeightDelta, BlockReference}; +use unc_primitives::views::QueryRequest; +use unc_primitives::views::QueryResponseKind::ViewAccount; + +fn get_validators_and_key_pairs() -> (ValidatorSchedule, Vec) { + let vs = ValidatorSchedule::new().num_shards(4).block_producers_per_epoch(vec![ + ["test1.1", "test1.2", "test1.3", "test1.4"] + .iter() + .map(|account_id| account_id.parse().unwrap()) + .collect(), + ["test2.1", "test2.2", "test2.3", "test2.4"] + .iter() + .map(|account_id| account_id.parse().unwrap()) + .collect(), + ["test3.1", "test3.2", "test3.3", "test3.4", "test3.5", "test3.6", "test3.7", "test3.8"] + .iter() + .map(|account_id| account_id.parse().unwrap()) + .collect(), + ]); + let key_pairs = vec![ + PeerInfo::random(), + PeerInfo::random(), + PeerInfo::random(), + PeerInfo::random(), // 4 + PeerInfo::random(), + PeerInfo::random(), + PeerInfo::random(), + PeerInfo::random(), // 8 + PeerInfo::random(), + PeerInfo::random(), + PeerInfo::random(), + PeerInfo::random(), + PeerInfo::random(), + PeerInfo::random(), + PeerInfo::random(), + PeerInfo::random(), // 16 + ]; + (vs, key_pairs) +} + +fn send_tx( + connector: &Addr, + from: AccountId, + to: AccountId, + amount: u128, + nonce: u64, + block_hash: CryptoHash, +) { + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + connector.do_send( + ProcessTxRequest { + transaction: SignedTransaction::send_money( + nonce, from, to, &signer, amount, block_hash, + ), + is_forwarded: false, + check_only: false, + } + .with_span_context(), + ); +} + +enum ReceiptsSyncPhases { + WaitingForFirstBlock, + WaitingForSecondBlock, + WaitingForDistantEpoch, + VerifyingOutgoingReceipts, + WaitingForValidate, +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct StateRequestStruct { + pub shard_id: u64, + pub sync_hash: CryptoHash, + pub part_id: Option, + pub peer_id: PeerId, +} + +/// Sanity checks that the incoming and outgoing receipts are properly sent and received +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_catchup_receipts_sync_third_epoch() { + test_catchup_receipts_sync_common(13, 1, false) +} + +/// The test aggressively blocks lots of state requests +/// and causes at least two timeouts per node (first for header, second for parts). +/// +/// WARNING! For your convenience, set manually STATE_SYNC_TIMEOUT to 1 before running the test. +/// It will be executed 10 times faster. +/// The reason of increasing block_prod_time in the test is to allow syncing complete. +/// Otherwise epochs will be changing faster than state sync happen. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_catchup_receipts_sync_hold() { + test_catchup_receipts_sync_common(13, 1, true) +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_catchup_receipts_sync_last_block() { + test_catchup_receipts_sync_common(13, 5, false) +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_catchup_receipts_sync_distant_epoch() { + test_catchup_receipts_sync_common(35, 1, false) +} + +fn test_catchup_receipts_sync_common(wait_till: u64, send: u64, sync_hold: bool) { + init_integration_logger(); + run_actix(async move { + let connectors: Arc>> = Arc::new(RwLock::new(vec![])); + + let (vs, key_pairs) = get_validators_and_key_pairs(); + let archive = vec![true; vs.all_block_producers().count()]; + let epoch_sync_enabled = vec![false; vs.all_block_producers().count()]; + + let phase = Arc::new(RwLock::new(ReceiptsSyncPhases::WaitingForFirstBlock)); + let seen_heights_with_receipts = Arc::new(RwLock::new(HashSet::::new())); + let seen_hashes_with_state = Arc::new(RwLock::new(HashSet::::new())); + + let connectors1 = connectors.clone(); + let mut block_prod_time: u64 = 3200; + if sync_hold { + block_prod_time *= TEST_STATE_SYNC_TIMEOUT as u64; + } + let (_, conn, _) = setup_mock_all_validators( + vs, + key_pairs, + true, + block_prod_time, + false, + false, + 5, + false, + archive, + epoch_sync_enabled, + false, + Box::new(move |_, _account_id: _, msg: &PeerManagerMessageRequest| { + let msg = msg.as_network_requests_ref(); + let account_from = "test3.3".parse().unwrap(); + let account_to = "test1.1".parse().unwrap(); + let source_shard_id = account_id_to_shard_id(&account_from, 4); + let destination_shard_id = account_id_to_shard_id(&account_to, 4); + + let mut phase = phase.write().unwrap(); + let mut seen_heights_with_receipts = seen_heights_with_receipts.write().unwrap(); + let mut seen_hashes_with_state = seen_hashes_with_state.write().unwrap(); + match *phase { + ReceiptsSyncPhases::WaitingForFirstBlock => { + if let NetworkRequests::Block { block } = msg { + assert!(block.header().height() <= send); + // This tx is rather fragile, specifically it's important that + // 1. the `from` and `to` account are not in the same shard; + // 2. ideally the producer of the chunk at height 3 for the shard + // in which `from` resides should not also be a block producer + // at height 3 + // 3. The `from` shard should also not match the block producer + // for height 1, because such block producer will produce + // the chunk for height 2 right away, before we manage to send + // the transaction. + if block.header().height() == send { + println!( + "From shard: {}, to shard: {}", + source_shard_id, destination_shard_id, + ); + for i in 0..16 { + send_tx( + &connectors1.write().unwrap()[i].client_actor, + account_from.clone(), + account_to.clone(), + 111, + 1, + *block.header().prev_hash(), + ); + } + *phase = ReceiptsSyncPhases::WaitingForSecondBlock; + } + } + } + ReceiptsSyncPhases::WaitingForSecondBlock => { + // This block now contains a chunk with the transaction sent above. + if let NetworkRequests::Block { block } = msg { + assert!(block.header().height() <= send + 1); + if block.header().height() == send + 1 { + *phase = ReceiptsSyncPhases::WaitingForDistantEpoch; + } + } + } + ReceiptsSyncPhases::WaitingForDistantEpoch => { + // This block now contains a chunk with the transaction sent above. + if let NetworkRequests::Block { block } = msg { + assert!(block.header().height() >= send + 1); + assert!(block.header().height() <= wait_till); + if block.header().height() == wait_till { + *phase = ReceiptsSyncPhases::VerifyingOutgoingReceipts; + } + } + if let NetworkRequests::PartialEncodedChunkMessage { + partial_encoded_chunk, + .. + } = msg + { + // The chunk producers in all epochs before `distant` need to be trying to + // include the receipt. The `distant` epoch is the first one that + // will get the receipt through the state sync. + let receipts: Vec = partial_encoded_chunk + .receipts + .iter() + .map(|x| x.0.clone()) + .flatten() + .collect(); + if !receipts.is_empty() { + assert_eq!( + partial_encoded_chunk.header.shard_id(), + source_shard_id + ); + seen_heights_with_receipts + .insert(partial_encoded_chunk.header.height_created()); + } else { + assert_ne!( + partial_encoded_chunk.header.shard_id(), + source_shard_id + ); + } + // Do not propagate any one parts, this will prevent any chunk from + // being included in the block + return (NetworkResponses::NoResponse.into(), false); + } + if let NetworkRequests::StateRequestHeader { + shard_id, + sync_hash, + peer_id, + } = msg + { + if sync_hold { + let srs = StateRequestStruct { + shard_id: *shard_id, + sync_hash: *sync_hash, + part_id: None, + peer_id: peer_id.clone(), + }; + if !seen_hashes_with_state + .contains(&hash_func(&borsh::to_vec(&srs).unwrap())) + { + seen_hashes_with_state + .insert(hash_func(&borsh::to_vec(&srs).unwrap())); + return (NetworkResponses::NoResponse.into(), false); + } + } + } + if let NetworkRequests::StateRequestPart { + shard_id, + sync_hash, + part_id, + peer_id, + } = msg + { + if sync_hold { + let srs = StateRequestStruct { + shard_id: *shard_id, + sync_hash: *sync_hash, + part_id: Some(*part_id), + peer_id: peer_id.clone(), + }; + if !seen_hashes_with_state + .contains(&hash_func(&borsh::to_vec(&srs).unwrap())) + { + seen_hashes_with_state + .insert(hash_func(&borsh::to_vec(&srs).unwrap())); + return (NetworkResponses::NoResponse.into(), false); + } + } + } + } + ReceiptsSyncPhases::VerifyingOutgoingReceipts => { + for height in send + 2..=wait_till { + println!( + "checking height {:?} out of {:?}, result = {:?}", + height, + wait_till, + seen_heights_with_receipts.contains(&height) + ); + if !sync_hold { + // If we don't delay the state, all heights should contain the same receipts + assert!(seen_heights_with_receipts.contains(&height)); + } + } + *phase = ReceiptsSyncPhases::WaitingForValidate; + } + ReceiptsSyncPhases::WaitingForValidate => { + // This block now contains a chunk with the transaction sent above. + if let NetworkRequests::Block { block } = msg { + assert!(block.header().height() >= wait_till); + assert!(block.header().height() <= wait_till + 20); + if block.header().height() == wait_till + 20 { + System::current().stop(); + } + if block.header().height() == wait_till + 10 { + for i in 0..16 { + let actor = + connectors1.write().unwrap()[i].view_client_actor.send( + Query::new( + BlockReference::latest(), + QueryRequest::ViewAccount { + account_id: account_to.clone(), + }, + ) + .with_span_context(), + ); + let actor = actor.then(move |res| { + let res_inner = res.unwrap(); + if let Ok(query_response) = res_inner { + if let ViewAccount(view_account_result) = + query_response.kind + { + assert_eq!(view_account_result.amount, 1111); + } + } + future::ready(()) + }); + actix::spawn(actor); + } + } + } + } + }; + (NetworkResponses::NoResponse.into(), true) + }), + ); + *connectors.write().unwrap() = conn; + let mut max_wait_ms = 240000; + if sync_hold { + max_wait_ms *= TEST_STATE_SYNC_TIMEOUT as u64; + } + + unc_network::test_utils::wait_or_panic(max_wait_ms); + }); +} + +enum RandomSinglePartPhases { + WaitingForFirstBlock, + WaitingForThirdEpoch, + WaitingForSixEpoch, +} + +/// Verifies that fetching of random parts works properly by issuing transactions during the +/// third epoch, and then making sure that the balances are correct for the next three epochs. +/// If random one parts fetched during the epoch preceding the epoch a block producer is +/// assigned to were to have incorrect receipts, the balances in the fourth epoch would have +/// been incorrect due to wrong receipts applied during the third epoch. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_catchup_random_single_part_sync() { + test_catchup_random_single_part_sync_common(false, false, 13) +} + +// Same test as `test_catchup_random_single_part_sync`, but skips the chunks on height 14 and 15 +// It causes all the receipts to be applied only on height 16, which is the next epoch. +// It tests that the incoming receipts are property synced through epochs +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_catchup_random_single_part_sync_skip_15() { + test_catchup_random_single_part_sync_common(true, false, 13) +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_catchup_random_single_part_sync_send_15() { + test_catchup_random_single_part_sync_common(false, false, 15) +} + +// Make sure that transactions are at least applied. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_catchup_random_single_part_sync_non_zero_amounts() { + test_catchup_random_single_part_sync_common(false, true, 13) +} + +// Use another height to send txs. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_catchup_random_single_part_sync_height_6() { + test_catchup_random_single_part_sync_common(false, false, 6) +} + +fn test_catchup_random_single_part_sync_common(skip_15: bool, non_zero: bool, height: u64) { + init_integration_logger(); + run_actix(async move { + let connectors: Arc>> = Arc::new(RwLock::new(vec![])); + + let (vs, key_pairs) = get_validators_and_key_pairs(); + let vs = vs.validator_groups(2); + let validators = vs.all_block_producers().cloned().collect::>(); + let phase = Arc::new(RwLock::new(RandomSinglePartPhases::WaitingForFirstBlock)); + let seen_heights_same_block = Arc::new(RwLock::new(HashSet::::new())); + + let amounts = Arc::new(RwLock::new(HashMap::new())); + + let check_amount = + move |amounts: Arc>>, account_id: AccountId, amount: u128| { + match amounts.write().unwrap().entry(account_id) { + Entry::Occupied(entry) => { + println!("OCCUPIED {:?}", entry); + assert_eq!(*entry.get(), amount); + } + Entry::Vacant(entry) => { + println!("VACANT {:?}", entry); + if non_zero { + assert_ne!(amount % 100, 0); + } else { + assert_eq!(amount % 100, 0); + } + entry.insert(amount); + } + } + }; + + let connectors1 = connectors.clone(); + let (_, conn, _) = setup_mock_all_validators( + vs, + key_pairs, + true, + 6000, + false, + false, + 5, + true, + vec![false; validators.len()], + vec![true; validators.len()], + false, + Box::new(move |_, _account_id: _, msg: &PeerManagerMessageRequest| { + let msg = msg.as_network_requests_ref(); + let mut seen_heights_same_block = seen_heights_same_block.write().unwrap(); + let mut phase = phase.write().unwrap(); + match *phase { + RandomSinglePartPhases::WaitingForFirstBlock => { + if let NetworkRequests::Block { block } = msg { + assert_eq!(block.header().height(), 1); + *phase = RandomSinglePartPhases::WaitingForThirdEpoch; + } + } + RandomSinglePartPhases::WaitingForThirdEpoch => { + if let NetworkRequests::Block { block } = msg { + if block.header().height() == 1 { + return (NetworkResponses::NoResponse.into(), false); + } + assert!(block.header().height() >= 2); + assert!(block.header().height() <= height); + let mut tx_count = 0; + if block.header().height() == height && block.header().height() >= 2 { + for (i, validator1) in validators.iter().enumerate() { + for (j, validator2) in validators.iter().enumerate() { + let mut amount = (((i + j + 17) * 701) % 42 + 1) as u128; + if non_zero { + if i > j { + amount = 2; + } else { + amount = 1; + } + } + println!( + "VALUES {:?} {:?} {:?}", + validator1.to_string(), + validator2.to_string(), + amount + ); + for conn in 0..validators.len() { + send_tx( + &connectors1.write().unwrap()[conn].client_actor, + validator1.clone(), + validator2.clone(), + amount, + (12345 + tx_count) as u64, + *block.header().prev_hash(), + ); + } + tx_count += 1; + } + } + *phase = RandomSinglePartPhases::WaitingForSixEpoch; + assert_eq!(tx_count, 16 * 16); + } + } + } + RandomSinglePartPhases::WaitingForSixEpoch => { + if let NetworkRequests::Block { block } = msg { + assert!(block.header().height() >= height); + assert!(block.header().height() <= 32); + let check_height = if skip_15 { 28 } else { 26 }; + if block.header().height() >= check_height { + println!("BLOCK HEIGHT {:?}", block.header().height()); + for i in 0..16 { + for j in 0..16 { + let amounts1 = amounts.clone(); + let validator = validators[j].clone(); + let actor = + connectors1.write().unwrap()[i].view_client_actor.send( + Query::new( + BlockReference::latest(), + QueryRequest::ViewAccount { + account_id: validators[j].clone(), + }, + ) + .with_span_context(), + ); + let actor = actor.then(move |res| { + let res_inner = res.unwrap(); + if let Ok(query_response) = res_inner { + if let ViewAccount(view_account_result) = + query_response.kind + { + check_amount( + amounts1, + validator, + view_account_result.amount, + ); + } + } + future::ready(()) + }); + actix::spawn(actor); + } + } + } + if block.header().height() == 32 { + println!( + "SEEN HEIGHTS SAME BLOCK {:?}", + seen_heights_same_block.len() + ); + assert_eq!(seen_heights_same_block.len(), 1); + let amounts1 = amounts.clone(); + for flat_validator in &validators { + match amounts1.write().unwrap().entry(flat_validator.clone()) { + Entry::Occupied(_) => { + continue; + } + Entry::Vacant(entry) => { + println!( + "VALIDATOR = {:?}, ENTRY = {:?}", + flat_validator, entry + ); + assert!(false); + } + }; + } + System::current().stop(); + } + } + if let NetworkRequests::PartialEncodedChunkMessage { + partial_encoded_chunk, + .. + } = msg + { + if partial_encoded_chunk.header.height_created() == 22 { + seen_heights_same_block + .insert(*partial_encoded_chunk.header.prev_block_hash()); + } + if skip_15 { + if partial_encoded_chunk.header.height_created() == 14 + || partial_encoded_chunk.header.height_created() == 15 + { + return (NetworkResponses::NoResponse.into(), false); + } + } + } + } + }; + (NetworkResponses::NoResponse.into(), true) + }), + ); + *connectors.write().unwrap() = conn; + + unc_network::test_utils::wait_or_panic(480000); + }); +} + +/// Makes sure that 24 consecutive blocks are produced by 12 validators split into three epochs. +/// For extra coverage doesn't allow block propagation of some heights (and expects the blocks +/// to be skipped) +/// This test would fail if at any point validators got stuck with state sync, or block +/// production stalled for any other reason. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_catchup_sanity_blocks_produced() { + init_integration_logger(); + run_actix(async move { + let connectors: Arc>> = Arc::new(RwLock::new(vec![])); + + let heights = Arc::new(RwLock::new(HashMap::new())); + let heights1 = heights; + + let check_height = + move |hash: CryptoHash, height| match heights1.write().unwrap().entry(hash) { + Entry::Occupied(entry) => { + assert_eq!(*entry.get(), height); + } + Entry::Vacant(entry) => { + entry.insert(height); + } + }; + + let (vs, key_pairs) = get_validators_and_key_pairs(); + let vs = vs.validator_groups(2); + let archive = vec![false; vs.all_block_producers().count()]; + let epoch_sync_enabled = vec![true; vs.all_block_producers().count()]; + + let (_, conn, _) = setup_mock_all_validators( + vs, + key_pairs, + true, + 2000, + false, + false, + 5, + true, + archive, + epoch_sync_enabled, + false, + Box::new(move |_, _account_id: _, msg: &PeerManagerMessageRequest| { + let msg = msg.as_network_requests_ref(); + let propagate = if let NetworkRequests::Block { block } = msg { + check_height(*block.hash(), block.header().height()); + + if block.header().height() % 10 == 5 { + check_height(*block.header().prev_hash(), block.header().height() - 2); + } else { + check_height(*block.header().prev_hash(), block.header().height() - 1); + } + + if block.header().height() >= 25 { + System::current().stop(); + } + + // Do not propagate blocks at heights %10=4 + block.header().height() % 10 != 4 + } else { + true + }; + + (NetworkResponses::NoResponse.into(), propagate) + }), + ); + *connectors.write().unwrap() = conn; + + unc_network::test_utils::wait_or_panic(60000); + }); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_all_chunks_accepted_1000() { + test_all_chunks_accepted_common(1000, 3000, 5) +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_all_chunks_accepted_1000_slow() { + test_all_chunks_accepted_common(1000, 6000, 5) +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_all_chunks_accepted_1000_rare_epoch_changing() { + test_all_chunks_accepted_common(1000, 1500, 100) +} + +fn test_all_chunks_accepted_common( + last_height: BlockHeight, + block_prod_time: u64, + epoch_length: BlockHeightDelta, +) { + init_integration_logger(); + run_actix(async move { + let connectors: Arc>> = Arc::new(RwLock::new(vec![])); + + let (vs, key_pairs) = get_validators_and_key_pairs(); + let archive = vec![false; vs.all_block_producers().count()]; + let epoch_sync_enabled = vec![true; vs.all_block_producers().count()]; + + let verbose = false; + + let seen_chunk_same_sender = Arc::new(RwLock::new(HashSet::<(AccountId, u64, u64)>::new())); + let requested = Arc::new(RwLock::new(HashSet::<(AccountId, Vec, ChunkHash)>::new())); + let responded = Arc::new(RwLock::new(HashSet::<(CryptoHash, Vec, ChunkHash)>::new())); + + let (_, conn, _) = setup_mock_all_validators( + vs, + key_pairs, + true, + block_prod_time, + false, + false, + epoch_length, + true, + archive, + epoch_sync_enabled, + false, + Box::new(move |_, sender_account_id: AccountId, msg: &PeerManagerMessageRequest| { + let msg = msg.as_network_requests_ref(); + let mut seen_chunk_same_sender = seen_chunk_same_sender.write().unwrap(); + let mut requested = requested.write().unwrap(); + let mut responded = responded.write().unwrap(); + if let NetworkRequests::PartialEncodedChunkMessage { + account_id, + partial_encoded_chunk, + } = msg + { + let header = &partial_encoded_chunk.header; + if seen_chunk_same_sender.contains(&( + account_id.clone(), + header.height_created(), + header.shard_id(), + )) { + println!("=== SAME CHUNK AGAIN!"); + assert!(false); + }; + seen_chunk_same_sender.insert(( + account_id.clone(), + header.height_created(), + header.shard_id(), + )); + } + if let NetworkRequests::PartialEncodedChunkRequest { request, .. } = msg { + if verbose { + if requested.contains(&( + sender_account_id.clone(), + request.part_ords.clone(), + request.chunk_hash.clone(), + )) { + println!("=== SAME REQUEST AGAIN!"); + }; + requested.insert(( + sender_account_id, + request.part_ords.clone(), + request.chunk_hash.clone(), + )); + } + } + if let NetworkRequests::PartialEncodedChunkResponse { route_back, response } = msg { + if verbose { + if responded.contains(&( + *route_back, + response.parts.iter().map(|x| x.part_ord).collect(), + response.chunk_hash.clone(), + )) { + println!("=== SAME RESPONSE AGAIN!"); + } + responded.insert(( + *route_back, + response.parts.iter().map(|x| x.part_ord).collect(), + response.chunk_hash.clone(), + )); + } + } + if let NetworkRequests::Block { block } = msg { + // There is no chunks at height 1 + if block.header().height() > 1 { + if block.header().height() % epoch_length != 1 { + if block.header().chunks_included() != 4 { + println!( + "BLOCK WITH {:?} CHUNKS, {:?}", + block.header().chunks_included(), + block + ); + assert!(false); + } + } + if block.header().height() == last_height { + System::current().stop(); + } + } + } + (NetworkResponses::NoResponse.into(), true) + }), + ); + *connectors.write().unwrap() = conn; + let max_wait_ms = block_prod_time * last_height / 10 * 18 + 20000; + + unc_network::test_utils::wait_or_panic(max_wait_ms); + }); +} diff --git a/chain/client/src/tests/chunks_management.rs b/chain/client/src/tests/chunks_management.rs new file mode 100644 index 000000000..455d02921 --- /dev/null +++ b/chain/client/src/tests/chunks_management.rs @@ -0,0 +1,51 @@ +use std::collections::HashSet; + +use crate::test_utils::TestEnv; +use unc_async::messaging::CanSend; +use unc_chain::ChainGenesis; +use unc_network::shards_manager::ShardsManagerRequestFromNetwork; +use unc_network::types::NetworkRequests; +use unc_network::types::PartialEncodedChunkRequestMsg; +use unc_o11y::testonly::init_integration_logger; +use unc_primitives::hash::CryptoHash; + +// TODO(#8269) Enable test after fixing the issue related to KeyValueRuntime. See env.restart() +#[ignore] +#[test] +fn test_request_chunk_restart() { + init_integration_logger(); + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + for i in 1..4 { + env.produce_block(0, i); + env.network_adapters[0].pop(); + } + let block1 = env.clients[0].chain.get_block_by_height(3).unwrap(); + let request = PartialEncodedChunkRequestMsg { + chunk_hash: block1.chunks()[0].chunk_hash(), + part_ords: vec![0], + tracking_shards: HashSet::default(), + }; + env.shards_manager_adapters[0].send( + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkRequest { + partial_encoded_chunk_request: request.clone(), + route_back: CryptoHash::default(), + }, + ); + assert!(env.network_adapters[0].pop().is_some()); + + env.restart(0); + env.shards_manager_adapters[0].send( + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkRequest { + partial_encoded_chunk_request: request, + route_back: CryptoHash::default(), + }, + ); + let response = env.network_adapters[0].pop().unwrap().as_network_requests(); + + if let NetworkRequests::PartialEncodedChunkResponse { response: response_body, .. } = response { + assert_eq!(response_body.chunk_hash, block1.chunks()[0].chunk_hash()); + } else { + println!("{:?}", response); + assert!(false); + } +} diff --git a/chain/client/src/tests/consensus.rs b/chain/client/src/tests/consensus.rs new file mode 100644 index 000000000..6ae186e02 --- /dev/null +++ b/chain/client/src/tests/consensus.rs @@ -0,0 +1,286 @@ +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::sync::{Arc, RwLock, RwLockWriteGuard}; + +use actix::System; +use unc_chain::test_utils::ValidatorSchedule; +use rand::{thread_rng, Rng}; + +use crate::adapter::{BlockApproval, BlockResponse}; +use crate::test_utils::{setup_mock_all_validators, ActorHandlesForTesting}; +use unc_actix_test_utils::run_actix; +use unc_chain::Block; +use unc_network::types::PeerInfo; +use unc_network::types::{NetworkRequests, NetworkResponses, PeerManagerMessageRequest}; +use unc_o11y::testonly::init_integration_logger; +use unc_o11y::WithSpanContextExt; +use unc_primitives::block::{Approval, ApprovalInner}; +use unc_primitives::types::{AccountId, BlockHeight}; + +/// Rotates three independent sets of block producers producing blocks with a very short epoch length. +/// Occasionally when an endorsement comes, make all the endorsers send a skip message far-ish into +/// the future, and delay the distribution of the block produced this way. +/// Periodically verify finality is not violated. +/// This test is designed to reproduce finality bugs on the epoch boundaries. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_consensus_with_epoch_switches() { + init_integration_logger(); + + const HEIGHT_GOAL: u64 = 120; + + run_actix(async move { + let connectors: Arc>> = Arc::new(RwLock::new(vec![])); + let connectors1 = connectors.clone(); + + let validators: Vec> = [ + [ + "test1.1", "test1.2", "test1.3", "test1.4", "test1.5", "test1.6", "test1.7", + "test1.8", + ], + [ + "test2.1", "test2.2", "test2.3", "test2.4", "test2.5", "test2.6", "test2.7", + "test2.8", + ], + [ + "test3.1", "test3.2", "test3.3", "test3.4", "test3.5", "test3.6", "test3.7", + "test3.8", + ], + ] + .iter() + .map(|l| l.iter().map(|account_id| account_id.parse().unwrap()).collect()) + .collect(); + let vs = + ValidatorSchedule::new().num_shards(8).block_producers_per_epoch(validators.clone()); + let key_pairs = (0..24).map(|_| PeerInfo::random()).collect::>(); + let archive = vec![true; vs.all_block_producers().count()]; + let epoch_sync_enabled = vec![false; vs.all_block_producers().count()]; + + let block_to_prev_block = Arc::new(RwLock::new(HashMap::new())); + let block_to_height = Arc::new(RwLock::new(HashMap::new())); + + let all_blocks = Arc::new(RwLock::new(BTreeMap::new())); + let final_block_heights = Arc::new(RwLock::new(HashSet::new())); + + let largest_target_height = Arc::new(RwLock::new(vec![0u64; 24])); + let skips_per_height = Arc::new(RwLock::new(vec![])); + + let largest_block_height = Arc::new(RwLock::new(0u64)); + let delayed_blocks = Arc::new(RwLock::new(vec![])); + + let (_, conn, _) = setup_mock_all_validators( + vs, + key_pairs.clone(), + true, + 1000, + false, + false, + 4, + true, + archive, + epoch_sync_enabled, + false, + Box::new(move |_, from_whom: AccountId, msg: &PeerManagerMessageRequest| { + let mut all_blocks: RwLockWriteGuard> = + all_blocks.write().unwrap(); + let mut final_block_heights = final_block_heights.write().unwrap(); + let mut block_to_height = block_to_height.write().unwrap(); + let mut block_to_prev_block = block_to_prev_block.write().unwrap(); + let mut largest_target_height = largest_target_height.write().unwrap(); + let mut skips_per_height = skips_per_height.write().unwrap(); + let mut largest_block_height = largest_block_height.write().unwrap(); + + let mut delayed_blocks = delayed_blocks.write().unwrap(); + + match msg.as_network_requests_ref() { + NetworkRequests::Block { block } => { + if !all_blocks.contains_key(&block.header().height()) { + println!( + "BLOCK @{} EPOCH: {:?}, APPROVALS: {:?}", + block.header().height(), + block.header().epoch_id(), + block + .header() + .approvals() + .iter() + .map(|x| if x.is_some() { 1 } else { 0 }) + .collect::>() + ); + } + all_blocks.insert(block.header().height(), block.clone()); + block_to_prev_block.insert(*block.hash(), *block.header().prev_hash()); + block_to_height.insert(*block.hash(), block.header().height()); + + if *largest_block_height / 20 < block.header().height() / 20 { + // Periodically verify the finality + println!("VERIFYING FINALITY CONDITIONS"); + for block in all_blocks.values() { + if let Some(prev_hash) = block_to_prev_block.get(&block.hash()) { + if let Some(prev_height) = block_to_height.get(prev_hash) { + let cur_height = block.header().height(); + for f in final_block_heights.iter() { + if f < &cur_height && f > prev_height { + assert!( + false, + "{} < {} < {}", + prev_height, f, cur_height + ); + } + } + } + } + } + + if *largest_block_height >= HEIGHT_GOAL { + System::current().stop(); + } + } + + if block.header().height() > *largest_block_height + 3 { + *largest_block_height = block.header().height(); + if delayed_blocks.len() < 2 { + delayed_blocks.push(block.clone()); + return (NetworkResponses::NoResponse.into(), false); + } + } + *largest_block_height = + std::cmp::max(block.header().height(), *largest_block_height); + + let mut new_delayed_blocks = vec![]; + for delayed_block in delayed_blocks.iter() { + if delayed_block.hash() == block.hash() { + return (NetworkResponses::NoResponse.into(), false); + } + if delayed_block.header().height() <= block.header().height() + 2 { + for target_ord in 0..24 { + connectors1.write().unwrap()[target_ord].client_actor.do_send( + BlockResponse { + block: delayed_block.clone(), + peer_id: key_pairs[0].clone().id, + was_requested: true, + } + .with_span_context(), + ); + } + } else { + new_delayed_blocks.push(delayed_block.clone()) + } + } + *delayed_blocks = new_delayed_blocks; + + let mut heights = vec![]; + let mut cur_hash = *block.hash(); + while let Some(height) = block_to_height.get(&cur_hash) { + heights.push(height); + cur_hash = *block_to_prev_block.get(&cur_hash).unwrap(); + if heights.len() > 10 { + break; + } + } + // Use Doomslug finality, since without duplicate blocks at the same height + // it also provides safety under 1/3 faults + let is_final = heights.len() > 1 && heights[1] + 1 == *heights[0]; + println!( + "IS_FINAL: {} DELAYED: ({:?}) BLOCK: {} HISTORY: {:?}", + is_final, + delayed_blocks.iter().map(|x| x.header().height()).collect::>(), + block.hash(), + heights, + ); + + if is_final { + final_block_heights.insert(*heights[1]); + } + } + NetworkRequests::Approval { approval_message } => { + // Identify who we are, and whom we are sending this message to + let mut epoch_id = 100; + let mut destination_ord = 100; + let mut my_ord = 100; + + for i in 0..validators.len() { + for j in 0..validators[i].len() { + if validators[i][j] == approval_message.target { + epoch_id = i; + destination_ord = j; + } + if validators[i][j] == from_whom { + my_ord = i * 8 + j; + } + } + } + assert_ne!(epoch_id, 100); + assert_ne!(my_ord, 100); + + // For each height we define `skips_per_height`, and each block producer sends + // skips that far into the future from that source height. + let source_height = match approval_message.approval.inner { + ApprovalInner::Endorsement(_) => { + if largest_target_height[my_ord] + >= approval_message.approval.target_height + && my_ord % 8 >= 2 + { + // We already manually sent a skip conflicting with this endorsement + // my_ord % 8 < 2 are two malicious actors in every epoch and they + // continue sending endorsements + return (NetworkResponses::NoResponse.into(), false); + } + + approval_message.approval.target_height - 1 + } + ApprovalInner::Skip(source_height) => source_height, + }; + + while source_height as usize >= skips_per_height.len() { + skips_per_height.push(if thread_rng().gen_bool(0.8) { + 0 + } else { + thread_rng().gen_range(2..9) + }); + } + if skips_per_height[source_height as usize] > 0 + && approval_message.approval.target_height - source_height == 1 + { + let delta = skips_per_height[source_height as usize]; + let approval = Approval { + target_height: approval_message.approval.target_height + + delta as u64, + inner: ApprovalInner::Skip(source_height), + ..approval_message.approval.clone() + }; + largest_target_height[my_ord] = std::cmp::max( + largest_target_height[my_ord], + approval.target_height as u64, + ); + connectors1.write().unwrap() + [epoch_id * 8 + (destination_ord + delta) % 8] + .client_actor + .do_send( + BlockApproval(approval, key_pairs[my_ord].id.clone()) + .with_span_context(), + ); + // Do not send the endorsement for couple block producers in each epoch + // This is needed because otherwise the block with enough endorsements + // sometimes comes faster than the sufficient number of skips is created, + // (because the block producer themselves doesn't send the endorsement + // over the network, they have one more approval ready to produce their + // block than the block producer that will be at the later height). If + // such a block is indeed produced faster than all the skips are created, + // the paritcipants who haven't sent their endorsements to be converted + // to skips change their head. + if my_ord % 8 < 2 { + return (NetworkResponses::NoResponse.into(), false); + } + } + } + _ => {} + }; + (NetworkResponses::NoResponse.into(), true) + }), + ); + *connectors.write().unwrap() = conn; + + // We only check the terminating condition once every 20 heights, thus extra 80 to + // account for possibly going beyond the HEIGHT_GOAL. + unc_network::test_utils::wait_or_panic(3000 * (80 + HEIGHT_GOAL)); + }); +} diff --git a/chain/client/src/tests/cross_shard_tx.rs b/chain/client/src/tests/cross_shard_tx.rs new file mode 100644 index 000000000..47ffc9ecd --- /dev/null +++ b/chain/client/src/tests/cross_shard_tx.rs @@ -0,0 +1,579 @@ +#![allow(unused_imports)] + +use std::collections::HashSet; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, RwLock}; + +use actix::{Addr, MailboxError, System}; +use futures::{future, FutureExt}; + +use unc_actix_test_utils::run_actix; +use unc_chain::test_utils::{account_id_to_shard_id, ValidatorSchedule}; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_network::types::PeerInfo; +use unc_network::types::{ + NetworkResponses, PeerManagerMessageRequest, PeerManagerMessageResponse, +}; +use unc_o11y::testonly::init_integration_logger; +use unc_o11y::WithSpanContextExt; +use unc_primitives::hash::CryptoHash; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{AccountId, BlockReference}; +use unc_primitives::views::QueryResponseKind::ViewAccount; +use unc_primitives::views::{QueryRequest, QueryResponse}; + +use crate::adapter::{ProcessTxRequest, ProcessTxResponse}; +use crate::test_utils::{setup_mock_all_validators, ActorHandlesForTesting, BlockStats}; +use crate::{ClientActor, Query, ViewClientActor}; + +/// Tests that the KeyValueRuntime properly sets balances in genesis and makes them queriable +#[test] +fn test_keyvalue_runtime_balances() { + let successful_queries = Arc::new(AtomicUsize::new(0)); + init_integration_logger(); + run_actix(async move { + let connectors: Arc>> = Arc::new(RwLock::new(vec![])); + + let vs = ValidatorSchedule::new() + .num_shards(4) + .block_producers_per_epoch(vec![vec![ + "test1".parse().unwrap(), + "test2".parse().unwrap(), + "test3".parse().unwrap(), + "test4".parse().unwrap(), + ]]) + .validator_groups(2); + let validators = vs.all_block_producers().cloned().collect::>(); + let key_pairs = + vec![PeerInfo::random(), PeerInfo::random(), PeerInfo::random(), PeerInfo::random()]; + let (_, conn, _) = setup_mock_all_validators( + vs, + key_pairs, + true, + 100, + false, + false, + 5, + false, + vec![false; validators.len()], + vec![true; validators.len()], + false, + Box::new(move |_, _account_id: _, _msg: &PeerManagerMessageRequest| { + (NetworkResponses::NoResponse.into(), true) + }), + ); + *connectors.write().unwrap() = conn; + + let connectors_ = connectors.write().unwrap(); + for i in 0..4 { + let expected = (1000 + i * 100) as u128; + + let successful_queries2 = successful_queries.clone(); + let actor = connectors_[i].view_client_actor.send( + Query::new( + BlockReference::latest(), + QueryRequest::ViewAccount { account_id: validators[i].clone() }, + ) + .with_span_context(), + ); + let actor = actor.then(move |res| { + let query_response = res.unwrap().unwrap(); + if let ViewAccount(view_account_result) = query_response.kind { + assert_eq!(view_account_result.amount, expected); + successful_queries2.fetch_add(1, Ordering::Relaxed); + if successful_queries2.load(Ordering::Relaxed) >= 4 { + System::current().stop(); + } + } + future::ready(()) + }); + actix::spawn(actor); + } + + unc_network::test_utils::wait_or_panic(5000); + }); +} + +fn send_tx( + num_validators: usize, + connectors: Arc>>, + connector_ordinal: usize, + from: AccountId, + to: AccountId, + amount: u128, + nonce: u64, + block_hash: CryptoHash, +) { + let connectors1 = connectors.clone(); + let signer = InMemorySigner::from_seed(from.clone(), KeyType::ED25519, from.as_ref()); + actix::spawn( + connectors.write().unwrap()[connector_ordinal] + .client_actor + .send( + ProcessTxRequest { + transaction: SignedTransaction::send_money( + nonce, + from.clone(), + to.clone(), + &signer, + amount, + block_hash, + ), + is_forwarded: false, + check_only: false, + } + .with_span_context(), + ) + .then(move |x| { + match x.unwrap() { + ProcessTxResponse::NoResponse | ProcessTxResponse::RequestRouted => { + assert_eq!(num_validators, 24); + send_tx( + num_validators, + connectors1, + (connector_ordinal + 8) % num_validators, + from, + to, + amount, + nonce, + block_hash, + ); + } + ProcessTxResponse::ValidTx => { + println!("Transaction was received by validator {:?}", connector_ordinal); + } + other @ _ => { + println!( + "Transaction was rejected with an unexpected outcome: {:?}", + other + ); + assert!(false) + } + } + future::ready(()) + }), + ); +} + +fn test_cross_shard_tx_callback( + res: Result, MailboxError>, + account_id: AccountId, + connectors: Arc>>, + iteration: Arc, + nonce: Arc, + validators: Vec, + successful_queries: Arc>>, + unsuccessful_queries: Arc, + balances: Arc>>, + observed_balances: Arc>>, + presumable_epoch: Arc>, + num_iters: usize, + block_hash: CryptoHash, + block_stats: Arc>, + min_ratio: Option, + max_ratio: Option, +) { + let res = res.unwrap(); + + let query_response = match res { + Ok(query_response) => query_response, + Err(e) => { + println!("Query failed with {:?}", e); + *presumable_epoch.write().unwrap() += 1; + let connectors_ = connectors.write().unwrap(); + let connectors1 = connectors.clone(); + let iteration1 = iteration; + let nonce1 = nonce; + let validators1 = validators; + let successful_queries1 = successful_queries; + let unsuccessful_queries1 = unsuccessful_queries; + let balances1 = balances; + let observed_balances1 = observed_balances; + let presumable_epoch1 = presumable_epoch.clone(); + let actor = &connectors_[account_id_to_shard_id(&account_id, 8) as usize + + (*presumable_epoch.read().unwrap() * 8) % 24] + .view_client_actor; + let actor = actor.send( + Query::new( + BlockReference::latest(), + QueryRequest::ViewAccount { account_id: account_id.clone() }, + ) + .with_span_context(), + ); + let actor = actor.then(move |x| { + test_cross_shard_tx_callback( + x, + account_id, + connectors1, + iteration1, + nonce1, + validators1, + successful_queries1, + unsuccessful_queries1, + balances1, + observed_balances1, + presumable_epoch1, + num_iters, + block_hash, + block_stats, + min_ratio, + max_ratio, + ); + future::ready(()) + }); + actix::spawn(actor); + return; + } + }; + + if let ViewAccount(view_account_result) = query_response.kind { + let mut expected = 0; + for i in 0..8 { + if validators[i] == account_id { + expected = balances.read().unwrap()[i]; + observed_balances.write().unwrap()[i] = view_account_result.amount; + } + } + + if view_account_result.amount == expected { + let mut successful_queries_local = successful_queries.write().unwrap(); + assert!(!successful_queries_local.contains(&account_id)); + successful_queries_local.insert(account_id); + if successful_queries_local.len() == 8 { + println!("Finished iteration {}", iteration.load(Ordering::Relaxed)); + + iteration.fetch_add(1, Ordering::Relaxed); + let iteration_local = iteration.load(Ordering::Relaxed); + if iteration_local > num_iters { + (&mut *block_stats.write().unwrap()).check_stats(true); + (&mut *block_stats.write().unwrap()).check_block_ratio(min_ratio, max_ratio); + System::current().stop(); + } + + let from = iteration_local % 8; + let to = (iteration_local / 8) % 8; + let amount = (5 + iteration_local) as u128; + let next_nonce = nonce.fetch_add(1, Ordering::Relaxed); + + send_tx( + validators.len(), + connectors.clone(), + account_id_to_shard_id(&validators[from], 8) as usize, + validators[from].clone(), + validators[to].clone(), + amount, + next_nonce as u64, + block_hash, + ); + + let connectors_ = connectors.write().unwrap(); + + let mut balances_local = balances.write().unwrap(); + balances_local[from] -= amount; + balances_local[to] += amount; + + successful_queries_local.clear(); + unsuccessful_queries.store(0, Ordering::Relaxed); + + // Send the initial balance queries for the iteration + for i in 0..8 { + let connectors1 = connectors.clone(); + let iteration1 = iteration.clone(); + let nonce1 = nonce.clone(); + let validators1 = validators.clone(); + let successful_queries1 = successful_queries.clone(); + let unsuccessful_queries1 = unsuccessful_queries.clone(); + let balances1 = balances.clone(); + let observed_balances1 = observed_balances.clone(); + let presumable_epoch1 = presumable_epoch.clone(); + let account_id1 = validators[i].clone(); + let block_stats1 = block_stats.clone(); + let actor = &connectors_[account_id_to_shard_id(&validators[i], 8) as usize + + (*presumable_epoch.read().unwrap() * 8) % 24] + .view_client_actor; + let actor = actor.send( + Query::new( + BlockReference::latest(), + QueryRequest::ViewAccount { account_id: validators[i].clone() }, + ) + .with_span_context(), + ); + let actor = actor.then(move |x| { + test_cross_shard_tx_callback( + x, + account_id1, + connectors1, + iteration1, + nonce1, + validators1, + successful_queries1, + unsuccessful_queries1, + balances1, + observed_balances1, + presumable_epoch1, + num_iters, + block_hash, + block_stats1, + min_ratio, + max_ratio, + ); + future::ready(()) + }); + actix::spawn(actor); + } + } + } else { + // The balance is not correct, optionally trace, and resend the query + unsuccessful_queries.fetch_add(1, Ordering::Relaxed); + if unsuccessful_queries.load(Ordering::Relaxed) % 100 == 0 { + println!("Waiting for balances"); + print!("Expected: "); + for i in 0..8 { + print!("{} ", balances.read().unwrap()[i]); + } + println!(); + print!("Received: "); + for i in 0..8 { + print!("{} ", observed_balances.read().unwrap()[i]); + } + println!(); + } + + let connectors_ = connectors.write().unwrap(); + let connectors1 = connectors.clone(); + let presumable_epoch1 = presumable_epoch.clone(); + let actor = &connectors_[account_id_to_shard_id(&account_id, 8) as usize + + (*presumable_epoch.read().unwrap() * 8) % 24] + .view_client_actor; + let actor = actor.send( + Query::new( + BlockReference::latest(), + QueryRequest::ViewAccount { account_id: account_id.clone() }, + ) + .with_span_context(), + ); + let actor = actor.then(move |x| { + test_cross_shard_tx_callback( + x, + account_id, + connectors1, + iteration, + nonce, + validators, + successful_queries, + unsuccessful_queries, + balances, + observed_balances, + presumable_epoch1, + num_iters, + block_hash, + block_stats, + min_ratio, + max_ratio, + ); + future::ready(()) + }); + actix::spawn(actor); + } + } +} + +/// The basic flow of the test spins up validators, and starts sending to them +/// several (at most 64) cross-shard transactions. i-th transaction sends money from +/// validator i/8 to validator i%8. What makes the test good is that due to very low +/// block production time, it creates lots of forks, delaying both the transaction +/// acceptance, and the receipt delivery. +/// +/// It submits txs one at a time, and waits for their completion before sending the +/// next. Whenever the transaction completed, it traces "Finished iteration 1" (with +/// the ordinal increasing). Given the test takes a while, checking how far below +/// the last some message is a good way to early tell that the test has stalled. +/// E.g. if the last message is not in the last 15% of the output, it is likely that +/// the test will not make further progress, depending on the mode (with validator +/// rotation some iterations are way longer than other). +fn test_cross_shard_tx_common( + num_iters: usize, + rotate_validators: bool, + drop_chunks: bool, + test_doomslug: bool, + block_production_time: u64, + min_ratio: Option, + max_ratio: Option, +) { + init_integration_logger(); + run_actix(async move { + let connectors: Arc>> = Arc::new(RwLock::new(vec![])); + + let vs = ValidatorSchedule::new() + .num_shards(8) + .block_producers_per_epoch( + if rotate_validators { + [ + [ + "test1.1", "test1.2", "test1.3", "test1.4", "test1.5", "test1.6", + "test1.7", "test1.8", + ], + [ + "test2.1", "test2.2", "test2.3", "test2.4", "test2.5", "test2.6", + "test2.7", "test2.8", + ], + [ + "test3.1", "test3.2", "test3.3", "test3.4", "test3.5", "test3.6", + "test3.7", "test3.8", + ], + ] + .iter() + } else { + [[ + "test1.1", "test1.2", "test1.3", "test1.4", "test1.5", "test1.6", + "test1.7", "test1.8", + ]] + .iter() + } + .map(|l| l.iter().map(|account_id| account_id.parse().unwrap()).collect()) + .collect(), + ) + .validator_groups(4); + let validators = vs.all_block_producers().cloned().collect::>(); + + let key_pairs = (0..32).map(|_| PeerInfo::random()).collect::>(); + let balances = Arc::new(RwLock::new(vec![])); + let observed_balances = Arc::new(RwLock::new(vec![])); + let presumable_epoch = Arc::new(RwLock::new(0usize)); + + let mut balances_local = balances.write().unwrap(); + let mut observed_balances_local = observed_balances.write().unwrap(); + for i in 0..8 { + balances_local.push(1000 + 100 * i); + observed_balances_local.push(0); + } + + let (genesis_block, conn, block_stats) = setup_mock_all_validators( + vs, + key_pairs, + true, + block_production_time, + drop_chunks, + !test_doomslug, + 20, + test_doomslug, + vec![true; validators.len()], + vec![false; validators.len()], + true, + Box::new(move |_, _account_id: _, _msg: &PeerManagerMessageRequest| { + (PeerManagerMessageResponse::NetworkResponses(NetworkResponses::NoResponse), true) + }), + ); + *connectors.write().unwrap() = conn; + let block_hash = *genesis_block.hash(); + + let connectors_ = connectors.write().unwrap(); + let iteration = Arc::new(AtomicUsize::new(0)); + let nonce = Arc::new(AtomicUsize::new(1)); + let successful_queries = Arc::new(RwLock::new(HashSet::new())); + let unsuccessful_queries = Arc::new(AtomicUsize::new(0)); + + for i in 0..8 { + let connectors1 = connectors.clone(); + let iteration1 = iteration.clone(); + let nonce1 = nonce.clone(); + let validators1 = validators.clone(); + let successful_queries1 = successful_queries.clone(); + let unsuccessful_queries1 = unsuccessful_queries.clone(); + let balances1 = balances.clone(); + let observed_balances1 = observed_balances.clone(); + let presumable_epoch1 = presumable_epoch.clone(); + let account_id1 = validators[i].clone(); + let block_stats1 = block_stats.clone(); + let actor = &connectors_[account_id_to_shard_id(&validators[i], 8) as usize + + *presumable_epoch.read().unwrap() * 8] + .view_client_actor; + let actor = actor.send( + Query::new( + BlockReference::latest(), + QueryRequest::ViewAccount { account_id: validators[i].clone() }, + ) + .with_span_context(), + ); + let actor = actor.then(move |x| { + test_cross_shard_tx_callback( + x, + account_id1, + connectors1, + iteration1, + nonce1, + validators1, + successful_queries1, + unsuccessful_queries1, + balances1, + observed_balances1, + presumable_epoch1, + num_iters, + block_hash, + block_stats1, + min_ratio, + max_ratio, + ); + future::ready(()) + }); + actix::spawn(actor); + } + + unc_network::test_utils::wait_or_panic(if rotate_validators { + 1000 * 60 * 80 + } else { + 1000 * 60 * 45 + }); + }); +} + +/// Doesn't drop chunks, disabled doomslug, no validator rotation (each epoch +/// has the same set of validators). +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_cross_shard_tx() { + test_cross_shard_tx_common(64, false, false, false, 70, Some(2.3), None); +} + +/// Same as above, but doomslug is enabled. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_cross_shard_tx_doomslug() { + test_cross_shard_tx_common(64, false, false, true, 200, None, Some(1.5)); +} + +/// Same as the first one but the chunks are sometimes dropped. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_cross_shard_tx_drop_chunks() { + test_cross_shard_tx_common(64, false, true, false, 250, None, None); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_cross_shard_tx_8_iterations() { + test_cross_shard_tx_common(8, false, false, false, 200, Some(2.4), None); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_cross_shard_tx_8_iterations_drop_chunks() { + test_cross_shard_tx_common(8, false, true, false, 200, Some(2.4), None); +} + +/// The next two tests are the same as the first one, but with validator +/// rotation. The two versions of the test have slightly different block +/// production times, and different number of iterations we expect to finish in +/// the allocated time. (the one with lower block production time is expected to +/// finish fewer because it has higher forkfulness). +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_cross_shard_tx_with_validator_rotation_1() { + test_cross_shard_tx_common(8, true, false, false, 220, Some(2.4), None); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_cross_shard_tx_with_validator_rotation_2() { + test_cross_shard_tx_common(24, true, false, false, 400, Some(2.4), None); +} diff --git a/chain/client/src/tests/doomslug.rs b/chain/client/src/tests/doomslug.rs new file mode 100644 index 000000000..723716c3b --- /dev/null +++ b/chain/client/src/tests/doomslug.rs @@ -0,0 +1,34 @@ +use crate::test_utils::TestEnv; +use unc_chain::{ChainGenesis, Provenance}; +use unc_crypto::KeyType; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::block::{Approval, ApprovalType}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::validator_signer::InMemoryValidatorSigner; + +/// This file contains tests that test the interaction of client and doomslug, including how client handles approvals, etc. +/// It does not include the unit tests for the Doomslug class. That is located in chain/chain/src/doomslug.rs + +// This tests the scenario that if the chain switch back and forth in between two forks, client code +// can process the skip messages correctly and send it to doomslug. Specifically, it tests the following +// case: +// existing chain looks like 0 - 1 +// \ 2 +// test that if the node receives Skip(2, 4), it can process it successfully. +#[test] +fn test_processing_skips_on_forks() { + init_test_logger(); + + let mut env = + TestEnv::builder(ChainGenesis::test()).clients_count(2).validator_seats(2).build(); + let b1 = env.clients[1].produce_block(1).unwrap().unwrap(); + let b2 = env.clients[0].produce_block(2).unwrap().unwrap(); + assert_eq!(b1.header().prev_hash(), b2.header().prev_hash()); + env.process_block(1, b1, Provenance::NONE); + env.process_block(1, b2, Provenance::NONE); + let validator_signer = + InMemoryValidatorSigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let approval = Approval::new(CryptoHash::default(), 1, 3, &validator_signer); + env.clients[1].collect_block_approval(&approval, ApprovalType::SelfApproval); + assert!(!env.clients[1].doomslug.approval_status_at_height(&3).approvals.is_empty()); +} diff --git a/chain/client/src/tests/maintenance_windows.rs b/chain/client/src/tests/maintenance_windows.rs new file mode 100644 index 000000000..cfd411ef7 --- /dev/null +++ b/chain/client/src/tests/maintenance_windows.rs @@ -0,0 +1,60 @@ +use crate::test_utils::setup_no_network; +use actix::System; +use futures::{future, FutureExt}; +use unc_actix_test_utils::run_actix; +use unc_client_primitives::types::GetMaintenanceWindows; + +use unc_o11y::testonly::init_test_logger; +use unc_o11y::WithSpanContextExt; + +/// get maintenance window from view client +#[test] +fn test_get_maintenance_windows_for_validator() { + init_test_logger(); + run_actix(async { + let actor_handles = setup_no_network( + vec!["test".parse().unwrap(), "other".parse().unwrap()], + "other".parse().unwrap(), + true, + true, + ); + let actor = actor_handles.view_client_actor.send( + GetMaintenanceWindows { account_id: "test".parse().unwrap() }.with_span_context(), + ); + + // block_height: 0 bp: test cps: [AccountId("test")] + // block_height: 1 bp: validator cps: [AccountId("validator")] + // block_height: 2 bp: test cps: [AccountId("test")] + // block_height: 3 bp: validator cps: [AccountId("validator")] + // block_height: 4 bp: test cps: [AccountId("test")] + // block_height: 5 bp: validator cps: [AccountId("validator")] + // block_height: 6 bp: test cps: [AccountId("test")] + // block_height: 7 bp: validator cps: [AccountId("validator")] + // block_height: 8 bp: test cps: [AccountId("test")] + // block_height: 9 bp: validator cps: [AccountId("validator")] + let actor = actor.then(|res| { + assert_eq!(res.unwrap().unwrap(), vec![1..2, 3..4, 5..6, 7..8, 9..10]); + System::current().stop(); + future::ready(()) + }); + actix::spawn(actor); + }); +} + +#[test] +fn test_get_maintenance_windows_for_not_validator() { + init_test_logger(); + run_actix(async { + let actor_handles = + setup_no_network(vec!["test".parse().unwrap()], "other".parse().unwrap(), true, true); + let actor = actor_handles.view_client_actor.send( + GetMaintenanceWindows { account_id: "alice".parse().unwrap() }.with_span_context(), + ); + let actor = actor.then(|res| { + assert_eq!(res.unwrap().unwrap(), vec![0..10]); + System::current().stop(); + future::ready(()) + }); + actix::spawn(actor); + }); +} diff --git a/chain/client/src/tests/mod.rs b/chain/client/src/tests/mod.rs new file mode 100644 index 000000000..3b45a4c05 --- /dev/null +++ b/chain/client/src/tests/mod.rs @@ -0,0 +1,9 @@ +mod bug_repros; +mod catching_up; +mod chunks_management; +mod consensus; +mod cross_shard_tx; +mod doomslug; +mod maintenance_windows; +mod process_blocks; +mod query_client; diff --git a/chain/client/src/tests/process_blocks.rs b/chain/client/src/tests/process_blocks.rs new file mode 100644 index 000000000..fd9c4cff3 --- /dev/null +++ b/chain/client/src/tests/process_blocks.rs @@ -0,0 +1,144 @@ +use crate::test_utils::TestEnv; +use assert_matches::assert_matches; +use unc_chain::{test_utils, ChainGenesis, Provenance}; +use unc_crypto::vrf::Value; +use unc_crypto::{KeyType, PublicKey, Signature}; +use unc_network::types::{NetworkRequests, PeerManagerMessageRequest}; +use unc_primitives::block::Block; +use unc_primitives::network::PeerId; +use unc_primitives::sharding::ShardChunkHeader; +use unc_primitives::sharding::ShardChunkHeaderV3; +use unc_primitives::test_utils::create_test_signer; +use unc_primitives::types::validator_stake::ValidatorStake; +use unc_primitives::utils::MaybeValidated; +use std::sync::Arc; + +/// Only process one block per height +/// Test that if a node receives two blocks at the same height, it doesn't process the second one +/// if the second block is not requested +#[test] +fn test_not_process_height_twice() { + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + let block = env.clients[0].produce_block(1).unwrap().unwrap(); + // modify the block and resign it + let mut duplicate_block = block.clone(); + env.process_block(0, block, Provenance::PRODUCED); + let validator_signer = create_test_signer("test0"); + + let proposals = + vec![ValidatorStake::new("test1".parse().unwrap(), PublicKey::empty(KeyType::ED25519), 0)]; + duplicate_block.mut_header().get_mut().inner_rest.prev_validator_proposals = proposals; + duplicate_block.mut_header().resign(&validator_signer); + let dup_block_hash = *duplicate_block.hash(); + // we should have dropped the block before we even tried to process it, so the result should be ok + env.clients[0] + .receive_block_impl( + duplicate_block, + PeerId::new(PublicKey::empty(KeyType::ED25519)), + false, + Arc::new(|_| {}), + ) + .unwrap(); + // check that the second block is not being processed + assert!(!test_utils::is_block_in_processing(&env.clients[0].chain, &dup_block_hash)); + // check that we didn't rebroadcast the second block + while let Some(msg) = env.network_adapters[0].pop() { + assert!(!matches!( + msg, + PeerManagerMessageRequest::NetworkRequests(NetworkRequests::Block { .. }) + )); + } +} + +/// Test that if a block contains chunks with invalid shard_ids, the client will return error. +#[test] +fn test_bad_shard_id() { + let mut env = TestEnv::builder(ChainGenesis::test()).num_shards(4).build(); + let prev_block = env.clients[0].produce_block(1).unwrap().unwrap(); + env.process_block(0, prev_block, Provenance::PRODUCED); + let mut block = env.clients[0].produce_block(2).unwrap().unwrap(); // modify the block and resign it + let validator_signer = create_test_signer("test0"); + let mut chunks: Vec<_> = block.chunks().iter().cloned().collect(); + // modify chunk 0 to have shard_id 1 + let chunk = chunks.get(0).unwrap(); + let outgoing_receipts_root = chunks.get(1).unwrap().prev_outgoing_receipts_root(); + let mut modified_chunk = ShardChunkHeaderV3::new( + *chunk.prev_block_hash(), + chunk.prev_state_root(), + chunk.prev_outcome_root(), + chunk.encoded_merkle_root(), + chunk.encoded_length(), + 2, + 1, + chunk.prev_gas_used(), + chunk.gas_limit(), + chunk.prev_balance_burnt(), + outgoing_receipts_root, + chunk.tx_root(), + chunk.prev_validator_proposals().collect(), + &validator_signer, + ); + modified_chunk.height_included = 2; + chunks[0] = ShardChunkHeader::V3(modified_chunk); + block.mut_header().get_mut().inner_rest.chunk_headers_root = + Block::compute_chunk_headers_root(&chunks).0; + block.mut_header().get_mut().inner_rest.prev_chunk_outgoing_receipts_root = + Block::compute_chunk_prev_outgoing_receipts_root(&chunks); + block.set_chunks(chunks); + block.mut_header().get_mut().inner_rest.block_body_hash = + block.compute_block_body_hash().unwrap(); + block.mut_header().resign(&validator_signer); + + let err = env.clients[0] + .process_block_test(MaybeValidated::from(block), Provenance::NONE) + .unwrap_err(); + assert_matches!(err, unc_chain::Error::InvalidShardId(1)); +} + +/// Test that if a block's content (vrf_value) is corrupted, the invalid block will not affect the node's block processing +#[test] +fn test_bad_block_content_vrf() { + let mut env = TestEnv::builder(ChainGenesis::test()).num_shards(4).build(); + let prev_block = env.clients[0].produce_block(1).unwrap().unwrap(); + env.process_block(0, prev_block, Provenance::PRODUCED); + let block = env.clients[0].produce_block(2).unwrap().unwrap(); + let mut bad_block = block.clone(); + bad_block.get_mut().body.vrf_value = Value([0u8; 32]); + + let err = env.clients[0] + .receive_block_impl( + bad_block, + PeerId::new(PublicKey::empty(KeyType::ED25519)), + false, + Arc::new(|_| {}), + ) + .unwrap_err(); + assert_matches!(err, unc_chain::Error::InvalidSignature); + + let _ = + env.clients[0].process_block_test(MaybeValidated::from(block), Provenance::NONE).unwrap(); +} + +/// Test that if a block's signature is corrupted, the invalid block will not affect the node's block processing +#[test] +fn test_bad_block_signature() { + let mut env = TestEnv::builder(ChainGenesis::test()).num_shards(4).build(); + let prev_block = env.clients[0].produce_block(1).unwrap().unwrap(); + env.process_block(0, prev_block, Provenance::PRODUCED); + let block = env.clients[0].produce_block(2).unwrap().unwrap(); + let mut bad_block = block.clone(); + bad_block.mut_header().get_mut().signature = Signature::default(); + + let err = env.clients[0] + .receive_block_impl( + bad_block, + PeerId::new(PublicKey::empty(KeyType::ED25519)), + false, + Arc::new(|_| {}), + ) + .unwrap_err(); + assert_matches!(err, unc_chain::Error::InvalidSignature); + + let _ = + env.clients[0].process_block_test(MaybeValidated::from(block), Provenance::NONE).unwrap(); +} diff --git a/chain/client/src/tests/query_client.rs b/chain/client/src/tests/query_client.rs new file mode 100644 index 000000000..d0441acd0 --- /dev/null +++ b/chain/client/src/tests/query_client.rs @@ -0,0 +1,392 @@ +use actix::System; +use futures::{future, FutureExt}; +use unc_chain::test_utils::ValidatorSchedule; +use unc_primitives::merkle::PartialMerkleTree; +use unc_primitives::test_utils::create_test_signer; +use std::sync::Arc; +use std::time::Duration; + +use crate::adapter::{BlockResponse, ProcessTxRequest, ProcessTxResponse, StateRequestHeader}; +use crate::test_utils::{setup_mock_all_validators, setup_no_network, setup_only_view}; +use crate::{ + GetBlock, GetBlockWithMerkleTree, GetExecutionOutcomesForBlock, Query, QueryError, Status, + TxStatus, +}; +use unc_actix_test_utils::run_actix; +use unc_chain_configs::DEFAULT_GC_NUM_EPOCHS_TO_KEEP; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_network::test_utils::MockPeerManagerAdapter; +use unc_network::types::PeerInfo; +use unc_network::types::{ + NetworkRequests, NetworkResponses, PeerManagerMessageRequest, PeerManagerMessageResponse, +}; + +use chrono::Utc; +use unc_o11y::testonly::init_test_logger; +use unc_o11y::WithSpanContextExt; +use unc_primitives::block::{Block, BlockHeader}; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{BlockId, BlockReference, EpochId}; +use unc_primitives::utils::to_timestamp; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_primitives::views::{QueryRequest, QueryResponseKind}; +use num_rational::Ratio; + +/// Query account from view client +#[test] +fn query_client() { + init_test_logger(); + run_actix(async { + let actor_handles = + setup_no_network(vec!["test".parse().unwrap()], "other".parse().unwrap(), true, true); + let actor = actor_handles.view_client_actor.send( + Query::new( + BlockReference::latest(), + QueryRequest::ViewAccount { account_id: "test".parse().unwrap() }, + ) + .with_span_context(), + ); + let actor = actor.then(|res| { + match res.unwrap().unwrap().kind { + QueryResponseKind::ViewAccount(_) => (), + _ => panic!("Invalid response"), + } + System::current().stop(); + future::ready(()) + }); + actix::spawn(actor); + }); +} + +/// When we receive health check and the latest block's timestamp is in the future, the client +/// should not crash. +#[test] +fn query_status_not_crash() { + init_test_logger(); + run_actix(async { + let actor_handles = + setup_no_network(vec!["test".parse().unwrap()], "other".parse().unwrap(), true, false); + let signer = create_test_signer("test"); + let actor = actor_handles + .view_client_actor + .send(GetBlockWithMerkleTree::latest().with_span_context()); + let actor = actor.then(move |res| { + let (block, block_merkle_tree) = res.unwrap().unwrap(); + let mut block_merkle_tree = PartialMerkleTree::clone(&block_merkle_tree); + let header: BlockHeader = block.header.clone().into(); + block_merkle_tree.insert(*header.hash()); + let mut next_block = Block::produce( + PROTOCOL_VERSION, + PROTOCOL_VERSION, + &header, + block.header.height + 1, + header.block_ordinal() + 1, + block.chunks.into_iter().map(|c| c.into()).collect(), + EpochId(block.header.next_epoch_id), + EpochId(block.header.hash), + None, + vec![], + Ratio::from_integer(0), + 0, + 100, + None, + vec![], + vec![], + &signer, + block.header.next_bp_hash, + block_merkle_tree.root(), + None, + ); + next_block.mut_header().get_mut().inner_lite.timestamp = + to_timestamp(next_block.header().timestamp() + chrono::Duration::seconds(60)); + next_block.mut_header().resign(&signer); + + actix::spawn( + actor_handles + .client_actor + .send( + BlockResponse { + block: next_block, + peer_id: PeerInfo::random().id, + was_requested: false, + } + .with_span_context(), + ) + .then(move |_| { + actix::spawn( + actor_handles + .client_actor + .send( + Status { is_health_check: true, detailed: false } + .with_span_context(), + ) + .then(move |_| { + System::current().stop(); + future::ready(()) + }), + ); + future::ready(()) + }), + ); + future::ready(()) + }); + actix::spawn(actor); + unc_network::test_utils::wait_or_panic(5000); + }); +} + +#[test] +fn test_execution_outcome_for_chunk() { + init_test_logger(); + run_actix(async { + let actor_handles = + setup_no_network(vec!["test".parse().unwrap()], "test".parse().unwrap(), true, false); + let signer = InMemorySigner::from_seed("test".parse().unwrap(), KeyType::ED25519, "test"); + + actix::spawn(async move { + let block_hash = actor_handles + .view_client_actor + .send(GetBlock::latest().with_span_context()) + .await + .unwrap() + .unwrap() + .header + .hash; + + let transaction = SignedTransaction::send_money( + 1, + "test".parse().unwrap(), + "near".parse().unwrap(), + &signer, + 10, + block_hash, + ); + let tx_hash = transaction.get_hash(); + let res = actor_handles + .client_actor + .send( + ProcessTxRequest { transaction, is_forwarded: false, check_only: false } + .with_span_context(), + ) + .await + .unwrap(); + assert!(matches!(res, ProcessTxResponse::ValidTx)); + + actix::clock::sleep(Duration::from_millis(500)).await; + let block_hash = actor_handles + .view_client_actor + .send( + TxStatus { + tx_hash, + signer_account_id: "test".parse().unwrap(), + fetch_receipt: false, + } + .with_span_context(), + ) + .await + .unwrap() + .unwrap() + .into_outcome() + .unwrap() + .transaction_outcome + .block_hash; + + let mut execution_outcomes_in_block = actor_handles + .view_client_actor + .send(GetExecutionOutcomesForBlock { block_hash }.with_span_context()) + .await + .unwrap() + .unwrap(); + assert_eq!(execution_outcomes_in_block.len(), 1); + let outcomes = execution_outcomes_in_block.remove(&0).unwrap(); + assert_eq!(outcomes[0].id, tx_hash); + System::current().stop(); + }); + unc_network::test_utils::wait_or_panic(5000); + }); +} + +#[test] +fn test_state_request() { + run_actix(async { + let vs = + ValidatorSchedule::new().block_producers_per_epoch(vec![vec!["test".parse().unwrap()]]); + let view_client = setup_only_view( + vs, + 10000000, + "test".parse().unwrap(), + true, + 200, + 400, + false, + true, + false, + true, + Arc::new(MockPeerManagerAdapter::default()).into(), + 100, + Utc::now(), + ); + actix::spawn(async move { + actix::clock::sleep(Duration::from_millis(500)).await; + let block_hash = view_client + .send(GetBlock(BlockReference::BlockId(BlockId::Height(0))).with_span_context()) + .await + .unwrap() + .unwrap() + .header + .hash; + for _ in 0..30 { + let res = view_client + .send( + StateRequestHeader { shard_id: 0, sync_hash: block_hash } + .with_span_context(), + ) + .await + .unwrap(); + assert!(res.is_some()); + } + + // immediately query again, should be rejected + let res = view_client + .send(StateRequestHeader { shard_id: 0, sync_hash: block_hash }.with_span_context()) + .await + .unwrap(); + assert!(res.is_none()); + actix::clock::sleep(Duration::from_secs(40)).await; + let res = view_client + .send(StateRequestHeader { shard_id: 0, sync_hash: block_hash }.with_span_context()) + .await + .unwrap(); + assert!(res.is_some()); + System::current().stop(); + }); + unc_network::test_utils::wait_or_panic(50000); + }); +} + +#[test] +/// When querying data which was garbage collected on a node it returns +/// `QueryError::GarbageCollectedBlock`. +fn test_garbage_collection() { + init_test_logger(); + run_actix(async { + let block_prod_time = 100; + let epoch_length = 5; + let target_height = epoch_length * (DEFAULT_GC_NUM_EPOCHS_TO_KEEP + 1); + let vs = ValidatorSchedule::new().num_shards(2).block_producers_per_epoch(vec![vec![ + "test1".parse().unwrap(), + "test2".parse().unwrap(), + ]]); + + setup_mock_all_validators( + vs, + vec![PeerInfo::random(), PeerInfo::random()], + true, + block_prod_time, + false, + false, + epoch_length, + true, + vec![false, true], // first validator non-archival, second archival + vec![true, true], + true, + Box::new( + move |conns, + _, + msg: &PeerManagerMessageRequest| + -> (PeerManagerMessageResponse, bool) { + if let NetworkRequests::Block { block } = msg.as_network_requests_ref() { + if block.header().height() > target_height { + let view_client_non_archival = &conns[0].view_client_actor; + let view_client_archival = &conns[1].view_client_actor; + let mut tests = vec![]; + + // Recent data is present on all nodes (archival or not). + let prev_height = block.header().prev_height().unwrap(); + for actor_handles in conns.iter() { + tests.push(actix::spawn( + actor_handles + .view_client_actor + .send( + Query::new( + BlockReference::BlockId(BlockId::Height( + prev_height, + )), + QueryRequest::ViewAccount { + account_id: "test1".parse().unwrap(), + }, + ) + .with_span_context(), + ) + .then(move |res| { + let res = res.unwrap().unwrap(); + match res.kind { + QueryResponseKind::ViewAccount(_) => (), + _ => panic!("Invalid response"), + } + futures::future::ready(()) + }), + )); + } + + // On non-archival node old data is garbage collected. + tests.push(actix::spawn( + view_client_non_archival + .send( + Query::new( + BlockReference::BlockId(BlockId::Height(1)), + QueryRequest::ViewAccount { + account_id: "test1".parse().unwrap(), + }, + ) + .with_span_context(), + ) + .then(move |res| { + let res = res.unwrap(); + match res { + Err(err) => assert!(matches!( + err, + QueryError::GarbageCollectedBlock { .. } + )), + Ok(_) => panic!("Unexpected Ok variant"), + } + futures::future::ready(()) + }), + )); + + // On archival node old data is _not_ garbage collected. + tests.push(actix::spawn( + view_client_archival + .send( + Query::new( + BlockReference::BlockId(BlockId::Height(1)), + QueryRequest::ViewAccount { + account_id: "test1".parse().unwrap(), + }, + ) + .with_span_context(), + ) + .then(move |res| { + let res = res.unwrap().unwrap(); + match res.kind { + QueryResponseKind::ViewAccount(_) => (), + _ => panic!("Invalid response"), + } + futures::future::ready(()) + }), + )); + + actix::spawn(futures::future::join_all(tests).then(|_| { + System::current().stop(); + futures::future::ready(()) + })); + } + } + (NetworkResponses::NoResponse.into(), true) + }, + ), + ); + + unc_network::test_utils::wait_or_panic(block_prod_time * target_height * 2 + 2000); + }) +} diff --git a/chain/client/src/view_client.rs b/chain/client/src/view_client.rs new file mode 100644 index 000000000..0ccb3b056 --- /dev/null +++ b/chain/client/src/view_client.rs @@ -0,0 +1,1662 @@ +//! Readonly view of the chain and state of the database. +//! Useful for querying from RPC. + +use crate::adapter::{AnnounceAccountRequest, BlockHeadersRequest, BlockRequest, ProviderRequest, StateRequestHeader, StateRequestPart, StateResponse, TxStatusRequest, TxStatusResponse}; +use crate::{ + metrics, sync, GetChunk, GetExecutionOutcomeResponse, GetNextLightClientBlock, GetStateChanges, + GetStateChangesInBlock, GetValidatorInfo, GetValidatorOrdered, +}; +use actix::{Actor, Addr, Handler, SyncArbiter, SyncContext}; +use unc_async::messaging::CanSend; +use unc_chain::types::{RuntimeAdapter, Tip}; +use unc_chain::{ + get_epoch_block_producers_view, Chain, ChainGenesis, ChainStoreAccess, DoomslugThresholdMode, +}; +use unc_chain_configs::{ClientConfig, ProtocolConfigView}; +use unc_chain_primitives::error::EpochErrorResultToChainError; +use unc_client_primitives::types::{Error, GetBlock, GetBlockError, GetBlockProof, GetBlockProofError, GetBlockProofResponse, GetBlockWithMerkleTree, GetChunkError, GetExecutionOutcome, GetExecutionOutcomeError, GetExecutionOutcomesForBlock, GetGasPrice, GetGasPriceError, GetMaintenanceWindows, GetMaintenanceWindowsError, GetNextLightClientBlockError, GetProtocolConfig, GetProtocolConfigError, GetProvider, GetProviderError, MinerChipsList, MinerChipsListError, GetReceipt, GetReceiptError, GetSplitStorageInfo, GetSplitStorageInfoError, GetStateChangesError, GetStateChangesWithCauseInBlock, GetStateChangesWithCauseInBlockForTrackedShards, GetValidatorInfoError, Query, QueryError, TxStatus, TxStatusError}; +use unc_epoch_manager::shard_tracker::ShardTracker; +use unc_epoch_manager::EpochManagerAdapter; +use unc_network::types::{ + NetworkRequests, PeerManagerAdapter, PeerManagerMessageRequest, ReasonForBan, + StateResponseInfo, StateResponseInfoV2, +}; +use unc_o11y::{handler_debug_span, OpenTelemetrySpanExt, WithSpanContext, WithSpanContextExt}; +use unc_performance_metrics_macros::perf; +use unc_primitives::block::{Block, BlockHeader}; +use unc_primitives::epoch_manager::epoch_info::EpochInfo; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::{merklize, PartialMerkleTree}; +use unc_primitives::network::AnnounceAccount; +use unc_primitives::receipt::Receipt; +use unc_primitives::sharding::ShardChunk; +use unc_primitives::state_sync::{ + ShardStateSyncResponse, ShardStateSyncResponseHeader, ShardStateSyncResponseV3, +}; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::types::{AccountId, BlockHeight, BlockId, BlockReference, EpochReference, Finality, MaybeBlockId, ShardId, SyncCheckpoint, TransactionOrReceiptId, ValidatorInfoIdentifier}; +use unc_primitives::views::{BlockView, ChipView, ChunkView, EpochValidatorInfo, ExecutionOutcomeWithIdView, ExecutionStatusView, FinalExecutionOutcomeView, FinalExecutionOutcomeViewEnum, GasPriceView, LightClientBlockView, MaintenanceWindowsView, MinerChipsListView, QueryRequest, QueryResponse, ReceiptView, SplitStorageInfoView, StateChangesKindsView, StateChangesView, TxExecutionStatus, TxStatusView}; +use unc_store::flat::{FlatStorageReadyStatus, FlatStorageStatus}; +use unc_store::{DBCol, COLD_HEAD_KEY, FINAL_HEAD_KEY, HEAD_KEY}; +use std::cmp::Ordering; +use std::collections::{BTreeSet, HashMap, VecDeque}; +use std::hash::Hash; +use std::sync::{Arc, Mutex, RwLock}; +use std::time::{Duration, Instant}; +use tracing::{error, info, warn}; +use unc_primitives::views::validator_power_and_frozen_view::ValidatorPowerAndFrozenView; + +/// Max number of queries that we keep. +const QUERY_REQUEST_LIMIT: usize = 500; +/// Waiting time between requests, in ms +const REQUEST_WAIT_TIME: u64 = 1000; + +const POISONED_LOCK_ERR: &str = "The lock was poisoned."; + +/// Request and response manager across all instances of ViewClientActor. +pub struct ViewClientRequestManager { + /// Transaction query that needs to be forwarded to other shards + pub tx_status_requests: lru::LruCache, + /// Transaction status response + pub tx_status_response: lru::LruCache, + /// Query requests that need to be forwarded to other shards + pub query_requests: lru::LruCache, + /// Query responses from other nodes (can be errors) + pub query_responses: lru::LruCache>, + /// Receipt outcome requests + pub receipt_outcome_requests: lru::LruCache, +} + +/// View client provides currently committed (to the storage) view of the current chain and state. +pub struct ViewClientActor { + pub adv: crate::adversarial::Controls, + + /// Validator account (if present). + validator_account_id: Option, + chain: Chain, + epoch_manager: Arc, + shard_tracker: ShardTracker, + runtime: Arc, + network_adapter: PeerManagerAdapter, + pub config: ClientConfig, + request_manager: Arc>, + state_request_cache: Arc>>, +} + +impl ViewClientRequestManager { + pub fn new() -> Self { + Self { + tx_status_requests: lru::LruCache::new(QUERY_REQUEST_LIMIT), + tx_status_response: lru::LruCache::new(QUERY_REQUEST_LIMIT), + query_requests: lru::LruCache::new(QUERY_REQUEST_LIMIT), + query_responses: lru::LruCache::new(QUERY_REQUEST_LIMIT), + receipt_outcome_requests: lru::LruCache::new(QUERY_REQUEST_LIMIT), + } + } +} + +impl ViewClientActor { + /// Maximum number of state requests allowed per `view_client_throttle_period`. + const MAX_NUM_STATE_REQUESTS: usize = 30; + + pub fn new( + validator_account_id: Option, + chain_genesis: &ChainGenesis, + epoch_manager: Arc, + shard_tracker: ShardTracker, + runtime: Arc, + network_adapter: PeerManagerAdapter, + config: ClientConfig, + request_manager: Arc>, + adv: crate::adversarial::Controls, + ) -> Result { + // TODO: should we create shared ChainStore that is passed to both Client and ViewClient? + let chain = Chain::new_for_view_client( + epoch_manager.clone(), + shard_tracker.clone(), + runtime.clone(), + chain_genesis, + DoomslugThresholdMode::TwoThirds, + config.save_trie_changes, + )?; + Ok(ViewClientActor { + adv, + validator_account_id, + chain, + epoch_manager, + shard_tracker, + runtime, + network_adapter, + config, + request_manager, + state_request_cache: Arc::new(Mutex::new(VecDeque::default())), + }) + } + + fn maybe_block_id_to_block_header( + &self, + block_id: MaybeBlockId, + ) -> Result { + match block_id { + None => { + let block_hash = self.chain.head()?.last_block_hash; + self.chain.get_block_header(&block_hash) + } + Some(BlockId::Height(height)) => self.chain.get_block_header_by_height(height), + Some(BlockId::Hash(block_hash)) => self.chain.get_block_header(&block_hash), + } + } + + fn need_request(key: K, cache: &mut lru::LruCache) -> bool { + let now = StaticClock::instant(); + let need_request = match cache.get(&key) { + Some(time) => now - *time > Duration::from_millis(REQUEST_WAIT_TIME), + None => true, + }; + if need_request { + cache.put(key, now); + } + need_request + } + + fn get_block_hash_by_finality( + &self, + finality: &Finality, + ) -> Result { + match finality { + Finality::None => Ok(self.chain.head()?.last_block_hash), + Finality::DoomSlug => Ok(*self.chain.head_header()?.last_ds_final_block()), + Finality::Final => Ok(self.chain.final_head()?.last_block_hash), + } + } + + /// Returns block header by reference. + /// + /// Returns `None` if the reference is a `SyncCheckpoint::EarliestAvailable` + /// reference and no such block exists yet. This is typically translated by + /// the caller into some form of ‘no sync block’ higher-level error. + fn get_block_header_by_reference( + &self, + reference: &BlockReference, + ) -> Result, unc_chain::Error> { + match reference { + BlockReference::BlockId(BlockId::Height(block_height)) => { + self.chain.get_block_header_by_height(*block_height).map(Some) + } + BlockReference::BlockId(BlockId::Hash(block_hash)) => { + self.chain.get_block_header(block_hash).map(Some) + } + BlockReference::Finality(finality) => self + .get_block_hash_by_finality(finality) + .and_then(|block_hash| self.chain.get_block_header(&block_hash)) + .map(Some), + BlockReference::SyncCheckpoint(SyncCheckpoint::Genesis) => { + Ok(Some(self.chain.genesis().clone())) + } + BlockReference::SyncCheckpoint(SyncCheckpoint::EarliestAvailable) => { + let block_hash = match self.chain.get_earliest_block_hash()? { + Some(block_hash) => block_hash, + None => return Ok(None), + }; + self.chain.get_block_header(&block_hash).map(Some) + } + } + } + + /// Returns block by reference. + /// + /// Returns `None` if the reference is a `SyncCheckpoint::EarliestAvailable` + /// reference and no such block exists yet. This is typically translated by + /// the caller into some form of ‘no sync block’ higher-level error. + fn get_block_by_reference( + &self, + reference: &BlockReference, + ) -> Result, unc_chain::Error> { + match reference { + BlockReference::BlockId(BlockId::Height(block_height)) => { + self.chain.get_block_by_height(*block_height).map(Some) + } + BlockReference::BlockId(BlockId::Hash(block_hash)) => { + self.chain.get_block(block_hash).map(Some) + } + BlockReference::Finality(finality) => self + .get_block_hash_by_finality(finality) + .and_then(|block_hash| self.chain.get_block(&block_hash)) + .map(Some), + BlockReference::SyncCheckpoint(SyncCheckpoint::Genesis) => { + Ok(Some(self.chain.genesis_block().clone())) + } + BlockReference::SyncCheckpoint(SyncCheckpoint::EarliestAvailable) => { + let block_hash = match self.chain.get_earliest_block_hash()? { + Some(block_hash) => block_hash, + None => return Ok(None), + }; + self.chain.get_block(&block_hash).map(Some) + } + } + } + + /// Returns maintenance windows by account. + fn get_maintenance_windows( + &self, + account_id: AccountId, + ) -> Result { + let head = self.chain.head()?; + let epoch_id = self.epoch_manager.get_epoch_id(&head.last_block_hash)?; + let epoch_info: Arc = self.epoch_manager.get_epoch_info(&epoch_id)?; + let shard_ids = self.epoch_manager.shard_ids(&epoch_id)?; + let cur_block_info = self.epoch_manager.get_block_info(&head.last_block_hash)?; + let next_epoch_start_height = + self.epoch_manager.get_epoch_start_height(cur_block_info.hash())? + + self.epoch_manager.get_epoch_config(&epoch_id)?.epoch_length; + + let mut windows: MaintenanceWindowsView = Vec::new(); + let mut start_block_of_window: Option = None; + let last_block_of_epoch = next_epoch_start_height - 1; + + for block_height in head.height..next_epoch_start_height { + let bp = epoch_info.sample_block_producer(block_height); + let bp = epoch_info.get_validator(bp).account_id().clone(); + let cps: Vec = shard_ids + .iter() + .map(|&shard_id| { + let cp = epoch_info.sample_chunk_producer(block_height, shard_id); + let cp = epoch_info.get_validator(cp).account_id().clone(); + cp + }) + .collect(); + if account_id != bp && !cps.iter().any(|a| *a == account_id) { + if let Some(start) = start_block_of_window { + if block_height == last_block_of_epoch { + windows.push(start..block_height + 1); + start_block_of_window = None; + } + } else { + start_block_of_window = Some(block_height); + } + } else if let Some(start) = start_block_of_window { + windows.push(start..block_height); + start_block_of_window = None; + } + } + if let Some(start) = start_block_of_window { + windows.push(start..next_epoch_start_height); + } + Ok(windows) + } + + fn handle_query(&mut self, msg: Query) -> Result { + let header = self.get_block_header_by_reference(&msg.block_reference); + let header = match header { + Ok(Some(header)) => Ok(header), + Ok(None) => Err(QueryError::NoSyncedBlocks), + Err(unc_chain::unc_chain_primitives::Error::DBNotFoundErr(_)) => { + Err(QueryError::UnknownBlock { block_reference: msg.block_reference }) + } + Err(unc_chain::unc_chain_primitives::Error::IOErr(err)) => { + Err(QueryError::InternalError { error_message: err.to_string() }) + } + Err(err) => Err(QueryError::Unreachable { error_message: err.to_string() }), + }?; + + let account_id = match &msg.request { + QueryRequest::ViewAccount { account_id, .. } => account_id, + QueryRequest::ViewState { account_id, .. } => account_id, + QueryRequest::ViewAccessKey { account_id, .. } => account_id, + QueryRequest::ViewAccessKeyList { account_id, .. } => account_id, + QueryRequest::CallFunction { account_id, .. } => account_id, + QueryRequest::ViewCode { account_id, .. } => account_id, + }; + let shard_id = self + .epoch_manager + .account_id_to_shard_id(account_id, header.epoch_id()) + .map_err(|err| QueryError::InternalError { error_message: err.to_string() })?; + let shard_uid = self + .epoch_manager + .shard_id_to_uid(shard_id, header.epoch_id()) + .map_err(|err| QueryError::InternalError { error_message: err.to_string() })?; + + let tip = self.chain.head(); + let chunk_extra = + self.chain.get_chunk_extra(header.hash(), &shard_uid).map_err(|err| match err { + unc_chain::unc_chain_primitives::Error::DBNotFoundErr(_) => match tip { + Ok(tip) => { + let gc_stop_height = self.runtime.get_gc_stop_height(&tip.last_block_hash); + if !self.config.archive && header.height() < gc_stop_height { + QueryError::GarbageCollectedBlock { + block_height: header.height(), + block_hash: *header.hash(), + } + } else { + QueryError::UnavailableShard { requested_shard_id: shard_id } + } + } + Err(err) => QueryError::InternalError { error_message: err.to_string() }, + }, + unc_chain::unc_chain_primitives::Error::IOErr(error) => { + QueryError::InternalError { error_message: error.to_string() } + } + _ => QueryError::Unreachable { error_message: err.to_string() }, + })?; + + let state_root = chunk_extra.state_root(); + match self.runtime.query( + shard_uid, + state_root, + header.height(), + header.raw_timestamp(), + header.prev_hash(), + header.hash(), + header.epoch_id(), + &msg.request, + ) { + Ok(query_response) => Ok(query_response), + Err(query_error) => Err(match query_error { + unc_chain::unc_chain_primitives::error::QueryError::InternalError { + error_message, + .. + } => QueryError::InternalError { error_message }, + unc_chain::unc_chain_primitives::error::QueryError::InvalidAccount { + requested_account_id, + block_height, + block_hash, + } => QueryError::InvalidAccount { requested_account_id, block_height, block_hash }, + unc_chain::unc_chain_primitives::error::QueryError::UnknownAccount { + requested_account_id, + block_height, + block_hash, + } => QueryError::UnknownAccount { requested_account_id, block_height, block_hash }, + unc_chain::unc_chain_primitives::error::QueryError::NoContractCode { + contract_account_id, + block_height, + block_hash, + } => QueryError::NoContractCode { contract_account_id, block_height, block_hash }, + unc_chain::unc_chain_primitives::error::QueryError::UnknownAccessKey { + public_key, + block_height, + block_hash, + } => QueryError::UnknownAccessKey { public_key, block_height, block_hash }, + unc_chain::unc_chain_primitives::error::QueryError::ContractExecutionError { + error_message, + block_hash, + block_height, + } => QueryError::ContractExecutionError { + vm_error: error_message, + block_height, + block_hash, + }, + unc_chain::unc_chain_primitives::error::QueryError::TooLargeContractState { + requested_account_id, + block_height, + block_hash, + } => QueryError::TooLargeContractState { + contract_account_id: requested_account_id, + block_height, + block_hash, + }, + }), + } + } + + // Return the lowest status the node can proof + fn get_tx_execution_status( + &self, + execution_outcome: &FinalExecutionOutcomeView, + ) -> Result { + if execution_outcome.transaction_outcome.outcome.status == ExecutionStatusView::Unknown { + return Ok(TxExecutionStatus::None); + } + + if let Err(_) = self.chain.check_blocks_final_and_canonical(&[self + .chain + .get_block_header(&execution_outcome.transaction_outcome.block_hash)?]) + { + return Ok(TxExecutionStatus::Included); + } + + if execution_outcome + .receipts_outcome + .iter() + .any(|e| e.outcome.status == ExecutionStatusView::Unknown) + { + return Ok(TxExecutionStatus::IncludedFinal); + } + + let block_hashes: BTreeSet = + execution_outcome.receipts_outcome.iter().map(|e| e.block_hash).collect(); + let mut headers = vec![]; + for block_hash in block_hashes { + headers.push(self.chain.get_block_header(&block_hash)?); + } + // We can't sort and check only the last block; + // previous blocks may be not in the canonical chain + Ok(match self.chain.check_blocks_final_and_canonical(&headers) { + Err(_) => TxExecutionStatus::Executed, + Ok(_) => TxExecutionStatus::Final, + }) + } + + fn get_tx_status( + &mut self, + tx_hash: CryptoHash, + signer_account_id: AccountId, + fetch_receipt: bool, + ) -> Result { + { + // TODO(telezhnaya): take into account `fetch_receipt()` + // https://github.com/utnet-org/utility/issues/9545 + let mut request_manager = self.request_manager.write().expect(POISONED_LOCK_ERR); + if let Some(res) = request_manager.tx_status_response.pop(&tx_hash) { + request_manager.tx_status_requests.pop(&tx_hash); + let status = self.get_tx_execution_status(&res)?; + return Ok(TxStatusView { + execution_outcome: Some(FinalExecutionOutcomeViewEnum::FinalExecutionOutcome( + res, + )), + status, + }); + } + } + + let head = self.chain.head()?; + let target_shard_id = self + .epoch_manager + .account_id_to_shard_id(&signer_account_id, &head.epoch_id) + .map_err(|err| TxStatusError::InternalError(err.to_string()))?; + // Check if we are tracking this shard. + if self.shard_tracker.care_about_shard( + self.validator_account_id.as_ref(), + &head.prev_block_hash, + target_shard_id, + true, + ) { + match self.chain.get_final_transaction_result(&tx_hash) { + Ok(tx_result) => { + let status = self.get_tx_execution_status(&tx_result)?; + let res = if fetch_receipt { + let final_result = + self.chain.get_final_transaction_result_with_receipt(tx_result)?; + FinalExecutionOutcomeViewEnum::FinalExecutionOutcomeWithReceipt( + final_result, + ) + } else { + FinalExecutionOutcomeViewEnum::FinalExecutionOutcome(tx_result) + }; + Ok(TxStatusView { execution_outcome: Some(res), status }) + } + Err(unc_chain::Error::DBNotFoundErr(_)) => { + if self.chain.get_execution_outcome(&tx_hash).is_ok() { + Ok(TxStatusView { + execution_outcome: None, + status: TxExecutionStatus::None, + }) + } else { + Err(TxStatusError::MissingTransaction(tx_hash)) + } + } + Err(err) => { + warn!(target: "client", ?err, "Error trying to get transaction result"); + Err(err.into()) + } + } + } else { + let mut request_manager = self.request_manager.write().expect(POISONED_LOCK_ERR); + if Self::need_request(tx_hash, &mut request_manager.tx_status_requests) { + let target_shard_id = self + .epoch_manager + .account_id_to_shard_id(&signer_account_id, &head.epoch_id) + .map_err(|err| TxStatusError::InternalError(err.to_string()))?; + let validator = self + .epoch_manager + .get_chunk_producer( + &head.epoch_id, + head.height + self.config.tx_routing_height_horizon - 1, + target_shard_id, + ) + .map_err(|err| TxStatusError::ChainError(err.into()))?; + + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests( + NetworkRequests::TxStatus(validator, signer_account_id, tx_hash), + )); + } + Ok(TxStatusView { execution_outcome: None, status: TxExecutionStatus::None }) + } + } + + fn retrieve_headers( + &mut self, + hashes: Vec, + ) -> Result, unc_chain::Error> { + self.chain.retrieve_headers(hashes, sync::header::MAX_BLOCK_HEADERS, None) + } + + fn check_signature_account_announce( + &self, + announce_account: &AnnounceAccount, + ) -> Result { + let announce_hash = announce_account.hash(); + let head = self.chain.head()?; + + self.epoch_manager + .verify_validator_signature( + &announce_account.epoch_id, + &head.last_block_hash, + &announce_account.account_id, + announce_hash.as_ref(), + &announce_account.signature, + ) + .map_err(|e| e.into()) + } + + /// Returns true if this request needs to be **dropped** due to exceeding a + /// rate limit of state sync requests. + fn throttle_state_sync_request(&self) -> bool { + let mut cache = self.state_request_cache.lock().expect(POISONED_LOCK_ERR); + let now = StaticClock::instant(); + while let Some(&instant) = cache.front() { + if now.saturating_duration_since(instant) > self.config.view_client_throttle_period { + cache.pop_front(); + } else { + // Assume that time is linear. While in different threads there might be some small differences, + // it should not matter in practice. + break; + } + } + if cache.len() >= Self::MAX_NUM_STATE_REQUESTS { + return true; + } + cache.push_back(now); + false + } + + fn has_state_snapshot(&self, sync_hash: &CryptoHash, shard_id: ShardId) -> Result { + let header = self.chain.get_block_header(sync_hash)?; + let prev_header = self.chain.get_block_header(header.prev_hash())?; + let prev_epoch_id = prev_header.epoch_id(); + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, prev_epoch_id)?; + let sync_prev_prev_hash = prev_header.prev_hash(); + let status = self + .runtime + .get_tries() + .get_snapshot_flat_storage_status(*sync_prev_prev_hash, shard_uid) + .map_err(|err| Error::Other(err.to_string()))?; + match status { + FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head }) => { + let flat_head_header = self.chain.get_block_header(&flat_head.hash)?; + let flat_head_epoch_id = flat_head_header.epoch_id(); + Ok(flat_head_epoch_id == prev_epoch_id) + } + _ => Ok(false), + } + } +} + +impl Actor for ViewClientActor { + type Context = SyncContext; +} + +impl Handler> for ViewClientActor { + type Result = Result; + + #[perf] + fn handle(&mut self, msg: WithSpanContext, _: &mut Self::Context) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME.with_label_values(&["Query"]).start_timer(); + self.handle_query(msg) + } +} + +/// Handles retrieving block provider from the chain. +impl Handler> for ViewClientActor { + type Result = Result; + #[perf] + fn handle(&mut self, msg: WithSpanContext, _: &mut Self::Context) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let epoch_id= msg.0; + let height = msg.1; + let block_author = self + .epoch_manager + .get_block_producer(&epoch_id, height) + .into_chain_error()?; + Ok(block_author) + } +} + +/// Handles retrieving miner chips list from the chain. +impl Handler> for ViewClientActor { + type Result = Result; + #[perf] + fn handle(&mut self, msg: WithSpanContext, _: &mut Self::Context) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let account_id = msg.0; + let chip0 = ChipView { + power: 5000000000, + serial_number: String::from("serialnumberabc000"), + bus_id: String::from("busidabc000"), + p2: String::from("p2abc000"), + public_key: String::from("publickeyabc000"), + p2_size: 120, + public_key_size: 235, + }; + let mut chips = Vec::new(); + chips.push(chip0); + Ok(MinerChipsListView{ + account_id, + chips, + }) + } +} + +/// Handles retrieving block from the chain. +impl Handler> for ViewClientActor { + type Result = Result; + + #[perf] + fn handle(&mut self, msg: WithSpanContext, _: &mut Self::Context) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = + metrics::VIEW_CLIENT_MESSAGE_TIME.with_label_values(&["GetBlock"]).start_timer(); + let block = self.get_block_by_reference(&msg.0)?.ok_or(GetBlockError::NotSyncedYet)?; + let block_author = self + .epoch_manager + .get_block_producer(block.header().epoch_id(), block.header().height()) + .into_chain_error()?; + Ok(BlockView::from_author_block(block_author, block)) + } +} + +impl Handler> for ViewClientActor { + type Result = Result<(BlockView, Arc), GetBlockError>; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["GetBlockWithMerkleTree"]) + .start_timer(); + let block_view = self.handle(GetBlock(msg.0).with_span_context(), ctx)?; + self.chain + .chain_store() + .get_block_merkle_tree(&block_view.header.hash) + .map(|merkle_tree| (block_view, merkle_tree)) + .map_err(|e| e.into()) + } +} + +impl Handler> for ViewClientActor { + type Result = Result; + + #[perf] + fn handle(&mut self, msg: WithSpanContext, _: &mut Self::Context) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = + metrics::VIEW_CLIENT_MESSAGE_TIME.with_label_values(&["GetChunk"]).start_timer(); + let get_chunk_from_block = |block: Block, + shard_id: ShardId, + chain: &Chain| + -> Result { + let chunk_header = block + .chunks() + .get(shard_id as usize) + .ok_or_else(|| unc_chain::Error::InvalidShardId(shard_id))? + .clone(); + let chunk_hash = chunk_header.chunk_hash(); + let chunk = chain.get_chunk(&chunk_hash)?; + let res = ShardChunk::with_header(ShardChunk::clone(&chunk), chunk_header).ok_or( + unc_chain::Error::Other(format!( + "Mismatched versions for chunk with hash {}", + chunk_hash.0 + )), + )?; + Ok(res) + }; + + let chunk = match msg { + GetChunk::ChunkHash(chunk_hash) => { + let chunk = self.chain.get_chunk(&chunk_hash)?; + ShardChunk::clone(&chunk) + } + GetChunk::BlockHash(block_hash, shard_id) => { + let block = self.chain.get_block(&block_hash)?; + get_chunk_from_block(block, shard_id, &self.chain)? + } + GetChunk::Height(height, shard_id) => { + let block = self.chain.get_block_by_height(height)?; + get_chunk_from_block(block, shard_id, &self.chain)? + } + }; + + let chunk_inner = chunk.cloned_header().take_inner(); + let epoch_id = self + .epoch_manager + .get_epoch_id_from_prev_block(chunk_inner.prev_block_hash()) + .into_chain_error()?; + let author = self + .epoch_manager + .get_chunk_producer(&epoch_id, chunk_inner.height_created(), chunk_inner.shard_id()) + .into_chain_error()?; + + Ok(ChunkView::from_author_chunk(author, chunk)) + } +} + +impl Handler> for ViewClientActor { + type Result = Result; + + #[perf] + fn handle(&mut self, msg: WithSpanContext, _: &mut Self::Context) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = + metrics::VIEW_CLIENT_MESSAGE_TIME.with_label_values(&["TxStatus"]).start_timer(); + self.get_tx_status(msg.tx_hash, msg.signer_account_id, msg.fetch_receipt) + } +} + +impl Handler> for ViewClientActor { + type Result = Result; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["GetValidatorInfo"]) + .start_timer(); + let epoch_identifier = match msg.epoch_reference { + EpochReference::EpochId(id) => { + // By `EpochId` we can get only cached epochs. + // Request for not finished epoch by `EpochId` will return an error because epoch has not been cached yet + // If the requested one is current ongoing we need to handle it like `Latest` + let tip = self.chain.header_head()?; + if tip.epoch_id == id { + ValidatorInfoIdentifier::BlockHash(tip.last_block_hash) + } else { + ValidatorInfoIdentifier::EpochId(id) + } + } + EpochReference::BlockId(block_id) => { + let block_header = match block_id { + BlockId::Hash(h) => self.chain.get_block_header(&h)?, + BlockId::Height(h) => self.chain.get_block_header_by_height(h)?, + }; + let next_block_hash = + self.chain.chain_store().get_next_block_hash(block_header.hash())?; + let next_block_header = self.chain.get_block_header(&next_block_hash)?; + if block_header.epoch_id() != next_block_header.epoch_id() + && block_header.next_epoch_id() == next_block_header.epoch_id() + { + ValidatorInfoIdentifier::EpochId(block_header.epoch_id().clone()) + } else { + return Err(GetValidatorInfoError::ValidatorInfoUnavailable); + } + } + EpochReference::Latest => { + // use header head because this is latest from the perspective of epoch manager + ValidatorInfoIdentifier::BlockHash(self.chain.header_head()?.last_block_hash) + } + }; + Ok(self.epoch_manager.get_validator_info(epoch_identifier).into_chain_error()?) + } +} + +impl Handler> for ViewClientActor { + type Result = Result, GetValidatorInfoError>; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["GetValidatorOrdered"]) + .start_timer(); + Ok(self.maybe_block_id_to_block_header(msg.block_id).and_then(|header| { + get_epoch_block_producers_view( + header.epoch_id(), + header.prev_hash(), + self.epoch_manager.as_ref(), + ) + })?) + } +} +/// Returns a list of change kinds per account in a store for a given block. +impl Handler> for ViewClientActor { + type Result = Result; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["GetStateChangesInBlock"]) + .start_timer(); + Ok(self + .chain + .chain_store() + .get_state_changes_in_block(&msg.block_hash)? + .into_iter() + .map(Into::into) + .collect()) + } +} + +/// Returns a list of changes in a store for a given block filtering by the state changes request. +impl Handler> for ViewClientActor { + type Result = Result; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = + metrics::VIEW_CLIENT_MESSAGE_TIME.with_label_values(&["GetStateChanges"]).start_timer(); + Ok(self + .chain + .chain_store() + .get_state_changes(&msg.block_hash, &msg.state_changes_request.into())? + .into_iter() + .map(Into::into) + .collect()) + } +} + +/// Returns a list of changes in a store with causes for a given block. +impl Handler> for ViewClientActor { + type Result = Result; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["GetStateChangesWithCauseInBlock"]) + .start_timer(); + Ok(self + .chain + .chain_store() + .get_state_changes_with_cause_in_block(&msg.block_hash)? + .into_iter() + .map(Into::into) + .collect()) + } +} + +/// Returns a hashmap where the key represents the ShardID and the value +/// is the list of changes in a store with causes for a given block. +impl Handler> for ViewClientActor { + type Result = Result, GetStateChangesError>; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["GetStateChangesWithCauseInBlockForTrackedShards"]) + .start_timer(); + let state_changes_with_cause_in_block = + self.chain.chain_store().get_state_changes_with_cause_in_block(&msg.block_hash)?; + + let mut state_changes_with_cause_split_by_shard_id: HashMap = + HashMap::new(); + for state_change_with_cause in state_changes_with_cause_in_block { + let account_id = state_change_with_cause.value.affected_account_id(); + let shard_id = match self + .epoch_manager + .account_id_to_shard_id(account_id, &msg.epoch_id) + { + Ok(shard_id) => shard_id, + Err(err) => { + return Err(GetStateChangesError::IOError { error_message: err.to_string() }) + } + }; + + let state_changes = + state_changes_with_cause_split_by_shard_id.entry(shard_id).or_default(); + state_changes.push(state_change_with_cause.into()); + } + + Ok(state_changes_with_cause_split_by_shard_id) + } +} + +/// Returns the next light client block, given the hash of the last block known to the light client. +/// There are three cases: +/// 1. The last block known to the light client is in the same epoch as the tip: +/// - Then return the last known final block, as long as it's more recent that the last known +/// 2. The last block known to the light client is in the epoch preceding that of the tip: +/// - Same as above +/// 3. Otherwise, return the last final block in the epoch that follows that of the last block known +/// to the light client +impl Handler> for ViewClientActor { + type Result = Result>, GetNextLightClientBlockError>; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["GetNextLightClientBlock"]) + .start_timer(); + let last_block_header = self.chain.get_block_header(&msg.last_block_hash)?; + let last_epoch_id = last_block_header.epoch_id().clone(); + let last_next_epoch_id = last_block_header.next_epoch_id().clone(); + let last_height = last_block_header.height(); + let head = self.chain.head()?; + + if last_epoch_id == head.epoch_id || last_next_epoch_id == head.epoch_id { + let head_header = self.chain.get_block_header(&head.last_block_hash)?; + let ret = Chain::create_light_client_block( + &head_header, + self.epoch_manager.as_ref(), + self.chain.chain_store(), + )?; + + if ret.inner_lite.height <= last_height { + Ok(None) + } else { + Ok(Some(Arc::new(ret))) + } + } else { + match self.chain.chain_store().get_epoch_light_client_block(&last_next_epoch_id.0) { + Ok(light_block) => Ok(Some(light_block)), + Err(e) => { + if let unc_chain::Error::DBNotFoundErr(_) = e { + Ok(None) + } else { + Err(e.into()) + } + } + } + } + } +} + +impl Handler> for ViewClientActor { + type Result = Result; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["GetExecutionOutcome"]) + .start_timer(); + let (id, account_id) = match msg.id { + TransactionOrReceiptId::Transaction { transaction_hash, sender_id } => { + (transaction_hash, sender_id) + } + TransactionOrReceiptId::Receipt { receipt_id, receiver_id } => { + (receipt_id, receiver_id) + } + }; + match self.chain.get_execution_outcome(&id) { + Ok(outcome) => { + let mut outcome_proof = outcome; + let epoch_id = + self.chain.get_block(&outcome_proof.block_hash)?.header().epoch_id().clone(); + let target_shard_id = self + .epoch_manager + .account_id_to_shard_id(&account_id, &epoch_id) + .into_chain_error()?; + let res = self.chain.get_next_block_hash_with_new_chunk( + &outcome_proof.block_hash, + target_shard_id, + )?; + if let Some((h, target_shard_id)) = res { + outcome_proof.block_hash = h; + // Here we assume the number of shards is small so this reconstruction + // should be fast + let outcome_roots = self + .chain + .get_block(&h)? + .chunks() + .iter() + .map(|header| header.prev_outcome_root()) + .collect::>(); + if target_shard_id >= (outcome_roots.len() as u64) { + return Err(GetExecutionOutcomeError::InconsistentState { + number_or_shards: outcome_roots.len(), + execution_outcome_shard_id: target_shard_id, + }); + } + Ok(GetExecutionOutcomeResponse { + outcome_proof: outcome_proof.into(), + outcome_root_proof: merklize(&outcome_roots).1[target_shard_id as usize] + .clone(), + }) + } else { + Err(GetExecutionOutcomeError::NotConfirmed { transaction_or_receipt_id: id }) + } + } + Err(unc_chain::Error::DBNotFoundErr(_)) => { + let head = self.chain.head()?; + let target_shard_id = self + .epoch_manager + .account_id_to_shard_id(&account_id, &head.epoch_id) + .into_chain_error()?; + if self.shard_tracker.care_about_shard( + self.validator_account_id.as_ref(), + &head.last_block_hash, + target_shard_id, + true, + ) { + Err(GetExecutionOutcomeError::UnknownTransactionOrReceipt { + transaction_or_receipt_id: id, + }) + } else { + Err(GetExecutionOutcomeError::UnavailableShard { + transaction_or_receipt_id: id, + shard_id: target_shard_id, + }) + } + } + Err(err) => Err(err.into()), + } + } +} + +/// Extract the list of execution outcomes that were produced in a given block +/// (including those created for local receipts). +impl Handler> for ViewClientActor { + type Result = Result>, String>; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["GetExecutionOutcomesForBlock"]) + .start_timer(); + Ok(self + .chain + .chain_store() + .get_block_execution_outcomes(&msg.block_hash) + .map_err(|e| e.to_string())? + .into_iter() + .map(|(k, v)| (k, v.into_iter().map(Into::into).collect())) + .collect()) + } +} + +impl Handler> for ViewClientActor { + type Result = Result, GetReceiptError>; + + #[perf] + fn handle(&mut self, msg: WithSpanContext, _: &mut Self::Context) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = + metrics::VIEW_CLIENT_MESSAGE_TIME.with_label_values(&["GetReceipt"]).start_timer(); + Ok(self + .chain + .chain_store() + .get_receipt(&msg.receipt_id)? + .map(|receipt| Receipt::clone(&receipt).into())) + } +} + +impl Handler> for ViewClientActor { + type Result = Result; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = + metrics::VIEW_CLIENT_MESSAGE_TIME.with_label_values(&["GetBlockProof"]).start_timer(); + let block_header = self.chain.get_block_header(&msg.block_hash)?; + let head_block_header = self.chain.get_block_header(&msg.head_block_hash)?; + self.chain.check_blocks_final_and_canonical(&[block_header.clone(), head_block_header])?; + let block_header_lite = block_header.into(); + let proof = self.chain.get_block_proof(&msg.block_hash, &msg.head_block_hash)?; + Ok(GetBlockProofResponse { block_header_lite, proof }) + } +} + +impl Handler> for ViewClientActor { + type Result = Result; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["GetProtocolConfig"]) + .start_timer(); + let header = match self.get_block_header_by_reference(&msg.0)? { + None => { + return Err(GetProtocolConfigError::UnknownBlock("EarliestAvailable".to_string())) + } + Some(header) => header, + }; + let config = self.runtime.get_protocol_config(header.epoch_id())?; + Ok(config.into()) + } +} + +#[cfg(feature = "test_features")] +use crate::NetworkAdversarialMessage; + +#[cfg(feature = "test_features")] +impl Handler> for ViewClientActor { + type Result = Option; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["NetworkAdversarialMessage"]) + .start_timer(); + match msg { + NetworkAdversarialMessage::AdvDisableDoomslug => { + info!(target: "adversary", "Turning Doomslug off"); + self.adv.set_disable_doomslug(true); + } + NetworkAdversarialMessage::AdvDisableHeaderSync => { + info!(target: "adversary", "Blocking header sync"); + self.adv.set_disable_header_sync(true); + } + NetworkAdversarialMessage::AdvSwitchToHeight(height) => { + info!(target: "adversary", "Switching to height"); + let mut chain_store_update = self.chain.mut_chain_store().store_update(); + chain_store_update.save_largest_target_height(height); + chain_store_update + .adv_save_latest_known(height) + .expect("adv method should not fail"); + chain_store_update.commit().expect("adv method should not fail"); + } + _ => panic!("invalid adversary message"), + } + None + } +} + +impl Handler> for ViewClientActor { + type Result = Option>; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = + metrics::VIEW_CLIENT_MESSAGE_TIME.with_label_values(&["TxStatusRequest"]).start_timer(); + let TxStatusRequest { tx_hash, signer_account_id } = msg; + if let Ok(Some(result)) = + self.get_tx_status(tx_hash, signer_account_id, false).map(|s| s.execution_outcome) + { + Some(Box::new(result.into_outcome())) + } else { + None + } + } +} + +impl Handler> for ViewClientActor { + type Result = (); + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["TxStatusResponse"]) + .start_timer(); + let TxStatusResponse(tx_result) = msg; + let tx_hash = tx_result.transaction_outcome.id; + let mut request_manager = self.request_manager.write().expect(POISONED_LOCK_ERR); + if request_manager.tx_status_requests.pop(&tx_hash).is_some() { + request_manager.tx_status_response.put(tx_hash, *tx_result); + } + } +} + +impl Handler> for ViewClientActor { + type Result = Option>; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = + metrics::VIEW_CLIENT_MESSAGE_TIME.with_label_values(&["BlockRequest"]).start_timer(); + let BlockRequest(hash) = msg; + if let Ok(block) = self.chain.get_block(&hash) { + Some(Box::new(block)) + } else { + None + } + } +} + + +impl Handler> for ViewClientActor { + type Result = Option; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = + metrics::VIEW_CLIENT_MESSAGE_TIME.with_label_values(&["ProviderRequest"]).start_timer(); + let ProviderRequest(epoch_id, height) = msg; + if let Ok(producer) = self.epoch_manager.get_block_producer(&epoch_id, height) { + Some(producer) + } else { + None + } + } +} + +impl Handler> for ViewClientActor { + type Result = Option>; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["BlockHeadersRequest"]) + .start_timer(); + let BlockHeadersRequest(hashes) = msg; + + if self.adv.disable_header_sync() { + None + } else if let Ok(headers) = self.retrieve_headers(hashes) { + Some(headers) + } else { + None + } + } +} + +impl Handler> for ViewClientActor { + type Result = Option; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["StateRequestHeader"]) + .start_timer(); + let StateRequestHeader { shard_id, sync_hash } = msg; + if self.throttle_state_sync_request() { + tracing::debug!(target: "sync", ?sync_hash, "Throttle state sync requests"); + return None; + } + let header = match self.chain.check_sync_hash_validity(&sync_hash) { + Ok(true) => match self.chain.get_state_response_header(shard_id, sync_hash) { + Ok(header) => Some(header), + Err(err) => { + error!(target: "sync", ?err, "Cannot build state sync header"); + None + } + }, + Ok(false) => { + warn!(target: "sync", ?sync_hash, "sync_hash didn't pass validation, possible malicious behavior"); + // Don't respond to the node, because the request is malformed. + return None; + } + Err(unc_chain::Error::DBNotFoundErr(_)) => { + // This case may appear in case of latency in epoch switching. + // Request sender is ready to sync but we still didn't get the block. + info!(target: "sync", ?sync_hash, "Can't get sync_hash block for state request header"); + None + } + Err(err) => { + error!(target: "sync", ?err, ?sync_hash, "Failed to verify sync_hash validity"); + None + } + }; + let state_response = match header { + Some(header) => { + let num_parts = header.num_state_parts(); + let cached_parts = match self + .chain + .get_cached_state_parts(sync_hash, shard_id, num_parts) + { + Ok(cached_parts) => Some(cached_parts), + Err(err) => { + tracing::error!(target: "sync", ?err, ?sync_hash, shard_id, "Failed to get cached state parts"); + None + } + }; + let header = match header { + ShardStateSyncResponseHeader::V2(inner) => inner, + _ => { + tracing::error!(target: "sync", ?sync_hash, shard_id, "Invalid state sync header format"); + return None; + } + }; + + let can_generate = self.has_state_snapshot(&sync_hash, shard_id).is_ok(); + ShardStateSyncResponse::V3(ShardStateSyncResponseV3 { + header: Some(header), + part: None, + cached_parts, + can_generate, + }) + } + None => ShardStateSyncResponse::V3(ShardStateSyncResponseV3 { + header: None, + part: None, + cached_parts: None, + can_generate: false, + }), + }; + let info = + StateResponseInfo::V2(StateResponseInfoV2 { shard_id, sync_hash, state_response }); + Some(StateResponse(Box::new(info))) + } +} + +impl Handler> for ViewClientActor { + type Result = Option; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["StateRequestPart"]) + .start_timer(); + let StateRequestPart { shard_id, sync_hash, part_id } = msg; + if self.throttle_state_sync_request() { + tracing::debug!(target: "sync", ?sync_hash, "Throttle state sync requests"); + return None; + } + if let Err(err) = self.has_state_snapshot(&sync_hash, shard_id) { + tracing::debug!(target: "sync", ?err, ?sync_hash, "Node doesn't have a matching state snapshot"); + return None; + } + tracing::debug!(target: "sync", ?shard_id, ?sync_hash, ?part_id, "Computing state request part"); + let part = match self.chain.check_sync_hash_validity(&sync_hash) { + Ok(true) => { + let part = match self.chain.get_state_response_part(shard_id, part_id, sync_hash) { + Ok(part) => Some((part_id, part)), + Err(err) => { + error!(target: "sync", ?err, ?sync_hash, shard_id, part_id, "Cannot build state part"); + None + } + }; + + tracing::trace!(target: "sync", ?sync_hash, shard_id, part_id, "Finished computation for state request part"); + part + } + Ok(false) => { + warn!(target: "sync", ?sync_hash, shard_id, "sync_hash didn't pass validation, possible malicious behavior"); + // Do not respond, possible malicious behavior. + return None; + } + Err(unc_chain::Error::DBNotFoundErr(_)) => { + // This case may appear in case of latency in epoch switching. + // Request sender is ready to sync but we still didn't get the block. + info!(target: "sync", ?sync_hash, "Can't get sync_hash block for state request part"); + None + } + Err(err) => { + error!(target: "sync", ?err, ?sync_hash, "Failed to verify sync_hash validity"); + None + } + }; + let num_parts = part.as_ref().and_then(|_| match self.chain.get_state_response_header(shard_id, sync_hash) { + Ok(header) => Some(header.num_state_parts()), + Err(err) => { + tracing::error!(target: "sync", ?err, ?sync_hash, shard_id, "Failed to get num state parts"); + None + } + }); + let cached_parts = num_parts.and_then(|num_parts| + match self.chain.get_cached_state_parts(sync_hash, shard_id, num_parts) { + Ok(cached_parts) => Some(cached_parts), + Err(err) => { + tracing::error!(target: "sync", ?err, ?sync_hash, shard_id, "Failed to get cached state parts"); + None + } + }); + let can_generate = part.is_some(); + let state_response = ShardStateSyncResponse::V3(ShardStateSyncResponseV3 { + header: None, + part, + cached_parts, + can_generate, + }); + let info = + StateResponseInfo::V2(StateResponseInfoV2 { shard_id, sync_hash, state_response }); + Some(StateResponse(Box::new(info))) + } +} + +impl Handler> for ViewClientActor { + type Result = Result, ReasonForBan>; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = metrics::VIEW_CLIENT_MESSAGE_TIME + .with_label_values(&["AnnounceAccountRequest"]) + .start_timer(); + let AnnounceAccountRequest(announce_accounts) = msg; + + let mut filtered_announce_accounts = Vec::new(); + + for (announce_account, last_epoch) in announce_accounts { + // Keep the announcement if it is newer than the last announcement from + // the same account. + if let Some(last_epoch) = last_epoch { + match self.epoch_manager.compare_epoch_id(&announce_account.epoch_id, &last_epoch) { + Ok(Ordering::Greater) => {} + _ => continue, + } + } + + match self.check_signature_account_announce(&announce_account) { + Ok(true) => { + filtered_announce_accounts.push(announce_account); + } + // TODO(gprusak): Here we ban for broadcasting accounts which have been slashed + // according to BlockInfo for the current chain tip. It is unfair, + // given that peers do not have perfectly synchronized heads: + // - AFAIU each block can introduce a slashed account, so the announcement + // could be OK at the moment that peer has sent it out. + // - the current epoch_id is not related to announce_account.epoch_id, + // so it carry a perfectly valid (outdated) information. + Ok(false) => { + return Err(ReasonForBan::InvalidSignature); + } + // Filter out this account. This covers both good reasons to ban the peer: + // - signature didn't match the data and public_key. + // - account is not a validator for the given epoch + // and cases when we were just unable to validate the data (so we shouldn't + // ban), for example when the node is not aware of the public key for the given + // (account_id,epoch_id) pair. + // We currently do NOT ban the peer for either. + // TODO(gprusak): consider whether we should change that. + Err(err) => { + tracing::debug!(target: "view_client", ?err, "Failed to validate account announce signature"); + } + } + } + Ok(filtered_announce_accounts) + } +} + +impl Handler> for ViewClientActor { + type Result = Result; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + let _timer = + metrics::VIEW_CLIENT_MESSAGE_TIME.with_label_values(&["GetGasPrice"]).start_timer(); + let header = self.maybe_block_id_to_block_header(msg.block_id); + Ok(GasPriceView { gas_price: header?.next_gas_price() }) + } +} + +impl Handler> for ViewClientActor { + type Result = Result; + + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + _ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + Ok(self.get_maintenance_windows(msg.account_id)?) + } +} + +impl Handler> for ViewClientActor { + type Result = Result; + + fn handle( + &mut self, + msg: WithSpanContext, + _: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "client", msg); + tracing::debug!(target: "client", ?msg); + + let store = self.chain.chain_store().store(); + let head = store.get_ser::(DBCol::BlockMisc, HEAD_KEY)?; + let final_head = store.get_ser::(DBCol::BlockMisc, FINAL_HEAD_KEY)?; + let cold_head = store.get_ser::(DBCol::BlockMisc, COLD_HEAD_KEY)?; + + let hot_db_kind = store.get_db_kind()?.map(|kind| kind.to_string()); + + Ok(SplitStorageInfoView { + head_height: head.map(|tip| tip.height), + final_head_height: final_head.map(|tip| tip.height), + cold_head_height: cold_head.map(|tip| tip.height), + hot_db_kind, + }) + } +} + +/// Starts the View Client in a new arbiter (thread). +pub fn start_view_client( + validator_account_id: Option, + chain_genesis: ChainGenesis, + epoch_manager: Arc, + shard_tracker: ShardTracker, + runtime: Arc, + network_adapter: PeerManagerAdapter, + config: ClientConfig, + adv: crate::adversarial::Controls, +) -> Addr { + let request_manager = Arc::new(RwLock::new(ViewClientRequestManager::new())); + SyncArbiter::start(config.view_client_threads, move || { + ViewClientActor::new( + validator_account_id.clone(), + &chain_genesis, + epoch_manager.clone(), + shard_tracker.clone(), + runtime.clone(), + network_adapter.clone(), + config.clone(), + request_manager.clone(), + adv.clone(), + ) + .unwrap() + }) +} diff --git a/chain/epoch-manager/Cargo.toml b/chain/epoch-manager/Cargo.toml new file mode 100644 index 000000000..54ad509a3 --- /dev/null +++ b/chain/epoch-manager/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "unc-epoch-manager" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +borsh.workspace = true +chrono = { workspace = true, optional = true } +num-rational.workspace = true +primitive-types.workspace = true +rand.workspace = true +rand_hc.workspace = true +serde_json.workspace = true +smart-default.workspace = true +tracing.workspace = true +# itertools has collect_vec which is useful in quick debugging prints +itertools.workspace = true + +unc-crypto.workspace = true +unc-primitives.workspace = true +unc-store.workspace = true +unc-chain-configs.workspace = true +unc-chain-primitives.workspace = true +unc-cache.workspace = true +protobuf.workspace = true +num-bigint.workspace = true +num-traits.workspace = true + +[features] +expensive_tests = [] +protocol_feature_fix_staking_threshold = [ + "unc-primitives/protocol_feature_fix_staking_threshold", +] +nightly = [ + "nightly_protocol", + "protocol_feature_fix_staking_threshold", + "unc-chain-configs/nightly", + "unc-primitives/nightly", + "unc-store/nightly", +] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", +] +no_cache = [] +new_epoch_sync = ["unc-store/new_epoch_sync", "unc-primitives/new_epoch_sync"] diff --git a/chain/epoch-manager/README.md b/chain/epoch-manager/README.md new file mode 100644 index 000000000..ab6a09f35 --- /dev/null +++ b/chain/epoch-manager/README.md @@ -0,0 +1,43 @@ +# unc-epoch-manager crate + +Epoch manager crate is responsible for code related to epochs and epoch switching. +An epoch is a unit of time when the set of validators of the network remain constant. + +You can read more about the epoch here: + +You can read more about Epoch finalization and Epoch changes here: + +## EpochManager + +Main class that has two main functions: + +* it creates new epochs (EpochIds) +* allows accessing information about past and current epochs (who is producing/approving given blocks, info about validators/fishermen etc ). + +### New Epoch Creation + +When 'finalize_epoch' is called, the EpochManager will do all the necessary processing (like computing validator rewards for the current epoch (T), selecting validators for the next next epoch (T+2) etc) and create the new EpochId/EpochInfo. + +### Accessing epoch information + +EpochManager has also a lot of methords that allows you to fetch information from different past and present epochs (like who is the chunk/block producer for a given chunk/block, whether the block is at the end of epoch boundary and requires more signatures etc) + +## RewardCalculator + +RewardCalculator is responsible for computing rewards for the validators at the end of the epoch, based on their block/chunk productions. +You can see more details on [the Nomicon documentation](https://nomicon.io/Economics/Economic#validator-rewards-calculation). + +## Validator Selection / proposals / proposals_to_epoch_info + +These files/functions are responsible for selecting the validators for the next epoch (and internally - also deciding which validator will produce which block and which chunk). + +We've recently (Dec 2021) introduced a new algorithm for validator selection (AliasValidatorSelectionAlgorithm), which is the reason why you can see both the old +and the new implementation present in the code - with new code existing in `validator_selection.rs`, while old code in `proposals.rs`. + +## Shard assignments + +This code is responsible for assigning validators (and chunk producers) to shards (chunks). This wil be used only once we enable `chunk_only_producers` feature (as before, we're simply assigning all the validators to validate each chunk). + +## Epoch info aggregator + +This is the class that keeps 'stats' for a given epoch (for example: info on how many blocks/chunks did the validators produce in the epoch, protocol version that validators support etc.). It is used to compute the validator rewards and new validators at the end of the epoch. diff --git a/chain/epoch-manager/src/adapter.rs b/chain/epoch-manager/src/adapter.rs new file mode 100644 index 000000000..bbca16b8a --- /dev/null +++ b/chain/epoch-manager/src/adapter.rs @@ -0,0 +1,1012 @@ +use crate::types::BlockHeaderInfo; +#[cfg(feature = "new_epoch_sync")] +use crate::EpochInfoAggregator; +use crate::EpochManagerHandle; +use unc_chain_primitives::Error; +use unc_crypto::Signature; +use unc_primitives::block_header::{Approval, ApprovalInner, BlockHeader}; +use unc_primitives::epoch_manager::block_info::BlockInfo; +use unc_primitives::epoch_manager::epoch_info::EpochInfo; +use unc_primitives::epoch_manager::EpochConfig; +use unc_primitives::epoch_manager::ShardConfig; +use unc_primitives::errors::EpochError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::{account_id_to_shard_id, ShardLayout, ShardLayoutError}; +use unc_primitives::sharding::{ChunkHash, ShardChunkHeader}; +use unc_primitives::types::{AccountId, ApprovalFrozen, Balance, BlockHeight, EpochHeight, EpochId, ShardId, ValidatorInfoIdentifier}; +use unc_primitives::validator_mandates::AssignmentWeight; +use unc_primitives::version::ProtocolVersion; +use unc_primitives::views::EpochValidatorInfo; +use unc_store::{ShardUId, StoreUpdate}; +use std::cmp::Ordering; +use std::collections::HashMap; +use std::sync::Arc; +use unc_primitives::types::validator_power_and_frozen::ValidatorPowerAndFrozen; + +/// A trait that abstracts the interface of the EpochManager. +/// The two implementations are EpochManagerHandle and KeyValueEpochManager. +/// Strongly prefer the former whenever possible. The latter is for legacy +/// tests. +pub trait EpochManagerAdapter: Send + Sync { + /// Check if epoch exists. + fn epoch_exists(&self, epoch_id: &EpochId) -> bool; + + /// Get the list of shard ids + fn shard_ids(&self, epoch_id: &EpochId) -> Result, EpochError>; + + /// Number of Reed-Solomon parts we split each chunk into. + /// + /// Note: this shouldn't be too large, our Reed-Solomon supports at most 256 + /// parts. + fn num_total_parts(&self) -> usize; + + /// How many Reed-Solomon parts are data parts. + /// + /// That is, fetching this many parts should be enough to reconstruct a + /// chunk, if there are no errors. + fn num_data_parts(&self) -> usize; + + /// Returns `account_id` that is supposed to have the `part_id`. + fn get_part_owner(&self, epoch_id: &EpochId, part_id: u64) -> Result; + + /// Which shard the account belongs to in the given epoch. + fn account_id_to_shard_id( + &self, + account_id: &AccountId, + epoch_id: &EpochId, + ) -> Result; + + /// Converts `ShardId` (index of shard in the *current* layout) to + /// `ShardUId` (`ShardId` + the version of shard layout itself.) + fn shard_id_to_uid( + &self, + shard_id: ShardId, + epoch_id: &EpochId, + ) -> Result; + + fn get_block_info(&self, hash: &CryptoHash) -> Result, EpochError>; + + fn get_epoch_config(&self, epoch_id: &EpochId) -> Result; + + fn get_epoch_info(&self, epoch_id: &EpochId) -> Result, EpochError>; + + fn get_shard_layout(&self, epoch_id: &EpochId) -> Result; + + fn get_shard_config(&self, epoch_id: &EpochId) -> Result; + + /// Returns true, if given hash is last block in it's epoch. + fn is_next_block_epoch_start(&self, parent_hash: &CryptoHash) -> Result; + + /// Returns true, if given hash is in an epoch that already finished. + /// `is_next_block_epoch_start` works even if we didn't fully process the provided block. + /// This function works even if we garbage collected `BlockInfo` of the first block of the epoch. + /// Thus, this function is better suited for use in garbage collection. + fn is_last_block_in_finished_epoch(&self, hash: &CryptoHash) -> Result; + + /// Get epoch id given hash of previous block. + fn get_epoch_id_from_prev_block(&self, parent_hash: &CryptoHash) + -> Result; + + /// Get epoch height given hash of previous block. + fn get_epoch_height_from_prev_block( + &self, + parent_hash: &CryptoHash, + ) -> Result; + + /// Get next epoch id given hash of the current block. + fn get_next_epoch_id(&self, block_hash: &CryptoHash) -> Result; + + /// Get next epoch id given hash of previous block. + fn get_next_epoch_id_from_prev_block( + &self, + parent_hash: &CryptoHash, + ) -> Result; + + /// For each `ShardId` in the current block, returns its parent `ShardId` + /// from previous block. + /// + /// Most of the times parent of the shard is the shard itself, unless a + /// resharding happened and some shards were split. + fn get_prev_shard_ids( + &self, + prev_hash: &CryptoHash, + shard_ids: Vec, + ) -> Result, Error>; + + /// Get shard layout given hash of previous block. + fn get_shard_layout_from_prev_block( + &self, + parent_hash: &CryptoHash, + ) -> Result; + + /// Get [`EpochId`] from a block belonging to the epoch. + fn get_epoch_id(&self, block_hash: &CryptoHash) -> Result; + + /// Which of the two epochs is earlier. + /// + /// This is well-defined because finality gadget guarantees that we cannot + /// have two different epochs on two forks. + fn compare_epoch_id( + &self, + epoch_id: &EpochId, + other_epoch_id: &EpochId, + ) -> Result; + + /// Get epoch start from a block belonging to the epoch. + fn get_epoch_start_height(&self, block_hash: &CryptoHash) -> Result; + + /// Get previous epoch id by hash of previous block. + fn get_prev_epoch_id_from_prev_block( + &self, + prev_block_hash: &CryptoHash, + ) -> Result; + + /// _If_ the next epoch will use a new protocol version, returns an + /// estimated block height for when the epoch switch occurs. + /// + /// This is very approximate and is used for logging only. + fn get_estimated_protocol_upgrade_block_height( + &self, + block_hash: CryptoHash, + ) -> Result, EpochError>; + + /// Epoch block producers ordered by their order in the proposals. + /// Returns EpochError if height is outside of known boundaries. + fn get_epoch_block_producers_ordered( + &self, + epoch_id: &EpochId, + last_known_block_hash: &CryptoHash, + ) -> Result, EpochError>; + + fn get_epoch_block_approvers_ordered( + &self, + parent_hash: &CryptoHash, + ) -> Result, EpochError>; + + /// Returns all the chunk producers for a given epoch. + fn get_epoch_chunk_producers( + &self, + epoch_id: &EpochId, + ) -> Result, EpochError>; + + /// Block producers for given height for the main block. Return EpochError if outside of known boundaries. + fn get_block_producer( + &self, + epoch_id: &EpochId, + height: BlockHeight, + ) -> Result; + + /// Block producers for given prev block hash. Return BlockError if outside of known boundaries. + fn get_block_producer_by_hash( + &self, + block_hash: &CryptoHash, + ) -> Result; + + /// Chunk producer for given height for given shard. Return EpochError if outside of known boundaries. + fn get_chunk_producer( + &self, + epoch_id: &EpochId, + height: BlockHeight, + shard_id: ShardId, + ) -> Result; + + /// Gets the chunk validators for a given height and shard. + fn get_chunk_validators( + &self, + epoch_id: &EpochId, + shard_id: ShardId, + height: BlockHeight, + ) -> Result, EpochError>; + + fn get_validator_by_account_id( + &self, + epoch_id: &EpochId, + last_known_block_hash: &CryptoHash, + account_id: &AccountId, + ) -> Result<(ValidatorPowerAndFrozen, bool), EpochError>; + + fn get_fisherman_by_account_id( + &self, + epoch_id: &EpochId, + last_known_block_hash: &CryptoHash, + account_id: &AccountId, + ) -> Result<(ValidatorPowerAndFrozen, bool), EpochError>; + + /// WARNING: this call may be expensive. + /// + /// This function is intended for diagnostic use in logging & rpc, don't use + /// it for "production" code. + fn get_validator_info( + &self, + epoch_id: ValidatorInfoIdentifier, + ) -> Result; + + fn add_validator_proposals( + &self, + block_header_info: BlockHeaderInfo, + ) -> Result; + + /// Amount of tokens minted in given epoch. + fn get_epoch_minted_amount(&self, epoch_id: &EpochId) -> Result; + + /// Epoch active protocol version. + fn get_epoch_protocol_version(&self, epoch_id: &EpochId) + -> Result; + + // TODO #3488 this likely to be updated + /// Data that is necessary for prove Epochs in Epoch Sync. + fn get_epoch_sync_data( + &self, + prev_epoch_last_block_hash: &CryptoHash, + epoch_id: &EpochId, + next_epoch_id: &EpochId, + ) -> Result< + ( + Arc, + Arc, + Arc, + Arc, + Arc, + Arc, + ), + EpochError, + >; + + // TODO #3488 this likely to be updated + /// Hash that is necessary for prove Epochs in Epoch Sync. + fn get_epoch_sync_data_hash( + &self, + prev_epoch_last_block_hash: &CryptoHash, + epoch_id: &EpochId, + next_epoch_id: &EpochId, + ) -> Result { + let ( + prev_epoch_first_block_info, + prev_epoch_prev_last_block_info, + prev_epoch_last_block_info, + prev_epoch_info, + cur_epoch_info, + next_epoch_info, + ) = self.get_epoch_sync_data(prev_epoch_last_block_hash, epoch_id, next_epoch_id)?; + Ok(CryptoHash::hash_borsh(&( + prev_epoch_first_block_info, + prev_epoch_prev_last_block_info, + prev_epoch_last_block_info, + prev_epoch_info, + cur_epoch_info, + next_epoch_info, + ))) + } + + /// Epoch Manager init procedure that is necessary after Epoch Sync. + fn epoch_sync_init_epoch_manager( + &self, + prev_epoch_first_block_info: BlockInfo, + prev_epoch_prev_last_block_info: BlockInfo, + prev_epoch_last_block_info: BlockInfo, + prev_epoch_id: &EpochId, + prev_epoch_info: EpochInfo, + epoch_id: &EpochId, + epoch_info: EpochInfo, + next_epoch_id: &EpochId, + next_epoch_info: EpochInfo, + ) -> Result<(), EpochError>; + + fn verify_block_vrf( + &self, + epoch_id: &EpochId, + block_height: BlockHeight, + prev_random_value: &CryptoHash, + vrf_value: &unc_crypto::vrf::Value, + vrf_proof: &unc_crypto::vrf::Proof, + ) -> Result<(), Error>; + + /// Verify validator signature for the given epoch. + /// Note: doesnt't account for slashed accounts within given epoch. USE WITH CAUTION. + fn verify_validator_signature( + &self, + epoch_id: &EpochId, + last_known_block_hash: &CryptoHash, + account_id: &AccountId, + data: &[u8], + signature: &Signature, + ) -> Result; + + /// Verify signature for validator or fisherman. Used for validating challenges. + fn verify_validator_or_fisherman_signature( + &self, + epoch_id: &EpochId, + last_known_block_hash: &CryptoHash, + account_id: &AccountId, + data: &[u8], + signature: &Signature, + ) -> Result; + + /// Verify header signature. + fn verify_header_signature(&self, header: &BlockHeader) -> Result; + + /// Verify chunk header signature. + /// return false if the header signature does not match the key for the assigned chunk producer + /// for this chunk, or if the chunk producer has been slashed + /// return `EpochError::NotAValidator` if cannot find chunk producer info for this chunk + /// `header`: chunk header + /// `epoch_id`: epoch_id that the chunk header belongs to + /// `last_known_hash`: used to determine the list of chunk producers that are slashed + fn verify_chunk_header_signature( + &self, + header: &ShardChunkHeader, + epoch_id: &EpochId, + last_known_hash: &CryptoHash, + ) -> Result { + self.verify_chunk_signature_with_header_parts( + &header.chunk_hash(), + header.signature(), + epoch_id, + last_known_hash, + header.height_created(), + header.shard_id(), + ) + } + + fn verify_chunk_signature_with_header_parts( + &self, + chunk_hash: &ChunkHash, + signature: &Signature, + epoch_id: &EpochId, + last_known_hash: &CryptoHash, + height_created: BlockHeight, + shard_id: ShardId, + ) -> Result; + + /// Verify aggregated bls signature + fn verify_approval( + &self, + prev_block_hash: &CryptoHash, + prev_block_height: BlockHeight, + block_height: BlockHeight, + approvals: &[Option>], + ) -> Result; + + /// Verify approvals and check threshold, but ignore next epoch approvals and slashing + fn verify_approvals_and_threshold_orphan( + &self, + epoch_id: &EpochId, + can_approved_block_be_produced: &dyn Fn( + &[Option>], + // (stake this in epoch, stake in next epoch, is_slashed) + &[(Balance, Balance, bool)], + ) -> bool, + prev_block_hash: &CryptoHash, + prev_block_height: BlockHeight, + block_height: BlockHeight, + approvals: &[Option>], + ) -> Result<(), Error>; + + fn cares_about_shard_from_prev_block( + &self, + parent_hash: &CryptoHash, + account_id: &AccountId, + shard_id: ShardId, + ) -> Result; + + fn cares_about_shard_next_epoch_from_prev_block( + &self, + parent_hash: &CryptoHash, + account_id: &AccountId, + shard_id: ShardId, + ) -> Result; + + fn will_shard_layout_change(&self, parent_hash: &CryptoHash) -> Result; + + /// Returns a vector of all hashes in the epoch ending with `last_block_info`. + /// Only return blocks on chain of `last_block_info`. + /// Hashes are returned in the order from the last block to the first block. + #[cfg(feature = "new_epoch_sync")] + fn get_all_epoch_hashes( + &self, + last_block_info: &BlockInfo, + hash_to_prev_hash: Option<&HashMap>, + ) -> Result, EpochError>; + + #[cfg(feature = "new_epoch_sync")] + fn force_update_aggregator(&self, epoch_id: &EpochId, hash: &CryptoHash); + // fn get_block_producer_by_hash(&self, block_hash: &CryptoHash) -> Result { + // let epoch_manager = self.read(); + // Ok(epoch_manager.get_block_producer_info_by_hash(block_hash)?.take_account_id()) + // } + // fn get_block_producer_by_height(&self, block_height: BlockHeight) -> Result; + // fn add_bad_validator(&self, height: BlockHeight, validator: AccountId) -> Result<(), EpochError>; +} + +impl EpochManagerAdapter for EpochManagerHandle { + fn epoch_exists(&self, epoch_id: &EpochId) -> bool { + let epoch_manager = self.read(); + epoch_manager.get_epoch_info(epoch_id).is_ok() + } + + fn shard_ids(&self, epoch_id: &EpochId) -> Result, EpochError> { + let epoch_manager = self.read(); + Ok(epoch_manager.get_shard_layout(epoch_id)?.shard_ids().collect()) + } + + fn num_total_parts(&self) -> usize { + 2 + } + + fn num_data_parts(&self) -> usize { + 1 + } + + fn get_part_owner(&self, epoch_id: &EpochId, part_id: u64) -> Result { + let epoch_manager = self.read(); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id)?; + let settlement = epoch_info.block_producers_settlement(); + let validator_id = settlement[part_id as usize % settlement.len()]; + Ok(epoch_info.get_validator(validator_id).account_id().clone()) + } + + fn account_id_to_shard_id( + &self, + account_id: &AccountId, + epoch_id: &EpochId, + ) -> Result { + let epoch_manager = self.read(); + let shard_layout = epoch_manager.get_shard_layout(epoch_id)?; + Ok(account_id_to_shard_id(account_id, &shard_layout)) + } + + fn shard_id_to_uid( + &self, + shard_id: ShardId, + epoch_id: &EpochId, + ) -> Result { + let epoch_manager = self.read(); + let shard_layout = epoch_manager.get_shard_layout(epoch_id)?; + Ok(ShardUId::from_shard_id_and_layout(shard_id, &shard_layout)) + } + + fn get_block_info(&self, hash: &CryptoHash) -> Result, EpochError> { + let epoch_manager = self.read(); + epoch_manager.get_block_info(hash) + } + + fn get_epoch_config(&self, epoch_id: &EpochId) -> Result { + let epoch_manager = self.read(); + epoch_manager.get_epoch_config(epoch_id) + } + + fn get_epoch_info(&self, epoch_id: &EpochId) -> Result, EpochError> { + let epoch_manager = self.read(); + epoch_manager.get_epoch_info(epoch_id) + } + + fn get_shard_layout(&self, epoch_id: &EpochId) -> Result { + let epoch_manager = self.read(); + epoch_manager.get_shard_layout(epoch_id) + } + + fn get_shard_config(&self, epoch_id: &EpochId) -> Result { + let epoch_manager = self.read(); + let epoch_config = epoch_manager.get_epoch_config(epoch_id)?; + Ok(ShardConfig::new(epoch_config)) + } + + fn is_next_block_epoch_start(&self, parent_hash: &CryptoHash) -> Result { + let epoch_manager = self.read(); + epoch_manager.is_next_block_epoch_start(parent_hash) + } + + fn is_last_block_in_finished_epoch(&self, hash: &CryptoHash) -> Result { + let epoch_manager = self.read(); + epoch_manager.is_last_block_in_finished_epoch(hash) + } + + fn get_epoch_id_from_prev_block( + &self, + parent_hash: &CryptoHash, + ) -> Result { + let epoch_manager = self.read(); + epoch_manager.get_epoch_id_from_prev_block(parent_hash) + } + + fn get_epoch_height_from_prev_block( + &self, + prev_block_hash: &CryptoHash, + ) -> Result { + let epoch_manager = self.read(); + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(prev_block_hash)?; + epoch_manager.get_epoch_info(&epoch_id).map(|info| info.epoch_height()) + } + + fn get_next_epoch_id(&self, block_hash: &CryptoHash) -> Result { + let epoch_manager = self.read(); + epoch_manager.get_next_epoch_id(block_hash) + } + + fn get_next_epoch_id_from_prev_block( + &self, + parent_hash: &CryptoHash, + ) -> Result { + let epoch_manager = self.read(); + epoch_manager.get_next_epoch_id_from_prev_block(parent_hash) + } + + fn get_prev_shard_ids( + &self, + prev_hash: &CryptoHash, + shard_ids: Vec, + ) -> Result, Error> { + if self.is_next_block_epoch_start(prev_hash)? { + let shard_layout = self.get_shard_layout_from_prev_block(prev_hash)?; + let prev_shard_layout = self.get_shard_layout(&self.get_epoch_id(prev_hash)?)?; + if prev_shard_layout != shard_layout { + return Ok(shard_ids + .into_iter() + .map(|shard_id| { + shard_layout.get_parent_shard_id(shard_id).map(|parent_shard_id|{ + assert!(prev_shard_layout.shard_ids().any(|i| i == parent_shard_id), + "invalid shard layout. parent_shard_id: {}\nshard_layout: {:?}\nprev_shard_layout: {:?}", + parent_shard_id, + shard_layout, + parent_shard_id + ); + parent_shard_id + }) + }) + .collect::>()?); + } + } + Ok(shard_ids) + } + + fn get_shard_layout_from_prev_block( + &self, + parent_hash: &CryptoHash, + ) -> Result { + let epoch_id = self.get_epoch_id_from_prev_block(parent_hash)?; + self.get_shard_layout(&epoch_id) + } + + fn get_epoch_id(&self, block_hash: &CryptoHash) -> Result { + let epoch_manager = self.read(); + epoch_manager.get_epoch_id(block_hash) + } + + fn compare_epoch_id( + &self, + epoch_id: &EpochId, + other_epoch_id: &EpochId, + ) -> Result { + let epoch_manager = self.read(); + epoch_manager.compare_epoch_id(epoch_id, other_epoch_id).map_err(|e| e.into()) + } + + fn get_epoch_start_height(&self, block_hash: &CryptoHash) -> Result { + let epoch_manager = self.read(); + epoch_manager.get_epoch_start_height(block_hash) + } + + fn get_prev_epoch_id_from_prev_block( + &self, + prev_block_hash: &CryptoHash, + ) -> Result { + let epoch_manager = self.read(); + if epoch_manager.is_next_block_epoch_start(prev_block_hash)? { + epoch_manager.get_epoch_id(prev_block_hash) + } else { + epoch_manager.get_prev_epoch_id(prev_block_hash) + } + } + + fn get_estimated_protocol_upgrade_block_height( + &self, + block_hash: CryptoHash, + ) -> Result, EpochError> { + let epoch_manager = self.read(); + epoch_manager.get_protocol_upgrade_block_height(block_hash) + } + + fn get_epoch_block_producers_ordered( + &self, + epoch_id: &EpochId, + last_known_block_hash: &CryptoHash, + ) -> Result, EpochError> { + let epoch_manager = self.read(); + Ok(epoch_manager.get_all_block_producers_ordered(epoch_id, last_known_block_hash)?.to_vec()) + } + + fn get_epoch_block_approvers_ordered( + &self, + parent_hash: &CryptoHash, + ) -> Result, EpochError> { + let epoch_manager = self.read(); + epoch_manager.get_all_block_approvers_ordered(parent_hash) + } + + fn get_epoch_chunk_producers( + &self, + epoch_id: &EpochId, + ) -> Result, EpochError> { + let epoch_manager = self.read(); + Ok(epoch_manager.get_all_chunk_producers(epoch_id)?.to_vec()) + } + + fn get_block_producer( + &self, + epoch_id: &EpochId, + height: BlockHeight, + ) -> Result { + let epoch_manager = self.read(); + Ok(epoch_manager.get_block_producer_info(epoch_id, height)?.take_account_id()) + } + + fn get_block_producer_by_hash(&self, block_hash: &CryptoHash) -> Result { + let epoch_manager = self.read(); + Ok(epoch_manager.get_block_producer_info_by_hash(block_hash)?.take_account_id()) + } + + fn get_chunk_producer( + &self, + epoch_id: &EpochId, + height: BlockHeight, + shard_id: ShardId, + ) -> Result { + let epoch_manager = self.read(); + Ok(epoch_manager.get_chunk_producer_info(epoch_id, height, shard_id)?.take_account_id()) + } + + fn get_chunk_validators( + &self, + epoch_id: &EpochId, + shard_id: ShardId, + height: BlockHeight, + ) -> Result, EpochError> { + let epoch_manager = self.read(); + epoch_manager.get_chunk_validators(epoch_id, shard_id, height) + } + + fn get_validator_by_account_id( + &self, + epoch_id: &EpochId, + last_known_block_hash: &CryptoHash, + account_id: &AccountId, + ) -> Result<(ValidatorPowerAndFrozen, bool), EpochError> { + let epoch_manager = self.read(); + let validator = epoch_manager.get_validator_by_account_id(epoch_id, account_id)?; + let block_info = epoch_manager.get_block_info(last_known_block_hash)?; + Ok((validator, block_info.slashed().contains_key(account_id))) + } + + fn get_fisherman_by_account_id( + &self, + epoch_id: &EpochId, + last_known_block_hash: &CryptoHash, + account_id: &AccountId, + ) -> Result<(ValidatorPowerAndFrozen, bool), EpochError> { + let epoch_manager = self.read(); + let fisherman = epoch_manager.get_fisherman_by_account_id(epoch_id, account_id)?; + let block_info = epoch_manager.get_block_info(last_known_block_hash)?; + Ok((fisherman, block_info.slashed().contains_key(account_id))) + } + + /// WARNING: this function calls EpochManager::get_epoch_info_aggregator_upto_last + /// underneath which can be very expensive. + fn get_validator_info( + &self, + epoch_id: ValidatorInfoIdentifier, + ) -> Result { + let epoch_manager = self.read(); + epoch_manager.get_validator_info(epoch_id) + } + + fn add_validator_proposals( + &self, + block_header_info: BlockHeaderInfo, + ) -> Result { + let mut epoch_manager = self.write(); + epoch_manager.add_validator_proposals(block_header_info) + } + + fn get_epoch_minted_amount(&self, epoch_id: &EpochId) -> Result { + let epoch_manager = self.read(); + Ok(epoch_manager.get_epoch_info(epoch_id)?.minted_amount()) + } + + fn get_epoch_protocol_version( + &self, + epoch_id: &EpochId, + ) -> Result { + let epoch_manager = self.read(); + Ok(epoch_manager.get_epoch_info(epoch_id)?.protocol_version()) + } + + // TODO #3488 this likely to be updated + fn get_epoch_sync_data( + &self, + prev_epoch_last_block_hash: &CryptoHash, + epoch_id: &EpochId, + next_epoch_id: &EpochId, + ) -> Result< + ( + Arc, + Arc, + Arc, + Arc, + Arc, + Arc, + ), + EpochError, + > { + let epoch_manager = self.read(); + let last_block_info = epoch_manager.get_block_info(prev_epoch_last_block_hash)?; + let prev_epoch_id = last_block_info.epoch_id().clone(); + Ok(( + epoch_manager.get_block_info(last_block_info.epoch_first_block())?, + epoch_manager.get_block_info(last_block_info.prev_hash())?, + last_block_info, + epoch_manager.get_epoch_info(&prev_epoch_id)?, + epoch_manager.get_epoch_info(epoch_id)?, + epoch_manager.get_epoch_info(next_epoch_id)?, + )) + } + + fn epoch_sync_init_epoch_manager( + &self, + prev_epoch_first_block_info: BlockInfo, + prev_epoch_prev_last_block_info: BlockInfo, + prev_epoch_last_block_info: BlockInfo, + prev_epoch_id: &EpochId, + prev_epoch_info: EpochInfo, + epoch_id: &EpochId, + epoch_info: EpochInfo, + next_epoch_id: &EpochId, + next_epoch_info: EpochInfo, + ) -> Result<(), EpochError> { + let mut epoch_manager = self.write(); + epoch_manager + .init_after_epoch_sync( + prev_epoch_first_block_info, + prev_epoch_prev_last_block_info, + prev_epoch_last_block_info, + prev_epoch_id, + prev_epoch_info, + epoch_id, + epoch_info, + next_epoch_id, + next_epoch_info, + )? + .commit() + .map_err(|err| err.into()) + } + + fn verify_block_vrf( + &self, + epoch_id: &EpochId, + block_height: BlockHeight, + prev_random_value: &CryptoHash, + vrf_value: &unc_crypto::vrf::Value, + vrf_proof: &unc_crypto::vrf::Proof, + ) -> Result<(), Error> { + let epoch_manager = self.read(); + let validator = epoch_manager.get_block_producer_info(epoch_id, block_height)?; + let public_key = unc_crypto::key_conversion::convert_public_key( + validator.public_key().unwrap_as_ed25519(), + ) + .unwrap(); + + if !public_key.is_vrf_valid(&prev_random_value.as_ref(), vrf_value, vrf_proof) { + return Err(Error::InvalidRandomnessBeaconOutput); + } + Ok(()) + } + + fn verify_validator_signature( + &self, + epoch_id: &EpochId, + last_known_block_hash: &CryptoHash, + account_id: &AccountId, + data: &[u8], + signature: &Signature, + ) -> Result { + let (validator, is_slashed) = + self.get_validator_by_account_id(epoch_id, last_known_block_hash, account_id)?; + if is_slashed { + return Ok(false); + } + Ok(signature.verify(data, validator.public_key())) + } + + fn verify_validator_or_fisherman_signature( + &self, + epoch_id: &EpochId, + last_known_block_hash: &CryptoHash, + account_id: &AccountId, + data: &[u8], + signature: &Signature, + ) -> Result { + match self.verify_validator_signature( + epoch_id, + last_known_block_hash, + account_id, + data, + signature, + ) { + Err(Error::NotAValidator) => { + let (fisherman, is_slashed) = + self.get_fisherman_by_account_id(epoch_id, last_known_block_hash, account_id)?; + if is_slashed { + return Ok(false); + } + Ok(signature.verify(data, fisherman.public_key())) + } + other => other, + } + } + + /// Returns true if the header signature is signed by the assigned block producer and the block + /// producer is not slashed + /// This function requires that the previous block of `header` has been processed. + /// If not, it returns EpochError::MissingBlock. + fn verify_header_signature(&self, header: &BlockHeader) -> Result { + let epoch_manager = self.read(); + let block_producer = + epoch_manager.get_block_producer_info(header.epoch_id(), header.height())?; + match epoch_manager.get_block_info(header.prev_hash()) { + Ok(block_info) => { + if block_info.slashed().contains_key(block_producer.account_id()) { + return Ok(false); + } + Ok(header.signature().verify(header.hash().as_ref(), block_producer.public_key())) + } + Err(_) => return Err(EpochError::MissingBlock(*header.prev_hash()).into()), + } + } + + fn verify_chunk_signature_with_header_parts( + &self, + chunk_hash: &ChunkHash, + signature: &Signature, + epoch_id: &EpochId, + last_known_hash: &CryptoHash, + height_created: BlockHeight, + shard_id: ShardId, + ) -> Result { + let epoch_manager = self.read(); + let chunk_producer = + epoch_manager.get_chunk_producer_info(epoch_id, height_created, shard_id)?; + let block_info = epoch_manager.get_block_info(last_known_hash)?; + if block_info.slashed().contains_key(chunk_producer.account_id()) { + return Ok(false); + } + Ok(signature.verify(chunk_hash.as_ref(), chunk_producer.public_key())) + } + + fn verify_approval( + &self, + prev_block_hash: &CryptoHash, + prev_block_height: BlockHeight, + block_height: BlockHeight, + approvals: &[Option>], + ) -> Result { + let info = { + let epoch_manager = self.read(); + epoch_manager.get_all_block_approvers_ordered(prev_block_hash)? + }; + if approvals.len() > info.len() { + return Ok(false); + } + + let message_to_sign = Approval::get_data_for_sig( + &if prev_block_height + 1 == block_height { + ApprovalInner::Endorsement(*prev_block_hash) + } else { + ApprovalInner::Skip(prev_block_height) + }, + block_height, + ); + + for ((validator, is_slashed), may_be_signature) in info.into_iter().zip(approvals.iter()) { + if let Some(signature) = may_be_signature { + if is_slashed || !signature.verify(message_to_sign.as_ref(), &validator.public_key) + { + return Ok(false); + } + } + } + Ok(true) + } + + fn verify_approvals_and_threshold_orphan( + &self, + epoch_id: &EpochId, + can_approved_block_be_produced: &dyn Fn( + &[Option>], + &[(Balance, Balance, bool)], + ) -> bool, + prev_block_hash: &CryptoHash, + prev_block_height: BlockHeight, + block_height: BlockHeight, + approvals: &[Option>], + ) -> Result<(), Error> { + let info = { + let epoch_manager = self.read(); + epoch_manager.get_heuristic_block_approvers_ordered(epoch_id)? + }; + + let message_to_sign = Approval::get_data_for_sig( + &if prev_block_height + 1 == block_height { + ApprovalInner::Endorsement(*prev_block_hash) + } else { + ApprovalInner::Skip(prev_block_height) + }, + block_height, + ); + + for (validator, may_be_signature) in info.iter().zip(approvals.iter()) { + if let Some(signature) = may_be_signature { + if !signature.verify(message_to_sign.as_ref(), &validator.public_key) { + return Err(Error::InvalidApprovals); + } + } + } + let all_frozen = info + .iter() + .map(|frozen| (frozen.frozen_this_epoch, frozen.frozen_next_epoch, false)) + .collect::>(); + if !can_approved_block_be_produced(approvals, &all_frozen) { + Err(Error::NotEnoughApprovals) + } else { + Ok(()) + } + } + + fn cares_about_shard_from_prev_block( + &self, + parent_hash: &CryptoHash, + account_id: &AccountId, + shard_id: ShardId, + ) -> Result { + let epoch_manager = self.read(); + epoch_manager.cares_about_shard_from_prev_block(parent_hash, account_id, shard_id) + } + + fn cares_about_shard_next_epoch_from_prev_block( + &self, + parent_hash: &CryptoHash, + account_id: &AccountId, + shard_id: ShardId, + ) -> Result { + let epoch_manager = self.read(); + epoch_manager.cares_about_shard_next_epoch_from_prev_block( + parent_hash, + account_id, + shard_id, + ) + } + + fn will_shard_layout_change(&self, parent_hash: &CryptoHash) -> Result { + let epoch_manager = self.read(); + epoch_manager.will_shard_layout_change(parent_hash) + } + + #[cfg(feature = "new_epoch_sync")] + fn get_all_epoch_hashes( + &self, + last_block_info: &BlockInfo, + hash_to_prev_hash: Option<&HashMap>, + ) -> Result, EpochError> { + let epoch_manager = self.read(); + match hash_to_prev_hash { + None => epoch_manager.get_all_epoch_hashes_from_db(last_block_info), + Some(hash_to_prev_hash) => { + epoch_manager.get_all_epoch_hashes_from_cache(last_block_info, hash_to_prev_hash) + } + } + } + + #[cfg(feature = "new_epoch_sync")] + fn force_update_aggregator(&self, epoch_id: &EpochId, hash: &CryptoHash) { + let mut epoch_manager = self.write(); + epoch_manager.epoch_info_aggregator = EpochInfoAggregator::new(epoch_id.clone(), *hash); + } +} diff --git a/chain/epoch-manager/src/lib.rs b/chain/epoch-manager/src/lib.rs new file mode 100644 index 000000000..c5d7e12d5 --- /dev/null +++ b/chain/epoch-manager/src/lib.rs @@ -0,0 +1,2376 @@ +use num_bigint::{BigInt, ToBigInt}; +use crate::proposals::proposals_to_block_summary; +use crate::proposals::proposals_to_epoch_info; +use crate::types::EpochInfoAggregator; +use unc_cache::SyncLruCache; +use unc_chain_configs::GenesisConfig; +use unc_primitives::checked_feature; +use unc_primitives::epoch_manager::block_info::{BlockInfo, BlockInfoV2}; +use unc_primitives::epoch_manager::block_summary::{BlockSummary, BlockSummaryV1}; +use unc_primitives::epoch_manager::epoch_info::{EpochInfo, EpochSummary}; +use unc_primitives::epoch_manager::{ + AllEpochConfig, AllEpochConfigTestOverrides, EpochConfig, ShardConfig, SlashState, + AGGREGATOR_KEY, +}; +use unc_primitives::errors::{BlockError, EpochError}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardLayout; +use unc_primitives::types::validator_frozen::ValidatorFrozen; +use unc_primitives::types::validator_power::ValidatorPower; +use unc_primitives::types::validator_power_and_frozen::{ValidatorPowerAndFrozen, ValidatorPowerAndFrozenIter}; +use unc_primitives::types::{AccountId, ApprovalFrozen, Balance, BlockChunkValidatorStats, BlockHeight, EpochId, EpochInfoProvider, NumBlocks, Power, ShardId, ValidatorId, ValidatorInfoIdentifier, ValidatorKickoutReason, ValidatorStats}; +use unc_primitives::validator_mandates::AssignmentWeight; +use unc_primitives::version::{ProtocolVersion, UPGRADABILITY_FIX_PROTOCOL_VERSION}; +use unc_primitives::views::{ + CurrentEpochValidatorInfo, EpochValidatorInfo, NextEpochValidatorInfo, ValidatorKickoutView, +}; +use unc_store::{DBCol, Store, StoreUpdate}; +use num_rational::Rational64; +use primitive_types::U256; +use std::cmp::Ordering; +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; +use num_traits::Zero; +use tracing::{debug, warn}; +use types::BlockHeaderInfo; + +pub use crate::adapter::EpochManagerAdapter; +pub use crate::reward_calculator::RewardCalculator; +pub use crate::reward_calculator::NUM_SECONDS_IN_A_YEAR; +pub use crate::types::RngSeed; + +mod adapter; +mod proposals; +mod reward_calculator; +mod shard_assignment; +pub mod shard_tracker; +pub mod test_utils; +#[cfg(test)] +mod tests; +pub mod types; +mod validator_selection; + +const EPOCH_CACHE_SIZE: usize = if cfg!(feature = "no_cache") { 1 } else { 50 }; +const BLOCK_CACHE_SIZE: usize = if cfg!(feature = "no_cache") { 5 } else { 1000 }; // TODO(#5080): fix this + +const _HASH_CACHE_SIZE: usize = if cfg!(feature = "no_cache") { 1 } else { 2 }; +const AGGREGATOR_SAVE_PERIOD: u64 = 1000; + +// In epoch_manager or a common module + +/// In the current architecture, various components have access to the same +/// shared mutable instance of [`EpochManager`]. This handle manages locking +/// required for such access. +/// +/// It's up to the caller to ensure that there are no logical races when using +/// `.write` access. +#[derive(Clone)] +pub struct EpochManagerHandle { + inner: Arc>, +} + +impl EpochManagerHandle { + pub fn write(&self) -> RwLockWriteGuard { + self.inner.write().unwrap() + } + + pub fn read(&self) -> RwLockReadGuard { + self.inner.read().unwrap() + } +} + +impl EpochInfoProvider for EpochManagerHandle { + fn validator_power( + &self, + epoch_id: &EpochId, + last_block_hash: &CryptoHash, + account_id: &AccountId, + ) -> Result, EpochError> { + let epoch_manager = self.read(); + let last_block_info = epoch_manager.get_block_info(last_block_hash)?; + if last_block_info.slashed().contains_key(account_id) { + return Ok(None); + } + let epoch_info = epoch_manager.get_epoch_info(epoch_id)?; + Ok(epoch_info.get_validator_id(account_id).map(|id| epoch_info.validator_power(*id))) + } + + fn validator_total_power( + &self, + epoch_id: &EpochId, + last_block_hash: &CryptoHash, + ) -> Result { + let epoch_manager = self.read(); + let last_block_info = epoch_manager.get_block_info(last_block_hash)?; + let epoch_info = epoch_manager.get_epoch_info(epoch_id)?; + Ok(epoch_info + .validators_iter() + .filter(|info| !last_block_info.slashed().contains_key(info.account_id())) + .map(|info| info.power()) + .sum()) + } + + fn minimum_power(&self, prev_block_hash: &CryptoHash) -> Result { + let epoch_manager = self.read(); + epoch_manager.minimum_power(prev_block_hash) + } + fn validator_frozen( + &self, + epoch_id: &EpochId, + last_block_hash: &CryptoHash, + account_id: &AccountId, + ) -> Result, EpochError> { + let epoch_manager = self.read(); + let last_block_info = epoch_manager.get_block_info(last_block_hash)?; + if last_block_info.slashed().contains_key(account_id) { + return Ok(None); + } + let epoch_info = epoch_manager.get_epoch_info(epoch_id)?; + Ok(epoch_info.get_validator_id(account_id).map(|id| epoch_info.validator_frozen(*id))) + } + + fn validator_total_frozen( + &self, + epoch_id: &EpochId, + last_block_hash: &CryptoHash, + ) -> Result { + let epoch_manager = self.read(); + let last_block_info = epoch_manager.get_block_info(last_block_hash)?; + let epoch_info = epoch_manager.get_epoch_info(epoch_id)?; + Ok(epoch_info + .validators_iter() + .filter(|info| !last_block_info.slashed().contains_key(info.account_id())) + .map(|info| info.frozen()) + .sum()) + } + + fn minimum_frozen(&self, prev_block_hash: &CryptoHash) -> Result { + let epoch_manager = self.read(); + epoch_manager.minimum_frozen(prev_block_hash) + } + +} + +/// Tracks epoch information across different forks, such as validators. +/// Note: that even after garbage collection, the data about genesis epoch should be in the store. +pub struct EpochManager { + store: Store, + /// Current epoch config. + config: AllEpochConfig, + reward_calculator: RewardCalculator, + /// Genesis protocol version. Useful when there are protocol upgrades. + genesis_protocol_version: ProtocolVersion, + + /// Cache of epoch information. + epochs_info: SyncLruCache>, + /// Cache of block information. + blocks_info: SyncLruCache>, + /// Cache of epoch id to epoch start height + epoch_id_to_start: SyncLruCache, + /// Epoch validators ordered by `block_producer_settlement`. + epoch_validators_ordered: SyncLruCache>, + /// Unique validators ordered by `block_producer_settlement`. + epoch_validators_ordered_unique: SyncLruCache>, + + /// Unique chunk producers. + epoch_chunk_producers_unique: SyncLruCache>, + /// Aggregator that keeps statistics about the current epoch. It’s data are + /// synced up to the last final block. The information are updated by + /// [`Self::update_epoch_info_aggregator_upto_final`] method. To get + /// statistics up to a last block use + /// [`Self::get_epoch_info_aggregator_upto_last`] method. + epoch_info_aggregator: EpochInfoAggregator, + /// Largest final height. Monotonically increasing. + largest_final_height: BlockHeight, + + /// Counts loop iterations inside of aggregate_epoch_info_upto method. + /// Used for tests as a bit of white-box testing. + #[cfg(test)] + epoch_info_aggregator_loop_counter: std::sync::atomic::AtomicUsize, +} + +impl EpochManager { + pub fn new_from_genesis_config( + store: Store, + genesis_config: &GenesisConfig, + ) -> Result { + Self::new_from_genesis_config_with_test_overrides(store, genesis_config, None) + } + + pub fn new_from_genesis_config_with_test_overrides( + store: Store, + genesis_config: &GenesisConfig, + test_overrides: Option, + ) -> Result { + let reward_calculator = RewardCalculator::new(genesis_config); + let all_epoch_config = + Self::new_all_epoch_config_with_test_overrides(genesis_config, test_overrides); + let validators = genesis_config.validators(); + // Transforming ValidatorPowerAndFrozen to ValidatorPower + let power_validators: Vec = validators.clone().into_iter().map(|validator| { + match validator { + ValidatorPowerAndFrozen::V1(v) => { + ValidatorPower::new_v1(v.account_id, v.public_key, v.power) + }, + } + }).collect(); + let frozen_validators: Vec = validators.clone().into_iter().map(|validator| { + match validator { + ValidatorPowerAndFrozen::V1(v) => { + ValidatorFrozen::new_v1(v.account_id, v.public_key, v.frozen) +}, + } + }).collect(); + Self::new( + store, + all_epoch_config, + genesis_config.protocol_version, + reward_calculator, + power_validators, + frozen_validators, + ) + } + + pub fn new_arc_handle(store: Store, genesis_config: &GenesisConfig) -> Arc { + Self::new_arc_handle_with_test_overrides(store, genesis_config, None) + } + + pub fn new_arc_handle_with_test_overrides( + store: Store, + genesis_config: &GenesisConfig, + test_overrides: Option, + ) -> Arc { + Arc::new( + Self::new_from_genesis_config_with_test_overrides( + store, + genesis_config, + test_overrides, + ) + .unwrap() + .into_handle(), + ) + } + + + fn new_all_epoch_config_with_test_overrides( + genesis_config: &GenesisConfig, + test_overrides: Option, + ) -> AllEpochConfig { + let initial_epoch_config = EpochConfig::from(genesis_config); + let epoch_config = AllEpochConfig::new_with_test_overrides( + genesis_config.use_production_config(), + initial_epoch_config, + &genesis_config.chain_id, + test_overrides, + ); + epoch_config + } + + pub fn new( + store: Store, + config: AllEpochConfig, + genesis_protocol_version: ProtocolVersion, + reward_calculator: RewardCalculator, + power_validators: Vec, + frozen_validators: Vec, + ) -> Result { + let validator_reward = + HashMap::from([(reward_calculator.protocol_treasury_account.clone(), 0u128)]); + let epoch_info_aggregator = store + .get_ser(DBCol::EpochInfo, AGGREGATOR_KEY) + .map_err(EpochError::from)? + .unwrap_or_default(); + let mut epoch_manager = EpochManager { + store, + config, + reward_calculator, + genesis_protocol_version, + epochs_info: SyncLruCache::new(EPOCH_CACHE_SIZE), + blocks_info: SyncLruCache::new(BLOCK_CACHE_SIZE), + epoch_id_to_start: SyncLruCache::new(EPOCH_CACHE_SIZE), + epoch_validators_ordered: SyncLruCache::new(EPOCH_CACHE_SIZE), + epoch_validators_ordered_unique: SyncLruCache::new(EPOCH_CACHE_SIZE), + epoch_chunk_producers_unique: SyncLruCache::new(EPOCH_CACHE_SIZE), + epoch_info_aggregator, + #[cfg(test)] + epoch_info_aggregator_loop_counter: Default::default(), + largest_final_height: 0, + }; + let genesis_epoch_id = EpochId::default(); + if !epoch_manager.has_epoch_info(&genesis_epoch_id)? { + // Missing genesis epoch, means that there is no validator initialize yet. + let genesis_epoch_config = + epoch_manager.config.for_protocol_version(genesis_protocol_version); + let epoch_info = proposals_to_epoch_info( + &genesis_epoch_config, + [0; 32], + &EpochInfo::default(), + power_validators.clone(), + frozen_validators.clone(), + HashMap::default(), + validator_reward.clone(), + 0, + genesis_protocol_version, + genesis_protocol_version, + )?; + // Dummy block info. + // Artificial block we add to simplify implementation: dummy block is the + // parent of genesis block that points to itself. + // If we view it as block in epoch -1 and height -1, it naturally extends the + // EpochId formula using T-2 for T=1, and height field is unused. + let the_block_info = BlockInfo::new( + Default::default(), + 0, + 0, + Default::default(), + Default::default(), + power_validators.clone(), + frozen_validators.clone(), + vec![], + vec![], + 0, + 0, + 0, + //customized by james savechives + Default::default(), + vec![], + Default::default(), + vec![], + vec![], + vec![], + Default::default(), + Default::default(), + Default::default(), + validator_reward, + 0, + 0, + power_validators, + frozen_validators, + Default::default(), + Default::default() + ); + let block_info = Arc::new(the_block_info); + let mut store_update = epoch_manager.store.store_update(); + epoch_manager.save_epoch_info( + &mut store_update, + &genesis_epoch_id, + Arc::new(epoch_info), + )?; + epoch_manager.save_block_info(&mut store_update, block_info.clone())?; + store_update.commit()?; + + } + Ok(epoch_manager) + } + + pub fn into_handle(self) -> EpochManagerHandle { + let inner = Arc::new(RwLock::new(self)); + EpochManagerHandle { inner } + } + + /// Only used in mock node + /// Copy the necessary epoch info related to `block_hash` from `source_epoch_manager` to + /// the current epoch manager. + /// Note that this function doesn't copy info stored in EpochInfoAggregator, so `block_hash` must be + /// the last block in an epoch in order for the epoch manager to work properly after this function + /// is called + pub fn copy_epoch_info_as_of_block( + &mut self, + block_hash: &CryptoHash, + source_epoch_manager: &EpochManager, + ) -> Result<(), EpochError> { + let block_info = source_epoch_manager.get_block_info(block_hash)?; + let prev_hash = block_info.prev_hash(); + let epoch_id = &source_epoch_manager.get_epoch_id_from_prev_block(prev_hash)?; + let next_epoch_id = &source_epoch_manager.get_next_epoch_id_from_prev_block(prev_hash)?; + let mut store_update = self.store.store_update(); + self.save_epoch_info( + &mut store_update, + epoch_id, + source_epoch_manager.get_epoch_info(epoch_id)?, + )?; + // save next epoch info too + self.save_epoch_info( + &mut store_update, + next_epoch_id, + source_epoch_manager.get_epoch_info(next_epoch_id)?, + )?; + // save next next epoch info if the block is the last block + if source_epoch_manager.is_next_block_epoch_start(block_hash)? { + let next_next_epoch_id = + source_epoch_manager.get_next_epoch_id_from_prev_block(block_hash)?; + self.save_epoch_info( + &mut store_update, + &next_next_epoch_id, + source_epoch_manager.get_epoch_info(&next_next_epoch_id)?, + )?; + } + + // save block info for the first block in the epoch + let epoch_first_block = block_info.epoch_first_block(); + self.save_block_info( + &mut store_update, + source_epoch_manager.get_block_info(epoch_first_block)?, + )?; + + self.save_block_info(&mut store_update, block_info)?; + + self.save_epoch_start( + &mut store_update, + epoch_id, + source_epoch_manager.get_epoch_start_from_epoch_id(epoch_id)?, + )?; + + store_update.commit()?; + Ok(()) + } + + pub fn init_after_epoch_sync( + &mut self, + prev_epoch_first_block_info: BlockInfo, + prev_epoch_prev_last_block_info: BlockInfo, + prev_epoch_last_block_info: BlockInfo, + prev_epoch_id: &EpochId, + prev_epoch_info: EpochInfo, + epoch_id: &EpochId, + epoch_info: EpochInfo, + next_epoch_id: &EpochId, + next_epoch_info: EpochInfo, + ) -> Result { + let mut store_update = self.store.store_update(); + self.save_block_info(&mut store_update, Arc::new(prev_epoch_first_block_info))?; + self.save_block_info(&mut store_update, Arc::new(prev_epoch_prev_last_block_info))?; + self.save_block_info(&mut store_update, Arc::new(prev_epoch_last_block_info))?; + self.save_epoch_info(&mut store_update, prev_epoch_id, Arc::new(prev_epoch_info))?; + self.save_epoch_info(&mut store_update, epoch_id, Arc::new(epoch_info))?; + self.save_epoch_info(&mut store_update, next_epoch_id, Arc::new(next_epoch_info))?; + // TODO #3488 + // put unreachable! here to avoid warnings + unreachable!(); + // Ok(store_update) + } + + /// When computing validators to kickout, we exempt some validators first so that + /// the total stake of exempted validators exceed a threshold. This is to make sure + /// we don't kick out too many validators in case of network instability. + /// We also make sure that these exempted validators were not kicked out in the last epoch, + /// so it is guaranteed that they will stay as validators after this epoch. + fn compute_exempted_kickout( + epoch_info: &EpochInfo, + validator_block_chunk_stats: &HashMap, + total_stake: Balance, + exempt_perc: u8, + prev_validator_kickout: &HashMap, + ) -> HashSet { + // We want to make sure the total stake of validators that will be kicked out in this epoch doesn't exceed + // config.validator_max_kickout_stake_ratio of total stake. + // To achieve that, we sort all validators by their average uptime (average of block and chunk + // uptime) and add validators to `exempted_validators` one by one, from high uptime to low uptime, + // until the total excepted stake exceeds the ratio of total stake that we need to keep. + // Later when we perform the check to kick out validators, we don't kick out validators in + // exempted_validators. + let mut exempted_validators = HashSet::new(); + if checked_feature!("stable", MaxKickoutStake, epoch_info.protocol_version()) { + let min_keep_stake = total_stake * (exempt_perc as u128) / 100; + let mut sorted_validators = validator_block_chunk_stats + .iter() + .map(|(account, stats)| { + let production_ratio = + if stats.block_stats.expected == 0 && stats.chunk_stats.expected == 0 { + Rational64::from_integer(1) + } else if stats.block_stats.expected == 0 { + Rational64::new( + stats.chunk_stats.produced as i64, + stats.chunk_stats.expected as i64, + ) + } else if stats.chunk_stats.expected == 0 { + Rational64::new( + stats.block_stats.produced as i64, + stats.block_stats.expected as i64, + ) + } else { + (Rational64::new( + stats.chunk_stats.produced as i64, + stats.chunk_stats.expected as i64, + ) + Rational64::new( + stats.block_stats.produced as i64, + stats.block_stats.expected as i64, + )) / 2 + }; + (account, production_ratio) + }) + .collect::>(); + sorted_validators.sort_by_key(|a| a.1); + let mut exempted_stake: Balance = 0; + for (account_id, _) in sorted_validators.into_iter().rev() { + if exempted_stake >= min_keep_stake { + break; + } + if !prev_validator_kickout.contains_key(account_id) { + exempted_stake += epoch_info + .get_validator_by_account(account_id) + .map(|v| v.frozen()) + .unwrap_or_default(); + exempted_validators.insert(account_id.clone()); + } + } + } + exempted_validators + } + + /// # Parameters + /// epoch_info + /// block_validator_tracker + /// chunk_validator_tracker + /// + /// slashed: set of slashed validators + /// prev_validator_kickout: previously kicked out + /// + /// # Returns + /// (set of validators to kickout, set of validators to reward with stats) + /// + /// - Slashed validators are ignored (they are handled separately) + /// - The total stake of validators that will be kicked out will not exceed + /// config.validator_max_kickout_stake_perc of total stake of all validators. This is + /// to ensure we don't kick out too many validators in case of network instability. + /// - A validator is kicked out if he produced too few blocks or chunks + /// - If all validators are either previously kicked out or to be kicked out, we choose one not to + /// kick out + fn compute_kickout_info( + config: &EpochConfig, + epoch_info: &EpochInfo, + block_validator_tracker: &HashMap, + chunk_validator_tracker: &HashMap>, + slashed: &HashMap, + prev_validator_kickout: &HashMap, + ) -> (HashMap, HashMap) + { + let block_producer_kickout_threshold = config.block_producer_kickout_threshold; + let chunk_producer_kickout_threshold = config.chunk_producer_kickout_threshold; + let mut validator_block_chunk_stats = HashMap::new(); + let mut total_stake: Balance = 0; + let mut maximum_block_prod = 0; + let mut max_validator = None; + + for (i, v) in epoch_info.validators_iter().enumerate() { + let account_id = v.account_id(); + if slashed.contains_key(account_id) { + continue; + } + let block_stats = block_validator_tracker + .get(&(i as u64)) + .unwrap_or_else(|| &ValidatorStats { expected: 0, produced: 0 }) + .clone(); + let mut chunk_stats = ValidatorStats { produced: 0, expected: 0 }; + for (_, tracker) in chunk_validator_tracker.iter() { + if let Some(stat) = tracker.get(&(i as u64)) { + chunk_stats.expected += stat.expected; + chunk_stats.produced += stat.produced; + } + } + total_stake += v.frozen(); + let is_already_kicked_out = prev_validator_kickout.contains_key(account_id); + if (max_validator.is_none() || block_stats.produced > maximum_block_prod) + && !is_already_kicked_out + { + maximum_block_prod = block_stats.produced; + max_validator = Some(account_id.clone()); + } + validator_block_chunk_stats + .insert(account_id.clone(), BlockChunkValidatorStats { block_stats, chunk_stats }); + } + + let exempt_perc = + 100_u8.checked_sub(config.validator_max_kickout_stake_perc).unwrap_or_default(); + let exempted_validators = Self::compute_exempted_kickout( + epoch_info, + &validator_block_chunk_stats, + total_stake, + exempt_perc, + prev_validator_kickout, + ); + let mut all_kicked_out = true; + let mut validator_kickout = HashMap::new(); + for (account_id, stats) in validator_block_chunk_stats.iter() { + if exempted_validators.contains(account_id) { + all_kicked_out = false; + continue; + } + if stats.block_stats.produced * 100 + < u64::from(block_producer_kickout_threshold) * stats.block_stats.expected + { + validator_kickout.insert( + account_id.clone(), + ValidatorKickoutReason::NotEnoughBlocks { + produced: stats.block_stats.produced, + expected: stats.block_stats.expected, + }, + ); + } + if stats.chunk_stats.produced * 100 + < u64::from(chunk_producer_kickout_threshold) * stats.chunk_stats.expected + { + validator_kickout.entry(account_id.clone()).or_insert_with(|| { + ValidatorKickoutReason::NotEnoughChunks { + produced: stats.chunk_stats.produced, + expected: stats.chunk_stats.expected, + } + }); + } + let is_already_kicked_out = prev_validator_kickout.contains_key(account_id); + if !validator_kickout.contains_key(account_id) { + if !is_already_kicked_out { + all_kicked_out = false; + } + } + } + if all_kicked_out { + tracing::info!(target:"epoch_manager", "We are about to kick out all validators in the next two epochs, so we are going to save one {:?}", max_validator); + if let Some(validator) = max_validator { + validator_kickout.remove(&validator); + } + } + for account_id in validator_kickout.keys() { + validator_block_chunk_stats.remove(account_id); + } + (validator_kickout, validator_block_chunk_stats) + } + + fn collect_blocks_info( + &mut self, + last_block_info: &BlockInfo, + last_block_hash: &CryptoHash, + ) -> Result { + let epoch_info = self.get_epoch_info(last_block_info.epoch_id())?; + let next_epoch_id = self.get_next_epoch_id(last_block_hash)?; + let next_epoch_info = self.get_epoch_info(&next_epoch_id)?; + + let EpochInfoAggregator { + block_tracker: block_validator_tracker, + shard_tracker: chunk_validator_tracker, + all_power_proposals, + all_frozen_proposals, + version_tracker, + .. + } = self.get_epoch_info_aggregator_upto_last(last_block_hash)?; + + let mut power_proposals = vec![]; + let mut frozen_proposals = vec![]; + let mut validator_kickout = HashMap::new(); + + // Next protocol version calculation. + // Implements https://github.com/nearprotocol/NEPs/pull/64/files#diff-45f773511fe4321b446c3c4226324873R76 + let mut versions = HashMap::new(); + for (validator_id, version) in version_tracker { + let stake = epoch_info.validator_frozen(validator_id); + *versions.entry(version).or_insert(0) += stake; + } + let total_block_producer_stake: u128 = epoch_info + .block_producers_settlement() + .iter() + .copied() + .collect::>() + .iter() + .map(|&id| epoch_info.validator_frozen(id)) + .sum(); + + let protocol_version = + if epoch_info.protocol_version() >= UPGRADABILITY_FIX_PROTOCOL_VERSION { + next_epoch_info.protocol_version() + } else { + epoch_info.protocol_version() + }; + + let config = self.config.for_protocol_version(protocol_version); + // Note: non-deterministic iteration is fine here, there can be only one + // version with large enough stake. + let next_version = if let Some((version, stake)) = + versions.into_iter().max_by_key(|&(_version, stake)| stake) + { + let numer = *config.protocol_upgrade_stake_threshold.numer() as u128; + let denom = *config.protocol_upgrade_stake_threshold.denom() as u128; + let threshold = total_block_producer_stake * numer / denom; + if stake > threshold { + version + } else { + protocol_version + } + } else { + protocol_version + }; + // Gather slashed validators and add them to kick out first. + let slashed_validators = last_block_info.slashed(); + for (account_id, _) in slashed_validators.iter() { + validator_kickout.insert(account_id.clone(), ValidatorKickoutReason::Slashed); + } + + for (account_id, power_proposal) in all_power_proposals { + if !slashed_validators.contains_key(&account_id) { + if power_proposal.power() == 0 + && *next_epoch_info.power_change().get(&account_id).unwrap_or(&0) != 0 + { + validator_kickout.insert(account_id.clone(), ValidatorKickoutReason::Unpowered); + } + power_proposals.push(power_proposal.clone()); + } + } + + for (account_id, frozen_proposal) in all_frozen_proposals { + if !slashed_validators.contains_key(&account_id) { + if frozen_proposal.frozen() == 0 + && *next_epoch_info.frozen_change().get(&account_id).unwrap_or(&0) != 0 + { + validator_kickout.insert(account_id.clone(), ValidatorKickoutReason::Unfrozen); + } + frozen_proposals.push(frozen_proposal.clone()); + } + } + + let prev_epoch_last_block_hash = + *self.get_block_info(last_block_info.epoch_first_block())?.prev_hash(); + let prev_validator_kickout = next_epoch_info.validator_kickout(); + + let config = self.config.for_protocol_version(epoch_info.protocol_version()); + // Compute kick outs for validators who are offline. + let (kickout, validator_block_chunk_stats) = Self::compute_kickout_info( + &config, + &epoch_info, + &block_validator_tracker, + &chunk_validator_tracker, + slashed_validators, + prev_validator_kickout, + ); + validator_kickout.extend(kickout); + // debug!( + // target: "epoch_manager", + // "All power proposals: {:?}, All frozen proposals: {:?}, Kickouts: {:?}, Block Tracker: {:?}, Shard Tracker: {:?}", + // all_power_proposals.clone(), all_frozen_proposals.clone(), validator_kickout.clone(), block_validator_tracker.clone(), chunk_validator_tracker.clone() + // ); + + Ok(EpochSummary { + prev_epoch_last_block_hash, + all_power_proposals: power_proposals, + all_frozen_proposals: frozen_proposals, + validator_kickout, + validator_block_chunk_stats, + next_version, + }) + } + /// Finalize block + fn finalize_block_summary_for_block( + &mut self, + block_info: &BlockInfo, + last_block_hash: &CryptoHash, + rng_seed: RngSeed, + ) -> Result { + + let validator_stake = + block_info.validators_iter().map(|r| r.account_and_frozen()).collect::>(); + + let ( + all_power_proposals, + all_frozen_proposals, + validator_kickout + ) = match block_info { // Assuming last_block_summary is wrapped in an Arc + BlockInfo::V1(summary) => { + // Now you can access the fields of BlockSummaryV1 through `summary` + (&summary.all_power_proposals,&summary.all_frozen_proposals,&summary.validator_kickout) + // Add more fields as needed + }, + BlockInfo::V2(summary) => { + // Now you can access the fields of BlockSummaryV1 through `summary` + (&summary.all_power_proposals,&summary.all_frozen_proposals,&summary.validator_kickout) + // Add more fields as needed + }, + }; + + let validator_block_chunk_stats = HashMap::default(); + let next_version = 1u16 as ProtocolVersion; + + let (validator_reward, minted_amount) = { + let last_epoch_last_block_hash = + *self.get_block_info(block_info.epoch_first_block())?.prev_hash(); + let last_block_in_last_epoch = self.get_block_info(&last_epoch_last_block_hash)?; + // assert!(block_info.timestamp_nanosec() > last_block_in_last_epoch.timestamp_nanosec()); + let epoch_duration = + block_info.timestamp_nanosec() - last_block_in_last_epoch.timestamp_nanosec(); + self.reward_calculator.calculate_reward( + validator_block_chunk_stats, + &validator_stake, + *block_info.total_supply(), + 0u32, + self.genesis_protocol_version, + epoch_duration, + ) + }; + let this_epoch_config = self.config.for_protocol_version(next_version); + let this_block_summary = match proposals_to_block_summary( + &this_epoch_config, + block_info.hash(), + &last_block_hash, + rng_seed, + &block_info, + all_power_proposals.to_vec(), + all_frozen_proposals.to_vec(), + validator_kickout.clone(), + validator_reward, + minted_amount, + next_version, + ) { + Ok(this_block_summary) => this_block_summary, + // Err(BlockError::ThresholdError { stake_sum, num_seats }) => { + // warn!(target: "epoch_manager", "Not enough stake for required number of seats (all validators tried to unstake?): amount = {} for {}", stake_sum, num_seats); + // return Err(BlockError::ThresholdError { stake_sum, num_seats }); + // } + // Err(BlockError::NotEnoughValidators { num_validators, num_shards }) => { + // warn!(target: "epoch_manager", "Not enough validators for required number of shards (all validators tried to unstake?): num_validators={} num_shards={}", num_validators, num_shards); + // return Err(BlockError::NotEnoughValidators { num_validators, num_shards }); + // } + // Err(err) => return Err(err), + _ => BlockSummary::default(), + }; + // This epoch info is computed for the epoch after next (T+2), + // where epoch_id of it is the hash of last block in this epoch (T). + // self.save_block_summary(store_update, &block_info.hash(), Arc::new(this_block_summary))?; + Ok(this_block_summary) + } + /// Finalizes epoch (T), where given last block hash is given, and returns next next epoch id (T + 2). + fn finalize_epoch( + &mut self, + store_update: &mut StoreUpdate, + block_info: &BlockInfo, + last_block_hash: &CryptoHash, + rng_seed: RngSeed, + ) -> Result<(), EpochError> { + let epoch_summary = self.collect_blocks_info(block_info, last_block_hash)?; + let epoch_info = self.get_epoch_info(block_info.epoch_id())?; + let epoch_protocol_version = epoch_info.protocol_version(); + let validator_stake = + epoch_info.validators_iter().map(|r| r.account_and_frozen()).collect::>(); + let next_epoch_id = self.get_next_epoch_id_from_info(block_info)?; + let next_epoch_info = self.get_epoch_info(&next_epoch_id)?; + self.save_epoch_validator_info(store_update, block_info.epoch_id(), &epoch_summary)?; + + let EpochSummary { + all_power_proposals, + all_frozen_proposals, + validator_kickout, + validator_block_chunk_stats, + next_version, + .. + } = epoch_summary; + + let (validator_reward, minted_amount) = { + let last_epoch_last_block_hash = + *self.get_block_info(block_info.epoch_first_block())?.prev_hash(); + let last_block_in_last_epoch = self.get_block_info(&last_epoch_last_block_hash)?; + assert!(block_info.timestamp_nanosec() > last_block_in_last_epoch.timestamp_nanosec()); + let epoch_duration = + block_info.timestamp_nanosec() - last_block_in_last_epoch.timestamp_nanosec(); + self.reward_calculator.calculate_reward( + validator_block_chunk_stats, + &validator_stake, + *block_info.total_supply(), + epoch_protocol_version, + self.genesis_protocol_version, + epoch_duration, + ) + }; + let next_next_epoch_config = self.config.for_protocol_version(next_version); + let next_next_epoch_info = match proposals_to_epoch_info( + &next_next_epoch_config, + rng_seed, + &next_epoch_info, + all_power_proposals, + all_frozen_proposals, + validator_kickout, + validator_reward, + minted_amount, + next_version, + epoch_protocol_version, + ) { + Ok(next_next_epoch_info) => next_next_epoch_info, + Err(EpochError::ThresholdError { stake_sum, num_seats }) => { + warn!(target: "epoch_manager", "Not enough stake for required number of seats (all validators tried to unstake?): amount = {} for {}", stake_sum, num_seats); + let mut epoch_info = EpochInfo::clone(&next_epoch_info); + *epoch_info.epoch_height_mut() += 1; + epoch_info + } + Err(EpochError::NotEnoughValidators { num_validators, num_shards }) => { + warn!(target: "epoch_manager", "Not enough validators for required number of shards (all validators tried to unstake?): num_validators={} num_shards={}", num_validators, num_shards); + let mut epoch_info = EpochInfo::clone(&next_epoch_info); + *epoch_info.epoch_height_mut() += 1; + epoch_info + } + Err(err) => return Err(err), + }; + let next_next_epoch_id = EpochId(*last_block_hash); + debug!(target: "epoch_manager", "next next epoch height: {}, id: {:?}, protocol version: {} shard layout: {:?} config: {:?}", + next_next_epoch_info.epoch_height(), + &next_next_epoch_id, + next_next_epoch_info.protocol_version(), + self.config.for_protocol_version(next_next_epoch_info.protocol_version()).shard_layout, + self.config.for_protocol_version(next_next_epoch_info.protocol_version())); + // This epoch info is computed for the epoch after next (T+2), + // where epoch_id of it is the hash of last block in this epoch (T). + self.save_epoch_info(store_update, &next_next_epoch_id, Arc::new(next_next_epoch_info))?; + Ok(()) + } + + pub fn record_block_info( + &mut self, + mut block_info: BlockInfo, + rng_seed: RngSeed, + ) -> Result { + let current_hash = *block_info.hash(); + let mut store_update = self.store.store_update(); + // Check that we didn't record this block yet. + if !self.has_block_info(¤t_hash)? { + if block_info.prev_hash() == &CryptoHash::default() { + // This is genesis block, we special case as new epoch. + assert_eq!(block_info.power_proposals_iter().len(), 0); + let pre_genesis_epoch_id = EpochId::default(); + let genesis_epoch_info = self.get_epoch_info(&pre_genesis_epoch_id)?; + self.save_block_info(&mut store_update, Arc::new(block_info.clone()))?; + self.save_epoch_info( + &mut store_update, + &EpochId(current_hash), + genesis_epoch_info, + )?; + } else { + let prev_block_info = self.get_block_info(block_info.prev_hash())?; + + let mut is_epoch_start = false; + if prev_block_info.prev_hash() == &CryptoHash::default() { + // This is first real block, starts the new epoch. + *block_info.epoch_id_mut() = EpochId::default(); + *block_info.epoch_first_block_mut() = current_hash; + is_epoch_start = true; + } else if self.is_next_block_in_next_epoch(&prev_block_info)? { + // Current block is in the new epoch, finalize the one in prev_block. + *block_info.epoch_id_mut() = + self.get_next_epoch_id_from_info(&prev_block_info)?; + *block_info.epoch_first_block_mut() = current_hash; + is_epoch_start = true; + } else { + // Same epoch as parent, copy epoch_id and epoch_start_height. + *block_info.epoch_id_mut() = prev_block_info.epoch_id().clone(); + *block_info.epoch_first_block_mut() = *prev_block_info.epoch_first_block(); + } + let epoch_info = self.get_epoch_info(block_info.epoch_id())?; + + // Keep `slashed` from previous block if they are still in the epoch info stake change + // (e.g. we need to keep track that they are still slashed, because when we compute + // returned stake we are skipping account ids that are slashed in `stake_change`). + for (account_id, slash_state) in prev_block_info.slashed() { + if is_epoch_start { + if slash_state == &SlashState::DoubleSign + || slash_state == &SlashState::Other + { + block_info + .slashed_mut() + .entry(account_id.clone()) + .or_insert(SlashState::AlreadySlashed); + } else if epoch_info.frozen_change().contains_key(account_id) { + block_info + .slashed_mut() + .entry(account_id.clone()) + .or_insert_with(|| slash_state.clone()); + } + } else { + block_info + .slashed_mut() + .entry(account_id.clone()) + .and_modify(|e| { + if let SlashState::Other = slash_state { + *e = SlashState::Other; + } + }) + .or_insert_with(|| slash_state.clone()); + } + } + + if is_epoch_start { + self.save_epoch_start( + &mut store_update, + block_info.epoch_id(), + block_info.height(), + )?; + } + + let block_info = Arc::new(block_info); + // Save current block info. + self.save_block_info(&mut store_update, Arc::clone(&block_info))?; + + // let block_summary = Arc::new(block_summary); + // // Save current block summary + // self.save_block_summary(&mut store_update, &block_info.hash().clone(), Arc::clone(&block_summary))?; + + if block_info.last_finalized_height() > self.largest_final_height { + self.largest_final_height = block_info.last_finalized_height(); + + // Update epoch info aggregator. We only update the if + // there is a change in the last final block. This way we + // never need to rollback any information in + // self.epoch_info_aggregator. + self.update_epoch_info_aggregator_upto_final( + block_info.last_final_block_hash(), + &mut store_update, + )?; + } + + // If this is the last block in the epoch, finalize this epoch. + if self.is_next_block_in_next_epoch(&block_info)? { + self.finalize_epoch(&mut store_update, &block_info.clone(), ¤t_hash.clone(), rng_seed.clone())?; + } + + } + } + Ok(store_update) + } + + /// Given epoch id and height, returns validator information that suppose to produce + /// the block at that height. We don't require caller to know about EpochIds. + pub fn get_block_producer_info( + &self, + epoch_id: &EpochId, + height: BlockHeight, + ) -> Result { + let epoch_info = self.get_epoch_info(epoch_id)?; + let validator_id = Self::block_producer_from_info(&epoch_info, height); + + Ok(epoch_info.get_validator(validator_id)) + } + + pub fn get_block_producer_info_by_hash( + &self, + block_hash: &CryptoHash, + // height: BlockHeight, + ) -> Result { + let block_info = self.get_block_info(block_hash)?; + // let current_height = block_info.height(); + // if current_height +1 != height { + // return Err(BlockError::BlockOutOfBounds(*block_hash)); + // } + let random_value = block_info.random_value(); + let validators = block_info.validators_iter(); + Self::choose_validator_vrf(validators,Self::hash_to_bigint(random_value)) + } + + fn hash_to_bigint(hash: &CryptoHash) -> BigInt { + BigInt::from_bytes_be(num_bigint::Sign::Plus, hash.as_ref()) + } + + fn choose_validator_vrf(validators_iter: ValidatorPowerAndFrozenIter, random_value: BigInt) -> Result { + let mut total_weight: BigInt = Zero::zero(); + for validator in validators_iter.clone() { + let validator_power = match validator { + ValidatorPowerAndFrozen::V1(v) => v.power.to_bigint().unwrap_or_else(Zero::zero), + }; + total_weight += validator_power; + } + + if total_weight.is_zero() { + return Err(BlockError::ValidatorTotalPowerError(String::from("Total Power is zero"))); + } + + let mut cumulative_weight = Zero::zero(); + let target = random_value % &total_weight; + + for validator in validators_iter { + let validator_power = match validator { + ValidatorPowerAndFrozen::V1(ref v) => v.power.to_bigint().unwrap_or_else(Zero::zero), + }; + cumulative_weight += &validator_power; + if target < cumulative_weight { + return Ok(validator.clone()); + } + } + + return Err(BlockError::NoAvailableValidator(String::from("Block Producer is not available"))); + } + + /// Returns settlement of all block producers in current epoch, with indicator on whether they are slashed or not. + pub fn get_all_block_producers_settlement( + &self, + epoch_id: &EpochId, + last_known_block_hash: &CryptoHash, + ) -> Result, EpochError> { + // TODO(3674): Revisit this when we enable slashing + self.epoch_validators_ordered.get_or_try_put(epoch_id.clone(), |epoch_id| { + let block_info = self.get_block_info(last_known_block_hash)?; + let epoch_info = self.get_epoch_info(epoch_id)?; + let result = epoch_info + .block_producers_settlement() + .iter() + .map(|&validator_id| { + let validator_stake = epoch_info.get_validator(validator_id); + let is_slashed = + block_info.slashed().contains_key(validator_stake.account_id()); + (validator_stake, is_slashed) + }) + .collect(); + Ok(result) + }) + } + + /// Returns all unique block producers in current epoch sorted by account_id, with indicator on whether they are slashed or not. + pub fn get_all_block_producers_ordered( + &self, + epoch_id: &EpochId, + last_known_block_hash: &CryptoHash, + ) -> Result, EpochError> { + self.epoch_validators_ordered_unique.get_or_try_put(epoch_id.clone(), |epoch_id| { + let settlement = + self.get_all_block_producers_settlement(epoch_id, last_known_block_hash)?; + let mut validators: HashSet = HashSet::default(); + let result = settlement + .iter() + .filter(|(validator_stake, _is_slashed)| { + let account_id = validator_stake.account_id(); + validators.insert(account_id.clone()) + }) + .cloned() + .collect(); + Ok(result) + }) + } + + /// Returns settlement of all chunk producers in the current epoch. + pub fn get_all_chunk_producers( + &self, + epoch_id: &EpochId, + ) -> Result, EpochError> { + self.epoch_chunk_producers_unique.get_or_try_put(epoch_id.clone(), |epoch_id| { + let mut producers: HashSet = HashSet::default(); + + // Collect unique chunk producers. + let epoch_info = self.get_epoch_info(epoch_id)?; + for chunk_producers in epoch_info.chunk_producers_settlement() { + producers.extend(chunk_producers); + } + + Ok(producers.iter().map(|producer_id| epoch_info.get_validator(*producer_id)).collect()) + }) + } + + /// Returns the list of chunk validators for the given shard_id and height. + pub fn get_chunk_validators( + &self, + epoch_id: &EpochId, + shard_id: ShardId, + height: BlockHeight, + ) -> Result, EpochError> { + let epoch_info = self.get_epoch_info(epoch_id)?; + let chunk_validators_per_shard = epoch_info.sample_chunk_validators(height); + let chunk_validators = + chunk_validators_per_shard.get(shard_id as usize).ok_or_else(|| { + EpochError::ChunkValidatorSelectionError(format!( + "Invalid shard ID {} for height {}, epoch {:?} for chunk validation", + shard_id, height, epoch_id, + )) + })?; + Ok(chunk_validators + .iter() + .map(|(validator_id, seats)| { + (epoch_info.get_validator(*validator_id).take_account_id(), seats.clone()) + }) + .collect()) + } + + /// get_heuristic_block_approvers_ordered: block producers for epoch + /// get_all_block_producers_ordered: block producers for epoch, slashing info + /// get_all_block_approvers_ordered: block producers for epoch, slashing info, sometimes block producers for next epoch + pub fn get_heuristic_block_approvers_ordered( + &self, + epoch_id: &EpochId, + ) -> Result, EpochError> { + let epoch_info = self.get_epoch_info(epoch_id)?; + let mut result = vec![]; + let mut validators: HashSet = HashSet::new(); + for validator_id in epoch_info.block_producers_settlement().into_iter() { + let validator_stake = epoch_info.get_validator(*validator_id); + let account_id = validator_stake.account_id(); + if validators.insert(account_id.clone()) { + result.push(validator_stake.get_approval_frozen(false)); + } + } + + Ok(result) + } + + pub fn get_all_block_approvers_ordered( + &self, + parent_hash: &CryptoHash, + ) -> Result, EpochError> { + let current_epoch_id = self.get_epoch_id_from_prev_block(parent_hash)?; + let next_epoch_id = self.get_next_epoch_id_from_prev_block(parent_hash)?; + + let mut settlement = + self.get_all_block_producers_settlement(¤t_epoch_id, parent_hash)?.to_vec(); + + let settlement_epoch_boundary = settlement.len(); + + let block_info = self.get_block_info(parent_hash)?; + if self.next_block_need_approvals_from_next_epoch(&block_info)? { + settlement.extend( + self.get_all_block_producers_settlement(&next_epoch_id, parent_hash)? + .iter() + .cloned(), + ); + } + + let mut result = vec![]; + let mut validators: HashMap = HashMap::default(); + for (ord, (validator_frozen, is_slashed)) in settlement.into_iter().enumerate() { + let account_id = validator_frozen.account_id(); + match validators.get(account_id) { + None => { + validators.insert(account_id.clone(), result.len()); + result.push(( + validator_frozen.get_approval_frozen(ord >= settlement_epoch_boundary), + is_slashed, + )); + } + Some(old_ord) => { + if ord >= settlement_epoch_boundary { + result[*old_ord].0.frozen_next_epoch = validator_frozen.frozen(); + }; + } + }; + } + Ok(result) + } + + /// For given epoch_id, height and shard_id returns validator that is chunk producer. + pub fn get_chunk_producer_info( + &self, + epoch_id: &EpochId, + height: BlockHeight, + shard_id: ShardId, + ) -> Result { + let epoch_info = self.get_epoch_info(epoch_id)?; + let validator_id = Self::chunk_producer_from_info(&epoch_info, height, shard_id); + Ok(epoch_info.get_validator(validator_id)) + } + + /// Returns validator for given account id for given epoch. + /// We don't require caller to know about EpochIds. Doesn't account for slashing. + pub fn get_validator_by_account_id( + &self, + epoch_id: &EpochId, + account_id: &AccountId, + ) -> Result { + let epoch_info = self.get_epoch_info(epoch_id)?; + epoch_info + .get_validator_by_account(account_id) + .ok_or_else(|| EpochError::NotAValidator(account_id.clone(), epoch_id.clone())) + } + + /// Returns fisherman for given account id for given epoch. + pub fn get_fisherman_by_account_id( + &self, + epoch_id: &EpochId, + account_id: &AccountId, + ) -> Result { + let epoch_info = self.get_epoch_info(epoch_id)?; + epoch_info + .get_fisherman_by_account(account_id) + .ok_or_else(|| EpochError::NotAValidator(account_id.clone(), epoch_id.clone())) + } + + pub fn get_epoch_id(&self, block_hash: &CryptoHash) -> Result { + Ok(self.get_block_info(block_hash)?.epoch_id().clone()) + } + + pub fn get_next_epoch_id(&self, block_hash: &CryptoHash) -> Result { + let block_info = self.get_block_info(block_hash)?; + self.get_next_epoch_id_from_info(&block_info) + } + + pub fn get_prev_epoch_id(&self, block_hash: &CryptoHash) -> Result { + let epoch_first_block = *self.get_block_info(block_hash)?.epoch_first_block(); + let prev_epoch_last_hash = *self.get_block_info(&epoch_first_block)?.prev_hash(); + self.get_epoch_id(&prev_epoch_last_hash) + } + + pub fn get_epoch_info_from_hash( + &self, + block_hash: &CryptoHash, + ) -> Result, EpochError> { + let epoch_id = self.get_epoch_id(block_hash)?; + self.get_epoch_info(&epoch_id) + } + + pub fn cares_about_shard_from_prev_block( + &self, + parent_hash: &CryptoHash, + account_id: &AccountId, + shard_id: ShardId, + ) -> Result { + let epoch_id = self.get_epoch_id_from_prev_block(parent_hash)?; + self.cares_about_shard_in_epoch(epoch_id, account_id, shard_id) + } + + // `shard_id` always refers to a shard in the current epoch that the next block from `parent_hash` belongs + // If shard layout will change next epoch, returns true if it cares about any shard + // that `shard_id` will split to + pub fn cares_about_shard_next_epoch_from_prev_block( + &self, + parent_hash: &CryptoHash, + account_id: &AccountId, + shard_id: ShardId, + ) -> Result { + let next_epoch_id = self.get_next_epoch_id_from_prev_block(parent_hash)?; + if self.will_shard_layout_change(parent_hash)? { + let shard_layout = self.get_shard_layout(&next_epoch_id)?; + let split_shards = shard_layout + .get_children_shards_ids(shard_id) + .expect("all shard layouts expect the first one must have a split map"); + for next_shard_id in split_shards { + if self.cares_about_shard_in_epoch( + next_epoch_id.clone(), + account_id, + next_shard_id, + )? { + return Ok(true); + } + } + Ok(false) + } else { + self.cares_about_shard_in_epoch(next_epoch_id, account_id, shard_id) + } + } + + /// Returns true if next block after given block hash is in the new epoch. + pub fn is_next_block_epoch_start(&self, parent_hash: &CryptoHash) -> Result { + let block_info = self.get_block_info(parent_hash)?; + self.is_next_block_in_next_epoch(&block_info) + } + + /// Relies on the fact that last block hash of an epoch is an EpochId of next next epoch. + /// If this block is the last one in some epoch, and we fully processed it, there will be `EpochInfo` record with `hash` key. + fn is_last_block_in_finished_epoch(&self, hash: &CryptoHash) -> Result { + match self.get_epoch_info(&EpochId(*hash)) { + Ok(_) => Ok(true), + Err(EpochError::IOErr(msg)) => Err(EpochError::IOErr(msg)), + Err(EpochError::MissingBlock(_)) => Ok(false), + Err(err) => { + warn!(target: "epoch_manager", ?err, "Unexpected error in is_last_block_in_finished_epoch"); + Ok(false) + } + } + } + + pub fn get_epoch_id_from_prev_block( + &self, + parent_hash: &CryptoHash, + ) -> Result { + if self.is_next_block_epoch_start(parent_hash)? { + self.get_next_epoch_id(parent_hash) + } else { + self.get_epoch_id(parent_hash) + } + } + + pub fn get_next_epoch_id_from_prev_block( + &self, + parent_hash: &CryptoHash, + ) -> Result { + if self.is_next_block_epoch_start(parent_hash)? { + // Because we ID epochs based on the last block of T - 2, this is ID for next next epoch. + Ok(EpochId(*parent_hash)) + } else { + self.get_next_epoch_id(parent_hash) + } + } + + pub fn get_epoch_start_height( + &self, + block_hash: &CryptoHash, + ) -> Result { + let epoch_first_block = *self.get_block_info(block_hash)?.epoch_first_block(); + Ok(self.get_block_info(&epoch_first_block)?.height()) + } + + /// Compute stake return info based on the last block hash of the epoch that is just finalized + /// return the hashmap of account id to max_of_stakes, which is used in the calculation of account + /// updates. + /// + /// # Returns + /// If successful, a triple of (hashmap of account id to max of stakes in the past three epochs, + /// validator rewards in the last epoch, double sign slashing for the past epoch). + pub fn compute_power_return_info_for_block( + &self, + last_block_hash: &CryptoHash, + ) -> Result< + (HashMap, HashMap, HashMap, HashMap), + EpochError, + > { + let last_block_info = self.get_block_info(last_block_hash)?; + let validator_reward = last_block_info.validator_reward().clone(); + // Fetch last block info to get the slashed accounts. + + // Since stake changes for epoch T are stored in epoch info for T+2, the one stored by epoch_id + // is the prev_prev_stake_change. + let frozen_change = last_block_info.frozen_change().clone(); + // Power changes are similar like stake changes + let power_change = last_block_info.power_change().clone(); + + let all_power_changes =power_change.iter(); + let all_power_keys: HashSet<&AccountId> = all_power_changes.map(|(key, _)| key).collect(); + + let mut power_info = HashMap::new(); + for account_id in all_power_keys { + let new_power = *power_change.get(account_id).unwrap_or(&0); + power_info.insert(account_id.clone(), new_power); + } + + let all_frozen_changes = frozen_change.iter(); + let all_frozen_keys: HashSet<&AccountId> = all_frozen_changes.map(|(key, _)| key).collect(); + + let mut frozen_info = HashMap::new(); + for account_id in all_frozen_keys { + if last_block_info.slashed().contains_key(account_id) { + if !frozen_change.contains_key(account_id) + { + // slashed in prev_prev epoch so it is safe to return the remaining stake in case of + // a double sign without violating the staking invariant. + } else { + continue; + } + } + let new_frozen = *frozen_change.get(account_id).unwrap_or(&0); + frozen_info.insert(account_id.clone(), new_frozen); + } + + // let slashing_info = self.compute_double_sign_slashing_info(last_block_hash)?; + let slashing_info = HashMap::default(); + debug!(target: "epoch_manager", "stake_info: {:?}, frozen_info: {:?}, validator_reward: {:?}", power_info, frozen_info, validator_reward); + Ok((power_info, frozen_info, validator_reward, slashing_info)) + } + pub fn compute_power_return_info( + &self, + last_block_hash: &CryptoHash, + ) -> Result< + (HashMap, HashMap, HashMap, HashMap), + EpochError, + > { + let next_next_epoch_id = EpochId(*last_block_hash); + let validator_reward = self.get_epoch_info(&next_next_epoch_id)?.validator_reward().clone(); + + let next_epoch_id = self.get_next_epoch_id(last_block_hash)?; + let epoch_id = self.get_epoch_id(last_block_hash)?; + debug!(target: "epoch_manager", + "epoch id: {:?}, prev_epoch_id: {:?}, prev_prev_epoch_id: {:?}", + next_next_epoch_id, next_epoch_id, epoch_id + ); + // Fetch last block info to get the slashed accounts. + let last_block_info = self.get_block_info(last_block_hash)?; + // Since stake changes for epoch T are stored in epoch info for T+2, the one stored by epoch_id + // is the prev_prev_stake_change. + let prev_prev_frozen_change = self.get_epoch_info(&epoch_id)?.frozen_change().clone(); + let prev_frozen_change = self.get_epoch_info(&next_epoch_id)?.frozen_change().clone(); + let frozen_change = self.get_epoch_info(&next_next_epoch_id)?.frozen_change().clone(); + // Power changes are similar like stake changes + let prev_prev_power_change = self.get_epoch_info(&epoch_id)?.power_change().clone(); + let prev_power_change = self.get_epoch_info(&next_epoch_id)?.power_change().clone(); + let power_change = self.get_epoch_info(&next_next_epoch_id)?.power_change().clone(); + + debug!(target: "epoch_manager", + "prev_prev_power_change: {:?}, prev_power_change: {:?}, power_change: {:?}, slashed: {:?}, + prev_prev_frozen_change: {:?}, prev_frozen_change: {:?}, frozen_change: {:?}", + prev_prev_power_change, prev_power_change, power_change, last_block_info.slashed(), + prev_prev_frozen_change, prev_frozen_change, frozen_change + ); + let all_power_changes = + prev_prev_power_change.iter().chain(&prev_power_change).chain(&power_change); + let all_power_keys: HashSet<&AccountId> = all_power_changes.map(|(key, _)| key).collect(); + + let mut power_info = HashMap::new(); + for account_id in all_power_keys { + let new_power = *power_change.get(account_id).unwrap_or(&0); + let prev_power = *prev_power_change.get(account_id).unwrap_or(&0); + let prev_prev_power = *prev_prev_power_change.get(account_id).unwrap_or(&0); + let max_of_power = + vec![prev_prev_power, prev_power, new_power].into_iter().max().unwrap(); + power_info.insert(account_id.clone(), max_of_power); + } + + let all_frozen_changes = prev_prev_frozen_change + .iter() + .chain(&prev_frozen_change) + .chain(&frozen_change); + let all_frozen_keys: HashSet<&AccountId> = all_frozen_changes.map(|(key, _)| key).collect(); + + let mut frozen_info = HashMap::new(); + for account_id in all_frozen_keys { + if last_block_info.slashed().contains_key(account_id) { + if prev_prev_frozen_change.contains_key(account_id) + && !prev_frozen_change.contains_key(account_id) + && !frozen_change.contains_key(account_id) + { + // slashed in prev_prev epoch so it is safe to return the remaining stake in case of + // a double sign without violating the staking invariant. + } else { + continue; + } + } + let new_frozen = *frozen_change.get(account_id).unwrap_or(&0); + let prev_frozen = *prev_frozen_change.get(account_id).unwrap_or(&0); + let prev_prev_frozen = *prev_prev_frozen_change.get(account_id).unwrap_or(&0); + let max_of_frozen = + vec![prev_prev_frozen, prev_frozen, new_frozen].into_iter().max().unwrap(); + frozen_info.insert(account_id.clone(), max_of_frozen); + } + + let slashing_info = self.compute_double_sign_slashing_info(last_block_hash)?; + debug!(target: "epoch_manager", "stake_info: {:?}, frozen_info: {:?}, validator_reward: {:?}", power_info, frozen_info, validator_reward); + Ok((power_info, frozen_info, validator_reward, slashing_info)) + } + + /// Compute slashing information. Returns a hashmap of account id to slashed amount for double sign + /// slashing. + fn compute_double_sign_slashing_info( + &self, + last_block_hash: &CryptoHash, + ) -> Result, EpochError> { + let last_block_info = self.get_block_info(last_block_hash)?; + let epoch_id = self.get_epoch_id(last_block_hash)?; + let epoch_info = self.get_epoch_info(&epoch_id)?; + let total_stake: Balance = epoch_info.validators_iter().map(|v| v.frozen()).sum(); + let total_slashed_stake: Balance = last_block_info + .slashed() + .iter() + .filter_map(|(account_id, slashed)| match slashed { + SlashState::DoubleSign => Some( + epoch_info + .get_validator_id(account_id) + .map_or(0, |id| epoch_info.validator_frozen(*id)), + ), + _ => None, + }) + .sum(); + let is_totally_slashed = total_slashed_stake * 3 >= total_stake; + let mut res = HashMap::default(); + for (account_id, slash_state) in last_block_info.slashed() { + if let SlashState::DoubleSign = slash_state { + if let Some(&idx) = epoch_info.get_validator_id(account_id) { + let stake = epoch_info.validator_frozen(idx); + let slashed_stake = if is_totally_slashed { + stake + } else { + let stake = U256::from(stake); + // 3 * (total_slashed_stake / total_stake) * stake + (U256::from(3) * U256::from(total_slashed_stake) * stake + / U256::from(total_stake)) + .as_u128() + }; + res.insert(account_id.clone(), slashed_stake); + } + } + } + Ok(res) + } + + /// Get validators for current epoch and next epoch. + /// WARNING: this function calls EpochManager::get_epoch_info_aggregator_upto_last + /// underneath which can be very expensive. + pub fn get_validator_info( + &self, + epoch_identifier: ValidatorInfoIdentifier, + ) -> Result { + let epoch_id = match epoch_identifier { + ValidatorInfoIdentifier::EpochId(ref id) => id.clone(), + ValidatorInfoIdentifier::BlockHash(ref b) => self.get_epoch_id(b)?, + }; + let cur_epoch_info = self.get_epoch_info(&epoch_id)?; + let epoch_height = cur_epoch_info.epoch_height(); + let epoch_start_height = self.get_epoch_start_from_epoch_id(&epoch_id)?; + let mut validator_to_shard = (0..cur_epoch_info.validators_len()) + .map(|_| HashSet::default()) + .collect::>>(); + for (shard_id, validators) in + cur_epoch_info.chunk_producers_settlement().into_iter().enumerate() + { + for validator_id in validators { + validator_to_shard[*validator_id as usize].insert(shard_id as ShardId); + } + } + + // This ugly code arises because of the incompatible types between `block_tracker` in `EpochInfoAggregator` + // and `validator_block_chunk_stats` in `EpochSummary`. Rust currently has no support for Either type + // in std. + let (current_validators, next_epoch_id, all_power_proposals, all_frozen_proposals) = match &epoch_identifier { + ValidatorInfoIdentifier::EpochId(id) => { + let epoch_summary = self.get_epoch_validator_info(id)?; + let cur_validators = cur_epoch_info + .validators_iter() + .enumerate() + .map(|(validator_id, info)| { + let validator_stats = epoch_summary + .validator_block_chunk_stats + .get(info.account_id()) + .unwrap_or(&BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 0, expected: 0 }, + chunk_stats: ValidatorStats { produced: 0, expected: 0 }, + }); + let mut shards = validator_to_shard[validator_id] + .iter() + .cloned() + .collect::>(); + shards.sort(); + let (account_id, public_key, power, frozen) = info.destructure(); + Ok(CurrentEpochValidatorInfo { + is_slashed: false, // currently there is no slashing + account_id, + public_key, + power, + frozen, + // TODO: Maybe fill in the per shard info about the chunk produced for requests coming from RPC. + num_produced_chunks_per_shard: vec![0; shards.len()], + num_expected_chunks_per_shard: vec![0; shards.len()], + shards, + num_produced_blocks: validator_stats.block_stats.produced, + num_expected_blocks: validator_stats.block_stats.expected, + num_produced_chunks: validator_stats.chunk_stats.produced, + num_expected_chunks: validator_stats.chunk_stats.expected, + }) + }) + .collect::, EpochError>>()?; + ( + cur_validators, + EpochId(epoch_summary.prev_epoch_last_block_hash), + epoch_summary.all_power_proposals.into_iter().map(Into::into).collect(), + epoch_summary.all_frozen_proposals.into_iter().map(Into::into).collect(), + ) + } + ValidatorInfoIdentifier::BlockHash(ref h) => { + // If we are here, `h` is hash of the latest block of the + // current epoch. + let aggregator = self.get_epoch_info_aggregator_upto_last(h)?; + let cur_validators = cur_epoch_info + .validators_iter() + .enumerate() + .map(|(validator_id, info)| { + let block_stats = aggregator + .block_tracker + .get(&(validator_id as u64)) + .unwrap_or_else(|| &ValidatorStats { produced: 0, expected: 0 }) + .clone(); + + let mut chunks_produced_by_shard: HashMap = + HashMap::new(); + let mut chunks_expected_by_shard: HashMap = + HashMap::new(); + let mut chunk_stats = ValidatorStats { produced: 0, expected: 0 }; + for (shard, tracker) in aggregator.shard_tracker.iter() { + if let Some(stats) = tracker.get(&(validator_id as u64)) { + chunk_stats.produced += stats.produced; + chunk_stats.expected += stats.expected; + *chunks_produced_by_shard.entry(*shard).or_insert(0) += + stats.produced; + *chunks_expected_by_shard.entry(*shard).or_insert(0) += + stats.expected; + } + } + let mut shards = validator_to_shard[validator_id] + .clone() + .into_iter() + .collect::>(); + shards.sort(); + let (account_id, public_key, power, frozen) = info.destructure(); + Ok(CurrentEpochValidatorInfo { + is_slashed: false, // currently there is no slashing + account_id, + public_key, + power, + frozen, + shards: shards.clone(), + num_produced_blocks: block_stats.produced, + num_expected_blocks: block_stats.expected, + num_produced_chunks: chunk_stats.produced, + num_expected_chunks: chunk_stats.expected, + num_produced_chunks_per_shard: shards + .iter() + .map(|shard| *chunks_produced_by_shard.entry(*shard).or_default()) + .collect(), + num_expected_chunks_per_shard: shards + .iter() + .map(|shard| *chunks_expected_by_shard.entry(*shard).or_default()) + .collect(), + }) + }) + .collect::, EpochError>>()?; + let all_power_proposals = + aggregator.all_power_proposals.iter().map(|(_, p)| p.clone().into()).collect(); + let all_frozen_proposals = + aggregator.all_frozen_proposals.iter().map(|(_, p)| p.clone().into()).collect(); + let next_epoch_id = self.get_next_epoch_id(h)?; + (cur_validators, next_epoch_id, all_power_proposals, all_frozen_proposals) + } + }; + + let next_epoch_info = self.get_epoch_info(&next_epoch_id)?; + let mut next_validator_to_shard = (0..next_epoch_info.validators_len()) + .map(|_| HashSet::default()) + .collect::>>(); + for (shard_id, validators) in + next_epoch_info.chunk_producers_settlement().iter().enumerate() + { + for validator_id in validators { + next_validator_to_shard[*validator_id as usize].insert(shard_id as u64); + } + } + let next_validators = next_epoch_info + .validators_iter() + .enumerate() + .map(|(validator_id, info)| { + let mut shards = next_validator_to_shard[validator_id] + .clone() + .into_iter() + .collect::>(); + shards.sort(); + let (account_id, public_key, power, frozen) = info.destructure(); + NextEpochValidatorInfo { account_id, public_key, power, frozen, shards } + }) + .collect(); + let prev_epoch_kickout = next_epoch_info + .validator_kickout() + .clone() + .into_iter() + .collect::>() + .into_iter() + .map(|(account_id, reason)| ValidatorKickoutView { account_id, reason }) + .collect(); + + Ok(EpochValidatorInfo { + current_validators, + next_validators, + current_fishermen: cur_epoch_info.fishermen_iter().map(Into::into).collect(), + next_fishermen: next_epoch_info.fishermen_iter().map(Into::into).collect(), + current_power_proposals: all_power_proposals, + current_frozen_proposals: all_frozen_proposals, + prev_epoch_kickout, + epoch_start_height, + epoch_height, + }) + } + + pub fn add_validator_proposals( + &mut self, + block_header_info: BlockHeaderInfo, + ) -> Result { + // Check that genesis block doesn't have any proposals. + assert!( + block_header_info.height > 0 + || (block_header_info.power_proposals.is_empty() + && block_header_info.frozen_proposals.is_empty() + && block_header_info.slashed_validators.is_empty()) + ); + debug!(target: "epoch_manager", + height = block_header_info.height, + power_proposals = ?block_header_info.power_proposals, + frozen_proposals = ?block_header_info.frozen_proposals, + "add_validator_proposals"); + let rng_seed = block_header_info.random_value.0; + // start customized by James Savechives + let BlockSummary::V1(BlockSummaryV1{ + random_value : _random_value, + validators, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + fishermen, + fishermen_to_index, + power_change, + frozen_change, + validator_reward, + seat_price, + minted_amount, + all_power_proposals, + all_frozen_proposals, + validator_kickout, + validator_mandates, .. + }) = + if block_header_info.hash == CryptoHash::default() { + BlockSummary::default() + } else { + let BlockInfo::V2(BlockInfoV2{ + validators, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + fishermen, + fishermen_to_index, + power_change, + frozen_change, + validator_reward, + seat_price, + minted_amount, + all_power_proposals, + all_frozen_proposals, + validator_kickout, + validator_mandates, .. + }) = &*self.get_block_info(&block_header_info.prev_hash)? else { todo!() }; + let all_power_proposals : Vec<_> = all_power_proposals.clone().into_iter().chain(block_header_info.power_proposals.clone().into_iter()).collect(); + let all_frozen_proposals : Vec<_> = all_frozen_proposals.clone().into_iter().chain(block_header_info.frozen_proposals.clone().into_iter()).collect(); + let block_info = BlockInfo::new( + block_header_info.hash, + block_header_info.height, + block_header_info.last_finalized_height, + block_header_info.last_finalized_block_hash, + block_header_info.prev_hash, + block_header_info.power_proposals.clone(), + block_header_info.frozen_proposals.clone(), + block_header_info.chunk_mask.clone(), + block_header_info.slashed_validators.clone(), + block_header_info.total_supply, + block_header_info.latest_protocol_version, + block_header_info.timestamp_nanosec, + // start customized by James Savechives + block_header_info.random_value, + validators.clone(), + validator_to_index.clone(), + block_producers_settlement.clone(), + chunk_producers_settlement.clone(), + fishermen.clone(), + fishermen_to_index.clone(), + power_change.clone(), + frozen_change.clone(), + validator_reward.clone(), + seat_price.clone(), + minted_amount.clone(), + all_power_proposals.clone(), + all_frozen_proposals.clone(), + validator_kickout.clone(), + validator_mandates.clone(), + // end customized by James Savechives + ); + self.finalize_block_summary_for_block( + &block_info, + &block_header_info.prev_hash, + rng_seed.clone(), + )? + }; + + // end customized by James Savechives + // Deal with validator proposals and epoch finishing. + let block_info = BlockInfo::new( + block_header_info.hash, + block_header_info.height, + block_header_info.last_finalized_height, + block_header_info.last_finalized_block_hash, + block_header_info.prev_hash, + block_header_info.power_proposals, + block_header_info.frozen_proposals.clone(), + block_header_info.chunk_mask, + block_header_info.slashed_validators, + block_header_info.total_supply, + block_header_info.latest_protocol_version, + block_header_info.timestamp_nanosec, + // start customized by James Savechives + block_header_info.random_value.clone(), + validators.clone(), + validator_to_index, + block_producers_settlement.clone(), + chunk_producers_settlement, + fishermen, + fishermen_to_index, + power_change, + frozen_change, + validator_reward, + seat_price, + minted_amount, + all_power_proposals, + all_frozen_proposals, + validator_kickout, + validator_mandates + // end customized by James Savechives + ); + // println!("the random value is : {:?}", block_header_info.random_value); + // println!("the validators value is : {:?}", validators); + println!("the block producers settlement from block_info is : {:?}",block_producers_settlement); + self.record_block_info(block_info, rng_seed) + + } + + /// Compare two epoch ids based on their start height. This works because finality gadget + /// guarantees that we cannot have two different epochs on two forks + pub fn compare_epoch_id( + &self, + epoch_id: &EpochId, + other_epoch_id: &EpochId, + ) -> Result { + if epoch_id.0 == other_epoch_id.0 { + return Ok(Ordering::Equal); + } + match ( + self.get_epoch_start_from_epoch_id(epoch_id), + self.get_epoch_start_from_epoch_id(other_epoch_id), + ) { + (Ok(index1), Ok(index2)) => Ok(index1.cmp(&index2)), + (Ok(_), Err(_)) => self.get_epoch_info(other_epoch_id).map(|_| Ordering::Less), + (Err(_), Ok(_)) => self.get_epoch_info(epoch_id).map(|_| Ordering::Greater), + (Err(_), Err(_)) => Err(EpochError::EpochOutOfBounds(epoch_id.clone())), // other_epoch_id may be out of bounds as well + } + } + + /// Get minimum stake allowed at current block. Attempts to stake with a lower stake will be + /// rejected. + pub fn minimum_frozen(&self, prev_block_hash: &CryptoHash) -> Result { + let next_epoch_id = self.get_next_epoch_id_from_prev_block(prev_block_hash)?; + let (protocol_version, seat_price) = { + let epoch_info = self.get_epoch_info(&next_epoch_id)?; + (epoch_info.protocol_version(), epoch_info.seat_price()) + }; + let config = self.config.for_protocol_version(protocol_version); + let stake_divisor = { config.minimum_stake_divisor as Balance }; + Ok(seat_price / stake_divisor) + } + + /// Get minimum power allowed at current block. Attempts to stake with a lower power will be + /// rejected. + pub fn minimum_power(&self,_prev_block_hash: &CryptoHash) -> Result { + // To Do + Ok(0) + } +} + +/// Private utilities for EpochManager. +impl EpochManager { + fn cares_about_shard_in_epoch( + &self, + epoch_id: EpochId, + account_id: &AccountId, + shard_id: ShardId, + ) -> Result { + let epoch_info = self.get_epoch_info(&epoch_id)?; + let chunk_producers = epoch_info.chunk_producers_settlement(); + for validator_id in chunk_producers[shard_id as usize].iter() { + if epoch_info.validator_account_id(*validator_id) == account_id { + return Ok(true); + } + } + Ok(false) + } + // #[inline] + // pub(crate) fn block_producer_from_info_vrf( + // epoch_info: &EpochInfo, + // random_value: &CryptoHash, + // ) -> ValidatorId { epoch_info.vrf_block_producer(random_value) } + #[inline] + pub(crate) fn block_producer_from_info( + epoch_info: &EpochInfo, + height: BlockHeight, + ) -> ValidatorId { + epoch_info.sample_block_producer(height) + } + + #[inline] + pub(crate) fn chunk_producer_from_info( + epoch_info: &EpochInfo, + height: BlockHeight, + shard_id: ShardId, + ) -> ValidatorId { + epoch_info.sample_chunk_producer(height, shard_id) + } + + /// Returns true, if given current block info, next block supposed to be in the next epoch. + fn is_next_block_in_next_epoch(&self, block_info: &BlockInfo) -> Result { + if block_info.prev_hash() == &CryptoHash::default() { + return Ok(true); + } + let protocol_version = self.get_epoch_info_from_hash(block_info.hash())?.protocol_version(); + let epoch_length = self.config.for_protocol_version(protocol_version).epoch_length; + let estimated_next_epoch_start = + self.get_block_info(block_info.epoch_first_block())?.height() + epoch_length; + + if epoch_length <= 3 { + // This is here to make epoch_manager tests pass. Needs to be removed, tracked in + // https://github.com/nearprotocol/nearcore/issues/2522 + return Ok(block_info.height() + 1 >= estimated_next_epoch_start); + } + + Ok(block_info.last_finalized_height() + 3 >= estimated_next_epoch_start) + } + + /// Returns true, if given current block info, next block must include the approvals from the next + /// epoch (in addition to the approvals from the current epoch) + fn next_block_need_approvals_from_next_epoch( + &self, + block_info: &BlockInfo, + ) -> Result { + if self.is_next_block_in_next_epoch(block_info)? { + return Ok(false); + } + let epoch_length = { + let protocol_version = + self.get_epoch_info_from_hash(block_info.hash())?.protocol_version(); + let config = self.config.for_protocol_version(protocol_version); + config.epoch_length + }; + let estimated_next_epoch_start = + self.get_block_info(block_info.epoch_first_block())?.height() + epoch_length; + Ok(block_info.last_finalized_height() + 3 < estimated_next_epoch_start + && block_info.height() + 3 >= estimated_next_epoch_start) + } + + /// Returns epoch id for the next epoch (T+1), given an block info in current epoch (T). + fn get_next_epoch_id_from_info(&self, block_info: &BlockInfo) -> Result { + let first_block_info = self.get_block_info(block_info.epoch_first_block())?; + Ok(EpochId(*first_block_info.prev_hash())) + } + + pub fn get_shard_config(&self, epoch_id: &EpochId) -> Result { + let protocol_version = self.get_epoch_info(epoch_id)?.protocol_version(); + let epoch_config = self.config.for_protocol_version(protocol_version); + Ok(ShardConfig::new(epoch_config)) + } + + pub fn get_epoch_config(&self, epoch_id: &EpochId) -> Result { + let protocol_version = self.get_epoch_info(epoch_id)?.protocol_version(); + Ok(self.config.for_protocol_version(protocol_version)) + } + + pub fn get_shard_layout(&self, epoch_id: &EpochId) -> Result { + let protocol_version = self.get_epoch_info(epoch_id)?.protocol_version(); + let shard_layout = self.config.for_protocol_version(protocol_version).shard_layout; + Ok(shard_layout) + } + + pub fn will_shard_layout_change(&self, parent_hash: &CryptoHash) -> Result { + let epoch_id = self.get_epoch_id_from_prev_block(parent_hash)?; + let next_epoch_id = self.get_next_epoch_id_from_prev_block(parent_hash)?; + let shard_layout = self.get_shard_layout(&epoch_id)?; + let next_shard_layout = self.get_shard_layout(&next_epoch_id)?; + Ok(shard_layout != next_shard_layout) + } + + pub fn get_epoch_info(&self, epoch_id: &EpochId) -> Result, EpochError> { + self.epochs_info.get_or_try_put(epoch_id.clone(), |epoch_id| { + self.store + .get_ser(DBCol::EpochInfo, epoch_id.as_ref())? + .ok_or_else(|| EpochError::EpochOutOfBounds(epoch_id.clone())) + }) + } + + fn has_epoch_info(&self, epoch_id: &EpochId) -> Result { + match self.get_epoch_info(epoch_id) { + Ok(_) => Ok(true), + Err(EpochError::EpochOutOfBounds(_)) => Ok(false), + Err(err) => Err(err), + } + } + + fn save_epoch_info( + &mut self, + store_update: &mut StoreUpdate, + epoch_id: &EpochId, + epoch_info: Arc, + ) -> Result<(), EpochError> { + store_update.set_ser(DBCol::EpochInfo, epoch_id.as_ref(), &epoch_info)?; + self.epochs_info.put(epoch_id.clone(), epoch_info); + Ok(()) + } + + pub fn get_epoch_validator_info(&self, epoch_id: &EpochId) -> Result { + // We don't use cache here since this query happens rarely and only for rpc. + self.store + .get_ser(DBCol::EpochValidatorInfo, epoch_id.as_ref())? + .ok_or_else(|| EpochError::EpochOutOfBounds(epoch_id.clone())) + } + + // Note(#6572): beware, after calling `save_epoch_validator_info`, + // `get_epoch_validator_info` will return stale results. + fn save_epoch_validator_info( + &self, + store_update: &mut StoreUpdate, + epoch_id: &EpochId, + epoch_summary: &EpochSummary, + ) -> Result<(), EpochError> { + store_update + .set_ser(DBCol::EpochValidatorInfo, epoch_id.as_ref(), epoch_summary) + .map_err(EpochError::from) + } + + fn has_block_info(&self, hash: &CryptoHash) -> Result { + match self.get_block_info(hash) { + Ok(_) => Ok(true), + Err(EpochError::MissingBlock(_)) => Ok(false), + Err(err) => Err(err), + } + } + + /// Get BlockInfo for a block + /// # Errors + /// EpochError::IOErr if storage returned an error + /// EpochError::MissingBlock if block is not in storage + pub fn get_block_info(&self, hash: &CryptoHash) -> Result, EpochError> { + self.blocks_info.get_or_try_put(*hash, |hash| { + self.store + .get_ser(DBCol::BlockInfo, hash.as_ref())? + .ok_or_else(|| EpochError::MissingBlock(*hash)) + .map(Arc::new) + }) + } + + fn save_block_info( + &mut self, + store_update: &mut StoreUpdate, + block_info: Arc, + ) -> Result<(), EpochError> { + let block_hash = *block_info.hash(); + store_update + .insert_ser(DBCol::BlockInfo, block_hash.as_ref(), &block_info) + .map_err(EpochError::from)?; + self.blocks_info.put(block_hash, block_info); + Ok(()) + } + + fn save_epoch_start( + &mut self, + store_update: &mut StoreUpdate, + epoch_id: &EpochId, + epoch_start: BlockHeight, + ) -> Result<(), EpochError> { + store_update + .set_ser(DBCol::EpochStart, epoch_id.as_ref(), &epoch_start) + .map_err(EpochError::from)?; + self.epoch_id_to_start.put(epoch_id.clone(), epoch_start); + Ok(()) + } + + fn get_epoch_start_from_epoch_id(&self, epoch_id: &EpochId) -> Result { + self.epoch_id_to_start.get_or_try_put(epoch_id.clone(), |epoch_id| { + self.store + .get_ser(DBCol::EpochStart, epoch_id.as_ref())? + .ok_or_else(|| EpochError::EpochOutOfBounds(epoch_id.clone())) + }) + } + + /// Updates epoch info aggregator to state as of `last_final_block_hash` + /// block. + /// + /// The block hash passed as argument should be a final block so that the + /// method can perform efficient incremental updates. Calling this method + /// on a block which has not been finalised yet is likely to result in + /// performance issues since handling forks will force it to traverse the + /// entire epoch from scratch. + /// + /// The result of the aggregation is stored in `self.epoch_info_aggregator`. + /// + /// Saves the aggregator to `store_update` if epoch id changes or every + /// [`AGGREGATOR_SAVE_PERIOD`] heights. + pub fn update_epoch_info_aggregator_upto_final( + &mut self, + last_final_block_hash: &CryptoHash, + store_update: &mut StoreUpdate, + ) -> Result<(), EpochError> { + if let Some((aggregator, replace)) = + self.aggregate_epoch_info_upto(last_final_block_hash)? + { + let save = if replace { + self.epoch_info_aggregator = aggregator; + true + } else { + self.epoch_info_aggregator.merge(aggregator); + let block_info = self.get_block_info(last_final_block_hash)?; + block_info.height() % AGGREGATOR_SAVE_PERIOD == 0 + }; + if save { + store_update.set_ser( + DBCol::EpochInfo, + AGGREGATOR_KEY, + &self.epoch_info_aggregator, + )?; + } + } + Ok(()) + } + + /// Returns epoch info aggregate with state up to `last_block_hash`. + /// + /// The block hash passed as argument should be the latest block belonging + /// to current epoch. Calling this method on any other block is likely to + /// result in performance issues since handling something which is not past + /// the final block will force it to traverse the entire epoch from scratch. + /// + /// This method does not change `self.epoch_info_aggregator`. + pub fn get_epoch_info_aggregator_upto_last( + &self, + last_block_hash: &CryptoHash, + ) -> Result { + if let Some((mut aggregator, replace)) = self.aggregate_epoch_info_upto(last_block_hash)? { + if !replace { + aggregator.merge_prefix(&self.epoch_info_aggregator); + } + Ok(aggregator) + } else { + Ok(self.epoch_info_aggregator.clone()) + } + } + + /// Aggregates epoch info between last final block and given block. + /// + /// More specifically, aggregates epoch information from block denoted by + /// `self.epoch_info_aggregator.last_block_hash` (excluding that block) up + /// to one denoted by `block_hash` (including that block). If the two + /// blocks belong to different epochs, stops aggregating once it reaches + /// start of epoch `block_hash` belongs to. + /// + /// The block hash passed as argument should be a latest final block or + /// a descendant of a latest final block. Calling this method on any other + /// block is likely to result in performance issues since handling forks + /// will force it to traverse the entire epoch from scratch. + /// + /// If `block_hash` equals `self.epoch_info_aggregator.last_block_hash` + /// returns None. Otherwise returns `Some((aggregator, full_info))` tuple. + /// The first element of the pair is aggregator with collected information; + /// the second specifies whether the returned aggregator includes full + /// information about an epoch (such that it does not need to be merged with + /// `self.epoch_info_aggregator`). That happens if the method reaches epoch + /// boundary. + fn aggregate_epoch_info_upto( + &self, + block_hash: &CryptoHash, + ) -> Result, EpochError> { + if block_hash == &self.epoch_info_aggregator.last_block_hash { + return Ok(None); + } + + if cfg!(debug) { + let agg_hash = self.epoch_info_aggregator.last_block_hash; + let agg_height = self.get_block_info(&agg_hash)?.height(); + let block_height = self.get_block_info(block_hash)?.height(); + assert!( + agg_height < block_height, + "#{agg_hash} {agg_height} >= #{block_hash} {block_height}", + ); + } + + let epoch_id = self.get_block_info(block_hash)?.epoch_id().clone(); + let epoch_info = self.get_epoch_info(&epoch_id)?; + + let mut aggregator = EpochInfoAggregator::new(epoch_id.clone(), *block_hash); + let mut cur_hash = *block_hash; + Ok(Some(loop { + #[cfg(test)] + { + self.epoch_info_aggregator_loop_counter + .fetch_add(1, std::sync::atomic::Ordering::SeqCst); + } + + // To avoid cloning BlockInfo we need to first get reference to the + // current block, but then drop it so that we can call + // get_block_info for previous block. + let block_info = self.get_block_info(&cur_hash)?; + let prev_hash = *block_info.prev_hash(); + let different_epoch = &epoch_id != block_info.epoch_id(); + + if different_epoch || prev_hash == CryptoHash::default() { + // We’ve reached the beginning of an epoch or a genesis block + // without seeing self.epoch_info_aggregator.last_block_hash. + // This implies self.epoch_info_aggregator.last_block_hash + // belongs to different epoch or we’re on different fork (though + // the latter should never happen). In either case, the + // aggregator contains full epoch information. + break (aggregator, true); + } + + let prev_info = self.get_block_info(&prev_hash)?; + let prev_height = prev_info.height(); + let prev_epoch = prev_info.epoch_id().clone(); + + let block_info = self.get_block_info(&cur_hash)?; + aggregator.update_tail(&block_info, &epoch_info, prev_height); + + if prev_hash == self.epoch_info_aggregator.last_block_hash { + // We’ve reached sync point of the old aggregator. If old + // aggregator was for a different epoch, we have full info in + // our aggregator; otherwise we don’t. + break (aggregator, epoch_id != prev_epoch); + } + + cur_hash = prev_hash; + })) + } + + pub fn get_protocol_upgrade_block_height( + &self, + block_hash: CryptoHash, + ) -> Result, EpochError> { + let cur_epoch_info = self.get_epoch_info_from_hash(&block_hash)?; + let next_epoch_id = self.get_next_epoch_id(&block_hash)?; + let next_epoch_info = self.get_epoch_info(&next_epoch_id)?; + if cur_epoch_info.protocol_version() != next_epoch_info.protocol_version() { + let block_info = self.get_block_info(&block_hash)?; + let epoch_length = + self.config.for_protocol_version(cur_epoch_info.protocol_version()).epoch_length; + let estimated_next_epoch_start = + self.get_block_info(block_info.epoch_first_block())?.height() + epoch_length; + + Ok(Some(estimated_next_epoch_start)) + } else { + Ok(None) + } + } + + #[cfg(feature = "new_epoch_sync")] + pub fn get_all_epoch_hashes_from_db( + &self, + last_block_info: &BlockInfo, + ) -> Result, EpochError> { + let _span = + tracing::debug_span!(target: "epoch_manager", "get_all_epoch_hashes_from_db", ?last_block_info) + .entered(); + + let mut result = vec![]; + let first_epoch_block_height = + self.get_block_info(last_block_info.epoch_first_block())?.height(); + let mut current_block_info = last_block_info.clone(); + while current_block_info.hash() != last_block_info.epoch_first_block() { + // Check that we didn't reach previous epoch. + // This only should happen if BlockInfo data is incorrect. + // Without this assert same BlockInfo will cause infinite loop instead of crash with a message. + assert!( + current_block_info.height() > first_epoch_block_height, + "Reached {:?} from {:?} when first epoch height is {:?}", + current_block_info, + last_block_info, + first_epoch_block_height + ); + + result.push(*current_block_info.hash()); + current_block_info = (*self.get_block_info(current_block_info.prev_hash())?).clone(); + } + // First block of an epoch is not covered by the while loop. + result.push(*current_block_info.hash()); + + Ok(result) + } + + #[cfg(feature = "new_epoch_sync")] + fn get_all_epoch_hashes_from_cache( + &self, + last_block_info: &BlockInfo, + hash_to_prev_hash: &HashMap, + ) -> Result, EpochError> { + let _span = + tracing::debug_span!(target: "epoch_manager", "get_all_epoch_hashes_from_cache", ?last_block_info) + .entered(); + + let mut result = vec![]; + let mut current_hash = *last_block_info.hash(); + while current_hash != *last_block_info.epoch_first_block() { + result.push(current_hash); + current_hash = *hash_to_prev_hash + .get(¤t_hash) + .ok_or(EpochError::MissingBlock(current_hash))?; + } + // First block of an epoch is not covered by the while loop. + result.push(current_hash); + + Ok(result) + } +} diff --git a/chain/epoch-manager/src/proposals.rs b/chain/epoch-manager/src/proposals.rs new file mode 100644 index 000000000..bdabda6a0 --- /dev/null +++ b/chain/epoch-manager/src/proposals.rs @@ -0,0 +1,407 @@ +use std::collections::HashMap; + +use unc_primitives::checked_feature; +use unc_primitives::epoch_manager::epoch_info::EpochInfo; +use unc_primitives::epoch_manager::{EpochConfig, RngSeed}; +use unc_primitives::epoch_manager::block_info::BlockInfo; +use unc_primitives::epoch_manager::block_summary::BlockSummary; +use unc_primitives::errors::{BlockError, EpochError}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::types::validator_power::ValidatorPower; +use unc_primitives::types::validator_frozen::ValidatorFrozen; +use unc_primitives::types::{ + AccountId, Balance, NumSeats, ProtocolVersion, ValidatorKickoutReason, +}; + +/// Find threshold of stake per seat, given provided stakes and required number of seats. +pub(crate) fn find_threshold( + stakes: &[Balance], + num_seats: NumSeats, +) -> Result { + let stake_sum: Balance = stakes.iter().sum(); + if stake_sum < num_seats.into() { + return Err(EpochError::ThresholdError { stake_sum, num_seats }); + } + let (mut left, mut right): (Balance, Balance) = (1, stake_sum + 1); + 'outer: loop { + if left == right - 1 { + break Ok(left); + } + let mid = (left + right) / 2; + let mut current_sum: Balance = 0; + for item in stakes.iter() { + current_sum += item / mid; + if current_sum >= u128::from(num_seats) { + left = mid; + continue 'outer; + } + } + right = mid; + } +} +/// +pub fn proposals_to_block_summary( + epoch_config: &EpochConfig, + this_block_hash: &CryptoHash, + last_block_hash: &CryptoHash, + rng_seed: RngSeed, + prev_block_summary: &BlockInfo, + power_proposals: Vec, + frozen_proposals: Vec, + validator_kickout: HashMap, + validator_reward: HashMap, + minted_amount: Balance, + next_version: ProtocolVersion, +) -> Result { + return crate::validator_selection::proposals_to_block_summary( + epoch_config, + this_block_hash, + last_block_hash, + rng_seed, + prev_block_summary, + power_proposals, + frozen_proposals, + validator_kickout, + validator_reward, + minted_amount, + next_version, + ); +} +/// Calculates new seat assignments based on current seat assignments and proposals. +pub fn proposals_to_epoch_info( + epoch_config: &EpochConfig, + rng_seed: RngSeed, + prev_epoch_info: &EpochInfo, + power_proposals: Vec, + frozen_proposals: Vec, + validator_kickout: HashMap, + validator_reward: HashMap, + minted_amount: Balance, + next_version: ProtocolVersion, + last_epoch_version: ProtocolVersion, +) -> Result { + if checked_feature!("stable", AliasValidatorSelectionAlgorithm, last_epoch_version) { + return crate::validator_selection::proposals_to_epoch_info( + epoch_config, + rng_seed, + prev_epoch_info, + power_proposals, + frozen_proposals, + validator_kickout, + validator_reward, + minted_amount, + next_version, + last_epoch_version, + ); + } else { + return old_validator_selection::proposals_to_epoch_info( + epoch_config, + rng_seed, + prev_epoch_info, + power_proposals, + frozen_proposals, + validator_kickout, + validator_reward, + minted_amount, + next_version, + ); + } +} + +mod old_validator_selection { + use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; + use std::iter; + + use unc_primitives::epoch_manager::epoch_info::EpochInfo; + use unc_primitives::epoch_manager::EpochConfig; + use unc_primitives::errors::EpochError; + use unc_primitives::types::validator_power::ValidatorPower; + use unc_primitives::types::validator_frozen::ValidatorFrozen; + use unc_primitives::types::{AccountId, Balance, NumSeats, ValidatorFrozenV1, ValidatorId, ValidatorKickoutReason, ValidatorPowerAndFrozenV1, ValidatorPowerV1}; + use unc_primitives::validator_mandates::ValidatorMandates; + use unc_primitives::version::ProtocolVersion; + use rand::{RngCore, SeedableRng}; + use rand_hc::Hc128Rng; + use unc_primitives::types::validator_power_and_frozen::ValidatorPowerAndFrozen; + + use crate::proposals::find_threshold; + use crate::types::RngSeed; + + pub fn proposals_to_epoch_info( + epoch_config: &EpochConfig, + rng_seed: RngSeed, + prev_epoch_info: &EpochInfo, + power_proposals: Vec, + frozen_proposals: Vec, + mut validator_kickout: HashMap, + validator_reward: HashMap, + minted_amount: Balance, + next_version: ProtocolVersion, + ) -> Result { + // Combine proposals with rollovers. + let mut ordered_power_proposals = BTreeMap::new(); + let mut ordered_frozen_proposals= BTreeMap::new(); + // Account -> new_stake + let mut power_change = BTreeMap::new(); + let mut frozen_change = BTreeMap::new(); + let mut fishermen = vec![]; + // debug_assert!( + // power_proposals.iter().map(|power| power.account_id()).collect::>().len() + // == power_proposals.len(), + // "Power proposals should not have duplicates" + // ); + + debug_assert!( + frozen_proposals.iter().map(|frozen| frozen.account_id()).collect::>().len() + == frozen_proposals.len(), + "Frozen proposals should not have duplicates" + ); + + for p in power_proposals { + let account_id = p.account_id(); + power_change.insert(account_id.clone(), p.power()); + ordered_power_proposals.insert(account_id.clone(), p); + } + + for f in frozen_proposals { + let account_id = f.account_id(); + if validator_kickout.contains_key(account_id) { + let account_id = f.take_account_id(); + frozen_change.insert(account_id,0); + } else { + frozen_change.insert(account_id.clone(), f.frozen()); + ordered_frozen_proposals.insert(account_id.clone(), f); + } + } + + for r in prev_epoch_info.validators_iter() { + let account_id = r.account_id().clone(); + if validator_kickout.contains_key(&account_id) { + frozen_change.insert(account_id,0); + continue; + } + let r_p = ValidatorPower::V1(ValidatorPowerV1{ + account_id: r.account_id().clone(), + public_key: r.public_key().clone(), + power: r.power().clone(), + }); + let p = ordered_power_proposals.entry(account_id.clone()).or_insert(r_p); + power_change.insert(account_id.clone(), p.power()); + let r_f = ValidatorFrozen::V1(ValidatorFrozenV1{ + account_id: r.account_id().clone(), + public_key: r.public_key().clone(), + frozen: r.frozen().clone(), + }); + let f = ordered_frozen_proposals.entry(account_id.clone()).or_insert(r_f); + *f.frozen_mut() += *validator_reward.get(&account_id).unwrap_or(&0); + frozen_change.insert(account_id, f.frozen()); + } + + for r in prev_epoch_info.fishermen_iter() { + let account_id = r.account_id(); + if validator_kickout.contains_key(account_id) { + frozen_change.insert(account_id.clone(), 0); + continue; + } + if !ordered_frozen_proposals.contains_key(account_id) { + // safe to do this here because fishermen from previous epoch is guaranteed to have no + // duplicates. + power_change.insert(account_id.clone(), r.power()); + frozen_change.insert(account_id.clone(), r.frozen()); + fishermen.push(r); + } + } + + // Get the threshold given current number of seats and stakes. + let num_hidden_validator_seats: NumSeats = + epoch_config.avg_hidden_validator_seats_per_shard.iter().sum(); + let num_total_seats = epoch_config.num_block_producer_seats + num_hidden_validator_seats; + let frozen = ordered_frozen_proposals.iter().map(|(_, p)| p.frozen()).collect::>(); + let threshold = find_threshold(&frozen, num_total_seats)?; + // Remove proposals under threshold. + let mut final_proposals = vec![]; + + for (account_id, p) in ordered_frozen_proposals { + let frozen = p.frozen(); + let power = ordered_power_proposals.get(&account_id.clone()).unwrap().power(); + let p_f = ValidatorPowerAndFrozen::V1(ValidatorPowerAndFrozenV1{ + account_id: account_id.clone(), + public_key: p.public_key().clone(), + power: power.clone(), + frozen: frozen.clone(), + }); + if frozen >= threshold { + final_proposals.push(p_f); + } else if frozen >= epoch_config.fishermen_threshold { + // Do not return stake back since they will become fishermen + fishermen.push(p_f); + } else { + *frozen_change.get_mut(&account_id).unwrap() = 0; + if prev_epoch_info.account_is_validator(&account_id) + || prev_epoch_info.account_is_fisherman(&account_id) + { + validator_kickout.insert( + account_id, + ValidatorKickoutReason::NotEnoughFrozen { frozen, threshold }, + ); + } + } + } + + // Duplicate each proposal for number of seats it has. + let mut dup_proposals = final_proposals + .iter() + .enumerate() + .flat_map(|(i, p)| iter::repeat(i as u64).take((p.frozen() / threshold) as usize)) + .collect::>(); + + assert!(dup_proposals.len() >= num_total_seats as usize, "bug in find_threshold"); + shuffle_duplicate_proposals(&mut dup_proposals, rng_seed); + + // Block producers are first `num_block_producer_seats` proposals. + let mut block_producers_settlement = + dup_proposals[..epoch_config.num_block_producer_seats as usize].to_vec(); + // remove proposals that are not selected + let indices_to_keep = block_producers_settlement.iter().copied().collect::>(); + let (final_proposals, proposals_to_remove) = final_proposals.into_iter().enumerate().fold( + (vec![], vec![]), + |(mut proposals, mut to_remove), (i, p)| { + if indices_to_keep.contains(&(i as u64)) { + proposals.push(p); + } else { + to_remove.push(p); + } + (proposals, to_remove) + }, + ); + for p in proposals_to_remove { + debug_assert!(p.frozen() >= threshold); + if p.frozen() >= epoch_config.fishermen_threshold { + fishermen.push(p); + } else { + let account_id = p.take_account_id(); + frozen_change.insert(account_id.clone(), 0); + if prev_epoch_info.account_is_validator(&account_id) + || prev_epoch_info.account_is_fisherman(&account_id) + { + validator_kickout.insert(account_id, ValidatorKickoutReason::DidNotGetASeat); + } + } + } + + // reset indices + for index in block_producers_settlement.iter_mut() { + *index = indices_to_keep.range(..*index).count() as u64; + } + + // Collect proposals into block producer assignments. + let mut chunk_producers_settlement: Vec> = vec![]; + let mut last_index: u64 = 0; + for num_seats_in_shard in epoch_config.num_block_producer_seats_per_shard.iter() { + let mut shard_settlement: Vec = vec![]; + for _ in 0..*num_seats_in_shard { + let proposal_index = block_producers_settlement[last_index as usize]; + shard_settlement.push(proposal_index); + last_index = (last_index + 1) % epoch_config.num_block_producer_seats; + } + chunk_producers_settlement.push(shard_settlement); + } + + let fishermen_to_index = fishermen + .iter() + .enumerate() + .map(|(index, s)| (s.account_id().clone(), index as ValidatorId)) + .collect::>(); + + let validator_to_index = final_proposals + .iter() + .enumerate() + .map(|(index, s)| (s.account_id().clone(), index as ValidatorId)) + .collect::>(); + + // Old validator selection is not aware of chunk validator mandates. + let validator_mandates: ValidatorMandates = Default::default(); + + Ok(EpochInfo::new( + prev_epoch_info.epoch_height() + 1, + final_proposals, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + vec![], + fishermen, + fishermen_to_index, + power_change, + frozen_change, + validator_reward, + validator_kickout, + minted_amount, + threshold, + next_version, + rng_seed, + validator_mandates, + )) + } + + fn shuffle_duplicate_proposals(dup_proposals: &mut Vec, rng_seed: RngSeed) { + let mut rng: Hc128Rng = SeedableRng::from_seed(rng_seed); + for i in (1..dup_proposals.len()).rev() { + dup_proposals.swap(i, gen_index_old(&mut rng, (i + 1) as u64) as usize); + } + } + + fn gen_index_old(rng: &mut Hc128Rng, bound: u64) -> u64 { + // This is a simplified copy of the rand gen_index implementation to ensure that + // upgrades to the rand library will not cause a change in the shuffling behavior. + let zone = (bound << bound.leading_zeros()).wrapping_sub(1); + loop { + let v = rng.next_u64(); + let mul = (v as u128) * (bound as u128); + let (hi, lo) = ((mul >> 64) as u64, mul as u64); + if lo < zone { + return hi; + } + } + } + #[cfg(test)] + mod tests { + use unc_primitives::hash::CryptoHash; + + use crate::proposals::old_validator_selection::shuffle_duplicate_proposals; + + #[test] + pub fn proposal_shuffling_sanity_checks() { + // Since we made our own impl for shuffling, do some sanity checks. + for i in 0..10 { + let mut dup_proposals = (0..i).collect::>(); + shuffle_duplicate_proposals( + &mut dup_proposals, + *CryptoHash::hash_bytes(&[1, 2, 3, 4, 5]).as_bytes(), + ); + assert_eq!(dup_proposals.len(), i as usize); + dup_proposals.sort(); + assert_eq!(dup_proposals, (0..i).collect::>()); + } + } + + #[test] + pub fn proposal_randomness_reproducibility() { + // Sanity check that the proposal shuffling implementation does not change. + let mut dup_proposals = (0..100).collect::>(); + shuffle_duplicate_proposals( + &mut dup_proposals, + *CryptoHash::hash_bytes(&[1, 2, 3, 4, 5]).as_bytes(), + ); + assert_eq!( + dup_proposals, + vec![ + 28, 64, 35, 39, 5, 19, 91, 93, 32, 55, 49, 86, 7, 34, 58, 48, 65, 11, 0, 3, 63, + 85, 96, 12, 23, 76, 29, 69, 31, 45, 1, 15, 33, 61, 38, 74, 87, 10, 62, 9, 40, + 56, 98, 8, 52, 75, 99, 13, 57, 44, 6, 79, 89, 84, 68, 36, 94, 53, 80, 70, 42, + 88, 73, 2, 72, 25, 20, 67, 37, 97, 41, 71, 47, 59, 24, 66, 54, 21, 18, 26, 60, + 92, 50, 77, 81, 14, 43, 17, 90, 95, 78, 16, 30, 46, 22, 83, 27, 4, 51, 82 + ] + ); + } + } +} diff --git a/chain/epoch-manager/src/reward_calculator.rs b/chain/epoch-manager/src/reward_calculator.rs new file mode 100644 index 000000000..33b9b47ef --- /dev/null +++ b/chain/epoch-manager/src/reward_calculator.rs @@ -0,0 +1,391 @@ +use std::collections::HashMap; + +use num_rational::Rational32; +use primitive_types::U256; + +use unc_chain_configs::GenesisConfig; +use unc_primitives::checked_feature; +use unc_primitives::types::{AccountId, Balance, BlockChunkValidatorStats}; +use unc_primitives::version::{ProtocolVersion, ENABLE_INFLATION_PROTOCOL_VERSION}; + +pub(crate) const NUM_NS_IN_SECOND: u64 = 1_000_000_000; +pub const NUM_SECONDS_IN_A_YEAR: u64 = 24 * 60 * 60 * 365; + +#[derive(Clone, Debug)] +pub struct RewardCalculator { + pub max_inflation_rate: Rational32, + pub num_blocks_per_year: u64, + pub epoch_length: u64, + pub protocol_reward_rate: Rational32, + pub protocol_treasury_account: AccountId, + pub online_min_threshold: Rational32, + pub online_max_threshold: Rational32, + pub num_seconds_per_year: u64, +} + +impl RewardCalculator { + pub fn new(config: &GenesisConfig) -> Self { + RewardCalculator { + max_inflation_rate: config.max_inflation_rate, + num_blocks_per_year: config.num_blocks_per_year, + epoch_length: config.epoch_length, + protocol_reward_rate: config.protocol_reward_rate, + protocol_treasury_account: config.protocol_treasury_account.clone(), + online_max_threshold: config.online_max_threshold, + online_min_threshold: config.online_min_threshold, + num_seconds_per_year: NUM_SECONDS_IN_A_YEAR, + } + } + /// Calculate validator reward for an epoch based on their block and chunk production stats. + /// Returns map of validators with their rewards and amount of newly minted tokens including to protocol's treasury. + /// See spec . + pub fn calculate_reward( + &self, + validator_block_chunk_stats: HashMap, + validator_stake: &HashMap, + total_supply: Balance, + protocol_version: ProtocolVersion, + genesis_protocol_version: ProtocolVersion, + epoch_duration: u64, + ) -> (HashMap, Balance) { + let mut res = HashMap::new(); + let num_validators = validator_block_chunk_stats.len(); + let use_hardcoded_value = genesis_protocol_version < protocol_version + && protocol_version >= ENABLE_INFLATION_PROTOCOL_VERSION; + let max_inflation_rate = + if use_hardcoded_value { Rational32::new_raw(1, 20) } else { self.max_inflation_rate }; + let protocol_reward_rate = if use_hardcoded_value { + Rational32::new_raw(1, 10) + } else { + self.protocol_reward_rate + }; + let epoch_total_reward: u128 = + if checked_feature!("stable", RectifyInflation, protocol_version) { + (U256::from(*max_inflation_rate.numer() as u64) + * U256::from(total_supply) + * U256::from(epoch_duration) + / (U256::from(self.num_seconds_per_year) + * U256::from(*max_inflation_rate.denom() as u64) + * U256::from(NUM_NS_IN_SECOND))) + .as_u128() + } else { + (U256::from(*max_inflation_rate.numer() as u64) + * U256::from(total_supply) + * U256::from(self.epoch_length) + / (U256::from(self.num_blocks_per_year) + * U256::from(*max_inflation_rate.denom() as u64))) + .as_u128() + }; + let epoch_protocol_treasury = (U256::from(epoch_total_reward) + * U256::from(*protocol_reward_rate.numer() as u64) + / U256::from(*protocol_reward_rate.denom() as u64)) + .as_u128(); + res.insert(self.protocol_treasury_account.clone(), epoch_protocol_treasury); + if num_validators == 0 { + return (res, 0); + } + let epoch_validator_reward = epoch_total_reward - epoch_protocol_treasury; + let mut epoch_actual_reward = epoch_protocol_treasury; + let total_stake: Balance = validator_stake.values().sum(); + for (account_id, stats) in validator_block_chunk_stats { + // Uptime is an average of block produced / expected and chunk produced / expected. + let (average_produced_numer, average_produced_denom) = + if stats.block_stats.expected == 0 && stats.chunk_stats.expected == 0 { + (U256::from(0), U256::from(1)) + } else if stats.block_stats.expected == 0 { + (U256::from(stats.chunk_stats.produced), U256::from(stats.chunk_stats.expected)) + } else if stats.chunk_stats.expected == 0 { + (U256::from(stats.block_stats.produced), U256::from(stats.block_stats.expected)) + } else { + ( + U256::from( + stats.block_stats.produced * stats.chunk_stats.expected + + stats.chunk_stats.produced * stats.block_stats.expected, + ), + U256::from(2 * stats.chunk_stats.expected * stats.block_stats.expected), + ) + }; + let online_min_numer = U256::from(*self.online_min_threshold.numer() as u64); + let online_min_denom = U256::from(*self.online_min_threshold.denom() as u64); + // If average of produced blocks below online min threshold, validator gets 0 reward. + let chunk_only_producers_enabled = + checked_feature!("stable", ChunkOnlyProducers, protocol_version); + let reward = if average_produced_numer * online_min_denom + < online_min_numer * average_produced_denom + || (chunk_only_producers_enabled + && stats.chunk_stats.expected == 0 + && stats.block_stats.expected == 0) + // This is for backwards compatibility. In 2021 December, after we changed to 4 shards, + // mainnet was ran without SynchronizeBlockChunkProduction for some time and it's + // possible that some validators have expected blocks or chunks to be zero. + || (!chunk_only_producers_enabled + && (stats.chunk_stats.expected == 0 || stats.block_stats.expected == 0)) + { + 0 + } else { + let stake = *validator_stake + .get(&account_id) + .unwrap_or_else(|| panic!("{} is not a validator", account_id)); + // Online reward multiplier is min(1., (uptime - online_threshold_min) / (online_threshold_max - online_threshold_min). + let online_max_numer = U256::from(*self.online_max_threshold.numer() as u64); + let online_max_denom = U256::from(*self.online_max_threshold.denom() as u64); + let online_numer = + online_max_numer * online_min_denom - online_min_numer * online_max_denom; + let mut uptime_numer = (average_produced_numer * online_min_denom + - online_min_numer * average_produced_denom) + * online_max_denom; + let uptime_denum = online_numer * average_produced_denom; + // Apply min between 1. and computed uptime. + uptime_numer = + if uptime_numer > uptime_denum { uptime_denum } else { uptime_numer }; + (U256::from(epoch_validator_reward) * uptime_numer * U256::from(stake) + / uptime_denum + / U256::from(total_stake)) + .as_u128() + }; + res.insert(account_id, reward); + epoch_actual_reward += reward; + } + (res, epoch_actual_reward) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use unc_primitives::types::{BlockChunkValidatorStats, ValidatorStats}; + use unc_primitives::version::PROTOCOL_VERSION; + use num_rational::Ratio; + use std::collections::HashMap; + + #[test] + fn test_zero_produced_and_expected() { + let epoch_length = 1; + let reward_calculator = RewardCalculator { + max_inflation_rate: Ratio::new(0, 1), + num_blocks_per_year: 1000000, + epoch_length, + protocol_reward_rate: Ratio::new(0, 1), + protocol_treasury_account: "near".parse().unwrap(), + online_min_threshold: Ratio::new(9, 10), + online_max_threshold: Ratio::new(1, 1), + num_seconds_per_year: 1000000, + }; + let validator_block_chunk_stats = HashMap::from([ + ( + "test1".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 0, expected: 0 }, + chunk_stats: ValidatorStats { produced: 0, expected: 0 }, + }, + ), + ( + "test2".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 0, expected: 1 }, + chunk_stats: ValidatorStats { produced: 0, expected: 1 }, + }, + ), + ]); + let validator_stake = + HashMap::from([("test1".parse().unwrap(), 100), ("test2".parse().unwrap(), 100)]); + let total_supply = 1_000_000_000_000; + let result = reward_calculator.calculate_reward( + validator_block_chunk_stats, + &validator_stake, + total_supply, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + epoch_length * NUM_NS_IN_SECOND, + ); + assert_eq!( + result.0, + HashMap::from([ + ("near".parse().unwrap(), 0u128), + ("test1".parse().unwrap(), 0u128), + ("test2".parse().unwrap(), 0u128) + ]) + ); + } + + /// Test reward calculation when validators are not fully online. + #[test] + fn test_reward_validator_different_online() { + let epoch_length = 1000; + let reward_calculator = RewardCalculator { + max_inflation_rate: Ratio::new(1, 100), + num_blocks_per_year: 1000, + epoch_length, + protocol_reward_rate: Ratio::new(0, 10), + protocol_treasury_account: "near".parse().unwrap(), + online_min_threshold: Ratio::new(9, 10), + online_max_threshold: Ratio::new(99, 100), + num_seconds_per_year: 1000, + }; + let validator_block_chunk_stats = HashMap::from([ + ( + "test1".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 945, expected: 1000 }, + chunk_stats: ValidatorStats { produced: 945, expected: 1000 }, + }, + ), + ( + "test2".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 999, expected: 1000 }, + chunk_stats: ValidatorStats { produced: 999, expected: 1000 }, + }, + ), + ( + "test3".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 850, expected: 1000 }, + chunk_stats: ValidatorStats { produced: 850, expected: 1000 }, + }, + ), + ]); + let validator_stake = HashMap::from([ + ("test1".parse().unwrap(), 500_000), + ("test2".parse().unwrap(), 500_000), + ("test3".parse().unwrap(), 500_000), + ]); + let total_supply = 1_000_000_000; + let result = reward_calculator.calculate_reward( + validator_block_chunk_stats, + &validator_stake, + total_supply, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + epoch_length * NUM_NS_IN_SECOND, + ); + // Total reward is 10_000_000. Divided by 3 equal stake validators - each gets 3_333_333. + // test1 with 94.5% online gets 50% because of linear between (0.99-0.9) online. + assert_eq!( + result.0, + HashMap::from([ + ("near".parse().unwrap(), 0), + ("test1".parse().unwrap(), 1_666_666u128), + ("test2".parse().unwrap(), 3_333_333u128), + ("test3".parse().unwrap(), 0u128) + ]) + ); + assert_eq!(result.1, 4_999_999u128); + } + + /// Test reward calculation for chunk only or block only producers + #[test] + fn test_reward_chunk_only_producer() { + let epoch_length = 1000; + let reward_calculator = RewardCalculator { + max_inflation_rate: Ratio::new(1, 100), + num_blocks_per_year: 1000, + epoch_length, + protocol_reward_rate: Ratio::new(0, 10), + protocol_treasury_account: "near".parse().unwrap(), + online_min_threshold: Ratio::new(9, 10), + online_max_threshold: Ratio::new(99, 100), + num_seconds_per_year: 1000, + }; + let validator_block_chunk_stats = HashMap::from([ + ( + "test1".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 945, expected: 1000 }, + chunk_stats: ValidatorStats { produced: 945, expected: 1000 }, + }, + ), + // chunk only producer + ( + "test2".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 0, expected: 0 }, + chunk_stats: ValidatorStats { produced: 999, expected: 1000 }, + }, + ), + // block only producer (not implemented right now, just for testing) + ( + "test3".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 945, expected: 1000 }, + chunk_stats: ValidatorStats { produced: 0, expected: 0 }, + }, + ), + // a validator that expected blocks and chunks are both 0 (this could occur with very + // small probability for validators with little stakes) + ( + "test4".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 0, expected: 0 }, + chunk_stats: ValidatorStats { produced: 0, expected: 0 }, + }, + ), + ]); + let validator_stake = HashMap::from([ + ("test1".parse().unwrap(), 500_000), + ("test2".parse().unwrap(), 500_000), + ("test3".parse().unwrap(), 500_000), + ("test4".parse().unwrap(), 500_000), + ]); + let total_supply = 1_000_000_000; + let result = reward_calculator.calculate_reward( + validator_block_chunk_stats, + &validator_stake, + total_supply, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + epoch_length * NUM_NS_IN_SECOND, + ); + // Total reward is 10_000_000. Divided by 4 equal stake validators - each gets 2_500_000. + // test1 with 94.5% online gets 50% because of linear between (0.99-0.9) online. + { + assert_eq!( + result.0, + HashMap::from([ + ("near".parse().unwrap(), 0), + ("test1".parse().unwrap(), 1_250_000u128), + ("test2".parse().unwrap(), 2_500_000u128), + ("test3".parse().unwrap(), 1_250_000u128), + ("test4".parse().unwrap(), 0u128) + ]) + ); + assert_eq!(result.1, 5_000_000u128); + } + } + + /// Test that under an extreme setting (total supply 100b, epoch length half a day), + /// reward calculation will not overflow. + #[test] + fn test_reward_no_overflow() { + let epoch_length = 60 * 60 * 12; + let reward_calculator = RewardCalculator { + max_inflation_rate: Ratio::new(5, 100), + num_blocks_per_year: 60 * 60 * 24 * 365, + // half a day + epoch_length, + protocol_reward_rate: Ratio::new(1, 10), + protocol_treasury_account: "near".parse().unwrap(), + online_min_threshold: Ratio::new(9, 10), + online_max_threshold: Ratio::new(1, 1), + num_seconds_per_year: 60 * 60 * 24 * 365, + }; + let validator_block_chunk_stats = HashMap::from([( + "test".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 43200, expected: 43200 }, + chunk_stats: ValidatorStats { produced: 345600, expected: 345600 }, + }, + )]); + let validator_stake = HashMap::from([("test".parse().unwrap(), 500_000 * 10_u128.pow(24))]); + // some hypothetical large total supply (100b) + let total_supply = 100_000_000_000 * 10_u128.pow(24); + reward_calculator.calculate_reward( + validator_block_chunk_stats, + &validator_stake, + total_supply, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + epoch_length * NUM_NS_IN_SECOND, + ); + } +} diff --git a/chain/epoch-manager/src/shard_assignment.rs b/chain/epoch-manager/src/shard_assignment.rs new file mode 100644 index 000000000..834aeff79 --- /dev/null +++ b/chain/epoch-manager/src/shard_assignment.rs @@ -0,0 +1,323 @@ +use unc_primitives::types::validator_power::ValidatorPower; +use unc_primitives::types::{Balance, NumShards, Power, ShardId}; +use unc_primitives::types::validator_frozen::ValidatorFrozen; +use unc_primitives::types::validator_power_and_frozen::ValidatorPowerAndFrozen; +use unc_primitives::utils::min_heap::{MinHeap, PeekMut}; + +/// Assign chunk producers (a.k.a. validators) to shards. The i-th element +/// of the output corresponds to the validators assigned to the i-th shard. +/// +/// This function ensures that every shard has at least `min_validators_per_shard` +/// assigned to it, and attempts to balance the stakes between shards (keep the total +/// stake assigned to each shard approximately equal). +/// +/// This function performs best when the number of chunk producers is greater or +/// equal than `num_shards * min_validators_per_shard` in which case each chunk +/// producer will be assigned to a single shard. If there are fewer producers, +/// some of them will be assigned to multiple shards. +/// +/// Panics if chunk_producers vector is not sorted in descending order by +/// producer’s stake. +pub fn assign_shards + Eq + Clone + PartialOrd>( + chunk_producers: Vec, + num_shards: NumShards, + min_validators_per_shard: usize, +) -> Result>, NotEnoughValidators> { + for (idx, pair) in chunk_producers.windows(2).enumerate() { + assert!( + pair[0].get_value() >= pair[1].get_value(), + "chunk_producers isn’t sorted; first discrepancy at {}", + idx + ); + } + + // If there’s not enough chunk producers to fill up a single shard there’s + // nothing we can do. Return with an error. + let num_chunk_producers = chunk_producers.len(); + if num_chunk_producers < min_validators_per_shard { + return Err(NotEnoughValidators); + } + + let mut result: Vec> = (0..num_shards).map(|_| Vec::new()).collect(); + + // Initially, sort by number of validators first so we fill shards up. + let mut shard_index: MinHeap<(usize, Balance, ShardId)> = + (0..num_shards).map(|s| (0, 0, s)).collect(); + + // First, distribute chunk producers until all shards have at least the + // minimum requested number. If there are not enough validators to satisfy + // that requirement, assign some of the validators to multiple shards. + let mut chunk_producers = chunk_producers.into_iter().enumerate().cycle(); + assign_with_possible_repeats( + &mut shard_index, + &mut result, + &mut chunk_producers, + min_validators_per_shard, + ); + + // Second, if there are any unassigned chunk producers left, distribute them + // between shards trying to balance total stake. + let remaining_producers = + num_chunk_producers.saturating_sub(num_shards as usize * min_validators_per_shard); + if remaining_producers > 0 { + // Re-index shards to favour lowest stake first. + let mut shard_index: MinHeap<(Balance, usize, ShardId)> = shard_index + .into_iter() + .map(|(count, stake, shard_id)| (stake, count, shard_id)) + .collect(); + + for (_, cp) in chunk_producers.take(remaining_producers) { + let (least_stake, least_validator_count, shard_id) = + shard_index.pop().expect("shard_index should never be empty"); + let get_value : Balance = cp.get_value(); + shard_index.push((least_stake + get_value, least_validator_count + 1, shard_id)); + result[usize::try_from(shard_id).unwrap()].push(cp); + } + } + + Ok(result) +} + +fn assign_with_possible_repeats + Eq, I: Iterator>( + shard_index: &mut MinHeap<(usize, Balance, ShardId)>, + result: &mut Vec>, + cp_iter: &mut I, + min_validators_per_shard: usize, +) { + let mut buffer = Vec::with_capacity(shard_index.len()); + // Stores (shard_id, cp_index) meaning that cp at cp_index has already been + // added to shard shard_id. Used to make sure we don’t add a cp to the same + // shard multiple times. + let mut seen = std::collections::HashSet::<(ShardId, usize)>::with_capacity( + result.len() * min_validators_per_shard, + ); + + while shard_index.peek().unwrap().0 < min_validators_per_shard { + // cp_iter is an infinite cycle iterator so getting next value can never + // fail. cp_index is index of each element in the iterator but the + // indexing is done before cycling thus the same cp always gets the same + // cp_index. + let (cp_index, cp) = cp_iter.next().unwrap(); + // Decide which shard to assign this chunk producer to. We mustn’t + // assign producers to a single shard multiple times. + loop { + match shard_index.peek_mut() { + None => { + // No shards left which don’t already contain this chunk + // producer. Skip it and move to another producer. + break; + } + Some(top) if top.0 >= min_validators_per_shard => { + // `shard_index` is sorted by number of chunk producers, + // thus all remaining shards have min_validators_per_shard + // producers already assigned to them. Don’t assign current + // one to any shard and move to next cp. + break; + } + Some(mut top) if seen.insert((top.2, cp_index)) => { + // Chunk producer is not yet assigned to the shard and the + // shard still needs more producers. Assign `cp` to it and + // move to next one. + top.0 += 1; + top.1 += cp.get_value(); + result[usize::try_from(top.2).unwrap()].push(cp); + break; + } + Some(top) => { + // This chunk producer is already assigned to this shard. + // Pop the shard from the heap for now and try assigning the + // producer to the next shard. (We’ll look back at the + // shard once we figure out what to do with current `cp`). + buffer.push(PeekMut::pop(top)); + } + } + } + // Any shards we skipped over (because `cp` was already assigned to + // them) need to be put back into the heap. + shard_index.extend(buffer.drain(..)); + } +} + +/// Marker struct to communicate the error where you try to assign validators to shards +/// and there are not enough to even meet the minimum per shard. +#[derive(Debug)] +pub struct NotEnoughValidators; + +pub trait HasStake { + fn get_value(&self) -> T; +} + +impl HasStake for ValidatorPower { + fn get_value(&self) -> Power { + self.power() + } +} + +impl HasStake for ValidatorFrozen { + fn get_value(&self) -> Balance { + self.frozen() + } +} + +impl HasStake for ValidatorPowerAndFrozen { + fn get_value(&self) -> Balance { + self.frozen() + } +} + +#[cfg(test)] +mod tests { + use unc_primitives::types::{Balance, NumShards}; + use std::collections::HashSet; + + const EXPONENTIAL_STAKES: [Balance; 12] = [100, 90, 81, 73, 66, 59, 53, 48, 43, 39, 35, 31]; + + #[test] + fn test_exponential_distribution_few_shards() { + // algorithm works well when there are few shards relative to the number of chunk producers + test_distribution_common(&EXPONENTIAL_STAKES, 3, 3); + } + + #[test] + fn test_exponential_distribution_several_shards() { + // algorithm performs less well when there are more shards + test_distribution_common(&EXPONENTIAL_STAKES, 6, 13); + } + + #[test] + fn test_exponential_distribution_many_shards() { + // algorithm performs even worse when there are many shards + test_distribution_common(&EXPONENTIAL_STAKES, 24, 41); + } + + /// Tests situation where assigning with possible repeats encounters a state + /// in which the same validator would end up assigned to the same shard + /// twice. + /// + /// The way this scenario works is as follows. There are three validators + /// [100, 90, 81] and they are distributed among two shards. First the code + /// will assign 100 to shard 0 and then 90 to shard 1. At that point, both + /// shards will have one validator but shard 1 will have less total stake so + /// the code will assign validator 81 to it. In the last step, shard 0 will + /// have only one validator so the code will try to assign validator 100 to + /// it. However, that validator is already assigned to that shard so the + /// algorithm will need to discard it and try another one. + #[test] + fn test_duplicate_validator() { + test_distribution_common(&EXPONENTIAL_STAKES[..3], 2, 11); + } + + /// Tests behaviour when there’s not enough validators to fill required + /// minimum number of spots per shard. + #[test] + fn test_not_enough_validators() { + // One validator cannot fill three slots. + assert!(assign_shards(&[100], 1, 3).is_err()) + } + + #[test] + fn test_step_distribution_shards() { + let num_shards = 2; + let min_validators_per_shard = 2; + // Note: Could divide as {{100} {10, 10, 10, 10, 10, 10, 10, 10, 10, 10}} + // the stakes are equal with this assignment, but this would not result in + // the minimum of 2 validators in the first shard + let stakes = &[100, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]; + let assignment = assign_shards(stakes, num_shards, min_validators_per_shard).unwrap(); + + // The algorithm ensures the minimum number of validators is present + // in each shard, even if it makes the stakes more uneven. + assert_eq!( + &[(min_validators_per_shard, 110), (stakes.len() - min_validators_per_shard, 90)], + &assignment[..] + ); + } + + /// Calls [`super::assign_shards`] and performs basic validation of the + /// result. Returns sorted and aggregated data in the form of a vector of + /// `(count, stake)` tuples where first element is number of chunk producers + /// in a shard and second is total stake assigned to that shard. + fn assign_shards( + stakes: &[Balance], + num_shards: NumShards, + min_validators_per_shard: usize, + ) -> Result, super::NotEnoughValidators> { + let chunk_producers = stakes.iter().copied().enumerate().collect(); + let assignments = + super::assign_shards(chunk_producers, num_shards, min_validators_per_shard)?; + + // All chunk producers must be assigned at least once. Furthermore, no + // chunk producer can be assigned to more than one shard than chunk + // producer with lowest number of assignments. + let mut chunk_producers_counts = vec![0; stakes.len()]; + for cp in assignments.iter().flat_map(|shard| shard.iter()) { + chunk_producers_counts[cp.0] += 1; + } + let min = chunk_producers_counts.iter().copied().min().unwrap(); + let max = chunk_producers_counts.iter().copied().max().unwrap(); + assert!(0 < min && max <= min + 1); + + let mut assignments = assignments + .into_iter() + .enumerate() + .map(|(shard_id, cps)| { + // All shards must have at least min_validators_per_shard validators. + assert!( + cps.len() >= min_validators_per_shard, + "Shard {} has only {} chunk producers; expected at least {}", + shard_id, + cps.len(), + min_validators_per_shard + ); + // No validator can exist twice in the same shard. + assert_eq!( + cps.len(), + cps.iter().map(|cp| cp.0).collect::>().len(), + "Shard {} contains duplicate chunk producers: {:?}", + shard_id, + cps + ); + // If all is good, aggregate as (cps_count, total_stake) pair. + (cps.len(), cps.iter().map(|cp| cp.1).sum()) + }) + .collect::>(); + assignments.sort(); + Ok(assignments) + } + + fn test_distribution_common(stakes: &[Balance], num_shards: NumShards, diff_tolerance: i128) { + let min_validators_per_shard = 2; + let validators_per_shard = + std::cmp::max(stakes.len() / (num_shards as usize), min_validators_per_shard); + let average_stake_per_shard = (validators_per_shard as Balance) + * stakes.iter().sum::() + / (stakes.len() as Balance); + let assignment = assign_shards(stakes, num_shards, min_validators_per_shard) + .expect("There should have been enough validators"); + for (shard_id, &cps) in assignment.iter().enumerate() { + // Validator distribution should be even. + assert_eq!( + validators_per_shard, cps.0, + "Shard {} has {} validators, expected {}", + shard_id, cps.0, validators_per_shard + ); + + // Stake distribution should be even + let diff = (cps.1 as i128) - (average_stake_per_shard as i128); + assert!( + diff.abs() < diff_tolerance, + "Shard {}'s stake {} is {} away from average; expected less than {} away", + shard_id, + cps.1, + diff.abs(), + diff_tolerance + ); + } + } + + impl super::HasStake for (usize, Balance) { + fn get_stake(&self) -> Balance { + self.1 + } + } +} diff --git a/chain/epoch-manager/src/shard_tracker.rs b/chain/epoch-manager/src/shard_tracker.rs new file mode 100644 index 000000000..9816f898c --- /dev/null +++ b/chain/epoch-manager/src/shard_tracker.rs @@ -0,0 +1,488 @@ +use std::sync::Arc; + +use crate::EpochManagerAdapter; +use unc_cache::SyncLruCache; +use unc_chain_configs::ClientConfig; +use unc_primitives::errors::EpochError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::account_id_to_shard_id; +use unc_primitives::types::{AccountId, EpochId, ShardId}; + +#[derive(Clone)] +pub enum TrackedConfig { + Accounts(Vec), + AllShards, + // Rotates between sets of shards to track. + Schedule(Vec>), +} + +impl TrackedConfig { + pub fn new_empty() -> Self { + TrackedConfig::Accounts(vec![]) + } + + pub fn from_config(config: &ClientConfig) -> Self { + if !config.tracked_shards.is_empty() { + TrackedConfig::AllShards + } else if !config.tracked_shard_schedule.is_empty() { + TrackedConfig::Schedule(config.tracked_shard_schedule.clone()) + } else { + TrackedConfig::Accounts(config.tracked_accounts.clone()) + } + } +} + +// bit mask for which shard to track +type BitMask = Vec; + +/// Tracker that tracks shard ids and accounts. Right now, it only supports two modes +/// TrackedConfig::Accounts(accounts): track the shards where `accounts` belong to +/// TrackedConfig::AllShards: track all shards +#[derive(Clone)] +pub struct ShardTracker { + tracked_config: TrackedConfig, + /// Stores shard tracking information by epoch, only useful if TrackedState == Accounts + tracking_shards_cache: Arc>, + epoch_manager: Arc, +} + +impl ShardTracker { + pub fn new(tracked_config: TrackedConfig, epoch_manager: Arc) -> Self { + ShardTracker { + tracked_config, + // 1024 epochs on mainnet is about 512 days which is more than enough, + // and this is a cache anyway. The data size is pretty small as well, + // only one bit per shard per epoch. + tracking_shards_cache: Arc::new(SyncLruCache::new(1024)), + epoch_manager, + } + } + + pub fn new_empty(epoch_manager: Arc) -> Self { + Self::new(TrackedConfig::new_empty(), epoch_manager) + } + + fn tracks_shard_at_epoch( + &self, + shard_id: ShardId, + epoch_id: &EpochId, + ) -> Result { + match &self.tracked_config { + TrackedConfig::Accounts(tracked_accounts) => { + let shard_layout = self.epoch_manager.get_shard_layout(epoch_id)?; + let tracking_mask = self.tracking_shards_cache.get_or_put(epoch_id.clone(), |_| { + let mut tracking_mask: Vec<_> = + shard_layout.shard_ids().map(|_| false).collect(); + for account_id in tracked_accounts { + let shard_id = account_id_to_shard_id(account_id, &shard_layout); + tracking_mask[shard_id as usize] = true; + } + tracking_mask + }); + Ok(tracking_mask.get(shard_id as usize).copied().unwrap_or(false)) + } + TrackedConfig::AllShards => Ok(true), + TrackedConfig::Schedule(schedule) => { + assert_ne!(schedule.len(), 0); + let epoch_info = self.epoch_manager.get_epoch_info(epoch_id)?; + let epoch_height = epoch_info.epoch_height(); + let index = epoch_height % schedule.len() as u64; + let subset = &schedule[index as usize]; + Ok(subset.contains(&shard_id)) + } + } + } + + fn tracks_shard(&self, shard_id: ShardId, prev_hash: &CryptoHash) -> Result { + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(prev_hash)?; + self.tracks_shard_at_epoch(shard_id, &epoch_id) + } + + fn tracks_shard_next_epoch_from_prev_block( + &self, + shard_id: ShardId, + prev_hash: &CryptoHash, + ) -> Result { + let epoch_id = self.epoch_manager.get_next_epoch_id_from_prev_block(prev_hash)?; + self.tracks_shard_at_epoch(shard_id, &epoch_id) + } + + /// Whether the client cares about some shard right now. + /// * If `account_id` is None, `is_me` is not checked and the + /// result indicates whether the client is tracking the shard + /// * If `account_id` is not None, it is supposed to be a validator + /// account and `is_me` indicates whether we check what shards + /// the client tracks. + pub fn care_about_shard( + &self, + account_id: Option<&AccountId>, + parent_hash: &CryptoHash, + shard_id: ShardId, + is_me: bool, + ) -> bool { + // TODO: fix these unwrap_or here and handle error correctly. The current behavior masks potential errors and bugs + // https://github.com/utnet-org/utility/issues/4936 + if let Some(account_id) = account_id { + let account_cares_about_shard = self + .epoch_manager + .cares_about_shard_from_prev_block(parent_hash, account_id, shard_id) + .unwrap_or(false); + if account_cares_about_shard { + // An account has to track this shard because of its validation duties. + return true; + } + if !is_me { + // We don't know how another node is configured. + // It may track all shards, it may track no additional shards. + return false; + } else { + // We have access to the node config. Use the config to find a definite answer. + } + } + match self.tracked_config { + TrackedConfig::AllShards => { + // Avoid looking up EpochId as a performance optimization. + true + } + _ => self.tracks_shard(shard_id, parent_hash).unwrap_or(false), + } + } + + /// Whether the client cares about some shard in the next epoch. + /// Note that `shard_id` always refers to a shard in the current epoch + /// If shard layout will change next epoch, + /// returns true if it cares about any shard that `shard_id` will split to + /// * If `account_id` is None, `is_me` is not checked and the + /// result indicates whether the client will track the shard + /// * If `account_id` is not None, it is supposed to be a validator + /// account and `is_me` indicates whether we check what shards + /// the client will track. + pub fn will_care_about_shard( + &self, + account_id: Option<&AccountId>, + parent_hash: &CryptoHash, + shard_id: ShardId, + is_me: bool, + ) -> bool { + if let Some(account_id) = account_id { + let account_cares_about_shard = { + self.epoch_manager + .cares_about_shard_next_epoch_from_prev_block(parent_hash, account_id, shard_id) + .unwrap_or(false) + }; + if account_cares_about_shard { + // An account has to track this shard because of its validation duties. + return true; + } + if !is_me { + // We don't know how another node is configured. + // It may track all shards, it may track no additional shards. + return false; + } else { + // We have access to the node config. Use the config to find a definite answer. + } + } + match self.tracked_config { + TrackedConfig::AllShards => { + // Avoid looking up EpochId as a performance optimization. + true + } + _ => { + self.tracks_shard_next_epoch_from_prev_block(shard_id, parent_hash).unwrap_or(false) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::{account_id_to_shard_id, ShardTracker}; + use crate::shard_tracker::TrackedConfig; + use crate::test_utils::{frozen, hash_range}; + use crate::{EpochManager, EpochManagerAdapter, EpochManagerHandle, RewardCalculator}; + use unc_crypto::{KeyType, PublicKey}; + use unc_primitives::epoch_manager::block_info::BlockInfo; + use unc_primitives::epoch_manager::{AllEpochConfig, EpochConfig}; + use unc_primitives::hash::CryptoHash; + use unc_primitives::shard_layout::ShardLayout; + use unc_primitives::types::validator_power::ValidatorPower; + use unc_primitives::types::{BlockHeight, EpochId, NumShards, ProtocolVersion, ShardId}; + use unc_primitives::version::ProtocolFeature::SimpleNightshade; + use unc_primitives::version::PROTOCOL_VERSION; + use unc_store::test_utils::create_test_store; + use num_rational::Ratio; + use std::collections::HashSet; + use std::sync::Arc; + use unc_primitives::types::validator_frozen::ValidatorFrozen; + + const DEFAULT_TOTAL_SUPPLY: u128 = 1_000_000_000_000; + + fn get_epoch_manager( + genesis_protocol_version: ProtocolVersion, + num_shards: NumShards, + use_production_config: bool, + ) -> EpochManagerHandle { + let store = create_test_store(); + let initial_epoch_config = EpochConfig { + epoch_length: 1, + num_block_producer_seats: 1, + num_block_producer_seats_per_shard: vec![1], + avg_hidden_validator_seats_per_shard: vec![], + block_producer_kickout_threshold: 90, + chunk_producer_kickout_threshold: 60, + fishermen_threshold: 0, + online_max_threshold: Ratio::from_integer(1), + online_min_threshold: Ratio::new(90, 100), + minimum_stake_divisor: 1, + protocol_upgrade_stake_threshold: Ratio::new(80, 100), + shard_layout: ShardLayout::v0(num_shards, 0), + validator_selection_config: Default::default(), + validator_max_kickout_stake_perc: 100, + }; + let reward_calculator = RewardCalculator { + max_inflation_rate: Ratio::from_integer(0), + num_blocks_per_year: 1000000, + epoch_length: 1, + protocol_reward_rate: Ratio::from_integer(0), + protocol_treasury_account: "test".parse().unwrap(), + online_max_threshold: initial_epoch_config.online_max_threshold, + online_min_threshold: initial_epoch_config.online_min_threshold, + num_seconds_per_year: 1000000, + }; + EpochManager::new( + store, + AllEpochConfig::new(use_production_config, initial_epoch_config, "test-chain"), + genesis_protocol_version, + reward_calculator, + vec![ValidatorPower::new( + "test".parse().unwrap(), + PublicKey::empty(KeyType::ED25519), + 100, + )], + ) + .unwrap() + .into_handle() + } + + pub fn record_block( + epoch_manager: &mut EpochManager, + prev_h: CryptoHash, + cur_h: CryptoHash, + height: BlockHeight, + power_proposals: Vec, + frozen_proposals: Vec, + protocol_version: ProtocolVersion, + ) { + epoch_manager + .record_block_info( + BlockInfo::new( + cur_h, + height, + 0, + prev_h, + prev_h, + power_proposals, + frozen_proposals, + vec![], + vec![], + DEFAULT_TOTAL_SUPPLY, + protocol_version, + height * 10u64.pow(9), + ), + [0; 32], + ) + .unwrap() + .commit() + .unwrap(); + } + + fn get_all_shards_care_about( + tracker: &ShardTracker, + shard_ids: &[ShardId], + parent_hash: &CryptoHash, + ) -> HashSet { + shard_ids + .into_iter() + .filter(|&&shard_id| tracker.care_about_shard(None, parent_hash, shard_id, true)) + .cloned() + .collect() + } + + fn get_all_shards_will_care_about( + tracker: &ShardTracker, + shard_ids: &[ShardId], + parent_hash: &CryptoHash, + ) -> HashSet { + shard_ids + .into_iter() + .filter(|&&shard_id| tracker.will_care_about_shard(None, parent_hash, shard_id, true)) + .cloned() + .collect() + } + + #[test] + fn test_track_accounts() { + let shard_ids: Vec<_> = (0..4).collect(); + let epoch_manager = + get_epoch_manager(PROTOCOL_VERSION, shard_ids.len() as NumShards, false); + let shard_layout = epoch_manager.read().get_shard_layout(&EpochId::default()).unwrap(); + let tracked_accounts = vec!["test1".parse().unwrap(), "test2".parse().unwrap()]; + let tracker = + ShardTracker::new(TrackedConfig::Accounts(tracked_accounts), Arc::new(epoch_manager)); + let mut total_tracked_shards = HashSet::new(); + total_tracked_shards + .insert(account_id_to_shard_id(&"test1".parse().unwrap(), &shard_layout)); + total_tracked_shards + .insert(account_id_to_shard_id(&"test2".parse().unwrap(), &shard_layout)); + + assert_eq!( + get_all_shards_care_about(&tracker, &shard_ids, &CryptoHash::default()), + total_tracked_shards + ); + assert_eq!( + get_all_shards_will_care_about(&tracker, &shard_ids, &CryptoHash::default()), + total_tracked_shards + ); + } + + #[test] + fn test_track_all_shards() { + let shard_ids: Vec<_> = (0..4).collect(); + let epoch_manager = + get_epoch_manager(PROTOCOL_VERSION, shard_ids.len() as NumShards, false); + let tracker = ShardTracker::new(TrackedConfig::AllShards, Arc::new(epoch_manager)); + let total_tracked_shards: HashSet<_> = shard_ids.iter().cloned().collect(); + + assert_eq!( + get_all_shards_care_about(&tracker, &shard_ids, &CryptoHash::default()), + total_tracked_shards + ); + assert_eq!( + get_all_shards_will_care_about(&tracker, &shard_ids, &CryptoHash::default()), + total_tracked_shards + ); + } + + #[test] + fn test_track_schedule() { + // Creates a ShardTracker that changes every epoch tracked shards. + let shard_ids: Vec<_> = (0..4).collect(); + let epoch_manager = + Arc::new(get_epoch_manager(PROTOCOL_VERSION, shard_ids.len() as NumShards, false)); + let subset1 = HashSet::from([0, 1]); + let subset2 = HashSet::from([1, 2]); + let subset3 = HashSet::from([2, 3]); + let tracker = ShardTracker::new( + TrackedConfig::Schedule(vec![ + subset1.clone().into_iter().collect(), + subset2.clone().into_iter().collect(), + subset3.clone().into_iter().collect(), + ]), + epoch_manager.clone(), + ); + + let h = hash_range(8); + { + let mut epoch_manager = epoch_manager.write(); + for i in 0..8 { + record_block( + &mut epoch_manager, + if i > 0 { h[i - 1] } else { CryptoHash::default() }, + h[i], + i as u64, + vec![], + PROTOCOL_VERSION, + ); + } + } + + assert_eq!(get_all_shards_care_about(&tracker, &shard_ids, &h[4]), subset2); + assert_eq!(get_all_shards_care_about(&tracker, &shard_ids, &h[5]), subset3); + assert_eq!(get_all_shards_care_about(&tracker, &shard_ids, &h[6]), subset1); + assert_eq!(get_all_shards_care_about(&tracker, &shard_ids, &h[7]), subset2); + + assert_eq!(get_all_shards_will_care_about(&tracker, &shard_ids, &h[4]), subset3); + assert_eq!(get_all_shards_will_care_about(&tracker, &shard_ids, &h[5]), subset1); + assert_eq!(get_all_shards_will_care_about(&tracker, &shard_ids, &h[6]), subset2); + assert_eq!(get_all_shards_will_care_about(&tracker, &shard_ids, &h[7]), subset3); + } + + #[test] + fn test_track_shards_shard_layout_change() { + let simple_nightshade_version = SimpleNightshade.protocol_version(); + let epoch_manager = get_epoch_manager(simple_nightshade_version - 1, 1, true); + let tracked_accounts = + vec!["a.near".parse().unwrap(), "near".parse().unwrap(), "zoo".parse().unwrap()]; + let tracker = ShardTracker::new( + TrackedConfig::Accounts(tracked_accounts.clone()), + Arc::new(epoch_manager.clone()), + ); + + let h = hash_range(8); + { + let mut epoch_manager = epoch_manager.write(); + record_block( + &mut epoch_manager, + CryptoHash::default(), + h[0], + 0, + vec![], + simple_nightshade_version, + ); + for i in 1..8 { + record_block( + &mut epoch_manager, + h[i - 1], + h[i], + i as u64, + vec![], + simple_nightshade_version, + ); + } + assert_eq!( + epoch_manager.get_epoch_info(&EpochId(h[0])).unwrap().protocol_version(), + simple_nightshade_version - 1 + ); + assert_eq!( + epoch_manager.get_epoch_info(&EpochId(h[1])).unwrap().protocol_version(), + simple_nightshade_version + ); + } + + // verify tracker is tracking the correct shards before and after resharding + for i in 1..8 { + let mut total_next_tracked_shards = HashSet::new(); + let next_epoch_id = epoch_manager.get_next_epoch_id_from_prev_block(&h[i - 1]).unwrap(); + let next_shard_layout = epoch_manager.get_shard_layout(&next_epoch_id).unwrap(); + + let mut total_tracked_shards = HashSet::new(); + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(&h[i - 1]).unwrap(); + let shard_layout = epoch_manager.get_shard_layout(&epoch_id).unwrap(); + + for account_id in tracked_accounts.iter() { + let shard_id = account_id_to_shard_id(account_id, &shard_layout); + total_tracked_shards.insert(shard_id); + + let next_shard_id = account_id_to_shard_id(account_id, &next_shard_layout); + total_next_tracked_shards.insert(next_shard_id); + } + + assert_eq!( + get_all_shards_care_about( + &tracker, + &shard_layout.shard_ids().collect::>(), + &h[i - 1] + ), + total_tracked_shards + ); + assert_eq!( + get_all_shards_will_care_about( + &tracker, + &next_shard_layout.shard_ids().collect::>(), + &h[i - 1] + ), + total_next_tracked_shards + ); + } + } +} diff --git a/chain/epoch-manager/src/test_utils.rs b/chain/epoch-manager/src/test_utils.rs new file mode 100644 index 000000000..86172be2e --- /dev/null +++ b/chain/epoch-manager/src/test_utils.rs @@ -0,0 +1,529 @@ +use std::collections::{BTreeMap, HashMap}; + +use unc_primitives::types::{EpochId, Power}; +use unc_store::Store; +use num_rational::Ratio; + +use crate::proposals::find_threshold; +use crate::RewardCalculator; +use crate::RngSeed; +use crate::{BlockInfo, EpochManager}; +use unc_crypto::{KeyType, SecretKey}; +use unc_primitives::challenge::SlashedValidator; +use unc_primitives::epoch_manager::block_info::BlockInfoV2; +use unc_primitives::epoch_manager::epoch_info::EpochInfo; +use unc_primitives::epoch_manager::{AllEpochConfig, EpochConfig, ValidatorWeight}; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::types::validator_power::ValidatorPower; +use unc_primitives::types::{ + AccountId, Balance, BlockHeight, BlockHeightDelta, EpochHeight, NumSeats, NumShards, + ValidatorId, ValidatorKickoutReason, +}; +use unc_primitives::utils::get_num_seats_per_shard; +use unc_primitives::validator_mandates::{ValidatorMandates, ValidatorMandatesConfig}; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_store::test_utils::create_test_store; + +use unc_primitives::shard_layout::ShardLayout; +use {crate::reward_calculator::NUM_NS_IN_SECOND, crate::NUM_SECONDS_IN_A_YEAR}; +use unc_primitives::types::validator_frozen::ValidatorFrozen; +use unc_primitives::types::validator_power_and_frozen::ValidatorPowerAndFrozen; + +pub const DEFAULT_GAS_PRICE: u128 = 100; +pub const DEFAULT_TOTAL_SUPPLY: u128 = 1_000_000_000_000; +pub const TEST_SEED: RngSeed = [3; 32]; + +pub fn hash_range(num: usize) -> Vec { + let mut result = vec![]; + for i in 0..num { + result.push(hash(i.to_le_bytes().as_ref())); + } + result +} + +pub fn change_power(power_changes: Vec<(AccountId, Power)>) -> BTreeMap { + power_changes.into_iter().collect() +} + +pub fn epoch_info( + epoch_height: EpochHeight, + accounts: Vec<(AccountId, Power, Balance)>, + block_producers_settlement: Vec, + chunk_producers_settlement: Vec>, + hidden_validators_settlement: Vec, + fishermen: Vec<(AccountId, Power, Balance)>, + power_change: BTreeMap, + frozen_change: BTreeMap, + validator_kickout: Vec<(AccountId, ValidatorKickoutReason)>, + validator_reward: HashMap, + minted_amount: Balance, +) -> EpochInfo { + let num_seats = block_producers_settlement.len() as u64; + epoch_info_with_num_seats( + epoch_height, + accounts, + block_producers_settlement, + chunk_producers_settlement, + hidden_validators_settlement, + fishermen, + power_change, + frozen_change, + validator_kickout, + validator_reward, + minted_amount, + num_seats, + ) +} + +pub fn epoch_info_with_num_seats( + epoch_height: EpochHeight, + mut accounts: Vec<(AccountId, Power, Balance)>, + block_producers_settlement: Vec, + chunk_producers_settlement: Vec>, + hidden_validators_settlement: Vec, + fishermen: Vec<(AccountId, Power, Balance)>, + power_change: BTreeMap, + frozen_change: BTreeMap, + validator_kickout: Vec<(AccountId, ValidatorKickoutReason)>, + validator_reward: HashMap, + minted_amount: Balance, + num_seats: NumSeats, +) -> EpochInfo { + let seat_price = + find_threshold(&accounts.iter().map(|(_,_, s)| *s).collect::>(), num_seats).unwrap(); + accounts.sort(); + let validator_to_index = accounts.iter().enumerate().fold(HashMap::new(), |mut acc, (i, x)| { + acc.insert(x.0.clone(), i as u64); + acc + }); + let fishermen_to_index = + fishermen.iter().enumerate().map(|(i, (s,_ , _))| (s.clone(), i as ValidatorId)).collect(); + let account_to_validators = |accounts: Vec<(AccountId, Power, Balance)>| -> Vec { + accounts + .into_iter() + .map(|(account_id, power,locked)| { + ValidatorPowerAndFrozen::new( + account_id.clone(), + SecretKey::from_seed(KeyType::ED25519, account_id.as_ref()).public_key(), + power, + locked, + ) + }) + .collect() + }; + let all_validators = account_to_validators(accounts); + let validator_mandates = { + // TODO(#10014) determine required stake per mandate instead of reusing seat price. + // TODO(#10014) determine `min_mandates_per_shard` + let num_shards = chunk_producers_settlement.len(); + let min_mandates_per_shard = 0; + let config = ValidatorMandatesConfig::new(seat_price, min_mandates_per_shard, num_shards); + ValidatorMandates::new(config, &all_validators) + }; + EpochInfo::new( + epoch_height, + all_validators, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + hidden_validators_settlement, + account_to_validators(fishermen), + fishermen_to_index, + power_change, + frozen_change, + validator_reward, + validator_kickout.into_iter().collect(), + minted_amount, + seat_price, + PROTOCOL_VERSION, + TEST_SEED, + validator_mandates, + ) +} + +pub fn epoch_config_with_production_config( + epoch_length: BlockHeightDelta, + num_shards: NumShards, + num_block_producer_seats: NumSeats, + num_hidden_validator_seats: NumSeats, + block_producer_kickout_threshold: u8, + chunk_producer_kickout_threshold: u8, + fishermen_threshold: Balance, + use_production_config: bool, +) -> AllEpochConfig { + let epoch_config = EpochConfig { + epoch_length, + num_block_producer_seats, + num_block_producer_seats_per_shard: get_num_seats_per_shard( + num_shards, + num_block_producer_seats, + ), + avg_hidden_validator_seats_per_shard: (0..num_shards) + .map(|_| num_hidden_validator_seats) + .collect(), + block_producer_kickout_threshold, + chunk_producer_kickout_threshold, + fishermen_threshold, + online_min_threshold: Ratio::new(90, 100), + online_max_threshold: Ratio::new(99, 100), + protocol_upgrade_stake_threshold: Ratio::new(80, 100), + minimum_stake_divisor: 1, + validator_selection_config: Default::default(), + shard_layout: ShardLayout::v0(num_shards, 0), + validator_max_kickout_stake_perc: 100, + }; + AllEpochConfig::new(use_production_config, epoch_config, "test-chain") +} + +pub fn epoch_config( + epoch_length: BlockHeightDelta, + num_shards: NumShards, + num_block_producer_seats: NumSeats, + num_hidden_validator_seats: NumSeats, + block_producer_kickout_threshold: u8, + chunk_producer_kickout_threshold: u8, + fishermen_threshold: Balance, +) -> AllEpochConfig { + epoch_config_with_production_config( + epoch_length, + num_shards, + num_block_producer_seats, + num_hidden_validator_seats, + block_producer_kickout_threshold, + chunk_producer_kickout_threshold, + fishermen_threshold, + false, + ) +} + +pub fn do_power(account_id: AccountId, power: Power) -> ValidatorPower { + let public_key = SecretKey::from_seed(KeyType::ED25519, account_id.as_ref()).public_key(); + ValidatorPower::new(account_id, public_key, power) +} + +pub fn frozen(account_id: AccountId, frozen: Balance) -> ValidatorFrozen { + let public_key = SecretKey::from_seed(KeyType::ED25519, account_id.as_ref()).public_key(); + ValidatorFrozen::new(account_id, public_key, frozen) +} + +/// No-op reward calculator. Will produce no reward +pub fn default_reward_calculator() -> RewardCalculator { + RewardCalculator { + max_inflation_rate: Ratio::from_integer(0), + num_blocks_per_year: 1, + epoch_length: 1, + protocol_reward_rate: Ratio::from_integer(0), + protocol_treasury_account: "near".parse().unwrap(), + online_min_threshold: Ratio::new(90, 100), + online_max_threshold: Ratio::new(99, 100), + num_seconds_per_year: NUM_SECONDS_IN_A_YEAR, + } +} + +pub fn reward(info: Vec<(AccountId, Balance)>) -> HashMap { + info.into_iter().collect() +} + +pub fn setup_epoch_manager( + power_validators: Vec<(AccountId, Power)>, + frozen_validators: Vec<(AccountId, Balance)>, + epoch_length: BlockHeightDelta, + num_shards: NumShards, + num_block_producer_seats: NumSeats, + num_hidden_validator_seats: NumSeats, + block_producer_kickout_threshold: u8, + chunk_producer_kickout_threshold: u8, + fishermen_threshold: Balance, + reward_calculator: RewardCalculator, +) -> EpochManager { + let store = create_test_store(); + let config = epoch_config( + epoch_length, + num_shards, + num_block_producer_seats, + num_hidden_validator_seats, + block_producer_kickout_threshold, + chunk_producer_kickout_threshold, + fishermen_threshold, + ); + EpochManager::new( + store, + config, + PROTOCOL_VERSION, + reward_calculator, + power_validators + .iter() + .map(|(account_id, power)| do_power(account_id.clone(), *power,)) + .collect(), + frozen_validators + .iter() + .map(|(account_id, balance)| frozen(account_id.clone(), *balance)) + .collect(), + ) + .unwrap() +} + +pub fn setup_default_epoch_manager( + power_validators: Vec<(AccountId, Power)>, + frozen_validators: Vec<(AccountId, Balance)>, + epoch_length: BlockHeightDelta, + num_shards: NumShards, + num_block_producer_seats: NumSeats, + num_hidden_validator_seats: NumSeats, + block_producer_kickout_threshold: u8, + chunk_producer_kickout_threshold: u8, +) -> EpochManager { + setup_epoch_manager( + power_validators, + frozen_validators, + epoch_length, + num_shards, + num_block_producer_seats, + num_hidden_validator_seats, + block_producer_kickout_threshold, + chunk_producer_kickout_threshold, + 1, + default_reward_calculator(), + ) +} + +/// Makes an EpochManager with the given block and chunk producers, +/// automatically coming up with stakes for them to ensure the desired +/// election outcome. +pub fn setup_epoch_manager_with_block_and_chunk_producers( + store: Store, + block_producers: Vec, + chunk_only_producers: Vec, + num_shards: NumShards, + epoch_length: BlockHeightDelta, +) -> EpochManager { + let num_block_producers = block_producers.len() as u64; + let block_producer_power = 1_000_000 as u64; + let block_producer_frozen = 1_000_000 as u128; + let mut total_frozen = 0; + let mut total_power = 0; + let mut power_validators = vec![]; + let mut frozen_validators = vec![]; + for block_producer in &block_producers { + power_validators.push((block_producer.clone(), block_producer_power)); + frozen_validators.push((block_producer.clone(), block_producer_frozen)); + total_frozen += block_producer_frozen; + total_power += block_producer_power; + } + for chunk_only_producer in &chunk_only_producers { + let minimum_frozen_to_ensure_election = + total_frozen * 160 / 1_000_000 / num_shards as u128 + 1; + let frozen = block_producer_frozen - 1; + assert!( + frozen >= minimum_frozen_to_ensure_election, + "Could not honor the specified list of producers" + ); + let minimum_power_to_ensure_election = + total_power * 160 / 1_000_000 / num_shards as u64 + 1; + let power = block_producer_power - 1; + assert!( + power >= minimum_power_to_ensure_election, + "Could not honor the specified list of producers" + ); + power_validators.push((chunk_only_producer.clone(), power)); + frozen_validators.push((chunk_only_producer.clone(), frozen)); + total_frozen += frozen; + total_power += power; + } + let config = epoch_config(epoch_length, num_shards, num_block_producers, 0, 0, 0, 0); + let epoch_manager = EpochManager::new( + store, + config, + PROTOCOL_VERSION, + default_reward_calculator(), + power_validators + .iter() + .map(|(account_id, power)| do_power(account_id.clone(), *power)) + .collect(), + frozen_validators + .iter() + .map(|(account_id, balance)| frozen(account_id.clone(), *balance)) + .collect(), + ) + .unwrap(); + // Sanity check that the election results are indeed as expected. + let actual_block_producers = epoch_manager + .get_all_block_producers_ordered(&EpochId::default(), &CryptoHash::default()) + .unwrap(); + assert_eq!(actual_block_producers.len(), block_producers.len()); + let actual_chunk_producers = + epoch_manager.get_all_chunk_producers(&EpochId::default()).unwrap(); + assert_eq!(actual_chunk_producers.len(), block_producers.len() + chunk_only_producers.len()); + epoch_manager +} + +pub fn record_block_with_final_block_hash( + epoch_manager: &mut EpochManager, + prev_h: CryptoHash, + cur_h: CryptoHash, + last_final_block_hash: CryptoHash, + height: BlockHeight, + power_proposals: Vec, + frozen_proposals: Vec, +) { + epoch_manager + .record_block_info( + BlockInfo::new( + cur_h, + height, + height.saturating_sub(2), + last_final_block_hash, + prev_h, + power_proposals.clone(), + frozen_proposals.clone(), + vec![], + vec![], + DEFAULT_TOTAL_SUPPLY, + PROTOCOL_VERSION, + height * NUM_NS_IN_SECOND, + CryptoHash::default(), + vec![], + HashMap::default(), + vec![], + vec![], + vec![], + HashMap::default(), + BTreeMap::default(), + BTreeMap::default(), + HashMap::default(), + 0, + 0, + power_proposals, + frozen_proposals, + HashMap::default(), + ValidatorMandates::default(), + ), + [0; 32], + ) + .unwrap() + .commit() + .unwrap(); +} + +pub fn record_block_with_slashes( + epoch_manager: &mut EpochManager, + prev_h: CryptoHash, + cur_h: CryptoHash, + height: BlockHeight, + power_proposals: Vec, + frozen_proposals: Vec, + slashed: Vec, +) { + epoch_manager + .record_block_info( + BlockInfo::new( + cur_h, + height, + height.saturating_sub(2), + prev_h, + prev_h, + power_proposals.clone(), + frozen_proposals.clone(), + vec![], + slashed, + DEFAULT_TOTAL_SUPPLY, + PROTOCOL_VERSION, + height * NUM_NS_IN_SECOND, + CryptoHash::default(), + vec![], + HashMap::default(), + vec![], + vec![], + vec![], + HashMap::default(), + BTreeMap::default(), + BTreeMap::default(), + HashMap::default(), + 0, + 0, + power_proposals, + frozen_proposals, + HashMap::default(), + ValidatorMandates::default(), + ), + [0; 32], + ) + .unwrap() + .commit() + .unwrap(); +} + +pub fn record_block( + epoch_manager: &mut EpochManager, + prev_h: CryptoHash, + cur_h: CryptoHash, + height: BlockHeight, + power_proposals: Vec, + frozen_proposals: Vec +) { + record_block_with_slashes(epoch_manager, prev_h, cur_h, height, power_proposals, frozen_proposals, vec![]); +} + +pub fn block_info( + hash: CryptoHash, + height: BlockHeight, + last_finalized_height: BlockHeight, + last_final_block_hash: CryptoHash, + prev_hash: CryptoHash, + epoch_first_block: CryptoHash, + chunk_mask: Vec, + total_supply: Balance, + random_value: CryptoHash, + validators: Vec, + validator_to_index: HashMap, + block_producers_settlement: Vec, + chunk_producers_settlement: Vec>, + fishermen: Vec, + fishermen_to_index: HashMap, + power_change: BTreeMap, + frozen_change: BTreeMap, + validator_reward: HashMap, + seat_price: Balance, + minted_amount: Balance, + all_power_proposals: Vec, + all_frozen_proposals: Vec, + validator_kickout: HashMap, + validator_mandates: ValidatorMandates, +) -> BlockInfo { + BlockInfo::V2(BlockInfoV2 { + hash, + height, + last_finalized_height, + last_final_block_hash, + prev_hash, + epoch_first_block, + epoch_id: Default::default(), + power_proposals: vec![], + frozen_proposals: vec![], + chunk_mask, + latest_protocol_version: PROTOCOL_VERSION, + slashed: Default::default(), + total_supply, + timestamp_nanosec: height * NUM_NS_IN_SECOND, + random_value, + validators, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + fishermen, + fishermen_to_index, + power_change, + frozen_change, + validator_reward, + seat_price, + minted_amount, + all_power_proposals, + all_frozen_proposals, + validator_kickout, + validator_mandates, + }) +} + +pub fn record_with_block_info(epoch_manager: &mut EpochManager, block_info: BlockInfo) { + epoch_manager.record_block_info(block_info, [0; 32]).unwrap().commit().unwrap(); +} diff --git a/chain/epoch-manager/src/tests/mod.rs b/chain/epoch-manager/src/tests/mod.rs new file mode 100644 index 000000000..765fa6bbb --- /dev/null +++ b/chain/epoch-manager/src/tests/mod.rs @@ -0,0 +1,2656 @@ +mod random_epochs; + +use super::*; +use crate::reward_calculator::NUM_NS_IN_SECOND; +use crate::test_utils::{ + block_info, change_power, default_reward_calculator, epoch_config, + epoch_config_with_production_config, epoch_info, epoch_info_with_num_seats, hash_range, + record_block, record_block_with_final_block_hash, record_block_with_slashes, + record_with_block_info, reward, setup_default_epoch_manager, setup_epoch_manager, do_power, + DEFAULT_TOTAL_SUPPLY, +}; +use unc_primitives::account::id::AccountIdRef; +use unc_primitives::challenge::SlashedValidator; +use unc_primitives::epoch_manager::EpochConfig; +use unc_primitives::hash::hash; +use unc_primitives::shard_layout::ShardLayout; +use unc_primitives::types::ValidatorKickoutReason::{NotEnoughBlocks, NotEnoughChunks}; +use unc_primitives::version::ProtocolFeature::SimpleNightshade; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_store::test_utils::create_test_store; +use num_rational::Ratio; +use unc_primitives::types::Power; + +impl EpochManager { + /// Returns number of produced and expected blocks by given validator. + fn get_num_validator_blocks( + &mut self, + epoch_id: &EpochId, + last_known_block_hash: &CryptoHash, + account_id: &AccountId, + ) -> Result { + let epoch_info = self.get_epoch_info(epoch_id)?; + let validator_id = *epoch_info + .get_validator_id(account_id) + .ok_or_else(|| EpochError::NotAValidator(account_id.clone(), epoch_id.clone()))?; + let aggregator = self.get_epoch_info_aggregator_upto_last(last_known_block_hash)?; + Ok(aggregator + .block_tracker + .get(&validator_id) + .unwrap_or_else(|| &ValidatorStats { produced: 0, expected: 0 }) + .clone()) + } +} + +#[test] +fn test_power_validator() { + let amount_powered :Power = 1_000_000; + let validators = vec![("test1".parse().unwrap(), amount_powered)]; + let mut epoch_manager = setup_default_epoch_manager(validators.clone(), 1, 1, 2, 2, 90, 60); + + let h = hash_range(4); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + + let expected0 = epoch_info_with_num_seats( + 1, + vec![("test1".parse().unwrap(), amount_powered)], + vec![0, 0], + vec![vec![0, 0]], + vec![], + vec![], + change_power(vec![("test1".parse().unwrap(), amount_powered)]), + vec![], + reward(vec![("near".parse().unwrap(), 0)]), + 0, + 4, + ); + let compare_epoch_infos = |a: &EpochInfo, b: &EpochInfo| -> bool { + a.validators_iter().eq(b.validators_iter()) + && a.fishermen_iter().eq(b.fishermen_iter()) + && a.stake_change() == b.stake_change() + && a.validator_kickout() == b.validator_kickout() + && a.validator_reward() == b.validator_reward() + }; + let epoch0 = epoch_manager.get_epoch_id(&h[0]).unwrap(); + assert!(compare_epoch_infos(&epoch_manager.get_epoch_info(&epoch0).unwrap(), &expected0)); + + record_block( + &mut epoch_manager, + h[0], + h[1], + 1, + vec![do_power("test2".parse().unwrap(), amount_powered)], + ); + let epoch1 = epoch_manager.get_epoch_id(&h[1]).unwrap(); + assert!(compare_epoch_infos(&epoch_manager.get_epoch_info(&epoch1).unwrap(), &expected0)); + assert_eq!(epoch_manager.get_epoch_id(&h[2]), Err(EpochError::MissingBlock(h[2]))); + + record_block(&mut epoch_manager, h[1], h[2], 2, vec![]); + // test2 staked in epoch 1 and therefore should be included in epoch 3. + let epoch2 = epoch_manager.get_epoch_id(&h[2]).unwrap(); + assert!(compare_epoch_infos(&epoch_manager.get_epoch_info(&epoch2).unwrap(), &expected0)); + + record_block(&mut epoch_manager, h[2], h[3], 3, vec![]); + + let expected3 = epoch_info_with_num_seats( + 2, + vec![("test1".parse().unwrap(), amount_powered), ("test2".parse().unwrap(), amount_powered)], + vec![0, 1], + vec![vec![0, 1]], + vec![], + vec![], + change_power(vec![ + ("test1".parse().unwrap(), amount_powered), + ("test2".parse().unwrap(), amount_powered), + ]), + vec![], + // only the validator who produced the block in this epoch gets the reward since epoch length is 1 + reward(vec![("test1".parse().unwrap(), 0), ("near".parse().unwrap(), 0)]), + 0, + 4, + ); + // no validator change in the last epoch + let epoch3 = epoch_manager.get_epoch_id(&h[3]).unwrap(); + assert!(compare_epoch_infos(&epoch_manager.get_epoch_info(&epoch3).unwrap(), &expected3)); + + // Start another epoch manager from the same store to check that it saved the state. + let epoch_manager2 = EpochManager::new( + epoch_manager.store.clone(), + epoch_manager.config.clone(), + PROTOCOL_VERSION, + epoch_manager.reward_calculator, + validators + .iter() + .map(|(account_id, power)| do_power(account_id.clone(), *power)) + .collect(), + ) + .unwrap(); + assert!(compare_epoch_infos(&epoch_manager2.get_epoch_info(&epoch3).unwrap(), &expected3)); +} + +#[test] +fn test_validator_change_of_stake() { + let amount_staked = 1_000_000; + let fishermen_threshold = 100; + let validators = + vec![("test1".parse().unwrap(), amount_staked), ("test2".parse().unwrap(), amount_staked)]; + let mut epoch_manager = setup_epoch_manager( + validators, + 2, + 1, + 2, + 0, + 90, + 60, + fishermen_threshold, + default_reward_calculator(), + ); + + let h = hash_range(4); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + record_block(&mut epoch_manager, h[0], h[1], 1, vec![do_power("test1".parse().unwrap(), 10)]); + record_block(&mut epoch_manager, h[1], h[2], 2, vec![]); + // New epoch starts here. + record_block(&mut epoch_manager, h[2], h[3], 3, vec![]); + let epoch_id = epoch_manager.get_next_epoch_id(&h[3]).unwrap(); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id).unwrap(); + check_validators(&epoch_info, &[("test2", amount_staked)]); + check_fishermen(&epoch_info, &[]); + check_stake_change( + &epoch_info, + vec![("test1".parse().unwrap(), 0), ("test2".parse().unwrap(), amount_staked)], + ); + check_reward( + &epoch_info, + vec![ + ("test1".parse().unwrap(), 0), + ("test2".parse().unwrap(), 0), + ("near".parse().unwrap(), 0), + ], + ); + matches!( + epoch_info.validator_kickout().get(AccountIdRef::new_or_panic("test1")), + Some(ValidatorKickoutReason::NotEnoughPower { power: 10, .. }) + ); +} + +/// Test handling forks across the epoch finalization. +/// Fork with where one BP produces blocks in one chain and 2 BPs are in another chain. +/// | | /--1---4------|--7---10------|---13--- +/// x-|-0-|- +/// | | \--2---3---5--|---6---8---9--|----11---12-- +/// In upper fork, only test2 left + new validator test4. +/// In lower fork, test1 and test3 are left. +#[test] +fn test_fork_finalization() { + let amount_powered = 1_000_000; + let validators = vec![ + ("test1".parse().unwrap(), amount_powered), + ("test2".parse().unwrap(), amount_powered), + ("test3".parse().unwrap(), amount_powered), + ]; + let epoch_length = 20; + let mut epoch_manager = + setup_default_epoch_manager(validators.clone(), epoch_length, 1, 3, 0, 90, 60); + + let h = hash_range((5 * epoch_length - 1) as usize); + // Have an alternate set of hashes to use on the other branch to avoid collisions. + let h2: Vec = h.iter().map(|x| hash(x.as_ref())).collect(); + + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + + let build_branch = |epoch_manager: &mut EpochManager, + base_block: CryptoHash, + hashes: &[CryptoHash], + validator_accounts: &[&str]| + -> Vec { + let mut prev_block = base_block; + let mut branch_blocks = Vec::new(); + for (i, curr_block) in hashes.iter().enumerate().skip(2) { + let height = i as u64; + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(&prev_block).unwrap(); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id).unwrap().clone(); + let block_producer_id = EpochManager::block_producer_from_info(&epoch_info, height); + let block_producer = epoch_info.get_validator(block_producer_id); + let account_id = block_producer.account_id(); + if validator_accounts.iter().any(|v| *v == account_id) { + record_block(epoch_manager, prev_block, *curr_block, height, vec![]); + prev_block = *curr_block; + branch_blocks.push(*curr_block); + } + } + branch_blocks + }; + + // build test2/test4 fork + record_block( + &mut epoch_manager, + h[0], + h[1], + 1, + vec![do_power("test4".parse().unwrap(), amount_powered)], + ); + let blocks_test2 = build_branch(&mut epoch_manager, h[1], &h, &["test2", "test4"]); + + // build test1/test3 fork + let blocks_test1 = build_branch(&mut epoch_manager, h[0], &h2, &["test1", "test3"]); + + let epoch1 = epoch_manager.get_epoch_id(&h[1]).unwrap(); + let mut bps = epoch_manager + .get_all_block_producers_ordered(&epoch1, &h[1]) + .unwrap() + .iter() + .map(|x| (x.0.account_id().clone(), x.1)) + .collect::>(); + bps.sort_unstable(); + assert_eq!( + bps, + vec![ + ("test1".parse().unwrap(), false), + ("test2".parse().unwrap(), false), + ("test3".parse().unwrap(), false) + ] + ); + + let last_block = blocks_test2.last().unwrap(); + let epoch2_1 = epoch_manager.get_epoch_id(last_block).unwrap(); + assert_eq!( + epoch_manager + .get_all_block_producers_ordered(&epoch2_1, &h[1]) + .unwrap() + .iter() + .map(|x| (x.0.account_id().clone(), x.1)) + .collect::>(), + vec![("test2".parse().unwrap(), false), ("test4".parse().unwrap(), false)] + ); + + let last_block = blocks_test1.last().unwrap(); + let epoch2_2 = epoch_manager.get_epoch_id(last_block).unwrap(); + assert_eq!( + epoch_manager + .get_all_block_producers_ordered(&epoch2_2, &h[1]) + .unwrap() + .iter() + .map(|x| (x.0.account_id().clone(), x.1)) + .collect::>(), + vec![("test1".parse().unwrap(), false), ("test3".parse().unwrap(), false),] + ); + + // Check that if we have a different epoch manager and apply only second branch we get the same results. + let mut epoch_manager2 = setup_default_epoch_manager(validators, epoch_length, 1, 3, 0, 90, 60); + record_block(&mut epoch_manager2, CryptoHash::default(), h[0], 0, vec![]); + build_branch(&mut epoch_manager2, h[0], &h2, &["test1", "test3"]); + assert_eq!(epoch_manager.get_epoch_info(&epoch2_2), epoch_manager2.get_epoch_info(&epoch2_2)); +} + +/// In the case where there is only one validator and the +/// number of blocks produced by the validator is under the +/// threshold for some given epoch, the validator should not +/// be kicked out +#[test] +fn test_one_validator_kickout() { + let amount_staked = 1_000; + let mut epoch_manager = setup_default_epoch_manager( + vec![("test1".parse().unwrap(), amount_staked)], + 2, + 1, + 1, + 0, + 90, + 60, + ); + + let h = hash_range(6); + // this validator only produces one block every epoch whereas they should have produced 2. However, since + // this is the only validator left, we still keep them as validator. + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + record_block(&mut epoch_manager, h[0], h[2], 2, vec![]); + record_block(&mut epoch_manager, h[2], h[4], 4, vec![]); + record_block(&mut epoch_manager, h[4], h[5], 5, vec![]); + let epoch_id = epoch_manager.get_next_epoch_id(&h[5]).unwrap(); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id).unwrap(); + check_validators(&epoch_info, &[("test1", amount_staked)]); + check_fishermen(&epoch_info, &[]); + check_kickout(&epoch_info, &[]); + check_stake_change(&epoch_info, vec![("test1".parse().unwrap(), amount_staked)]); +} + +/// When computing validator kickout, we should not kickout validators such that the union +/// of kickout for this epoch and last epoch equals the entire validator set. +#[test] +fn test_validator_kickout() { + let amount_staked = 1_000_000; + let validators = + vec![("test1".parse().unwrap(), amount_staked), ("test2".parse().unwrap(), amount_staked)]; + let epoch_length = 10; + let mut epoch_manager = setup_default_epoch_manager(validators, epoch_length, 1, 2, 0, 90, 60); + let h = hash_range((3 * epoch_length) as usize); + + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + let mut prev_block = h[0]; + let mut test2_expected_blocks = 0; + let init_epoch_id = epoch_manager.get_epoch_id_from_prev_block(&prev_block).unwrap(); + for (i, curr_block) in h.iter().enumerate().skip(1) { + let height = i as u64; + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(&prev_block).unwrap(); + let block_producer = epoch_manager.get_block_producer_info(&epoch_id, height).unwrap(); + if block_producer.account_id() == "test2" && epoch_id == init_epoch_id { + // test2 skips its blocks in the first epoch + test2_expected_blocks += 1; + } else if block_producer.account_id() == "test1" && epoch_id != init_epoch_id { + // test1 skips its blocks in subsequent epochs + () + } else { + record_block(&mut epoch_manager, prev_block, *curr_block, height, vec![]); + prev_block = *curr_block; + } + } + let epoch_infos: Vec<_> = + h.iter().filter_map(|x| epoch_manager.get_epoch_info(&EpochId(*x)).ok()).collect(); + check_kickout( + &epoch_infos[1], + &[( + "test2", + ValidatorKickoutReason::NotEnoughBlocks { + produced: 0, + expected: test2_expected_blocks, + }, + )], + ); + let epoch_info = &epoch_infos[2]; + check_validators(epoch_info, &[("test1", amount_staked)]); + check_fishermen(epoch_info, &[]); + check_stake_change(epoch_info, vec![("test1".parse().unwrap(), amount_staked)]); + check_kickout(epoch_info, &[]); + check_reward( + epoch_info, + vec![ + ("test2".parse().unwrap(), 0), + ("near".parse().unwrap(), 0), + ("test1".parse().unwrap(), 0), + ], + ); +} + +#[test] +fn test_validator_unstake() { + let store = create_test_store(); + let config = epoch_config(2, 1, 2, 0, 90, 60, 0); + let amount_staked = 1_000_000; + let validators = vec![ + do_power("test1".parse().unwrap(), amount_staked), + do_power("test2".parse().unwrap(), amount_staked), + ]; + let mut epoch_manager = + EpochManager::new(store, config, PROTOCOL_VERSION, default_reward_calculator(), validators) + .unwrap(); + let h = hash_range(8); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + // test1 unstakes in epoch 1, and should be kicked out in epoch 3 (validators stored at h2). + record_block(&mut epoch_manager, h[0], h[1], 1, vec![do_power("test1".parse().unwrap(), 0)]); + record_block(&mut epoch_manager, h[1], h[2], 2, vec![]); + record_block(&mut epoch_manager, h[2], h[3], 3, vec![]); + + let epoch_id = epoch_manager.get_next_epoch_id(&h[3]).unwrap(); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id).unwrap(); + check_validators(&epoch_info, &[("test2", amount_staked)]); + check_fishermen(&epoch_info, &[]); + check_stake_change( + &epoch_info, + vec![("test1".parse().unwrap(), 0), ("test2".parse().unwrap(), amount_staked)], + ); + check_kickout(&epoch_info, &[("test1", ValidatorKickoutReason::Unpowered)]); + check_reward( + &epoch_info, + vec![ + ("test1".parse().unwrap(), 0), + ("test2".parse().unwrap(), 0), + ("near".parse().unwrap(), 0), + ], + ); + + record_block(&mut epoch_manager, h[3], h[4], 4, vec![]); + record_block(&mut epoch_manager, h[4], h[5], 5, vec![]); + let epoch_id = epoch_manager.get_next_epoch_id(&h[5]).unwrap(); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id).unwrap(); + check_validators(&epoch_info, &[("test2", amount_staked)]); + check_fishermen(&epoch_info, &[]); + check_stake_change(&epoch_info, vec![("test2".parse().unwrap(), amount_staked)]); + check_kickout(&epoch_info, &[]); + check_reward( + &epoch_info, + vec![ + ("test1".parse().unwrap(), 0), + ("test2".parse().unwrap(), 0), + ("near".parse().unwrap(), 0), + ], + ); + + record_block(&mut epoch_manager, h[5], h[6], 6, vec![]); + record_block(&mut epoch_manager, h[6], h[7], 7, vec![]); + let epoch_id = epoch_manager.get_next_epoch_id(&h[7]).unwrap(); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id).unwrap(); + check_validators(&epoch_info, &[("test2", amount_staked)]); + check_fishermen(&epoch_info, &[]); + check_stake_change(&epoch_info, vec![("test2".parse().unwrap(), amount_staked)]); + check_kickout(&epoch_info, &[]); + check_reward(&epoch_info, vec![("test2".parse().unwrap(), 0), ("near".parse().unwrap(), 0)]); +} + +#[test] +fn test_slashing() { + let store = create_test_store(); + let config = epoch_config(2, 1, 2, 0, 90, 60, 0); + let amount_staked = 1_000_000; + let validators = vec![ + do_power("test1".parse().unwrap(), amount_staked), + do_power("test2".parse().unwrap(), amount_staked), + ]; + let mut epoch_manager = + EpochManager::new(store, config, PROTOCOL_VERSION, default_reward_calculator(), validators) + .unwrap(); + + let h = hash_range(10); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + + // Slash test1 + let mut slashed = HashMap::new(); + slashed.insert("test1".parse::().unwrap(), SlashState::Other); + record_block_with_slashes( + &mut epoch_manager, + h[0], + h[1], + 1, + vec![], + vec![SlashedValidator::new("test1".parse().unwrap(), false)], + ); + + let epoch_id = epoch_manager.get_epoch_id(&h[1]).unwrap(); + let mut bps = epoch_manager + .get_all_block_producers_ordered(&epoch_id, &h[1]) + .unwrap() + .iter() + .map(|x| (x.0.account_id().clone(), x.1)) + .collect::>(); + bps.sort_unstable(); + assert_eq!(bps, vec![("test1".parse().unwrap(), true), ("test2".parse().unwrap(), false)]); + + record_block(&mut epoch_manager, h[1], h[2], 2, vec![]); + record_block(&mut epoch_manager, h[2], h[3], 3, vec![]); + record_block(&mut epoch_manager, h[3], h[4], 4, vec![]); + // Epoch 3 -> defined by proposals/slashes in h[1]. + record_block(&mut epoch_manager, h[4], h[5], 5, vec![]); + + let epoch_id = epoch_manager.get_epoch_id(&h[5]).unwrap(); + assert_eq!(epoch_id.0, h[2]); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id).unwrap(); + check_validators(&epoch_info, &[("test2", amount_staked)]); + check_fishermen(&epoch_info, &[]); + check_stake_change( + &epoch_info, + vec![("test1".parse().unwrap(), 0), ("test2".parse().unwrap(), amount_staked)], + ); + check_kickout(&epoch_info, &[("test1", ValidatorKickoutReason::Slashed)]); + + let slashed1: Vec<_> = + epoch_manager.get_block_info(&h[2]).unwrap().slashed().clone().into_iter().collect(); + let slashed2: Vec<_> = + epoch_manager.get_block_info(&h[3]).unwrap().slashed().clone().into_iter().collect(); + let slashed3: Vec<_> = + epoch_manager.get_block_info(&h[5]).unwrap().slashed().clone().into_iter().collect(); + assert_eq!(slashed1, vec![("test1".parse().unwrap(), SlashState::Other)]); + assert_eq!(slashed2, vec![("test1".parse().unwrap(), SlashState::AlreadySlashed)]); + assert_eq!(slashed3, vec![("test1".parse().unwrap(), SlashState::AlreadySlashed)]); +} + +/// Test that double sign interacts with other challenges in the correct way. +#[test] +fn test_double_sign_slashing1() { + let store = create_test_store(); + let config = epoch_config(2, 1, 2, 0, 90, 60, 0); + let amount_powered = 1_000_000; + let validators = vec![ + do_power("test1".parse().unwrap(), amount_powered), + do_power("test2".parse().unwrap(), amount_powered), + ]; + let mut epoch_manager = + EpochManager::new(store, config, PROTOCOL_VERSION, default_reward_calculator(), validators) + .unwrap(); + + let h = hash_range(10); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + record_block(&mut epoch_manager, h[0], h[1], 1, vec![]); + record_block_with_slashes( + &mut epoch_manager, + h[1], + h[2], + 2, + vec![], + vec![ + SlashedValidator::new("test1".parse().unwrap(), true), + SlashedValidator::new("test1".parse().unwrap(), false), + ], + ); + let slashed: Vec<_> = + epoch_manager.get_block_info(&h[2]).unwrap().slashed().clone().into_iter().collect(); + assert_eq!(slashed, vec![("test1".parse().unwrap(), SlashState::Other)]); + record_block(&mut epoch_manager, h[2], h[3], 3, vec![]); + // new epoch + let slashed: Vec<_> = + epoch_manager.get_block_info(&h[3]).unwrap().slashed().clone().into_iter().collect(); + assert_eq!(slashed, vec![("test1".parse().unwrap(), SlashState::AlreadySlashed)]); + // slash test1 for double sign + record_block_with_slashes( + &mut epoch_manager, + h[3], + h[4], + 4, + vec![], + vec![SlashedValidator::new("test1".parse().unwrap(), true)], + ); + + // Epoch 3 -> defined by proposals/slashes in h[1]. + record_block(&mut epoch_manager, h[4], h[5], 5, vec![]); + let epoch_id = epoch_manager.get_epoch_id(&h[5]).unwrap(); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id).unwrap(); + assert_eq!( + epoch_info + .validators_iter() + .map(|v| (v.account_id().clone(), v.power())) + .collect::>(), + vec![("test2".parse().unwrap(), amount_powered)], + ); + assert_eq!( + epoch_info.validator_kickout(), + &[("test1".parse().unwrap(), ValidatorKickoutReason::Slashed)] + .into_iter() + .collect::>() + ); + assert_eq!( + epoch_info.stake_change(), + &change_power(vec![ + ("test1".parse().unwrap(), 0), + ("test2".parse().unwrap(), amount_powered) + ]), + ); + + let slashed: Vec<_> = + epoch_manager.get_block_info(&h[5]).unwrap().slashed().clone().into_iter().collect(); + assert_eq!(slashed, vec![("test1".parse().unwrap(), SlashState::AlreadySlashed)]); +} + +/// Test that two double sign challenge in two epochs works +#[test] +fn test_double_sign_slashing2() { + let amount_staked = 1_000_000; + let validators = + vec![("test1".parse().unwrap(), amount_staked), ("test2".parse().unwrap(), amount_staked)]; + let mut epoch_manager = setup_default_epoch_manager(validators, 2, 1, 2, 0, 90, 60); + + let h = hash_range(10); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + record_block_with_slashes( + &mut epoch_manager, + h[0], + h[1], + 1, + vec![], + vec![SlashedValidator::new("test1".parse().unwrap(), true)], + ); + + let slashed: Vec<_> = + epoch_manager.get_block_info(&h[1]).unwrap().slashed().clone().into_iter().collect(); + assert_eq!(slashed, vec![("test1".parse().unwrap(), SlashState::DoubleSign)]); + + record_block(&mut epoch_manager, h[1], h[2], 2, vec![]); + let slashed: Vec<_> = + epoch_manager.get_block_info(&h[2]).unwrap().slashed().clone().into_iter().collect(); + assert_eq!(slashed, vec![("test1".parse().unwrap(), SlashState::DoubleSign)]); + // new epoch + record_block_with_slashes( + &mut epoch_manager, + h[2], + h[3], + 3, + vec![], + vec![SlashedValidator::new("test1".parse().unwrap(), true)], + ); + let slashed: Vec<_> = + epoch_manager.get_block_info(&h[3]).unwrap().slashed().clone().into_iter().collect(); + assert_eq!(slashed, vec![("test1".parse().unwrap(), SlashState::DoubleSign)]); +} + +/// If all current validator try to unstake, we disallow that. +#[test] +fn test_all_validators_unstake() { + let stake_amount = 1_000; + let validators = vec![ + ("test1".parse().unwrap(), stake_amount), + ("test2".parse().unwrap(), stake_amount), + ("test3".parse().unwrap(), stake_amount), + ]; + let mut epoch_manager = setup_default_epoch_manager(validators, 1, 1, 3, 0, 90, 60); + let h = hash_range(5); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + // all validators are trying to unstake. + record_block( + &mut epoch_manager, + h[0], + h[1], + 1, + vec![ + do_power("test1".parse().unwrap(), 0), + do_power("test2".parse().unwrap(), 0), + do_power("test3".parse().unwrap(), 0), + ], + ); + record_block(&mut epoch_manager, h[1], h[2], 2, vec![]); + let next_epoch = epoch_manager.get_next_epoch_id(&h[2]).unwrap(); + assert_eq!( + epoch_manager.get_epoch_info(&next_epoch).unwrap().validators_iter().collect::>(), + vec![ + do_power("test1".parse().unwrap(), stake_amount), + do_power("test2".parse().unwrap(), stake_amount), + do_power("test3".parse().unwrap(), stake_amount) + ], + ); +} + +#[test] +fn test_validator_reward_one_validator() { + let stake_amount = 1_000_000; + let test1_stake_amount = 110; + let validators = vec![ + ("test1".parse().unwrap(), test1_stake_amount), + ("test2".parse().unwrap(), stake_amount), + ]; + let epoch_length = 2; + let total_supply = validators.iter().map(|(_, stake)| stake).sum(); + let reward_calculator = RewardCalculator { + max_inflation_rate: Ratio::new(5, 100), + num_blocks_per_year: 50, + epoch_length, + protocol_reward_rate: Ratio::new(1, 10), + protocol_treasury_account: "near".parse().unwrap(), + online_min_threshold: Ratio::new(90, 100), + online_max_threshold: Ratio::new(99, 100), + num_seconds_per_year: 50, + }; + let mut epoch_manager = setup_epoch_manager( + validators, + epoch_length, + 1, + 1, + 0, + 90, + 60, + 100, + reward_calculator.clone(), + ); + let rng_seed = [0; 32]; + let h = hash_range(5); + + epoch_manager + .record_block_info( + block_info( + h[0], + 0, + 0, + Default::default(), + Default::default(), + h[0], + vec![true], + total_supply, + ), + rng_seed, + ) + .unwrap(); + epoch_manager + .record_block_info( + block_info(h[1], 1, 1, h[0], h[0], h[1], vec![true], total_supply), + rng_seed, + ) + .unwrap(); + epoch_manager + .record_block_info( + block_info(h[2], 2, 2, h[1], h[1], h[1], vec![true], total_supply), + rng_seed, + ) + .unwrap(); + let mut validator_online_ratio = HashMap::new(); + validator_online_ratio.insert( + "test2".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 1, expected: 1 }, + chunk_stats: ValidatorStats { produced: 1, expected: 1 }, + }, + ); + let mut validator_stakes = HashMap::new(); + validator_stakes.insert("test2".parse().unwrap(), stake_amount); + let (validator_reward, inflation) = reward_calculator.calculate_reward( + validator_online_ratio, + &validator_stakes, + total_supply, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + epoch_length * NUM_NS_IN_SECOND, + ); + let test2_reward = *validator_reward.get(AccountIdRef::new_or_panic("test2")).unwrap(); + let protocol_reward = *validator_reward.get(AccountIdRef::new_or_panic("near")).unwrap(); + + let epoch_info = epoch_manager.get_epoch_info(&EpochId(h[2])).unwrap(); + check_validators(&epoch_info, &[("test2", stake_amount + test2_reward)]); + check_fishermen(&epoch_info, &[("test1", test1_stake_amount)]); + check_stake_change( + &epoch_info, + vec![ + ("test1".parse().unwrap(), test1_stake_amount), + ("test2".parse().unwrap(), stake_amount + test2_reward), + ], + ); + check_kickout(&epoch_info, &[]); + check_reward( + &epoch_info, + vec![("test2".parse().unwrap(), test2_reward), ("near".parse().unwrap(), protocol_reward)], + ); + assert_eq!(epoch_info.minted_amount(), inflation); +} + +#[test] +fn test_validator_reward_weight_by_stake() { + let stake_amount1 = 1_000_000; + let stake_amount2 = 500_000; + let validators = + vec![("test1".parse().unwrap(), stake_amount1), ("test2".parse().unwrap(), stake_amount2)]; + let epoch_length = 2; + let total_supply = (stake_amount1 + stake_amount2) * validators.len() as u128; + let reward_calculator = RewardCalculator { + max_inflation_rate: Ratio::new(5, 100), + num_blocks_per_year: 50, + epoch_length, + protocol_reward_rate: Ratio::new(1, 10), + protocol_treasury_account: "near".parse().unwrap(), + online_min_threshold: Ratio::new(90, 100), + online_max_threshold: Ratio::new(99, 100), + num_seconds_per_year: 50, + }; + let mut epoch_manager = setup_epoch_manager( + validators, + epoch_length, + 1, + 2, + 0, + 90, + 60, + 100, + reward_calculator.clone(), + ); + let h = hash_range(5); + record_with_block_info( + &mut epoch_manager, + block_info( + h[0], + 0, + 0, + Default::default(), + Default::default(), + h[0], + vec![true], + total_supply, + ), + ); + record_with_block_info( + &mut epoch_manager, + block_info(h[1], 1, 1, h[0], h[0], h[1], vec![true], total_supply), + ); + record_with_block_info( + &mut epoch_manager, + block_info(h[2], 2, 2, h[1], h[1], h[1], vec![true], total_supply), + ); + let mut validator_online_ratio = HashMap::new(); + validator_online_ratio.insert( + "test1".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 1, expected: 1 }, + chunk_stats: ValidatorStats { produced: 1, expected: 1 }, + }, + ); + validator_online_ratio.insert( + "test2".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 1, expected: 1 }, + chunk_stats: ValidatorStats { produced: 1, expected: 1 }, + }, + ); + let mut validators_stakes = HashMap::new(); + validators_stakes.insert("test1".parse().unwrap(), stake_amount1); + validators_stakes.insert("test2".parse().unwrap(), stake_amount2); + let (validator_reward, inflation) = reward_calculator.calculate_reward( + validator_online_ratio, + &validators_stakes, + total_supply, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + epoch_length * NUM_NS_IN_SECOND, + ); + let test1_reward = *validator_reward.get(AccountIdRef::new_or_panic("test1")).unwrap(); + let test2_reward = *validator_reward.get(AccountIdRef::new_or_panic("test2")).unwrap(); + assert_eq!(test1_reward, test2_reward * 2); + let protocol_reward = *validator_reward.get(AccountIdRef::new_or_panic("near")).unwrap(); + + let epoch_info = epoch_manager.get_epoch_info(&EpochId(h[2])).unwrap(); + check_validators( + &epoch_info, + &[("test1", stake_amount1 + test1_reward), ("test2", stake_amount2 + test2_reward)], + ); + check_fishermen(&epoch_info, &[]); + check_stake_change( + &epoch_info, + vec![ + ("test1".parse().unwrap(), stake_amount1 + test1_reward), + ("test2".parse().unwrap(), stake_amount2 + test2_reward), + ], + ); + check_kickout(&epoch_info, &[]); + check_reward( + &epoch_info, + vec![ + ("test1".parse().unwrap(), test1_reward), + ("test2".parse().unwrap(), test2_reward), + ("near".parse().unwrap(), protocol_reward), + ], + ); + assert_eq!(epoch_info.minted_amount(), inflation); +} + +#[test] +fn test_reward_multiple_shards() { + let stake_amount = 1_000_000; + let validators = + vec![("test1".parse().unwrap(), stake_amount), ("test2".parse().unwrap(), stake_amount)]; + let epoch_length = 10; + let total_supply = stake_amount * validators.len() as u128; + let reward_calculator = RewardCalculator { + max_inflation_rate: Ratio::new(5, 100), + num_blocks_per_year: 1_000_000, + epoch_length, + protocol_reward_rate: Ratio::new(1, 10), + protocol_treasury_account: "near".parse().unwrap(), + online_min_threshold: Ratio::new(90, 100), + online_max_threshold: Ratio::new(99, 100), + num_seconds_per_year: 1_000_000, + }; + let num_shards = 2; + let mut epoch_manager = setup_epoch_manager( + validators, + epoch_length, + num_shards, + 2, + 0, + 90, + 60, + 0, + reward_calculator.clone(), + ); + let h = hash_range((2 * epoch_length + 1) as usize); + record_with_block_info( + &mut epoch_manager, + block_info( + h[0], + 0, + 0, + Default::default(), + Default::default(), + h[0], + vec![true], + total_supply, + ), + ); + let mut expected_chunks = 0; + let init_epoch_id = epoch_manager.get_epoch_id_from_prev_block(&h[0]).unwrap(); + for height in 1..(2 * epoch_length) { + let i = height as usize; + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(&h[i - 1]).unwrap(); + // test1 skips its chunks in the first epoch + let chunk_mask = (0..num_shards) + .map(|shard_index| { + let expected_chunk_producer = epoch_manager + .get_chunk_producer_info(&epoch_id, height, shard_index as u64) + .unwrap(); + if expected_chunk_producer.account_id() == "test1" && epoch_id == init_epoch_id { + expected_chunks += 1; + false + } else { + true + } + }) + .collect(); + record_with_block_info( + &mut epoch_manager, + block_info(h[i], height, height, h[i - 1], h[i - 1], h[i], chunk_mask, total_supply), + ); + } + let mut validator_online_ratio = HashMap::new(); + validator_online_ratio.insert( + "test2".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 1, expected: 1 }, + chunk_stats: ValidatorStats { produced: 1, expected: 1 }, + }, + ); + let mut validators_stakes = HashMap::new(); + validators_stakes.insert("test1".parse().unwrap(), stake_amount); + validators_stakes.insert("test2".parse().unwrap(), stake_amount); + let (validator_reward, inflation) = reward_calculator.calculate_reward( + validator_online_ratio, + &validators_stakes, + total_supply, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + epoch_length * NUM_NS_IN_SECOND, + ); + let test2_reward = *validator_reward.get(AccountIdRef::new_or_panic("test2")).unwrap(); + let protocol_reward = *validator_reward.get(AccountIdRef::new_or_panic("near")).unwrap(); + let epoch_infos: Vec<_> = + h.iter().filter_map(|x| epoch_manager.get_epoch_info(&EpochId(*x)).ok()).collect(); + let epoch_info = &epoch_infos[1]; + check_validators(epoch_info, &[("test2", stake_amount + test2_reward)]); + check_fishermen(epoch_info, &[]); + check_stake_change( + epoch_info, + vec![ + ("test1".parse().unwrap(), 0), + ("test2".parse().unwrap(), stake_amount + test2_reward), + ], + ); + check_kickout( + epoch_info, + &[( + "test1", + ValidatorKickoutReason::NotEnoughChunks { produced: 0, expected: expected_chunks }, + )], + ); + check_reward( + epoch_info, + vec![("test2".parse().unwrap(), test2_reward), ("near".parse().unwrap(), protocol_reward)], + ); + assert_eq!(epoch_info.minted_amount(), inflation); +} + +#[test] +fn test_unstake_and_then_change_stake() { + let amount_powered = 1_000_000; + let validators = + vec![("test1".parse().unwrap(), amount_powered), ("test2".parse().unwrap(), amount_powered)]; + let mut epoch_manager = setup_default_epoch_manager(validators, 2, 1, 2, 0, 90, 60); + let h = hash_range(8); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + // test1 unstakes in epoch 1, and should be kicked out in epoch 3 (validators stored at h2). + record_block(&mut epoch_manager, h[0], h[1], 1, vec![do_power("test1".parse().unwrap(), 0)]); + record_block( + &mut epoch_manager, + h[1], + h[2], + 2, + vec![do_power("test1".parse().unwrap(), amount_powered)], + ); + record_block(&mut epoch_manager, h[2], h[3], 3, vec![]); + let epoch_id = epoch_manager.get_next_epoch_id(&h[3]).unwrap(); + assert_eq!(epoch_id, EpochId(h[2])); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id).unwrap(); + check_validators(&epoch_info, &[("test1", amount_powered), ("test2", amount_powered)]); + check_fishermen(&epoch_info, &[]); + check_stake_change( + &epoch_info, + vec![("test1".parse().unwrap(), amount_powered), ("test2".parse().unwrap(), amount_powered)], + ); + check_kickout(&epoch_info, &[]); + check_reward( + &epoch_info, + vec![ + ("test1".parse().unwrap(), 0), + ("test2".parse().unwrap(), 0), + ("near".parse().unwrap(), 0), + ], + ); +} + +/// When a block producer fails to produce a block, check that other chunk producers who produce +/// chunks for that block are not kicked out because of it. +#[test] +fn test_expected_chunks() { + let stake_amount = 1_000_000; + let validators = vec![ + ("test1".parse().unwrap(), stake_amount), + ("test2".parse().unwrap(), stake_amount), + ("test3".parse().unwrap(), stake_amount), + ]; + let epoch_length = 20; + let total_supply = stake_amount * validators.len() as u128; + let mut epoch_manager = setup_epoch_manager( + validators, + epoch_length, + 3, + 3, + 0, + 90, + 60, + 0, + default_reward_calculator(), + ); + let rng_seed = [0; 32]; + let hashes = hash_range((2 * epoch_length) as usize); + record_block(&mut epoch_manager, Default::default(), hashes[0], 0, vec![]); + let mut expected = 0; + let mut prev_block = hashes[0]; + let initial_epoch_id = epoch_manager.get_epoch_id_from_prev_block(&prev_block).unwrap(); + for (i, curr_block) in hashes.iter().enumerate().skip(1) { + let height = i as u64; + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(&prev_block).unwrap(); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id).unwrap().clone(); + let block_producer = EpochManager::block_producer_from_info(&epoch_info, height); + // test1 does not produce blocks during first epoch + if block_producer == 0 && epoch_id == initial_epoch_id { + expected += 1; + } else { + epoch_manager + .record_block_info( + block_info( + *curr_block, + height, + height, + prev_block, + prev_block, + epoch_id.0, + vec![true, true, true], + total_supply, + ), + rng_seed, + ) + .unwrap() + .commit() + .unwrap(); + prev_block = *curr_block; + } + if epoch_id != initial_epoch_id { + break; + } + } + let epoch_info = hashes + .iter() + .filter_map(|x| epoch_manager.get_epoch_info(&EpochId(*x)).ok()) + .last() + .unwrap(); + assert_eq!( + epoch_info.validator_kickout(), + &[( + "test1".parse::().unwrap(), + ValidatorKickoutReason::NotEnoughBlocks { produced: 0, expected } + )] + .into_iter() + .collect::>() + ); +} + +#[test] +fn test_expected_chunks_prev_block_not_produced() { + let stake_amount = 1_000_000; + let validators = vec![ + ("test1".parse().unwrap(), stake_amount), + ("test2".parse().unwrap(), stake_amount), + ("test3".parse().unwrap(), stake_amount), + ]; + let epoch_length = 50; + let total_supply = stake_amount * validators.len() as u128; + let mut epoch_manager = setup_epoch_manager( + validators, + epoch_length, + 1, + 3, + 0, + 90, + 90, + 0, + default_reward_calculator(), + ); + let rng_seed = [0; 32]; + let hashes = hash_range((2 * epoch_length) as usize); + record_block(&mut epoch_manager, Default::default(), hashes[0], 0, vec![]); + let mut expected = 0; + let mut prev_block = hashes[0]; + let initial_epoch_id = epoch_manager.get_epoch_id_from_prev_block(&prev_block).unwrap(); + for (i, curr_block) in hashes.iter().enumerate().skip(1) { + let height = i as u64; + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(&prev_block).unwrap(); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id).unwrap().clone(); + let block_producer = EpochManager::block_producer_from_info(&epoch_info, height); + let prev_block_info = epoch_manager.get_block_info(&prev_block).unwrap(); + let prev_height = prev_block_info.height(); + let expected_chunk_producer = + EpochManager::chunk_producer_from_info(&epoch_info, prev_height + 1, 0); + // test1 does not produce blocks during first epoch + if block_producer == 0 && epoch_id == initial_epoch_id { + expected += 1; + } else { + // test1 also misses all their chunks + let should_produce_chunk = expected_chunk_producer != 0; + epoch_manager + .record_block_info( + block_info( + *curr_block, + height, + height, + prev_block, + prev_block, + epoch_id.0, + vec![should_produce_chunk], + total_supply, + ), + rng_seed, + ) + .unwrap() + .commit() + .unwrap(); + prev_block = *curr_block; + } + if epoch_id != initial_epoch_id { + break; + } + } + let epoch_info = hashes + .iter() + .filter_map(|x| epoch_manager.get_epoch_info(&EpochId(*x)).ok()) + .last() + .unwrap(); + assert_eq!( + epoch_info.validator_kickout(), + &[( + "test1".parse().unwrap(), + ValidatorKickoutReason::NotEnoughBlocks { produced: 0, expected } + )] + .into_iter() + .collect::>() + ); +} + +fn update_tracker( + epoch_info: &EpochInfo, + heights: std::ops::Range, + produced_heights: &[BlockHeight], + tracker: &mut HashMap, +) { + for h in heights { + let block_producer = EpochManager::block_producer_from_info(epoch_info, h); + let entry = + tracker.entry(block_producer).or_insert(ValidatorStats { produced: 0, expected: 0 }); + if produced_heights.contains(&h) { + entry.produced += 1; + } + entry.expected += 1; + } +} + +#[test] +fn test_epoch_info_aggregator() { + let stake_amount = 1_000_000; + let validators = + vec![("test1".parse().unwrap(), stake_amount), ("test2".parse().unwrap(), stake_amount)]; + let epoch_length = 5; + let mut em = setup_epoch_manager( + validators, + epoch_length, + 1, + 2, + 0, + 10, + 10, + 0, + default_reward_calculator(), + ); + let h = hash_range(6); + record_block(&mut em, Default::default(), h[0], 0, vec![]); + record_block_with_final_block_hash(&mut em, h[0], h[1], h[0], 1, vec![]); + record_block_with_final_block_hash(&mut em, h[1], h[3], h[0], 3, vec![]); + assert_eq!(h[0], em.epoch_info_aggregator.last_block_hash); + let epoch_id = em.get_epoch_id(&h[3]).unwrap(); + let epoch_info = em.get_epoch_info(&epoch_id).unwrap(); + + let mut tracker = HashMap::new(); + update_tracker(&epoch_info, 1..4, &[1, 3], &mut tracker); + + let aggregator = em.get_epoch_info_aggregator_upto_last(&h[3]).unwrap(); + assert_eq!(aggregator.block_tracker, tracker); + // get_epoch_info_aggregator_upto_last does not change + // epoch_info_aggregator + assert_eq!(h[0], em.epoch_info_aggregator.last_block_hash); + + record_block_with_final_block_hash(&mut em, h[3], h[5], h[1], 5, vec![]); + assert_eq!(h[1], em.epoch_info_aggregator.last_block_hash); + + update_tracker(&epoch_info, 4..6, &[5], &mut tracker); + + let aggregator = em.get_epoch_info_aggregator_upto_last(&h[5]).unwrap(); + assert_eq!(aggregator.block_tracker, tracker); + assert_eq!(h[1], em.epoch_info_aggregator.last_block_hash); +} + +/// If the node stops and restarts, the aggregator should be able to recover +#[test] +fn test_epoch_info_aggregator_data_loss() { + let power_amount = 1_000_000; + let validators = + vec![("test1".parse().unwrap(), power_amount), ("test2".parse().unwrap(), power_amount)]; + let epoch_length = 5; + let mut em = setup_epoch_manager( + validators, + epoch_length, + 1, + 2, + 0, + 10, + 10, + 0, + default_reward_calculator(), + ); + let h = hash_range(6); + record_block(&mut em, Default::default(), h[0], 0, vec![]); + record_block(&mut em, h[0], h[1], 1, vec![do_power("test1".parse().unwrap(), power_amount - 10)]); + record_block(&mut em, h[1], h[3], 3, vec![do_power("test2".parse().unwrap(), power_amount + 10)]); + assert_eq!(h[1], em.epoch_info_aggregator.last_block_hash); + em.epoch_info_aggregator = EpochInfoAggregator::default(); + record_block(&mut em, h[3], h[5], 5, vec![do_power("test1".parse().unwrap(), power_amount - 1)]); + assert_eq!(h[3], em.epoch_info_aggregator.last_block_hash); + let epoch_id = em.get_epoch_id(&h[5]).unwrap(); + let epoch_info = em.get_epoch_info(&epoch_id).unwrap(); + let mut tracker = HashMap::new(); + update_tracker(&epoch_info, 1..6, &[1, 3, 5], &mut tracker); + let aggregator = em.get_epoch_info_aggregator_upto_last(&h[5]).unwrap(); + assert_eq!(aggregator.block_tracker, tracker); + assert_eq!( + aggregator.all_proposals, + vec![ + do_power("test1".parse().unwrap(), power_amount - 1), + do_power("test2".parse().unwrap(), power_amount + 10) + ] + .into_iter() + .map(|p| (p.account_id().clone(), p)) + .collect::>() + ); +} + +/// Aggregator should still work even if there is a reorg past the last final block. +#[test] +fn test_epoch_info_aggregator_reorg_past_final_block() { + let power_amount = 1_000_000; + let validators = + vec![("test1".parse().unwrap(), power_amount), ("test2".parse().unwrap(), power_amount)]; + let epoch_length = 6; + let mut em = setup_epoch_manager( + validators, + epoch_length, + 1, + 2, + 0, + 10, + 10, + 0, + default_reward_calculator(), + ); + let h = hash_range(6); + record_block(&mut em, Default::default(), h[0], 0, vec![]); + record_block_with_final_block_hash(&mut em, h[0], h[1], h[0], 1, vec![]); + record_block_with_final_block_hash(&mut em, h[1], h[2], h[0], 2, vec![]); + record_block_with_final_block_hash( + &mut em, + h[2], + h[3], + h[1], + 3, + vec![do_power("test1".parse().unwrap(), power_amount - 1)], + ); + record_block_with_final_block_hash(&mut em, h[3], h[4], h[3], 4, vec![]); + record_block_with_final_block_hash(&mut em, h[2], h[5], h[1], 5, vec![]); + let epoch_id = em.get_epoch_id(&h[5]).unwrap(); + let epoch_info = em.get_epoch_info(&epoch_id).unwrap(); + let mut tracker = HashMap::new(); + update_tracker(&epoch_info, 1..6, &[1, 2, 5], &mut tracker); + let aggregator = em.get_epoch_info_aggregator_upto_last(&h[5]).unwrap(); + assert_eq!(aggregator.block_tracker, tracker); + assert!(aggregator.all_proposals.is_empty()); +} + +#[test] +fn test_epoch_info_aggregator_reorg_beginning_of_epoch() { + let power_amount = 1_000_000; + let validators = + vec![("test1".parse().unwrap(), power_amount), ("test2".parse().unwrap(), power_amount)]; + let epoch_length = 4; + let mut em = setup_epoch_manager( + validators, + epoch_length, + 1, + 2, + 0, + 10, + 10, + 0, + default_reward_calculator(), + ); + let h = hash_range(10); + record_block(&mut em, Default::default(), h[0], 0, vec![]); + for i in 1..5 { + record_block(&mut em, h[i - 1], h[i], i as u64, vec![]); + } + record_block(&mut em, h[4], h[5], 5, vec![do_power("test1".parse().unwrap(), power_amount - 1)]); + record_block_with_final_block_hash( + &mut em, + h[5], + h[6], + h[4], + 6, + vec![do_power("test2".parse().unwrap(), power_amount - 100)], + ); + // reorg + record_block(&mut em, h[4], h[7], 7, vec![]); + let epoch_id = em.get_epoch_id(&h[7]).unwrap(); + let epoch_info = em.get_epoch_info(&epoch_id).unwrap(); + let mut tracker = HashMap::new(); + update_tracker(&epoch_info, 5..8, &[7], &mut tracker); + let aggregator = em.get_epoch_info_aggregator_upto_last(&h[7]).unwrap(); + assert_eq!(aggregator.block_tracker, tracker); + assert!(aggregator.all_proposals.is_empty()); +} + +fn count_missing_blocks( + epoch_manager: &EpochManager, + epoch_id: &EpochId, + height_range: std::ops::Range, + produced_heights: &[u64], + validator: &str, +) -> ValidatorStats { + let mut result = ValidatorStats { produced: 0, expected: 0 }; + for h in height_range { + let block_producer = epoch_manager.get_block_producer_info(epoch_id, h).unwrap(); + if validator == block_producer.account_id() { + if produced_heights.contains(&h) { + result.produced += 1; + } + result.expected += 1; + } + } + result +} + +#[test] +fn test_num_missing_blocks() { + let stake_amount = 1_000_000; + let validators = + vec![("test1".parse().unwrap(), stake_amount), ("test2".parse().unwrap(), stake_amount)]; + let epoch_length = 2; + let mut em = setup_epoch_manager( + validators, + epoch_length, + 1, + 2, + 0, + 10, + 10, + 0, + default_reward_calculator(), + ); + let h = hash_range(8); + record_block(&mut em, Default::default(), h[0], 0, vec![]); + record_block(&mut em, h[0], h[1], 1, vec![]); + record_block(&mut em, h[1], h[3], 3, vec![]); + let epoch_id = em.get_epoch_id(&h[1]).unwrap(); + assert_eq!( + em.get_num_validator_blocks(&epoch_id, &h[3], &"test1".parse().unwrap()).unwrap(), + count_missing_blocks(&mut em, &epoch_id, 1..4, &[1, 3], "test1"), + ); + assert_eq!( + em.get_num_validator_blocks(&epoch_id, &h[3], &"test2".parse().unwrap()).unwrap(), + count_missing_blocks(&mut em, &epoch_id, 1..4, &[1, 3], "test2"), + ); + + // Build chain 0 <- x <- x <- x <- ( 4 <- 5 ) <- x <- 7 + record_block(&mut em, h[0], h[4], 4, vec![]); + let epoch_id = em.get_epoch_id(&h[4]).unwrap(); + // Block 4 is first block after genesis and starts new epoch, but we actually count how many missed blocks have happened since block 0. + assert_eq!( + em.get_num_validator_blocks(&epoch_id, &h[4], &"test1".parse().unwrap()).unwrap(), + count_missing_blocks(&mut em, &epoch_id, 1..5, &[4], "test1"), + ); + assert_eq!( + em.get_num_validator_blocks(&epoch_id, &h[4], &"test2".parse().unwrap()).unwrap(), + count_missing_blocks(&mut em, &epoch_id, 1..5, &[4], "test2"), + ); + record_block(&mut em, h[4], h[5], 5, vec![]); + record_block(&mut em, h[5], h[7], 7, vec![]); + let epoch_id = em.get_epoch_id(&h[7]).unwrap(); + // The next epoch started after 5 with 6, and test2 missed their slot from perspective of block 7. + assert_eq!( + em.get_num_validator_blocks(&epoch_id, &h[7], &"test2".parse().unwrap()).unwrap(), + count_missing_blocks(&mut em, &epoch_id, 6..8, &[7], "test2"), + ); +} + +/// Test when blocks are all produced, validators can be kicked out because of not producing +/// enough chunks +#[test] +fn test_chunk_validator_kickout() { + let stake_amount = 1_000_000; + let validators = + vec![("test1".parse().unwrap(), stake_amount), ("test2".parse().unwrap(), stake_amount)]; + let epoch_length = 10; + let total_supply = stake_amount * validators.len() as u128; + let mut em = setup_epoch_manager( + validators, + epoch_length, + 4, + 2, + 0, + 90, + 70, + 0, + default_reward_calculator(), + ); + let rng_seed = [0; 32]; + let hashes = hash_range((epoch_length + 2) as usize); + record_block(&mut em, Default::default(), hashes[0], 0, vec![]); + let mut expected = 0; + for (prev_block, (height, curr_block)) in hashes.iter().zip(hashes.iter().enumerate().skip(1)) { + let height = height as u64; + let epoch_id = em.get_epoch_id_from_prev_block(prev_block).unwrap(); + let epoch_info = em.get_epoch_info(&epoch_id).unwrap().clone(); + if height < epoch_length { + let chunk_mask = (0..4) + .map(|shard_id| { + let chunk_producer = EpochManager::chunk_producer_from_info( + &epoch_info, + height, + shard_id as u64, + ); + // test1 skips chunks + if chunk_producer == 0 { + expected += 1; + false + } else { + true + } + }) + .collect(); + em.record_block_info( + block_info( + *curr_block, + height, + height - 1, + *prev_block, + *prev_block, + epoch_id.0, + chunk_mask, + total_supply, + ), + rng_seed, + ) + .unwrap(); + } else { + em.record_block_info( + block_info( + *curr_block, + height, + height - 1, + *prev_block, + *prev_block, + epoch_id.0, + vec![true, true, true, true], + total_supply, + ), + rng_seed, + ) + .unwrap(); + } + } + + let last_epoch_info = hashes.iter().filter_map(|x| em.get_epoch_info(&EpochId(*x)).ok()).last(); + assert_eq!( + last_epoch_info.unwrap().validator_kickout(), + &[( + "test1".parse().unwrap(), + ValidatorKickoutReason::NotEnoughChunks { produced: 0, expected } + )] + .into_iter() + .collect::>(), + ); +} + +#[test] +fn test_compare_epoch_id() { + let amount_staked = 1_000_000; + let validators = + vec![("test1".parse().unwrap(), amount_staked), ("test2".parse().unwrap(), amount_staked)]; + let mut epoch_manager = setup_default_epoch_manager(validators, 2, 1, 2, 0, 90, 60); + let h = hash_range(8); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + // test1 unstakes in epoch 1, and should be kicked out in epoch 3 (validators stored at h2). + record_block(&mut epoch_manager, h[0], h[1], 1, vec![do_power("test1".parse().unwrap(), 0)]); + record_block( + &mut epoch_manager, + h[1], + h[2], + 2, + vec![do_power("test1".parse().unwrap(), amount_staked)], + ); + record_block(&mut epoch_manager, h[2], h[3], 3, vec![]); + let epoch_id0 = epoch_manager.get_epoch_id(&h[0]).unwrap(); + let epoch_id1 = epoch_manager.get_epoch_id(&h[1]).unwrap(); + let epoch_id2 = epoch_manager.get_next_epoch_id(&h[1]).unwrap(); + let epoch_id3 = epoch_manager.get_next_epoch_id(&h[3]).unwrap(); + assert_eq!(epoch_manager.compare_epoch_id(&epoch_id0, &epoch_id1), Ok(Ordering::Equal)); + assert_eq!(epoch_manager.compare_epoch_id(&epoch_id2, &epoch_id3), Ok(Ordering::Less)); + assert_eq!(epoch_manager.compare_epoch_id(&epoch_id3, &epoch_id1), Ok(Ordering::Greater)); + let random_epoch_id = EpochId(hash(&[100])); + assert!(epoch_manager.compare_epoch_id(&epoch_id3, &random_epoch_id).is_err()); +} + +#[test] +fn test_fishermen() { + let stake_amount = 1_000_000; + let fishermen_threshold = 100; + let validators = vec![ + ("test1".parse().unwrap(), stake_amount), + ("test2".parse().unwrap(), stake_amount), + ("test3".parse().unwrap(), fishermen_threshold), + ("test4".parse().unwrap(), fishermen_threshold / 2), + ]; + let epoch_length = 4; + let em = setup_epoch_manager( + validators, + epoch_length, + 1, + 4, + 0, + 90, + 70, + fishermen_threshold, + default_reward_calculator(), + ); + let epoch_info = em.get_epoch_info(&EpochId::default()).unwrap(); + check_validators(&epoch_info, &[("test1", stake_amount), ("test2", stake_amount)]); + check_fishermen(&epoch_info, &[("test3", fishermen_threshold)]); + check_stake_change( + &epoch_info, + vec![ + ("test1".parse().unwrap(), stake_amount), + ("test2".parse().unwrap(), stake_amount), + ("test3".parse().unwrap(), fishermen_threshold), + ("test4".parse().unwrap(), 0), + ], + ); + check_kickout(&epoch_info, &[]); +} + +#[test] +fn test_fishermen_unstake() { + let stake_amount = 1_000_000; + let fishermen_threshold = 100; + let validators = vec![ + ("test1".parse().unwrap(), stake_amount), + ("test2".parse().unwrap(), fishermen_threshold), + ("test3".parse().unwrap(), fishermen_threshold), + ]; + let mut em = setup_epoch_manager( + validators, + 2, + 1, + 1, + 0, + 90, + 70, + fishermen_threshold, + default_reward_calculator(), + ); + let h = hash_range(5); + record_block(&mut em, CryptoHash::default(), h[0], 0, vec![]); + // fishermen unstake + record_block(&mut em, h[0], h[1], 1, vec![do_power("test2".parse().unwrap(), 0)]); + record_block(&mut em, h[1], h[2], 2, vec![do_power("test3".parse().unwrap(), 1)]); + + let epoch_info = em.get_epoch_info(&EpochId(h[2])).unwrap(); + check_validators(&epoch_info, &[("test1", stake_amount)]); + check_fishermen(&epoch_info, &[]); + check_stake_change( + &epoch_info, + vec![ + ("test1".parse().unwrap(), stake_amount), + ("test2".parse().unwrap(), 0), + ("test3".parse().unwrap(), 0), + ], + ); + let kickout = epoch_info.validator_kickout(); + assert_eq!( + kickout.get(AccountIdRef::new_or_panic("test2")).unwrap(), + &ValidatorKickoutReason::Unpowered + ); + matches!( + kickout.get(AccountIdRef::new_or_panic("test3")), + Some(ValidatorKickoutReason::NotEnoughPower { .. }) + ); +} + +#[test] +fn test_validator_consistency() { + let stake_amount = 1_000; + let validators = + vec![("test1".parse().unwrap(), stake_amount), ("test2".parse().unwrap(), stake_amount)]; + let mut epoch_manager = setup_default_epoch_manager(validators, 2, 1, 1, 0, 90, 60); + let h = hash_range(5); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + let epoch_id = epoch_manager.get_epoch_id(&h[0]).unwrap(); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id).unwrap(); + let mut actual_block_producers = HashSet::new(); + for index in epoch_info.block_producers_settlement().into_iter() { + let bp = epoch_info.validator_account_id(*index).clone(); + actual_block_producers.insert(bp); + } + for index in epoch_info.chunk_producers_settlement().into_iter().flatten() { + let bp = epoch_info.validator_account_id(*index).clone(); + actual_block_producers.insert(bp); + } + for bp in actual_block_producers { + assert!(epoch_info.account_is_validator(&bp)) + } +} + +/// Test that when epoch length is larger than the cache size of block info cache, there is +/// no unexpected error. +#[test] +fn test_finalize_epoch_large_epoch_length() { + let power_amount = 1_000; + let validators = + vec![("test1".parse().unwrap(), power_amount), ("test2".parse().unwrap(), power_amount)]; + let mut epoch_manager = + setup_default_epoch_manager(validators, (BLOCK_CACHE_SIZE + 1) as u64, 1, 2, 0, 90, 60); + let h = hash_range(BLOCK_CACHE_SIZE + 2); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + for i in 1..=(BLOCK_CACHE_SIZE + 1) { + record_block(&mut epoch_manager, h[i - 1], h[i], i as u64, vec![]); + } + let epoch_info = epoch_manager.get_epoch_info(&EpochId(h[BLOCK_CACHE_SIZE + 1])).unwrap(); + assert_eq!( + epoch_info.validators_iter().map(|v| v.account_and_power()).collect::>(), + vec![("test1".parse().unwrap(), power_amount), ("test2".parse().unwrap(), power_amount)], + ); + assert_eq!( + epoch_info.stake_change(), + &change_power(vec![ + ("test1".parse().unwrap(), power_amount), + ("test2".parse().unwrap(), power_amount) + ]), + ); + assert_eq!( + BLOCK_CACHE_SIZE + 2, + epoch_manager.epoch_info_aggregator_loop_counter.load(std::sync::atomic::Ordering::SeqCst), + "Expected every block to be visited exactly once" + ); +} + +#[test] +fn test_kickout_set() { + let power_amount = 1_000_000; + let validators = vec![ + ("test1".parse().unwrap(), power_amount), + ("test2".parse().unwrap(), 0), + ("test3".parse().unwrap(), 10), + ]; + // have two seats to that 500 would be the threshold + let mut epoch_manager = setup_default_epoch_manager(validators, 2, 1, 2, 0, 90, 60); + let h = hash_range(5); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + record_block( + &mut epoch_manager, + h[0], + h[1], + 1, + vec![do_power("test2".parse().unwrap(), power_amount)], + ); + record_block(&mut epoch_manager, h[1], h[2], 2, vec![do_power("test2".parse().unwrap(), 0)]); + let epoch_info1 = epoch_manager.get_epoch_info(&EpochId(h[2])).unwrap(); + assert_eq!( + epoch_info1.validators_iter().map(|r| r.account_id().clone()).collect::>(), + vec!["test1"] + ); + assert_eq!( + epoch_info1.stake_change().clone(), + change_power(vec![ + ("test1".parse().unwrap(), power_amount), + ("test2".parse().unwrap(), 0), + ("test3".parse().unwrap(), 10) + ]) + ); + assert!(epoch_info1.validator_kickout().is_empty()); + record_block( + &mut epoch_manager, + h[2], + h[3], + 3, + vec![do_power("test2".parse().unwrap(), power_amount)], + ); + record_block(&mut epoch_manager, h[3], h[4], 4, vec![]); + let epoch_info = epoch_manager.get_epoch_info(&EpochId(h[4])).unwrap(); + check_validators(&epoch_info, &[("test1", power_amount), ("test2", power_amount)]); + check_fishermen(&epoch_info, &[("test3", 10)]); + check_kickout(&epoch_info, &[]); + check_stake_change( + &epoch_info, + vec![ + ("test1".parse().unwrap(), power_amount), + ("test2".parse().unwrap(), power_amount), + ("test3".parse().unwrap(), 10), + ], + ); +} + +#[test] +fn test_epoch_height_increase() { + let power_amount = 1_000; + let validators = vec![ + ("test1".parse().unwrap(), power_amount), + ("test2".parse().unwrap(), power_amount), + ("test3".parse().unwrap(), power_amount), + ]; + let mut epoch_manager = setup_default_epoch_manager(validators, 1, 1, 3, 0, 90, 60); + let h = hash_range(5); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + record_block(&mut epoch_manager, h[0], h[2], 2, vec![do_power("test1".parse().unwrap(), 223)]); + record_block(&mut epoch_manager, h[2], h[4], 4, vec![]); + + let epoch_info2 = epoch_manager.get_epoch_info(&EpochId(h[2])).unwrap(); + let epoch_info3 = epoch_manager.get_epoch_info(&EpochId(h[4])).unwrap(); + assert_ne!(epoch_info2.epoch_height(), epoch_info3.epoch_height()); +} + +#[test] +/// Slashed after unstaking: slashed for 2 epochs +fn test_unstake_slash() { + let power_amount = 1_000; + let validators = vec![ + ("test1".parse().unwrap(), power_amount), + ("test2".parse().unwrap(), power_amount), + ("test3".parse().unwrap(), power_amount), + ]; + let mut epoch_manager = setup_default_epoch_manager(validators, 1, 1, 3, 0, 90, 60); + let h = hash_range(9); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + record_block(&mut epoch_manager, h[0], h[1], 1, vec![do_power("test1".parse().unwrap(), 0)]); + record_block_with_slashes( + &mut epoch_manager, + h[1], + h[2], + 2, + vec![], + vec![SlashedValidator::new("test1".parse().unwrap(), false)], + ); + record_block(&mut epoch_manager, h[2], h[3], 3, vec![]); + record_block( + &mut epoch_manager, + h[3], + h[4], + 4, + vec![do_power("test1".parse().unwrap(), power_amount)], + ); + + let epoch_info1 = epoch_manager.get_epoch_info(&EpochId(h[1])).unwrap(); + let epoch_info2 = epoch_manager.get_epoch_info(&EpochId(h[2])).unwrap(); + let epoch_info3 = epoch_manager.get_epoch_info(&EpochId(h[3])).unwrap(); + let epoch_info4 = epoch_manager.get_epoch_info(&EpochId(h[4])).unwrap(); + assert_eq!( + epoch_info1.validator_kickout().get(AccountIdRef::new_or_panic("test1")), + Some(&ValidatorKickoutReason::Unpowered) + ); + assert_eq!( + epoch_info2.validator_kickout().get(AccountIdRef::new_or_panic("test1")), + Some(&ValidatorKickoutReason::Slashed) + ); + assert_eq!( + epoch_info3.validator_kickout().get(AccountIdRef::new_or_panic("test1")), + Some(&ValidatorKickoutReason::Slashed) + ); + assert!(epoch_info4.validator_kickout().is_empty()); + assert!(epoch_info4.account_is_validator(&"test1".parse().unwrap())); +} + +#[test] +/// Slashed with no unstake in previous epoch: slashed for 3 epochs +fn test_no_unstake_slash() { + let power_amount = 1_000; + let validators = vec![ + ("test1".parse().unwrap(), power_amount), + ("test2".parse().unwrap(), power_amount), + ("test3".parse().unwrap(), power_amount), + ]; + let mut epoch_manager = setup_default_epoch_manager(validators, 1, 1, 3, 0, 90, 60); + let h = hash_range(9); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + record_block_with_slashes( + &mut epoch_manager, + h[0], + h[1], + 1, + vec![], + vec![SlashedValidator::new("test1".parse().unwrap(), false)], + ); + record_block(&mut epoch_manager, h[1], h[2], 2, vec![]); + record_block(&mut epoch_manager, h[2], h[3], 3, vec![]); + record_block( + &mut epoch_manager, + h[3], + h[4], + 4, + vec![do_power("test1".parse().unwrap(), power_amount)], + ); + + let epoch_info1 = epoch_manager.get_epoch_info(&EpochId(h[1])).unwrap(); + let epoch_info2 = epoch_manager.get_epoch_info(&EpochId(h[2])).unwrap(); + let epoch_info3 = epoch_manager.get_epoch_info(&EpochId(h[3])).unwrap(); + let epoch_info4 = epoch_manager.get_epoch_info(&EpochId(h[4])).unwrap(); + assert_eq!( + epoch_info1.validator_kickout().get(AccountIdRef::new_or_panic("test1")), + Some(&ValidatorKickoutReason::Slashed) + ); + assert_eq!( + epoch_info2.validator_kickout().get(AccountIdRef::new_or_panic("test1")), + Some(&ValidatorKickoutReason::Slashed) + ); + assert_eq!( + epoch_info3.validator_kickout().get(AccountIdRef::new_or_panic("test1")), + Some(&ValidatorKickoutReason::Slashed) + ); + assert!(epoch_info4.validator_kickout().is_empty()); + assert!(epoch_info4.account_is_validator(&"test1".parse().unwrap())); +} + +#[test] +/// Slashed right after validator rotated out +fn test_slash_non_validator() { + let power_amount = 1_000; + let validators = vec![ + ("test1".parse().unwrap(), power_amount), + ("test2".parse().unwrap(), power_amount), + ("test3".parse().unwrap(), power_amount), + ]; + let mut epoch_manager = setup_default_epoch_manager(validators, 1, 1, 3, 0, 90, 60); + let h = hash_range(9); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + record_block(&mut epoch_manager, h[0], h[1], 1, vec![do_power("test1".parse().unwrap(), 0)]); + record_block(&mut epoch_manager, h[1], h[2], 2, vec![]); + record_block_with_slashes( + &mut epoch_manager, + h[2], + h[3], + 3, + vec![], + vec![SlashedValidator::new("test1".parse().unwrap(), false)], + ); + record_block(&mut epoch_manager, h[3], h[4], 4, vec![]); + record_block( + &mut epoch_manager, + h[4], + h[5], + 5, + vec![do_power("test1".parse().unwrap(), power_amount)], + ); + + let epoch_info1 = epoch_manager.get_epoch_info(&EpochId(h[1])).unwrap(); // Unstaked + let epoch_info2 = epoch_manager.get_epoch_info(&EpochId(h[2])).unwrap(); // - + let epoch_info3 = epoch_manager.get_epoch_info(&EpochId(h[3])).unwrap(); // Slashed + let epoch_info4 = epoch_manager.get_epoch_info(&EpochId(h[4])).unwrap(); // Slashed + let epoch_info5 = epoch_manager.get_epoch_info(&EpochId(h[5])).unwrap(); // Ok + assert_eq!( + epoch_info1.validator_kickout().get(AccountIdRef::new_or_panic("test1")), + Some(&ValidatorKickoutReason::Unpowered) + ); + assert!(epoch_info2.validator_kickout().is_empty()); + assert_eq!( + epoch_info3.validator_kickout().get(AccountIdRef::new_or_panic("test1")), + Some(&ValidatorKickoutReason::Slashed) + ); + assert_eq!( + epoch_info4.validator_kickout().get(AccountIdRef::new_or_panic("test1")), + Some(&ValidatorKickoutReason::Slashed) + ); + assert!(epoch_info5.validator_kickout().is_empty()); + assert!(epoch_info5.account_is_validator(&"test1".parse().unwrap())); +} + +#[test] +/// Slashed and attempt to restake: proposal gets ignored +fn test_slash_restake() { + let stake_amount = 1_000; + let validators = vec![ + ("test1".parse().unwrap(), stake_amount), + ("test2".parse().unwrap(), stake_amount), + ("test3".parse().unwrap(), stake_amount), + ]; + let mut epoch_manager = setup_default_epoch_manager(validators, 1, 1, 3, 0, 90, 60); + let h = hash_range(9); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + record_block_with_slashes( + &mut epoch_manager, + h[0], + h[1], + 1, + vec![], + vec![SlashedValidator::new("test1".parse().unwrap(), false)], + ); + record_block( + &mut epoch_manager, + h[1], + h[2], + 2, + vec![do_power("test1".parse().unwrap(), stake_amount)], + ); + record_block(&mut epoch_manager, h[2], h[3], 3, vec![]); + record_block( + &mut epoch_manager, + h[3], + h[4], + 4, + vec![do_power("test1".parse().unwrap(), stake_amount)], + ); + let epoch_info2 = epoch_manager.get_epoch_info(&EpochId(h[2])).unwrap(); + assert!(epoch_info2.stake_change().get(AccountIdRef::new_or_panic("test1")).is_none()); + let epoch_info4 = epoch_manager.get_epoch_info(&EpochId(h[4])).unwrap(); + assert!(epoch_info4.stake_change().get(AccountIdRef::new_or_panic("test1")).is_some()); +} + +#[test] +fn test_all_kickout_edge_case() { + let power_amount = 1_000; + let validators = vec![ + ("test1".parse().unwrap(), power_amount), + ("test2".parse().unwrap(), power_amount), + ("test3".parse().unwrap(), power_amount), + ]; + const EPOCH_LENGTH: u64 = 10; + let mut epoch_manager = setup_default_epoch_manager(validators, EPOCH_LENGTH, 1, 3, 0, 90, 60); + let hashes = hash_range((8 * EPOCH_LENGTH + 1) as usize); + + record_block(&mut epoch_manager, CryptoHash::default(), hashes[0], 0, vec![]); + let mut prev_block = hashes[0]; + for (height, curr_block) in hashes.iter().enumerate().skip(1) { + let height = height as u64; + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(&prev_block).unwrap(); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id).unwrap().clone(); + let block_producer = EpochManager::block_producer_from_info(&epoch_info, height); + let block_producer = epoch_info.validator_account_id(block_producer); + if height < EPOCH_LENGTH { + // kickout test2 during first epoch + if block_producer == "test1" || block_producer == "test3" { + record_block(&mut epoch_manager, prev_block, *curr_block, height, Vec::new()); + prev_block = *curr_block; + } + } else if height < 2 * EPOCH_LENGTH { + // produce blocks as normal during the second epoch + record_block(&mut epoch_manager, prev_block, *curr_block, height, Vec::new()); + prev_block = *curr_block; + } else if height < 5 * EPOCH_LENGTH { + // no one produces blocks during epochs 3, 4, 5 + // (but only 2 get kicked out because we can't kickout all) + () + } else if height < 6 * EPOCH_LENGTH { + // produce blocks normally during epoch 6 + record_block(&mut epoch_manager, prev_block, *curr_block, height, Vec::new()); + prev_block = *curr_block; + } else if height < 7 * EPOCH_LENGTH { + // the validator which was not kicked out in epoch 6 stops producing blocks, + // but cannot be kicked out now because they are the last validator + if block_producer != epoch_info.validator_account_id(0) { + record_block(&mut epoch_manager, prev_block, *curr_block, height, Vec::new()); + prev_block = *curr_block; + } + } else { + // produce blocks normally again + record_block(&mut epoch_manager, prev_block, *curr_block, height, Vec::new()); + prev_block = *curr_block; + } + } + + let last_epoch_info = + hashes.iter().filter_map(|x| epoch_manager.get_epoch_info(&EpochId(*x)).ok()).last(); + assert_eq!(last_epoch_info.unwrap().validator_kickout(), &HashMap::default()); +} + +fn check_validators(epoch_info: &EpochInfo, expected_validators: &[(&str, u128)]) { + for (v, (account_id, power)) in + epoch_info.validators_iter().zip(expected_validators.into_iter()) + { + assert_eq!(v.account_id(), *account_id); + assert_eq!(v.power(), *power); + } +} + +fn check_fishermen(epoch_info: &EpochInfo, expected_fishermen: &[(&str, u128)]) { + for (v, (account_id, power)) in epoch_info.fishermen_iter().zip(expected_fishermen.into_iter()) + { + assert_eq!(v.account_id(), *account_id); + assert_eq!(v.power(), *power); + } +} + +fn check_stake_change(epoch_info: &EpochInfo, changes: Vec<(AccountId, u128)>) { + assert_eq!(epoch_info.stake_change(), &change_power(changes)); +} + +fn check_reward(epoch_info: &EpochInfo, changes: Vec<(AccountId, u128)>) { + assert_eq!(epoch_info.validator_reward(), &reward(changes)); +} + +fn check_kickout(epoch_info: &EpochInfo, reasons: &[(&str, ValidatorKickoutReason)]) { + let kickout = reasons + .into_iter() + .map(|(account, reason)| (account.parse().unwrap(), reason.clone())) + .collect::>(); + assert_eq!(epoch_info.validator_kickout(), &kickout); +} + +#[test] +fn test_fisherman_kickout() { + let power_amount = 1_000_000; + let validators = vec![ + ("test1".parse().unwrap(), power_amount), + ("test2".parse().unwrap(), power_amount), + ("test3".parse().unwrap(), power_amount), + ]; + let mut epoch_manager = setup_default_epoch_manager(validators, 1, 1, 3, 0, 90, 60); + let h = hash_range(6); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + record_block(&mut epoch_manager, h[0], h[1], 1, vec![do_power("test1".parse().unwrap(), 148)]); + // test1 starts as validator, + // - reduces stake in epoch T, will be fisherman in epoch T+2 + // - Misses a block in epoch T+1, will be kicked out in epoch T+3 + // - Finalize epoch T+1 => T+3 kicks test1 as fisherman without a record in stake_change + record_block(&mut epoch_manager, h[1], h[3], 3, vec![]); + + let epoch_info2 = epoch_manager.get_epoch_info(&EpochId(h[1])).unwrap(); + check_validators(&epoch_info2, &[("test2", power_amount), ("test3", power_amount)]); + check_fishermen(&epoch_info2, &[("test1", 148)]); + check_stake_change( + &epoch_info2, + vec![ + ("test1".parse().unwrap(), 148), + ("test2".parse().unwrap(), power_amount), + ("test3".parse().unwrap(), power_amount), + ], + ); + check_kickout(&epoch_info2, &[]); + + let epoch_info3 = epoch_manager.get_epoch_info(&EpochId(h[3])).unwrap(); + check_validators(&epoch_info3, &[("test2", power_amount), ("test3", power_amount)]); + check_fishermen(&epoch_info3, &[]); + check_stake_change( + &epoch_info3, + vec![ + ("test1".parse().unwrap(), 0), + ("test2".parse().unwrap(), power_amount), + ("test3".parse().unwrap(), power_amount), + ], + ); + check_kickout(&epoch_info3, &[("test1", NotEnoughBlocks { produced: 0, expected: 1 })]); +} + +fn set_block_info_protocol_version(info: &mut BlockInfo, protocol_version: ProtocolVersion) { + match info { + BlockInfo::V1(v1) => v1.latest_protocol_version = protocol_version, + BlockInfo::V2(v2) => v2.latest_protocol_version = protocol_version, + } +} + +#[test] +fn test_protocol_version_switch() { + let store = create_test_store(); + let config = epoch_config(2, 1, 2, 0, 90, 60, 0); + let amount_powered = 1_000_000; + let validators = vec![ + do_power("test1".parse().unwrap(), amount_powered), + do_power("test2".parse().unwrap(), amount_powered), + ]; + let mut epoch_manager = + EpochManager::new(store, config, 0, default_reward_calculator(), validators).unwrap(); + let h = hash_range(8); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + let mut block_info1 = block_info(h[1], 1, 1, h[0], h[0], h[0], vec![], DEFAULT_TOTAL_SUPPLY); + set_block_info_protocol_version(&mut block_info1, 0); + epoch_manager.record_block_info(block_info1, [0; 32]).unwrap(); + for i in 2..6 { + record_block(&mut epoch_manager, h[i - 1], h[i], i as u64, vec![]); + } + assert_eq!(epoch_manager.get_epoch_info(&EpochId(h[2])).unwrap().protocol_version(), 0); + assert_eq!( + epoch_manager.get_epoch_info(&EpochId(h[4])).unwrap().protocol_version(), + PROTOCOL_VERSION + ); +} + +#[test] +fn test_protocol_version_switch_with_shard_layout_change() { + let store = create_test_store(); + let config = epoch_config_with_production_config(2, 1, 2, 0, 90, 60, 0, true); + let amount_powered = 1_000_000; + let validators = vec![ + do_power("test1".parse().unwrap(), amount_powered), + do_power("test2".parse().unwrap(), amount_powered), + ]; + let new_protocol_version = SimpleNightshade.protocol_version(); + let mut epoch_manager = EpochManager::new( + store, + config, + new_protocol_version - 1, + default_reward_calculator(), + validators, + ) + .unwrap(); + let h = hash_range(8); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + for i in 1..8 { + let mut block_info = block_info( + h[i], + i as u64, + i as u64 - 1, + h[i - 1], + h[i - 1], + h[0], + vec![], + DEFAULT_TOTAL_SUPPLY, + ); + if i == 1 { + set_block_info_protocol_version(&mut block_info, new_protocol_version - 1); + } else { + set_block_info_protocol_version(&mut block_info, new_protocol_version); + } + epoch_manager.record_block_info(block_info, [0; 32]).unwrap(); + } + let epochs = vec![EpochId::default(), EpochId(h[2]), EpochId(h[4])]; + assert_eq!( + epoch_manager.get_epoch_info(&epochs[1]).unwrap().protocol_version(), + new_protocol_version - 1 + ); + assert_eq!(epoch_manager.get_shard_layout(&epochs[1]).unwrap(), ShardLayout::v0_single_shard(),); + assert_eq!( + epoch_manager.get_epoch_info(&epochs[2]).unwrap().protocol_version(), + new_protocol_version + ); + assert_eq!( + epoch_manager.get_shard_layout(&epochs[2]).unwrap(), + ShardLayout::get_simple_nightshade_layout() + ); + + // Check split shards + // h[5] is the first block of epoch epochs[1] and shard layout will change at epochs[2] + assert_eq!(epoch_manager.will_shard_layout_change(&h[3]).unwrap(), false); + for i in 4..=5 { + assert_eq!(epoch_manager.will_shard_layout_change(&h[i]).unwrap(), true); + } + assert_eq!(epoch_manager.will_shard_layout_change(&h[6]).unwrap(), false); +} + +#[test] +fn test_protocol_version_switch_with_many_seats() { + let store = create_test_store(); + let num_block_producer_seats_per_shard = vec![10]; + let epoch_config = EpochConfig { + epoch_length: 10, + num_block_producer_seats: 4, + num_block_producer_seats_per_shard, + avg_hidden_validator_seats_per_shard: Vec::from([0]), + block_producer_kickout_threshold: 90, + chunk_producer_kickout_threshold: 60, + fishermen_threshold: 0, + online_min_threshold: Ratio::new(90, 100), + online_max_threshold: Ratio::new(99, 100), + protocol_upgrade_stake_threshold: Ratio::new(80, 100), + minimum_stake_divisor: 1, + shard_layout: ShardLayout::v0_single_shard(), + validator_selection_config: Default::default(), + validator_max_kickout_stake_perc: 100, + }; + let config = AllEpochConfig::new(false, epoch_config, "test-chain"); + let amount_powered = 1_000_000; + let validators = vec![ + do_power("test1".parse().unwrap(), amount_powered), + do_power("test2".parse().unwrap(), amount_powered / 5), + ]; + let mut epoch_manager = + EpochManager::new(store, config, 0, default_reward_calculator(), validators).unwrap(); + let h = hash_range(50); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + let mut block_info1 = block_info(h[1], 1, 1, h[0], h[0], h[0], vec![], DEFAULT_TOTAL_SUPPLY); + set_block_info_protocol_version(&mut block_info1, 0); + epoch_manager.record_block_info(block_info1, [0; 32]).unwrap(); + for i in 2..32 { + record_block(&mut epoch_manager, h[i - 1], h[i], i as u64, vec![]); + } + assert_eq!( + epoch_manager.get_epoch_info(&EpochId(h[10])).unwrap().protocol_version(), + PROTOCOL_VERSION + ); + assert_eq!( + epoch_manager.get_epoch_info(&EpochId(h[20])).unwrap().protocol_version(), + PROTOCOL_VERSION + ); +} + +#[test] +fn test_protocol_version_switch_after_switch() { + let store = create_test_store(); + let epoch_length: usize = 10; + let config = epoch_config(epoch_length as u64, 1, 2, 0, 90, 60, 0); + let amount_powered = 1_000_000; + let validators = vec![ + do_power("test1".parse().unwrap(), amount_powered), + do_power("test2".parse().unwrap(), amount_powered), + ]; + let mut epoch_manager = EpochManager::new( + store, + config, + UPGRADABILITY_FIX_PROTOCOL_VERSION, + default_reward_calculator(), + validators, + ) + .unwrap(); + let h = hash_range(5 * epoch_length); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + for i in 1..(2 * epoch_length + 1) { + let mut block_info = block_info( + h[i], + i as u64, + i as u64 - 1, + h[i - 1], + h[i - 1], + h[0], + vec![], + DEFAULT_TOTAL_SUPPLY, + ); + if i != 2 * epoch_length { + set_block_info_protocol_version( + &mut block_info, + UPGRADABILITY_FIX_PROTOCOL_VERSION + 1, + ); + } else { + set_block_info_protocol_version(&mut block_info, UPGRADABILITY_FIX_PROTOCOL_VERSION); + } + epoch_manager.record_block_info(block_info, [0; 32]).unwrap(); + } + + let get_epoch_infos = |em: &mut EpochManager| -> Vec> { + h.iter().filter_map(|x| em.get_epoch_info(&EpochId(*x)).ok()).collect() + }; + + let epoch_infos = get_epoch_infos(&mut epoch_manager); + + assert_eq!(epoch_infos[1].protocol_version(), UPGRADABILITY_FIX_PROTOCOL_VERSION + 1); + + assert_eq!(epoch_infos[2].protocol_version(), UPGRADABILITY_FIX_PROTOCOL_VERSION + 1); + + // if there are enough votes to use the old version, it should be allowed + for i in (2 * epoch_length + 1)..(4 * epoch_length - 1) { + let mut block_info = block_info( + h[i], + i as u64, + i as u64 - 1, + h[i - 1], + h[i - 1], + h[0], + vec![], + DEFAULT_TOTAL_SUPPLY, + ); + set_block_info_protocol_version(&mut block_info, UPGRADABILITY_FIX_PROTOCOL_VERSION); + epoch_manager.record_block_info(block_info, [0; 32]).unwrap(); + } + + let epoch_infos = get_epoch_infos(&mut epoch_manager); + + assert_eq!(epoch_infos[3].protocol_version(), UPGRADABILITY_FIX_PROTOCOL_VERSION); +} + +/// Epoch aggregator should not need to be recomputed under the following scenario +/// /-----------h+2 +/// h-2 ---- h-1 ------ h +/// \------h+1 +/// even though from the perspective of h+2 the last final block is h-2. +#[test] +fn test_final_block_consistency() { + let amount_powered = 1_000_000; + let validators = + vec![("test1".parse().unwrap(), amount_powered), ("test2".parse().unwrap(), amount_powered)]; + let mut epoch_manager = setup_default_epoch_manager(validators, 10, 1, 3, 0, 90, 60); + + let h = hash_range(10); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + for i in 1..5 { + record_block_with_final_block_hash( + &mut epoch_manager, + h[i - 1], + h[i], + if i == 1 { CryptoHash::default() } else { h[i - 2] }, + i as u64, + vec![], + ); + } + + let epoch_aggregator_final_hash = epoch_manager.epoch_info_aggregator.last_block_hash; + + epoch_manager + .record_block_info( + block_info(h[5], 5, 1, h[1], h[2], h[1], vec![], DEFAULT_TOTAL_SUPPLY), + [0; 32], + ) + .unwrap() + .commit() + .unwrap(); + let new_epoch_aggregator_final_hash = epoch_manager.epoch_info_aggregator.last_block_hash; + assert_eq!(epoch_aggregator_final_hash, new_epoch_aggregator_final_hash); +} + +#[test] +fn test_epoch_validators_cache() { + let amount_powered = 1_000_000; + let validators = + vec![("test1".parse().unwrap(), amount_powered), ("test2".parse().unwrap(), amount_powered)]; + let mut epoch_manager = setup_default_epoch_manager(validators, 2, 1, 10, 0, 90, 60); + let h = hash_range(10); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + for i in 1..4 { + record_block(&mut epoch_manager, h[i - 1], h[i], i as u64, vec![]); + } + assert_eq!(epoch_manager.epoch_validators_ordered.len(), 0); + + let epoch_id = EpochId(h[2]); + let epoch_validators = + epoch_manager.get_all_block_producers_settlement(&epoch_id, &h[3]).unwrap().to_vec(); + assert_eq!(epoch_manager.epoch_validators_ordered.len(), 1); + let epoch_validators_in_cache = epoch_manager.epoch_validators_ordered.get(&epoch_id).unwrap(); + assert_eq!(*epoch_validators, *epoch_validators_in_cache); + + assert_eq!(epoch_manager.epoch_validators_ordered_unique.len(), 0); + let epoch_validators_unique = + epoch_manager.get_all_block_producers_ordered(&epoch_id, &h[3]).unwrap().to_vec(); + let epoch_validators_unique_in_cache = + epoch_manager.epoch_validators_ordered_unique.get(&epoch_id).unwrap(); + assert_eq!(*epoch_validators_unique, *epoch_validators_unique_in_cache); +} + +#[test] +fn test_chunk_producers() { + let amount_staked = 1_000_000; + // Make sure that last validator has at least 160/1'000'000 / num_shards of stake. + // We're running with 2 shards and test1 + test2 has 2'000'000 tokens - so chunk_only should have over 160. + let validators = vec![ + ("test1".parse().unwrap(), amount_staked), + ("test2".parse().unwrap(), amount_staked), + ("chunk_only".parse().unwrap(), 200), + ("not_enough_producer".parse().unwrap(), 100), + ]; + + // There are 2 shards, and 2 block producers seats. + // So test1 and test2 should become block producers, and chunk_only should become chunk only producer. + let mut epoch_manager = setup_default_epoch_manager(validators, 2, 2, 2, 0, 90, 60); + let h = hash_range(10); + record_block(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![]); + for i in 1..=4 { + record_block(&mut epoch_manager, h[i - 1], h[i], i as u64, vec![]); + } + + let epoch_id = EpochId(h[2]); + + let block_producers = epoch_manager + .get_all_block_producers_settlement(&epoch_id, &h[4]) + .unwrap() + .iter() + .map(|(stake, _)| stake.account_id().to_string()) + .collect::>(); + assert_eq!(vec!(String::from("test1"), String::from("test2")), block_producers); + + let mut chunk_producers = epoch_manager + .get_all_chunk_producers(&epoch_id) + .unwrap() + .to_vec() + .iter() + .map(|stake| stake.account_id().to_string()) + .collect::>(); + chunk_producers.sort(); + + assert_eq!( + vec!(String::from("chunk_only"), String::from("test1"), String::from("test2")), + chunk_producers + ); +} + +/// A sanity test for the compute_kickout_info function, tests that +/// the validators that don't meet the block/chunk producer kickout threshold is kicked out +#[test] +fn test_validator_kickout_sanity() { + let epoch_config = epoch_config(5, 2, 4, 0, 90, 80, 0).for_protocol_version(PROTOCOL_VERSION); + let accounts = vec![ + ("test0".parse().unwrap(), 1000), + ("test1".parse().unwrap(), 1000), + ("test2".parse().unwrap(), 1000), + ("test3".parse().unwrap(), 1000), + ("test4".parse().unwrap(), 500), + ]; + let epoch_info = epoch_info( + 0, + accounts, + vec![0, 1, 2, 3], + vec![vec![0, 1, 2], vec![0, 1, 3, 4]], + vec![], + vec![], + BTreeMap::new(), + vec![], + HashMap::new(), + 0, + ); + let (kickouts, validator_stats) = EpochManager::compute_kickout_info( + &epoch_config, + &epoch_info, + &HashMap::from([ + (0, ValidatorStats { produced: 100, expected: 100 }), + (1, ValidatorStats { produced: 90, expected: 100 }), + (2, ValidatorStats { produced: 100, expected: 100 }), + // test3 will be kicked out + (3, ValidatorStats { produced: 89, expected: 100 }), + ]), + &HashMap::from([ + ( + 0, + HashMap::from([ + (0, ValidatorStats { produced: 100, expected: 100 }), + (1, ValidatorStats { produced: 80, expected: 100 }), + (2, ValidatorStats { produced: 70, expected: 100 }), + ]), + ), + ( + 1, + HashMap::from([ + (0, ValidatorStats { produced: 70, expected: 100 }), + (1, ValidatorStats { produced: 79, expected: 100 }), + (3, ValidatorStats { produced: 100, expected: 100 }), + (4, ValidatorStats { produced: 100, expected: 100 }), + ]), + ), + ]), + &HashMap::new(), + &HashMap::new(), + ); + assert_eq!( + kickouts, + HashMap::from([ + ("test1".parse().unwrap(), NotEnoughChunks { produced: 159, expected: 200 }), + ("test2".parse().unwrap(), NotEnoughChunks { produced: 70, expected: 100 }), + ("test3".parse().unwrap(), NotEnoughBlocks { produced: 89, expected: 100 }), + ]) + ); + assert_eq!( + validator_stats, + HashMap::from([ + ( + "test0".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 100, expected: 100 }, + chunk_stats: ValidatorStats { produced: 170, expected: 200 } + } + ), + ( + "test4".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 0, expected: 0 }, + chunk_stats: ValidatorStats { produced: 100, expected: 100 } + } + ), + ]) + ); +} + +#[test] +/// Test that the stake of validators kicked out in an epoch doesn't exceed the max_kickout_stake_ratio +fn test_max_kickout_stake_ratio() { + let mut epoch_config = + epoch_config(5, 2, 4, 0, 90, 80, 0).for_protocol_version(PROTOCOL_VERSION); + let accounts = vec![ + ("test0".parse().unwrap(), 1000), + ("test1".parse().unwrap(), 1000), + ("test2".parse().unwrap(), 1000), + ("test3".parse().unwrap(), 1000), + ("test4".parse().unwrap(), 1000), + ]; + let epoch_info = epoch_info( + 0, + accounts, + vec![0, 1, 2, 3], + vec![vec![0, 1], vec![2, 4]], + vec![], + vec![], + BTreeMap::new(), + vec![], + HashMap::new(), + 0, + ); + let block_stats = HashMap::from([ + (0, ValidatorStats { produced: 50, expected: 100 }), + // here both test1 and test2 produced the most number of blocks, we made that intentionally + // to test the algorithm to pick one deterministically to save in this case. + (1, ValidatorStats { produced: 70, expected: 100 }), + (2, ValidatorStats { produced: 70, expected: 100 }), + // validator 3 doesn't need to produce any block or chunk + (3, ValidatorStats { produced: 0, expected: 0 }), + ]); + let chunk_stats = HashMap::from([ + ( + 0, + HashMap::from([ + (0, ValidatorStats { produced: 0, expected: 100 }), + (1, ValidatorStats { produced: 0, expected: 100 }), + ]), + ), + ( + 1, + HashMap::from([ + (2, ValidatorStats { produced: 100, expected: 100 }), + (4, ValidatorStats { produced: 50, expected: 100 }), + ]), + ), + ]); + let prev_validator_kickout = + HashMap::from([("test3".parse().unwrap(), ValidatorKickoutReason::Unpowered)]); + let (kickouts, validator_stats) = EpochManager::compute_kickout_info( + &epoch_config, + &epoch_info, + &block_stats, + &chunk_stats, + &HashMap::new(), + &prev_validator_kickout, + ); + assert_eq!( + kickouts, + // We would have kicked out test0, test1, test2 and test4, but test3 was kicked out + // last epoch. To avoid kicking out all validators in two epochs, we saved test1 because + // it produced the most blocks (test1 and test2 produced the same number of blocks, but test1 + // is listed before test2 in the validators list). + HashMap::from([ + ("test0".parse().unwrap(), NotEnoughBlocks { produced: 50, expected: 100 }), + ("test2".parse().unwrap(), NotEnoughBlocks { produced: 70, expected: 100 }), + ("test4".parse().unwrap(), NotEnoughChunks { produced: 50, expected: 100 }), + ]) + ); + assert_eq!( + validator_stats, + HashMap::from([ + ( + "test3".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 0, expected: 0 }, + chunk_stats: ValidatorStats { produced: 0, expected: 0 } + } + ), + ( + "test1".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 70, expected: 100 }, + chunk_stats: ValidatorStats { produced: 0, expected: 100 } + } + ), + ]) + ); + // At most 50% of total stake can be kicked out + epoch_config.validator_max_kickout_stake_perc = 40; + let (kickouts, validator_stats) = EpochManager::compute_kickout_info( + &epoch_config, + &epoch_info, + &block_stats, + &chunk_stats, + &HashMap::new(), + &prev_validator_kickout, + ); + assert_eq!( + kickouts, + // We would have kicked out test0, test1, test2 and test4, but + // test1, test2, and test4 are exempted. Note that test3 can't be exempted because it + // is in prev_validator_kickout. + HashMap::from([( + "test0".parse().unwrap(), + NotEnoughBlocks { produced: 50, expected: 100 } + ),]) + ); + assert_eq!( + validator_stats, + HashMap::from([ + ( + "test1".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 70, expected: 100 }, + chunk_stats: ValidatorStats { produced: 0, expected: 100 } + } + ), + ( + "test2".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 70, expected: 100 }, + chunk_stats: ValidatorStats { produced: 100, expected: 100 } + } + ), + ( + "test3".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 0, expected: 0 }, + chunk_stats: ValidatorStats { produced: 0, expected: 0 } + } + ), + ( + "test4".parse().unwrap(), + BlockChunkValidatorStats { + block_stats: ValidatorStats { produced: 0, expected: 0 }, + chunk_stats: ValidatorStats { produced: 50, expected: 100 } + } + ), + ]) + ); +} diff --git a/chain/epoch-manager/src/tests/random_epochs.rs b/chain/epoch-manager/src/tests/random_epochs.rs new file mode 100644 index 000000000..326e31eb1 --- /dev/null +++ b/chain/epoch-manager/src/tests/random_epochs.rs @@ -0,0 +1,379 @@ +use rand::prelude::StdRng; +use rand::{Rng, SeedableRng}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::sync::Arc; + +use crate::test_utils::{ + hash_range, record_block_with_slashes, setup_default_epoch_manager, do_power, +}; +use crate::EpochManager; +use unc_primitives::challenge::SlashedValidator; +use unc_primitives::epoch_manager::block_info::BlockInfo; +use unc_primitives::epoch_manager::epoch_info::EpochInfo; +use unc_primitives::epoch_manager::SlashState; +use unc_primitives::hash::CryptoHash; +use unc_primitives::types::validator_power::ValidatorPower; +use unc_primitives::types::{AccountId, Balance, EpochId, ValidatorKickoutReason}; + +const DEBUG_PRINT: bool = false; + +#[test] +fn test_random_epochs() { + run_with_seed_range(2, 10, 1000); +} + +fn run_with_seed_range(epoch_length: u64, num_heights: u64, max_seed: u64) { + for test_seed in 0..max_seed { + if let Err(e) = + std::panic::catch_unwind(move || run_with_seed(epoch_length, num_heights, test_seed)) + { + println!("Found! run_with_seed({}, {}, {})", epoch_length, num_heights, test_seed); + std::panic::resume_unwind(e); + } + } +} + +fn run_with_seed(epoch_length: u64, num_heights: u64, test: u64) { + let mut seed = [0u8; 32]; + for i in 0..8 { + seed[i] = ((test >> ((8 * i) as u64)) & 0xFF) as u8 + } + let mut rng = StdRng::from_seed(seed); + let do_slashes = rng.gen_bool(0.5); + do_random_test(&mut rng, epoch_length, num_heights, do_slashes); +} + +fn do_random_test( + rng: &mut RngImpl, + epoch_length: u64, + num_heights: u64, + do_slashes: bool, +) { + let stake_amount = 1_000; + + let validators = vec![ + ("test1".parse().unwrap(), stake_amount), + ("test2".parse().unwrap(), stake_amount), + ("test3".parse().unwrap(), stake_amount), + ]; + let mut epoch_manager = setup_default_epoch_manager(validators, epoch_length, 1, 3, 0, 90, 60); + let h = hash_range(num_heights as usize); + let skip_height_probability = rng.gen_range(0.0..1.0) * rng.gen_range(0.0..1.0); + + let heights_to_pick = (0u64..1u64) + .chain((1u64..num_heights).filter(|_i| rng.gen_range(0.0..1.0) >= skip_height_probability)) + .collect::>(); + + let mut slashes_per_block = Vec::new(); + record_block_with_slashes(&mut epoch_manager, CryptoHash::default(), h[0], 0, vec![], vec![]); + slashes_per_block.push(vec![]); + let mut prev_hash = h[0]; + for &height in &heights_to_pick[1..] { + let proposals = random_proposals(rng); + let slashes = if do_slashes { random_slashes(rng) } else { vec![] }; + slashes_per_block.push(slashes.clone()); + record_block_with_slashes( + &mut epoch_manager, + prev_hash, + h[height as usize], + height, + proposals, + slashes, + ); + prev_hash = h[height as usize]; + } + + validate(&mut epoch_manager, heights_to_pick, slashes_per_block); +} + +fn random_proposals(rng: &mut RngImpl) -> Vec { + let mut proposals = Vec::new(); + let proposal_chance = 0.2; + if rng.gen_range(0.0..1.0) < proposal_chance { + let account_id = AccountId::try_from(format!("test{}", rng.gen_range(1..6))).unwrap(); + let stake_amount = rng.gen_range(100..2000); + proposals.push(do_power(account_id, stake_amount)); + } + proposals +} + +fn random_slashes(rng: &mut RngImpl) -> Vec { + let mut slashes = Vec::new(); + let slash_chance = 0.2; + if rng.gen_range(0.0..1.0) < slash_chance { + let account_id = AccountId::try_from(format!("test{}", rng.gen_range(1..6))).unwrap(); + slashes.push(SlashedValidator::new(account_id, true)); + } + slashes +} + +fn validate( + epoch_manager: &EpochManager, + heights: Vec, + slashes_per_block: Vec>, +) { + let num_blocks = heights.len(); + let height_to_hash = hash_range((heights[num_blocks - 1] + 1) as usize); + let block_hashes = + heights.iter().map(|&height| height_to_hash[height as usize]).collect::>(); + let epoch_start_heights = block_hashes + .iter() + .map(|&hash| epoch_manager.get_epoch_start_height(&hash).unwrap()) + .collect::>() + .into_iter() + .collect::>(); + let epoch_infos = block_hashes + .iter() + .filter_map(|hash| { + if epoch_manager.is_next_block_epoch_start(hash).unwrap() { + Some(epoch_manager.get_epoch_info(&EpochId(*hash)).unwrap()) + } else { + None + } + }) + .collect::>(); + assert_eq!( + &epoch_infos[0], + &epoch_manager.get_epoch_info(&EpochId(CryptoHash::default())).unwrap(), + "first two epoch are the same, even epoch_height" + ); + let block_infos = block_hashes + .iter() + .map(|&hash| epoch_manager.get_block_info(&hash).unwrap()) + .collect::>(); + if DEBUG_PRINT { + println!("Block heights: {:?}", heights); + println!("Epoch start heights: {:?}", epoch_start_heights); + println!("== Begin epoch infos =="); + for info in &epoch_infos { + println!("{:?}", info); + } + println!("== End epoch infos =="); + println!("== Begin block infos =="); + for block_info in &block_infos { + println!( + "height: {:?}, proposals: {:?}", + block_info.height(), + block_info.proposals_iter().collect::>() + ); + } + println!("== End block infos =="); + } + + verify_block_stats(epoch_manager, heights, &block_infos, &block_hashes); + verify_slashes(epoch_manager, &block_infos, &slashes_per_block); + verify_proposals(epoch_manager, &block_infos); + verify_epochs(&epoch_infos); +} + +fn verify_epochs(epoch_infos: &[Arc]) { + for i in 1..epoch_infos.len() { + let epoch_info = &epoch_infos[i]; + let prev_epoch_info = &epoch_infos[i - 1]; + assert_eq!( + epoch_info.epoch_height(), + prev_epoch_info.epoch_height() + 1, + "epoch height increases by 1" + ); + let stakes_before_change = get_stakes_map(prev_epoch_info); + assert!(!stakes_before_change.is_empty(), "validator set cannot be empty"); + for (_, stake) in &stakes_before_change { + assert_ne!(*stake, 0, "validators cannot have 0 stake"); + } + let stakes_after_change = get_stakes_map(epoch_info); + let mut stakes_with_change = stakes_before_change.clone(); + for (account_id, new_stake) in epoch_info.stake_change() { + if *new_stake == 0 { + if stakes_before_change.get(account_id).is_none() { + // Stake change from 0 to 0 + assert!(prev_epoch_info.validator_kickout().contains_key(account_id)); + assert!(epoch_info.validator_kickout().contains_key(account_id)); + } + stakes_with_change.remove(account_id); + } else { + stakes_with_change.insert(account_id.clone(), *new_stake); + } + } + assert_eq!( + stakes_with_change, + stakes_after_change, + "stake change: {:?}", + epoch_info.stake_change() + ); + + for account_id in stakes_after_change.keys() { + assert!( + epoch_info.validator_kickout().get(account_id).is_none(), + "cannot be in both validator and kickout set" + ); + } + + if is_possible_bad_epochs_case(&epoch_infos[i - 1], &epoch_infos[i]) { + continue; + } + for (account_id, reason) in epoch_info.validator_kickout() { + let was_validaror_2_ago = (i >= 2 + && epoch_infos[i - 2].account_is_validator(account_id)) + || (i == 1 && epoch_infos[0].account_is_validator(account_id)); + let in_slashes_set = reason == &ValidatorKickoutReason::Slashed; + assert!( + was_validaror_2_ago || in_slashes_set, + "Kickout set can only have validators from 2 epochs ago" + ); + } + } +} + +fn verify_proposals(epoch_manager: &EpochManager, block_infos: &[Arc]) { + let mut proposals = BTreeMap::default(); + for i in 1..block_infos.len() { + let prev_block_info = &block_infos[i - 1]; + let block_info = &block_infos[i]; + assert!(block_info.last_finalized_height() >= prev_block_info.last_finalized_height()); + if epoch_manager.is_next_block_epoch_start(block_infos[i].prev_hash()).unwrap() { + assert_ne!(prev_block_info.epoch_first_block(), block_info.epoch_first_block()); + if prev_block_info.height() == 0 { + // special case: epochs 0 and 1 + assert_eq!( + prev_block_info.epoch_id(), + block_info.epoch_id(), + "first two epochs have same id" + ); + } else { + assert_ne!(prev_block_info.epoch_id(), block_info.epoch_id(), "epoch id changes"); + } + let aggregator = + epoch_manager.get_epoch_info_aggregator_upto_last(block_info.prev_hash()).unwrap(); + assert_eq!(aggregator.all_proposals, proposals, "Proposals do not match"); + proposals = BTreeMap::from_iter( + block_info.proposals_iter().map(|p| (p.account_id().clone(), p)), + ); + } else { + proposals.extend(block_info.proposals_iter().map(|p| (p.account_id().clone(), p))); + } + } +} + +fn verify_slashes( + epoch_manager: &EpochManager, + block_infos: &[Arc], + slashes_per_block: &[Vec], +) { + for i in 1..block_infos.len() { + let prev_slashes_set = block_infos[i - 1].slashed(); + let slashes_set = block_infos[i].slashed(); + + let this_block_slashes = slashes_per_block[i] + .iter() + .map(|sv| { + ( + sv.account_id.clone(), + if sv.is_double_sign { SlashState::DoubleSign } else { SlashState::Other }, + ) + }) + .collect::>(); + if epoch_manager.is_next_block_epoch_start(block_infos[i].prev_hash()).unwrap() { + let epoch_info = epoch_manager.get_epoch_info(block_infos[i].epoch_id()).unwrap(); + + // Epoch boundary. + // DoubleSign or Other => become AlreadySlashed + // AlreadySlashed and in stake_change => keep AlreadySlashed + // ALreadySlashed and not in stake_change => remove + for (account, slash_state) in prev_slashes_set { + if let Some(slash) = this_block_slashes.get(account) { + assert_eq!(slashes_set.get(account), Some(slash)); + continue; + } + if slash_state == &SlashState::AlreadySlashed { + if epoch_info.stake_change().contains_key(account) { + assert_eq!(slashes_set.get(account), Some(&SlashState::AlreadySlashed)); + } else { + assert_eq!(slashes_set.get(account), None); + } + } else { + assert_eq!(slashes_set.get(account), Some(&SlashState::AlreadySlashed)); + } + } + } else { + // Not epoch boundary: slash state gets overwritten unless it was Other + for (account, prev_slash_state) in prev_slashes_set { + if let Some(state) = this_block_slashes.get(account) { + if prev_slash_state == &SlashState::Other { + assert_eq!(slashes_set.get(account), Some(&SlashState::Other)) + } else { + assert_eq!(slashes_set.get(account), Some(state)) + } + } else { + assert_eq!(slashes_set.get(account), Some(prev_slash_state)); + } + } + } + } +} + +fn verify_block_stats( + epoch_manager: &EpochManager, + heights: Vec, + block_infos: &[Arc], + block_hashes: &[CryptoHash], +) { + for i in 1..block_infos.len() { + let prev_epoch_end = + *epoch_manager.get_block_info(block_infos[i].epoch_first_block()).unwrap().prev_hash(); + let prev_epoch_end_height = epoch_manager.get_block_info(&prev_epoch_end).unwrap().height(); + let blocks_in_epoch = (i - heights.binary_search(&prev_epoch_end_height).unwrap()) as u64; + let blocks_in_epoch_expected = heights[i] - prev_epoch_end_height; + { + let aggregator = + epoch_manager.get_epoch_info_aggregator_upto_last(&block_hashes[i]).unwrap(); + let epoch_info = epoch_manager.get_epoch_info(block_infos[i].epoch_id()).unwrap(); + for key in aggregator.block_tracker.keys().copied() { + assert!(key < epoch_info.validators_iter().len() as u64); + } + for shard_stats in aggregator.shard_tracker.values() { + for key in shard_stats.keys().copied() { + assert!(key < epoch_info.validators_iter().len() as u64); + } + } + let sum_produced = + aggregator.block_tracker.values().map(|value| value.produced).sum::(); + let sum_expected = + aggregator.block_tracker.values().map(|value| value.expected).sum::(); + assert_eq!(sum_produced, blocks_in_epoch); + assert_eq!(sum_expected, blocks_in_epoch_expected); + for shard_id in 0..(aggregator.shard_tracker.len() as u64) { + let sum_produced = aggregator + .shard_tracker + .get(&shard_id) + .unwrap() + .values() + .map(|value| value.produced) + .sum::(); + let sum_expected = aggregator + .shard_tracker + .get(&shard_id) + .unwrap() + .values() + .map(|value| value.expected) + .sum::(); + assert_eq!(sum_produced, blocks_in_epoch); + assert_eq!(sum_expected, blocks_in_epoch_expected); + } + } + } +} + +// Bad epoch case: All validators are kicked out. +fn is_possible_bad_epochs_case(prev: &EpochInfo, curr: &EpochInfo) -> bool { + let mut copy = prev.clone(); + *copy.epoch_height_mut() += 1; + © == curr +} + +fn get_stakes_map(epoch_info: &EpochInfo) -> HashMap { + epoch_info + .validators_iter() + .chain(epoch_info.fishermen_iter()) + .map(|power| power.account_and_power()) + .collect::>() +} diff --git a/chain/epoch-manager/src/types.rs b/chain/epoch-manager/src/types.rs new file mode 100644 index 000000000..f26409b4d --- /dev/null +++ b/chain/epoch-manager/src/types.rs @@ -0,0 +1,291 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_primitives::block_header::BlockHeader; +use unc_primitives::challenge::SlashedValidator; +use unc_primitives::epoch_manager::block_info::BlockInfo; +use unc_primitives::epoch_manager::epoch_info::EpochInfo; +use unc_primitives::hash::CryptoHash; +use unc_primitives::types::validator_power::ValidatorPower; +use unc_primitives::types::{ + AccountId, Balance, BlockHeight, EpochId, ShardId, ValidatorId, ValidatorStats, +}; +use unc_primitives::version::ProtocolVersion; +use std::collections::{BTreeMap, HashMap}; +use tracing::{debug, debug_span}; +use unc_primitives::types::validator_frozen::ValidatorFrozen; + +use crate::EpochManager; + +pub type RngSeed = [u8; 32]; + +/// Compressed information about block. +/// Useful for epoch manager. +#[derive(Default, Clone, Debug)] +pub struct BlockHeaderInfo { + pub hash: CryptoHash, + pub prev_hash: CryptoHash, + pub height: BlockHeight, + pub random_value: CryptoHash, + pub last_finalized_height: BlockHeight, + pub last_finalized_block_hash: CryptoHash, + pub power_proposals: Vec, + pub frozen_proposals: Vec, + pub slashed_validators: Vec, + pub chunk_mask: Vec, + pub total_supply: Balance, + pub latest_protocol_version: ProtocolVersion, + pub timestamp_nanosec: u64, +} + +impl BlockHeaderInfo { + pub fn new(header: &BlockHeader, last_finalized_height: u64) -> Self { + Self { + hash: *header.hash(), + prev_hash: *header.prev_hash(), + height: header.height(), + random_value: *header.random_value(), + last_finalized_height, + last_finalized_block_hash: *header.last_final_block(), + power_proposals: header.prev_validator_power_proposals().collect(), + frozen_proposals: header.prev_validator_frozen_proposals().collect(), + slashed_validators: vec![], + chunk_mask: header.chunk_mask().to_vec(), + total_supply: header.total_supply(), + latest_protocol_version: header.latest_protocol_version(), + timestamp_nanosec: header.raw_timestamp(), + } + } +} + +/// Aggregator of information needed for validator computation at the end of the epoch. +#[derive(Clone, BorshSerialize, BorshDeserialize, Debug, Default)] +pub struct EpochInfoAggregator { + /// Map from validator index to (num_blocks_produced, num_blocks_expected) so far in the given epoch. + pub block_tracker: HashMap, + /// For each shard, a map of validator id to (num_chunks_produced, num_chunks_expected) so far in the given epoch. + pub shard_tracker: HashMap>, + /// Latest protocol version that each validator supports. + pub version_tracker: HashMap, + /// All power proposals in this epoch up to this block. + pub all_power_proposals: BTreeMap, + /// All frozen proposals in this epoch up to this block. + pub all_frozen_proposals: BTreeMap, + /// Id of the epoch that this aggregator is in. + pub epoch_id: EpochId, + /// Last block hash recorded. + pub last_block_hash: CryptoHash, +} + +impl EpochInfoAggregator { + pub fn new(epoch_id: EpochId, last_block_hash: CryptoHash) -> Self { + Self { + block_tracker: Default::default(), + shard_tracker: Default::default(), + version_tracker: Default::default(), + all_power_proposals: BTreeMap::default(), + all_frozen_proposals: BTreeMap::default(), + epoch_id, + last_block_hash, + } + } + + /// Aggregates data from a block which directly precede the first block this + /// aggregator has statistic on. + /// + /// For example, this method can be used in the following situation (where + /// A through G are blocks ordered in increasing height and arrows denote + /// ‘is parent’ relationship and B is start of a new epoch): + /// + /// ```text + /// ┌─ block_info + /// A ← B ← C ← D ← E ← F ← G ← H ← I + /// │←── self ─→│ + /// ``` + /// + /// Note that there is no method which allows adding information about G, + /// H or I blocks into the aggregator. The expected usage is to create + /// a new aggregator starting from I, add H and G into it (using this + /// method) and then [merge][`Self::merge`] it into `self`. + pub fn update_tail( + &mut self, + block_info: &BlockInfo, + epoch_info: &EpochInfo, + prev_block_height: BlockHeight, + ) { + let _span = + debug_span!(target: "epoch_tracker", "update_tail", prev_block_height).entered(); + // Step 1: update block tracer + let block_info_height = block_info.height(); + for height in prev_block_height + 1..=block_info_height { + let block_producer_id = EpochManager::block_producer_from_info(epoch_info, height); + let entry = self.block_tracker.entry(block_producer_id); + if height == block_info_height { + entry + .and_modify(|validator_stats| { + validator_stats.produced += 1; + validator_stats.expected += 1; + }) + .or_insert(ValidatorStats { produced: 1, expected: 1 }); + } else { + debug!( + target: "epoch_tracker", + block_producer = ?epoch_info.validator_account_id(block_producer_id), + block_height = height, "Missed block"); + entry + .and_modify(|validator_stats| { + validator_stats.expected += 1; + }) + .or_insert(ValidatorStats { produced: 0, expected: 1 }); + } + } + + // Step 2: update shard tracker + for (i, mask) in block_info.chunk_mask().iter().enumerate() { + let chunk_validator_id = EpochManager::chunk_producer_from_info( + epoch_info, + prev_block_height + 1, + i as ShardId, + ); + let tracker = self.shard_tracker.entry(i as ShardId).or_insert_with(HashMap::new); + tracker + .entry(chunk_validator_id) + .and_modify(|stats| { + if *mask { + stats.produced += 1; + } else { + debug!( + target: "epoch_tracker", + chunk_validator = ?epoch_info.validator_account_id(chunk_validator_id), + shard_id = i, + block_height = prev_block_height + 1, + "Missed chunk"); + } + stats.expected += 1; + }) + .or_insert(ValidatorStats { produced: u64::from(*mask), expected: 1 }); + } + + // Step 3: update version tracker + let block_producer_id = + EpochManager::block_producer_from_info(epoch_info, block_info_height); + self.version_tracker + .entry(block_producer_id) + .or_insert_with(|| *block_info.latest_protocol_version()); + + // Step 4: update proposals + for proposal in block_info.power_proposals_iter() { + self.all_power_proposals.entry(proposal.account_id().clone()).or_insert(proposal); + } + + for proposal in block_info.frozen_proposals_iter() { + self.all_frozen_proposals.entry(proposal.account_id().clone()).or_insert(proposal); + } + } + + /// Merges information from `other` aggregator into `self`. + /// + /// The `other` aggregator must hold statistics from blocks which **follow** + /// the ones this aggregator has. Both aggregators have to be for the same + /// epoch (the function panics if they aren’t). + /// + /// For example, this method can be used in the following situation (where + /// A through J are blocks ordered in increasing height, arrows denote ‘is + /// parent’ relationship and B is start of a new epoch): + /// + /// ```text + /// │←─── self ────→│ │←─ other ─→│ + /// A ← B ← C ← D ← E ← F ← G ← H ← I ← J + /// └── new epoch ──→ + /// ``` + /// + /// Once the method finishes `self` will hold statistics for blocks from + /// B till J. + pub fn merge(&mut self, other: EpochInfoAggregator) { + self.merge_common(&other); + + // merge version tracker + self.version_tracker.extend(other.version_tracker); + // merge proposals + self.all_power_proposals.extend(other.all_power_proposals); + + self.all_frozen_proposals.extend(other.all_frozen_proposals); + + self.last_block_hash = other.last_block_hash; + } + + /// Merges information from `other` aggregator into `self`. + /// + /// The `other` aggregator must hold statistics from blocks which + /// **precede** the ones this aggregator has. Both aggregators have to be + /// for the same epoch (the function panics if they aren’t). + /// + /// For example, this method can be used in the following situation (where + /// A through J are blocks ordered in increasing height, arrows denote ‘is + /// parent’ relationship and B is start of a new epoch): + /// + /// ```text + /// │←─── other ───→│ │←─ self ──→│ + /// A ← B ← C ← D ← E ← F ← G ← H ← I ← J + /// └── new epoch ──→ + /// ``` + /// + /// Once the method finishes `self` will hold statistics for blocks from + /// B till J. + /// + /// The method is a bit like doing `other.merge(self)` except that `other` + /// is not changed. + pub fn merge_prefix(&mut self, other: &EpochInfoAggregator) { + self.merge_common(&other); + + // merge version tracker + self.version_tracker.reserve(other.version_tracker.len()); + // TODO(mina86): Use try_insert once map_try_insert is stabilised. + for (k, v) in other.version_tracker.iter() { + self.version_tracker.entry(*k).or_insert_with(|| *v); + } + + // merge proposals + // TODO(mina86): Use try_insert once map_try_insert is stabilised. + for (k, v) in other.all_power_proposals.iter() { + self.all_power_proposals.entry(k.clone()).or_insert_with(|| v.clone()); + } + + for (k, v) in other.all_frozen_proposals.iter() { + self.all_frozen_proposals.entry(k.clone()).or_insert_with(|| v.clone()); + } + } + + /// Merges block and shard trackers from `other` into `self`. + /// + /// See [`Self::merge`] and [`Self::merge_prefix`] method for description of + /// merging. + fn merge_common(&mut self, other: &EpochInfoAggregator) { + assert_eq!(self.epoch_id, other.epoch_id); + + // merge block tracker + for (block_producer_id, stats) in other.block_tracker.iter() { + self.block_tracker + .entry(*block_producer_id) + .and_modify(|e| { + e.expected += stats.expected; + e.produced += stats.produced + }) + .or_insert_with(|| stats.clone()); + } + // merge shard tracker + for (shard_id, stats) in other.shard_tracker.iter() { + self.shard_tracker + .entry(*shard_id) + .and_modify(|e| { + for (chunk_producer_id, stat) in stats.iter() { + e.entry(*chunk_producer_id) + .and_modify(|entry| { + entry.expected += stat.expected; + entry.produced += stat.produced; + }) + .or_insert_with(|| stat.clone()); + } + }) + .or_insert_with(|| stats.clone()); + } + } +} diff --git a/chain/epoch-manager/src/validator_selection.rs b/chain/epoch-manager/src/validator_selection.rs new file mode 100644 index 000000000..816b499c5 --- /dev/null +++ b/chain/epoch-manager/src/validator_selection.rs @@ -0,0 +1,1522 @@ +use crate::shard_assignment::assign_shards; +use unc_primitives::checked_feature; +use unc_primitives::epoch_manager::epoch_info::EpochInfo; +use unc_primitives::epoch_manager::{EpochConfig, RngSeed}; +use unc_primitives::errors::{BlockError, EpochError}; +use unc_primitives::types::validator_power::ValidatorPower; +use unc_primitives::types::{AccountId, Balance, NumShards, Power, ProtocolVersion, ValidatorFrozenV1, ValidatorId, ValidatorKickoutReason, ValidatorPowerAndFrozenV1, ValidatorPowerV1}; +use unc_primitives::validator_mandates::{ValidatorMandates, ValidatorMandatesConfig}; +use num_rational::Ratio; +use std::cmp::{self, Ordering}; +use std::collections::hash_map; +use std::collections::{BTreeMap, BinaryHeap, HashMap, HashSet}; +use unc_primitives::epoch_manager::block_info::BlockInfo; +use unc_primitives::epoch_manager::block_summary::BlockSummary; +use unc_primitives::hash::CryptoHash; +use unc_primitives::types::validator_frozen::ValidatorFrozen; +use unc_primitives::types::validator_power_and_frozen::ValidatorPowerAndFrozen; +/// Select providers for next block and generate block info +pub fn proposals_to_block_summary( + epoch_config: &EpochConfig, + this_block_hash: &CryptoHash, + last_block_hash: &CryptoHash, + _rng_seed: RngSeed, + prev_block_summary: &BlockInfo, + power_proposals: Vec, + frozen_proposals: Vec, + mut validator_kickout: HashMap, + validator_reward: HashMap, + minted_amount: Balance, + next_version: ProtocolVersion, +) -> Result { + // debug_assert!( + // power_proposals.iter().map(|power| power.account_id()).collect::>().len() + // == power_proposals.len(), + // "Power proposals should not have duplicates" + // ); + + debug_assert!( + frozen_proposals.iter().map(|frozen| frozen.account_id()).collect::>().len() + == frozen_proposals.len(), + "Frozen proposals should not have duplicates" + ); + let shard_ids: Vec<_> = epoch_config.shard_layout.shard_ids().collect(); + let min_stake_ratio = { + let rational = epoch_config.validator_selection_config.minimum_stake_ratio; + Ratio::new(*rational.numer() as u128, *rational.denom() as u128) + }; + let max_bp_selected = epoch_config.num_block_producer_seats as usize; + let mut power_change = BTreeMap::new(); + let mut frozen_change = BTreeMap::new(); + let mut fishermen = vec![]; + let ( + power_proposals, + frozen_proposals + ) = proposals_with_rollover_block( + power_proposals, + frozen_proposals, + prev_block_summary, + &validator_reward, + &validator_kickout, + &mut power_change, + &mut frozen_change, + &mut fishermen, + ); + let mut bp_power_proposals = order_power_proposals(power_proposals.values().cloned()); + let mut bp_frozen_proposals = order_frozen_proposals(frozen_proposals.values().cloned()); + let (block_producers, bp_stake_threshold) = select_block_producers( + &mut bp_power_proposals, + &mut bp_frozen_proposals, + max_bp_selected, + min_stake_ratio, + next_version, + ); + let ( + cp_power_proposals, + cp_frozen_proposals, + chunk_producers, + cp_stake_threshold + ) = if checked_feature!("stable", ChunkOnlyProducers, next_version) { + let mut cp_power_proposals = order_power_proposals(power_proposals.clone().into_values()); + let mut cp_frozen_proposals = order_frozen_proposals(frozen_proposals.clone().into_values()); + let max_cp_selected = max_bp_selected + + (epoch_config.validator_selection_config.num_chunk_only_producer_seats as usize); + let (chunk_producers, cp_stake_treshold) = select_chunk_producers( + &mut cp_power_proposals, + &mut cp_frozen_proposals, + max_cp_selected, + min_stake_ratio, + shard_ids.len() as NumShards, + next_version, + ); + (cp_power_proposals, cp_frozen_proposals, chunk_producers, cp_stake_treshold) + } else { + (bp_power_proposals, bp_frozen_proposals, block_producers.clone(), bp_stake_threshold) + }; + + // since block producer proposals could become chunk producers, their actual stake threshold + // is the smaller of the two thresholds + let threshold = cmp::min(bp_stake_threshold, cp_stake_threshold); + let cp_power_map = create_power_map(&cp_power_proposals); + // process remaining chunk_producer_proposals that were not selected for either role + for OrderedValidatorFrozen(p) in cp_frozen_proposals { + let frozen = p.frozen(); + let account_id = p.account_id(); + let power = cp_power_map.get(&account_id.clone()).unwrap_or(&0); + let r_p = ValidatorPowerAndFrozen::V1(ValidatorPowerAndFrozenV1{ + account_id : account_id.clone(), + public_key : p.public_key().clone(), + power : power.clone(), + frozen : frozen.clone(), + }); + if frozen >= epoch_config.fishermen_threshold { + fishermen.push(r_p); + } else { + *frozen_change.get_mut(account_id).unwrap() = 0; + if prev_block_summary.account_is_validator(account_id) + || prev_block_summary.account_is_fisherman(account_id) + { + debug_assert!(frozen < threshold); + let account_id = p.take_account_id(); + let frozen_value = frozen; + validator_kickout.insert( + account_id, + ValidatorKickoutReason::NotEnoughFrozen { frozen : frozen_value, threshold }, + ); + } + } + } + + let num_chunk_producers = chunk_producers.len(); + // Constructing `all_validators` such that a validators position corresponds to its `ValidatorId`. + let mut all_validators: Vec = Vec::with_capacity(num_chunk_producers); + let mut validator_to_index = HashMap::new(); + let mut block_producers_settlement = Vec::with_capacity(block_producers.len()); + + for (i, bp) in block_producers.into_iter().enumerate() { + let id = i as ValidatorId; + validator_to_index.insert(bp.account_id().clone(), id); + block_producers_settlement.push(id); + all_validators.push(bp); + } + + let chunk_producers_settlement = if checked_feature!("stable", ChunkOnlyProducers, next_version) + { + let minimum_validators_per_shard = + epoch_config.validator_selection_config.minimum_validators_per_shard as usize; + let shard_assignment = assign_shards( + chunk_producers, + shard_ids.len() as NumShards, + minimum_validators_per_shard, + ) + .map_err(|_| BlockError::NotEnoughValidators { + num_validators: num_chunk_producers as u64, + num_shards: shard_ids.len() as NumShards, + })?; + + let mut chunk_producers_settlement: Vec> = + shard_assignment.iter().map(|vs| Vec::with_capacity(vs.len())).collect(); + let mut i = all_validators.len(); + // Here we assign validator ids to all chunk only validators + for (shard_validators, shard_validator_ids) in + shard_assignment.into_iter().zip(chunk_producers_settlement.iter_mut()) + { + for validator in shard_validators { + debug_assert_eq!(i, all_validators.len()); + match validator_to_index.entry(validator.account_id().clone()) { + hash_map::Entry::Vacant(entry) => { + let validator_id = i as ValidatorId; + entry.insert(validator_id); + shard_validator_ids.push(validator_id); + all_validators.push(validator); + i += 1; + } + // Validators which have an entry in the validator_to_index map + // have already been inserted into `all_validators`. + hash_map::Entry::Occupied(entry) => { + let validator_id = *entry.get(); + shard_validator_ids.push(validator_id); + } + } + } + } + chunk_producers_settlement + } else { + if chunk_producers.is_empty() { + // All validators tried to unstake? + return Err(BlockError::NotEnoughValidators { + num_validators: 0u64, + num_shards: shard_ids.len() as NumShards, + }); + } + let mut id = 0usize; + // Here we assign validators to chunks (we try to keep number of shards assigned for + // each validator as even as possible). Note that in prod configuration number of seats + // per shard is the same as maximal number of block producers, so normally all + // validators would be assigned to all chunks + shard_ids + .iter() + .map(|&shard_id| shard_id as usize) + .map(|shard_id| { + (0..epoch_config.num_block_producer_seats_per_shard[shard_id] + .min(block_producers_settlement.len() as u64)) + .map(|_| { + let res = block_producers_settlement[id]; + id = (id + 1) % block_producers_settlement.len(); + res + }) + .collect() + }) + .collect() + }; + + let validator_mandates = if checked_feature!("stable", ChunkValidation, next_version) { + // TODO(#10014) determine required stake per mandate instead of reusing seat price. + // TODO(#10014) determine `min_mandates_per_shard` + let min_mandates_per_shard = 0; + let validator_mandates_config = + ValidatorMandatesConfig::new(threshold, min_mandates_per_shard, shard_ids.len()); + // We can use `all_validators` to construct mandates Since a validator's position in + // `all_validators` corresponds to its `ValidatorId` + ValidatorMandates::new(validator_mandates_config, &all_validators) + } else { + ValidatorMandates::default() + }; + + let fishermen_to_index = fishermen + .iter() + .enumerate() + .map(|(index, s)| (s.account_id().clone(), index as ValidatorId)) + .collect::>(); + let all_power_proposals= power_proposals.values().cloned().collect(); + let all_frozen_proposals = frozen_proposals.values().cloned().collect(); + Ok(BlockSummary::new( + *this_block_hash, + *last_block_hash, + CryptoHash::default(), + all_validators, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + fishermen, + fishermen_to_index, + power_change, + frozen_change, + validator_reward, + threshold, + minted_amount, + all_power_proposals, + all_frozen_proposals, + validator_kickout, + validator_mandates, + )) +} +/// Select validators for next epoch and generate epoch info +pub fn proposals_to_epoch_info( + epoch_config: &EpochConfig, + rng_seed: RngSeed, + prev_epoch_info: &EpochInfo, + power_proposals: Vec, + frozen_proposals: Vec, + mut validator_kickout: HashMap, + validator_reward: HashMap, + minted_amount: Balance, + next_version: ProtocolVersion, + last_version: ProtocolVersion, +) -> Result { + // debug_assert!( + // power_proposals.iter().map(|power| power.account_id()).collect::>().len() + // == power_proposals.len(), + // "Power proposals should not have duplicates" + // ); + + debug_assert!( + frozen_proposals.iter().map(|frozen| frozen.account_id()).collect::>().len() + == frozen_proposals.len(), + "Frozen proposals should not have duplicates" + ); + + let shard_ids: Vec<_> = epoch_config.shard_layout.shard_ids().collect(); + let min_stake_ratio = { + let rational = epoch_config.validator_selection_config.minimum_stake_ratio; + Ratio::new(*rational.numer() as u128, *rational.denom() as u128) + }; + let max_bp_selected = epoch_config.num_block_producer_seats as usize; + let mut power_change = BTreeMap::new(); + let mut frozen_change = BTreeMap::new(); + let mut fishermen = vec![]; + let (power_proposals, frozen_proposals) = proposals_with_rollover( + power_proposals, + frozen_proposals, + prev_epoch_info, + &validator_reward, + &validator_kickout, + &mut power_change, + &mut frozen_change, + &mut fishermen, + ); + let mut bp_power_proposals = order_power_proposals(power_proposals.values().cloned()); + let mut bp_frozen_proposals = order_frozen_proposals(frozen_proposals.values().cloned()); + let (block_producers, bp_stake_threshold) = select_block_producers( + &mut bp_power_proposals, + &mut bp_frozen_proposals, + max_bp_selected, + min_stake_ratio, + last_version, + ); + let ( + cp_power_proposals, + cp_frozen_proposals, + chunk_producers, + cp_stake_threshold + ) = if checked_feature!("stable", ChunkOnlyProducers, next_version) { + let mut cp_power_proposals = order_power_proposals(power_proposals.into_values()); + let mut cp_frozen_proposals = order_frozen_proposals(frozen_proposals.into_values()); + let max_cp_selected = max_bp_selected + + (epoch_config.validator_selection_config.num_chunk_only_producer_seats as usize); + let (chunk_producers, cp_stake_treshold) = select_chunk_producers( + &mut cp_power_proposals, + &mut cp_frozen_proposals, + max_cp_selected, + min_stake_ratio, + shard_ids.len() as NumShards, + last_version, + ); + (cp_power_proposals, cp_frozen_proposals, chunk_producers, cp_stake_treshold) + } else { + (bp_power_proposals, bp_frozen_proposals, block_producers.clone(), bp_stake_threshold) + }; + + // since block producer proposals could become chunk producers, their actual stake threshold + // is the smaller of the two thresholds + let threshold = cmp::min(bp_stake_threshold, cp_stake_threshold); + let cp_power_map = create_power_map(&cp_power_proposals); + // process remaining chunk_producer_proposals that were not selected for either role + for OrderedValidatorFrozen(p) in cp_frozen_proposals { + let frozen = p.frozen(); + let account_id = p.account_id(); + let power = cp_power_map.get(&account_id.clone()).unwrap_or(&0); + let r_p = ValidatorPowerAndFrozen::V1(ValidatorPowerAndFrozenV1{ + account_id : account_id.clone(), + public_key : p.public_key().clone(), + power : power.clone(), + frozen : frozen.clone(), + }); + if frozen >= epoch_config.fishermen_threshold { + fishermen.push(r_p); + } else { + *frozen_change.get_mut(account_id).unwrap() = 0; + if prev_epoch_info.account_is_validator(account_id) + || prev_epoch_info.account_is_fisherman(account_id) + { + debug_assert!(frozen < threshold); + let account_id = p.take_account_id(); + let frozen_value = frozen; + validator_kickout.insert( + account_id, + ValidatorKickoutReason::NotEnoughFrozen { frozen : frozen_value, threshold }, + ); + } + } + } + + let num_chunk_producers = chunk_producers.len(); + // Constructing `all_validators` such that a validators position corresponds to its `ValidatorId`. + let mut all_validators: Vec = Vec::with_capacity(num_chunk_producers); + let mut validator_to_index = HashMap::new(); + let mut block_producers_settlement = Vec::with_capacity(block_producers.len()); + + for (i, bp) in block_producers.into_iter().enumerate() { + let id = i as ValidatorId; + validator_to_index.insert(bp.account_id().clone(), id); + block_producers_settlement.push(id); + all_validators.push(bp); + } + + let chunk_producers_settlement = if checked_feature!("stable", ChunkOnlyProducers, next_version) + { + let minimum_validators_per_shard = + epoch_config.validator_selection_config.minimum_validators_per_shard as usize; + let shard_assignment = assign_shards( + chunk_producers, + shard_ids.len() as NumShards, + minimum_validators_per_shard, + ) + .map_err(|_| EpochError::NotEnoughValidators { + num_validators: num_chunk_producers as u64, + num_shards: shard_ids.len() as NumShards, + })?; + + let mut chunk_producers_settlement: Vec> = + shard_assignment.iter().map(|vs| Vec::with_capacity(vs.len())).collect(); + let mut i = all_validators.len(); + // Here we assign validator ids to all chunk only validators + for (shard_validators, shard_validator_ids) in + shard_assignment.into_iter().zip(chunk_producers_settlement.iter_mut()) + { + for validator in shard_validators { + debug_assert_eq!(i, all_validators.len()); + match validator_to_index.entry(validator.account_id().clone()) { + hash_map::Entry::Vacant(entry) => { + let validator_id = i as ValidatorId; + entry.insert(validator_id); + shard_validator_ids.push(validator_id); + all_validators.push(validator); + i += 1; + } + // Validators which have an entry in the validator_to_index map + // have already been inserted into `all_validators`. + hash_map::Entry::Occupied(entry) => { + let validator_id = *entry.get(); + shard_validator_ids.push(validator_id); + } + } + } + } + chunk_producers_settlement + } else { + if chunk_producers.is_empty() { + // All validators tried to unstake? + return Err(EpochError::NotEnoughValidators { + num_validators: 0u64, + num_shards: shard_ids.len() as NumShards, + }); + } + let mut id = 0usize; + // Here we assign validators to chunks (we try to keep number of shards assigned for + // each validator as even as possible). Note that in prod configuration number of seats + // per shard is the same as maximal number of block producers, so normally all + // validators would be assigned to all chunks + shard_ids + .iter() + .map(|&shard_id| shard_id as usize) + .map(|shard_id| { + (0..epoch_config.num_block_producer_seats_per_shard[shard_id] + .min(block_producers_settlement.len() as u64)) + .map(|_| { + let res = block_producers_settlement[id]; + id = (id + 1) % block_producers_settlement.len(); + res + }) + .collect() + }) + .collect() + }; + + let validator_mandates = if checked_feature!("stable", ChunkValidation, next_version) { + // TODO(#10014) determine required stake per mandate instead of reusing seat price. + // TODO(#10014) determine `min_mandates_per_shard` + let min_mandates_per_shard = 0; + let validator_mandates_config = + ValidatorMandatesConfig::new(threshold, min_mandates_per_shard, shard_ids.len()); + // We can use `all_validators` to construct mandates Since a validator's position in + // `all_validators` corresponds to its `ValidatorId` + ValidatorMandates::new(validator_mandates_config, &all_validators) + } else { + ValidatorMandates::default() + }; + + let fishermen_to_index = fishermen + .iter() + .enumerate() + .map(|(index, s)| (s.account_id().clone(), index as ValidatorId)) + .collect::>(); + + Ok(EpochInfo::new( + prev_epoch_info.epoch_height() + 1, + all_validators, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + vec![], + fishermen, + fishermen_to_index, + power_change, + frozen_change, + validator_reward, + validator_kickout, + minted_amount, + threshold, + next_version, + rng_seed, + validator_mandates, + )) +} +/// Copy from proposals_with_rollover +/// +/// +fn proposals_with_rollover_block( + power_proposals: Vec, + frozen_proposals: Vec, + prev_block_summary: &BlockInfo, + validator_reward: &HashMap, + validator_kickout: &HashMap, + power_change: &mut BTreeMap, + frozen_change: &mut BTreeMap, + fishermen: &mut Vec, +) -> ( + HashMap, + HashMap, +){ + let mut power_proposals_by_account = HashMap::new(); + for p in power_proposals { + let account_id = p.account_id(); + power_change.insert(account_id.clone(), p.power()); + power_proposals_by_account.insert(account_id.clone(), p); + } + + let mut frozen_proposals_by_account = HashMap::new(); + for f in frozen_proposals { + let account_id = f.account_id(); + if validator_kickout.contains_key(account_id) { + let account_id = f.take_account_id(); + frozen_change.insert(account_id, 0); + } else { + frozen_change.insert(account_id.clone(), f.frozen()); + frozen_proposals_by_account.insert(account_id.clone(), f); + } + } + + for r in prev_block_summary.validators_iter() { + let account_id = r.account_id().clone(); + if validator_kickout.contains_key(&account_id) { + frozen_change.insert(account_id, 0); + continue; + } + let r_f = ValidatorFrozen::V1(ValidatorFrozenV1{ + account_id : account_id.clone(), + public_key : r.public_key().clone(), + frozen : r.frozen().clone(), + }); + let r_p = ValidatorPower::V1(ValidatorPowerV1{ + account_id : account_id.clone(), + public_key : r.public_key().clone(), + power : r.power().clone(), + }); + // The reward will given to the validator in the next epoch, + // so we need to add it to the stake but not power. + let f = frozen_proposals_by_account.entry(account_id.clone()).or_insert(r_f); + if let Some(reward) = validator_reward.get(f.account_id()) { + *f.frozen_mut() += *reward; + } + frozen_change.insert(f.account_id().clone(), f.frozen()); + + let p = power_proposals_by_account.entry(account_id).or_insert(r_p); + power_change.insert(p.account_id().clone(), p.power()); + + } + + for r in prev_block_summary.fishermen_iter() { + let account_id = r.account_id(); + if validator_kickout.contains_key(account_id) { + frozen_change.insert(account_id.clone(), 0); + continue; + } + if !frozen_proposals_by_account.contains_key(account_id) { + // safe to do this here because fishermen from previous epoch is guaranteed to have no + // duplicates. + power_change.insert(account_id.clone(), r.power()); + frozen_change.insert(account_id.clone(), r.frozen()); + fishermen.push(r); + } + } + + (power_proposals_by_account, frozen_proposals_by_account) +} +/// Generates proposals based on new proposals, last epoch validators/fishermen and validator +/// kickouts +/// For each account that was validator or fisherman in last epoch or made stake action last epoch +/// we apply the following in the order of priority +/// 1. If account is in validator_kickout it cannot be validator or fisherman for the next epoch, +/// we will not include it in proposals or fishermen +/// 2. If account made staking action last epoch, it will be included in proposals with stake +/// adjusted by rewards from last epoch, if any +/// 3. If account was validator last epoch, it will be included in proposals with the same stake +/// as last epoch, adjusted by rewards from last epoch, if any +/// 4. If account was fisherman last epoch, it is included in fishermen +fn proposals_with_rollover( + power_proposals: Vec, + frozen_proposals: Vec, + prev_epoch_info: &EpochInfo, + validator_reward: &HashMap, + validator_kickout: &HashMap, + power_change: &mut BTreeMap, + frozen_change: &mut BTreeMap, + fishermen: &mut Vec, +) -> ( + HashMap, + HashMap, +){ + let mut power_proposals_by_account = HashMap::new(); + for p in power_proposals { + let account_id = p.account_id(); + power_change.insert(account_id.clone(), p.power()); + power_proposals_by_account.insert(account_id.clone(), p); + } + + let mut frozen_proposals_by_account = HashMap::new(); + for f in frozen_proposals { + let account_id = f.account_id(); + if validator_kickout.contains_key(account_id) { + let account_id = f.take_account_id(); + frozen_change.insert(account_id, 0); + } else { + frozen_change.insert(account_id.clone(), f.frozen()); + frozen_proposals_by_account.insert(account_id.clone(), f); + } + } + + for r in prev_epoch_info.validators_iter() { + let account_id = r.account_id().clone(); + if validator_kickout.contains_key(&account_id) { + frozen_change.insert(account_id, 0); + continue; + } + let r_f = ValidatorFrozen::V1(ValidatorFrozenV1{ + account_id : account_id.clone(), + public_key : r.public_key().clone(), + frozen : r.frozen().clone(), + }); + let r_p = ValidatorPower::V1(ValidatorPowerV1{ + account_id : account_id.clone(), + public_key : r.public_key().clone(), + power : r.power().clone(), + }); + // The reward will given to the validator in the next epoch, + // so we need to add it to the stake but not power. + let f = frozen_proposals_by_account.entry(account_id.clone()).or_insert(r_f); + if let Some(reward) = validator_reward.get(f.account_id()) { + *f.frozen_mut() += *reward; + } + frozen_change.insert(f.account_id().clone(), f.frozen()); + + let p = power_proposals_by_account.entry(account_id).or_insert(r_p); + power_change.insert(p.account_id().clone(), p.power()); + + } + + for r in prev_epoch_info.fishermen_iter() { + let account_id = r.account_id(); + if validator_kickout.contains_key(account_id) { + frozen_change.insert(account_id.clone(), 0); + continue; + } + if !frozen_proposals_by_account.contains_key(account_id) { + // safe to do this here because fishermen from previous epoch is guaranteed to have no + // duplicates. + power_change.insert(account_id.clone(), r.power()); + frozen_change.insert(account_id.clone(), r.frozen()); + fishermen.push(r); + } + } + + (power_proposals_by_account, frozen_proposals_by_account) +} + +fn order_power_proposals>( + proposals: I, +) -> BinaryHeap { + proposals.into_iter().map(OrderedValidatorPower).collect() +} + +fn order_frozen_proposals>( + proposals: I, +) -> BinaryHeap { + proposals.into_iter().map(OrderedValidatorFrozen).collect() +} +fn select_block_producers( + bp_power_proposals: &mut BinaryHeap, + bp_frozen_proposals: &mut BinaryHeap, + max_num_selected: usize, + min_stake_ratio: Ratio, + protocol_version: ProtocolVersion, +) -> (Vec, Balance) { + select_validators(bp_power_proposals, bp_frozen_proposals, max_num_selected, min_stake_ratio, protocol_version) +} + +fn select_chunk_producers( + all_power_proposals: &mut BinaryHeap, + all_frozen_proposals: &mut BinaryHeap, + max_num_selected: usize, + min_stake_ratio: Ratio, + num_shards: u64, + protocol_version: ProtocolVersion, +) -> (Vec, Balance) { + select_validators( + all_power_proposals, + all_frozen_proposals, + max_num_selected, + min_stake_ratio * Ratio::new(1, num_shards as u128), + protocol_version, + ) +} + +#[allow(irrefutable_let_patterns)] +fn create_power_map(heap: &BinaryHeap) -> HashMap { + let mut power_map = HashMap::new(); + + // Explicitly define cloned_heap as an owned BinaryHeap + let cloned_heap: BinaryHeap = heap.clone(); + + // Now we can call into_sorted_vec() on this owned BinaryHeap + let vec = cloned_heap.into_sorted_vec(); + + // Iterate through the vector and populate the hash map + for ordered_validator in vec { + if let ValidatorPower::V1(ref v1) = ordered_validator.0 { + power_map.insert(v1.account_id.clone(), v1.power); + } + } + + power_map +} + + +// Use the function with the appropriate account_id and store + + + +// Takes the top N proposals (by stake), or fewer if there are not enough or the +// next proposals is too small relative to the others. In the case where all N +// slots are filled, or the stake ratio falls too low, the threshold stake to be included +// is also returned. +fn select_validators( + power_proposals: &mut BinaryHeap, + frozen_proposals: &mut BinaryHeap, + max_number_selected: usize, + min_stake_ratio: Ratio, + protocol_version: ProtocolVersion, +) -> (Vec, Balance) { + let mut total_frozen = 0; + let n = cmp::min(max_number_selected, frozen_proposals.len()); + let mut validators = Vec::with_capacity(n); + let power_map = create_power_map(power_proposals); + for _ in 0..n { + let p = frozen_proposals.pop().unwrap().0; + let p_frozen = p.frozen(); + let power = power_map.get(&p.account_id().clone()).unwrap_or(&0); + let p_r = ValidatorPowerAndFrozen::V1(ValidatorPowerAndFrozenV1{ + account_id : p.account_id().clone(), + public_key : p.public_key().clone(), + power : power.clone(), + frozen : p_frozen.clone(), + }); + let total_frozen_with_p = total_frozen + p_frozen; + if Ratio::new(p_frozen, total_frozen_with_p) > min_stake_ratio { + validators.push(p_r); + total_frozen = total_frozen_with_p; + } else { + // p was not included, return it to the list of proposals + frozen_proposals.push(OrderedValidatorFrozen(p)); + break; + } + } + if validators.len() == max_number_selected { + // all slots were filled, so the threshold stake is 1 more than the current + // smallest stake + let threshold = validators.last().unwrap().frozen() + 1; + (validators, threshold) + } else { + // the stake ratio condition prevented all slots from being filled, + // or there were fewer proposals than available slots, + // so the threshold stake is whatever amount pass the stake ratio condition + let threshold = if checked_feature!( + "protocol_feature_fix_staking_threshold", + FixStakingThreshold, + protocol_version + ) { + (min_stake_ratio * Ratio::from_integer(total_frozen) + / (Ratio::from_integer(1u128) - min_stake_ratio)) + .ceil() + .to_integer() + } else { + (min_stake_ratio * Ratio::new(total_frozen, 1)).ceil().to_integer() + }; + (validators, threshold) + } +} + +/// We store stakes in max heap and want to order them such that the validator +/// with the largest state and (in case of a tie) lexicographically smallest +/// AccountId comes at the top. +#[derive(Eq, PartialEq, Clone)] +struct OrderedValidatorFrozen(ValidatorFrozen); +impl OrderedValidatorFrozen { + fn key(&self) -> impl Ord + '_ { + (self.0.frozen(), std::cmp::Reverse(self.0.account_id())) + } +} +impl PartialOrd for OrderedValidatorFrozen { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl Ord for OrderedValidatorFrozen { + fn cmp(&self, other: &Self) -> Ordering { + self.key().cmp(&other.key()) + } +} +#[derive(Eq, PartialEq, Clone)] +struct OrderedValidatorPower(ValidatorPower); +impl OrderedValidatorPower { + fn key(&self) -> impl Ord + '_ { + (self.0.power(), std::cmp::Reverse(self.0.account_id())) + } +} +impl PartialOrd for OrderedValidatorPower { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl Ord for OrderedValidatorPower { + fn cmp(&self, other: &Self) -> Ordering { + self.key().cmp(&other.key()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use unc_crypto::{KeyType, PublicKey}; + use unc_primitives::account::id::AccountIdRef; + use unc_primitives::epoch_manager::epoch_info::{EpochInfo, EpochInfoV3}; + use unc_primitives::epoch_manager::ValidatorSelectionConfig; + use unc_primitives::shard_layout::ShardLayout; + use unc_primitives::types::validator_power::ValidatorPower; + #[cfg(feature = "nightly")] + use unc_primitives::validator_mandates::{AssignmentWeight, ValidatorMandatesAssignment}; + use unc_primitives::version::PROTOCOL_VERSION; + use num_rational::Ratio; + use crate::test_utils::frozen; + + #[test] + fn test_validator_assignment_all_block_producers() { + // A simple sanity test. Given fewer proposals than the number of seats, + // none of which has too little stake, they all get assigned as block and + // chunk producers. + let epoch_config = create_epoch_config(2, 100, 0, Default::default()); + let prev_epoch_height = 7; + let prev_epoch_info = create_prev_epoch_info(prev_epoch_height, &["test1", "test2"], &[]); + let power_proposals = create_power_proposals(&[("test1", 1000), ("test2", 2000), ("test3", 300)]); + let frozen_proposals = create_frozen_proposals(&[("test1", 1000), ("test2", 2000), ("test3", 300)]); + let epoch_info = proposals_to_epoch_info( + &epoch_config, + [0; 32], + &prev_epoch_info, + power_proposals.clone(), + frozen_proposals.clone(), + Default::default(), + Default::default(), + 0, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + ) + .unwrap(); + + // increment height + assert_eq!(epoch_info.epoch_height(), prev_epoch_height + 1); + + // assign block producers in decreasing order of stake + let mut sorted_proposals = power_proposals; + sorted_proposals.sort_unstable_by(|a, b| b.power().cmp(&a.power())); + assert_eq!(sorted_proposals, epoch_info.validators_iter().collect::>()); + + // All proposals become block producers + assert_eq!(epoch_info.block_producers_settlement(), &[0, 1, 2]); + assert_eq!(epoch_info.fishermen_iter().len(), 0); + + // Validators are split between chunks to make roughly equal stakes + // (in this case shard 0 has 2000, while shard 1 has 1300). + assert_eq!(epoch_info.chunk_producers_settlement(), &[vec![0], vec![1, 2]]); + } + + #[test] + fn test_validator_assignment_with_chunk_only_producers() { + // A more complex test. Here there are more BP proposals than spots, so some will + // become chunk-only producers, along side the other chunk-only proposals. + let num_bp_seats = 10; + let num_cp_seats = 30; + let epoch_config = create_epoch_config( + 2, + num_bp_seats, + // purposely set the fishermen threshold high so that none become fishermen + 10_000, + ValidatorSelectionConfig { + num_chunk_only_producer_seats: num_cp_seats, + minimum_validators_per_shard: 1, + minimum_stake_ratio: Ratio::new(160, 1_000_000), + }, + ); + let prev_epoch_height = 3; + let test1_stake = 1000; + let prev_epoch_info = create_prev_epoch_info( + prev_epoch_height, + &[ + // test1 is not included in proposals below, and will get kicked out because + // their stake is too low. + ("test1", test1_stake, Proposal::BlockProducer), + // test2 submitted a new proposal, so their stake will come from there, but it + // too will be kicked out + ("test2", 1234, Proposal::ChunkOnlyProducer), + ], + &[], + ); + let frozen_proposals = create_frozen_proposals((2..(2 * num_bp_seats + num_cp_seats)).map(|i| { + ( + format!("test{}", i), + 2000u128 + (i as u128), + if i <= num_cp_seats { + Proposal::ChunkOnlyProducer + } else { + Proposal::BlockProducer + }, + ) + })); + let power_proposals = create_power_proposals((2..(2 * num_bp_seats + num_cp_seats)).map(|i| { + ( + format!("test{}", i), + 2000u128 + (i as u128), + if i <= num_cp_seats { + Proposal::ChunkOnlyProducer + } else { + Proposal::BlockProducer + }, + ) + })); + let epoch_info = proposals_to_epoch_info( + &epoch_config, + [0; 32], + &prev_epoch_info, + power_proposals.clone(), + frozen_proposals.clone(), + Default::default(), + Default::default(), + 0, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + ) + .unwrap(); + + assert_eq!(epoch_info.epoch_height(), prev_epoch_height + 1); + + // the top stakes are the chosen block producers + let mut sorted_proposals = frozen_proposals; + sorted_proposals.sort_unstable_by(|a, b| b.power().cmp(&a.power())); + assert!(sorted_proposals.iter().take(num_bp_seats as usize).cloned().eq(epoch_info + .block_producers_settlement() + .into_iter() + .map(|id| epoch_info.get_validator(*id)))); + + // stakes are evenly distributed between shards + assert_eq!( + stake_sum(&epoch_info, epoch_info.chunk_producers_settlement()[0].iter()), + stake_sum(&epoch_info, epoch_info.chunk_producers_settlement()[1].iter()), + ); + + // The top proposals are all chunk producers + let mut chosen_chunk_producers: Vec = epoch_info + .chunk_producers_settlement() + .into_iter() + .flatten() + .map(|id| epoch_info.get_validator(*id)) + .collect(); + chosen_chunk_producers.sort_unstable_by(|a, b| b.power().cmp(&a.power())); + assert!(sorted_proposals + .into_iter() + .take((num_bp_seats + num_cp_seats) as usize) + .eq(chosen_chunk_producers)); + + // the old, low-stake proposals were not accepted + let kickout = epoch_info.validator_kickout(); + assert_eq!(kickout.len(), 2); + assert_eq!( + kickout.get(AccountIdRef::new_or_panic("test1")).unwrap(), + &ValidatorKickoutReason::NotEnoughFrozen { frozen: test1_stake, threshold: 2011 }, + ); + assert_eq!( + kickout.get(AccountIdRef::new_or_panic("test2")).unwrap(), + &ValidatorKickoutReason::NotEnoughFrozen { frozen: 2002, threshold: 2011 }, + ); + } + + #[test] + fn test_block_producer_sampling() { + let num_shards = 4; + let epoch_config = create_epoch_config( + num_shards, + 2, + 0, + ValidatorSelectionConfig { + num_chunk_only_producer_seats: 0, + minimum_validators_per_shard: 1, + minimum_stake_ratio: Ratio::new(160, 1_000_000), + }, + ); + let prev_epoch_height = 7; + let prev_epoch_info = create_prev_epoch_info(prev_epoch_height, &["test1", "test2"], &[]); + let power_proposals = create_power_proposals(&[("test1", 1000), ("test2", 2000)]); + let frozen_proposals = create_frozen_proposals(&[("test1", 1000), ("test2", 2000)]); + + let epoch_info = proposals_to_epoch_info( + &epoch_config, + [0; 32], + &prev_epoch_info, + power_proposals, + frozen_proposals, + Default::default(), + Default::default(), + 0, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + ) + .unwrap(); + + // test2 is chosen as the bp 2x more often than test1 + let mut counts: [i32; 2] = [0, 0]; + for h in 0..100_000 { + let bp = epoch_info.sample_block_producer(h); + counts[bp as usize] += 1; + } + let diff = (2 * counts[1] - counts[0]).abs(); + assert!(diff < 100); + } + + #[test] + fn test_chunk_producer_sampling() { + // When there is 1 CP per shard, they are chosen 100% of the time. + let num_shards = 4; + let epoch_config = create_epoch_config( + num_shards, + 2 * num_shards, + 0, + ValidatorSelectionConfig { + num_chunk_only_producer_seats: 0, + minimum_validators_per_shard: 1, + minimum_stake_ratio: Ratio::new(160, 1_000_000), + }, + ); + let prev_epoch_height = 7; + let prev_epoch_info = create_prev_epoch_info(prev_epoch_height, &["test1", "test2"], &[]); + let power_proposals = + create_power_proposals(&[("test1", 1000), ("test2", 1000), ("test3", 1000), ("test4", 1000)]); + let frozen_proposals = + create_frozen_proposals(&[("test1", 1000), ("test2", 1000), ("test3", 1000), ("test4", 1000)]); + + let epoch_info = proposals_to_epoch_info( + &epoch_config, + [0; 32], + &prev_epoch_info, + power_proposals, + frozen_proposals, + Default::default(), + Default::default(), + 0, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + ) + .unwrap(); + + for shard_id in 0..num_shards { + for h in 0..100_000 { + let cp = epoch_info.sample_chunk_producer(h, shard_id); + // Don't read too much into this. The reason the ValidatorId always + // equals the ShardId is because the validators are assigned to shards in order. + assert_eq!(cp, shard_id) + } + } + + // When there are multiple CPs they are chosen in proportion to stake. + let power_proposals = create_power_proposals((1..=num_shards).flat_map(|i| { + // Each shard gets a pair of validators, one with twice as + // much stake as the other. + vec![(format!("test{}", i), 1000), (format!("test{}", 100 * i), 2000)].into_iter() + })); + let frozen_proposals = create_frozen_proposals((1..=num_shards).flat_map(|i| { + // Each shard gets a pair of validators, one with twice as + // much stake as the other. + vec![(format!("test{}", i), 1000), (format!("test{}", 100 * i), 2000)].into_iter() + })); + let epoch_info = proposals_to_epoch_info( + &epoch_config, + [0; 32], + &prev_epoch_info, + power_proposals, + frozen_proposals, + Default::default(), + Default::default(), + 0, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + ) + .unwrap(); + + for shard_id in 0..num_shards { + let mut counts: [i32; 2] = [0, 0]; + for h in 0..100_000 { + let cp = epoch_info.sample_chunk_producer(h, shard_id); + // if ValidatorId is in the second half then it is the lower + // stake validator (because they are sorted by decreasing stake). + let index = if cp >= num_shards { 1 } else { 0 }; + counts[index] += 1; + } + let diff = (2 * counts[1] - counts[0]).abs(); + assert!(diff < 500); + } + } + + /// This test only verifies that chunk validator mandates are correctly wired up with + /// `EpochInfo`. The internals of mandate assignment are tested in the module containing + /// [`ValidatorMandates`]. + #[test] + #[cfg(feature = "nightly")] + fn test_chunk_validators_sampling() { + let num_shards = 4; + let epoch_config = create_epoch_config( + num_shards, + 2 * num_shards, + 0, + ValidatorSelectionConfig { + num_chunk_only_producer_seats: 0, + minimum_validators_per_shard: 1, + // for example purposes, we choose a higher ratio than in production + minimum_stake_ratio: Ratio::new(1, 10), + }, + ); + let prev_epoch_height = 7; + let prev_epoch_info = create_prev_epoch_info(prev_epoch_height, &["test1", "test2"], &[]); + + // Choosing proposals s.t. the `threshold` (i.e. seat price) calculated in + // `proposals_to_epoch_info` below will be 100. For now, this `threshold` is used as the + // stake required for a chunk validator mandate. + // + // Note that `proposals_to_epoch_info` will not include `test6` in the set of validators, + // hence it will not hold a (partial) mandate + let power_proposals = create_power_proposals(&[ + ("test1", 1500), + ("test2", 1000), + ("test3", 1000), + ("test4", 260), + ("test5", 140), + ("test6", 50), + ]); + let frozen_proposals = create_frozen_proposals(&[ + ("test1", 1500), + ("test2", 1000), + ("test3", 1000), + ("test4", 260), + ("test5", 140), + ("test6", 50), + ]); + + let epoch_info = proposals_to_epoch_info( + &epoch_config, + [0; 32], + &prev_epoch_info, + power_proposals, + frozen_proposals, + Default::default(), + Default::default(), + 0, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + ) + .unwrap(); + + // Given `epoch_info` and `proposals` above, the sample at a given height is deterministic. + let height = 42; + let expected_assignments: ValidatorMandatesAssignment = vec![ + HashMap::from([ + (0, AssignmentWeight::new(3, 0)), + (1, AssignmentWeight::new(3, 0)), + (2, AssignmentWeight::new(3, 0)), + (3, AssignmentWeight::new(0, 60)), + ]), + HashMap::from([ + (0, AssignmentWeight::new(6, 0)), + (1, AssignmentWeight::new(2, 0)), + (2, AssignmentWeight::new(2, 0)), + ]), + HashMap::from([ + (0, AssignmentWeight::new(4, 0)), + (1, AssignmentWeight::new(1, 0)), + (2, AssignmentWeight::new(3, 0)), + (3, AssignmentWeight::new(2, 0)), + ]), + HashMap::from([ + (0, AssignmentWeight::new(2, 0)), + (1, AssignmentWeight::new(4, 0)), + (2, AssignmentWeight::new(2, 0)), + (4, AssignmentWeight::new(1, 40)), + ]), + ]; + assert_eq!(epoch_info.sample_chunk_validators(height), expected_assignments); + } + + #[test] + fn test_validator_assignment_ratio_condition() { + // There are more seats than proposals, however the + // lower proposals are too small relative to the total + // (the reason we can't choose them is because the the probability of them actually + // being selected to make a block would be too low since it is done in + // proportion to stake). + let epoch_config = create_epoch_config( + 1, + 100, + 150, + ValidatorSelectionConfig { + num_chunk_only_producer_seats: 300, + minimum_validators_per_shard: 1, + // for example purposes, we choose a higher ratio than in production + minimum_stake_ratio: Ratio::new(1, 10), + }, + ); + let prev_epoch_height = 7; + // test5 and test6 are going to get kicked out for not enough stake. + let prev_epoch_info = create_prev_epoch_info(prev_epoch_height, &["test5", "test6"], &[]); + let power_proposals = create_power_proposals(&[ + ("test1", 1000), + ("test2", 1000), + ("test3", 1000), // the total up to this point is 3000 + ("test4", 200), // 200 is < 1/10 of 3000, so not validator, but can be fisherman + ("test5", 100), // 100 is even too small to be a fisherman, cannot get any role + ("test6", 50), + ]); + let frozen_proposals = create_frozen_proposals(&[ + ("test1", 1000), + ("test2", 1000), + ("test3", 1000), // the total up to this point is 3000 + ("test4", 200), // 200 is < 1/10 of 3000, so not validator, but can be fisherman + ("test5", 100), // 100 is even too small to be a fisherman, cannot get any role + ("test6", 50), + ]); + let epoch_info = proposals_to_epoch_info( + &epoch_config, + [0; 32], + &prev_epoch_info, + power_proposals, + frozen_proposals, + Default::default(), + Default::default(), + 0, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + ) + .unwrap(); + + // stake below validator threshold, but above fishermen threshold become fishermen + let fishermen: Vec<_> = epoch_info.fishermen_iter().map(|v| v.take_account_id()).collect(); + assert_eq!(fishermen, vec!["test4"]); + + // too low stakes are kicked out + let kickout = epoch_info.validator_kickout(); + assert_eq!(kickout.len(), 2); + #[cfg(feature = "protocol_feature_fix_staking_threshold")] + let expected_threshold = 334; + #[cfg(not(feature = "protocol_feature_fix_staking_threshold"))] + let expected_threshold = 300; + assert_eq!( + kickout.get(AccountIdRef::new_or_panic("test5")).unwrap(), + &ValidatorKickoutReason::NotEnoughPower { power: 100, threshold: expected_threshold }, + ); + assert_eq!( + kickout.get(AccountIdRef::new_or_panic("test6")).unwrap(), + &ValidatorKickoutReason::NotEnoughPower { power: 50, threshold: expected_threshold }, + ); + + let bp_threshold = epoch_info.seat_price(); + let num_validators = epoch_info.validators_iter().len(); + let power_proposals = create_power_proposals(&[ + ("test1", 1000), + ("test2", 1000), + ("test3", 1000), // the total up to this point is 3000 + ("test4", 200), // 200 is < 1/10 of 3000, so not validator, but can be fisherman + ("test5", 100), // 100 is even too small to be a fisherman, cannot get any role + ("test6", 50), + ("test7", bp_threshold), + ]); + let frozen_proposals = create_frozen_proposals(&[ + ("test1", 1000), + ("test2", 1000), + ("test3", 1000), // the total up to this point is 3000 + ("test4", 200), // 200 is < 1/10 of 3000, so not validator, but can be fisherman + ("test5", 100), // 100 is even too small to be a fisherman, cannot get any role + ("test6", 50), + ("test7", bp_threshold), + ]); + let epoch_info = proposals_to_epoch_info( + &epoch_config, + [0; 32], + &epoch_info, + power_proposals, + frozen_proposals, + Default::default(), + Default::default(), + 0, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + ) + .unwrap(); + #[cfg(feature = "protocol_feature_fix_staking_threshold")] + assert_eq!(num_validators + 1, epoch_info.validators_iter().len()); + + let power_proposals = create_power_proposals(&[ + ("test1", 1000), + ("test2", 1000), + ("test3", 1000), // the total up to this point is 3000 + ("test4", 200), // 200 is < 1/10 of 3000, so not validator, but can be fisherman + ("test5", 100), // 100 is even too small to be a fisherman, cannot get any role + ("test6", 50), + ("test7", bp_threshold - 1), + ]); + let frozen_proposals = create_frozen_proposals(&[ + ("test1", 1000), + ("test2", 1000), + ("test3", 1000), // the total up to this point is 3000 + ("test4", 200), // 200 is < 1/10 of 3000, so not validator, but can be fisherman + ("test5", 100), // 100 is even too small to be a fisherman, cannot get any role + ("test6", 50), + ("test7", bp_threshold - 1), + ]); + let epoch_info = proposals_to_epoch_info( + &epoch_config, + [0; 32], + &epoch_info, + power_proposals, + frozen_proposals, + Default::default(), + Default::default(), + 0, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + ) + .unwrap(); + assert_eq!(num_validators, epoch_info.validators_iter().len()); + } + + #[test] + fn test_validator_assignment_with_kickout() { + // kicked out validators are not selected + let epoch_config = create_epoch_config(1, 100, 0, Default::default()); + let prev_epoch_height = 7; + let prev_epoch_info = create_prev_epoch_info( + prev_epoch_height, + &[("test1", 10_000), ("test2", 2000), ("test3", 3000)], + &[], + ); + let mut kick_out = HashMap::new(); + // test1 is kicked out + kick_out.insert("test1".parse().unwrap(), ValidatorKickoutReason::Unpowered); + let epoch_info = proposals_to_epoch_info( + &epoch_config, + [0; 32], + &prev_epoch_info, + Default::default(), + Default::default(), + kick_out, + Default::default(), + 0, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + ) + .unwrap(); + + // test1 is not selected + assert_eq!(epoch_info.get_validator_id(&"test1".parse().unwrap()), None); + } + + #[test] + fn test_validator_assignment_with_rewards() { + // validator balances are updated based on their rewards + let validators = [("test1", 3000), ("test2", 2000), ("test3", 1000)]; + let rewards: [u128; 3] = [7, 8, 9]; + let epoch_config = create_epoch_config(1, 100, 0, Default::default()); + let prev_epoch_height = 7; + let prev_epoch_info = create_prev_epoch_info(prev_epoch_height, &validators, &[]); + let rewards_map = validators + .iter() + .zip(rewards.iter()) + .map(|((name, _), reward)| (name.parse().unwrap(), *reward)) + .collect(); + let epoch_info = proposals_to_epoch_info( + &epoch_config, + [0; 32], + &prev_epoch_info, + Default::default(), + Default::default(), + rewards_map, + Default::default(), + 0, + PROTOCOL_VERSION, + PROTOCOL_VERSION, + ) + .unwrap(); + + for (v, ((_, power), reward)) in + epoch_info.validators_iter().zip(validators.iter().zip(rewards.iter())) + { + assert_eq!(v.power(), power + reward); + } + } + + fn stake_sum<'a, I: IntoIterator>( + epoch_info: &EpochInfo, + validator_ids: I, + ) -> u128 { + validator_ids.into_iter().map(|id| epoch_info.get_validator(*id).power()).sum() + } + + /// Create EpochConfig, only filling in the fields important for validator selection. + fn create_epoch_config( + num_shards: u64, + num_block_producer_seats: u64, + fishermen_threshold: Balance, + validator_selection_config: ValidatorSelectionConfig, + ) -> EpochConfig { + EpochConfig { + epoch_length: 10, + num_block_producer_seats, + num_block_producer_seats_per_shard: vec![num_block_producer_seats; num_shards as usize], + avg_hidden_validator_seats_per_shard: vec![0; num_shards as usize], + block_producer_kickout_threshold: 0, + chunk_producer_kickout_threshold: 0, + validator_max_kickout_stake_perc: 100, + online_min_threshold: 0.into(), + online_max_threshold: 0.into(), + fishermen_threshold, + minimum_stake_divisor: 0, + protocol_upgrade_stake_threshold: 0.into(), + shard_layout: ShardLayout::v0(num_shards, 0), + validator_selection_config, + } + } + + fn create_prev_epoch_info( + epoch_height: u64, + prev_validators: &[T], + prev_fishermen: &[T], + ) -> EpochInfo { + let mut result: EpochInfoV3 = Default::default(); + + result.epoch_height = epoch_height; + result.validators = create_proposals(prev_validators); + result.fishermen = create_proposals(prev_fishermen); + + result.validator_to_index = to_map(&result.validators); + result.fishermen_to_index = to_map(&result.fishermen); + + EpochInfo::V3(result) + } + + fn to_map(vs: &[ValidatorPowerAndFrozen]) -> HashMap { + vs.iter().enumerate().map(|(i, v)| (v.account_id().clone(), i as u64)).collect() + } + + fn create_proposals(ps: I) -> Vec + where + T: IntoValidatorStake, + I: IntoIterator, + { + ps.into_iter().map(IntoValidatorStake::into_validator_stake).collect() + } + fn create_power_proposals(ps: I) -> Vec + where + T: IntoValidatorStake, + I: IntoIterator, + { + ps.into_iter().map(IntoValidatorStake::into_validator_stake).collect() + } + + fn create_frozen_proposals(ps: I) -> Vec + where + T: IntoValidatorStake, + I: IntoIterator, + { + ps.into_iter().map(IntoValidatorStake::into_validator_stake).collect() + } + + #[derive(Debug, PartialEq, Eq, Copy, Clone)] + enum Proposal { + BlockProducer, + ChunkOnlyProducer, + } + + trait IntoValidatorStake { + fn into_validator_stake(self) -> ValidatorFrozen; + } + + impl IntoValidatorStake for &str { + fn into_validator_stake(self) -> ValidatorFrozen { + ValidatorFrozen::new(self.parse().unwrap(), PublicKey::empty(KeyType::ED25519), 100) + } + } + + impl IntoValidatorStake for (&str, Balance) { + fn into_validator_stake(self) -> ValidatorFrozen { + ValidatorFrozen::new(self.0.parse().unwrap(), PublicKey::empty(KeyType::ED25519), self.1) + } + } + + impl IntoValidatorStake for (String, Balance) { + fn into_validator_stake(self) -> ValidatorFrozen { + ValidatorFrozen::new(self.0.parse().unwrap(), PublicKey::empty(KeyType::ED25519), self.1) + } + } + + impl IntoValidatorStake for (&str, Balance, Proposal) { + fn into_validator_stake(self) -> ValidatorFrozen { + ValidatorFrozen::new(self.0.parse().unwrap(), PublicKey::empty(KeyType::ED25519), self.1) + } + } + + impl IntoValidatorStake for (String, Balance, Proposal) { + fn into_validator_stake(self) -> ValidatorFrozen { + ValidatorFrozen::new(self.0.parse().unwrap(), PublicKey::empty(KeyType::ED25519), self.1) + } + } + + impl IntoValidatorStake for &T { + fn into_validator_stake(self) -> ValidatorFrozen { + (*self).into_validator_stake() + } + } +} diff --git a/chain/indexer-primitives/Cargo.toml b/chain/indexer-primitives/Cargo.toml new file mode 100644 index 000000000..6fc04c98e --- /dev/null +++ b/chain/indexer-primitives/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "unc-indexer-primitives" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "This crate hosts structures for the NEAR Indexer Framework types" +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +serde.workspace = true +serde_json.workspace = true + +unc-primitives.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-primitives/nightly", +] +nightly_protocol = [ + "unc-primitives/nightly_protocol", +] diff --git a/chain/indexer-primitives/README.md b/chain/indexer-primitives/README.md new file mode 100644 index 000000000..1de12c7e5 --- /dev/null +++ b/chain/indexer-primitives/README.md @@ -0,0 +1,3 @@ +# unc-indexer-primitives + +This crate holds the types that is used in NEAR Indexer Framework to allow other projects to use them without a need to depend on entire `framework`. diff --git a/chain/indexer-primitives/src/lib.rs b/chain/indexer-primitives/src/lib.rs new file mode 100644 index 000000000..6882b3706 --- /dev/null +++ b/chain/indexer-primitives/src/lib.rs @@ -0,0 +1,43 @@ +pub use unc_primitives::hash::CryptoHash; +pub use unc_primitives::{self, types, views}; + +/// Resulting struct represents block with chunks +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct StreamerMessage { + pub block: views::BlockView, + pub shards: Vec, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct IndexerChunkView { + pub author: types::AccountId, + pub header: views::ChunkHeaderView, + pub transactions: Vec, + pub receipts: Vec, +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct IndexerTransactionWithOutcome { + pub transaction: views::SignedTransactionView, + pub outcome: IndexerExecutionOutcomeWithOptionalReceipt, +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct IndexerExecutionOutcomeWithOptionalReceipt { + pub execution_outcome: views::ExecutionOutcomeWithIdView, + pub receipt: Option, +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct IndexerExecutionOutcomeWithReceipt { + pub execution_outcome: views::ExecutionOutcomeWithIdView, + pub receipt: views::ReceiptView, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct IndexerShard { + pub shard_id: types::ShardId, + pub chunk: Option, + pub receipt_execution_outcomes: Vec, + pub state_changes: views::StateChangesView, +} diff --git a/chain/indexer/CHANGELOG.md b/chain/indexer/CHANGELOG.md new file mode 100644 index 000000000..fa90995de --- /dev/null +++ b/chain/indexer/CHANGELOG.md @@ -0,0 +1,132 @@ +# Changelog + +## 1.32.x + +* Add `nightly` feature to NEAR Indexer Framework to respect this feature for `framework` lib (requried for `betanet`) + +## 1.26.0 + +* `state_changes` field is moved from the top-level `StreamerMessage` to `IndexerShard` struct to align better with the sharded nature of NEAR protocol. In the future, when framework will be able to track only a subset of shards, this API will work naturally, so we take pro-active measures to solidify the APIs +* All the NEAR Indexer Framework types were extracted to a separate crate `unc-indexer-primitives` +* Increase the streamer size from 16 to 100 in order to increase the speed of streaming messages (affects reindexing jobs) + +## Breaking changes + +The field `state_changes` is moved from the root of `StreamerMessage` +to the `IndexerShard.state_changes` and now contains only changes related +to the specific shard. + +## 0.10.1 + +* (mainnet only) Add additional handler to inject restored receipts to the block #47317863. See [PR 4248](https://github.com/utnet-org/utility/pull/4248) for reference + +## 0.10.0 + +* Add additional logs on Indexer Framework start +* Avoid double genesis validation by removing the explicit validation on Indexer instantiation +* Replaced the method how genesis is being read to optimize memory usage + +## Breaking changes + +Since the change of reading genesis method to optimize memory usage. You'd be able to iterate over genesis records with `unc_config.genesis.for_each_record(|record| {...})`. Nothing is changed for you your indexer does nothing about genesis records. + +## 0.9.2 + +* Optimize the delayed receipts tracking process introduced in previous version to avoid indexer stuck. + +## 0.9.1 + +* Introduce a hot-fix. Execution outcome for local receipt might appear not in the same block as the receipt. Local receipts are not saved in database and unable to be fetched. To include a receipt in `IndexerExecutionOutcomeWithReceipt` and prevent NEAR Indexer Framework from panic we fetch previous blocks to find corresponding local receipt to include. + +## 0.9.0 (do not use this version, it contains a bug) + +* Introduce `IndexerShard` structure which contains corresponding chunks and `IndexerExecutionOutcomeWithReceipt` +* `receipt` field in `IndexerExecutionOutcomeWithReceipt` is no longer optional as it used to be always set anyway, + so now we explicitly communicate this relation ("every outcome has a corresponding receipt") through the type system +* Introduce `IndexerExecutionOutcomeWithOptionalReceipt` which is the same as `IndexerExecutionOutcomeWithReceipt` + but with optional `receipt` field. + +## Breaking changes + +* `IndexerChunkView` doesn't contain field `receipt_execution_outcomes` anymore, this field has been moved to `IndexerShard` +* `StreamerMessage` structure was aligned more with NEAR Protocol specification and now looks like: + ``` + StreamerMessage { + block: BlockView, + shards: Vec, + state_changes: StateChangesView, + } + ``` + +## 0.8.1 + +* Add `InitConfigArgs` and `indexer_init_configs` + +As current `uncd::init_configs()` signature is a bit hard to read and use we introduce `InitConfigArgs` struct to make a process of passing arguments more explicit. That's why we introduce `indexer_init_configs` which is just a wrapper on `uncd::init_configs()` but takes `dir` and `InitConfigArgs` as an input. + +## 0.8.0 + +* Upgrade dependencies + +## Breaking change + +actix update changed the way we used to deal with starting the node and getting necessary data from uncd. +The `start()` method was deleted, `Indexer` struct doesn't have `actix_runtime` anymore and runtime should be +created and started on the Indexer implementation, not on the Indexer Framework one. + +## 0.7.0 + +* State changes return changes with cause instead of kinds + +## Breaking changes + +* `StreamerMessage` now contains `StateChangesView` which is an alias for `Vec`, previously it contained `StateChangesKindsView` + +## 0.6.0 + +* Add a way to turn off the requirement to wait for the node to be fully synced before starting streaming. + +## Breaking changes + +* `IndexerConfig` was extended with another field `await_for_node_synced`. Corresponding enum is `AwaitForNodeSyncedEnum` with variants: + - `WaitForFullSync` - await for node to be fully synced (previous default behaviour) + - `StreamWhileSyncing`- start streaming right away while node is syncing (it's useful in case of Indexing from genesis) + +## 0.5.0 + +* Attach receipt execution outcomes to a relevant chunk and preserve their execution order (!) + +## Breaking changes + +Since #3529 framework stores `ExecutionOutcome`s in their execution order, and we can also attribute outcomes to specific chunk. That's why: + +* `receipt_execution_outcomes` was moved from `StreamerMessage` to a relevant `IndexerChunkView` +* `ExecutionOutcomesWithReceipts` type alias was removed (just use `Vec` instead) + +## 0.4.0 + +* Prepend chunk's receipts with local receipts to attach latter to specific chunk + +## Breaking changes + +* For local receipt to have a relation to specific chunk we have prepended them to original receipts in particular chunk + as in the most cases local receipts are executed before normal receipts. That's why there is no reason to have `local_receipts` + field in `StreamerMessage` struct anymore. `local_receipts` field was removed. + +## 0.3.1 + +* Add local receipt to `receipt_execution_outcomes` if possible + +## 0.3.0 + +### Breaking changes + +* To extended the `receipt_execution_outcomes` with information about the corresponding receipt we had to break the API + (the old outcome structure is just one layer deeper now [under `execution_outcome` field]) + +## 0.2.0 + +* Refactor the way of fetching `ExecutionOutcome`s (use the new way to get all of them for specific block) +* Rename `StreamerMessage.outcomes` field to `receipt_execution_outcomes` and change type to `HashMap` and now it includes only `ExecutionOutcome`s for receipts (no transactions) +* Introduce `IndexerTransactionWithOutcome` struct to contain `SignedTransactionView` and `ExecutionOutcomeWithId` for the transaction +* Introduce `IndexerChunkView` to replace `StreamerMessage.chunks` to include `IndexerTransactionWithOutcome` vec in `transactions` diff --git a/chain/indexer/Cargo.toml b/chain/indexer/Cargo.toml new file mode 100644 index 000000000..76e99ce68 --- /dev/null +++ b/chain/indexer/Cargo.toml @@ -0,0 +1,64 @@ +[package] +name = "unc-indexer" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix.workspace = true +anyhow.workspace = true +async-recursion.workspace = true +futures.workspace = true +once_cell.workspace = true +rocksdb.workspace = true +serde.workspace = true +serde_json.workspace = true +tokio.workspace = true +tracing.workspace = true + +framework.workspace = true +unc-client.workspace = true +unc-chain-configs.workspace = true +unc-dyn-configs.workspace = true +unc-crypto.workspace = true +unc-indexer-primitives.workspace = true +unc-o11y.workspace = true +unc-parameters.workspace = true +unc-primitives.workspace = true +unc-store.workspace = true +node-runtime.workspace = true + +[features] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-client/nightly_protocol", + "unc-dyn-configs/nightly_protocol", + "unc-indexer-primitives/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-parameters/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", + "framework/nightly_protocol", + "node-runtime/nightly_protocol", +] +calimero_zero_storage = ["unc-primitives/calimero_zero_storage"] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-client/nightly", + "unc-dyn-configs/nightly", + "unc-indexer-primitives/nightly", + "unc-o11y/nightly", + "unc-parameters/nightly", + "unc-primitives/nightly", + "unc-store/nightly", + "framework/nightly", + "node-runtime/nightly", +] diff --git a/chain/indexer/README.md b/chain/indexer/README.md new file mode 100644 index 000000000..93b929f08 --- /dev/null +++ b/chain/indexer/README.md @@ -0,0 +1,118 @@ +# NEAR Indexer + +NEAR Indexer is a micro-framework, which provides you with a stream of blocks that are recorded on NEAR network. It is useful to handle real-time "events" on the chain. + +## Rationale + +As scaling dApps enter NEAR’s mainnet, an issue may arise: how do they quickly and efficiently access state from our deployed smart contracts, and cut out the cruft? Contracts may grow to have complex data structures and querying the network RPC may not be the optimal way to access state data. The NEAR Indexer Framework allows for streams to be captured and indexed in a customized manner. The typical use-case is for this data to make its way to a relational database. Seeing as this is custom per project, there is engineering work involved in using this framework. + +NEAR Indexer is already in use for several new projects, namely, we index all the events for NEAR Blockchain Explorer, and we also dig into Access Keys and index all of them for NEAR Wallet passphrase recovery and multi-factor authentication. With NEAR Indexer you can do high-level aggregation as well as low-level introspection of all the events inside the blockchain. + +We are going to build more Indexers in the future, and will also consider building Indexer integrations with streaming solutions like Kafka, RabbitMQ, ZeroMQ, and NoSQL databases. Feel free to [join our discussions](https://github.com/utnet-org/utility/issues/2996). + +See the [example](https://github.com/utnet-org/utility/tree/master/tools/indexer/example) for further technical details. + +## How to set up and test NEAR Indexer + +Before you proceed, make sure you have the following software installed: +* [rustup](https://rustup.rs/) or Rust version that is mentioned in `rust-toolchain` file in the root of framework project. + +### localnet + +Clone [framework](https://github.com/utnet-org/utility) + +To run the NEAR Indexer connected to a network we need to have configs and keys prepopulated. To generate configs for localnet do the following + +```bash +$ git clone git@github.com:utnet-org/utility.git +$ cd framework/tools/indexer/example +$ cargo run --release -- --home-dir ~/.near/localnet init +``` + +The above commands should initialize necessary configs and keys to run localnet in `~/.near/localnet`. + +```bash +$ cargo run --release -- --home-dir ~/.near/localnet/ run +``` + +After the node is started, you should see logs of every block produced in your localnet. Get back to the code to implement any custom handling of the data flowing into the indexer. + +Use [unc-shell](https://github.com/near/unc-shell) to submit transactions. For example, to create a new user you run the following command: + +```bash +$ unc_ENV=local near --keyPath ~/.near/localnet/validator_key.json \ + create_account new-account.test.near --masterAccount test.near +``` + + +### testnet / betanet + +To run the NEAR Indexer connected to testnet or betanet we need to have configs and keys prepopulated, you can get them with the NEAR Indexer Example like above with a little change. Follow the instructions below to run non-validating node (leaving account ID empty). + +```bash +$ cargo run --release -- --home-dir ~/.near/testnet init --chain-id testnet --download +``` + +The above code will download the official genesis config and generate necessary configs. You can replace `testnet` in the command above to different network ID `betanet`. + +**NB!** According to changes in `framework` config generation we don't fill all the necessary fields in the config file. While this issue is open you need to download config you want and replace the generated one manually. + - [testnet config.json](https://s3-us-west-1.amazonaws.com/build.nearprotocol.com/framework-deploy/testnet/config.json) + - [betanet config.json](https://s3-us-west-1.amazonaws.com/build.nearprotocol.com/framework-deploy/betanet/config.json) + - [mainnet config.json](https://s3-us-west-1.amazonaws.com/build.nearprotocol.com/framework-deploy/mainnet/config.json) + +Replace `config.json` in your `--home-dir` (e.g. `~/.near/testnet/config.json`) with downloaded one. + +Configs for the specified network are in the `--home-dir` provided folder. We need to ensure that NEAR Indexer follows all the necessary shards, so `"tracked_shards"` parameters in `~/.near/testnet/config.json` needs to be configured properly. For example, with a single shared network, you just add the shard #0 to the list: + +```text +... +"tracked_shards": [0], +... +``` + +Hint: See the Tweaks section below to learn more about further configuration options. + +After that we can run NEAR Indexer. + + +```bash +$ cargo run --release -- --home-dir ~/.near/testnet run +``` + +After the network is synced, you should see logs of every block produced in Testnet. Get back to the code to implement any custom handling of the data flowing into the indexer. + +## Tweaks + +By default, framework is configured to do as little work as possible while still operating on an up-to-date state. Indexers may have different requirements, so there is no solution that would work for everyone, and thus we are going to provide you with the set of knobs you can tune for your requirements. + +As already has been mentioned above, the most common tweak you need to apply is listing all the shards you want to index data from; to do that, you should ensure that `"tracked_shards"` in the `config.json` lists all the shard IDs, e.g. for the current betanet and testnet, which have a single shard: + +```json +... +"tracked_shards": [0], +... +``` + + +You can choose Indexer Framework sync mode by setting what to stream: + - `LatestSynced` - Real-time syncing, always taking the latest finalized block to stream + - `FromInterruption` - Starts syncing from the block NEAR Indexer was interrupted last time + - `BlockHeight(u64)` - Specific block height to start syncing from + + Refer to `main()` function in [Indexer Example](https://github.com/utnet-org/utility/blob/master/tools/indexer/example/src/main.rs) + +Indexer Framework also exposes access to the internal APIs (see `Indexer::client_actors` method), so you can fetch data about any block, transaction, etc, yet by default, framework is configured to remove old data (garbage collection), so querying the data that was observed a few epochs before may return an error saying that the data is not found. If you only need blocks streaming, you don't need this tweak, but if you need access to the historical data right from your Indexer, consider updating `"archive"` setting in `config.json` to `true`: + +```json +... +"archive": true, +... +``` + + +## Who is using NEAR Indexer? + +*This list is not exhaustive, feel free to submit your project by sending a pull request.* + +* [Indexer for NEAR Wallet](https://github.com/near/unc-indexer-for-wallet) +* [Indexer for NEAR Explorer](https://github.com/near/unc-indexer-for-explorer) diff --git a/chain/indexer/src/lib.rs b/chain/indexer/src/lib.rs new file mode 100644 index 000000000..ac146de59 --- /dev/null +++ b/chain/indexer/src/lib.rs @@ -0,0 +1,174 @@ +#![doc = include_str!("../README.md")] + +use anyhow::Context; +use tokio::sync::mpsc; + +use unc_chain_configs::GenesisValidationMode; +pub use unc_primitives; +use unc_primitives::types::Gas; +pub use framework::{get_default_home, init_configs, UncConfig}; + +pub use unc_indexer_primitives::{ + IndexerChunkView, IndexerExecutionOutcomeWithOptionalReceipt, + IndexerExecutionOutcomeWithReceipt, IndexerShard, IndexerTransactionWithOutcome, + StreamerMessage, +}; + +mod streamer; + +pub const INDEXER: &str = "indexer"; + +/// Config wrapper to simplify signature and usage of `framework::init_configs` +/// function by making args more explicit via struct +#[derive(Debug, Clone)] +pub struct InitConfigArgs { + /// chain/network id (localnet, testnet, devnet, betanet) + pub chain_id: Option, + /// Account ID for the validator key + pub account_id: Option, + /// Specify private key generated from seed (TESTING ONLY) + pub test_seed: Option, + /// Number of shards to initialize the chain with + pub num_shards: u64, + /// Makes block production fast (TESTING ONLY) + pub fast: bool, + /// Genesis file to use when initializing testnet (including downloading) + pub genesis: Option, + /// Download the verified NEAR genesis file automatically. + pub download_genesis: bool, + /// Specify a custom download URL for the genesis file. + pub download_genesis_url: Option, + /// Specify a custom download URL for the records file. + pub download_records_url: Option, + /// Download the verified NEAR config file automatically. + pub download_config: bool, + /// Specify a custom download URL for the config file. + pub download_config_url: Option, + /// Specify the boot nodes to bootstrap the network + pub boot_nodes: Option, + /// Specify a custom max_gas_burnt_view limit. + pub max_gas_burnt_view: Option, +} + +/// Enum to define a mode of syncing for NEAR Indexer +#[derive(Debug, Clone)] +pub enum SyncModeEnum { + /// Real-time syncing, always taking the latest finalized block to stream + LatestSynced, + /// Starts syncing from the block NEAR Indexer was interrupted last time + FromInterruption, + /// Specific block height to start syncing from + BlockHeight(u64), +} + +/// Enum to define whether await for node to be fully synced or stream while syncing (useful for indexing from genesis) +#[derive(Debug, Clone)] +pub enum AwaitForNodeSyncedEnum { + /// Don't stream until the node is fully synced + WaitForFullSync, + /// Stream while node is syncing + StreamWhileSyncing, +} + +/// NEAR Indexer configuration to be provided to `Indexer::new(IndexerConfig)` +#[derive(Debug, Clone)] +pub struct IndexerConfig { + /// Path to `home_dir` where configs and keys can be found + pub home_dir: std::path::PathBuf, + /// Mode of syncing for NEAR Indexer instance + pub sync_mode: SyncModeEnum, + /// Whether await for node to be synced or not + pub await_for_node_synced: AwaitForNodeSyncedEnum, + /// Tells whether to validate the genesis file before starting + pub validate_genesis: bool, +} + +/// This is the core component, which handles `framework` and internal `streamer`. +pub struct Indexer { + indexer_config: IndexerConfig, + unc_config: framework::UncConfig, + view_client: actix::Addr, + client: actix::Addr, +} + +impl Indexer { + /// Initialize Indexer by configuring `framework` + pub fn new(indexer_config: IndexerConfig) -> Result { + tracing::info!( + target: INDEXER, + "Load config from {}...", + indexer_config.home_dir.display() + ); + + let genesis_validation_mode = if indexer_config.validate_genesis { + GenesisValidationMode::Full + } else { + GenesisValidationMode::UnsafeFast + }; + let unc_config = + framework::config::load_config(&indexer_config.home_dir, genesis_validation_mode) + .unwrap_or_else(|e| panic!("Error loading config: {:#}", e)); + + assert!( + !&unc_config.client_config.tracked_shards.is_empty(), + "Indexer should track at least one shard. \n\ + Tip: You may want to update {} with `\"tracked_shards\": [0]` + ", + indexer_config.home_dir.join("config.json").display() + ); + let framework::NearNode { client, view_client, .. } = + framework::start_with_config(&indexer_config.home_dir, unc_config.clone()) + .with_context(|| "start_with_config")?; + Ok(Self { view_client, client, unc_config, indexer_config }) + } + + /// Boots up `unc_indexer::streamer`, so it monitors the new blocks with chunks, transactions, receipts, and execution outcomes inside. The returned stream handler should be drained and handled on the user side. + pub fn streamer(&self) -> mpsc::Receiver { + let (sender, receiver) = mpsc::channel(100); + actix::spawn(streamer::start( + self.view_client.clone(), + self.client.clone(), + self.indexer_config.clone(), + self.unc_config.config.store.clone(), + self.unc_config.config.archive, + sender, + )); + receiver + } + + /// Expose uncd config + pub fn unc_config(&self) -> &framework::UncConfig { + &self.unc_config + } + + /// Internal client actors just in case. Use on your own risk, backward compatibility is not guaranteed + pub fn client_actors( + &self, + ) -> (actix::Addr, actix::Addr) { + (self.view_client.clone(), self.client.clone()) + } +} + +/// Function that initializes configs for the node which +/// accepts `InitConfigWrapper` and calls original `init_configs` from `uncd` +pub fn indexer_init_configs( + dir: &std::path::PathBuf, + params: InitConfigArgs, +) -> Result<(), anyhow::Error> { + init_configs( + dir, + params.chain_id, + params.account_id.and_then(|account_id| account_id.parse().ok()), + params.test_seed.as_deref(), + params.num_shards, + params.fast, + params.genesis.as_deref(), + params.download_genesis, + params.download_genesis_url.as_deref(), + params.download_records_url.as_deref(), + params.download_config, + params.download_config_url.as_deref(), + params.boot_nodes.as_deref(), + params.max_gas_burnt_view, + ) +} diff --git a/chain/indexer/src/streamer/errors.rs b/chain/indexer/src/streamer/errors.rs new file mode 100644 index 000000000..53675cf47 --- /dev/null +++ b/chain/indexer/src/streamer/errors.rs @@ -0,0 +1,14 @@ +use actix::MailboxError; + +/// Error occurs in case of failed data fetch +#[derive(Debug)] +pub enum FailedToFetchData { + MailboxError(MailboxError), + String(String), +} + +impl From for FailedToFetchData { + fn from(actix_error: MailboxError) -> Self { + FailedToFetchData::MailboxError(actix_error) + } +} diff --git a/chain/indexer/src/streamer/fetchers.rs b/chain/indexer/src/streamer/fetchers.rs new file mode 100644 index 000000000..6972c8ebd --- /dev/null +++ b/chain/indexer/src/streamer/fetchers.rs @@ -0,0 +1,182 @@ +//! Streamer watches the network and collects all the blocks and related chunks +//! into one struct and pushes in in to the given queue +use std::collections::HashMap; + +use actix::Addr; +use futures::stream::StreamExt; +use tracing::warn; + +use unc_indexer_primitives::IndexerExecutionOutcomeWithOptionalReceipt; +use unc_o11y::WithSpanContextExt; +use unc_primitives::hash::CryptoHash; +use unc_primitives::{types, views}; + +use super::errors::FailedToFetchData; +use super::INDEXER; + +pub(crate) async fn fetch_status( + client: &Addr, +) -> Result { + client + .send(unc_client::Status { is_health_check: false, detailed: false }.with_span_context()) + .await? + .map_err(|err| FailedToFetchData::String(err.to_string())) +} + +/// Fetches the status to retrieve `latest_block_height` to determine if we need to fetch +/// entire block or we already fetched this block. +pub(crate) async fn fetch_latest_block( + client: &Addr, +) -> Result { + client + .send( + unc_client::GetBlock(unc_primitives::types::BlockReference::Finality( + unc_primitives::types::Finality::Final, + )) + .with_span_context(), + ) + .await? + .map_err(|err| FailedToFetchData::String(err.to_string())) +} + +/// Fetches specific block by it's height +pub(crate) async fn fetch_block_by_height( + client: &Addr, + height: u64, +) -> Result { + client + .send( + unc_client::GetBlock(unc_primitives::types::BlockId::Height(height).into()) + .with_span_context(), + ) + .await? + .map_err(|err| FailedToFetchData::String(err.to_string())) +} + +/// Fetches specific block by it's hash +pub(crate) async fn fetch_block( + client: &Addr, + hash: CryptoHash, +) -> Result { + client + .send( + unc_client::GetBlock(unc_primitives::types::BlockId::Hash(hash).into()) + .with_span_context(), + ) + .await? + .map_err(|err| FailedToFetchData::String(err.to_string())) +} + +pub(crate) async fn fetch_state_changes( + client: &Addr, + block_hash: CryptoHash, + epoch_id: unc_primitives::types::EpochId, +) -> Result, FailedToFetchData> { + client + .send( + unc_client::GetStateChangesWithCauseInBlockForTrackedShards { block_hash, epoch_id } + .with_span_context(), + ) + .await? + .map_err(|err| FailedToFetchData::String(err.to_string())) +} + +/// Fetch all ExecutionOutcomeWithId for current block +/// Returns a HashMap where the key is shard id IndexerExecutionOutcomeWithOptionalReceipt +pub(crate) async fn fetch_outcomes( + client: &Addr, + block_hash: CryptoHash, +) -> Result< + HashMap>, + FailedToFetchData, +> { + let outcomes = client + .send(unc_client::GetExecutionOutcomesForBlock { block_hash }.with_span_context()) + .await? + .map_err(FailedToFetchData::String)?; + + let mut shard_execution_outcomes_with_receipts: HashMap< + unc_primitives::types::ShardId, + Vec, + > = HashMap::new(); + for (shard_id, shard_outcomes) in outcomes { + let mut outcomes_with_receipts: Vec = vec![]; + for outcome in shard_outcomes { + let receipt = match fetch_receipt_by_id(&client, outcome.id).await { + Ok(res) => res, + Err(e) => { + warn!( + target: INDEXER, + "Unable to fetch Receipt with id {}. Skipping it in ExecutionOutcome \n {:#?}", + outcome.id, + e, + ); + None + } + }; + outcomes_with_receipts.push(IndexerExecutionOutcomeWithOptionalReceipt { + execution_outcome: outcome, + receipt, + }); + } + shard_execution_outcomes_with_receipts.insert(shard_id, outcomes_with_receipts); + } + + Ok(shard_execution_outcomes_with_receipts) +} + +async fn fetch_receipt_by_id( + client: &Addr, + receipt_id: CryptoHash, +) -> Result, FailedToFetchData> { + client + .send(unc_client::GetReceipt { receipt_id }.with_span_context()) + .await? + .map_err(|err| FailedToFetchData::String(err.to_string())) +} + +/// Fetches single chunk (as `unc_primitives::views::ChunkView`) by provided +/// chunk hash. +async fn fetch_single_chunk( + client: &Addr, + chunk_hash: unc_primitives::hash::CryptoHash, +) -> Result { + client + .send(unc_client::GetChunk::ChunkHash(chunk_hash.into()).with_span_context()) + .await? + .map_err(|err| FailedToFetchData::String(err.to_string())) +} + +/// Fetches all chunks belonging to given block. +/// Includes transactions and receipts in custom struct (to provide more info). +pub(crate) async fn fetch_block_chunks( + client: &Addr, + block: &views::BlockView, +) -> Result, FailedToFetchData> { + let mut futures: futures::stream::FuturesUnordered<_> = block + .chunks + .iter() + .filter(|chunk| chunk.height_included == block.header.height) + .map(|chunk| fetch_single_chunk(&client, chunk.chunk_hash)) + .collect(); + let mut chunks = Vec::::with_capacity(futures.len()); + while let Some(chunk) = futures.next().await { + chunks.push(chunk?); + } + Ok(chunks) +} + +pub(crate) async fn fetch_protocol_config( + client: &Addr, + block_hash: unc_primitives::hash::CryptoHash, +) -> Result { + Ok(client + .send( + unc_client::GetProtocolConfig(types::BlockReference::from(types::BlockId::Hash( + block_hash, + ))) + .with_span_context(), + ) + .await? + .map_err(|err| FailedToFetchData::String(err.to_string()))?) +} diff --git a/chain/indexer/src/streamer/metrics.rs b/chain/indexer/src/streamer/metrics.rs new file mode 100644 index 000000000..97f62e643 --- /dev/null +++ b/chain/indexer/src/streamer/metrics.rs @@ -0,0 +1,45 @@ +use unc_o11y::metrics::{ + try_create_histogram, try_create_int_counter, try_create_int_gauge, Histogram, IntCounter, + IntGauge, +}; +use once_cell::sync::Lazy; + +pub(crate) static START_BLOCK_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_indexer_streaming_start_block_height", + "Block height from which the indexing iteration started", + ) + .unwrap() +}); + +pub(crate) static LATEST_BLOCK_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_indexer_streaming_latest_block_height", + "Block height to which the indexing iteration runs", + ) + .unwrap() +}); + +pub(crate) static CURRENT_BLOCK_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_indexer_streaming_current_block_height", + "Current height of the block being indexed", + ) + .unwrap() +}); + +pub(crate) static NUM_STREAMER_MESSAGES_SENT: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_indexer_num_streamer_messages_sent", + "Number of Streamer messages sent to", + ) + .unwrap() +}); + +pub(crate) static BUILD_STREAMER_MESSAGE_TIME: Lazy = Lazy::new(|| { + try_create_histogram( + "unc_indexer_build_streamer_message_time", + "Time taken to build a streamer message", + ) + .unwrap() +}); diff --git a/chain/indexer/src/streamer/mod.rs b/chain/indexer/src/streamer/mod.rs new file mode 100644 index 000000000..3ecffe587 --- /dev/null +++ b/chain/indexer/src/streamer/mod.rs @@ -0,0 +1,380 @@ +use self::errors::FailedToFetchData; +use self::fetchers::{ + fetch_block, fetch_block_by_height, fetch_block_chunks, fetch_latest_block, fetch_outcomes, + fetch_state_changes, fetch_status, +}; +use self::utils::convert_transactions_sir_into_local_receipts; +use crate::streamer::fetchers::fetch_protocol_config; +use crate::INDEXER; +use crate::{AwaitForNodeSyncedEnum, IndexerConfig}; +use actix::Addr; +use async_recursion::async_recursion; +use unc_indexer_primitives::{ + IndexerChunkView, IndexerExecutionOutcomeWithOptionalReceipt, + IndexerExecutionOutcomeWithReceipt, IndexerShard, IndexerTransactionWithOutcome, + StreamerMessage, +}; +use unc_parameters::RuntimeConfig; +use unc_primitives::hash::CryptoHash; +use unc_primitives::views; +use rocksdb::DB; +use std::time::Duration; +use tokio::sync::mpsc; +use tokio::time; +use tracing::{debug, info}; + +mod errors; +mod fetchers; +mod metrics; +mod utils; + +const INTERVAL: Duration = Duration::from_millis(500); + +/// Blocks #47317863 and #47317864 with restored receipts. +const PROBLEMATIC_BLOCKS: [CryptoHash; 2] = [ + CryptoHash( + *b"\xcd\xde\x9a\x3f\x5d\xdf\xb4\x2c\xb9\x9b\xf4\x8c\x04\x95\x6f\x5b\ + \xa0\xb7\x29\xe2\xa5\x04\xf8\xbd\x9c\x86\x92\xd6\x16\x8c\xcf\x14", + ), + CryptoHash( + *b"\x12\xa9\x5a\x1a\x3d\x14\xa7\x36\xb3\xce\xe6\xea\x07\x20\x8e\x75\ + \x4e\xb5\xc2\xd7\xf9\x11\xca\x29\x09\xe0\xb8\x85\xb5\x2b\x95\x6a", + ), +]; + +/// Tests whether raw hashes in [`PROBLEMATIC_BLOCKS`] match expected +/// user-readable hashes. Ideally we would compute the hashes at compile time +/// but there’s no const function for base58→bytes conversion so instead we’re +/// hard-coding the raw base in [`PROBLEMATIC_BLOCKS`] and have this test to +/// confirm the raw values are correct. +#[test] +fn test_problematic_blocks_hash() { + let got: Vec = + PROBLEMATIC_BLOCKS.iter().map(std::string::ToString::to_string).collect(); + assert_eq!( + vec![ + "ErdT2vLmiMjkRoSUfgowFYXvhGaLJZUWrgimHRkousrK", + "2Fr7dVAZGoPYgpwj6dfASSde6Za34GNUJb4CkZ8NSQqw" + ], + got + ); +} + +/// This function supposed to return the entire `StreamerMessage`. +/// It fetches the block and all related parts (chunks, outcomes, state changes etc.) +/// and returns everything together in one struct +#[async_recursion] +async fn build_streamer_message( + client: &Addr, + block: views::BlockView, +) -> Result { + let _timer = metrics::BUILD_STREAMER_MESSAGE_TIME.start_timer(); + let chunks = fetch_block_chunks(&client, &block).await?; + + let protocol_config_view = fetch_protocol_config(&client, block.header.hash).await?; + let num_shards = protocol_config_view.num_block_producer_seats_per_shard.len() + as unc_primitives::types::NumShards; + + let runtime_config_store = unc_parameters::RuntimeConfigStore::new(None); + let runtime_config = runtime_config_store.get_config(protocol_config_view.protocol_version); + + let mut shards_outcomes = fetch_outcomes(&client, block.header.hash).await?; + let mut state_changes = fetch_state_changes( + &client, + block.header.hash, + unc_primitives::types::EpochId(block.header.epoch_id), + ) + .await?; + let mut indexer_shards = (0..num_shards) + .map(|shard_id| IndexerShard { + shard_id, + chunk: None, + receipt_execution_outcomes: vec![], + state_changes: state_changes.remove(&shard_id).unwrap_or_default(), + }) + .collect::>(); + + for chunk in chunks { + let views::ChunkView { transactions, author, header, receipts: chunk_non_local_receipts } = + chunk; + + let shard_id = header.shard_id as usize; + + let mut outcomes = shards_outcomes + .remove(&header.shard_id) + .expect("Execution outcomes for given shard should be present"); + + // Take execution outcomes for receipts from the vec and keep only the ones for transactions + let mut receipt_outcomes = outcomes.split_off(transactions.len()); + + let indexer_transactions = transactions + .into_iter() + .zip(outcomes.into_iter()) + .map(|(transaction, outcome)| { + assert_eq!( + outcome.execution_outcome.id, transaction.hash, + "This ExecutionOutcome must have the same id as Transaction hash" + ); + IndexerTransactionWithOutcome { outcome, transaction } + }) + .collect::>(); + + let chunk_local_receipts = convert_transactions_sir_into_local_receipts( + &client, + &runtime_config, + indexer_transactions + .iter() + .filter(|tx| tx.transaction.signer_id == tx.transaction.receiver_id) + .collect::>(), + &block, + ) + .await?; + + // Add local receipts to corresponding outcomes + for receipt in &chunk_local_receipts { + if let Some(outcome) = receipt_outcomes + .iter_mut() + .find(|outcome| outcome.execution_outcome.id == receipt.receipt_id) + { + debug_assert!(outcome.receipt.is_none()); + outcome.receipt = Some(receipt.clone()); + } + } + + let mut chunk_receipts = chunk_local_receipts; + + let mut receipt_execution_outcomes: Vec = vec![]; + for outcome in receipt_outcomes { + let IndexerExecutionOutcomeWithOptionalReceipt { execution_outcome, receipt } = outcome; + let receipt = if let Some(receipt) = receipt { + receipt + } else { + // Receipt might be missing only in case of delayed local receipt + // that appeared in some of the previous blocks + // we will be iterating over previous blocks until we found the receipt + let mut prev_block_tried = 0u16; + let mut prev_block_hash = block.header.prev_hash; + 'find_local_receipt: loop { + if prev_block_tried > 1000 { + panic!("Failed to find local receipt in 1000 prev blocks"); + } + let prev_block = match fetch_block(&client, prev_block_hash).await { + Ok(block) => block, + Err(err) => panic!("Unable to get previous block: {:?}", err), + }; + + prev_block_hash = prev_block.header.prev_hash; + + if let Some(receipt) = find_local_receipt_by_id_in_block( + &client, + &runtime_config, + prev_block, + execution_outcome.id, + ) + .await? + { + break 'find_local_receipt receipt; + } + + prev_block_tried += 1; + } + }; + receipt_execution_outcomes + .push(IndexerExecutionOutcomeWithReceipt { execution_outcome, receipt }); + } + + // Blocks #47317863 and #47317864 + // (ErdT2vLmiMjkRoSUfgowFYXvhGaLJZUWrgimHRkousrK, 2Fr7dVAZGoPYgpwj6dfASSde6Za34GNUJb4CkZ8NSQqw) + // are the first blocks of an upgraded protocol version on mainnet. + // In this block ExecutionOutcomes for restored Receipts appear. + // However the Receipts are not included in any Chunk. Indexer Framework needs to include them, + // so it was decided to artificially include the Receipts into the Chunk of the Block where + // ExecutionOutcomes appear. + // ref: https://github.com/utnet-org/utility/pull/4248 + if PROBLEMATIC_BLOCKS.contains(&block.header.hash) + && &protocol_config_view.chain_id == unc_primitives::chains::MAINNET + { + let mut restored_receipts: Vec = vec![]; + let receipt_ids_included: std::collections::HashSet = + chunk_non_local_receipts.iter().map(|receipt| receipt.receipt_id).collect(); + for outcome in &receipt_execution_outcomes { + if receipt_ids_included.get(&outcome.receipt.receipt_id).is_none() { + restored_receipts.push(outcome.receipt.clone()); + } + } + + chunk_receipts.extend(restored_receipts); + } + + chunk_receipts.extend(chunk_non_local_receipts); + + indexer_shards[shard_id].receipt_execution_outcomes = receipt_execution_outcomes; + // Put the chunk into corresponding indexer shard + indexer_shards[shard_id].chunk = Some(IndexerChunkView { + author, + header, + transactions: indexer_transactions, + receipts: chunk_receipts, + }); + } + + // Ideally we expect `shards_outcomes` to be empty by this time, but if something went wrong with + // chunks and we end up with non-empty `shards_outcomes` we want to be sure we put them into IndexerShard + // That might happen before the fix https://github.com/utnet-org/utility/pull/4228 + for (shard_id, outcomes) in shards_outcomes { + indexer_shards[shard_id as usize].receipt_execution_outcomes.extend( + outcomes.into_iter().map(|outcome| IndexerExecutionOutcomeWithReceipt { + execution_outcome: outcome.execution_outcome, + receipt: outcome.receipt.expect("`receipt` must be present at this moment"), + }), + ) + } + + Ok(StreamerMessage { block, shards: indexer_shards }) +} + +/// Function that tries to find specific local receipt by it's ID and returns it +/// otherwise returns None +async fn find_local_receipt_by_id_in_block( + client: &Addr, + runtime_config: &RuntimeConfig, + block: views::BlockView, + receipt_id: unc_primitives::hash::CryptoHash, +) -> Result, FailedToFetchData> { + let chunks = fetch_block_chunks(&client, &block).await?; + + let mut shards_outcomes = fetch_outcomes(&client, block.header.hash).await?; + + for chunk in chunks { + let views::ChunkView { header, transactions, .. } = chunk; + + let outcomes = shards_outcomes + .remove(&header.shard_id) + .expect("Execution outcomes for given shard should be present"); + + if let Some((transaction, outcome)) = + transactions.into_iter().zip(outcomes.into_iter()).find(|(_, outcome)| { + outcome + .execution_outcome + .outcome + .receipt_ids + .first() + .expect("The transaction ExecutionOutcome should have one receipt id in vec") + == &receipt_id + }) + { + let indexer_transaction = IndexerTransactionWithOutcome { transaction, outcome }; + let local_receipts = convert_transactions_sir_into_local_receipts( + &client, + &runtime_config, + vec![&indexer_transaction], + &block, + ) + .await?; + + return Ok(local_receipts.into_iter().next()); + } + } + Ok(None) +} + +/// Function that starts Streamer's busy loop. Every half a seconds it fetches the status +/// compares to already fetched block height and in case it differs fetches new block of given height. +/// +/// We have to pass `client: Addr` and `view_client: Addr`. +pub(crate) async fn start( + view_client: Addr, + client: Addr, + indexer_config: IndexerConfig, + store_config: unc_store::StoreConfig, + archive: bool, + blocks_sink: mpsc::Sender, +) { + info!(target: INDEXER, "Starting Streamer..."); + let indexer_db_path = + unc_store::NodeStorage::opener(&indexer_config.home_dir, archive, &store_config, None) + .path() + .join("indexer"); + + // TODO: implement proper error handling + let db = DB::open_default(indexer_db_path).unwrap(); + let mut last_synced_block_height: Option = None; + + 'main: loop { + time::sleep(INTERVAL).await; + match indexer_config.await_for_node_synced { + AwaitForNodeSyncedEnum::WaitForFullSync => { + let status = fetch_status(&client).await; + if let Ok(status) = status { + if status.sync_info.syncing { + continue; + } + } + } + AwaitForNodeSyncedEnum::StreamWhileSyncing => {} + }; + + let block = if let Ok(block) = fetch_latest_block(&view_client).await { + block + } else { + continue; + }; + + let latest_block_height = block.header.height; + let start_syncing_block_height = if let Some(last_synced_block_height) = + last_synced_block_height + { + last_synced_block_height + 1 + } else { + match indexer_config.sync_mode { + crate::SyncModeEnum::FromInterruption => { + match db.get(b"last_synced_block_height").unwrap() { + Some(value) => String::from_utf8(value).unwrap().parse::().unwrap(), + None => latest_block_height, + } + } + crate::SyncModeEnum::LatestSynced => latest_block_height, + crate::SyncModeEnum::BlockHeight(height) => height, + } + }; + + debug!( + target: INDEXER, + "Streaming is about to start from block #{} and the latest block is #{}", + start_syncing_block_height, + latest_block_height + ); + metrics::START_BLOCK_HEIGHT.set(start_syncing_block_height as i64); + metrics::LATEST_BLOCK_HEIGHT.set(latest_block_height as i64); + for block_height in start_syncing_block_height..=latest_block_height { + metrics::CURRENT_BLOCK_HEIGHT.set(block_height as i64); + if let Ok(block) = fetch_block_by_height(&view_client, block_height).await { + let response = build_streamer_message(&view_client, block).await; + + match response { + Ok(streamer_message) => { + debug!(target: INDEXER, "{:#?}", &streamer_message); + if blocks_sink.send(streamer_message).await.is_err() { + info!( + target: INDEXER, + "Unable to send StreamerMessage to listener, listener doesn't listen. terminating..." + ); + break 'main; + } else { + metrics::NUM_STREAMER_MESSAGES_SENT.inc(); + } + } + Err(err) => { + debug!( + target: INDEXER, + "Missing data, skipping block #{}...", block_height + ); + debug!(target: INDEXER, "{:#?}", err); + } + } + } + db.put(b"last_synced_block_height", &block_height.to_string()).unwrap(); + last_synced_block_height = Some(block_height); + } + } +} diff --git a/chain/indexer/src/streamer/utils.rs b/chain/indexer/src/streamer/utils.rs new file mode 100644 index 000000000..29691aa70 --- /dev/null +++ b/chain/indexer/src/streamer/utils.rs @@ -0,0 +1,68 @@ +use actix::Addr; + +use unc_indexer_primitives::IndexerTransactionWithOutcome; +use unc_parameters::RuntimeConfig; +use unc_primitives::views; +use node_runtime::config::tx_cost; + +use super::errors::FailedToFetchData; +use super::fetchers::fetch_block; + +pub(crate) async fn convert_transactions_sir_into_local_receipts( + client: &Addr, + runtime_config: &RuntimeConfig, + txs: Vec<&IndexerTransactionWithOutcome>, + block: &views::BlockView, +) -> Result, FailedToFetchData> { + if txs.is_empty() { + return Ok(vec![]); + } + let prev_block = fetch_block(&client, block.header.prev_hash).await?; + let prev_block_gas_price = prev_block.header.gas_price; + + let local_receipts: Vec = + txs.into_iter() + .map(|tx| { + let cost = tx_cost( + &runtime_config, + &unc_primitives::transaction::Transaction { + signer_id: tx.transaction.signer_id.clone(), + public_key: tx.transaction.public_key.clone(), + nonce: tx.transaction.nonce, + receiver_id: tx.transaction.receiver_id.clone(), + block_hash: block.header.hash, + actions: tx + .transaction + .actions + .clone() + .into_iter() + .map(|action| { + unc_primitives::transaction::Action::try_from(action).unwrap() + }) + .collect(), + }, + prev_block_gas_price, + true, + ); + views::ReceiptView { + predecessor_id: tx.transaction.signer_id.clone(), + receiver_id: tx.transaction.receiver_id.clone(), + receipt_id: *tx.outcome.execution_outcome.outcome.receipt_ids.first().expect( + "The transaction ExecutionOutcome should have one receipt id in vec", + ), + receipt: views::ReceiptEnumView::Action { + signer_id: tx.transaction.signer_id.clone(), + signer_public_key: tx.transaction.public_key.clone(), + gas_price: cost + .expect("TransactionCost returned IntegerOverflowError") + .receipt_gas_price, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: tx.transaction.actions.clone(), + }, + } + }) + .collect(); + + Ok(local_receipts) +} diff --git a/chain/jsonrpc-adversarial-primitives/Cargo.toml b/chain/jsonrpc-adversarial-primitives/Cargo.toml new file mode 100644 index 000000000..54e9a42b0 --- /dev/null +++ b/chain/jsonrpc-adversarial-primitives/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "unc-jsonrpc-adversarial-primitives" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +serde.workspace = true + +unc-primitives.workspace = true +unc-network.workspace = true + +[features] +nightly_protocol = [ + "unc-network/nightly_protocol", + "unc-primitives/nightly_protocol", +] +nightly = [ + "nightly_protocol", + "unc-network/nightly", + "unc-primitives/nightly", +] +test_features = ["unc-network/test_features"] diff --git a/chain/jsonrpc-adversarial-primitives/src/lib.rs b/chain/jsonrpc-adversarial-primitives/src/lib.rs new file mode 100644 index 000000000..010b86af3 --- /dev/null +++ b/chain/jsonrpc-adversarial-primitives/src/lib.rs @@ -0,0 +1,6 @@ +use unc_primitives::network::PeerId; + +#[derive(serde::Deserialize)] +pub struct StartRoutingTableSyncRequest { + pub peer_id: PeerId, +} diff --git a/chain/jsonrpc-primitives/Cargo.toml b/chain/jsonrpc-primitives/Cargo.toml new file mode 100644 index 000000000..3d0eae238 --- /dev/null +++ b/chain/jsonrpc-primitives/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "unc-jsonrpc-primitives" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "This crate hosts structures for the NEAR JSON RPC Requests, Responses and Error types" +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +arbitrary.workspace = true +serde.workspace = true +serde_json.workspace = true +thiserror.workspace = true + +unc-crypto.workspace = true +unc-primitives.workspace = true +unc-chain-configs.workspace = true +unc-rpc-error-macro.workspace = true +unc-client-primitives = { workspace = true, optional = true } + +[features] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-client-primitives/nightly_protocol", + "unc-primitives/nightly_protocol", +] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-client-primitives/nightly", + "unc-primitives/nightly", +] +full = ["debug_types"] +debug_types = ["unc-client-primitives"] +test_features = [] diff --git a/chain/jsonrpc-primitives/src/errors.rs b/chain/jsonrpc-primitives/src/errors.rs new file mode 100644 index 000000000..0108a8697 --- /dev/null +++ b/chain/jsonrpc-primitives/src/errors.rs @@ -0,0 +1,199 @@ +use unc_primitives::errors::TxExecutionError; +use serde_json::{to_value, Value}; +use std::fmt; + +#[derive(Debug, serde::Serialize)] +pub struct RpcParseError(pub String); + +/// This struct may be returned from JSON RPC server in case of error +/// It is expected that that this struct has impls From<_> all other RPC errors +/// like [RpcBlockError](crate::types::blocks::RpcBlockError) +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct RpcError { + #[serde(flatten)] + pub error_struct: Option, + /// Deprecated please use the `error_struct` instead + pub code: i64, + /// Deprecated please use the `error_struct` instead + pub message: String, + /// Deprecated please use the `error_struct` instead + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)] +#[serde(tag = "name", content = "cause", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcErrorKind { + RequestValidationError(RpcRequestValidationErrorKind), + HandlerError(Value), + InternalError(Value), +} + +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcRequestValidationErrorKind { + MethodNotFound { method_name: String }, + ParseError { error_message: String }, +} + +/// A general Server Error +#[derive( + serde::Serialize, + serde::Deserialize, + Debug, + PartialEq, + Eq, + Clone, + unc_rpc_error_macro::RpcError, +)] +pub enum ServerError { + TxExecutionError(TxExecutionError), + Timeout, + Closed, +} + +impl RpcError { + /// A generic constructor. + /// + /// Mostly for completeness, doesn't do anything but filling in the corresponding fields. + pub fn new(code: i64, message: String, data: Option) -> Self { + RpcError { code, message, data, error_struct: None } + } + + /// Create an Invalid Param error. + #[cfg(feature = "test_features")] + pub fn invalid_params(data: impl serde::Serialize) -> Self { + let value = match to_value(data) { + Ok(value) => value, + Err(err) => { + return Self::server_error(Some(format!( + "Failed to serialize invalid parameters error: {:?}", + err.to_string() + ))) + } + }; + RpcError::new(-32_602, "Invalid params".to_owned(), Some(value)) + } + + /// Create a server error. + #[cfg(feature = "test_features")] + pub fn server_error(e: Option) -> Self { + RpcError::new( + -32_000, + "Server error".to_owned(), + e.map(|v| to_value(v).expect("Must be representable in JSON")), + ) + } + + /// Create a parse error. + pub fn parse_error(e: String) -> Self { + RpcError { + code: -32_700, + message: "Parse error".to_owned(), + data: Some(Value::String(e.clone())), + error_struct: Some(RpcErrorKind::RequestValidationError( + RpcRequestValidationErrorKind::ParseError { error_message: e }, + )), + } + } + + pub fn serialization_error(e: String) -> Self { + RpcError::new_internal_error(Some(Value::String(e.clone())), e) + } + + /// Helper method to define extract INTERNAL_ERROR in separate RpcErrorKind + /// Returns HANDLER_ERROR if the error is not internal one + pub fn new_internal_or_handler_error(error_data: Option, error_struct: Value) -> Self { + if error_struct["name"] == "INTERNAL_ERROR" { + let error_message = match error_struct["info"].get("error_message") { + Some(Value::String(error_message)) => error_message.as_str(), + _ => "InternalError happened during serializing InternalError", + }; + Self::new_internal_error(error_data, error_message.to_string()) + } else { + Self::new_handler_error(error_data, error_struct) + } + } + + pub fn new_internal_error(error_data: Option, info: String) -> Self { + RpcError { + code: -32_000, + message: "Server error".to_owned(), + data: error_data, + error_struct: Some(RpcErrorKind::InternalError(serde_json::json!({ + "name": "INTERNAL_ERROR", + "info": serde_json::json!({"error_message": info}) + }))), + } + } + + fn new_handler_error(error_data: Option, error_struct: Value) -> Self { + RpcError { + code: -32_000, + message: "Server error".to_owned(), + data: error_data, + error_struct: Some(RpcErrorKind::HandlerError(error_struct)), + } + } + + /// Create a method not found error. + pub fn method_not_found(method: String) -> Self { + RpcError { + code: -32_601, + message: "Method not found".to_owned(), + data: Some(Value::String(method.clone())), + error_struct: Some(RpcErrorKind::RequestValidationError( + RpcRequestValidationErrorKind::MethodNotFound { method_name: method }, + )), + } + } +} + +impl fmt::Display for RpcError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl From for RpcError { + fn from(parse_error: RpcParseError) -> Self { + Self::parse_error(parse_error.0) + } +} + +impl From for RpcError { + fn from(_: std::convert::Infallible) -> Self { + unsafe { core::hint::unreachable_unchecked() } + } +} + +impl fmt::Display for ServerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ServerError::TxExecutionError(e) => write!(f, "ServerError: {}", e), + ServerError::Timeout => write!(f, "ServerError: Timeout"), + ServerError::Closed => write!(f, "ServerError: Closed"), + } + } +} + +impl From for RpcError { + fn from(e: ServerError) -> RpcError { + let error_data = match to_value(&e) { + Ok(value) => value, + Err(_err) => { + return RpcError::new_internal_error( + None, + "Failed to serialize ServerError".to_string(), + ) + } + }; + match e { + ServerError::TxExecutionError(_) => { + RpcError::new_handler_error(Some(error_data.clone()), error_data) + } + _ => RpcError::new_internal_error(Some(error_data), e.to_string()), + } + } +} diff --git a/chain/jsonrpc-primitives/src/lib.rs b/chain/jsonrpc-primitives/src/lib.rs new file mode 100644 index 000000000..a85478f51 --- /dev/null +++ b/chain/jsonrpc-primitives/src/lib.rs @@ -0,0 +1,3 @@ +pub mod errors; +pub mod message; +pub mod types; diff --git a/chain/jsonrpc-primitives/src/message.rs b/chain/jsonrpc-primitives/src/message.rs new file mode 100644 index 000000000..70dfd49ee --- /dev/null +++ b/chain/jsonrpc-primitives/src/message.rs @@ -0,0 +1,506 @@ +// Copyright 2017 tokio-jsonrpc Developers +// +// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +//! JSON-RPC 2.0 messages. +//! +//! The main entrypoint here is the [Message](enum.Message.html). The others are just building +//! blocks and you should generally work with `Message` instead. +use crate::errors::RpcError; +use serde::de::{Deserializer, Error, Unexpected, Visitor}; +use serde::ser::{SerializeStruct, Serializer}; +use serde_json::{Result as JsonResult, Value}; +use std::fmt::{Formatter, Result as FmtResult}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct Version; + +impl serde::Serialize for Version { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str("2.0") + } +} + +impl<'de> serde::Deserialize<'de> for Version { + fn deserialize>(deserializer: D) -> Result { + struct VersionVisitor; + impl<'de> Visitor<'de> for VersionVisitor { + type Value = Version; + + fn expecting(&self, formatter: &mut Formatter<'_>) -> FmtResult { + formatter.write_str("a version string") + } + + fn visit_str(self, value: &str) -> Result { + match value { + "2.0" => Ok(Version), + _ => Err(E::invalid_value(Unexpected::Str(value), &"value 2.0")), + } + } + } + deserializer.deserialize_str(VersionVisitor) + } +} + +/// An RPC request. +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct Request { + jsonrpc: Version, + pub method: String, + #[serde(default, skip_serializing_if = "Value::is_null")] + pub params: Value, + pub id: Value, +} + +impl Request { + /// Answer the request with a (positive) reply. + /// + /// The ID is taken from the request. + pub fn reply(&self, reply: Value) -> Message { + Message::Response(Response { jsonrpc: Version, result: Ok(reply), id: self.id.clone() }) + } + /// Answer the request with an error. + pub fn error(&self, error: RpcError) -> Message { + Message::Response(Response { jsonrpc: Version, result: Err(error), id: self.id.clone() }) + } +} + +/// A response to an RPC. +/// +/// It is created by the methods on [Request](struct.Request.html). +#[derive(Debug, Clone, PartialEq)] +pub struct Response { + jsonrpc: Version, + pub result: Result, + pub id: Value, +} + +impl serde::Serialize for Response { + fn serialize(&self, serializer: S) -> Result { + let mut sub = serializer.serialize_struct("Response", 3)?; + sub.serialize_field("jsonrpc", &self.jsonrpc)?; + match self.result { + Ok(ref value) => sub.serialize_field("result", value), + Err(ref err) => sub.serialize_field("error", err), + }?; + sub.serialize_field("id", &self.id)?; + sub.end() + } +} + +/// Deserializer for `Option` that produces `Some(Value::Null)`. +/// +/// The usual one produces None in that case. But we need to know the difference between +/// `{x: null}` and `{}`. +fn some_value<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> { + serde::Deserialize::deserialize(deserializer).map(Some) +} + +/// A helper trick for deserialization. +#[derive(serde::Deserialize)] +#[serde(deny_unknown_fields)] +struct WireResponse { + // It is actually used to eat and sanity check the deserialized text + #[allow(dead_code)] + jsonrpc: Version, + // Make sure we accept null as Some(Value::Null), instead of going to None + #[serde(default, deserialize_with = "some_value")] + result: Option, + error: Option, + id: Value, +} + +// Implementing deserialize is hard. We sidestep the difficulty by deserializing a similar +// structure that directly corresponds to whatever is on the wire and then convert it to our more +// convenient representation. +impl<'de> serde::Deserialize<'de> for Response { + fn deserialize>(deserializer: D) -> Result { + let wr: WireResponse = serde::Deserialize::deserialize(deserializer)?; + let result = match (wr.result, wr.error) { + (Some(res), None) => Ok(res), + (None, Some(err)) => Err(err), + _ => { + let err = D::Error::custom("Either 'error' or 'result' is expected, but not both"); + return Err(err); + } + }; + Ok(Response { jsonrpc: Version, result, id: wr.id }) + } +} + +/// A notification (doesn't expect an answer). +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct Notification { + jsonrpc: Version, + pub method: String, + #[serde(default, skip_serializing_if = "Value::is_null")] + pub params: Value, +} + +/// One message of the JSON RPC protocol. +/// +/// One message, directly mapped from the structures of the protocol. See the +/// [specification](http://www.jsonrpc.org/specification) for more details. +/// +/// Since the protocol allows one endpoint to be both client and server at the same time, the +/// message can decode and encode both directions of the protocol. +/// +/// The `Batch` variant is supposed to be created directly, without a constructor. +/// +/// The `UnmatchedSub` variant is used when a request is an array and some of the subrequests +/// aren't recognized as valid json rpc 2.0 messages. This is never returned as a top-level +/// element, it is returned as `Err(Broken::Unmatched)`. +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum Message { + /// An RPC request. + Request(Request), + /// A response to a Request. + Response(Response), + /// A notification. + Notification(Notification), + /// A batch of more requests or responses. + /// + /// The protocol allows bundling multiple requests, notifications or responses to a single + /// message. + /// + /// This variant has no direct constructor and is expected to be constructed manually. + Batch(Vec), + /// An unmatched sub entry in a `Batch`. + /// + /// When there's a `Batch` and an element doesn't comform to the JSONRPC 2.0 format, that one + /// is represented by this. This is never produced as a top-level value when parsing, the + /// `Err(Broken::Unmatched)` is used instead. It is not possible to serialize. + #[serde(skip_serializing)] + UnmatchedSub(Value), +} + +impl Message { + /// A constructor for a request. + /// + /// The ID is auto-generated. + pub fn request(method: String, params: Value) -> Self { + let id = Value::from(unc_primitives::utils::generate_random_string(9)); + Message::Request(Request { jsonrpc: Version, method, params, id }) + } + /// Create a top-level error (without an ID). + pub fn error(error: RpcError) -> Self { + Message::Response(Response { jsonrpc: Version, result: Err(error), id: Value::Null }) + } + /// A constructor for a notification. + pub fn notification(method: String, params: Value) -> Self { + Message::Notification(Notification { jsonrpc: Version, method, params }) + } + /// A constructor for a response. + pub fn response(id: Value, result: Result) -> Self { + Message::Response(Response { jsonrpc: Version, result, id }) + } + /// Returns id or Null if there is no id. + pub fn id(&self) -> Value { + match self { + Message::Request(req) => req.id.clone(), + _ => Value::Null, + } + } +} + +/// A broken message. +/// +/// Protocol-level errors. +#[derive(Debug, Clone, PartialEq, serde::Deserialize)] +#[serde(untagged)] +pub enum Broken { + /// It was valid JSON, but doesn't match the form of a JSONRPC 2.0 message. + Unmatched(Value), + /// Invalid JSON. + #[serde(skip_deserializing)] + SyntaxError(String), +} + +impl Broken { + /// Generate an appropriate error message. + /// + /// The error message for these things are specified in the RFC, so this just creates an error + /// with the right values. + pub fn reply(&self) -> Message { + match *self { + Broken::Unmatched(_) => Message::error(RpcError::parse_error( + "JSON RPC Request format was expected".to_owned(), + )), + Broken::SyntaxError(ref e) => Message::error(RpcError::parse_error(e.clone())), + } + } +} + +/// A trick to easily deserialize and detect valid JSON, but invalid Message. +#[derive(serde::Deserialize)] +#[serde(untagged)] +pub enum WireMessage { + Message(Message), + Broken(Broken), +} + +pub fn decoded_to_parsed(res: JsonResult) -> Parsed { + match res { + Ok(WireMessage::Message(Message::UnmatchedSub(value))) => Err(Broken::Unmatched(value)), + Ok(WireMessage::Message(m)) => Ok(m), + Ok(WireMessage::Broken(b)) => Err(b), + Err(e) => Err(Broken::SyntaxError(e.to_string())), + } +} + +pub type Parsed = Result; + +/// Read a [Message](enum.Message.html) from a slice. +/// +/// Invalid JSON or JSONRPC messages are reported as [Broken](enum.Broken.html). +pub fn from_slice(s: &[u8]) -> Parsed { + decoded_to_parsed(::serde_json::de::from_slice(s)) +} + +/// Read a [Message](enum.Message.html) from a string. +/// +/// Invalid JSON or JSONRPC messages are reported as [Broken](enum.Broken.html). +pub fn from_str(s: &str) -> Parsed { + from_slice(s.as_bytes()) +} + +impl Into for Message { + fn into(self) -> String { + ::serde_json::ser::to_string(&self).unwrap() + } +} + +impl Into> for Message { + fn into(self) -> Vec { + ::serde_json::ser::to_vec(&self).unwrap() + } +} + +#[cfg(test)] +mod tests { + use serde_json::de::from_slice; + use serde_json::json; + use serde_json::ser::to_vec; + use serde_json::Value; + + use super::*; + + /// Test serialization and deserialization of the Message + /// + /// We first deserialize it from a string. That way we check deserialization works. + /// But since serialization doesn't have to produce the exact same result (order, spaces, …), + /// we then serialize and deserialize the thing again and check it matches. + #[test] + fn message_serde() { + // A helper for running one message test + fn one(input: &str, expected: &Message) { + let parsed: Message = from_str(input).unwrap(); + assert_eq!(*expected, parsed); + let serialized = to_vec(&parsed).unwrap(); + let deserialized: Message = from_slice(&serialized).unwrap(); + assert_eq!(parsed, deserialized); + } + + // A request without parameters + one( + r#"{"jsonrpc": "2.0", "method": "call", "id": 1}"#, + &Message::Request(Request { + jsonrpc: Version, + method: "call".to_owned(), + params: Value::Null, + id: json!(1), + }), + ); + // A request with parameters + one( + r#"{"jsonrpc": "2.0", "method": "call", "params": [1, 2, 3], "id": 2}"#, + &Message::Request(Request { + jsonrpc: Version, + method: "call".to_owned(), + params: json!([1, 2, 3]), + id: json!(2), + }), + ); + // A notification (with parameters) + one( + r#"{"jsonrpc": "2.0", "method": "notif", "params": {"x": "y"}}"#, + &Message::Notification(Notification { + jsonrpc: Version, + method: "notif".to_owned(), + params: json!({"x": "y"}), + }), + ); + // A successful response + one( + r#"{"jsonrpc": "2.0", "result": 42, "id": 3}"#, + &Message::Response(Response { jsonrpc: Version, result: Ok(json!(42)), id: json!(3) }), + ); + // A successful response + one( + r#"{"jsonrpc": "2.0", "result": null, "id": 3}"#, + &Message::Response(Response { + jsonrpc: Version, + result: Ok(Value::Null), + id: json!(3), + }), + ); + // An error + one( + r#"{"jsonrpc": "2.0", "error": {"code": 42, "message": "Wrong!"}, "id": null}"#, + &Message::Response(Response { + jsonrpc: Version, + result: Err(RpcError::new(42, "Wrong!".to_owned(), None)), + id: Value::Null, + }), + ); + // A batch + one( + r#"[ + {"jsonrpc": "2.0", "method": "notif"}, + {"jsonrpc": "2.0", "method": "call", "id": 42} + ]"#, + &Message::Batch(vec![ + Message::Notification(Notification { + jsonrpc: Version, + method: "notif".to_owned(), + params: Value::Null, + }), + Message::Request(Request { + jsonrpc: Version, + method: "call".to_owned(), + params: Value::Null, + id: json!(42), + }), + ]), + ); + // Some handling of broken messages inside a batch + let parsed = from_str( + r#"[ + {"jsonrpc": "2.0", "method": "notif"}, + {"jsonrpc": "2.0", "method": "call", "id": 42}, + true + ]"#, + ) + .unwrap(); + assert_eq!( + Message::Batch(vec![ + Message::Notification(Notification { + jsonrpc: Version, + method: "notif".to_owned(), + params: Value::Null, + }), + Message::Request(Request { + jsonrpc: Version, + method: "call".to_owned(), + params: Value::Null, + id: json!(42), + }), + Message::UnmatchedSub(Value::Bool(true)), + ]), + parsed + ); + to_vec(&Message::UnmatchedSub(Value::Null)).unwrap_err(); + } + + /// A helper for the `broken` test. + /// + /// Check that the given JSON string parses, but is not recognized as a valid RPC message. + + /// Test things that are almost but not entirely JSONRPC are rejected + /// + /// The reject is done by returning it as Unmatched. + #[test] + fn broken() { + // A helper with one test + fn one(input: &str) { + let msg = from_str(input); + match msg { + Err(Broken::Unmatched(_)) => (), + _ => panic!("{} recognized as an RPC message: {:?}!", input, msg), + } + } + + // Missing the version + one(r#"{"method": "notif"}"#); + // Wrong version + one(r#"{"jsonrpc": 2.0, "method": "notif"}"#); + // A response with both result and error + one(r#"{"jsonrpc": "2.0", "result": 42, "error": {"code": 42, "message": "!"}, "id": 1}"#); + // A response without an id + one(r#"{"jsonrpc": "2.0", "result": 42}"#); + // An extra field + one(r#"{"jsonrpc": "2.0", "method": "weird", "params": 42, "others": 43, "id": 2}"#); + // Something completely different + one(r#"{"x": [1, 2, 3]}"#); + + match from_str(r#"{]"#) { + Err(Broken::SyntaxError(_)) => (), + other => panic!("Something unexpected: {:?}", other), + }; + } + + /// Test some non-trivial aspects of the constructors + /// + /// This doesn't have a full coverage, because there's not much to actually test there. + /// Most of it is related to the ids. + #[test] + fn constructors() { + let msg1 = Message::request("call".to_owned(), json!([1, 2, 3])); + let msg2 = Message::request("call".to_owned(), json!([1, 2, 3])); + // They differ, even when created with the same parameters + assert_ne!(msg1, msg2); + // And, specifically, they differ in the ID's + let (req1, req2) = if let (Message::Request(req1), Message::Request(req2)) = (msg1, msg2) { + assert_ne!(req1.id, req2.id); + assert!(req1.id.is_string()); + assert!(req2.id.is_string()); + (req1, req2) + } else { + panic!("Non-request received"); + }; + let id1 = req1.id.clone(); + // When we answer a message, we get the same ID + if let Message::Response(ref resp) = req1.reply(json!([1, 2, 3])) { + assert_eq!(*resp, Response { jsonrpc: Version, result: Ok(json!([1, 2, 3])), id: id1 }); + } else { + panic!("Not a response"); + } + let id2 = req2.id.clone(); + // The same with an error + if let Message::Response(ref resp) = + req2.error(RpcError::new(42, "Wrong!".to_owned(), None)) + { + assert_eq!( + *resp, + Response { + jsonrpc: Version, + result: Err(RpcError::new(42, "Wrong!".to_owned(), None)), + id: id2, + } + ); + } else { + panic!("Not a response"); + } + // When we have unmatched, we generate a top-level error with Null id. + if let Message::Response(ref resp) = + Message::error(RpcError::new(43, "Also wrong!".to_owned(), None)) + { + assert_eq!( + *resp, + Response { + jsonrpc: Version, + result: Err(RpcError::new(43, "Also wrong!".to_owned(), None)), + id: Value::Null, + } + ); + } else { + panic!("Not a response"); + } + } +} diff --git a/chain/jsonrpc-primitives/src/types/blocks.rs b/chain/jsonrpc-primitives/src/types/blocks.rs new file mode 100644 index 000000000..930847493 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/blocks.rs @@ -0,0 +1,56 @@ +use serde_json::Value; + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcBlockError { + #[error("Block not found: {error_message}")] + UnknownBlock { + // We are skipping this field for now + // until we can provide useful struct like block_height or block_hash + // that was requested + #[serde(skip_serializing)] + error_message: String, + }, + #[error("There are no fully synchronized blocks yet")] + NotSyncedYet, + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize, arbitrary::Arbitrary)] +pub struct RpcBlockRequest { + #[serde(flatten)] + pub block_reference: unc_primitives::types::BlockReference, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct RpcBlockResponse { + #[serde(flatten)] + pub block_view: unc_primitives::views::BlockView, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcBlockError) -> Self { + let error_data = match &error { + RpcBlockError::UnknownBlock { error_message } => Some(Value::String(format!( + "DB Not Found Error: {} \n Cause: Unknown", + error_message + ))), + RpcBlockError::NotSyncedYet | RpcBlockError::InternalError { .. } => { + Some(Value::String(error.to_string())) + } + }; + + let error_data_value = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcBlockError: {:?}", err), + ) + } + }; + + Self::new_internal_or_handler_error(error_data, error_data_value) + } +} diff --git a/chain/jsonrpc-primitives/src/types/changes.rs b/chain/jsonrpc-primitives/src/types/changes.rs new file mode 100644 index 000000000..6e70fdaae --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/changes.rs @@ -0,0 +1,54 @@ +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct RpcStateChangesInBlockRequest { + #[serde(flatten)] + pub block_reference: unc_primitives::types::BlockReference, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct RpcStateChangesInBlockResponse { + pub block_hash: unc_primitives::hash::CryptoHash, + pub changes: unc_primitives::views::StateChangesView, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct RpcStateChangesInBlockByTypeRequest { + #[serde(flatten)] + pub block_reference: unc_primitives::types::BlockReference, + #[serde(flatten)] + pub state_changes_request: unc_primitives::views::StateChangesRequestView, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct RpcStateChangesInBlockByTypeResponse { + pub block_hash: unc_primitives::hash::CryptoHash, + pub changes: unc_primitives::views::StateChangesKindsView, +} + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcStateChangesError { + #[error("Block not found: {error_message}")] + UnknownBlock { + #[serde(skip_serializing)] + error_message: String, + }, + #[error("There are no fully synchronized blocks yet")] + NotSyncedYet, + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcStateChangesError) -> Self { + let error_data = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcStateChangesError: {:?}", err), + ) + } + }; + Self::new_internal_or_handler_error(Some(error_data.clone()), error_data) + } +} diff --git a/chain/jsonrpc-primitives/src/types/chunks.rs b/chain/jsonrpc-primitives/src/types/chunks.rs new file mode 100644 index 000000000..9af191241 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/chunks.rs @@ -0,0 +1,70 @@ +use serde_json::Value; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, arbitrary::Arbitrary)] +#[serde(untagged)] +pub enum ChunkReference { + BlockShardId { + block_id: unc_primitives::types::BlockId, + shard_id: unc_primitives::types::ShardId, + }, + ChunkHash { + chunk_id: unc_primitives::hash::CryptoHash, + }, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, arbitrary::Arbitrary)] +pub struct RpcChunkRequest { + #[serde(flatten)] + pub chunk_reference: ChunkReference, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcChunkResponse { + #[serde(flatten)] + pub chunk_view: unc_primitives::views::ChunkView, +} + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcChunkError { + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { + #[serde(skip_serializing)] + error_message: String, + }, + #[error("Shard id {shard_id} does not exist")] + InvalidShardId { shard_id: u64 }, + #[error("Chunk with hash {chunk_hash:?} has never been observed on this node")] + UnknownChunk { chunk_hash: unc_primitives::sharding::ChunkHash }, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcChunkError) -> Self { + let error_data = match &error { + RpcChunkError::InternalError { .. } => Some(Value::String(error.to_string())), + RpcChunkError::UnknownBlock { error_message } => Some(Value::String(format!( + "DB Not Found Error: {} \n Cause: Unknown", + error_message + ))), + RpcChunkError::InvalidShardId { .. } => Some(Value::String(error.to_string())), + RpcChunkError::UnknownChunk { chunk_hash } => Some(Value::String(format!( + "Chunk Missing (unavailable on the node): ChunkHash(`{}`) \n Cause: Unknown", + chunk_hash.0 + ))), + }; + + let error_data_value = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcStateChangesError: {:?}", err), + ) + } + }; + + Self::new_internal_or_handler_error(error_data, error_data_value) + } +} diff --git a/chain/jsonrpc-primitives/src/types/client_config.rs b/chain/jsonrpc-primitives/src/types/client_config.rs new file mode 100644 index 000000000..9f8197d6b --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/client_config.rs @@ -0,0 +1,35 @@ +use serde::Serialize; +use serde_json::Value; + +#[derive(Serialize)] +pub struct RpcClientConfigResponse { + #[serde(flatten)] + pub client_config: unc_chain_configs::ClientConfig, +} + +#[derive(thiserror::Error, Debug, Serialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcClientConfigError { + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcClientConfigError) -> Self { + let error_data = match &error { + RpcClientConfigError::InternalError { .. } => Some(Value::String(error.to_string())), + }; + + let error_data_value = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcClientConfigError: {:?}", err), + ) + } + }; + + Self::new_internal_or_handler_error(error_data, error_data_value) + } +} diff --git a/chain/jsonrpc-primitives/src/types/config.rs b/chain/jsonrpc-primitives/src/types/config.rs new file mode 100644 index 000000000..f3d5584b4 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/config.rs @@ -0,0 +1,48 @@ +use serde_json::Value; + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcProtocolConfigRequest { + #[serde(flatten)] + pub block_reference: unc_primitives::types::BlockReference, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcProtocolConfigResponse { + #[serde(flatten)] + pub config_view: unc_chain_configs::ProtocolConfigView, +} + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcProtocolConfigError { + #[error("Block has never been observed: {error_message}")] + UnknownBlock { + #[serde(skip_serializing)] + error_message: String, + }, + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcProtocolConfigError) -> Self { + let error_data = match &error { + RpcProtocolConfigError::UnknownBlock { error_message } => { + Some(Value::String(format!("Block Not Found: {}", error_message))) + } + RpcProtocolConfigError::InternalError { .. } => Some(Value::String(error.to_string())), + }; + + let error_data_value = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcProtocolConfigError: {:?}", err), + ) + } + }; + + Self::new_internal_or_handler_error(error_data, error_data_value) + } +} diff --git a/chain/jsonrpc-primitives/src/types/entity_debug.rs b/chain/jsonrpc-primitives/src/types/entity_debug.rs new file mode 100644 index 000000000..c04bcba6b --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/entity_debug.rs @@ -0,0 +1,92 @@ +// This file contains structures for the Entity Debug UI. +// They are to be sent to the UI as JSON. +use crate::errors::RpcError; +use unc_primitives::types::{BlockHeight, EpochId, ShardId}; +use unc_primitives::{hash::CryptoHash, shard_layout::ShardUId}; +use serde::{Deserialize, Serialize}; + +/// One entry to be displayed in the UI as a single row. +#[derive(Serialize, Debug, PartialEq, Eq)] +pub struct EntityDataEntry { + /// Can be a struct field name or a stringified array index. + pub name: String, + pub value: EntityDataValue, +} + +/// Represents either a single value, or a struct. An array is also considered +/// a struct, with keys being array indices. All value types are represented as +/// strings even if they are numerical, for simplicity. +#[derive(Serialize, Debug, PartialEq, Eq)] +#[serde(untagged)] +pub enum EntityDataValue { + String(String), + Struct(Box), +} + +/// A list of entries - either a struct or an array. +#[derive(Serialize, Debug, PartialEq, Eq)] +pub struct EntityDataStruct { + pub entries: Vec, +} + +impl EntityDataStruct { + pub fn new() -> EntityDataStruct { + EntityDataStruct { entries: Vec::new() } + } +} + +/// All queries supported by the Entity Debug UI. +/// To add a new query, make a new enum variant. The only constraints are: +/// - The variant must either be (()), or a struct variant whose field names +/// correspond to some EntityKeyType (in the UI code). +/// - Across all queries, each unique entity key name must have the same type, +/// e.g. "epoch_id" must always have the same type, in this case 'EpochId'. +/// +/// Queries in general can return anything. On the UI side we annotate on the +/// returned structure to provide links for further queries. For example, on the +/// UI side we annotate that TipAtHead returns a structure whose prev_block_hash +/// corresponds to a block_hash entity key, which can then be used to query for +/// e.g. BlockHeaderByHash. +#[derive(Serialize, Deserialize)] +pub enum EntityQuery { + AllShardsByEpochId { epoch_id: EpochId }, + BlockByHash { block_hash: CryptoHash }, + BlockHashByHeight { block_height: BlockHeight }, + BlockHeaderByHash { block_hash: CryptoHash }, + ChunkByHash { chunk_hash: CryptoHash }, + EpochInfoByEpochId { epoch_id: EpochId }, + FlatStateByTrieKey { trie_key: String, shard_uid: ShardUId }, + FlatStateChangesByBlockHash { block_hash: CryptoHash, shard_uid: ShardUId }, + FlatStateDeltaMetadataByBlockHash { block_hash: CryptoHash, shard_uid: ShardUId }, + FlatStorageStatusByShardUId { shard_uid: ShardUId }, + OutcomeByReceiptId { receipt_id: CryptoHash }, + OutcomeByReceiptIdAndBlockHash { receipt_id: CryptoHash, block_hash: CryptoHash }, + OutcomeByTransactionHash { transaction_hash: CryptoHash }, + OutcomeByTransactionHashAndBlockHash { transaction_hash: CryptoHash, block_hash: CryptoHash }, + ReceiptById { receipt_id: CryptoHash }, + ShardIdByAccountId { account_id: String, epoch_id: EpochId }, + ShardLayoutByEpochId { epoch_id: EpochId }, + ShardUIdByShardId { shard_id: ShardId, epoch_id: EpochId }, + TipAtFinalHead(()), + TipAtHead(()), + TipAtHeaderHead(()), + TransactionByHash { transaction_hash: CryptoHash }, + TrieNode { trie_path: String }, + TrieRootByChunkHash { chunk_hash: CryptoHash }, + TrieRootByStateRoot { state_root: CryptoHash, shard_uid: ShardUId }, +} + +/// We use a trait for this, because jsonrpc does not have access to low-level +/// blockchain data structures for implementing the queries. +pub trait EntityDebugHandler: Sync + Send { + fn query(&self, query: EntityQuery) -> Result; +} + +/// For tests. +pub struct DummyEntityDebugHandler {} + +impl EntityDebugHandler for DummyEntityDebugHandler { + fn query(&self, _query: EntityQuery) -> Result { + Err(RpcError::new_internal_error(None, "Not implemented".to_string())) + } +} diff --git a/chain/jsonrpc-primitives/src/types/gas_price.rs b/chain/jsonrpc-primitives/src/types/gas_price.rs new file mode 100644 index 000000000..f306ddf02 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/gas_price.rs @@ -0,0 +1,49 @@ +use unc_primitives::types::MaybeBlockId; +use serde_json::Value; + +#[derive(serde::Serialize, serde::Deserialize, Debug, arbitrary::Arbitrary)] +pub struct RpcGasPriceRequest { + pub block_id: MaybeBlockId, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcGasPriceResponse { + #[serde(flatten)] + pub gas_price_view: unc_primitives::views::GasPriceView, +} + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcGasPriceError { + #[error("Internal error: {error_message}")] + InternalError { error_message: String }, + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { + #[serde(skip_serializing)] + error_message: String, + }, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcGasPriceError) -> Self { + let error_data = match &error { + RpcGasPriceError::UnknownBlock { error_message } => Some(Value::String(format!( + "DB Not Found Error: {} \n Cause: Unknown", + error_message + ))), + RpcGasPriceError::InternalError { .. } => Some(Value::String(error.to_string())), + }; + + let error_data_value = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcGasPriceError: {:?}", err), + ) + } + }; + + Self::new_internal_or_handler_error(error_data, error_data_value) + } +} diff --git a/chain/jsonrpc-primitives/src/types/light_client.rs b/chain/jsonrpc-primitives/src/types/light_client.rs new file mode 100644 index 000000000..7a5bc5197 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/light_client.rs @@ -0,0 +1,106 @@ +use serde_json::Value; +use std::sync::Arc; + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct RpcLightClientExecutionProofRequest { + #[serde(flatten)] + pub id: unc_primitives::types::TransactionOrReceiptId, + pub light_client_head: unc_primitives::hash::CryptoHash, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct RpcLightClientNextBlockRequest { + pub last_block_hash: unc_primitives::hash::CryptoHash, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct RpcLightClientExecutionProofResponse { + pub outcome_proof: unc_primitives::views::ExecutionOutcomeWithIdView, + pub outcome_root_proof: unc_primitives::merkle::MerklePath, + pub block_header_lite: unc_primitives::views::LightClientBlockLiteView, + pub block_proof: unc_primitives::merkle::MerklePath, +} + +#[derive(Debug, serde::Serialize)] +pub struct RpcLightClientNextBlockResponse { + #[serde(flatten)] + pub light_client_block: Option>, +} + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcLightClientProofError { + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { + #[serde(skip_serializing)] + error_message: String, + }, + #[error("Inconsistent state. Total number of shards is {number_or_shards} but the execution outcome is in shard {execution_outcome_shard_id}")] + InconsistentState { + number_or_shards: usize, + execution_outcome_shard_id: unc_primitives::types::ShardId, + }, + #[error("{transaction_or_receipt_id} has not been confirmed")] + NotConfirmed { transaction_or_receipt_id: unc_primitives::hash::CryptoHash }, + #[error("{transaction_or_receipt_id} does not exist")] + UnknownTransactionOrReceipt { transaction_or_receipt_id: unc_primitives::hash::CryptoHash }, + #[error("Node doesn't track the shard where {transaction_or_receipt_id} is executed")] + UnavailableShard { + transaction_or_receipt_id: unc_primitives::hash::CryptoHash, + shard_id: unc_primitives::types::ShardId, + }, + #[error("Internal error: {error_message}")] + InternalError { error_message: String }, +} + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcLightClientNextBlockError { + #[error("Internal error: {error_message}")] + InternalError { error_message: String }, + #[error("Block either has never been observed on the node or has been garbage collected: {error_message}")] + UnknownBlock { + #[serde(skip_serializing)] + error_message: String, + }, + #[error("Epoch Out Of Bounds {epoch_id:?}")] + EpochOutOfBounds { epoch_id: unc_primitives::types::EpochId }, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcLightClientProofError) -> Self { + let error_data = match &error { + RpcLightClientProofError::UnknownBlock { error_message } => { + Some(Value::String(format!("DB Not Found Error: {}", error_message))) + } + _ => Some(Value::String(error.to_string())), + }; + + let error_data_value = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcLightClientProofError: {:?}", err), + ) + } + }; + + Self::new_internal_or_handler_error(error_data, error_data_value) + } +} + +impl From for crate::errors::RpcError { + fn from(error: RpcLightClientNextBlockError) -> Self { + let error_data = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcLightClientNextBlockError: {:?}", err), + ) + } + }; + Self::new_internal_or_handler_error(Some(error_data.clone()), error_data) + } +} diff --git a/chain/jsonrpc-primitives/src/types/maintenance.rs b/chain/jsonrpc-primitives/src/types/maintenance.rs new file mode 100644 index 000000000..60f72af10 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/maintenance.rs @@ -0,0 +1,38 @@ +use serde_json::Value; + +pub type RpcMaintenanceWindowsResponse = + Vec<(unc_primitives::types::BlockHeight, unc_primitives::types::BlockHeight)>; + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcMaintenanceWindowsError { + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcMaintenanceWindowsRequest { + pub account_id: unc_primitives::types::AccountId, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcMaintenanceWindowsError) -> Self { + let error_data = match &error { + RpcMaintenanceWindowsError::InternalError { .. } => { + Some(Value::String(error.to_string())) + } + }; + + let error_data_value = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcMaintenanceError: {:?}", err), + ) + } + }; + + Self::new_internal_or_handler_error(error_data, error_data_value) + } +} diff --git a/chain/jsonrpc-primitives/src/types/miner_chips_list.rs b/chain/jsonrpc-primitives/src/types/miner_chips_list.rs new file mode 100644 index 000000000..0e8d9f6d6 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/miner_chips_list.rs @@ -0,0 +1,49 @@ +use serde_json::Value; +use unc_primitives::types::{AccountId}; +use unc_primitives::views::ChipView; + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcMinerChipsListError { + #[error("Account not found")] + UnknownAccount { error_message: String }, + #[error("Chips info unavailable")] + ChipsInfoUnavailable, + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct RpcMinerChipsListRequest { + pub account_id: AccountId, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcMinerChipsListResponse { + pub account_id: AccountId, + pub chips: Vec, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcMinerChipsListError) -> Self { + let error_data = match &error { + RpcMinerChipsListError::UnknownAccount{ error_message } => Some(Value::String(error_message.to_string())), + RpcMinerChipsListError::ChipsInfoUnavailable => { + Some(Value::String("Chips info unavailable".to_string())) + } + RpcMinerChipsListError::InternalError { .. } => Some(Value::String(error.to_string())), + }; + + let error_data_value = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcValidatorError: {:?}", err), + ) + } + }; + + Self::new_internal_or_handler_error(error_data, error_data_value) + } +} diff --git a/chain/jsonrpc-primitives/src/types/mod.rs b/chain/jsonrpc-primitives/src/types/mod.rs new file mode 100644 index 000000000..cc5543960 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/mod.rs @@ -0,0 +1,19 @@ +pub mod blocks; +pub mod changes; +pub mod chunks; +pub mod client_config; +pub mod config; +pub mod entity_debug; +pub mod gas_price; +pub mod light_client; +pub mod maintenance; +pub mod network_info; +pub mod query; +pub mod receipts; +pub mod sandbox; +pub mod split_storage; +pub mod status; +pub mod transactions; +pub mod validator; +pub mod provider; +pub mod miner_chips_list; diff --git a/chain/jsonrpc-primitives/src/types/network_info.rs b/chain/jsonrpc-primitives/src/types/network_info.rs new file mode 100644 index 000000000..0ef3ad056 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/network_info.rs @@ -0,0 +1,50 @@ +use unc_primitives::network::PeerId; +use unc_primitives::types::AccountId; +use std::net::SocketAddr; + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcPeerInfo { + pub id: PeerId, + pub addr: Option, + pub account_id: Option, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcKnownProducer { + pub account_id: AccountId, + pub addr: Option, + pub peer_id: PeerId, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcNetworkInfoResponse { + pub active_peers: Vec, + pub num_active_peers: usize, + pub peer_max_count: u32, + pub sent_bytes_per_sec: u64, + pub received_bytes_per_sec: u64, + /// Accounts of known block and chunk producers from routing table. + pub known_producers: Vec, +} + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcNetworkInfoError { + #[error("Internal error: {error_message}")] + InternalError { error_message: String }, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcNetworkInfoError) -> Self { + let error_data = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcNetworkInfoError: {:?}", err), + ) + } + }; + Self::new_internal_or_handler_error(Some(error_data.clone()), error_data) + } +} diff --git a/chain/jsonrpc-primitives/src/types/provider.rs b/chain/jsonrpc-primitives/src/types/provider.rs new file mode 100644 index 000000000..c7fa3a1ac --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/provider.rs @@ -0,0 +1,48 @@ +use serde_json::Value; +use unc_primitives::types::{AccountId, BlockHeight, EpochId}; + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcProviderError { + #[error("Block not found")] + UnknownBlock, + #[error("Validator info unavailable")] + ProviderInfoUnavailable, + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, arbitrary::Arbitrary, PartialEq, Eq)] +pub struct RpcProviderRequest { + pub epoch_id: EpochId, + pub block_height: BlockHeight, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcProviderResponse { + pub provider_account: AccountId, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcProviderError) -> Self { + let error_data = match &error { + RpcProviderError::UnknownBlock => Some(Value::String("Unknown Block".to_string())), + RpcProviderError::ProviderInfoUnavailable => { + Some(Value::String("Validator info unavailable".to_string())) + } + RpcProviderError::InternalError { .. } => Some(Value::String(error.to_string())), + }; + + let error_data_value = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcValidatorError: {:?}", err), + ) + } + }; + + Self::new_internal_or_handler_error(error_data, error_data_value) + } +} diff --git a/chain/jsonrpc-primitives/src/types/query.rs b/chain/jsonrpc-primitives/src/types/query.rs new file mode 100644 index 000000000..5b67cd2a6 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/query.rs @@ -0,0 +1,100 @@ +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcQueryRequest { + #[serde(flatten)] + pub block_reference: unc_primitives::types::BlockReference, + #[serde(flatten)] + pub request: unc_primitives::views::QueryRequest, +} + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcQueryError { + #[error("There are no fully synchronized blocks on the node yet")] + NoSyncedBlocks, + #[error("The node does not track the shard ID {requested_shard_id}")] + UnavailableShard { requested_shard_id: unc_primitives::types::ShardId }, + #[error( + "The data for block #{block_height} is garbage collected on this node, use an archival node to fetch historical data" + )] + GarbageCollectedBlock { + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("Block either has never been observed on the node or has been garbage collected: {block_reference:?}")] + UnknownBlock { block_reference: unc_primitives::types::BlockReference }, + #[error("Account ID {requested_account_id} is invalid")] + InvalidAccount { + requested_account_id: unc_primitives::types::AccountId, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("account {requested_account_id} does not exist while viewing")] + UnknownAccount { + requested_account_id: unc_primitives::types::AccountId, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error( + "Contract code for contract ID #{contract_account_id} has never been observed on the node" + )] + NoContractCode { + contract_account_id: unc_primitives::types::AccountId, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("State of contract {contract_account_id} is too large to be viewed")] + TooLargeContractState { + contract_account_id: unc_primitives::types::AccountId, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("Access key for public key {public_key} has never been observed on the node")] + UnknownAccessKey { + public_key: unc_crypto::PublicKey, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("Function call returned an error: {vm_error}")] + ContractExecutionError { + vm_error: String, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + }, + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcQueryResponse { + #[serde(flatten)] + pub kind: QueryResponseKind, + pub block_height: unc_primitives::types::BlockHeight, + pub block_hash: unc_primitives::hash::CryptoHash, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +#[serde(untagged)] +pub enum QueryResponseKind { + ViewAccount(unc_primitives::views::AccountView), + ViewCode(unc_primitives::views::ContractCodeView), + ViewState(unc_primitives::views::ViewStateResult), + CallResult(unc_primitives::views::CallResult), + AccessKey(unc_primitives::views::AccessKeyView), + AccessKeyList(unc_primitives::views::AccessKeyList), +} + +impl From for crate::errors::RpcError { + fn from(error: RpcQueryError) -> Self { + let error_data = Some(serde_json::Value::String(error.to_string())); + let error_data_value = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcQueryError: {:?}", err), + ) + } + }; + Self::new_internal_or_handler_error(error_data, error_data_value) + } +} diff --git a/chain/jsonrpc-primitives/src/types/receipts.rs b/chain/jsonrpc-primitives/src/types/receipts.rs new file mode 100644 index 000000000..27b421b6c --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/receipts.rs @@ -0,0 +1,40 @@ +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct ReceiptReference { + pub receipt_id: unc_primitives::hash::CryptoHash, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcReceiptRequest { + #[serde(flatten)] + pub receipt_reference: ReceiptReference, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcReceiptResponse { + #[serde(flatten)] + pub receipt_view: unc_primitives::views::ReceiptView, +} + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcReceiptError { + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, + #[error("Receipt with id {receipt_id} has never been observed on this node")] + UnknownReceipt { receipt_id: unc_primitives::hash::CryptoHash }, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcReceiptError) -> Self { + let error_data = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcReceiptError: {:?}", err), + ) + } + }; + Self::new_internal_or_handler_error(Some(error_data.clone()), error_data) + } +} diff --git a/chain/jsonrpc-primitives/src/types/sandbox.rs b/chain/jsonrpc-primitives/src/types/sandbox.rs new file mode 100644 index 000000000..7e2ad8265 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/sandbox.rs @@ -0,0 +1,62 @@ +use unc_primitives::state_record::StateRecord; +use unc_primitives::types::BlockHeightDelta; + +#[derive(serde::Deserialize, serde::Serialize, Debug)] +pub struct RpcSandboxPatchStateRequest { + pub records: Vec, +} + +#[derive(serde::Deserialize, serde::Serialize, Debug)] +pub struct RpcSandboxPatchStateResponse {} + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcSandboxPatchStateError { + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcSandboxPatchStateError) -> Self { + let error_data = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcSandboxPatchStateError: {:?}", err), + ) + } + }; + Self::new_internal_or_handler_error(Some(error_data.clone()), error_data) + } +} + +#[derive(serde::Deserialize, serde::Serialize)] +pub struct RpcSandboxFastForwardRequest { + pub delta_height: BlockHeightDelta, +} + +#[derive(serde::Deserialize, serde::Serialize)] +pub struct RpcSandboxFastForwardResponse {} + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcSandboxFastForwardError { + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcSandboxFastForwardError) -> Self { + let error_data = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcSandboxFastForwardError: {:?}", err), + ) + } + }; + Self::new_internal_or_handler_error(Some(error_data.clone()), error_data) + } +} diff --git a/chain/jsonrpc-primitives/src/types/split_storage.rs b/chain/jsonrpc-primitives/src/types/split_storage.rs new file mode 100644 index 000000000..5d83b3d8a --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/split_storage.rs @@ -0,0 +1,56 @@ +use unc_primitives::views::SplitStorageInfoView; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +use crate::types::status::RpcStatusError; + +#[derive(Serialize, Deserialize, Debug)] +pub struct RpcSplitStorageInfoRequest {} + +#[derive(Serialize, Deserialize, Debug)] +pub struct RpcSplitStorageInfoResponse { + #[serde(flatten)] + pub result: SplitStorageInfoView, +} + +#[derive(thiserror::Error, Debug, Serialize, Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcSplitStorageInfoError { + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcSplitStorageInfoError) -> Self { + let error_data = match &error { + RpcSplitStorageInfoError::InternalError { .. } => { + Some(Value::String(error.to_string())) + } + }; + + let error_data_value = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcSplitStorageInfoError: {:?}", err), + ) + } + }; + + Self::new_internal_or_handler_error(error_data, error_data_value) + } +} + +impl RpcSplitStorageInfoError { + // Implementing From for RpcStatusError causes cargo to spit out hundreds + // of lines of compilation errors. I don't want to spend time debugging this, so let's use this function instead. + // It's good enough. + pub fn into_rpc_status_error(self) -> RpcStatusError { + match self { + RpcSplitStorageInfoError::InternalError { error_message } => { + RpcStatusError::InternalError { error_message } + } + } + } +} diff --git a/chain/jsonrpc-primitives/src/types/status.rs b/chain/jsonrpc-primitives/src/types/status.rs new file mode 100644 index 000000000..0496bf79e --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/status.rs @@ -0,0 +1,76 @@ +#[cfg(feature = "debug_types")] +use unc_client_primitives::debug::{ + DebugBlockStatusData, EpochInfoView, TrackedShardsView, ValidatorStatus, +}; +#[cfg(feature = "debug_types")] +use unc_primitives::views::{ + CatchupStatusView, ChainProcessingInfo, NetworkGraphView, NetworkRoutesView, PeerStoreView, + RecentOutboundConnectionsView, RequestedStatePartsView, SnapshotHostsView, + SplitStorageInfoView, SyncStatusView, +}; + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct RpcStatusResponse { + #[serde(flatten)] + pub status_response: unc_primitives::views::StatusResponse, +} + +#[cfg(feature = "debug_types")] +#[derive(serde::Serialize, Debug)] +pub enum DebugStatusResponse { + SyncStatus(SyncStatusView), + CatchupStatus(Vec), + TrackedShards(TrackedShardsView), + // List of epochs - in descending order (next epoch is first). + EpochInfo(Vec), + // Detailed information about blocks. + BlockStatus(DebugBlockStatusData), + // Detailed information about the validator (approvals, block & chunk production etc.) + ValidatorStatus(ValidatorStatus), + PeerStore(PeerStoreView), + ChainProcessingStatus(ChainProcessingInfo), + // The state parts already requested. + RequestedStateParts(Vec), + NetworkGraph(NetworkGraphView), + RecentOutboundConnections(RecentOutboundConnectionsView), + Routes(NetworkRoutesView), + SnapshotHosts(SnapshotHostsView), + SplitStoreStatus(SplitStorageInfoView), +} + +#[cfg(feature = "debug_types")] +#[derive(Debug, serde::Serialize)] +pub struct RpcDebugStatusResponse { + pub status_response: DebugStatusResponse, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct RpcHealthResponse; + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcStatusError { + #[error("Node is syncing")] + NodeIsSyncing, + #[error("No blocks for {elapsed:?}")] + NoNewBlocks { elapsed: std::time::Duration }, + #[error("Epoch Out Of Bounds {epoch_id:?}")] + EpochOutOfBounds { epoch_id: unc_primitives::types::EpochId }, + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcStatusError) -> Self { + let error_data = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcStateChangesError: {:?}", err), + ) + } + }; + Self::new_internal_or_handler_error(Some(error_data.clone()), error_data) + } +} diff --git a/chain/jsonrpc-primitives/src/types/transactions.rs b/chain/jsonrpc-primitives/src/types/transactions.rs new file mode 100644 index 000000000..1f51d2de3 --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/transactions.rs @@ -0,0 +1,138 @@ +use unc_primitives::hash::CryptoHash; +use unc_primitives::types::AccountId; +use serde_json::Value; + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct RpcSendTransactionRequest { + #[serde(rename = "signed_tx_base64")] + pub signed_transaction: unc_primitives::transaction::SignedTransaction, + #[serde(default)] + pub wait_until: unc_primitives::views::TxExecutionStatus, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct RpcTransactionStatusRequest { + #[serde(flatten)] + pub transaction_info: TransactionInfo, + #[serde(default)] + pub wait_until: unc_primitives::views::TxExecutionStatus, +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum TransactionInfo { + Transaction(SignedTransaction), + TransactionId { tx_hash: CryptoHash, sender_account_id: AccountId }, +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub enum SignedTransaction { + #[serde(rename = "signed_tx_base64")] + SignedTransaction(unc_primitives::transaction::SignedTransaction), +} + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize, Clone)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcTransactionError { + #[error("An error happened during transaction execution: {context:?}")] + InvalidTransaction { + #[serde(skip_serializing)] + context: unc_primitives::errors::InvalidTxError, + }, + #[error("Node doesn't track this shard. Cannot determine whether the transaction is valid")] + DoesNotTrackShard, + #[error("Transaction with hash {transaction_hash} was routed")] + RequestRouted { transaction_hash: unc_primitives::hash::CryptoHash }, + #[error("Transaction {requested_transaction_hash} doesn't exist")] + UnknownTransaction { requested_transaction_hash: unc_primitives::hash::CryptoHash }, + #[error("The node reached its limits. Try again later. More details: {debug_info}")] + InternalError { debug_info: String }, + #[error("Timeout")] + TimeoutError, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcTransactionResponse { + #[serde(flatten)] + pub final_execution_outcome: Option, + pub final_execution_status: unc_primitives::views::TxExecutionStatus, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcBroadcastTxSyncResponse { + pub transaction_hash: unc_primitives::hash::CryptoHash, +} + +impl TransactionInfo { + pub fn from_signed_tx(tx: unc_primitives::transaction::SignedTransaction) -> Self { + Self::Transaction(SignedTransaction::SignedTransaction(tx)) + } + + pub fn to_signed_tx(&self) -> Option<&unc_primitives::transaction::SignedTransaction> { + match self { + TransactionInfo::Transaction(tx) => match tx { + SignedTransaction::SignedTransaction(tx) => Some(tx), + }, + TransactionInfo::TransactionId { .. } => None, + } + } + + pub fn to_tx_hash_and_account(&self) -> (CryptoHash, &AccountId) { + match self { + TransactionInfo::Transaction(tx) => match tx { + SignedTransaction::SignedTransaction(tx) => { + (tx.get_hash(), &tx.transaction.signer_id) + } + }, + TransactionInfo::TransactionId { tx_hash, sender_account_id } => { + (*tx_hash, sender_account_id) + } + } + } +} + +impl From for TransactionInfo { + fn from(transaction_info: unc_primitives::transaction::SignedTransaction) -> Self { + Self::Transaction(SignedTransaction::SignedTransaction(transaction_info)) + } +} + +impl From for RpcTransactionResponse { + fn from(view: unc_primitives::views::TxStatusView) -> Self { + Self { + final_execution_outcome: view.execution_outcome, + final_execution_status: view.status, + } + } +} + +impl From for crate::errors::RpcError { + fn from(error: RpcTransactionError) -> Self { + let error_data = match &error { + RpcTransactionError::InvalidTransaction { context } => { + if let Ok(value) = + serde_json::to_value(crate::errors::ServerError::TxExecutionError( + unc_primitives::errors::TxExecutionError::InvalidTxError(context.clone()), + )) + { + value + } else { + Value::String(error.to_string()) + } + } + _ => Value::String(error.to_string()), + }; + + let error_data_value = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcTransactionError: {:?}", err), + ) + } + }; + + Self::new_internal_or_handler_error(Some(error_data), error_data_value) + } +} diff --git a/chain/jsonrpc-primitives/src/types/validator.rs b/chain/jsonrpc-primitives/src/types/validator.rs new file mode 100644 index 000000000..4ee8dc78f --- /dev/null +++ b/chain/jsonrpc-primitives/src/types/validator.rs @@ -0,0 +1,56 @@ +use serde_json::Value; + +pub type RpcValidatorsOrderedResponse = + Vec; + +#[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "name", content = "info", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum RpcValidatorError { + #[error("Epoch not found")] + UnknownEpoch, + #[error("Validator info unavailable")] + ValidatorInfoUnavailable, + #[error("The node reached its limits. Try again later. More details: {error_message}")] + InternalError { error_message: String }, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, arbitrary::Arbitrary, PartialEq, Eq)] +pub struct RpcValidatorRequest { + #[serde(flatten)] + pub epoch_reference: unc_primitives::types::EpochReference, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcValidatorsOrderedRequest { + pub block_id: unc_primitives::types::MaybeBlockId, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct RpcValidatorResponse { + #[serde(flatten)] + pub validator_info: unc_primitives::views::EpochValidatorInfo, +} + +impl From for crate::errors::RpcError { + fn from(error: RpcValidatorError) -> Self { + let error_data = match &error { + RpcValidatorError::UnknownEpoch => Some(Value::String("Unknown Epoch".to_string())), + RpcValidatorError::ValidatorInfoUnavailable => { + Some(Value::String("Validator info unavailable".to_string())) + } + RpcValidatorError::InternalError { .. } => Some(Value::String(error.to_string())), + }; + + let error_data_value = match serde_json::to_value(error) { + Ok(value) => value, + Err(err) => { + return Self::new_internal_error( + None, + format!("Failed to serialize RpcValidatorError: {:?}", err), + ) + } + }; + + Self::new_internal_or_handler_error(error_data, error_data_value) + } +} diff --git a/chain/jsonrpc/CHANGELOG.md b/chain/jsonrpc/CHANGELOG.md new file mode 100644 index 000000000..c7d855b88 --- /dev/null +++ b/chain/jsonrpc/CHANGELOG.md @@ -0,0 +1,53 @@ +# Changelog + +## 0.2.3 + +* Added `send_tx` method which gives configurable execution guarantees options and potentially replaces existing `broadcast_tx_async`, `broadcast_tx_commit` +* Field `final_execution_status` is presented in the response of methods `tx`, `EXPERIMENTAL_tx_status`, `broadcast_tx_commit`, `send_tx` +* Allowed use json in request for methods `EXPERIMENTAL_tx_status`, `tx`, `broadcast_tx_commit`, `broadcast_tx_async`, `send_tx` +* Parameter `wait_until` (same entity as `final_execution_status` in the response) is presented as optional request parameter for methods `EXPERIMENTAL_tx_status`, `tx`, `send_tx`. The response will be returned only when the desired level of finality is reached + +### Breaking changes + +* Removed `EXPERIMENTAL_check_tx` method. Use `tx` method instead +* Removed `EXPERIMENTAL_broadcast_tx_sync` method. Use `send_tx` method instead +* `EXPERIMENTAL_tx_status`, `tx` methods now wait for recently sent tx (~3-6 seconds) and then show it. Previously, `UnknownTransaction` was immediately returned +* `EXPERIMENTAL_tx_status`, `tx` methods wait 10 seconds and then return `TimeoutError` for never existed transactions. Previously, `UnknownTransaction` was immediately returned + +## 0.2.2 + +* Extended error structures to be more explicit. See [#2976 decision comment for reference](https://github.com/utnet-org/utility/issues/2976#issuecomment-865834617) + +## 0.2.1 + +* Refactored methods: + * `broadcast_tx_async` + * `broadcast_tx_commit` + * `EXPERIMENTAL_broadcast_tx_sync` + * `EXPERIMENTAL_check_tx` + * `EXPERIMENTAL_tx_status` + * `tx` + +## Breaking changes + +Response from `EXPERIMENTAL_broadcast_tx_sync` and `EXPERIMENTAL_check_tx` doesn't return `is_routed` +field anymore. In case of a transaction getting routed an error will be returned. Also, `EXPERIMENTAL_check_tx` +returns response with transaction hash instead of empty body. + +* Added `EXPERIMENTAL_tx_status` endpoint exposing receipts in addition to all + the rest data available in `tx` endpoint + ([#3383](https://github.com/utnet-org/utility/pull/3383)) + +## 0.2.0 + +* Started tracking all the JSON-RPC API changes. + +### Breaking changes + +* Removed `EXPERIMENTAL_genesis_records` API to shrink the memory footprint on + the node since we don't need genesis records (potentially gigabytes of data) + for operation. + +## 0.1.0 + +* Start the versioning timeline here diff --git a/chain/jsonrpc/Cargo.toml b/chain/jsonrpc/Cargo.toml new file mode 100644 index 000000000..036363149 --- /dev/null +++ b/chain/jsonrpc/Cargo.toml @@ -0,0 +1,74 @@ +[package] +name = "unc-jsonrpc" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix-cors.workspace = true +actix-web.workspace = true +actix.workspace = true +bs58.workspace = true +easy-ext.workspace = true +futures.workspace = true +hex.workspace = true +once_cell.workspace = true +serde.workspace = true +serde_json.workspace = true +serde_with.workspace = true +tokio.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true + +unc-chain-configs.workspace = true +unc-client-primitives.workspace = true +unc-primitives.workspace = true +unc-client.workspace = true +unc-network.workspace = true +unc-o11y.workspace = true +unc-jsonrpc-client.workspace = true +unc-jsonrpc-primitives.workspace = true +unc-jsonrpc-adversarial-primitives = { workspace = true, optional = true } +unc-rpc-error-macro.workspace = true + +[features] +dump_errors_schema = ["unc-rpc-error-macro/dump_errors_schema"] +test_features = [ + "unc-client/test_features", + "unc-network/test_features", + "unc-jsonrpc-primitives/test_features", + "unc-jsonrpc-adversarial-primitives/test_features", +] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-client-primitives/nightly", + "unc-client/nightly", + "unc-jsonrpc-adversarial-primitives/nightly", + "unc-jsonrpc-client/nightly", + "unc-jsonrpc-primitives/nightly", + "unc-network/nightly", + "unc-o11y/nightly", + "unc-primitives/nightly", +] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-client-primitives/nightly_protocol", + "unc-client/nightly_protocol", + "unc-jsonrpc-adversarial-primitives/nightly_protocol", + "unc-jsonrpc-client/nightly_protocol", + "unc-jsonrpc-primitives/nightly_protocol", + "unc-network/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-primitives/nightly_protocol", +] +sandbox = [ + "unc-client/sandbox", +] diff --git a/chain/jsonrpc/README.md b/chain/jsonrpc/README.md new file mode 100644 index 000000000..8af2c11d7 --- /dev/null +++ b/chain/jsonrpc/README.md @@ -0,0 +1,51 @@ +# JSON-RPC API for framework + +[JSON-RPC](https://www.jsonrpc.org/) API for framework node exposes handles to +inspect the data, inspect the network state, and the node state, and allows to +submit a transaction. + +## Guarantees + +All the APIs that are compiled by default (default-features) and not prefixed +with `EXPERIMENTAL_` are kept stable without breaking changes. + +We also support `unc-api-*` bindings to JSON-RPC in the latest state and +propagate deprecation warnings through them. + +The breaking changes (e.g. removal or change of `EXPERIMENTAL_` APIs) are +communicated through the CHANGELOG next to this file. + +## Policies for API Changes + +1. We only add the APIs to the data that is already available in framework + storage. +2. We don't violate the guaranties described in the section above. +3. We prefix new APIs with `EXPERIMENTAL_` (see the Experimental API Policies + below). +4. We document the API change on [NEAR Docs](https://docs.near.org/api/rpc/introduction) + BEFORE merging the change to framework. +5. We log changes to the methods and API input/output structures through + CHANGELOG.md file in the jsonrpc crate. + +## Experimental API Policies + +When you introduce new API, we may go two ways: + +1. Hide the new API behind a feature-flag that is not enabled by default +2. Prefix the method name with `EXPERIMENTAL_` + +Either way, we need to document the new API in our [RPC endpoint docs](https://docs.near.org/api/rpc/introduction). + +Stabilization of the Experimental APIs is multistage: + +1. Once the `EXPERIMENTAL_` prefixed API handler lands to master it starts its + lifetime. While the API is Experimental, we have the freedom to play with it + and iterate fearlessly (if we need to change it, we change it and only + record the change in the CHANGELOG and update the documentation, no need for + backwards compatibility). +2. If we feel that the API is stable (being in **use** for a while), we need to + release a new API method without the `EXPERIMENTAL_` prefix while keeping + the old method name as an alias for the transition period. +3. Drop the `EXPERIMENTAL_` alias completely when framework version with the + stable method name is deployed to the majority nodes in the network, and + most (all) clients have transitioned to the new API. diff --git a/chain/jsonrpc/client/Cargo.toml b/chain/jsonrpc/client/Cargo.toml new file mode 100644 index 000000000..3c2e27395 --- /dev/null +++ b/chain/jsonrpc/client/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "unc-jsonrpc-client" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix-http.workspace = true +awc.workspace = true +futures.workspace = true +serde.workspace = true +serde_json.workspace = true + +unc-jsonrpc-primitives.workspace = true +unc-primitives.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-jsonrpc-primitives/nightly", + "unc-primitives/nightly", +] +nightly_protocol = [ + "unc-jsonrpc-primitives/nightly_protocol", + "unc-primitives/nightly_protocol", +] diff --git a/chain/jsonrpc/client/src/lib.rs b/chain/jsonrpc/client/src/lib.rs new file mode 100644 index 000000000..d022b88ad --- /dev/null +++ b/chain/jsonrpc/client/src/lib.rs @@ -0,0 +1,299 @@ +use awc::{Client, Connector}; +use futures::{future, future::LocalBoxFuture, FutureExt, TryFutureExt}; +use unc_jsonrpc_primitives::errors::RpcError; +use unc_jsonrpc_primitives::message::{from_slice, Message}; +use unc_jsonrpc_primitives::types::changes::{ + RpcStateChangesInBlockByTypeRequest, RpcStateChangesInBlockByTypeResponse, +}; +use unc_jsonrpc_primitives::types::transactions::{ + RpcTransactionResponse, RpcTransactionStatusRequest, +}; +use unc_jsonrpc_primitives::types::validator::RpcValidatorsOrderedRequest; +use unc_primitives::hash::CryptoHash; +use unc_primitives::types::{BlockId, BlockReference, EpochReference, MaybeBlockId, ShardId}; +use unc_primitives::views::validator_power_view::ValidatorPowerView; +use unc_primitives::views::{ + BlockView, ChunkView, EpochValidatorInfo, GasPriceView, StatusResponse, +}; +use std::time::Duration; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum ChunkId { + BlockShardId(BlockId, ShardId), + Hash(CryptoHash), +} + +/// Timeout for establishing connection. +const CONNECT_TIMEOUT: Duration = Duration::from_secs(30); + +/// Max size of the payload JsonRpcClient can receive. Be careful adjusting this value since +/// smaller values can raise overflow messages. +const PAYLOAD_LIMIT: usize = 100 * 1024 * 1024; + +type HttpRequest = LocalBoxFuture<'static, Result>; +type RpcRequest = LocalBoxFuture<'static, Result>; + +/// Prepare a `RPCRequest` with a given client, server address, method and parameters. +fn call_method(client: &Client, server_addr: &str, method: &str, params: P) -> RpcRequest +where + P: serde::Serialize, + R: serde::de::DeserializeOwned + 'static, +{ + let request = Message::request(method.to_string(), serde_json::to_value(¶ms).unwrap()); + // TODO: simplify this. + client + .post(server_addr) + .insert_header(("Content-Type", "application/json")) + .send_json(&request) + .map_err(|err| RpcError::new_internal_error(None, format!("{:?}", err))) + .and_then(|mut response| { + response.body().limit(PAYLOAD_LIMIT).map(|body| match body { + Ok(bytes) => from_slice(&bytes).map_err(|err| { + RpcError::parse_error(format!("Error {:?} in {:?}", err, bytes)) + }), + Err(err) => { + Err(RpcError::parse_error(format!("Failed to retrieve payload: {:?}", err))) + } + }) + }) + .and_then(|message| { + future::ready(match message { + Message::Response(resp) => resp.result.and_then(|x| { + serde_json::from_value(x) + .map_err(|err| RpcError::parse_error(format!("Failed to parse: {:?}", err))) + }), + _ => Err(RpcError::parse_error("Failed to parse JSON RPC response".to_string())), + }) + }) + .boxed_local() +} + +/// Prepare a `HttpRequest` with a given client, server address and parameters. +fn call_http_get( + client: &Client, + server_addr: &str, + method: &str, + _params: P, +) -> HttpRequest +where + P: serde::Serialize, + R: serde::de::DeserializeOwned + 'static, +{ + // TODO: url encode params. + client + .get(format!("{}/{}", server_addr, method)) + .send() + .map_err(|err| err.to_string()) + .and_then(|mut response| { + response.body().map(|body| match body { + Ok(bytes) => serde_json::from_slice(&bytes).map_err(|err| err.to_string()), + Err(err) => Err(format!("Payload error: {err}")), + }) + }) + .boxed_local() +} + +/// Expands a variable list of parameters into its serializable form. Is needed to make the params +/// of a nullary method equal to `[]` instead of `()` and thus make sure it serializes to `[]` +/// instead of `null`. +#[doc(hidden)] +macro_rules! expand_params { + () => ([] as [(); 0]); + ($($arg_name:ident,)+) => (($($arg_name,)+)) +} + +/// Generates a simple HTTP client with automatic serialization and deserialization. +/// Method calls get correct types automatically. +macro_rules! http_client { + ( + $(#[$struct_attr:meta])* + pub struct $struct_name:ident {$( + $(#[$attr:meta])* + pub fn $method:ident(&mut $selff:ident $(, $arg_name:ident: $arg_ty:ty)*) + -> HttpRequest<$return_ty:ty>; + )*} + ) => ( + $(#[$struct_attr])* + pub struct $struct_name { + server_addr: String, + client: Client, + } + + impl $struct_name { + /// Creates a new HTTP client backed by the given transport implementation. + pub fn new(server_addr: &str, client: Client) -> Self { + $struct_name { server_addr: server_addr.to_string(), client } + } + + $( + $(#[$attr])* + pub fn $method(&$selff $(, $arg_name: $arg_ty)*) + -> HttpRequest<$return_ty> + { + let method = stringify!($method); + let params = expand_params!($($arg_name,)*); + call_http_get(&$selff.client, &$selff.server_addr, &method, params) + } + )* + } + ) +} + +/// Generates JSON-RPC 2.0 client structs with automatic serialization +/// and deserialization. Method calls get correct types automatically. +macro_rules! jsonrpc_client { + ( + $(#[$struct_attr:meta])* + pub struct $struct_name:ident {$( + $(#[$attr:meta])* + pub fn $method:ident(&$selff:ident $(, $arg_name:ident: $arg_ty:ty)*) + -> RpcRequest<$return_ty:ty>; + )*} + ) => ( + $(#[$struct_attr])* + pub struct $struct_name { + pub server_addr: String, + pub client: Client, + } + + impl $struct_name { + /// Creates a new RPC client backed by the given transport implementation. + pub fn new(server_addr: &str, client: Client) -> Self { + $struct_name { server_addr: server_addr.to_string(), client } + } + + $( + $(#[$attr])* + pub fn $method(&$selff $(, $arg_name: $arg_ty)*) + -> RpcRequest<$return_ty> + { + let method = stringify!($method); + let params = expand_params!($($arg_name,)*); + call_method(&$selff.client, &$selff.server_addr, &method, params) + } + )* + } + ) +} + +jsonrpc_client!(pub struct JsonRpcClient { + pub fn broadcast_tx_async(&self, tx: String) -> RpcRequest; + pub fn broadcast_tx_commit(&self, tx: String) -> RpcRequest; + pub fn status(&self) -> RpcRequest; + #[allow(non_snake_case)] + pub fn EXPERIMENTAL_genesis_config(&self) -> RpcRequest; + #[allow(non_snake_case)] + pub fn EXPERIMENTAL_tx_status(&self, tx: String) -> RpcRequest; + pub fn health(&self) -> RpcRequest<()>; + pub fn chunk(&self, id: ChunkId) -> RpcRequest; + pub fn gas_price(&self, block_id: MaybeBlockId) -> RpcRequest; +}); + +impl JsonRpcClient { + /// This is a soft-deprecated method to do query RPC request with a path and data positional + /// parameters. + pub fn query_by_path( + &self, + path: String, + data: String, + ) -> RpcRequest { + call_method(&self.client, &self.server_addr, "query", [path, data]) + } + + pub fn query( + &self, + request: unc_jsonrpc_primitives::types::query::RpcQueryRequest, + ) -> RpcRequest { + call_method(&self.client, &self.server_addr, "query", request) + } + + pub fn block_by_id(&self, block_id: BlockId) -> RpcRequest { + call_method(&self.client, &self.server_addr, "block", [block_id]) + } + + pub fn block(&self, request: BlockReference) -> RpcRequest { + call_method(&self.client, &self.server_addr, "block", request) + } + + pub fn tx(&self, request: RpcTransactionStatusRequest) -> RpcRequest { + call_method(&self.client, &self.server_addr, "tx", request) + } + + #[allow(non_snake_case)] + pub fn EXPERIMENTAL_changes( + &self, + request: RpcStateChangesInBlockByTypeRequest, + ) -> RpcRequest { + call_method(&self.client, &self.server_addr, "EXPERIMENTAL_changes", request) + } + + #[allow(non_snake_case)] + pub fn EXPERIMENTAL_validators_ordered( + &self, + request: RpcValidatorsOrderedRequest, + ) -> RpcRequest> { + call_method(&self.client, &self.server_addr, "EXPERIMENTAL_validators_ordered", request) + } + + #[allow(non_snake_case)] + pub fn EXPERIMENTAL_receipt( + &self, + request: unc_jsonrpc_primitives::types::receipts::RpcReceiptRequest, + ) -> RpcRequest { + call_method(&self.client, &self.server_addr, "EXPERIMENTAL_receipt", request) + } + + #[allow(non_snake_case)] + pub fn EXPERIMENTAL_protocol_config( + &self, + request: unc_jsonrpc_primitives::types::config::RpcProtocolConfigRequest, + ) -> RpcRequest { + call_method(&self.client, &self.server_addr, "EXPERIMENTAL_protocol_config", request) + } + + #[allow(non_snake_case)] + pub fn EXPERIMENTAL_split_storage_info( + &self, + request: unc_jsonrpc_primitives::types::split_storage::RpcSplitStorageInfoRequest, + ) -> RpcRequest + { + call_method(&self.client, &self.server_addr, "EXPERIMENTAL_split_storage_info", request) + } + + pub fn validators( + &self, + epoch_id_or_block_id: Option, + ) -> RpcRequest { + let epoch_reference = match epoch_id_or_block_id { + Some(epoch_reference) => epoch_reference, + _ => EpochReference::Latest, + }; + call_method(&self.client, &self.server_addr, "validators", epoch_reference) + } +} + +fn create_client() -> Client { + Client::builder() + .timeout(CONNECT_TIMEOUT) + .connector( + Connector::new() + .conn_lifetime(Duration::from_secs(u64::max_value())) + .conn_keep_alive(Duration::from_secs(30)), + ) + .finish() +} + +/// Create new JSON RPC client that connects to the given address. +pub fn new_client(server_addr: &str) -> JsonRpcClient { + JsonRpcClient::new(server_addr, create_client()) +} + +http_client!(pub struct HttpClient { + pub fn status(&mut self) -> HttpRequest; +}); + +/// Create new HTTP client that connects to the given address. +pub fn new_http_client(server_addr: &str) -> HttpClient { + HttpClient::new(server_addr, create_client()) +} diff --git a/chain/jsonrpc/fuzz/.gitignore b/chain/jsonrpc/fuzz/.gitignore new file mode 100644 index 000000000..572e03bdf --- /dev/null +++ b/chain/jsonrpc/fuzz/.gitignore @@ -0,0 +1,4 @@ + +target +corpus +artifacts diff --git a/chain/jsonrpc/fuzz/Cargo.toml b/chain/jsonrpc/fuzz/Cargo.toml new file mode 100644 index 000000000..dc3b9cea4 --- /dev/null +++ b/chain/jsonrpc/fuzz/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "unc-jsonrpc-fuzz" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[package.metadata] +cargo-fuzz = true + +[dependencies] +actix.workspace = true +arbitrary.workspace = true +awc.workspace = true +libfuzzer-sys.workspace = true +once_cell.workspace = true +serde.workspace = true +serde_json.workspace = true +tokio.workspace = true + +unc-jsonrpc.workspace = true +unc-jsonrpc-primitives.workspace = true +unc-jsonrpc-tests.workspace = true +unc-primitives.workspace = true + +[[bin]] +name = "fuzz_target_1" +path = "fuzz_targets_disabled/fuzz_target_1.rs" +test = false +doc = false diff --git a/chain/jsonrpc/fuzz/fuzz_targets_disabled/fuzz_target_1.rs b/chain/jsonrpc/fuzz/fuzz_targets_disabled/fuzz_target_1.rs new file mode 100644 index 000000000..96a84b0d5 --- /dev/null +++ b/chain/jsonrpc/fuzz/fuzz_targets_disabled/fuzz_target_1.rs @@ -0,0 +1,141 @@ +#![no_main] +use actix::System; +use libfuzzer_sys::{arbitrary, fuzz_target}; +use serde::ser::{Serialize, Serializer}; +use serde_json::json; +use tokio; + +use unc_jsonrpc_primitives::types; +use unc_jsonrpc_tests as test_utils; +use unc_primitives::hash::CryptoHash; +use unc_primitives::types::Finality; + +static mut NODE_ADDR: Option = None; +static NODE_INIT: std::sync::Once = std::sync::Once::new(); + +#[derive(Debug, arbitrary::Arbitrary)] +enum JsonRpcRequest { + Query(RpcQueryRequest), + Block(types::blocks::RpcBlockRequest), + Chunk(types::chunks::RpcChunkRequest), + Tx(RpcTxStatusRequest), + Validators(types::validator::RpcValidatorRequest), + GasPrice(types::gas_price::RpcGasPriceRequest), + BroadcastTxAsync(RpcBroadcastTx), + BroadcastTxCommit(RpcBroadcastTx), + Provider(types::provider::RpcProviderRequest), +} + +#[derive(Debug, arbitrary::Arbitrary, serde::Serialize)] +#[serde(tag = "request_type", rename_all = "snake_case")] +enum RpcQueryRequest { + ViewAccount { + finality: Finality, + account_id: String, + }, + ViewState { + finality: Finality, + account_id: String, + prefix_base64: String, + }, + ViewAccessKey { + finality: Finality, + account_id: String, + public_key: String, + }, + ViewAccessKeyList { + finality: Finality, + account_id: String, + }, + CallFunction { + finality: Finality, + account_id: String, + method_name: String, + args_base64: String, + }, +} + +#[derive(Debug, arbitrary::Arbitrary)] +struct Base64String([u8; 32]); + +#[derive(Debug, arbitrary::Arbitrary, serde::Serialize)] +#[serde(untagged)] +enum RpcTxStatusRequest { + Transaction(CryptoHash, String), +} + +#[derive(Debug, arbitrary::Arbitrary, serde::Serialize)] +#[serde(untagged)] +enum RpcBroadcastTx { + SignedTransaction([Base64String; 1]), +} + +impl JsonRpcRequest { + fn method_and_params(&self) -> (&str, serde_json::Value) { + match self { + JsonRpcRequest::Query(request) => ("query", json!(request)), + JsonRpcRequest::Block(request) => ("block", json!(request)), + JsonRpcRequest::Chunk(request) => ("chunk", json!(request)), + JsonRpcRequest::Tx(request) => ("tx", json!(request)), + JsonRpcRequest::Validators(request) => ("validators", json!(request)), + JsonRpcRequest::GasPrice(request) => ("gas_price", json!(request)), + JsonRpcRequest::BroadcastTxAsync(request) => ("broadcast_tx_async", json!(request)), + JsonRpcRequest::BroadcastTxCommit(request) => ("broadcast_tx_commit", json!(request)), + JsonRpcRequest::Provider(request) => ("provider", json!(request)), + } + } +} + +impl Serialize for Base64String { + fn serialize(&self, serializer: S) -> Result { + let encoded = unc_primitives::serialize::to_base64(&self.0); + serializer.serialize_str(&encoded) + } +} + +static RUNTIME: once_cell::sync::Lazy> = + once_cell::sync::Lazy::new(|| { + std::sync::Mutex::new( + tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap(), + ) + }); + +fuzz_target!(|requests: Vec| { + NODE_INIT.call_once(|| { + std::thread::spawn(|| { + System::new().block_on(async { + let (_view_client_addr, addr) = + test_utils::start_all(test_utils::NodeType::NonValidator); + unsafe { NODE_ADDR = Some(addr.to_string()) } + }); + }); + }); + + for _ in 1..30 { + if let Some(_node_addr) = unsafe { NODE_ADDR.as_ref() } { + break; + } else { + std::thread::sleep(std::time::Duration::from_millis(100)); // ensure node have enough time to start + } + } + + RUNTIME.lock().unwrap().block_on(async move { + for request in requests { + let (method, params) = request.method_and_params(); + eprintln!("POST DATA: {{method = {}}} {{params = {}}}", method, params); + + let client = awc::Client::new(); + let result_or_error = test_utils::call_method::( + &client, + unsafe { NODE_ADDR.as_ref().unwrap() }, + method, + params, + ) + .await + .unwrap(); + eprintln!("RESPONSE: {:#?}", result_or_error); + assert!(result_or_error["error"] != serde_json::json!(null)); + } + true + }); +}); diff --git a/chain/jsonrpc/jsonrpc-tests/Cargo.toml b/chain/jsonrpc/jsonrpc-tests/Cargo.toml new file mode 100644 index 000000000..98c86f52f --- /dev/null +++ b/chain/jsonrpc/jsonrpc-tests/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "unc-jsonrpc-tests" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix.workspace = true +awc.workspace = true +once_cell.workspace = true +futures.workspace = true +borsh.workspace = true +serde.workspace = true +serde_json.workspace = true + +unc-chain-configs.workspace = true +unc-crypto.workspace = true +unc-primitives.workspace = true +unc-client.workspace = true +unc-store.workspace = true +unc-o11y.workspace = true +unc-network.workspace = true +unc-jsonrpc.workspace = true +unc-jsonrpc-primitives.workspace = true + +[dev-dependencies] +unc-actix-test-utils.workspace = true + +[features] +test_features = ["unc-jsonrpc/test_features"] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-client/nightly", + "unc-jsonrpc-primitives/nightly", + "unc-jsonrpc/nightly", + "unc-network/nightly", + "unc-o11y/nightly", + "unc-primitives/nightly", + "unc-store/nightly", +] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-client/nightly_protocol", + "unc-jsonrpc-primitives/nightly_protocol", + "unc-jsonrpc/nightly_protocol", + "unc-network/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", +] +sandbox = ["unc-jsonrpc/sandbox"] diff --git a/chain/jsonrpc/jsonrpc-tests/res/genesis_config.json b/chain/jsonrpc/jsonrpc-tests/res/genesis_config.json new file mode 100644 index 000000000..0a39d77e4 --- /dev/null +++ b/chain/jsonrpc/jsonrpc-tests/res/genesis_config.json @@ -0,0 +1,72 @@ +{ + "protocol_version": 64, + "genesis_time": "1970-01-01T00:00:00.000000000Z", + "chain_id": "sample", + "genesis_height": 0, + "num_block_producer_seats": 50, + "num_block_producer_seats_per_shard": [ + 50 + ], + "avg_hidden_validator_seats_per_shard": [ + 0 + ], + "dynamic_resharding": false, + "protocol_upgrade_stake_threshold": [ + 4, + 5 + ], + "epoch_length": 500, + "gas_limit": 1000000000000000, + "min_gas_price": "100000000", + "max_gas_price": "10000000000000000000000", + "block_producer_kickout_threshold": 90, + "chunk_producer_kickout_threshold": 90, + "online_min_threshold": [ + 9, + 10 + ], + "online_max_threshold": [ + 99, + 100 + ], + "gas_price_adjustment_rate": [ + 1, + 100 + ], + "validators": [ + { + "account_id": "test.near", + "public_key": "ed25519:9BmAFNRTa5mRRXpSAm6MxSEeqRASDGNh2FuuwZ4gyxTw", + "amount": "50000000000000000000000000000000" + } + ], + "transaction_validity_period": 100, + "protocol_reward_rate": [ + 1, + 10 + ], + "max_inflation_rate": [ + 1, + 20 + ], + "total_supply": "2050000000000000000000000000000000", + "num_blocks_per_year": 31536000, + "protocol_treasury_account": "test.near", + "fishermen_threshold": "10000000000000000000000000", + "minimum_stake_divisor": 10, + "shard_layout": { + "V0": { + "num_shards": 1, + "version": 0 + } + }, + "num_chunk_only_producer_seats": 300, + "minimum_validators_per_shard": 1, + "max_kickout_stake_perc": 100, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "use_production_config": false, + "records": [] +} \ No newline at end of file diff --git a/chain/jsonrpc/jsonrpc-tests/src/lib.rs b/chain/jsonrpc/jsonrpc-tests/src/lib.rs new file mode 100644 index 000000000..35313a246 --- /dev/null +++ b/chain/jsonrpc/jsonrpc-tests/src/lib.rs @@ -0,0 +1,136 @@ +use std::sync::Arc; + +use actix::Addr; +use futures::{future, future::LocalBoxFuture, FutureExt, TryFutureExt}; +use unc_chain_configs::GenesisConfig; +use unc_client::test_utils::setup_no_network_with_validity_period_and_no_epoch_sync; +use unc_client::ViewClientActor; +use unc_jsonrpc::{start_http, RpcConfig}; +use unc_jsonrpc_primitives::{ + message::{from_slice, Message}, + types::entity_debug::DummyEntityDebugHandler, +}; +use unc_network::tcp; +use unc_primitives::types::NumBlocks; +use once_cell::sync::Lazy; +use serde_json::json; + +pub static TEST_GENESIS_CONFIG: Lazy = + Lazy::new(|| GenesisConfig::from_json(include_str!("../res/genesis_config.json"))); + +pub enum NodeType { + Validator, + NonValidator, +} + +pub fn start_all(node_type: NodeType) -> (Addr, tcp::ListenerAddr) { + start_all_with_validity_period_and_no_epoch_sync(node_type, 100, false) +} + +pub fn start_all_with_validity_period_and_no_epoch_sync( + node_type: NodeType, + transaction_validity_period: NumBlocks, + enable_doomslug: bool, +) -> (Addr, tcp::ListenerAddr) { + let actor_handles: unc_client::test_utils::ActorHandlesForTesting = setup_no_network_with_validity_period_and_no_epoch_sync( + vec!["test1".parse().unwrap()], + if let NodeType::Validator = node_type { + "test1".parse().unwrap() + } else { + "other".parse().unwrap() + }, + true, + transaction_validity_period, + enable_doomslug, + ); + + let addr = tcp::ListenerAddr::reserve_for_test(); + start_http( + RpcConfig::new(addr), + TEST_GENESIS_CONFIG.clone(), + actor_handles.client_actor, + actor_handles.view_client_actor.clone(), + None, + Arc::new(DummyEntityDebugHandler {}), + ); + (actor_handles.view_client_actor, addr) +} + +#[macro_export] +macro_rules! test_with_client { + ($node_type:expr, $client:ident, $block:expr) => { + init_test_logger(); + + unc_actix_test_utils::run_actix(async { + let (_view_client_addr, addr) = test_utils::start_all($node_type); + + let $client = new_client(&format!("http://{}", addr)); + + actix::spawn(async move { + $block.await; + System::current().stop(); + }); + }); + }; +} + +type RpcRequest = LocalBoxFuture<'static, Result>; + +/// Prepare a `RPCRequest` with a given client, server address, method and parameters. +pub fn call_method( + client: &awc::Client, + server_addr: &str, + method: &str, + params: serde_json::Value, +) -> RpcRequest +where + R: serde::de::DeserializeOwned + 'static, +{ + let request = json!({ + "jsonrpc": "2.0", + "method": method, + "id": "dontcare", + "params": params, + }); + // TODO: simplify this. + client + .post(server_addr) + .insert_header(("Content-Type", "application/json")) + .send_json(&request) + .map_err(|err| { + unc_jsonrpc_primitives::errors::RpcError::new_internal_error( + None, + format!("{:?}", err), + ) + }) + .and_then(|mut response| { + response.body().map(|body| match body { + Ok(bytes) => from_slice(&bytes).map_err(|err| { + unc_jsonrpc_primitives::errors::RpcError::parse_error(format!( + "Error {:?} in {:?}", + err, bytes + )) + }), + Err(err) => Err(unc_jsonrpc_primitives::errors::RpcError::parse_error(format!( + "Failed to retrieve payload: {:?}", + err + ))), + }) + }) + .and_then(|message| { + future::ready(match message { + Message::Response(resp) => resp.result.and_then(|x| { + serde_json::from_value(x).map_err(|err| { + unc_jsonrpc_primitives::errors::RpcError::parse_error(format!( + "Failed to parse: {:?}", + err + )) + }) + }), + _ => Err(unc_jsonrpc_primitives::errors::RpcError::parse_error( + "Failed to parse JSON RPC response".to_string(), + )), + }) + }) + .boxed_local() +} diff --git a/chain/jsonrpc/jsonrpc-tests/tests/http_query.rs b/chain/jsonrpc/jsonrpc-tests/tests/http_query.rs new file mode 100644 index 000000000..2b72eaf75 --- /dev/null +++ b/chain/jsonrpc/jsonrpc-tests/tests/http_query.rs @@ -0,0 +1,28 @@ +use actix::System; +use futures::{future, FutureExt}; + +use unc_actix_test_utils::run_actix; +use unc_jsonrpc::client::new_http_client; +use unc_o11y::testonly::init_test_logger; + +use unc_jsonrpc_tests as test_utils; + +/// Retrieve client status via HTTP GET. +#[test] +fn test_status() { + init_test_logger(); + + run_actix(async { + let (_view_client_addr, addr) = test_utils::start_all(test_utils::NodeType::NonValidator); + + let client = new_http_client(&format!("http://{}", addr)); + actix::spawn(client.status().then(|res| { + let res = res.unwrap(); + assert_eq!(res.chain_id, "unittest"); + assert_eq!(res.sync_info.latest_block_height, 0); + assert_eq!(res.sync_info.syncing, false); + System::current().stop(); + future::ready(()) + })); + }); +} diff --git a/chain/jsonrpc/jsonrpc-tests/tests/rpc_query.rs b/chain/jsonrpc/jsonrpc-tests/tests/rpc_query.rs new file mode 100644 index 000000000..337a616f3 --- /dev/null +++ b/chain/jsonrpc/jsonrpc-tests/tests/rpc_query.rs @@ -0,0 +1,584 @@ +use std::ops::ControlFlow; +use std::str::FromStr; + +use actix::System; +use futures::{future, FutureExt}; +use serde_json::json; + +use unc_actix_test_utils::run_actix; +use unc_crypto::{KeyType, PublicKey, Signature}; +use unc_jsonrpc::client::{new_client, ChunkId}; +use unc_jsonrpc_primitives::types::query::QueryResponseKind; +use unc_jsonrpc_primitives::types::validator::RpcValidatorsOrderedRequest; +use unc_network::test_utils::wait_or_timeout; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::account::{AccessKey, AccessKeyPermission}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::types::{BlockId, BlockReference, EpochId, SyncCheckpoint}; +use unc_primitives::views::QueryRequest; + +use unc_jsonrpc_tests::{self as test_utils, test_with_client}; + +/// Retrieve blocks via json rpc +#[test] +fn test_block_by_id_height() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let block = client.block_by_id(BlockId::Height(0)).await.unwrap(); + assert_eq!(block.author, "test1"); + assert_eq!(block.header.height, 0); + assert_eq!(block.header.epoch_id.0.as_ref(), &[0; 32]); + assert_eq!(block.header.hash.0.as_ref().len(), 32); + assert_eq!(block.header.prev_hash.0.as_ref(), &[0; 32]); + assert_eq!( + block.header.prev_state_root, + CryptoHash::from_str("7tkzFg8RHBmMw1ncRJZCCZAizgq4rwCftTKYLce8RU8t").unwrap() + ); + assert!(block.header.timestamp > 0); + assert_eq!(block.header.validator_proposals.len(), 0); + }); +} + +/// Retrieve blocks via json rpc +#[test] +fn test_block_by_id_hash() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let block = client.block_by_id(BlockId::Height(0)).await.unwrap(); + let same_block = client.block_by_id(BlockId::Hash(block.header.hash)).await.unwrap(); + assert_eq!(block.header.height, 0); + assert_eq!(same_block.header.height, 0); + }); +} + +/// Retrieve blocks via json rpc +#[test] +fn test_block_query() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let block_response1 = + client.block(BlockReference::BlockId(BlockId::Height(0))).await.unwrap(); + let block_response2 = client + .block(BlockReference::BlockId(BlockId::Hash(block_response1.header.hash))) + .await + .unwrap(); + let block_response3 = client.block(BlockReference::latest()).await.unwrap(); + let block_response4 = + client.block(BlockReference::SyncCheckpoint(SyncCheckpoint::Genesis)).await.unwrap(); + let block_response5 = client + .block(BlockReference::SyncCheckpoint(SyncCheckpoint::EarliestAvailable)) + .await + .unwrap(); + for block in + &[block_response1, block_response2, block_response3, block_response4, block_response5] + { + assert_eq!(block.author, "test1"); + assert_eq!(block.header.height, 0); + assert_eq!(block.header.epoch_id.as_ref(), &[0; 32]); + assert_eq!(block.header.hash.as_ref().len(), 32); + assert_eq!(block.header.prev_hash.as_ref(), &[0; 32]); + assert_eq!( + block.header.prev_state_root, + CryptoHash::from_str("7tkzFg8RHBmMw1ncRJZCCZAizgq4rwCftTKYLce8RU8t").unwrap() + ); + assert!(block.header.timestamp > 0); + assert_eq!(block.header.validator_proposals.len(), 0); + } + }); +} + +/// Retrieve chunk via json rpc +#[test] +fn test_chunk_by_hash() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let chunk = client.chunk(ChunkId::BlockShardId(BlockId::Height(0), 0u64)).await.unwrap(); + assert_eq!(chunk.author, "test1"); + assert_eq!(chunk.header.balance_burnt, 0); + assert_eq!(chunk.header.chunk_hash.as_ref().len(), 32); + assert_eq!(chunk.header.encoded_length, 8); + assert_eq!(chunk.header.encoded_merkle_root.as_ref().len(), 32); + assert_eq!(chunk.header.gas_limit, 1000000); + assert_eq!(chunk.header.gas_used, 0); + assert_eq!(chunk.header.height_created, 0); + assert_eq!(chunk.header.height_included, 0); + assert_eq!(chunk.header.outgoing_receipts_root.as_ref().len(), 32); + assert_eq!(chunk.header.prev_block_hash.as_ref().len(), 32); + assert_eq!(chunk.header.prev_state_root.as_ref().len(), 32); + assert_eq!(chunk.header.rent_paid, 0); + assert_eq!(chunk.header.shard_id, 0); + assert!(if let Signature::ED25519(_) = chunk.header.signature { true } else { false }); + assert_eq!(chunk.header.tx_root.as_ref(), &[0; 32]); + assert_eq!(chunk.header.validator_proposals, vec![]); + assert_eq!(chunk.header.validator_reward, 0); + let same_chunk = client.chunk(ChunkId::Hash(chunk.header.chunk_hash)).await.unwrap(); + assert_eq!(chunk.header.chunk_hash, same_chunk.header.chunk_hash); + }); +} + +/// Retrieve chunk via json rpc +#[test] +fn test_chunk_invalid_shard_id() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let chunk = client.chunk(ChunkId::BlockShardId(BlockId::Height(0), 100)).await; + match chunk { + Ok(_) => panic!("should result in an error"), + Err(e) => { + let s = serde_json::to_string(&e.data.unwrap()).unwrap(); + assert!(s.starts_with("\"Shard id 100 does not exist")); + } + } + }); +} + +/// Connect to json rpc and query account info with soft-deprecated query API. +#[test] +fn test_query_by_path_account() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let status = client.status().await.unwrap(); + let block_hash = status.sync_info.latest_block_hash; + let query_response = + client.query_by_path("account/test".to_string(), "".to_string()).await.unwrap(); + assert_eq!(query_response.block_height, 0); + assert_eq!(query_response.block_hash, block_hash); + let account_info = if let QueryResponseKind::ViewAccount(account) = query_response.kind { + account + } else { + panic!("queried account, but received something else: {:?}", query_response.kind); + }; + assert_eq!(account_info.amount, 0); + assert_eq!(account_info.code_hash.as_ref(), &[0; 32]); + assert_eq!(account_info.locked, 0); + assert_eq!(account_info.storage_paid_at, 0); + assert_eq!(account_info.storage_usage, 0); + }); +} + +/// Connect to json rpc and query account info. +#[test] +fn test_query_account() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let status = client.status().await.unwrap(); + let block_hash = status.sync_info.latest_block_hash; + let query_response_1 = client + .query(unc_jsonrpc_primitives::types::query::RpcQueryRequest { + block_reference: BlockReference::latest(), + request: QueryRequest::ViewAccount { account_id: "test".parse().unwrap() }, + }) + .await + .unwrap(); + let query_response_2 = client + .query(unc_jsonrpc_primitives::types::query::RpcQueryRequest { + block_reference: BlockReference::BlockId(BlockId::Height(0)), + request: QueryRequest::ViewAccount { account_id: "test".parse().unwrap() }, + }) + .await + .unwrap(); + let query_response_3 = client + .query(unc_jsonrpc_primitives::types::query::RpcQueryRequest { + block_reference: BlockReference::BlockId(BlockId::Hash(block_hash)), + request: QueryRequest::ViewAccount { account_id: "test".parse().unwrap() }, + }) + .await + .unwrap(); + for query_response in [query_response_1, query_response_2, query_response_3].iter() { + assert_eq!(query_response.block_height, 0); + assert_eq!(query_response.block_hash, block_hash); + let account_info = if let QueryResponseKind::ViewAccount(ref account) = + query_response.kind + { + account + } else { + panic!("queried account, but received something else: {:?}", query_response.kind); + }; + assert_eq!(account_info.amount, 0); + assert_eq!(account_info.code_hash.as_ref(), &[0; 32]); + assert_eq!(account_info.locked, 0); + assert_eq!(account_info.storage_paid_at, 0); + assert_eq!(account_info.storage_usage, 0); + } + }); +} + +/// Connect to json rpc and query account info with soft-deprecated query API. +#[test] +fn test_query_by_path_access_keys() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let query_response = + client.query_by_path("access_key/test".to_string(), "".to_string()).await.unwrap(); + assert_eq!(query_response.block_height, 0); + let access_keys = if let QueryResponseKind::AccessKeyList(access_keys) = query_response.kind + { + access_keys + } else { + panic!("queried access keys, but received something else: {:?}", query_response.kind); + }; + assert_eq!(access_keys.keys.len(), 1); + assert_eq!(access_keys.keys[0].access_key, AccessKey::full_access().into()); + assert_eq!(access_keys.keys[0].public_key, PublicKey::empty(KeyType::ED25519)); + }); +} + +/// Connect to json rpc and query account info. +#[test] +fn test_query_access_keys() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let query_response = client + .query(unc_jsonrpc_primitives::types::query::RpcQueryRequest { + block_reference: BlockReference::latest(), + request: QueryRequest::ViewAccessKeyList { account_id: "test".parse().unwrap() }, + }) + .await + .unwrap(); + assert_eq!(query_response.block_height, 0); + let access_keys = if let QueryResponseKind::AccessKeyList(access_keys) = query_response.kind + { + access_keys + } else { + panic!("queried access keys, but received something else: {:?}", query_response.kind); + }; + assert_eq!(access_keys.keys.len(), 1); + assert_eq!(access_keys.keys[0].access_key, AccessKey::full_access().into()); + assert_eq!(access_keys.keys[0].public_key, PublicKey::empty(KeyType::ED25519)); + }); +} + +/// Connect to json rpc and query account info with soft-deprecated query API. +#[test] +fn test_query_by_path_access_key() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let query_response = client + .query_by_path( + "access_key/test/ed25519:23vYngy8iL7q94jby3gszBnZ9JptpMf5Hgf7KVVa2yQ2".to_string(), + "".to_string(), + ) + .await + .unwrap(); + assert_eq!(query_response.block_height, 0); + let access_key = if let QueryResponseKind::AccessKey(access_keys) = query_response.kind { + access_keys + } else { + panic!("queried access keys, but received something else: {:?}", query_response.kind); + }; + assert_eq!(access_key.nonce, 0); + assert_eq!(access_key.permission, AccessKeyPermission::FullAccess.into()); + }); +} + +/// Connect to json rpc and query account info. +#[test] +fn test_query_access_key() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let query_response = client + .query(unc_jsonrpc_primitives::types::query::RpcQueryRequest { + block_reference: BlockReference::latest(), + request: QueryRequest::ViewAccessKey { + account_id: "test".parse().unwrap(), + public_key: "ed25519:23vYngy8iL7q94jby3gszBnZ9JptpMf5Hgf7KVVa2yQ2" + .parse() + .unwrap(), + }, + }) + .await + .unwrap(); + assert_eq!(query_response.block_height, 0); + let access_key = if let QueryResponseKind::AccessKey(access_keys) = query_response.kind { + access_keys + } else { + panic!("queried access keys, but received something else: {:?}", query_response.kind); + }; + assert_eq!(access_key.nonce, 0); + assert_eq!(access_key.permission, AccessKeyPermission::FullAccess.into()); + }); +} + +/// Connect to json rpc and query state. +#[test] +fn test_query_state() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let query_response = client + .query(unc_jsonrpc_primitives::types::query::RpcQueryRequest { + block_reference: BlockReference::latest(), + request: QueryRequest::ViewState { + account_id: "test".parse().unwrap(), + prefix: vec![].into(), + include_proof: false, + }, + }) + .await + .unwrap(); + assert_eq!(query_response.block_height, 0); + let state = if let QueryResponseKind::ViewState(state) = query_response.kind { + state + } else { + panic!("queried state, but received something else: {:?}", query_response.kind); + }; + assert_eq!(state.values.len(), 0); + }); +} + +/// Connect to json rpc and call function +#[test] +fn test_query_call_function() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let query_response = client + .query(unc_jsonrpc_primitives::types::query::RpcQueryRequest { + block_reference: BlockReference::latest(), + request: QueryRequest::CallFunction { + account_id: "test".parse().unwrap(), + method_name: "method".to_string(), + args: vec![].into(), + }, + }) + .await + .unwrap(); + assert_eq!(query_response.block_height, 0); + let call_result = if let QueryResponseKind::CallResult(call_result) = query_response.kind { + call_result + } else { + panic!( + "expected a call function result, but received something else: {:?}", + query_response.kind + ); + }; + assert_eq!(call_result.result.len(), 0); + assert_eq!(call_result.logs.len(), 0); + }); +} + +/// query contract code +#[test] +fn test_query_contract_code() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let query_response = client + .query(unc_jsonrpc_primitives::types::query::RpcQueryRequest { + block_reference: BlockReference::latest(), + request: QueryRequest::ViewCode { account_id: "test".parse().unwrap() }, + }) + .await + .unwrap(); + assert_eq!(query_response.block_height, 0); + let code = if let QueryResponseKind::ViewCode(code) = query_response.kind { + code + } else { + panic!("queried code, but received something else: {:?}", query_response.kind); + }; + assert_eq!(code.code, Vec::::new()); + assert_eq!(code.hash.to_string(), "11111111111111111111111111111111"); + }); +} + +/// Retrieve client status via JSON RPC. +#[test] +fn test_status() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let status = client.status().await.unwrap(); + assert_eq!(status.chain_id, "unittest"); + assert_eq!(status.sync_info.latest_block_height, 0); + assert_eq!(status.sync_info.syncing, false); + assert_eq!(status.sync_info.epoch_id, Some(EpochId::default())); + assert_eq!(status.sync_info.epoch_start_height, Some(0)); + }); +} + +/// Retrieve client status failed. +#[test] +fn test_status_fail() { + init_test_logger(); + + run_actix(async { + let (_, addr) = test_utils::start_all(test_utils::NodeType::NonValidator); + + let client = new_client(&format!("http://{}", addr)); + wait_or_timeout(100, 10000, || async { + let res = client.health().await; + if res.is_err() { + return ControlFlow::Break(()); + } + ControlFlow::Continue(()) + }) + .await + .unwrap(); + System::current().stop() + }); +} + +/// Check health fails when node is absent. +#[test] +fn test_health_fail() { + init_test_logger(); + + run_actix(async { + let client = new_client("http://127.0.0.1:12322/health"); + actix::spawn(client.health().then(|res| { + assert!(res.is_err()); + System::current().stop(); + future::ready(()) + })); + }); +} + +/// Health fails when node doesn't produce block for period of time. +#[test] +fn test_health_fail_no_blocks() { + init_test_logger(); + + run_actix(async { + let (_, addr) = test_utils::start_all(test_utils::NodeType::NonValidator); + + let client = new_client(&format!("http://{}", addr)); + wait_or_timeout(300, 10000, || async { + let res = client.health().await; + if res.is_err() { + return ControlFlow::Break(()); + } + ControlFlow::Continue(()) + }) + .await + .unwrap(); + System::current().stop() + }); +} + +/// Retrieve client health. +#[test] +fn test_health_ok() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let health = client.health().await; + assert_eq!(health, Ok(())); + }); +} + +#[test] +fn test_validators_ordered() { + test_with_client!(test_utils::NodeType::Validator, client, async move { + let validators = client + .EXPERIMENTAL_validators_ordered(RpcValidatorsOrderedRequest { block_id: None }) + .await + .unwrap(); + assert_eq!( + validators.into_iter().map(|v| v.take_account_id()).collect::>(), + vec!["test1"] + ) + }); +} + +/// Retrieve genesis config via JSON RPC. +/// WARNING: Be mindful about changing genesis structure as it is part of the public protocol! +#[test] +fn test_genesis_config() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let genesis_config = client.EXPERIMENTAL_genesis_config().await.unwrap(); + if !cfg!(feature = "nightly_protocol") { + assert_eq!( + genesis_config["protocol_version"].as_u64().unwrap(), + unc_primitives::version::PROTOCOL_VERSION as u64 + ); + } + assert!(!genesis_config["chain_id"].as_str().unwrap().is_empty()); + assert!(!genesis_config.as_object().unwrap().contains_key("records")); + }); +} + +/// Retrieve gas price +#[test] +fn test_gas_price_by_height() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let gas_price = client.gas_price(Some(BlockId::Height(0))).await.unwrap(); + assert!(gas_price.gas_price > 0); + }); +} + +/// Retrieve gas price +#[test] +fn test_gas_price_by_hash() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let block = client.block(BlockReference::BlockId(BlockId::Height(0))).await.unwrap(); + let gas_price = client.gas_price(Some(BlockId::Hash(block.header.hash))).await.unwrap(); + assert!(gas_price.gas_price > 0); + }); +} + +/// Retrieve gas price +#[test] +fn test_gas_price() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let gas_price = client.gas_price(None).await.unwrap(); + assert!(gas_price.gas_price > 0); + }); +} + +#[test] +fn test_invalid_methods() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let method_names = vec![ + serde_json::json!( + "\u{0}\u{0}\u{0}k\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}SRP" + ), + serde_json::json!(null), + serde_json::json!(true), + serde_json::json!(false), + serde_json::json!(0), + serde_json::json!(""), + ]; + + for method_name in method_names { + let json = serde_json::json!({ + "jsonrpc": "2.0", + "id": "dontcare", + "method": &method_name, + "params": serde_json::json!([]), + }); + let response = &mut client + .client + .post(&client.server_addr) + .insert_header(("Content-Type", "application/json")) + .send_json(&json) + .await + .unwrap(); + + let response = + serde_json::from_value::(response.json().await.unwrap()) + .unwrap(); + + assert!( + response["error"] != serde_json::json!(null), + "Invalid method {:?} must return error", + method_name + ); + } + }); +} + +#[test] +fn test_get_chunk_with_object_in_params() { + test_with_client!(test_utils::NodeType::NonValidator, client, async move { + let chunk: unc_primitives::views::ChunkView = test_utils::call_method( + &client.client, + &client.server_addr, + "chunk", + json!({ + "block_id": 0u64, + "shard_id": 0u64, + }), + ) + .await + .unwrap(); + assert_eq!(chunk.author, "test1"); + assert_eq!(chunk.header.balance_burnt, 0); + assert_eq!(chunk.header.chunk_hash.as_ref().len(), 32); + assert_eq!(chunk.header.encoded_length, 8); + assert_eq!(chunk.header.encoded_merkle_root.as_ref().len(), 32); + assert_eq!(chunk.header.gas_limit, 1000000); + assert_eq!(chunk.header.gas_used, 0); + assert_eq!(chunk.header.height_created, 0); + assert_eq!(chunk.header.height_included, 0); + assert_eq!(chunk.header.outgoing_receipts_root.as_ref().len(), 32); + assert_eq!(chunk.header.prev_block_hash.as_ref().len(), 32); + assert_eq!(chunk.header.prev_state_root.as_ref().len(), 32); + assert_eq!(chunk.header.rent_paid, 0); + assert_eq!(chunk.header.shard_id, 0); + assert!(if let Signature::ED25519(_) = chunk.header.signature { true } else { false }); + assert_eq!(chunk.header.tx_root.as_ref(), &[0; 32]); + assert_eq!(chunk.header.validator_proposals, vec![]); + assert_eq!(chunk.header.validator_reward, 0); + let same_chunk = client.chunk(ChunkId::Hash(chunk.header.chunk_hash)).await.unwrap(); + assert_eq!(chunk.header.chunk_hash, same_chunk.header.chunk_hash); + }); +} diff --git a/chain/jsonrpc/jsonrpc-tests/tests/rpc_transactions.rs b/chain/jsonrpc/jsonrpc-tests/tests/rpc_transactions.rs new file mode 100644 index 000000000..2683d8ce5 --- /dev/null +++ b/chain/jsonrpc/jsonrpc-tests/tests/rpc_transactions.rs @@ -0,0 +1,251 @@ +use std::sync::{Arc, Mutex}; + +use actix::{Actor, System}; + +use futures::{future, FutureExt, TryFutureExt}; + +use unc_actix_test_utils::run_actix; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_jsonrpc::client::new_client; +use unc_jsonrpc_primitives::types::transactions::{RpcTransactionStatusRequest, TransactionInfo}; +use unc_network::test_utils::WaitOrTimeoutActor; +use unc_o11y::testonly::{init_integration_logger, init_test_logger}; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::serialize::to_base64; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::BlockReference; +use unc_primitives::views::{FinalExecutionStatus, TxExecutionStatus}; + +use unc_jsonrpc_tests::{self as test_utils, test_with_client}; + +/// Test sending transaction via json rpc without waiting. +#[test] +fn test_send_tx_async() { + init_test_logger(); + + run_actix(async { + let (_, addr) = test_utils::start_all(test_utils::NodeType::Validator); + + let client = new_client(&format!("http://{}", addr)); + + let tx_hash2 = Arc::new(Mutex::new(None)); + let tx_hash2_1 = tx_hash2.clone(); + let tx_hash2_2 = tx_hash2; + let signer_account_id = "test1".to_string(); + + actix::spawn(client.block(BlockReference::latest()).then(move |res| { + let block_hash = res.unwrap().header.hash; + let signer = + InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let tx = SignedTransaction::send_money( + 1, + signer_account_id.parse().unwrap(), + "test2".parse().unwrap(), + &signer, + 100, + block_hash, + ); + let bytes = borsh::to_vec(&tx).unwrap(); + let tx_hash = tx.get_hash().to_string(); + *tx_hash2_1.lock().unwrap() = Some(tx.get_hash()); + client + .broadcast_tx_async(to_base64(&bytes)) + .map_ok(move |result| assert_eq!(tx_hash, result)) + .map(drop) + })); + let client1 = new_client(&format!("http://{}", addr)); + WaitOrTimeoutActor::new( + Box::new(move |_| { + let signer_account_id = "test1".parse().unwrap(); + if let Some(tx_hash) = *tx_hash2_2.lock().unwrap() { + actix::spawn( + client1 + .tx(RpcTransactionStatusRequest { + transaction_info: TransactionInfo::TransactionId { + tx_hash, + sender_account_id: signer_account_id, + }, + wait_until: TxExecutionStatus::Executed, + }) + .map_err(|err| println!("Error: {:?}", err)) + .map_ok(|result| { + if let FinalExecutionStatus::SuccessValue(_) = + result.final_execution_outcome.unwrap().into_outcome().status + { + System::current().stop(); + } + }) + .map(drop), + ); + } + }), + 100, + 2000, + ) + .start(); + }); +} + +/// Test sending transaction and waiting for it to be committed to a block. +#[test] +fn test_send_tx_commit() { + test_with_client!(test_utils::NodeType::Validator, client, async move { + let block_hash = client.block(BlockReference::latest()).await.unwrap().header.hash; + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let tx = SignedTransaction::send_money( + 1, + "test1".parse().unwrap(), + "test2".parse().unwrap(), + &signer, + 100, + block_hash, + ); + let bytes = borsh::to_vec(&tx).unwrap(); + let result = client.broadcast_tx_commit(to_base64(&bytes)).await.unwrap(); + assert_eq!( + result.final_execution_outcome.unwrap().into_outcome().status, + FinalExecutionStatus::SuccessValue(Vec::new()) + ); + assert!( + [TxExecutionStatus::Executed, TxExecutionStatus::Final] + .contains(&result.final_execution_status), + "All the receipts should be already executed" + ); + }); +} + +/// Test that expired transaction should be rejected +#[test] +fn test_expired_tx() { + init_integration_logger(); + run_actix(async { + let (_, addr) = test_utils::start_all_with_validity_period_and_no_epoch_sync( + test_utils::NodeType::Validator, + 1, + false, + ); + + let block_hash = Arc::new(Mutex::new(None)); + let block_height = Arc::new(Mutex::new(None)); + + WaitOrTimeoutActor::new( + Box::new(move |_| { + let block_hash = block_hash.clone(); + let block_height = block_height.clone(); + let client = new_client(&format!("http://{}", addr)); + actix::spawn(client.block(BlockReference::latest()).then(move |res| { + let header = res.unwrap().header; + let hash = *block_hash.lock().unwrap(); + let height = *block_height.lock().unwrap(); + if let Some(block_hash) = hash { + if let Some(height) = height { + if header.height - height >= 2 { + let signer = InMemorySigner::from_seed( + "test1".parse().unwrap(), + KeyType::ED25519, + "test1", + ); + let tx = SignedTransaction::send_money( + 1, + "test1".parse().unwrap(), + "test2".parse().unwrap(), + &signer, + 100, + block_hash, + ); + let bytes = borsh::to_vec(&tx).unwrap(); + actix::spawn( + client + .broadcast_tx_commit(to_base64(&bytes)) + .map_err(|err| { + assert_eq!( + err.data.unwrap(), + serde_json::json!({"TxExecutionError": { + "InvalidTxError": "Expired" + }}) + ); + System::current().stop(); + }) + .map(|_| ()), + ); + } + } + } else { + *block_hash.lock().unwrap() = Some(header.hash); + *block_height.lock().unwrap() = Some(header.height); + }; + future::ready(()) + })); + }), + 100, + 1000, + ) + .start(); + }); +} + +/// Test sending transaction based on a different fork should be rejected +#[test] +fn test_replay_protection() { + test_with_client!(test_utils::NodeType::Validator, client, async move { + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let tx = SignedTransaction::send_money( + 1, + "test1".parse().unwrap(), + "test2".parse().unwrap(), + &signer, + 100, + hash(&[1]), + ); + let bytes = borsh::to_vec(&tx).unwrap(); + if let Ok(_) = client.broadcast_tx_commit(to_base64(&bytes)).await { + panic!("transaction should not succeed"); + } + }); +} + +#[test] +fn test_tx_status_missing_tx() { + test_with_client!(test_utils::NodeType::Validator, client, async move { + let request = RpcTransactionStatusRequest { + transaction_info: TransactionInfo::TransactionId { + tx_hash: CryptoHash::new(), + sender_account_id: "test1".parse().unwrap(), + }, + wait_until: TxExecutionStatus::None, + }; + match client.tx(request).await { + Err(e) => { + let s = serde_json::to_string(&e.data.unwrap()).unwrap(); + assert_eq!(s, "\"Transaction 11111111111111111111111111111111 doesn't exist\""); + } + Ok(_) => panic!("transaction should not succeed"), + } + }); +} + +#[test] +fn test_check_invalid_tx() { + test_with_client!(test_utils::NodeType::Validator, client, async move { + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + // invalid base hash + let request = RpcTransactionStatusRequest { + transaction_info: TransactionInfo::from_signed_tx(SignedTransaction::send_money( + 1, + "test1".parse().unwrap(), + "test2".parse().unwrap(), + &signer, + 100, + hash(&[1]), + )), + wait_until: TxExecutionStatus::None, + }; + match client.tx(request).await { + Err(e) => { + let s = serde_json::to_string(&e.data.unwrap()).unwrap(); + assert_eq!(s, "{\"TxExecutionError\":{\"InvalidTxError\":\"Expired\"}}"); + } + Ok(_) => panic!("transaction should not succeed"), + } + }); +} diff --git a/chain/jsonrpc/res/chain_n_chunk_info.css b/chain/jsonrpc/res/chain_n_chunk_info.css new file mode 100644 index 000000000..eff1995f3 --- /dev/null +++ b/chain/jsonrpc/res/chain_n_chunk_info.css @@ -0,0 +1,27 @@ +table { + width: 100%; + border-collapse: collapse; +} + +table, +th, +td { + border: 1px solid black; +} + +td { + text-align: left; + vertical-align: top; + padding: 8px; +} + +th { + text-align: center; + vertical-align: center; + padding: 8px; + background-color: lightgrey; +} + +tr.active { + background-color: #eff8bf; +} \ No newline at end of file diff --git a/chain/jsonrpc/res/chain_n_chunk_info.html b/chain/jsonrpc/res/chain_n_chunk_info.html new file mode 100644 index 000000000..22f58fb78 --- /dev/null +++ b/chain/jsonrpc/res/chain_n_chunk_info.html @@ -0,0 +1,196 @@ + + + + + + + + +

+ Welcome to the Chain & Chunk Status page! +

+

Chain Info Summary

+

+

+

+

+

+ +

Floating chunks

+
Floating chunks are the chunks for which we don't know the block they belong to yet.
+ + + + + + + + + + + + +
HeightShardIdHashCreated byStatus
+ +

Blocks

+ + + + + +
+ diff --git a/chain/jsonrpc/res/debug.html b/chain/jsonrpc/res/debug.html new file mode 100644 index 000000000..2f27210b8 --- /dev/null +++ b/chain/jsonrpc/res/debug.html @@ -0,0 +1,73 @@ + + + + + + + + +

+

Chain:

+

Protocol:

+

Binary:

+

Uptime:

+ +

+ +

Last blocks

+

Network info

+

TIER1 Network info

+

Epoch info

+

Chain & Chunk info

+

Sync info

+

Validator info

+

Client Config

+

Split Store

+ + + diff --git a/chain/jsonrpc/res/epoch_info.css b/chain/jsonrpc/res/epoch_info.css new file mode 100644 index 000000000..eff1995f3 --- /dev/null +++ b/chain/jsonrpc/res/epoch_info.css @@ -0,0 +1,27 @@ +table { + width: 100%; + border-collapse: collapse; +} + +table, +th, +td { + border: 1px solid black; +} + +td { + text-align: left; + vertical-align: top; + padding: 8px; +} + +th { + text-align: center; + vertical-align: center; + padding: 8px; + background-color: lightgrey; +} + +tr.active { + background-color: #eff8bf; +} \ No newline at end of file diff --git a/chain/jsonrpc/res/epoch_info.html b/chain/jsonrpc/res/epoch_info.html new file mode 100644 index 000000000..ea204e22a --- /dev/null +++ b/chain/jsonrpc/res/epoch_info.html @@ -0,0 +1,358 @@ + + + + + + + + + + +

Epochs

+

+ + + + + + + + + + + + + + + +
Epoch idStart heightProtocol versionFirst blockEpoch startBlock producersChunk only producers
+ +
Click table headers to toggle.
+ +

+

+

Validators

+
Validators that missed more than 20% blocks or chunks are marked red.
+ + + + + + + + + + + + + + + + +
Account idStake / Stake %Shards# Missed blocks# Expected blocksMissed blocks %# Missed chunks# Expected chunksMissed chunks %
+ +

Validators for next epoch

+ + + + + + + + + + +
AccountIdStake/Stake PercentageShards
+ +

Proposals

+ + + + + + + + + +
AccountIdStake/Stake Percentage
+ +

Previous Epoch Kickouts

+ + + + + + + + + +
AccountIdKickout Reason
+ +

Validators

+ + + + + + + + + +
Account ID
+ +

Shard sizes

+ + + + + + + + + + + + +
Epoch Id
+ Orange color means, that a given shard was requested by other peer for syncing. + + + diff --git a/chain/jsonrpc/res/last_blocks.css b/chain/jsonrpc/res/last_blocks.css new file mode 100644 index 000000000..be1355b65 --- /dev/null +++ b/chain/jsonrpc/res/last_blocks.css @@ -0,0 +1,113 @@ +.explanation { + color: gray; +} + +.missed-blocks, +.missed-chunks { + margin-top: 10px; + margin-bottom: 10px; +} + +button { + margin-right: 8px; +} + +table { + width: 100%; + border-collapse: collapse; + margin-top: 10px; +} + +table, +th, +td { + border: 1px solid black; +} + +td { + text-align: left; + padding: 8px; + vertical-align: middle; +} + +.graph-node-cell { + border: none; + text-align: center; +} + +th { + text-align: center; + vertical-align: middle; + padding: 8px; + background-color: lightgrey; +} + +.skipped-chunk { + background-color: lightgray; +} + +.hash-element { + font-family: monospace; + cursor: pointer; +} + +.validator-unavailable { + color: red; +} + +.error { + color: red; + white-space: pre; +} + +.not-on-canonical-chain { + color: gray; +} + +.missed-height { + color: gray; +} + +.hidden { + display: none; +} + +.graph-dot { + width: 12px; + height: 12px; + background-color: black; + border-radius: 100%; + display: inline-block; +} + +.graph-dot-col-1 { + margin-left: 24px; +} + +.graph-dot-col-0.graph-dot-total-2 { + margin-right: 24px; +} + +.not-on-canonical-chain .graph-dot { + background-color: gray; +} + +.head-label, +.header-head-label { + font-size: 10px; + font-family: Arial, Helvetica, sans-serif; + font-weight: bold; + color: white; + border-radius: 4px; + text-align: center; + padding: 4px; + margin-top: 2px; +} + +.head-label { + background-color: rgb(76, 208, 0); +} + +.header-head-label { + background-color: rgb(235, 215, 0); +} \ No newline at end of file diff --git a/chain/jsonrpc/res/last_blocks.html b/chain/jsonrpc/res/last_blocks.html new file mode 100644 index 000000000..164cd2b8b --- /dev/null +++ b/chain/jsonrpc/res/last_blocks.html @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + +
+ + + + + \ No newline at end of file diff --git a/chain/jsonrpc/res/last_blocks.js b/chain/jsonrpc/res/last_blocks.js new file mode 100644 index 000000000..6eab53042 --- /dev/null +++ b/chain/jsonrpc/res/last_blocks.js @@ -0,0 +1,346 @@ +function ellipsify(str, maxLen) { + if (str.length > maxLen) { + return str.substring(0, maxLen - 3) + '...'; + } + return str; +} + +// Makes an element that when clicked, expands or ellipsifies the hash and creator. +function HashElement({ hashValue, creator, expandAll, knownProducers }) { + let [expanded, setExpanded] = React.useState(false); + let updateXarrow = reactXarrow.useXarrow(); + return { + setExpanded((value) => !value); + // xarrows need to be updated whenever graph dot positions may change. + updateXarrow(); + }}> + {expanded || expandAll + ? `${hashValue} ${creator}` + : `${ellipsify(hashValue, 8)} ${ellipsify(creator, 13)}`} + ; +} + +// Sorts the API response into easily displayable rows, and computes the graph layout. +// +// Inputs: +// blocks: array of DebugBlockStatus +// missedHeights: array of MissedHeightInfo +// head: block hash of the chain's head +// headerHead: block hash of the chain's header head +// Output: array of elements where each element is either { +// block: DebugBlockStatus, +// parentIndex: number?, // the index of the parent block, or null if parent not included in the data +// graphColumn: number, // the column to display the graph node in +// blockDelay: number?, // number of seconds since parent's block timestamp, or null if parent not included in the data +// chunkSkipped: boolean[], // for each chunk, whether the chunk is the same as that chunk of parent block +// isHead: boolean, +// isHeaderHead: boolean, +// } or { missedHeight: MissedHeightInfo } +function sortBlocksAndDetermineBlockGraphLayout(blocks, missedHeights, head, headerHead) { + const rows = []; + for (let block of blocks) { + rows.push({ + block, + parentIndex: null, + graphColumn: -1, + blockDelay: null, + chunkSkipped: block.chunks.map(() => false), + isHead: head == block.block_hash, + isHeaderHead: headerHead == block.block_hash, + }); + } + for (let missedHeight of missedHeights) { + rows.push({ missedHeight }); + } + + function sortingKey(row) { + if ('block' in row) { + // some lousy tie-breaking for same-height rows. + return row.block.block_height + (row.block.block_timestamp / 1e12 % 1); + } else { + return row.missedHeight.block_height; + } + } + + rows.sort((a, b) => sortingKey(b) - sortingKey(a)); + + const rowIndexByHash = new Map(); + rows.forEach((row, rowIndex) => { + if ('block' in row) { + rowIndexByHash.set(row.block.block_hash, rowIndex); + } + }); + + let highestNodeOnFirstColumn = rows.length; + for (let i = rows.length - 1; i >= 0; i--) { + let row = rows[i]; + if ('missedHeight' in row) { + continue; + } + const block = row.block; + + // Look up parent index, and also compute things that depend on the parent block. + if (rowIndexByHash.has(block.prev_block_hash)) { + row.parentIndex = rowIndexByHash.get(block.prev_block_hash); + const parent = rows[row.parentIndex]; + row.blockDelay = (block.block_timestamp - parent.block.block_timestamp) / 1e9; + for (let j = 0; + j < Math.min(block.chunks.length, parent.block.chunks.length); + j++) { + row.chunkSkipped[j] = + block.chunks[j].chunk_hash == parent.block.chunks[j].chunk_hash; + } + } + // We'll use a two-column layout for the block graph. We traverse from bottom + // up (oldest block to latest), and for each row we pick the first column unless + // that would make us draw a line (from the parent to this node) through another + // node; in which case we would pick the second column. To do that we just need + // to keep track of the highest node we've seen so far for the first column. + // + // Not the best layout for a graph, but it's sufficient since we rarely have forks. + let column = 0; + if (row.parentIndex != null && + rows[row.parentIndex].graphColumn == 0 && + row.parentIndex > highestNodeOnFirstColumn) { + column = 1; + } else { + highestNodeOnFirstColumn = i; + } + row.graphColumn = column; + } + return rows; +} + +function BlocksTable({ rows, knownProducers, expandAll, hideMissingHeights }) { + let numGraphColumns = 1; // either 1 or 2; determines the width of leftmost td + let numShards = 0; + for (let row of rows) { + if ('block' in row) { + numGraphColumns = Math.max(numGraphColumns, row.graphColumn + 1); + for (let chunk of row.block.chunks) { + numShards = Math.max(numShards, chunk.shard_id + 1); + } + } + } + const header = + Chain + Height + {'Hash & creator'} + Processing Time (ms) + Block Delay (s) + Gas price ratio + {[...Array(numShards).keys()].map(i => + Shard {i} (hash/gas(Tgas)/time(ms)))} + ; + + // One xarrow element per arrow (from block to block). + const graphArrows = []; + + // One 'tr' element per row. + const tableRows = []; + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + if ('missedHeight' in row) { + if (!hideMissingHeights) { + tableRows.push( + + {row.missedHeight.block_height} + {row.missedHeight.block_producer} missed block + ); + } + continue; + } + let block = row.block; + + const chunkCells = []; + block.chunks.forEach((chunk, shardId) => { + chunkCells.push( + + + + {(chunk.gas_used / (1024 * 1024 * 1024 * 1024)).toFixed(1)} + {chunk.processing_time_ms} + ); + }); + + tableRows.push( + + +
+
+ + + {block.block_height} + {row.isHead &&
HEAD
} + {row.isHeaderHead &&
HEADER HEAD
} + + + + + {block.processing_time_ms} + {row.blockDelay ?? ''} + {block.gas_price_ratio} + {block.full_block_missing && header only} + {chunkCells} + ); + if (row.parentIndex != null) { + graphArrows.push(); + } + } + return
+ {graphArrows} + + + {header} + {tableRows} + +
+
+} + +function Page() { + const [rows, setRows] = React.useState([]); + const [error, setError] = React.useState(null); + const [knownProducers, setKnownProducers] = React.useState(new Set()); + const [expandAll, setExpandAll] = React.useState(false); + const [hideMissingHeights, setHideMissingHeights] = React.useState(false); + const updateXarrow = reactXarrow.useXarrow(); + let blockStatusApiPath = '../api/block_status'; + const url = new URL(window.location.toString()); + let title = 'Most Recent Blocks'; + if (url.searchParams.has('height')) { + blockStatusApiPath += '/' + url.searchParams.get('height'); + title = 'Blocks from ' + url.searchParams.get('height'); + } + // useEffect with empty dependency list means to run this once at beginning. + React.useEffect(() => { + (async () => { + try { + let resp = await fetch('../api/status'); + if (resp.status == 405) { + throw new Error('Debug not allowed - did you set enable_debug_rpc: true in your config?'); + } else if (!resp.ok) { + throw new Error('Debug API call failed: ' + resp.statusText); + } + const { detailed_debug_status: { network_info: { known_producers } } } = await resp.json(); + const knownProducerSet = new Set(); + for (const producer of known_producers) { + knownProducerSet.add(producer.account_id); + } + setKnownProducers(knownProducerSet); + + resp = await fetch(blockStatusApiPath); + if (!resp.ok) { + throw new Error('Could not fetch block debug status: ' + resp.statusText); + } + const { status_response: { BlockStatus: data } } = await resp.json(); + setRows(sortBlocksAndDetermineBlockGraphLayout( + data.blocks, + data.missed_heights, + data.head, + data.header_head)); + } catch (error) { + setError(error); + } + })(); + }, []); + + // Compute missing blocks and chunks statistics (whenever rows changes). + const { numCanonicalBlocks, canonicalHeightCount, numChunksSkipped } = React.useMemo(() => { + let firstCanonicalHeight = 0; + let lastCanonicalHeight = 0; + let numCanonicalBlocks = 0; + const numChunksSkipped = []; + for (const row of rows) { + if (!('block' in row)) { + continue; + } + const block = row.block; + if (!block.is_on_canonical_chain) { + continue; + } + if (firstCanonicalHeight == 0) { + firstCanonicalHeight = block.block_height; + } + lastCanonicalHeight = block.block_height; + numCanonicalBlocks++; + for (let i = 0; i < row.chunkSkipped.length; i++) { + while (numChunksSkipped.length < i + 1) { + numChunksSkipped.push(0); + } + if (row.chunkSkipped[i]) { + numChunksSkipped[i]++; + } + } + } + return { + numCanonicalBlocks, + canonicalHeightCount: firstCanonicalHeight - lastCanonicalHeight + 1, + numChunksSkipped, + }; + }, [rows]); + + return +

{title}

+
Skipped chunks have grey background.
+
+ Red text means that we don't know this producer + (it's not present in our announce account list). +
+ {error &&
{error.stack}
} +
+ Missing blocks: {canonicalHeightCount - numCanonicalBlocks} { } + Produced: {numCanonicalBlocks} { } + Missing Rate: {((canonicalHeightCount - numCanonicalBlocks) / canonicalHeightCount * 100).toFixed(2)}% +
+
+ {numChunksSkipped.map((numSkipped, shardId) => +
+ Shard {shardId}: Missing chunks: {numSkipped} { } + Produced: {numCanonicalBlocks - numSkipped} { } + Missing Rate: {(numSkipped / numCanonicalBlocks * 100).toFixed(2)}% +
)} +
+ + + + +
; +} + +ReactDOM + .createRoot(document.getElementById('react-container')) + .render(); diff --git a/chain/jsonrpc/res/network_info.css b/chain/jsonrpc/res/network_info.css new file mode 100644 index 000000000..727b21c28 --- /dev/null +++ b/chain/jsonrpc/res/network_info.css @@ -0,0 +1,51 @@ +table { + width: 100%; + border-collapse: collapse; +} + +table, +th, +td { + border: 1px solid black; +} + +td { + text-align: left; + vertical-align: top; + padding: 8px; +} + +th { + text-align: center; + vertical-align: center; + padding: 8px; + background-color: lightgrey; +} + +tr.active { + background-color: #eff8bf; +} + +.peer_in_sync { + background-color: green; +} + +.peer_ahead { + background-color: lightblue; +} + +.peer_ahead_alot { + background-color: blueviolet; +} + +.peer_behind_a_little { + background-color: yellowgreen; +} + +.peer_behind { + background-color: yellow; +} + +.peer_far_behind { + background-color: red; +} diff --git a/chain/jsonrpc/res/network_info.html b/chain/jsonrpc/res/network_info.html new file mode 100644 index 000000000..1a23c4f9b --- /dev/null +++ b/chain/jsonrpc/res/network_info.html @@ -0,0 +1,339 @@ + + + + + + + + + + +

+ Welcome to the Network Info page! +

+

+

+ PeerId: + +

+

+ Current Sync Status: + +

+ +

+ Number of peers: / +

+

+ Block Producers: Known: + Reachable: +

+

+ +
+Unknown: 
+Unreachable: 
+    
+

+

+ Chunk Producers: Known: + Reachable: +

+

+
+Unknown: 
+Unreachable: 
+    
+ + Unknown means that we didn't receive 'announce' information about this validator (so we don't know on which + peer it + is). This usually means that the validator didn't connect to the network + during current epoch. + +
+ + Unreachable means, that we know the peer_id of this validator, but we cannot find it in our routing table. + This + usually means that validator did connect + to the network in the past, but now it is gone for at least 1 hour. +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + +
AddressValidator?PeerIdLast pingHeightLast Block HashTracked ShardsArchivalConnection typeNonceFirst connectionTraffic (last minute)Route to validators
+
+ +
+

Peers in storage:

+ + + + + + + + + + + + +
Peer idPeer addressFirst seenLast seenLast connection attemptStatus
+
+
+ +
+

Outbound connections in storage:

+ + + + + + + + + + +
Peer idPeer addressFirst connectedLast connected
+
+ + + diff --git a/chain/jsonrpc/res/network_info.js b/chain/jsonrpc/res/network_info.js new file mode 100644 index 000000000..e51467144 --- /dev/null +++ b/chain/jsonrpc/res/network_info.js @@ -0,0 +1,123 @@ +function convertTime(millis) { + if (millis == null) { + return '(null)'; + } + let total_seconds = Math.floor(millis / 1000); + let hours = Math.floor(total_seconds / 3600) + let minutes = Math.floor((total_seconds - (hours * 3600)) / 60) + let seconds = total_seconds - (hours * 3600) - (minutes * 60) + if (hours > 0) { + if (minutes > 0) { + return `${hours}h ${minutes}m ${seconds}s` + } else { + return `${hours}h ${seconds}s` + } + } + if (minutes > 0) { + return `${minutes}m ${seconds}s` + } + return `${seconds}s` +} + +function convertBps(bytes_per_second) { + if (bytes_per_second == null) { + return '-' + } + if (bytes_per_second < 3000) { + return `${bytes_per_second} bps` + } [] + let kilobytes_per_second = bytes_per_second / 1024; + if (kilobytes_per_second < 3000) { + return `${kilobytes_per_second.toFixed(1)} Kbps` + } + let megabytes_per_second = kilobytes_per_second / 1024; + return `${megabytes_per_second.toFixed(1)} Mbps` +} + +function computeTraffic(bytes_received, bytes_sent) { + return "⬇ " + convertBps(bytes_received) + "
⬆ " + convertBps(bytes_sent); +} + +function add_debug_port_link(peer_network_addr) { + // Each node running in a machine is assigned ports 24567 + peer_num and 3030 + peer_num, whereby peer_num is a whole number + // peer_rpc_address is not shared between peer nodes. Hence, it cannot be programmatically fetched. + // https://github.com/utnet-org/utility/blob/700ec29270f72f2e78a17029b4799a8228926c07/chain/network/src/network_protocol/peer.rs#L13-L19 + DEFAULT_RPC_PORT = 3030 + DEFAULT_NETWORK_PORT = 24567 + peer_network_addr_array = peer_network_addr.split(":") + peer_network_port = peer_network_addr_array.pop() + peer_network_ip = peer_network_addr_array.pop() + peer_num = 0; + if (peer_network_ip.includes("127.0.0.1")) { + peer_num = peer_network_port - DEFAULT_NETWORK_PORT; + } + peer_rpc_port = DEFAULT_RPC_PORT + peer_num; + peer_rpc_address = "http://" + peer_network_addr.replace(/:.*/, ":") + peer_rpc_port + "/debug" + return $('', { + href: peer_rpc_address, + text: peer_network_addr + }); +} + +function displayHash(peer) { + if (peer.is_highest_block_invalid) { + return peer.block_hash + " (INVALID)" + } else { + return peer.block_hash + " (Valid)" + } +} + +function peerClass(current_height, peer_height) { + if (peer_height > current_height + 5) { + return 'peer_ahead_alot'; + } + if (peer_height > current_height + 2) { + return 'peer_ahead'; + } + + if (peer_height < current_height - 100) { + return 'peer_far_behind'; + } + if (peer_height < current_height - 10) { + return 'peer_behind'; + } + if (peer_height < current_height - 3) { + return 'peer_behind_a_little'; + } + return 'peer_in_sync'; +} + +function getIntersection(setA, setB) { + const intersection = new Set( + [...setA].filter(element => setB.has(element)) + ); + + return intersection; +} + +function getDifference(setA, setB) { + return new Set( + [...setA].filter(element => !setB.has(element)) + ); +} + +function to_human_time(seconds) { + let result = ""; + if (seconds >= 60) { + let minutes = Math.floor(seconds / 60); + seconds = seconds % 60; + if (minutes > 60) { + let hours = Math.floor(minutes / 60); + minutes = minutes % 60; + if (hours > 24) { + let days = Math.floor(hours / 24); + hours = hours % 24; + result += days + " days "; + } + result += hours + " h "; + } + result += minutes + " m "; + } + result += seconds + " s" + return result; +} diff --git a/chain/jsonrpc/res/rpc_errors_schema.json b/chain/jsonrpc/res/rpc_errors_schema.json new file mode 100644 index 000000000..1279c6175 --- /dev/null +++ b/chain/jsonrpc/res/rpc_errors_schema.json @@ -0,0 +1,915 @@ +{ + "schema": { + "AccessKeyNotFound": { + "name": "AccessKeyNotFound", + "subtypes": [], + "props": { + "account_id": "", + "public_key": "" + } + }, + "AccountAlreadyExists": { + "name": "AccountAlreadyExists", + "subtypes": [], + "props": { + "account_id": "" + } + }, + "AccountDoesNotExist": { + "name": "AccountDoesNotExist", + "subtypes": [], + "props": { + "account_id": "" + } + }, + "ActionError": { + "name": "ActionError", + "subtypes": [ + "AccountAlreadyExists", + "AccountDoesNotExist", + "CreateAccountOnlyByRegistrar", + "CreateAccountNotAllowed", + "ActorNoPermission", + "DeleteKeyDoesNotExist", + "AddKeyAlreadyExists", + "DeleteAccountStaking", + "LackBalanceForState", + "TriesToUnstake", + "TriesToStake", + "InsufficientStake", + "FunctionCallError", + "NewReceiptValidationError", + "OnlyImplicitAccountCreationAllowed", + "DeleteAccountWithLargeState", + "DelegateActionInvalidSignature", + "DelegateActionSenderDoesNotMatchTxReceiver", + "DelegateActionExpired", + "DelegateActionAccessKeyError", + "DelegateActionInvalidNonce", + "DelegateActionNonceTooLarge" + ], + "props": { + "index": "" + } + }, + "ActionsValidationError": { + "name": "ActionsValidationError", + "subtypes": [ + "DeleteActionMustBeFinal", + "TotalPrepaidGasExceeded", + "TotalNumberOfActionsExceeded", + "AddKeyMethodNamesNumberOfBytesExceeded", + "AddKeyMethodNameLengthExceeded", + "IntegerOverflow", + "InvalidAccountId", + "ContractSizeExceeded", + "FunctionCallMethodNameLengthExceeded", + "FunctionCallArgumentsLengthExceeded", + "UnsuitableStakingKey", + "FunctionCallZeroAttachedGas", + "DelegateActionMustBeOnlyOne", + "UnsupportedProtocolFeature" + ], + "props": {} + }, + "ActorNoPermission": { + "name": "ActorNoPermission", + "subtypes": [], + "props": { + "account_id": "", + "actor_id": "" + } + }, + "AddKeyAlreadyExists": { + "name": "AddKeyAlreadyExists", + "subtypes": [], + "props": { + "account_id": "", + "public_key": "" + } + }, + "AddKeyMethodNameLengthExceeded": { + "name": "AddKeyMethodNameLengthExceeded", + "subtypes": [], + "props": { + "length": "", + "limit": "" + } + }, + "AddKeyMethodNamesNumberOfBytesExceeded": { + "name": "AddKeyMethodNamesNumberOfBytesExceeded", + "subtypes": [], + "props": { + "limit": "", + "total_number_of_bytes": "" + } + }, + "AltBn128InvalidInput": { + "name": "AltBn128InvalidInput", + "subtypes": [], + "props": { + "msg": "" + } + }, + "BadUTF16": { + "name": "BadUTF16", + "subtypes": [], + "props": {} + }, + "BadUTF8": { + "name": "BadUTF8", + "subtypes": [], + "props": {} + }, + "BalanceExceeded": { + "name": "BalanceExceeded", + "subtypes": [], + "props": {} + }, + "BalanceMismatchError": { + "name": "BalanceMismatchError", + "subtypes": [], + "props": { + "final_accounts_balance": "", + "final_postponed_receipts_balance": "", + "incoming_receipts_balance": "", + "incoming_validator_rewards": "", + "initial_accounts_balance": "", + "initial_postponed_receipts_balance": "", + "new_delayed_receipts_balance": "", + "other_burnt_amount": "", + "outgoing_receipts_balance": "", + "processed_delayed_receipts_balance": "", + "slashed_burnt_amount": "", + "tx_burnt_amount": "" + } + }, + "CallIndirectOOB": { + "name": "CallIndirectOOB", + "subtypes": [], + "props": {} + }, + "CannotAppendActionToJointPromise": { + "name": "CannotAppendActionToJointPromise", + "subtypes": [], + "props": {} + }, + "CannotReturnJointPromise": { + "name": "CannotReturnJointPromise", + "subtypes": [], + "props": {} + }, + "CodeDoesNotExist": { + "name": "CodeDoesNotExist", + "subtypes": [], + "props": { + "account_id": "" + } + }, + "CompilationError": { + "name": "CompilationError", + "subtypes": [ + "CodeDoesNotExist", + "PrepareError", + "WasmerCompileError" + ], + "props": {} + }, + "ContractSizeExceeded": { + "name": "ContractSizeExceeded", + "subtypes": [], + "props": { + "limit": "", + "size": "" + } + }, + "CostOverflow": { + "name": "CostOverflow", + "subtypes": [], + "props": {} + }, + "CreateAccountNotAllowed": { + "name": "CreateAccountNotAllowed", + "subtypes": [], + "props": { + "account_id": "", + "predecessor_id": "" + } + }, + "CreateAccountOnlyByRegistrar": { + "name": "CreateAccountOnlyByRegistrar", + "subtypes": [], + "props": { + "account_id": "", + "predecessor_id": "", + "registrar_account_id": "" + } + }, + "DelegateActionExpired": { + "name": "DelegateActionExpired", + "subtypes": [], + "props": {} + }, + "DelegateActionInvalidNonce": { + "name": "DelegateActionInvalidNonce", + "subtypes": [], + "props": { + "ak_nonce": "", + "delegate_nonce": "" + } + }, + "DelegateActionInvalidSignature": { + "name": "DelegateActionInvalidSignature", + "subtypes": [], + "props": {} + }, + "DelegateActionMustBeOnlyOne": { + "name": "DelegateActionMustBeOnlyOne", + "subtypes": [], + "props": {} + }, + "DelegateActionNonceTooLarge": { + "name": "DelegateActionNonceTooLarge", + "subtypes": [], + "props": { + "delegate_nonce": "", + "upper_bound": "" + } + }, + "DelegateActionSenderDoesNotMatchTxReceiver": { + "name": "DelegateActionSenderDoesNotMatchTxReceiver", + "subtypes": [], + "props": { + "receiver_id": "", + "sender_id": "" + } + }, + "DeleteAccountStaking": { + "name": "DeleteAccountStaking", + "subtypes": [], + "props": { + "account_id": "" + } + }, + "DeleteAccountWithLargeState": { + "name": "DeleteAccountWithLargeState", + "subtypes": [], + "props": { + "account_id": "" + } + }, + "DeleteActionMustBeFinal": { + "name": "DeleteActionMustBeFinal", + "subtypes": [], + "props": {} + }, + "DeleteKeyDoesNotExist": { + "name": "DeleteKeyDoesNotExist", + "subtypes": [], + "props": { + "account_id": "", + "public_key": "" + } + }, + "DepositWithFunctionCall": { + "name": "DepositWithFunctionCall", + "subtypes": [], + "props": {} + }, + "Deprecated": { + "name": "Deprecated", + "subtypes": [], + "props": { + "method_name": "" + } + }, + "Deserialization": { + "name": "Deserialization", + "subtypes": [], + "props": {} + }, + "ECRecoverError": { + "name": "ECRecoverError", + "subtypes": [], + "props": { + "msg": "" + } + }, + "Ed25519VerifyInvalidInput": { + "name": "Ed25519VerifyInvalidInput", + "subtypes": [], + "props": { + "msg": "" + } + }, + "EmptyMethodName": { + "name": "EmptyMethodName", + "subtypes": [], + "props": {} + }, + "Expired": { + "name": "Expired", + "subtypes": [], + "props": {} + }, + "FunctionCallArgumentsLengthExceeded": { + "name": "FunctionCallArgumentsLengthExceeded", + "subtypes": [], + "props": { + "length": "", + "limit": "" + } + }, + "FunctionCallMethodNameLengthExceeded": { + "name": "FunctionCallMethodNameLengthExceeded", + "subtypes": [], + "props": { + "length": "", + "limit": "" + } + }, + "FunctionCallZeroAttachedGas": { + "name": "FunctionCallZeroAttachedGas", + "subtypes": [], + "props": {} + }, + "GasExceeded": { + "name": "GasExceeded", + "subtypes": [], + "props": {} + }, + "GasInstrumentation": { + "name": "GasInstrumentation", + "subtypes": [], + "props": {} + }, + "GasLimitExceeded": { + "name": "GasLimitExceeded", + "subtypes": [], + "props": {} + }, + "GenericTrap": { + "name": "GenericTrap", + "subtypes": [], + "props": {} + }, + "GuestPanic": { + "name": "GuestPanic", + "subtypes": [], + "props": { + "panic_msg": "" + } + }, + "HostError": { + "name": "HostError", + "subtypes": [ + "BadUTF16", + "BadUTF8", + "GasExceeded", + "GasLimitExceeded", + "BalanceExceeded", + "EmptyMethodName", + "GuestPanic", + "IntegerOverflow", + "InvalidPromiseIndex", + "CannotAppendActionToJointPromise", + "CannotReturnJointPromise", + "InvalidPromiseResultIndex", + "InvalidRegisterId", + "IteratorWasInvalidated", + "MemoryAccessViolation", + "InvalidReceiptIndex", + "InvalidIteratorIndex", + "InvalidAccountId", + "InvalidMethodName", + "InvalidPublicKey", + "ProhibitedInView", + "NumberOfLogsExceeded", + "KeyLengthExceeded", + "ValueLengthExceeded", + "TotalLogLengthExceeded", + "NumberPromisesExceeded", + "NumberInputDataDependenciesExceeded", + "ReturnedValueLengthExceeded", + "ContractSizeExceeded", + "Deprecated", + "ECRecoverError", + "AltBn128InvalidInput", + "Ed25519VerifyInvalidInput" + ], + "props": {} + }, + "IllegalArithmetic": { + "name": "IllegalArithmetic", + "subtypes": [], + "props": {} + }, + "IncorrectCallIndirectSignature": { + "name": "IncorrectCallIndirectSignature", + "subtypes": [], + "props": {} + }, + "IndirectCallToNull": { + "name": "IndirectCallToNull", + "subtypes": [], + "props": {} + }, + "Instantiate": { + "name": "Instantiate", + "subtypes": [], + "props": {} + }, + "InsufficientStake": { + "name": "InsufficientStake", + "subtypes": [], + "props": { + "account_id": "", + "minimum_stake": "", + "stake": "" + } + }, + "IntegerOverflow": { + "name": "IntegerOverflow", + "subtypes": [], + "props": {} + }, + "InternalMemoryDeclared": { + "name": "InternalMemoryDeclared", + "subtypes": [], + "props": {} + }, + "InvalidAccessKeyError": { + "name": "InvalidAccessKeyError", + "subtypes": [ + "AccessKeyNotFound", + "ReceiverMismatch", + "MethodNameMismatch", + "RequiresFullAccess", + "NotEnoughAllowance", + "DepositWithFunctionCall" + ], + "props": {} + }, + "InvalidAccountId": { + "name": "InvalidAccountId", + "subtypes": [], + "props": {} + }, + "InvalidChain": { + "name": "InvalidChain", + "subtypes": [], + "props": {} + }, + "InvalidDataReceiverId": { + "name": "InvalidDataReceiverId", + "subtypes": [], + "props": { + "account_id": "" + } + }, + "InvalidIteratorIndex": { + "name": "InvalidIteratorIndex", + "subtypes": [], + "props": { + "iterator_index": "" + } + }, + "InvalidMethodName": { + "name": "InvalidMethodName", + "subtypes": [], + "props": {} + }, + "InvalidNonce": { + "name": "InvalidNonce", + "subtypes": [], + "props": { + "ak_nonce": "", + "tx_nonce": "" + } + }, + "InvalidPredecessorId": { + "name": "InvalidPredecessorId", + "subtypes": [], + "props": { + "account_id": "" + } + }, + "InvalidPromiseIndex": { + "name": "InvalidPromiseIndex", + "subtypes": [], + "props": { + "promise_idx": "" + } + }, + "InvalidPromiseResultIndex": { + "name": "InvalidPromiseResultIndex", + "subtypes": [], + "props": { + "result_idx": "" + } + }, + "InvalidPublicKey": { + "name": "InvalidPublicKey", + "subtypes": [], + "props": {} + }, + "InvalidReceiptIndex": { + "name": "InvalidReceiptIndex", + "subtypes": [], + "props": { + "receipt_index": "" + } + }, + "InvalidReceiverId": { + "name": "InvalidReceiverId", + "subtypes": [], + "props": { + "account_id": "" + } + }, + "InvalidRegisterId": { + "name": "InvalidRegisterId", + "subtypes": [], + "props": { + "register_id": "" + } + }, + "InvalidSignature": { + "name": "InvalidSignature", + "subtypes": [], + "props": {} + }, + "InvalidSignerId": { + "name": "InvalidSignerId", + "subtypes": [], + "props": { + "account_id": "" + } + }, + "InvalidTxError": { + "name": "InvalidTxError", + "subtypes": [ + "InvalidAccessKeyError", + "InvalidSignerId", + "SignerDoesNotExist", + "InvalidNonce", + "NonceTooLarge", + "InvalidReceiverId", + "InvalidSignature", + "NotEnoughBalance", + "LackBalanceForState", + "CostOverflow", + "InvalidChain", + "Expired", + "ActionsValidation", + "TransactionSizeExceeded" + ], + "props": {} + }, + "IteratorWasInvalidated": { + "name": "IteratorWasInvalidated", + "subtypes": [], + "props": { + "iterator_index": "" + } + }, + "KeyLengthExceeded": { + "name": "KeyLengthExceeded", + "subtypes": [], + "props": { + "length": "", + "limit": "" + } + }, + "LackBalanceForState": { + "name": "LackBalanceForState", + "subtypes": [], + "props": { + "account_id": "", + "amount": "" + } + }, + "Memory": { + "name": "Memory", + "subtypes": [], + "props": {} + }, + "MemoryAccessViolation": { + "name": "MemoryAccessViolation", + "subtypes": [], + "props": {} + }, + "MemoryOutOfBounds": { + "name": "MemoryOutOfBounds", + "subtypes": [], + "props": {} + }, + "MethodEmptyName": { + "name": "MethodEmptyName", + "subtypes": [], + "props": {} + }, + "MethodInvalidSignature": { + "name": "MethodInvalidSignature", + "subtypes": [], + "props": {} + }, + "MethodNameMismatch": { + "name": "MethodNameMismatch", + "subtypes": [], + "props": { + "method_name": "" + } + }, + "MethodNotFound": { + "name": "MethodNotFound", + "subtypes": [], + "props": {} + }, + "MethodResolveError": { + "name": "MethodResolveError", + "subtypes": [ + "MethodEmptyName", + "MethodNotFound", + "MethodInvalidSignature" + ], + "props": {} + }, + "MisalignedAtomicAccess": { + "name": "MisalignedAtomicAccess", + "subtypes": [], + "props": {} + }, + "NonceTooLarge": { + "name": "NonceTooLarge", + "subtypes": [], + "props": { + "tx_nonce": "", + "upper_bound": "" + } + }, + "NotEnoughAllowance": { + "name": "NotEnoughAllowance", + "subtypes": [], + "props": { + "account_id": "", + "allowance": "", + "cost": "", + "public_key": "" + } + }, + "NotEnoughBalance": { + "name": "NotEnoughBalance", + "subtypes": [], + "props": { + "balance": "", + "cost": "", + "signer_id": "" + } + }, + "NumberInputDataDependenciesExceeded": { + "name": "NumberInputDataDependenciesExceeded", + "subtypes": [], + "props": { + "limit": "", + "number_of_input_data_dependencies": "" + } + }, + "NumberOfLogsExceeded": { + "name": "NumberOfLogsExceeded", + "subtypes": [], + "props": { + "limit": "" + } + }, + "NumberPromisesExceeded": { + "name": "NumberPromisesExceeded", + "subtypes": [], + "props": { + "limit": "", + "number_of_promises": "" + } + }, + "OnlyImplicitAccountCreationAllowed": { + "name": "OnlyImplicitAccountCreationAllowed", + "subtypes": [], + "props": { + "account_id": "" + } + }, + "PrepareError": { + "name": "PrepareError", + "subtypes": [ + "Serialization", + "Deserialization", + "InternalMemoryDeclared", + "GasInstrumentation", + "StackHeightInstrumentation", + "Instantiate", + "Memory", + "TooManyFunctions", + "TooManyLocals" + ], + "props": {} + }, + "ProhibitedInView": { + "name": "ProhibitedInView", + "subtypes": [], + "props": { + "method_name": "" + } + }, + "ReceiptValidationError": { + "name": "ReceiptValidationError", + "subtypes": [ + "InvalidPredecessorId", + "InvalidReceiverId", + "InvalidSignerId", + "InvalidDataReceiverId", + "ReturnedValueLengthExceeded", + "NumberInputDataDependenciesExceeded", + "ActionsValidation" + ], + "props": {} + }, + "ReceiverMismatch": { + "name": "ReceiverMismatch", + "subtypes": [], + "props": { + "ak_receiver": "", + "tx_receiver": "" + } + }, + "RequiresFullAccess": { + "name": "RequiresFullAccess", + "subtypes": [], + "props": {} + }, + "ReturnedValueLengthExceeded": { + "name": "ReturnedValueLengthExceeded", + "subtypes": [], + "props": { + "length": "", + "limit": "" + } + }, + "Serialization": { + "name": "Serialization", + "subtypes": [], + "props": {} + }, + "SignerDoesNotExist": { + "name": "SignerDoesNotExist", + "subtypes": [], + "props": { + "signer_id": "" + } + }, + "StackHeightInstrumentation": { + "name": "StackHeightInstrumentation", + "subtypes": [], + "props": {} + }, + "StackOverflow": { + "name": "StackOverflow", + "subtypes": [], + "props": {} + }, + "TooManyFunctions": { + "name": "TooManyFunctions", + "subtypes": [], + "props": {} + }, + "TooManyLocals": { + "name": "TooManyLocals", + "subtypes": [], + "props": {} + }, + "TotalLogLengthExceeded": { + "name": "TotalLogLengthExceeded", + "subtypes": [], + "props": { + "length": "", + "limit": "" + } + }, + "TotalNumberOfActionsExceeded": { + "name": "TotalNumberOfActionsExceeded", + "subtypes": [], + "props": { + "limit": "", + "total_number_of_actions": "" + } + }, + "TotalPrepaidGasExceeded": { + "name": "TotalPrepaidGasExceeded", + "subtypes": [], + "props": { + "limit": "", + "total_prepaid_gas": "" + } + }, + "TransactionSizeExceeded": { + "name": "TransactionSizeExceeded", + "subtypes": [], + "props": { + "limit": "", + "size": "" + } + }, + "TriesToStake": { + "name": "TriesToStake", + "subtypes": [], + "props": { + "account_id": "", + "balance": "", + "locked": "", + "stake": "" + } + }, + "TriesToUnstake": { + "name": "TriesToUnstake", + "subtypes": [], + "props": { + "account_id": "" + } + }, + "TxExecutionError": { + "name": "TxExecutionError", + "subtypes": [ + "ActionError", + "InvalidTxError" + ], + "props": {} + }, + "Unreachable": { + "name": "Unreachable", + "subtypes": [], + "props": {} + }, + "UnsuitableStakingKey": { + "name": "UnsuitableStakingKey", + "subtypes": [], + "props": { + "public_key": "" + } + }, + "UnsupportedProtocolFeature": { + "name": "UnsupportedProtocolFeature", + "subtypes": [], + "props": { + "protocol_feature": "", + "version": "" + } + }, + "ValueLengthExceeded": { + "name": "ValueLengthExceeded", + "subtypes": [], + "props": { + "length": "", + "limit": "" + } + }, + "WasmTrap": { + "name": "WasmTrap", + "subtypes": [ + "Unreachable", + "IncorrectCallIndirectSignature", + "MemoryOutOfBounds", + "CallIndirectOOB", + "IllegalArithmetic", + "MisalignedAtomicAccess", + "IndirectCallToNull", + "StackOverflow", + "GenericTrap" + ], + "props": {} + }, + "WasmerCompileError": { + "name": "WasmerCompileError", + "subtypes": [], + "props": { + "msg": "" + } + }, + "Closed": { + "name": "Closed", + "subtypes": [], + "props": {} + }, + "ServerError": { + "name": "ServerError", + "subtypes": [ + "TxExecutionError", + "Timeout", + "Closed" + ], + "props": {} + }, + "Timeout": { + "name": "Timeout", + "subtypes": [], + "props": {} + } + } +} \ No newline at end of file diff --git a/chain/jsonrpc/res/split_store.html b/chain/jsonrpc/res/split_store.html new file mode 100644 index 000000000..2b671b33d --- /dev/null +++ b/chain/jsonrpc/res/split_store.html @@ -0,0 +1,33 @@ + + + + Split Store + + + +

+ Split Store +

+ +
    +
  • Head height:
  • +
  • Cold head height:
  • +
  • Final head height:
  • +
  • Hot db kind:
  • +
+ + + + + diff --git a/chain/jsonrpc/res/sync.css b/chain/jsonrpc/res/sync.css new file mode 100644 index 000000000..eff1995f3 --- /dev/null +++ b/chain/jsonrpc/res/sync.css @@ -0,0 +1,27 @@ +table { + width: 100%; + border-collapse: collapse; +} + +table, +th, +td { + border: 1px solid black; +} + +td { + text-align: left; + vertical-align: top; + padding: 8px; +} + +th { + text-align: center; + vertical-align: center; + padding: 8px; + background-color: lightgrey; +} + +tr.active { + background-color: #eff8bf; +} \ No newline at end of file diff --git a/chain/jsonrpc/res/sync.html b/chain/jsonrpc/res/sync.html new file mode 100644 index 000000000..5a81028d3 --- /dev/null +++ b/chain/jsonrpc/res/sync.html @@ -0,0 +1,247 @@ + + + + + + + + + +

+ Sync page +

+
+

+

Tracked shards

+

+ + + + + + + + +
Epoch
+
+

+

+ +

+

+ +

+

+ +

+

+
+

+

Progress

+

+ + + + + + + + + + +
ShardProgressStatus
+
+

+

Catchup

+

+

+ +

+ + Header sync is a fast process, where we fetch 512 'headers' at a time from the network (basically header consists of + a few hashes). +
+ State sync is downloading the current storage state - it can take couple GB for each shard that you're tracking. +
+ Block sync is executing all the transactions in the a block (which might take up to 1 second/block) - can be faster + if blocks are not full. + + + + diff --git a/chain/jsonrpc/res/tier1_network_info.html b/chain/jsonrpc/res/tier1_network_info.html new file mode 100644 index 000000000..e57a599af --- /dev/null +++ b/chain/jsonrpc/res/tier1_network_info.html @@ -0,0 +1,161 @@ + + + + + + + + + + +

+ Welcome to the TIER1 Network Info page! +

+ + + + + + + + + + + + + + + + + + + +
AddressAccountKeyAccountIdProxiesPeerIdLast pingTracked ShardsArchivalConnection typeFirst connectionTraffic (last minute)
+ + + diff --git a/chain/jsonrpc/res/validator.css b/chain/jsonrpc/res/validator.css new file mode 100644 index 000000000..989dffc7c --- /dev/null +++ b/chain/jsonrpc/res/validator.css @@ -0,0 +1,88 @@ +table { + width: 100%; + border-collapse: collapse; +} + +table, +th, +td { + border: 1px solid black; +} + +td { + text-align: left; + vertical-align: top; + padding: 8px; +} + +th { + text-align: center; + vertical-align: center; + padding: 8px; + background-color: lightgrey; +} + +tr.active { + background-color: #eff8bf; +} + +.approval-skip { + background-color: yellow; +} + +.approval-target-rollback { + background-color: red; +} + +.approval-ok { + background-color: lightgreen; +} + +.client-actor-delayed { + font-size: x-large; + font-weight: bold; +} + +.validator-missing { + background-color: grey; +} + +.validator-endorse { + background-color: lightgreen; +} + +.validator-skip-0 { + background-color: #75f09e33; +} + +.validator-skip-1 { + background-color: #ffce5633; +} + +.validator-skip-2 { + background-color: #36a2eb33; +} + +.validator-skip-3 { + background-color: #4bc0c033; +} + +.validator-skip-4 { + background-color: #ff641e33; +} + +.chunk-delay-orange { + color: orange; +} + +.chunk-delay-red { + color: red; +} + +.chunk-missing { + background-color: grey; +} + +.block-missing { + background-color: grey; +} \ No newline at end of file diff --git a/chain/jsonrpc/res/validator.html b/chain/jsonrpc/res/validator.html new file mode 100644 index 000000000..26c0d56b0 --- /dev/null +++ b/chain/jsonrpc/res/validator.html @@ -0,0 +1,390 @@ + + + + + + + + + + + +

+ Validator page - + +

+ +
+

+

Production

+

+

+ F is the time when the first approval arrives for a given height.
+ T is the time when a given blocks gets a threshold approval (66%).
+ Shards can either be produced by this validator (marked as 'ME') or received from other + validators.
+ Shards have missing chunks are marked as grey.
+ We also mark shards arrival time in color. Shards that are delayed by more than 150ms after T are marked as + orange, + and ones delayed more than 300 marked as red.
+ Approvals
+ Green field means that validators endorses the PREVIOUS block.
+ Grey means that we didn't get any info from the validator.
+ Other colors means different amount of skips. + +

+
+ + + + + + + + +
Height
+
+ +
+

+

Approval history

+

+ + + + + + + + + + + + + +
TimeApproval fromApproval toDelayClient-actor delay
+
+ + + + + \ No newline at end of file diff --git a/chain/jsonrpc/src/api/blocks.rs b/chain/jsonrpc/src/api/blocks.rs new file mode 100644 index 000000000..6074af481 --- /dev/null +++ b/chain/jsonrpc/src/api/blocks.rs @@ -0,0 +1,40 @@ +use serde_json::Value; + +use unc_client_primitives::types::GetBlockError; +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::types::blocks::{RpcBlockError, RpcBlockRequest}; +use unc_primitives::types::BlockReference; + +use super::{Params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcBlockRequest { + fn parse(value: Value) -> Result { + let block_reference = Params::new(value) + .try_singleton(|block_id| Ok(BlockReference::BlockId(block_id))) + .unwrap_or_parse()?; + Ok(Self { block_reference }) + } +} + +impl RpcFrom for RpcBlockError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcBlockError { + fn rpc_from(error: GetBlockError) -> Self { + match error { + GetBlockError::UnknownBlock { error_message } => Self::UnknownBlock { error_message }, + GetBlockError::NotSyncedYet => Self::NotSyncedYet, + GetBlockError::IOError { error_message } => Self::InternalError { error_message }, + GetBlockError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcBlockError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/changes.rs b/chain/jsonrpc/src/api/changes.rs new file mode 100644 index 000000000..9519d6d7a --- /dev/null +++ b/chain/jsonrpc/src/api/changes.rs @@ -0,0 +1,65 @@ +use serde_json::Value; + +use unc_client_primitives::types::{GetBlockError, GetStateChangesError}; +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::types::changes::{ + RpcStateChangesError, RpcStateChangesInBlockByTypeRequest, RpcStateChangesInBlockRequest, +}; + +use super::{Params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcStateChangesInBlockRequest { + fn parse(value: Value) -> Result { + Params::parse(value) + } +} + +impl RpcRequest for RpcStateChangesInBlockByTypeRequest { + fn parse(value: Value) -> Result { + Params::parse(value) + } +} + +impl RpcFrom for RpcStateChangesError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcStateChangesError { + fn rpc_from(error: GetBlockError) -> Self { + match error { + GetBlockError::UnknownBlock { error_message } => Self::UnknownBlock { error_message }, + GetBlockError::NotSyncedYet => Self::NotSyncedYet, + GetBlockError::IOError { error_message } => Self::InternalError { error_message }, + GetBlockError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcStateChangesError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} + +impl RpcFrom for RpcStateChangesError { + fn rpc_from(error: GetStateChangesError) -> Self { + match error { + GetStateChangesError::IOError { error_message } => { + Self::InternalError { error_message } + } + GetStateChangesError::UnknownBlock { error_message } => { + Self::UnknownBlock { error_message } + } + GetStateChangesError::NotSyncedYet => Self::NotSyncedYet, + GetStateChangesError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcStateChangesError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/chunks.rs b/chain/jsonrpc/src/api/chunks.rs new file mode 100644 index 000000000..0205bcb08 --- /dev/null +++ b/chain/jsonrpc/src/api/chunks.rs @@ -0,0 +1,65 @@ +use serde_json::Value; + +use unc_client_primitives::types::{GetChunk, GetChunkError}; +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::types::chunks::{ChunkReference, RpcChunkError, RpcChunkRequest}; +use unc_primitives::types::BlockId; + +use super::{Params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcChunkRequest { + fn parse(value: Value) -> Result { + // params can be: + // - chunk_reference (an object), + // - [[block_id, shard_id]] (a one-element array with array element) or + // - [chunk_id] (a one-element array with hash element). + let chunk_reference = Params::new(value) + .try_singleton(|value: Value| { + if value.is_array() { + let (block_id, shard_id) = Params::parse(value)?; + Ok(ChunkReference::BlockShardId { block_id, shard_id }) + } else { + let chunk_id = Params::parse(value)?; + Ok(ChunkReference::ChunkHash { chunk_id }) + } + }) + .unwrap_or_parse()?; + Ok(Self { chunk_reference }) + } +} + +impl RpcFrom for RpcChunkError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for GetChunk { + fn rpc_from(chunk_reference: ChunkReference) -> Self { + match chunk_reference { + ChunkReference::BlockShardId { block_id, shard_id } => match block_id { + BlockId::Height(height) => Self::Height(height, shard_id), + BlockId::Hash(block_hash) => Self::BlockHash(block_hash, shard_id), + }, + ChunkReference::ChunkHash { chunk_id } => Self::ChunkHash(chunk_id.into()), + } + } +} + +impl RpcFrom for RpcChunkError { + fn rpc_from(error: GetChunkError) -> Self { + match error { + GetChunkError::IOError { error_message } => Self::InternalError { error_message }, + GetChunkError::UnknownBlock { error_message } => Self::UnknownBlock { error_message }, + GetChunkError::InvalidShardId { shard_id } => Self::InvalidShardId { shard_id }, + GetChunkError::UnknownChunk { chunk_hash } => Self::UnknownChunk { chunk_hash }, + GetChunkError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcChunkError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/client_config.rs b/chain/jsonrpc/src/api/client_config.rs new file mode 100644 index 000000000..81d12e4c5 --- /dev/null +++ b/chain/jsonrpc/src/api/client_config.rs @@ -0,0 +1,25 @@ +use unc_client_primitives::types::GetClientConfigError; +use unc_jsonrpc_primitives::types::client_config::RpcClientConfigError; + +use super::RpcFrom; + +impl RpcFrom for RpcClientConfigError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcClientConfigError { + fn rpc_from(error: GetClientConfigError) -> Self { + match error { + GetClientConfigError::IOError(error_message) => Self::InternalError { error_message }, + GetClientConfigError::Unreachable(ref error_message) => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcClientConfigError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/config.rs b/chain/jsonrpc/src/api/config.rs new file mode 100644 index 000000000..7f24f45a4 --- /dev/null +++ b/chain/jsonrpc/src/api/config.rs @@ -0,0 +1,37 @@ +use serde_json::Value; + +use unc_client_primitives::types::GetProtocolConfigError; +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::types::config::{RpcProtocolConfigError, RpcProtocolConfigRequest}; + +use super::{Params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcProtocolConfigRequest { + fn parse(value: Value) -> Result { + Params::parse(value).map(|block_reference| Self { block_reference }) + } +} + +impl RpcFrom for RpcProtocolConfigError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcProtocolConfigError { + fn rpc_from(error: GetProtocolConfigError) -> Self { + match error { + GetProtocolConfigError::UnknownBlock(error_message) => { + Self::UnknownBlock { error_message } + } + GetProtocolConfigError::IOError(error_message) => Self::InternalError { error_message }, + GetProtocolConfigError::Unreachable(ref error_message) => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcProtocolConfigError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/gas_price.rs b/chain/jsonrpc/src/api/gas_price.rs new file mode 100644 index 000000000..bd48414c5 --- /dev/null +++ b/chain/jsonrpc/src/api/gas_price.rs @@ -0,0 +1,39 @@ +use serde_json::Value; + +use unc_client_primitives::types::GetGasPriceError; +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::types::gas_price::{RpcGasPriceError, RpcGasPriceRequest}; + +use super::{Params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcGasPriceRequest { + fn parse(value: Value) -> Result { + Params::parse(value).map(|(block_id,)| Self { block_id }) + } +} + +impl RpcFrom for RpcGasPriceError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcGasPriceError { + fn rpc_from(error: GetGasPriceError) -> Self { + match error { + GetGasPriceError::UnknownBlock { error_message } => { + Self::UnknownBlock { error_message } + } + GetGasPriceError::InternalError { error_message } => { + Self::InternalError { error_message } + } + GetGasPriceError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcGasPriceError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/light_client.rs b/chain/jsonrpc/src/api/light_client.rs new file mode 100644 index 000000000..3645e6e5a --- /dev/null +++ b/chain/jsonrpc/src/api/light_client.rs @@ -0,0 +1,123 @@ +use std::sync::Arc; + +use serde_json::Value; + +use unc_client_primitives::types::{ + GetBlockProofError, GetExecutionOutcomeError, GetNextLightClientBlockError, +}; +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::types::light_client::{ + RpcLightClientExecutionProofRequest, RpcLightClientNextBlockError, + RpcLightClientNextBlockRequest, RpcLightClientNextBlockResponse, RpcLightClientProofError, +}; +use unc_primitives::views::LightClientBlockView; + +use super::{Params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcLightClientExecutionProofRequest { + fn parse(value: Value) -> Result { + Params::parse(value) + } +} + +impl RpcRequest for RpcLightClientNextBlockRequest { + fn parse(value: Value) -> Result { + Params::new(value) + .try_singleton(|last_block_hash| Ok(Self { last_block_hash })) + .unwrap_or_parse() + } +} + +impl RpcFrom>> for RpcLightClientNextBlockResponse { + fn rpc_from(light_client_block: Option>) -> Self { + Self { light_client_block } + } +} + +impl RpcFrom for RpcLightClientProofError { + fn rpc_from(error: GetExecutionOutcomeError) -> Self { + match error { + GetExecutionOutcomeError::UnknownBlock { error_message } => { + Self::UnknownBlock { error_message } + } + GetExecutionOutcomeError::InconsistentState { + number_or_shards, + execution_outcome_shard_id, + } => Self::InconsistentState { number_or_shards, execution_outcome_shard_id }, + GetExecutionOutcomeError::NotConfirmed { transaction_or_receipt_id } => { + Self::NotConfirmed { transaction_or_receipt_id } + } + GetExecutionOutcomeError::UnknownTransactionOrReceipt { transaction_or_receipt_id } => { + Self::UnknownTransactionOrReceipt { transaction_or_receipt_id } + } + GetExecutionOutcomeError::UnavailableShard { transaction_or_receipt_id, shard_id } => { + Self::UnavailableShard { transaction_or_receipt_id, shard_id } + } + GetExecutionOutcomeError::InternalError { error_message } => { + Self::InternalError { error_message } + } + GetExecutionOutcomeError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcLightClientProofError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} + +impl RpcFrom for RpcLightClientProofError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcLightClientProofError { + fn rpc_from(error: GetBlockProofError) -> Self { + match error { + GetBlockProofError::UnknownBlock { error_message } => { + Self::UnknownBlock { error_message } + } + GetBlockProofError::InternalError { error_message } => { + Self::InternalError { error_message } + } + GetBlockProofError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcLightClientProofError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} + +impl RpcFrom for RpcLightClientNextBlockError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcLightClientNextBlockError { + fn rpc_from(error: GetNextLightClientBlockError) -> Self { + match error { + GetNextLightClientBlockError::InternalError { error_message } => { + Self::InternalError { error_message } + } + GetNextLightClientBlockError::UnknownBlock { error_message } => { + Self::UnknownBlock { error_message } + } + GetNextLightClientBlockError::EpochOutOfBounds { epoch_id } => { + Self::EpochOutOfBounds { epoch_id } + } + GetNextLightClientBlockError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcLightClientNextBlockError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/maintenance.rs b/chain/jsonrpc/src/api/maintenance.rs new file mode 100644 index 000000000..1a0a43527 --- /dev/null +++ b/chain/jsonrpc/src/api/maintenance.rs @@ -0,0 +1,35 @@ +use serde_json::Value; + +use unc_client_primitives::types::GetMaintenanceWindowsError; +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::types::maintenance::{ + RpcMaintenanceWindowsError, RpcMaintenanceWindowsRequest, +}; + +use super::{Params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcMaintenanceWindowsRequest { + fn parse(value: Value) -> Result { + Params::parse(value) + } +} + +impl RpcFrom for RpcMaintenanceWindowsError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcMaintenanceWindowsError { + fn rpc_from(error: GetMaintenanceWindowsError) -> Self { + match error { + GetMaintenanceWindowsError::IOError(error_message) => { + Self::InternalError { error_message } + } + GetMaintenanceWindowsError::Unreachable(ref error_message) => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/miner_chips_list.rs b/chain/jsonrpc/src/api/miner_chips_list.rs new file mode 100644 index 000000000..255926ebc --- /dev/null +++ b/chain/jsonrpc/src/api/miner_chips_list.rs @@ -0,0 +1,58 @@ +use unc_primitives::types::{AccountId}; +use serde_json::Value; + +use unc_client_primitives::types::{MinerChipsListError}; +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::types::miner_chips_list::{ + RpcMinerChipsListError, RpcMinerChipsListRequest, +}; + +use super::{RpcFrom, RpcRequest}; + +impl RpcRequest for RpcMinerChipsListRequest { + // fn parse(value: Value) -> Result { + // let block_height = value + // .get("block_height") + // .and_then(|v| v.as_u64()) + // .ok_or_else(|| RpcParseError("block_height not found or not a u64".parse().unwrap()))?; + // Ok(Self { block_height }) + // } + fn parse(value: Value) -> Result { + // Extract block_hash_str from value + let account_id_str = value + .get("account_id") + .and_then(|v| v.as_str()) + .ok_or_else(|| RpcParseError("account_id not found or not a string".parse().unwrap()))?; + + // Construct the CryptoHash from the decoded bytes + let account_id : AccountId = account_id_str + .parse() + .map_err(|_| RpcParseError("Failed to parse epoch_id from base58".parse().unwrap()))?; + + Ok(Self { account_id }) + } + +} + +impl RpcFrom for RpcMinerChipsListError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcMinerChipsListError { + fn rpc_from(error: MinerChipsListError) -> Self { + match error { + MinerChipsListError::UnknownAccount{ error_message } => Self::UnknownAccount { error_message }, + MinerChipsListError::ChipsInfoUnavailable => Self::ChipsInfoUnavailable, + MinerChipsListError::IOError{ error_message } => Self::InternalError { error_message }, + MinerChipsListError::Unreachable{ ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["MinerChipsListError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/mod.rs b/chain/jsonrpc/src/api/mod.rs new file mode 100644 index 000000000..7d2b3add5 --- /dev/null +++ b/chain/jsonrpc/src/api/mod.rs @@ -0,0 +1,174 @@ +use serde_json::Value; + +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::errors::{RpcError, ServerError}; + +mod blocks; +mod changes; +mod chunks; +mod client_config; +mod config; +mod gas_price; +mod light_client; +mod maintenance; +mod network_info; +mod query; +mod receipts; +mod sandbox; +mod split_storage; +mod status; +mod transactions; +mod validator; + +mod provider; +mod miner_chips_list; + +pub(crate) trait RpcRequest: Sized { + fn parse(value: Value) -> Result; +} + +impl RpcRequest for () { + fn parse(_: Value) -> Result { + Ok(()) + } +} + +pub trait RpcFrom { + fn rpc_from(_: T) -> Self; +} + +pub trait RpcInto { + fn rpc_into(self) -> T; +} + +impl RpcFrom for T { + fn rpc_from(val: T) -> Self { + val + } +} + +impl RpcInto for T +where + X: RpcFrom, +{ + fn rpc_into(self) -> X { + X::rpc_from(self) + } +} + +impl RpcFrom for RpcError { + fn rpc_from(error: actix::MailboxError) -> Self { + RpcError::new( + -32_000, + "Server error".to_string(), + Some(serde_json::Value::String(error.to_string())), + ) + } +} + +impl RpcFrom for ServerError { + fn rpc_from(error: actix::MailboxError) -> Self { + match error { + actix::MailboxError::Closed => ServerError::Closed, + actix::MailboxError::Timeout => ServerError::Timeout, + } + } +} + +impl RpcFrom for ServerError { + fn rpc_from(e: unc_primitives::errors::InvalidTxError) -> ServerError { + ServerError::TxExecutionError(unc_primitives::errors::TxExecutionError::InvalidTxError(e)) + } +} + +mod params { + use serde::de::DeserializeOwned; + use serde_json::Value; + + use unc_jsonrpc_primitives::errors::RpcParseError; + + /// Helper wrapper for parsing JSON value into expected request format. + /// + /// If you don’t need to handle legacy APIs you most likely just want to do + /// `Params::parse(params)` to convert JSON value into parameters format you + /// expect. + /// + /// This is over-engineered to help handle legacy API for some of the JSON + /// API requests. For example, parameters for block request can be a block + /// request object (the new API) or a one-element array with block id + /// element (the old API). + pub(crate) struct Params( + /// Regarding representation: + /// - `Ok(Ok(value))` means value has been parsed successfully. No + /// further parsing attempts will happen. + /// - `Ok(Err(err))` means value has been parsed unsuccessfully + /// (resulting in a parse error). No further parsing attempts will + /// happen. + /// - `Err(value)` means value hasn’t been parsed yet and needs to be + /// parsed with one of the methods. + Result, Value>, + ); + + impl Params { + pub fn new(params: Value) -> Self { + Self(Err(params)) + } + + pub fn parse(value: Value) -> Result + where + T: DeserializeOwned, + { + serde_json::from_value(value) + .map_err(|e| RpcParseError(format!("Failed parsing args: {e}"))) + } + + /// If value hasn’t been parsed yet, tries to deserialise it directly + /// into `T`. + pub fn unwrap_or_parse(self) -> Result + where + T: DeserializeOwned, + { + self.0.unwrap_or_else(Self::parse) + } + + /// If value hasn’t been parsed yet and it’s a one-element array + /// (i.e. singleton) deserialises the element and calls `func` on it. + /// + /// `try_singleton` and `try_pair` methods can be chained together + /// (though it doesn’t make sense to use the same method twice) before + /// a final `parse` call. + pub fn try_singleton( + self, + func: impl FnOnce(U) -> Result, + ) -> Self { + Self(match self.0 { + Err(Value::Array(mut array)) if array.len() == 1 => { + Ok(Params::parse(array[0].take()).and_then(func)) + } + x => x, + }) + } + + /// If value hasn’t been parsed yet and it’s a two-element array + /// (i.e. couple) deserialises the element and calls `func` on it. + /// + /// `try_singleton` and `try_pair` methods can be chained together + /// (though it doesn’t make sense to use the same method twice) before + /// a final `parse` call. + pub fn try_pair( + self, + func: impl FnOnce(U, V) -> Result, + ) -> Self { + Self(match self.0 { + Err(Value::Array(mut array)) if array.len() == 2 => { + Ok(Params::parse(array[0].take()) + .and_then(|u| Ok((u, Params::parse(array[1].take())?))) + .and_then(|(u, v)| func(u, v))) + } + x => x, + }) + } + } +} + +pub(crate) use params::Params; diff --git a/chain/jsonrpc/src/api/network_info.rs b/chain/jsonrpc/src/api/network_info.rs new file mode 100644 index 000000000..f3062e999 --- /dev/null +++ b/chain/jsonrpc/src/api/network_info.rs @@ -0,0 +1,55 @@ +use super::{RpcFrom, RpcInto}; +use unc_client_primitives::types::NetworkInfoResponse; +use unc_client_primitives::types::{KnownProducer, PeerInfo}; +use unc_jsonrpc_primitives::types::network_info::{ + RpcKnownProducer, RpcNetworkInfoError, RpcNetworkInfoResponse, RpcPeerInfo, +}; + +impl RpcFrom for RpcNetworkInfoError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcPeerInfo { + fn rpc_from(peer_info: PeerInfo) -> Self { + Self { id: peer_info.id, addr: peer_info.addr, account_id: peer_info.account_id } + } +} + +impl RpcFrom for RpcKnownProducer { + fn rpc_from(known_producer: KnownProducer) -> Self { + Self { + account_id: known_producer.account_id, + addr: known_producer.addr, + peer_id: known_producer.peer_id, + } + } +} + +impl RpcFrom for RpcNetworkInfoResponse { + fn rpc_from(network_info_response: NetworkInfoResponse) -> Self { + Self { + active_peers: network_info_response + .connected_peers + .iter() + .map(|pi| pi.clone().rpc_into()) + .collect(), + num_active_peers: network_info_response.num_connected_peers, + peer_max_count: network_info_response.peer_max_count, + sent_bytes_per_sec: network_info_response.sent_bytes_per_sec, + received_bytes_per_sec: network_info_response.received_bytes_per_sec, + known_producers: network_info_response + .known_producers + .iter() + .map(|kp| kp.clone().rpc_into()) + .collect(), + } + } +} + +impl RpcFrom for RpcNetworkInfoError { + fn rpc_from(error_message: String) -> Self { + Self::InternalError { error_message } + } +} diff --git a/chain/jsonrpc/src/api/provider.rs b/chain/jsonrpc/src/api/provider.rs new file mode 100644 index 000000000..0383e8deb --- /dev/null +++ b/chain/jsonrpc/src/api/provider.rs @@ -0,0 +1,90 @@ +use unc_primitives::types::EpochId; +use serde_json::Value; + +use unc_client_primitives::types::{GetProviderError, GetProviderInfoError}; +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::types::provider::{ + RpcProviderError, RpcProviderRequest, +}; + +use super::{RpcFrom, RpcRequest}; + +impl RpcRequest for RpcProviderRequest { + // fn parse(value: Value) -> Result { + // let block_height = value + // .get("block_height") + // .and_then(|v| v.as_u64()) + // .ok_or_else(|| RpcParseError("block_height not found or not a u64".parse().unwrap()))?; + // Ok(Self { block_height }) + // } + fn parse(value: Value) -> Result { + // Extract block_hash_str from value + let epoch_id_str = value + .get("epoch_id") + .and_then(|v| v.as_str()) + .ok_or_else(|| RpcParseError("epoch_id not found or not a string".parse().unwrap()))?; + + // Decode the base58-encoded string to bytes + let bytes = bs58::decode(epoch_id_str) + .into_vec() + .map_err(|_| RpcParseError("Invalid base58-encoded hash".parse().unwrap()))?; + + // Ensure the decoded bytes have the correct length for a CryptoHash + if bytes.len() != 32 { + return Err(RpcParseError("Decoded hash does not match expected length".parse().unwrap())); + } + + // Construct the CryptoHash from the decoded bytes + let epoch_id : EpochId = epoch_id_str + .parse() + .map_err(|_| RpcParseError("Failed to parse epoch_id from base58".parse().unwrap()))?; + + let block_height = value + .get("block_height") + .and_then(|v| v.as_u64()) + .ok_or_else(|| RpcParseError("block_height not found or not a u64".parse().unwrap()))?; + + Ok(Self { epoch_id, block_height }) + } + +} + +impl RpcFrom for RpcProviderError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcProviderError { + fn rpc_from(error: GetProviderInfoError) -> Self { + match error { + GetProviderInfoError::UnknownBlock => Self::UnknownBlock, + GetProviderInfoError::ProviderInfoUnavailable => Self::ProviderInfoUnavailable, + GetProviderInfoError::IOError(error_message) => Self::InternalError { error_message }, + GetProviderInfoError::Unreachable(ref error_message) => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcProviderError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} + +impl RpcFrom for RpcProviderError { + fn rpc_from(error: GetProviderError) -> Self { + match error { + GetProviderError::UnknownBlock{.. } => Self::UnknownBlock{}, + GetProviderError::NotSyncedYet{.. } => Self::ProviderInfoUnavailable, + GetProviderError::IOError{error_message} => Self::InternalError { error_message }, + GetProviderError::Unreachable{ref error_message} => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcProviderError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/query.rs b/chain/jsonrpc/src/api/query.rs new file mode 100644 index 000000000..b3e57bd9b --- /dev/null +++ b/chain/jsonrpc/src/api/query.rs @@ -0,0 +1,171 @@ +use serde_json::Value; + +use unc_client_primitives::types::QueryError; +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::types::query::{RpcQueryError, RpcQueryRequest, RpcQueryResponse}; +use unc_primitives::types::BlockReference; +use unc_primitives::views::{QueryRequest, QueryResponse}; + +use super::{Params, RpcFrom, RpcRequest}; + +/// Max size of the query path (soft-deprecated) +const QUERY_DATA_MAX_SIZE: usize = 10 * 1024; + +/// Parses base58-encoded data from legacy path+data request format. +fn parse_bs58_data(max_len: usize, encoded: String) -> Result, RpcParseError> { + // N-byte encoded base58 string decodes to at most N bytes so there’s no + // need to allocate full max_len output buffer if encoded length is shorter. + let mut data = vec![0u8; max_len.min(encoded.len())]; + match bs58::decode(encoded.as_bytes()).into(data.as_mut_slice()) { + Ok(len) => { + data.truncate(len); + Ok(data) + } + Err(bs58::decode::Error::BufferTooSmall) => { + Err(RpcParseError("Query data size is too large".to_string())) + } + Err(err) => Err(RpcParseError(err.to_string())), + } +} + +impl RpcRequest for RpcQueryRequest { + fn parse(value: Value) -> Result { + Params::new(value).try_pair(parse_path_data).unwrap_or_parse() + } +} + +fn parse_path_data(path: String, data: String) -> Result { + // Handle a soft-deprecated version of the query API, which is based on + // positional arguments with a "path"-style first argument. + // + // This whole block can be removed one day, when the new API is 100% adopted. + + let parse_data = || { + let max_len = QUERY_DATA_MAX_SIZE.saturating_sub(path.len()); + parse_bs58_data(max_len, data) + }; + + let mut path_parts = path.splitn(3, '/'); + let make_err = || RpcParseError("Not enough query parameters provided".to_string()); + let query_command = path_parts.next().ok_or_else(make_err)?; + let account_id = path_parts + .next() + .ok_or_else(make_err)? + .parse() + .map_err(|err| RpcParseError(format!("{}", err)))?; + let maybe_extra_arg = path_parts.next(); + + let request = match query_command { + "account" => QueryRequest::ViewAccount { account_id }, + "access_key" => match maybe_extra_arg { + None => QueryRequest::ViewAccessKeyList { account_id }, + Some(pk) => QueryRequest::ViewAccessKey { + account_id, + public_key: pk + .parse() + .map_err(|_| RpcParseError("Invalid public key".to_string()))?, + }, + }, + "code" => QueryRequest::ViewCode { account_id }, + "contract" => QueryRequest::ViewState { + account_id, + prefix: parse_data()?.into(), + include_proof: false, + }, + "call" => match maybe_extra_arg { + Some(method_name) => QueryRequest::CallFunction { + account_id, + method_name: method_name.to_string(), + args: parse_data()?.into(), + }, + None => return Err(RpcParseError("Method name is missing".to_string())), + }, + _ => return Err(RpcParseError(format!("Unknown path {}", query_command))), + }; + // Use Finality::None here to make backward compatibility tests work + Ok(RpcQueryRequest { request, block_reference: BlockReference::latest() }) +} + +impl RpcFrom for RpcQueryError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcQueryError { + fn rpc_from(error: QueryError) -> Self { + match error { + QueryError::InternalError { error_message } => Self::InternalError { error_message }, + QueryError::NoSyncedBlocks => Self::NoSyncedBlocks, + QueryError::UnavailableShard { requested_shard_id } => { + Self::UnavailableShard { requested_shard_id } + } + QueryError::UnknownBlock { block_reference } => Self::UnknownBlock { block_reference }, + QueryError::GarbageCollectedBlock { block_height, block_hash } => { + Self::GarbageCollectedBlock { block_height, block_hash } + } + QueryError::InvalidAccount { requested_account_id, block_height, block_hash } => { + Self::InvalidAccount { requested_account_id, block_height, block_hash } + } + QueryError::UnknownAccount { requested_account_id, block_height, block_hash } => { + Self::UnknownAccount { requested_account_id, block_height, block_hash } + } + QueryError::NoContractCode { contract_account_id, block_height, block_hash } => { + Self::NoContractCode { contract_account_id, block_height, block_hash } + } + QueryError::UnknownAccessKey { public_key, block_height, block_hash } => { + Self::UnknownAccessKey { public_key, block_height, block_hash } + } + QueryError::ContractExecutionError { vm_error, block_height, block_hash } => { + Self::ContractExecutionError { vm_error, block_height, block_hash } + } + QueryError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcQueryError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + QueryError::TooLargeContractState { contract_account_id, block_height, block_hash } => { + Self::TooLargeContractState { contract_account_id, block_height, block_hash } + } + } + } +} + +impl RpcFrom for RpcQueryResponse { + fn rpc_from(query_response: QueryResponse) -> Self { + Self { + kind: RpcFrom::rpc_from(query_response.kind), + block_hash: query_response.block_hash, + block_height: query_response.block_height, + } + } +} + +impl RpcFrom + for unc_jsonrpc_primitives::types::query::QueryResponseKind +{ + fn rpc_from(query_response_kind: unc_primitives::views::QueryResponseKind) -> Self { + match query_response_kind { + unc_primitives::views::QueryResponseKind::ViewAccount(account_view) => { + Self::ViewAccount(account_view) + } + unc_primitives::views::QueryResponseKind::ViewCode(contract_code_view) => { + Self::ViewCode(contract_code_view) + } + unc_primitives::views::QueryResponseKind::ViewState(view_state_result) => { + Self::ViewState(view_state_result) + } + unc_primitives::views::QueryResponseKind::CallResult(call_result) => { + Self::CallResult(call_result) + } + unc_primitives::views::QueryResponseKind::AccessKey(access_key_view) => { + Self::AccessKey(access_key_view) + } + unc_primitives::views::QueryResponseKind::AccessKeyList(access_key_list) => { + Self::AccessKeyList(access_key_list) + } + } + } +} diff --git a/chain/jsonrpc/src/api/receipts.rs b/chain/jsonrpc/src/api/receipts.rs new file mode 100644 index 000000000..91a7789f4 --- /dev/null +++ b/chain/jsonrpc/src/api/receipts.rs @@ -0,0 +1,41 @@ +use super::{Params, RpcFrom, RpcRequest}; +use unc_client_primitives::types::{GetReceipt, GetReceiptError}; +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::types::receipts::{ + ReceiptReference, RpcReceiptError, RpcReceiptRequest, +}; +use serde_json::Value; + +impl RpcRequest for RpcReceiptRequest { + fn parse(value: Value) -> Result { + Ok(Self { receipt_reference: Params::parse(value)? }) + } +} + +impl RpcFrom for RpcReceiptError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for GetReceipt { + fn rpc_from(receipt_reference: ReceiptReference) -> Self { + Self { receipt_id: receipt_reference.receipt_id } + } +} + +impl RpcFrom for RpcReceiptError { + fn rpc_from(error: GetReceiptError) -> Self { + match error { + GetReceiptError::IOError(error_message) => Self::InternalError { error_message }, + GetReceiptError::UnknownReceipt(hash) => Self::UnknownReceipt { receipt_id: hash }, + GetReceiptError::Unreachable(ref error_message) => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcReceiptError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/sandbox.rs b/chain/jsonrpc/src/api/sandbox.rs new file mode 100644 index 000000000..60bc4315b --- /dev/null +++ b/chain/jsonrpc/src/api/sandbox.rs @@ -0,0 +1,33 @@ +use serde_json::Value; + +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::types::sandbox::{ + RpcSandboxFastForwardError, RpcSandboxFastForwardRequest, RpcSandboxPatchStateError, + RpcSandboxPatchStateRequest, +}; + +use super::{Params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcSandboxPatchStateRequest { + fn parse(value: Value) -> Result { + Params::parse(value) + } +} + +impl RpcRequest for RpcSandboxFastForwardRequest { + fn parse(value: Value) -> Result { + Params::parse(value) + } +} + +impl RpcFrom for RpcSandboxPatchStateError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcSandboxFastForwardError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} diff --git a/chain/jsonrpc/src/api/split_storage.rs b/chain/jsonrpc/src/api/split_storage.rs new file mode 100644 index 000000000..c6c209819 --- /dev/null +++ b/chain/jsonrpc/src/api/split_storage.rs @@ -0,0 +1,37 @@ +use unc_client_primitives::types::GetSplitStorageInfoError; +use unc_jsonrpc_primitives::{ + errors::RpcParseError, + types::split_storage::{RpcSplitStorageInfoError, RpcSplitStorageInfoRequest}, +}; +use serde_json::Value; + +use super::{Params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcSplitStorageInfoRequest { + fn parse(value: Value) -> Result { + Params::parse(value) + } +} + +impl RpcFrom for RpcSplitStorageInfoError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcSplitStorageInfoError { + fn rpc_from(error: GetSplitStorageInfoError) -> Self { + match error { + GetSplitStorageInfoError::IOError(error_message) => { + Self::InternalError { error_message } + } + GetSplitStorageInfoError::Unreachable(ref error_message) => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcSplitStorageInfoError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/status.rs b/chain/jsonrpc/src/api/status.rs new file mode 100644 index 000000000..0dc27242d --- /dev/null +++ b/chain/jsonrpc/src/api/status.rs @@ -0,0 +1,102 @@ +use unc_client_primitives::types::StatusError; +use unc_jsonrpc_primitives::types::status::{ + RpcHealthResponse, RpcStatusError, RpcStatusResponse, +}; +use unc_primitives::views::StatusResponse; + +use super::RpcFrom; + +impl RpcFrom for RpcStatusError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcStatusResponse { + fn rpc_from(status_response: StatusResponse) -> Self { + Self { status_response } + } +} + +impl RpcFrom + for unc_jsonrpc_primitives::types::status::DebugStatusResponse +{ + fn rpc_from(response: unc_client_primitives::debug::DebugStatusResponse) -> Self { + match response { + unc_client_primitives::debug::DebugStatusResponse::SyncStatus(x) => { + unc_jsonrpc_primitives::types::status::DebugStatusResponse::SyncStatus(x) + } + unc_client_primitives::debug::DebugStatusResponse::CatchupStatus(x) => { + unc_jsonrpc_primitives::types::status::DebugStatusResponse::CatchupStatus(x) + } + unc_client_primitives::debug::DebugStatusResponse::RequestedStateParts(x) => { + unc_jsonrpc_primitives::types::status::DebugStatusResponse::RequestedStateParts(x) + } + unc_client_primitives::debug::DebugStatusResponse::TrackedShards(x) => { + unc_jsonrpc_primitives::types::status::DebugStatusResponse::TrackedShards(x) + } + unc_client_primitives::debug::DebugStatusResponse::EpochInfo(x) => { + unc_jsonrpc_primitives::types::status::DebugStatusResponse::EpochInfo(x) + } + unc_client_primitives::debug::DebugStatusResponse::BlockStatus(x) => { + unc_jsonrpc_primitives::types::status::DebugStatusResponse::BlockStatus(x) + } + unc_client_primitives::debug::DebugStatusResponse::ValidatorStatus(x) => { + unc_jsonrpc_primitives::types::status::DebugStatusResponse::ValidatorStatus(x) + } + unc_client_primitives::debug::DebugStatusResponse::ChainProcessingStatus(x) => { + unc_jsonrpc_primitives::types::status::DebugStatusResponse::ChainProcessingStatus( + x, + ) + } + } + } +} + +impl RpcFrom + for unc_jsonrpc_primitives::types::status::DebugStatusResponse +{ + fn rpc_from(response: unc_network::debug::DebugStatus) -> Self { + match response { + unc_network::debug::DebugStatus::PeerStore(x) => { + unc_jsonrpc_primitives::types::status::DebugStatusResponse::PeerStore(x) + } + unc_network::debug::DebugStatus::Graph(x) => { + unc_jsonrpc_primitives::types::status::DebugStatusResponse::NetworkGraph(x) + } + unc_network::debug::DebugStatus::RecentOutboundConnections(x) => { + unc_jsonrpc_primitives::types::status::DebugStatusResponse::RecentOutboundConnections(x) + } + unc_network::debug::DebugStatus::Routes(x) => { + unc_jsonrpc_primitives::types::status::DebugStatusResponse::Routes(x) + } + unc_network::debug::DebugStatus::SnapshotHosts(x) => { + unc_jsonrpc_primitives::types::status::DebugStatusResponse::SnapshotHosts(x) + } + } + } +} + +impl RpcFrom for RpcHealthResponse { + fn rpc_from(_status_response: StatusResponse) -> Self { + Self {} + } +} + +impl RpcFrom for RpcStatusError { + fn rpc_from(error: StatusError) -> Self { + match error { + StatusError::InternalError { error_message } => Self::InternalError { error_message }, + StatusError::NodeIsSyncing => Self::NodeIsSyncing, + StatusError::NoNewBlocks { elapsed } => Self::NoNewBlocks { elapsed }, + StatusError::EpochOutOfBounds { epoch_id } => Self::EpochOutOfBounds { epoch_id }, + StatusError::Unreachable { ref error_message } => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcStatusError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} diff --git a/chain/jsonrpc/src/api/transactions.rs b/chain/jsonrpc/src/api/transactions.rs new file mode 100644 index 000000000..e39b91046 --- /dev/null +++ b/chain/jsonrpc/src/api/transactions.rs @@ -0,0 +1,200 @@ +use serde_json::Value; + +use unc_client_primitives::types::TxStatusError; +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::types::transactions::{ + RpcSendTransactionRequest, RpcTransactionError, RpcTransactionStatusRequest, TransactionInfo, +}; +use unc_primitives::borsh::BorshDeserialize; +use unc_primitives::transaction::SignedTransaction; + +use super::{Params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcSendTransactionRequest { + fn parse(value: Value) -> Result { + let tx_request = Params::new(value) + .try_singleton(|value| { + Ok(RpcSendTransactionRequest { + signed_transaction: decode_signed_transaction(value)?, + // will be ignored in `broadcast_tx_async`, `broadcast_tx_commit` + wait_until: Default::default(), + }) + }) + .try_pair(|_: String, _: String| { + // Here, we restrict serde parsing object from the array + // `wait_until` is a new feature supported only in object + Err(RpcParseError( + "Unable to parse send request: too many params passed".to_string(), + )) + }) + .unwrap_or_parse()?; + Ok(tx_request) + } +} + +impl RpcRequest for RpcTransactionStatusRequest { + fn parse(value: Value) -> Result { + Ok(Params::new(value) + .try_singleton(|signed_tx| { + Ok(RpcTransactionStatusRequest { + transaction_info: decode_signed_transaction(signed_tx)?.into(), + wait_until: Default::default(), + }) + }) + .try_pair(|tx_hash, sender_account_id| { + Ok(RpcTransactionStatusRequest { + transaction_info: TransactionInfo::TransactionId { tx_hash, sender_account_id } + .into(), + wait_until: Default::default(), + }) + }) + .unwrap_or_parse()?) + } +} + +impl RpcFrom for RpcTransactionError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { debug_info: error.to_string() } + } +} + +impl RpcFrom for RpcTransactionError { + fn rpc_from(error: TxStatusError) -> Self { + match error { + TxStatusError::ChainError(err) => { + Self::InternalError { debug_info: format!("{:?}", err) } + } + TxStatusError::MissingTransaction(requested_transaction_hash) => { + Self::UnknownTransaction { requested_transaction_hash } + } + TxStatusError::InternalError(debug_info) => Self::InternalError { debug_info }, + TxStatusError::TimeoutError => Self::TimeoutError, + } + } +} + +fn decode_signed_transaction(value: String) -> Result { + let bytes = unc_primitives::serialize::from_base64(&value) + .map_err(|err| RpcParseError(format!("Failed to decode transaction: {}", err)))?; + SignedTransaction::try_from_slice(&bytes) + .map_err(|err| RpcParseError(format!("Failed to decode transaction: {}", err))) +} + +#[cfg(test)] +mod tests { + use crate::api::RpcRequest; + use unc_jsonrpc_primitives::types::transactions::{ + RpcSendTransactionRequest, RpcTransactionStatusRequest, + }; + use unc_primitives::borsh; + use unc_primitives::hash::CryptoHash; + use unc_primitives::serialize::to_base64; + use unc_primitives::transaction::SignedTransaction; + + #[test] + fn test_serialize_tx_status_params_as_vec() { + let tx_hash = CryptoHash::new().to_string(); + let account_id = "sender.testnet"; + let params = serde_json::json!([tx_hash, account_id]); + assert!(RpcTransactionStatusRequest::parse(params).is_ok()); + } + + #[test] + fn test_serialize_tx_status_params_as_object() { + let tx_hash = CryptoHash::new().to_string(); + let account_id = "sender.testnet"; + let params = serde_json::json!({"tx_hash": tx_hash, "sender_account_id": account_id}); + assert!(RpcTransactionStatusRequest::parse(params).is_ok()); + } + + #[test] + fn test_serialize_tx_status_params_as_object_with_signed_tx() { + let tx_hash = CryptoHash::new(); + let tx = SignedTransaction::empty(tx_hash); + let bytes_tx = borsh::to_vec(&tx).unwrap(); + let str_tx = to_base64(&bytes_tx); + let wait_until = "INCLUDED_FINAL"; + let params = serde_json::json!({"signed_tx_base64": str_tx, "wait_until": wait_until}); + assert!(RpcTransactionStatusRequest::parse(params).is_ok()); + } + + #[test] + fn test_serialize_tx_status_params_as_object_with_wait_until() { + let tx_hash = CryptoHash::new().to_string(); + let account_id = "sender.testnet"; + let wait_until = "INCLUDED"; + let params = serde_json::json!({"tx_hash": tx_hash, "sender_account_id": account_id, "wait_until": wait_until}); + assert!(RpcTransactionStatusRequest::parse(params).is_ok()); + } + + #[test] + fn test_serialize_tx_status_params_as_binary_signed_tx() { + let tx_hash = CryptoHash::new(); + let tx = SignedTransaction::empty(tx_hash); + let bytes_tx = borsh::to_vec(&tx).unwrap(); + let str_tx = to_base64(&bytes_tx); + let params = serde_json::json!([str_tx]); + assert!(RpcTransactionStatusRequest::parse(params).is_ok()); + } + + // The params are invalid because sender_account_id is missing + #[test] + fn test_serialize_invalid_tx_status_params() { + let tx_hash = CryptoHash::new().to_string(); + let params = serde_json::json!([tx_hash]); + assert!(RpcTransactionStatusRequest::parse(params).is_err()); + } + + // The params are invalid because wait_until is supported only in tx status params passed by object + #[test] + fn test_serialize_tx_status_too_many_params() { + let tx_hash = CryptoHash::new().to_string(); + let account_id = "sender.testnet"; + let wait_until = "EXECUTED"; + let params = serde_json::json!([tx_hash, account_id, wait_until]); + assert!(RpcTransactionStatusRequest::parse(params).is_err()); + } + + #[test] + fn test_serialize_send_tx_params_as_binary_signed_tx() { + let tx_hash = CryptoHash::new(); + let tx = SignedTransaction::empty(tx_hash); + let bytes_tx = borsh::to_vec(&tx).unwrap(); + let str_tx = to_base64(&bytes_tx); + let params = serde_json::json!([str_tx]); + assert!(RpcSendTransactionRequest::parse(params).is_ok()); + } + + #[test] + fn test_serialize_send_tx_params_as_object() { + let tx_hash = CryptoHash::new(); + let tx = SignedTransaction::empty(tx_hash); + let bytes_tx = borsh::to_vec(&tx).unwrap(); + let str_tx = to_base64(&bytes_tx); + let params = serde_json::json!({"signed_tx_base64": str_tx}); + assert!(RpcSendTransactionRequest::parse(params).is_ok()); + } + + #[test] + fn test_serialize_send_tx_params_as_object_with_wait_until() { + let tx_hash = CryptoHash::new(); + let tx = SignedTransaction::empty(tx_hash); + let bytes_tx = borsh::to_vec(&tx).unwrap(); + let str_tx = to_base64(&bytes_tx); + let wait_until = "INCLUDED_FINAL"; + let params = serde_json::json!({"signed_tx_base64": str_tx, "wait_until": wait_until}); + assert!(RpcSendTransactionRequest::parse(params).is_ok()); + } + + // The params are invalid because wait_until is supported only in send tx params passed by object + #[test] + fn test_serialize_send_tx_too_many_params() { + let tx_hash = CryptoHash::new(); + let tx = SignedTransaction::empty(tx_hash); + let bytes_tx = borsh::to_vec(&tx).unwrap(); + let str_tx = to_base64(&bytes_tx); + let wait_until = "EXECUTED"; + let params = serde_json::json!([str_tx, wait_until]); + assert!(RpcSendTransactionRequest::parse(params).is_err()); + } +} diff --git a/chain/jsonrpc/src/api/validator.rs b/chain/jsonrpc/src/api/validator.rs new file mode 100644 index 000000000..2be288c52 --- /dev/null +++ b/chain/jsonrpc/src/api/validator.rs @@ -0,0 +1,109 @@ +use serde_json::Value; + +use unc_client_primitives::types::GetValidatorInfoError; +use unc_jsonrpc_primitives::errors::RpcParseError; +use unc_jsonrpc_primitives::types::validator::{ + RpcValidatorError, RpcValidatorRequest, RpcValidatorsOrderedRequest, +}; +use unc_primitives::types::EpochReference; + +use super::{Params, RpcFrom, RpcRequest}; + +impl RpcRequest for RpcValidatorRequest { + fn parse(value: Value) -> Result { + let epoch_reference = Params::new(value) + .try_singleton(|block_id| match block_id { + Some(id) => Ok(EpochReference::BlockId(id)), + None => Ok(EpochReference::Latest), + }) + .unwrap_or_parse()?; + Ok(Self { epoch_reference: epoch_reference }) + } +} + +impl RpcRequest for RpcValidatorsOrderedRequest { + fn parse(value: Value) -> Result { + Params::parse(value) + } +} + +impl RpcFrom for RpcValidatorError { + fn rpc_from(error: actix::MailboxError) -> Self { + Self::InternalError { error_message: error.to_string() } + } +} + +impl RpcFrom for RpcValidatorError { + fn rpc_from(error: GetValidatorInfoError) -> Self { + match error { + GetValidatorInfoError::UnknownEpoch => Self::UnknownEpoch, + GetValidatorInfoError::ValidatorInfoUnavailable => Self::ValidatorInfoUnavailable, + GetValidatorInfoError::IOError(error_message) => Self::InternalError { error_message }, + GetValidatorInfoError::Unreachable(ref error_message) => { + tracing::warn!(target: "jsonrpc", "Unreachable error occurred: {}", error_message); + crate::metrics::RPC_UNREACHABLE_ERROR_COUNT + .with_label_values(&["RpcValidatorError"]) + .inc(); + Self::InternalError { error_message: error.to_string() } + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::api::RpcRequest; + use unc_jsonrpc_primitives::types::validator::RpcValidatorRequest; + use unc_primitives::hash::CryptoHash; + use unc_primitives::types::{BlockId, EpochId, EpochReference}; + + #[test] + fn test_serialize_validators_params_as_vec() { + let block_hash = CryptoHash::new(); + let params = serde_json::json!([block_hash.to_string()]); + let result = RpcValidatorRequest::parse(params); + assert_eq!( + result.unwrap(), + RpcValidatorRequest { + epoch_reference: EpochReference::BlockId(BlockId::Hash(block_hash)), + } + ); + } + + #[test] + fn test_serialize_validators_params_as_object_input_block_hash() { + let block_hash = CryptoHash::new(); + let params = serde_json::json!({"block_id": block_hash.to_string()}); + let result = RpcValidatorRequest::parse(params); + assert_eq!( + result.unwrap(), + RpcValidatorRequest { + epoch_reference: EpochReference::BlockId(BlockId::Hash(block_hash)), + } + ); + } + + #[test] + fn test_serialize_validators_params_as_object_input_block_height() { + let block_height: u64 = 12345; + let params = serde_json::json!({"block_id": block_height}); + let result = RpcValidatorRequest::parse(params); + assert_eq!( + result.unwrap(), + RpcValidatorRequest { + epoch_reference: EpochReference::BlockId(BlockId::Height(block_height)), + } + ); + } + + #[test] + fn test_serialize_validators_params_as_object_input_epoch_id() { + let epoch_id = CryptoHash::new(); + let params = serde_json::json!({"epoch_id": epoch_id.to_string()}); + let result = RpcValidatorRequest::parse(params); + assert_eq!( + result.unwrap(), + RpcValidatorRequest { epoch_reference: EpochReference::EpochId(EpochId(epoch_id)) } + ); + } +} diff --git a/chain/jsonrpc/src/lib.rs b/chain/jsonrpc/src/lib.rs new file mode 100644 index 000000000..0e633962d --- /dev/null +++ b/chain/jsonrpc/src/lib.rs @@ -0,0 +1,1611 @@ +#![doc = include_str!("../README.md")] + +use actix::{Addr, MailboxError}; +use actix_cors::Cors; +use actix_web::http::header; +use actix_web::HttpRequest; +use actix_web::{get, http, middleware, web, App, Error as HttpError, HttpResponse, HttpServer}; +use api::RpcRequest; +pub use api::{RpcFrom, RpcInto}; +use futures::Future; +use futures::FutureExt; +use unc_chain_configs::GenesisConfig; +use unc_client::{ + ClientActor, DebugStatus, GetBlock, GetBlockProof, GetChunk, GetClientConfig, + GetExecutionOutcome, GetGasPrice, GetMaintenanceWindows, GetNetworkInfo, + GetNextLightClientBlock, GetProtocolConfig, GetReceipt, GetStateChanges, + GetStateChangesInBlock, GetValidatorInfo, GetValidatorOrdered, ProcessTxRequest, + ProcessTxResponse, Query, Status, TxStatus, ViewClientActor, +}; +use unc_client_primitives::types::{MinerChipsList, GetProvider, GetSplitStorageInfo}; +pub use unc_jsonrpc_client as client; +use unc_jsonrpc_primitives::errors::RpcError; +use unc_jsonrpc_primitives::message::{Message, Request}; +use unc_jsonrpc_primitives::types::config::RpcProtocolConfigResponse; +use unc_jsonrpc_primitives::types::entity_debug::{EntityDebugHandler, EntityQuery}; +use unc_jsonrpc_primitives::types::query::RpcQueryRequest; +use unc_jsonrpc_primitives::types::split_storage::{ + RpcSplitStorageInfoRequest, RpcSplitStorageInfoResponse, +}; +use unc_jsonrpc_primitives::types::transactions::{ + RpcSendTransactionRequest, RpcTransactionResponse, +}; +use unc_network::tcp; +use unc_network::PeerManagerActor; +use unc_o11y::metrics::{prometheus, Encoder, TextEncoder}; +use unc_o11y::{WithSpanContext, WithSpanContextExt}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{AccountId, BlockHeight}; +use unc_primitives::views::{QueryRequest, TxExecutionStatus}; +use serde_json::{json, Value}; +use std::path::PathBuf; +use std::sync::Arc; +use std::time::{Duration, Instant}; +use tokio::time::{sleep, timeout}; +use tracing::{error, info}; + +mod api; +mod metrics; + +#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] +pub struct RpcPollingConfig { + pub polling_interval: Duration, + pub polling_timeout: Duration, +} + +impl Default for RpcPollingConfig { + fn default() -> Self { + Self { + polling_interval: Duration::from_millis(500), + polling_timeout: Duration::from_secs(10), + } + } +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub struct RpcLimitsConfig { + /// Maximum byte size of the json payload. + pub json_payload_max_size: usize, +} + +impl Default for RpcLimitsConfig { + fn default() -> Self { + Self { json_payload_max_size: 10 * 1024 * 1024 } + } +} + +fn default_enable_debug_rpc() -> bool { + false +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub struct RpcConfig { + pub addr: tcp::ListenerAddr, + // If provided, will start an http server exporting only Prometheus metrics on that address. + pub prometheus_addr: Option, + pub cors_allowed_origins: Vec, + pub polling_config: RpcPollingConfig, + #[serde(default)] + pub limits_config: RpcLimitsConfig, + // If true, enable some debug RPC endpoints (like one to get the latest block). + // We disable it by default, as some of those endpoints might be quite CPU heavy. + #[serde(default = "default_enable_debug_rpc")] + pub enable_debug_rpc: bool, + // For node developers only: if specified, the HTML files used to serve the debug pages will + // be read from this directory, instead of the contents compiled into the binary. This allows + // for quick iterative development. + pub experimental_debug_pages_src_path: Option, +} + +impl Default for RpcConfig { + fn default() -> Self { + RpcConfig { + addr: tcp::ListenerAddr::new("0.0.0.0:3030".parse().unwrap()), + prometheus_addr: None, + cors_allowed_origins: vec!["*".to_owned()], + polling_config: Default::default(), + limits_config: Default::default(), + enable_debug_rpc: false, + experimental_debug_pages_src_path: None, + } + } +} + +impl RpcConfig { + pub fn new(addr: tcp::ListenerAddr) -> Self { + RpcConfig { addr, ..Default::default() } + } +} + +/// Serialises response of a query into JSON to be sent to the client. +/// +/// Returns an internal server error if the value fails to serialise. +fn serialize_response(value: impl serde::ser::Serialize) -> Result { + serde_json::to_value(value).map_err(|err| RpcError::serialization_error(err.to_string())) +} + +/// Processes a specific method call. +/// +/// The arguments for the method (which is implemented by the `callback`) will +/// be parsed (using [`RpcRequest::parse`]) from the `request.params`. Ok +/// results of the `callback` will be converted into a [`Value`] via serde +/// serialisation. +async fn process_method_call( + request: Request, + callback: impl FnOnce(R) -> F, +) -> Result +where + R: RpcRequest, + V: serde::ser::Serialize, + RpcError: std::convert::From, + F: std::future::Future>, +{ + serialize_response(callback(R::parse(request.params)?).await?) +} + +#[easy_ext::ext(FromNetworkClientResponses)] +impl unc_jsonrpc_primitives::types::transactions::RpcTransactionError { + pub fn from_network_client_responses(resp: ProcessTxResponse) -> Self { + match resp { + ProcessTxResponse::InvalidTx(context) => Self::InvalidTransaction { context }, + ProcessTxResponse::NoResponse => Self::TimeoutError, + ProcessTxResponse::DoesNotTrackShard | ProcessTxResponse::RequestRouted => { + Self::DoesNotTrackShard + } + internal_error => Self::InternalError { debug_info: format!("{:?}", internal_error) }, + } + } +} + +/// This function processes response from query method to introduce +/// backward compatible response in case of specific errors +fn process_query_response( + query_response: Result< + unc_jsonrpc_primitives::types::query::RpcQueryResponse, + unc_jsonrpc_primitives::types::query::RpcQueryError, + >, +) -> Result { + // This match is used here to give backward compatible error message for specific + // error variants. Should be refactored once structured errors fully shipped + match query_response { + Ok(rpc_query_response) => serialize_response(rpc_query_response), + Err(err) => match err { + unc_jsonrpc_primitives::types::query::RpcQueryError::ContractExecutionError { + vm_error, + block_height, + block_hash, + } => Ok(json!({ + "error": vm_error, + "logs": json!([]), + "block_height": block_height, + "block_hash": block_hash, + })), + unc_jsonrpc_primitives::types::query::RpcQueryError::UnknownAccessKey { + public_key, + block_height, + block_hash, + } => Ok(json!({ + "error": format!("access key {} does not exist while viewing", public_key), + "logs": json!([]), + "block_height": block_height, + "block_hash": block_hash, + })), + unc_jsonrpc_primitives::types::query::RpcQueryError::UnknownBlock { + block_reference: unc_primitives::types::BlockReference::BlockId(ref block_id), + } => { + let error_data = Some(match block_id { + unc_primitives::types::BlockId::Height(height) => json!(format!( + "DB Not Found Error: BLOCK HEIGHT: {} \n Cause: Unknown", + height + )), + unc_primitives::types::BlockId::Hash(block_hash) => { + json!(format!("DB Not Found Error: BLOCK HEADER: {}", block_hash)) + } + }); + let error_data_value = match serde_json::to_value(err) { + Ok(value) => value, + Err(err) => { + return Err(RpcError::new_internal_error( + None, + format!("Failed to serialize RpcQueryError: {:?}", err), + )) + } + }; + Err(RpcError::new_internal_or_handler_error(error_data, error_data_value)) + } + _ => Err(err.into()), + }, + } +} + +struct JsonRpcHandler { + client_addr: Addr, + view_client_addr: Addr, + peer_manager_addr: Option>, + polling_config: RpcPollingConfig, + genesis_config: GenesisConfig, + enable_debug_rpc: bool, + debug_pages_src_path: Option, + entity_debug_handler: Arc, +} + +impl JsonRpcHandler { + pub async fn process(&self, message: Message) -> Result { + let id = message.id(); + match message { + Message::Request(request) => { + Ok(Message::response(id, self.process_request(request).await)) + } + _ => Ok(Message::error(RpcError::parse_error( + "JSON RPC Request format was expected".to_owned(), + ))), + } + } + + // `process_request` increments affected metrics but the request processing is done by + // `process_request_internal`. + async fn process_request(&self, request: Request) -> Result { + let timer = Instant::now(); + let (metrics_name, response) = self.process_request_internal(request).await; + + metrics::HTTP_RPC_REQUEST_COUNT.with_label_values(&[&metrics_name]).inc(); + metrics::RPC_PROCESSING_TIME + .with_label_values(&[&metrics_name]) + .observe(timer.elapsed().as_secs_f64()); + + if let Err(err) = &response { + metrics::RPC_ERROR_COUNT + .with_label_values(&[&metrics_name, &err.code.to_string()]) + .inc(); + } + + response + } + + /// Processes the request without updating any metrics. + /// Returns metrics name (method name with optional details as a suffix) + /// and the result of the execution. + async fn process_request_internal( + &self, + request: Request, + ) -> (String, Result) { + let method_name = request.method.to_string(); + let request = match self.process_adversarial_request_internal(request).await { + Ok(response) => return (method_name, response), + Err(request) => request, + }; + + let request = match self.process_basic_requests_internal(request).await { + Ok(response) => return (method_name, response), + Err(request) => request, + }; + + match request.method.as_ref() { + "query" => { + let params: RpcQueryRequest = match RpcRequest::parse(request.params) { + Ok(params) => params, + Err(err) => return (method_name, Err(RpcError::from(err))), + }; + let metrics_name = match params.request { + QueryRequest::ViewAccount { .. } => "query_view_account", + QueryRequest::ViewCode { .. } => "query_view_code", + QueryRequest::ViewState { include_proof, .. } => { + if include_proof { + "query_view_state_with_proof" + } else { + "query_view_state" + } + } + QueryRequest::ViewAccessKey { .. } => "query_view_access_key", + QueryRequest::ViewAccessKeyList { .. } => "query_view_access_key_list", + QueryRequest::CallFunction { .. } => "query_call_function", + }; + (metrics_name.to_string(), process_query_response(self.query(params).await)) + } + _ => { + ("UNSUPPORTED_METHOD".to_string(), Err(RpcError::method_not_found(request.method))) + } + } + } + + async fn process_basic_requests_internal( + &self, + request: Request, + ) -> Result, Request> { + Ok(match request.method.as_ref() { + // Handlers ordered alphabetically + "block" => process_method_call(request, |params| self.block(params)).await, + "broadcast_tx_async" => { + process_method_call(request, |params| async { + let tx = self.send_tx_async(params).await.to_string(); + Result::<_, std::convert::Infallible>::Ok(tx) + }) + .await + } + "broadcast_tx_commit" => { + process_method_call(request, |params| self.send_tx_commit(params)).await + } + "chunk" => process_method_call(request, |params| self.chunk(params)).await, + "gas_price" => process_method_call(request, |params| self.gas_price(params)).await, + "health" => process_method_call(request, |_params: ()| self.health()).await, + "light_client_proof" => { + process_method_call(request, |params| { + self.light_client_execution_outcome_proof(params) + }) + .await + } + "next_light_client_block" => { + process_method_call(request, |params| self.next_light_client_block(params)).await + } + "network_info" => process_method_call(request, |_params: ()| self.network_info()).await, + "send_tx" => process_method_call(request, |params| self.send_tx(params)).await, + "status" => process_method_call(request, |_params: ()| self.status()).await, + "tx" => { + process_method_call(request, |params| self.tx_status_common(params, false)).await + } + "validators" => process_method_call(request, |params| self.validators(params)).await, + "client_config" => { + process_method_call(request, |_params: ()| self.client_config()).await + } + "EXPERIMENTAL_changes" => { + process_method_call(request, |params| self.changes_in_block_by_type(params)).await + } + "EXPERIMENTAL_changes_in_block" => { + process_method_call(request, |params| self.changes_in_block(params)).await + } + "EXPERIMENTAL_genesis_config" => { + process_method_call(request, |_params: ()| async { + Result::<_, std::convert::Infallible>::Ok(&self.genesis_config) + }) + .await + } + "EXPERIMENTAL_light_client_proof" => { + process_method_call(request, |params| { + self.light_client_execution_outcome_proof(params) + }) + .await + } + "EXPERIMENTAL_protocol_config" => { + process_method_call(request, |params| self.protocol_config(params)).await + } + "EXPERIMENTAL_receipt" => { + process_method_call(request, |params| self.receipt(params)).await + } + "EXPERIMENTAL_tx_status" => { + process_method_call(request, |params| self.tx_status_common(params, true)).await + } + "EXPERIMENTAL_validators_ordered" => { + process_method_call(request, |params| self.validators_ordered(params)).await + } + "EXPERIMENTAL_maintenance_windows" => { + process_method_call(request, |params| self.maintenance_windows(params)).await + } + "EXPERIMENTAL_split_storage_info" => { + process_method_call(request, |params| self.split_storage_info(params)).await + } + #[cfg(feature = "sandbox")] + "sandbox_patch_state" => { + process_method_call(request, |params| self.sandbox_patch_state(params)).await + } + #[cfg(feature = "sandbox")] + "sandbox_fast_forward" => { + process_method_call(request, |params| self.sandbox_fast_forward(params)).await + } + "provider" => { + process_method_call(request, |params | self.get_provider(params)).await + } + "miner_chips_list" => { + process_method_call(request, |params | self.miner_chips_list(params)).await + } + _ => return Err(request), + }) + } + + /// Handles adversarial requests if they are enabled. + /// + /// Adversarial requests are only enabled when `test_features` Cargo feature + /// is turned on. If the request has not been recognised as an adversarial + /// request, returns `Err(request)` so that caller can continue handling the + /// request. Otherwise returns `Ok(response)` where `response` is the + /// result of handling the request. + #[cfg(not(feature = "test_features"))] + async fn process_adversarial_request_internal( + &self, + request: Request, + ) -> Result, Request> { + Err(request) + } + + #[cfg(feature = "test_features")] + async fn process_adversarial_request_internal( + &self, + request: Request, + ) -> Result, Request> { + Ok(match request.method.as_ref() { + "adv_disable_header_sync" => self.adv_disable_header_sync(request.params).await, + "adv_disable_doomslug" => self.adv_disable_doomslug(request.params).await, + "adv_produce_blocks" => self.adv_produce_blocks(request.params).await, + "adv_switch_to_height" => self.adv_switch_to_height(request.params).await, + "adv_get_saved_blocks" => self.adv_get_saved_blocks(request.params).await, + "adv_check_store" => self.adv_check_store(request.params).await, + _ => return Err(request), + }) + } + + async fn client_send(&self, msg: M) -> Result + where + ClientActor: actix::Handler>, + M: actix::Message> + Send + 'static, + M::Result: Send, + E: RpcFrom, + E: RpcFrom, + { + self.client_addr + .send(msg.with_span_context()) + .await + .map_err(RpcFrom::rpc_from)? + .map_err(RpcFrom::rpc_from) + } + + async fn view_client_send(&self, msg: M) -> Result + where + ViewClientActor: actix::Handler>, + M: actix::Message> + Send + 'static, + M::Result: Send, + E: RpcFrom, + E: RpcFrom, + { + self.view_client_addr + .send(msg.with_span_context()) + .await + .map_err(RpcFrom::rpc_from)? + .map_err(RpcFrom::rpc_from) + } + + async fn peer_manager_send(&self, msg: M) -> Result + where + PeerManagerActor: actix::Handler, + M: actix::Message + Send + 'static, + M::Result: Send, + E: RpcFrom, + { + match &self.peer_manager_addr { + Some(peer_manager_addr) => peer_manager_addr.send(msg).await.map_err(RpcFrom::rpc_from), + None => Err(RpcFrom::rpc_from(MailboxError::Closed)), + } + } + + async fn send_tx_async( + &self, + request_data: unc_jsonrpc_primitives::types::transactions::RpcSendTransactionRequest, + ) -> CryptoHash { + let tx = request_data.signed_transaction; + let hash = tx.get_hash(); + self.client_addr.do_send( + ProcessTxRequest { + transaction: tx, + is_forwarded: false, + check_only: false, // if we set true here it will not actually send the transaction + } + .with_span_context(), + ); + hash + } + + async fn tx_exists( + &self, + tx_hash: CryptoHash, + signer_account_id: &AccountId, + ) -> Result { + timeout(self.polling_config.polling_timeout, async { + loop { + // TODO(optimization): Introduce a view_client method to only get transaction + // status without the information about execution outcomes. + match self.view_client_send( + TxStatus { + tx_hash, + signer_account_id: signer_account_id.clone(), + fetch_receipt: false, + }) + .await + { + Ok(status) => { + if let Some(_) = status.execution_outcome { + return Ok(true); + } + } + Err(unc_jsonrpc_primitives::types::transactions::RpcTransactionError::UnknownTransaction { + .. + }) => { + return Ok(false); + } + _ => {} + } + sleep(self.polling_config.polling_interval).await; + } + }) + .await + .map_err(|_| { + metrics::RPC_TIMEOUT_TOTAL.inc(); + tracing::warn!( + target: "jsonrpc", "Timeout: tx_exists method. tx_hash {:?} signer_account_id {:?}", + tx_hash, + signer_account_id + ); + unc_jsonrpc_primitives::types::transactions::RpcTransactionError::TimeoutError + })? + } + + /// Return status of the given transaction + /// + /// `finality` forces the execution to wait until the desired finality level is reached + async fn tx_status_fetch( + &self, + tx_info: unc_jsonrpc_primitives::types::transactions::TransactionInfo, + finality: unc_primitives::views::TxExecutionStatus, + fetch_receipt: bool, + ) -> Result< + unc_jsonrpc_primitives::types::transactions::RpcTransactionResponse, + unc_jsonrpc_primitives::types::transactions::RpcTransactionError, + > { + let (tx_hash, account_id) = tx_info.to_tx_hash_and_account(); + let mut tx_status_result = + Err(unc_jsonrpc_primitives::types::transactions::RpcTransactionError::TimeoutError); + timeout(self.polling_config.polling_timeout, async { + loop { + tx_status_result = self.view_client_send( TxStatus { + tx_hash, + signer_account_id: account_id.clone(), + fetch_receipt, + }) + .await; + match tx_status_result.clone() { + Ok(result) => { + if result.status >= finality { + break Ok(result.into()) + } + // else: No such transaction recorded on chain yet + }, + Err(err @ unc_jsonrpc_primitives::types::transactions::RpcTransactionError::UnknownTransaction { + .. + }) => { + if let Some(tx) = tx_info.to_signed_tx() { + if let Ok(ProcessTxResponse::InvalidTx(context)) = + self.send_tx_internal(tx.clone(), true).await + { + break Err( + unc_jsonrpc_primitives::types::transactions::RpcTransactionError::InvalidTransaction { + context + } + ); + } + } + if finality == TxExecutionStatus::None { + break Err(err); + } + } + Err(err) => break Err(err), + } + sleep(self.polling_config.polling_interval).await; + } + }) + .await + .map_err(|_| { + metrics::RPC_TIMEOUT_TOTAL.inc(); + tracing::warn!( + target: "jsonrpc", "Timeout: tx_status_fetch method. tx_info {:?} fetch_receipt {:?}", + tx_info, + fetch_receipt, + ); + if let Err(error) = tx_status_result { + error + } else { + unc_jsonrpc_primitives::types::transactions::RpcTransactionError::TimeoutError + } + })? + } + + /// Send a transaction idempotently (subsequent send of the same transaction will not cause + /// any new side-effects and the result will be the same unless we garbage collected it + /// already). + async fn send_tx_internal( + &self, + tx: SignedTransaction, + check_only: bool, + ) -> Result + { + let tx_hash = tx.get_hash(); + let signer_account_id = tx.transaction.signer_id.clone(); + let response = self + .client_addr + .send( + ProcessTxRequest { transaction: tx, is_forwarded: false, check_only } + .with_span_context(), + ) + .await + .map_err(RpcFrom::rpc_from)?; + + // If we receive InvalidNonce error, it might be the case that the transaction was + // resubmitted, and we should check if that is the case and return ValidTx response to + // maintain idempotence of the send_tx method. + if let ProcessTxResponse::InvalidTx( + unc_primitives::errors::InvalidTxError::InvalidNonce { .. }, + ) = response + { + if self.tx_exists(tx_hash, &signer_account_id).await? { + return Ok(ProcessTxResponse::ValidTx); + } + } + + Ok(response) + } + + async fn send_tx( + &self, + request_data: unc_jsonrpc_primitives::types::transactions::RpcSendTransactionRequest, + ) -> Result< + unc_jsonrpc_primitives::types::transactions::RpcTransactionResponse, + unc_jsonrpc_primitives::types::transactions::RpcTransactionError, + > { + if request_data.wait_until == TxExecutionStatus::None { + self.send_tx_async(request_data).await; + return Ok(RpcTransactionResponse { + final_execution_outcome: None, + final_execution_status: TxExecutionStatus::None, + }); + } + let tx = request_data.signed_transaction; + match self.send_tx_internal(tx.clone(), false).await? { + ProcessTxResponse::ValidTx | ProcessTxResponse::RequestRouted => { + self.tx_status_fetch( + unc_jsonrpc_primitives::types::transactions::TransactionInfo::from_signed_tx(tx.clone()), + request_data.wait_until, + false, + ).await + } + network_client_response=> { + Err( + unc_jsonrpc_primitives::types::transactions::RpcTransactionError::from_network_client_responses( + network_client_response + ) + ) + } + } + } + + async fn send_tx_commit( + &self, + request_data: unc_jsonrpc_primitives::types::transactions::RpcSendTransactionRequest, + ) -> Result< + unc_jsonrpc_primitives::types::transactions::RpcTransactionResponse, + unc_jsonrpc_primitives::types::transactions::RpcTransactionError, + > { + self.send_tx(RpcSendTransactionRequest { + signed_transaction: request_data.signed_transaction, + wait_until: TxExecutionStatus::Final, + }) + .await + } + + async fn health( + &self, + ) -> Result< + unc_jsonrpc_primitives::types::status::RpcHealthResponse, + unc_jsonrpc_primitives::types::status::RpcStatusError, + > { + let status = self.client_send(Status { is_health_check: true, detailed: false }).await?; + Ok(status.rpc_into()) + } + + pub async fn status( + &self, + ) -> Result< + unc_jsonrpc_primitives::types::status::RpcStatusResponse, + unc_jsonrpc_primitives::types::status::RpcStatusError, + > { + let status = self.client_send(Status { is_health_check: false, detailed: false }).await?; + Ok(status.rpc_into()) + } + + pub async fn old_debug( + &self, + ) -> Result< + Option, + unc_jsonrpc_primitives::types::status::RpcStatusError, + > { + if self.enable_debug_rpc { + let status = + self.client_send(Status { is_health_check: false, detailed: true }).await?; + Ok(Some(status.rpc_into())) + } else { + Ok(None) + } + } + + pub async fn debug( + &self, + path: &str, + ) -> Result< + Option, + unc_jsonrpc_primitives::types::status::RpcStatusError, + > { + if self.enable_debug_rpc { + let debug_status: unc_jsonrpc_primitives::types::status::DebugStatusResponse = + match path { + "/debug/api/tracked_shards" => { + self.client_send(DebugStatus::TrackedShards).await?.rpc_into() + } + "/debug/api/sync_status" => { + self.client_send(DebugStatus::SyncStatus).await?.rpc_into() + } + "/debug/api/catchup_status" => { + self.client_send(DebugStatus::CatchupStatus).await?.rpc_into() + } + "/debug/api/epoch_info" => { + self.client_send(DebugStatus::EpochInfo).await?.rpc_into() + } + "/debug/api/block_status" => { + self.client_send(DebugStatus::BlockStatus(None)).await?.rpc_into() + } + "/debug/api/validator_status" => { + self.client_send(DebugStatus::ValidatorStatus).await?.rpc_into() + } + "/debug/api/chain_processing_status" => { + self.client_send(DebugStatus::ChainProcessingStatus).await?.rpc_into() + } + "/debug/api/requested_state_parts" => { + self.client_send(DebugStatus::RequestedStateParts).await?.rpc_into() + } + "/debug/api/peer_store" => self + .peer_manager_send(unc_network::debug::GetDebugStatus::PeerStore) + .await? + .rpc_into(), + "/debug/api/network_graph" => self + .peer_manager_send(unc_network::debug::GetDebugStatus::Graph) + .await? + .rpc_into(), + "/debug/api/recent_outbound_connections" => self + .peer_manager_send( + unc_network::debug::GetDebugStatus::RecentOutboundConnections, + ) + .await? + .rpc_into(), + "/debug/api/network_routes" => self + .peer_manager_send(unc_network::debug::GetDebugStatus::Routes) + .await? + .rpc_into(), + "/debug/api/snapshot_hosts" => self + .peer_manager_send(unc_network::debug::GetDebugStatus::SnapshotHosts) + .await? + .rpc_into(), + "/debug/api/split_store_info" => { + let split_storage_info: RpcSplitStorageInfoResponse = self + .split_storage_info(RpcSplitStorageInfoRequest {}) + .await + .map_err(|e| e.into_rpc_status_error())?; + unc_jsonrpc_primitives::types::status::DebugStatusResponse::SplitStoreStatus(split_storage_info.result) + } + _ => return Ok(None), + }; + Ok(Some(unc_jsonrpc_primitives::types::status::RpcDebugStatusResponse { + status_response: debug_status, + })) + } else { + Ok(None) + } + } + + pub async fn debug_block_status( + &self, + starting_height: Option, + ) -> Result< + Option, + unc_jsonrpc_primitives::types::status::RpcStatusError, + > { + if self.enable_debug_rpc { + let debug_status = + self.client_send(DebugStatus::BlockStatus(starting_height)).await?.rpc_into(); + Ok(Some(unc_jsonrpc_primitives::types::status::RpcDebugStatusResponse { + status_response: debug_status, + })) + } else { + Ok(None) + } + } + + pub async fn protocol_config( + &self, + request_data: unc_jsonrpc_primitives::types::config::RpcProtocolConfigRequest, + ) -> Result< + unc_jsonrpc_primitives::types::config::RpcProtocolConfigResponse, + unc_jsonrpc_primitives::types::config::RpcProtocolConfigError, + > { + let config_view = + self.view_client_send(GetProtocolConfig(request_data.block_reference)).await?; + Ok(RpcProtocolConfigResponse { config_view }) + } + + async fn query( + &self, + request_data: unc_jsonrpc_primitives::types::query::RpcQueryRequest, + ) -> Result< + unc_jsonrpc_primitives::types::query::RpcQueryResponse, + unc_jsonrpc_primitives::types::query::RpcQueryError, + > { + let query_response = self + .view_client_send(Query::new(request_data.block_reference, request_data.request)) + .await?; + Ok(query_response.rpc_into()) + } + + async fn tx_status_common( + &self, + request_data: unc_jsonrpc_primitives::types::transactions::RpcTransactionStatusRequest, + fetch_receipt: bool, + ) -> Result< + unc_jsonrpc_primitives::types::transactions::RpcTransactionResponse, + unc_jsonrpc_primitives::types::transactions::RpcTransactionError, + > { + let tx_status = self + .tx_status_fetch(request_data.transaction_info, request_data.wait_until, fetch_receipt) + .await?; + Ok(tx_status.rpc_into()) + } + + async fn get_provider( + &self, + request_data: unc_jsonrpc_primitives::types::provider::RpcProviderRequest, + ) -> Result< + unc_jsonrpc_primitives::types::provider::RpcProviderResponse, + unc_jsonrpc_primitives::types::provider::RpcProviderError, + > { + let provider_account = self.view_client_send(GetProvider(request_data.epoch_id, request_data.block_height)).await?; + Ok(unc_jsonrpc_primitives::types::provider::RpcProviderResponse{ provider_account }) + } + + async fn miner_chips_list( + &self, + request_data: unc_jsonrpc_primitives::types::miner_chips_list::RpcMinerChipsListRequest, + ) -> Result< + unc_jsonrpc_primitives::types::miner_chips_list::RpcMinerChipsListResponse, + unc_jsonrpc_primitives::types::miner_chips_list::RpcMinerChipsListError, + > { + let account_id = request_data.account_id; + let chips = self.view_client_send(MinerChipsList(account_id.clone())).await?; + Ok(unc_jsonrpc_primitives::types::miner_chips_list::RpcMinerChipsListResponse{ account_id, chips: chips.chips }) + } + + async fn block( + &self, + request_data: unc_jsonrpc_primitives::types::blocks::RpcBlockRequest, + ) -> Result< + unc_jsonrpc_primitives::types::blocks::RpcBlockResponse, + unc_jsonrpc_primitives::types::blocks::RpcBlockError, + > { + let block_view = self.view_client_send(GetBlock(request_data.block_reference)).await?; + Ok(unc_jsonrpc_primitives::types::blocks::RpcBlockResponse { block_view }) + } + + async fn chunk( + &self, + request_data: unc_jsonrpc_primitives::types::chunks::RpcChunkRequest, + ) -> Result< + unc_jsonrpc_primitives::types::chunks::RpcChunkResponse, + unc_jsonrpc_primitives::types::chunks::RpcChunkError, + > { + let chunk_view = + self.view_client_send(GetChunk::rpc_from(request_data.chunk_reference)).await?; + Ok(unc_jsonrpc_primitives::types::chunks::RpcChunkResponse { chunk_view }) + } + + async fn receipt( + &self, + request_data: unc_jsonrpc_primitives::types::receipts::RpcReceiptRequest, + ) -> Result< + unc_jsonrpc_primitives::types::receipts::RpcReceiptResponse, + unc_jsonrpc_primitives::types::receipts::RpcReceiptError, + > { + match self + .view_client_send(GetReceipt { receipt_id: request_data.receipt_reference.receipt_id }) + .await? + { + Some(receipt_view) => { + Ok(unc_jsonrpc_primitives::types::receipts::RpcReceiptResponse { receipt_view }) + } + None => { + Err(unc_jsonrpc_primitives::types::receipts::RpcReceiptError::UnknownReceipt { + receipt_id: request_data.receipt_reference.receipt_id, + }) + } + } + } + + async fn changes_in_block( + &self, + request: unc_jsonrpc_primitives::types::changes::RpcStateChangesInBlockRequest, + ) -> Result< + unc_jsonrpc_primitives::types::changes::RpcStateChangesInBlockByTypeResponse, + unc_jsonrpc_primitives::types::changes::RpcStateChangesError, + > { + let block: unc_primitives::views::BlockView = + self.view_client_send(GetBlock(request.block_reference)).await?; + + let block_hash = block.header.hash; + let changes = self.view_client_send(GetStateChangesInBlock { block_hash }).await?; + + Ok(unc_jsonrpc_primitives::types::changes::RpcStateChangesInBlockByTypeResponse { + block_hash: block.header.hash, + changes, + }) + } + + async fn changes_in_block_by_type( + &self, + request: unc_jsonrpc_primitives::types::changes::RpcStateChangesInBlockByTypeRequest, + ) -> Result< + unc_jsonrpc_primitives::types::changes::RpcStateChangesInBlockResponse, + unc_jsonrpc_primitives::types::changes::RpcStateChangesError, + > { + let block: unc_primitives::views::BlockView = + self.view_client_send(GetBlock(request.block_reference)).await?; + + let block_hash = block.header.hash; + let changes = self + .view_client_send(GetStateChanges { + block_hash, + state_changes_request: request.state_changes_request, + }) + .await?; + + Ok(unc_jsonrpc_primitives::types::changes::RpcStateChangesInBlockResponse { + block_hash: block.header.hash, + changes, + }) + } + + async fn next_light_client_block( + &self, + request: unc_jsonrpc_primitives::types::light_client::RpcLightClientNextBlockRequest, + ) -> Result< + unc_jsonrpc_primitives::types::light_client::RpcLightClientNextBlockResponse, + unc_jsonrpc_primitives::types::light_client::RpcLightClientNextBlockError, + > { + let response = self + .view_client_send(GetNextLightClientBlock { last_block_hash: request.last_block_hash }) + .await?; + Ok(response.rpc_into()) + } + + async fn light_client_execution_outcome_proof( + &self, + request: unc_jsonrpc_primitives::types::light_client::RpcLightClientExecutionProofRequest, + ) -> Result< + unc_jsonrpc_primitives::types::light_client::RpcLightClientExecutionProofResponse, + unc_jsonrpc_primitives::types::light_client::RpcLightClientProofError, + > { + let unc_jsonrpc_primitives::types::light_client::RpcLightClientExecutionProofRequest { + id, + light_client_head, + } = request; + + let execution_outcome_proof: unc_client_primitives::types::GetExecutionOutcomeResponse = + self.view_client_send(GetExecutionOutcome { id }).await?; + + let block_proof: unc_client_primitives::types::GetBlockProofResponse = self + .view_client_send(GetBlockProof { + block_hash: execution_outcome_proof.outcome_proof.block_hash, + head_block_hash: light_client_head, + }) + .await?; + + Ok(unc_jsonrpc_primitives::types::light_client::RpcLightClientExecutionProofResponse { + outcome_proof: execution_outcome_proof.outcome_proof, + outcome_root_proof: execution_outcome_proof.outcome_root_proof, + block_header_lite: block_proof.block_header_lite, + block_proof: block_proof.proof, + }) + } + + async fn network_info( + &self, + ) -> Result< + unc_jsonrpc_primitives::types::network_info::RpcNetworkInfoResponse, + unc_jsonrpc_primitives::types::network_info::RpcNetworkInfoError, + > { + let network_info = self.client_send(GetNetworkInfo {}).await?; + Ok(network_info.rpc_into()) + } + + async fn gas_price( + &self, + request_data: unc_jsonrpc_primitives::types::gas_price::RpcGasPriceRequest, + ) -> Result< + unc_jsonrpc_primitives::types::gas_price::RpcGasPriceResponse, + unc_jsonrpc_primitives::types::gas_price::RpcGasPriceError, + > { + let gas_price_view = + self.view_client_send(GetGasPrice { block_id: request_data.block_id }).await?; + Ok(unc_jsonrpc_primitives::types::gas_price::RpcGasPriceResponse { gas_price_view }) + } + + async fn validators( + &self, + request_data: unc_jsonrpc_primitives::types::validator::RpcValidatorRequest, + ) -> Result< + unc_jsonrpc_primitives::types::validator::RpcValidatorResponse, + unc_jsonrpc_primitives::types::validator::RpcValidatorError, + > { + let validator_info = self + .view_client_send(GetValidatorInfo { epoch_reference: request_data.epoch_reference }) + .await?; + Ok(unc_jsonrpc_primitives::types::validator::RpcValidatorResponse { validator_info }) + } + + /// Returns the current epoch validators ordered in the block producer order with repetition. + /// This endpoint is solely used for bridge currently and is not intended for other external use + /// cases. + async fn validators_ordered( + &self, + request: unc_jsonrpc_primitives::types::validator::RpcValidatorsOrderedRequest, + ) -> Result< + unc_jsonrpc_primitives::types::validator::RpcValidatorsOrderedResponse, + unc_jsonrpc_primitives::types::validator::RpcValidatorError, + > { + let unc_jsonrpc_primitives::types::validator::RpcValidatorsOrderedRequest { block_id } = + request; + let validators = self.view_client_send(GetValidatorOrdered { block_id }).await?; + Ok(validators) + } + + /// If experimental_debug_pages_src_path config is set, reads the html file from that + /// directory. Otherwise, returns None. + fn read_html_file_override(&self, html_file: &'static str) -> Option { + if let Some(directory) = &self.debug_pages_src_path { + let path = directory.join(html_file); + return Some(std::fs::read_to_string(path.clone()).unwrap_or_else(|err| { + format!("Could not load path {}: {:?}", path.display(), err) + })); + } + None + } + + /// Returns the future windows for maintenance in current epoch for the specified account + /// In the maintenance windows, the node will not be block producer or chunk producer + async fn maintenance_windows( + &self, + request: unc_jsonrpc_primitives::types::maintenance::RpcMaintenanceWindowsRequest, + ) -> Result< + unc_jsonrpc_primitives::types::maintenance::RpcMaintenanceWindowsResponse, + unc_jsonrpc_primitives::types::maintenance::RpcMaintenanceWindowsError, + > { + let unc_jsonrpc_primitives::types::maintenance::RpcMaintenanceWindowsRequest { + account_id, + } = request; + let windows = self.view_client_send(GetMaintenanceWindows { account_id }).await?; + Ok(windows.iter().map(|r| (r.start, r.end)).collect()) + } + + async fn client_config( + &self, + ) -> Result< + unc_jsonrpc_primitives::types::client_config::RpcClientConfigResponse, + unc_jsonrpc_primitives::types::client_config::RpcClientConfigError, + > { + let client_config = self.client_send(GetClientConfig {}).await?; + Ok(unc_jsonrpc_primitives::types::client_config::RpcClientConfigResponse { client_config }) + } + + pub async fn split_storage_info( + &self, + _request_data: unc_jsonrpc_primitives::types::split_storage::RpcSplitStorageInfoRequest, + ) -> Result< + unc_jsonrpc_primitives::types::split_storage::RpcSplitStorageInfoResponse, + unc_jsonrpc_primitives::types::split_storage::RpcSplitStorageInfoError, + > { + let split_storage = self.view_client_send(GetSplitStorageInfo {}).await?; + Ok(RpcSplitStorageInfoResponse { result: split_storage }) + } +} + +#[cfg(feature = "sandbox")] +impl JsonRpcHandler { + async fn sandbox_patch_state( + &self, + patch_state_request: unc_jsonrpc_primitives::types::sandbox::RpcSandboxPatchStateRequest, + ) -> Result< + unc_jsonrpc_primitives::types::sandbox::RpcSandboxPatchStateResponse, + unc_jsonrpc_primitives::types::sandbox::RpcSandboxPatchStateError, + > { + self.client_addr + .send( + unc_client_primitives::types::SandboxMessage::SandboxPatchState( + patch_state_request.records, + ) + .with_span_context(), + ) + .await + .map_err(RpcFrom::rpc_from)?; + + timeout(self.polling_config.polling_timeout, async { + loop { + let patch_state_finished = self + .client_addr + .send( + unc_client_primitives::types::SandboxMessage::SandboxPatchStateStatus {} + .with_span_context(), + ) + .await; + if let Ok( + unc_client_primitives::types::SandboxResponse::SandboxPatchStateFinished(true), + ) = patch_state_finished + { + break; + } + let _ = sleep(self.polling_config.polling_interval).await; + } + }) + .await + .expect("patch state should happen at next block, never timeout"); + + Ok(unc_jsonrpc_primitives::types::sandbox::RpcSandboxPatchStateResponse {}) + } + + async fn sandbox_fast_forward( + &self, + fast_forward_request: unc_jsonrpc_primitives::types::sandbox::RpcSandboxFastForwardRequest, + ) -> Result< + unc_jsonrpc_primitives::types::sandbox::RpcSandboxFastForwardResponse, + unc_jsonrpc_primitives::types::sandbox::RpcSandboxFastForwardError, + > { + use unc_client_primitives::types::SandboxResponse; + + self.client_addr + .send( + unc_client_primitives::types::SandboxMessage::SandboxFastForward( + fast_forward_request.delta_height, + ) + .with_span_context(), + ) + .await + .map_err(RpcFrom::rpc_from)?; + + // Hard limit the request to timeout at an hour, since fast forwarding can take a while, + // where we can leave it to the rpc clients to set their own timeouts if necessary. + timeout(Duration::from_secs(60 * 60), async { + loop { + let fast_forward_finished = self + .client_addr + .send( + unc_client_primitives::types::SandboxMessage::SandboxFastForwardStatus {} + .with_span_context(), + ) + .await; + + match fast_forward_finished { + Ok(SandboxResponse::SandboxFastForwardFinished(true)) => break, + Ok(SandboxResponse::SandboxFastForwardFailed(err)) => return Err(err), + _ => (), + } + + let _ = sleep(self.polling_config.polling_interval).await; + } + Ok(()) + }) + .await + .map_err(|_| { + unc_jsonrpc_primitives::types::sandbox::RpcSandboxFastForwardError::InternalError { + error_message: "sandbox failed to fast forward within reasonable time of an hour" + .to_string(), + } + })? + .map_err(|err| { + unc_jsonrpc_primitives::types::sandbox::RpcSandboxFastForwardError::InternalError { + error_message: format!("sandbox failed to fast forward due to: {:?}", err), + } + })?; + + Ok(unc_jsonrpc_primitives::types::sandbox::RpcSandboxFastForwardResponse {}) + } +} + +#[cfg(feature = "test_features")] +impl JsonRpcHandler { + async fn adv_disable_header_sync(&self, _params: Value) -> Result { + actix::spawn( + self.client_addr + .send( + unc_client::NetworkAdversarialMessage::AdvDisableHeaderSync + .with_span_context(), + ) + .map(|_| ()), + ); + actix::spawn( + self.view_client_addr + .send( + unc_client::NetworkAdversarialMessage::AdvDisableHeaderSync + .with_span_context(), + ) + .map(|_| ()), + ); + Ok(Value::String(String::new())) + } + + async fn adv_disable_doomslug(&self, _params: Value) -> Result { + actix::spawn( + self.client_addr + .send( + unc_client::NetworkAdversarialMessage::AdvDisableDoomslug.with_span_context(), + ) + .map(|_| ()), + ); + actix::spawn( + self.view_client_addr + .send( + unc_client::NetworkAdversarialMessage::AdvDisableDoomslug.with_span_context(), + ) + .map(|_| ()), + ); + Ok(Value::String(String::new())) + } + + async fn adv_produce_blocks(&self, params: Value) -> Result { + let (num_blocks, only_valid) = crate::api::Params::parse(params)?; + actix::spawn( + self.client_addr + .send( + unc_client::NetworkAdversarialMessage::AdvProduceBlocks( + num_blocks, only_valid, + ) + .with_span_context(), + ) + .map(|_| ()), + ); + Ok(Value::String(String::new())) + } + + async fn adv_switch_to_height(&self, params: Value) -> Result { + let (height,) = crate::api::Params::parse(params)?; + actix::spawn( + self.client_addr + .send( + unc_client::NetworkAdversarialMessage::AdvSwitchToHeight(height) + .with_span_context(), + ) + .map(|_| ()), + ); + actix::spawn( + self.view_client_addr + .send( + unc_client::NetworkAdversarialMessage::AdvSwitchToHeight(height) + .with_span_context(), + ) + .map(|_| ()), + ); + Ok(Value::String(String::new())) + } + + async fn adv_get_saved_blocks(&self, _params: Value) -> Result { + match self + .client_addr + .send(unc_client::NetworkAdversarialMessage::AdvGetSavedBlocks.with_span_context()) + .await + { + Ok(result) => match result { + Some(value) => serialize_response(value), + None => Err(RpcError::server_error::(None)), + }, + _ => Err(RpcError::server_error::(None)), + } + } + + async fn adv_check_store(&self, _params: Value) -> Result { + match self + .client_addr + .send( + unc_client::NetworkAdversarialMessage::AdvCheckStorageConsistency + .with_span_context(), + ) + .await + { + Ok(result) => match result { + Some(value) => serialize_response(value), + None => Err(RpcError::server_error::(None)), + }, + _ => Err(RpcError::server_error::(None)), + } + } +} + +fn rpc_handler( + message: web::Json, + handler: web::Data, +) -> impl Future> { + let response = async move { + let message = handler.process(message.0).await?; + Ok(HttpResponse::Ok().json(&message)) + }; + response.boxed() +} + +fn status_handler( + handler: web::Data, +) -> impl Future> { + metrics::HTTP_STATUS_REQUEST_COUNT.inc(); + + let response = async move { + match handler.status().await { + Ok(value) => Ok(HttpResponse::Ok().json(&value)), + Err(_) => Ok(HttpResponse::ServiceUnavailable().finish()), + } + }; + response.boxed() +} + +async fn debug_handler( + req: HttpRequest, + handler: web::Data, +) -> Result { + if req.path() == "/debug/api/status" { + // This is a temporary workaround - as we migrate the debug information to the separate class below. + return match handler.old_debug().await { + Ok(Some(value)) => Ok(HttpResponse::Ok().json(&value)), + Ok(None) => Ok(HttpResponse::MethodNotAllowed().finish()), + Err(_) => Ok(HttpResponse::ServiceUnavailable().finish()), + }; + } + match handler.debug(req.path()).await { + Ok(Some(value)) => Ok(HttpResponse::Ok().json(&value)), + Ok(None) => Ok(HttpResponse::MethodNotAllowed().finish()), + Err(_) => Ok(HttpResponse::ServiceUnavailable().finish()), + } +} + +async fn handle_entity_debug( + req: web::Json, + handler: web::Data, +) -> Result { + match handler.entity_debug_handler.query(req.0) { + Ok(value) => Ok(HttpResponse::Ok().json(&value)), + Err(err) => Ok(HttpResponse::ServiceUnavailable().body(format!("{:?}", err))), + } +} + +async fn debug_block_status_handler( + path: web::Path, + handler: web::Data, +) -> Result { + match handler.debug_block_status(Some(*path)).await { + Ok(Some(value)) => Ok(HttpResponse::Ok().json(&value)), + Ok(None) => Ok(HttpResponse::MethodNotAllowed().finish()), + Err(_) => Ok(HttpResponse::ServiceUnavailable().finish()), + } +} + +fn health_handler( + handler: web::Data, +) -> impl Future> { + let response = async move { + match handler.health().await { + Ok(value) => Ok(HttpResponse::Ok().json(&value)), + Err(_) => Ok(HttpResponse::ServiceUnavailable().finish()), + } + }; + response.boxed() +} + +fn network_info_handler( + handler: web::Data, +) -> impl Future> { + let response = async move { + match handler.network_info().await { + Ok(value) => Ok(HttpResponse::Ok().json(&value)), + Err(_) => Ok(HttpResponse::ServiceUnavailable().finish()), + } + }; + response.boxed() +} + +pub async fn prometheus_handler() -> Result { + metrics::PROMETHEUS_REQUEST_COUNT.inc(); + + let mut buffer = vec![]; + let encoder = TextEncoder::new(); + encoder.encode(&prometheus::gather(), &mut buffer).unwrap(); + + match String::from_utf8(buffer) { + Ok(text) => Ok(HttpResponse::Ok().body(text)), + Err(_) => Ok(HttpResponse::ServiceUnavailable().finish()), + } +} + +fn client_config_handler( + handler: web::Data, +) -> impl Future> { + let response = async move { + match handler.client_config().await { + Ok(value) => Ok(HttpResponse::Ok().json(&value)), + Err(_) => Ok(HttpResponse::ServiceUnavailable().finish()), + } + }; + response.boxed() +} + +fn get_cors(cors_allowed_origins: &[String]) -> Cors { + let mut cors = Cors::permissive(); + if cors_allowed_origins != ["*".to_string()] { + for origin in cors_allowed_origins { + cors = cors.allowed_origin(origin); + } + } + cors.allowed_methods(vec!["GET", "POST"]) + .allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT]) + .allowed_header(http::header::CONTENT_TYPE) + .max_age(3600) +} + +macro_rules! debug_page_string { + ($html_file: literal, $handler: expr) => { + $handler + .read_html_file_override($html_file) + .unwrap_or_else(|| include_str!(concat!("../res/", $html_file)).to_string()) + }; +} + +#[get("/debug")] +async fn debug_html( + handler: web::Data, +) -> actix_web::Result { + Ok(HttpResponse::Ok().body(debug_page_string!("debug.html", handler))) +} + +#[get("/debug/pages/{page}")] +async fn display_debug_html( + path: web::Path<(String,)>, + handler: web::Data, +) -> actix_web::Result { + let page_name = path.into_inner().0; + + let content = match page_name.as_str() { + "last_blocks" => Some(debug_page_string!("last_blocks.html", handler)), + "last_blocks.css" => Some(debug_page_string!("last_blocks.css", handler)), + "last_blocks.js" => Some(debug_page_string!("last_blocks.js", handler)), + "network_info" => Some(debug_page_string!("network_info.html", handler)), + "network_info.css" => Some(debug_page_string!("network_info.css", handler)), + "network_info.js" => Some(debug_page_string!("network_info.js", handler)), + "tier1_network_info" => Some(debug_page_string!("tier1_network_info.html", handler)), + "epoch_info" => Some(debug_page_string!("epoch_info.html", handler)), + "epoch_info.css" => Some(debug_page_string!("epoch_info.css", handler)), + "chain_n_chunk_info" => Some(debug_page_string!("chain_n_chunk_info.html", handler)), + "chain_n_chunk_info.css" => Some(debug_page_string!("chain_n_chunk_info.css", handler)), + "sync" => Some(debug_page_string!("sync.html", handler)), + "sync.css" => Some(debug_page_string!("sync.css", handler)), + "validator" => Some(debug_page_string!("validator.html", handler)), + "validator.css" => Some(debug_page_string!("validator.css", handler)), + "split_store" => Some(debug_page_string!("split_store.html", handler)), + _ => None, + }; + + match content { + Some(content) => { + Ok(HttpResponse::Ok().insert_header(header::ContentType::html()).body(content)) + } + None => Ok(HttpResponse::NotFound().finish()), + } +} + +/// Starts HTTP server(s) listening for RPC requests. +/// +/// Starts an HTTP server which handles JSON RPC calls as well as states +/// endpoints such as `/status`, `/health`, `/metrics` etc. Depending on +/// configuration may also start another HTTP server just for providing +/// Prometheus metrics (i.e. covering the `/metrics` path). +/// +/// Returns a vector of servers that have been started. Each server is returned +/// as a tuple containing a name of the server (e.g. `"JSON RPC"`) which can be +/// used in diagnostic messages and a [`actix_web::dev::Server`] object which +/// can be used to control the server (most notably stop it). +pub fn start_http( + config: RpcConfig, + genesis_config: GenesisConfig, + client_addr: Addr, + view_client_addr: Addr, + peer_manager_addr: Option>, + entity_debug_handler: Arc, +) -> Vec<(&'static str, actix_web::dev::ServerHandle)> { + let RpcConfig { + addr, + prometheus_addr, + cors_allowed_origins, + polling_config, + limits_config, + enable_debug_rpc, + experimental_debug_pages_src_path: debug_pages_src_path, + } = config; + let prometheus_addr = prometheus_addr.filter(|it| it != &addr.to_string()); + let cors_allowed_origins_clone = cors_allowed_origins.clone(); + info!(target:"network", "Starting http server at {}", addr); + let mut servers = Vec::new(); + let listener = HttpServer::new(move || { + App::new() + .wrap(get_cors(&cors_allowed_origins)) + .app_data(web::Data::new(JsonRpcHandler { + client_addr: client_addr.clone(), + view_client_addr: view_client_addr.clone(), + peer_manager_addr: peer_manager_addr.clone(), + polling_config, + genesis_config: genesis_config.clone(), + enable_debug_rpc, + debug_pages_src_path: debug_pages_src_path.clone().map(Into::into), + entity_debug_handler: entity_debug_handler.clone(), + })) + .app_data(web::JsonConfig::default().limit(limits_config.json_payload_max_size)) + .wrap(middleware::Logger::default()) + .service(web::resource("/").route(web::post().to(rpc_handler))) + .service( + web::resource("/status") + .route(web::get().to(status_handler)) + .route(web::head().to(status_handler)), + ) + .service( + web::resource("/health") + .route(web::get().to(health_handler)) + .route(web::head().to(health_handler)), + ) + .service(web::resource("/network_info").route(web::get().to(network_info_handler))) + .service(web::resource("/metrics").route(web::get().to(prometheus_handler))) + .service(web::resource("/debug/api/entity").route(web::post().to(handle_entity_debug))) + .service(web::resource("/debug/api/{api}").route(web::get().to(debug_handler))) + .service( + web::resource("/debug/api/block_status/{starting_height}") + .route(web::get().to(debug_block_status_handler)), + ) + .service( + web::resource("/debug/client_config").route(web::get().to(client_config_handler)), + ) + .service(debug_html) + .service(display_debug_html) + }); + + match listener.listen(addr.std_listener().unwrap()) { + std::result::Result::Ok(s) => { + let server = s.workers(4).shutdown_timeout(5).disable_signals().run(); + servers.push(("JSON RPC", server.handle())); + tokio::spawn(server); + } + std::result::Result::Err(e) => { + error!( + target:"network", + "Could not start http server at {} due to {:?}", &addr, e, + ) + } + }; + + if let Some(prometheus_addr) = prometheus_addr { + info!(target:"network", "Starting http monitoring server at {}", prometheus_addr); + // Export only the /metrics service. It's a read-only service and can have very relaxed + // access restrictions. + let listener = HttpServer::new(move || { + App::new() + .wrap(get_cors(&cors_allowed_origins_clone)) + .wrap(middleware::Logger::default()) + .service(web::resource("/metrics").route(web::get().to(prometheus_handler))) + }); + + match listener.bind(&prometheus_addr) { + std::result::Result::Ok(s) => { + let server = s.workers(2).shutdown_timeout(5).disable_signals().run(); + servers.push(("Prometheus Metrics", server.handle())); + tokio::spawn(server); + } + std::result::Result::Err(e) => { + error!( + target:"network", + "Can't export Prometheus metrics at {} due to {:?}", &prometheus_addr, e, + ) + } + }; + } + + servers +} diff --git a/chain/jsonrpc/src/metrics.rs b/chain/jsonrpc/src/metrics.rs new file mode 100644 index 000000000..60686f0fe --- /dev/null +++ b/chain/jsonrpc/src/metrics.rs @@ -0,0 +1,57 @@ +use unc_o11y::metrics::{exponential_buckets, HistogramVec, IntCounter, IntCounterVec}; +use once_cell::sync::Lazy; + +pub static RPC_PROCESSING_TIME: Lazy = Lazy::new(|| { + unc_o11y::metrics::try_create_histogram_vec( + "unc_rpc_processing_time", + "Time taken to process rpc queries", + &["method"], + Some(exponential_buckets(0.001, 2.0, 16).unwrap()), + ) + .unwrap() +}); +pub static RPC_TIMEOUT_TOTAL: Lazy = Lazy::new(|| { + unc_o11y::metrics::try_create_int_counter( + "unc_rpc_timeout_total", + "Total count of rpc queries that ended on timeout", + ) + .unwrap() +}); +pub static PROMETHEUS_REQUEST_COUNT: Lazy = Lazy::new(|| { + unc_o11y::metrics::try_create_int_counter( + "unc_http_prometheus_requests_total", + "Total count of Prometheus requests received", + ) + .unwrap() +}); +pub static HTTP_RPC_REQUEST_COUNT: Lazy = Lazy::new(|| { + unc_o11y::metrics::try_create_int_counter_vec( + "unc_rpc_total_count", + "Total count of HTTP RPC requests received, by method", + &["method"], + ) + .unwrap() +}); +pub static HTTP_STATUS_REQUEST_COUNT: Lazy = Lazy::new(|| { + unc_o11y::metrics::try_create_int_counter( + "unc_http_status_requests_total", + "Total count of HTTP Status requests received", + ) + .unwrap() +}); +pub static RPC_ERROR_COUNT: Lazy = Lazy::new(|| { + unc_o11y::metrics::try_create_int_counter_vec( + "unc_rpc_error_count", + "Total count of errors by method and message", + &["method", "err_code"], + ) + .unwrap() +}); +pub static RPC_UNREACHABLE_ERROR_COUNT: Lazy = Lazy::new(|| { + unc_o11y::metrics::try_create_int_counter_vec( + "unc_rpc_unreachable_errors_total", + "Total count of Unreachable RPC errors returned, by target error enum", + &["target_error_enum"], + ) + .unwrap() +}); diff --git a/chain/network/Cargo.toml b/chain/network/Cargo.toml new file mode 100644 index 000000000..fbacd5fa5 --- /dev/null +++ b/chain/network/Cargo.toml @@ -0,0 +1,91 @@ +[package] +name = "unc-network" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[build-dependencies] +anyhow.workspace = true +protobuf-codegen.workspace = true + +[dependencies] +async-trait.workspace = true +actix.workspace = true +anyhow.workspace = true +arc-swap.workspace = true +assert_matches.workspace = true +borsh.workspace = true +bytes.workspace = true +bytesize.workspace = true +chrono.workspace = true +crossbeam-channel.workspace = true +derive_more.workspace = true +futures-util.workspace = true +futures.workspace = true +im.workspace = true +itertools.workspace = true +lru.workspace = true +once_cell.workspace = true +opentelemetry.workspace = true +parking_lot.workspace = true +pin-project.workspace = true +protobuf.workspace = true +rand.workspace = true +rand_xorshift.workspace = true +rayon.workspace = true +serde.workspace = true +smart-default.workspace = true +strum.workspace = true +stun.workspace = true +thiserror.workspace = true +tokio.workspace = true +tokio-stream.workspace = true +tokio-util.workspace = true +tracing.workspace = true +time.workspace = true + +unc-async.workspace = true +unc-fmt.workspace = true +unc-o11y.workspace = true +unc-crypto.workspace = true +unc-performance-metrics.workspace = true +unc-performance-metrics-macros.workspace = true +unc-primitives.workspace = true +unc-stable-hasher.workspace = true +unc-store.workspace = true + +[dev-dependencies] +criterion.workspace = true +pretty_assertions.workspace = true +tempfile.workspace = true +rlimit.workspace = true +turn.workspace = true +webrtc-util.workspace = true + +[features] +nightly_protocol = [ + "unc-async/nightly_protocol", + "unc-fmt/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", +] +nightly = [ + "nightly_protocol", + "unc-async/nightly", + "unc-fmt/nightly", + "unc-o11y/nightly", + "unc-primitives/nightly", + "unc-store/nightly", +] +performance_stats = [ + "unc-performance-metrics/performance_stats", +] +test_features = [] diff --git a/chain/network/build.rs b/chain/network/build.rs new file mode 100644 index 000000000..af6fb632b --- /dev/null +++ b/chain/network/build.rs @@ -0,0 +1,9 @@ +fn main() -> anyhow::Result<()> { + println!("cargo:rerun-if-changed=src/network_protocol/network.proto"); + protobuf_codegen::Codegen::new() + .pure() + .includes(["src/"]) + .input("src/network_protocol/network.proto") + .cargo_out_dir("proto") + .run() +} diff --git a/chain/network/src/accounts_data/mod.rs b/chain/network/src/accounts_data/mod.rs new file mode 100644 index 000000000..0a99195ff --- /dev/null +++ b/chain/network/src/accounts_data/mod.rs @@ -0,0 +1,312 @@ +//! Cache of AccountData. It keeps AccountData for important accounts for the current epoch. +//! The set of important accounts for the given epoch is expected to never change (should be +//! deterministic). Note that "important accounts for the current epoch" is not limited to +//! "validators of the current epoch", but rather may include for example "validators of the next +//! epoch" so that AccountData of future validators is broadcasted in advance. +//! +//! Assumptions: +//! - verifying signatures is expensive, we need a dedicated threadpool for handling that. +//! TODO(gprusak): it would be nice to have a benchmark for that +//! - a bad peer may attack by sending a lot of invalid signatures +//! - we can afford verifying each valid signature of the current epoch once. +//! - we can afford verifying a few invalid signatures per SyncAccountsData message. +//! +//! Strategy: +//! - handling of SyncAccountsData should be throttled by PeerActor/PeerManagerActor. +//! - synchronously select interesting AccountData (i.e. those with newer version than any +//! previously seen for the given (account_id,epoch_id) pair. +//! - asynchronously verify signatures, until an invalid signature is encountered. +//! - if any signature is invalid, drop validation of the remaining signature and ban the peer +//! - all valid signatures verified, so far should be inserted, since otherwise we are open to the +//! following attack: +//! - a bad peer may spam us with + <1 invalid AccountData> +//! - we would validate everything every time, realizing that the last one is invalid, then +//! discarding the progress +//! - banning a peer wouldn't help since peers are anonymous, so a single attacker can act as a +//! lot of peers +use crate::concurrency; +use crate::concurrency::arc_mutex::ArcMutex; +use crate::network_protocol; +use crate::network_protocol::{AccountData, SignedAccountData, VersionedAccountData}; +use crate::types::AccountKeys; +use unc_async::time; +use unc_crypto::PublicKey; +use unc_primitives::validator_signer::ValidatorSigner; +use rayon::iter::ParallelBridge; +use std::collections::HashMap; +use std::sync::Arc; + +#[cfg(test)] +mod tests; + +#[derive(thiserror::Error, Debug, PartialEq, Eq)] +pub(crate) enum AccountDataError { + #[error("found an invalid signature")] + InvalidSignature, + #[error("found too large payload")] + DataTooLarge, + #[error("found multiple entries for the same (epoch_id,account_id)")] + SingleAccountMultipleData, +} + +/// Most up-to-date AccountData of this node and a signer +/// to sign it with when there is a need to override some +/// already signed data received from the network. See `AccountDataCache::set_local` +/// for more details. +#[derive(Clone)] +pub struct LocalAccountData { + pub signer: Arc, + pub data: Arc, +} + +/// See module-level documentation. +#[derive(Clone)] +pub struct AccountDataCacheSnapshot { + /// Map from account ID to account key. + /// Used only for selecting target when routing a message to a TIER1 peer. + /// TODO(gprusak): In fact, since the account key assigned to a given account ID can change + /// between epochs, Client should rather send messages to node with a specific account key, + /// rather than with a specific account ID. + pub keys_by_id: Arc, + /// Set of account keys allowed on TIER1 network. + pub keys: im::HashSet, + /// Current state of knowledge about an account. + /// `data.keys()` is a subset of `keys` at all times, + /// as cache is collecting data only about the accounts from `keys`, + /// and data about the particular account might be not known at the given moment. + pub data: im::HashMap>, + + pub local: Option, +} + +impl AccountDataCacheSnapshot { + /// Checks if `(d.version,d.timestamp)` is newer (greater) than + /// `(old.version,old.timestamp)`, where `old` is the AccountData for + /// `d.account_key` already stored in the AccountDataCache. + /// + /// It returns `false` in case `d.account_key` is not in `d.keys`, + /// because it means that `AccountDataCache` is not interested in these data at all. + /// + /// Note that when the node is restarted, it forgets + /// which version it has signed last, so it will again start from version + /// 0, until it learns from the network about data it already signed in the + /// previous execution. It means that a node may sign 2 data with the exact same + /// version. To avoid an inconsistent state of the network (i.e. situation in which + /// some nodes will learn about one data with the given version, some about the other) + /// we introduce a tie breaker in a form of UTC timestamp of signing (note that here + /// we do not rely on clock monotonicity, which is not guaranteed for UTC clock, + /// just assume that timestamp collision is unlikely). + /// + /// The alternatives to using a timestamp would be: + /// * adding a random_minor_version to AccountData, specifically to avoid collisions + /// (so we would be comparing `(version,random_minor_version)` instead) + /// * using some crypto hash function `h` and compare `(version,h(data))`. Assuming that `h` + /// behaves like a random oracle, the semantics will be equivaluent to + /// `random_minor_version`, except that if a node signs exactly the same data and in the + /// previous run, then there will be a collision. But in such a case it doesn't matter + /// since the data is the same. + /// * storing `version` of the last signed AccountData in persistent storage, + /// so that the node literally never signs AccountsData with colling versions. + /// This assumption is fragile as long as validators migrate their nodes without copying over + /// the whole storage. + fn is_new(&self, d: &SignedAccountData) -> bool { + self.keys.contains(&d.account_key) + && match self.data.get(&d.account_key) { + Some(old) if (old.version, old.timestamp) >= (d.version, d.timestamp) => false, + _ => true, + } + } + + /// Inserts d into self.data, if + /// * `d.account_data` is in self.keys AND + /// * `d.version > self.data[d.account_data].version`. + /// If d would override local for this node, an AccountData based on `self.local` is signed + /// and inserted instead to rollback the overriding change (it can happen in case the node has + /// been restarted and we observe the old value emitted by the previous run). + /// It returns the newly inserted value (or None if nothing changed). + /// The returned value should be broadcasted to the network. + fn try_insert( + &mut self, + clock: &time::Clock, + d: Arc, + ) -> Option> { + if !self.is_new(&d) { + return None; + } + let d = match &self.local { + Some(local) if d.account_key == local.signer.public_key() => Arc::new( + VersionedAccountData { + data: local.data.as_ref().clone(), + account_key: local.signer.public_key(), + version: d.version + 1, + timestamp: clock.now_utc(), + } + .sign(local.signer.as_ref()) + .unwrap(), + ), + _ => d, + }; + self.data.insert(d.account_key.clone(), d.clone()); + Some(d) + } + + /// Set the information about this node's account (i.e. AccountData for this node). + /// It should be called whenever AccountData for the current node changes. This function + /// is expected to be called periodically, even if AccountData doesn't change. + /// + /// If `self.signer` is in `self.keys` then it means that this node is a TIER1 node and + /// the new AccountData should be broadcasted immediately - set_local() will return a value + /// to be broadcasted then. Even if the AccountData didn't change since the last call to + /// set_local(), a value to be broadcasted will be returned (just with newer timestamp). + /// It is important to tell the network that "yes, I'm still alive, my AccountData didn't + /// change" (eventually we might want to use the timestamp set expiration date on AccountData). + /// + /// If `self.signer` is not in `self.keys` (this is not a TIER1 node), set_local() returns + /// None. + fn set_local( + &mut self, + clock: &time::Clock, + local: LocalAccountData, + ) -> Option> { + let account_key = local.signer.public_key(); + let result = match self.keys.contains(&account_key) { + false => None, + true => { + let d = Arc::new( + VersionedAccountData { + data: local.data.as_ref().clone(), + account_key: account_key.clone(), + version: self.data.get(&account_key).map_or(0, |d| d.version) + 1, + timestamp: clock.now_utc(), + } + .sign(local.signer.as_ref()) + .unwrap(), + ); + self.data.insert(account_key, d.clone()); + Some(d) + } + }; + self.local = Some(local); + result + } +} + +pub(crate) struct AccountDataCache(ArcMutex); + +impl AccountDataCache { + pub fn new() -> Self { + Self(ArcMutex::new(AccountDataCacheSnapshot { + keys_by_id: Arc::new(AccountKeys::default()), + keys: im::HashSet::new(), + data: im::HashMap::new(), + local: None, + })) + } + + /// Updates the set of important accounts and their public keys. + /// The AccountData which is no longer important is dropped. + /// Returns true iff the set of accounts actually changed. + /// TODO(gprusak): note that local data won't be generated, even if it could be + /// (i.e. in case self.local.signer was not present in the old key set, but is in the new) + /// so a call to set_local afterwards is required to do that. For now it is fine because + /// the AccountDataCache owner is expected to call set_local periodically anyway. + pub fn set_keys(&self, keys_by_id: Arc) -> bool { + self.0 + .try_update(|mut inner| { + // Skip further processing if the key set didn't change. + // NOTE: if T implements Eq, then Arc short circuits equality for x == x. + if keys_by_id == inner.keys_by_id { + return Err(()); + } + inner.keys_by_id = keys_by_id; + inner.keys = inner.keys_by_id.values().flatten().cloned().collect(); + inner.data.retain(|k, _| inner.keys.contains(k)); + Ok(((), inner)) + }) + .is_ok() + } + + /// Selects new data and verifies the signatures. + /// Returns the verified new data and an optional error. + /// Note that even if error has been returned the partially validated output is returned + /// anyway. + async fn verify( + &self, + data: Vec>, + ) -> (Vec>, Option) { + // Filter out non-interesting data, so that we never check signatures for valid non-interesting data. + // Bad peers may force us to check signatures for fake data anyway, but we will ban them after first invalid signature. + let mut new_data = HashMap::new(); + let inner = self.0.load(); + for d in data { + // There is a limit on the amount of RAM occupied by per-account datasets. + // Broadcasting larger datasets is considered malicious behavior. + if d.payload().len() > network_protocol::MAX_ACCOUNT_DATA_SIZE_BYTES { + return (vec![], Some(AccountDataError::DataTooLarge)); + } + // We want the communication needed for broadcasting per-account data to be minimal. + // Therefore broadcasting multiple datasets per account is considered malicious + // behavior, since all but one are obviously outdated. + if new_data.contains_key(&d.account_key) { + return (vec![], Some(AccountDataError::SingleAccountMultipleData)); + } + // It is fine to broadcast data we already know about. + // It is fine to broadcast account data that we don't care about. + if inner.is_new(&d) { + new_data.insert(d.account_key.clone(), d); + } + } + + // Verify the signatures in parallel. + // Verification will stop at the first encountered error. + let (data, ok) = concurrency::rayon::run(move || { + concurrency::rayon::try_map(new_data.into_values().par_bridge(), |d| { + match d.payload().verify(&d.account_key) { + Ok(()) => Some(d), + Err(()) => None, + } + }) + }) + .await; + if !ok { + return (data, Some(AccountDataError::InvalidSignature)); + } + (data, None) + } + + pub fn set_local( + self: &Arc, + clock: &time::Clock, + local: LocalAccountData, + ) -> Option> { + self.0.update(|mut inner| { + let data = inner.set_local(clock, local); + (data, inner) + }) + } + + /// Verifies the signatures and inserts verified data to the cache. + /// Returns the data inserted and optionally a verification error. + /// WriteLock is acquired only for the final update (after verification). + pub async fn insert( + self: &Arc, + clock: &time::Clock, + data: Vec>, + ) -> (Vec>, Option) { + let this = self.clone(); + // Execute verification on the rayon threadpool. + let (data, err) = this.verify(data).await; + // Insert the successfully verified data, even if an error has been encountered. + let inserted = self.0.update(|mut inner| { + let inserted = data.into_iter().filter_map(|d| inner.try_insert(clock, d)).collect(); + (inserted, inner) + }); + // Return the inserted data. + (inserted, err) + } + + /// Loads the current cache snapshot. + pub fn load(&self) -> Arc { + self.0.load() + } +} diff --git a/chain/network/src/accounts_data/tests.rs b/chain/network/src/accounts_data/tests.rs new file mode 100644 index 000000000..784d038d6 --- /dev/null +++ b/chain/network/src/accounts_data/tests.rs @@ -0,0 +1,284 @@ +use crate::accounts_data::*; +use crate::network_protocol::testonly as data; +use crate::network_protocol::SignedAccountData; +use crate::testonly::{assert_is_superset, make_rng, AsSet as _, Rng}; +use unc_async::time; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::validator_signer::InMemoryValidatorSigner; +use pretty_assertions::assert_eq; +use std::collections::HashSet; +use std::sync::Arc; + +fn make_account_data( + rng: &mut Rng, + clock: &time::Clock, + version: u64, + signer: &InMemoryValidatorSigner, +) -> SignedAccountData { + let peer_id = data::make_peer_id(rng); + data::make_account_data(rng, version, clock.now_utc(), signer.public_key(), peer_id) + .sign(signer) + .unwrap() +} + +fn unwrap<'a, T: std::hash::Hash + std::cmp::Eq, E: std::fmt::Debug>( + v: &'a (T, Option), +) -> &'a T { + if let Some(err) = &v.1 { + panic!("unexpected error: {err:?}"); + } + &v.0 +} + +fn make_signers(rng: &mut Rng, n: usize) -> Vec { + (0..n).map(|_| data::make_validator_signer(rng)).collect() +} + +#[tokio::test] +async fn happy_path() { + init_test_logger(); + let mut rng = make_rng(2947294234); + let rng = &mut rng; + let clock = time::FakeClock::default(); + + let signers: Vec<_> = make_signers(rng, 7); + let e0 = Arc::new(data::make_account_keys(&signers[0..5])); + let e1 = Arc::new(data::make_account_keys(&signers[2..7])); + + let cache = Arc::new(AccountDataCache::new()); + assert_eq!(cache.load().data.values().count(), 0); // initially empty + assert!(cache.set_keys(e0.clone())); + assert_eq!(cache.load().data.values().count(), 0); // empty after initial set_keys. + + // initial insert + let a0 = Arc::new(make_account_data(rng, &clock.clock(), 1, &signers[0])); + let a1 = Arc::new(make_account_data(rng, &clock.clock(), 1, &signers[1])); + let res = cache.clone().insert(&clock.clock(), vec![a0.clone(), a1.clone()]).await; + assert_eq!([&a0, &a1].as_set(), unwrap(&res).as_set()); + assert_eq!([&a0, &a1].as_set(), cache.load().data.values().collect::>()); + + // entries of various types + let a0new = Arc::new(make_account_data(rng, &clock.clock(), 2, &signers[0])); + let a1old = Arc::new(make_account_data(rng, &clock.clock(), 0, &signers[1])); + let a2 = Arc::new(make_account_data(rng, &clock.clock(), 1, &signers[2])); + let a5 = Arc::new(make_account_data(rng, &clock.clock(), 1, &signers[5])); + let res = cache + .clone() + .insert( + &clock.clock(), + vec![ + a2.clone(), // initial value => insert + a0new.clone(), // with newer timestamp => insert, + a1old.clone(), // with older timestamp => filter out, + a5.clone(), // not in e0 => filter out. + ], + ) + .await; + assert_eq!([&a2, &a0new].as_set(), unwrap(&res).as_set()); + assert_eq!([&a0new, &a1, &a2].as_set(), cache.load().data.values().collect::>()); + + // try setting the same key set again, should be a noop. + assert!(!cache.set_keys(e0)); + assert_eq!([&a0new, &a1, &a2].as_set(), cache.load().data.values().collect::>()); + + // set_keys again. Data for accounts which are not in the new set should be dropped. + assert!(cache.set_keys(e1)); + assert_eq!([&a2].as_set(), cache.load().data.values().collect::>()); + // insert some entries again. + let res = cache + .clone() + .insert( + &clock.clock(), + vec![ + a0.clone(), // a0 is not in e1 => filter out + a5.clone(), // a5 is in e1 => insert, + ], + ) + .await; + assert_eq!([&a5].as_set(), unwrap(&res).as_set()); + assert_eq!([&a2, &a5].as_set(), cache.load().data.values().collect::>()); +} + +#[tokio::test] +async fn data_too_large() { + init_test_logger(); + let mut rng = make_rng(2947294234); + let rng = &mut rng; + let clock = time::FakeClock::default(); + + let signers = make_signers(rng, 3); + let e = Arc::new(data::make_account_keys(&signers)); + + let cache = Arc::new(AccountDataCache::new()); + cache.set_keys(e); + let a0 = Arc::new(make_account_data(rng, &clock.clock(), 1, &signers[0])); + let a1 = Arc::new(make_account_data(rng, &clock.clock(), 1, &signers[1])); + let mut a2_too_large: SignedAccountData = + make_account_data(rng, &clock.clock(), 1, &signers[2]); + *a2_too_large.payload_mut() = + (0..crate::network_protocol::MAX_ACCOUNT_DATA_SIZE_BYTES + 1).map(|_| 17).collect(); + let a2_too_large = Arc::new(a2_too_large); + + // too large payload => DataTooLarge + let res = cache + .clone() + .insert( + &clock.clock(), + vec![ + a0.clone(), + a1.clone(), + a2_too_large.clone(), // invalid entry => DataTooLarge + ], + ) + .await; + assert_eq!(Some(AccountDataError::DataTooLarge), res.1); + // Partial update is allowed, in case an error is encountered. + assert_is_superset(&[&a0, &a1].as_set(), &res.0.as_set()); + // Partial update should match the state. + assert_eq!(res.0.as_set(), cache.load().data.values().collect::>()); +} + +#[tokio::test] +async fn invalid_signature() { + init_test_logger(); + let mut rng = make_rng(2947294234); + let rng = &mut rng; + let clock = time::FakeClock::default(); + + let signers = make_signers(rng, 3); + let e = Arc::new(data::make_account_keys(&signers)); + + let cache = Arc::new(AccountDataCache::new()); + cache.set_keys(e); + let a0 = Arc::new(make_account_data(rng, &clock.clock(), 1, &signers[0])); + let mut a1 = make_account_data(rng, &clock.clock(), 1, &signers[1]); + let mut a2_invalid_sig = make_account_data(rng, &clock.clock(), 1, &signers[2]); + *a2_invalid_sig.signature_mut() = a1.signature_mut().clone(); + let a1 = Arc::new(a1); + let a2_invalid_sig = Arc::new(a2_invalid_sig); + + // invalid signature => InvalidSignature + let res = cache + .clone() + .insert( + &clock.clock(), + vec![ + a0.clone(), + a1.clone(), + a2_invalid_sig.clone(), // invalid entry => DataTooLarge + ], + ) + .await; + assert_eq!(Some(AccountDataError::InvalidSignature), res.1); + // Partial update is allowed, in case an error is encountered. + assert_is_superset(&[&a0, &a1].as_set(), &res.0.as_set()); + // Partial update should match the state. + assert_eq!(res.0.as_set(), cache.load().data.values().collect::>()); +} + +#[tokio::test] +async fn single_account_multiple_data() { + init_test_logger(); + let mut rng = make_rng(2947294234); + let rng = &mut rng; + let clock = time::FakeClock::default(); + + let signers = make_signers(rng, 3); + let e = Arc::new(data::make_account_keys(&signers)); + + let cache = Arc::new(AccountDataCache::new()); + cache.set_keys(e); + let a0 = Arc::new(make_account_data(rng, &clock.clock(), 1, &signers[0])); + let a1 = Arc::new(make_account_data(rng, &clock.clock(), 1, &signers[1])); + let a2old = Arc::new(make_account_data(rng, &clock.clock(), 1, &signers[2])); + let a2new = Arc::new(make_account_data(rng, &clock.clock(), 2, &signers[2])); + + // 2 entries for the same (epoch_id,account_id) => SingleAccountMultipleData + let res = cache + .clone() + .insert(&clock.clock(), vec![a0.clone(), a1.clone(), a2old.clone(), a2new.clone()]) + .await; + assert_eq!(Some(AccountDataError::SingleAccountMultipleData), res.1); + // Partial update is allowed, in case an error is encountered. + assert_is_superset(&[&a0, &a1, &a2old, &a2new].as_set(), &res.0.as_set()); + // Partial update should match the state, this also verifies that only 1 of the competing + // entries has been applied. + assert_eq!(res.0.as_set(), cache.load().data.values().collect::>()); +} + +/// Test checking that cache immediately overrides any inserted AccountData for local.signer +/// with local.data. +#[tokio::test] +async fn set_local() { + init_test_logger(); + let mut rng = make_rng(2947294234); + let rng = &mut rng; + let clock = time::FakeClock::default(); + + let signers: Vec<_> = make_signers(rng, 3); + let e0 = Arc::new(data::make_account_keys(&signers[0..2])); + let e1 = Arc::new(data::make_account_keys(&signers[1..3])); + + let cache = Arc::new(AccountDataCache::new()); + assert!(cache.set_keys(e0.clone())); + + // Set local while local.signer is in cache.keys. + // A new AccountData should be signed. + let local = LocalAccountData { + signer: Arc::new(signers[0].clone()), + data: Arc::new(make_account_data(rng, &clock.clock(), 1, &signers[0]).data.clone()), + }; + let got = cache.set_local(&clock.clock(), local.clone()).unwrap(); + assert_eq!(local.data.as_ref(), &got.data); + assert_eq!(local.signer.public_key(), got.account_key); + clock.advance(time::Duration::hours(1)); + + // Insert new version while local data is set and local.signer is in cache.keys. + // AccountDataCache should immediately emit AccountData for local.signer which overrides + // the new version received. + let a0 = Arc::new(make_account_data(rng, &clock.clock(), 7, &signers[0])); + // Regular entry for a signer in cache.keys. The new version should be accepted. + let a1 = Arc::new(make_account_data(rng, &clock.clock(), 10, &signers[1])); + // Regular entry for a signer outside of cache.keys. The new version should be ignored. + let a2 = Arc::new(make_account_data(rng, &clock.clock(), 8, &signers[2])); + + let res = cache.clone().insert(&clock.clock(), vec![a0.clone(), a1.clone(), a2.clone()]).await; + assert_eq!(res.0.as_set(), cache.load().data.values().collect::>()); + let res: HashMap<_, _> = unwrap(&res).iter().map(|a| (a.account_key.clone(), a)).collect(); + let got = res.get(&signers[0].public_key()).unwrap(); + assert_eq!(local.data.as_ref(), &got.data); + assert!(a0.version < got.version); + assert_eq!(a1.as_ref(), res.get(&signers[1].public_key()).unwrap().as_ref()); + assert_eq!(None, res.get(&signers[2].public_key())); + + // Insert new version while local data is set but local.signer is not in cache.keys. + // local data should be just ignored. + clock.advance(time::Duration::hours(1)); + assert!(cache.set_keys(e1.clone())); + let a0 = Arc::new(make_account_data(rng, &clock.clock(), got.version + 1, &signers[0])); + // Regular entries for a signers in cache.keys. The new version should be accepted. + let a1 = Arc::new(make_account_data(rng, &clock.clock(), a1.version + 1, &signers[1])); + let a2 = Arc::new(make_account_data(rng, &clock.clock(), a2.version + 1, &signers[2])); + + let res = cache.clone().insert(&clock.clock(), vec![a0.clone(), a1.clone(), a2.clone()]).await; + assert_eq!(res.0.as_set(), cache.load().data.values().collect::>()); + assert_eq!([&a1, &a2].as_set(), unwrap(&res).as_set()); + + // Update local data to a signer in cache.keys. + let local = LocalAccountData { + signer: Arc::new(signers[2].clone()), + data: Arc::new(make_account_data(rng, &clock.clock(), 1, &signers[2]).data.clone()), + }; + let got = cache.set_local(&clock.clock(), local.clone()).unwrap(); + assert_eq!(local.data.as_ref(), &got.data); + assert_eq!(local.signer.public_key(), got.account_key); + assert_eq!([&a1, &got].as_set(), cache.load().data.values().collect::>()); + + // Update local data to a signer outside of cache.keys. + let local = LocalAccountData { + signer: Arc::new(signers[0].clone()), + data: Arc::new(make_account_data(rng, &clock.clock(), 1, &signers[0]).data.clone()), + }; + assert_eq!(None, cache.set_local(&clock.clock(), local)); + assert_eq!([&a1, &got].as_set(), cache.load().data.values().collect::>()); +} diff --git a/chain/network/src/actix.rs b/chain/network/src/actix.rs new file mode 100644 index 000000000..c7ba49b11 --- /dev/null +++ b/chain/network/src/actix.rs @@ -0,0 +1,55 @@ +use anyhow::anyhow; + +// A system thread which is joined on drop. +// TODO: replace with std::thread::ScopedJoinHandle once it is stable. +pub struct Thread(Option>>); + +impl Thread { + pub fn spawn anyhow::Result<()>>(f: F) -> Self { + Self(Some(std::thread::spawn(f))) + } +} + +impl Drop for Thread { + fn drop(&mut self) { + let res = self.0.take().unwrap().join(); + // Panic, unless we are in test and are already panicking. + // A double panic prevents "cargo test" from displaying error message. + if !std::thread::panicking() { + res.unwrap().unwrap(); + } + } +} + +pub struct ActixSystem { + pub addr: actix::Addr
, + system: actix::System, + // dropping _thread has a side effect of joining the system thread. + // Still, linter considers it a dead_code, so "_" is needed to silence it. + _thread: Thread, +} + +impl ActixSystem { + pub async fn spawn actix::Addr>(f: F) -> Self { + let (send, recv) = tokio::sync::oneshot::channel(); + let thread = Thread::spawn(move || { + let s = actix::System::new(); + s.block_on(async move { + let system = actix::System::current(); + let addr = f(); + send.send((system, addr)).map_err(|_| anyhow!("send failed")) + }) + .unwrap(); + s.run().unwrap(); + Ok(()) + }); + let (system, addr) = recv.await.unwrap(); + Self { addr, system, _thread: thread } + } +} + +impl Drop for ActixSystem { + fn drop(&mut self) { + self.system.stop(); + } +} diff --git a/chain/network/src/announce_accounts/mod.rs b/chain/network/src/announce_accounts/mod.rs new file mode 100644 index 000000000..1e9223da9 --- /dev/null +++ b/chain/network/src/announce_accounts/mod.rs @@ -0,0 +1,117 @@ +use crate::store; +use lru::LruCache; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::types::AccountId; +use parking_lot::Mutex; +use std::collections::HashMap; + +#[cfg(test)] +mod tests; + +const ANNOUNCE_ACCOUNT_CACHE_SIZE: usize = 10_000; + +struct Inner { + /// Maps an account_id to a peer owning it. + account_peers: LruCache, + /// Subset of account_peers, which we have broadcasted to the peers. + /// It is used to skip rebroadcasting the same data multiple times. + /// It contains less entries than account_peers in case some AnnounceAccounts + /// have been loaded from storage without broadcasting. + account_peers_broadcasted: LruCache, + /// Access to store on disk + store: store::Store, +} + +impl Inner { + /// Get AnnounceAccount for the given AccountId. + fn get_announce(&mut self, account_id: &AccountId) -> Option { + if let Some(announce_account) = self.account_peers.get(account_id) { + return Some(announce_account.clone()); + } + + match self.store.get_account_announcement(&account_id) { + Err(err) => { + tracing::warn!(target: "network", "Error loading announce account from store: {:?}", err); + None + } + Ok(None) => None, + Ok(Some(stored_announce_account)) => { + self.account_peers.put(account_id.clone(), stored_announce_account.clone()); + Some(stored_announce_account) + } + } + } +} + +pub(crate) struct AnnounceAccountCache(Mutex); + +impl AnnounceAccountCache { + pub fn new(store: store::Store) -> Self { + Self(Mutex::new(Inner { + account_peers: LruCache::new(ANNOUNCE_ACCOUNT_CACHE_SIZE), + account_peers_broadcasted: LruCache::new(ANNOUNCE_ACCOUNT_CACHE_SIZE), + store, + })) + } + + /// Adds accounts to the cache. + /// Returns the diff: new values that should be broadcasted. + /// Note: There is at most one peer id per account id. + pub(crate) fn add_accounts( + &self, + account_announcements: Vec, + ) -> Vec { + let mut inner = self.0.lock(); + let mut res = vec![]; + for announcement in account_announcements { + let account_id = &announcement.account_id; + let epoch_id = &announcement.epoch_id; + + // We skip broadcasting stuff that is already broadcasted. + if inner.account_peers_broadcasted.get(account_id).map(|x| &x.epoch_id) + == Some(epoch_id) + { + continue; + } + + inner.account_peers.put(account_id.clone(), announcement.clone()); + inner.account_peers_broadcasted.put(account_id.clone(), announcement.clone()); + + // Add account to store. Best effort + if let Err(e) = inner.store.set_account_announcement(account_id, &announcement) { + tracing::warn!(target: "network", "Error saving announce account to store: {:?}", e); + } + res.push(announcement); + } + res + } + + /// Find peer that owns this AccountId. + pub(crate) fn get_account_owner(&self, account_id: &AccountId) -> Option { + self.0.lock().get_announce(account_id).map(|announce_account| announce_account.peer_id) + } + + /// Public interface for `account_peers`. + /// Get keys currently on cache. + pub(crate) fn get_accounts_keys(&self) -> Vec { + self.0.lock().account_peers.iter().map(|(k, _)| k).cloned().collect() + } + + /// Get announce accounts on cache. + pub(crate) fn get_announcements(&self) -> Vec { + self.0.lock().account_peers.iter().map(|(_, v)| v.clone()).collect() + } + + /// Get AnnounceAccount for the given AccountIds, that we already broadcasted. + pub(crate) fn get_broadcasted_announcements<'a>( + &'a self, + account_ids: impl Iterator, + ) -> HashMap { + let mut inner = self.0.lock(); + account_ids + .filter_map(|id| { + inner.account_peers_broadcasted.get(id).map(|a| (id.clone(), a.clone())) + }) + .collect() + } +} diff --git a/chain/network/src/announce_accounts/tests.rs b/chain/network/src/announce_accounts/tests.rs new file mode 100644 index 000000000..220f3ef4c --- /dev/null +++ b/chain/network/src/announce_accounts/tests.rs @@ -0,0 +1,108 @@ +use crate::announce_accounts::*; +use crate::test_utils::{random_epoch_id, random_peer_id}; +use unc_crypto::Signature; +use unc_primitives::network::AnnounceAccount; + +#[test] +fn announcement_same_epoch() { + let store = crate::store::Store::from(unc_store::db::TestDB::new()); + + let peer_id0 = random_peer_id(); + let peer_id1 = random_peer_id(); + let epoch_id0 = random_epoch_id(); + + let announcements_cache = AnnounceAccountCache::new(store); + + let announce0 = AnnounceAccount { + account_id: "near0".parse().unwrap(), + peer_id: peer_id0.clone(), + epoch_id: epoch_id0.clone(), + signature: Signature::default(), + }; + + // Same as announce1 but with different peer id + let announce1 = AnnounceAccount { + account_id: "near0".parse().unwrap(), + peer_id: peer_id1, + epoch_id: epoch_id0, + signature: Signature::default(), + }; + + // Adding multiple announcements for the same account_id and epoch_id. + // The first one should win. + assert_eq!( + announcements_cache.add_accounts(vec![announce0.clone(), announce1.clone()]), + vec![announce0.clone()] + ); + assert_eq!(announcements_cache.get_announcements(), vec![announce0.clone()]); + assert_eq!(announcements_cache.get_account_owner(&announce0.account_id).unwrap(), peer_id0); + + // Adding a conflicting announcement later. Should be a noop. + assert_eq!(announcements_cache.add_accounts(vec![announce1]), vec![]); + assert_eq!(announcements_cache.get_announcements(), vec![announce0.clone()]); + assert_eq!(announcements_cache.get_account_owner(&announce0.account_id).unwrap(), peer_id0); +} + +#[test] +fn dont_load_on_build() { + let store = crate::store::Store::from(unc_store::db::TestDB::new()); + + let peer_id0 = random_peer_id(); + let peer_id1 = random_peer_id(); + let epoch_id0 = random_epoch_id(); + let epoch_id1 = random_epoch_id(); + + let announcements_cache = AnnounceAccountCache::new(store.clone()); + + let announce0 = AnnounceAccount { + account_id: "near0".parse().unwrap(), + peer_id: peer_id0, + epoch_id: epoch_id0, + signature: Signature::default(), + }; + + // Same as announce1 but with different peer id + let announce1 = AnnounceAccount { + account_id: "near1".parse().unwrap(), + peer_id: peer_id1, + epoch_id: epoch_id1, + signature: Signature::default(), + }; + + announcements_cache.add_accounts(vec![announce0.clone()]); + announcements_cache.add_accounts(vec![announce1.clone()]); + let accounts: Vec = announcements_cache.get_announcements(); + assert!(vec![announce0, announce1].iter().all(|announce| { accounts.contains(&announce) })); + assert_eq!(accounts.len(), 2); + + let announcements_cache1 = AnnounceAccountCache::new(store); + assert_eq!(announcements_cache1.get_announcements().len(), 0); +} + +#[test] +fn load_from_disk() { + let store = crate::store::Store::from(unc_store::db::TestDB::new()); + + let peer_id0 = random_peer_id(); + let epoch_id0 = random_epoch_id(); + + let announcements_cache = AnnounceAccountCache::new(store.clone()); + let announcements_cache1 = AnnounceAccountCache::new(store); + + let announce0 = AnnounceAccount { + account_id: "near0".parse().unwrap(), + peer_id: peer_id0.clone(), + epoch_id: epoch_id0, + signature: Signature::default(), + }; + + // Announcement is added to first cache and to disk + announcements_cache.add_accounts(vec![announce0.clone()]); + assert_eq!(announcements_cache.get_announcements().len(), 1); + // Second cache is empty + assert_eq!(announcements_cache1.get_announcements().len(), 0); + // Try to find this peer and load it from disk + assert_eq!(announcements_cache1.get_account_owner(&announce0.account_id).unwrap(), peer_id0); + // Second cache should contain account loaded from disk + assert_eq!(announcements_cache1.get_announcements().len(), 1); +} diff --git a/chain/network/src/blacklist.rs b/chain/network/src/blacklist.rs new file mode 100644 index 000000000..075a7fb26 --- /dev/null +++ b/chain/network/src/blacklist.rs @@ -0,0 +1,127 @@ +use std::collections::HashSet; +use std::net; + +/// Only IPv6 addresses are stored. IPv4 addresses are mapped to IPv6 before being added. +/// +/// Without the mapping, we could blacklist an IPv4 and still interact with that address if +/// it is presented as IPv6. +/// TODO: alternatively we could use IpAddr::to_canonical(), but then the variants of +/// the Entry enum would have to be private. +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub enum Entry { + Ip(net::Ipv6Addr), + IpPort(net::Ipv6Addr, u16), +} + +impl Entry { + pub fn from_ip(ip: net::IpAddr) -> Entry { + Entry::Ip(match ip { + net::IpAddr::V4(ip) => ip.to_ipv6_mapped(), + net::IpAddr::V6(ip) => ip, + }) + } + + pub fn from_addr(addr: net::SocketAddr) -> Entry { + Entry::IpPort( + match addr.ip() { + net::IpAddr::V4(ip) => ip.to_ipv6_mapped(), + net::IpAddr::V6(ip) => ip, + }, + addr.port(), + ) + } +} + +impl std::str::FromStr for Entry { + type Err = std::net::AddrParseError; + + fn from_str(s: &str) -> Result { + match s.parse::() { + Ok(ip) => Ok(Entry::from_ip(ip)), + Err(_) => Ok(Entry::from_addr(s.parse::()?)), + } + } +} + +/// A blacklist for socket addresses. Supports adding individual IP:port tuples +/// to the blacklist or entire IPs. +#[derive(Debug, Default, Clone)] +pub struct Blacklist(HashSet); + +// TODO(CP-34): merge Blacklist with whitelist functionality and replace them with sth +// like AuthorizationConfig. +impl FromIterator for Blacklist { + fn from_iter>(i: I) -> Self { + Self(i.into_iter().collect()) + } +} + +impl Blacklist { + /// Returns whether given address is on the blacklist. + pub fn contains(&self, addr: net::SocketAddr) -> bool { + self.0.contains(&Entry::from_ip(addr.ip())) || self.0.contains(&Entry::from_addr(addr)) + } +} + +#[cfg(test)] +mod test { + use super::*; + + const LO4: net::IpAddr = net::IpAddr::V4(net::Ipv4Addr::LOCALHOST); + const LO6: net::IpAddr = net::IpAddr::V6(net::Ipv6Addr::LOCALHOST); + + #[test] + fn test_parse_entry() { + fn parse(value: &str) -> Option { + value.parse().ok() + } + + assert_eq!(None, parse("foo")); + assert_eq!(None, parse("192.0.2.*")); + assert_eq!(None, parse("192.0.2.0/24")); + assert_eq!(None, parse("192.0.2.4.5")); + assert_eq!(None, parse("192.0.2.4:424242")); + + assert_eq!(parse("::ffff:192.0.2.4").unwrap(), parse("192.0.2.4").unwrap()); + assert_eq!(parse("[::ffff:192.0.2.4]:0").unwrap(), parse("192.0.2.4:0").unwrap()); + assert_eq!(parse("[::ffff:192.0.2.4]:42").unwrap(), parse("192.0.2.4:42").unwrap()); + + assert_eq!(Entry::from_ip(LO6), parse("::1").unwrap()); + assert_eq!(Entry::from_addr(net::SocketAddr::new(LO6, 42)), parse("[::1]:42").unwrap()); + + assert_eq!(Entry::from_ip(LO4), parse("::ffff:127.0.0.1").unwrap()); + assert_eq!( + Entry::from_addr(net::SocketAddr::new(LO4, 42)), + parse("[::ffff:127.0.0.1]:42").unwrap() + ); + } + + #[test] + fn test_blacklist() { + use std::net::*; + + let ip: net::IpAddr = net::Ipv4Addr::new(192, 0, 2, 4).into(); + + let mapped_ip = IpAddr::V6("::ffff:192.0.2.4".parse().unwrap()); + let mapped_lo4 = IpAddr::V6("::ffff:127.0.0.1".parse().unwrap()); + + let blacklist: Blacklist = [ + Entry::from_ip(LO4), + Entry::from_addr(SocketAddr::new(ip, 42)), + Entry::from_addr(SocketAddr::new(LO6, 42)), + ] + .into_iter() + .collect(); + + assert!(blacklist.contains(SocketAddr::new(LO4, 42))); + assert!(blacklist.contains(SocketAddr::new(LO4, 8080))); + assert!(blacklist.contains(SocketAddr::new(ip, 42))); + assert!(!blacklist.contains(SocketAddr::new(ip, 8080))); + assert!(blacklist.contains(SocketAddr::new(LO6, 42))); + assert!(!blacklist.contains(SocketAddr::new(LO6, 8080))); + assert!(blacklist.contains(SocketAddr::new(mapped_lo4, 42))); + assert!(blacklist.contains(SocketAddr::new(mapped_lo4, 8080))); + assert!(blacklist.contains(SocketAddr::new(mapped_ip, 42))); + assert!(!blacklist.contains(SocketAddr::new(mapped_ip, 8080))); + } +} diff --git a/chain/network/src/broadcast/mod.rs b/chain/network/src/broadcast/mod.rs new file mode 100644 index 000000000..0e9715478 --- /dev/null +++ b/chain/network/src/broadcast/mod.rs @@ -0,0 +1,102 @@ +//! TEST-ONLY: unbounded version of tokio::sync::broadcast channel. +//! It never forgets values, so should be only used in tests. +use crate::sink::Sink; +use std::sync::Arc; + +#[cfg(test)] +mod tests; + +pub fn unbounded_channel() -> (Sender, Receiver) { + let ch = Arc::new(Channel { + stream: std::sync::RwLock::new(vec![]), + notify: tokio::sync::Notify::new(), + }); + (Sender(ch.clone()), Receiver { next: 0, channel: ch }) +} + +struct Channel { + stream: std::sync::RwLock>, + notify: tokio::sync::Notify, +} + +pub struct Sender(Arc>); + +#[derive(Clone)] +pub struct Receiver { + channel: Arc>, + next: usize, +} + +impl Clone for Sender { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl Sender { + pub fn send(&self, v: T) { + let mut l = self.0.stream.write().unwrap(); + l.push(v); + self.0.notify.notify_waiters(); + } + + pub fn sink(&self) -> Sink { + let s = self.clone(); + Sink::new(move |v| s.send(v)) + } +} + +impl Receiver { + /// Returns a copy of the receiver which ignores all the events until now. + // TODO(gprusak): this still doesn't solve + // the events being mixed in the stream. + // Without actix, awaiting the expected state + // should get way easier. + pub fn from_now(&self) -> Self { + Self { channel: self.channel.clone(), next: self.channel.stream.read().unwrap().len() } + } + + /// recv() extracts a value from the channel. + /// If channel is empty, awaits until a value is pushed to the channel. + pub async fn recv(&mut self) -> T { + self.next += 1; + let new_value_pushed = { + // The lock has to be inside a block without await, + // because otherwise recv() is not Send. + let l = self.channel.stream.read().unwrap(); + let new_value_pushed = self.channel.notify.notified(); + // Synchronically check if the channel is non-empty. + // If so, pop a value and return immediately. + if let Some(v) = l.get(self.next - 1) { + return v.clone(); + } + new_value_pushed + }; + // Channel was empty, so we wait for the new value. + new_value_pushed.await; + let v = self.channel.stream.read().unwrap()[self.next - 1].clone(); + v + } + + /// Calls recv() in a loop until the returned value satisfies the predicate `pred` + /// (predicate is satisfied iff it returns `Some(u)`). Returns `u`. + /// All the values popped from the channel in the process are dropped silently. + pub async fn recv_until(&mut self, mut pred: impl FnMut(T) -> Option) -> U { + loop { + if let Some(u) = pred(self.recv().await) { + return u; + } + } + } + + /// Non-blocking version of recv(): pops a value from the channel, + /// or returns None if channel is empty. + pub fn try_recv(&mut self) -> Option { + let l = self.channel.stream.read().unwrap(); + if l.len() <= self.next { + return None; + } + self.next += 1; + Some(l[self.next - 1].clone()) + } +} diff --git a/chain/network/src/broadcast/tests.rs b/chain/network/src/broadcast/tests.rs new file mode 100644 index 000000000..ad19ba08d --- /dev/null +++ b/chain/network/src/broadcast/tests.rs @@ -0,0 +1,15 @@ +use crate::broadcast; + +#[tokio::test] +async fn channel() { + let (send, mut recv) = broadcast::unbounded_channel(); + send.send(1); + send.send(2); + send.send(3); + assert_eq!(1, recv.recv().await); + let mut recv2 = recv.clone(); + assert_eq!(2, recv.recv().await); + assert_eq!(3, recv.recv().await); + assert_eq!(2, recv2.recv().await); + assert_eq!(3, recv2.recv().await); +} diff --git a/chain/network/src/client.rs b/chain/network/src/client.rs new file mode 100644 index 000000000..e430681ad --- /dev/null +++ b/chain/network/src/client.rs @@ -0,0 +1,141 @@ +use crate::network_protocol::StateResponseInfo; + +use crate::types::{NetworkInfo, ReasonForBan}; + +use unc_primitives::block::{Approval, Block, BlockHeader}; +use unc_primitives::challenge::Challenge; +use unc_primitives::chunk_validation::{ChunkEndorsement, ChunkStateWitness}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{AccountId, EpochId, ShardId}; +use unc_primitives::views::FinalExecutionOutcomeView; + +/// A strongly typed asynchronous API for the Client logic. +/// It abstracts away the fact that client is implemented using actix +/// actors. +#[async_trait::async_trait] +pub trait Client: Send + Sync + 'static { + async fn tx_status_request( + &self, + account_id: AccountId, + tx_hash: CryptoHash, + ) -> Option>; + + async fn tx_status_response(&self, tx_result: FinalExecutionOutcomeView); + + async fn state_request_header( + &self, + shard_id: ShardId, + sync_hash: CryptoHash, + ) -> Result, ReasonForBan>; + + async fn state_request_part( + &self, + shard_id: ShardId, + sync_hash: CryptoHash, + part_id: u64, + ) -> Result, ReasonForBan>; + + async fn state_response(&self, info: StateResponseInfo); + + async fn block_approval(&self, approval: Approval, peer_id: PeerId); + + async fn transaction(&self, transaction: SignedTransaction, is_forwarded: bool); + + async fn block_request(&self, hash: CryptoHash) -> Option>; + + async fn block_headers_request(&self, hashes: Vec) -> Option>; + + async fn block(&self, block: Block, peer_id: PeerId, was_requested: bool); + + async fn block_headers( + &self, + headers: Vec, + peer_id: PeerId, + ) -> Result<(), ReasonForBan>; + + async fn challenge(&self, challenge: Challenge); + + async fn network_info(&self, info: NetworkInfo); + + async fn announce_account( + &self, + accounts: Vec<(AnnounceAccount, Option)>, + ) -> Result, ReasonForBan>; + + async fn chunk_state_witness(&self, witness: ChunkStateWitness); + + async fn chunk_endorsement(&self, endorsement: ChunkEndorsement); +} + +/// Implementation of Client which doesn't do anything and never returns errors. +pub struct Noop; + +#[async_trait::async_trait] +impl Client for Noop { + async fn tx_status_request( + &self, + _account_id: AccountId, + _tx_hash: CryptoHash, + ) -> Option> { + None + } + + async fn tx_status_response(&self, _tx_result: FinalExecutionOutcomeView) {} + + async fn state_request_header( + &self, + _shard_id: ShardId, + _sync_hash: CryptoHash, + ) -> Result, ReasonForBan> { + Ok(None) + } + + async fn state_request_part( + &self, + _shard_id: ShardId, + _sync_hash: CryptoHash, + _part_id: u64, + ) -> Result, ReasonForBan> { + Ok(None) + } + + async fn state_response(&self, _info: StateResponseInfo) {} + async fn block_approval(&self, _approval: Approval, _peer_id: PeerId) {} + + async fn transaction(&self, _transaction: SignedTransaction, _is_forwarded: bool) {} + + async fn block_request(&self, _hash: CryptoHash) -> Option> { + None + } + + async fn block_headers_request(&self, _hashes: Vec) -> Option> { + None + } + + async fn block(&self, _block: Block, _peer_id: PeerId, _was_requested: bool) {} + + async fn block_headers( + &self, + _headers: Vec, + _peer_id: PeerId, + ) -> Result<(), ReasonForBan> { + Ok(()) + } + + async fn challenge(&self, _challenge: Challenge) {} + + async fn network_info(&self, _info: NetworkInfo) {} + + async fn announce_account( + &self, + _accounts: Vec<(AnnounceAccount, Option)>, + ) -> Result, ReasonForBan> { + Ok(vec![]) + } + + async fn chunk_state_witness(&self, _witness: ChunkStateWitness) {} + + async fn chunk_endorsement(&self, _endorsement: ChunkEndorsement) {} +} diff --git a/chain/network/src/concurrency/arc_mutex.rs b/chain/network/src/concurrency/arc_mutex.rs new file mode 100644 index 000000000..54e96d59d --- /dev/null +++ b/chain/network/src/concurrency/arc_mutex.rs @@ -0,0 +1,45 @@ +use arc_swap::ArcSwap; +use std::sync::{Arc, Mutex}; + +/// Mutex which only synchronizes on writes. +/// Reads always succeed and return the latest written version. +pub struct ArcMutex { + value: ArcSwap, + mutex: Mutex<()>, +} + +impl ArcMutex { + pub fn new(v: T) -> Self { + Self { value: ArcSwap::new(Arc::new(v)), mutex: Mutex::new(()) } + } + + /// Loads the last value stored. Non-blocking. + pub fn load(&self) -> Arc { + self.value.load_full() + } + + /// Atomic update of the value. Blocking. + /// Note that `T -> (R,T)` is a state monad. + /// State monad is a function which takes the old state and + /// returns the new state + additional result value. + pub fn update(&self, f: impl FnOnce(T) -> (R, T)) -> R { + let _guard = self.mutex.lock().unwrap(); + let (res, val) = f(self.value.load().as_ref().clone()); + self.value.store(Arc::new(val)); + res + } + + /// Atomic update of the value. Value is not modified if an error is returned. Blocking. + /// Note that `T -> Result<(R,T),E>` is a state monad transformer applied to the exception + /// monad. + pub fn try_update(&self, f: impl FnOnce(T) -> Result<(R, T), E>) -> Result { + let _guard = self.mutex.lock().unwrap(); + match f(self.value.load().as_ref().clone()) { + Ok((res, val)) => { + self.value.store(Arc::new(val)); + Ok(res) + } + Err(e) => Err(e), + } + } +} diff --git a/chain/network/src/concurrency/asyncfn.rs b/chain/network/src/concurrency/asyncfn.rs new file mode 100644 index 000000000..ef012feb9 --- /dev/null +++ b/chain/network/src/concurrency/asyncfn.rs @@ -0,0 +1,21 @@ +use futures::future::{BoxFuture, Future, FutureExt}; + +/// Boxed asynchronous function. In rust asynchronous functions +/// are just regular functions which return a Future. +/// This is a convenience alias to express a +pub type BoxAsyncFn<'a, Arg, Res> = Box BoxFuture<'a, Res>>; + +/// AsyncFn trait represents asynchronous functions which can be boxed. +pub trait AsyncFn<'a, Arg: 'a, Res: 'a>: 'a { + fn wrap(self) -> BoxAsyncFn<'a, Arg, Res>; +} + +impl<'a, F: 'a, Arg: 'a, Res: 'a, Fut: 'a> AsyncFn<'a, Arg, Res> for F +where + F: FnOnce(Arg) -> Fut, + Fut: Send + Future, +{ + fn wrap(self) -> BoxAsyncFn<'a, Arg, Res> { + Box::new(move |a: Arg| self(a).boxed()) + } +} diff --git a/chain/network/src/concurrency/atomic_cell.rs b/chain/network/src/concurrency/atomic_cell.rs new file mode 100644 index 000000000..0c80d62df --- /dev/null +++ b/chain/network/src/concurrency/atomic_cell.rs @@ -0,0 +1,16 @@ +use std::sync::Mutex; + +// AtomicCell narrows down a Mutex API to load/store calls. +pub(crate) struct AtomicCell(Mutex); + +impl AtomicCell { + pub fn new(v: T) -> Self { + Self(Mutex::new(v)) + } + pub fn load(&self) -> T { + self.0.lock().unwrap().clone() + } + pub fn store(&self, v: T) { + *self.0.lock().unwrap() = v; + } +} diff --git a/chain/network/src/concurrency/ctx/mod.rs b/chain/network/src/concurrency/ctx/mod.rs new file mode 100644 index 000000000..632401a8e --- /dev/null +++ b/chain/network/src/concurrency/ctx/mod.rs @@ -0,0 +1,223 @@ +use crate::concurrency::signal; +use std::future::Future; +use std::sync::Arc; + +pub mod time; + +#[cfg(test)] +mod tests; + +thread_local! { + static CTX: std::cell::UnsafeCell = std::cell::UnsafeCell::new(Ctx::new( + unc_async::time::Clock::real() + )); +} + +/// Ensures that the local ctx is rolled back even when stack unwinding happens. +struct SetLocalCtx<'a>(&'a mut Ctx); + +impl<'a> SetLocalCtx<'a> { + fn new(ctx: &'a mut Ctx) -> Self { + CTX.with(|x| std::mem::swap(unsafe { &mut *x.get() }, ctx)); + Self(ctx) + } +} + +impl<'a> Drop for SetLocalCtx<'a> { + fn drop(&mut self) { + CTX.with(|x| std::mem::swap(unsafe { &mut *x.get() }, self.0)); + } +} + +/// Inner representation of a context. +struct Inner { + canceled: signal::Once, + deadline: unc_async::time::Deadline, + clock: unc_async::time::Clock, + /// When Inner gets dropped, the context gets cancelled, so that + /// the tokio task which propagates the cancellation from + /// parent to child it terminated immediately and therefore doesn't + /// leak memory (see `Ctx::with_deadline`). However we don't want + /// the context to get cancelled only because the references to parent + /// are dropped. Therefore we keep a reference to parent here, so + /// that the parent is not dropped until all its children are dropped. + _parent: Option>, +} + +impl Drop for Inner { + fn drop(&mut self) { + // This will wake the task propagating cancellation to children (see `Ctx::with_deadline`). + // Note that since children keep a reference to the parent, at this point nobody awaits + // the cancelation of any child. + self.canceled.send(); + } +} + +/// See `Ctx::wait`. +#[derive(thiserror::Error, Debug)] +#[error("task has been canceled")] +pub struct ErrCanceled; + +/// See `Ctx::wait`. +pub type OrCanceled = Result; + +/// Ctx is an implementation of https://pkg.go.dev/context. +/// Ctxs are a mechanism of broadcasting cancelation to concurrent routines (aka tokio tasks). +/// The routines are expected to react to context cancelation on their own (they are not preempted) +/// and perform a graceful shutdown. A tokio task is expected to return immediately once the context +/// is cancelled (i.e. a task with canceled context, when polled in a loop should complete +/// eventually without requiring any wake()). +/// +/// Ctx is similar to a rust lifetime but for runtime: +/// Ctx is expected to be passed down the call stack and to the spawned tokio subtasks. +/// At any level of the call stack the Ctx can be narrowed down via `Ctx::with_cancel` (which +/// allows the routine to cancel context of subroutines it spawns) or via +/// `Ctx::with_timeout`/`Ctx::with_deadline`, which cancels the scope automatically after a given time. +/// If is NOT possible to extend the context provided by the caller - a subtask is expected +/// to adhere to the lifetime of its context and finish as soon as it gets canceled (or earlier). +#[derive(Clone)] +pub(super) struct Ctx(Arc); + +impl Ctx { + /// Constructs a new context: + /// * without a parent + /// * with real clock + /// * with infinite deadline + /// + /// It should be called directly from main.rs. + pub(crate) fn new(clock: unc_async::time::Clock) -> Ctx { + return Ctx(Arc::new(Inner { + canceled: signal::Once::new(), + deadline: unc_async::time::Deadline::Infinite, + clock, + _parent: None, + })); + } + + pub fn cancel(&self) { + self.0.canceled.send(); + } + + pub fn sub(&self, deadline: unc_async::time::Deadline) -> Ctx { + let child = Ctx(Arc::new(Inner { + canceled: signal::Once::new(), + clock: self.0.clock.clone(), + deadline: std::cmp::min(self.0.deadline, deadline), + _parent: Some(self.0.clone()), + })); + tokio::spawn({ + let clock = self.0.clock.clone(); + let deadline = child.0.deadline; + let parent = self.0.canceled.clone(); + let child = child.0.canceled.clone(); + async move { + tokio::select! { + _ = clock.sleep_until_deadline(deadline) => child.send(), + _ = parent.recv() => child.send(), + _ = child.recv() => {} + } + } + }); + child + } +} + +/// Awaits for the current task to get canceled. +pub async fn canceled() { + local().0.canceled.recv().await +} + +/// Check if the context has been canceled. +pub fn is_canceled() -> bool { + local().0.canceled.try_recv() +} + +/// The time at which the local context will be canceled. +/// The current task should use it to schedule its work accordingly. +/// Remember that this is just a hint, because the local context +/// may get canceled earlier. +pub fn get_deadline() -> unc_async::time::Deadline { + local().0.deadline +} + +pub(super) fn local() -> Ctx { + CTX.with(|ctx| unsafe { &*ctx.get() }.clone()) +} + +impl Ctx { + /// Awaits until f completes, or the context gets canceled. + /// f is required to be cancellable. + async fn wait(&self, f: F) -> OrCanceled { + tokio::select! { + v = f => Ok(v), + _ = self.0.canceled.recv() => Err(ErrCanceled), + } + } +} + +pub async fn wait(f: F) -> OrCanceled { + local().wait(f).await +} + +// TODO(gprusak): run_with_timeout, run_with_deadline, run_canceled, run_test all are expected +// to be awaited at the construction site, so perhaps they should be macros, similarly to +// scope::run!. + +/// Equivalent to `with_deadline(now()+d,f)`. +pub fn run_with_timeout( + d: unc_async::time::Duration, + f: F, +) -> impl Future { + let ctx = local(); + let ctx = ctx.sub((ctx.0.clock.now() + d).into()); + CtxFuture { ctx, inner: f } +} + +/// Runs a future with a context restricted to be canceled at time `t`. +/// It could be emulated via `scope::run!` with a background subtask +/// returning an error after `sleep_until(t)`, but that would be more +/// expensive and other tasks won't see the deadline via `ctx::get_deadline()`. +pub fn run_with_deadline( + t: unc_async::time::Instant, + f: F, +) -> impl Future { + let ctx = local().sub(t.into()); + CtxFuture { ctx, inner: f } +} + +/// Executes the future in a context that has been already canceled. +/// Useful for tests (also outside of this crate). +pub fn run_canceled(f: F) -> impl Future { + let ctx = local().sub(unc_async::time::Deadline::Infinite); + ctx.cancel(); + CtxFuture { ctx, inner: f } +} + +/// Executes the future with a given clock, which can be set to fake clock. +/// Useful for tests. +pub fn run_test( + clock: unc_async::time::Clock, + f: F, +) -> impl Future { + CtxFuture { ctx: Ctx::new(clock), inner: f } +} + +#[pin_project::pin_project] +pub(super) struct CtxFuture { + #[pin] + pub(super) inner: F, + pub(super) ctx: Ctx, +} + +impl Future for CtxFuture { + type Output = F::Output; + + fn poll( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + let this = self.project(); + let _guard = SetLocalCtx::new(this.ctx); + this.inner.poll(cx) + } +} diff --git a/chain/network/src/concurrency/ctx/tests.rs b/chain/network/src/concurrency/ctx/tests.rs new file mode 100644 index 000000000..7c20c4ca1 --- /dev/null +++ b/chain/network/src/concurrency/ctx/tests.rs @@ -0,0 +1,75 @@ +use crate::concurrency::ctx; +use crate::concurrency::scope; +use crate::testonly::abort_on_panic; +use unc_async::time; + +#[tokio::test] +async fn test_run_canceled() { + abort_on_panic(); + ctx::run_canceled(async { + assert!(ctx::is_canceled()); + }) + .await; +} + +#[tokio::test] +async fn test_run_test() { + abort_on_panic(); + let clock = time::FakeClock::default(); + ctx::run_test(clock.clock(), async { + assert_eq!(ctx::time::now(), clock.now()); + assert_eq!(ctx::time::now_utc(), clock.now_utc()); + clock.advance(time::Duration::seconds(10)); + assert_eq!(ctx::time::now(), clock.now()); + }) + .await; +} + +type R = Result<(), E>; + +#[tokio::test] +async fn test_run_with_timeout() { + abort_on_panic(); + let sec = unc_async::time::Duration::SECOND; + let clock = time::FakeClock::default(); + let res = ctx::run_test(clock.clock(), async { + scope::run!(|s| async { + s.spawn(ctx::run_with_timeout(1000 * sec, async { + ctx::canceled().await; + R::Err(9) + })); + clock.advance(1001 * sec); + Ok(()) + }) + }) + .await; + assert_eq!(Err(9), res); +} + +#[tokio::test] +async fn test_sleep_until() { + abort_on_panic(); + let sec = unc_async::time::Duration::SECOND; + let clock = time::FakeClock::default(); + ctx::run_test(clock.clock(), async { + let _ = scope::run!(|s| async { + let t = ctx::time::now() + 1000 * sec; + s.spawn(async move { + ctx::time::sleep_until(t).await.unwrap(); + R::Err(1) + }); + clock.advance_until(t + sec); + Ok(()) + }); + let _ = scope::run!(|s| async { + let t = ctx::time::now() + 1000 * sec; + s.spawn(async move { + assert!(ctx::time::sleep_until(t).await.is_err()); + Ok(()) + }); + clock.advance_until(t - sec); + R::Err(1) + }); + }) + .await +} diff --git a/chain/network/src/concurrency/ctx/time.rs b/chain/network/src/concurrency/ctx/time.rs new file mode 100644 index 000000000..0da5a2e48 --- /dev/null +++ b/chain/network/src/concurrency/ctx/time.rs @@ -0,0 +1,20 @@ +use crate::concurrency::ctx; +use unc_async::time; + +pub fn now() -> time::Instant { + ctx::local().0.clock.now() +} + +pub fn now_utc() -> time::Utc { + ctx::local().0.clock.now_utc() +} + +pub async fn sleep(d: time::Duration) -> ctx::OrCanceled<()> { + let ctx = ctx::local(); + ctx.wait(ctx.0.clock.sleep(d)).await +} + +pub async fn sleep_until(t: time::Instant) -> ctx::OrCanceled<()> { + let ctx = ctx::local(); + ctx.wait(ctx.0.clock.sleep_until(t)).await +} diff --git a/chain/network/src/concurrency/demux.rs b/chain/network/src/concurrency/demux.rs new file mode 100644 index 000000000..c31a92f51 --- /dev/null +++ b/chain/network/src/concurrency/demux.rs @@ -0,0 +1,192 @@ +//! A rate-limited demultiplexer. +//! It can be useful for example, if you want to aggregate a bunch of +//! network requests produced by unrelated routines into a single +//! bulk message to rate limit the QPS. +//! +//! Example usage: +//! ` +//! let d = Demux::new(rate::Limit(10.,1)); +//! ... +//! let res = d.call(arg,|inout| async { +//! // Process all inout[i].arg together. +//! // Send the results to inout[i].out. +//! }).await; +//! ` +//! If d.call is called simultaneously multiple times, +//! the arguments `arg` will be collected and just one +//! of the provided handlers will be executed asynchronously +//! (other handlers will be dropped). +//! +use crate::concurrency::rate; +use futures::future::BoxFuture; +use futures::FutureExt; +use unc_async::time; +use std::future::Future; +use tokio::sync::mpsc; +use tokio::sync::oneshot; + +/// Boxed asynchronous function. In rust asynchronous functions +/// are just regular functions which return a Future. +/// This is a convenience alias to express a +pub type BoxAsyncFn = Box BoxFuture<'static, Res>>; + +/// AsyncFn trait represents asynchronous functions which can be boxed. +/// As a simplification (which makes the error messages more readable) +/// we require the argument and result types to be 'static + Send, which is +/// usually required anyway in practice due to Rust limitations. +pub trait AsyncFn { + fn wrap(self) -> BoxAsyncFn; +} + +impl AsyncFn for F +where + F: 'static + Send + FnOnce(Arg) -> Fut, + Fut: 'static + Send + Future, + Arg: 'static + Send, + Res: 'static + Send, +{ + fn wrap(self) -> BoxAsyncFn { + Box::new(move |a: Arg| self(a).boxed()) + } +} + +/// A demux handler should be in practice of type `[Arg; n]` → `[Res; n]` for +/// arbitrary `n`. We approximate that by a function `Vec` → `Vec`. +/// If the sizes do not match, demux will panic. +type Handler = BoxAsyncFn, Vec>; + +struct Call { + arg: Arg, + out: oneshot::Sender, + handler: Handler, +} +type Stream = mpsc::UnboundedSender>; + +/// Rate limited demultiplexer. +/// The current implementation spawns a dedicated future with an infinite loop to +/// aggregate the requests, and every bulk of requests is handled also in a separate spawned +/// future. The drawback of this approach is that every `d.call()` call requires to specify a +/// handler (and with multiple call sites these handlers might be inconsistent). +/// Instead we could technically make the handler an argument of the new() call. Then however +/// we risk that the handler will (indirectly) store the reference to the demux, therefore creating +/// a reference loop. In such case we get a memory leak: the spawned demux-handling future will never be cleaned +/// up, because the channel will never be closed. +/// +/// Alternatives: +/// - use a separate closing signal (a golang-like structured concurrency). +/// - get rid of the dedicated futures whatsoever and make one of the callers do the work: +/// callers may synchronize and select a leader to execute the handler. This will however make +/// the demux implementation way more complicated. +#[derive(Clone)] +pub struct Demux(Stream); + +#[derive(thiserror::Error, Debug, PartialEq)] +#[error("tokio::Runtime running the demux service has been stopped")] +pub struct ServiceStoppedError; + +impl Demux { + pub fn call( + &self, + arg: Arg, + f: impl AsyncFn, Vec>, + ) -> impl std::future::Future> { + let stream = self.0.clone(); + async move { + let (send, recv) = oneshot::channel(); + // ok().unwrap(), because DemuxCall doesn't implement Debug. + stream + .send(Call { arg, out: send, handler: f.wrap() }) + .map_err(|_| ServiceStoppedError)?; + recv.await.map_err(|_| ServiceStoppedError) + } + } + + // Spawns a subroutine performing the demultiplexing. + // Panics if rl is not valid. + pub fn new(rl: rate::Limit) -> Demux { + rl.validate().unwrap(); + let (send, mut recv): (Stream, _) = mpsc::unbounded_channel(); + // TODO(gprusak): this task should be running as long as Demux object exists. + // "Current" runtime can have a totally different lifespan, so we shouldn't spawn on it. + // Find a way to express "runtime lifetime > Demux lifetime". + tokio::spawn(async move { + let mut calls = vec![]; + let mut closed = false; + let mut tokens = rl.burst; + let mut next_token = None; + let interval = (time::Duration::SECOND / rl.qps).try_into().unwrap(); + while !(calls.is_empty() && closed) { + // Restarting the timer every time a new request comes could + // cause a starvation, so we compute the next token arrival time + // just once for each token. + if tokens < rl.burst && next_token.is_none() { + next_token = Some(tokio::time::Instant::now() + interval); + } + + tokio::select! { + // TODO(gprusak): implement sleep future support for FakeClock, + // so that we don't use tokio directly here. + _ = async { + // without async {} wrapper, next_token.unwrap() would be evaluated + // unconditionally. + tokio::time::sleep_until(next_token.unwrap()).await + }, if next_token.is_some() => { + tokens += 1; + next_token = None; + } + call = recv.recv(), if !closed => match call { + Some(call) => calls.push(call), + None => closed = true, + }, + } + if !calls.is_empty() && tokens > 0 { + // First pop all the elements already accumulated on the queue. + // TODO(gprusak): technically calling try_recv() in a loop may cause a starvation, + // in case elements are added to the queue faster than we can take them out, + // so ideally we should rather atomically dump the content of the queue: + // we can achieve that by maintaining an atomic counter with number of elements in + // the queue. + while let Ok(call) = recv.try_recv() { + calls.push(call); + } + + tokens -= 1; + // TODO(gprusak): as of now Demux (as a concurrency primitive) doesn't support + // cancellation. Once we add cancellation support, this task could accept a context sum: + // the sum is valid iff any context is valid. + let calls = std::mem::take(&mut calls); + let mut args = vec![]; + let mut outs = vec![]; + let mut handlers = vec![]; + // TODO(gprusak): due to inlining the call at most 1 call is executed at any + // given time. Ideally we should have a separate limit for the number of + // in-flight calls. It would be dangerous to have it unbounded, especially in a + // case when the concurrent calls would cause a contention. + // TODO(gprusak): add metrics for: + // - demuxed call latency (the one inlined here) + // - outer call latency (i.e. latency of the whole Demux.call, from the PoV of + // the caller). + for call in calls { + args.push(call.arg); + outs.push(call.out); + handlers.push(call.handler); + } + let res = handlers.swap_remove(0)(args).await; + assert_eq!( + res.len(), + outs.len(), + "demux handler returned {} results, expected {}", + res.len(), + outs.len(), + ); + for (res, out) in std::iter::zip(res, outs) { + // If the caller is no longer interested in the result, + // the channel will be closed. Ignore that. + let _ = out.send(res); + } + } + } + }); + Demux(send) + } +} diff --git a/chain/network/src/concurrency/mod.rs b/chain/network/src/concurrency/mod.rs new file mode 100644 index 000000000..404c531ee --- /dev/null +++ b/chain/network/src/concurrency/mod.rs @@ -0,0 +1,13 @@ +pub mod arc_mutex; +mod asyncfn; +pub mod atomic_cell; +pub mod ctx; +pub mod demux; +pub mod rate; +pub mod rayon; +pub mod runtime; +pub mod scope; +mod signal; + +#[cfg(test)] +mod tests; diff --git a/chain/network/src/concurrency/rate.rs b/chain/network/src/concurrency/rate.rs new file mode 100644 index 000000000..565c032a8 --- /dev/null +++ b/chain/network/src/concurrency/rate.rs @@ -0,0 +1,27 @@ +/// Config of a rate limiter algorithm, which behaves like a semaphore +/// - with maximal capacity `burst` +/// - with a new ticket added automatically every 1/qps seconds (qps stands for "queries per +/// second") +/// In case of large load, semaphore will be empty most of the time, +/// letting through requests at frequency `qps`. +/// In case a number of requests come after a period of inactivity, semaphore will immediately +/// let through up to `burst` requests, before going into the previous mode. +#[derive(Copy, Clone)] +pub struct Limit { + pub burst: u64, + pub qps: f64, +} + +impl Limit { + // TODO(gprusak): consider having a constructor for RateLimit which enforces validation + // and getters for fields, so that they cannot be modified after construction. + pub fn validate(&self) -> anyhow::Result<()> { + if self.qps <= 0. { + anyhow::bail!("qps has to be >0"); + } + if self.burst == 0 { + anyhow::bail!("burst has to be >0"); + } + Ok(()) + } +} diff --git a/chain/network/src/concurrency/rayon.rs b/chain/network/src/concurrency/rayon.rs new file mode 100644 index 000000000..f9c8985cd --- /dev/null +++ b/chain/network/src/concurrency/rayon.rs @@ -0,0 +1,177 @@ +use rayon::iter::{Either, ParallelIterator}; +use std::error::Error; +use std::sync::atomic::{AtomicBool, Ordering}; + +/// spawns a closure on a global rayon threadpool and awaits its completion. +/// Returns the closure result. +/// WARNING: panicking within a rayon task seems to be causing a double panic, +/// and hence the panic message is not visible when running "cargo test". +pub async fn run(f: impl 'static + Send + FnOnce() -> T) -> T { + let (send, recv) = tokio::sync::oneshot::channel(); + rayon::spawn(move || { + if send.send(f()).is_err() { + tracing::warn!("rayon::run has been aborted"); + } + }); + recv.await.unwrap() +} + +pub fn run_blocking(f: impl 'static + Send + FnOnce() -> T) -> T { + let (send, recv) = tokio::sync::oneshot::channel(); + rayon::spawn(move || { + if send.send(f()).is_err() { + tracing::warn!("rayon::run has been aborted"); + } + }); + recv.blocking_recv().unwrap() +} + +/// Applies f to the iterated elements and collects the results, until the first None is returned. +/// Returns the results collected so far and a bool (false iff any None was returned). +pub fn try_map( + iter: I, + f: impl Sync + Send + Fn(I::Item) -> Option, +) -> (Vec, bool) { + let ok = AtomicBool::new(true); + let res = iter + .filter_map(|v| { + if !ok.load(Ordering::Acquire) { + return None; + } + let res = f(v); + if res.is_none() { + ok.store(false, Ordering::Release); + } + res + }) + .collect(); + (res, ok.load(Ordering::Acquire)) +} + +/// Applies `func` to the iterated elements and collects the outputs. On the first `Error` the execution is stopped. +/// Returns the outputs collected so far and a [`Result`] ([`Result::Err`] iff any `Error` was returned). +/// Same as [`try_map`], but it operates on [`Result`] instead of [`Option`]. +pub fn try_map_result( + iter: I, + func: impl Sync + Send + Fn(I::Item) -> Result, +) -> (Vec, Result<(), E>) { + // Call the function on every input value and emit some items for every result. + // On a successful call this iterator emits one item: `Some(Ok(output_value))` + // When an error occurs, the iterator emits two items: `Some(Err(the_error))` and `None`` + // The `None` will later be used to tell rayon to stop the execution. + let optional_result_iter /* impl Iterator>> */ = iter + .map(|v| match func(v) { + Ok(val) => Either::Left(std::iter::once(Some(Ok(val)))), + Err(err) => Either::Right([Some(Err(err)), None].into_iter()), + }) + .flatten_iter(); + + // `while_some()` monitors a stream of `Option` values and stops the execution when it spots a `None` value. + // It's used to implement the short-circuit logic - on the first error rayon will stop processing subsequent items. + let results_iter = optional_result_iter.while_some(); + + // Split the results into two groups - the left group contains the outputs resulting from a successful execution, + // while the right group contains errors. Collect them into two separate Vecs. + let (outputs, errors): (Vec, Vec) = results_iter + .map(|res| match res { + Ok(value) => Either::Left(value), + Err(error) => Either::Right(error), + }) + .collect(); + + // Return the output and the first error (if there was any) + match errors.into_iter().next() { + Some(first_error) => (outputs, Err(first_error)), + None => (outputs, Ok(())), + } +} + +#[cfg(test)] +mod tests { + use super::try_map_result; + use rayon::iter::ParallelBridge; + + #[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)] + #[error("error for testing")] + struct TestError; + + /// On empty input try_map_result returns empty output and Ok(()) + #[test] + fn try_map_result_empty_iter() { + let empty_array: [i32; 0] = []; + + let panicking_function = |_input: i32| -> Result { + panic!(); + }; + + let (outputs, result): (Vec, Result<(), TestError>) = + try_map_result(empty_array.into_iter().par_bridge(), panicking_function); + assert_eq!(outputs, Vec::::new()); + assert_eq!(result, Ok(())); + } + + /// Happy path of try_map_result - all items are successfully processed, should return the outputs and Ok(()) + #[test] + fn try_map_result_success() { + let inputs = [1, 2, 3, 4, 5, 6].into_iter(); + let func = |input: i32| -> Result { Ok(input as i64 + 1) }; + + let (mut outputs, result): (Vec, Result<(), TestError>) = + try_map_result(inputs.into_iter().par_bridge(), func); + outputs.sort(); + assert_eq!(outputs, vec![2i64, 3, 4, 5, 6, 7]); + assert_eq!(result, Ok(())); + } + + /// Run `try_map_result` with an infinite stream of tasks, but the 100th task returns an Error. + /// `try_map_result` should stop the execution and return some successful outputs along with the Error. + #[test] + fn try_map_result_stops_on_error() { + // Infinite iterator of inputs: 1, 2, 3, 4, 5, ... + let infinite_iter = (1..).into_iter(); + + let func = |input: i32| -> Result { + if input == 100 { + return Err(TestError); + } + + Ok(2 * input as i64) + }; + + let (mut outputs, result): (Vec, Result<(), TestError>) = + try_map_result(infinite_iter.par_bridge(), func); + outputs.sort(); + + // The error will happen on 100th input, but other threads might produce subsequent outputs in parallel, + // so there might be over 100 outputs. We can't really assume antything about the outputs due to the + // nature of multithreading, but we can check that some basic conditions hold. + + // All outputs should be distinct + for two_outputs in outputs.windows(2) { + assert_ne!(two_outputs[0], two_outputs[1]); + + // All outputs should be even, func multiplies the inputs by 2. + assert_eq!(two_outputs[0] % 2, 0); + assert_eq!(two_outputs[1] % 2, 0); + } + + assert_eq!(result, Err(TestError)); + } + + /// When using try_map_result, a panic in the function will be propagated to the caller + #[test] + #[should_panic] + fn try_map_result_panic() { + let inputs = (1..1000).into_iter(); + + let panicking_function = |input: i32| -> Result { + if input == 100 { + panic!("Oh no the input is equal to 100"); + } + Ok(2 * input as i64) + }; + + let (_outputs, _result) = try_map_result(inputs.par_bridge(), panicking_function); + // Should panic + } +} diff --git a/chain/network/src/concurrency/runtime.rs b/chain/network/src/concurrency/runtime.rs new file mode 100644 index 000000000..538b5ae8f --- /dev/null +++ b/chain/network/src/concurrency/runtime.rs @@ -0,0 +1,34 @@ +use std::sync::Arc; + +/// Single-threaded runtime which cancels all the tasks as soon as it is dropped. +/// A potential in-place replacement for actix::Actor. +pub(crate) struct Runtime { + pub handle: tokio::runtime::Handle, + stop: Arc, + thread: Option>, +} + +impl Runtime { + pub fn new() -> Self { + let stop = Arc::new(tokio::sync::Notify::new()); + let runtime = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap(); + let handle = runtime.handle().clone(); + let thread = std::thread::spawn({ + let stop = stop.clone(); + move || runtime.block_on(stop.notified()) + }); + Self { handle, stop, thread: Some(thread) } + } +} + +impl Drop for Runtime { + fn drop(&mut self) { + self.stop.notify_one(); + let thread = self.thread.take().unwrap(); + // Await for the thread to stop, unless it is the current thread + // (i.e. nobody waits for it). + if std::thread::current().id() != thread.thread().id() { + thread.join().unwrap(); + } + } +} diff --git a/chain/network/src/concurrency/scope/mod.rs b/chain/network/src/concurrency/scope/mod.rs new file mode 100644 index 000000000..cdec88ace --- /dev/null +++ b/chain/network/src/concurrency/scope/mod.rs @@ -0,0 +1,459 @@ +#![cfg(not(doctest))] +//! Asynchronous scope. +//! +//! You can think of the scope as a lifetime 'env within a rust future, such that: +//! * within 'env you can spawn subtasks, which may return an error E. +//! * subtasks can spawn more subtasks. +//! * 'env has special semantics: at the end of 'env all spawned substasks are awaited for +//! completion. +//! * if ANY of the subtasks returns an error, all the other subtasks are GRACEFULLY cancelled. +//! It means that they are not just dropped, but rather they have a handle (aka Ctx) to be able +//! to check at any time whether they were cancelled. +//! +//! let (send,recv) = channel(); +//! ... +//! 'env: { +//! spawn<'env>(async { +//! recv.await +//! Err(e) +//! }); +//! +//! while !is_cancelled() { +//! // do some useful async unit of work +//! } +//! // do some graceful cleanup. +//! Ok(()) +//! } +//! +//! Since we cannot directly address lifetimes like that we simulate it via Scope and Ctx structs. +//! Ctx is hidden in the thread_local storage and is configured by Scope. +//! We cannot directly implement a function +//! run : (Scope<'env> -> (impl 'env + Future)) -> (impl 'env + Future) +//! Because the compiler is not smart enough to deduce 'env for us. +//! Instead we first construct Scope<'env> explicitly, therefore fixing its lifetime, +//! and only then we pass a reference to it to another function. +//! +//! let (send,recv) = channel(); +//! ... +//! { +//! let s = Scope<'env>::new(); +//! s.run(|s| async { +//! s.spawn(async { +//! recv.await +//! Err(e) +//! }) +//! +//! for !ctx::is_cancelled() { +//! // do some useful async unit of work +//! } +//! // do some graceful cleanup. +//! Ok(()) +//! }).await +//! } +//! +//! We wrap these 2 steps into a macro "run!" to hide this hack and avoid incorrect use. +use crate::concurrency::ctx; +use crate::concurrency::signal; +use futures::future::{BoxFuture, Future, FutureExt}; +use unc_async::time; +use std::borrow::Borrow; +use std::sync::{Arc, Weak}; + +#[cfg(test)] +mod tests; + +// TODO(gprusak): Consider making the context implicit by moving it to a thread_local storage. + +struct Output { + ctx: ctx::Ctx, + send: crossbeam_channel::Sender, +} + +impl Clone for Output { + fn clone(&self) -> Self { + Self { ctx: self.ctx.clone(), send: self.send.clone() } + } +} + +impl Output { + pub fn new(ctx: ctx::Ctx) -> (Self, crossbeam_channel::Receiver) { + let (send, recv) = crossbeam_channel::bounded(1); + (Self { ctx, send }, recv) + } + + pub fn send(&self, err: E) { + if let Ok(_) = self.send.try_send(err) { + self.ctx.cancel(); + } + } +} + +/// Internal representation of a scope. +struct Inner { + /// Signal sent once the scope is terminated (i.e. when Inner is dropped). + /// + /// Since all tasks keep a reference to the scope they belong to, all the tasks + /// of the scope are complete when terminated is sent. + terminated: signal::Once, + /// Context of this scope. + /// + /// It is a child context of the parent scope. + /// All tasks spawned in this scope are provided with this context. + ctx: ctx::Ctx, + /// The channel over which the first error reported by any completed task is sent. + /// + /// `output` channel has capacity 1, so that any subsequent attempts to send an error + /// will fail. The channel is created in `internal::run` and the value is received from + /// the channel only after the top-level scope has terminated. + /// Thanks to the fact that we have a single channel per top-level scope: + /// * if you await Service::terminate() from within some task, it will complete only + /// after the error is actually registered, so the awaiting task won't be able to cause a + /// race condition. + output: Output, + /// Parent scope. We keep it to prevent termination of the parent, until this scope terminates. + _parent: Option>, +} + +impl Drop for Inner { + fn drop(&mut self) { + self.terminated.send(); + } +} + +impl Inner { + /// Creates a new scope with the given context. + pub fn new(ctx: &ctx::Ctx) -> (Arc, crossbeam_channel::Receiver) { + let ctx = ctx.sub(time::Deadline::Infinite); + let (output, recv) = Output::new(ctx.clone()); + (Arc::new(Self { ctx, output, _parent: None, terminated: signal::Once::new() }), recv) + } + + pub fn new_subscope(self: Arc) -> Arc> { + Arc::new(Inner { + ctx: self.ctx.sub(time::Deadline::Infinite), + output: self.output.clone(), + _parent: Some(self), + terminated: signal::Once::new(), + }) + } +} + +/// Represents a task that can be joined by another task within Scope<'env>. +/// We do not support awaiting for tasks outside of the scope, to simplify +/// the concurrency model (you can still implement a workaround by using a channel, +/// if you really want to, but that might mean that Scope is not what you want +/// in the first place). +/// In particular Service::spawn() doesn't return a JoinHandle, +/// because it can be called outside of the scope that it belongs to. +pub struct JoinHandle<'env, T>( + tokio::task::JoinHandle>, + std::marker::PhantomData &'env ()>, +); + +#[derive(thiserror::Error, Debug)] +#[error("awaited task has been canceled")] +pub struct ErrTaskCanceled; + +impl<'env, T> JoinHandle<'env, T> { + /// Awaits for a task to be completed and returns the result. + /// Returns Err(ErrCanceled) if the awaiting (current task) has been canceled. + /// Returns Ok(Err(ErrTaskCanceled) if the awaited (joined task) has been canceled. + pub async fn join(self) -> ctx::OrCanceled> { + ctx::wait(async { self.0.await.unwrap() }).await + } +} + +impl Inner { + /// Spawns a task in the scope, which owns a reference of to the scope, so that scope doesn't + /// terminate before all tasks are completed. + /// + /// The reference to the scope can be an arbitrary + /// type, so that a custom drop() behavior can be added. For example, see `StrongService` scope reference, + /// which cancels the scope when dropped. + fn spawn, T: 'static + Send>( + m: Arc, + f: impl 'static + Send + Future>, + ) -> tokio::task::JoinHandle> { + tokio::spawn(must_complete(async move { + match (ctx::CtxFuture { ctx: m.as_ref().borrow().ctx.clone(), inner: f }).await { + Ok(v) => Ok(v), + Err(err) => { + m.as_ref().borrow().output.send(err); + Err(ErrTaskCanceled) + } + } + })) + } + + /// Spawns a new service in the scope. + /// + /// A service is a scope, which gets canceled when + /// its handler (`Service`) is dropped. Service belongs to a scope, in a sense that + /// a dedicated task is spawned on the scope which awaits for service to terminate and + /// returns the service's result. + pub fn new_service(self: Arc) -> Service { + let subscope = self.new_subscope(); + let service = Service(Arc::downgrade(&subscope)); + // Spawn a guard task in the Service which will prevent termination of the Service + // until the context is not canceled. See `Service` for a list + // of events canceling the Service. + Inner::spawn(subscope, async move { Ok(ctx::canceled().await) }); + service + } +} + +/// Error returned when the `Service` has been already terminated +/// and therefore spawning a task/service in it is not possible. +#[derive(thiserror::Error, Debug)] +#[error("ErrTerminated")] +pub struct ErrTerminated; + +/// A service is a subscope which doesn't keep the scope +/// alive, i.e. if all tasks spawned via `Scope::spawn` complete, the scope will +/// be cancelled (even though tasks in a service may be still running). +/// +/// Note however that the scope won't be terminated until the tasks of the service complete. +/// Service is cancelled when the handle is dropped, so make sure to store it somewhere. +/// Service is cancelled when ANY of the tasks/services in the service returns an error. +/// Service is cancelled when the parent scope/service is cancelled. +/// Service is NOT cancelled just when all tasks within the service complete - in particular +/// a newly started service has no tasks. +/// Service is terminated when it is cancelled AND all tasks within the service complete. +pub struct Service(Weak>); + +impl Drop for Service { + fn drop(&mut self) { + self.0.upgrade().map(|inner| inner.ctx.cancel()); + } +} + +impl Service { + /// Checks if the referred scope has been terminated. + pub fn is_terminated(&self) -> bool { + self.0.upgrade().is_none() + } + + /// Waits until the scope is terminated. + /// + /// Returns `ErrCanceled` iff `ctx` was canceled before that. + pub fn terminated<'a>( + &'a self, + ) -> impl Future> + Send + Sync + 'a { + let terminated = self.0.upgrade().map(|inner| inner.terminated.clone()); + async move { + if let Some(t) = terminated { + ctx::wait(t.recv()).await + } else { + Ok(()) + } + } + } + + /// Cancels the scope's context and waits until the scope is terminated. + /// + /// Note that ErrCanceled is returned if the `ctx` passed as argument is canceled before that, + /// not when scope's context is cancelled. + pub fn terminate<'a>(&'a self) -> impl Future> + Send + Sync + 'a { + let terminated = self.0.upgrade().map(|inner| { + inner.ctx.cancel(); + inner.terminated.clone() + }); + async move { + if let Some(t) = terminated { + ctx::wait(t.recv()).await + } else { + Ok(()) + } + } + } + + /// Spawns a task in this scope. + /// + /// Returns ErrTerminated if the scope has already terminated. + pub fn spawn( + &self, + f: impl 'static + Send + Future>, + ) -> Result<(), ErrTerminated> { + self.0 + .upgrade() + .map(|m| { + Inner::spawn(m, f); + }) + .ok_or(ErrTerminated) + } + + /// Spawns a service in this scope. + /// + /// Returns ErrTerminated if the scope has already terminated. + pub fn new_service(&self) -> Result, ErrTerminated> { + self.0.upgrade().map(|m| Inner::new_service(m)).ok_or(ErrTerminated) + } +} + +/// Wrapper of a scope reference which cancels the scope when dropped. +/// +/// Used by Scope to cancel the scope as soon as all tasks spawned via +/// `Scope::spawn` complete. +struct StrongService(Arc>); + +impl Borrow> for StrongService { + fn borrow(&self) -> &Inner { + &*self.0 + } +} + +impl Drop for StrongService { + fn drop(&mut self) { + self.0.ctx.cancel() + } +} + +/// Scope represents a concurrent computation bounded by lifetime 'env. +/// +/// It should be created only via `run!` macro. +/// Scope is cancelled when the provided context is cancelled. +/// Scope is cancelled when any of the tasks in the scope returns an error. +/// Scope is cancelled when all the tasks in the scope complete. +/// Scope is terminated when it is cancelled AND all tasks in the scope complete. +pub struct Scope<'env, E: 'static>( + /// Scope is equivalent to a strong service, but bounds + Weak>, + Weak>, + /// Makes Scope<'env,E> invariant in 'env. + std::marker::PhantomData &'env ()>, +); + +unsafe fn to_static<'env, T>(f: BoxFuture<'env, T>) -> BoxFuture<'static, T> { + std::mem::transmute::, BoxFuture<'static, _>>(f) +} + +impl<'env, E: 'static + Send> Scope<'env, E> { + /// Spawns a "main" task in the scope. + /// Scope gets canceled as soon as all the "main" tasks complete. + pub fn spawn( + &self, + f: impl 'env + Send + Future>, + ) -> JoinHandle<'env, T> { + match self.0.upgrade() { + Some(strong) => JoinHandle( + Inner::spawn(strong, unsafe { to_static(f.boxed()) }), + std::marker::PhantomData, + ), + // Upgrade may fail only if all the "main" tasks have already completed + // so the caller is a "background" task. In that case we fall back + // to spawning a "background" task instead. It is ok, since the distinction + // between main task and background task disappears, once the scope is canceled. + None => self.spawn_bg(f), + } + } + + /// Spawns a "background" task in the scope. + /// It behaves just like a single-task Service, but + /// has the same lifetime as the Scope, so it can spawn + /// more tasks in the scope. It is not a "main" task, so + /// it doesn't prevent scope cancelation. + pub fn spawn_bg( + &self, + f: impl 'env + Send + Future>, + ) -> JoinHandle<'env, T> { + JoinHandle( + Inner::spawn(self.1.upgrade().unwrap(), unsafe { to_static(f.boxed()) }), + std::marker::PhantomData, + ) + } + + /// Spawns a service. + /// + /// Returns a handle to the service, which allows spawning new tasks within the service. + pub fn new_service(&self) -> Service { + Inner::new_service(self.0.upgrade().unwrap().0.clone()) + } +} + +/// must_complete wraps a future, so that it aborts if it is dropped before completion. +/// +/// Possibility that a future can be dropped/aborted at every await makes the control flow unnecessarily complicated. +/// In fact, only few basic futures (like io primitives) actually need to be abortable, so +/// that they can be put together into a tokio::select block. All the higher level logic +/// would greatly benefit (in terms of readability and bug-resistance) from being non-abortable. +/// Rust doesn't support linear types as of now, so best we can do is a runtime check. +fn must_complete(fut: Fut) -> impl Future { + let guard = MustCompleteGuard; + async move { + let res = fut.await; + let _ = std::mem::ManuallyDrop::new(guard); + res + } +} + +struct MustCompleteGuard; + +impl Drop for MustCompleteGuard { + fn drop(&mut self) { + // We always abort here, no matter if compiled with panic=abort or panic=unwind. + eprintln!("dropped a non-abortable future before completion"); + eprintln!("backtrace:\n{}", std::backtrace::Backtrace::force_capture()); + std::process::abort(); + } +} + +/// Should be used only via run! macro. +#[doc(hidden)] +pub mod internal { + use super::*; + + pub fn new_scope<'env, E: 'static>() -> Scope<'env, E> { + Scope(Weak::new(), Weak::new(), std::marker::PhantomData) + } + + pub async fn run<'env, E, T, F, Fut>(scope: &'env mut Scope<'env, E>, f: F) -> Result + where + E: 'static + Send, + T: 'static + Send, + F: 'env + FnOnce(&'env Scope<'env, E>) -> Fut, + Fut: 'env + Send + Future>, + { + must_complete(async move { + let (inner, err) = Inner::new(&ctx::local()); + let service = Arc::new(StrongService(inner)); + let terminated = service.0.terminated.clone(); + scope.0 = Arc::downgrade(&service); + scope.1 = Arc::downgrade(&service.0); + let task = scope.spawn(f(scope)); + // each task spawned on `scope` keeps its own reference to `service`. + // As soon as all references to `service` are dropped, scope will be cancelled. + drop(service); + terminated.recv().await; + if let Ok(err) = err.try_recv() { + return Err(err); + } + // All tasks terminated, no errors have been returned. + // In particular `f` has returned an Ok(_), which we + // extract here. + Ok(task.0.await.unwrap().unwrap()) + }) + .await + } +} + +/// A future running a task within a scope (see `Scope`). +/// +/// `await` is called within the macro instantiation, so `run!` can be called only in an async context. +/// Dropping this future while incomplete will ABORT (not panic, since the future is not +/// UnwindSafe). +/// Note that immediate-await doesn't prevent dropping the future, as the outer future still can be dropped. +#[macro_export] +macro_rules! run { + ($f:expr) => {{ + $crate::concurrency::scope::internal::run( + // We pass a created scope via argument (rather than construct it within `run()` + // So that rust compiler fixes the lifespan of the Scope, rather than trying to + // reason about it - which is not smart enough to do. + &mut $crate::concurrency::scope::internal::new_scope(), + $f, + ) + .await + }}; +} + +pub use run; diff --git a/chain/network/src/concurrency/scope/tests.rs b/chain/network/src/concurrency/scope/tests.rs new file mode 100644 index 000000000..ea0325358 --- /dev/null +++ b/chain/network/src/concurrency/scope/tests.rs @@ -0,0 +1,312 @@ +use crate::concurrency::ctx; +use crate::concurrency::scope; +use crate::testonly::abort_on_panic; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::Arc; + +// run a trivial future until completion => OK +#[tokio::test] +async fn must_complete_ok() { + assert_eq!(5, scope::must_complete(async move { 5 }).await); +} + +type R = Result<(), E>; + +#[tokio::test] +async fn test_drop_service() { + abort_on_panic(); + let res = scope::run!(|s| async { + let service = s.new_service(); + service + .spawn(async { + ctx::canceled().await; + R::Err(2) + }) + .unwrap(); + // Both scope task and service task await context cancellation. + // However, scope task drops the only reference to service before + // awaiting, which should cause service to cancel. + // The canceled service task returns an error which should cancel + // the whole scope. + drop(service); + ctx::canceled().await; + Ok(()) + }); + assert_eq!(Err(2), res); +} + +#[tokio::test] +async fn test_spawn_after_cancelling_scope() { + abort_on_panic(); + let res = scope::run!(|s| async { + s.spawn(async { R::Err(7) }); + ctx::canceled().await; + s.spawn(async { R::Err(3) }); + Ok(()) + }); + assert_eq!(R::Err(7), res); +} + +#[tokio::test] +async fn test_spawn_after_dropping_service() { + abort_on_panic(); + let res = scope::run!(|s| async { + let service = Arc::new(s.new_service()); + service + .spawn({ + let service = service.clone(); + async move { + ctx::canceled().await; + // Even though the service has been cancelled, you can spawn more tasks on it + // until it is actually terminated. So it is always OK to spawn new tasks on + // a service from another task running on this service. + service.spawn(async { R::Err(5) }).unwrap(); + Ok(()) + } + }) + .unwrap(); + // terminate() may get cancelled, because we expect the service to return an error. + // Error may or may not get propagated before terminate completes. + let _ = service.terminate().await; + Ok(()) + }); + assert_eq!(Err(5), res); +} + +#[tokio::test] +async fn test_service_termination() { + abort_on_panic(); + let res = scope::run!(|s| async { + let service = s.new_service(); + service.spawn(async { Ok(ctx::canceled().await) }).unwrap(); + service.spawn(async { Ok(ctx::canceled().await) }).unwrap(); + service.terminate().await.unwrap(); + // Spawning after service termination should fail. + assert!(service.spawn(async { R::Err(1) }).is_err()); + Ok(()) + }); + assert_eq!(Ok(()), res); +} + +#[tokio::test] +async fn test_nested_service() { + abort_on_panic(); + let res = scope::run!(|s| async { + let outer = Arc::new(s.new_service()); + outer + .spawn({ + let outer = outer.clone(); + async move { + let inner = outer.new_service().unwrap(); + inner + .spawn(async { + ctx::canceled().await; + R::Err(9) + }) + .unwrap(); + ctx::canceled().await; + Ok(()) + } + }) + .unwrap(); + // Scope (and all the transitive subservices) get cancelled once this task completes. + // Scope won't be terminated until all the transitive subservices terminate. + Ok(()) + }); + assert_eq!(Err(9), res); +} + +#[tokio::test] +async fn test_nested_scopes() { + abort_on_panic(); + let res = scope::run!(|s| async { + s.spawn(async { + scope::run!(|s| async move { + s.spawn(async { scope::run!(|_| async { R::Err(8) }) }); + Ok(()) + }) + }); + Ok(()) + }); + assert_eq!(Err(8), res); +} + +#[tokio::test] +async fn test_already_canceled() { + abort_on_panic(); + let res = ctx::run_canceled(async { + // scope::run! should start a task, + // even though the task has been already canceled. + scope::run!(|s| async { + s.spawn(async { + ctx::canceled().await; + R::Err(4) + }); + Ok(()) + }) + }) + .await; + assert_eq!(Err(4), res); +} + +#[tokio::test] +async fn test_service_cancel() { + abort_on_panic(); + scope::run!(|s| async { + let service = Arc::new(s.new_service()); + service + .spawn({ + let service = service.clone(); + async move { + let _service = service; + ctx::canceled().await; + Ok(()) + } + }) + .unwrap(); + Result::<(), ()>::Ok(()) + }) + .unwrap(); +} + +#[tokio::test] +async fn test_service_error_before_cancel() { + abort_on_panic(); + let res = scope::run!(|s| async { + let service = Arc::new(s.new_service()); + + service + .spawn({ + let service = service.clone(); + async move { + let _service = service; + R::Err(1) + } + }) + .unwrap(); + ctx::canceled().await; + Ok(()) + }); + assert_eq!(Err(1), res); +} + +#[tokio::test] +async fn test_service_error_after_cancel() { + abort_on_panic(); + let res = scope::run!(|s| async { + let service = Arc::new(s.new_service()); + + service + .spawn({ + let service = service.clone(); + async move { + let _service = service; + ctx::canceled().await; + R::Err(2) + } + }) + .unwrap(); + Ok(()) + }); + assert_eq!(Err(2), res); +} + +#[tokio::test] +async fn test_scope_error() { + abort_on_panic(); + let res = scope::run!(|s| async { + let service = Arc::new(s.new_service()); + + service + .spawn({ + let service = service.clone(); + async move { + let _service = service; + ctx::canceled().await; + Ok(()) + } + }) + .unwrap(); + R::Err(2) + }); + assert_eq!(Err(2), res); +} + +#[tokio::test] +async fn test_scope_error_nonoverridable() { + abort_on_panic(); + let res = scope::run!(|s| async { + let service = Arc::new(s.new_service()); + service + .spawn({ + let service = service.clone(); + async { + let _service = service; + ctx::canceled().await; + R::Err(3) + } + }) + .unwrap(); + R::Err(2) + }); + assert_eq!(Err(2), res); +} + +// After all main tasks complete succesfully, the scope gets canceled. +// Background tasks of the scope should still be able to spawn more tasks +// both via `Scope::spawn()` and `Scope::spawn_bg` (although after scope +// cancelation they behave exactly the same). +#[tokio::test] +async fn test_spawn_from_spawn_bg() { + abort_on_panic(); + let res = scope::run!(|s| async { + s.spawn_bg(async { + ctx::canceled().await; + s.spawn(async { + assert!(ctx::is_canceled()); + R::Err(3) + }); + Ok(()) + }); + Ok(()) + }); + assert_eq!(Err(3), res); +} + +#[tokio::test] +async fn test_access_to_vars_outside_of_scope() { + abort_on_panic(); + // Lifetime of a is larger than scope's lifetime, + // so it should be accessible from scope's tasks. + let a = AtomicU64::new(0); + let a = &a; + scope::run!(|s| async { + s.spawn(async move { + scope::run!(|s| async { + s.spawn(async { + a.fetch_add(1, Ordering::Relaxed); + R::<()>::Ok(()) + }); + Ok(()) + }) + }); + + s.spawn(async { + s.spawn(async { + a.fetch_add(1, Ordering::Relaxed); + Ok(()) + }); + a.fetch_add(1, Ordering::Relaxed); + Ok(()) + }); + a.fetch_add(1, Ordering::Relaxed); + s.spawn(async { + a.fetch_add(1, Ordering::Relaxed); + Ok(()) + }); + a.fetch_add(1, Ordering::Relaxed); + Ok(()) + }) + .unwrap(); + assert_eq!(6, a.load(Ordering::Relaxed)); +} diff --git a/chain/network/src/concurrency/signal.rs b/chain/network/src/concurrency/signal.rs new file mode 100644 index 000000000..f00dc499f --- /dev/null +++ b/chain/network/src/concurrency/signal.rs @@ -0,0 +1,34 @@ +use std::sync::Arc; + +#[derive(Clone)] +pub(super) struct Once(Arc); + +impl Once { + pub fn new() -> Self { + Self(Arc::new(tokio::sync::Semaphore::new(0))) + } + + /// Sends the signal, waking all tasks awaiting for recv(). + /// + /// After this call recv().await will always return immediately. + /// After this call any subsequent call to send() is a noop. + pub fn send(&self) { + self.0.close(); + } + + /// recv() waits for the first call to send(). + /// + /// Cancellable. + pub async fn recv(&self) { + // We await for the underlying semaphore to get closed. + // This is the only possible outcome, because we never add + // any permits to the semaphore. + let res = self.0.acquire().await; + debug_assert!(res.is_err()); + } + + /// Checks if send() was already called. + pub fn try_recv(&self) -> bool { + self.0.is_closed() + } +} diff --git a/chain/network/src/concurrency/tests.rs b/chain/network/src/concurrency/tests.rs new file mode 100644 index 000000000..bf615661c --- /dev/null +++ b/chain/network/src/concurrency/tests.rs @@ -0,0 +1,99 @@ +use crate::concurrency::arc_mutex::ArcMutex; +use crate::concurrency::demux; +use crate::concurrency::rate; + +#[tokio::test] +async fn test_demux() { + let demux = demux::Demux::new(rate::Limit { qps: 50., burst: 1 }); + for _ in 0..5 { + let mut handles = vec![]; + for i in 0..1000 { + let demux = demux.clone(); + handles.push(tokio::spawn(async move { + let j = demux + .call(i, |is: Vec| async { is.into_iter().map(|i| i + 1).collect() }) + .await + .unwrap(); + assert_eq!(i + 1, j); + })); + } + for h in handles { + h.await.unwrap(); + } + } +} + +#[test] +fn demux_runtime_dropped_before_call() { + let r1 = tokio::runtime::Runtime::new().unwrap(); + let r2 = tokio::runtime::Runtime::new().unwrap(); + let demux = r1.block_on(async { demux::Demux::new(rate::Limit { qps: 1., burst: 1000 }) }); + drop(r1); + let call = demux.call(0, |is: Vec| async { is }); + assert_eq!(Err(demux::ServiceStoppedError), r2.block_on(call)); +} + +#[test] +fn demux_runtime_dropped_during_call() { + let r1 = tokio::runtime::Runtime::new().unwrap(); + let r2 = tokio::runtime::Runtime::new().unwrap(); + let demux = r1.block_on(async { demux::Demux::new(rate::Limit { qps: 1., burst: 1000 }) }); + + // Start the call and pause. + let (send, recv) = tokio::sync::oneshot::channel(); + let call = r2.spawn(demux.call(0, move |_: Vec| async { + send.send(()).unwrap(); + std::future::pending::>().await + })); + r2.block_on(recv).unwrap(); + + // Drop the demux runtime. + drop(r1); + assert_eq!(Err(demux::ServiceStoppedError), r2.block_on(call).unwrap()); +} + +#[test] +fn arc_mutex_update() { + let v = 5; + let v2 = 10; + let v3 = 15; + let m = ArcMutex::new(v); + // Loaded value should be the same as constructor argument. + assert_eq!(v, *m.load()); + + // The extra result should be passed forward. + assert_eq!( + 19, + m.update(|x| { + // Initial content of x should be the same as before update. + assert_eq!(v, x); + // Concurrent load() should be possible and should return the value from before the update. + assert_eq!(v, *m.load()); + (19, v2) + }) + ); + // After update, load() should return the new value. + assert_eq!(v2, *m.load()); + + // try_update returning an Error. + assert_eq!( + Err::<(), i32>(35), + m.try_update(|x| { + assert_eq!(v2, x); + assert_eq!(v2, *m.load()); + Err(35) + }) + ); + assert_eq!(v2, *m.load()); + + // try_update returning Ok. + assert_eq!( + Ok::(21), + m.try_update(|x| { + assert_eq!(v2, x); + assert_eq!(v2, *m.load()); + Ok((21, v3)) + }) + ); + assert_eq!(v3, *m.load()); +} diff --git a/chain/network/src/config.rs b/chain/network/src/config.rs new file mode 100644 index 000000000..7fbb06858 --- /dev/null +++ b/chain/network/src/config.rs @@ -0,0 +1,635 @@ +use crate::blacklist; +use crate::concurrency::rate; +use crate::network_protocol::PeerAddr; +use crate::network_protocol::PeerInfo; +use crate::peer_manager::peer_manager_actor::Event; +use crate::peer_manager::peer_store; +use crate::sink::Sink; +use crate::snapshot_hosts; +use crate::stun; +use crate::tcp; +use crate::types::ROUTED_MESSAGE_TTL; +use anyhow::Context; +use unc_async::time; +use unc_crypto::{KeyType, SecretKey}; +use unc_primitives::network::PeerId; +use unc_primitives::test_utils::create_test_signer; +use unc_primitives::types::AccountId; +use unc_primitives::validator_signer::ValidatorSigner; +use std::collections::HashSet; +use std::sync::Arc; + +/// How much height horizon to give to consider peer up to date. +pub const HIGHEST_PEER_HORIZON: u64 = 5; + +/// Maximum amount of routes to store for each account id. +pub const MAX_ROUTES_TO_STORE: usize = 5; + +/// Maximum number of PeerAddrs in the ValidatorConfig::endpoints field. +pub const MAX_PEER_ADDRS: usize = 10; + +/// Maximum number of peers to include in a PeersResponse message. +pub const PEERS_RESPONSE_MAX_PEERS: u32 = 512; + +/// ValidatorProxies are nodes with public IP (aka proxies) that this validator trusts to be honest +/// and willing to forward traffic to this validator. Whenever this node is a TIER1 validator +/// (i.e. whenever it is a block producer/chunk producer/approver for the given epoch), +/// it will connect to all the proxies in this config and advertise a signed list of proxies that +/// it has established a connection to. +/// +/// Once other TIER1 nodes learn the list of proxies, they will maintain a connection to a random +/// proxy on this list. This way a message from any TIER1 node to this node will require at most 2 +/// hops. +/// +/// uncd supports 2 modes for configuring proxy addresses: +/// * [recommended] `Static` list of proxies (public SocketAddr + PeerId), supports up to 10 proxies. +/// It is a totally valid setup for a TIER1 validator to be its own (perahaps only) proxy: +/// to achieve that, add an entry with the public address of this node to the Static list. +/// * [discouraged] `Dynamic` proxy - in case you want this validator to be its own and only proxy, +/// instead of adding the public address explicitly to the `Static` list, you can specify a STUN +/// server address (or a couple of them) which will be used to dynamically resolve the public IP +/// of this validator. Note that in this case the validator trusts the STUN servers to correctly +/// resolve the public IP. +#[derive(Clone)] +pub enum ValidatorProxies { + Static(Vec), + Dynamic(Vec), +} + +#[derive(Clone)] +pub struct ValidatorConfig { + pub signer: Arc, + pub proxies: ValidatorProxies, +} + +impl ValidatorConfig { + pub fn account_id(&self) -> AccountId { + self.signer.validator_id().clone() + } +} + +#[derive(Clone)] +pub struct Tier1 { + /// Interval between attempts to connect to proxies of other TIER1 nodes. + pub connect_interval: time::Duration, + /// Maximal number of new connections established every connect_interval. + /// TIER1 can consists of hundreds of nodes, so it is not feasible to connect to all of them at + /// once. + pub new_connections_per_attempt: u64, + /// Interval between broacasts of the list of validator's proxies. + /// Before the broadcast, validator tries to establish all the missing connections to proxies. + pub advertise_proxies_interval: time::Duration, + /// Support for gradual TIER1 feature rollout: + /// - establishing connection to node's own proxies is always enabled (it is a part of peer + /// discovery mechanism). Note that unless the proxy has enable_inbound set, establishing + /// those connections will fail anyway. + /// - a node will start accepting TIER1 inbound connections iff `enable_inbound` is true. + /// - a node will try to start outbound TIER1 connections iff `enable_outbound` is true. + pub enable_inbound: bool, + pub enable_outbound: bool, +} + +/// Validated configuration for the peer-to-peer manager. +#[derive(Clone)] +pub struct NetworkConfig { + pub node_addr: Option, + pub node_key: SecretKey, + pub validator: Option, + + pub peer_store: peer_store::Config, + pub snapshot_hosts: snapshot_hosts::Config, + pub whitelist_nodes: Vec, + pub handshake_timeout: time::Duration, + + /// Whether to re-establish connection to known reliable peers from previous uncd run(s). + /// See unc_network::peer_manager::connection_store for details. + pub connect_to_reliable_peers_on_startup: bool, + /// Maximum time between refreshing the peer list. + pub monitor_peers_max_period: time::Duration, + /// Maximum number of active peers. Hard limit. + pub max_num_peers: u32, + /// Minimum outbound connections a peer should have to avoid eclipse attacks. + pub minimum_outbound_peers: u32, + /// Lower bound of the ideal number of connections. + pub ideal_connections_lo: u32, + /// Upper bound of the ideal number of connections. + pub ideal_connections_hi: u32, + /// Peers which last message is was within this period of time are considered active recent peers. + pub peer_recent_time_window: time::Duration, + /// Number of peers to keep while removing a connection. + /// Used to avoid disconnecting from peers we have been connected since long time. + pub safe_set_size: u32, + /// Lower bound of the number of connections to archival peers to keep + /// if we are an archival node. + pub archival_peer_connections_lower_bound: u32, + /// Maximum number of peer addresses we should ever send on PeersRequest. + pub max_send_peers: u32, + /// Duration for checking on stats from the peers. + pub peer_stats_period: time::Duration, + /// Time to persist Accounts Id in the router without removing them. + pub ttl_account_id_router: time::Duration, + /// Number of hops a message is allowed to travel before being dropped. + /// This is used to avoid infinite loop because of inconsistent view of the network + /// by different nodes. + pub routed_message_ttl: u8, + /// Maximum number of routes that we should keep track for each Account id in the Routing Table. + pub max_routes_to_store: usize, + /// Height horizon for highest height peers + /// For example if one peer is 1 height away from max height peer, + /// we still want to use the rest to query for state/headers/blocks. + pub highest_peer_horizon: u64, + /// Period between pushing network info to client + pub push_info_period: time::Duration, + /// Flag to disable outbound connections. When this flag is active, nodes will not try to + /// establish connection with other nodes, but will accept incoming connection if other requirements + /// are satisfied. + /// This flag should be ALWAYS FALSE. Only set to true for testing purposes. + pub outbound_disabled: bool, + /// Flag to disable inbound connections. When true, all the incoming handshake/connection requests will be rejected. + pub inbound_disabled: bool, + /// Whether this is an archival node. + pub archive: bool, + /// Maximal rate at which SyncAccountsData can be broadcasted. + pub accounts_data_broadcast_rate_limit: rate::Limit, + /// Maximal rate at which SyncSnapshotHosts can be broadcasted. + pub snapshot_hosts_broadcast_rate_limit: rate::Limit, + /// Maximal rate at which RoutingTable can be recomputed. + pub routing_table_update_rate_limit: rate::Limit, + /// Config of the TIER1 network. + pub tier1: Option, + + // Whether to ignore tombstones some time after startup. + // + // Ignoring tombstones means: + // * not broadcasting deleted edges + // * ignoring received deleted edges as well + pub skip_tombstones: Option, + + /// TEST-ONLY + /// TODO(gprusak): make it pub(crate), once all integration tests + /// are merged into unc_network. + pub event_sink: Sink, +} + +impl NetworkConfig { + /// Overrides values of NetworkConfig with values for the JSON config. + /// We need all the values from NetworkConfig to be configurable. + /// We need this in case of emergency. It is faster to change the config than to recompile. + fn override_config(&mut self, overrides: crate::config_json::NetworkConfigOverrides) { + if let Some(connect_to_reliable_peers_on_startup) = + overrides.connect_to_reliable_peers_on_startup + { + self.connect_to_reliable_peers_on_startup = connect_to_reliable_peers_on_startup + } + if let Some(max_send_peers) = overrides.max_send_peers { + self.max_send_peers = max_send_peers + } + if let Some(routed_message_ttl) = overrides.routed_message_ttl { + self.routed_message_ttl = routed_message_ttl + } + if let Some(max_routes_to_store) = overrides.max_routes_to_store { + self.max_routes_to_store = max_routes_to_store + } + if let Some(highest_peer_horizon) = overrides.highest_peer_horizon { + self.highest_peer_horizon = highest_peer_horizon + } + if let Some(millis) = overrides.push_info_period_millis { + self.push_info_period = time::Duration::milliseconds(millis) + } + if let Some(outbound_disabled) = overrides.outbound_disabled { + self.outbound_disabled = outbound_disabled + } + if let (Some(qps), Some(burst)) = ( + overrides.accounts_data_broadcast_rate_limit_qps, + overrides.accounts_data_broadcast_rate_limit_burst, + ) { + self.accounts_data_broadcast_rate_limit = rate::Limit { qps, burst } + } + if let (Some(qps), Some(burst)) = ( + overrides.routing_table_update_rate_limit_qps, + overrides.routing_table_update_rate_limit_burst, + ) { + self.routing_table_update_rate_limit = rate::Limit { qps, burst } + } + } + + pub fn new( + cfg: crate::config_json::Config, + node_key: SecretKey, + validator_signer: Option>, + archive: bool, + ) -> anyhow::Result { + if cfg.public_addrs.len() > MAX_PEER_ADDRS { + anyhow::bail!( + "public_addrs has {} entries, limit is {MAX_PEER_ADDRS}", + cfg.public_addrs.len() + ); + } + let mut proxies = HashSet::new(); + for proxy in &cfg.public_addrs { + if proxies.contains(&proxy.peer_id) { + anyhow::bail!("public_addrs: found multiple entries with peer_id {}. Only 1 entry per peer_id is supported.",proxy.peer_id); + } + proxies.insert(proxy.peer_id.clone()); + let ip = proxy.addr.ip(); + if cfg.allow_private_ip_in_public_addrs { + if ip.is_unspecified() { + anyhow::bail!("public_addrs: {ip} is not a valid IP. If you wanted to specify a loopback IP, use 127.0.0.1 instead."); + } + } else { + // TODO(gprusak): use !ip.is_global() instead, once it is stable. + if ip.is_loopback() + || ip.is_unspecified() + || match ip { + std::net::IpAddr::V4(ip) => ip.is_private(), + // TODO(gprusak): use ip.is_unique_local() once stable. + std::net::IpAddr::V6(_) => false, + } + { + anyhow::bail!("public_addrs: {ip} is not a public IP."); + } + } + } + let mut this = Self { + node_key, + validator: validator_signer.map(|signer| ValidatorConfig { + signer, + proxies: if !cfg.public_addrs.is_empty() { + ValidatorProxies::Static(cfg.public_addrs) + } else { + ValidatorProxies::Dynamic(cfg.trusted_stun_servers) + }, + }), + node_addr: match cfg.addr.as_str() { + "" => None, + addr => Some(tcp::ListenerAddr::new( + addr.parse().context("Failed to parse SocketAddr")?, + )), + }, + peer_store: peer_store::Config { + boot_nodes: if cfg.boot_nodes.is_empty() { + vec![] + } else { + cfg.boot_nodes + .split(',') + .map(|chunk| chunk.parse()) + .collect::>() + .context("boot_nodes")? + }, + blacklist: cfg + .blacklist + .iter() + .map(|e| e.parse()) + .collect::>() + .context("failed to parse blacklist")?, + peer_states_cache_size: cfg.peer_states_cache_size, + connect_only_to_boot_nodes: cfg.experimental.connect_only_to_boot_nodes, + ban_window: cfg.ban_window.try_into()?, + peer_expiration_duration: cfg.peer_expiration_duration.try_into()?, + }, + snapshot_hosts: snapshot_hosts::Config { + snapshot_hosts_cache_size: cfg.snapshot_hosts_cache_size, + }, + whitelist_nodes: if cfg.whitelist_nodes.is_empty() { + vec![] + } else { + cfg.whitelist_nodes + .split(',') + .map(|peer| match peer.parse::() { + Ok(peer) if peer.addr.is_none() => anyhow::bail!( + "whitelist_nodes are required to specify both PeerId and IP:port" + ), + Ok(peer) => Ok(peer), + Err(err) => Err(err.into()), + }) + .collect::>() + .context("whitelist_nodes")? + }, + connect_to_reliable_peers_on_startup: true, + handshake_timeout: cfg.handshake_timeout.try_into()?, + monitor_peers_max_period: cfg.monitor_peers_max_period.try_into()?, + max_num_peers: cfg.max_num_peers, + minimum_outbound_peers: cfg.minimum_outbound_peers, + ideal_connections_lo: cfg.ideal_connections_lo, + ideal_connections_hi: cfg.ideal_connections_hi, + peer_recent_time_window: cfg.peer_recent_time_window.try_into()?, + safe_set_size: cfg.safe_set_size, + archival_peer_connections_lower_bound: cfg.archival_peer_connections_lower_bound, + max_send_peers: PEERS_RESPONSE_MAX_PEERS, + peer_stats_period: cfg.peer_stats_period.try_into()?, + ttl_account_id_router: cfg.ttl_account_id_router.try_into()?, + routed_message_ttl: ROUTED_MESSAGE_TTL, + max_routes_to_store: MAX_ROUTES_TO_STORE, + highest_peer_horizon: HIGHEST_PEER_HORIZON, + push_info_period: time::Duration::milliseconds(100), + outbound_disabled: false, + archive, + accounts_data_broadcast_rate_limit: rate::Limit { qps: 0.1, burst: 1 }, + snapshot_hosts_broadcast_rate_limit: rate::Limit { qps: 0.1, burst: 1 }, + routing_table_update_rate_limit: rate::Limit { qps: 1., burst: 1 }, + tier1: Some(Tier1 { + connect_interval: cfg.experimental.tier1_connect_interval.try_into()?, + new_connections_per_attempt: cfg.experimental.tier1_new_connections_per_attempt, + advertise_proxies_interval: time::Duration::minutes(15), + enable_inbound: cfg.experimental.tier1_enable_inbound, + enable_outbound: cfg.experimental.tier1_enable_outbound, + }), + inbound_disabled: cfg.experimental.inbound_disabled, + skip_tombstones: if cfg.experimental.skip_sending_tombstones_seconds > 0 { + Some(time::Duration::seconds(cfg.experimental.skip_sending_tombstones_seconds)) + } else { + None + }, + event_sink: Sink::null(), + }; + this.override_config(cfg.experimental.network_config_overrides); + Ok(this) + } + + pub fn node_id(&self) -> PeerId { + PeerId::new(self.node_key.public_key()) + } + + /// TEST-ONLY: Returns network config with given seed used for peer id. + pub fn from_seed(seed: &str, node_addr: tcp::ListenerAddr) -> Self { + let node_key = SecretKey::from_seed(KeyType::ED25519, seed); + let validator = ValidatorConfig { + signer: Arc::new(create_test_signer(seed)), + proxies: ValidatorProxies::Static(vec![PeerAddr { + addr: *node_addr, + peer_id: PeerId::new(node_key.public_key()), + }]), + }; + NetworkConfig { + node_addr: Some(node_addr), + node_key, + validator: Some(validator), + peer_store: peer_store::Config { + boot_nodes: vec![], + blacklist: blacklist::Blacklist::default(), + peer_states_cache_size: 1000, + ban_window: time::Duration::seconds(1), + peer_expiration_duration: time::Duration::seconds(60 * 60), + connect_only_to_boot_nodes: false, + }, + snapshot_hosts: snapshot_hosts::Config { snapshot_hosts_cache_size: 1000 }, + whitelist_nodes: vec![], + handshake_timeout: time::Duration::seconds(5), + connect_to_reliable_peers_on_startup: true, + monitor_peers_max_period: time::Duration::seconds(100), + max_num_peers: 40, + minimum_outbound_peers: 5, + ideal_connections_lo: 30, + ideal_connections_hi: 35, + peer_recent_time_window: time::Duration::seconds(600), + safe_set_size: 20, + archival_peer_connections_lower_bound: 10, + max_send_peers: PEERS_RESPONSE_MAX_PEERS, + peer_stats_period: time::Duration::seconds(5), + ttl_account_id_router: time::Duration::seconds(60 * 60), + routed_message_ttl: ROUTED_MESSAGE_TTL, + max_routes_to_store: 1, + highest_peer_horizon: 5, + push_info_period: time::Duration::milliseconds(100), + outbound_disabled: false, + inbound_disabled: false, + archive: false, + accounts_data_broadcast_rate_limit: rate::Limit { qps: 100., burst: 1000000 }, + snapshot_hosts_broadcast_rate_limit: rate::Limit { qps: 100., burst: 1000000 }, + routing_table_update_rate_limit: rate::Limit { qps: 10., burst: 1 }, + tier1: Some(Tier1 { + // Interval is very large, so that it doesn't happen spontaneously in tests. + // It should rather be triggered manually in tests. + connect_interval: time::Duration::hours(1000), + new_connections_per_attempt: 10000, + advertise_proxies_interval: time::Duration::hours(1000), + enable_inbound: true, + enable_outbound: true, + }), + skip_tombstones: None, + event_sink: Sink::null(), + } + } + + pub fn verify(self) -> anyhow::Result { + if !(self.ideal_connections_lo <= self.ideal_connections_hi) { + anyhow::bail!( + "Invalid ideal_connections values. lo({}) > hi({}).", + self.ideal_connections_lo, + self.ideal_connections_hi + ); + } + + if !(self.ideal_connections_hi <= self.max_num_peers) { + anyhow::bail!( + "max_num_peers({}) < ideal_connections_hi({}) which may lead to connection saturation and declining new connections.", + self.max_num_peers, self.ideal_connections_hi + ); + } + + if !(self.safe_set_size > self.minimum_outbound_peers) { + anyhow::bail!( + "safe_set_size({}) must be larger than minimum_outbound_peers({}).", + self.safe_set_size, + self.minimum_outbound_peers + ); + } + + if UPDATE_INTERVAL_LAST_TIME_RECEIVED_MESSAGE * 2 > self.peer_recent_time_window { + anyhow::bail!( + "Very short peer_recent_time_window({}). it should be at least twice update_interval_last_time_received_message({}).", + self.peer_recent_time_window, UPDATE_INTERVAL_LAST_TIME_RECEIVED_MESSAGE + ); + } + + if !(self.max_send_peers <= PEERS_RESPONSE_MAX_PEERS) { + anyhow::bail!( + "max_send_peers({}) can be at most {}", + self.max_send_peers, + PEERS_RESPONSE_MAX_PEERS + ); + } + + self.accounts_data_broadcast_rate_limit + .validate() + .context("accounts_Data_broadcast_rate_limit")?; + self.routing_table_update_rate_limit + .validate() + .context("routing_table_update_rate_limit")?; + Ok(VerifiedConfig { node_id: self.node_id(), inner: self }) + } +} + +/// On every message from peer don't update `last_time_received_message` +/// but wait some "small" timeout between updates to avoid a lot of messages between +/// Peer and PeerManager. +pub const UPDATE_INTERVAL_LAST_TIME_RECEIVED_MESSAGE: time::Duration = time::Duration::seconds(60); + +#[derive(Clone)] +pub struct VerifiedConfig { + inner: NetworkConfig, + /// Cached inner.node_id(). + /// It allows to avoid recomputing the public key every time. + node_id: PeerId, +} + +impl VerifiedConfig { + pub fn node_id(&self) -> PeerId { + self.node_id.clone() + } +} + +impl std::ops::Deref for VerifiedConfig { + type Target = NetworkConfig; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +#[cfg(test)] +mod test { + use super::UPDATE_INTERVAL_LAST_TIME_RECEIVED_MESSAGE; + use crate::config; + use crate::config_json::NetworkConfigOverrides; + use crate::network_protocol; + use crate::network_protocol::testonly as data; + use crate::network_protocol::{AccountData, VersionedAccountData}; + use crate::tcp; + use crate::testonly::make_rng; + use unc_async::time; + + #[test] + fn test_network_config() { + let nc = config::NetworkConfig::from_seed("123", tcp::ListenerAddr::reserve_for_test()); + assert!(nc.verify().is_ok()); + + let mut nc = config::NetworkConfig::from_seed("123", tcp::ListenerAddr::reserve_for_test()); + nc.ideal_connections_lo = nc.ideal_connections_hi + 1; + assert!(nc.verify().is_err()); + + let mut nc = config::NetworkConfig::from_seed("123", tcp::ListenerAddr::reserve_for_test()); + nc.ideal_connections_hi = nc.max_num_peers + 1; + assert!(nc.verify().is_err()); + + let mut nc = config::NetworkConfig::from_seed("123", tcp::ListenerAddr::reserve_for_test()); + nc.safe_set_size = nc.minimum_outbound_peers; + assert!(nc.verify().is_err()); + + let mut nc = config::NetworkConfig::from_seed("123", tcp::ListenerAddr::reserve_for_test()); + nc.peer_recent_time_window = UPDATE_INTERVAL_LAST_TIME_RECEIVED_MESSAGE; + assert!(nc.verify().is_err()); + } + + #[test] + fn test_network_config_override() { + fn check_override_field( + before: &T, + after: &T, + override_val: &Option, + ) -> bool { + if let Some(val) = override_val { + return after == val; + } else { + return after == before; + } + } + let check_fields = |before: &config::NetworkConfig, + after: &config::NetworkConfig, + overrides: &NetworkConfigOverrides| { + assert!(check_override_field( + &before.connect_to_reliable_peers_on_startup, + &after.connect_to_reliable_peers_on_startup, + &overrides.connect_to_reliable_peers_on_startup + )); + assert!(check_override_field( + &before.max_send_peers, + &after.max_send_peers, + &overrides.max_send_peers + )); + assert!(check_override_field( + &before.routed_message_ttl, + &after.routed_message_ttl, + &overrides.routed_message_ttl + )); + assert!(check_override_field( + &before.max_routes_to_store, + &after.max_routes_to_store, + &overrides.max_routes_to_store + )); + assert!(check_override_field( + &before.highest_peer_horizon, + &after.highest_peer_horizon, + &overrides.highest_peer_horizon + )); + assert!(check_override_field( + &before.push_info_period, + &after.push_info_period, + &overrides + .push_info_period_millis + .map(|millis| time::Duration::milliseconds(millis)) + )); + assert!(check_override_field( + &before.outbound_disabled, + &after.outbound_disabled, + &overrides.outbound_disabled + )); + assert!(check_override_field( + &before.accounts_data_broadcast_rate_limit.burst, + &after.accounts_data_broadcast_rate_limit.burst, + &overrides.accounts_data_broadcast_rate_limit_burst + )); + assert!(check_override_field( + &before.accounts_data_broadcast_rate_limit.qps, + &after.accounts_data_broadcast_rate_limit.qps, + &overrides.accounts_data_broadcast_rate_limit_qps + )); + }; + let no_overrides = NetworkConfigOverrides::default(); + let mut overrides = NetworkConfigOverrides::default(); + overrides.connect_to_reliable_peers_on_startup = Some(false); + overrides.max_send_peers = Some(42); + overrides.routed_message_ttl = Some(43); + overrides.accounts_data_broadcast_rate_limit_burst = Some(44); + overrides.accounts_data_broadcast_rate_limit_qps = Some(45.0); + + let nc_before = + config::NetworkConfig::from_seed("123", tcp::ListenerAddr::reserve_for_test()); + + let mut nc_after = nc_before.clone(); + nc_after.override_config(no_overrides.clone()); + check_fields(&nc_before, &nc_after, &no_overrides); + assert!(nc_after.verify().is_ok()); + + nc_after = nc_before.clone(); + nc_after.override_config(overrides.clone()); + check_fields(&nc_before, &nc_after, &overrides); + assert!(nc_after.verify().is_ok()); + } + + // Check that MAX_PEER_ADDRS limit is consistent with the + // network_protocol::MAX_ACCOUNT_DATA_SIZE_BYTES limit + #[test] + fn accounts_data_size_limit() { + let mut rng = make_rng(39521947542); + let clock = time::FakeClock::default(); + let signer = data::make_validator_signer(&mut rng); + + let ad = VersionedAccountData { + data: AccountData { + proxies: (0..config::MAX_PEER_ADDRS) + .map(|_| { + // Using IPv6 gives maximal size of the resulting config. + let ip = data::make_ipv6(&mut rng); + data::make_peer_addr(&mut rng, ip) + }) + .collect(), + peer_id: data::make_peer_id(&mut rng), + }, + account_key: signer.public_key(), + version: 0, + timestamp: clock.now_utc(), + }; + let sad = ad.sign(&signer).unwrap(); + assert!(sad.payload().len() <= network_protocol::MAX_ACCOUNT_DATA_SIZE_BYTES); + } +} diff --git a/chain/network/src/config_json.rs b/chain/network/src/config_json.rs new file mode 100644 index 000000000..5eb8773e8 --- /dev/null +++ b/chain/network/src/config_json.rs @@ -0,0 +1,291 @@ +use crate::network_protocol::PeerAddr; +use crate::stun; +use std::time::Duration; + +/// Time to persist Accounts Id in the router without removing them in seconds. +pub const TTL_ACCOUNT_ID_ROUTER: u64 = 60 * 60; + +/// Maximum number of active peers. Hard limit. +fn default_max_num_peers() -> u32 { + 40 +} +/// Minimum outbound connections a peer should have to avoid eclipse attacks. +fn default_minimum_outbound_connections() -> u32 { + 5 +} +/// Lower bound of the ideal number of connections. +fn default_ideal_connections_lo() -> u32 { + 30 +} +/// Upper bound of the ideal number of connections. +fn default_ideal_connections_hi() -> u32 { + 35 +} +/// Peers which last message is was within this period of time are considered active recent peers. +fn default_peer_recent_time_window() -> Duration { + Duration::from_secs(600) +} +/// Number of peers to keep while removing a connection. +/// Used to avoid disconnecting from peers we have been connected since long time. +fn default_safe_set_size() -> u32 { + 20 +} +/// Lower bound of the number of connections to archival peers to keep +/// if we are an archival node. +fn default_archival_peer_connections_lower_bound() -> u32 { + 10 +} +/// Time to persist Accounts Id in the router without removing them in seconds. +fn default_ttl_account_id_router() -> Duration { + Duration::from_secs(TTL_ACCOUNT_ID_ROUTER) +} +/// Period to check on peer status +fn default_peer_stats_period() -> Duration { + Duration::from_secs(5) +} +/// Period to update the list of peers we connect to. +fn default_monitor_peers_max_period() -> Duration { + Duration::from_secs(60) +} +/// Maximum number of peer states to keep in memory. +fn default_peer_states_cache_size() -> u32 { + 1000 +} +/// Maximum number of snapshot hosts to keep in memory. +fn default_snapshot_hosts_cache_size() -> u32 { + 1000 +} +/// Remove peers that we didn't hear about for this amount of time. +fn default_peer_expiration_duration() -> Duration { + Duration::from_secs(7 * 24 * 60 * 60) +} + +/// This is a list of public STUN servers provided by Google, +/// which are known to have good availability. To avoid trusting +/// a centralized entity (and DNS used for domain resolution), +/// prefer to set up your own STUN server, or (even better) +/// use public_addrs instead. +fn default_trusted_stun_servers() -> Vec { + vec![ + "stun.l.google.com:19302".to_string(), + "stun1.l.google.com:19302".to_string(), + "stun2.l.google.com:19302".to_string(), + "stun3.l.google.com:19302".to_string(), + "stun4.l.google.com:19302".to_string(), + ] +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub struct Config { + /// Local address to listen for incoming connections. + pub addr: String, + /// Comma separated list of nodes to connect to. + /// Examples: + /// ed25519:86EtEy7epneKyrcJwSWP7zsisTkfDRH5CFVszt4qiQYw@31.192.22.209:24567 + /// ed25519:86EtEy7epneKyrcJwSWP7zsisTkfDRH5CFVszt4qiQYw@nearnode.com:24567 + pub boot_nodes: String, + /// Comma separated list of whitelisted nodes. Inbound connections from the nodes on + /// the whitelist are accepted even if the limit of the inbound connection has been reached. + /// For each whitelisted node specifying both PeerId and one of IP:port or Host:port is required: + /// Examples: + /// ed25519:86EtEy7epneKyrcJwSWP7zsisTkfDRH5CFVszt4qiQYw@31.192.22.209:24567 + /// ed25519:86EtEy7epneKyrcJwSWP7zsisTkfDRH5CFVszt4qiQYw@nearnode.com:24567 + #[serde(default)] + pub whitelist_nodes: String, + /// Maximum number of active peers. Hard limit. + #[serde(default = "default_max_num_peers")] + pub max_num_peers: u32, + /// Minimum outbound connections a peer should have to avoid eclipse attacks. + #[serde(default = "default_minimum_outbound_connections")] + pub minimum_outbound_peers: u32, + /// Lower bound of the ideal number of connections. + #[serde(default = "default_ideal_connections_lo")] + pub ideal_connections_lo: u32, + /// Upper bound of the ideal number of connections. + #[serde(default = "default_ideal_connections_hi")] + pub ideal_connections_hi: u32, + /// Peers which last message is was within this period of time are considered active recent peers (in seconds). + #[serde(default = "default_peer_recent_time_window")] + pub peer_recent_time_window: Duration, + /// Number of peers to keep while removing a connection. + /// Used to avoid disconnecting from peers we have been connected since long time. + #[serde(default = "default_safe_set_size")] + pub safe_set_size: u32, + /// Lower bound of the number of connections to archival peers to keep + /// if we are an archival node. + #[serde(default = "default_archival_peer_connections_lower_bound")] + pub archival_peer_connections_lower_bound: u32, + /// Handshake timeout. + pub handshake_timeout: Duration, + /// Skip waiting for peers before starting node. + pub skip_sync_wait: bool, + /// Ban window for peers who misbehave. + pub ban_window: Duration, + /// List of addresses that will not be accepted as valid neighbors. + /// It can be IP:Port or IP (to blacklist all connections coming from this address). + #[serde(default)] + pub blacklist: Vec, + /// Time to persist Accounts Id in the router without removing them in seconds. + #[serde(default = "default_ttl_account_id_router")] + pub ttl_account_id_router: Duration, + /// Period to check on peer status + #[serde(default = "default_peer_stats_period")] + pub peer_stats_period: Duration, + // Period to monitor peers (connect to new ones etc). + #[serde(default = "default_monitor_peers_max_period")] + pub monitor_peers_max_period: Duration, + + /// Maximum number of peer states to keep in memory. + #[serde(default = "default_peer_states_cache_size")] + pub peer_states_cache_size: u32, + /// Maximum number of snapshot hosts to keep in memory. + #[serde(default = "default_snapshot_hosts_cache_size")] + pub snapshot_hosts_cache_size: u32, + // Remove peers that were not active for this amount of time. + #[serde(default = "default_peer_expiration_duration")] + pub peer_expiration_duration: Duration, + + /// List of the public addresses (in the format "@:") of trusted nodes, + /// which are willing to route messages to this node. Useful only if this node is a validator. + /// This list will be signed and broadcasted to the whole network, so that everyone + /// knows how to reach the validator. + /// + /// Example: + /// ["ed25519:86EtEy7epneKyrcJwSWP7zsisTkfDRH5CFVszt4qiQYw@31.192.22.209:24567"] + /// + /// Recommended setup (requires public static IP): + /// In the simplest case this list should contains just 1 public address (with the node public + /// key) of this validator. + /// In case the validator doesn't have a public IP (i.e. it is hidden in a private network), + /// this list should contain public addresses of the trusted nodes which will be routing messages to the + /// validator - validator will connect to these nodes immediately after startup. + /// TODO(gprusak): in case a connection cannot be established (the peer is + /// unreachable/down/etc.) validator should probably remove (temporarily) the problematic peer from the list + /// and broadcast the new version of the list. + /// + /// Less recommended setup (requires exactly one public dynamic/ephemeral or static IP): + /// If the list is empty, the validator node will query trusted_stun_servers to determine its own IP. + /// Only if the answer from the STUN servers is unambiguous (at least 1 server responds and + /// all received responses provide the same IP), the IP (together with the port deduced from + /// the addr field in this config) will be signed and broadcasted. + /// + /// Discouraged setup (might be removed in the future) + /// If the list is empty and STUN servers' response is ambiguous, the peers which connect to + /// this validator node will natually observe the address of the validator and broadcast it. + /// This setup is not reliable in presence of byzantine peers. + #[serde(default)] + pub public_addrs: Vec, + /// For local tests only (localnet). Allows specifying IPs from private range + /// (which are not visible from the public internet) in public_addrs field. + #[serde(default)] + pub allow_private_ip_in_public_addrs: bool, + /// List of endpoints of trusted [STUN servers](https://datatracker.ietf.org/doc/html/rfc8489). + /// + /// Used only if this node is a validator and public_addrs is empty (see + /// description of public_addrs field). Format `:`, for + /// example `stun.l.google.com:19302`. The STUN servers are queried periodically in parallel. + /// We do not expect all the servers listed to be up all the time, but all the + /// responses are expected to be consistent - if different servers return differn IPs, then + /// the response set would be considered ambiguous and the node won't advertise any proxy in + /// such a case. + #[serde(default = "default_trusted_stun_servers")] + pub trusted_stun_servers: Vec, + // Experimental part of the JSON config. Regular users/validators should not have to set any values there. + // Field names in here can change/disappear at any moment without warning. + #[serde(default)] + pub experimental: ExperimentalConfig, +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub struct ExperimentalConfig { + // If true - don't allow any inbound connections. + pub inbound_disabled: bool, + // If true - connect only to the boot nodes. + pub connect_only_to_boot_nodes: bool, + + // If greater than 0, then system will no longer send or receive tombstones + // during sync and during that many seconds after startup. + // + // The better name is `skip_tombstones_seconds`, but we keep send for + // compatibility. + pub skip_sending_tombstones_seconds: i64, + + /// See `unc_network::config::Tier1::enable_inbound`. + pub tier1_enable_inbound: bool, + + /// See `unc_network::config::Tier1::enable_outbound`. + pub tier1_enable_outbound: bool, + + /// See `unc_network::config::Tier1::connect_interval`. + pub tier1_connect_interval: Duration, + + /// See `unc_network::config::Tier1::new_connections_per_attempt`. + pub tier1_new_connections_per_attempt: u64, + + /// See `NetworkConfig`. + /// Fields set here will override the NetworkConfig fields. + pub network_config_overrides: NetworkConfigOverrides, +} + +/// Overrides values from NetworkConfig. +/// This enables the user to override the hardcoded values. +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Default)] +pub struct NetworkConfigOverrides { + pub connect_to_reliable_peers_on_startup: Option, + pub max_send_peers: Option, + pub routed_message_ttl: Option, + pub max_routes_to_store: Option, + pub highest_peer_horizon: Option, + pub push_info_period_millis: Option, + pub outbound_disabled: Option, + pub accounts_data_broadcast_rate_limit_burst: Option, + pub accounts_data_broadcast_rate_limit_qps: Option, + pub routing_table_update_rate_limit_burst: Option, + pub routing_table_update_rate_limit_qps: Option, +} + +impl Default for ExperimentalConfig { + fn default() -> Self { + ExperimentalConfig { + inbound_disabled: false, + connect_only_to_boot_nodes: false, + skip_sending_tombstones_seconds: 0, + tier1_enable_inbound: true, + tier1_enable_outbound: true, + tier1_connect_interval: Duration::from_secs(60), + tier1_new_connections_per_attempt: 50, + network_config_overrides: Default::default(), + } + } +} + +impl Default for Config { + fn default() -> Self { + Config { + addr: "0.0.0.0:24567".to_string(), + boot_nodes: "".to_string(), + whitelist_nodes: "".to_string(), + max_num_peers: default_max_num_peers(), + minimum_outbound_peers: default_minimum_outbound_connections(), + ideal_connections_lo: default_ideal_connections_lo(), + ideal_connections_hi: default_ideal_connections_hi(), + peer_recent_time_window: default_peer_recent_time_window(), + safe_set_size: default_safe_set_size(), + archival_peer_connections_lower_bound: default_archival_peer_connections_lower_bound(), + handshake_timeout: Duration::from_secs(20), + skip_sync_wait: false, + peer_states_cache_size: default_peer_states_cache_size(), + snapshot_hosts_cache_size: default_snapshot_hosts_cache_size(), + ban_window: Duration::from_secs(3 * 60 * 60), + blacklist: vec![], + ttl_account_id_router: default_ttl_account_id_router(), + peer_stats_period: default_peer_stats_period(), + monitor_peers_max_period: default_monitor_peers_max_period(), + peer_expiration_duration: default_peer_expiration_duration(), + public_addrs: vec![], + allow_private_ip_in_public_addrs: false, + trusted_stun_servers: default_trusted_stun_servers(), + experimental: Default::default(), + } + } +} diff --git a/chain/network/src/debug.rs b/chain/network/src/debug.rs new file mode 100644 index 000000000..1aee4942a --- /dev/null +++ b/chain/network/src/debug.rs @@ -0,0 +1,27 @@ +use ::actix::Message; +use unc_primitives::views::{ + NetworkGraphView, NetworkRoutesView, PeerStoreView, RecentOutboundConnectionsView, + SnapshotHostsView, +}; + +// Different debug requests that can be sent by HTML pages, via GET. +pub enum GetDebugStatus { + PeerStore, + Graph, + RecentOutboundConnections, + Routes, + SnapshotHosts, +} + +#[derive(actix::MessageResponse, Debug)] +pub enum DebugStatus { + PeerStore(PeerStoreView), + Graph(NetworkGraphView), + RecentOutboundConnections(RecentOutboundConnectionsView), + Routes(NetworkRoutesView), + SnapshotHosts(SnapshotHostsView), +} + +impl Message for GetDebugStatus { + type Result = DebugStatus; +} diff --git a/chain/network/src/lib.rs b/chain/network/src/lib.rs new file mode 100644 index 000000000..d257857cc --- /dev/null +++ b/chain/network/src/lib.rs @@ -0,0 +1,35 @@ +pub use crate::peer_manager::peer_manager_actor::{Event, PeerManagerActor}; + +mod accounts_data; +mod announce_accounts; +mod network_protocol; +mod peer; +mod peer_manager; +mod private_actix; +mod snapshot_hosts; +mod stats; +mod store; +mod stun; + +pub mod actix; +pub mod blacklist; +pub mod client; +pub mod concurrency; +pub mod config; +pub mod config_json; +pub mod debug; +pub mod raw; +pub mod routing; +pub mod shards_manager; +pub mod state_sync; +pub mod tcp; +pub mod test_loop; +pub mod test_utils; +pub mod types; + +#[cfg(test)] +pub(crate) mod testonly; + +// TODO(gprusak): these should be testonly, once all network integration tests are moved to unc_network. +pub mod broadcast; +pub mod sink; diff --git a/chain/network/src/network_protocol/borsh.rs b/chain/network/src/network_protocol/borsh.rs new file mode 100644 index 000000000..c465fd70f --- /dev/null +++ b/chain/network/src/network_protocol/borsh.rs @@ -0,0 +1,165 @@ +//! Contains types that belong to the `network protocol. +//! +//! WARNING WARNING WARNING +//! WARNING WARNING WARNING +//! We need to maintain backwards compatibility, all changes to this file needs to be reviews. +use crate::network_protocol::edge::{Edge, PartialEdgeInfo}; +use crate::network_protocol::SyncSnapshotHosts; +use crate::network_protocol::{PeerChainInfoV2, PeerInfo, RoutedMessage, StateResponseInfo}; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_primitives::block::{Block, BlockHeader, GenesisId}; +use unc_primitives::challenge::Challenge; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::ShardId; +use std::fmt; +use std::fmt::Formatter; + +#[derive(BorshSerialize, PartialEq, Eq, Clone, Debug)] +/// Structure representing handshake between peers. +/// This replaces deprecated handshake `HandshakeV2`. +pub struct Handshake { + /// Current protocol version. + pub(crate) protocol_version: u32, + /// Oldest supported protocol version. + pub(crate) oldest_supported_version: u32, + /// Sender's peer id. + pub(crate) sender_peer_id: PeerId, + /// Receiver's peer id. + pub(crate) target_peer_id: PeerId, + /// Sender's listening addr. + pub(crate) sender_listen_port: Option, + /// Peer's chain information. + pub(crate) sender_chain_info: PeerChainInfoV2, + /// Represents new `edge`. Contains only `none` and `Signature` from the sender. + pub(crate) partial_edge_info: PartialEdgeInfo, +} + +/// Struct describing the layout for Handshake. +/// It is used to automatically derive BorshDeserialize. +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)] +struct HandshakeAutoDes { + /// Protocol version. + protocol_version: u32, + /// Oldest supported protocol version. + oldest_supported_version: u32, + /// Sender's peer id. + sender_peer_id: PeerId, + /// Receiver's peer id. + target_peer_id: PeerId, + /// Sender's listening addr. + sender_listen_port: Option, + /// Peer's chain information. + sender_chain_info: PeerChainInfoV2, + /// Info for new edge. + partial_edge_info: PartialEdgeInfo, +} + +// Use custom deserializer for HandshakeV2. Try to read version of the other peer from the header. +// If the version is supported then fallback to standard deserializer. +impl BorshDeserialize for Handshake { + fn deserialize_reader(rd: &mut R) -> std::io::Result { + HandshakeAutoDes::deserialize_reader(rd).map(Into::into) + } +} + +impl From for Handshake { + fn from(handshake: HandshakeAutoDes) -> Self { + Self { + protocol_version: handshake.protocol_version, + oldest_supported_version: handshake.oldest_supported_version, + sender_peer_id: handshake.sender_peer_id, + target_peer_id: handshake.target_peer_id, + sender_listen_port: handshake.sender_listen_port, + sender_chain_info: handshake.sender_chain_info, + partial_edge_info: handshake.partial_edge_info, + } + } +} + +#[derive(Default, BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)] +pub(super) struct RoutingTableUpdate { + pub edges: Vec, + pub accounts: Vec, +} + +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)] +pub struct AdvertisedPeerDistance { + pub destination: PeerId, + pub distance: u32, +} + +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)] +pub(super) struct DistanceVector { + pub root: PeerId, + pub distances: Vec, + pub edges: Vec, +} + +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)] +pub enum HandshakeFailureReason { + ProtocolVersionMismatch { version: u32, oldest_supported_version: u32 }, + GenesisMismatch(GenesisId), + InvalidTarget, +} +const _: () = assert!( + std::mem::size_of::() <= 64, + "HandshakeFailureReason > 64 bytes" +); + +impl fmt::Display for HandshakeFailureReason { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "HandshakeFailureReason") + } +} + +impl std::error::Error for HandshakeFailureReason {} + +/// Warning, position of each message type in this enum defines the protocol due to serialization. +/// DO NOT MOVE, REORDER, DELETE items from the list. Only add new items to the end. +/// If need to remove old items - replace with `None`. +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug, strum::AsRefStr)] +// TODO(#1313): Use Box +pub(super) enum PeerMessage { + Handshake(Handshake), + HandshakeFailure(PeerInfo, HandshakeFailureReason), + /// When a failed nonce is used by some peer, this message is sent back as evidence. + LastEdge(Edge), + /// Contains accounts and edge information. + SyncRoutingTable(RoutingTableUpdate), + RequestUpdateNonce(PartialEdgeInfo), + _ResponseUpdateNonce, + + PeersRequest, + PeersResponse(Vec), + + BlockHeadersRequest(Vec), + BlockHeaders(Vec), + + BlockRequest(CryptoHash), + Block(Block), + + Transaction(SignedTransaction), + Routed(Box), + + /// Gracefully disconnect from other peer. + Disconnect, + Challenge(Challenge), + + _HandshakeV2, + _EpochSyncRequest, + _EpochSyncResponse, + _EpochSyncFinalizationRequest, + _EpochSyncFinalizationResponse, + _RoutingTableSyncV2, + + DistanceVector(DistanceVector), + + StateRequestHeader(ShardId, CryptoHash), + StateRequestPart(ShardId, CryptoHash, u64), + VersionedStateResponse(StateResponseInfo), + SyncSnapshotHosts(SyncSnapshotHosts), +} +//#[cfg(target_arch = "x86_64")] // Non-x86_64 doesn't match this requirement yet but it's not bad as it's not production-ready +//const _: () = assert!(std::mem::size_of::() <= 1500, "PeerMessage > 1500 bytes"); diff --git a/chain/network/src/network_protocol/borsh_conv.rs b/chain/network/src/network_protocol/borsh_conv.rs new file mode 100644 index 000000000..4ef69a0dc --- /dev/null +++ b/chain/network/src/network_protocol/borsh_conv.rs @@ -0,0 +1,258 @@ +/// Contains borsh <-> network_protocol conversions. +use crate::network_protocol as mem; +use crate::network_protocol::borsh_ as net; +use crate::network_protocol::{PeersRequest, PeersResponse, RoutedMessageV2}; + +impl From<&net::Handshake> for mem::Handshake { + fn from(x: &net::Handshake) -> Self { + Self { + protocol_version: x.protocol_version, + oldest_supported_version: x.oldest_supported_version, + sender_peer_id: x.sender_peer_id.clone(), + target_peer_id: x.target_peer_id.clone(), + sender_listen_port: x.sender_listen_port, + sender_chain_info: x.sender_chain_info.clone(), + partial_edge_info: x.partial_edge_info.clone(), + owned_account: None, + } + } +} + +impl From<&mem::Handshake> for net::Handshake { + fn from(x: &mem::Handshake) -> Self { + Self { + protocol_version: x.protocol_version, + oldest_supported_version: x.oldest_supported_version, + sender_peer_id: x.sender_peer_id.clone(), + target_peer_id: x.target_peer_id.clone(), + sender_listen_port: x.sender_listen_port, + sender_chain_info: x.sender_chain_info.clone(), + partial_edge_info: x.partial_edge_info.clone(), + } + } +} + +////////////////////////////////////////// + +impl From<&net::HandshakeFailureReason> for mem::HandshakeFailureReason { + fn from(x: &net::HandshakeFailureReason) -> Self { + match x { + net::HandshakeFailureReason::ProtocolVersionMismatch { + version, + oldest_supported_version, + } => mem::HandshakeFailureReason::ProtocolVersionMismatch { + version: *version, + oldest_supported_version: *oldest_supported_version, + }, + net::HandshakeFailureReason::GenesisMismatch(genesis_id) => { + mem::HandshakeFailureReason::GenesisMismatch(genesis_id.clone()) + } + net::HandshakeFailureReason::InvalidTarget => { + mem::HandshakeFailureReason::InvalidTarget + } + } + } +} + +impl From<&mem::HandshakeFailureReason> for net::HandshakeFailureReason { + fn from(x: &mem::HandshakeFailureReason) -> Self { + match x { + mem::HandshakeFailureReason::ProtocolVersionMismatch { + version, + oldest_supported_version, + } => net::HandshakeFailureReason::ProtocolVersionMismatch { + version: *version, + oldest_supported_version: *oldest_supported_version, + }, + mem::HandshakeFailureReason::GenesisMismatch(genesis_id) => { + net::HandshakeFailureReason::GenesisMismatch(genesis_id.clone()) + } + mem::HandshakeFailureReason::InvalidTarget => { + net::HandshakeFailureReason::InvalidTarget + } + } + } +} + +////////////////////////////////////////// + +impl From for mem::RoutingTableUpdate { + fn from(x: net::RoutingTableUpdate) -> Self { + Self { edges: x.edges, accounts: x.accounts } + } +} + +impl From for net::RoutingTableUpdate { + fn from(x: mem::RoutingTableUpdate) -> Self { + Self { edges: x.edges, accounts: x.accounts } + } +} + +////////////////////////////////////////// + +impl From for mem::AdvertisedPeerDistance { + fn from(x: net::AdvertisedPeerDistance) -> Self { + Self { destination: x.destination, distance: x.distance } + } +} + +impl From for net::AdvertisedPeerDistance { + fn from(x: mem::AdvertisedPeerDistance) -> Self { + Self { destination: x.destination, distance: x.distance } + } +} + +////////////////////////////////////////// + +impl From for mem::DistanceVector { + fn from(x: net::DistanceVector) -> Self { + Self { + root: x.root, + distances: x.distances.into_iter().map(|y| y.into()).collect(), + edges: x.edges, + } + } +} + +impl From for net::DistanceVector { + fn from(x: mem::DistanceVector) -> Self { + Self { + root: x.root, + distances: x.distances.into_iter().map(|y| y.into()).collect(), + edges: x.edges, + } + } +} + +////////////////////////////////////////// + +#[derive(thiserror::Error, Debug)] +pub enum ParsePeerMessageError { + #[error("HandshakeV2 is deprecated")] + DeprecatedHandshakeV2, + #[error("RoutingTableSyncV2 is deprecated")] + DeprecatedRoutingTableSyncV2, + #[error("EpochSync is deprecated")] + DeprecatedEpochSync, + #[error("ResponseUpdateNonce is deprecated")] + DeprecatedResponseUpdateNonce, +} + +impl TryFrom<&net::PeerMessage> for mem::PeerMessage { + type Error = ParsePeerMessageError; + fn try_from(x: &net::PeerMessage) -> Result { + Ok(match x.clone() { + net::PeerMessage::Handshake(h) => mem::PeerMessage::Tier2Handshake((&h).into()), + net::PeerMessage::HandshakeFailure(pi, hfr) => { + mem::PeerMessage::HandshakeFailure(pi, (&hfr).into()) + } + net::PeerMessage::LastEdge(e) => mem::PeerMessage::LastEdge(e), + net::PeerMessage::SyncRoutingTable(rtu) => { + mem::PeerMessage::SyncRoutingTable(rtu.into()) + } + net::PeerMessage::RequestUpdateNonce(e) => mem::PeerMessage::RequestUpdateNonce(e), + net::PeerMessage::_ResponseUpdateNonce => { + return Err(Self::Error::DeprecatedResponseUpdateNonce) + } + net::PeerMessage::PeersRequest => mem::PeerMessage::PeersRequest(PeersRequest { + max_peers: None, + max_direct_peers: None, + }), + net::PeerMessage::PeersResponse(pis) => { + mem::PeerMessage::PeersResponse(PeersResponse { peers: pis, direct_peers: vec![] }) + } + net::PeerMessage::BlockHeadersRequest(bhs) => { + mem::PeerMessage::BlockHeadersRequest(bhs) + } + net::PeerMessage::BlockHeaders(bhs) => mem::PeerMessage::BlockHeaders(bhs), + net::PeerMessage::BlockRequest(bh) => mem::PeerMessage::BlockRequest(bh), + net::PeerMessage::Block(b) => mem::PeerMessage::Block(b), + net::PeerMessage::Transaction(t) => mem::PeerMessage::Transaction(t), + net::PeerMessage::Routed(r) => mem::PeerMessage::Routed(Box::new(RoutedMessageV2 { + msg: *r, + created_at: None, + num_hops: Some(0), + })), + net::PeerMessage::Disconnect => mem::PeerMessage::Disconnect(mem::Disconnect { + // This flag is used by the disconnecting peer to advise the other peer that there + // is a reason to remove the connection from storage (for example, a peer ban). + // In the absence of such information, it should default to false. + remove_from_connection_store: false, + }), + net::PeerMessage::Challenge(c) => mem::PeerMessage::Challenge(c), + net::PeerMessage::_HandshakeV2 => return Err(Self::Error::DeprecatedHandshakeV2), + net::PeerMessage::_EpochSyncRequest => return Err(Self::Error::DeprecatedEpochSync), + net::PeerMessage::_EpochSyncResponse => return Err(Self::Error::DeprecatedEpochSync), + net::PeerMessage::_EpochSyncFinalizationRequest => { + return Err(Self::Error::DeprecatedEpochSync) + } + net::PeerMessage::_EpochSyncFinalizationResponse => { + return Err(Self::Error::DeprecatedEpochSync) + } + net::PeerMessage::_RoutingTableSyncV2 => { + return Err(Self::Error::DeprecatedRoutingTableSyncV2) + } + net::PeerMessage::DistanceVector(dv) => mem::PeerMessage::DistanceVector(dv.into()), + net::PeerMessage::StateRequestHeader(shard_id, sync_hash) => { + mem::PeerMessage::StateRequestHeader(shard_id, sync_hash) + } + net::PeerMessage::StateRequestPart(shard_id, sync_hash, part_id) => { + mem::PeerMessage::StateRequestPart(shard_id, sync_hash, part_id) + } + net::PeerMessage::VersionedStateResponse(sri) => { + mem::PeerMessage::VersionedStateResponse(sri) + } + net::PeerMessage::SyncSnapshotHosts(ssh) => mem::PeerMessage::SyncSnapshotHosts(ssh), + }) + } +} + +// We are working on deprecating Borsh support for network messages altogether, +// so any new message variants are simply unsupported. +impl From<&mem::PeerMessage> for net::PeerMessage { + fn from(x: &mem::PeerMessage) -> Self { + match x.clone() { + mem::PeerMessage::Tier1Handshake(_) => { + panic!("Tier1Handshake is not supported in Borsh encoding") + } + mem::PeerMessage::Tier2Handshake(h) => net::PeerMessage::Handshake((&h).into()), + mem::PeerMessage::HandshakeFailure(pi, hfr) => { + net::PeerMessage::HandshakeFailure(pi, (&hfr).into()) + } + mem::PeerMessage::LastEdge(e) => net::PeerMessage::LastEdge(e), + mem::PeerMessage::SyncRoutingTable(rtu) => { + net::PeerMessage::SyncRoutingTable(rtu.into()) + } + mem::PeerMessage::RequestUpdateNonce(e) => net::PeerMessage::RequestUpdateNonce(e), + mem::PeerMessage::DistanceVector(dv) => net::PeerMessage::DistanceVector(dv.into()), + + // This message is not supported, we translate it to an empty RoutingTableUpdate. + mem::PeerMessage::SyncAccountsData(_) => { + net::PeerMessage::SyncRoutingTable(net::RoutingTableUpdate::default()) + } + + mem::PeerMessage::PeersRequest(_) => net::PeerMessage::PeersRequest, + mem::PeerMessage::PeersResponse(pr) => net::PeerMessage::PeersResponse(pr.peers), + mem::PeerMessage::BlockHeadersRequest(bhs) => { + net::PeerMessage::BlockHeadersRequest(bhs) + } + mem::PeerMessage::BlockHeaders(bhs) => net::PeerMessage::BlockHeaders(bhs), + mem::PeerMessage::BlockRequest(bh) => net::PeerMessage::BlockRequest(bh), + mem::PeerMessage::Block(b) => net::PeerMessage::Block(b), + mem::PeerMessage::Transaction(t) => net::PeerMessage::Transaction(t), + mem::PeerMessage::Routed(r) => net::PeerMessage::Routed(Box::new(r.msg.clone())), + mem::PeerMessage::Disconnect(_) => net::PeerMessage::Disconnect, + mem::PeerMessage::Challenge(c) => net::PeerMessage::Challenge(c), + mem::PeerMessage::StateRequestHeader(shard_id, sync_hash) => { + net::PeerMessage::StateRequestHeader(shard_id, sync_hash) + } + mem::PeerMessage::StateRequestPart(shard_id, sync_hash, part_id) => { + net::PeerMessage::StateRequestPart(shard_id, sync_hash, part_id) + } + mem::PeerMessage::VersionedStateResponse(sri) => { + net::PeerMessage::VersionedStateResponse(sri) + } + mem::PeerMessage::SyncSnapshotHosts(ssh) => net::PeerMessage::SyncSnapshotHosts(ssh), + } + } +} diff --git a/chain/network/src/network_protocol/edge.rs b/chain/network/src/network_protocol/edge.rs new file mode 100644 index 000000000..da6687cc3 --- /dev/null +++ b/chain/network/src/network_protocol/edge.rs @@ -0,0 +1,338 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_async::time; +use unc_crypto::{KeyType, SecretKey, Signature}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::PeerId; +use once_cell::sync::Lazy; +use std::sync::Arc; + +// We'd treat all nonces that are below this values as 'old style' (without any expiration time). +// And all nonces above this value as new style (that would expire after some time). +// This value is set to August 8, 2022. +// TODO: Remove this in Dec 2022 - once we finish migration to new nonces. +pub const EDGE_MIN_TIMESTAMP_NONCE: Lazy = + Lazy::new(|| time::Utc::from_unix_timestamp(1660000000).unwrap()); + +/// Information that will be ultimately used to create a new edge. +/// It contains nonce proposed for the edge with signature from peer. +#[derive(Clone, BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Default)] +pub struct PartialEdgeInfo { + pub nonce: u64, + pub signature: Signature, +} + +impl PartialEdgeInfo { + pub fn new(peer0: &PeerId, peer1: &PeerId, nonce: u64, secret_key: &SecretKey) -> Self { + let data = Edge::build_hash(peer0, peer1, nonce); + let signature = secret_key.sign(data.as_ref()); + Self { nonce, signature } + } +} + +#[derive(thiserror::Error, Debug)] +pub enum InvalidNonceError { + #[error("nonce is overflowing i64: {nonce}")] + NonceOutOfBoundsError { nonce: u64 }, +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "test_features", derive(serde::Serialize, serde::Deserialize))] +pub struct Edge(pub Arc); + +impl Edge { + /// Create an addition edge. + pub fn new( + peer0: PeerId, + peer1: PeerId, + nonce: u64, + signature0: Signature, + signature1: Signature, + ) -> Self { + Edge(Arc::new(EdgeInner::new(peer0, peer1, nonce, signature0, signature1))) + } + + pub fn with_removal_info(mut self, ri: Option<(bool, Signature)>) -> Edge { + Arc::make_mut(&mut self.0).removal_info = ri; + self + } + + pub fn key(&self) -> &(PeerId, PeerId) { + &self.0.key + } + + pub fn nonce(&self) -> u64 { + self.0.nonce + } + + pub fn signature0(&self) -> &Signature { + &self.0.signature0 + } + + pub fn signature1(&self) -> &Signature { + &self.0.signature1 + } + + pub fn removal_info(&self) -> Option<&(bool, Signature)> { + self.0.removal_info.as_ref() + } + + pub fn make_fake_edge(peer0: PeerId, peer1: PeerId, nonce: u64) -> Self { + Self(Arc::new(EdgeInner { + key: if peer0 < peer1 { (peer0, peer1) } else { (peer1, peer0) }, + nonce, + signature0: Signature::empty(KeyType::ED25519), + signature1: Signature::empty(KeyType::ED25519), + removal_info: None, + })) + } + + /// Build a new edge with given information from the other party. + pub fn build_with_secret_key( + peer0: PeerId, + peer1: PeerId, + nonce: u64, + secret_key: &SecretKey, + signature1: Signature, + ) -> Self { + let hash = if peer0 < peer1 { + Self::build_hash(&peer0, &peer1, nonce) + } else { + Self::build_hash(&peer1, &peer0, nonce) + }; + let signature0 = secret_key.sign(hash.as_ref()); + Self::new(peer0, peer1, nonce, signature0, signature1) + } + + /// Build the hash of the edge given its content. + /// It is important that peer0 < peer1 at this point. + pub fn build_hash(peer0: &PeerId, peer1: &PeerId, nonce: u64) -> CryptoHash { + let (peer0, peer1) = if peer0 < peer1 { (peer0, peer1) } else { (peer1, peer0) }; + CryptoHash::hash_borsh((peer0, peer1, nonce)) + } + + pub fn make_key(peer0: PeerId, peer1: PeerId) -> (PeerId, PeerId) { + if peer0 < peer1 { + (peer0, peer1) + } else { + (peer1, peer0) + } + } + + /// Helper function when adding a new edge and we receive information from new potential peer + /// to verify the signature. + pub fn partial_verify(peer0: &PeerId, peer1: &PeerId, edge_info: &PartialEdgeInfo) -> bool { + let pk = peer1.public_key(); + let data = Edge::build_hash(peer0, peer1, edge_info.nonce); + edge_info.signature.verify(data.as_ref(), pk) + } + + /// Next nonce of valid addition edge. + pub fn next_nonce(nonce: u64) -> u64 { + if nonce % 2 == 1 { + nonce + 2 + } else { + nonce + 1 + } + } + + /// Create a fresh nonce (based on the current time). + pub fn create_fresh_nonce(clock: &time::Clock) -> u64 { + let mut nonce = clock.now_utc().unix_timestamp() as u64; + // Even nonce means that the edge should be removed, so if the timestamp is even, add one to get the odd value. + if nonce % 2 == 0 { + nonce += 1; + } + nonce + } + + /// Create the remove edge change from an added edge change. + pub fn remove_edge(&self, my_peer_id: PeerId, sk: &SecretKey) -> Edge { + assert_eq!(self.edge_type(), EdgeState::Active); + let mut edge = self.0.as_ref().clone(); + edge.nonce += 1; + let me = edge.key.0 == my_peer_id; + let hash = edge.hash(); + let signature = sk.sign(hash.as_ref()); + edge.removal_info = Some((me, signature)); + Edge(Arc::new(edge)) + } + + pub(crate) fn hash(&self) -> CryptoHash { + Edge::build_hash(&self.key().0, &self.key().1, self.nonce()) + } + + fn prev_hash(&self) -> CryptoHash { + Edge::build_hash(&self.key().0, &self.key().1, self.nonce() - 1) + } + + pub fn verify(&self) -> bool { + if self.key().0 > self.key().1 { + return false; + } + + match self.edge_type() { + EdgeState::Active => { + let data = self.hash(); + + self.removal_info().is_none() + && self.signature0().verify(data.as_ref(), self.key().0.public_key()) + && self.signature1().verify(data.as_ref(), self.key().1.public_key()) + } + EdgeState::Removed => { + // nonce should be an even positive number + if self.nonce() == 0 { + return false; + } + + // Check referring added edge is valid. + let add_hash = self.prev_hash(); + if !self.signature0().verify(add_hash.as_ref(), self.key().0.public_key()) + || !self.signature1().verify(add_hash.as_ref(), self.key().1.public_key()) + { + return false; + } + + if let Some((party, signature)) = self.removal_info() { + let peer = if *party { &self.key().0 } else { &self.key().1 }; + let del_hash = self.hash(); + signature.verify(del_hash.as_ref(), peer.public_key()) + } else { + false + } + } + } + } + + /// It will be considered as a new edge if the nonce is odd, otherwise it is canceling the + /// previous edge. + pub fn edge_type(&self) -> EdgeState { + if self.nonce() % 2 == 1 { + EdgeState::Active + } else { + EdgeState::Removed + } + } + /// Next nonce of valid addition edge. + pub fn next(&self) -> u64 { + Edge::next_nonce(self.nonce()) + } + + pub fn contains_peer(&self, peer_id: &PeerId) -> bool { + self.key().0 == *peer_id || self.key().1 == *peer_id + } + + /// Find a peer id in this edge different from `me`. + pub fn other(&self, me: &PeerId) -> Option<&PeerId> { + if self.key().0 == *me { + Some(&self.key().1) + } else if self.key().1 == *me { + Some(&self.key().0) + } else { + None + } + } + + // Checks if edge was created before a given timestamp. + pub fn is_edge_older_than(&self, utc_timestamp: time::Utc) -> bool { + Edge::nonce_to_utc(self.nonce()).map_or(false, |maybe_timestamp| { + // Old-style nonce - for now, assume that they are always fresh. + maybe_timestamp.map_or(false, |nonce_timestamp| nonce_timestamp < utc_timestamp) + }) + } + + pub fn nonce_to_utc(nonce: u64) -> Result, InvalidNonceError> { + if let Ok(nonce_as_i64) = i64::try_from(nonce) { + time::Utc::from_unix_timestamp(nonce_as_i64) + .map( + |nonce_ts| { + if nonce_ts > *EDGE_MIN_TIMESTAMP_NONCE { + Some(nonce_ts) + } else { + None + } + }, + ) + .map_err(|_| InvalidNonceError::NonceOutOfBoundsError { nonce }) + } else { + Err(InvalidNonceError::NonceOutOfBoundsError { nonce }) + } + } + + // Returns a single edge with the highest nonce for each key of the input edges. + pub fn deduplicate<'a>(mut edges: Vec) -> Vec { + edges.sort_by(|a, b| (b.key(), b.nonce()).cmp(&(a.key(), a.nonce()))); + edges.dedup_by_key(|e| e.key().clone()); + edges + } +} + +/// An `Edge` represents a direct connection between two peers in unc Protocol P2P network. +/// +/// Note that edge might either in `Active` or `Removed` state. +/// We need to keep explicitly `Removed` edges, in order to be able to proof, that given `Edge` +/// isn't `Active` anymore. In case, someone delivers a proof that the edge existed. +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "test_features", derive(serde::Serialize, serde::Deserialize))] +pub struct EdgeInner { + /// Each edge consists of unordered pair of public keys of both peers. + /// `key.0 < key.1` holds true. + key: (PeerId, PeerId), + /// `nonce` is unique number representing state of an edge. + /// Odd number indicates that `edge` has been added, `even` number that it was removed. + /// New edge starts with value of `1`. + /// We update the edge only if it's `nonce` is higher. All versions of `Edge` with lower + /// `nonce` will be ignored. + nonce: u64, + /// Each `edge` consists of two signatures, one for each `peer`. + /// It's generated by signing triple (key.0, key.1, nonce) by each `peer` private key. + /// `Signature` is generated at the time when edge is added, that is when `nonce` is `odd`. + /// `Signature` can be verified by checking `peers` `PublicKey` against the signature. + /// `Signature` from peer `key.0`. + signature0: Signature, + /// `Signature` from peer `key.1`. + signature1: Signature, + /// There are two cases: + /// - `nonce` is odd, then `removal_info` will be None + /// - `nonce` is even, then the structure will be a pair with a signature of the party removing + /// the edge: + /// - `bool` - `false` if `peer0` signed the `edge` `true` if `peer1`. + /// - `Signature` - `Signature` of either `peer0` or `peer1`, depending on which peer + /// removed the edge. + removal_info: Option<(bool, Signature)>, +} + +impl EdgeInner { + /// Create an addition edge. + fn new( + peer0: PeerId, + peer1: PeerId, + nonce: u64, + signature0: Signature, + signature1: Signature, + ) -> Self { + let (peer0, signature0, peer1, signature1) = if peer0 < peer1 { + (peer0, signature0, peer1, signature1) + } else { + (peer1, signature1, peer0, signature0) + }; + + Self { key: (peer0, peer1), nonce, signature0, signature1, removal_info: None } + } + + fn hash(&self) -> CryptoHash { + Edge::build_hash(&self.key.0, &self.key.1, self.nonce) + } +} + +/// State of a given edge. +/// Every edge starts in `Active` state. It can be removed and go to `Removed` state, and then +/// added back to go to `Active` state, etc. +#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug, Hash)] +pub enum EdgeState { + /// `Edge` is `Active` if there is an active connection between two peers on the network. + Active, + /// `Edge` is in `Removed` state if it was previously in `Active` state, but has been removed. + /// A signature of one of the peers is requires, otherwise the edge will stay active. + /// Though, it may be removed from memory if both peers become unreachable. + Removed, +} diff --git a/chain/network/src/network_protocol/mod.rs b/chain/network/src/network_protocol/mod.rs new file mode 100644 index 000000000..42f734b65 --- /dev/null +++ b/chain/network/src/network_protocol/mod.rs @@ -0,0 +1,842 @@ +/// Contains types that belong to the `network protocol. +#[path = "borsh.rs"] +mod borsh_; +mod borsh_conv; +mod edge; +mod peer; +mod proto_conv; +mod state_sync; +pub use edge::*; +use unc_primitives::chunk_validation::ChunkEndorsement; +use unc_primitives::chunk_validation::ChunkStateWitness; +pub use peer::*; +pub use state_sync::*; + +#[cfg(test)] +pub(crate) mod testonly; +#[cfg(test)] +mod tests; + +mod _proto { + include!(concat!(env!("OUT_DIR"), "/proto/mod.rs")); +} + +pub use _proto::network as proto; + +use crate::network_protocol::proto_conv::trace_context::{ + extract_span_context, inject_trace_context, +}; +use borsh::BorshDeserialize as _; +use unc_async::time; +use unc_crypto::PublicKey; +use unc_crypto::Signature; +use unc_o11y::OpenTelemetrySpanExt; +use unc_primitives::block::{Approval, Block, BlockHeader, GenesisId}; +use unc_primitives::challenge::Challenge; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::combine_hash; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::sharding::{ + ChunkHash, PartialEncodedChunk, PartialEncodedChunkPart, ReceiptProof, ShardChunkHeader, +}; +use unc_primitives::state_sync::{ShardStateSyncResponse, ShardStateSyncResponseV1}; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::AccountId; +use unc_primitives::types::{BlockHeight, ShardId}; +use unc_primitives::validator_signer::ValidatorSigner; +use unc_primitives::views::FinalExecutionOutcomeView; +use protobuf::Message as _; +use std::collections::HashSet; +use std::fmt; +use std::fmt::Debug; +use std::sync::Arc; +use tracing::Span; + +#[derive(PartialEq, Eq, Clone, Debug, Hash)] +pub struct PeerAddr { + pub addr: std::net::SocketAddr, + pub peer_id: PeerId, +} + +impl serde::Serialize for PeerAddr { + fn serialize(&self, s: S) -> Result { + s.serialize_str(&format!("{}@{}", self.peer_id, self.addr)) + } +} + +impl<'a> serde::Deserialize<'a> for PeerAddr { + fn deserialize>(d: D) -> Result { + ::deserialize(d)?.parse().map_err(serde::de::Error::custom) + } +} + +#[derive(thiserror::Error, Debug)] +pub enum ParsePeerAddrError { + #[error("expected @:, got \'{0}\'")] + Format(String), + #[error("PeerId: {0}")] + PeerId(#[source] unc_crypto::ParseKeyError), + #[error("SocketAddr: {0}")] + SocketAddr(#[source] std::net::AddrParseError), +} + +impl std::str::FromStr for PeerAddr { + type Err = ParsePeerAddrError; + fn from_str(s: &str) -> Result { + let parts: Vec<_> = s.split('@').collect(); + if parts.len() != 2 { + return Err(Self::Err::Format(s.to_string())); + } + Ok(PeerAddr { + peer_id: PeerId::new(parts[0].parse().map_err(Self::Err::PeerId)?), + addr: parts[1].parse().map_err(Self::Err::SocketAddr)?, + }) + } +} + +/// AccountData is a piece of global state that a validator +/// signs and broadcasts to the network. It is essentially +/// the data that a validator wants to share with the network. +/// All the nodes in the network are collecting the account data +/// broadcasted by the validators. +/// Since the number of the validators is bounded and their +/// identity is known (and the maximal size of allowed AccountData is bounded) +/// the global state that is distributed in the form of AccountData is bounded +/// as well. +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub struct AccountData { + /// ID of the node that handles the account key (aka validator key). + pub peer_id: PeerId, + /// Proxy nodes that are directly connected to the validator node + /// (this list may include the validator node itself). + /// TIER1 nodes should connect to one of the proxies to sent TIER1 + /// messages to the validator. + pub proxies: Vec, +} + +/// Wrapper of the AccountData which adds metadata to it. +/// It allows to decide which AccountData is newer (authoritative) +/// and discard the older versions. +#[derive(PartialEq, Eq, Debug, Hash)] +pub struct VersionedAccountData { + /// The wrapped account data. + pub data: AccountData, + /// Account key of the validator signing this AccountData. + pub account_key: PublicKey, + /// Version of the AccountData. Each network node stores only + /// the newest version of the data per validator. The newest AccountData + /// is the one with the lexicographically biggest (version,timestamp) tuple: + /// * version is a manually incremented version counter. In case a validator + /// (after a restart/crash/state loss) learns from the network that it has + /// already published AccountData with some version, it can immediately + /// override it by signing and broadcasting AccountData with a higher version. + /// * timestamp is a version tie breaker, introduced only to minimize + /// the risk of version collision (see accounts_data/mod.rs). + pub version: u64, + /// UTC timestamp of when the AccountData has been signed. + pub timestamp: time::Utc, +} + +/// Limit on the size of the serialized AccountData message. +/// It is important to have such a constraint on the serialized proto, +/// because it may contain many unknown fields (which are dropped during parsing). +pub const MAX_ACCOUNT_DATA_SIZE_BYTES: usize = 10000; // 10kB + +/// Limit on the number of shard ids in a single [`SnapshotHostInfo`](state_sync::SnapshotHostInfo) message. +/// The number of shards has to be limited, otherwise a malicious attack could fill the snapshot host cache +/// with millions of shards. +/// The assumption is that no single host is going to track state for more than 512 shards. Keeping state for +/// a shard requires significant resources, so a single peer shouldn't be able to handle too many of them. +/// If this assumption changes in the future, this limit will have to be revisited. +/// +/// Warning: adjusting this constant directly will break upgradeability. A new versioned-node would not interop +/// correctly with an old-versioned node; it could send an excessively large message to an old node. +/// If we ever want to change it we will need to introduce separate send and receive limits, +/// increase the receive limit in one release then increase the send limit in the next. +pub const MAX_SHARDS_PER_SNAPSHOT_HOST_INFO: usize = 512; + +impl VersionedAccountData { + /// Serializes AccountData to proto and signs it using `signer`. + /// Panics if AccountData.account_id doesn't match signer.validator_id(), + /// as this would likely be a bug. + /// Returns an error if the serialized data is too large to be broadcasted. + /// TODO(gprusak): consider separating serialization from signing (so introducing an + /// intermediate SerializedAccountData type) so that sign() then could fail only + /// due to account_id mismatch. Then instead of panicking we could return an error + /// and the caller (who constructs the arguments) would do an unwrap(). This would + /// consistute a cleaner never-panicking interface. + pub fn sign(self, signer: &dyn ValidatorSigner) -> anyhow::Result { + assert_eq!( + self.account_key, + signer.public_key(), + "AccountData.account_key doesn't match the signer's account_key" + ); + let payload = proto::AccountKeyPayload::from(&self).write_to_bytes().unwrap(); + if payload.len() > MAX_ACCOUNT_DATA_SIZE_BYTES { + anyhow::bail!( + "payload size = {}, max is {}", + payload.len(), + MAX_ACCOUNT_DATA_SIZE_BYTES + ); + } + let signature = signer.sign_account_key_payload(&payload); + Ok(SignedAccountData { + account_data: self, + payload: AccountKeySignedPayload { payload, signature }, + }) + } +} + +impl std::ops::Deref for VersionedAccountData { + type Target = AccountData; + fn deref(&self) -> &Self::Target { + &self.data + } +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub struct AccountKeySignedPayload { + payload: Vec, + signature: unc_crypto::Signature, +} + +impl AccountKeySignedPayload { + pub fn len(&self) -> usize { + self.payload.len() + } + pub fn signature(&self) -> &unc_crypto::Signature { + &self.signature + } + pub fn verify(&self, key: &PublicKey) -> Result<(), ()> { + match self.signature.verify(&self.payload, key) { + true => Ok(()), + false => Err(()), + } + } +} + +// TODO(gprusak): this is effectively immutable, and we always pass it around +// in an Arc, so the Arc can be moved inside (except that constructing malformed +// SignedAccountData for tests may get a little tricky). +#[derive(PartialEq, Eq, Debug, Hash)] +pub struct SignedAccountData { + account_data: VersionedAccountData, + // Serialized and signed AccountData. + payload: AccountKeySignedPayload, +} + +impl std::ops::Deref for SignedAccountData { + type Target = VersionedAccountData; + fn deref(&self) -> &Self::Target { + &self.account_data + } +} + +impl SignedAccountData { + pub fn payload(&self) -> &AccountKeySignedPayload { + &self.payload + } +} + +/// Proof that a given peer owns the account key. +/// Included in every handshake sent by a validator node. +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub struct OwnedAccount { + pub(crate) account_key: PublicKey, + pub(crate) peer_id: PeerId, + pub(crate) timestamp: time::Utc, +} + +impl OwnedAccount { + /// Serializes OwnedAccount to proto and signs it using `signer`. + /// Panics if OwnedAccount.account_key doesn't match signer.public_key(), + /// as this would likely be a bug. + pub fn sign(self, signer: &dyn ValidatorSigner) -> SignedOwnedAccount { + assert_eq!( + self.account_key, + signer.public_key(), + "OwnedAccount.account_key doesn't match the signer's account_key" + ); + let payload = proto::AccountKeyPayload::from(&self).write_to_bytes().unwrap(); + let signature = signer.sign_account_key_payload(&payload); + SignedOwnedAccount { + owned_account: self, + payload: AccountKeySignedPayload { payload, signature }, + } + } +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub struct SignedOwnedAccount { + owned_account: OwnedAccount, + // Serialized and signed OwnedAccount. + payload: AccountKeySignedPayload, +} + +impl std::ops::Deref for SignedOwnedAccount { + type Target = OwnedAccount; + fn deref(&self) -> &Self::Target { + &self.owned_account + } +} + +impl SignedOwnedAccount { + pub fn payload(&self) -> &AccountKeySignedPayload { + &self.payload + } +} + +#[derive(PartialEq, Eq, Clone, Debug, Default)] +pub struct RoutingTableUpdate { + pub edges: Vec, + pub accounts: Vec, +} + +impl RoutingTableUpdate { + pub(crate) fn from_edges(edges: Vec) -> Self { + Self { edges, accounts: Vec::new() } + } + + pub fn from_accounts(accounts: Vec) -> Self { + Self { edges: Vec::new(), accounts } + } + + pub(crate) fn new(edges: Vec, accounts: Vec) -> Self { + Self { edges, accounts } + } +} + +/// Denotes a network path to `destination` of length `distance`. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct AdvertisedPeerDistance { + pub destination: PeerId, + pub distance: u32, +} + +/// Struct shared by a peer listing the distances it has to other peers +/// in the NEAR network. +/// +/// It includes a collection of signed edges forming a spanning tree +/// which verifiably achieves the advertised routing distances. +/// +/// The distances in the tree may be the same or better than the advertised +/// distances; see routing::graph_v2::tests::inconsistent_peers. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct DistanceVector { + /// PeerId of the node sending the message. + pub root: PeerId, + /// List of distances the root has to other peers in the network. + pub distances: Vec, + /// Spanning tree of signed edges achieving the claimed distances (or better). + pub edges: Vec, +} + +/// Structure representing handshake between peers. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct Handshake { + /// Current protocol version. + pub(crate) protocol_version: u32, + /// Oldest supported protocol version. + pub(crate) oldest_supported_version: u32, + /// Sender's peer id. + pub(crate) sender_peer_id: PeerId, + /// Receiver's peer id. + pub(crate) target_peer_id: PeerId, + /// Sender's listening addr. + pub(crate) sender_listen_port: Option, + /// Peer's chain information. + pub(crate) sender_chain_info: PeerChainInfoV2, + /// Represents new `edge`. Contains only `none` and `Signature` from the sender. + pub(crate) partial_edge_info: PartialEdgeInfo, + /// Account owned by the sender. + pub(crate) owned_account: Option, +} + +#[derive(PartialEq, Eq, Clone, Debug, strum::IntoStaticStr)] +pub enum HandshakeFailureReason { + ProtocolVersionMismatch { version: u32, oldest_supported_version: u32 }, + GenesisMismatch(GenesisId), + InvalidTarget, +} + +/// See SyncAccountsData in network_protocol/network.proto. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct SyncAccountsData { + pub accounts_data: Vec>, + pub requesting_full_sync: bool, + pub incremental: bool, +} + +/// Message sent to request a PeersResponse +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct PeersRequest { + /// Limits the number of peers to send back + pub max_peers: Option, + /// Limits the number of direct peers to send back + pub max_direct_peers: Option, +} + +/// Message sent as a response to PeersRequest +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct PeersResponse { + /// Peers drawn from the PeerStore of the responding node, + /// which includes peers learned transitively from other peers + pub peers: Vec, + /// Peers directly connected to the responding node + pub direct_peers: Vec, +} + +/// Message sent when gracefully disconnecting from the other peer. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct Disconnect { + /// Advises the other peer to remove the connection from storage + /// Used when it is not expected that a reconnect attempt would succeed + pub remove_from_connection_store: bool, +} + +#[derive(PartialEq, Eq, Clone, Debug, strum::IntoStaticStr, strum::EnumVariantNames)] +#[allow(clippy::large_enum_variant)] +pub enum PeerMessage { + Tier1Handshake(Handshake), + Tier2Handshake(Handshake), + HandshakeFailure(PeerInfo, HandshakeFailureReason), + /// When a failed nonce is used by some peer, this message is sent back as evidence. + LastEdge(Edge), + /// Contains accounts and edge information. + SyncRoutingTable(RoutingTableUpdate), + DistanceVector(DistanceVector), + RequestUpdateNonce(PartialEdgeInfo), + + SyncAccountsData(SyncAccountsData), + + PeersRequest(PeersRequest), + PeersResponse(PeersResponse), + + BlockHeadersRequest(Vec), + BlockHeaders(Vec), + + BlockRequest(CryptoHash), + Block(Block), + + Transaction(SignedTransaction), + Routed(Box), + + /// Gracefully disconnect from other peer. + Disconnect(Disconnect), + Challenge(Challenge), + + SyncSnapshotHosts(SyncSnapshotHosts), + StateRequestHeader(ShardId, CryptoHash), + StateRequestPart(ShardId, CryptoHash, u64), + VersionedStateResponse(StateResponseInfo), +} + +impl fmt::Display for PeerMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.msg_variant(), f) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, strum::IntoStaticStr)] +pub enum Encoding { + Borsh, + Proto, +} + +#[derive(thiserror::Error, Debug)] +pub enum ParsePeerMessageError { + #[error("BorshDecode")] + BorshDecode(#[source] std::io::Error), + #[error("BorshConv")] + BorshConv(#[source] borsh_conv::ParsePeerMessageError), + #[error("ProtoDecode")] + ProtoDecode(#[source] protobuf::Error), + #[error("ProtoConv")] + ProtoConv(#[source] proto_conv::ParsePeerMessageError), +} + +impl PeerMessage { + /// Serializes a message in the given encoding. + /// If the encoding is `Proto`, then also attaches current Span's context to the message. + pub(crate) fn serialize(&self, enc: Encoding) -> Vec { + match enc { + Encoding::Borsh => borsh::to_vec(&borsh_::PeerMessage::from(self)).unwrap(), + Encoding::Proto => { + let mut msg = proto::PeerMessage::from(self); + let cx = Span::current().context(); + msg.trace_context = inject_trace_context(&cx); + msg.write_to_bytes().unwrap() + } + } + } + + pub(crate) fn deserialize( + enc: Encoding, + data: &[u8], + ) -> Result { + let span = tracing::trace_span!(target: "network", "deserialize").entered(); + Ok(match enc { + Encoding::Borsh => (&borsh_::PeerMessage::try_from_slice(data) + .map_err(ParsePeerMessageError::BorshDecode)?) + .try_into() + .map_err(ParsePeerMessageError::BorshConv)?, + Encoding::Proto => { + let proto_msg: proto::PeerMessage = proto::PeerMessage::parse_from_bytes(data) + .map_err(ParsePeerMessageError::ProtoDecode)?; + if let Ok(extracted_span_context) = extract_span_context(&proto_msg.trace_context) { + span.clone().or_current().add_link(extracted_span_context); + } + (&proto_msg).try_into().map_err(|err| ParsePeerMessageError::ProtoConv(err))? + } + }) + } + + pub(crate) fn msg_variant(&self) -> &'static str { + match self { + PeerMessage::Routed(routed_msg) => routed_msg.body_variant(), + _ => self.into(), + } + } +} + +// TODO(#1313): Use Box +#[derive( + borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Eq, Clone, strum::IntoStaticStr, +)] +pub enum RoutedMessageBody { + BlockApproval(Approval), + ForwardTx(SignedTransaction), + TxStatusRequest(AccountId, CryptoHash), + TxStatusResponse(FinalExecutionOutcomeView), + /// Not used, but needed for borsh backward compatibility. + _UnusedQueryRequest, + _UnusedQueryResponse, + _UnusedReceiptOutcomeRequest(CryptoHash), + _UnusedReceiptOutcomeResponse, + _UnusedStateRequestHeader, + _UnusedStateRequestPart, + /// StateResponse in not produced since protocol version 58. + /// We can remove the support for it in protocol version 60. + /// It has been obsoleted by VersionedStateResponse which + /// is a superset of StateResponse values. + StateResponse(StateResponseInfoV1), + PartialEncodedChunkRequest(PartialEncodedChunkRequestMsg), + PartialEncodedChunkResponse(PartialEncodedChunkResponseMsg), + _UnusedPartialEncodedChunk, + /// Ping/Pong used for testing networking and routing. + Ping(Ping), + Pong(Pong), + VersionedPartialEncodedChunk(PartialEncodedChunk), + _UnusedVersionedStateResponse, + PartialEncodedChunkForward(PartialEncodedChunkForwardMsg), + ChunkStateWitness(ChunkStateWitness), + ChunkEndorsement(ChunkEndorsement), +} + +impl RoutedMessageBody { + // Return whether this message is important. + // In routing logics, we send important messages multiple times to minimize the risk that they are + // lost + pub fn is_important(&self) -> bool { + match self { + // These messages are important because they are critical for block and chunk production, + // and lost messages cannot be requested again. + RoutedMessageBody::BlockApproval(_) + | RoutedMessageBody::ChunkEndorsement(_) + | RoutedMessageBody::ChunkStateWitness(_) + | RoutedMessageBody::VersionedPartialEncodedChunk(_) => true, + _ => false, + } + } +} + +impl fmt::Debug for RoutedMessageBody { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + RoutedMessageBody::BlockApproval(approval) => write!( + f, + "Approval({}, {}, {:?})", + approval.target_height, approval.account_id, approval.inner + ), + RoutedMessageBody::ForwardTx(tx) => write!(f, "tx {}", tx.get_hash()), + RoutedMessageBody::TxStatusRequest(account_id, hash) => { + write!(f, "TxStatusRequest({}, {})", account_id, hash) + } + RoutedMessageBody::TxStatusResponse(response) => { + write!(f, "TxStatusResponse({})", response.transaction.hash) + } + RoutedMessageBody::_UnusedQueryRequest => write!(f, "QueryRequest"), + RoutedMessageBody::_UnusedQueryResponse => write!(f, "QueryResponse"), + RoutedMessageBody::_UnusedReceiptOutcomeRequest(_) => write!(f, "ReceiptRequest"), + RoutedMessageBody::_UnusedReceiptOutcomeResponse => write!(f, "ReceiptResponse"), + RoutedMessageBody::_UnusedStateRequestHeader => write!(f, "StateRequestHeader"), + RoutedMessageBody::_UnusedStateRequestPart => write!(f, "StateRequestPart"), + RoutedMessageBody::StateResponse(response) => { + write!(f, "StateResponse({}, {})", response.shard_id, response.sync_hash) + } + RoutedMessageBody::PartialEncodedChunkRequest(request) => { + write!(f, "PartialChunkRequest({:?}, {:?})", request.chunk_hash, request.part_ords) + } + RoutedMessageBody::PartialEncodedChunkResponse(response) => write!( + f, + "PartialChunkResponse({:?}, {:?})", + response.chunk_hash, + response.parts.iter().map(|p| p.part_ord).collect::>() + ), + RoutedMessageBody::_UnusedPartialEncodedChunk => write!(f, "PartiaEncodedChunk"), + RoutedMessageBody::VersionedPartialEncodedChunk(_) => { + write!(f, "VersionedPartialEncodedChunk(?)") + } + RoutedMessageBody::PartialEncodedChunkForward(forward) => write!( + f, + "PartialChunkForward({:?}, {:?})", + forward.chunk_hash, + forward.parts.iter().map(|p| p.part_ord).collect::>(), + ), + RoutedMessageBody::Ping(_) => write!(f, "Ping"), + RoutedMessageBody::Pong(_) => write!(f, "Pong"), + RoutedMessageBody::_UnusedVersionedStateResponse => write!(f, "VersionedStateResponse"), + RoutedMessageBody::ChunkStateWitness(_) => write!(f, "ChunkStateWitness"), + RoutedMessageBody::ChunkEndorsement(_) => write!(f, "ChunkEndorsement"), + } + } +} + +/// RoutedMessage represent a package that will travel the network towards a specific peer id. +/// It contains the peer_id and signature from the original sender. Every intermediate peer in the +/// route must verify that this signature is valid otherwise previous sender of this package should +/// be banned. If the final receiver of this package finds that the body is invalid the original +/// sender of the package should be banned instead. +/// If target is hash, it is a message that should be routed back using the same path used to route +/// the request in first place. It is the hash of the request message. +#[derive(borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Eq, Clone, Debug)] +pub struct RoutedMessage { + /// Peer id which is directed this message. + /// If `target` is hash, this a message should be routed back. + pub target: PeerIdOrHash, + /// Original sender of this message + pub author: PeerId, + /// Signature from the author of the message. If this signature is invalid we should ban + /// last sender of this message. If the message is invalid we should ben author of the message. + pub signature: Signature, + /// Time to live for this message. After passing through some hop this number should be + /// decreased by 1. If this number is 0, drop this message. + pub ttl: u8, + /// Message + pub body: RoutedMessageBody, +} + +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct RoutedMessageV2 { + /// Message + pub msg: RoutedMessage, + /// The time the Routed message was created by `author`. + pub created_at: Option, + /// Number of peers this routed message travelled through. + /// Doesn't include the peers that are the source and the destination of the message. + pub num_hops: Option, +} + +impl std::ops::Deref for RoutedMessageV2 { + type Target = RoutedMessage; + + fn deref(&self) -> &Self::Target { + &self.msg + } +} + +impl std::ops::DerefMut for RoutedMessageV2 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.msg + } +} + +#[derive(borsh::BorshSerialize, PartialEq, Eq, Clone, Debug)] +struct RoutedMessageNoSignature<'a> { + target: &'a PeerIdOrHash, + author: &'a PeerId, + body: &'a RoutedMessageBody, +} + +impl RoutedMessage { + pub fn build_hash( + target: &PeerIdOrHash, + source: &PeerId, + body: &RoutedMessageBody, + ) -> CryptoHash { + CryptoHash::hash_borsh(RoutedMessageNoSignature { target, author: source, body }) + } + + pub fn hash(&self) -> CryptoHash { + RoutedMessage::build_hash(&self.target, &self.author, &self.body) + } + + pub fn verify(&self) -> bool { + self.signature.verify(self.hash().as_ref(), self.author.public_key()) + } + + pub fn expect_response(&self) -> bool { + matches!( + self.body, + RoutedMessageBody::Ping(_) + | RoutedMessageBody::TxStatusRequest(_, _) + | RoutedMessageBody::PartialEncodedChunkRequest(_) + ) + } + + /// Return true if ttl is positive after decreasing ttl by one, false otherwise. + pub fn decrease_ttl(&mut self) -> bool { + self.ttl = self.ttl.saturating_sub(1); + self.ttl > 0 + } + + pub fn body_variant(&self) -> &'static str { + (&self.body).into() + } +} + +#[derive(borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Eq, Clone, Debug, Hash)] +pub enum PeerIdOrHash { + PeerId(PeerId), + Hash(CryptoHash), +} + +/// Message for chunk part owners to forward their parts to validators tracking that shard. +/// This reduces the number of requests a node tracking a shard needs to send to obtain enough +/// parts to reconstruct the message (in the best case no such requests are needed). +#[derive(Clone, Debug, Eq, PartialEq, borsh::BorshSerialize, borsh::BorshDeserialize)] +pub struct PartialEncodedChunkForwardMsg { + pub chunk_hash: ChunkHash, + pub inner_header_hash: CryptoHash, + pub merkle_root: CryptoHash, + pub signature: Signature, + pub prev_block_hash: CryptoHash, + pub height_created: BlockHeight, + pub shard_id: ShardId, + pub parts: Vec, +} + +/// Test code that someone become part of our protocol? +#[derive(borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Eq, Clone, Debug, Hash)] +pub struct Ping { + pub nonce: u64, + pub source: PeerId, +} + +/// Test code that someone become part of our protocol? +#[derive(borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Eq, Clone, Debug, Hash)] +pub struct Pong { + pub nonce: u64, + pub source: PeerId, +} + +impl PartialEncodedChunkForwardMsg { + pub fn from_header_and_parts( + header: &ShardChunkHeader, + parts: Vec, + ) -> Self { + Self { + chunk_hash: header.chunk_hash(), + inner_header_hash: header.inner_header_hash(), + merkle_root: header.encoded_merkle_root(), + signature: header.signature().clone(), + prev_block_hash: *header.prev_block_hash(), + height_created: header.height_created(), + shard_id: header.shard_id(), + parts, + } + } + + pub fn is_valid_hash(&self) -> bool { + let correct_hash = combine_hash(&self.inner_header_hash, &self.merkle_root); + ChunkHash(correct_hash) == self.chunk_hash + } +} + +#[derive(Clone, Debug, Eq, PartialEq, borsh::BorshSerialize, borsh::BorshDeserialize)] +pub struct PartialEncodedChunkRequestMsg { + pub chunk_hash: ChunkHash, + pub part_ords: Vec, + pub tracking_shards: HashSet, +} + +#[derive(Clone, Debug, Eq, PartialEq, borsh::BorshSerialize, borsh::BorshDeserialize)] +pub struct PartialEncodedChunkResponseMsg { + pub chunk_hash: ChunkHash, + pub parts: Vec, + pub receipts: Vec, +} + +#[derive(PartialEq, Eq, Clone, Debug, borsh::BorshSerialize, borsh::BorshDeserialize)] +pub struct StateResponseInfoV1 { + pub shard_id: ShardId, + pub sync_hash: CryptoHash, + pub state_response: ShardStateSyncResponseV1, +} + +#[derive(PartialEq, Eq, Clone, Debug, borsh::BorshSerialize, borsh::BorshDeserialize)] +pub struct StateResponseInfoV2 { + pub shard_id: ShardId, + pub sync_hash: CryptoHash, + pub state_response: ShardStateSyncResponse, +} + +#[derive(PartialEq, Eq, Clone, Debug, borsh::BorshSerialize, borsh::BorshDeserialize)] +pub enum StateResponseInfo { + V1(StateResponseInfoV1), + V2(StateResponseInfoV2), +} + +impl StateResponseInfo { + pub fn shard_id(&self) -> ShardId { + match self { + Self::V1(info) => info.shard_id, + Self::V2(info) => info.shard_id, + } + } + + pub fn sync_hash(&self) -> CryptoHash { + match self { + Self::V1(info) => info.sync_hash, + Self::V2(info) => info.sync_hash, + } + } + + pub fn take_state_response(self) -> ShardStateSyncResponse { + match self { + Self::V1(info) => ShardStateSyncResponse::V1(info.state_response), + Self::V2(info) => info.state_response, + } + } +} + +pub(crate) struct RawRoutedMessage { + pub target: PeerIdOrHash, + pub body: RoutedMessageBody, +} + +impl RawRoutedMessage { + /// Add signature to the message. + /// Panics if the target is an AccountId instead of a PeerId. + pub fn sign( + self, + node_key: &unc_crypto::SecretKey, + routed_message_ttl: u8, + now: Option, + ) -> RoutedMessageV2 { + let author = PeerId::new(node_key.public_key()); + let hash = RoutedMessage::build_hash(&self.target, &author, &self.body); + let signature = node_key.sign(hash.as_ref()); + RoutedMessageV2 { + msg: RoutedMessage { + target: self.target, + author, + signature, + ttl: routed_message_ttl, + body: self.body, + }, + created_at: now, + num_hops: Some(0), + } + } +} diff --git a/chain/network/src/network_protocol/network.proto b/chain/network/src/network_protocol/network.proto new file mode 100644 index 000000000..74296a1ec --- /dev/null +++ b/chain/network/src/network_protocol/network.proto @@ -0,0 +1,502 @@ +/// After changing this file, regenerate protobuf code. +/// See build.rs for details. +syntax = "proto3"; +package network; + +import "google/protobuf/timestamp.proto"; + +// Proof that a given peer owns the account key. +// Included in every handshake sent by a validator node. +// Note: sign AccountKeyPayload, rather than OwnedAccount directly. +message OwnedAccount { + PublicKey account_key = 1; // required + // PeerId of the node owning the account_key. + PublicKey peer_id = 2; // required + // Timestamp indicates the date of signing - we do not assume the + // nodes' clocks to be synchronized, but for security if the timestamp + // deviation is too large, the handshake will be rejected. + // TODO(gprusak): an alternative would be a 3-way handshake with a + // random challenge to sign, or even better: just use some standard + // asymetric entryption. + google.protobuf.Timestamp timestamp = 3; // required +} + +// A payload that can be signed with account keys. +// Since account keys are used to sign things in independent contexts, +// we need this common enum to prevent message replay attacks, like this one: +// - messages M1 and M2 of different types happen to have the same serialized representation. +// - an attacker observes M1 signed by A in some context +// - the attacker then sends M2 with A's signature of M1 (which also matches M2, since +// their serialized representations match) to B, effectively impersonating A. +// NOTE: that proto serialization is non-unique, so the message passed around with the signature +// should be in serialized form. +// TODO: move to a separate file, probably in a separate package. +message AccountKeyPayload { + reserved 1; + oneof payload_type { + AccountData account_data = 2; + OwnedAccount owned_account = 3; + } +} + +// Wrapper of borsh-encoded Signature. +// TODO: link to the rust Signature type. +message Signature { + bytes borsh = 1; +} + +message AccountKeySignedPayload { + // protobuf-serialized AccountKeyPayload, required. + // It is passed in serialized form, because the protobuf encoding is non-deterministic. + // In particular encode(decode(payload)) might not match the signature. + bytes payload = 1; + // Signature of the payload, required. + Signature signature = 2; + // TODO: this is a good place to add optional fields: account_id, account_public_key, + // in case the signer of the message is not implied by the payload, or the context. + // Add them if needed. +} +////////////////////////////////////// + +// Wrapper of borsh-encoded PublicKey. +// https://github.com/utnet-org/utility/blob/1a4edefd0116f7d1e222bc96569367a02fe64199/core/crypto/src/signature.rs#L201 +message PublicKey { + bytes borsh = 1; +} + +// Wrapper of borsh-encoded PeerInfo. +// https://github.com/utnet-org/utility/blob/1a4edefd0116f7d1e222bc96569367a02fe64199/chain/network-primitives/src/network_protocol/mod.rs#L30 +message PeerInfo { + bytes borsh = 1; +} + +// sha256 hash of the borsh-encoded NEAR Block. +message CryptoHash { + // sha256 hash (32 bytes) + bytes hash = 1; +} + +// Wrapper of borsh-encoded Edge. +// https://cs.github.com/utnet-org/utility/blob/1a4edefd0116f7d1e222bc96569367a02fe64199/chain/network-primitives/src/network_protocol/edge.rs#L32 +message Edge { + bytes borsh = 1; +} + +// Wrapper of the borsh-encoded PartialEdgeInfo. +// https://github.com/utnet-org/utility/blob/1a4edefd0116f7d1e222bc96569367a02fe64199/chain/network-primitives/src/network_protocol/edge.rs#L11 +message PartialEdgeInfo { + bytes borsh = 1; +} + +// Wrapper of the borsh-encoded AnnounceAccount. +// https://github.com/utnet-org/utility/blob/1a4edefd0116f7d1e222bc96569367a02fe64199/core/primitives/src/network.rs#L86 +message AnnounceAccount { + bytes borsh = 1; +} + +// Wrapper of the borsh-encoded NEAR chain block. +// https://github.com/utnet-org/utility/blob/1a4edefd0116f7d1e222bc96569367a02fe64199/core/primitives/src/block.rs#L77 +message Block { + bytes borsh = 1; +} + +// Wrapper of the borsh-encoded BlockHeader. +// https://github.com/utnet-org/utility/blob/1a4edefd0116f7d1e222bc96569367a02fe64199/core/primitives/src/block_header.rs#L325 +message BlockHeader { + bytes borsh = 1; +} + +// Wrapper of the borsh-encoded StateResponseInfo. +message StateResponseInfo { + bytes borsh = 1; +} + +// Unique identifier of the NEAR chain. +message GenesisId { + // Name of the chain (for example "mainnet"). + string chain_id = 1; + // Hash of the genesis block(?) of the NEAR chain. + CryptoHash hash = 2; +} + +// Basic information about the chain view maintained by a peer. +message PeerChainInfo { + GenesisId genesis_id = 1; + // Height of the highest NEAR chain block known to a peer. + uint64 height = 2; + // Shards of the NEAR chain tracked by the peer. + repeated uint64 tracked_shards = 3; + // Whether the peer is an archival node. + bool archival = 4; +} + +////////////////////////////////////// + +// Handshake is the first message exchanged after establishing a TCP connection. +// If A opened a connection B, then +// 1. A sends Handshake to B. +// 2a. If B accepts the handshake, it sends Handshake to A and connection is established. +// 2b. If B rejects the handshake, it sends HandshakeFailure to A. +// A may retry the Handshake with a different payload. +message Handshake { + // The protocol_version that the sender wants to use for communication. + // Currently NEAR protocol and NEAR network protocol are versioned together + // (it may change in the future), however peers may communicate with the newer version + // of the NEAR network protol, than the NEAR protocol version approved by the quorum of + // the validators. If B doesn't support protocol_version, it sends back HandshakeFailure + // with reason ProtocolVersionMismatch. + uint32 protocol_version = 1; + // Oldest version of the NEAR network protocol that the peer supports. + uint32 oldest_supported_version = 2; + // PeerId of the sender. + PublicKey sender_peer_id = 3; + // PeerId of the receiver that the sender expects. + // In case of mismatch, receiver sends back HandshakeFailure with + // reason InvalidTarget. + PublicKey target_peer_id = 4; + // TCP port on which sender is listening for inbound connections. + uint32 sender_listen_port = 5; + // Basic info about the NEAR chain that the sender belongs to. + // Sender expects receiver to belong to the same chain. + // In case of mismatch, receiver sends back HandshakeFailure with + // reason GenesisMismatch. + PeerChainInfo sender_chain_info = 6; + // Edge (sender,receiver) signed by sender, which once signed by + // receiver may be broadcasted to the network to prove that the + // connection has been established. + // In case receiver accepts the Handshake, it sends back back a Handshake + // containing his signature in this field. + // WARNING: this field contains a signature of (sender_peer_id,target_peer_id,nonce) tuple, + // which currently the only thing that we have as a substitute for a real authentication. + // TODO(gprusak): for TIER1 authentication is way more important than for TIER2, so this + // thing should be replaced with sth better. + PartialEdgeInfo partial_edge_info = 7; + // See description of OwnedAccount. + AccountKeySignedPayload owned_account = 8; // optional + reserved 9; // https://github.com/utnet-org/utility/pull/9191 +} + +// Response to Handshake, in case the Handshake was rejected. +message HandshakeFailure { + enum Reason { + UNKNOWN = 0; + // Peer doesn't support protocol_version indicated in the handshake. + ProtocolVersionMismatch = 1; + // Peer doesn't belong to the chain indicated in the handshake. + GenesisMismatch = 2; + // target_id doesn't match the id of the peer. + InvalidTarget = 3; + } + // Reason for rejecting the Handshake. + Reason reason = 1; + + // Data about the peer. + PeerInfo peer_info = 2; + // GenesisId of the NEAR chain that the peer belongs to. + GenesisId genesis_id = 3; + // Newest NEAR network version supported by the peer. + uint32 version = 4; + // Oldest NEAR network version supported by the peer. + uint32 oldest_supported_version = 5; +} + +// TODO: document it. +message LastEdge { + Edge edge = 1; +} + +message SocketAddr { + // IPv4 (4 bytes) or IPv6 (16 bytes) in network byte order. + bytes ip = 1; + // TCP port (actually uint16, however uint32 is smallest supported protobuf type). + uint32 port = 2; +} + +message PeerAddr { + SocketAddr addr = 1; // required + PublicKey peer_id = 2; // required +} + +message AccountData { + reserved 1,3; + + // PeerId of the node owning the account_key. + // Used to route the message over TIER1. + // TODO(gprusak): it should be possible to add support for routing + // messages to an account_id directly (for TIER1), instead of routing + // to a specific peer_id. Then this field won't be necessary. + // Unless we use it instead of AnnounceAccount. + PublicKey peer_id = 5; // required. + + PublicKey account_key = 6; // required. + + // List of nodes which + // - are trusted by the validator and + // - are connected to the validator directly + // - are willing to proxy traffic to the validator. + // It may include the validator node itself, if it has a public IP. + // If empty, the validator explicitly declares that it has no public IP + // and the TIER2 routing should be used instead (discouraged, might be disallowed in the future). + repeated PeerAddr proxies = 2; + + // Version of the AccountData. A node can override a previous version, + // by broadcasting a never version. + uint64 version = 7; + // Time of creation of this AccountData. + // TODO(gprusak): consider expiring the AccountData based on this field. + google.protobuf.Timestamp timestamp = 4; +} + +// Message sent whenever the sender learns about new connections +// between the peers in the network (I think). +// It provides a view of the whole NEAR network to each peer. +// +// Edges constitute a graph between PeerIds, signed by both of +// the peers. This is one of the first messages sent after Handshake. +// First RoutingTableUpdate contains the whole graph known to peer. +// Afterwards only the graph delta (changed edges) are included. +// +// Accounts provides a mapping AccountId -> PeerId, providing knowledge +// about which NEAR peer controls which NEAR account. +message RoutingTableUpdate { + reserved 3,4; + repeated Edge edges = 1; + // list of known NEAR validator accounts + repeated AnnounceAccount accounts = 2; +} + +// Denotes an available route to `destination` of length `distance` +message AdvertisedPeerDistance { + PublicKey destination = 1; + uint32 distance = 2; +} + +/// Message shared by a peer listing the distances it has to other peers +/// in the NEAR network. +/// +/// It includes a collection of signed edges forming a spanning tree +/// which verifiably achieves the advertised routing distances. +/// +/// The distances in the tree may be the same or better than the advertised +/// distances; see routing::graph_v2::tests::inconsistent_peers. +message DistanceVector { + // PeerId of the node sending the message. + PublicKey root = 1; + // List of distances the root has to other peers in the network. + repeated AdvertisedPeerDistance distances = 2; + // Spanning tree of signed edges achieving the claimed distances (or better). + repeated Edge edges = 3; +} + +// TODO: document it. +message UpdateNonceRequest { + PartialEdgeInfo partial_edge_info = 1; +} + +// Deprecated. Use SyncRoutingTable instead. +message UpdateNonceResponse { + Edge edge = 1; +} + +// SyncAccountData message can represent: +// - incremental sync (incremental = true, requesting_full_sync = false) +// - full sync request (incremental = false, requesting_full_sync = true) +// - full sync response (incremental = false, requesting_full_sync = false) +message SyncAccountsData { + // Data about the (important) accounts, + // which should be broadcasted to the whole network. + // Contains AccountKeyPayload.account_data. + repeated AccountKeySignedPayload accounts_data = 1; + // Indicates whether this message is an incremental sync (true), or + // a full sync (false). Useful for tracking time since the last full sync. + bool incremental = 2; + // Indicates that sender requests a full sync message in return. + // Useful for soliciting a full sync periodically. + bool requesting_full_sync = 3; +} + +// Request to send a list of known healthy peers +// (i.e. considered honest and available by the receiver). +// max_peers limits the number of peers to send back. +// max_direct_peers limits the number of direct peers to send back. +// See PeersResponse below for the response. +message PeersRequest { + optional uint32 max_peers = 1; + optional uint32 max_direct_peers = 2; +} + +// Response to PeersRequest +message PeersResponse { + // Peers drawn from the PeerStore of the responding node, + // which includes peers learned transitively from other peers + repeated PeerInfo peers = 1; + // Peers directly connected to the responding node + repeated PeerInfo direct_peers = 2; +} + +// Request to send back headers of the NEAR chain blocks. +// Receiver finds in block_hashes the first hash of a block it knows about +// and rends back BlockHeadersResponse with block headers following that block. +// At most 512 block headers are returned: +// https://github.com/utnet-org/utility/blob/1a4edefd0116f7d1e222bc96569367a02fe64199/chain/client/src/sync.rs#L38 +// It might happen that the receiver doesn't know some of the hashes in the list +// in the following cases: +// - sender's view of the chain forked from the receiver's view of the chain +// - sender's view of the chain is ahead of receiver's view of the chain. +message BlockHeadersRequest { + repeated CryptoHash block_hashes = 1; +} + +// A collection of headers of the NEAR chain blocks. +message BlockHeadersResponse { + repeated BlockHeader block_headers = 1; +} + +// Request to send back a NEAR chain block with a given hash. +message BlockRequest { + CryptoHash block_hash = 1; +} + +// NEAR chain Block. +// It might be send both as a response to BlockRequest, +// or unsolicitated in case a new Block is being broadcasted. +message BlockResponse { + Block block = 1; +} + +// Wrapper of borsh-encoded SignedTransaction +// https://github.com/utnet-org/utility/blob/1a4edefd0116f7d1e222bc96569367a02fe64199/core/primitives/src/transaction.rs#L218 +message SignedTransaction { + bytes borsh = 1; +} + +// Wrapper of borsh-encoded RoutedMessage +// https://github.com/utnet-org/utility/blob/1a4edefd0116f7d1e222bc96569367a02fe64199/chain/network-primitives/src/network_protocol/mod.rs#L295 +message RoutedMessage { + bytes borsh = 1; + // Timestamp of creating the Routed message by its original author. + google.protobuf.Timestamp created_at = 2; + // Number of peers this routed message travelled through. Doesn't include the peer that created the message. + optional int32 num_hops = 3; +} + +// Disconnect is send by a node before closing a TCP connection. +// There is no guarantee that it will be sent in all circumstances. +message Disconnect { + bool remove_from_connection_store = 1; +} + +// Wrapper of borsh-encoded Challenge +// https://github.com/utnet-org/utility/blob/1a4edefd0116f7d1e222bc96569367a02fe64199/core/primitives/src/challenge.rs#L89 +message Challenge { + bytes borsh = 1; +} + +// Wrapper of borsh-encoded RoutingSyncV2 +// https://github.com/utnet-org/utility/blob/1a4edefd0116f7d1e222bc96569367a02fe64199/chain/network/src/network_protocol.rs#L225 +message RoutingSyncV2 { + bytes borsh = 1; +} + +// Inter-process tracing information. +message TraceContext { + enum SamplingPriority { + UNKNOWN = 0; + AutoReject = 1; + UserReject = 2; + AutoKeep = 3; + UserKeep = 4; + } + // 16 bytes representing TraceId: https://docs.rs/opentelemetry/latest/opentelemetry/trace/struct.TraceId.html + bytes trace_id = 1; + // 8 bytes representing SpanId: https://docs.rs/opentelemetry/latest/opentelemetry/trace/struct.SpanId.html + bytes span_id = 2; + SamplingPriority sampling_priority = 3; +} + +message StateRequestHeader { + uint64 shard_id = 1; + CryptoHash sync_hash = 2; +} + +message StateRequestPart { + uint64 shard_id = 1; + CryptoHash sync_hash = 2; + uint64 part_id = 3; +} + +message StateResponse { + StateResponseInfo state_response_info = 1; +} + +message SnapshotHostInfo { + PublicKey peer_id = 1; + CryptoHash sync_hash = 2; + uint64 epoch_height = 3; + repeated uint64 shards = 4; + Signature signature = 5; +} + +message SyncSnapshotHosts { + // Information about peers in the network hosting state snapshots + repeated SnapshotHostInfo hosts = 1; +} + +// PeerMessage is a wrapper of all message types exchanged between NEAR nodes. +// The wire format of a single message M consists of len(M)+4 bytes: +// : 4 bytes : little endian uint32 +// : N bytes : binary encoded protobuf PeerMessage M +message PeerMessage { + // Leaving 1,2,3 unused allows us to ensure that there will be no collision + // between borsh and protobuf encodings: + // https://docs.google.com/document/d/1gCWmt9O-h_-5JDXIqbKxAaSS3Q9pryB1f9DDY1mMav4/edit + reserved 1,2,3; + // Deprecated fields. + reserved 20,21,22,23,24; + + // Inter-process tracing information. + TraceContext trace_context = 26; + + oneof message_type { + // Handshakes for TIER1 and TIER2 networks are considered separate, + // so that a node binary which doesn't support TIER1 connection won't + // be even able to PARSE the handshake. This way we avoid accidental + // connections, such that one end thinks it is a TIER2 connection and the + // other thinks it is a TIER1 connection. As currently both TIER1 and TIER2 + // connections are handled by the same PeerActor, both fields use the same + // underlying message type. If we ever decide to separate the handshake + // implementations, we can copy the Handshake message type defition and + // make it evolve differently for TIER1 and TIER2. + Handshake tier1_handshake = 27; + Handshake tier2_handshake = 4; + + HandshakeFailure handshake_failure = 5; + LastEdge last_edge = 6; + RoutingTableUpdate sync_routing_table = 7; + DistanceVector distance_vector = 28; + + UpdateNonceRequest update_nonce_request = 8; + UpdateNonceResponse update_nonce_response = 9; + + SyncAccountsData sync_accounts_data = 25; + + PeersRequest peers_request = 10; + PeersResponse peers_response = 11; + + BlockHeadersRequest block_headers_request = 12; + BlockHeadersResponse block_headers_response = 13; + + BlockRequest block_request = 14; + BlockResponse block_response = 15; + + SignedTransaction transaction = 16; + RoutedMessage routed = 17; + Disconnect disconnect = 18; + Challenge challenge = 19; + + StateRequestHeader state_request_header = 29; + StateRequestPart state_request_part = 30; + StateResponse state_response = 31; + SyncSnapshotHosts sync_snapshot_hosts = 32; + } +} diff --git a/chain/network/src/network_protocol/peer.rs b/chain/network/src/network_protocol/peer.rs new file mode 100644 index 000000000..27b486213 --- /dev/null +++ b/chain/network/src/network_protocol/peer.rs @@ -0,0 +1,152 @@ +/// `network_protocol.rs` contains types which are part of network protocol. +/// We need to maintain backward compatibility in network protocol. +/// All changes to this file should be reviewed. +/// +/// TODO: - document all types in this file +use unc_primitives::block::GenesisId; +use unc_primitives::network::PeerId; +use unc_primitives::types::{AccountId, BlockHeight, ShardId}; +use std::fmt; +use std::net::{SocketAddr, ToSocketAddrs}; +use std::str::FromStr; + +/// Peer information. +#[derive(borsh::BorshSerialize, borsh::BorshDeserialize, Clone, Debug, Eq, PartialEq, Hash)] +pub struct PeerInfo { + pub id: PeerId, + pub addr: Option, + pub account_id: Option, +} + +impl PeerInfo { + /// Creates random peer info. + pub fn new(id: PeerId, addr: SocketAddr) -> Self { + PeerInfo { id, addr: Some(addr), account_id: None } + } + + pub fn random() -> Self { + PeerInfo { id: PeerId::random(), addr: None, account_id: None } + } + + pub fn addr_port(&self) -> Option { + self.addr.map(|addr| addr.port()) + } +} + +// Note, `Display` automatically implements `ToString` which must be reciprocal to `FromStr`. +impl fmt::Display for PeerInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.id)?; + if let Some(addr) = &self.addr { + write!(f, "@{}", addr)?; + } + if let Some(account_id) = &self.account_id { + write!(f, "@{}", account_id)?; + } + Ok(()) + } +} + +#[derive(thiserror::Error, Debug)] +pub enum ParsePeerInfoError { + #[error("invalid format: {0}")] + InvalidFormat(String), + #[error("PeerId: {0}")] + PeerId(#[source] unc_crypto::ParseKeyError), +} + +impl FromStr for PeerInfo { + type Err = ParsePeerInfoError; + /// Returns a PeerInfo from string + /// + /// Valid format examples: + /// ed25519:C6HLP37VJN1Wj2irxxZPsVsSya92Rnx12tqK3us5erKV + /// ed25519:C6HLP37VJN1Wj2irxxZPsVsSya92Rnx12tqK3us5erKV@127.0.0.1:24567 + /// ed25519:C6HLP37VJN1Wj2irxxZPsVsSya92Rnx12tqK3us5erKV@test.near + /// ed25519:C6HLP37VJN1Wj2irxxZPsVsSya92Rnx12tqK3us5erKV@127.0.0.1:24567@test.near + /// + /// Hostname can be used instead of IP address, if node trusts DNS server it connects to, for example: + /// ed25519:C6HLP37VJN1Wj2irxxZPsVsSya92Rnx12tqK3us5erKV@localhost:24567@test.near + /// ed25519:C6HLP37VJN1Wj2irxxZPsVsSya92Rnx12tqK3us5erKV@my.own.node.test:24567@test.near + /// + fn from_str(s: &str) -> Result { + let chunks: Vec<&str> = s.split('@').collect(); + let id = match chunks.get(0) { + Some(c) => PeerId::new(c.parse().map_err(Self::Err::PeerId)?), + None => return Err(Self::Err::InvalidFormat(s.to_string())), + }; + let mut i = 1; + let addr = match chunks.get(i).map(|s| s.to_socket_addrs()) { + Some(Ok(mut x)) => { + i += 1; + x.next() + } + _ => None, + }; + let account_id = match chunks.get(i).map(|c| c.parse()) { + Some(Ok(it)) => { + i += 1; + Some(it) + } + _ => None, + }; + if i < chunks.len() { + return Err(Self::Err::InvalidFormat(s.to_string())); + } + Ok(PeerInfo { id, addr, account_id }) + } +} + +/// Peer chain information. +#[derive(borsh::BorshSerialize, borsh::BorshDeserialize, Clone, Debug, Eq, PartialEq, Default)] +pub struct PeerChainInfoV2 { + /// Chain Id and hash of genesis block. + pub genesis_id: GenesisId, + /// Last known chain height of the peer. + pub height: BlockHeight, + /// Shards that the peer is tracking. + pub tracked_shards: Vec, + /// Denote if a node is running in archival mode or not. + pub archival: bool, +} + +#[cfg(test)] +mod test { + use std::net::IpAddr; + use std::net::SocketAddr; + use std::net::{Ipv4Addr, Ipv6Addr}; + + #[test] + /// TODO this test might require an improvement (probably by mocking the DNS resolution) + fn test_from_str() { + use crate::network_protocol::PeerInfo; + use std::str::FromStr; + + let socket_v4 = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 1337); + let socket_v6 = SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 1337); + + let mut peer_test = PeerInfo::from_str( + "ed25519:C6HLP37VJN1Wj2irxxZPsVsSya92Rnx12tqK3us5erKV@localhost:1337@account.near", + ) + .unwrap(); + assert!(peer_test.addr.unwrap() == socket_v4 || peer_test.addr.unwrap() == socket_v6); + + peer_test = PeerInfo::from_str( + "ed25519:C6HLP37VJN1Wj2irxxZPsVsSya92Rnx12tqK3us5erKV@localhost:1337", + ) + .unwrap(); + assert!(peer_test.addr.unwrap() == socket_v4 || peer_test.addr.unwrap() == socket_v6); + + peer_test = PeerInfo::from_str( + "ed25519:C6HLP37VJN1Wj2irxxZPsVsSya92Rnx12tqK3us5erKV@127.0.0.1:1337@account.near", + ) + .unwrap(); + assert!(peer_test.addr.unwrap() == socket_v4 || peer_test.addr.unwrap() == socket_v6); + + peer_test = PeerInfo::from_str( + "ed25519:C6HLP37VJN1Wj2irxxZPsVsSya92Rnx12tqK3us5erKV@127.0.0.1:1337", + ) + .unwrap(); + assert!(peer_test.addr.unwrap() == socket_v4 || peer_test.addr.unwrap() == socket_v6); + } +} diff --git a/chain/network/src/network_protocol/proto_conv/account_key.rs b/chain/network/src/network_protocol/proto_conv/account_key.rs new file mode 100644 index 000000000..7f170abc8 --- /dev/null +++ b/chain/network/src/network_protocol/proto_conv/account_key.rs @@ -0,0 +1,179 @@ +/// Conversion functions for payloads signable by an account key. +use super::*; + +use crate::network_protocol::proto; +use crate::network_protocol::proto::account_key_payload::Payload_type as ProtoPT; +use crate::network_protocol::{ + AccountData, AccountKeySignedPayload, OwnedAccount, SignedAccountData, SignedOwnedAccount, + VersionedAccountData, +}; +use protobuf::{Message as _, MessageField as MF}; + +#[derive(thiserror::Error, Debug)] +pub enum ParseAccountDataError { + #[error("bad payload type")] + BadPayloadType, + #[error("peer_id: {0}")] + PeerId(ParseRequiredError), + #[error("account_key: {0}")] + AccountKey(ParseRequiredError), + #[error("peers: {0}")] + Peers(ParseVecError), + #[error("timestamp: {0}")] + Timestamp(ParseRequiredError), +} + +// TODO: consider whether to introduce an intermediate AccountKeyPayload enum. +impl From<&VersionedAccountData> for proto::AccountKeyPayload { + fn from(x: &VersionedAccountData) -> Self { + Self { + payload_type: Some(ProtoPT::AccountData(proto::AccountData { + peer_id: MF::some((&x.peer_id).into()), + account_key: MF::some((&x.account_key).into()), + proxies: x.proxies.iter().map(Into::into).collect(), + version: x.version, + timestamp: MF::some(utc_to_proto(&x.timestamp)), + ..Default::default() + })), + ..Self::default() + } + } +} + +impl TryFrom<&proto::AccountKeyPayload> for VersionedAccountData { + type Error = ParseAccountDataError; + fn try_from(x: &proto::AccountKeyPayload) -> Result { + let x = match x.payload_type.as_ref().ok_or(Self::Error::BadPayloadType)? { + ProtoPT::AccountData(a) => a, + _ => return Err(Self::Error::BadPayloadType), + }; + Ok(Self { + data: AccountData { + peer_id: try_from_required(&x.peer_id).map_err(Self::Error::PeerId)?, + proxies: try_from_slice(&x.proxies).map_err(Self::Error::Peers)?, + }, + account_key: try_from_required(&x.account_key).map_err(Self::Error::AccountKey)?, + version: x.version, + timestamp: map_from_required(&x.timestamp, utc_from_proto) + .map_err(Self::Error::Timestamp)?, + }) + } +} + +////////////////////////////////////////// + +#[derive(thiserror::Error, Debug)] +pub enum ParseSignedAccountDataError { + #[error("decode: {0}")] + Decode(protobuf::Error), + #[error("validator: {0}")] + AccountData(ParseAccountDataError), + #[error("signature: {0}")] + Signature(ParseRequiredError), +} + +impl From<&SignedAccountData> for proto::AccountKeySignedPayload { + fn from(x: &SignedAccountData) -> Self { + Self { + payload: (&x.payload.payload).clone(), + signature: MF::some((&x.payload.signature).into()), + ..Self::default() + } + } +} + +impl TryFrom<&proto::AccountKeySignedPayload> for SignedAccountData { + type Error = ParseSignedAccountDataError; + fn try_from(x: &proto::AccountKeySignedPayload) -> Result { + let account_data = + proto::AccountKeyPayload::parse_from_bytes(&x.payload).map_err(Self::Error::Decode)?; + Ok(Self { + account_data: (&account_data).try_into().map_err(Self::Error::AccountData)?, + payload: AccountKeySignedPayload { + payload: x.payload.clone(), + signature: try_from_required(&x.signature).map_err(Self::Error::Signature)?, + }, + }) + } +} + +////////////////////////////////////////// + +#[derive(thiserror::Error, Debug)] +pub enum ParseOwnedAccountError { + #[error("bad payload type")] + BadPayloadType, + #[error("peer_id: {0}")] + PeerId(ParseRequiredError), + #[error("account_key: {0}")] + AccountKey(ParseRequiredError), + #[error("timestamp: {0}")] + Timestamp(ParseRequiredError), +} + +impl From<&OwnedAccount> for proto::AccountKeyPayload { + fn from(x: &OwnedAccount) -> Self { + Self { + payload_type: Some(ProtoPT::OwnedAccount(proto::OwnedAccount { + account_key: MF::some((&x.account_key).into()), + peer_id: MF::some((&x.peer_id).into()), + timestamp: MF::some(utc_to_proto(&x.timestamp)), + ..Default::default() + })), + ..Self::default() + } + } +} + +impl TryFrom<&proto::AccountKeyPayload> for OwnedAccount { + type Error = ParseOwnedAccountError; + fn try_from(x: &proto::AccountKeyPayload) -> Result { + let x = match x.payload_type.as_ref().ok_or(Self::Error::BadPayloadType)? { + ProtoPT::OwnedAccount(a) => a, + _ => return Err(Self::Error::BadPayloadType), + }; + Ok(Self { + account_key: try_from_required(&x.account_key).map_err(Self::Error::AccountKey)?, + peer_id: try_from_required(&x.peer_id).map_err(Self::Error::PeerId)?, + timestamp: map_from_required(&x.timestamp, utc_from_proto) + .map_err(Self::Error::Timestamp)?, + }) + } +} + +////////////////////////////////////////// + +#[derive(thiserror::Error, Debug)] +pub enum ParseSignedOwnedAccountError { + #[error("decode: {0}")] + Decode(protobuf::Error), + #[error("owned_account: {0}")] + OwnedAccount(ParseOwnedAccountError), + #[error("signature: {0}")] + Signature(ParseRequiredError), +} + +impl From<&SignedOwnedAccount> for proto::AccountKeySignedPayload { + fn from(x: &SignedOwnedAccount) -> Self { + Self { + payload: (&x.payload.payload).clone(), + signature: MF::some((&x.payload.signature).into()), + ..Self::default() + } + } +} + +impl TryFrom<&proto::AccountKeySignedPayload> for SignedOwnedAccount { + type Error = ParseSignedOwnedAccountError; + fn try_from(x: &proto::AccountKeySignedPayload) -> Result { + let owned_account = + proto::AccountKeyPayload::parse_from_bytes(&x.payload).map_err(Self::Error::Decode)?; + Ok(Self { + owned_account: (&owned_account).try_into().map_err(Self::Error::OwnedAccount)?, + payload: AccountKeySignedPayload { + payload: x.payload.clone(), + signature: try_from_required(&x.signature).map_err(Self::Error::Signature)?, + }, + }) + } +} diff --git a/chain/network/src/network_protocol/proto_conv/crypto.rs b/chain/network/src/network_protocol/proto_conv/crypto.rs new file mode 100644 index 000000000..41b811b67 --- /dev/null +++ b/chain/network/src/network_protocol/proto_conv/crypto.rs @@ -0,0 +1,72 @@ +/// Conversion functions for messages representing crypto primitives. +use crate::network_protocol::proto; +use borsh::BorshDeserialize as _; +use unc_crypto::PublicKey; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::PeerId; + +////////////////////////////////////////// + +pub type ParseCryptoHashError = Box; + +impl From<&CryptoHash> for proto::CryptoHash { + fn from(x: &CryptoHash) -> Self { + let mut y = Self::new(); + y.hash = x.0.into(); + y + } +} + +impl TryFrom<&proto::CryptoHash> for CryptoHash { + type Error = ParseCryptoHashError; + fn try_from(p: &proto::CryptoHash) -> Result { + CryptoHash::try_from(&p.hash[..]) + } +} + +////////////////////////////////////////// + +pub type ParsePublicKeyError = std::io::Error; + +impl From<&PublicKey> for proto::PublicKey { + fn from(x: &PublicKey) -> Self { + Self { borsh: borsh::to_vec(&x).unwrap(), ..Self::default() } + } +} + +impl TryFrom<&proto::PublicKey> for PublicKey { + type Error = ParsePublicKeyError; + fn try_from(p: &proto::PublicKey) -> Result { + Self::try_from_slice(&p.borsh) + } +} + +impl From<&PeerId> for proto::PublicKey { + fn from(x: &PeerId) -> Self { + x.public_key().into() + } +} + +impl TryFrom<&proto::PublicKey> for PeerId { + type Error = ParsePublicKeyError; + fn try_from(p: &proto::PublicKey) -> Result { + Ok(PeerId::new(PublicKey::try_from(p)?)) + } +} + +////////////////////////////////////////// + +pub type ParseSignatureError = std::io::Error; + +impl From<&unc_crypto::Signature> for proto::Signature { + fn from(x: &unc_crypto::Signature) -> Self { + Self { borsh: borsh::to_vec(&x).unwrap(), ..Self::default() } + } +} + +impl TryFrom<&proto::Signature> for unc_crypto::Signature { + type Error = ParseSignatureError; + fn try_from(x: &proto::Signature) -> Result { + Self::try_from_slice(&x.borsh) + } +} diff --git a/chain/network/src/network_protocol/proto_conv/handshake.rs b/chain/network/src/network_protocol/proto_conv/handshake.rs new file mode 100644 index 000000000..bee3fbcbb --- /dev/null +++ b/chain/network/src/network_protocol/proto_conv/handshake.rs @@ -0,0 +1,190 @@ +/// Conversion functions for `Handshake` messages. +use super::*; + +use crate::network_protocol::proto; +use crate::network_protocol::{Handshake, HandshakeFailureReason}; +use crate::network_protocol::{PeerChainInfoV2, PeerInfo}; +use unc_primitives::block::GenesisId; +use protobuf::MessageField as MF; + +impl From<&GenesisId> for proto::GenesisId { + fn from(x: &GenesisId) -> Self { + Self { chain_id: x.chain_id.clone(), hash: MF::some((&x.hash).into()), ..Self::default() } + } +} + +#[derive(thiserror::Error, Debug)] +pub enum ParseGenesisIdError { + #[error("hash: {0}")] + Hash(ParseRequiredError), +} + +impl TryFrom<&proto::GenesisId> for GenesisId { + type Error = ParseGenesisIdError; + fn try_from(p: &proto::GenesisId) -> Result { + Ok(Self { + chain_id: p.chain_id.clone(), + hash: try_from_required(&p.hash).map_err(Self::Error::Hash)?, + }) + } +} + +////////////////////////////////////////// + +#[derive(thiserror::Error, Debug)] +pub enum ParsePeerChainInfoV2Error { + #[error("genesis_id {0}")] + GenesisId(ParseRequiredError), +} + +impl From<&PeerChainInfoV2> for proto::PeerChainInfo { + fn from(x: &PeerChainInfoV2) -> Self { + Self { + genesis_id: MF::some((&x.genesis_id).into()), + height: x.height, + tracked_shards: x.tracked_shards.clone(), + archival: x.archival, + ..Self::default() + } + } +} + +impl TryFrom<&proto::PeerChainInfo> for PeerChainInfoV2 { + type Error = ParsePeerChainInfoV2Error; + fn try_from(p: &proto::PeerChainInfo) -> Result { + Ok(Self { + genesis_id: try_from_required(&p.genesis_id).map_err(Self::Error::GenesisId)?, + height: p.height, + tracked_shards: p.tracked_shards.clone(), + archival: p.archival, + }) + } +} + +////////////////////////////////////////// + +#[derive(thiserror::Error, Debug)] +pub enum ParseHandshakeError { + #[error("sender_peer_id {0}")] + SenderPeerId(ParseRequiredError), + #[error("target_peer_id {0}")] + TargetPeerId(ParseRequiredError), + #[error("sender_listen_port {0}")] + SenderListenPort(std::num::TryFromIntError), + #[error("sender_chain_info {0}")] + SenderChainInfo(ParseRequiredError), + #[error("partial_edge_info {0}")] + PartialEdgeInfo(ParseRequiredError), + #[error("owned_account {0}")] + OwnedAccount(ParseSignedOwnedAccountError), +} + +impl From<&Handshake> for proto::Handshake { + fn from(x: &Handshake) -> Self { + Self { + protocol_version: x.protocol_version, + oldest_supported_version: x.oldest_supported_version, + sender_peer_id: MF::some((&x.sender_peer_id).into()), + target_peer_id: MF::some((&x.target_peer_id).into()), + sender_listen_port: x.sender_listen_port.unwrap_or(0).into(), + sender_chain_info: MF::some((&x.sender_chain_info).into()), + partial_edge_info: MF::some((&x.partial_edge_info).into()), + owned_account: x.owned_account.as_ref().map(Into::into).into(), + ..Self::default() + } + } +} + +impl TryFrom<&proto::Handshake> for Handshake { + type Error = ParseHandshakeError; + fn try_from(p: &proto::Handshake) -> Result { + Ok(Self { + protocol_version: p.protocol_version, + oldest_supported_version: p.oldest_supported_version, + sender_peer_id: try_from_required(&p.sender_peer_id) + .map_err(Self::Error::SenderPeerId)?, + target_peer_id: try_from_required(&p.target_peer_id) + .map_err(Self::Error::TargetPeerId)?, + sender_listen_port: { + let port = + u16::try_from(p.sender_listen_port).map_err(Self::Error::SenderListenPort)?; + if port == 0 { + None + } else { + Some(port) + } + }, + sender_chain_info: try_from_required(&p.sender_chain_info) + .map_err(Self::Error::SenderChainInfo)?, + partial_edge_info: try_from_required(&p.partial_edge_info) + .map_err(Self::Error::PartialEdgeInfo)?, + owned_account: try_from_optional(&p.owned_account) + .map_err(Self::Error::OwnedAccount)?, + }) + } +} + +////////////////////////////////////////// + +impl From<(&PeerInfo, &HandshakeFailureReason)> for proto::HandshakeFailure { + fn from((pi, hfr): (&PeerInfo, &HandshakeFailureReason)) -> Self { + match hfr { + HandshakeFailureReason::ProtocolVersionMismatch { + version, + oldest_supported_version, + } => Self { + peer_info: MF::some(pi.into()), + reason: proto::handshake_failure::Reason::ProtocolVersionMismatch.into(), + version: *version, + oldest_supported_version: *oldest_supported_version, + ..Self::default() + }, + HandshakeFailureReason::GenesisMismatch(genesis_id) => Self { + peer_info: MF::some(pi.into()), + reason: proto::handshake_failure::Reason::GenesisMismatch.into(), + genesis_id: MF::some(genesis_id.into()), + ..Self::default() + }, + HandshakeFailureReason::InvalidTarget => Self { + peer_info: MF::some(pi.into()), + reason: proto::handshake_failure::Reason::InvalidTarget.into(), + ..Self::default() + }, + } + } +} + +#[derive(thiserror::Error, Debug)] +pub enum ParseHandshakeFailureError { + #[error("peer_info: {0}")] + PeerInfo(ParseRequiredError), + #[error("genesis_id: {0}")] + GenesisId(ParseRequiredError), + #[error("reason: unknown")] + UnknownReason, +} + +impl TryFrom<&proto::HandshakeFailure> for (PeerInfo, HandshakeFailureReason) { + type Error = ParseHandshakeFailureError; + fn try_from(x: &proto::HandshakeFailure) -> Result { + let pi = try_from_required(&x.peer_info).map_err(Self::Error::PeerInfo)?; + let hfr = match x.reason.enum_value_or_default() { + proto::handshake_failure::Reason::ProtocolVersionMismatch => { + HandshakeFailureReason::ProtocolVersionMismatch { + version: x.version, + oldest_supported_version: x.oldest_supported_version, + } + } + proto::handshake_failure::Reason::GenesisMismatch => { + HandshakeFailureReason::GenesisMismatch( + try_from_required(&x.genesis_id).map_err(Self::Error::GenesisId)?, + ) + } + proto::handshake_failure::Reason::InvalidTarget => { + HandshakeFailureReason::InvalidTarget + } + proto::handshake_failure::Reason::UNKNOWN => return Err(Self::Error::UnknownReason), + }; + Ok((pi, hfr)) + } +} diff --git a/chain/network/src/network_protocol/proto_conv/mod.rs b/chain/network/src/network_protocol/proto_conv/mod.rs new file mode 100644 index 000000000..941ad8055 --- /dev/null +++ b/chain/network/src/network_protocol/proto_conv/mod.rs @@ -0,0 +1,17 @@ +mod account_key; +mod crypto; +mod handshake; +mod net; +mod peer_message; +mod time; +pub mod trace_context; +/// Contains protobuf <-> network_protocol conversions. +mod util; + +use self::time::*; +use account_key::*; +use crypto::*; +use handshake::*; +use net::*; +pub(crate) use peer_message::*; +use util::*; diff --git a/chain/network/src/network_protocol/proto_conv/net.rs b/chain/network/src/network_protocol/proto_conv/net.rs new file mode 100644 index 000000000..7f997170e --- /dev/null +++ b/chain/network/src/network_protocol/proto_conv/net.rs @@ -0,0 +1,142 @@ +/// Conversion functions for the messages representing network primitives. +use super::*; + +use crate::network_protocol::proto; +use crate::network_protocol::PeerAddr; +use crate::network_protocol::{Edge, PartialEdgeInfo, PeerInfo}; +use borsh::BorshDeserialize as _; +use unc_primitives::network::AnnounceAccount; +use protobuf::MessageField as MF; +use std::net::{IpAddr, SocketAddr}; + +#[derive(thiserror::Error, Debug)] +pub enum ParseSocketAddrError { + #[error("invalid IP")] + InvalidIP, + #[error("invalid port")] + InvalidPort, +} + +impl From<&SocketAddr> for proto::SocketAddr { + fn from(x: &SocketAddr) -> Self { + Self { + ip: match x.ip() { + IpAddr::V4(ip) => ip.octets().to_vec(), + IpAddr::V6(ip) => ip.octets().to_vec(), + }, + port: x.port() as u32, + ..Self::default() + } + } +} + +impl TryFrom<&proto::SocketAddr> for SocketAddr { + type Error = ParseSocketAddrError; + fn try_from(x: &proto::SocketAddr) -> Result { + let ip = match x.ip.len() { + 4 => IpAddr::from(<[u8; 4]>::try_from(&x.ip[..]).unwrap()), + 16 => IpAddr::from(<[u8; 16]>::try_from(&x.ip[..]).unwrap()), + _ => return Err(Self::Error::InvalidIP), + }; + let port = u16::try_from(x.port).map_err(|_| Self::Error::InvalidPort)?; + Ok(SocketAddr::new(ip, port)) + } +} + +//////////////////////////////////////// + +#[derive(thiserror::Error, Debug)] +pub enum ParsePeerAddrError { + #[error("addr: {0}")] + Addr(ParseRequiredError), + #[error("peer_id: {0}")] + PeerId(ParseRequiredError), +} + +impl From<&PeerAddr> for proto::PeerAddr { + fn from(x: &PeerAddr) -> Self { + Self { + addr: MF::some((&x.addr).into()), + peer_id: MF::some((&x.peer_id).into()), + ..Self::default() + } + } +} + +impl TryFrom<&proto::PeerAddr> for PeerAddr { + type Error = ParsePeerAddrError; + fn try_from(x: &proto::PeerAddr) -> Result { + Ok(Self { + addr: try_from_required(&x.addr).map_err(Self::Error::Addr)?, + peer_id: try_from_required(&x.peer_id).map_err(Self::Error::PeerId)?, + }) + } +} + +//////////////////////////////////////// + +impl From<&PeerInfo> for proto::PeerInfo { + fn from(x: &PeerInfo) -> Self { + Self { borsh: borsh::to_vec(&x).unwrap(), ..Self::default() } + } +} + +pub type ParsePeerInfoError = std::io::Error; + +impl TryFrom<&proto::PeerInfo> for PeerInfo { + type Error = ParsePeerInfoError; + fn try_from(x: &proto::PeerInfo) -> Result { + Self::try_from_slice(&x.borsh) + } +} + +//////////////////////////////////////// + +pub type ParsePartialEdgeInfoError = std::io::Error; + +impl From<&PartialEdgeInfo> for proto::PartialEdgeInfo { + fn from(x: &PartialEdgeInfo) -> Self { + Self { borsh: borsh::to_vec(&x).unwrap(), ..Self::default() } + } +} + +impl TryFrom<&proto::PartialEdgeInfo> for PartialEdgeInfo { + type Error = ParsePartialEdgeInfoError; + fn try_from(p: &proto::PartialEdgeInfo) -> Result { + Self::try_from_slice(&p.borsh) + } +} + +//////////////////////////////////////// + +pub type ParseEdgeError = std::io::Error; + +impl From<&Edge> for proto::Edge { + fn from(x: &Edge) -> Self { + Self { borsh: borsh::to_vec(&x).unwrap(), ..Self::default() } + } +} + +impl TryFrom<&proto::Edge> for Edge { + type Error = ParseEdgeError; + fn try_from(x: &proto::Edge) -> Result { + Self::try_from_slice(&x.borsh) + } +} + +//////////////////////////////////////// + +pub type ParseAnnounceAccountError = std::io::Error; + +impl From<&AnnounceAccount> for proto::AnnounceAccount { + fn from(x: &AnnounceAccount) -> Self { + Self { borsh: borsh::to_vec(&x).unwrap(), ..Self::default() } + } +} + +impl TryFrom<&proto::AnnounceAccount> for AnnounceAccount { + type Error = ParseAnnounceAccountError; + fn try_from(x: &proto::AnnounceAccount) -> Result { + Self::try_from_slice(&x.borsh) + } +} diff --git a/chain/network/src/network_protocol/proto_conv/peer_message.rs b/chain/network/src/network_protocol/proto_conv/peer_message.rs new file mode 100644 index 000000000..d10a5a4df --- /dev/null +++ b/chain/network/src/network_protocol/proto_conv/peer_message.rs @@ -0,0 +1,492 @@ +/// Conversion functions for PeerMessage - the top-level message for the NEAR P2P protocol format. +use super::*; + +use crate::network_protocol::proto::peer_message::Message_type as ProtoMT; +use crate::network_protocol::proto::{self}; +use crate::network_protocol::state_sync::{SnapshotHostInfo, SyncSnapshotHosts}; +use crate::network_protocol::{ + AdvertisedPeerDistance, Disconnect, DistanceVector, PeerMessage, PeersRequest, PeersResponse, + RoutingTableUpdate, SyncAccountsData, +}; +use crate::network_protocol::{RoutedMessage, RoutedMessageV2}; +use crate::types::StateResponseInfo; +use borsh::BorshDeserialize as _; +use unc_async::time::error::ComponentRange; +use unc_primitives::block::{Block, BlockHeader}; +use unc_primitives::challenge::Challenge; +use unc_primitives::transaction::SignedTransaction; +use protobuf::MessageField as MF; +use std::sync::Arc; + +#[derive(thiserror::Error, Debug)] +pub enum ParseRoutingTableUpdateError { + #[error("edges {0}")] + Edges(ParseVecError), + #[error("accounts {0}")] + Accounts(ParseVecError), +} + +impl From<&RoutingTableUpdate> for proto::RoutingTableUpdate { + fn from(x: &RoutingTableUpdate) -> Self { + Self { + edges: x.edges.iter().map(Into::into).collect(), + accounts: x.accounts.iter().map(Into::into).collect(), + ..Default::default() + } + } +} + +impl TryFrom<&proto::RoutingTableUpdate> for RoutingTableUpdate { + type Error = ParseRoutingTableUpdateError; + fn try_from(x: &proto::RoutingTableUpdate) -> Result { + Ok(Self { + edges: try_from_slice(&x.edges).map_err(Self::Error::Edges)?, + accounts: try_from_slice(&x.accounts).map_err(Self::Error::Accounts)?, + }) + } +} + +////////////////////////////////////////// + +#[derive(thiserror::Error, Debug)] +pub enum ParseAdvertisedPeerDistanceError { + #[error("destination {0}")] + Destination(ParseRequiredError), +} + +impl From<&AdvertisedPeerDistance> for proto::AdvertisedPeerDistance { + fn from(x: &AdvertisedPeerDistance) -> Self { + Self { + destination: MF::some((&x.destination).into()), + distance: x.distance, + ..Default::default() + } + } +} + +impl TryFrom<&proto::AdvertisedPeerDistance> for AdvertisedPeerDistance { + type Error = ParseAdvertisedPeerDistanceError; + fn try_from(x: &proto::AdvertisedPeerDistance) -> Result { + Ok(Self { + destination: try_from_required(&x.destination).map_err(Self::Error::Destination)?, + distance: x.distance, + }) + } +} + +////////////////////////////////////////// + +////////////////////////////////////////// + +#[derive(thiserror::Error, Debug)] +pub enum ParseDistanceVectorError { + #[error("root {0}")] + Root(ParseRequiredError), + #[error("distances {0}")] + Distances(ParseVecError), + #[error("edges {0}")] + Edges(ParseVecError), +} + +impl From<&DistanceVector> for proto::DistanceVector { + fn from(x: &DistanceVector) -> Self { + Self { + root: MF::some((&x.root).into()), + distances: x.distances.iter().map(Into::into).collect(), + edges: x.edges.iter().map(Into::into).collect(), + ..Default::default() + } + } +} + +impl TryFrom<&proto::DistanceVector> for DistanceVector { + type Error = ParseDistanceVectorError; + fn try_from(x: &proto::DistanceVector) -> Result { + Ok(Self { + root: try_from_required(&x.root).map_err(Self::Error::Root)?, + distances: try_from_slice(&x.distances).map_err(Self::Error::Distances)?, + edges: try_from_slice(&x.edges).map_err(Self::Error::Edges)?, + }) + } +} + +////////////////////////////////////////// + +impl From<&BlockHeader> for proto::BlockHeader { + fn from(x: &BlockHeader) -> Self { + Self { borsh: borsh::to_vec(&x).unwrap(), ..Default::default() } + } +} + +pub type ParseBlockHeaderError = std::io::Error; + +impl TryFrom<&proto::BlockHeader> for BlockHeader { + type Error = ParseBlockHeaderError; + fn try_from(x: &proto::BlockHeader) -> Result { + Self::try_from_slice(&x.borsh) + } +} + +////////////////////////////////////////// + +impl From<&Block> for proto::Block { + fn from(x: &Block) -> Self { + Self { borsh: borsh::to_vec(&x).unwrap(), ..Default::default() } + } +} + +pub type ParseBlockError = std::io::Error; + +impl TryFrom<&proto::Block> for Block { + type Error = ParseBlockError; + fn try_from(x: &proto::Block) -> Result { + Self::try_from_slice(&x.borsh) + } +} + +////////////////////////////////////////// + +impl From<&StateResponseInfo> for proto::StateResponseInfo { + fn from(x: &StateResponseInfo) -> Self { + Self { borsh: borsh::to_vec(&x).unwrap(), ..Default::default() } + } +} + +pub type ParseStateInfoError = std::io::Error; + +impl TryFrom<&proto::StateResponseInfo> for StateResponseInfo { + type Error = ParseStateInfoError; + fn try_from(x: &proto::StateResponseInfo) -> Result { + Self::try_from_slice(&x.borsh) + } +} + +////////////////////////////////////////// + +#[derive(thiserror::Error, Debug)] +pub enum ParseSnapshotHostInfoError { + #[error("peer_id {0}")] + PeerId(ParseRequiredError), + #[error("sync_hash {0}")] + SyncHash(ParseRequiredError), + #[error("signature {0}")] + Signature(ParseRequiredError), +} + +impl From<&SnapshotHostInfo> for proto::SnapshotHostInfo { + fn from(x: &SnapshotHostInfo) -> Self { + Self { + peer_id: MF::some((&x.peer_id).into()), + sync_hash: MF::some((&x.sync_hash).into()), + epoch_height: x.epoch_height, + shards: x.shards.clone(), + signature: MF::some((&x.signature).into()), + ..Default::default() + } + } +} + +impl TryFrom<&proto::SnapshotHostInfo> for SnapshotHostInfo { + type Error = ParseSnapshotHostInfoError; + fn try_from(x: &proto::SnapshotHostInfo) -> Result { + Ok(Self { + peer_id: try_from_required(&x.peer_id).map_err(Self::Error::PeerId)?, + sync_hash: try_from_required(&x.sync_hash).map_err(Self::Error::SyncHash)?, + epoch_height: x.epoch_height, + shards: x.shards.clone(), + signature: try_from_required(&x.signature).map_err(Self::Error::Signature)?, + }) + } +} + +////////////////////////////////////////// + +#[derive(thiserror::Error, Debug)] +pub enum ParseSyncSnapshotHostsError { + #[error("hosts {0}")] + Hosts(ParseVecError), +} + +impl From<&SyncSnapshotHosts> for proto::SyncSnapshotHosts { + fn from(x: &SyncSnapshotHosts) -> Self { + Self { hosts: x.hosts.iter().map(|d| d.as_ref().into()).collect(), ..Default::default() } + } +} + +impl TryFrom<&proto::SyncSnapshotHosts> for SyncSnapshotHosts { + type Error = ParseSyncSnapshotHostsError; + fn try_from(x: &proto::SyncSnapshotHosts) -> Result { + Ok(Self { + hosts: try_from_slice(&x.hosts) + .map_err(Self::Error::Hosts)? + .into_iter() + .map(Arc::new) + .collect(), + }) + } +} + +////////////////////////////////////////// + +impl From<&PeerMessage> for proto::PeerMessage { + fn from(x: &PeerMessage) -> Self { + Self { + message_type: Some(match x { + PeerMessage::Tier1Handshake(h) => ProtoMT::Tier1Handshake(h.into()), + PeerMessage::Tier2Handshake(h) => ProtoMT::Tier2Handshake(h.into()), + PeerMessage::HandshakeFailure(pi, hfr) => { + ProtoMT::HandshakeFailure((pi, hfr).into()) + } + PeerMessage::LastEdge(e) => ProtoMT::LastEdge(proto::LastEdge { + edge: MF::some(e.into()), + ..Default::default() + }), + PeerMessage::SyncRoutingTable(rtu) => ProtoMT::SyncRoutingTable(rtu.into()), + PeerMessage::DistanceVector(spt) => ProtoMT::DistanceVector(spt.into()), + PeerMessage::RequestUpdateNonce(pei) => { + ProtoMT::UpdateNonceRequest(proto::UpdateNonceRequest { + partial_edge_info: MF::some(pei.into()), + ..Default::default() + }) + } + PeerMessage::SyncAccountsData(msg) => { + ProtoMT::SyncAccountsData(proto::SyncAccountsData { + accounts_data: msg + .accounts_data + .iter() + .map(|d| d.as_ref().into()) + .collect(), + incremental: msg.incremental, + requesting_full_sync: msg.requesting_full_sync, + ..Default::default() + }) + } + PeerMessage::PeersRequest(pr) => ProtoMT::PeersRequest(proto::PeersRequest { + max_peers: pr.max_peers, + max_direct_peers: pr.max_direct_peers, + ..Default::default() + }), + PeerMessage::PeersResponse(pr) => ProtoMT::PeersResponse(proto::PeersResponse { + peers: pr.peers.iter().map(Into::into).collect(), + direct_peers: pr.direct_peers.iter().map(Into::into).collect(), + ..Default::default() + }), + PeerMessage::BlockHeadersRequest(bhs) => { + ProtoMT::BlockHeadersRequest(proto::BlockHeadersRequest { + block_hashes: bhs.iter().map(Into::into).collect(), + ..Default::default() + }) + } + PeerMessage::BlockHeaders(bhs) => { + ProtoMT::BlockHeadersResponse(proto::BlockHeadersResponse { + block_headers: bhs.iter().map(Into::into).collect(), + ..Default::default() + }) + } + PeerMessage::BlockRequest(bh) => ProtoMT::BlockRequest(proto::BlockRequest { + block_hash: MF::some(bh.into()), + ..Default::default() + }), + PeerMessage::Block(b) => ProtoMT::BlockResponse(proto::BlockResponse { + block: MF::some(b.into()), + ..Default::default() + }), + PeerMessage::Transaction(t) => ProtoMT::Transaction(proto::SignedTransaction { + borsh: borsh::to_vec(&t).unwrap(), + ..Default::default() + }), + PeerMessage::Routed(r) => ProtoMT::Routed(proto::RoutedMessage { + borsh: borsh::to_vec(&r.msg).unwrap(), + created_at: MF::from_option(r.created_at.as_ref().map(utc_to_proto)), + num_hops: r.num_hops, + ..Default::default() + }), + PeerMessage::Disconnect(r) => ProtoMT::Disconnect(proto::Disconnect { + remove_from_connection_store: r.remove_from_connection_store, + ..Default::default() + }), + PeerMessage::Challenge(r) => ProtoMT::Challenge(proto::Challenge { + borsh: borsh::to_vec(&r).unwrap(), + ..Default::default() + }), + PeerMessage::SyncSnapshotHosts(ssh) => ProtoMT::SyncSnapshotHosts(ssh.into()), + PeerMessage::StateRequestHeader(shard_id, sync_hash) => { + ProtoMT::StateRequestHeader(proto::StateRequestHeader { + shard_id: *shard_id, + sync_hash: MF::some(sync_hash.into()), + ..Default::default() + }) + } + PeerMessage::StateRequestPart(shard_id, sync_hash, part_id) => { + ProtoMT::StateRequestPart(proto::StateRequestPart { + shard_id: *shard_id, + sync_hash: MF::some(sync_hash.into()), + part_id: *part_id, + ..Default::default() + }) + } + PeerMessage::VersionedStateResponse(sri) => { + ProtoMT::StateResponse(proto::StateResponse { + state_response_info: MF::some(sri.into()), + ..Default::default() + }) + } + }), + ..Default::default() + } + } +} + +pub type ParsePeersRequestError = std::io::Error; +pub type ParseTransactionError = std::io::Error; +pub type ParseRoutedError = std::io::Error; +pub type ParseChallengeError = std::io::Error; + +#[derive(thiserror::Error, Debug)] +pub enum ParsePeerMessageError { + #[error("empty message")] + Empty, + #[error("handshake: {0}")] + Handshake(ParseHandshakeError), + #[error("handshake_failure: {0}")] + HandshakeFailure(ParseHandshakeFailureError), + #[error("last_edge: {0}")] + LastEdge(ParseRequiredError), + #[error("sync_routing_table: {0}")] + SyncRoutingTable(ParseRoutingTableUpdateError), + #[error("shortest_path_tree: {0}")] + DistanceVector(ParseDistanceVectorError), + #[error("update_nonce_requrest: {0}")] + UpdateNonceRequest(ParseRequiredError), + #[error("update_nonce_response: {0}")] + UpdateNonceResponse(ParseRequiredError), + #[error("peers_request: {0}")] + PeersRequest(ParsePeersRequestError), + #[error("peers_response: {0}")] + PeersResponse(ParseVecError), + #[error("block_headers_request: {0}")] + BlockHeadersRequest(ParseVecError), + #[error("block_headers_response: {0}")] + BlockHeadersResponse(ParseVecError), + #[error("block_request: {0}")] + BlockRequest(ParseRequiredError), + #[error("block_response: {0}")] + BlockResponse(ParseRequiredError), + #[error("transaction: {0}")] + Transaction(ParseTransactionError), + #[error("routed: {0}")] + Routed(ParseRoutedError), + #[error("challenge: {0}")] + Challenge(ParseChallengeError), + #[error("routed_created_at: {0}")] + RoutedCreatedAtTimestamp(ComponentRange), + #[error("sync_accounts_data: {0}")] + SyncAccountsData(ParseVecError), + #[error("state_response: {0}")] + StateResponse(ParseRequiredError), + #[error("sync_snapshot_hosts: {0}")] + SyncSnapshotHosts(ParseSyncSnapshotHostsError), +} + +impl TryFrom<&proto::PeerMessage> for PeerMessage { + type Error = ParsePeerMessageError; + fn try_from(x: &proto::PeerMessage) -> Result { + Ok(match x.message_type.as_ref().ok_or(Self::Error::Empty)? { + ProtoMT::Tier1Handshake(h) => { + PeerMessage::Tier1Handshake(h.try_into().map_err(Self::Error::Handshake)?) + } + ProtoMT::Tier2Handshake(h) => { + PeerMessage::Tier2Handshake(h.try_into().map_err(Self::Error::Handshake)?) + } + ProtoMT::HandshakeFailure(hf) => { + let (pi, hfr) = hf.try_into().map_err(Self::Error::HandshakeFailure)?; + PeerMessage::HandshakeFailure(pi, hfr) + } + ProtoMT::LastEdge(le) => { + PeerMessage::LastEdge(try_from_required(&le.edge).map_err(Self::Error::LastEdge)?) + } + ProtoMT::SyncRoutingTable(rtu) => PeerMessage::SyncRoutingTable( + rtu.try_into().map_err(Self::Error::SyncRoutingTable)?, + ), + ProtoMT::DistanceVector(spt) => { + PeerMessage::DistanceVector(spt.try_into().map_err(Self::Error::DistanceVector)?) + } + ProtoMT::UpdateNonceRequest(unr) => PeerMessage::RequestUpdateNonce( + try_from_required(&unr.partial_edge_info) + .map_err(Self::Error::UpdateNonceRequest)?, + ), + ProtoMT::UpdateNonceResponse(unr) => { + PeerMessage::SyncRoutingTable(RoutingTableUpdate { + edges: vec![ + try_from_required(&unr.edge).map_err(Self::Error::UpdateNonceResponse)? + ], + accounts: vec![], + }) + } + ProtoMT::SyncAccountsData(msg) => PeerMessage::SyncAccountsData(SyncAccountsData { + accounts_data: try_from_slice(&msg.accounts_data) + .map_err(Self::Error::SyncAccountsData)? + .into_iter() + .map(Arc::new) + .collect(), + incremental: msg.incremental, + requesting_full_sync: msg.requesting_full_sync, + }), + ProtoMT::PeersRequest(pr) => PeerMessage::PeersRequest(PeersRequest { + max_peers: pr.max_peers, + max_direct_peers: pr.max_direct_peers, + }), + ProtoMT::PeersResponse(pr) => PeerMessage::PeersResponse(PeersResponse { + peers: try_from_slice(&pr.peers).map_err(Self::Error::PeersResponse)?, + direct_peers: try_from_slice(&pr.direct_peers) + .map_err(Self::Error::PeersResponse)?, + }), + ProtoMT::BlockHeadersRequest(bhr) => PeerMessage::BlockHeadersRequest( + try_from_slice(&bhr.block_hashes).map_err(Self::Error::BlockHeadersRequest)?, + ), + ProtoMT::BlockHeadersResponse(bhr) => PeerMessage::BlockHeaders( + try_from_slice(&bhr.block_headers).map_err(Self::Error::BlockHeadersResponse)?, + ), + ProtoMT::BlockRequest(br) => PeerMessage::BlockRequest( + try_from_required(&br.block_hash).map_err(Self::Error::BlockRequest)?, + ), + ProtoMT::BlockResponse(br) => PeerMessage::Block( + try_from_required(&br.block).map_err(Self::Error::BlockResponse)?, + ), + ProtoMT::Transaction(t) => PeerMessage::Transaction( + SignedTransaction::try_from_slice(&t.borsh).map_err(Self::Error::Transaction)?, + ), + ProtoMT::Routed(r) => PeerMessage::Routed(Box::new(RoutedMessageV2 { + msg: RoutedMessage::try_from_slice(&r.borsh).map_err(Self::Error::Routed)?, + created_at: r + .created_at + .as_ref() + .map(utc_from_proto) + .transpose() + .map_err(Self::Error::RoutedCreatedAtTimestamp)?, + num_hops: r.num_hops, + })), + ProtoMT::Disconnect(d) => PeerMessage::Disconnect(Disconnect { + remove_from_connection_store: d.remove_from_connection_store, + }), + ProtoMT::Challenge(c) => PeerMessage::Challenge( + Challenge::try_from_slice(&c.borsh).map_err(Self::Error::Challenge)?, + ), + ProtoMT::StateRequestHeader(srh) => PeerMessage::StateRequestHeader( + srh.shard_id, + try_from_required(&srh.sync_hash).map_err(Self::Error::BlockRequest)?, + ), + ProtoMT::StateRequestPart(srp) => PeerMessage::StateRequestPart( + srp.shard_id, + try_from_required(&srp.sync_hash).map_err(Self::Error::BlockRequest)?, + srp.part_id, + ), + ProtoMT::StateResponse(t) => PeerMessage::VersionedStateResponse( + try_from_required(&t.state_response_info).map_err(Self::Error::StateResponse)?, + ), + ProtoMT::SyncSnapshotHosts(srh) => PeerMessage::SyncSnapshotHosts( + srh.try_into().map_err(Self::Error::SyncSnapshotHosts)?, + ), + }) + } +} diff --git a/chain/network/src/network_protocol/proto_conv/time.rs b/chain/network/src/network_protocol/proto_conv/time.rs new file mode 100644 index 000000000..7c38565af --- /dev/null +++ b/chain/network/src/network_protocol/proto_conv/time.rs @@ -0,0 +1,18 @@ +/// Conversion functions for the proto timestamp messages. +use unc_async::time; +pub use protobuf::well_known_types::timestamp::Timestamp as ProtoTimestamp; + +pub type ParseTimestampError = time::error::ComponentRange; + +pub fn utc_to_proto(x: &time::Utc) -> ProtoTimestamp { + ProtoTimestamp { + seconds: x.unix_timestamp(), + // x.nanosecond() is guaranteed to be in range [0,10^9). + nanos: x.nanosecond() as i32, + ..Default::default() + } +} + +pub fn utc_from_proto(x: &ProtoTimestamp) -> Result { + time::Utc::from_unix_timestamp_nanos((x.seconds as i128 * 1_000_000_000) + (x.nanos as i128)) +} diff --git a/chain/network/src/network_protocol/proto_conv/trace_context.rs b/chain/network/src/network_protocol/proto_conv/trace_context.rs new file mode 100644 index 000000000..76762fa3e --- /dev/null +++ b/chain/network/src/network_protocol/proto_conv/trace_context.rs @@ -0,0 +1,77 @@ +use crate::network_protocol::proto::trace_context::SamplingPriority; +use crate::network_protocol::proto::TraceContext; +use opentelemetry::trace::{SpanContext, SpanId, TraceContextExt, TraceFlags, TraceId, TraceState}; +use opentelemetry::Context; +use protobuf::{EnumOrUnknown, MessageField}; + +/// Lowest available value. +/// 0x01 is reserved for `SAMPLED`: https://docs.rs/opentelemetry/latest/opentelemetry/trace/struct.TraceFlags.html#associatedconstant.SAMPLED +const TRACE_FLAG_DEFERRED: TraceFlags = TraceFlags::new(0x02); + +#[derive(Debug, thiserror::Error)] +pub(crate) enum ExtractError { + #[error("Malformed or invalid TraceId")] + TraceId, + #[error("Malformed or invalid SpanId")] + SpanId, + #[error("Missing trace_id or span_id")] + Empty, +} + +/// Extracts a `SpanContext` from a potentially empty `TraceContext`. +pub(crate) fn extract_span_context( + trace_context: &MessageField, +) -> Result { + if trace_context.is_some() { + let trace_id = extract_trace_id(&trace_context.trace_id)?; + // If we have a trace_id but can't get the parent span, we default it to invalid instead of completely erroring + // out so that the rest of the spans aren't completely lost. + let span_id = extract_span_id(&trace_context.span_id).unwrap_or(SpanId::INVALID); + let sampled = match trace_context.sampling_priority.enum_value() { + Ok(SamplingPriority::UserReject) | Ok(SamplingPriority::AutoReject) => { + TraceFlags::default() + } + Ok(SamplingPriority::UserKeep) | Ok(SamplingPriority::AutoKeep) => TraceFlags::SAMPLED, + // Treat the sampling as DEFERRED instead of erring on extracting the span context + Ok(SamplingPriority::UNKNOWN) | Err(_) => TRACE_FLAG_DEFERRED, + }; + let trace_state = TraceState::default(); + Ok(SpanContext::new(trace_id, span_id, sampled, true, trace_state)) + } else { + Err(ExtractError::Empty) + } +} + +/// Populates `TraceContext` representing the current span. +/// Returns `None` if no current span is available. +pub(crate) fn inject_trace_context(cx: &Context) -> MessageField { + let span = cx.span(); + let span_context = span.span_context(); + if span_context.is_valid() { + let mut trace_context = TraceContext::new(); + + // Uses `u128::to_be_bytes()` internally. + trace_context.trace_id = span_context.trace_id().to_bytes().to_vec(); + // Uses `u64::to_be_bytes()` internally. + trace_context.span_id = span_context.span_id().to_bytes().to_vec(); + + if span_context.trace_flags() & TRACE_FLAG_DEFERRED != TRACE_FLAG_DEFERRED { + let sampling_priority = if span_context.is_sampled() { + SamplingPriority::AutoKeep + } else { + SamplingPriority::AutoReject + }; + trace_context.sampling_priority = EnumOrUnknown::new(sampling_priority); + } + MessageField::some(trace_context) + } else { + MessageField::none() + } +} +fn extract_trace_id(trace_id: &[u8]) -> Result { + Ok(TraceId::from_bytes(trace_id.try_into().map_err(|_| ExtractError::TraceId)?)) +} + +fn extract_span_id(span_id: &[u8]) -> Result { + Ok(SpanId::from_bytes(span_id.try_into().map_err(|_| ExtractError::SpanId)?)) +} diff --git a/chain/network/src/network_protocol/proto_conv/util.rs b/chain/network/src/network_protocol/proto_conv/util.rs new file mode 100644 index 000000000..186db2292 --- /dev/null +++ b/chain/network/src/network_protocol/proto_conv/util.rs @@ -0,0 +1,45 @@ +/// Proto conversion utilities. +use protobuf::MessageField as MF; + +#[derive(thiserror::Error, Debug)] +#[error("[{idx}]: {source}")] +pub struct ParseVecError { + idx: usize, + #[source] + source: E, +} + +pub fn try_from_slice<'a, X, Y: TryFrom<&'a X>>( + xs: &'a [X], +) -> Result, ParseVecError> { + let mut ys = vec![]; + for (idx, x) in xs.iter().enumerate() { + ys.push(x.try_into().map_err(|source| ParseVecError { idx, source })?); + } + Ok(ys) +} + +#[derive(thiserror::Error, Debug)] +pub enum ParseRequiredError { + #[error("missing, while required")] + Missing, + #[error(transparent)] + Other(E), +} + +pub fn try_from_optional<'a, X, Y: TryFrom<&'a X>>(x: &'a MF) -> Result, Y::Error> { + x.as_ref().map(|x| x.try_into()).transpose() +} + +pub fn try_from_required<'a, X, Y: TryFrom<&'a X>>( + x: &'a MF, +) -> Result> { + x.as_ref().ok_or(ParseRequiredError::Missing)?.try_into().map_err(ParseRequiredError::Other) +} + +pub fn map_from_required<'a, X, Y, E>( + x: &'a MF, + f: impl FnOnce(&'a X) -> Result, +) -> Result> { + f(x.as_ref().ok_or(ParseRequiredError::Missing)?).map_err(ParseRequiredError::Other) +} diff --git a/chain/network/src/network_protocol/state_sync.rs b/chain/network/src/network_protocol/state_sync.rs new file mode 100644 index 000000000..ee6ca9436 --- /dev/null +++ b/chain/network/src/network_protocol/state_sync.rs @@ -0,0 +1,91 @@ +use super::MAX_SHARDS_PER_SNAPSHOT_HOST_INFO; +use crate::network_protocol::Arc; +use unc_crypto::SecretKey; +use unc_crypto::Signature; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::PeerId; +use unc_primitives::types::EpochHeight; +use unc_primitives::types::ShardId; + +// TODO(saketh): Consider moving other types related to state sync into this file +// e.g. StateResponseInfo + +/// Specifies information about a state snapshot hosted by a network peer. +/// +/// A signature is included so that we know it was really published by that peer. +/// +#[derive(Clone, Debug, Eq, PartialEq, Hash, borsh::BorshSerialize, borsh::BorshDeserialize)] +pub struct SnapshotHostInfo { + /// Id of the node serving the snapshot + pub peer_id: PeerId, + /// Hash of the snapshot's state root + pub sync_hash: CryptoHash, + /// Ordinal of the epoch of the state root + pub epoch_height: EpochHeight, + /// List of shards included in the snapshot. + pub shards: Vec, + /// Signature on (sync_hash, epoch_height, shards) + pub signature: Signature, +} + +impl SnapshotHostInfo { + fn build_hash( + sync_hash: &CryptoHash, + epoch_height: &EpochHeight, + shards: &Vec, + ) -> CryptoHash { + CryptoHash::hash_borsh((sync_hash, epoch_height, shards)) + } + + pub(crate) fn new( + peer_id: PeerId, + sync_hash: CryptoHash, + epoch_height: EpochHeight, + shards: Vec, + secret_key: &SecretKey, + ) -> Self { + #[cfg(not(test))] + assert_eq!(&secret_key.public_key(), peer_id.public_key()); + let hash = Self::build_hash(&sync_hash, &epoch_height, &shards); + let signature = secret_key.sign(hash.as_ref()); + Self { peer_id, sync_hash, epoch_height, shards, signature } + } + + pub(crate) fn hash(&self) -> CryptoHash { + Self::build_hash(&self.sync_hash, &self.epoch_height, &self.shards) + } + + pub(crate) fn verify(&self) -> Result<(), SnapshotHostInfoVerificationError> { + // Number of shards must be limited, otherwise it'd be possible to create malicious + // messages with millions of shard ids. + if self.shards.len() > MAX_SHARDS_PER_SNAPSHOT_HOST_INFO { + return Err(SnapshotHostInfoVerificationError::TooManyShards(self.shards.len())); + } + + if !self.signature.verify(self.hash().as_ref(), self.peer_id.public_key()) { + return Err(SnapshotHostInfoVerificationError::InvalidSignature); + } + + Ok(()) + } +} + +// TODO(saketh): Think about whether to add some kind of incremental sync +// vs. full sync behavior here (similar to what we have for SyncAccountsData). +// It doesn't seem necessary, but I don't fully understand why we need it for +// SyncAccountsData either so it's worth revisiting. +#[derive(Clone, Debug, Eq, PartialEq, Hash, borsh::BorshSerialize, borsh::BorshDeserialize)] +pub struct SyncSnapshotHosts { + pub hosts: Vec>, +} + +#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)] +pub enum SnapshotHostInfoVerificationError { + #[error("SnapshotHostInfo is signed with an invalid signature")] + InvalidSignature, + #[error( + "SnapshotHostInfo contains more shards than allowed: {0} > {} (MAX_SHARDS_PER_SNAPSHOT_HOST_INFO)", + MAX_SHARDS_PER_SNAPSHOT_HOST_INFO + )] + TooManyShards(usize), +} diff --git a/chain/network/src/network_protocol/testonly.rs b/chain/network/src/network_protocol/testonly.rs new file mode 100644 index 000000000..2533efb58 --- /dev/null +++ b/chain/network/src/network_protocol/testonly.rs @@ -0,0 +1,420 @@ +use super::*; + +use crate::config; +use crate::network_protocol::{ + Edge, PartialEdgeInfo, PeerInfo, RawRoutedMessage, RoutedMessageBody, +}; +use crate::tcp; +use crate::types::{AccountKeys, ChainInfo, Handshake, RoutingTableUpdate}; +use unc_async::time; +use unc_crypto::{InMemorySigner, KeyType, SecretKey}; +use unc_primitives::block::{genesis_chunks, Block, BlockHeader, GenesisId}; +use unc_primitives::challenge::{BlockDoubleSign, Challenge, ChallengeBody}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::num_rational::Ratio; +use unc_primitives::sharding::{ + ChunkHash, EncodedShardChunk, EncodedShardChunkBody, PartialEncodedChunkPart, + ReedSolomonWrapper, ShardChunk, +}; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{AccountId, BlockHeight, EpochId, StateRoot}; +use unc_primitives::validator_signer::{InMemoryValidatorSigner, ValidatorSigner}; +use unc_primitives::version; +use rand::distributions::Standard; +use rand::Rng; +use std::collections::HashMap; +use std::net; +use std::sync::Arc; + +pub fn make_genesis_block(_clock: &time::Clock, chunks: Vec) -> Block { + Block::genesis( + version::PROTOCOL_VERSION, + chunks.into_iter().map(|c| c.take_header()).collect(), + // TODO: this should be clock.now(), but Block::genesis has to be migrated + // from chrono to time first. + chrono::Utc::now(), + 0, // height + 1000, // initial_gas_price + 1000, // initial_total_supply + CryptoHash::default(), // next_bp_hash (next block producers' hash) + ) +} + +pub fn make_block( + _clock: &time::Clock, + signer: &dyn ValidatorSigner, + prev: &Block, + chunks: Vec, +) -> Block { + Block::produce( + version::PROTOCOL_VERSION, // this_epoch_protocol_version + version::PROTOCOL_VERSION, // next_epoch_protocol_version + prev.header(), // prev + prev.header().height() + 5, // height + prev.header().block_ordinal() + 1, // block_ordinal + chunks.into_iter().map(|c| c.take_header()).collect(), // chunks + EpochId::default(), // epoch_id + EpochId::default(), // next_epoch_id + None, // epoch_sync_data_hash + vec![], // approvals + Ratio::from_integer(0), // gas_price_adjustment_rate + 0, // min_gas_price + 0, // max_gas_price + Some(0), // minted_amount + vec![], // challenges_result + vec![], // challenges + signer, + CryptoHash::default(), // next_bp_hash + CryptoHash::default(), // block_merkle_root + // TODO: migrate to clock.now() + Some(chrono::Utc::now()), // timestamp_override + ) +} + +pub fn make_account_id(rng: &mut R) -> AccountId { + format!("account{}", rng.gen::()).parse().unwrap() +} + +pub fn make_secret_key(rng: &mut R) -> SecretKey { + SecretKey::from_seed(KeyType::ED25519, &rng.gen::().to_string()) +} + +pub fn make_peer_id(rng: &mut R) -> PeerId { + PeerId::new(make_secret_key(rng).public_key()) +} + +pub fn make_signer(rng: &mut R) -> InMemorySigner { + InMemorySigner::from_secret_key(make_account_id(rng), make_secret_key(rng)) +} + +pub fn make_validator_signer(rng: &mut R) -> InMemoryValidatorSigner { + let account_id = make_account_id(rng); + let seed = rng.gen::().to_string(); + InMemoryValidatorSigner::from_seed(account_id, KeyType::ED25519, &seed) +} + +pub fn make_peer_info(rng: &mut R) -> PeerInfo { + let signer = make_signer(rng); + PeerInfo { + id: PeerId::new(signer.public_key), + addr: Some(make_addr(rng)), + account_id: Some(signer.account_id), + } +} + +pub fn make_announce_account(rng: &mut R) -> AnnounceAccount { + let peer_id = make_peer_id(rng); + let validator_signer = make_validator_signer(rng); + let signature = validator_signer.sign_account_announce( + validator_signer.validator_id(), + &peer_id, + &EpochId::default(), + ); + AnnounceAccount { + account_id: validator_signer.validator_id().clone(), + peer_id: peer_id, + epoch_id: EpochId::default(), + signature, + } +} + +pub fn make_partial_edge(rng: &mut R) -> PartialEdgeInfo { + let a = make_signer(rng); + let b = make_signer(rng); + PartialEdgeInfo::new( + &PeerId::new(a.public_key), + &PeerId::new(b.public_key), + rng.gen(), + &a.secret_key, + ) +} + +pub fn make_edge(a: &SecretKey, b: &SecretKey, nonce: u64) -> Edge { + let (a, b) = if a.public_key() < b.public_key() { (a, b) } else { (b, a) }; + let ap = PeerId::new(a.public_key()); + let bp = PeerId::new(b.public_key()); + let hash = Edge::build_hash(&ap, &bp, nonce); + Edge::new(ap, bp, nonce, a.sign(hash.as_ref()), b.sign(hash.as_ref())) +} + +pub fn make_edge_tombstone(a: &SecretKey, b: &SecretKey) -> Edge { + make_edge(a, b, 1).remove_edge(PeerId::new(a.public_key()), &a) +} + +pub fn make_routing_table(rng: &mut R) -> RoutingTableUpdate { + let signers: Vec<_> = (0..7).map(|_| make_secret_key(rng)).collect(); + RoutingTableUpdate { + accounts: (0..10).map(|_| make_announce_account(rng)).collect(), + edges: { + let mut e = vec![]; + for i in 0..signers.len() { + for j in 0..i { + e.push(make_edge(&signers[i], &signers[j], 1)); + } + } + e + }, + } +} + +pub fn make_signed_transaction(rng: &mut R) -> SignedTransaction { + let sender = make_signer(rng); + let receiver = make_account_id(rng); + SignedTransaction::send_money( + rng.gen(), + sender.account_id.clone(), + receiver, + &sender, + 15, + CryptoHash::default(), + ) +} + +pub fn make_challenge(rng: &mut R) -> Challenge { + Challenge::produce( + ChallengeBody::BlockDoubleSign(BlockDoubleSign { + left_block_header: rng.sample_iter(&Standard).take(65).collect(), + right_block_header: rng.sample_iter(&Standard).take(34).collect(), + }), + &make_validator_signer(rng), + ) +} + +// Based on ShardsManager::prepare_partial_encoded_chunk_response_from_chunk. +// I give no guarantee that it will produce correct data, I'm just approximating +// the real thing, since this functionality is not encapsulated in +// the production code well enough to reuse it in tests. +pub fn make_chunk_parts(chunk: ShardChunk) -> Vec { + let mut rs = ReedSolomonWrapper::new(10, 5); + let (parts, _) = EncodedShardChunk::encode_transaction_receipts( + &mut rs, + chunk.transactions().to_vec(), + &chunk.prev_outgoing_receipts(), + ) + .unwrap(); + let mut content = EncodedShardChunkBody { parts }; + content.reconstruct(&mut rs).unwrap(); + let (_, merkle_paths) = content.get_merkle_hash_and_paths(); + let mut parts = vec![]; + for ord in 0..rs.total_shard_count() { + parts.push(PartialEncodedChunkPart { + part_ord: ord as u64, + part: content.parts[ord].take().unwrap(), + merkle_proof: merkle_paths[ord].clone(), + }); + } + parts +} + +struct ChunkSet { + chunks: HashMap, +} + +impl ChunkSet { + pub fn new() -> Self { + Self { chunks: HashMap::default() } + } + pub fn make(&mut self) -> Vec { + let shard_ids: Vec<_> = (0..4).collect(); + // TODO: these are always genesis chunks. + // Consider making this more realistic. + let chunks = + genesis_chunks(vec![StateRoot::new()], &shard_ids, 1000, 0, version::PROTOCOL_VERSION); + self.chunks.extend(chunks.iter().map(|c| (c.chunk_hash(), c.clone()))); + chunks + } +} + +pub fn make_hash(rng: &mut R) -> CryptoHash { + CryptoHash::hash_bytes(&rng.gen::<[u8; 19]>()) +} + +pub fn make_account_keys(signers: &[InMemoryValidatorSigner]) -> AccountKeys { + let mut account_keys = AccountKeys::new(); + for s in signers { + account_keys.entry(s.validator_id().clone()).or_default().insert(s.public_key()); + } + account_keys +} + +pub struct Chain { + pub genesis_id: GenesisId, + pub blocks: Vec, + pub chunks: HashMap, + pub tier1_accounts: Vec, +} + +impl Chain { + pub fn make(clock: &time::FakeClock, rng: &mut R, block_count: usize) -> Chain { + let mut chunks = ChunkSet::new(); + let mut blocks = vec![]; + blocks.push(make_genesis_block(&clock.clock(), chunks.make())); + let signer = make_validator_signer(rng); + for _ in 1..block_count { + clock.advance(time::Duration::seconds(15)); + blocks.push(make_block(&clock.clock(), &signer, blocks.last().unwrap(), chunks.make())); + } + Chain { + genesis_id: GenesisId { + chain_id: format!("testchain{}", rng.gen::()), + hash: Default::default(), + }, + blocks, + tier1_accounts: (0..10).map(|_| make_validator_signer(rng)).collect(), + chunks: chunks.chunks, + } + } + + pub fn height(&self) -> BlockHeight { + self.tip().height() + } + + pub fn tip(&self) -> &BlockHeader { + self.blocks.last().unwrap().header() + } + + pub fn get_tier1_accounts(&self) -> AccountKeys { + make_account_keys(&self.tier1_accounts) + } + + pub fn get_chain_info(&self) -> ChainInfo { + ChainInfo { + tracked_shards: Default::default(), + block: self.blocks.last().unwrap().clone(), + tier1_accounts: Arc::new(self.get_tier1_accounts()), + } + } + + pub fn get_peer_chain_info(&self) -> PeerChainInfoV2 { + PeerChainInfoV2 { + genesis_id: self.genesis_id.clone(), + tracked_shards: Default::default(), + archival: false, + height: self.height(), + } + } + + pub fn get_block_headers(&self) -> Vec { + self.blocks.iter().map(|b| b.header().clone()).collect() + } + + pub fn make_config(&self, rng: &mut R) -> config::NetworkConfig { + let seed = &rng.gen::().to_string(); + let mut cfg = + config::NetworkConfig::from_seed(&seed, tcp::ListenerAddr::reserve_for_test()); + // Currently, in unit tests PeerManagerActor is not allowed to try to establish + // connections on its own. + cfg.outbound_disabled = true; + cfg + } + + pub fn make_tier1_data( + &self, + rng: &mut R, + clock: &time::Clock, + ) -> Vec> { + self.tier1_accounts + .iter() + .map(|v| { + let peer_id = make_peer_id(rng); + Arc::new( + make_account_data(rng, 1, clock.now_utc(), v.public_key(), peer_id) + .sign(v) + .unwrap(), + ) + }) + .collect() + } +} + +pub fn make_handshake(rng: &mut R, chain: &Chain) -> Handshake { + let a = make_signer(rng); + let b = make_signer(rng); + let a_id = PeerId::new(a.public_key); + let b_id = PeerId::new(b.public_key); + Handshake { + protocol_version: version::PROTOCOL_VERSION, + oldest_supported_version: version::PEER_MIN_ALLOWED_PROTOCOL_VERSION, + sender_peer_id: a_id, + target_peer_id: b_id, + sender_listen_port: Some(rng.gen()), + sender_chain_info: chain.get_peer_chain_info(), + partial_edge_info: make_partial_edge(rng), + owned_account: None, + } +} + +pub fn make_routed_message(rng: &mut R, body: RoutedMessageBody) -> RoutedMessageV2 { + let signer = make_signer(rng); + let peer_id = PeerId::new(signer.public_key); + RawRoutedMessage { target: PeerIdOrHash::PeerId(peer_id), body }.sign( + &signer.secret_key, + /*ttl=*/ 1, + None, + ) +} +pub fn make_ipv4(rng: &mut impl Rng) -> net::IpAddr { + net::IpAddr::V4(net::Ipv4Addr::from(rng.gen::<[u8; 4]>())) +} + +pub fn make_ipv6(rng: &mut impl Rng) -> net::IpAddr { + net::IpAddr::V6(net::Ipv6Addr::from(rng.gen::<[u8; 16]>())) +} + +pub fn make_addr(rng: &mut R) -> net::SocketAddr { + net::SocketAddr::new(make_ipv4(rng), rng.gen()) +} + +pub fn make_peer_addr(rng: &mut impl Rng, ip: net::IpAddr) -> PeerAddr { + PeerAddr { addr: net::SocketAddr::new(ip, rng.gen()), peer_id: make_peer_id(rng) } +} + +pub fn make_account_data( + rng: &mut impl Rng, + version: u64, + timestamp: time::Utc, + account_key: PublicKey, + peer_id: PeerId, +) -> VersionedAccountData { + VersionedAccountData { + data: AccountData { + proxies: vec![ + // Can't inline make_ipv4/ipv6 calls, because 2-phase borrow + // doesn't work. + { + let ip = make_ipv4(rng); + make_peer_addr(rng, ip) + }, + { + let ip = make_ipv4(rng); + make_peer_addr(rng, ip) + }, + { + let ip = make_ipv6(rng); + make_peer_addr(rng, ip) + }, + ], + peer_id, + }, + account_key, + version, + timestamp, + } +} + +pub fn make_signed_account_data(rng: &mut impl Rng, clock: &time::Clock) -> SignedAccountData { + let signer = make_validator_signer(rng); + let peer_id = make_peer_id(rng); + make_account_data(rng, 1, clock.now_utc(), signer.public_key(), peer_id).sign(&signer).unwrap() +} + +// Accessors for creating malformed SignedAccountData +impl SignedAccountData { + pub(crate) fn payload_mut(&mut self) -> &mut Vec { + &mut self.payload.payload + } + pub(crate) fn signature_mut(&mut self) -> &mut unc_crypto::Signature { + &mut self.payload.signature + } +} diff --git a/chain/network/src/network_protocol/tests.rs b/chain/network/src/network_protocol/tests.rs new file mode 100644 index 000000000..7d712fb58 --- /dev/null +++ b/chain/network/src/network_protocol/tests.rs @@ -0,0 +1,172 @@ +use super::*; +use crate::network_protocol::testonly as data; +use crate::network_protocol::{Encoding, PeersResponse}; +use crate::testonly::make_rng; +use crate::types::{Disconnect, HandshakeFailureReason, PeerMessage}; +use crate::types::{PartialEncodedChunkRequestMsg, PartialEncodedChunkResponseMsg}; +use anyhow::{bail, Context as _}; +use itertools::Itertools as _; +use unc_async::time; +use rand::Rng as _; + +#[test] +fn deduplicate_edges() { + let mut rng = make_rng(19385389); + let rng = &mut rng; + let a = data::make_secret_key(rng); + let b = data::make_secret_key(rng); + let c = data::make_secret_key(rng); + let ab1 = data::make_edge(&a, &b, 1); + let ab3 = data::make_edge(&a, &b, 3); + let ab5 = data::make_edge(&a, &b, 5); + let ac7 = data::make_edge(&a, &c, 7); + let ac9 = data::make_edge(&a, &c, 9); + let bc1 = data::make_edge(&b, &c, 1); + let mut want = vec![ab5.clone(), ac9.clone(), bc1.clone()]; + want.sort_by_key(|e| e.key().clone()); + let input = vec![ab1, ab3, ab5, ac7, ac9, bc1]; + for p in input.iter().permutations(input.len()) { + let mut got = Edge::deduplicate(p.into_iter().cloned().collect()); + got.sort_by_key(|e| e.key().clone()); + assert_eq!(got, want); + } +} + +#[test] +fn bad_account_data_size() { + let mut rng = make_rng(19385389); + let clock = time::FakeClock::default(); + // rule of thumb: 1000x IPv6 should be considered too much. + let signer = data::make_validator_signer(&mut rng); + + let ad = VersionedAccountData { + data: AccountData { + proxies: (0..1000) + .map(|_| { + let ip = data::make_ipv6(&mut rng); + data::make_peer_addr(&mut rng, ip) + }) + .collect(), + peer_id: data::make_peer_id(&mut rng), + }, + account_key: signer.public_key(), + version: rng.gen(), + timestamp: clock.now_utc(), + }; + assert!(ad.sign(&signer).is_err()); +} + +#[test] +fn serialize_deserialize_protobuf_only() { + let mut rng = make_rng(39521947542); + let mut clock = time::FakeClock::default(); + let chain = data::Chain::make(&mut clock, &mut rng, 12); + let msgs = [ + PeerMessage::Tier1Handshake(data::make_handshake(&mut rng, &chain)), + PeerMessage::SyncAccountsData(SyncAccountsData { + accounts_data: (0..4) + .map(|_| Arc::new(data::make_signed_account_data(&mut rng, &clock.clock()))) + .collect(), + incremental: true, + requesting_full_sync: true, + }), + ]; + for m in msgs { + let m2 = PeerMessage::deserialize(Encoding::Proto, &m.serialize(Encoding::Proto)) + .with_context(|| m.to_string()) + .unwrap(); + assert_eq!(m, m2); + } +} + +#[test] +fn serialize_deserialize() -> anyhow::Result<()> { + let mut rng = make_rng(89028037453); + let mut clock = time::FakeClock::default(); + + let chain = data::Chain::make(&mut clock, &mut rng, 12); + let a = data::make_secret_key(&mut rng); + let b = data::make_secret_key(&mut rng); + let edge = data::make_edge(&a, &b, 1); + + let chunk_hash = chain.blocks[3].chunks()[0].chunk_hash(); + let routed_message1 = Box::new(data::make_routed_message( + &mut rng, + RoutedMessageBody::PartialEncodedChunkRequest(PartialEncodedChunkRequestMsg { + chunk_hash: chunk_hash.clone(), + part_ords: vec![], + tracking_shards: Default::default(), + }), + )); + let routed_message2 = Box::new(data::make_routed_message( + &mut rng, + RoutedMessageBody::PartialEncodedChunkResponse(PartialEncodedChunkResponseMsg { + chunk_hash: chunk_hash.clone(), + parts: data::make_chunk_parts(chain.chunks[&chunk_hash].clone()), + receipts: vec![], + }), + )); + let msgs = [ + PeerMessage::Tier2Handshake(data::make_handshake(&mut rng, &chain)), + PeerMessage::HandshakeFailure( + data::make_peer_info(&mut rng), + HandshakeFailureReason::InvalidTarget, + ), + PeerMessage::LastEdge(edge), + PeerMessage::SyncRoutingTable(data::make_routing_table(&mut rng)), + PeerMessage::RequestUpdateNonce(data::make_partial_edge(&mut rng)), + PeerMessage::PeersRequest(PeersRequest { max_peers: None, max_direct_peers: None }), + PeerMessage::PeersResponse(PeersResponse { + peers: (0..5).map(|_| data::make_peer_info(&mut rng)).collect(), + direct_peers: vec![], // TODO: populate this field once borsh support is dropped + }), + PeerMessage::BlockHeadersRequest(chain.blocks.iter().map(|b| *b.hash()).collect()), + PeerMessage::BlockHeaders(chain.get_block_headers()), + PeerMessage::BlockRequest(*chain.blocks[5].hash()), + PeerMessage::Block(chain.blocks[5].clone()), + PeerMessage::Transaction(data::make_signed_transaction(&mut rng)), + PeerMessage::Routed(routed_message1), + PeerMessage::Routed(routed_message2), + PeerMessage::Disconnect(Disconnect { remove_from_connection_store: false }), + PeerMessage::Challenge(data::make_challenge(&mut rng)), + ]; + + // Check that serialize;deserialize = 1 + for enc in [Encoding::Proto, Encoding::Borsh] { + for m in &msgs { + (|| { + let m2 = PeerMessage::deserialize(enc, &m.serialize(enc)) + .with_context(|| m.to_string())?; + if *m != m2 { + bail!("deserialize(serialize({m}) = {m2}"); + } + anyhow::Ok(()) + })() + .with_context(|| format!("encoding={enc:?}"))?; + } + } + + // Test the unambiguous parsing argument described in + // https://docs.google.com/document/d/1gCWmt9O-h_-5JDXIqbKxAaSS3Q9pryB1f9DDY1mMav4/edit#heading=h.x1awbr2acslb + for m in &msgs { + let x = m.serialize(Encoding::Proto); + assert!(x[0] >= 32, "serialize({},PROTO)[0] = {:?}, want >= 32", m, x.get(0)); + let y = m.serialize(Encoding::Borsh); + assert!(y[0] <= 21, "serialize({},BORSH)[0] = {:?}, want <= 21", m, y.get(0)); + } + + // Encodings should never be compatible. + for (from, to) in [(Encoding::Proto, Encoding::Borsh), (Encoding::Borsh, Encoding::Proto)] { + for m in &msgs { + let bytes = &m.serialize(from); + match PeerMessage::deserialize(to, bytes) { + Err(_) => {} + Ok(m2) => { + bail!("from={from:?},to={to:?}: deserialize(serialize({m})) = {m2}, want error") + } + } + } + } + + Ok(()) +} diff --git a/chain/network/src/peer/mod.rs b/chain/network/src/peer/mod.rs new file mode 100644 index 000000000..3d1ef6d8d --- /dev/null +++ b/chain/network/src/peer/mod.rs @@ -0,0 +1,9 @@ +pub(crate) mod peer_actor; +mod stream; +mod tracker; +mod transfer_stats; + +#[cfg(test)] +pub(crate) mod testonly; +#[cfg(test)] +mod tests; diff --git a/chain/network/src/peer/peer_actor.rs b/chain/network/src/peer/peer_actor.rs new file mode 100644 index 000000000..cdff47ddc --- /dev/null +++ b/chain/network/src/peer/peer_actor.rs @@ -0,0 +1,1720 @@ +use crate::accounts_data::AccountDataError; +use crate::concurrency::atomic_cell::AtomicCell; +use crate::concurrency::demux; +use crate::config::PEERS_RESPONSE_MAX_PEERS; +use crate::network_protocol::SnapshotHostInfoVerificationError; +use crate::network_protocol::{ + DistanceVector, Edge, EdgeState, Encoding, OwnedAccount, ParsePeerMessageError, + PartialEdgeInfo, PeerChainInfoV2, PeerIdOrHash, PeerInfo, PeersRequest, PeersResponse, + RawRoutedMessage, RoutedMessageBody, RoutingTableUpdate, StateResponseInfo, SyncAccountsData, + SyncSnapshotHosts, +}; +use crate::peer::stream; +use crate::peer::tracker::Tracker; +use crate::peer_manager::connection; +use crate::peer_manager::network_state::{NetworkState, PRUNE_EDGES_AFTER}; +use crate::peer_manager::peer_manager_actor::Event; +use crate::peer_manager::peer_manager_actor::MAX_TIER2_PEERS; +use crate::private_actix::{RegisterPeerError, SendMessage}; +use crate::routing::edge::verify_nonce; +use crate::routing::NetworkTopologyChange; +use crate::shards_manager::ShardsManagerRequestFromNetwork; +use crate::snapshot_hosts::SnapshotHostInfoError; +use crate::stats::metrics; +use crate::tcp; +use crate::types::{ + BlockInfo, Disconnect, Handshake, HandshakeFailureReason, PeerMessage, PeerType, ReasonForBan, +}; +use actix::fut::future::wrap_future; +use actix::{Actor as _, ActorContext as _, ActorFutureExt as _, AsyncContext as _}; +use lru::LruCache; +use unc_async::time; +use unc_crypto::Signature; +use unc_o11y::{handler_debug_span, log_assert, OpenTelemetrySpanExt, WithSpanContext}; +use unc_performance_metrics_macros::perf; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::types::EpochId; +use unc_primitives::utils::DisplayOption; +use unc_primitives::version::{ + ProtocolVersion, PEER_MIN_ALLOWED_PROTOCOL_VERSION, PROTOCOL_VERSION, +}; +use parking_lot::Mutex; +use rand::seq::IteratorRandom; +use rand::thread_rng; +use std::cmp::min; +use std::fmt::Debug; +use std::io; +use std::net::SocketAddr; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use tracing::Instrument as _; + +/// How often to request peers from active peers. +const REQUEST_PEERS_INTERVAL: time::Duration = time::Duration::seconds(60); + +/// Maximal allowed UTC clock skew between this node and the peer. +const MAX_CLOCK_SKEW: time::Duration = time::Duration::minutes(30); + +/// Maximum number of transaction messages we will accept between block messages. +/// The purpose of this constant is to ensure we do not spend too much time deserializing and +/// dispatching transactions when we should be focusing on consensus-related messages. +const MAX_TRANSACTIONS_PER_BLOCK_MESSAGE: usize = 1000; +/// Limit cache size of 1000 messages +const ROUTED_MESSAGE_CACHE_SIZE: usize = 1000; +/// Duplicated messages will be dropped if routed through the same peer multiple times. +pub(crate) const DROP_DUPLICATED_MESSAGES_PERIOD: time::Duration = time::Duration::milliseconds(50); +/// How often to send the latest block to peers. +const SYNC_LATEST_BLOCK_INTERVAL: time::Duration = time::Duration::seconds(60); +/// How often to perform a full sync of AccountsData with the peer. +const ACCOUNTS_DATA_FULL_SYNC_INTERVAL: time::Duration = time::Duration::minutes(10); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConnectionClosedEvent { + pub(crate) stream_id: tcp::StreamId, + pub(crate) reason: ClosingReason, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct HandshakeStartedEvent { + pub(crate) stream_id: tcp::StreamId, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct HandshakeCompletedEvent { + pub(crate) stream_id: tcp::StreamId, + pub(crate) edge: Edge, + pub(crate) tier: tcp::Tier, +} + +#[derive(thiserror::Error, Clone, PartialEq, Eq, Debug)] +pub(crate) enum ClosingReason { + #[error("too many inbound connections in connecting state")] + TooManyInbound, + #[error("outbound not allowed: {0}")] + OutboundNotAllowed(connection::PoolError), + + #[error("peer banned: {0:?}")] + Ban(ReasonForBan), + #[error("handshake failed")] + HandshakeFailed, + #[error("rejected by PeerManager: {0:?}")] + RejectedByPeerManager(RegisterPeerError), + #[error("stream error")] + StreamError, + /// Read through `tcp::Tier::is_allowed()` to see which message types + /// are allowed for a connection of each tier. + #[error("Received a message of type not allowed on this connection.")] + DisallowedMessage, + #[error("PeerManager requested to close the connection")] + PeerManagerRequest, + #[error("Received DisconnectMessage from peer")] + DisconnectMessage, + #[error("Peer clock skew exceeded {MAX_CLOCK_SKEW}")] + TooLargeClockSkew, + #[error("owned_account.peer_id doesn't match handshake.sender_peer_id")] + OwnedAccountMismatch, + #[error("PeerActor stopped NOT via PeerActor::stop()")] + Unknown, +} + +impl ClosingReason { + /// Used upon closing an outbound connection to decide whether to remove it from the ConnectionStore. + /// If the inbound side is the one closing the connection, it evaluates this function on the closing + /// reason and includes the result in the Disconnect message. + pub(crate) fn remove_from_connection_store(&self) -> bool { + match self { + ClosingReason::TooManyInbound => false, // outbound may be still be OK + ClosingReason::OutboundNotAllowed(_) => true, // outbound not allowed + ClosingReason::Ban(_) => true, // banned + ClosingReason::HandshakeFailed => false, // handshake may simply time out + ClosingReason::RejectedByPeerManager(_) => true, // rejected by peer manager + ClosingReason::StreamError => false, // connection issue + ClosingReason::DisallowedMessage => true, // misbehaving peer + ClosingReason::PeerManagerRequest => true, // closed intentionally + ClosingReason::DisconnectMessage => false, // graceful disconnect + ClosingReason::TooLargeClockSkew => true, // reconnect will fail for the same reason + ClosingReason::OwnedAccountMismatch => true, // misbehaving peer + ClosingReason::Unknown => false, // only happens in tests + } + } +} + +pub(crate) struct PeerActor { + clock: time::Clock, + + /// Shared state of the network module. + network_state: Arc, + /// This node's id and address (either listening or socket address). + my_node_info: PeerInfo, + + /// TEST-ONLY + stream_id: crate::tcp::StreamId, + /// Peer address from connection. + peer_addr: SocketAddr, + /// Peer type. + peer_type: PeerType, + + /// Framed wrapper to send messages through the TCP connection. + framed: stream::FramedStream, + + /// Tracker for requests and responses. + tracker: Arc>, + /// Network bandwidth stats. + stats: Arc, + /// Cache of recently routed messages, this allows us to drop duplicates + routed_message_cache: LruCache<(PeerId, PeerIdOrHash, Signature), time::Instant>, + /// Whether we detected support for protocol buffers during handshake. + protocol_buffers_supported: bool, + /// Whether the PeerActor should skip protobuf support detection and use + /// a given encoding right away. + force_encoding: Option, + + /// Peer status. + peer_status: PeerStatus, + closing_reason: Option, + /// Peer id and info. Present when Ready, + /// or (for outbound only) when Connecting. + // TODO: move it to ConnectingStatus::Outbound. + // When ready, use connection.peer_info instead. + peer_info: DisplayOption, +} + +impl Debug for PeerActor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "{:?}", self.my_node_info) + } +} + +#[derive(Clone, Debug)] +struct HandshakeSpec { + /// ID of the peer on the other side of the connection. + peer_id: PeerId, + tier: tcp::Tier, + protocol_version: ProtocolVersion, + partial_edge_info: PartialEdgeInfo, +} + +type HandshakeSignalSender = tokio::sync::oneshot::Sender; +pub type HandshakeSignal = tokio::sync::oneshot::Receiver; + +impl PeerActor { + /// Spawns a PeerActor on a separate actix::Arbiter and awaits for the + /// handshake to succeed/fail. The actual result is not returned because + /// actix makes everything complicated. + pub(crate) async fn spawn_and_handshake( + clock: time::Clock, + stream: tcp::Stream, + force_encoding: Option, + network_state: Arc, + ) -> anyhow::Result> { + let (addr, handshake_signal) = Self::spawn(clock, stream, force_encoding, network_state)?; + // Await for the handshake to complete, by awaiting the handshake_signal channel. + // This is a receiver of Infallible, so it only completes when the channel is closed. + handshake_signal.await.err().unwrap(); + Ok(addr) + } + + /// Spawns a PeerActor on a separate actix arbiter. + /// Returns the actor address and a HandshakeSignal: an asynchronous channel + /// which will be closed as soon as the handshake is finished (successfully or not). + /// You can asynchronously await the returned HandshakeSignal. + pub(crate) fn spawn( + clock: time::Clock, + stream: tcp::Stream, + force_encoding: Option, + network_state: Arc, + ) -> anyhow::Result<(actix::Addr, HandshakeSignal)> { + let stream_id = stream.id(); + match Self::spawn_inner(clock, stream, force_encoding, network_state.clone()) { + Ok(it) => Ok(it), + Err(reason) => { + network_state.config.event_sink.push(Event::ConnectionClosed( + ConnectionClosedEvent { stream_id, reason: reason.clone() }, + )); + Err(reason.into()) + } + } + } + + fn spawn_inner( + clock: time::Clock, + stream: tcp::Stream, + force_encoding: Option, + network_state: Arc, + ) -> Result<(actix::Addr, HandshakeSignal), ClosingReason> { + let connecting_status = match &stream.type_ { + tcp::StreamType::Inbound => ConnectingStatus::Inbound( + network_state + .inbound_handshake_permits + .clone() + .try_acquire_owned() + .map_err(|_| ClosingReason::TooManyInbound)?, + ), + tcp::StreamType::Outbound { tier, peer_id } => ConnectingStatus::Outbound { + _permit: match tier { + tcp::Tier::T1 => network_state + .tier1 + .start_outbound(peer_id.clone()) + .map_err(ClosingReason::OutboundNotAllowed)?, + tcp::Tier::T2 => { + // A loop connection is not allowed on TIER2 + // (it is allowed on TIER1 to verify node's public IP). + // TODO(gprusak): try to make this more consistent. + if peer_id == &network_state.config.node_id() { + return Err(ClosingReason::OutboundNotAllowed( + connection::PoolError::UnexpectedLoopConnection, + )); + } + network_state + .tier2 + .start_outbound(peer_id.clone()) + .map_err(ClosingReason::OutboundNotAllowed)? + } + }, + handshake_spec: HandshakeSpec { + partial_edge_info: network_state.propose_edge(&clock, peer_id, None), + protocol_version: PROTOCOL_VERSION, + tier: *tier, + peer_id: peer_id.clone(), + }, + }, + }; + // Override force_encoding for outbound Tier1 connections, + // since Tier1Handshake is supported only with proto encoding. + let force_encoding = match &stream.type_ { + tcp::StreamType::Outbound { tier, .. } if tier == &tcp::Tier::T1 => { + Some(Encoding::Proto) + } + _ => force_encoding, + }; + let my_node_info = PeerInfo { + id: network_state.config.node_id(), + addr: network_state.config.node_addr.as_ref().map(|a| **a), + account_id: network_state.config.validator.as_ref().map(|v| v.account_id()), + }; + // recv is the HandshakeSignal returned by this spawn_inner() call. + let (send, recv): (HandshakeSignalSender, HandshakeSignal) = + tokio::sync::oneshot::channel(); + // Start PeerActor on separate thread. + Ok(( + Self::start_in_arbiter(&actix::Arbiter::new().handle(), move |ctx| { + let stream_id = stream.id(); + let peer_addr = stream.peer_addr; + let stream_type = stream.type_.clone(); + let stats = Arc::new(connection::Stats::default()); + let framed = stream::FramedStream::spawn(ctx, stream, stats.clone()); + Self { + closing_reason: None, + clock, + my_node_info, + stream_id, + peer_addr, + peer_type: match &stream_type { + tcp::StreamType::Inbound => PeerType::Inbound, + tcp::StreamType::Outbound { .. } => PeerType::Outbound, + }, + peer_status: PeerStatus::Connecting(send, connecting_status), + framed, + tracker: Default::default(), + stats, + routed_message_cache: LruCache::new(ROUTED_MESSAGE_CACHE_SIZE), + protocol_buffers_supported: false, + force_encoding, + peer_info: match &stream_type { + tcp::StreamType::Inbound => None, + tcp::StreamType::Outbound { peer_id, .. } => Some(PeerInfo { + id: peer_id.clone(), + addr: Some(peer_addr), + account_id: None, + }), + } + .into(), + network_state, + } + }), + recv, + )) + } + + // Determines the encoding to use for communication with the peer. + // It can be None while Handshake with the peer has not been finished yet. + // In case it is None, both encodings are attempted for parsing, and each message + // is sent twice. + fn encoding(&self) -> Option { + if self.force_encoding.is_some() { + return self.force_encoding; + } + if self.protocol_buffers_supported { + return Some(Encoding::Proto); + } + match self.peer_status { + PeerStatus::Connecting { .. } => None, + PeerStatus::Ready { .. } => Some(Encoding::Borsh), + } + } + + fn parse_message(&mut self, msg: &[u8]) -> Result { + if let Some(e) = self.encoding() { + return PeerMessage::deserialize(e, msg); + } + if let Ok(msg) = PeerMessage::deserialize(Encoding::Proto, msg) { + self.protocol_buffers_supported = true; + return Ok(msg); + } + return PeerMessage::deserialize(Encoding::Borsh, msg); + } + + fn send_message_or_log(&self, msg: &PeerMessage) { + self.send_message(msg); + } + + fn send_message(&self, msg: &PeerMessage) { + if let (PeerStatus::Ready(conn), PeerMessage::PeersRequest(_)) = (&self.peer_status, msg) { + conn.last_time_peer_requested.store(Some(self.clock.now())); + } + if let Some(enc) = self.encoding() { + return self.send_message_with_encoding(msg, enc); + } + self.send_message_with_encoding(msg, Encoding::Proto); + self.send_message_with_encoding(msg, Encoding::Borsh); + } + + fn send_message_with_encoding(&self, msg: &PeerMessage, enc: Encoding) { + let msg_type: &str = msg.msg_variant(); + let _span = tracing::trace_span!( + target: "network", + "send_message_with_encoding", + msg_type) + .entered(); + // Skip sending block and headers if we received it or header from this peer. + // Record block requests in tracker. + match msg { + // Temporarily disable this check because now the node needs to send block to its + // peers to update its height at the peer. In the future we will introduce a new + // peer message type for that and then we can enable this check again. + //PeerMessage::Block(b) if self.tracker.lock().has_received(b.hash()) => return, + PeerMessage::BlockRequest(h) => self.tracker.lock().push_request(*h), + PeerMessage::SyncAccountsData(d) => metrics::SYNC_ACCOUNTS_DATA + .with_label_values(&[ + "sent", + metrics::bool_to_str(d.incremental), + metrics::bool_to_str(d.requesting_full_sync), + ]) + .inc(), + PeerMessage::SyncSnapshotHosts(_) => { + metrics::SYNC_SNAPSHOT_HOSTS.with_label_values(&["sent"]).inc() + } + _ => (), + }; + + let bytes = msg.serialize(enc); + self.tracker.lock().increment_sent(&self.clock, bytes.len() as u64); + let bytes_len = bytes.len(); + tracing::trace!(target: "network", msg_len = bytes_len); + self.framed.send(stream::Frame(bytes)); + metrics::PEER_DATA_SENT_BYTES.inc_by(bytes_len as u64); + metrics::PEER_MESSAGE_SENT_BY_TYPE_TOTAL.with_label_values(&[msg_type]).inc(); + metrics::PEER_MESSAGE_SENT_BY_TYPE_BYTES + .with_label_values(&[msg_type]) + .inc_by(bytes_len as u64); + } + + fn send_handshake(&self, spec: HandshakeSpec) { + let (height, tracked_shards) = + if let Some(chain_info) = self.network_state.chain_info.load().as_ref() { + (chain_info.block.header().height(), chain_info.tracked_shards.clone()) + } else { + (0, vec![]) + }; + let handshake = Handshake { + protocol_version: spec.protocol_version, + oldest_supported_version: PEER_MIN_ALLOWED_PROTOCOL_VERSION, + sender_peer_id: self.network_state.config.node_id(), + target_peer_id: spec.peer_id, + sender_listen_port: self.network_state.config.node_addr.as_ref().map(|a| a.port()), + sender_chain_info: PeerChainInfoV2 { + genesis_id: self.network_state.genesis_id.clone(), + // TODO: remove `height` from PeerChainInfo + height, + tracked_shards, + archival: self.network_state.config.archive, + }, + partial_edge_info: spec.partial_edge_info, + owned_account: self.network_state.config.validator.as_ref().map(|vc| { + OwnedAccount { + account_key: vc.signer.public_key(), + peer_id: self.network_state.config.node_id(), + timestamp: self.clock.now_utc(), + } + .sign(vc.signer.as_ref()) + }), + }; + let msg = match spec.tier { + tcp::Tier::T1 => PeerMessage::Tier1Handshake(handshake), + tcp::Tier::T2 => PeerMessage::Tier2Handshake(handshake), + }; + self.send_message_or_log(&msg); + } + + fn stop(&mut self, ctx: &mut actix::Context, reason: ClosingReason) { + // Only the first call to stop sets the closing_reason. + if self.closing_reason.is_none() { + self.closing_reason = Some(reason); + } + ctx.stop(); + } + + /// `PeerId` of the current node. + fn my_node_id(&self) -> &PeerId { + &self.my_node_info.id + } + + /// `PeerId` of the other node. + fn other_peer_id(&self) -> Option<&PeerId> { + self.peer_info.as_ref().as_ref().map(|peer_info| &peer_info.id) + } + + fn process_handshake( + &mut self, + ctx: &mut ::Context, + tier: tcp::Tier, + handshake: Handshake, + ) { + tracing::debug!(target: "network", "{:?}: Received handshake {:?}", self.my_node_info.id, handshake); + let cs = match &self.peer_status { + PeerStatus::Connecting(_, it) => it, + _ => panic!("process_handshake called in non-connecting state"), + }; + match cs { + ConnectingStatus::Outbound { handshake_spec: spec, .. } => { + if handshake.protocol_version != spec.protocol_version { + tracing::warn!(target: "network", "Protocol version mismatch. Disconnecting peer {}", handshake.sender_peer_id); + self.stop(ctx, ClosingReason::HandshakeFailed); + return; + } + if handshake.sender_chain_info.genesis_id != self.network_state.genesis_id { + tracing::warn!(target: "network", "Genesis mismatch. Disconnecting peer {}", handshake.sender_peer_id); + self.stop(ctx, ClosingReason::HandshakeFailed); + return; + } + if handshake.sender_peer_id != spec.peer_id { + tracing::warn!(target: "network", "PeerId mismatch. Disconnecting peer {}", handshake.sender_peer_id); + self.stop(ctx, ClosingReason::HandshakeFailed); + return; + } + // This can happen only in case of a malicious node. + // Outbound peer requests a connection of a given TIER, the inbound peer can just + // confirm the TIER or drop connection. TIER is not negotiable during handshake. + if tier != spec.tier { + tracing::warn!(target: "network", "Connection TIER mismatch. Disconnecting peer {}", handshake.sender_peer_id); + self.stop(ctx, ClosingReason::HandshakeFailed); + return; + } + if handshake.partial_edge_info.nonce != spec.partial_edge_info.nonce { + tracing::warn!(target: "network", "Nonce mismatch. Disconnecting peer {}", handshake.sender_peer_id); + self.stop(ctx, ClosingReason::HandshakeFailed); + return; + } + } + ConnectingStatus::Inbound { .. } => { + if PEER_MIN_ALLOWED_PROTOCOL_VERSION > handshake.protocol_version + || handshake.protocol_version > PROTOCOL_VERSION + { + tracing::debug!( + target: "network", + version = handshake.protocol_version, + "Received connection from node with unsupported PROTOCOL_VERSION."); + self.send_message_or_log(&PeerMessage::HandshakeFailure( + self.my_node_info.clone(), + HandshakeFailureReason::ProtocolVersionMismatch { + version: PROTOCOL_VERSION, + oldest_supported_version: PEER_MIN_ALLOWED_PROTOCOL_VERSION, + }, + )); + return; + } + let genesis_id = self.network_state.genesis_id.clone(); + if handshake.sender_chain_info.genesis_id != genesis_id { + tracing::debug!(target: "network", "Received connection from node with different genesis."); + self.send_message_or_log(&PeerMessage::HandshakeFailure( + self.my_node_info.clone(), + HandshakeFailureReason::GenesisMismatch(genesis_id), + )); + return; + } + if handshake.target_peer_id != self.my_node_info.id { + tracing::debug!(target: "network", "Received handshake from {:?} to {:?} but I am {:?}", handshake.sender_peer_id, handshake.target_peer_id, self.my_node_info.id); + self.send_message_or_log(&PeerMessage::HandshakeFailure( + self.my_node_info.clone(), + HandshakeFailureReason::InvalidTarget, + )); + return; + } + + // Verify if nonce is sane. + if let Err(err) = verify_nonce(&self.clock, handshake.partial_edge_info.nonce) { + tracing::debug!(target: "network", nonce=?handshake.partial_edge_info.nonce, my_node_id = ?self.my_node_id(), peer_id=?handshake.sender_peer_id, "bad nonce, disconnecting: {err}"); + self.stop(ctx, ClosingReason::HandshakeFailed); + return; + } + // Check that the received nonce is greater than the current nonce of this connection. + // If not (and this is an inbound connection) propose a new nonce. + if let Some(last_edge) = + self.network_state.graph.load().local_edges.get(&handshake.sender_peer_id) + { + if last_edge.nonce() >= handshake.partial_edge_info.nonce { + tracing::debug!(target: "network", "{:?}: Received too low nonce from peer {:?} sending evidence.", self.my_node_id(), self.peer_addr); + self.send_message_or_log(&PeerMessage::LastEdge(last_edge.clone())); + return; + } + } + } + } + + // Verify that handshake.owned_account is valid. + if let Some(owned_account) = &handshake.owned_account { + if let Err(_) = owned_account.payload().verify(&owned_account.account_key) { + self.stop(ctx, ClosingReason::Ban(ReasonForBan::InvalidSignature)); + return; + } + if owned_account.peer_id != handshake.sender_peer_id { + self.stop(ctx, ClosingReason::OwnedAccountMismatch); + return; + } + if (owned_account.timestamp - self.clock.now_utc()).abs() >= MAX_CLOCK_SKEW { + self.stop(ctx, ClosingReason::TooLargeClockSkew); + return; + } + } + + // Merge partial edges. + let nonce = handshake.partial_edge_info.nonce; + let partial_edge_info = match cs { + ConnectingStatus::Outbound { handshake_spec, .. } => { + handshake_spec.partial_edge_info.clone() + } + ConnectingStatus::Inbound { .. } => { + self.network_state.propose_edge(&self.clock, &handshake.sender_peer_id, Some(nonce)) + } + }; + let edge = Edge::new( + self.my_node_id().clone(), + handshake.sender_peer_id.clone(), + nonce, + partial_edge_info.signature.clone(), + handshake.partial_edge_info.signature.clone(), + ); + + // TODO(gprusak): not enabling a port for listening is also a valid setup. + // In that case peer_info.addr should be None (same as now), however + // we still should do the check against the PeerStore::blacklist. + // Currently PeerManager is rejecting connections with peer_info.addr == None + // preemptively. + let peer_info = PeerInfo { + id: handshake.sender_peer_id.clone(), + addr: handshake + .sender_listen_port + .map(|port| SocketAddr::new(self.peer_addr.ip(), port)), + account_id: None, + }; + + let now = self.clock.now(); + let conn = Arc::new(connection::Connection { + tier, + addr: ctx.address(), + peer_info: peer_info.clone(), + owned_account: handshake.owned_account.clone(), + genesis_id: handshake.sender_chain_info.genesis_id.clone(), + tracked_shards: handshake.sender_chain_info.tracked_shards.clone(), + archival: handshake.sender_chain_info.archival, + last_block: Default::default(), + peer_type: self.peer_type, + stats: self.stats.clone(), + _peer_connections_metric: metrics::PEER_CONNECTIONS.new_point(&metrics::Connection { + type_: self.peer_type, + encoding: self.encoding(), + }), + last_time_peer_requested: AtomicCell::new(None), + last_time_received_message: AtomicCell::new(now), + established_time: now, + send_accounts_data_demux: demux::Demux::new( + self.network_state.config.accounts_data_broadcast_rate_limit, + ), + send_snapshot_hosts_demux: demux::Demux::new( + self.network_state.config.snapshot_hosts_broadcast_rate_limit, + ), + }); + + let tracker = self.tracker.clone(); + let clock = self.clock.clone(); + + let mut interval = + time::Interval::new(clock.now(), self.network_state.config.peer_stats_period); + ctx.spawn({ + let conn = conn.clone(); + wrap_future(async move { + loop { + interval.tick(&clock).await; + let sent = tracker.lock().sent_bytes.minute_stats(&clock); + let received = tracker.lock().received_bytes.minute_stats(&clock); + conn.stats + .received_bytes_per_sec + .store(received.bytes_per_min / 60, Ordering::Relaxed); + conn.stats.sent_bytes_per_sec.store(sent.bytes_per_min / 60, Ordering::Relaxed); + } + }) + }); + + // This time is used to figure out when the first run of the callbacks it run. + // It is important that it is set here (rather than calling clock.now() within the future) - as it makes testing a lot easier (and more deterministic). + + let start_time = self.clock.now(); + + // Here we stop processing any PeerActor events until PeerManager + // decides whether to accept the connection or not: ctx.wait makes + // the actor event loop poll on the future until it completes before + // processing any other events. + ctx.wait(wrap_future({ + let network_state = self.network_state.clone(); + let clock = self.clock.clone(); + let conn = conn.clone(); + let edge = edge.clone(); + async move { network_state.register(&clock,edge,conn).await } + }) + .map(move |res, act: &mut PeerActor, ctx| { + match res { + Ok(()) => { + act.peer_info = Some(peer_info).into(); + act.peer_status = PeerStatus::Ready(conn.clone()); + // Respond to handshake if it's inbound and connection was consolidated. + if act.peer_type == PeerType::Inbound { + act.send_handshake(HandshakeSpec{ + peer_id: handshake.sender_peer_id.clone(), + tier, + protocol_version: handshake.protocol_version, + partial_edge_info: partial_edge_info, + }); + } + // TIER1 is strictly reserved for BFT consensensus messages, + // so all kinds of periodical syncs happen only on TIER2 connections. + if tier==tcp::Tier::T2 { + // Trigger a full accounts data sync periodically. + // Note that AccountsData is used to establish TIER1 network, + // it is broadcasted over TIER2 network. This is a bootstrapping + // mechanism, because TIER2 is established before TIER1. + // + // TODO(gprusak): consider whether it wouldn't be more uniform to just + // send full sync from both sides of the connection independently. Or + // perhaps make the full sync request a separate message which doesn't + // carry the accounts_data at all. + if conn.peer_type == PeerType::Outbound { + ctx.spawn(wrap_future({ + let clock = act.clock.clone(); + let conn = conn.clone(); + let network_state = act.network_state.clone(); + let mut interval = time::Interval::new(clock.now(),ACCOUNTS_DATA_FULL_SYNC_INTERVAL); + async move { + loop { + interval.tick(&clock).await; + conn.send_message(Arc::new(PeerMessage::SyncAccountsData(SyncAccountsData{ + accounts_data: network_state.accounts_data.load().data.values().cloned().collect(), + incremental: false, + requesting_full_sync: true, + }))); + } + } + })); + } + // Exchange peers periodically. + ctx.spawn(wrap_future({ + let clock = act.clock.clone(); + let conn = conn.clone(); + let mut interval = time::Interval::new(clock.now(),REQUEST_PEERS_INTERVAL); + async move { + loop { + interval.tick(&clock).await; + conn.send_message(Arc::new(PeerMessage::PeersRequest( PeersRequest{ + max_peers: Some(PEERS_RESPONSE_MAX_PEERS as u32), + max_direct_peers: Some(MAX_TIER2_PEERS as u32), + }))); + } + } + })); + // Send latest block periodically + ctx.spawn(wrap_future({ + let clock = act.clock.clone(); + let conn = conn.clone(); + let state = act.network_state.clone(); + let mut interval = time::Interval::new(clock.now(), SYNC_LATEST_BLOCK_INTERVAL); + async move { + loop { + interval.tick(&clock).await; + if let Some(chain_info) = state.chain_info.load().as_ref() { + conn.send_message(Arc::new(PeerMessage::Block( + chain_info.block.clone(), + ))); + } + } + } + })); + + // Refresh connection nonces but only if we're outbound. For inbound connection, the other party should + // take care of nonce refresh. + if act.peer_type == PeerType::Outbound { + ctx.spawn(wrap_future({ + let conn = conn.clone(); + let network_state = act.network_state.clone(); + let clock = act.clock.clone(); + // How often should we refresh a nonce from a peer. + // It should be smaller than PRUNE_EDGES_AFTER. + let mut interval = time::Interval::new(start_time + PRUNE_EDGES_AFTER / 3, PRUNE_EDGES_AFTER / 3); + async move { + loop { + interval.tick(&clock).await; + conn.send_message(Arc::new( + PeerMessage::RequestUpdateNonce(PartialEdgeInfo::new( + &network_state.config.node_id(), + &conn.peer_info.id, + Edge::create_fresh_nonce(&clock), + &network_state.config.node_key, + ) + ))); + + } + } + })); + } + // Sync the RoutingTable. + act.sync_routing_table(); + // Sync snapshot hosts + act.sync_snapshot_hosts(); + } + + act.network_state.config.event_sink.push(Event::HandshakeCompleted(HandshakeCompletedEvent{ + stream_id: act.stream_id, + edge, + tier: conn.tier, + })); + }, + Err(err) => { + tracing::info!(target: "network", "{:?}: Connection with {:?} rejected by PeerManager: {:?}", act.my_node_id(),conn.peer_info.id,err); + act.stop(ctx,ClosingReason::RejectedByPeerManager(err)); + } + } + }) + ); + } + + // Send full RoutingTable. + fn sync_routing_table(&self) { + let mut known_edges: Vec = + self.network_state.graph.load().edges.values().cloned().collect(); + if self.network_state.config.skip_tombstones.is_some() { + known_edges.retain(|edge| edge.removal_info().is_none()); + metrics::EDGE_TOMBSTONE_SENDING_SKIPPED.inc(); + } + let known_accounts = self.network_state.account_announcements.get_announcements(); + self.send_message_or_log(&PeerMessage::SyncRoutingTable(RoutingTableUpdate::new( + known_edges, + known_accounts, + ))); + } + + // Send all known snapshot hosts. + fn sync_snapshot_hosts(&self) { + let hosts = self.network_state.snapshot_hosts.get_hosts(); + self.send_message_or_log(&PeerMessage::SyncSnapshotHosts(SyncSnapshotHosts { hosts })); + } + + fn handle_msg_connecting(&mut self, ctx: &mut actix::Context, msg: PeerMessage) { + match (&mut self.peer_status, msg) { + ( + PeerStatus::Connecting(_, ConnectingStatus::Outbound { handshake_spec, .. }), + PeerMessage::HandshakeFailure(peer_info, reason), + ) => { + match reason { + HandshakeFailureReason::GenesisMismatch(genesis) => { + tracing::warn!(target: "network", "Attempting to connect to a node ({}) with a different genesis block. Our genesis: {:?}, their genesis: {:?}", peer_info, self.network_state.genesis_id, genesis); + self.stop(ctx, ClosingReason::HandshakeFailed); + } + HandshakeFailureReason::ProtocolVersionMismatch { + version, + oldest_supported_version, + } => { + // Retry the handshake with the common protocol version. + let common_version = std::cmp::min(version, PROTOCOL_VERSION); + if common_version < oldest_supported_version + || common_version < PEER_MIN_ALLOWED_PROTOCOL_VERSION + { + tracing::warn!(target: "network", "Unable to connect to a node ({}) due to a network protocol version mismatch. Our version: {:?}, their: {:?}", peer_info, (PROTOCOL_VERSION, PEER_MIN_ALLOWED_PROTOCOL_VERSION), (version, oldest_supported_version)); + self.stop(ctx, ClosingReason::HandshakeFailed); + return; + } + handshake_spec.protocol_version = common_version; + let spec = handshake_spec.clone(); + ctx.wait(actix::fut::ready(()).then(move |_, act: &mut Self, _| { + act.send_handshake(spec); + actix::fut::ready(()) + })); + } + HandshakeFailureReason::InvalidTarget => { + tracing::debug!(target: "network", "Peer found was not what expected. Updating peer info with {:?}", peer_info); + self.network_state.peer_store.add_direct_peer(&self.clock, peer_info); + self.stop(ctx, ClosingReason::HandshakeFailed); + } + } + } + // TODO(gprusak): LastEdge should rather be a variant of HandshakeFailure. + // Clean this up (you don't have to modify the proto, just the translation layer). + ( + PeerStatus::Connecting(_, ConnectingStatus::Outbound { handshake_spec, .. }), + PeerMessage::LastEdge(edge), + ) => { + // Check that the edge provided: + let ok = + // - is for the relevant pair of peers + edge.key()==&Edge::make_key(self.my_node_info.id.clone(),handshake_spec.peer_id.clone()) && + // - is not younger than what we proposed originally. This protects us from + // a situation in which the peer presents us with a very outdated edge e, + // and then we sign a new edge with nonce e.nonce+1 which is also outdated. + // It may still happen that an edge with an old nonce gets signed, but only + // if both nodes not know about the newer edge. We don't defend against that. + // Also a malicious peer might send the LastEdge with the edge we just + // signed (pretending that it is old) but we cannot detect that, because the + // signatures are currently deterministic. + edge.nonce() >= handshake_spec.partial_edge_info.nonce && + // - is a correctly signed edge + edge.verify(); + // Disconnect if neighbor sent an invalid edge. + if !ok { + tracing::info!(target: "network", "{:?}: Peer {:?} sent invalid edge. Disconnect.", self.my_node_id(), self.peer_addr); + self.stop(ctx, ClosingReason::HandshakeFailed); + return; + } + // Recreate the edge with a newer nonce. + handshake_spec.partial_edge_info = self.network_state.propose_edge( + &self.clock, + &handshake_spec.peer_id, + Some(std::cmp::max(edge.next(), Edge::create_fresh_nonce(&self.clock))), + ); + let spec = handshake_spec.clone(); + ctx.wait(actix::fut::ready(()).then(move |_, act: &mut Self, _| { + act.send_handshake(spec); + actix::fut::ready(()) + })); + } + (PeerStatus::Connecting { .. }, PeerMessage::Tier1Handshake(msg)) => { + self.process_handshake(ctx, tcp::Tier::T1, msg) + } + (PeerStatus::Connecting { .. }, PeerMessage::Tier2Handshake(msg)) => { + self.process_handshake(ctx, tcp::Tier::T2, msg) + } + (_, msg) => { + tracing::warn!(target:"network","unexpected message during handshake: {}",msg) + } + } + } + + async fn receive_routed_message( + clock: &time::Clock, + network_state: &NetworkState, + peer_id: PeerId, + msg_hash: CryptoHash, + body: RoutedMessageBody, + ) -> Result, ReasonForBan> { + let _span = tracing::trace_span!( + target: "network", + "receive_routed_message", + "type" = <&RoutedMessageBody as Into<&'static str>>::into(&body) + ) + .entered(); + Ok(match body { + RoutedMessageBody::TxStatusRequest(account_id, tx_hash) => network_state + .client + .tx_status_request(account_id, tx_hash) + .await + .map(|v| RoutedMessageBody::TxStatusResponse(*v)), + RoutedMessageBody::TxStatusResponse(tx_result) => { + network_state.client.tx_status_response(tx_result).await; + None + } + RoutedMessageBody::StateResponse(info) => { + network_state.client.state_response(StateResponseInfo::V1(info)).await; + None + } + RoutedMessageBody::BlockApproval(approval) => { + network_state.client.block_approval(approval, peer_id).await; + None + } + RoutedMessageBody::ForwardTx(transaction) => { + network_state.client.transaction(transaction, /*is_forwarded=*/ true).await; + None + } + RoutedMessageBody::PartialEncodedChunkRequest(request) => { + network_state.shards_manager_adapter.send( + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkRequest { + partial_encoded_chunk_request: request, + route_back: msg_hash, + }, + ); + None + } + RoutedMessageBody::PartialEncodedChunkResponse(response) => { + network_state.shards_manager_adapter.send( + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkResponse { + partial_encoded_chunk_response: response, + received_time: clock.now().into(), + }, + ); + None + } + RoutedMessageBody::VersionedPartialEncodedChunk(chunk) => { + network_state + .shards_manager_adapter + .send(ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunk(chunk)); + None + } + RoutedMessageBody::PartialEncodedChunkForward(msg) => { + network_state + .shards_manager_adapter + .send(ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkForward(msg)); + None + } + RoutedMessageBody::ChunkStateWitness(witness) => { + network_state.client.chunk_state_witness(witness).await; + None + } + RoutedMessageBody::ChunkEndorsement(endorsement) => { + network_state.client.chunk_endorsement(endorsement).await; + None + } + body => { + tracing::error!(target: "network", "Peer received unexpected message type: {:?}", body); + None + } + }) + } + + fn receive_message( + &self, + ctx: &mut actix::Context, + conn: &connection::Connection, + msg: PeerMessage, + ) { + let _span = tracing::trace_span!(target: "network", "receive_message").entered(); + // This is a fancy way to clone the message iff event_sink is non-null. + // If you have a better idea on how to achieve that, feel free to improve this. + let message_processed_event = self + .network_state + .config + .event_sink + .delayed_push(|| Event::MessageProcessed(conn.tier, msg.clone())); + let was_requested = match &msg { + PeerMessage::Block(block) => { + self.network_state.txns_since_last_block.store(0, Ordering::Release); + let hash = *block.hash(); + let height = block.header().height(); + conn.last_block.rcu(|last_block| { + if last_block.is_none() || last_block.unwrap().height <= height { + Arc::new(Some(BlockInfo { height, hash })) + } else { + last_block.clone() + } + }); + let mut tracker = self.tracker.lock(); + tracker.push_received(hash); + tracker.has_request(&hash) + } + _ => false, + }; + let clock = self.clock.clone(); + let network_state = self.network_state.clone(); + let peer_id = conn.peer_info.id.clone(); + ctx.spawn(wrap_future(async move { + Ok(match msg { + PeerMessage::Routed(msg) => { + let msg_hash = msg.hash(); + Self::receive_routed_message(&clock, &network_state, peer_id, msg_hash, msg.msg.body).await?.map( + |body| { + PeerMessage::Routed(network_state.sign_message( + &clock, + RawRoutedMessage { target: PeerIdOrHash::Hash(msg_hash), body }, + )) + }, + ) + } + PeerMessage::BlockRequest(hash) => { + network_state.client.block_request(hash).await.map(|b|PeerMessage::Block(*b)) + } + PeerMessage::BlockHeadersRequest(hashes) => { + network_state.client.block_headers_request(hashes).await.map(PeerMessage::BlockHeaders) + } + PeerMessage::Block(block) => { + network_state.client.block(block, peer_id, was_requested).await; + None + } + PeerMessage::Transaction(transaction) => { + network_state.client.transaction(transaction, /*is_forwarded=*/ false).await; + None + } + PeerMessage::BlockHeaders(headers) => { + network_state.client.block_headers(headers, peer_id).await?; + None + } + PeerMessage::Challenge(challenge) => { + network_state.client.challenge(challenge).await; + None + } + PeerMessage::StateRequestHeader(shard_id, sync_hash) => network_state + .client + .state_request_header(shard_id, sync_hash) + .await? + .map(PeerMessage::VersionedStateResponse), + PeerMessage::StateRequestPart(shard_id, sync_hash, part_id) => network_state + .client + .state_request_part(shard_id, sync_hash, part_id) + .await? + .map(PeerMessage::VersionedStateResponse), + PeerMessage::VersionedStateResponse(info) => { + //TODO: Route to state sync actor. + network_state.client.state_response(info).await; + None + } + msg => { + tracing::error!(target: "network", "Peer received unexpected type: {:?}", msg); + None + } + })}.in_current_span()) + .map(|res, act: &mut PeerActor, ctx| { + match res { + // TODO(gprusak): make sure that for routed messages we drop routeback info correctly. + Ok(Some(resp)) => act.send_message_or_log(&resp), + Ok(None) => {} + Err(ban_reason) => act.stop(ctx, ClosingReason::Ban(ban_reason)), + } + message_processed_event(); + }), + ); + } + + fn handle_msg_ready( + &mut self, + ctx: &mut actix::Context, + conn: Arc, + peer_msg: PeerMessage, + ) { + let _span = tracing::trace_span!( + target: "network", + "handle_msg_ready", + "type" = <&PeerMessage as Into<&'static str>>::into(&peer_msg) + ) + .entered(); + + // Clones message iff someone is listening on the sink. Should be in tests only. + let message_processed_event = self + .network_state + .config + .event_sink + .delayed_push(|| Event::MessageProcessed(conn.tier, peer_msg.clone())); + + match peer_msg { + PeerMessage::Disconnect(d) => { + tracing::debug!(target: "network", "Disconnect signal. Me: {:?} Peer: {:?}", self.my_node_info.id, self.other_peer_id()); + + if d.remove_from_connection_store { + self.network_state + .connection_store + .remove_from_connection_store(self.other_peer_id().unwrap()) + } + + self.stop(ctx, ClosingReason::DisconnectMessage); + } + PeerMessage::Tier1Handshake(_) | PeerMessage::Tier2Handshake(_) => { + // Received handshake after already have seen handshake from this peer. + tracing::debug!(target: "network", "Duplicate handshake from {}", self.peer_info); + } + PeerMessage::PeersRequest(PeersRequest { max_peers, max_direct_peers }) => { + let mut num_peers = self.network_state.config.max_send_peers; + if let Some(max_peers) = max_peers { + num_peers = min(num_peers, max_peers); + } + let peers = self.network_state.peer_store.healthy_peers(num_peers as usize); + + let mut direct_peers = self.network_state.get_direct_peers(); + if let Some(max_direct_peers) = max_direct_peers { + if direct_peers.len() > max_direct_peers as usize { + direct_peers = direct_peers + .into_iter() + .choose_multiple(&mut thread_rng(), max_direct_peers as usize); + } + } + + if !peers.is_empty() || !direct_peers.is_empty() { + tracing::debug!(target: "network", "Peers request from {}: sending {} peers and {} direct peers.", self.peer_info, peers.len(), direct_peers.len()); + self.send_message_or_log(&PeerMessage::PeersResponse(PeersResponse { + peers, + direct_peers, + })); + } + message_processed_event(); + } + PeerMessage::PeersResponse(PeersResponse { peers, direct_peers }) => { + tracing::debug!(target: "network", "Received peers from {}: {} peers and {} direct peers.", self.peer_info, peers.len(), direct_peers.len()); + + // Check for abusive behavior (sending too many peers) + if peers.len() > PEERS_RESPONSE_MAX_PEERS.try_into().unwrap() { + self.stop(ctx, ClosingReason::Ban(ReasonForBan::Abusive)); + } + // Check for abusive behavior (sending too many direct peers) + if direct_peers.len() > MAX_TIER2_PEERS { + self.stop(ctx, ClosingReason::Ban(ReasonForBan::Abusive)); + } + + // Add received peers to the peer store + let node_id = self.network_state.config.node_id(); + self.network_state.peer_store.add_indirect_peers( + &self.clock, + peers.into_iter().filter(|peer_info| peer_info.id != node_id), + ); + // Direct peers of the responding peer are still indirect peers for this node. + // However, we may treat them with more trust in the future. + self.network_state.peer_store.add_indirect_peers( + &self.clock, + direct_peers.into_iter().filter(|peer_info| peer_info.id != node_id), + ); + message_processed_event(); + } + PeerMessage::RequestUpdateNonce(edge_info) => { + let clock = self.clock.clone(); + let network_state = self.network_state.clone(); + ctx.spawn(wrap_future(async move { + let peer_id = &conn.peer_info.id; + match network_state.graph.load().local_edges.get(peer_id) { + Some(cur_edge) + if cur_edge.edge_type() == EdgeState::Active + && cur_edge.nonce() >= edge_info.nonce => + { + // Found a newer local edge, so just send it to the peer. + conn.send_message(Arc::new(PeerMessage::SyncRoutingTable( + RoutingTableUpdate::from_edges(vec![cur_edge.clone()]), + ))); + } + // Sign the edge and broadcast it to everyone (finalize_edge does both). + _ => { + if let Err(ban_reason) = network_state + .finalize_edge(&clock, peer_id.clone(), edge_info) + .await + { + conn.stop(Some(ban_reason)); + } + } + }; + message_processed_event(); + })); + } + PeerMessage::SyncRoutingTable(rtu) => { + let clock = self.clock.clone(); + let conn = conn.clone(); + let network_state = self.network_state.clone(); + ctx.spawn(wrap_future(async move { + Self::handle_sync_routing_table(&clock, &network_state, conn.clone(), rtu) + .await; + message_processed_event(); + })); + } + PeerMessage::DistanceVector(dv) => { + let clock = self.clock.clone(); + let conn = conn.clone(); + let network_state = self.network_state.clone(); + ctx.spawn(wrap_future(async move { + Self::handle_distance_vector(&clock, &network_state, conn.clone(), dv).await; + message_processed_event(); + })); + } + PeerMessage::SyncAccountsData(msg) => { + metrics::SYNC_ACCOUNTS_DATA + .with_label_values(&[ + "received", + metrics::bool_to_str(msg.incremental), + metrics::bool_to_str(msg.requesting_full_sync), + ]) + .inc(); + let network_state = self.network_state.clone(); + // In case a full sync is requested, immediately send what we got. + // It is a microoptimization: we do not send back the data we just received. + if msg.requesting_full_sync { + self.send_message_or_log(&PeerMessage::SyncAccountsData(SyncAccountsData { + requesting_full_sync: false, + incremental: false, + accounts_data: network_state + .accounts_data + .load() + .data + .values() + .cloned() + .collect(), + })); + } + // Early exit, if there is no data in the message. + if msg.accounts_data.is_empty() { + message_processed_event(); + return; + } + let network_state = self.network_state.clone(); + let clock = self.clock.clone(); + ctx.spawn(wrap_future(async move { + if let Some(err) = + network_state.add_accounts_data(&clock, msg.accounts_data).await + { + conn.stop(Some(match err { + AccountDataError::InvalidSignature => ReasonForBan::InvalidSignature, + AccountDataError::DataTooLarge => ReasonForBan::Abusive, + AccountDataError::SingleAccountMultipleData => ReasonForBan::Abusive, + })); + } + message_processed_event(); + })); + } + PeerMessage::SyncSnapshotHosts(msg) => { + metrics::SYNC_SNAPSHOT_HOSTS.with_label_values(&["received"]).inc(); + // Early exit, if there is no data in the message. + if msg.hosts.is_empty() { + message_processed_event(); + return; + } + let network_state = self.network_state.clone(); + ctx.spawn(wrap_future(async move { + if let Some(err) = network_state.add_snapshot_hosts(msg.hosts).await { + conn.stop(Some(match err { + SnapshotHostInfoError::VerificationError( + SnapshotHostInfoVerificationError::InvalidSignature, + ) => ReasonForBan::InvalidSignature, + SnapshotHostInfoError::VerificationError( + SnapshotHostInfoVerificationError::TooManyShards(_), + ) + | SnapshotHostInfoError::DuplicatePeerId => ReasonForBan::Abusive, + })); + } + message_processed_event(); + })); + } + PeerMessage::Routed(mut msg) => { + tracing::trace!( + target: "network", + "Received routed message from {} to {:?}.", + self.peer_info, + msg.target); + let for_me = self.network_state.message_for_me(&msg.target); + if for_me { + // Check if we have already received this message. + let fastest = self + .network_state + .recent_routed_messages + .lock() + .put(CryptoHash::hash_borsh(&msg.body), ()) + .is_none(); + // Register that the message has been received. + metrics::record_routed_msg_metrics(&self.clock, &msg, conn.tier, fastest); + } + + // Drop duplicated messages routed within DROP_DUPLICATED_MESSAGES_PERIOD ms + let key = (msg.author.clone(), msg.target.clone(), msg.signature.clone()); + let now = self.clock.now(); + if let Some(&t) = self.routed_message_cache.get(&key) { + if now <= t + DROP_DUPLICATED_MESSAGES_PERIOD { + metrics::MessageDropped::Duplicate.inc(&msg.body); + self.network_state.config.event_sink.push(Event::RoutedMessageDropped); + tracing::debug!(target: "network", "Dropping duplicated message from {} to {:?}", msg.author, msg.target); + return; + } + } + if let RoutedMessageBody::ForwardTx(_) = &msg.body { + // Check whenever we exceeded number of transactions we got since last block. + // If so, drop the transaction. + let r = self.network_state.txns_since_last_block.load(Ordering::Acquire); + // TODO(gprusak): this constraint doesn't take into consideration such + // parameters as number of nodes or number of shards. Reconsider why do we need + // this and whether this is really the right way of handling it. + if r > MAX_TRANSACTIONS_PER_BLOCK_MESSAGE { + metrics::MessageDropped::TransactionsPerBlockExceeded.inc(&msg.body); + return; + } + self.network_state.txns_since_last_block.fetch_add(1, Ordering::AcqRel); + } + self.routed_message_cache.put(key, now); + + if !msg.verify() { + // Received invalid routed message from peer. + self.stop(ctx, ClosingReason::Ban(ReasonForBan::InvalidSignature)); + return; + } + + self.network_state.add_route_back(&self.clock, &conn, msg.as_ref()); + if for_me { + // Handle Ping and Pong message if they are for us without sending to client. + // i.e. Return false in case of Ping and Pong + match &msg.body { + RoutedMessageBody::Ping(ping) => { + self.network_state.send_pong( + &self.clock, + conn.tier, + ping.nonce, + msg.hash(), + ); + // TODO(gprusak): deprecate Event::Ping/Pong in favor of + // MessageProcessed. + self.network_state.config.event_sink.push(Event::Ping(ping.clone())); + message_processed_event(); + } + RoutedMessageBody::Pong(pong) => { + self.network_state.config.event_sink.push(Event::Pong(pong.clone())); + message_processed_event(); + } + _ => self.receive_message(ctx, &conn, PeerMessage::Routed(msg)), + } + } else { + if msg.decrease_ttl() { + self.network_state.send_message_to_peer(&self.clock, conn.tier, msg); + } else { + self.network_state.config.event_sink.push(Event::RoutedMessageDropped); + tracing::warn!(target: "network", ?msg, from = ?conn.peer_info.id, "Message dropped because TTL reached 0."); + metrics::ROUTED_MESSAGE_DROPPED + .with_label_values(&[msg.body_variant()]) + .inc(); + } + } + } + msg => self.receive_message(ctx, &conn, msg), + } + } + + async fn handle_sync_routing_table( + clock: &time::Clock, + network_state: &Arc, + conn: Arc, + rtu: RoutingTableUpdate, + ) { + let _span = tracing::trace_span!(target: "network", "handle_sync_routing_table").entered(); + if let Err(ban_reason) = network_state.add_edges(&clock, rtu.edges.clone()).await { + conn.stop(Some(ban_reason)); + } + + // Also pass the edges to the V2 routing table + if let Err(ban_reason) = network_state + .update_routes(&clock, NetworkTopologyChange::EdgeNonceRefresh(rtu.edges)) + .await + { + conn.stop(Some(ban_reason)); + } + + // For every announce we received, we fetch the last announce with the same account_id + // that we already broadcasted. Client actor will both verify signatures of the received announces + // as well as filter out those which are older than the fetched ones (to avoid overriding + // a newer announce with an older one). + let old = network_state + .account_announcements + .get_broadcasted_announcements(rtu.accounts.iter().map(|a| &a.account_id)); + let accounts: Vec<(AnnounceAccount, Option)> = rtu + .accounts + .into_iter() + .map(|aa| { + let id = aa.account_id.clone(); + (aa, old.get(&id).map(|old| old.epoch_id.clone())) + }) + .collect(); + match network_state.client.announce_account(accounts).await { + Err(ban_reason) => conn.stop(Some(ban_reason)), + Ok(accounts) => network_state.add_accounts(accounts).await, + } + } + + async fn handle_distance_vector( + clock: &time::Clock, + network_state: &Arc, + conn: Arc, + distance_vector: DistanceVector, + ) { + let _span = tracing::trace_span!(target: "network", "handle_distance_vector").entered(); + + if conn.peer_info.id != distance_vector.root { + conn.stop(Some(ReasonForBan::InvalidDistanceVector)); + return; + } + + if let Err(ban_reason) = network_state + .update_routes(&clock, NetworkTopologyChange::PeerAdvertisedDistances(distance_vector)) + .await + { + conn.stop(Some(ban_reason)); + } + } +} + +impl actix::Actor for PeerActor { + type Context = actix::Context; + + fn started(&mut self, ctx: &mut Self::Context) { + metrics::PEER_CONNECTIONS_TOTAL.inc(); + tracing::debug!(target: "network", "{:?}: Peer {:?} {:?} started", self.my_node_info.id, self.peer_addr, self.peer_type); + // Set Handshake timeout for stopping actor if peer is not ready after given period of time. + + unc_performance_metrics::actix::run_later( + ctx, + self.network_state.config.handshake_timeout.try_into().unwrap(), + move |act, ctx| match act.peer_status { + PeerStatus::Connecting { .. } => { + tracing::info!(target: "network", "Handshake timeout expired for {}", act.peer_info); + act.stop(ctx, ClosingReason::HandshakeFailed); + } + _ => {} + }, + ); + + // If outbound peer, initiate handshake. + if let PeerStatus::Connecting(_, ConnectingStatus::Outbound { handshake_spec, .. }) = + &self.peer_status + { + self.send_handshake(handshake_spec.clone()); + } + self.network_state + .config + .event_sink + .push(Event::HandshakeStarted(HandshakeStartedEvent { stream_id: self.stream_id })); + } + + fn stopping(&mut self, _: &mut Self::Context) -> actix::Running { + actix::Running::Stop + } + + fn stopped(&mut self, _ctx: &mut Self::Context) { + // closing_reason may be None in case the whole actix system is stopped. + // It happens a lot in tests. + metrics::PEER_CONNECTIONS_TOTAL.dec(); + match &self.closing_reason { + None => { + // Due to Actix semantics, sometimes closing reason may be not set. + // But it is only expected to happen in tests. + tracing::error!(target:"network", "closing reason not set. This should happen only in tests."); + } + Some(reason) => { + tracing::info!(target: "network", "{:?}: Peer {} disconnected, reason: {reason}", self.my_node_info.id, self.peer_info); + + // If we are on the inbound side of the connection, set a flag in the disconnect + // message advising the outbound side whether to attempt to re-establish the connection. + let remove_from_connection_store = + self.peer_type == PeerType::Inbound && reason.remove_from_connection_store(); + + self.send_message_or_log(&PeerMessage::Disconnect(Disconnect { + remove_from_connection_store, + })); + } + } + + match &self.peer_status { + // If PeerActor is in Connecting state, then + // it was not registered in the NetworkState, + // so there is nothing to be done. + PeerStatus::Connecting(..) => { + // TODO(gprusak): reporting ConnectionClosed event is quite scattered right now and + // it is very ugly: it may happen here, in spawn_inner, or in NetworkState::unregister(). + // Centralize it, once we get rid of actix. + self.network_state.config.event_sink.push(Event::ConnectionClosed( + ConnectionClosedEvent { + stream_id: self.stream_id, + reason: self.closing_reason.clone().unwrap_or(ClosingReason::Unknown), + }, + )); + } + // Clean up the Connection from the NetworkState. + PeerStatus::Ready(conn) => { + let network_state = self.network_state.clone(); + let clock = self.clock.clone(); + let conn = conn.clone(); + network_state.unregister( + &clock, + &conn, + self.stream_id, + self.closing_reason.clone().unwrap_or(ClosingReason::Unknown), + ); + } + } + actix::Arbiter::current().stop(); + } +} + +impl actix::Handler for PeerActor { + type Result = (); + #[perf] + fn handle(&mut self, err: stream::Error, ctx: &mut Self::Context) { + let expected = match &err { + stream::Error::Recv(stream::RecvError::MessageTooLarge { .. }) => { + self.stop(ctx, ClosingReason::Ban(ReasonForBan::Abusive)); + true + } + // It is expected in a sense that the peer might be just slow. + stream::Error::Send(stream::SendError::QueueOverflow { .. }) => true, + stream::Error::Recv(stream::RecvError::IO(err)) + | stream::Error::Send(stream::SendError::IO(err)) => match err.kind() { + // Connection has been closed. + io::ErrorKind::UnexpectedEof + | io::ErrorKind::ConnectionReset + | io::ErrorKind::BrokenPipe + // libc::ETIIMEDOUT = 110, translates to io::ErrorKind::TimedOut. + | io::ErrorKind::TimedOut => true, + // When stopping tokio runtime, an "IO driver has terminated" is sometimes + // returned. + io::ErrorKind::Other => true, + // It is unexpected in a sense that stream got broken in an unexpected way. + // In case you encounter an error that was actually to be expected, + // please add it here and document. + _ => false, + }, + }; + log_assert!(expected, "unexpected closing reason: {err}"); + tracing::info!(target: "network", ?err, "Closing connection to {}", self.peer_info); + self.stop(ctx, ClosingReason::StreamError); + } +} + +impl actix::Handler for PeerActor { + type Result = (); + #[perf] + fn handle(&mut self, stream::Frame(msg): stream::Frame, ctx: &mut Self::Context) { + let _span = tracing::debug_span!( + target: "network", + "handle", + handler = "bytes", + actor = "PeerActor", + msg_len = msg.len(), + peer = %self.peer_info) + .entered(); + + if self.closing_reason.is_some() { + tracing::warn!(target: "network", "Received message from closing connection {:?}. Ignoring", self.peer_type); + return; + } + + // Message type agnostic stats. + { + metrics::PEER_DATA_RECEIVED_BYTES.inc_by(msg.len() as u64); + tracing::trace!(target: "network", msg_len=msg.len()); + self.tracker.lock().increment_received(&self.clock, msg.len() as u64); + } + + let mut peer_msg = match self.parse_message(&msg) { + Ok(msg) => msg, + Err(err) => { + tracing::debug!(target: "network", "Received invalid data {} from {}: {}", unc_fmt::AbbrBytes(&msg), self.peer_info, err); + return; + } + }; + + tracing::trace!(target: "network", "Received message: {}", peer_msg); + + { + let labels = [peer_msg.msg_variant()]; + metrics::PEER_MESSAGE_RECEIVED_BY_TYPE_TOTAL.with_label_values(&labels).inc(); + metrics::PEER_MESSAGE_RECEIVED_BY_TYPE_BYTES + .with_label_values(&labels) + .inc_by(msg.len() as u64); + } + match &self.peer_status { + PeerStatus::Connecting { .. } => self.handle_msg_connecting(ctx, peer_msg), + PeerStatus::Ready(conn) => { + if self.closing_reason.is_some() { + tracing::warn!(target: "network", "Received {} from closing connection {:?}. Ignoring", peer_msg, self.peer_type); + return; + } + conn.last_time_received_message.store(self.clock.now()); + // Check if the message type is allowed given the TIER of the connection: + // TIER1 connections are reserved exclusively for BFT consensus messages. + if !conn.tier.is_allowed(&peer_msg) { + tracing::warn!(target: "network", "Received {} on {:?} connection, disconnecting",peer_msg.msg_variant(),conn.tier); + // TODO(gprusak): this is abusive behavior. Consider banning for it. + self.stop(ctx, ClosingReason::DisallowedMessage); + return; + } + + // Optionally, ignore any received tombstones after startup. This is to + // prevent overload from too much accumulated deleted edges. + // + // We have similar code to skip sending tombstones, here we handle the + // case when our peer doesn't use that logic yet. + if let Some(skip_tombstones) = self.network_state.config.skip_tombstones { + if let PeerMessage::SyncRoutingTable(routing_table) = &mut peer_msg { + if conn.established_time + skip_tombstones > self.clock.now() { + routing_table + .edges + .retain(|edge| edge.edge_type() == EdgeState::Active); + metrics::EDGE_TOMBSTONE_RECEIVING_SKIPPED.inc(); + } + } + } + // Handle the message. + self.handle_msg_ready(ctx, conn.clone(), peer_msg); + } + } + } +} + +impl actix::Handler> for PeerActor { + type Result = (); + + #[perf] + fn handle(&mut self, msg: WithSpanContext, _: &mut Self::Context) { + let (_span, msg) = handler_debug_span!(target: "network", msg); + self.send_message_or_log(&msg.message); + } +} + +/// Messages from PeerManager to Peer +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub(crate) struct Stop { + pub ban_reason: Option, +} + +impl actix::Handler> for PeerActor { + type Result = (); + + #[perf] + fn handle(&mut self, msg: WithSpanContext, ctx: &mut Self::Context) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "network", msg); + self.stop( + ctx, + match msg.ban_reason { + Some(reason) => ClosingReason::Ban(reason), + None => ClosingReason::PeerManagerRequest, + }, + ); + } +} + +type InboundHandshakePermit = tokio::sync::OwnedSemaphorePermit; + +#[derive(Debug)] +enum ConnectingStatus { + Inbound(InboundHandshakePermit), + Outbound { _permit: connection::OutboundHandshakePermit, handshake_spec: HandshakeSpec }, +} + +/// State machine of the PeerActor. +/// The transition graph for inbound connection is: +/// Connecting(Inbound) -> Ready +/// for outbound connection is: +/// Connecting(Outbound) -> Ready +/// +/// From every state the PeerActor can be immediately shut down. +/// In the Connecting state only Handshake-related messages are allowed. +/// All the other messages can be exchanged only in the Ready state. +/// +/// For the exact process of establishing a connection between peers, +/// see PoolSnapshot in chain/network/src/peer_manager/connection.rs. +#[derive(Debug)] +enum PeerStatus { + /// Handshake in progress. + Connecting(HandshakeSignalSender, ConnectingStatus), + /// Ready to go. + Ready(Arc), +} diff --git a/chain/network/src/peer/stream.rs b/chain/network/src/peer/stream.rs new file mode 100644 index 000000000..e01c5f978 --- /dev/null +++ b/chain/network/src/peer/stream.rs @@ -0,0 +1,215 @@ +use crate::peer_manager::connection; +use crate::stats::metrics; +use crate::tcp; +use actix::fut::future::wrap_future; +use actix::AsyncContext as _; +use bytesize::{GIB, MIB}; +use std::io; +use std::net::SocketAddr; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use tokio::io::AsyncReadExt as _; +use tokio::io::AsyncWriteExt as _; + +/// Maximum size of network message in encoded format. +/// We encode length as `u32`, and therefore maximum size can't be larger than `u32::MAX`. +const NETWORK_MESSAGE_MAX_SIZE_BYTES: usize = 512 * MIB as usize; +/// Maximum capacity of write buffer in bytes. +const MAX_WRITE_BUFFER_CAPACITY_BYTES: usize = GIB as usize; + +type ReadHalf = tokio::io::ReadHalf; +type WriteHalf = tokio::io::WriteHalf; + +#[derive(thiserror::Error, Debug)] +pub(crate) enum SendError { + #[error("IO error: {0}")] + IO(#[source] io::Error), + #[error("queue is full, got {got_bytes}B, max capacity is {want_max_bytes}")] + QueueOverflow { got_bytes: usize, want_max_bytes: usize }, +} + +#[derive(thiserror::Error, Debug)] +pub(crate) enum RecvError { + #[error("IO error")] + IO(#[source] io::Error), + #[error("message too large: got {got_bytes}B, want <={want_max_bytes}B")] + MessageTooLarge { got_bytes: usize, want_max_bytes: usize }, +} + +#[derive(actix::Message, PartialEq, Eq, Clone, Debug)] +#[rtype(result = "()")] +pub(crate) struct Frame(pub Vec); + +/// Stream critical error. +/// Actor is responsible for calling ctx.stop() after receiving stream::Error. +/// Actor might receive more than 1 stream::Error, but should call ctx.stop() just after the +/// first one. +/// WARNING: send/recv loops might not get closed if Actor won't call ctx.stop()!. +// TODO(gprusak): once we implement cancellation support for structured concurrency, +// send/recv loops should be cancelled and return before the error is reported to Actor. +#[derive(thiserror::Error, Debug, actix::Message)] +#[rtype(result = "()")] +pub(crate) enum Error { + #[error("send: {0}")] + Send(#[source] SendError), + #[error("recv: {0}")] + Recv(#[source] RecvError), +} + +pub(crate) struct FramedStream { + queue_send: tokio::sync::mpsc::UnboundedSender, + stats: Arc, + send_buf_size_metric: Arc, + addr: actix::Addr, +} + +impl FramedStream +where + Actor: actix::Actor> + + actix::Handler + + actix::Handler, +{ + pub fn spawn( + ctx: &mut actix::Context, + stream: tcp::Stream, + stats: Arc, + ) -> Self { + let (tcp_recv, tcp_send) = tokio::io::split(stream.stream); + let (queue_send, queue_recv) = tokio::sync::mpsc::unbounded_channel(); + let send_buf_size_metric = Arc::new(metrics::MetricGuard::new( + &*metrics::PEER_DATA_WRITE_BUFFER_SIZE, + vec![stream.peer_addr.to_string()], + )); + ctx.spawn(wrap_future({ + let addr = ctx.address(); + let stats = stats.clone(); + let m = send_buf_size_metric.clone(); + async move { + if let Err(err) = Self::run_send_loop(tcp_send, queue_recv, stats, m).await { + addr.do_send(Error::Send(SendError::IO(err))); + } + } + })); + ctx.spawn(wrap_future({ + let addr = ctx.address(); + let stats = stats.clone(); + async move { + if let Err(err) = + Self::run_recv_loop(stream.peer_addr, tcp_recv, addr.clone(), stats).await + { + addr.do_send(Error::Recv(err)); + } + } + })); + Self { queue_send, stats, send_buf_size_metric, addr: ctx.address() } + } + + /// Pushes `msg` to the send queue. + /// Silently drops message if the connection has been closed. + /// If the message is too large, it will be silently dropped inside run_send_loop. + /// Emits a critical error to Actor if send queue is full. + pub fn send(&self, frame: Frame) { + let msg = &frame.0; + let mut buf_size = + self.stats.bytes_to_send.fetch_add(msg.len() as u64, Ordering::Acquire) as usize; + buf_size += msg.len(); + self.stats.messages_to_send.fetch_add(1, Ordering::Acquire); + self.send_buf_size_metric.add(msg.len() as i64); + // Exceeding buffer capacity is a critical error and Actor should call ctx.stop() + // when receiving one. It is not like we do any extra allocations, so we can affort + // pushing the message to the queue anyway. + if buf_size > MAX_WRITE_BUFFER_CAPACITY_BYTES { + metrics::MessageDropped::MaxCapacityExceeded.inc_unknown_msg(); + self.addr.do_send(Error::Send(SendError::QueueOverflow { + got_bytes: buf_size, + want_max_bytes: MAX_WRITE_BUFFER_CAPACITY_BYTES, + })); + } + let _ = self.queue_send.send(frame); + } + + /// Event loop receiving and processing messages. + /// Loop waits for the message to be processed before reading the next message. + /// Note that if the message handler spawns an asynchronous subhandler and returns, + /// then the loop will start reading the next message before the subhandler returns. + /// Loop uses a fixed small buffer allocated by BufReader. + /// For each message it allocates a Vec with exact size of the message. + // TODO(gprusak): once borsh support is dropped, we can parse a proto + // directly from the stream. + async fn run_recv_loop( + peer_addr: SocketAddr, + read: ReadHalf, + addr: actix::Addr, + stats: Arc, + ) -> Result<(), RecvError> { + const READ_BUFFER_CAPACITY: usize = 8 * 1024; + let mut read = tokio::io::BufReader::with_capacity(READ_BUFFER_CAPACITY, read); + + let msg_size_metric = + metrics::MetricGuard::new(&metrics::PEER_MSG_SIZE_BYTES, vec![peer_addr.to_string()]); + let buf_size_metric = metrics::MetricGuard::new( + &metrics::PEER_DATA_READ_BUFFER_SIZE, + vec![peer_addr.to_string()], + ); + loop { + let n = read.read_u32_le().await.map_err(RecvError::IO)? as usize; + if n > NETWORK_MESSAGE_MAX_SIZE_BYTES { + return Err(RecvError::MessageTooLarge { + got_bytes: n, + want_max_bytes: NETWORK_MESSAGE_MAX_SIZE_BYTES, + }); + } + msg_size_metric.observe(n as f64); + buf_size_metric.set(n as i64); + let mut buf = vec![0; n]; + let t = metrics::PEER_MSG_READ_LATENCY.start_timer(); + read.read_exact(&mut buf[..]).await.map_err(RecvError::IO)?; + t.observe_duration(); + buf_size_metric.set(0); + stats.received_messages.fetch_add(1, Ordering::Relaxed); + stats.received_bytes.fetch_add(n as u64, Ordering::Relaxed); + if let Err(_) = addr.send(Frame(buf)).await { + // We got mailbox error, which means that Actor has stopped, + // so we should just close the stream. + return Ok(()); + } + } + } + async fn run_send_loop( + tcp_send: WriteHalf, + mut queue_recv: tokio::sync::mpsc::UnboundedReceiver, + stats: Arc, + buf_size_metric: Arc, + ) -> io::Result<()> { + const WRITE_BUFFER_CAPACITY: usize = 8 * 1024; + let mut writer = tokio::io::BufWriter::with_capacity(WRITE_BUFFER_CAPACITY, tcp_send); + while let Some(Frame(mut msg)) = queue_recv.recv().await { + // Try writing a batch of messages and flush once at the end. + loop { + // TODO(gprusak): sending a too large message should probably be treated as a bug, + // since dropping messages may lead to hard-to-debug high-level issues. + if msg.len() > NETWORK_MESSAGE_MAX_SIZE_BYTES { + metrics::MessageDropped::InputTooLong.inc_unknown_msg(); + } else { + writer.write_u32_le(msg.len() as u32).await?; + writer.write_all(&msg[..]).await?; + } + stats.messages_to_send.fetch_sub(1, Ordering::Release); + stats.bytes_to_send.fetch_sub(msg.len() as u64, Ordering::Release); + buf_size_metric.sub(msg.len() as i64); + msg = match queue_recv.try_recv() { + Ok(Frame(it)) => it, + Err(_) => break, + }; + } + // This is an unconditional flush, which means that even if new messages + // will be added to the queue in the meantime, we will wait for the buffer + // to be flushed before sending them. This is suboptimal in case messages are small + // and added to the queue at a rate similar to flush latency. To fix that + // we would need to put writer.flush() and queue_recv.recv() into a tokio::select + // and make sure that both are cancellation-safe. + writer.flush().await?; + } + Ok(()) + } +} diff --git a/chain/network/src/peer/testonly.rs b/chain/network/src/peer/testonly.rs new file mode 100644 index 000000000..8aa84640c --- /dev/null +++ b/chain/network/src/peer/testonly.rs @@ -0,0 +1,121 @@ +use crate::broadcast; +use crate::config::NetworkConfig; +use crate::network_protocol::testonly as data; +use crate::network_protocol::{ + Edge, PartialEdgeInfo, PeerIdOrHash, PeerMessage, RawRoutedMessage, RoutedMessageBody, + RoutedMessageV2, +}; +use crate::peer::peer_actor::PeerActor; +use crate::peer_manager::network_state::NetworkState; +use crate::peer_manager::peer_manager_actor; +use crate::peer_manager::peer_store; +use crate::private_actix::SendMessage; +use crate::store; +use crate::tcp; +use crate::testonly::actix::ActixSystem; +use crate::testonly::fake_client; +use unc_async::messaging::IntoSender; +use unc_async::time; +use unc_o11y::WithSpanContextExt; +use unc_primitives::network::PeerId; +use std::sync::Arc; + +pub struct PeerConfig { + pub chain: Arc, + pub network: NetworkConfig, + pub force_encoding: Option, +} + +impl PeerConfig { + pub fn id(&self) -> PeerId { + self.network.node_id() + } + + pub fn partial_edge_info(&self, other: &PeerId, nonce: u64) -> PartialEdgeInfo { + PartialEdgeInfo::new(&self.id(), other, nonce, &self.network.node_key) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum Event { + Client(fake_client::Event), + Network(peer_manager_actor::Event), +} + +pub(crate) struct PeerHandle { + pub cfg: Arc, + actix: ActixSystem, + pub events: broadcast::Receiver, + pub edge: Option, +} + +impl PeerHandle { + pub async fn send(&self, message: PeerMessage) { + self.actix + .addr + .send(SendMessage { message: Arc::new(message) }.with_span_context()) + .await + .unwrap(); + } + + pub async fn complete_handshake(&mut self) { + self.edge = Some( + self.events + .recv_until(|ev| match ev { + Event::Network(peer_manager_actor::Event::HandshakeCompleted(ev)) => { + Some(ev.edge) + } + Event::Network(peer_manager_actor::Event::ConnectionClosed(ev)) => { + panic!("handshake failed: {}", ev.reason) + } + _ => None, + }) + .await, + ); + } + + pub fn routed_message( + &self, + body: RoutedMessageBody, + peer_id: PeerId, + ttl: u8, + utc: Option, + ) -> RoutedMessageV2 { + RawRoutedMessage { target: PeerIdOrHash::PeerId(peer_id), body }.sign( + &self.cfg.network.node_key, + ttl, + utc, + ) + } + + pub async fn start_endpoint( + clock: time::Clock, + cfg: PeerConfig, + stream: tcp::Stream, + ) -> PeerHandle { + let cfg = Arc::new(cfg); + let (send, recv) = broadcast::unbounded_channel(); + + let fc = Arc::new(fake_client::Fake { event_sink: send.sink().compose(Event::Client) }); + let store = store::Store::from(unc_store::db::TestDB::new()); + let mut network_cfg = cfg.network.clone(); + network_cfg.event_sink = send.sink().compose(Event::Network); + let network_state = Arc::new(NetworkState::new( + &clock, + store.clone(), + peer_store::PeerStore::new(&clock, network_cfg.peer_store.clone()).unwrap(), + network_cfg.verify().unwrap(), + cfg.chain.genesis_id.clone(), + fc.clone(), + fc.as_sender(), + vec![], + )); + let actix = ActixSystem::spawn({ + let clock = clock.clone(); + let cfg = cfg.clone(); + move || PeerActor::spawn(clock, stream, cfg.force_encoding, network_state).unwrap().0 + }) + .await; + Self { actix, cfg, events: recv, edge: None } + } +} diff --git a/chain/network/src/peer/tests/communication.rs b/chain/network/src/peer/tests/communication.rs new file mode 100644 index 000000000..842725f9a --- /dev/null +++ b/chain/network/src/peer/tests/communication.rs @@ -0,0 +1,268 @@ +use crate::network_protocol::testonly as data; +use crate::network_protocol::{ + Encoding, Handshake, HandshakeFailureReason, PartialEdgeInfo, PeerMessage, PeersRequest, + PeersResponse, RoutedMessageBody, +}; +use crate::peer::testonly::{Event, PeerConfig, PeerHandle}; +use crate::peer_manager::peer_manager_actor::Event as PME; +use crate::tcp; +use crate::testonly::make_rng; +use crate::testonly::stream::Stream; +use crate::types::{PartialEncodedChunkRequestMsg, PartialEncodedChunkResponseMsg}; +use anyhow::Context as _; +use assert_matches::assert_matches; +use unc_async::time; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::version::{PEER_MIN_ALLOWED_PROTOCOL_VERSION, PROTOCOL_VERSION}; +use std::sync::Arc; + +async fn test_peer_communication( + outbound_encoding: Option, + inbound_encoding: Option, +) -> anyhow::Result<()> { + tracing::info!("test_peer_communication({outbound_encoding:?},{inbound_encoding:?})"); + + let mut rng = make_rng(89028037453); + let mut clock = time::FakeClock::default(); + + let chain = Arc::new(data::Chain::make(&mut clock, &mut rng, 12)); + let inbound_cfg = PeerConfig { + chain: chain.clone(), + network: chain.make_config(&mut rng), + force_encoding: inbound_encoding, + }; + let outbound_cfg = PeerConfig { + chain: chain.clone(), + network: chain.make_config(&mut rng), + force_encoding: outbound_encoding, + }; + let (outbound_stream, inbound_stream) = + tcp::Stream::loopback(inbound_cfg.id(), tcp::Tier::T2).await; + let mut inbound = PeerHandle::start_endpoint(clock.clock(), inbound_cfg, inbound_stream).await; + let mut outbound = + PeerHandle::start_endpoint(clock.clock(), outbound_cfg, outbound_stream).await; + + outbound.complete_handshake().await; + inbound.complete_handshake().await; + + let message_processed = |want| { + move |ev| match ev { + Event::Network(PME::MessageProcessed(_, got)) if got == want => Some(()), + _ => None, + } + }; + + tracing::info!(target:"test","RequestUpdateNonce"); + let mut events = inbound.events.from_now(); + let want = PeerMessage::RequestUpdateNonce(PartialEdgeInfo::new( + &outbound.cfg.network.node_id(), + &inbound.cfg.network.node_id(), + 15, + &outbound.cfg.network.node_key, + )); + outbound.send(want.clone()).await; + events.recv_until(message_processed(want)).await; + + tracing::info!(target:"test","PeersRequest"); + let mut events = inbound.events.from_now(); + let want = PeerMessage::PeersRequest(PeersRequest { max_peers: None, max_direct_peers: None }); + outbound.send(want.clone()).await; + events.recv_until(message_processed(want)).await; + + tracing::info!(target:"test","PeersResponse"); + let mut events = inbound.events.from_now(); + let want = PeerMessage::PeersResponse(PeersResponse { + peers: (0..5).map(|_| data::make_peer_info(&mut rng)).collect(), + direct_peers: vec![], + }); + outbound.send(want.clone()).await; + events.recv_until(message_processed(want)).await; + + tracing::info!(target:"test","BlockRequest"); + let mut events = inbound.events.from_now(); + let want = PeerMessage::BlockRequest(*chain.blocks[5].hash()); + outbound.send(want.clone()).await; + events.recv_until(message_processed(want)).await; + + tracing::info!(target:"test","Block"); + let mut events = inbound.events.from_now(); + let want = PeerMessage::Block(chain.blocks[5].clone()); + outbound.send(want.clone()).await; + events.recv_until(message_processed(want)).await; + + tracing::info!(target:"test","BlockHeadersRequest"); + let mut events = inbound.events.from_now(); + let want = PeerMessage::BlockHeadersRequest(chain.blocks.iter().map(|b| *b.hash()).collect()); + outbound.send(want.clone()).await; + events.recv_until(message_processed(want)).await; + + tracing::info!(target:"test","BlockHeaders"); + let mut events = inbound.events.from_now(); + let want = PeerMessage::BlockHeaders(chain.get_block_headers()); + outbound.send(want.clone()).await; + events.recv_until(message_processed(want)).await; + + tracing::info!(target:"test","SyncRoutingTable"); + let mut events = inbound.events.from_now(); + let want = PeerMessage::SyncRoutingTable(data::make_routing_table(&mut rng)); + outbound.send(want.clone()).await; + events.recv_until(message_processed(want)).await; + + tracing::info!(target:"test","PartialEncodedChunkRequest"); + let mut events = inbound.events.from_now(); + let want = PeerMessage::Routed(Box::new(outbound.routed_message( + RoutedMessageBody::PartialEncodedChunkRequest(PartialEncodedChunkRequestMsg { + chunk_hash: chain.blocks[5].chunks()[2].chunk_hash(), + part_ords: vec![], + tracking_shards: Default::default(), + }), + inbound.cfg.id(), + 1, // ttl + None, // TODO(gprusak): this should be clock.now_utc(), once borsh support is dropped. + ))); + outbound.send(want.clone()).await; + events.recv_until(message_processed(want)).await; + + tracing::info!(target:"test","PartialEncodedChunkResponse"); + let mut events = inbound.events.from_now(); + let want_hash = chain.blocks[3].chunks()[0].chunk_hash(); + let want_parts = data::make_chunk_parts(chain.chunks[&want_hash].clone()); + let want = PeerMessage::Routed(Box::new(outbound.routed_message( + RoutedMessageBody::PartialEncodedChunkResponse(PartialEncodedChunkResponseMsg { + chunk_hash: want_hash, + parts: want_parts.clone(), + receipts: vec![], + }), + inbound.cfg.id(), + 1, // ttl + None, // TODO(gprusak): this should be clock.now_utc(), once borsh support is dropped. + ))); + outbound.send(want.clone()).await; + events.recv_until(message_processed(want)).await; + + tracing::info!(target:"test","Transaction"); + let mut events = inbound.events.from_now(); + let want = data::make_signed_transaction(&mut rng); + let want = PeerMessage::Transaction(want); + outbound.send(want.clone()).await; + events.recv_until(message_processed(want)).await; + + tracing::info!(target:"test","Challenge"); + let mut events = inbound.events.from_now(); + let want = PeerMessage::Challenge(data::make_challenge(&mut rng)); + outbound.send(want.clone()).await; + events.recv_until(message_processed(want)).await; + + // TODO: + // LastEdge, HandshakeFailure, Disconnect - affect the state of the PeerActor and are + // observable only under specific conditions. + Ok(()) +} + +#[tokio::test] +// Verifies that peers are able to establish a common encoding protocol. +async fn peer_communication() -> anyhow::Result<()> { + init_test_logger(); + let encodings = [None, Some(Encoding::Proto), Some(Encoding::Borsh)]; + for outbound in &encodings { + for inbound in &encodings { + if let (Some(a), Some(b)) = (outbound, inbound) { + if a != b { + continue; + } + } + test_peer_communication(*outbound, *inbound) + .await + .with_context(|| format!("(outbound={outbound:?},inbound={inbound:?})"))?; + } + } + Ok(()) +} + +async fn test_handshake(outbound_encoding: Option, inbound_encoding: Option) { + let mut rng = make_rng(89028037453); + let mut clock = time::FakeClock::default(); + + let chain = Arc::new(data::Chain::make(&mut clock, &mut rng, 12)); + let inbound_cfg = PeerConfig { + network: chain.make_config(&mut rng), + chain: chain.clone(), + force_encoding: inbound_encoding, + }; + let outbound_cfg = PeerConfig { + network: chain.make_config(&mut rng), + chain: chain.clone(), + force_encoding: outbound_encoding, + }; + let (outbound_stream, inbound_stream) = + tcp::Stream::loopback(inbound_cfg.id(), tcp::Tier::T2).await; + let inbound = PeerHandle::start_endpoint(clock.clock(), inbound_cfg, inbound_stream).await; + let outbound_port = outbound_stream.local_addr.port(); + let mut outbound = Stream::new(outbound_encoding, outbound_stream); + + // Send too old PROTOCOL_VERSION, expect ProtocolVersionMismatch + let mut handshake = Handshake { + protocol_version: PEER_MIN_ALLOWED_PROTOCOL_VERSION - 1, + oldest_supported_version: PEER_MIN_ALLOWED_PROTOCOL_VERSION - 1, + sender_peer_id: outbound_cfg.id(), + target_peer_id: inbound.cfg.id(), + sender_listen_port: Some(outbound_port), + sender_chain_info: outbound_cfg.chain.get_peer_chain_info(), + partial_edge_info: outbound_cfg.partial_edge_info(&inbound.cfg.id(), 1), + owned_account: None, + }; + // We will also introduce chain_id mismatch, but ProtocolVersionMismatch is expected to take priority. + handshake.sender_chain_info.genesis_id.chain_id = "unknown_chain".to_string(); + outbound.write(&PeerMessage::Tier2Handshake(handshake.clone())).await; + let resp = outbound.read().await.unwrap(); + assert_matches!( + resp, + PeerMessage::HandshakeFailure(_, HandshakeFailureReason::ProtocolVersionMismatch { .. }) + ); + + // Send too new PROTOCOL_VERSION, expect ProtocolVersionMismatch + handshake.protocol_version = PROTOCOL_VERSION + 1; + handshake.oldest_supported_version = PROTOCOL_VERSION + 1; + outbound.write(&PeerMessage::Tier2Handshake(handshake.clone())).await; + let resp = outbound.read().await.unwrap(); + assert_matches!( + resp, + PeerMessage::HandshakeFailure(_, HandshakeFailureReason::ProtocolVersionMismatch { .. }) + ); + + // Send mismatching chain_id, expect GenesisMismatch. + // We fix protocol_version, but chain_id is still mismatching. + handshake.protocol_version = PROTOCOL_VERSION; + handshake.oldest_supported_version = PROTOCOL_VERSION; + outbound.write(&PeerMessage::Tier2Handshake(handshake.clone())).await; + let resp = outbound.read().await.unwrap(); + assert_matches!( + resp, + PeerMessage::HandshakeFailure(_, HandshakeFailureReason::GenesisMismatch(_)) + ); + + // Send a correct Handshake, expect a matching Handshake response. + handshake.sender_chain_info = chain.get_peer_chain_info(); + outbound.write(&PeerMessage::Tier2Handshake(handshake.clone())).await; + let resp = outbound.read().await.unwrap(); + assert_matches!(resp, PeerMessage::Tier2Handshake(_)); +} + +#[tokio::test] +// Verifies that HandshakeFailures are served correctly. +async fn handshake() -> anyhow::Result<()> { + init_test_logger(); + let encodings = [None, Some(Encoding::Proto), Some(Encoding::Borsh)]; + for outbound in &encodings { + for inbound in &encodings { + println!("oubound = {:?}, inbound = {:?}", outbound, inbound); + if let (Some(a), Some(b)) = (outbound, inbound) { + if a != b { + continue; + } + } + test_handshake(*outbound, *inbound).await; + } + } + Ok(()) +} diff --git a/chain/network/src/peer/tests/mod.rs b/chain/network/src/peer/tests/mod.rs new file mode 100644 index 000000000..fd35dbe7b --- /dev/null +++ b/chain/network/src/peer/tests/mod.rs @@ -0,0 +1,2 @@ +mod communication; +mod stream; diff --git a/chain/network/src/peer/tests/stream.rs b/chain/network/src/peer/tests/stream.rs new file mode 100644 index 000000000..a28e5da41 --- /dev/null +++ b/chain/network/src/peer/tests/stream.rs @@ -0,0 +1,92 @@ +use crate::actix::ActixSystem; +use crate::network_protocol::testonly as data; +use crate::peer::stream; +use crate::tcp; +use crate::testonly::make_rng; +use actix::Actor as _; +use actix::ActorContext as _; +use rand::Rng as _; +use std::sync::Arc; +use tokio::sync::mpsc; + +struct Actor { + stream: stream::FramedStream, + queue_send: mpsc::UnboundedSender, +} + +impl actix::Actor for Actor { + type Context = actix::Context; +} + +#[derive(actix::Message)] +#[rtype("()")] +struct SendFrame(stream::Frame); + +impl actix::Handler for Actor { + type Result = (); + fn handle(&mut self, SendFrame(frame): SendFrame, _ctx: &mut Self::Context) { + self.stream.send(frame); + } +} + +impl actix::Handler for Actor { + type Result = (); + fn handle(&mut self, frame: stream::Frame, _ctx: &mut Self::Context) { + self.queue_send.send(frame).ok().unwrap(); + } +} + +impl actix::Handler for Actor { + type Result = (); + fn handle(&mut self, _err: stream::Error, ctx: &mut Self::Context) { + ctx.stop(); + } +} + +struct Handler { + queue_recv: mpsc::UnboundedReceiver, + system: ActixSystem, +} + +impl Actor { + async fn spawn(s: tcp::Stream) -> Handler { + let (queue_send, queue_recv) = mpsc::unbounded_channel(); + Handler { + queue_recv, + system: ActixSystem::spawn(|| { + Actor::create(|ctx| { + let stream = stream::FramedStream::spawn(ctx, s, Arc::default()); + Self { stream, queue_send } + }) + }) + .await, + } + } +} + +#[tokio::test] +async fn send_recv() { + let mut rng = make_rng(98324532); + let (s1, s2) = tcp::Stream::loopback(data::make_peer_id(&mut rng), tcp::Tier::T2).await; + let a1 = Actor::spawn(s1).await; + let mut a2 = Actor::spawn(s2).await; + + for _ in 0..5 { + let n = rng.gen_range(1..10); + let msgs: Vec<_> = (0..n) + .map(|_| { + let size = rng.gen_range(0..10000); + let mut msg = vec![0; size]; + rng.fill(&mut msg[..]); + stream::Frame(msg) + }) + .collect(); + for msg in &msgs { + a1.system.addr.send(SendFrame(msg.clone())).await.unwrap(); + } + for want in &msgs { + let got = a2.queue_recv.recv().await.unwrap(); + assert_eq!(&got, want); + } + } +} diff --git a/chain/network/src/peer/tracker.rs b/chain/network/src/peer/tracker.rs new file mode 100644 index 000000000..fbadf18f0 --- /dev/null +++ b/chain/network/src/peer/tracker.rs @@ -0,0 +1,168 @@ +use crate::peer::transfer_stats::TransferStats; +use unc_async::time; +use unc_primitives::hash::CryptoHash; + +/// Maximum number of requests and responses to track. +const MAX_TRACK_SIZE: usize = 30; + +/// Internal structure to keep a circular queue within a tracker with unique hashes. +struct CircularUniqueQueue { + v: Vec, + index: usize, + limit: usize, +} + +impl CircularUniqueQueue { + fn new(limit: usize) -> Self { + assert!(limit > 0); + Self { v: Vec::with_capacity(limit), index: 0, limit } + } + + fn contains(&self, hash: &CryptoHash) -> bool { + self.v.contains(hash) + } + + /// Pushes an element if it's not in the queue already. The queue will pop the oldest element. + fn push(&mut self, hash: CryptoHash) { + if !self.contains(&hash) { + if self.v.len() < self.limit { + self.v.push(hash); + } else { + self.v[self.index] = hash; + self.index += 1; + if self.index == self.limit { + self.index = 0; + } + } + } + } +} + +/// Keeps track of requests and received hashes of transactions and blocks. +/// Also keeps track of number of bytes sent and received from this peer to prevent abuse. +pub(crate) struct Tracker { + /// Bytes we've sent. + pub(crate) sent_bytes: TransferStats, + /// Bytes we've received. + pub(crate) received_bytes: TransferStats, + /// Sent requests. + requested: CircularUniqueQueue, + /// Received elements. + received: CircularUniqueQueue, +} + +impl Default for Tracker { + fn default() -> Self { + Tracker { + sent_bytes: TransferStats::default(), + received_bytes: TransferStats::default(), + requested: CircularUniqueQueue::new(MAX_TRACK_SIZE), + received: CircularUniqueQueue::new(MAX_TRACK_SIZE), + } + } +} + +impl Tracker { + pub(crate) fn increment_received(&mut self, clock: &time::Clock, size: u64) { + self.received_bytes.record(clock, size); + } + + pub(crate) fn increment_sent(&mut self, clock: &time::Clock, size: u64) { + self.sent_bytes.record(clock, size); + } + + // TODO: uncomment this once we add a new message type to sync block height + /* + pub(crate) fn has_received(&self, hash: &CryptoHash) -> bool { + self.received.contains(hash) + } + */ + + pub(crate) fn push_received(&mut self, hash: CryptoHash) { + self.received.push(hash); + } + + pub(crate) fn has_request(&self, hash: &CryptoHash) -> bool { + self.requested.contains(hash) + } + + pub(crate) fn push_request(&mut self, hash: CryptoHash) { + self.requested.push(hash); + } +} + +#[cfg(test)] +mod tests { + use unc_primitives::hash::hash; + + use super::*; + + #[test] + #[should_panic] + fn test_circular_queue_zero_capacity() { + let _ = CircularUniqueQueue::new(0); + } + + #[test] + fn test_circular_queue_empty_queue() { + let q = CircularUniqueQueue::new(5); + + assert!(!q.contains(&hash(&[0]))); + } + + #[test] + fn test_circular_queue_partially_full_queue() { + let mut q = CircularUniqueQueue::new(5); + for i in 1..=3 { + q.push(hash(&[i])); + } + + for i in 1..=3 { + assert!(q.contains(&hash(&[i]))); + } + } + + #[test] + fn test_circular_queue_full_queue() { + let mut q = CircularUniqueQueue::new(5); + for i in 1..=5 { + q.push(hash(&[i])); + } + + for i in 1..=5 { + assert!(q.contains(&hash(&[i]))); + } + } + + #[test] + fn test_circular_queue_over_full_queue() { + let mut q = CircularUniqueQueue::new(5); + for i in 1..=7 { + q.push(hash(&[i])); + } + + for i in 1..=2 { + assert!(!q.contains(&hash(&[i]))); + } + for i in 3..=7 { + assert!(q.contains(&hash(&[i]))); + } + } + + #[test] + fn test_circular_queue_similar_inputs() { + let mut q = CircularUniqueQueue::new(5); + q.push(hash(&[5])); + for _ in 0..3 { + for i in 1..=3 { + for _ in 0..5 { + q.push(hash(&[i])); + } + } + } + for i in 1..=3 { + assert!(q.contains(&hash(&[i]))); + } + assert!(q.contains(&hash(&[5]))); + } +} diff --git a/chain/network/src/peer/transfer_stats.rs b/chain/network/src/peer/transfer_stats.rs new file mode 100644 index 000000000..ef44126c2 --- /dev/null +++ b/chain/network/src/peer/transfer_stats.rs @@ -0,0 +1,124 @@ +use unc_async::time; +/// The purpose of `TransferStats` is to keep track of transfer sizes in done in a period of 1 minute. +/// And then; to provide a summary, the count and total size in bytes when requested. +/// +/// ```rust,ignore +/// use crate::peer::transfer_stats::TransferStats; +/// use std::time::time::Instant; +/// +/// let ts = TransferStats::new(); +/// let start = time::Instant::now(); +/// +/// ts.record(1234, start); +/// +/// let later = time::Instant::now(); +/// println!("{}", ts.minute_stats(later)); +/// ``` +use std::collections::VecDeque; + +/// Defines how long should entries be tracked. +const TRANSFER_STATS_INTERVAL: time::Duration = time::Duration::seconds(60); + +/// Represents a single event in time. +struct Event { + /// Time when event happened. + instant: time::Instant, + /// Number of bytes + bytes: u64, +} + +/// Represents all events which happened in last minute. +#[derive(Default)] +pub(crate) struct TransferStats { + /// We keep list of entries not older than 1m. + /// Events in the queue have timestamps in non-decreasing order. + events: VecDeque, + /// Sum of bytes for all entries. + total_bytes_in_events: u64, +} + +/// Represents cumulative stats per minute. +#[derive(Eq, PartialEq, Debug)] +pub(crate) struct MinuteStats { + /// Bytes per minute. + pub(crate) bytes_per_min: u64, + /// Messages per minute. + pub(crate) count_per_min: usize, +} + +impl TransferStats { + /// Record event at current time `now` with `bytes` bytes. + /// Time in `now` should be monotonically increasing. + pub(crate) fn record(&mut self, clock: &time::Clock, bytes: u64) { + let now = clock.now(); + self.remove_old_entries(now); + self.total_bytes_in_events += bytes; + self.events.push_back(Event { instant: now, bytes }); + } + + /// Get stats stored in `MinuteStats` struct. + pub(crate) fn minute_stats(&mut self, clock: &time::Clock) -> MinuteStats { + self.remove_old_entries(clock.now()); + MinuteStats { bytes_per_min: self.total_bytes_in_events, count_per_min: self.events.len() } + } + + /// Remove entries older than 1m. + fn remove_old_entries(&mut self, now: time::Instant) { + while let Some(event) = self.events.pop_front() { + if now - event.instant > TRANSFER_STATS_INTERVAL { + self.total_bytes_in_events -= event.bytes; + } else { + // add the event back + self.events.push_front(event); + break; + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_transfer_stats() { + let mut ts = TransferStats::default(); + let clock = time::FakeClock::default(); + assert_eq!( + ts.minute_stats(&clock.clock()), + MinuteStats { bytes_per_min: 0, count_per_min: 0 } + ); + + ts.record(&clock.clock(), 10); + + assert_eq!( + ts.minute_stats(&clock.clock()), + MinuteStats { bytes_per_min: 10, count_per_min: 1 } + ); + + clock.advance(time::Duration::seconds(45)); + ts.record(&clock.clock(), 100); + assert_eq!( + ts.minute_stats(&clock.clock()), + MinuteStats { bytes_per_min: 110, count_per_min: 2 } + ); + + clock.advance(time::Duration::seconds(14)); + ts.record(&clock.clock(), 1000); + assert_eq!( + ts.minute_stats(&clock.clock()), + MinuteStats { bytes_per_min: 1110, count_per_min: 3 } + ); + + clock.advance(time::Duration::seconds(2)); + assert_eq!( + ts.minute_stats(&clock.clock()), + MinuteStats { bytes_per_min: 1100, count_per_min: 2 } + ); + + clock.advance(time::Duration::seconds(60)); + assert_eq!( + ts.minute_stats(&clock.clock()), + MinuteStats { bytes_per_min: 0, count_per_min: 0 } + ); + } +} diff --git a/chain/network/src/peer_manager/connection/mod.rs b/chain/network/src/peer_manager/connection/mod.rs new file mode 100644 index 000000000..cfcb243c9 --- /dev/null +++ b/chain/network/src/peer_manager/connection/mod.rs @@ -0,0 +1,498 @@ +use crate::concurrency::arc_mutex::ArcMutex; +use crate::concurrency::atomic_cell::AtomicCell; +use crate::concurrency::demux; +use crate::network_protocol::{ + PeerInfo, PeerMessage, RoutedMessageBody, SignedAccountData, SignedOwnedAccount, + SnapshotHostInfo, SyncAccountsData, SyncSnapshotHosts, +}; +use crate::peer::peer_actor; +use crate::peer::peer_actor::PeerActor; +use crate::private_actix::SendMessage; +use crate::stats::metrics; +use crate::tcp; +use crate::types::{BlockInfo, FullPeerInfo, PeerChainInfo, PeerType, ReasonForBan}; +use arc_swap::ArcSwap; +use unc_async::time; +use unc_crypto::PublicKey; +use unc_o11y::WithSpanContextExt; +use unc_primitives::block::GenesisId; +use unc_primitives::network::PeerId; +use unc_primitives::types::ShardId; +use std::collections::{hash_map::Entry, HashMap}; +use std::fmt; +use std::future::Future; +use std::sync::atomic::AtomicU64; +use std::sync::{Arc, Weak}; + +#[cfg(test)] +mod tests; + +impl tcp::Tier { + /// Checks if the given message type is allowed on a connection of the given Tier. + /// TIER1 is reserved exclusively for BFT consensus messages. + /// Each validator establishes a lot of TIER1 connections, so bandwidth shouldn't be + /// wasted on broadcasting or periodic state syncs on TIER1 connections. + pub(crate) fn is_allowed(self, msg: &PeerMessage) -> bool { + match msg { + PeerMessage::Tier1Handshake(_) => self == tcp::Tier::T1, + PeerMessage::Tier2Handshake(_) => self == tcp::Tier::T2, + PeerMessage::HandshakeFailure(_, _) => true, + PeerMessage::LastEdge(_) => true, + PeerMessage::Routed(msg) => self.is_allowed_routed(&msg.body), + _ => self == tcp::Tier::T2, + } + } + + pub(crate) fn is_allowed_routed(self, body: &RoutedMessageBody) -> bool { + match body { + RoutedMessageBody::BlockApproval(..) => true, + RoutedMessageBody::VersionedPartialEncodedChunk(..) => true, + _ => self == tcp::Tier::T2, + } + } +} + +#[derive(Default)] +pub(crate) struct Stats { + /// Number of messages received since the last reset of the counter. + pub received_messages: AtomicU64, + /// Number of bytes received since the last reset of the counter. + pub received_bytes: AtomicU64, + /// Avg received bytes/s, based on the last few minutes of traffic. + pub received_bytes_per_sec: AtomicU64, + /// Avg sent bytes/s, based on the last few minutes of traffic. + pub sent_bytes_per_sec: AtomicU64, + + /// Number of messages in the buffer to send. + pub messages_to_send: AtomicU64, + /// Number of bytes (sum of message sizes) in the buffer to send. + pub bytes_to_send: AtomicU64, +} + +/// Contains information relevant to a connected peer. +pub(crate) struct Connection { + // TODO(gprusak): add rate limiting on TIER1 connections for defence in-depth. + pub tier: tcp::Tier, + // TODO(gprusak): addr should be internal, so that Connection will become an API of the + // PeerActor. + pub addr: actix::Addr, + + pub peer_info: PeerInfo, + /// AccountKey ownership proof. + pub owned_account: Option, + /// Chain Id and hash of genesis block. + pub genesis_id: GenesisId, + /// Shards that the peer is tracking. + pub tracked_shards: Vec, + /// Denote if a node is running in archival mode or not. + pub archival: bool, + pub last_block: ArcSwap>, + + /// Who started connection. Inbound (other) or Outbound (us). + pub peer_type: PeerType, + /// Time where the connection was established. + pub established_time: time::Instant, + + /// Last time requested peers. + pub last_time_peer_requested: AtomicCell>, + /// Last time we received a message from this peer. + pub last_time_received_message: AtomicCell, + /// Connection stats + pub stats: Arc, + /// prometheus gauge point guard. + pub _peer_connections_metric: metrics::GaugePoint, + + /// Demultiplexer for the calls to send_accounts_data(). + pub send_accounts_data_demux: demux::Demux>, ()>, + /// Demultiplexer for the calls to send_snapshot_hosts(). + pub send_snapshot_hosts_demux: demux::Demux>, ()>, +} + +impl fmt::Debug for Connection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Connection") + .field("peer_info", &self.peer_info) + .field("peer_type", &self.peer_type) + .field("established_time", &self.established_time) + .finish() + } +} + +impl Connection { + pub fn full_peer_info(&self) -> FullPeerInfo { + let chain_info = PeerChainInfo { + genesis_id: self.genesis_id.clone(), + last_block: *self.last_block.load().as_ref(), + tracked_shards: self.tracked_shards.clone(), + archival: self.archival, + }; + FullPeerInfo { peer_info: self.peer_info.clone(), chain_info } + } + + pub fn stop(&self, ban_reason: Option) { + self.addr.do_send(peer_actor::Stop { ban_reason }.with_span_context()); + } + + // TODO(gprusak): embed Stream directly in Connection, + // so that we can skip actix queue when sending messages. + pub fn send_message(&self, msg: Arc) { + let msg_kind = msg.msg_variant().to_string(); + tracing::trace!(target: "network", ?msg_kind, "Send message"); + self.addr.do_send(SendMessage { message: msg }.with_span_context()); + } + + pub fn send_accounts_data( + self: &Arc, + data: Vec>, + ) -> impl Future { + let this = self.clone(); + async move { + let res = this + .send_accounts_data_demux + .call(data, { + let this = this.clone(); + |ds: Vec>>| async move { + let res = ds.iter().map(|_| ()).collect(); + let mut sum = HashMap::<_, Arc>::new(); + for d in ds.into_iter().flatten() { + match sum.entry(d.account_key.clone()) { + Entry::Occupied(mut x) => { + if x.get().version < d.version { + x.insert(d); + } + } + Entry::Vacant(x) => { + x.insert(d); + } + } + } + let msg = Arc::new(PeerMessage::SyncAccountsData(SyncAccountsData { + incremental: true, + requesting_full_sync: false, + accounts_data: sum.into_values().collect(), + })); + this.send_message(msg); + res + } + }) + .await; + if res.is_err() { + tracing::info!( + "peer {} disconnected, while sending SyncAccountsData", + this.peer_info.id + ); + } + } + } + + // Accepts a list of SnapshotHostInfos to be broadcast to all neighbors of the node. + // Multiple calls to this function made in quick succession will be condensed into a + // single outgoing message. + pub fn send_snapshot_hosts( + self: &Arc, + data: Vec>, + ) -> impl Future { + let this = self.clone(); + async move { + // Pass the data through the snapshot_hosts_demux to + // rate-limit the production of network messages. + let res = this + .send_snapshot_hosts_demux + .call(data, { + let this = this.clone(); + // This function describes how to combine multiple vectors of + // SnapshotHostInfos into a single batch of data. + |ds: Vec>>| async move { + let res = ds.iter().map(|_| ()).collect(); + let mut sum = HashMap::<_, Arc>::new(); + for d in ds.into_iter().flatten() { + match sum.entry(d.peer_id.clone()) { + Entry::Occupied(mut x) => { + // If two entries are present for the same peer, + // keep one with the greatest epoch_height. + if x.get().epoch_height < d.epoch_height { + x.insert(d); + } + } + Entry::Vacant(x) => { + x.insert(d); + } + } + } + // Send a single SyncSnapshotHosts message with the condensed data. + let msg = Arc::new(PeerMessage::SyncSnapshotHosts(SyncSnapshotHosts { + hosts: sum.into_values().collect(), + })); + this.send_message(msg); + res + } + }) + .await; + if res.is_err() { + tracing::info!( + "peer {} disconnected, while sending SyncSnapshotHosts", + this.peer_info.id + ); + } + } + } +} + +#[derive(Clone)] +pub(crate) struct PoolSnapshot { + pub me: PeerId, + /// Connections which have completed the handshake and are ready + /// for transmitting messages. + pub ready: im::HashMap>, + /// Index on `ready` by Connection.owned_account.account_key. + /// We allow only 1 connection to a peer with the given account_key, + /// as it is an invalid setup to have 2 nodes acting as the same validator. + pub ready_by_account_key: im::HashMap>, + /// Set of started outbound connections, which are not ready yet. + /// We need to keep those to prevent a deadlock when 2 peers try + /// to connect to each other at the same time. + /// + /// The procedure of establishing a connections should look as follows: + /// 1. Peer A decides to connect to peer B. + /// 2. Peer A gets an OutboundHandshakePermit by calling pool.start_outbound(B). + /// 3. Peer A connects to peer B. + /// 4. Peer B accepts the connection by calling pool.insert_ready(). + /// 5. Peer B notifies A that it has accepted the connection. + /// 6. Peer A accepts the connection by calling pool.insert_ready(). + /// 7. Peer A drops the OutboundHandshakePermit. + /// + /// In case any of these steps fails the connection and the OutboundHandshakePermit + /// should be dropped. + /// + /// Now imagine that A and B try to connect to each other at the same time: + /// a. Peer A executes 1,2,3. + /// b. Peer B executes 1,2,3. + /// c. Both A and B try to execute 4 and exactly one of these calls will succeed: the tie + /// is broken by comparing PeerIds: connection from smaller to bigger takes priority. + /// WLOG let us assume that A < B. + /// d. Peer A rejects connection from B, peer B accepts connection from A and notifies A. + /// e. A continues with 6,7, B just drops the connection and the permit. + /// + /// Now imagine a different interleaving: + /// a. Peer B executes 1,2,3 and A accepts the connection (i.e. 4) + /// b. Peer A executes 1 and then attempts 2. + /// In this scenario A will fail to obtain a permit, because it has already accepted a + /// connection from B. + pub outbound_handshakes: im::HashSet, + /// Inbound end of the loop connection. The outbound end is added to the `ready` set. + pub loop_inbound: Option>, +} + +pub(crate) struct OutboundHandshakePermit(PeerId, Weak>); + +impl OutboundHandshakePermit { + pub fn peer_id(&self) -> &PeerId { + &self.0 + } +} + +impl fmt::Debug for OutboundHandshakePermit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.peer_id().fmt(f) + } +} + +impl Drop for OutboundHandshakePermit { + fn drop(&mut self) { + if let Some(pool) = self.1.upgrade() { + pool.update(|mut pool| { + pool.outbound_handshakes.remove(&self.0); + ((), pool) + }); + } + } +} + +#[derive(Clone)] +pub(crate) struct Pool(Arc>); + +#[derive(thiserror::Error, Clone, Debug, PartialEq, Eq)] +pub(crate) enum PoolError { + #[error("already connected to this peer")] + AlreadyConnected, + #[error("already connected to peer {peer_id} with the same account key {account_key}")] + AlreadyConnectedAccount { peer_id: PeerId, account_key: PublicKey }, + #[error("already started another outbound connection to this peer")] + AlreadyStartedConnecting, + #[error("loop connections are not allowed")] + UnexpectedLoopConnection, + #[error("OutboundHandshakePermit dropped before calling Pool.insert_ready()")] + PermitDropped, +} + +impl Pool { + pub fn new(me: PeerId) -> Pool { + Self(Arc::new(ArcMutex::new(PoolSnapshot { + loop_inbound: None, + me, + ready: im::HashMap::new(), + ready_by_account_key: im::HashMap::new(), + outbound_handshakes: im::HashSet::new(), + }))) + } + + pub fn load(&self) -> Arc { + self.0.load() + } + + pub fn insert_ready(&self, peer: Arc) -> Result<(), PoolError> { + self.0.try_update(move |mut pool| { + let id = peer.peer_info.id.clone(); + // We support loopback connections for the purpose of + // validating our own external IP. This is the only case + // in which we allow 2 connections in a pool to have the same + // PeerId. The outbound connection is added to the + // `ready` set, the inbound connection is put into dedicated `loop_inbound` field. + if id == pool.me && peer.peer_type == PeerType::Inbound { + if pool.loop_inbound.is_some() { + return Err(PoolError::AlreadyConnected); + } + // Detect a situation in which a different node tries to connect + // to us with the same PeerId. This can happen iff the node key + // has been stolen (or just copied over by mistake). + if !pool.ready.contains_key(&id) && !pool.outbound_handshakes.contains(&id) { + return Err(PoolError::UnexpectedLoopConnection); + } + pool.loop_inbound = Some(peer); + return Ok(((), pool)); + } + match peer.peer_type { + PeerType::Inbound => { + if pool.outbound_handshakes.contains(&id) && id >= pool.me { + return Err(PoolError::AlreadyStartedConnecting); + } + } + PeerType::Outbound => { + // This is a bug, if an outbound permit is not kept + // until insert_ready is called. + // TODO(gprusak): in fact, we can make insert_ready + // consume the outbound permit to additionally ensure + // that permit is dropped properly. However we will still + // need a runtime check to verify that the permit comes + // from the same Pool instance and is for the right PeerId. + if !pool.outbound_handshakes.contains(&id) { + return Err(PoolError::PermitDropped); + } + } + } + if pool.ready.insert(id.clone(), peer.clone()).is_some() { + return Err(PoolError::AlreadyConnected); + } + if let Some(owned_account) = &peer.owned_account { + // Only 1 connection per account key is allowed. + // Having 2 peers use the same account key is an invalid setup, + // which violates the BFT consensus anyway. + // TODO(gprusak): an incorrectly closed TCP connection may remain in ESTABLISHED + // state up to minutes/hours afterwards. This may cause problems in + // case a validator is restarting a node after crash and the new node has the same + // peer_id/account_key/IP:port as the old node. What is the desired behavior is + // such a case? Linux TCP implementation supports: + // TCP_USER_TIMEOUT - timeout for ACKing the sent data + // TCP_KEEPIDLE - idle connection time after which a KEEPALIVE is sent + // TCP_KEEPINTVL - interval between subsequent KEEPALIVE probes + // TCP_KEEPCNT - number of KEEPALIVE probes before closing the connection. + // If it ever becomes a problem, we can eiter: + // 1. replace TCP with sth else, like QUIC. + // 2. use some lower level API than tokio::net to be able to set the linux flags. + // 3. implement KEEPALIVE equivalent manually on top of TCP to resolve conflicts. + // 4. allow overriding old connections by new connections, but that will require + // a deeper thought to make sure that the connections will be eventually stable + // and that incorrect setups will be detectable. + if let Some(conn) = pool + .ready_by_account_key + .insert(owned_account.account_key.clone(), peer.clone()) + { + // Unwrap is safe, because pool.ready_by_account_key is an index on connections + // with owned_account present. + let err = PoolError::AlreadyConnectedAccount { + peer_id: conn.peer_info.id.clone(), + account_key: conn.owned_account.as_ref().unwrap().account_key.clone(), + }; + // We duplicate the error logging here, because returning an error + // from insert_ready is expected (pool may regularly reject connections), + // however conflicting connections with the same account key indicate an + // incorrect validator setup, so we log it here as a warn!, rather than just + // info!. + tracing::warn!(target:"network", "Pool::register({id}): {err}"); + metrics::ALREADY_CONNECTED_ACCOUNT.inc(); + return Err(err); + } + } + Ok(((), pool)) + }) + } + + /// Reserves an OutboundHandshakePermit for the given peer_id. + /// It should be called before attempting to connect to this peer. + /// The returned permit shouldn't be dropped until insert_ready for this + /// outbound connection is called. + /// + /// This is required to resolve race conditions in case 2 nodes try to connect + /// to each other at the same time. + /// + /// NOTE: Pool supports loop connections (i.e. connections in which both ends are the same + /// node) for the purpose of verifying one's own public IP. + // TODO(gprusak): simplify this flow. + pub fn start_outbound(&self, peer_id: PeerId) -> Result { + self.0.try_update(move |mut pool| { + if pool.ready.contains_key(&peer_id) { + return Err(PoolError::AlreadyConnected); + } + if pool.outbound_handshakes.contains(&peer_id) { + return Err(PoolError::AlreadyStartedConnecting); + } + pool.outbound_handshakes.insert(peer_id.clone()); + Ok((OutboundHandshakePermit(peer_id, Arc::downgrade(&self.0)), pool)) + }) + } + + pub fn remove(&self, conn: &Arc) { + self.0.update(|mut pool| { + match pool.ready.entry(conn.peer_info.id.clone()) { + im::hashmap::Entry::Occupied(e) if Arc::ptr_eq(e.get(), conn) => { + e.remove_entry(); + } + _ => {} + } + if let Some(owned_account) = &conn.owned_account { + match pool.ready_by_account_key.entry(owned_account.account_key.clone()) { + im::hashmap::Entry::Occupied(e) if Arc::ptr_eq(e.get(), conn) => { + e.remove_entry(); + } + _ => {} + } + } + ((), pool) + }); + } + + /// Send message to peer that belongs to our active set + /// Return whether the message is sent or not. + pub fn send_message(&self, peer_id: PeerId, msg: Arc) -> bool { + let pool = self.load(); + if let Some(peer) = pool.ready.get(&peer_id) { + peer.send_message(msg); + return true; + } + tracing::debug!(target: "network", + to = ?peer_id, + num_connected_peers = pool.ready.len(), + ?msg, + "Failed sending message: peer not connected" + ); + false + } + + /// Broadcast message to all ready peers. + pub fn broadcast_message(&self, msg: Arc) { + metrics::BROADCAST_MESSAGES.with_label_values(&[msg.msg_variant()]).inc(); + for peer in self.load().ready.values() { + peer.send_message(msg.clone()); + } + } +} diff --git a/chain/network/src/peer_manager/connection/tests.rs b/chain/network/src/peer_manager/connection/tests.rs new file mode 100644 index 000000000..1ccb407f4 --- /dev/null +++ b/chain/network/src/peer_manager/connection/tests.rs @@ -0,0 +1,122 @@ +use crate::network_protocol::testonly as data; +use crate::peer::peer_actor::ClosingReason; +use crate::peer_manager; +use crate::peer_manager::connection; +use crate::private_actix::RegisterPeerError; +use crate::tcp; +use crate::testonly::make_rng; +use unc_async::time; +use unc_o11y::testonly::init_test_logger; +use std::sync::Arc; + +#[tokio::test] +async fn connection_tie_break() { + init_test_logger(); + let mut rng = make_rng(33955575545); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let mut cfgs: Vec<_> = (0..3).map(|_| chain.make_config(rng)).collect(); + cfgs.sort_by_key(|c| c.node_id()); + + let pm = peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + cfgs[1].clone(), + chain.clone(), + ) + .await; + + // pm.id is lower + let outbound_conn = pm.start_outbound(chain.clone(), cfgs[2].clone(), tcp::Tier::T2).await; + let inbound_conn = pm.start_inbound(chain.clone(), cfgs[2].clone()).await; + // inbound should be rejected, outbound accepted. + assert_eq!( + ClosingReason::RejectedByPeerManager(RegisterPeerError::PoolError( + connection::PoolError::AlreadyStartedConnecting + )), + inbound_conn.manager_fail_handshake(&clock.clock()).await, + ); + outbound_conn.handshake(&clock.clock()).await; + + // pm.id is higher + let outbound_conn = pm.start_outbound(chain.clone(), cfgs[0].clone(), tcp::Tier::T2).await; + let inbound_conn = pm.start_inbound(chain.clone(), cfgs[0].clone()).await; + // inbound should be accepted, outbound rejected by PM. + let inbound = inbound_conn.handshake(&clock.clock()).await; + assert_eq!( + ClosingReason::RejectedByPeerManager(RegisterPeerError::PoolError( + connection::PoolError::AlreadyConnected + )), + outbound_conn.manager_fail_handshake(&clock.clock()).await, + ); + drop(inbound); +} + +#[tokio::test] +async fn duplicate_connections() { + init_test_logger(); + let mut rng = make_rng(33955575545); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let pm = peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + + // Double outbound. + let cfg = chain.make_config(rng); + let conn1 = pm.start_outbound(chain.clone(), cfg.clone(), tcp::Tier::T2).await; + let conn2 = pm.start_outbound(chain.clone(), cfg.clone(), tcp::Tier::T2).await; + // conn2 shouldn't even be started, so it should fail before conn1 completes. + assert_eq!( + ClosingReason::OutboundNotAllowed(connection::PoolError::AlreadyStartedConnecting), + conn2.manager_fail_handshake(&clock.clock()).await, + ); + conn1.handshake(&clock.clock()).await; + + // Double inbound. + let cfg = chain.make_config(rng); + let conn1 = pm.start_inbound(chain.clone(), cfg.clone()).await; + let conn2 = pm.start_inbound(chain.clone(), cfg.clone()).await; + // Second inbound should be rejected. + let conn1 = conn1.handshake(&clock.clock()).await; + assert_eq!( + ClosingReason::RejectedByPeerManager(RegisterPeerError::PoolError( + connection::PoolError::AlreadyConnected + )), + conn2.manager_fail_handshake(&clock.clock()).await, + ); + pm.check_consistency().await; + drop(conn1); + + // Inbound then outbound. + let cfg = chain.make_config(rng); + let conn1 = pm.start_inbound(chain.clone(), cfg.clone()).await; + let conn1 = conn1.handshake(&clock.clock()).await; + let conn2 = pm.start_outbound(chain.clone(), cfg.clone(), tcp::Tier::T2).await; + assert_eq!( + ClosingReason::OutboundNotAllowed(connection::PoolError::AlreadyConnected), + conn2.manager_fail_handshake(&clock.clock()).await, + ); + drop(conn1); + + // Outbound then inbound. + let cfg = chain.make_config(rng); + let conn1 = pm.start_outbound(chain.clone(), cfg.clone(), tcp::Tier::T2).await; + let conn1 = conn1.handshake(&clock.clock()).await; + let conn2 = pm.start_inbound(chain.clone(), cfg.clone()).await; + assert_eq!( + ClosingReason::RejectedByPeerManager(RegisterPeerError::PoolError( + connection::PoolError::AlreadyConnected + )), + conn2.manager_fail_handshake(&clock.clock()).await, + ); + drop(conn1); +} diff --git a/chain/network/src/peer_manager/connection_store/mod.rs b/chain/network/src/peer_manager/connection_store/mod.rs new file mode 100644 index 000000000..4c9e9ef6b --- /dev/null +++ b/chain/network/src/peer_manager/connection_store/mod.rs @@ -0,0 +1,149 @@ +use crate::concurrency::arc_mutex::ArcMutex; +use crate::peer::peer_actor::ClosingReason; +use crate::peer_manager::connection; +use crate::store; +use crate::types::{ConnectionInfo, PeerInfo, PeerType}; +use unc_async::time; +use unc_primitives::network::PeerId; +use std::collections::HashSet; + +#[cfg(test)] +mod testonly; +#[cfg(test)] +mod tests; + +/// Size of the LRU cache of recent outbound connections. +pub const OUTBOUND_CONNECTIONS_CACHE_SIZE: usize = 40; +/// How long a connection should survive before being stored. +pub(crate) const STORED_CONNECTIONS_MIN_DURATION: time::Duration = time::Duration::minutes(10); + +#[derive(Clone)] +struct Inner { + store: store::Store, + outbound: Vec, +} + +impl Inner { + /// Returns whether the store contains an outbound connection to the given peer + fn contains_outbound(&self, peer_id: &PeerId) -> bool { + for stored_info in &self.outbound { + if stored_info.peer_info.id == *peer_id { + return true; + } + } + return false; + } + + /// If there is an outbound connection to the given peer in storage, removes it + fn remove_outbound(&mut self, peer_id: &PeerId) { + self.outbound.retain(|c| c.peer_info.id != *peer_id); + if let Err(err) = self.store.set_recent_outbound_connections(&self.outbound) { + tracing::error!(target: "network", ?err, "Failed to save recent outbound connections"); + } + } + + /// Takes a list of ConnectionInfos and inserts them to the front of the outbound store. + /// Any existing entry having the same PeerId as a newly inserted entry is dropped. + /// Evicts from the back if OUTBOUND_CONNECTIONS_CACHE_SIZE is reached. + /// Timestamps are stored for debugging purposes, but not otherwise used. + fn push_front_outbound(&mut self, mut conns: Vec) { + // Collect the PeerIds of the newly inserted connections + let updated_peer_ids: HashSet = + conns.iter().map(|c| c.peer_info.id.clone()).collect(); + + // Append entries from storage for disconnected peers, preserving order + for stored in &self.outbound { + if !updated_peer_ids.contains(&stored.peer_info.id) { + conns.push(stored.clone()); + } + } + + // Evict the longest-disconnected connections, if needed + conns.truncate(OUTBOUND_CONNECTIONS_CACHE_SIZE); + + if let Err(err) = self.store.set_recent_outbound_connections(&conns) { + tracing::error!(target: "network", ?err, "Failed to save recent outbound connections"); + } + self.outbound = conns; + } +} + +/// ConnectionStore is cheap to clone, so we can use ArcMutex and avoid blocking on read +pub(crate) struct ConnectionStore(ArcMutex); + +impl ConnectionStore { + pub fn new(store: store::Store) -> anyhow::Result { + let outbound = store.get_recent_outbound_connections(); + let inner = Inner { store, outbound }; + Ok(ConnectionStore(ArcMutex::new(inner))) + } + + /// Returns all stored outbound connections + pub fn get_recent_outbound_connections(&self) -> Vec { + return self.0.load().outbound.clone(); + } + + /// If there is an outbound connection to the given peer in storage, removes it + pub fn remove_from_connection_store(&self, peer_id: &PeerId) { + self.0.update(|mut inner| { + inner.remove_outbound(peer_id); + ((), inner) + }); + } + + /// Called upon closing a connection. If closed for a reason indicating we should + /// not reconnect (for example, a ban), removes the connection from storage. + /// Returns a boolean indicating whether the connection should be re-established. + pub fn connection_closed( + &self, + peer_info: &PeerInfo, + peer_type: &PeerType, + reason: &ClosingReason, + ) -> bool { + if *peer_type != PeerType::Outbound { + return false; + } + + if reason.remove_from_connection_store() { + // If the outbound connection closed for a reason which indicates we should not + // re-establish the connection, remove it from the connection store. + self.0.update(|mut inner| { + inner.remove_outbound(&peer_info.id); + ((), inner) + }); + } + + return self.0.load().contains_outbound(&peer_info.id); + } + + /// Given a snapshot of the TIER2 connection pool, updates the connections in storage. + pub fn update(&self, clock: &time::Clock, tier2: &connection::PoolSnapshot) { + let now = clock.now(); + let now_utc = clock.now_utc(); + + // Gather information about active outbound connections which have lasted long enough + let mut outbound = vec![]; + for c in tier2.ready.values() { + if c.peer_type != PeerType::Outbound { + continue; + } + + let connected_duration: time::Duration = now - c.established_time; + if connected_duration < STORED_CONNECTIONS_MIN_DURATION { + continue; + } + + outbound.push(ConnectionInfo { + peer_info: c.peer_info.clone(), + time_established: now_utc - connected_duration, + time_connected_until: now_utc, + }); + } + + // Push the information about the active connections to the front of the store + self.0.update(|mut inner| { + inner.push_front_outbound(outbound); + ((), inner) + }); + } +} diff --git a/chain/network/src/peer_manager/connection_store/testonly.rs b/chain/network/src/peer_manager/connection_store/testonly.rs new file mode 100644 index 000000000..348499e4f --- /dev/null +++ b/chain/network/src/peer_manager/connection_store/testonly.rs @@ -0,0 +1,10 @@ +use crate::types::ConnectionInfo; + +impl super::ConnectionStore { + pub(crate) fn insert_outbound_connections(&self, outbound: Vec) { + self.0.update(|mut inner| { + inner.push_front_outbound(outbound); + ((), inner) + }); + } +} diff --git a/chain/network/src/peer_manager/connection_store/tests.rs b/chain/network/src/peer_manager/connection_store/tests.rs new file mode 100644 index 000000000..4394498c1 --- /dev/null +++ b/chain/network/src/peer_manager/connection_store/tests.rs @@ -0,0 +1,131 @@ +use crate::network_protocol::testonly::make_peer_info; +use crate::peer_manager::connection_store::ConnectionStore; +use crate::peer_manager::connection_store::OUTBOUND_CONNECTIONS_CACHE_SIZE; +use crate::store; +use crate::testonly::make_rng; +use crate::testonly::AsSet; +use crate::types::ConnectionInfo; +use unc_async::time; +use rand::Rng; + +/// Returns a ConnectionInfo with the given value for time_connected_until, +/// and with randomly generated peer_info and time_established +fn make_connection_info(rng: &mut R, time_connected_until: time::Utc) -> ConnectionInfo { + ConnectionInfo { + peer_info: make_peer_info(rng), + time_established: time_connected_until - time::Duration::hours(rng.gen_range(1..1000)), + time_connected_until, + } +} + +/// Returns num_connections ConnectionInfos with the given value for time_connected_until, +/// and with randomly generated peer_infos and time_established values +fn make_connection_infos( + rng: &mut R, + time_connected_until: time::Utc, + num_connections: usize, +) -> Vec { + (0..num_connections).map(|_| make_connection_info(rng, time_connected_until)).collect() +} + +#[test] +fn test_reload_from_storage() { + let mut rng = make_rng(921853233); + let rng = &mut rng; + let clock = time::FakeClock::default(); + let store = store::Store::from(unc_store::db::TestDB::new()); + + let conn_info = make_connection_info(rng, clock.now_utc()); + { + tracing::debug!(target:"test", "write connection info to storage"); + let connection_store = ConnectionStore::new(store.clone()).unwrap(); + connection_store.insert_outbound_connections(vec![conn_info.clone()]); + } + { + tracing::debug!(target:"test", "read connection info from storage"); + let connection_store = ConnectionStore::new(store).unwrap(); + assert_eq!(connection_store.get_recent_outbound_connections(), vec![conn_info]); + } +} + +#[test] +fn test_overwrite_stored_connection() { + let mut rng = make_rng(921853233); + let rng = &mut rng; + let clock = time::FakeClock::default(); + let store = store::Store::from(unc_store::db::TestDB::new()); + let connection_store = ConnectionStore::new(store).unwrap(); + + tracing::debug!(target:"test", "create and store a connection"); + let now_utc = clock.now_utc(); + let conn_info_a = make_connection_info(rng, now_utc); + connection_store.insert_outbound_connections(vec![conn_info_a.clone()]); + + tracing::debug!(target:"test", "insert a second connection with the same PeerId but different data"); + clock.advance(time::Duration::seconds(123)); + let now_utc = clock.now_utc(); + let mut conn_info_b = make_connection_info(rng, now_utc); + conn_info_b.peer_info.id = conn_info_a.peer_info.id; + connection_store.insert_outbound_connections(vec![conn_info_b.clone()]); + + tracing::debug!(target:"test", "check that precisely the second connection is present (and not the first)"); + assert_eq!(connection_store.get_recent_outbound_connections(), vec![conn_info_b]); +} + +#[test] +fn test_evict_longest_disconnected() { + let mut rng = make_rng(921853233); + let rng = &mut rng; + let clock = time::FakeClock::default(); + let store = store::Store::from(unc_store::db::TestDB::new()); + let connection_store = ConnectionStore::new(store).unwrap(); + + tracing::debug!(target:"test", "create and store live connections up to the ConnectionStore limit"); + let now_utc = clock.now_utc(); + let mut conn_infos = make_connection_infos(rng, now_utc, OUTBOUND_CONNECTIONS_CACHE_SIZE); + connection_store.insert_outbound_connections(conn_infos.clone()); + + tracing::debug!(target:"test", "remove one of the connections, advance the clock and update the ConnectionStore"); + conn_infos.remove(rand::thread_rng().gen_range(0..OUTBOUND_CONNECTIONS_CACHE_SIZE)); + clock.advance(time::Duration::hours(1)); + let now_utc = clock.now_utc(); + for c in conn_infos.iter_mut() { + c.time_connected_until = now_utc; + } + connection_store.insert_outbound_connections(conn_infos.clone()); + + tracing::debug!(target:"test", "insert a new connection"); + let conn = make_connection_info(rng, now_utc); + connection_store.insert_outbound_connections(vec![conn.clone()]); + conn_infos.push(conn); + + tracing::debug!(target:"test", "check that the store contains exactly the expected connections"); + assert_eq!(connection_store.get_recent_outbound_connections().as_set(), conn_infos.as_set()); +} + +#[test] +fn test_recovery_from_clock_rewind() { + let mut rng = make_rng(921853233); + let rng = &mut rng; + let clock = time::FakeClock::default(); + let store = store::Store::from(unc_store::db::TestDB::new()); + let connection_store = ConnectionStore::new(store).unwrap(); + + tracing::debug!(target:"test", "create and store live connections up to the ConnectionStore limit"); + let now_utc = clock.now_utc(); + connection_store.insert_outbound_connections(make_connection_infos( + rng, + now_utc, + OUTBOUND_CONNECTIONS_CACHE_SIZE, + )); + + tracing::debug!(target:"test", "turn back the clock 1 year"); + clock.set_utc(now_utc - time::Duration::days(365)); + assert!(clock.now_utc() < now_utc); + + tracing::debug!(target:"test", "insert a new connection and check that it's at the front of the storage"); + let now_utc = clock.now_utc(); + let conn = make_connection_info(rng, now_utc); + connection_store.insert_outbound_connections(vec![conn.clone()]); + assert_eq!(connection_store.get_recent_outbound_connections()[0], conn); +} diff --git a/chain/network/src/peer_manager/mod.rs b/chain/network/src/peer_manager/mod.rs new file mode 100644 index 000000000..2fc73e731 --- /dev/null +++ b/chain/network/src/peer_manager/mod.rs @@ -0,0 +1,11 @@ +pub(crate) mod connection; +pub(crate) mod connection_store; +pub(crate) mod network_state; +pub(crate) mod peer_manager_actor; +pub(crate) mod peer_store; + +#[cfg(test)] +pub(crate) mod testonly; + +#[cfg(test)] +mod tests; diff --git a/chain/network/src/peer_manager/network_state/mod.rs b/chain/network/src/peer_manager/network_state/mod.rs new file mode 100644 index 000000000..8b3e01a1d --- /dev/null +++ b/chain/network/src/peer_manager/network_state/mod.rs @@ -0,0 +1,827 @@ +use crate::accounts_data::{AccountDataCache, AccountDataError}; +use crate::announce_accounts::AnnounceAccountCache; +use crate::client; +use crate::concurrency::demux; +use crate::concurrency::runtime::Runtime; +use crate::config; +use crate::network_protocol::{ + Edge, EdgeState, PartialEdgeInfo, PeerIdOrHash, PeerInfo, PeerMessage, RawRoutedMessage, + RoutedMessageBody, RoutedMessageV2, SignedAccountData, SnapshotHostInfo, +}; +use crate::peer::peer_actor::PeerActor; +use crate::peer::peer_actor::{ClosingReason, ConnectionClosedEvent}; +use crate::peer_manager::connection; +use crate::peer_manager::connection_store; +use crate::peer_manager::peer_manager_actor::Event; +use crate::peer_manager::peer_store; +use crate::private_actix::RegisterPeerError; +use crate::routing::route_back_cache::RouteBackCache; +use crate::routing::NetworkTopologyChange; +use crate::shards_manager::ShardsManagerRequestFromNetwork; +use crate::snapshot_hosts::{SnapshotHostInfoError, SnapshotHostsCache}; +use crate::stats::metrics; +use crate::store; +use crate::tcp; +use crate::types::{ChainInfo, PeerType, ReasonForBan}; +use anyhow::Context; +use arc_swap::ArcSwap; +use unc_async::messaging::Sender; +use unc_async::time; +use unc_primitives::block::GenesisId; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::PeerId; +use unc_primitives::types::AccountId; +use parking_lot::Mutex; +use std::net::SocketAddr; +use std::sync::atomic::AtomicUsize; +use std::sync::Arc; +use tracing::Instrument as _; + +mod routing; +mod tier1; + +/// Limit number of pending Peer actors to avoid OOM. +pub(crate) const LIMIT_PENDING_PEERS: usize = 60; + +/// Send important messages three times. +/// We send these messages multiple times to reduce the chance that they are lost +const IMPORTANT_MESSAGE_RESENT_COUNT: usize = 3; + +/// Size of LRU cache size of recent routed messages. +/// It should be large enough to detect duplicates (i.e. all messages received during +/// production of 1 block should fit). +const RECENT_ROUTED_MESSAGES_CACHE_SIZE: usize = 10000; + +/// How long a peer has to be unreachable, until we prune it from the in-memory graph. +const PRUNE_UNREACHABLE_PEERS_AFTER: time::Duration = time::Duration::hours(1); + +/// Remove the edges that were created more that this duration ago. +pub const PRUNE_EDGES_AFTER: time::Duration = time::Duration::minutes(30); + +/// How long to wait between reconnection attempts to the same peer +pub(crate) const RECONNECT_ATTEMPT_INTERVAL: time::Duration = time::Duration::seconds(10); + +impl WhitelistNode { + pub fn from_peer_info(pi: &PeerInfo) -> anyhow::Result { + Ok(Self { + id: pi.id.clone(), + addr: if let Some(addr) = pi.addr { + addr + } else { + anyhow::bail!("addess is missing"); + }, + account_id: pi.account_id.clone(), + }) + } +} + +#[derive(Clone, PartialEq, Eq)] +pub(crate) struct WhitelistNode { + id: PeerId, + addr: SocketAddr, + account_id: Option, +} + +pub(crate) struct NetworkState { + /// Dedicated runtime for `NetworkState` which runs in a separate thread. + /// Async methods of NetworkState are not cancellable, + /// so calling them from, for example, PeerActor is dangerous because + /// PeerActor can be stopped at any moment. + /// WARNING: DO NOT spawn infinite futures/background loops on this arbiter, + /// as it will be automatically closed only when the NetworkState is dropped. + /// WARNING: actix actors can be spawned only when actix::System::current() is set. + /// DO NOT spawn actors from a task on this runtime. + runtime: Runtime, + /// PeerManager config. + pub config: config::VerifiedConfig, + /// When network state has been constructed. + pub created_at: time::Instant, + /// GenesisId of the chain. + pub genesis_id: GenesisId, + pub client: Arc, + pub shards_manager_adapter: Sender, + + /// Network-related info about the chain. + pub chain_info: ArcSwap>, + /// AccountsData for TIER1 accounts. + pub accounts_data: Arc, + /// AnnounceAccounts mapping TIER1 account ids to peer ids. + pub account_announcements: Arc, + /// Connected peers (inbound and outbound) with their full peer information. + pub tier2: connection::Pool, + pub tier1: connection::Pool, + /// Semaphore limiting inflight inbound handshakes. + pub inbound_handshake_permits: Arc, + /// Peer store that provides read/write access to peers. + pub peer_store: peer_store::PeerStore, + /// Information about state snapshots hosted by network peers. + pub snapshot_hosts: Arc, + /// Connection store that provides read/write access to stored connections. + pub connection_store: connection_store::ConnectionStore, + /// List of peers to which we should re-establish a connection + pub pending_reconnect: Mutex>, + /// A graph of the whole NEAR network. + pub graph: Arc, + /// A sparsified graph of the whole NEAR network. + /// TODO(saketh): deprecate graph above, rename this to RoutingTable + pub graph_v2: Arc, + + /// Hashes of the body of recently received routed messages. + /// It allows us to determine whether messages arrived faster over TIER1 or TIER2 network. + pub recent_routed_messages: Mutex>, + + /// Hash of messages that requires routing back to respective previous hop. + pub tier2_route_back: Mutex, + /// Currently unused, as TIER1 messages do not require a response. + /// Also TIER1 connections are direct by design (except for proxies), + /// so routing shouldn't really be needed. + /// TODO(gprusak): consider removing it altogether. + pub tier1_route_back: Mutex, + + /// Shared counter across all PeerActors, which counts number of `RoutedMessageBody::ForwardTx` + /// messages sincce last block. + pub txns_since_last_block: AtomicUsize, + + /// Whitelisted nodes, which are allowed to connect even if the connection limit has been + /// reached. + whitelist_nodes: Vec, + + /// Mutex which prevents overlapping calls to tier1_advertise_proxies. + tier1_advertise_proxies_mutex: tokio::sync::Mutex<()>, + /// Demultiplexer aggregating calls to add_edges(), for V1 routing protocol + add_edges_demux: demux::Demux, Result<(), ReasonForBan>>, + /// Demultiplexer aggregating calls to update_routes(), for V2 routing protocol + update_routes_demux: + demux::Demux>, + + /// Mutex serializing calls to set_chain_info(), which mutates a bunch of stuff non-atomically. + /// TODO(gprusak): make it use synchronization primitives in some more canonical way. + set_chain_info_mutex: Mutex<()>, +} + +impl NetworkState { + pub fn new( + clock: &time::Clock, + store: store::Store, + peer_store: peer_store::PeerStore, + config: config::VerifiedConfig, + genesis_id: GenesisId, + client: Arc, + shards_manager_adapter: Sender, + whitelist_nodes: Vec, + ) -> Self { + Self { + runtime: Runtime::new(), + graph: Arc::new(crate::routing::Graph::new( + crate::routing::GraphConfig { + node_id: config.node_id(), + prune_unreachable_peers_after: PRUNE_UNREACHABLE_PEERS_AFTER, + prune_edges_after: Some(PRUNE_EDGES_AFTER), + }, + store.clone(), + )), + graph_v2: Arc::new(crate::routing::GraphV2::new(crate::routing::GraphConfigV2 { + node_id: config.node_id(), + prune_edges_after: Some(PRUNE_EDGES_AFTER), + })), + genesis_id, + client, + shards_manager_adapter, + chain_info: Default::default(), + tier2: connection::Pool::new(config.node_id()), + tier1: connection::Pool::new(config.node_id()), + inbound_handshake_permits: Arc::new(tokio::sync::Semaphore::new(LIMIT_PENDING_PEERS)), + peer_store, + snapshot_hosts: Arc::new(SnapshotHostsCache::new(config.snapshot_hosts.clone())), + connection_store: connection_store::ConnectionStore::new(store.clone()).unwrap(), + pending_reconnect: Mutex::new(Vec::::new()), + accounts_data: Arc::new(AccountDataCache::new()), + account_announcements: Arc::new(AnnounceAccountCache::new(store)), + tier2_route_back: Mutex::new(RouteBackCache::default()), + tier1_route_back: Mutex::new(RouteBackCache::default()), + recent_routed_messages: Mutex::new(lru::LruCache::new( + RECENT_ROUTED_MESSAGES_CACHE_SIZE, + )), + txns_since_last_block: AtomicUsize::new(0), + whitelist_nodes, + add_edges_demux: demux::Demux::new(config.routing_table_update_rate_limit), + update_routes_demux: demux::Demux::new(config.routing_table_update_rate_limit), + set_chain_info_mutex: Mutex::new(()), + config, + created_at: clock.now(), + tier1_advertise_proxies_mutex: tokio::sync::Mutex::new(()), + } + } + + /// Spawn a future on the runtime which has the same lifetime as the NetworkState instance. + /// In particular if the future contains the NetworkState handler, it will be run until + /// completion. It is safe to self.spawn(...).await.unwrap(), since runtime will be kept alive + /// by the reference to self. + /// + /// It should be used to make the public methods cancellable: you spawn the + /// noncancellable logic on self.runtime and just await it: in case the call is cancelled, + /// the noncancellable logic will be run in the background anyway. + fn spawn( + &self, + fut: impl std::future::Future + 'static + Send, + ) -> tokio::task::JoinHandle { + self.runtime.handle.spawn(fut.in_current_span()) + } + + /// Stops peer instance if it is still connected, + /// and then mark peer as banned in the peer store. + pub fn disconnect_and_ban( + &self, + clock: &time::Clock, + peer_id: &PeerId, + ban_reason: ReasonForBan, + ) { + let tier2 = self.tier2.load(); + if let Some(peer) = tier2.ready.get(peer_id) { + peer.stop(Some(ban_reason)); + } else { + if let Err(err) = self.peer_store.peer_ban(clock, peer_id, ban_reason) { + tracing::error!(target: "network", ?err, "Failed to save peer data"); + } + } + } + + /// is_peer_whitelisted checks whether a peer is a whitelisted node. + /// whitelisted nodes are allowed to connect, even if the inbound connections limit has + /// been reached. This predicate should be evaluated AFTER the Handshake. + pub fn is_peer_whitelisted(&self, peer_info: &PeerInfo) -> bool { + self.whitelist_nodes + .iter() + .filter(|wn| wn.id == peer_info.id) + .filter(|wn| Some(wn.addr) == peer_info.addr) + .any(|wn| wn.account_id.is_none() || wn.account_id == peer_info.account_id) + } + + /// predicate checking whether we should allow an inbound connection from peer_info. + fn is_inbound_allowed(&self, peer_info: &PeerInfo) -> bool { + // Check if we have spare inbound connections capacity. + let tier2 = self.tier2.load(); + if tier2.ready.len() + tier2.outbound_handshakes.len() < self.config.max_num_peers as usize + && !self.config.inbound_disabled + { + return true; + } + // Whitelisted nodes are allowed to connect, even if the inbound connections limit has + // been reached. + if self.is_peer_whitelisted(peer_info) { + return true; + } + false + } + + /// Register a direct connection to a new peer. This will be called after successfully + /// establishing a connection with another peer. It becomes part of the connected peers. + /// + /// To build new edge between this pair of nodes both signatures are required. + /// Signature from this node is passed in `edge_info` + /// Signature from the other node is passed in `full_peer_info.edge_info`. + pub async fn register( + self: &Arc, + clock: &time::Clock, + edge: Edge, + conn: Arc, + ) -> Result<(), RegisterPeerError> { + let this = self.clone(); + let clock = clock.clone(); + self.spawn(async move { + let peer_info = &conn.peer_info; + // Check if this is a blacklisted peer. + if peer_info.addr.as_ref().map_or(true, |addr| this.peer_store.is_blacklisted(addr)) { + tracing::debug!(target: "network", peer_info = ?peer_info, "Dropping connection from blacklisted peer or unknown address"); + return Err(RegisterPeerError::Blacklisted); + } + + if this.peer_store.is_banned(&peer_info.id) { + tracing::debug!(target: "network", id = ?peer_info.id, "Dropping connection from banned peer"); + return Err(RegisterPeerError::Banned); + } + + match conn.tier { + tcp::Tier::T1 => { + if conn.peer_type == PeerType::Inbound { + if !this.config.tier1.as_ref().map_or(false, |c| c.enable_inbound) { + return Err(RegisterPeerError::Tier1InboundDisabled); + } + // Allow for inbound TIER1 connections only directly from a TIER1 peers. + let owned_account = conn.owned_account.as_ref().ok_or(RegisterPeerError::NotTier1Peer)?; + if !this.accounts_data.load().keys.contains(&owned_account.account_key) { + return Err(RegisterPeerError::NotTier1Peer); + } + } + if !edge.verify() { + return Err(RegisterPeerError::InvalidEdge); + } + this.tier1.insert_ready(conn).map_err(RegisterPeerError::PoolError)?; + } + tcp::Tier::T2 => { + if conn.peer_type == PeerType::Inbound { + if !this.is_inbound_allowed(&peer_info) { + // TODO(1896): Gracefully drop inbound connection for other peer. + let tier2 = this.tier2.load(); + tracing::debug!(target: "network", + tier2 = tier2.ready.len(), outgoing_peers = tier2.outbound_handshakes.len(), + max_num_peers = this.config.max_num_peers, + "Dropping handshake (network at max capacity)." + ); + return Err(RegisterPeerError::ConnectionLimitExceeded); + } + } + // First verify and broadcast the edge of the connection, so that in case + // it is invalid, the connection is not added to the pool. + // TODO(gprusak): consider actually banning the peer for consistency. + this.add_edges(&clock, vec![edge.clone()]) + .await + .map_err(|_: ReasonForBan| RegisterPeerError::InvalidEdge)?; + // Insert to the local connection pool + this.tier2.insert_ready(conn.clone()).map_err(RegisterPeerError::PoolError)?; + // Update the V2 routing table + this.update_routes(&clock, NetworkTopologyChange::PeerConnected(peer_info.id.clone(), edge.clone())) + .await.map_err(|_: ReasonForBan| RegisterPeerError::InvalidEdge)?; + // Write to the peer store + this.peer_store.peer_connected(&clock, peer_info); + } + } + Ok(()) + }).await.unwrap() + } + + /// Removes the connection from the state. + /// It is intentionally synchronous and expected to be called from PeerActor.stopping. + /// If it was async, there would be a risk that the unregister will be cancelled before + /// even starting. + pub fn unregister( + self: &Arc, + clock: &time::Clock, + conn: &Arc, + stream_id: tcp::StreamId, + reason: ClosingReason, + ) { + let this = self.clone(); + let clock = clock.clone(); + let conn = conn.clone(); + self.spawn(async move { + let peer_id = conn.peer_info.id.clone(); + if conn.tier == tcp::Tier::T1 { + // There is no banning or routing table for TIER1. + // Just remove the connection from the network_state. + this.tier1.remove(&conn); + return; + } + this.tier2.remove(&conn); + + // If the last edge we have with this peer represent a connection addition, create the edge + // update that represents the connection removal. + if let Some(edge) = this.graph.load().local_edges.get(&peer_id) { + if edge.edge_type() == EdgeState::Active { + let edge_update = + edge.remove_edge(this.config.node_id(), &this.config.node_key); + this.add_edges(&clock, vec![edge_update.clone()]).await.unwrap(); + } + } + + // Update the V2 routing table + this.update_routes(&clock, NetworkTopologyChange::PeerDisconnected(peer_id.clone())) + .await + .unwrap(); + + // Save the fact that we are disconnecting to the PeerStore. + let res = match reason { + ClosingReason::Ban(ban_reason) => { + this.peer_store.peer_ban(&clock, &conn.peer_info.id, ban_reason) + } + _ => this.peer_store.peer_disconnected(&clock, &conn.peer_info.id), + }; + if let Err(err) = res { + tracing::error!(target: "network", ?err, "Failed to save peer data"); + } + + // Save the fact that we are disconnecting to the ConnectionStore, + // and push a reconnect attempt, if applicable + if this.connection_store.connection_closed(&conn.peer_info, &conn.peer_type, &reason) { + this.pending_reconnect.lock().push(conn.peer_info.clone()); + } + + this.config + .event_sink + .push(Event::ConnectionClosed(ConnectionClosedEvent { stream_id, reason })); + }); + } + + /// Attempt to connect to the given peer until successful, up to max_attempts times + pub async fn reconnect( + self: &Arc, + clock: time::Clock, + peer_info: PeerInfo, + max_attempts: usize, + ) { + let mut interval = time::Interval::new(clock.now(), RECONNECT_ATTEMPT_INTERVAL); + for _attempt in 0..max_attempts { + interval.tick(&clock).await; + + let result = async { + let stream = tcp::Stream::connect(&peer_info, tcp::Tier::T2) + .await + .context("tcp::Stream::connect()")?; + PeerActor::spawn_and_handshake(clock.clone(), stream, None, self.clone()) + .await + .context("PeerActor::spawn()")?; + anyhow::Ok(()) + } + .await; + + let succeeded = !result.is_err(); + + if let Err(ref err) = result { + tracing::info!(target:"network", err = format!("{:#}", err), "Failed to connect to {peer_info}"); + } + + if self.peer_store.peer_connection_attempt(&clock, &peer_info.id, result).is_err() { + tracing::error!(target: "network", ?peer_info, "Failed to store connection attempt."); + } + + if succeeded { + return; + } + } + } + + /// Determine if the given target is referring to us. + pub fn message_for_me(&self, target: &PeerIdOrHash) -> bool { + let my_peer_id = self.config.node_id(); + match target { + PeerIdOrHash::PeerId(peer_id) => &my_peer_id == peer_id, + PeerIdOrHash::Hash(hash) => self.compare_route_back(*hash, &my_peer_id), + } + } + + #[cfg(test)] + pub fn send_ping(&self, clock: &time::Clock, tier: tcp::Tier, nonce: u64, target: PeerId) { + let body = RoutedMessageBody::Ping(crate::network_protocol::Ping { + nonce, + source: self.config.node_id(), + }); + let msg = RawRoutedMessage { target: PeerIdOrHash::PeerId(target), body }; + self.send_message_to_peer(clock, tier, self.sign_message(clock, msg)); + } + + pub fn send_pong(&self, clock: &time::Clock, tier: tcp::Tier, nonce: u64, target: CryptoHash) { + let body = RoutedMessageBody::Pong(crate::network_protocol::Pong { + nonce, + source: self.config.node_id(), + }); + let msg = RawRoutedMessage { target: PeerIdOrHash::Hash(target), body }; + self.send_message_to_peer(clock, tier, self.sign_message(clock, msg)); + } + + pub fn sign_message(&self, clock: &time::Clock, msg: RawRoutedMessage) -> Box { + Box::new(msg.sign( + &self.config.node_key, + self.config.routed_message_ttl, + Some(clock.now_utc()), + )) + } + + /// Route signed message to target peer. + /// Return whether the message is sent or not. + pub fn send_message_to_peer( + &self, + clock: &time::Clock, + tier: tcp::Tier, + msg: Box, + ) -> bool { + let my_peer_id = self.config.node_id(); + + // Check if the message is for myself and don't try to send it in that case. + if let PeerIdOrHash::PeerId(target) = &msg.target { + if target == &my_peer_id { + tracing::debug!(target: "network", account_id = ?self.config.validator.as_ref().map(|v|v.account_id()), ?my_peer_id, ?msg, "Drop signed message to myself"); + metrics::CONNECTED_TO_MYSELF.inc(); + return false; + } + } + match tier { + tcp::Tier::T1 => { + let peer_id = match &msg.target { + // If a message is a response, we try to load the target from the route back + // cache. + PeerIdOrHash::Hash(hash) => { + match self.tier1_route_back.lock().remove(clock, hash) { + Some(peer_id) => peer_id, + None => return false, + } + } + PeerIdOrHash::PeerId(peer_id) => peer_id.clone(), + }; + return self.tier1.send_message(peer_id, Arc::new(PeerMessage::Routed(msg))); + } + tcp::Tier::T2 => { + match self.tier2_find_route(&clock, &msg.target) { + Ok(peer_id) => { + // Remember if we expect a response for this message. + if msg.author == my_peer_id && msg.expect_response() { + tracing::trace!(target: "network", ?msg, "initiate route back"); + self.tier2_route_back.lock().insert(clock, msg.hash(), my_peer_id); + } + return self + .tier2 + .send_message(peer_id, Arc::new(PeerMessage::Routed(msg))); + } + Err(find_route_error) => { + // TODO(MarX, #1369): Message is dropped here. Define policy for this case. + metrics::MessageDropped::NoRouteFound.inc(&msg.body); + + tracing::debug!(target: "network", + account_id = ?self.config.validator.as_ref().map(|v|v.account_id()), + to = ?msg.target, + reason = ?find_route_error, + known_peers = ?self.graph.routing_table.reachable_peers(), + msg = ?msg.body, + "Drop signed message" + ); + return false; + } + } + } + } + } + + /// Send message to specific account. + /// Return whether the message is sent or not. + /// The message might be sent over TIER1 and/or TIER2 connection depending on the message type. + pub fn send_message_to_account( + &self, + clock: &time::Clock, + account_id: &AccountId, + msg: RoutedMessageBody, + ) -> bool { + let mut success = false; + let accounts_data = self.accounts_data.load(); + // All TIER1 messages are being sent over both TIER1 and TIER2 connections for now, + // so that we can actually observe the latency/reliability improvements in practice: + // for each message we track over which network tier it arrived faster? + if tcp::Tier::T1.is_allowed_routed(&msg) { + for key in accounts_data.keys_by_id.get(account_id).iter().flat_map(|keys| keys.iter()) + { + let data = match accounts_data.data.get(key) { + Some(data) => data, + None => continue, + }; + let conn = match self.get_tier1_proxy(data) { + Some(conn) => conn, + None => continue, + }; + // TODO(gprusak): in case of PartialEncodedChunk, consider stripping everything + // but the header. This will bound the message size + conn.send_message(Arc::new(PeerMessage::Routed(self.sign_message( + clock, + RawRoutedMessage { + target: PeerIdOrHash::PeerId(data.peer_id.clone()), + body: msg.clone(), + }, + )))); + success |= true; + break; + } + } + + let peer_id_from_account_data = accounts_data + .keys_by_id + .get(account_id) + .iter() + .flat_map(|keys| keys.iter()) + .flat_map(|key| accounts_data.data.get(key)) + .next() + .map(|data| data.peer_id.clone()); + // Find the target peer_id: + // - first look it up in self.accounts_data + // - if missing, fall back to lookup in self.graph.routing_table + // We want to deprecate self.graph.routing_table.account_owner in the next release. + let target = if let Some(peer_id) = peer_id_from_account_data { + metrics::ACCOUNT_TO_PEER_LOOKUPS.with_label_values(&["AccountData"]).inc(); + peer_id + } else if let Some(peer_id) = self.account_announcements.get_account_owner(account_id) { + metrics::ACCOUNT_TO_PEER_LOOKUPS.with_label_values(&["AnnounceAccount"]).inc(); + peer_id + } else { + // TODO(MarX, #1369): Message is dropped here. Define policy for this case. + metrics::MessageDropped::UnknownAccount.inc(&msg); + tracing::debug!(target: "network", + account_id = ?self.config.validator.as_ref().map(|v|v.account_id()), + to = ?account_id, + ?msg,"Drop message: unknown account", + ); + tracing::trace!(target: "network", known_peers = ?self.account_announcements.get_accounts_keys(), "Known peers"); + return false; + }; + + let msg = RawRoutedMessage { target: PeerIdOrHash::PeerId(target), body: msg }; + let msg = self.sign_message(clock, msg); + if msg.body.is_important() { + for _ in 0..IMPORTANT_MESSAGE_RESENT_COUNT { + success |= self.send_message_to_peer(clock, tcp::Tier::T2, msg.clone()); + } + } else { + success |= self.send_message_to_peer(clock, tcp::Tier::T2, msg) + } + success + } + + pub async fn add_accounts_data( + self: &Arc, + clock: &time::Clock, + accounts_data: Vec>, + ) -> Option { + let this = self.clone(); + let clock = clock.clone(); + self.spawn(async move { + // Verify and add the new data to the internal state. + let (new_data, err) = this.accounts_data.clone().insert(&clock, accounts_data).await; + // Broadcast any new data we have found, even in presence of an error. + // This will prevent a malicious peer from forcing us to re-verify valid + // datasets. See accounts_data::Cache documentation for details. + if !new_data.is_empty() { + let tier2 = this.tier2.load(); + let tasks: Vec<_> = tier2 + .ready + .values() + .map(|p| this.spawn(p.send_accounts_data(new_data.clone()))) + .collect(); + for t in tasks { + t.await.unwrap(); + } + } + err + }) + .await + .unwrap() + } + + pub async fn add_snapshot_hosts( + self: &Arc, + hosts: Vec>, + ) -> Option { + let this = self.clone(); + self.spawn(async move { + // Verify and add the new data to the internal state. + let (new_data, err) = this.snapshot_hosts.clone().insert(hosts).await; + // Broadcast any valid new data, even if an err was returned. + // The presence of one invalid entry doesn't invalidate the remaining ones. + if !new_data.is_empty() { + let tier2 = this.tier2.load(); + let tasks: Vec<_> = tier2 + .ready + .values() + .map(|p| this.spawn(p.send_snapshot_hosts(new_data.clone()))) + .collect(); + for t in tasks { + t.await.unwrap(); + } + } + err + }) + .await + .unwrap() + } + + /// a) there is a peer we should be connected to, but we aren't + /// b) there is an edge indicating that we should be disconnected from a peer, but we are connected. + /// Try to resolve the inconsistency. + /// We call this function every FIX_LOCAL_EDGES_INTERVAL from peer_manager_actor.rs. + pub async fn fix_local_edges(self: &Arc, clock: &time::Clock, timeout: time::Duration) { + let this = self.clone(); + let clock = clock.clone(); + self.spawn(async move { + let graph = this.graph.load(); + let tier2 = this.tier2.load(); + let mut tasks = vec![]; + for edge in graph.local_edges.values() { + let edge = edge.clone(); + let node_id = this.config.node_id(); + let other_peer = edge.other(&node_id).unwrap(); + match (tier2.ready.get(other_peer), edge.edge_type()) { + // This is an active connection, while the edge indicates it shouldn't. + (Some(conn), EdgeState::Removed) => tasks.push(this.spawn({ + let this = this.clone(); + let conn = conn.clone(); + let clock = clock.clone(); + async move { + conn.send_message(Arc::new(PeerMessage::RequestUpdateNonce( + PartialEdgeInfo::new( + &node_id, + &conn.peer_info.id, + std::cmp::max(Edge::create_fresh_nonce(&clock), edge.next()), + &this.config.node_key, + ), + ))); + // TODO(gprusak): here we should synchronically wait for the RequestUpdateNonce + // response (with timeout). Until network round trips are implemented, we just + // blindly wait for a while, then check again. + clock.sleep(timeout).await; + match this.graph.load().local_edges.get(&conn.peer_info.id) { + Some(edge) if edge.edge_type() == EdgeState::Active => return, + _ => conn.stop(None), + } + } + })), + // We are not connected to this peer, but routing table contains + // information that we do. We should wait and remove that peer + // from routing table + (None, EdgeState::Active) => tasks.push(this.spawn({ + let this = this.clone(); + let clock = clock.clone(); + let other_peer = other_peer.clone(); + async move { + // This edge says this is an connected peer, which is currently not in the set of connected peers. + // Wait for some time to let the connection begin or broadcast edge removal instead. + clock.sleep(timeout).await; + if this.tier2.load().ready.contains_key(&other_peer) { + return; + } + // Peer is still not connected after waiting a timeout. + // Unwrap is safe, because new_edge is always valid. + let new_edge = + edge.remove_edge(this.config.node_id(), &this.config.node_key); + this.add_edges(&clock, vec![new_edge.clone()]).await.unwrap() + } + })), + // OK + _ => {} + } + } + for t in tasks { + let _ = t.await; + } + + // Now that `graph` has been synchronized with the state of the local connections, + // use it as the source of truth to fix the local state in `graph_v2` + let mut tasks = vec![]; + let node_id = this.config.node_id(); + for edge in graph.local_edges.values() { + let other_peer = edge.other(&node_id).unwrap(); + tasks.push(match edge.edge_type() { + EdgeState::Active => this.update_routes( + &clock, + NetworkTopologyChange::PeerConnected(other_peer.clone(), edge.clone()), + ), + EdgeState::Removed => this.update_routes( + &clock, + NetworkTopologyChange::PeerDisconnected(other_peer.clone()), + ), + }); + } + for t in tasks { + let _ = t.await; + } + }) + .await + .unwrap() + } + + pub fn update_connection_store(self: &Arc, clock: &time::Clock) { + self.connection_store.update(clock, &self.tier2.load()); + } + + /// Clears pending_reconnect and returns the cleared values + pub fn poll_pending_reconnect(&self) -> Vec { + let mut pending_reconnect = self.pending_reconnect.lock(); + let polled = pending_reconnect.clone(); + pending_reconnect.clear(); + return polled; + } + + /// Collects and returns PeerInfos for all directly connected TIER2 peers. + pub fn get_direct_peers(self: &Arc) -> Vec { + return self.tier2.load().ready.values().map(|c| c.peer_info.clone()).collect(); + } + + /// Sets the chain info, and updates the set of TIER1 keys. + /// Returns true iff the set of TIER1 keys has changed. + pub fn set_chain_info(self: &Arc, info: ChainInfo) -> bool { + let _mutex = self.set_chain_info_mutex.lock(); + + // We set state.chain_info and call accounts_data.set_keys + // synchronously, therefore, assuming actix in-order delivery, + // there will be no race condition between subsequent SetChainInfo + // calls. + self.chain_info.store(Arc::new(Some(info.clone()))); + + // If tier1 is not enabled, we skip set_keys() call. + // This way self.state.accounts_data is always empty, hence no data + // will be collected or broadcasted. + if self.config.tier1.is_none() { + return false; + } + let has_changed = self.accounts_data.set_keys(info.tier1_accounts); + // The set of TIER1 accounts has changed, so we might be missing some accounts_data + // that our peers know about. + if has_changed { + self.tier1_request_full_sync(); + } + has_changed + } +} diff --git a/chain/network/src/peer_manager/network_state/routing.rs b/chain/network/src/peer_manager/network_state/routing.rs new file mode 100644 index 000000000..701ec552a --- /dev/null +++ b/chain/network/src/peer_manager/network_state/routing.rs @@ -0,0 +1,256 @@ +use super::NetworkState; +use crate::network_protocol::{ + DistanceVector, Edge, EdgeState, PartialEdgeInfo, PeerMessage, RoutedMessageV2, + RoutingTableUpdate, +}; +use crate::peer_manager::connection; +use crate::peer_manager::network_state::PeerIdOrHash; +use crate::peer_manager::peer_manager_actor::Event; +use crate::routing::routing_table_view::FindRouteError; +use crate::routing::NetworkTopologyChange; +use crate::stats::metrics; +use crate::tcp; +use crate::types::ReasonForBan; +use unc_async::time; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use std::collections::HashSet; +use std::sync::Arc; + +impl NetworkState { + // TODO(gprusak): eventually, this should be blocking, as it should be up to the caller + // whether to wait for the broadcast to finish, or run it in parallel with sth else. + fn broadcast_routing_table_update(&self, mut rtu: RoutingTableUpdate) { + if rtu == RoutingTableUpdate::default() { + return; + } + rtu.edges = Edge::deduplicate(rtu.edges); + let msg = Arc::new(PeerMessage::SyncRoutingTable(rtu)); + for conn in self.tier2.load().ready.values() { + conn.send_message(msg.clone()); + } + } + + // TODO(saketh-are): eventually, this should be blocking, as it should be up to the caller + // whether to wait for the broadcast to finish, or run it in parallel with sth else. + fn broadcast_distance_vector(&self, distance_vector: DistanceVector) { + let msg = Arc::new(PeerMessage::DistanceVector(distance_vector)); + for conn in self.tier2.load().ready.values() { + conn.send_message(msg.clone()); + } + } + + /// Adds AnnounceAccounts (without validating them) to the routing table. + /// Then it broadcasts all the AnnounceAccounts that haven't been seen before. + pub async fn add_accounts(self: &Arc, accounts: Vec) { + let this = self.clone(); + self.spawn(async move { + let new_accounts = this.account_announcements.add_accounts(accounts); + tracing::debug!(target: "network", account_id = ?this.config.validator.as_ref().map(|v|v.account_id()), ?new_accounts, "Received new accounts"); + this.broadcast_routing_table_update(RoutingTableUpdate::from_accounts( + new_accounts.clone(), + )); + this.config.event_sink.push(Event::AccountsAdded(new_accounts)); + }).await.unwrap() + } + + /// Constructs a partial edge to the given peer with the nonce specified. + /// If nonce is None, nonce is selected automatically. + pub fn propose_edge( + &self, + clock: &time::Clock, + peer1: &PeerId, + with_nonce: Option, + ) -> PartialEdgeInfo { + // When we create a new edge we increase the latest nonce by 2 in case we miss a removal + // proposal from our partner. + let nonce = with_nonce.unwrap_or_else(|| { + let nonce = Edge::create_fresh_nonce(clock); + // If we already had a connection to this peer - check that edge's nonce. + // And use either that one or the one from the current timestamp. + // We would use existing edge's nonce, if we were trying to connect to a given peer multiple times per second. + self.graph + .load() + .local_edges + .get(peer1) + .map_or(nonce, |edge| std::cmp::max(edge.next(), nonce)) + }); + PartialEdgeInfo::new(&self.config.node_id(), peer1, nonce, &self.config.node_key) + } + + /// Constructs an edge from the partial edge constructed by the peer, + /// adds it to the graph and then broadcasts it. + pub async fn finalize_edge( + self: &Arc, + clock: &time::Clock, + peer_id: PeerId, + edge_info: PartialEdgeInfo, + ) -> Result { + let edge = Edge::build_with_secret_key( + self.config.node_id(), + peer_id.clone(), + edge_info.nonce, + &self.config.node_key, + edge_info.signature, + ); + self.add_edges(&clock, vec![edge.clone()]).await?; + self.update_routes( + &clock, + NetworkTopologyChange::PeerConnected(peer_id.clone(), edge.clone()), + ) + .await?; + + Ok(edge) + } + + /// Validates edges, then adds them to the graph and then broadcasts all the edges that + /// hasn't been observed before. Returns an error iff any edge was invalid. Even if an + /// error was returned some of the valid input edges might have been added to the graph. + pub async fn add_edges( + self: &Arc, + clock: &time::Clock, + edges: Vec, + ) -> Result<(), ReasonForBan> { + if edges.is_empty() { + return Ok(()); + } + let this = self.clone(); + let clock = clock.clone(); + self.add_edges_demux + .call(edges, |edges: Vec>| async move { + let (mut edges, oks) = this.graph.update(&clock, edges).await; + // Don't send tombstones during the initial time. + // Most of the network is created during this time, which results + // in us sending a lot of tombstones to peers. + // Later, the amount of new edges is a lot smaller. + if let Some(skip_tombstones_duration) = this.config.skip_tombstones { + if clock.now() < this.created_at + skip_tombstones_duration { + edges.retain(|edge| edge.edge_type() == EdgeState::Active); + metrics::EDGE_TOMBSTONE_SENDING_SKIPPED.inc(); + } + } + // Broadcast new edges to all other peers. + this.config.event_sink.push(Event::EdgesAdded(edges.clone())); + this.broadcast_routing_table_update(RoutingTableUpdate::from_edges(edges)); + // Retu + oks.iter() + .map(|ok| match ok { + true => Ok(()), + false => Err(ReasonForBan::InvalidEdge), + }) + .collect() + }) + .await + .unwrap_or(Ok(())) + } + + fn record_routing_protocol_metrics(&self, target: &PeerId) { + let v1_dist = self.graph.routing_table.get_distance(target); + let v2_dist = self.graph_v2.routing_table.get_distance(target); + + match (v1_dist, v2_dist) { + (None, None) => { + metrics::NETWORK_ROUTED_MSG_DISTANCES.with_label_values(&["NONE"]).inc(); + } + (Some(_v1), None) => { + metrics::NETWORK_ROUTED_MSG_DISTANCES.with_label_values(&["v1 ONLY"]).inc(); + } + (None, Some(_v2)) => { + metrics::NETWORK_ROUTED_MSG_DISTANCES.with_label_values(&["v2 ONLY"]).inc(); + } + (Some(v1), Some(v2)) => { + if v1 == v2 { + metrics::NETWORK_ROUTED_MSG_DISTANCES.with_label_values(&["v1 == v2"]).inc(); + } else if v1 < v2 { + metrics::NETWORK_ROUTED_MSG_DISTANCES.with_label_values(&["v1 < v2"]).inc(); + } else { + metrics::NETWORK_ROUTED_MSG_DISTANCES.with_label_values(&["v1 > v2"]).inc(); + } + } + } + } + + pub(crate) fn tier2_find_route( + &self, + clock: &time::Clock, + target: &PeerIdOrHash, + ) -> Result { + match target { + PeerIdOrHash::PeerId(peer_id) => { + self.record_routing_protocol_metrics(peer_id); + + match self.graph.routing_table.find_next_hop_for_target(peer_id) { + Ok(peer_id) => Ok(peer_id), + Err(_) => self.graph_v2.routing_table.find_next_hop_for_target(peer_id), + } + } + PeerIdOrHash::Hash(hash) => self + .tier2_route_back + .lock() + .remove(clock, hash) + .ok_or(FindRouteError::RouteBackNotFound), + } + } + + /// Accepts a routed message. If we expect a response for the message, writes an entry in + /// the appropriate RouteBackCache recording the peer node from which the message came. + /// The cache entry will later be used to route back the response to the message. + pub(crate) fn add_route_back( + &self, + clock: &time::Clock, + conn: &connection::Connection, + msg: &RoutedMessageV2, + ) { + if !msg.expect_response() { + return; + } + + tracing::trace!(target: "network", route_back = ?msg.clone(), "Received peer message that requires response"); + let from = &conn.peer_info.id; + match conn.tier { + tcp::Tier::T1 => self.tier1_route_back.lock().insert(&clock, msg.hash(), from.clone()), + tcp::Tier::T2 => self.tier2_route_back.lock().insert(&clock, msg.hash(), from.clone()), + } + } + + pub(crate) fn compare_route_back(&self, hash: CryptoHash, peer_id: &PeerId) -> bool { + self.tier2_route_back.lock().get(&hash).map_or(false, |value| value == peer_id) + } + + /// Accepts NetworkTopologyChange events. + /// Changes are batched via the `update_routes_demux`, then passed to the V2 routing table. + /// If an updated DistanceVector is returned by the routing table, broadcasts it to peers. + /// If an error occurs while processing a DistanceVector advertised by a peer, bans the peer. + pub async fn update_routes( + self: &Arc, + clock: &time::Clock, + event: NetworkTopologyChange, + ) -> Result<(), ReasonForBan> { + let this = self.clone(); + let clock = clock.clone(); + self.update_routes_demux + .call(event, |events: Vec| async move { + let (to_broadcast, oks) = + this.graph_v2.batch_process_network_changes(&clock, events).await; + + if let Some(my_distance_vector) = to_broadcast { + this.broadcast_distance_vector(my_distance_vector); + } + + oks.iter() + .map(|ok| match ok { + true => Ok(()), + false => Err(ReasonForBan::InvalidDistanceVector), + }) + .collect() + }) + .await + .unwrap_or(Ok(())) + } + + /// Update the routing protocols with a set of peers to avoid routing through. + pub fn set_unreliable_peers(&self, unreliable_peers: HashSet) { + self.graph.set_unreliable_peers(unreliable_peers.clone()); + self.graph_v2.set_unreliable_peers(unreliable_peers); + } +} diff --git a/chain/network/src/peer_manager/network_state/tier1.rs b/chain/network/src/peer_manager/network_state/tier1.rs new file mode 100644 index 000000000..309047f16 --- /dev/null +++ b/chain/network/src/peer_manager/network_state/tier1.rs @@ -0,0 +1,366 @@ +use crate::accounts_data::{AccountDataCacheSnapshot, LocalAccountData}; +use crate::config; +use crate::network_protocol::{ + AccountData, PeerAddr, PeerInfo, PeerMessage, SignedAccountData, SyncAccountsData, +}; +use crate::peer::peer_actor::PeerActor; +use crate::peer_manager::connection; +use crate::stun; +use crate::tcp; +use crate::types::PeerType; +use unc_async::time; +use unc_crypto::PublicKey; +use unc_o11y::log_assert; +use unc_primitives::network::PeerId; +use rand::seq::IteratorRandom as _; +use rand::seq::SliceRandom as _; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; + +impl super::NetworkState { + // Returns ValidatorConfig of this node iff it belongs to TIER1 according to `accounts_data`. + pub fn tier1_validator_config( + &self, + accounts_data: &AccountDataCacheSnapshot, + ) -> Option<&config::ValidatorConfig> { + if self.config.tier1.is_none() { + return None; + } + self.config + .validator + .as_ref() + .filter(|cfg| accounts_data.keys.contains(&cfg.signer.public_key())) + } + + async fn tier1_connect_to_my_proxies( + self: &Arc, + clock: &time::Clock, + proxies: &[PeerAddr], + ) { + let tier1 = self.tier1.load(); + // Try to connect to all proxies in parallel. + let mut handles = vec![]; + for proxy in proxies { + // Skip the proxies we are already connected to. + if tier1.ready.contains_key(&proxy.peer_id) { + continue; + } + handles.push(async move { + let res = async { + let stream = tcp::Stream::connect( + &PeerInfo { + id: proxy.peer_id.clone(), + addr: Some(proxy.addr), + account_id: None, + }, + tcp::Tier::T1, + ) + .await?; + anyhow::Ok(PeerActor::spawn_and_handshake(clock.clone(), stream, None, self.clone()).await?) + }.await; + if let Err(err) = res { + tracing::warn!(target:"network", ?err, "failed to establish connection to TIER1 proxy {:?}",proxy); + } + }); + } + futures_util::future::join_all(handles).await; + } + + /// Requests direct peers for accounts data full sync. + /// Should be called whenever the accounts_data.keys changes, and + /// periodically just in case. + pub fn tier1_request_full_sync(&self) { + self.tier2.broadcast_message(Arc::new(PeerMessage::SyncAccountsData(SyncAccountsData { + incremental: true, + requesting_full_sync: true, + accounts_data: vec![], + }))); + } + + /// Tries to connect to ALL trusted proxies from the config, then broadcasts AccountData with + /// the set of proxies it managed to connect to. This way other TIER1 nodes can just connect + /// to ANY proxy of this node. + pub async fn tier1_advertise_proxies( + self: &Arc, + clock: &time::Clock, + ) -> Option> { + // Tier1 advertise proxies calls should be disjoint, + // to avoid a race condition while connecting to the proxies. + // TODO(gprusak): there are more corner cases to cover, because + // tier1_connect may also spawn TIER1 connections conflicting with + // tier1_advertise_proxies. It would be better to be able to await + // handshake on connection attempts, even if another call spawned them. + let _lock = self.tier1_advertise_proxies_mutex.lock().await; + let accounts_data = self.accounts_data.load(); + + let vc = self.tier1_validator_config(&accounts_data)?; + let proxies = match (&self.config.node_addr, &vc.proxies) { + (None, _) => vec![], + (_, config::ValidatorProxies::Static(peer_addrs)) => peer_addrs.clone(), + // If Dynamic are specified, + // it means that this node is its own proxy. + // Discover the public IP of this node using those STUN servers. + // We do not require all stun servers to be available, but + // we require the received responses to be consistent. + (Some(node_addr), config::ValidatorProxies::Dynamic(stun_servers)) => { + // Query all the STUN servers in parallel. + let queries = stun_servers.iter().map(|addr| { + let clock = clock.clone(); + let addr = addr.clone(); + self.spawn(async move { + match stun::query(&clock, &addr).await { + Ok(ip) => Some(ip), + Err(err) => { + tracing::warn!(target:"network", "STUN lookup failed for {addr}: {err}"); + None + } + } + }) + }); + let mut node_ips = vec![]; + for q in queries { + node_ips.extend(q.await.unwrap()); + } + // Check that we have received non-zero responses and that they are consistent. + if node_ips.is_empty() { + vec![] + } else if !node_ips.iter().all(|ip| ip == &node_ips[0]) { + tracing::warn!(target:"network", "received inconsistent responses from the STUN servers"); + vec![] + } else { + vec![PeerAddr { + peer_id: self.config.node_id(), + addr: std::net::SocketAddr::new(node_ips[0], node_addr.port()), + }] + } + } + }; + self.tier1_connect_to_my_proxies(clock, &proxies).await; + + // Snapshot tier1 connections again before broadcasting. + let tier1 = self.tier1.load(); + + let my_proxies = match &vc.proxies { + // In case of dynamic configuration, only the node itself can be its proxy, + // so we look for a loop connection which would prove our node's address. + config::ValidatorProxies::Dynamic(_) => match tier1.ready.get(&self.config.node_id()) { + Some(conn) => { + log_assert!(PeerType::Outbound == conn.peer_type); + log_assert!(conn.peer_info.addr.is_some()); + match conn.peer_info.addr { + Some(addr) => vec![PeerAddr { peer_id: self.config.node_id(), addr }], + None => vec![], + } + } + None => vec![], + }, + // In case of static configuration, we look for connections to proxies matching the config. + config::ValidatorProxies::Static(proxies) => { + let mut connected_proxies = vec![]; + for proxy in proxies { + match tier1.ready.get(&proxy.peer_id) { + // Here we compare the address from the config with the + // address of the connection (which is the IP, to which the + // TCP socket is connected + port indicated by the peer). + // We will broadcast only those addresses which we confirmed are + // valid (i.e. we managed to connect to them). + // + // TODO(gprusak): It may happen that a single peer will be + // available under multiple IPs, in which case, we should + // prefer to connect to the IP from the config, however + // that would require having separate inbound and outbound + // pools, so that both endpoints can keep a connection + // to the IP that they prefer. This is a corner case which can happen + // only if 2 TIER1 validators are proxies for some other validator. + Some(conn) if conn.peer_info.addr == Some(proxy.addr) => { + connected_proxies.push(proxy.clone()); + } + Some(conn) => { + tracing::info!(target:"network", "connected to {}, but got addr {:?}, while want {}",conn.peer_info.id,conn.peer_info.addr,proxy.addr) + } + _ => {} + } + } + connected_proxies + } + }; + tracing::info!(target:"network","connected to proxies {my_proxies:?}"); + let new_data = self.accounts_data.set_local( + clock, + LocalAccountData { + signer: vc.signer.clone(), + data: Arc::new(AccountData { peer_id: self.config.node_id(), proxies: my_proxies }), + }, + ); + // Early exit in case this node is not a TIER1 node any more. + let new_data = new_data?; + // Advertise the new_data. + self.tier2.broadcast_message(Arc::new(PeerMessage::SyncAccountsData(SyncAccountsData { + incremental: true, + requesting_full_sync: false, + accounts_data: vec![new_data.clone()], + }))); + Some(new_data) + } + + /// Closes TIER1 connections from nodes which are not TIER1 any more. + /// If this node is TIER1, it additionally connects to proxies of other TIER1 nodes. + pub async fn tier1_connect(self: &Arc, clock: &time::Clock) { + let tier1_cfg = match &self.config.tier1 { + Some(it) => it, + None => return, + }; + if !tier1_cfg.enable_outbound { + return; + } + let accounts_data = self.accounts_data.load(); + let validator_cfg = self.tier1_validator_config(&accounts_data); + + // Construct indices on accounts_data. + let mut accounts_by_proxy = HashMap::<_, Vec<_>>::new(); + let mut proxies_by_account = HashMap::<_, Vec<_>>::new(); + for d in accounts_data.data.values() { + proxies_by_account.entry(&d.account_key).or_default().extend(d.proxies.iter()); + for p in &d.proxies { + accounts_by_proxy.entry(&p.peer_id).or_default().push(&d.account_key); + } + } + + // Browse the connections from newest to oldest. + let tier1 = self.tier1.load(); + let mut ready: Vec<_> = tier1.ready.values().collect(); + ready.sort_unstable_by_key(|c| c.established_time); + ready.reverse(); + + // Select the oldest TIER1 connection for each account. + let mut safe = HashMap::<&PublicKey, &PeerId>::new(); + + match validator_cfg { + // TIER1 nodes can establish outbound connections to other TIER1 nodes and TIER1 proxies. + // TIER1 nodes can also accept inbound connections from TIER1 nodes. + Some(_) => { + for conn in &ready { + if conn.peer_type != PeerType::Outbound { + continue; + } + let peer_id = &conn.peer_info.id; + for key in accounts_by_proxy.get(peer_id).into_iter().flatten() { + safe.insert(key, peer_id); + } + } + // Direct TIER1 connections have priority over proxy connections. + for key in &accounts_data.keys { + if let Some(conn) = tier1.ready_by_account_key.get(&key) { + safe.insert(key, &conn.peer_info.id); + } + } + } + // All the other nodes should accept inbound connections from TIER1 nodes + // (to act as a TIER1 proxy). + None => { + for key in &accounts_data.keys { + if let Some(conn) = tier1.ready_by_account_key.get(&key) { + if conn.peer_type == PeerType::Inbound { + safe.insert(key, &conn.peer_info.id); + } + } + } + } + } + + // Construct a safe set of connections. + let mut safe_set: HashSet = safe.values().map(|v| (*v).clone()).collect(); + // Add proxies of our node to the safe set. + if let Some(vc) = validator_cfg { + match &vc.proxies { + config::ValidatorProxies::Dynamic(_) => { + safe_set.insert(self.config.node_id()); + } + config::ValidatorProxies::Static(peer_addrs) => { + // TODO(gprusak): here we add peer_id to a safe set, even if + // the conn.peer_addr doesn't match the address from the validator config + // (so we cannot advertise it as our proxy). Consider making it more precise. + safe_set.extend(peer_addrs.iter().map(|pa| pa.peer_id.clone())); + } + } + } + // Close all other connections, as they are redundant or are no longer TIER1. + for conn in tier1.ready.values() { + if !safe_set.contains(&conn.peer_info.id) { + conn.stop(None); + } + } + if let Some(vc) = validator_cfg { + // Try to establish new TIER1 connections to accounts in random order. + let mut handles = vec![]; + let mut account_keys: Vec<_> = proxies_by_account.keys().copied().collect(); + account_keys.shuffle(&mut rand::thread_rng()); + for account_key in account_keys { + // tier1_connect() is responsible for connecting to proxies + // of this node. tier1_connect() connects only to proxies + // of other TIER1 nodes. + if account_key == &vc.signer.public_key() { + continue; + } + // Bound the number of connections established at a single call to + // tier1_connect(). + if handles.len() as u64 >= tier1_cfg.new_connections_per_attempt { + break; + } + // If we are already connected to some proxy of account_key, then + // don't establish another connection. + if safe.contains_key(account_key) { + continue; + } + // Find addresses of proxies of account_key. + let proxies: Vec<&PeerAddr> = + proxies_by_account.get(account_key).into_iter().flatten().map(|x| *x).collect(); + // Select a random proxy of the account_key and try to connect to it. + let proxy = proxies.iter().choose(&mut rand::thread_rng()); + if let Some(proxy) = proxy { + let proxy = (*proxy).clone(); + handles.push(async move { + let stream = tcp::Stream::connect( + &PeerInfo { + id: proxy.peer_id, + addr: Some(proxy.addr), + account_id: None, + }, + tcp::Tier::T1, + ) + .await?; + PeerActor::spawn_and_handshake(clock.clone(), stream, None, self.clone()) + .await + }); + } + } + tracing::debug!(target:"network","{}: establishing {} new connections",self.config.node_id(),handles.len()); + for res in futures_util::future::join_all(handles).await { + if let Err(err) = res { + tracing::info!(target:"network", ?err, "{}: failed to establish a TIER1 connection",self.config.node_id()); + } + } + tracing::debug!(target:"network","{}: establishing new connections DONE",self.config.node_id()); + } + } + + /// Finds a TIER1 connection for the given SignedAccountData. + /// It is expected to perform <10 lookups total on average, + /// so the call latency should be negligible wrt sending a TCP packet. + // TODO(gprusak): If not, consider precomputing the AccountKey -> Connection mapping. + pub fn get_tier1_proxy(&self, data: &SignedAccountData) -> Option> { + let tier1 = self.tier1.load(); + // Prefer direct connections. + if let Some(conn) = tier1.ready_by_account_key.get(&data.account_key) { + return Some(conn.clone()); + } + // In case there is no direct connection and our node is a TIER1 validator, use a proxy. + // TODO(gprusak): add a check that our node is actually a TIER1 validator. + for proxy in &data.proxies { + if let Some(conn) = tier1.ready.get(&proxy.peer_id) { + return Some(conn.clone()); + } + } + None + } +} diff --git a/chain/network/src/peer_manager/peer_manager_actor.rs b/chain/network/src/peer_manager/peer_manager_actor.rs new file mode 100644 index 000000000..617c9be98 --- /dev/null +++ b/chain/network/src/peer_manager/peer_manager_actor.rs @@ -0,0 +1,1144 @@ +use crate::config; +use crate::debug::{DebugStatus, GetDebugStatus}; +use crate::network_protocol::SyncSnapshotHosts; +use crate::network_protocol::{ + Disconnect, Edge, PeerIdOrHash, PeerMessage, Ping, Pong, RawRoutedMessage, RoutedMessageBody, +}; +use crate::peer::peer_actor::PeerActor; +use crate::peer_manager::connection; +use crate::peer_manager::network_state::{NetworkState, WhitelistNode}; +use crate::peer_manager::peer_store; +use crate::shards_manager::ShardsManagerRequestFromNetwork; +use crate::stats::metrics; +use crate::store; +use crate::tcp; +use crate::types::{ + ConnectedPeerInfo, HighestHeightPeerInfo, KnownProducer, NetworkInfo, NetworkRequests, + NetworkResponses, PeerInfo, PeerManagerMessageRequest, PeerManagerMessageResponse, PeerType, + SetChainInfo, SnapshotHostInfo, +}; +use crate::{client, network_protocol}; +use actix::fut::future::wrap_future; +use actix::{Actor as _, AsyncContext as _}; +use anyhow::Context as _; +use unc_async::messaging::Sender; +use unc_async::time; +use unc_o11y::{handler_debug_span, handler_trace_span, OpenTelemetrySpanExt, WithSpanContext}; +use unc_performance_metrics_macros::perf; +use unc_primitives::block::GenesisId; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::views::{ + ConnectionInfoView, EdgeView, KnownPeerStateView, NetworkGraphView, PeerStoreView, + RecentOutboundConnectionsView, SnapshotHostInfoView, SnapshotHostsView, +}; +use network_protocol::MAX_SHARDS_PER_SNAPSHOT_HOST_INFO; +use rand::seq::{IteratorRandom, SliceRandom}; +use rand::thread_rng; +use rand::Rng; +use std::cmp::min; +use std::collections::HashSet; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use tracing::Instrument as _; + +/// Ratio between consecutive attempts to establish connection with another peer. +/// In the kth step node should wait `10 * EXPONENTIAL_BACKOFF_RATIO**k` milliseconds +const EXPONENTIAL_BACKOFF_RATIO: f64 = 1.1; +/// The initial waiting time between consecutive attempts to establish connection +const MONITOR_PEERS_INITIAL_DURATION: time::Duration = time::Duration::milliseconds(10); +/// How often should we check wheter local edges match the connection pool. +const FIX_LOCAL_EDGES_INTERVAL: time::Duration = time::Duration::seconds(60); +/// How much time we give fix_local_edges() to resolve the discrepancies, before forcing disconnect. +const FIX_LOCAL_EDGES_TIMEOUT: time::Duration = time::Duration::seconds(6); + +/// Number of times to attempt reconnection when trying to re-establish a connection. +const MAX_RECONNECT_ATTEMPTS: usize = 6; + +/// How often to report bandwidth stats. +const REPORT_BANDWIDTH_STATS_TRIGGER_INTERVAL: time::Duration = + time::Duration::milliseconds(60_000); + +/// If we received more than `REPORT_BANDWIDTH_THRESHOLD_BYTES` of data from given peer it's bandwidth stats will be reported. +const REPORT_BANDWIDTH_THRESHOLD_BYTES: usize = 10_000_000; +/// If we received more than REPORT_BANDWIDTH_THRESHOLD_COUNT` of messages from given peer it's bandwidth stats will be reported. +const REPORT_BANDWIDTH_THRESHOLD_COUNT: usize = 10_000; + +/// If a peer is more than these blocks behind (comparing to our current head) - don't route any messages through it. +/// We are updating the list of unreliable peers every MONITOR_PEER_MAX_DURATION (60 seconds) - so the current +/// horizon value is roughly matching this threshold (if the node is 60 blocks behind, it will take it a while to recover). +/// If we set this horizon too low (for example 2 blocks) - we're risking excluding a lot of peers in case of a short +/// network issue. +const UNRELIABLE_PEER_HORIZON: u64 = 60; + +/// Due to implementation limits of `Graph` in `unc-network`, we support up to 128 client. +pub const MAX_TIER2_PEERS: usize = 128; + +/// When picking a peer to connect to, we'll pick from the 'safer peers' +/// (a.k.a. ones that we've been connected to in the past) with these odds. +/// Otherwise, we'd pick any peer that we've heard about. +const PREFER_PREVIOUSLY_CONNECTED_PEER: f64 = 0.6; + +/// How often to update the connections in storage. +pub(crate) const UPDATE_CONNECTION_STORE_INTERVAL: time::Duration = time::Duration::minutes(1); +/// How often to poll the NetworkState for closed connections we'd like to re-establish. +pub(crate) const POLL_CONNECTION_STORE_INTERVAL: time::Duration = time::Duration::minutes(1); + +/// Actor that manages peers connections. +pub struct PeerManagerActor { + pub(crate) clock: time::Clock, + /// Peer information for this node. + my_peer_id: PeerId, + /// Flag that track whether we started attempts to establish outbound connections. + started_connect_attempts: bool, + + /// State that is shared between multiple threads (including PeerActors). + pub(crate) state: Arc, +} + +/// TEST-ONLY +/// A generic set of events (observable in tests) that the Network may generate. +/// Ideally the tests should observe only public API properties, but until +/// we are at that stage, feel free to add any events that you need to observe. +/// In particular prefer emitting a new event to polling for a state change. +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Event { + PeerManagerStarted, + ServerStarted, + RoutedMessageDropped, + AccountsAdded(Vec), + EdgesAdded(Vec), + Ping(Ping), + Pong(Pong), + // Reported once a message has been processed. + // In contrast to typical RPC protocols, many P2P messages do not trigger + // sending a response at the end of processing. + // However, for precise instrumentation in tests it is useful to know when + // processing has been finished. We simulate the "RPC response" by reporting + // an event MessageProcessed. + // + // Given that processing is asynchronous and unstructured as of now, + // it is hard to pinpoint all the places when the processing of a message is + // actually complete. Currently this event is reported only for some message types, + // feel free to add support for more. + MessageProcessed(tcp::Tier, PeerMessage), + // Reported when a reconnect loop is spawned. + ReconnectLoopSpawned(PeerInfo), + // Reported when a handshake has been started. + HandshakeStarted(crate::peer::peer_actor::HandshakeStartedEvent), + // Reported when a handshake has been successfully completed. + HandshakeCompleted(crate::peer::peer_actor::HandshakeCompletedEvent), + // Reported when the TCP connection has been closed. + ConnectionClosed(crate::peer::peer_actor::ConnectionClosedEvent), +} + +impl actix::Actor for PeerManagerActor { + type Context = actix::Context; + + fn started(&mut self, ctx: &mut Self::Context) { + // Periodically push network information to client. + self.push_network_info_trigger(ctx, self.state.config.push_info_period); + + // Attempt to reconnect to recent outbound connections from storage + if self.state.config.connect_to_reliable_peers_on_startup { + tracing::debug!(target: "network", "Reconnecting to reliable peers from storage"); + self.bootstrap_outbound_from_recent_connections(ctx); + } else { + tracing::debug!(target: "network", "Skipping reconnection to reliable peers"); + } + + // Periodically starts peer monitoring. + tracing::debug!(target: "network", + max_period=?self.state.config.monitor_peers_max_period, + "monitor_peers_trigger"); + self.monitor_peers_trigger( + ctx, + MONITOR_PEERS_INITIAL_DURATION, + (MONITOR_PEERS_INITIAL_DURATION, self.state.config.monitor_peers_max_period), + ); + + // Periodically fix local edges. + let clock = self.clock.clone(); + let state = self.state.clone(); + ctx.spawn(wrap_future(async move { + let mut interval = time::Interval::new(clock.now(), FIX_LOCAL_EDGES_INTERVAL); + loop { + interval.tick(&clock).await; + state.fix_local_edges(&clock, FIX_LOCAL_EDGES_TIMEOUT).await; + } + })); + + // Periodically update the connection store. + let clock = self.clock.clone(); + let state = self.state.clone(); + ctx.spawn(wrap_future(async move { + let mut interval = time::Interval::new(clock.now(), UPDATE_CONNECTION_STORE_INTERVAL); + loop { + interval.tick(&clock).await; + state.update_connection_store(&clock); + } + })); + + // Periodically prints bandwidth stats for each peer. + self.report_bandwidth_stats_trigger(ctx, REPORT_BANDWIDTH_STATS_TRIGGER_INTERVAL); + + self.state.config.event_sink.push(Event::PeerManagerStarted); + } + + /// Try to gracefully disconnect from connected peers. + fn stopping(&mut self, _ctx: &mut Self::Context) -> actix::Running { + tracing::warn!("PeerManager: stopping"); + self.state.tier2.broadcast_message(Arc::new(PeerMessage::Disconnect(Disconnect { + remove_from_connection_store: false, + }))); + actix::Running::Stop + } + + fn stopped(&mut self, _ctx: &mut Self::Context) { + actix::Arbiter::current().stop(); + } +} + +impl PeerManagerActor { + pub fn spawn( + clock: time::Clock, + store: Arc, + config: config::NetworkConfig, + client: Arc, + shards_manager_adapter: Sender, + genesis_id: GenesisId, + ) -> anyhow::Result> { + let config = config.verify().context("config")?; + let store = store::Store::from(store); + let peer_store = peer_store::PeerStore::new(&clock, config.peer_store.clone()) + .context("PeerStore::new")?; + tracing::debug!(target: "network", + len = peer_store.len(), + boot_nodes = config.peer_store.boot_nodes.len(), + banned = peer_store.count_banned(), + "Found known peers"); + tracing::debug!(target: "network", blacklist = ?config.peer_store.blacklist, "Blacklist"); + let whitelist_nodes = { + let mut v = vec![]; + for wn in &config.whitelist_nodes { + v.push(WhitelistNode::from_peer_info(wn)?); + } + v + }; + let my_peer_id = config.node_id(); + let arbiter = actix::Arbiter::new().handle(); + let clock = clock; + let state = Arc::new(NetworkState::new( + &clock, + store, + peer_store, + config, + genesis_id, + client, + shards_manager_adapter, + whitelist_nodes, + )); + arbiter.spawn({ + let arbiter = arbiter.clone(); + let state = state.clone(); + let clock = clock.clone(); + async move { + // Start server if address provided. + if let Some(server_addr) = &state.config.node_addr { + tracing::debug!(target: "network", at = ?server_addr, "starting public server"); + let mut listener = match server_addr.listener() { + Ok(it) => it, + Err(e) => { + panic!("failed to start listening on server_addr={server_addr:?} e={e:?}") + } + }; + state.config.event_sink.push(Event::ServerStarted); + arbiter.spawn({ + let clock = clock.clone(); + let state = state.clone(); + async move { + loop { + if let Ok(stream) = listener.accept().await { + // Always let the new peer to send a handshake message. + // Only then we can decide whether we should accept a connection. + // It is expected to be reasonably cheap: eventually, for TIER2 network + // we would like to exchange set of connected peers even without establishing + // a proper connection. + tracing::debug!(target: "network", from = ?stream.peer_addr, "got new connection"); + if let Err(err) = + PeerActor::spawn(clock.clone(), stream, None, state.clone()) + { + tracing::info!(target:"network", ?err, "PeerActor::spawn()"); + } + } + } + } + }); + } + if let Some(cfg) = state.config.tier1.clone() { + // Connect to TIER1 proxies and broadcast the list those connections periodically. + arbiter.spawn({ + let clock = clock.clone(); + let state = state.clone(); + let mut interval = time::Interval::new(clock.now(), cfg.advertise_proxies_interval); + async move { + loop { + interval.tick(&clock).await; + state.tier1_request_full_sync(); + state.tier1_advertise_proxies(&clock).await; + } + } + }); + // Update TIER1 connections periodically. + arbiter.spawn({ + let clock = clock.clone(); + let state = state.clone(); + let mut interval = tokio::time::interval(cfg.connect_interval.try_into().unwrap()); + interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); + async move { + loop { + interval.tick().await; + state.tier1_connect(&clock).await; + } + } + }); + } + // Periodically poll the connection store for connections we'd like to re-establish + arbiter.spawn({ + let clock = clock.clone(); + let state = state.clone(); + let arbiter = arbiter.clone(); + let mut interval = time::Interval::new(clock.now(), POLL_CONNECTION_STORE_INTERVAL); + async move { + loop { + interval.tick(&clock).await; + // Poll the NetworkState for all pending reconnect attempts + let pending_reconnect = state.poll_pending_reconnect(); + // Spawn a separate reconnect loop for each pending reconnect attempt + for peer_info in pending_reconnect { + arbiter.spawn({ + let state = state.clone(); + let clock = clock.clone(); + let peer_info = peer_info.clone(); + async move { + state.reconnect(clock, peer_info, MAX_RECONNECT_ATTEMPTS).await; + } + }); + + state.config.event_sink.push(Event::ReconnectLoopSpawned(peer_info)); + } + } + } + }); + } + }); + Ok(Self::start_in_arbiter(&arbiter, move |_ctx| Self { + my_peer_id: my_peer_id.clone(), + started_connect_attempts: false, + state, + clock, + })) + } + + /// Periodically prints bandwidth stats for each peer. + fn report_bandwidth_stats_trigger( + &mut self, + ctx: &mut actix::Context, + every: time::Duration, + ) { + let _timer = metrics::PEER_MANAGER_TRIGGER_TIME + .with_label_values(&["report_bandwidth_stats"]) + .start_timer(); + let mut total_bandwidth_used_by_all_peers: usize = 0; + let mut total_msg_received_count: usize = 0; + for (peer_id, connected_peer) in &self.state.tier2.load().ready { + let bandwidth_used = + connected_peer.stats.received_bytes.swap(0, Ordering::Relaxed) as usize; + let msg_received_count = + connected_peer.stats.received_messages.swap(0, Ordering::Relaxed) as usize; + if bandwidth_used > REPORT_BANDWIDTH_THRESHOLD_BYTES + || msg_received_count > REPORT_BANDWIDTH_THRESHOLD_COUNT + { + tracing::debug!(target: "bandwidth", + ?peer_id, + bandwidth_used, msg_received_count, "Peer bandwidth exceeded threshold", + ); + } + total_bandwidth_used_by_all_peers += bandwidth_used; + total_msg_received_count += msg_received_count; + } + + tracing::info!( + target: "bandwidth", + total_bandwidth_used_by_all_peers, + total_msg_received_count, "Bandwidth stats" + ); + + unc_performance_metrics::actix::run_later( + ctx, + every.try_into().unwrap(), + move |act, ctx| { + act.report_bandwidth_stats_trigger(ctx, every); + }, + ); + } + + /// Check if it is needed to create a new outbound connection. + /// If the number of active connections is less than `ideal_connections_lo` or + /// (the number of outgoing connections is less than `minimum_outbound_peers` + /// and the total connections is less than `max_num_peers`) + fn is_outbound_bootstrap_needed(&self) -> bool { + let tier2 = self.state.tier2.load(); + let total_connections = tier2.ready.len() + tier2.outbound_handshakes.len(); + let potential_outbound_connections = + tier2.ready.values().filter(|peer| peer.peer_type == PeerType::Outbound).count() + + tier2.outbound_handshakes.len(); + + (total_connections < self.state.config.ideal_connections_lo as usize + || (total_connections < self.state.config.max_num_peers as usize + && potential_outbound_connections + < self.state.config.minimum_outbound_peers as usize)) + && !self.state.config.outbound_disabled + } + + /// Returns peers close to the highest height + fn highest_height_peers(&self) -> Vec { + let infos: Vec = self + .state + .tier2 + .load() + .ready + .values() + .filter_map(|p| p.full_peer_info().into()) + .collect(); + + // This finds max height among peers, and returns one peer close to such height. + let max_height = match infos.iter().map(|i| i.highest_block_height).max() { + Some(height) => height, + None => return vec![], + }; + // Find all peers whose height is within `highest_peer_horizon` from max height peer(s). + infos + .into_iter() + .filter(|i| { + i.highest_block_height.saturating_add(self.state.config.highest_peer_horizon) + >= max_height + }) + .collect() + } + + // Get peers that are potentially unreliable and we should avoid routing messages through them. + // Currently we're picking the peers that are too much behind (in comparison to us). + fn unreliable_peers(&self) -> HashSet { + // If chain info is not set, that means we haven't received chain info message + // from chain yet. Return empty set in that case. This should only last for a short period + // of time. + let binding = self.state.chain_info.load(); + let chain_info = if let Some(it) = binding.as_ref() { + it + } else { + return HashSet::new(); + }; + let my_height = chain_info.block.header().height(); + // Find all peers whose height is below `highest_peer_horizon` from max height peer(s). + // or the ones we don't have height information yet + self.state + .tier2 + .load() + .ready + .values() + .filter(|p| { + p.last_block + .load() + .as_ref() + .map(|x| x.height.saturating_add(UNRELIABLE_PEER_HORIZON) < my_height) + .unwrap_or(false) + }) + .map(|p| p.peer_info.id.clone()) + .collect() + } + + /// Check if the number of connections (excluding whitelisted ones) exceeds ideal_connections_hi. + /// If so, constructs a safe set of peers and selects one random peer outside of that set + /// and sends signal to stop connection to it gracefully. + /// + /// Safe set contruction process: + /// 1. Add all whitelisted peers to the safe set. + /// 2. If the number of outbound connections is less or equal than minimum_outbound_connections, + /// add all outbound connections to the safe set. + /// 3. Find all peers who sent us a message within the last peer_recent_time_window, + /// and add them one by one to the safe_set (starting from earliest connection time) + /// until safe set has safe_set_size elements. + fn maybe_stop_active_connection(&self) { + let tier2 = self.state.tier2.load(); + let filter_peers = |predicate: &dyn Fn(&connection::Connection) -> bool| -> Vec<_> { + tier2 + .ready + .values() + .filter(|peer| predicate(&*peer)) + .map(|peer| peer.peer_info.id.clone()) + .collect() + }; + + // Build safe set + let mut safe_set = HashSet::new(); + + // Add whitelisted nodes to the safe set. + let whitelisted_peers = filter_peers(&|p| self.state.is_peer_whitelisted(&p.peer_info)); + safe_set.extend(whitelisted_peers); + + // If there is not enough non-whitelisted peers, return without disconnecting anyone. + if tier2.ready.len() - safe_set.len() <= self.state.config.ideal_connections_hi as usize { + return; + } + + // If there is not enough outbound peers, add them to the safe set. + let outbound_peers = filter_peers(&|p| p.peer_type == PeerType::Outbound); + if outbound_peers.len() + tier2.outbound_handshakes.len() + <= self.state.config.minimum_outbound_peers as usize + { + safe_set.extend(outbound_peers); + } + + // If there is not enough archival peers, add them to the safe set. + if self.state.config.archive { + let archival_peers = filter_peers(&|p| p.archival); + if archival_peers.len() + <= self.state.config.archival_peer_connections_lower_bound as usize + { + safe_set.extend(archival_peers); + } + } + + // Find all recently active peers. + let now = self.clock.now(); + let mut active_peers: Vec> = tier2 + .ready + .values() + .filter(|p| { + now - p.last_time_received_message.load() + < self.state.config.peer_recent_time_window + }) + .cloned() + .collect(); + + // Sort by established time. + active_peers.sort_by_key(|p| p.established_time); + // Saturate safe set with recently active peers. + let set_limit = self.state.config.safe_set_size as usize; + for p in active_peers { + if safe_set.len() >= set_limit { + break; + } + safe_set.insert(p.peer_info.id.clone()); + } + + // Build valid candidate list to choose the peer to be removed. All peers outside the safe set. + let candidates = tier2.ready.values().filter(|p| !safe_set.contains(&p.peer_info.id)); + if let Some(p) = candidates.choose(&mut rand::thread_rng()) { + tracing::debug!(target: "network", id = ?p.peer_info.id, + tier2_len = tier2.ready.len(), + ideal_connections_hi = self.state.config.ideal_connections_hi, + "Stop active connection" + ); + p.stop(None); + } + } + + /// Periodically monitor list of peers and: + /// - request new peers from connected peers, + /// - bootstrap outbound connections from known peers, + /// - unban peers that have been banned for awhile, + /// - remove expired peers, + /// + /// # Arguments: + /// - `interval` - Time between consequent runs. + /// - `default_interval` - we will set `interval` to this value once, after first successful connection + /// - `max_interval` - maximum value of interval + /// NOTE: in the current implementation `interval` increases by 1% every time, and it will + /// reach value of `max_internal` eventually. + fn monitor_peers_trigger( + &mut self, + ctx: &mut actix::Context, + mut interval: time::Duration, + (default_interval, max_interval): (time::Duration, time::Duration), + ) { + let _span = tracing::trace_span!(target: "network", "monitor_peers_trigger").entered(); + let _timer = + metrics::PEER_MANAGER_TRIGGER_TIME.with_label_values(&["monitor_peers"]).start_timer(); + + self.state.peer_store.update(&self.clock); + + if self.is_outbound_bootstrap_needed() { + let tier2 = self.state.tier2.load(); + // With some odds - try picking one of the 'NotConnected' peers -- these are the ones that we were able to connect to in the past. + let prefer_previously_connected_peer = + thread_rng().gen_bool(PREFER_PREVIOUSLY_CONNECTED_PEER); + if let Some(peer_info) = self.state.peer_store.unconnected_peer( + |peer_state| { + // Ignore connecting to ourself + self.my_peer_id == peer_state.peer_info.id + || self.state.config.node_addr.as_ref().map(|a|**a) == peer_state.peer_info.addr + // Or to peers we are currently trying to connect to + || tier2.outbound_handshakes.contains(&peer_state.peer_info.id) + }, + prefer_previously_connected_peer, + ) { + // Start monitor_peers_attempts from start after we discover the first healthy peer + if !self.started_connect_attempts { + self.started_connect_attempts = true; + interval = default_interval; + } + ctx.spawn(wrap_future({ + let state = self.state.clone(); + let clock = self.clock.clone(); + async move { + let result = async { + let stream = tcp::Stream::connect(&peer_info, tcp::Tier::T2).await.context("tcp::Stream::connect()")?; + PeerActor::spawn_and_handshake(clock.clone(),stream,None,state.clone()).await.context("PeerActor::spawn()")?; + anyhow::Ok(()) + }.await; + + if let Err(ref err) = result { + tracing::info!(target: "network", err = format!("{:#}", err), "failed to connect to {peer_info}"); + } + if state.peer_store.peer_connection_attempt(&clock, &peer_info.id, result).is_err() { + tracing::error!(target: "network", ?peer_info, "Failed to store connection attempt."); + } + }.instrument(tracing::trace_span!(target: "network", "monitor_peers_trigger_connect")) + })); + } + } + + // If there are too many active connections try to remove some connections + self.maybe_stop_active_connection(); + + // Find peers that are not reliable (too much behind) - and make sure that we're not routing messages through them. + let unreliable_peers = self.unreliable_peers(); + metrics::PEER_UNRELIABLE.set(unreliable_peers.len() as i64); + self.state.set_unreliable_peers(unreliable_peers); + + let new_interval = min(max_interval, interval * EXPONENTIAL_BACKOFF_RATIO); + + unc_performance_metrics::actix::run_later( + ctx, + interval.try_into().unwrap(), + move |act, ctx| { + act.monitor_peers_trigger(ctx, new_interval, (default_interval, max_interval)); + }, + ); + } + + /// Re-establish each outbound connection in the connection store (single attempt) + fn bootstrap_outbound_from_recent_connections(&self, ctx: &mut actix::Context) { + for conn_info in self.state.connection_store.get_recent_outbound_connections() { + ctx.spawn(wrap_future({ + let state = self.state.clone(); + let clock = self.clock.clone(); + let peer_info = conn_info.peer_info.clone(); + async move { + state.reconnect(clock, peer_info, 1).await; + } + })); + + self.state + .config + .event_sink + .push(Event::ReconnectLoopSpawned(conn_info.peer_info.clone())); + } + } + + pub(crate) fn get_network_info(&self) -> NetworkInfo { + let tier1 = self.state.tier1.load(); + let tier2 = self.state.tier2.load(); + let now = self.clock.now(); + let graph = self.state.graph.load(); + let connected_peer = |cp: &Arc| ConnectedPeerInfo { + full_peer_info: cp.full_peer_info(), + received_bytes_per_sec: cp.stats.received_bytes_per_sec.load(Ordering::Relaxed), + sent_bytes_per_sec: cp.stats.sent_bytes_per_sec.load(Ordering::Relaxed), + last_time_peer_requested: cp.last_time_peer_requested.load().unwrap_or(now), + last_time_received_message: cp.last_time_received_message.load(), + connection_established_time: cp.established_time, + peer_type: cp.peer_type, + nonce: match graph.local_edges.get(&cp.peer_info.id) { + Some(e) => e.nonce(), + None => 0, + }, + }; + NetworkInfo { + connected_peers: tier2.ready.values().map(connected_peer).collect(), + tier1_connections: tier1.ready.values().map(connected_peer).collect(), + num_connected_peers: tier2.ready.len(), + peer_max_count: self.state.config.max_num_peers, + highest_height_peers: self.highest_height_peers(), + sent_bytes_per_sec: tier2 + .ready + .values() + .map(|x| x.stats.sent_bytes_per_sec.load(Ordering::Relaxed)) + .sum(), + received_bytes_per_sec: tier2 + .ready + .values() + .map(|x| x.stats.received_bytes_per_sec.load(Ordering::Relaxed)) + .sum(), + known_producers: self + .state + .account_announcements + .get_announcements() + .into_iter() + .map(|announce_account| KnownProducer { + account_id: announce_account.account_id, + peer_id: announce_account.peer_id.clone(), + // TODO: fill in the address. + addr: None, + next_hops: self.state.graph.routing_table.view_route(&announce_account.peer_id), + }) + .collect(), + tier1_accounts_keys: self.state.accounts_data.load().keys.iter().cloned().collect(), + tier1_accounts_data: self.state.accounts_data.load().data.values().cloned().collect(), + } + } + + fn push_network_info_trigger(&self, ctx: &mut actix::Context, interval: time::Duration) { + let _span = tracing::trace_span!(target: "network", "push_network_info_trigger").entered(); + let network_info = self.get_network_info(); + let _timer = metrics::PEER_MANAGER_TRIGGER_TIME + .with_label_values(&["push_network_info"]) + .start_timer(); + // TODO(gprusak): just spawn a loop. + let state = self.state.clone(); + ctx.spawn(wrap_future( + async move { state.client.network_info(network_info).await }.instrument( + tracing::trace_span!(target: "network", "push_network_info_trigger_future"), + ), + )); + + unc_performance_metrics::actix::run_later( + ctx, + interval.try_into().unwrap(), + move |act, ctx| { + act.push_network_info_trigger(ctx, interval); + }, + ); + } + + #[perf] + fn handle_msg_network_requests( + &mut self, + msg: NetworkRequests, + ctx: &mut actix::Context, + ) -> NetworkResponses { + let msg_type: &str = msg.as_ref(); + let _span = + tracing::trace_span!(target: "network", "handle_msg_network_requests", msg_type) + .entered(); + metrics::REQUEST_COUNT_BY_TYPE_TOTAL.with_label_values(&[msg.as_ref()]).inc(); + match msg { + NetworkRequests::Block { block } => { + self.state.tier2.broadcast_message(Arc::new(PeerMessage::Block(block))); + NetworkResponses::NoResponse + } + NetworkRequests::Approval { approval_message } => { + self.state.send_message_to_account( + &self.clock, + &approval_message.target, + RoutedMessageBody::BlockApproval(approval_message.approval), + ); + NetworkResponses::NoResponse + } + NetworkRequests::BlockRequest { hash, peer_id } => { + if self.state.tier2.send_message(peer_id, Arc::new(PeerMessage::BlockRequest(hash))) + { + NetworkResponses::NoResponse + } else { + NetworkResponses::RouteNotFound + } + } + NetworkRequests::BlockHeadersRequest { hashes, peer_id } => { + if self + .state + .tier2 + .send_message(peer_id, Arc::new(PeerMessage::BlockHeadersRequest(hashes))) + { + NetworkResponses::NoResponse + } else { + NetworkResponses::RouteNotFound + } + } + NetworkRequests::StateRequestHeader { shard_id, sync_hash, peer_id } => { + if self.state.tier2.send_message( + peer_id, + Arc::new(PeerMessage::StateRequestHeader(shard_id, sync_hash)), + ) { + NetworkResponses::NoResponse + } else { + NetworkResponses::RouteNotFound + } + } + NetworkRequests::StateRequestPart { shard_id, sync_hash, part_id, peer_id } => { + if self.state.tier2.send_message( + peer_id, + Arc::new(PeerMessage::StateRequestPart(shard_id, sync_hash, part_id)), + ) { + NetworkResponses::NoResponse + } else { + NetworkResponses::RouteNotFound + } + } + NetworkRequests::SnapshotHostInfo { sync_hash, epoch_height, mut shards } => { + if shards.len() > MAX_SHARDS_PER_SNAPSHOT_HOST_INFO { + tracing::warn!("PeerManager: Sending out a SnapshotHostInfo message with {} shards, \ + this is more than the allowed limit. The list of shards will be truncated. \ + Please adjust the MAX_SHARDS_PER_SNAPSHOT_HOST_INFO constant ({})", shards.len(), MAX_SHARDS_PER_SNAPSHOT_HOST_INFO); + + // We can's send out more than MAX_SHARDS_PER_SNAPSHOT_HOST_INFO shards because other nodes would + // ban us for abusive behavior. Let's truncate the shards vector by choosing a random subset of + // MAX_SHARDS_PER_SNAPSHOT_HOST_INFO shard ids. Choosing a random subset slightly increases the chances + // that other nodes will have snapshot sync information about all shards from some node. + shards = shards + .choose_multiple(&mut rand::thread_rng(), MAX_SHARDS_PER_SNAPSHOT_HOST_INFO) + .copied() + .collect(); + } + // Sort the shards to keep things tidy + shards.sort(); + + // Sign the information about the locally created snapshot using the keys in the + // network config before broadcasting it + let snapshot_host_info = SnapshotHostInfo::new( + self.state.config.node_id(), + sync_hash, + epoch_height, + shards, + &self.state.config.node_key, + ); + + self.state.tier2.broadcast_message(Arc::new(PeerMessage::SyncSnapshotHosts( + SyncSnapshotHosts { hosts: vec![snapshot_host_info.into()] }, + ))); + NetworkResponses::NoResponse + } + NetworkRequests::BanPeer { peer_id, ban_reason } => { + self.state.disconnect_and_ban(&self.clock, &peer_id, ban_reason); + NetworkResponses::NoResponse + } + NetworkRequests::AnnounceAccount(announce_account) => { + let state = self.state.clone(); + ctx.spawn(wrap_future(async move { + state.add_accounts(vec![announce_account]).await; + })); + NetworkResponses::NoResponse + } + NetworkRequests::PartialEncodedChunkRequest { target, request, create_time } => { + metrics::PARTIAL_ENCODED_CHUNK_REQUEST_DELAY + .observe((self.clock.now() - create_time.0).as_seconds_f64()); + let mut success = false; + + // Make two attempts to send the message. First following the preference of `prefer_peer`, + // and if it fails, against the preference. + for prefer_peer in &[target.prefer_peer, !target.prefer_peer] { + if !prefer_peer { + if let Some(account_id) = target.account_id.as_ref() { + if self.state.send_message_to_account( + &self.clock, + account_id, + RoutedMessageBody::PartialEncodedChunkRequest(request.clone()), + ) { + success = true; + break; + } + } + } else { + let mut matching_peers = vec![]; + for (peer_id, peer) in &self.state.tier2.load().ready { + let last_block = peer.last_block.load(); + if (peer.archival || !target.only_archival) + && last_block.is_some() + && last_block.as_ref().unwrap().height >= target.min_height + && peer.tracked_shards.contains(&target.shard_id) + { + matching_peers.push(peer_id.clone()); + } + } + + if let Some(matching_peer) = matching_peers.iter().choose(&mut thread_rng()) + { + if self.state.send_message_to_peer( + &self.clock, + tcp::Tier::T2, + self.state.sign_message( + &self.clock, + RawRoutedMessage { + target: PeerIdOrHash::PeerId(matching_peer.clone()), + body: RoutedMessageBody::PartialEncodedChunkRequest( + request.clone(), + ), + }, + ), + ) { + success = true; + break; + } + } else { + tracing::debug!(target: "network", chunk_hash=?request.chunk_hash, "Failed to find any matching peer for chunk"); + } + } + } + + if success { + NetworkResponses::NoResponse + } else { + tracing::debug!(target: "network", chunk_hash=?request.chunk_hash, "Failed to find a route for chunk"); + NetworkResponses::RouteNotFound + } + } + NetworkRequests::PartialEncodedChunkResponse { route_back, response } => { + if self.state.send_message_to_peer( + &self.clock, + tcp::Tier::T2, + self.state.sign_message( + &self.clock, + RawRoutedMessage { + target: PeerIdOrHash::Hash(route_back), + body: RoutedMessageBody::PartialEncodedChunkResponse(response), + }, + ), + ) { + NetworkResponses::NoResponse + } else { + NetworkResponses::RouteNotFound + } + } + NetworkRequests::PartialEncodedChunkMessage { account_id, partial_encoded_chunk } => { + if self.state.send_message_to_account( + &self.clock, + &account_id, + RoutedMessageBody::VersionedPartialEncodedChunk(partial_encoded_chunk.into()), + ) { + NetworkResponses::NoResponse + } else { + NetworkResponses::RouteNotFound + } + } + NetworkRequests::PartialEncodedChunkForward { account_id, forward } => { + if self.state.send_message_to_account( + &self.clock, + &account_id, + RoutedMessageBody::PartialEncodedChunkForward(forward), + ) { + NetworkResponses::NoResponse + } else { + NetworkResponses::RouteNotFound + } + } + NetworkRequests::ForwardTx(account_id, tx) => { + if self.state.send_message_to_account( + &self.clock, + &account_id, + RoutedMessageBody::ForwardTx(tx), + ) { + NetworkResponses::NoResponse + } else { + NetworkResponses::RouteNotFound + } + } + NetworkRequests::TxStatus(account_id, signer_account_id, tx_hash) => { + if self.state.send_message_to_account( + &self.clock, + &account_id, + RoutedMessageBody::TxStatusRequest(signer_account_id, tx_hash), + ) { + NetworkResponses::NoResponse + } else { + NetworkResponses::RouteNotFound + } + } + NetworkRequests::Challenge(challenge) => { + // TODO(illia): smarter routing? + self.state.tier2.broadcast_message(Arc::new(PeerMessage::Challenge(challenge))); + NetworkResponses::NoResponse + } + NetworkRequests::ChunkStateWitness(chunk_validators, state_witness) => { + for chunk_validator in chunk_validators { + self.state.send_message_to_account( + &self.clock, + &chunk_validator, + RoutedMessageBody::ChunkStateWitness(state_witness.clone()), + ); + } + NetworkResponses::NoResponse + } + NetworkRequests::ChunkEndorsement(target, endorsement) => { + self.state.send_message_to_account( + &self.clock, + &target, + RoutedMessageBody::ChunkEndorsement(endorsement), + ); + NetworkResponses::NoResponse + } + } + } + + fn handle_peer_manager_message( + &mut self, + msg: PeerManagerMessageRequest, + ctx: &mut actix::Context, + ) -> PeerManagerMessageResponse { + match msg { + PeerManagerMessageRequest::NetworkRequests(msg) => { + PeerManagerMessageResponse::NetworkResponses( + self.handle_msg_network_requests(msg, ctx), + ) + } + PeerManagerMessageRequest::OutboundTcpConnect(stream) => { + let peer_addr = stream.peer_addr; + if let Err(err) = + PeerActor::spawn(self.clock.clone(), stream, None, self.state.clone()) + { + tracing::info!(target:"network", ?err, ?peer_addr, "spawn_outbound()"); + } + PeerManagerMessageResponse::OutboundTcpConnect + } + // TEST-ONLY + PeerManagerMessageRequest::FetchRoutingTable => { + PeerManagerMessageResponse::FetchRoutingTable(self.state.graph.routing_table.info()) + } + } + } +} + +impl actix::Handler> for PeerManagerActor { + type Result = (); + #[perf] + fn handle(&mut self, msg: WithSpanContext, ctx: &mut Self::Context) { + let (_span, SetChainInfo(info)) = handler_trace_span!(target: "network", msg); + let _timer = + metrics::PEER_MANAGER_MESSAGES_TIME.with_label_values(&["SetChainInfo"]).start_timer(); + // We call self.state.set_chain_info() + // synchronously, therefore, assuming actix in-order delivery, + // there will be no race condition between subsequent SetChainInfo + // calls. + if !self.state.set_chain_info(info) { + // We early exit in case the set of TIER1 account keys hasn't changed. + return; + } + + let state = self.state.clone(); + let clock = self.clock.clone(); + ctx.spawn(wrap_future( + async move { + // This node might have become a TIER1 node due to the change of the key set. + // If so we should recompute and readvertise the list of proxies. + // This is mostly important in case a node is its own proxy. In all other cases + // (when proxies are different nodes) the update of the key set happens asynchronously + // and this node won't be able to connect to proxies until it happens (and only the + // connected proxies are included in the advertisement). We run tier1_advertise_proxies + // periodically in the background anyway to cover those cases. + state.tier1_advertise_proxies(&clock).await; + } + .in_current_span(), + )); + } +} + +impl actix::Handler> for PeerManagerActor { + type Result = PeerManagerMessageResponse; + #[perf] + fn handle( + &mut self, + msg: WithSpanContext, + ctx: &mut Self::Context, + ) -> Self::Result { + let msg_type: &str = (&msg.msg).into(); + let (_span, msg) = handler_debug_span!(target: "network", msg, msg_type); + let _timer = + metrics::PEER_MANAGER_MESSAGES_TIME.with_label_values(&[(&msg).into()]).start_timer(); + self.handle_peer_manager_message(msg, ctx) + } +} + +impl actix::Handler for PeerManagerActor { + type Result = DebugStatus; + #[perf] + fn handle(&mut self, msg: GetDebugStatus, _ctx: &mut actix::Context) -> Self::Result { + match msg { + GetDebugStatus::PeerStore => { + let mut peer_states_view = self + .state + .peer_store + .load() + .iter() + .map(|(peer_id, known_peer_state)| KnownPeerStateView { + peer_id: peer_id.clone(), + status: format!("{:?}", known_peer_state.status), + addr: format!("{:?}", known_peer_state.peer_info.addr), + first_seen: known_peer_state.first_seen.unix_timestamp(), + last_seen: known_peer_state.last_seen.unix_timestamp(), + last_attempt: known_peer_state.last_outbound_attempt.clone().map( + |(attempt_time, attempt_result)| { + let foo = match attempt_result { + Ok(_) => String::from("Ok"), + Err(err) => format!("Error: {:?}", err.as_str()), + }; + (attempt_time.unix_timestamp(), foo) + }, + ), + }) + .collect::>(); + + peer_states_view.sort_by_key(|a| { + ( + -a.last_attempt.clone().map(|(attempt_time, _)| attempt_time).unwrap_or(0), + -a.last_seen, + ) + }); + DebugStatus::PeerStore(PeerStoreView { peer_states: peer_states_view }) + } + GetDebugStatus::Graph => DebugStatus::Graph(NetworkGraphView { + edges: self + .state + .graph + .load() + .edges + .values() + .map(|edge| { + let key = edge.key(); + EdgeView { peer0: key.0.clone(), peer1: key.1.clone(), nonce: edge.nonce() } + }) + .collect(), + next_hops: (*self.state.graph.routing_table.info().next_hops).clone(), + }), + GetDebugStatus::RecentOutboundConnections => { + DebugStatus::RecentOutboundConnections(RecentOutboundConnectionsView { + recent_outbound_connections: self + .state + .connection_store + .get_recent_outbound_connections() + .iter() + .map(|c| ConnectionInfoView { + peer_id: c.peer_info.id.clone(), + addr: format!("{:?}", c.peer_info.addr), + time_established: c.time_established.unix_timestamp(), + time_connected_until: c.time_connected_until.unix_timestamp(), + }) + .collect::>(), + }) + } + GetDebugStatus::Routes => DebugStatus::Routes(self.state.graph_v2.get_debug_view()), + GetDebugStatus::SnapshotHosts => DebugStatus::SnapshotHosts(SnapshotHostsView { + hosts: self + .state + .snapshot_hosts + .get_hosts() + .iter() + .map(|h| SnapshotHostInfoView { + peer_id: h.peer_id.clone(), + sync_hash: h.sync_hash, + epoch_height: h.epoch_height, + shards: h.shards.clone(), + }) + .collect::>(), + }), + } + } +} diff --git a/chain/network/src/peer_manager/peer_store/mod.rs b/chain/network/src/peer_manager/peer_store/mod.rs new file mode 100644 index 000000000..4d6d7415f --- /dev/null +++ b/chain/network/src/peer_manager/peer_store/mod.rs @@ -0,0 +1,518 @@ +use crate::blacklist; +use crate::network_protocol::PeerInfo; +use crate::types::{KnownPeerState, KnownPeerStatus, ReasonForBan}; +use anyhow::bail; +use im::hashmap::Entry; +use im::{HashMap, HashSet}; +use lru::LruCache; +use unc_async::time; +use unc_primitives::network::PeerId; +use parking_lot::Mutex; +use rand::seq::IteratorRandom; +use rand::thread_rng; +use std::net::SocketAddr; +use std::ops::Not; + +#[cfg(test)] +mod testonly; +#[cfg(test)] +mod tests; + +/// The PeerStore is an in-memory cache of known peer states. It is used to: +/// - Store information about known peers in the network. Peers may be discovered +/// by connecting to them directly or by learning about them from other peers. +/// - Respond to requests from other peers for known peers (see PeerStore::healthy_peers). +/// - Select peers to which we may try to connect directly (see PeerStore::unconnected_peer). +/// +/// Contents of the PeerStore are not persisted to the database. Upon starting a node, +/// the PeerStore is initialized from the boot nodes in its config. + +/// Level of trust we have about a new (PeerId, Addr) pair. +#[derive(Eq, PartialEq, Debug, Clone, Copy)] +enum TrustLevel { + /// We learn about it from other peers. + Indirect, + /// Responding node at addr claims to possess PeerId. + Direct, + /// Responding peer proved to have SecretKey associated with this PeerID. + Signed, +} + +#[derive(Debug, Clone)] +struct VerifiedPeer { + peer_id: PeerId, + trust_level: TrustLevel, +} + +impl VerifiedPeer { + fn signed(peer_id: PeerId) -> Self { + Self { peer_id, trust_level: TrustLevel::Signed } + } +} + +#[derive(Clone)] +pub struct Config { + /// A list of nodes to connect to on the first run of the uncd server. + /// Once it connects to some of them, the server will learn about other + /// nodes in the network and will try to connect to them as well. + /// + /// The recommended boot nodes are distributed together with the config.json + /// file, but you can modify the boot_nodes field to contain any nodes that + /// you trust. + pub boot_nodes: Vec, + /// Nodes will not accept or try to establish connection to such peers. + pub blacklist: blacklist::Blacklist, + /// If true - connect only to the bootnodes. + pub connect_only_to_boot_nodes: bool, + /// The maximum number of peers to store. If capacity is exceeded, the peers + /// with the earliest last_seen value will be evicted. + pub peer_states_cache_size: u32, + /// Remove expired peers. + pub peer_expiration_duration: time::Duration, + /// Duration of the ban for misbehaving peers. + pub ban_window: time::Duration, +} + +/// Known peers store, maintaining cache of known peers +struct Inner { + config: Config, + boot_nodes: HashSet, + // LruCache of the known peer states. Be sure to use peek/peek_mut to access information. + // Using the get/put methods modifies the cache order. + peer_states: LruCache, + // This is a reverse index, from physical address to peer_id + // It can happens that some peers don't have known address, so + // they will not be present in this list, otherwise they will be present. + addr_peers: HashMap, +} + +impl Inner { + /// Adds a peer which proved to have secret key associated with the ID. + /// + /// The host have sent us a message signed with a secret key corresponding + /// to the peer ID thus we can be sure that they control the secret key. + /// + /// See also [`Self::add_indirect_peers`] and [`Self::add_direct_peer`]. + fn add_signed_peer(&mut self, clock: &time::Clock, peer_info: PeerInfo) { + self.add_peer(clock, peer_info, TrustLevel::Signed) + } + + /// Adds a peer into the store with given trust level. + fn add_peer(&mut self, clock: &time::Clock, peer_info: PeerInfo, trust_level: TrustLevel) { + if let Some(peer_addr) = peer_info.addr { + match trust_level { + TrustLevel::Signed => { + self.update_peer_info(clock, peer_info, peer_addr, TrustLevel::Signed); + } + TrustLevel::Direct => { + // If this peer already exists with a signed connection ignore this update. + // Warning: This is a problem for nodes that changes its address without changing peer_id. + // It is recommended to change peer_id if address is changed. + let trust_level = (|| { + let state = self.peer_states.get(&peer_info.id)?; + let addr = state.peer_info.addr?; + let verified_peer = self.addr_peers.get(&addr)?; + Some(verified_peer.trust_level) + })(); + if trust_level == Some(TrustLevel::Signed) { + return; + } + self.update_peer_info(clock, peer_info, peer_addr, TrustLevel::Direct); + } + TrustLevel::Indirect => { + // We should only update an Indirect connection if we don't know anything about the peer + // or about the address. + if !self.peer_states.contains(&peer_info.id) + && !self.addr_peers.contains_key(&peer_addr) + { + self.update_peer_info(clock, peer_info, peer_addr, TrustLevel::Indirect); + } + } + } + } else { + // If doesn't have the address attached it is not verified and we add it + // only if it is unknown to us. + if !self.peer_states.contains(&peer_info.id) { + if let Some((_, popped_peer_state)) = self + .peer_states + .push(peer_info.id.clone(), KnownPeerState::new(peer_info, clock.now_utc())) + { + // If a peer was evicted from peer_states due to the bounded cache size + // and it has an address, remove the corresponding entry from addr_peers + if let Some(popped_peer_addr) = popped_peer_state.peer_info.addr { + self.addr_peers.remove(&popped_peer_addr); + } + } + } + } + } + + fn peer_unban(&mut self, peer_id: &PeerId) -> anyhow::Result<()> { + if let Some(peer_state) = self.peer_states.get_mut(peer_id) { + peer_state.status = KnownPeerStatus::NotConnected; + } else { + bail!("Peer {} is missing in the peer store", peer_id); + } + Ok(()) + } + + /// Deletes peers from the internal cache + fn delete_peers(&mut self, peer_ids: &[PeerId]) { + for peer_id in peer_ids { + if let Some(peer_state) = self.peer_states.pop(peer_id) { + if let Some(addr) = peer_state.peer_info.addr { + self.addr_peers.remove(&addr); + } + } + } + } + + /// Find a random subset of peers based on filter. + fn find_peers(&self, filter: F, count: usize) -> Vec + where + F: FnMut(&&KnownPeerState) -> bool, + { + (self.peer_states.iter().map(|(_, v)| v)) + .filter(filter) + .choose_multiple(&mut thread_rng(), count) + .into_iter() + .map(|kps| kps.peer_info.clone()) + .collect() + } + + /// Create new pair between peer_info.id and peer_addr removing + /// old pairs if necessary. + fn update_peer_info( + &mut self, + clock: &time::Clock, + peer_info: PeerInfo, + peer_addr: SocketAddr, + trust_level: TrustLevel, + ) { + // If there is a peer associated with current address remove the address from it. + if let Some(verified_peer) = self.addr_peers.remove(&peer_addr) { + self.peer_states.peek_mut(&verified_peer.peer_id).unwrap().peer_info.addr = None; + } + + // If this peer already has an address, remove that pair from the index. + if let Some(peer_state) = self.peer_states.get_mut(&peer_info.id) { + if let Some(cur_addr) = peer_state.peer_info.addr.take() { + self.addr_peers.remove(&cur_addr); + } + } + + // Add new address + self.addr_peers + .insert(peer_addr, VerifiedPeer { peer_id: peer_info.id.clone(), trust_level }); + + // Update or insert peer_id addr + if let Some(peer_state) = self.peer_states.peek_mut(&peer_info.id) { + peer_state.peer_info.addr = Some(peer_addr); + } else { + let now = clock.now_utc(); + if let Some((_, popped_peer_state)) = self + .peer_states + .push(peer_info.id.clone(), KnownPeerState::new(peer_info.clone(), now)) + { + // If a peer was evicted from peer_states due to the bounded cache size + // and it has an address, remove the corresponding entry from addr_peers + if let Some(popped_peer_addr) = popped_peer_state.peer_info.addr { + self.addr_peers.remove(&popped_peer_addr); + } + } + } + } + + /// Removes peers that are not responding for expiration period. + fn remove_expired(&mut self, now: time::Utc) { + let mut to_remove = vec![]; + for (peer_id, peer_status) in self.peer_states.iter() { + if peer_status.status != KnownPeerStatus::Connected + && now > peer_status.last_seen + self.config.peer_expiration_duration + { + tracing::debug!(target: "network", "Removing peer: last seen {:?} ago", now-peer_status.last_seen); + to_remove.push(peer_id.clone()); + } + } + self.delete_peers(&to_remove); + } + + fn unban(&mut self, now: time::Utc) { + let mut to_unban = vec![]; + for (peer_id, peer_state) in &self.peer_states { + if let KnownPeerStatus::Banned(_, ban_time) = peer_state.status { + if now < ban_time + self.config.ban_window { + continue; + } + tracing::info!(target: "network", unbanned = ?peer_id, ?ban_time, "unbanning a peer"); + to_unban.push(peer_id.clone()); + } + } + for peer_id in &to_unban { + if let Err(err) = self.peer_unban(&peer_id) { + tracing::error!(target: "network", ?peer_id, ?err, "Failed to unban a peer"); + } + } + } + + /// Update the 'last_seen' time for all the peers that we're currently connected to. + fn update_last_seen(&mut self, now: time::Utc) { + let mut connected_peer_ids = vec![]; + for (peer_id, peer_state) in self.peer_states.iter_mut() { + if peer_state.status == KnownPeerStatus::Connected { + connected_peer_ids.push(peer_id.clone()); + peer_state.last_seen = now; + } + } + + // Access the connected peers to move them to the head of the LruCache + for peer_id in connected_peer_ids { + self.peer_states.get(&peer_id); + } + } + + /// Cleans up the state of the PeerStore, due to passing time. + /// * it unbans a peer if config.ban_window has passed + /// * it updates KnownPeerStatus.last_seen of the connected peers + /// * it removes peers which were not seen for config.peer_expiration_duration + /// This function should be called periodically. + pub fn update(&mut self, clock: &time::Clock) { + let now = clock.now_utc(); + self.unban(now); + self.update_last_seen(now); + self.remove_expired(now); + } +} + +pub(crate) struct PeerStore(Mutex); + +impl PeerStore { + pub fn new(clock: &time::Clock, config: Config) -> anyhow::Result { + let boot_nodes: HashSet<_> = config.boot_nodes.iter().map(|p| p.id.clone()).collect(); + // A mapping from `PeerId` to `KnownPeerState`. + let mut peerid_2_state = LruCache::new(config.peer_states_cache_size as usize); + // Stores mapping from `SocketAddr` to `VerifiedPeer`, which contains `PeerId`. + // Only one peer can exist with given `PeerId` or `SocketAddr`. + // In case of collision, we will choose the first one. + let mut addr_2_peer = HashMap::default(); + + if boot_nodes.len() > (config.peer_states_cache_size as usize) { + tracing::error!( + "Num boot nodes is {} but the size of the peer store is limited to {}.", + boot_nodes.len(), + config.peer_states_cache_size + ); + } + + let now = clock.now_utc(); + for peer_info in &config.boot_nodes { + if peerid_2_state.contains(&peer_info.id) { + tracing::error!(id = ?peer_info.id, "There is a duplicated peer in boot_nodes"); + continue; + } + let peer_addr = match peer_info.addr { + None => continue, + Some(addr) => addr, + }; + let entry = match addr_2_peer.entry(peer_addr) { + Entry::Occupied(entry) => { + // There is already a different peer_id with this address. + anyhow::bail!("Two boot nodes have the same address {:?}", entry.key()); + } + Entry::Vacant(entry) => entry, + }; + entry.insert(VerifiedPeer::signed(peer_info.id.clone())); + + if let Some((_, popped_peer_state)) = peerid_2_state + .push(peer_info.id.clone(), KnownPeerState::new(peer_info.clone(), now)) + { + // If a peer was evicted from peer_states due to the bounded cache size + // and it has an address, remove the corresponding entry from addr_peers + if let Some(popped_peer_addr) = popped_peer_state.peer_info.addr { + addr_2_peer.remove(&popped_peer_addr); + } + } + } + + let inner = + Inner { config, boot_nodes, peer_states: peerid_2_state, addr_peers: addr_2_peer }; + Ok(PeerStore(Mutex::new(inner))) + } + + pub fn is_blacklisted(&self, addr: &SocketAddr) -> bool { + self.0.lock().config.blacklist.contains(*addr) + } + + pub fn len(&self) -> usize { + self.0.lock().peer_states.len() + } + + pub fn is_banned(&self, peer_id: &PeerId) -> bool { + self.0.lock().peer_states.get(peer_id).map_or(false, |s| s.status.is_banned()) + } + + pub fn count_banned(&self) -> usize { + self.0.lock().peer_states.iter().filter(|(_, st)| st.status.is_banned()).count() + } + + pub fn update(&self, clock: &time::Clock) { + self.0.lock().update(clock) + } + + #[allow(dead_code)] + /// Returns the state of the current peer in memory. + pub fn get_peer_state(&self, peer_id: &PeerId) -> Option { + self.0.lock().peer_states.get(peer_id).cloned() + } + + pub fn peer_connected(&self, clock: &time::Clock, peer_info: &PeerInfo) { + let mut inner = self.0.lock(); + inner.add_signed_peer(clock, peer_info.clone()); + let entry = inner.peer_states.get_mut(&peer_info.id).unwrap(); + entry.last_seen = clock.now_utc(); + entry.status = KnownPeerStatus::Connected; + } + + pub fn peer_disconnected(&self, clock: &time::Clock, peer_id: &PeerId) -> anyhow::Result<()> { + let mut inner = self.0.lock(); + if let Some(peer_state) = inner.peer_states.get_mut(peer_id) { + peer_state.last_seen = clock.now_utc(); + peer_state.status = KnownPeerStatus::NotConnected; + } else { + bail!("Peer {} is missing in the peer store", peer_id); + } + Ok(()) + } + + /// Records the last attempt to connect to peer. + pub fn peer_connection_attempt( + &self, + clock: &time::Clock, + peer_id: &PeerId, + result: Result<(), anyhow::Error>, + ) -> anyhow::Result<()> { + let mut inner = self.0.lock(); + + if let Some(peer_state) = inner.peer_states.get_mut(peer_id) { + if result.is_err() { + // Marks the peer status as Unknown (as we failed to connect to it). + peer_state.status = KnownPeerStatus::Unknown; + } + peer_state.last_outbound_attempt = + Some((clock.now_utc(), result.map_err(|err| err.to_string()))); + peer_state.last_seen = clock.now_utc(); + } else { + bail!("Peer {} is missing in the peer store", peer_id); + } + + Ok(()) + } + + pub fn peer_ban( + &self, + clock: &time::Clock, + peer_id: &PeerId, + ban_reason: ReasonForBan, + ) -> anyhow::Result<()> { + tracing::warn!(target: "network", "Banning peer {} for {:?}", peer_id, ban_reason); + let mut inner = self.0.lock(); + if let Some(peer_state) = inner.peer_states.get_mut(peer_id) { + let now = clock.now_utc(); + peer_state.last_seen = now; + peer_state.status = KnownPeerStatus::Banned(ban_reason, now); + } else { + bail!("Peer {} is missing in the peer store", peer_id); + } + Ok(()) + } + + /// Return unconnected or peers with unknown status that we can try to connect to. + /// Peers with unknown addresses are filtered out. + pub fn unconnected_peer( + &self, + ignore_fn: impl Fn(&KnownPeerState) -> bool, + prefer_previously_connected_peer: bool, + ) -> Option { + let inner = self.0.lock(); + if prefer_previously_connected_peer { + let preferred_peer = inner.find_peers( + |p| { + (p.status == KnownPeerStatus::NotConnected) + && !ignore_fn(p) + && p.peer_info.addr.is_some() + // if we're connecting only to the boot nodes - filter out the nodes that are not bootnodes. + && (!inner.config.connect_only_to_boot_nodes || inner.boot_nodes.contains(&p.peer_info.id)) + }, + 1, + ) + .get(0) + .cloned(); + // If we found a preferred peer - return it. + if preferred_peer.is_some() { + return preferred_peer; + }; + // otherwise, pick a peer from the wider pool below. + } + inner.find_peers( + |p| { + (p.status == KnownPeerStatus::NotConnected || p.status == KnownPeerStatus::Unknown) + && !ignore_fn(p) + && p.peer_info.addr.is_some() + // If we're connecting only to the boot nodes - filter out the nodes that are not boot nodes. + && (!inner.config.connect_only_to_boot_nodes || inner.boot_nodes.contains(&p.peer_info.id)) + }, + 1, + ) + .get(0) + .cloned() + } + + /// Return healthy known peers up to given amount. + pub fn healthy_peers(&self, max_count: usize) -> Vec { + self.0 + .lock() + .find_peers(|p| matches!(p.status, KnownPeerStatus::Banned(_, _)).not(), max_count) + } + + /// Adds peers we’ve learned about from other peers. + /// + /// Identities of the nodes hasn’t been verified in any way. We don’t even + /// know if there is anything running at given addresses and even if there + /// are nodes there we haven’t received signatures of their peer ID. + /// + /// See also [`Self::add_direct_peer`] and [`Self::add_signed_peer`]. + pub fn add_indirect_peers(&self, clock: &time::Clock, peers: impl Iterator) { + let mut inner = self.0.lock(); + let mut total: usize = 0; + let mut blacklisted: usize = 0; + for peer_info in peers { + total += 1; + let is_blacklisted = + peer_info.addr.map_or(false, |addr| inner.config.blacklist.contains(addr)); + if is_blacklisted { + blacklisted += 1; + } else { + inner.add_peer(clock, peer_info, TrustLevel::Indirect); + } + } + if blacklisted != 0 { + tracing::info!(target: "network", "Ignored {} blacklisted peers out of {} indirect peer(s)", + blacklisted, total); + } + } + + /// Adds a peer we’ve connected to but haven’t verified ID yet. + /// + /// We've connected to the host (thus know that the address is correct) and + /// they claim they control given peer ID but we haven’t received signature + /// confirming that identity yet. + /// + /// See also [`Self::add_indirect_peers`] and [`Self::add_signed_peer`]. + pub fn add_direct_peer(&self, clock: &time::Clock, peer_info: PeerInfo) { + self.0.lock().add_peer(clock, peer_info, TrustLevel::Direct) + } + + pub fn load(&self) -> HashMap { + self.0.lock().peer_states.iter().map(|(k, v)| (k.clone(), v.clone())).collect() + } +} diff --git a/chain/network/src/peer_manager/peer_store/testonly.rs b/chain/network/src/peer_manager/peer_store/testonly.rs new file mode 100644 index 000000000..a346a6ec9 --- /dev/null +++ b/chain/network/src/peer_manager/peer_store/testonly.rs @@ -0,0 +1,7 @@ +use crate::types::KnownPeerState; + +impl super::PeerStore { + pub fn dump(&self) -> Vec { + self.0.lock().peer_states.iter().map(|(_, v)| v.clone()).collect() + } +} diff --git a/chain/network/src/peer_manager/peer_store/tests.rs b/chain/network/src/peer_manager/peer_store/tests.rs new file mode 100644 index 000000000..6a700a2f5 --- /dev/null +++ b/chain/network/src/peer_manager/peer_store/tests.rs @@ -0,0 +1,487 @@ +use super::*; +use crate::blacklist::Blacklist; +use unc_async::time; +use unc_crypto::{KeyType, SecretKey}; +use std::collections::HashSet; +use std::net::{Ipv4Addr, SocketAddrV4}; + +fn get_peer_id(seed: String) -> PeerId { + PeerId::new(SecretKey::from_seed(KeyType::ED25519, seed.as_str()).public_key()) +} + +fn get_addr(port: u16) -> SocketAddr { + SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port).into() +} + +fn get_peer_info(peer_id: PeerId, addr: Option) -> PeerInfo { + PeerInfo { id: peer_id, addr, account_id: None } +} + +fn gen_peer_info(port: u16) -> PeerInfo { + PeerInfo { + id: PeerId::new(SecretKey::from_random(KeyType::ED25519).public_key()), + addr: Some(get_addr(port)), + account_id: None, + } +} + +fn make_config( + boot_nodes: &[PeerInfo], + blacklist: blacklist::Blacklist, + connect_only_to_boot_nodes: bool, +) -> Config { + Config { + boot_nodes: boot_nodes.iter().cloned().collect(), + blacklist, + peer_states_cache_size: 1000, + connect_only_to_boot_nodes, + ban_window: time::Duration::seconds(1), + peer_expiration_duration: time::Duration::days(1000), + } +} + +#[test] +fn ban_store() { + let clock = time::FakeClock::default(); + let peer_info_a = gen_peer_info(0); + let peer_info_to_ban = gen_peer_info(1); + let boot_nodes = vec![peer_info_a, peer_info_to_ban.clone()]; + + let peer_store = + PeerStore::new(&clock.clock(), make_config(&boot_nodes, Blacklist::default(), false)) + .unwrap(); + assert_eq!(peer_store.healthy_peers(3).len(), 2); + peer_store.peer_ban(&clock.clock(), &peer_info_to_ban.id, ReasonForBan::Abusive).unwrap(); + assert_eq!(peer_store.healthy_peers(3).len(), 1); +} + +#[test] +fn test_unconnected_peer() { + let clock = time::FakeClock::default(); + let peer_info_a = gen_peer_info(0); + let peer_info_to_ban = gen_peer_info(1); + let boot_nodes = vec![peer_info_a, peer_info_to_ban]; + + let peer_store = + PeerStore::new(&clock.clock(), make_config(&boot_nodes, Blacklist::default(), false)) + .unwrap(); + + assert!(peer_store.unconnected_peer(|_| false, false).is_some()); + assert!(peer_store.unconnected_peer(|_| true, false).is_none()); +} + +#[test] +fn test_unknown_vs_not_connected() { + use KnownPeerStatus::{Connected, NotConnected, Unknown}; + let clock = time::FakeClock::default(); + let peer_info_a = gen_peer_info(0); + let peer_info_b = gen_peer_info(1); + let peer_info_boot_node = gen_peer_info(2); + let boot_nodes = vec![peer_info_boot_node.clone()]; + + let nodes = [&peer_info_a, &peer_info_b, &peer_info_boot_node]; + + let get_in_memory_status = |peer_store: &PeerStore| { + nodes.map(|peer| peer_store.get_peer_state(&peer.id).map(|known_state| known_state.status)) + }; + + let peer_store = + PeerStore::new(&clock.clock(), make_config(&boot_nodes, Blacklist::default(), false)) + .unwrap(); + + // Check the status of the in-memory store. + // Boot node should be marked as not-connected, as we've verified it. + // TODO(mm-near) - the boot node should have been added as 'NotConnected' and not Unknown. + assert_eq!(get_in_memory_status(&peer_store), [None, None, Some(Unknown)]); + + // Add the remaining peers. + peer_store.add_direct_peer(&clock.clock(), peer_info_a.clone()); + peer_store.add_direct_peer(&clock.clock(), peer_info_b.clone()); + + assert_eq!(get_in_memory_status(&peer_store), [Some(Unknown), Some(Unknown), Some(Unknown)]); + + // Connect to both nodes + for peer_info in [peer_info_a.clone(), peer_info_b.clone()] { + peer_store.peer_connected(&clock.clock(), &peer_info); + } + assert_eq!( + get_in_memory_status(&peer_store), + [Some(Connected), Some(Connected), Some(Unknown)] + ); + + // Disconnect from 'b' + peer_store.peer_disconnected(&clock.clock(), &peer_info_b.id).unwrap(); + + assert_eq!( + get_in_memory_status(&peer_store), + [Some(Connected), Some(NotConnected), Some(Unknown)] + ); + + // if we prefer 'previously connected' peers - we should keep picking 'b'. + assert_eq!( + (0..10) + .map(|_| peer_store.unconnected_peer(|_| false, true).unwrap().id) + .collect::>(), + [peer_info_b.id.clone()].into_iter().collect::>() + ); + + // if we don't care, we should pick either 'b' or 'boot'. + assert_eq!( + (0..100) + .map(|_| peer_store.unconnected_peer(|_| false, false).unwrap().id) + .collect::>(), + [peer_info_b.id.clone(), peer_info_boot_node.id.clone()] + .into_iter() + .collect::>() + ); + + // And fail when trying to reconnect to b. + peer_store + .peer_connection_attempt( + &clock.clock(), + &peer_info_b.id, + Err(anyhow::anyhow!("b failed to connect error")), + ) + .unwrap(); + + // It should move 'back' into Unknown state. + assert_eq!(get_in_memory_status(&peer_store), [Some(Connected), Some(Unknown), Some(Unknown)]); +} + +#[test] +fn test_unconnected_peer_only_boot_nodes() { + let clock = time::FakeClock::default(); + let peer_info_a = gen_peer_info(0); + let peer_in_store = gen_peer_info(3); + let boot_nodes = vec![peer_info_a.clone()]; + + // 1 boot node (peer_info_a) that we're already connected to. + // 1 non-boot (peer_in_store) node peer that is in the store. + // we should connect to peer_in_store + { + let peer_store = + PeerStore::new(&clock.clock(), make_config(&boot_nodes, Blacklist::default(), false)) + .unwrap(); + peer_store.add_direct_peer(&clock.clock(), peer_in_store.clone()); + peer_store.peer_connected(&clock.clock(), &peer_info_a); + assert_eq!(peer_store.unconnected_peer(|_| false, false), Some(peer_in_store.clone())); + } + + // 1 boot node (peer_info_a) that we're already connected to. + // 1 non-boot (peer_in_store) node peer that is in the store. + // connect to only boot nodes is enabled - we should not find any peer to connect to. + { + let peer_store = + PeerStore::new(&clock.clock(), make_config(&boot_nodes, Default::default(), true)) + .unwrap(); + peer_store.add_direct_peer(&clock.clock(), peer_in_store); + peer_store.peer_connected(&clock.clock(), &peer_info_a); + assert_eq!(peer_store.unconnected_peer(|_| false, false), None); + } + + // 1 boot node (peer_info_a) is in the store. + // we should connect to it - no matter what the setting is. + for connect_to_boot_nodes in [true, false] { + let peer_store = PeerStore::new( + &clock.clock(), + make_config(&boot_nodes, Default::default(), connect_to_boot_nodes), + ) + .unwrap(); + peer_store.add_direct_peer(&clock.clock(), peer_info_a.clone()); + assert_eq!(peer_store.unconnected_peer(|_| false, false), Some(peer_info_a.clone())); + } +} + +fn check_exist( + peer_store: &PeerStore, + peer_id: &PeerId, + addr_level: Option<(SocketAddr, TrustLevel)>, +) -> bool { + let inner = peer_store.0.lock(); + if let Some(peer_info) = inner.peer_states.peek(peer_id) { + let peer_info = &peer_info.peer_info; + if let Some((addr, level)) = addr_level { + peer_info.addr.map_or(false, |cur_addr| cur_addr == addr) + && inner + .addr_peers + .get(&addr) + .map_or(false, |verified| verified.trust_level == level) + } else { + peer_info.addr.is_none() + } + } else { + false + } +} + +fn check_integrity(peer_store: &PeerStore) -> bool { + let inner = peer_store.0.lock(); + inner.peer_states.iter().all(|(k, v)| { + if let Some(addr) = v.peer_info.addr { + if inner.addr_peers.get(&addr).map_or(true, |value| value.peer_id != *k) { + return false; + } + } + true + }) && inner.addr_peers.clone().iter().all(|(k, v)| { + !inner + .peer_states + .peek(&v.peer_id) + .map_or(true, |value| value.peer_info.addr.map_or(true, |addr| addr != *k)) + }) +} + +/// If we know there is a peer_id A at address #A, and after some time +/// we learn that there is a new peer B at address #A, we discard address of A +#[test] +fn handle_peer_id_change() { + let clock = time::FakeClock::default(); + let peer_store = + PeerStore::new(&clock.clock(), make_config(&[], Default::default(), false)).unwrap(); + + let peers_id = (0..2).map(|ix| get_peer_id(format!("node{}", ix))).collect::>(); + let addr = get_addr(0); + + let peer_aa = get_peer_info(peers_id[0].clone(), Some(addr)); + peer_store.peer_connected(&clock.clock(), &peer_aa); + assert!(check_exist(&peer_store, &peers_id[0], Some((addr, TrustLevel::Signed)))); + + let peer_ba = get_peer_info(peers_id[1].clone(), Some(addr)); + peer_store.add_direct_peer(&clock.clock(), peer_ba); + + assert!(check_exist(&peer_store, &peers_id[0], None)); + assert!(check_exist(&peer_store, &peers_id[1], Some((addr, TrustLevel::Direct)))); + assert!(check_integrity(&peer_store)); +} + +/// If we know there is a peer_id A at address #A, and then we learn about +/// the same peer_id A at address #B, if that connection wasn't signed it is not updated, +/// to avoid malicious actor making us forget about known peers. +#[test] +fn dont_handle_address_change() { + let clock = time::FakeClock::default(); + let peer_store = + PeerStore::new(&clock.clock(), make_config(&[], Default::default(), false)).unwrap(); + + let peers_id = (0..1).map(|ix| get_peer_id(format!("node{}", ix))).collect::>(); + let addrs = (0..2).map(get_addr).collect::>(); + + let peer_aa = get_peer_info(peers_id[0].clone(), Some(addrs[0])); + peer_store.peer_connected(&clock.clock(), &peer_aa); + assert!(check_exist(&peer_store, &peers_id[0], Some((addrs[0], TrustLevel::Signed)))); + + let peer_ba = get_peer_info(peers_id[0].clone(), Some(addrs[1])); + peer_store.add_direct_peer(&clock.clock(), peer_ba); + assert!(check_exist(&peer_store, &peers_id[0], Some((addrs[0], TrustLevel::Signed)))); + assert!(check_integrity(&peer_store)); +} + +#[test] +fn check_add_peers_overriding() { + let clock = time::FakeClock::default(); + let peer_store = + PeerStore::new(&clock.clock(), make_config(&[], Default::default(), false)).unwrap(); + + // Five peers: A, B, C, D, X, T + let peers_id = (0..6).map(|ix| get_peer_id(format!("node{}", ix))).collect::>(); + // Five addresses: #A, #B, #C, #D, #X, #T + let addrs = (0..6).map(get_addr).collect::>(); + + // Create signed connection A - #A + let peer_00 = get_peer_info(peers_id[0].clone(), Some(addrs[0])); + peer_store.peer_connected(&clock.clock(), &peer_00); + assert!(check_exist(&peer_store, &peers_id[0], Some((addrs[0], TrustLevel::Signed)))); + assert!(check_integrity(&peer_store)); + + // Create direct connection B - #B + let peer_11 = get_peer_info(peers_id[1].clone(), Some(addrs[1])); + peer_store.add_direct_peer(&clock.clock(), peer_11.clone()); + assert!(check_exist(&peer_store, &peers_id[1], Some((addrs[1], TrustLevel::Direct)))); + assert!(check_integrity(&peer_store)); + + // Create signed connection B - #B + peer_store.peer_connected(&clock.clock(), &peer_11); + assert!(check_exist(&peer_store, &peers_id[1], Some((addrs[1], TrustLevel::Signed)))); + assert!(check_integrity(&peer_store)); + + // Create indirect connection C - #C + let peer_22 = get_peer_info(peers_id[2].clone(), Some(addrs[2])); + peer_store.add_indirect_peers(&clock.clock(), [peer_22.clone()].into_iter()); + assert!(check_exist(&peer_store, &peers_id[2], Some((addrs[2], TrustLevel::Indirect)))); + assert!(check_integrity(&peer_store)); + + // Create signed connection C - #C + peer_store.peer_connected(&clock.clock(), &peer_22); + assert!(check_exist(&peer_store, &peers_id[2], Some((addrs[2], TrustLevel::Signed)))); + assert!(check_integrity(&peer_store)); + + // Create signed connection C - #B + // This overrides C - #C and B - #B + let peer_21 = get_peer_info(peers_id[2].clone(), Some(addrs[1])); + peer_store.peer_connected(&clock.clock(), &peer_21); + assert!(check_exist(&peer_store, &peers_id[1], None)); + assert!(check_exist(&peer_store, &peers_id[2], Some((addrs[1], TrustLevel::Signed)))); + assert!(check_integrity(&peer_store)); + + // Create indirect connection D - #D + let peer_33 = get_peer_info(peers_id[3].clone(), Some(addrs[3])); + peer_store.add_indirect_peers(&clock.clock(), [peer_33].into_iter()); + assert!(check_exist(&peer_store, &peers_id[3], Some((addrs[3], TrustLevel::Indirect)))); + assert!(check_integrity(&peer_store)); + + // Try to create indirect connection A - #X but fails since A - #A exists + let peer_04 = get_peer_info(peers_id[0].clone(), Some(addrs[4])); + peer_store.add_indirect_peers(&clock.clock(), [peer_04].into_iter()); + assert!(check_exist(&peer_store, &peers_id[0], Some((addrs[0], TrustLevel::Signed)))); + assert!(check_integrity(&peer_store)); + + // Try to create indirect connection X - #D but fails since D - #D exists + let peer_43 = get_peer_info(peers_id[4].clone(), Some(addrs[3])); + peer_store.add_indirect_peers(&clock.clock(), [peer_43.clone()].into_iter()); + assert!(check_exist(&peer_store, &peers_id[3], Some((addrs[3], TrustLevel::Indirect)))); + assert!(check_integrity(&peer_store)); + + // Create Direct connection X - #D and succeed removing connection D - #D + peer_store.add_direct_peer(&clock.clock(), peer_43); + assert!(check_exist(&peer_store, &peers_id[4], Some((addrs[3], TrustLevel::Direct)))); + // D should still exist, but without any addr + assert!(check_exist(&peer_store, &peers_id[3], None)); + assert!(check_integrity(&peer_store)); + + // Try to create indirect connection A - #T but fails since A - #A (signed) exists + let peer_05 = get_peer_info(peers_id[0].clone(), Some(addrs[5])); + peer_store.add_direct_peer(&clock.clock(), peer_05); + assert!(check_exist(&peer_store, &peers_id[0], Some((addrs[0], TrustLevel::Signed)))); + assert!(check_integrity(&peer_store)); +} + +#[test] +fn check_ignore_blacklisted_peers() { + let clock = time::FakeClock::default(); + + #[track_caller] + fn assert_peers(peer_store: &PeerStore, expected: &[&PeerId]) { + let inner = peer_store.0.lock(); + let expected: HashSet<&PeerId> = HashSet::from_iter(expected.iter().cloned()); + let got = HashSet::from_iter(inner.peer_states.iter().map(|(k, _)| k)); + assert_eq!(expected, got); + } + + let ids = (0..3).map(|ix| get_peer_id(format!("node{}", ix))).collect::>(); + + let blacklist: blacklist::Blacklist = + ["127.0.0.1:1"].iter().map(|e| e.parse().unwrap()).collect(); + + let peer_store = PeerStore::new(&clock.clock(), make_config(&[], blacklist, false)).unwrap(); + + peer_store.add_indirect_peers( + &clock.clock(), + [ + get_peer_info(ids[0].clone(), None), + get_peer_info(ids[1].clone(), Some(get_addr(1))), + get_peer_info(ids[2].clone(), Some(get_addr(2))), + ] + .into_iter(), + ); + + // Peer 127.0.0.1:1 is ignored and never added. + assert_peers(&peer_store, &[&ids[0], &ids[2]]); +} + +#[track_caller] +fn assert_peers_in_cache( + peer_store: &PeerStore, + expected_peers: &[PeerId], + expected_addresses: &[SocketAddr], +) { + let inner = peer_store.0.lock(); + let expected_peers: HashSet<&PeerId> = HashSet::from_iter(expected_peers); + let cached_peers = HashSet::from_iter(inner.peer_states.iter().map(|(k, _)| k)); + assert_eq!(expected_peers, cached_peers); + + let expected_addresses: HashSet<&SocketAddr> = HashSet::from_iter(expected_addresses); + let cached_addresses = HashSet::from_iter(inner.addr_peers.keys()); + assert_eq!(expected_addresses, cached_addresses); +} + +#[test] +fn test_delete_peers() { + let clock = time::FakeClock::default(); + let (peer_ids, peer_infos): (Vec<_>, Vec<_>) = (0..3) + .map(|i| { + let id = get_peer_id(format!("node{}", i)); + let info = get_peer_info(id.clone(), Some(get_addr(i))); + (id, info) + }) + .unzip(); + let peer_addresses = peer_infos.iter().map(|info| info.addr.unwrap()).collect::>(); + + let peer_store = + PeerStore::new(&clock.clock(), make_config(&[], Default::default(), false)).unwrap(); + + peer_store.add_indirect_peers(&clock.clock(), peer_infos.into_iter()); + assert_peers_in_cache(&peer_store, &peer_ids, &peer_addresses); + + peer_store.0.lock().delete_peers(&peer_ids); + assert_peers_in_cache(&peer_store, &[], &[]); +} + +#[test] +fn test_lru_eviction() { + let clock = time::FakeClock::default(); + let mut config = make_config(&[], Default::default(), false); + config.peer_states_cache_size = 10; + let peer_store = PeerStore::new(&clock.clock(), config).unwrap(); + + let (peer_ids, peer_infos): (Vec<_>, Vec<_>) = (0..15) + .map(|i| { + let id = get_peer_id(format!("node{}", i)); + let info = get_peer_info(id.clone(), Some(get_addr(i as u16))); + (id, info) + }) + .unzip(); + let peer_addresses = peer_infos.iter().map(|info| info.addr.unwrap()).collect::>(); + + // Fill the peer_store with the first peer_infos + peer_store.add_indirect_peers(&clock.clock(), peer_infos[0..10].iter().cloned()); + assert_peers_in_cache(&peer_store, &peer_ids[0..10], &peer_addresses[0..10]); + + // Push additional peers and check that the most recent peers are retained + peer_store.add_indirect_peers(&clock.clock(), peer_infos[10..].iter().cloned()); + assert_peers_in_cache(&peer_store, &peer_ids[5..], &peer_addresses[5..]); +} + +/// Tests that pushing the same peers twice to the peer store does not update their +/// place in the LruCache the second time. +/// +/// We may learn of the same peer(s) multiple times as we exchange PeersRequest/PeersResponse +/// with any given neighbor. However, it is not a signal of any new information about the peer, +/// just that the peer continues to be stored by the responding neighbor. +#[test] +fn test_lru_ignore_duplicate_peers() { + let clock = time::FakeClock::default(); + let mut config = make_config(&[], Default::default(), false); + config.peer_states_cache_size = 10; + let peer_store = PeerStore::new(&clock.clock(), config).unwrap(); + + let (peer_ids, peer_infos): (Vec<_>, Vec<_>) = (0..15) + .map(|i| { + let id = get_peer_id(format!("node{}", i)); + let info = get_peer_info(id.clone(), Some(get_addr(i as u16))); + (id, info) + }) + .unzip(); + let peer_addresses = peer_infos.iter().map(|info| info.addr.unwrap()).collect::>(); + + // Fill the peer_store with the first 10 peer_infos + peer_store.add_indirect_peers(&clock.clock(), peer_infos[0..10].iter().cloned()); + assert_peers_in_cache(&peer_store, &peer_ids[0..10], &peer_addresses[0..10]); + + // Push the first 5 peer_infos again, which should not affect their order in the cache + peer_store.add_indirect_peers(&clock.clock(), peer_infos[0..5].iter().cloned()); + assert_peers_in_cache(&peer_store, &peer_ids[0..10], &peer_addresses[0..10]); + + // Push additional peers and check that the most recent peers are retained + peer_store.add_indirect_peers(&clock.clock(), peer_infos[10..].iter().cloned()); + assert_peers_in_cache(&peer_store, &peer_ids[5..], &peer_addresses[5..]); +} diff --git a/chain/network/src/peer_manager/testonly.rs b/chain/network/src/peer_manager/testonly.rs new file mode 100644 index 000000000..77a5c7c73 --- /dev/null +++ b/chain/network/src/peer_manager/testonly.rs @@ -0,0 +1,577 @@ +use crate::accounts_data::AccountDataCacheSnapshot; +use crate::broadcast; +use crate::config; +use crate::network_protocol::testonly as data; +use crate::network_protocol::SnapshotHostInfo; +use crate::network_protocol::SyncSnapshotHosts; +use crate::network_protocol::{ + EdgeState, Encoding, PeerInfo, PeerMessage, SignedAccountData, SyncAccountsData, +}; +use crate::peer; +use crate::peer::peer_actor::ClosingReason; +use crate::peer_manager::network_state::NetworkState; +use crate::peer_manager::peer_manager_actor::Event as PME; +use crate::snapshot_hosts::SnapshotHostsCache; +use crate::tcp; +use crate::test_utils; +use crate::testonly::actix::ActixSystem; +use crate::testonly::fake_client; +use crate::types::{ + AccountKeys, ChainInfo, KnownPeerStatus, NetworkRequests, PeerManagerMessageRequest, + ReasonForBan, +}; +use crate::PeerManagerActor; +use unc_async::messaging::IntoSender; +use unc_async::time; +use unc_o11y::WithSpanContextExt; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::types::AccountId; +use std::collections::HashSet; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; + +/// Each actix arbiter (in fact, the underlying tokio runtime) creates 4 file descriptors: +/// 1. eventfd2() +/// 2. epoll_create1() +/// 3. fcntl() duplicating one end of some globally shared socketpair() +/// 4. fcntl() duplicating epoll socket created in (2) +/// This gives 5 file descriptors per PeerActor (4 + 1 TCP socket). +pub(crate) const FDS_PER_PEER: usize = 5; + +#[derive(actix::Message)] +#[rtype("()")] +struct WithNetworkState( + Box) -> Pin>>>, +); + +impl actix::Handler for PeerManagerActor { + type Result = (); + fn handle( + &mut self, + WithNetworkState(f): WithNetworkState, + _: &mut Self::Context, + ) -> Self::Result { + assert!(actix::Arbiter::current().spawn(f(self.state.clone()))); + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Event { + Client(fake_client::Event), + PeerManager(PME), +} + +pub(crate) struct ActorHandler { + pub cfg: config::NetworkConfig, + pub events: broadcast::Receiver, + pub actix: ActixSystem, +} + +pub(crate) fn unwrap_sync_accounts_data_processed(ev: Event) -> Option { + match ev { + Event::PeerManager(PME::MessageProcessed( + tcp::Tier::T2, + PeerMessage::SyncAccountsData(msg), + )) => Some(msg), + _ => None, + } +} + +pub(crate) fn unwrap_sync_snapshot_hosts_data_processed(ev: Event) -> Option { + match ev { + Event::PeerManager(PME::MessageProcessed( + tcp::Tier::T2, + PeerMessage::SyncSnapshotHosts(msg), + )) => Some(msg), + _ => None, + } +} + +pub(crate) fn make_chain_info( + chain: &data::Chain, + validators: &[&config::NetworkConfig], +) -> ChainInfo { + // Construct ChainInfo with tier1_accounts set to `validators`. + let mut chain_info = chain.get_chain_info(); + let mut account_keys = AccountKeys::new(); + for cfg in validators { + let s = &cfg.validator.as_ref().unwrap().signer; + account_keys.entry(s.validator_id().clone()).or_default().insert(s.public_key()); + } + chain_info.tier1_accounts = Arc::new(account_keys); + chain_info +} + +pub(crate) struct RawConnection { + events: broadcast::Receiver, + stream: tcp::Stream, + cfg: peer::testonly::PeerConfig, +} + +impl RawConnection { + pub async fn handshake(mut self, clock: &time::Clock) -> peer::testonly::PeerHandle { + let stream_id = self.stream.id(); + let mut peer = + peer::testonly::PeerHandle::start_endpoint(clock.clone(), self.cfg, self.stream).await; + + // Wait for the new peer to complete the handshake. + peer.complete_handshake().await; + + // Wait for the peer manager to complete the handshake. + self.events + .recv_until(|ev| match ev { + Event::PeerManager(PME::HandshakeCompleted(ev)) if ev.stream_id == stream_id => { + Some(()) + } + Event::PeerManager(PME::ConnectionClosed(ev)) if ev.stream_id == stream_id => { + panic!("handshake aborted: {}", ev.reason) + } + _ => None, + }) + .await; + peer + } + + // Try to perform a handshake. PeerManager is expected to reject the handshake. + pub async fn manager_fail_handshake(mut self, clock: &time::Clock) -> ClosingReason { + let stream_id = self.stream.id(); + let peer = + peer::testonly::PeerHandle::start_endpoint(clock.clone(), self.cfg, self.stream).await; + let reason = self + .events + .recv_until(|ev| match ev { + Event::PeerManager(PME::ConnectionClosed(ev)) if ev.stream_id == stream_id => { + Some(ev.reason) + } + Event::PeerManager(PME::HandshakeCompleted(ev)) if ev.stream_id == stream_id => { + panic!("PeerManager accepted the handshake") + } + _ => None, + }) + .await; + drop(peer); + reason + } +} + +impl ActorHandler { + pub fn peer_info(&self) -> PeerInfo { + PeerInfo { + id: PeerId::new(self.cfg.node_key.public_key()), + addr: self.cfg.node_addr.as_ref().map(|a| **a), + account_id: None, + } + } + + pub async fn send_outbound_connect(&self, peer_info: &PeerInfo, tier: tcp::Tier) { + let addr = self.actix.addr.clone(); + let peer_info = peer_info.clone(); + let stream = tcp::Stream::connect(&peer_info, tier).await.unwrap(); + addr.do_send(PeerManagerMessageRequest::OutboundTcpConnect(stream).with_span_context()); + } + + pub fn connect_to( + &self, + peer_info: &PeerInfo, + tier: tcp::Tier, + ) -> impl 'static + Send + Future { + let addr = self.actix.addr.clone(); + let events = self.events.clone(); + let peer_info = peer_info.clone(); + async move { + let stream = tcp::Stream::connect(&peer_info, tier).await.unwrap(); + let mut events = events.from_now(); + let stream_id = stream.id(); + addr.do_send(PeerManagerMessageRequest::OutboundTcpConnect(stream).with_span_context()); + events + .recv_until(|ev| match &ev { + Event::PeerManager(PME::HandshakeCompleted(ev)) + if ev.stream_id == stream_id => + { + Some(stream_id) + } + Event::PeerManager(PME::ConnectionClosed(ev)) if ev.stream_id == stream_id => { + panic!("PeerManager rejected the handshake") + } + _ => None, + }) + .await + } + } + + pub async fn with_state>( + &self, + f: impl 'static + Send + FnOnce(Arc) -> Fut, + ) -> R { + let (send, recv) = tokio::sync::oneshot::channel(); + self.actix + .addr + .send(WithNetworkState(Box::new(|s| { + Box::pin(async { send.send(f(s).await).ok().unwrap() }) + }))) + .await + .unwrap(); + recv.await.unwrap() + } + + pub async fn start_inbound( + &self, + chain: Arc, + network_cfg: config::NetworkConfig, + ) -> RawConnection { + // To avoid race condition: + // 1. reserve a TCP port + // 2. snapshot event stream + // 3. establish connection. + let socket = tcp::Socket::bind(); + let events = self.events.from_now(); + let stream = socket.connect(&self.peer_info(), tcp::Tier::T2).await; + let stream_id = stream.id(); + let conn = RawConnection { + events, + stream, + cfg: peer::testonly::PeerConfig { + network: network_cfg, + chain, + force_encoding: Some(Encoding::Proto), + }, + }; + // Wait until the TCP connection is accepted or rejected. + // The Handshake is not performed yet. + conn.events + .clone() + .recv_until(|ev| match ev { + Event::PeerManager(PME::HandshakeStarted(ev)) if ev.stream_id == stream_id => { + Some(()) + } + Event::PeerManager(PME::ConnectionClosed(ev)) if ev.stream_id == stream_id => { + Some(()) + } + _ => None, + }) + .await; + conn + } + + pub async fn start_outbound( + &self, + chain: Arc, + network_cfg: config::NetworkConfig, + tier: tcp::Tier, + ) -> RawConnection { + let (outbound_stream, inbound_stream) = + tcp::Stream::loopback(network_cfg.node_id(), tier).await; + let stream_id = outbound_stream.id(); + let events = self.events.from_now(); + self.actix.addr.do_send( + PeerManagerMessageRequest::OutboundTcpConnect(outbound_stream).with_span_context(), + ); + let conn = RawConnection { + events, + stream: inbound_stream, + cfg: peer::testonly::PeerConfig { + network: network_cfg, + chain, + force_encoding: Some(Encoding::Proto), + }, + }; + // Wait until the handshake started or connection is closed. + // The Handshake is not performed yet. + conn.events + .clone() + .recv_until(|ev| match ev { + Event::PeerManager(PME::HandshakeStarted(ev)) if ev.stream_id == stream_id => { + Some(()) + } + Event::PeerManager(PME::ConnectionClosed(ev)) if ev.stream_id == stream_id => { + Some(()) + } + _ => None, + }) + .await; + conn + } + + /// Checks internal consistency of the PeerManagerActor. + /// This is a partial implementation, add more invariant checks + /// if needed. + pub async fn check_consistency(&self) { + self.with_state(|s| async move { + // Check that the set of ready connections matches the PeerStore state. + let tier2: HashSet<_> = s.tier2.load().ready.keys().cloned().collect(); + let store: HashSet<_> = s + .peer_store + .dump() + .into_iter() + .filter_map(|state| { + if state.status == KnownPeerStatus::Connected { + Some(state.peer_info.id) + } else { + None + } + }) + .collect(); + assert_eq!(tier2, store); + + // Check that the local_edges of the graph match the TIER2 connection pool. + let node_id = s.config.node_id(); + let local_edges: HashSet<_> = s + .graph + .load() + .local_edges + .values() + .filter_map(|e| match e.edge_type() { + EdgeState::Active => Some(e.other(&node_id).unwrap().clone()), + EdgeState::Removed => None, + }) + .collect(); + assert_eq!(tier2, local_edges); + }) + .await + } + + pub async fn fix_local_edges(&self, clock: &time::Clock, timeout: time::Duration) { + let clock = clock.clone(); + self.with_state(move |s| async move { s.fix_local_edges(&clock, timeout).await }).await + } + + pub async fn set_chain_info(&self, chain_info: ChainInfo) -> bool { + self.with_state(move |s| async move { s.set_chain_info(chain_info) }).await + } + + pub async fn tier1_advertise_proxies( + &self, + clock: &time::Clock, + ) -> Option> { + let clock = clock.clone(); + self.with_state(move |s| async move { s.tier1_advertise_proxies(&clock).await }).await + } + + pub async fn disconnect(&self, peer_id: &PeerId) { + let peer_id = peer_id.clone(); + self.with_state(move |s| async move { + let stopped: Vec<()> = s + .tier2 + .load() + .ready + .values() + .filter(|c| c.peer_info.id == peer_id) + .map(|c| { + c.stop(None); + () + }) + .collect(); + assert!(stopped.len() == 1); + }) + .await + } + + pub async fn disconnect_and_ban( + &self, + clock: &time::Clock, + peer_id: &PeerId, + reason: ReasonForBan, + ) { + // TODO(gprusak): make it wait asynchronously for the connection to get closed. + // TODO(gprusak): figure out how to await for both ends to disconnect. + let clock = clock.clone(); + let peer_id = peer_id.clone(); + self.with_state(move |s| async move { s.disconnect_and_ban(&clock, &peer_id, reason) }) + .await + } + + pub async fn peer_store_update(&self, clock: &time::Clock) { + let clock = clock.clone(); + self.with_state(move |s| async move { s.peer_store.update(&clock) }).await; + } + + pub async fn send_ping(&self, clock: &time::Clock, nonce: u64, target: PeerId) { + let clock = clock.clone(); + self.with_state(move |s| async move { + s.send_ping(&clock, tcp::Tier::T2, nonce, target); + }) + .await; + } + + pub async fn announce_account(&self, aa: AnnounceAccount) { + self.actix + .addr + .send( + PeerManagerMessageRequest::NetworkRequests(NetworkRequests::AnnounceAccount(aa)) + .with_span_context(), + ) + .await + .unwrap(); + } + + // Awaits until the accounts_data state satisfies predicate `pred`. + pub async fn wait_for_accounts_data_pred( + &self, + pred: impl Fn(Arc) -> bool, + ) { + let mut events = self.events.from_now(); + loop { + let got = self.with_state(move |s| async move { s.accounts_data.load() }).await; + if pred(got) { + break; + } + // It is important that we wait for the next PeerMessage::SyncAccountsData to get + // PROCESSED, not just RECEIVED. Otherwise we would get a race condition. + events.recv_until(unwrap_sync_accounts_data_processed).await; + } + } + + // Awaits until the accounts_data state matches `want`. + pub async fn wait_for_accounts_data(&self, want: &HashSet>) { + self.wait_for_accounts_data_pred(|cache| { + &cache.data.values().cloned().collect::>() == want + }) + .await + } + + // Awaits until the snapshot_hosts state satisfies predicate `pred`. + pub async fn wait_for_snapshot_hosts_pred( + &self, + pred: impl Fn(Arc) -> bool, + ) { + let mut events = self.events.from_now(); + loop { + let got = self.with_state(move |s| async move { s.snapshot_hosts.clone() }).await; + if pred(got) { + break; + } + + // If the state doesn't match, wait until the next snapshot_hosts event is processed and check again. + events.recv_until(unwrap_sync_snapshot_hosts_data_processed).await; + } + } + + // Awaits until the snapshot_hosts state matches `want`. + pub async fn wait_for_snapshot_hosts(&self, want: &HashSet>) { + self.wait_for_snapshot_hosts_pred(|cache| { + &cache.get_hosts().into_iter().collect::>() == want + }) + .await + } + + pub async fn wait_for_direct_connection(&self, target_peer_id: PeerId) { + let mut events = self.events.from_now(); + loop { + let connections = + self.with_state(|s| async move { s.tier2.load().ready.clone() }).await; + + if connections.contains_key(&target_peer_id) { + return; + } + + events + .recv_until(|ev| match ev { + Event::PeerManager(PME::HandshakeCompleted { .. }) => Some(()), + _ => None, + }) + .await; + } + } + + // Awaits until the routing_table matches `want`. + pub async fn wait_for_routing_table(&self, want: &[(PeerId, Vec)]) { + let mut events = self.events.from_now(); + loop { + let got = + self.with_state(|s| async move { s.graph.routing_table.info().next_hops }).await; + if test_utils::expected_routing_tables(&got, want) { + return; + } + events + .recv_until(|ev| match ev { + Event::PeerManager(PME::EdgesAdded { .. }) => Some(()), + _ => None, + }) + .await; + } + } + + pub async fn wait_for_account_owner(&self, account: &AccountId) -> PeerId { + let mut events = self.events.from_now(); + loop { + let account = account.clone(); + let got = self + .with_state(|s| async move { s.account_announcements.get_account_owner(&account) }) + .await; + if let Some(got) = got { + return got; + } + events + .recv_until(|ev| match ev { + Event::PeerManager(PME::AccountsAdded(_)) => Some(()), + _ => None, + }) + .await; + } + } + + pub async fn wait_for_num_connected_peers(&self, wanted: usize) { + let mut events = self.events.from_now(); + loop { + let got = self.with_state(|s| async move { s.tier2.load().ready.len() }).await; + if got == wanted { + return; + } + events + .recv_until(|ev| match ev { + Event::PeerManager(PME::EdgesAdded { .. }) => Some(()), + _ => None, + }) + .await; + } + } + + /// Executes `NetworkState::tier1_connect` method. + pub async fn tier1_connect(&self, clock: &time::Clock) { + let clock = clock.clone(); + self.with_state(move |s| async move { + s.tier1_connect(&clock).await; + }) + .await; + } + + /// Executes `NetworkState::update_connection_store` method. + pub async fn update_connection_store(&self, clock: &time::Clock) { + let clock = clock.clone(); + self.with_state(move |s| async move { + s.update_connection_store(&clock); + }) + .await; + } +} + +pub(crate) async fn start( + clock: time::Clock, + store: Arc, + cfg: config::NetworkConfig, + chain: Arc, +) -> ActorHandler { + let (send, mut recv) = broadcast::unbounded_channel(); + let actix = ActixSystem::spawn({ + let mut cfg = cfg.clone(); + let chain = chain.clone(); + move || { + let genesis_id = chain.genesis_id.clone(); + let fc = Arc::new(fake_client::Fake { event_sink: send.sink().compose(Event::Client) }); + cfg.event_sink = send.sink().compose(Event::PeerManager); + PeerManagerActor::spawn(clock, store, cfg, fc.clone(), fc.as_sender(), genesis_id) + .unwrap() + } + }) + .await; + let h = ActorHandler { cfg, actix, events: recv.clone() }; + // Wait for the server to start. + recv.recv_until(|ev| match ev { + Event::PeerManager(PME::ServerStarted) => Some(()), + _ => None, + }) + .await; + h.set_chain_info(chain.get_chain_info()).await; + h +} diff --git a/chain/network/src/peer_manager/tests/accounts_data.rs b/chain/network/src/peer_manager/tests/accounts_data.rs new file mode 100644 index 000000000..239d875e3 --- /dev/null +++ b/chain/network/src/peer_manager/tests/accounts_data.rs @@ -0,0 +1,335 @@ +use crate::concurrency::rate; +use crate::network_protocol::testonly as data; +use crate::network_protocol::SyncAccountsData; +use crate::peer; +use crate::peer_manager; +use crate::peer_manager::peer_manager_actor::Event as PME; +use crate::peer_manager::testonly; +use crate::peer_manager::testonly::start as start_pm; +use crate::tcp; +use crate::testonly::{make_rng, AsSet as _}; +use crate::types::PeerMessage; +use itertools::Itertools; +use unc_async::time; +use unc_o11y::testonly::init_test_logger; +use unc_store::db::TestDB; +use peer_manager::testonly::FDS_PER_PEER; +use pretty_assertions::assert_eq; +use rand::seq::SliceRandom as _; +use std::collections::HashSet; +use std::sync::Arc; + +#[tokio::test] +async fn broadcast() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + let clock = clock.clock(); + let clock = &clock; + + let pm = peer_manager::testonly::start( + clock.clone(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + + let take_incremental_sync = |ev| match ev { + peer::testonly::Event::Network(PME::MessageProcessed( + tcp::Tier::T2, + PeerMessage::SyncAccountsData(msg), + )) if msg.incremental => Some(msg), + _ => None, + }; + let take_full_sync = |ev| match ev { + peer::testonly::Event::Network(PME::MessageProcessed( + tcp::Tier::T2, + PeerMessage::SyncAccountsData(msg), + )) if !msg.incremental => Some(msg), + _ => None, + }; + + let data = chain.make_tier1_data(rng, clock); + tracing::info!(target:"test", "Connect peer, expect initial sync to be empty."); + let mut peer1 = + pm.start_inbound(chain.clone(), chain.make_config(rng)).await.handshake(clock).await; + let got1 = peer1.events.recv_until(take_full_sync).await; + assert_eq!(got1.accounts_data, vec![]); + + tracing::info!(target:"test", "Send some data."); + let msg = SyncAccountsData { + accounts_data: vec![data[0].clone(), data[1].clone()], + incremental: true, + requesting_full_sync: false, + }; + let want: HashSet<_> = msg.accounts_data.iter().cloned().collect(); + peer1.send(PeerMessage::SyncAccountsData(msg)).await; + pm.wait_for_accounts_data(&want).await; + + tracing::info!(target:"test", "Connect another peer and perform initial full sync."); + let mut peer2 = + pm.start_inbound(chain.clone(), chain.make_config(rng)).await.handshake(clock).await; + let got2 = peer2.events.recv_until(take_full_sync).await; + assert_eq!(got2.accounts_data.as_set(), want.iter().collect::>()); + + tracing::info!(target:"test", "Send a mix of new and old data. Only new data should be broadcasted."); + let msg = SyncAccountsData { + accounts_data: vec![data[1].clone(), data[2].clone()], + incremental: true, + requesting_full_sync: false, + }; + let want = vec![data[2].clone()]; + peer1.send(PeerMessage::SyncAccountsData(msg)).await; + let got2 = peer2.events.recv_until(take_incremental_sync).await; + assert_eq!(got2.accounts_data.as_set(), want.as_set()); + + tracing::info!(target:"test", "Send a request for a full sync."); + let want = vec![data[0].clone(), data[1].clone(), data[2].clone()]; + let mut events = peer1.events.from_now(); + peer1 + .send(PeerMessage::SyncAccountsData(SyncAccountsData { + accounts_data: vec![], + incremental: true, + requesting_full_sync: true, + })) + .await; + let got1 = events.recv_until(take_full_sync).await; + assert_eq!(got1.accounts_data.as_set(), want.as_set()); +} + +// Test with 3 peer managers connected sequentially: 1-2-3 +// All of them are validators. +// No matter what the order of shifting into the epoch, +// all of them should receive all the AccountDatas eventually. +#[tokio::test] +async fn gradual_epoch_change() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let mut pms = vec![]; + for _ in 0..3 { + pms.push( + peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await, + ); + } + + // 0 <-> 1 <-> 2 + let pm1 = pms[1].peer_info(); + let pm2 = pms[2].peer_info(); + pms[0].connect_to(&pm1, tcp::Tier::T2).await; + pms[1].connect_to(&pm2, tcp::Tier::T2).await; + + // For every order of nodes. + for ids in (0..pms.len()).permutations(pms.len()) { + tracing::info!(target:"test", "permutation {ids:?}"); + clock.advance(time::Duration::hours(1)); + let chain_info = testonly::make_chain_info( + &chain, + &pms.iter().map(|pm| &pm.cfg).collect::>()[..], + ); + + let mut want = HashSet::new(); + tracing::info!(target:"test", "advance epoch in the given order."); + for id in ids { + pms[id].set_chain_info(chain_info.clone()).await; + // In this tests each node is its own proxy, so it can immediately + // connect to itself (to verify the public addr) and advertise it. + // If some other node B was a proxy for a node A, then first both + // A and B would have to update their chain_info, and only then A + // would be able to connect to B and advertise B as proxy afterwards. + want.extend(pms[id].tier1_advertise_proxies(&clock.clock()).await); + } + for pm in &mut pms { + tracing::info!(target:"test", "wait for data to arrive to {}.",pm.cfg.node_id()); + pm.wait_for_accounts_data(&want).await; + } + } +} + +// Test is expected to take ~5s. +// Test with 20 peer managers connected in layers: +// - 1st 5 and 2nd 5 are connected in full bipartite graph. +// - 2nd 5 and 3rd 5 ... +// - 3rd 5 and 4th 5 ... +// All of them are validators. +#[tokio::test(flavor = "multi_thread")] +async fn rate_limiting() { + init_test_logger(); + // Adjust the file descriptors limit, so that we can create many connection in the test. + const MAX_CONNECTIONS: usize = 300; + let limit = rlimit::Resource::NOFILE.get().unwrap(); + rlimit::Resource::NOFILE + .set(std::cmp::min(limit.1, (1000 + 2 * FDS_PER_PEER * MAX_CONNECTIONS) as u64), limit.1) + .unwrap(); + + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + // TODO(gprusak): 10 connections per peer is not much, try to scale up this test 2x (some config + // tweaking might be required). + let n = 4; // layers + let m = 5; // peer managers per layer + let mut pms = vec![]; + for _ in 0..n * m { + let mut cfg = chain.make_config(rng); + cfg.accounts_data_broadcast_rate_limit = rate::Limit { qps: 0.5, burst: 1 }; + pms.push( + peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + cfg, + chain.clone(), + ) + .await, + ); + } + tracing::info!(target:"test", "Construct a 4-layer bipartite graph."); + + let mut connections = 0; + let mut tasks = vec![]; + for i in 0..n - 1 { + for j in 0..m { + for k in 0..m { + let pi = pms[(i + 1) * m + k].peer_info(); + tasks.push(tokio::spawn(pms[i * m + j].connect_to(&pi, tcp::Tier::T2))); + connections += 1; + } + } + } + for t in tasks { + t.await.unwrap(); + } + + // Construct ChainInfo with tier1_accounts containing all validators. + let chain_info = + testonly::make_chain_info(&chain, &pms.iter().map(|pm| &pm.cfg).collect::>()[..]); + + clock.advance(time::Duration::hours(1)); + + // Capture the event streams now, so that we can compute + // the total number of SyncAccountsData messages exchanged in the process. + let events: Vec<_> = pms.iter().map(|pm| pm.events.from_now()).collect(); + + tracing::info!(target:"test","Advance epoch in random order."); + pms.shuffle(rng); + let mut want = HashSet::new(); + for pm in &mut pms { + pm.set_chain_info(chain_info.clone()).await; + want.extend(pm.tier1_advertise_proxies(&clock.clock()).await); + } + + tracing::info!(target:"test","Wait for data to arrive."); + for pm in &mut pms { + pm.wait_for_accounts_data(&want).await; + } + + tracing::info!(target:"test","Count the SyncAccountsData messages exchanged."); + let mut msgs = 0; + for mut es in events { + while let Some(ev) = es.try_recv() { + if peer_manager::testonly::unwrap_sync_accounts_data_processed(ev).is_some() { + msgs += 1; + } + } + } + + // We expect 3 rounds communication to cover the distance from 1st layer to 4th layer + // and +1 full sync at handshake. + // The communication is bidirectional, which gives 8 messages per connection. + // Then add +50% to accomodate for test execution flakiness (12 messages per connection). + // TODO(gprusak): if the test is still flaky, upgrade FakeClock for stricter flow control. + let want_max = connections * 12; + println!("got {msgs}, want <= {want_max}"); + assert!(msgs <= want_max, "got {msgs} messages, want at most {want_max}"); + + drop(pms); +} + +/// When validator node is restarted, it should immediately override +/// the AccountData that is present in the network from the previous run. +#[tokio::test] +async fn validator_node_restart() { + init_test_logger(); + + const ZERO: time::Duration = time::Duration::ZERO; + const SEC: time::Duration = time::Duration::seconds(1); + + // We test restarting node with various number of AccountsData versions + // broadcasted before restart, and various downtime (represented as UTC clock shift, which may + // be negative). + for (n, downtime) in [ + // If no version was emitted, timestamp is not important + (0, -SEC), + (0, ZERO), + (0, SEC), + // If 1 version was emitted, the version will be immediately correctly + // overriden as long as timestamp has changed. + (1, -SEC), + (1, SEC), + // If multiple versions were emitted, timestamp is not important + (2, -SEC), + (2, ZERO), + (2, SEC), + ] { + tracing::info!(target:"test", "test case (n = {n}, downtime = {downtime})"); + + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + // Start 2 nodes with node pm0 being a validator. + let mut cfg = chain.make_config(rng); + let chain_info = testonly::make_chain_info(&chain, &[&cfg]); + let pm0 = start_pm(clock.clock(), TestDB::new(), cfg.clone(), chain.clone()).await; + let pm1 = + start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + pm0.set_chain_info(chain_info.clone()).await; + pm1.set_chain_info(chain_info.clone()).await; + + // Connect the nodes and make pm0 broadcast `n` versions of the AccountData. + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + for _ in 0..n { + let want = pm0.tier1_advertise_proxies(&clock.clock()).await.unwrap(); + pm1.wait_for_accounts_data(&HashSet::from([want.clone()])).await; + } + + // Shut down pm0 and restart it after `downtime` with a different node_key. + drop(pm0); + clock.set_utc(clock.now_utc() + downtime); + cfg.node_key = data::make_secret_key(rng); + let pm0 = start_pm(clock.clock(), TestDB::new(), cfg.clone(), chain.clone()).await; + pm0.set_chain_info(chain_info.clone()).await; + + // Sign AccountData before even connecting to the network. + assert!(pm0.tier1_advertise_proxies(&clock.clock()).await.is_some()); + // Connect to the node which knows the old AccountData. + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + + // Now pm0 should learn from pm1 about the conflicting version and should broadcast + // new AccountData (with higher version) to override the old AccountData. + let pm0_account_key = cfg.validator.as_ref().unwrap().signer.public_key(); + pm1.wait_for_accounts_data_pred(|accounts_data| { + let data = match accounts_data.data.get(&pm0_account_key) { + Some(it) => it, + None => return false, + }; + data.peer_id == cfg.node_id() + }) + .await; + } +} diff --git a/chain/network/src/peer_manager/tests/connection_pool.rs b/chain/network/src/peer_manager/tests/connection_pool.rs new file mode 100644 index 000000000..c7779b98f --- /dev/null +++ b/chain/network/src/peer_manager/tests/connection_pool.rs @@ -0,0 +1,312 @@ +use crate::network_protocol::testonly as data; +use crate::network_protocol::PeerMessage; +use crate::network_protocol::{Encoding, Handshake, OwnedAccount, PartialEdgeInfo}; +use crate::peer::peer_actor::ClosingReason; +use crate::peer_manager; +use crate::peer_manager::connection; +use crate::peer_manager::network_state::LIMIT_PENDING_PEERS; +use crate::peer_manager::peer_manager_actor::Event as PME; +use crate::peer_manager::testonly::Event; +use crate::private_actix::RegisterPeerError; +use crate::tcp; +use crate::testonly::make_rng; +use crate::testonly::stream::Stream; +use unc_async::time; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::version::PROTOCOL_VERSION; +use std::sync::Arc; + +#[tokio::test] +async fn connection_spam_security_test() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + const PEERS_OVER_LIMIT: usize = 10; + let mut cfg = chain.make_config(rng); + cfg.max_num_peers = (LIMIT_PENDING_PEERS + PEERS_OVER_LIMIT) as u32; + let mut cfg = chain.make_config(rng); + // Make sure that connections will never get dropped. + cfg.handshake_timeout = time::Duration::hours(1); + let pm = peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + cfg, + chain.clone(), + ) + .await; + + // Saturate the pending connections limit. + let mut conns = vec![]; + for _ in 0..LIMIT_PENDING_PEERS { + conns.push(pm.start_inbound(chain.clone(), chain.make_config(rng)).await); + } + // Try to establish additional connections. Should fail. + for _ in 0..PEERS_OVER_LIMIT { + let conn = pm.start_inbound(chain.clone(), chain.make_config(rng)).await; + assert_eq!( + ClosingReason::TooManyInbound, + conn.manager_fail_handshake(&clock.clock()).await + ); + } + // Terminate the pending connections. Should succeed. + for c in conns { + c.handshake(&clock.clock()).await; + } +} + +#[tokio::test] +async fn loop_connection() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let pm = peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + let mut cfg = chain.make_config(rng); + cfg.node_key = pm.cfg.node_key.clone(); + + // Starting an outbound loop connection on TIER2 should be stopped without sending the handshake. + let conn = pm.start_outbound(chain.clone(), cfg, tcp::Tier::T2).await; + assert_eq!( + ClosingReason::OutboundNotAllowed(connection::PoolError::UnexpectedLoopConnection), + conn.manager_fail_handshake(&clock.clock()).await + ); + + // An inbound connection pretending to be a loop should be rejected. + let stream = tcp::Stream::connect(&pm.peer_info(), tcp::Tier::T2).await.unwrap(); + let stream_id = stream.id(); + let port = stream.local_addr.port(); + let mut events = pm.events.from_now(); + let mut stream = Stream::new(Some(Encoding::Proto), stream); + stream + .write(&PeerMessage::Tier2Handshake(Handshake { + protocol_version: PROTOCOL_VERSION, + oldest_supported_version: PROTOCOL_VERSION, + sender_peer_id: pm.cfg.node_id(), + target_peer_id: pm.cfg.node_id(), + sender_listen_port: Some(port), + sender_chain_info: chain.get_peer_chain_info(), + partial_edge_info: PartialEdgeInfo::new( + &pm.cfg.node_id(), + &pm.cfg.node_id(), + 1, + &pm.cfg.node_key, + ), + owned_account: None, + })) + .await; + let reason = events + .recv_until(|ev| match ev { + Event::PeerManager(PME::ConnectionClosed(ev)) if ev.stream_id == stream_id => { + Some(ev.reason) + } + Event::PeerManager(PME::HandshakeCompleted(ev)) if ev.stream_id == stream_id => { + panic!("PeerManager accepted the handshake") + } + _ => None, + }) + .await; + assert_eq!( + ClosingReason::RejectedByPeerManager(RegisterPeerError::PoolError( + connection::PoolError::UnexpectedLoopConnection + )), + reason + ); +} + +#[tokio::test] +async fn owned_account_mismatch() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let pm = peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + + // An inbound connection pretending to be a loop should be rejected. + let stream = tcp::Stream::connect(&pm.peer_info(), tcp::Tier::T2).await.unwrap(); + let stream_id = stream.id(); + let port = stream.local_addr.port(); + let mut events = pm.events.from_now(); + let mut stream = Stream::new(Some(Encoding::Proto), stream); + let cfg = chain.make_config(rng); + let vc = cfg.validator.clone().unwrap(); + stream + .write(&PeerMessage::Tier2Handshake(Handshake { + protocol_version: PROTOCOL_VERSION, + oldest_supported_version: PROTOCOL_VERSION, + sender_peer_id: cfg.node_id(), + target_peer_id: pm.cfg.node_id(), + sender_listen_port: Some(port), + sender_chain_info: chain.get_peer_chain_info(), + partial_edge_info: PartialEdgeInfo::new( + &cfg.node_id(), + &pm.cfg.node_id(), + 1, + &cfg.node_key, + ), + owned_account: Some( + OwnedAccount { + account_key: vc.signer.public_key().clone(), + // random peer_id, different than the expected cfg.node_id(). + peer_id: data::make_peer_id(rng), + timestamp: clock.now_utc(), + } + .sign(vc.signer.as_ref()), + ), + })) + .await; + let reason = events + .recv_until(|ev| match ev { + Event::PeerManager(PME::ConnectionClosed(ev)) if ev.stream_id == stream_id => { + Some(ev.reason) + } + Event::PeerManager(PME::HandshakeCompleted(ev)) if ev.stream_id == stream_id => { + panic!("PeerManager accepted the handshake") + } + _ => None, + }) + .await; + assert_eq!(ClosingReason::OwnedAccountMismatch, reason); +} + +#[tokio::test] +async fn owned_account_conflict() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let pm = peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + + let cfg1 = chain.make_config(rng); + let mut cfg2 = chain.make_config(rng); + cfg2.validator = cfg1.validator.clone(); + + // Start 2 connections with the same account_key. + // The second should be rejected. + let conn1 = pm.start_inbound(chain.clone(), cfg1.clone()).await.handshake(&clock.clock()).await; + let reason = + pm.start_inbound(chain.clone(), cfg2).await.manager_fail_handshake(&clock.clock()).await; + assert_eq!( + reason, + ClosingReason::RejectedByPeerManager(RegisterPeerError::PoolError( + connection::PoolError::AlreadyConnectedAccount { + peer_id: cfg1.node_id(), + account_key: cfg1.validator.as_ref().unwrap().signer.public_key(), + } + )) + ); + drop(conn1); + drop(pm); +} + +#[tokio::test] +async fn invalid_edge() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let pm = peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + let cfg = chain.make_config(rng); + let chain_info = peer_manager::testonly::make_chain_info(&chain, &[&cfg]); + pm.set_chain_info(chain_info).await; + + let testcases = [ + ( + "wrong key", + PartialEdgeInfo::new(&cfg.node_id(), &pm.cfg.node_id(), 1, &data::make_secret_key(rng)), + ), + ( + "wrong source", + PartialEdgeInfo::new(&data::make_peer_id(rng), &pm.cfg.node_id(), 1, &cfg.node_key), + ), + ( + "wrong target", + PartialEdgeInfo::new(&cfg.node_id(), &data::make_peer_id(rng), 1, &cfg.node_key), + ), + ]; + + for (name, edge) in &testcases { + for tier in [tcp::Tier::T1, tcp::Tier::T2] { + tracing::info!(target:"test","{name} {tier:?}"); + let stream = tcp::Stream::connect(&pm.peer_info(), tier).await.unwrap(); + let stream_id = stream.id(); + let port = stream.local_addr.port(); + let mut events = pm.events.from_now(); + let mut stream = Stream::new(Some(Encoding::Proto), stream); + let vc = cfg.validator.clone().unwrap(); + let handshake = Handshake { + protocol_version: PROTOCOL_VERSION, + oldest_supported_version: PROTOCOL_VERSION, + sender_peer_id: cfg.node_id(), + target_peer_id: pm.cfg.node_id(), + sender_listen_port: Some(port), + sender_chain_info: chain.get_peer_chain_info(), + partial_edge_info: edge.clone(), + owned_account: Some( + OwnedAccount { + account_key: vc.signer.public_key().clone(), + peer_id: cfg.node_id(), + timestamp: clock.now_utc(), + } + .sign(vc.signer.as_ref()), + ), + }; + let handshake = match tier { + tcp::Tier::T1 => PeerMessage::Tier1Handshake(handshake), + tcp::Tier::T2 => PeerMessage::Tier2Handshake(handshake), + }; + stream.write(&handshake).await; + let reason = events + .recv_until(|ev| match ev { + Event::PeerManager(PME::ConnectionClosed(ev)) if ev.stream_id == stream_id => { + Some(ev.reason) + } + Event::PeerManager(PME::HandshakeCompleted(ev)) + if ev.stream_id == stream_id => + { + panic!("PeerManager accepted the handshake") + } + _ => None, + }) + .await; + assert_eq!( + ClosingReason::RejectedByPeerManager(RegisterPeerError::InvalidEdge), + reason + ); + } + } +} diff --git a/chain/network/src/peer_manager/tests/mod.rs b/chain/network/src/peer_manager/tests/mod.rs new file mode 100644 index 000000000..91fa60d06 --- /dev/null +++ b/chain/network/src/peer_manager/tests/mod.rs @@ -0,0 +1,7 @@ +mod accounts_data; +mod connection_pool; +mod nonce; +mod routing; +mod snapshot_hosts; +mod tier1; +mod tier2; diff --git a/chain/network/src/peer_manager/tests/nonce.rs b/chain/network/src/peer_manager/tests/nonce.rs new file mode 100644 index 000000000..d8df774ea --- /dev/null +++ b/chain/network/src/peer_manager/tests/nonce.rs @@ -0,0 +1,172 @@ +use crate::network_protocol::testonly as data; +use crate::network_protocol::{ + Encoding, Handshake, PartialEdgeInfo, PeerMessage, EDGE_MIN_TIMESTAMP_NONCE, +}; +use crate::peer_manager::testonly::{ActorHandler, Event}; +use crate::peer_manager::{self, peer_manager_actor}; +use crate::tcp; +use crate::testonly::make_rng; +use crate::testonly::stream; +use crate::types::Edge; +use unc_async::time; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::network::PeerId; +use unc_primitives::version; +use std::sync::Arc; + +// Nonces must be odd (as even ones are reserved for tombstones). +fn to_active_nonce(timestamp: time::Utc) -> u64 { + let value = timestamp.unix_timestamp() as u64; + if value % 2 == 0 { + value + 1 + } else { + value + } +} + +// Test connecting to peer manager with timestamp-like nonces. +#[tokio::test] +async fn test_nonces() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::new(*EDGE_MIN_TIMESTAMP_NONCE + time::Duration::days(2)); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let test_cases = [ + // Try to connect with peer with a valid nonce (current timestamp). + (to_active_nonce(clock.now_utc()), true, "current timestamp"), + // Now try the peer with invalid timestamp (in the past) + (to_active_nonce(clock.now_utc() - time::Duration::days(1)), false, "past timestamp"), + // Now try the peer with invalid timestamp (in the future) + (to_active_nonce(clock.now_utc() + time::Duration::days(1)), false, "future timestamp"), + (u64::MAX, false, "u64 max"), + (i64::MAX as u64, false, "i64 max"), + ((i64::MAX - 1) as u64, false, "i64 max - 1"), + (253402300799, false, "Max time"), + (253402300799 + 2, false, "Over max time"), + //(Some(0), false, "Nonce 0"), + (1, true, "Nonce 1"), + ]; + + for test in test_cases { + tracing::info!(target: "test", "Running test {:?}", test.2); + // Start a PeerManager and connect a peer to it. + let pm = peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + + let stream = tcp::Stream::connect(&pm.peer_info(), tcp::Tier::T2).await.unwrap(); + let mut stream = stream::Stream::new(Some(Encoding::Proto), stream); + let peer_key = data::make_secret_key(rng); + let peer_id = PeerId::new(peer_key.public_key()); + let handshake = PeerMessage::Tier2Handshake(Handshake { + protocol_version: version::PROTOCOL_VERSION, + oldest_supported_version: version::PEER_MIN_ALLOWED_PROTOCOL_VERSION, + sender_peer_id: peer_id.clone(), + target_peer_id: pm.cfg.node_id(), + // we have to set this even if we have no intention of listening since otherwise + // the peer will drop our connection + sender_listen_port: Some(24567), + sender_chain_info: chain.get_peer_chain_info(), + partial_edge_info: PartialEdgeInfo::new(&peer_id, &pm.cfg.node_id(), test.0, &peer_key), + owned_account: None, + }); + stream.write(&handshake).await; + if test.1 { + match stream.read().await { + Ok(PeerMessage::Tier2Handshake { .. }) => {} + got => panic!("got = {got:?}, want Handshake"), + } + } else { + match stream.read().await { + Err(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => {} + got => panic!("got = {got:?}, want UnexpectedEof"), + } + } + } +} + +async fn wait_for_edge(actor_handler: &mut ActorHandler) -> Edge { + actor_handler + .events + .recv_until(|ev| match ev { + Event::PeerManager(peer_manager_actor::Event::EdgesAdded(ev)) => Some(ev[0].clone()), + _ => None, + }) + .await +} + +#[tokio::test] +#[ignore] // TODO: #8854 +/// Create 2 peer managers, that connect to each other. +/// Verify that the will refresh their nonce after some time. +async fn test_nonce_refresh() { + init_test_logger(); + let mut rng = make_rng(921853255); + let rng = &mut rng; + let mut clock = time::FakeClock::new(*EDGE_MIN_TIMESTAMP_NONCE + time::Duration::days(2)); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + // Start a PeerManager. + let pm = peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + + // Start another peer manager. + let mut pm2 = peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + + pm2.connect_to(&pm.peer_info(), tcp::Tier::T2).await; + + let edge = wait_for_edge(&mut pm2).await; + let start_time = clock.now_utc(); + // First edge between them should have the nonce equal to the current time. + assert_eq!(Edge::nonce_to_utc(edge.nonce()).unwrap().unwrap(), start_time); + + // Advance a clock by 1 hour. + clock.advance(time::Duration::HOUR); + + let new_nonce_utc = clock.now_utc(); + + loop { + let edge = wait_for_edge(&mut pm2).await; + if Edge::nonce_to_utc(edge.nonce()).unwrap().unwrap() == start_time { + tracing::debug!("Still seeing old edge.."); + } else { + assert_eq!(Edge::nonce_to_utc(edge.nonce()).unwrap().unwrap(), new_nonce_utc); + break; + } + } + + // Check that the nonces were properly updates on both pm and pm2 states. + let pm_peer_info = pm.peer_info().id; + let pm2_nonce = pm2 + .with_state( + |s| async move { s.graph.load().local_edges.get(&pm_peer_info).unwrap().nonce() }, + ) + .await; + + assert_eq!(Edge::nonce_to_utc(pm2_nonce).unwrap().unwrap(), new_nonce_utc); + + let pm_nonce = pm + .with_state(|s| async move { + s.graph.load().local_edges.get(&pm2.peer_info().id).unwrap().nonce() + }) + .await; + + assert_eq!(Edge::nonce_to_utc(pm_nonce).unwrap().unwrap(), new_nonce_utc); +} diff --git a/chain/network/src/peer_manager/tests/routing.rs b/chain/network/src/peer_manager/tests/routing.rs new file mode 100644 index 000000000..c87782e89 --- /dev/null +++ b/chain/network/src/peer_manager/tests/routing.rs @@ -0,0 +1,1426 @@ +use crate::blacklist; +use crate::broadcast; +use crate::config::NetworkConfig; +use crate::network_protocol::testonly as data; +use crate::network_protocol::{Edge, Encoding, Ping, Pong, RoutedMessageBody, RoutingTableUpdate}; +use crate::peer; +use crate::peer::peer_actor::{ + ClosingReason, ConnectionClosedEvent, DROP_DUPLICATED_MESSAGES_PERIOD, +}; +use crate::peer_manager; +use crate::peer_manager::peer_manager_actor::Event as PME; +use crate::peer_manager::testonly::start as start_pm; +use crate::peer_manager::testonly::Event; +use crate::private_actix::RegisterPeerError; +use crate::store; +use crate::tcp; +use crate::testonly::{abort_on_panic, make_rng, Rng}; +use crate::types::PeerMessage; +use crate::types::{PeerInfo, ReasonForBan}; +use unc_async::time; +use unc_primitives::network::PeerId; +use unc_store::db::TestDB; +use pretty_assertions::assert_eq; +use rand::seq::IteratorRandom; +use rand::Rng as _; +use std::collections::HashSet; +use std::net::Ipv6Addr; +use std::sync::Arc; + +// test routing in a two-node network before and after connecting the nodes +#[tokio::test] +async fn simple() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "start two nodes"); + let pm0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[]).await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[]).await; + + tracing::info!(target:"test", "connect the nodes"); + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[(id1.clone(), vec![id1.clone()])]).await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[(id0.clone(), vec![id0.clone()])]).await; +} + +// test routing for three nodes in a line +#[tokio::test] +async fn three_nodes_path() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "connect three nodes in a line"); + let pm0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm2 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + pm1.connect_to(&pm2.peer_info(), tcp::Tier::T2).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + let id2 = pm2.cfg.node_id(); + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[ + (id1.clone(), vec![id1.clone()]), + (id2.clone(), vec![id1.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[ + (id0.clone(), vec![id0.clone()]), + (id2.clone(), vec![id2.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id2} routing table"); + pm2.wait_for_routing_table(&[ + (id0.clone(), vec![id1.clone()]), + (id1.clone(), vec![id1.clone()]), + ]) + .await; +} + +// test routing for three nodes in a line, then test routing after completing the triangle +#[tokio::test] +async fn three_nodes_star() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "connect three nodes in a line"); + let pm0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm2 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + pm1.connect_to(&pm2.peer_info(), tcp::Tier::T2).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + let id2 = pm2.cfg.node_id(); + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[ + (id1.clone(), vec![id1.clone()]), + (id2.clone(), vec![id1.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[ + (id0.clone(), vec![id0.clone()]), + (id2.clone(), vec![id2.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id2} routing table"); + pm2.wait_for_routing_table(&[ + (id0.clone(), vec![id1.clone()]), + (id1.clone(), vec![id1.clone()]), + ]) + .await; + + tracing::info!(target:"test", "connect {id0} and {id2}"); + pm0.connect_to(&pm2.peer_info(), tcp::Tier::T2).await; + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[ + (id1.clone(), vec![id1.clone()]), + (id2.clone(), vec![id2.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[ + (id0.clone(), vec![id0.clone()]), + (id2.clone(), vec![id2.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id2} routing table"); + pm2.wait_for_routing_table(&[ + (id0.clone(), vec![id0.clone()]), + (id1.clone(), vec![id1.clone()]), + ]) + .await; +} + +// test routing in a network with two connected components having two nodes each, +// then test routing after joining them into a square +#[tokio::test] +async fn join_components() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "create two connected components having two nodes each"); + let pm0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm2 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm3 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + let id2 = pm2.cfg.node_id(); + let id3 = pm3.cfg.node_id(); + + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + pm2.connect_to(&pm3.peer_info(), tcp::Tier::T2).await; + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[(id1.clone(), vec![id1.clone()])]).await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[(id0.clone(), vec![id0.clone()])]).await; + + tracing::info!(target:"test", "wait for {id2} routing table"); + pm2.wait_for_routing_table(&[(id3.clone(), vec![id3.clone()])]).await; + tracing::info!(target:"test", "wait for {id3} routing table"); + pm3.wait_for_routing_table(&[(id2.clone(), vec![id2.clone()])]).await; + + tracing::info!(target:"test", "join the two components into a square"); + pm0.connect_to(&pm2.peer_info(), tcp::Tier::T2).await; + pm3.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[ + (id1.clone(), vec![id1.clone()]), + (id2.clone(), vec![id2.clone()]), + (id3.clone(), vec![id1.clone(), id2.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[ + (id0.clone(), vec![id0.clone()]), + (id2.clone(), vec![id0.clone(), id3.clone()]), + (id3.clone(), vec![id3.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id2} routing table"); + pm2.wait_for_routing_table(&[ + (id0.clone(), vec![id0.clone()]), + (id1.clone(), vec![id0.clone(), id3.clone()]), + (id3.clone(), vec![id3.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id3} routing table"); + pm3.wait_for_routing_table(&[ + (id0.clone(), vec![id1.clone(), id2.clone()]), + (id1.clone(), vec![id1.clone()]), + (id2.clone(), vec![id2.clone()]), + ]) + .await; + drop(pm0); + drop(pm1); + drop(pm2); + drop(pm3); +} + +// test routing for three nodes in a line, then test dropping the middle node +#[tokio::test] +async fn simple_remove() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "connect 3 nodes in a line"); + let pm0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm2 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + pm1.connect_to(&pm2.peer_info(), tcp::Tier::T2).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + let id2 = pm2.cfg.node_id(); + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[ + (id1.clone(), vec![id1.clone()]), + (id2.clone(), vec![id1.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[ + (id0.clone(), vec![id0.clone()]), + (id2.clone(), vec![id2.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id2} routing table"); + pm2.wait_for_routing_table(&[ + (id0.clone(), vec![id1.clone()]), + (id1.clone(), vec![id1.clone()]), + ]) + .await; + + tracing::info!(target:"test","stop {id1}"); + drop(pm1); + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[]).await; + tracing::info!(target:"test", "wait for {id2} routing table"); + pm2.wait_for_routing_table(&[]).await; +} + +// Awaits until the expected ping is seen in the event stream. +pub async fn wait_for_ping(events: &mut broadcast::Receiver, want_ping: Ping) { + events + .recv_until(|ev| match ev { + Event::PeerManager(PME::Ping(ping)) => { + if ping == want_ping { + Some(()) + } else { + None + } + } + _ => None, + }) + .await; +} + +// Awaits until the expected pong is seen in the event stream. +pub async fn wait_for_pong(events: &mut broadcast::Receiver, want_pong: Pong) { + events + .recv_until(|ev| match ev { + Event::PeerManager(PME::Pong(pong)) => { + if pong == want_pong { + Some(()) + } else { + None + } + } + _ => None, + }) + .await; +} + +// Awaits until RoutedMessageDropped is seen in the event stream. +pub async fn wait_for_message_dropped(events: &mut broadcast::Receiver) { + events + .recv_until(|ev| match ev { + Event::PeerManager(PME::RoutedMessageDropped) => Some(()), + _ => None, + }) + .await; +} + +// test ping in a two-node network +#[tokio::test] +async fn ping_simple() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "start two nodes"); + let pm0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[(id1.clone(), vec![id1.clone()])]).await; + + // capture event streams before pinging + let mut pm0_ev = pm0.events.from_now(); + let mut pm1_ev = pm1.events.from_now(); + + tracing::info!(target:"test", "send ping from {id0} to {id1}"); + pm0.send_ping(&clock.clock(), 0, id1.clone()).await; + + tracing::info!(target:"test", "await ping at {id1}"); + wait_for_ping(&mut pm1_ev, Ping { nonce: 0, source: id0.clone() }).await; + + tracing::info!(target:"test", "await pong at {id0}"); + wait_for_pong(&mut pm0_ev, Pong { nonce: 0, source: id1.clone() }).await; + + drop(pm0); + drop(pm1); +} + +// test ping without a direct connection +#[tokio::test] +async fn ping_jump() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "start three nodes"); + let pm0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm2 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + let id2 = pm2.cfg.node_id(); + + tracing::info!(target:"test", "connect nodes in a line"); + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + pm1.connect_to(&pm2.peer_info(), tcp::Tier::T2).await; + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[ + (id1.clone(), vec![id1.clone()]), + (id2.clone(), vec![id1.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[ + (id0.clone(), vec![id0.clone()]), + (id2.clone(), vec![id2.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id2} routing table"); + pm2.wait_for_routing_table(&[ + (id0.clone(), vec![id1.clone()]), + (id1.clone(), vec![id1.clone()]), + ]) + .await; + + // capture event streams before pinging + let mut pm0_ev = pm0.events.from_now(); + let mut pm2_ev = pm2.events.from_now(); + + tracing::info!(target:"test", "send ping from {id0} to {id2}"); + pm0.send_ping(&clock.clock(), 0, id2.clone()).await; + + tracing::info!(target:"test", "await ping at {id2}"); + wait_for_ping(&mut pm2_ev, Ping { nonce: 0, source: id0.clone() }).await; + + tracing::info!(target:"test", "await pong at {id0}"); + wait_for_pong(&mut pm0_ev, Pong { nonce: 0, source: id2.clone() }).await; +} + +// test that ping over an indirect connection with ttl=2 is delivered +#[tokio::test] +async fn test_dont_drop_after_ttl() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "start three nodes"); + let mut cfg0 = chain.make_config(rng); + cfg0.routed_message_ttl = 2; + let pm0 = start_pm(clock.clock(), TestDB::new(), cfg0, chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm2 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + let id2 = pm2.cfg.node_id(); + + tracing::info!(target:"test", "connect nodes in a line"); + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + pm1.connect_to(&pm2.peer_info(), tcp::Tier::T2).await; + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[ + (id1.clone(), vec![id1.clone()]), + (id2.clone(), vec![id1.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[ + (id0.clone(), vec![id0.clone()]), + (id2.clone(), vec![id2.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id2} routing table"); + pm2.wait_for_routing_table(&[ + (id0.clone(), vec![id1.clone()]), + (id1.clone(), vec![id1.clone()]), + ]) + .await; + + // capture event streams before pinging + let mut pm0_ev = pm0.events.from_now(); + let mut pm2_ev = pm2.events.from_now(); + + tracing::info!(target:"test", "send ping from {id0} to {id2}"); + pm0.send_ping(&clock.clock(), 0, id2.clone()).await; + + tracing::info!(target:"test", "await ping at {id2}"); + wait_for_ping(&mut pm2_ev, Ping { nonce: 0, source: id0.clone() }).await; + + tracing::info!(target:"test", "await pong at {id0}"); + wait_for_pong(&mut pm0_ev, Pong { nonce: 0, source: id2.clone() }).await; +} + +// test that ping over an indirect connection with ttl=1 is dropped +#[tokio::test] +async fn test_drop_after_ttl() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "start three nodes"); + let mut cfg0 = chain.make_config(rng); + cfg0.routed_message_ttl = 1; + let pm0 = start_pm(clock.clock(), TestDB::new(), cfg0, chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm2 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + let id2 = pm2.cfg.node_id(); + + tracing::info!(target:"test", "connect nodes in a line"); + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + pm1.connect_to(&pm2.peer_info(), tcp::Tier::T2).await; + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[ + (id1.clone(), vec![id1.clone()]), + (id2.clone(), vec![id1.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[ + (id0.clone(), vec![id0.clone()]), + (id2.clone(), vec![id2.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id2} routing table"); + pm2.wait_for_routing_table(&[ + (id0.clone(), vec![id1.clone()]), + (id1.clone(), vec![id1.clone()]), + ]) + .await; + + // capture event stream before pinging + let mut pm1_ev = pm1.events.from_now(); + + tracing::info!(target:"test", "send ping from {id0} to {id2}"); + pm0.send_ping(&clock.clock(), 0, id2.clone()).await; + + tracing::info!(target:"test", "await message dropped at {id1}"); + wait_for_message_dropped(&mut pm1_ev).await; +} + +// test dropping behavior for duplicate messages +#[tokio::test] +async fn test_dropping_duplicate_messages() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "start three nodes"); + let pm0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm2 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + let id2 = pm2.cfg.node_id(); + + tracing::info!(target:"test", "connect nodes in a line"); + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + pm1.connect_to(&pm2.peer_info(), tcp::Tier::T2).await; + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[ + (id1.clone(), vec![id1.clone()]), + (id2.clone(), vec![id1.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[ + (id0.clone(), vec![id0.clone()]), + (id2.clone(), vec![id2.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id2} routing table"); + pm2.wait_for_routing_table(&[ + (id0.clone(), vec![id1.clone()]), + (id1.clone(), vec![id1.clone()]), + ]) + .await; + + // capture event streams before pinging + let mut pm0_ev = pm0.events.from_now(); + let mut pm1_ev = pm1.events.from_now(); + let mut pm2_ev = pm2.events.from_now(); + + // Send two identical messages. One will be dropped, because the delay between them was less than 50ms. + tracing::info!(target:"test", "send ping from {id0} to {id2}"); + pm0.send_ping(&clock.clock(), 0, id2.clone()).await; + tracing::info!(target:"test", "await ping at {id2}"); + wait_for_ping(&mut pm2_ev, Ping { nonce: 0, source: id0.clone() }).await; + tracing::info!(target:"test", "await pong at {id0}"); + wait_for_pong(&mut pm0_ev, Pong { nonce: 0, source: id2.clone() }).await; + + tracing::info!(target:"test", "send ping from {id0} to {id2}"); + pm0.send_ping(&clock.clock(), 0, id2.clone()).await; + tracing::info!(target:"test", "await message dropped at {id1}"); + wait_for_message_dropped(&mut pm1_ev).await; + + // Send two identical messages but with 300ms delay so they don't get dropped. + tracing::info!(target:"test", "send ping from {id0} to {id2}"); + pm0.send_ping(&clock.clock(), 1, id2.clone()).await; + tracing::info!(target:"test", "await ping at {id2}"); + wait_for_ping(&mut pm2_ev, Ping { nonce: 1, source: id0.clone() }).await; + tracing::info!(target:"test", "await pong at {id0}"); + wait_for_pong(&mut pm0_ev, Pong { nonce: 1, source: id2.clone() }).await; + + clock.advance(DROP_DUPLICATED_MESSAGES_PERIOD + time::Duration::milliseconds(1)); + + tracing::info!(target:"test", "send ping from {id0} to {id2}"); + pm0.send_ping(&clock.clock(), 1, id2.clone()).await; + tracing::info!(target:"test", "await ping at {id2}"); + wait_for_ping(&mut pm2_ev, Ping { nonce: 1, source: id0.clone() }).await; + tracing::info!(target:"test", "await pong at {id0}"); + wait_for_pong(&mut pm0_ev, Pong { nonce: 1, source: id2.clone() }).await; +} + +/// Awaits until a ConnectionClosed event with the expected reason is seen in the event stream. +/// This helper function should be used in tests with peer manager instances with +/// `config.outbound_enabled = true`, because it makes the order of spawning connections +/// non-deterministic, so we cannot just wait for the first ConnectionClosed event. +pub(crate) async fn wait_for_connection_closed( + events: &mut broadcast::Receiver, + want_reason: ClosingReason, +) { + events + .recv_until(|ev| match ev { + Event::PeerManager(PME::ConnectionClosed(ConnectionClosedEvent { + stream_id: _, + reason, + })) => { + if reason == want_reason { + Some(()) + } else { + None + } + } + _ => None, + }) + .await; +} + +// Constructs NetworkConfigs for num_nodes nodes, the first num_boot_nodes of which +// are configured as boot nodes for all nodes. +fn make_configs( + chain: &data::Chain, + rng: &mut Rng, + num_nodes: usize, + num_boot_nodes: usize, + enable_outbound: bool, +) -> Vec { + let mut cfgs: Vec<_> = (0..num_nodes).map(|_i| chain.make_config(rng)).collect(); + let boot_nodes: Vec<_> = cfgs[0..num_boot_nodes] + .iter() + .map(|c| PeerInfo { + id: c.node_id(), + addr: c.node_addr.as_ref().map(|a| **a), + account_id: None, + }) + .collect(); + for config in cfgs.iter_mut() { + config.outbound_disabled = !enable_outbound; + config.peer_store.boot_nodes = boot_nodes.clone(); + } + cfgs +} + +// test bootstrapping a two-node network with one boot node +#[tokio::test] +async fn from_boot_nodes() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "start two nodes"); + let cfgs = make_configs(&chain, rng, 2, 1, true); + let pm0 = start_pm(clock.clock(), TestDB::new(), cfgs[0].clone(), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), cfgs[1].clone(), chain.clone()).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[(id1.clone(), vec![id1.clone()])]).await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[(id0.clone(), vec![id0.clone()])]).await; +} + +// test node 0 blacklisting node 1 +#[tokio::test] +async fn blacklist_01() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "start two nodes with 0 blacklisting 1"); + let mut cfgs = make_configs(&chain, rng, 2, 2, true); + cfgs[0].peer_store.blacklist = + [blacklist::Entry::from_addr(**cfgs[1].node_addr.as_ref().unwrap())].into_iter().collect(); + + let pm0 = start_pm(clock.clock(), TestDB::new(), cfgs[0].clone(), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), cfgs[1].clone(), chain.clone()).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + + tracing::info!(target:"test", "wait for the connection to be attempted and rejected"); + wait_for_connection_closed( + &mut pm0.events.clone(), + ClosingReason::RejectedByPeerManager(RegisterPeerError::Blacklisted), + ) + .await; + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[]).await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[]).await; +} + +// test node 1 blacklisting node 0 +#[tokio::test] +async fn blacklist_10() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "start two nodes with 1 blacklisting 0"); + let mut cfgs = make_configs(&chain, rng, 2, 2, true); + cfgs[1].peer_store.blacklist = + [blacklist::Entry::from_addr(**cfgs[0].node_addr.as_ref().unwrap())].into_iter().collect(); + + let pm0 = start_pm(clock.clock(), TestDB::new(), cfgs[0].clone(), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), cfgs[1].clone(), chain.clone()).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + + tracing::info!(target:"test", "wait for the connection to be attempted and rejected"); + wait_for_connection_closed( + &mut pm1.events.clone(), + ClosingReason::RejectedByPeerManager(RegisterPeerError::Blacklisted), + ) + .await; + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[]).await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[]).await; +} + +// test node 0 blacklisting all nodes +#[tokio::test] +async fn blacklist_all() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "start two nodes with 0 blacklisting everything"); + let mut cfgs = make_configs(&chain, rng, 2, 2, true); + cfgs[0].peer_store.blacklist = + [blacklist::Entry::from_ip(Ipv6Addr::LOCALHOST.into())].into_iter().collect(); + + let pm0 = start_pm(clock.clock(), TestDB::new(), cfgs[0].clone(), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), cfgs[1].clone(), chain.clone()).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + + tracing::info!(target:"test", "wait for the connection to be attempted and rejected"); + wait_for_connection_closed( + &mut pm0.events.clone(), + ClosingReason::RejectedByPeerManager(RegisterPeerError::Blacklisted), + ) + .await; + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[]).await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[]).await; +} + +// Spawn 3 nodes with max peers configured to 2, then allow them to connect to each other in a triangle. +// Spawn a fourth node and see it fail to connect since the first three are at max capacity. +#[tokio::test] +async fn max_num_peers_limit() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "start three nodes with max_num_peers=2"); + let mut cfgs = make_configs(&chain, rng, 4, 4, false); + for config in cfgs.iter_mut() { + config.max_num_peers = 2; + config.ideal_connections_lo = 2; + config.ideal_connections_hi = 2; + } + + let pm0 = start_pm(clock.clock(), TestDB::new(), cfgs[0].clone(), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), cfgs[1].clone(), chain.clone()).await; + let pm2 = start_pm(clock.clock(), TestDB::new(), cfgs[2].clone(), chain.clone()).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + let id2 = pm2.cfg.node_id(); + + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + pm0.connect_to(&pm2.peer_info(), tcp::Tier::T2).await; + pm1.connect_to(&pm2.peer_info(), tcp::Tier::T2).await; + + tracing::info!(target:"test", "wait for {id0} routing table"); + pm0.wait_for_routing_table(&[ + (id1.clone(), vec![id1.clone()]), + (id2.clone(), vec![id2.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id1} routing table"); + pm1.wait_for_routing_table(&[ + (id0.clone(), vec![id0.clone()]), + (id2.clone(), vec![id2.clone()]), + ]) + .await; + tracing::info!(target:"test", "wait for {id2} routing table"); + pm2.wait_for_routing_table(&[ + (id0.clone(), vec![id0.clone()]), + (id1.clone(), vec![id1.clone()]), + ]) + .await; + + let mut pm0_ev = pm0.events.from_now(); + let mut pm1_ev = pm1.events.from_now(); + let mut pm2_ev = pm2.events.from_now(); + + tracing::info!(target:"test", "start a fourth node"); + let pm3 = start_pm(clock.clock(), TestDB::new(), cfgs[3].clone(), chain.clone()).await; + + let id3 = pm3.cfg.node_id(); + + tracing::info!(target:"test", "wait for {id0} to reject attempted connection"); + pm3.send_outbound_connect(&pm0.peer_info(), tcp::Tier::T2).await; + wait_for_connection_closed( + &mut pm0_ev, + ClosingReason::RejectedByPeerManager(RegisterPeerError::ConnectionLimitExceeded), + ) + .await; + tracing::info!(target:"test", "wait for {id1} to reject attempted connection"); + pm3.send_outbound_connect(&pm1.peer_info(), tcp::Tier::T2).await; + wait_for_connection_closed( + &mut pm1_ev, + ClosingReason::RejectedByPeerManager(RegisterPeerError::ConnectionLimitExceeded), + ) + .await; + tracing::info!(target:"test", "wait for {id2} to reject attempted connection"); + pm3.send_outbound_connect(&pm2.peer_info(), tcp::Tier::T2).await; + wait_for_connection_closed( + &mut pm2_ev, + ClosingReason::RejectedByPeerManager(RegisterPeerError::ConnectionLimitExceeded), + ) + .await; + + tracing::info!(target:"test", "wait for {id3} routing table"); + pm3.wait_for_routing_table(&[]).await; + + // These drop() calls fix the place at which we want the values to be dropped, + // so that they live long enough for the test to succeed. + // AFAIU (gprusak) NLL (https://blog.rust-lang.org/2022/08/05/nll-by-default.html) + // this is NOT strictly necessary, as the destructors of these values should + // be called exactly at the end of the function anyway + // (unless the problem is even deeper: for example the compiler incorrectly + // figures out that it can reorder some instructions). However, I add those every time + // I observe some flakiness that I can't reproduce, just to eliminate the possibility that + // I just don't understand how NLL works. + // + // The proper solution would be to: + // 1. Make framework presubmit run tests with "[panic=abort]" (which is not supported with + // "cargo test"), so that the test actually stop if some thread panic. + // 2. Set some timeout on the test execution (with an enourmous heardoom), so that we actually get some logs + // if the presubmit times out (also not supported by "cargo test"). + drop(pm0); + drop(pm1); + drop(pm2); + drop(pm3); +} + +/// Test that TTL is handled properly. +#[tokio::test] +async fn ttl() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + let mut pm = peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + let cfg = peer::testonly::PeerConfig { + network: chain.make_config(rng), + chain, + force_encoding: Some(Encoding::Proto), + }; + let stream = tcp::Stream::connect(&pm.peer_info(), tcp::Tier::T2).await.unwrap(); + let mut peer = peer::testonly::PeerHandle::start_endpoint(clock.clock(), cfg, stream).await; + peer.complete_handshake().await; + pm.wait_for_routing_table(&[(peer.cfg.id(), vec![peer.cfg.id()])]).await; + + for ttl in 0..5 { + let msg = RoutedMessageBody::Ping(Ping { nonce: rng.gen(), source: peer.cfg.id() }); + let msg = Box::new(peer.routed_message(msg, peer.cfg.id(), ttl, Some(clock.now_utc()))); + peer.send(PeerMessage::Routed(msg.clone())).await; + // If TTL is <2, then the message will be dropped (at least 2 hops are required). + if ttl < 2 { + pm.events + .recv_until(|ev| match ev { + Event::PeerManager(PME::RoutedMessageDropped) => Some(()), + _ => None, + }) + .await; + } else { + let got = peer + .events + .recv_until(|ev| match ev { + peer::testonly::Event::Network(PME::MessageProcessed( + tcp::Tier::T2, + PeerMessage::Routed(msg), + )) => Some(msg), + _ => None, + }) + .await; + assert_eq!(msg.body, got.body); + assert_eq!(msg.ttl - 1, got.ttl); + } + } +} + +/// After the initial exchange, all subsequent SyncRoutingTable messages are +/// expected to contain only the diff of the known data. +#[tokio::test] +async fn repeated_data_in_sync_routing_table() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + let pm = peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + let cfg = peer::testonly::PeerConfig { + network: chain.make_config(rng), + chain, + force_encoding: Some(Encoding::Proto), + }; + let stream = tcp::Stream::connect(&pm.peer_info(), tcp::Tier::T2).await.unwrap(); + let mut peer = peer::testonly::PeerHandle::start_endpoint(clock.clock(), cfg, stream).await; + peer.complete_handshake().await; + + let mut edges_got = HashSet::new(); + let mut edges_want = HashSet::new(); + let mut accounts_got = HashSet::new(); + let mut accounts_want = HashSet::new(); + edges_want.insert(peer.edge.clone().unwrap()); + + // Gradually increment the amount of data in the system and then broadcast it. + for i in 0..10 { + tracing::info!(target: "test", "iteration {i}"); + // Wait for the new data to be broadcasted. + // Note that in the first iteration we expect just 1 edge, without sending anything before. + // It is important because the first SyncRoutingTable contains snapshot of all data known to + // the node (not just the diff), so we expect incremental behavior only after the first + // SyncRoutingTable. + while edges_got != edges_want || accounts_got != accounts_want { + match peer.events.recv().await { + peer::testonly::Event::Network(PME::MessageProcessed( + tcp::Tier::T2, + PeerMessage::SyncRoutingTable(got), + )) => { + for a in got.accounts { + assert!(!accounts_got.contains(&a), "repeated broadcast: {a:?}"); + assert!(accounts_want.contains(&a), "unexpected broadcast: {a:?}"); + accounts_got.insert(a); + } + for e in got.edges { + // TODO(gprusak): Currently there is a race condition between + // initial full sync and broadcasting the new connection edge, + // which may cause the new connection edge to be broadcasted twice. + // Synchronize those 2 events, so that behavior here is deterministic. + if &e != peer.edge.as_ref().unwrap() { + assert!(!edges_got.contains(&e), "repeated broadcast: {e:?}"); + } + assert!(edges_want.contains(&e), "unexpected broadcast: {e:?}"); + edges_got.insert(e); + } + } + // Ignore other messages. + _ => {} + } + } + // Add more data. + let key = data::make_secret_key(rng); + edges_want.insert(data::make_edge(&peer.cfg.network.node_key, &key, 1)); + accounts_want.insert(data::make_announce_account(rng)); + // Send all the data created so far. PeerManager is expected to discard the duplicates. + peer.send(PeerMessage::SyncRoutingTable(RoutingTableUpdate { + edges: edges_want.iter().cloned().collect(), + accounts: accounts_want.iter().cloned().collect(), + })) + .await; + } +} + +/// Awaits for SyncRoutingTable messages until all edges from `want` arrive. +/// Panics if any other edges arrive. +async fn wait_for_edges( + mut events: broadcast::Receiver, + want: &HashSet, +) { + let mut got = HashSet::new(); + tracing::info!(target: "test", "want edges: {:?}",want.iter().map(|e|e.hash()).collect::>()); + while &got != want { + match events.recv().await { + peer::testonly::Event::Network(PME::MessageProcessed( + tcp::Tier::T2, + PeerMessage::SyncRoutingTable(msg), + )) => { + tracing::info!(target: "test", "got edges: {:?}",msg.edges.iter().map(|e|e.hash()).collect::>()); + got.extend(msg.edges); + assert!(want.is_superset(&got), "want: {:#?}, got: {:#?}", want, got); + } + // Ignore other messages. + _ => {} + } + } +} + +// After each handshake a full sync of routing table is performed with the peer. +// After a restart, some edges reside in storage. The node shouldn't broadcast +// edges which it learned about before the restart. +#[tokio::test] +async fn no_edge_broadcast_after_restart() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let make_edges = |rng: &mut Rng| { + vec![ + data::make_edge(&data::make_secret_key(rng), &data::make_secret_key(rng), 1), + data::make_edge(&data::make_secret_key(rng), &data::make_secret_key(rng), 1), + data::make_edge_tombstone(&data::make_secret_key(rng), &data::make_secret_key(rng)), + ] + }; + + // Create a bunch of fresh unreachable edges, then send all the edges created so far. + let stored_edges = make_edges(rng); + + // We are preparing the initial storage by hand (rather than simulating the restart), + // because semantics of the RoutingTable protocol are very poorly defined, and it + // is hard to write a solid test for it without literally assuming the implementation details. + let store = unc_store::db::TestDB::new(); + { + let mut stored_peers = HashSet::new(); + for e in &stored_edges { + stored_peers.insert(e.key().0.clone()); + stored_peers.insert(e.key().1.clone()); + } + let mut store: store::Store = store.clone().into(); + store.push_component(&stored_peers, &stored_edges).unwrap(); + } + + // Start a PeerManager and connect a peer to it. + let pm = + peer_manager::testonly::start(clock.clock(), store, chain.make_config(rng), chain.clone()) + .await; + let peer = pm + .start_inbound(chain.clone(), chain.make_config(rng)) + .await + .handshake(&clock.clock()) + .await; + tracing::info!(target:"test","pm = {}",pm.cfg.node_id()); + tracing::info!(target:"test","peer = {}",peer.cfg.id()); + // Wait for the initial sync, which will contain just 1 edge. + // Only incremental sync are guaranteed to not contain already known edges. + wait_for_edges(peer.events.clone(), &[peer.edge.clone().unwrap()].into()).await; + + let fresh_edges = make_edges(rng); + let mut total_edges = stored_edges.clone(); + total_edges.extend(fresh_edges.iter().cloned()); + let events = peer.events.from_now(); + peer.send(PeerMessage::SyncRoutingTable(RoutingTableUpdate { + edges: total_edges, + accounts: vec![], + })) + .await; + + // Wait for the fresh edges to be broadcasted back. + tracing::info!(target: "test", "wait_for_edges()"); + wait_for_edges(events, &fresh_edges.into_iter().collect()).await; +} + +#[tokio::test] +async fn square() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "connect 4 nodes in a square"); + let pm0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm2 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm3 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + pm1.connect_to(&pm2.peer_info(), tcp::Tier::T2).await; + pm2.connect_to(&pm3.peer_info(), tcp::Tier::T2).await; + pm3.connect_to(&pm0.peer_info(), tcp::Tier::T2).await; + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + let id2 = pm2.cfg.node_id(); + let id3 = pm3.cfg.node_id(); + + pm0.wait_for_routing_table(&[ + (id1.clone(), vec![id1.clone()]), + (id3.clone(), vec![id3.clone()]), + (id2.clone(), vec![id1.clone(), id3.clone()]), + ]) + .await; + tracing::info!(target:"test","stop {id1}"); + drop(pm1); + tracing::info!(target:"test","wait for {id0} routing table"); + pm0.wait_for_routing_table(&[ + (id3.clone(), vec![id3.clone()]), + (id2.clone(), vec![id3.clone()]), + ]) + .await; + tracing::info!(target:"test","wait for {id2} routing table"); + pm2.wait_for_routing_table(&[ + (id3.clone(), vec![id3.clone()]), + (id0.clone(), vec![id3.clone()]), + ]) + .await; + tracing::info!(target:"test","wait for {id3} routing table"); + pm3.wait_for_routing_table(&[ + (id2.clone(), vec![id2.clone()]), + (id0.clone(), vec![id0.clone()]), + ]) + .await; + drop(pm0); + drop(pm2); + drop(pm3); +} + +#[tokio::test] +async fn fix_local_edges() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let pm = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let conn = pm + .start_inbound(chain.clone(), chain.make_config(rng)) + .await + .handshake(&clock.clock()) + .await; + // TODO(gprusak): the case when the edge is updated via UpdateNondeRequest is not covered yet, + // as it requires awaiting for the RPC roundtrip which is not implemented yet. + let edge1 = data::make_edge(&pm.cfg.node_key, &data::make_secret_key(rng), 1); + let edge2 = conn + .edge + .as_ref() + .unwrap() + .remove_edge(conn.cfg.network.node_id(), &conn.cfg.network.node_key); + let msg = PeerMessage::SyncRoutingTable(RoutingTableUpdate::from_edges(vec![ + edge1.clone(), + edge2.clone(), + ])); + + tracing::info!(target:"test", "waiting for fake edges to be processed"); + let mut events = pm.events.from_now(); + conn.send(msg.clone()).await; + events + .recv_until(|ev| match ev { + Event::PeerManager(PME::MessageProcessed(tcp::Tier::T2, got)) if got == msg => Some(()), + _ => None, + }) + .await; + + tracing::info!(target:"test","waiting for fake edges to be fixed"); + let mut events = pm.events.from_now(); + pm.fix_local_edges(&clock.clock(), time::Duration::ZERO).await; + // TODO(gprusak): make fix_local_edges await closing of the connections, so + // that we don't have to wait for it explicitly here. + events + .recv_until(|ev| match ev { + Event::PeerManager(PME::ConnectionClosed { .. }) => Some(()), + _ => None, + }) + .await; + + tracing::info!(target:"test","checking the consistency"); + pm.check_consistency().await; + drop(conn); +} + +#[tokio::test] +async fn do_not_block_announce_account_broadcast() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let db0 = TestDB::new(); + let db1 = TestDB::new(); + let aa = data::make_announce_account(rng); + + tracing::info!(target:"test", "spawn 2 nodes and announce the account."); + let pm0 = start_pm(clock.clock(), db0.clone(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), db1.clone(), chain.make_config(rng), chain.clone()).await; + pm1.connect_to(&pm0.peer_info(), tcp::Tier::T2).await; + pm1.announce_account(aa.clone()).await; + assert_eq!(&aa.peer_id, &pm0.wait_for_account_owner(&aa.account_id).await); + drop(pm0); + drop(pm1); + + tracing::info!(target:"test", "spawn 3 nodes and re-announce the account."); + // Even though the account was previously announced (pm0 and pm1 have it in DB), + // the nodes should allow to let the broadcast through. + let pm0 = start_pm(clock.clock(), db0, chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), db1, chain.make_config(rng), chain.clone()).await; + let pm2 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + pm1.connect_to(&pm0.peer_info(), tcp::Tier::T2).await; + pm2.connect_to(&pm0.peer_info(), tcp::Tier::T2).await; + pm1.announce_account(aa.clone()).await; + assert_eq!(&aa.peer_id, &pm2.wait_for_account_owner(&aa.account_id).await); +} + +/// Check that two archival nodes keep connected after network rebalance. Nodes 0 and 1 are archival nodes, others aren't. +/// Initially connect 2, 3, 4 to 0. Then connect 1 to 0, this connection should persist, even after other nodes tries +/// to connect to node 0 again. +/// +/// Do four rounds where 2, 3, 4 tries to connect to 0 and check that connection between 0 and 1 was never dropped. +#[tokio::test] +async fn archival_node() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let mut cfgs = make_configs(&chain, rng, 5, 5, false); + for config in cfgs.iter_mut() { + config.max_num_peers = 3; + config.ideal_connections_lo = 2; + config.ideal_connections_hi = 2; + config.safe_set_size = 1; + config.minimum_outbound_peers = 0; + } + cfgs[0].archive = true; + cfgs[1].archive = true; + + tracing::info!(target:"test", "start five nodes, the first two of which are archival nodes"); + let pm0 = start_pm(clock.clock(), TestDB::new(), cfgs[0].clone(), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), cfgs[1].clone(), chain.clone()).await; + let pm2 = start_pm(clock.clock(), TestDB::new(), cfgs[2].clone(), chain.clone()).await; + let pm3 = start_pm(clock.clock(), TestDB::new(), cfgs[3].clone(), chain.clone()).await; + let pm4 = start_pm(clock.clock(), TestDB::new(), cfgs[4].clone(), chain.clone()).await; + + let id1 = pm1.cfg.node_id(); + + // capture pm0 event stream + let mut pm0_ev = pm0.events.from_now(); + + tracing::info!(target:"test", "connect node 2 to node 0"); + pm2.send_outbound_connect(&pm0.peer_info(), tcp::Tier::T2).await; + tracing::info!(target:"test", "connect node 3 to node 0"); + pm3.send_outbound_connect(&pm0.peer_info(), tcp::Tier::T2).await; + + tracing::info!(target:"test", "connect node 4 to node 0 and wait for pm0 to close a connection"); + pm4.send_outbound_connect(&pm0.peer_info(), tcp::Tier::T2).await; + wait_for_connection_closed(&mut pm0_ev, ClosingReason::PeerManagerRequest).await; + + tracing::info!(target:"test", "connect node 1 to node 0 and wait for pm0 to close a connection"); + pm1.send_outbound_connect(&pm0.peer_info(), tcp::Tier::T2).await; + wait_for_connection_closed(&mut pm0_ev, ClosingReason::PeerManagerRequest).await; + + tracing::info!(target:"test", "check that node 0 and node 1 are still connected"); + pm0.wait_for_direct_connection(id1.clone()).await; + + for _step in 0..10 { + tracing::info!(target:"test", "[{_step}] select a node which node 0 is not connected to"); + let pm0_connections: HashSet = + pm0.with_state(|s| async move { s.tier2.load().ready.keys().cloned().collect() }).await; + + let pms = [&pm2, &pm3, &pm4]; + let chosen = pms + .iter() + .filter(|&pm| !pm0_connections.contains(&pm.cfg.node_id())) + .choose(rng) + .unwrap(); + + tracing::info!(target:"test", "[{_step}] wait for the chosen node to finish disconnecting from node 0"); + chosen.wait_for_num_connected_peers(0).await; + + tracing::info!(target:"test", "[{_step}] connect the chosen node to node 0 and wait for pm0 to close a connection"); + chosen.send_outbound_connect(&pm0.peer_info(), tcp::Tier::T2).await; + wait_for_connection_closed(&mut pm0_ev, ClosingReason::PeerManagerRequest).await; + + tracing::info!(target:"test", "[{_step}] check that node 0 and node 1 are still connected"); + pm0.wait_for_direct_connection(id1.clone()).await; + } + + drop(pm0); + drop(pm1); + drop(pm2); + drop(pm3); + drop(pm4); +} + +/// Awaits for ConnectionClosed event for a given `stream_id`. +async fn wait_for_stream_closed( + events: &mut broadcast::Receiver, + stream_id: tcp::StreamId, +) -> ClosingReason { + events + .recv_until(|ev| match ev { + Event::PeerManager(PME::ConnectionClosed(ev)) if ev.stream_id == stream_id => { + Some(ev.reason) + } + _ => None, + }) + .await +} + +/// Check two peers are able to connect again after one peers is banned and unbanned. +#[tokio::test] +async fn connect_to_unbanned_peer() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let mut pm0 = + start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let mut pm1 = + start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + tracing::info!(target:"test", "pm0 connects to pm1"); + let stream_id = pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + + tracing::info!(target:"test", "pm1 bans pm0"); + let ban_reason = ReasonForBan::BadBlock; + pm1.disconnect_and_ban(&clock.clock(), &pm0.cfg.node_id(), ban_reason).await; + wait_for_stream_closed(&mut pm0.events, stream_id).await; + assert_eq!( + ClosingReason::Ban(ban_reason), + wait_for_stream_closed(&mut pm1.events, stream_id).await + ); + + tracing::info!(target:"test", "pm0 fails to reconnect to pm1"); + let got_reason = pm1 + .start_inbound(chain.clone(), pm0.cfg.clone()) + .await + .manager_fail_handshake(&clock.clock()) + .await; + assert_eq!(ClosingReason::RejectedByPeerManager(RegisterPeerError::Banned), got_reason); + + tracing::info!(target:"test", "pm1 unbans pm0"); + clock.advance(pm1.cfg.peer_store.ban_window); + pm1.peer_store_update(&clock.clock()).await; + + tracing::info!(target:"test", "pm0 reconnects to pm1"); + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + + drop(pm0); + drop(pm1); +} + +/// Awaits a DistanceVector message from a given `peer_id` +async fn wait_for_distance_vector(events: &mut broadcast::Receiver, peer_id: PeerId) { + events + .recv_until(|ev| match ev { + Event::PeerManager(PME::MessageProcessed(_, msg)) => match msg { + PeerMessage::DistanceVector(dv) if dv.root == peer_id => Some(()), + _ => None, + }, + _ => None, + }) + .await +} + +/// Check that connecting to a peer triggers exchange of DistanceVectors +#[tokio::test] +async fn peer_connect_send_distance_vector() { + abort_on_panic(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "start two nodes"); + let pm0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + + // capture event streams before connecting + let mut pm0_ev = pm0.events.from_now(); + let mut pm1_ev = pm1.events.from_now(); + + tracing::info!(target:"test", "connect the nodes"); + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + + wait_for_distance_vector(&mut pm0_ev, id1).await; + wait_for_distance_vector(&mut pm1_ev, id0).await; +} diff --git a/chain/network/src/peer_manager/tests/snapshot_hosts.rs b/chain/network/src/peer_manager/tests/snapshot_hosts.rs new file mode 100644 index 000000000..772fb95ce --- /dev/null +++ b/chain/network/src/peer_manager/tests/snapshot_hosts.rs @@ -0,0 +1,454 @@ +use crate::network_protocol::SnapshotHostInfo; +use crate::network_protocol::SyncSnapshotHosts; +use crate::network_protocol::MAX_SHARDS_PER_SNAPSHOT_HOST_INFO; +use crate::peer; +use crate::peer_manager; +use crate::peer_manager::peer_manager_actor::Event as PME; +use crate::tcp; +use crate::testonly::{make_rng, AsSet as _}; +use crate::types::NetworkRequests; +use crate::types::PeerManagerMessageRequest; +use crate::types::PeerMessage; +use crate::{network_protocol::testonly as data, peer::testonly::PeerHandle}; +use unc_async::time; +use unc_crypto::SecretKey; +use unc_o11y::testonly::init_test_logger; +use unc_o11y::WithSpanContextExt; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::PeerId; +use unc_primitives::types::EpochHeight; +use unc_primitives::types::ShardId; +use peer_manager::testonly::FDS_PER_PEER; +use pretty_assertions::assert_eq; +use rand::seq::IteratorRandom; +use rand::Rng; +use std::collections::HashSet; +use std::sync::Arc; + +/// Create an instance of SnapshotHostInfo for testing purposes +fn make_snapshot_host_info( + peer_id: &PeerId, + secret_key: &SecretKey, + rng: &mut impl Rng, +) -> Arc { + let epoch_height: EpochHeight = rng.gen::(); + let max_shard_id: ShardId = 32; + let shards_num: usize = rng.gen_range(1..16); + let mut shards: Vec = (0..max_shard_id).choose_multiple(rng, shards_num); + shards.sort(); + let sync_hash = CryptoHash::hash_borsh(epoch_height); + Arc::new(SnapshotHostInfo::new(peer_id.clone(), sync_hash, epoch_height, shards, secret_key)) +} + +/// Used to consume peer events until there's an event of type SyncSnapshotHosts +fn take_sync_snapshot_msg(event: crate::peer::testonly::Event) -> Option { + match event { + peer::testonly::Event::Network(PME::MessageProcessed( + tcp::Tier::T2, + PeerMessage::SyncSnapshotHosts(msg), + )) => Some(msg), + _ => None, + } +} + +/// Receives events from the given PeerHandle until the desired target_info is found +/// Ignores infos defined in allowed_ignorable_infos. Any other unexpected info will trigger a panic. +async fn wait_for_host_info( + peer: &mut PeerHandle, + target_info: &SnapshotHostInfo, + allowed_ignorable_infos: &[Arc], +) { + loop { + let msg = peer.events.recv_until(take_sync_snapshot_msg).await; + for info in msg.hosts { + if info.as_ref() == target_info { + return; + } else if !allowed_ignorable_infos.contains(&info) { + panic!("wait_for_host_info: received unexpected SnapshotHostInfo: {:?}", info); + } + } + } +} + +/// Test that PeerManager broadcasts SnapshotHostInfo messages to all connected peers +#[tokio::test] +async fn broadcast() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + let clock = clock.clock(); + let clock = &clock; + + let pm = peer_manager::testonly::start( + clock.clone(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + + tracing::info!(target:"test", "Connect a peer, expect initial sync to be empty."); + let peer1_config = chain.make_config(rng); + let mut peer1 = + pm.start_inbound(chain.clone(), peer1_config.clone()).await.handshake(clock).await; + let empty_sync_msg = peer1.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(empty_sync_msg.hosts, vec![]); + + tracing::info!(target:"test", "Connect two more peers."); + let peer2_config = chain.make_config(rng); + let mut peer2 = + pm.start_inbound(chain.clone(), peer2_config.clone()).await.handshake(clock).await; + let empty_sync_msg = peer2.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(empty_sync_msg.hosts, vec![]); + + let mut peer3 = + pm.start_inbound(chain.clone(), chain.make_config(rng)).await.handshake(clock).await; + let empty_sync_msg = peer3.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(empty_sync_msg.hosts, vec![]); + + tracing::info!(target:"test", "Send a SyncSnapshotHosts message from peer1, make sure that all peers receive it."); + + let info1 = make_snapshot_host_info(&peer1_config.node_id(), &peer1_config.node_key, rng); + + peer1 + .send(PeerMessage::SyncSnapshotHosts(SyncSnapshotHosts { hosts: vec![info1.clone()] })) + .await; + + let got1 = peer1.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(got1.hosts, vec![info1.clone()]); + + let got2 = peer2.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(got2.hosts, vec![info1.clone()]); + + let got3 = peer3.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(got3.hosts, vec![info1.clone()]); + + tracing::info!(target:"test", "Connect another peer, make sure that it receives the correct information on joining."); + let mut peer4 = + pm.start_inbound(chain.clone(), chain.make_config(rng)).await.handshake(clock).await; + let peer4_sync_msg = peer4.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(peer4_sync_msg.hosts, vec![info1.clone()]); + + tracing::info!(target:"test", "Publish another piece of snapshot information, check that it's also broadcasted."); + let info2 = make_snapshot_host_info(&peer2_config.node_id(), &peer2_config.node_key, rng); + + peer2 + .send(PeerMessage::SyncSnapshotHosts(SyncSnapshotHosts { hosts: vec![info2.clone()] })) + .await; + + let got1 = peer1.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(got1.hosts, vec![info2.clone()]); + + tracing::info!(target:"test", "Connect another peer, check that it receieves all of the published information."); + let mut peer5 = + pm.start_inbound(chain.clone(), chain.make_config(rng)).await.handshake(clock).await; + let peer5_sync_msg = peer5.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(peer5_sync_msg.hosts.as_set(), vec![info1, info2].as_set()); +} + +/// Test that a SyncSnapshotHosts message with an invalid signature isn't broadcast by PeerManager. +#[tokio::test] +async fn invalid_signature_not_broadcast() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + let clock = clock.clock(); + let clock = &clock; + + let pm = peer_manager::testonly::start( + clock.clone(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + + tracing::info!(target:"test", "Connect peers, expect initial sync to be empty."); + let peer1_config = chain.make_config(rng); + let mut peer1 = + pm.start_inbound(chain.clone(), peer1_config.clone()).await.handshake(clock).await; + let empty_sync_msg = peer1.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(empty_sync_msg.hosts, vec![]); + + let peer2_config = chain.make_config(rng); + let mut peer2 = + pm.start_inbound(chain.clone(), peer2_config.clone()).await.handshake(clock).await; + let empty_sync_msg = peer2.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(empty_sync_msg.hosts, vec![]); + + let mut peer3 = + pm.start_inbound(chain.clone(), chain.make_config(rng)).await.handshake(clock).await; + let empty_sync_msg = peer3.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(empty_sync_msg.hosts, vec![]); + + tracing::info!(target:"test", "Send an invalid SyncSnapshotHosts message from from peer1. One of the host infos has an invalid signature."); + let random_secret_key = SecretKey::from_random(unc_crypto::KeyType::ED25519); + let invalid_info = make_snapshot_host_info(&peer1_config.node_id(), &random_secret_key, rng); + + let ok_info_a = make_snapshot_host_info(&peer1_config.node_id(), &peer1_config.node_key, rng); + let ok_info_b = make_snapshot_host_info(&peer1_config.node_id(), &peer1_config.node_key, rng); + + let invalid_message = PeerMessage::SyncSnapshotHosts(SyncSnapshotHosts { + hosts: vec![ok_info_a.clone(), invalid_info, ok_info_b.clone()], + }); + peer1.send(invalid_message).await; + + tracing::info!(target:"test", "Send a vaid message from peer2 (as peer1 got banned), it should reach peer3."); + + let info2 = make_snapshot_host_info(&peer2_config.node_id(), &peer2_config.node_key, rng); + + peer2 + .send(PeerMessage::SyncSnapshotHosts(SyncSnapshotHosts { hosts: vec![info2.clone()] })) + .await; + + tracing::info!(target:"test", "Make sure that only the valid messages are broadcast."); + + // Wait until peer2 receives info2. Ignore ok_info_a and ok_info_b, + // as the PeerManager could accept and broadcast them despite the neighbouring invalid_info. + wait_for_host_info(&mut peer2, &info2, &[ok_info_a, ok_info_b]).await; +} + +/// Test that a SnapshotHostInfo message with more shards than allowed isn't broadcast by PeerManager. +#[tokio::test] +async fn too_many_shards_not_broadcast() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + let clock = clock.clock(); + let clock = &clock; + + let pm = peer_manager::testonly::start( + clock.clone(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + + tracing::info!(target:"test", "Connect peers, expect initial sync to be empty."); + let peer1_config = chain.make_config(rng); + let mut peer1 = + pm.start_inbound(chain.clone(), peer1_config.clone()).await.handshake(clock).await; + let empty_sync_msg = peer1.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(empty_sync_msg.hosts, vec![]); + + let peer2_config = chain.make_config(rng); + let mut peer2 = + pm.start_inbound(chain.clone(), peer2_config.clone()).await.handshake(clock).await; + let empty_sync_msg = peer2.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(empty_sync_msg.hosts, vec![]); + + let mut peer3 = + pm.start_inbound(chain.clone(), chain.make_config(rng)).await.handshake(clock).await; + let empty_sync_msg = peer3.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(empty_sync_msg.hosts, vec![]); + + tracing::info!(target:"test", "Send an invalid SyncSnapshotHosts message from from peer1. One of the host infos has more shard ids than allowed."); + let too_many_shards: Vec = + (0..(MAX_SHARDS_PER_SNAPSHOT_HOST_INFO as u64 + 1)).collect(); + let invalid_info = Arc::new(SnapshotHostInfo::new( + peer1_config.node_id(), + CryptoHash::hash_borsh(rng.gen::()), + rng.gen(), + too_many_shards, + &peer1_config.node_key, + )); + + let ok_info_a = make_snapshot_host_info(&peer1_config.node_id(), &peer1_config.node_key, rng); + let ok_info_b = make_snapshot_host_info(&peer1_config.node_id(), &peer1_config.node_key, rng); + + let invalid_message = PeerMessage::SyncSnapshotHosts(SyncSnapshotHosts { + hosts: vec![ok_info_a.clone(), invalid_info, ok_info_b.clone()], + }); + peer1.send(invalid_message).await; + + tracing::info!(target:"test", "Send a vaid message from peer2 (as peer1 got banned), it should reach peer3."); + + let info2 = make_snapshot_host_info(&peer2_config.node_id(), &peer2_config.node_key, rng); + + peer2 + .send(PeerMessage::SyncSnapshotHosts(SyncSnapshotHosts { hosts: vec![info2.clone()] })) + .await; + + tracing::info!(target:"test", "Make sure that only valid messages are broadcast."); + + // Wait until peer2 receives info2. Ignore ok_info_a and ok_info_b, + // as the PeerManager could accept and broadcast them despite the neighbouring invalid_info. + wait_for_host_info(&mut peer2, &info2, &[ok_info_a, ok_info_b]).await; +} + +/// Test that SyncSnapshotHosts message is propagated between many PeerManager instances. +/// Four peer managers are connected into a ring: +/// [0] - [1] +/// | | +/// [2] - [3] +/// And then the managers propagate messages among themeselves. +#[tokio::test] +async fn propagate() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + // Adjust the file descriptors limit, so that we can create many connection in the test. + const MAX_CONNECTIONS: usize = 2; + let limit = rlimit::Resource::NOFILE.get().unwrap(); + rlimit::Resource::NOFILE + .set(std::cmp::min(limit.1, (1000 + 4 * FDS_PER_PEER * MAX_CONNECTIONS) as u64), limit.1) + .unwrap(); + + tracing::info!(target:"test", "Create four peer manager instances."); + let mut pms = vec![]; + for _ in 0..4 { + pms.push( + peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await, + ); + } + + tracing::info!(target:"test", "Connect the four peer managers into a ring."); + // [0] - [1] + // | | + // [2] - [3] + pms[0].connect_to(&pms[1].peer_info(), tcp::Tier::T2).await; + pms[0].connect_to(&pms[2].peer_info(), tcp::Tier::T2).await; + pms[1].connect_to(&pms[3].peer_info(), tcp::Tier::T2).await; + pms[2].connect_to(&pms[3].peer_info(), tcp::Tier::T2).await; + + tracing::info!(target:"test", "Send a SnapshotHostInfo message from peer manager #1."); + let info1 = make_snapshot_host_info(&pms[1].peer_info().id, &pms[1].cfg.node_key, rng); + + let message = PeerManagerMessageRequest::NetworkRequests(NetworkRequests::SnapshotHostInfo { + sync_hash: info1.sync_hash, + epoch_height: info1.epoch_height, + shards: info1.shards.clone(), + }); + + pms[1].actix.addr.send(message.with_span_context()).await.unwrap(); + + tracing::info!(target:"test", "Make sure that the message sent from #1 reaches #2 on the other side of the ring."); + let want: HashSet> = std::iter::once(info1).collect(); + pms[2].wait_for_snapshot_hosts(&want).await; +} + +/// Send a SyncSnapshotHosts message with very large shard ids. +/// Makes sure that PeerManager processes large shard ids without any problems. +#[tokio::test] +async fn large_shard_id_in_cache() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + let clock = clock.clock(); + let clock = &clock; + + tracing::info!(target:"test", "Create a peer manager."); + let pm = peer_manager::testonly::start( + clock.clone(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + + tracing::info!(target:"test", "Connect a peer"); + let peer1_config = chain.make_config(rng); + let peer1 = pm.start_inbound(chain.clone(), peer1_config.clone()).await.handshake(clock).await; + + tracing::info!(target:"test", "Send a SnapshotHostInfo message with very large shard ids."); + let big_shard_info = Arc::new(SnapshotHostInfo::new( + peer1_config.node_id(), + CryptoHash::hash_borsh(1234_u64), + 1234, + vec![0, 1232232, ShardId::MAX - 1, ShardId::MAX], + &peer1_config.node_key, + )); + + peer1 + .send(PeerMessage::SyncSnapshotHosts(SyncSnapshotHosts { + hosts: vec![big_shard_info.clone()], + })) + .await; + + tracing::info!(target:"test", "Make sure that the message is received and processed without any problems."); + let want: HashSet> = std::iter::once(big_shard_info).collect(); + pm.wait_for_snapshot_hosts(&want).await; +} + +// When PeerManager receives a request to share SnaphotHostInfo with more than MAX_SHARDS_PER_SNAPSHOT_HOST_INFO +// shards it should truncate the list of shards to prevent being banned for abusive behavior by other peers. +// Truncation is done by choosing a random subset from the original list of shards. +#[tokio::test] +async fn too_many_shards_truncate() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + let clock = clock.clock(); + let clock = &clock; + + tracing::info!(target:"test", "Create a single peer manager."); + let pm = peer_manager::testonly::start( + clock.clone(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + + tracing::info!(target:"test", "Connect a peer, expect initial sync message to be empty."); + let mut peer1 = + pm.start_inbound(chain.clone(), chain.make_config(rng)).await.handshake(clock).await; + let empty_sync_msg = peer1.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(empty_sync_msg.hosts, vec![]); + + tracing::info!(target:"test", "Ask peer manager to send out an invalid SyncSnapshotHosts message. The info has more shard ids than allowed."); + // Create a list of shards with twice as many shard ids as is allowed + let too_many_shards: Vec = + (0..(2 * MAX_SHARDS_PER_SNAPSHOT_HOST_INFO as u64)).collect(); + + let sync_hash = CryptoHash::hash_borsh(rng.gen::()); + let epoch_height: EpochHeight = rng.gen(); + + let message = PeerManagerMessageRequest::NetworkRequests(NetworkRequests::SnapshotHostInfo { + sync_hash, + epoch_height, + shards: too_many_shards.clone(), + }); + + pm.actix.addr.send(message.with_span_context()).await.unwrap(); + + tracing::info!(target:"test", "Receive the truncated SnapshotHostInfo message on peer1, make sure that the contents are correct."); + let msg = peer1.events.recv_until(take_sync_snapshot_msg).await; + assert_eq!(msg.hosts.len(), 1); + let info: &SnapshotHostInfo = &msg.hosts[0]; + assert_eq!(info.peer_id, pm.peer_info().id); + assert_eq!(info.epoch_height, epoch_height); + assert_eq!(info.sync_hash, sync_hash); + + // The list of shards should contain MAX_SHARDS_PER_SNAPSHOT_HOST_INFO randomly sampled, unique shard ids taken from too_many_shards + assert_eq!(info.shards.len(), MAX_SHARDS_PER_SNAPSHOT_HOST_INFO); + for shard_id in &info.shards { + // Shard ids are taken from the original vector + assert!(*shard_id < 2 * MAX_SHARDS_PER_SNAPSHOT_HOST_INFO as u64); + } + // The shard_ids are sorted and unique (no two elements are equal, hence the < condition instead of <=) + assert!(info.shards.windows(2).all(|twoelems| twoelems[0] < twoelems[1])); + // The list isn't truncated by choosing the first half of the shards vec, it should be chosen randomly. + // MAX_SHARDS_PER_SNAPSHOT_HOST_INFO is at least 128, so the chance of this check failing due to randomness is extremely low. + assert_ne!(&info.shards, &too_many_shards[..MAX_SHARDS_PER_SNAPSHOT_HOST_INFO]); +} diff --git a/chain/network/src/peer_manager/tests/tier1.rs b/chain/network/src/peer_manager/tests/tier1.rs new file mode 100644 index 000000000..2093766d4 --- /dev/null +++ b/chain/network/src/peer_manager/tests/tier1.rs @@ -0,0 +1,419 @@ +use crate::config; +use crate::network_protocol::testonly as data; +use crate::network_protocol::{PeerAddr, PeerMessage, RoutedMessageBody}; +use crate::peer_manager; +use crate::peer_manager::peer_manager_actor::Event as PME; +use crate::peer_manager::testonly::start as start_pm; +use crate::peer_manager::testonly::Event; +use crate::stun; +use crate::tcp; +use crate::testonly::{make_rng, Rng}; +use unc_async::time; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::block_header::{Approval, ApprovalInner}; +use unc_primitives::validator_signer::ValidatorSigner; +use unc_store::db::TestDB; +use rand::Rng as _; +use std::collections::HashSet; +use std::sync::Arc; + +/// Constructs a random TIER1 message. +fn make_block_approval(rng: &mut Rng, signer: &dyn ValidatorSigner) -> Approval { + let inner = ApprovalInner::Endorsement(data::make_hash(rng)); + let target_height = rng.gen_range(0..100000); + Approval { + signature: signer.sign_approval(&inner, target_height), + account_id: signer.validator_id().clone(), + target_height, + inner, + } +} + +async fn establish_connections(clock: &time::Clock, pms: &[&peer_manager::testonly::ActorHandler]) { + // Make TIER1 validators connect to proxies. + let mut data = HashSet::new(); + for pm in pms { + data.extend(pm.tier1_advertise_proxies(clock).await); + } + tracing::info!(target:"test", "tier1_advertise_proxies() DONE"); + + // Wait for accounts data to propagate. + for pm in pms { + tracing::info!(target:"test", "{}: wait_for_accounts_data()",pm.cfg.node_id()); + pm.wait_for_accounts_data(&data).await; + tracing::info!(target:"test", "{}: wait_for_accounts_data() DONE",pm.cfg.node_id()); + pm.tier1_connect(clock).await; + tracing::info!(target:"test", "{}: tier1_connect() DONE",pm.cfg.node_id()); + } +} + +// Sends a routed TIER1 message from `from` to `to`. +// Returns the message body that was sent, or None if the routing information was missing. +async fn send_tier1_message( + rng: &mut Rng, + clock: &time::Clock, + from: &peer_manager::testonly::ActorHandler, + to: &peer_manager::testonly::ActorHandler, +) -> Option { + let from_signer = from.cfg.validator.as_ref().unwrap().signer.clone(); + let to_signer = to.cfg.validator.as_ref().unwrap().signer.clone(); + let target = to_signer.validator_id().clone(); + let want = RoutedMessageBody::BlockApproval(make_block_approval(rng, from_signer.as_ref())); + let clock = clock.clone(); + from.with_state(move |s| async move { + if s.send_message_to_account(&clock, &target, want.clone()) { + Some(want) + } else { + None + } + }) + .await +} + +// Sends a routed TIER1 message from `from` to `to`, then waits until `to` receives it. +// `recv_tier` specifies over which network the message is expected to be actually delivered. +async fn send_and_recv_tier1_message( + rng: &mut Rng, + clock: &time::Clock, + from: &peer_manager::testonly::ActorHandler, + to: &peer_manager::testonly::ActorHandler, + recv_tier: tcp::Tier, +) { + let mut events = to.events.from_now(); + let want = send_tier1_message(rng, clock, from, to).await.expect("routing info not available"); + let got = events + .recv_until(|ev| match ev { + Event::PeerManager(PME::MessageProcessed(tier, PeerMessage::Routed(got))) + if tier == recv_tier => + { + Some(got) + } + _ => None, + }) + .await; + assert_eq!(from.cfg.node_id(), got.author); + assert_eq!(want, got.body); +} + +/// Send a message over each connection. +async fn test_clique( + rng: &mut Rng, + clock: &time::Clock, + pms: &[&peer_manager::testonly::ActorHandler], +) { + for from in pms { + for to in pms { + if from.cfg.node_id() == to.cfg.node_id() { + continue; + } + send_and_recv_tier1_message(rng, clock, from, to, tcp::Tier::T1).await; + } + } +} + +// In case a node is its own proxy, it should advertise its address as soon as +// it becomes a TIER1 node. +#[tokio::test] +async fn first_proxy_advertisement() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + let pm = start_pm( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + let chain_info = peer_manager::testonly::make_chain_info(&chain, &[&pm.cfg]); + tracing::info!(target:"test", "set_chain_info()"); + // TODO(gprusak): The default config constructed via chain.make_config(), + // currently returns a validator config with its own server addr in the list of TIER1 proxies. + // You might want to set it explicitly within this test to not rely on defaults. + pm.set_chain_info(chain_info).await; + let got = pm.tier1_advertise_proxies(&clock.clock()).await; + assert_eq!( + got.unwrap().proxies, + vec![PeerAddr { peer_id: pm.cfg.node_id(), addr: **pm.cfg.node_addr.as_ref().unwrap() }] + ); +} + +#[tokio::test] +async fn direct_connections() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let mut pms = vec![]; + for _ in 0..5 { + pms.push( + start_pm( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await, + ); + } + let pms: Vec<_> = pms.iter().collect(); + + tracing::info!(target:"test", "Connect peers serially."); + for i in 1..pms.len() { + pms[i - 1].connect_to(&pms[i].peer_info(), tcp::Tier::T2).await; + } + + tracing::info!(target:"test", "Set chain info."); + let chain_info = peer_manager::testonly::make_chain_info( + &chain, + &pms.iter().map(|pm| &pm.cfg).collect::>()[..], + ); + for pm in &pms { + pm.set_chain_info(chain_info.clone()).await; + } + tracing::info!(target:"test", "Establish connections."); + establish_connections(&clock.clock(), &pms[..]).await; + tracing::info!(target:"test", "Test clique."); + test_clique(rng, &clock.clock(), &pms[..]).await; +} + +/// Test which spawns N validators, each with 1 proxy. +/// All the nodes are connected in TIER2 star topology. +/// Then all validators connect to the proxy of each other validator. +#[tokio::test] +async fn proxy_connections() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + const N: usize = 5; + + let mut proxies = vec![]; + for _ in 0..N { + proxies.push( + start_pm( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await, + ); + } + let proxies: Vec<_> = proxies.iter().collect(); + + let mut validators = vec![]; + for i in 0..N { + let mut cfg = chain.make_config(rng); + cfg.validator.as_mut().unwrap().proxies = + config::ValidatorProxies::Static(vec![PeerAddr { + peer_id: proxies[i].cfg.node_id(), + addr: **proxies[i].cfg.node_addr.as_ref().unwrap(), + }]); + validators + .push(start_pm(clock.clock(), unc_store::db::TestDB::new(), cfg, chain.clone()).await); + } + let validators: Vec<_> = validators.iter().collect(); + + // Connect validators and proxies in a star topology. Any connected graph would do. + let hub = start_pm( + clock.clock(), + unc_store::db::TestDB::new(), + chain.make_config(rng), + chain.clone(), + ) + .await; + for pm in &validators { + pm.connect_to(&hub.peer_info(), tcp::Tier::T2).await; + } + for pm in &proxies { + pm.connect_to(&hub.peer_info(), tcp::Tier::T2).await; + } + + let mut all = vec![]; + all.extend(validators.clone()); + all.extend(proxies.clone()); + all.push(&hub); + + let chain_info = peer_manager::testonly::make_chain_info( + &chain, + &validators.iter().map(|pm| &pm.cfg).collect::>()[..], + ); + for pm in &all { + pm.set_chain_info(chain_info.clone()).await; + } + establish_connections(&clock.clock(), &all[..]).await; + test_clique(rng, &clock.clock(), &validators[..]).await; +} + +#[tokio::test] +async fn account_keys_change() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let v0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let v1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let v2 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let hub = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + hub.connect_to(&v0.peer_info(), tcp::Tier::T2).await; + hub.connect_to(&v1.peer_info(), tcp::Tier::T2).await; + hub.connect_to(&v2.peer_info(), tcp::Tier::T2).await; + + // TIER1 nodes in 1st epoch are {v0,v1}. + let chain_info = peer_manager::testonly::make_chain_info(&chain, &[&v0.cfg, &v1.cfg]); + for pm in [&v0, &v1, &v2, &hub] { + pm.set_chain_info(chain_info.clone()).await; + } + establish_connections(&clock.clock(), &[&v0, &v1, &v2, &hub]).await; + test_clique(rng, &clock.clock(), &[&v0, &v1]).await; + + // TIER1 nodes in 2nd epoch are {v0,v2}. + let chain_info = peer_manager::testonly::make_chain_info(&chain, &[&v0.cfg, &v2.cfg]); + for pm in [&v0, &v1, &v2, &hub] { + pm.set_chain_info(chain_info.clone()).await; + } + establish_connections(&clock.clock(), &[&v0, &v1, &v2, &hub]).await; + test_clique(rng, &clock.clock(), &[&v0, &v2]).await; + + drop(v0); + drop(v1); + drop(v2); + drop(hub); +} + +// Let's say that a validator has 2 proxies configured. At first proxy0 is available and proxy1 is not, +// then proxy1 is available and proxy0 is not. In both situations validator should be reachable, +// as long as it manages to advertise the currently available proxy and the TIER1 nodes connect to +// that proxy. +#[tokio::test] +async fn proxy_change() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + // v0 has proxies {p0,p1} + // v1 has no proxies. + let p0cfg = chain.make_config(rng); + let p1cfg = chain.make_config(rng); + let mut v0cfg = chain.make_config(rng); + v0cfg.validator.as_mut().unwrap().proxies = config::ValidatorProxies::Static(vec![ + PeerAddr { peer_id: p0cfg.node_id(), addr: **p0cfg.node_addr.as_ref().unwrap() }, + PeerAddr { peer_id: p1cfg.node_id(), addr: **p1cfg.node_addr.as_ref().unwrap() }, + ]); + let mut v1cfg = chain.make_config(rng); + v1cfg.validator.as_mut().unwrap().proxies = config::ValidatorProxies::Static(vec![]); + + tracing::info!(target:"test", "Start all nodes."); + let p0 = start_pm(clock.clock(), TestDB::new(), p0cfg.clone(), chain.clone()).await; + let p1 = start_pm(clock.clock(), TestDB::new(), p1cfg.clone(), chain.clone()).await; + let v0 = start_pm(clock.clock(), TestDB::new(), v0cfg.clone(), chain.clone()).await; + let v1 = start_pm(clock.clock(), TestDB::new(), v1cfg.clone(), chain.clone()).await; + let hub = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + hub.connect_to(&p0.peer_info(), tcp::Tier::T2).await; + hub.connect_to(&p1.peer_info(), tcp::Tier::T2).await; + hub.connect_to(&v0.peer_info(), tcp::Tier::T2).await; + hub.connect_to(&v1.peer_info(), tcp::Tier::T2).await; + + tracing::info!(target:"test", "p0 goes down"); + drop(p0); + tracing::info!(target:"test", "remaining nodes learn that [v0,v1] are TIER1 nodes"); + let chain_info = peer_manager::testonly::make_chain_info(&chain, &[&v0.cfg, &v1.cfg]); + for pm in [&v0, &v1, &p1, &hub] { + pm.set_chain_info(chain_info.clone()).await; + } + tracing::info!(target:"test", "TIER1 connections get established: v0 -> p1 <- v1."); + establish_connections(&clock.clock(), &[&v0, &v1, &p1, &hub]).await; + tracing::info!(target:"test", "Send message v1 -> v0 over TIER1."); + send_and_recv_tier1_message(rng, &clock.clock(), &v1, &v0, tcp::Tier::T1).await; + + // Advance time, so that the new AccountsData has newer timestamp. + clock.advance(time::Duration::hours(1)); + + tracing::info!(target:"test", "p1 goes down."); + drop(p1); + tracing::info!(target:"test", "p0 goes up and learns that [v0,v1] are TIER1 nodes."); + let p0 = start_pm(clock.clock(), TestDB::new(), p0cfg.clone(), chain.clone()).await; + p0.set_chain_info(chain_info).await; + hub.connect_to(&p0.peer_info(), tcp::Tier::T2).await; + tracing::info!(target:"test", "TIER1 connections get established: v0 -> p0 <- v1."); + establish_connections(&clock.clock(), &[&v0, &v1, &p0, &hub]).await; + tracing::info!(target:"test", "Send message v1 -> v0 over TIER1."); + send_and_recv_tier1_message(rng, &clock.clock(), &v1, &v0, tcp::Tier::T1).await; + + drop(hub); + drop(v0); + drop(v1); + drop(p0); +} + +#[tokio::test] +async fn tier2_routing_using_accounts_data() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "start 2 nodes and connect them"); + let pm0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + + tracing::info!(target:"test", "Try to send a routed message pm0 -> pm1 over TIER2"); + // It should fail due to missing routing information: neither AccountData or AnnounceAccount is + // broadcasted by default in tests. + // TODO(gprusak): send_tier1_message sends an Approval message, which is not a valid message to + // be sent from a non-TIER1 node. Make it more realistic by sending a Transaction message. + assert!(send_tier1_message(rng, &clock.clock(), &pm0, &pm1).await.is_none()); + + tracing::info!(target:"test", "propagate AccountsData"); + let chain_info = peer_manager::testonly::make_chain_info(&chain, &[&pm1.cfg]); + for pm in [&pm0, &pm1] { + pm.set_chain_info(chain_info.clone()).await; + } + let data: HashSet<_> = pm1.tier1_advertise_proxies(&clock.clock()).await.into_iter().collect(); + pm0.wait_for_accounts_data(&data).await; + + tracing::info!(target:"test", "Send a routed message pm0 -> pm1 over TIER2."); + send_and_recv_tier1_message(rng, &clock.clock(), &pm0, &pm1, tcp::Tier::T2).await; +} + +#[tokio::test] +async fn stun_self_discovery() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + tracing::info!(target:"test", "configure TIER1 self discovery to use 2 local STUN servers"); + let stun_server1 = stun::testonly::Server::new().await; + let stun_server2 = stun::testonly::Server::new().await; + let mut cfg = chain.make_config(rng); + let vc = cfg.validator.as_mut().unwrap(); + vc.proxies = config::ValidatorProxies::Dynamic(vec![stun_server1.addr(), stun_server2.addr()]); + + tracing::info!(target:"test", "spawn a node and advertize AccountData."); + let pm = start_pm(clock.clock(), TestDB::new(), cfg, chain.clone()).await; + let chain_info = peer_manager::testonly::make_chain_info(&chain, &[&pm.cfg]); + pm.set_chain_info(chain_info).await; + let got = pm.tier1_advertise_proxies(&clock.clock()).await.unwrap(); + let want = vec![PeerAddr { peer_id: pm.cfg.node_id(), addr: *pm.cfg.node_addr.unwrap() }]; + assert_eq!(want, got.proxies); + + tracing::info!(target:"test", "close the stun servers"); + drop(pm); + stun_server1.close().await; + stun_server2.close().await; +} diff --git a/chain/network/src/peer_manager/tests/tier2.rs b/chain/network/src/peer_manager/tests/tier2.rs new file mode 100644 index 000000000..afd8e6912 --- /dev/null +++ b/chain/network/src/peer_manager/tests/tier2.rs @@ -0,0 +1,297 @@ +use crate::broadcast; +use crate::network_protocol::testonly as data; +use crate::peer_manager::connection_store::STORED_CONNECTIONS_MIN_DURATION; +use crate::peer_manager::network_state::RECONNECT_ATTEMPT_INTERVAL; +use crate::peer_manager::peer_manager_actor::Event as PME; +use crate::peer_manager::peer_manager_actor::POLL_CONNECTION_STORE_INTERVAL; +use crate::peer_manager::testonly::start as start_pm; +use crate::peer_manager::testonly::ActorHandler; +use crate::peer_manager::testonly::Event; +use crate::tcp; +use crate::testonly::make_rng; +use crate::testonly::AsSet; +use unc_async::time; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::network::PeerId; +use unc_store::db::TestDB; +use std::sync::Arc; + +async fn check_recent_outbound_connections(pm: &ActorHandler, want: Vec) { + let got: Vec = pm + .with_state(move |s| async move { + s.connection_store + .get_recent_outbound_connections() + .iter() + .map(|c| c.peer_info.id.clone()) + .collect() + }) + .await; + + assert_eq!(got.as_set(), want.as_set()); +} + +async fn wait_for_connection_closed(events: &mut broadcast::Receiver) { + events + .recv_until(|ev| match ev { + Event::PeerManager(PME::ConnectionClosed(_)) => Some(()), + _ => None, + }) + .await +} + +#[tokio::test] +async fn test_store_outbound_connection() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let pm0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm2 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + + tracing::info!(target:"test", "connect pm0 to pm1"); + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + pm2.connect_to(&pm0.peer_info(), tcp::Tier::T2).await; + clock.advance(STORED_CONNECTIONS_MIN_DURATION); + + tracing::info!(target:"test", "check that pm0 stores the outbound connection to pm1"); + pm0.update_connection_store(&clock.clock()).await; + check_recent_outbound_connections(&pm0, vec![id1.clone()]).await; + + tracing::info!(target:"test", "check that pm1 does not store anything, as it has no outbound connections"); + check_recent_outbound_connections(&pm1, vec![]).await; + + tracing::info!(target:"test", "check that pm2 stores the outbound connection to pm0"); + pm2.update_connection_store(&clock.clock()).await; + check_recent_outbound_connections(&pm2, vec![id0.clone()]).await; +} + +#[tokio::test] +async fn test_storage_after_disconnect() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let pm0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + let id0 = pm0.cfg.node_id(); + let id1 = pm1.cfg.node_id(); + + tracing::info!(target:"test", "connect pm0 to pm1"); + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + clock.advance(STORED_CONNECTIONS_MIN_DURATION); + + tracing::info!(target:"test", "check that pm0 stores the outbound connection to pm1"); + pm0.update_connection_store(&clock.clock()).await; + check_recent_outbound_connections(&pm0, vec![id1.clone()]).await; + + tracing::info!(target:"test", "have pm1 disconnect from pm0"); + let mut pm0_ev = pm0.events.from_now(); + pm1.disconnect(&id0).await; + wait_for_connection_closed(&mut pm0_ev).await; + + tracing::info!(target:"test", "check that pm0 retains the stored outbound connection to pm1 after disconnect"); + pm0.update_connection_store(&clock.clock()).await; + check_recent_outbound_connections(&pm0, vec![id1.clone()]).await; +} + +#[tokio::test] +async fn test_reconnect_after_restart_outbound_side() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let pm0_db = TestDB::new(); + let pm0 = start_pm(clock.clock(), pm0_db.clone(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + let id1 = pm1.cfg.node_id(); + + tracing::info!(target:"test", "connect pm0 to pm1"); + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + clock.advance(STORED_CONNECTIONS_MIN_DURATION); + + tracing::info!(target:"test", "check that pm0 stores the outbound connection to pm1"); + pm0.update_connection_store(&clock.clock()).await; + check_recent_outbound_connections(&pm0, vec![id1.clone()]).await; + + tracing::info!(target:"test", "drop pm0 and start it again with the same db"); + drop(pm0); + let pm0 = start_pm(clock.clock(), pm0_db, chain.make_config(rng), chain.clone()).await; + + tracing::info!(target:"test", "check that pm0 reconnects to pm1"); + pm0.wait_for_direct_connection(id1.clone()).await; +} + +#[tokio::test] +async fn test_skip_reconnect_after_restart_outbound_side() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let pm0_db = TestDB::new(); + let mut pm0_cfg = chain.make_config(rng); + pm0_cfg.connect_to_reliable_peers_on_startup = false; + + let pm0 = start_pm(clock.clock(), pm0_db.clone(), pm0_cfg.clone(), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + let id1 = pm1.cfg.node_id(); + + tracing::info!(target:"test", "connect pm0 to pm1"); + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + clock.advance(STORED_CONNECTIONS_MIN_DURATION); + + tracing::info!(target:"test", "check that pm0 stores the outbound connection to pm1"); + pm0.update_connection_store(&clock.clock()).await; + check_recent_outbound_connections(&pm0, vec![id1.clone()]).await; + + tracing::info!(target:"test", "drop pm0 and start it again with the same db"); + drop(pm0); + let pm0 = start_pm(clock.clock(), pm0_db.clone(), pm0_cfg.clone(), chain.clone()).await; + + tracing::info!(target:"test", "check that pm0 starts without attempting to reconnect to pm1"); + let mut pm0_ev = pm0.events.clone(); + pm0_ev + .recv_until(|ev| match &ev { + Event::PeerManager(PME::ReconnectLoopSpawned(_)) => { + panic!("PeerManager spawned a reconnect loop during startup"); + } + Event::PeerManager(PME::PeerManagerStarted) => Some(()), + _ => None, + }) + .await; + + tracing::info!(target:"test", "check that pm0 has pm1 as a recent connection"); + check_recent_outbound_connections(&pm0, vec![id1.clone()]).await; +} + +#[tokio::test] +async fn test_reconnect_after_restart_inbound_side() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let pm0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + let pm1_cfg = chain.make_config(rng); + let pm1 = start_pm(clock.clock(), TestDB::new(), pm1_cfg.clone(), chain.clone()).await; + + let id1 = pm1.cfg.node_id().clone(); + + tracing::info!(target:"test", "connect pm0 to pm1"); + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + clock.advance(STORED_CONNECTIONS_MIN_DURATION); + + tracing::info!(target:"test", "check that pm0 stores the outbound connection to pm1"); + pm0.update_connection_store(&clock.clock()).await; + check_recent_outbound_connections(&pm0, vec![id1.clone()]).await; + + tracing::info!(target:"test", "drop pm1"); + let mut pm0_ev = pm0.events.from_now(); + drop(pm1); + wait_for_connection_closed(&mut pm0_ev).await; + + tracing::info!(target:"test", "start pm1 again with the same config, check that pm0 reconnects"); + let _pm1 = start_pm(clock.clock(), TestDB::new(), pm1_cfg.clone(), chain.clone()).await; + clock.advance(POLL_CONNECTION_STORE_INTERVAL + RECONNECT_ATTEMPT_INTERVAL); + pm0.wait_for_direct_connection(id1.clone()).await; +} + +#[tokio::test] +async fn test_reconnect_after_disconnect_inbound_side() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let pm0 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + let pm1_cfg = chain.make_config(rng); + let pm1 = start_pm(clock.clock(), TestDB::new(), pm1_cfg.clone(), chain.clone()).await; + + let id0 = pm0.cfg.node_id().clone(); + let id1 = pm1.cfg.node_id().clone(); + + tracing::info!(target:"test", "connect pm0 to pm1"); + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + clock.advance(STORED_CONNECTIONS_MIN_DURATION); + + tracing::info!(target:"test", "check that pm0 stores the outbound connection to pm1"); + pm0.update_connection_store(&clock.clock()).await; + check_recent_outbound_connections(&pm0, vec![id1.clone()]).await; + + tracing::info!(target:"test", "have pm1 disconnect gracefully from pm0"); + let mut pm0_ev = pm0.events.from_now(); + pm1.disconnect(&id0).await; + wait_for_connection_closed(&mut pm0_ev).await; + + tracing::info!(target:"test", "check that pm0 reconnects"); + clock.advance(POLL_CONNECTION_STORE_INTERVAL + RECONNECT_ATTEMPT_INTERVAL); + pm0.wait_for_direct_connection(id1.clone()).await; +} + +#[tokio::test] +async fn test_reconnect_after_restart_outbound_side_multi() { + init_test_logger(); + let mut rng = make_rng(921853233); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let pm0_db = TestDB::new(); + let pm0 = start_pm(clock.clock(), pm0_db.clone(), chain.make_config(rng), chain.clone()).await; + let pm1 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm2 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm3 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + let pm4 = start_pm(clock.clock(), TestDB::new(), chain.make_config(rng), chain.clone()).await; + + let id1 = pm1.cfg.node_id(); + let id2 = pm2.cfg.node_id(); + let id3 = pm3.cfg.node_id(); + let id4 = pm4.cfg.node_id(); + + tracing::info!(target:"test", "connect pm0 to other pms"); + pm0.connect_to(&pm1.peer_info(), tcp::Tier::T2).await; + pm0.wait_for_direct_connection(id1.clone()).await; + pm0.connect_to(&pm2.peer_info(), tcp::Tier::T2).await; + pm0.wait_for_direct_connection(id2.clone()).await; + pm0.connect_to(&pm3.peer_info(), tcp::Tier::T2).await; + pm0.wait_for_direct_connection(id3.clone()).await; + pm0.connect_to(&pm4.peer_info(), tcp::Tier::T2).await; + pm0.wait_for_direct_connection(id4.clone()).await; + clock.advance(STORED_CONNECTIONS_MIN_DURATION); + + tracing::info!(target:"test", "check that pm0 stores the outbound connections"); + pm0.update_connection_store(&clock.clock()).await; + check_recent_outbound_connections( + &pm0, + vec![id1.clone(), id2.clone(), id3.clone(), id4.clone()], + ) + .await; + + tracing::info!(target:"test", "drop pm0 and start it again with the same db"); + drop(pm0); + let pm0 = start_pm(clock.clock(), pm0_db, chain.make_config(rng), chain.clone()).await; + + tracing::info!(target:"test", "wait for pm0 to reconnect to the other nodes"); + pm0.wait_for_direct_connection(id1.clone()).await; + pm0.wait_for_direct_connection(id2.clone()).await; + pm0.wait_for_direct_connection(id3.clone()).await; + pm0.wait_for_direct_connection(id4.clone()).await; +} diff --git a/chain/network/src/private_actix.rs b/chain/network/src/private_actix.rs new file mode 100644 index 000000000..aa2aa08a8 --- /dev/null +++ b/chain/network/src/private_actix.rs @@ -0,0 +1,27 @@ +/// This file is contains all types used for communication between `Actors` within this crate. +/// They are not meant to be used outside. +use crate::network_protocol::PeerMessage; +use crate::peer_manager::connection; +use std::fmt::Debug; +use std::sync::Arc; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum RegisterPeerError { + Blacklisted, + Banned, + PoolError(connection::PoolError), + ConnectionLimitExceeded, + NotTier1Peer, + Tier1InboundDisabled, + InvalidEdge, +} + +#[derive(actix::Message)] +#[rtype(result = "()")] +pub(crate) struct StopMsg {} + +#[derive(actix::Message, Clone, Debug)] +#[rtype(result = "()")] +pub(crate) struct SendMessage { + pub message: Arc, +} diff --git a/chain/network/src/raw/connection.rs b/chain/network/src/raw/connection.rs new file mode 100644 index 000000000..2c01583a5 --- /dev/null +++ b/chain/network/src/raw/connection.rs @@ -0,0 +1,652 @@ +use crate::network_protocol::{ + Encoding, Handshake, HandshakeFailureReason, PartialEdgeInfo, PeerChainInfoV2, PeerIdOrHash, + PeerMessage, Ping, Pong, RawRoutedMessage, RoutedMessageBody, RoutingTableUpdate, +}; +use crate::tcp; +use crate::types::{ + PartialEncodedChunkRequestMsg, PartialEncodedChunkResponseMsg, PeerInfo, StateResponseInfo, +}; +use bytes::buf::{Buf, BufMut}; +use bytes::BytesMut; +use unc_async::time::{Duration, Instant, Utc}; +use unc_crypto::{KeyType, SecretKey}; +use unc_primitives::block::{Block, BlockHeader, GenesisId}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::types::{BlockHeight, ShardId}; +use unc_primitives::version::{ProtocolVersion, PROTOCOL_VERSION}; +use std::fmt; +use std::io; +use std::net::SocketAddr; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; + +/// Represents a connection to a peer, and provides only minimal functionality. +/// Almost none of the usual NEAR network logic is implemented, and the user +/// will receive messages via the recv() function and send messages via +/// send_message() and send_routed_message() +pub struct Connection { + secret_key: SecretKey, + my_peer_id: PeerId, + peer_id: PeerId, + stream: PeerStream, + // this is used to keep track of routed messages we've sent so that when we get a reply + // that references one of our previously sent messages, we can determine that the message is for us + route_cache: lru::LruCache, + // when a peer connects to us, it'll send two handshakes. One as a proto and one borsh-encoded. + // If this field is true, it means we expect to receive a message that won't parse as a proto, and + // will accept and drop one such message without giving an error. + borsh_message_expected: bool, +} + +// The types of messages it's possible to route to a target PeerId via the connected peer as a first hop +// These can be sent with Connection::send_routed_message(), and received in Message::Routed() from +// Connection::recv() +#[derive(Clone, strum::IntoStaticStr)] +pub enum RoutedMessage { + Ping { nonce: u64 }, + Pong { nonce: u64, source: PeerId }, + PartialEncodedChunkRequest(PartialEncodedChunkRequestMsg), + PartialEncodedChunkResponse(PartialEncodedChunkResponseMsg), +} + +impl fmt::Display for RoutedMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(<&'static str>::from(self), f) + } +} + +impl fmt::Debug for RoutedMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Ping { nonce } => write!(f, "Ping({})", nonce), + Self::Pong { nonce, source } => write!(f, "Pong({}, {})", nonce, source), + Self::PartialEncodedChunkRequest(r) => write!(f, "PartialEncodedChunkRequest({:?})", r), + Self::PartialEncodedChunkResponse(r) => write!( + f, + "PartialEncodedChunkResponse(chunk_hash: {} parts_ords: {:?} num_receipts: {})", + &r.chunk_hash.0, + r.parts.iter().map(|p| p.part_ord).collect::>(), + r.receipts.len() + ), + } + } +} + +// The types of messages it's possible to send directly to the connected peer. +// These can be sent with Connection::send_message(), and received in Message::Direct() from +// Connection::recv() +#[derive(Clone, strum::IntoStaticStr)] +pub enum DirectMessage { + AnnounceAccounts(Vec), + BlockRequest(CryptoHash), + Block(Block), + BlockHeadersRequest(Vec), + BlockHeaders(Vec), + StateRequestHeader(ShardId, CryptoHash), + StateRequestPart(ShardId, CryptoHash, u64), + VersionedStateResponse(StateResponseInfo), +} + +impl fmt::Display for DirectMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(<&'static str>::from(self), f) + } +} + +impl fmt::Debug for DirectMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::AnnounceAccounts(a) => write!(f, "AnnounceAccounts({:?})", a), + Self::BlockRequest(r) => write!(f, "BlockRequest({})", r), + Self::Block(b) => write!(f, "Block(#{} {})", b.header().height(), b.header().hash()), + Self::BlockHeadersRequest(r) => write!(f, "BlockHeadersRequest({:?})", r), + Self::BlockHeaders(h) => { + write!( + f, + "BlockHeaders({:?})", + h.iter().map(|h| format!("#{} {}", h.height(), h.hash())).collect::>() + ) + } + Self::StateRequestHeader(shard_id, hash) => { + write!(f, "StateRequestHeader({}, {})", shard_id, hash) + } + Self::StateRequestPart(shard_id, hash, part_id) => { + write!(f, "StateRequestPart({}, {}, {})", shard_id, hash, part_id) + } + Self::VersionedStateResponse(r) => write!( + f, + "VersionedStateResponse(shard_id: {} sync_hash: {})", + r.shard_id(), + r.sync_hash() + ), + } + } +} + +/// The types of messages it's possible to send/receive from a `Connection`. Any PeerMessage +/// we receive that doesn't fit one of the types we've enumerated will just be logged and dropped. +#[derive(Clone, strum::IntoStaticStr)] +pub enum Message { + Direct(DirectMessage), + Routed(RoutedMessage), +} + +impl fmt::Display for Message { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Direct(msg) => write!(f, "raw::{}", msg), + Self::Routed(msg) => write!(f, "raw::Routed({})", msg), + } + } +} + +impl fmt::Debug for Message { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Direct(msg) => write!(f, "raw::{:?}", msg), + Self::Routed(msg) => write!(f, "raw::Routed({:?})", msg), + } + } +} + +impl std::fmt::Debug for Connection { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!( + f, + "raw::Connection({:?}@{} -> {:?}@{})", + &self.my_peer_id, + &self.stream.stream.local_addr, + &self.peer_id, + &self.stream.stream.peer_addr + ) + } +} + +#[derive(thiserror::Error, Debug)] +pub enum ConnectError { + #[error(transparent)] + IO(std::io::Error), + #[error("failed parsing protobuf of length {0}")] + Parse(usize), + #[error("handshake failed {0:?}")] + HandshakeFailure(HandshakeFailureReason), + #[error("received unexpected message before the handshake: {0:?}")] + UnexpectedFirstMessage(PeerMessage), + #[error(transparent)] + TcpConnect(anyhow::Error), +} + +impl From for ConnectError { + fn from(err: RecvError) -> Self { + match err { + RecvError::IO(io) => Self::IO(io), + RecvError::Parse(len) => Self::Parse(len), + } + } +} + +fn new_handshake( + secret_key: &SecretKey, + my_peer_id: &PeerId, + target_peer_id: &PeerId, + listen_port: u16, + nonce: u64, + protocol_version: ProtocolVersion, + chain_id: &str, + genesis_hash: CryptoHash, + head_height: BlockHeight, + tracked_shards: Vec, + archival: bool, +) -> PeerMessage { + PeerMessage::Tier2Handshake(Handshake { + protocol_version, + oldest_supported_version: protocol_version - 2, + sender_peer_id: my_peer_id.clone(), + target_peer_id: target_peer_id.clone(), + // we have to set this even if we have no intention of listening since otherwise + // the peer will drop our connection + sender_listen_port: Some(listen_port), + sender_chain_info: PeerChainInfoV2 { + genesis_id: GenesisId { chain_id: chain_id.to_string(), hash: genesis_hash }, + height: head_height, + tracked_shards, + archival, + }, + partial_edge_info: PartialEdgeInfo::new(my_peer_id, target_peer_id, nonce, secret_key), + owned_account: None, + }) +} + +impl Connection { + /// Connect to the NEAR node at `peer_id`@`addr`. The inputs are used to build out handshake, + /// and this function will return a `Peer` when a handshake has been received successfully. + pub async fn connect( + addr: SocketAddr, + peer_id: PeerId, + my_protocol_version: Option, + chain_id: &str, + genesis_hash: CryptoHash, + head_height: BlockHeight, + tracked_shards: Vec, + recv_timeout: Duration, + ) -> Result { + let secret_key = SecretKey::from_random(KeyType::ED25519); + let my_peer_id = PeerId::new(secret_key.public_key()); + + let start = Instant::now(); + let stream = tcp::Stream::connect(&PeerInfo::new(peer_id.clone(), addr), tcp::Tier::T2) + .await + .map_err(ConnectError::TcpConnect)?; + tracing::info!( + target: "network", "Connection to {}@{:?} established. latency: {}", + &peer_id, &addr, start.elapsed(), + ); + let mut peer = Self { + stream: PeerStream::new(stream, recv_timeout), + peer_id, + secret_key, + my_peer_id, + route_cache: lru::LruCache::new(1_000_000), + borsh_message_expected: false, + }; + peer.do_handshake( + my_protocol_version.unwrap_or(PROTOCOL_VERSION), + chain_id, + genesis_hash, + head_height, + tracked_shards, + ) + .await?; + + Ok(peer) + } + + async fn on_accept( + stream: tcp::Stream, + secret_key: SecretKey, + chain_id: &str, + genesis_hash: CryptoHash, + head_height: BlockHeight, + tracked_shards: Vec, + archival: bool, + recv_timeout: Duration, + ) -> Result { + let mut stream = PeerStream::new(stream, recv_timeout); + let mut borsh_message_expected = true; + let (message, _timestamp) = match stream.recv_message().await { + Ok(m) => m, + Err(RecvError::Parse(len)) => { + tracing::debug!(target: "network", "dropping a non protobuf message of length {}. Probably an extra handshake.", len); + borsh_message_expected = false; + stream.recv_message().await? + } + Err(RecvError::IO(e)) => return Err(ConnectError::IO(e)), + }; + + let (peer_id, nonce) = match message { + // TODO: maybe check the handshake for sanity + PeerMessage::Tier2Handshake(h) => (h.sender_peer_id, h.partial_edge_info.nonce), + PeerMessage::HandshakeFailure(_peer_info, reason) => { + return Err(ConnectError::HandshakeFailure(reason)) + } + _ => return Err(ConnectError::UnexpectedFirstMessage(message)), + }; + + let my_peer_id = PeerId::new(secret_key.public_key()); + let handshake = new_handshake( + &secret_key, + &my_peer_id, + &peer_id, + stream.stream.local_addr.port(), + nonce, + PROTOCOL_VERSION, + chain_id, + genesis_hash, + head_height, + tracked_shards, + archival, + ); + + stream.write_message(&handshake).await.map_err(ConnectError::IO)?; + + Ok(Self { + secret_key, + my_peer_id, + stream, + peer_id, + route_cache: lru::LruCache::new(1_000_000), + borsh_message_expected, + }) + } + + async fn do_handshake( + &mut self, + protocol_version: ProtocolVersion, + chain_id: &str, + genesis_hash: CryptoHash, + head_height: BlockHeight, + tracked_shards: Vec, + ) -> Result<(), ConnectError> { + let handshake = new_handshake( + &self.secret_key, + &self.my_peer_id, + &self.peer_id, + self.stream.stream.local_addr.port(), + 1, + protocol_version, + chain_id, + genesis_hash, + head_height, + tracked_shards, + false, + ); + + self.stream.write_message(&handshake).await.map_err(ConnectError::IO)?; + + let start = Instant::now(); + + let (message, timestamp) = self.stream.recv_message().await?; + + match message { + // TODO: maybe check the handshake for sanity + PeerMessage::Tier2Handshake(_) => { + tracing::info!(target: "network", "handshake latency: {}", timestamp - start); + } + PeerMessage::HandshakeFailure(_peer_info, reason) => { + return Err(ConnectError::HandshakeFailure(reason)) + } + _ => return Err(ConnectError::UnexpectedFirstMessage(message)), + }; + + Ok(()) + } + + // Try to send a PeerMessage corresponding to the given DirectMessage + pub async fn send_message(&mut self, msg: DirectMessage) -> io::Result<()> { + let peer_msg = match msg { + DirectMessage::AnnounceAccounts(accounts) => { + PeerMessage::SyncRoutingTable(RoutingTableUpdate { edges: Vec::new(), accounts }) + } + DirectMessage::BlockRequest(h) => PeerMessage::BlockRequest(h), + DirectMessage::Block(b) => PeerMessage::Block(b), + DirectMessage::BlockHeadersRequest(h) => PeerMessage::BlockHeadersRequest(h), + DirectMessage::BlockHeaders(h) => PeerMessage::BlockHeaders(h), + DirectMessage::StateRequestHeader(shard_id, sync_hash) => { + PeerMessage::StateRequestHeader(shard_id, sync_hash) + } + DirectMessage::StateRequestPart(shard_id, sync_hash, part_id) => { + PeerMessage::StateRequestPart(shard_id, sync_hash, part_id) + } + DirectMessage::VersionedStateResponse(request) => { + PeerMessage::VersionedStateResponse(request) + } + }; + + self.stream.write_message(&peer_msg).await + } + + // Try to send a routed PeerMessage corresponding to the given RoutedMessage + pub async fn send_routed_message( + &mut self, + msg: RoutedMessage, + target: PeerId, + ttl: u8, + ) -> io::Result<()> { + let body = match msg { + RoutedMessage::Ping { nonce } => { + RoutedMessageBody::Ping(Ping { nonce, source: self.my_peer_id.clone() }) + } + RoutedMessage::Pong { nonce, source } => { + RoutedMessageBody::Pong(Pong { nonce, source }) + } + RoutedMessage::PartialEncodedChunkRequest(request) => { + RoutedMessageBody::PartialEncodedChunkRequest(request) + } + RoutedMessage::PartialEncodedChunkResponse(response) => { + RoutedMessageBody::PartialEncodedChunkResponse(response) + } + }; + let msg = RawRoutedMessage { target: PeerIdOrHash::PeerId(target), body }.sign( + &self.secret_key, + ttl, + Some(Utc::now_utc()), + ); + self.route_cache.put(msg.hash(), ()); + self.stream.write_message(&PeerMessage::Routed(Box::new(msg))).await + } + + fn target_is_for_me(&mut self, target: &PeerIdOrHash) -> bool { + match target { + PeerIdOrHash::PeerId(peer_id) => peer_id == &self.my_peer_id, + PeerIdOrHash::Hash(hash) => self.route_cache.pop(hash).is_some(), + } + } + + fn recv_routed_msg( + &mut self, + msg: &crate::network_protocol::RoutedMessage, + ) -> Option { + if !self.target_is_for_me(&msg.target) { + tracing::debug!( + target: "network", "{:?} dropping routed message {} for {:?}", + &self, <&'static str>::from(&msg.body), &msg.target + ); + return None; + } + match &msg.body { + RoutedMessageBody::Ping(p) => Some(RoutedMessage::Ping { nonce: p.nonce }), + RoutedMessageBody::Pong(p) => { + Some(RoutedMessage::Pong { nonce: p.nonce, source: p.source.clone() }) + } + RoutedMessageBody::PartialEncodedChunkRequest(request) => { + Some(RoutedMessage::PartialEncodedChunkRequest(request.clone())) + } + RoutedMessageBody::PartialEncodedChunkResponse(response) => { + Some(RoutedMessage::PartialEncodedChunkResponse(response.clone())) + } + _ => None, + } + } + + /// Reads from the socket until we receive some message that we care to pass to the caller + /// (that is, represented in `DirectMessage` or `RoutedMessage`). + pub async fn recv(&mut self) -> io::Result<(Message, Instant)> { + loop { + let (msg, timestamp) = match self.stream.recv_message().await { + Ok(m) => m, + Err(RecvError::Parse(len)) => { + if self.borsh_message_expected { + tracing::debug!(target: "network", "{:?} dropping a non protobuf message. Probably an extra handshake.", &self); + self.borsh_message_expected = false; + continue; + } else { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("error parsing protobuf of length {}", len), + )); + } + } + Err(RecvError::IO(e)) => return Err(e), + }; + match msg { + PeerMessage::Routed(r) => { + if let Some(msg) = self.recv_routed_msg(&r) { + return Ok((Message::Routed(msg), timestamp)); + } + } + PeerMessage::SyncRoutingTable(r) => { + return Ok(( + Message::Direct(DirectMessage::AnnounceAccounts(r.accounts)), + timestamp, + )); + } + PeerMessage::BlockRequest(hash) => { + return Ok((Message::Direct(DirectMessage::BlockRequest(hash)), timestamp)); + } + PeerMessage::Block(b) => { + return Ok((Message::Direct(DirectMessage::Block(b)), timestamp)); + } + PeerMessage::BlockHeadersRequest(hashes) => { + return Ok(( + Message::Direct(DirectMessage::BlockHeadersRequest(hashes)), + timestamp, + )); + } + PeerMessage::BlockHeaders(headers) => { + return Ok((Message::Direct(DirectMessage::BlockHeaders(headers)), timestamp)); + } + PeerMessage::VersionedStateResponse(state_response) => { + return Ok(( + Message::Direct(DirectMessage::VersionedStateResponse(state_response)), + timestamp, + )); + } + _ => {} + } + } + } + + pub fn peer_id(&self) -> &PeerId { + &self.peer_id + } +} + +#[derive(thiserror::Error, Debug)] +enum RecvError { + #[error(transparent)] + IO(#[from] std::io::Error), + #[error("error parsing protobuf of length {0}")] + Parse(usize), +} + +struct PeerStream { + stream: tcp::Stream, + buf: BytesMut, + recv_timeout: Duration, +} + +impl std::fmt::Debug for PeerStream { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "raw::PeerStream({} -> {})", &self.stream.local_addr, &self.stream.peer_addr) + } +} + +impl PeerStream { + fn new(stream: tcp::Stream, recv_timeout: Duration) -> Self { + Self { stream, buf: BytesMut::with_capacity(1024), recv_timeout } + } + + async fn write_message(&mut self, msg: &PeerMessage) -> io::Result<()> { + let mut msg = msg.serialize(Encoding::Proto); + let mut buf = (msg.len() as u32).to_le_bytes().to_vec(); + buf.append(&mut msg); + self.stream.stream.write_all(&buf).await + } + + async fn do_read(&mut self) -> io::Result<()> { + let n = tokio::time::timeout( + self.recv_timeout.try_into().unwrap(), + self.stream.stream.read_buf(&mut self.buf), + ) + .await??; + tracing::trace!(target: "network", "Read {} bytes from {:?}", n, self.stream.peer_addr); + if n == 0 { + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "no more bytes available, but expected to receive a message", + )); + } + Ok(()) + } + + // read at least 4 bytes, but probably the whole message in most cases + async fn read_msg_length(&mut self) -> io::Result<(usize, Instant)> { + let mut first_byte_time = None; + while self.buf.remaining() < 4 { + self.do_read().await?; + if first_byte_time.is_none() { + first_byte_time = Some(Instant::now()); + } + } + let len = u32::from_le_bytes(self.buf[..4].try_into().unwrap()); + // If first_byte_time is None, there were already 4 bytes from last time, + // and they must have come before a partial frame. + // So the Instant::now() is not quite correct, since the time was really in the past, + // but this is prob not so important + Ok((len as usize, first_byte_time.unwrap_or(Instant::now()))) + } + + // Reads from the socket until there is at least one full PeerMessage available. + async fn recv_message(&mut self) -> Result<(PeerMessage, Instant), RecvError> { + let (msg_length, first_byte_time) = self.read_msg_length().await?; + + while self.buf.remaining() < msg_length + 4 { + self.do_read().await?; + } + + self.buf.advance(4); + let msg = PeerMessage::deserialize(Encoding::Proto, &self.buf[..msg_length]); + self.buf.advance(msg_length); + + // make sure we can probably read the next message in one syscall next time + let max_len_after_next_read = self.buf.chunk_mut().len() + self.buf.remaining(); + if max_len_after_next_read < 512 { + self.buf.reserve(512 - max_len_after_next_read); + } + msg.map(|m| { + tracing::debug!(target: "network", "{:?} received PeerMessage::{} len: {}", &self, &m, msg_length); + (m, first_byte_time) + }) + .map_err(|_| RecvError::Parse(msg_length)) + } +} + +pub struct Listener { + listener: tcp::Listener, + secret_key: SecretKey, + chain_id: String, + genesis_hash: CryptoHash, + head_height: BlockHeight, + tracked_shards: Vec, + archival: bool, + recv_timeout: Duration, +} + +impl Listener { + pub async fn bind( + addr: tcp::ListenerAddr, + secret_key: SecretKey, + chain_id: &str, + genesis_hash: CryptoHash, + head_height: BlockHeight, + tracked_shards: Vec, + archival: bool, + recv_timeout: Duration, + ) -> io::Result { + Ok(Self { + listener: addr.listener()?, + secret_key, + chain_id: chain_id.to_string(), + genesis_hash, + head_height, + tracked_shards, + archival, + recv_timeout, + }) + } + + pub async fn accept(&mut self) -> Result { + let stream = self.listener.accept().await.map_err(ConnectError::IO)?; + Connection::on_accept( + stream, + self.secret_key.clone(), + &self.chain_id, + self.genesis_hash, + self.head_height, + self.tracked_shards.clone(), + self.archival, + self.recv_timeout, + ) + .await + } +} diff --git a/chain/network/src/raw/mod.rs b/chain/network/src/raw/mod.rs new file mode 100644 index 000000000..848168b6a --- /dev/null +++ b/chain/network/src/raw/mod.rs @@ -0,0 +1,6 @@ +mod connection; + +pub use connection::{ConnectError, Connection, DirectMessage, Listener, Message, RoutedMessage}; + +#[cfg(test)] +mod tests; diff --git a/chain/network/src/raw/tests.rs b/chain/network/src/raw/tests.rs new file mode 100644 index 000000000..58bbb5006 --- /dev/null +++ b/chain/network/src/raw/tests.rs @@ -0,0 +1,186 @@ +use crate::network_protocol::testonly as data; +use crate::raw; +use crate::tcp; +use crate::testonly; +use crate::types::PeerInfo; +use unc_async::time; +use unc_crypto::{KeyType, SecretKey}; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::PeerId; +use std::sync::Arc; + +#[tokio::test] +async fn test_raw_conn_pings() { + init_test_logger(); + let mut rng = testonly::make_rng(33955575545); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let cfg = chain.make_config(rng); + let peer_id = cfg.node_id(); + let addr = **cfg.node_addr.as_ref().unwrap(); + let genesis_id = chain.genesis_id.clone(); + let _pm = crate::peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + cfg, + chain, + ) + .await; + + let mut conn = raw::Connection::connect( + addr, + peer_id.clone(), + None, + &genesis_id.chain_id, + genesis_id.hash, + 0, + vec![0], + time::Duration::SECOND, + ) + .await + .unwrap(); + + let num_pings = 5; + for nonce in 1..num_pings { + conn.send_routed_message(raw::RoutedMessage::Ping { nonce }, peer_id.clone(), 2) + .await + .unwrap(); + } + + let mut nonce_received = 0; + loop { + let (msg, _timestamp) = conn.recv().await.unwrap(); + + if let raw::Message::Routed(raw::RoutedMessage::Pong { nonce, .. }) = msg { + if nonce != nonce_received + 1 { + panic!( + "received out of order nonce {} when {} was expected", + nonce, + nonce_received + 1 + ); + } + nonce_received = nonce; + if nonce == num_pings - 1 { + break; + } + } + } +} + +#[tokio::test] +async fn test_raw_conn_state_parts() { + init_test_logger(); + let mut rng = testonly::make_rng(33955575545); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + + let cfg = chain.make_config(rng); + let peer_id = cfg.node_id(); + let addr = **cfg.node_addr.as_ref().unwrap(); + let genesis_id = chain.genesis_id.clone(); + let _pm = crate::peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + cfg, + chain, + ) + .await; + + let mut conn = raw::Connection::connect( + addr, + peer_id.clone(), + None, + &genesis_id.chain_id, + genesis_id.hash, + 0, + vec![0], + time::Duration::SECOND, + ) + .await + .unwrap(); + + let num_parts = 5; + // Block hash needs to correspond to the hash of the first block of an epoch. + // But the fake node simply ignores the block hash. + let block_hash = CryptoHash::new(); + for part_id in 0..num_parts { + conn.send_message(raw::DirectMessage::StateRequestPart(0, block_hash, part_id)) + .await + .unwrap(); + } + + let mut part_id_received = -1i64; + loop { + match conn.recv().await { + Ok((msg, _timestamp)) => { + if let raw::Message::Direct(raw::DirectMessage::VersionedStateResponse( + state_response, + )) = msg + { + let response = state_response.take_state_response(); + let part_id = response.part_id(); + if part_id.is_none() || part_id.unwrap() as i64 != (part_id_received + 1) { + panic!( + "received out of order part_id {:?} when {} was expected", + part_id, + part_id_received + 1 + ); + } + part_id_received = part_id.unwrap() as i64; + if part_id_received + 1 == num_parts as i64 { + break; + } + } + } + Err(e) => { + panic!("error receiving part: {:?}", e); + } + } + } +} + +#[tokio::test] +async fn test_listener() { + init_test_logger(); + let mut rng = testonly::make_rng(33955575545); + let rng = &mut rng; + let mut clock = time::FakeClock::default(); + let chain = Arc::new(data::Chain::make(&mut clock, rng, 10)); + let mut cfg = chain.make_config(rng); + let genesis_id = chain.genesis_id.clone(); + + let addr = tcp::ListenerAddr::reserve_for_test(); + let secret_key = SecretKey::from_random(KeyType::ED25519); + let peer_id = PeerId::new(secret_key.public_key()); + cfg.peer_store.boot_nodes.push(PeerInfo::new(peer_id, *addr)); + cfg.outbound_disabled = false; + let _pm = crate::peer_manager::testonly::start( + clock.clock(), + unc_store::db::TestDB::new(), + cfg, + chain, + ) + .await; + + let mut l = raw::Listener::bind( + addr, + secret_key, + &genesis_id.chain_id, + genesis_id.hash, + 0, + vec![0], + false, + time::Duration::SECOND, + ) + .await + .unwrap(); + + let mut conn = l.accept().await.unwrap(); + // just test that we can receive some message, which checks that + // at least the handshake logic has gotten exercised somewhat + let _ = conn.recv().await.unwrap(); +} diff --git a/chain/network/src/routing/bfs.rs b/chain/network/src/routing/bfs.rs new file mode 100644 index 000000000..a4dd24ccc --- /dev/null +++ b/chain/network/src/routing/bfs.rs @@ -0,0 +1,478 @@ +use crate::peer_manager::peer_manager_actor::MAX_TIER2_PEERS; +use unc_primitives::network::PeerId; +use std::collections::hash_map::Entry; +use std::collections::{HashMap, HashSet, VecDeque}; +use tracing::warn; + +/// `Graph` is used to compute `peer_routing`, which contains information how to route messages to +/// all known peers. That is, for each `peer`, we get a sub-set of peers to which we are connected +/// to that are on the shortest path between us as destination `peer`. +#[derive(Clone)] +pub struct Graph { + /// `id` as integer corresponding to `my_peer_id`. + /// We use u32 to reduce both improve performance, and reduce memory usage. + source_id: u32, + /// Mapping from `PeerId` to `id` + p2id: HashMap, + /// List of existing `PeerId`s + id2p: Vec, + /// Which ids are currently in use + used: Vec, + /// List of unused peer ids + unused: Vec, + /// Compressed adjacency table, we use 32 bit integer as ids instead of using full `PeerId`. + /// This is undirected graph, we store edges in both directions. + adjacency: Vec>, + + /// Total number of edges used for stats. + total_active_edges: u64, +} + +impl Graph { + pub fn new(source: PeerId) -> Self { + let mut res = Self { + source_id: 0, + p2id: HashMap::default(), + id2p: Vec::default(), + used: Vec::default(), + unused: Vec::default(), + adjacency: Vec::default(), + total_active_edges: 0, + }; + res.id2p.push(source.clone()); + res.adjacency.push(Vec::default()); + res.p2id.insert(source, res.source_id); + res.used.push(true); + + res + } + + pub fn total_active_edges(&self) -> u64 { + self.total_active_edges + } + + // Compute number of active edges. We divide by 2 to remove duplicates. + #[cfg(test)] + pub fn compute_total_active_edges(&self) -> u64 { + let result: u64 = self.adjacency.iter().map(|x| x.len() as u64).sum(); + assert_eq!(result % 2, 0); + result / 2 + } + + fn contains_edge(&self, peer0: &PeerId, peer1: &PeerId) -> bool { + if let Some(&id0) = self.p2id.get(peer0) { + if let Some(&id1) = self.p2id.get(peer1) { + return self.adjacency[id0 as usize].contains(&id1); + } + } + false + } + + fn remove_if_unused(&mut self, id: u32) { + let entry = &self.adjacency[id as usize]; + + if entry.is_empty() && id != self.source_id { + self.used[id as usize] = false; + self.unused.push(id); + self.p2id.remove(&self.id2p[id as usize]); + } + } + + fn get_id(&mut self, peer: &PeerId) -> u32 { + match self.p2id.entry(peer.clone()) { + Entry::Occupied(occupied) => *occupied.get(), + Entry::Vacant(vacant) => { + let val = if let Some(val) = self.unused.pop() { + assert!(!self.used[val as usize]); + assert!(self.adjacency[val as usize].is_empty()); + self.id2p[val as usize] = peer.clone(); + self.used[val as usize] = true; + val + } else { + let val = self.id2p.len() as u32; + self.id2p.push(peer.clone()); + self.used.push(true); + self.adjacency.push(Vec::default()); + val + }; + + vacant.insert(val); + val + } + } + } + + pub fn add_edge(&mut self, peer0: &PeerId, peer1: &PeerId) { + assert_ne!(peer0, peer1); + if !self.contains_edge(peer0, peer1) { + let id0 = self.get_id(peer0); + let id1 = self.get_id(peer1); + + self.adjacency[id0 as usize].push(id1); + self.adjacency[id1 as usize].push(id0); + + self.total_active_edges += 1; + } + } + + pub fn remove_edge(&mut self, peer0: &PeerId, peer1: &PeerId) { + assert_ne!(peer0, peer1); + if self.contains_edge(peer0, peer1) { + let id0 = self.get_id(peer0); + let id1 = self.get_id(peer1); + + self.adjacency[id0 as usize].retain(|&x| x != id1); + self.adjacency[id1 as usize].retain(|&x| x != id0); + + self.remove_if_unused(id0); + self.remove_if_unused(id1); + + self.total_active_edges -= 1; + } + } + + /// Compute for every node `u` on the graph (other than `source`) which are the neighbors of + /// `sources` which belong to the shortest path from `source` to `u`. Nodes that are + /// not connected to `source` will not appear in the result. + pub fn calculate_next_hops_and_distance( + &self, + unreliable_peers: &HashSet, + ) -> (HashMap>, HashMap) { + // TODO add removal of unreachable nodes + + let unreliable_peers: HashSet<_> = + unreliable_peers.iter().filter_map(|peer_id| self.p2id.get(peer_id).cloned()).collect(); + + let mut queue = VecDeque::new(); + + let nodes = self.id2p.len(); + let mut distance: Vec = vec![-1; nodes]; + let mut routes: Vec = vec![0; nodes]; + + distance[self.source_id as usize] = 0; + + { + let neighbors = &self.adjacency[self.source_id as usize]; + for (id, &neighbor) in neighbors.iter().enumerate().take(MAX_TIER2_PEERS) { + if !unreliable_peers.contains(&neighbor) { + queue.push_back(neighbor); + } + + distance[neighbor as usize] = 1; + routes[neighbor as usize] = 1u128 << id; + } + } + + while let Some(cur_peer) = queue.pop_front() { + let cur_distance = distance[cur_peer as usize]; + + for &neighbor in &self.adjacency[cur_peer as usize] { + if distance[neighbor as usize] == -1 { + distance[neighbor as usize] = cur_distance + 1; + queue.push_back(neighbor); + } + // If this edge belong to a shortest path, all paths to + // the closer nodes are also valid for the current node. + if distance[neighbor as usize] == cur_distance + 1 { + routes[neighbor as usize] |= routes[cur_peer as usize]; + } + } + } + + // This takes 75% of the total time computation time of this function. + self.compute_result(&routes, &distance) + } + + /// Converts representation of the result, from an array representation, to + /// a hashmap of PeerId -> Vec + /// Arguments: + /// - routes - for node given node at index `i`, give list of connected peers, which + /// are on the optimal path + /// - distances - not really needed: TODO remove this argument + fn compute_result( + &self, + routes: &[u128], + distance: &[i32], + ) -> (HashMap>, HashMap) { + let mut res = HashMap::with_capacity(routes.len()); + let mut distances = HashMap::with_capacity(routes.len()); + + let neighbors = &self.adjacency[self.source_id as usize]; + let mut unreachable_nodes = 0; + + for (key, &cur_route) in routes.iter().enumerate() { + if distance[key] == -1 && self.used[key] { + unreachable_nodes += 1; + } + if key as u32 == self.source_id + || distance[key] == -1 + || cur_route == 0u128 + || !self.used[key] + { + continue; + } + // We convert list of peers, which are represented as bits + // to a list of Vec + // This is a bit wasteful representation, but that's ok. + let peer_set = neighbors + .iter() + .enumerate() + .take(MAX_TIER2_PEERS) + .filter(|(id, _)| (cur_route & (1u128 << id)) != 0) + .map(|(_, &neighbor)| self.id2p[neighbor as usize].clone()) + .collect(); + res.insert(self.id2p[key].clone(), peer_set); + distances.insert(self.id2p[key].clone(), distance[key] as u32); + } + if unreachable_nodes > 1000 { + warn!("We store more than 1000 unreachable nodes: {}", unreachable_nodes); + } + + (res, distances) + } + + #[cfg(test)] + pub fn calculate_distance( + &self, + unreliable_peers: &HashSet, + ) -> HashMap> { + let (next_hops, _) = self.calculate_next_hops_and_distance(&unreliable_peers); + next_hops + } +} + +#[cfg(test)] +mod test { + use super::Graph; + use crate::test_utils::{expected_routing_tables, random_peer_id}; + use std::collections::HashSet; + use std::ops::Not; + + #[test] + fn graph_contains_edge() { + let source = random_peer_id(); + + let node0 = random_peer_id(); + let node1 = random_peer_id(); + + let mut graph = Graph::new(source.clone()); + + assert!(graph.contains_edge(&source, &node0).not()); + assert!(graph.contains_edge(&source, &node1).not()); + assert!(graph.contains_edge(&node0, &node1).not()); + assert!(graph.contains_edge(&node1, &node0).not()); + + graph.add_edge(&node0, &node1); + + assert!(graph.contains_edge(&source, &node0).not()); + assert!(graph.contains_edge(&source, &node1).not()); + assert!(graph.contains_edge(&node0, &node1)); + assert!(graph.contains_edge(&node1, &node0)); + + graph.remove_edge(&node1, &node0); + + assert!(graph.contains_edge(&node0, &node1).not()); + assert!(graph.contains_edge(&node1, &node0).not()); + + assert_eq!(0, graph.total_active_edges() as usize); + assert_eq!(0, graph.compute_total_active_edges() as usize); + } + + #[test] + fn graph_distance0() { + let source = random_peer_id(); + let node0 = random_peer_id(); + + let mut graph = Graph::new(source.clone()); + graph.add_edge(&source, &node0); + graph.remove_edge(&source, &node0); + graph.add_edge(&source, &node0); + + assert!(expected_routing_tables( + &graph.calculate_distance(&HashSet::new()), + &[(node0.clone(), vec![node0.clone()])], + )); + + assert_eq!(1, graph.total_active_edges() as usize); + assert_eq!(1, graph.compute_total_active_edges() as usize); + } + + #[test] + fn graph_distance1() { + let source = random_peer_id(); + let nodes: Vec<_> = (0..3).map(|_| random_peer_id()).collect(); + + let mut graph = Graph::new(source); + + graph.add_edge(&nodes[0], &nodes[1]); + graph.add_edge(&nodes[2], &nodes[1]); + graph.add_edge(&nodes[1], &nodes[2]); + + assert!(expected_routing_tables(&graph.calculate_distance(&HashSet::new()), &[])); + + assert_eq!(2, graph.total_active_edges() as usize); + assert_eq!(2, graph.compute_total_active_edges() as usize); + } + + #[test] + fn graph_distance2() { + let source = random_peer_id(); + let nodes: Vec<_> = (0..3).map(|_| random_peer_id()).collect(); + + let mut graph = Graph::new(source.clone()); + + graph.add_edge(&nodes[0], &nodes[1]); + graph.add_edge(&nodes[2], &nodes[1]); + graph.add_edge(&nodes[1], &nodes[2]); + graph.add_edge(&source, &nodes[0]); + + assert!(expected_routing_tables( + &graph.calculate_distance(&HashSet::new()), + &[ + (nodes[0].clone(), vec![nodes[0].clone()]), + (nodes[1].clone(), vec![nodes[0].clone()]), + (nodes[2].clone(), vec![nodes[0].clone()]), + ], + )); + + assert_eq!(3, graph.total_active_edges() as usize); + assert_eq!(3, graph.compute_total_active_edges() as usize); + } + #[test] + fn graph_distance3() { + let source = random_peer_id(); + let nodes: Vec<_> = (0..3).map(|_| random_peer_id()).collect(); + + let mut graph = Graph::new(source.clone()); + + graph.add_edge(&nodes[0], &nodes[1]); + graph.add_edge(&nodes[2], &nodes[1]); + graph.add_edge(&nodes[0], &nodes[2]); + graph.add_edge(&source, &nodes[0]); + graph.add_edge(&source, &nodes[1]); + + assert!(expected_routing_tables( + &graph.calculate_distance(&HashSet::new()), + &[ + (nodes[0].clone(), vec![nodes[0].clone()]), + (nodes[1].clone(), vec![nodes[1].clone()]), + (nodes[2].clone(), vec![nodes[0].clone(), nodes[1].clone()]), + ], + )); + + assert_eq!(5, graph.total_active_edges() as usize); + assert_eq!(5, graph.compute_total_active_edges() as usize); + } + + /// Test the following graph + /// 0 - 3 - 6 + /// / x x + /// s - 1 - 4 - 7 + /// \ x x + /// 2 - 5 - 8 + /// + /// 9 - 10 (Dummy edge disconnected) + /// + /// There is a shortest path to nodes [3..9) going through 0, 1, and 2. + #[test] + fn graph_distance4() { + let source = random_peer_id(); + let nodes: Vec<_> = (0..11).map(|_| random_peer_id()).collect(); + + let mut graph = Graph::new(source.clone()); + + for node in &nodes[0..3] { + graph.add_edge(&source, node); + } + + for level in 0..2 { + for i in 0..3 { + for j in 0..3 { + graph.add_edge(&nodes[level * 3 + i], &nodes[level * 3 + 3 + j]); + } + } + } + + // Dummy edge. + graph.add_edge(&nodes[9], &nodes[10]); + + let mut next_hops: Vec<_> = + (0..3).map(|i| (nodes[i].clone(), vec![nodes[i].clone()])).collect(); + let target: Vec<_> = (0..3).map(|i| nodes[i].clone()).collect(); + + for node in &nodes[3..9] { + next_hops.push((node.clone(), target.clone())); + } + + assert!(expected_routing_tables(&graph.calculate_distance(&HashSet::new()), &next_hops)); + + assert_eq!(22, graph.total_active_edges() as usize); + assert_eq!(22, graph.compute_total_active_edges() as usize); + } + + // Same test as above, but node 0 is marked as bad. + // So it can be used only for its own messages. + #[test] + fn graph_distance4_with_unreliable_nodes() { + let source = random_peer_id(); + let nodes: Vec<_> = (0..11).map(|_| random_peer_id()).collect(); + + let mut graph = Graph::new(source.clone()); + + for node in &nodes[0..3] { + graph.add_edge(&source, node); + } + + for level in 0..2 { + for i in 0..3 { + for j in 0..3 { + graph.add_edge(&nodes[level * 3 + i], &nodes[level * 3 + 3 + j]); + } + } + } + + // Dummy edge. + graph.add_edge(&nodes[9], &nodes[10]); + let unreliable_peers = HashSet::from([nodes[0].clone()]); + + let mut next_hops: Vec<_> = + (0..3).map(|i| (nodes[i].clone(), vec![nodes[i].clone()])).collect(); + let target: Vec<_> = (1..3).map(|i| nodes[i].clone()).collect(); + + for node in &nodes[3..9] { + next_hops.push((node.clone(), target.clone())); + } + + assert!(expected_routing_tables(&graph.calculate_distance(&unreliable_peers), &next_hops)); + + assert_eq!(22, graph.total_active_edges() as usize); + assert_eq!(22, graph.compute_total_active_edges() as usize); + } + + // Test looks like this: + // s - 0 ----- 1 + // \--2 - 3 --/ + // When 0 is marked as unreliable, the calls to 1 should go via 2. + #[test] + fn graph_longer_distance_with_unreliable_nodes() { + let source = random_peer_id(); + let nodes: Vec<_> = (0..4).map(|_| random_peer_id()).collect(); + + let mut graph = Graph::new(source.clone()); + graph.add_edge(&source, &nodes[0]); + graph.add_edge(&source, &nodes[2]); + graph.add_edge(&nodes[2], &nodes[3]); + graph.add_edge(&nodes[3], &nodes[1]); + graph.add_edge(&nodes[0], &nodes[1]); + + let unreliable_peers = HashSet::from([nodes[0].clone()]); + + let next_hops = vec![ + (nodes[0].clone(), vec![nodes[0].clone()]), + (nodes[1].clone(), vec![nodes[2].clone()]), + (nodes[2].clone(), vec![nodes[2].clone()]), + (nodes[3].clone(), vec![nodes[2].clone()]), + ]; + assert!(expected_routing_tables(&graph.calculate_distance(&unreliable_peers), &next_hops)); + } +} diff --git a/chain/network/src/routing/edge.rs b/chain/network/src/routing/edge.rs new file mode 100644 index 000000000..f671bc07e --- /dev/null +++ b/chain/network/src/routing/edge.rs @@ -0,0 +1,40 @@ +use crate::network_protocol::{Edge, InvalidNonceError}; +use crate::stats::metrics; +use unc_async::time; + +// Don't accept nonces (edges) that are more than this delta from current time. +// This value should be smaller than PRUNE_EDGES_AFTER (otherwise, the might accept the edge and garbage collect it seconds later). +pub(crate) const EDGE_NONCE_MAX_TIME_DELTA: time::Duration = time::Duration::minutes(20); + +#[derive(thiserror::Error, Debug)] +pub(crate) enum VerifyNonceError { + #[error("{0}")] + InvalidNonce(#[source] InvalidNonceError), + #[error("nonce timestamp too distant in the future/past: got = {got}, now_timestamp = {now}, max_delta = {EDGE_NONCE_MAX_TIME_DELTA}")] + NonceTimestampTooDistant { got: time::Utc, now: time::Utc }, + #[error("nonce cannot be 0")] + ZeroNonce, +} + +pub(crate) fn verify_nonce(clock: &time::Clock, nonce: u64) -> Result<(), VerifyNonceError> { + if nonce == 0 { + return Err(VerifyNonceError::ZeroNonce); + } + match Edge::nonce_to_utc(nonce) { + Err(err) => Err(VerifyNonceError::InvalidNonce(err)), + Ok(Some(nonce)) => { + let now = clock.now_utc(); + if (now - nonce).abs() >= EDGE_NONCE_MAX_TIME_DELTA { + metrics::EDGE_NONCE.with_label_values(&["error_timestamp_too_distant"]).inc(); + Err(VerifyNonceError::NonceTimestampTooDistant { got: nonce, now }) + } else { + metrics::EDGE_NONCE.with_label_values(&["new_style"]).inc(); + Ok(()) + } + } + Ok(None) => { + metrics::EDGE_NONCE.with_label_values(&["old_style"]).inc(); + Ok(()) + } + } +} diff --git a/chain/network/src/routing/edge_cache/mod.rs b/chain/network/src/routing/edge_cache/mod.rs new file mode 100644 index 000000000..01fa2487d --- /dev/null +++ b/chain/network/src/routing/edge_cache/mod.rs @@ -0,0 +1,402 @@ +use crate::network_protocol::Edge; +use unc_primitives::network::PeerId; +use unc_primitives::views::{EdgeCacheView, LabeledEdgeView}; +use std::collections::hash_map::{Entry, Iter}; +use std::collections::{HashMap, HashSet}; + +#[cfg(test)] +mod testonly; +#[cfg(test)] +mod tests; + +// Connections in the network are bi-directional between a pair of peers (peer0, peer1). +// For keys in the EdgeCache, we maintain the invariant that peer0 < peer1 +#[derive(Clone, Eq, Hash, PartialEq, Debug)] +pub(crate) struct EdgeKey { + peer0: PeerId, + peer1: PeerId, +} + +impl From<&(PeerId, PeerId)> for EdgeKey { + fn from(peers: &(PeerId, PeerId)) -> EdgeKey { + let (peer0, peer1) = peers.clone(); + if peer0 < peer1 { + Self { peer0, peer1 } + } else { + Self { peer1, peer0 } + } + } +} + +/// The EdgeCache stores the latest spanning tree shared by each direct peer of the local node. +/// The trees are stored in `active_trees` as lists of EdgeKeys. +/// A separate map `active_edges` is kept mapping EdgeKeys to complete signed Edge objects. +/// This struct is used to store a signed Edge object along with a `refcount`; the number of +/// spanning trees which contain the edge. +#[derive(Clone)] +struct ActiveEdge { + edge: Edge, + refcount: u32, +} + +/// Cache of all known edges in the network. +/// +/// Edges in the network come to us in the form of signed tuples +/// (PeerId, PeerId, nonce: u64) +/// The two nodes connected by the edge both sign the tuple as proof of the connection's +/// existence. The nonce is a unix timestamp, letting us know the time at which the +/// connection was signed. +/// +/// We maintain multiple representations of the network, each serving different purposes. +/// +/// 1) `verified_nonces`: +/// A mapping from (PeerId, PeerId) to the latest nonce we have verified ourselves +/// for that pair of nodes. It allows the local node to avoid repeating computationally +/// expensive cryptographic verification of signatures. +/// +/// Storing only the verified nonce (and not the actual Edge object with signatures) +/// is a memory optimization. We can trust existence of these edges as we have seen and +/// verified the signatures locally at some point, but we cannot provide trustless proof +/// to a peer that these edges exist in the network. +/// +/// 2) `active_edges` +/// A mapping from (PeerId, PeerId) to complete Edge objects. It does not contain all known +/// edges, but rather a subset which the local node may wish to subsequently re-broadcast +/// to peers in the network. +/// +/// In particular, for each direct peer of the local node, the set of edges appearing in the +/// most recent spanning tree advertised by the peer are kept in memory. +/// +/// 3) `p2id` +/// A mapping from known PeerIds to distinct integer (u32) ids 0,1,2,... +/// The `p2id` mapping is used to allow indexing nodes into Vecs rather than HashMaps, +/// improving performance and reducing memory usage of the routing protocol implementation. +pub struct EdgeCache { + verified_nonces: im::HashMap, + active_edges: im::HashMap, + + // Mapping from neighbor PeerId to the latest spanning tree advertised by the peer, + // used to decide which edges are active. The key set of `active_edges` is the + // union of the value set of `active_trees`. + active_trees: HashMap>, + + /// Mapping from PeerId to assigned u32 id + pub(crate) p2id: HashMap, + /// Mapping from u32 id to the number of distinct active edges for the node + degree: Vec, + /// List of unused u32 ids + unused: Vec, +} + +impl EdgeCache { + pub fn new(local_node_id: PeerId) -> Self { + // Initializes the EdgeCache assigning id 0 to the local node + Self { + verified_nonces: Default::default(), + active_edges: Default::default(), + active_trees: HashMap::new(), + p2id: HashMap::from([(local_node_id, 0)]), + degree: vec![0], + unused: vec![], + } + } + + /// Accepts a verified edge and updates the state of `verified_nonces`. + pub fn write_verified_nonce(&mut self, edge: &Edge) { + self.verified_nonces.insert(edge.key().into(), edge.nonce()); + } + + /// Accepts an edge. Returns true iff we already have a verified nonce + /// for the edge's key which is at least as new as the edge's nonce. + pub fn has_edge_nonce_or_newer(&self, edge: &Edge) -> bool { + self.verified_nonces + .get(&edge.key().into()) + .map_or(false, |cached_nonce| cached_nonce >= &edge.nonce()) + } + + /// Returns the u32 id associated with the given PeerId. + /// Expects that an id was already assigned; will error otherwise. + pub(crate) fn get_id(&self, peer: &PeerId) -> u32 { + *self.p2id.get(peer).unwrap() + } + + /// Id 0 is always assigned to the local node. + pub(crate) fn get_local_node_id(&self) -> u32 { + 0 + } + + /// Returns the u32 id associated with the given PeerId, assigning one if necessary. + pub(crate) fn get_or_create_id(&mut self, peer: &PeerId) -> u32 { + match self.p2id.entry(peer.clone()) { + Entry::Occupied(occupied) => *occupied.get(), + Entry::Vacant(vacant) => { + let id = if let Some(id) = self.unused.pop() { + // If some unused id is available, take it + assert!(self.degree[id as usize] == 0); + id + } else { + // Otherwise, create a new one + let id = self.degree.len() as u32; + self.degree.push(0); + id + }; + + vacant.insert(id); + id + } + } + } + + /// Iterates over all peers appearing in the given spanning tree, + /// assigning ids to those which don't have one already. + pub(crate) fn create_ids_for_tree(&mut self, root: &PeerId, edges: &Vec) { + self.get_or_create_id(root); + + edges.iter().for_each(|edge| { + let (peer0, peer1) = edge.key(); + self.get_or_create_id(peer0); + self.get_or_create_id(peer1); + }); + } + + /// Checks for and frees any assigned ids which have degree 0. + /// Id 0 remains assigned to the local node, regardless of degree. + pub(crate) fn free_unused_ids(&mut self) { + assert!(self.get_local_node_id() == 0); + + // Erase entries from the `p2id` map + self.p2id.retain(|_, id| *id == 0 || self.degree[*id as usize] != 0); + + // Shrink max_id if possible + let mut max_id = self.max_id(); + while max_id >= 2 && self.degree[max_id - 1] == 0 { + max_id -= 1; + } + self.degree.truncate(max_id); + + // Reconstruct the list of unused ids + self.unused.clear(); + for id in 1..self.max_id() { + if self.degree[id] == 0 { + self.unused.push(id as u32); + } + } + } + + /// Called when storing an active edge. + /// Increments the degrees for the connected peers, assigning ids if necessary. + fn increment_degrees_for_key(&mut self, key: &EdgeKey) { + let id0 = self.get_or_create_id(&key.peer0) as usize; + let id1 = self.get_or_create_id(&key.peer1) as usize; + self.degree[id0] += 1; + self.degree[id1] += 1; + } + + /// Called when erasing an active edge. + /// Decrements the degrees for the connected peers. + fn decrement_degrees_for_key(&mut self, key: &EdgeKey) { + self.decrement_degree(&key.peer0); + self.decrement_degree(&key.peer1); + } + + /// Decrements the degree for the given peer. + /// If the degree reaches 0, frees the peer's id for reuse. + fn decrement_degree(&mut self, peer_id: &PeerId) { + let id = self.get_id(peer_id) as usize; + assert!(self.degree[id] > 0); + self.degree[id] -= 1; + + // If the degree for the id reaches 0, free it. + // The local node is always mapped to 0. + if self.degree[id] == 0 && id != 0 { + self.p2id.remove(peer_id); + self.unused.push(id as u32); + } + } + + /// Inserts a copy of the given edge to `active_edges`. + /// If it's the first copy, increments degrees for the incident nodes. + fn insert_active_edge(&mut self, edge: &Edge) { + let is_newly_active = match self.active_edges.entry(edge.key().into()) { + im::hashmap::Entry::Occupied(mut occupied) => { + let val: &mut ActiveEdge = occupied.get_mut(); + if edge.nonce() > val.edge.nonce() { + val.edge = edge.clone(); + } + val.refcount += 1; + false + } + im::hashmap::Entry::Vacant(vacant) => { + vacant.insert(ActiveEdge { edge: edge.clone(), refcount: 1 }); + true + } + }; + if is_newly_active { + self.increment_degrees_for_key(&edge.key().into()); + } + } + + /// Removes an edge with the given EdgeKey from the active edge cache. + /// If the last such edge is removed, decrements degrees for the incident nodes. + fn remove_active_edge(&mut self, key: &EdgeKey) { + let is_newly_inactive = match self.active_edges.entry(key.clone()) { + im::hashmap::Entry::Occupied(mut occupied) => { + let val: &mut ActiveEdge = occupied.get_mut(); + assert!(val.refcount > 0); + if val.refcount == 1 { + occupied.remove_entry(); + true + } else { + val.refcount -= 1; + false + } + } + im::hashmap::Entry::Vacant(_) => { + assert!(false); + false + } + }; + if is_newly_inactive { + self.decrement_degrees_for_key(key); + } + } + + /// Stores the key-value pair (peer_id, edges) in the EdgeCache's `active_trees` map, overwriting + /// any previous entry for the same peer. Updates `active_edges` accordingly. + pub fn update_tree(&mut self, peer_id: &PeerId, tree: &Vec) { + // Insert the new edges before removing any old ones. + // Nodes are pruned from the `p2id` mapping as soon as all edges incident with them are + // removed. If we removed the edges in the old tree first, we might unlabel and relabel a + // node unnecessarily. Inserting the new edges first minimizes churn on `p2id`. + for edge in tree { + self.insert_active_edge(edge); + } + + let edge_keys: Vec = tree.iter().map(|edge| edge.key().into()).collect(); + + if let Some(old_edge_keys) = self.active_trees.insert(peer_id.clone(), edge_keys) { + // If a previous tree was present, process removal of its edges + for key in &old_edge_keys { + self.remove_active_edge(key); + } + } + } + + /// If we have a tree stored for the given peer, + /// returns the min nonce among the edges in the tree. + /// + /// Returns None if no tree is stored. + /// Returns None if for any edge in the tree, no nonce is available. + pub fn get_min_nonce(&mut self, peer_id: &PeerId) -> Option { + let edge_keys = self.active_trees.get(peer_id)?; + + let mut min_nonce: Option = None; + + for key in edge_keys { + let nonce = *self.verified_nonces.get(key)?; + min_nonce = Some(min_nonce.map_or(nonce, |min_val| std::cmp::min(min_val, nonce))); + } + + min_nonce + } + + /// Removes the tree stored for the given peer, if there is one. + pub fn remove_tree(&mut self, peer_id: &PeerId) { + if let Some(edges) = self.active_trees.remove(peer_id) { + for e in &edges { + self.remove_active_edge(e); + } + } + } + + /// Upper bound on mapped u32 ids; not inclusive + pub fn max_id(&self) -> usize { + self.degree.len() + } + + /// Iterator over the (PeerId, u32) mapping + pub fn iter_peers(&self) -> Iter<'_, PeerId, u32> { + self.p2id.iter() + } + + /// Number of known edges in the network + pub fn known_edges_ct(&self) -> usize { + self.verified_nonces.len() + } + + /// Prunes entries with nonces older than `prune_nonces_older_than` + /// from `verified_nonces` + pub fn prune_old_edges(&mut self, prune_nonces_older_than: u64) { + // Drop any entries with old nonces from the verified_nonces cache + self.verified_nonces.retain(|_, nonce| nonce >= &prune_nonces_older_than); + } + + /// Accepts a mapping over the set of reachable PeerIds in the network + /// to the shortest path lengths to those peers. + /// + /// Constructs a tree from among the `active_edges` which has the same + /// reachability and the same distances or better. + /// + /// Returns None if the input is inconsistent with the state of the cache + /// (reachability or distances are not consistent with the `active_edges`). + pub fn construct_spanning_tree(&self, distance: &HashMap) -> Option> { + let mut edges = Vec::::new(); + let mut has_edge = HashSet::::new(); + + // Make sure some node has distance 0 + let mut has_root = false; + for (_, val) in distance { + if *val == 0 { + has_root = true; + } + } + + // Iterate through the known useful edges. + // We want to find for each node in the tree some edge + // which connects it to another node with smaller distance. + for (edge_key, active_edge) in &self.active_edges { + if let (Some(dist0), Some(dist1)) = + (distance.get(&edge_key.peer0), distance.get(&edge_key.peer1)) + { + if dist0 < dist1 && !has_edge.contains(&edge_key.peer1) { + has_edge.insert(edge_key.peer1.clone()); + edges.push(active_edge.edge.clone()); + } + + if dist1 < dist0 && !has_edge.contains(&edge_key.peer0) { + has_edge.insert(edge_key.peer0.clone()); + edges.push(active_edge.edge.clone()); + } + } + } + + if has_root && has_edge.len() + 1 == distance.len() { + Some(edges) + } else { + None + } + } + + pub(crate) fn get_debug_view(&self) -> EdgeCacheView { + EdgeCacheView { + peer_labels: self.p2id.clone(), + spanning_trees: self + .active_trees + .iter() + .map(|(peer_id, edge_keys)| { + ( + self.get_id(&peer_id), + edge_keys + .iter() + .map(|key| LabeledEdgeView { + peer0: self.get_id(&key.peer0), + peer1: self.get_id(&key.peer1), + nonce: *self.verified_nonces.get(&key).unwrap(), + }) + .collect(), + ) + }) + .collect(), + } + } +} diff --git a/chain/network/src/routing/edge_cache/testonly.rs b/chain/network/src/routing/edge_cache/testonly.rs new file mode 100644 index 000000000..aa5d6f531 --- /dev/null +++ b/chain/network/src/routing/edge_cache/testonly.rs @@ -0,0 +1,61 @@ +use crate::routing::edge_cache::{EdgeCache, EdgeKey}; +use crate::types::Edge; +use unc_primitives::network::PeerId; + +impl EdgeCache { + pub(crate) fn is_active(&self, edge: &Edge) -> bool { + self.active_edges.contains_key(&edge.key().into()) + } + + pub(crate) fn get_nonce_for_active_edge(&self, key: &EdgeKey) -> Option { + self.active_edges.get(key).map(|val| val.edge.nonce()) + } + + pub(crate) fn check_mapping_external(&self, mapped_nodes: &Vec) { + // Check the mapped ids for externally visible properties of the mapping + let mut assigned_ids: Vec = + mapped_nodes.iter().map(|peer_id| self.get_id(peer_id)).collect(); + assigned_ids.sort(); + assigned_ids.dedup(); + assert_eq!(mapped_nodes.len(), assigned_ids.len()); + for id in assigned_ids { + assert!(id < (self.max_id() as u32)); + } + } + + pub(crate) fn check_mapping_internal(&self, mapped_nodes: &Vec) { + // Check internally that the set of mapped nodes is exactly those which are expected + assert_eq!(mapped_nodes.len(), self.p2id.len()); + for peer_id in mapped_nodes { + assert!(self.p2id.contains_key(&peer_id)); + } + + // Check internally that the mapped ids and unused ids together are precisely 0,1,2,... + let universe = Vec::from_iter(0..(self.max_id() as u32)); + let mut actual_ids: Vec = self.p2id.values().cloned().collect(); + actual_ids.append(&mut self.unused.clone()); + actual_ids.sort(); + assert_eq!(universe, actual_ids); + + // An id should be in use iff it's id 0 (the local node's id) + // or if it's assigned to some node incident with an active edge + for id in universe { + let should_be_used = id == 0 || self.degree[id as usize] != 0; + assert_eq!(should_be_used, !self.unused.contains(&id)); + } + + // Check exact consistency of the degree counts with the active edges + let mut expected_degree = vec![0; self.max_id()]; + for (key, edge) in &self.active_edges { + assert!(edge.refcount > 0); + expected_degree[self.get_id(&key.peer0) as usize] += 1; + expected_degree[self.get_id(&key.peer1) as usize] += 1; + } + assert_eq!(expected_degree, self.degree); + } + + pub(crate) fn check_mapping(&self, mapped_nodes: Vec) { + self.check_mapping_external(&mapped_nodes); + self.check_mapping_internal(&mapped_nodes); + } +} diff --git a/chain/network/src/routing/edge_cache/tests.rs b/chain/network/src/routing/edge_cache/tests.rs new file mode 100644 index 000000000..3c9d5d08e --- /dev/null +++ b/chain/network/src/routing/edge_cache/tests.rs @@ -0,0 +1,282 @@ +use crate::routing::edge_cache::*; +use crate::test_utils::random_peer_id; +use crate::testonly::make_rng; +use rand::Rng; +use std::collections::HashSet; + +#[test] +fn has_edge_nonce_or_newer() { + let node0 = random_peer_id(); + let node1 = random_peer_id(); + + let edge0 = Edge::make_fake_edge(node0.clone(), node1.clone(), 123); + let edge1 = Edge::make_fake_edge(node0.clone(), node1, 456); + + let mut ec = EdgeCache::new(node0); + + // Initially empty + assert!(!ec.has_edge_nonce_or_newer(&edge0)); + assert!(!ec.has_edge_nonce_or_newer(&edge1)); + + // Write the older nonce + ec.write_verified_nonce(&edge0); + assert!(ec.has_edge_nonce_or_newer(&edge0)); + assert!(!ec.has_edge_nonce_or_newer(&edge1)); + + // Write the newer nonce + ec.write_verified_nonce(&edge1); + assert!(ec.has_edge_nonce_or_newer(&edge0)); + assert!(ec.has_edge_nonce_or_newer(&edge1)); +} + +#[test] +fn update_active_edge_nonce() { + let node0 = random_peer_id(); + let node1 = random_peer_id(); + + let edge0 = Edge::make_fake_edge(node0.clone(), node1.clone(), 123); + let edge1 = Edge::make_fake_edge(node0.clone(), node1, 456); + + assert_eq!(edge0.key(), edge1.key()); + let key: EdgeKey = edge0.key().into(); + + let mut ec = EdgeCache::new(node0); + + // First insert with the older nonce + ec.insert_active_edge(&edge0); + assert_eq!(Some(123), ec.get_nonce_for_active_edge(&key)); + + // Insert another copy of the same edge with a newer nonce + ec.insert_active_edge(&edge1); + assert_eq!(Some(456), ec.get_nonce_for_active_edge(&key)); + + // Insert with the older nonce again; should not overwrite + ec.insert_active_edge(&edge0); + assert_eq!(Some(456), ec.get_nonce_for_active_edge(&key)); + + // Remove a copy; should still remember it + ec.remove_active_edge(&key); + assert_eq!(Some(456), ec.get_nonce_for_active_edge(&key)); + + // Remove another copy; should still remember it + ec.remove_active_edge(&key); + assert_eq!(Some(456), ec.get_nonce_for_active_edge(&key)); + + // Remove final copy + ec.remove_active_edge(&key); + assert_eq!(None, ec.get_nonce_for_active_edge(&key)); +} + +#[test] +fn test_p2id_mapping() { + let node0 = random_peer_id(); + let node1 = random_peer_id(); + let node2 = random_peer_id(); + let node3 = random_peer_id(); + + // Set up a simple line graph 0--1--2--3 + let edge0 = Edge::make_fake_edge(node0.clone(), node1.clone(), 123); + let edge1 = Edge::make_fake_edge(node1.clone(), node2.clone(), 456); + let edge2 = Edge::make_fake_edge(node2.clone(), node3.clone(), 789); + + // Set up the EdgeCache with node0 as the local node + let mut ec = EdgeCache::new(node0.clone()); + ec.check_mapping(vec![node0.clone()]); + + // Insert and remove a single edge + ec.insert_active_edge(&edge0); + ec.check_mapping(vec![node0.clone(), node1.clone()]); + ec.remove_active_edge(&edge0.key().into()); + ec.check_mapping(vec![node0.clone()]); + + // Insert all edges (0--1--2--3) + ec.insert_active_edge(&edge0); + ec.insert_active_edge(&edge1); + ec.insert_active_edge(&edge2); + ec.check_mapping(vec![node0.clone(), node1.clone(), node2.clone(), node3.clone()]); + + // Remove edge1; all nodes are still active (0--1 2--3) + ec.remove_active_edge(&edge1.key().into()); + ec.check_mapping(vec![node0.clone(), node1.clone(), node2.clone(), node3.clone()]); + + // Remove edge0; node1 will no longer be active (0 1 2--3) + ec.remove_active_edge(&edge0.key().into()); + ec.check_mapping(vec![node0.clone(), node2.clone(), node3.clone()]); + + // Insert edge1; reactivates node1 (0 1--2--3) + ec.insert_active_edge(&edge1); + ec.check_mapping(vec![node0.clone(), node1.clone(), node2.clone(), node3]); + + // Remove edge2; deactivates only node2 (0 1--2 3) + ec.remove_active_edge(&edge2.key().into()); + ec.check_mapping(vec![node0.clone(), node1, node2]); + + // Remove edge1; only the local node should remain mapped (0 1 2 3) + ec.remove_active_edge(&edge1.key().into()); + ec.check_mapping(vec![node0]); +} + +#[test] +fn reuse_ids() { + let max_node_ct = 5; + + let mut rng = make_rng(921853233); + let rng = &mut rng; + + let local_node_id = random_peer_id(); + let mut ec = EdgeCache::new(local_node_id.clone()); + + // Run multiple iterations of inserting and deleting sets of edges + for _ in 0..25 { + // Generate some random PeerIds; should have at least 2 so we can make some edges + let node_ct = rng.gen::() % (max_node_ct - 2) + 2; + let mut peer_ids: Vec = (1..node_ct).map(|_| random_peer_id()).collect(); + peer_ids.push(local_node_id.clone()); + + // Generate some random edges + let edge_ct = rng.gen::() % 10 + 0; + let edges: Vec = (0..edge_ct) + .map(|_| { + // Generate two distinct indices at random in (0..node_ct) + let peer0 = rng.gen::() % (node_ct - 1); + let peer1 = peer0 + 1 + (rng.gen::() % (node_ct - peer0 - 1)); + + // Make an edge with the chosen nodes and a random nonce + Edge::make_fake_edge( + peer_ids[peer0].clone(), + peer_ids[peer1].clone(), + rng.gen::(), + ) + }) + .collect(); + + let mut active = HashSet::::from([local_node_id.clone()]); + for e in &edges { + // Insert the edge to the EdgeCache + ec.insert_active_edge(e); + + // Update our record of active nodes + let (peer0, peer1) = e.key().clone(); + active.insert(peer0); + active.insert(peer1); + + ec.check_mapping(Vec::from_iter(active.clone())); + + // u32 ids should be reused across iterations + assert!(ec.max_id() <= max_node_ct); + } + + for e in &edges { + ec.remove_active_edge(&e.key().into()); + } + } +} + +#[test] +fn free_unused_after_create_for_tree() { + let node0 = random_peer_id(); + let node1 = random_peer_id(); + let node2 = random_peer_id(); + + let edge = Edge::make_fake_edge(node1.clone(), node2.clone(), 123); + + // Initialize the edge cache and check that just the local node has an id + let mut ec = EdgeCache::new(node0.clone()); + ec.check_mapping(vec![node0.clone()]); + + // Create and check ids for the tree 1--2 + ec.create_ids_for_tree(&node1, &vec![edge]); + ec.check_mapping_external(&vec![node0.clone(), node1, node2]); + + // Free unused ids + ec.free_unused_ids(); + ec.check_mapping(vec![node0]); +} + +#[test] +fn overwrite_shortest_path_tree() { + let node0 = random_peer_id(); + let node1 = random_peer_id(); + let node2 = random_peer_id(); + + let mut ec = EdgeCache::new(node0.clone()); + + let edge0 = Edge::make_fake_edge(node0, node1.clone(), 123); + let edge1 = Edge::make_fake_edge(node1.clone(), node2.clone(), 123); + + // Write an SPT for node1 advertising node2 behind it; 0--1--2 + ec.update_tree(&node1, &vec![edge0.clone(), edge1.clone()]); + + assert!(ec.is_active(&edge1)); + assert!(ec.p2id.contains_key(&node2)); + + // Now write an SPT for node1 without the connection to node2; 0--1 2 + ec.update_tree(&node1, &vec![edge0]); + + // edge1 should have been pruned from node0's `active_edges` map + assert!(!ec.is_active(&edge1)); + // node2 should have been pruned from node0's `p2id` mapping + assert!(!ec.p2id.contains_key(&node2)); +} + +fn assert_eq_unordered(a: Vec, b: Vec) { + for x in &a { + assert!(b.contains(x)); + } + for x in &b { + assert!(a.contains(x)); + } +} + +#[test] +fn test_construct_shortest_path_tree() { + let node0 = random_peer_id(); + let node1 = random_peer_id(); + let node2 = random_peer_id(); + let node3 = random_peer_id(); + + // Set up a simple line graph 0--1--2--3 + let edge0 = Edge::make_fake_edge(node0.clone(), node1.clone(), 123); + let edge1 = Edge::make_fake_edge(node1.clone(), node2.clone(), 456); + let edge2 = Edge::make_fake_edge(node2.clone(), node3.clone(), 789); + + // Set up the EdgeCache with node0 as the local node + let mut ec = EdgeCache::new(node0.clone()); + ec.check_mapping(vec![node0.clone()]); + + // Insert the edges to the cache + ec.insert_active_edge(&edge0); + ec.insert_active_edge(&edge1); + ec.insert_active_edge(&edge2); + + // Construct tree 0--1--2--3 + assert_eq_unordered( + ec.construct_spanning_tree(&HashMap::from([ + (node0.clone(), 0), + (node1.clone(), 1), + (node2.clone(), 2), + (node3.clone(), 3), + ])) + .unwrap(), + vec![edge0.clone(), edge1.clone(), edge2.clone()], + ); + + // Add direct edges to node2 and node3 + let edge02 = Edge::make_fake_edge(node0.clone(), node2.clone(), 123); + let edge03 = Edge::make_fake_edge(node0.clone(), node3.clone(), 456); + + ec.insert_active_edge(&edge02); + ec.insert_active_edge(&edge03); + + // Construct tree 0--{1,2,3} + assert_eq_unordered( + ec.construct_spanning_tree(&HashMap::from([ + (node0, 0), + (node1, 1), + (node2, 1), + (node3, 1), + ])) + .unwrap(), + vec![edge0, edge02, edge03], + ); +} diff --git a/chain/network/src/routing/graph/mod.rs b/chain/network/src/routing/graph/mod.rs new file mode 100644 index 000000000..49d8b8c1a --- /dev/null +++ b/chain/network/src/routing/graph/mod.rs @@ -0,0 +1,360 @@ +use crate::concurrency; +use crate::concurrency::runtime::Runtime; +use crate::network_protocol::{Edge, EdgeState}; +use crate::routing::bfs; +use crate::routing::routing_table_view::RoutingTableView; +use crate::stats::metrics; +use crate::store; +use arc_swap::ArcSwap; +use unc_async::time; +use unc_primitives::network::PeerId; +use parking_lot::Mutex; +use rayon::iter::ParallelBridge; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; + +#[cfg(test)] +mod tests; + +// TODO: make it opaque, so that the key.0 < key.1 invariant is protected. +type EdgeKey = (PeerId, PeerId); +pub type NextHopTable = HashMap>; +pub type DistanceTable = HashMap; + +#[derive(Clone)] +pub struct GraphConfig { + pub node_id: PeerId, + pub prune_unreachable_peers_after: time::Duration, + pub prune_edges_after: Option, +} + +#[derive(Default)] +pub struct GraphSnapshot { + pub edges: im::HashMap, + pub local_edges: HashMap, + pub next_hops: Arc, + pub distances: Arc, +} + +struct Inner { + config: GraphConfig, + + /// Current view of the network represented by an undirected graph. + /// Contains only validated edges. + /// Nodes are Peers and edges are active connections. + graph: bfs::Graph, + + edges: im::HashMap, + /// Last time a peer was reachable. + peer_reachable_at: HashMap, + store: store::Store, +} + +fn has(set: &im::HashMap, edge: &Edge) -> bool { + set.get(&edge.key()).map_or(false, |x| x.nonce() >= edge.nonce()) +} + +impl Inner { + /// Adds an edge without validating the signatures. O(1). + /// Returns true, iff was newer than an already known version of this edge. + fn update_edge(&mut self, now: time::Utc, edge: Edge) -> bool { + if has(&self.edges, &edge) { + return false; + } + if let Some(prune_edges_after) = self.config.prune_edges_after { + // Don't add edges that are older than the limit. + if edge.is_edge_older_than(now - prune_edges_after) { + return false; + } + } + let key = edge.key(); + // Add the edge. + match edge.edge_type() { + EdgeState::Active => self.graph.add_edge(&key.0, &key.1), + EdgeState::Removed => self.graph.remove_edge(&key.0, &key.1), + } + self.edges.insert(key.clone(), edge); + true + } + + /// Removes an edge by key. O(1). + fn remove_edge(&mut self, key: &EdgeKey) { + if self.edges.remove(key).is_some() { + self.graph.remove_edge(&key.0, &key.1); + } + } + + /// Removes all edges adjacent to the peers from the set. + /// It is used to prune unreachable connected components from the inmem graph. + fn remove_adjacent_edges(&mut self, peers: &HashSet) -> Vec { + let mut edges = vec![]; + for e in self.edges.clone().values() { + if peers.contains(&e.key().0) || peers.contains(&e.key().1) { + self.remove_edge(e.key()); + edges.push(e.clone()); + } + } + edges + } + + fn prune_old_edges(&mut self, prune_edges_older_than: time::Utc) { + for e in self.edges.clone().values() { + if e.is_edge_older_than(prune_edges_older_than) { + self.remove_edge(e.key()); + } + } + } + + /// If peer_id is not in memory check if it is on disk in bring it back on memory. + /// + /// Note: here an advanced example, which shows what's happening. + /// Let's say we have a full graph fully connected with nodes `A, B, C, D`. + /// Step 1 ) `A`, `B` get removed. + /// We store edges belonging to `A` and `B`: `, , , , ` + /// into component 1 let's call it `C_1`. + /// And mapping from `A` to `C_1`, and from `B` to `C_1` + /// + /// Note that `C`, `D` is still active. + /// + /// Step 2) 'C' gets removed. + /// We stored edges into component 2 `C_2`. + /// And a mapping from `C` to `C_2`. + /// + /// Note that `D` is still active. + /// + /// Step 3) An active edge gets added from `D` to `A`. + /// We will load `C_1` and try to re-add all edges belonging to `C_1`. + /// We will add `, , , , ` + /// + /// Important note: `C_1` also contains an edge from `A` to `C`, though `C` was removed in `C_2`. + /// - 1) We will not load edges belonging to `C_2`, even though we are adding an edges from `A` to deleted `C`. + /// - 2) We will not delete mapping from `C` to `C_2`, because `C` doesn't belong to `C_1`. + /// - 3) Later, `C` will be deleted, because we will figure out it's not reachable. + /// New component `C_3` will be created. + /// And mapping from `C` to `C_2` will be overridden by mapping from `C` to `C_3`. + /// And therefore `C_2` component will become unreachable. + /// TODO(gprusak): this whole algorithm seems to be leaking stuff to storage and never cleaning up. + /// What is the point of it? What does it actually gives us? + fn load_component(&mut self, now: time::Utc, peer_id: PeerId) { + if peer_id == self.config.node_id || self.peer_reachable_at.contains_key(&peer_id) { + return; + } + let edges = match self.store.pop_component(&peer_id) { + Ok(edges) => edges, + Err(e) => { + tracing::warn!("self.store.pop_component({}): {}", peer_id, e); + return; + } + }; + for e in edges { + self.update_edge(now, e); + } + } + + /// Prunes peers unreachable since (and their adjacent edges) + /// from the in-mem graph and stores them in DB. + fn prune_unreachable_peers(&mut self, unreachable_since: time::Instant) { + // Select peers to prune. + let mut peers = HashSet::new(); + for k in self.edges.keys() { + for peer_id in [&k.0, &k.1] { + if self + .peer_reachable_at + .get(peer_id) + .map(|t| t < &unreachable_since) + .unwrap_or(true) + { + peers.insert(peer_id.clone()); + } + } + } + if peers.is_empty() { + return; + } + + // Prune peers from peer_reachable_at. + for peer_id in &peers { + self.peer_reachable_at.remove(&peer_id); + } + + // Prune edges from graph. + let edges = self.remove_adjacent_edges(&peers); + + // Store the pruned data in DB. + if let Err(e) = self.store.push_component(&peers, &edges) { + tracing::warn!("self.store.push_component(): {}", e); + } + } + + /// Verifies edges, then adds them to the graph. + /// Returns a list of newly added edges (not known so far), which should be broadcasted. + /// Returns true iff all the edges provided were valid. + /// + /// This method implements a security measure against an adversary sending invalid edges: + /// * it deduplicates edges and drops known edges before verification, because verification is expensive. + /// * it verifies edges in parallel until the first invalid edge is found. It adds the edges + /// verified so far (and returns them), but drops all the remaining ones. This way the + /// wasted work (verification of invalid edges) is constant, no matter how large the input + /// size is. + fn add_edges(&mut self, clock: &time::Clock, mut edges: Vec) -> (Vec, bool) { + metrics::EDGE_UPDATES.inc_by(edges.len() as u64); + // Start with deduplicating the edges. + // TODO(gprusak): sending duplicate edges should be considered a malicious behavior + // instead, however that would be backward incompatible, so it can be introduced in + // PROTOCOL_VERSION 60 earliest. + edges = Edge::deduplicate(edges); + + // load the components BEFORE updating the edges. + // so that result doesn't contain edges we already have in storage. + // It is especially important for initial full sync with peers, because + // we broadcast all the returned edges to all connected peers. + let now = clock.now_utc(); + for edge in &edges { + let key = edge.key(); + self.load_component(now, key.0.clone()); + self.load_component(now, key.1.clone()); + } + + // Retain only new edges. + edges.retain(|e| !has(&self.edges, e)); + + // Verify the edges in parallel on rayon. + // Stop at first invalid edge. + let (mut edges, ok) = concurrency::rayon::run_blocking(move || { + concurrency::rayon::try_map(edges.into_iter().par_bridge(), |e| { + if e.verify() { + Some(e) + } else { + None + } + }) + }); + + // Add the verified edges to the graph. + edges.retain(|e| self.update_edge(now, e.clone())); + (edges, ok) + } + + /// 1. Prunes expired edges. + /// 2. Prunes unreachable graph components. + /// 3. Recomputes GraphSnapshot. + pub fn update( + &mut self, + clock: &time::Clock, + unreliable_peers: &HashSet, + ) -> GraphSnapshot { + let _update_time = metrics::ROUTING_TABLE_RECALCULATION_HISTOGRAM.start_timer(); + // Update metrics after edge update + if let Some(prune_edges_after) = self.config.prune_edges_after { + self.prune_old_edges(clock.now_utc() - prune_edges_after); + } + + let (next_hops, distances) = self.graph.calculate_next_hops_and_distance(unreliable_peers); + let next_hops = Arc::new(next_hops); + let distances = Arc::new(distances); + + // Update peer_reachable_at. + let now = clock.now(); + self.peer_reachable_at.insert(self.config.node_id.clone(), now); + for peer in next_hops.keys() { + self.peer_reachable_at.insert(peer.clone(), now); + } + if let Some(unreachable_since) = now.checked_sub(self.config.prune_unreachable_peers_after) + { + self.prune_unreachable_peers(unreachable_since); + } + let mut local_edges = HashMap::new(); + for e in self.edges.clone().values() { + if let Some(other) = e.other(&self.config.node_id) { + local_edges.insert(other.clone(), e.clone()); + } + } + metrics::ROUTING_TABLE_RECALCULATIONS.inc(); + metrics::PEER_REACHABLE.set(next_hops.len() as i64); + metrics::EDGE_ACTIVE.set(self.graph.total_active_edges() as i64); + metrics::EDGE_TOTAL.set(self.edges.len() as i64); + GraphSnapshot { edges: self.edges.clone(), local_edges, next_hops, distances } + } +} + +pub(crate) struct Graph { + inner: Arc>, + snapshot: ArcSwap, + unreliable_peers: ArcSwap>, + pub routing_table: RoutingTableView, + + runtime: Runtime, +} + +impl Graph { + pub fn new(config: GraphConfig, store: store::Store) -> Self { + Self { + routing_table: RoutingTableView::new(), + inner: Arc::new(Mutex::new(Inner { + graph: bfs::Graph::new(config.node_id.clone()), + config, + edges: Default::default(), + peer_reachable_at: HashMap::new(), + store, + })), + unreliable_peers: ArcSwap::default(), + snapshot: ArcSwap::default(), + runtime: Runtime::new(), + } + } + + pub fn load(&self) -> Arc { + self.snapshot.load_full() + } + + pub fn set_unreliable_peers(&self, unreliable_peers: HashSet) { + self.unreliable_peers.store(Arc::new(unreliable_peers)); + } + + /// Verifies, then adds edges to the graph, then recomputes the routing table. + /// Each entry of `edges` are edges coming from a different source. + /// Returns (new_edges,oks) where + /// * new_edges contains new valid edges that should be broadcasted. + /// * oks.len() == edges.len() and oks[i] is true iff all edges in edges[i] were valid. + /// + /// The validation of each `edges[i]` separately, stops at the first invalid edge, + /// and all remaining edges of `edges[i]` are discarded. + /// + /// Edge verification is expensive, and it would be an attack vector if we dropped on the + /// floor valid edges verified so far: an attacker could prepare a SyncRoutingTable + /// containing a lot of valid edges, except for the last one, and send it repeatedly to a + /// node. The node would then validate all the edges every time, then reject the whole set + /// because just the last edge was invalid. Instead, we accept all the edges verified so + /// far and return an error only afterwards. + pub async fn update( + self: &Arc, + clock: &time::Clock, + edges: Vec>, + ) -> (Vec, Vec) { + // Computation is CPU heavy and accesses DB so we execute it on a dedicated thread. + // TODO(gprusak): It would be better to move CPU heavy stuff to rayon and make DB calls async, + // but that will require further refactor. Or even better: get rid of the Graph all + // together. + let this = self.clone(); + let clock = clock.clone(); + self.runtime + .handle + .spawn_blocking(move || { + let mut inner = this.inner.lock(); + let mut new_edges = vec![]; + let mut oks = vec![]; + for es in edges { + let (es, ok) = inner.add_edges(&clock, es); + oks.push(ok); + new_edges.extend(es); + } + let snapshot = inner.update(&clock, &this.unreliable_peers.load()); + let snapshot = Arc::new(snapshot); + this.routing_table.update(snapshot.next_hops.clone(), snapshot.distances.clone()); + this.snapshot.store(snapshot); + (new_edges, oks) + }) + .await + .unwrap() + } +} diff --git a/chain/network/src/routing/graph/tests.rs b/chain/network/src/routing/graph/tests.rs new file mode 100644 index 000000000..29f9278ea --- /dev/null +++ b/chain/network/src/routing/graph/tests.rs @@ -0,0 +1,281 @@ +use super::{Graph, GraphConfig}; +use crate::network_protocol::testonly as data; +use crate::network_protocol::Edge; +use crate::network_protocol::EDGE_MIN_TIMESTAMP_NONCE; +use crate::store; +use crate::store::testonly::Component; +use crate::testonly::make_rng; +use unc_async::time; +use unc_crypto::SecretKey; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::network::PeerId; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; + +impl Graph { + async fn simple_update(self: &Arc, clock: &time::Clock, edges: Vec) { + assert_eq!(vec![true], self.update(clock, vec![edges]).await.1); + } + + async fn check(&self, want_mem: &[Edge], want_db: &[Component]) { + let got_mem = self.load(); + let got_mem: HashMap<_, _> = got_mem.edges.iter().collect(); + let mut want_mem_map = HashMap::new(); + for e in want_mem { + if want_mem_map.insert(e.key(), e).is_some() { + panic!("want_mem: multiple entries for {:?}", e.key()); + } + } + assert_eq!(got_mem, want_mem_map); + + let got_db: HashSet<_> = + self.inner.lock().store.list_components().into_iter().map(|c| c.normal()).collect(); + let want_db: HashSet<_> = want_db.iter().map(|c| c.clone().normal()).collect(); + assert_eq!(got_db, want_db); + } +} + +fn store() -> store::Store { + store::Store::from(unc_store::db::TestDB::new()) +} + +fn peer_id(key: &SecretKey) -> PeerId { + PeerId::new(key.public_key()) +} + +#[tokio::test] +async fn empty() { + init_test_logger(); + let mut rng = make_rng(87927345); + let rng = &mut rng; + let node_key = data::make_secret_key(rng); + let cfg = GraphConfig { + node_id: peer_id(&node_key), + prune_unreachable_peers_after: time::Duration::seconds(3), + prune_edges_after: None, + }; + let g = Graph::new(cfg, store()); + g.check(&[], &[]).await; +} + +const SEC: time::Duration = time::Duration::seconds(1); + +#[tokio::test] +async fn one_edge() { + init_test_logger(); + let clock = time::FakeClock::default(); + let mut rng = make_rng(87927345); + let rng = &mut rng; + let node_key = data::make_secret_key(rng); + let cfg = GraphConfig { + node_id: peer_id(&node_key), + prune_unreachable_peers_after: time::Duration::seconds(3), + prune_edges_after: None, + }; + let g = Arc::new(Graph::new(cfg.clone(), store())); + + let p1 = data::make_secret_key(rng); + let e1 = data::make_edge(&node_key, &p1, 1); + let e1v2 = e1.remove_edge(peer_id(&p1), &p1); + + tracing::info!(target:"test", "Add an active edge. Update RT with pruning."); + // NOOP, since p1 is reachable. + g.simple_update(&clock.clock(), vec![e1.clone()]).await; + g.check(&[e1.clone()], &[]).await; + + tracing::info!(target:"test", "Override with an inactive edge."); + g.simple_update(&clock.clock(), vec![e1v2.clone()]).await; + g.check(&[e1v2.clone()], &[]).await; + + tracing::info!(target:"test", "After 2s, simple_update RT with pruning unreachable for 3s."); + // NOOP, since p1 is unreachable for 2s. + clock.advance(2 * SEC); + g.simple_update(&clock.clock(), vec![]).await; + g.check(&[e1v2.clone()], &[]).await; + + tracing::info!(target:"test", "Update RT with pruning unreachable for 1s."); + // p1 should be moved to DB. + clock.advance(2 * SEC); + g.simple_update(&clock.clock(), vec![]).await; + g.check(&[], &[Component { edges: vec![e1v2.clone()], peers: vec![peer_id(&p1)] }]).await; +} + +#[tokio::test] +async fn load_component() { + init_test_logger(); + let clock = time::FakeClock::default(); + let mut rng = make_rng(87927345); + let rng = &mut rng; + let node_key = data::make_secret_key(rng); + let cfg = GraphConfig { + node_id: peer_id(&node_key), + prune_unreachable_peers_after: time::Duration::seconds(3), + prune_edges_after: None, + }; + let g = Arc::new(Graph::new(cfg.clone(), store())); + + let p1 = data::make_secret_key(rng); + let p2 = data::make_secret_key(rng); + let e1 = data::make_edge_tombstone(&node_key, &p1); + let e2 = data::make_edge_tombstone(&node_key, &p2); + let e3 = data::make_edge(&p1, &p2, 1); + let e1v2 = data::make_edge(&node_key, &p1, e1.nonce() + 1); + + // There is an active edge between p1,p2, but neither is reachable from me(). + // They should be pruned. + g.simple_update(&clock.clock(), vec![e1.clone(), e2.clone(), e3.clone()]).await; + g.check( + &[], + &[Component { + edges: vec![e1.clone(), e2.clone(), e3.clone()], + peers: vec![peer_id(&p1), peer_id(&p2)], + }], + ) + .await; + + // Add an active edge from me() to p1. This should trigger loading the whole component from DB. + g.simple_update(&clock.clock(), vec![e1v2.clone()]).await; + g.check(&[e1v2, e2, e3], &[]).await; +} + +#[tokio::test] +async fn components_nonces_are_tracked_in_storage() { + init_test_logger(); + let clock = time::FakeClock::default(); + let mut rng = make_rng(87927345); + let rng = &mut rng; + let node_key = data::make_secret_key(rng); + let cfg = GraphConfig { + node_id: peer_id(&node_key), + prune_unreachable_peers_after: time::Duration::seconds(3), + prune_edges_after: None, + }; + let store = store(); + let g = Arc::new(Graph::new(cfg.clone(), store.clone())); + + tracing::info!(target:"test", "Add an inactive edge and prune it."); + let p1 = data::make_secret_key(rng); + let e1 = data::make_edge_tombstone(&node_key, &p1); + g.simple_update(&clock.clock(), vec![e1.clone()]).await; + g.check(&[], &[Component { edges: vec![e1.clone()], peers: vec![peer_id(&p1)] }]).await; + + tracing::info!(target:"test", "Add an active unreachable edge, which also should get pruned."); + let p2 = data::make_secret_key(rng); + let p3 = data::make_secret_key(rng); + let e23 = data::make_edge(&p2, &p3, 3); + g.simple_update(&clock.clock(), vec![e23.clone()]).await; + g.check( + &[], + &[ + Component { edges: vec![e1.clone()], peers: vec![peer_id(&p1)] }, + Component { edges: vec![e23.clone()], peers: vec![peer_id(&p2), peer_id(&p3)] }, + ], + ) + .await; + + // Spawn a new graph with the same storage. + // Add another inactive edge and prune it. The previously created component shouldn't get + // overwritten, but rather a new one should be created. + // This verifies that the last_component_nonce (which indicates which component IDs have been + // already utilized) is persistently stored in DB. + let g = Arc::new(Graph::new(cfg.clone(), store)); + let p4 = data::make_secret_key(rng); + let e4 = data::make_edge_tombstone(&node_key, &p4); + g.simple_update(&clock.clock(), vec![e4.clone()]).await; + g.check( + &[], + &[ + Component { edges: vec![e1.clone()], peers: vec![peer_id(&p1)] }, + Component { edges: vec![e23.clone()], peers: vec![peer_id(&p2), peer_id(&p3)] }, + Component { edges: vec![e4.clone()], peers: vec![peer_id(&p4)] }, + ], + ) + .await; + + // Add an active edge between unreachable nodes, which will merge 2 components in DB. + let e34 = data::make_edge(&p3, &p4, 1); + g.simple_update(&clock.clock(), vec![e34.clone()]).await; + g.check( + &[], + &[ + Component { edges: vec![e1.clone()], peers: vec![peer_id(&p1)] }, + Component { + edges: vec![e4.clone(), e23.clone(), e34.clone()], + peers: vec![peer_id(&p2), peer_id(&p3), peer_id(&p4)], + }, + ], + ) + .await; +} + +fn to_active_nonce(t: time::Utc) -> u64 { + let value = t.unix_timestamp() as u64; + if value % 2 == 1 { + return value; + } + return value + 1; +} + +#[tokio::test] +async fn expired_edges() { + init_test_logger(); + let clock = time::FakeClock::default(); + clock.set_utc(*EDGE_MIN_TIMESTAMP_NONCE + time::Duration::days(2)); + let mut rng = make_rng(87927345); + let rng = &mut rng; + let node_key = data::make_secret_key(rng); + let cfg = GraphConfig { + node_id: peer_id(&node_key), + prune_unreachable_peers_after: time::Duration::hours(100), + prune_edges_after: Some(110 * SEC), + }; + let g = Arc::new(Graph::new(cfg.clone(), store())); + + let p1 = data::make_secret_key(rng); + let p2 = data::make_secret_key(rng); + + let now = clock.now_utc(); + let e1 = data::make_edge(&node_key, &p1, to_active_nonce(now)); + let old_e2 = data::make_edge(&node_key, &p2, to_active_nonce(now - 100 * SEC)); + let still_old_e2 = data::make_edge(&node_key, &p2, to_active_nonce(now - 90 * SEC)); + let fresh_e2 = data::make_edge(&node_key, &p2, to_active_nonce(now)); + + tracing::info!(target:"test", "Add an active edge."); + g.simple_update(&clock.clock(), vec![e1.clone(), old_e2.clone()]).await; + g.check(&[e1.clone(), old_e2.clone()], &[]).await; + tracing::info!(target:"test", "Update RT with pruning."); + // e1 should stay - as it is fresh, but old_e2 should be removed. + clock.advance(40 * SEC); + g.simple_update(&clock.clock(), vec![]).await; + g.check(&[e1.clone()], &[]).await; + + tracing::info!(target:"test", "Adding 'still old' edge to e2 should fail."); + // (as it is older than the last prune_edges_older_than) + g.simple_update(&clock.clock(), vec![still_old_e2.clone()]).await; + g.check(&[e1.clone()], &[]).await; + + tracing::info!(target:"test", "But adding the fresh edge should work."); + g.simple_update(&clock.clock(), vec![fresh_e2.clone()]).await; + g.check(&[e1.clone(), fresh_e2.clone()], &[]).await; + + tracing::info!(target:"test", "Advance so that the edge is 'too old' and should be removed."); + clock.advance(100 * SEC); + g.simple_update(&clock.clock(), vec![]).await; + g.check(&[], &[]).await; + + tracing::info!(target:"test", "Let's create a removal edge."); + let e1v2 = data::make_edge(&node_key, &p1, to_active_nonce(clock.now_utc())) + .remove_edge(peer_id(&p1), &p1); + g.simple_update(&clock.clock(), vec![e1v2.clone()]).await; + g.check(&[e1v2.clone()], &[]).await; + + // Advance time a bit. The edge should stay. + clock.advance(20 * SEC); + g.simple_update(&clock.clock(), vec![]).await; + g.check(&[e1v2.clone()], &[]).await; + + // Advance time a lot. The edge should be pruned. + clock.advance(100 * SEC); + g.simple_update(&clock.clock(), vec![]).await; + g.check(&[], &[]).await; +} diff --git a/chain/network/src/routing/graph_v2/mod.rs b/chain/network/src/routing/graph_v2/mod.rs new file mode 100644 index 000000000..3693e7f94 --- /dev/null +++ b/chain/network/src/routing/graph_v2/mod.rs @@ -0,0 +1,764 @@ +use crate::concurrency::runtime::Runtime; +use crate::network_protocol; +use crate::network_protocol::{AdvertisedPeerDistance, Edge, EdgeState}; +use crate::routing::edge_cache::EdgeCache; +use crate::routing::routing_table_view::RoutingTableView; +use crate::stats::metrics; +use arc_swap::ArcSwap; +use unc_async::time; +use unc_primitives::network::PeerId; +use unc_primitives::views::{EdgeView, NetworkRoutesView, PeerDistancesView}; +use parking_lot::Mutex; +use std::collections::VecDeque; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; + +#[cfg(not(test))] +use crate::concurrency; +#[cfg(not(test))] +use rayon::iter::ParallelBridge; + +#[cfg(test)] +mod testonly; +#[cfg(test)] +mod tests; + +pub type NextHopTable = HashMap>; + +#[derive(Clone)] +pub struct GraphConfigV2 { + pub node_id: PeerId, + pub prune_edges_after: Option, +} + +#[derive(Debug)] +pub enum NetworkTopologyChange { + PeerConnected(PeerId, Edge), + PeerDisconnected(PeerId), + PeerAdvertisedDistances(network_protocol::DistanceVector), + EdgeNonceRefresh(Vec), +} + +/// Locally stored properties of a received network_protocol::DistanceVector message +#[derive(Debug)] +struct PeerDistances { + /// Advertised distances indexed by the local EdgeCache's peer to id mapping. + pub distance: Vec>, + /// The lowest nonce among all edges used to validate the distances. + pub min_nonce: u64, +} + +struct Inner { + config: GraphConfigV2, + + /// Data structure maintaing information about the entire known network + edge_cache: EdgeCache, + + /// Edges of the local node's direct connections + local_edges: HashMap, + /// Distances advertised by the local node's direct peers + peer_distances: HashMap, + + /// Distances from the local node to other nodes + my_distances: HashMap, + /// The latest DistanceVector advertised by the local node + my_distance_vector: network_protocol::DistanceVector, +} + +impl Inner { + /// Function which verifies signed edges. + /// Returns true iff all the edges provided were valid. + /// + /// This method implements a security measure against an adversary sending invalid edges. + /// It verifies edges in parallel until the first invalid edge is found. It adds nonces + /// for all the edges verified so far to the cache, but drops all the remaining ones. This way + /// the wasted work (verification of invalid edges) is constant, no matter how large the input + /// size is. + /// + /// Edge verification is expensive, and it would be an attack vector if we dropped on the + /// floor valid edges verified so far: an attacker could prepare a message containing + /// a lot of valid edges, except for the last one, and send it repeatedly to a node. + /// The node would then validate all the edges every time, then reject the whole set + /// because just the last edge was invalid. Instead, we cache all the edges verified so + /// far and return an error only afterwards. + #[cfg(not(test))] + fn verify_and_cache_edge_nonces(&mut self, edges: &Vec) -> bool { + metrics::EDGE_UPDATES.inc_by(edges.len() as u64); + + // Collect only those edges which are new to us for verification. + let mut unverified_edges = Vec::::new(); + for e in edges { + // V2 routing protocol only shares Active edges + // TODO(saketh): deprecate tombstones entirely + if e.edge_type() != EdgeState::Active { + return false; + } + + if !self.edge_cache.has_edge_nonce_or_newer(e) { + unverified_edges.push(e.clone()); + } + } + + // Verify the new edges in parallel on rayon. + // Stop at first invalid edge. + let (verified_edges, ok) = concurrency::rayon::run_blocking(move || { + concurrency::rayon::try_map(unverified_edges.into_iter().par_bridge(), |e| { + if e.verify() { + Some(e) + } else { + None + } + }) + }); + + // Store the verified nonces in the cache + verified_edges.iter().for_each(|e| self.edge_cache.write_verified_nonce(e)); + + ok + } + + /// Function computing basic properties of a tree. + /// + /// Accepts a root node and a list of edges specifying a tree. If the edges form + /// a valid tree containing the specified `root`, returns a pair of vectors + /// (distance, first_step). Otherwise, returns None. + /// + /// Nodes are indexed into the vectors according to the peer to id mapping in the EdgeCache. + /// If `tree_edges` contain some previously unseen peers, new ids are allocated for them. + /// + /// For each node in the tree, `distance` indicates the length of the path + /// from the root to the node. Nodes outside the tree have distance None. + /// + /// For each node in the tree, `first_step` indicates the root's neighbor on the path + /// from the root to the node. The root of the tree, as well as any nodes outside + /// the tree, have a first_step of None. + pub(crate) fn calculate_tree_distances( + &mut self, + root: &PeerId, + tree_edges: &Vec, + ) -> Option<(Vec>, Vec>)> { + // Prepare for graph traversal by ensuring all PeerIds in the tree have a u32 label + self.edge_cache.create_ids_for_tree(root, tree_edges); + + // Build adjacency-list representation of the edges + let mut adjacency = vec![Vec::::new(); self.edge_cache.max_id()]; + for edge in tree_edges { + let (peer0, peer1) = edge.key(); + let id0 = self.edge_cache.get_id(peer0); + let id1 = self.edge_cache.get_id(peer1); + adjacency[id0 as usize].push(id1); + adjacency[id1 as usize].push(id0); + } + + // Compute distances from the root by breadth-first search + let mut distance: Vec> = vec![None; self.edge_cache.max_id()]; + let mut first_step: Vec> = vec![None; self.edge_cache.max_id()]; + { + let root_id = self.edge_cache.get_id(root); + let mut queue = VecDeque::new(); + queue.push_back(root_id); + distance[root_id as usize] = Some(0); + + while let Some(cur_peer) = queue.pop_front() { + let cur_peer = cur_peer as usize; + // The unwrap here is safe because anything pushed to the queue has a distance + let cur_distance = distance[cur_peer].unwrap(); + + for &neighbor in &adjacency[cur_peer] { + let neighbor = neighbor as usize; + if distance[neighbor].is_none() { + distance[neighbor] = Some(cur_distance + 1); + first_step[neighbor] = first_step[cur_peer].or(Some(neighbor as u32)); + queue.push_back(neighbor as u32); + } + } + } + } + + // Check that the edges in `tree_edges` actually form a tree containing `root` + let mut num_reachable_nodes = 0; + for &dist in &distance { + if dist.is_some() { + num_reachable_nodes += 1; + } + } + if num_reachable_nodes != tree_edges.len() + 1 { + return None; + } + + Some((distance, first_step)) + } + + /// Given a DistanceVector message, validates the advertised distances against the spanning tree. + /// + /// If valid, returns a vector of distances indexed according to the local node's EdgeCache's + /// peer to id mapping. Otherwise, returns None. + /// + /// Removes any advertised routes which go through the local node; it doesn't make sense + /// to forward to a neighbor who will just sent the message right back to us. + pub(crate) fn validate_routing_distances( + &mut self, + distance_vector: &network_protocol::DistanceVector, + ) -> Option>> { + // A valid DistanceVector must contain distinct, correctly signed edges + let original_len = distance_vector.edges.len(); + let edges = Edge::deduplicate(distance_vector.edges.clone()); + if edges.len() != original_len || !self.verify_and_cache_edge_nonces(&edges) { + return None; + } + + // Check validity of the spanning tree and compute its basic properties + let tree_traversal = self.calculate_tree_distances(&distance_vector.root, &edges); + let (tree_distance, first_step) = tree_traversal?; + + // Verify that the advertised distances are corroborated by the spanning tree distances + let mut advertised_distances: Vec> = vec![None; self.edge_cache.max_id()]; + for entry in &distance_vector.distances { + let destination_id = self.edge_cache.get_or_create_id(&entry.destination) as usize; + advertised_distances[destination_id] = Some(entry.distance); + } + let mut consistent = true; + for id in 0..self.edge_cache.max_id() { + if let Some(advertised_distance) = advertised_distances[id] { + // The tree must have a route, but it can be shorter than the advertised distance + consistent &= tree_distance[id] + .is_some_and(|tree_distance| tree_distance <= advertised_distance); + } else { + consistent &= tree_distance[id].is_none(); + } + } + // After this point, we know that the DistanceVector message is valid + if !consistent { + return None; + } + + // Now, prune any advertised routes which go through the local node; it doesn't make + // sense to forward a message to a neighbor who will send it back to us + let local_node_id = self.edge_cache.get_local_node_id() as usize; + for id in 0..self.edge_cache.max_id() { + if id != local_node_id + && first_step[id].is_some_and(|first_step| first_step == local_node_id as u32) + { + advertised_distances[id] = None; + } + } + + Some(advertised_distances) + } + + /// Accepts a validated DistanceVector and its `advertised_distances`. + /// Updates the status of the direct connection between the local node and the direct peer. + /// If the peer can be used for forwarding, stores the advertised distances. + /// Returns true iff the distances are stored. + fn store_validated_peer_distances( + &mut self, + distance_vector: &network_protocol::DistanceVector, + mut advertised_distances: Vec>, + ) -> bool { + let local_node_id = self.edge_cache.get_local_node_id() as usize; + + // A direct peer's distance vector which advertises an indirect path to the local node + // is outdated and can be ignored. + if advertised_distances[local_node_id].is_some_and(|distance| distance > 1) { + // TODO(saketh): We could try to be more clever here and do some surgery on the tree + // to replace the indirect path and speed up convergence of the routing protocol. + return false; + } + + // Look in the spanning tree for the direct edge between the local node and the root + let tree_edge = distance_vector.edges.iter().find(|edge| { + edge.contains_peer(&self.config.node_id) && edge.contains_peer(&distance_vector.root) + }); + + // If the tree has more recent state for the direct edge, replace the local state + if let Some(tree_edge) = tree_edge { + self.local_edges + .entry(distance_vector.root.clone()) + .and_modify(|local_edge| { + if tree_edge.nonce() > local_edge.nonce() { + *local_edge = tree_edge.clone(); + } + }) + .or_insert(tree_edge.clone()); + } + + // Without a direct edge, we cannot use the distances advertised by the peer + let Some(local_edge) = self.local_edges.get(&distance_vector.root) else { + return false; + }; + if local_edge.edge_type() == EdgeState::Removed { + return false; + } + + // If the spanning tree doesn't already include the direct edge, add it + let mut spanning_tree = distance_vector.edges.clone(); + if tree_edge.is_none() { + if !advertised_distances[local_node_id].is_none() { + debug_assert!(false); + return false; + } + + spanning_tree.push(local_edge.clone()); + advertised_distances[local_node_id] = Some(1); + } + + // .min().unwrap() is safe here because the tree is now guaranteed to at least + // include the direct edge between the local node and the peer + debug_assert!(!spanning_tree.is_empty()); + let min_nonce = spanning_tree.iter().map(|e| e.nonce()).min().unwrap(); + + // Store the tree used to validate the distances. + self.edge_cache.update_tree(&distance_vector.root, &spanning_tree); + // Store the validated distances + self.peer_distances.insert( + distance_vector.root.clone(), + PeerDistances { distance: advertised_distances, min_nonce }, + ); + + true + } + + /// Verifies the given DistanceVector. + /// Returns a boolean indicating whether the DistanceVector was valid. + /// If applicable, stores the advertised distances for forwarding. + fn handle_distance_vector( + &mut self, + distance_vector: &network_protocol::DistanceVector, + ) -> bool { + // Basic sanity check; `distance_vector` should come from some other peer + if self.config.node_id == distance_vector.root { + return false; + } + + // Validate the advertised distances against the accompanying spanning tree + let validated_distances = self.validate_routing_distances(distance_vector); + + let is_valid = validated_distances.is_some(); + + let stored = match validated_distances { + Some(distances) => self.store_validated_peer_distances(&distance_vector, distances), + None => false, + }; + + if !stored { + // Free ids which may have been allocated to perform validation + self.edge_cache.free_unused_ids(); + } + + return is_valid; + } + + /// Updates the local state of the edge cache with the nonces for the given edges. + fn handle_edge_nonce_refresh(&mut self, edges: &Vec) -> bool { + for e in edges { + // TODO(saketh): deprecate tombstones entirely + if e.edge_type() != EdgeState::Active { + continue; + } + + // TODO (saketh): After V1 routing is deprecated, we will need to actually perform + // edge verification here. For now, edges make it here after already being verified. + if !self.edge_cache.has_edge_nonce_or_newer(e) { + self.edge_cache.write_verified_nonce(e); + } + } + + return true; + } + + /// Handles disconnection of a peer. + /// - Updates the state of `local_edges`. + /// - Erases the peer's latest spanning tree, if there is one, from `edge_cache`. + /// - Erases the advertised distances for the peer. + pub(crate) fn remove_direct_peer(&mut self, peer_id: &PeerId) { + if let Some(edge) = self.local_edges.get_mut(peer_id) { + // TODO(saketh): refactor Edge once the old routing protocol is deprecated + if edge.edge_type() != EdgeState::Removed { + let (peer0, peer1) = edge.key().clone(); + // V2 routing protocol doesn't broadcast tombstones; don't bother to sign them + *edge = Edge::make_fake_edge(peer0, peer1, edge.nonce() + 1); + } + assert!(edge.edge_type() == EdgeState::Removed); + } + + self.edge_cache.remove_tree(peer_id); + self.peer_distances.remove(peer_id); + } + + /// Handles connection of a new peer or nonce refresh for an existing one. + /// - Updates the state of `local_edges`. + /// - Adds or updates the nonce in the `edge_cache`. + /// - If we don't already have a DistanceVector for this peer, initializes one. + pub(crate) fn add_or_update_direct_peer(&mut self, peer_id: PeerId, edge: Edge) -> bool { + assert_eq!(edge.edge_type(), EdgeState::Active); + + // We have this nonce or a newer one already; ignore the update entirely + if self.edge_cache.has_edge_nonce_or_newer(&edge) { + return true; + } + + // Reject invalid edge + if !self.verify_and_cache_edge_nonces(&vec![edge.clone()]) { + return false; + } + + // Update the state of `local_edges` + self.local_edges.insert(peer_id.clone(), edge.clone()); + + // If we don't already have a DistanceVector received from this peer, + // create one for it and process it as if we received it + if !self.peer_distances.contains_key(&peer_id) { + self.handle_distance_vector(&network_protocol::DistanceVector { + root: peer_id.clone(), + distances: vec![ + // The peer has distance 0 to itself + AdvertisedPeerDistance { destination: peer_id, distance: 0 }, + // The peer is distance 1 from this node + AdvertisedPeerDistance { + destination: self.config.node_id.clone(), + distance: 1, + }, + ], + edges: vec![edge], + }); + } + + true + } + + pub(crate) fn handle_network_change( + &mut self, + _clock: &time::Clock, + update: &NetworkTopologyChange, + ) -> bool { + match update { + NetworkTopologyChange::PeerConnected(peer_id, edge) => { + self.add_or_update_direct_peer(peer_id.clone(), edge.clone()) + } + NetworkTopologyChange::PeerDisconnected(peer_id) => { + self.remove_direct_peer(peer_id); + true + } + NetworkTopologyChange::PeerAdvertisedDistances(distance_vector) => { + self.handle_distance_vector(distance_vector) + } + NetworkTopologyChange::EdgeNonceRefresh(edges) => self.handle_edge_nonce_refresh(edges), + } + } + + /// Computes and returns "next hops" for all reachable destinations in the network. + /// Accepts a set of "unreliable peers" to avoid routing through. + /// TODO: Actually avoid the unreliable peers + /// + /// Returns the NextHopTable along with a mapping from the reachable nodes in the + /// network to their shortest-path distances. + pub(crate) fn compute_next_hops( + &mut self, + _unreliable_peers: &HashSet, + ) -> (NextHopTable, HashMap) { + let max_id = self.edge_cache.max_id(); + let local_node_id = self.edge_cache.get_local_node_id() as usize; + + // Calculate the min distance to each routable node + let mut min_distance: Vec> = vec![None; max_id]; + min_distance[local_node_id] = Some(0); + for (_, entry) in &mut self.peer_distances { + // The peer to id mapping in the edge_cache is dynamic. We can still use previous distance + // calculations because a node incident to an active edge won't be relabelled. However, + // we may need to resize the distance vector. + entry.distance.resize(max_id, None); + + for id in 0..max_id { + if let Some(peer_distance) = entry.distance[id] { + if !min_distance[id] + .is_some_and(|min_distance| min_distance <= peer_distance + 1) + { + min_distance[id] = Some(peer_distance + 1); + } + } + } + } + + // Compute the next hop table + let mut next_hops_by_id: Vec> = vec![vec![]; self.edge_cache.max_id()]; + for id in 0..max_id { + if let Some(id_distance) = min_distance[id] { + for (peer_id, entry) in &self.peer_distances { + if entry.distance[id] + .is_some_and(|peer_distance| peer_distance + 1 == id_distance) + { + next_hops_by_id[id].push(peer_id.clone()); + } + } + } + } + let mut next_hops = HashMap::>::new(); + for (peer_id, id) in self.edge_cache.iter_peers() { + if !next_hops_by_id[*id as usize].is_empty() { + next_hops.insert(peer_id.clone(), next_hops_by_id[*id as usize].clone()); + } + } + + // Build a PeerId-keyed map of distances + let mut distance: HashMap = HashMap::new(); + for (peer_id, id) in self.edge_cache.iter_peers() { + if let Some(peer_distance) = min_distance[*id as usize] { + distance.insert(peer_id.clone(), peer_distance); + } + } + + (next_hops, distance) + } + + /// Each DistanceVector advertised by a peer includes a collection of edges + /// used to validate the advertised distances. + /// + /// Edges are timestamped when signed and we consider them to be expired + /// once a duration of `self.config.prune_edges_after` has passed. + /// + /// This function checks `peer_distances` for any DistanceVectors containing + /// expired edges. Any such DistanceVectors are removed in their entirety. + /// + /// Also removes old edges from `local_edges` and from the EdgeCache. + fn prune_expired_peer_distances(&mut self, clock: &time::Clock) { + if let Some(prune_edges_after) = self.config.prune_edges_after { + let prune_nonces_older_than = + (clock.now_utc() - prune_edges_after).unix_timestamp() as u64; + + let peers_to_remove: Vec = self + .peer_distances + .iter_mut() + .filter_map(|(peer, entry)| { + // If the tree's min_nonce is too old, first try refreshing it + // from the latest nonces in the edge cache. + if entry.min_nonce < prune_nonces_older_than { + if let Some(refreshed_min_nonce) = self.edge_cache.get_min_nonce(peer) { + entry.min_nonce = refreshed_min_nonce; + } + } + + if entry.min_nonce < prune_nonces_older_than { + Some(peer.clone()) + } else { + None + } + }) + .collect(); + + for peer_id in &peers_to_remove { + self.remove_direct_peer(peer_id); + } + + self.local_edges.retain(|_, edge| edge.nonce() >= prune_nonces_older_than); + + self.edge_cache.prune_old_edges(prune_nonces_older_than); + } + } + + /// Constructs an instance of network_protocol::DistanceVector advertising the given distances. + /// Returns None iff the `edge_cache` cannot construct a spanning tree achieving the distances. + fn construct_distance_vector_message( + &self, + distances: &HashMap, + ) -> Option { + Some(network_protocol::DistanceVector { + root: self.config.node_id.clone(), + // Collect distances for all known reachable nodes + distances: distances + .iter() + .map(|(destination, distance)| AdvertisedPeerDistance { + destination: destination.clone(), + distance: *distance, + }) + .collect(), + // Construct a spanning tree of signed edges achieving the claimed distances + edges: self.edge_cache.construct_spanning_tree(distances)?, + }) + } + + /// Given the latest computed `distances`, updates `my_distances` and `my_distance_vector`. + /// If distances have changed, returns a DistanceVector message to be broadcast to peers. + fn update_distances( + &mut self, + distances: HashMap, + ) -> Option { + if self.my_distances == distances { + tracing::debug!(target: "routing", "No change to routing distances after processing network updates"); + return None; + } + + tracing::debug!(target: "routing", "Routing distances have changed; reconstructing distance vector"); + + let distance_vector = self.construct_distance_vector_message(&distances)?; + + self.my_distances = distances; + self.my_distance_vector = distance_vector; + + Some(self.my_distance_vector.clone()) + } + + /// Prunes expired peer distances, then recomputes the distances for the local node. + /// Returns the recomputed NextHopTable. + /// If distances have changed, returns an updated DistanceVector to be broadcast. + pub(crate) fn compute_routes( + &mut self, + clock: &time::Clock, + unreliable_peers: &HashSet, + ) -> (NextHopTable, Option) { + let _update_time = metrics::ROUTING_TABLE_RECALCULATION_HISTOGRAM.start_timer(); + + // First prune any peer distances which have expired + self.prune_expired_peer_distances(&clock); + + // Recompute the NextHopTable + let (next_hops, distances) = self.compute_next_hops(unreliable_peers); + + // Store the newly computed distances and construct a DistanceVector message for broadcast + let to_broadcast = self.update_distances(distances); + + // Update metrics after update + metrics::ROUTING_TABLE_RECALCULATIONS.inc(); + metrics::PEER_REACHABLE.set(next_hops.len() as i64); + metrics::EDGE_TOTAL.set(self.edge_cache.known_edges_ct() as i64); + + (next_hops, to_broadcast) + } + + /// Logs the state of the routing table + pub(crate) fn log_state(&self) { + tracing::debug!(target: "routing", "My distances: {:?}", self.my_distances); + tracing::debug!(target: "routing", "Peer labels: {:?}", self.edge_cache.p2id); + tracing::debug!(target: "routing", "Peer distance vectors: {:?}", self.peer_distances); + } +} + +pub(crate) struct GraphV2 { + inner: Arc>, + unreliable_peers: ArcSwap>, + pub routing_table: RoutingTableView, + + runtime: Runtime, +} + +impl GraphV2 { + pub fn new(config: GraphConfigV2) -> Self { + let local_node = config.node_id.clone(); + let edge_cache = EdgeCache::new(local_node.clone()); + + let my_distance_vector = network_protocol::DistanceVector { + root: local_node.clone(), + distances: vec![AdvertisedPeerDistance { + destination: local_node.clone(), + distance: 0, + }], + edges: vec![], + }; + + Self { + routing_table: RoutingTableView::new(), + inner: Arc::new(Mutex::new(Inner { + config, + edge_cache, + local_edges: HashMap::new(), + peer_distances: HashMap::new(), + my_distances: HashMap::from([(local_node, 0)]), + my_distance_vector, + })), + unreliable_peers: ArcSwap::default(), + runtime: Runtime::new(), + } + } + + pub fn set_unreliable_peers(&self, unreliable_peers: HashSet) { + self.unreliable_peers.store(Arc::new(unreliable_peers)); + } + + /// Logs the given batch of updates and the results from processing them. + fn write_event_logs(updates: &Vec, oks: &Vec) { + for (update, &ok) in updates.iter().zip(oks) { + if ok { + tracing::debug!(target: "routing", "Processed event {:?}", update); + } else { + tracing::debug!(target: "routing", "Rejected invalid distance vector {:?}", update); + } + } + } + + /// Accepts and processes a batch of NetworkTopologyChanges. + /// Each update is verified and, if valid, the advertised distances are stored. + /// After all updates are processed, recomputes the local node's next hop table. + /// + /// May return a new DistanceVector for the local node, to be broadcasted to peers. + /// Does so iff routing distances have changed due to the processed updates. + /// + /// Returns (distance_vector, oks) where + /// * distance_vector is an Option to be broadcasted + /// * oks.len() == distance_vectors.len() and oks[i] is true iff distance_vectors[i] was valid + pub async fn batch_process_network_changes( + self: &Arc, + clock: &time::Clock, + updates: Vec, + ) -> (Option, Vec) { + tracing::debug!( + target: "routing", + length = updates.len(), + "Processing a batch of network topology changes", + ); + + // TODO(saketh): Consider whether we can move this to rayon. + let this = self.clone(); + let clock = clock.clone(); + self.runtime + .handle + .spawn_blocking(move || { + let mut inner = this.inner.lock(); + + let oks: Vec = updates + .iter() + .map(|update| inner.handle_network_change(&clock, update)) + .collect(); + + Self::write_event_logs(&updates, &oks); + + let (next_hops, to_broadcast) = + inner.compute_routes(&clock, &this.unreliable_peers.load()); + + this.routing_table.update(next_hops.into(), Arc::new(inner.my_distances.clone())); + + inner.log_state(); + + (to_broadcast, oks) + }) + .await + .unwrap() + } + + pub(crate) fn get_debug_view(&self) -> NetworkRoutesView { + let inner = self.inner.lock(); + NetworkRoutesView { + edge_cache: inner.edge_cache.get_debug_view(), + local_edges: inner + .local_edges + .iter() + .map(|(peer_id, edge)| { + let (peer0, peer1) = edge.key().clone(); + (peer_id.clone(), EdgeView { peer0, peer1, nonce: edge.nonce() }) + }) + .collect(), + peer_distances: inner + .peer_distances + .iter() + .map(|(peer_id, routes)| { + ( + peer_id.clone(), + PeerDistancesView { + distance: routes.distance.clone(), + min_nonce: routes.min_nonce, + }, + ) + }) + .collect(), + my_distances: inner.my_distances.clone(), + } + } +} diff --git a/chain/network/src/routing/graph_v2/testonly.rs b/chain/network/src/routing/graph_v2/testonly.rs new file mode 100644 index 000000000..c09c55ba6 --- /dev/null +++ b/chain/network/src/routing/graph_v2/testonly.rs @@ -0,0 +1,93 @@ +use crate::network_protocol; +use crate::routing::graph_v2::AdvertisedPeerDistance; +use crate::routing::graph_v2::Inner; +use crate::routing::{GraphV2, NetworkTopologyChange, NextHopTable}; +use crate::types::Edge; +use unc_async::time; +use unc_primitives::network::PeerId; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; + +impl Inner { + pub(crate) fn verify_and_cache_edge_nonces(&mut self, edges: &Vec) -> bool { + // In tests we make fake edges and don't bother to sign them + for edge in edges { + self.edge_cache.write_verified_nonce(edge); + } + true + } +} + +impl GraphV2 { + pub(crate) fn compute_next_hops(&self) -> (NextHopTable, HashMap) { + self.inner.lock().compute_next_hops(&HashSet::new()) + } + + pub(crate) fn update_distance_vector( + &self, + root: PeerId, + distances: Vec, + edges: Vec, + ) -> bool { + self.inner.lock().handle_distance_vector(&network_protocol::DistanceVector { + root, + distances, + edges, + }) + } + + pub(crate) async fn process_network_event( + self: &Arc, + event: NetworkTopologyChange, + ) -> Option { + let clock = time::FakeClock::default(); + let (to_broadcast, oks) = + self.batch_process_network_changes(&clock.clock(), vec![event]).await; + assert!(oks[0]); + to_broadcast + } + + pub(crate) async fn process_invalid_network_event( + self: &Arc, + event: NetworkTopologyChange, + ) { + let clock = time::FakeClock::default(); + let (_, oks) = self.batch_process_network_changes(&clock.clock(), vec![event]).await; + assert!(!oks[0]); + } + + pub(crate) async fn recompute_routes( + self: &Arc, + clock: &time::Clock, + ) -> Option { + let (to_broadcast, _) = self.batch_process_network_changes(&clock, vec![]).await; + to_broadcast + } + + // Checks that the DistanceVector message for the local node is valid + // and correctly advertises the node's available routes. + pub(crate) fn verify_own_distance_vector( + &self, + expected_distances: HashMap, + distance_vector: &network_protocol::DistanceVector, + ) { + let mut inner = self.inner.lock(); + + assert_eq!(expected_distances, inner.my_distances); + + let mut expected_distances_by_id: Vec> = vec![None; inner.edge_cache.max_id()]; + for (peer_id, distance) in expected_distances.iter() { + let id = inner.edge_cache.get_id(peer_id) as usize; + expected_distances_by_id[id] = Some(*distance); + } + + assert_eq!( + expected_distances_by_id, + inner.validate_routing_distances(distance_vector).unwrap() + ); + } + + pub(crate) fn has_distance_vector(&self, peer_id: &PeerId) -> bool { + self.inner.lock().peer_distances.contains_key(peer_id) + } +} diff --git a/chain/network/src/routing/graph_v2/tests.rs b/chain/network/src/routing/graph_v2/tests.rs new file mode 100644 index 000000000..59493447c --- /dev/null +++ b/chain/network/src/routing/graph_v2/tests.rs @@ -0,0 +1,720 @@ +use crate::network_protocol; +use crate::network_protocol::AdvertisedPeerDistance; +use crate::peer_manager::network_state::PRUNE_EDGES_AFTER; +use crate::routing::{GraphConfigV2, GraphV2, NetworkTopologyChange}; +use crate::test_utils::expected_routing_tables; +use crate::test_utils::random_peer_id; +use crate::types::Edge; +use unc_async::time; +use unc_primitives::network::PeerId; +use std::collections::HashMap; +use std::sync::Arc; + +// Calls `calculate_tree_distances` on the given `root` and `edges`. +// Verifies that the calculated distances and first steps match those in `expected`. +fn verify_calculate_tree_distances( + expected: Option)>>, + root: PeerId, + edges: Vec, +) { + let graph = GraphV2::new(GraphConfigV2 { node_id: random_peer_id(), prune_edges_after: None }); + let mut inner = graph.inner.lock(); + + let calculated = inner.calculate_tree_distances(&root, &edges); + match expected { + Some(ref expected) => { + let (distance, first_step) = calculated.unwrap(); + + // Check for the expected entries + for (node, (expected_distance, expected_first_step)) in expected { + let id = inner.edge_cache.get_id(node) as usize; + + // Map the expected first step to its internal label + let expected_first_step = + expected_first_step.as_ref().map(|peer_id| inner.edge_cache.get_id(&peer_id)); + + // Expected distance should match the calculated one + assert_eq!(*expected_distance, distance[id].unwrap()); + + // Expected first step should match the calculated one + assert_eq!(expected_first_step, first_step[id]); + } + + // Make sure there are no unexpected entries + let mut calculated_reachable_nodes = 0; + for id in 0..inner.edge_cache.max_id() { + if distance[id].is_some() || first_step[id].is_some() { + calculated_reachable_nodes += 1; + } + } + assert_eq!(calculated_reachable_nodes, expected.len()); + } + None => { + assert_eq!(None, calculated); + } + } +} + +#[test] +fn calculate_tree_distances() { + let node0 = random_peer_id(); + let node1 = random_peer_id(); + let node2 = random_peer_id(); + + let edge0 = Edge::make_fake_edge(node0.clone(), node1.clone(), 123); + let edge1 = Edge::make_fake_edge(node1.clone(), node2.clone(), 123); + let edge2 = Edge::make_fake_edge(node0.clone(), node2.clone(), 123); + + // Test behavior of distance calculation on an empty tree + verify_calculate_tree_distances( + Some(HashMap::from([(node0.clone(), (0, None))])), + node0.clone(), + vec![], + ); + + // Test behavior of distance calculation on a simple tree 0--1 + verify_calculate_tree_distances( + Some(HashMap::from([ + (node0.clone(), (0, None)), + (node1.clone(), (1, Some(node1.clone()))), + ])), + node0.clone(), + vec![edge0.clone()], + ); + + // Distance calculation should reject a tree which doesn't contain the root + verify_calculate_tree_distances(None, node0.clone(), vec![edge1.clone()]); + + // Test behavior of distance calculation on a line graph 0--1--2 + verify_calculate_tree_distances( + Some(HashMap::from([ + (node0.clone(), (0, None)), + (node1.clone(), (1, Some(node1.clone()))), + (node2.clone(), (2, Some(node1.clone()))), + ])), + node0.clone(), + vec![edge0.clone(), edge1.clone()], + ); + // Test again from root 1 in 0--1--2 + verify_calculate_tree_distances( + Some(HashMap::from([ + (node0.clone(), (1, Some(node0.clone()))), + (node1.clone(), (0, None)), + (node2.clone(), (1, Some(node2))), + ])), + node1, + vec![edge0.clone(), edge1.clone()], + ); + + // Distance calculation rejects non-trees + verify_calculate_tree_distances(None, node0, vec![edge0, edge1, edge2]); +} + +#[test] +fn compute_next_hops() { + let node0 = random_peer_id(); + let graph = GraphV2::new(GraphConfigV2 { node_id: node0.clone(), prune_edges_after: None }); + + // Test behavior on a node with no peers + assert_eq!((HashMap::new(), HashMap::from([(node0.clone(), 0)])), graph.compute_next_hops()); + + // Add a peer node1; 0--1 + let node1 = random_peer_id(); + let edge01 = Edge::make_fake_edge(node0.clone(), node1.clone(), 123); + assert!(graph.update_distance_vector( + node1.clone(), + vec![ + AdvertisedPeerDistance { destination: node1.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node0.clone(), distance: 1 } + ], + vec![edge01.clone()] + )); + + let (next_hops, distance) = graph.compute_next_hops(); + assert!(expected_routing_tables(&next_hops, &[(node1.clone(), vec![node1.clone()])])); + assert_eq!(distance, HashMap::from([(node0.clone(), 0), (node1.clone(), 1)])); + + // Add another peer node2 advertising a node3 behind it; 0--2--3 + let node2 = random_peer_id(); + let node3 = random_peer_id(); + let edge02 = Edge::make_fake_edge(node0.clone(), node2.clone(), 123); + let edge23 = Edge::make_fake_edge(node2.clone(), node3.clone(), 123); + assert!(graph.update_distance_vector( + node2.clone(), + vec![ + AdvertisedPeerDistance { destination: node2.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node0.clone(), distance: 1 }, + AdvertisedPeerDistance { destination: node3.clone(), distance: 1 }, + ], + vec![edge02.clone(), edge23] + )); + + let (next_hops, distance) = graph.compute_next_hops(); + assert!(expected_routing_tables( + &next_hops, + &[ + (node1.clone(), vec![node1.clone()]), + (node2.clone(), vec![node2.clone()]), + (node3.clone(), vec![node2.clone()]), + ] + )); + assert_eq!( + distance, + HashMap::from([ + (node0.clone(), 0), + (node1.clone(), 1), + (node2.clone(), 1), + (node3.clone(), 2) + ]) + ); + + // Update the SPT for node1, also advertising node3 behind it; 0--1--3 + let edge13 = Edge::make_fake_edge(node1.clone(), node3.clone(), 123); + assert!(graph.update_distance_vector( + node1.clone(), + vec![ + AdvertisedPeerDistance { destination: node1.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node0.clone(), distance: 1 }, + AdvertisedPeerDistance { destination: node3.clone(), distance: 1 }, + ], + vec![edge01, edge13] + )); + + let (next_hops, distance) = graph.compute_next_hops(); + assert!(expected_routing_tables( + &next_hops, + &[ + (node1.clone(), vec![node1.clone()]), + (node2.clone(), vec![node2.clone()]), + (node3.clone(), vec![node1.clone(), node2.clone()]), + ] + )); + assert_eq!( + distance, + HashMap::from([ + (node0.clone(), 0), + (node1.clone(), 1), + (node2.clone(), 1), + (node3.clone(), 2) + ]) + ); + + // Update the SPT for node2, removing the route to node3; 0--2 + assert!(graph.update_distance_vector( + node2.clone(), + vec![ + AdvertisedPeerDistance { destination: node2.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node0.clone(), distance: 1 }, + ], + vec![edge02] + )); + + let (next_hops, distance) = graph.compute_next_hops(); + assert!(expected_routing_tables( + &next_hops, + &[ + (node1.clone(), vec![node1.clone()]), + (node2.clone(), vec![node2.clone()]), + (node3.clone(), vec![node1.clone()]), + ] + )); + assert_eq!(distance, HashMap::from([(node0, 0), (node1, 1), (node2, 1), (node3, 2)])); +} + +#[test] +fn compute_next_hops_discard_loop() { + let node0 = random_peer_id(); + let graph = GraphV2::new(GraphConfigV2 { node_id: node0.clone(), prune_edges_after: None }); + + // Add a peer node1 which advertises node2 via node0; 2--0--1 + let node1 = random_peer_id(); + let node2 = random_peer_id(); + let edge01 = Edge::make_fake_edge(node0.clone(), node1.clone(), 123); + let edge02 = Edge::make_fake_edge(node0.clone(), node2.clone(), 123); + assert!(graph.update_distance_vector( + node1.clone(), + vec![ + AdvertisedPeerDistance { destination: node1.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node0.clone(), distance: 1 }, + AdvertisedPeerDistance { destination: node2, distance: 2 }, + ], + vec![edge01, edge02] + )); + + // node2 should be ignored because the advertised route to it goes back through the local node + let (next_hops, distance) = graph.compute_next_hops(); + assert!(expected_routing_tables(&next_hops, &[(node1.clone(), vec![node1.clone()])])); + assert_eq!(distance, HashMap::from([(node0, 0), (node1, 1)])); +} + +#[tokio::test] +async fn test_process_network_event() { + let node0 = random_peer_id(); + let node1 = random_peer_id(); + let node2 = random_peer_id(); + + let graph = + Arc::new(GraphV2::new(GraphConfigV2 { node_id: node0.clone(), prune_edges_after: None })); + + let edge0 = Edge::make_fake_edge(node0.clone(), node1.clone(), 123); + let edge1 = Edge::make_fake_edge(node1.clone(), node2.clone(), 456); + + // Process a new connection 0--1 + let distance_vector_update = graph + .process_network_event(NetworkTopologyChange::PeerConnected(node1.clone(), edge0.clone())) + .await + .unwrap(); + graph.verify_own_distance_vector( + HashMap::from([(node0.clone(), 0), (node1.clone(), 1)]), + &distance_vector_update, + ); + + // Receive a DistanceVector from node1 with node2 behind it; 0--1--2 + let distance_vector_update = graph + .process_network_event(NetworkTopologyChange::PeerAdvertisedDistances( + network_protocol::DistanceVector { + root: node1.clone(), + distances: vec![ + AdvertisedPeerDistance { destination: node1.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node0.clone(), distance: 1 }, + AdvertisedPeerDistance { destination: node2.clone(), distance: 1 }, + ], + edges: vec![edge0.clone(), edge1.clone()], + }, + )) + .await + .unwrap(); + graph.verify_own_distance_vector( + HashMap::from([(node0.clone(), 0), (node1.clone(), 1), (node2.clone(), 2)]), + &distance_vector_update, + ); + + // Process a local update (nonce refresh) to the connection 0--1 + let edge0_refreshed = Edge::make_fake_edge(node0.clone(), node1.clone(), 789); + let distance_vector_update = graph + .process_network_event(NetworkTopologyChange::PeerConnected(node1.clone(), edge0_refreshed)) + .await; + // This update doesn't trigger a broadcast because node0's available routes haven't changed + assert_eq!(None, distance_vector_update); + // node0's locally stored DistanceVector should have the route to node2 + let distance_vector_update = graph.inner.lock().my_distance_vector.clone(); + graph.verify_own_distance_vector( + HashMap::from([(node0.clone(), 0), (node1.clone(), 1), (node2.clone(), 2)]), + &distance_vector_update, + ); + + // Process disconnection of node1 + let distance_vector_update = graph + .process_network_event(NetworkTopologyChange::PeerDisconnected(node1.clone())) + .await + .unwrap(); + graph.verify_own_distance_vector(HashMap::from([(node0.clone(), 0)]), &distance_vector_update); +} + +#[tokio::test] +async fn test_process_network_event_idempotent() { + let node0 = random_peer_id(); + let node1 = random_peer_id(); + + let graph = + Arc::new(GraphV2::new(GraphConfigV2 { node_id: node0.clone(), prune_edges_after: None })); + + let edge0 = Edge::make_fake_edge(node0.clone(), node1.clone(), 123); + + // Process a new connection 0--1 + let distance_vector_update = graph + .process_network_event(NetworkTopologyChange::PeerConnected(node1.clone(), edge0.clone())) + .await + .unwrap(); + graph.verify_own_distance_vector( + HashMap::from([(node0.clone(), 0), (node1.clone(), 1)]), + &distance_vector_update, + ); + // Process the same event without error + let distance_vector_update = graph + .process_network_event(NetworkTopologyChange::PeerConnected(node1.clone(), edge0.clone())) + .await; + // This update doesn't trigger a broadcast because node0's available routes haven't changed + assert_eq!(None, distance_vector_update); + + // Process disconnection of node1 + let distance_vector_update = graph + .process_network_event(NetworkTopologyChange::PeerDisconnected(node1.clone())) + .await + .unwrap(); + graph.verify_own_distance_vector(HashMap::from([(node0.clone(), 0)]), &distance_vector_update); + // Process the same event without error + let distance_vector_update = + graph.process_network_event(NetworkTopologyChange::PeerDisconnected(node1.clone())).await; + // This update doesn't trigger a broadcast because node0's available routes haven't changed + assert_eq!(None, distance_vector_update); +} + +#[tokio::test] +async fn test_receive_distance_vector_before_processing_local_connection() { + let node0 = random_peer_id(); + let node1 = random_peer_id(); + let node2 = random_peer_id(); + + let graph = + Arc::new(GraphV2::new(GraphConfigV2 { node_id: node0.clone(), prune_edges_after: None })); + + let edge0 = Edge::make_fake_edge(node0.clone(), node1.clone(), 123); + let edge1 = Edge::make_fake_edge(node1.clone(), node2.clone(), 456); + + // Receive a DistanceVector from node1 with node2 behind it; 0--1--2 + // The local node has not processed a NetworkTopologyChange::PeerConnected event + // for node1, but it should handle this DistanceVector correctly anyway. + let distance_vector_update = graph + .process_network_event(NetworkTopologyChange::PeerAdvertisedDistances( + network_protocol::DistanceVector { + root: node1.clone(), + distances: vec![ + AdvertisedPeerDistance { destination: node1.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node0.clone(), distance: 1 }, + AdvertisedPeerDistance { destination: node2.clone(), distance: 1 }, + ], + edges: vec![edge0.clone(), edge1.clone()], + }, + )) + .await + .unwrap(); + graph.verify_own_distance_vector( + HashMap::from([(node0.clone(), 0), (node1.clone(), 1), (node2.clone(), 2)]), + &distance_vector_update, + ); +} + +#[tokio::test] +async fn test_receive_invalid_distance_vector() { + let node0 = random_peer_id(); + let node1 = random_peer_id(); + let node2 = random_peer_id(); + + let graph = + Arc::new(GraphV2::new(GraphConfigV2 { node_id: node0.clone(), prune_edges_after: None })); + + let edge0 = Edge::make_fake_edge(node0.clone(), node1.clone(), 123); + let edge1 = Edge::make_fake_edge(node1.clone(), node2.clone(), 456); + + graph + .process_invalid_network_event(NetworkTopologyChange::PeerAdvertisedDistances( + network_protocol::DistanceVector { + root: node1.clone(), + distances: vec![ + AdvertisedPeerDistance { destination: node1.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node0.clone(), distance: 1 }, + AdvertisedPeerDistance { destination: node2.clone(), distance: 1 }, + ], + // Missing edge + edges: vec![edge1.clone()], + }, + )) + .await; + + graph + .process_invalid_network_event(NetworkTopologyChange::PeerAdvertisedDistances( + network_protocol::DistanceVector { + root: node1.clone(), + // Missing route shown by edges + distances: vec![ + AdvertisedPeerDistance { destination: node1.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node0.clone(), distance: 1 }, + ], + edges: vec![edge0.clone(), edge1.clone()], + }, + )) + .await; + + graph + .process_invalid_network_event(NetworkTopologyChange::PeerAdvertisedDistances( + network_protocol::DistanceVector { + root: node1.clone(), + distances: vec![ + AdvertisedPeerDistance { destination: node1.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node0.clone(), distance: 1 }, + // Route length is shorter than shown by edges + AdvertisedPeerDistance { destination: node2.clone(), distance: 0 }, + ], + edges: vec![edge0.clone(), edge1.clone()], + }, + )) + .await; +} + +#[tokio::test] +async fn receive_distance_vector_without_route_to_local_node() { + let node0 = random_peer_id(); + let node1 = random_peer_id(); + let node2 = random_peer_id(); + + let graph = + Arc::new(GraphV2::new(GraphConfigV2 { node_id: node0.clone(), prune_edges_after: None })); + + let edge0 = Edge::make_fake_edge(node0.clone(), node1.clone(), 123); + let edge1 = Edge::make_fake_edge(node1.clone(), node2.clone(), 456); + + // Broadcasting a distance vector which doesn't have a route to the receiving node + // is valid behavior, but it doesn't provide the receiving node any routes. + let distance_vector_update = graph + .process_network_event(NetworkTopologyChange::PeerAdvertisedDistances( + network_protocol::DistanceVector { + root: node1.clone(), + distances: vec![ + // No route to the receiving node node0 + AdvertisedPeerDistance { destination: node1.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node2.clone(), distance: 1 }, + ], + edges: vec![edge1.clone()], + }, + )) + .await; + assert_eq!(None, distance_vector_update); + + // Let node0 realize it has a direct connection to node1 + let distance_vector_update = graph + .process_network_event(NetworkTopologyChange::PeerConnected(node1.clone(), edge0)) + .await + .unwrap(); + graph.verify_own_distance_vector( + HashMap::from([(node0.clone(), 0), (node1.clone(), 1)]), + &distance_vector_update, + ); + + // Now the same advertised routes from the tree tree 1--2 can be handled by node0, + // which will combine it with the direct edge 0--1 to produce 0--1--2. + let distance_vector_update = graph + .process_network_event(NetworkTopologyChange::PeerAdvertisedDistances( + network_protocol::DistanceVector { + root: node1.clone(), + distances: vec![ + // No route to the receiving node node0 + AdvertisedPeerDistance { destination: node1.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node2.clone(), distance: 1 }, + ], + edges: vec![edge1.clone()], + }, + )) + .await + .unwrap(); + graph.verify_own_distance_vector( + HashMap::from([(node0.clone(), 0), (node1.clone(), 1), (node2.clone(), 2)]), + &distance_vector_update, + ); + + // node0 should also be able to handle node1's default DistanceVector with no edges + let distance_vector_update = graph + .process_network_event(NetworkTopologyChange::PeerAdvertisedDistances( + network_protocol::DistanceVector { + root: node1.clone(), + distances: vec![AdvertisedPeerDistance { destination: node1.clone(), distance: 0 }], + edges: vec![], + }, + )) + .await + .unwrap(); + graph.verify_own_distance_vector( + HashMap::from([(node0.clone(), 0), (node1.clone(), 1)]), + &distance_vector_update, + ); +} + +/// This test produces a situation in which it is not possible for node0 to construct a spanning +/// tree which is exactly consistent with its distance vector. +/// +/// node0 ends up with a distance of 3 to node4 and a distance of 1 to node2. +/// For either destination, node0 knows a chain of signed edges producing the claimed distance: +/// 0--1--2--4 +/// 0--2 +/// However, it is not possible to construct a tree containing both of these chains. +/// +/// We handle this by allowing the node to construct a spanning tree which achieves all of its +/// claimed distances _or better_. In this case, 0--2--4 is valid. +/// +/// The situation arises as a result of inconsistent states of node1 and node2: +/// - node1 is telling us that node2 has a connection to node4 +/// - node2 is telling us that it has no connection to node4 +/// +/// It is not the responsibility of node0 to decide who is right; perhaps the connection was lost +/// and node1 hasn't realized it yet, or perhaps the connection is newly formed and we haven't received +/// an update from node2 yet (note that direct latency for 0--2 may be worse than the latency 0--1--2). +/// +/// Instead, node0 trusts its peers to have the exact distances which they claim, and does not try to +/// deduce anything from the spanning trees they provide other than verifying the claimed distances. +#[tokio::test] +async fn inconsistent_peers() { + let node0 = random_peer_id(); + let node1 = random_peer_id(); + let node2 = random_peer_id(); + let node3 = random_peer_id(); + let node4 = random_peer_id(); + + let graph = + Arc::new(GraphV2::new(GraphConfigV2 { node_id: node0.clone(), prune_edges_after: None })); + + let edge01 = Edge::make_fake_edge(node0.clone(), node1.clone(), 123); + let edge02 = Edge::make_fake_edge(node0.clone(), node2.clone(), 123); + let edge12 = Edge::make_fake_edge(node1.clone(), node2.clone(), 123); + let edge13 = Edge::make_fake_edge(node1.clone(), node3.clone(), 123); + let edge24 = Edge::make_fake_edge(node2.clone(), node4.clone(), 123); + + // Receive a DistanceVector from node1 with routes to 2, 3, 4 behind it + // 0 -- 1 -- 3 + // \ + // 2 -- 4 + graph + .process_network_event(NetworkTopologyChange::PeerAdvertisedDistances( + network_protocol::DistanceVector { + root: node1.clone(), + distances: vec![ + AdvertisedPeerDistance { destination: node1.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node0.clone(), distance: 1 }, + AdvertisedPeerDistance { destination: node2.clone(), distance: 1 }, + AdvertisedPeerDistance { destination: node3.clone(), distance: 1 }, + AdvertisedPeerDistance { destination: node4.clone(), distance: 2 }, + ], + edges: vec![edge01.clone(), edge12.clone(), edge13.clone(), edge24.clone()], + }, + )) + .await; + + // Receive a DistanceVector from node2 with routes to 1, 3 behind it + // 1 -- 3 + // / + // 0 -- 2 + // + // Notably, node2 does not advertise a route to node 4 + let distance_vector_update = graph + .process_network_event(NetworkTopologyChange::PeerAdvertisedDistances( + network_protocol::DistanceVector { + root: node2.clone(), + distances: vec![ + AdvertisedPeerDistance { destination: node2.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node0.clone(), distance: 1 }, + AdvertisedPeerDistance { destination: node1.clone(), distance: 1 }, + AdvertisedPeerDistance { destination: node3.clone(), distance: 2 }, + ], + edges: vec![edge02.clone(), edge12.clone(), edge13.clone()], + }, + )) + .await + .unwrap(); + + // Best available advertised route to each destination + let expected_routes = HashMap::from([ + (node0.clone(), 0), + (node1.clone(), 1), + (node2.clone(), 1), + (node3.clone(), 2), + (node4.clone(), 3), + ]); + + // There is no set of edges which produces a tree exactly consistent with `expected_routes`, + // but we should be able to construct a valid DistanceVector anyway + graph.verify_own_distance_vector(expected_routes, &distance_vector_update); +} + +#[tokio::test] +async fn test_distance_vector_nonce_expiration() { + let clock = time::FakeClock::default(); + + let node0 = random_peer_id(); + let graph = Arc::new(GraphV2::new(GraphConfigV2 { + node_id: node0.clone(), + prune_edges_after: Some(PRUNE_EDGES_AFTER), + })); + + let initial_nonce = Edge::create_fresh_nonce(&clock.clock()); + + // Add a peer node1 which advertises node2 behind it; 0--1--2 + let node1 = random_peer_id(); + let node2 = random_peer_id(); + let edge01 = Edge::make_fake_edge(node0.clone(), node1.clone(), initial_nonce); + let edge12 = Edge::make_fake_edge(node1.clone(), node2.clone(), initial_nonce); + assert!(graph.update_distance_vector( + node1.clone(), + vec![ + AdvertisedPeerDistance { destination: node1.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node0.clone(), distance: 1 }, + AdvertisedPeerDistance { destination: node2, distance: 1 }, + ], + vec![edge01, edge12] + )); + + assert!(graph.has_distance_vector(&node1)); + + // Advance the clock until the edges in the original DistanceVector shared by node1 expire + clock.advance(PRUNE_EDGES_AFTER + time::Duration::seconds(1)); + + // Recompute routes + graph.recompute_routes(&clock.clock()).await; + + // Check that the expired distance vector was removed + assert!(!graph.has_distance_vector(&node1)); +} + +#[tokio::test] +async fn test_distance_vector_nonce_refresh() { + let clock = time::FakeClock::default(); + + let node0 = random_peer_id(); + let graph = Arc::new(GraphV2::new(GraphConfigV2 { + node_id: node0.clone(), + prune_edges_after: Some(PRUNE_EDGES_AFTER), + })); + + let initial_nonce = Edge::create_fresh_nonce(&clock.clock()); + + // Add a peer node1 which advertises node2 behind it; 0--1--2 + let node1 = random_peer_id(); + let node2 = random_peer_id(); + let edge01 = Edge::make_fake_edge(node0.clone(), node1.clone(), initial_nonce); + let edge12 = Edge::make_fake_edge(node1.clone(), node2.clone(), initial_nonce); + assert!(graph.update_distance_vector( + node1.clone(), + vec![ + AdvertisedPeerDistance { destination: node1.clone(), distance: 0 }, + AdvertisedPeerDistance { destination: node0.clone(), distance: 1 }, + AdvertisedPeerDistance { destination: node2.clone(), distance: 1 }, + ], + vec![edge01, edge12] + )); + + assert!(graph.has_distance_vector(&node1)); + + // Advance the clock, then refresh the nonces on the stored edges; + clock.advance(PRUNE_EDGES_AFTER / 2); + let refreshed_nonce = Edge::create_fresh_nonce(&clock.clock()); + let edge01 = Edge::make_fake_edge(node0.clone(), node1.clone(), refreshed_nonce); + let edge12 = Edge::make_fake_edge(node1.clone(), node2, refreshed_nonce); + + graph + .process_network_event(NetworkTopologyChange::EdgeNonceRefresh(vec![ + edge01, + edge12.clone(), + ])) + .await; + + // Further advance the clock until the edges in the original DistanceVector shared by node1 expire + clock.advance(PRUNE_EDGES_AFTER / 2 + time::Duration::seconds(1)); + + // Recompute routes and check that the distance vector did not expire + graph.recompute_routes(&clock.clock()).await; + assert!(graph.has_distance_vector(&node1)); + + // Advance the clock again, then refresh only edge01 + clock.advance(PRUNE_EDGES_AFTER / 2); + let refreshed_nonce = Edge::create_fresh_nonce(&clock.clock()); + let edge01 = Edge::make_fake_edge(node0.clone(), node1.clone(), refreshed_nonce); + + graph + .process_network_event(NetworkTopologyChange::EdgeNonceRefresh(vec![edge01, edge12])) + .await; + + // Further advance the clock so that edge12 expires + clock.advance(PRUNE_EDGES_AFTER / 2 + time::Duration::seconds(1)); + + // Recompute routes and check that the distance vector expires + graph.recompute_routes(&clock.clock()).await; + assert!(!graph.has_distance_vector(&node1)); +} diff --git a/chain/network/src/routing/mod.rs b/chain/network/src/routing/mod.rs new file mode 100644 index 000000000..f37d9e38e --- /dev/null +++ b/chain/network/src/routing/mod.rs @@ -0,0 +1,10 @@ +mod bfs; +pub(crate) mod edge; +mod edge_cache; +mod graph; +mod graph_v2; +pub(crate) mod route_back_cache; +pub mod routing_table_view; + +pub(crate) use graph::{DistanceTable, Graph, GraphConfig, NextHopTable}; +pub(crate) use graph_v2::{GraphConfigV2, GraphV2, NetworkTopologyChange}; diff --git a/chain/network/src/routing/route_back_cache.rs b/chain/network/src/routing/route_back_cache.rs new file mode 100644 index 000000000..13d508262 --- /dev/null +++ b/chain/network/src/routing/route_back_cache.rs @@ -0,0 +1,461 @@ +use unc_async::time; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::PeerId; +use std::collections::{btree_map, BTreeMap, BTreeSet, HashMap}; + +/// default value for `capacity` +const DEFAULT_CAPACITY: usize = 100_000; +/// default value for `evict_timeout` +const DEFAULT_CACHE_EVICT_TIMEOUT: time::Duration = time::Duration::milliseconds(120_000); +/// default value for `remove_frequent_min_size` +const DEFAULT_REMOVE_BATCH_SIZE: usize = 100; + +/// Cache to store route back messages. +/// +/// The interface of the cache is similar to a regular `HashMap`: +/// elements can be inserted, fetched and removed. +/// +/// Motivation behind the following (complex) design: +/// +/// Since this cache is populated with messages that should be routed +/// back it is important that elements are not removed unless there is +/// some confidence that the response is not going to arrive. +/// +/// A naive cache can be easily abused, since a malicious actor can send +/// void messages that should be routed back, letting users replacing useful +/// entries on the cache for fake ones, and producing most of the messages +/// that require route back being dropped. +/// +/// Solution: +/// +/// The cache will accept new elements until it is full. If it receives a new +/// element but it implies going over the capacity, the message to remove is +/// selected as following: +/// +/// 1. For every message store the time it arrives. +/// 2. For every peer store how many message should be routed to it. +/// +/// First are removed messages that have been in the cache more time than +/// `EVICTED_TIMEOUT`. If no message was removed, it is removed the oldest +/// message from the peer with more messages in the cache. +/// +/// Rationale: +/// +/// - Old entries in the cache will be eventually removed (no memory leak). +/// - If the cache is not at full capacity, all new records will be stored. +/// - If a peer try to abuse the system, it will be able to allocate at most +/// `capacity / number_of_active_connections` entries. +pub struct RouteBackCache { + /// Maximum number of records allowed in the cache. + capacity: usize, + /// Maximum time allowed before removing a record from the cache. + evict_timeout: time::Duration, + /// Minimum number of records to delete from offending peer when the cache is full. + remove_frequent_min_size: usize, + /// Main map from message hash to time where it was created + target peer + /// Size: O(capacity) + main: HashMap, + /// Number of records allocated by each PeerId. + /// The size is stored with negative sign, to order in PeerId in decreasing order. + /// To avoid handling with negative number all sizes are added by capacity. + /// Size: O(number of active connections) + size_per_target: BTreeSet<(usize, PeerId)>, + /// List of all hashes associated with each PeerId. Hashes within each PeerId + /// are sorted by the time they arrived from older to newer. + /// Size: O(capacity) + record_per_target: BTreeMap>, +} + +impl Default for RouteBackCache { + fn default() -> Self { + Self::new(DEFAULT_CAPACITY, DEFAULT_CACHE_EVICT_TIMEOUT, DEFAULT_REMOVE_BATCH_SIZE) + } +} + +impl RouteBackCache { + pub fn new( + capacity: usize, + evict_timeout: time::Duration, + remove_frequent_min_size: usize, + ) -> Self { + assert!(capacity > 0); + + Self { + capacity, + evict_timeout, + remove_frequent_min_size, + main: HashMap::new(), + size_per_target: BTreeSet::new(), + record_per_target: BTreeMap::new(), + } + } + + fn is_full(&self) -> bool { + self.capacity == self.main.len() + } + + fn remove_frequent(&mut self) { + let (mut size, target) = self.size_per_target.iter().next().cloned().unwrap(); + let mut removed = 0; + + if let btree_map::Entry::Occupied(mut entry) = self.record_per_target.entry(target.clone()) + { + { + let records = entry.get_mut(); + + match records.iter().nth(self.remove_frequent_min_size).cloned() { + Some(key) => { + let mut to_remove = records.split_off(&key); + std::mem::swap(&mut to_remove, records); + + for record in to_remove { + self.main.remove(&record.1); + removed += 1; + } + } + None => { + for record in records.iter() { + self.main.remove(&record.1); + removed += 1; + } + records.clear(); + } + } + } + + if entry.get().is_empty() { + entry.remove(); + } + } + + self.size_per_target.remove(&(size, target.clone())); + // Since self.size is equal to capacity - real_size, adding 1, is equivalent to subtracting 1 from the real size. + size += removed; + + if self.capacity - size != 0 { + self.size_per_target.insert((size, target)); + } + } + + fn remove_evicted(&mut self, clock: &time::Clock) { + if self.is_full() { + self.remove_frequent(); + + let now = clock.now(); + let remove_until = match now.checked_sub(self.evict_timeout) { + Some(t) => t, + None => return, + }; + let mut remove_empty = vec![]; + + for (key, value) in self.record_per_target.iter_mut() { + let prev_size = value.len(); + let keep = value.split_off(&(remove_until, CryptoHash::default())); + + for evicted in value.iter() { + self.main.remove(&evicted.1); + } + + *value = keep; + let new_size = value.len(); + + if prev_size != new_size { + self.size_per_target.remove(&(self.capacity - prev_size, key.clone())); + + if new_size > 0 { + self.size_per_target.insert((self.capacity - new_size, key.clone())); + } + } + + if new_size == 0 { + remove_empty.push(key.clone()); + } + } + + for key in remove_empty { + self.record_per_target.remove(&key); + } + } + } + + pub fn get(&self, hash: &CryptoHash) -> Option<&PeerId> { + self.main.get(hash).map(|(_, target)| target) + } + + pub fn remove(&mut self, clock: &time::Clock, hash: &CryptoHash) -> Option { + self.remove_evicted(clock); + + if let Some((time, target)) = self.main.remove(hash) { + // Number of elements associated with this target + let mut size = self.record_per_target.get(&target).map(|x| x.len()).unwrap(); + + // Remove from `size_per_target` since value is going to be updated + self.size_per_target.remove(&(self.capacity - size, target.clone())); + + // Remove current hash from the list associated with `record_par_target` + if let Some(records) = self.record_per_target.get_mut(&target) { + records.remove(&(time, *hash)); + } + + // Calculate new size + size -= 1; + + if size == 0 { + // If there are no elements remove entry associated with this peer + self.record_per_target.remove(&target); + } else { + // otherwise, add this peer to `size_per_target` with new size + self.size_per_target.insert((self.capacity - size, target.clone())); + } + + Some(target) + } else { + None + } + } + + pub fn insert(&mut self, clock: &time::Clock, hash: CryptoHash, target: PeerId) { + if self.main.contains_key(&hash) { + return; + } + + self.remove_evicted(clock); + + let now = clock.now(); + + self.main.insert(hash, (now, target.clone())); + + let mut size = self.record_per_target.get(&target).map_or(0, |x| x.len()); + + if size > 0 { + self.size_per_target.remove(&(self.capacity - size, target.clone())); + } + + self.record_per_target.entry(target.clone()).or_default().insert((now, hash)); + + size += 1; + self.size_per_target.insert((self.capacity - size, target)); + } +} + +#[cfg(test)] +mod test { + use super::*; + use unc_primitives::hash::hash; + + /// Check internal state of the cache is ok + fn check_consistency(cache: &RouteBackCache) { + assert!(cache.main.len() <= cache.capacity); + assert_eq!(cache.size_per_target.len(), cache.record_per_target.len()); + + for (neg_size, target) in cache.size_per_target.iter() { + let size = cache.capacity - neg_size; + assert!(size > 0); + assert_eq!(size, cache.record_per_target.get(target).map(|x| x.len()).unwrap()); + } + + let mut total = 0; + + for (target, records) in cache.record_per_target.iter() { + total += records.len(); + + for (time, record) in records.iter() { + assert_eq!(cache.main.get(record).unwrap(), &(*time, target.clone())); + } + } + + assert_eq!(cache.main.len(), total); + } + + fn create_message(ix: u8) -> (PeerId, CryptoHash) { + (PeerId::random(), hash(&[ix])) + } + + #[test] + fn simple() { + let clock = time::FakeClock::default(); + let mut cache = RouteBackCache::new(100, time::Duration::milliseconds(1000000000), 1); + let (peer0, hash0) = create_message(0); + + check_consistency(&cache); + assert_eq!(cache.get(&hash0), None); + cache.insert(&clock.clock(), hash0, peer0.clone()); + check_consistency(&cache); + assert_eq!(cache.get(&hash0), Some(&peer0)); + assert_eq!(cache.remove(&clock.clock(), &hash0), Some(peer0)); + check_consistency(&cache); + assert_eq!(cache.get(&hash0), None); + } + + /// Check record is removed after some timeout. + #[test] + fn evicted() { + let clock = time::FakeClock::default(); + let mut cache = RouteBackCache::new(1, time::Duration::milliseconds(1), 1); + let (peer0, hash0) = create_message(0); + + cache.insert(&clock.clock(), hash0, peer0.clone()); + check_consistency(&cache); + assert_eq!(cache.get(&hash0), Some(&peer0)); + clock.advance(time::Duration::milliseconds(2)); + cache.remove_evicted(&clock.clock()); + check_consistency(&cache); + assert_eq!(cache.get(&hash0), None); + } + + /// Check element is removed after timeout triggered by insert at max capacity. + #[test] + fn insert_evicted() { + let clock = time::FakeClock::default(); + let mut cache = RouteBackCache::new(1, time::Duration::milliseconds(1), 1); + let (peer0, hash0) = create_message(0); + let (peer1, hash1) = create_message(1); + + cache.insert(&clock.clock(), hash0, peer0.clone()); + check_consistency(&cache); + assert_eq!(cache.get(&hash0), Some(&peer0)); + clock.advance(time::Duration::milliseconds(2)); + cache.insert(&clock.clock(), hash1, peer1.clone()); + check_consistency(&cache); + assert_eq!(cache.get(&hash1), Some(&peer1)); + assert_eq!(cache.get(&hash0), None); + } + + /// Check element is removed after insert because cache is at max capacity. + #[test] + fn insert_override() { + let clock = time::FakeClock::default(); + let mut cache = RouteBackCache::new(1, time::Duration::milliseconds(1000000000), 1); + let (peer0, hash0) = create_message(0); + let (peer1, hash1) = create_message(1); + + cache.insert(&clock.clock(), hash0, peer0.clone()); + check_consistency(&cache); + assert_eq!(cache.get(&hash0), Some(&peer0)); + clock.advance(time::Duration::milliseconds(2)); + cache.insert(&clock.clock(), hash1, peer1.clone()); + check_consistency(&cache); + assert_eq!(cache.get(&hash1), Some(&peer1)); + assert_eq!(cache.get(&hash0), None); + } + + /// Insert three elements. One old element from peer0 and two recent elements from peer1. + /// Check that old element from peer0 is removed, even while peer1 has more elements. + #[test] + fn prefer_evict() { + let clock = time::FakeClock::default(); + let mut cache = RouteBackCache::new(3, time::Duration::milliseconds(100), 1); + let (peer0, hash0) = create_message(0); + let (peer1, hash1) = create_message(1); + let (_, hash2) = create_message(2); + let (peer3, hash3) = create_message(3); + + cache.insert(&clock.clock(), hash0, peer0); + clock.advance(time::Duration::milliseconds(1100)); + cache.insert(&clock.clock(), hash1, peer1.clone()); + cache.insert(&clock.clock(), hash2, peer1); + cache.insert(&clock.clock(), hash3, peer3); + check_consistency(&cache); + + assert!(cache.get(&hash0).is_none()); // This is removed because it was evicted + assert!(cache.get(&hash1).is_none()); // This is removed since frequent are always removed + assert!(cache.get(&hash2).is_some()); + assert!(cache.get(&hash3).is_some()); + } + + /// Insert three elements. One old element from peer0 and two recent elements from peer1. + /// Check that older element from peer1 is removed, since evict timeout haven't passed yet. + #[test] + fn prefer_full() { + let clock = time::FakeClock::default(); + let mut cache = RouteBackCache::new(3, time::Duration::milliseconds(100000), 1); + let (peer0, hash0) = create_message(0); + let (peer1, hash1) = create_message(1); + let (_, hash2) = create_message(2); + let (peer3, hash3) = create_message(3); + + cache.insert(&clock.clock(), hash0, peer0); + clock.advance(time::Duration::milliseconds(1000)); + cache.insert(&clock.clock(), hash1, peer1.clone()); + cache.insert(&clock.clock(), hash2, peer1); + cache.insert(&clock.clock(), hash3, peer3); + check_consistency(&cache); + + assert!(cache.get(&hash0).is_some()); + assert!(cache.get(&hash1).is_none()); // This is removed, other exists + assert!(cache.get(&hash2).is_some()); + assert!(cache.get(&hash3).is_some()); + } + + /// Insert three elements. One old element from peer0 and two recent elements from peer1. + /// Check that older element from peer1 is removed, since evict timeout haven't passed yet. + #[test] + fn remove_all_frequent() { + let clock = time::FakeClock::default(); + let mut cache = RouteBackCache::new(3, time::Duration::milliseconds(100000), 2); + let (peer0, hash0) = create_message(0); + let (peer1, hash1) = create_message(1); + let (_, hash2) = create_message(2); + let (peer3, hash3) = create_message(3); + + cache.insert(&clock.clock(), hash0, peer0); + clock.advance(time::Duration::milliseconds(1000)); + cache.insert(&clock.clock(), hash1, peer1.clone()); + cache.insert(&clock.clock(), hash2, peer1); + cache.insert(&clock.clock(), hash3, peer3); + check_consistency(&cache); + + assert!(cache.get(&hash0).is_some()); + assert!(cache.get(&hash1).is_none()); // This is removed since belong to most frequent + assert!(cache.get(&hash2).is_none()); // This is removed since belong to most frequent + assert!(cache.get(&hash3).is_some()); + } + + /// Simulate an attack from a malicious actor which sends several routing back message + /// to overtake the cache. Create 4 legitimate hashes from 3 peers. Then insert + /// 50 hashes from attacker. Since the cache size is 17, first 5 message from attacker will + /// be stored, and it will become the peer with more entries (5 > 4). All 12 legitimate + /// initial hashes should be present in the cache after the attack. + #[test] + fn poison_attack() { + let clock = time::FakeClock::default(); + let mut cache = RouteBackCache::new(17, time::Duration::milliseconds(1000000), 1); + let mut ix = 0; + + let mut peers = vec![]; + + for _ in 0..3 { + let peer = PeerId::random(); + + for _ in 0..4 { + let hashi = hash(&[ix]); + ix += 1; + cache.insert(&clock.clock(), hashi, peer.clone()); + } + + peers.push(peer); + } + + let attacker = PeerId::random(); + + for _ in 0..50 { + let hashi = hash(&[ix]); + ix += 1; + cache.insert(&clock.clock(), hashi, attacker.clone()); + } + + check_consistency(&cache); + + ix = 0; + + for i in 0..3 { + let peer = peers[i as usize].clone(); + + for _ in 0..4 { + let hashi = hash(&[ix]); + ix += 1; + assert_eq!(cache.get(&hashi), Some(&peer)); + } + } + } +} diff --git a/chain/network/src/routing/routing_table_view/mod.rs b/chain/network/src/routing/routing_table_view/mod.rs new file mode 100644 index 000000000..68fe8fc97 --- /dev/null +++ b/chain/network/src/routing/routing_table_view/mod.rs @@ -0,0 +1,112 @@ +use crate::routing; +use lru::LruCache; +use unc_primitives::network::PeerId; +use parking_lot::Mutex; +use std::sync::Arc; + +#[cfg(test)] +mod tests; + +const LAST_ROUTED_CACHE_SIZE: usize = 10_000; + +pub(crate) struct RoutingTableView(Mutex); + +struct Inner { + /// For each peer, the set of neighbors which are one hop closer to `my_peer_id`. + /// Alternatively, if we look at the set of all shortest path from `my_peer_id` to peer, + /// this will be the set of first nodes on all such paths. + next_hops: Arc, + + /// Contains the shortest path length for each routable peer in the network. + /// Used only to collect metrics measuring routing performance. + /// TODO(saketh): Remove this when we deprecate the V1 routing protocol. + distance: Arc, + + /// Counter of number of calls to find_route_by_peer_id. + find_route_calls: u64, + /// Last time the given peer was selected by find_route_by_peer_id. + last_routed: LruCache, +} + +impl Inner { + /// Select a connected peer on some shortest path to `peer_id`. + /// If there are several such peers, pick the least recently used one. + fn find_next_hop(&mut self, peer_id: &PeerId) -> Result { + let peers = self.next_hops.get(peer_id).ok_or(FindRouteError::PeerUnreachable)?; + let next_hop = peers + .iter() + .min_by_key(|p| self.last_routed.get(*p).copied().unwrap_or(0)) + .ok_or(FindRouteError::PeerUnreachable)?; + self.last_routed.put(next_hop.clone(), self.find_route_calls); + self.find_route_calls += 1; + Ok(next_hop.clone()) + } + + fn update( + &mut self, + next_hops: Arc, + distance: Arc, + ) { + self.next_hops = next_hops; + self.distance = distance + } +} + +#[derive(Debug)] +pub(crate) enum FindRouteError { + PeerUnreachable, + RouteBackNotFound, +} + +impl RoutingTableView { + pub fn new() -> Self { + Self(Mutex::new(Inner { + next_hops: Default::default(), + distance: Default::default(), + find_route_calls: 0, + last_routed: LruCache::new(LAST_ROUTED_CACHE_SIZE), + })) + } + + pub(crate) fn update( + &self, + next_hops: Arc, + distance: Arc, + ) { + self.0.lock().update(next_hops, distance) + } + + pub(crate) fn reachable_peers(&self) -> usize { + // There is an implicit assumption here that all next_hops entries are non-empty. + // To enforce this, we would need to make NextHopTable a newtype rather than an alias, + // and add appropriate constructors, which would filter out empty entries. + self.0.lock().next_hops.len() + } + + // Given a PeerId to which we wish to route a message, returns the first hop on a + // route to the target. If no route is known, produces FindRouteError. + pub(crate) fn find_next_hop_for_target( + &self, + target: &PeerId, + ) -> Result { + self.0.lock().find_next_hop(target) + } + + pub(crate) fn get_distance(&self, peer_id: &PeerId) -> Option { + self.0.lock().distance.get(peer_id).copied() + } + + pub(crate) fn view_route(&self, peer_id: &PeerId) -> Option> { + self.0.lock().next_hops.get(peer_id).cloned() + } + + pub(crate) fn info(&self) -> RoutingTableInfo { + let inner = self.0.lock(); + RoutingTableInfo { next_hops: inner.next_hops.clone() } + } +} + +#[derive(Debug)] +pub struct RoutingTableInfo { + pub next_hops: Arc, +} diff --git a/chain/network/src/routing/routing_table_view/tests.rs b/chain/network/src/routing/routing_table_view/tests.rs new file mode 100644 index 000000000..9c10b2ae2 --- /dev/null +++ b/chain/network/src/routing/routing_table_view/tests.rs @@ -0,0 +1,29 @@ +use crate::network_protocol::testonly as data; +use crate::routing; +use crate::routing::routing_table_view::*; +use crate::testonly::make_rng; +use rand::seq::SliceRandom; +use std::sync::Arc; + +#[test] +fn find_route() { + let mut rng = make_rng(385305732); + let rng = &mut rng; + + // Create a sample NextHopTable. + let peers: Vec<_> = (0..10).map(|_| data::make_peer_id(rng)).collect(); + let mut next_hops = routing::NextHopTable::new(); + for p in &peers { + next_hops.insert(p.clone(), (0..3).map(|_| peers.choose(rng).cloned().unwrap()).collect()); + } + let next_hops = Arc::new(next_hops); + + // Check that RoutingTableView always selects a valid next hop. + let rtv = RoutingTableView::new(); + rtv.update(next_hops.clone(), Default::default()); + for _ in 0..1000 { + let p = peers.choose(rng).unwrap(); + let got = rtv.find_next_hop_for_target(&p).unwrap(); + assert!(next_hops.get(p).unwrap().contains(&got)); + } +} diff --git a/chain/network/src/shards_manager.rs b/chain/network/src/shards_manager.rs new file mode 100644 index 000000000..5ce81914f --- /dev/null +++ b/chain/network/src/shards_manager.rs @@ -0,0 +1,23 @@ +use std::time::Instant; + +use actix::Message; +use unc_primitives::{hash::CryptoHash, sharding::PartialEncodedChunk}; + +use crate::types::{ + PartialEncodedChunkForwardMsg, PartialEncodedChunkRequestMsg, PartialEncodedChunkResponseMsg, +}; + +#[derive(Message, Debug, strum::IntoStaticStr)] +#[rtype(result = "()")] +pub enum ShardsManagerRequestFromNetwork { + ProcessPartialEncodedChunk(PartialEncodedChunk), + ProcessPartialEncodedChunkForward(PartialEncodedChunkForwardMsg), + ProcessPartialEncodedChunkResponse { + partial_encoded_chunk_response: PartialEncodedChunkResponseMsg, + received_time: Instant, + }, + ProcessPartialEncodedChunkRequest { + partial_encoded_chunk_request: PartialEncodedChunkRequestMsg, + route_back: CryptoHash, + }, +} diff --git a/chain/network/src/sink.rs b/chain/network/src/sink.rs new file mode 100644 index 000000000..21329e264 --- /dev/null +++ b/chain/network/src/sink.rs @@ -0,0 +1,56 @@ +/// Sink is a handler/wrapper of a function Fn(T) -> (). +/// It supports composition with functions Fn(U) -> Fn(T). +/// It is a test-only feature for aggregating internal events of a component under test +/// which are otherwise not observable via public API. +/// Ideally tests should rely solely on public API, however it is not the case as of today. +/// TODO(gprusak): once all network integration tests are migrated to crate, Sink should +/// be hidden behind #[cfg(test)]. +use std::sync::Arc; + +pub struct Sink(Option>>); + +impl Clone for Sink { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl Sink { + pub fn null() -> Self { + Self(None) + } + + pub fn push(&self, t: T) { + if let Some(f) = &self.0 { + f(t) + } + } + + pub fn new(f: impl Fn(T) + Sync + Send + 'static) -> Self { + Sink(Some(Arc::new(Box::new(f)))) + } +} + +impl Sink { + // Accepts a constructor of the value to push. + // Returns a function which does the push. + // If sink is null it doesn't call make() at all, + // therefore you can use this to skip expensive computation + // in non-test env. + pub fn delayed_push(&self, make: impl FnOnce() -> T) -> Box { + let maybe_ev = self.0.as_ref().map(|_| make()); + let this = self.clone(); + Box::new(move || { + maybe_ev.map(|ev| this.push(ev)); + }) + } +} + +impl Sink { + pub fn compose(&self, f: impl Send + Sync + 'static + Fn(U) -> T) -> Sink { + match self.0.clone() { + None => Sink::null(), + Some(s) => Sink::new(Box::new(move |u| s(f(u)))), + } + } +} diff --git a/chain/network/src/snapshot_hosts/mod.rs b/chain/network/src/snapshot_hosts/mod.rs new file mode 100644 index 000000000..c9ab08e6e --- /dev/null +++ b/chain/network/src/snapshot_hosts/mod.rs @@ -0,0 +1,136 @@ +//! Cache of SnapshotHostInfos. +//! +//! Each node in the network which is willing to generate and serve state snapshots +//! publishes a SnapshotHostInfo once per epoch. The info is flooded to all nodes +//! in the network and stored locally in this cache. + +use crate::concurrency; +use crate::network_protocol::SnapshotHostInfo; +use crate::network_protocol::SnapshotHostInfoVerificationError; +use lru::LruCache; +use unc_primitives::network::PeerId; +use parking_lot::Mutex; +use rayon::iter::ParallelBridge; +use std::collections::HashMap; +use std::sync::Arc; + +#[cfg(test)] +mod tests; + +#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)] +pub(crate) enum SnapshotHostInfoError { + #[error("found multiple entries for the same peer_id")] + DuplicatePeerId, + #[error(transparent)] + VerificationError(#[from] SnapshotHostInfoVerificationError), +} + +#[derive(Clone)] +pub struct Config { + /// The maximum number of SnapshotHostInfos to store locally. + /// At present this constraint is enforced using a simple + /// least-recently-used cache. In the future, we may wish to + /// implement something more sophisticated. + pub snapshot_hosts_cache_size: u32, +} + +struct Inner { + /// The latest known SnapshotHostInfo for each node in the network + hosts: LruCache>, +} + +impl Inner { + fn is_new(&self, h: &SnapshotHostInfo) -> bool { + match self.hosts.peek(&h.peer_id) { + Some(old) if old.epoch_height >= h.epoch_height => false, + _ => true, + } + } + + /// Inserts d into self.data, if it's new. + /// It returns the newly inserted value (or None if nothing changed). + /// The returned value should be broadcasted to the network. + fn try_insert(&mut self, d: Arc) -> Option> { + if !self.is_new(&d) { + return None; + } + self.hosts.push(d.peer_id.clone(), d.clone()); + Some(d) + } +} + +pub(crate) struct SnapshotHostsCache(Mutex); + +impl SnapshotHostsCache { + pub fn new(config: Config) -> Self { + let hosts = LruCache::new(config.snapshot_hosts_cache_size as usize); + Self(Mutex::new(Inner { hosts })) + } + + /// Selects new data and verifies the signatures. + /// Returns the verified new data and an optional error. + /// Note that even if error has been returned the partially validated output is returned anyway. + async fn verify( + &self, + data: Vec>, + ) -> (Vec>, Option) { + // Filter out any data which is outdated or which we already have. + let mut new_data = HashMap::new(); + { + let inner = self.0.lock(); + for d in data { + // Sharing multiple entries for the same peer is considered malicious, + // since all but one are obviously outdated. + if new_data.contains_key(&d.peer_id) { + return (vec![], Some(SnapshotHostInfoError::DuplicatePeerId)); + } + // It is fine to broadcast data we already know about. + // It is fine to broadcast data which we know to be outdated. + if inner.is_new(&d) { + new_data.insert(d.peer_id.clone(), d); + } + } + } + + // Verify the signatures in parallel. + // Verification will stop at the first encountered error. + let (data, verification_result) = concurrency::rayon::run(move || { + concurrency::rayon::try_map_result(new_data.into_values().par_bridge(), |d| { + match d.verify() { + Ok(()) => Ok(d), + Err(err) => Err(err), + } + }) + }) + .await; + match verification_result { + Ok(()) => (data, None), + Err(err) => (data, Some(SnapshotHostInfoError::VerificationError(err))), + } + } + + /// Verifies the signatures and inserts verified data to the cache. + /// Returns the data inserted and optionally a verification error. + /// WriteLock is acquired only for the final update (after verification). + pub async fn insert( + self: &Self, + data: Vec>, + ) -> (Vec>, Option) { + // Execute verification on the rayon threadpool. + let (data, err) = self.verify(data).await; + // Insert the successfully verified data, even if an error has been encountered. + let mut newly_inserted_data: Vec> = vec![]; + let mut inner = self.0.lock(); + for d in data { + if let Some(inserted) = inner.try_insert(d) { + newly_inserted_data.push(inserted); + } + } + // Return the inserted data. + (newly_inserted_data, err) + } + + pub fn get_hosts(&self) -> Vec> { + self.0.lock().hosts.iter().map(|(_, v)| v.clone()).collect() + } +} diff --git a/chain/network/src/snapshot_hosts/tests.rs b/chain/network/src/snapshot_hosts/tests.rs new file mode 100644 index 000000000..0767a8c5f --- /dev/null +++ b/chain/network/src/snapshot_hosts/tests.rs @@ -0,0 +1,202 @@ +use crate::network_protocol::{ + testonly as data, SnapshotHostInfoVerificationError, MAX_SHARDS_PER_SNAPSHOT_HOST_INFO, +}; +use crate::snapshot_hosts::{Config, SnapshotHostInfoError, SnapshotHostsCache}; +use crate::testonly::assert_is_superset; +use crate::testonly::{make_rng, AsSet as _}; +use crate::types::SnapshotHostInfo; +use unc_crypto::SecretKey; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::PeerId; +use unc_primitives::types::EpochHeight; +use unc_primitives::types::ShardId; +use std::collections::HashSet; +use std::sync::Arc; + +fn make_snapshot_host_info( + peer_id: &PeerId, + epoch_height: EpochHeight, + shards: Vec, + secret_key: &SecretKey, +) -> SnapshotHostInfo { + let sync_hash = CryptoHash::hash_borsh(epoch_height); + SnapshotHostInfo::new(peer_id.clone(), sync_hash, epoch_height, shards, secret_key) +} + +fn unwrap<'a, T: std::hash::Hash + std::cmp::Eq, E: std::fmt::Debug>( + v: &'a (T, Option), +) -> &'a T { + if let Some(err) = &v.1 { + panic!("unexpected error: {err:?}"); + } + &v.0 +} + +#[tokio::test] +async fn happy_path() { + init_test_logger(); + let mut rng = make_rng(2947294234); + let rng = &mut rng; + + let key0 = data::make_secret_key(rng); + let key1 = data::make_secret_key(rng); + let key2 = data::make_secret_key(rng); + + let peer0 = PeerId::new(key0.public_key()); + let peer1 = PeerId::new(key1.public_key()); + let peer2 = PeerId::new(key2.public_key()); + + let config = Config { snapshot_hosts_cache_size: 100 }; + let cache = SnapshotHostsCache::new(config); + assert_eq!(cache.get_hosts().len(), 0); // initially empty + + // initial insert + let info0 = Arc::new(make_snapshot_host_info(&peer0, 123, vec![0, 1, 2, 3], &key0)); + let info1 = Arc::new(make_snapshot_host_info(&peer1, 123, vec![2], &key1)); + let res = cache.insert(vec![info0.clone(), info1.clone()]).await; + assert_eq!([&info0, &info1].as_set(), unwrap(&res).as_set()); + assert_eq!([&info0, &info1].as_set(), cache.get_hosts().iter().collect::>()); + + // second insert with various types of updates + let info0new = Arc::new(make_snapshot_host_info(&peer0, 124, vec![1, 3], &key0)); + let info1old = Arc::new(make_snapshot_host_info(&peer1, 122, vec![0, 1, 2, 3], &key1)); + let info2 = Arc::new(make_snapshot_host_info(&peer2, 123, vec![2], &key2)); + let res = cache.insert(vec![info0new.clone(), info1old.clone(), info2.clone()]).await; + assert_eq!([&info0new, &info2].as_set(), unwrap(&res).as_set()); + assert_eq!( + [&info0new, &info1, &info2].as_set(), + cache.get_hosts().iter().collect::>() + ); +} + +#[tokio::test] +async fn invalid_signature() { + init_test_logger(); + let mut rng = make_rng(2947294234); + let rng = &mut rng; + + let key0 = data::make_secret_key(rng); + let key1 = data::make_secret_key(rng); + + let peer0 = PeerId::new(key0.public_key()); + let peer1 = PeerId::new(key1.public_key()); + + let config = Config { snapshot_hosts_cache_size: 100 }; + let cache = SnapshotHostsCache::new(config); + + let info0_invalid_sig = Arc::new(make_snapshot_host_info(&peer0, 1, vec![0, 1, 2, 3], &key1)); + let info1 = Arc::new(make_snapshot_host_info(&peer1, 1, vec![0, 1, 2, 3], &key1)); + let res = cache.insert(vec![info0_invalid_sig.clone(), info1.clone()]).await; + // invalid signature => InvalidSignature + assert_eq!( + Some(SnapshotHostInfoError::VerificationError( + SnapshotHostInfoVerificationError::InvalidSignature + )), + res.1 + ); + // Partial update is allowed in case an error is encountered. + // The valid info1 may or may not be processed before the invalid info0 is detected + // due to parallelization, so we check for superset rather than strict equality. + assert_is_superset(&[&info1].as_set(), &res.0.as_set()); + // Partial update should match the state. + assert_eq!(res.0.as_set(), cache.get_hosts().iter().collect::>()); +} + +#[tokio::test] +async fn too_many_shards() { + init_test_logger(); + let mut rng = make_rng(2947294234); + let rng = &mut rng; + + let key0 = data::make_secret_key(rng); + let key1 = data::make_secret_key(rng); + + let peer0 = PeerId::new(key0.public_key()); + let peer1 = PeerId::new(key1.public_key()); + + let config = Config { snapshot_hosts_cache_size: 100 }; + let cache = SnapshotHostsCache::new(config); + + // info0 is valid + let info0 = Arc::new(make_snapshot_host_info(&peer0, 1, vec![0, 1, 2, 3], &key0)); + + // info1 is invalid - it has more shard ids than MAX_SHARDS_PER_SNAPSHOT_HOST_INFO + let too_many_shards: Vec = + (0..(MAX_SHARDS_PER_SNAPSHOT_HOST_INFO as u64 + 1)).collect(); + let info1 = Arc::new(make_snapshot_host_info(&peer1, 1, too_many_shards, &key1)); + + // info1.verify() should fail + let expected_error = + SnapshotHostInfoVerificationError::TooManyShards(MAX_SHARDS_PER_SNAPSHOT_HOST_INFO + 1); + assert_eq!(info1.verify(), Err(expected_error.clone())); + + // Inserting should return the expected error (TooManyShards) + let res = cache.insert(vec![info0.clone(), info1.clone()]).await; + assert_eq!(Some(SnapshotHostInfoError::VerificationError(expected_error)), res.1); + // Partial update is allowed in case an error is encountered. + // The valid info0 may or may not be processed before the invalid info1 is detected + // due to parallelization, so we check for superset rather than strict equality. + assert_is_superset(&[&info0].as_set(), &res.0.as_set()); + // Partial update should match the state. + assert_eq!(res.0.as_set(), cache.get_hosts().iter().collect::>()); +} + +#[tokio::test] +async fn duplicate_peer_id() { + init_test_logger(); + let mut rng = make_rng(2947294234); + let rng = &mut rng; + + let key0 = data::make_secret_key(rng); + let peer0 = PeerId::new(key0.public_key()); + + let config = Config { snapshot_hosts_cache_size: 100 }; + let cache = SnapshotHostsCache::new(config); + + let info00 = Arc::new(make_snapshot_host_info(&peer0, 1, vec![0, 1, 2, 3], &key0)); + let info01 = Arc::new(make_snapshot_host_info(&peer0, 2, vec![0, 3], &key0)); + let res = cache.insert(vec![info00.clone(), info01.clone()]).await; + // duplicate peer ids => DuplicatePeerId + assert_eq!(Some(SnapshotHostInfoError::DuplicatePeerId), res.1); + // this type of malicious behavior is detected before verification even begins; + // no partial data is stored + assert_eq!(0, cache.get_hosts().len()); +} + +#[tokio::test] +async fn test_lru_eviction() { + init_test_logger(); + let mut rng = make_rng(2947294234); + let rng = &mut rng; + + let key0 = data::make_secret_key(rng); + let key1 = data::make_secret_key(rng); + let key2 = data::make_secret_key(rng); + + let peer0 = PeerId::new(key0.public_key()); + let peer1 = PeerId::new(key1.public_key()); + let peer2 = PeerId::new(key2.public_key()); + + let config = Config { snapshot_hosts_cache_size: 2 }; + let cache = SnapshotHostsCache::new(config); + + // initial inserts to capacity + let info0 = Arc::new(make_snapshot_host_info(&peer0, 123, vec![0, 1, 2, 3], &key0)); + let res = cache.insert(vec![info0.clone()]).await; + assert_eq!([&info0].as_set(), unwrap(&res).as_set()); + assert_eq!([&info0].as_set(), cache.get_hosts().iter().collect::>()); + + let info1 = Arc::new(make_snapshot_host_info(&peer1, 123, vec![2], &key1)); + let res = cache.insert(vec![info1.clone()]).await; + assert_eq!([&info1].as_set(), unwrap(&res).as_set()); + assert_eq!([&info0, &info1].as_set(), cache.get_hosts().iter().collect::>()); + + // insert past capacity + let info2 = Arc::new(make_snapshot_host_info(&peer2, 123, vec![1, 3], &key2)); + let res = cache.insert(vec![info2.clone()]).await; + // check that the new data is accepted + assert_eq!([&info2].as_set(), unwrap(&res).as_set()); + // check that the oldest data was evicted + assert_eq!([&info1, &info2].as_set(), cache.get_hosts().iter().collect::>()); +} diff --git a/chain/network/src/state_sync.rs b/chain/network/src/state_sync.rs new file mode 100644 index 000000000..6fa9ea221 --- /dev/null +++ b/chain/network/src/state_sync.rs @@ -0,0 +1,17 @@ +use unc_store::ShardUId; + +/// State sync response from peers. +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub enum StateSyncResponse { + HeaderResponse, + PartResponse, +} + +/// A strongly typed asynchronous API for the State Sync logic +/// It abstracts away the fact that it is implemented using actix +/// actors. +#[async_trait::async_trait] +pub trait StateSync: Send + Sync + 'static { + async fn send(&self, shard_uid: ShardUId, msg: StateSyncResponse); +} diff --git a/chain/network/src/stats/metrics.rs b/chain/network/src/stats/metrics.rs new file mode 100644 index 000000000..e6b0e5e29 --- /dev/null +++ b/chain/network/src/stats/metrics.rs @@ -0,0 +1,464 @@ +use crate::network_protocol::Encoding; +use crate::network_protocol::{RoutedMessageBody, RoutedMessageV2}; +use crate::tcp; +use crate::types::PeerType; +use unc_async::time; +use unc_o11y::metrics::prometheus; +use unc_o11y::metrics::{ + exponential_buckets, try_create_histogram, try_create_histogram_vec, + try_create_histogram_with_buckets, try_create_int_counter, try_create_int_counter_vec, + try_create_int_gauge, try_create_int_gauge_vec, Histogram, HistogramVec, IntCounter, + IntCounterVec, IntGauge, IntGaugeVec, MetricVec, MetricVecBuilder, +}; +use once_cell::sync::Lazy; + +/// Labels represents a schema of an IntGaugeVec metric. +pub trait Labels: 'static { + /// Array should be [&'static str;N], where N is the number of labels. + type Array: AsRef<[&'static str]>; + /// Names of the gauge vector labels. + const NAMES: Self::Array; + /// Converts self to a list of label values. + /// values().len() should be always equal to names().len(). + fn values(&self) -> Self::Array; +} + +/// Type-safe wrapper of IntGaugeVec. +pub struct Gauge { + inner: IntGaugeVec, + _labels: std::marker::PhantomData, +} + +pub struct GaugePoint(IntGauge); + +impl Gauge { + /// Constructs a new prometheus Gauge with schema `L`. + pub fn new(name: &str, help: &str) -> Result { + Ok(Self { + inner: try_create_int_gauge_vec(name, help, L::NAMES.as_ref())?, + _labels: std::marker::PhantomData, + }) + } + + /// Adds a point represented by `labels` to the gauge. + /// Returns a guard of the point - when the guard is dropped + /// the point is removed from the gauge. + pub fn new_point(&'static self, labels: &L) -> GaugePoint { + let point = self.inner.with_label_values(labels.values().as_ref()); + point.inc(); + GaugePoint(point) + } +} + +impl Drop for GaugePoint { + fn drop(&mut self) { + self.0.dec(); + } +} + +pub struct Connection { + pub type_: PeerType, + pub encoding: Option, +} + +impl Labels for Connection { + type Array = [&'static str; 2]; + const NAMES: Self::Array = ["peer_type", "encoding"]; + fn values(&self) -> Self::Array { + [self.type_.into(), self.encoding.map(|e| e.into()).unwrap_or("unknown")] + } +} + +pub(crate) struct MetricGuard { + metric: M, + drop: Option>, +} + +impl MetricGuard { + pub fn new>( + metric_vec: &'static MetricVec, + labels: Vec, + ) -> Self { + let labels_str: Vec<_> = labels.iter().map(String::as_str).collect(); + Self { + metric: metric_vec.with_label_values(&labels_str[..]), + drop: Some(Box::new(move || { + // This can return an error in tests, when multiple PeerManagerActors + // connect to the same endpoint. + let labels: Vec<_> = labels.iter().map(String::as_str).collect(); + let _ = metric_vec.remove_label_values(&labels[..]); + })), + } + } +} + +impl Drop for MetricGuard { + fn drop(&mut self) { + self.drop.take().map(|f| f()); + } +} + +impl std::ops::Deref for MetricGuard { + type Target = M; + fn deref(&self) -> &Self::Target { + &self.metric + } +} + +pub(crate) type IntGaugeGuard = MetricGuard; + +pub static PEER_CONNECTIONS: Lazy> = + Lazy::new(|| Gauge::new("unc_peer_connections", "Number of connected peers").unwrap()); + +pub(crate) static PEER_CONNECTIONS_TOTAL: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_peer_connections_total", "Number of connected peers").unwrap() +}); +pub(crate) static PEER_DATA_RECEIVED_BYTES: Lazy = Lazy::new(|| { + try_create_int_counter("unc_peer_data_received_bytes", "Total data received from peers") + .unwrap() +}); + +pub(crate) static PEER_MSG_SIZE_BYTES: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_peer_msg_size_bytes", + "Histogram of message sizes in bytes", + &["addr"], + // very coarse buckets, because we keep them for every connection + // separately. + // TODO(gprusak): this might get too expensive with TIER1 connections. + Some(exponential_buckets(100., 10., 6).unwrap()), + ) + .unwrap() +}); + +pub(crate) static PEER_MSG_READ_LATENCY: Lazy = Lazy::new(|| { + try_create_histogram_with_buckets( + "unc_peer_msg_read_latency", + "Time that PeerActor spends on reading a message from a socket", + exponential_buckets(0.001, 1.3, 35).unwrap(), + ) + .unwrap() +}); + +pub(crate) static PEER_DATA_SENT_BYTES: Lazy = Lazy::new(|| { + try_create_int_counter("unc_peer_data_sent_bytes", "Total data sent to peers").unwrap() +}); + +pub(crate) static PEER_DATA_READ_BUFFER_SIZE: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_peer_read_buffer_size", + "Size of the message that this peer is currently sending to us", + &["addr"], + ) + .unwrap() +}); +pub(crate) static PEER_DATA_WRITE_BUFFER_SIZE: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_peer_write_buffer_size", + "Size of the outgoing buffer for this peer", + &["addr"], + ) + .unwrap() +}); +pub(crate) static PEER_MESSAGE_RECEIVED_BY_TYPE_BYTES: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_peer_message_received_by_type_bytes", + "Total data received from peers by message types", + &["type"], + ) + .unwrap() +}); +pub(crate) static PEER_MESSAGE_RECEIVED_BY_TYPE_TOTAL: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_peer_message_received_by_type_total", + "Number of messages received from peers by message types", + &["type"], + ) + .unwrap() +}); +pub(crate) static PEER_MESSAGE_SENT_BY_TYPE_BYTES: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_peer_message_sent_by_type_bytes", + "Total data sent to peers by message types", + &["type"], + ) + .unwrap() +}); +pub(crate) static PEER_MESSAGE_SENT_BY_TYPE_TOTAL: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_peer_message_sent_by_type_total", + "Number of messages sent to peers by message types", + &["type"], + ) + .unwrap() +}); +pub(crate) static SYNC_ACCOUNTS_DATA: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_sync_accounts_data", + "Number of SyncAccountsData messages sent/received", + &["direction", "incremental", "requesting_full_sync"], + ) + .unwrap() +}); +pub(crate) static SYNC_SNAPSHOT_HOSTS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_sync_snapshot_hosts", + "Number of SyncSnapshotHost messages sent/received", + &["direction"], + ) + .unwrap() +}); + +pub(crate) static REQUEST_COUNT_BY_TYPE_TOTAL: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_requests_count_by_type_total", + "Number of network requests we send out, by message types", + &["type"], + ) + .unwrap() +}); + +// Routing table metrics +pub(crate) static ROUTING_TABLE_RECALCULATIONS: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_routing_table_recalculations_total", + "Number of times routing table have been recalculated from scratch", + ) + .unwrap() +}); +pub(crate) static ROUTING_TABLE_RECALCULATION_HISTOGRAM: Lazy = Lazy::new(|| { + try_create_histogram( + "unc_routing_table_recalculation_seconds", + "Time spent recalculating routing table", + ) + .unwrap() +}); +pub(crate) static EDGE_UPDATES: Lazy = + Lazy::new(|| try_create_int_counter("unc_edge_updates", "Unique edge updates").unwrap()); +pub(crate) static EDGE_NONCE: Lazy = Lazy::new(|| { + try_create_int_counter_vec("unc_edge_nonce", "Edge nonce types", &["type"]).unwrap() +}); +pub(crate) static EDGE_ACTIVE: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_edge_active", "Total edges active between peers").unwrap() +}); +pub(crate) static EDGE_TOTAL: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_edge_total", "Total edges between peers (including removed ones).") + .unwrap() +}); + +pub(crate) static EDGE_TOMBSTONE_SENDING_SKIPPED: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_edge_tombstone_sending_skip", + "Number of times that we didn't send tombstones.", + ) + .unwrap() +}); + +pub(crate) static EDGE_TOMBSTONE_RECEIVING_SKIPPED: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_edge_tombstone_receiving_skip", + "Number of times that we pruned tombstones upon receiving.", + ) + .unwrap() +}); + +pub(crate) static PEER_UNRELIABLE: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_peer_unreliable", + "Total peers that are behind and will not be used to route messages", + ) + .unwrap() +}); +pub(crate) static PEER_MANAGER_TRIGGER_TIME: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_peer_manager_trigger_time", + "Time that PeerManagerActor spends on different types of triggers", + &["trigger"], + Some(exponential_buckets(0.0001, 2., 15).unwrap()), + ) + .unwrap() +}); +pub(crate) static PEER_MANAGER_MESSAGES_TIME: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_peer_manager_messages_time", + "Time that PeerManagerActor spends on handling different types of messages", + &["message"], + Some(exponential_buckets(0.0001, 2., 15).unwrap()), + ) + .unwrap() +}); +pub(crate) static ROUTED_MESSAGE_DROPPED: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_routed_message_dropped", + "Number of messages dropped due to TTL=0, by routed message type", + &["type"], + ) + .unwrap() +}); + +pub(crate) static PEER_REACHABLE: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_peer_reachable", + "Total peers such that there is a path potentially through other peers", + ) + .unwrap() +}); +static DROPPED_MESSAGE_COUNT: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_dropped_message_by_type_and_reason_count", + "Total count of messages which were dropped by type of message and \ + reason why the message has been dropped", + &["type", "reason"], + ) + .unwrap() +}); +pub(crate) static PARTIAL_ENCODED_CHUNK_REQUEST_DELAY: Lazy = Lazy::new(|| { + try_create_histogram( + "unc_partial_encoded_chunk_request_delay", + "Delay between when a partial encoded chunk request is sent from ClientActor and when it is received by PeerManagerActor", + ) + .unwrap() +}); + +pub(crate) static BROADCAST_MESSAGES: Lazy = Lazy::new(|| { + try_create_int_counter_vec("unc_broadcast_msg", "Broadcasted messages", &["type"]).unwrap() +}); + +static NETWORK_ROUTED_MSG_LATENCY: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_network_routed_msg_latency", + "Latency of network messages, assuming clocks are perfectly synchronized. 'tier' indicates what is the tier of the connection on which the message arrived (TIER1 is expected to be faster than TIER2) and 'fastest' indicates whether this was the first copy of the message to arrive.", + &["routed","tier","fastest"], + Some(exponential_buckets(0.0001, 1.6, 20).unwrap()), + ) + .unwrap() +}); +static NETWORK_ROUTED_MSG_NUM_HOPS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_network_routed_msg_hops", + "Number of peers the routed message travelled through", + &["routed", "hops"], + ) + .unwrap() +}); + +pub(crate) static CONNECTED_TO_MYSELF: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_connected_to_myself", + "This node connected to itself, this shouldn't happen", + ) + .unwrap() +}); + +pub(crate) static ALREADY_CONNECTED_ACCOUNT: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_already_connected_account", + "A second peer with the same validator key is trying to connect to our node. This means that the validator peer has invalid setup." + ) + .unwrap() +}); + +pub(crate) static ACCOUNT_TO_PEER_LOOKUPS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_account_to_peer_lookups", + "number of lookups of peer_id by account_id (for routed messages)", + // Source is either "AnnounceAccount" or "AccountData". + // We want to deprecate AnnounceAccount, so eventually we want all + // lookups to be done via AccountData. For now AnnounceAccount is + // used as a fallback. + &["source"], + ) + .unwrap() +}); + +pub(crate) static NETWORK_ROUTED_MSG_DISTANCES: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_network_routed_msg_distances", + "compares routing distance by protocol (V1 vs V2)", + // Compares the routing distances for the V1 and V2 routing protocols. + // We are currently running both while validating performance of V2. + // Eventually we want to deprecate V1 and run only V2. + &["cmp"], + ) + .unwrap() +}); + +/// Updated the prometheus metrics about the received routed message `msg`. +/// `tier` indicates the network over which the message was transmitted. +/// `fastest` indicates whether this message is the first copy of `msg` received - +/// important messages are sent multiple times over different routing paths +/// simultaneously to improve the chance that the message will be delivered on time. +pub(crate) fn record_routed_msg_metrics( + clock: &time::Clock, + msg: &RoutedMessageV2, + tier: tcp::Tier, + fastest: bool, +) { + record_routed_msg_latency(clock, msg, tier, fastest); + record_routed_msg_hops(msg); +} + +pub(crate) fn bool_to_str(b: bool) -> &'static str { + match b { + true => "true", + false => "false", + } +} + +// The routed message reached its destination. If the timestamp of creation of this message is +// known, then update the corresponding latency metric histogram. +fn record_routed_msg_latency( + clock: &time::Clock, + msg: &RoutedMessageV2, + tier: tcp::Tier, + fastest: bool, +) { + if let Some(created_at) = msg.created_at { + let now = clock.now_utc(); + let duration = now - created_at; + NETWORK_ROUTED_MSG_LATENCY + .with_label_values(&[msg.body_variant(), tier.as_ref(), bool_to_str(fastest)]) + .observe(duration.as_seconds_f64()); + } +} + +// The routed message reached its destination. If the number of hops is known, then update the +// corresponding metric. +fn record_routed_msg_hops(msg: &RoutedMessageV2) { + const MAX_NUM_HOPS: i32 = 20; + // We assume that the number of hops is small. + // As long as the number of hops is below 10, this metric will not consume too much memory. + if let Some(num_hops) = msg.num_hops { + if num_hops >= 0 { + let num_hops = if num_hops > MAX_NUM_HOPS { MAX_NUM_HOPS } else { num_hops }; + NETWORK_ROUTED_MSG_NUM_HOPS + .with_label_values(&[msg.body_variant(), &num_hops.to_string()]) + .inc(); + } + } +} + +#[derive(Clone, Copy, strum::AsRefStr)] +pub(crate) enum MessageDropped { + NoRouteFound, + UnknownAccount, + InputTooLong, + MaxCapacityExceeded, + TransactionsPerBlockExceeded, + Duplicate, +} + +impl MessageDropped { + pub fn inc(self, msg: &RoutedMessageBody) { + self.inc_msg_type(msg.into()) + } + + pub fn inc_unknown_msg(self) { + self.inc_msg_type("unknown") + } + + fn inc_msg_type(self, msg_type: &str) { + let reason = self.as_ref(); + DROPPED_MESSAGE_COUNT.with_label_values(&[msg_type, reason]).inc(); + } +} diff --git a/chain/network/src/stats/mod.rs b/chain/network/src/stats/mod.rs new file mode 100644 index 000000000..e14488328 --- /dev/null +++ b/chain/network/src/stats/mod.rs @@ -0,0 +1 @@ +pub mod metrics; diff --git a/chain/network/src/store/mod.rs b/chain/network/src/store/mod.rs new file mode 100644 index 000000000..c282faf7c --- /dev/null +++ b/chain/network/src/store/mod.rs @@ -0,0 +1,152 @@ +/// Store module defines atomic DB operations on top of schema module. +/// All transactions should be implemented within this module, +/// in particular schema::StoreUpdate is not exported. +use crate::network_protocol::Edge; +use crate::types::ConnectionInfo; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::types::AccountId; +use std::collections::HashSet; +use std::sync::Arc; +use tracing::debug; + +mod schema; +#[cfg(test)] +pub mod testonly; + +/// Opaque error type representing storage errors. +/// +/// Invariant: any store error is a critical operational operational error +/// which signals about data corruption. It wouldn't be wrong to replace all places /// where the error originates with outright panics. +/// +/// If you have an error condition which needs to be handled somehow, it should be +/// some *other* error type. +#[derive(thiserror::Error, Debug)] +#[error("{0}")] +pub(crate) struct Error(schema::Error); + +/// Store allows for performing synchronous atomic operations on the DB. +/// In particular it doesn't implement Clone and requires &mut self for +/// methods writing to the DB. +#[derive(Clone)] +pub(crate) struct Store(schema::Store); + +/// Everytime a group of peers becomes unreachable at the same time; We store edges belonging to +/// them in components. We remove all of those edges from memory, and save them to database, +/// If any of them become reachable again, we re-add whole component. +/// +/// To store components, we have following column in the DB. +/// DBCol::LastComponentNonce -> stores component_nonce: u64, which is the lowest nonce that +/// hasn't been used yet. If new component gets created it will use +/// this nonce. +/// DBCol::ComponentEdges -> Mapping from `component_nonce` to list of edges +/// DBCol::PeerComponent -> Mapping from `peer_id` to last component nonce if there +/// exists one it belongs to. +impl Store { + /// Inserts (account_id,aa) to the AccountAnnouncements column. + pub fn set_account_announcement( + &mut self, + account_id: &AccountId, + aa: &AnnounceAccount, + ) -> Result<(), Error> { + let mut update = self.0.new_update(); + update.set::(account_id, aa); + self.0.commit(update).map_err(Error) + } + + /// Fetches row with key account_id from the AccountAnnouncements column. + pub fn get_account_announcement( + &self, + account_id: &AccountId, + ) -> Result, Error> { + self.0.get::(account_id).map_err(Error) + } + + /// Atomically stores a graph component consisting of and + /// to the DB. On completion, all peers are considered members of the new component + /// (even if they were members of a different component so far). + /// The name (even though technically correct) is misleading, because the do + /// NOT have to constitute a CONNECTED component. I'm not fixing that because + /// the whole routing table in the current form is scheduled for deprecation. + pub fn push_component( + &mut self, + peers: &HashSet, + edges: &Vec, + ) -> Result<(), Error> { + debug!(target: "network", "push_component: moving {} peers from memory to DB", peers.len()); + let component = + self.0.get::(&()).map_err(Error)?.unwrap_or(0) + 1; + let mut update = self.0.new_update(); + update.set::(&(), &component); + update.set::(&component, &edges); + for peer_id in peers { + update.set::(peer_id, &component); + } + self.0.commit(update).map_err(Error) + } + + /// Reads and deletes from DB the component that is a member of. + /// Returns Ok(vec![]) if peer_id is not a member of any component. + pub fn pop_component(&mut self, peer_id: &PeerId) -> Result, Error> { + // Fetch the component assigned to the peer. + let component = match self.0.get::(peer_id).map_err(Error)? { + Some(c) => c, + None => return Ok(vec![]), + }; + let edges = + self.0.get::(&component).map_err(Error)?.unwrap_or(vec![]); + let mut update = self.0.new_update(); + update.delete::(&component); + let mut peers_checked = HashSet::new(); + for edge in &edges { + let key = edge.key(); + for peer_id in [&key.0, &key.1] { + if !peers_checked.insert(peer_id.clone()) { + // Store doesn't accept 2 mutations modifying the same row in a single + // transaction, even if they are identical. Therefore tracking peers_checked + // is critical for correctness, rather than just an optimization minimizing + // the number of lookups. + continue; + } + match self.0.get::(&peer_id).map_err(Error)? { + Some(c) if c == component => update.delete::(&peer_id), + _ => {} + } + } + } + self.0.commit(update).map_err(Error)?; + Ok(edges) + } +} + +// ConnectionStore storage. +impl Store { + pub fn set_recent_outbound_connections( + &mut self, + recent_outbound_connections: &Vec, + ) -> Result<(), Error> { + let mut update = self.0.new_update(); + update.set::(&(), &recent_outbound_connections); + self.0.commit(update).map_err(Error) + } + + pub fn get_recent_outbound_connections(&self) -> Vec { + self.0 + .get::(&()) + .unwrap_or(Some(vec![])) + .unwrap_or(vec![]) + } +} + +impl From> for Store { + fn from(store: Arc) -> Self { + Self(schema::Store::from(store)) + } +} + +#[cfg(test)] +impl From> for Store { + fn from(store: Arc) -> Self { + let database: Arc = store; + Self(schema::Store::from(database)) + } +} diff --git a/chain/network/src/store/schema/mod.rs b/chain/network/src/store/schema/mod.rs new file mode 100644 index 000000000..dbf0af2af --- /dev/null +++ b/chain/network/src/store/schema/mod.rs @@ -0,0 +1,261 @@ +use crate::types as primitives; +/// Schema module defines a type-safe access to the DB. +/// It is a concise definition of key and value types +/// of the DB columns. For high level access see store.rs. +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_async::time; +use unc_crypto::Signature; +use unc_primitives::account::id::AccountId; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_store::DBCol; +use std::io; +use std::sync::Arc; + +#[cfg(test)] +mod testonly; +#[cfg(test)] +mod tests; + +pub struct AccountIdFormat; +impl Format for AccountIdFormat { + type T = AccountId; + fn encode(a: &AccountId, w: &mut W) -> io::Result<()> { + w.write_all(a.as_bytes()) + } + fn decode(a: &[u8]) -> Result { + std::str::from_utf8(a).map_err(invalid_data)?.parse().map_err(invalid_data) + } +} + +/// A Borsh representation of the primitives::ConnectionInfo. +#[derive(BorshSerialize, BorshDeserialize)] +pub(super) struct ConnectionInfoRepr { + peer_info: primitives::PeerInfo, + /// UNIX timestamps in nanos. + time_established: u64, + time_connected_until: u64, +} + +impl BorshRepr for ConnectionInfoRepr { + type T = primitives::ConnectionInfo; + fn to_repr(s: &primitives::ConnectionInfo) -> Self { + Self { + peer_info: s.peer_info.clone(), + time_established: s.time_established.unix_timestamp_nanos() as u64, + time_connected_until: s.time_connected_until.unix_timestamp_nanos() as u64, + } + } + + fn from_repr(s: Self) -> Result { + Ok(primitives::ConnectionInfo { + peer_info: s.peer_info, + time_established: time::Utc::from_unix_timestamp_nanos(s.time_established as i128) + .map_err(invalid_data)?, + time_connected_until: time::Utc::from_unix_timestamp_nanos( + s.time_connected_until as i128, + ) + .map_err(invalid_data)?, + }) + } +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub(super) struct EdgeRepr { + key: (PeerId, PeerId), + nonce: u64, + signature0: Signature, + signature1: Signature, + removal_info: Option<(bool, Signature)>, +} + +impl BorshRepr for EdgeRepr { + type T = primitives::Edge; + + fn to_repr(e: &Self::T) -> Self { + Self { + key: e.key().clone(), + nonce: e.nonce(), + signature0: e.signature0().clone(), + signature1: e.signature1().clone(), + removal_info: e.removal_info().cloned(), + } + } + fn from_repr(e: Self) -> Result { + Ok(primitives::Edge::new(e.key.0, e.key.1, e.nonce, e.signature0, e.signature1) + .with_removal_info(e.removal_info)) + } +} + +///////////////////////////////////////////// +// Columns + +pub(super) struct AccountAnnouncements; +impl Column for AccountAnnouncements { + const COL: DBCol = DBCol::AccountAnnouncements; + type Key = AccountIdFormat; + type Value = Borsh; +} + +pub(super) struct RecentOutboundConnections; +impl Column for RecentOutboundConnections { + const COL: DBCol = DBCol::RecentOutboundConnections; + type Key = Borsh<()>; + type Value = Vec; +} + +pub(super) struct PeerComponent; +impl Column for PeerComponent { + const COL: DBCol = DBCol::PeerComponent; + type Key = Borsh; + type Value = Borsh; +} + +pub(super) struct ComponentEdges; +impl Column for ComponentEdges { + const COL: DBCol = DBCol::ComponentEdges; + type Key = U64LE; + type Value = Vec; +} + +pub(super) struct LastComponentNonce; +impl Column for LastComponentNonce { + const COL: DBCol = DBCol::LastComponentNonce; + type Key = Borsh<()>; + type Value = Borsh; +} + +//////////////////////////////////////////////////// +// Storage + +pub type Error = io::Error; +fn invalid_data(e: impl std::error::Error + Send + Sync + 'static) -> Error { + Error::new(io::ErrorKind::InvalidData, e) +} + +pub trait Format { + type T; + /// Encode should write encoded to . + /// Errors may come only from calling methods of . + fn encode(a: &Self::T, w: &mut W) -> io::Result<()>; + fn decode(a: &[u8]) -> io::Result; +} + +fn to_vec(a: &F::T) -> Vec { + let mut out = Vec::new(); + F::encode(a, &mut out).unwrap(); + out +} + +/// BorshRepr defines an isomorphism between T and Self, +/// where Self implements serialization to Borsh. +/// Format trait is automatically derived for BorshRepr instances, +/// by first converting T to Self and then serializing to Borsh +/// (Format::decode analogically). +pub trait BorshRepr: BorshSerialize + BorshDeserialize { + type T; + fn to_repr(a: &Self::T) -> Self; + fn from_repr(s: Self) -> Result; +} + +impl Format for R { + type T = R::T; + fn encode(a: &Self::T, w: &mut W) -> io::Result<()> { + R::to_repr(a).serialize(w) + } + fn decode(a: &[u8]) -> io::Result { + R::from_repr(R::try_from_slice(a)?) + } +} + +/// This is a wrapper which doesn't change the borsh encoding. +/// It automatically derives BorshRepr by using the trivial embedding +/// as the isomorphism (therefore the derived Format of Borsh is +/// just borsh serialization of T). +#[derive(BorshSerialize, BorshDeserialize)] +pub struct Borsh(T); + +impl BorshRepr for Borsh { + type T = T; + fn to_repr(a: &T) -> Self { + Self(a.clone()) + } + fn from_repr(a: Self) -> Result { + Ok(a.0) + } +} + +/// Combinator which derives BorshRepr for Vec, given +/// BorshRepr for R. +impl BorshRepr for Vec { + type T = Vec; + fn to_repr(a: &Self::T) -> Vec { + a.iter().map(R::to_repr).collect() + } + fn from_repr(a: Vec) -> Result { + a.into_iter().map(R::from_repr).collect() + } +} + +// Little endian representation for u64. +pub struct U64LE; +impl Format for U64LE { + type T = u64; + fn encode(a: &u64, w: &mut W) -> io::Result<()> { + w.write_all(&a.to_le_bytes()) + } + fn decode(a: &[u8]) -> Result { + a.try_into().map(u64::from_le_bytes).map_err(invalid_data) + } +} + +/// Column is a type-safe specification of the DB column. +/// It defines how to encode/decode keys and values stored in the column. +pub trait Column { + const COL: DBCol; + type Key: Format; + type Value: Format; +} + +/// A type-safe wrapper of the unc_store::Store. +#[derive(Clone)] +pub struct Store(std::sync::Arc); + +/// A type-safe wrapper of the unc_store::StoreUpdate. +#[derive(Default)] +pub struct StoreUpdate(unc_store::db::DBTransaction); + +impl Store { + pub fn new_update(&mut self) -> StoreUpdate { + Default::default() + } + pub fn commit(&mut self, update: StoreUpdate) -> Result<(), Error> { + self.0.write(update.0) + } + + pub fn get( + &self, + k: &::T, + ) -> Result::T>, Error> { + debug_assert!(!C::COL.is_rc()); + let v = self.0.get_raw_bytes(C::COL, to_vec::(k).as_ref())?; + Ok(match v { + Some(v) => Some(C::Value::decode(&v)?), + None => None, + }) + } +} + +impl From> for Store { + fn from(db: Arc) -> Self { + Self(db) + } +} + +impl StoreUpdate { + pub fn set(&mut self, k: &::T, v: &::T) { + self.0.set(C::COL, to_vec::(k), to_vec::(v)) + } + pub fn delete(&mut self, k: &::T) { + self.0.delete(C::COL, to_vec::(k)) + } +} diff --git a/chain/network/src/store/schema/testonly.rs b/chain/network/src/store/schema/testonly.rs new file mode 100644 index 000000000..af48754c1 --- /dev/null +++ b/chain/network/src/store/schema/testonly.rs @@ -0,0 +1,13 @@ +use crate::store::schema::{Column, Error, Format}; + +impl super::Store { + pub fn iter( + &self, + ) -> impl Iterator::T, ::T), Error>> + '_ + { + debug_assert!(!C::COL.is_rc()); + self.0 + .iter_raw_bytes(C::COL) + .map(|item| item.and_then(|(k, v)| Ok((C::Key::decode(&k)?, C::Value::decode(&v)?)))) + } +} diff --git a/chain/network/src/store/schema/tests.rs b/chain/network/src/store/schema/tests.rs new file mode 100644 index 000000000..40c319fe7 --- /dev/null +++ b/chain/network/src/store/schema/tests.rs @@ -0,0 +1,13 @@ +use super::*; +use crate::network_protocol::testonly as data; +use crate::testonly::make_rng; + +#[test] +fn borsh_wrapper_is_transparent() { + let mut rng = make_rng(423423); + let rng = &mut rng; + let s1 = data::make_secret_key(rng); + let s2 = data::make_secret_key(rng); + let e = data::make_edge(&s1, &s2, 1); + assert_eq!(borsh::to_vec(&Borsh(e.clone())).unwrap(), borsh::to_vec(&e).unwrap()); +} diff --git a/chain/network/src/store/testonly.rs b/chain/network/src/store/testonly.rs new file mode 100644 index 000000000..4ab7b9870 --- /dev/null +++ b/chain/network/src/store/testonly.rs @@ -0,0 +1,52 @@ +use super::*; +use std::collections::HashMap; + +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)] +pub struct Component { + pub peers: Vec, + pub edges: Vec, +} + +impl Component { + pub fn normal(mut self) -> Self { + self.peers.sort(); + self.edges.sort_by(|a, b| a.key().cmp(b.key())); + self + } +} + +impl Store { + /// Reads all the components from the database. + /// Panics if any of the invariants has been violated. + pub fn list_components(&self) -> Vec { + let edges: HashMap<_, _> = + self.0.iter::().map(|x| x.unwrap()).collect(); + let peers: HashMap<_, _> = + self.0.iter::().map(|x| x.unwrap()).collect(); + let lcn: HashMap<(), _> = + self.0.iter::().map(|x| x.unwrap()).collect(); + // all component nonces should be <= LastComponentNonce + let lcn = lcn.get(&()).unwrap_or(&0); + for (c, _) in &edges { + assert!(c <= lcn); + } + for (_, c) in &peers { + assert!(c <= lcn); + } + // Each edge has to be incident to at least one peer in the same component. + for (c, es) in &edges { + for e in es { + let key = e.key(); + assert!(peers.get(&key.0) == Some(c) || peers.get(&key.1) == Some(c)); + } + } + let mut cs = HashMap::::new(); + for (c, es) in edges { + cs.entry(c).or_default().edges = es; + } + for (p, c) in peers { + cs.entry(c).or_default().peers.push(p); + } + cs.into_iter().map(|(_, v)| v).collect() + } +} diff --git a/chain/network/src/stun/mod.rs b/chain/network/src/stun/mod.rs new file mode 100644 index 000000000..4a3c0e80d --- /dev/null +++ b/chain/network/src/stun/mod.rs @@ -0,0 +1,58 @@ +use unc_async::time; +use std::sync::Arc; +use stun::message::Getter as _; + +#[cfg(test)] +mod tests; + +#[cfg(test)] +pub(crate) mod testonly; + +/// Address of the format ":" of STUN servers. +// TODO(gprusak): turn into a proper struct implementing Display and FromStr. +pub type ServerAddr = String; + +pub(crate) type Error = stun::Error; + +const QUERY_TIMEOUT: time::Duration = time::Duration::seconds(5); + +/// Sends a STUN BINDING request to `addr`. +/// Returns the result of the query: the IP of this machine as perceived by the STUN server. +/// It should be used to determine the public IP of this machine. +pub(crate) async fn query( + clock: &time::Clock, + addr: &ServerAddr, +) -> Result { + let socket = tokio::net::UdpSocket::bind("[::]:0").await?; + socket.connect(addr).await?; + let mut client = stun::client::ClientBuilder::new().with_conn(Arc::new(socket)).build()?; + let mut msg = stun::message::Message::new(); + msg.new_transaction_id()?; + msg.set_type(stun::message::BINDING_REQUEST); + msg.build(&[])?; + let (send, mut recv) = tokio::sync::mpsc::unbounded_channel(); + client.send(&msg, Some(Arc::new(send))).await?; + // Note that both clock.sleep() and recv.recv() are cancellable, + // so it is safe to use them in tokio::select!. + let ip = tokio::select! { + _ = clock.sleep(QUERY_TIMEOUT) => { + return Err(Error::ErrTransactionTimeOut); + } + e = recv.recv() => match e { + None => { + // stun crate doesn't document whether and when it can happen. + // We treat it as a failed STUN transaction and log an error because + // it is not an expected behavior. + tracing::error!("STUN client has closed the output channel before returning a response - this is unexpected"); + return Err(Error::ErrTransactionStopped); + } + Some(e) => { + let mut addr = stun::xoraddr::XorMappedAddress::default(); + addr.get_from(&e.event_body?)?; + addr.ip + } + } + }; + client.close().await?; + Ok(ip) +} diff --git a/chain/network/src/stun/testonly.rs b/chain/network/src/stun/testonly.rs new file mode 100644 index 000000000..aaebe43e3 --- /dev/null +++ b/chain/network/src/stun/testonly.rs @@ -0,0 +1,61 @@ +use std::sync::Arc; + +struct TestAuthHandler; + +impl turn::auth::AuthHandler for TestAuthHandler { + fn auth_handle( + &self, + _username: &str, + _realm: &str, + _src_addr: std::net::SocketAddr, + ) -> Result, turn::Error> { + unimplemented!(); + } +} + +/// STUN server. Use new() to spawn a new server, use close() to close it. +/// Remember to call close() explicitly, otherwise the server will keep running +/// in the background. +pub(crate) struct Server { + inner: turn::server::Server, + addr: std::net::SocketAddr, +} + +impl Server { + /// Spawns a new STUN server on localhost interface. + /// In fact a TURN server is spawned, which implements a superset + /// of STUN functionality. + pub async fn new() -> Self { + let server_conn = Arc::new(tokio::net::UdpSocket::bind("[::1]:0").await.unwrap()); + let server_addr = server_conn.local_addr().unwrap(); + + Self { + addr: server_addr, + inner: turn::server::Server::new(turn::server::config::ServerConfig { + conn_configs: vec![turn::server::config::ConnConfig { + conn: server_conn, + relay_addr_generator: Box::new( + turn::relay::relay_none::RelayAddressGeneratorNone { + address: "[::]".to_owned(), + net: Arc::new(webrtc_util::vnet::net::Net::new(None)), + }, + ), + }], + realm: String::default(), + auth_handler: Arc::new(TestAuthHandler), + channel_bind_timeout: std::time::Duration::from_secs(0), + }) + .await + .unwrap(), + } + } + + pub fn addr(&self) -> super::ServerAddr { + self.addr.to_string() + } + + /// Closes the STUN server. close() is async so it cannot be implemented as Drop. + pub async fn close(self) { + self.inner.close().await.unwrap(); + } +} diff --git a/chain/network/src/stun/tests.rs b/chain/network/src/stun/tests.rs new file mode 100644 index 000000000..15a35ab41 --- /dev/null +++ b/chain/network/src/stun/tests.rs @@ -0,0 +1,13 @@ +use crate::stun; +use unc_async::time; +use unc_o11y::testonly::init_test_logger; + +#[tokio::test] +async fn test_query() { + init_test_logger(); + let clock = time::FakeClock::default(); + let server = stun::testonly::Server::new().await; + let ip = stun::query(&clock.clock(), &server.addr()).await.unwrap(); + assert_eq!(std::net::Ipv6Addr::LOCALHOST, ip); + server.close().await; +} diff --git a/chain/network/src/tcp.rs b/chain/network/src/tcp.rs new file mode 100644 index 000000000..590ef84c4 --- /dev/null +++ b/chain/network/src/tcp.rs @@ -0,0 +1,219 @@ +use crate::network_protocol::PeerInfo; +use anyhow::{anyhow, Context as _}; +use unc_primitives::network::PeerId; +use once_cell::sync::Lazy; +use std::collections::HashMap; +use std::fmt; +use std::sync::Mutex; + +const LISTENER_BACKLOG: u32 = 128; + +/// TEST-ONLY: guards ensuring that OS considers the given TCP listener port to be in use until +/// this OS process is terminated. +static RESERVED_LISTENER_ADDRS: Lazy>> = + Lazy::new(|| Mutex::new(HashMap::new())); + +/// TCP connections established by a node belong to different logical networks (aka tiers), +/// which serve different purpose. +// TODO(gprusak): add a link to the design on github docs (but first write those docs). +#[derive(Clone, Copy, Debug, PartialEq, Eq, strum::AsRefStr)] +pub enum Tier { + /// Tier1 connections are established between the BFT consensus participants (or their proxies) + /// and are reserved exclusively for exchanging BFT consensus messages. + T1, + /// Tier2 connections form a P2P gossip network, which is used for everything, except the BFT + /// consensus messages. Also, Tier1 peer discovery actually happens on Tier2 network, i.e. + /// Tier2 network is necessary to bootstrap Tier1 connections. + T2, +} + +#[derive(Clone, Debug)] +pub(crate) enum StreamType { + Inbound, + Outbound { peer_id: PeerId, tier: Tier }, +} + +#[derive(Debug)] +pub struct Stream { + pub(crate) stream: tokio::net::TcpStream, + pub(crate) type_: StreamType, + /// cached stream.local_addr() + pub(crate) local_addr: std::net::SocketAddr, + /// cached peer_addr.local_addr() + pub(crate) peer_addr: std::net::SocketAddr, +} + +/// TEST-ONLY. Used to identify events relevant to a specific TCP connection in unit tests. +/// Every outbound TCP connection has a unique TCP port (while inbound TCP connections +/// have the same port as the TCP listen socket). +/// We are assuming here that the unit test is executed on a single machine on the loopback +/// network interface, so that both inbound and outbound IP is always 127.0.0.1. +/// To create a reliable StreamId for a distributed, we would have to transmit it over the connection itself, +/// which is doable, but not yet needed in our testing framework. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct StreamId { + inbound: std::net::SocketAddr, + outbound: std::net::SocketAddr, +} + +#[cfg(test)] +pub(crate) struct Socket(tokio::net::TcpSocket); + +#[cfg(test)] +impl Socket { + pub fn bind() -> Self { + let socket = tokio::net::TcpSocket::new_v6().unwrap(); + socket.bind("[::1]:0".parse().unwrap()).unwrap(); + Self(socket) + } + + pub async fn connect(self, peer_info: &PeerInfo, tier: Tier) -> Stream { + // TODO(gprusak): this could replace Stream::connect, + // however this means that we will have to replicate everything + // that tokio::net::TcpStream sets on the socket. + // As long as Socket::connect is test-only we may ignore that. + let stream = self.0.connect(peer_info.addr.unwrap()).await.unwrap(); + Stream::new(stream, StreamType::Outbound { peer_id: peer_info.id.clone(), tier }).unwrap() + } +} + +impl Stream { + fn new(stream: tokio::net::TcpStream, type_: StreamType) -> std::io::Result { + Ok(Self { peer_addr: stream.peer_addr()?, local_addr: stream.local_addr()?, stream, type_ }) + } + + pub async fn connect(peer_info: &PeerInfo, tier: Tier) -> anyhow::Result { + let addr = + peer_info.addr.ok_or(anyhow!("Trying to connect to peer with no public address"))?; + // The `connect` may take several minutes. This happens when the + // `SYN` packet for establishing a TCP connection gets silently + // dropped, in which case the default TCP timeout is applied. That's + // too long for us, so we shorten it to one second. + // + // Why exactly a second? It was hard-coded in a library we used + // before, so we keep it to preserve behavior. Removing the timeout + // completely was observed to break stuff for real on the testnet. + let stream = tokio::time::timeout( + std::time::Duration::from_secs(1), + tokio::net::TcpStream::connect(addr), + ) + .await? + .context("TcpStream::connect()")?; + Ok(Stream::new(stream, StreamType::Outbound { peer_id: peer_info.id.clone(), tier })?) + } + + /// Establishes a loopback TCP connection to localhost with random ports. + /// Returns a pair of streams: (outbound,inbound). + #[cfg(test)] + pub async fn loopback(peer_id: PeerId, tier: Tier) -> (Stream, Stream) { + let listener_addr = ListenerAddr::reserve_for_test(); + let peer_info = PeerInfo { id: peer_id, addr: Some(*listener_addr), account_id: None }; + let mut listener = listener_addr.listener().unwrap(); + let (outbound, inbound) = + tokio::join!(Stream::connect(&peer_info, tier), listener.accept()); + (outbound.unwrap(), inbound.unwrap()) + } + + // TEST-ONLY used in reporting test events. + pub(crate) fn id(&self) -> StreamId { + match self.type_ { + StreamType::Inbound => StreamId { inbound: self.local_addr, outbound: self.peer_addr }, + StreamType::Outbound { .. } => { + StreamId { inbound: self.peer_addr, outbound: self.local_addr } + } + } + } +} + +/// ListenerAddr is isomorphic to std::net::SocketAddr, but it should be used +/// solely for opening a TCP listener socket on it. +/// +/// In tests it additionally allows to "reserve" a random unused TCP port: +/// * it allows to avoid race conditions in tests which require a dedicated TCP port to spawn a +/// node on (and potentially restart it every now and then). +/// * it is implemented by usign SO_REUSEPORT socket option (do not confuse with SO_REUSEADDR), +/// which allows multiple sockets to share a port. reserve_for_test() creates a socket and binds +/// it to a random unused local port (without starting a TCP listener). +/// This socket won't be used for anything but telling the OS that the given TCP port is in use. +/// However thanks to SO_REUSEPORT we can create another socket bind it to the same port +/// and make it a listener. +/// * The reserved port stays reserved until the process terminates - hence during a process +/// lifetime reserve_for_test() should be called a small amount of times (~1000 should be fine, +/// there are only 2^16 ports on a network interface). TODO(gprusak): we may want to track the +/// lifecycle of ListenerAddr (for example via reference counter), so that we can reuse the port +/// after all the references are dropped. +/// * The drawback of this solution that it is hard to detect a situation in which multiple +/// listener sockets in test are bound to the same port. TODO(gprusak): we can prevent creating +/// multiple listeners for a single port within the same process by adding a mutex to the port +/// guard. +#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Eq)] +pub struct ListenerAddr(std::net::SocketAddr); + +impl fmt::Debug for ListenerAddr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::Display for ListenerAddr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl std::ops::Deref for ListenerAddr { + type Target = std::net::SocketAddr; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl ListenerAddr { + pub fn new(addr: std::net::SocketAddr) -> Self { + assert!( + addr.port() != 0, + "using an anyport (i.e. 0) for the tcp::ListenerAddr is allowed only \ + in tests and only via reserve_for_test() method" + ); + Self(addr) + } + + /// TEST-ONLY: reserves a random port on localhost for a TCP listener. + pub fn reserve_for_test() -> Self { + let guard = tokio::net::TcpSocket::new_v6().unwrap(); + guard.set_reuseaddr(true).unwrap(); + guard.set_reuseport(true).unwrap(); + guard.bind("[::1]:0".parse().unwrap()).unwrap(); + let addr = guard.local_addr().unwrap(); + RESERVED_LISTENER_ADDRS.lock().unwrap().insert(addr, guard); + Self(addr) + } + + /// Constructs a std::net::TcpListener, for usage outside of unc_network. + pub fn std_listener(&self) -> std::io::Result { + self.listener()?.0.into_std() + } + + /// Constructs a Listener out of ListenerAddr. + pub(crate) fn listener(&self) -> std::io::Result { + let socket = match &self.0 { + std::net::SocketAddr::V4(_) => tokio::net::TcpSocket::new_v4()?, + std::net::SocketAddr::V6(_) => tokio::net::TcpSocket::new_v6()?, + }; + if RESERVED_LISTENER_ADDRS.lock().unwrap().contains_key(&self.0) { + socket.set_reuseport(true)?; + } + socket.set_reuseaddr(true)?; + socket.bind(self.0)?; + Ok(Listener(socket.listen(LISTENER_BACKLOG)?)) + } +} + +pub(crate) struct Listener(tokio::net::TcpListener); + +impl Listener { + pub async fn accept(&mut self) -> std::io::Result { + let (stream, _) = self.0.accept().await?; + Stream::new(stream, StreamType::Inbound) + } +} diff --git a/chain/network/src/test_loop.rs b/chain/network/src/test_loop.rs new file mode 100644 index 000000000..9210347b1 --- /dev/null +++ b/chain/network/src/test_loop.rs @@ -0,0 +1,16 @@ +use unc_primitives::types::AccountId; + +/// A multi-instance test using the TestLoop framework can support routing +/// lookup for network messages, as long as the Data type contains AccountId. +/// This trait is just a helper for looking up the index. +pub trait SupportsRoutingLookup { + fn index_for_account(&self, account: &AccountId) -> usize; +} + +impl> SupportsRoutingLookup for Vec { + fn index_for_account(&self, account: &AccountId) -> usize { + self.iter() + .position(|data| data.as_ref() == account) + .unwrap_or_else(|| panic!("Account not found: {}", account)) + } +} diff --git a/chain/network/src/test_utils.rs b/chain/network/src/test_utils.rs new file mode 100644 index 000000000..e3951d58a --- /dev/null +++ b/chain/network/src/test_utils.rs @@ -0,0 +1,301 @@ +use crate::network_protocol::PeerInfo; +use crate::types::{ + NetworkInfo, NetworkResponses, PeerManagerMessageRequest, PeerManagerMessageResponse, + SetChainInfo, +}; +use crate::PeerManagerActor; +use actix::{Actor, ActorContext, Context, Handler}; +use futures::future::BoxFuture; +use futures::{future, Future, FutureExt}; +use unc_async::messaging::{CanSend, CanSendAsync}; +use unc_crypto::{KeyType, SecretKey}; +use unc_o11y::{handler_debug_span, OpenTelemetrySpanExt, WithSpanContext}; +use unc_primitives::hash::hash; +use unc_primitives::network::PeerId; +use unc_primitives::types::EpochId; +use unc_primitives::utils::index_to_bytes; +use rand::{thread_rng, RngCore}; +use std::collections::{HashMap, VecDeque}; +use std::ops::ControlFlow; +use std::sync::{Arc, RwLock}; +use tokio::sync::Notify; +use tracing::debug; + +// `peer_id_from_seed` generate `PeerId` from seed for unit tests +pub fn peer_id_from_seed(seed: &str) -> PeerId { + PeerId::new(SecretKey::from_seed(KeyType::ED25519, seed).public_key()) +} + +// `convert_boot_nodes` generate list of `PeerInfos` for unit tests +pub fn convert_boot_nodes(boot_nodes: Vec<(&str, std::net::SocketAddr)>) -> Vec { + let mut result = vec![]; + for (peer_seed, addr) in boot_nodes { + let id = peer_id_from_seed(peer_seed); + result.push(PeerInfo::new(id, addr)); + } + result +} + +/// Timeouts by stopping system without any condition and raises panic. +/// Useful in tests to prevent them from running forever. +#[allow(unreachable_code)] +pub fn wait_or_panic(max_wait_ms: u64) { + actix::spawn(tokio::time::sleep(tokio::time::Duration::from_millis(max_wait_ms)).then(|_| { + panic!("Timeout exceeded."); + future::ready(()) + })); +} + +/// Waits until condition or timeouts with panic. +/// Use in tests to check for a condition and stop or fail otherwise. +/// +/// Prefer using [`wait_or_timeout`], which is not specific to actix. +/// +/// # Example +/// +/// ```rust,ignore +/// use actix::{System, Actor}; +/// use unc_network::test_utils::WaitOrTimeoutActor; +/// use std::time::{Instant, Duration}; +/// +/// unc_actix_test_utils::run_actix(async { +/// let start = Instant::now(); +/// WaitOrTimeoutActor::new( +/// Box::new(move |ctx| { +/// if start.elapsed() > Duration::from_millis(10) { +/// System::current().stop() +/// } +/// }), +/// 1000, +/// 60000, +/// ).start(); +/// }); +/// ``` +pub struct WaitOrTimeoutActor { + f: Box)>, + check_interval_ms: u64, + max_wait_ms: u64, + ms_slept: u64, +} + +impl WaitOrTimeoutActor { + pub fn new( + f: Box)>, + check_interval_ms: u64, + max_wait_ms: u64, + ) -> Self { + WaitOrTimeoutActor { f, check_interval_ms, max_wait_ms, ms_slept: 0 } + } + + fn wait_or_timeout(&mut self, ctx: &mut Context) { + (self.f)(ctx); + + unc_performance_metrics::actix::run_later( + ctx, + tokio::time::Duration::from_millis(self.check_interval_ms), + move |act, ctx| { + act.ms_slept += act.check_interval_ms; + if act.ms_slept > act.max_wait_ms { + println!("BBBB Slept {}; max_wait_ms {}", act.ms_slept, act.max_wait_ms); + panic!("Timed out waiting for the condition"); + } + act.wait_or_timeout(ctx); + }, + ); + } +} + +impl Actor for WaitOrTimeoutActor { + type Context = Context; + + fn started(&mut self, ctx: &mut Context) { + self.wait_or_timeout(ctx); + } +} + +/// Blocks until `cond` returns `ControlFlow::Break`, checking it every +/// `check_interval_ms`. +/// +/// If condition wasn't fulfilled within `max_wait_ms`, returns an error. +pub async fn wait_or_timeout( + check_interval_ms: u64, + max_wait_ms: u64, + mut cond: C, +) -> Result +where + C: FnMut() -> F, + F: Future>, +{ + assert!( + check_interval_ms < max_wait_ms, + "interval shorter than wait time, did you swap the argument order?" + ); + let mut interval = tokio::time::interval(tokio::time::Duration::from_millis(check_interval_ms)); + tokio::time::timeout(tokio::time::Duration::from_millis(max_wait_ms), async { + loop { + interval.tick().await; + if let ControlFlow::Break(res) = cond().await { + break res; + } + } + }) + .await +} + +// Gets random PeerId +pub fn random_peer_id() -> PeerId { + let sk = SecretKey::from_random(KeyType::ED25519); + PeerId::new(sk.public_key()) +} + +// Gets random EpochId +pub fn random_epoch_id() -> EpochId { + EpochId(hash(index_to_bytes(thread_rng().next_u64()).as_ref())) +} + +// Compare whenever routing table match. +pub fn expected_routing_tables( + got: &HashMap>, + want: &[(PeerId, Vec)], +) -> bool { + if got.len() != want.len() { + return false; + } + + for (target, want_peers) in want { + let got_peers = match got.get(target) { + Some(ps) => ps, + None => { + return false; + } + }; + if got_peers.len() != want_peers.len() { + return false; + } + for peer in want_peers { + if !got_peers.contains(peer) { + return false; + } + } + } + true +} + +/// `GetInfo` gets `NetworkInfo` from `PeerManager`. +#[derive(actix::Message, Debug)] +#[rtype(result = "NetworkInfo")] +pub struct GetInfo {} + +impl Handler> for PeerManagerActor { + type Result = crate::types::NetworkInfo; + + fn handle(&mut self, msg: WithSpanContext, _ctx: &mut Context) -> Self::Result { + let (_span, _msg) = handler_debug_span!(target: "network", msg); + self.get_network_info() + } +} + +// `StopSignal is used to stop PeerManagerActor for unit tests +#[derive(actix::Message, Default, Debug)] +#[rtype(result = "()")] +pub struct StopSignal { + pub should_panic: bool, +} + +impl StopSignal { + pub fn should_panic() -> Self { + Self { should_panic: true } + } +} + +impl Handler> for PeerManagerActor { + type Result = (); + + fn handle( + &mut self, + msg: WithSpanContext, + ctx: &mut Self::Context, + ) -> Self::Result { + let (_span, msg) = handler_debug_span!(target: "network", msg); + debug!(target: "network", "Receive Stop Signal."); + + if msg.should_panic { + panic!("Node crashed"); + } else { + ctx.stop(); + } + } +} + +// Mocked `PeerManager` adapter, has a queue of `PeerManagerMessageRequest` messages. +#[derive(Default)] +pub struct MockPeerManagerAdapter { + pub requests: Arc>>, + pub notify: Notify, +} + +impl CanSendAsync> + for MockPeerManagerAdapter +{ + fn send_async( + &self, + message: PeerManagerMessageRequest, + ) -> BoxFuture<'static, Result> { + self.requests.write().unwrap().push_back(message); + self.notify.notify_one(); + async { Ok(PeerManagerMessageResponse::NetworkResponses(NetworkResponses::NoResponse)) } + .boxed() + } +} + +impl CanSend for MockPeerManagerAdapter { + fn send(&self, msg: PeerManagerMessageRequest) { + self.requests.write().unwrap().push_back(msg); + self.notify.notify_one(); + } +} + +impl CanSend for MockPeerManagerAdapter { + fn send(&self, _msg: SetChainInfo) {} +} + +impl MockPeerManagerAdapter { + pub fn pop(&self) -> Option { + self.requests.write().unwrap().pop_front() + } + pub fn pop_most_recent(&self) -> Option { + self.requests.write().unwrap().pop_back() + } + pub fn put_back_most_recent(&self, request: PeerManagerMessageRequest) { + self.requests.write().unwrap().push_back(request); + } + /// Calls the handler for each message, but removing only those for which the handler returns + /// None (or else the returned message gets requeued; the returned message should be the same + /// as the one given). Returns true if any message was removed. + pub fn handle_filtered( + &self, + mut f: impl FnMut(PeerManagerMessageRequest) -> Option, + ) -> bool { + // We get the count first and then pop one by one, that way we avoid + // grabbing the lock for the whole duration, which might lead to a + // deadlock if the processing of the request results in more network + // messages. + let num_requests = self.requests.read().unwrap().len(); + let mut handled = false; + for _ in 0..num_requests { + let request = self.requests.write().unwrap().pop_front().unwrap(); + if let Some(request) = f(request) { + self.requests.write().unwrap().push_back(request); + } else { + handled = true; + } + } + handled + } +} + +#[derive(actix::Message, Clone, Debug)] +#[rtype(result = "()")] +pub struct SetAdvOptions { + pub set_max_peers: Option, +} diff --git a/chain/network/src/testonly/fake_client.rs b/chain/network/src/testonly/fake_client.rs new file mode 100644 index 000000000..99a016c33 --- /dev/null +++ b/chain/network/src/testonly/fake_client.rs @@ -0,0 +1,152 @@ +use crate::client; +use crate::network_protocol::StateResponseInfo; +use crate::shards_manager::ShardsManagerRequestFromNetwork; +use crate::sink::Sink; +use crate::types::{NetworkInfo, ReasonForBan, StateResponseInfoV2}; +use unc_async::messaging; +use unc_primitives::block::{Approval, Block, BlockHeader}; +use unc_primitives::challenge::Challenge; +use unc_primitives::chunk_validation::{ChunkEndorsement, ChunkStateWitness}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::sharding::{ChunkHash, PartialEncodedChunkPart}; +use unc_primitives::state_sync::{ShardStateSyncResponse, ShardStateSyncResponseV2}; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{AccountId, EpochId, ShardId}; +use unc_primitives::views::FinalExecutionOutcomeView; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[allow(clippy::large_enum_variant)] +pub enum Event { + AnnounceAccount(Vec<(AnnounceAccount, Option)>), + Block(Block), + BlockApproval(Approval, PeerId), + BlockHeaders(Vec), + BlockHeadersRequest(Vec), + BlockRequest(CryptoHash), + Challenge(Challenge), + Chunk(Vec), + ChunkEndorsement(ChunkEndorsement), + ChunkRequest(ChunkHash), + ChunkStateWitness(ChunkStateWitness), + Transaction(SignedTransaction), +} + +pub(crate) struct Fake { + pub event_sink: Sink, +} + +#[async_trait::async_trait] +impl client::Client for Fake { + async fn tx_status_request( + &self, + _account_id: AccountId, + _tx_hash: CryptoHash, + ) -> Option> { + unimplemented!(); + } + + async fn tx_status_response(&self, _tx_result: FinalExecutionOutcomeView) {} + + async fn state_request_header( + &self, + _shard_id: ShardId, + _sync_hash: CryptoHash, + ) -> Result, ReasonForBan> { + unimplemented!(); + } + + async fn state_request_part( + &self, + shard_id: ShardId, + sync_hash: CryptoHash, + part_id: u64, + ) -> Result, ReasonForBan> { + let part = Some((part_id, vec![])); + let state_response = + ShardStateSyncResponse::V2(ShardStateSyncResponseV2 { header: None, part }); + let result = Some(StateResponseInfo::V2(StateResponseInfoV2 { + shard_id, + sync_hash, + state_response, + })); + Ok(result) + } + + async fn state_response(&self, _info: StateResponseInfo) { + unimplemented!(); + } + + async fn block_approval(&self, approval: Approval, peer_id: PeerId) { + self.event_sink.push(Event::BlockApproval(approval, peer_id)); + } + + async fn transaction(&self, transaction: SignedTransaction, _is_forwarded: bool) { + self.event_sink.push(Event::Transaction(transaction)); + } + + async fn block_request(&self, hash: CryptoHash) -> Option> { + self.event_sink.push(Event::BlockRequest(hash)); + None + } + + async fn block_headers_request(&self, hashes: Vec) -> Option> { + self.event_sink.push(Event::BlockHeadersRequest(hashes)); + None + } + + async fn block(&self, block: Block, _peer_id: PeerId, _was_requested: bool) { + self.event_sink.push(Event::Block(block)); + } + + async fn block_headers( + &self, + headers: Vec, + _peer_id: PeerId, + ) -> Result<(), ReasonForBan> { + self.event_sink.push(Event::BlockHeaders(headers)); + Ok(()) + } + + async fn challenge(&self, challenge: Challenge) { + self.event_sink.push(Event::Challenge(challenge)); + } + + async fn network_info(&self, _info: NetworkInfo) {} + + async fn announce_account( + &self, + accounts: Vec<(AnnounceAccount, Option)>, + ) -> Result, ReasonForBan> { + self.event_sink.push(Event::AnnounceAccount(accounts.clone())); + Ok(accounts.into_iter().map(|a| a.0).collect()) + } + + async fn chunk_state_witness(&self, witness: ChunkStateWitness) { + self.event_sink.push(Event::ChunkStateWitness(witness)); + } + + async fn chunk_endorsement(&self, endorsement: ChunkEndorsement) { + self.event_sink.push(Event::ChunkEndorsement(endorsement)); + } +} + +impl messaging::CanSend for Fake { + fn send(&self, message: ShardsManagerRequestFromNetwork) { + match message { + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkRequest { + partial_encoded_chunk_request, + .. + } => { + self.event_sink.push(Event::ChunkRequest(partial_encoded_chunk_request.chunk_hash)); + } + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkResponse { + partial_encoded_chunk_response, + .. + } => { + self.event_sink.push(Event::Chunk(partial_encoded_chunk_response.parts)); + } + _ => {} + } + } +} diff --git a/chain/network/src/testonly/mod.rs b/chain/network/src/testonly/mod.rs new file mode 100644 index 000000000..16594423f --- /dev/null +++ b/chain/network/src/testonly/mod.rs @@ -0,0 +1,63 @@ +use unc_o11y::testonly::init_test_logger; +use pretty_assertions::Comparison; +use std::cmp::Eq; +use std::collections::HashSet; +use std::fmt::Debug; +use std::hash::Hash; + +pub use super::actix; +pub mod fake_client; +pub mod stream; + +pub type Rng = rand_xorshift::XorShiftRng; + +pub fn make_rng(seed: u64) -> Rng { + rand::SeedableRng::seed_from_u64(seed) +} + +pub trait AsSet<'a, T> { + fn as_set(&'a self) -> HashSet<&'a T>; +} + +impl<'a, T: Hash + Eq> AsSet<'a, T> for Vec { + fn as_set(&'a self) -> HashSet<&'a T> { + self.iter().collect() + } +} + +impl<'a, T: Hash + Eq> AsSet<'a, T> for [&'a T] { + fn as_set(&'a self) -> HashSet<&'a T> { + self.iter().cloned().collect() + } +} + +#[track_caller] +pub fn assert_is_superset<'a, T: Debug + Hash + Eq>(sup: &HashSet<&'a T>, sub: &HashSet<&'a T>) { + if !sup.is_superset(sub) { + panic!("expected a super set, got diff:\n{}", Comparison::new(sup, sub)); + } +} + +/// Initializes the test logger and then, iff the current process is executed under +/// nextest in process-per-test mode, changes the behavior of the process to [panic=abort]. +/// In particular it doesn't enable [panic=abort] when run via "cargo test". +/// Note that (unfortunately) some tests may expect a panic, so we cannot apply blindly +/// [panic=abort] in compilation time to all tests. +// TODO: investigate whether "-Zpanic-abort-tests" could replace this function once the flag +// becomes stable: https://github.com/rust-lang/rust/issues/67650, so we don't use it. +pub(crate) fn abort_on_panic() { + init_test_logger(); + // I don't know a way to set panic=abort for nextest builds in compilation time, so we set it + // in runtime. https://nexte.st/book/env-vars.html#environment-variables-nextest-sets + let Ok(nextest) = std::env::var("NEXTEST") else { return }; + let Ok(nextest_execution_mode) = std::env::var("NEXTEST_EXECUTION_MODE") else { return }; + if nextest != "1" || nextest_execution_mode != "process-per-test" { + return; + } + tracing::info!(target:"test", "[panic=abort] enabled"); + let orig_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |panic_info| { + orig_hook(panic_info); + std::process::abort(); + })); +} diff --git a/chain/network/src/testonly/stream.rs b/chain/network/src/testonly/stream.rs new file mode 100644 index 000000000..0ba206eec --- /dev/null +++ b/chain/network/src/testonly/stream.rs @@ -0,0 +1,68 @@ +//! Stream wrapper, which allows for custom interactions with the network protocol. +//! We might want to turn it into a fuzz testing framework for the network protocol. +use bytes::BytesMut; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; + +use crate::network_protocol::{Encoding, PeerMessage}; +use crate::tcp; + +pub struct Stream { + stream: tcp::Stream, + force_encoding: Option, + protocol_buffers_supported: bool, +} + +impl Stream { + pub fn new(force_encoding: Option, stream: tcp::Stream) -> Self { + Self { stream, force_encoding, protocol_buffers_supported: false } + } + + fn encoding(&self) -> Option { + if self.force_encoding.is_some() { + return self.force_encoding; + } + if self.protocol_buffers_supported { + return Some(Encoding::Proto); + } + return None; + } + + pub async fn read(&mut self) -> Result { + 'read: loop { + let n = self.stream.stream.read_u32_le().await? as usize; + let mut buf = BytesMut::new(); + buf.resize(n, 0); + self.stream.stream.read_exact(&mut buf[..]).await?; + for enc in [Encoding::Proto, Encoding::Borsh] { + if let Ok(msg) = PeerMessage::deserialize(enc, &buf[..]) { + // If deserialize() succeeded but we expected different encoding, ignore the + // message. + if self.encoding().unwrap_or(enc) != enc { + println!("unexpected encoding, ignoring message"); + continue 'read; + } + if enc == Encoding::Proto { + self.protocol_buffers_supported = true; + } + return Ok(msg); + } + } + panic!("unknown encoding"); + } + } + + pub async fn write(&mut self, msg: &PeerMessage) { + if let Some(enc) = self.encoding() { + self.write_encoded(&msg.serialize(enc)).await; + } else { + self.write_encoded(&msg.serialize(Encoding::Proto)).await; + self.write_encoded(&msg.serialize(Encoding::Borsh)).await; + } + } + + async fn write_encoded(&mut self, msg: &[u8]) { + self.stream.stream.write_u32_le(msg.len() as u32).await.unwrap(); + self.stream.stream.write_all(msg).await.unwrap(); + self.stream.stream.flush().await.unwrap(); + } +} diff --git a/chain/network/src/types.rs b/chain/network/src/types.rs new file mode 100644 index 000000000..8c6bbd697 --- /dev/null +++ b/chain/network/src/types.rs @@ -0,0 +1,491 @@ +/// Type that belong to the network protocol. +pub use crate::network_protocol::{ + Disconnect, Encoding, Handshake, HandshakeFailureReason, PeerMessage, RoutingTableUpdate, + SignedAccountData, +}; +/// Exported types, which are part of network protocol. +pub use crate::network_protocol::{ + Edge, PartialEdgeInfo, PartialEncodedChunkForwardMsg, PartialEncodedChunkRequestMsg, + PartialEncodedChunkResponseMsg, PeerChainInfoV2, PeerInfo, SnapshotHostInfo, StateResponseInfo, + StateResponseInfoV1, StateResponseInfoV2, +}; +use crate::routing::routing_table_view::RoutingTableInfo; +pub use crate::state_sync::{StateSync, StateSyncResponse}; +use unc_async::messaging::{ + AsyncSender, CanSend, CanSendAsync, IntoAsyncSender, IntoSender, Sender, +}; +use unc_async::time; +use unc_crypto::PublicKey; +use unc_primitives::block::{ApprovalMessage, Block, GenesisId}; +use unc_primitives::challenge::Challenge; +use unc_primitives::chunk_validation::{ChunkEndorsement, ChunkStateWitness}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::sharding::PartialEncodedChunkWithArcReceipts; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{AccountId, BlockHeight, EpochHeight, ShardId}; +use std::collections::{HashMap, HashSet}; +use std::fmt::Debug; +use std::net::SocketAddr; +use std::sync::Arc; + +/// Number of hops a message is allowed to travel before being dropped. +/// This is used to avoid infinite loop because of inconsistent view of the network +/// by different nodes. +pub const ROUTED_MESSAGE_TTL: u8 = 100; + +/// Peer type. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, strum::IntoStaticStr)] +pub enum PeerType { + /// Inbound session + Inbound, + /// Outbound session + Outbound, +} + +#[derive(Debug, Clone)] +pub struct KnownProducer { + pub account_id: AccountId, + pub addr: Option, + pub peer_id: PeerId, + pub next_hops: Option>, +} + +/// Ban reason. +#[derive(borsh::BorshSerialize, borsh::BorshDeserialize, Debug, Clone, PartialEq, Eq, Copy)] +#[borsh(use_discriminant = false)] +pub enum ReasonForBan { + None = 0, + BadBlock = 1, + BadBlockHeader = 2, + HeightFraud = 3, + BadHandshake = 4, + BadBlockApproval = 5, + Abusive = 6, + InvalidSignature = 7, + InvalidPeerId = 8, + InvalidHash = 9, + InvalidEdge = 10, + InvalidDistanceVector = 11, + Blacklisted = 14, + ProvidedNotEnoughHeaders = 15, +} + +/// Banning signal sent from Peer instance to PeerManager +/// just before Peer instance is stopped. +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub struct Ban { + pub peer_id: PeerId, + pub ban_reason: ReasonForBan, +} + +/// Status of the known peers. +#[derive(Eq, PartialEq, Debug, Clone)] +pub enum KnownPeerStatus { + /// We got information about this peer from someone, but we didn't + /// verify them yet. This peer might not exist, invalid IP etc. + /// Also the peers that we failed to connect to, will be marked as 'Unknown'. + Unknown, + /// We know that this peer exists - we were connected to it, or it was provided as boot node. + NotConnected, + /// We're currently connected to this peer. + Connected, + /// We banned this peer for some reason. Once the ban time is over, it will move to 'NotConnected' state. + Banned(ReasonForBan, time::Utc), +} + +/// Information node stores about known peers. +#[derive(Debug, Clone)] +pub struct KnownPeerState { + pub peer_info: PeerInfo, + pub status: KnownPeerStatus, + pub first_seen: time::Utc, + pub last_seen: time::Utc, + // Last time we tried to connect to this peer. + // This data is not persisted in storage. + pub last_outbound_attempt: Option<(time::Utc, Result<(), String>)>, +} + +impl KnownPeerState { + pub fn new(peer_info: PeerInfo, now: time::Utc) -> Self { + KnownPeerState { + peer_info, + status: KnownPeerStatus::Unknown, + first_seen: now, + last_seen: now, + last_outbound_attempt: None, + } + } +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct ConnectionInfo { + pub peer_info: PeerInfo, + pub time_established: time::Utc, + pub time_connected_until: time::Utc, +} + +impl KnownPeerStatus { + pub fn is_banned(&self) -> bool { + matches!(self, KnownPeerStatus::Banned(_, _)) + } +} + +/// Set of account keys. +/// This is information which chain pushes to network to implement tier1. +/// See ChainInfo. +pub type AccountKeys = HashMap>; + +/// Network-relevant data about the chain. +// TODO(gprusak): it is more like node info, or sth. +#[derive(Debug, Clone)] +pub struct ChainInfo { + pub tracked_shards: Vec, + // The lastest block on chain. + pub block: Block, + // Public keys of accounts participating in the BFT consensus + // It currently includes "block producers", "chunk producers" and "approvers". + // They are collectively known as "validators". + // Peers acting on behalf of these accounts have a higher + // priority on the NEAR network than other peers. + pub tier1_accounts: Arc, +} + +#[derive(Debug, actix::Message)] +#[rtype(result = "()")] +pub struct SetChainInfo(pub ChainInfo); + +/// Public actix interface of `PeerManagerActor`. +#[derive(actix::Message, Debug, strum::IntoStaticStr)] +#[rtype(result = "PeerManagerMessageResponse")] +pub enum PeerManagerMessageRequest { + NetworkRequests(NetworkRequests), + /// Request PeerManager to connect to the given peer. + /// Used in tests and internally by PeerManager. + /// TODO: replace it with AsyncContext::spawn/run_later for internal use. + OutboundTcpConnect(crate::tcp::Stream), + /// The following types of requests are used to trigger actions in the Peer Manager for testing. + /// TEST-ONLY: Fetch current routing table. + FetchRoutingTable, +} + +impl PeerManagerMessageRequest { + pub fn as_network_requests(self) -> NetworkRequests { + if let PeerManagerMessageRequest::NetworkRequests(item) = self { + item + } else { + panic!("expected PeerMessageRequest::NetworkRequests("); + } + } + + pub fn as_network_requests_ref(&self) -> &NetworkRequests { + if let PeerManagerMessageRequest::NetworkRequests(item) = self { + item + } else { + panic!("expected PeerMessageRequest::NetworkRequests"); + } + } +} + +/// List of all replies to messages to `PeerManager`. See `PeerManagerMessageRequest` for more details. +#[derive(actix::MessageResponse, Debug)] +pub enum PeerManagerMessageResponse { + NetworkResponses(NetworkResponses), + /// TEST-ONLY + OutboundTcpConnect, + FetchRoutingTable(RoutingTableInfo), +} + +impl PeerManagerMessageResponse { + pub fn as_network_response(self) -> NetworkResponses { + if let PeerManagerMessageResponse::NetworkResponses(item) = self { + item + } else { + panic!("expected PeerMessageRequest::NetworkResponses"); + } + } +} + +impl From for PeerManagerMessageResponse { + fn from(msg: NetworkResponses) -> Self { + PeerManagerMessageResponse::NetworkResponses(msg) + } +} + +// TODO(#1313): Use Box +#[derive(Clone, strum::AsRefStr, Debug, Eq, PartialEq)] +#[allow(clippy::large_enum_variant)] +pub enum NetworkRequests { + /// Sends block, either when block was just produced or when requested. + Block { block: Block }, + /// Sends approval. + Approval { approval_message: ApprovalMessage }, + /// Request block with given hash from given peer. + BlockRequest { hash: CryptoHash, peer_id: PeerId }, + /// Request given block headers. + BlockHeadersRequest { hashes: Vec, peer_id: PeerId }, + /// Request state header for given shard at given state root. + StateRequestHeader { shard_id: ShardId, sync_hash: CryptoHash, peer_id: PeerId }, + /// Request state part for given shard at given state root. + StateRequestPart { shard_id: ShardId, sync_hash: CryptoHash, part_id: u64, peer_id: PeerId }, + /// Ban given peer. + BanPeer { peer_id: PeerId, ban_reason: ReasonForBan }, + /// Announce account + AnnounceAccount(AnnounceAccount), + /// Broadcast information about a hosted snapshot. + SnapshotHostInfo { sync_hash: CryptoHash, epoch_height: EpochHeight, shards: Vec }, + + /// Request chunk parts and/or receipts + PartialEncodedChunkRequest { + target: AccountIdOrPeerTrackingShard, + request: PartialEncodedChunkRequestMsg, + create_time: time::Instant, + }, + /// Information about chunk such as its header, some subset of parts and/or incoming receipts + PartialEncodedChunkResponse { route_back: CryptoHash, response: PartialEncodedChunkResponseMsg }, + /// Information about chunk such as its header, some subset of parts and/or incoming receipts + PartialEncodedChunkMessage { + account_id: AccountId, + partial_encoded_chunk: PartialEncodedChunkWithArcReceipts, + }, + /// Forwarding a chunk part to a validator tracking the shard + PartialEncodedChunkForward { account_id: AccountId, forward: PartialEncodedChunkForwardMsg }, + /// Valid transaction but since we are not validators we send this transaction to current validators. + ForwardTx(AccountId, SignedTransaction), + /// Query transaction status + TxStatus(AccountId, AccountId, CryptoHash), + /// A challenge to invalidate a block. + Challenge(Challenge), + /// A chunk's state witness. + ChunkStateWitness(Vec, ChunkStateWitness), + /// Message for a chunk endorsement, sent by a chunk validator to the block producer. + ChunkEndorsement(AccountId, ChunkEndorsement), +} + +/// Combines peer address info, chain. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct FullPeerInfo { + pub peer_info: PeerInfo, + pub chain_info: PeerChainInfo, +} + +/// These are the information needed for highest height peers. For these peers, we guarantee that +/// the height and hash of the latest block are set. +#[derive(Debug, Clone)] +pub struct HighestHeightPeerInfo { + pub peer_info: PeerInfo, + /// Chain Id and hash of genesis block. + pub genesis_id: GenesisId, + /// Height and hash of the highest block we've ever received from the peer + pub highest_block_height: BlockHeight, + /// Hash of the latest block + pub highest_block_hash: CryptoHash, + /// Shards that the peer is tracking. + pub tracked_shards: Vec, + /// Denote if a node is running in archival mode or not. + pub archival: bool, +} + +impl From for Option { + fn from(p: FullPeerInfo) -> Self { + if p.chain_info.last_block.is_some() { + Some(HighestHeightPeerInfo { + peer_info: p.peer_info, + genesis_id: p.chain_info.genesis_id, + highest_block_height: p.chain_info.last_block.unwrap().height, + highest_block_hash: p.chain_info.last_block.unwrap().hash, + tracked_shards: p.chain_info.tracked_shards, + archival: p.chain_info.archival, + }) + } else { + None + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct BlockInfo { + pub height: BlockHeight, + pub hash: CryptoHash, +} + +/// This is the internal representation of PeerChainInfoV2. +/// We separate these two structs because PeerChainInfoV2 is part of network protocol, and can't be +/// modified easily. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct PeerChainInfo { + /// Chain Id and hash of genesis block. + pub genesis_id: GenesisId, + /// Height and hash of the highest block we've ever received from the peer + pub last_block: Option, + /// Shards that the peer is tracking. + pub tracked_shards: Vec, + /// Denote if a node is running in archival mode or not. + pub archival: bool, +} + +// Information about the connected peer that is shared with the rest of the system. +#[derive(Debug, Clone)] +pub struct ConnectedPeerInfo { + pub full_peer_info: FullPeerInfo, + /// Number of bytes we've received from the peer. + pub received_bytes_per_sec: u64, + /// Number of bytes we've sent to the peer. + pub sent_bytes_per_sec: u64, + /// Last time requested peers. + pub last_time_peer_requested: time::Instant, + /// Last time we received a message from this peer. + pub last_time_received_message: time::Instant, + /// Time where the connection was established. + pub connection_established_time: time::Instant, + /// Who started connection. Inbound (other) or Outbound (us). + pub peer_type: PeerType, + /// Nonce used for the connection with the peer. + pub nonce: u64, +} + +#[derive(Debug, Clone, actix::MessageResponse)] +pub struct NetworkInfo { + /// TIER2 connections. + pub connected_peers: Vec, + pub num_connected_peers: usize, + pub peer_max_count: u32, + pub highest_height_peers: Vec, + pub sent_bytes_per_sec: u64, + pub received_bytes_per_sec: u64, + /// Accounts of known block and chunk producers from routing table. + pub known_producers: Vec, + /// Collected data about the current TIER1 accounts. + pub tier1_accounts_keys: Vec, + pub tier1_accounts_data: Vec>, + /// TIER1 connections. + pub tier1_connections: Vec, +} + +#[derive(Debug, actix::MessageResponse, PartialEq, Eq)] +pub enum NetworkResponses { + NoResponse, + RouteNotFound, +} + +#[derive(Clone, derive_more::AsRef)] +pub struct PeerManagerAdapter { + pub async_request_sender: + AsyncSender>, + pub request_sender: Sender, + pub set_chain_info_sender: Sender, +} + +impl< + A: CanSendAsync> + + CanSend + + CanSend, + > From> for PeerManagerAdapter +{ + fn from(arc: Arc) -> Self { + Self { + async_request_sender: arc.as_async_sender(), + request_sender: arc.as_sender(), + set_chain_info_sender: arc.as_sender(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::network_protocol::{RawRoutedMessage, RoutedMessage, RoutedMessageBody}; + + const ALLOWED_SIZE: usize = 1 << 20; + const NOTIFY_SIZE: usize = 1024; + + macro_rules! assert_size { + ($type:ident) => { + let struct_size = std::mem::size_of::<$type>(); + if struct_size >= NOTIFY_SIZE { + println!("The size of {} is {}", stringify!($type), struct_size); + } + assert!(struct_size <= ALLOWED_SIZE); + }; + } + + #[test] + fn test_size() { + assert_size!(HandshakeFailureReason); + assert_size!(NetworkRequests); + assert_size!(NetworkResponses); + assert_size!(Handshake); + assert_size!(RoutingTableUpdate); + assert_size!(FullPeerInfo); + assert_size!(NetworkInfo); + } + + macro_rules! assert_size { + ($type:ident) => { + let struct_size = std::mem::size_of::<$type>(); + if struct_size >= NOTIFY_SIZE { + println!("The size of {} is {}", stringify!($type), struct_size); + } + assert!(struct_size <= ALLOWED_SIZE); + }; + } + + #[test] + fn test_enum_size() { + assert_size!(PeerType); + assert_size!(RoutedMessageBody); + assert_size!(KnownPeerStatus); + assert_size!(ReasonForBan); + } + + #[test] + fn test_struct_size() { + assert_size!(PeerInfo); + assert_size!(AnnounceAccount); + assert_size!(RawRoutedMessage); + assert_size!(RoutedMessage); + assert_size!(KnownPeerState); + assert_size!(Ban); + assert_size!(StateResponseInfoV1); + assert_size!(PartialEncodedChunkRequestMsg); + } + + #[test] + fn routed_message_body_compatibility_smoke_test() { + #[track_caller] + fn check(msg: RoutedMessageBody, expected: &[u8]) { + let actual = borsh::to_vec(&msg).unwrap(); + assert_eq!(actual.as_slice(), expected); + } + + check( + RoutedMessageBody::TxStatusRequest("test_x".parse().unwrap(), CryptoHash([42; 32])), + &[ + 2, 6, 0, 0, 0, 116, 101, 115, 116, 95, 120, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, + ], + ); + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +/// Defines the destination for a network request. +/// The request should be sent either to the `account_id` as a routed message, or directly to +/// any peer that tracks the shard. +/// If `prefer_peer` is `true`, should be sent to the peer, unless no peer tracks the shard, in which +/// case fall back to sending to the account. +/// Otherwise, send to the account, unless we do not know the route, in which case send to the peer. +pub struct AccountIdOrPeerTrackingShard { + /// Target account to send the the request to + pub account_id: Option, + /// Whether to check peers first or target account first + pub prefer_peer: bool, + /// Select peers that track shard `shard_id` + pub shard_id: ShardId, + /// Select peers that are archival nodes if it is true + pub only_archival: bool, + /// Only send messages to peers whose latest chain height is no less `min_height` + pub min_height: BlockHeight, +} diff --git a/chain/pool/Cargo.toml b/chain/pool/Cargo.toml new file mode 100644 index 000000000..06b1fac3f --- /dev/null +++ b/chain/pool/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "unc-pool" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +borsh.workspace = true +once_cell.workspace = true +rand.workspace = true + +unc-crypto.workspace = true +unc-o11y.workspace = true +unc-primitives.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-o11y/nightly", + "unc-primitives/nightly", +] +nightly_protocol = [ + "unc-o11y/nightly_protocol", + "unc-primitives/nightly_protocol", +] diff --git a/chain/pool/README.md b/chain/pool/README.md new file mode 100644 index 000000000..c71751d1d --- /dev/null +++ b/chain/pool/README.md @@ -0,0 +1,3 @@ +# unc-pool crate + +This crate holds TransactionPool (with PoolIterator), that is used to keep track of transactions that were not yet accepted into the blockchain. \ No newline at end of file diff --git a/chain/pool/src/lib.rs b/chain/pool/src/lib.rs new file mode 100644 index 000000000..d59dc7167 --- /dev/null +++ b/chain/pool/src/lib.rs @@ -0,0 +1,597 @@ +use std::collections::btree_map::Entry; +use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; + +use crate::types::{PoolIterator, PoolKey, TransactionGroup}; + +use unc_crypto::PublicKey; +use unc_o11y::metrics::prometheus::core::{AtomicI64, GenericGauge}; +use unc_primitives::epoch_manager::RngSeed; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::AccountId; +use std::ops::Bound; + +mod metrics; +pub mod types; + +#[derive(Debug, PartialEq)] +pub enum InsertTransactionResult { + /// Transaction was successfully inserted. + Success, + /// Transaction is already in the pool. + Duplicate, + /// Not enough space to fit the transaction. + NoSpaceLeft, +} + +/// Transaction pool: keeps track of transactions that were not yet accepted into the block chain. +pub struct TransactionPool { + /// Transactions are grouped by a pair of (account ID, signer public key). + /// NOTE: It's more efficient on average to keep transactions unsorted and with potentially + /// conflicting nonce than to create a BTreeMap for every transaction. + transactions: BTreeMap>, + /// Set of all hashes to quickly check if the given transaction is in the pool. + unique_transactions: HashSet, + /// A uniquely generated key seed to randomize PoolKey order. + key_seed: RngSeed, + /// The key after which the pool iterator starts. Doesn't have to be present in the pool. + last_used_key: PoolKey, + /// If set, new transactions that bring the size of the pool over this limit will be rejected. + total_transaction_size_limit: Option, + /// Total size of transactions in the pool measured in bytes. + total_transaction_size: u64, + /// Metrics tracked for transaction pool. + transaction_pool_count_metric: GenericGauge, + transaction_pool_size_metric: GenericGauge, +} + +impl TransactionPool { + pub fn new( + key_seed: RngSeed, + total_transaction_size_limit: Option, + metrics_label: &str, + ) -> Self { + let transaction_pool_count_metric = + metrics::TRANSACTION_POOL_COUNT.with_label_values(&[metrics_label]); + let transaction_pool_size_metric = + metrics::TRANSACTION_POOL_SIZE.with_label_values(&[metrics_label]); + // A `get()` call initializes a metric even if its value is zero. + transaction_pool_count_metric.get(); + transaction_pool_size_metric.get(); + + Self { + key_seed, + transactions: BTreeMap::new(), + unique_transactions: HashSet::new(), + last_used_key: CryptoHash::default(), + total_transaction_size_limit, + total_transaction_size: 0, + transaction_pool_count_metric, + transaction_pool_size_metric, + } + } + + fn key(&self, account_id: &AccountId, public_key: &PublicKey) -> PoolKey { + let mut v = borsh::to_vec(&public_key).unwrap(); + v.extend_from_slice(&self.key_seed); + v.extend_from_slice(account_id.as_bytes()); + hash(&v) + } + + /// Inserts a signed transaction that passed validation into the pool. + #[must_use] + pub fn insert_transaction( + &mut self, + signed_transaction: SignedTransaction, + ) -> InsertTransactionResult { + if !self.unique_transactions.insert(signed_transaction.get_hash()) { + // The hash of this transaction was already seen, skip it. + return InsertTransactionResult::Duplicate; + } + // We never expect the total size to go over `u64` during real operation as that would + // be more than 10^9 GiB of RAM consumed for transaction pool, so panicing here is intended + // to catch a logic error in estimation of transaction size. + let new_total_transaction_size = self + .total_transaction_size + .checked_add(signed_transaction.get_size()) + .expect("Total transaction size is too large"); + if let Some(limit) = self.total_transaction_size_limit { + if new_total_transaction_size > limit { + return InsertTransactionResult::NoSpaceLeft; + } + } + + // At this point transaction is accepted to the pool. + self.total_transaction_size = new_total_transaction_size; + let signer_id = &signed_transaction.transaction.signer_id; + let signer_public_key = &signed_transaction.transaction.public_key; + self.transactions + .entry(self.key(signer_id, signer_public_key)) + .or_insert_with(Vec::new) + .push(signed_transaction); + + self.transaction_pool_count_metric.inc(); + self.transaction_pool_size_metric.set(self.total_transaction_size as i64); + InsertTransactionResult::Success + } + + /// Returns a pool iterator wrapper that implements an iterator-like trait to iterate over + /// transaction groups in the proper order defined by the protocol. + /// When the iterator is dropped, all remaining groups are inserted back into the pool. + pub fn pool_iterator(&mut self) -> PoolIteratorWrapper<'_> { + PoolIteratorWrapper::new(self) + } + + /// Removes given transactions from the pool. + /// + /// In practice, used to evict transactions that have already been included into the block or + /// became invalid. + pub fn remove_transactions(&mut self, transactions: &[SignedTransaction]) { + let mut grouped_transactions = HashMap::new(); + for tx in transactions { + // If transaction is not present in the pool, skip it. + if !self.unique_transactions.remove(&tx.get_hash()) { + continue; + } + + let signer_id = &tx.transaction.signer_id; + let signer_public_key = &tx.transaction.public_key; + grouped_transactions + .entry(self.key(signer_id, signer_public_key)) + .or_insert_with(HashSet::new) + .insert(tx.get_hash()); + } + for (key, hashes) in grouped_transactions { + if let Entry::Occupied(mut entry) = self.transactions.entry(key) { + entry.get_mut().retain(|tx| { + if !hashes.contains(&tx.get_hash()) { + return true; + } + // See the comment above where we increase the size for reasoning why panicing + // here catches a logic error. + self.total_transaction_size = self + .total_transaction_size + .checked_sub(tx.get_size()) + .expect("Total transaction size dropped below zero"); + false + }); + if entry.get().is_empty() { + entry.remove_entry(); + } + } + } + + // We can update metrics only once for the whole batch of transactions. + self.transaction_pool_count_metric.set(self.unique_transactions.len() as i64); + self.transaction_pool_size_metric.set(self.total_transaction_size as i64); + } + + /// Returns the number of unique transactions in the pool. + pub fn len(&self) -> usize { + self.unique_transactions.len() + } + + /// Returns the total size of transactions in the pool in bytes. + pub fn transaction_size(&self) -> u64 { + self.total_transaction_size + } +} + +/// PoolIterator is a structure to pull transactions from the pool. +/// It implements `PoolIterator` trait that iterates over transaction groups one by one. +/// When the wrapper is dropped the remaining transactions are returned back to the pool. +pub struct PoolIteratorWrapper<'a> { + /// Mutable reference to the pool, to avoid exposing it while the iterator exists. + pool: &'a mut TransactionPool, + + /// Queue of transaction groups. Each group there is sorted by nonce. + sorted_groups: VecDeque, +} + +impl<'a> PoolIteratorWrapper<'a> { + pub fn new(pool: &'a mut TransactionPool) -> Self { + Self { pool, sorted_groups: Default::default() } + } +} + +/// The iterator works with the following algorithm: +/// On next(), the iterator tries to get a transaction group from the pool, sorts transactions in +/// it, and add it to the back of the sorted groups queue. +/// Remembers the last used key, so it can continue from the next key. +/// +/// If the pool is empty, the iterator gets the group from the front of the sorted groups queue. +/// +/// If this group is empty (no transactions left inside), then the iterator discards it and +/// updates `unique_transactions` in the pool. Then gets the next one. +/// +/// Once a non-empty group is found, this group is pushed to the back of the sorted groups queue +/// and the iterator returns a mutable reference to this group. +/// +/// If the sorted groups queue is empty, the iterator returns None. +/// +/// When the iterator is dropped, `unique_transactions` in the pool is updated for every group. +/// And all non-empty group from the sorted groups queue are inserted back into the pool. +impl<'a> PoolIterator for PoolIteratorWrapper<'a> { + fn next(&mut self) -> Option<&mut TransactionGroup> { + if !self.pool.transactions.is_empty() { + let key = *self + .pool + .transactions + .range((Bound::Excluded(self.pool.last_used_key), Bound::Unbounded)) + .next() + .map(|(k, _v)| k) + .unwrap_or_else(|| { + self.pool + .transactions + .keys() + .next() + .expect("we've just checked that the map is not empty") + }); + self.pool.last_used_key = key; + let mut transactions = + self.pool.transactions.remove(&key).expect("just checked existence"); + transactions.sort_by_key(|st| std::cmp::Reverse(st.transaction.nonce)); + self.sorted_groups.push_back(TransactionGroup { + key, + transactions, + removed_transaction_hashes: vec![], + removed_transaction_size: 0, + }); + Some(self.sorted_groups.back_mut().expect("just pushed")) + } else { + while let Some(sorted_group) = self.sorted_groups.pop_front() { + if sorted_group.transactions.is_empty() { + for hash in sorted_group.removed_transaction_hashes { + self.pool.unique_transactions.remove(&hash); + } + // See the comment in `insert_transaction` where we increase the size for reasoning + // why panicing here catches a logic error. + self.pool.total_transaction_size = self + .pool + .total_transaction_size + .checked_sub(sorted_group.removed_transaction_size) + .expect("Total transaction size dropped below zero"); + + self.pool + .transaction_pool_count_metric + .set(self.pool.unique_transactions.len() as i64); + self.pool.transaction_pool_size_metric.set(self.pool.transaction_size() as i64); + } else { + self.sorted_groups.push_back(sorted_group); + return Some(self.sorted_groups.back_mut().expect("just pushed")); + } + } + None + } + } +} + +/// When a pool iterator is dropped, all remaining non empty transaction groups from the sorted +/// groups queue are inserted back into the pool. And removed transactions hashes from groups are +/// removed from the pool's unique_transactions. +impl<'a> Drop for PoolIteratorWrapper<'a> { + fn drop(&mut self) { + for group in self.sorted_groups.drain(..) { + for hash in group.removed_transaction_hashes { + self.pool.unique_transactions.remove(&hash); + } + // See the comment in `insert_transaction` where we increase the size for reasoning + // why panicing here catches a logic error. + self.pool.total_transaction_size = self + .pool + .total_transaction_size + .checked_sub(group.removed_transaction_size) + .expect("Total transaction size dropped below zero"); + + if !group.transactions.is_empty() { + self.pool.transactions.insert(group.key, group.transactions); + } + } + // We can update metrics only once for the whole batch of transactions. + self.pool.transaction_pool_count_metric.set(self.pool.unique_transactions.len() as i64); + self.pool.transaction_pool_size_metric.set(self.pool.transaction_size() as i64); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::Arc; + + use rand::seq::SliceRandom; + use rand::thread_rng; + + use unc_crypto::{InMemorySigner, KeyType}; + + use unc_primitives::hash::CryptoHash; + use unc_primitives::types::Balance; + + const TEST_SEED: RngSeed = [3; 32]; + + fn generate_transactions( + signer_id: &str, + signer_seed: &str, + starting_nonce: u64, + end_nonce: u64, + ) -> Vec { + let signer_id: AccountId = signer_id.parse().unwrap(); + let signer = + Arc::new(InMemorySigner::from_seed(signer_id.clone(), KeyType::ED25519, signer_seed)); + (starting_nonce..=end_nonce) + .map(|i| { + SignedTransaction::send_money( + i, + signer_id.clone(), + "bob.near".parse().unwrap(), + &*signer, + i as Balance, + CryptoHash::default(), + ) + }) + .collect() + } + + fn process_txs_to_nonces( + mut transactions: Vec, + expected_weight: u32, + ) -> (Vec, TransactionPool) { + let mut pool = TransactionPool::new(TEST_SEED, None, ""); + let mut rng = thread_rng(); + transactions.shuffle(&mut rng); + for tx in transactions { + assert_eq!(pool.insert_transaction(tx), InsertTransactionResult::Success); + } + ( + prepare_transactions(&mut pool, expected_weight) + .iter() + .map(|tx| tx.transaction.nonce) + .collect(), + pool, + ) + } + + fn sort_pairs(a: &mut [u64]) { + for c in a.chunks_exact_mut(2) { + if c[0] > c[1] { + c.swap(0, 1); + } + } + } + + fn prepare_transactions( + pool: &mut TransactionPool, + max_number_of_transactions: u32, + ) -> Vec { + let mut res = vec![]; + let mut pool_iter = pool.pool_iterator(); + while res.len() < max_number_of_transactions as usize { + if let Some(iter) = pool_iter.next() { + if let Some(tx) = iter.next() { + res.push(tx); + } + } else { + break; + } + } + res + } + + /// Add transactions of nonce from 1..10 in random order. Check that mempool + /// orders them correctly. + #[test] + fn test_order_nonce() { + let transactions = generate_transactions("alice.near", "alice.near", 1, 10); + let (nonces, _) = process_txs_to_nonces(transactions, 10); + assert_eq!(nonces, (1..=10).collect::>()); + } + + /// Add transactions of nonce from 1..10 in random order from 2 signers. Check that mempool + /// orders them correctly. + #[test] + fn test_order_nonce_two_signers() { + let mut transactions = generate_transactions("alice.near", "alice.near", 1, 10); + transactions.extend(generate_transactions("bob.near", "bob.near", 1, 10)); + + let (nonces, _) = process_txs_to_nonces(transactions, 10); + assert_eq!(nonces, (1..=5).map(|a| vec![a; 2]).flatten().collect::>()); + } + + /// Add transactions of nonce from 1..10 in random order from the same account but with + /// different public keys. + #[test] + fn test_order_nonce_same_account_two_access_keys_variable_nonces() { + let mut transactions = generate_transactions("alice.near", "alice.near", 1, 10); + transactions.extend(generate_transactions("alice.near", "bob.near", 21, 30)); + + let (mut nonces, _) = process_txs_to_nonces(transactions, 10); + sort_pairs(&mut nonces[..]); + assert_eq!(nonces, (1..=5).map(|a| vec![a, a + 20]).flatten().collect::>()); + } + + /// Add transactions of nonce from 1..=3 and transactions with nonce 21..=31. Pull 10. + /// Then try to get another 10. + #[test] + fn test_retain() { + let mut transactions = generate_transactions("alice.near", "alice.near", 1, 3); + transactions.extend(generate_transactions("alice.near", "bob.near", 21, 31)); + + let (mut nonces, mut pool) = process_txs_to_nonces(transactions, 10); + sort_pairs(&mut nonces[..6]); + assert_eq!(nonces, vec![1, 21, 2, 22, 3, 23, 24, 25, 26, 27]); + let nonces: Vec = + prepare_transactions(&mut pool, 10).iter().map(|tx| tx.transaction.nonce).collect(); + assert_eq!(nonces, vec![28, 29, 30, 31]); + } + + #[test] + fn test_remove_transactions() { + let n = 100; + let mut transactions = (1..=n) + .map(|i| { + let signer_id = AccountId::try_from(format!("user_{}", i % 5)).unwrap(); + let signer_seed = format!("user_{}", i % 3); + let signer = Arc::new(InMemorySigner::from_seed( + signer_id.clone(), + KeyType::ED25519, + &signer_seed, + )); + SignedTransaction::send_money( + i, + signer_id, + "bob.near".parse().unwrap(), + &*signer, + i as Balance, + CryptoHash::default(), + ) + }) + .collect::>(); + + let mut pool = TransactionPool::new(TEST_SEED, None, ""); + let mut rng = thread_rng(); + transactions.shuffle(&mut rng); + for tx in transactions.clone() { + println!("{:?}", tx); + assert_eq!(pool.insert_transaction(tx), InsertTransactionResult::Success); + } + assert_eq!(pool.len(), n as usize); + + transactions.shuffle(&mut rng); + let (txs_to_remove, txs_to_check) = transactions.split_at(transactions.len() / 2); + pool.remove_transactions(txs_to_remove); + + assert_eq!(pool.len(), txs_to_check.len()); + + let mut pool_txs = prepare_transactions(&mut pool, txs_to_check.len() as u32); + pool_txs.sort_by_key(|tx| tx.transaction.nonce); + let mut expected_txs = txs_to_check.to_vec(); + expected_txs.sort_by_key(|tx| tx.transaction.nonce); + + assert_eq!(pool_txs, expected_txs); + } + + /// Add transactions of nonce from 1..=3 and transactions with nonce 21..=31. Pull 10. + /// Then try to get another 10. + #[test] + fn test_pool_iterator() { + let mut transactions = generate_transactions("alice.near", "alice.near", 1, 3); + transactions.extend(generate_transactions("alice.near", "bob.near", 21, 31)); + + let (nonces, mut pool) = process_txs_to_nonces(transactions, 0); + assert!(nonces.is_empty()); + let mut res = vec![]; + let mut pool_iter = pool.pool_iterator(); + while let Some(iter) = pool_iter.next() { + while let Some(tx) = iter.next() { + if tx.transaction.nonce & 1 == 1 { + res.push(tx); + break; + } + } + } + drop(pool_iter); + assert_eq!(pool.len(), 0); + assert_eq!(pool.transaction_size(), 0); + let mut nonces: Vec<_> = res.into_iter().map(|tx| tx.transaction.nonce).collect(); + sort_pairs(&mut nonces[..4]); + assert_eq!(nonces, vec![1, 21, 3, 23, 25, 27, 29, 31]); + } + + /// Test pool iterator updates unique transactions. + #[test] + fn test_pool_iterator_removes_unique() { + let transactions = generate_transactions("alice.near", "alice.near", 1, 10); + + let (nonces, mut pool) = process_txs_to_nonces(transactions.clone(), 5); + assert_eq!(nonces.len(), 5); + assert_eq!(pool.len(), 5); + + for tx in transactions { + assert!(matches!( + pool.insert_transaction(tx), + InsertTransactionResult::Success | InsertTransactionResult::Duplicate + )); + } + assert_eq!(pool.len(), 10); + let txs = prepare_transactions(&mut pool, 10); + assert_eq!(txs.len(), 10); + assert_eq!(pool.len(), 0); + assert_eq!(pool.transaction_size(), 0); + } + + /// Test pool iterator remembers the last key. + #[test] + fn test_pool_iterator_remembers_the_last_key() { + let transactions = (1..=10) + .map(|i| { + let signer_id = AccountId::try_from(format!("user_{}", i)).unwrap(); + let signer_seed = signer_id.as_ref(); + let signer = Arc::new(InMemorySigner::from_seed( + signer_id.clone(), + KeyType::ED25519, + signer_seed, + )); + SignedTransaction::send_money( + i, + signer_id, + "bob.near".parse().unwrap(), + &*signer, + i as Balance, + CryptoHash::default(), + ) + }) + .collect::>(); + let (mut nonces, mut pool) = process_txs_to_nonces(transactions.clone(), 5); + assert_eq!(nonces.len(), 5); + assert_eq!(pool.len(), 5); + + for tx in transactions { + assert!(matches!( + pool.insert_transaction(tx), + InsertTransactionResult::Success | InsertTransactionResult::Duplicate + )); + } + assert_eq!(pool.len(), 10); + let txs = prepare_transactions(&mut pool, 5); + assert_eq!(txs.len(), 5); + nonces.sort(); + let mut new_nonces = txs.iter().map(|tx| tx.transaction.nonce).collect::>(); + new_nonces.sort(); + assert_ne!(nonces, new_nonces); + } + + #[test] + fn test_transaction_pool_size() { + let mut pool = TransactionPool::new(TEST_SEED, None, ""); + let transactions = generate_transactions("alice.near", "alice.near", 1, 100); + let mut total_transaction_size = 0; + // Adding transactions increases the size. + for tx in transactions.clone() { + total_transaction_size += tx.get_size(); + assert_eq!(pool.insert_transaction(tx), InsertTransactionResult::Success); + assert_eq!(pool.transaction_size(), total_transaction_size); + } + // Removing transactions decreases the size. + for tx in transactions { + total_transaction_size -= tx.get_size(); + pool.remove_transactions(&[tx]); + assert_eq!(pool.transaction_size(), total_transaction_size); + } + assert_eq!(pool.transaction_size(), 0); + } + + #[test] + fn test_transaction_pool_size_limit() { + let transactions = generate_transactions("alice.near", "alice.near", 1, 100); + // Each transaction is at least 1 byte in size, so the last transaction will not fit. + let pool_size_limit = + transactions.iter().map(|tx| tx.get_size()).sum::().checked_sub(1).unwrap(); + let mut pool = TransactionPool::new(TEST_SEED, Some(pool_size_limit), ""); + for (i, tx) in transactions.iter().cloned().enumerate() { + if i + 1 < transactions.len() { + assert_eq!(pool.insert_transaction(tx), InsertTransactionResult::Success); + } else { + assert_eq!(pool.insert_transaction(tx), InsertTransactionResult::NoSpaceLeft); + } + } + } +} diff --git a/chain/pool/src/metrics.rs b/chain/pool/src/metrics.rs new file mode 100644 index 000000000..cd74ed962 --- /dev/null +++ b/chain/pool/src/metrics.rs @@ -0,0 +1,20 @@ +use unc_o11y::metrics::IntGaugeVec; +use once_cell::sync::Lazy; + +pub static TRANSACTION_POOL_COUNT: Lazy = Lazy::new(|| { + unc_o11y::metrics::try_create_int_gauge_vec( + "unc_transaction_pool_entries", + "Total number of transactions currently tracked by the node in a given shard pool", + &["shard_id"], + ) + .unwrap() +}); + +pub static TRANSACTION_POOL_SIZE: Lazy = Lazy::new(|| { + unc_o11y::metrics::try_create_int_gauge_vec( + "unc_transaction_pool_size", + "Total size in bytes of transactions currently tracked by the node in a given shard pool", + &["shard_id"], + ) + .unwrap() +}); diff --git a/chain/pool/src/types.rs b/chain/pool/src/types.rs new file mode 100644 index 000000000..6b351f47b --- /dev/null +++ b/chain/pool/src/types.rs @@ -0,0 +1,40 @@ +use unc_primitives::hash::CryptoHash; +use unc_primitives::transaction::SignedTransaction; + +/// Trait acts like an iterator. It iterates over transactions groups by returning mutable +/// references to them. Each transaction group implements a draining iterator to pull transactions. +/// The order of the transaction groups is round robin scheduling. +/// When this iterator is dropped the remaining transactions are returned back to the pool. +pub trait PoolIterator { + fn next(&mut self) -> Option<&mut TransactionGroup>; +} + +/// A hash of (an AccountId, a PublicKey and a seed). +/// Used to randomize the order of the keys. +pub(crate) type PoolKey = CryptoHash; + +/// Represents a group of transactions with the same key. +pub struct TransactionGroup { + /// The key of the group. + pub(crate) key: PoolKey, + /// Ordered transactions by nonce in non-increasing order (e.g. 3, 2, 2). + pub(crate) transactions: Vec, + /// Hashes of the transactions that were pulled from the group using `.next()`. + pub(crate) removed_transaction_hashes: Vec, + /// Total size of transactions that were pulled from the group using `.next()`. + pub(crate) removed_transaction_size: u64, +} + +impl TransactionGroup { + /// Returns the next transaction with the smallest nonce and removes it from the group. + /// It also stores all hashes of returned transactions. + pub fn next(&mut self) -> Option { + if let Some(tx) = self.transactions.pop() { + self.removed_transaction_hashes.push(tx.get_hash()); + self.removed_transaction_size += tx.get_size(); + Some(tx) + } else { + None + } + } +} diff --git a/chain/telemetry/Cargo.toml b/chain/telemetry/Cargo.toml new file mode 100644 index 000000000..d08f55d53 --- /dev/null +++ b/chain/telemetry/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "unc-telemetry" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix.workspace = true +awc.workspace = true +futures.workspace = true +once_cell.workspace = true +openssl.workspace = true +serde.workspace = true +serde_json.workspace = true +tracing.workspace = true + +unc-o11y.workspace = true +unc-performance-metrics.workspace = true +unc-performance-metrics-macros.workspace = true +unc-primitives.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-o11y/nightly", + "unc-primitives/nightly", +] +nightly_protocol = [ + "unc-o11y/nightly_protocol", + "unc-primitives/nightly_protocol", +] diff --git a/chain/telemetry/README.md b/chain/telemetry/README.md new file mode 100644 index 000000000..45122eb40 --- /dev/null +++ b/chain/telemetry/README.md @@ -0,0 +1,10 @@ +# unc Telemetry + +A small utility (TelemetryActor), that tries to send the telemetry (metrics) information as JSON over HTTP-post to selected list of servers. +Telemetry is sent from all the framework binaries (that enabled it in the config.json) - like validators, RPC nodes etc. + +The data that is sent over is of type TelemetryInfo, and is signed with the server's key. + +It contains info about the code (release version), server (cpu, memory and network speeds), and chain (node_id, status, peer connected, block height etc). + +TODO: add pointer to the code, that is used by the receiving server. diff --git a/chain/telemetry/src/lib.rs b/chain/telemetry/src/lib.rs new file mode 100644 index 000000000..af7512389 --- /dev/null +++ b/chain/telemetry/src/lib.rs @@ -0,0 +1,124 @@ +mod metrics; + +use actix::{Actor, Addr, Context, Handler}; +use awc::{Client, Connector}; +use futures::FutureExt; +use unc_o11y::{handler_debug_span, OpenTelemetrySpanExt, WithSpanContext, WithSpanContextExt}; +use unc_performance_metrics_macros::perf; +use unc_primitives::static_clock::StaticClock; +use std::ops::Sub; +use std::time::{Duration, Instant}; + +/// Timeout for establishing connection. +const CONNECT_TIMEOUT: Duration = Duration::from_secs(10); + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub struct TelemetryConfig { + pub endpoints: Vec, + /// Only one request will be allowed in the specified time interval. + #[serde(default = "default_reporting_interval")] + pub reporting_interval: std::time::Duration, +} + +fn default_reporting_interval() -> std::time::Duration { + std::time::Duration::from_secs(10) +} + +impl Default for TelemetryConfig { + fn default() -> Self { + Self { endpoints: vec![], reporting_interval: default_reporting_interval() } + } +} + +/// Event to send over telemetry. +#[derive(actix::Message, Debug)] +#[rtype(result = "()")] +pub struct TelemetryEvent { + content: serde_json::Value, +} + +pub struct TelemetryActor { + config: TelemetryConfig, + client: Client, + last_telemetry_update: Instant, +} + +impl Default for TelemetryActor { + fn default() -> Self { + Self::new(TelemetryConfig::default()) + } +} + +impl TelemetryActor { + pub fn new(config: TelemetryConfig) -> Self { + for endpoint in config.endpoints.iter() { + if endpoint.is_empty() { + panic!( + "All telemetry endpoints must be valid URLs. Received: {:?}", + config.endpoints + ); + } + } + + let client = Client::builder() + .timeout(CONNECT_TIMEOUT) + .connector(Connector::new().max_http_version(awc::http::Version::HTTP_11)) + .finish(); + let reporting_interval = config.reporting_interval; + Self { + config, + client, + // Let the node report telemetry info at the startup. + last_telemetry_update: std::time::Instant::now().sub(reporting_interval), + } + } +} + +impl Actor for TelemetryActor { + type Context = Context; +} + +impl Handler> for TelemetryActor { + type Result = (); + + #[perf] + fn handle(&mut self, msg: WithSpanContext, _ctx: &mut Context) { + let (_span, msg) = handler_debug_span!(target: "telemetry", msg); + tracing::debug!(target: "client", ?msg); + let now = StaticClock::instant(); + if now.duration_since(self.last_telemetry_update) < self.config.reporting_interval { + // Throttle requests to the telemetry endpoints, to at most one + // request per `self.config.reporting_interval`. + return; + } + for endpoint in self.config.endpoints.iter() { + let endpoint = endpoint.clone(); + unc_performance_metrics::actix::spawn( + "telemetry", + self.client + .post(endpoint.clone()) + .insert_header(("Content-Type", "application/json")) + .send_json(&msg.content) + .map(move |response| { + let result = if let Err(error) = response { + tracing::warn!( + target: "telemetry", + err = ?error, + endpoint = ?endpoint, + "Failed to send telemetry data"); + "failed" + } else { + "ok" + }; + metrics::TELEMETRY_RESULT.with_label_values(&[result]).inc(); + }), + ); + } + self.last_telemetry_update = now; + } +} + +/// Send telemetry event to all the endpoints. +pub fn telemetry(telemetry: &Addr, content: serde_json::Value) { + telemetry.do_send(TelemetryEvent { content }.with_span_context()); +} diff --git a/chain/telemetry/src/metrics.rs b/chain/telemetry/src/metrics.rs new file mode 100644 index 000000000..e91869a82 --- /dev/null +++ b/chain/telemetry/src/metrics.rs @@ -0,0 +1,10 @@ +use once_cell::sync::Lazy; + +pub(crate) static TELEMETRY_RESULT: Lazy = Lazy::new(|| { + unc_o11y::metrics::try_create_int_counter_vec( + "unc_telemetry_result", + "Count of 'ok' or 'failed' results of uploading telemetry data", + &["success"], + ) + .unwrap() +}); diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..bc3617b3e --- /dev/null +++ b/codecov.yml @@ -0,0 +1,31 @@ +codecov: + notify: + after_n_builds: 10 # Keep in sync with .github/workflows/ci.yml + +coverage: + status: + project: + default: + target: auto + threshold: 5% + patch: + default: false + +component_management: + default_rules: + paths: + - "!debug_scripts/**" + - "!docs/**" + - "!genesis-tools/**" + - "!nightly/**" + - "!tools/**" + statuses: + - type: project + target: auto + threshold: 0.1% + - type: patch + target: auto + individual_components: + - component_id: unit-tests + flag_regexes: ["unittests"] + - component_id: all-tests diff --git a/core/async/Cargo.toml b/core/async/Cargo.toml new file mode 100644 index 000000000..756cc47b2 --- /dev/null +++ b/core/async/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "unc-async" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +description = "This crate contains the async helpers specific for framework" +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix.workspace = true +derive-enum-from-into.workspace = true +derive_more.workspace = true +futures.workspace = true +once_cell.workspace = true +serde.workspace = true +serde_json.workspace = true +time.workspace = true +tokio.workspace = true + +unc-primitives.workspace = true +unc-o11y.workspace = true +unc-performance-metrics.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-o11y/nightly", + "unc-primitives/nightly", +] +nightly_protocol = [ + "unc-o11y/nightly_protocol", + "unc-primitives/nightly_protocol", +] diff --git a/core/async/README.md b/core/async/README.md new file mode 100644 index 000000000..928ec2c44 --- /dev/null +++ b/core/async/README.md @@ -0,0 +1,55 @@ +# Core Async Helpers + +This crate contains helpers related to common asynchronous programming patterns +used in framework: + +* messaging: common interfaces for sending messages between components. +* test_loop: a event-loop-based test framework that can test multiple components + together in a synchronous way. + + +## Messaging + +`Sender` and `AsyncSender` are abstractions of our Actix interfaces. When +a component needs to send a message to another component, the component should +keep a `Sender` as a field and require it during construction, like: + +```rust +struct MyComponent { + downstream_component: Sender, +} + +impl MyComponent { + pub fn new(downstream_component: Sender) -> Self { ... } +} +``` + +The sender can then be used to send messages: +```rust +impl MyComponent { + fn do_something(&mut self, args: ...) { + self.downstream_component.send(DownstreamMessage::DataReady(...)); + } +} +``` + +To create a `Sender`, we need any implementation of `CanSend`. One way is +to use an Actix address: +```rust +impl Handler for DownstreamActor {...} + +impl DownstreamActor { + pub fn spawn(...) -> Addr {...} +} + +fn setup_system() { + let addr = DownstreamActor::spawn(...); + let my_component = MyComponent::new(addr.into_sender()); +} +``` + +In tests, the `TestLoopBuilder` provides the `sender()` function which also +implements `CanSend`, see the examples directory under this crate. + +`AsyncSender` is similar, except that calling `send_async` returns a future +that carries the response to the message. \ No newline at end of file diff --git a/core/async/src/actix.rs b/core/async/src/actix.rs new file mode 100644 index 000000000..89a9866fd --- /dev/null +++ b/core/async/src/actix.rs @@ -0,0 +1,86 @@ +use futures::{future::BoxFuture, FutureExt, TryFutureExt}; +use unc_o11y::{WithSpanContext, WithSpanContextExt}; + +use crate::messaging::{CanSend, CanSendAsync}; + +/// An actix Addr implements CanSend for any message type that the actor handles. +impl CanSend for actix::Addr +where + M: actix::Message + Send + 'static, + M::Result: Send, + A: actix::Actor + actix::Handler, + A::Context: actix::dev::ToEnvelope, +{ + fn send(&self, message: M) { + actix::Addr::do_send(self, message) + } +} + +/// An actix Addr implements CanSendAsync for any message type that the actor handles. +/// Here, the future output of send_async is a Result, because Actix may return an +/// error. The error is converted to (), so that the caller does not need to be aware of +/// Actix-specific error messages. +impl CanSendAsync> for actix::Addr +where + M: actix::Message + Send + 'static, + M::Result: Send, + A: actix::Actor + actix::Handler, + A::Context: actix::dev::ToEnvelope, +{ + fn send_async(&self, message: M) -> BoxFuture<'static, Result> { + self.send(message).map_err(|_| ()).boxed() + } +} + +/// Allows an actix Addr> to act as if it were an Addr, +/// by automatically wrapping any message sent with .with_span_context(). +/// +/// This way, the sender side does not need to be concerned about attaching span contexts, e.g. +/// +/// impl SomeOtherComponent { +/// pub fn new(sender: Sender) -> Self {...} +/// } +/// +/// impl actix::Handler> for SomeActor {...} +/// +/// let addr = SomeActor::spawn(...); +/// let other = SomeOtherComponent::new( +/// addr.with_auto_span_context().into_sender() // or .clone() on the addr if needed +/// ); +pub struct AddrWithAutoSpanContext { + inner: actix::Addr, +} + +/// Extension function to convert an Addr> to an AddrWithAutoSpanContext. +pub trait AddrWithAutoSpanContextExt { + fn with_auto_span_context(self) -> AddrWithAutoSpanContext; +} + +impl AddrWithAutoSpanContextExt for actix::Addr { + fn with_auto_span_context(self) -> AddrWithAutoSpanContext { + AddrWithAutoSpanContext { inner: self } + } +} + +impl CanSend for AddrWithAutoSpanContext +where + M: actix::Message + 'static, + S: actix::Actor, + actix::Addr: CanSend>, +{ + fn send(&self, message: M) { + CanSend::send(&self.inner, message.with_span_context()); + } +} + +impl CanSendAsync> for AddrWithAutoSpanContext +where + M: actix::Message + 'static, + M::Result: Send, + S: actix::Actor, + actix::Addr: CanSendAsync, Result>, +{ + fn send_async(&self, message: M) -> BoxFuture<'static, Result> { + self.inner.send_async(message.with_span_context()) + } +} diff --git a/core/async/src/examples/async_component.rs b/core/async/src/examples/async_component.rs new file mode 100644 index 000000000..0564670cc --- /dev/null +++ b/core/async/src/examples/async_component.rs @@ -0,0 +1,58 @@ +use crate::{ + futures::FutureSpawner, + messaging::{AsyncSender, Sender}, +}; + +// For this test, we have an InnerComponent which handles an InnerRequest and +// responds with InnerResponse, and an OuterComponent which handles an +// OuterRequest, spawns a future to send a request to the InnerComponent, and +// then responds back with an OuterResponse (but not as an Actix response; just +// another message). This mimics how we use Actix in framework. + +#[derive(Debug)] +pub(crate) struct InnerRequest(pub String); + +#[derive(Debug)] +pub(crate) struct InnerResponse(pub String); + +#[derive(Debug)] +pub(crate) struct OuterRequest(pub String); + +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct OuterResponse(pub String); + +pub(crate) struct InnerComponent; + +impl InnerComponent { + pub fn process_request(&mut self, request: InnerRequest) -> InnerResponse { + InnerResponse(request.0 + "!") + } +} + +pub(crate) struct OuterComponent { + inner_sender: AsyncSender, + outer_response_sender: Sender, +} + +impl OuterComponent { + pub fn new( + inner_sender: AsyncSender, + outer_response_sender: Sender, + ) -> Self { + Self { inner_sender, outer_response_sender } + } + + pub fn process_request(&mut self, request: OuterRequest, future_spawner: &dyn FutureSpawner) { + let inner_request = InnerRequest(request.0); + let sender = self.inner_sender.clone(); + let response_sender = self.outer_response_sender.clone(); + + // We're mimicing how we use Actix, and in an Actix handler context we don't have access + // to async/await. So we use a FutureSpawner to do that. + future_spawner.spawn("inner request", async move { + let inner_response = sender.send_async(inner_request).await; + let response = OuterResponse(inner_response.0.repeat(2)); + response_sender.send(response); + }); + } +} diff --git a/core/async/src/examples/async_component_test.rs b/core/async/src/examples/async_component_test.rs new file mode 100644 index 000000000..f4996509d --- /dev/null +++ b/core/async/src/examples/async_component_test.rs @@ -0,0 +1,75 @@ +use derive_enum_from_into::{EnumFrom, EnumTryInto}; +use std::sync::Arc; + +use crate::{ + messaging::{CanSend, IntoAsyncSender, IntoSender}, + test_loop::{ + event_handler::{capture_events, LoopEventHandler}, + futures::{drive_futures, MessageExpectingResponse, TestLoopFutureSpawner, TestLoopTask}, + TestLoopBuilder, + }, +}; + +use super::async_component::{ + InnerComponent, InnerRequest, InnerResponse, OuterComponent, OuterRequest, OuterResponse, +}; + +#[derive(derive_more::AsMut, derive_more::AsRef)] +struct TestData { + dummy: (), // needed for any handlers that don't require data + output: Vec, // needed for capture_events handler + inner_component: InnerComponent, + outer_component: OuterComponent, +} + +#[derive(Debug, EnumTryInto, EnumFrom)] +enum TestEvent { + OuterResponse(OuterResponse), + OuterRequest(OuterRequest), + // Requests that need responses need to use MessageExpectingResponse. + InnerRequest(MessageExpectingResponse), + // Arc is needed to support futures. + Task(Arc), +} + +fn outer_request_handler( + future_spawner: TestLoopFutureSpawner, +) -> LoopEventHandler { + LoopEventHandler::new_simple(move |event, data: &mut OuterComponent| { + data.process_request(event, &future_spawner); + }) +} + +fn inner_request_handler( +) -> LoopEventHandler> { + LoopEventHandler::new_simple( + |event: MessageExpectingResponse, + data: &mut InnerComponent| { + (event.responder)(data.process_request(event.message)); + }, + ) +} + +#[test] +fn test_async_component() { + let builder = TestLoopBuilder::::new(); + let sender = builder.sender(); + let future_spawner = builder.future_spawner(); + let mut test = builder.build(TestData { + dummy: (), + output: vec![], + inner_component: InnerComponent, + outer_component: OuterComponent::new( + sender.clone().into_async_sender(), + sender.clone().into_sender(), + ), + }); + test.register_handler(drive_futures().widen()); + test.register_handler(capture_events::().widen()); + test.register_handler(outer_request_handler(future_spawner).widen()); + test.register_handler(inner_request_handler().widen()); + + sender.send(OuterRequest("hello".to_string())); + test.run_instant(); + assert_eq!(test.data.output, vec![OuterResponse("hello!hello!".to_string())]); +} diff --git a/core/async/src/examples/mod.rs b/core/async/src/examples/mod.rs new file mode 100644 index 000000000..e48d526ab --- /dev/null +++ b/core/async/src/examples/mod.rs @@ -0,0 +1,7 @@ +mod async_component; +mod async_component_test; +mod multi_instance_test; +mod sum_numbers; +mod sum_numbers_test; +mod timed_component; +mod timed_component_test; diff --git a/core/async/src/examples/multi_instance_test.rs b/core/async/src/examples/multi_instance_test.rs new file mode 100644 index 000000000..c345bd12d --- /dev/null +++ b/core/async/src/examples/multi_instance_test.rs @@ -0,0 +1,91 @@ +use crate::time; +use derive_enum_from_into::{EnumFrom, EnumTryInto}; + +use crate::{ + examples::sum_numbers_test::forward_sum_request, + messaging::{CanSend, IntoSender}, + test_loop::{ + event_handler::{capture_events, LoopEventHandler}, + TestLoopBuilder, + }, +}; + +use super::sum_numbers::{ReportSumMsg, SumNumbersComponent, SumRequest}; + +#[derive(derive_more::AsMut, derive_more::AsRef)] +struct TestData { + summer: SumNumbersComponent, + sums: Vec, +} + +#[derive(Debug, EnumTryInto, EnumFrom)] +enum TestEvent { + RemoteRequest(i64), + LocalRequest(SumRequest), + Sum(ReportSumMsg), +} + +/// Let's pretend that when we send a remote request, the number gets sent to +/// every other instance in the setup as a local request. +fn forward_remote_request_to_other_instances() -> LoopEventHandler, (usize, TestEvent)> +{ + LoopEventHandler::new(|event: (usize, TestEvent), data: &mut Vec, context| { + if let TestEvent::RemoteRequest(number) = event.1 { + for i in 0..data.len() { + if i != event.0 { + context.sender.send((i, TestEvent::LocalRequest(SumRequest::Number(number)))) + } + } + Ok(()) + } else { + Err(event) + } + }) +} + +#[test] +fn test_multi_instance() { + let builder = TestLoopBuilder::<(usize, TestEvent)>::new(); + // Build the SumNumberComponents so that it sends messages back to the test loop. + let mut data = vec![]; + for i in 0..5 { + data.push(TestData { + // Multi-instance sender can be converted to a single-instance sender + // so we can pass it into a component's constructor. + summer: SumNumbersComponent::new(builder.sender().for_index(i).into_sender()), + sums: vec![], + }); + } + let sender = builder.sender(); + let mut test = builder.build(data); + test.register_handler(forward_remote_request_to_other_instances()); + for i in 0..5 { + // Single-instance handlers can be reused for multi-instance tests. + test.register_handler(forward_sum_request().widen().for_index(i)); + test.register_handler(capture_events::().widen().for_index(i)); + } + + // Send a RemoteRequest from each instance. + sender.send((0, TestEvent::RemoteRequest(1))); + sender.send((1, TestEvent::RemoteRequest(2))); + sender.send((2, TestEvent::RemoteRequest(3))); + sender.send((3, TestEvent::RemoteRequest(4))); + sender.send((4, TestEvent::RemoteRequest(5))); + + // Then send a GetSum request for each instance; we use a delay so that we can ensure + // these messages arrive later. (In a real test we wouldn't do this - the component would + // automatically emit some events and we would assert on these events. But for this + // contrived test we'll do it manually as a demonstration.) + for i in 0..5 { + sender.send_with_delay( + (i, TestEvent::LocalRequest(SumRequest::GetSum)), + time::Duration::milliseconds(1), + ); + } + test.run_for(time::Duration::milliseconds(2)); + assert_eq!(test.data[0].sums, vec![ReportSumMsg(14)]); + assert_eq!(test.data[1].sums, vec![ReportSumMsg(13)]); + assert_eq!(test.data[2].sums, vec![ReportSumMsg(12)]); + assert_eq!(test.data[3].sums, vec![ReportSumMsg(11)]); + assert_eq!(test.data[4].sums, vec![ReportSumMsg(10)]); +} diff --git a/core/async/src/examples/sum_numbers.rs b/core/async/src/examples/sum_numbers.rs new file mode 100644 index 000000000..0316236df --- /dev/null +++ b/core/async/src/examples/sum_numbers.rs @@ -0,0 +1,36 @@ +use crate::messaging::Sender; + +#[derive(Debug, PartialEq, Eq)] +pub struct ReportSumMsg(pub i64); + +#[derive(Debug)] +pub enum SumRequest { + Number(i64), + GetSum, +} + +// Mimics a typical backing component of some actor in framework. Handles request +// messages, and sends some other messages to another actor. The other actor is +// abstracted with an Sender here. We'll show how to test this in +// sum_numbers_test.rs. +pub struct SumNumbersComponent { + result_sender: Sender, + numbers: Vec, +} + +impl SumNumbersComponent { + pub fn new(result_sender: Sender) -> Self { + Self { result_sender, numbers: vec![] } + } + + pub fn handle(&mut self, msg: SumRequest) { + match msg { + SumRequest::Number(n) => self.numbers.push(n), + SumRequest::GetSum => { + let sum = self.numbers.iter().sum(); + self.numbers.clear(); + self.result_sender.send(ReportSumMsg(sum)); + } + } + } +} diff --git a/core/async/src/examples/sum_numbers_test.rs b/core/async/src/examples/sum_numbers_test.rs new file mode 100644 index 000000000..b01eca9e3 --- /dev/null +++ b/core/async/src/examples/sum_numbers_test.rs @@ -0,0 +1,109 @@ +use crate::time; +use derive_enum_from_into::{EnumFrom, EnumTryInto}; + +use crate::{ + messaging::{CanSend, IntoSender}, + test_loop::{ + adhoc::{handle_adhoc_events, AdhocEvent, AdhocEventSender}, + event_handler::{capture_events, LoopEventHandler}, + TestLoopBuilder, + }, +}; + +use super::sum_numbers::{ReportSumMsg, SumNumbersComponent, SumRequest}; + +#[derive(derive_more::AsMut, derive_more::AsRef)] +struct TestData { + summer: SumNumbersComponent, + sums: Vec, +} + +#[derive(Debug, EnumTryInto, EnumFrom)] +enum TestEvent { + Request(SumRequest), + Sum(ReportSumMsg), +} + +// Handler that forwards SumRequest messages to the SumNumberComponent. +// Note that typically we would have a single handler like this, and it can +// be reused for any test that needs to send messages to this component. +pub fn forward_sum_request() -> LoopEventHandler { + LoopEventHandler::new_simple(|event, data: &mut SumNumbersComponent| { + data.handle(event); + }) +} + +#[test] +fn test_simple() { + let builder = TestLoopBuilder::::new(); + // Build the SumNumberComponents so that it sends messages back to the test loop. + let data = + TestData { summer: SumNumbersComponent::new(builder.sender().into_sender()), sums: vec![] }; + let mut test = builder.build(data); + test.register_handler(forward_sum_request().widen()); + test.register_handler(capture_events::().widen()); + + test.sender().send(SumRequest::Number(1)); + test.sender().send(SumRequest::Number(2)); + test.sender().send(SumRequest::GetSum); + test.sender().send(SumRequest::Number(3)); + test.sender().send(SumRequest::Number(4)); + test.sender().send(SumRequest::Number(5)); + test.sender().send(SumRequest::GetSum); + + test.run_for(time::Duration::milliseconds(1)); + assert_eq!(test.data.sums, vec![ReportSumMsg(3), ReportSumMsg(12)]); +} + +#[derive(Debug, EnumTryInto, EnumFrom)] +enum TestEventWithAdhoc { + Request(SumRequest), + Sum(ReportSumMsg), + Adhoc(AdhocEvent), +} + +#[test] +fn test_simple_with_adhoc() { + let builder = TestLoopBuilder::::new(); + // Build the SumNumberComponents so that it sends messages back to the test loop. + let data = + TestData { summer: SumNumbersComponent::new(builder.sender().into_sender()), sums: vec![] }; + let mut test = builder.build(data); + test.register_handler(forward_sum_request().widen()); + test.register_handler(capture_events::().widen()); + test.register_handler(handle_adhoc_events()); + + // It is preferrable to put as much setup logic as possible into an adhoc + // event (queued by .run below), so that as much logic as possible is + // executed in the TestLoop context. This allows the setup logic to show + // up in the visualizer too, with any of its logging shown under the + // adhoc event. + let sender = test.sender(); + test.sender().send_adhoc_event("initial events", move |_| { + sender.send(SumRequest::Number(1)); + sender.send(SumRequest::Number(2)); + sender.send(SumRequest::GetSum); + sender.send(SumRequest::Number(3)); + sender.send(SumRequest::Number(4)); + sender.send(SumRequest::Number(5)); + sender.send(SumRequest::GetSum); + }); + + test.run_instant(); + + // We can put assertions inside an adhoc event as well. This is + // especially useful if we had a multi-instance test, so that in the + // visualizer we can easily see which assertion was problematic. + // + // Here, we queue these events after the first test.run call, so we + // need to remember to call test.run again to actually execute them. + // Alternatively we can use test.sender().schedule_adhoc_event to queue + // the assertion events after the logic we expect to execute has been + // executed; that way we only need to call test.run once. Either way, + // don't worry if you forget to call test.run again; the test will + // panic at the end if there are unhandled events. + test.sender().send_adhoc_event("assertions", |data| { + assert_eq!(data.sums, vec![ReportSumMsg(3), ReportSumMsg(12)]); + }); + test.run_instant(); +} diff --git a/core/async/src/examples/timed_component.rs b/core/async/src/examples/timed_component.rs new file mode 100644 index 000000000..373a486af --- /dev/null +++ b/core/async/src/examples/timed_component.rs @@ -0,0 +1,28 @@ +use crate::messaging::Sender; + +pub(crate) struct TimedComponent { + buffered_messages: Vec, + message_sender: Sender>, +} + +/// Mimics a component that has a specific function that is supposed to be +/// triggered by a timer. +impl TimedComponent { + pub fn new(message_sender: Sender>) -> Self { + Self { buffered_messages: vec![], message_sender } + } + + pub fn send_message(&mut self, msg: String) { + self.buffered_messages.push(msg); + } + + /// This is supposed to be triggered by a timer so it flushes the + /// messages every tick. + pub fn flush(&mut self) { + if self.buffered_messages.is_empty() { + return; + } + self.message_sender.send(self.buffered_messages.clone()); + self.buffered_messages.clear(); + } +} diff --git a/core/async/src/examples/timed_component_test.rs b/core/async/src/examples/timed_component_test.rs new file mode 100644 index 000000000..4d6c677a7 --- /dev/null +++ b/core/async/src/examples/timed_component_test.rs @@ -0,0 +1,63 @@ +use crate::time; +use derive_enum_from_into::{EnumFrom, EnumTryInto}; + +use crate::{ + messaging::IntoSender, + test_loop::event_handler::{capture_events, interval, LoopEventHandler}, +}; + +use super::timed_component::TimedComponent; + +#[derive(Debug, Clone, PartialEq)] +struct Flush; + +#[derive(Debug, EnumTryInto, EnumFrom)] +enum TestEvent { + SendMessage(String), + Flush(Flush), + MessageSent(Vec), +} + +#[derive(derive_more::AsMut, derive_more::AsRef)] +struct TestData { + component: TimedComponent, + messages_sent: Vec>, +} + +fn forward_send_message() -> LoopEventHandler { + LoopEventHandler::new_simple(|event, data: &mut TimedComponent| { + data.send_message(event); + }) +} + +#[test] +fn test_timed_component() { + let builder = crate::test_loop::TestLoopBuilder::::new(); + let data = TestData { + component: TimedComponent::new(builder.sender().into_sender()), + messages_sent: vec![], + }; + let sender = builder.sender(); + let mut test = builder.build(data); + test.register_handler(forward_send_message().widen()); + test.register_handler( + interval(time::Duration::milliseconds(100), Flush, |data: &mut TimedComponent| { + data.flush() + }) + .widen(), + ); + test.register_handler(capture_events::>().widen()); + + sender.send_with_delay("Hello".to_string().into(), time::Duration::milliseconds(10)); + sender.send_with_delay("World".to_string().into(), time::Duration::milliseconds(20)); + // The timer fires at 100ms here and flushes "Hello" and "World". + sender.send_with_delay("!".to_string().into(), time::Duration::milliseconds(110)); + // The timer fires again at 200ms here and flushes "!"". + // Further timer events do not send messages. + + test.run_for(time::Duration::seconds(1)); + assert_eq!( + test.data.messages_sent, + vec![vec!["Hello".to_string(), "World".to_string()], vec!["!".to_string()]] + ); +} diff --git a/core/async/src/futures.rs b/core/async/src/futures.rs new file mode 100644 index 000000000..a0c9a3cfe --- /dev/null +++ b/core/async/src/futures.rs @@ -0,0 +1,33 @@ +use futures::{future::BoxFuture, FutureExt}; + +/// Abstraction for something that can drive futures. +/// +/// Rust futures don't run by itself. It needs a driver to execute it. This can +/// for example be a thread, a thread pool, or Actix. This trait abstracts over +/// the execution mechanism. +/// +/// The reason why we need an abstraction is (1) we can intercept the future +/// spawning to add additional instrumentation (2) we can support driving the +/// future with TestLoop for testing. +pub trait FutureSpawner { + fn spawn_boxed(&self, description: &'static str, f: BoxFuture<'static, ()>); +} + +impl<'a> dyn FutureSpawner + 'a { + /// Spawns a future by automatically boxing it. + pub fn spawn(&self, description: &'static str, f: F) + where + F: futures::Future + Send + 'static, + { + self.spawn_boxed(description, f.boxed()) + } +} + +/// A FutureSpawner that hands over the future to Actix. +pub struct ActixFutureSpawner; + +impl FutureSpawner for ActixFutureSpawner { + fn spawn_boxed(&self, description: &'static str, f: BoxFuture<'static, ()>) { + unc_performance_metrics::actix::spawn(description, f); + } +} diff --git a/core/async/src/lib.rs b/core/async/src/lib.rs new file mode 100644 index 000000000..b2e73d769 --- /dev/null +++ b/core/async/src/lib.rs @@ -0,0 +1,7 @@ +pub mod actix; +#[cfg(test)] +mod examples; +pub mod futures; +pub mod messaging; +pub mod test_loop; +pub mod time; diff --git a/core/async/src/messaging.rs b/core/async/src/messaging.rs new file mode 100644 index 000000000..b4ad06aa1 --- /dev/null +++ b/core/async/src/messaging.rs @@ -0,0 +1,180 @@ +use futures::future::BoxFuture; +use once_cell::sync::OnceCell; +use std::sync::Arc; + +/// Trait for sending a typed message. +pub trait CanSend: Send + Sync + 'static { + fn send(&self, message: M); +} + +/// Wraps a CanSend. This should be used to pass around an Arc>, instead +/// of spelling out that type. Using a wrapper struct allows us to define more flexible +/// APIs. +pub struct Sender { + sender: Arc>, +} + +impl Clone for Sender { + fn clone(&self) -> Self { + Self { sender: self.sender.clone() } + } +} + +/// Extension functions to wrap a CanSend as a Sender. +pub trait IntoSender { + /// This allows conversion of an owned CanSend into a Sender. + fn into_sender(self) -> Sender; + /// This allows conversion of a reference-counted CanSend into a Sender. + fn as_sender(self: &Arc) -> Sender; +} + +impl> IntoSender for T { + fn into_sender(self) -> Sender { + Sender::from_impl(self) + } + fn as_sender(self: &Arc) -> Sender { + Sender::from_arc(self.clone()) + } +} + +impl Sender { + pub fn send(&self, message: M) { + self.sender.send(message) + } + + fn from_impl(sender: impl CanSend + 'static) -> Self { + Self { sender: Arc::new(sender) } + } + + fn from_arc + 'static>(arc: Arc) -> Self { + Self { sender: arc } + } + + /// Creates a no-op sender that does nothing with the message. + pub fn noop() -> Self { + Self::from_impl(Noop) + } +} + +/// Allows the sending of a message while expecting a response. +pub trait CanSendAsync: Send + Sync + 'static { + fn send_async(&self, message: M) -> BoxFuture<'static, R>; +} + +pub struct AsyncSender { + sender: Arc>, +} + +impl Clone for AsyncSender { + fn clone(&self) -> Self { + Self { sender: self.sender.clone() } + } +} + +/// Extension functions to wrap a CanSendAsync as an AsyncSender. +pub trait IntoAsyncSender { + /// This allows conversion of an owned CanSendAsync into an AsyncSender. + fn into_async_sender(self) -> AsyncSender; + /// This allows conversion of a reference-counted CanSendAsync into an AsyncSender. + fn as_async_sender(self: &Arc) -> AsyncSender; +} + +impl> IntoAsyncSender for T { + fn into_async_sender(self) -> AsyncSender { + AsyncSender::from_impl(self) + } + fn as_async_sender(self: &Arc) -> AsyncSender { + AsyncSender::from_arc(self.clone()) + } +} + +impl AsyncSender { + pub fn send_async(&self, message: M) -> BoxFuture<'static, R> { + self.sender.send_async(message) + } + + fn from_impl(sender: impl CanSendAsync + 'static) -> Self { + Self { sender: Arc::new(sender) } + } + + fn from_arc + 'static>(arc: Arc) -> Self { + Self { sender: arc } + } +} + +/// Sometimes we want to be able to pass in a sender that has not yet been fully constructed. +/// LateBoundSender can act as a placeholder to pass CanSend and CanSendAsync capabilities +/// through to the inner object. bind() should be called when the inner object is ready. +/// All calls to send() and send_async() through this wrapper will block until bind() is called. +/// +/// This struct is intended to be wrapped with an Arc, e.g. +/// let late_bound = Arc::new(LateBoundSender::default()); +/// let something_else = SomethingElse::new(late_bound.as_sender()); +/// let implementation = Implementation::new(something_else); +/// late_bound.bind(implementation); +pub struct LateBoundSender { + sender: OnceCell, +} + +impl Default for LateBoundSender { + fn default() -> Self { + Self { sender: OnceCell::default() } + } +} + +impl LateBoundSender { + pub fn bind(&self, sender: S) { + self.sender.set(sender).map_err(|_| ()).expect("cannot set sender twice"); + } +} + +/// Allows LateBoundSender to be convertible to a Sender as long as the inner object could be. +impl> CanSend for LateBoundSender { + fn send(&self, message: M) { + self.sender.wait().send(message); + } +} + +/// Allows LateBoundSender to be convertible to an AsyncSender as long as the inner object could +/// be. +impl> CanSendAsync for LateBoundSender { + fn send_async(&self, message: M) -> BoxFuture<'static, R> { + self.sender.wait().send_async(message) + } +} + +struct Noop; + +impl CanSend for Noop { + fn send(&self, _message: M) {} +} + +/// Anything that contains a Sender also implements CanSend. This is useful for implementing +/// APIs that require multiple sender interfaces, so that the multi-sender API can be used +/// to send individual message types directly. +/// +/// For example: +/// +/// #[derive(Clone, derive_more::AsRef)] +/// pub struct MyAPI { +/// client: Sender, +/// network: AsyncSender, +/// } +/// +/// fn something(api: &MyAPI) { +/// // There's no need to do api.client.send() or api.network.send_async() here. +/// api.send(ClientMessage::Something); +/// api.send_async(NetworkMessage::Something).then(...); +/// } +impl> + Send + Sync + 'static, M: 'static> CanSend for A { + fn send(&self, message: M) { + self.as_ref().send(message) + } +} +impl> + Send + Sync + 'static, M: 'static, R: 'static> CanSendAsync + for A +{ + fn send_async(&self, message: M) -> BoxFuture<'static, R> { + self.as_ref().send_async(message) + } +} diff --git a/core/async/src/test_loop.rs b/core/async/src/test_loop.rs new file mode 100644 index 000000000..2de3450b1 --- /dev/null +++ b/core/async/src/test_loop.rs @@ -0,0 +1,346 @@ +//! This is a framework to test async code in a way that is versatile, deterministic, +//! easy-to-setup, and easy-to-debug. +//! +//! The primary concept here is an event loop that the test framework controls. The +//! event loop acts as a central hub for all messages, including Actix messages, +//! network messages, timers, etc. Business logic is only executed as a response to +//! such events. +//! +//! This brings several major benefits: +//! - Ease of setup: +//! - There is no need to set up mock objects that implement some +//! message sender interface, instead, the test loop provides a sender object that +//! can be used to send messages to the event loop. For example, suppose we were +//! to make a Client whose constructor requires a network adapter; instead of having +//! to make a mock for the network adapter, we can simply pass in `loop.sender()`. +//! - Compared to writing synchronous tests, there is no need to manually deliver +//! network messages or handle actix messages at certain points of the test. Instead, +//! the event loop will invoke the appropriate event handlers whenever there is any +//! event remaining in the event loop. This ensures that no messages are ever missed. +//! - Test setup code can be modular and reusable, because the test specification +//! consists entirely of registering the desired event handlers. Rather than passing +//! a giant callback into a giant setup(...) function to customize one part of a huge +//! integration test, we can flexibly compose specific event handlers. For example, +//! we may add an event handler to route all ShardsManager-related network messages +//! reliably, and at the same time another event handler to drop 50% of Block network +//! messages. Also, we can use an event handler as long as it is relevant for a test +//! (i.e. a ForwardShardsManagerRequest event handler can be used as long as the test +//! involves ShardsManagers), regardless of the exact architecture of the test. +//! See `LoopEventHandler` for more details. +//! +//! - Debuggability: +//! - Because ALL execution is in response of events, the whole test can be cleanly +//! segmented into the response to each event. The framework automatically outputs +//! a log message at the beginning of each event execution, so that the log output +//! can be loaded into a visualizer to show the exact sequence of events, their +//! relationship, the exact contents of the event messages, and the log output +//! during the handling of each event. This is especially useful when debugging +//! multi-instance tests. +//! +//! - Determinism: +//! - Many tests, especially those that involve multiple instances, are most easily +//! written by spawning actual actors and threads. This however makes the tests +//! inherently asynchronous and may be more flaky. +//! - The test loop framework also provides a synchronous and deterministic way to +//! invoke timers without waiting for the actual duration. This makes tests run +//! much faster than asynchronous tests. +//! +//! - Versatilty: +//! - A test can be constructed with any combination of components. The framework does +//! not dictate what components should exist, or how many instances there should be. +//! This allows for both small and focused tests, and large multi-instance tests. +//! - Timed tests can be written to check the theoretical performance of certain tasks, +//! such as distributing chunks to other nodes within X milliseconds provided that +//! network messages have a 10ms delay. +//! - The framework does not require major migrations to existing code, e.g. it is +//! compatible with the Actix framework (and possibly futures in the future). +//! +//! A note on the order of execution of the events: all events that are due at the same +//! timestamp are executed in FIFO order. For example, if the events are emitted in the +//! following order: (A due 100ms), (B due 0ms), (C due 200ms), (D due 0ms), (E due 100ms) +//! then the actual order of execution is B, D, A, E, C. +pub mod adhoc; +pub mod delay_sender; +pub mod event_handler; +pub mod futures; +pub mod multi_instance; + +use self::{ + delay_sender::DelaySender, + event_handler::LoopEventHandler, + futures::{TestLoopFutureSpawner, TestLoopTask}, +}; +use crate::test_loop::event_handler::LoopHandlerContext; +use crate::time; +use unc_o11y::{testonly::init_test_logger, tracing::log::info}; +use serde::Serialize; +use std::{ + collections::BinaryHeap, + fmt::Debug, + sync::{self, Arc}, +}; + +/// Main struct for the Test Loop framework. +/// The `Data` type should contain all the business logic state that is relevant +/// to the test. The `Event` type should contain all the possible events that +/// are sent to the event loop. +/// +/// The convention is that, for single-instance tests, +/// - `Data` should be a struct with a derive_more::AsMut and derive_more::AsRef +/// (so that `Data` implements AsMut and AsRef for each of its +/// fields.) +/// - `Event` should be an enum with a derive(EnumTryInto, EnumFrom), so that it +/// implements TryInto and From for each of its variants. +/// and that for multi-instance tests, `Data` is `Vec` and `Event` is +/// `(usize, SingleEvent)`. +pub struct TestLoop { + pub data: Data, + + /// The sender is used to send events to the event loop. + sender: DelaySender, + + /// The events that are yet to be handled. They are kept in a heap so that + /// events that shall execute earlier (by our own virtual clock) are popped + /// first. + events: BinaryHeap>, + /// The events that will enter the events heap upon the next iteration. + /// This is the receiving end of all the senders that we give out. + pending_events: sync::mpsc::Receiver>, + /// The next ID to assign to an event we receive. + next_event_index: usize, + /// The current virtual time. + current_time: time::Duration, + /// Fake clock that always returns the virtual time. + clock: time::FakeClock, + + /// Handlers are initialized only once, upon the first call to run(). + handlers_initialized: bool, + /// All the event handlers that are registered. We invoke them one by one + /// for each event, until one of them handles the event (or panic if no one + /// handles it). + handlers: Vec>, +} + +/// An event waiting to be executed, ordered by the due time and then by ID. +struct EventInHeap { + event: Event, + due: time::Duration, + id: usize, +} + +impl PartialEq for EventInHeap { + fn eq(&self, other: &Self) -> bool { + self.due == other.due && self.id == other.id + } +} + +impl Eq for EventInHeap {} + +impl PartialOrd for EventInHeap { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for EventInHeap { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + (self.due, self.id).cmp(&(other.due, other.id)).reverse() + } +} + +/// An event that is in-flight. The delay here is relative to the virtual time +/// when the handler that emitted this event is invoked (e.g. a network routing +/// handler may respond to an outbound message and emit an inbound message with +/// a 10ms delay). +struct EventInFlight { + event: Event, + delay: time::Duration, +} + +/// Builder that should be used to construct a `TestLoop`. The reason why the +/// builder exists is that usually the `Data` type can only be constructed using +/// the event sender provided by the test loop, so this way we can avoid a +/// construction dependency cycle. +pub struct TestLoopBuilder { + clock: time::FakeClock, + pending_events: sync::mpsc::Receiver>, + pending_events_sender: DelaySender, +} + +impl TestLoopBuilder { + pub fn new() -> Self { + // Initialize the logger to make sure the test loop printouts are visible. + init_test_logger(); + let (pending_events_sender, pending_events) = sync::mpsc::sync_channel(65536); + Self { + clock: time::FakeClock::default(), + pending_events, + pending_events_sender: DelaySender::new(move |event, delay| { + pending_events_sender.send(EventInFlight { event, delay }).unwrap(); + }), + } + } + + /// Returns a sender that can be used anywhere to send events to the loop. + pub fn sender(&self) -> DelaySender { + self.pending_events_sender.clone() + } + + /// Returns a clock that will always return the current virtual time. + pub fn clock(&self) -> time::Clock { + self.clock.clock() + } + + /// Returns a FutureSpawner that can be used to spawn futures into the loop. + pub fn future_spawner(&self) -> TestLoopFutureSpawner + where + Event: From>, + { + self.sender().narrow() + } + + pub fn build(self, data: Data) -> TestLoop { + TestLoop::new(self.pending_events, self.pending_events_sender, self.clock, data) + } +} + +/// The log output line that can be used to visualize the execution of a test. +/// It is only used to serialize into JSON. This is enough data to reconstruct +/// the event dependency graph, and to segment log messages. +#[derive(Serialize)] +struct EventStartLogOutput { + /// Index of the current event we're about to handle. + current_index: usize, + /// The total number of events we have seen of so far (i.e. + /// [previous total_events, total_events) are the events emitted by the + /// previous event's handler). + total_events: usize, + /// The Debug representation of the event payload. + current_event: String, + /// The current virtual time. + current_time_ms: u64, +} + +impl TestLoop { + fn new( + pending_events: sync::mpsc::Receiver>, + sender: DelaySender, + clock: time::FakeClock, + data: Data, + ) -> Self { + Self { + data, + sender, + events: BinaryHeap::new(), + pending_events, + next_event_index: 0, + current_time: time::Duration::ZERO, + clock, + handlers_initialized: false, + handlers: Vec::new(), + } + } + + pub fn sender(&self) -> DelaySender { + self.sender.clone() + } + + /// Registers a new event handler to the test loop. + pub fn register_handler(&mut self, handler: LoopEventHandler) { + assert!(!self.handlers_initialized, "Cannot register more handlers after run() is called"); + self.handlers.push(handler); + } + + fn maybe_initialize_handlers(&mut self) { + if self.handlers_initialized { + return; + } + for handler in &mut self.handlers { + handler.init(LoopHandlerContext { + sender: self.sender.clone(), + clock: self.clock.clock(), + }); + } + } + + /// Helper to push events we have just received into the heap. + fn queue_received_events(&mut self) { + while let Ok(event) = self.pending_events.try_recv() { + self.events.push(EventInHeap { + due: self.current_time + event.delay, + event: event.event, + id: self.next_event_index, + }); + self.next_event_index += 1; + } + } + + /// Runs the test loop for the given duration. This function may be called + /// multiple times, but further test handlers may not be registered after + /// the first call. + pub fn run_for(&mut self, duration: time::Duration) { + self.maybe_initialize_handlers(); + let deadline = self.current_time + duration; + 'outer: loop { + // Push events we have just received into the heap. + self.queue_received_events(); + // Don't execute any more events after the deadline. + match self.events.peek() { + Some(event) => { + if event.due > deadline { + break; + } + } + None => break, + } + // Find the next event, log a line about it, and execute. + let event = self.events.pop().unwrap(); + let json_printout = serde_json::to_string(&EventStartLogOutput { + current_index: event.id, + total_events: self.next_event_index, + current_event: format!("{:?}", event.event), + current_time_ms: event.due.whole_milliseconds() as u64, + }) + .unwrap(); + info!(target: "test_loop", "TEST_LOOP_EVENT_START {}", json_printout); + self.clock.advance(event.due - self.current_time); + self.current_time = event.due; + + let mut current_event = event.event; + for handler in &mut self.handlers { + if let Err(event) = handler.handle(current_event, &mut self.data) { + current_event = event; + } else { + continue 'outer; + } + } + panic!("Unhandled event: {:?}", current_event); + } + self.current_time = deadline; + } + + pub fn run_instant(&mut self) { + self.run_for(time::Duration::ZERO); + } +} + +impl Drop for TestLoop { + fn drop(&mut self) { + self.queue_received_events(); + 'outer: for event in self.events.drain() { + let mut current_event = event.event; + for handler in &mut self.handlers { + if let Err(event) = handler.try_drop(current_event) { + current_event = event; + } else { + continue 'outer; + } + } + panic!( + "Important event scheduled at {} is not handled at the end of the test: {:?}. + Consider calling `test.run()` again, or with a longer duration.", + event.due, current_event + ); + } + } +} diff --git a/core/async/src/test_loop/adhoc.rs b/core/async/src/test_loop/adhoc.rs new file mode 100644 index 000000000..f8e51e3ee --- /dev/null +++ b/core/async/src/test_loop/adhoc.rs @@ -0,0 +1,64 @@ +use super::{ + delay_sender::DelaySender, + event_handler::{LoopEventHandler, TryIntoOrSelf}, +}; +use crate::messaging::CanSend; +use crate::time; +use std::fmt::Debug; + +/// Any arbitrary logic that runs as part of the test loop. +/// +/// This is not necessary (since one can just take the data and perform +/// arbitrary logic on it), but this is good for documentation and allows +/// the logs emitted as part of this function's execution to be segmented +/// in the TestLoop visualizer. +pub struct AdhocEvent { + pub description: String, + pub handler: Box, +} + +impl Debug for AdhocEvent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.description) + } +} + +/// Allows DelaySender to be used to send or schedule adhoc events. +pub trait AdhocEventSender { + fn send_adhoc_event(&self, description: &str, f: impl FnOnce(&mut Data) + Send + 'static); + fn schedule_adhoc_event( + &self, + description: &str, + f: impl FnOnce(&mut Data) + Send + 'static, + delay: time::Duration, + ); +} + +impl> + 'static> AdhocEventSender + for DelaySender +{ + fn send_adhoc_event(&self, description: &str, f: impl FnOnce(&mut Data) + Send + 'static) { + self.send(AdhocEvent { description: description.to_string(), handler: Box::new(f) }) + } + fn schedule_adhoc_event( + &self, + description: &str, + f: impl FnOnce(&mut Data) + Send + 'static, + delay: time::Duration, + ) { + self.send_with_delay( + AdhocEvent { description: description.to_string(), handler: Box::new(f) }.into(), + delay, + ) + } +} + +/// Handler to handle adhoc events. +pub fn handle_adhoc_events>>( +) -> LoopEventHandler { + LoopEventHandler::new(|event: Event, data, _ctx| { + let event = event.try_into_or_self()?; + (event.handler)(data); + Ok(()) + }) +} diff --git a/core/async/src/test_loop/delay_sender.rs b/core/async/src/test_loop/delay_sender.rs new file mode 100644 index 000000000..cc9ec5f3b --- /dev/null +++ b/core/async/src/test_loop/delay_sender.rs @@ -0,0 +1,50 @@ +use crate::messaging; +use crate::time; +use std::sync::Arc; + +/// Interface to send an event with a delay (in virtual time). It can be +/// converted to a Sender for any message type that can be converted into +/// the event type, so that a DelaySender given by the test loop may be passed +/// to production code that expects a Sender. +pub struct DelaySender(Arc); + +impl + 'static> messaging::CanSend for DelaySender { + fn send(&self, message: Message) { + self.send_with_delay(message.into(), time::Duration::ZERO); + } +} + +impl DelaySender { + pub fn new(inner: impl Fn(Event, time::Duration) + Send + Sync + 'static) -> Self { + Self(Arc::new(inner)) + } + + pub fn send_with_delay(&self, event: Event, delay: time::Duration) { + self.0(event, delay); + } + + pub fn narrow(self) -> DelaySender + where + Event: From + 'static, + { + DelaySender::::new(move |event, delay| { + self.send_with_delay(event.into(), delay) + }) + } +} + +impl DelaySender<(usize, Event)> { + /// Converts a multi-instance sender to a single-instance sender. + pub fn for_index(self, index: usize) -> DelaySender { + DelaySender::new(move |event, delay| { + self.send_with_delay((index, event), delay); + }) + } +} + +/// Custom implementation because #derive wouldn't work if Event does not Clone. +impl Clone for DelaySender { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} diff --git a/core/async/src/test_loop/event_handler.rs b/core/async/src/test_loop/event_handler.rs new file mode 100644 index 000000000..37304d555 --- /dev/null +++ b/core/async/src/test_loop/event_handler.rs @@ -0,0 +1,221 @@ +use super::{delay_sender::DelaySender, multi_instance::IndexedLoopEventHandler}; +use crate::time; + +/// Context given to the loop handler on each call. +pub struct LoopHandlerContext { + /// The sender that can be used to send more messages to the loop. + pub sender: DelaySender, + /// The clock whose .now() returns the current virtual time maintained by + /// the test loop. + pub clock: time::Clock, +} + +/// An event handler registered on a test loop. Each event handler usually +/// handles only some events, so we will usually have multiple event handlers +/// registered to cover all event types. +pub struct LoopEventHandler { + inner: Box>, +} + +impl LoopEventHandler { + /// Creates a handler from the handling logic function. The function is + /// called on each event. It should return Ok(()) if the event was handled, + /// or Err(event) if the event was not handled (which will cause it to be + /// passed to the next handler). + pub fn new( + handler: impl FnMut(Event, &mut Data, &LoopHandlerContext) -> Result<(), Event> + 'static, + ) -> Self { + Self { + inner: Box::new(LoopEventHandlerImplByFunction { + initial_event: None, + handler: Box::new(handler), + ok_to_drop: Box::new(|_| false), + context: None, + }), + } + } + + /// Like new(), but the handler function is only given an event and data, + /// without the context, and also without the ability to reject the event. + pub fn new_simple(mut handler: impl FnMut(Event, &mut Data) + 'static) -> Self { + Self::new(move |event, data, _| { + handler(event, data); + Ok(()) + }) + } + + /// Like new(), but additionally sends an initial event with an initial + /// delay. See periodic_interval() for why this is useful. + pub fn new_with_initial_event( + initial_event: Event, + initial_delay: time::Duration, + handler: impl FnMut(Event, &mut Data, &LoopHandlerContext) -> Result<(), Event> + 'static, + ok_to_drop: impl Fn(&Event) -> bool + 'static, + ) -> Self { + Self { + inner: Box::new(LoopEventHandlerImplByFunction { + initial_event: Some((initial_event, initial_delay)), + handler: Box::new(handler), + ok_to_drop: Box::new(ok_to_drop), + context: None, + }), + } + } + + /// Adapts this handler to a handler whose data is a superset of our data + /// and whose event is a superset of our event. + /// For data, A is a superset of B if A implements AsRef and AsMut. + /// For event, A is a superset of B if A implements From and + /// TryIntoOrSelf. + pub fn widen< + OuterData: AsMut, + OuterEvent: TryIntoOrSelf + From + 'static, + >( + self, + ) -> LoopEventHandler { + LoopEventHandler { inner: Box::new(WideningEventHandler(self)) } + } + + /// Adapts this handler to a handler whose data is a vector of our data, + /// and whose event is a is the tuple (index, our event), for a specific + /// index. + pub fn for_index(self, index: usize) -> LoopEventHandler, (usize, Event)> { + LoopEventHandler { inner: Box::new(IndexedLoopEventHandler { inner: self, index }) } + } + + pub(crate) fn init(&mut self, context: LoopHandlerContext) { + self.inner.init(context) + } + + pub(crate) fn handle(&mut self, event: Event, data: &mut Data) -> Result<(), Event> { + self.inner.handle(event, data) + } + + pub(crate) fn try_drop(&self, event: Event) -> Result<(), Event> { + self.inner.try_drop(event) + } +} + +/// Internal implementation of LoopEventHandler. +pub(crate) trait LoopEventHandlerImpl { + /// init is called when the test loop runs for the first time. + fn init(&mut self, context: LoopHandlerContext); + /// handle is called when we have a pending event from the test loop. + fn handle(&mut self, event: Event, data: &mut Data) -> Result<(), Event>; + /// try_drop is called when the TestLoop is dropped, but an event + /// remains in the event queue. If this handler knows that it's OK to + /// drop the event, it should return Ok(()); otherwise it should return + /// the original event as an Err. + /// + /// This is basically used for periodic timers, as it's OK to drop timers, + /// but not OK to drop an event that forgot to be handled. + fn try_drop(&self, event: Event) -> Result<(), Event>; +} + +/// Implementation of LoopEventHandlerImpl by a closure. We cache the context +/// upon receiving the init() call, so that we can pass a reference to the +/// closure every time we receive the handle() call. +struct LoopEventHandlerImplByFunction { + initial_event: Option<(Event, time::Duration)>, + handler: Box) -> Result<(), Event>>, + ok_to_drop: Box bool>, + context: Option>, +} + +impl LoopEventHandlerImpl + for LoopEventHandlerImplByFunction +{ + fn init(&mut self, context: LoopHandlerContext) { + if let Some((event, delay)) = self.initial_event.take() { + context.sender.send_with_delay(event, delay); + } + self.context = Some(context); + } + + fn handle(&mut self, event: Event, data: &mut Data) -> Result<(), Event> { + let context = self.context.as_ref().unwrap(); + (self.handler)(event, data, context) + } + + fn try_drop(&self, event: Event) -> Result<(), Event> { + if (self.ok_to_drop)(&event) { + Ok(()) + } else { + Err(event) + } + } +} + +/// A convenient trait to TryInto, or else return the original object. It's useful +/// for implementing event handlers. +pub trait TryIntoOrSelf: Sized { + fn try_into_or_self(self) -> Result; +} + +impl> TryIntoOrSelf for T { + fn try_into_or_self(self) -> Result { + self.try_into() + } +} + +/// Implements .widen() for an event handler. +struct WideningEventHandler(LoopEventHandler); + +impl< + Data, + Event, + OuterData: AsMut, + OuterEvent: TryIntoOrSelf + From + 'static, + > LoopEventHandlerImpl for WideningEventHandler +{ + fn init(&mut self, context: LoopHandlerContext) { + self.0.init(LoopHandlerContext { sender: context.sender.narrow(), clock: context.clock }) + } + + fn handle(&mut self, event: OuterEvent, data: &mut OuterData) -> Result<(), OuterEvent> { + let mut inner_data = data.as_mut(); + let inner_event = event.try_into_or_self()?; + self.0.handle(inner_event, &mut inner_data)?; + Ok(()) + } + + fn try_drop(&self, event: OuterEvent) -> Result<(), OuterEvent> { + let inner_event = event.try_into_or_self()?; + self.0.try_drop(inner_event)?; + Ok(()) + } +} + +/// An event handler that puts the event into a vector in the Data, as long as +/// the Data contains a Vec. (Use widen() right after). +/// +/// This is used on output events so that after the test loop finishes running +/// we can assert on those events. +pub fn capture_events() -> LoopEventHandler, Event> { + LoopEventHandler::new_simple(|event, data: &mut Vec| data.push(event)) +} + +/// Periodically sends to the event loop the given event by the given interval. +/// Each time this event is handled, the given function is called. +/// The first invocation is triggered after the interval, not immediately. +pub fn interval( + interval: time::Duration, + event: Event, + func: impl Fn(&mut Data) + 'static, +) -> LoopEventHandler { + let event_cloned = event.clone(); + LoopEventHandler::new_with_initial_event( + event.clone(), + interval, + move |actual_event, data, context| { + if actual_event == event { + func(data); + context.sender.send_with_delay(actual_event, interval); + Ok(()) + } else { + Err(actual_event) + } + }, + move |actual_event| actual_event == &event_cloned, + ) +} diff --git a/core/async/src/test_loop/futures.rs b/core/async/src/test_loop/futures.rs new file mode 100644 index 000000000..914a140c8 --- /dev/null +++ b/core/async/src/test_loop/futures.rs @@ -0,0 +1,133 @@ +use crate::time; +use futures::future::BoxFuture; +use futures::task::{waker_ref, ArcWake}; +use futures::FutureExt; +use std::fmt::Debug; +use std::sync::{Arc, Mutex}; +use std::task::Context; +use tokio::sync::oneshot; + +use crate::{ + futures::FutureSpawner, + messaging::{self, CanSend}, +}; + +use super::{delay_sender::DelaySender, event_handler::LoopEventHandler}; + +// Support for futures in TestLoop. +// +// There are two key features this file provides for TestLoop: +// +// 1. A general way to spawn futures and have the TestLoop drive the futures. +// To support this, add () to the Data, add Arc as an Event, +// and add DriveFutures as a handler. Finally, pass +// DelaySender> as the &dyn FutureSpawner to any +// component that needs to spawn futures. +// +// This causes any futures spawned during the test to end up as an event +// (an Arc) in the test loop. The event will eventually be +// executed by the DriveFutures handler, which will drive the future +// until it is either suspended or completed. If suspended, then the waker +// of the future (called when the future is ready to resume) will place +// the Arc event back into the test loop to be executed +// again. +// +// 2. A way to send a message to the TestLoop and expect a response as a +// future, which will resolve whenever the TestLoop handles the message. +// To support this, use MessageExpectingResponse as the +// event type, and in the handler, call (event.responder)(result) +// (possibly asynchronously) to complete the future. +// +// This is needed to support the AsyncSender interface, which is required +// by some components as they expect a response to each message. The way +// this is implemented is by implementing a conversion from +// DelaySender> to +// AsyncSender. + +/// A message, plus a response callback. This should be used as the event type +/// when testing an Actix component that's expected to return a result. +/// +/// The response is used to complete the future that is returned by +/// our `AsyncSender::send_async` implementation. +pub struct MessageExpectingResponse { + pub message: T, + pub responder: Box, +} + +impl Debug for MessageExpectingResponse { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("MessageWithResponder").field(&self.message).finish() + } +} + +impl< + Message: 'static, + Response: Send + 'static, + Event: From> + 'static, + > messaging::CanSendAsync for DelaySender +{ + fn send_async(&self, message: Message) -> BoxFuture<'static, Response> { + let (sender, receiver) = oneshot::channel::(); + let future = async move { receiver.await.expect("Future was cancelled") }; + let responder = Box::new(move |r| sender.send(r).ok().unwrap()); + self.send_with_delay( + MessageExpectingResponse { message, responder }.into(), + time::Duration::ZERO, + ); + future.boxed() + } +} + +pub struct TestLoopTask { + future: Mutex>>, + sender: DelaySender>, + description: String, +} + +impl ArcWake for TestLoopTask { + fn wake_by_ref(arc_self: &Arc) { + let clone = arc_self.clone(); + arc_self.sender.send(clone); + } +} + +impl Debug for TestLoopTask { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Task").field(&self.description).finish() + } +} + +/// Drives any Arc events (futures spawned by our implementation +/// of FutureSpawner) that are remaining in the loop. +pub fn drive_futures() -> LoopEventHandler<(), Arc> { + LoopEventHandler::new_simple(|task: Arc, _| { + // The following is copied from the Rust async book. + // Take the future, and if it has not yet completed (is still Some), + // poll it in an attempt to complete it. + let mut future_slot = task.future.lock().unwrap(); + if let Some(mut future) = future_slot.take() { + let waker = waker_ref(&task); + let context = &mut Context::from_waker(&*waker); + if future.as_mut().poll(context).is_pending() { + // We're still not done processing the future, so put it + // back in its task to be run again in the future. + *future_slot = Some(future); + } + } + }) +} + +/// A DelaySender> is a FutureSpawner that can be used to +/// spawn futures into the test loop. We give it a convenient alias. +pub type TestLoopFutureSpawner = DelaySender>; + +impl FutureSpawner for TestLoopFutureSpawner { + fn spawn_boxed(&self, description: &str, f: BoxFuture<'static, ()>) { + let task = Arc::new(TestLoopTask { + future: Mutex::new(Some(f)), + sender: self.clone(), + description: description.to_string(), + }); + self.send(task); + } +} diff --git a/core/async/src/test_loop/multi_instance.rs b/core/async/src/test_loop/multi_instance.rs new file mode 100644 index 000000000..2a84f2b3b --- /dev/null +++ b/core/async/src/test_loop/multi_instance.rs @@ -0,0 +1,42 @@ +use super::event_handler::{LoopEventHandler, LoopEventHandlerImpl, LoopHandlerContext}; + +/// Event handler that handles a specific single instance in a multi-instance +/// setup. +/// +/// To convert a single-instance handler to a multi-instance handler +/// (for one instance), use handler.for_index(index). +pub(crate) struct IndexedLoopEventHandler { + pub(crate) inner: LoopEventHandler, + pub(crate) index: usize, +} + +impl LoopEventHandlerImpl, (usize, Event)> + for IndexedLoopEventHandler +{ + fn init(&mut self, context: LoopHandlerContext<(usize, Event)>) { + self.inner.init(LoopHandlerContext { + sender: context.sender.for_index(self.index), + clock: context.clock, + }) + } + + fn handle( + &mut self, + event: (usize, Event), + data: &mut Vec, + ) -> Result<(), (usize, Event)> { + if event.0 == self.index { + self.inner.handle(event.1, &mut data[self.index]).map_err(|event| (self.index, event)) + } else { + Err(event) + } + } + + fn try_drop(&self, event: (usize, Event)) -> Result<(), (usize, Event)> { + if event.0 == self.index { + self.inner.try_drop(event.1).map_err(|event| (self.index, event)) + } else { + Err(event) + } + } +} diff --git a/core/async/src/time.rs b/core/async/src/time.rs new file mode 100644 index 000000000..020179977 --- /dev/null +++ b/core/async/src/time.rs @@ -0,0 +1,258 @@ +//! Time module provides a non-global clock, which should be passed +//! as an argument to functions which need to read the current time. +//! In particular try to avoid storing the clock instances in the objects. +//! Functions which use system clock directly are non-hermetic, which +//! makes them effectively non-deterministic and hard to test. +//! +//! Clock provides 2 types of time reads: +//! 1. now() (aka POSIX CLOCK_MONOTONIC, aka time::Instant) +//! time as perceived by the machine making the measurement. +//! The subsequent calls to now() are guaranteed to return monotonic +//! results. It should be used for measuring the latency of operations +//! as observed by the machine. The time::Instant itself doesn't +//! translate to any specific timestamp, so it is not meaningful for +//! anyone other than the machine doing the measurement. +//! 2. now_utc() (aka POSIX CLOCK_REALTIME, aka time::Utc) +//! expected to approximate the (global) UTC time. +//! There is NO guarantee that the subsequent reads will be monotonic, +//! as CLOCK_REALTIME it configurable in the OS settings, or can be updated +//! during NTP sync. Should be used whenever you need to communicate a timestamp +//! over the network, or store it for later use. Remember that clocks +//! of different machines are not perfectly synchronized, and in extreme +//! cases can be totally skewed. +use once_cell::sync::Lazy; +use std::sync::{Arc, Mutex}; +pub use time::error; +use tokio::sync::watch; + +// TODO: consider wrapping these types to prevent interactions +// with other time libraries, especially to prevent the direct access +// to the realtime (i.e. not through the Clock). +pub type Instant = time::Instant; +// TODO: OffsetDateTime stores the timestamp in a decomposed form of +// (year,month,day,hour,...). If we find it inefficient, we should +// probably migrate to a pure UNIX timestamp and convert is to datetime +// only when needed. +pub type Utc = time::OffsetDateTime; +pub type Duration = time::Duration; + +// Instant doesn't have a deterministic contructor, +// however since Instant is not convertible to an unix timestamp, +// we can snapshot Instant::now() once and treat it as a constant. +// All observable effects will be then deterministic. +static FAKE_CLOCK_MONO_START: Lazy = Lazy::new(Instant::now); + +// An arbitrary non-trivial deterministic Utc timestamp. +const FAKE_CLOCK_UTC_START: Lazy = Lazy::new(|| Utc::from_unix_timestamp(89108233).unwrap()); + +// By the definition of derive(PartialEq), Finite(...) < Infinite. +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +pub enum Deadline { + Finite(Instant), + Infinite, +} + +impl From for Deadline { + fn from(t: Instant) -> Deadline { + Deadline::Finite(t) + } +} + +#[derive(Clone)] +enum ClockInner { + Real, + Fake(FakeClock), +} + +/// Clock encapsulates a system clock, allowing to replace it +/// with a fake in tests. +/// Since system clock is a source of external information, +/// it has to be replaced with a fake double, if we want our +/// tests to be deterministic. +/// +/// TODO: add tests. +#[derive(Clone)] +pub struct Clock(ClockInner); + +impl Clock { + /// Constructor of the real clock. Use it in production code. + /// Preferrably construct it directly in the main() function, + /// so that it can be faked out in every other function. + pub fn real() -> Clock { + Clock(ClockInner::Real) + } + + /// Current time according to the monotone clock. + pub fn now(&self) -> Instant { + match &self.0 { + ClockInner::Real => Instant::now(), + ClockInner::Fake(fake) => fake.now(), + } + } + + /// Current time according to the system/walltime clock. + pub fn now_utc(&self) -> Utc { + match &self.0 { + ClockInner::Real => Utc::now_utc(), + ClockInner::Fake(fake) => fake.now_utc(), + } + } + + /// Cancellable. + pub async fn sleep_until_deadline(&self, t: Deadline) { + match t { + Deadline::Infinite => std::future::pending().await, + Deadline::Finite(t) => self.sleep_until(t).await, + } + } + + /// Cancellable. + pub async fn sleep_until(&self, t: Instant) { + match &self.0 { + ClockInner::Real => tokio::time::sleep_until(t.into_inner().into()).await, + ClockInner::Fake(fake) => fake.sleep_until(t).await, + } + } + + /// Cancellable. + pub async fn sleep(&self, d: Duration) { + match &self.0 { + ClockInner::Real => tokio::time::sleep(d.try_into().unwrap()).await, + ClockInner::Fake(fake) => fake.sleep(d).await, + } + } +} + +struct FakeClockInner { + /// `mono` keeps the current time of the monotonic clock. + /// It is wrapped in watch::Sender, so that the value can + /// be observed from the clock::sleep() futures. + mono: watch::Sender, + utc: Utc, + /// We need to keep it so that mono.send() always succeeds. + _mono_recv: watch::Receiver, +} + +impl FakeClockInner { + pub fn new(utc: Utc) -> Self { + let (mono, _mono_recv) = watch::channel(*FAKE_CLOCK_MONO_START); + Self { utc, mono, _mono_recv } + } + + pub fn now(&mut self) -> Instant { + *self.mono.borrow() + } + pub fn now_utc(&mut self) -> Utc { + self.utc + } + pub fn advance(&mut self, d: Duration) { + assert!(d >= Duration::ZERO); + if d == Duration::ZERO { + return; + } + let now = *self.mono.borrow(); + self.mono.send(now + d).unwrap(); + self.utc += d; + } + pub fn advance_until(&mut self, t: Instant) { + let now = *self.mono.borrow(); + if t <= now { + return; + } + self.mono.send(t).unwrap(); + self.utc += t - now; + } +} + +/// TEST-ONLY +#[derive(Clone)] +pub struct FakeClock(Arc>); + +impl FakeClock { + /// Constructor of a fake clock. Use it in tests. + /// It supports manually moving time forward (via advance()). + /// You can also arbitrarly set the UTC time in runtime. + /// Use FakeClock::clock() when calling prod code from tests. + pub fn new(utc: Utc) -> Self { + Self(Arc::new(Mutex::new(FakeClockInner::new(utc)))) + } + pub fn now(&self) -> Instant { + self.0.lock().unwrap().now() + } + + pub fn now_utc(&self) -> Utc { + self.0.lock().unwrap().now_utc() + } + pub fn advance(&self, d: Duration) { + self.0.lock().unwrap().advance(d); + } + pub fn advance_until(&self, t: Instant) { + self.0.lock().unwrap().advance_until(t); + } + pub fn clock(&self) -> Clock { + Clock(ClockInner::Fake(self.clone())) + } + pub fn set_utc(&self, utc: Utc) { + self.0.lock().unwrap().utc = utc; + } + + /// Cancel-safe. + pub async fn sleep(&self, d: Duration) { + let mut watch = self.0.lock().unwrap().mono.subscribe(); + let t = *watch.borrow() + d; + while *watch.borrow() < t { + watch.changed().await.unwrap(); + } + } + + /// Cancel-safe. + pub async fn sleep_until(&self, t: Instant) { + let mut watch = self.0.lock().unwrap().mono.subscribe(); + while *watch.borrow() < t { + watch.changed().await.unwrap(); + } + } +} + +impl Default for FakeClock { + fn default() -> FakeClock { + Self::new(*FAKE_CLOCK_UTC_START) + } +} + +/// Interval equivalent to tokio::time::Interval with +/// MissedTickBehavior::Skip. +pub struct Interval { + next: time::Instant, + period: time::Duration, +} + +impl Interval { + pub fn new(next: time::Instant, period: time::Duration) -> Self { + Self { next, period } + } + + /// Cancel-safe. + pub async fn tick(&mut self, clock: &Clock) { + clock.sleep_until(self.next).await; + let now = clock.now(); + // Implementation of `tokio::time::MissedTickBehavior::Skip`. + // Please refer to https://docs.rs/tokio/latest/tokio/time/enum.MissedTickBehavior.html# + // for details. In essence, if more than `period` of time passes between consecutive + // calls to tick, then the second tick completes immediately and the next one will be + // aligned to the original schedule. + self.next = now + self.period + - Duration::nanoseconds( + ((now - self.next).whole_nanoseconds() % self.period.whole_nanoseconds()) + .try_into() + // This operation is practically guaranteed not to + // fail, as in order for it to fail, `period` would + // have to be longer than `now - timeout`, and both + // would have to be longer than 584 years. + // + // If it did fail, there's not a good way to pass + // the error along to the user, so we just panic. + .expect("too much time has elapsed since the interval was supposed to tick"), + ); + } +} diff --git a/core/chain-configs/Cargo.toml b/core/chain-configs/Cargo.toml new file mode 100644 index 000000000..6d1d6e92d --- /dev/null +++ b/core/chain-configs/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "unc-chain-configs" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "This crate provides typed interfaces to the NEAR Genesis and Chain Configs" +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +bytesize.workspace = true +chrono.workspace = true +derive_more.workspace = true +num-rational.workspace = true +once_cell.workspace = true +serde.workspace = true +serde_json.workspace = true +sha2.workspace = true +smart-default.workspace = true +tracing.workspace = true + +unc-crypto.workspace = true +unc-o11y = { workspace = true, optional = true } +unc-parameters.workspace = true +unc-primitives.workspace = true +unc-config-utils.workspace = true + +[features] +nightly_protocol = [ + "unc-o11y/nightly_protocol", + "unc-parameters/nightly_protocol", + "unc-primitives/nightly_protocol", +] +nightly = [ + "nightly_protocol", + "unc-o11y/nightly", + "unc-parameters/nightly", + "unc-primitives/nightly", +] +default = [] +metrics = ["unc-o11y"] diff --git a/core/chain-configs/README.md b/core/chain-configs/README.md new file mode 100644 index 000000000..bfb798b62 --- /dev/null +++ b/core/chain-configs/README.md @@ -0,0 +1,13 @@ +# Chain configs crate + +This crate provides typed interfaces to the NEAR Genesis and Client Configs, together with the functions to validate their correctness. + +## Genesis config +Genesis config is the one that 'defines' the chain. It was set at the beginning and generally is not mutable. + +## Client config + +Client config is the part of the config that client can configure on their own - it controls things like: how many peers it should connect to before syncing, which shards to track etc. + +## Protocol config +This is the type that is spanning over GenesisConfig and RuntimeConfig. People should not use it directly, but use the ProtocolConfigView class instead. \ No newline at end of file diff --git a/core/chain-configs/src/client_config.rs b/core/chain-configs/src/client_config.rs new file mode 100644 index 000000000..45755c73f --- /dev/null +++ b/core/chain-configs/src/client_config.rs @@ -0,0 +1,507 @@ +//! Chain Client Configuration +use crate::ExternalStorageLocation::GCS; +use crate::MutableConfigValue; +use unc_primitives::types::{ + AccountId, BlockHeight, BlockHeightDelta, Gas, NumBlocks, NumSeats, ShardId, +}; +use unc_primitives::version::Version; +use std::cmp::{max, min}; +use std::path::PathBuf; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; +use std::time::Duration; + +pub const TEST_STATE_SYNC_TIMEOUT: u64 = 5; + +#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)] +pub enum LogSummaryStyle { + #[serde(rename = "plain")] + Plain, + #[serde(rename = "colored")] + Colored, +} + +/// Minimum number of epochs for which we keep store data +pub const MIN_GC_NUM_EPOCHS_TO_KEEP: u64 = 3; + +/// Default number of epochs for which we keep store data +pub const DEFAULT_GC_NUM_EPOCHS_TO_KEEP: u64 = 5; + +/// Default number of concurrent requests to external storage to fetch state parts. +pub const DEFAULT_STATE_SYNC_NUM_CONCURRENT_REQUESTS_EXTERNAL: u32 = 25; +pub const DEFAULT_STATE_SYNC_NUM_CONCURRENT_REQUESTS_ON_CATCHUP_EXTERNAL: u32 = 5; + +/// Configuration for garbage collection. +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)] +#[serde(default)] +pub struct GCConfig { + /// Maximum number of blocks to garbage collect at every garbage collection + /// call. + pub gc_blocks_limit: NumBlocks, + + /// Maximum number of height to go through at each garbage collection step + /// when cleaning forks during garbage collection. + pub gc_fork_clean_step: u64, + + /// Number of epochs for which we keep store data. + pub gc_num_epochs_to_keep: u64, +} + +impl Default for GCConfig { + fn default() -> Self { + Self { + gc_blocks_limit: 2, + gc_fork_clean_step: 100, + gc_num_epochs_to_keep: DEFAULT_GC_NUM_EPOCHS_TO_KEEP, + } + } +} + +impl GCConfig { + pub fn gc_num_epochs_to_keep(&self) -> u64 { + max(MIN_GC_NUM_EPOCHS_TO_KEEP, self.gc_num_epochs_to_keep) + } +} + +fn default_num_concurrent_requests() -> u32 { + DEFAULT_STATE_SYNC_NUM_CONCURRENT_REQUESTS_EXTERNAL +} + +fn default_num_concurrent_requests_during_catchup() -> u32 { + DEFAULT_STATE_SYNC_NUM_CONCURRENT_REQUESTS_ON_CATCHUP_EXTERNAL +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub struct ExternalStorageConfig { + /// Location of state parts. + pub location: ExternalStorageLocation, + /// When fetching state parts from external storage, throttle fetch requests + /// to this many concurrent requests. + #[serde(default = "default_num_concurrent_requests")] + pub num_concurrent_requests: u32, + /// During catchup, the node will use a different number of concurrent requests + /// to reduce the performance impact of state sync. + #[serde(default = "default_num_concurrent_requests_during_catchup")] + pub num_concurrent_requests_during_catchup: u32, +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub enum ExternalStorageLocation { + S3 { + /// Location of state dumps on S3. + bucket: String, + /// Data may only be available in certain locations. + region: String, + }, + Filesystem { + root_dir: PathBuf, + }, + GCS { + bucket: String, + }, +} + +/// Configures how to dump state to external storage. +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub struct DumpConfig { + /// Specifies where to write the obtained state parts. + pub location: ExternalStorageLocation, + /// Use in case a node that dumps state to the external storage + /// gets in trouble. + #[serde(skip_serializing_if = "Option::is_none")] + pub restart_dump_for_shards: Option>, + /// How often to check if a new epoch has started. + /// Feel free to set to `None`, defaults are sensible. + #[serde(skip_serializing_if = "Option::is_none")] + pub iteration_delay: Option, + /// Location of a json file with credentials allowing write access to the bucket. + #[serde(skip_serializing_if = "Option::is_none")] + pub credentials_file: Option, +} + +/// Configures how to fetch state parts during state sync. +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub enum SyncConfig { + /// Syncs state from the peers without reading anything from external storage. + Peers, + /// Expects parts to be available in external storage. + ExternalStorage(ExternalStorageConfig), +} + +impl Default for SyncConfig { + fn default() -> Self { + Self::Peers + } +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Default)] +/// Options for dumping state to S3. +pub struct StateSyncConfig { + #[serde(skip_serializing_if = "Option::is_none")] + /// `none` value disables state dump to external storage. + pub dump: Option, + #[serde(skip_serializing_if = "SyncConfig::is_default", default = "SyncConfig::default")] + pub sync: SyncConfig, +} + +impl SyncConfig { + /// Checks whether the object equals its default value. + fn is_default(&self) -> bool { + matches!(self, Self::Peers) + } +} + +// A handle that allows the main process to interrupt resharding if needed. +// This typically happens when the main process is interrupted. +#[derive(Clone)] +pub struct ReshardingHandle { + keep_going: Arc, +} + +impl ReshardingHandle { + pub fn new() -> Self { + Self { keep_going: Arc::new(AtomicBool::new(true)) } + } + + pub fn get(&self) -> bool { + self.keep_going.load(std::sync::atomic::Ordering::Relaxed) + } + + pub fn stop(&self) -> () { + self.keep_going.store(false, std::sync::atomic::Ordering::Relaxed); + } +} + +/// Configuration for resharding. +#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug, PartialEq)] +#[serde(default)] +pub struct ReshardingConfig { + /// The soft limit on the size of a single batch. The batch size can be + /// decreased if resharding is consuming too many resources and interfering + /// with regular node operation. + pub batch_size: bytesize::ByteSize, + + /// The delay between writing batches to the db. The batch delay can be + /// increased if resharding is consuming too many resources and interfering + /// with regular node operation. + pub batch_delay: Duration, + + /// The delay between attempts to start resharding while waiting for the + /// state snapshot to become available. + pub retry_delay: Duration, + + /// The delay between the resharding request is received and when the actor + /// actually starts working on it. This delay should only be used in tests. + pub initial_delay: Duration, + + /// The maximum time that the actor will wait for the snapshot to be ready, + /// before starting resharding. Do not wait indefinitely since we want to + /// report error early enough for the node maintainer to have time to recover. + pub max_poll_time: Duration, +} + +impl Default for ReshardingConfig { + fn default() -> Self { + // Conservative default for a slower resharding that puts as little + // extra load on the node as possible. + Self { + batch_size: bytesize::ByteSize::kb(500), + batch_delay: Duration::from_millis(100), + retry_delay: Duration::from_secs(10), + initial_delay: Duration::from_secs(0), + // The snapshot typically is available within a minute from the + // epoch start. Set the default higher in case we need to wait for + // state sync. + max_poll_time: Duration::from_secs(2 * 60 * 60), // 2 hours + } + } +} + +pub fn default_header_sync_initial_timeout() -> Duration { + Duration::from_secs(10) +} + +pub fn default_header_sync_progress_timeout() -> Duration { + Duration::from_secs(2) +} + +pub fn default_header_sync_stall_ban_timeout() -> Duration { + Duration::from_secs(120) +} + +pub fn default_state_sync_timeout() -> Duration { + Duration::from_secs(60) +} + +pub fn default_header_sync_expected_height_per_second() -> u64 { + 10 +} + +pub fn default_sync_check_period() -> Duration { + Duration::from_secs(10) +} + +pub fn default_sync_step_period() -> Duration { + Duration::from_millis(10) +} + +pub fn default_sync_height_threshold() -> u64 { + 1 +} + +pub fn default_epoch_sync_enabled() -> bool { + false +} + +pub fn default_state_sync() -> Option { + Some(StateSyncConfig { + dump: None, + sync: SyncConfig::ExternalStorage(ExternalStorageConfig { + location: GCS { bucket: "state-parts".to_string() }, + num_concurrent_requests: DEFAULT_STATE_SYNC_NUM_CONCURRENT_REQUESTS_EXTERNAL, + num_concurrent_requests_during_catchup: + DEFAULT_STATE_SYNC_NUM_CONCURRENT_REQUESTS_ON_CATCHUP_EXTERNAL, + }), + }) +} + +pub fn default_state_sync_enabled() -> bool { + true +} + +pub fn default_view_client_threads() -> usize { + 4 +} + +pub fn default_log_summary_period() -> Duration { + Duration::from_secs(10) +} + +pub fn default_view_client_throttle_period() -> Duration { + Duration::from_secs(30) +} + +pub fn default_trie_viewer_state_size_limit() -> Option { + Some(50_000) +} + +pub fn default_transaction_pool_size_limit() -> Option { + Some(100_000_000) // 100 MB. +} + +pub fn default_tx_routing_height_horizon() -> BlockHeightDelta { + 4 +} + +pub fn default_enable_multiline_logging() -> Option { + Some(true) +} + +pub fn default_produce_chunk_add_transactions_time_limit() -> Option { + Some(Duration::from_millis(200)) +} + +/// ClientConfig where some fields can be updated at runtime. +#[derive(Clone, serde::Serialize)] +pub struct ClientConfig { + /// Version of the binary. + pub version: Version, + /// Chain id for status. + pub chain_id: String, + /// Listening rpc port for status. + pub rpc_addr: Option, + /// Graceful shutdown at expected block height. + pub expected_shutdown: MutableConfigValue>, + /// Duration to check for producing / skipping block. + pub block_production_tracking_delay: Duration, + /// Minimum duration before producing block. + pub min_block_production_delay: Duration, + /// Maximum wait for approvals before producing block. + pub max_block_production_delay: Duration, + /// Maximum duration before skipping given height. + pub max_block_wait_delay: Duration, + /// Skip waiting for sync (for testing or single node testnet). + pub skip_sync_wait: bool, + /// How often to check that we are not out of sync. + pub sync_check_period: Duration, + /// While syncing, how long to check for each step. + pub sync_step_period: Duration, + /// Sync height threshold: below this difference in height don't start syncing. + pub sync_height_threshold: BlockHeightDelta, + /// How much time to wait after initial header sync + pub header_sync_initial_timeout: Duration, + /// How much time to wait after some progress is made in header sync + pub header_sync_progress_timeout: Duration, + /// How much time to wait before banning a peer in header sync if sync is too slow + pub header_sync_stall_ban_timeout: Duration, + /// Expected increase of header head height per second during header sync + pub header_sync_expected_height_per_second: u64, + /// How long to wait for a response during state sync + pub state_sync_timeout: Duration, + /// Minimum number of peers to start syncing. + pub min_num_peers: usize, + /// Period between logging summary information. + pub log_summary_period: Duration, + /// Enable coloring of the logs + pub log_summary_style: LogSummaryStyle, + /// Produce empty blocks, use `false` for testing. + pub produce_empty_blocks: bool, + /// Epoch length. + pub epoch_length: BlockHeightDelta, + /// Number of block producer seats + pub num_block_producer_seats: NumSeats, + /// Time to persist Accounts Id in the router without removing them. + pub ttl_account_id_router: Duration, + /// Horizon at which instead of fetching block, fetch full state. + pub block_fetch_horizon: BlockHeightDelta, + /// Time between check to perform catchup. + pub catchup_step_period: Duration, + /// Time between checking to re-request chunks. + pub chunk_request_retry_period: Duration, + /// Time between running doomslug timer. + pub doosmslug_step_period: Duration, + /// Behind this horizon header fetch kicks in. + pub block_header_fetch_horizon: BlockHeightDelta, + /// Garbage collection configuration. + pub gc: GCConfig, + /// Accounts that this client tracks. + pub tracked_accounts: Vec, + /// Shards that this client tracks. + pub tracked_shards: Vec, + /// Rotate between these sets of tracked shards. + /// Used to simulate the behavior of chunk only producers without staking tokens. + /// This field is only used if `tracked_shards` is empty. + pub tracked_shard_schedule: Vec>, + /// Not clear old data, set `true` for archive nodes. + pub archive: bool, + /// save_trie_changes should be set to true iff + /// - archive if false - non-archivale nodes need trie changes to perform garbage collection + /// - archive is true, cold_store is configured and migration to split_storage is finished - node + /// working in split storage mode needs trie changes in order to do garbage collection on hot. + pub save_trie_changes: bool, + /// Number of threads for ViewClientActor pool. + pub view_client_threads: usize, + /// Run Epoch Sync on the start. + pub epoch_sync_enabled: bool, + /// Number of seconds between state requests for view client. + pub view_client_throttle_period: Duration, + /// Upper bound of the byte size of contract state that is still viewable. None is no limit + pub trie_viewer_state_size_limit: Option, + /// Max burnt gas per view method. If present, overrides value stored in + /// genesis file. The value only affects the RPCs without influencing the + /// protocol thus changing it per-node doesn’t affect the blockchain. + pub max_gas_burnt_view: Option, + /// Re-export storage layer statistics as prometheus metrics. + pub enable_statistics_export: bool, + /// Number of threads to execute background migration work in client. + pub client_background_migration_threads: usize, + /// Enables background flat storage creation. + pub flat_storage_creation_enabled: bool, + /// Duration to perform background flat storage creation step. + pub flat_storage_creation_period: Duration, + /// Whether to use the State Sync mechanism. + /// If disabled, the node will do Block Sync instead of State Sync. + pub state_sync_enabled: bool, + /// Options for syncing state. + pub state_sync: StateSyncConfig, + /// Limit of the size of per-shard transaction pool measured in bytes. If not set, the size + /// will be unbounded. + pub transaction_pool_size_limit: Option, + // Allows more detailed logging, for example a list of orphaned blocks. + pub enable_multiline_logging: bool, + // Configuration for resharding. + pub resharding_config: MutableConfigValue, + /// If the node is not a chunk producer within that many blocks, then route + /// to upcoming chunk producers. + pub tx_routing_height_horizon: BlockHeightDelta, + /// Limit the time of adding transactions to a chunk. + /// A node produces a chunk by adding transactions from the transaction pool until + /// some limit is reached. This time limit ensures that adding transactions won't take + /// longer than the specified duration, which helps to produce the chunk quickly. + pub produce_chunk_add_transactions_time_limit: MutableConfigValue>, +} + +impl ClientConfig { + pub fn test( + skip_sync_wait: bool, + min_block_prod_time: u64, + max_block_prod_time: u64, + num_block_producer_seats: NumSeats, + archive: bool, + save_trie_changes: bool, + epoch_sync_enabled: bool, + state_sync_enabled: bool, + ) -> Self { + assert!( + archive || save_trie_changes, + "Configuration with archive = false and save_trie_changes = false is not supported \ + because non-archival nodes must save trie changes in order to do do garbage collection." + ); + + Self { + version: Default::default(), + chain_id: "unittest".to_string(), + rpc_addr: Some("0.0.0.0:3030".to_string()), + expected_shutdown: MutableConfigValue::new(None, "expected_shutdown"), + block_production_tracking_delay: Duration::from_millis(std::cmp::max( + 10, + min_block_prod_time / 5, + )), + min_block_production_delay: Duration::from_millis(min_block_prod_time), + max_block_production_delay: Duration::from_millis(max_block_prod_time), + max_block_wait_delay: Duration::from_millis(3 * min_block_prod_time), + skip_sync_wait, + sync_check_period: Duration::from_millis(100), + sync_step_period: Duration::from_millis(10), + sync_height_threshold: 1, + header_sync_initial_timeout: Duration::from_secs(10), + header_sync_progress_timeout: Duration::from_secs(2), + header_sync_stall_ban_timeout: Duration::from_secs(30), + state_sync_timeout: Duration::from_secs(TEST_STATE_SYNC_TIMEOUT), + header_sync_expected_height_per_second: 1, + min_num_peers: 1, + log_summary_period: Duration::from_secs(10), + produce_empty_blocks: true, + epoch_length: 10, + num_block_producer_seats, + ttl_account_id_router: Duration::from_secs(60 * 60), + block_fetch_horizon: 50, + catchup_step_period: Duration::from_millis(1), + chunk_request_retry_period: min( + Duration::from_millis(100), + Duration::from_millis(min_block_prod_time / 5), + ), + doosmslug_step_period: Duration::from_millis(100), + block_header_fetch_horizon: 50, + gc: GCConfig { gc_blocks_limit: 100, ..GCConfig::default() }, + tracked_accounts: vec![], + tracked_shards: vec![], + tracked_shard_schedule: vec![], + archive, + save_trie_changes, + log_summary_style: LogSummaryStyle::Colored, + view_client_threads: 1, + epoch_sync_enabled, + view_client_throttle_period: Duration::from_secs(1), + trie_viewer_state_size_limit: None, + max_gas_burnt_view: None, + enable_statistics_export: true, + client_background_migration_threads: 1, + flat_storage_creation_enabled: true, + flat_storage_creation_period: Duration::from_secs(1), + state_sync_enabled, + state_sync: StateSyncConfig::default(), + transaction_pool_size_limit: None, + enable_multiline_logging: false, + resharding_config: MutableConfigValue::new( + ReshardingConfig::default(), + "resharding_config", + ), + tx_routing_height_horizon: 4, + produce_chunk_add_transactions_time_limit: MutableConfigValue::new( + default_produce_chunk_add_transactions_time_limit(), + "produce_chunk_add_transactions_time_limit", + ), + } + } +} diff --git a/core/chain-configs/src/genesis_config.rs b/core/chain-configs/src/genesis_config.rs new file mode 100644 index 000000000..ef618f5a3 --- /dev/null +++ b/core/chain-configs/src/genesis_config.rs @@ -0,0 +1,1203 @@ +//! Genesis Configuration +//! +//! NOTE: chain-configs is not the best place for `GenesisConfig` since it +//! contains `RuntimeConfig`, but we keep it here for now until we figure +//! out the better place. +use crate::genesis_validate::validate_genesis; +use anyhow::Context; +use chrono::{DateTime, Utc}; +use unc_config_utils::ValidationError; +use unc_parameters::{RuntimeConfig, RuntimeConfigView}; +use unc_primitives::epoch_manager::EpochConfig; +use unc_primitives::shard_layout::ShardLayout; +use unc_primitives::types::StateRoot; +use unc_primitives::{ + hash::CryptoHash, + serialize::dec_format, + state_record::StateRecord, + types::{ + AccountId, AccountInfo, Balance, BlockHeight, BlockHeightDelta, Gas, NumBlocks, NumSeats, + }, + version::ProtocolVersion, +}; +use num_rational::Rational32; +use serde::de::{self, DeserializeSeed, IgnoredAny, MapAccess, SeqAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_json::Serializer; +use sha2::digest::Digest; +use smart_default::SmartDefault; +use std::collections::HashSet; +use std::fmt; +use std::fs::File; +use std::io::{BufReader, Read}; +use std::path::{Path, PathBuf}; +use tracing::warn; +use unc_primitives::types::validator_power_and_frozen::ValidatorPowerAndFrozen; + +const MAX_GAS_PRICE: Balance = 10_000_000_000_000_000_000_000; + +fn default_online_min_threshold() -> Rational32 { + Rational32::new(90, 100) +} + +fn default_online_max_threshold() -> Rational32 { + Rational32::new(99, 100) +} + +fn default_minimum_stake_divisor() -> u64 { + 10 +} + +fn default_protocol_upgrade_stake_threshold() -> Rational32 { + Rational32::new(8, 10) +} + +fn default_shard_layout() -> ShardLayout { + ShardLayout::v0_single_shard() +} + +fn default_minimum_stake_ratio() -> Rational32 { + Rational32::new(160, 1_000_000) +} + +fn default_minimum_validators_per_shard() -> u64 { + 1 +} + +fn default_num_chunk_only_producer_seats() -> u64 { + 300 +} + +fn default_use_production_config() -> bool { + false +} + +fn default_max_kickout_stake_threshold() -> u8 { + 100 +} + +#[derive(Debug, Clone, SmartDefault, serde::Serialize, serde::Deserialize)] +pub struct GenesisConfig { + /// Protocol version that this genesis works with. + pub protocol_version: ProtocolVersion, + /// Official time of blockchain start. + #[default(Utc::now())] + pub genesis_time: DateTime, + /// ID of the blockchain. This must be unique for every blockchain. + /// If your testnet blockchains do not have unique chain IDs, you will have a bad time. + pub chain_id: String, + /// Height of genesis block. + pub genesis_height: BlockHeight, + /// Number of block producer seats at genesis. + pub num_block_producer_seats: NumSeats, + /// Defines number of shards and number of block producer seats per each shard at genesis. + /// Note: not used with protocol_feature_chunk_only_producers -- replaced by minimum_validators_per_shard + /// Note: not used before as all block producers produce chunks for all shards + pub num_block_producer_seats_per_shard: Vec, + /// Expected number of hidden validators per shard. + pub avg_hidden_validator_seats_per_shard: Vec, + /// Enable dynamic re-sharding. + pub dynamic_resharding: bool, + /// Threshold of stake that needs to indicate that they ready for upgrade. + #[serde(default = "default_protocol_upgrade_stake_threshold")] + #[default(Rational32::new(8, 10))] + pub protocol_upgrade_stake_threshold: Rational32, + /// Epoch length counted in block heights. + pub epoch_length: BlockHeightDelta, + /// Initial gas limit. + pub gas_limit: Gas, + /// Minimum gas price. It is also the initial gas price. + #[serde(with = "dec_format")] + pub min_gas_price: Balance, + #[serde(with = "dec_format")] + #[default(MAX_GAS_PRICE)] + pub max_gas_price: Balance, + /// Criterion for kicking out block producers (this is a number between 0 and 100) + pub block_producer_kickout_threshold: u8, + /// Criterion for kicking out chunk producers (this is a number between 0 and 100) + pub chunk_producer_kickout_threshold: u8, + /// Online minimum threshold below which validator doesn't receive reward. + #[serde(default = "default_online_min_threshold")] + #[default(Rational32::new(90, 100))] + pub online_min_threshold: Rational32, + /// Online maximum threshold above which validator gets full reward. + #[serde(default = "default_online_max_threshold")] + #[default(Rational32::new(99, 100))] + pub online_max_threshold: Rational32, + /// Gas price adjustment rate + #[default(Rational32::from_integer(0))] + pub gas_price_adjustment_rate: Rational32, + /// List of initial validators. + pub validators: Vec, + /// Number of blocks for which a given transaction is valid + pub transaction_validity_period: NumBlocks, + /// Protocol treasury rate + #[default(Rational32::from_integer(0))] + pub protocol_reward_rate: Rational32, + /// Maximum inflation on the total supply every epoch. + #[default(Rational32::from_integer(0))] + pub max_inflation_rate: Rational32, + /// Total supply of tokens at genesis. + #[serde(with = "dec_format")] + pub total_supply: Balance, + /// Expected number of blocks per year + pub num_blocks_per_year: NumBlocks, + /// Protocol treasury account + #[default("unc".parse().unwrap())] + pub protocol_treasury_account: AccountId, + /// Fishermen stake threshold. + #[serde(with = "dec_format")] + pub fishermen_threshold: Balance, + /// The minimum stake required for staking is last seat price divided by this number. + #[serde(default = "default_minimum_stake_divisor")] + #[default(10)] + pub minimum_stake_divisor: u64, + /// Layout information regarding how to split accounts to shards + #[serde(default = "default_shard_layout")] + #[default(ShardLayout::v0_single_shard())] + pub shard_layout: ShardLayout, + #[serde(default = "default_num_chunk_only_producer_seats")] + #[default(300)] + pub num_chunk_only_producer_seats: NumSeats, + /// The minimum number of validators each shard must have + #[serde(default = "default_minimum_validators_per_shard")] + #[default(1)] + pub minimum_validators_per_shard: NumSeats, + #[serde(default = "default_max_kickout_stake_threshold")] + #[default(100)] + /// Max stake percentage of the validators we will kick out. + pub max_kickout_stake_perc: u8, + /// The lowest ratio s/s_total any block producer can have. + /// See for details + #[serde(default = "default_minimum_stake_ratio")] + #[default(Rational32::new(160, 1_000_000))] + pub minimum_stake_ratio: Rational32, + #[serde(default = "default_use_production_config")] + #[default(false)] + /// This is only for test purposes. We hard code some configs for mainnet and testnet + /// in AllEpochConfig, and we want to have a way to test that code path. This flag is for that. + /// If set to true, the node will use the same config override path as mainnet and testnet. + pub use_production_config: bool, +} + +impl GenesisConfig { + pub fn use_production_config(&self) -> bool { + self.use_production_config + || self.chain_id == unc_primitives::chains::TESTNET + || self.chain_id == unc_primitives::chains::MAINNET + } +} + +impl From<&GenesisConfig> for EpochConfig { + fn from(config: &GenesisConfig) -> Self { + EpochConfig { + epoch_length: config.epoch_length, + num_block_producer_seats: config.num_block_producer_seats, + num_block_producer_seats_per_shard: config.num_block_producer_seats_per_shard.clone(), + avg_hidden_validator_seats_per_shard: config + .avg_hidden_validator_seats_per_shard + .clone(), + block_producer_kickout_threshold: config.block_producer_kickout_threshold, + chunk_producer_kickout_threshold: config.chunk_producer_kickout_threshold, + fishermen_threshold: config.fishermen_threshold, + online_min_threshold: config.online_min_threshold, + online_max_threshold: config.online_max_threshold, + protocol_upgrade_stake_threshold: config.protocol_upgrade_stake_threshold, + minimum_stake_divisor: config.minimum_stake_divisor, + shard_layout: config.shard_layout.clone(), + validator_selection_config: unc_primitives::epoch_manager::ValidatorSelectionConfig { + num_chunk_only_producer_seats: config.num_chunk_only_producer_seats, + minimum_validators_per_shard: config.minimum_validators_per_shard, + minimum_stake_ratio: config.minimum_stake_ratio, + }, + validator_max_kickout_stake_perc: config.max_kickout_stake_perc, + } + } +} + +/// Records in storage at genesis (get split into shards at genesis creation). +#[derive( + Debug, + Clone, + SmartDefault, + derive_more::AsRef, + derive_more::AsMut, + derive_more::From, + serde::Serialize, + serde::Deserialize, +)] +pub struct GenesisRecords(pub Vec); + +/// custom deserializer that does *almost the same thing that #[serde(default)] would do -- +/// if no value is provided in json, returns default value. +/// * Here if `null` is provided as value in JSON, default value will be returned, +/// while in serde default implementation that scenario wouldn't parse. +fn no_value_and_null_as_default<'de, D, T>(de: D) -> Result +where + D: Deserializer<'de>, + T: Deserialize<'de> + Default, +{ + let opt = Option::::deserialize(de)?; + match opt { + None => Ok(T::default()), + Some(value) => Ok(value), + } +} + +#[derive(Debug, Clone, SmartDefault, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum GenesisContents { + /// Records in storage at genesis (get split into shards at genesis creation). + #[default] + Records { records: GenesisRecords }, + /// Genesis object may not contain records. + /// In this case records can be found in records_file. + /// The idea is that all records consume too much memory, + /// so they should be processed in streaming fashion with for_each_record. + RecordsFile { records_file: PathBuf }, + /// Use records already in storage, represented by these state roots. + /// Used only for mock network forking for testing purposes. + /// WARNING: THIS IS USED FOR TESTING ONLY. IT IS **NOT CORRECT**, because + /// it is impossible to compute the corresponding genesis hash in this form, + /// such that the genesis hash is consistent with that of an equivalent + /// genesis that spells out the records. + StateRoots { state_roots: Vec }, +} + +fn contents_are_from_record_file(contents: &GenesisContents) -> bool { + match contents { + GenesisContents::RecordsFile { .. } => true, + _ => false, + } +} + +/// `Genesis` has an invariant that `total_supply` is equal to the supply seen in the records. +/// However, we can't enforce that invariant. All fields are public, but the clients are expected to +/// use the provided methods for instantiation, serialization and deserialization. +/// Refer to `test_loading_localnet_genesis` to see an example of serialized Genesis JSON. +#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] +pub struct Genesis { + #[serde(flatten)] + pub config: GenesisConfig, + /// Custom deserializer used instead of serde(default), + /// because serde(flatten) doesn't work with default for some reason + /// The corresponding issue has been open since 2019, so any day now. + #[serde( + flatten, + deserialize_with = "no_value_and_null_as_default", + skip_serializing_if = "contents_are_from_record_file" + )] + pub contents: GenesisContents, +} + +impl GenesisConfig { + /// Parses GenesisConfig from a JSON string. + /// The string can be a JSON with comments. + /// It panics if the contents cannot be parsed from JSON to the GenesisConfig structure. + pub fn from_json(value: &str) -> Self { + let json_str_without_comments: String = + unc_config_utils::strip_comments_from_json_str(&value.to_string()) + .expect("Failed to strip comments from genesis config."); + serde_json::from_str(&json_str_without_comments) + .expect("Failed to deserialize the genesis config.") + } + + /// Reads GenesisConfig from a JSON file. + /// The file can be a JSON with comments. + /// It panics if file cannot be open or read, or the contents cannot be parsed from JSON to the + /// GenesisConfig structure. + pub fn from_file>(path: P) -> anyhow::Result { + let mut file = File::open(path).with_context(|| "Could not open genesis config file.")?; + let mut json_str = String::new(); + file.read_to_string(&mut json_str)?; + let json_str_without_comments: String = + unc_config_utils::strip_comments_from_json_str(&json_str)?; + let genesis_config: GenesisConfig = serde_json::from_str(&json_str_without_comments) + .with_context(|| "Failed to deserialize the genesis config.")?; + Ok(genesis_config) + } + + /// Writes GenesisConfig to the file. + pub fn to_file>(&self, path: P) { + std::fs::write( + path, + serde_json::to_vec_pretty(self).expect("Error serializing the genesis config."), + ) + .expect("Failed to create / write a genesis config file."); + } + + /// Get validators from genesis config + pub fn validators(&self) -> Vec { + self.validators + .iter() + .map(|account_info| { + ValidatorPowerAndFrozen::new( + account_info.account_id.clone(), + account_info.public_key.clone(), + account_info.power, + account_info.locked, + ) + }) + .collect() + } +} + +impl GenesisRecords { + /// Parses GenesisRecords from a JSON string. + /// + /// It panics if the contents cannot be parsed from JSON to the GenesisConfig structure. + pub fn from_json(value: &str) -> Self { + serde_json::from_str(value).expect("Failed to deserialize the genesis records.") + } + + /// Reads GenesisRecords from a JSON file. + /// The file can be a JSON with comments. + /// It panics if file cannot be open or read, or the contents cannot be parsed from JSON to the + /// GenesisConfig structure. + pub fn from_file>(path: P) -> Self { + let mut file = File::open(path).expect("Failed to open genesis config file."); + let mut json_str = String::new(); + file.read_to_string(&mut json_str) + .expect("Failed to read the genesis config file to string. "); + let json_str_without_comments: String = + unc_config_utils::strip_comments_from_json_str(&json_str) + .expect("Failed to strip comments from Genesis config file."); + serde_json::from_str(&json_str_without_comments) + .expect("Failed to deserialize the genesis records.") + } + + /// Writes GenesisRecords to the file. + pub fn to_file>(&self, path: P) { + std::fs::write( + path, + serde_json::to_vec_pretty(self).expect("Error serializing the genesis records."), + ) + .expect("Failed to create / write a genesis records file."); + } +} + +/// Visitor for records. +/// Reads records one by one and passes them to sink. +/// If full genesis file is passed, reads records from "records" field and +/// IGNORES OTHER FIELDS. +struct RecordsProcessor { + sink: F, +} + +impl<'de, F: FnMut(StateRecord)> Visitor<'de> for RecordsProcessor<&'_ mut F> { + type Value = (); + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str( + "either:\ + 1. array of StateRecord\ + 2. map with records field which is array of StateRecord", + ) + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + while let Some(record) = seq.next_element::()? { + (self.sink)(record) + } + Ok(()) + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut me = Some(self); + let mut has_records_field = false; + while let Some(key) = map.next_key::()? { + match key.as_str() { + "records" => { + let me = + me.take().ok_or_else(|| de::Error::custom("duplicate field: records"))?; + map.next_value_seed(me)?; + has_records_field = true; + } + _ => { + map.next_value::()?; + } + } + } + if has_records_field { + Ok(()) + } else { + Err(de::Error::custom("missing field: records")) + } + } +} + +impl<'de, F: FnMut(StateRecord)> DeserializeSeed<'de> for RecordsProcessor<&'_ mut F> { + type Value = (); + + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_seq(self) + } +} + +/// The file can be a JSON with comments +pub fn stream_records_from_file( + reader: impl Read, + mut callback: impl FnMut(StateRecord), +) -> serde_json::Result<()> { + let reader_without_comments = unc_config_utils::strip_comments_from_json_reader(reader); + let mut deserializer = serde_json::Deserializer::from_reader(reader_without_comments); + let records_processor = RecordsProcessor { sink: &mut callback }; + deserializer.deserialize_any(records_processor) +} + +pub struct GenesisJsonHasher { + digest: sha2::Sha256, +} + +impl GenesisJsonHasher { + pub fn new() -> Self { + Self { digest: sha2::Sha256::new() } + } + + pub fn process_config(&mut self, config: &GenesisConfig) { + let mut ser = Serializer::pretty(&mut self.digest); + config.serialize(&mut ser).expect("Error serializing the genesis config."); + } + + pub fn process_record(&mut self, record: &StateRecord) { + let mut ser = Serializer::pretty(&mut self.digest); + record.serialize(&mut ser).expect("Error serializing the genesis record."); + } + + pub fn process_state_roots(&mut self, state_roots: &[StateRoot]) { + // WARNING: THIS IS INCORRECT, because it is impossible to compute the + // genesis hash from the state root in a way that is consistent to that + // of a genesis that spells out all the records. + // THEREFORE, THIS IS ONLY USED FOR TESTING, and this logic is only + // present at all because a genesis hash always has to at least exist. + let mut ser = Serializer::pretty(&mut self.digest); + state_roots.serialize(&mut ser).expect("Error serializing the state roots."); + } + + pub fn process_genesis(&mut self, genesis: &Genesis) { + self.process_config(&genesis.config); + match &genesis.contents { + GenesisContents::StateRoots { state_roots } => { + self.process_state_roots(state_roots); + } + _ => { + genesis.for_each_record(|record: &StateRecord| { + self.process_record(record); + }); + } + } + } + + pub fn finalize(self) -> CryptoHash { + CryptoHash(self.digest.finalize().into()) + } +} + +pub enum GenesisValidationMode { + Full, + UnsafeFast, +} + +impl Genesis { + pub fn new(config: GenesisConfig, records: GenesisRecords) -> Result { + Self::new_validated(config, records, GenesisValidationMode::Full) + } + + pub fn new_with_path>( + config: GenesisConfig, + records_file: P, + ) -> Result { + Self::new_with_path_validated(config, records_file, GenesisValidationMode::Full) + } + + /// Reads Genesis from a single JSON file, the file can be JSON with comments + /// This function will collect all errors regarding genesis.json and push them to validation_errors + pub fn from_file>( + path: P, + genesis_validation: GenesisValidationMode, + ) -> Result { + let mut file = File::open(&path).map_err(|e| ValidationError::GenesisFileError { + error_message: format!( + "Could not open genesis config file at path {}: {:?}", + &path.as_ref().display(), + e, + ), + })?; + + let mut json_str = String::new(); + file.read_to_string(&mut json_str).map_err(|e| ValidationError::GenesisFileError { + error_message: format!("Failed to read genesis config file to string: {:?}", e), + })?; + + let json_str_without_comments = unc_config_utils::strip_comments_from_json_str(&json_str) + .map_err(|e| ValidationError::GenesisFileError { + error_message: format!( + "Failed to strip comments from genesis config file: {:?}", + e + ), + })?; + + let genesis = serde_json::from_str::(&json_str_without_comments).map_err(|e| { + ValidationError::GenesisFileError { + error_message: format!("Failed to deserialize the genesis records: {:?}", e), + } + })?; + + genesis.validate(genesis_validation)?; + Ok(genesis) + } + + /// Reads Genesis from config and records files. + pub fn from_files( + config_path: P1, + records_path: P2, + genesis_validation: GenesisValidationMode, + ) -> Result + where + P1: AsRef, + P2: AsRef, + { + let genesis_config = GenesisConfig::from_file(config_path).map_err(|error| { + let error_message = error.to_string(); + ValidationError::GenesisFileError { error_message: error_message } + })?; + Self::new_with_path_validated(genesis_config, records_path, genesis_validation) + } + + pub fn new_from_state_roots(config: GenesisConfig, state_roots: Vec) -> Self { + Self { config, contents: GenesisContents::StateRoots { state_roots } } + } + + fn new_validated( + config: GenesisConfig, + records: GenesisRecords, + genesis_validation: GenesisValidationMode, + ) -> Result { + let genesis = Self { config, contents: GenesisContents::Records { records } }; + genesis.validate(genesis_validation)?; + Ok(genesis) + } + + fn new_with_path_validated>( + config: GenesisConfig, + records_file: P, + genesis_validation: GenesisValidationMode, + ) -> Result { + let genesis = Self { + config, + contents: GenesisContents::RecordsFile { + records_file: records_file.as_ref().to_path_buf(), + }, + }; + genesis.validate(genesis_validation)?; + Ok(genesis) + } + + pub fn validate( + &self, + genesis_validation: GenesisValidationMode, + ) -> Result<(), ValidationError> { + match genesis_validation { + GenesisValidationMode::Full => validate_genesis(self), + GenesisValidationMode::UnsafeFast => { + warn!(target: "genesis", "Skipped genesis validation"); + Ok(()) + } + } + } + + /// Writes Genesis to the file. + pub fn to_file>(&self, path: P) { + std::fs::write( + path, + serde_json::to_vec_pretty(self).expect("Error serializing the genesis config."), + ) + .expect("Failed to create / write a genesis config file."); + } + + /// Hash of the json-serialized input. + /// DEVNOTE: the representation is not unique, and could change on upgrade. + pub fn json_hash(&self) -> CryptoHash { + let mut hasher = GenesisJsonHasher::new(); + hasher.process_genesis(self); + hasher.finalize() + } + + /// If records vector is empty processes records stream from records_file. + /// May panic if records_file is removed or is in wrong format. + pub fn for_each_record(&self, mut callback: impl FnMut(&StateRecord)) { + match &self.contents { + GenesisContents::Records { records } => { + for record in &records.0 { + callback(record); + } + } + GenesisContents::RecordsFile { records_file } => { + let callback_move = |record: StateRecord| { + callback(&record); + }; + let reader = BufReader::new( + File::open(&records_file).expect("error while opening records file"), + ); + stream_records_from_file(reader, callback_move) + .expect("error while streaming records"); + } + GenesisContents::StateRoots { .. } => { + unreachable!("Cannot iterate through records when genesis uses state roots"); + } + } + } + + /// Forces loading genesis records into memory. + /// + /// This is meant for **tests only**. In production code you should be + /// using [`Self::for_each_record`] instead to iterate over records. + /// + /// If the records are already loaded, simply returns mutable reference to + /// them. Otherwise, reads them from `records_file`, stores them in memory + /// and then returns mutable reference to them. + pub fn force_read_records(&mut self) -> &mut GenesisRecords { + match &self.contents { + GenesisContents::RecordsFile { records_file } => { + self.contents = + GenesisContents::Records { records: GenesisRecords::from_file(records_file) }; + } + GenesisContents::Records { .. } => {} + GenesisContents::StateRoots { .. } => { + unreachable!("Cannot iterate through records when genesis uses state roots"); + } + } + match &mut self.contents { + GenesisContents::Records { records } => records, + _ => { + unreachable!("Records should have been set previously"); + } + } + } +} + +/// Config for changes applied to state dump. +#[derive(Debug, Default)] +pub struct GenesisChangeConfig { + pub select_account_ids: Option>, + pub whitelist_validators: Option>, +} + +impl GenesisChangeConfig { + pub fn with_select_account_ids(mut self, select_account_ids: Option>) -> Self { + self.select_account_ids = select_account_ids; + self + } + + pub fn with_whitelist_validators( + mut self, + whitelist_validators: Option>, + ) -> Self { + self.whitelist_validators = match whitelist_validators { + None => None, + Some(whitelist) => Some(whitelist.into_iter().collect::>()), + }; + self + } +} + +// Note: this type cannot be placed in primitives/src/view.rs because of `RuntimeConfig` dependency issues. +// Ideally we should create `RuntimeConfigView`, but given the deeply nested nature and the number of fields inside +// `RuntimeConfig`, it should be its own endeavor. +// TODO: This has changed, there is now `RuntimeConfigView`. Reconsider if moving this is possible now. +// TODO: Consider replacing tens of fields with a combination of `GenesisConfig` +// and `EpochConfig` fields, similar to how `RuntimeConfig` is represented as a +// separate struct and not inlined. +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct ProtocolConfigView { + /// Current Protocol Version + pub protocol_version: ProtocolVersion, + /// Official time of blockchain start. + pub genesis_time: DateTime, + /// ID of the blockchain. This must be unique for every blockchain. + /// If your testnet blockchains do not have unique chain IDs, you will have a bad time. + pub chain_id: String, + /// Height of genesis block. + pub genesis_height: BlockHeight, + /// Number of block producer seats at genesis. + pub num_block_producer_seats: NumSeats, + /// Defines number of shards and number of block producer seats per each shard at genesis. + pub num_block_producer_seats_per_shard: Vec, + /// Expected number of hidden validators per shard. + pub avg_hidden_validator_seats_per_shard: Vec, + /// Enable dynamic re-sharding. + pub dynamic_resharding: bool, + /// Threshold of stake that needs to indicate that they ready for upgrade. + pub protocol_upgrade_stake_threshold: Rational32, + /// Epoch length counted in block heights. + pub epoch_length: BlockHeightDelta, + /// Initial gas limit. + pub gas_limit: Gas, + /// Minimum gas price. It is also the initial gas price. + #[serde(with = "dec_format")] + pub min_gas_price: Balance, + /// Maximum gas price. + #[serde(with = "dec_format")] + pub max_gas_price: Balance, + /// Criterion for kicking out block producers (this is a number between 0 and 100) + pub block_producer_kickout_threshold: u8, + /// Criterion for kicking out chunk producers (this is a number between 0 and 100) + pub chunk_producer_kickout_threshold: u8, + /// Online minimum threshold below which validator doesn't receive reward. + pub online_min_threshold: Rational32, + /// Online maximum threshold above which validator gets full reward. + pub online_max_threshold: Rational32, + /// Gas price adjustment rate + pub gas_price_adjustment_rate: Rational32, + /// Runtime configuration (mostly economics constants). + pub runtime_config: RuntimeConfigView, + /// Number of blocks for which a given transaction is valid + pub transaction_validity_period: NumBlocks, + /// Protocol treasury rate + pub protocol_reward_rate: Rational32, + /// Maximum inflation on the total supply every epoch. + pub max_inflation_rate: Rational32, + /// Expected number of blocks per year + pub num_blocks_per_year: NumBlocks, + /// Protocol treasury account + pub protocol_treasury_account: AccountId, + /// Fishermen stake threshold. + #[serde(with = "dec_format")] + pub fishermen_threshold: Balance, + /// The minimum stake required for staking is last seat price divided by this number. + pub minimum_stake_divisor: u64, + /// Max stake percentage of the validators we will kick out. + pub max_kickout_stake_perc: u8, + /// The lowest ratio s/s_total any block producer can have. + /// See for details + pub minimum_stake_ratio: Rational32, + /// The minimum number of validators each shard must have + pub minimum_validators_per_shard: NumSeats, + /// Number of validator seats for chunk only producers. + pub num_chunk_only_producer_seats: NumSeats, + /// Layout information regarding how to split accounts to shards + pub shard_layout: ShardLayout, +} + +pub struct ProtocolConfig { + pub genesis_config: GenesisConfig, + pub runtime_config: RuntimeConfig, +} + +impl From for ProtocolConfigView { + fn from(protocol_config: ProtocolConfig) -> Self { + let ProtocolConfig { genesis_config, runtime_config } = protocol_config; + + ProtocolConfigView { + protocol_version: genesis_config.protocol_version, + genesis_time: genesis_config.genesis_time, + chain_id: genesis_config.chain_id, + genesis_height: genesis_config.genesis_height, + num_block_producer_seats: genesis_config.num_block_producer_seats, + num_block_producer_seats_per_shard: genesis_config.num_block_producer_seats_per_shard, + avg_hidden_validator_seats_per_shard: genesis_config + .avg_hidden_validator_seats_per_shard, + dynamic_resharding: genesis_config.dynamic_resharding, + protocol_upgrade_stake_threshold: genesis_config.protocol_upgrade_stake_threshold, + epoch_length: genesis_config.epoch_length, + gas_limit: genesis_config.gas_limit, + min_gas_price: genesis_config.min_gas_price, + max_gas_price: genesis_config.max_gas_price, + block_producer_kickout_threshold: genesis_config.block_producer_kickout_threshold, + chunk_producer_kickout_threshold: genesis_config.chunk_producer_kickout_threshold, + online_min_threshold: genesis_config.online_min_threshold, + online_max_threshold: genesis_config.online_max_threshold, + gas_price_adjustment_rate: genesis_config.gas_price_adjustment_rate, + runtime_config: RuntimeConfigView::from(runtime_config), + transaction_validity_period: genesis_config.transaction_validity_period, + protocol_reward_rate: genesis_config.protocol_reward_rate, + max_inflation_rate: genesis_config.max_inflation_rate, + num_blocks_per_year: genesis_config.num_blocks_per_year, + protocol_treasury_account: genesis_config.protocol_treasury_account, + fishermen_threshold: genesis_config.fishermen_threshold, + minimum_stake_divisor: genesis_config.minimum_stake_divisor, + max_kickout_stake_perc: genesis_config.max_kickout_stake_perc, + minimum_stake_ratio: genesis_config.minimum_stake_ratio, + minimum_validators_per_shard: genesis_config.minimum_validators_per_shard, + num_chunk_only_producer_seats: genesis_config.num_chunk_only_producer_seats, + shard_layout: genesis_config.shard_layout, + } + } +} + +pub fn get_initial_supply(records: &[StateRecord]) -> Balance { + let mut total_supply = 0; + for record in records { + if let StateRecord::Account { account, .. } = record { + total_supply += account.amount() + account.locked(); + } + } + total_supply +} + +#[cfg(test)] +mod test { + use crate::genesis_config::RecordsProcessor; + use crate::{Genesis, GenesisValidationMode}; + use unc_primitives::state_record::StateRecord; + use serde::Deserializer; + + fn stream_records_from_json_str(genesis: &str) -> serde_json::Result<()> { + let mut deserializer = serde_json::Deserializer::from_reader(genesis.as_bytes()); + let records_processor = RecordsProcessor { sink: &mut |_record: StateRecord| {} }; + deserializer.deserialize_any(records_processor) + } + + #[test] + fn test_genesis_with_empty_records() { + let genesis = r#"{ + "a": [1, 2], + "b": "random", + "records": [] + }"#; + stream_records_from_json_str(genesis).expect("error reading empty records"); + } + + #[test] + #[should_panic(expected = "missing field: records")] + fn test_genesis_with_no_records() { + let genesis = r#"{ + "a": [1, 2], + "b": "random" + }"#; + stream_records_from_json_str(genesis).unwrap(); + } + + #[test] + #[should_panic(expected = "duplicate field: records")] + fn test_genesis_with_several_records_fields() { + let genesis = r#"{ + "a": [1, 2], + "records": [{ + "Account": { + "account_id": "01.near", + "account": { + "amount": "49999999958035075000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 264 + } + } + }], + "b": "random", + "records": [{ + "Account": { + "account_id": "01.near", + "account": { + "amount": "49999999958035075000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 264 + } + } + }] + }"#; + stream_records_from_json_str(genesis).unwrap(); + } + + #[test] + fn test_genesis_with_fields_after_records() { + let genesis = r#"{ + "a": [1, 2], + "b": "random", + "records": [ + { + "Account": { + "account_id": "01.near", + "account": { + "amount": "49999999958035075000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 264 + } + } + } + ], + "c": { + "d": 1, + "e": [] + } + }"#; + stream_records_from_json_str(genesis).expect("error reading records with a field after"); + } + + #[test] + fn test_genesis_with_fields_before_records() { + let genesis = r#"{ + "a": [1, 2], + "b": "random", + "c": { + "d": 1, + "e": [] + }, + "records": [ + { + "Account": { + "account_id": "01.near", + "account": { + "amount": "49999999958035075000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 264 + } + } + } + ] + }"#; + stream_records_from_json_str(genesis).expect("error reading records from genesis"); + } + + #[test] + fn test_genesis_with_several_records() { + let genesis = r#"{ + "a": [1, 2], + "b": "random", + "c": { + "d": 1, + "e": [] + }, + "records": [ + { + "Account": { + "account_id": "01.near", + "account": { + "amount": "49999999958035075000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 264 + } + } + }, + { + "Account": { + "account_id": "01.near", + "account": { + "amount": "49999999958035075000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 264 + } + } + } + ] + }"#; + stream_records_from_json_str(genesis).expect("error reading records from genesis"); + } + + #[test] + fn test_loading_localnet_genesis() { + let genesis_str = r#"{ + "protocol_version": 57, + "genesis_time": "2022-11-15T05:17:59.578706Z", + "chain_id": "localnet", + "genesis_height": 0, + "num_block_producer_seats": 50, + "num_block_producer_seats_per_shard": [ + 50 + ], + "avg_hidden_validator_seats_per_shard": [ + 0 + ], + "dynamic_resharding": false, + "protocol_upgrade_stake_threshold": [ + 4, + 5 + ], + "protocol_upgrade_num_epochs": 2, + "epoch_length": 60, + "gas_limit": 1000000000000000, + "min_gas_price": "100000000", + "max_gas_price": "10000000000000000000000", + "block_producer_kickout_threshold": 90, + "chunk_producer_kickout_threshold": 90, + "online_min_threshold": [ + 9, + 10 + ], + "online_max_threshold": [ + 99, + 100 + ], + "gas_price_adjustment_rate": [ + 1, + 100 + ], + "validators": [ + { + "account_id": "test.near", + "public_key": "ed25519:Gc4yTakj3QVm5T9XpsFNooVKBxXcYnhnuQdGMXf5Hjcf", + "amount": "50000000000000000000000000000000" + } + ], + "transaction_validity_period": 100, + "protocol_reward_rate": [ + 1, + 10 + ], + "max_inflation_rate": [ + 1, + 20 + ], + "total_supply": "2050000000000000000000000000000000", + "num_blocks_per_year": 31536000, + "protocol_treasury_account": "test.near", + "fishermen_threshold": "10000000000000000000000000", + "minimum_stake_divisor": 10, + "shard_layout": { + "V0": { + "num_shards": 1, + "version": 0 + } + }, + "num_chunk_only_producer_seats": 300, + "minimum_validators_per_shard": 1, + "max_kickout_stake_perc": 100, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "use_production_config": false, + "records": [ + { + "Account": { + "account_id": "test.near", + "account": { + "amount": "1000000000000000000000000000000000", + "locked": "50000000000000000000000000000000", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 0, + "version": "V1" + } + } + }, + { + "AccessKey": { + "account_id": "test.near", + "public_key": "ed25519:Gc4yTakj3QVm5T9XpsFNooVKBxXcYnhnuQdGMXf5Hjcf", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "Account": { + "account_id": "near", + "account": { + "amount": "1000000000000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 0, + "version": "V1" + } + } + }, + { + "AccessKey": { + "account_id": "near", + "public_key": "ed25519:546XB2oHhj7PzUKHiH9Xve3Ze5q1JiW2WTh6abXFED3c", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + } + ] + }"#; + let genesis = + serde_json::from_str::(&genesis_str).expect("Failed to deserialize Genesis"); + genesis.validate(GenesisValidationMode::Full).expect("Failed to validate Genesis"); + } + + #[test] + fn test_loading_localnet_genesis_without_records() { + let genesis_str = r#"{ + "protocol_version": 57, + "genesis_time": "2022-11-15T05:17:59.578706Z", + "chain_id": "localnet", + "genesis_height": 0, + "num_block_producer_seats": 50, + "num_block_producer_seats_per_shard": [ + 50 + ], + "avg_hidden_validator_seats_per_shard": [ + 0 + ], + "dynamic_resharding": false, + "protocol_upgrade_stake_threshold": [ + 4, + 5 + ], + "protocol_upgrade_num_epochs": 2, + "epoch_length": 60, + "gas_limit": 1000000000000000, + "min_gas_price": "100000000", + "max_gas_price": "10000000000000000000000", + "block_producer_kickout_threshold": 90, + "chunk_producer_kickout_threshold": 90, + "online_min_threshold": [ + 9, + 10 + ], + "online_max_threshold": [ + 99, + 100 + ], + "gas_price_adjustment_rate": [ + 1, + 100 + ], + "validators": [ + { + "account_id": "test.near", + "public_key": "ed25519:Gc4yTakj3QVm5T9XpsFNooVKBxXcYnhnuQdGMXf5Hjcf", + "amount": "50000000000000000000000000000000" + } + ], + "transaction_validity_period": 100, + "protocol_reward_rate": [ + 1, + 10 + ], + "max_inflation_rate": [ + 1, + 20 + ], + "total_supply": "2050000000000000000000000000000000", + "num_blocks_per_year": 31536000, + "protocol_treasury_account": "test.near", + "fishermen_threshold": "10000000000000000000000000", + "minimum_stake_divisor": 10, + "shard_layout": { + "V0": { + "num_shards": 1, + "version": 0 + } + }, + "num_chunk_only_producer_seats": 300, + "minimum_validators_per_shard": 1, + "max_kickout_stake_perc": 100, + "minimum_stake_ratio": [ + 1, + 6250 + ], + "use_production_config": false + }"#; + let _genesis = + serde_json::from_str::(&genesis_str).expect("Failed to deserialize Genesis"); + } +} diff --git a/core/chain-configs/src/genesis_validate.rs b/core/chain-configs/src/genesis_validate.rs new file mode 100644 index 000000000..3bbe061bf --- /dev/null +++ b/core/chain-configs/src/genesis_validate.rs @@ -0,0 +1,323 @@ +use crate::genesis_config::{Genesis, GenesisConfig, GenesisContents}; +use unc_config_utils::{ValidationError, ValidationErrors}; +use unc_crypto::key_conversion::is_valid_staking_key; +use unc_primitives::state_record::StateRecord; +use unc_primitives::types::AccountId; +use num_rational::Rational32; +use std::collections::{HashMap, HashSet}; + +/// Validate genesis config and records. Returns ValidationError if semantic checks of genesis failed. +pub fn validate_genesis(genesis: &Genesis) -> Result<(), ValidationError> { + if let GenesisContents::StateRoots { .. } = &genesis.contents { + // TODO(robin-near): We don't have a great way of validating the + // genesis records if we're given state roots directly, though we + // could still validate things that aren't related to records. + return Ok(()); + } + let mut validation_errors = ValidationErrors::new(); + let mut genesis_validator = GenesisValidator::new(&genesis.config, &mut validation_errors); + tracing::info!(target: "config", "Validating Genesis config and records. This could take a few minutes..."); + genesis.for_each_record(|record: &StateRecord| { + genesis_validator.process_record(record); + }); + genesis_validator.validate_processed_records(); + genesis_validator.result_with_full_error() +} + +struct GenesisValidator<'a> { + genesis_config: &'a GenesisConfig, + total_supply: u128, + staked_accounts: HashMap, + account_ids: HashSet, + access_key_account_ids: HashSet, + contract_account_ids: HashSet, + validation_errors: &'a mut ValidationErrors, +} + +impl<'a> GenesisValidator<'a> { + pub fn new( + genesis_config: &'a GenesisConfig, + validation_errors: &'a mut ValidationErrors, + ) -> Self { + Self { + genesis_config, + total_supply: 0, + staked_accounts: HashMap::new(), + account_ids: HashSet::new(), + access_key_account_ids: HashSet::new(), + contract_account_ids: HashSet::new(), + validation_errors: validation_errors, + } + } + + pub fn process_record(&mut self, record: &StateRecord) { + match record { + StateRecord::Account { account_id, account } => { + if self.account_ids.contains(account_id) { + let error_message = + format!("Duplicate account id {} in genesis records", account_id); + self.validation_errors.push_genesis_semantics_error(error_message) + } + self.total_supply += account.locked() + account.amount(); + self.account_ids.insert(account_id.clone()); + if account.locked() > 0 { + self.staked_accounts.insert(account_id.clone(), account.locked()); + } + } + StateRecord::AccessKey { account_id, .. } => { + self.access_key_account_ids.insert(account_id.clone()); + } + StateRecord::Contract { account_id, .. } => { + if self.contract_account_ids.contains(account_id) { + let error_message = + format!("account {} has more than one contract deployed", account_id); + self.validation_errors.push_genesis_semantics_error(error_message) + } + self.contract_account_ids.insert(account_id.clone()); + } + _ => {} + } + } + + pub fn validate_processed_records(&mut self) { + let validators = self + .genesis_config + .validators + .clone() + .into_iter() + .map(|account_info| { + if !is_valid_staking_key(&account_info.public_key) { + let error_message = format!("validator staking key is not valid"); + self.validation_errors.push_genesis_semantics_error(error_message); + } + (account_info.account_id, account_info.amount) + }) + .collect::>(); + + if validators.len() != self.genesis_config.validators.len() { + let error_message = format!("Duplicate account in validators. The number of account_ids: {} does not match the number of validators: {}.", self.account_ids.len(), validators.len()); + self.validation_errors.push_genesis_semantics_error(error_message) + } + + if validators.is_empty() { + let error_message = format!("No validators in genesis"); + self.validation_errors.push_genesis_semantics_error(error_message) + } + + if self.total_supply != self.genesis_config.total_supply { + let error_message = format!("wrong total supply. account.locked() + account.amount() = {} is not equal to the total supply = {} specified in genesis config.", self.total_supply, self.genesis_config.total_supply); + self.validation_errors.push_genesis_semantics_error(error_message) + } + + if validators != self.staked_accounts { + let error_message = format!("Validator accounts do not match staked accounts."); + self.validation_errors.push_genesis_semantics_error(error_message) + } + + for account_id in &self.access_key_account_ids { + if !self.account_ids.contains(account_id) { + let error_message = format!("access key account {} does not exist", account_id); + self.validation_errors.push_genesis_semantics_error(error_message) + } + } + + for account_id in &self.contract_account_ids { + if !self.account_ids.contains(account_id) { + let error_message = format!("contract account {} does not exist,", account_id); + self.validation_errors.push_genesis_semantics_error(error_message) + } + } + + if self.genesis_config.online_max_threshold <= self.genesis_config.online_min_threshold { + let error_message = format!( + "Online max threshold {} smaller than min threshold {}", + self.genesis_config.online_max_threshold, self.genesis_config.online_min_threshold + ); + self.validation_errors.push_genesis_semantics_error(error_message) + } + + if self.genesis_config.online_max_threshold > Rational32::from_integer(1) { + let error_message = format!( + "Online max threshold must be less or equal than 1, but current value is {}", + self.genesis_config.online_max_threshold + ); + self.validation_errors.push_genesis_semantics_error(error_message) + } + + if *self.genesis_config.online_max_threshold.numer() >= 10_000_000 { + let error_message = + format!("online_max_threshold's numerator is too large, may lead to overflow."); + self.validation_errors.push_genesis_semantics_error(error_message) + } + + if *self.genesis_config.online_min_threshold.numer() >= 10_000_000 { + let error_message = + format!("online_min_threshold's numerator is too large, may lead to overflow."); + self.validation_errors.push_genesis_semantics_error(error_message) + } + + if *self.genesis_config.online_max_threshold.denom() >= 10_000_000 { + let error_message = + format!("online_max_threshold's denominator is too large, may lead to overflow."); + self.validation_errors.push_genesis_semantics_error(error_message) + } + + if *self.genesis_config.online_min_threshold.denom() >= 10_000_000 { + let error_message = + format!("online_min_threshold's denominator is too large, may lead to overflow."); + self.validation_errors.push_genesis_semantics_error(error_message) + } + + if self.genesis_config.gas_price_adjustment_rate >= Rational32::from_integer(1) { + let error_message = format!( + "Gas price adjustment rate must be less than 1, value in config is {}", + self.genesis_config.gas_price_adjustment_rate + ); + self.validation_errors.push_genesis_semantics_error(error_message) + } + + if self.genesis_config.epoch_length == 0 { + let error_message = format!("Epoch Length must be greater than 0"); + self.validation_errors.push_genesis_semantics_error(error_message) + } + } + + fn result_with_full_error(&self) -> Result<(), ValidationError> { + if self.validation_errors.is_empty() { + Ok(()) + } else { + let full_error = self.validation_errors.generate_error_message_per_type().unwrap(); + Err(ValidationError::GenesisSemanticsError { error_message: full_error }) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + use crate::GenesisRecords; + use unc_crypto::{KeyType, PublicKey}; + use unc_primitives::account::{AccessKey, Account}; + use unc_primitives::types::AccountInfo; + + const VALID_ED25519_RISTRETTO_KEY: &str = "ed25519:KuTCtARNzxZQ3YvXDeLjx83FDqxv2SdQTSbiq876zR7"; + + fn create_account() -> Account { + Account::new(100, 10, 5, Default::default(), 0) + } + + #[test] + #[should_panic(expected = "wrong total supply")] + fn test_total_supply_not_match() { + let mut config = GenesisConfig::default(); + config.validators = vec![AccountInfo { + account_id: "test".parse().unwrap(), + public_key: VALID_ED25519_RISTRETTO_KEY.parse().unwrap(), + amount: 10, + power: 10, + locked: 5, + }]; + let records = GenesisRecords(vec![StateRecord::Account { + account_id: "test".parse().unwrap(), + account: create_account(), + }]); + let genesis = &Genesis::new(config, records).unwrap(); + validate_genesis(genesis).unwrap(); + } + + #[test] + #[should_panic(expected = "validator staking key is not valid")] + fn test_invalid_staking_key() { + let mut config = GenesisConfig::default(); + config.validators = vec![AccountInfo { + account_id: "test".parse().unwrap(), + public_key: PublicKey::empty(KeyType::ED25519), + amount: 10, + power: 10, + }]; + let records = GenesisRecords(vec![StateRecord::Account { + account_id: "test".parse().unwrap(), + account: create_account(), + }]); + let genesis = &Genesis::new(config, records).unwrap(); + validate_genesis(genesis).unwrap(); + } + + #[test] + #[should_panic(expected = "Validator accounts do not match staked accounts")] + fn test_validator_not_match() { + let mut config = GenesisConfig::default(); + config.validators = vec![AccountInfo { + account_id: "test".parse().unwrap(), + public_key: VALID_ED25519_RISTRETTO_KEY.parse().unwrap(), + amount: 100, + power: 100, + }]; + config.total_supply = 110; + let records = GenesisRecords(vec![StateRecord::Account { + account_id: "test".parse().unwrap(), + account: create_account(), + }]); + let genesis = &Genesis::new(config, records).unwrap(); + validate_genesis(genesis).unwrap(); + } + + #[test] + #[should_panic(expected = "No validators in genesis")] + fn test_empty_validator() { + let config = GenesisConfig::default(); + let records = GenesisRecords(vec![StateRecord::Account { + account_id: "test".parse().unwrap(), + account: create_account(), + }]); + let genesis = &Genesis::new(config, records).unwrap(); + validate_genesis(genesis).unwrap(); + } + + #[test] + #[should_panic(expected = "access key account test1 does not exist")] + fn test_access_key_with_nonexistent_account() { + let mut config = GenesisConfig::default(); + config.validators = vec![AccountInfo { + account_id: "test".parse().unwrap(), + public_key: VALID_ED25519_RISTRETTO_KEY.parse().unwrap(), + amount: 10, + power: 10, + }]; + config.total_supply = 110; + let records = GenesisRecords(vec![ + StateRecord::Account { account_id: "test".parse().unwrap(), account: create_account() }, + StateRecord::AccessKey { + account_id: "test1".parse().unwrap(), + public_key: PublicKey::empty(KeyType::ED25519), + access_key: AccessKey::full_access(), + }, + ]); + let genesis = &Genesis::new(config, records).unwrap(); + validate_genesis(genesis).unwrap(); + } + + #[test] + #[should_panic(expected = "account test has more than one contract deployed")] + fn test_more_than_one_contract() { + let mut config = GenesisConfig::default(); + config.validators = vec![AccountInfo { + account_id: "test".parse().unwrap(), + public_key: VALID_ED25519_RISTRETTO_KEY.parse().unwrap(), + amount: 10, + power: 10, + }]; + config.total_supply = 110; + let records = GenesisRecords(vec![ + StateRecord::Account { account_id: "test".parse().unwrap(), account: create_account() }, + StateRecord::Contract { account_id: "test".parse().unwrap(), code: [1, 2, 3].to_vec() }, + StateRecord::Contract { + account_id: "test".parse().unwrap(), + code: [1, 2, 3, 4].to_vec(), + }, + ]); + let genesis = &Genesis::new(config, records).unwrap(); + validate_genesis(genesis).unwrap(); + } +} diff --git a/core/chain-configs/src/lib.rs b/core/chain-configs/src/lib.rs new file mode 100644 index 000000000..4e8754cf8 --- /dev/null +++ b/core/chain-configs/src/lib.rs @@ -0,0 +1,28 @@ +mod client_config; +mod genesis_config; +pub mod genesis_validate; +#[cfg(feature = "metrics")] +mod metrics; +mod updateable_config; + +pub use client_config::{ + default_enable_multiline_logging, default_epoch_sync_enabled, + default_header_sync_expected_height_per_second, default_header_sync_initial_timeout, + default_header_sync_progress_timeout, default_header_sync_stall_ban_timeout, + default_log_summary_period, default_produce_chunk_add_transactions_time_limit, + default_state_sync, default_state_sync_enabled, default_state_sync_timeout, + default_sync_check_period, default_sync_height_threshold, default_sync_step_period, + default_transaction_pool_size_limit, default_trie_viewer_state_size_limit, + default_tx_routing_height_horizon, default_view_client_threads, + default_view_client_throttle_period, ClientConfig, DumpConfig, ExternalStorageConfig, + ExternalStorageLocation, GCConfig, LogSummaryStyle, ReshardingConfig, ReshardingHandle, + StateSyncConfig, SyncConfig, DEFAULT_GC_NUM_EPOCHS_TO_KEEP, + DEFAULT_STATE_SYNC_NUM_CONCURRENT_REQUESTS_EXTERNAL, + DEFAULT_STATE_SYNC_NUM_CONCURRENT_REQUESTS_ON_CATCHUP_EXTERNAL, MIN_GC_NUM_EPOCHS_TO_KEEP, + TEST_STATE_SYNC_TIMEOUT, +}; +pub use genesis_config::{ + get_initial_supply, stream_records_from_file, Genesis, GenesisChangeConfig, GenesisConfig, + GenesisContents, GenesisRecords, GenesisValidationMode, ProtocolConfig, ProtocolConfigView, +}; +pub use updateable_config::{MutableConfigValue, UpdateableClientConfig}; diff --git a/core/chain-configs/src/metrics.rs b/core/chain-configs/src/metrics.rs new file mode 100644 index 000000000..e039897a0 --- /dev/null +++ b/core/chain-configs/src/metrics.rs @@ -0,0 +1,11 @@ +use unc_o11y::metrics::{try_create_int_gauge_vec, IntGaugeVec}; +use once_cell::sync::Lazy; + +pub static CONFIG_MUTABLE_FIELD: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_config_mutable_field", + "Timestamp and value of a mutable config field", + &["field_name", "timestamp", "value"], + ) + .unwrap() +}); diff --git a/core/chain-configs/src/updateable_config.rs b/core/chain-configs/src/updateable_config.rs new file mode 100644 index 000000000..5783e29d0 --- /dev/null +++ b/core/chain-configs/src/updateable_config.rs @@ -0,0 +1,99 @@ +use unc_primitives::types::BlockHeight; +use serde::{Deserialize, Serialize, Serializer}; +use std::sync::{Arc, Mutex}; +use std::{fmt::Debug, time::Duration}; + +use crate::ReshardingConfig; + +/// A wrapper for a config value that can be updated while the node is running. +/// When initializing sub-objects (e.g. `ShardsManager`), please make sure to +/// pass this wrapper instead of passing a value from a single moment in time. +/// See `expected_shutdown` for an example how to use it. +#[derive(Clone, Debug)] +pub struct MutableConfigValue { + value: Arc>, + // For metrics. + // Mutable config values are exported to prometheus with labels [field_name][last_update][value]. + field_name: String, + #[cfg(feature = "metrics")] + // For metrics. + // Mutable config values are exported to prometheus with labels [field_name][last_update][value]. + last_update: chrono::DateTime, +} + +impl Serialize for MutableConfigValue { + /// Only include the value field of MutableConfigValue in serialized result + /// since field_name and last_update are only relevant for internal monitoring + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let to_string_result = serde_json::to_string(&self.value); + let value_str = to_string_result.unwrap_or("unable to serialize the value".to_string()); + serializer.serialize_str(&value_str) + } +} + +impl MutableConfigValue { + /// Initializes a value. + /// `field_name` is needed to export the config value as a prometheus metric. + pub fn new(val: T, field_name: &str) -> Self { + let res = Self { + value: Arc::new(Mutex::new(val)), + field_name: field_name.to_string(), + #[cfg(feature = "metrics")] + last_update: unc_primitives::static_clock::StaticClock::utc(), + }; + res.set_metric_value(val, 1); + res + } + + pub fn get(&self) -> T { + *self.value.lock().unwrap() + } + + pub fn update(&self, val: T) { + let mut lock = self.value.lock().unwrap(); + if *lock != val { + tracing::info!(target: "config", "Updated config field '{}' from {:?} to {:?}", self.field_name, *lock, val); + self.set_metric_value(*lock, 0); + *lock = val; + self.set_metric_value(val, 1); + } else { + tracing::info!(target: "config", "Mutable config field '{}' remains the same: {:?}", self.field_name, val); + } + } + + #[cfg(feature = "metrics")] + fn set_metric_value(&self, value: T, metric_value: i64) { + // Use field_name as a label to tell different mutable config values apart. + // Use timestamp as a label to give some idea to the node operator (or + // people helping them debug their node) when exactly and what values + // exactly were part of the config. + // Use the config value as a label to make this work with config values + // of any type: int, float, string or even a composite object. + crate::metrics::CONFIG_MUTABLE_FIELD + .with_label_values(&[ + &self.field_name, + &self.last_update.timestamp().to_string(), + &format!("{:?}", value), + ]) + .set(metric_value); + } + + #[cfg(not(feature = "metrics"))] + fn set_metric_value(&self, _value: T, _metric_value: i64) {} +} + +#[derive(Default, Clone, Serialize, Deserialize)] +/// A subset of Config that can be updated white the node is running. +pub struct UpdateableClientConfig { + /// Graceful shutdown at expected block height. + pub expected_shutdown: Option, + + // Configuration for resharding. + pub resharding_config: ReshardingConfig, + + /// Time limit for adding transactions in produce_chunk() + pub produce_chunk_add_transactions_time_limit: Option, +} diff --git a/core/crypto/Cargo.toml b/core/crypto/Cargo.toml new file mode 100644 index 000000000..1d419f800 --- /dev/null +++ b/core/crypto/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "unc-crypto" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "This is an internal crate for common cryptographic types" +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +blake2.workspace = true +borsh.workspace = true +bs58.workspace = true +c2-chacha.workspace = true +curve25519-dalek.workspace = true +derive_more.workspace = true +ed25519-dalek.workspace = true +hex.workspace = true +unc-account-id.workspace = true +once_cell.workspace = true +primitive-types.workspace = true +rand = "0.7" # TODO: this is probably wrong? +rsa.workspace = true +rsa-export.workspace = true +secp256k1.workspace = true +serde.workspace = true +serde_json.workspace = true +stdx.workspace = true +subtle.workspace = true +thiserror.workspace = true +unc-config-utils.workspace = true + +[dev-dependencies] +bolero.workspace = true +hex-literal.workspace = true +sha2.workspace = true +tempfile.workspace = true diff --git a/core/crypto/src/errors.rs b/core/crypto/src/errors.rs new file mode 100644 index 000000000..f466e5e20 --- /dev/null +++ b/core/crypto/src/errors.rs @@ -0,0 +1,53 @@ +use unc_account_id::AccountId; + +#[derive(Debug, Clone, thiserror::Error)] +pub enum ParseKeyTypeError { + #[error("unknown key type '{unknown_key_type}'")] + UnknownKeyType { unknown_key_type: String }, +} + +#[derive(Debug, Clone, thiserror::Error)] +pub enum ParseKeyError { + #[error("unknown key type '{unknown_key_type}'")] + UnknownKeyType { unknown_key_type: String }, + #[error("invalid key length: expected the input of {expected_length} bytes, but {received_length} was given")] + InvalidLength { expected_length: usize, received_length: usize }, + #[error("invalid key data: {error_message}")] + InvalidData { error_message: String }, +} + +impl From for ParseKeyError { + fn from(err: ParseKeyTypeError) -> Self { + match err { + ParseKeyTypeError::UnknownKeyType { unknown_key_type } => { + Self::UnknownKeyType { unknown_key_type } + } + } + } +} + +#[derive(Debug, Clone, thiserror::Error)] +pub enum ParseSignatureError { + #[error("unknown key type '{unknown_key_type}'")] + UnknownKeyType { unknown_key_type: String }, + #[error("invalid signature length: expected the input of {expected_length} bytes, but {received_length} was given")] + InvalidLength { expected_length: usize, received_length: usize }, + #[error("invalid signature data: {error_message}")] + InvalidData { error_message: String }, +} + +impl From for ParseSignatureError { + fn from(err: ParseKeyTypeError) -> Self { + match err { + ParseKeyTypeError::UnknownKeyType { unknown_key_type } => { + Self::UnknownKeyType { unknown_key_type } + } + } + } +} + +#[derive(Debug, Clone, thiserror::Error)] +pub enum ImplicitPublicKeyError { + #[error("'{account_id}' is not a NEAR-implicit account")] + AccountIsNotNearImplicit { account_id: AccountId }, +} diff --git a/core/crypto/src/hash.rs b/core/crypto/src/hash.rs new file mode 100644 index 000000000..13f15b0ce --- /dev/null +++ b/core/crypto/src/hash.rs @@ -0,0 +1,160 @@ +use crate::util::{Packable, Point, Scalar}; +use blake2::digest::generic_array::{typenum::U32, GenericArray}; +use blake2::digest::{BlockInput, FixedOutput, Reset, Update, VariableOutput}; +use blake2::VarBlake2b; + +pub use blake2::Blake2b as Hash512; + +#[derive(Clone)] +pub struct Hash256(VarBlake2b); + +impl Default for Hash256 { + fn default() -> Self { + Hash256(VarBlake2b::new(32).unwrap()) + } +} + +impl Update for Hash256 { + fn update(&mut self, data: impl AsRef<[u8]>) { + self.0.update(data); + } +} + +impl BlockInput for Hash256 { + type BlockSize = ::BlockSize; +} + +impl FixedOutput for Hash256 { + type OutputSize = U32; + + fn finalize_into(self, out: &mut GenericArray) { + self.0.finalize_variable(|s| { + out.copy_from_slice(&s[0..32]); + }); + } + + fn finalize_into_reset(&mut self, out: &mut GenericArray) { + self.0.finalize_variable_reset(|s| { + out.copy_from_slice(&s[0..32]); + }); + } +} + +impl Reset for Hash256 { + fn reset(&mut self) { + self.0.reset(); + } +} + +mod hashable_trait { + pub trait Hashable { + fn hash_into(self, digest: D) -> D; + } +} + +use hashable_trait::*; + +impl + ?Sized> Hashable for &T { + fn hash_into(self, digest: D) -> D { + digest.chain(self.as_ref()) + } +} + +impl Hashable for Point { + fn hash_into(self, digest: D) -> D { + digest.chain(self.pack()) + } +} + +impl Hashable for Scalar { + fn hash_into(self, digest: D) -> D { + digest.chain(self.pack()) + } +} + +pub fn _hash_new() -> D { + D::default() +} + +pub fn _hash_chain(digest: D, data: T) -> D { + data.hash_into(digest) +} + +pub fn _hash_result>(digest: D) -> [u8; 32] { + digest.finalize_fixed().into() +} + +pub fn _hash_to_scalar(hash: [u8; 32]) -> Scalar { + Scalar::from_bytes_mod_order(hash) +} + +macro_rules! hash_chain { + ($h:expr, $d:expr $(, $dd:expr)*) => { + hash_chain!($crate::hash::_hash_chain($h, $d) $(, $dd)*) + }; + ($h:expr) => { + $h + }; +} + +macro_rules! hash { + ($($d:expr),*) => { + $crate::hash::_hash_result(hash_chain!($crate::hash::_hash_new::<$crate::hash::Hash256>() $(, $d)*)) + }; +} + +macro_rules! hash_s { + ($($d:expr),*) => { + $crate::hash::_hash_to_scalar(hash!($($d),*)) + }; +} + +pub fn _prs_result(digest: Hash512) -> Scalar { + let res = digest.finalize_fixed(); + Scalar::from_bytes_mod_order_wide((&res[0..64]).try_into().unwrap()) +} + +macro_rules! prs { + ($($d:expr),*) => { + $crate::hash::_prs_result(hash_chain!($crate::hash::_hash_new::<$crate::hash::Hash512>() $(, $d)*)) + }; +} + +#[cfg(test)] +mod tests { + use super::*; + use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT as G; + use hex_literal::hex; + + #[test] + fn test_hashes() { + assert_eq!( + hash!(), + hex!("0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8") + ); + assert_eq!(hash!(b""), hash!()); + assert_eq!(hash!(b"", b""), hash!()); + assert_eq!( + hash_s!(), + Scalar::from_canonical_bytes(hex!( + "cc0fb71e1f068c41898b8252aed624d1d0e5df47778f7787faab45cdf12fe308" + )) + .unwrap() + ); + assert_eq!( + prs!(), + Scalar::from_canonical_bytes(hex!( + "4d31ff252ec727ffb194a0557482c659e4376e76e8148134678460cb24223e06" + )) + .unwrap() + ); + assert_eq!( + hash!(hash_s!()), + hex!("f32d6e5c532a11cee4ce38370622441ad181b72e3d68f042736e6ba3434c5b77") + ); + assert_eq!( + hash!(G), + hex!("0993bca60aa601325f1dc1959caf9ab0453cd395a2ad8229c7221d70d0904f0f") + ); + } +} diff --git a/core/crypto/src/key_conversion.rs b/core/crypto/src/key_conversion.rs new file mode 100644 index 000000000..f7ca66780 --- /dev/null +++ b/core/crypto/src/key_conversion.rs @@ -0,0 +1,60 @@ +use crate::{signature, vrf, PublicKey}; +use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint}; +use curve25519_dalek::ristretto::RistrettoPoint; +use std::mem::transmute; + +pub fn is_valid_staking_key(public_key: &PublicKey) -> bool { + // The valid staking key is ED25519, and can be converted to ristretto. + match public_key { + PublicKey::ED25519(key) => convert_public_key(key).is_some(), + _ => false, + } +} + +pub fn is_valid_challenge_key(public_key: &PublicKey) -> bool { + match public_key { + PublicKey::RSA(_) => true, + _ => false, + } +} + +pub fn convert_public_key(key: &signature::ED25519PublicKey) -> Option { + let ep: EdwardsPoint = CompressedEdwardsY::from_slice(&key.0).ok()?.decompress()?; + // All properly generated public keys are torsion-free. RistrettoPoint type can handle some values that are not torsion-free, but not all. + if !ep.is_torsion_free() { + return None; + } + // Unfortunately, dalek library doesn't provide a better way to do this. + let rp: RistrettoPoint = unsafe { transmute(ep) }; + Some(vrf::PublicKey(rp.compress().to_bytes(), rp)) +} + +pub fn convert_secret_key(key: &signature::ED25519SecretKey) -> vrf::SecretKey { + let b = <&[u8; 32]>::try_from(&key.0[..32]).unwrap(); + let s = ed25519_dalek::hazmat::ExpandedSecretKey::from(b).scalar; + vrf::SecretKey::from_scalar(s) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_conversion() { + for _ in 0..10 { + let kk = signature::SecretKey::from_random(signature::KeyType::ED25519); + let pk = match kk.public_key() { + signature::PublicKey::ED25519(k) => k, + _ => unreachable!(), + }; + let sk = match kk { + signature::SecretKey::ED25519(k) => k, + _ => unreachable!(), + }; + assert_eq!( + convert_secret_key(&sk).public_key().clone(), + convert_public_key(&pk).unwrap() + ); + } + } +} diff --git a/core/crypto/src/key_file.rs b/core/crypto/src/key_file.rs new file mode 100644 index 000000000..aa9e7c69c --- /dev/null +++ b/core/crypto/src/key_file.rs @@ -0,0 +1,114 @@ +use crate::{PublicKey, SecretKey}; +use unc_account_id::AccountId; +use std::fs::File; +use std::io; +use std::io::{Read, Write}; +use std::path::Path; + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct KeyFile { + pub account_id: AccountId, + pub public_key: PublicKey, + // Credential files generated which near cli works with have private_key + // rather than secret_key field. To make it possible to read those from + // uncd add private_key as an alias to this field so either will work. + #[serde(alias = "private_key")] + pub secret_key: SecretKey, +} + +impl KeyFile { + pub fn write_to_file(&self, path: &Path) -> io::Result<()> { + let data = serde_json::to_string_pretty(self)?; + let mut file = Self::create(path)?; + file.write_all(data.as_bytes()) + } + + #[cfg(unix)] + fn create(path: &Path) -> io::Result { + use std::os::unix::fs::OpenOptionsExt; + std::fs::File::options().mode(0o600).write(true).create(true).open(path) + } + + #[cfg(not(unix))] + fn create(path: &Path) -> io::Result { + std::fs::File::create(path) + } + + pub fn from_file(path: &Path) -> io::Result { + let mut file = File::open(path)?; + let mut json_config_str = String::new(); + file.read_to_string(&mut json_config_str)?; + let json_str_without_comments: String = + unc_config_utils::strip_comments_from_json_str(&json_config_str)?; + + Ok(serde_json::from_str(&json_str_without_comments)?) + } +} + +#[cfg(test)] +mod test { + use super::*; + + const ACCOUNT_ID: &str = "example"; + const SECRET_KEY: &str = "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr"; + const KEY_FILE_CONTENTS: &str = r#"{ + "account_id": "example", + "public_key": "ed25519:6DSjZ8mvsRZDvFqFxo8tCKePG96omXW7eVYVSySmDk8e", + "secret_key": "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr" +}"#; + + #[test] + fn test_to_file() { + let tmp = tempfile::TempDir::new().unwrap(); + let path = tmp.path().join("key-file"); + + let account_id = ACCOUNT_ID.parse().unwrap(); + let secret_key: SecretKey = SECRET_KEY.parse().unwrap(); + let public_key = secret_key.public_key(); + let key = KeyFile { account_id, public_key, secret_key }; + key.write_to_file(&path).unwrap(); + + assert_eq!(KEY_FILE_CONTENTS, std::fs::read_to_string(&path).unwrap()); + + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let got = std::fs::metadata(&path).unwrap().permissions().mode(); + assert_eq!(0o600, got & 0o777); + } + } + + #[test] + fn test_from_file() { + fn load(contents: &[u8]) -> io::Result<()> { + let tmp = tempfile::NamedTempFile::new().unwrap(); + tmp.as_file().write_all(contents).unwrap(); + let result = KeyFile::from_file(tmp.path()); + tmp.close().unwrap(); + + result.map(|key| { + assert_eq!(ACCOUNT_ID, key.account_id.to_string()); + let secret_key: SecretKey = SECRET_KEY.parse().unwrap(); + assert_eq!(secret_key, key.secret_key); + assert_eq!(secret_key.public_key(), key.public_key); + }) + } + + load(KEY_FILE_CONTENTS.as_bytes()).unwrap(); + + // Test private_key alias for secret_key works. + let contents = KEY_FILE_CONTENTS.replace("secret_key", "private_key"); + load(contents.as_bytes()).unwrap(); + + // Test private_key is mutually exclusive with secret_key. + let err = load(br#"{ + "account_id": "example", + "public_key": "ed25519:6DSjZ8mvsRZDvFqFxo8tCKePG96omXW7eVYVSySmDk8e", + "secret_key": "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr", + "private_key": "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr" + }"#).unwrap_err(); + assert_eq!(err.kind(), io::ErrorKind::InvalidData); + let inner_msg = err.into_inner().unwrap().to_string(); + assert!(inner_msg.contains("duplicate field")); + } +} diff --git a/core/crypto/src/lib.rs b/core/crypto/src/lib.rs new file mode 100644 index 000000000..36bea0778 --- /dev/null +++ b/core/crypto/src/lib.rs @@ -0,0 +1,25 @@ +#![deny(clippy::arithmetic_side_effects)] + +pub use errors::{ParseKeyError, ParseKeyTypeError, ParseSignatureError}; +pub use key_file::KeyFile; +pub use signature::{ + ED25519PublicKey, ED25519SecretKey, KeyType, PublicKey, Secp256K1PublicKey, Secp256K1Signature, + Rsa2048PublicKey, Rsa2048Signature, + SecretKey, Signature, +}; +pub use signer::{EmptySigner, InMemorySigner, Signer}; + +#[macro_use] +mod hash; +#[macro_use] +mod traits; +#[macro_use] +mod util; + +mod errors; +pub mod key_conversion; +mod key_file; +mod signature; +mod signer; +mod test_utils; +pub mod vrf; diff --git a/core/crypto/src/signature.rs b/core/crypto/src/signature.rs new file mode 100644 index 000000000..e6c23856f --- /dev/null +++ b/core/crypto/src/signature.rs @@ -0,0 +1,1089 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use ed25519_dalek::ed25519::signature::{Signer, Verifier}; +use once_cell::sync::Lazy; +use primitive_types::U256; +use secp256k1::rand::rngs::OsRng; +use secp256k1::Message; +use std::convert::AsRef; +use std::fmt::{Debug, Display, Formatter}; +use std::hash::{Hash, Hasher}; +use std::io::{Error, ErrorKind, Read, Write}; +use std::str::FromStr; +use rsa::Pkcs1v15Sign; +use rsa::pkcs8::{EncodePrivateKey, EncodePublicKey, DecodePublicKey, DecodePrivateKey}; + +pub static SECP256K1: Lazy> = + Lazy::new(secp256k1::Secp256k1::new); + +#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)] +#[cfg_attr(test, derive(bolero::TypeGenerator))] +pub enum KeyType { + ED25519 = 0, + SECP256K1 = 1, + RSA2048 = 2, +} + +impl Display for KeyType { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + f.write_str(match self { + KeyType::ED25519 => "ed25519", + KeyType::SECP256K1 => "secp256k1", + KeyType::RSA2048 => "rsa2048", + }) + } +} + +impl FromStr for KeyType { + type Err = crate::errors::ParseKeyTypeError; + + fn from_str(value: &str) -> Result { + let lowercase_key_type = value.to_ascii_lowercase(); + match lowercase_key_type.as_str() { + "ed25519" => Ok(KeyType::ED25519), + "secp256k1" => Ok(KeyType::SECP256K1), + "rsa2048" => Ok(KeyType::RSA2048), + _ => Err(Self::Err::UnknownKeyType { unknown_key_type: lowercase_key_type }), + } + } +} + +impl TryFrom for KeyType { + type Error = crate::errors::ParseKeyTypeError; + + fn try_from(value: u8) -> Result { + match value { + 0_u8 => Ok(KeyType::ED25519), + 1_u8 => Ok(KeyType::SECP256K1), + 2_u8 => Ok(KeyType::RSA2048), + unknown_key_type => { + Err(Self::Error::UnknownKeyType { unknown_key_type: unknown_key_type.to_string() }) + } + } + } +} + +fn split_key_type_data(value: &str) -> Result<(KeyType, &str), crate::errors::ParseKeyTypeError> { + if let Some((prefix, key_data)) = value.split_once(':') { + Ok((KeyType::from_str(prefix)?, key_data)) + } else { + // If there is no prefix then we Default to ED25519. + Ok((KeyType::ED25519, value)) + } +} + +// RSA +const RAW_PUBLIC_KEY_RSA_2048_LENGTH: usize = 294; +#[derive(Clone, Eq, Ord, PartialEq, PartialOrd, derive_more::AsRef, derive_more::From)] +#[cfg_attr(test, derive(bolero::TypeGenerator))] +#[as_ref(forward)] +pub struct Rsa2048PublicKey([u8; RAW_PUBLIC_KEY_RSA_2048_LENGTH]); + +impl TryFrom<&[u8]> for crate::Rsa2048PublicKey { + type Error = crate::errors::ParseKeyError; + + fn try_from(data: &[u8]) -> Result { + data.try_into().map(Self).map_err(|_| Self::Error::InvalidLength { + expected_length: RAW_PUBLIC_KEY_RSA_2048_LENGTH, + received_length: data.len(), + }) + } +} + +impl std::fmt::Debug for crate::Rsa2048PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + Display::fmt(&Bs58(&self.0), f) + } +} + +// SECP256K1 +const PUBLIC_KEY_SECP256K1_LENGTH: usize = 64; + +#[derive(Clone, Eq, Ord, PartialEq, PartialOrd, derive_more::AsRef, derive_more::From)] +#[cfg_attr(test, derive(bolero::TypeGenerator))] +#[as_ref(forward)] +pub struct Secp256K1PublicKey([u8; PUBLIC_KEY_SECP256K1_LENGTH]); + +impl TryFrom<&[u8]> for Secp256K1PublicKey { + type Error = crate::errors::ParseKeyError; + + fn try_from(data: &[u8]) -> Result { + data.try_into().map(Self).map_err(|_| Self::Error::InvalidLength { + expected_length: PUBLIC_KEY_SECP256K1_LENGTH, + received_length: data.len(), + }) + } +} + +impl std::fmt::Debug for Secp256K1PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + Display::fmt(&Bs58(&self.0), f) + } +} + +#[derive(Clone, Eq, Ord, PartialEq, PartialOrd, derive_more::AsRef, derive_more::From)] +#[cfg_attr(test, derive(bolero::TypeGenerator))] +#[as_ref(forward)] +pub struct ED25519PublicKey(pub [u8; ed25519_dalek::PUBLIC_KEY_LENGTH]); + +impl TryFrom<&[u8]> for ED25519PublicKey { + type Error = crate::errors::ParseKeyError; + + fn try_from(data: &[u8]) -> Result { + data.try_into().map(Self).map_err(|_| Self::Error::InvalidLength { + expected_length: ed25519_dalek::PUBLIC_KEY_LENGTH, + received_length: data.len(), + }) + } +} + +impl std::fmt::Debug for ED25519PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + Display::fmt(&Bs58(&self.0), f) + } +} + +/// Public key container supporting different curves. +#[derive(Clone, PartialEq, PartialOrd, Ord, Eq)] +#[cfg_attr(test, derive(bolero::TypeGenerator))] +pub enum PublicKey { + /// 256 bit elliptic curve based public-key. + ED25519(ED25519PublicKey), + /// 512 bit elliptic curve based public-key used in Bitcoin's public-key cryptography. + SECP256K1(Secp256K1PublicKey), + /// 2048 bit rsa + RSA(Rsa2048PublicKey), +} + +impl PublicKey { + // `is_empty` always returns false, so there is no point in adding it + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + const ED25519_LEN: usize = ed25519_dalek::PUBLIC_KEY_LENGTH + 1; + match self { + Self::ED25519(_) => ED25519_LEN, + Self::SECP256K1(_) => PUBLIC_KEY_SECP256K1_LENGTH + 1, + Self::RSA(_) => RAW_PUBLIC_KEY_RSA_2048_LENGTH + 1, + } + } + + pub fn empty(key_type: KeyType) -> Self { + match key_type { + KeyType::ED25519 => { + PublicKey::ED25519(ED25519PublicKey([0u8; ed25519_dalek::PUBLIC_KEY_LENGTH])) + } + KeyType::SECP256K1 => PublicKey::SECP256K1(Secp256K1PublicKey([0u8; PUBLIC_KEY_SECP256K1_LENGTH])), + KeyType::RSA2048 => PublicKey::RSA(Rsa2048PublicKey([0u8; RAW_PUBLIC_KEY_RSA_2048_LENGTH])), + } + } + + pub fn key_type(&self) -> KeyType { + match self { + Self::ED25519(_) => KeyType::ED25519, + Self::SECP256K1(_) => KeyType::SECP256K1, + Self::RSA(_) => KeyType::RSA2048, + } + } + + pub fn key_data(&self) -> &[u8] { + match self { + Self::ED25519(key) => key.as_ref(), + Self::SECP256K1(key) => key.as_ref(), + Self::RSA(key) => key.as_ref(), + } + } + + pub fn unwrap_as_ed25519(&self) -> &ED25519PublicKey { + match self { + Self::ED25519(key) => key, + _ => panic!(), + } + } + + pub fn unwrap_as_secp256k1(&self) -> &Secp256K1PublicKey { + match self { + Self::SECP256K1(key) => key, + _ => panic!(), + } + } + + pub fn unwrap_as_rsa2048(&self) -> &Rsa2048PublicKey { + match self { + Self::RSA(key) => key, + _ => panic!(), + } + } +} + +// This `Hash` implementation is safe since it retains the property +// `k1 == k2 ⇒ hash(k1) == hash(k2)`. +impl Hash for PublicKey { + fn hash(&self, state: &mut H) { + match self { + PublicKey::ED25519(public_key) => { + state.write_u8(0u8); + state.write(&public_key.0); + } + PublicKey::SECP256K1(public_key) => { + state.write_u8(1u8); + state.write(&public_key.0); + } + PublicKey::RSA(public_key) => { + state.write_u8(2u8); + state.write(&public_key.0); + } + } + } +} + +impl Display for PublicKey { + fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result { + let (key_type, key_data) = match self { + PublicKey::ED25519(public_key) => (KeyType::ED25519, &public_key.0[..]), + PublicKey::SECP256K1(public_key) => (KeyType::SECP256K1, &public_key.0[..]), + PublicKey::RSA(public_key) => (KeyType::RSA2048, &public_key.0[..]), + }; + write!(fmt, "{}:{}", key_type, Bs58(key_data)) + } +} + +impl Debug for PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + Display::fmt(self, f) + } +} + +impl BorshSerialize for PublicKey { + fn serialize(&self, writer: &mut W) -> Result<(), Error> { + match self { + PublicKey::ED25519(public_key) => { + BorshSerialize::serialize(&0u8, writer)?; + writer.write_all(&public_key.0)?; + } + PublicKey::SECP256K1(public_key) => { + BorshSerialize::serialize(&1u8, writer)?; + writer.write_all(&public_key.0)?; + } + PublicKey::RSA(public_key) => { + BorshSerialize::serialize(&2u8, writer)?; + writer.write_all(&public_key.0)?; + } + } + Ok(()) + } +} + +impl BorshDeserialize for PublicKey { + fn deserialize_reader(rd: &mut R) -> std::io::Result { + let key_type = KeyType::try_from(u8::deserialize_reader(rd)?) + .map_err(|err| Error::new(ErrorKind::InvalidData, err.to_string()))?; + match key_type { + KeyType::ED25519 => { + Ok(PublicKey::ED25519(ED25519PublicKey(BorshDeserialize::deserialize_reader(rd)?))) + } + KeyType::SECP256K1 => Ok(PublicKey::SECP256K1(Secp256K1PublicKey( + BorshDeserialize::deserialize_reader(rd)?, + ))), + KeyType::RSA2048 => Ok(PublicKey::RSA(Rsa2048PublicKey( + BorshDeserialize::deserialize_reader(rd)?, + ))), + } + } +} + +impl serde::Serialize for PublicKey { + fn serialize( + &self, + serializer: S, + ) -> Result<::Ok, ::Error> + where + S: serde::Serializer, + { + serializer.collect_str(self) + } +} + +impl<'de> serde::Deserialize<'de> for PublicKey { + fn deserialize(deserializer: D) -> Result>::Error> + where + D: serde::Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + s.parse() + .map_err(|err: crate::errors::ParseKeyError| serde::de::Error::custom(err.to_string())) + } +} + +impl FromStr for PublicKey { + type Err = crate::errors::ParseKeyError; + + fn from_str(value: &str) -> Result { + let (key_type, key_data) = split_key_type_data(value)?; + Ok(match key_type { + KeyType::ED25519 => Self::ED25519(ED25519PublicKey(decode_bs58(key_data)?)), + KeyType::SECP256K1 => Self::SECP256K1(Secp256K1PublicKey(decode_bs58(key_data)?)), + KeyType::RSA2048 => Self::RSA(Rsa2048PublicKey(decode_bs58(key_data)?)), + }) + } +} + +impl From for PublicKey { + fn from(ed25519: ED25519PublicKey) -> Self { + Self::ED25519(ed25519) + } +} + +impl From for PublicKey { + fn from(secp256k1: Secp256K1PublicKey) -> Self { + Self::SECP256K1(secp256k1) + } +} + +impl From for PublicKey { + fn from(rsa2048: Rsa2048PublicKey) -> Self { + Self::RSA(rsa2048) + } +} + +#[derive(Clone, Eq)] +// This is actually a keypair, because ed25519_dalek api only has keypair.sign +// From ed25519_dalek doc: The first SECRET_KEY_LENGTH of bytes is the SecretKey +// The last PUBLIC_KEY_LENGTH of bytes is the public key, in total it's KEYPAIR_LENGTH +pub struct ED25519SecretKey(pub [u8; ed25519_dalek::KEYPAIR_LENGTH]); + +impl PartialEq for ED25519SecretKey { + fn eq(&self, other: &Self) -> bool { + self.0[..ed25519_dalek::SECRET_KEY_LENGTH] == other.0[..ed25519_dalek::SECRET_KEY_LENGTH] + } +} + +impl std::fmt::Debug for ED25519SecretKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + Display::fmt(&Bs58(&self.0[..ed25519_dalek::SECRET_KEY_LENGTH]), f) + } +} + + +pub(crate) const PRIVTAE_KEY_DEFAULT_RSA_KEY_BITS: usize = 2048; + +/// Secret key container supporting different curves. +#[derive(Clone, Eq, PartialEq, Debug)] +pub enum SecretKey { + ED25519(ED25519SecretKey), + SECP256K1(secp256k1::SecretKey), + RSA(rsa::RsaPrivateKey), +} + +impl SecretKey { + pub fn key_type(&self) -> KeyType { + match self { + SecretKey::ED25519(_) => KeyType::ED25519, + SecretKey::SECP256K1(_) => KeyType::SECP256K1, + SecretKey::RSA(_) => KeyType::RSA2048, + } + } + + pub fn from_random(key_type: KeyType) -> SecretKey { + match key_type { + KeyType::ED25519 => { + let keypair = ed25519_dalek::SigningKey::generate(&mut OsRng); + SecretKey::ED25519(ED25519SecretKey(keypair.to_keypair_bytes())) + } + KeyType::SECP256K1 => SecretKey::SECP256K1(secp256k1::SecretKey::new(&mut OsRng)), + KeyType::RSA2048 => { + SecretKey::RSA(rsa::RsaPrivateKey::new(&mut OsRng, PRIVTAE_KEY_DEFAULT_RSA_KEY_BITS).unwrap()) + } + } + } + + pub fn sign(&self, data: &[u8]) -> Signature { + match &self { + SecretKey::ED25519(secret_key) => { + let keypair = ed25519_dalek::SigningKey::from_keypair_bytes(&secret_key.0).unwrap(); + Signature::ED25519(keypair.sign(data)) + } + + SecretKey::SECP256K1(secret_key) => { + let signature = SECP256K1.sign_ecdsa_recoverable( + &secp256k1::Message::from_slice(data).expect("32 bytes"), + secret_key, + ); + let (rec_id, data) = signature.serialize_compact(); + let mut buf = [0; 65]; + buf[0..64].copy_from_slice(&data[0..64]); + buf[64] = rec_id.to_i32() as u8; + Signature::SECP256K1(Secp256K1Signature(buf)) + } + SecretKey::RSA(secret_key) => { + let sign_data = secret_key.sign(Pkcs1v15Sign::new_unprefixed(), data).unwrap(); + Signature::RSA(Rsa2048Signature(<[u8; 256]>::try_from(sign_data.as_slice()).unwrap())) + } + + } + } + + pub fn public_key(&self) -> PublicKey { + match &self { + SecretKey::ED25519(secret_key) => PublicKey::ED25519(ED25519PublicKey( + secret_key.0[ed25519_dalek::SECRET_KEY_LENGTH..].try_into().unwrap(), + )), + SecretKey::SECP256K1(secret_key) => { + let pk = secp256k1::PublicKey::from_secret_key(&SECP256K1, secret_key); + let serialized = pk.serialize_uncompressed(); + let mut public_key = Secp256K1PublicKey([0; 64]); + public_key.0.copy_from_slice(&serialized[1..65]); + PublicKey::SECP256K1(public_key) + }, + SecretKey::RSA(secret_key) => { + let pk = secret_key.to_public_key(); + let mut public_key = [0; RAW_PUBLIC_KEY_RSA_2048_LENGTH]; + public_key.copy_from_slice(&pk.to_public_key_der().unwrap().as_bytes()); + PublicKey::RSA(Rsa2048PublicKey(public_key)) + } + } + } + + pub fn unwrap_as_ed25519(&self) -> &ED25519SecretKey { + match self { + SecretKey::ED25519(key) => key, + _ => panic!(), + } + } +} + +impl std::fmt::Display for SecretKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + SecretKey::ED25519(secret_key) => { + write!(f, "{}:{}", KeyType::ED25519, Bs58(&secret_key.0[..])) + }, + SecretKey::SECP256K1(secret_key) => { + write!(f, "{}:{}", KeyType::SECP256K1, Bs58(&secret_key[..])) + }, + SecretKey::RSA(secret_key) => { + // 先将 DER 编码的密钥存储在一个变量中 + let pkcs8_bytes = secret_key.to_pkcs8_der().unwrap().to_bytes(); + // 然后获取它的切片 + write!(f, "{}:{}", KeyType::RSA2048, Bs58(&pkcs8_bytes.as_slice())) + }, + } + } +} + +impl FromStr for SecretKey { + type Err = crate::errors::ParseKeyError; + + fn from_str(s: &str) -> Result { + let (key_type, key_data) = split_key_type_data(s)?; + Ok(match key_type { + KeyType::ED25519 => Self::ED25519(ED25519SecretKey(decode_bs58(key_data)?)), + KeyType::SECP256K1 => { + let data = decode_bs58::<{ secp256k1::constants::SECRET_KEY_SIZE }>(key_data)?; + let sk = secp256k1::SecretKey::from_slice(&data) + .map_err(|err| Self::Err::InvalidData { error_message: err.to_string() })?; + Self::SECP256K1(sk) + }, + KeyType::RSA2048 => { + let buffer = parse_bs58_data(2048, key_data)?; + let sk = rsa::RsaPrivateKey::from_pkcs8_der(&buffer) + .map_err(|err| Self::Err::InvalidData { error_message: err.to_string() })?; + Self::RSA(sk) + } + }) + } +} + +impl serde::Serialize for SecretKey { + fn serialize( + &self, + serializer: S, + ) -> Result<::Ok, ::Error> + where + S: serde::Serializer, + { + serializer.collect_str(self) + } +} + +impl<'de> serde::Deserialize<'de> for SecretKey { + fn deserialize(deserializer: D) -> Result>::Error> + where + D: serde::Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + Self::from_str(&s).map_err(|err| serde::de::Error::custom(err.to_string())) + } +} + +const SECP256K1_N: U256 = + U256([0xbfd25e8cd0364141, 0xbaaedce6af48a03b, 0xfffffffffffffffe, 0xffffffffffffffff]); + +// Half of SECP256K1_N + 1. +const SECP256K1_N_HALF_ONE: U256 = + U256([0xdfe92f46681b20a1, 0x5d576e7357a4501d, 0xffffffffffffffff, 0x7fffffffffffffff]); + +const SECP256K1_SIGNATURE_LENGTH: usize = 65; + +#[derive(Clone, Eq, PartialEq, Hash, derive_more::From, derive_more::Into)] +pub struct Secp256K1Signature([u8; SECP256K1_SIGNATURE_LENGTH]); + +impl Secp256K1Signature { + pub fn check_signature_values(&self, reject_upper: bool) -> bool { + let mut r_bytes = [0u8; 32]; + r_bytes.copy_from_slice(&self.0[0..32]); + let r = U256::from(r_bytes); + + let mut s_bytes = [0u8; 32]; + s_bytes.copy_from_slice(&self.0[32..64]); + let s = U256::from(s_bytes); + + let s_check = if reject_upper { + // Reject upper range of s values (ECDSA malleability) + SECP256K1_N_HALF_ONE + } else { + SECP256K1_N + }; + + r < SECP256K1_N && s < s_check + } + + pub fn recover( + &self, + msg: [u8; 32], + ) -> Result { + let recoverable_sig = secp256k1::ecdsa::RecoverableSignature::from_compact( + &self.0[0..64], + secp256k1::ecdsa::RecoveryId::from_i32(i32::from(self.0[64])).unwrap(), + ) + .map_err(|err| crate::errors::ParseSignatureError::InvalidData { + error_message: err.to_string(), + })?; + let msg = Message::from_slice(&msg).unwrap(); + + let res = SECP256K1 + .recover_ecdsa(&msg, &recoverable_sig) + .map_err(|err| crate::errors::ParseSignatureError::InvalidData { + error_message: err.to_string(), + })? + .serialize_uncompressed(); + + // Can not fail + let pk = Secp256K1PublicKey::try_from(&res[1..65]).unwrap(); + + Ok(pk) + } +} + +impl TryFrom<&[u8]> for Secp256K1Signature { + type Error = crate::errors::ParseSignatureError; + + fn try_from(data: &[u8]) -> Result { + Ok(Self(data.try_into().map_err(|_| Self::Error::InvalidLength { + expected_length: 65, + received_length: data.len(), + })?)) + } +} + +impl Debug for Secp256K1Signature { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + Display::fmt(&Bs58(&self.0), f) + } +} + + +// RSA Signature +const RSA2048_SIGNATURE_LENGTH: usize = 256; + +#[derive(Clone, Eq, PartialEq, Hash, derive_more::From, derive_more::Into)] +pub struct Rsa2048Signature([u8; RSA2048_SIGNATURE_LENGTH]); + +impl TryFrom<&[u8]> for Rsa2048Signature { + type Error = crate::errors::ParseSignatureError; + + fn try_from(data: &[u8]) -> Result { + Ok(Self(data.try_into().map_err(|_| Self::Error::InvalidLength { + expected_length: RSA2048_SIGNATURE_LENGTH, + received_length: data.len(), + })?)) + } +} + +impl Debug for Rsa2048Signature { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + Display::fmt(&Bs58(&self.0), f) + } +} + +/// Signature container supporting different curves. +#[derive(Clone, PartialEq, Eq)] +pub enum Signature { + ED25519(ed25519_dalek::Signature), + SECP256K1(Secp256K1Signature), + RSA(Rsa2048Signature), +} + +// This `Hash` implementation is safe since it retains the property +// `k1 == k2 ⇒ hash(k1) == hash(k2)`. +impl Hash for Signature { + fn hash(&self, state: &mut H) { + match self { + Signature::ED25519(sig) => sig.to_bytes().hash(state), + Signature::SECP256K1(sig) => sig.hash(state), + Signature::RSA(sig) => sig.hash(state), + }; + } +} + +impl Signature { + /// Construct Signature from key type and raw signature blob + pub fn from_parts( + signature_type: KeyType, + signature_data: &[u8], + ) -> Result { + match signature_type { + KeyType::ED25519 => Ok(Signature::ED25519(ed25519_dalek::Signature::from_bytes( + <&[u8; ed25519_dalek::SIGNATURE_LENGTH]>::try_from(signature_data).map_err( + |err| crate::errors::ParseSignatureError::InvalidData { + error_message: err.to_string(), + }, + )?, + ))), + KeyType::SECP256K1 => { + Ok(Signature::SECP256K1(Secp256K1Signature::try_from(signature_data).map_err( + |_| crate::errors::ParseSignatureError::InvalidData { + error_message: "invalid Secp256k1 signature length".to_string(), + }, + )?)) + } + KeyType::RSA2048 => Ok(Signature::RSA(Rsa2048Signature::try_from(signature_data).map_err( + |_| crate::errors::ParseSignatureError::InvalidData { + error_message: "invalid RSA2048 signature length".to_string(), + }, + )?)), + } + } + + /// Verifies that this signature is indeed signs the data with given public key. + /// Also if public key doesn't match on the curve returns `false`. + pub fn verify(&self, data: &[u8], public_key: &PublicKey) -> bool { + match (&self, public_key) { + (Signature::ED25519(signature), PublicKey::ED25519(public_key)) => { + match ed25519_dalek::VerifyingKey::from_bytes(&public_key.0) { + Err(_) => false, + Ok(public_key) => public_key.verify(data, signature).is_ok(), + } + } + (Signature::SECP256K1(signature), PublicKey::SECP256K1(public_key)) => { + let rec_id = + match secp256k1::ecdsa::RecoveryId::from_i32(i32::from(signature.0[64])) { + Ok(r) => r, + Err(_) => return false, + }; + let rsig = match secp256k1::ecdsa::RecoverableSignature::from_compact( + &signature.0[0..64], + rec_id, + ) { + Ok(r) => r, + Err(_) => return false, + }; + let sig = rsig.to_standard(); + let pdata: [u8; 65] = { + // code borrowed from https://github.com/openethereum/openethereum/blob/98b7c07171cd320f32877dfa5aa528f585dc9a72/ethkey/src/signature.rs#L210 + let mut temp = [4u8; 65]; + temp[1..65].copy_from_slice(&public_key.0); + temp + }; + let message = match secp256k1::Message::from_slice(data) { + Ok(m) => m, + Err(_) => return false, + }; + let pub_key = match secp256k1::PublicKey::from_slice(&pdata) { + Ok(p) => p, + Err(_) => return false, + }; + SECP256K1.verify_ecdsa(&message, &sig, &pub_key).is_ok() + } + (Signature::RSA(signature), PublicKey::RSA(public_key)) => { + let pk = rsa::RsaPublicKey::from_public_key_der(&public_key.0).unwrap(); + match pk.verify(Pkcs1v15Sign::new_unprefixed(), &data, signature.0.as_ref()) { + Ok(_) => true, + Err(_) => false, + } + } + + _ => false, + } + } + + pub fn key_type(&self) -> KeyType { + match self { + Signature::ED25519(_) => KeyType::ED25519, + Signature::SECP256K1(_) => KeyType::SECP256K1, + Signature::RSA(_) => KeyType::RSA2048, + } + } +} + +impl Default for Signature { + fn default() -> Self { + Signature::empty(KeyType::ED25519) + } +} + +impl BorshSerialize for Signature { + fn serialize(&self, writer: &mut W) -> Result<(), Error> { + match self { + Signature::ED25519(signature) => { + BorshSerialize::serialize(&0u8, writer)?; + writer.write_all(&signature.to_bytes())?; + } + Signature::SECP256K1(signature) => { + BorshSerialize::serialize(&1u8, writer)?; + writer.write_all(&signature.0)?; + } + Signature::RSA(signature) => { + BorshSerialize::serialize(&2u8, writer)?; + writer.write_all(&signature.0)?; + } + } + Ok(()) + } +} + +impl BorshDeserialize for Signature { + fn deserialize_reader(rd: &mut R) -> std::io::Result { + let key_type = KeyType::try_from(u8::deserialize_reader(rd)?) + .map_err(|err| Error::new(ErrorKind::InvalidData, err.to_string()))?; + match key_type { + KeyType::ED25519 => { + let array: [u8; ed25519_dalek::SIGNATURE_LENGTH] = + BorshDeserialize::deserialize_reader(rd)?; + // Sanity-check that was performed by ed25519-dalek in from_bytes before version 2, + // but was removed with version 2. It is not actually any good a check, but we have + // it here in case we need to keep backward compatibility. Maybe this check is not + // actually required, but please think carefully before removing it. + if array[ed25519_dalek::SIGNATURE_LENGTH - 1] & 0b1110_0000 != 0 { + return Err(Error::new(ErrorKind::InvalidData, "signature error")); + } + Ok(Signature::ED25519(ed25519_dalek::Signature::from_bytes(&array))) + } + KeyType::SECP256K1 => { + let array: [u8; 65] = BorshDeserialize::deserialize_reader(rd)?; + Ok(Signature::SECP256K1(Secp256K1Signature(array))) + } + KeyType::RSA2048 => { + let array: [u8; 256] = BorshDeserialize::deserialize_reader(rd)?; + Ok(Signature::RSA(Rsa2048Signature(array))) + } + } + } +} + +impl Display for Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + let buf; + let (key_type, key_data) = match self { + Signature::ED25519(signature) => { + buf = signature.to_bytes(); + (KeyType::ED25519, &buf[..]) + } + Signature::SECP256K1(signature) => (KeyType::SECP256K1, &signature.0[..]), + Signature::RSA(signature) => (KeyType::RSA2048, &signature.0[..]), + }; + write!(f, "{}:{}", key_type, Bs58(&key_data)) + } +} + +impl Debug for Signature { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + Display::fmt(self, f) + } +} + +impl serde::Serialize for Signature { + fn serialize( + &self, + serializer: S, + ) -> Result<::Ok, ::Error> + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl FromStr for Signature { + type Err = crate::errors::ParseSignatureError; + + fn from_str(value: &str) -> Result { + let (sig_type, sig_data) = split_key_type_data(value)?; + Ok(match sig_type { + KeyType::ED25519 => { + let data = decode_bs58::<{ ed25519_dalek::SIGNATURE_LENGTH }>(sig_data)?; + let sig = ed25519_dalek::Signature::from_bytes(&data); + Signature::ED25519(sig) + } + KeyType::SECP256K1 => Signature::SECP256K1(Secp256K1Signature(decode_bs58(sig_data)?)), + KeyType::RSA2048 => Signature::RSA(Rsa2048Signature(decode_bs58(sig_data)?)), + }) + } +} + +impl<'de> serde::Deserialize<'de> for Signature { + fn deserialize(deserializer: D) -> Result>::Error> + where + D: serde::Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + s.parse().map_err(|err: crate::errors::ParseSignatureError| { + serde::de::Error::custom(err.to_string()) + }) + } +} + +/// Helper struct which provides Display implementation for bytes slice +/// encoding them using base58. +// TODO(mina86): Get rid of it once bs58 has this feature. There’s currently PR +// for that: https://github.com/Nullus157/bs58-rs/pull/97 +struct Bs58<'a>(&'a [u8]); + +impl<'a> core::fmt::Display for Bs58<'a> { + fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + debug_assert!(self.0.len() <= 2048); + // The largest buffer we’re ever encoding is 65-byte long. Base58 + // increases size of the value by less than 40%. 96-byte buffer is + // therefore enough to fit the largest value we’re ever encoding. + let mut buf = [0u8; 2048]; + let len = bs58::encode(self.0).into(&mut buf[..]).unwrap(); + let output = &buf[..len]; + // SAFETY: we know that alphabet can only include ASCII characters + // thus our result is an ASCII string. + fmt.write_str(unsafe { std::str::from_utf8_unchecked(output) }) + } +} + +/// Helper which decodes fixed-length base58-encoded data. +/// +/// If the encoded string decodes into a buffer of different length than `N`, +/// returns error. Similarly returns error if decoding fails. +fn decode_bs58(encoded: &str) -> Result<[u8; N], DecodeBs58Error> { + let mut buffer = [0u8; N]; + decode_bs58_impl(&mut buffer[..], encoded)?; + Ok(buffer) +} + +fn decode_bs58_impl(dst: &mut [u8], encoded: &str) -> Result<(), DecodeBs58Error> { + let expected = dst.len(); + match bs58::decode(encoded).into(dst) { + Ok(received) if received == expected => Ok(()), + Ok(received) => Err(DecodeBs58Error::BadLength { expected, received }), + Err(bs58::decode::Error::BufferTooSmall) => { + Err(DecodeBs58Error::BadLength { expected, received: expected.saturating_add(1) }) + } + Err(err) => Err(DecodeBs58Error::BadData(err.to_string())), + } +} + +fn parse_bs58_data(max_len: usize, encoded: &str) -> Result, DecodeBs58Error> { + // N-byte encoded base58 string decodes to at most N bytes so there’s no + // need to allocate full max_len output buffer if encoded length is shorter. + let mut data = vec![0u8; max_len.min(encoded.len())]; + let expected = data.len(); + match bs58::decode(encoded.as_bytes()).into(data.as_mut_slice()) { + Ok(len) => { + data.truncate(len); + Ok(data) + } + Err(bs58::decode::Error::BufferTooSmall) => { + Err(DecodeBs58Error::BadLength { expected, received: expected.saturating_add(1) }) + } + Err(err) => Err(DecodeBs58Error::BadData(err.to_string())), + } +} + +enum DecodeBs58Error { + BadLength { expected: usize, received: usize }, + BadData(String), +} + +impl std::convert::From for crate::errors::ParseKeyError { + fn from(err: DecodeBs58Error) -> Self { + match err { + DecodeBs58Error::BadLength { expected, received } => { + crate::errors::ParseKeyError::InvalidLength { + expected_length: expected, + received_length: received, + } + } + DecodeBs58Error::BadData(error_message) => Self::InvalidData { error_message }, + } + } +} + +impl std::convert::From for crate::errors::ParseSignatureError { + fn from(err: DecodeBs58Error) -> Self { + match err { + DecodeBs58Error::BadLength { expected, received } => { + Self::InvalidLength { expected_length: expected, received_length: received } + } + DecodeBs58Error::BadData(error_message) => Self::InvalidData { error_message }, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_sign_verify() { + for key_type in [KeyType::ED25519, KeyType::SECP256K1, KeyType::RSA2048] { + let secret_key = SecretKey::from_random(key_type); + let public_key = secret_key.public_key(); + use sha2::Digest; + let data = sha2::Sha256::digest(b"123").to_vec(); + let signature = secret_key.sign(&data); + assert!(signature.verify(&data, &public_key)); + } + } + + #[test] + fn signature_verify_fuzzer() { + bolero::check!().with_type().for_each( + |(key_type, sign, data, public_key): &(KeyType, [u8; 65], Vec, PublicKey)| { + let signature = match key_type { + KeyType::ED25519 => { + Signature::from_parts(KeyType::ED25519, &sign[..64]).unwrap() + } + KeyType::SECP256K1 => { + Signature::from_parts(KeyType::SECP256K1, &sign[..65]).unwrap() + } + KeyType::RSA2048 => { + Signature::from_parts(KeyType::RSA2048, &sign[..256]).unwrap() + } + }; + let _ = signature.verify(&data, &public_key); + }, + ); + } + + #[test] + fn regression_signature_verification_originally_failed() { + let signature = Signature::from_parts(KeyType::SECP256K1, &[4; 65]).unwrap(); + let _ = signature.verify(&[], &PublicKey::empty(KeyType::SECP256K1)); + } + + #[test] + fn test_json_serialize_ed25519() { + let sk = SecretKey::from_seed(KeyType::ED25519, "test"); + let pk = sk.public_key(); + let expected = "\"ed25519:DcA2MzgpJbrUATQLLceocVckhhAqrkingax4oJ9kZ847\""; + assert_eq!(serde_json::to_string(&pk).unwrap(), expected); + assert_eq!(pk, serde_json::from_str(expected).unwrap()); + assert_eq!( + pk, + serde_json::from_str("\"DcA2MzgpJbrUATQLLceocVckhhAqrkingax4oJ9kZ847\"").unwrap() + ); + let pk2: PublicKey = pk.to_string().parse().unwrap(); + assert_eq!(pk, pk2); + + let expected = "\"ed25519:3KyUuch8pYP47krBq4DosFEVBMR5wDTMQ8AThzM8kAEcBQEpsPdYTZ2FPX5ZnSoLrerjwg66hwwJaW1wHzprd5k3\""; + assert_eq!(serde_json::to_string(&sk).unwrap(), expected); + assert_eq!(sk, serde_json::from_str(expected).unwrap()); + + let signature = sk.sign(b"123"); + let expected = "\"ed25519:3s1dvZdQtcAjBksMHFrysqvF63wnyMHPA4owNQmCJZ2EBakZEKdtMsLqrHdKWQjJbSRN6kRknN2WdwSBLWGCokXj\""; + assert_eq!(serde_json::to_string(&signature).unwrap(), expected); + assert_eq!(signature, serde_json::from_str(expected).unwrap()); + let signature_str: String = signature.to_string(); + let signature2: Signature = signature_str.parse().unwrap(); + assert_eq!(signature, signature2); + } + + #[test] + fn test_json_serialize_secp256k1() { + use sha2::Digest; + let data = sha2::Sha256::digest(b"123").to_vec(); + + let sk = SecretKey::from_seed(KeyType::SECP256K1, "test"); + let pk = sk.public_key(); + let expected = "\"secp256k1:5ftgm7wYK5gtVqq1kxMGy7gSudkrfYCbpsjL6sH1nwx2oj5NR2JktohjzB6fbEhhRERQpiwJcpwnQjxtoX3GS3cQ\""; + assert_eq!(serde_json::to_string(&pk).unwrap(), expected); + assert_eq!(pk, serde_json::from_str(expected).unwrap()); + let pk2: PublicKey = pk.to_string().parse().unwrap(); + assert_eq!(pk, pk2); + + let expected = "\"secp256k1:X4ETFKtQkSGVoZEnkn7bZ3LyajJaK2b3eweXaKmynGx\""; + assert_eq!(serde_json::to_string(&sk).unwrap(), expected); + assert_eq!(sk, serde_json::from_str(expected).unwrap()); + + let signature = sk.sign(&data); + let expected = "\"secp256k1:5N5CB9H1dmB9yraLGCo4ZCQTcF24zj4v2NT14MHdH3aVhRoRXrX3AhprHr2w6iXNBZDmjMS1Ntzjzq8Bv6iBvwth6\""; + assert_eq!(serde_json::to_string(&signature).unwrap(), expected); + assert_eq!(signature, serde_json::from_str(expected).unwrap()); + let signature_str: String = signature.to_string(); + let signature2: Signature = signature_str.parse().unwrap(); + assert_eq!(signature, signature2); + } + + #[test] + fn test_json_serialize_rsa2048() { + use sha2::Digest; + let data = sha2::Sha256::digest(b"123").to_vec(); + + let sk = SecretKey::from_seed(KeyType::RSA2048, "test"); + let pk = sk.public_key(); + let expected = "\"rsa2048:2TuPVgMCHJy5atawrsADEzjP7MCVbyyCA89UW6Wvjp9HrBuhZpGCRvEqExjN4wDfrT97k75BySeWiWgDoRmWBCVMQzCNFWQcfVmzeeZJFnVVceSziJsciYeCEeJGzjQnWBj4PEESKNgdKGWrQyUckRvknPQE3v7GVp9tXRPL81nLAgNm29E4SQ3u6ZV3DzJTCnnsoW75H8vdMMRY3zNzpTWKjEkMYA9qow6nnpS9asJ3HqXshDh3ookoAqzYgVwYmh2CDYFyw3cdwzimFFTYv3STud6erWxiMogeqP2XNnUyFYPKRWrhrrY966QDk4mEz1JgvBN9U4Vh5tsJGZLrZQPpt1owEjrGuCB6iqZQFwKxxjmNTcCZXZZn2WbdYVnSXGFR68uAjtPmHktzwS\""; + assert_eq!(serde_json::to_string(&pk).unwrap(), expected); + assert_eq!(pk, serde_json::from_str(expected).unwrap()); + let pk2: PublicKey = pk.to_string().parse().unwrap(); + assert_eq!(pk, pk2); + + let expected = "\"rsa2048:riiewRJm2wpE3rWTs1ikUc83so8ZXMX8vp9dUTnRgMC8GyfLr99MgiVFAbK3mdNq6mGY5dNdUfn3anQVSqFHL4sPbZD4w7QBx5Dzj4MzqJ8LjqmiKxE64G9tNDjfzkyYdinPssorC9yab7EhBMe24m3dMSnwHBJHQsXXaGibBtJUBcgPCbwYerZjfJB7TjMrj7WF1A2Q9SNdLUMYNX5CuKbWnpmrgFdkUzR1rZjrcgzSyUs4LrWwPBy2uA8PjJLwRabvoPpSr6hTMoHjeGMnQsLbVxKs7SC5aucdXru6ox9jJeD9Jackd5HKjAmobBaKiR1i9f7EsoxfsmibsqML8B5fFuHCRzMT6Ea5oEETevn4H5uBszJtrPJQpM5kwNogcNchHhK8GG2FZDGY5bsZuJEvzrWeuK7XR1ef1JmAmCtSqQNLe42CkqvBun8Cwj61Gf2rkvU2He1Wc6Lg81CwQKLUZTFRDXkdmaJEjAdweXhcksbMhajDp1D5mHtL3LY3FvxvgZpHxVq4gnKQTQenCvmgoH6JAJNQK5pmP68hMaJ4EZ45LgCzfzNs5eYYq3jqUQHGY7mvKi7E4ZFkY8fmgk5VQWcTyb3WeiqXzSYB79c2cR4XSUgmXiaFnLUYM1kqaNzeUhiprCTC43k9MhX5kMw3VRcg2RzrdnofHetPn75MPeR4g9i4kooZyRRkEvdg4YAWL6rhYQ5vV99cbQvTZSAzYTasiHfUKLkB76yoXJiok57tAjbz9XBGgWeqGRF8UFFcMDw8KJqrrEA4E1FhYEEYNR84kuU4ZwnnJakBCXf1UoYC7RKJEiWtcBqcL3Epcp3x6d4qxLij3M1pCDeFPZPYyMqYPvM8yB6GfMVwcycJSxWjK7cxmVRPF9WT3HyVNqFHA4o1aXHJ9LGMgDdVCUSk1QfEC1kLxMMFZMVY6RK6ycUPmotJxbJgBL9SAFypzNg63tipocAXucqaJ3NQrA5ujLnV4GhrmwF9Eo6T7FH9qgqsKZV1FN7m83TtXUuRqSDMdpDLLNotcC4MQ6nFH46R73ct8CE4ibn6j4dtPMMJrEuWQqAE8tqpvGJoxifvVfwmtJMvozTTu69DgXn38MHZL2f3K25M7iW4yWiZjve4b7AFXhnaaKQuCwoZ6CNf31X2STT29wFvw6HMZNZt4WdXMxUrgP5mkM8r2Fio8iEQUbSfhrAj3SuZXDV3xiRYRXb45cL7umoZ446YctmQuyHzaRfP8yLsy3Y7Bn8GGTj4bbzPNhT4r4QHitobymKScePdFTms4P8HNogebkBf4K7QrNSJxA4EVRgf9aP4KejHUfhq9v7pLGsfXv3rGaxRZnCNrgTYY215e8FoJcx8mQGvykCRejto8Gghp1gw5n5eC3ddMUiYqphteoYfuhVYfiweMDSiRrajko4JAxuXpvHRVeTwSypPYUkiazcog7z8bgPSq1FNS8Vnqhyx4oSj5rBGXTK8y7MR9zPB8yN78DacxPBBLfUcMvVan4GueCi2wxq9KL8XMj8DvDccBBotc8c1jftgaYdLqESVqpiKj3ZSu8Ui3SpdhELMFzk22kwRXN2p9nK78u94Gpp44J9upyiNpHsLbkB3kpT4vtvxa8P9H1YhMqVRB2k9EhVHUwATRVb3uoznRqXVnXmE8cq\""; + assert_eq!(serde_json::to_string(&sk).unwrap(), expected); + assert_eq!(sk, serde_json::from_str(expected).unwrap()); + + let signature = sk.sign(&data); + let expected = "\"rsa2048:9UXu2UtEzfgJWw5goaHcjAueJcRkwNS9VPHsF1Re2MR8p7WcA9Q77DTPAMWXkDnEsaebWFwrQHqqk8jAZfLsZDTBmDQ28XNsPgsx3wJkwrujYT5o99Zf6J1SbFK3umfzgo26BNWGLD44nrqhFJDwy1UdXqQPMKGKs7P56g2dqbEe3daoVze6UrhHQAdLbEXN9BQJBkNz254MLey7pzbAforMfoqy2S3RdvgFRQuXdgHbsXSHJEemmQEVpMiMvDW5Hz4vVMx3XaLkLLUQfqpT9Tom6NbGsNfPn7M1Ge1xXEFs25Zcqv3e7mq5Ps8pXovCexeznHJz5VSkDGY2h2r6tpACjDM2LW\""; + assert_eq!(serde_json::to_string(&signature).unwrap(), expected); + assert_eq!(signature, serde_json::from_str(expected).unwrap()); + let signature_str: String = signature.to_string(); + let signature2: Signature = signature_str.parse().unwrap(); + assert_eq!(signature, signature2); + } + + #[test] + fn test_borsh_serialization() { + use sha2::Digest; + let data = sha2::Sha256::digest(b"123").to_vec(); + for key_type in [KeyType::ED25519, KeyType::SECP256K1, KeyType::RSA2048] { + let sk = SecretKey::from_seed(key_type, "test"); + let pk = sk.public_key(); + let bytes = borsh::to_vec(&pk).unwrap(); + assert_eq!(PublicKey::try_from_slice(&bytes).unwrap(), pk); + + let signature = sk.sign(&data); + let bytes = borsh::to_vec(&signature).unwrap(); + assert_eq!(Signature::try_from_slice(&bytes).unwrap(), signature); + + assert!(PublicKey::try_from_slice(&[0]).is_err()); + assert!(Signature::try_from_slice(&[0]).is_err()); + } + } + + #[test] + fn test_invalid_data() { + let invalid = "\"secp256k1:2xVqteU8PWhadHTv99TGh3bSf\""; + assert!(serde_json::from_str::(invalid).is_err()); + assert!(serde_json::from_str::(invalid).is_err()); + assert!(serde_json::from_str::(invalid).is_err()); + } + + #[test] + fn test_invalid_rsa_data() { + let invalid = "\"rsa2048:riiewRJm2wpE3rWTs1ikUc83so8ZXMX8vp9dUTnRgMC8GyfLr99MgiVFAbK3mdNq6mGY5dNdUfn3anQVSqFHL4sPbZD4w7QBx5Dzj4MzqJ8LjqmiKxE64G9tNDjfzkyYdinPssorC9yab7EhBMe24m3dMSnwHBJHQsXXaGibBtJUBcgPCbwYerZjfJB7TjMrj7WF1A2Q9SNdLUMYNX5CuKbWnpmrgFdkUzR1rZjrcgzSyUs4LrWwPBy2uA8PjJLwRabvoPpSr6hTMoHjeGMnQsLbVxKs7SC5aucdXru6ox9jJeD9Jackd5HKjAmobBaKiR1i9f7EsoxfsmibsqML8B5fFuHCRzMT6Ea5oEETevn4H5uBszJtrPJQpM5kwNogcNchHhK8GG2FZDGY5bsZuJEvzrWeuK7XR1ef1JmAmCtSqQNLe42CkqvBun8Cwj61Gf2rkvU2He1Wc6Lg81CwQKLUZTFRDXkdmaJEjAdweXhcksbMhajDp1D5mHtL3LY3FvxvgZpHxVq4gnKQTQenCvmgoH6JAJNQK5pmP68hMaJ4EZ45LgCzfzNs5eYYq3jqUQHGY7mvKi7E4ZFkY8fmgk5VQWcTyb3WeiqXzSYB79c2cR4XSUgmXiaFnLUYM1kqaNzeUhiprCTC43k9MhX5kMw3VRcg2RzrdnofHetPn75MPeR4g9i4kooZyRRkEvdg4YAWL6rhYQ5vV99cbQvTZSAzYTasiHfUKLkB76yoXJiok57tAjbz9XBGgWeqGRF8UFFcMDw8KJqrrEA4E1FhYEEYNR84kuU4ZwnnJakBCXf1UoYC7RKJEiWtcBqcL3Epcp3x6d4qxLij3M1pCDeFPZPYyMqYPvM8yB6GfMVwcycJSxWjK7cxmVRPF9WT3HyVNqFHA4o1aXHJ9LGMgDdVCUSk1QfEC1kLxMMFZMVY6RK6ycUPmotJxbJgBL9SAFypzNg63tipocAXucqaJ3NQrA5ujLnV4GhrmwF9Eo6T7FH9qgqsKZV1FN7m83TtXUuRqSDMdpDLLNotcC4MQ6nFH46R73ct8CE4ibn6j4dtPMMJrEuWQqAE8tqpvGJoxifvVfwmtJMvozTTu69DgXn38MHZL2f3K25M7iW4yWiZjve4b7AFXhnaaKQuCwoZ6CNf31X2STT29wFvw6HMZNZt4WdXMxUrgP5mkM8r2Fio8iEQUbSfhrAj3SuZXDV3xiRYRXb45cL7umoZ446YctmQuyHzaRfP8yLsy3Y7Bn8GGTj4bbzPNhT4r4QHitobymKScePdFTms4P8HNogebkBf4K7QrNSJxA4EVRgf9aP4KejHUfhq9v7pLGsfXv3rGaxRZnCNrgTYY215e8FoJcx8mQGvykCRejto8Gghp1gw5n5eC3ddMUiYqphteoYfuhVYfiweMDSiRrajko4JAxuXpvHRVeTwSypPYUkiazcog7z8bgPSq1FNS8Vnqhyx4oSj5rBGXTK8y7MR9zPB8yN78DacxPBBLfUcMvVan4GueCi2wxq9KL8XMj8DvDccBBotc8c1jftgaYdLqESVqpiKj3ZSu8Ui3SpdhELMFzk22kwRXN2p9nK78u94Gpp44J9upyiNpHsLbkB3kpT4vtvxa8P9H1YhMqVRB2k9EhVHUwATRVb3uoznRqXVnXmE8cq\""; + assert!(serde_json::from_str::(invalid).is_err()); + assert!(serde_json::from_str::(invalid).is_ok()); + assert!(serde_json::from_str::(invalid).is_err()); + } +} diff --git a/core/crypto/src/signer.rs b/core/crypto/src/signer.rs new file mode 100644 index 000000000..62f88a995 --- /dev/null +++ b/core/crypto/src/signer.rs @@ -0,0 +1,113 @@ +use crate::key_conversion::convert_secret_key; +use crate::key_file::KeyFile; +use crate::{KeyType, PublicKey, SecretKey, Signature}; +use unc_account_id::AccountId; +use std::io; +use std::path::Path; +use std::sync::Arc; + +/// Generic signer trait, that can sign with some subset of supported curves. +pub trait Signer: Sync + Send { + fn public_key(&self) -> PublicKey; + fn sign(&self, data: &[u8]) -> Signature; + + fn verify(&self, data: &[u8], signature: &Signature) -> bool { + signature.verify(data, &self.public_key()) + } + + fn compute_vrf_with_proof(&self, _data: &[u8]) -> (crate::vrf::Value, crate::vrf::Proof); + + /// Used by test infrastructure, only implement if make sense for testing otherwise raise `unimplemented`. + fn write_to_file(&self, _path: &Path) -> io::Result<()> { + unimplemented!(); + } +} + +// Signer that returns empty signature. Used for transaction testing. +pub struct EmptySigner {} + +impl Signer for EmptySigner { + fn public_key(&self) -> PublicKey { + PublicKey::empty(KeyType::ED25519) + } + + fn sign(&self, _data: &[u8]) -> Signature { + Signature::empty(KeyType::ED25519) + } + + fn compute_vrf_with_proof(&self, _data: &[u8]) -> (crate::vrf::Value, crate::vrf::Proof) { + unimplemented!() + } +} + +/// Signer that keeps secret key in memory. +#[derive(Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)] +pub struct InMemorySigner { + pub account_id: AccountId, + pub public_key: PublicKey, + pub secret_key: SecretKey, +} + +impl InMemorySigner { + pub fn from_seed(account_id: AccountId, key_type: KeyType, seed: &str) -> Self { + let secret_key = SecretKey::from_seed(key_type, seed); + Self { account_id, public_key: secret_key.public_key(), secret_key } + } + + pub fn from_secret_key(account_id: AccountId, secret_key: SecretKey) -> Self { + Self { account_id, public_key: secret_key.public_key(), secret_key } + } + + pub fn from_file(path: &Path) -> io::Result { + KeyFile::from_file(path).map(Self::from) + } +} + +impl Signer for InMemorySigner { + fn public_key(&self) -> PublicKey { + self.public_key.clone() + } + + fn sign(&self, data: &[u8]) -> Signature { + self.secret_key.sign(data) + } + + fn compute_vrf_with_proof(&self, data: &[u8]) -> (crate::vrf::Value, crate::vrf::Proof) { + let secret_key = convert_secret_key(self.secret_key.unwrap_as_ed25519()); + secret_key.compute_vrf_with_proof(&data) + } + + fn write_to_file(&self, path: &Path) -> io::Result<()> { + KeyFile::from(self).write_to_file(path) + } +} + +impl From for InMemorySigner { + fn from(key_file: KeyFile) -> Self { + Self { + account_id: key_file.account_id, + public_key: key_file.public_key, + secret_key: key_file.secret_key, + } + } +} + +impl From<&InMemorySigner> for KeyFile { + fn from(signer: &InMemorySigner) -> KeyFile { + KeyFile { + account_id: signer.account_id.clone(), + public_key: signer.public_key.clone(), + secret_key: signer.secret_key.clone(), + } + } +} + +impl From> for KeyFile { + fn from(signer: Arc) -> KeyFile { + KeyFile { + account_id: signer.account_id.clone(), + public_key: signer.public_key.clone(), + secret_key: signer.secret_key.clone(), + } + } +} diff --git a/core/crypto/src/test_utils.rs b/core/crypto/src/test_utils.rs new file mode 100644 index 000000000..d094003ad --- /dev/null +++ b/core/crypto/src/test_utils.rs @@ -0,0 +1,82 @@ +use secp256k1::rand::SeedableRng; + +use crate::signature::{ED25519PublicKey, ED25519SecretKey, KeyType, PRIVTAE_KEY_DEFAULT_RSA_KEY_BITS, PublicKey, SecretKey}; +use crate::{InMemorySigner, Signature}; +use unc_account_id::AccountId; + +fn ed25519_key_pair_from_seed(seed: &str) -> ed25519_dalek::SigningKey { + let seed_bytes = seed.as_bytes(); + let len = std::cmp::min(ed25519_dalek::SECRET_KEY_LENGTH, seed_bytes.len()); + let mut seed: [u8; ed25519_dalek::SECRET_KEY_LENGTH] = [b' '; ed25519_dalek::SECRET_KEY_LENGTH]; + seed[..len].copy_from_slice(&seed_bytes[..len]); + ed25519_dalek::SigningKey::from_bytes(&seed) +} + +fn secp256k1_secret_key_from_seed(seed: &str) -> secp256k1::SecretKey { + let seed_bytes = seed.as_bytes(); + let len = std::cmp::min(32, seed_bytes.len()); + let mut seed: [u8; 32] = [b' '; 32]; + seed[..len].copy_from_slice(&seed_bytes[..len]); + let mut rng = secp256k1::rand::rngs::StdRng::from_seed(seed); + secp256k1::SecretKey::new(&mut rng) +} + +fn rsa2048_secret_key_from_seed(seed: &str) -> rsa::RsaPrivateKey { + let seed_bytes = seed.as_bytes(); + let len = std::cmp::min(32, seed_bytes.len()); + let mut seed: [u8; 32] = [b' '; 32]; + seed[..len].copy_from_slice(&seed_bytes[..len]); + let mut rng = secp256k1::rand::rngs::StdRng::from_seed(seed); + rsa::RsaPrivateKey::new(&mut rng, PRIVTAE_KEY_DEFAULT_RSA_KEY_BITS).unwrap() +} + +impl PublicKey { + pub fn from_seed(key_type: KeyType, seed: &str) -> Self { + match key_type { + KeyType::ED25519 => { + let keypair = ed25519_key_pair_from_seed(seed); + PublicKey::ED25519(ED25519PublicKey(keypair.verifying_key().to_bytes())) + } + KeyType::SECP256K1 => { + let secret_key = SecretKey::SECP256K1(secp256k1_secret_key_from_seed(seed)); + PublicKey::SECP256K1(secret_key.public_key().unwrap_as_secp256k1().clone()) + } + KeyType::RSA2048 => { + let secret_key = SecretKey::RSA(rsa2048_secret_key_from_seed(seed)); + PublicKey::RSA(secret_key.public_key().unwrap_as_rsa2048().clone()) + } + } + } +} + +impl SecretKey { + pub fn from_seed(key_type: KeyType, seed: &str) -> Self { + match key_type { + KeyType::ED25519 => { + let keypair = ed25519_key_pair_from_seed(seed); + SecretKey::ED25519(ED25519SecretKey(keypair.to_keypair_bytes())) + } + KeyType::SECP256K1 => SecretKey::SECP256K1(secp256k1_secret_key_from_seed(seed)), + KeyType::RSA2048 => SecretKey::RSA(rsa2048_secret_key_from_seed(seed)), + } + } +} + +const SIG: [u8; ed25519_dalek::SIGNATURE_LENGTH] = [0u8; ed25519_dalek::SIGNATURE_LENGTH]; + +impl Signature { + /// Empty signature that doesn't correspond to anything. + pub fn empty(key_type: KeyType) -> Self { + match key_type { + KeyType::ED25519 => Signature::ED25519(ed25519_dalek::Signature::from_bytes(&SIG)), + _ => unimplemented!(), + } + } +} + +impl InMemorySigner { + pub fn from_random(account_id: AccountId, key_type: KeyType) -> Self { + let secret_key = SecretKey::from_random(key_type); + Self { account_id, public_key: secret_key.public_key(), secret_key } + } +} diff --git a/core/crypto/src/traits.rs b/core/crypto/src/traits.rs new file mode 100644 index 000000000..2916469ed --- /dev/null +++ b/core/crypto/src/traits.rs @@ -0,0 +1,142 @@ +macro_rules! to_str { + ($v:expr) => { + ::std::convert::identity::($v.into()).as_str() + }; +} + +macro_rules! common_conversions { + ($ty:ty, $what:literal) => { + impl ::std::convert::TryFrom for $ty { + type Error = (); + + fn try_from(value: String) -> Result { + Self::try_from(value.as_str()) + } + } + + impl From<$ty> for String { + fn from(v: $ty) -> String { + bs58::encode(v).into_string() + } + } + + impl From<&$ty> for String { + fn from(v: &$ty) -> String { + bs58::encode(v).into_string() + } + } + + impl ::std::fmt::Debug for $ty { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + f.write_str(to_str!(self)) + } + } + + impl ::std::fmt::Display for $ty { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + f.write_str(to_str!(self)) + } + } + + impl<'de> ::serde::Deserialize<'de> for $ty { + fn deserialize>( + deserializer: D, + ) -> Result { + let s = <&str as ::serde::Deserialize<'de>>::deserialize(deserializer)?; + >::try_from(s).map_err(|_| { + ::invalid_value( + ::serde::de::Unexpected::Str(s), + &concat!("a valid ", $what), + ) + }) + } + } + + impl ::serde::Serialize for $ty { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(to_str!(self)) + } + } + }; +} + +macro_rules! common_conversions_fixed { + ($ty:ty, $l:literal, $bytes:expr, $what:literal) => { + impl AsRef<[u8; $l]> for $ty { + fn as_ref(&self) -> &[u8; $l] { + ::std::convert::identity:: &[u8; $l]>($bytes)(self) + } + } + + impl AsRef<[u8]> for $ty { + fn as_ref(&self) -> &[u8] { + >::as_ref(self) + } + } + + impl From<$ty> for [u8; $l] { + fn from(v: $ty) -> [u8; $l] { + *v.as_ref() + } + } + + impl From<&$ty> for [u8; $l] { + fn from(v: &$ty) -> [u8; $l] { + *v.as_ref() + } + } + + impl ::std::convert::TryFrom<&str> for $ty { + type Error = (); + + fn try_from(value: &str) -> Result { + let mut buf = [0; $l]; + match bs58::decode(value).into(&mut buf[..]) { + Ok($l) => Self::try_from(&buf).or(Err(())), + _ => Err(()), + } + } + } + + common_conversions!($ty, $what); + }; +} + +macro_rules! eq { + ($ty:ty, $e:expr) => { + impl PartialEq for $ty { + fn eq(&self, other: &Self) -> bool { + ::std::convert::identity:: bool>($e)(self, other) + } + } + + impl Eq for $ty {} + }; +} + +macro_rules! value_type { + ($vis:vis, $ty:ident, $l:literal, $what:literal) => { + #[derive(Copy, Clone, Eq, PartialEq, borsh::BorshDeserialize, borsh::BorshSerialize)] + $vis struct $ty(pub [u8; $l]); + + impl AsMut<[u8; $l]> for $ty { + fn as_mut(&mut self) -> &mut [u8; $l] { + &mut self.0 + } + } + + impl AsMut<[u8]> for $ty { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0[..] + } + } + + impl From<&[u8; $l]> for $ty { + fn from(value: &[u8; $l]) -> Self { + Self(*value) + } + } + + common_conversions_fixed!($ty, $l, |s| &s.0, $what); + }; +} diff --git a/core/crypto/src/util.rs b/core/crypto/src/util.rs new file mode 100644 index 000000000..eab17ed9f --- /dev/null +++ b/core/crypto/src/util.rs @@ -0,0 +1,134 @@ +use crate::errors::ImplicitPublicKeyError; +use crate::{KeyType, PublicKey}; +use borsh::BorshDeserialize; +use curve25519_dalek::ristretto::CompressedRistretto; +use curve25519_dalek::traits::VartimeMultiscalarMul; + +pub use curve25519_dalek::ristretto::RistrettoPoint as Point; +pub use curve25519_dalek::scalar::Scalar; + +use unc_account_id::AccountType; + +pub fn vmul2(s1: Scalar, p1: &Point, s2: Scalar, p2: &Point) -> Point { + Point::vartime_multiscalar_mul(&[s1, s2], [p1, p2].iter().copied()) +} + +pub trait Packable: Sized { + type Packed; + fn unpack(data: &Self::Packed) -> Option; + fn pack(&self) -> Self::Packed; +} + +pub fn unpack(data: &T::Packed) -> Option { + Packable::unpack(data) +} + +impl Packable for [u8; 32] { + type Packed = [u8; 32]; + + fn unpack(data: &[u8; 32]) -> Option { + Some(*data) + } + + fn pack(&self) -> [u8; 32] { + *self + } +} + +impl Packable for Point { + type Packed = [u8; 32]; + + fn unpack(data: &[u8; 32]) -> Option { + CompressedRistretto(*data).decompress() + } + + fn pack(&self) -> [u8; 32] { + self.compress().to_bytes() + } +} + +impl Packable for Scalar { + type Packed = [u8; 32]; + + fn unpack(data: &[u8; 32]) -> Option { + Scalar::from_canonical_bytes(*data).into() + } + + fn pack(&self) -> [u8; 32] { + self.to_bytes() + } +} + +impl, T2: Packable> Packable for (T1, T2) { + type Packed = [u8; 64]; + + fn unpack(data: &[u8; 64]) -> Option { + let (d1, d2) = stdx::split_array::<64, 32, 32>(data); + Some((unpack(d1)?, unpack(d2)?)) + } + + fn pack(&self) -> [u8; 64] { + stdx::join_array(self.0.pack(), self.1.pack()) + } +} + +impl< + T1: Packable, + T2: Packable, + T3: Packable, + > Packable for (T1, T2, T3) +{ + type Packed = [u8; 96]; + + fn unpack(data: &[u8; 96]) -> Option { + let (d1, d2) = stdx::split_array::<96, 32, 64>(data); + let (d2, d3) = stdx::split_array::<64, 32, 32>(d2); + Some((unpack(d1)?, unpack(d2)?, unpack(d3)?)) + } + + fn pack(&self) -> [u8; 96] { + let mut res = [0; 96]; + let (d1, d2) = stdx::split_array_mut::<96, 32, 64>(&mut res); + let (d2, d3) = stdx::split_array_mut::<64, 32, 32>(d2); + *d1 = self.0.pack(); + *d2 = self.1.pack(); + *d3 = self.2.pack(); + res + } +} + +impl PublicKey { + /// Create the implicit public key from an NEAR-implicit account ID. + /// + /// Returns `ImplicitPublicKeyError::AccountIsNotNearImplicit` if the given + /// account id is not a valid NEAR-implicit account ID. + /// See [`unc_account_id::AccountId#is_unc_implicit`] for the definition. + pub fn from_unc_implicit_account( + account_id: &unc_account_id::AccountId, + ) -> Result { + if account_id.get_account_type() != AccountType::NearImplicitAccount { + return Err(ImplicitPublicKeyError::AccountIsNotNearImplicit { + account_id: account_id.clone(), + }); + } + let mut public_key_data = Vec::with_capacity(33); + public_key_data.push(KeyType::ED25519 as u8); + public_key_data.extend( + hex::decode(account_id.as_bytes()) + .expect("account id was a valid hex of length 64 resulting in 32 bytes"), + ); + debug_assert_eq!(public_key_data.len(), 33); + let public_key = PublicKey::try_from_slice(&public_key_data) + .expect("we should be able to deserialize ED25519 public key"); + Ok(public_key) + } +} + +macro_rules! unwrap_or_return_false { + ($e:expr) => { + match $e { + ::std::option::Option::Some(v) => v, + ::std::option::Option::None => return false, + } + }; +} diff --git a/core/crypto/src/vrf.rs b/core/crypto/src/vrf.rs new file mode 100644 index 000000000..651989bac --- /dev/null +++ b/core/crypto/src/vrf.rs @@ -0,0 +1,195 @@ +use crate::util::*; +use bs58; +use curve25519_dalek::constants::{ + RISTRETTO_BASEPOINT_POINT as G, RISTRETTO_BASEPOINT_TABLE as GT, +}; +use std::borrow::Borrow; +use subtle::{ConditionallySelectable, ConstantTimeEq}; + +#[derive(Clone)] +pub struct PublicKey(pub(crate) [u8; 32], pub(crate) Point); +#[derive(Clone)] +pub struct SecretKey(Scalar, PublicKey); +value_type!(pub, Value, 32, "value"); +value_type!(pub, Proof, 64, "proof"); + +impl PublicKey { + fn from_bytes(bytes: &[u8; 32]) -> Option { + Some(PublicKey(*bytes, unpack(bytes)?)) + } + + fn offset(&self, input: &[u8]) -> Scalar { + hash_s!(&self.0, input) + } + + pub fn is_vrf_valid(&self, input: &impl Borrow<[u8]>, value: &Value, proof: &Proof) -> bool { + self.is_valid(input.borrow(), value, proof) + } + + // FIXME: no clear fix is available here -- the underlying library runs a non-trivial amount of + // unchecked arithmetic inside and provides no apparent way to do it in a checked manner. + #[allow(clippy::arithmetic_side_effects)] + fn is_valid(&self, input: &[u8], value: &Value, proof: &Proof) -> bool { + let p = unwrap_or_return_false!(unpack(&value.0)); + let (r, c) = unwrap_or_return_false!(unpack(&proof.0)); + hash_s!( + &self.0, + &value.0, + vmul2(r + c * self.offset(input), &G, c, &self.1), + vmul2(r, &p, c, &G) + ) == c + } +} + +// FIXME: no clear fix is available here -- the underlying library runs a non-trivial amount of +// unchecked arithmetic inside and provides no apparent way to do it in a checked or wrapping +// manner. +#[allow(clippy::arithmetic_side_effects)] +fn basemul(s: Scalar) -> Point { + &s * &*GT +} + +fn safe_invert(s: Scalar) -> Scalar { + Scalar::conditional_select(&s, &Scalar::ONE, s.ct_eq(&Scalar::ZERO)).invert() +} + +impl SecretKey { + pub(crate) fn from_scalar(sk: Scalar) -> Self { + let pk = basemul(sk); + SecretKey(sk, PublicKey(pk.pack(), pk)) + } + + fn from_bytes(bytes: &[u8; 32]) -> Option { + Some(Self::from_scalar(unpack(bytes)?)) + } + + pub fn public_key(&self) -> &PublicKey { + &self.1 + } + + pub fn compute_vrf(&self, input: &impl Borrow<[u8]>) -> Value { + self.compute(input.borrow()) + } + + // FIXME: no clear fix is available here -- the underlying library runs a non-trivial amount of + // unchecked arithmetic inside and provides no apparent way to do it in a checked or wrapping + // manner. + #[allow(clippy::arithmetic_side_effects)] + fn compute(&self, input: &[u8]) -> Value { + Value(basemul(safe_invert(self.0 + self.1.offset(input))).pack()) + } + + pub fn compute_vrf_with_proof(&self, input: &impl Borrow<[u8]>) -> (Value, Proof) { + self.compute_with_proof(input.borrow()) + } + + // FIXME: no clear fix is available here -- the underlying library runs a non-trivial amount of + // unchecked arithmetic inside and provides no apparent way to do it in a checked or wrapping + // manner. + #[allow(clippy::arithmetic_side_effects)] + fn compute_with_proof(&self, input: &[u8]) -> (Value, Proof) { + let x = self.0 + self.1.offset(input); + let inv = safe_invert(x); + let val = basemul(inv).pack(); + let k = prs!(x); + let c = hash_s!(&(self.1).0, &val, basemul(k), basemul(inv * k)); + (Value(val), Proof((k - c * x, c).pack())) + } + + pub fn is_vrf_valid(&self, input: &impl Borrow<[u8]>, value: &Value, proof: &Proof) -> bool { + self.1.is_valid(input.borrow(), value, proof) + } +} + +macro_rules! traits { + ($ty:ident, $l:literal, $bytes:expr, $what:literal) => { + eq!($ty, |a, b| a.0 == b.0); + common_conversions_fixed!($ty, 32, $bytes, $what); + + impl TryFrom<&[u8; $l]> for $ty { + type Error = (); + fn try_from(value: &[u8; $l]) -> Result { + Self::from_bytes(value).ok_or(()) + } + } + }; +} + +traits!(PublicKey, 32, |s| &s.0, "public key"); +traits!(SecretKey, 32, |s| s.0.as_bytes(), "secret key"); + +#[cfg(test)] +mod tests { + use super::*; + + use secp256k1::rand::rngs::OsRng; + use serde::{Deserialize, Serialize}; + use serde_json::{from_str, to_string}; + + fn random_secret_key() -> SecretKey { + SecretKey::from_scalar(Scalar::random(&mut OsRng)) + } + + #[test] + fn test_conversion() { + let sk = random_secret_key(); + let sk2 = SecretKey::from_bytes(&sk.clone().into()).unwrap(); + assert_eq!(sk, sk2); + let pk = sk.public_key(); + let pk2 = sk2.public_key(); + let pk3 = PublicKey::from_bytes(&pk2.into()).unwrap(); + assert_eq!(pk, pk2); + assert_eq!(pk.clone(), pk3); + } + + #[test] + fn test_verify() { + let sk = random_secret_key(); + let (val, proof) = sk.compute_vrf_with_proof(b"Test"); + let val2 = sk.compute_vrf(b"Test"); + assert_eq!(val, val2); + assert!(sk.public_key().is_vrf_valid(b"Test", &val, &proof)); + assert!(!sk.public_key().is_vrf_valid(b"Tent", &val, &proof)); + } + + #[test] + fn test_different_keys() { + let sk = random_secret_key(); + let sk2 = random_secret_key(); + assert_ne!(sk, sk2); + assert_ne!(Into::<[u8; 32]>::into(sk.clone()), Into::<[u8; 32]>::into(sk2.clone())); + let pk = sk.public_key(); + let pk2 = sk2.public_key(); + assert_ne!(pk, pk2); + assert_ne!(Into::<[u8; 32]>::into(pk), Into::<[u8; 32]>::into(pk2)); + let (val, proof) = sk.compute_vrf_with_proof(b"Test"); + let (val2, proof2) = sk2.compute_vrf_with_proof(b"Test"); + assert_ne!(val, val2); + assert_ne!(proof, proof2); + assert!(!pk2.is_vrf_valid(b"Test", &val, &proof)); + assert!(!pk2.is_vrf_valid(b"Test", &val2, &proof)); + assert!(!pk2.is_vrf_valid(b"Test", &val, &proof2)); + } + + fn round_trip Deserialize<'de>>(value: &T) -> T { + from_str(to_string(value).unwrap().as_str()).unwrap() + } + + #[test] + fn test_serialize() { + let sk = random_secret_key(); + let sk2 = round_trip(&sk); + assert_eq!(sk, sk2); + let (val, proof) = sk.compute_vrf_with_proof(b"Test"); + let (val2, proof2) = sk2.compute_vrf_with_proof(b"Test"); + let (val3, proof3) = (round_trip(&val), round_trip(&proof)); + assert_eq!((val, proof), (val2, proof2)); + assert_eq!((val, proof), (val3, proof3)); + let pk = sk.public_key(); + let pk2 = sk2.public_key(); + let pk3 = round_trip(pk); + assert!(pk.is_vrf_valid(b"Test", &val, &proof)); + assert!(pk2.is_vrf_valid(b"Test", &val, &proof)); + assert!(pk3.is_vrf_valid(b"Test", &val, &proof)); + } +} diff --git a/core/dyn-configs/Cargo.toml b/core/dyn-configs/Cargo.toml new file mode 100644 index 000000000..49a04d6cc --- /dev/null +++ b/core/dyn-configs/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "unc-dyn-configs" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "Dynamic configure helpers for the near codebase" +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +once_cell.workspace = true +prometheus.workspace = true +serde.workspace = true +serde_json.workspace = true +thiserror.workspace = true +tokio.workspace = true +tracing.workspace = true + +unc-chain-configs.workspace = true +unc-o11y.workspace = true +unc-primitives.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-o11y/nightly", + "unc-primitives/nightly", +] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-primitives/nightly_protocol", +] diff --git a/core/dyn-configs/README.md b/core/dyn-configs/README.md new file mode 100644 index 000000000..0d89dc7ba --- /dev/null +++ b/core/dyn-configs/README.md @@ -0,0 +1,26 @@ +Dynamic config helpers for the NEAR codebase. + +This crate contains utilities that allow to reconfigure the node while it is running. + +## How to: + +### Logging and tracing + +Make changes to `log_config.json` and send `SIGHUP` signal to the `uncd` process. + +### Other config values + +Makes changes to `config.json` and send `SIGHUP` signal to the `uncd` process. + +#### Fields of config that can be changed while the node is running: + +- `expected_shutdown`: the specified block height uncd will gracefully shutdown at. + +#### Changing other fields of `config.json` + +The changes to other fields of `config.json` will be silently ignored as long as +`config.json` remains a valid json object and passes internal validation. + +Please be careful about making changes to `config.json` because when a node +starts (or restarts), it checks the validity of the config files and crashes if +detects any issues. diff --git a/core/dyn-configs/src/lib.rs b/core/dyn-configs/src/lib.rs new file mode 100644 index 000000000..444659700 --- /dev/null +++ b/core/dyn-configs/src/lib.rs @@ -0,0 +1,74 @@ +#![doc = include_str!("../README.md")] + +use unc_chain_configs::UpdateableClientConfig; +use unc_o11y::log_config::LogConfig; +use unc_primitives::static_clock::StaticClock; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; +use std::sync::Arc; +use tokio::sync::broadcast::Sender; + +mod metrics; + +#[derive(Serialize, Deserialize, Clone, Default)] +/// Contains the latest state of configs which can be updated at runtime. +pub struct UpdateableConfigs { + /// Contents of the file LOG_CONFIG_FILENAME. + pub log_config: Option, + /// Contents of the `config.json` corresponding to the mutable fields of `ClientConfig`. + pub client_config: Option, +} + +/// Pushes the updates to listeners. +#[derive(Default)] +pub struct UpdateableConfigLoader { + /// Notifies receivers about the new config values available. + tx: Option>>>, +} + +#[derive(thiserror::Error, Debug)] +#[non_exhaustive] +pub enum UpdateableConfigLoaderError { + #[error("Failed to parse a dynamic config file {file:?}: {err:?}")] + Parse { file: PathBuf, err: serde_json::Error }, + #[error("Can't open or read a dynamic config file {file:?}: {err:?}")] + OpenAndRead { file: PathBuf, err: std::io::Error }, + #[error("Can't open or read the config file {file:?}: {err:?}")] + ConfigFileError { file: PathBuf, err: anyhow::Error }, + #[error("One or multiple dynamic config files reload errors {0:?}")] + Errors(Vec), + #[error("No home dir set")] + NoHomeDir(), +} + +impl UpdateableConfigLoader { + pub fn new( + updateable_configs: UpdateableConfigs, + tx: Sender>>, + ) -> Self { + let mut result = Self { tx: Some(tx) }; + result.reload(Ok(updateable_configs)); + result + } + + pub fn reload( + &mut self, + updateable_configs: Result, + ) { + match updateable_configs { + Ok(updateable_configs) => { + unc_o11y::reload_log_config(updateable_configs.log_config.as_ref()); + self.tx.as_ref().map(|tx| tx.send(Ok(updateable_configs.clone()))); + Self::update_metrics(); + } + Err(err) => { + self.tx.as_ref().map(|tx| tx.send(Err(Arc::new(err)))); + } + } + } + + fn update_metrics() { + metrics::CONFIG_RELOAD_TIMESTAMP.set(StaticClock::utc().timestamp()); + metrics::CONFIG_RELOADS.inc(); + } +} diff --git a/core/dyn-configs/src/metrics.rs b/core/dyn-configs/src/metrics.rs new file mode 100644 index 000000000..7f16f8254 --- /dev/null +++ b/core/dyn-configs/src/metrics.rs @@ -0,0 +1,18 @@ +use unc_o11y::metrics::{try_create_int_counter, try_create_int_gauge, IntCounter, IntGauge}; +use once_cell::sync::Lazy; + +pub static CONFIG_RELOADS: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_config_reloads_total", + "Number of times the configs were reloaded during the current run of the process", + ) + .unwrap() +}); + +pub static CONFIG_RELOAD_TIMESTAMP: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_config_reload_timestamp_seconds", + "Timestamp of the last reload of the config", + ) + .unwrap() +}); diff --git a/core/o11y/Cargo.toml b/core/o11y/Cargo.toml new file mode 100644 index 000000000..08b387e53 --- /dev/null +++ b/core/o11y/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "unc-o11y" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "Observability helpers for the near codebase" +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +unc-crypto.workspace = true +unc-fmt.workspace = true +unc-primitives-core.workspace = true + +actix.workspace = true +base64.workspace = true +clap.workspace = true +once_cell.workspace = true +opentelemetry.workspace = true +opentelemetry-otlp.workspace = true +opentelemetry-semantic-conventions.workspace = true +prometheus.workspace = true +serde.workspace = true +serde_json.workspace = true +strum.workspace = true +thiserror.workspace = true +tokio.workspace = true +tracing.workspace = true +tracing-appender.workspace = true +tracing-opentelemetry.workspace = true +tracing-subscriber.workspace = true + +[dev-dependencies] +bencher.workspace = true +itoa.workspace = true +smartstring.workspace = true + +[features] +nightly_protocol = [ + "unc-fmt/nightly_protocol", + "unc-primitives-core/nightly_protocol", +] +nightly = [ + "nightly_protocol", + "unc-fmt/nightly", + "unc-primitives-core/nightly", +] +io_trace = [] + +[[bench]] +name = "metrics" +harness = false diff --git a/core/o11y/README.md b/core/o11y/README.md new file mode 100644 index 000000000..637f6959b --- /dev/null +++ b/core/o11y/README.md @@ -0,0 +1,10 @@ +Observability (o11y) helpers for the NEAR codebase. + +This crate contains all sorts of utilities to enable a more convenient observability implementation +in the NEAR codebase. + +The are three infrastructures: + +* `tracing`, for structured, hierarchical logging of events (see [`default_subscriber`] function function in particular) +* `metrics` -- convenience wrappers around prometheus metric, for reporting statistics. +* `io-tracer` -- custom infrastructure for observing DB accesses in particular (mostly for parameter estimator) diff --git a/core/o11y/benches/metrics.rs b/core/o11y/benches/metrics.rs new file mode 100644 index 000000000..dceadde02 --- /dev/null +++ b/core/o11y/benches/metrics.rs @@ -0,0 +1,109 @@ +#[macro_use] +extern crate bencher; + +use bencher::Bencher; +use unc_o11y::metrics::{try_create_int_counter_vec, IntCounter, IntCounterVec}; +use once_cell::sync::Lazy; + +static COUNTERS: Lazy = Lazy::new(|| { + try_create_int_counter_vec("unc_test_counters_1", "Just counters", &["shard_id"]).unwrap() +}); + +const NUM_SHARDS: usize = 8; + +fn inc_counter_vec_with_label_values(bench: &mut Bencher) { + bench.iter(|| { + for shard_id in 0..NUM_SHARDS { + COUNTERS.with_label_values(&[&shard_id.to_string()]).inc(); + } + }); +} + +fn inc_counter_vec_with_label_values_itoa(bench: &mut Bencher) { + bench.iter(|| { + for shard_id in 0..NUM_SHARDS { + let mut buffer = itoa::Buffer::new(); + let printed = buffer.format(shard_id); + COUNTERS.with_label_values(&[printed]).inc(); + } + }); +} + +fn inc_counter_vec_with_label_values_smartstring(bench: &mut Bencher) { + use std::fmt::Write; + bench.iter(|| { + for shard_id in 0..NUM_SHARDS { + let mut label = smartstring::alias::String::new(); + write!(label, "{shard_id}").unwrap(); + COUNTERS.with_label_values(&[&label]).inc(); + } + }); +} + +fn inc_counter_vec_with_label_values_stack(bench: &mut Bencher) { + use std::io::Write; + bench.iter(|| { + for shard_id in 0..NUM_SHARDS { + let mut buf = [0u8; 10]; + let mut cursor = std::io::Cursor::new(&mut buf[..]); + write!(cursor, "{shard_id}").unwrap(); + let len = cursor.position() as usize; + let label = unsafe { std::str::from_utf8_unchecked(&buf[..len]) }; + COUNTERS.with_label_values(&[label]).inc(); + } + }); +} + +fn inc_counter_vec_with_label_values_stack_no_format(bench: &mut Bencher) { + bench.iter(|| { + for shard_id in 0..NUM_SHARDS { + let mut buf = [0u8; 16]; + let mut idx = buf.len(); + let mut n = shard_id; + loop { + idx -= 1; + buf[idx] = b'0' + (n % 10) as u8; + n = n / 10; + if n == 0 { + break; + } + } + let label = unsafe { std::str::from_utf8_unchecked(&buf[idx..]) }; + COUNTERS.with_label_values(&[label]).inc(); + } + }); +} + +fn inc_counter_vec_cached(bench: &mut Bencher) { + const NUM_SHARDS: usize = 8; + let counters: Vec = (0..NUM_SHARDS) + .map(|shard_id| COUNTERS.with_label_values(&[&shard_id.to_string()])) + .collect(); + bench.iter(|| { + for counter in &counters { + counter.inc(); + } + }); +} + +fn inc_counter_vec_cached_str(bench: &mut Bencher) { + const NUM_SHARDS: usize = 8; + let shard_ids: Vec = (0..NUM_SHARDS).map(|shard_id| shard_id.to_string()).collect(); + bench.iter(|| { + for shard_id in &shard_ids { + COUNTERS.with_label_values(&[shard_id]).inc(); + } + }); +} + +benchmark_group!( + benches, + inc_counter_vec_with_label_values, + inc_counter_vec_with_label_values_itoa, + inc_counter_vec_with_label_values_smartstring, + inc_counter_vec_with_label_values_stack, + inc_counter_vec_with_label_values_stack_no_format, + inc_counter_vec_cached_str, + inc_counter_vec_cached, +); +benchmark_main!(benches); diff --git a/core/o11y/src/context.rs b/core/o11y/src/context.rs new file mode 100644 index 000000000..c265f9d27 --- /dev/null +++ b/core/o11y/src/context.rs @@ -0,0 +1,28 @@ +use tracing::Span; +use tracing_opentelemetry::OpenTelemetrySpanExt; + +/// Wraps an actix message with the current Span's context. +/// This lets us trace execution across several actix Actors. +#[derive(actix::Message, Debug)] +#[rtype(result = "::Result")] +pub struct WithSpanContext { + pub msg: T, + pub context: opentelemetry::Context, +} + +impl WithSpanContext { + pub fn new(msg: T) -> Self { + Self { msg, context: Span::current().context() } + } +} + +/// Allows easily attaching the current span's context to a Message. +pub trait WithSpanContextExt: actix::Message { + fn with_span_context(self) -> WithSpanContext + where + Self: Sized, + { + WithSpanContext::::new(self) + } +} +impl WithSpanContextExt for T {} diff --git a/core/o11y/src/delay_detector.rs b/core/o11y/src/delay_detector.rs new file mode 100644 index 000000000..c5e59a7b8 --- /dev/null +++ b/core/o11y/src/delay_detector.rs @@ -0,0 +1,92 @@ +use crate::metrics::try_create_histogram_vec; +use once_cell::sync::Lazy; +use prometheus::{exponential_buckets, HistogramVec}; +use std::time::Instant; +use tracing::span::Attributes; +use tracing::Id; +use tracing_subscriber::layer::Context; +use tracing_subscriber::registry::LookupSpan; +use tracing_subscriber::Layer; + +#[derive(Default)] +pub(crate) struct DelayDetectorLayer {} + +const MAX_BUSY_DURATION_NS: u64 = 500000000; // 0.5 sec + +pub(crate) static LONG_SPAN_HISTOGRAM: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_long_span_elapsed", + "Distribution of the duration of spans wth long duration", + &["name", "level", "target", "file", "line"], + Some(exponential_buckets(1.0, 1.2, 30).unwrap()), + ) + .unwrap() +}); + +struct Timings { + idle: u64, + busy: u64, + last: Instant, +} + +impl Timings { + fn new() -> Self { + Self { idle: 0, busy: 0, last: Instant::now() } + } +} + +impl Layer for DelayDetectorLayer +where + S: tracing::Subscriber + for<'span> LookupSpan<'span> + Send + Sync, +{ + fn on_new_span(&self, _attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { + let span = ctx.span(id).expect("Span not found, this is a bug"); + let mut extensions = span.extensions_mut(); + extensions.insert(Timings::new()); + } + + fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { + let span = ctx.span(id).expect("Span not found, this is a bug"); + let mut extensions = span.extensions_mut(); + if let Some(timings) = extensions.get_mut::() { + let now = Instant::now(); + timings.idle += (now - timings.last).as_nanos() as u64; + timings.last = now; + } + } + + fn on_exit(&self, id: &Id, ctx: Context<'_, S>) { + let span = ctx.span(id).expect("Span not found, this is a bug"); + let mut extensions = span.extensions_mut(); + if let Some(timings) = extensions.get_mut::() { + let now = Instant::now(); + timings.busy += (now - timings.last).as_nanos() as u64; + timings.last = now; + } + } + + fn on_close(&self, id: Id, ctx: Context<'_, S>) { + let span = ctx.span(&id).expect("Span not found, this is a bug"); + let extensions = span.extensions(); + if let Some(Timings { busy, mut idle, last }) = extensions.get::() { + idle += (Instant::now() - *last).as_nanos() as u64; + + if busy > &MAX_BUSY_DURATION_NS { + let level = span.metadata().level(); + let target = span.metadata().target(); + let file = span.metadata().file().unwrap_or(""); + let line = span.metadata().line().map_or("".to_string(), |x| x.to_string()); + let name = span.name(); + + let busy_sec = *busy as f64 * 1e-9; + let idle_sec = idle as f64 * 1e-9; + tracing::warn!(target: "delay_detector", + "Span duration too long: {busy_sec:.2}s. Idle time: {idle_sec:.2}s. {level}: {target}: {name}. {file}:{line}", + ); + LONG_SPAN_HISTOGRAM + .with_label_values(&[name, level.as_str(), target, file, &line]) + .observe(busy_sec); + } + } + } +} diff --git a/core/o11y/src/env_filter.rs b/core/o11y/src/env_filter.rs new file mode 100644 index 000000000..c73177041 --- /dev/null +++ b/core/o11y/src/env_filter.rs @@ -0,0 +1,90 @@ +use std::borrow::Cow; +use tracing_subscriber::filter::ParseError; +use tracing_subscriber::EnvFilter; + +/// The default value for the `RUST_LOG` environment variable if one isn't specified otherwise. +const DEFAULT_RUST_LOG: &str = "tokio_reactor=info,\ + config=info,\ + near=info,\ + recompress=info,\ + stats=info,\ + telemetry=info,\ + db=info,\ + delay_detector=info,\ + unc-performance-metrics=info,\ + state_viewer=info,\ + warn"; + +#[non_exhaustive] +#[derive(thiserror::Error, Debug)] +pub enum BuildEnvFilterError { + #[error("could not create a log filter for {1}")] + CreateEnvFilter(#[source] ParseError, String), +} + +#[derive(Debug)] +pub struct EnvFilterBuilder<'a> { + rust_log: Cow<'a, str>, + verbose: Option<&'a str>, +} + +impl<'a> EnvFilterBuilder<'a> { + /// Create the `EnvFilter` from the environment variable or the [`DEFAULT_RUST_LOG`] value if + /// the environment is not set. + pub fn from_env() -> Self { + Self::new( + std::env::var("RUST_LOG").map(Cow::Owned).unwrap_or(Cow::Borrowed(DEFAULT_RUST_LOG)), + ) + } + + /// Specify an exact `RUST_LOG` value to use. + /// + /// This method will not inspect the environment variable. + pub fn new>>(rust_log: S) -> Self { + Self { rust_log: rust_log.into(), verbose: None } + } + + /// Make the produced [`EnvFilter`] verbose. + /// + /// If the `module` string is empty, all targets will log debug output. Otherwise only the + /// specified target will log the debug output. + pub fn verbose(mut self, target: Option<&'a str>) -> Self { + self.verbose = target; + self + } + + /// Construct an [`EnvFilter`] as configured. + pub fn finish(self) -> Result { + let mut env_filter = EnvFilter::try_new(self.rust_log.clone()) + .map_err(|err| BuildEnvFilterError::CreateEnvFilter(err, self.rust_log.to_string()))?; + if let Some(module) = self.verbose { + env_filter = env_filter + .add_directive("cranelift_codegen=warn".parse().expect("parse directive")) + .add_directive("h2=warn".parse().expect("parse directive")) + .add_directive("tower=warn".parse().expect("parse directive")) + .add_directive("trust_dns_resolver=warn".parse().expect("parse directive")) + .add_directive("trust_dns_proto=warn".parse().expect("parse directive")); + env_filter = if module.is_empty() { + env_filter.add_directive(tracing::Level::DEBUG.into()) + } else { + let directive = format!("{}=debug", module).parse().map_err(|err| { + BuildEnvFilterError::CreateEnvFilter(err, format!("{}=debug", module)) + })?; + env_filter.add_directive(directive) + }; + } + Ok(env_filter) + } +} + +pub fn make_env_filter(verbose: Option<&str>) -> Result { + let env_filter = EnvFilterBuilder::from_env().verbose(verbose).finish()?; + // Sandbox node can log to sandbox logging target via sandbox_debug_log host function. + // This is hidden by default so we enable it for sandbox node. + let env_filter = if cfg!(feature = "sandbox") { + env_filter.add_directive("sandbox=debug".parse().unwrap()) + } else { + env_filter + }; + Ok(env_filter) +} diff --git a/core/o11y/src/io_tracer.rs b/core/o11y/src/io_tracer.rs new file mode 100644 index 000000000..f468d09d2 --- /dev/null +++ b/core/o11y/src/io_tracer.rs @@ -0,0 +1,332 @@ +#![cfg(feature = "io_trace")] +//! `tracing` layer to collect the sequence of IO operations executed. +//! +//! This module contains a somewhat quirky way of collecting a detailed log of +//! which IO operations have been executed. The `tracing` crate is abused for +//! this, as it provides an easy way to globally emit such events and organize +//! them in a hierarchy of spans. +//! +//! The goal is to have something to look at to understand what requests are +//! sent to the DB and for what reason. Not necessarily timing of events.(Timing +//! might be affected by the sheer amount of details that this instrumentation +//! is capable of producing.) But rather something that makes it obvious, for +//! example, how effective an existing caching layer is for a workload by +//! showing when the cache was hit and when it was missed. +//! +//! Another, equally important, use case is replaying the output for further +//! analysis. The estimator has a replay command that understands the output +//! produced by the IO trace. + +use base64::Engine; +use std::collections::HashMap; +use std::io::Write; +use tracing::{span, Subscriber}; +use tracing_appender::non_blocking::{NonBlocking, WorkerGuard}; +use tracing_subscriber::fmt::MakeWriter; +use tracing_subscriber::registry::LookupSpan; +use tracing_subscriber::Layer; + +/// Tracing layer that produces a record of IO operations. +pub struct IoTraceLayer { + make_writer: NonBlocking, +} + +enum IoEventType { + StorageOp(StorageOp), + DbOp(DbOp), +} +#[derive(strum::Display)] +#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] +enum StorageOp { + Read, + Write, + Remove, + Exists, + Other, +} +#[derive(strum::Display)] +#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] +enum DbOp { + Get, + Insert, + Set, + UpdateRc, + Delete, + DeleteAll, + Other, +} + +/// Formatted but not-yet printed output lines. +/// +/// Events are bundled together and only printed after the enclosing span exits. +/// This allows to print information at the tpo that is only available later on. +/// +/// Note: Type used as key in `AnyMap` inside span extensions. +struct OutputBuffer(Vec); + +/// Formatted but not-yet printed output line. +struct BufferedLine { + indent: usize, + output_line: String, +} + +/// Information added to a span through events happening within. +#[derive(Default)] +struct SpanInfo { + key_values: Vec, + counts: HashMap, +} + +impl LookupSpan<'span>> Layer for IoTraceLayer { + fn on_new_span( + &self, + attrs: &span::Attributes<'_>, + id: &span::Id, + ctx: tracing_subscriber::layer::Context<'_, S>, + ) { + let span = ctx.span(id).unwrap(); + + // Store span field values to be printed on exit, after they are + // enhanced with additional information from events. + let mut span_info = SpanInfo::default(); + attrs.record(&mut span_info); + span.extensions_mut().insert(span_info); + + // This will be used to add lines that should be printed below the span + // opening line. + span.extensions_mut().insert(OutputBuffer(vec![])); + } + + fn on_event(&self, event: &tracing::Event<'_>, ctx: tracing_subscriber::layer::Context<'_, S>) { + if event.metadata().target() == "io_tracer_count" { + // Events specifically added to add more info to spans in IO Tracer. + // Marked with `target: "io_tracer_count"`. + let mut span = ctx.event_span(event); + while let Some(parent) = span { + if let Some(span_info) = parent.extensions_mut().get_mut::() { + event.record(span_info); + break; + } else { + span = parent.parent(); + } + } + } else { + // All other events. These can be for target "io_tracer" or + // something else, that has values for the `IoEventVisitor`. + self.record_io_event(event, ctx); + } + } + + #[allow(clippy::arithmetic_side_effects)] + fn on_exit(&self, id: &span::Id, ctx: tracing_subscriber::layer::Context<'_, S>) { + // When the span exits, produce one line for the span itself that + // includes key=value pairs from `SpanInfo`. Then also add indentation + // to all lines buffered in the `OutputBuffer` extension. + // If no parent span exists, print all buffered lines. + let span = ctx.span(id).unwrap(); + let name = span.name(); + let span_line = { + let mut span_info = span.extensions_mut().replace(SpanInfo::default()).unwrap(); + for (key, count) in span_info.counts.drain() { + span_info.key_values.push(format!("{key}={count}")); + } + format!("{name} {}", span_info.key_values.join(" ")) + }; + + let OutputBuffer(mut exiting_buffer) = + span.extensions_mut().replace(OutputBuffer(vec![])).unwrap(); + + if let Some(parent) = span.parent() { + let mut ext = parent.extensions_mut(); + let OutputBuffer(parent_buffer) = ext.get_mut().unwrap(); + parent_buffer.push(BufferedLine { indent: 2, output_line: span_line }); + parent_buffer.extend(exiting_buffer.drain(..).map(|mut line| { + line.indent += 2; + line + })); + } else { + let mut out = self.make_writer.make_writer(); + writeln!(out, "{span_line}").unwrap(); + for BufferedLine { indent, output_line } in exiting_buffer.drain(..) { + writeln!(out, "{:indent$}{output_line}", "").unwrap(); + } + } + } + + fn on_record( + &self, + span: &span::Id, + values: &span::Record<'_>, + ctx: tracing_subscriber::layer::Context<'_, S>, + ) { + // Similar to how span fields are recorded on the `SpanInfo` extension, + // add records to the same. + let mut span = ctx.span(span); + while let Some(parent) = span { + if let Some(span_info) = parent.extensions_mut().get_mut::() { + values.record(span_info); + break; + } else { + span = parent.parent(); + } + } + } +} + +impl IoTraceLayer { + pub(crate) fn new(out: W) -> (Self, WorkerGuard) { + let (make_writer, guard) = NonBlocking::new(out); + (Self { make_writer }, guard) + } + + /// Print or buffer formatted tracing events that look like an IO event. + /// + /// IO events are: + /// - DB operations, emitted in core/store/src/lib.rs + /// - Storage operations, emitted in runtime/unc-vm-logic/src/logic.rs + fn record_io_event LookupSpan<'span>>( + &self, + event: &tracing::Event, + ctx: tracing_subscriber::layer::Context, + ) { + /// `Display`s ` size=` if wrapped value is Some; nothing + /// otherwise. + struct FormattedSize(Option); + + impl std::fmt::Display for FormattedSize { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.map_or(Ok(()), |size| write!(fmt, " size={size}")) + } + } + + let mut visitor = IoEventVisitor::default(); + event.record(&mut visitor); + match visitor.t { + Some(IoEventType::DbOp(db_op)) => { + let col = visitor.col.as_deref().unwrap_or("?"); + let key = visitor.key.as_deref().unwrap_or("?"); + let size = FormattedSize(visitor.size); + let output_line = format!("{db_op} {col} {key:?}{size}"); + if let Some(span) = ctx.event_span(event) { + span.extensions_mut() + .get_mut::() + .unwrap() + .0 + .push(BufferedLine { indent: 2, output_line }); + } else { + // Print top level unbuffered. + writeln!(self.make_writer.make_writer(), "{output_line}").unwrap(); + } + } + Some(IoEventType::StorageOp(storage_op)) => { + let key_bytes = visitor.key.map(|key| { + base64::engine::general_purpose::STANDARD + .decode(key) + .expect("key was not properly base64-encoded") + }); + let key = key_bytes + .as_ref() + .map(|k| format!("{}", unc_fmt::Bytes(&*k))) + .unwrap_or_else(|| String::from("?")); + let size = FormattedSize(visitor.size); + let tn_db_reads = visitor.tn_db_reads.unwrap(); + let tn_mem_reads = visitor.tn_mem_reads.unwrap(); + + let span_info = + format!("{storage_op} key={key}{size} tn_db_reads={tn_db_reads} tn_mem_reads={tn_mem_reads}"); + + let span = + ctx.event_span(event).expect("storage operations must happen inside span"); + span.extensions_mut().get_mut::().unwrap().key_values.push(span_info); + } + None => { + // Ignore irrelevant tracing events. + } + } + } +} + +/// Builder object to fill in field-by-field on traced events. +#[derive(Default)] +struct IoEventVisitor { + t: Option, + key: Option, + col: Option, + size: Option, + evicted_len: Option, + tn_db_reads: Option, + tn_mem_reads: Option, +} + +impl tracing::field::Visit for IoEventVisitor { + fn record_u64(&mut self, field: &tracing::field::Field, value: u64) { + match field.name() { + "size" => self.size = Some(value), + "evicted_len" => self.evicted_len = Some(value), + "tn_db_reads" => self.tn_db_reads = Some(value), + "tn_mem_reads" => self.tn_mem_reads = Some(value), + _ => { /* Ignore other values, likely they are used in logging. */ } + } + } + fn record_i64(&mut self, field: &tracing::field::Field, value: i64) { + if value >= 0 { + self.record_u64(field, value as u64); + } + } + fn record_str(&mut self, field: &tracing::field::Field, value: &str) { + match field.name() { + "key" => self.key = Some(value.to_owned()), + "col" => self.col = Some(value.to_owned()), + "storage_op" => { + let op = match value { + "write" => StorageOp::Write, + "read" => StorageOp::Read, + "exists" => StorageOp::Exists, + "remove" => StorageOp::Remove, + _ => StorageOp::Other, + }; + self.t = Some(IoEventType::StorageOp(op)); + } + "size" => { + self.size = value.parse().ok(); + } + "db_op" => { + let op = match value { + "get" => DbOp::Get, + "insert" => DbOp::Insert, + "set" => DbOp::Set, + "update_rc" => DbOp::UpdateRc, + "delete" => DbOp::Delete, + "delete_all" => DbOp::DeleteAll, + _ => DbOp::Other, + }; + self.t = Some(IoEventType::DbOp(op)); + } + _ => { /* Ignore other values, likely they are used in logging. */ } + } + } + fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { + self.record_str(field, &format!("{value:?}")) + } +} + +impl tracing::field::Visit for SpanInfo { + #[allow(clippy::arithmetic_side_effects)] + fn record_str(&mut self, field: &tracing::field::Field, value: &str) { + // "count" is a special field, everything else are key values pairs. + if field.name() == "counter" { + *self.counts.entry(value.to_string()).or_default() += 1; + } else { + self.record_debug(field, &value); + } + } + + fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { + let name = field.name(); + // Some fields are too verbose for the trace, ignore them on a case-by-case basis. + let ignore = ["message", "node_counter"]; + if !ignore.contains(&name) { + self.key_values.push(format!("{name}={value:?}")); + } + } +} diff --git a/core/o11y/src/lib.rs b/core/o11y/src/lib.rs new file mode 100644 index 000000000..8bcfbd509 --- /dev/null +++ b/core/o11y/src/lib.rs @@ -0,0 +1,113 @@ +#![doc = include_str!("../README.md")] +#![deny(clippy::arithmetic_side_effects)] + +pub use context::*; +pub use env_filter::{BuildEnvFilterError, EnvFilterBuilder}; +pub use opentelemetry::OpenTelemetryLevel; +pub use reload::{reload, reload_log_config}; +#[cfg(feature = "io_trace")] +pub use subscriber::make_io_tracing_layer; +pub use subscriber::{default_subscriber, default_subscriber_with_opentelemetry, Options}; +pub use tracing_opentelemetry::OpenTelemetrySpanExt; +pub use {tracing, tracing_appender, tracing_subscriber}; + +/// Custom tracing subscriber implementation that produces IO traces. +pub mod context; +pub mod env_filter; +mod io_tracer; +pub mod log_config; +mod log_counter; +pub mod macros; +pub mod metrics; +mod opentelemetry; +mod reload; +mod subscriber; +pub mod testonly; + +/// Produce a tracing-event for target "io_tracer" that will be consumed by the +/// IO-tracer, if the feature has been enabled. +#[macro_export] +#[cfg(feature = "io_trace")] +macro_rules! io_trace { + (count: $name:expr) => { tracing::trace!( target: "io_tracer_count", counter = $name) }; + ($($fields:tt)*) => { tracing::trace!( target: "io_tracer", $($fields)*) }; +} + +#[macro_export] +#[cfg(not(feature = "io_trace"))] +macro_rules! io_trace { + (count: $name:expr) => {}; + ($($fields:tt)*) => {}; +} + +/// Prints backtrace to stderr. +/// +/// This is intended as a printf-debugging aid. +pub fn print_backtrace() { + let bt = std::backtrace::Backtrace::force_capture(); + eprintln!("{bt:?}") +} + +/// Asserts that the condition is true, logging an error otherwise. +/// +/// This macro complements `assert!` and `debug_assert`. All three macros should +/// only be used for conditions, whose violation signifise a programming error. +/// All three macros are no-ops if the condition is true. +/// +/// The behavior when the condition is false (i.e. when the assert fails) is +/// different, and informs different usage patterns. +/// +/// `assert!` panics. Use it for sanity-checking invariants, whose violation can +/// compromise correctness of the protocol. For example, it's better to shut a +/// node down via a panic than to admit potentially non-deterministic behavior. +/// +/// `debug_assert!` panics if `cfg(debug_assertions)` is true, that is, only +/// during development. In production, `debug_assert!` is compiled away (that +/// is, the condition is not evaluated at all). Use `debug_assert!` if +/// evaluating the condition is too slow. In other words, `debug_assert!` is a +/// performance optimization. +/// +/// Finally, `log_assert!` panics in debug mode, while in release mode it emits +/// a `tracing::error!` log line. Use it for sanity-checking non-essential +/// invariants, whose violation signals a bug in the code, where we'd rather +/// avoid shutting the whole node down. +/// +/// For example, `log_assert` is a great choice to use in some auxilary code +/// paths -- would be a shame if a bug in, eg, metrics collection code brought +/// the whole network down. +/// +/// Another use case is adding new asserts to the old code -- if you are only +/// 99% sure that the assert is correct, and there's evidance that the old code +/// is working fine in practice, `log_assert!` is the right choice! +/// +/// References: +/// * +#[macro_export] +macro_rules! log_assert { + ($cond:expr) => { + $crate::log_assert!($cond, "assertion failed: {}", stringify!($cond)) + }; + + ($cond:expr, $fmt:literal $($arg:tt)*) => { + if cfg!(debug_assertions) { + assert!($cond, $fmt $($arg)*); + } else { + if !$cond { + $crate::tracing::error!($fmt $($arg)*); + } + } + }; +} + +/// The same as 'log_assert' but always fails. +/// +/// `log_assert_fail!` panics in debug mode, while in release mode it emits +/// a `tracing::error!` log line. Use it for sanity-checking non-essential +/// invariants, whose violation signals a bug in the code, where we'd rather +/// avoid shutting the whole node down. +#[macro_export] +macro_rules! log_assert_fail { + ($fmt:literal $($arg:tt)*) => { + $crate::log_assert!(false, $fmt $($arg)*); + }; +} diff --git a/core/o11y/src/log_config.rs b/core/o11y/src/log_config.rs new file mode 100644 index 000000000..304724cf6 --- /dev/null +++ b/core/o11y/src/log_config.rs @@ -0,0 +1,24 @@ +use crate::OpenTelemetryLevel; +use serde::{Deserialize, Serialize}; +use std::path::Path; +use std::{fs::File, io::Write}; + +/// Configures logging. +#[derive(Default, Serialize, Deserialize, Clone, Debug)] +pub struct LogConfig { + /// Comma-separated list of EnvFitler directives. + pub rust_log: Option, + /// Some("") enables global debug logging. + /// Some("module") enables debug logging for "module". + pub verbose_module: Option, + /// Verbosity level of collected traces. + pub opentelemetry_level: Option, +} + +impl LogConfig { + pub fn write_to_file(&self, path: &Path) -> std::io::Result<()> { + let mut file = File::create(path)?; + let str = serde_json::to_string_pretty(self)?; + file.write_all(str.as_bytes()) + } +} diff --git a/core/o11y/src/log_counter.rs b/core/o11y/src/log_counter.rs new file mode 100644 index 000000000..91561fd0b --- /dev/null +++ b/core/o11y/src/log_counter.rs @@ -0,0 +1,101 @@ +use crate::metrics::try_create_int_counter_vec; +use once_cell::sync::Lazy; +use prometheus::{IntCounter, IntCounterVec}; +use tracing_subscriber::layer::{Context, Layered}; +use tracing_subscriber::registry::LookupSpan; +use tracing_subscriber::Layer; + +pub(crate) static LOG_COUNTER: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_log_msg_total", + "Number of messages logged at various log levels", + &["level"], + ) + .unwrap() +}); + +pub(crate) static LOG_WITH_LOCATION_COUNTER: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_log_msg_with_loc_total", + "Number of messages logged at various log levels wth target and location", + &["level", "target", "file", "line"], + ) + .unwrap() +}); + +pub(crate) type LogCountingLayer = Layered; + +/// A tracing Layer that updates prometheus metrics based on the metadata of log events. +pub(crate) struct LogCounter { + // Initializes individual counters as a performance optimization. + error_metric: IntCounter, + warn_metric: IntCounter, + info_metric: IntCounter, + debug_metric: IntCounter, + trace_metric: IntCounter, +} + +impl LogCounter { + /// Increments a counter for every log message. + fn count_log(&self, level: &tracing::Level) { + match level { + &tracing::Level::ERROR => self.error_metric.inc(), + &tracing::Level::WARN => self.warn_metric.inc(), + &tracing::Level::INFO => self.info_metric.inc(), + &tracing::Level::DEBUG => self.debug_metric.inc(), + &tracing::Level::TRACE => self.trace_metric.inc(), + }; + } + + /// Increments a counter with target and LoC for high severity messages. + fn count_log_with_loc( + &self, + level: &tracing::Level, + target: &str, + file: Option<&str>, + line: Option, + ) { + match level { + &tracing::Level::ERROR | &tracing::Level::WARN | &tracing::Level::INFO => { + LOG_WITH_LOCATION_COUNTER + .with_label_values(&[ + &level.as_str(), + target, + file.unwrap_or(""), + &line.map_or("".to_string(), |x| x.to_string()), + ]) + .inc() + } + // Retaining LoC for low-severity messages can lead to excessive memory usage. + // Therefore, only record LoC for high severity log messages. + _ => {} + }; + } +} + +impl Default for LogCounter { + fn default() -> Self { + Self { + error_metric: LOG_COUNTER.with_label_values(&["error"]), + warn_metric: LOG_COUNTER.with_label_values(&["warn"]), + info_metric: LOG_COUNTER.with_label_values(&["info"]), + debug_metric: LOG_COUNTER.with_label_values(&["debug"]), + trace_metric: LOG_COUNTER.with_label_values(&["trace"]), + } + } +} + +impl Layer for LogCounter +where + S: tracing::Subscriber + for<'span> LookupSpan<'span> + Send + Sync, +{ + fn on_event(&self, event: &tracing::Event, _ctx: Context) { + let level = event.metadata().level(); + self.count_log(level); + + let target = event.metadata().target(); + let file = event.metadata().file(); + let line = event.metadata().line(); + self.count_log_with_loc(level, target, file, line); + } +} diff --git a/core/o11y/src/macros.rs b/core/o11y/src/macros.rs new file mode 100644 index 000000000..1faa59a92 --- /dev/null +++ b/core/o11y/src/macros.rs @@ -0,0 +1,56 @@ +/// Internal use only. +#[macro_export] +macro_rules! handler_span { + (target: $target:expr, level: $lvl:expr, $msg:expr, $($extra_fields:tt)*) => {{ + let WithSpanContext { msg, context, .. } = $msg; + let span = tracing::span!( + target: $target, + $lvl, + "handle", + handler = unc_o11y::macros::type_name_of(&msg), + actor = unc_o11y::macros::last_component_of_name(std::any::type_name::()), + $($extra_fields)*) + .entered(); + span.set_parent(context); + (span, msg) + }}; +} + +/// A macro that lets attach `handle()` functions to the tracing context that +/// generated the actix message being processed. +/// Creates a DEBUG-level span with the handler type name and the message type name as attributes. +#[macro_export] +macro_rules! handler_debug_span { + (target: $target:expr, $msg:expr) => { + $crate::handler_span!(target: $target, level: tracing::Level::DEBUG, $msg, ) + }; + (target: $target:expr, $msg:expr, $($extra_fields:tt)*) => { + $crate::handler_span!(target: $target, level: tracing::Level::DEBUG, $msg, $($extra_fields)*) + }; +} + +/// A macro that lets attach `handle()` functions to the tracing context that +/// generated the actix message being processed. +/// Creates a TRACE-level span with the handler type name and the message type name as attributes. +#[macro_export] +macro_rules! handler_trace_span { + (target: $target:expr, $msg:expr) => { + $crate::handler_span!(target: $target, level: tracing::Level::TRACE, $msg, ) + }; + (target: $target:expr, $msg:expr, $($extra_fields:tt)*) => { + $crate::handler_span!(target: $target, level: tracing::Level::TRACE, $msg, $($extra_fields)*) + }; +} + +/// For internal use by `handler_span!`. +/// Given 'abc::bcd::cde' returns 'cde'. +/// Given 'abc' returns 'abc'. +pub fn last_component_of_name(name: &str) -> &str { + name.rsplit_once("::").map_or(name, |(_, name)| name) +} + +/// For internal use by `handler_span!`. +/// Returns the last component of the name of type `T`. +pub fn type_name_of(_: &T) -> &str { + last_component_of_name(std::any::type_name::()) +} diff --git a/core/o11y/src/metrics.rs b/core/o11y/src/metrics.rs new file mode 100644 index 000000000..86e027170 --- /dev/null +++ b/core/o11y/src/metrics.rs @@ -0,0 +1,249 @@ +//! A fork of the lighthouse_metrics crate used to implement prometheus +//! +//! A wrapper around the `prometheus` crate that provides a global, `lazy_static` metrics registry +//! and functions to add and use the following components (more info at +//! [Prometheus docs](https://prometheus.io/docs/concepts/metric_types/)): +//! +//! - `Histogram`: used with `start_timer()` and `observe_duration()` or +//! `observe()` method to record durations (e.g., block processing time). +//! - `IncCounter`: used to represent an ideally ever-growing, never-shrinking +//! integer (e.g., number of block processing requests). +//! - `IntGauge`: used to represent an varying integer (e.g., number of +//! attestations per block). +//! +//! ## Important +//! +//! Metrics will fail if two items have the same `name`. All metrics must have a unique `name`. +//! Because we use a global registry there is no namespace per crate, it's one big global space. +//! +//! See the [Prometheus naming best practices](https://prometheus.io/docs/practices/naming/) when +//! choosing metric names. +//! +//! ## Example +//! +//! ```rust +//! use once_cell::sync::Lazy; +//! +//! use unc_o11y::metrics::*; +//! +//! // These metrics are "magically" linked to the global registry defined in `lighthouse_metrics`. +//! pub static RUN_COUNT: Lazy = Lazy::new(|| { +//! try_create_int_counter( +//! "unc_runs_total", +//! "Total number of runs", +//! ) +//! .unwrap() +//! }); +//! pub static CURRENT_VALUE: Lazy = Lazy::new(|| { +//! try_create_int_gauge( +//! "unc_current_value", +//! "The current value", +//! ) +//! .unwrap() +//! }); +//! pub static RUN_TIME: Lazy = Lazy::new(|| { +//! try_create_histogram( +//! "unc_run_seconds", +//! "Time taken (measured to high precision)", +//! ) +//! .unwrap() +//! }); +//! +//! fn main() { +//! for i in 0..100 { +//! RUN_COUNT.inc(); +//! let timer = RUN_TIME.start_timer(); +//! for j in 0..10 { +//! CURRENT_VALUE.set(j); +//! println!("Howdy partner"); +//! } +//! timer.observe_duration(); +//! } +//! +//! assert_eq!(100, RUN_COUNT.get()); +//! assert_eq!(9, CURRENT_VALUE.get()); +//! assert_eq!(100, RUN_TIME.get_sample_count()); +//! assert!(0.0 < RUN_TIME.get_sample_sum()); +//! } +//! ``` + +use once_cell::sync::Lazy; +pub use prometheus::{ + self, core::MetricVec, core::MetricVecBuilder, exponential_buckets, linear_buckets, Counter, + Encoder, Gauge, GaugeVec, Histogram, HistogramOpts, HistogramVec, IntCounter, IntCounterVec, + IntGauge, IntGaugeVec, Opts, Result, TextEncoder, +}; +use std::collections::HashSet; + +/// Collect all the metrics for reporting. +pub fn gather() -> Vec { + prometheus::gather() +} + +/// Attempts to crate an `IntCounter`, returning `Err` if the registry does not accept the counter +/// (potentially due to naming conflict). +pub fn try_create_int_counter(name: &str, help: &str) -> Result { + check_metric_unc_prefix(name)?; + let opts = Opts::new(name, help); + let counter = IntCounter::with_opts(opts)?; + prometheus::register(Box::new(counter.clone()))?; + Ok(counter) +} + +/// Attempts to crate an `IntCounterVec`, returning `Err` if the registry does not accept the counter +/// (potentially due to naming conflict). +pub fn try_create_int_counter_vec( + name: &str, + help: &str, + labels: &[&str], +) -> Result { + check_metric_unc_prefix(name)?; + let opts = Opts::new(name, help); + let counter = IntCounterVec::new(opts, labels)?; + prometheus::register(Box::new(counter.clone()))?; + Ok(counter) +} + +/// Attempts to crate an `Counter`, returning `Err` if the registry does not accept the counter +/// (potentially due to naming conflict). +pub fn try_create_counter(name: &str, help: &str) -> Result { + check_metric_unc_prefix(name)?; + let opts = Opts::new(name, help); + let counter = Counter::with_opts(opts)?; + prometheus::register(Box::new(counter.clone()))?; + Ok(counter) +} + +/// Attempts to crate an `IntGauge`, returning `Err` if the registry does not accept the gauge +/// (potentially due to naming conflict). +pub fn try_create_int_gauge(name: &str, help: &str) -> Result { + check_metric_unc_prefix(name)?; + let opts = Opts::new(name, help); + let gauge = IntGauge::with_opts(opts)?; + prometheus::register(Box::new(gauge.clone()))?; + Ok(gauge) +} + +/// Attempts to crate an `IntGaugeVec`, returning `Err` if the registry does not accept the gauge +/// (potentially due to naming conflict). +pub fn try_create_int_gauge_vec(name: &str, help: &str, labels: &[&str]) -> Result { + check_metric_unc_prefix(name)?; + let opts = Opts::new(name, help); + let gauge = IntGaugeVec::new(opts, labels)?; + prometheus::register(Box::new(gauge.clone()))?; + Ok(gauge) +} + +/// Attempts to crate an `Gauge`, returning `Err` if the registry does not accept the gauge +/// (potentially due to naming conflict). +pub fn try_create_gauge(name: &str, help: &str) -> Result { + check_metric_unc_prefix(name)?; + let opts = Opts::new(name, help); + let gauge = Gauge::with_opts(opts)?; + prometheus::register(Box::new(gauge.clone()))?; + Ok(gauge) +} + +/// Attempts to crate an `GaugeVec`, returning `Err` if the registry does not accept the gauge +/// (potentially due to naming conflict). +pub fn try_create_gauge_vec(name: &str, help: &str, labels: &[&str]) -> Result { + check_metric_unc_prefix(name)?; + let opts = Opts::new(name, help); + let gauge = GaugeVec::new(opts, labels)?; + prometheus::register(Box::new(gauge.clone()))?; + Ok(gauge) +} + +/// Attempts to crate a `Histogram`, returning `Err` if the registry does not accept the counter +/// (potentially due to naming conflict). +pub fn try_create_histogram(name: &str, help: &str) -> Result { + check_metric_unc_prefix(name)?; + let opts = HistogramOpts::new(name, help); + let histogram = Histogram::with_opts(opts)?; + prometheus::register(Box::new(histogram.clone()))?; + Ok(histogram) +} + +/// Attempts to crate a `Histogram`, returning `Err` if the registry does not accept the counter +/// (potentially due to naming conflict). +pub fn try_create_histogram_with_buckets( + name: &str, + help: &str, + buckets: Vec, +) -> Result { + check_metric_unc_prefix(name)?; + let opts = HistogramOpts::new(name, help).buckets(buckets); + let histogram = Histogram::with_opts(opts)?; + prometheus::register(Box::new(histogram.clone()))?; + Ok(histogram) +} + +/// Attempts to create a `HistogramVector`, returning `Err` if the registry does not accept the counter +/// (potentially due to naming conflict). +pub fn try_create_histogram_vec( + name: &str, + help: &str, + labels: &[&str], + buckets: Option>, +) -> Result { + check_metric_unc_prefix(name)?; + let mut opts = HistogramOpts::new(name, help); + if let Some(buckets) = buckets { + opts = opts.buckets(buckets); + } + let histogram = HistogramVec::new(opts, labels)?; + prometheus::register(Box::new(histogram.clone()))?; + Ok(histogram) +} + +pub fn processing_time_buckets() -> Vec { + let mut buckets = vec![0.01, 0.025, 0.05, 0.1, 0.25, 0.5]; + buckets.extend_from_slice(&exponential_buckets(1.0, 1.3, 12).unwrap()); + buckets +} + +static EXCEPTIONS: Lazy> = Lazy::new(|| { + HashSet::from([ + "flat_storage_cached_changes_num_items", + "flat_storage_cached_changes_size", + "flat_storage_cached_deltas", + "flat_storage_creation_fetched_state_items", + "flat_storage_creation_fetched_state_parts", + "flat_storage_creation_remaining_state_parts", + "flat_storage_creation_status", + "flat_storage_creation_threads_used", + "flat_storage_distance_to_head", + "flat_storage_head_height", + "flat_storage_hops_to_head", + ]) +}); + +/// Expect metrics exported by framework to have a common prefix. This helps in the following cases: +/// * Avoids name conflicts with metrics from other systems. +/// * Helps filter and query metrics. +/// * Makes it easy to understand which binary export a certain metric. +fn check_metric_unc_prefix(name: &str) -> Result<()> { + // Some metrics were already introduced without the desired prefix. + // TODO(#9065): Consistent metric naming. + if name.starts_with("unc_") || EXCEPTIONS.contains(name) { + Ok(()) + } else { + Err(prometheus::Error::Msg(format!( + "Metrics are expected to start with 'unc_', got {}", + name + ))) + } +} + +#[cfg(test)] +mod tests { + use crate::metrics::check_metric_unc_prefix; + + #[test] + fn test_unc_prefix() { + assert!(check_metric_unc_prefix("unc_abc").is_ok()); + assert!(check_metric_unc_prefix("flat_storage_head_height").is_ok()); + assert!(check_metric_unc_prefix("near").is_err()); + assert!(check_metric_unc_prefix("abc").is_err()); + } +} diff --git a/core/o11y/src/opentelemetry.rs b/core/o11y/src/opentelemetry.rs new file mode 100644 index 000000000..0b53fece1 --- /dev/null +++ b/core/o11y/src/opentelemetry.rs @@ -0,0 +1,76 @@ +use crate::reload::TracingLayer; +use unc_crypto::PublicKey; +use unc_primitives_core::types::AccountId; +use opentelemetry::sdk::trace::{self, IdGenerator, Sampler}; +use opentelemetry::sdk::Resource; +use opentelemetry::KeyValue; +use opentelemetry_semantic_conventions::resource::SERVICE_NAME; +use tracing::level_filters::LevelFilter; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::registry::LookupSpan; +use tracing_subscriber::{reload, Layer}; + +// Doesn't define WARN and ERROR, because the highest verbosity of spans is INFO. +#[derive(Copy, Clone, Debug, Default, clap::ValueEnum, serde::Serialize, serde::Deserialize)] +pub enum OpenTelemetryLevel { + #[default] + OFF, + INFO, + DEBUG, + TRACE, +} + +/// Constructs an OpenTelemetryConfig which sends span data to an external collector. +// +// NB: this function is `async` because `install_batch(Tokio)` requires a tokio context to +// register timers and channels and whatnot. +pub(crate) async fn add_opentelemetry_layer( + opentelemetry_level: OpenTelemetryLevel, + chain_id: String, + node_public_key: PublicKey, + account_id: Option, + subscriber: S, +) -> (TracingLayer, reload::Handle) +where + S: tracing::Subscriber + for<'span> LookupSpan<'span> + Send + Sync, +{ + let filter = get_opentelemetry_filter(opentelemetry_level); + let (filter, handle) = reload::Layer::::new(filter); + + let mut resource = vec![ + KeyValue::new("chain_id", chain_id), + KeyValue::new("node_id", node_public_key.to_string()), + ]; + // Prefer account name as the node name. + // Fallback to a node public key if a validator key is unavailable. + let service_name = if let Some(account_id) = account_id { + resource.push(KeyValue::new("account_id", account_id.to_string())); + format!("uncd:{}", account_id) + } else { + format!("uncd:{}", node_public_key) + }; + resource.push(KeyValue::new(SERVICE_NAME, service_name)); + + let tracer = opentelemetry_otlp::new_pipeline() + .tracing() + .with_exporter(opentelemetry_otlp::new_exporter().tonic()) + .with_trace_config( + trace::config() + .with_sampler(Sampler::AlwaysOn) + .with_id_generator(IdGenerator::default()) + .with_resource(Resource::new(resource)), + ) + .install_batch(opentelemetry::runtime::Tokio) + .unwrap(); + let layer = tracing_opentelemetry::layer().with_tracer(tracer).with_filter(filter); + (subscriber.with(layer), handle) +} + +pub(crate) fn get_opentelemetry_filter(opentelemetry_level: OpenTelemetryLevel) -> LevelFilter { + match opentelemetry_level { + OpenTelemetryLevel::OFF => LevelFilter::OFF, + OpenTelemetryLevel::INFO => LevelFilter::INFO, + OpenTelemetryLevel::DEBUG => LevelFilter::DEBUG, + OpenTelemetryLevel::TRACE => LevelFilter::TRACE, + } +} diff --git a/core/o11y/src/reload.rs b/core/o11y/src/reload.rs new file mode 100644 index 000000000..2e0338606 --- /dev/null +++ b/core/o11y/src/reload.rs @@ -0,0 +1,167 @@ +use crate::opentelemetry::get_opentelemetry_filter; +use crate::{log_config, log_counter, BuildEnvFilterError, EnvFilterBuilder, OpenTelemetryLevel}; +use once_cell::sync::OnceCell; +use opentelemetry::sdk::trace::Tracer; +use tracing::level_filters::LevelFilter; +use tracing_appender::non_blocking::NonBlocking; +use tracing_opentelemetry::OpenTelemetryLayer; +use tracing_subscriber::filter::Filtered; +use tracing_subscriber::layer::Layered; +use tracing_subscriber::reload::Handle; +use tracing_subscriber::{fmt, reload, EnvFilter, Registry}; + +static LOG_LAYER_RELOAD_HANDLE: OnceCell< + Handle>, +> = OnceCell::new(); +static OTLP_LAYER_RELOAD_HANDLE: OnceCell< + Handle>>, +> = OnceCell::new(); + +// Records the level of opentelemetry tracing verbosity configured via command-line flags at the startup. +static DEFAULT_OTLP_LEVEL: OnceCell = OnceCell::new(); + +pub(crate) type LogLayer = Layered< + Filtered< + fmt::Layer, + reload::Layer, + Inner, + >, + Inner, +>; + +pub(crate) type SimpleLogLayer = Layered< + Filtered< + fmt::Layer, + EnvFilter, + Inner, + >, + Inner, +>; + +pub(crate) type TracingLayer = Layered< + Filtered, reload::Layer, Inner>, + Inner, +>; + +pub(crate) fn set_log_layer_handle( + handle: Handle>, +) { + LOG_LAYER_RELOAD_HANDLE + .set(handle) + .unwrap_or_else(|_| panic!("Failed to set Log Layer Filter")); +} + +pub(crate) fn set_otlp_layer_handle( + handle: Handle>>, +) { + OTLP_LAYER_RELOAD_HANDLE + .set(handle) + .unwrap_or_else(|_| panic!("Failed to set OTLP Layer Filter")); +} + +pub(crate) fn set_default_otlp_level(level: OpenTelemetryLevel) { + // Record the initial tracing level specified as a command-line flag. Use this recorded value to + // reset opentelemetry filter when the LogConfig file gets deleted. + DEFAULT_OTLP_LEVEL.set(level).unwrap(); +} +#[derive(thiserror::Error, Debug)] +#[non_exhaustive] +pub enum ReloadError { + #[error("env_filter reload handle is not available")] + NoLogReloadHandle, + #[error("opentelemetry reload handle is not available")] + NoOpentelemetryReloadHandle, + #[error("could not set the new log filter")] + ReloadLogLayer(#[source] reload::Error), + #[error("could not set the new opentelemetry filter")] + ReloadOpentelemetryLayer(#[source] reload::Error), + #[error("could not create the log filter")] + Parse(#[source] BuildEnvFilterError), +} + +pub fn reload_log_config(config: Option<&log_config::LogConfig>) { + let result = if let Some(config) = config { + reload( + config.rust_log.as_deref(), + config.verbose_module.as_deref(), + config.opentelemetry_level, + ) + } else { + // When the LOG_CONFIG_FILENAME is not available, reset to the tracing and logging config + // when the node was started. + reload(None, None, None) + }; + match result { + Ok(_) => { + tracing::info!("Updated the logging layer according to `log_config.json`"); + } + Err(err) => { + eprintln!( + "Failed to update the logging layer according to the changed `log_config.json`. Errors: {:?}", + err + ); + } + } +} + +/// Constructs new filters for the logging and opentelemetry layers. +/// +/// Attempts to reload all available errors. Returns errors for each layer that failed to reload. +/// +/// The newly constructed `EnvFilter` provides behavior equivalent to what can be obtained via +/// setting `RUST_LOG` environment variable and the `--verbose` command-line flag. +/// `rust_log` is equivalent to setting `RUST_LOG` environment variable. +/// `verbose` indicates whether `--verbose` command-line flag is present. +/// `verbose_module` is equivalent to the value of the `--verbose` command-line flag. +pub fn reload( + rust_log: Option<&str>, + verbose_module: Option<&str>, + opentelemetry_level: Option, +) -> Result<(), Vec> { + let log_reload_result = LOG_LAYER_RELOAD_HANDLE.get().map_or( + Err(ReloadError::NoLogReloadHandle), + |reload_handle| { + let mut builder = + rust_log.map_or_else(EnvFilterBuilder::from_env, EnvFilterBuilder::new); + if let Some(module) = verbose_module { + builder = builder.verbose(Some(module)); + } + let env_filter = builder.finish().map_err(ReloadError::Parse)?; + + reload_handle + .modify(|log_filter| { + *log_filter = env_filter; + }) + .map_err(ReloadError::ReloadLogLayer)?; + Ok(()) + }, + ); + + let opentelemetry_level = opentelemetry_level + .unwrap_or(*DEFAULT_OTLP_LEVEL.get().unwrap_or(&OpenTelemetryLevel::OFF)); + let opentelemetry_reload_result = OTLP_LAYER_RELOAD_HANDLE.get().map_or( + Err(ReloadError::NoOpentelemetryReloadHandle), + |reload_handle| { + reload_handle + .modify(|otlp_filter| { + *otlp_filter = get_opentelemetry_filter(opentelemetry_level); + }) + .map_err(ReloadError::ReloadOpentelemetryLayer)?; + Ok(()) + }, + ); + + let mut errors: Vec = vec![]; + if let Err(err) = log_reload_result { + errors.push(err); + } + if let Err(err) = opentelemetry_reload_result { + errors.push(err); + } + + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } +} diff --git a/core/o11y/src/span_duration_logger.rs b/core/o11y/src/span_duration_logger.rs new file mode 100644 index 000000000..fa0f03842 --- /dev/null +++ b/core/o11y/src/span_duration_logger.rs @@ -0,0 +1,124 @@ +use crate::metrics::try_create_histogram_vec; +use once_cell::sync::Lazy; +use prometheus::HistogramVec; +use std::time::{Duration, Instant}; +use tracing::span::Attributes; +use tracing::Id; +use tracing_subscriber::layer::Context; +use tracing_subscriber::registry::LookupSpan; +use tracing_subscriber::Layer; + +#[derive(Default)] +pub(crate) struct SpanDurationLogger {} + +pub(crate) static SPAN_BUSY_DURATIONS: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_span_busy_duration", + "Busy duration of spans", + &["name", "level", "target"], + // Cover the range from 0.01s to 10s. + // Keep the number of buckets small to limit the memory usage. + Some(vec![0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]), + ) + .unwrap() +}); + +// Keeps track of the time a span existed and was entered. +// The time since creation of a span is `idle + busy`. +struct Timings { + /// The time a span existed but wasn't entered. + idle: Duration, + /// Measures the time spent in the span, i.e. between enter() and exit(). + /// Note that a span may be entered and exited multiple times. + busy: Duration, + /// Instant of a last event: creation, enter, exit. + last: Instant, +} + +impl Timings { + fn new() -> Self { + Self { idle: Duration::ZERO, busy: Duration::ZERO, last: Instant::now() } + } + + // Unlikely to overflow. Even if overflows, the impact is negligible. + #[allow(clippy::arithmetic_side_effects)] + fn observe_idle(&mut self) { + let previous = std::mem::replace(&mut self.last, Instant::now()); + self.idle += self.last.duration_since(previous); + } + + // Unlikely to overflow. Even if overflows, the impact is negligible. + #[allow(clippy::arithmetic_side_effects)] + fn observe_busy(&mut self) { + let previous = std::mem::replace(&mut self.last, Instant::now()); + self.busy += self.last.duration_since(previous); + } +} + +impl Layer for SpanDurationLogger +where + S: tracing::Subscriber + for<'span> LookupSpan<'span> + Send + Sync, +{ + fn on_new_span(&self, _attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { + if let Some(span) = ctx.span(id) { + let mut extensions = span.extensions_mut(); + extensions.insert(Timings::new()); + } else { + tracing::error!(target: "span_duration_logger", ?id, "on_new_span: no span available"); + } + } + + fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { + if let Some(span) = ctx.span(id) { + let mut extensions = span.extensions_mut(); + if let Some(timings) = extensions.get_mut::() { + timings.observe_idle(); + } + } else { + tracing::error!(target: "span_duration_logger", ?id, "on_enter: no span available"); + } + } + + fn on_exit(&self, id: &Id, ctx: Context<'_, S>) { + if let Some(span) = ctx.span(id) { + let mut extensions = span.extensions_mut(); + if let Some(timings) = extensions.get_mut::() { + timings.observe_busy(); + } + } else { + tracing::error!(target: "span_duration_logger", ?id, "on_exit: no span available"); + } + } + + fn on_close(&self, id: Id, ctx: Context<'_, S>) { + if let Some(span) = ctx.span(&id) { + let mut extensions = span.extensions_mut(); + if let Some(timings) = extensions.get_mut::() { + timings.observe_idle(); + + let name = span.name(); + let level = span.metadata().level(); + let target = span.metadata().target(); + SPAN_BUSY_DURATIONS + .with_label_values(&[name, level.as_str(), target]) + .observe(timings.busy.as_secs_f64()); + + const MAX_SPAN_BUSY_DURATION_SEC: u64 = 1; + if timings.busy > Duration::from_secs(MAX_SPAN_BUSY_DURATION_SEC) { + tracing::debug!( + target: "span_duration_logger", + busy = ?timings.busy, + idle = ?timings.idle, + ?level, + ?target, + ?name, + file = ?span.metadata().file(), + line = ?span.metadata().line(), + "Span duration too long"); + } + } + } else { + tracing::error!(target: "span_duration_logger", ?id, "on_close: no span available"); + } + } +} diff --git a/core/o11y/src/subscriber.rs b/core/o11y/src/subscriber.rs new file mode 100644 index 000000000..de42d83c4 --- /dev/null +++ b/core/o11y/src/subscriber.rs @@ -0,0 +1,296 @@ +use crate::opentelemetry::add_opentelemetry_layer; +use crate::reload::{ + set_default_otlp_level, set_log_layer_handle, set_otlp_layer_handle, LogLayer, SimpleLogLayer, +}; +use crate::{log_counter, OpenTelemetryLevel}; +use unc_crypto::PublicKey; +use unc_primitives_core::types::AccountId; +use std::path::PathBuf; +use tracing::subscriber::DefaultGuard; +use tracing_appender::non_blocking::NonBlocking; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::registry::LookupSpan; +use tracing_subscriber::{fmt, reload, EnvFilter, Layer}; + +/// The resource representing a registered subscriber. +/// +/// Once dropped, the subscriber is unregistered, and the output is flushed. Any messages output +/// after this value is dropped will be delivered to a previously active subscriber, if any. +pub struct DefaultSubscriberGuard { + // NB: the field order matters here. I would've used `ManuallyDrop` to indicate this + // particularity, but somebody decided at some point that doing so is unconventional Rust and + // that implicit is better than explicit. + // + // We must first drop the `local_subscriber_guard` so that no new messages are delivered to + // this subscriber while we take care of flushing the messages already in queue. If dropped the + // other way around, the events/spans generated while the subscriber drop guard runs would be + // lost. + subscriber: Option, + local_subscriber_guard: Option, + #[allow(dead_code)] // This field is never read, but has semantic purpose as a drop guard. + writer_guard: Option, + #[allow(dead_code)] // This field is never read, but has semantic purpose as a drop guard. + io_trace_guard: Option, +} + +/// Configures exporter of span and trace data. +#[derive(Debug, Default, clap::Parser)] +pub struct Options { + /// Enables export of span data using opentelemetry exporters. + #[clap(long, value_enum, default_value = "off")] + opentelemetry: OpenTelemetryLevel, + + /// Whether the log needs to be colored. + #[clap(long, value_enum, default_value = "auto")] + color: ColorOutput, + + /// Enable logging of spans. For instance, this prints timestamps of entering and exiting a span, + /// together with the span duration and used/idle CPU time. + #[clap(long)] + log_span_events: bool, + + /// Enable JSON output of IO events, written to a file. + #[clap(long)] + record_io_trace: Option, +} + +impl DefaultSubscriberGuard { + /// Register this default subscriber globally , for all threads. + /// + /// Must not be called more than once. Mutually exclusive with `Self::local`. + pub fn global(mut self) -> Self { + if let Some(subscriber) = self.subscriber.take() { + tracing::subscriber::set_global_default(subscriber) + .expect("could not set a global subscriber"); + } else { + panic!("trying to set a default subscriber that has been already taken") + } + self + } + + /// Register this default subscriber for the current thread. + /// + /// Must not be called more than once. Mutually exclusive with `Self::global`. + pub fn local(mut self) -> Self { + if let Some(subscriber) = self.subscriber.take() { + self.local_subscriber_guard = Some(tracing::subscriber::set_default(subscriber)); + } else { + panic!("trying to set a default subscriber that has been already taken") + } + self + } +} + +/// Whether to use colored log format. +/// Option `Auto` enables color output only if the logging is done to a terminal and +/// `NO_COLOR` environment variable is not set. +#[derive(clap::ValueEnum, Debug, Clone, Default)] +pub enum ColorOutput { + #[default] + Always, + Never, + Auto, +} + +fn is_terminal() -> bool { + use std::io::IsTerminal; + std::io::stderr().is_terminal() +} + +fn add_simple_log_layer( + filter: EnvFilter, + writer: W, + ansi: bool, + with_span_events: bool, + subscriber: S, +) -> SimpleLogLayer +where + S: tracing::Subscriber + for<'span> LookupSpan<'span> + Send + Sync, + W: for<'writer> fmt::MakeWriter<'writer> + 'static, +{ + let layer = fmt::layer() + .with_ansi(ansi) + .with_span_events(get_fmt_span(with_span_events)) + .with_writer(writer) + .with_filter(filter); + + subscriber.with(layer) +} + +fn get_fmt_span(with_span_events: bool) -> fmt::format::FmtSpan { + if with_span_events { + fmt::format::FmtSpan::ENTER | fmt::format::FmtSpan::CLOSE + } else { + fmt::format::FmtSpan::NONE + } +} + +fn add_non_blocking_log_layer( + filter: EnvFilter, + writer: NonBlocking, + ansi: bool, + with_span_events: bool, + subscriber: S, +) -> (LogLayer, reload::Handle) +where + S: tracing::Subscriber + for<'span> LookupSpan<'span> + Send + Sync, +{ + let (filter, handle) = reload::Layer::::new(filter); + + let layer = fmt::layer() + .with_ansi(ansi) + .with_span_events(get_fmt_span(with_span_events)) + .with_writer(writer) + .with_filter(filter); + + (subscriber.with(layer), handle) +} + +/// The constructed layer writes storage and DB events in a custom format to a +/// specified file. +/// +/// This layer is useful to collect detailed IO access patterns for block +/// production. Typically used for debugging IO and to replay on the estimator. +#[cfg(feature = "io_trace")] +pub fn make_io_tracing_layer( + file: std::fs::File, +) -> ( + tracing_subscriber::filter::Filtered, + tracing_appender::non_blocking::WorkerGuard, +) +where + S: tracing::Subscriber + for<'span> LookupSpan<'span>, +{ + use std::io::BufWriter; + let (base_io_layer, guard) = crate::io_tracer::IoTraceLayer::new(BufWriter::new(file)); + let io_layer = base_io_layer.with_filter(EnvFilter::new( + "store=trace,vm_logic=trace,host-function=trace,runtime=debug,crate::io_tracer=trace,io_tracer_count=trace", + )); + (io_layer, guard) +} + +fn use_color_output(options: &Options) -> bool { + match options.color { + ColorOutput::Always => true, + ColorOutput::Never => false, + ColorOutput::Auto => use_color_auto(), + } +} + +pub(crate) fn use_color_auto() -> bool { + std::env::var_os("NO_COLOR").is_none() && is_terminal() +} + +/// Constructs a subscriber set to the option appropriate for the NEAR code. +/// +/// Subscriber enables only logging. +/// +/// # Example +/// +/// ```rust +/// let filter = unc_o11y::EnvFilterBuilder::from_env().finish().unwrap(); +/// let _subscriber = unc_o11y::default_subscriber(filter, &Default::default()).global(); +/// ``` +pub fn default_subscriber( + env_filter: EnvFilter, + options: &Options, +) -> DefaultSubscriberGuard LookupSpan<'span> + Send + Sync> { + let color_output = use_color_output(options); + + let make_writer = || { + let stderr = std::io::stderr(); + std::io::LineWriter::new(stderr) + }; + + let subscriber = tracing_subscriber::registry(); + let subscriber = subscriber.with(log_counter::LogCounter::default()); + let subscriber = add_simple_log_layer( + env_filter, + make_writer, + color_output, + options.log_span_events, + subscriber, + ); + + #[allow(unused_mut)] + let mut io_trace_guard = None; + #[cfg(feature = "io_trace")] + let subscriber = subscriber.with(options.record_io_trace.as_ref().map(|output_path| { + let (sub, guard) = make_io_tracing_layer( + std::fs::File::create(output_path) + .expect("unable to create or truncate IO trace output file"), + ); + io_trace_guard = Some(guard); + sub + })); + + DefaultSubscriberGuard { + subscriber: Some(subscriber), + local_subscriber_guard: None, + writer_guard: None, + io_trace_guard, + } +} + +/// Constructs a subscriber set to the option appropriate for the NEAR code. +/// +/// The subscriber enables logging, tracing and io tracing. +/// Subscriber creation needs an async runtime. +pub async fn default_subscriber_with_opentelemetry( + env_filter: EnvFilter, + options: &Options, + chain_id: String, + node_public_key: PublicKey, + account_id: Option, +) -> DefaultSubscriberGuard { + let color_output = use_color_output(options); + + // Do not lock the `stderr` here to allow for things like `dbg!()` work during development. + let stderr = std::io::stderr(); + let lined_stderr = std::io::LineWriter::new(stderr); + let (writer, writer_guard) = tracing_appender::non_blocking(lined_stderr); + + let subscriber = tracing_subscriber::registry(); + // Installs LogCounter as the innermost layer. + let subscriber = subscriber.with(log_counter::LogCounter::default()); + + set_default_otlp_level(options.opentelemetry); + + let (subscriber, handle) = add_non_blocking_log_layer( + env_filter, + writer, + color_output, + options.log_span_events, + subscriber, + ); + set_log_layer_handle(handle); + + let (subscriber, handle) = add_opentelemetry_layer( + options.opentelemetry, + chain_id, + node_public_key, + account_id, + subscriber, + ) + .await; + set_otlp_layer_handle(handle); + + #[allow(unused_mut)] + let mut io_trace_guard = None; + #[cfg(feature = "io_trace")] + let subscriber = subscriber.with(options.record_io_trace.as_ref().map(|output_path| { + let (sub, guard) = make_io_tracing_layer( + std::fs::File::create(output_path) + .expect("unable to create or truncate IO trace output file"), + ); + io_trace_guard = Some(guard); + sub + })); + + DefaultSubscriberGuard { + subscriber: Some(subscriber), + local_subscriber_guard: None, + writer_guard: Some(writer_guard), + io_trace_guard, + } +} diff --git a/core/o11y/src/testonly.rs b/core/o11y/src/testonly.rs new file mode 100644 index 000000000..e8bffd8a5 --- /dev/null +++ b/core/o11y/src/testonly.rs @@ -0,0 +1,79 @@ +mod tracing_capture; +use crate::subscriber::use_color_auto; +use core::fmt::Result; +use std::time::Instant; +pub use tracing_capture::TracingCapture; +use tracing_subscriber::fmt; +use tracing_subscriber::fmt::format::Writer; +use tracing_subscriber::fmt::time::FormatTime; +use tracing_subscriber::EnvFilter; + +fn setup_subscriber_from_filter(mut env_filter: EnvFilter) { + if let Ok(rust_log) = std::env::var("RUST_LOG") { + for directive in rust_log.split(',').filter_map(|s| match s.parse() { + Ok(directive) => Some(directive), + Err(err) => { + eprintln!("Ignoring directive `{}`: {}", s, err); + None + } + }) { + env_filter = env_filter.add_directive(directive); + } + } + + let _ = fmt::Subscriber::builder() + .with_ansi(use_color_auto()) + .with_span_events(fmt::format::FmtSpan::CLOSE) + .with_env_filter(env_filter) + .with_writer(fmt::TestWriter::new()) + .with_timer(TestUptime::default()) + .try_init(); +} + +pub fn init_test_logger() { + let env_filter = EnvFilter::new("cranelift=warn,wasmtime=warn,h2=warn,tower=warn,trust_dns=warn,tokio_reactor=info,tokio_core=info,hyper=info,debug"); + setup_subscriber_from_filter(env_filter); +} + +pub fn init_test_logger_allow_panic() { + let env_filter = EnvFilter::new("cranelift=warn,wasmtime=warn,h2=warn,tower=warn,trust_dns=warn,tokio_reactor=info,tokio_core=info,hyper=info,debug"); + setup_subscriber_from_filter(env_filter); +} + +pub fn init_test_module_logger(module: &str) { + let env_filter = + EnvFilter::new("cranelift=warn,wasmtime=warn,h2=warn,tower=warn,trust_dns=warn,tokio_reactor=info,tokio_core=info,hyper=info,cranelift_wasm=warn,info") + .add_directive(format!("{}=info", module).parse().unwrap()); + setup_subscriber_from_filter(env_filter); +} + +pub fn init_integration_logger() { + let env_filter = EnvFilter::new("cranelift=warn,wasmtime=warn,actix_web=warn,info"); + setup_subscriber_from_filter(env_filter); +} + +/// Shameless copy paste of the Uptime timer in the tracing subscriber with +/// adjusted time formatting. It measures time since the subscriber is configured. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct TestUptime { + epoch: Instant, +} + +impl Default for TestUptime { + fn default() -> Self { + TestUptime { epoch: Instant::now() } + } +} + +impl From for TestUptime { + fn from(epoch: Instant) -> Self { + TestUptime { epoch } + } +} + +impl FormatTime for TestUptime { + fn format_time(&self, w: &mut Writer<'_>) -> Result { + let e = self.epoch.elapsed(); + write!(w, "{:2}.{:03}s", e.as_secs(), e.subsec_millis()) + } +} diff --git a/core/o11y/src/testonly/tracing_capture.rs b/core/o11y/src/testonly/tracing_capture.rs new file mode 100644 index 000000000..183fa5cb6 --- /dev/null +++ b/core/o11y/src/testonly/tracing_capture.rs @@ -0,0 +1,87 @@ +use std::fmt::Write; +use std::mem; +use std::sync::{Arc, Mutex}; + +/// Intercepts `tracing` logs. +/// +/// The intended use-case is for tests which want to probe inner workings of the +/// system which are not observable through public APIs only. +pub struct TracingCapture { + captured: Arc>, + _guard: tracing::subscriber::DefaultGuard, +} + +struct Captured { + on_log: Arc, + logs: Vec, +} + +struct Subscriber(Arc>); + +impl TracingCapture { + /// Sets `TracingCapture` as the "default subscriber". + /// + /// "default subscriber" is essentially a thread-local, so some care must be + /// taken to properly propagate this across threads for multi-threaded + /// tests. + pub fn enable() -> TracingCapture { + let captured = + Arc::new(Mutex::new(Captured { on_log: Arc::new(|_| ()), logs: Vec::new() })); + let subscriber = Subscriber(Arc::clone(&captured)); + let _guard = tracing::subscriber::set_default(subscriber); + TracingCapture { captured, _guard } + } + /// Get all the logs so-far. + /// + /// Useful to verify that some particular code-path was hit by a test. + pub fn drain(&mut self) -> Vec { + let mut guard = self.captured.lock().unwrap(); + mem::take(&mut guard.logs) + } + /// Sets the callback to execute on every log line emitted. + /// + /// The intended use-case is for testing multithreaded code: by *blocking* + /// in the `on_log` for specific log-lines, the test can maneuver the + /// threads into particularly interesting interleavings. + pub fn set_callback(&mut self, on_log: impl Fn(&str) + Send + Sync + 'static) { + self.captured.lock().unwrap().on_log = Arc::new(on_log) + } +} + +impl tracing::Subscriber for Subscriber { + fn enabled(&self, _metadata: &tracing::Metadata<'_>) -> bool { + true + } + + fn new_span(&self, span: &tracing::span::Attributes<'_>) -> tracing::span::Id { + let buf = span.metadata().name().to_string(); + + let buf = { + let mut visitor = AppendToString(buf); + span.record(&mut visitor); + visitor.0 + }; + + // Tricky: as `on_log` is expected to block, we take care to call it + // *without* holding any mutexes. + let on_log = Arc::clone(&self.0.lock().unwrap().on_log); + on_log(&buf); + + let mut guard = self.0.lock().unwrap(); + guard.logs.push(buf); + + tracing::span::Id::from_u64(guard.logs.len() as u64) + } + fn record(&self, _span: &tracing::span::Id, _values: &tracing::span::Record<'_>) {} + fn record_follows_from(&self, _span: &tracing::span::Id, _follows: &tracing::span::Id) {} + fn event(&self, _event: &tracing::Event<'_>) {} + fn enter(&self, _span: &tracing::span::Id) {} + fn exit(&self, _span: &tracing::span::Id) {} +} + +struct AppendToString(String); +impl tracing::field::Visit for AppendToString { + fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { + let _ = write!(self.0, " {}={:?}", field.name(), value); + } +} diff --git a/core/parameters/Cargo.toml b/core/parameters/Cargo.toml new file mode 100644 index 000000000..686a19cd3 --- /dev/null +++ b/core/parameters/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "unc-parameters" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "This crate provides the information about the configuration of the near protocol" +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +assert_matches.workspace = true +enum-map.workspace = true +num-rational.workspace = true +serde.workspace = true +serde_yaml.workspace = true +serde_repr.workspace = true +strum.workspace = true +thiserror.workspace = true +borsh.workspace = true +clap = { workspace = true, optional = true } + + +# FIXME: this ideally should not depend on unc-primitives-core... +unc-primitives-core.workspace = true +unc-account-id.workspace = true + +[dev-dependencies] +insta.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-primitives-core/nightly", +] +nightly_protocol = [ + "unc-primitives-core/nightly_protocol", +] +calimero_zero_storage = [] diff --git a/core/parameters/res/README.md b/core/parameters/res/README.md new file mode 100644 index 000000000..02900c5e3 --- /dev/null +++ b/core/parameters/res/README.md @@ -0,0 +1,25 @@ +## Core Resource Files + +Stores resource data which is part of the protocol stable enough to be moved outside of the code. + +### `runtime_configs` + +All parameter value to configure the runtime are defined in `parameters.yaml`. +Parameters added or changed in protocol upgrades are defined in differential +config files with a naming scheme like `V.yaml`, where `V` is the new version. + +The content of the base configuration file is one flat list of typed keys and +typed values (possibly with nested structure). Key names are defined in +`core/primitives-core/src/parameter.rs`. + +The format of the differential files is slightly different. Inserting new +parameters uses the following syntax: `key: { new: value }`. +Parameters that change are specified like this: `key: { old: old_value, new: new_value }`. +Removing a previously defined parameter for a new version is done as follows: +`key: { old: old_value }`. This causes the parameter value to be undefined in newer +versions which generally means the default value is used to fill in the +`RuntimeConfig` object. + +The latest values of parameters can be found in `parameters.snap`. This file is +automatically generated by tests and needs to be reviewed and commited whenever +any of the parameters changes. diff --git a/core/parameters/res/runtime_configs/129.yaml b/core/parameters/res/runtime_configs/129.yaml new file mode 100644 index 000000000..c60af6224 --- /dev/null +++ b/core/parameters/res/runtime_configs/129.yaml @@ -0,0 +1 @@ +fix_contract_loading_cost: { old: false, new: true } diff --git a/core/parameters/res/runtime_configs/138.yaml b/core/parameters/res/runtime_configs/138.yaml new file mode 100644 index 000000000..df5938200 --- /dev/null +++ b/core/parameters/res/runtime_configs/138.yaml @@ -0,0 +1 @@ +eth_implicit_accounts: { old: false, new: true } diff --git a/core/parameters/res/runtime_configs/35.yaml b/core/parameters/res/runtime_configs/35.yaml new file mode 100644 index 000000000..74b5b963c --- /dev/null +++ b/core/parameters/res/runtime_configs/35.yaml @@ -0,0 +1 @@ +implicit_account_creation: { old: false, new: true } diff --git a/core/parameters/res/runtime_configs/42.yaml b/core/parameters/res/runtime_configs/42.yaml new file mode 100644 index 000000000..8f79cc5cd --- /dev/null +++ b/core/parameters/res/runtime_configs/42.yaml @@ -0,0 +1 @@ +storage_amount_per_byte: { old: 100_000_000_000_000_000_000, new: 10_000_000_000_000_000_000 } diff --git a/core/parameters/res/runtime_configs/46.yaml b/core/parameters/res/runtime_configs/46.yaml new file mode 100644 index 000000000..670962b70 --- /dev/null +++ b/core/parameters/res/runtime_configs/46.yaml @@ -0,0 +1 @@ +math_extension: { old: false, new: true } diff --git a/core/parameters/res/runtime_configs/48.yaml b/core/parameters/res/runtime_configs/48.yaml new file mode 100644 index 000000000..34fd52ed8 --- /dev/null +++ b/core/parameters/res/runtime_configs/48.yaml @@ -0,0 +1,28 @@ +wasm_regular_op_cost: { old: 3_856_371, new: 2_207_874 } +wasm_ecrecover_base: { old: 3_365_369_625_000, new: 278_821_988_457 } +data_receipt_creation_base: { + old: { + send_sir: 4_697_339_419_375, + send_not_sir: 4_697_339_419_375, + execution: 4_697_339_419_375, + }, + new: { + send_sir: 36_486_732_312, + send_not_sir: 36_486_732_312, + execution: 36_486_732_312, + }, +} + +data_receipt_creation_per_byte: { + old: { + send_sir: 59_357_464, + send_not_sir: 59_357_464, + execution: 59_357_464, + }, + new: { + send_sir: 17_212_011, + send_not_sir: 17_212_011, + execution: 17_212_011, + }, +} +vm_kind: { old: Wasmer0, new: Wasmer2 } diff --git a/core/parameters/res/runtime_configs/49.yaml b/core/parameters/res/runtime_configs/49.yaml new file mode 100644 index 000000000..c88d0db79 --- /dev/null +++ b/core/parameters/res/runtime_configs/49.yaml @@ -0,0 +1,2 @@ +wasm_regular_op_cost: { old: 2_207_874, new: 822_756 } +max_functions_number_per_contract: { new: 10_000 } diff --git a/core/parameters/res/runtime_configs/50.yaml b/core/parameters/res/runtime_configs/50.yaml new file mode 100644 index 000000000..5405a6acf --- /dev/null +++ b/core/parameters/res/runtime_configs/50.yaml @@ -0,0 +1 @@ +contract_prepare_version: { old: 0, new: 1 } diff --git a/core/parameters/res/runtime_configs/52.yaml b/core/parameters/res/runtime_configs/52.yaml new file mode 100644 index 000000000..cbd07cdaf --- /dev/null +++ b/core/parameters/res/runtime_configs/52.yaml @@ -0,0 +1,2 @@ +max_gas_burnt: { old: 200_000_000_000_000, new: 300_000_000_000_000 } +max_gas_burnt_view: { old: 200_000_000_000_000, new: 300_000_000_000_000 } diff --git a/core/parameters/res/runtime_configs/53.yaml b/core/parameters/res/runtime_configs/53.yaml new file mode 100644 index 000000000..22c3ec11e --- /dev/null +++ b/core/parameters/res/runtime_configs/53.yaml @@ -0,0 +1,16 @@ +action_deploy_contract_per_byte: { + old: { + send_sir: 6_812_999, + send_not_sir: 6_812_999, + execution: 6_812_999, + }, + new: { + send_sir: 6_812_999, + send_not_sir: 6_812_999, + execution: 64_572_944, + }, +} +wasmer2_stack_limit: { new: 204_800 } +max_length_storage_key: { old: 4_194_304, new: 2_048 } +max_locals_per_contract: { new: 1_000_000 } +function_call_weight: { old: false, new: true } diff --git a/core/parameters/res/runtime_configs/55.yaml b/core/parameters/res/runtime_configs/55.yaml new file mode 100644 index 000000000..4e347fae5 --- /dev/null +++ b/core/parameters/res/runtime_configs/55.yaml @@ -0,0 +1 @@ +alt_bn128: { old: false, new: true } diff --git a/core/parameters/res/runtime_configs/57.yaml b/core/parameters/res/runtime_configs/57.yaml new file mode 100644 index 000000000..436ee3e08 --- /dev/null +++ b/core/parameters/res/runtime_configs/57.yaml @@ -0,0 +1 @@ +account_id_validity_rules_version: { old: 0, new: 1 } diff --git a/core/parameters/res/runtime_configs/59.yaml b/core/parameters/res/runtime_configs/59.yaml new file mode 100644 index 000000000..2b9f57124 --- /dev/null +++ b/core/parameters/res/runtime_configs/59.yaml @@ -0,0 +1,13 @@ +ed25519_verify: { old: false, new: true } +action_create_account: { + old: { + send_sir: 99_607_375_000, + send_not_sir: 99_607_375_000, + execution: 99_607_375_000, + }, + new: { + send_sir: 3_850_000_000_000, + send_not_sir: 3_850_000_000_000, + execution: 3_850_000_000_000, + } +} diff --git a/core/parameters/res/runtime_configs/61.yaml b/core/parameters/res/runtime_configs/61.yaml new file mode 100644 index 000000000..f77f19baa --- /dev/null +++ b/core/parameters/res/runtime_configs/61.yaml @@ -0,0 +1,8 @@ +# Compute costs to allow for flat storage read-only MVP. +# See https://github.com/utnet-org/utility/issues/8006 +wasm_touching_trie_node: { old: 16_101_955_926, new: { gas: 16_101_955_926, compute: 110_000_000_000 } } +wasm_storage_write_base: { old: 64_196_736_000, new: { gas: 64_196_736_000, compute: 200_000_000_000 } } +wasm_storage_remove_base: { old: 53_473_030_500, new: { gas: 53_473_030_500, compute: 200_000_000_000 } } +wasm_storage_read_base: { old: 56_356_845_750, new: { gas: 56_356_845_750, compute: 200_000_000_000 } } +wasm_storage_has_key_base: { old: 54_039_896_625, new: { gas: 54_039_896_625, compute: 200_000_000_000 } } +flat_storage_reads: { old: false, new: true } diff --git a/core/parameters/res/runtime_configs/62.yaml b/core/parameters/res/runtime_configs/62.yaml new file mode 100644 index 000000000..d9652448d --- /dev/null +++ b/core/parameters/res/runtime_configs/62.yaml @@ -0,0 +1,9 @@ +# Finite-wasm counts in bytes, parity-wasm was counting in stack slots, so there’s a factor of at +# least 8. Plus some additional slack for the new algorithm potentially being more accurate or +# correct. +max_stack_height: { old: 16384, new: 262144 } +contract_prepare_version: { old: 1, new: 2 } + +# There was a bug for a short period of time that we need to reproduce... +disable_9393_fix: { old: false, new: true } +vm_kind: { old: Wasmer2, new: NearVm } diff --git a/core/parameters/res/runtime_configs/63.yaml b/core/parameters/res/runtime_configs/63.yaml new file mode 100644 index 000000000..18dc4c1b7 --- /dev/null +++ b/core/parameters/res/runtime_configs/63.yaml @@ -0,0 +1 @@ +disable_9393_fix: { old: true, new: false } diff --git a/core/parameters/res/runtime_configs/64.yaml b/core/parameters/res/runtime_configs/64.yaml new file mode 100644 index 000000000..985eb7453 --- /dev/null +++ b/core/parameters/res/runtime_configs/64.yaml @@ -0,0 +1,2 @@ +# Implements NEP-492, disallowing all top-level accounts. +min_allowed_top_level_account_length: { old: 0, new: 0 } \ No newline at end of file diff --git a/core/parameters/res/runtime_configs/parameters.snap b/core/parameters/res/runtime_configs/parameters.snap new file mode 100644 index 000000000..59ff9afe3 --- /dev/null +++ b/core/parameters/res/runtime_configs/parameters.snap @@ -0,0 +1,178 @@ +--- +source: core/primitives/src/runtime/config_store.rs +description: THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +--- +burnt_gas_reward 3 / 10 +pessimistic_gas_price_inflation 103 / 100 +min_allowed_top_level_account_length 65 +registrar_account_id registrar +storage_amount_per_byte 10000000000000000000 +storage_num_bytes_account 100 +storage_num_extra_bytes_record 40 +action_receipt_creation +- send_sir: 108_059_500_000 +- send_not_sir: 108_059_500_000 +- execution: 108_059_500_000 +data_receipt_creation_base +- send_sir: 36_486_732_312 +- send_not_sir: 36_486_732_312 +- execution: 36_486_732_312 +data_receipt_creation_per_byte +- send_sir: 17_212_011 +- send_not_sir: 17_212_011 +- execution: 17_212_011 +action_create_account +- send_sir: 3_850_000_000_000 +- send_not_sir: 3_850_000_000_000 +- execution: 3_850_000_000_000 +action_delete_account +- send_sir: 147_489_000_000 +- send_not_sir: 147_489_000_000 +- execution: 147_489_000_000 +action_deploy_contract +- send_sir: 184_765_750_000 +- send_not_sir: 184_765_750_000 +- execution: 184_765_750_000 +action_deploy_contract_per_byte +- send_sir: 6_812_999 +- send_not_sir: 6_812_999 +- execution: 64_572_944 +action_function_call +- send_sir: 2_319_861_500_000 +- send_not_sir: 2_319_861_500_000 +- execution: 2_319_861_500_000 +action_function_call_per_byte +- send_sir: 2_235_934 +- send_not_sir: 2_235_934 +- execution: 2_235_934 +action_transfer +- send_sir: 115_123_062_500 +- send_not_sir: 115_123_062_500 +- execution: 115_123_062_500 +action_stake +- send_sir: 141_715_687_500 +- send_not_sir: 141_715_687_500 +- execution: 102_217_625_000 +action_add_full_access_key +- send_sir: 101_765_125_000 +- send_not_sir: 101_765_125_000 +- execution: 101_765_125_000 +action_add_function_call_key +- send_sir: 102_217_625_000 +- send_not_sir: 102_217_625_000 +- execution: 102_217_625_000 +action_add_function_call_key_per_byte +- send_sir: 1_925_331 +- send_not_sir: 1_925_331 +- execution: 1_925_331 +action_delete_key +- send_sir: 94_946_625_000 +- send_not_sir: 94_946_625_000 +- execution: 94_946_625_000 +action_delegate +- send_sir: 200_000_000_000 +- send_not_sir: 200_000_000_000 +- execution: 200_000_000_000 +wasm_regular_op_cost 822_756 +wasm_grow_mem_cost 1 +wasm_base 264_768_111 +wasm_contract_loading_base 35_445_963 +wasm_contract_loading_bytes 216_750 +wasm_read_memory_base 2_609_863_200 +wasm_read_memory_byte 3_801_333 +wasm_write_memory_base 2_803_794_861 +wasm_write_memory_byte 2_723_772 +wasm_read_register_base 2_517_165_186 +wasm_read_register_byte 98_562 +wasm_write_register_base 2_865_522_486 +wasm_write_register_byte 3_801_564 +wasm_utf8_decoding_base 3_111_779_061 +wasm_utf8_decoding_byte 291_580_479 +wasm_utf16_decoding_base 3_543_313_050 +wasm_utf16_decoding_byte 163_577_493 +wasm_sha256_base 4_540_970_250 +wasm_sha256_byte 24_117_351 +wasm_keccak256_base 5_879_491_275 +wasm_keccak256_byte 21_471_105 +wasm_keccak512_base 5_811_388_236 +wasm_keccak512_byte 36_649_701 +wasm_ripemd160_base 853_675_086 +wasm_ripemd160_block 680_107_584 +wasm_ecrecover_base 278_821_988_457 +wasm_ed25519_verify_base 210_000_000_000 +wasm_ed25519_verify_byte 9_000_000 +wasm_log_base 3_543_313_050 +wasm_log_byte 13_198_791 +wasm_storage_write_base 64_196_736_000, compute: 200_000_000_000 +wasm_storage_write_key_byte 70_482_867 +wasm_storage_write_value_byte 31_018_539 +wasm_storage_write_evicted_byte 32_117_307 +wasm_storage_read_base 56_356_845_750, compute: 200_000_000_000 +wasm_storage_read_key_byte 30_952_533 +wasm_storage_read_value_byte 5_611_005 +wasm_storage_remove_base 53_473_030_500, compute: 200_000_000_000 +wasm_storage_remove_key_byte 38_220_384 +wasm_storage_remove_ret_value_byte 11_531_556 +wasm_storage_has_key_base 54_039_896_625, compute: 200_000_000_000 +wasm_storage_has_key_byte 30_790_845 +wasm_storage_iter_create_prefix_base 0 +wasm_storage_iter_create_prefix_byte 0 +wasm_storage_iter_create_range_base 0 +wasm_storage_iter_create_from_byte 0 +wasm_storage_iter_create_to_byte 0 +wasm_storage_iter_next_base 0 +wasm_storage_iter_next_key_byte 0 +wasm_storage_iter_next_value_byte 0 +wasm_touching_trie_node 16_101_955_926, compute: 110_000_000_000 +wasm_read_cached_trie_node 2_280_000_000 +wasm_promise_and_base 1_465_013_400 +wasm_promise_and_per_promise 5_452_176 +wasm_promise_return 560_152_386 +wasm_validator_frozen_base 911_834_726_400 +wasm_validator_total_frozen_base 911_834_726_400 +wasm_validator_power_base 3_000_000_000 +wasm_validator_total_power_base 3_000_000_000 +wasm_alt_bn128_g1_multiexp_base 713_000_000_000 +wasm_alt_bn128_g1_multiexp_element 320_000_000_000 +wasm_alt_bn128_pairing_check_base 9_686_000_000_000 +wasm_alt_bn128_pairing_check_element 5_102_000_000_000 +wasm_alt_bn128_g1_sum_base 3_000_000_000 +wasm_alt_bn128_g1_sum_element 5_000_000_000 +max_gas_burnt 300_000_000_000_000 +max_gas_burnt_view 300_000_000_000_000 +max_stack_height 262_144 +contract_prepare_version 2 +initial_memory_pages 1_024 +max_memory_pages 2_048 +registers_memory_limit 1_073_741_824 +max_register_size 104_857_600 +max_number_registers 100 +max_number_logs 100 +max_total_log_length 16_384 +max_total_prepaid_gas 300_000_000_000_000 +max_actions_per_receipt 100 +max_number_bytes_method_names 2_000 +max_length_method_name 256 +max_arguments_length 4_194_304 +max_length_returned_data 4_194_304 +max_contract_size 4_194_304 +max_transaction_size 4_194_304 +max_length_storage_key 2_048 +max_length_storage_value 4_194_304 +max_promises_per_function_call_action 1_024 +max_number_input_data_dependencies 128 +max_functions_number_per_contract 10_000 +wasmer2_stack_limit 204_800 +max_locals_per_contract 1_000_000 +account_id_validity_rules_version 1 +disable_9393_fix false +flat_storage_reads true +implicit_account_creation true +fix_contract_loading_cost false +math_extension true +ed25519_verify true +alt_bn128 true +function_call_weight true +vm_kind NearVm +eth_implicit_accounts false + diff --git a/core/parameters/res/runtime_configs/parameters.yaml b/core/parameters/res/runtime_configs/parameters.yaml new file mode 100644 index 000000000..569b28387 --- /dev/null +++ b/core/parameters/res/runtime_configs/parameters.yaml @@ -0,0 +1,224 @@ +# Initial protocol configuration parameters. +# Each protocol version can change, add, or remove parameters. +# The diffs are stored in files named `NN.txt`, where `NN` is the version. + +# Gas economics config +burnt_gas_reward: { + numerator: 3, + denominator: 10, +} +pessimistic_gas_price_inflation: { + numerator: 103, + denominator: 100, +} + +# Account creation config +min_allowed_top_level_account_length: 0 +registrar_account_id: "registrar" + +# Storage usage config +storage_amount_per_byte: 100_000_000_000_000_000_000 +storage_num_bytes_account: 100 +storage_num_extra_bytes_record: 40 + +# Static action costs: +# send_sir / send_not_sir is burned when creating a receipt on the signer shard +# (SIR = signer is receiver, which guarantees the receipt is local) +# execution is burned when applying receipt on receiver shard +action_receipt_creation: { + send_sir: 108_059_500_000, + send_not_sir: 108_059_500_000, + execution: 108_059_500_000, +} +data_receipt_creation_base: { + send_sir: 4_697_339_419_375, + send_not_sir: 4_697_339_419_375, + execution: 4_697_339_419_375, +} +data_receipt_creation_per_byte: { + send_sir: 59_357_464, + send_not_sir: 59_357_464, + execution: 59_357_464, +} +action_create_account: { + send_sir: 99_607_375_000, + send_not_sir: 99_607_375_000, + execution: 99_607_375_000, +} +action_delete_account: { + send_sir: 147_489_000_000, + send_not_sir: 147_489_000_000, + execution: 147_489_000_000, +} +action_deploy_contract: { + send_sir: 184_765_750_000, + send_not_sir: 184_765_750_000, + execution: 184_765_750_000, +} +action_deploy_contract_per_byte: { + send_sir: 6_812_999, + send_not_sir: 6_812_999, + execution: 6_812_999, +} +action_function_call: { + send_sir: 2_319_861_500_000, + send_not_sir: 2_319_861_500_000, + execution: 2_319_861_500_000, +} +action_function_call_per_byte: { + send_sir: 2_235_934, + send_not_sir: 2_235_934, + execution: 2_235_934, +} +action_transfer: { + send_sir: 115_123_062_500, + send_not_sir: 115_123_062_500, + execution: 115_123_062_500, +} +action_stake: { + send_sir: 141_715_687_500, + send_not_sir: 141_715_687_500, + execution: 102_217_625_000, +} +action_add_full_access_key: { + send_sir: 101_765_125_000, + send_not_sir: 101_765_125_000, + execution: 101_765_125_000, +} +action_add_function_call_key: { + send_sir: 102_217_625_000, + send_not_sir: 102_217_625_000, + execution: 102_217_625_000, +} +action_add_function_call_key_per_byte: { + send_sir: 1_925_331, + send_not_sir: 1_925_331, + execution: 1_925_331, +} +action_delete_key: { + send_sir: 94_946_625_000, + send_not_sir: 94_946_625_000, + execution: 94_946_625_000, +} +action_delegate: { + send_sir: 200_000_000_000, + send_not_sir: 200_000_000_000, + execution: 200_000_000_000, +} + +action_register_rsa2048_keys: { + send_sir: 101765125000, + send_not_sir: 101765125000, + execution: 101765125000, +} + +action_create_rsa2048_challenge: { + send_sir: 101765125000, + send_not_sir: 101765125000, + execution: 101765125000, +} + +# Smart contract dynamic gas costs +wasm_regular_op_cost: 3_856_371 +wasm_grow_mem_cost: 1 +wasm_base: 264_768_111 +wasm_contract_loading_base: 35_445_963 +wasm_contract_loading_bytes: 216_750 +wasm_read_memory_base: 2_609_863_200 +wasm_read_memory_byte: 3_801_333 +wasm_write_memory_base: 2_803_794_861 +wasm_write_memory_byte: 2_723_772 +wasm_read_register_base: 2_517_165_186 +wasm_read_register_byte: 98_562 +wasm_write_register_base: 2_865_522_486 +wasm_write_register_byte: 3_801_564 +wasm_utf8_decoding_base: 3_111_779_061 +wasm_utf8_decoding_byte: 291_580_479 +wasm_utf16_decoding_base: 3_543_313_050 +wasm_utf16_decoding_byte: 163_577_493 +wasm_sha256_base: 4_540_970_250 +wasm_sha256_byte: 24_117_351 +wasm_keccak256_base: 5_879_491_275 +wasm_keccak256_byte: 21_471_105 +wasm_keccak512_base: 5_811_388_236 +wasm_keccak512_byte: 36_649_701 +wasm_ripemd160_base: 853_675_086 +wasm_ripemd160_block: 680_107_584 +wasm_ecrecover_base: 3_365_369_625_000 +wasm_ed25519_verify_base: 210_000_000_000 +wasm_ed25519_verify_byte: 9_000_000 +wasm_log_base: 3_543_313_050 +wasm_log_byte: 13_198_791 +wasm_storage_write_base: 64_196_736_000 +wasm_storage_write_key_byte: 70_482_867 +wasm_storage_write_value_byte: 31_018_539 +wasm_storage_write_evicted_byte: 32_117_307 +wasm_storage_read_base: 56_356_845_750 +wasm_storage_read_key_byte: 30_952_533 +wasm_storage_read_value_byte: 5_611_005 +wasm_storage_remove_base: 53_473_030_500 +wasm_storage_remove_key_byte: 38_220_384 +wasm_storage_remove_ret_value_byte: 11_531_556 +wasm_storage_has_key_base: 54_039_896_625 +wasm_storage_has_key_byte: 30_790_845 +wasm_storage_iter_create_prefix_base: 0 +wasm_storage_iter_create_prefix_byte: 0 +wasm_storage_iter_create_range_base: 0 +wasm_storage_iter_create_from_byte: 0 +wasm_storage_iter_create_to_byte: 0 +wasm_storage_iter_next_base: 0 +wasm_storage_iter_next_key_byte: 0 +wasm_storage_iter_next_value_byte: 0 +wasm_touching_trie_node: 16_101_955_926 +wasm_read_cached_trie_node: 2_280_000_000 +wasm_promise_and_base: 1_465_013_400 +wasm_promise_and_per_promise: 5_452_176 +wasm_promise_return: 560_152_386 +wasm_validator_frozen_base: 911_834_726_400 +wasm_validator_total_frozen_base: 911_834_726_400 +wasm_alt_bn128_g1_multiexp_base: 713_000_000_000 +wasm_alt_bn128_g1_multiexp_element: 320_000_000_000 +wasm_alt_bn128_pairing_check_base: 9_686_000_000_000 +wasm_alt_bn128_pairing_check_element: 5_102_000_000_000 +wasm_alt_bn128_g1_sum_base: 3_000_000_000 +wasm_alt_bn128_g1_sum_element: 5_000_000_000 +wasm_validator_power_base: 3_000_000_000 +wasm_validator_total_power_base: 3_000_000_000 + +# Smart contract limits +max_gas_burnt: 200_000_000_000_000 +max_gas_burnt_view: 200_000_000_000_000 +max_stack_height: 16_384 +contract_prepare_version: 0 +initial_memory_pages: 1_024 +max_memory_pages: 2_048 +registers_memory_limit: 1_073_741_824 +max_register_size: 104_857_600 +max_number_registers: 100 +max_number_logs: 100 +max_total_log_length: 16_384 +max_total_prepaid_gas: 300_000_000_000_000 +max_actions_per_receipt: 100 +max_number_bytes_method_names: 2_000 +max_length_method_name: 256 +max_arguments_length: 4_194_304 +max_length_returned_data: 4_194_304 +max_contract_size: 4_194_304 +max_transaction_size: 4_194_304 +max_length_storage_key: 4_194_304 +max_length_storage_value: 4_194_304 +max_promises_per_function_call_action: 1_024 +max_number_input_data_dependencies: 128 +account_id_validity_rules_version: 0 + +# Contract runtime configuration +disable_9393_fix: false +flat_storage_reads: false +implicit_account_creation: false +fix_contract_loading_cost: false +math_extension: false +ed25519_verify: false +alt_bn128: false +function_call_weight: false +vm_kind: Wasmer0 +eth_implicit_accounts: false diff --git a/core/parameters/res/runtime_configs/parameters_testnet.yaml b/core/parameters/res/runtime_configs/parameters_testnet.yaml new file mode 100644 index 000000000..bbb5bdb3c --- /dev/null +++ b/core/parameters/res/runtime_configs/parameters_testnet.yaml @@ -0,0 +1,219 @@ +# Gas economics config +burnt_gas_reward: { + numerator: 3, + denominator: 10, +} +pessimistic_gas_price_inflation: { + numerator: 103, + denominator: 100, +} + +# Account creation config +min_allowed_top_level_account_length: 0 +registrar_account_id: "registrar" + +# Storage usage config +storage_amount_per_byte: 100_000_000_000_000_000_000 +storage_num_bytes_account: 100 +storage_num_extra_bytes_record: 40 + +# Static action costs: +# send_sir / send_not_sir is burned when creating a receipt on the signer shard +# (SIR = signer is receiver, which guarantees the receipt is local) +# execution is burned when applying receipt on receiver shard +action_receipt_creation: { + send_sir: 108_059_500_000, + send_not_sir: 108_059_500_000, + execution: 108_059_500_000, +} +data_receipt_creation_base: { + send_sir: 4_697_339_419_375, + send_not_sir: 4_697_339_419_375, + execution: 4_697_339_419_375, +} +data_receipt_creation_per_byte: { + send_sir: 59_357_464, + send_not_sir: 59_357_464, + execution: 59_357_464, +} +action_create_account: { + send_sir: 99_607_375_000, + send_not_sir: 99_607_375_000, + execution: 99_607_375_000, +} +action_delete_account: { + send_sir: 147_489_000_000, + send_not_sir: 147_489_000_000, + execution: 147_489_000_000, +} +action_deploy_contract: { + send_sir: 184_765_750_000, + send_not_sir: 184_765_750_000, + execution: 184_765_750_000, +} +action_deploy_contract_per_byte: { + send_sir: 6_812_999, + send_not_sir: 6_812_999, + execution: 6_812_999, +} +action_function_call: { + send_sir: 2_319_861_500_000, + send_not_sir: 2_319_861_500_000, + execution: 2_319_861_500_000, +} +action_function_call_per_byte: { + send_sir: 2_235_934, + send_not_sir: 2_235_934, + execution: 2_235_934, +} +action_transfer: { + send_sir: 115_123_062_500, + send_not_sir: 115_123_062_500, + execution: 115_123_062_500, +} +action_stake: { + send_sir: 141_715_687_500, + send_not_sir: 141_715_687_500, + execution: 102_217_625_000, +} +action_add_full_access_key: { + send_sir: 101_765_125_000, + send_not_sir: 101_765_125_000, + execution: 101_765_125_000, +} +action_add_function_call_key: { + send_sir: 102_217_625_000, + send_not_sir: 102_217_625_000, + execution: 102_217_625_000, +} +action_add_function_call_key_per_byte: { + send_sir: 1_925_331, + send_not_sir: 1_925_331, + execution: 1_925_331, +} +action_delete_key: { + send_sir: 94_946_625_000, + send_not_sir: 94_946_625_000, + execution: 94_946_625_000, +} +# TODO: place-holder values, needs estimation, tracked in #8114 +action_delegate: { + send_sir: 2_319_861_500_000, + send_not_sir: 2_319_861_500_000, + execution: 2_319_861_500_000, +} + +action_register_rsa2048_keys: { + send_sir: 101765125000, + send_not_sir: 101765125000, + execution: 101765125000, +} + +action_create_rsa2048_challenge: { + send_sir: 101765125000, + send_not_sir: 101765125000, + execution: 101765125000, +} + +# Smart contract dynamic gas costs +wasm_regular_op_cost: 3_856_371 +wasm_grow_mem_cost: 1 +wasm_base: 264_768_111 +wasm_contract_loading_base: 35_445_963 +wasm_contract_loading_bytes: 216_750 +wasm_read_memory_base: 2_609_863_200 +wasm_read_memory_byte: 3_801_333 +wasm_write_memory_base: 2_803_794_861 +wasm_write_memory_byte: 2_723_772 +wasm_read_register_base: 2_517_165_186 +wasm_read_register_byte: 98_562 +wasm_write_register_base: 2_865_522_486 +wasm_write_register_byte: 3_801_564 +wasm_utf8_decoding_base: 3_111_779_061 +wasm_utf8_decoding_byte: 291_580_479 +wasm_utf16_decoding_base: 3_543_313_050 +wasm_utf16_decoding_byte: 163_577_493 +wasm_sha256_base: 4_540_970_250 +wasm_sha256_byte: 24_117_351 +wasm_keccak256_base: 5_879_491_275 +wasm_keccak256_byte: 21_471_105 +wasm_keccak512_base: 5_811_388_236 +wasm_keccak512_byte: 36_649_701 +wasm_ripemd160_base: 853_675_086 +wasm_ripemd160_block: 680_107_584 +wasm_ecrecover_base: 3_365_369_625_000 +wasm_ed25519_verify_base: 210_000_000_000 +wasm_ed25519_verify_byte: 9_000_000 +wasm_log_base: 3_543_313_050 +wasm_log_byte: 13_198_791 +wasm_storage_write_base: 64_196_736_000 +wasm_storage_write_key_byte: 70_482_867 +wasm_storage_write_value_byte: 31_018_539 +wasm_storage_write_evicted_byte: 32_117_307 +wasm_storage_read_base: 56_356_845_750 +wasm_storage_read_key_byte: 30_952_533 +wasm_storage_read_value_byte: 5_611_005 +wasm_storage_remove_base: 53_473_030_500 +wasm_storage_remove_key_byte: 38_220_384 +wasm_storage_remove_ret_value_byte: 11_531_556 +wasm_storage_has_key_base: 54_039_896_625 +wasm_storage_has_key_byte: 30_790_845 +wasm_storage_iter_create_prefix_base: 0 +wasm_storage_iter_create_prefix_byte: 0 +wasm_storage_iter_create_range_base: 0 +wasm_storage_iter_create_from_byte: 0 +wasm_storage_iter_create_to_byte: 0 +wasm_storage_iter_next_base: 0 +wasm_storage_iter_next_key_byte: 0 +wasm_storage_iter_next_value_byte: 0 +wasm_touching_trie_node: 16_101_955_926 +wasm_read_cached_trie_node: 2_280_000_000 +wasm_promise_and_base: 1_465_013_400 +wasm_promise_and_per_promise: 5_452_176 +wasm_promise_return: 560_152_386 +wasm_validator_frozen_base: 911_834_726_400 +wasm_validator_total_frozen_base: 911_834_726_400 +wasm_alt_bn128_g1_multiexp_base: 713_006_929_500 +wasm_alt_bn128_g1_multiexp_element: 3_335_092_461 +wasm_alt_bn128_pairing_check_base: 9_685_508_901_000 +wasm_alt_bn128_pairing_check_element: 26_575_188_546 +wasm_alt_bn128_g1_sum_base: 3_175_314_375 +wasm_alt_bn128_g1_sum_element: 76_218_543 +wasm_validator_power_base: 3_834_726_400 +wasm_validator_total_power_base: 3_834_726_400 + +# Smart contract limits +max_gas_burnt: 200_000_000_000_000 +max_gas_burnt_view: 200_000_000_000_000 +max_stack_height: 16_384 +contract_prepare_version: 0 +initial_memory_pages: 1_024 +max_memory_pages: 2_048 +registers_memory_limit: 1_073_741_824 +max_register_size: 104_857_600 +max_number_registers: 100 +max_number_logs: 100 +max_total_log_length: 16_384 +max_total_prepaid_gas: 300_000_000_000_000 +max_actions_per_receipt: 100 +max_number_bytes_method_names: 2_000 +max_length_method_name: 256 +max_arguments_length: 4_194_304 +max_length_returned_data: 4_194_304 +max_contract_size: 4_194_304 +max_transaction_size: 4_194_304 +max_length_storage_key: 4_194_304 +max_length_storage_value: 4_194_304 +max_promises_per_function_call_action: 1_024 +max_number_input_data_dependencies: 128 + +disable_9393_fix: false +flat_storage_reads: false +implicit_account_creation: false +fix_contract_loading_cost: false +math_extension: false +ed25519_verify: false +alt_bn128: false +function_call_weight: false +vm_kind: Wasmer0 +eth_implicit_accounts: false diff --git a/core/parameters/src/config.rs b/core/parameters/src/config.rs new file mode 100644 index 000000000..570bc3f91 --- /dev/null +++ b/core/parameters/src/config.rs @@ -0,0 +1,85 @@ +//! Settings of the parameters of the runtime. +use crate::config_store::INITIAL_TESTNET_CONFIG; +use crate::cost::RuntimeFeesConfig; +use crate::parameter_table::ParameterTable; +use unc_account_id::AccountId; +use unc_primitives_core::types::Balance; +use unc_primitives_core::version::PROTOCOL_VERSION; + +use super::parameter_table::InvalidConfigError; + +/// The structure that holds the parameters of the runtime, mostly economics. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RuntimeConfig { + /// Action gas costs, storage fees, and economic constants around them. + /// + /// This contains parameters that are required by the WASM runtime and the + /// transaction runtime. + pub fees: RuntimeFeesConfig, + /// Config of wasm operations, also includes wasm gas costs. + /// + /// This contains all the configuration parameters that are only required by + /// the WASM runtime. + pub wasm_config: crate::vm::Config, + /// Config that defines rules for account creation. + pub account_creation_config: AccountCreationConfig, +} + +impl RuntimeConfig { + pub(crate) fn new(params: &ParameterTable) -> Result { + RuntimeConfig::try_from(params) + } + + pub fn initial_testnet_config() -> RuntimeConfig { + INITIAL_TESTNET_CONFIG + .parse() + .and_then(|params| RuntimeConfig::new(¶ms)) + .expect("Failed parsing initial testnet config") + } + + pub fn test() -> Self { + let config_store = super::config_store::RuntimeConfigStore::new(None); + let wasm_config = + crate::vm::Config::clone(&config_store.get_config(PROTOCOL_VERSION).wasm_config); + RuntimeConfig { + fees: RuntimeFeesConfig::test(), + wasm_config, + account_creation_config: AccountCreationConfig::default(), + } + } + + pub fn free() -> Self { + let config_store = super::config_store::RuntimeConfigStore::new(None); + let mut wasm_config = + crate::vm::Config::clone(&config_store.get_config(PROTOCOL_VERSION).wasm_config); + wasm_config.make_free(); + Self { + fees: RuntimeFeesConfig::free(), + wasm_config, + account_creation_config: AccountCreationConfig::default(), + } + } + + pub fn storage_amount_per_byte(&self) -> Balance { + self.fees.storage_usage_config.storage_amount_per_byte + } +} + +/// The structure describes configuration for creation of new accounts. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AccountCreationConfig { + /// The minimum length of the top-level account ID that is allowed to be created by any account. + pub min_allowed_top_level_account_length: u8, + /// The account ID of the account registrar. This account ID allowed to create top-level + /// accounts of any valid length. + pub registrar_account_id: AccountId, +} + +impl Default for AccountCreationConfig { + fn default() -> Self { + Self { + min_allowed_top_level_account_length: 0, + registrar_account_id: "registrar".parse().unwrap(), + } + } +} diff --git a/core/parameters/src/config_store.rs b/core/parameters/src/config_store.rs new file mode 100644 index 000000000..ab954a736 --- /dev/null +++ b/core/parameters/src/config_store.rs @@ -0,0 +1,382 @@ +use crate::config::RuntimeConfig; +use crate::parameter_table::{ParameterTable, ParameterTableDiff}; +use unc_primitives_core::types::ProtocolVersion; +use std::collections::BTreeMap; +use std::ops::Bound; +use std::sync::Arc; + +macro_rules! include_config { + ($file:expr) => { + include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/res/runtime_configs/", $file)) + }; +} + +/// The base config file with all initial parameter values defined. +/// Later version are calculated by applying diffs to this base. +static BASE_CONFIG: &str = include_config!("parameters.yaml"); + +/// Stores pairs of protocol versions for which runtime config was updated and +/// the file containing the diffs in bytes. +static CONFIG_DIFFS: &[(ProtocolVersion, &str)] = &[ + (35, include_config!("35.yaml")), + (42, include_config!("42.yaml")), + (46, include_config!("46.yaml")), + (48, include_config!("48.yaml")), + (49, include_config!("49.yaml")), + (50, include_config!("50.yaml")), + // max_gas_burnt increased to 300 TGas + (52, include_config!("52.yaml")), + // Increased deployment costs, increased wasmer2 stack_limit, added limiting of contract locals, + // set read_cached_trie_node cost, decrease storage key limit + (53, include_config!("53.yaml")), + (55, include_config!("55.yaml")), + (57, include_config!("57.yaml")), + // Introduce Zero Balance Account and increase account creation cost to 7.7Tgas + (59, include_config!("59.yaml")), + (61, include_config!("61.yaml")), + (62, include_config!("62.yaml")), + (63, include_config!("63.yaml")), + (64, include_config!("64.yaml")), + (129, include_config!("129.yaml")), + // Introduce ETH-implicit accounts. + (138, include_config!("138.yaml")), +]; + +/// Testnet parameters for versions <= 29, which (incorrectly) differed from mainnet parameters +pub static INITIAL_TESTNET_CONFIG: &str = include_config!("parameters_testnet.yaml"); + +/// Stores runtime config for each protocol version where it was updated. +#[derive(Clone, Debug)] +pub struct RuntimeConfigStore { + /// Maps protocol version to the config. + store: BTreeMap>, +} + +impl RuntimeConfigStore { + /// Constructs a new store. + /// + /// If genesis_runtime_config is Some, configs for protocol versions 0 and 42 are overridden by + /// this config and config with lowered storage cost, respectively. + /// This is done to preserve compatibility with previous implementation, where we updated + /// runtime config by sequential modifications to the genesis runtime config. + /// calimero_zero_storage flag sets all storages fees to zero by setting + /// storage_amount_per_byte to zero, to keep calimero private shards compatible with future + /// protocol upgrades this is done for all protocol versions + /// TODO #4775: introduce new protocol version to have the same runtime config for all chains + pub fn new(genesis_runtime_config: Option<&RuntimeConfig>) -> Self { + let mut params: ParameterTable = + BASE_CONFIG.parse().expect("Failed parsing base parameter file."); + + let mut store = BTreeMap::new(); + #[cfg(not(feature = "calimero_zero_storage"))] + { + let initial_config = RuntimeConfig::new(¶ms).unwrap_or_else(|err| panic!("Failed generating `RuntimeConfig` from parameters for base parameter file. Error: {err}")); + store.insert(0, Arc::new(initial_config)); + } + #[cfg(feature = "calimero_zero_storage")] + { + let mut initial_config = RuntimeConfig::new(¶ms).unwrap_or_else(|err| panic!("Failed generating `RuntimeConfig` from parameters for base parameter file. Error: {err}")); + initial_config.fees.storage_usage_config.storage_amount_per_byte = 0; + store.insert(0, Arc::new(initial_config)); + } + + for (protocol_version, diff_bytes) in CONFIG_DIFFS { + let diff :ParameterTableDiff= diff_bytes.parse().unwrap_or_else(|err| panic!("Failed parsing runtime parameters diff for version {protocol_version}. Error: {err}")); + params.apply_diff(diff).unwrap_or_else(|err| panic!("Failed applying diff to `RuntimeConfig` for version {protocol_version}. Error: {err}")); + #[cfg(not(feature = "calimero_zero_storage"))] + store.insert( + *protocol_version, + Arc::new(RuntimeConfig::new(¶ms).unwrap_or_else(|err| panic!("Failed generating `RuntimeConfig` from parameters for version {protocol_version}. Error: {err}"))), + ); + #[cfg(feature = "calimero_zero_storage")] + { + let mut runtime_config = RuntimeConfig::new(¶ms).unwrap_or_else(|err| panic!("Failed generating `RuntimeConfig` from parameters for version {protocol_version}. Error: {err}")); + runtime_config.fees.storage_usage_config.storage_amount_per_byte = 0; + store.insert(*protocol_version, Arc::new(runtime_config)); + } + } + + if let Some(runtime_config) = genesis_runtime_config { + let mut config = runtime_config.clone(); + store.insert(0, Arc::new(config.clone())); + + config.fees.storage_usage_config.storage_amount_per_byte = 10u128.pow(19); + store.insert(42, Arc::new(config)); + } + + Self { store } + } + + /// Create store of runtime configs for the given chain id. + /// + /// For mainnet and other chains except testnet we don't need to override runtime config for + /// first protocol versions. + /// For testnet, runtime config for genesis block was (incorrectly) different, that's why we + /// need to override it specifically to preserve compatibility. + pub fn for_chain_id(chain_id: &str) -> Self { + match chain_id { + unc_primitives_core::chains::TESTNET => { + let genesis_runtime_config = RuntimeConfig::initial_testnet_config(); + Self::new(Some(&genesis_runtime_config)) + } + _ => Self::new(None), + } + } + + /// Constructs test store. + pub fn with_one_config(runtime_config: RuntimeConfig) -> Self { + Self { store: BTreeMap::from_iter([(0, Arc::new(runtime_config))].iter().cloned()) } + } + + /// Constructs test store. + pub fn test() -> Self { + Self::with_one_config(RuntimeConfig::test()) + } + + /// Constructs store with a single config with zero costs. + pub fn free() -> Self { + Self::with_one_config(RuntimeConfig::free()) + } + + /// Returns a `RuntimeConfig` for the corresponding protocol version. + pub fn get_config(&self, protocol_version: ProtocolVersion) -> &Arc { + self.store + .range((Bound::Unbounded, Bound::Included(protocol_version))) + .next_back() + .unwrap_or_else(|| { + panic!("Not found RuntimeConfig for protocol version {}", protocol_version) + }) + .1 + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cost::{ActionCosts, ExtCosts}; + use unc_primitives_core::version::ProtocolFeature::{ + LowerDataReceiptAndEcrecoverBaseCost, LowerStorageCost, LowerStorageKeyLimit, + }; + use std::collections::HashSet; + + const GENESIS_PROTOCOL_VERSION: ProtocolVersion = 29; + const RECEIPTS_DEPTH: u64 = 63; + + #[test] + fn all_configs_are_specified() { + let file_versions = + std::fs::read_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/res/runtime_configs/")) + .expect("can open config directory"); + let mut files = file_versions + .into_iter() + .map(|de| { + de.expect("direntry should read successfully") + .path() + .file_name() + .expect("direntry should have a filename") + .to_string_lossy() + .into_owned() + }) + .collect::>(); + + for (ver, _) in super::CONFIG_DIFFS { + assert!(files.remove(&format!("{ver}.yaml")), "{ver}.yaml file is missing?"); + } + + for file in files { + let Some((name, "yaml")) = file.rsplit_once(".") else { continue }; + let Ok(version_num) = name.parse::() else { continue }; + panic!("CONFIG_DIFFS does not contain reference to the {version_num}.yaml file!"); + } + } + + #[test] + fn test_max_prepaid_gas() { + let store = RuntimeConfigStore::new(None); + for (protocol_version, config) in store.store.iter() { + assert!( + config.wasm_config.limit_config.max_total_prepaid_gas + / config.fees.min_receipt_with_function_call_gas() + <= 63, + "The maximum desired depth of receipts for protocol version {} should be at most {}", + protocol_version, + RECEIPTS_DEPTH + ); + } + } + + #[test] + #[cfg(not(feature = "calimero_zero_storage"))] + fn test_lower_storage_cost() { + let store = RuntimeConfigStore::new(None); + let base_cfg = store.get_config(GENESIS_PROTOCOL_VERSION); + let new_cfg = store.get_config(LowerStorageCost.protocol_version()); + assert!(base_cfg.storage_amount_per_byte() > new_cfg.storage_amount_per_byte()); + } + + #[test] + fn test_override_account_length() { + // Check that default value is 32. + let base_store = RuntimeConfigStore::new(None); + let base_cfg = base_store.get_config(GENESIS_PROTOCOL_VERSION); + assert_eq!(base_cfg.account_creation_config.min_allowed_top_level_account_length, 32); + + let mut cfg = base_cfg.as_ref().clone(); + cfg.account_creation_config.min_allowed_top_level_account_length = 0; + + // Check that length was changed. + let new_store = RuntimeConfigStore::new(Some(&cfg)); + let new_cfg = new_store.get_config(GENESIS_PROTOCOL_VERSION); + assert_eq!(new_cfg.account_creation_config.min_allowed_top_level_account_length, 0); + } + + #[test] + fn test_lower_data_receipt_cost() { + let store = RuntimeConfigStore::new(None); + let base_cfg = store.get_config(LowerStorageCost.protocol_version()); + let new_cfg = store.get_config(LowerDataReceiptAndEcrecoverBaseCost.protocol_version()); + assert!( + base_cfg.fees.fee(ActionCosts::new_data_receipt_base).send_sir + > new_cfg.fees.fee(ActionCosts::new_data_receipt_base).send_sir + ); + assert!( + base_cfg.fees.fee(ActionCosts::new_data_receipt_byte).send_sir + > new_cfg.fees.fee(ActionCosts::new_data_receipt_byte).send_sir + ); + } + + // Check that for protocol version with lowered data receipt cost, runtime config passed to + // config store is overridden. + #[test] + #[cfg(not(feature = "calimero_zero_storage"))] + fn test_override_runtime_config() { + let store = RuntimeConfigStore::new(None); + let config = store.get_config(0); + + let mut base_params = BASE_CONFIG.parse().unwrap(); + let base_config = RuntimeConfig::new(&base_params).unwrap(); + assert_eq!(config.as_ref(), &base_config); + + let config = store.get_config(LowerStorageCost.protocol_version()); + assert_eq!(base_config.storage_amount_per_byte(), 100_000_000_000_000_000_000u128); + assert_eq!(config.storage_amount_per_byte(), 10_000_000_000_000_000_000u128); + assert_eq!(config.fees.fee(ActionCosts::new_data_receipt_base).send_sir, 4_697_339_419_375); + assert_ne!(config.as_ref(), &base_config); + assert_ne!( + config.as_ref(), + store.get_config(LowerStorageCost.protocol_version() - 1).as_ref() + ); + + for (ver, diff) in &CONFIG_DIFFS[..] { + if *ver <= LowerStorageCost.protocol_version() { + base_params.apply_diff(diff.parse().unwrap()).unwrap(); + } + } + let expected_config = RuntimeConfig::new(&base_params).unwrap(); + assert_eq!(**config, expected_config); + + let config = store.get_config(LowerDataReceiptAndEcrecoverBaseCost.protocol_version()); + assert_eq!(config.fees.fee(ActionCosts::new_data_receipt_base).send_sir, 36_486_732_312); + for (ver, diff) in &CONFIG_DIFFS[..] { + if *ver <= LowerStorageCost.protocol_version() { + continue; + } else if *ver <= LowerDataReceiptAndEcrecoverBaseCost.protocol_version() { + base_params.apply_diff(diff.parse().unwrap()).unwrap(); + } + } + let expected_config = RuntimeConfig::new(&base_params).unwrap(); + assert_eq!(config.as_ref(), &expected_config); + } + + #[test] + fn test_lower_ecrecover_base_cost() { + let store = RuntimeConfigStore::new(None); + let base_cfg = store.get_config(LowerStorageCost.protocol_version()); + let new_cfg = store.get_config(LowerDataReceiptAndEcrecoverBaseCost.protocol_version()); + assert!( + base_cfg.wasm_config.ext_costs.gas_cost(ExtCosts::ecrecover_base) + > new_cfg.wasm_config.ext_costs.gas_cost(ExtCosts::ecrecover_base) + ); + } + + #[test] + fn test_lower_max_length_storage_key() { + let store = RuntimeConfigStore::new(None); + let base_cfg = store.get_config(LowerStorageKeyLimit.protocol_version() - 1); + let new_cfg = store.get_config(LowerStorageKeyLimit.protocol_version()); + assert!( + base_cfg.wasm_config.limit_config.max_length_storage_key + > new_cfg.wasm_config.limit_config.max_length_storage_key + ); + } + + /// Use snapshot testing to check that the JSON representation of the + /// configurations of each version is unchanged. + /// If tests fail after an intended change, run `cargo insta review` accept + /// the new snapshot if it looks right. + #[test] + #[cfg(not(feature = "nightly"))] + #[cfg(not(feature = "calimero_zero_storage"))] + fn test_json_unchanged() { + use crate::view::RuntimeConfigView; + use unc_primitives_core::version::PROTOCOL_VERSION; + + let store = RuntimeConfigStore::new(None); + let mut any_failure = false; + + for version in store.store.keys() { + let snapshot_name = format!("{version}.json"); + let config_view = RuntimeConfigView::from(store.get_config(*version).as_ref().clone()); + any_failure |= std::panic::catch_unwind(|| { + insta::assert_json_snapshot!(snapshot_name, config_view, { ".wasm_config.vm_kind" => ""}); + }) + .is_err(); + } + + // Store the latest values of parameters in a human-readable snapshot. + { + let mut params: ParameterTable = BASE_CONFIG.parse().unwrap(); + for (_, diff_bytes) in + CONFIG_DIFFS.iter().filter(|(version, _)| *version <= PROTOCOL_VERSION) + { + params.apply_diff(diff_bytes.parse().unwrap()).unwrap(); + } + insta::with_settings!({ + snapshot_path => "../res/runtime_configs", + prepend_module_to_snapshot => false, + description => "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + omit_expression => true, + }, { + any_failure |= std::panic::catch_unwind(|| { + insta::assert_display_snapshot!("parameters", params); + }).is_err(); + }); + } + + // Testnet initial config for old version was different, thus needs separate testing + let params = INITIAL_TESTNET_CONFIG.parse().unwrap(); + let new_genesis_runtime_config = RuntimeConfig::new(¶ms).unwrap(); + let testnet_store = RuntimeConfigStore::new(Some(&new_genesis_runtime_config)); + + for version in testnet_store.store.keys() { + let snapshot_name = format!("testnet_{version}.json"); + let config_view = RuntimeConfigView::from(store.get_config(*version).as_ref().clone()); + any_failure |= std::panic::catch_unwind(|| { + insta::assert_json_snapshot!(snapshot_name, config_view, { ".wasm_config.vm_kind" => ""}); + }) + .is_err(); + } + if any_failure { + panic!("some snapshot assertions failed"); + } + } + + #[test] + #[cfg(feature = "calimero_zero_storage")] + fn test_calimero_storage_costs_zero() { + let store = RuntimeConfigStore::new(None); + for (_, config) in store.store.iter() { + assert_eq!(config.storage_amount_per_byte(), 0u128); + } + } +} diff --git a/core/parameters/src/cost.rs b/core/parameters/src/cost.rs new file mode 100644 index 000000000..5a7277ae1 --- /dev/null +++ b/core/parameters/src/cost.rs @@ -0,0 +1,571 @@ +use crate::parameter::Parameter; +use enum_map::{enum_map, EnumMap}; +use unc_account_id::AccountType; +use unc_primitives_core::types::{Balance, Compute, Gas}; +use num_rational::Rational32; + +/// Costs associated with an object that can only be sent over the network (and executed +/// by the receiver). +/// NOTE: `send_sir` or `send_not_sir` fees are usually burned when the item is being created. +/// And `execution` fee is burned when the item is being executed. +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Hash, PartialEq, Eq)] +pub struct Fee { + /// Fee for sending an object from the sender to itself, guaranteeing that it does not leave + /// the shard. + pub send_sir: Gas, + /// Fee for sending an object potentially across the shards. + pub send_not_sir: Gas, + /// Fee for executing the object. + pub execution: Gas, +} + +impl Fee { + #[inline] + pub fn send_fee(&self, sir: bool) -> Gas { + if sir { + self.send_sir + } else { + self.send_not_sir + } + } + + pub fn exec_fee(&self) -> Gas { + self.execution + } + + /// The minimum fee to send and execute. + pub fn min_send_and_exec_fee(&self) -> Gas { + std::cmp::min(self.send_sir, self.send_not_sir) + self.execution + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct ParameterCost { + pub gas: Gas, + pub compute: Compute, +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct ExtCostsConfig { + pub costs: EnumMap, +} + +// We multiply the actual computed costs by the fixed factor to ensure we +// have certain reserve for further gas price variation. +const SAFETY_MULTIPLIER: u64 = 3; + +impl ExtCostsConfig { + pub fn gas_cost(&self, param: ExtCosts) -> Gas { + self.costs[param].gas + } + + pub fn compute_cost(&self, param: ExtCosts) -> Compute { + self.costs[param].compute + } + + /// Convenience constructor to use in tests where the exact gas cost does + /// not need to correspond to a specific protocol version. + pub fn test_with_undercharging_factor(factor: u64) -> ExtCostsConfig { + let costs = enum_map! { + ExtCosts::base => SAFETY_MULTIPLIER * 88256037, + ExtCosts::contract_loading_base => SAFETY_MULTIPLIER * 11815321, + ExtCosts::contract_loading_bytes => SAFETY_MULTIPLIER * 72250, + ExtCosts::read_memory_base => SAFETY_MULTIPLIER * 869954400, + ExtCosts::read_memory_byte => SAFETY_MULTIPLIER * 1267111, + ExtCosts::write_memory_base => SAFETY_MULTIPLIER * 934598287, + ExtCosts::write_memory_byte => SAFETY_MULTIPLIER * 907924, + ExtCosts::read_register_base => SAFETY_MULTIPLIER * 839055062, + ExtCosts::read_register_byte => SAFETY_MULTIPLIER * 32854, + ExtCosts::write_register_base => SAFETY_MULTIPLIER * 955174162, + ExtCosts::write_register_byte => SAFETY_MULTIPLIER * 1267188, + ExtCosts::utf8_decoding_base => SAFETY_MULTIPLIER * 1037259687, + ExtCosts::utf8_decoding_byte => SAFETY_MULTIPLIER * 97193493, + ExtCosts::utf16_decoding_base => SAFETY_MULTIPLIER * 1181104350, + ExtCosts::utf16_decoding_byte => SAFETY_MULTIPLIER * 54525831, + ExtCosts::sha256_base => SAFETY_MULTIPLIER * 1513656750, + ExtCosts::sha256_byte => SAFETY_MULTIPLIER * 8039117, + ExtCosts::keccak256_base => SAFETY_MULTIPLIER * 1959830425, + ExtCosts::keccak256_byte => SAFETY_MULTIPLIER * 7157035, + ExtCosts::keccak512_base => SAFETY_MULTIPLIER * 1937129412, + ExtCosts::keccak512_byte => SAFETY_MULTIPLIER * 12216567, + ExtCosts::ripemd160_base => SAFETY_MULTIPLIER * 284558362, + ExtCosts::ed25519_verify_base => SAFETY_MULTIPLIER * 1513656750, + ExtCosts::ed25519_verify_byte => SAFETY_MULTIPLIER * 7157035, + ExtCosts::ripemd160_block => SAFETY_MULTIPLIER * 226702528, + ExtCosts::ecrecover_base => SAFETY_MULTIPLIER * 1121789875000, + ExtCosts::log_base => SAFETY_MULTIPLIER * 1181104350, + ExtCosts::log_byte => SAFETY_MULTIPLIER * 4399597, + ExtCosts::storage_write_base => SAFETY_MULTIPLIER * 21398912000, + ExtCosts::storage_write_key_byte => SAFETY_MULTIPLIER * 23494289, + ExtCosts::storage_write_value_byte => SAFETY_MULTIPLIER * 10339513, + ExtCosts::storage_write_evicted_byte => SAFETY_MULTIPLIER * 10705769, + ExtCosts::storage_read_base => SAFETY_MULTIPLIER * 18785615250, + ExtCosts::storage_read_key_byte => SAFETY_MULTIPLIER * 10317511, + ExtCosts::storage_read_value_byte => SAFETY_MULTIPLIER * 1870335, + ExtCosts::storage_remove_base => SAFETY_MULTIPLIER * 17824343500, + ExtCosts::storage_remove_key_byte => SAFETY_MULTIPLIER * 12740128, + ExtCosts::storage_remove_ret_value_byte => SAFETY_MULTIPLIER * 3843852, + ExtCosts::storage_has_key_base => SAFETY_MULTIPLIER * 18013298875, + ExtCosts::storage_has_key_byte => SAFETY_MULTIPLIER * 10263615, + // Here it should be `SAFETY_MULTIPLIER * 0` for consistency, but then + // clippy complains with "this operation will always return zero" warning + ExtCosts::storage_iter_create_prefix_base => 0, + ExtCosts::storage_iter_create_prefix_byte => 0, + ExtCosts::storage_iter_create_range_base => 0, + ExtCosts::storage_iter_create_from_byte => 0, + ExtCosts::storage_iter_create_to_byte => 0, + ExtCosts::storage_iter_next_base => 0, + ExtCosts::storage_iter_next_key_byte => 0, + ExtCosts::storage_iter_next_value_byte => 0, + ExtCosts::touching_trie_node => SAFETY_MULTIPLIER * 5367318642, + ExtCosts::read_cached_trie_node => SAFETY_MULTIPLIER * 760_000_000, + ExtCosts::promise_and_base => SAFETY_MULTIPLIER * 488337800, + ExtCosts::promise_and_per_promise => SAFETY_MULTIPLIER * 1817392, + ExtCosts::promise_return => SAFETY_MULTIPLIER * 186717462, + ExtCosts::validator_frozen_base => SAFETY_MULTIPLIER * 303944908800, + ExtCosts::validator_total_frozen_base => SAFETY_MULTIPLIER * 303944908800, + ExtCosts::alt_bn128_g1_multiexp_base => 713_000_000_000, + ExtCosts::alt_bn128_g1_multiexp_element => 320_000_000_000, + ExtCosts::alt_bn128_pairing_check_base => 9_686_000_000_000, + ExtCosts::alt_bn128_pairing_check_element => 5_102_000_000_000, + ExtCosts::alt_bn128_g1_sum_base => 3_000_000_000, + ExtCosts::alt_bn128_g1_sum_element => 5_000_000_000, + ExtCosts::validator_power_base => SAFETY_MULTIPLIER * 3_000_000_000, + ExtCosts::validator_total_power_base => SAFETY_MULTIPLIER * 3_000_000_000, + } + .map(|_, value| ParameterCost { gas: value, compute: value * factor }); + ExtCostsConfig { costs } + } + + /// `test_with_undercharging_factor` with a factor of 1. + pub fn test() -> ExtCostsConfig { + Self::test_with_undercharging_factor(1) + } +} + +/// Strongly-typed representation of the fees for counting. +/// +/// Do not change the enum discriminants here, they are used for borsh +/// (de-)serialization. +#[derive( + Copy, + Clone, + Hash, + PartialEq, + Eq, + Debug, + PartialOrd, + Ord, + strum::Display, + strum::EnumIter, + enum_map::Enum, +)] +#[allow(non_camel_case_types)] +pub enum ExtCosts { + base = 0, + contract_loading_base = 1, + contract_loading_bytes = 2, + read_memory_base = 3, + read_memory_byte = 4, + write_memory_base = 5, + write_memory_byte = 6, + read_register_base = 7, + read_register_byte = 8, + write_register_base = 9, + write_register_byte = 10, + utf8_decoding_base = 11, + utf8_decoding_byte = 12, + utf16_decoding_base = 13, + utf16_decoding_byte = 14, + sha256_base = 15, + sha256_byte = 16, + keccak256_base = 17, + keccak256_byte = 18, + keccak512_base = 19, + keccak512_byte = 20, + ripemd160_base = 21, + ripemd160_block = 22, + ecrecover_base = 23, + log_base = 24, + log_byte = 25, + storage_write_base = 26, + storage_write_key_byte = 27, + storage_write_value_byte = 28, + storage_write_evicted_byte = 29, + storage_read_base = 30, + storage_read_key_byte = 31, + storage_read_value_byte = 32, + storage_remove_base = 33, + storage_remove_key_byte = 34, + storage_remove_ret_value_byte = 35, + storage_has_key_base = 36, + storage_has_key_byte = 37, + storage_iter_create_prefix_base = 38, + storage_iter_create_prefix_byte = 39, + storage_iter_create_range_base = 40, + storage_iter_create_from_byte = 41, + storage_iter_create_to_byte = 42, + storage_iter_next_base = 43, + storage_iter_next_key_byte = 44, + storage_iter_next_value_byte = 45, + touching_trie_node = 46, + read_cached_trie_node = 47, + promise_and_base = 48, + promise_and_per_promise = 49, + promise_return = 50, + validator_frozen_base = 51, + validator_total_frozen_base = 52, + alt_bn128_g1_multiexp_base = 53, + alt_bn128_g1_multiexp_element = 54, + alt_bn128_pairing_check_base = 55, + alt_bn128_pairing_check_element = 56, + alt_bn128_g1_sum_base = 57, + alt_bn128_g1_sum_element = 58, + ed25519_verify_base = 59, + ed25519_verify_byte = 60, + validator_power_base = 61, + validator_total_power_base = 62, +} + +// Type of an action, used in fees logic. +#[derive( + Copy, + Clone, + Hash, + PartialEq, + Eq, + Debug, + PartialOrd, + Ord, + strum::Display, + strum::EnumIter, + enum_map::Enum, +)] +#[allow(non_camel_case_types)] +pub enum ActionCosts { + create_account = 0, + delete_account = 1, + deploy_contract_base = 2, + deploy_contract_byte = 3, + function_call_base = 4, + function_call_byte = 5, + transfer = 6, + stake = 7, + add_full_access_key = 8, + add_function_call_key_base = 9, + add_function_call_key_byte = 10, + delete_key = 11, + new_action_receipt = 12, + new_data_receipt_base = 13, + new_data_receipt_byte = 14, + delegate = 15, + register_rsa2048_keys = 16, + create_rsa2048_challenge = 17, +} + +impl ExtCosts { + pub fn gas(self, config: &ExtCostsConfig) -> Gas { + config.gas_cost(self) + } + + pub fn compute(self, config: &ExtCostsConfig) -> Compute { + config.compute_cost(self) + } + + pub fn param(&self) -> Parameter { + match self { + ExtCosts::base => Parameter::WasmBase, + ExtCosts::contract_loading_base => Parameter::WasmContractLoadingBase, + ExtCosts::contract_loading_bytes => Parameter::WasmContractLoadingBytes, + ExtCosts::read_memory_base => Parameter::WasmReadMemoryBase, + ExtCosts::read_memory_byte => Parameter::WasmReadMemoryByte, + ExtCosts::write_memory_base => Parameter::WasmWriteMemoryBase, + ExtCosts::write_memory_byte => Parameter::WasmWriteMemoryByte, + ExtCosts::read_register_base => Parameter::WasmReadRegisterBase, + ExtCosts::read_register_byte => Parameter::WasmReadRegisterByte, + ExtCosts::write_register_base => Parameter::WasmWriteRegisterBase, + ExtCosts::write_register_byte => Parameter::WasmWriteRegisterByte, + ExtCosts::utf8_decoding_base => Parameter::WasmUtf8DecodingBase, + ExtCosts::utf8_decoding_byte => Parameter::WasmUtf8DecodingByte, + ExtCosts::utf16_decoding_base => Parameter::WasmUtf16DecodingBase, + ExtCosts::utf16_decoding_byte => Parameter::WasmUtf16DecodingByte, + ExtCosts::sha256_base => Parameter::WasmSha256Base, + ExtCosts::sha256_byte => Parameter::WasmSha256Byte, + ExtCosts::keccak256_base => Parameter::WasmKeccak256Base, + ExtCosts::keccak256_byte => Parameter::WasmKeccak256Byte, + ExtCosts::keccak512_base => Parameter::WasmKeccak512Base, + ExtCosts::keccak512_byte => Parameter::WasmKeccak512Byte, + ExtCosts::ripemd160_base => Parameter::WasmRipemd160Base, + ExtCosts::ripemd160_block => Parameter::WasmRipemd160Block, + ExtCosts::ecrecover_base => Parameter::WasmEcrecoverBase, + ExtCosts::ed25519_verify_base => Parameter::WasmEd25519VerifyBase, + ExtCosts::ed25519_verify_byte => Parameter::WasmEd25519VerifyByte, + ExtCosts::log_base => Parameter::WasmLogBase, + ExtCosts::log_byte => Parameter::WasmLogByte, + ExtCosts::storage_write_base => Parameter::WasmStorageWriteBase, + ExtCosts::storage_write_key_byte => Parameter::WasmStorageWriteKeyByte, + ExtCosts::storage_write_value_byte => Parameter::WasmStorageWriteValueByte, + ExtCosts::storage_write_evicted_byte => Parameter::WasmStorageWriteEvictedByte, + ExtCosts::storage_read_base => Parameter::WasmStorageReadBase, + ExtCosts::storage_read_key_byte => Parameter::WasmStorageReadKeyByte, + ExtCosts::storage_read_value_byte => Parameter::WasmStorageReadValueByte, + ExtCosts::storage_remove_base => Parameter::WasmStorageRemoveBase, + ExtCosts::storage_remove_key_byte => Parameter::WasmStorageRemoveKeyByte, + ExtCosts::storage_remove_ret_value_byte => Parameter::WasmStorageRemoveRetValueByte, + ExtCosts::storage_has_key_base => Parameter::WasmStorageHasKeyBase, + ExtCosts::storage_has_key_byte => Parameter::WasmStorageHasKeyByte, + ExtCosts::storage_iter_create_prefix_base => Parameter::WasmStorageIterCreatePrefixBase, + ExtCosts::storage_iter_create_prefix_byte => Parameter::WasmStorageIterCreatePrefixByte, + ExtCosts::storage_iter_create_range_base => Parameter::WasmStorageIterCreateRangeBase, + ExtCosts::storage_iter_create_from_byte => Parameter::WasmStorageIterCreateFromByte, + ExtCosts::storage_iter_create_to_byte => Parameter::WasmStorageIterCreateToByte, + ExtCosts::storage_iter_next_base => Parameter::WasmStorageIterNextBase, + ExtCosts::storage_iter_next_key_byte => Parameter::WasmStorageIterNextKeyByte, + ExtCosts::storage_iter_next_value_byte => Parameter::WasmStorageIterNextValueByte, + ExtCosts::touching_trie_node => Parameter::WasmTouchingTrieNode, + ExtCosts::read_cached_trie_node => Parameter::WasmReadCachedTrieNode, + ExtCosts::promise_and_base => Parameter::WasmPromiseAndBase, + ExtCosts::promise_and_per_promise => Parameter::WasmPromiseAndPerPromise, + ExtCosts::promise_return => Parameter::WasmPromiseReturn, + ExtCosts::validator_frozen_base => Parameter::WasmValidatorPowerBase, + ExtCosts::validator_total_frozen_base => Parameter::WasmValidatorTotalPowerBase, + ExtCosts::alt_bn128_g1_multiexp_base => Parameter::WasmAltBn128G1MultiexpBase, + ExtCosts::alt_bn128_g1_multiexp_element => Parameter::WasmAltBn128G1MultiexpElement, + ExtCosts::alt_bn128_pairing_check_base => Parameter::WasmAltBn128PairingCheckBase, + ExtCosts::alt_bn128_pairing_check_element => Parameter::WasmAltBn128PairingCheckElement, + ExtCosts::alt_bn128_g1_sum_base => Parameter::WasmAltBn128G1SumBase, + ExtCosts::alt_bn128_g1_sum_element => Parameter::WasmAltBn128G1SumElement, + ExtCosts::validator_power_base => Parameter::WasmValidatorFrozenBase, + ExtCosts::validator_total_power_base => Parameter::WasmValidatorTotalFrozenBase, + } + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct RuntimeFeesConfig { + /// Gas fees for sending and executing actions. + pub action_fees: EnumMap, + + /// Describes fees for storage. + pub storage_usage_config: StorageUsageConfig, + + /// Fraction of the burnt gas to reward to the contract account for execution. + pub burnt_gas_reward: Rational32, + + /// Pessimistic gas price inflation ratio. + pub pessimistic_gas_price_inflation_ratio: Rational32, +} + +/// Describes cost of storage per block +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct StorageUsageConfig { + /// Amount of yN per byte required to have on the account. See + /// for details. + pub storage_amount_per_byte: Balance, + /// Number of bytes for an account record, including rounding up for account id. + pub num_bytes_account: u64, + /// Additional number of bytes for a k/v record + pub num_extra_bytes_record: u64, +} + +impl RuntimeFeesConfig { + /// Access action fee by `ActionCosts`. + pub fn fee(&self, cost: ActionCosts) -> &Fee { + &self.action_fees[cost] + } + + pub fn test() -> Self { + Self { + storage_usage_config: StorageUsageConfig::test(), + burnt_gas_reward: Rational32::new(3, 10), + pessimistic_gas_price_inflation_ratio: Rational32::new(103, 100), + action_fees: enum_map::enum_map! { + ActionCosts::create_account => Fee { + send_sir: 3_850_000_000_000, + send_not_sir: 3_850_000_000_000, + execution: 3_850_000_000_000, + }, + ActionCosts::delete_account => Fee { + send_sir: 147489000000, + send_not_sir: 147489000000, + execution: 147489000000, + }, + ActionCosts::deploy_contract_base => Fee { + send_sir: 184765750000, + send_not_sir: 184765750000, + execution: 184765750000, + }, + ActionCosts::deploy_contract_byte => Fee { + send_sir: 6812999, + send_not_sir: 6812999, + execution: 6812999, + }, + ActionCosts::function_call_base => Fee { + send_sir: 2319861500000, + send_not_sir: 2319861500000, + execution: 2319861500000, + }, + ActionCosts::function_call_byte => Fee { + send_sir: 2235934, + send_not_sir: 2235934, + execution: 2235934, + }, + ActionCosts::transfer => Fee { + send_sir: 115123062500, + send_not_sir: 115123062500, + execution: 115123062500, + }, + ActionCosts::stake => Fee { + send_sir: 141715687500, + send_not_sir: 141715687500, + execution: 102217625000, + }, + ActionCosts::add_full_access_key => Fee { + send_sir: 101765125000, + send_not_sir: 101765125000, + execution: 101765125000, + }, + ActionCosts::add_function_call_key_base => Fee { + send_sir: 102217625000, + send_not_sir: 102217625000, + execution: 102217625000, + }, + ActionCosts::add_function_call_key_byte => Fee { + send_sir: 1925331, + send_not_sir: 1925331, + execution: 1925331, + }, + ActionCosts::delete_key => Fee { + send_sir: 94946625000, + send_not_sir: 94946625000, + execution: 94946625000, + }, + ActionCosts::new_action_receipt => Fee { + send_sir: 108059500000, + send_not_sir: 108059500000, + execution: 108059500000, + }, + ActionCosts::new_data_receipt_base => Fee { + send_sir: 4697339419375, + send_not_sir: 4697339419375, + execution: 4697339419375, + }, + ActionCosts::new_data_receipt_byte => Fee { + send_sir: 59357464, + send_not_sir: 59357464, + execution: 59357464, + }, + ActionCosts::delegate => Fee { + send_sir: 200_000_000_000, + send_not_sir: 200_000_000_000, + execution: 200_000_000_000, + }, + ActionCosts::register_rsa2048_keys => Fee { + send_sir: 101765125000, + send_not_sir: 101765125000, + execution: 101765125000, + }, + ActionCosts::create_rsa2048_challenge => Fee { + send_sir: 115123062500, + send_not_sir: 115123062500, + execution: 115123062500, + }, + }, + } + } + + pub fn free() -> Self { + Self { + action_fees: enum_map::enum_map! { + _ => Fee { send_sir: 0, send_not_sir: 0, execution: 0 } + }, + storage_usage_config: StorageUsageConfig::free(), + burnt_gas_reward: Rational32::from_integer(0), + pessimistic_gas_price_inflation_ratio: Rational32::from_integer(0), + } + } + + /// The minimum amount of gas required to create and execute a new receipt with a function call + /// action. + /// This amount is used to determine how many receipts can be created, send and executed for + /// some amount of prepaid gas using function calls. + pub fn min_receipt_with_function_call_gas(&self) -> Gas { + self.fee(ActionCosts::new_action_receipt).min_send_and_exec_fee() + + self.fee(ActionCosts::function_call_base).min_send_and_exec_fee() + } +} + +impl StorageUsageConfig { + pub fn test() -> Self { + Self { + num_bytes_account: 100, + num_extra_bytes_record: 40, + storage_amount_per_byte: 909 * 100_000_000_000_000_000, + } + } + + pub(crate) fn free() -> StorageUsageConfig { + Self { num_bytes_account: 0, num_extra_bytes_record: 0, storage_amount_per_byte: 0 } + } +} + +/// Helper functions for computing Transfer fees. +/// In case of implicit account creation they include extra fees for the CreateAccount and +/// AddFullAccessKey (for NEAR-implicit account only) actions that are implicit. +/// We can assume that no overflow will happen here. +pub fn transfer_exec_fee( + cfg: &RuntimeFeesConfig, + implicit_account_creation_allowed: bool, + eth_implicit_accounts_enabled: bool, + receiver_account_type: AccountType, +) -> Gas { + let transfer_fee = cfg.fee(ActionCosts::transfer).exec_fee(); + match (implicit_account_creation_allowed, eth_implicit_accounts_enabled, receiver_account_type) + { + // Regular transfer to a named account. + (_, _, AccountType::NamedAccount) => transfer_fee, + // No account will be created, just a regular transfer. + (false, _, _) => transfer_fee, + // No account will be created, just a regular transfer. + (true, false, AccountType::EthImplicitAccount) => transfer_fee, + // Extra fee for the CreateAccount. + (true, true, AccountType::EthImplicitAccount) => { + transfer_fee + cfg.fee(ActionCosts::create_account).exec_fee() + } + // Extra fees for the CreateAccount and AddFullAccessKey. + (true, _, AccountType::NearImplicitAccount) => { + transfer_fee + + cfg.fee(ActionCosts::create_account).exec_fee() + + cfg.fee(ActionCosts::add_full_access_key).exec_fee() + } + } +} + +pub fn transfer_send_fee( + cfg: &RuntimeFeesConfig, + sender_is_receiver: bool, + implicit_account_creation_allowed: bool, + eth_implicit_accounts_enabled: bool, + receiver_account_type: AccountType, +) -> Gas { + let transfer_fee = cfg.fee(ActionCosts::transfer).send_fee(sender_is_receiver); + match (implicit_account_creation_allowed, eth_implicit_accounts_enabled, receiver_account_type) + { + // Regular transfer to a named account. + (_, _, AccountType::NamedAccount) => transfer_fee, + // No account will be created, just a regular transfer. + (false, _, _) => transfer_fee, + // No account will be created, just a regular transfer. + (true, false, AccountType::EthImplicitAccount) => transfer_fee, + // Extra fee for the CreateAccount. + (true, true, AccountType::EthImplicitAccount) => { + transfer_fee + cfg.fee(ActionCosts::create_account).send_fee(sender_is_receiver) + } + // Extra fees for the CreateAccount and AddFullAccessKey. + (true, _, AccountType::NearImplicitAccount) => { + transfer_fee + + cfg.fee(ActionCosts::create_account).send_fee(sender_is_receiver) + + cfg.fee(ActionCosts::add_full_access_key).send_fee(sender_is_receiver) + } + } +} diff --git a/core/parameters/src/lib.rs b/core/parameters/src/lib.rs new file mode 100644 index 000000000..3eaa3900c --- /dev/null +++ b/core/parameters/src/lib.rs @@ -0,0 +1,16 @@ +pub mod config; +pub mod config_store; +pub mod cost; +pub mod parameter; +pub mod parameter_table; +pub mod view; +pub mod vm; + +pub use config::{AccountCreationConfig, RuntimeConfig}; +pub use config_store::RuntimeConfigStore; +pub use cost::{ + transfer_exec_fee, transfer_send_fee, ActionCosts, ExtCosts, ExtCostsConfig, Fee, + ParameterCost, RuntimeFeesConfig, StorageUsageConfig, +}; +pub use parameter::Parameter; +pub use view::{RuntimeConfigView, RuntimeFeesConfigView}; diff --git a/core/parameters/src/parameter.rs b/core/parameters/src/parameter.rs new file mode 100644 index 000000000..b19145bbf --- /dev/null +++ b/core/parameters/src/parameter.rs @@ -0,0 +1,262 @@ +use crate::cost::ActionCosts; +use std::slice; + +/// Protocol configuration parameter which may change between protocol versions. +#[derive( + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Debug, + strum::Display, + strum::EnumString, + strum::IntoStaticStr, +)] +#[strum(serialize_all = "snake_case")] +pub enum Parameter { + // Gas economics config + BurntGasReward, + PessimisticGasPriceInflation, + + // Account creation config + MinAllowedTopLevelAccountLength, + RegistrarAccountId, + + // Storage usage config + StorageAmountPerByte, + StorageNumBytesAccount, + StorageNumExtraBytesRecord, + + // Static action costs + // send_sir / send_not_sir is burned when creating a receipt on the signer shard. + // (SIR = signer is receiver, which guarantees the receipt is local.) + // Execution is burned when applying receipt on receiver shard. + ActionReceiptCreation, + DataReceiptCreationBase, + DataReceiptCreationPerByte, + ActionCreateAccount, + ActionDeleteAccount, + ActionDeployContract, + ActionDeployContractPerByte, + ActionFunctionCall, + ActionFunctionCallPerByte, + ActionTransfer, + ActionStake, + ActionAddFullAccessKey, + ActionAddFunctionCallKey, + ActionAddFunctionCallKeyPerByte, + ActionDeleteKey, + ActionDelegate, + + // Smart contract dynamic gas costs + WasmRegularOpCost, + WasmGrowMemCost, + /// Base cost for a host function + WasmBase, + WasmContractLoadingBase, + WasmContractLoadingBytes, + WasmReadMemoryBase, + WasmReadMemoryByte, + WasmWriteMemoryBase, + WasmWriteMemoryByte, + WasmReadRegisterBase, + WasmReadRegisterByte, + WasmWriteRegisterBase, + WasmWriteRegisterByte, + WasmUtf8DecodingBase, + WasmUtf8DecodingByte, + WasmUtf16DecodingBase, + WasmUtf16DecodingByte, + WasmSha256Base, + WasmSha256Byte, + WasmKeccak256Base, + WasmKeccak256Byte, + WasmKeccak512Base, + WasmKeccak512Byte, + WasmRipemd160Base, + WasmRipemd160Block, + WasmEcrecoverBase, + WasmEd25519VerifyBase, + WasmEd25519VerifyByte, + WasmLogBase, + WasmLogByte, + WasmStorageWriteBase, + WasmStorageWriteKeyByte, + WasmStorageWriteValueByte, + WasmStorageWriteEvictedByte, + WasmStorageReadBase, + WasmStorageReadKeyByte, + WasmStorageReadValueByte, + WasmStorageRemoveBase, + WasmStorageRemoveKeyByte, + WasmStorageRemoveRetValueByte, + WasmStorageHasKeyBase, + WasmStorageHasKeyByte, + WasmStorageIterCreatePrefixBase, + WasmStorageIterCreatePrefixByte, + WasmStorageIterCreateRangeBase, + WasmStorageIterCreateFromByte, + WasmStorageIterCreateToByte, + WasmStorageIterNextBase, + WasmStorageIterNextKeyByte, + WasmStorageIterNextValueByte, + WasmTouchingTrieNode, + WasmReadCachedTrieNode, + WasmPromiseAndBase, + WasmPromiseAndPerPromise, + WasmPromiseReturn, + WasmValidatorFrozenBase, + WasmValidatorTotalFrozenBase, + WasmValidatorPowerBase, + WasmValidatorTotalPowerBase, + WasmAltBn128G1MultiexpBase, + WasmAltBn128G1MultiexpElement, + WasmAltBn128PairingCheckBase, + WasmAltBn128PairingCheckElement, + WasmAltBn128G1SumBase, + WasmAltBn128G1SumElement, + + // Smart contract limits + MaxGasBurnt, + MaxGasBurntView, + MaxStackHeight, + ContractPrepareVersion, + InitialMemoryPages, + MaxMemoryPages, + RegistersMemoryLimit, + MaxRegisterSize, + MaxNumberRegisters, + MaxNumberLogs, + MaxTotalLogLength, + MaxTotalPrepaidGas, + MaxActionsPerReceipt, + MaxNumberBytesMethodNames, + MaxLengthMethodName, + MaxArgumentsLength, + MaxLengthReturnedData, + MaxContractSize, + MaxTransactionSize, + MaxLengthStorageKey, + MaxLengthStorageValue, + MaxPromisesPerFunctionCallAction, + MaxNumberInputDataDependencies, + MaxFunctionsNumberPerContract, + Wasmer2StackLimit, + MaxLocalsPerContract, + AccountIdValidityRulesVersion, + + // Contract runtime features + #[strum(serialize = "disable_9393_fix")] + Disable9393Fix, + FlatStorageReads, + ImplicitAccountCreation, + FixContractLoadingCost, + MathExtension, + Ed25519Verify, + AltBn128, + FunctionCallWeight, + VmKind, + EthImplicitAccounts, + + ActionRegisterRSA2048Keys, + ActionCreateRSA2048Challenge, +} + +#[derive( + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Debug, + strum::Display, + strum::EnumString, + strum::IntoStaticStr, +)] +#[strum(serialize_all = "snake_case")] +pub enum FeeParameter { + ActionReceiptCreation, + DataReceiptCreationBase, + DataReceiptCreationPerByte, + ActionCreateAccount, + ActionDeleteAccount, + ActionDeployContract, + ActionDeployContractPerByte, + ActionFunctionCall, + ActionFunctionCallPerByte, + ActionTransfer, + ActionStake, + ActionAddFullAccessKey, + ActionAddFunctionCallKey, + ActionAddFunctionCallKeyPerByte, + ActionDeleteKey, + ActionDelegate, + ActionRegisterRSA2048Keys, + ActionCreateRSA2048Challenge, +} + +impl Parameter { + /// Iterate through all parameters that define numerical limits for + /// contracts that are executed in the WASM VM. + pub fn vm_limits() -> slice::Iter<'static, Parameter> { + [ + Parameter::MaxGasBurnt, + Parameter::MaxStackHeight, + Parameter::ContractPrepareVersion, + Parameter::InitialMemoryPages, + Parameter::MaxMemoryPages, + Parameter::RegistersMemoryLimit, + Parameter::MaxRegisterSize, + Parameter::MaxNumberRegisters, + Parameter::MaxNumberLogs, + Parameter::MaxTotalLogLength, + Parameter::MaxTotalPrepaidGas, + Parameter::MaxActionsPerReceipt, + Parameter::MaxNumberBytesMethodNames, + Parameter::MaxLengthMethodName, + Parameter::MaxArgumentsLength, + Parameter::MaxLengthReturnedData, + Parameter::MaxContractSize, + Parameter::MaxTransactionSize, + Parameter::MaxLengthStorageKey, + Parameter::MaxLengthStorageValue, + Parameter::MaxPromisesPerFunctionCallAction, + Parameter::MaxNumberInputDataDependencies, + Parameter::MaxFunctionsNumberPerContract, + Parameter::Wasmer2StackLimit, + Parameter::MaxLocalsPerContract, + Parameter::AccountIdValidityRulesVersion, + ] + .iter() + } +} + +// TODO: consider renaming parameters to "action_{ActionCosts}" and deleting +// `FeeParameter` all together. +impl From for FeeParameter { + fn from(other: ActionCosts) -> Self { + match other { + ActionCosts::create_account => Self::ActionCreateAccount, + ActionCosts::delete_account => Self::ActionDeleteAccount, + ActionCosts::delegate => Self::ActionDelegate, + ActionCosts::deploy_contract_base => Self::ActionDeployContract, + ActionCosts::deploy_contract_byte => Self::ActionDeployContractPerByte, + ActionCosts::function_call_base => Self::ActionFunctionCall, + ActionCosts::function_call_byte => Self::ActionFunctionCallPerByte, + ActionCosts::transfer => Self::ActionTransfer, + ActionCosts::stake => Self::ActionStake, + ActionCosts::add_full_access_key => Self::ActionAddFullAccessKey, + ActionCosts::add_function_call_key_base => Self::ActionAddFunctionCallKey, + ActionCosts::add_function_call_key_byte => Self::ActionAddFunctionCallKeyPerByte, + ActionCosts::delete_key => Self::ActionDeleteKey, + ActionCosts::new_action_receipt => Self::ActionReceiptCreation, + ActionCosts::new_data_receipt_base => Self::DataReceiptCreationBase, + ActionCosts::new_data_receipt_byte => Self::DataReceiptCreationPerByte, + ActionCosts::register_rsa2048_keys => Self::ActionRegisterRSA2048Keys, + ActionCosts::create_rsa2048_challenge => Self::ActionCreateRSA2048Challenge, + } + } +} diff --git a/core/parameters/src/parameter_table.rs b/core/parameters/src/parameter_table.rs new file mode 100644 index 000000000..671bb3bc1 --- /dev/null +++ b/core/parameters/src/parameter_table.rs @@ -0,0 +1,828 @@ +use super::config::{AccountCreationConfig, RuntimeConfig}; +use crate::cost::{ + ActionCosts, ExtCostsConfig, Fee, ParameterCost, RuntimeFeesConfig, StorageUsageConfig, +}; +use crate::parameter::{FeeParameter, Parameter}; +use crate::vm::VMKind; +use crate::vm::{Config, StorageGetMode}; +use unc_primitives_core::account::id::ParseAccountError; +use unc_primitives_core::types::AccountId; +use num_rational::Rational32; +use std::collections::BTreeMap; + +/// Represents values supported by parameter config. +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq)] +#[serde(untagged)] +pub(crate) enum ParameterValue { + U64(u64), + Rational { numerator: i32, denominator: i32 }, + ParameterCost { gas: u64, compute: u64 }, + Fee { send_sir: u64, send_not_sir: u64, execution: u64 }, + // Can be used to store either a string or u128. Ideally, we would use a dedicated enum member + // for u128, but this is currently impossible to express in YAML (see + // `canonicalize_yaml_string`). + String(String), + Flag(bool), +} + +#[derive(thiserror::Error, Debug)] +pub(crate) enum ValueConversionError { + #[error("expected a value of type `{0}`, but could not parse it from `{1:?}`")] + ParseType(&'static str, ParameterValue), + + #[error("expected an integer of type `{1}` but could not parse it from `{2:?}`")] + ParseInt(#[source] std::num::ParseIntError, &'static str, ParameterValue), + + #[error("expected an integer of type `{1}` but could not parse it from `{2:?}`")] + TryFromInt(#[source] std::num::TryFromIntError, &'static str, ParameterValue), + + #[error("expected an account id, but could not parse it from `{1}`")] + ParseAccountId(#[source] ParseAccountError, String), + + #[error("expected a VM kind, but could not parse it from `{1}`")] + ParseVmKind(#[source] strum::ParseError, String), +} + +macro_rules! implement_conversion_to { + ($($ty: ty),*) => { + $(impl TryFrom<&ParameterValue> for $ty { + type Error = ValueConversionError; + fn try_from(value: &ParameterValue) -> Result { + match value { + ParameterValue::U64(v) => <$ty>::try_from(*v).map_err(|err| { + ValueConversionError::TryFromInt( + err.into(), + std::any::type_name::<$ty>(), + value.clone(), + ) + }), + _ => Err(ValueConversionError::ParseType( + std::any::type_name::<$ty>(), value.clone() + )), + } + } + })* + } +} + +implement_conversion_to!(u64, u32, u16, u8, i64, i32, i16, i8, usize, isize); + +impl TryFrom<&ParameterValue> for u128 { + type Error = ValueConversionError; + + fn try_from(value: &ParameterValue) -> Result { + match value { + ParameterValue::U64(v) => Ok(u128::from(*v)), + ParameterValue::String(s) => s.parse().map_err(|err| { + ValueConversionError::ParseInt(err, std::any::type_name::(), value.clone()) + }), + _ => Err(ValueConversionError::ParseType(std::any::type_name::(), value.clone())), + } + } +} + +impl TryFrom<&ParameterValue> for bool { + type Error = ValueConversionError; + + fn try_from(value: &ParameterValue) -> Result { + match value { + ParameterValue::Flag(b) => Ok(*b), + ParameterValue::String(s) => match &**s { + "true" => Ok(true), + "false" => Ok(false), + _ => Err(ValueConversionError::ParseType("bool", value.clone())), + }, + _ => Err(ValueConversionError::ParseType("bool", value.clone())), + } + } +} + +impl TryFrom<&ParameterValue> for Rational32 { + type Error = ValueConversionError; + + fn try_from(value: &ParameterValue) -> Result { + match value { + &ParameterValue::Rational { numerator, denominator } => { + Ok(Rational32::new(numerator, denominator)) + } + _ => Err(ValueConversionError::ParseType( + std::any::type_name::(), + value.clone(), + )), + } + } +} + +impl TryFrom<&ParameterValue> for ParameterCost { + type Error = ValueConversionError; + + fn try_from(value: &ParameterValue) -> Result { + match value { + ParameterValue::ParameterCost { gas, compute } => { + Ok(ParameterCost { gas: *gas, compute: *compute }) + } + // If not specified, compute costs default to gas costs. + &ParameterValue::U64(v) => Ok(ParameterCost { gas: v, compute: v }), + _ => Err(ValueConversionError::ParseType( + std::any::type_name::(), + value.clone(), + )), + } + } +} + +impl TryFrom<&ParameterValue> for Fee { + type Error = ValueConversionError; + + fn try_from(value: &ParameterValue) -> Result { + match value { + &ParameterValue::Fee { send_sir, send_not_sir, execution } => { + Ok(Fee { send_sir, send_not_sir, execution }) + } + _ => Err(ValueConversionError::ParseType(std::any::type_name::(), value.clone())), + } + } +} + +impl<'a> TryFrom<&'a ParameterValue> for &'a str { + type Error = ValueConversionError; + + fn try_from(value: &'a ParameterValue) -> Result { + match value { + ParameterValue::String(v) => Ok(v), + _ => { + Err(ValueConversionError::ParseType(std::any::type_name::(), value.clone())) + } + } + } +} + +impl TryFrom<&ParameterValue> for AccountId { + type Error = ValueConversionError; + + fn try_from(value: &ParameterValue) -> Result { + let value: &str = value.try_into()?; + value.parse().map_err(|err| ValueConversionError::ParseAccountId(err, value.to_string())) + } +} + +impl TryFrom<&ParameterValue> for VMKind { + type Error = ValueConversionError; + + fn try_from(value: &ParameterValue) -> Result { + match value { + ParameterValue::String(v) => v + .parse() + .map(|v: VMKind| v.replace_with_wasmtime_if_unsupported()) + .map_err(|e| ValueConversionError::ParseVmKind(e, value.to_string())), + _ => { + Err(ValueConversionError::ParseType(std::any::type_name::(), value.clone())) + } + } + } +} + +fn format_number(mut n: u64) -> String { + let mut parts = Vec::new(); + while n >= 1000 { + parts.push(format!("{:03?}", n % 1000)); + n /= 1000; + } + parts.push(n.to_string()); + parts.reverse(); + parts.join("_") +} + +impl core::fmt::Display for ParameterValue { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + ParameterValue::U64(v) => write!(f, "{:>20}", format_number(*v)), + ParameterValue::Rational { numerator, denominator } => { + write!(f, "{numerator} / {denominator}") + } + ParameterValue::ParameterCost { gas, compute } => { + write!(f, "{:>20}, compute: {:>20}", format_number(*gas), format_number(*compute)) + } + ParameterValue::Fee { send_sir, send_not_sir, execution } => { + write!( + f, + r#" +- send_sir: {:>20} +- send_not_sir: {:>20} +- execution: {:>20}"#, + format_number(*send_sir), + format_number(*send_not_sir), + format_number(*execution) + ) + } + ParameterValue::String(v) => write!(f, "{v}"), + ParameterValue::Flag(b) => write!(f, "{b:?}"), + } + } +} + +pub(crate) struct ParameterTable { + parameters: BTreeMap, +} + +/// Formats `ParameterTable` in human-readable format which is a subject to change and is not +/// intended to be parsed back. +impl core::fmt::Display for ParameterTable { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + for (key, value) in &self.parameters { + write!(f, "{key:40}{value}\n")? + } + Ok(()) + } +} + +/// Changes made to parameters between versions. +pub(crate) struct ParameterTableDiff { + parameters: BTreeMap, Option)>, +} + +/// Error returned by ParameterTable::from_str() that parses a runtime configuration YAML file. +#[derive(thiserror::Error, Debug)] +pub(crate) enum InvalidConfigError { + #[error("could not parse `{1}` as a parameter")] + UnknownParameter(#[source] strum::ParseError, String), + #[error("could not parse `{1}` as a value")] + ValueParseError(#[source] serde_yaml::Error, String), + #[error("could not parse YAML that defines the structure of the config")] + InvalidYaml(#[source] serde_yaml::Error), + #[error("config diff expected to contain old value `{1:?}` for parameter `{0}`")] + OldValueExists(Parameter, ParameterValue), + #[error( + "unexpected old value `{1:?}` for parameter `{0}` in config diff, previous version does not have such a value" + )] + NoOldValueExists(Parameter, ParameterValue), + #[error("expected old value `{1:?}` but found `{2:?}` for parameter `{0}` in config diff")] + WrongOldValue(Parameter, ParameterValue, ParameterValue), + #[error("expected a value for `{0}` but found none")] + MissingParameter(Parameter), + #[error("failed to convert a value for `{1}`")] + ValueConversionError(#[source] ValueConversionError, Parameter), +} + +impl std::str::FromStr for ParameterTable { + type Err = InvalidConfigError; + fn from_str(arg: &str) -> Result { + let yaml_map: BTreeMap = + serde_yaml::from_str(arg).map_err(|err| InvalidConfigError::InvalidYaml(err))?; + + let parameters = yaml_map + .iter() + .map(|(key, value)| { + let typed_key: Parameter = key + .parse() + .map_err(|err| InvalidConfigError::UnknownParameter(err, key.to_owned()))?; + Ok((typed_key, parse_parameter_value(value)?)) + }) + .collect::, _>>()?; + + Ok(ParameterTable { parameters }) + } +} + +impl TryFrom<&ParameterTable> for RuntimeConfig { + type Error = InvalidConfigError; + + fn try_from(params: &ParameterTable) -> Result { + Ok(RuntimeConfig { + fees: RuntimeFeesConfig { + action_fees: enum_map::enum_map! { + action_cost => params.get_fee(action_cost)? + }, + burnt_gas_reward: params.get(Parameter::BurntGasReward)?, + pessimistic_gas_price_inflation_ratio: params + .get(Parameter::PessimisticGasPriceInflation)?, + storage_usage_config: StorageUsageConfig { + storage_amount_per_byte: params.get(Parameter::StorageAmountPerByte)?, + num_bytes_account: params.get(Parameter::StorageNumBytesAccount)?, + num_extra_bytes_record: params.get(Parameter::StorageNumExtraBytesRecord)?, + }, + }, + wasm_config: Config { + ext_costs: ExtCostsConfig { + costs: enum_map::enum_map! { + cost => params.get(cost.param())? + }, + }, + vm_kind: params.get(Parameter::VmKind)?, + grow_mem_cost: params.get(Parameter::WasmGrowMemCost)?, + regular_op_cost: params.get(Parameter::WasmRegularOpCost)?, + disable_9393_fix: params.get(Parameter::Disable9393Fix)?, + limit_config: serde_yaml::from_value(params.yaml_map(Parameter::vm_limits())) + .map_err(InvalidConfigError::InvalidYaml)?, + fix_contract_loading_cost: params.get(Parameter::FixContractLoadingCost)?, + storage_get_mode: match params.get(Parameter::FlatStorageReads)? { + true => StorageGetMode::FlatStorage, + false => StorageGetMode::Trie, + }, + implicit_account_creation: params.get(Parameter::ImplicitAccountCreation)?, + math_extension: params.get(Parameter::MathExtension)?, + ed25519_verify: params.get(Parameter::Ed25519Verify)?, + alt_bn128: params.get(Parameter::AltBn128)?, + function_call_weight: params.get(Parameter::FunctionCallWeight)?, + eth_implicit_accounts: params.get(Parameter::EthImplicitAccounts)?, + }, + account_creation_config: AccountCreationConfig { + min_allowed_top_level_account_length: params + .get(Parameter::MinAllowedTopLevelAccountLength)?, + registrar_account_id: params.get(Parameter::RegistrarAccountId)?, + }, + }) + } +} + +impl ParameterTable { + pub(crate) fn apply_diff( + &mut self, + diff: ParameterTableDiff, + ) -> Result<(), InvalidConfigError> { + for (key, (before, after)) in diff.parameters { + let old_value = self.parameters.get(&key); + if old_value != before.as_ref() { + if old_value.is_none() { + return Err(InvalidConfigError::NoOldValueExists(key, before.unwrap())); + } + if before.is_none() { + return Err(InvalidConfigError::OldValueExists( + key, + old_value.unwrap().clone(), + )); + } + return Err(InvalidConfigError::WrongOldValue( + key, + old_value.unwrap().clone(), + before.unwrap(), + )); + } + + if let Some(new_value) = after { + self.parameters.insert(key, new_value); + } else { + self.parameters.remove(&key); + } + } + Ok(()) + } + + fn yaml_map(&self, params: impl Iterator) -> serde_yaml::Value { + // All parameter values can be serialized as YAML, so we don't ever expect this to fail. + serde_yaml::to_value( + params + .filter_map(|param| Some((param.to_string(), self.parameters.get(param)?))) + .collect::>(), + ) + .expect("failed to convert parameter values to YAML") + } + + /// Read and parse a typed parameter from the `ParameterTable`. + fn get<'a, T>(&'a self, key: Parameter) -> Result + where + T: TryFrom<&'a ParameterValue, Error = ValueConversionError>, + { + let value = self.parameters.get(&key).ok_or(InvalidConfigError::MissingParameter(key))?; + value.try_into().map_err(|err| InvalidConfigError::ValueConversionError(err, key)) + } + + /// Access action fee by `ActionCosts`. + fn get_fee(&self, cost: ActionCosts) -> Result { + let key: Parameter = format!("{}", FeeParameter::from(cost)).parse().unwrap(); + self.get(key) + } +} + +/// Represents values supported by parameter diff config. +#[derive(serde::Deserialize, Clone, Debug)] +struct ParameterDiffConfigValue { + old: Option, + new: Option, +} + +impl std::str::FromStr for ParameterTableDiff { + type Err = InvalidConfigError; + fn from_str(arg: &str) -> Result { + let yaml_map: BTreeMap = + serde_yaml::from_str(arg).map_err(|err| InvalidConfigError::InvalidYaml(err))?; + + let parameters = yaml_map + .iter() + .map(|(key, value)| { + let typed_key: Parameter = key + .parse() + .map_err(|err| InvalidConfigError::UnknownParameter(err, key.to_owned()))?; + + let old_value = + if let Some(s) = &value.old { Some(parse_parameter_value(s)?) } else { None }; + + let new_value = + if let Some(s) = &value.new { Some(parse_parameter_value(s)?) } else { None }; + + Ok((typed_key, (old_value, new_value))) + }) + .collect::, _>>()?; + Ok(ParameterTableDiff { parameters }) + } +} + +/// Parses a value from YAML to a more restricted type of parameter values. +fn parse_parameter_value(value: &serde_yaml::Value) -> Result { + Ok(serde_yaml::from_value(canonicalize_yaml_value(value)?) + .map_err(|err| InvalidConfigError::InvalidYaml(err))?) +} + +/// Recursively canonicalizes values inside of the YAML structure. +fn canonicalize_yaml_value( + value: &serde_yaml::Value, +) -> Result { + Ok(match value { + serde_yaml::Value::String(s) => canonicalize_yaml_string(s)?, + serde_yaml::Value::Mapping(m) => serde_yaml::Value::Mapping( + m.iter() + .map(|(key, value)| { + let canonical_value = canonicalize_yaml_value(value)?; + Ok((key.clone(), canonical_value)) + }) + .collect::>()?, + ), + _ => value.clone(), + }) +} + +/// Parses a value from the custom format for runtime parameter definitions. +/// +/// A value can be a positive integer or a string, with or without quotes. +/// Integers can use underlines as separators (for readability). +/// +/// The main purpose of this function is to add support for integers with underscore digit +/// separators which we use in the config but are not supported in YAML. +fn canonicalize_yaml_string(value: &str) -> Result { + if value.is_empty() { + return Ok(serde_yaml::Value::Null); + } + if value.bytes().all(|c| c.is_ascii_digit() || c == '_' as u8) { + let mut raw_number = value.to_owned(); + raw_number.retain(char::is_numeric); + // We do not have "arbitrary_precision" serde feature enabled, thus we + // can only store up to `u64::MAX`, which is `18446744073709551615` and + // has 20 characters. + if raw_number.len() < 20 { + serde_yaml::from_str(&raw_number) + .map_err(|err| InvalidConfigError::ValueParseError(err, value.to_owned())) + } else { + Ok(serde_yaml::Value::String(raw_number)) + } + } else { + Ok(serde_yaml::Value::String(value.to_owned())) + } +} + +#[cfg(test)] +mod tests { + use super::{ + parse_parameter_value, InvalidConfigError, ParameterTable, ParameterTableDiff, + ParameterValue, + }; + use crate::Parameter; + use assert_matches::assert_matches; + use std::collections::BTreeMap; + + #[track_caller] + fn check_parameter_table( + base_config: &str, + diffs: &[&str], + expected: impl IntoIterator, + ) { + let mut params: ParameterTable = base_config.parse().unwrap(); + for diff in diffs { + let diff: ParameterTableDiff = diff.parse().unwrap(); + params.apply_diff(diff).unwrap(); + } + + let expected_map = BTreeMap::from_iter(expected.into_iter().map(|(param, value)| { + (param, { + assert!(!value.is_empty(), "omit the parameter in the test instead"); + parse_parameter_value( + &serde_yaml::from_str(value).expect("Test data has invalid YAML"), + ) + .unwrap() + }) + })); + + assert_eq!(params.parameters, expected_map); + } + + #[track_caller] + fn check_invalid_parameter_table(base_config: &str, diffs: &[&str]) -> InvalidConfigError { + let params = base_config.parse(); + + let result = params.and_then(|params: ParameterTable| { + diffs.iter().try_fold(params, |mut params, diff| { + params.apply_diff(diff.parse()?)?; + Ok(params) + }) + }); + + match result { + Ok(_) => panic!("Input should have parser error"), + Err(err) => err, + } + } + + static BASE_0: &str = r#" +# Comment line +registrar_account_id: registrar +min_allowed_top_level_account_length: 0 +storage_amount_per_byte: 100_000_000_000_000_000_000 +storage_num_bytes_account: 100 +storage_num_extra_bytes_record: 40 +burnt_gas_reward: { + numerator: 1_000_000, + denominator: 300, +} +wasm_storage_read_base: { gas: 50_000_000_000, compute: 100_000_000_000 } +"#; + + static BASE_1: &str = r#" +registrar_account_id: registrar +# Comment line +min_allowed_top_level_account_length: 0 + +# Comment line with trailing whitespace # + +# Note the quotes here, they are necessary as otherwise the value can't be parsed by `serde_yaml` +# due to not fitting into u64 type. +storage_amount_per_byte: "100000000000000000000" +storage_num_bytes_account: 100 +storage_num_extra_bytes_record : 40 + +"#; + + static DIFF_0: &str = r#" +# Comment line +registrar_account_id: { old: "registrar", new: "near" } +min_allowed_top_level_account_length: { old: 32, new: 32_000 } +wasm_regular_op_cost: { new: 3_856_371 } +burnt_gas_reward: { + old: { numerator: 1_000_000, denominator: 300 }, + new: { numerator: 2_000_000, denominator: 500 }, +} +wasm_storage_read_base: { + old: { gas: 50_000_000_000, compute: 100_000_000_000 }, + new: { gas: 50_000_000_000, compute: 200_000_000_000 }, +} +"#; + + static DIFF_1: &str = r#" +# Comment line +registrar_account_id: { old: "near", new: "registrar" } +storage_num_extra_bytes_record: { old: 40, new: 77 } +wasm_regular_op_cost: { old: 3_856_371, new: 0 } +max_memory_pages: { new: 512 } +burnt_gas_reward: { + old: { numerator: 2_000_000, denominator: 500 }, + new: { numerator: 3_000_000, denominator: 800 }, +} +"#; + + // Tests synthetic small example configurations. For tests with "real" + // input data, we already have + // `test_old_and_new_runtime_config_format_match` in `configs_store.rs`. + + /// Check empty input + #[test] + fn test_empty_parameter_table() { + check_parameter_table("", &[], []); + } + + /// Reading reading a normally formatted base parameter file with no diffs + #[test] + fn test_basic_parameter_table() { + check_parameter_table( + BASE_0, + &[], + [ + (Parameter::RegistrarAccountId, "\"registrar\""), + (Parameter::MinAllowedTopLevelAccountLength, "32"), + (Parameter::StorageAmountPerByte, "\"100000000000000000000\""), + (Parameter::StorageNumBytesAccount, "100"), + (Parameter::StorageNumExtraBytesRecord, "40"), + (Parameter::BurntGasReward, "{ numerator: 1_000_000, denominator: 300 }"), + ( + Parameter::WasmStorageReadBase, + "{ gas: 50_000_000_000, compute: 100_000_000_000 }", + ), + ], + ); + } + + /// Reading reading a slightly funky formatted base parameter file with no diffs + #[test] + fn test_basic_parameter_table_weird_syntax() { + check_parameter_table( + BASE_1, + &[], + [ + (Parameter::RegistrarAccountId, "\"registrar\""), + (Parameter::MinAllowedTopLevelAccountLength, "32"), + (Parameter::StorageAmountPerByte, "\"100000000000000000000\""), + (Parameter::StorageNumBytesAccount, "100"), + (Parameter::StorageNumExtraBytesRecord, "40"), + ], + ); + } + + /// Apply one diff + #[test] + fn test_parameter_table_with_diff() { + check_parameter_table( + BASE_0, + &[DIFF_0], + [ + (Parameter::RegistrarAccountId, "\"near\""), + (Parameter::MinAllowedTopLevelAccountLength, "32000"), + (Parameter::StorageAmountPerByte, "\"100000000000000000000\""), + (Parameter::StorageNumBytesAccount, "100"), + (Parameter::StorageNumExtraBytesRecord, "40"), + (Parameter::WasmRegularOpCost, "3856371"), + (Parameter::BurntGasReward, "{ numerator: 2_000_000, denominator: 500 }"), + ( + Parameter::WasmStorageReadBase, + "{ gas: 50_000_000_000, compute: 200_000_000_000 }", + ), + ], + ); + } + + /// Apply two diffs + #[test] + fn test_parameter_table_with_diffs() { + check_parameter_table( + BASE_0, + &[DIFF_0, DIFF_1], + [ + (Parameter::RegistrarAccountId, "\"registrar\""), + (Parameter::MinAllowedTopLevelAccountLength, "32000"), + (Parameter::StorageAmountPerByte, "\"100000000000000000000\""), + (Parameter::StorageNumBytesAccount, "100"), + (Parameter::StorageNumExtraBytesRecord, "77"), + (Parameter::WasmRegularOpCost, "0"), + (Parameter::MaxMemoryPages, "512"), + (Parameter::BurntGasReward, "{ numerator: 3_000_000, denominator: 800 }"), + ( + Parameter::WasmStorageReadBase, + "{ gas: 50_000_000_000, compute: 200_000_000_000 }", + ), + ], + ); + } + + #[test] + fn test_parameter_table_with_empty_value() { + let diff_with_empty_value = "min_allowed_top_level_account_length: { old: 32 }"; + check_parameter_table( + BASE_0, + &[diff_with_empty_value], + [ + (Parameter::RegistrarAccountId, "\"registrar\""), + (Parameter::StorageAmountPerByte, "\"100000000000000000000\""), + (Parameter::StorageNumBytesAccount, "100"), + (Parameter::StorageNumExtraBytesRecord, "40"), + (Parameter::BurntGasReward, "{ numerator: 1_000_000, denominator: 300 }"), + ( + Parameter::WasmStorageReadBase, + "{ gas: 50_000_000_000, compute: 100_000_000_000 }", + ), + ], + ); + } + + #[test] + fn test_parameter_table_invalid_key() { + // Key that is not a `Parameter` + assert_matches!( + check_invalid_parameter_table("invalid_key: 100", &[]), + InvalidConfigError::UnknownParameter(_, _) + ); + } + + #[test] + fn test_parameter_table_invalid_key_in_diff() { + assert_matches!( + check_invalid_parameter_table( + "wasm_regular_op_cost: 100", + &["invalid_key: { new: 100 }"] + ), + InvalidConfigError::UnknownParameter(_, _) + ); + } + + #[test] + fn test_parameter_table_no_key() { + assert_matches!( + check_invalid_parameter_table(": 100", &[]), + InvalidConfigError::InvalidYaml(_) + ); + } + + #[test] + fn test_parameter_table_no_key_in_diff() { + assert_matches!( + check_invalid_parameter_table("wasm_regular_op_cost: 100", &[": 100"]), + InvalidConfigError::InvalidYaml(_) + ); + } + + #[test] + fn test_parameter_table_wrong_separator() { + assert_matches!( + check_invalid_parameter_table("wasm_regular_op_cost=100", &[]), + InvalidConfigError::InvalidYaml(_) + ); + } + + #[test] + fn test_parameter_table_wrong_separator_in_diff() { + assert_matches!( + check_invalid_parameter_table( + "wasm_regular_op_cost: 100", + &["wasm_regular_op_cost=100"] + ), + InvalidConfigError::InvalidYaml(_) + ); + } + + #[test] + fn test_parameter_table_wrong_old_value() { + assert_matches!( + check_invalid_parameter_table( + "min_allowed_top_level_account_length: 3_200_000_000", + &["min_allowed_top_level_account_length: { old: 3_200_000, new: 1_600_000 }"] + ), + InvalidConfigError::WrongOldValue( + Parameter::MinAllowedTopLevelAccountLength, + expected, + found + ) => { + assert_eq!(expected, ParameterValue::U64(3200000000)); + assert_eq!(found, ParameterValue::U64(3200000)); + } + ); + } + + #[test] + fn test_parameter_table_no_old_value() { + assert_matches!( + check_invalid_parameter_table( + "min_allowed_top_level_account_length: 3_200_000_000", + &["min_allowed_top_level_account_length: { new: 1_600_000 }"] + ), + InvalidConfigError::OldValueExists(Parameter::MinAllowedTopLevelAccountLength, expected) => { + assert_eq!(expected, ParameterValue::U64(3200000000)); + } + ); + } + + #[test] + fn test_parameter_table_old_parameter_undefined() { + assert_matches!( + check_invalid_parameter_table( + "min_allowed_top_level_account_length: 3_200_000_000", + &["wasm_regular_op_cost: { old: 3_200_000, new: 1_600_000 }"] + ), + InvalidConfigError::NoOldValueExists(Parameter::WasmRegularOpCost, found) => { + assert_eq!(found, ParameterValue::U64(3200000)); + } + ); + } + + #[test] + fn test_parameter_table_yaml_map() { + let params: ParameterTable = BASE_0.parse().unwrap(); + let yaml = params.yaml_map( + [ + Parameter::RegistrarAccountId, + Parameter::MinAllowedTopLevelAccountLength, + Parameter::StorageAmountPerByte, + Parameter::StorageNumBytesAccount, + Parameter::StorageNumExtraBytesRecord, + Parameter::BurntGasReward, + Parameter::WasmStorageReadBase, + ] + .iter(), + ); + assert_eq!( + yaml, + serde_yaml::to_value( + params + .parameters + .iter() + .map(|(key, value)| (key.to_string(), value)) + .collect::>() + ) + .unwrap() + ); + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__0.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__0.json.snap new file mode 100644 index 000000000..07d5d68cc --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__0.json.snap @@ -0,0 +1,216 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "100000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 4697339419375, + "send_not_sir": 4697339419375, + "execution": 4697339419375 + }, + "cost_per_byte": { + "send_sir": 59357464, + "send_not_sir": 59357464, + "execution": 59357464 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 3365369625000, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 3856371, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": false, + "math_extension": false, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 200000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 0, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__129.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__129.json.snap new file mode 100644 index 000000000..c3c915f3f --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__129.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "FlatStorage", + "fix_contract_loading_cost": true, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 262144, + "contract_prepare_version": 2, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 65, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__138.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__138.json.snap new file mode 100644 index 000000000..28e17518e --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__138.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "FlatStorage", + "fix_contract_loading_cost": true, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": true, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 262144, + "contract_prepare_version": 2, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 65, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__35.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__35.json.snap new file mode 100644 index 000000000..e7be60d9c --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__35.json.snap @@ -0,0 +1,216 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "100000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 4697339419375, + "send_not_sir": 4697339419375, + "execution": 4697339419375 + }, + "cost_per_byte": { + "send_sir": 59357464, + "send_not_sir": 59357464, + "execution": 59357464 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 3365369625000, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 3856371, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": false, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 200000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 0, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__42.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__42.json.snap new file mode 100644 index 000000000..f7fff9787 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__42.json.snap @@ -0,0 +1,216 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 4697339419375, + "send_not_sir": 4697339419375, + "execution": 4697339419375 + }, + "cost_per_byte": { + "send_sir": 59357464, + "send_not_sir": 59357464, + "execution": 59357464 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 3365369625000, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 3856371, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": false, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 200000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 0, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__46.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__46.json.snap new file mode 100644 index 000000000..da7b48168 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__46.json.snap @@ -0,0 +1,216 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 4697339419375, + "send_not_sir": 4697339419375, + "execution": 4697339419375 + }, + "cost_per_byte": { + "send_sir": 59357464, + "send_not_sir": 59357464, + "execution": 59357464 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 3365369625000, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 3856371, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 200000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 0, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__48.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__48.json.snap new file mode 100644 index 000000000..6e6d6f412 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__48.json.snap @@ -0,0 +1,216 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 2207874, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 200000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 0, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__49.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__49.json.snap new file mode 100644 index 000000000..d4b8cf7af --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__49.json.snap @@ -0,0 +1,217 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 200000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 0, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__50.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__50.json.snap new file mode 100644 index 000000000..9e8775491 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__50.json.snap @@ -0,0 +1,217 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 200000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 1, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__52.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__52.json.snap new file mode 100644 index 000000000..caf2fc799 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__52.json.snap @@ -0,0 +1,217 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 1, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__53.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__53.json.snap new file mode 100644 index 000000000..099a78b2e --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__53.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 1, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__55.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__55.json.snap new file mode 100644 index 000000000..67210a55a --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__55.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 1, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__57.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__57.json.snap new file mode 100644 index 000000000..91a12944a --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__57.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 1, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__59.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__59.json.snap new file mode 100644 index 000000000..ba1df6aef --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__59.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 1, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__61.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__61.json.snap new file mode 100644 index 000000000..29ae5fe9d --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__61.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "FlatStorage", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 1, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__62.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__62.json.snap new file mode 100644 index 000000000..5093fe2f3 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__62.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": true, + "storage_get_mode": "FlatStorage", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 262144, + "contract_prepare_version": 2, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__63.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__63.json.snap new file mode 100644 index 000000000..1843cfc90 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__63.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "FlatStorage", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 262144, + "contract_prepare_version": 2, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__64.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__64.json.snap new file mode 100644 index 000000000..487f4a6e3 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__64.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "FlatStorage", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 262144, + "contract_prepare_version": 2, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 65, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_0.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_0.json.snap new file mode 100644 index 000000000..07d5d68cc --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_0.json.snap @@ -0,0 +1,216 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "100000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 4697339419375, + "send_not_sir": 4697339419375, + "execution": 4697339419375 + }, + "cost_per_byte": { + "send_sir": 59357464, + "send_not_sir": 59357464, + "execution": 59357464 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 3365369625000, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 3856371, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": false, + "math_extension": false, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 200000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 0, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_129.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_129.json.snap new file mode 100644 index 000000000..c3c915f3f --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_129.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "FlatStorage", + "fix_contract_loading_cost": true, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 262144, + "contract_prepare_version": 2, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 65, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_138.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_138.json.snap new file mode 100644 index 000000000..28e17518e --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_138.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "FlatStorage", + "fix_contract_loading_cost": true, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": true, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 262144, + "contract_prepare_version": 2, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 65, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_35.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_35.json.snap new file mode 100644 index 000000000..e7be60d9c --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_35.json.snap @@ -0,0 +1,216 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "100000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 4697339419375, + "send_not_sir": 4697339419375, + "execution": 4697339419375 + }, + "cost_per_byte": { + "send_sir": 59357464, + "send_not_sir": 59357464, + "execution": 59357464 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 3365369625000, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 3856371, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": false, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 200000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 0, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_42.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_42.json.snap new file mode 100644 index 000000000..f7fff9787 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_42.json.snap @@ -0,0 +1,216 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 4697339419375, + "send_not_sir": 4697339419375, + "execution": 4697339419375 + }, + "cost_per_byte": { + "send_sir": 59357464, + "send_not_sir": 59357464, + "execution": 59357464 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 3365369625000, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 3856371, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": false, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 200000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 0, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_46.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_46.json.snap new file mode 100644 index 000000000..da7b48168 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_46.json.snap @@ -0,0 +1,216 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 4697339419375, + "send_not_sir": 4697339419375, + "execution": 4697339419375 + }, + "cost_per_byte": { + "send_sir": 59357464, + "send_not_sir": 59357464, + "execution": 59357464 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 3365369625000, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 3856371, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 200000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 0, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_48.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_48.json.snap new file mode 100644 index 000000000..6e6d6f412 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_48.json.snap @@ -0,0 +1,216 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 2207874, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 200000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 0, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_49.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_49.json.snap new file mode 100644 index 000000000..d4b8cf7af --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_49.json.snap @@ -0,0 +1,217 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 200000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 0, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_50.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_50.json.snap new file mode 100644 index 000000000..9e8775491 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_50.json.snap @@ -0,0 +1,217 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 200000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 1, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_52.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_52.json.snap new file mode 100644 index 000000000..caf2fc799 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_52.json.snap @@ -0,0 +1,217 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": false, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 1, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 102400, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_53.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_53.json.snap new file mode 100644 index 000000000..099a78b2e --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_53.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": false, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 1, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_55.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_55.json.snap new file mode 100644 index 000000000..67210a55a --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_55.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 1, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 0 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_57.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_57.json.snap new file mode 100644 index 000000000..91a12944a --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_57.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": false, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 1, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_59.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_59.json.snap new file mode 100644 index 000000000..ba1df6aef --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_59.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "Trie", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 1, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_61.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_61.json.snap new file mode 100644 index 000000000..29ae5fe9d --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_61.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "FlatStorage", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 16384, + "contract_prepare_version": 1, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_62.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_62.json.snap new file mode 100644 index 000000000..5093fe2f3 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_62.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": true, + "storage_get_mode": "FlatStorage", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 262144, + "contract_prepare_version": 2, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_63.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_63.json.snap new file mode 100644 index 000000000..1843cfc90 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_63.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "FlatStorage", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 262144, + "contract_prepare_version": 2, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_64.json.snap b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_64.json.snap new file mode 100644 index 000000000..487f4a6e3 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_64.json.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "FlatStorage", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 262144, + "contract_prepare_version": 2, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 65, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/snapshots/near_parameters__view__tests__runtime_config_view.snap b/core/parameters/src/snapshots/near_parameters__view__tests__runtime_config_view.snap new file mode 100644 index 000000000..c61f0a0a7 --- /dev/null +++ b/core/parameters/src/snapshots/near_parameters__view__tests__runtime_config_view.snap @@ -0,0 +1,218 @@ +--- +source: core/parameters/src/view.rs +expression: "&view" +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "FlatStorage", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 262144, + "contract_prepare_version": 2, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 65, + "registrar_account_id": "registrar" + } +} diff --git a/core/parameters/src/view.rs b/core/parameters/src/view.rs new file mode 100644 index 000000000..d3aa54eae --- /dev/null +++ b/core/parameters/src/view.rs @@ -0,0 +1,611 @@ +use crate::{ActionCosts, ExtCosts, Fee, ParameterCost}; +use unc_account_id::AccountId; +use unc_primitives_core::serialize::dec_format; +use unc_primitives_core::types::{Balance, Gas}; +use num_rational::Rational32; + +/// View that preserves JSON format of the runtime config. +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)] +pub struct RuntimeConfigView { + /// Amount of yN per byte required to have on the account. See + /// for details. + #[serde(with = "dec_format")] + pub storage_amount_per_byte: Balance, + /// Costs of different actions that need to be performed when sending and + /// processing transaction and receipts. + pub transaction_costs: RuntimeFeesConfigView, + /// Config of wasm operations. + pub wasm_config: VMConfigView, + /// Config that defines rules for account creation. + pub account_creation_config: AccountCreationConfigView, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)] +pub struct RuntimeFeesConfigView { + /// Describes the cost of creating an action receipt, `ActionReceipt`, excluding the actual cost + /// of actions. + /// - `send` cost is burned when a receipt is created using `promise_create` or + /// `promise_batch_create` + /// - `exec` cost is burned when the receipt is being executed. + pub action_receipt_creation_config: Fee, + /// Describes the cost of creating a data receipt, `DataReceipt`. + pub data_receipt_creation_config: DataReceiptCreationConfigView, + /// Describes the cost of creating a certain action, `Action`. Includes all variants. + pub action_creation_config: ActionCreationConfigView, + /// Describes fees for storage. + pub storage_usage_config: StorageUsageConfigView, + + /// Fraction of the burnt gas to reward to the contract account for execution. + pub burnt_gas_reward: Rational32, + + /// Pessimistic gas price inflation ratio. + pub pessimistic_gas_price_inflation_ratio: Rational32, +} + +/// The structure describes configuration for creation of new accounts. +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)] +pub struct AccountCreationConfigView { + /// The minimum length of the top-level account ID that is allowed to be created by any account. + pub min_allowed_top_level_account_length: u8, + /// The account ID of the account registrar. This account ID allowed to create top-level + /// accounts of any valid length. + pub registrar_account_id: AccountId, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Hash, PartialEq, Eq)] +pub struct DataReceiptCreationConfigView { + /// Base cost of creating a data receipt. + /// Both `send` and `exec` costs are burned when a new receipt has input dependencies. The gas + /// is charged for each input dependency. The dependencies are specified when a receipt is + /// created using `promise_then` and `promise_batch_then`. + /// NOTE: Any receipt with output dependencies will produce data receipts. Even if it fails. + /// Even if the last action is not a function call (in case of success it will return empty + /// value). + pub base_cost: Fee, + /// Additional cost per byte sent. + /// Both `send` and `exec` costs are burned when a function call finishes execution and returns + /// `N` bytes of data to every output dependency. For each output dependency the cost is + /// `(send(sir) + exec()) * N`. + pub cost_per_byte: Fee, +} + +/// Describes the cost of creating a specific action, `Action`. Includes all variants. +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Hash, PartialEq, Eq)] +pub struct ActionCreationConfigView { + /// Base cost of creating an account. + pub create_account_cost: Fee, + + /// Base cost of deploying a contract. + pub deploy_contract_cost: Fee, + /// Cost per byte of deploying a contract. + pub deploy_contract_cost_per_byte: Fee, + + /// Base cost of calling a function. + pub function_call_cost: Fee, + /// Cost per byte of method name and arguments of calling a function. + pub function_call_cost_per_byte: Fee, + + /// Base cost of making a transfer. + pub transfer_cost: Fee, + + /// Base cost of staking. + pub stake_cost: Fee, + + /// Base cost of adding a key. + pub add_key_cost: AccessKeyCreationConfigView, + + /// Base cost of deleting a key. + pub delete_key_cost: Fee, + + /// Base cost of deleting an account. + pub delete_account_cost: Fee, + + /// Base cost for processing a delegate action. + /// + /// This is on top of the costs for the actions inside the delegate action. + pub delegate_cost: Fee, +} + +/// Describes the cost of creating an access key. +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Hash, PartialEq, Eq)] +pub struct AccessKeyCreationConfigView { + /// Base cost of creating a full access access-key. + pub full_access_cost: Fee, + /// Base cost of creating an access-key restricted to specific functions. + pub function_call_cost: Fee, + /// Cost per byte of method_names of creating a restricted access-key. + pub function_call_cost_per_byte: Fee, +} + +/// Describes cost of storage per block +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Hash, PartialEq, Eq)] +pub struct StorageUsageConfigView { + /// Number of bytes for an account record, including rounding up for account id. + pub num_bytes_account: u64, + /// Additional number of bytes for a k/v record + pub num_extra_bytes_record: u64, +} + +impl From for RuntimeConfigView { + fn from(config: crate::RuntimeConfig) -> Self { + Self { + storage_amount_per_byte: config.storage_amount_per_byte(), + transaction_costs: RuntimeFeesConfigView { + action_receipt_creation_config: config + .fees + .fee(ActionCosts::new_action_receipt) + .clone(), + data_receipt_creation_config: DataReceiptCreationConfigView { + base_cost: config.fees.fee(ActionCosts::new_data_receipt_base).clone(), + cost_per_byte: config.fees.fee(ActionCosts::new_data_receipt_byte).clone(), + }, + action_creation_config: ActionCreationConfigView { + create_account_cost: config.fees.fee(ActionCosts::create_account).clone(), + deploy_contract_cost: config + .fees + .fee(ActionCosts::deploy_contract_base) + .clone(), + deploy_contract_cost_per_byte: config + .fees + .fee(ActionCosts::deploy_contract_byte) + .clone(), + function_call_cost: config.fees.fee(ActionCosts::function_call_base).clone(), + function_call_cost_per_byte: config + .fees + .fee(ActionCosts::function_call_byte) + .clone(), + transfer_cost: config.fees.fee(ActionCosts::transfer).clone(), + stake_cost: config.fees.fee(ActionCosts::stake).clone(), + add_key_cost: AccessKeyCreationConfigView { + full_access_cost: config.fees.fee(ActionCosts::add_full_access_key).clone(), + function_call_cost: config + .fees + .fee(ActionCosts::add_function_call_key_base) + .clone(), + function_call_cost_per_byte: config + .fees + .fee(ActionCosts::add_function_call_key_byte) + .clone(), + }, + delete_key_cost: config.fees.fee(ActionCosts::delete_key).clone(), + delete_account_cost: config.fees.fee(ActionCosts::delete_account).clone(), + delegate_cost: config.fees.fee(ActionCosts::delegate).clone(), + }, + storage_usage_config: StorageUsageConfigView { + num_bytes_account: config.fees.storage_usage_config.num_bytes_account, + num_extra_bytes_record: config.fees.storage_usage_config.num_extra_bytes_record, + }, + burnt_gas_reward: config.fees.burnt_gas_reward, + pessimistic_gas_price_inflation_ratio: config + .fees + .pessimistic_gas_price_inflation_ratio, + }, + wasm_config: VMConfigView::from(config.wasm_config), + account_creation_config: AccountCreationConfigView { + min_allowed_top_level_account_length: config + .account_creation_config + .min_allowed_top_level_account_length, + registrar_account_id: config.account_creation_config.registrar_account_id, + }, + } + } +} + +#[derive(Clone, Debug, Hash, serde::Serialize, serde::Deserialize, PartialEq, Eq)] +pub struct VMConfigView { + /// Costs for runtime externals + pub ext_costs: ExtCostsConfigView, + + /// Gas cost of a growing memory by single page. + pub grow_mem_cost: u32, + /// Gas cost of a regular operation. + pub regular_op_cost: u32, + + /// See [`VMConfig::vm_kind`]. + pub vm_kind: crate::vm::VMKind, + /// See [`VMConfig::disable_9393_fix`]. + pub disable_9393_fix: bool, + /// See [`VMConfig::flat_storage_reads`]. + pub storage_get_mode: crate::vm::StorageGetMode, + /// See [`VMConfig::fix_contract_loading_cost`]. + pub fix_contract_loading_cost: bool, + /// See [`VMConfig::implicit_account_creation`]. + pub implicit_account_creation: bool, + /// See [`VMConfig::math_extension`]. + pub math_extension: bool, + /// See [`VMConfig::ed25519_verify`]. + pub ed25519_verify: bool, + /// See [`VMConfig::alt_bn128`]. + pub alt_bn128: bool, + /// See [`VMConfig::function_call_weight`]. + pub function_call_weight: bool, + /// See [`VMConfig::eth_implicit_accounts`]. + pub eth_implicit_accounts: bool, + + /// Describes limits for VM and Runtime. + /// + /// TODO: Consider changing this to `VMLimitConfigView` to avoid dependency + /// on runtime. + pub limit_config: crate::vm::LimitConfig, +} + +impl From for VMConfigView { + fn from(config: crate::vm::Config) -> Self { + Self { + ext_costs: ExtCostsConfigView::from(config.ext_costs), + grow_mem_cost: config.grow_mem_cost, + regular_op_cost: config.regular_op_cost, + disable_9393_fix: config.disable_9393_fix, + limit_config: config.limit_config, + storage_get_mode: config.storage_get_mode, + fix_contract_loading_cost: config.fix_contract_loading_cost, + implicit_account_creation: config.implicit_account_creation, + math_extension: config.math_extension, + ed25519_verify: config.ed25519_verify, + alt_bn128: config.alt_bn128, + function_call_weight: config.function_call_weight, + vm_kind: config.vm_kind, + eth_implicit_accounts: config.eth_implicit_accounts, + } + } +} + +impl From for crate::vm::Config { + fn from(view: VMConfigView) -> Self { + Self { + ext_costs: crate::ExtCostsConfig::from(view.ext_costs), + grow_mem_cost: view.grow_mem_cost, + regular_op_cost: view.regular_op_cost, + disable_9393_fix: view.disable_9393_fix, + limit_config: view.limit_config, + storage_get_mode: view.storage_get_mode, + fix_contract_loading_cost: view.fix_contract_loading_cost, + implicit_account_creation: view.implicit_account_creation, + math_extension: view.math_extension, + ed25519_verify: view.ed25519_verify, + alt_bn128: view.alt_bn128, + function_call_weight: view.function_call_weight, + vm_kind: view.vm_kind, + eth_implicit_accounts: view.eth_implicit_accounts, + } + } +} + +/// Typed view of ExtCostsConfig to preserve JSON output field names in protocol +/// config RPC output. +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Hash, PartialEq, Eq)] +pub struct ExtCostsConfigView { + /// Base cost for calling a host function. + pub base: Gas, + + /// Base cost of loading a pre-compiled contract + pub contract_loading_base: Gas, + /// Cost per byte of loading a pre-compiled contract + pub contract_loading_bytes: Gas, + + /// Base cost for guest memory read + pub read_memory_base: Gas, + /// Cost for guest memory read + pub read_memory_byte: Gas, + + /// Base cost for guest memory write + pub write_memory_base: Gas, + /// Cost for guest memory write per byte + pub write_memory_byte: Gas, + + /// Base cost for reading from register + pub read_register_base: Gas, + /// Cost for reading byte from register + pub read_register_byte: Gas, + + /// Base cost for writing into register + pub write_register_base: Gas, + /// Cost for writing byte into register + pub write_register_byte: Gas, + + /// Base cost of decoding utf8. It's used for `log_utf8` and `panic_utf8`. + pub utf8_decoding_base: Gas, + /// Cost per byte of decoding utf8. It's used for `log_utf8` and `panic_utf8`. + pub utf8_decoding_byte: Gas, + + /// Base cost of decoding utf16. It's used for `log_utf16`. + pub utf16_decoding_base: Gas, + /// Cost per byte of decoding utf16. It's used for `log_utf16`. + pub utf16_decoding_byte: Gas, + + /// Cost of getting sha256 base + pub sha256_base: Gas, + /// Cost of getting sha256 per byte + pub sha256_byte: Gas, + + /// Cost of getting sha256 base + pub keccak256_base: Gas, + /// Cost of getting sha256 per byte + pub keccak256_byte: Gas, + + /// Cost of getting sha256 base + pub keccak512_base: Gas, + /// Cost of getting sha256 per byte + pub keccak512_byte: Gas, + + /// Cost of getting ripemd160 base + pub ripemd160_base: Gas, + /// Cost of getting ripemd160 per message block + pub ripemd160_block: Gas, + + /// Cost of getting ed25519 base + pub ed25519_verify_base: Gas, + /// Cost of getting ed25519 per byte + pub ed25519_verify_byte: Gas, + + /// Cost of calling ecrecover + pub ecrecover_base: Gas, + + /// Cost for calling logging. + pub log_base: Gas, + /// Cost for logging per byte + pub log_byte: Gas, + + // ############### + // # Storage API # + // ############### + /// Storage trie write key base cost + pub storage_write_base: Gas, + /// Storage trie write key per byte cost + pub storage_write_key_byte: Gas, + /// Storage trie write value per byte cost + pub storage_write_value_byte: Gas, + /// Storage trie write cost per byte of evicted value. + pub storage_write_evicted_byte: Gas, + + /// Storage trie read key base cost + pub storage_read_base: Gas, + /// Storage trie read key per byte cost + pub storage_read_key_byte: Gas, + /// Storage trie read value cost per byte cost + pub storage_read_value_byte: Gas, + + /// Remove key from trie base cost + pub storage_remove_base: Gas, + /// Remove key from trie per byte cost + pub storage_remove_key_byte: Gas, + /// Remove key from trie ret value byte cost + pub storage_remove_ret_value_byte: Gas, + + /// Storage trie check for key existence cost base + pub storage_has_key_base: Gas, + /// Storage trie check for key existence per key byte + pub storage_has_key_byte: Gas, + + /// Create trie prefix iterator cost base + pub storage_iter_create_prefix_base: Gas, + /// Create trie prefix iterator cost per byte. + pub storage_iter_create_prefix_byte: Gas, + + /// Create trie range iterator cost base + pub storage_iter_create_range_base: Gas, + /// Create trie range iterator cost per byte of from key. + pub storage_iter_create_from_byte: Gas, + /// Create trie range iterator cost per byte of to key. + pub storage_iter_create_to_byte: Gas, + + /// Trie iterator per key base cost + pub storage_iter_next_base: Gas, + /// Trie iterator next key byte cost + pub storage_iter_next_key_byte: Gas, + /// Trie iterator next key byte cost + pub storage_iter_next_value_byte: Gas, + + /// Cost per reading trie node from DB + pub touching_trie_node: Gas, + /// Cost for reading trie node from memory + pub read_cached_trie_node: Gas, + + // ############### + // # Promise API # + // ############### + /// Cost for calling `promise_and` + pub promise_and_base: Gas, + /// Cost for calling `promise_and` for each promise + pub promise_and_per_promise: Gas, + /// Cost for calling `promise_return` + pub promise_return: Gas, + + // ############### + // # Validator API # + // ############### + /// Cost of calling `validator_stake`. + pub validator_frozen_base: Gas, + pub validator_power_base: Gas, + /// Cost of calling `validator_total_stake`. + pub validator_total_frozen_base: Gas, + pub validator_total_power_base: Gas, + + // Removed parameters, only here for keeping the output backward-compatible. + pub contract_compile_base: Gas, + pub contract_compile_bytes: Gas, + + // ############# + // # Alt BN128 # + // ############# + /// Base cost for multiexp + pub alt_bn128_g1_multiexp_base: Gas, + /// Per element cost for multiexp + pub alt_bn128_g1_multiexp_element: Gas, + /// Base cost for sum + pub alt_bn128_g1_sum_base: Gas, + /// Per element cost for sum + pub alt_bn128_g1_sum_element: Gas, + /// Base cost for pairing check + pub alt_bn128_pairing_check_base: Gas, + /// Per element cost for pairing check + pub alt_bn128_pairing_check_element: Gas, +} + +impl From for ExtCostsConfigView { + fn from(config: crate::ExtCostsConfig) -> Self { + Self { + base: config.gas_cost(ExtCosts::base), + contract_loading_base: config.gas_cost(ExtCosts::contract_loading_base), + contract_loading_bytes: config.gas_cost(ExtCosts::contract_loading_bytes), + read_memory_base: config.gas_cost(ExtCosts::read_memory_base), + read_memory_byte: config.gas_cost(ExtCosts::read_memory_byte), + write_memory_base: config.gas_cost(ExtCosts::write_memory_base), + write_memory_byte: config.gas_cost(ExtCosts::write_memory_byte), + read_register_base: config.gas_cost(ExtCosts::read_register_base), + read_register_byte: config.gas_cost(ExtCosts::read_register_byte), + write_register_base: config.gas_cost(ExtCosts::write_register_base), + write_register_byte: config.gas_cost(ExtCosts::write_register_byte), + utf8_decoding_base: config.gas_cost(ExtCosts::utf8_decoding_base), + utf8_decoding_byte: config.gas_cost(ExtCosts::utf8_decoding_byte), + utf16_decoding_base: config.gas_cost(ExtCosts::utf16_decoding_base), + utf16_decoding_byte: config.gas_cost(ExtCosts::utf16_decoding_byte), + sha256_base: config.gas_cost(ExtCosts::sha256_base), + sha256_byte: config.gas_cost(ExtCosts::sha256_byte), + keccak256_base: config.gas_cost(ExtCosts::keccak256_base), + keccak256_byte: config.gas_cost(ExtCosts::keccak256_byte), + keccak512_base: config.gas_cost(ExtCosts::keccak512_base), + keccak512_byte: config.gas_cost(ExtCosts::keccak512_byte), + ripemd160_base: config.gas_cost(ExtCosts::ripemd160_base), + ripemd160_block: config.gas_cost(ExtCosts::ripemd160_block), + ed25519_verify_base: config.gas_cost(ExtCosts::ed25519_verify_base), + ed25519_verify_byte: config.gas_cost(ExtCosts::ed25519_verify_byte), + ecrecover_base: config.gas_cost(ExtCosts::ecrecover_base), + log_base: config.gas_cost(ExtCosts::log_base), + log_byte: config.gas_cost(ExtCosts::log_byte), + storage_write_base: config.gas_cost(ExtCosts::storage_write_base), + storage_write_key_byte: config.gas_cost(ExtCosts::storage_write_key_byte), + storage_write_value_byte: config.gas_cost(ExtCosts::storage_write_value_byte), + storage_write_evicted_byte: config.gas_cost(ExtCosts::storage_write_evicted_byte), + storage_read_base: config.gas_cost(ExtCosts::storage_read_base), + storage_read_key_byte: config.gas_cost(ExtCosts::storage_read_key_byte), + storage_read_value_byte: config.gas_cost(ExtCosts::storage_read_value_byte), + storage_remove_base: config.gas_cost(ExtCosts::storage_remove_base), + storage_remove_key_byte: config.gas_cost(ExtCosts::storage_remove_key_byte), + storage_remove_ret_value_byte: config.gas_cost(ExtCosts::storage_remove_ret_value_byte), + storage_has_key_base: config.gas_cost(ExtCosts::storage_has_key_base), + storage_has_key_byte: config.gas_cost(ExtCosts::storage_has_key_byte), + storage_iter_create_prefix_base: config + .gas_cost(ExtCosts::storage_iter_create_prefix_base), + storage_iter_create_prefix_byte: config + .gas_cost(ExtCosts::storage_iter_create_prefix_byte), + storage_iter_create_range_base: config + .gas_cost(ExtCosts::storage_iter_create_range_base), + storage_iter_create_from_byte: config.gas_cost(ExtCosts::storage_iter_create_from_byte), + storage_iter_create_to_byte: config.gas_cost(ExtCosts::storage_iter_create_to_byte), + storage_iter_next_base: config.gas_cost(ExtCosts::storage_iter_next_base), + storage_iter_next_key_byte: config.gas_cost(ExtCosts::storage_iter_next_key_byte), + storage_iter_next_value_byte: config.gas_cost(ExtCosts::storage_iter_next_value_byte), + touching_trie_node: config.gas_cost(ExtCosts::touching_trie_node), + read_cached_trie_node: config.gas_cost(ExtCosts::read_cached_trie_node), + promise_and_base: config.gas_cost(ExtCosts::promise_and_base), + promise_and_per_promise: config.gas_cost(ExtCosts::promise_and_per_promise), + promise_return: config.gas_cost(ExtCosts::promise_return), + validator_frozen_base: config.gas_cost(ExtCosts::validator_frozen_base), + validator_total_frozen_base: config.gas_cost(ExtCosts::validator_total_frozen_base), + validator_power_base: config.gas_cost(ExtCosts::validator_power_base), + validator_total_power_base: config.gas_cost(ExtCosts::validator_total_power_base), + alt_bn128_g1_multiexp_base: config.gas_cost(ExtCosts::alt_bn128_g1_multiexp_base), + alt_bn128_g1_multiexp_element: config.gas_cost(ExtCosts::alt_bn128_g1_multiexp_element), + alt_bn128_g1_sum_base: config.gas_cost(ExtCosts::alt_bn128_g1_sum_base), + alt_bn128_g1_sum_element: config.gas_cost(ExtCosts::alt_bn128_g1_sum_element), + alt_bn128_pairing_check_base: config.gas_cost(ExtCosts::alt_bn128_pairing_check_base), + alt_bn128_pairing_check_element: config + .gas_cost(ExtCosts::alt_bn128_pairing_check_element), + // removed parameters + contract_compile_base: 0, + contract_compile_bytes: 0, + } + } +} + +impl From for crate::ExtCostsConfig { + fn from(view: ExtCostsConfigView) -> Self { + let costs = enum_map::enum_map! { + ExtCosts::base => view.base, + ExtCosts::contract_loading_base => view.contract_loading_base, + ExtCosts::contract_loading_bytes => view.contract_loading_bytes, + ExtCosts::read_memory_base => view.read_memory_base, + ExtCosts::read_memory_byte => view.read_memory_byte, + ExtCosts::write_memory_base => view.write_memory_base, + ExtCosts::write_memory_byte => view.write_memory_byte, + ExtCosts::read_register_base => view.read_register_base, + ExtCosts::read_register_byte => view.read_register_byte, + ExtCosts::write_register_base => view.write_register_base, + ExtCosts::write_register_byte => view.write_register_byte, + ExtCosts::utf8_decoding_base => view.utf8_decoding_base, + ExtCosts::utf8_decoding_byte => view.utf8_decoding_byte, + ExtCosts::utf16_decoding_base => view.utf16_decoding_base, + ExtCosts::utf16_decoding_byte => view.utf16_decoding_byte, + ExtCosts::sha256_base => view.sha256_base, + ExtCosts::sha256_byte => view.sha256_byte, + ExtCosts::keccak256_base => view.keccak256_base, + ExtCosts::keccak256_byte => view.keccak256_byte, + ExtCosts::keccak512_base => view.keccak512_base, + ExtCosts::keccak512_byte => view.keccak512_byte, + ExtCosts::ripemd160_base => view.ripemd160_base, + ExtCosts::ripemd160_block => view.ripemd160_block, + ExtCosts::ed25519_verify_base => view.ed25519_verify_base, + ExtCosts::ed25519_verify_byte => view.ed25519_verify_byte, + ExtCosts::ecrecover_base => view.ecrecover_base, + ExtCosts::log_base => view.log_base, + ExtCosts::log_byte => view.log_byte, + ExtCosts::storage_write_base => view.storage_write_base, + ExtCosts::storage_write_key_byte => view.storage_write_key_byte, + ExtCosts::storage_write_value_byte => view.storage_write_value_byte, + ExtCosts::storage_write_evicted_byte => view.storage_write_evicted_byte, + ExtCosts::storage_read_base => view.storage_read_base, + ExtCosts::storage_read_key_byte => view.storage_read_key_byte, + ExtCosts::storage_read_value_byte => view.storage_read_value_byte, + ExtCosts::storage_remove_base => view.storage_remove_base, + ExtCosts::storage_remove_key_byte => view.storage_remove_key_byte, + ExtCosts::storage_remove_ret_value_byte => view.storage_remove_ret_value_byte, + ExtCosts::storage_has_key_base => view.storage_has_key_base, + ExtCosts::storage_has_key_byte => view.storage_has_key_byte, + ExtCosts::storage_iter_create_prefix_base => view.storage_iter_create_prefix_base, + ExtCosts::storage_iter_create_prefix_byte => view.storage_iter_create_prefix_byte, + ExtCosts::storage_iter_create_range_base => view.storage_iter_create_range_base, + ExtCosts::storage_iter_create_from_byte => view.storage_iter_create_from_byte, + ExtCosts::storage_iter_create_to_byte => view.storage_iter_create_to_byte, + ExtCosts::storage_iter_next_base => view.storage_iter_next_base, + ExtCosts::storage_iter_next_key_byte => view.storage_iter_next_key_byte, + ExtCosts::storage_iter_next_value_byte => view.storage_iter_next_value_byte, + ExtCosts::touching_trie_node => view.touching_trie_node, + ExtCosts::read_cached_trie_node => view.read_cached_trie_node, + ExtCosts::promise_and_base => view.promise_and_base, + ExtCosts::promise_and_per_promise => view.promise_and_per_promise, + ExtCosts::promise_return => view.promise_return, + ExtCosts::validator_frozen_base => view.validator_frozen_base, + ExtCosts::validator_total_frozen_base => view.validator_total_frozen_base, + ExtCosts::validator_power_base => view.validator_power_base, + ExtCosts::validator_total_power_base => view.validator_total_power_base, + ExtCosts::alt_bn128_g1_multiexp_base => view.alt_bn128_g1_multiexp_base, + ExtCosts::alt_bn128_g1_multiexp_element => view.alt_bn128_g1_multiexp_element, + ExtCosts::alt_bn128_g1_sum_base => view.alt_bn128_g1_sum_base, + ExtCosts::alt_bn128_g1_sum_element => view.alt_bn128_g1_sum_element, + ExtCosts::alt_bn128_pairing_check_base => view.alt_bn128_pairing_check_base, + ExtCosts::alt_bn128_pairing_check_element => view.alt_bn128_pairing_check_element, + } + .map(|_, value| ParameterCost { gas: value, compute: value }); + Self { costs } + } +} + +#[cfg(test)] +mod tests { + /// The JSON representation used in RPC responses must not remove or rename + /// fields, only adding fields is allowed or we risk breaking clients. + #[test] + #[cfg_attr(feature = "nightly", ignore)] + fn test_runtime_config_view() { + use crate::view::RuntimeConfigView; + use crate::RuntimeConfig; + use crate::RuntimeConfigStore; + use unc_primitives_core::version::PROTOCOL_VERSION; + + let config_store = RuntimeConfigStore::new(None); + let config = config_store.get_config(PROTOCOL_VERSION); + let view = RuntimeConfigView::from(RuntimeConfig::clone(config)); + insta::assert_json_snapshot!(&view, { ".wasm_config.vm_kind" => ""}); + } +} diff --git a/core/parameters/src/vm.rs b/core/parameters/src/vm.rs new file mode 100644 index 000000000..8786eec67 --- /dev/null +++ b/core/parameters/src/vm.rs @@ -0,0 +1,250 @@ +use crate::cost::{ExtCostsConfig, ParameterCost}; +use borsh::BorshSerialize; +use unc_primitives_core::config::AccountIdValidityRulesVersion; +use unc_primitives_core::types::Gas; +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; + +// NOTE that VMKind is part of serialization protocol, so we cannot remove entries from this list +// if particular VM reached publicly visible networks. +// +// Additionally, this is public only for the purposes of internal tools like the estimator. This +// API should otherwise be considered a private configuration of the `unc-vm-runner` +// crate. +#[derive( + Clone, + Copy, + Debug, + Hash, + BorshSerialize, + PartialEq, + Eq, + strum::EnumString, + serde::Serialize, + serde::Deserialize, +)] +#[cfg_attr(feature = "clap", derive(clap::ValueEnum))] +pub enum VMKind { + /// Wasmer 0.17.x VM. + Wasmer0, + /// Wasmtime VM. + Wasmtime, + /// Wasmer 2.x VM. + Wasmer2, + /// NearVM. + NearVm, +} + +impl VMKind { + pub fn replace_with_wasmtime_if_unsupported(self) -> Self { + if cfg!(not(target_arch = "x86_64")) { + Self::Wasmtime + } else { + self + } + } +} + +/// This enum represents if a storage_get call will be performed through flat storage or trie +#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] +pub enum StorageGetMode { + FlatStorage, + Trie, +} + +/// Describes limits for VM and Runtime. +/// TODO #4139: consider switching to strongly-typed wrappers instead of raw quantities +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Hash, PartialEq, Eq)] +pub struct LimitConfig { + /// Max amount of gas that can be used, excluding gas attached to promises. + pub max_gas_burnt: Gas, + + /// How tall the stack is allowed to grow? + /// + /// See to find out how the stack frame cost + /// is calculated. + pub max_stack_height: u32, + /// Whether a legacy version of stack limiting should be used, see + /// [`ContractPrepareVersion`]. + #[serde(default = "ContractPrepareVersion::v0")] + pub contract_prepare_version: ContractPrepareVersion, + + /// The initial number of memory pages. + /// NOTE: It's not a limiter itself, but it's a value we use for initial_memory_pages. + pub initial_memory_pages: u32, + /// What is the maximal memory pages amount is allowed to have for a contract. + pub max_memory_pages: u32, + + /// Limit of memory used by registers. + pub registers_memory_limit: u64, + /// Maximum number of bytes that can be stored in a single register. + pub max_register_size: u64, + /// Maximum number of registers that can be used simultaneously. + /// + /// Note that due to an implementation quirk [read: a bug] in VMLogic, if we + /// have this number of registers, no subsequent writes to the registers + /// will succeed even if they replace an existing register. + pub max_number_registers: u64, + + /// Maximum number of log entries. + pub max_number_logs: u64, + /// Maximum total length in bytes of all log messages. + pub max_total_log_length: u64, + + /// Max total prepaid gas for all function call actions per receipt. + pub max_total_prepaid_gas: Gas, + + /// Max number of actions per receipt. + pub max_actions_per_receipt: u64, + /// Max total length of all method names (including terminating character) for a function call + /// permission access key. + pub max_number_bytes_method_names: u64, + /// Max length of any method name (without terminating character). + pub max_length_method_name: u64, + /// Max length of arguments in a function call action. + pub max_arguments_length: u64, + /// Max length of returned data + pub max_length_returned_data: u64, + /// Max contract size + pub max_contract_size: u64, + /// Max transaction size + pub max_transaction_size: u64, + /// Max storage key size + pub max_length_storage_key: u64, + /// Max storage value size + pub max_length_storage_value: u64, + /// Max number of promises that a function call can create + pub max_promises_per_function_call_action: u64, + /// Max number of input data dependencies + pub max_number_input_data_dependencies: u64, + /// If present, stores max number of functions in one contract + #[serde(skip_serializing_if = "Option::is_none")] + pub max_functions_number_per_contract: Option, + /// If present, stores the secondary stack limit as implemented by wasmer2. + /// + /// This limit should never be hit normally. + #[serde(default = "wasmer2_stack_limit_default")] + pub wasmer2_stack_limit: i32, + /// If present, stores max number of locals declared globally in one contract + #[serde(skip_serializing_if = "Option::is_none")] + pub max_locals_per_contract: Option, + /// Whether to enforce account_id well-formedness where it wasn't enforced + /// historically. + #[serde(default = "AccountIdValidityRulesVersion::v0")] + pub account_id_validity_rules_version: AccountIdValidityRulesVersion, +} + +/// Dynamic configuration parameters required for the WASM runtime to +/// execute a smart contract. +/// +/// This (`VMConfig`) and `RuntimeFeesConfig` combined are sufficient to define +/// protocol specific behavior of the contract runtime. The former contains +/// configuration for the WASM runtime specifically, while the latter contains +/// configuration for the transaction runtime and WASM runtime. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct Config { + /// Costs for runtime externals + pub ext_costs: ExtCostsConfig, + + /// Gas cost of a growing memory by single page. + pub grow_mem_cost: u32, + + /// Gas cost of a regular operation. + pub regular_op_cost: u32, + + /// The kind of the VM implementation to use + pub vm_kind: VMKind, + + /// Disable the fix for the #9393 issue in unc-vm-runner. + pub disable_9393_fix: bool, + + /// Set to `StorageGetMode::FlatStorage` in order to enable the `FlatStorageReads` protocol + /// feature. + pub storage_get_mode: StorageGetMode, + + /// Enable the `FixContractLoadingCost` protocol feature. + pub fix_contract_loading_cost: bool, + + /// Enable the `ImplicitAccountCreation` protocol feature. + pub implicit_account_creation: bool, + + /// Enable the host functions added by the `MathExtension` protocol feature. + pub math_extension: bool, + + /// Enable the host functions added by the `Ed25519Verify` protocol feature. + pub ed25519_verify: bool, + + /// Enable the host functions added by the `AltBn128` protocol feature. + pub alt_bn128: bool, + + /// Enable the `FunctionCallWeight` protocol feature. + pub function_call_weight: bool, + + /// Enable the `EthImplicitAccounts` protocol feature. + pub eth_implicit_accounts: bool, + + /// Describes limits for VM and Runtime. + pub limit_config: LimitConfig, +} + +impl Config { + /// Computes non-cryptographically-proof hash. The computation is fast but not cryptographically + /// secure. + pub fn non_crypto_hash(&self) -> u64 { + let mut s = DefaultHasher::new(); + self.hash(&mut s); + s.finish() + } + + pub fn make_free(&mut self) { + self.ext_costs = ExtCostsConfig { + costs: unc_primitives_core::enum_map::enum_map! { + _ => ParameterCost { gas: 0, compute: 0 } + }, + }; + self.grow_mem_cost = 0; + self.regular_op_cost = 0; + self.limit_config.max_gas_burnt = u64::MAX; + } +} + +fn wasmer2_stack_limit_default() -> i32 { + 100 * 1024 +} + +/// Our original code for limiting WASM stack was buggy. We fixed that, but we +/// still have to use old (`V0`) limiter for old protocol versions. +/// +/// This struct here exists to enforce that the value in the config is either +/// `0` or `1`. We could have used a `bool` instead, but there's a chance that +/// our current impl isn't perfect either and would need further tweaks in the +/// future. +#[derive( + Debug, + Clone, + Copy, + Hash, + PartialEq, + Eq, + serde_repr::Serialize_repr, + serde_repr::Deserialize_repr, +)] +#[repr(u8)] +pub enum ContractPrepareVersion { + /// Oldest, buggiest version. + /// + /// Don't use it unless specifically to support old protocol version. + V0, + /// Old, slow and buggy version. + /// + /// Better than V0, but don’t use this nevertheless. + V1, + /// finite-wasm 0.3.0 based contract preparation code. + V2, +} + +impl ContractPrepareVersion { + pub fn v0() -> ContractPrepareVersion { + ContractPrepareVersion::V0 + } +} diff --git a/core/primitives-core/Cargo.toml b/core/primitives-core/Cargo.toml new file mode 100644 index 000000000..6981c6ed8 --- /dev/null +++ b/core/primitives-core/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "unc-primitives-core" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "This crate provides the core set of primitives used by other framework crates including unc-primitives" +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +arbitrary.workspace = true +base64.workspace = true +borsh.workspace = true +bs58.workspace = true +derive_more.workspace = true +enum-map.workspace = true +num-rational.workspace = true +serde.workspace = true +serde_repr.workspace = true +serde_with.workspace = true +sha2.workspace = true +strum.workspace = true +thiserror.workspace = true + +unc-account-id.workspace = true + +[dev-dependencies] +serde_json.workspace = true +insta.workspace = true + +[features] +default = [] +protocol_feature_fix_staking_threshold = [] +protocol_feature_fix_contract_loading_cost = [] +protocol_feature_reject_blocks_with_outdated_protocol_version = [] + +nightly = [ + "nightly_protocol", + "protocol_feature_fix_contract_loading_cost", + "protocol_feature_fix_staking_threshold", + "protocol_feature_reject_blocks_with_outdated_protocol_version", +] + +nightly_protocol = [ +] diff --git a/core/primitives-core/src/account.rs b/core/primitives-core/src/account.rs new file mode 100644 index 000000000..77a5eccfd --- /dev/null +++ b/core/primitives-core/src/account.rs @@ -0,0 +1,284 @@ +use crate::hash::CryptoHash; +use crate::serialize::dec_format; +use crate::types::{Balance, Nonce, StorageUsage, Power}; +use borsh::{BorshDeserialize, BorshSerialize}; +pub use unc_account_id as id; +use std::io; + +#[derive( + BorshSerialize, + BorshDeserialize, + PartialEq, + Eq, + Clone, + Copy, + Debug, + Default, + serde::Serialize, + serde::Deserialize, +)] +pub enum AccountVersion { + #[default] + V1, +} + +/// Per account information stored in the state. +#[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Debug, Clone)] +pub struct Account { + /// The total not locked tokens. + #[serde(with = "dec_format")] + amount: Balance, + /// The amount locked due to staking. + #[serde(with = "dec_format")] + locked: Balance, + /// + #[serde(with = "dec_format")] + power: Power, + /// Hash of the code stored in the storage for this account. + code_hash: CryptoHash, + /// Storage used by the given account, includes account id, this struct, access keys and other data. + storage_usage: StorageUsage, + /// Version of Account in re migrations and similar + #[serde(default)] + version: AccountVersion, +} + +impl Account { + /// Max number of bytes an account can have in its state (excluding contract code) + /// before it is infeasible to delete. + pub const MAX_ACCOUNT_DELETION_STORAGE_USAGE: u64 = 10_000; + + pub fn new( + amount: Balance, + locked: Balance, + power: Power, + code_hash: CryptoHash, + storage_usage: StorageUsage, + ) -> Self { + Account { amount, locked, power, code_hash, storage_usage, version: AccountVersion::V1 } + } + + #[inline] + pub fn amount(&self) -> Balance { + self.amount + } + + #[inline] + pub fn locked(&self) -> Balance { + self.locked + } + + #[inline] + pub fn power(&self) -> Power { self.power } + + #[inline] + pub fn code_hash(&self) -> CryptoHash { + self.code_hash + } + + #[inline] + pub fn storage_usage(&self) -> StorageUsage { + self.storage_usage + } + + #[inline] + pub fn version(&self) -> AccountVersion { + self.version + } + + #[inline] + pub fn set_amount(&mut self, amount: Balance) { + self.amount = amount; + } + + #[inline] + pub fn set_power(&mut self, power: Power) { self.power = power; } + + #[inline] + pub fn set_locked(&mut self, locked: Balance) { + self.locked = locked; + } + + #[inline] + pub fn set_code_hash(&mut self, code_hash: CryptoHash) { + self.code_hash = code_hash; + } + + #[inline] + pub fn set_storage_usage(&mut self, storage_usage: StorageUsage) { + self.storage_usage = storage_usage; + } + + pub fn set_version(&mut self, version: AccountVersion) { + self.version = version; + } +} + +#[derive(BorshSerialize, BorshDeserialize)] +struct LegacyAccount { + amount: Balance, + locked: Balance, + power: Power, + code_hash: CryptoHash, + storage_usage: StorageUsage, +} + +impl BorshDeserialize for Account { + fn deserialize_reader(rd: &mut R) -> io::Result { + // This should only ever happen if we have pre-transition account serialized in state + // See test_account_size + let deserialized_account = LegacyAccount::deserialize_reader(rd)?; + Ok(Account { + amount: deserialized_account.amount, + locked: deserialized_account.locked, + power: deserialized_account.power, + code_hash: deserialized_account.code_hash, + storage_usage: deserialized_account.storage_usage, + version: AccountVersion::V1, + }) + } +} + +impl BorshSerialize for Account { + fn serialize(&self, writer: &mut W) -> io::Result<()> { + match self.version { + AccountVersion::V1 => LegacyAccount { + amount: self.amount, + locked: self.locked, + power: self.power, + code_hash: self.code_hash, + storage_usage: self.storage_usage, + } + .serialize(writer), + } + } +} + +/// Access key provides limited access to an account. Each access key belongs to some account and +/// is identified by a unique (within the account) public key. One account may have large number of +/// access keys. Access keys allow to act on behalf of the account by restricting transactions +/// that can be issued. +/// `account_id,public_key` is a key in the state +#[derive( + BorshSerialize, + BorshDeserialize, + PartialEq, + Eq, + Hash, + Clone, + Debug, + serde::Serialize, + serde::Deserialize, +)] +pub struct AccessKey { + /// Nonce for this access key, used for tx nonce generation. When access key is created, nonce + /// is set to `(block_height - 1) * 1e6` to avoid tx hash collision on access key re-creation. + /// See for more details. + pub nonce: Nonce, + + /// Defines permissions for this access key. + pub permission: AccessKeyPermission, +} + +impl AccessKey { + pub const ACCESS_KEY_NONCE_RANGE_MULTIPLIER: u64 = 1_000_000; + + pub fn full_access() -> Self { + Self { nonce: 0, permission: AccessKeyPermission::FullAccess } + } +} + +/// Defines permissions for AccessKey +#[derive( + BorshSerialize, + BorshDeserialize, + PartialEq, + Eq, + Hash, + Clone, + Debug, + serde::Serialize, + serde::Deserialize, +)] +pub enum AccessKeyPermission { + FunctionCall(FunctionCallPermission), + + /// Grants full access to the account. + /// NOTE: It's used to replace account-level public keys. + FullAccess, +} + +/// Grants limited permission to make transactions with FunctionCallActions +/// The permission can limit the allowed balance to be spent on the prepaid gas. +/// It also restrict the account ID of the receiver for this function call. +/// It also can restrict the method name for the allowed function calls. +#[derive( + BorshSerialize, + BorshDeserialize, + serde::Serialize, + serde::Deserialize, + PartialEq, + Eq, + Hash, + Clone, + Debug, +)] +pub struct FunctionCallPermission { + /// Allowance is a balance limit to use by this access key to pay for function call gas and + /// transaction fees. When this access key is used, both account balance and the allowance is + /// decreased by the same value. + /// `None` means unlimited allowance. + /// NOTE: To change or increase the allowance, the old access key needs to be deleted and a new + /// access key should be created. + #[serde(with = "dec_format")] + pub allowance: Option, + + // This isn't an AccountId because already existing records in testnet genesis have invalid + // values for this field (see: https://github.com/utnet-org/utility/pull/4621#issuecomment-892099860) + // we accomodate those by using a string, allowing us to read and parse genesis. + /// The access key only allows transactions with the given receiver's account id. + pub receiver_id: String, + + /// A list of method names that can be used. The access key only allows transactions with the + /// function call of one of the given method names. + /// Empty list means any method name can be used. + pub method_names: Vec, +} + +#[cfg(test)] +mod tests { + + use crate::hash::hash; + + use super::*; + + #[test] + fn test_account_serialization() { + let acc = Account::new(1_000_000, 1_000_000, 5, CryptoHash::default(), 100); + let bytes = borsh::to_vec(&acc).unwrap(); + assert_eq!(hash(&bytes).to_string(), "EVk5UaxBe8LQ8r8iD5EAxVBs6TJcMDKqyH7PBuho6bBJ"); + } + + #[test] + fn test_account_deserialization() { + let old_account = LegacyAccount { + amount: 100, + locked: 200, + power: 5, + code_hash: CryptoHash::default(), + storage_usage: 300, + }; + let mut old_bytes = &borsh::to_vec(&old_account).unwrap()[..]; + let new_account = ::deserialize(&mut old_bytes).unwrap(); + assert_eq!(new_account.amount, old_account.amount); + assert_eq!(new_account.locked, old_account.locked); + assert_eq!(new_account.power, old_account.power); + assert_eq!(new_account.code_hash, old_account.code_hash); + assert_eq!(new_account.storage_usage, old_account.storage_usage); + assert_eq!(new_account.version, AccountVersion::V1); + let mut new_bytes = &borsh::to_vec(&new_account).unwrap()[..]; + let deserialized_account = + ::deserialize(&mut new_bytes).unwrap(); + assert_eq!(deserialized_account, new_account); + } +} diff --git a/core/primitives-core/src/chains.rs b/core/primitives-core/src/chains.rs new file mode 100644 index 000000000..17c034401 --- /dev/null +++ b/core/primitives-core/src/chains.rs @@ -0,0 +1,7 @@ +/// Chain IDs of commonly used environment. + +/// Main production environment. +pub const MAINNET: &str = "mainnet"; + +/// Primary testing environment. +pub const TESTNET: &str = "testnet"; diff --git a/core/primitives-core/src/config.rs b/core/primitives-core/src/config.rs new file mode 100644 index 000000000..70b3dd8c4 --- /dev/null +++ b/core/primitives-core/src/config.rs @@ -0,0 +1,33 @@ +use crate::types::Gas; +use std::hash::Hash; + +#[derive( + Debug, + Clone, + Copy, + Hash, + PartialEq, + Eq, + serde_repr::Serialize_repr, + serde_repr::Deserialize_repr, +)] +#[repr(u8)] +pub enum AccountIdValidityRulesVersion { + /// Skip account ID validation according to legacy rules. + V0, + /// Limit `receiver_id` in `FunctionCallPermission` to be a valid account ID. + V1, +} + +impl AccountIdValidityRulesVersion { + pub fn v0() -> AccountIdValidityRulesVersion { + AccountIdValidityRulesVersion::V0 + } +} + +/// Configuration of view methods execution, during which no costs should be charged. +#[derive(Default, Clone, serde::Serialize, serde::Deserialize, Debug, Hash, PartialEq, Eq)] +pub struct ViewConfig { + /// If specified, defines max burnt gas per view method. + pub max_gas_burnt: Gas, +} diff --git a/core/primitives-core/src/hash.rs b/core/primitives-core/src/hash.rs new file mode 100644 index 000000000..403a3737e --- /dev/null +++ b/core/primitives-core/src/hash.rs @@ -0,0 +1,347 @@ +use borsh::BorshSerialize; +use serde::{Deserializer, Serializer}; +use sha2::Digest; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::io::Write; + +#[derive( + Copy, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + derive_more::AsRef, + derive_more::AsMut, + arbitrary::Arbitrary, + borsh::BorshDeserialize, + borsh::BorshSerialize, +)] +#[as_ref(forward)] +#[as_mut(forward)] +pub struct CryptoHash(pub [u8; 32]); + +impl CryptoHash { + pub const LENGTH: usize = 32; + + pub const fn new() -> Self { + Self([0; Self::LENGTH]) + } + + /// Calculates hash of given bytes. + pub fn hash_bytes(bytes: &[u8]) -> CryptoHash { + CryptoHash(sha2::Sha256::digest(bytes).into()) + } + + /// Calculates hash of borsh-serialised representation of an object. + /// + /// Note that using this function with an array may lead to unexpected + /// results. For example, `CryptoHash::hash_borsh(&[1u32, 2, 3])` hashes + /// a representation of a `[u32; 3]` array rather than a slice. It may be + /// cleaner to use [`Self::hash_borsh_iter`] instead. + pub fn hash_borsh(value: T) -> CryptoHash { + let mut hasher = sha2::Sha256::default(); + value.serialize(&mut hasher).unwrap(); + CryptoHash(hasher.finalize().into()) + } + + /// Calculates hash of a borsh-serialised representation of list of objects. + /// + /// This behaves as if it first collected all the items in the iterator into + /// a vector and then calculating hash of borsh-serialised representation of + /// that vector. + /// + /// Panics if the iterator lies about its length. + pub fn hash_borsh_iter(values: I) -> CryptoHash + where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + I::Item: BorshSerialize, + { + let iter = values.into_iter(); + let n = u32::try_from(iter.len()).unwrap(); + let mut hasher = sha2::Sha256::default(); + hasher.write_all(&n.to_le_bytes()).unwrap(); + let count = + iter.inspect(|value| BorshSerialize::serialize(&value, &mut hasher).unwrap()).count(); + assert_eq!(n as usize, count); + CryptoHash(hasher.finalize().into()) + } + + pub const fn as_bytes(&self) -> &[u8; Self::LENGTH] { + &self.0 + } + + /// Converts hash into base58-encoded string and passes it to given visitor. + /// + /// The conversion is performed without any memory allocation. The visitor + /// is given a reference to a string stored on stack. Returns whatever the + /// visitor returns. + fn to_base58_impl(self, visitor: impl FnOnce(&str) -> Out) -> Out { + // base58-encoded string is at most 1.4 times longer than the binary + // sequence. We’re serialising 32 bytes so ⌈32 * 1.4⌉ = 45 should be + // enough. + let mut buffer = [0u8; 45]; + let len = bs58::encode(self).into(&mut buffer[..]).unwrap(); + let value = std::str::from_utf8(&buffer[..len]).unwrap(); + visitor(value) + } + + /// Decodes base58-encoded string into a 32-byte hash. + /// + /// Returns one of three results: success with the decoded CryptoHash, + /// invalid length error indicating that the encoded value was too short or + /// too long or other decoding error (e.g. invalid character). + fn from_base58_impl(encoded: &str) -> Decode58Result { + let mut result = Self::new(); + match bs58::decode(encoded).into(&mut result.0) { + Ok(len) if len == result.0.len() => Decode58Result::Ok(result), + Ok(_) | Err(bs58::decode::Error::BufferTooSmall) => Decode58Result::BadLength, + Err(err) => Decode58Result::Err(err), + } + } +} + +/// Result of decoding base58-encoded crypto hash. +enum Decode58Result { + /// Decoding succeeded. + Ok(CryptoHash), + /// The decoded data has incorrect length; either too short or too long. + BadLength, + /// There have been other decoding errors; e.g. an invalid character in the + /// input buffer. + Err(bs58::decode::Error), +} + +impl Default for CryptoHash { + fn default() -> Self { + Self::new() + } +} + +impl serde::Serialize for CryptoHash { + fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> + where + S: Serializer, + { + self.to_base58_impl(|encoded| serializer.serialize_str(encoded)) + } +} + +/// Serde visitor for [`CryptoHash`]. +/// +/// The visitor expects a string which is then base58-decoded into a crypto +/// hash. +struct Visitor; + +impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = CryptoHash; + + fn expecting(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmt.write_str("base58-encoded 256-bit hash") + } + + fn visit_str(self, s: &str) -> Result { + match CryptoHash::from_base58_impl(s) { + Decode58Result::Ok(result) => Ok(result), + Decode58Result::BadLength => Err(E::invalid_length(s.len(), &self)), + Decode58Result::Err(err) => Err(E::custom(err)), + } + } +} + +impl<'de> serde::Deserialize<'de> for CryptoHash { + fn deserialize(deserializer: D) -> Result>::Error> + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(Visitor) + } +} + +impl std::str::FromStr for CryptoHash { + type Err = Box; + + /// Decodes base58-encoded string into a 32-byte crypto hash. + fn from_str(encoded: &str) -> Result { + match Self::from_base58_impl(encoded) { + Decode58Result::Ok(result) => Ok(result), + Decode58Result::BadLength => Err("incorrect length for hash".into()), + Decode58Result::Err(err) => Err(err.into()), + } + } +} + +impl TryFrom<&[u8]> for CryptoHash { + type Error = Box; + + fn try_from(bytes: &[u8]) -> Result { + Ok(CryptoHash(bytes.try_into()?)) + } +} + +impl From for Vec { + fn from(hash: CryptoHash) -> Vec { + hash.0.to_vec() + } +} + +impl From<&CryptoHash> for Vec { + fn from(hash: &CryptoHash) -> Vec { + hash.0.to_vec() + } +} + +impl From for [u8; CryptoHash::LENGTH] { + fn from(hash: CryptoHash) -> [u8; CryptoHash::LENGTH] { + hash.0 + } +} + +impl fmt::Debug for CryptoHash { + fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, fmtr) + } +} + +impl fmt::Display for CryptoHash { + fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result { + self.to_base58_impl(|encoded| fmtr.write_str(encoded)) + } +} + +// This implementation is compatible with derived PartialEq. +// Custom PartialEq implementation was explicitly removed in #4220. +impl Hash for CryptoHash { + fn hash(&self, state: &mut H) { + state.write(self.as_ref()); + } +} + +/// Calculates a hash of a bytes slice. +/// +/// # Examples +/// +/// The example below calculates the hash of the indicated data. +/// +/// ``` +/// let data = [1, 2, 3]; +/// let hash = unc_primitives_core::hash::hash(&data); +/// ``` +pub fn hash(data: &[u8]) -> CryptoHash { + CryptoHash::hash_bytes(data) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::str::FromStr; + + #[derive(serde::Deserialize, serde::Serialize)] + struct Struct { + hash: CryptoHash, + } + + #[test] + fn test_hash_borsh() { + fn value(want: &str, value: T) { + assert_eq!(want, CryptoHash::hash_borsh(&value).to_string()); + } + + fn slice(want: &str, slice: &[T]) { + assert_eq!(want, CryptoHash::hash_borsh(slice).to_string()); + iter(want, slice.iter()); + iter(want, slice); + } + + fn iter(want: &str, iter: I) + where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + I::Item: BorshSerialize, + { + assert_eq!(want, CryptoHash::hash_borsh_iter(iter).to_string()); + } + + value("CuoNgQBWsXnTqup6FY3UXNz6RRufnYyQVxx8HKZLUaRt", "foo"); + value("CuoNgQBWsXnTqup6FY3UXNz6RRufnYyQVxx8HKZLUaRt", "foo".as_bytes()); + value("CuoNgQBWsXnTqup6FY3UXNz6RRufnYyQVxx8HKZLUaRt", &b"foo"[..]); + value("CuoNgQBWsXnTqup6FY3UXNz6RRufnYyQVxx8HKZLUaRt", [3, 0, 0, 0, b'f', b'o', b'o']); + slice("CuoNgQBWsXnTqup6FY3UXNz6RRufnYyQVxx8HKZLUaRt", "foo".as_bytes()); + iter( + "CuoNgQBWsXnTqup6FY3UXNz6RRufnYyQVxx8HKZLUaRt", + "FOO".bytes().map(|ch| ch.to_ascii_lowercase()), + ); + + value("3yMApqCuCjXDWPrbjfR5mjCPTHqFG8Pux1TxQrEM35jj", b"foo"); + value("3yMApqCuCjXDWPrbjfR5mjCPTHqFG8Pux1TxQrEM35jj", [b'f', b'o', b'o']); + value("3yMApqCuCjXDWPrbjfR5mjCPTHqFG8Pux1TxQrEM35jj", [b'f', b'o', b'o']); + slice("CuoNgQBWsXnTqup6FY3UXNz6RRufnYyQVxx8HKZLUaRt", &[b'f', b'o', b'o']); + } + + #[test] + fn test_base58_successes() { + for (encoded, hash) in [ + ("11111111111111111111111111111111", CryptoHash::new()), + ("CjNSmWXTWhC3EhRVtqLhRmWMTkRbU96wUACqxMtV1uGf", hash(&[0, 1, 2])), + ] { + assert_eq!(encoded, hash.to_string()); + assert_eq!(hash, CryptoHash::from_str(encoded).unwrap()); + + let json = format!("\"{}\"", encoded); + assert_eq!(json, serde_json::to_string(&hash).unwrap()); + assert_eq!(hash, serde_json::from_str::(&json).unwrap()); + } + } + + #[test] + fn test_from_str_failures() { + fn test(input: &str, want_err: &str) { + match CryptoHash::from_str(input) { + Ok(got) => panic!("‘{input}’ should have failed; got ‘{got}’"), + Err(err) => { + assert!(err.to_string().starts_with(want_err), "input: ‘{input}’; err: {err}") + } + } + } + + // Invalid characters + test("foo-bar-baz", "provided string contained invalid character '-' at byte 3"); + + // Wrong length + for encoded in &[ + "CjNSmWXTWhC3ELhRmWMTkRbU96wUACqxMtV1uGf".to_string(), + "".to_string(), + "1".repeat(31), + "1".repeat(33), + "1".repeat(1000), + ] { + test(encoded, "incorrect length for hash"); + } + } + + #[test] + fn test_serde_deserialise_failures() { + fn test(input: &str, want_err: &str) { + match serde_json::from_str::(input) { + Ok(got) => panic!("‘{input}’ should have failed; got ‘{got}’"), + Err(err) => { + assert!(err.to_string().starts_with(want_err), "input: ‘{input}’; err: {err}") + } + } + } + + test("\"foo-bar-baz\"", "provided string contained invalid character"); + // Wrong length + for encoded in &[ + "\"CjNSmWXTWhC3ELhRmWMTkRbU96wUACqxMtV1uGf\"".to_string(), + "\"\"".to_string(), + format!("\"{}\"", "1".repeat(31)), + format!("\"{}\"", "1".repeat(33)), + format!("\"{}\"", "1".repeat(1000)), + ] { + test(encoded, "invalid length"); + } + } +} diff --git a/core/primitives-core/src/lib.rs b/core/primitives-core/src/lib.rs new file mode 100644 index 000000000..ff82fde18 --- /dev/null +++ b/core/primitives-core/src/lib.rs @@ -0,0 +1,12 @@ +pub use borsh; +pub use num_rational; + +pub mod account; +pub mod chains; +pub mod config; +pub mod hash; +pub mod serialize; +pub mod types; +pub mod version; + +pub use enum_map; diff --git a/core/primitives-core/src/serialize.rs b/core/primitives-core/src/serialize.rs new file mode 100644 index 000000000..326d39563 --- /dev/null +++ b/core/primitives-core/src/serialize.rs @@ -0,0 +1,208 @@ +use base64::display::Base64Display; +use base64::engine::general_purpose::GeneralPurpose; +use base64::engine::general_purpose::STANDARD as BASE64_STANDARD; +use base64::Engine; + +pub fn to_base64(input: &[u8]) -> String { + BASE64_STANDARD.encode(input) +} + +pub fn base64_display(input: &[u8]) -> Base64Display<'_, 'static, GeneralPurpose> { + Base64Display::new(input, &BASE64_STANDARD) +} + +pub fn from_base64(encoded: &str) -> Result, base64::DecodeError> { + BASE64_STANDARD.decode(encoded) +} + +/// Serialises number as a string; deserialises either as a string or number. +/// +/// This format works for `u64`, `u128`, `Option` and `Option` types. +/// When serialising, numbers are serialised as decimal strings. When +/// deserialising, strings are parsed as decimal numbers while numbers are +/// interpreted as is. +pub mod dec_format { + use serde::de; + use serde::{Deserializer, Serializer}; + + #[derive(thiserror::Error, Debug)] + #[error("cannot parse from unit")] + pub struct ParseUnitError; + + /// Abstraction between integers that we serialise. + pub trait DecType: Sized { + /// Formats number as a decimal string; passes `None` as is. + fn serialize(&self) -> Option; + + /// Constructs Self from a `null` value. Returns error if this type + /// does not accept `null` values. + fn try_from_unit() -> Result { + Err(ParseUnitError) + } + + /// Tries to parse decimal string as an integer. + fn try_from_str(value: &str) -> Result; + + /// Constructs Self from a 64-bit unsigned integer. + fn from_u64(value: u64) -> Self; + } + + impl DecType for u64 { + fn serialize(&self) -> Option { + Some(self.to_string()) + } + fn try_from_str(value: &str) -> Result { + Self::from_str_radix(value, 10) + } + fn from_u64(value: u64) -> Self { + value + } + } + + impl DecType for u128 { + fn serialize(&self) -> Option { + Some(self.to_string()) + } + fn try_from_str(value: &str) -> Result { + Self::from_str_radix(value, 10) + } + fn from_u64(value: u64) -> Self { + value.into() + } + } + + impl DecType for Option { + fn serialize(&self) -> Option { + self.as_ref().and_then(DecType::serialize) + } + fn try_from_unit() -> Result { + Ok(None) + } + fn try_from_str(value: &str) -> Result { + Some(T::try_from_str(value)).transpose() + } + fn from_u64(value: u64) -> Self { + Some(T::from_u64(value)) + } + } + + struct Visitor(core::marker::PhantomData); + + impl<'de, T: DecType> de::Visitor<'de> for Visitor { + type Value = T; + + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("a non-negative integer as a string") + } + + fn visit_unit(self) -> Result { + T::try_from_unit().map_err(|_| de::Error::invalid_type(de::Unexpected::Option, &self)) + } + + fn visit_u64(self, value: u64) -> Result { + Ok(T::from_u64(value)) + } + + fn visit_str(self, value: &str) -> Result { + T::try_from_str(value).map_err(de::Error::custom) + } + } + + pub fn deserialize<'de, D, T>(deserializer: D) -> Result + where + D: Deserializer<'de>, + T: DecType, + { + deserializer.deserialize_any(Visitor(Default::default())) + } + + pub fn serialize(num: &T, serializer: S) -> Result + where + S: Serializer, + T: DecType, + { + match num.serialize() { + Some(value) => serializer.serialize_str(&value), + None => serializer.serialize_none(), + } + } +} + +#[test] +fn test_u64_dec_format() { + #[derive(PartialEq, Debug, serde::Deserialize, serde::Serialize)] + struct Test { + #[serde(with = "dec_format")] + field: u64, + } + + assert_round_trip("{\"field\":\"42\"}", Test { field: 42 }); + assert_round_trip("{\"field\":\"18446744073709551615\"}", Test { field: u64::MAX }); + assert_deserialise("{\"field\":42}", Test { field: 42 }); + assert_de_error::("{\"field\":18446744073709551616}"); + assert_de_error::("{\"field\":\"18446744073709551616\"}"); + assert_de_error::("{\"field\":42.0}"); +} + +#[test] +fn test_u128_dec_format() { + #[derive(PartialEq, Debug, serde::Deserialize, serde::Serialize)] + struct Test { + #[serde(with = "dec_format")] + field: u128, + } + + assert_round_trip("{\"field\":\"42\"}", Test { field: 42 }); + assert_round_trip("{\"field\":\"18446744073709551615\"}", Test { field: u64::MAX as u128 }); + assert_round_trip("{\"field\":\"18446744073709551616\"}", Test { field: 18446744073709551616 }); + assert_deserialise("{\"field\":42}", Test { field: 42 }); + assert_de_error::("{\"field\":null}"); + assert_de_error::("{\"field\":42.0}"); +} + +#[test] +fn test_option_u128_dec_format() { + #[derive(PartialEq, Debug, serde::Deserialize, serde::Serialize)] + struct Test { + #[serde(with = "dec_format")] + field: Option, + } + + assert_round_trip("{\"field\":null}", Test { field: None }); + assert_round_trip("{\"field\":\"42\"}", Test { field: Some(42) }); + assert_round_trip( + "{\"field\":\"18446744073709551615\"}", + Test { field: Some(u64::MAX as u128) }, + ); + assert_round_trip( + "{\"field\":\"18446744073709551616\"}", + Test { field: Some(18446744073709551616) }, + ); + assert_deserialise("{\"field\":42}", Test { field: Some(42) }); + assert_de_error::("{\"field\":42.0}"); +} + +#[cfg(test)] +#[track_caller] +fn assert_round_trip<'a, T>(serialised: &'a str, obj: T) +where + T: serde::Deserialize<'a> + serde::Serialize + std::fmt::Debug + std::cmp::PartialEq, +{ + assert_eq!(serialised, serde_json::to_string(&obj).unwrap()); + assert_eq!(obj, serde_json::from_str(serialised).unwrap()); +} + +#[cfg(test)] +#[track_caller] +fn assert_deserialise<'a, T>(serialised: &'a str, obj: T) +where + T: serde::Deserialize<'a> + std::fmt::Debug + std::cmp::PartialEq, +{ + assert_eq!(obj, serde_json::from_str(serialised).unwrap()); +} + +#[cfg(test)] +#[track_caller] +fn assert_de_error<'a, T: serde::Deserialize<'a> + std::fmt::Debug>(serialised: &'a str) { + serde_json::from_str::(serialised).unwrap_err(); +} diff --git a/core/primitives-core/src/types.rs b/core/primitives-core/src/types.rs new file mode 100644 index 000000000..843e41013 --- /dev/null +++ b/core/primitives-core/src/types.rs @@ -0,0 +1,49 @@ +use crate::hash::CryptoHash; + +/// Account identifier. Provides access to user's state. +pub use crate::account::id::AccountId; +/// Hash used by a struct implementing the Merkle tree. +pub type MerkleHash = CryptoHash; +/// Validator identifier in current group. +pub type ValidatorId = u64; +/// Mask which validators participated in multi sign. +pub type ValidatorMask = Vec; +/// StorageUsage is used to count the amount of storage used by a contract. +pub type StorageUsage = u64; +/// StorageUsageChange is used to count the storage usage within a single contract call. +pub type StorageUsageChange = i64; +/// Nonce for transactions. +pub type Nonce = u64; +/// Height of the block. +pub type BlockHeight = u64; +/// Height of the epoch. +pub type EpochHeight = u64; +/// Shard index, from 0 to NUM_SHARDS - 1. +pub type ShardId = u64; +/// Balance is type for storing amounts of tokens. +pub type Balance = u128; +/// +pub type Power = u64; +/// Gas is a type for storing amount of gas. +pub type Gas = u64; +/// Compute is a type for storing compute time. Measured in femtoseconds (10^-15 seconds). +pub type Compute = u64; + +/// Weight of unused gas to distribute to scheduled function call actions. +/// Used in `promise_batch_action_function_call_weight` host function. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct GasWeight(pub u64); + +/// Number of blocks in current group. +pub type NumBlocks = u64; +/// Number of shards in current group. +pub type NumShards = u64; +/// Number of seats of validators (block producer or hidden ones) in current group (settlement). +pub type NumSeats = u64; +/// Block height delta that measures the difference between `BlockHeight`s. +pub type BlockHeightDelta = u64; + +pub type ReceiptIndex = usize; +pub type PromiseId = Vec; + +pub type ProtocolVersion = u32; diff --git a/core/primitives-core/src/version.rs b/core/primitives-core/src/version.rs new file mode 100644 index 000000000..e99a43d4e --- /dev/null +++ b/core/primitives-core/src/version.rs @@ -0,0 +1,250 @@ +use crate::types::ProtocolVersion; + +/// New Protocol features should go here. Features are guarded by their corresponding feature flag. +/// For example, if we have `ProtocolFeature::EVM` and a corresponding feature flag `evm`, it will look +/// like +/// +/// #[cfg(feature = "protocol_feature_evm")] +/// EVM code +/// +#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)] +pub enum ProtocolFeature { + // stable features + ImplicitAccountCreation, + RectifyInflation, + /// Add `AccessKey` nonce range by setting nonce to `(block_height - 1) * 1e6`, see + /// . + AccessKeyNonceRange, + /// Don't process any receipts for shard when chunk is not present. + /// Always use gas price computed in the previous block. + FixApplyChunks, + LowerStorageCost, + DeleteActionRestriction, + /// Add versions to `Account` data structure + AccountVersions, + TransactionSizeLimit, + /// Fix a bug in `storage_usage` for account caused by #3824 + FixStorageUsage, + /// Cap maximum gas price to 2,000,000,000 yoctoNEAR + CapMaxGasPrice, + CountRefundReceiptsInGasLimit, + /// Add `ripemd60` and `ecrecover` host function + MathExtension, + /// Restore receipts that were previously stuck because of + /// . + RestoreReceiptsAfterFixApplyChunks, + /// This feature switch our WASM engine implementation from wasmer 0.* to + /// wasmer 2.*, bringing better performance and reliability. + /// + /// The implementations should be sufficiently similar for this to not be a + /// protocol upgrade, but we conservatively do a protocol upgrade to be on + /// the safe side. + /// + /// Although wasmer2 is faster, we don't change fees with this protocol + /// version -- we can safely do that in a separate step. + Wasmer2, + SimpleNightshade, + LowerDataReceiptAndEcrecoverBaseCost, + /// Lowers the cost of wasm instruction due to switch to wasmer2. + LowerRegularOpCost, + /// Lowers the cost of wasm instruction due to switch to faster, + /// compiler-intrinsics based gas counter. + LowerRegularOpCost2, + /// Limit number of wasm functions in one contract. See + /// for more details. + LimitContractFunctionsNumber, + BlockHeaderV3, + /// Changes how we select validators for epoch and how we select validators + /// within epoch. See for general + /// description, note that we would not introduce chunk-only validators with + /// this feature + AliasValidatorSelectionAlgorithm, + /// Make block producers produce chunks for the same block they would later produce to avoid + /// network delays + SynchronizeBlockChunkProduction, + /// Change the algorithm to count WASM stack usage to avoid undercounting in + /// some cases. + CorrectStackLimit, + /// Add `AccessKey` nonce range for implicit accounts, as in `AccessKeyNonceRange` feature. + AccessKeyNonceForImplicitAccounts, + /// Increase cost per deployed code byte to cover for the compilation steps + /// that a deployment triggers. Only affects the action execution cost. + IncreaseDeploymentCost, + FunctionCallWeight, + /// This feature enforces a global limit on the function local declarations in a WebAssembly + /// contract. See <...> for more information. + LimitContractLocals, + /// Ensure caching all nodes in the chunk for which touching trie node cost was charged. Charge for each such node + /// only once per chunk at the first access time. + ChunkNodesCache, + /// Lower `max_length_storage_key` limit, which itself limits trie node sizes. + LowerStorageKeyLimit, + // alt_bn128_g1_multiexp, alt_bn128_g1_sum, alt_bn128_pairing_check host functions + AltBn128, + ChunkOnlyProducers, + /// Ensure the total stake of validators that are kicked out does not exceed a percentage of total stakes + MaxKickoutStake, + /// Validate account id for function call access keys. + AccountIdInFunctionCallPermission, + /// Zero Balance Account NEP 448: https://github.com/near/NEPs/pull/448 + ZeroBalanceAccount, + /// Execute a set of actions on behalf of another account. + /// + /// Meta Transaction NEP-366: https://github.com/near/NEPs/blob/master/neps/nep-0366.md + DelegateAction, + Ed25519Verify, + /// Decouple compute and gas costs of operations to safely limit the compute time it takes to + /// process the chunk. + /// + /// Compute Costs NEP-455: https://github.com/near/NEPs/blob/master/neps/nep-0455.md + ComputeCosts, + /// Enable flat storage for reads, reducing number of DB accesses from `2 * key.len()` in + /// the worst case to 2. + /// + /// Flat Storage NEP-399: https://github.com/near/NEPs/blob/master/neps/nep-0399.md + FlatStorageReads, + /// Enables preparation V2. Note that this setting is not supported in production settings + /// without NearVmRuntime enabled alongside it, as the VM runner would be too slow. + PreparationV2, + /// Enables unc-Vm. Note that this setting is not at all supported without PreparationV2, + /// as it hardcodes preparation v2 code into the generated assembly. + NearVmRuntime, + BlockHeaderV4, + /// Resharding V2. A new implementation for resharding and a new shard + /// layout for the production networks. + SimpleNightshadeV2, + /// In case not all validator seats are occupied our algorithm provide incorrect minimal seat + /// price - it reports as alpha * sum_stake instead of alpha * sum_stake / (1 - alpha), where + /// alpha is min stake ratio + #[cfg(feature = "protocol_feature_fix_staking_threshold")] + FixStakingThreshold, + /// Charge for contract loading before it happens. + #[cfg(feature = "protocol_feature_fix_contract_loading_cost")] + FixContractLoadingCost, + #[cfg(feature = "protocol_feature_reject_blocks_with_outdated_protocol_version")] + RejectBlocksWithOutdatedProtocolVersions, + RestrictTla, + /// Increases the number of chunk producers. + TestnetFewerBlockProducers, + /// Enables chunk validation which is introduced with stateless validation. + /// NEP: https://github.com/near/NEPs/pull/509 + ChunkValidation, + EthImplicitAccounts, +} + +impl ProtocolFeature { + pub const fn protocol_version(self) -> ProtocolVersion { + match self { + // Stable features + ProtocolFeature::ImplicitAccountCreation => 35, + ProtocolFeature::LowerStorageCost => 42, + ProtocolFeature::DeleteActionRestriction => 43, + ProtocolFeature::FixApplyChunks => 44, + ProtocolFeature::RectifyInflation | ProtocolFeature::AccessKeyNonceRange => 45, + ProtocolFeature::AccountVersions + | ProtocolFeature::TransactionSizeLimit + | ProtocolFeature::FixStorageUsage + | ProtocolFeature::CapMaxGasPrice + | ProtocolFeature::CountRefundReceiptsInGasLimit + | ProtocolFeature::MathExtension => 46, + ProtocolFeature::RestoreReceiptsAfterFixApplyChunks => 47, + ProtocolFeature::Wasmer2 + | ProtocolFeature::LowerDataReceiptAndEcrecoverBaseCost + | ProtocolFeature::LowerRegularOpCost + | ProtocolFeature::SimpleNightshade => 48, + ProtocolFeature::LowerRegularOpCost2 + | ProtocolFeature::LimitContractFunctionsNumber + | ProtocolFeature::BlockHeaderV3 + | ProtocolFeature::AliasValidatorSelectionAlgorithm => 49, + ProtocolFeature::SynchronizeBlockChunkProduction + | ProtocolFeature::CorrectStackLimit => 50, + ProtocolFeature::AccessKeyNonceForImplicitAccounts => 51, + ProtocolFeature::IncreaseDeploymentCost + | ProtocolFeature::FunctionCallWeight + | ProtocolFeature::LimitContractLocals + | ProtocolFeature::ChunkNodesCache + | ProtocolFeature::LowerStorageKeyLimit => 53, + ProtocolFeature::AltBn128 => 55, + ProtocolFeature::ChunkOnlyProducers | ProtocolFeature::MaxKickoutStake => 56, + ProtocolFeature::AccountIdInFunctionCallPermission => 57, + ProtocolFeature::Ed25519Verify + | ProtocolFeature::ZeroBalanceAccount + | ProtocolFeature::DelegateAction => 59, + ProtocolFeature::ComputeCosts | ProtocolFeature::FlatStorageReads => 61, + ProtocolFeature::PreparationV2 | ProtocolFeature::NearVmRuntime => 62, + ProtocolFeature::BlockHeaderV4 => 63, + ProtocolFeature::RestrictTla + | ProtocolFeature::TestnetFewerBlockProducers + | ProtocolFeature::SimpleNightshadeV2 => 64, + + // Nightly features + #[cfg(feature = "protocol_feature_fix_staking_threshold")] + ProtocolFeature::FixStakingThreshold => 126, + #[cfg(feature = "protocol_feature_fix_contract_loading_cost")] + ProtocolFeature::FixContractLoadingCost => 129, + #[cfg(feature = "protocol_feature_reject_blocks_with_outdated_protocol_version")] + ProtocolFeature::RejectBlocksWithOutdatedProtocolVersions => 132, + ProtocolFeature::ChunkValidation => 137, + ProtocolFeature::EthImplicitAccounts => 138, + } + } +} + +/// Current protocol version used on the mainnet. +/// Some features (e. g. FixStorageUsage) require that there is at least one epoch with exactly +/// the corresponding version +const STABLE_PROTOCOL_VERSION: ProtocolVersion = 64; + +/// Largest protocol version supported by the current binary. +pub const PROTOCOL_VERSION: ProtocolVersion = if cfg!(feature = "nightly_protocol") { + // On nightly, pick big enough version to support all features. + 139 +} else { + // Enable all stable features. + STABLE_PROTOCOL_VERSION +}; + +/// Both, outgoing and incoming tcp connections to peers, will be rejected if `peer's` +/// protocol version is lower than this. +pub const PEER_MIN_ALLOWED_PROTOCOL_VERSION: ProtocolVersion = STABLE_PROTOCOL_VERSION - 2; + +#[macro_export] +macro_rules! checked_feature { + ("stable", $feature:ident, $current_protocol_version:expr) => {{ + $crate::version::ProtocolFeature::$feature.protocol_version() <= $current_protocol_version + }}; + ($feature_name:tt, $feature:ident, $current_protocol_version:expr) => {{ + #[cfg(feature = $feature_name)] + let is_feature_enabled = $crate::version::ProtocolFeature::$feature.protocol_version() + <= $current_protocol_version; + #[cfg(not(feature = $feature_name))] + let is_feature_enabled = { + // Workaround unused variable warning + let _ = $current_protocol_version; + + false + }; + is_feature_enabled + }}; + + ($feature_name:tt, $feature:ident, $current_protocol_version:expr, $feature_block:block) => {{ + checked_feature!($feature_name, $feature, $current_protocol_version, $feature_block, {}) + }}; + + ($feature_name:tt, $feature:ident, $current_protocol_version:expr, $feature_block:block, $non_feature_block:block) => {{ + #[cfg(feature = $feature_name)] + { + if checked_feature!($feature_name, $feature, $current_protocol_version) { + $feature_block + } else { + $non_feature_block + } + } + // Workaround unused variable warning + #[cfg(not(feature = $feature_name))] + { + let _ = $current_protocol_version; + $non_feature_block + } + }}; +} diff --git a/core/primitives/Cargo.toml b/core/primitives/Cargo.toml new file mode 100644 index 000000000..ad7c0dbd2 --- /dev/null +++ b/core/primitives/Cargo.toml @@ -0,0 +1,88 @@ +[package] +name = "unc-primitives" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "This crate provides the base set of primitives used by other framework crates" +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +arbitrary.workspace = true +base64.workspace = true +borsh.workspace = true +bytesize.workspace = true +cfg-if.workspace = true +chrono.workspace = true +derive_more.workspace = true +easy-ext.workspace = true +enum-map.workspace = true +hex.workspace = true +num-rational.workspace = true +once_cell.workspace = true +primitive-types.workspace = true +rand.workspace = true +rand_chacha.workspace = true +reed-solomon-erasure.workspace = true +serde.workspace = true +serde_json.workspace = true +serde_with.workspace = true +serde_yaml.workspace = true +sha3.workspace = true +smart-default.workspace = true +stdx.workspace = true +strum.workspace = true +thiserror.workspace = true +time.workspace = true +tracing.workspace = true + +unc-crypto.workspace = true +unc-fmt.workspace = true +unc-o11y.workspace = true +unc-primitives-core.workspace = true +unc-rpc-error-macro.workspace = true +unc-vm-runner.workspace = true +unc-parameters.workspace = true + +[features] +sandbox = [] +dump_errors_schema = ["unc-rpc-error-macro/dump_errors_schema"] +protocol_feature_fix_staking_threshold = ["unc-primitives-core/protocol_feature_fix_staking_threshold"] +protocol_feature_fix_contract_loading_cost = ["unc-primitives-core/protocol_feature_fix_contract_loading_cost"] +protocol_feature_reject_blocks_with_outdated_protocol_version = ["unc-primitives-core/protocol_feature_reject_blocks_with_outdated_protocol_version"] +nightly = [ + "nightly_protocol", + "protocol_feature_fix_contract_loading_cost", + "protocol_feature_fix_staking_threshold", + "protocol_feature_reject_blocks_with_outdated_protocol_version", + "unc-fmt/nightly", + "unc-o11y/nightly", + "unc-parameters/nightly", + "unc-primitives-core/nightly", + "unc-vm-runner/nightly", +] + +nightly_protocol = [ + "unc-fmt/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-parameters/nightly_protocol", + "unc-primitives-core/nightly_protocol", + "unc-vm-runner/nightly_protocol", +] +new_epoch_sync = [] + + +calimero_zero_storage = [] + +[dev-dependencies] +assert_matches.workspace = true +bencher.workspace = true +insta.workspace = true + +[[bench]] +name = "serialization" +harness = false diff --git a/core/primitives/benches/serialization.rs b/core/primitives/benches/serialization.rs new file mode 100644 index 000000000..16172882d --- /dev/null +++ b/core/primitives/benches/serialization.rs @@ -0,0 +1,149 @@ +#[macro_use] +extern crate bencher; + +use bencher::{black_box, Bencher}; +use borsh::BorshDeserialize; +use unc_primitives::static_clock::StaticClock; + +use unc_crypto::{KeyType, PublicKey, Signature}; +use unc_primitives::account::Account; +use unc_primitives::block::{genesis_chunks, Block}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::combine_hash; +use unc_primitives::test_utils::account_new; +use unc_primitives::transaction::{Action, SignedTransaction, Transaction, TransferAction}; +use unc_primitives::types::{EpochId, StateRoot}; +use unc_primitives::validator_signer::InMemoryValidatorSigner; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_primitives_core::types::MerkleHash; +use num_rational::Rational32; + +fn create_transaction() -> SignedTransaction { + let mut actions = vec![]; + for _ in 0..10 { + actions.push(Action::Transfer(TransferAction { deposit: 1_000_000_000 })); + } + SignedTransaction::new( + Signature::empty(KeyType::ED25519), + Transaction { + signer_id: "123213123123".parse().unwrap(), + public_key: PublicKey::empty(KeyType::ED25519), + nonce: 123, + receiver_id: "1231231232131".parse().unwrap(), + block_hash: Default::default(), + actions, + }, + ) +} + +fn create_block() -> Block { + let genesis_chunks = genesis_chunks(vec![StateRoot::new()], &[0], 1_000, 0, PROTOCOL_VERSION); + let genesis = Block::genesis( + PROTOCOL_VERSION, + genesis_chunks.into_iter().map(|chunk| chunk.take_header()).collect(), + StaticClock::utc(), + 0, + 1_000, + 1_000, + CryptoHash::default(), + ); + let signer = InMemoryValidatorSigner::from_random("test".parse().unwrap(), KeyType::ED25519); + Block::produce( + PROTOCOL_VERSION, + PROTOCOL_VERSION, + genesis.header(), + 10, + genesis.header().block_ordinal() + 1, + vec![genesis.chunks()[0].clone()], + EpochId::default(), + EpochId::default(), + None, + vec![], + Rational32::from_integer(0), + 0, + 0, + Some(0), + vec![], + vec![], + &signer, + CryptoHash::default(), + CryptoHash::default(), + None, + ) +} + +fn create_account() -> Account { + account_new(0, CryptoHash::default()) +} + +fn serialize_tx(bench: &mut Bencher) { + let t = create_transaction(); + bench.iter(|| { + let bytes = borsh::to_vec(&t).unwrap(); + assert!(!bytes.is_empty()); + }); +} + +fn deserialize_tx(bench: &mut Bencher) { + let t = create_transaction(); + let bytes = borsh::to_vec(&t).unwrap(); + bench.iter(|| { + let nt = SignedTransaction::try_from_slice(&bytes).unwrap(); + assert_eq!(nt, t); + }); +} + +fn serialize_block(bench: &mut Bencher) { + let b = create_block(); + bench.iter(|| { + let bytes = borsh::to_vec(&b).unwrap(); + assert!(!bytes.is_empty()); + }); +} + +fn deserialize_block(bench: &mut Bencher) { + let b = create_block(); + let bytes = borsh::to_vec(&b).unwrap(); + bench.iter(|| { + let nb = Block::try_from_slice(&bytes).unwrap(); + assert_eq!(nb, b); + }); +} + +fn serialize_account(bench: &mut Bencher) { + let acc = create_account(); + bench.iter(|| { + let bytes = borsh::to_vec(&acc).unwrap(); + assert!(!bytes.is_empty()); + }); +} + +fn deserialize_account(bench: &mut Bencher) { + let acc = create_account(); + let bytes = borsh::to_vec(&acc).unwrap(); + bench.iter(|| { + let nacc = Account::try_from_slice(&bytes).unwrap(); + assert_eq!(nacc, acc); + }); +} + +fn combine_hash_bench(bench: &mut Bencher) { + let a = MerkleHash::default(); + let b = MerkleHash::default(); + bench.iter(|| { + let res = combine_hash(black_box(&a), black_box(&b)); + black_box(res) + }); +} + +benchmark_group!( + benches, + serialize_tx, + deserialize_tx, + serialize_block, + deserialize_block, + serialize_account, + deserialize_account, + combine_hash_bench, +); +benchmark_main!(benches); diff --git a/core/primitives/src/action/delegate.rs b/core/primitives/src/action/delegate.rs new file mode 100644 index 000000000..278e5ccd8 --- /dev/null +++ b/core/primitives/src/action/delegate.rs @@ -0,0 +1,213 @@ +//! DelegateAction is a type of action to support meta transactions. +//! +//! NEP: https://github.com/near/NEPs/pull/366 +//! This is the module containing the types introduced for delegate actions. + +pub use self::private_non_delegate_action::NonDelegateAction; +use super::Action; +use crate::signable_message::{SignableMessage, SignableMessageType}; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_crypto::{PublicKey, Signature}; +use unc_primitives_core::hash::{hash, CryptoHash}; +use unc_primitives_core::types::BlockHeight; +use unc_primitives_core::types::{AccountId, Nonce}; +use serde::{Deserialize, Serialize}; +use std::io::{Error, ErrorKind, Read}; + +/// This is an index number of Action::Delegate in Action enumeration +const ACTION_DELEGATE_NUMBER: u8 = 8; +/// This action allows to execute the inner actions behalf of the defined sender. +#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] +pub struct DelegateAction { + /// Signer of the delegated actions + pub sender_id: AccountId, + /// Receiver of the delegated actions. + pub receiver_id: AccountId, + /// List of actions to be executed. + /// + /// With the meta transactions MVP defined in NEP-366, nested + /// DelegateActions are not allowed. A separate type is used to enforce it. + pub actions: Vec, + /// Nonce to ensure that the same delegate action is not sent twice by a + /// relayer and should match for given account's `public_key`. + /// After this action is processed it will increment. + pub nonce: Nonce, + /// The maximal height of the block in the blockchain below which the given DelegateAction is valid. + pub max_block_height: BlockHeight, + /// Public key used to sign this delegated action. + pub public_key: PublicKey, +} + +#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] +pub struct SignedDelegateAction { + pub delegate_action: DelegateAction, + pub signature: Signature, +} + +impl SignedDelegateAction { + pub fn verify(&self) -> bool { + let delegate_action = &self.delegate_action; + let hash = delegate_action.get_nep461_hash(); + let public_key = &delegate_action.public_key; + + self.signature.verify(hash.as_ref(), public_key) + } +} + +impl From for Action { + fn from(delegate_action: SignedDelegateAction) -> Self { + Self::Delegate(Box::new(delegate_action)) + } +} + +impl DelegateAction { + pub fn get_actions(&self) -> Vec { + self.actions.iter().map(|a| a.clone().into()).collect() + } + + /// Delegate action hash used for NEP-461 signature scheme which tags + /// different messages before hashing + /// + /// For more details, see: [NEP-461](https://github.com/near/NEPs/pull/461) + pub fn get_nep461_hash(&self) -> CryptoHash { + let signable = SignableMessage::new(&self, SignableMessageType::DelegateAction); + let bytes = borsh::to_vec(&signable).expect("Failed to deserialize"); + hash(&bytes) + } +} + +/// A small private module to protect the private fields inside `NonDelegateAction`. +mod private_non_delegate_action { + use super::*; + + /// This is Action which mustn't contain DelegateAction. + /// + /// This struct is needed to avoid the recursion when Action/DelegateAction is deserialized. + /// + /// Important: Don't make the inner Action public, this must only be constructed + /// through the correct interface that ensures the inner Action is actually not + /// a delegate action. That would break an assumption of this type, which we use + /// in several places. For example, borsh de-/serialization relies on it. If the + /// invariant is broken, we may end up with a `Transaction` or `Receipt` that we + /// can serialize but deserializing it back causes a parsing error. + #[derive(Serialize, BorshSerialize, Deserialize, PartialEq, Eq, Clone, Debug)] + pub struct NonDelegateAction(Action); + + impl From for Action { + fn from(action: NonDelegateAction) -> Self { + action.0 + } + } + + #[derive(Debug, thiserror::Error)] + #[error("attempted to construct NonDelegateAction from Action::Delegate")] + pub struct IsDelegateAction; + + impl TryFrom for NonDelegateAction { + type Error = IsDelegateAction; + + fn try_from(action: Action) -> Result { + if matches!(action, Action::Delegate(_)) { + Err(IsDelegateAction) + } else { + Ok(Self(action)) + } + } + } + + impl borsh::de::BorshDeserialize for NonDelegateAction { + fn deserialize_reader(rd: &mut R) -> ::core::result::Result { + match u8::deserialize_reader(rd)? { + ACTION_DELEGATE_NUMBER => Err(Error::new( + ErrorKind::InvalidInput, + "DelegateAction mustn't contain a nested one", + )), + n => borsh::de::EnumExt::deserialize_variant(rd, n).map(Self), + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::action::CreateAccountAction; + use unc_crypto::KeyType; + + /// A serialized `Action::Delegate(SignedDelegateAction)` for testing. + /// + /// We want this to be parseable and accepted by protocol versions with meta + /// transactions enabled. But it should fail either in parsing or in + /// validation when this is included in a receipt for a block of an earlier + /// version. For now, it just fails to parse, as a test below checks. + const DELEGATE_ACTION_HEX: &str = concat!( + "0803000000616161030000006262620100000000010000000000000002000000000000", + "0000000000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000" + ); + + fn create_delegate_action(actions: Vec) -> Action { + Action::Delegate(Box::new(SignedDelegateAction { + delegate_action: DelegateAction { + sender_id: "aaa".parse().unwrap(), + receiver_id: "bbb".parse().unwrap(), + actions: actions + .iter() + .map(|a| NonDelegateAction::try_from(a.clone()).unwrap()) + .collect(), + nonce: 1, + max_block_height: 2, + public_key: PublicKey::empty(KeyType::ED25519), + }, + signature: Signature::empty(KeyType::ED25519), + })) + } + + #[test] + fn test_delegate_action_deserialization() { + // Expected an error. Buffer is empty + assert_eq!( + NonDelegateAction::try_from_slice(Vec::new().as_ref()).map_err(|e| e.kind()), + Err(ErrorKind::InvalidData) + ); + + let delegate_action = create_delegate_action(Vec::::new()); + let serialized_non_delegate_action = borsh::to_vec(&delegate_action).expect("Expect ok"); + + // Expected Action::Delegate has not been moved in enum Action + assert_eq!(serialized_non_delegate_action[0], ACTION_DELEGATE_NUMBER); + + // Expected a nested DelegateAction error + assert_eq!( + NonDelegateAction::try_from_slice(&serialized_non_delegate_action) + .map_err(|e| e.kind()), + Err(ErrorKind::InvalidInput) + ); + + let delegate_action = + create_delegate_action(vec![Action::CreateAccount(CreateAccountAction {})]); + let serialized_delegate_action = borsh::to_vec(&delegate_action).expect("Expect ok"); + + // Valid action + assert_eq!( + Action::try_from_slice(&serialized_delegate_action).expect("Expect ok"), + delegate_action + ); + } + + /// Check that the hard-coded delegate action is valid. + #[test] + fn test_delegate_action_deserialization_hard_coded() { + let serialized_delegate_action = hex::decode(DELEGATE_ACTION_HEX).expect("invalid hex"); + // The hex data is the same as the one we create below. + let delegate_action = + create_delegate_action(vec![Action::CreateAccount(CreateAccountAction {})]); + + // Valid action + assert_eq!( + Action::try_from_slice(&serialized_delegate_action).expect("Expect ok"), + delegate_action + ); + } +} diff --git a/core/primitives/src/action/mod.rs b/core/primitives/src/action/mod.rs new file mode 100644 index 000000000..0e4d92825 --- /dev/null +++ b/core/primitives/src/action/mod.rs @@ -0,0 +1,326 @@ +pub mod delegate; + +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_crypto::PublicKey; +use unc_primitives_core::{ + account::AccessKey, + serialize::dec_format, + types::{AccountId, Balance, Gas}, +}; +use serde_with::base64::Base64; +use serde_with::serde_as; +use std::fmt; + +fn base64(s: &[u8]) -> String { + use base64::Engine; + base64::engine::general_purpose::STANDARD.encode(s) +} + +#[derive( + BorshSerialize, + BorshDeserialize, + PartialEq, + Eq, + Clone, + Debug, + serde::Serialize, + serde::Deserialize, +)] +pub struct AddKeyAction { + /// A public key which will be associated with an access_key + pub public_key: PublicKey, + /// An access key with the permission + pub access_key: AccessKey, +} + +/// Create account action +#[derive( + BorshSerialize, + BorshDeserialize, + PartialEq, + Eq, + Clone, + Debug, + serde::Serialize, + serde::Deserialize, +)] +pub struct CreateAccountAction {} + +#[derive( + BorshSerialize, + BorshDeserialize, + PartialEq, + Eq, + Clone, + Debug, + serde::Serialize, + serde::Deserialize, +)] +pub struct DeleteAccountAction { + pub beneficiary_id: AccountId, +} + +#[derive( + BorshSerialize, + BorshDeserialize, + PartialEq, + Eq, + Clone, + Debug, + serde::Serialize, + serde::Deserialize, +)] +pub struct DeleteKeyAction { + /// A public key associated with the access_key to be deleted. + pub public_key: PublicKey, +} + +/// Deploy contract action +#[serde_as] +#[derive( + BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, +)] +pub struct DeployContractAction { + /// WebAssembly binary + #[serde_as(as = "Base64")] + pub code: Vec, +} + +impl fmt::Debug for DeployContractAction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("DeployContractAction") + .field("code", &format_args!("{}", base64(&self.code))) + .finish() + } +} + +#[serde_as] +#[derive( + BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, +)] +pub struct FunctionCallAction { + pub method_name: String, + #[serde_as(as = "Base64")] + pub args: Vec, + pub gas: Gas, + #[serde(with = "dec_format")] + pub deposit: Balance, +} + +impl fmt::Debug for FunctionCallAction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FunctionCallAction") + .field("method_name", &format_args!("{}", &self.method_name)) + .field("args", &format_args!("{}", base64(&self.args))) + .field("gas", &format_args!("{}", &self.gas)) + .field("deposit", &format_args!("{}", &self.deposit)) + .finish() + } +} + +/// An action which stakes signer_id tokens and setup's validator public key +#[derive( + BorshSerialize, + BorshDeserialize, + PartialEq, + Eq, + Clone, + Debug, + serde::Serialize, + serde::Deserialize, +)] +pub struct StakeAction { + /// Amount of tokens to stake. + #[serde(with = "dec_format")] + pub stake: Balance, + /// Validator key which will be used to sign transactions on behalf of signer_id + pub public_key: PublicKey, +} + +#[derive( + BorshSerialize, + BorshDeserialize, + PartialEq, + Eq, + Clone, + Debug, + serde::Serialize, + serde::Deserialize, +)] +pub struct TransferAction { + #[serde(with = "dec_format")] + pub deposit: Balance, +} + +#[serde_as] +#[derive( + BorshSerialize, + BorshDeserialize, + serde::Serialize, + serde::Deserialize, + PartialEq, Eq, Clone, +)] +pub struct RegisterRsa2048KeysAction { + /// this only can be used by the owner of root account + /// Public key used to sign this rsa keys action. + pub public_key: PublicKey, + /// addkeys or deletekeys + pub operation_type: u8, + /// attach args such as Miner id, sequence number,power,etc. + #[serde_as(as = "Base64")] + pub args: Vec, +} + +impl fmt::Debug for RegisterRsa2048KeysAction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RegisterRsa2048KeysAction") + .field("public_key", &format_args!("{}", &self.public_key)) + .field("operation_type", &format_args!("{}", &self.operation_type)) + .field("args", &format_args!("{}", base64(&self.args))) + .finish() + } +} + +#[serde_as] +#[derive( + BorshSerialize, + BorshDeserialize, + serde::Serialize, + serde::Deserialize, + PartialEq, Eq, Clone, +)] +pub struct CreateRsa2048ChallengeAction { + /// real miner request to create rsa2048 challenge + /// Public key used to sign this rsa keys action. + pub public_key: PublicKey, + /// Challenge key used to bind ValidatorPower + pub challenge_key: PublicKey, + /// attach args such as Miner id, sequence number,power,etc. + #[serde_as(as = "Base64")] + pub args: Vec, +} + +impl fmt::Debug for CreateRsa2048ChallengeAction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CreateRsa2048ChallengeAction") + .field("public_key", &format_args!("{}", &self.public_key)) + .field("challenge_key", &format_args!("{}", &self.challenge_key)) + .field("args", &format_args!("{}", base64(&self.args))) + .finish() + } +} + +#[derive( + BorshSerialize, + BorshDeserialize, + PartialEq, + Eq, + Debug, + Clone, + serde::Serialize, + serde::Deserialize, + strum::AsRefStr, +)] +pub enum Action { + /// Create an (sub)account using a transaction `receiver_id` as an ID for + /// a new account ID must pass validation rules described here + /// . + CreateAccount(CreateAccountAction), + /// Sets a Wasm code to a receiver_id + DeployContract(DeployContractAction), + FunctionCall(Box), + Transfer(TransferAction), + Stake(Box), + AddKey(Box), + DeleteKey(Box), + DeleteAccount(DeleteAccountAction), + Delegate(Box), + RegisterRsa2048Keys(Box), + CreateRsa2048Challenge(Box), +} +// Note: If this number ever goes down, please adjust the equality accordingly. Otherwise, +// we would get used to better performance and would be subject to a performance loss should +// we come back up to 32 bytes later on. +// If compiling with nightly, this check may fail due to new optimizations introduced by +// rustc. So, cfg-them out, as our production system only runs with stable. +#[cfg(not(fuzz))] +const _: () = assert!( + cfg!(not(target_pointer_width = "64")) || std::mem::size_of::() == 32, + "Action is 32 bytes for performance reasons, see #9451" +); + +impl Action { + pub fn get_prepaid_gas(&self) -> Gas { + match self { + Action::FunctionCall(a) => a.gas, + _ => 0, + } + } + pub fn get_deposit_balance(&self) -> Balance { + match self { + Action::FunctionCall(a) => a.deposit, + Action::Transfer(a) => a.deposit, + _ => 0, + } + } +} + +impl From for Action { + fn from(create_account_action: CreateAccountAction) -> Self { + Self::CreateAccount(create_account_action) + } +} + +impl From for Action { + fn from(deploy_contract_action: DeployContractAction) -> Self { + Self::DeployContract(deploy_contract_action) + } +} + +impl From for Action { + fn from(function_call_action: FunctionCallAction) -> Self { + Self::FunctionCall(Box::new(function_call_action)) + } +} + +impl From for Action { + fn from(transfer_action: TransferAction) -> Self { + Self::Transfer(transfer_action) + } +} + +impl From for Action { + fn from(stake_action: StakeAction) -> Self { + Self::Stake(Box::new(stake_action)) + } +} + +impl From for Action { + fn from(add_key_action: AddKeyAction) -> Self { + Self::AddKey(Box::new(add_key_action)) + } +} + +impl From for Action { + fn from(delete_key_action: DeleteKeyAction) -> Self { + Self::DeleteKey(Box::new(delete_key_action)) + } +} + +impl From for Action { + fn from(delete_account_action: DeleteAccountAction) -> Self { + Self::DeleteAccount(delete_account_action) + } +} + +impl From for Action { + fn from(rsa2048_keys_action: RegisterRsa2048KeysAction) -> Self { + Self::RegisterRsa2048Keys(Box::new(rsa2048_keys_action)) + } +} + +impl From for Action { + fn from(create_rsa2048_challenge_action: CreateRsa2048ChallengeAction) -> Self { + Self::CreateRsa2048Challenge(Box::new(create_rsa2048_challenge_action)) + } +} \ No newline at end of file diff --git a/core/primitives/src/block.rs b/core/primitives/src/block.rs new file mode 100644 index 000000000..0495c6b6f --- /dev/null +++ b/core/primitives/src/block.rs @@ -0,0 +1,720 @@ +use crate::block::BlockValidityError::{ + InvalidChallengeRoot, InvalidChunkHeaderRoot, InvalidChunkMask, InvalidReceiptRoot, + InvalidStateRoot, InvalidTransactionRoot, +}; +pub use crate::block_header::*; +use crate::challenge::{Challenges, ChallengesResult}; +use crate::checked_feature; +use crate::hash::{hash, CryptoHash}; +use crate::merkle::{merklize, verify_path, MerklePath}; +use crate::num_rational::Rational32; +use crate::sharding::{ + ChunkHashHeight, EncodedShardChunk, ReedSolomonWrapper, ShardChunk, ShardChunkHeader, + ShardChunkHeaderV1, +}; +use crate::static_clock::StaticClock; +use crate::types::{Balance, BlockHeight, EpochId, Gas, NumBlocks, StateRoot}; +use crate::utils::to_timestamp; +use crate::validator_signer::{EmptyValidatorSigner, ValidatorSigner}; +use crate::version::{ProtocolVersion, SHARD_CHUNK_HEADER_UPGRADE_VERSION}; +use borsh::{BorshDeserialize, BorshSerialize}; +use chrono::{DateTime, Utc}; +use unc_crypto::Signature; +use unc_primitives_core::types::ShardId; +use primitive_types::U256; +use std::ops::Index; +use std::sync::Arc; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq, Default)] +pub struct GenesisId { + /// Chain Id + pub chain_id: String, + /// Hash of genesis block + pub hash: CryptoHash, +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +pub enum BlockValidityError { + InvalidStateRoot, + InvalidReceiptRoot, + InvalidChunkHeaderRoot, + InvalidTransactionRoot, + InvalidChunkMask, + InvalidChallengeRoot, +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)] +pub struct BlockV1 { + pub header: BlockHeader, + pub chunks: Vec, + pub challenges: Challenges, + + // Data to confirm the correctness of randomness beacon output + pub vrf_value: unc_crypto::vrf::Value, + pub vrf_proof: unc_crypto::vrf::Proof, +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)] +pub struct BlockV2 { + pub header: BlockHeader, + pub chunks: Vec, + pub challenges: Challenges, + + // Data to confirm the correctness of randomness beacon output + pub vrf_value: unc_crypto::vrf::Value, + pub vrf_proof: unc_crypto::vrf::Proof, +} + +/// V2 -> V3: added BlockBody +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)] +pub struct BlockV3 { + pub header: BlockHeader, + pub body: BlockBody, +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)] +pub struct BlockBody { + pub chunks: Vec, + pub challenges: Challenges, + + // Data to confirm the correctness of randomness beacon output + pub vrf_value: unc_crypto::vrf::Value, + pub vrf_proof: unc_crypto::vrf::Proof, +} + +/// Versioned Block data structure. +/// For each next version, document what are the changes between versions. +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)] +pub enum Block { + BlockV1(Arc), + BlockV2(Arc), + BlockV3(Arc), +} + +pub fn genesis_chunks( + state_roots: Vec, + shard_ids: &[ShardId], + initial_gas_limit: Gas, + genesis_height: BlockHeight, + genesis_protocol_version: ProtocolVersion, +) -> Vec { + let mut rs = ReedSolomonWrapper::new(1, 1); + let state_roots = if state_roots.len() == shard_ids.len() { + state_roots + } else { + assert_eq!(state_roots.len(), 1); + std::iter::repeat(state_roots[0]).take(shard_ids.len()).collect() + }; + + shard_ids + .into_iter() + .zip(state_roots) + .map(|(&shard_id, state_root)| { + let (encoded_chunk, _) = EncodedShardChunk::new( + CryptoHash::default(), + state_root, + CryptoHash::default(), + genesis_height, + shard_id, + &mut rs, + 0, + initial_gas_limit, + 0, + CryptoHash::default(), + vec![], + vec![], + vec![], + &[], + CryptoHash::default(), + &EmptyValidatorSigner::default(), + genesis_protocol_version, + ) + .expect("Failed to decode genesis chunk"); + let mut chunk = encoded_chunk.decode_chunk(1).expect("Failed to decode genesis chunk"); + chunk.set_height_included(genesis_height); + chunk + }) + .collect() +} + +impl Block { + fn block_from_protocol_version( + this_epoch_protocol_version: ProtocolVersion, + next_epoch_protocol_version: ProtocolVersion, + header: BlockHeader, + body: BlockBody, + ) -> Block { + if next_epoch_protocol_version < SHARD_CHUNK_HEADER_UPGRADE_VERSION { + let legacy_chunks = body + .chunks + .into_iter() + .map(|chunk| match chunk { + ShardChunkHeader::V1(header) => header, + ShardChunkHeader::V2(_) => panic!( + "Attempted to include VersionedShardChunkHeaderV2 in old protocol version" + ), + ShardChunkHeader::V3(_) => panic!( + "Attempted to include VersionedShardChunkHeaderV3 in old protocol version" + ), + }) + .collect(); + + Block::BlockV1(Arc::new(BlockV1 { + header, + chunks: legacy_chunks, + challenges: body.challenges, + vrf_value: body.vrf_value, + vrf_proof: body.vrf_proof, + })) + } else if !checked_feature!("stable", BlockHeaderV4, this_epoch_protocol_version) { + Block::BlockV2(Arc::new(BlockV2 { + header, + chunks: body.chunks, + challenges: body.challenges, + vrf_value: body.vrf_value, + vrf_proof: body.vrf_proof, + })) + } else { + Block::BlockV3(Arc::new(BlockV3 { header, body })) + } + } + + /// Returns genesis block for given genesis date and state root. + pub fn genesis( + genesis_protocol_version: ProtocolVersion, + chunks: Vec, + timestamp: DateTime, + height: BlockHeight, + initial_gas_price: Balance, + initial_total_supply: Balance, + next_bp_hash: CryptoHash, + ) -> Self { + let challenges = vec![]; + for chunk in &chunks { + assert_eq!(chunk.height_included(), height); + } + let vrf_value = unc_crypto::vrf::Value([0; 32]); + let vrf_proof = unc_crypto::vrf::Proof([0; 64]); + let body = BlockBody { chunks, challenges, vrf_value, vrf_proof }; + let header = BlockHeader::genesis( + genesis_protocol_version, + height, + Block::compute_state_root(&body.chunks), + Block::compute_block_body_hash_impl(&body), + Block::compute_chunk_prev_outgoing_receipts_root(&body.chunks), + Block::compute_chunk_headers_root(&body.chunks).0, + Block::compute_chunk_tx_root(&body.chunks), + body.chunks.len() as u64, + Block::compute_challenges_root(&body.challenges), + timestamp, + initial_gas_price, + initial_total_supply, + next_bp_hash, + ); + + Self::block_from_protocol_version( + genesis_protocol_version, + genesis_protocol_version, + header, + body, + ) + } + + /// Produces new block from header of previous block, current state root and set of transactions. + pub fn produce( + this_epoch_protocol_version: ProtocolVersion, + next_epoch_protocol_version: ProtocolVersion, + prev: &BlockHeader, + height: BlockHeight, + block_ordinal: NumBlocks, + chunks: Vec, + epoch_id: EpochId, + next_epoch_id: EpochId, + epoch_sync_data_hash: Option, + approvals: Vec>>, + gas_price_adjustment_rate: Rational32, + min_gas_price: Balance, + max_gas_price: Balance, + minted_amount: Option, + challenges_result: ChallengesResult, + challenges: Challenges, + signer: &dyn ValidatorSigner, + next_bp_hash: CryptoHash, + block_merkle_root: CryptoHash, + timestamp_override: Option>, + ) -> Self { + // Collect aggregate of validators and gas usage/limits from chunks. + let mut prev_validator_power_proposals = vec![]; + let mut prev_validator_frozen_proposals = vec![]; + let mut gas_used = 0; + // This computation of chunk_mask relies on the fact that chunks are ordered by shard_id. + let mut chunk_mask = vec![]; + let mut balance_burnt = 0; + let mut gas_limit = 0; + for chunk in chunks.iter() { + if chunk.height_included() == height { + prev_validator_power_proposals.extend(chunk.prev_validator_power_proposals()); + prev_validator_frozen_proposals.extend(chunk.prev_validator_frozen_proposals()); + gas_used += chunk.prev_gas_used(); + gas_limit += chunk.gas_limit(); + balance_burnt += chunk.prev_balance_burnt(); + chunk_mask.push(true); + } else { + chunk_mask.push(false); + } + } + let next_gas_price = Self::compute_next_gas_price( + prev.next_gas_price(), + gas_used, + gas_limit, + gas_price_adjustment_rate, + min_gas_price, + max_gas_price, + ); + + let new_total_supply = prev.total_supply() + minted_amount.unwrap_or(0) - balance_burnt; + let now = to_timestamp(timestamp_override.unwrap_or_else(StaticClock::utc)); + let time = if now <= prev.raw_timestamp() { prev.raw_timestamp() + 1 } else { now }; + + let (vrf_value, vrf_proof) = signer.compute_vrf_with_proof(prev.random_value().as_ref()); + let random_value = hash(vrf_value.0.as_ref()); + + let last_ds_final_block = + if height == prev.height() + 1 { prev.hash() } else { prev.last_ds_final_block() }; + + let last_final_block = + if height == prev.height() + 1 && prev.last_ds_final_block() == prev.prev_hash() { + prev.prev_hash() + } else { + prev.last_final_block() + }; + + match prev { + BlockHeader::BlockHeaderV1(_) => debug_assert_eq!(prev.block_ordinal(), 0), + BlockHeader::BlockHeaderV2(_) => debug_assert_eq!(prev.block_ordinal(), 0), + BlockHeader::BlockHeaderV3(_) => { + debug_assert_eq!(prev.block_ordinal() + 1, block_ordinal) + } + BlockHeader::BlockHeaderV4(_) => { + debug_assert_eq!(prev.block_ordinal() + 1, block_ordinal) + } + }; + + let body = BlockBody { chunks, challenges, vrf_value, vrf_proof }; + let header = BlockHeader::new( + this_epoch_protocol_version, + next_epoch_protocol_version, + height, + *prev.hash(), + Block::compute_block_body_hash_impl(&body), + Block::compute_state_root(&body.chunks), + Block::compute_chunk_prev_outgoing_receipts_root(&body.chunks), + Block::compute_chunk_headers_root(&body.chunks).0, + Block::compute_chunk_tx_root(&body.chunks), + Block::compute_outcome_root(&body.chunks), + time, + Block::compute_challenges_root(&body.challenges), + random_value, + prev_validator_power_proposals, + prev_validator_frozen_proposals, + chunk_mask, + block_ordinal, + epoch_id, + next_epoch_id, + next_gas_price, + new_total_supply, + challenges_result, + signer, + *last_final_block, + *last_ds_final_block, + epoch_sync_data_hash, + approvals, + next_bp_hash, + block_merkle_root, + prev.height(), + ); + + Self::block_from_protocol_version( + this_epoch_protocol_version, + next_epoch_protocol_version, + header, + body, + ) + } + + pub fn verify_total_supply( + &self, + prev_total_supply: Balance, + minted_amount: Option, + ) -> bool { + let mut balance_burnt = 0; + + for chunk in self.chunks().iter() { + if chunk.height_included() == self.header().height() { + balance_burnt += chunk.prev_balance_burnt(); + } + } + + let new_total_supply = prev_total_supply + minted_amount.unwrap_or(0) - balance_burnt; + self.header().total_supply() == new_total_supply + } + + pub fn verify_gas_price( + &self, + gas_price: Balance, + min_gas_price: Balance, + max_gas_price: Balance, + gas_price_adjustment_rate: Rational32, + ) -> bool { + let gas_used = Self::compute_gas_used(self.chunks().iter(), self.header().height()); + let gas_limit = Self::compute_gas_limit(self.chunks().iter(), self.header().height()); + let expected_price = Self::compute_next_gas_price( + gas_price, + gas_used, + gas_limit, + gas_price_adjustment_rate, + min_gas_price, + max_gas_price, + ); + self.header().next_gas_price() == expected_price + } + + /// Computes gas price for applying chunks in the next block according to the formula: + /// next_gas_price = gas_price * (1 + (gas_used/gas_limit - 1/2) * adjustment_rate) + /// and clamped between min_gas_price and max_gas_price. + pub fn compute_next_gas_price( + gas_price: Balance, + gas_used: Gas, + gas_limit: Gas, + gas_price_adjustment_rate: Rational32, + min_gas_price: Balance, + max_gas_price: Balance, + ) -> Balance { + // If block was skipped, the price does not change. + if gas_limit == 0 { + return gas_price; + } + + let gas_used = u128::from(gas_used); + let gas_limit = u128::from(gas_limit); + let adjustment_rate_numer = *gas_price_adjustment_rate.numer() as u128; + let adjustment_rate_denom = *gas_price_adjustment_rate.denom() as u128; + + // This number can never be negative as long as gas_used <= gas_limit and + // adjustment_rate_numer <= adjustment_rate_denom. + let numerator = 2 * adjustment_rate_denom * gas_limit + + 2 * adjustment_rate_numer * gas_used + - adjustment_rate_numer * gas_limit; + let denominator = 2 * adjustment_rate_denom * gas_limit; + let next_gas_price = + U256::from(gas_price) * U256::from(numerator) / U256::from(denominator); + + next_gas_price.clamp(U256::from(min_gas_price), U256::from(max_gas_price)).as_u128() + } + + pub fn compute_state_root<'a, T: IntoIterator>( + chunks: T, + ) -> CryptoHash { + merklize( + &chunks.into_iter().map(|chunk| chunk.prev_state_root()).collect::>(), + ) + .0 + } + + pub fn compute_block_body_hash_impl(body: &BlockBody) -> CryptoHash { + CryptoHash::hash_borsh(body) + } + + pub fn compute_chunk_prev_outgoing_receipts_root< + 'a, + T: IntoIterator, + >( + chunks: T, + ) -> CryptoHash { + merklize( + &chunks + .into_iter() + .map(|chunk| chunk.prev_outgoing_receipts_root()) + .collect::>(), + ) + .0 + } + + pub fn compute_chunk_headers_root<'a, T: IntoIterator>( + chunks: T, + ) -> (CryptoHash, Vec) { + merklize( + &chunks + .into_iter() + .map(|chunk| ChunkHashHeight(chunk.chunk_hash(), chunk.height_included())) + .collect::>(), + ) + } + + pub fn compute_chunk_tx_root<'a, T: IntoIterator>( + chunks: T, + ) -> CryptoHash { + merklize(&chunks.into_iter().map(|chunk| chunk.tx_root()).collect::>()).0 + } + + pub fn compute_outcome_root<'a, T: IntoIterator>( + chunks: T, + ) -> CryptoHash { + merklize( + &chunks.into_iter().map(|chunk| chunk.prev_outcome_root()).collect::>(), + ) + .0 + } + + pub fn compute_challenges_root(challenges: &Challenges) -> CryptoHash { + merklize(&challenges.iter().map(|challenge| challenge.hash).collect::>()).0 + } + + pub fn compute_gas_used<'a, T: IntoIterator>( + chunks: T, + height: BlockHeight, + ) -> Gas { + chunks.into_iter().fold(0, |acc, chunk| { + if chunk.height_included() == height { + acc + chunk.prev_gas_used() + } else { + acc + } + }) + } + + pub fn compute_gas_limit<'a, T: IntoIterator>( + chunks: T, + height: BlockHeight, + ) -> Gas { + chunks.into_iter().fold(0, |acc, chunk| { + if chunk.height_included() == height { + acc + chunk.gas_limit() + } else { + acc + } + }) + } + + pub fn validate_chunk_header_proof( + chunk: &ShardChunkHeader, + chunk_root: &CryptoHash, + merkle_path: &MerklePath, + ) -> bool { + verify_path( + *chunk_root, + merkle_path, + &ChunkHashHeight(chunk.chunk_hash(), chunk.height_included()), + ) + } + + #[inline] + pub fn header(&self) -> &BlockHeader { + match self { + Block::BlockV1(block) => &block.header, + Block::BlockV2(block) => &block.header, + Block::BlockV3(block) => &block.header, + } + } + + pub fn chunks(&self) -> ChunksCollection { + match self { + Block::BlockV1(block) => ChunksCollection::V1( + block.chunks.iter().map(|h| ShardChunkHeader::V1(h.clone())).collect(), + ), + Block::BlockV2(block) => ChunksCollection::V2(&block.chunks), + Block::BlockV3(block) => ChunksCollection::V2(&block.body.chunks), + } + } + + #[inline] + pub fn challenges(&self) -> &Challenges { + match self { + Block::BlockV1(block) => &block.challenges, + Block::BlockV2(block) => &block.challenges, + Block::BlockV3(block) => &block.body.challenges, + } + } + + #[inline] + pub fn vrf_value(&self) -> &unc_crypto::vrf::Value { + match self { + Block::BlockV1(block) => &block.vrf_value, + Block::BlockV2(block) => &block.vrf_value, + Block::BlockV3(block) => &block.body.vrf_value, + } + } + + #[inline] + pub fn vrf_proof(&self) -> &unc_crypto::vrf::Proof { + match self { + Block::BlockV1(block) => &block.vrf_proof, + Block::BlockV2(block) => &block.vrf_proof, + Block::BlockV3(block) => &block.body.vrf_proof, + } + } + + pub fn hash(&self) -> &CryptoHash { + self.header().hash() + } + + pub fn compute_block_body_hash(&self) -> Option { + match self { + Block::BlockV1(_) => None, + Block::BlockV2(_) => None, + Block::BlockV3(block) => Some(Self::compute_block_body_hash_impl(&block.body)), + } + } + + /// Checks that block content matches block hash, with the possible exception of chunk signatures + pub fn check_validity(&self) -> Result<(), BlockValidityError> { + // Check that state root stored in the header matches the state root of the chunks + let state_root = Block::compute_state_root(self.chunks().iter()); + if self.header().prev_state_root() != &state_root { + return Err(InvalidStateRoot); + } + + // Check that chunk receipts root stored in the header matches the state root of the chunks + let chunk_receipts_root = + Block::compute_chunk_prev_outgoing_receipts_root(self.chunks().iter()); + if self.header().prev_chunk_outgoing_receipts_root() != &chunk_receipts_root { + return Err(InvalidReceiptRoot); + } + + // Check that chunk headers root stored in the header matches the chunk headers root of the chunks + let chunk_headers_root = Block::compute_chunk_headers_root(self.chunks().iter()).0; + if self.header().chunk_headers_root() != &chunk_headers_root { + return Err(InvalidChunkHeaderRoot); + } + + // Check that chunk tx root stored in the header matches the tx root of the chunks + let chunk_tx_root = Block::compute_chunk_tx_root(self.chunks().iter()); + if self.header().chunk_tx_root() != &chunk_tx_root { + return Err(InvalidTransactionRoot); + } + + let outcome_root = Block::compute_outcome_root(self.chunks().iter()); + if self.header().outcome_root() != &outcome_root { + return Err(InvalidTransactionRoot); + } + + // Check that chunk included root stored in the header matches the chunk included root of the chunks + let chunk_mask: Vec = self + .chunks() + .iter() + .map(|chunk| chunk.height_included() == self.header().height()) + .collect(); + if self.header().chunk_mask() != &chunk_mask[..] { + return Err(InvalidChunkMask); + } + + // Check that challenges root stored in the header matches the challenges root of the challenges + let challenges_root = Block::compute_challenges_root(self.challenges()); + if self.header().challenges_root() != &challenges_root { + return Err(InvalidChallengeRoot); + } + + Ok(()) + } +} + +pub enum ChunksCollection<'a> { + V1(Vec), + V2(&'a [ShardChunkHeader]), +} + +pub struct VersionedChunksIter<'a> { + chunks: &'a [ShardChunkHeader], + curr_index: usize, + len: usize, +} + +impl<'a> VersionedChunksIter<'a> { + fn new(chunks: &'a [ShardChunkHeader]) -> Self { + Self { chunks, curr_index: 0, len: chunks.len() } + } +} + +impl<'a> Iterator for VersionedChunksIter<'a> { + type Item = &'a ShardChunkHeader; + + fn next(&mut self) -> Option { + if self.curr_index < self.len { + let item = &self.chunks[self.curr_index]; + self.curr_index += 1; + Some(item) + } else { + None + } + } +} + +impl<'a> ExactSizeIterator for VersionedChunksIter<'a> { + fn len(&self) -> usize { + self.len - self.curr_index + } +} + +impl<'a> Index for ChunksCollection<'a> { + type Output = ShardChunkHeader; + + fn index(&self, index: usize) -> &Self::Output { + match self { + ChunksCollection::V1(chunks) => &chunks[index], + ChunksCollection::V2(chunks) => &chunks[index], + } + } +} + +impl<'a> ChunksCollection<'a> { + pub fn len(&self) -> usize { + match self { + ChunksCollection::V1(chunks) => chunks.len(), + ChunksCollection::V2(chunks) => chunks.len(), + } + } + + pub fn iter(&'a self) -> VersionedChunksIter<'a> { + match self { + ChunksCollection::V1(chunks) => VersionedChunksIter::new(chunks), + ChunksCollection::V2(chunks) => VersionedChunksIter::new(chunks), + } + } + + pub fn get(&self, index: usize) -> Option<&ShardChunkHeader> { + match self { + ChunksCollection::V1(chunks) => chunks.get(index), + ChunksCollection::V2(chunks) => chunks.get(index), + } + } +} + +/// The tip of a fork. A handle to the fork ancestry from its leaf in the +/// blockchain tree. References the max height and the latest and previous +/// blocks for convenience +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, serde::Serialize)] +pub struct Tip { + /// Height of the tip (max height of the fork) + pub height: BlockHeight, + /// Last block pushed to the fork + pub last_block_hash: CryptoHash, + /// Previous block + pub prev_block_hash: CryptoHash, + /// Current epoch id. Used for getting validator info. + pub epoch_id: EpochId, + /// Next epoch id. + pub next_epoch_id: EpochId, +} + +impl Tip { + /// Creates a new tip based on provided header. + pub fn from_header(header: &BlockHeader) -> Tip { + Tip { + height: header.height(), + last_block_hash: *header.hash(), + prev_block_hash: *header.prev_hash(), + epoch_id: header.epoch_id().clone(), + next_epoch_id: header.next_epoch_id().clone(), + } + } +} diff --git a/core/primitives/src/block_header.rs b/core/primitives/src/block_header.rs new file mode 100644 index 000000000..365587dbf --- /dev/null +++ b/core/primitives/src/block_header.rs @@ -0,0 +1,1158 @@ +use crate::challenge::ChallengesResult; +use crate::hash::{hash, CryptoHash}; +use crate::merkle::combine_hash; +use crate::network::PeerId; +use crate::types::validator_power::{ValidatorPower, ValidatorPowerIter, ValidatorPowerV1}; +use crate::types::{AccountId, Balance, BlockHeight, EpochId, MerkleHash, NumBlocks, ValidatorFrozenV1}; +use crate::utils::{from_timestamp, to_timestamp}; +use crate::validator_signer::ValidatorSigner; +use crate::version::{get_protocol_version, ProtocolVersion, PROTOCOL_VERSION}; +use borsh::{BorshDeserialize, BorshSerialize}; +use chrono::{DateTime, Utc}; +use unc_crypto::{KeyType, PublicKey, Signature}; +use std::sync::Arc; +use crate::types::validator_frozen::{ValidatorFrozen, ValidatorFrozenIter}; + +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, Eq, PartialEq)] +pub struct BlockHeaderInnerLite { + /// Height of this block. + pub height: BlockHeight, + /// Epoch start hash of this block's epoch. + /// Used for retrieving validator information + pub epoch_id: EpochId, + pub next_epoch_id: EpochId, + /// Root hash of the state at the previous block. + pub prev_state_root: MerkleHash, + /// Root of the outcomes of transactions and receipts from the previous chunks. + pub prev_outcome_root: MerkleHash, + /// Timestamp at which the block was built (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC). + pub timestamp: u64, + /// Hash of the next epoch block producers set + pub next_bp_hash: CryptoHash, + /// Merkle root of block hashes up to the current block. + pub block_merkle_root: CryptoHash, +} + +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, Eq, PartialEq)] +pub struct BlockHeaderInnerRest { + /// Root hash of the previous chunks' outgoing receipts in the given block. + pub prev_chunk_outgoing_receipts_root: MerkleHash, + /// Root hash of the chunk headers in the given block. + pub chunk_headers_root: MerkleHash, + /// Root hash of the chunk transactions in the given block. + pub chunk_tx_root: MerkleHash, + /// Number of chunks included into the block. + pub chunks_included: u64, + /// Root hash of the challenges in the given block. + pub challenges_root: MerkleHash, + /// The output of the randomness beacon + pub random_value: CryptoHash, + /// Validator power proposals from the previous chunks. + pub prev_validator_power_proposals: Vec, + /// Validator frozen proposals from the previous chunks. + pub prev_validator_frozen_proposals: Vec, + /// Mask for new chunks included in the block + pub chunk_mask: Vec, + /// Gas price for chunks in the next block. + pub next_gas_price: Balance, + /// Total supply of tokens in the system + pub total_supply: Balance, + /// List of challenges result from previous block. + pub challenges_result: ChallengesResult, + + /// Last block that has full BFT finality + pub last_final_block: CryptoHash, + /// Last block that has doomslug finality + pub last_ds_final_block: CryptoHash, + + /// All the approvals included in this block + pub approvals: Vec>>, + + /// Latest protocol version that this block producer has. + pub latest_protocol_version: ProtocolVersion, +} + +/// Remove `chunks_included` from V1 +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, Eq, PartialEq)] +pub struct BlockHeaderInnerRestV2 { + /// Root hash of the previous chunks' outgoing receipts in the given block. + pub prev_chunk_outgoing_receipts_root: MerkleHash, + /// Root hash of the chunk headers in the given block. + pub chunk_headers_root: MerkleHash, + /// Root hash of the chunk transactions in the given block. + pub chunk_tx_root: MerkleHash, + /// Root hash of the challenges in the given block. + pub challenges_root: MerkleHash, + /// The output of the randomness beacon + pub random_value: CryptoHash, + /// Validator proposals from the previous chunks. + pub prev_validator_power_proposals: Vec, + /// Validator proposals from the previous chunks. + pub prev_validator_frozen_proposals: Vec, + /// Mask for new chunks included in the block + pub chunk_mask: Vec, + /// Gas price for chunks in the next block. + pub next_gas_price: Balance, + /// Total supply of tokens in the system + pub total_supply: Balance, + /// List of challenges result from previous block. + pub challenges_result: ChallengesResult, + + /// Last block that has full BFT finality + pub last_final_block: CryptoHash, + /// Last block that has doomslug finality + pub last_ds_final_block: CryptoHash, + + /// All the approvals included in this block + pub approvals: Vec>>, + + /// Latest protocol version that this block producer has. + pub latest_protocol_version: ProtocolVersion, +} + +/// Add `prev_height` +/// Add `block_ordinal` +/// Add `epoch_sync_data_hash` +/// Use new `ValidatorStake` struct +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, Eq, PartialEq)] +pub struct BlockHeaderInnerRestV3 { + /// Root hash of the previous chunks' outgoing receipts in the given block. + pub prev_chunk_outgoing_receipts_root: MerkleHash, + /// Root hash of the chunk headers in the given block. + pub chunk_headers_root: MerkleHash, + /// Root hash of the chunk transactions in the given block. + pub chunk_tx_root: MerkleHash, + /// Root hash of the challenges in the given block. + pub challenges_root: MerkleHash, + /// The output of the randomness beacon + pub random_value: CryptoHash, + /// Validator proposals from the previous chunks. + pub prev_validator_power_proposals: Vec, + /// Validator proposals from the previous chunks. + pub prev_validator_frozen_proposals: Vec, + /// Mask for new chunks included in the block + pub chunk_mask: Vec, + /// Gas price for chunks in the next block. + pub next_gas_price: Balance, + /// Total supply of tokens in the system + pub total_supply: Balance, + /// List of challenges result from previous block. + pub challenges_result: ChallengesResult, + + /// Last block that has full BFT finality + pub last_final_block: CryptoHash, + /// Last block that has doomslug finality + pub last_ds_final_block: CryptoHash, + + /// The ordinal of the Block on the Canonical Chain + pub block_ordinal: NumBlocks, + + pub prev_height: BlockHeight, + + pub epoch_sync_data_hash: Option, + + /// All the approvals included in this block + pub approvals: Vec>>, + + /// Latest protocol version that this block producer has. + pub latest_protocol_version: ProtocolVersion, +} + +/// Add `block_body_hash` +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, Eq, PartialEq)] +pub struct BlockHeaderInnerRestV4 { + /// Hash of block body + pub block_body_hash: CryptoHash, + /// Root hash of the previous chunks' outgoing receipts in the given block. + pub prev_chunk_outgoing_receipts_root: MerkleHash, + /// Root hash of the chunk headers in the given block. + pub chunk_headers_root: MerkleHash, + /// Root hash of the chunk transactions in the given block. + pub chunk_tx_root: MerkleHash, + /// Root hash of the challenges in the given block. + pub challenges_root: MerkleHash, + /// The output of the randomness beacon + pub random_value: CryptoHash, + /// Validator proposals from the previous chunks. + pub prev_validator_power_proposals: Vec, + /// Validator proposals from the previous chunks. + pub prev_validator_frozen_proposals: Vec, + /// Mask for new chunks included in the block + pub chunk_mask: Vec, + /// Gas price for chunks in the next block. + pub next_gas_price: Balance, + /// Total supply of tokens in the system + pub total_supply: Balance, + /// List of challenges result from previous block. + pub challenges_result: ChallengesResult, + + /// Last block that has full BFT finality + pub last_final_block: CryptoHash, + /// Last block that has doomslug finality + pub last_ds_final_block: CryptoHash, + + /// The ordinal of the Block on the Canonical Chain + pub block_ordinal: NumBlocks, + + pub prev_height: BlockHeight, + + pub epoch_sync_data_hash: Option, + + /// All the approvals included in this block + pub approvals: Vec>>, + + /// Latest protocol version that this block producer has. + pub latest_protocol_version: ProtocolVersion, +} + +/// The part of the block approval that is different for endorsements and skips +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, PartialEq, Eq, Hash)] +pub enum ApprovalInner { + Endorsement(CryptoHash), + Skip(BlockHeight), +} + +/// Block approval by other block producers with a signature +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, PartialEq, Eq)] +pub struct Approval { + pub inner: ApprovalInner, + pub target_height: BlockHeight, + pub signature: Signature, + pub account_id: AccountId, +} + +/// The type of approvals. It is either approval from self or from a peer +#[derive(PartialEq, Eq, Debug)] +pub enum ApprovalType { + SelfApproval, + PeerApproval(PeerId), +} + +/// Block approval by other block producers. +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, PartialEq, Eq)] +pub struct ApprovalMessage { + pub approval: Approval, + pub target: AccountId, +} + +impl ApprovalInner { + pub fn new( + parent_hash: &CryptoHash, + parent_height: BlockHeight, + target_height: BlockHeight, + ) -> Self { + if target_height == parent_height + 1 { + ApprovalInner::Endorsement(*parent_hash) + } else { + ApprovalInner::Skip(parent_height) + } + } +} + +impl Approval { + pub fn new( + parent_hash: CryptoHash, + parent_height: BlockHeight, + target_height: BlockHeight, + signer: &dyn ValidatorSigner, + ) -> Self { + let inner = ApprovalInner::new(&parent_hash, parent_height, target_height); + let signature = signer.sign_approval(&inner, target_height); + Approval { inner, target_height, signature, account_id: signer.validator_id().clone() } + } + + pub fn get_data_for_sig(inner: &ApprovalInner, target_height: BlockHeight) -> Vec { + [borsh::to_vec(&inner).unwrap().as_ref(), target_height.to_le_bytes().as_ref()].concat() + } +} + +impl ApprovalMessage { + pub fn new(approval: Approval, target: AccountId) -> Self { + ApprovalMessage { approval, target } + } +} + +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, Eq, PartialEq)] +#[borsh(init=init)] +pub struct BlockHeaderV1 { + pub prev_hash: CryptoHash, + + /// Inner part of the block header that gets hashed, split into two parts, one that is sent + /// to light clients, and the rest + pub inner_lite: BlockHeaderInnerLite, + pub inner_rest: BlockHeaderInnerRest, + + /// Signature of the block producer. + pub signature: Signature, + + /// Cached value of hash for this block. + #[borsh(skip)] + pub hash: CryptoHash, +} + +impl BlockHeaderV1 { + pub fn init(&mut self) { + self.hash = BlockHeader::compute_hash( + self.prev_hash, + &borsh::to_vec(&self.inner_lite).expect("Failed to serialize"), + &borsh::to_vec(&self.inner_rest).expect("Failed to serialize"), + ); + } +} + +/// V1 -> V2: Remove `chunks_included` from `inner_reset` +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, Eq, PartialEq)] +#[borsh(init=init)] +pub struct BlockHeaderV2 { + pub prev_hash: CryptoHash, + + /// Inner part of the block header that gets hashed, split into two parts, one that is sent + /// to light clients, and the rest + pub inner_lite: BlockHeaderInnerLite, + pub inner_rest: BlockHeaderInnerRestV2, + + /// Signature of the block producer. + pub signature: Signature, + + /// Cached value of hash for this block. + #[borsh(skip)] + pub hash: CryptoHash, +} + +/// V2 -> V3: Add `prev_height` to `inner_rest` and use new `ValidatorStake` +// Add `block_ordinal` to `inner_rest` +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, Eq, PartialEq)] +#[borsh(init=init)] +pub struct BlockHeaderV3 { + pub prev_hash: CryptoHash, + + /// Inner part of the block header that gets hashed, split into two parts, one that is sent + /// to light clients, and the rest + pub inner_lite: BlockHeaderInnerLite, + pub inner_rest: BlockHeaderInnerRestV3, + + /// Signature of the block producer. + pub signature: Signature, + + /// Cached value of hash for this block. + #[borsh(skip)] + pub hash: CryptoHash, +} + +/// V3 -> V4: Add hash of block body to inner_rest +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, Eq, PartialEq)] +#[borsh(init=init)] +pub struct BlockHeaderV4 { + pub prev_hash: CryptoHash, + + /// Inner part of the block header that gets hashed, split into two parts, one that is sent + /// to light clients, and the rest + pub inner_lite: BlockHeaderInnerLite, + pub inner_rest: BlockHeaderInnerRestV4, + + /// Signature of the block producer. + pub signature: Signature, + + /// Cached value of hash for this block. + #[borsh(skip)] + pub hash: CryptoHash, +} + +impl BlockHeaderV2 { + pub fn init(&mut self) { + self.hash = BlockHeader::compute_hash( + self.prev_hash, + &borsh::to_vec(&self.inner_lite).expect("Failed to serialize"), + &borsh::to_vec(&self.inner_rest).expect("Failed to serialize"), + ); + } +} + +impl BlockHeaderV3 { + pub fn init(&mut self) { + self.hash = BlockHeader::compute_hash( + self.prev_hash, + &borsh::to_vec(&self.inner_lite).expect("Failed to serialize"), + &borsh::to_vec(&self.inner_rest).expect("Failed to serialize"), + ); + } +} + +impl BlockHeaderV4 { + pub fn init(&mut self) { + self.hash = BlockHeader::compute_hash( + self.prev_hash, + &borsh::to_vec(&self.inner_lite).expect("Failed to serialize"), + &borsh::to_vec(&self.inner_rest).expect("Failed to serialize"), + ); + } +} + +/// Versioned BlockHeader data structure. +/// For each next version, document what are the changes between versions. +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, Eq, PartialEq)] +pub enum BlockHeader { + BlockHeaderV1(Arc), + BlockHeaderV2(Arc), + BlockHeaderV3(Arc), + BlockHeaderV4(Arc), +} + +impl BlockHeader { + pub fn compute_inner_hash(inner_lite: &[u8], inner_rest: &[u8]) -> CryptoHash { + let hash_lite = hash(inner_lite); + let hash_rest = hash(inner_rest); + combine_hash(&hash_lite, &hash_rest) + } + + pub fn compute_hash(prev_hash: CryptoHash, inner_lite: &[u8], inner_rest: &[u8]) -> CryptoHash { + let hash_inner = BlockHeader::compute_inner_hash(inner_lite, inner_rest); + + combine_hash(&hash_inner, &prev_hash) + } + + pub fn new( + this_epoch_protocol_version: ProtocolVersion, + next_epoch_protocol_version: ProtocolVersion, + height: BlockHeight, + prev_hash: CryptoHash, + block_body_hash: CryptoHash, + prev_state_root: MerkleHash, + prev_chunk_outgoing_receipts_root: MerkleHash, + chunk_headers_root: MerkleHash, + chunk_tx_root: MerkleHash, + outcome_root: MerkleHash, + timestamp: u64, + challenges_root: MerkleHash, + random_value: CryptoHash, + prev_validator_power_proposals: Vec, + prev_validator_frozen_proposals: Vec, + chunk_mask: Vec, + block_ordinal: NumBlocks, + epoch_id: EpochId, + next_epoch_id: EpochId, + next_gas_price: Balance, + total_supply: Balance, + challenges_result: ChallengesResult, + signer: &dyn ValidatorSigner, + last_final_block: CryptoHash, + last_ds_final_block: CryptoHash, + epoch_sync_data_hash: Option, + approvals: Vec>>, + next_bp_hash: CryptoHash, + block_merkle_root: CryptoHash, + prev_height: BlockHeight, + ) -> Self { + let inner_lite = BlockHeaderInnerLite { + height, + epoch_id, + next_epoch_id, + prev_state_root, + prev_outcome_root: outcome_root, + timestamp, + next_bp_hash, + block_merkle_root, + }; + // This function still preserves code for old block header versions. These code are no longer + // used in production, but we still have features tests in the code that uses them. + // So we still keep the old code here. + let last_header_v2_version = + crate::version::ProtocolFeature::BlockHeaderV3.protocol_version() - 1; + // Previously we passed next_epoch_protocol_version here, which is incorrect, but we need + // to preserve this for archival nodes + if next_epoch_protocol_version <= 29 { + let chunks_included = chunk_mask.iter().map(|val| *val as u64).sum::(); + let inner_rest = BlockHeaderInnerRest { + prev_chunk_outgoing_receipts_root, + chunk_headers_root, + chunk_tx_root, + chunks_included, + challenges_root, + random_value, + prev_validator_power_proposals: prev_validator_power_proposals + .into_iter() + .map(|v| v.into_v1()) + .collect(), + prev_validator_frozen_proposals: prev_validator_frozen_proposals + .into_iter() + .map(|v| v.into_v1()) + .collect(), + chunk_mask, + next_gas_price, + total_supply, + challenges_result, + last_final_block, + last_ds_final_block, + approvals, + latest_protocol_version: PROTOCOL_VERSION, + }; + let (hash, signature) = signer.sign_block_header_parts( + prev_hash, + &borsh::to_vec(&inner_lite).expect("Failed to serialize"), + &borsh::to_vec(&inner_rest).expect("Failed to serialize"), + ); + Self::BlockHeaderV1(Arc::new(BlockHeaderV1 { + prev_hash, + inner_lite, + inner_rest, + signature, + hash, + })) + } else if this_epoch_protocol_version <= last_header_v2_version { + let inner_rest = BlockHeaderInnerRestV2 { + prev_chunk_outgoing_receipts_root, + chunk_headers_root, + chunk_tx_root, + challenges_root, + random_value, + prev_validator_power_proposals: prev_validator_power_proposals + .into_iter() + .map(|v| v.into_v1()) + .collect(), + prev_validator_frozen_proposals: prev_validator_frozen_proposals + .into_iter() + .map(|v| v.into_v1()) + .collect(), + chunk_mask, + next_gas_price, + total_supply, + challenges_result, + last_final_block, + last_ds_final_block, + approvals, + latest_protocol_version: PROTOCOL_VERSION, + }; + let (hash, signature) = signer.sign_block_header_parts( + prev_hash, + &borsh::to_vec(&inner_lite).expect("Failed to serialize"), + &borsh::to_vec(&inner_rest).expect("Failed to serialize"), + ); + Self::BlockHeaderV2(Arc::new(BlockHeaderV2 { + prev_hash, + inner_lite, + inner_rest, + signature, + hash, + })) + } else if !crate::checked_feature!("stable", BlockHeaderV4, this_epoch_protocol_version) { + let inner_rest = BlockHeaderInnerRestV3 { + prev_chunk_outgoing_receipts_root, + chunk_headers_root, + chunk_tx_root, + challenges_root, + random_value, + prev_validator_power_proposals, + prev_validator_frozen_proposals, + chunk_mask, + next_gas_price, + block_ordinal, + total_supply, + challenges_result, + last_final_block, + last_ds_final_block, + prev_height, + epoch_sync_data_hash, + approvals, + latest_protocol_version: get_protocol_version(next_epoch_protocol_version), + }; + let (hash, signature) = signer.sign_block_header_parts( + prev_hash, + &borsh::to_vec(&inner_lite).expect("Failed to serialize"), + &borsh::to_vec(&inner_rest).expect("Failed to serialize"), + ); + Self::BlockHeaderV3(Arc::new(BlockHeaderV3 { + prev_hash, + inner_lite, + inner_rest, + signature, + hash, + })) + } else { + let inner_rest = BlockHeaderInnerRestV4 { + block_body_hash, + prev_chunk_outgoing_receipts_root, + chunk_headers_root, + chunk_tx_root, + challenges_root, + random_value, + prev_validator_power_proposals, + prev_validator_frozen_proposals, + chunk_mask, + next_gas_price, + block_ordinal, + total_supply, + challenges_result, + last_final_block, + last_ds_final_block, + prev_height, + epoch_sync_data_hash, + approvals, + latest_protocol_version: get_protocol_version(next_epoch_protocol_version), + }; + let (hash, signature) = signer.sign_block_header_parts( + prev_hash, + &borsh::to_vec(&inner_lite).expect("Failed to serialize"), + &borsh::to_vec(&inner_rest).expect("Failed to serialize"), + ); + Self::BlockHeaderV4(Arc::new(BlockHeaderV4 { + prev_hash, + inner_lite, + inner_rest, + signature, + hash, + })) + } + } + + pub fn genesis( + genesis_protocol_version: ProtocolVersion, + height: BlockHeight, + state_root: MerkleHash, + block_body_hash: CryptoHash, + prev_chunk_outgoing_receipts_root: MerkleHash, + chunk_headers_root: MerkleHash, + chunk_tx_root: MerkleHash, + num_shards: u64, + challenges_root: MerkleHash, + timestamp: DateTime, + initial_gas_price: Balance, + initial_total_supply: Balance, + next_bp_hash: CryptoHash, + ) -> Self { + let chunks_included = if height == 0 { num_shards } else { 0 }; + let inner_lite = BlockHeaderInnerLite { + height, + epoch_id: EpochId::default(), + next_epoch_id: EpochId::default(), + prev_state_root: state_root, + prev_outcome_root: CryptoHash::default(), + timestamp: to_timestamp(timestamp), + next_bp_hash, + block_merkle_root: CryptoHash::default(), + }; + let last_header_v2_version = + crate::version::ProtocolFeature::BlockHeaderV3.protocol_version() - 1; + if genesis_protocol_version <= 29 { + let inner_rest = BlockHeaderInnerRest { + prev_chunk_outgoing_receipts_root, + chunk_headers_root, + chunk_tx_root, + chunks_included, + challenges_root, + random_value: CryptoHash::default(), + prev_validator_power_proposals: vec![], + prev_validator_frozen_proposals: vec![], + chunk_mask: vec![], + next_gas_price: initial_gas_price, + total_supply: initial_total_supply, + challenges_result: vec![], + last_final_block: CryptoHash::default(), + last_ds_final_block: CryptoHash::default(), + approvals: vec![], + latest_protocol_version: genesis_protocol_version, + }; + let hash = BlockHeader::compute_hash( + CryptoHash::default(), + &borsh::to_vec(&inner_lite).expect("Failed to serialize"), + &borsh::to_vec(&inner_rest).expect("Failed to serialize"), + ); + Self::BlockHeaderV1(Arc::new(BlockHeaderV1 { + prev_hash: CryptoHash::default(), + inner_lite, + inner_rest, + signature: Signature::empty(KeyType::ED25519), + hash, + })) + } else if genesis_protocol_version <= last_header_v2_version { + let inner_rest = BlockHeaderInnerRestV2 { + prev_chunk_outgoing_receipts_root, + chunk_headers_root, + chunk_tx_root, + challenges_root, + random_value: CryptoHash::default(), + prev_validator_power_proposals: vec![], + prev_validator_frozen_proposals: vec![], + chunk_mask: vec![true; chunks_included as usize], + next_gas_price: initial_gas_price, + total_supply: initial_total_supply, + challenges_result: vec![], + last_final_block: CryptoHash::default(), + last_ds_final_block: CryptoHash::default(), + approvals: vec![], + latest_protocol_version: genesis_protocol_version, + }; + let hash = BlockHeader::compute_hash( + CryptoHash::default(), + &borsh::to_vec(&inner_lite).expect("Failed to serialize"), + &borsh::to_vec(&inner_rest).expect("Failed to serialize"), + ); + Self::BlockHeaderV2(Arc::new(BlockHeaderV2 { + prev_hash: CryptoHash::default(), + inner_lite, + inner_rest, + signature: Signature::empty(KeyType::ED25519), + hash, + })) + } else if !crate::checked_feature!("stable", BlockHeaderV4, genesis_protocol_version) { + let inner_rest = BlockHeaderInnerRestV3 { + prev_chunk_outgoing_receipts_root, + chunk_headers_root, + chunk_tx_root, + challenges_root, + random_value: CryptoHash::default(), + prev_validator_power_proposals: vec![], + prev_validator_frozen_proposals: vec![], + chunk_mask: vec![true; chunks_included as usize], + block_ordinal: 1, // It is guaranteed that Chain has the only Block which is Genesis + next_gas_price: initial_gas_price, + total_supply: initial_total_supply, + challenges_result: vec![], + last_final_block: CryptoHash::default(), + last_ds_final_block: CryptoHash::default(), + prev_height: 0, + epoch_sync_data_hash: None, // Epoch Sync cannot be executed up to Genesis + approvals: vec![], + latest_protocol_version: genesis_protocol_version, + }; + let hash = BlockHeader::compute_hash( + CryptoHash::default(), + &borsh::to_vec(&inner_lite).expect("Failed to serialize"), + &borsh::to_vec(&inner_rest).expect("Failed to serialize"), + ); + Self::BlockHeaderV3(Arc::new(BlockHeaderV3 { + prev_hash: CryptoHash::default(), + inner_lite, + inner_rest, + signature: Signature::empty(KeyType::ED25519), + hash, + })) + } else { + let inner_rest = BlockHeaderInnerRestV4 { + prev_chunk_outgoing_receipts_root, + chunk_headers_root, + chunk_tx_root, + challenges_root, + block_body_hash, + random_value: CryptoHash::default(), + prev_validator_power_proposals: vec![], + prev_validator_frozen_proposals: vec![], + chunk_mask: vec![true; chunks_included as usize], + block_ordinal: 1, // It is guaranteed that Chain has the only Block which is Genesis + next_gas_price: initial_gas_price, + total_supply: initial_total_supply, + challenges_result: vec![], + last_final_block: CryptoHash::default(), + last_ds_final_block: CryptoHash::default(), + prev_height: 0, + epoch_sync_data_hash: None, // Epoch Sync cannot be executed up to Genesis + approvals: vec![], + latest_protocol_version: genesis_protocol_version, + }; + let hash = BlockHeader::compute_hash( + CryptoHash::default(), + &borsh::to_vec(&inner_lite).expect("Failed to serialize"), + &borsh::to_vec(&inner_rest).expect("Failed to serialize"), + ); + Self::BlockHeaderV4(Arc::new(BlockHeaderV4 { + prev_hash: CryptoHash::default(), + inner_lite, + inner_rest, + signature: Signature::empty(KeyType::ED25519), + hash, + })) + } + } + + #[inline] + pub fn hash(&self) -> &CryptoHash { + match self { + BlockHeader::BlockHeaderV1(header) => &header.hash, + BlockHeader::BlockHeaderV2(header) => &header.hash, + BlockHeader::BlockHeaderV3(header) => &header.hash, + BlockHeader::BlockHeaderV4(header) => &header.hash, + } + } + + #[inline] + pub fn prev_hash(&self) -> &CryptoHash { + match self { + BlockHeader::BlockHeaderV1(header) => &header.prev_hash, + BlockHeader::BlockHeaderV2(header) => &header.prev_hash, + BlockHeader::BlockHeaderV3(header) => &header.prev_hash, + BlockHeader::BlockHeaderV4(header) => &header.prev_hash, + } + } + + #[inline] + pub fn signature(&self) -> &Signature { + match self { + BlockHeader::BlockHeaderV1(header) => &header.signature, + BlockHeader::BlockHeaderV2(header) => &header.signature, + BlockHeader::BlockHeaderV3(header) => &header.signature, + BlockHeader::BlockHeaderV4(header) => &header.signature, + } + } + + #[inline] + pub fn height(&self) -> BlockHeight { + match self { + BlockHeader::BlockHeaderV1(header) => header.inner_lite.height, + BlockHeader::BlockHeaderV2(header) => header.inner_lite.height, + BlockHeader::BlockHeaderV3(header) => header.inner_lite.height, + BlockHeader::BlockHeaderV4(header) => header.inner_lite.height, + } + } + + #[inline] + pub fn prev_height(&self) -> Option { + match self { + BlockHeader::BlockHeaderV1(_) => None, + BlockHeader::BlockHeaderV2(_) => None, + BlockHeader::BlockHeaderV3(header) => Some(header.inner_rest.prev_height), + BlockHeader::BlockHeaderV4(header) => Some(header.inner_rest.prev_height), + } + } + + #[inline] + pub fn epoch_id(&self) -> &EpochId { + match self { + BlockHeader::BlockHeaderV1(header) => &header.inner_lite.epoch_id, + BlockHeader::BlockHeaderV2(header) => &header.inner_lite.epoch_id, + BlockHeader::BlockHeaderV3(header) => &header.inner_lite.epoch_id, + BlockHeader::BlockHeaderV4(header) => &header.inner_lite.epoch_id, + } + } + + #[inline] + pub fn next_epoch_id(&self) -> &EpochId { + match self { + BlockHeader::BlockHeaderV1(header) => &header.inner_lite.next_epoch_id, + BlockHeader::BlockHeaderV2(header) => &header.inner_lite.next_epoch_id, + BlockHeader::BlockHeaderV3(header) => &header.inner_lite.next_epoch_id, + BlockHeader::BlockHeaderV4(header) => &header.inner_lite.next_epoch_id, + } + } + + #[inline] + pub fn prev_state_root(&self) -> &MerkleHash { + match self { + BlockHeader::BlockHeaderV1(header) => &header.inner_lite.prev_state_root, + BlockHeader::BlockHeaderV2(header) => &header.inner_lite.prev_state_root, + BlockHeader::BlockHeaderV3(header) => &header.inner_lite.prev_state_root, + BlockHeader::BlockHeaderV4(header) => &header.inner_lite.prev_state_root, + } + } + + #[inline] + pub fn prev_chunk_outgoing_receipts_root(&self) -> &MerkleHash { + match self { + BlockHeader::BlockHeaderV1(header) => { + &header.inner_rest.prev_chunk_outgoing_receipts_root + } + BlockHeader::BlockHeaderV2(header) => { + &header.inner_rest.prev_chunk_outgoing_receipts_root + } + BlockHeader::BlockHeaderV3(header) => { + &header.inner_rest.prev_chunk_outgoing_receipts_root + } + BlockHeader::BlockHeaderV4(header) => { + &header.inner_rest.prev_chunk_outgoing_receipts_root + } + } + } + + #[inline] + pub fn chunk_headers_root(&self) -> &MerkleHash { + match self { + BlockHeader::BlockHeaderV1(header) => &header.inner_rest.chunk_headers_root, + BlockHeader::BlockHeaderV2(header) => &header.inner_rest.chunk_headers_root, + BlockHeader::BlockHeaderV3(header) => &header.inner_rest.chunk_headers_root, + BlockHeader::BlockHeaderV4(header) => &header.inner_rest.chunk_headers_root, + } + } + + #[inline] + pub fn chunk_tx_root(&self) -> &MerkleHash { + match self { + BlockHeader::BlockHeaderV1(header) => &header.inner_rest.chunk_tx_root, + BlockHeader::BlockHeaderV2(header) => &header.inner_rest.chunk_tx_root, + BlockHeader::BlockHeaderV3(header) => &header.inner_rest.chunk_tx_root, + BlockHeader::BlockHeaderV4(header) => &header.inner_rest.chunk_tx_root, + } + } + + pub fn chunks_included(&self) -> u64 { + let mask = match self { + BlockHeader::BlockHeaderV1(header) => return header.inner_rest.chunks_included, + BlockHeader::BlockHeaderV2(header) => &header.inner_rest.chunk_mask, + BlockHeader::BlockHeaderV3(header) => &header.inner_rest.chunk_mask, + BlockHeader::BlockHeaderV4(header) => &header.inner_rest.chunk_mask, + }; + mask.iter().map(|&x| u64::from(x)).sum::() + } + + #[inline] + pub fn challenges_root(&self) -> &MerkleHash { + match self { + BlockHeader::BlockHeaderV1(header) => &header.inner_rest.challenges_root, + BlockHeader::BlockHeaderV2(header) => &header.inner_rest.challenges_root, + BlockHeader::BlockHeaderV3(header) => &header.inner_rest.challenges_root, + BlockHeader::BlockHeaderV4(header) => &header.inner_rest.challenges_root, + } + } + + #[inline] + pub fn outcome_root(&self) -> &MerkleHash { + match self { + BlockHeader::BlockHeaderV1(header) => &header.inner_lite.prev_outcome_root, + BlockHeader::BlockHeaderV2(header) => &header.inner_lite.prev_outcome_root, + BlockHeader::BlockHeaderV3(header) => &header.inner_lite.prev_outcome_root, + BlockHeader::BlockHeaderV4(header) => &header.inner_lite.prev_outcome_root, + } + } + + #[inline] + pub fn block_body_hash(&self) -> Option { + match self { + BlockHeader::BlockHeaderV1(_) => None, + BlockHeader::BlockHeaderV2(_) => None, + BlockHeader::BlockHeaderV3(_) => None, + BlockHeader::BlockHeaderV4(header) => Some(header.inner_rest.block_body_hash), + } + } + + #[inline] + pub fn raw_timestamp(&self) -> u64 { + match self { + BlockHeader::BlockHeaderV1(header) => header.inner_lite.timestamp, + BlockHeader::BlockHeaderV2(header) => header.inner_lite.timestamp, + BlockHeader::BlockHeaderV3(header) => header.inner_lite.timestamp, + BlockHeader::BlockHeaderV4(header) => header.inner_lite.timestamp, + } + } + + #[inline] + pub fn prev_validator_power_proposals(&self) -> ValidatorPowerIter { + match self { + BlockHeader::BlockHeaderV1(header) => { + ValidatorPowerIter::v1(&header.inner_rest.prev_validator_power_proposals) + } + BlockHeader::BlockHeaderV2(header) => { + ValidatorPowerIter::v1(&header.inner_rest.prev_validator_power_proposals) + } + BlockHeader::BlockHeaderV3(header) => { + ValidatorPowerIter::new(&header.inner_rest.prev_validator_power_proposals) + } + BlockHeader::BlockHeaderV4(header) => { + ValidatorPowerIter::new(&header.inner_rest.prev_validator_power_proposals) + } + } + } + + #[inline] + pub fn prev_validator_frozen_proposals(&self) -> ValidatorFrozenIter { + match self { + BlockHeader::BlockHeaderV1(header) => { + ValidatorFrozenIter::v1(&header.inner_rest.prev_validator_frozen_proposals) + } + BlockHeader::BlockHeaderV2(header) => { + ValidatorFrozenIter::v1(&header.inner_rest.prev_validator_frozen_proposals) + } + BlockHeader::BlockHeaderV3(header) => { + ValidatorFrozenIter::new(&header.inner_rest.prev_validator_frozen_proposals) + } + BlockHeader::BlockHeaderV4(header) => { + ValidatorFrozenIter::new(&header.inner_rest.prev_validator_frozen_proposals) + } + } + } + + #[inline] + pub fn chunk_mask(&self) -> &[bool] { + match self { + BlockHeader::BlockHeaderV1(header) => &header.inner_rest.chunk_mask, + BlockHeader::BlockHeaderV2(header) => &header.inner_rest.chunk_mask, + BlockHeader::BlockHeaderV3(header) => &header.inner_rest.chunk_mask, + BlockHeader::BlockHeaderV4(header) => &header.inner_rest.chunk_mask, + } + } + + #[inline] + pub fn block_ordinal(&self) -> NumBlocks { + match self { + BlockHeader::BlockHeaderV1(_) => 0, // not applicable + BlockHeader::BlockHeaderV2(_) => 0, // not applicable + BlockHeader::BlockHeaderV3(header) => header.inner_rest.block_ordinal, + BlockHeader::BlockHeaderV4(header) => header.inner_rest.block_ordinal, + } + } + + #[inline] + pub fn next_gas_price(&self) -> Balance { + match self { + BlockHeader::BlockHeaderV1(header) => header.inner_rest.next_gas_price, + BlockHeader::BlockHeaderV2(header) => header.inner_rest.next_gas_price, + BlockHeader::BlockHeaderV3(header) => header.inner_rest.next_gas_price, + BlockHeader::BlockHeaderV4(header) => header.inner_rest.next_gas_price, + } + } + + #[inline] + pub fn total_supply(&self) -> Balance { + match self { + BlockHeader::BlockHeaderV1(header) => header.inner_rest.total_supply, + BlockHeader::BlockHeaderV2(header) => header.inner_rest.total_supply, + BlockHeader::BlockHeaderV3(header) => header.inner_rest.total_supply, + BlockHeader::BlockHeaderV4(header) => header.inner_rest.total_supply, + } + } + + #[inline] + pub fn random_value(&self) -> &CryptoHash { + match self { + BlockHeader::BlockHeaderV1(header) => &header.inner_rest.random_value, + BlockHeader::BlockHeaderV2(header) => &header.inner_rest.random_value, + BlockHeader::BlockHeaderV3(header) => &header.inner_rest.random_value, + BlockHeader::BlockHeaderV4(header) => &header.inner_rest.random_value, + } + } + + #[inline] + pub fn last_final_block(&self) -> &CryptoHash { + match self { + BlockHeader::BlockHeaderV1(header) => &header.inner_rest.last_final_block, + BlockHeader::BlockHeaderV2(header) => &header.inner_rest.last_final_block, + BlockHeader::BlockHeaderV3(header) => &header.inner_rest.last_final_block, + BlockHeader::BlockHeaderV4(header) => &header.inner_rest.last_final_block, + } + } + + #[inline] + pub fn last_ds_final_block(&self) -> &CryptoHash { + match self { + BlockHeader::BlockHeaderV1(header) => &header.inner_rest.last_ds_final_block, + BlockHeader::BlockHeaderV2(header) => &header.inner_rest.last_ds_final_block, + BlockHeader::BlockHeaderV3(header) => &header.inner_rest.last_ds_final_block, + BlockHeader::BlockHeaderV4(header) => &header.inner_rest.last_ds_final_block, + } + } + + #[inline] + pub fn challenges_result(&self) -> &ChallengesResult { + match self { + BlockHeader::BlockHeaderV1(header) => &header.inner_rest.challenges_result, + BlockHeader::BlockHeaderV2(header) => &header.inner_rest.challenges_result, + BlockHeader::BlockHeaderV3(header) => &header.inner_rest.challenges_result, + BlockHeader::BlockHeaderV4(header) => &header.inner_rest.challenges_result, + } + } + + #[inline] + pub fn next_bp_hash(&self) -> &CryptoHash { + match self { + BlockHeader::BlockHeaderV1(header) => &header.inner_lite.next_bp_hash, + BlockHeader::BlockHeaderV2(header) => &header.inner_lite.next_bp_hash, + BlockHeader::BlockHeaderV3(header) => &header.inner_lite.next_bp_hash, + BlockHeader::BlockHeaderV4(header) => &header.inner_lite.next_bp_hash, + } + } + + #[inline] + pub fn block_merkle_root(&self) -> &CryptoHash { + match self { + BlockHeader::BlockHeaderV1(header) => &header.inner_lite.block_merkle_root, + BlockHeader::BlockHeaderV2(header) => &header.inner_lite.block_merkle_root, + BlockHeader::BlockHeaderV3(header) => &header.inner_lite.block_merkle_root, + BlockHeader::BlockHeaderV4(header) => &header.inner_lite.block_merkle_root, + } + } + + #[inline] + pub fn epoch_sync_data_hash(&self) -> Option { + match self { + BlockHeader::BlockHeaderV1(_) => None, + BlockHeader::BlockHeaderV2(_) => None, + BlockHeader::BlockHeaderV3(header) => header.inner_rest.epoch_sync_data_hash, + BlockHeader::BlockHeaderV4(header) => header.inner_rest.epoch_sync_data_hash, + } + } + + #[inline] + pub fn approvals(&self) -> &[Option>] { + match self { + BlockHeader::BlockHeaderV1(header) => &header.inner_rest.approvals, + BlockHeader::BlockHeaderV2(header) => &header.inner_rest.approvals, + BlockHeader::BlockHeaderV3(header) => &header.inner_rest.approvals, + BlockHeader::BlockHeaderV4(header) => &header.inner_rest.approvals, + } + } + + /// Verifies that given public key produced the block. + pub fn verify_block_producer(&self, public_key: &PublicKey) -> bool { + self.signature().verify(self.hash().as_ref(), public_key) + } + + pub fn timestamp(&self) -> DateTime { + from_timestamp(self.raw_timestamp()) + } + + pub fn num_approvals(&self) -> u64 { + self.approvals().iter().filter(|x| x.is_some()).count() as u64 + } + + pub fn verify_chunks_included(&self) -> bool { + match self { + BlockHeader::BlockHeaderV1(header) => { + header.inner_rest.chunk_mask.iter().map(|&x| u64::from(x)).sum::() + == header.inner_rest.chunks_included + } + BlockHeader::BlockHeaderV2(_header) => true, + BlockHeader::BlockHeaderV3(_header) => true, + BlockHeader::BlockHeaderV4(_header) => true, + } + } + + #[inline] + pub fn latest_protocol_version(&self) -> u32 { + match self { + BlockHeader::BlockHeaderV1(header) => header.inner_rest.latest_protocol_version, + BlockHeader::BlockHeaderV2(header) => header.inner_rest.latest_protocol_version, + BlockHeader::BlockHeaderV3(header) => header.inner_rest.latest_protocol_version, + BlockHeader::BlockHeaderV4(header) => header.inner_rest.latest_protocol_version, + } + } + + pub fn inner_lite_bytes(&self) -> Vec { + match self { + BlockHeader::BlockHeaderV1(header) => { + borsh::to_vec(&header.inner_lite).expect("Failed to serialize") + } + BlockHeader::BlockHeaderV2(header) => { + borsh::to_vec(&header.inner_lite).expect("Failed to serialize") + } + BlockHeader::BlockHeaderV3(header) => { + borsh::to_vec(&header.inner_lite).expect("Failed to serialize") + } + BlockHeader::BlockHeaderV4(header) => { + borsh::to_vec(&header.inner_lite).expect("Failed to serialize") + } + } + } + + pub fn inner_rest_bytes(&self) -> Vec { + match self { + BlockHeader::BlockHeaderV1(header) => { + borsh::to_vec(&header.inner_rest).expect("Failed to serialize") + } + BlockHeader::BlockHeaderV2(header) => { + borsh::to_vec(&header.inner_rest).expect("Failed to serialize") + } + BlockHeader::BlockHeaderV3(header) => { + borsh::to_vec(&header.inner_rest).expect("Failed to serialize") + } + BlockHeader::BlockHeaderV4(header) => { + borsh::to_vec(&header.inner_rest).expect("Failed to serialize") + } + } + } +} diff --git a/core/primitives/src/challenge.rs b/core/primitives/src/challenge.rs new file mode 100644 index 000000000..35576470c --- /dev/null +++ b/core/primitives/src/challenge.rs @@ -0,0 +1,143 @@ +use crate::hash::CryptoHash; +use crate::merkle::MerklePath; +use crate::sharding::{EncodedShardChunk, ShardChunk, ShardChunkHeader}; +use crate::types::AccountId; +use crate::validator_signer::ValidatorSigner; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_crypto::Signature; + +/// Serialized TrieNodeWithSize or state value. +pub type TrieValue = std::sync::Arc<[u8]>; + +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, Eq, PartialEq)] +/// TODO (#8984): consider supporting format containing trie values only for +/// state part boundaries and storing state items for state part range. +pub enum PartialState { + /// State represented by the set of unique trie values (`RawTrieNodeWithSize`s and state values). + TrieValues(Vec), +} + +impl Default for PartialState { + fn default() -> Self { + PartialState::TrieValues(vec![]) + } +} + +impl PartialState { + pub fn len(&self) -> usize { + let Self::TrieValues(values) = self; + values.len() + } +} + +/// Double signed block. +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)] +pub struct BlockDoubleSign { + pub left_block_header: Vec, + pub right_block_header: Vec, +} + +impl std::fmt::Display for BlockDoubleSign { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "{:?}", self) + } +} + +/// Invalid chunk (body of the chunk doesn't match proofs or invalid encoding). +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)] +pub struct ChunkProofs { + /// Encoded block header that contains invalid chunk. + pub block_header: Vec, + /// Merkle proof of inclusion of this chunk. + pub merkle_proof: MerklePath, + /// Invalid chunk in an encoded form or in a decoded form. + pub chunk: Box, +} + +/// Either `EncodedShardChunk` or `ShardChunk`. Used for `ChunkProofs`. +/// `Decoded` is used to avoid re-encoding an already decoded chunk to construct a challenge. +/// `Encoded` is still needed in case a challenge challenges an invalid encoded chunk that can't be +/// decoded. +#[allow(clippy::large_enum_variant)] // both variants are large +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)] +pub enum MaybeEncodedShardChunk { + Encoded(EncodedShardChunk), + Decoded(ShardChunk), +} + +/// Doesn't match post-{state root, outgoing receipts, gas used, etc} results after applying previous chunk. +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)] +pub struct ChunkState { + /// Encoded prev block header. + pub prev_block_header: Vec, + /// Encoded block header that contains invalid chunnk. + pub block_header: Vec, + /// Merkle proof in inclusion of prev chunk. + pub prev_merkle_proof: MerklePath, + /// Previous chunk that contains transactions. + pub prev_chunk: ShardChunk, + /// Merkle proof of inclusion of this chunk. + pub merkle_proof: MerklePath, + /// Invalid chunk header. + pub chunk_header: ShardChunkHeader, + /// Partial state that was affected by transactions of given chunk. + pub partial_state: PartialState, +} + +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)] +// TODO(#1313): Use Box +#[allow(clippy::large_enum_variant)] +pub enum ChallengeBody { + BlockDoubleSign(BlockDoubleSign), + ChunkProofs(ChunkProofs), + ChunkState(ChunkState), +} + +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug)] +#[borsh(init=init)] +pub struct Challenge { + pub body: ChallengeBody, + pub account_id: AccountId, + pub signature: Signature, + + #[borsh(skip)] + pub hash: CryptoHash, +} + +impl Challenge { + pub fn init(&mut self) { + self.hash = CryptoHash::hash_borsh(&self.body); + } + + pub fn produce(body: ChallengeBody, signer: &dyn ValidatorSigner) -> Self { + let (hash, signature) = signer.sign_challenge(&body); + Self { body, account_id: signer.validator_id().clone(), signature, hash } + } +} + +pub type Challenges = Vec; + +#[derive( + BorshSerialize, + BorshDeserialize, + PartialEq, + Eq, + Clone, + Debug, + serde::Serialize, + serde::Deserialize, +)] +pub struct SlashedValidator { + pub account_id: AccountId, + pub is_double_sign: bool, +} + +impl SlashedValidator { + pub fn new(account_id: AccountId, is_double_sign: bool) -> Self { + SlashedValidator { account_id, is_double_sign } + } +} + +/// Result of checking challenge, contains which accounts to slash. +/// If challenge is invalid this is sender, otherwise author of chunk (and possibly other participants that signed invalid blocks). +pub type ChallengesResult = Vec; diff --git a/core/primitives/src/chunk_validation.rs b/core/primitives/src/chunk_validation.rs new file mode 100644 index 000000000..63541abab --- /dev/null +++ b/core/primitives/src/chunk_validation.rs @@ -0,0 +1,138 @@ +use std::collections::HashMap; + +use crate::challenge::PartialState; +use crate::sharding::{ChunkHash, ReceiptProof, ShardChunkHeader}; +use crate::transaction::SignedTransaction; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_crypto::Signature; +use unc_primitives_core::hash::CryptoHash; +use unc_primitives_core::types::AccountId; + +/// The state witness for a chunk; proves the state transition that the +/// chunk attests to. +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct ChunkStateWitness { + /// The chunk header that this witness is for. While this is not needed + /// to apply the state transition, it is needed for a chunk validator to + /// produce a chunk endorsement while knowing what they are endorsing. + pub chunk_header: ShardChunkHeader, + /// The base state and post-state-root of the main transition where we + /// apply transactions and receipts. Corresponds to the state transition + /// that takes us from the pre-state-root of the last new chunk of this + /// shard to the post-state-root of that same chunk. + pub main_state_transition: ChunkStateTransition, + /// For the main state transition, we apply transactions and receipts. + /// Exactly which of them must be applied is a deterministic property + /// based on the blockchain history this chunk is based on. + /// + /// The set of receipts is exactly + /// Filter(R, |receipt| receipt.target_shard = S), where + /// - R is the set of outgoing receipts included in the set of chunks C + /// (defined below), + /// - S is the shard of this chunk. + /// + /// The set of chunks C, from which the receipts are sourced, is defined as + /// all new chunks included in the set of blocks B. + /// + /// The set of blocks B is defined as the contiguous subsequence of blocks + /// B1 (EXCLUSIVE) to B2 (inclusive) in this chunk's chain (i.e. the linear + /// chain that this chunk's parent block is on), where B1 is the block that + /// contains the last new chunk of shard S before this chunk, and B1 is the + /// block that contains the last new chunk of shard S before B2. + /// + /// Furthermore, the set of transactions to apply is exactly the + /// transactions included in the chunk of shard S at B2. + /// + /// For the purpose of this text, a "new chunk" is defined as a chunk that + /// is proposed by a chunk producer, not one that was copied from the + /// previous block (commonly called a "missing chunk"). + /// + /// This field, `source_receipt_proofs`, is a (non-strict) superset of the + /// receipts that must be applied, along with information that allows these + /// receipts to be verifiable against the blockchain history. + pub source_receipt_proofs: HashMap, + /// An overall hash of the list of receipts that should be applied. This is + /// redundant information but is useful for diagnosing why a witness might + /// fail. This is the hash of the borsh encoding of the Vec in the + /// order that they should be applied. + pub exact_receipts_hash: CryptoHash, + /// The transactions to apply. These must be in the correct order in which + /// they are to be applied. + pub transactions: Vec, + /// For each missing chunk after the last new chunk of the shard, we need + /// to carry out an implicit state transition. Mostly, this is for + /// distributing validator rewards. This list contains one for each such + /// chunk, in forward chronological order. + /// + /// After these are applied as well, we should arrive at the pre-state-root + /// of the chunk that this witness is for. + pub implicit_transitions: Vec, + /// Finally, we need to be able to verify that the new transitions proposed + /// by the chunk (that this witness is for) are valid. For that, we need + /// the transactions as well as another partial storage (based on the + /// pre-state-root of this chunk) in order to verify that the sender + /// accounts have appropriate balances, access keys, nonces, etc. + pub new_transactions: Vec, + pub new_transactions_validation_state: PartialState, +} + +/// Represents the base state and the expected post-state-root of a chunk's state +/// transition. The actual state transition itself is not included here. +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct ChunkStateTransition { + /// The block that contains the chunk; this identifies which part of the + /// state transition we're talking about. + pub block_hash: CryptoHash, + /// The partial state before the state transition. This includes whatever + /// initial state that is necessary to compute the state transition for this + /// chunk. + pub base_state: PartialState, + /// The expected final state root after applying the state transition. + /// This is redundant information, because the post state root can be + /// derived by applying the state transition onto the base state, but + /// this makes it easier to debug why a state witness may fail to validate. + pub post_state_root: CryptoHash, +} + +/// The endorsement of a chunk by a chunk validator. By providing this, a +/// chunk validator has verified that the chunk state witness is correct. +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct ChunkEndorsement { + pub inner: ChunkEndorsementInner, + pub account_id: AccountId, + pub signature: Signature, +} + +/// This is the part of the chunk endorsement that is actually being signed. +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct ChunkEndorsementInner { + pub chunk_hash: ChunkHash, + /// An arbitrary static string to make sure that this struct cannot be + /// serialized to look identical to another serialized struct. For chunk + /// production we are signing a chunk hash, so we need to make sure that + /// this signature means something different. + /// + /// This is a messy workaround until we know what to do with NEP 483. + signature_differentiator: String, +} + +impl ChunkEndorsementInner { + pub fn new(chunk_hash: ChunkHash) -> Self { + Self { chunk_hash, signature_differentiator: "ChunkEndorsement".to_owned() } + } +} + +/// Stored on disk for each chunk, including missing chunks, in order to +/// produce a chunk state witness when needed. +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct StoredChunkStateTransitionData { + /// The partial state that is needed to apply the state transition, + /// whether it is a new chunk state transition or a implicit missing chunk + /// state transition. + pub base_state: PartialState, + /// If this is a new chunk state transition, the hash of the receipts that + /// were used to apply the state transition. This is redundant information, + /// but is used to validate against `StateChunkWitness::exact_receipts_hash` + /// to ease debugging of why a state witness may be incorrect. + pub receipts_hash: CryptoHash, +} diff --git a/core/primitives/src/epoch_manager.rs b/core/primitives/src/epoch_manager.rs new file mode 100644 index 000000000..378a2114b --- /dev/null +++ b/core/primitives/src/epoch_manager.rs @@ -0,0 +1,2072 @@ +use crate::challenge::SlashedValidator; +use crate::num_rational::Rational32; +use crate::shard_layout::ShardLayout; +use crate::types::validator_power_and_frozen::{ValidatorPowerAndFrozen, ValidatorPowerAndFrozenV1}; +use crate::types::validator_power::{ValidatorPower, ValidatorPowerV1}; +use crate::types::validator_frozen::{ValidatorFrozen, ValidatorFrozenV1}; + +use crate::types::{ + AccountId, Balance, BlockHeightDelta, EpochHeight, EpochId, NumSeats, ProtocolVersion, + ValidatorId, ValidatorKickoutReason, +}; +use crate::version::PROTOCOL_VERSION; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_primitives_core::checked_feature; +use unc_primitives_core::hash::CryptoHash; +use unc_primitives_core::types::{BlockHeight, Power}; +use smart_default::SmartDefault; +use std::collections::{BTreeMap, HashMap}; +use crate::validator_mandates::ValidatorMandates; + +pub type RngSeed = [u8; 32]; + +pub const AGGREGATOR_KEY: &[u8] = b"AGGREGATOR"; + + +/// +/// +/// +#[derive(Clone, Eq, Debug, PartialEq)] +pub struct BlockConfig { + /// Number of seats for block producers. + pub num_block_producer_seats: NumSeats, + /// Number of seats of block producers per each shard. + pub num_block_producer_seats_per_shard: Vec, + /// Expected number of hidden validator seats per each shard. + pub avg_hidden_validator_seats_per_shard: Vec, + /// Criterion for kicking out block producers. + pub block_producer_kickout_threshold: u8, + /// Criterion for kicking out chunk producers. + pub chunk_producer_kickout_threshold: u8, + /// Max ratio of validators that we can kick out in an epoch + pub validator_max_kickout_stake_perc: u8, + /// Online minimum threshold below which validator doesn't receive reward. + pub online_min_threshold: Rational32, + /// Online maximum threshold above which validator gets full reward. + pub online_max_threshold: Rational32, + /// Stake threshold for becoming a fisherman. + pub fishermen_threshold: Balance, + /// The minimum stake required for staking is last seat price divided by this number. + pub minimum_stake_divisor: u64, + /// Threshold of stake that needs to indicate that they ready for upgrade. + pub protocol_upgrade_stake_threshold: Rational32, + /// Shard layout of this epoch, may change from epoch to epoch + pub shard_layout: ShardLayout, + /// Additional config for validator selection algorithm + pub validator_selection_config: ValidatorSelectionConfig, +} +/// Epoch config, determines validator assignment for given epoch. +/// Can change from epoch to epoch depending on the sharding and other parameters, etc. +#[derive(Clone, Eq, Debug, PartialEq)] +pub struct EpochConfig { + /// Epoch length in block heights. + pub epoch_length: BlockHeightDelta, + /// Number of seats for block producers. + pub num_block_producer_seats: NumSeats, + /// Number of seats of block producers per each shard. + pub num_block_producer_seats_per_shard: Vec, + /// Expected number of hidden validator seats per each shard. + pub avg_hidden_validator_seats_per_shard: Vec, + /// Criterion for kicking out block producers. + pub block_producer_kickout_threshold: u8, + /// Criterion for kicking out chunk producers. + pub chunk_producer_kickout_threshold: u8, + /// Max ratio of validators that we can kick out in an epoch + pub validator_max_kickout_stake_perc: u8, + /// Online minimum threshold below which validator doesn't receive reward. + pub online_min_threshold: Rational32, + /// Online maximum threshold above which validator gets full reward. + pub online_max_threshold: Rational32, + /// Stake threshold for becoming a fisherman. + pub fishermen_threshold: Balance, + /// The minimum stake required for staking is last seat price divided by this number. + pub minimum_stake_divisor: u64, + /// Threshold of stake that needs to indicate that they ready for upgrade. + pub protocol_upgrade_stake_threshold: Rational32, + /// Shard layout of this epoch, may change from epoch to epoch + pub shard_layout: ShardLayout, + /// Additional config for validator selection algorithm + pub validator_selection_config: ValidatorSelectionConfig, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct ShardConfig { + pub num_block_producer_seats_per_shard: Vec, + pub avg_hidden_validator_seats_per_shard: Vec, + pub shard_layout: ShardLayout, +} + +impl ShardConfig { + pub fn new(epoch_config: EpochConfig) -> Self { + Self { + num_block_producer_seats_per_shard: epoch_config + .num_block_producer_seats_per_shard + .clone(), + avg_hidden_validator_seats_per_shard: epoch_config + .avg_hidden_validator_seats_per_shard + .clone(), + shard_layout: epoch_config.shard_layout, + } + } +} + +/// Testing overrides to apply to the EpochConfig returned by the `for_protocol_version`. +/// All fields should be optional and the default should be a no-op. +#[derive(Clone, Default)] +pub struct AllEpochConfigTestOverrides { + pub block_producer_kickout_threshold: Option, + pub chunk_producer_kickout_threshold: Option, +} + +/// AllEpochConfig manages protocol configs that might be changing throughout epochs (hence EpochConfig). +/// The main function in AllEpochConfig is ::for_protocol_version which takes a protocol version +/// and returns the EpochConfig that should be used for this protocol version. +#[derive(Clone)] +pub struct AllEpochConfig { + /// Whether this is for production (i.e., mainnet or testnet). This is a temporary implementation + /// to allow us to change protocol config for mainnet and testnet without changing the genesis config + use_production_config: bool, + /// EpochConfig from genesis + genesis_epoch_config: EpochConfig, + /// Chain Id. Some parameters are specific to certain chains. + chain_id: String, + + /// Testing overrides to apply to the EpochConfig returned by the `for_protocol_version`. + test_overrides: AllEpochConfigTestOverrides, +} + +impl AllEpochConfig { + pub fn new( + use_production_config: bool, + genesis_epoch_config: EpochConfig, + chain_id: &str, + ) -> Self { + Self { + use_production_config, + genesis_epoch_config, + chain_id: chain_id.to_string(), + test_overrides: AllEpochConfigTestOverrides::default(), + } + } + + pub fn new_with_test_overrides( + use_production_config: bool, + genesis_epoch_config: EpochConfig, + chain_id: &str, + test_overrides: Option, + ) -> Self { + Self { + use_production_config, + genesis_epoch_config, + chain_id: chain_id.to_string(), + test_overrides: test_overrides.unwrap_or_default(), + } + } + + pub fn for_protocol_version(&self, protocol_version: ProtocolVersion) -> EpochConfig { + let mut config = self.genesis_epoch_config.clone(); + if !self.use_production_config { + return config; + } + + Self::config_nightshade(&mut config, protocol_version); + + Self::config_chunk_only_producers(&mut config, &self.chain_id, protocol_version); + + Self::config_max_kickout_stake(&mut config, protocol_version); + + Self::config_test_overrides(&mut config, &self.test_overrides); + + config + } + + fn config_nightshade(config: &mut EpochConfig, _protocol_version: ProtocolVersion) { + Self::config_nightshade_impl(config, ShardLayout::v0_single_shard()); + } + + fn config_nightshade_impl(config: &mut EpochConfig, shard_layout: ShardLayout) { + let num_block_producer_seats = config.num_block_producer_seats; + config.num_block_producer_seats_per_shard = + shard_layout.shard_ids().map(|_| num_block_producer_seats).collect(); + config.avg_hidden_validator_seats_per_shard = shard_layout.shard_ids().map(|_| 0).collect(); + config.shard_layout = shard_layout; + } + + fn config_chunk_only_producers( + config: &mut EpochConfig, + chain_id: &str, + protocol_version: u32, + ) { + if checked_feature!("stable", ChunkOnlyProducers, protocol_version) { + // On testnet, genesis config set num_block_producer_seats to 200 + // This is to bring it back to 100 to be the same as on mainnet + config.num_block_producer_seats = 100; + // Technically, after ChunkOnlyProducers is enabled, this field is no longer used + // We still set it here just in case + config.num_block_producer_seats_per_shard = + config.shard_layout.shard_ids().map(|_| 100).collect(); + config.block_producer_kickout_threshold = 80; + config.chunk_producer_kickout_threshold = 80; + config.validator_selection_config.num_chunk_only_producer_seats = 200; + } + + // Adjust the number of block and chunk producers for all chains except + // mainnet, to make it easier to test the change. + if chain_id != unc_primitives_core::chains::MAINNET + && checked_feature!("stable", TestnetFewerBlockProducers, protocol_version) + { + let shard_ids = config.shard_layout.shard_ids(); + // Decrease the number of block producers from 100 to 20. + config.num_block_producer_seats = 20; + config.num_block_producer_seats_per_shard = + shard_ids.map(|_| config.num_block_producer_seats).collect(); + // Decrease the number of chunk producers. + config.validator_selection_config.num_chunk_only_producer_seats = 100; + } + } + + fn config_max_kickout_stake(config: &mut EpochConfig, protocol_version: u32) { + if checked_feature!("stable", MaxKickoutStake, protocol_version) { + config.validator_max_kickout_stake_perc = 30; + } + } + + fn config_test_overrides( + config: &mut EpochConfig, + test_overrides: &AllEpochConfigTestOverrides, + ) { + if let Some(block_producer_kickout_threshold) = + test_overrides.block_producer_kickout_threshold + { + config.block_producer_kickout_threshold = block_producer_kickout_threshold; + } + + if let Some(chunk_producer_kickout_threshold) = + test_overrides.chunk_producer_kickout_threshold + { + config.chunk_producer_kickout_threshold = chunk_producer_kickout_threshold; + } + } +} + +/// Additional configuration parameters for the new validator selection +/// algorithm. See for details. +#[derive(Debug, Clone, SmartDefault, PartialEq, Eq)] +pub struct ValidatorSelectionConfig { + #[default(300)] + pub num_chunk_only_producer_seats: NumSeats, + #[default(1)] + pub minimum_validators_per_shard: NumSeats, + #[default(Rational32::new(160, 1_000_000))] + pub minimum_stake_ratio: Rational32, +} + +pub mod block_summary { + use std::collections::{BTreeMap, HashMap}; + use borsh::{BorshDeserialize, BorshSerialize}; + use unc_primitives_core::hash::CryptoHash; + use unc_primitives_core::types::{Balance, Power, ValidatorId}; + use crate::types::{AccountId, ValidatorKickoutReason}; + use crate::types::validator_frozen::ValidatorFrozen; + use crate::types::validator_power::ValidatorPower; + use crate::types::validator_power_and_frozen::{ValidatorPowerAndFrozen, ValidatorPowerAndFrozenIter}; + use crate::validator_mandates::ValidatorMandates; + + #[derive(BorshSerialize, BorshDeserialize, Eq, PartialEq, Clone, Debug, serde::Serialize)] + pub enum BlockSummary { + V1(BlockSummaryV1), + } + #[derive(Default, Eq, PartialEq, BorshSerialize, Clone, Debug, BorshDeserialize, serde::Serialize)] + pub struct BlockSummaryV1 { + pub this_block_hash: CryptoHash, + pub prev_block_hash: CryptoHash, + pub random_value: CryptoHash, + pub validators: Vec, + pub validator_to_index: HashMap, + pub block_producers_settlement: Vec, + pub chunk_producers_settlement: Vec>, + pub fishermen: Vec, + pub fishermen_to_index: HashMap, + pub power_change: BTreeMap, + pub frozen_change: BTreeMap, + pub validator_reward: HashMap, + pub seat_price: Balance, + pub minted_amount: Balance, + /// Power proposals from the block, only the latest one per account + pub all_power_proposals: Vec, + /// Frozen proposals from the block, only the latest one per account + pub all_frozen_proposals: Vec, + /// Kickout set, includes slashed + pub validator_kickout: HashMap, + /// Only for validators who met the threshold and didn't get slashed + pub validator_mandates: ValidatorMandates, + } + impl Default for BlockSummary { + fn default() -> Self { + Self::V1(BlockSummaryV1::default()) + } + } + impl BlockSummary { + + pub fn new( + this_block_hash: CryptoHash, + prev_block_hash: CryptoHash, + random_value: CryptoHash, + validators: Vec, + validator_to_index: HashMap, + block_producers_settlement: Vec, + chunk_producers_settlement: Vec>, + fishermen: Vec, + fishermen_to_index: HashMap, + power_change: BTreeMap, + frozen_change: BTreeMap, + validator_reward: HashMap, + seat_price: Balance, + minted_amount: Balance, + all_power_proposals: Vec, + all_frozen_proposals: Vec, + validator_kickout: HashMap, + validator_mandates: ValidatorMandates, + ) -> Self { + Self::V1(BlockSummaryV1 { + this_block_hash, + prev_block_hash, + random_value, + validators, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + fishermen, + fishermen_to_index, + power_change, + frozen_change, + validator_reward, + seat_price, + minted_amount, + all_power_proposals, + all_frozen_proposals, + validator_kickout, + validator_mandates, + }) + } + + #[inline] + pub fn block_hash(&self) -> CryptoHash { + match self { + Self::V1(v1) => v1.this_block_hash, + } + } + #[inline] + pub fn random_value(&self) -> &CryptoHash { + match self { + Self::V1(v1) => &v1.random_value, + } + } + + #[inline] + pub fn seat_price(&self) -> Balance { + match self { + Self::V1(v1) => v1.seat_price, + } + } + + #[inline] + pub fn minted_amount(&self) -> Balance { + match self { + Self::V1(v1) => v1.minted_amount, + } + } + + #[inline] + pub fn block_producers_settlement(&self) -> &[ValidatorId] { + match self { + Self::V1(v1) => &v1.block_producers_settlement, + } + } + + #[inline] + pub fn chunk_producers_settlement(&self) -> &[Vec] { + match self { + Self::V1(v1) => &v1.chunk_producers_settlement, + } + } + + #[inline] + pub fn validator_kickout(&self) -> &HashMap { + match self { + Self::V1(v1) => &v1.validator_kickout, + } + } + + #[inline] + pub fn frozen_change(&self) -> &BTreeMap { + match self { + Self::V1(v1) => &v1.frozen_change, + } + } + + #[inline] + pub fn power_change(&self) -> &BTreeMap { + match self { + Self::V1(v1) => &v1.power_change, + } + } + + #[inline] + pub fn validator_reward(&self) -> &HashMap { + match self { + Self::V1(v1) => &v1.validator_reward, + } + } + + #[inline] + pub fn validators_iter(&self) -> ValidatorPowerAndFrozenIter { + match self { + Self::V1(v1) => ValidatorPowerAndFrozenIter::new(&v1.validators), + } + } + + #[inline] + pub fn fishermen_iter(&self) -> ValidatorPowerAndFrozenIter { + match self { + Self::V1(v1) => ValidatorPowerAndFrozenIter::new(&v1.fishermen), + } + } + + #[inline] + pub fn validator_power(&self, validator_id: u64) -> Power { + match self { + Self::V1(v1) => v1.validators[validator_id as usize].power(), + } + } + + #[inline] + pub fn validator_frozen(&self, validator_id: u64) -> Balance { + match self { + Self::V1(v1) => v1.validators[validator_id as usize].frozen(), + } + } + + #[inline] + pub fn validator_account_id(&self, validator_id: u64) -> &AccountId { + match self { + Self::V1(v1) => v1.validators[validator_id as usize].account_id(), + } + } + + #[inline] + pub fn account_is_validator(&self, account_id: &AccountId) -> bool { + match self { + Self::V1(v1) => v1.validator_to_index.contains_key(account_id), + } + } + + pub fn get_validator_id(&self, account_id: &AccountId) -> Option<&ValidatorId> { + match self { + Self::V1(v1) => v1.validator_to_index.get(account_id), + } + } + + pub fn get_validator_by_account(&self, account_id: &AccountId) -> Option { + match self { + Self::V1(v1) => v1 + .validator_to_index + .get(account_id) + .map(|validator_id| v1.validators[*validator_id as usize].clone()), + } + } + + #[inline] + pub fn get_validator(&self, validator_id: u64) -> ValidatorPowerAndFrozen { + match self { + Self::V1(v1) => v1.validators[validator_id as usize].clone(), + } + } + + #[inline] + pub fn account_is_fisherman(&self, account_id: &AccountId) -> bool { + match self { + Self::V1(v1) => v1.fishermen_to_index.contains_key(account_id), + } + } + + pub fn get_fisherman_by_account(&self, account_id: &AccountId) -> Option { + match self { + Self::V1(v1) => v1 + .fishermen_to_index + .get(account_id) + .map(|validator_id| v1.fishermen[*validator_id as usize].clone()), + } + } + + #[inline] + pub fn get_fisherman(&self, fisherman_id: u64) -> ValidatorPowerAndFrozen { + match self { + Self::V1(v1) => v1.fishermen[fisherman_id as usize].clone(), + } + } + + #[inline] + pub fn validators_len(&self) -> usize { + match self { + Self::V1(v1) => v1.validators.len(), + } + } + + pub fn vrf_block_producer(&self, _random_value: &CryptoHash) -> ValidatorId { + return 0; + } + + + } + + +} +pub mod block_info { + use std::collections::{BTreeMap, HashMap}; + + use super::SlashState; + use crate::challenge::SlashedValidator; + use crate::types::validator_power::{ValidatorPower, ValidatorPowerIter}; + use crate::types::{EpochId, ValidatorKickoutReason}; + use borsh::{BorshDeserialize, BorshSerialize}; + use unc_primitives_core::hash::CryptoHash; + use unc_primitives_core::types::{AccountId, Balance, BlockHeight, Power, ProtocolVersion, ValidatorId}; + use crate::types::validator_frozen::{ValidatorFrozen, ValidatorFrozenIter}; + use crate::types::validator_power_and_frozen::{ValidatorPowerAndFrozen, ValidatorPowerAndFrozenIter}; + use crate::validator_mandates::ValidatorMandates; + pub use super::BlockInfoV1; + + /// Information per each block. + #[derive(BorshSerialize, BorshDeserialize, Eq, PartialEq, Clone, Debug, serde::Serialize)] + pub enum BlockInfo { + V1(BlockInfoV1), + V2(BlockInfoV2), + } + + impl Default for BlockInfo { + fn default() -> Self { + Self::V2(BlockInfoV2::default()) + } + } + + impl BlockInfo { + pub fn new( + hash: CryptoHash, + height: BlockHeight, + last_finalized_height: BlockHeight, + last_final_block_hash: CryptoHash, + prev_hash: CryptoHash, + power_proposals: Vec, + frozen_proposals: Vec, + validator_mask: Vec, + slashed: Vec, + total_supply: Balance, + latest_protocol_version: ProtocolVersion, + timestamp_nanosec: u64, + // start customized by James Savechives + random_value: CryptoHash, + validators: Vec, + validator_to_index: HashMap, + block_producers_settlement: Vec, + chunk_producers_settlement: Vec>, + fishermen: Vec, + fishermen_to_index: HashMap, + power_change: BTreeMap, + frozen_change: BTreeMap, + validator_reward: HashMap, + seat_price: Balance, + minted_amount: Balance, + all_power_proposals: Vec, + all_frozen_proposals: Vec, + validator_kickout: HashMap, + validator_mandates: ValidatorMandates, + ) -> Self { + Self::V2(BlockInfoV2 { + hash, + height, + last_finalized_height, + last_final_block_hash, + prev_hash, + power_proposals, + frozen_proposals, + chunk_mask: validator_mask, + latest_protocol_version, + slashed: slashed + .into_iter() + .map(|s| { + let slash_state = if s.is_double_sign { + SlashState::DoubleSign + } else { + SlashState::Other + }; + (s.account_id, slash_state) + }) + .collect(), + total_supply, + epoch_first_block: Default::default(), + epoch_id: Default::default(), + timestamp_nanosec, + // start customized by James Savechives + random_value, + validators, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + fishermen, + fishermen_to_index, + power_change, + frozen_change, + validator_reward, + seat_price, + minted_amount, + all_power_proposals, + all_frozen_proposals, + validator_kickout, + validator_mandates, + // end customized by James Savechives + }) + } + + #[inline] + pub fn power_proposals_iter(&self) -> ValidatorPowerIter { + match self { + Self::V1(v1) => ValidatorPowerIter::v1(&v1.power_proposals), + Self::V2(v2) => ValidatorPowerIter::new(&v2.power_proposals), + } + } + + #[inline] + pub fn frozen_proposals_iter(&self) -> ValidatorFrozenIter { + match self { + Self::V1(v1) => ValidatorFrozenIter::v1(&v1.frozen_proposals), + Self::V2(v2) => ValidatorFrozenIter::new(&v2.frozen_proposals), + } + } + #[inline] + pub fn hash(&self) -> &CryptoHash { + match self { + Self::V1(v1) => &v1.hash, + Self::V2(v2) => &v2.hash, + } + } + #[inline] + pub fn slashed(&self) -> &HashMap { + match self { + Self::V1(v1) => &v1.slashed, + Self::V2(v2) => &v2.slashed, + } + } + + #[inline] + pub fn slashed_mut(&mut self) -> &mut HashMap { + match self { + Self::V1(v1) => &mut v1.slashed, + Self::V2(v2) => &mut v2.slashed, + } + } + + #[inline] + pub fn height(&self) -> BlockHeight { + match self { + Self::V1(v1) => v1.height, + Self::V2(v2) => v2.height, + } + } + + #[inline] + pub fn last_finalized_height(&self) -> BlockHeight { + match self { + Self::V1(v1) => v1.last_finalized_height, + Self::V2(v2) => v2.last_finalized_height, + } + } + + #[inline] + pub fn last_final_block_hash(&self) -> &CryptoHash { + match self { + Self::V1(v1) => &v1.last_final_block_hash, + Self::V2(v2) => &v2.last_final_block_hash, + } + } + + #[inline] + pub fn prev_hash(&self) -> &CryptoHash { + match self { + Self::V1(v1) => &v1.prev_hash, + Self::V2(v2) => &v2.prev_hash, + } + } + + #[inline] + pub fn epoch_first_block(&self) -> &CryptoHash { + match self { + Self::V1(v1) => &v1.epoch_first_block, + Self::V2(v2) => &v2.epoch_first_block, + } + } + + #[inline] + pub fn epoch_first_block_mut(&mut self) -> &mut CryptoHash { + match self { + Self::V1(v1) => &mut v1.epoch_first_block, + Self::V2(v2) => &mut v2.epoch_first_block, + } + } + + #[inline] + pub fn epoch_id(&self) -> &EpochId { + match self { + Self::V1(v1) => &v1.epoch_id, + Self::V2(v2) => &v2.epoch_id, + } + } + + #[inline] + pub fn epoch_id_mut(&mut self) -> &mut EpochId { + match self { + Self::V1(v1) => &mut v1.epoch_id, + Self::V2(v2) => &mut v2.epoch_id, + } + } + + #[inline] + pub fn chunk_mask(&self) -> &[bool] { + match self { + Self::V1(v1) => &v1.chunk_mask, + Self::V2(v2) => &v2.chunk_mask, + } + } + + #[inline] + pub fn latest_protocol_version(&self) -> &ProtocolVersion { + match self { + Self::V1(v1) => &v1.latest_protocol_version, + Self::V2(v2) => &v2.latest_protocol_version, + } + } + + #[inline] + pub fn total_supply(&self) -> &Balance { + match self { + Self::V1(v1) => &v1.total_supply, + Self::V2(v2) => &v2.total_supply, + } + } + + #[inline] + pub fn timestamp_nanosec(&self) -> &u64 { + match self { + Self::V1(v1) => &v1.timestamp_nanosec, + Self::V2(v2) => &v2.timestamp_nanosec, + } + } + + /// start customized by James Savechives + #[inline] + pub fn random_value(&self) -> &CryptoHash { + match self { + Self::V1(v1) => &v1.random_value, + Self::V2(v2) => &v2.random_value, + } + } + + #[inline] + pub fn seat_price(&self) -> Balance { + match self { + Self::V1(v1) => v1.seat_price, + Self::V2(v2) => v2.seat_price, + } + } + + #[inline] + pub fn minted_amount(&self) -> Balance { + match self { + Self::V1(v1) => v1.minted_amount, + Self::V2(v2) => v2.minted_amount, + } + } + + #[inline] + pub fn block_producers_settlement(&self) -> &[ValidatorId] { + match self { + Self::V1(v1) => &v1.block_producers_settlement, + Self::V2(v2) => &v2.block_producers_settlement, + } + } + + #[inline] + pub fn chunk_producers_settlement(&self) -> &[Vec] { + match self { + Self::V1(v1) => &v1.chunk_producers_settlement, + Self::V2(v2) => &v2.chunk_producers_settlement, + } + } + + #[inline] + pub fn validator_kickout(&self) -> &HashMap { + match self { + Self::V1(v1) => &v1.validator_kickout, + Self::V2(v2) => &v2.validator_kickout, + } + } + + #[inline] + pub fn frozen_change(&self) -> &BTreeMap { + match self { + Self::V1(v1) => &v1.frozen_change, + Self::V2(v2) => &v2.frozen_change, + } + } + + #[inline] + pub fn power_change(&self) -> &BTreeMap { + match self { + Self::V1(v1) => &v1.power_change, + Self::V2(v2) => &v2.power_change, + } + } + + #[inline] + pub fn validator_reward(&self) -> &HashMap { + match self { + Self::V1(v1) => &v1.validator_reward, + Self::V2(v2) => &v2.validator_reward, + } + } + + #[inline] + pub fn validators_iter(&self) -> ValidatorPowerAndFrozenIter { + match self { + Self::V1(v1) => ValidatorPowerAndFrozenIter::new(&v1.validators), + Self::V2(v2) => ValidatorPowerAndFrozenIter::new(&v2.validators), + } + } + + #[inline] + pub fn fishermen_iter(&self) -> ValidatorPowerAndFrozenIter { + match self { + Self::V1(v1) => ValidatorPowerAndFrozenIter::new(&v1.fishermen), + Self::V2(v2) => ValidatorPowerAndFrozenIter::new(&v2.fishermen), + } + } + + #[inline] + pub fn validator_power(&self, validator_id: u64) -> Power { + match self { + Self::V1(v1) => v1.validators[validator_id as usize].power(), + Self::V2(v2) => v2.validators[validator_id as usize].power(), + } + } + + #[inline] + pub fn validator_frozen(&self, validator_id: u64) -> Balance { + match self { + Self::V1(v1) => v1.validators[validator_id as usize].frozen(), + Self::V2(v2) => v2.validators[validator_id as usize].frozen(), + } + } + + #[inline] + pub fn validator_account_id(&self, validator_id: u64) -> &AccountId { + match self { + Self::V1(v1) => v1.validators[validator_id as usize].account_id(), + Self::V2(v2) => v2.validators[validator_id as usize].account_id(), + } + } + + #[inline] + pub fn account_is_validator(&self, account_id: &AccountId) -> bool { + match self { + Self::V1(v1) => v1.validator_to_index.contains_key(account_id), + Self::V2(v2) => v2.validator_to_index.contains_key(account_id), + } + } + + pub fn get_validator_id(&self, account_id: &AccountId) -> Option<&ValidatorId> { + match self { + Self::V1(v1) => v1.validator_to_index.get(account_id), + Self::V2(v2) => v2.validator_to_index.get(account_id), + } + } + + pub fn get_validator_by_account(&self, account_id: &AccountId) -> Option { + match self { + Self::V1(v1) => v1 + .validator_to_index + .get(account_id) + .map(|validator_id| v1.validators[*validator_id as usize].clone()), + Self::V2(v2) => v2 + .validator_to_index + .get(account_id) + .map(|validator_id| v2.validators[*validator_id as usize].clone()), + } + } + + #[inline] + pub fn get_validator(&self, validator_id: u64) -> ValidatorPowerAndFrozen { + match self { + Self::V1(v1) => v1.validators[validator_id as usize].clone(), + Self::V2(v2) => v2.validators[validator_id as usize].clone(), + } + } + + #[inline] + pub fn account_is_fisherman(&self, account_id: &AccountId) -> bool { + match self { + Self::V1(v1) => v1.fishermen_to_index.contains_key(account_id), + Self::V2(v2) => v2.fishermen_to_index.contains_key(account_id), + } + } + + pub fn get_fisherman_by_account(&self, account_id: &AccountId) -> Option { + match self { + Self::V1(v1) => v1 + .fishermen_to_index + .get(account_id) + .map(|validator_id| v1.fishermen[*validator_id as usize].clone()), + Self::V2(v2) => v2 + .fishermen_to_index + .get(account_id) + .map(|validator_id| v2.fishermen[*validator_id as usize].clone()), + } + } + + #[inline] + pub fn get_fisherman(&self, fisherman_id: u64) -> ValidatorPowerAndFrozen { + match self { + Self::V1(v1) => v1.fishermen[fisherman_id as usize].clone(), + Self::V2(v2) => v2.fishermen[fisherman_id as usize].clone(), + } + } + + #[inline] + pub fn validators_len(&self) -> usize { + match self { + Self::V1(v1) => v1.validators.len(), + Self::V2(v2) => v2.validators.len(), + } + } + + pub fn vrf_block_producer(&self, _random_value: &CryptoHash) -> ValidatorId { + return 0; + } + // end customized by James Savechives + + } + + // V1 -> V2: Use versioned ValidatorStake structure in proposals + #[derive( + Default, BorshSerialize, BorshDeserialize, Eq, PartialEq, Clone, Debug, serde::Serialize, + )] + pub struct BlockInfoV2 { + pub hash: CryptoHash, + pub height: BlockHeight, + pub last_finalized_height: BlockHeight, + pub last_final_block_hash: CryptoHash, + pub prev_hash: CryptoHash, + pub epoch_first_block: CryptoHash, + pub epoch_id: EpochId, + pub power_proposals: Vec, + pub frozen_proposals: Vec, + pub chunk_mask: Vec, + /// Latest protocol version this validator observes. + pub latest_protocol_version: ProtocolVersion, + /// Validators slashed since the start of epoch or in previous epoch. + pub slashed: HashMap, + /// Total supply at this block. + pub total_supply: Balance, + pub timestamp_nanosec: u64, + /// start customized by James Savechives + pub random_value: CryptoHash, + pub validators: Vec, + pub validator_to_index: HashMap, + pub block_producers_settlement: Vec, + pub chunk_producers_settlement: Vec>, + pub fishermen: Vec, + pub fishermen_to_index: HashMap, + pub power_change: BTreeMap, + pub frozen_change: BTreeMap, + pub validator_reward: HashMap, + pub seat_price: Balance, + pub minted_amount: Balance, + /// Power proposals from the block, only the latest one per account + pub all_power_proposals: Vec, + /// Frozen proposals from the block, only the latest one per account + pub all_frozen_proposals: Vec, + /// Kickout set, includes slashed + pub validator_kickout: HashMap, + /// Only for validators who met the threshold and didn't get slashed + pub validator_mandates: ValidatorMandates, + // end customized by James Savechives + } + + +} + +/// Information per each block. +#[derive( + Default, BorshSerialize, BorshDeserialize, Eq, PartialEq, Clone, Debug, serde::Serialize, +)] +pub struct BlockInfoV1 { + pub hash: CryptoHash, + pub height: BlockHeight, + pub last_finalized_height: BlockHeight, + pub last_final_block_hash: CryptoHash, + pub prev_hash: CryptoHash, + pub epoch_first_block: CryptoHash, + pub epoch_id: EpochId, + pub power_proposals: Vec, + pub frozen_proposals: Vec, + pub chunk_mask: Vec, + /// Latest protocol version this validator observes. + pub latest_protocol_version: ProtocolVersion, + /// Validators slashed since the start of epoch or in previous epoch. + pub slashed: HashMap, + /// Total supply at this block. + pub total_supply: Balance, + pub timestamp_nanosec: u64, + /// start customized by James Savechives + pub random_value: CryptoHash, + pub validators: Vec, + pub validator_to_index: HashMap, + pub block_producers_settlement: Vec, + pub chunk_producers_settlement: Vec>, + pub fishermen: Vec, + pub fishermen_to_index: HashMap, + pub power_change: BTreeMap, + pub frozen_change: BTreeMap, + pub validator_reward: HashMap, + pub seat_price: Balance, + pub minted_amount: Balance, + /// Power proposals from the block, only the latest one per account + pub all_power_proposals: Vec, + /// Frozen proposals from the block, only the latest one per account + pub all_frozen_proposals: Vec, + /// Kickout set, includes slashed + pub validator_kickout: HashMap, + /// Only for validators who met the threshold and didn't get slashed + pub validator_mandates: ValidatorMandates, + // end customized by James Savechives +} + +impl BlockInfoV1 { + pub fn new( + hash: CryptoHash, + height: BlockHeight, + last_finalized_height: BlockHeight, + last_final_block_hash: CryptoHash, + prev_hash: CryptoHash, + power_proposals: Vec, + frozen_proposals: Vec, + validator_mask: Vec, + slashed: Vec, + total_supply: Balance, + latest_protocol_version: ProtocolVersion, + timestamp_nanosec: u64, + // start customized by James Savechives + random_value: CryptoHash, + validators: Vec, + validator_to_index: HashMap, + block_producers_settlement: Vec, + chunk_producers_settlement: Vec>, + fishermen: Vec, + fishermen_to_index: HashMap, + power_change: BTreeMap, + frozen_change: BTreeMap, + validator_reward: HashMap, + seat_price: Balance, + minted_amount: Balance, + all_power_proposals: Vec, + all_frozen_proposals: Vec, + validator_kickout: HashMap, + validator_mandates: ValidatorMandates, + // end customized by James Savechives + ) -> Self { + Self { + hash, + height, + last_finalized_height, + last_final_block_hash, + prev_hash, + power_proposals, + frozen_proposals, + chunk_mask: validator_mask, + latest_protocol_version, + slashed: slashed + .into_iter() + .map(|s| { + let slash_state = + if s.is_double_sign { SlashState::DoubleSign } else { SlashState::Other }; + (s.account_id, slash_state) + }) + .collect(), + total_supply, + epoch_first_block: Default::default(), + epoch_id: Default::default(), + timestamp_nanosec, + // start customized by James Savechives + random_value, + validators, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + fishermen, + fishermen_to_index, + power_change, + frozen_change, + validator_reward, + seat_price, + minted_amount, + all_power_proposals, + all_frozen_proposals, + validator_kickout, + validator_mandates, + // end customized by James Savechives + } + } +} + +#[derive( + Default, BorshSerialize, BorshDeserialize, Clone, Debug, PartialEq, Eq, serde::Serialize, +)] +pub struct ValidatorWeight(ValidatorId, u64); + +pub mod epoch_info { +use crate::epoch_manager::ValidatorWeight; + use crate::types::validator_power::ValidatorPower; + use crate::types::{BlockChunkValidatorStats, ValidatorKickoutReason, ValidatorPowerAndFrozenV1}; + use crate::validator_mandates::{ValidatorMandates, ValidatorMandatesAssignment}; + use crate::version::PROTOCOL_VERSION; + use borsh::{BorshDeserialize, BorshSerialize}; + use unc_primitives_core::hash::CryptoHash; + use unc_primitives_core::types::{ + AccountId, Balance, EpochHeight, ProtocolVersion, ValidatorId, Power, + }; + use rand::SeedableRng; + use rand_chacha::ChaCha20Rng; + use smart_default::SmartDefault; + use std::collections::{BTreeMap, HashMap}; + + use crate::{epoch_manager::RngSeed, rand::WeightedIndex}; + use unc_primitives_core::{ + checked_feature, + hash::hash, + types::{BlockHeight, ShardId}, + }; + use crate::types::validator_frozen::ValidatorFrozen; + use crate::types::validator_power_and_frozen::{ValidatorPowerAndFrozen, ValidatorPowerAndFrozenIter}; + + pub use super::EpochInfoV1; + + /// Information per epoch. + #[derive(BorshSerialize, BorshDeserialize, Clone, Debug, PartialEq, Eq, serde::Serialize)] + pub enum EpochInfo { + V1(EpochInfoV1), + V2(EpochInfoV2), + V3(EpochInfoV3), + V4(EpochInfoV4), + } + + impl Default for EpochInfo { + fn default() -> Self { + Self::V2(EpochInfoV2::default()) + } + } + + // V1 -> V2: Use versioned ValidatorStake structure in validators and fishermen + #[derive( + SmartDefault, + BorshSerialize, + BorshDeserialize, + Clone, + Debug, + PartialEq, + Eq, + serde::Serialize, + )] + pub struct EpochInfoV2 { + /// Ordinal of given epoch from genesis. + /// There can be multiple epochs with the same ordinal in case of long forks. + pub epoch_height: EpochHeight, + /// List of current validators. + pub validators: Vec, + /// Validator account id to index in proposals. + pub validator_to_index: HashMap, + /// Settlement of validators responsible for block production. + pub block_producers_settlement: Vec, + /// Per each shard, settlement validators that are responsible. + pub chunk_producers_settlement: Vec>, + /// Settlement of hidden validators with weights used to determine how many shards they will validate. + pub hidden_validators_settlement: Vec, + /// List of current fishermen. + pub fishermen: Vec, + /// Fisherman account id to index of proposal. + pub fishermen_to_index: HashMap, + /// New power for validators. + pub power_change: BTreeMap, + /// New stake for validators. + pub frozen_change: BTreeMap, + /// Validator reward for the epoch. + pub validator_reward: HashMap, + /// Validators who are kicked out in this epoch. + pub validator_kickout: HashMap, + /// Total minted tokens in the epoch. + pub minted_amount: Balance, + /// Seat price of this epoch. + pub seat_price: Balance, + /// Current protocol version during this epoch. + #[default(PROTOCOL_VERSION)] + pub protocol_version: ProtocolVersion, + } + + // V2 -> V3: Structures for randomly selecting validators at each height based on new + // block producer and chunk producer selection algorithm. + #[derive( + SmartDefault, + BorshSerialize, + BorshDeserialize, + Clone, + Debug, + PartialEq, + Eq, + serde::Serialize, + )] + pub struct EpochInfoV3 { + pub epoch_height: EpochHeight, + pub validators: Vec, + pub validator_to_index: HashMap, + pub block_producers_settlement: Vec, + pub chunk_producers_settlement: Vec>, + pub hidden_validators_settlement: Vec, + pub fishermen: Vec, + pub fishermen_to_index: HashMap, + pub power_change: BTreeMap, + pub frozen_change: BTreeMap, + pub validator_reward: HashMap, + pub validator_kickout: HashMap, + pub minted_amount: Balance, + pub seat_price: Balance, + #[default(PROTOCOL_VERSION)] + pub protocol_version: ProtocolVersion, + // stuff for selecting validators at each height + rng_seed: RngSeed, + block_producers_sampler: WeightedIndex, + chunk_producers_sampler: Vec, + } + + // V3 -> V4: Add structures and methods for stateless validator assignment. + #[derive( + SmartDefault, + BorshSerialize, + BorshDeserialize, + Clone, + Debug, + PartialEq, + Eq, + serde::Serialize, + )] + pub struct EpochInfoV4 { + pub epoch_height: EpochHeight, + pub validators: Vec, + pub validator_to_index: HashMap, + pub block_producers_settlement: Vec, + pub chunk_producers_settlement: Vec>, + pub hidden_validators_settlement: Vec, + pub fishermen: Vec, + pub fishermen_to_index: HashMap, + pub power_change: BTreeMap, + pub frozen_change: BTreeMap, + pub validator_reward: HashMap, + pub validator_kickout: HashMap, + pub minted_amount: Balance, + pub seat_price: Balance, + #[default(PROTOCOL_VERSION)] + pub protocol_version: ProtocolVersion, + // stuff for selecting validators at each height + rng_seed: RngSeed, + block_producers_sampler: WeightedIndex, + chunk_producers_sampler: Vec, + /// Contains the epoch's validator mandates. Used to sample chunk validators. + validator_mandates: ValidatorMandates, + } + + impl EpochInfo { + pub fn new( + epoch_height: EpochHeight, + validators: Vec, + validator_to_index: HashMap, + block_producers_settlement: Vec, + chunk_producers_settlement: Vec>, + hidden_validators_settlement: Vec, + fishermen: Vec, + fishermen_to_index: HashMap, + power_change: BTreeMap, + frozen_change: BTreeMap, + validator_reward: HashMap, + validator_kickout: HashMap, + minted_amount: Balance, + seat_price: Balance, + protocol_version: ProtocolVersion, + rng_seed: RngSeed, + validator_mandates: ValidatorMandates, + ) -> Self { + if checked_feature!("stable", AliasValidatorSelectionAlgorithm, protocol_version) { + let power_weights = |ids: &[ValidatorId]| -> WeightedIndex { + WeightedIndex::new( + ids.iter() + .copied() + .map(|validator_id| validators[validator_id as usize].power()) + .collect(), + ) + }; + let block_producers_sampler = power_weights(&block_producers_settlement); + let chunk_producers_sampler = + chunk_producers_settlement.iter().map(|vs| power_weights(vs)).collect(); + if checked_feature!("stable", ChunkValidation, protocol_version) { + Self::V4(EpochInfoV4 { + epoch_height, + validators, + fishermen, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + hidden_validators_settlement, + power_change, + frozen_change, + validator_reward, + validator_kickout, + fishermen_to_index, + minted_amount, + seat_price, + protocol_version, + rng_seed, + block_producers_sampler, + chunk_producers_sampler, + validator_mandates, + }) + } else { + Self::V3(EpochInfoV3 { + epoch_height, + validators, + fishermen, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + hidden_validators_settlement, + power_change, + frozen_change, + validator_reward, + validator_kickout, + fishermen_to_index, + minted_amount, + seat_price, + protocol_version, + rng_seed, + block_producers_sampler, + chunk_producers_sampler, + }) + } + } else { + Self::V2(EpochInfoV2 { + epoch_height, + validators, + fishermen, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + hidden_validators_settlement, + power_change, + frozen_change, + validator_reward, + validator_kickout, + fishermen_to_index, + minted_amount, + seat_price, + protocol_version, + }) + } + } + + pub fn v1_test() -> Self { + Self::V1(EpochInfoV1 { + epoch_height: 10, + validators: vec![ + ValidatorPowerAndFrozenV1 { + account_id: "test".parse().unwrap(), + public_key: "ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp" + .parse() + .unwrap(), + power: 0, + frozen: 0, + }, + ValidatorPowerAndFrozenV1 { + account_id: "validator".parse().unwrap(), + public_key: "ed25519:9E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp" + .parse() + .unwrap(), + power: 0, + frozen: 0, + }, + ], + validator_to_index: HashMap::new(), + block_producers_settlement: vec![0u64, 1u64], + chunk_producers_settlement: vec![vec![0u64, 1u64]], + hidden_validators_settlement: vec![], + fishermen: vec![], + fishermen_to_index: HashMap::new(), + power_change: BTreeMap::new(), + frozen_change: BTreeMap::new(), + validator_reward: HashMap::new(), + validator_kickout: HashMap::new(), + minted_amount: 1, + seat_price: 1, + protocol_version: 1, + }) + } + + #[inline] + pub fn epoch_height_mut(&mut self) -> &mut EpochHeight { + match self { + Self::V1(v1) => &mut v1.epoch_height, + Self::V2(v2) => &mut v2.epoch_height, + Self::V3(v3) => &mut v3.epoch_height, + Self::V4(v4) => &mut v4.epoch_height, + } + } + + #[inline] + pub fn epoch_height(&self) -> EpochHeight { + match self { + Self::V1(v1) => v1.epoch_height, + Self::V2(v2) => v2.epoch_height, + Self::V3(v3) => v3.epoch_height, + Self::V4(v4) => v4.epoch_height, + } + } + + #[inline] + pub fn seat_price(&self) -> Balance { + match self { + Self::V1(v1) => v1.seat_price, + Self::V2(v2) => v2.seat_price, + Self::V3(v3) => v3.seat_price, + Self::V4(v4) => v4.seat_price, + } + } + + #[inline] + pub fn minted_amount(&self) -> Balance { + match self { + Self::V1(v1) => v1.minted_amount, + Self::V2(v2) => v2.minted_amount, + Self::V3(v3) => v3.minted_amount, + Self::V4(v4) => v4.minted_amount, + } + } + + #[inline] + pub fn block_producers_settlement(&self) -> &[ValidatorId] { + match self { + Self::V1(v1) => &v1.block_producers_settlement, + Self::V2(v2) => &v2.block_producers_settlement, + Self::V3(v3) => &v3.block_producers_settlement, + Self::V4(v4) => &v4.block_producers_settlement, + } + } + + #[inline] + pub fn chunk_producers_settlement(&self) -> &[Vec] { + match self { + Self::V1(v1) => &v1.chunk_producers_settlement, + Self::V2(v2) => &v2.chunk_producers_settlement, + Self::V3(v3) => &v3.chunk_producers_settlement, + Self::V4(v4) => &v4.chunk_producers_settlement, + } + } + + #[inline] + pub fn validator_kickout(&self) -> &HashMap { + match self { + Self::V1(v1) => &v1.validator_kickout, + Self::V2(v2) => &v2.validator_kickout, + Self::V3(v3) => &v3.validator_kickout, + Self::V4(v4) => &v4.validator_kickout, + } + } + + #[inline] + pub fn protocol_version(&self) -> ProtocolVersion { + match self { + Self::V1(v1) => v1.protocol_version, + Self::V2(v2) => v2.protocol_version, + Self::V3(v3) => v3.protocol_version, + Self::V4(v4) => v4.protocol_version, + } + } + + #[inline] + pub fn frozen_change(&self) -> &BTreeMap { + match self { + Self::V1(v1) => &v1.frozen_change, + Self::V2(v2) => &v2.frozen_change, + Self::V3(v3) => &v3.frozen_change, + Self::V4(v4) => &v4.frozen_change, + } + } + + #[inline] + pub fn power_change(&self) -> &BTreeMap { + match self { + Self::V1(v1) => &v1.power_change, + Self::V2(v2) => &v2.power_change, + Self::V3(v3) => &v3.power_change, + Self::V4(v4) => &v4.power_change, + } + } + + #[inline] + pub fn validator_reward(&self) -> &HashMap { + match self { + Self::V1(v1) => &v1.validator_reward, + Self::V2(v2) => &v2.validator_reward, + Self::V3(v3) => &v3.validator_reward, + Self::V4(v4) => &v4.validator_reward, + } + } + + #[inline] + pub fn validators_iter(&self) -> ValidatorPowerAndFrozenIter { + match self { + Self::V1(v1) => ValidatorPowerAndFrozenIter::v1(&v1.validators), + Self::V2(v2) => ValidatorPowerAndFrozenIter::new(&v2.validators), + Self::V3(v3) => ValidatorPowerAndFrozenIter::new(&v3.validators), + Self::V4(v4) => ValidatorPowerAndFrozenIter::new(&v4.validators), + } + } + + #[inline] + pub fn fishermen_iter(&self) -> ValidatorPowerAndFrozenIter { + match self { + Self::V1(v1) => ValidatorPowerAndFrozenIter::v1(&v1.fishermen), + Self::V2(v2) => ValidatorPowerAndFrozenIter::new(&v2.fishermen), + Self::V3(v3) => ValidatorPowerAndFrozenIter::new(&v3.fishermen), + Self::V4(v4) => ValidatorPowerAndFrozenIter::new(&v4.fishermen), + } + } + + #[inline] + pub fn validator_power(&self, validator_id: u64) -> Power { + match self { + Self::V1(v1) => v1.validators[validator_id as usize].power, + Self::V2(v2) => v2.validators[validator_id as usize].power(), + Self::V3(v3) => v3.validators[validator_id as usize].power(), + Self::V4(v4) => v4.validators[validator_id as usize].power(), + } + } + + #[inline] + pub fn validator_frozen(&self, validator_id: u64) -> Balance { + match self { + Self::V1(v1) => v1.validators[validator_id as usize].frozen, + Self::V2(v2) => v2.validators[validator_id as usize].frozen(), + Self::V3(v3) => v3.validators[validator_id as usize].frozen(), + Self::V4(v4) => v4.validators[validator_id as usize].frozen(), + } + } + + #[inline] + pub fn validator_account_id(&self, validator_id: u64) -> &AccountId { + match self { + Self::V1(v1) => &v1.validators[validator_id as usize].account_id, + Self::V2(v2) => v2.validators[validator_id as usize].account_id(), + Self::V3(v3) => v3.validators[validator_id as usize].account_id(), + Self::V4(v4) => v4.validators[validator_id as usize].account_id(), + } + } + + #[inline] + pub fn account_is_validator(&self, account_id: &AccountId) -> bool { + match self { + Self::V1(v1) => v1.validator_to_index.contains_key(account_id), + Self::V2(v2) => v2.validator_to_index.contains_key(account_id), + Self::V3(v3) => v3.validator_to_index.contains_key(account_id), + Self::V4(v4) => v4.validator_to_index.contains_key(account_id), + } + } + + pub fn get_validator_id(&self, account_id: &AccountId) -> Option<&ValidatorId> { + match self { + Self::V1(v1) => v1.validator_to_index.get(account_id), + Self::V2(v2) => v2.validator_to_index.get(account_id), + Self::V3(v3) => v3.validator_to_index.get(account_id), + Self::V4(v4) => v4.validator_to_index.get(account_id), + } + } + + pub fn get_validator_by_account(&self, account_id: &AccountId) -> Option { + match self { + Self::V1(v1) => v1.validator_to_index.get(account_id).map(|validator_id| { + ValidatorPowerAndFrozen::V1(v1.validators[*validator_id as usize].clone()) + }), + Self::V2(v2) => v2 + .validator_to_index + .get(account_id) + .map(|validator_id| v2.validators[*validator_id as usize].clone()), + Self::V3(v3) => v3 + .validator_to_index + .get(account_id) + .map(|validator_id| v3.validators[*validator_id as usize].clone()), + Self::V4(v4) => v4 + .validator_to_index + .get(account_id) + .map(|validator_id| v4.validators[*validator_id as usize].clone()), + } + } + + #[inline] + pub fn get_validator(&self, validator_id: u64) -> ValidatorPowerAndFrozen { + match self { + Self::V1(v1) => ValidatorPowerAndFrozen::V1(v1.validators[validator_id as usize].clone()), + Self::V2(v2) => v2.validators[validator_id as usize].clone(), + Self::V3(v3) => v3.validators[validator_id as usize].clone(), + Self::V4(v4) => v4.validators[validator_id as usize].clone(), + } + } + + #[inline] + pub fn account_is_fisherman(&self, account_id: &AccountId) -> bool { + match self { + Self::V1(v1) => v1.fishermen_to_index.contains_key(account_id), + Self::V2(v2) => v2.fishermen_to_index.contains_key(account_id), + Self::V3(v3) => v3.fishermen_to_index.contains_key(account_id), + Self::V4(v4) => v4.fishermen_to_index.contains_key(account_id), + } + } + + pub fn get_fisherman_by_account(&self, account_id: &AccountId) -> Option { + match self { + Self::V1(v1) => v1.fishermen_to_index.get(account_id).map(|validator_id| { + ValidatorPowerAndFrozen::V1(v1.fishermen[*validator_id as usize].clone()) + }), + Self::V2(v2) => v2 + .fishermen_to_index + .get(account_id) + .map(|validator_id| v2.fishermen[*validator_id as usize].clone()), + Self::V3(v3) => v3 + .fishermen_to_index + .get(account_id) + .map(|validator_id| v3.fishermen[*validator_id as usize].clone()), + Self::V4(v4) => v4 + .fishermen_to_index + .get(account_id) + .map(|validator_id| v4.fishermen[*validator_id as usize].clone()), + } + } + + #[inline] + pub fn get_fisherman(&self, fisherman_id: u64) -> ValidatorPowerAndFrozen { + match self { + Self::V1(v1) => ValidatorPowerAndFrozen::V1(v1.fishermen[fisherman_id as usize].clone()), + Self::V2(v2) => v2.fishermen[fisherman_id as usize].clone(), + Self::V3(v3) => v3.fishermen[fisherman_id as usize].clone(), + Self::V4(v4) => v4.fishermen[fisherman_id as usize].clone(), + } + } + + #[inline] + pub fn validators_len(&self) -> usize { + match self { + Self::V1(v1) => v1.validators.len(), + Self::V2(v2) => v2.validators.len(), + Self::V3(v3) => v3.validators.len(), + Self::V4(v4) => v4.validators.len(), + } + } + + pub fn vrf_block_producer(&self, _random_value: &CryptoHash) -> ValidatorId { + return 0; + } + + pub fn sample_block_producer(&self, height: BlockHeight) -> ValidatorId { + match &self { + Self::V1(v1) => { + let bp_settlement = &v1.block_producers_settlement; + bp_settlement[(height % (bp_settlement.len() as u64)) as usize] + } + Self::V2(v2) => { + let bp_settlement = &v2.block_producers_settlement; + bp_settlement[(height % (bp_settlement.len() as u64)) as usize] + } + Self::V3(v3) => { + let seed = Self::block_produce_seed(height, &v3.rng_seed); + v3.block_producers_settlement[v3.block_producers_sampler.sample(seed)] + } + Self::V4(v4) => { + let seed = Self::block_produce_seed(height, &v4.rng_seed); + v4.block_producers_settlement[v4.block_producers_sampler.sample(seed)] + } + } + } + + pub fn sample_chunk_producer(&self, height: BlockHeight, shard_id: ShardId) -> ValidatorId { + match &self { + Self::V1(v1) => { + let cp_settlement = &v1.chunk_producers_settlement; + let shard_cps = &cp_settlement[shard_id as usize]; + shard_cps[(height as u64 % (shard_cps.len() as u64)) as usize] + } + Self::V2(v2) => { + let cp_settlement = &v2.chunk_producers_settlement; + let shard_cps = &cp_settlement[shard_id as usize]; + shard_cps[(height as u64 % (shard_cps.len() as u64)) as usize] + } + Self::V3(v3) => { + let protocol_version = self.protocol_version(); + let seed = + Self::chunk_produce_seed(protocol_version, &v3.rng_seed, height, shard_id); + let shard_id = shard_id as usize; + let sample = v3.chunk_producers_sampler[shard_id].sample(seed); + v3.chunk_producers_settlement[shard_id][sample] + } + Self::V4(v4) => { + let protocol_version = self.protocol_version(); + let seed = + Self::chunk_produce_seed(protocol_version, &v4.rng_seed, height, shard_id); + let shard_id = shard_id as usize; + let sample = v4.chunk_producers_sampler[shard_id].sample(seed); + v4.chunk_producers_settlement[shard_id][sample] + } + } + } + + pub fn sample_chunk_validators(&self, height: BlockHeight) -> ValidatorMandatesAssignment { + // Chunk validator assignment was introduced with `V4`. + match &self { + Self::V1(_) => Default::default(), + Self::V2(_) => Default::default(), + Self::V3(_) => Default::default(), + Self::V4(v4) => { + let mut rng = Self::chunk_validate_rng(&v4.rng_seed, height); + v4.validator_mandates.sample(&mut rng) + } + } + } + + /// 32 bytes from epoch_seed, 8 bytes from height + fn block_produce_seed(height: BlockHeight, seed: &RngSeed) -> [u8; 32] { + let mut buffer = [0u8; 40]; + buffer[0..32].copy_from_slice(seed); + buffer[32..40].copy_from_slice(&height.to_le_bytes()); + hash(&buffer).0 + } + + fn chunk_produce_seed( + protocol_version: ProtocolVersion, + seed: &RngSeed, + height: BlockHeight, + shard_id: ShardId, + ) -> [u8; 32] { + if checked_feature!("stable", SynchronizeBlockChunkProduction, protocol_version) + && !checked_feature!("stable", ChunkOnlyProducers, protocol_version) + { + // This is same seed that used for determining block + // producer. This seed does not contain the shard id + // so all shards will be produced by the same + // validator. + Self::block_produce_seed(height, seed) + } else { + // 32 bytes from epoch_seed, 8 bytes from height, 8 bytes from shard_id + let mut buffer = [0u8; 48]; + buffer[0..32].copy_from_slice(seed); + buffer[32..40].copy_from_slice(&height.to_le_bytes()); + buffer[40..48].copy_from_slice(&shard_id.to_le_bytes()); + hash(&buffer).0 + } + } + + /// Returns a new RNG obtained from combining the provided `seed` and `height`. + /// + /// The returned RNG can be used to shuffle slices via [`rand::seq::SliceRandom`]. + fn chunk_validate_rng(seed: &RngSeed, height: BlockHeight) -> ChaCha20Rng { + let mut buffer = [0u8; 40]; + buffer[0..32].copy_from_slice(seed); + buffer[32..40].copy_from_slice(&height.to_le_bytes()); + + // The recommended seed for cryptographic RNG's is `[u8; 32]` and some required traits + // are not implemented for larger seeds, see + // https://docs.rs/rand_core/0.6.2/rand_core/trait.SeedableRng.html#associated-types + // Therefore `buffer` is hashed to obtain a `[u8; 32]`. + let seed = hash(&buffer); + SeedableRng::from_seed(seed.0) + } + } + + #[derive(BorshSerialize, BorshDeserialize)] + pub struct EpochSummary { + pub prev_epoch_last_block_hash: CryptoHash, + /// Power proposals from the epoch, only the latest one per account + pub all_power_proposals: Vec, + /// Frozen proposals from the epoch, only the latest one per account + pub all_frozen_proposals: Vec, + /// Kickout set, includes slashed + pub validator_kickout: HashMap, + /// Only for validators who met the threshold and didn't get slashed + pub validator_block_chunk_stats: HashMap, + /// Protocol version for next epoch. + pub next_version: ProtocolVersion, + } +} + +/// Information per epoch. +#[derive( + SmartDefault, BorshSerialize, BorshDeserialize, Clone, Debug, PartialEq, Eq, serde::Serialize, +)] +pub struct EpochInfoV1 { + /// Ordinal of given epoch from genesis. + /// There can be multiple epochs with the same ordinal in case of long forks. + pub epoch_height: EpochHeight, + /// List of current validators. + pub validators: Vec, + /// Validator account id to index in proposals. + pub validator_to_index: HashMap, + /// Settlement of validators responsible for block production. + pub block_producers_settlement: Vec, + /// Per each shard, settlement validators that are responsible. + pub chunk_producers_settlement: Vec>, + /// Settlement of hidden validators with weights used to determine how many shards they will validate. + pub hidden_validators_settlement: Vec, + /// List of current fishermen. + pub fishermen: Vec, + /// Fisherman account id to index of proposal. + pub fishermen_to_index: HashMap, + /// New power for validators. + pub power_change: BTreeMap, + /// New stake for validators. + pub frozen_change: BTreeMap, + /// Validator reward for the epoch. + pub validator_reward: HashMap, + /// Validators who are kicked out in this epoch. + pub validator_kickout: HashMap, + /// Total minted tokens in the epoch. + pub minted_amount: Balance, + /// Seat price of this epoch. + pub seat_price: Balance, + /// Current protocol version during this epoch. + #[default(PROTOCOL_VERSION)] + pub protocol_version: ProtocolVersion, +} + +/// State that a slashed validator can be in. +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, PartialEq, Eq)] +pub enum SlashState { + /// Double Sign, will be partially slashed. + DoubleSign, + /// Malicious behavior but is already slashed (tokens taken away from account). + AlreadySlashed, + /// All other cases (tokens should be entirely slashed), + Other, +} + +#[cfg(feature = "new_epoch_sync")] +pub mod epoch_sync { + use crate::block_header::BlockHeader; + use crate::epoch_manager::block_info::BlockInfo; + use crate::epoch_manager::epoch_info::EpochInfo; + use crate::errors::epoch_sync::{EpochSyncHashType, EpochSyncInfoError}; + use crate::types::EpochId; + use borsh::{BorshDeserialize, BorshSerialize}; + use unc_o11y::log_assert; + use unc_primitives_core::hash::CryptoHash; + use std::collections::{HashMap, HashSet}; + use crate::epoch_manager::block_summary::{BlockSummary, BlockSummaryV1}; + + /// Struct to keep all the info that is transferred for one epoch during Epoch Sync. + #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] + pub struct EpochSyncInfo { + /// All block hashes of this epoch. In order of production. + pub all_block_hashes: Vec, + /// All headers relevant to epoch sync. + /// Contains epoch headers that need to be saved + supporting headers needed for validation. + /// Probably contains one header from the previous epoch. + /// It refers to `last_final_block` of the first block of the epoch. + /// Also contains first header from the next epoch. + /// It refers to `next_epoch_first_hash`. + pub headers: HashMap, + /// Hashes of headers that need to be validated and saved. + pub headers_to_save: HashSet, + /// Hash of the first block of the next epoch. + /// Header of this block contains `epoch_sync_data_hash`. + pub next_epoch_first_hash: CryptoHash, + pub epoch_info: EpochInfo, + pub next_epoch_info: EpochInfo, + pub next_next_epoch_info: EpochInfo, + } + + impl EpochSyncInfo { + pub fn get_epoch_id(&self) -> Result<&EpochId, EpochSyncInfoError> { + Ok(self.get_epoch_first_header()?.epoch_id()) + } + + pub fn get_next_epoch_id(&self) -> Result<&EpochId, EpochSyncInfoError> { + Ok(self + .get_header(self.next_epoch_first_hash, EpochSyncHashType::NextEpochFirstBlock)? + .epoch_id()) + } + + pub fn get_next_next_epoch_id(&self) -> Result { + Ok(EpochId(*self.get_epoch_last_hash()?)) + } + + pub fn get_epoch_last_hash(&self) -> Result<&CryptoHash, EpochSyncInfoError> { + let epoch_height = self.epoch_info.epoch_height(); + + self.all_block_hashes.last().ok_or(EpochSyncInfoError::ShortEpoch { epoch_height }) + } + + pub fn get_epoch_last_header(&self) -> Result<&BlockHeader, EpochSyncInfoError> { + self.get_header(*self.get_epoch_last_hash()?, EpochSyncHashType::LastEpochBlock) + } + + pub fn get_epoch_last_finalised_hash(&self) -> Result<&CryptoHash, EpochSyncInfoError> { + Ok(self.get_epoch_last_header()?.last_final_block()) + } + + pub fn get_epoch_last_finalised_header(&self) -> Result<&BlockHeader, EpochSyncInfoError> { + self.get_header( + *self.get_epoch_last_finalised_hash()?, + EpochSyncHashType::LastFinalBlock, + ) + } + + pub fn get_epoch_first_hash(&self) -> Result<&CryptoHash, EpochSyncInfoError> { + let epoch_height = self.epoch_info.epoch_height(); + + self.all_block_hashes.first().ok_or(EpochSyncInfoError::ShortEpoch { epoch_height }) + } + + pub fn get_epoch_first_header(&self) -> Result<&BlockHeader, EpochSyncInfoError> { + self.get_header(*self.get_epoch_first_hash()?, EpochSyncHashType::FirstEpochBlock) + } + + /// Reconstruct BlockInfo for `hash` from information in EpochSyncInfo. + pub fn get_block_info(&self, hash: &CryptoHash) -> Result { + let epoch_first_header = self.get_epoch_first_header()?; + let header = self.get_header(*hash, EpochSyncHashType::Other)?; + + log_assert!( + epoch_first_header.epoch_id() == header.epoch_id(), + "We can only correctly reconstruct headers from this epoch" + ); + + let last_finalized_height = if *header.last_final_block() == CryptoHash::default() { + 0 + } else { + let last_finalized_header = + self.get_header(*header.last_final_block(), EpochSyncHashType::LastFinalBlock)?; + last_finalized_header.height() + }; + // start customized by James Savechives + + let BlockSummary::V1(BlockSummaryV1{ + random_value:_random_value, + validators, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + fishermen, + fishermen_to_index, + power_change, + frozen_change, + validator_reward, + seat_price, + minted_amount, + all_power_proposals, + all_frozen_proposals, + validator_kickout, + validator_mandates, .. + }) = BlockSummary::default(); + // end customized by James Savechives + let mut block_info = BlockInfo::new( + *header.hash(), + header.height(), + last_finalized_height, + *header.last_final_block(), + *header.prev_hash(), + header.prev_validator_power_proposals().collect(), + header.prev_validator_frozen_proposals().collect(), + header.chunk_mask().to_vec(), + vec![], + header.total_supply(), + header.latest_protocol_version(), + header.raw_timestamp(), + // start customized by James Savechives + *header.random_value(), + validators, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + fishermen, + fishermen_to_index, + power_change, + frozen_change, + validator_reward, + seat_price, + minted_amount, + all_power_proposals, + all_frozen_proposals, + validator_kickout, + validator_mandates + // end customized by James Savechives + ); + + *block_info.epoch_id_mut() = epoch_first_header.epoch_id().clone(); + *block_info.epoch_first_block_mut() = *epoch_first_header.hash(); + Ok(block_info) + } + + /// Reconstruct legacy `epoch_sync_data_hash` from `EpochSyncInfo`. + /// `epoch_sync_data_hash` was introduced in `BlockHeaderInnerRestV3`. + /// Using this hash we can verify that `EpochInfo` data provided in `EpochSyncInfo` is correct. + pub fn calculate_epoch_sync_data_hash(&self) -> Result { + let epoch_height = self.epoch_info.epoch_height(); + + if self.all_block_hashes.len() < 2 { + return Err(EpochSyncInfoError::ShortEpoch { epoch_height }); + } + let epoch_first_block = self.all_block_hashes[0]; + let epoch_prev_last_block = self.all_block_hashes[self.all_block_hashes.len() - 2]; + let epoch_last_block = self.all_block_hashes[self.all_block_hashes.len() - 1]; + + Ok(CryptoHash::hash_borsh(&( + self.get_block_info(&epoch_first_block)?, + self.get_block_info(&epoch_prev_last_block)?, + self.get_block_info(&epoch_last_block)?, + &self.epoch_info, + &self.next_epoch_info, + &self.next_next_epoch_info, + ))) + } + + /// Read legacy `epoch_sync_data_hash` from next epoch first header. + /// `epoch_sync_data_hash` was introduced in `BlockHeaderInnerRestV3`. + /// Using this hash we can verify that `EpochInfo` data provided in `EpochSyncInfo` is correct. + pub fn get_epoch_sync_data_hash(&self) -> Result, EpochSyncInfoError> { + let next_epoch_first_header = + self.get_header(self.next_epoch_first_hash, EpochSyncHashType::Other)?; + Ok(next_epoch_first_header.epoch_sync_data_hash()) + } + + pub fn get_header( + &self, + hash: CryptoHash, + hash_type: EpochSyncHashType, + ) -> Result<&BlockHeader, EpochSyncInfoError> { + self.headers.get(&hash).ok_or(EpochSyncInfoError::HashNotFound { + hash, + hash_type, + epoch_height: self.epoch_info.epoch_height(), + }) + } + } +} diff --git a/core/primitives/src/epoch_sync.rs b/core/primitives/src/epoch_sync.rs new file mode 100644 index 000000000..cd0703836 --- /dev/null +++ b/core/primitives/src/epoch_sync.rs @@ -0,0 +1,34 @@ +use crate::block_header::BlockHeader; +use crate::epoch_manager::block_info::BlockInfo; +use crate::epoch_manager::epoch_info::EpochInfo; +use crate::merkle::PartialMerkleTree; +use crate::views::LightClientBlockView; +use borsh::{BorshDeserialize, BorshSerialize}; + +#[derive(BorshSerialize, BorshDeserialize, Eq, PartialEq, Debug, Clone)] +pub struct EpochSyncFinalizationResponse { + pub cur_epoch_header: BlockHeader, + pub prev_epoch_headers: Vec, + pub header_sync_init_header: BlockHeader, + pub header_sync_init_header_tree: PartialMerkleTree, + // This Block Info is required by Epoch Manager when it checks if it's a good time to start a new Epoch. + // Epoch Manager asks for height difference by obtaining first Block Info of the Epoch. + pub prev_epoch_first_block_info: BlockInfo, + // This Block Info is required in State Sync that is started right after Epoch Sync is finished. + // It is used by `verify_chunk_signature_with_header_parts` in `save_block` as it calls `get_epoch_id_from_prev_block`. + pub prev_epoch_prev_last_block_info: BlockInfo, + // This Block Info is connected with the first actual Block received in State Sync. + // It is also used in Epoch Manager. + pub prev_epoch_last_block_info: BlockInfo, + pub prev_epoch_info: EpochInfo, + pub cur_epoch_info: EpochInfo, + // Next Epoch Info is required by Block Sync when Blocks of current Epoch will come. + // It asks in `process_block_single`, returns `Epoch Out Of Bounds` error otherwise. + pub next_epoch_info: EpochInfo, +} + +#[derive(BorshSerialize, BorshDeserialize, Eq, PartialEq, Debug, Clone)] +pub enum EpochSyncResponse { + UpToDate, + Advance { light_client_block_view: Box }, +} diff --git a/core/primitives/src/errors.rs b/core/primitives/src/errors.rs new file mode 100644 index 000000000..3e5cd4f9e --- /dev/null +++ b/core/primitives/src/errors.rs @@ -0,0 +1,1429 @@ +use crate::hash::CryptoHash; +use crate::serialize::dec_format; +use crate::types::{AccountId, Balance, EpochId, Gas, Nonce}; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_crypto::PublicKey; +use unc_primitives_core::types::{BlockHeight, ProtocolVersion}; +use unc_rpc_error_macro::RpcError; +use std::fmt::{Debug, Display}; + +/// Error returned in the ExecutionOutcome in case of failure +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + Clone, + PartialEq, + Eq, + RpcError, + serde::Deserialize, + serde::Serialize, +)] +pub enum TxExecutionError { + /// An error happened during Action execution + ActionError(ActionError), + /// An error happened during Transaction execution + InvalidTxError(InvalidTxError), +} + +impl std::error::Error for TxExecutionError {} + +impl Display for TxExecutionError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + TxExecutionError::ActionError(e) => write!(f, "{}", e), + TxExecutionError::InvalidTxError(e) => write!(f, "{}", e), + } + } +} + +impl From for TxExecutionError { + fn from(error: ActionError) -> Self { + TxExecutionError::ActionError(error) + } +} + +impl From for TxExecutionError { + fn from(error: InvalidTxError) -> Self { + TxExecutionError::InvalidTxError(error) + } +} + +/// Error returned from `Runtime::apply` +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RuntimeError { + /// An unexpected integer overflow occurred. The likely issue is an invalid state or the transition. + UnexpectedIntegerOverflow, + /// An error happened during TX verification and account charging. It's likely the chunk is invalid. + /// and should be challenged. + InvalidTxError(InvalidTxError), + /// Unexpected error which is typically related to the node storage corruption. + /// It's possible the input state is invalid or malicious. + StorageError(StorageError), + /// An error happens if `check_balance` fails, which is likely an indication of an invalid state. + BalanceMismatchError(Box), + /// The incoming receipt didn't pass the validation, it's likely a malicious behaviour. + ReceiptValidationError(ReceiptValidationError), + /// Error when accessing validator information. Happens inside epoch manager. + ValidatorError(EpochError), +} + +impl std::fmt::Display for RuntimeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + f.write_str(&format!("{:?}", self)) + } +} + +impl std::error::Error for RuntimeError {} + +/// Contexts in which `StorageError::MissingTrieValue` error might occur. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MissingTrieValueContext { + /// Missing trie value when reading from TrieIterator. + TrieIterator, + /// Missing trie value when reading from TriePrefetchingStorage. + TriePrefetchingStorage, + /// Missing trie value when reading from TrieMemoryPartialStorage. + TrieMemoryPartialStorage, + /// Missing trie value when reading from TrieStorage. + TrieStorage, +} + +/// Errors which may occur during working with trie storages, storing +/// trie values (trie nodes and state values) by their hashes. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum StorageError { + /// Key-value db internal failure + StorageInternalError, + /// Requested trie value by its hash which is missing in storage. + MissingTrieValue(MissingTrieValueContext, CryptoHash), + /// Found trie node which shouldn't be part of state. Raised during + /// validation of state sync parts where incorrect node was passed. + /// TODO (#8997): consider including hash of trie node. + UnexpectedTrieValue, + /// Either invalid state or key-value db is corrupted. + /// For PartialStorage it cannot be corrupted. + /// Error message is unreliable and for debugging purposes only. It's also probably ok to + /// panic in every place that produces this error. + /// We can check if db is corrupted by verifying everything in the state trie. + StorageInconsistentState(String), + /// Flat storage error, meaning that it doesn't support some block anymore. + /// We guarantee that such block cannot become final, thus block processing + /// must resume normally. + FlatStorageBlockNotSupported(String), + /// In-memory trie could not be loaded for some reason. + MemTrieLoadingError(String), +} + +impl std::fmt::Display for StorageError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + f.write_str(&format!("{:?}", self)) + } +} + +impl std::error::Error for StorageError {} + +/// An error happened during TX execution +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + Clone, + PartialEq, + Eq, + RpcError, + serde::Deserialize, + serde::Serialize, +)] +pub enum InvalidTxError { + /// Happens if a wrong AccessKey used or AccessKey has not enough permissions + InvalidAccessKeyError(InvalidAccessKeyError), + /// TX signer_id is not a valid [`AccountId`] + InvalidSignerId { signer_id: String }, + /// TX signer_id is not found in a storage + SignerDoesNotExist { signer_id: AccountId }, + /// Transaction nonce must be `account[access_key].nonce + 1`. + InvalidNonce { tx_nonce: Nonce, ak_nonce: Nonce }, + /// Transaction nonce is larger than the upper bound given by the block height + NonceTooLarge { tx_nonce: Nonce, upper_bound: Nonce }, + /// TX receiver_id is not a valid AccountId + InvalidReceiverId { receiver_id: String }, + /// TX signature is not valid + InvalidSignature, + /// Account does not have enough balance to cover TX cost + NotEnoughBalance { + signer_id: AccountId, + #[serde(with = "dec_format")] + balance: Balance, + #[serde(with = "dec_format")] + cost: Balance, + }, + /// Signer account doesn't have enough balance after transaction. + LackBalanceForState { + /// An account which doesn't have enough balance to cover storage. + signer_id: AccountId, + /// Required balance to cover the state. + #[serde(with = "dec_format")] + amount: Balance, + }, + /// An integer overflow occurred during transaction cost estimation. + CostOverflow, + /// Transaction parent block hash doesn't belong to the current chain + InvalidChain, + /// Transaction has expired + Expired, + /// An error occurred while validating actions of a Transaction. + ActionsValidation(ActionsValidationError), + /// The size of serialized transaction exceeded the limit. + TransactionSizeExceeded { size: u64, limit: u64 }, +} + +impl std::error::Error for InvalidTxError {} + +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + Clone, + PartialEq, + Eq, + RpcError, + serde::Deserialize, + serde::Serialize, +)] +pub enum InvalidAccessKeyError { + /// The access key identified by the `public_key` doesn't exist for the account + AccessKeyNotFound { account_id: AccountId, public_key: Box }, + /// Transaction `receiver_id` doesn't match the access key receiver_id + ReceiverMismatch { tx_receiver: AccountId, ak_receiver: String }, + /// Transaction method name isn't allowed by the access key + MethodNameMismatch { method_name: String }, + /// Transaction requires a full permission access key. + RequiresFullAccess, + /// Access Key does not have enough allowance to cover transaction cost + NotEnoughAllowance { + account_id: AccountId, + public_key: Box, + #[serde(with = "dec_format")] + allowance: Balance, + #[serde(with = "dec_format")] + cost: Balance, + }, + /// Having a deposit with a function call action is not allowed with a function call access key. + DepositWithFunctionCall, +} + +/// Describes the error for validating a list of actions. +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + Clone, + PartialEq, + Eq, + RpcError, + serde::Serialize, + serde::Deserialize, +)] +pub enum ActionsValidationError { + /// The delete action must be a final aciton in transaction + DeleteActionMustBeFinal, + /// The total prepaid gas (for all given actions) exceeded the limit. + TotalPrepaidGasExceeded { total_prepaid_gas: Gas, limit: Gas }, + /// The number of actions exceeded the given limit. + TotalNumberOfActionsExceeded { total_number_of_actions: u64, limit: u64 }, + /// The total number of bytes of the method names exceeded the limit in a Add Key action. + AddKeyMethodNamesNumberOfBytesExceeded { total_number_of_bytes: u64, limit: u64 }, + /// The length of some method name exceeded the limit in a Add Key action. + AddKeyMethodNameLengthExceeded { length: u64, limit: u64 }, + /// Integer overflow during a compute. + IntegerOverflow, + /// Invalid account ID. + InvalidAccountId { account_id: String }, + /// The size of the contract code exceeded the limit in a DeployContract action. + ContractSizeExceeded { size: u64, limit: u64 }, + /// The length of the method name exceeded the limit in a Function Call action. + FunctionCallMethodNameLengthExceeded { length: u64, limit: u64 }, + /// The length of the arguments exceeded the limit in a Function Call action. + FunctionCallArgumentsLengthExceeded { length: u64, limit: u64 }, + /// An attempt to stake with a public key that is not convertible to ristretto. + UnsuitableStakingKey { public_key: Box }, + /// The attached amount of gas in a FunctionCall action has to be a positive number. + FunctionCallZeroAttachedGas, + /// There should be the only one DelegateAction + DelegateActionMustBeOnlyOne, + /// The transaction includes a feature that the current protocol version + /// does not support. + /// + /// Note: we stringify the protocol feature name instead of using + /// `ProtocolFeature` here because we don't want to leak the internals of + /// that type into observable borsh serialization. + UnsupportedProtocolFeature { protocol_feature: String, version: ProtocolVersion }, +} + +/// Describes the error for validating a receipt. +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + Clone, + PartialEq, + Eq, + RpcError, + serde::Serialize, + serde::Deserialize, +)] +pub enum ReceiptValidationError { + /// The `predecessor_id` of a Receipt is not valid. + InvalidPredecessorId { account_id: String }, + /// The `receiver_id` of a Receipt is not valid. + InvalidReceiverId { account_id: String }, + /// The `signer_id` of an ActionReceipt is not valid. + InvalidSignerId { account_id: String }, + /// The `receiver_id` of a DataReceiver within an ActionReceipt is not valid. + InvalidDataReceiverId { account_id: String }, + /// The length of the returned data exceeded the limit in a DataReceipt. + ReturnedValueLengthExceeded { length: u64, limit: u64 }, + /// The number of input data dependencies exceeds the limit in an ActionReceipt. + NumberInputDataDependenciesExceeded { number_of_input_data_dependencies: u64, limit: u64 }, + /// An error occurred while validating actions of an ActionReceipt. + ActionsValidation(ActionsValidationError), +} + +impl Display for ReceiptValidationError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + ReceiptValidationError::InvalidPredecessorId { account_id } => { + write!(f, "The predecessor_id `{}` of a Receipt is not valid.", account_id) + } + ReceiptValidationError::InvalidReceiverId { account_id } => { + write!(f, "The receiver_id `{}` of a Receipt is not valid.", account_id) + } + ReceiptValidationError::InvalidSignerId { account_id } => { + write!(f, "The signer_id `{}` of an ActionReceipt is not valid.", account_id) + } + ReceiptValidationError::InvalidDataReceiverId { account_id } => write!( + f, + "The receiver_id `{}` of a DataReceiver within an ActionReceipt is not valid.", + account_id + ), + ReceiptValidationError::ReturnedValueLengthExceeded { length, limit } => write!( + f, + "The length of the returned data {} exceeded the limit {} in a DataReceipt", + length, limit + ), + ReceiptValidationError::NumberInputDataDependenciesExceeded { number_of_input_data_dependencies, limit } => write!( + f, + "The number of input data dependencies {} exceeded the limit {} in an ActionReceipt", + number_of_input_data_dependencies, limit + ), + ReceiptValidationError::ActionsValidation(e) => write!(f, "{}", e), + } + } +} + +impl std::error::Error for ReceiptValidationError {} + +impl Display for ActionsValidationError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + ActionsValidationError::DeleteActionMustBeFinal => { + write!(f, "The delete action must be the last action in transaction") + } + ActionsValidationError::TotalPrepaidGasExceeded { total_prepaid_gas, limit } => { + write!(f, "The total prepaid gas {} exceeds the limit {}", total_prepaid_gas, limit) + } + ActionsValidationError::TotalNumberOfActionsExceeded {total_number_of_actions, limit } => { + write!( + f, + "The total number of actions {} exceeds the limit {}", + total_number_of_actions, limit + ) + } + ActionsValidationError::AddKeyMethodNamesNumberOfBytesExceeded { total_number_of_bytes, limit } => write!( + f, + "The total number of bytes in allowed method names {} exceeds the maximum allowed number {} in a AddKey action", + total_number_of_bytes, limit + ), + ActionsValidationError::AddKeyMethodNameLengthExceeded { length, limit } => write!( + f, + "The length of some method name {} exceeds the maximum allowed length {} in a AddKey action", + length, limit + ), + ActionsValidationError::IntegerOverflow => write!( + f, + "Integer overflow during a compute", + ), + ActionsValidationError::InvalidAccountId { account_id } => write!( + f, + "Invalid account ID `{}`", + account_id + ), + ActionsValidationError::ContractSizeExceeded { size, limit } => write!( + f, + "The length of the contract size {} exceeds the maximum allowed size {} in a DeployContract action", + size, limit + ), + ActionsValidationError::FunctionCallMethodNameLengthExceeded { length, limit } => write!( + f, + "The length of the method name {} exceeds the maximum allowed length {} in a FunctionCall action", + length, limit + ), + ActionsValidationError::FunctionCallArgumentsLengthExceeded { length, limit } => write!( + f, + "The length of the arguments {} exceeds the maximum allowed length {} in a FunctionCall action", + length, limit + ), + ActionsValidationError::UnsuitableStakingKey { public_key } => write!( + f, + "The staking key must be ristretto compatible ED25519 key. {} is provided instead.", + public_key, + ), + ActionsValidationError::FunctionCallZeroAttachedGas => write!( + f, + "The attached amount of gas in a FunctionCall action has to be a positive number", + ), + ActionsValidationError::DelegateActionMustBeOnlyOne => write!( + f, + "The actions can contain the ony one DelegateAction" + ), + ActionsValidationError::UnsupportedProtocolFeature { protocol_feature, version } => write!( + f, + "Transaction requires protocol feature {} / version {} which is not supported by the current protocol version", + protocol_feature, + version, + ), + } + } +} + +impl std::error::Error for ActionsValidationError {} + +/// An error happened during Action execution +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + Clone, + PartialEq, + Eq, + RpcError, + serde::Deserialize, + serde::Serialize, +)] +pub struct ActionError { + /// Index of the failed action in the transaction. + /// Action index is not defined if ActionError.kind is `ActionErrorKind::LackBalanceForState` + pub index: Option, + /// The kind of ActionError happened + pub kind: ActionErrorKind, +} + +impl std::error::Error for ActionError {} + +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + Clone, + PartialEq, + Eq, + RpcError, + serde::Deserialize, + serde::Serialize, +)] +pub enum ActionErrorKind { + /// Happens when CreateAccount action tries to create an account with account_id which is already exists in the storage + AccountAlreadyExists { account_id: AccountId }, + /// Happens when TX receiver_id doesn't exist (but action is not Action::CreateAccount) + AccountDoesNotExist { account_id: AccountId }, + /// A top-level account ID can only be created by registrar. + CreateAccountOnlyByRegistrar { + account_id: AccountId, + registrar_account_id: AccountId, + predecessor_id: AccountId, + }, + + /// A newly created account must be under a namespace of the creator account + CreateAccountNotAllowed { account_id: AccountId, predecessor_id: AccountId }, + /// Administrative actions like `DeployContract`, `Stake`, `AddKey`, `DeleteKey`. can be proceed only if sender=receiver + /// or the first TX action is a `CreateAccount` action + ActorNoPermission { account_id: AccountId, actor_id: AccountId }, + /// Account tries to remove an access key that doesn't exist + DeleteKeyDoesNotExist { account_id: AccountId, public_key: Box }, + /// The public key is already used for an existing access key + AddKeyAlreadyExists { account_id: AccountId, public_key: Box }, + /// Account is staking and can not be deleted + DeleteAccountStaking { account_id: AccountId }, + /// ActionReceipt can't be completed, because the remaining balance will not be enough to cover storage. + LackBalanceForState { + /// An account which needs balance + account_id: AccountId, + /// Balance required to complete an action. + #[serde(with = "dec_format")] + amount: Balance, + }, + /// Account is not yet staked, but tries to unstake + TriesToUnstake { account_id: AccountId }, + /// The account doesn't have enough balance to increase the stake. + TriesToStake { + account_id: AccountId, + #[serde(with = "dec_format")] + stake: Balance, + #[serde(with = "dec_format")] + locked: Balance, + #[serde(with = "dec_format")] + balance: Balance, + }, + InsufficientStake { + account_id: AccountId, + #[serde(with = "dec_format")] + stake: Balance, + #[serde(with = "dec_format")] + minimum_stake: Balance, + }, + /// An error occurred during a `FunctionCall` Action, parameter is debug message. + FunctionCallError(FunctionCallError), + /// Error occurs when a new `ActionReceipt` created by the `FunctionCall` action fails + /// receipt validation. + NewReceiptValidationError(ReceiptValidationError), + /// Error occurs when a `CreateAccount` action is called on a NEAR-implicit or ETH-implicit account. + /// See NEAR-implicit account creation NEP: . + /// Also, see ETH-implicit account creation NEP: . + /// + /// TODO(#8598): This error is named very poorly. A better name would be + /// `OnlyNamedAccountCreationAllowed`. + OnlyImplicitAccountCreationAllowed { account_id: AccountId }, + /// Delete account whose state is large is temporarily banned. + DeleteAccountWithLargeState { account_id: AccountId }, + /// Signature does not match the provided actions and given signer public key. + DelegateActionInvalidSignature, + /// Receiver of the transaction doesn't match Sender of the delegate action + DelegateActionSenderDoesNotMatchTxReceiver { sender_id: AccountId, receiver_id: AccountId }, + /// Delegate action has expired. `max_block_height` is less than actual block height. + DelegateActionExpired, + /// The given public key doesn't exist for Sender account + DelegateActionAccessKeyError(InvalidAccessKeyError), + /// DelegateAction nonce must be greater sender[public_key].nonce + DelegateActionInvalidNonce { delegate_nonce: Nonce, ak_nonce: Nonce }, + /// DelegateAction nonce is larger than the upper bound given by the block height + DelegateActionNonceTooLarge { delegate_nonce: Nonce, upper_bound: Nonce }, + + /// The public key used for an not existed rsa key + RsaKeysNotFound { account_id: AccountId, public_key: Box }, +} + +impl From for ActionError { + fn from(e: ActionErrorKind) -> ActionError { + ActionError { index: None, kind: e } + } +} + +impl Display for InvalidTxError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + InvalidTxError::InvalidSignerId { signer_id } => { + write!(f, "Invalid signer account ID {:?} according to requirements", signer_id) + } + InvalidTxError::SignerDoesNotExist { signer_id } => { + write!(f, "Signer {:?} does not exist", signer_id) + } + InvalidTxError::InvalidAccessKeyError(access_key_error) => { + Display::fmt(&access_key_error, f) + } + InvalidTxError::InvalidNonce { tx_nonce, ak_nonce } => write!( + f, + "Transaction nonce {} must be larger than nonce of the used access key {}", + tx_nonce, ak_nonce + ), + InvalidTxError::InvalidReceiverId { receiver_id } => { + write!(f, "Invalid receiver account ID {:?} according to requirements", receiver_id) + } + InvalidTxError::InvalidSignature => { + write!(f, "Transaction is not signed with the given public key") + } + InvalidTxError::NotEnoughBalance { signer_id, balance, cost } => write!( + f, + "Sender {:?} does not have enough balance {} for operation costing {}", + signer_id, balance, cost + ), + InvalidTxError::LackBalanceForState { signer_id, amount } => { + write!(f, "Failed to execute, because the account {:?} wouldn't have enough balance to cover storage, required to have {} yoctoNEAR more", signer_id, amount) + } + InvalidTxError::CostOverflow => { + write!(f, "Transaction gas or balance cost is too high") + } + InvalidTxError::InvalidChain => { + write!(f, "Transaction parent block hash doesn't belong to the current chain") + } + InvalidTxError::Expired => { + write!(f, "Transaction has expired") + } + InvalidTxError::ActionsValidation(error) => { + write!(f, "Transaction actions validation error: {}", error) + } + InvalidTxError::NonceTooLarge { tx_nonce, upper_bound } => { + write!( + f, + "Transaction nonce {} must be smaller than the access key nonce upper bound {}", + tx_nonce, upper_bound + ) + } + InvalidTxError::TransactionSizeExceeded { size, limit } => { + write!(f, "Size of serialized transaction {} exceeded the limit {}", size, limit) + } + } + } +} + +impl From for InvalidTxError { + fn from(error: InvalidAccessKeyError) -> Self { + InvalidTxError::InvalidAccessKeyError(error) + } +} + +impl Display for InvalidAccessKeyError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + InvalidAccessKeyError::AccessKeyNotFound { account_id, public_key } => write!( + f, + "Signer {:?} doesn't have access key with the given public_key {}", + account_id, public_key + ), + InvalidAccessKeyError::ReceiverMismatch { tx_receiver, ak_receiver } => write!( + f, + "Transaction receiver_id {:?} doesn't match the access key receiver_id {:?}", + tx_receiver, ak_receiver + ), + InvalidAccessKeyError::MethodNameMismatch { method_name } => write!( + f, + "Transaction method name {:?} isn't allowed by the access key", + method_name + ), + InvalidAccessKeyError::RequiresFullAccess => { + write!(f, "Invalid access key type. Full-access keys are required for transactions that have multiple or non-function-call actions") + } + InvalidAccessKeyError::NotEnoughAllowance { + account_id, + public_key, + allowance, + cost, + } => write!( + f, + "Access Key {:?}:{} does not have enough balance {} for transaction costing {}", + account_id, public_key, allowance, cost + ), + InvalidAccessKeyError::DepositWithFunctionCall => { + write!(f, "Having a deposit with a function call action is not allowed with a function call access key.") + } + } + } +} + +impl std::error::Error for InvalidAccessKeyError {} + +/// Happens when the input balance doesn't match the output balance in Runtime apply. +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + Clone, + PartialEq, + Eq, + RpcError, + serde::Deserialize, + serde::Serialize, +)] +pub struct BalanceMismatchError { + // Input balances + #[serde(with = "dec_format")] + pub incoming_validator_rewards: Balance, + #[serde(with = "dec_format")] + pub initial_accounts_balance: Balance, + #[serde(with = "dec_format")] + pub incoming_receipts_balance: Balance, + #[serde(with = "dec_format")] + pub processed_delayed_receipts_balance: Balance, + #[serde(with = "dec_format")] + pub initial_postponed_receipts_balance: Balance, + // Output balances + #[serde(with = "dec_format")] + pub final_accounts_balance: Balance, + #[serde(with = "dec_format")] + pub outgoing_receipts_balance: Balance, + #[serde(with = "dec_format")] + pub new_delayed_receipts_balance: Balance, + #[serde(with = "dec_format")] + pub final_postponed_receipts_balance: Balance, + #[serde(with = "dec_format")] + pub tx_burnt_amount: Balance, + #[serde(with = "dec_format")] + pub slashed_burnt_amount: Balance, + #[serde(with = "dec_format")] + pub other_burnt_amount: Balance, +} + +impl Display for BalanceMismatchError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + // Using saturating add to avoid overflow in display + let initial_balance = self + .incoming_validator_rewards + .saturating_add(self.initial_accounts_balance) + .saturating_add(self.incoming_receipts_balance) + .saturating_add(self.processed_delayed_receipts_balance) + .saturating_add(self.initial_postponed_receipts_balance); + let final_balance = self + .final_accounts_balance + .saturating_add(self.outgoing_receipts_balance) + .saturating_add(self.new_delayed_receipts_balance) + .saturating_add(self.final_postponed_receipts_balance) + .saturating_add(self.tx_burnt_amount) + .saturating_add(self.slashed_burnt_amount) + .saturating_add(self.other_burnt_amount); + write!( + f, + "Balance Mismatch Error. The input balance {} doesn't match output balance {}\n\ + Inputs:\n\ + \tIncoming validator rewards sum: {}\n\ + \tInitial accounts balance sum: {}\n\ + \tIncoming receipts balance sum: {}\n\ + \tProcessed delayed receipts balance sum: {}\n\ + \tInitial postponed receipts balance sum: {}\n\ + Outputs:\n\ + \tFinal accounts balance sum: {}\n\ + \tOutgoing receipts balance sum: {}\n\ + \tNew delayed receipts balance sum: {}\n\ + \tFinal postponed receipts balance sum: {}\n\ + \tTx fees burnt amount: {}\n\ + \tSlashed amount: {}\n\ + \tOther burnt amount: {}", + initial_balance, + final_balance, + self.incoming_validator_rewards, + self.initial_accounts_balance, + self.incoming_receipts_balance, + self.processed_delayed_receipts_balance, + self.initial_postponed_receipts_balance, + self.final_accounts_balance, + self.outgoing_receipts_balance, + self.new_delayed_receipts_balance, + self.final_postponed_receipts_balance, + self.tx_burnt_amount, + self.slashed_burnt_amount, + self.other_burnt_amount, + ) + } +} + +impl std::error::Error for BalanceMismatchError {} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq)] +pub struct IntegerOverflowError; + +impl std::fmt::Display for IntegerOverflowError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + f.write_str(&format!("{:?}", self)) + } +} + +impl std::error::Error for IntegerOverflowError {} + +impl From for InvalidTxError { + fn from(_: IntegerOverflowError) -> Self { + InvalidTxError::CostOverflow + } +} + +impl From for RuntimeError { + fn from(_: IntegerOverflowError) -> Self { + RuntimeError::UnexpectedIntegerOverflow + } +} + +impl From for RuntimeError { + fn from(e: StorageError) -> Self { + RuntimeError::StorageError(e) + } +} + +impl From for RuntimeError { + fn from(e: BalanceMismatchError) -> Self { + RuntimeError::BalanceMismatchError(Box::new(e)) + } +} + +impl From for RuntimeError { + fn from(e: InvalidTxError) -> Self { + RuntimeError::InvalidTxError(e) + } +} + +impl From for RuntimeError { + fn from(e: EpochError) -> Self { + RuntimeError::ValidatorError(e) + } +} + +impl Display for ActionError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "Action #{}: {}", self.index.unwrap_or_default(), self.kind) + } +} + +impl Display for ActionErrorKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + ActionErrorKind::AccountAlreadyExists { account_id } => { + write!(f, "Can't create a new account {:?}, because it already exists", account_id) + } + ActionErrorKind::AccountDoesNotExist { account_id } => write!( + f, + "Can't complete the action because account {:?} doesn't exist", + account_id + ), + ActionErrorKind::ActorNoPermission { actor_id, account_id } => write!( + f, + "Actor {:?} doesn't have permission to account {:?} to complete the action", + actor_id, account_id + ), + ActionErrorKind::LackBalanceForState { account_id, amount } => write!( + f, + "The account {} wouldn't have enough balance to cover storage, required to have {} yoctoNEAR more", + account_id, amount + ), + ActionErrorKind::TriesToUnstake { account_id } => { + write!(f, "Account {:?} is not yet staked, but tries to unstake", account_id) + } + ActionErrorKind::TriesToStake { account_id, stake, locked, balance } => write!( + f, + "Account {:?} tries to stake {}, but has staked {} and only has {}", + account_id, stake, locked, balance + ), + ActionErrorKind::CreateAccountOnlyByRegistrar { account_id, registrar_account_id, predecessor_id } => write!( + f, + "A top-level account ID {:?} can't be created by {:?}, short top-level account IDs can only be created by {:?}", + account_id, predecessor_id, registrar_account_id, + ), + ActionErrorKind::CreateAccountNotAllowed { account_id, predecessor_id } => write!( + f, + "A sub-account ID {:?} can't be created by account {:?}", + account_id, predecessor_id, + ), + ActionErrorKind::DeleteKeyDoesNotExist { account_id, .. } => write!( + f, + "Account {:?} tries to remove an access key that doesn't exist", + account_id + ), + ActionErrorKind::AddKeyAlreadyExists { public_key, .. } => write!( + f, + "The public key {:?} is already used for an existing access key", + public_key + ), + ActionErrorKind::DeleteAccountStaking { account_id } => { + write!(f, "Account {:?} is staking and can not be deleted", account_id) + } + ActionErrorKind::FunctionCallError(s) => write!(f, "{:?}", s), + ActionErrorKind::NewReceiptValidationError(e) => { + write!(f, "An new action receipt created during a FunctionCall is not valid: {}", e) + } + ActionErrorKind::InsufficientStake { account_id, stake, minimum_stake } => write!(f, "Account {} tries to stake {} but minimum required stake is {}", account_id, stake, minimum_stake), + ActionErrorKind::OnlyImplicitAccountCreationAllowed { account_id } => write!(f, "CreateAccount action is called on hex-characters account of length 64 {}", account_id), + ActionErrorKind::DeleteAccountWithLargeState { account_id } => write!(f, "The state of account {} is too large and therefore cannot be deleted", account_id), + ActionErrorKind::DelegateActionInvalidSignature => write!(f, "DelegateAction is not signed with the given public key"), + ActionErrorKind::DelegateActionSenderDoesNotMatchTxReceiver { sender_id, receiver_id } => write!(f, "Transaction receiver {} doesn't match DelegateAction sender {}", receiver_id, sender_id), + ActionErrorKind::DelegateActionExpired => write!(f, "DelegateAction has expired"), + ActionErrorKind::DelegateActionAccessKeyError(access_key_error) => Display::fmt(&access_key_error, f), + ActionErrorKind::DelegateActionInvalidNonce { delegate_nonce, ak_nonce } => write!(f, "DelegateAction nonce {} must be larger than nonce of the used access key {}", delegate_nonce, ak_nonce), + ActionErrorKind::DelegateActionNonceTooLarge { delegate_nonce, upper_bound } => write!(f, "DelegateAction nonce {} must be smaller than the access key nonce upper bound {}", delegate_nonce, upper_bound), + ActionErrorKind::RsaKeysNotFound { public_key, .. } => write!( + f, + "The public key {:?} is doesn't exist rsa key", + public_key + ), + } + } +} +#[derive(Eq, PartialEq, Clone)] +pub enum BlockError { + /// Error calculating threshold from given stakes for given number of seats. + /// Only should happened if calling code doesn't check for integer value of stake > number of seats. + ThresholdError { + stake_sum: Balance, + num_seats: u64, + }, + /// Requesting validators for an epoch that wasn't computed yet. + BlockOutOfBounds(CryptoHash), + /// Missing block hash in the storage (means there is some structural issue). + MissingBlock(CryptoHash), + /// Error due to IO (DB read/write, serialization, etc.). + IOErr(String), + /// Given account ID is not a validator in the given block height. + NotAValidator(AccountId, BlockHeight), + /// Error getting information for a shard + ShardingError(String), + NotEnoughValidators { + num_validators: u64, + num_shards: u64, + }, + /// Error selecting validators for a chunk. + ChunkValidatorSelectionError(String), + /// ValidatorTotalPowerError + ValidatorTotalPowerError(String), + /// NoAvailableValidator + NoAvailableValidator(String), +} + +impl std::error::Error for crate::errors::BlockError {} + +impl Display for crate::errors::BlockError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + crate::errors::BlockError::ThresholdError { stake_sum, num_seats } => write!( + f, + "Total stake {} must be higher than the number of seats {}", + stake_sum, num_seats + ), + crate::errors::BlockError::BlockOutOfBounds(block_height) => { + write!(f, "Block {:?} is out of bounds", block_height) + } + crate::errors::BlockError::MissingBlock(hash) => write!(f, "Missing block {}", hash), + crate::errors::BlockError::IOErr(err) => write!(f, "IO: {}", err), + crate::errors::BlockError::NotAValidator(account_id, block_height) => { + write!(f, "{} is not a validator in epoch {:?}", account_id, block_height) + } + crate::errors::BlockError::ShardingError(err) => write!(f, "Sharding Error: {}", err), + crate::errors::BlockError::NotEnoughValidators { num_shards, num_validators } => { + write!(f, "There were not enough validator proposals to fill all shards. num_proposals: {}, num_shards: {}", num_validators, num_shards) + } + crate::errors::BlockError::ChunkValidatorSelectionError(err) => { + write!(f, "Error selecting validators for a chunk: {}", err) + } + crate::errors::BlockError::ValidatorTotalPowerError(err) => { + write!(f, "Error when computing total power: {}", err) + } + crate::errors::BlockError::NoAvailableValidator(err) => { + write!(f, "Error selecting produce: {}", err) + } + } + } +} + +impl Debug for crate::errors::BlockError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + crate::errors::BlockError::ThresholdError { stake_sum, num_seats } => { + write!(f, "ThresholdError({}, {})", stake_sum, num_seats) + } + crate::errors::BlockError::BlockOutOfBounds(block_height) => write!(f, "EpochOutOfBounds({:?})", block_height), + crate::errors::BlockError::MissingBlock(hash) => write!(f, "MissingBlock({})", hash), + crate::errors::BlockError::IOErr(err) => write!(f, "IOErr({})", err), + crate::errors::BlockError::NotAValidator(account_id, block_height) => { + write!(f, "NotAValidator({}, {:?})", account_id, block_height) + } + crate::errors::BlockError::ShardingError(err) => write!(f, "ShardingError({})", err), + crate::errors::BlockError::NotEnoughValidators { num_shards, num_validators } => { + write!(f, "NotEnoughValidators({}, {})", num_validators, num_shards) + } + crate::errors::BlockError::ChunkValidatorSelectionError(err) => { + write!(f, "ChunkValidatorSelectionError({})", err) + } + crate::errors::BlockError::ValidatorTotalPowerError(err) => { + write!(f, "Error when computing total power: {}", err) + } + crate::errors::BlockError::NoAvailableValidator(err) => { + write!(f, "Error selecting produce: {}", err) + } + } + } +} + +impl From for crate::errors::BlockError { + fn from(error: std::io::Error) -> Self { + crate::errors::BlockError::IOErr(error.to_string()) + } +} + +impl From for BlockError { + fn from(error: EpochError) -> Self { + match error { + EpochError::IOErr(..) => { + BlockError::IOErr(error.to_string()) + }, + EpochError::ChunkValidatorSelectionError(..) => { + BlockError::ChunkValidatorSelectionError(error.to_string()) + }, + EpochError::EpochOutOfBounds(..) => { + BlockError::BlockOutOfBounds(CryptoHash::default()) + }, + EpochError::MissingBlock(block_hash) => { + BlockError::MissingBlock(block_hash) + }, + EpochError::NotAValidator(account_id, _hash) => { + BlockError::NotAValidator(account_id, 0) + }, + EpochError::NotEnoughValidators{ num_validators: x, num_shards: y } => { + BlockError::NotEnoughValidators{ num_validators: x, num_shards: y } + }, + EpochError::ShardingError(..) => { + BlockError::ShardingError(error.to_string()) + }, + EpochError::ThresholdError{ + stake_sum: stake, + num_seats: seats, + } => { + BlockError::ThresholdError{ + stake_sum: stake, + num_seats: seats, + } + } + } + + } +} +#[derive(Eq, PartialEq, Clone)] +pub enum EpochError { + /// Error calculating threshold from given stakes for given number of seats. + /// Only should happened if calling code doesn't check for integer value of stake > number of seats. + ThresholdError { + stake_sum: Balance, + num_seats: u64, + }, + /// Requesting validators for an epoch that wasn't computed yet. + EpochOutOfBounds(EpochId), + /// Missing block hash in the storage (means there is some structural issue). + MissingBlock(CryptoHash), + /// Error due to IO (DB read/write, serialization, etc.). + IOErr(String), + /// Given account ID is not a validator in the given epoch ID. + NotAValidator(AccountId, EpochId), + /// Error getting information for a shard + ShardingError(String), + NotEnoughValidators { + num_validators: u64, + num_shards: u64, + }, + /// Error selecting validators for a chunk. + ChunkValidatorSelectionError(String), +} + +impl std::error::Error for EpochError {} + +impl Display for EpochError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + EpochError::ThresholdError { stake_sum, num_seats } => write!( + f, + "Total stake {} must be higher than the number of seats {}", + stake_sum, num_seats + ), + EpochError::EpochOutOfBounds(epoch_id) => { + write!(f, "Epoch {:?} is out of bounds", epoch_id) + } + EpochError::MissingBlock(hash) => write!(f, "Missing block {}", hash), + EpochError::IOErr(err) => write!(f, "IO: {}", err), + EpochError::NotAValidator(account_id, epoch_id) => { + write!(f, "{} is not a validator in epoch {:?}", account_id, epoch_id) + } + EpochError::ShardingError(err) => write!(f, "Sharding Error: {}", err), + EpochError::NotEnoughValidators { num_shards, num_validators } => { + write!(f, "There were not enough validator proposals to fill all shards. num_proposals: {}, num_shards: {}", num_validators, num_shards) + } + EpochError::ChunkValidatorSelectionError(err) => { + write!(f, "Error selecting validators for a chunk: {}", err) + } + } + } +} + +impl Debug for EpochError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + EpochError::ThresholdError { stake_sum, num_seats } => { + write!(f, "ThresholdError({}, {})", stake_sum, num_seats) + } + EpochError::EpochOutOfBounds(epoch_id) => write!(f, "EpochOutOfBounds({:?})", epoch_id), + EpochError::MissingBlock(hash) => write!(f, "MissingBlock({})", hash), + EpochError::IOErr(err) => write!(f, "IOErr({})", err), + EpochError::NotAValidator(account_id, epoch_id) => { + write!(f, "NotAValidator({}, {:?})", account_id, epoch_id) + } + EpochError::ShardingError(err) => write!(f, "ShardingError({})", err), + EpochError::NotEnoughValidators { num_shards, num_validators } => { + write!(f, "NotEnoughValidators({}, {})", num_validators, num_shards) + } + EpochError::ChunkValidatorSelectionError(err) => { + write!(f, "ChunkValidatorSelectionError({})", err) + } + } + } +} + +impl From for EpochError { + fn from(error: std::io::Error) -> Self { + EpochError::IOErr(error.to_string()) + } +} + +impl From for EpochError { + fn from(error: BlockError) -> Self { + match error { + BlockError::IOErr(..) => { + EpochError::IOErr(error.to_string()) + }, + BlockError::ChunkValidatorSelectionError(..) => { + EpochError::ChunkValidatorSelectionError(error.to_string()) + }, + BlockError::BlockOutOfBounds(..) => { + EpochError::EpochOutOfBounds(EpochId::default()) + }, + BlockError::MissingBlock(block_hash) => { + EpochError::MissingBlock(block_hash) + }, + BlockError::NotAValidator(account_id, _block_height) => { + EpochError::NotAValidator(account_id, EpochId::default()) + }, + BlockError::NotEnoughValidators{ num_validators: x, num_shards: y } => { + EpochError::NotEnoughValidators{ num_validators: x, num_shards: y } + }, + BlockError::ShardingError(..) => { + EpochError::ShardingError(error.to_string()) + }, + BlockError::ThresholdError{ + stake_sum: stake, + num_seats: seats, + } => { + EpochError::ThresholdError{ + stake_sum: stake, + num_seats: seats, + } + }, + BlockError::ValidatorTotalPowerError(..) => { + EpochError::IOErr(error.to_string()) + }, + BlockError::NoAvailableValidator(..) => { + EpochError::IOErr(error.to_string()) + } + } + + } +} + +#[derive( + Debug, + Clone, + PartialEq, + Eq, + BorshDeserialize, + BorshSerialize, + RpcError, + serde::Deserialize, + serde::Serialize, +)] +/// Error that can occur while preparing or executing Wasm smart-contract. +pub enum PrepareError { + /// Error happened while serializing the module. + Serialization, + /// Error happened while deserializing the module. + Deserialization, + /// Internal memory declaration has been found in the module. + InternalMemoryDeclared, + /// Gas instrumentation failed. + /// + /// This most likely indicates the module isn't valid. + GasInstrumentation, + /// Stack instrumentation failed. + /// + /// This most likely indicates the module isn't valid. + StackHeightInstrumentation, + /// Error happened during instantiation. + /// + /// This might indicate that `start` function trapped, or module isn't + /// instantiable and/or unlinkable. + Instantiate, + /// Error creating memory. + Memory, + /// Contract contains too many functions. + TooManyFunctions, + /// Contract contains too many locals. + TooManyLocals, +} + +/// A kind of a trap happened during execution of a binary +#[derive( + Debug, + Clone, + PartialEq, + Eq, + BorshDeserialize, + BorshSerialize, + RpcError, + serde::Deserialize, + serde::Serialize, + strum::IntoStaticStr, +)] +pub enum WasmTrap { + /// An `unreachable` opcode was executed. + Unreachable, + /// Call indirect incorrect signature trap. + IncorrectCallIndirectSignature, + /// Memory out of bounds trap. + MemoryOutOfBounds, + /// Call indirect out of bounds trap. + CallIndirectOOB, + /// An arithmetic exception, e.g. divided by zero. + IllegalArithmetic, + /// Misaligned atomic access trap. + MisalignedAtomicAccess, + /// Indirect call to null. + IndirectCallToNull, + /// Stack overflow. + StackOverflow, + /// Generic trap. + GenericTrap, +} + +#[derive( + Debug, + Clone, + PartialEq, + Eq, + BorshDeserialize, + BorshSerialize, + RpcError, + serde::Deserialize, + serde::Serialize, + strum::IntoStaticStr, +)] +pub enum HostError { + /// String encoding is bad UTF-16 sequence + BadUTF16, + /// String encoding is bad UTF-8 sequence + BadUTF8, + /// Exceeded the prepaid gas + GasExceeded, + /// Exceeded the maximum amount of gas allowed to burn per contract + GasLimitExceeded, + /// Exceeded the account balance + BalanceExceeded, + /// Tried to call an empty method name + EmptyMethodName, + /// Smart contract panicked + GuestPanic { panic_msg: String }, + /// IntegerOverflow happened during a contract execution + IntegerOverflow, + /// `promise_idx` does not correspond to existing promises + InvalidPromiseIndex { promise_idx: u64 }, + /// Actions can only be appended to non-joint promise. + CannotAppendActionToJointPromise, + /// Returning joint promise is currently prohibited + CannotReturnJointPromise, + /// Accessed invalid promise result index + InvalidPromiseResultIndex { result_idx: u64 }, + /// Accessed invalid register id + InvalidRegisterId { register_id: u64 }, + /// Iterator `iterator_index` was invalidated after its creation by performing a mutable operation on trie + IteratorWasInvalidated { iterator_index: u64 }, + /// Accessed memory outside the bounds + MemoryAccessViolation, + /// VM Logic returned an invalid receipt index + InvalidReceiptIndex { receipt_index: u64 }, + /// Iterator index `iterator_index` does not exist + InvalidIteratorIndex { iterator_index: u64 }, + /// VM Logic returned an invalid account id + InvalidAccountId, + /// VM Logic returned an invalid method name + InvalidMethodName, + /// VM Logic provided an invalid public key + InvalidPublicKey, + /// `method_name` is not allowed in view calls + ProhibitedInView { method_name: String }, + /// The total number of logs will exceed the limit. + NumberOfLogsExceeded { limit: u64 }, + /// The storage key length exceeded the limit. + KeyLengthExceeded { length: u64, limit: u64 }, + /// The storage value length exceeded the limit. + ValueLengthExceeded { length: u64, limit: u64 }, + /// The total log length exceeded the limit. + TotalLogLengthExceeded { length: u64, limit: u64 }, + /// The maximum number of promises within a FunctionCall exceeded the limit. + NumberPromisesExceeded { number_of_promises: u64, limit: u64 }, + /// The maximum number of input data dependencies exceeded the limit. + NumberInputDataDependenciesExceeded { number_of_input_data_dependencies: u64, limit: u64 }, + /// The returned value length exceeded the limit. + ReturnedValueLengthExceeded { length: u64, limit: u64 }, + /// The contract size for DeployContract action exceeded the limit. + ContractSizeExceeded { size: u64, limit: u64 }, + /// The host function was deprecated. + Deprecated { method_name: String }, + /// General errors for ECDSA recover. + ECRecoverError { msg: String }, + /// Invalid input to alt_bn128 familiy of functions (e.g., point which isn't + /// on the curve). + AltBn128InvalidInput { msg: String }, + /// Invalid input to ed25519 signature verification function (e.g. signature cannot be + /// derived from bytes). + Ed25519VerifyInvalidInput { msg: String }, +} + +#[derive( + Debug, + Clone, + PartialEq, + Eq, + BorshDeserialize, + BorshSerialize, + RpcError, + serde::Deserialize, + serde::Serialize, + strum::IntoStaticStr, +)] +pub enum MethodResolveError { + MethodEmptyName, + MethodNotFound, + MethodInvalidSignature, +} + +#[derive( + Debug, + Clone, + PartialEq, + Eq, + BorshDeserialize, + BorshSerialize, + RpcError, + serde::Deserialize, + serde::Serialize, + strum::IntoStaticStr, +)] +pub enum CompilationError { + CodeDoesNotExist { + account_id: AccountId, + }, + PrepareError(PrepareError), + /// This is for defense in depth. + /// We expect our runtime-independent preparation code to fully catch all invalid wasms, + /// but, if it ever misses something we’ll emit this error + WasmerCompileError { + msg: String, + }, +} + +/// Serializable version of `unc-vm-runner::FunctionCallError`. +/// +/// Must never reorder/remove elements, can only add new variants at the end (but do that very +/// carefully). It describes stable serialization format, and only used by serialization logic. +#[derive( + Debug, + Clone, + PartialEq, + Eq, + BorshDeserialize, + BorshSerialize, + serde::Serialize, + serde::Deserialize, +)] +pub enum FunctionCallError { + /// Wasm compilation error + CompilationError(CompilationError), + /// Wasm binary env link error + /// + /// Note: this is only to deserialize old data, use execution error for new data + LinkError { + msg: String, + }, + /// Import/export resolve error + MethodResolveError(MethodResolveError), + /// A trap happened during execution of a binary + /// + /// Note: this is only to deserialize old data, use execution error for new data + WasmTrap(WasmTrap), + WasmUnknownError, + /// Note: this is only to deserialize old data, use execution error for new data + HostError(HostError), + // Unused, can be reused by a future error but must be exactly one error to keep ExecutionError + // error borsh serialized at correct index + _EVMError, + ExecutionError(String), +} + +impl From for MethodResolveError { + fn from(outer_err: unc_vm_runner::logic::errors::MethodResolveError) -> Self { + use unc_vm_runner::logic::errors::MethodResolveError as MRE; + match outer_err { + MRE::MethodEmptyName => Self::MethodEmptyName, + MRE::MethodNotFound => Self::MethodNotFound, + MRE::MethodInvalidSignature => Self::MethodInvalidSignature, + } + } +} + +impl From for PrepareError { + fn from(outer_err: unc_vm_runner::logic::errors::PrepareError) -> Self { + use unc_vm_runner::logic::errors::PrepareError as PE; + match outer_err { + PE::Serialization => Self::Serialization, + PE::Deserialization => Self::Deserialization, + PE::InternalMemoryDeclared => Self::InternalMemoryDeclared, + PE::GasInstrumentation => Self::GasInstrumentation, + PE::StackHeightInstrumentation => Self::StackHeightInstrumentation, + PE::Instantiate => Self::Instantiate, + PE::Memory => Self::Memory, + PE::TooManyFunctions => Self::TooManyFunctions, + PE::TooManyLocals => Self::TooManyLocals, + } + } +} + +impl From for CompilationError { + fn from(outer_err: unc_vm_runner::logic::errors::CompilationError) -> Self { + use unc_vm_runner::logic::errors::CompilationError as CE; + match outer_err { + CE::CodeDoesNotExist { account_id } => Self::CodeDoesNotExist { + account_id: account_id.parse().expect("account_id in error must be valid"), + }, + CE::PrepareError(pe) => Self::PrepareError(pe.into()), + CE::WasmerCompileError { msg } => Self::WasmerCompileError { msg }, + } + } +} + +impl From for FunctionCallError { + fn from(outer_err: unc_vm_runner::logic::errors::FunctionCallError) -> Self { + use unc_vm_runner::logic::errors::FunctionCallError as FCE; + match outer_err { + FCE::CompilationError(e) => Self::CompilationError(e.into()), + FCE::MethodResolveError(e) => Self::MethodResolveError(e.into()), + // Note: We deliberately collapse all execution errors for + // serialization to make the DB representation less dependent + // on specific types in Rust code. + FCE::HostError(ref _e) => Self::ExecutionError(outer_err.to_string()), + FCE::LinkError { msg } => Self::ExecutionError(format!("Link Error: {}", msg)), + FCE::WasmTrap(ref _e) => Self::ExecutionError(outer_err.to_string()), + } + } +} + +#[cfg(feature = "new_epoch_sync")] +pub mod epoch_sync { + use unc_primitives_core::hash::CryptoHash; + use unc_primitives_core::types::EpochHeight; + use std::fmt::Debug; + + #[derive(Eq, PartialEq, Clone, strum::Display, Debug)] + pub enum EpochSyncHashType { + LastEpochBlock, + LastFinalBlock, + FirstEpochBlock, + NextEpochFirstBlock, + Other, + BlockToSave, + } + + #[derive(Eq, PartialEq, Clone, thiserror::Error, Debug)] + pub enum EpochSyncInfoError { + #[error("{hash_type} hash {hash:?} not a part of EpochSyncInfo for epoch {epoch_height}")] + HashNotFound { hash: CryptoHash, hash_type: EpochSyncHashType, epoch_height: EpochHeight }, + #[error("all_block_hashes.len() < 2 for epoch {epoch_height}")] + ShortEpoch { epoch_height: EpochHeight }, + } +} diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs new file mode 100644 index 000000000..3f434c8c6 --- /dev/null +++ b/core/primitives/src/lib.rs @@ -0,0 +1,43 @@ +pub use unc_primitives_core::account; +pub use unc_primitives_core::borsh; +pub use unc_primitives_core::config; +pub use unc_primitives_core::hash; +pub use unc_primitives_core::num_rational; +pub use unc_primitives_core::serialize; + +pub mod action; +pub mod block; +pub mod block_header; +pub mod challenge; +pub mod chunk_validation; +pub mod epoch_manager; +pub mod epoch_sync; +pub mod errors; +pub mod merkle; +pub mod network; +pub mod rand; +pub mod receipt; +pub mod runtime; +pub mod sandbox; +pub mod shard_layout; +pub mod sharding; +pub mod signable_message; +pub mod state; +pub mod state_part; +pub mod state_record; +pub mod state_sync; +pub mod static_clock; +pub mod telemetry; +pub mod test_utils; +pub mod transaction; +pub mod trie_key; +pub mod types; +mod upgrade_schedule; +pub mod utils; +pub mod validator_mandates; +pub mod validator_signer; +pub mod version; +pub mod views; + +pub use crate::version::checked_feature; +pub use unc_primitives_core::chains; diff --git a/core/primitives/src/merkle.rs b/core/primitives/src/merkle.rs new file mode 100644 index 000000000..16bbc9f70 --- /dev/null +++ b/core/primitives/src/merkle.rs @@ -0,0 +1,274 @@ +use crate::hash::CryptoHash; +use crate::types::MerkleHash; +use borsh::{BorshDeserialize, BorshSerialize}; + +#[derive( + Debug, + Clone, + PartialEq, + Eq, + BorshSerialize, + BorshDeserialize, + serde::Serialize, + serde::Deserialize, +)] +pub struct MerklePathItem { + pub hash: MerkleHash, + pub direction: Direction, +} + +pub type MerklePath = Vec; + +#[derive( + Debug, + Clone, + PartialEq, + Eq, + BorshSerialize, + BorshDeserialize, + serde::Serialize, + serde::Deserialize, +)] +pub enum Direction { + Left, + Right, +} + +pub fn combine_hash(hash1: &MerkleHash, hash2: &MerkleHash) -> MerkleHash { + CryptoHash::hash_borsh((hash1, hash2)) +} + +/// Merklize an array of items. If the array is empty, returns hash of 0 +pub fn merklize(arr: &[T]) -> (MerkleHash, Vec) { + if arr.is_empty() { + return (MerkleHash::default(), vec![]); + } + let mut len = arr.len().next_power_of_two(); + let mut hashes = arr.iter().map(CryptoHash::hash_borsh).collect::>(); + + // degenerate case + if len == 1 { + return (hashes[0], vec![vec![]]); + } + let mut arr_len = arr.len(); + let mut paths: Vec = (0..arr_len) + .map(|i| { + if i % 2 == 0 { + if i + 1 < arr_len { + vec![MerklePathItem { + hash: hashes[(i + 1) as usize], + direction: Direction::Right, + }] + } else { + vec![] + } + } else { + vec![MerklePathItem { hash: hashes[(i - 1) as usize], direction: Direction::Left }] + } + }) + .collect(); + + let mut counter = 1; + while len > 1 { + len /= 2; + counter *= 2; + for i in 0..len { + let hash = if 2 * i >= arr_len { + continue; + } else if 2 * i + 1 >= arr_len { + hashes[2 * i] + } else { + combine_hash(&hashes[2 * i], &hashes[2 * i + 1]) + }; + hashes[i] = hash; + if len > 1 { + if i % 2 == 0 { + for j in 0..counter { + let index = ((i + 1) * counter + j) as usize; + if index < arr.len() { + paths[index].push(MerklePathItem { hash, direction: Direction::Left }); + } + } + } else { + for j in 0..counter { + let index = ((i - 1) * counter + j) as usize; + if index < arr.len() { + paths[index].push(MerklePathItem { hash, direction: Direction::Right }); + } + } + } + } + } + arr_len = (arr_len + 1) / 2; + } + (hashes[0], paths) +} + +/// Verify merkle path for given item and corresponding path. +pub fn verify_path(root: MerkleHash, path: &MerklePath, item: T) -> bool { + verify_hash(root, path, CryptoHash::hash_borsh(item)) +} + +pub fn verify_hash(root: MerkleHash, path: &MerklePath, item_hash: MerkleHash) -> bool { + compute_root_from_path(path, item_hash) == root +} + +pub fn compute_root_from_path(path: &MerklePath, item_hash: MerkleHash) -> MerkleHash { + let mut res = item_hash; + for item in path { + match item.direction { + Direction::Left => { + res = combine_hash(&item.hash, &res); + } + Direction::Right => { + res = combine_hash(&res, &item.hash); + } + } + } + res +} + +pub fn compute_root_from_path_and_item( + path: &MerklePath, + item: T, +) -> MerkleHash { + compute_root_from_path(path, CryptoHash::hash_borsh(item)) +} + +/// Merkle tree that only maintains the path for the next leaf, i.e, +/// when a new leaf is inserted, the existing `path` is its proof. +/// The root can be computed by folding `path` from right but is not explicitly +/// maintained to save space. +/// The size of the object is O(log(n)) where n is the number of leaves in the tree, i.e, `size`. +#[derive( + Default, Clone, BorshSerialize, BorshDeserialize, Eq, PartialEq, Debug, serde::Serialize, +)] +pub struct PartialMerkleTree { + /// Path for the next leaf. + path: Vec, + /// Number of leaves in the tree. + size: u64, +} + +impl PartialMerkleTree { + pub fn root(&self) -> MerkleHash { + if self.path.is_empty() { + CryptoHash::default() + } else { + let mut res = *self.path.last().unwrap(); + let len = self.path.len(); + for i in (0..len - 1).rev() { + res = combine_hash(&self.path[i], &res); + } + res + } + } + + pub fn insert(&mut self, elem: MerkleHash) { + let mut s = self.size; + let mut node = elem; + while s % 2 == 1 { + let last_path_elem = self.path.pop().unwrap(); + node = combine_hash(&last_path_elem, &node); + s /= 2; + } + self.path.push(node); + self.size += 1; + } + + pub fn size(&self) -> u64 { + self.size + } + + pub fn get_path(&self) -> &[MerkleHash] { + &self.path + } +} + +#[cfg(test)] +mod tests { + use rand::rngs::StdRng; + use rand::{Rng, SeedableRng}; + + use super::*; + + fn test_with_len(n: u32, rng: &mut StdRng) { + let mut arr: Vec = vec![]; + for _ in 0..n { + arr.push(rng.gen_range(0..1000)); + } + let (root, paths) = merklize(&arr); + assert_eq!(paths.len() as u32, n); + for (i, item) in arr.iter().enumerate() { + assert!(verify_path(root, &paths[i], item)); + } + } + + #[test] + fn test_merkle_path() { + let mut rng: StdRng = SeedableRng::seed_from_u64(1); + for _ in 0..10 { + let len: u32 = rng.gen_range(1..100); + test_with_len(len, &mut rng); + } + } + + #[test] + fn test_incorrect_path() { + let items = vec![111, 222, 333]; + let (root, paths) = merklize(&items); + for i in 0..items.len() { + assert!(!verify_path(root, &paths[(i + 1) % 3], &items[i])) + } + } + + #[test] + fn test_elements_order() { + let items = vec![1, 2]; + let (root, _) = merklize(&items); + let items2 = vec![2, 1]; + let (root2, _) = merklize(&items2); + assert_ne!(root, root2); + } + + /// Compute the merkle root of a given array. + fn compute_root(hashes: &[CryptoHash]) -> CryptoHash { + if hashes.is_empty() { + CryptoHash::default() + } else if hashes.len() == 1 { + hashes[0] + } else { + let len = hashes.len(); + let subtree_len = len.next_power_of_two() / 2; + let left_root = compute_root(&hashes[0..subtree_len]); + let right_root = compute_root(&hashes[subtree_len..len]); + combine_hash(&left_root, &right_root) + } + } + + #[test] + fn test_merkle_tree() { + let mut tree = PartialMerkleTree::default(); + let mut hashes = vec![]; + for i in 0..50 { + assert_eq!(compute_root(&hashes), tree.root()); + let cur_hash = CryptoHash::hash_bytes(&[i]); + hashes.push(cur_hash); + tree.insert(cur_hash); + } + } + + #[test] + fn test_combine_hash_stability() { + let a = MerkleHash::default(); + let b = MerkleHash::default(); + let cc = combine_hash(&a, &b); + assert_eq!( + cc.0, + [ + 245, 165, 253, 66, 209, 106, 32, 48, 39, 152, 239, 110, 211, 9, 151, 155, 67, 0, + 61, 35, 32, 217, 240, 232, 234, 152, 49, 169, 39, 89, 251, 75 + ] + ); + } +} diff --git a/core/primitives/src/network.rs b/core/primitives/src/network.rs new file mode 100644 index 000000000..5628636ad --- /dev/null +++ b/core/primitives/src/network.rs @@ -0,0 +1,79 @@ +use crate::hash::CryptoHash; +use crate::types::{AccountId, EpochId}; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_crypto::{KeyType, PublicKey, SecretKey, Signature}; +use std::fmt; +use std::hash::Hash; +use std::sync::Arc; + +/// Peer id is the public key. +#[derive( + BorshSerialize, + BorshDeserialize, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + serde::Serialize, + serde::Deserialize, +)] +pub struct PeerId(Arc); + +impl PeerId { + pub fn new(key: PublicKey) -> Self { + Self(Arc::new(key)) + } + + pub fn public_key(&self) -> &PublicKey { + &self.0 + } +} + +impl PeerId { + pub fn random() -> Self { + PeerId::new(SecretKey::from_random(KeyType::ED25519).public_key()) + } +} + +impl fmt::Display for PeerId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl fmt::Debug for PeerId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +/// Account announcement information +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Debug, Hash)] +pub struct AnnounceAccount { + /// AccountId to be announced. + pub account_id: AccountId, + /// PeerId from the owner of the account. + pub peer_id: PeerId, + /// This announcement is only valid for this `epoch`. + pub epoch_id: EpochId, + /// Signature using AccountId associated secret key. + pub signature: Signature, +} + +impl AnnounceAccount { + /// We hash only (account_id, peer_id, epoch_id). There is no need hash the signature + /// as it's uniquely determined the the triple. + pub fn build_header_hash( + account_id: &AccountId, + peer_id: &PeerId, + epoch_id: &EpochId, + ) -> CryptoHash { + CryptoHash::hash_borsh((account_id, peer_id, epoch_id)) + } + + pub fn hash(&self) -> CryptoHash { + AnnounceAccount::build_header_hash(&self.account_id, &self.peer_id, &self.epoch_id) + } +} diff --git a/core/primitives/src/rand.rs b/core/primitives/src/rand.rs new file mode 100644 index 000000000..5ae154d86 --- /dev/null +++ b/core/primitives/src/rand.rs @@ -0,0 +1,185 @@ +use aliases::Aliases; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_primitives_core::types::Power; + +#[derive( + Default, BorshSerialize, BorshDeserialize, serde::Serialize, Clone, Debug, PartialEq, Eq, +)] +pub struct WeightedIndex { + weight_sum: Power, + aliases: Vec, + no_alias_odds: Vec, +} + +impl WeightedIndex { + pub fn new(weights: Vec) -> Self { + let n = Power::from(weights.len() as u64); + let mut aliases = Aliases::new(weights.len()); + + let mut no_alias_odds = weights; + let mut weight_sum: Power = 0; + for w in no_alias_odds.iter_mut() { + weight_sum += *w; + *w *= n; + } + + for (index, &odds) in no_alias_odds.iter().enumerate() { + if odds < weight_sum { + aliases.push_small(index); + } else { + aliases.push_big(index); + } + } + + while !aliases.smalls_is_empty() && !aliases.bigs_is_empty() { + let s = aliases.pop_small(); + let b = aliases.pop_big(); + + aliases.set_alias(s, b); + no_alias_odds[b] = no_alias_odds[b] - weight_sum + no_alias_odds[s]; + + if no_alias_odds[b] < weight_sum { + aliases.push_small(b); + } else { + aliases.push_big(b); + } + } + + while !aliases.smalls_is_empty() { + no_alias_odds[aliases.pop_small()] = weight_sum; + } + + while !aliases.bigs_is_empty() { + no_alias_odds[aliases.pop_big()] = weight_sum; + } + + Self { weight_sum, no_alias_odds, aliases: aliases.get_aliases() } + } + + pub fn sample(&self, seed: [u8; 32]) -> usize { + let usize_seed = Self::copy_8_bytes(&seed[0..8]); + let power_seed = Self::copy_8_bytes(&seed[8..16]); + let uniform_index = usize::from_le_bytes(usize_seed) % self.aliases.len(); + let uniform_weight = Power::from_le_bytes(power_seed) % self.weight_sum; + + if uniform_weight < self.no_alias_odds[uniform_index] { + uniform_index + } else { + self.aliases[uniform_index] as usize + } + } + + pub fn get_aliases(&self) -> &[u64] { + &self.aliases + } + + pub fn get_no_alias_odds(&self) -> &[Power] { + &self.no_alias_odds + } + + fn copy_8_bytes(arr: &[u8]) -> [u8; 8] { + let mut result = [0u8; 8]; + result.clone_from_slice(arr); + result + } + + // fn copy_16_bytes(arr: &[u8]) -> [u8; 16] { + // let mut result = [0u8; 16]; + // result.clone_from_slice(arr); + // result + // } +} + +/// Sub-module to encapsulate helper struct for managing aliases +mod aliases { + pub struct Aliases { + aliases: Vec, + smalls: Vec, + bigs: Vec, + } + + impl Aliases { + pub fn new(n: usize) -> Self { + Self { aliases: vec![0; n], smalls: Vec::with_capacity(n), bigs: Vec::with_capacity(n) } + } + + pub fn push_big(&mut self, b: usize) { + self.bigs.push(b); + } + + pub fn pop_big(&mut self) -> usize { + self.bigs.pop().unwrap() + } + + pub fn bigs_is_empty(&self) -> bool { + self.bigs.is_empty() + } + + pub fn push_small(&mut self, s: usize) { + self.smalls.push(s); + } + + pub fn pop_small(&mut self) -> usize { + self.smalls.pop().unwrap() + } + + pub fn smalls_is_empty(&self) -> bool { + self.smalls.is_empty() + } + + pub fn set_alias(&mut self, index: usize, alias: usize) { + self.aliases[index] = alias; + } + + pub fn get_aliases(self) -> Vec { + self.aliases.into_iter().map(|a| a as u64).collect() + } + } +} + +#[cfg(test)] +mod test { + use crate::hash; + use crate::rand::WeightedIndex; + + #[test] + fn test_should_correctly_compute_odds_and_aliases() { + // Example taken from https://www.keithschwarz.com/darts-dice-coins/ + let weights = vec![5, 8, 4, 10, 4, 4, 5]; + let weighted_index = WeightedIndex::new(weights); + + assert_eq!(weighted_index.get_aliases(), &[1, 0, 3, 1, 3, 3, 3]); + + assert_eq!(weighted_index.get_no_alias_odds(), &[35, 40, 28, 29, 28, 28, 35]); + } + + #[test] + fn test_sample_should_produce_correct_distribution() { + let weights = vec![5, 1, 1]; + let weighted_index = WeightedIndex::new(weights); + + let n_samples = 1_000_000; + let mut seed = hash(&[0; 32]); + let mut counts: [i32; 3] = [0, 0, 0]; + for _ in 0..n_samples { + let index = weighted_index.sample(seed); + counts[index] += 1; + seed = hash(&seed); + } + + assert_relative_closeness(counts[0], 5 * counts[1]); + assert_relative_closeness(counts[1], counts[2]); + } + + /// Assert y is within 0.5% of x. + #[track_caller] + fn assert_relative_closeness(x: i32, y: i32) { + let diff = (y - x).abs(); + let relative_diff = f64::from(diff) / f64::from(x); + assert!(relative_diff < 0.005); + } + + fn hash(input: &[u8]) -> [u8; 32] { + hash::hash(input).0 + } +} diff --git a/core/primitives/src/receipt.rs b/core/primitives/src/receipt.rs new file mode 100644 index 000000000..4174684a2 --- /dev/null +++ b/core/primitives/src/receipt.rs @@ -0,0 +1,208 @@ +use crate::hash::CryptoHash; +use crate::serialize::dec_format; +use crate::transaction::{Action, TransferAction}; +use crate::types::{AccountId, Balance, ShardId}; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_crypto::{KeyType, PublicKey}; +use unc_fmt::AbbrBytes; +use serde_with::base64::Base64; +use serde_with::serde_as; +use std::borrow::Borrow; +use std::collections::HashMap; +use std::fmt; + +pub use unc_vm_runner::logic::DataReceiver; + +/// Receipts are used for a cross-shard communication. +/// Receipts could be 2 types (determined by a `ReceiptEnum`): `ReceiptEnum::Action` of `ReceiptEnum::Data`. +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + PartialEq, + Eq, + Clone, + serde::Serialize, + serde::Deserialize, +)] +pub struct Receipt { + /// An issuer account_id of a particular receipt. + /// `predecessor_id` could be either `Transaction` `signer_id` or intermediate contract's `account_id`. + pub predecessor_id: AccountId, + /// `receiver_id` is a receipt destination. + pub receiver_id: AccountId, + /// An unique id for the receipt + pub receipt_id: CryptoHash, + /// A receipt type + pub receipt: ReceiptEnum, +} + +impl Borrow for Receipt { + fn borrow(&self) -> &CryptoHash { + &self.receipt_id + } +} + +impl Receipt { + /// It's not a content hash, but receipt_id is unique. + pub fn get_hash(&self) -> CryptoHash { + self.receipt_id + } + + /// Generates a receipt with a transfer from system for a given balance without a receipt_id. + /// This should be used for token refunds instead of gas refunds. It doesn't refund the + /// allowance of the access key. For gas refunds use `new_gas_refund`. + pub fn new_balance_refund(receiver_id: &AccountId, refund: Balance) -> Self { + Receipt { + predecessor_id: "system".parse().unwrap(), + receiver_id: receiver_id.clone(), + receipt_id: CryptoHash::default(), + + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: "system".parse().unwrap(), + signer_public_key: PublicKey::empty(KeyType::ED25519), + gas_price: 0, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: vec![Action::Transfer(TransferAction { deposit: refund })], + }), + } + } + + /// Generates a receipt with a transfer action from system for a given balance without a + /// receipt_id. It contains `signer_id` and `signer_public_key` to indicate this is a gas + /// refund. The execution of this receipt will try to refund the allowance of the + /// access key with the given public key. + /// NOTE: The access key may be replaced by the owner, so the execution can't rely that the + /// access key is the same and it should use best effort for the refund. + pub fn new_gas_refund( + receiver_id: &AccountId, + refund: Balance, + signer_public_key: PublicKey, + ) -> Self { + Receipt { + predecessor_id: "system".parse().unwrap(), + receiver_id: receiver_id.clone(), + receipt_id: CryptoHash::default(), + + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: receiver_id.clone(), + signer_public_key, + gas_price: 0, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: vec![Action::Transfer(TransferAction { deposit: refund })], + }), + } + } +} + +/// Receipt could be either ActionReceipt or DataReceipt +#[derive( + BorshSerialize, + BorshDeserialize, + Clone, + Debug, + PartialEq, + Eq, + serde::Serialize, + serde::Deserialize, +)] +pub enum ReceiptEnum { + Action(ActionReceipt), + Data(DataReceipt), +} + +/// ActionReceipt is derived from an Action from `Transaction or from Receipt` +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + PartialEq, + Eq, + Clone, + serde::Serialize, + serde::Deserialize, +)] +pub struct ActionReceipt { + /// A signer of the original transaction + pub signer_id: AccountId, + /// An access key which was used to sign the original transaction + pub signer_public_key: PublicKey, + /// A gas_price which has been used to buy gas in the original transaction + #[serde(with = "dec_format")] + pub gas_price: Balance, + /// If present, where to route the output data + pub output_data_receivers: Vec, + /// A list of the input data dependencies for this Receipt to process. + /// If all `input_data_ids` for this receipt are delivered to the account + /// that means we have all the `ReceivedData` input which will be than converted to a + /// `PromiseResult::Successful(value)` or `PromiseResult::Failed` + /// depending on `ReceivedData` is `Some(_)` or `None` + pub input_data_ids: Vec, + /// A list of actions to process when all input_data_ids are filled + pub actions: Vec, +} + +/// An incoming (ingress) `DataReceipt` which is going to a Receipt's `receiver` input_data_ids +/// Which will be converted to `PromiseResult::Successful(value)` or `PromiseResult::Failed` +#[serde_as] +#[derive( + BorshSerialize, + BorshDeserialize, + Hash, + PartialEq, + Eq, + Clone, + serde::Serialize, + serde::Deserialize, +)] +pub struct DataReceipt { + pub data_id: CryptoHash, + #[serde_as(as = "Option")] + pub data: Option>, +} + +impl fmt::Debug for DataReceipt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("DataReceipt") + .field("data_id", &self.data_id) + .field("data", &format_args!("{}", AbbrBytes(self.data.as_deref()))) + .finish() + } +} + +/// A temporary data which is created by processing of DataReceipt +/// stored in a state trie with a key = `account_id` + `data_id` until +/// `input_data_ids` of all incoming Receipts are satisfied +/// None means data retrieval was failed +#[derive(BorshSerialize, BorshDeserialize, Hash, PartialEq, Eq, Clone)] +pub struct ReceivedData { + pub data: Option>, +} + +impl fmt::Debug for ReceivedData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ReceivedData") + .field("data", &format_args!("{}", AbbrBytes(self.data.as_deref()))) + .finish() + } +} + +/// Stores indices for a persistent queue for delayed receipts that didn't fit into a block. +#[derive(Default, BorshSerialize, BorshDeserialize, Clone, PartialEq, Debug)] +pub struct DelayedReceiptIndices { + // First inclusive index in the queue. + pub first_index: u64, + // Exclusive end index of the queue + pub next_available_index: u64, +} + +impl DelayedReceiptIndices { + pub fn len(&self) -> u64 { + self.next_available_index - self.first_index + } +} + +/// Map of shard to list of receipts to send to it. +pub type ReceiptResult = HashMap>; diff --git a/core/primitives/src/runtime/apply_state.rs b/core/primitives/src/runtime/apply_state.rs new file mode 100644 index 000000000..aad281dcc --- /dev/null +++ b/core/primitives/src/runtime/apply_state.rs @@ -0,0 +1,45 @@ +use crate::runtime::migration_data::{MigrationData, MigrationFlags}; +use crate::{ + hash::CryptoHash, + types::{Balance, BlockHeight, EpochHeight, EpochId, Gas}, + version::ProtocolVersion, +}; +use unc_parameters::RuntimeConfig; +use unc_vm_runner::logic::CompiledContractCache; +use std::sync::Arc; + +#[derive(Debug)] +pub struct ApplyState { + /// Currently building block height. + pub block_height: BlockHeight, + /// Prev block hash + pub prev_block_hash: CryptoHash, + /// Current block hash + pub block_hash: CryptoHash, + /// Current epoch id + pub epoch_id: EpochId, + /// Current epoch height + pub epoch_height: EpochHeight, + /// Price for the gas. + pub gas_price: Balance, + /// The current block timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC). + pub block_timestamp: u64, + /// Gas limit for a given chunk. + /// If None is given, assumes there is no gas limit. + pub gas_limit: Option, + /// Current random seed (from current block vrf output). + pub random_seed: CryptoHash, + /// Current Protocol version when we apply the state transition + pub current_protocol_version: ProtocolVersion, + /// The Runtime config to use for the current transition. + pub config: Arc, + /// Cache for compiled contracts. + pub cache: Option>, + /// Whether the chunk being applied is new. + pub is_new_chunk: bool, + /// Data for migrations that may need to be applied at the start of an epoch when protocol + /// version changes + pub migration_data: Arc, + /// Flags for migrations indicating whether they can be applied at this block + pub migration_flags: MigrationFlags, +} diff --git a/core/primitives/src/runtime/migration_data.rs b/core/primitives/src/runtime/migration_data.rs new file mode 100644 index 000000000..dbafa2377 --- /dev/null +++ b/core/primitives/src/runtime/migration_data.rs @@ -0,0 +1,27 @@ +use crate::receipt::ReceiptResult; +use crate::types::AccountId; +use crate::types::Gas; +use std::fmt; +use std::fmt::{Debug, Formatter}; + +#[derive(Default)] +pub struct MigrationData { + pub storage_usage_delta: Vec<(AccountId, u64)>, + pub storage_usage_fix_gas: Gas, + pub restored_receipts: ReceiptResult, +} + +impl Debug for MigrationData { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("MigrationData").finish() + } +} + +#[derive(Debug, Default)] +pub struct MigrationFlags { + // True iff the current block is the first one in the chain with current protocol version + pub is_first_block_of_version: bool, + // True iff, among all blocks containing chunk for some specific shard, the current block is the + // first one in the first epoch with the current protocol version + pub is_first_block_with_chunk_of_version: bool, +} diff --git a/core/primitives/src/runtime/mod.rs b/core/primitives/src/runtime/mod.rs new file mode 100644 index 000000000..ddc8b677a --- /dev/null +++ b/core/primitives/src/runtime/mod.rs @@ -0,0 +1,2 @@ +pub mod apply_state; +pub mod migration_data; diff --git a/core/primitives/src/sandbox.rs b/core/primitives/src/sandbox.rs new file mode 100644 index 000000000..40eedef72 --- /dev/null +++ b/core/primitives/src/sandbox.rs @@ -0,0 +1,80 @@ +#[cfg(feature = "sandbox")] +pub mod state_patch { + use crate::state_record::StateRecord; + + /// Changes to the state to be applied via sandbox-only state patching + /// feature. + /// + /// As we only expose this functionality for sandbox, we make sure that the + /// object can be non-empty only if `sandbox` feature is enabled. On + /// non-sandbox build, this struct is ZST and its methods are essentially + /// short-circuited by treating the type as always empty. + #[derive(Default)] + pub struct SandboxStatePatch { + records: Vec, + } + + impl SandboxStatePatch { + pub fn new(records: Vec) -> SandboxStatePatch { + SandboxStatePatch { records } + } + + pub fn is_empty(&self) -> bool { + self.records.is_empty() + } + + pub fn clear(&mut self) { + self.records.clear(); + } + + pub fn take(&mut self) -> SandboxStatePatch { + Self { records: core::mem::take(&mut self.records) } + } + + pub fn merge(&mut self, other: SandboxStatePatch) { + self.records.extend(other.records); + } + } + + impl IntoIterator for SandboxStatePatch { + type Item = StateRecord; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.records.into_iter() + } + } +} + +#[cfg(not(feature = "sandbox"))] +pub mod state_patch { + use crate::state_record::StateRecord; + + #[derive(Default)] + pub struct SandboxStatePatch; + + impl SandboxStatePatch { + #[inline(always)] + pub fn is_empty(&self) -> bool { + true + } + #[inline(always)] + pub fn clear(&self) {} + #[inline(always)] + pub fn take(&mut self) -> Self { + Self + } + #[inline(always)] + pub fn merge(&self, _other: SandboxStatePatch) {} + } + + impl IntoIterator for SandboxStatePatch { + type Item = StateRecord; + type IntoIter = std::iter::Empty; + + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + std::iter::empty() + } + } +} diff --git a/core/primitives/src/shard_layout.rs b/core/primitives/src/shard_layout.rs new file mode 100644 index 000000000..27bd73bce --- /dev/null +++ b/core/primitives/src/shard_layout.rs @@ -0,0 +1,618 @@ +use crate::hash::CryptoHash; +use crate::types::{AccountId, NumShards}; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_primitives_core::types::ShardId; +use std::collections::HashMap; +use std::{fmt, str}; + +/// This file implements two data structure `ShardLayout` and `ShardUId` +/// +/// `ShardLayout` +/// A versioned struct that contains all information needed to assign accounts +/// to shards. Because of re-sharding, the chain may use different shard layout to +/// split shards at different times. +/// Currently, `ShardLayout` is stored as part of `EpochConfig`, which is generated each epoch +/// given the epoch protocol version. +/// In mainnet/testnet, we use two shard layouts since re-sharding has only happened once. +/// It is stored as part of genesis config, see default_simple_nightshade_shard_layout() +/// Below is an overview for some important functionalities of ShardLayout interface. +/// +/// `version` +/// `ShardLayout` has a version number. The version number should increment as when sharding changes. +/// This guarantees the version number is unique across different shard layouts, which in turn guarantees +/// `ShardUId` is different across shards from different shard layouts, as `ShardUId` includes +/// `version` and `shard_id` +/// +/// `get_parent_shard_id` and `get_split_shard_ids` +/// `ShardLayout` also includes information needed for resharding. In particular, it encodes +/// which shards from the previous shard layout split to which shards in the following shard layout. +/// If shard A in shard layout 0 splits to shard B and C in shard layout 1, +/// we call shard A the parent shard of shard B and C. +/// Note that a shard can only have one parent shard. For example, the following case will be prohibited, +/// a shard C in shard layout 1 contains accounts in both shard A and B in shard layout 0. +/// Parent/split shard information can be accessed through these two functions. +/// +/// `account_id_to_shard_id` +/// Maps an account to the shard that it belongs to given a shard_layout +/// +/// `ShardUId` +/// `ShardUId` is a unique representation for shards from different shard layouts. +/// Comparing to `ShardId`, which is just an ordinal number ranging from 0 to NUM_SHARDS-1, +/// `ShardUId` provides a way to unique identify shards when shard layouts may change across epochs. +/// This is important because we store states indexed by shards in our database, so we need a +/// way to unique identify shard even when shards change across epochs. +/// Another difference between `ShardUId` and `ShardId` is that `ShardUId` should only exist in +/// a node's internal state while `ShardId` can be exposed to outside APIs and used in protocol +/// level information (for example, `ShardChunkHeader` contains `ShardId` instead of `ShardUId`) + +pub type ShardVersion = u32; + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq)] +pub enum ShardLayout { + V0(ShardLayoutV0), + V1(ShardLayoutV1), +} + +/// A shard layout that maps accounts evenly across all shards -- by calculate the hash of account +/// id and mod number of shards. This is added to capture the old `account_id_to_shard_id` algorithm, +/// to keep backward compatibility for some existing tests. +/// `parent_shards` for `ShardLayoutV1` is always `None`, meaning it can only be the first shard layout +/// a chain uses. +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct ShardLayoutV0 { + /// Map accounts evenly across all shards + num_shards: NumShards, + /// Version of the shard layout, this is useful for uniquely identify the shard layout + version: ShardVersion, +} + +/// A map that maps shards from the last shard layout to shards that it splits to in this shard layout. +/// Instead of using map, we just use a vec here because shard_id ranges from 0 to num_shards-1 +/// For example, if a shard layout with only shard 0 splits into shards 0, 1, 2, 3, the ShardsSplitMap +/// will be `[[0, 1, 2, 3]]` +type ShardSplitMap = Vec>; + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct ShardLayoutV1 { + /// The boundary accounts are the accounts on boundaries between shards. + /// Each shard contains a range of accounts from one boundary account to + /// another - or the the smallest or largest account possible. The total + /// number of shards is equal to the number of boundary accounts plus 1. + boundary_accounts: Vec, + /// Maps shards from the last shard layout to shards that it splits to in this shard layout, + /// Useful for constructing states for the shards. + /// None for the genesis shard layout + shards_split_map: Option, + /// Maps shard in this shard layout to their parent shard + /// Since shard_ids always range from 0 to num_shards - 1, we use vec instead of a hashmap + to_parent_shard_map: Option>, + /// Version of the shard layout, this is useful for uniquely identify the shard layout + version: ShardVersion, +} + +#[derive(Debug)] +pub enum ShardLayoutError { + InvalidShardIdError { shard_id: ShardId }, +} + +impl ShardLayout { + /* Some constructors */ + pub fn v0_single_shard() -> Self { + Self::v0(1, 0) + } + + /// Return a V0 Shardlayout + pub fn v0(num_shards: NumShards, version: ShardVersion) -> Self { + Self::V0(ShardLayoutV0 { num_shards, version }) + } + + /// Return a V1 Shardlayout + pub fn v1( + boundary_accounts: Vec, + shards_split_map: Option, + version: ShardVersion, + ) -> Self { + let to_parent_shard_map = if let Some(shards_split_map) = &shards_split_map { + let mut to_parent_shard_map = HashMap::new(); + let num_shards = (boundary_accounts.len() + 1) as NumShards; + for (parent_shard_id, shard_ids) in shards_split_map.iter().enumerate() { + for &shard_id in shard_ids { + let prev = to_parent_shard_map.insert(shard_id, parent_shard_id as ShardId); + assert!(prev.is_none(), "no shard should appear in the map twice"); + assert!(shard_id < num_shards, "shard id should be valid"); + } + } + Some((0..num_shards).map(|shard_id| to_parent_shard_map[&shard_id]).collect()) + } else { + None + }; + Self::V1(ShardLayoutV1 { + boundary_accounts, + shards_split_map, + to_parent_shard_map, + version, + }) + } + + /// Returns a V1 ShardLayout. It is only used in tests + pub fn v1_test() -> Self { + ShardLayout::v1( + vec!["abc", "foo", "test0"].into_iter().map(|s| s.parse().unwrap()).collect(), + Some(vec![vec![0, 1, 2, 3]]), + 1, + ) + } + + /// Given a parent shard id, return the shard uids for the shards in the current shard layout that + /// are split from this parent shard. If this shard layout has no parent shard layout, return None + pub fn get_children_shards_uids(&self, parent_shard_id: ShardId) -> Option> { + self.get_children_shards_ids(parent_shard_id).map(|shards| { + shards.into_iter().map(|id| ShardUId::from_shard_id_and_layout(id, self)).collect() + }) + } + + /// Given a parent shard id, return the shard ids for the shards in the current shard layout that + /// are split from this parent shard. If this shard layout has no parent shard layout, return None + pub fn get_children_shards_ids(&self, parent_shard_id: ShardId) -> Option> { + match self { + Self::V0(_) => None, + Self::V1(v1) => match &v1.shards_split_map { + Some(shards_split_map) => shards_split_map.get(parent_shard_id as usize).cloned(), + None => None, + }, + } + } + + /// Return the parent shard id for a given shard in the shard layout + /// Only calls this function for shard layout that has parent shard layouts + /// Returns error if `shard_id` is an invalid shard id in the current layout + /// Panics if `self` has no parent shard layout + pub fn get_parent_shard_id(&self, shard_id: ShardId) -> Result { + if !self.shard_ids().any(|id| id == shard_id) { + return Err(ShardLayoutError::InvalidShardIdError { shard_id }); + } + let parent_shard_id = match self { + Self::V0(_) => panic!("shard layout has no parent shard"), + Self::V1(v1) => match &v1.to_parent_shard_map { + // we can safely unwrap here because the construction of to_parent_shard_map guarantees + // that every shard has a parent shard + Some(to_parent_shard_map) => *to_parent_shard_map.get(shard_id as usize).unwrap(), + None => panic!("shard_layout has no parent shard"), + }, + }; + Ok(parent_shard_id) + } + + #[inline] + pub fn version(&self) -> ShardVersion { + match self { + Self::V0(v0) => v0.version, + Self::V1(v1) => v1.version, + } + } + + fn num_shards(&self) -> NumShards { + match self { + Self::V0(v0) => v0.num_shards, + Self::V1(v1) => (v1.boundary_accounts.len() + 1) as NumShards, + } + } + + pub fn shard_ids(&self) -> impl Iterator { + 0..self.num_shards() + } + + /// Returns an iterator that iterates over all the shard uids for all the + /// shards in the shard layout + pub fn shard_uids(&self) -> impl Iterator + '_ { + self.shard_ids().map(|shard_id| ShardUId::from_shard_id_and_layout(shard_id, self)) + } +} + +/// Maps an account to the shard that it belongs to given a shard_layout +/// For V0, maps according to hash of account id +/// For V1, accounts are divided to ranges, each range of account is mapped to a shard. +pub fn account_id_to_shard_id(account_id: &AccountId, shard_layout: &ShardLayout) -> ShardId { + match shard_layout { + ShardLayout::V0(ShardLayoutV0 { num_shards, .. }) => { + let hash = CryptoHash::hash_bytes(account_id.as_bytes()); + let (bytes, _) = stdx::split_array::<32, 8, 24>(hash.as_bytes()); + u64::from_le_bytes(*bytes) % num_shards + } + ShardLayout::V1(ShardLayoutV1 { boundary_accounts, .. }) => { + // Note: As we scale up the number of shards we can consider + // changing this method to do a binary search rather than linear + // scan. For the time being, with only 4 shards, this is perfectly fine. + let mut shard_id: ShardId = 0; + for boundary_account in boundary_accounts { + if account_id < boundary_account { + break; + } + shard_id += 1; + } + shard_id + } + } +} + +/// Maps an account to the shard that it belongs to given a shard_layout +pub fn account_id_to_shard_uid(account_id: &AccountId, shard_layout: &ShardLayout) -> ShardUId { + ShardUId::from_shard_id_and_layout( + account_id_to_shard_id(account_id, shard_layout), + shard_layout, + ) +} + +/// ShardUId is an unique representation for shards from different shard layout +#[derive(BorshSerialize, BorshDeserialize, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct ShardUId { + pub version: ShardVersion, + pub shard_id: u32, +} + +impl ShardUId { + pub fn single_shard() -> Self { + Self { version: 0, shard_id: 0 } + } + + /// Byte representation of the shard uid + pub fn to_bytes(&self) -> [u8; 8] { + let mut res = [0; 8]; + res[0..4].copy_from_slice(&u32::to_le_bytes(self.version)); + res[4..].copy_from_slice(&u32::to_le_bytes(self.shard_id)); + res + } + + pub fn next_shard_prefix(shard_uid_bytes: &[u8; 8]) -> [u8; 8] { + let mut result = *shard_uid_bytes; + for i in (0..8).rev() { + if result[i] == u8::MAX { + result[i] = 0; + } else { + result[i] += 1; + return result; + } + } + panic!("Next shard prefix for shard bytes {shard_uid_bytes:?} does not exist"); + } + + /// Constructs a shard uid from shard id and a shard layout + pub fn from_shard_id_and_layout(shard_id: ShardId, shard_layout: &ShardLayout) -> Self { + assert!(shard_layout.shard_ids().any(|i| i == shard_id)); + Self { shard_id: shard_id as u32, version: shard_layout.version() } + } + + /// Returns shard id + pub fn shard_id(&self) -> ShardId { + ShardId::from(self.shard_id) + } +} + +impl TryFrom<&[u8]> for ShardUId { + type Error = Box; + + /// Deserialize `bytes` to shard uid + fn try_from(bytes: &[u8]) -> Result { + if bytes.len() != 8 { + return Err("incorrect length for ShardUId".into()); + } + let version = u32::from_le_bytes(bytes[0..4].try_into().unwrap()); + let shard_id = u32::from_le_bytes(bytes[4..8].try_into().unwrap()); + Ok(Self { version, shard_id }) + } +} + +/// Returns the byte representation for (block, shard_uid) +pub fn get_block_shard_uid(block_hash: &CryptoHash, shard_uid: &ShardUId) -> Vec { + let mut res = Vec::with_capacity(40); + res.extend_from_slice(block_hash.as_ref()); + res.extend_from_slice(&shard_uid.to_bytes()); + res +} + +/// Deserialize from a byte representation to (block, shard_uid) +#[allow(unused)] +pub fn get_block_shard_uid_rev( + key: &[u8], +) -> Result<(CryptoHash, ShardUId), Box> { + if key.len() != 40 { + return Err( + std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid key length").into() + ); + } + let block_hash = CryptoHash::try_from(&key[..32])?; + let shard_id = ShardUId::try_from(&key[32..])?; + Ok((block_hash, shard_id)) +} + +impl fmt::Display for ShardUId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "s{}.v{}", self.shard_id, self.version) + } +} + +impl fmt::Debug for ShardUId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl str::FromStr for ShardUId { + type Err = String; + + fn from_str(s: &str) -> Result { + let (shard_str, version_str) = s + .split_once(".") + .ok_or_else(|| "shard version and number must be separated by \".\"".to_string())?; + + let version = version_str + .strip_prefix("v") + .ok_or_else(|| "shard version must start with \"v\"".to_string())? + .parse::() + .map_err(|e| format!("shard version after \"v\" must be a number, {e}"))?; + + let shard_str = shard_str + .strip_prefix("s") + .ok_or_else(|| "shard id must start with \"s\"".to_string())?; + let shard_id = shard_str + .parse::() + .map_err(|e| format!("shard id after \"s\" must be a number, {e}"))?; + + Ok(ShardUId { shard_id, version }) + } +} + +impl<'de> serde::Deserialize<'de> for ShardUId { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_any(ShardUIdVisitor) + } +} + +impl serde::Serialize for ShardUId { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +struct ShardUIdVisitor; +impl<'de> serde::de::Visitor<'de> for ShardUIdVisitor { + type Value = ShardUId; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!( + formatter, + "either string format of `ShardUId` like s0v1 for shard 0 version 1, or a map" + ) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + v.parse().map_err(|e| E::custom(e)) + } + + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + // custom struct deserialization for backwards compatibility + // TODO(#7894): consider removing this code after checking + // `ShardUId` is nowhere serialized in the old format + let mut version = None; + let mut shard_id = None; + + while let Some((field, value)) = map.next_entry()? { + match field { + "version" => version = Some(value), + "shard_id" => shard_id = Some(value), + _ => return Err(serde::de::Error::unknown_field(field, &["version", "shard_id"])), + } + } + + match (version, shard_id) { + (None, _) => Err(serde::de::Error::missing_field("version")), + (_, None) => Err(serde::de::Error::missing_field("shard_id")), + (Some(version), Some(shard_id)) => Ok(ShardUId { version, shard_id }), + } + } +} + +#[cfg(test)] +mod tests { + use crate::shard_layout::{account_id_to_shard_id, ShardLayout, ShardLayoutV1, ShardUId}; + use unc_primitives_core::types::{AccountId, ShardId}; + use rand::distributions::Alphanumeric; + use rand::rngs::StdRng; + use rand::{Rng, SeedableRng}; + use std::collections::HashMap; + + use super::{ShardSplitMap, ShardVersion}; + + // The old ShardLayoutV1, before fixed shards were removed. tests only + #[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq)] + pub struct OldShardLayoutV1 { + /// num_shards = fixed_shards.len() + boundary_accounts.len() + 1 + /// Each account and all sub-accounts map to the shard of position in this array. + fixed_shards: Vec, + /// The rest are divided by boundary_accounts to ranges, each range is mapped to a shard + boundary_accounts: Vec, + /// Maps shards from the last shard layout to shards that it splits to in this shard layout, + /// Useful for constructing states for the shards. + /// None for the genesis shard layout + shards_split_map: Option, + /// Maps shard in this shard layout to their parent shard + /// Since shard_ids always range from 0 to num_shards - 1, we use vec instead of a hashmap + to_parent_shard_map: Option>, + /// Version of the shard layout, this is useful for uniquely identify the shard layout + version: ShardVersion, + } + + #[test] + fn test_shard_layout_v0() { + let num_shards = 4; + let shard_layout = ShardLayout::v0(num_shards, 0); + let mut shard_id_distribution: HashMap<_, _> = + shard_layout.shard_ids().map(|shard_id| (shard_id, 0)).collect(); + let mut rng = StdRng::from_seed([0; 32]); + for _i in 0..1000 { + let s: Vec = (&mut rng).sample_iter(&Alphanumeric).take(10).collect(); + let s = String::from_utf8(s).unwrap(); + let account_id = s.to_lowercase().parse().unwrap(); + let shard_id = account_id_to_shard_id(&account_id, &shard_layout); + assert!(shard_id < num_shards); + *shard_id_distribution.get_mut(&shard_id).unwrap() += 1; + } + let expected_distribution: HashMap<_, _> = + [(0, 247), (1, 268), (2, 233), (3, 252)].into_iter().collect(); + assert_eq!(shard_id_distribution, expected_distribution); + } + + #[test] + fn test_shard_layout_v1() { + let shard_layout = ShardLayout::v1( + parse_account_ids(&["aurora", "bar", "foo", "foo.baz", "paz"]), + Some(vec![vec![0, 1, 2], vec![3, 4, 5]]), + 1, + ); + assert_eq!( + shard_layout.get_children_shards_uids(0).unwrap(), + (0..3).map(|x| ShardUId { version: 1, shard_id: x }).collect::>() + ); + assert_eq!( + shard_layout.get_children_shards_uids(1).unwrap(), + (3..6).map(|x| ShardUId { version: 1, shard_id: x }).collect::>() + ); + for x in 0..3 { + assert_eq!(shard_layout.get_parent_shard_id(x).unwrap(), 0); + assert_eq!(shard_layout.get_parent_shard_id(x + 3).unwrap(), 1); + } + + assert_eq!(account_id_to_shard_id(&"aurora".parse().unwrap(), &shard_layout), 1); + assert_eq!(account_id_to_shard_id(&"foo.aurora".parse().unwrap(), &shard_layout), 3); + assert_eq!(account_id_to_shard_id(&"bar.foo.aurora".parse().unwrap(), &shard_layout), 2); + assert_eq!(account_id_to_shard_id(&"bar".parse().unwrap(), &shard_layout), 2); + assert_eq!(account_id_to_shard_id(&"bar.bar".parse().unwrap(), &shard_layout), 2); + assert_eq!(account_id_to_shard_id(&"foo".parse().unwrap(), &shard_layout), 3); + assert_eq!(account_id_to_shard_id(&"baz.foo".parse().unwrap(), &shard_layout), 2); + assert_eq!(account_id_to_shard_id(&"foo.baz".parse().unwrap(), &shard_layout), 4); + assert_eq!(account_id_to_shard_id(&"a.foo.baz".parse().unwrap(), &shard_layout), 0); + + assert_eq!(account_id_to_shard_id(&"aaa".parse().unwrap(), &shard_layout), 0); + assert_eq!(account_id_to_shard_id(&"abc".parse().unwrap(), &shard_layout), 0); + assert_eq!(account_id_to_shard_id(&"bbb".parse().unwrap(), &shard_layout), 2); + assert_eq!(account_id_to_shard_id(&"foo.goo".parse().unwrap(), &shard_layout), 4); + assert_eq!(account_id_to_shard_id(&"goo".parse().unwrap(), &shard_layout), 4); + assert_eq!(account_id_to_shard_id(&"zoo".parse().unwrap(), &shard_layout), 5); + } + + // check that after removing the fixed shards from the shard layout v1 + // the fixed shards are skipped in deserialization + // this should be the default as long as serde(deny_unknown_fields) is not set + #[test] + fn test_remove_fixed_shards() { + let old = OldShardLayoutV1 { + fixed_shards: vec![], + boundary_accounts: parse_account_ids(&["aaa", "bbb"]), + shards_split_map: Some(vec![vec![0, 1, 2]]), + to_parent_shard_map: Some(vec![0, 0, 0]), + version: 1, + }; + let json = serde_json::to_string_pretty(&old).unwrap(); + println!("json"); + println!("{json:#?}"); + + let new = serde_json::from_str::(json.as_str()).unwrap(); + assert_eq!(old.boundary_accounts, new.boundary_accounts); + assert_eq!(old.shards_split_map, new.shards_split_map); + assert_eq!(old.to_parent_shard_map, new.to_parent_shard_map); + assert_eq!(old.version, new.version); + } + + fn parse_account_ids(ids: &[&str]) -> Vec { + ids.into_iter().map(|a| a.parse().unwrap()).collect() + } + + #[test] + fn test_shard_layout_all() { + let v0 = ShardLayout::v0(1, 0); + let v1 = ShardLayout::get_simple_nightshade_layout(); + let v2 = ShardLayout::get_simple_nightshade_layout_v2(); + + insta::assert_snapshot!(serde_json::to_string_pretty(&v0).unwrap(), @r###" + { + "V0": { + "num_shards": 1, + "version": 0 + } + } + "###); + insta::assert_snapshot!(serde_json::to_string_pretty(&v1).unwrap(), @r###" + { + "V1": { + "boundary_accounts": [ + "aurora", + "aurora-0", + "kkuuue2akv_1630967379.near" + ], + "shards_split_map": [ + [ + 0, + 1, + 2, + 3 + ] + ], + "to_parent_shard_map": [ + 0, + 0, + 0, + 0 + ], + "version": 1 + } + } + "###); + insta::assert_snapshot!(serde_json::to_string_pretty(&v2).unwrap(), @r###" + { + "V1": { + "boundary_accounts": [ + "aurora", + "aurora-0", + "kkuuue2akv_1630967379.near", + "tge-lockup.sweat" + ], + "shards_split_map": [ + [ + 0 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3, + 4 + ] + ], + "to_parent_shard_map": [ + 0, + 1, + 2, + 3, + 3 + ], + "version": 2 + } + } + "###); + } +} diff --git a/core/primitives/src/sharding.rs b/core/primitives/src/sharding.rs new file mode 100644 index 000000000..6ecf78488 --- /dev/null +++ b/core/primitives/src/sharding.rs @@ -0,0 +1,1282 @@ +use crate::hash::{hash, CryptoHash}; +use crate::merkle::{combine_hash, merklize, verify_path, MerklePath}; +use crate::receipt::Receipt; +use crate::transaction::SignedTransaction; +use crate::types::validator_power::{ValidatorPower, ValidatorPowerIter, ValidatorPowerV1}; +use crate::types::{Balance, BlockHeight, Gas, MerkleHash, ShardId, StateRoot, ValidatorFrozenV1}; +use crate::validator_signer::ValidatorSigner; +use crate::version::{ProtocolFeature, ProtocolVersion, SHARD_CHUNK_HEADER_UPGRADE_VERSION}; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_crypto::Signature; +use unc_fmt::AbbrBytes; +use reed_solomon_erasure::galois_8::{Field, ReedSolomon}; +use reed_solomon_erasure::ReconstructShard; +use std::cmp::Ordering; +use std::sync::Arc; +use tracing::debug_span; + +#[derive( + BorshSerialize, + BorshDeserialize, + Hash, + Eq, + PartialEq, + Ord, + PartialOrd, + Clone, + Debug, + Default, + serde::Serialize, + serde::Deserialize, +)] +pub struct ChunkHash(pub CryptoHash); + +impl ChunkHash { + pub fn as_bytes(&self) -> &[u8; 32] { + self.0.as_bytes() + } +} + +impl AsRef<[u8]> for ChunkHash { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl From for Vec { + fn from(chunk_hash: ChunkHash) -> Self { + chunk_hash.0.into() + } +} + +impl From for ChunkHash { + fn from(crypto_hash: CryptoHash) -> Self { + Self(crypto_hash) + } +} + +#[derive(Debug, PartialEq, BorshSerialize, BorshDeserialize)] +pub struct ShardInfo(pub ShardId, pub ChunkHash); + +/// Contains the information that is used to sync state for shards as epochs switch +#[derive(Debug, PartialEq, BorshSerialize, BorshDeserialize)] +pub struct StateSyncInfo { + /// The first block of the epoch for which syncing is happening + pub epoch_tail_hash: CryptoHash, + /// Shards to fetch state + pub shards: Vec, +} + +pub mod shard_chunk_header_inner; +pub use shard_chunk_header_inner::{ + ShardChunkHeaderInner, ShardChunkHeaderInnerV1, ShardChunkHeaderInnerV2, +}; +use crate::types::validator_frozen::{ValidatorFrozen, ValidatorFrozenIter}; + +#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug)] +#[borsh(init=init)] +pub struct ShardChunkHeaderV1 { + pub inner: ShardChunkHeaderInnerV1, + + pub height_included: BlockHeight, + + /// Signature of the chunk producer. + pub signature: Signature, + + #[borsh(skip)] + pub hash: ChunkHash, +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug)] +#[borsh(init=init)] +pub struct ShardChunkHeaderV2 { + pub inner: ShardChunkHeaderInnerV1, + + pub height_included: BlockHeight, + + /// Signature of the chunk producer. + pub signature: Signature, + + #[borsh(skip)] + pub hash: ChunkHash, +} + +impl ShardChunkHeaderV2 { + pub fn init(&mut self) { + self.hash = Self::compute_hash(&self.inner); + } + + pub fn compute_hash(inner: &ShardChunkHeaderInnerV1) -> ChunkHash { + let inner_bytes = borsh::to_vec(&inner).expect("Failed to serialize"); + let inner_hash = hash(&inner_bytes); + + ChunkHash(combine_hash(&inner_hash, &inner.encoded_merkle_root)) + } + + pub fn new( + prev_block_hash: CryptoHash, + prev_state_root: StateRoot, + prev_outcome_root: CryptoHash, + encoded_merkle_root: CryptoHash, + encoded_length: u64, + height: BlockHeight, + shard_id: ShardId, + prev_gas_used: Gas, + gas_limit: Gas, + prev_balance_burnt: Balance, + prev_outgoing_receipts_root: CryptoHash, + tx_root: CryptoHash, + prev_validator_power_proposals: Vec, + prev_validator_frozen_proposals: Vec, + signer: &dyn ValidatorSigner, + ) -> Self { + let inner = ShardChunkHeaderInnerV1 { + prev_block_hash, + prev_state_root, + prev_outcome_root, + encoded_merkle_root, + encoded_length, + height_created: height, + shard_id, + prev_gas_used, + gas_limit, + prev_balance_burnt, + prev_outgoing_receipts_root, + tx_root, + prev_validator_power_proposals, + prev_validator_frozen_proposals, + }; + let hash = Self::compute_hash(&inner); + let signature = signer.sign_chunk_hash(&hash); + Self { inner, height_included: 0, signature, hash } + } +} + +// V2 -> V3: Use versioned ShardChunkHeaderInner structure +#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug)] +#[borsh(init=init)] +pub struct ShardChunkHeaderV3 { + pub inner: ShardChunkHeaderInner, + + pub height_included: BlockHeight, + + /// Signature of the chunk producer. + pub signature: Signature, + + #[borsh(skip)] + pub hash: ChunkHash, +} + +impl ShardChunkHeaderV3 { + pub fn init(&mut self) { + self.hash = Self::compute_hash(&self.inner); + } + + pub fn compute_hash(inner: &ShardChunkHeaderInner) -> ChunkHash { + let inner_bytes = borsh::to_vec(&inner).expect("Failed to serialize"); + let inner_hash = hash(&inner_bytes); + + ChunkHash(combine_hash(&inner_hash, inner.encoded_merkle_root())) + } + + pub fn new( + prev_block_hash: CryptoHash, + prev_state_root: StateRoot, + prev_outcome_root: CryptoHash, + encoded_merkle_root: CryptoHash, + encoded_length: u64, + height: BlockHeight, + shard_id: ShardId, + prev_gas_used: Gas, + gas_limit: Gas, + prev_balance_burnt: Balance, + prev_outgoing_receipts_root: CryptoHash, + tx_root: CryptoHash, + prev_validator_power_proposals: Vec, + prev_validator_frozen_proposals: Vec, + signer: &dyn ValidatorSigner, + ) -> Self { + let inner = ShardChunkHeaderInner::V2(ShardChunkHeaderInnerV2 { + prev_block_hash, + prev_state_root, + prev_outcome_root, + encoded_merkle_root, + encoded_length, + height_created: height, + shard_id, + prev_gas_used, + gas_limit, + prev_balance_burnt, + prev_outgoing_receipts_root, + tx_root, + prev_validator_power_proposals, + prev_validator_frozen_proposals, + }); + Self::from_inner(inner, signer) + } + + pub fn from_inner(inner: ShardChunkHeaderInner, signer: &dyn ValidatorSigner) -> Self { + let hash = Self::compute_hash(&inner); + let signature = signer.sign_chunk_hash(&hash); + Self { inner, height_included: 0, signature, hash } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug)] +pub enum ShardChunkHeader { + V1(ShardChunkHeaderV1), + V2(ShardChunkHeaderV2), + V3(ShardChunkHeaderV3), +} + +impl ShardChunkHeader { + #[inline] + pub fn take_inner(self) -> ShardChunkHeaderInner { + match self { + Self::V1(header) => ShardChunkHeaderInner::V1(header.inner), + Self::V2(header) => ShardChunkHeaderInner::V1(header.inner), + Self::V3(header) => header.inner, + } + } + + pub fn inner_header_hash(&self) -> CryptoHash { + let inner_bytes = match self { + Self::V1(header) => borsh::to_vec(&header.inner), + Self::V2(header) => borsh::to_vec(&header.inner), + Self::V3(header) => borsh::to_vec(&header.inner), + }; + hash(&inner_bytes.expect("Failed to serialize")) + } + + #[inline] + pub fn height_created(&self) -> BlockHeight { + match self { + Self::V1(header) => header.inner.height_created, + Self::V2(header) => header.inner.height_created, + Self::V3(header) => header.inner.height_created(), + } + } + + #[inline] + pub fn signature(&self) -> &Signature { + match self { + Self::V1(header) => &header.signature, + Self::V2(header) => &header.signature, + Self::V3(header) => &header.signature, + } + } + + #[inline] + pub fn height_included(&self) -> BlockHeight { + match self { + Self::V1(header) => header.height_included, + Self::V2(header) => header.height_included, + Self::V3(header) => header.height_included, + } + } + + #[inline] + pub fn height_included_mut(&mut self) -> &mut BlockHeight { + match self { + Self::V1(header) => &mut header.height_included, + Self::V2(header) => &mut header.height_included, + Self::V3(header) => &mut header.height_included, + } + } + + pub fn is_new_chunk(&self) -> bool { + self.height_created() == self.height_included() + } + + #[inline] + pub fn prev_validator_power_proposals(&self) -> ValidatorPowerIter { + match self { + Self::V1(header) => ValidatorPowerIter::v1(&header.inner.prev_validator_power_proposals), + Self::V2(header) => ValidatorPowerIter::v1(&header.inner.prev_validator_power_proposals), + Self::V3(header) => header.inner.prev_validator_power_proposals(), + } + } + + #[inline] + pub fn prev_validator_frozen_proposals(&self) -> ValidatorFrozenIter { + match self { + Self::V1(header) => ValidatorFrozenIter::v1(&header.inner.prev_validator_frozen_proposals), + Self::V2(header) => ValidatorFrozenIter::v1(&header.inner.prev_validator_frozen_proposals), + Self::V3(header) => header.inner.prev_validator_frozen_proposals(), + } + } + + #[inline] + pub fn prev_state_root(&self) -> StateRoot { + match self { + Self::V1(header) => header.inner.prev_state_root, + Self::V2(header) => header.inner.prev_state_root, + Self::V3(header) => *header.inner.prev_state_root(), + } + } + + #[inline] + pub fn prev_block_hash(&self) -> &CryptoHash { + match self { + Self::V1(header) => &header.inner.prev_block_hash, + Self::V2(header) => &header.inner.prev_block_hash, + Self::V3(header) => header.inner.prev_block_hash(), + } + } + + #[inline] + pub fn encoded_merkle_root(&self) -> CryptoHash { + match self { + Self::V1(header) => header.inner.encoded_merkle_root, + Self::V2(header) => header.inner.encoded_merkle_root, + Self::V3(header) => *header.inner.encoded_merkle_root(), + } + } + + #[inline] + pub fn shard_id(&self) -> ShardId { + match self { + Self::V1(header) => header.inner.shard_id, + Self::V2(header) => header.inner.shard_id, + Self::V3(header) => header.inner.shard_id(), + } + } + + #[inline] + pub fn encoded_length(&self) -> u64 { + match self { + Self::V1(header) => header.inner.encoded_length, + Self::V2(header) => header.inner.encoded_length, + Self::V3(header) => header.inner.encoded_length(), + } + } + + #[inline] + pub fn prev_gas_used(&self) -> Gas { + match &self { + ShardChunkHeader::V1(header) => header.inner.prev_gas_used, + ShardChunkHeader::V2(header) => header.inner.prev_gas_used, + ShardChunkHeader::V3(header) => header.inner.prev_gas_used(), + } + } + + #[inline] + pub fn gas_limit(&self) -> Gas { + match &self { + ShardChunkHeader::V1(header) => header.inner.gas_limit, + ShardChunkHeader::V2(header) => header.inner.gas_limit, + ShardChunkHeader::V3(header) => header.inner.gas_limit(), + } + } + + #[inline] + pub fn prev_balance_burnt(&self) -> Balance { + match &self { + ShardChunkHeader::V1(header) => header.inner.prev_balance_burnt, + ShardChunkHeader::V2(header) => header.inner.prev_balance_burnt, + ShardChunkHeader::V3(header) => header.inner.prev_balance_burnt(), + } + } + + #[inline] + pub fn prev_outgoing_receipts_root(&self) -> CryptoHash { + match &self { + ShardChunkHeader::V1(header) => header.inner.prev_outgoing_receipts_root, + ShardChunkHeader::V2(header) => header.inner.prev_outgoing_receipts_root, + ShardChunkHeader::V3(header) => *header.inner.prev_outgoing_receipts_root(), + } + } + + #[inline] + pub fn prev_outcome_root(&self) -> CryptoHash { + match &self { + ShardChunkHeader::V1(header) => header.inner.prev_outcome_root, + ShardChunkHeader::V2(header) => header.inner.prev_outcome_root, + ShardChunkHeader::V3(header) => *header.inner.prev_outcome_root(), + } + } + + #[inline] + pub fn tx_root(&self) -> CryptoHash { + match &self { + ShardChunkHeader::V1(header) => header.inner.tx_root, + ShardChunkHeader::V2(header) => header.inner.tx_root, + ShardChunkHeader::V3(header) => *header.inner.tx_root(), + } + } + + #[inline] + pub fn chunk_hash(&self) -> ChunkHash { + match &self { + ShardChunkHeader::V1(header) => header.hash.clone(), + ShardChunkHeader::V2(header) => header.hash.clone(), + ShardChunkHeader::V3(header) => header.hash.clone(), + } + } + + /// Returns whether the header is valid for given `ProtocolVersion`. + pub fn valid_for(&self, version: ProtocolVersion) -> bool { + const BLOCK_HEADER_V3_VERSION: ProtocolVersion = + ProtocolFeature::BlockHeaderV3.protocol_version(); + match &self { + ShardChunkHeader::V1(_) => version < SHARD_CHUNK_HEADER_UPGRADE_VERSION, + ShardChunkHeader::V2(_) => { + SHARD_CHUNK_HEADER_UPGRADE_VERSION <= version && version < BLOCK_HEADER_V3_VERSION + } + ShardChunkHeader::V3(_) => BLOCK_HEADER_V3_VERSION <= version, + } + } + + pub fn compute_hash(&self) -> ChunkHash { + match self { + ShardChunkHeader::V1(header) => ShardChunkHeaderV1::compute_hash(&header.inner), + ShardChunkHeader::V2(header) => ShardChunkHeaderV2::compute_hash(&header.inner), + ShardChunkHeader::V3(header) => ShardChunkHeaderV3::compute_hash(&header.inner), + } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Hash, Eq, PartialEq, Clone, Debug, Default)] +pub struct ChunkHashHeight(pub ChunkHash, pub BlockHeight); + +impl ShardChunkHeaderV1 { + pub fn init(&mut self) { + self.hash = Self::compute_hash(&self.inner); + } + + pub fn chunk_hash(&self) -> ChunkHash { + self.hash.clone() + } + + pub fn compute_hash(inner: &ShardChunkHeaderInnerV1) -> ChunkHash { + let inner_bytes = borsh::to_vec(&inner).expect("Failed to serialize"); + let inner_hash = hash(&inner_bytes); + + ChunkHash(inner_hash) + } + + pub fn new( + prev_block_hash: CryptoHash, + prev_state_root: StateRoot, + prev_outcome_root: CryptoHash, + encoded_merkle_root: CryptoHash, + encoded_length: u64, + height: BlockHeight, + shard_id: ShardId, + prev_gas_used: Gas, + gas_limit: Gas, + prev_balance_burnt: Balance, + prev_outgoing_receipts_root: CryptoHash, + tx_root: CryptoHash, + prev_validator_power_proposals: Vec, + prev_validator_frozen_proposals: Vec, + signer: &dyn ValidatorSigner, + ) -> Self { + let inner = ShardChunkHeaderInnerV1 { + prev_block_hash, + prev_state_root, + prev_outcome_root, + encoded_merkle_root, + encoded_length, + height_created: height, + shard_id, + prev_gas_used, + gas_limit, + prev_balance_burnt, + prev_outgoing_receipts_root, + tx_root, + prev_validator_power_proposals, + prev_validator_frozen_proposals, + }; + let hash = Self::compute_hash(&inner); + let signature = signer.sign_chunk_hash(&hash); + Self { inner, height_included: 0, signature, hash } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)] +pub enum PartialEncodedChunk { + V1(PartialEncodedChunkV1), + V2(PartialEncodedChunkV2), +} + +impl PartialEncodedChunk { + pub fn new( + header: ShardChunkHeader, + parts: Vec, + receipts: Vec, + ) -> Self { + match header { + ShardChunkHeader::V1(header) => { + Self::V1(PartialEncodedChunkV1 { header, parts, receipts }) + } + header => Self::V2(PartialEncodedChunkV2 { header, parts, receipts }), + } + } + + pub fn cloned_header(&self) -> ShardChunkHeader { + match self { + Self::V1(chunk) => ShardChunkHeader::V1(chunk.header.clone()), + Self::V2(chunk) => chunk.header.clone(), + } + } + + pub fn chunk_hash(&self) -> ChunkHash { + match self { + Self::V1(chunk) => chunk.header.hash.clone(), + Self::V2(chunk) => chunk.header.chunk_hash(), + } + } + + pub fn height_included(&self) -> BlockHeight { + match self { + Self::V1(chunk) => chunk.header.height_included, + Self::V2(chunk) => chunk.header.height_included(), + } + } + + #[inline] + pub fn parts(&self) -> &[PartialEncodedChunkPart] { + match self { + Self::V1(chunk) => &chunk.parts, + Self::V2(chunk) => &chunk.parts, + } + } + + #[inline] + pub fn receipts(&self) -> &[ReceiptProof] { + match self { + Self::V1(chunk) => &chunk.receipts, + Self::V2(chunk) => &chunk.receipts, + } + } + + #[inline] + pub fn prev_block(&self) -> &CryptoHash { + match &self { + PartialEncodedChunk::V1(chunk) => &chunk.header.inner.prev_block_hash, + PartialEncodedChunk::V2(chunk) => chunk.header.prev_block_hash(), + } + } + + /// Returns whether the chenk is valid for given `ProtocolVersion`. + pub fn valid_for(&self, version: ProtocolVersion) -> bool { + match &self { + PartialEncodedChunk::V1(_) => version < SHARD_CHUNK_HEADER_UPGRADE_VERSION, + PartialEncodedChunk::V2(_) => SHARD_CHUNK_HEADER_UPGRADE_VERSION <= version, + } + } + + pub fn height_created(&self) -> BlockHeight { + match self { + Self::V1(chunk) => chunk.header.inner.height_created, + Self::V2(chunk) => chunk.header.height_created(), + } + } + pub fn shard_id(&self) -> ShardId { + match self { + Self::V1(chunk) => chunk.header.inner.shard_id, + Self::V2(chunk) => chunk.header.shard_id(), + } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)] +pub struct PartialEncodedChunkV2 { + pub header: ShardChunkHeader, + pub parts: Vec, + pub receipts: Vec, +} + +impl From for PartialEncodedChunkV2 { + fn from(pec: PartialEncodedChunk) -> Self { + match pec { + PartialEncodedChunk::V1(chunk) => PartialEncodedChunkV2 { + header: ShardChunkHeader::V1(chunk.header), + parts: chunk.parts, + receipts: chunk.receipts, + }, + PartialEncodedChunk::V2(chunk) => chunk, + } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)] +pub struct PartialEncodedChunkV1 { + pub header: ShardChunkHeaderV1, + pub parts: Vec, + pub receipts: Vec, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct PartialEncodedChunkWithArcReceipts { + pub header: ShardChunkHeader, + pub parts: Vec, + pub receipts: Vec>, +} + +impl From for PartialEncodedChunk { + fn from(pec: PartialEncodedChunkWithArcReceipts) -> Self { + Self::V2(PartialEncodedChunkV2 { + header: pec.header, + parts: pec.parts, + receipts: pec.receipts.into_iter().map(|r| ReceiptProof::clone(&r)).collect(), + }) + } +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq, serde::Deserialize)] +pub struct ShardProof { + pub from_shard_id: ShardId, + pub to_shard_id: ShardId, + pub proof: MerklePath, +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq, serde::Deserialize)] +/// For each Merkle proof there is a subset of receipts which may be proven. +pub struct ReceiptProof(pub Vec, pub ShardProof); + +// Implement ordering to ensure `ReceiptProofs` are ordered consistently, +// because we expect messages with ReceiptProofs to be deterministic. +impl PartialOrd for ReceiptProof { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ReceiptProof { + fn cmp(&self, other: &Self) -> Ordering { + (self.1.from_shard_id, self.1.to_shard_id) + .cmp(&(other.1.from_shard_id, other.1.to_shard_id)) + } +} + +impl ReceiptProof { + pub fn verify_against_receipt_root(&self, receipt_root: CryptoHash) -> bool { + let ReceiptProof(shard_receipts, receipt_proof) = self; + let receipt_hash = + CryptoHash::hash_borsh(ReceiptList(receipt_proof.to_shard_id, shard_receipts)); + verify_path(receipt_root, &receipt_proof.proof, &receipt_hash) + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Eq, PartialEq)] +pub struct PartialEncodedChunkPart { + pub part_ord: u64, + pub part: Box<[u8]>, + pub merkle_proof: MerklePath, +} + +impl std::fmt::Debug for PartialEncodedChunkPart { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PartialEncodedChunkPart") + .field("part_ord", &self.part_ord) + .field("part", &format_args!("{}", AbbrBytes(self.part.as_ref()))) + .field("merkle_proof", &self.merkle_proof) + .finish() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)] +pub struct ShardChunkV1 { + pub chunk_hash: ChunkHash, + pub header: ShardChunkHeaderV1, + pub transactions: Vec, + pub prev_outgoing_receipts: Vec, +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)] +pub struct ShardChunkV2 { + pub chunk_hash: ChunkHash, + pub header: ShardChunkHeader, + pub transactions: Vec, + pub prev_outgoing_receipts: Vec, +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)] +pub enum ShardChunk { + V1(ShardChunkV1), + V2(ShardChunkV2), +} + +impl ShardChunk { + pub fn with_header(chunk: ShardChunk, header: ShardChunkHeader) -> Option { + match chunk { + Self::V1(chunk) => match header { + ShardChunkHeader::V1(header) => Some(ShardChunk::V1(ShardChunkV1 { + chunk_hash: header.chunk_hash(), + header, + transactions: chunk.transactions, + prev_outgoing_receipts: chunk.prev_outgoing_receipts, + })), + ShardChunkHeader::V2(_) => None, + ShardChunkHeader::V3(_) => None, + }, + Self::V2(chunk) => Some(ShardChunk::V2(ShardChunkV2 { + chunk_hash: header.chunk_hash(), + header, + transactions: chunk.transactions, + prev_outgoing_receipts: chunk.prev_outgoing_receipts, + })), + } + } + + pub fn set_height_included(&mut self, height: BlockHeight) { + match self { + Self::V1(chunk) => chunk.header.height_included = height, + Self::V2(chunk) => *chunk.header.height_included_mut() = height, + } + } + + #[inline] + pub fn height_included(&self) -> BlockHeight { + match self { + Self::V1(chunk) => chunk.header.height_included, + Self::V2(chunk) => chunk.header.height_included(), + } + } + + #[inline] + pub fn height_created(&self) -> BlockHeight { + match self { + Self::V1(chunk) => chunk.header.inner.height_created, + Self::V2(chunk) => chunk.header.height_created(), + } + } + + #[inline] + pub fn prev_block(&self) -> &CryptoHash { + match &self { + ShardChunk::V1(chunk) => &chunk.header.inner.prev_block_hash, + ShardChunk::V2(chunk) => chunk.header.prev_block_hash(), + } + } + + #[inline] + pub fn prev_state_root(&self) -> StateRoot { + match self { + Self::V1(chunk) => chunk.header.inner.prev_state_root, + Self::V2(chunk) => chunk.header.prev_state_root(), + } + } + + #[inline] + pub fn tx_root(&self) -> CryptoHash { + match self { + Self::V1(chunk) => chunk.header.inner.tx_root, + Self::V2(chunk) => chunk.header.tx_root(), + } + } + + #[inline] + pub fn prev_outgoing_receipts_root(&self) -> CryptoHash { + match self { + Self::V1(chunk) => chunk.header.inner.prev_outgoing_receipts_root, + Self::V2(chunk) => chunk.header.prev_outgoing_receipts_root(), + } + } + + #[inline] + pub fn shard_id(&self) -> ShardId { + match self { + Self::V1(chunk) => chunk.header.inner.shard_id, + Self::V2(chunk) => chunk.header.shard_id(), + } + } + + #[inline] + pub fn chunk_hash(&self) -> ChunkHash { + match self { + Self::V1(chunk) => chunk.chunk_hash.clone(), + Self::V2(chunk) => chunk.chunk_hash.clone(), + } + } + + #[inline] + pub fn prev_outgoing_receipts(&self) -> &[Receipt] { + match self { + Self::V1(chunk) => &chunk.prev_outgoing_receipts, + Self::V2(chunk) => &chunk.prev_outgoing_receipts, + } + } + + #[inline] + pub fn transactions(&self) -> &[SignedTransaction] { + match self { + Self::V1(chunk) => &chunk.transactions, + Self::V2(chunk) => &chunk.transactions, + } + } + + #[inline] + pub fn header_hash(&self) -> ChunkHash { + match self { + Self::V1(chunk) => chunk.header.chunk_hash(), + Self::V2(chunk) => chunk.header.chunk_hash(), + } + } + + #[inline] + pub fn prev_block_hash(&self) -> CryptoHash { + match self { + Self::V1(chunk) => chunk.header.inner.prev_block_hash, + Self::V2(chunk) => *chunk.header.prev_block_hash(), + } + } + + #[inline] + pub fn take_header(self) -> ShardChunkHeader { + match self { + Self::V1(chunk) => ShardChunkHeader::V1(chunk.header), + Self::V2(chunk) => chunk.header, + } + } + + pub fn cloned_header(&self) -> ShardChunkHeader { + match self { + Self::V1(chunk) => ShardChunkHeader::V1(chunk.header.clone()), + Self::V2(chunk) => chunk.header.clone(), + } + } + + pub fn compute_header_hash(&self) -> ChunkHash { + match self { + Self::V1(chunk) => ShardChunkHeaderV1::compute_hash(&chunk.header.inner), + Self::V2(chunk) => chunk.header.compute_hash(), + } + } +} + +#[derive(Default, BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq)] +pub struct EncodedShardChunkBody { + pub parts: Vec>>, +} + +impl EncodedShardChunkBody { + pub fn num_fetched_parts(&self) -> usize { + let mut fetched_parts: usize = 0; + + for part in self.parts.iter() { + if part.is_some() { + fetched_parts += 1; + } + } + + fetched_parts + } + + /// Returns true if reconstruction was successful + pub fn reconstruct( + &mut self, + rs: &mut ReedSolomonWrapper, + ) -> Result<(), reed_solomon_erasure::Error> { + rs.reconstruct(self.parts.as_mut_slice()) + } + + pub fn get_merkle_hash_and_paths(&self) -> (MerkleHash, Vec) { + let parts: Vec<&[u8]> = + self.parts.iter().map(|x| x.as_deref().unwrap()).collect::>(); + merklize(&parts) + } +} + +#[derive(BorshSerialize, Debug, Clone)] +pub struct ReceiptList<'a>(pub ShardId, pub &'a [Receipt]); + +#[derive(BorshSerialize, BorshDeserialize)] +struct TransactionReceipt(Vec, Vec); + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq)] +pub struct EncodedShardChunkV1 { + pub header: ShardChunkHeaderV1, + pub content: EncodedShardChunkBody, +} + +impl EncodedShardChunkV1 { + pub fn chunk_hash(&self) -> ChunkHash { + self.header.chunk_hash() + } + + pub fn decode_chunk(&self, data_parts: usize) -> Result { + let transaction_receipts = EncodedShardChunk::decode_transaction_receipts( + &self.content.parts[0..data_parts], + self.header.inner.encoded_length, + )?; + + Ok(ShardChunkV1 { + chunk_hash: self.header.chunk_hash(), + header: self.header.clone(), + transactions: transaction_receipts.0, + prev_outgoing_receipts: transaction_receipts.1, + }) + } +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq)] +pub struct EncodedShardChunkV2 { + pub header: ShardChunkHeader, + pub content: EncodedShardChunkBody, +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq)] +pub enum EncodedShardChunk { + V1(EncodedShardChunkV1), + V2(EncodedShardChunkV2), +} + +impl EncodedShardChunk { + pub fn cloned_header(&self) -> ShardChunkHeader { + match self { + Self::V1(chunk) => ShardChunkHeader::V1(chunk.header.clone()), + Self::V2(chunk) => chunk.header.clone(), + } + } + + #[inline] + pub fn content(&self) -> &EncodedShardChunkBody { + match self { + Self::V1(chunk) => &chunk.content, + Self::V2(chunk) => &chunk.content, + } + } + + #[inline] + pub fn content_mut(&mut self) -> &mut EncodedShardChunkBody { + match self { + Self::V1(chunk) => &mut chunk.content, + Self::V2(chunk) => &mut chunk.content, + } + } + + #[inline] + pub fn shard_id(&self) -> ShardId { + match self { + Self::V1(chunk) => chunk.header.inner.shard_id, + Self::V2(chunk) => chunk.header.shard_id(), + } + } + + #[inline] + pub fn encoded_merkle_root(&self) -> CryptoHash { + match self { + Self::V1(chunk) => chunk.header.inner.encoded_merkle_root, + Self::V2(chunk) => chunk.header.encoded_merkle_root(), + } + } + + #[inline] + pub fn encoded_length(&self) -> u64 { + match self { + Self::V1(chunk) => chunk.header.inner.encoded_length, + Self::V2(chunk) => chunk.header.encoded_length(), + } + } + + pub fn from_header( + header: ShardChunkHeader, + total_parts: usize, + protocol_version: ProtocolVersion, + ) -> Self { + if protocol_version < SHARD_CHUNK_HEADER_UPGRADE_VERSION { + if let ShardChunkHeader::V1(header) = header { + let chunk = EncodedShardChunkV1 { + header, + content: EncodedShardChunkBody { parts: vec![None; total_parts] }, + }; + Self::V1(chunk) + } else { + panic!("Attempted to include ShardChunkHeader::V2 in old protocol version"); + } + } else { + let chunk = EncodedShardChunkV2 { + header, + content: EncodedShardChunkBody { parts: vec![None; total_parts] }, + }; + Self::V2(chunk) + } + } + + fn decode_transaction_receipts( + parts: &[Option>], + encoded_length: u64, + ) -> Result { + let encoded_data = parts + .iter() + .flat_map(|option| option.as_ref().expect("Missing shard").iter()) + .cloned() + .take(encoded_length as usize) + .collect::>(); + + TransactionReceipt::try_from_slice(&encoded_data) + } + + pub fn encode_transaction_receipts( + rs: &ReedSolomonWrapper, + transactions: Vec, + outgoing_receipts: &[Receipt], + ) -> Result<(Vec>>, u64), std::io::Error> { + let mut bytes = + borsh::to_vec(&TransactionReceipt(transactions, outgoing_receipts.to_vec()))?; + + let mut parts = Vec::with_capacity(rs.total_shard_count()); + let data_parts = rs.data_shard_count(); + let total_parts = rs.total_shard_count(); + let encoded_length = bytes.len(); + + if bytes.len() % data_parts != 0 { + bytes.extend((bytes.len() % data_parts..data_parts).map(|_| 0)); + } + let shard_length = (encoded_length + data_parts - 1) / data_parts; + assert_eq!(bytes.len(), shard_length * data_parts); + + for i in 0..data_parts { + parts.push(Some( + bytes[i * shard_length..(i + 1) * shard_length].to_vec().into_boxed_slice() + as Box<[u8]>, + )); + } + for _ in data_parts..total_parts { + parts.push(None); + } + Ok((parts, encoded_length as u64)) + } + + pub fn new( + prev_block_hash: CryptoHash, + prev_state_root: StateRoot, + prev_outcome_root: CryptoHash, + height: BlockHeight, + shard_id: ShardId, + rs: &mut ReedSolomonWrapper, + prev_gas_used: Gas, + gas_limit: Gas, + prev_balance_burnt: Balance, + tx_root: CryptoHash, + prev_validator_power_proposals: Vec, + prev_validator_frozen_proposals: Vec, + transactions: Vec, + prev_outgoing_receipts: &[Receipt], + prev_outgoing_receipts_root: CryptoHash, + signer: &dyn ValidatorSigner, + protocol_version: ProtocolVersion, + ) -> Result<(Self, Vec), std::io::Error> { + let (transaction_receipts_parts, encoded_length) = + Self::encode_transaction_receipts(rs, transactions, prev_outgoing_receipts)?; + + let mut content = EncodedShardChunkBody { parts: transaction_receipts_parts }; + content.reconstruct(rs).unwrap(); + let (encoded_merkle_root, merkle_paths) = content.get_merkle_hash_and_paths(); + + let block_header_v3_version = Some(ProtocolFeature::BlockHeaderV3.protocol_version()); + + if protocol_version < SHARD_CHUNK_HEADER_UPGRADE_VERSION { + let prev_validator_power_proposals = + prev_validator_power_proposals.into_iter().map(|v| v.into_v1()).collect(); + let prev_validator_frozen_proposals = + prev_validator_frozen_proposals.into_iter().map(|v| v.into_v1()).collect(); + let header = ShardChunkHeaderV1::new( + prev_block_hash, + prev_state_root, + prev_outcome_root, + encoded_merkle_root, + encoded_length, + height, + shard_id, + prev_gas_used, + gas_limit, + prev_balance_burnt, + prev_outgoing_receipts_root, + tx_root, + prev_validator_power_proposals, + prev_validator_frozen_proposals, + signer, + ); + let chunk = EncodedShardChunkV1 { header, content }; + Ok((Self::V1(chunk), merkle_paths)) + } else if block_header_v3_version.is_none() + || protocol_version < block_header_v3_version.unwrap() + { + let validator_power_proposals = + prev_validator_power_proposals.into_iter().map(|v| v.into_v1()).collect(); + let validator_frozen_proposals = + prev_validator_frozen_proposals.into_iter().map(|v| v.into_v1()).collect(); + let header = ShardChunkHeaderV2::new( + prev_block_hash, + prev_state_root, + prev_outcome_root, + encoded_merkle_root, + encoded_length, + height, + shard_id, + prev_gas_used, + gas_limit, + prev_balance_burnt, + prev_outgoing_receipts_root, + tx_root, + validator_power_proposals, + validator_frozen_proposals, + signer, + ); + let chunk = EncodedShardChunkV2 { header: ShardChunkHeader::V2(header), content }; + Ok((Self::V2(chunk), merkle_paths)) + } else { + let header = ShardChunkHeaderV3::new( + prev_block_hash, + prev_state_root, + prev_outcome_root, + encoded_merkle_root, + encoded_length, + height, + shard_id, + prev_gas_used, + gas_limit, + prev_balance_burnt, + prev_outgoing_receipts_root, + tx_root, + prev_validator_power_proposals, + prev_validator_frozen_proposals, + signer, + ); + let chunk = EncodedShardChunkV2 { header: ShardChunkHeader::V3(header), content }; + Ok((Self::V2(chunk), merkle_paths)) + } + } + + pub fn chunk_hash(&self) -> ChunkHash { + match self { + Self::V1(chunk) => chunk.header.chunk_hash(), + Self::V2(chunk) => chunk.header.chunk_hash(), + } + } + + fn part_ords_to_parts( + &self, + part_ords: Vec, + merkle_paths: &[MerklePath], + ) -> Vec { + let parts = match self { + Self::V1(chunk) => &chunk.content.parts, + Self::V2(chunk) => &chunk.content.parts, + }; + part_ords + .into_iter() + .map(|part_ord| PartialEncodedChunkPart { + part_ord, + part: parts[part_ord as usize].clone().unwrap(), + merkle_proof: merkle_paths[part_ord as usize].clone(), + }) + .collect() + } + + pub fn create_partial_encoded_chunk( + &self, + part_ords: Vec, + receipts: Vec, + merkle_paths: &[MerklePath], + ) -> PartialEncodedChunk { + let parts = self.part_ords_to_parts(part_ords, merkle_paths); + match self { + Self::V1(chunk) => { + let chunk = PartialEncodedChunkV1 { header: chunk.header.clone(), parts, receipts }; + PartialEncodedChunk::V1(chunk) + } + Self::V2(chunk) => { + let chunk = PartialEncodedChunkV2 { header: chunk.header.clone(), parts, receipts }; + PartialEncodedChunk::V2(chunk) + } + } + } + + pub fn create_partial_encoded_chunk_with_arc_receipts( + &self, + part_ords: Vec, + receipts: Vec>, + merkle_paths: &[MerklePath], + ) -> PartialEncodedChunkWithArcReceipts { + let parts = self.part_ords_to_parts(part_ords, merkle_paths); + let header = match self { + Self::V1(chunk) => ShardChunkHeader::V1(chunk.header.clone()), + Self::V2(chunk) => chunk.header.clone(), + }; + PartialEncodedChunkWithArcReceipts { header, parts, receipts } + } + + pub fn decode_chunk(&self, data_parts: usize) -> Result { + let _span = debug_span!( + target: "sharding", + "decode_chunk", + data_parts, + height_included = self.cloned_header().height_included(), + shard_id = self.cloned_header().shard_id(), + chunk_hash = ?self.chunk_hash()) + .entered(); + let parts = match self { + Self::V1(chunk) => &chunk.content.parts[0..data_parts], + Self::V2(chunk) => &chunk.content.parts[0..data_parts], + }; + let encoded_length = match self { + Self::V1(chunk) => chunk.header.inner.encoded_length, + Self::V2(chunk) => chunk.header.encoded_length(), + }; + + let transaction_receipts = Self::decode_transaction_receipts(parts, encoded_length)?; + + match self { + Self::V1(chunk) => Ok(ShardChunk::V1(ShardChunkV1 { + chunk_hash: chunk.header.chunk_hash(), + header: chunk.header.clone(), + transactions: transaction_receipts.0, + prev_outgoing_receipts: transaction_receipts.1, + })), + + Self::V2(chunk) => Ok(ShardChunk::V2(ShardChunkV2 { + chunk_hash: chunk.header.chunk_hash(), + header: chunk.header.clone(), + transactions: transaction_receipts.0, + prev_outgoing_receipts: transaction_receipts.1, + })), + } + } +} + +/// The ttl for a reed solomon instance to control memory usage. This number below corresponds to +/// roughly 60MB of memory usage. +const RS_TTL: u64 = 2 * 1024; + +/// Wrapper around reed solomon which occasionally resets the underlying +/// reed solomon instead to work around the memory leak in reed solomon +/// implementation +pub struct ReedSolomonWrapper { + rs: ReedSolomon, + ttl: u64, +} + +impl ReedSolomonWrapper { + pub fn new(data_shards: usize, parity_shards: usize) -> Self { + ReedSolomonWrapper { + rs: ReedSolomon::new(data_shards, parity_shards).unwrap(), + ttl: RS_TTL, + } + } + + pub fn reconstruct>( + &mut self, + slices: &mut [T], + ) -> Result<(), reed_solomon_erasure::Error> { + let res = self.rs.reconstruct(slices); + self.ttl -= 1; + if self.ttl == 0 { + *self = + ReedSolomonWrapper::new(self.rs.data_shard_count(), self.rs.parity_shard_count()); + } + res + } + + pub fn data_shard_count(&self) -> usize { + self.rs.data_shard_count() + } + + pub fn total_shard_count(&self) -> usize { + self.rs.total_shard_count() + } +} diff --git a/core/primitives/src/sharding/shard_chunk_header_inner.rs b/core/primitives/src/sharding/shard_chunk_header_inner.rs new file mode 100644 index 000000000..46f2d5633 --- /dev/null +++ b/core/primitives/src/sharding/shard_chunk_header_inner.rs @@ -0,0 +1,183 @@ +use crate::types::validator_power::{ValidatorPower, ValidatorPowerIter, ValidatorPowerV1}; +use crate::types::{StateRoot, ValidatorFrozenV1}; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_primitives_core::hash::CryptoHash; +use unc_primitives_core::types::{Balance, BlockHeight, Gas, ShardId}; +use crate::types::validator_frozen::{ValidatorFrozen, ValidatorFrozenIter}; + +#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug)] +pub enum ShardChunkHeaderInner { + V1(ShardChunkHeaderInnerV1), + V2(ShardChunkHeaderInnerV2), +} + +impl ShardChunkHeaderInner { + #[inline] + pub fn prev_state_root(&self) -> &StateRoot { + match self { + Self::V1(inner) => &inner.prev_state_root, + Self::V2(inner) => &inner.prev_state_root, + } + } + + #[inline] + pub fn prev_block_hash(&self) -> &CryptoHash { + match self { + Self::V1(inner) => &inner.prev_block_hash, + Self::V2(inner) => &inner.prev_block_hash, + } + } + + #[inline] + pub fn gas_limit(&self) -> Gas { + match self { + Self::V1(inner) => inner.gas_limit, + Self::V2(inner) => inner.gas_limit, + } + } + + #[inline] + pub fn prev_gas_used(&self) -> Gas { + match self { + Self::V1(inner) => inner.prev_gas_used, + Self::V2(inner) => inner.prev_gas_used, + } + } + + #[inline] + pub fn prev_validator_power_proposals(&self) -> ValidatorPowerIter { + match self { + Self::V1(inner) => ValidatorPowerIter::v1(&inner.prev_validator_power_proposals), + Self::V2(inner) => ValidatorPowerIter::new(&inner.prev_validator_power_proposals), + } + } + + #[inline] + pub fn prev_validator_frozen_proposals(&self) -> ValidatorFrozenIter { + match self { + Self::V1(inner) => ValidatorFrozenIter::v1(&inner.prev_validator_frozen_proposals), + Self::V2(inner) => ValidatorFrozenIter::new(&inner.prev_validator_frozen_proposals), + } + } + + #[inline] + pub fn height_created(&self) -> BlockHeight { + match self { + Self::V1(inner) => inner.height_created, + Self::V2(inner) => inner.height_created, + } + } + + #[inline] + pub fn shard_id(&self) -> ShardId { + match self { + Self::V1(inner) => inner.shard_id, + Self::V2(inner) => inner.shard_id, + } + } + + #[inline] + pub fn prev_outcome_root(&self) -> &CryptoHash { + match self { + Self::V1(inner) => &inner.prev_outcome_root, + Self::V2(inner) => &inner.prev_outcome_root, + } + } + + #[inline] + pub fn encoded_merkle_root(&self) -> &CryptoHash { + match self { + Self::V1(inner) => &inner.encoded_merkle_root, + Self::V2(inner) => &inner.encoded_merkle_root, + } + } + + #[inline] + pub fn encoded_length(&self) -> u64 { + match self { + Self::V1(inner) => inner.encoded_length, + Self::V2(inner) => inner.encoded_length, + } + } + + #[inline] + pub fn prev_balance_burnt(&self) -> Balance { + match self { + Self::V1(inner) => inner.prev_balance_burnt, + Self::V2(inner) => inner.prev_balance_burnt, + } + } + + #[inline] + pub fn prev_outgoing_receipts_root(&self) -> &CryptoHash { + match self { + Self::V1(inner) => &inner.prev_outgoing_receipts_root, + Self::V2(inner) => &inner.prev_outgoing_receipts_root, + } + } + + #[inline] + pub fn tx_root(&self) -> &CryptoHash { + match self { + Self::V1(inner) => &inner.tx_root, + Self::V2(inner) => &inner.tx_root, + } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug)] +pub struct ShardChunkHeaderInnerV1 { + /// Previous block hash. + pub prev_block_hash: CryptoHash, + pub prev_state_root: StateRoot, + /// Root of the outcomes from execution transactions and results of the previous chunk. + pub prev_outcome_root: CryptoHash, + pub encoded_merkle_root: CryptoHash, + pub encoded_length: u64, + pub height_created: BlockHeight, + /// Shard index. + pub shard_id: ShardId, + /// Gas used in the previous chunk. + pub prev_gas_used: Gas, + /// Gas limit voted by validators. + pub gas_limit: Gas, + /// Total balance burnt in the previous chunk. + pub prev_balance_burnt: Balance, + /// Previous chunk's outgoing receipts merkle root. + pub prev_outgoing_receipts_root: CryptoHash, + /// Tx merkle root. + pub tx_root: CryptoHash, + /// Validator proposals from the previous chunk. + pub prev_validator_power_proposals: Vec, + /// Validator proposals from the previous chunk. + pub prev_validator_frozen_proposals: Vec, +} + +// V1 -> V2: Use versioned ValidatorStake structure in proposals +#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug)] +pub struct ShardChunkHeaderInnerV2 { + /// Previous block hash. + pub prev_block_hash: CryptoHash, + pub prev_state_root: StateRoot, + /// Root of the outcomes from execution transactions and results of the previous chunk. + pub prev_outcome_root: CryptoHash, + pub encoded_merkle_root: CryptoHash, + pub encoded_length: u64, + pub height_created: BlockHeight, + /// Shard index. + pub shard_id: ShardId, + /// Gas used in the previous chunk. + pub prev_gas_used: Gas, + /// Gas limit voted by validators. + pub gas_limit: Gas, + /// Total balance burnt in the previous chunk. + pub prev_balance_burnt: Balance, + /// Previous chunk's outgoing receipts merkle root. + pub prev_outgoing_receipts_root: CryptoHash, + /// Tx merkle root. + pub tx_root: CryptoHash, + /// Validator proposals from the previous chunk. + pub prev_validator_power_proposals: Vec, + /// Validator proposals from the previous chunk. + pub prev_validator_frozen_proposals: Vec, +} diff --git a/core/primitives/src/signable_message.rs b/core/primitives/src/signable_message.rs new file mode 100644 index 000000000..4ca7b04f3 --- /dev/null +++ b/core/primitives/src/signable_message.rs @@ -0,0 +1,299 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_crypto::{Signature, Signer}; +use unc_primitives_core::hash::hash; +use unc_primitives_core::types::AccountId; + +// These numbers are picked to be compatible with the current protocol and how +// transactions are defined in it. Introducing this is no protocol change. This +// is just a forward-looking implementation detail of meta transactions. +// +// We plan to establish a standard with NEP-461 that makes this an official +// specification in the wider ecosystem. Note that NEP-461 should not change the +// protocol in any way, unless we have to change meta transaction implementation +// details to adhere to the future standard. +// [NEP-461](https://github.com/near/NEPs/pull/461) +// +// TODO: consider making these public once there is an approved standard. +const MIN_ON_CHAIN_DISCRIMINANT: u32 = 1 << 30; +const MAX_ON_CHAIN_DISCRIMINANT: u32 = (1 << 31) - 1; +const MIN_OFF_CHAIN_DISCRIMINANT: u32 = 1 << 31; +const MAX_OFF_CHAIN_DISCRIMINANT: u32 = u32::MAX; + +// NEPs currently included in the scheme +const NEP_366_META_TRANSACTIONS: u32 = 366; + +/// Used to distinguish message types that are sign by account keys, to avoid an +/// abuse of signed messages as something else. +/// +/// This prefix must be be at the first four bytes of a message body that is +/// signed under this signature scheme. +/// +/// The scheme is a draft introduced to avoid security issues with the +/// implementation of meta transactions (NEP-366) but will eventually be +/// standardized with NEP-461 that solves the problem more generally. +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + BorshSerialize, + BorshDeserialize, + serde::Serialize, + serde::Deserialize, +)] +pub struct MessageDiscriminant { + /// The unique prefix, serialized in little-endian by borsh. + discriminant: u32, +} + +/// A wrapper around a message that should be signed using this scheme. +/// +/// Only used for constructing a signature, not used to transmit messages. The +/// discriminant prefix is implicit and should be known by the receiver based on +/// the context in which the message is received. +#[derive(BorshSerialize)] +pub struct SignableMessage<'a, T> { + pub discriminant: MessageDiscriminant, + pub msg: &'a T, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[non_exhaustive] +pub enum SignableMessageType { + /// A delegate action, intended for a relayer to included it in an action list of a transaction. + DelegateAction, +} + +#[derive(thiserror::Error, Debug)] +#[non_exhaustive] +pub enum ReadDiscriminantError { + #[error("does not fit any known categories")] + UnknownMessageType, + #[error("NEP {0} does not have a known on-chain use")] + UnknownOnChainNep(u32), + #[error("NEP {0} does not have a known off-chain use")] + UnknownOffChainNep(u32), + #[error("discriminant is in the range for transactions")] + TransactionFound, +} + +#[derive(thiserror::Error, Debug)] +#[non_exhaustive] +pub enum CreateDiscriminantError { + #[error("nep number {0} is too big")] + NepTooLarge(u32), +} + +impl<'a, T: BorshSerialize> SignableMessage<'a, T> { + pub fn new(msg: &'a T, ty: SignableMessageType) -> Self { + let discriminant = ty.into(); + Self { discriminant, msg } + } + + pub fn sign(&self, signer: &dyn Signer) -> Signature { + let bytes = borsh::to_vec(&self).expect("Failed to deserialize"); + let hash = hash(&bytes); + signer.sign(hash.as_bytes()) + } +} + +impl MessageDiscriminant { + /// Create a discriminant for an on-chain actionable message that was introduced in the specified NEP. + /// + /// Allows creating discriminants currently unknown in this crate, which can + /// be useful to prototype new standards. For example, when the client + /// project still relies on an older version of this crate while nightly + /// framework already supports a new NEP. + pub fn new_on_chain(nep: u32) -> Result { + // unchecked arithmetic: these are constants + if nep > MAX_ON_CHAIN_DISCRIMINANT - MIN_ON_CHAIN_DISCRIMINANT { + Err(CreateDiscriminantError::NepTooLarge(nep)) + } else { + Ok(Self { + // unchecked arithmetic: just checked range + discriminant: MIN_ON_CHAIN_DISCRIMINANT + nep, + }) + } + } + + /// Create a discriminant for an off-chain message that was introduced in the specified NEP. + /// + /// Allows creating discriminants currently unknown in this crate, which can + /// be useful to prototype new standards. For example, when the client + /// project still relies on an older version of this crate while nightly + /// framework already supports a new NEP. + pub fn new_off_chain(nep: u32) -> Result { + // unchecked arithmetic: these are constants + if nep > MAX_OFF_CHAIN_DISCRIMINANT - MIN_OFF_CHAIN_DISCRIMINANT { + Err(CreateDiscriminantError::NepTooLarge(nep)) + } else { + Ok(Self { + // unchecked arithmetic: just checked range + discriminant: MIN_OFF_CHAIN_DISCRIMINANT + nep, + }) + } + } + + /// Returns the raw integer value of the discriminant as an integer value. + pub fn raw_discriminant(&self) -> u32 { + self.discriminant + } + + /// Whether this discriminant marks a traditional `SignedTransaction`. + pub fn is_transaction(&self) -> bool { + // Backwards compatibility with transaction that were defined before this standard: + // Transaction begins with `AccountId`, which is just a `String` in + // borsh serialization, which starts with the length of the underlying + // byte vector in little endian u32. + // Currently allowed AccountIds are between 2 and 64 bytes. + self.discriminant >= AccountId::MIN_LEN as u32 + && self.discriminant <= AccountId::MAX_LEN as u32 + } + + /// If this discriminant marks a message intended for on-chain use, return + /// the NEP in which the message type was introduced. + pub fn on_chain_nep(&self) -> Option { + if self.discriminant < MIN_ON_CHAIN_DISCRIMINANT + || self.discriminant > MAX_ON_CHAIN_DISCRIMINANT + { + None + } else { + // unchecked arithmetic: just checked it is in range + let nep = self.discriminant - MIN_ON_CHAIN_DISCRIMINANT; + Some(nep) + } + } + + /// If this discriminant marks a message intended for off-chain use, return + /// the NEP in which the message type was introduced. + /// + /// clippy: MAX_OFF_CHAIN_DISCRIMINANT currently is u32::MAX which makes the + /// comparison pointless, however I think it helps code readability to have + /// it spelled out anyway + #[allow(clippy::absurd_extreme_comparisons)] + pub fn off_chain_nep(&self) -> Option { + if self.discriminant < MIN_OFF_CHAIN_DISCRIMINANT + || self.discriminant > MAX_OFF_CHAIN_DISCRIMINANT + { + None + } else { + // unchecked arithmetic: just checked it is in range + let nep = self.discriminant - MIN_OFF_CHAIN_DISCRIMINANT; + Some(nep) + } + } +} + +impl TryFrom for SignableMessageType { + type Error = ReadDiscriminantError; + + fn try_from(discriminant: MessageDiscriminant) -> Result { + if discriminant.is_transaction() { + Err(Self::Error::TransactionFound) + } else if let Some(nep) = discriminant.on_chain_nep() { + match nep { + NEP_366_META_TRANSACTIONS => Ok(Self::DelegateAction), + _ => Err(Self::Error::UnknownOnChainNep(nep)), + } + } else if let Some(nep) = discriminant.off_chain_nep() { + Err(Self::Error::UnknownOffChainNep(nep)) + } else { + Err(Self::Error::UnknownMessageType) + } + } +} + +impl From for MessageDiscriminant { + fn from(ty: SignableMessageType) -> Self { + // unwrapping here is ok, we know the constant NEP numbers used are in range + match ty { + SignableMessageType::DelegateAction => { + MessageDiscriminant::new_on_chain(NEP_366_META_TRANSACTIONS).unwrap() + } + } + } +} + +#[cfg(test)] +mod tests { + use unc_crypto::{InMemorySigner, KeyType, PublicKey}; + use unc_primitives_core::account::id::AccountIdRef; + + use super::*; + use crate::action::delegate::{DelegateAction, SignedDelegateAction}; + + // Note: this is currently a simplified copy of unc-primitives::test_utils::create_user_test_signer + // TODO: consider whether it’s worth re-unifying them? it’s test-only code anyway. + fn create_user_test_signer(account_id: &AccountIdRef) -> InMemorySigner { + InMemorySigner::from_seed(account_id.to_owned(), KeyType::ED25519, account_id.as_str()) + } + + // happy path for NEP-366 signature + #[test] + fn nep_366_ok() { + let sender_id: AccountId = "alice.near".parse().unwrap(); + let receiver_id: AccountId = "bob.near".parse().unwrap(); + let signer = create_user_test_signer(&sender_id); + + let delegate_action = delegate_action(sender_id, receiver_id, signer.public_key()); + let signable = SignableMessage::new(&delegate_action, SignableMessageType::DelegateAction); + let signed = SignedDelegateAction { signature: signable.sign(&signer), delegate_action }; + + assert!(signed.verify()); + } + + // Try to use a wrong nep number in NEP-366 signature verification. + #[test] + fn nep_366_wrong_nep() { + let sender_id: AccountId = "alice.near".parse().unwrap(); + let receiver_id: AccountId = "bob.near".parse().unwrap(); + let signer = create_user_test_signer(&sender_id); + + let delegate_action = delegate_action(sender_id, receiver_id, signer.public_key()); + let wrong_nep = 777; + let signable = SignableMessage { + discriminant: MessageDiscriminant::new_on_chain(wrong_nep).unwrap(), + msg: &delegate_action, + }; + let signed = SignedDelegateAction { signature: signable.sign(&signer), delegate_action }; + + assert!(!signed.verify()); + } + + // Try to use a wrong message type in NEP-366 signature verification. + #[test] + fn nep_366_wrong_msg_type() { + let sender_id: AccountId = "alice.near".parse().unwrap(); + let receiver_id: AccountId = "bob.near".parse().unwrap(); + let signer = create_user_test_signer(&sender_id); + + let delegate_action = delegate_action(sender_id, receiver_id, signer.public_key()); + let correct_nep = 366; + // here we use it as an off-chain only signature + let wrong_discriminant = MessageDiscriminant::new_off_chain(correct_nep).unwrap(); + let signable = SignableMessage { discriminant: wrong_discriminant, msg: &delegate_action }; + let signed = SignedDelegateAction { signature: signable.sign(&signer), delegate_action }; + + assert!(!signed.verify()); + } + + fn delegate_action( + sender_id: AccountId, + receiver_id: AccountId, + public_key: PublicKey, + ) -> DelegateAction { + let delegate_action = DelegateAction { + sender_id, + receiver_id, + actions: vec![], + nonce: 0, + max_block_height: 1000, + public_key, + }; + delegate_action + } +} diff --git a/core/primitives/src/snapshots/near_primitives__views__tests__exec_metadata_v1_view.snap b/core/primitives/src/snapshots/near_primitives__views__tests__exec_metadata_v1_view.snap new file mode 100644 index 000000000..38bc34efc --- /dev/null +++ b/core/primitives/src/snapshots/near_primitives__views__tests__exec_metadata_v1_view.snap @@ -0,0 +1,8 @@ +--- +source: core/primitives/src/views.rs +expression: view +--- +{ + "version": 1, + "gas_profile": null +} diff --git a/core/primitives/src/snapshots/near_primitives__views__tests__exec_metadata_v2_view.snap b/core/primitives/src/snapshots/near_primitives__views__tests__exec_metadata_v2_view.snap new file mode 100644 index 000000000..cede5600b --- /dev/null +++ b/core/primitives/src/snapshots/near_primitives__views__tests__exec_metadata_v2_view.snap @@ -0,0 +1,369 @@ +--- +source: core/primitives/src/views.rs +expression: view +--- +{ + "version": 2, + "gas_profile": [ + { + "cost_category": "ACTION_COST", + "cost": "ADD_KEY", + "gas_used": "1006" + }, + { + "cost_category": "ACTION_COST", + "cost": "CREATE_ACCOUNT", + "gas_used": "1000" + }, + { + "cost_category": "ACTION_COST", + "cost": "DELETE_ACCOUNT", + "gas_used": "1001" + }, + { + "cost_category": "ACTION_COST", + "cost": "DELETE_KEY", + "gas_used": "1007" + }, + { + "cost_category": "ACTION_COST", + "cost": "DEPLOY_CONTRACT", + "gas_used": "1002" + }, + { + "cost_category": "ACTION_COST", + "cost": "FUNCTION_CALL", + "gas_used": "1003" + }, + { + "cost_category": "ACTION_COST", + "cost": "NEW_DATA_RECEIPT_BYTE", + "gas_used": "1008" + }, + { + "cost_category": "ACTION_COST", + "cost": "NEW_RECEIPT", + "gas_used": "1009" + }, + { + "cost_category": "ACTION_COST", + "cost": "STAKE", + "gas_used": "1005" + }, + { + "cost_category": "ACTION_COST", + "cost": "TRANSFER", + "gas_used": "1004" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ALT_BN128_G1_MULTIEXP_BASE", + "gas_used": "54" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ALT_BN128_G1_MULTIEXP_ELEMENT", + "gas_used": "55" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ALT_BN128_G1_SUM_BASE", + "gas_used": "58" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ALT_BN128_G1_SUM_ELEMENT", + "gas_used": "59" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ALT_BN128_PAIRING_CHECK_BASE", + "gas_used": "56" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ALT_BN128_PAIRING_CHECK_ELEMENT", + "gas_used": "57" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "BASE", + "gas_used": "0" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "CONTRACT_LOADING_BASE", + "gas_used": "1" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "CONTRACT_LOADING_BYTES", + "gas_used": "2" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ECRECOVER_BASE", + "gas_used": "23" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ED25519_VERIFY_BASE", + "gas_used": "0" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ED25519_VERIFY_BYTE", + "gas_used": "0" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "KECCAK256_BASE", + "gas_used": "17" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "KECCAK256_BYTE", + "gas_used": "18" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "KECCAK512_BASE", + "gas_used": "19" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "KECCAK512_BYTE", + "gas_used": "20" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "LOG_BASE", + "gas_used": "24" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "LOG_BYTE", + "gas_used": "25" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "PROMISE_AND_BASE", + "gas_used": "47" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "PROMISE_AND_PER_PROMISE", + "gas_used": "48" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "PROMISE_RETURN", + "gas_used": "49" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "READ_CACHED_TRIE_NODE", + "gas_used": "53" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "READ_MEMORY_BASE", + "gas_used": "3" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "READ_MEMORY_BYTE", + "gas_used": "4" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "READ_REGISTER_BASE", + "gas_used": "7" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "READ_REGISTER_BYTE", + "gas_used": "8" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "RIPEMD160_BASE", + "gas_used": "21" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "RIPEMD160_BLOCK", + "gas_used": "22" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "SHA256_BASE", + "gas_used": "15" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "SHA256_BYTE", + "gas_used": "16" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_HAS_KEY_BASE", + "gas_used": "36" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_HAS_KEY_BYTE", + "gas_used": "37" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_CREATE_FROM_BYTE", + "gas_used": "41" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_CREATE_PREFIX_BASE", + "gas_used": "38" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_CREATE_PREFIX_BYTE", + "gas_used": "39" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_CREATE_RANGE_BASE", + "gas_used": "40" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_CREATE_TO_BYTE", + "gas_used": "42" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_NEXT_BASE", + "gas_used": "43" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_NEXT_KEY_BYTE", + "gas_used": "44" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_NEXT_VALUE_BYTE", + "gas_used": "45" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_READ_BASE", + "gas_used": "30" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_READ_KEY_BYTE", + "gas_used": "31" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_READ_VALUE_BYTE", + "gas_used": "32" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_REMOVE_BASE", + "gas_used": "33" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_REMOVE_KEY_BYTE", + "gas_used": "34" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_REMOVE_RET_VALUE_BYTE", + "gas_used": "35" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_WRITE_BASE", + "gas_used": "26" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_WRITE_EVICTED_BYTE", + "gas_used": "29" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_WRITE_KEY_BYTE", + "gas_used": "27" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_WRITE_VALUE_BYTE", + "gas_used": "28" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "TOUCHING_TRIE_NODE", + "gas_used": "46" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "UTF16_DECODING_BASE", + "gas_used": "13" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "UTF16_DECODING_BYTE", + "gas_used": "14" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "UTF8_DECODING_BASE", + "gas_used": "11" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "UTF8_DECODING_BYTE", + "gas_used": "12" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "VALIDATOR_STAKE_BASE", + "gas_used": "50" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "VALIDATOR_TOTAL_STAKE_BASE", + "gas_used": "51" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "WASM_INSTRUCTION", + "gas_used": "52" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "WRITE_MEMORY_BASE", + "gas_used": "5" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "WRITE_MEMORY_BYTE", + "gas_used": "6" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "WRITE_REGISTER_BASE", + "gas_used": "9" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "WRITE_REGISTER_BYTE", + "gas_used": "10" + } + ] +} diff --git a/core/primitives/src/snapshots/near_primitives__views__tests__exec_metadata_v3_view.snap b/core/primitives/src/snapshots/near_primitives__views__tests__exec_metadata_v3_view.snap new file mode 100644 index 000000000..5f09da2d3 --- /dev/null +++ b/core/primitives/src/snapshots/near_primitives__views__tests__exec_metadata_v3_view.snap @@ -0,0 +1,389 @@ +--- +source: core/primitives/src/views.rs +expression: view +--- +{ + "version": 3, + "gas_profile": [ + { + "cost_category": "ACTION_COST", + "cost": "ADD_FULL_ACCESS_KEY", + "gas_used": "1008" + }, + { + "cost_category": "ACTION_COST", + "cost": "ADD_FUNCTION_CALL_KEY_BASE", + "gas_used": "1009" + }, + { + "cost_category": "ACTION_COST", + "cost": "ADD_FUNCTION_CALL_KEY_BYTE", + "gas_used": "1010" + }, + { + "cost_category": "ACTION_COST", + "cost": "CREATE_ACCOUNT", + "gas_used": "1000" + }, + { + "cost_category": "ACTION_COST", + "cost": "DELEGATE", + "gas_used": "1015" + }, + { + "cost_category": "ACTION_COST", + "cost": "DELETE_ACCOUNT", + "gas_used": "1001" + }, + { + "cost_category": "ACTION_COST", + "cost": "DELETE_KEY", + "gas_used": "1011" + }, + { + "cost_category": "ACTION_COST", + "cost": "DEPLOY_CONTRACT_BASE", + "gas_used": "1002" + }, + { + "cost_category": "ACTION_COST", + "cost": "DEPLOY_CONTRACT_BYTE", + "gas_used": "1003" + }, + { + "cost_category": "ACTION_COST", + "cost": "FUNCTION_CALL_BASE", + "gas_used": "1004" + }, + { + "cost_category": "ACTION_COST", + "cost": "FUNCTION_CALL_BYTE", + "gas_used": "1005" + }, + { + "cost_category": "ACTION_COST", + "cost": "NEW_ACTION_RECEIPT", + "gas_used": "1012" + }, + { + "cost_category": "ACTION_COST", + "cost": "NEW_DATA_RECEIPT_BASE", + "gas_used": "1013" + }, + { + "cost_category": "ACTION_COST", + "cost": "NEW_DATA_RECEIPT_BYTE", + "gas_used": "1014" + }, + { + "cost_category": "ACTION_COST", + "cost": "STAKE", + "gas_used": "1007" + }, + { + "cost_category": "ACTION_COST", + "cost": "TRANSFER", + "gas_used": "1006" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ALT_BN128_G1_MULTIEXP_BASE", + "gas_used": "53" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ALT_BN128_G1_MULTIEXP_ELEMENT", + "gas_used": "54" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ALT_BN128_G1_SUM_BASE", + "gas_used": "57" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ALT_BN128_G1_SUM_ELEMENT", + "gas_used": "58" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ALT_BN128_PAIRING_CHECK_BASE", + "gas_used": "55" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ALT_BN128_PAIRING_CHECK_ELEMENT", + "gas_used": "56" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "CONTRACT_LOADING_BASE", + "gas_used": "1" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "CONTRACT_LOADING_BYTES", + "gas_used": "2" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ECRECOVER_BASE", + "gas_used": "23" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ED25519_VERIFY_BASE", + "gas_used": "59" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "ED25519_VERIFY_BYTE", + "gas_used": "60" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "KECCAK256_BASE", + "gas_used": "17" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "KECCAK256_BYTE", + "gas_used": "18" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "KECCAK512_BASE", + "gas_used": "19" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "KECCAK512_BYTE", + "gas_used": "20" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "LOG_BASE", + "gas_used": "24" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "LOG_BYTE", + "gas_used": "25" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "PROMISE_AND_BASE", + "gas_used": "48" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "PROMISE_AND_PER_PROMISE", + "gas_used": "49" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "PROMISE_RETURN", + "gas_used": "50" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "READ_CACHED_TRIE_NODE", + "gas_used": "47" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "READ_MEMORY_BASE", + "gas_used": "3" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "READ_MEMORY_BYTE", + "gas_used": "4" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "READ_REGISTER_BASE", + "gas_used": "7" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "READ_REGISTER_BYTE", + "gas_used": "8" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "RIPEMD160_BASE", + "gas_used": "21" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "RIPEMD160_BLOCK", + "gas_used": "22" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "SHA256_BASE", + "gas_used": "15" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "SHA256_BYTE", + "gas_used": "16" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_HAS_KEY_BASE", + "gas_used": "36" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_HAS_KEY_BYTE", + "gas_used": "37" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_CREATE_FROM_BYTE", + "gas_used": "41" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_CREATE_PREFIX_BASE", + "gas_used": "38" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_CREATE_PREFIX_BYTE", + "gas_used": "39" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_CREATE_RANGE_BASE", + "gas_used": "40" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_CREATE_TO_BYTE", + "gas_used": "42" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_NEXT_BASE", + "gas_used": "43" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_NEXT_KEY_BYTE", + "gas_used": "44" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_ITER_NEXT_VALUE_BYTE", + "gas_used": "45" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_READ_BASE", + "gas_used": "30" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_READ_KEY_BYTE", + "gas_used": "31" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_READ_VALUE_BYTE", + "gas_used": "32" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_REMOVE_BASE", + "gas_used": "33" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_REMOVE_KEY_BYTE", + "gas_used": "34" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_REMOVE_RET_VALUE_BYTE", + "gas_used": "35" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_WRITE_BASE", + "gas_used": "26" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_WRITE_EVICTED_BYTE", + "gas_used": "29" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_WRITE_KEY_BYTE", + "gas_used": "27" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "STORAGE_WRITE_VALUE_BYTE", + "gas_used": "28" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "TOUCHING_TRIE_NODE", + "gas_used": "46" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "UTF16_DECODING_BASE", + "gas_used": "13" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "UTF16_DECODING_BYTE", + "gas_used": "14" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "UTF8_DECODING_BASE", + "gas_used": "11" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "UTF8_DECODING_BYTE", + "gas_used": "12" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "VALIDATOR_STAKE_BASE", + "gas_used": "51" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "VALIDATOR_TOTAL_STAKE_BASE", + "gas_used": "52" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "WRITE_MEMORY_BASE", + "gas_used": "5" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "WRITE_MEMORY_BYTE", + "gas_used": "6" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "WRITE_REGISTER_BASE", + "gas_used": "9" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "WRITE_REGISTER_BYTE", + "gas_used": "10" + } + ] +} diff --git a/core/primitives/src/snapshots/near_primitives__views__tests__runtime_config_view.snap b/core/primitives/src/snapshots/near_primitives__views__tests__runtime_config_view.snap new file mode 100644 index 000000000..4fc91b441 --- /dev/null +++ b/core/primitives/src/snapshots/near_primitives__views__tests__runtime_config_view.snap @@ -0,0 +1,218 @@ +--- +source: core/primitives/src/views.rs +expression: "&view" +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "vm_kind": "", + "disable_9393_fix": false, + "storage_get_mode": "FlatStorage", + "fix_contract_loading_cost": false, + "implicit_account_creation": true, + "math_extension": true, + "ed25519_verify": true, + "alt_bn128": true, + "function_call_weight": true, + "eth_implicit_accounts": false, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 262144, + "contract_prepare_version": 2, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 65, + "registrar_account_id": "registrar" + } +} diff --git a/core/primitives/src/state.rs b/core/primitives/src/state.rs new file mode 100644 index 000000000..3789eff62 --- /dev/null +++ b/core/primitives/src/state.rs @@ -0,0 +1,109 @@ +use borsh::{BorshDeserialize, BorshSerialize}; + +use unc_primitives_core::hash::{hash, CryptoHash}; + +/// State value reference. Used to charge fees for value length before retrieving the value itself. +#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Hash)] +pub struct ValueRef { + /// Value length in bytes. + pub length: u32, + /// Unique value hash. + pub hash: CryptoHash, +} + +impl ValueRef { + /// Create serialized value reference by the value. + /// Resulting array stores 4 bytes of length and then 32 bytes of hash. + /// TODO (#7327): consider passing hash here to avoid double computation + pub fn new(value: &[u8]) -> Self { + Self { length: value.len() as u32, hash: hash(value) } + } + + /// Decode value reference from the raw byte array. + pub fn decode(bytes: &[u8; 36]) -> Self { + let (length, hash) = stdx::split_array(bytes); + let length = u32::from_le_bytes(*length); + ValueRef { length, hash: CryptoHash(*hash) } + } + + /// Returns length of the referenced value. + pub fn len(&self) -> usize { + usize::try_from(self.length).unwrap() + } +} + +impl std::cmp::PartialEq<[u8]> for ValueRef { + fn eq(&self, rhs: &[u8]) -> bool { + self.len() == rhs.len() && self.hash == CryptoHash::hash_bytes(rhs) + } +} + +impl std::fmt::Debug for ValueRef { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(fmt, "({}, {})", self.length, self.hash) + } +} + +#[cfg(test)] +mod tests { + use crate::state::ValueRef; + use unc_primitives_core::hash::hash; + + #[test] + fn test_encode_decode() { + let value = vec![1, 2, 3]; + let old_value_ref = ValueRef::new(&value); + let mut value_ref_ser = [0u8; 36]; + value_ref_ser[0..4].copy_from_slice(&old_value_ref.length.to_le_bytes()); + value_ref_ser[4..36].copy_from_slice(&old_value_ref.hash.0); + let value_ref = ValueRef::decode(&value_ref_ser); + assert_eq!(value_ref.length, value.len() as u32); + assert_eq!(value_ref.hash, hash(&value)); + } +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq)] +pub enum FlatStateValue { + Ref(ValueRef), + Inlined(Vec), +} + +impl FlatStateValue { + /// Defines value size threshold for flat state inlining. + /// It means that values having size greater than the threshold will be stored + /// in FlatState as `FlatStateValue::Ref`, otherwise the whole value will be + /// stored as `FlatStateValue::Inlined`. + /// See the following comment for reasoning behind the threshold value: + /// https://github.com/utnet-org/utility/issues/8243#issuecomment-1523049994 + pub const INLINE_DISK_VALUE_THRESHOLD: usize = 4000; + + pub fn on_disk(value: &[u8]) -> Self { + if value.len() <= Self::INLINE_DISK_VALUE_THRESHOLD { + Self::inlined(value) + } else { + Self::value_ref(value) + } + } + + pub fn value_ref(value: &[u8]) -> Self { + Self::Ref(ValueRef::new(value)) + } + + pub fn inlined(value: &[u8]) -> Self { + Self::Inlined(value.to_vec()) + } + + pub fn to_value_ref(&self) -> ValueRef { + match self { + Self::Ref(value_ref) => value_ref.clone(), + Self::Inlined(value) => ValueRef::new(value), + } + } + + pub fn value_len(&self) -> usize { + match self { + Self::Ref(value_ref) => value_ref.len(), + Self::Inlined(value) => value.len(), + } + } +} diff --git a/core/primitives/src/state_part.rs b/core/primitives/src/state_part.rs new file mode 100644 index 000000000..18fdb207c --- /dev/null +++ b/core/primitives/src/state_part.rs @@ -0,0 +1,12 @@ +// to specify a part we always specify both part_id and num_parts together +#[derive(Copy, Clone, Debug)] +pub struct PartId { + pub idx: u64, + pub total: u64, +} +impl PartId { + pub fn new(part_id: u64, num_parts: u64) -> PartId { + assert!(part_id < num_parts); + PartId { idx: part_id, total: num_parts } + } +} diff --git a/core/primitives/src/state_record.rs b/core/primitives/src/state_record.rs new file mode 100644 index 000000000..e74b93b2e --- /dev/null +++ b/core/primitives/src/state_record.rs @@ -0,0 +1,190 @@ +use crate::account::{AccessKey, Account}; +use crate::hash::{hash, CryptoHash}; +use crate::receipt::{Receipt, ReceivedData}; +use crate::trie_key::trie_key_parsers::{ + parse_account_id_from_access_key_key, parse_account_id_from_account_key, + parse_account_id_from_contract_code_key, parse_account_id_from_contract_data_key, + parse_account_id_from_received_data_key, parse_data_id_from_received_data_key, + parse_data_key_from_contract_data_key, parse_public_key_from_access_key_key, +}; +use crate::trie_key::{col, TrieKey}; +use crate::types::{AccountId, StoreKey, StoreValue}; +use borsh::BorshDeserialize; +use unc_crypto::PublicKey; +use serde_with::base64::Base64; +use serde_with::serde_as; +use std::fmt::{Display, Formatter}; + +/// Record in the state storage. +#[serde_as] +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Eq, PartialEq)] +pub enum StateRecord { + /// Account information. + Account { account_id: AccountId, account: Account }, + /// Data records inside the contract, encoded in base64. + Data { account_id: AccountId, data_key: StoreKey, value: StoreValue }, + /// Contract code encoded in base64. + Contract { + account_id: AccountId, + #[serde_as(as = "Base64")] + code: Vec, + }, + /// Access key associated with some account. + AccessKey { account_id: AccountId, public_key: PublicKey, access_key: AccessKey }, + /// Postponed Action Receipt. + PostponedReceipt(Box), + /// Received data from DataReceipt encoded in base64 for the given account_id and data_id. + ReceivedData { + account_id: AccountId, + data_id: CryptoHash, + #[serde_as(as = "Option")] + data: Option>, + }, + /// Delayed Receipt. + /// The receipt was delayed because the shard was overwhelmed. + DelayedReceipt(Box), +} + +impl StateRecord { + /// NOTE: This function is not safe to be running during block production. It contains a lot + /// of `unwrap` and should only be used during `state_dump`. + /// Most `unwrap()` here are because the implementation of columns and data are internal and + /// can't be influenced by external calls. + pub fn from_raw_key_value(key: Vec, value: Vec) -> Option { + Self::from_raw_key_value_impl(key, value).unwrap_or(None) + } + + pub fn from_raw_key_value_impl( + key: Vec, + value: Vec, + ) -> Result, std::io::Error> { + Ok(match key[0] { + col::ACCOUNT => Some(StateRecord::Account { + account_id: parse_account_id_from_account_key(&key)?, + account: Account::try_from_slice(&value)?, + }), + col::CONTRACT_DATA => { + let account_id = parse_account_id_from_contract_data_key(&key)?; + let data_key = parse_data_key_from_contract_data_key(&key, &account_id)?; + Some(StateRecord::Data { + account_id, + data_key: data_key.to_vec().into(), + value: value.into(), + }) + } + col::CONTRACT_CODE => Some(StateRecord::Contract { + account_id: parse_account_id_from_contract_code_key(&key)?, + code: value, + }), + col::ACCESS_KEY => { + let access_key = AccessKey::try_from_slice(&value)?; + let account_id = parse_account_id_from_access_key_key(&key)?; + let public_key = parse_public_key_from_access_key_key(&key, &account_id)?; + Some(StateRecord::AccessKey { account_id, public_key, access_key }) + } + col::RECEIVED_DATA => { + let data = ReceivedData::try_from_slice(&value)?.data; + let account_id = parse_account_id_from_received_data_key(&key)?; + let data_id = parse_data_id_from_received_data_key(&key, &account_id)?; + Some(StateRecord::ReceivedData { account_id, data_id, data }) + } + col::POSTPONED_RECEIPT_ID => None, + col::PENDING_DATA_COUNT => None, + col::POSTPONED_RECEIPT => { + let receipt = Receipt::try_from_slice(&value)?; + Some(StateRecord::PostponedReceipt(Box::new(receipt))) + } + col::DELAYED_RECEIPT_OR_INDICES + if key.len() == TrieKey::DelayedReceiptIndices.len() => + { + None + } + col::DELAYED_RECEIPT_OR_INDICES => { + let receipt = Receipt::try_from_slice(&value)?; + Some(StateRecord::DelayedReceipt(Box::new(receipt))) + } + _ => { + println!("key[0]: {} is unreachable", key[0]); + None + } + }) + } + + pub fn get_type_string(&self) -> String { + match self { + StateRecord::Account { .. } => "Account", + StateRecord::Data { .. } => "Data", + StateRecord::Contract { .. } => "Contract", + StateRecord::AccessKey { .. } => "AccessKey", + StateRecord::PostponedReceipt { .. } => "PostponedReceipt", + StateRecord::ReceivedData { .. } => "ReceivedData", + StateRecord::DelayedReceipt { .. } => "DelayedReceipt", + } + .to_string() + } +} + +impl Display for StateRecord { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + StateRecord::Account { account_id, account } => { + write!(f, "Account {:?}: {:?}", account_id, account) + } + StateRecord::Data { account_id, data_key, value } => write!( + f, + "Storage {:?},{:?}: {:?}", + account_id, + to_printable(data_key.as_ref()), + to_printable(value.as_ref()) + ), + StateRecord::Contract { account_id, code: _ } => { + write!(f, "Code for {:?}: ...", account_id) + } + StateRecord::AccessKey { account_id, public_key, access_key } => { + write!(f, "Access key {:?},{:?}: {:?}", account_id, public_key, access_key) + } + StateRecord::ReceivedData { account_id, data_id, data } => write!( + f, + "Received data {:?},{:?}: {:?}", + account_id, + data_id, + data.as_ref().map(|v| to_printable(v)) + ), + StateRecord::PostponedReceipt(receipt) => write!(f, "Postponed receipt {:?}", receipt), + StateRecord::DelayedReceipt(receipt) => write!(f, "Delayed receipt {:?}", receipt), + } + } +} + +fn to_printable(blob: &[u8]) -> String { + if blob.len() > 60 { + format!("{} bytes, hash: {}", blob.len(), hash(blob)) + } else { + let ugly = blob.iter().any(|&x| x < b' '); + if ugly { + return format!("0x{}", hex::encode(blob)); + } + match String::from_utf8(blob.to_vec()) { + Ok(v) => v, + Err(_e) => format!("0x{}", hex::encode(blob)), + } + } +} + +pub fn state_record_to_account_id(state_record: &StateRecord) -> &AccountId { + match state_record { + StateRecord::Account { account_id, .. } + | StateRecord::AccessKey { account_id, .. } + | StateRecord::Contract { account_id, .. } + | StateRecord::ReceivedData { account_id, .. } + | StateRecord::Data { account_id, .. } => account_id, + StateRecord::PostponedReceipt(receipt) | StateRecord::DelayedReceipt(receipt) => { + &receipt.receiver_id + } + } +} + +pub fn is_contract_code_key(key: &[u8]) -> bool { + debug_assert!(!key.is_empty()); + key[0] == col::CONTRACT_CODE +} diff --git a/core/primitives/src/state_sync.rs b/core/primitives/src/state_sync.rs new file mode 100644 index 000000000..e35c8d370 --- /dev/null +++ b/core/primitives/src/state_sync.rs @@ -0,0 +1,294 @@ +use crate::hash::CryptoHash; +use crate::merkle::MerklePath; +use crate::sharding::{ + ReceiptProof, ShardChunk, ShardChunkHeader, ShardChunkHeaderV1, ShardChunkV1, +}; +use crate::types::{BlockHeight, EpochId, ShardId, StateRoot, StateRootNode}; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_primitives_core::types::EpochHeight; +use std::sync::Arc; + +#[derive(PartialEq, Eq, Clone, Debug, BorshSerialize, BorshDeserialize)] +pub struct ReceiptProofResponse(pub CryptoHash, pub Arc>); + +#[derive(PartialEq, Eq, Clone, Debug, BorshSerialize, BorshDeserialize)] +pub struct RootProof(pub CryptoHash, pub MerklePath); + +#[derive(PartialEq, Eq, Clone, Debug, BorshSerialize, BorshDeserialize)] +pub struct StateHeaderKey(pub ShardId, pub CryptoHash); + +#[derive(PartialEq, Eq, Clone, Debug, BorshSerialize, BorshDeserialize)] +pub struct StatePartKey(pub CryptoHash, pub ShardId, pub u64 /* PartId */); + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct ShardStateSyncResponseHeaderV1 { + pub chunk: ShardChunkV1, + pub chunk_proof: MerklePath, + pub prev_chunk_header: Option, + pub prev_chunk_proof: Option, + pub incoming_receipts_proofs: Vec, + pub root_proofs: Vec>, + pub state_root_node: StateRootNode, +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct ShardStateSyncResponseHeaderV2 { + pub chunk: ShardChunk, + pub chunk_proof: MerklePath, + pub prev_chunk_header: Option, + pub prev_chunk_proof: Option, + pub incoming_receipts_proofs: Vec, + pub root_proofs: Vec>, + pub state_root_node: StateRootNode, +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub enum CachedParts { + AllParts, + NoParts, + /// Represents a subset of parts cached. + /// Can represent both NoParts and AllParts, but in those cases use the + /// corresponding enum values for efficiency. + BitArray(BitArray), +} + +/// Represents an array of boolean values in a compact form. +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct BitArray { + data: Vec, + capacity: u64, +} + +impl BitArray { + pub fn new(capacity: u64) -> Self { + let num_bytes = (capacity + 7) / 8; + Self { data: vec![0; num_bytes as usize], capacity } + } + + pub fn set_bit(&mut self, bit: u64) { + assert!(bit < self.capacity); + self.data[(bit / 8) as usize] |= 1 << (bit % 8); + } +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub enum ShardStateSyncResponseHeader { + V1(ShardStateSyncResponseHeaderV1), + V2(ShardStateSyncResponseHeaderV2), +} + +impl ShardStateSyncResponseHeader { + #[inline] + pub fn take_chunk(self) -> ShardChunk { + match self { + Self::V1(header) => ShardChunk::V1(header.chunk), + Self::V2(header) => header.chunk, + } + } + + #[inline] + pub fn cloned_chunk(&self) -> ShardChunk { + match self { + Self::V1(header) => ShardChunk::V1(header.chunk.clone()), + Self::V2(header) => header.chunk.clone(), + } + } + + #[inline] + pub fn cloned_prev_chunk_header(&self) -> Option { + match self { + Self::V1(header) => header.prev_chunk_header.clone().map(ShardChunkHeader::V1), + Self::V2(header) => header.prev_chunk_header.clone(), + } + } + + #[inline] + pub fn chunk_height_included(&self) -> BlockHeight { + match self { + Self::V1(header) => header.chunk.header.height_included, + Self::V2(header) => header.chunk.height_included(), + } + } + + #[inline] + pub fn chunk_prev_state_root(&self) -> StateRoot { + match self { + Self::V1(header) => header.chunk.header.inner.prev_state_root, + Self::V2(header) => header.chunk.prev_state_root(), + } + } + + #[inline] + pub fn chunk_proof(&self) -> &MerklePath { + match self { + Self::V1(header) => &header.chunk_proof, + Self::V2(header) => &header.chunk_proof, + } + } + + #[inline] + pub fn prev_chunk_proof(&self) -> &Option { + match self { + Self::V1(header) => &header.prev_chunk_proof, + Self::V2(header) => &header.prev_chunk_proof, + } + } + + #[inline] + pub fn incoming_receipts_proofs(&self) -> &[ReceiptProofResponse] { + match self { + Self::V1(header) => &header.incoming_receipts_proofs, + Self::V2(header) => &header.incoming_receipts_proofs, + } + } + + #[inline] + pub fn root_proofs(&self) -> &[Vec] { + match self { + Self::V1(header) => &header.root_proofs, + Self::V2(header) => &header.root_proofs, + } + } + + #[inline] + pub fn state_root_node(&self) -> &StateRootNode { + match self { + Self::V1(header) => &header.state_root_node, + Self::V2(header) => &header.state_root_node, + } + } + + pub fn num_state_parts(&self) -> u64 { + get_num_state_parts(self.state_root_node().memory_usage) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct ShardStateSyncResponseV1 { + pub header: Option, + pub part: Option<(u64, Vec)>, +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct ShardStateSyncResponseV2 { + pub header: Option, + pub part: Option<(u64, Vec)>, +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct ShardStateSyncResponseV3 { + pub header: Option, + pub part: Option<(u64, Vec)>, + /// Parts that can be provided **cheaply**. + // Can be `None` only if both `header` and `part` are `None`. + pub cached_parts: Option, + /// Whether the node can provide parts for this epoch of this shard. + /// Assumes that a node can either provide all state parts or no state parts. + pub can_generate: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub enum ShardStateSyncResponse { + V1(ShardStateSyncResponseV1), + V2(ShardStateSyncResponseV2), + V3(ShardStateSyncResponseV3), +} + +impl ShardStateSyncResponse { + pub fn part_id(&self) -> Option { + match self { + Self::V1(response) => response.part_id(), + Self::V2(response) => response.part.as_ref().map(|(part_id, _)| *part_id), + Self::V3(response) => response.part.as_ref().map(|(part_id, _)| *part_id), + } + } + + pub fn take_header(self) -> Option { + match self { + Self::V1(response) => response.header.map(ShardStateSyncResponseHeader::V1), + Self::V2(response) => response.header.map(ShardStateSyncResponseHeader::V2), + Self::V3(response) => response.header.map(ShardStateSyncResponseHeader::V2), + } + } + + pub fn part(&self) -> &Option<(u64, Vec)> { + match self { + Self::V1(response) => &response.part, + Self::V2(response) => &response.part, + Self::V3(response) => &response.part, + } + } + + pub fn take_part(self) -> Option<(u64, Vec)> { + match self { + Self::V1(response) => response.part, + Self::V2(response) => response.part, + Self::V3(response) => response.part, + } + } + + pub fn can_generate(&self) -> bool { + match self { + Self::V1(_response) => false, + Self::V2(_response) => false, + Self::V3(response) => response.can_generate, + } + } + + pub fn cached_parts(&self) -> &Option { + match self { + Self::V1(_response) => &None, + Self::V2(_response) => &None, + Self::V3(response) => &response.cached_parts, + } + } +} + +impl ShardStateSyncResponseV1 { + pub fn part_id(&self) -> Option { + self.part.as_ref().map(|(part_id, _)| *part_id) + } +} + +pub const STATE_PART_MEMORY_LIMIT: bytesize::ByteSize = bytesize::ByteSize(30 * bytesize::MIB); + +pub fn get_num_state_parts(memory_usage: u64) -> u64 { + (memory_usage + STATE_PART_MEMORY_LIMIT.as_u64() - 1) / STATE_PART_MEMORY_LIMIT.as_u64() +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone)] +/// Represents the progress of dumps state of a shard. +pub enum StateSyncDumpProgress { + /// Represents two cases: + /// * An epoch dump is complete + /// * The node is running its first epoch and there is nothing to dump. + AllDumped { + /// The dumped state corresponds to the state at the beginning of the specified epoch. + epoch_id: EpochId, + epoch_height: EpochHeight, + }, + /// Represents the case of an epoch being partially dumped. + InProgress { + /// The dumped state corresponds to the state at the beginning of the specified epoch. + epoch_id: EpochId, + epoch_height: EpochHeight, + /// Block hash of the first block of the epoch. + /// The dumped state corresponds to the state before applying this block. + sync_hash: CryptoHash, + }, +} + +#[cfg(test)] +mod tests { + use crate::state_sync::{get_num_state_parts, STATE_PART_MEMORY_LIMIT}; + + #[test] + fn test_get_num_state_parts() { + assert_eq!(get_num_state_parts(0), 0); + assert_eq!(get_num_state_parts(1), 1); + assert_eq!(get_num_state_parts(STATE_PART_MEMORY_LIMIT.as_u64()), 1); + assert_eq!(get_num_state_parts(STATE_PART_MEMORY_LIMIT.as_u64() + 1), 2); + assert_eq!(get_num_state_parts(STATE_PART_MEMORY_LIMIT.as_u64() * 100), 100); + assert_eq!(get_num_state_parts(STATE_PART_MEMORY_LIMIT.as_u64() * 100 + 1), 101); + } +} diff --git a/core/primitives/src/static_clock.rs b/core/primitives/src/static_clock.rs new file mode 100644 index 000000000..e621ff955 --- /dev/null +++ b/core/primitives/src/static_clock.rs @@ -0,0 +1,181 @@ +/// Provides structs used for getting time. +/// +/// DEPRECATION WARNING: This module is DEPRECATED. Use `unc_primitives::time` instead. +/// +/// WARNING WARNING WARNING +/// WARNING WARNING WARNING +/// Use at your own risk. The implementation is not complete, we have a places in code not mocked properly. +/// For example, it's possible to call `::elapsed()` on `Instant` returned by `Instant::now()`. +/// We use that that function, throughout the code, and the current mocking of `Instant` is not done properly. +/// +/// Example: +/// ```rust, ignore +/// fn some_production_function() { +/// let start = Clock::instant(); +/// // some computation +/// let end = Clock::instant(); +/// assert!(end.duration_since(start) == Duration::from_secs(1)); +/// } +/// +/// Test: +/// fn test() { +/// let mock_clock_guard = MockClockGuard::default(); +/// mock_clock_guard.add_instant(Instant::now()); +/// mock_clock_guard.add_instant(Instant::now() + Duration::from_secs(1)); +/// +/// // This won't crash anymore as long as mock works. +/// some_production_function(); +/// } +/// ``` +use chrono; +use chrono::DateTime; +use chrono::Utc; +use std::cell::RefCell; +use std::collections::VecDeque; +use std::default::Default; +use std::time::Instant; + +#[derive(Default)] +struct MockClockPerState { + /// List of timestamps, we will return one timestamp for each call of `Clock::utc()`. + utc_list: VecDeque>, + /// List of timestamps, we will return one timestamp to each call of `Clock::instant()`. + instant_list: VecDeque, + /// Number of times `Clock::utc()` method was called since we started mocking. + utc_call_count: u64, + /// Number of times `Clock::instant()` method was called since we started mocking. + instant_call_count: u64, +} + +/// Stores the mocking state. +#[derive(Default)] +struct MockClockPerThread { + mock: Option, +} + +impl MockClockPerThread { + fn with(f: F) -> T + where + F: FnOnce(&mut MockClockPerThread) -> T, + { + thread_local! { + static INSTANCE: RefCell = RefCell::default() + } + INSTANCE.with(|it| f(&mut *it.borrow_mut())) + } +} + +pub struct MockClockGuard {} + +impl MockClockGuard { + /// Adds timestamp to queue, it will be returned in `Self::utc()`. + pub fn add_utc(&self, mock_date: DateTime) { + MockClockPerThread::with(|clock| match &mut clock.mock { + Some(clock) => { + clock.utc_list.push_back(mock_date); + } + None => { + panic!("Use MockClockGuard in your test"); + } + }); + } + + /// Adds timestamp to queue, it will be returned in `Self::utc()`. + pub fn add_instant(&self, mock_date: Instant) { + MockClockPerThread::with(|clock| match &mut clock.mock { + Some(clock) => { + clock.instant_list.push_back(mock_date); + } + None => { + panic!("Use MockClockGuard in your test"); + } + }); + } + + /// Returns number of calls to `Self::utc` since `Self::mock()` was called. + pub fn utc_call_count(&self) -> u64 { + MockClockPerThread::with(|clock| match &mut clock.mock { + Some(clock) => clock.utc_call_count, + None => { + panic!("Use MockClockGuard in your test"); + } + }) + } + + /// Returns number of calls to `Self::instant` since `Self::mock()` was called. + pub fn instant_call_count(&self) -> u64 { + MockClockPerThread::with(|clock| match &mut clock.mock { + Some(clock) => clock.instant_call_count, + None => { + panic!("Use MockClockGuard in your test"); + } + }) + } +} + +impl Default for MockClockGuard { + fn default() -> Self { + StaticClock::set_mock(); + Self {} + } +} + +impl Drop for MockClockGuard { + fn drop(&mut self) { + StaticClock::reset(); + } +} + +/// Depreciated. +pub struct StaticClock {} + +impl StaticClock { + /// Turns the mocking logic on. + fn set_mock() { + MockClockPerThread::with(|clock| { + assert!(clock.mock.is_none()); + clock.mock = Some(MockClockPerState::default()) + }) + } + + /// Resets mocks to default state. + fn reset() { + MockClockPerThread::with(|clock| clock.mock = None); + } + + /// This methods gets current time as `std::Instant` + /// unless it's mocked, then returns time added by `Self::add_utc(...)` + pub fn instant() -> Instant { + MockClockPerThread::with(|clock| match &mut clock.mock { + Some(clock) => { + clock.instant_call_count += 1; + let x = clock.instant_list.pop_front(); + match x { + Some(t) => t, + None => { + panic!("Mock clock run out of samples"); + } + } + } + None => Instant::now(), + }) + } + + /// This methods gets current time as `std::Instant` + /// unless it's mocked, then returns time added by `Self::add_instant(...)` + pub fn utc() -> DateTime { + MockClockPerThread::with(|clock| match &mut clock.mock { + Some(clock) => { + clock.utc_call_count += 1; + let x = clock.utc_list.pop_front(); + match x { + Some(t) => t, + None => { + panic!("Mock clock run out of samples"); + } + } + } + None => chrono::Utc::now(), + }) + } +} diff --git a/core/primitives/src/telemetry.rs b/core/primitives/src/telemetry.rs new file mode 100644 index 000000000..74fbf333a --- /dev/null +++ b/core/primitives/src/telemetry.rs @@ -0,0 +1,45 @@ +//! Types for telemetry reporting. Can be received by any telemetry dashboard to display +//! node count and their status across the network. +use crate::types::AccountId; +use crate::types::BlockHeight; +use unc_primitives_core::hash::CryptoHash; + +#[derive(serde::Serialize, Debug)] +pub struct TelemetryAgentInfo { + pub name: String, + pub version: String, + pub build: String, +} + +#[derive(serde::Serialize, Debug)] +pub struct TelemetrySystemInfo { + pub bandwidth_download: u64, + pub bandwidth_upload: u64, + pub cpu_usage: f32, + pub memory_usage: u64, + pub boot_time_seconds: i64, +} + +#[derive(serde::Serialize, Debug)] +pub struct TelemetryChainInfo { + pub node_id: String, + pub account_id: Option, + pub is_validator: bool, + pub status: String, + pub latest_block_hash: CryptoHash, + pub latest_block_height: BlockHeight, + pub num_peers: usize, + pub block_production_tracking_delay: f64, + pub min_block_production_delay: f64, + pub max_block_production_delay: f64, + pub max_block_wait_delay: f64, +} + +#[derive(serde::Serialize, Debug)] +pub struct TelemetryInfo { + pub agent: TelemetryAgentInfo, + pub system: TelemetrySystemInfo, + pub chain: TelemetryChainInfo, + // Extra telemetry information that will be ignored by the explorer frontend. + pub extra_info: String, +} diff --git a/core/primitives/src/test_utils.rs b/core/primitives/src/test_utils.rs new file mode 100644 index 000000000..486956560 --- /dev/null +++ b/core/primitives/src/test_utils.rs @@ -0,0 +1,617 @@ +use std::collections::HashMap; +use std::sync::Arc; + +use unc_crypto::{EmptySigner, InMemorySigner, KeyType, PublicKey, SecretKey, Signature, Signer}; +use unc_primitives_core::account::id::AccountIdRef; +use unc_primitives_core::types::{Power, ProtocolVersion}; + +use crate::account::{AccessKey, AccessKeyPermission, Account}; +use crate::block::Block; +use crate::block::BlockV3; +use crate::block_header::BlockHeader; +use crate::errors::EpochError; +use crate::hash::CryptoHash; +use crate::merkle::PartialMerkleTree; +use crate::num_rational::Ratio; +use crate::sharding::{ShardChunkHeader, ShardChunkHeaderV3}; +use crate::transaction::{ + Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, + DeployContractAction, FunctionCallAction, SignedTransaction, StakeAction, Transaction, + TransferAction, +}; +use crate::types::{AccountId, Balance, EpochId, EpochInfoProvider, Gas, Nonce}; +use crate::validator_signer::{InMemoryValidatorSigner, ValidatorSigner}; +use crate::version::PROTOCOL_VERSION; +use crate::views::{ExecutionStatusView, FinalExecutionOutcomeView, FinalExecutionStatus}; + +pub fn account_new(amount: Balance, code_hash: CryptoHash) -> Account { + Account::new(amount, 0, 0, code_hash, std::mem::size_of::() as u64) +} + +impl Transaction { + pub fn new( + signer_id: AccountId, + public_key: PublicKey, + receiver_id: AccountId, + nonce: Nonce, + block_hash: CryptoHash, + ) -> Self { + Self { signer_id, public_key, nonce, receiver_id, block_hash, actions: vec![] } + } + + pub fn sign(self, signer: &dyn Signer) -> SignedTransaction { + let signature = signer.sign(self.get_hash_and_size().0.as_ref()); + SignedTransaction::new(signature, self) + } + + pub fn create_account(mut self) -> Self { + self.actions.push(Action::CreateAccount(CreateAccountAction {})); + self + } + + pub fn deploy_contract(mut self, code: Vec) -> Self { + self.actions.push(Action::DeployContract(DeployContractAction { code })); + self + } + + pub fn function_call( + mut self, + method_name: String, + args: Vec, + gas: Gas, + deposit: Balance, + ) -> Self { + self.actions.push(Action::FunctionCall(Box::new(FunctionCallAction { + method_name, + args, + gas, + deposit, + }))); + self + } + + pub fn transfer(mut self, deposit: Balance) -> Self { + self.actions.push(Action::Transfer(TransferAction { deposit })); + self + } + + pub fn stake(mut self, stake: Balance, public_key: PublicKey) -> Self { + self.actions.push(Action::Stake(Box::new(StakeAction { stake, public_key }))); + self + } + pub fn add_key(mut self, public_key: PublicKey, access_key: AccessKey) -> Self { + self.actions.push(Action::AddKey(Box::new(AddKeyAction { public_key, access_key }))); + self + } + + pub fn delete_key(mut self, public_key: PublicKey) -> Self { + self.actions.push(Action::DeleteKey(Box::new(DeleteKeyAction { public_key }))); + self + } + + pub fn delete_account(mut self, beneficiary_id: AccountId) -> Self { + self.actions.push(Action::DeleteAccount(DeleteAccountAction { beneficiary_id })); + self + } +} + +impl SignedTransaction { + pub fn from_actions( + nonce: Nonce, + signer_id: AccountId, + receiver_id: AccountId, + signer: &dyn Signer, + actions: Vec, + block_hash: CryptoHash, + ) -> Self { + Transaction { + nonce, + signer_id, + public_key: signer.public_key(), + receiver_id, + block_hash, + actions, + } + .sign(signer) + } + + pub fn send_money( + nonce: Nonce, + signer_id: AccountId, + receiver_id: AccountId, + signer: &dyn Signer, + deposit: Balance, + block_hash: CryptoHash, + ) -> Self { + Self::from_actions( + nonce, + signer_id, + receiver_id, + signer, + vec![Action::Transfer(TransferAction { deposit })], + block_hash, + ) + } + + pub fn stake( + nonce: Nonce, + signer_id: AccountId, + signer: &dyn Signer, + stake: Balance, + public_key: PublicKey, + block_hash: CryptoHash, + ) -> Self { + Self::from_actions( + nonce, + signer_id.clone(), + signer_id, + signer, + vec![Action::Stake(Box::new(StakeAction { stake, public_key }))], + block_hash, + ) + } + + pub fn create_account( + nonce: Nonce, + originator: AccountId, + new_account_id: AccountId, + amount: Balance, + public_key: PublicKey, + signer: &dyn Signer, + block_hash: CryptoHash, + ) -> Self { + Self::from_actions( + nonce, + originator, + new_account_id, + signer, + vec![ + Action::CreateAccount(CreateAccountAction {}), + Action::AddKey(Box::new(AddKeyAction { + public_key, + access_key: AccessKey { nonce: 0, permission: AccessKeyPermission::FullAccess }, + })), + Action::Transfer(TransferAction { deposit: amount }), + ], + block_hash, + ) + } + + pub fn create_contract( + nonce: Nonce, + originator: AccountId, + new_account_id: AccountId, + code: Vec, + amount: Balance, + public_key: PublicKey, + signer: &dyn Signer, + block_hash: CryptoHash, + ) -> Self { + Self::from_actions( + nonce, + originator, + new_account_id, + signer, + vec![ + Action::CreateAccount(CreateAccountAction {}), + Action::AddKey(Box::new(AddKeyAction { + public_key, + access_key: AccessKey { nonce: 0, permission: AccessKeyPermission::FullAccess }, + })), + Action::Transfer(TransferAction { deposit: amount }), + Action::DeployContract(DeployContractAction { code }), + ], + block_hash, + ) + } + + pub fn call( + nonce: Nonce, + signer_id: AccountId, + receiver_id: AccountId, + signer: &dyn Signer, + deposit: Balance, + method_name: String, + args: Vec, + gas: Gas, + block_hash: CryptoHash, + ) -> Self { + Self::from_actions( + nonce, + signer_id, + receiver_id, + signer, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + args, + method_name, + gas, + deposit, + }))], + block_hash, + ) + } + + pub fn delete_account( + nonce: Nonce, + signer_id: AccountId, + receiver_id: AccountId, + beneficiary_id: AccountId, + signer: &dyn Signer, + block_hash: CryptoHash, + ) -> Self { + Self::from_actions( + nonce, + signer_id, + receiver_id, + signer, + vec![Action::DeleteAccount(DeleteAccountAction { beneficiary_id })], + block_hash, + ) + } + + pub fn empty(block_hash: CryptoHash) -> Self { + Self::from_actions( + 0, + "test".parse().unwrap(), + "test".parse().unwrap(), + &EmptySigner {}, + vec![], + block_hash, + ) + } +} + +impl Block { + pub fn get_mut(&mut self) -> &mut BlockV3 { + match self { + Block::BlockV1(_) | Block::BlockV2(_) => { + panic!("older block version should not appear in tests") + } + Block::BlockV3(block) => Arc::make_mut(block), + } + } +} + +impl BlockHeader { + pub fn get_mut(&mut self) -> &mut crate::block_header::BlockHeaderV4 { + match self { + BlockHeader::BlockHeaderV1(_) + | BlockHeader::BlockHeaderV2(_) + | BlockHeader::BlockHeaderV3(_) => { + panic!("old header should not appear in tests") + } + BlockHeader::BlockHeaderV4(header) => Arc::make_mut(header), + } + } + + pub fn set_latest_protocol_version(&mut self, latest_protocol_version: ProtocolVersion) { + match self { + BlockHeader::BlockHeaderV1(header) => { + let header = Arc::make_mut(header); + header.inner_rest.latest_protocol_version = latest_protocol_version; + } + BlockHeader::BlockHeaderV2(header) => { + let header = Arc::make_mut(header); + header.inner_rest.latest_protocol_version = latest_protocol_version; + } + BlockHeader::BlockHeaderV3(header) => { + let header = Arc::make_mut(header); + header.inner_rest.latest_protocol_version = latest_protocol_version; + } + BlockHeader::BlockHeaderV4(header) => { + let header = Arc::make_mut(header); + header.inner_rest.latest_protocol_version = latest_protocol_version; + } + } + } + + pub fn resign(&mut self, signer: &dyn ValidatorSigner) { + let (hash, signature) = signer.sign_block_header_parts( + *self.prev_hash(), + &self.inner_lite_bytes(), + &self.inner_rest_bytes(), + ); + match self { + BlockHeader::BlockHeaderV1(header) => { + let header = Arc::make_mut(header); + header.hash = hash; + header.signature = signature; + } + BlockHeader::BlockHeaderV2(header) => { + let header = Arc::make_mut(header); + header.hash = hash; + header.signature = signature; + } + BlockHeader::BlockHeaderV3(header) => { + let header = Arc::make_mut(header); + header.hash = hash; + header.signature = signature; + } + BlockHeader::BlockHeaderV4(header) => { + let header = Arc::make_mut(header); + header.hash = hash; + header.signature = signature; + } + } + } +} + +impl ShardChunkHeader { + pub fn get_mut(&mut self) -> &mut ShardChunkHeaderV3 { + match self { + ShardChunkHeader::V1(_) | ShardChunkHeader::V2(_) => { + unreachable!("old header should not appear in tests") + } + ShardChunkHeader::V3(chunk) => chunk, + } + } +} +/// Builder class for blocks to make testing easier. +/// # Examples +/// +/// // TODO(mm-near): change it to doc-tested code once we have easy way to create a genesis block. +/// let signer = EmptyValidatorSigner::default(); +/// let test_block = test_utils::TestBlockBuilder::new(prev, signer).height(33).build(); +/// + +pub struct TestBlockBuilder { + prev: Block, + signer: Arc, + height: u64, + epoch_id: EpochId, + next_epoch_id: EpochId, + next_bp_hash: CryptoHash, + approvals: Vec>>, + block_merkle_root: CryptoHash, +} + +impl TestBlockBuilder { + pub fn new(prev: &Block, signer: Arc) -> Self { + let mut tree = PartialMerkleTree::default(); + tree.insert(*prev.hash()); + + Self { + prev: prev.clone(), + signer: signer.clone(), + height: prev.header().height() + 1, + epoch_id: prev.header().epoch_id().clone(), + next_epoch_id: if prev.header().prev_hash() == &CryptoHash::default() { + EpochId(*prev.hash()) + } else { + prev.header().next_epoch_id().clone() + }, + next_bp_hash: *prev.header().next_bp_hash(), + approvals: vec![], + block_merkle_root: tree.root(), + } + } + pub fn height(mut self, height: u64) -> Self { + self.height = height; + self + } + pub fn epoch_id(mut self, epoch_id: EpochId) -> Self { + self.epoch_id = epoch_id; + self + } + pub fn next_epoch_id(mut self, next_epoch_id: EpochId) -> Self { + self.next_epoch_id = next_epoch_id; + self + } + pub fn next_bp_hash(mut self, next_bp_hash: CryptoHash) -> Self { + self.next_bp_hash = next_bp_hash; + self + } + pub fn approvals(mut self, approvals: Vec>>) -> Self { + self.approvals = approvals; + self + } + + /// Updates the merkle tree by adding the previous hash, and updates the new block's merkle_root. + pub fn block_merkle_tree(mut self, block_merkle_tree: &mut PartialMerkleTree) -> Self { + block_merkle_tree.insert(*self.prev.hash()); + self.block_merkle_root = block_merkle_tree.root(); + self + } + + pub fn build(self) -> Block { + tracing::debug!(target: "test", height=self.height, ?self.epoch_id, "produce block"); + Block::produce( + PROTOCOL_VERSION, + PROTOCOL_VERSION, + self.prev.header(), + self.height, + self.prev.header().block_ordinal() + 1, + self.prev.chunks().iter().cloned().collect(), + self.epoch_id, + self.next_epoch_id, + None, + self.approvals, + Ratio::new(0, 1), + 0, + 0, + Some(0), + vec![], + vec![], + self.signer.as_ref(), + self.next_bp_hash, + self.block_merkle_root, + None, + ) + } +} + +impl Block { + pub fn mut_header(&mut self) -> &mut BlockHeader { + match self { + Block::BlockV1(block) => { + let block = Arc::make_mut(block); + &mut block.header + } + Block::BlockV2(block) => { + let block = Arc::make_mut(block); + &mut block.header + } + Block::BlockV3(block) => { + let block = Arc::make_mut(block); + &mut block.header + } + } + } + + pub fn set_chunks(&mut self, chunks: Vec) { + match self { + Block::BlockV1(block) => { + let block = Arc::make_mut(block); + let legacy_chunks = chunks + .into_iter() + .map(|chunk| match chunk { + ShardChunkHeader::V1(header) => header, + ShardChunkHeader::V2(_) => { + panic!("Attempted to set V1 block chunks with V2") + } + ShardChunkHeader::V3(_) => { + panic!("Attempted to set V1 block chunks with V3") + } + }) + .collect(); + block.chunks = legacy_chunks; + } + Block::BlockV2(block) => { + let block = Arc::make_mut(block); + block.chunks = chunks; + } + Block::BlockV3(block) => { + let block = Arc::make_mut(block); + block.body.chunks = chunks; + } + } + } +} + +#[derive(Default)] +pub struct MockEpochInfoProvider { + pub validators: HashMap, +} + +impl MockEpochInfoProvider { + pub fn new(validators: impl Iterator) -> Self { + MockEpochInfoProvider { validators: validators.collect() } + } +} + +impl EpochInfoProvider for MockEpochInfoProvider { + fn validator_power( + &self, + _epoch_id: &EpochId, + _last_block_hash: &CryptoHash, + account_id: &AccountId, + ) -> Result, EpochError> { + + if let Some((power, _balance)) = self.validators.get(account_id) { + Ok(Some(power).cloned()) + } else { + Ok(None) + } + } + + fn validator_total_power( + &self, + _epoch_id: &EpochId, + _last_block_hash: &CryptoHash, + ) -> Result { + + let total_power: Power = self.validators.values().map(|(power, _)| power).sum(); + Ok(total_power) + } + + fn minimum_power(&self, _prev_block_hash: &CryptoHash) -> Result { + Ok(0) + } + + fn validator_frozen( + &self, + _epoch_id: &EpochId, + _last_block_hash: &CryptoHash, + account_id: &AccountId, + ) -> Result, EpochError> { + if let Some((_power, balance)) = self.validators.get(account_id) { + Ok(Some(balance).cloned()) + } else { + Ok(None) + } + } + + fn validator_total_frozen( + &self, + _epoch_id: &EpochId, + _last_block_hash: &CryptoHash, + ) -> Result { + let total_frozen: Balance = self.validators.values().map(|(_, frozen)| frozen).sum(); + Ok(total_frozen) + } + + fn minimum_frozen(&self, _prev_block_hash: &CryptoHash) -> Result { + Ok(0) + } +} + +/// Encode array of `u64` to be passed as a smart contract argument. +pub fn encode(xs: &[u64]) -> Vec { + xs.iter().flat_map(|it| it.to_le_bytes()).collect() +} + +// Helper function that creates a new signer for a given account, that uses the account name as seed. +// Should be used only in tests. +pub fn create_test_signer(account_name: &str) -> InMemoryValidatorSigner { + InMemoryValidatorSigner::from_seed( + account_name.parse().unwrap(), + KeyType::ED25519, + account_name, + ) +} + +/// Helper function that creates a new signer for a given account, that uses the account name as seed. +/// +/// This also works for predefined implicit accounts, where the signer will use the implicit key. +/// +/// Should be used only in tests. +pub fn create_user_test_signer(account_name: &AccountIdRef) -> InMemorySigner { + let account_id = account_name.to_owned(); + if account_id == unc_implicit_test_account() { + InMemorySigner::from_secret_key(account_id, unc_implicit_test_account_secret()) + } else { + InMemorySigner::from_seed(account_id, KeyType::ED25519, account_name.as_str()) + } +} + +/// A fixed NEAR-implicit account for which tests can know the private key. +pub fn unc_implicit_test_account() -> AccountId { + "061b1dd17603213b00e1a1e53ba060ad427cef4887bd34a5e0ef09010af23b0a".parse().unwrap() +} + +/// Private key for the fixed NEAR-implicit test account. +pub fn unc_implicit_test_account_secret() -> SecretKey { + "ed25519:5roj6k68kvZu3UEJFyXSfjdKGrodgZUfFLZFpzYXWtESNsLWhYrq3JGi4YpqeVKuw1m9R2TEHjfgWT1fjUqB1DNy".parse().unwrap() +} + +/// A fixed ETH-implicit account. +pub fn eth_implicit_test_account() -> AccountId { + "0x96791e923f8cf697ad9c3290f2c9059f0231b24c".parse().unwrap() +} + +impl FinalExecutionOutcomeView { + #[track_caller] + /// Check transaction and all transitive receipts for success status. + pub fn assert_success(&self) { + assert!(matches!(self.status, FinalExecutionStatus::SuccessValue(_))); + for (i, receipt) in self.receipts_outcome.iter().enumerate() { + assert!( + matches!( + receipt.outcome.status, + ExecutionStatusView::SuccessReceiptId(_) | ExecutionStatusView::SuccessValue(_), + ), + "receipt #{i} failed: {receipt:?}", + ); + } + } +} diff --git a/core/primitives/src/transaction.rs b/core/primitives/src/transaction.rs new file mode 100644 index 000000000..0df2eac7e --- /dev/null +++ b/core/primitives/src/transaction.rs @@ -0,0 +1,418 @@ +use crate::errors::TxExecutionError; +use crate::hash::{hash, CryptoHash}; +use crate::merkle::MerklePath; +use crate::types::{AccountId, Balance, Gas, Nonce}; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_crypto::{PublicKey, Signature}; +use unc_fmt::{AbbrBytes, Slice}; +use unc_primitives_core::serialize::{from_base64, to_base64}; +use unc_primitives_core::types::Compute; +use unc_vm_runner::{ProfileDataV2, ProfileDataV3}; +use serde::de::Error as DecodeError; +use serde::ser::Error as EncodeError; +use std::borrow::Borrow; +use std::fmt; +use std::hash::{Hash, Hasher}; + +pub use crate::action::{ + Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, + DeployContractAction, FunctionCallAction, StakeAction, TransferAction, + RegisterRsa2048KeysAction, CreateRsa2048ChallengeAction, +}; + +pub type LogEntry = String; + +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, PartialEq, Eq, Debug, Clone)] +pub struct Transaction { + /// An account on which behalf transaction is signed + pub signer_id: AccountId, + /// A public key of the access key which was used to sign an account. + /// Access key holds permissions for calling certain kinds of actions. + pub public_key: PublicKey, + /// Nonce is used to determine order of transaction in the pool. + /// It increments for a combination of `signer_id` and `public_key` + pub nonce: Nonce, + /// Receiver account for this transaction + pub receiver_id: AccountId, + /// The hash of the block in the blockchain on top of which the given transaction is valid + pub block_hash: CryptoHash, + /// A list of actions to be applied + pub actions: Vec, +} + +impl Transaction { + /// Computes a hash of the transaction for signing and size of serialized transaction + pub fn get_hash_and_size(&self) -> (CryptoHash, u64) { + let bytes = borsh::to_vec(&self).expect("Failed to deserialize"); + (hash(&bytes), bytes.len() as u64) + } +} + +#[derive(BorshSerialize, BorshDeserialize, Eq, Debug, Clone)] +#[borsh(init=init)] +pub struct SignedTransaction { + pub transaction: Transaction, + pub signature: Signature, + #[borsh(skip)] + hash: CryptoHash, + #[borsh(skip)] + size: u64, +} + +impl SignedTransaction { + pub fn new(signature: Signature, transaction: Transaction) -> Self { + let mut signed_tx = + Self { signature, transaction, hash: CryptoHash::default(), size: u64::default() }; + signed_tx.init(); + signed_tx + } + + pub fn init(&mut self) { + let (hash, size) = self.transaction.get_hash_and_size(); + self.hash = hash; + self.size = size; + } + + pub fn get_hash(&self) -> CryptoHash { + self.hash + } + + pub fn get_size(&self) -> u64 { + self.size + } +} + +impl Hash for SignedTransaction { + fn hash(&self, state: &mut H) { + self.hash.hash(state) + } +} + +impl PartialEq for SignedTransaction { + fn eq(&self, other: &SignedTransaction) -> bool { + self.hash == other.hash && self.signature == other.signature + } +} + +impl Borrow for SignedTransaction { + fn borrow(&self) -> &CryptoHash { + &self.hash + } +} + +impl serde::Serialize for SignedTransaction { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let signed_tx_borsh = borsh::to_vec(self).map_err(|err| { + S::Error::custom(&format!("the value could not be borsh encoded due to: {}", err)) + })?; + let signed_tx_base64 = to_base64(&signed_tx_borsh); + serializer.serialize_str(&signed_tx_base64) + } +} + +impl<'de> serde::Deserialize<'de> for SignedTransaction { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let signed_tx_base64 = ::deserialize(deserializer)?; + let signed_tx_borsh = from_base64(&signed_tx_base64).map_err(|err| { + D::Error::custom(&format!("the value could not decoded from base64 due to: {}", err)) + })?; + borsh::from_slice::(&signed_tx_borsh).map_err(|err| { + D::Error::custom(&format!("the value could not decoded from borsh due to: {}", err)) + }) + } +} + +/// The status of execution for a transaction or a receipt. +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Default)] +pub enum ExecutionStatus { + /// The execution is pending or unknown. + #[default] + Unknown, + /// The execution has failed with the given execution error. + Failure(TxExecutionError), + /// The final action succeeded and returned some value or an empty vec. + SuccessValue(Vec), + /// The final action of the receipt returned a promise or the signed transaction was converted + /// to a receipt. Contains the receipt_id of the generated receipt. + SuccessReceiptId(CryptoHash), +} + +impl fmt::Debug for ExecutionStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ExecutionStatus::Unknown => f.write_str("Unknown"), + ExecutionStatus::Failure(e) => f.write_fmt(format_args!("Failure({})", e)), + ExecutionStatus::SuccessValue(v) => { + f.write_fmt(format_args!("SuccessValue({})", AbbrBytes(v))) + } + ExecutionStatus::SuccessReceiptId(receipt_id) => { + f.write_fmt(format_args!("SuccessReceiptId({})", receipt_id)) + } + } + } +} + +/// ExecutionOutcome for proof. Excludes logs and metadata +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Clone)] +pub struct PartialExecutionOutcome { + pub receipt_ids: Vec, + pub gas_burnt: Gas, + pub tokens_burnt: Balance, + pub executor_id: AccountId, + pub status: PartialExecutionStatus, +} + +impl From<&ExecutionOutcome> for PartialExecutionOutcome { + fn from(outcome: &ExecutionOutcome) -> Self { + Self { + receipt_ids: outcome.receipt_ids.clone(), + gas_burnt: outcome.gas_burnt, + tokens_burnt: outcome.tokens_burnt, + executor_id: outcome.executor_id.clone(), + status: outcome.status.clone().into(), + } + } +} + +/// ExecutionStatus for proof. Excludes failure debug info. +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Clone)] +pub enum PartialExecutionStatus { + Unknown, + Failure, + SuccessValue(Vec), + SuccessReceiptId(CryptoHash), +} + +impl From for PartialExecutionStatus { + fn from(status: ExecutionStatus) -> PartialExecutionStatus { + match status { + ExecutionStatus::Unknown => PartialExecutionStatus::Unknown, + ExecutionStatus::Failure(_) => PartialExecutionStatus::Failure, + ExecutionStatus::SuccessValue(value) => PartialExecutionStatus::SuccessValue(value), + ExecutionStatus::SuccessReceiptId(id) => PartialExecutionStatus::SuccessReceiptId(id), + } + } +} + +/// Execution outcome for one signed transaction or one receipt. +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Clone, smart_default::SmartDefault, Eq)] +pub struct ExecutionOutcome { + /// Logs from this transaction or receipt. + pub logs: Vec, + /// Receipt IDs generated by this transaction or receipt. + pub receipt_ids: Vec, + /// The amount of the gas burnt by the given transaction or receipt. + pub gas_burnt: Gas, + /// The amount of compute time spent by the given transaction or receipt. + // TODO(#8859): Treat this field in the same way as `gas_burnt`. + // At the moment this field is only set at runtime and is not persisted in the database. + // This means that when execution outcomes are read from the database, this value will not be + // set and any code that attempts to use it will crash. + #[borsh(skip)] + pub compute_usage: Option, + /// The amount of tokens burnt corresponding to the burnt gas amount. + /// This value doesn't always equal to the `gas_burnt` multiplied by the gas price, because + /// the prepaid gas price might be lower than the actual gas price and it creates a deficit. + pub tokens_burnt: Balance, + /// The id of the account on which the execution happens. For transaction this is signer_id, + /// for receipt this is receiver_id. + #[default("test".parse().unwrap())] + pub executor_id: AccountId, + /// Execution status. Contains the result in case of successful execution. + /// NOTE: Should be the latest field since it contains unparsable by light client + /// ExecutionStatus::Failure + pub status: ExecutionStatus, + /// Execution metadata, versioned + pub metadata: ExecutionMetadata, +} + +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Clone, Eq, Debug, Default)] +pub enum ExecutionMetadata { + /// V1: Empty Metadata + #[default] + V1, + /// V2: With ProfileData by legacy `Cost` enum + V2(ProfileDataV2), + /// V3: With ProfileData by gas parameters + V3(Box), +} + +impl fmt::Debug for ExecutionOutcome { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ExecutionOutcome") + .field("logs", &Slice(&self.logs)) + .field("receipt_ids", &Slice(&self.receipt_ids)) + .field("burnt_gas", &self.gas_burnt) + .field("compute_usage", &self.compute_usage.unwrap_or_default()) + .field("tokens_burnt", &self.tokens_burnt) + .field("status", &self.status) + .field("metadata", &self.metadata) + .finish() + } +} + +/// Execution outcome with the identifier. +/// For a signed transaction, the ID is the hash of the transaction. +/// For a receipt, the ID is the receipt ID. +#[derive(PartialEq, Clone, Default, Debug, BorshSerialize, BorshDeserialize, Eq)] +pub struct ExecutionOutcomeWithId { + /// The transaction hash or the receipt ID. + pub id: CryptoHash, + /// Should be the latest field since contains unparsable by light client ExecutionStatus::Failure + pub outcome: ExecutionOutcome, +} + +impl ExecutionOutcomeWithId { + pub fn to_hashes(&self) -> Vec { + let mut result = Vec::with_capacity(2 + self.outcome.logs.len()); + result.push(self.id); + result.push(CryptoHash::hash_borsh(PartialExecutionOutcome::from(&self.outcome))); + result.extend(self.outcome.logs.iter().map(|log| hash(log.as_bytes()))); + result + } +} + +/// Execution outcome with path from it to the outcome root and ID. +#[derive(PartialEq, Clone, Default, Debug, BorshSerialize, BorshDeserialize, Eq)] +pub struct ExecutionOutcomeWithIdAndProof { + pub proof: MerklePath, + pub block_hash: CryptoHash, + /// Should be the latest field since contains unparsable by light client ExecutionStatus::Failure + pub outcome_with_id: ExecutionOutcomeWithId, +} + +impl ExecutionOutcomeWithIdAndProof { + pub fn id(&self) -> &CryptoHash { + &self.outcome_with_id.id + } +} + +pub fn verify_transaction_signature( + transaction: &SignedTransaction, + public_keys: &[PublicKey], +) -> bool { + let hash = transaction.get_hash(); + let hash = hash.as_ref(); + public_keys.iter().any(|key| transaction.signature.verify(hash, key)) +} + +/// A more compact struct, just for storage. +#[derive(Clone, BorshSerialize, BorshDeserialize, Debug)] +pub struct ExecutionOutcomeWithProof { + pub proof: MerklePath, + pub outcome: ExecutionOutcome, +} +#[cfg(test)] +mod tests { + use super::*; + use crate::account::{AccessKey, AccessKeyPermission, FunctionCallPermission}; + use borsh::BorshDeserialize; + use unc_crypto::{InMemorySigner, KeyType, Signature, Signer}; + + #[test] + fn test_verify_transaction() { + let signer = InMemorySigner::from_random("test".parse().unwrap(), KeyType::ED25519); + let transaction = Transaction { + signer_id: "test".parse().unwrap(), + public_key: signer.public_key(), + nonce: 0, + receiver_id: "test".parse().unwrap(), + block_hash: Default::default(), + actions: vec![], + } + .sign(&signer); + let wrong_public_key = PublicKey::from_seed(KeyType::ED25519, "wrong"); + let valid_keys = vec![signer.public_key(), wrong_public_key.clone()]; + assert!(verify_transaction_signature(&transaction, &valid_keys)); + + let invalid_keys = vec![wrong_public_key]; + assert!(!verify_transaction_signature(&transaction, &invalid_keys)); + + let bytes = borsh::to_vec(&transaction).unwrap(); + let decoded_tx = SignedTransaction::try_from_slice(&bytes).unwrap(); + assert!(verify_transaction_signature(&decoded_tx, &valid_keys)); + } + + /// This test is change checker for a reason - we don't expect transaction format to change. + /// If it does - you MUST update all of the dependencies: like nearlib and other clients. + #[test] + fn test_serialize_transaction() { + let public_key: PublicKey = "22skMptHjFWNyuEWY22ftn2AbLPSYpmYwGJRGwpNHbTV".parse().unwrap(); + let transaction = Transaction { + signer_id: "test.near".parse().unwrap(), + public_key: public_key.clone(), + nonce: 1, + receiver_id: "123".parse().unwrap(), + block_hash: Default::default(), + actions: vec![ + Action::CreateAccount(CreateAccountAction {}), + Action::DeployContract(DeployContractAction { code: vec![1, 2, 3] }), + Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "qqq".to_string(), + args: vec![1, 2, 3], + gas: 1_000, + deposit: 1_000_000, + })), + Action::Transfer(TransferAction { deposit: 123 }), + Action::Stake(Box::new(StakeAction { + public_key: public_key.clone(), + stake: 1_000_000, + })), + Action::AddKey(Box::new(AddKeyAction { + public_key: public_key.clone(), + access_key: AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: "zzz".parse().unwrap(), + method_names: vec!["www".to_string()], + }), + }, + })), + Action::DeleteKey(Box::new(DeleteKeyAction { public_key })), + Action::DeleteAccount(DeleteAccountAction { + beneficiary_id: "123".parse().unwrap(), + }), + ], + }; + let signed_tx = SignedTransaction::new(Signature::empty(KeyType::ED25519), transaction); + let new_signed_tx = + SignedTransaction::try_from_slice(&borsh::to_vec(&signed_tx).unwrap()).unwrap(); + + assert_eq!( + new_signed_tx.get_hash().to_string(), + "4GXvjMFN6wSxnU9jEVT8HbXP5Yk6yELX9faRSKp6n9fX" + ); + } + + #[test] + fn test_outcome_to_hashes() { + let outcome = ExecutionOutcome { + status: ExecutionStatus::SuccessValue(vec![123]), + logs: vec!["123".to_string(), "321".to_string()], + receipt_ids: vec![], + gas_burnt: 123, + compute_usage: Some(456), + tokens_burnt: 1234000, + executor_id: "alice".parse().unwrap(), + metadata: ExecutionMetadata::V1, + }; + let id = CryptoHash([42u8; 32]); + let outcome = ExecutionOutcomeWithId { id, outcome }; + assert_eq!( + vec![ + id, + "5JQs5ekQqKudMmYejuccbtEu1bzhQPXa92Zm4HdV64dQ".parse().unwrap(), + hash("123".as_bytes()), + hash("321".as_bytes()), + ], + outcome.to_hashes() + ); + } +} diff --git a/core/primitives/src/trie_key.rs b/core/primitives/src/trie_key.rs new file mode 100644 index 000000000..e6b6e9260 --- /dev/null +++ b/core/primitives/src/trie_key.rs @@ -0,0 +1,772 @@ +use std::mem::size_of; + +use borsh::{BorshDeserialize, BorshSerialize}; + +use unc_crypto::PublicKey; + +use crate::hash::CryptoHash; +use crate::types::AccountId; + +pub(crate) const ACCOUNT_DATA_SEPARATOR: u8 = b','; +// The use of `ACCESS_KEY` as a separator is a historical artefact. +// Changing it would require a very long DB migration for basically no benefits. +pub(crate) const ACCESS_KEY_SEPARATOR: u8 = col::ACCESS_KEY; + +pub(crate) const RSA2048_KEY_SEPARATOR: u8 = col::RSA2048_KEY; + +/// Type identifiers used for DB key generation to store values in the key-value storage. +pub mod col { + /// This column id is used when storing `primitives::account::Account` type about a given + /// `account_id`. + pub const ACCOUNT: u8 = 0; + /// This column id is used when storing contract blob for a given `account_id`. + pub const CONTRACT_CODE: u8 = 1; + /// This column id is used when storing `primitives::account::AccessKey` type for a given + /// `account_id`. + pub const ACCESS_KEY: u8 = 2; + /// This column id is used when storing `primitives::receipt::ReceivedData` type (data received + /// for a key `data_id`). The required postponed receipt might be still not received or requires + /// more pending input data. + pub const RECEIVED_DATA: u8 = 3; + /// This column id is used when storing `primitives::hash::CryptoHash` (ReceiptId) type. The + /// ReceivedData is not available and is needed for the postponed receipt to execute. + pub const POSTPONED_RECEIPT_ID: u8 = 4; + /// This column id is used when storing the number of missing data inputs that are still not + /// available for a key `receipt_id`. + pub const PENDING_DATA_COUNT: u8 = 5; + /// This column id is used when storing the postponed receipts (`primitives::receipt::Receipt`). + pub const POSTPONED_RECEIPT: u8 = 6; + /// This column id is used when storing: + /// * the indices of the delayed receipts queue (a singleton per shard) + /// * the delayed receipts themselves + /// The identifier is shared between two different key types for for historical reasons. It + /// is valid because the length of `TrieKey::DelayedReceipt` is always greater than + /// `TrieKey::DelayedReceiptIndices` when serialized to bytes. + pub const DELAYED_RECEIPT_OR_INDICES: u8 = 7; + /// This column id is used when storing Key-Value data from a contract on an `account_id`. + pub const CONTRACT_DATA: u8 = 9; + + pub const RSA2048_KEY: u8 = 10; + /// All columns + pub const NON_DELAYED_RECEIPT_COLUMNS: [(u8, &str); 8] = [ + (ACCOUNT, "Account"), + (CONTRACT_CODE, "ContractCode"), + (ACCESS_KEY, "AccessKey"), + (RECEIVED_DATA, "ReceivedData"), + (POSTPONED_RECEIPT_ID, "PostponedReceiptId"), + (PENDING_DATA_COUNT, "PendingDataCount"), + (POSTPONED_RECEIPT, "PostponedReceipt"), + (CONTRACT_DATA, "ContractData"), + ]; +} + +/// Describes the key of a specific key-value record in a state trie. +#[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize)] +pub enum TrieKey { + /// Used to store `primitives::account::Account` struct for a given `AccountId`. + Account { account_id: AccountId }, + /// Used to store `Vec` contract code for a given `AccountId`. + ContractCode { account_id: AccountId }, + /// Used to store `primitives::account::AccessKey` struct for a given `AccountId` and + /// a given `public_key` of the `AccessKey`. + AccessKey { account_id: AccountId, public_key: PublicKey }, + /// Used to store `primitives::receipt::ReceivedData` struct for a given receiver's `AccountId` + /// of `DataReceipt` and a given `data_id` (the unique identifier for the data). + /// NOTE: This is one of the input data for some action receipt. + /// The action receipt might be still not be received or requires more pending input data. + ReceivedData { receiver_id: AccountId, data_id: CryptoHash }, + /// Used to store receipt ID `primitives::hash::CryptoHash` for a given receiver's `AccountId` + /// of the receipt and a given `data_id` (the unique identifier for the required input data). + /// NOTE: This receipt ID indicates the postponed receipt. We store `receipt_id` for performance + /// purposes to avoid deserializing the entire receipt. + PostponedReceiptId { receiver_id: AccountId, data_id: CryptoHash }, + /// Used to store the number of still missing input data `u32` for a given receiver's + /// `AccountId` and a given `receipt_id` of the receipt. + PendingDataCount { receiver_id: AccountId, receipt_id: CryptoHash }, + /// Used to store the postponed receipt `primitives::receipt::Receipt` for a given receiver's + /// `AccountId` and a given `receipt_id` of the receipt. + PostponedReceipt { receiver_id: AccountId, receipt_id: CryptoHash }, + /// Used to store indices of the delayed receipts queue (`node-runtime::DelayedReceiptIndices`). + /// NOTE: It is a singleton per shard. + DelayedReceiptIndices, + /// Used to store a delayed receipt `primitives::receipt::Receipt` for a given index `u64` + /// in a delayed receipt queue. The queue is unique per shard. + DelayedReceipt { index: u64 }, + /// Used to store a key-value record `Vec` within a contract deployed on a given `AccountId` + /// and a given key. + ContractData { account_id: AccountId, key: Vec }, + + ///ca rsakeys + Rsa2048Keys { account_id: AccountId, public_key: PublicKey }, +} + +/// Provides `len` function. +/// +/// This trait exists purely so that we can do `col::ACCOUNT.len()` rather than +/// using naked `1` when we calculate lengths and refer to lengths of slices. +/// See [`TrieKey::len`] for an example. +trait Byte { + fn len(self) -> usize; +} + +impl Byte for u8 { + fn len(self) -> usize { + 1 + } +} + +impl TrieKey { + pub fn len(&self) -> usize { + match self { + TrieKey::Account { account_id } => col::ACCOUNT.len() + account_id.len(), + TrieKey::ContractCode { account_id } => col::CONTRACT_CODE.len() + account_id.len(), + TrieKey::AccessKey { account_id, public_key } => { + col::ACCESS_KEY.len() * 2 + account_id.len() + public_key.len() + } + TrieKey::ReceivedData { receiver_id, data_id } => { + col::RECEIVED_DATA.len() + + receiver_id.len() + + ACCOUNT_DATA_SEPARATOR.len() + + data_id.as_ref().len() + } + TrieKey::PostponedReceiptId { receiver_id, data_id } => { + col::POSTPONED_RECEIPT_ID.len() + + receiver_id.len() + + ACCOUNT_DATA_SEPARATOR.len() + + data_id.as_ref().len() + } + TrieKey::PendingDataCount { receiver_id, receipt_id } => { + col::PENDING_DATA_COUNT.len() + + receiver_id.len() + + ACCOUNT_DATA_SEPARATOR.len() + + receipt_id.as_ref().len() + } + TrieKey::PostponedReceipt { receiver_id, receipt_id } => { + col::POSTPONED_RECEIPT.len() + + receiver_id.len() + + ACCOUNT_DATA_SEPARATOR.len() + + receipt_id.as_ref().len() + } + TrieKey::DelayedReceiptIndices => col::DELAYED_RECEIPT_OR_INDICES.len(), + TrieKey::DelayedReceipt { .. } => { + col::DELAYED_RECEIPT_OR_INDICES.len() + size_of::() + } + TrieKey::ContractData { account_id, key } => { + col::CONTRACT_DATA.len() + + account_id.len() + + ACCOUNT_DATA_SEPARATOR.len() + + key.len() + } + TrieKey::Rsa2048Keys { account_id, public_key } => { + col::RSA2048_KEY.len() * 2 + account_id.len() + public_key.len() + } + } + } + + pub fn append_into(&self, buf: &mut Vec) { + let expected_len = self.len(); + let start_len = buf.len(); + buf.reserve(self.len()); + match self { + TrieKey::Account { account_id } => { + buf.push(col::ACCOUNT); + buf.extend(account_id.as_bytes()); + } + TrieKey::ContractCode { account_id } => { + buf.push(col::CONTRACT_CODE); + buf.extend(account_id.as_bytes()); + } + TrieKey::AccessKey { account_id, public_key } => { + buf.push(col::ACCESS_KEY); + buf.extend(account_id.as_bytes()); + buf.push(ACCESS_KEY_SEPARATOR); + buf.extend(borsh::to_vec(&public_key).unwrap()); + } + TrieKey::ReceivedData { receiver_id, data_id } => { + buf.push(col::RECEIVED_DATA); + buf.extend(receiver_id.as_bytes()); + buf.push(ACCOUNT_DATA_SEPARATOR); + buf.extend(data_id.as_ref()); + } + TrieKey::PostponedReceiptId { receiver_id, data_id } => { + buf.push(col::POSTPONED_RECEIPT_ID); + buf.extend(receiver_id.as_bytes()); + buf.push(ACCOUNT_DATA_SEPARATOR); + buf.extend(data_id.as_ref()); + } + TrieKey::PendingDataCount { receiver_id, receipt_id } => { + buf.push(col::PENDING_DATA_COUNT); + buf.extend(receiver_id.as_bytes()); + buf.push(ACCOUNT_DATA_SEPARATOR); + buf.extend(receipt_id.as_ref()); + } + TrieKey::PostponedReceipt { receiver_id, receipt_id } => { + buf.push(col::POSTPONED_RECEIPT); + buf.extend(receiver_id.as_bytes()); + buf.push(ACCOUNT_DATA_SEPARATOR); + buf.extend(receipt_id.as_ref()); + } + TrieKey::DelayedReceiptIndices => { + buf.push(col::DELAYED_RECEIPT_OR_INDICES); + } + TrieKey::DelayedReceipt { index } => { + buf.push(col::DELAYED_RECEIPT_OR_INDICES); + buf.extend(&index.to_le_bytes()); + } + TrieKey::ContractData { account_id, key } => { + buf.push(col::CONTRACT_DATA); + buf.extend(account_id.as_bytes()); + buf.push(ACCOUNT_DATA_SEPARATOR); + buf.extend(key); + } + TrieKey::Rsa2048Keys { account_id, public_key } => { + buf.push(col::RSA2048_KEY); + buf.extend(account_id.as_bytes()); + buf.push(RSA2048_KEY_SEPARATOR); + buf.extend(borsh::to_vec(&public_key).unwrap()); + } + }; + debug_assert_eq!(expected_len, buf.len() - start_len); + } + + pub fn to_vec(&self) -> Vec { + let mut buf = Vec::with_capacity(self.len()); + self.append_into(&mut buf); + buf + } + + /// Extracts account id from a TrieKey if available. + pub fn get_account_id(&self) -> Option { + match self { + TrieKey::Account { account_id, .. } => Some(account_id.clone()), + TrieKey::ContractCode { account_id, .. } => Some(account_id.clone()), + TrieKey::AccessKey { account_id, .. } => Some(account_id.clone()), + TrieKey::ReceivedData { receiver_id, .. } => Some(receiver_id.clone()), + TrieKey::PostponedReceiptId { receiver_id, .. } => Some(receiver_id.clone()), + TrieKey::PendingDataCount { receiver_id, .. } => Some(receiver_id.clone()), + TrieKey::PostponedReceipt { receiver_id, .. } => Some(receiver_id.clone()), + TrieKey::DelayedReceiptIndices => None, + TrieKey::DelayedReceipt { .. } => None, + TrieKey::ContractData { account_id, .. } => Some(account_id.clone()), + TrieKey::Rsa2048Keys { account_id, .. } => Some(account_id.clone()), + } + } +} + +// TODO: Remove once we switch to non-raw keys everywhere. +pub mod trie_key_parsers { + use super::*; + + pub fn parse_public_key_from_access_key_key( + raw_key: &[u8], + account_id: &AccountId, + ) -> Result { + let prefix_len = col::ACCESS_KEY.len() * 2 + account_id.len(); + if raw_key.len() < prefix_len { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "raw key is too short for TrieKey::AccessKey", + )); + } + PublicKey::try_from_slice(&raw_key[prefix_len..]) + } + + pub fn parse_data_key_from_contract_data_key<'a>( + raw_key: &'a [u8], + account_id: &AccountId, + ) -> Result<&'a [u8], std::io::Error> { + let prefix_len = col::CONTRACT_DATA.len() + account_id.len() + ACCOUNT_DATA_SEPARATOR.len(); + if raw_key.len() < prefix_len { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "raw key is too short for TrieKey::ContractData", + )); + } + Ok(&raw_key[prefix_len..]) + } + + pub fn parse_account_id_prefix<'a>( + column: u8, + raw_key: &'a [u8], + ) -> Result<&'a [u8], std::io::Error> { + let prefix = std::slice::from_ref(&column); + if let Some(tail) = raw_key.strip_prefix(prefix) { + Ok(tail) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "raw key is does not start with a proper column marker", + )) + } + } + + fn parse_account_id_from_slice( + data: &[u8], + trie_key: &str, + ) -> Result { + std::str::from_utf8(data) + .map_err(|_| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!( + "raw key AccountId has invalid UTF-8 format to be TrieKey::{}", + trie_key + ), + ) + })? + .parse() + .map_err(|_| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!("raw key does not have a valid AccountId to be TrieKey::{}", trie_key), + ) + }) + } + + /// Returns next `separator`-terminated token in `data`. + /// + /// In other words, returns slice of `data` from its start up to but + /// excluding first occurrence of `separator`. Returns `None` if `data` + /// does not contain `separator`. + fn next_token(data: &[u8], separator: u8) -> Option<&[u8]> { + data.iter().position(|&byte| byte == separator).map(|idx| &data[..idx]) + } + + pub fn parse_account_id_from_contract_data_key( + raw_key: &[u8], + ) -> Result { + let account_id_prefix = parse_account_id_prefix(col::CONTRACT_DATA, raw_key)?; + if let Some(account_id) = next_token(account_id_prefix, ACCOUNT_DATA_SEPARATOR) { + parse_account_id_from_slice(account_id, "ContractData") + } else { + Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "raw key does not have ACCOUNT_DATA_SEPARATOR to be TrieKey::ContractData", + )) + } + } + + pub fn parse_account_id_from_account_key(raw_key: &[u8]) -> Result { + let account_id = parse_account_id_prefix(col::ACCOUNT, raw_key)?; + parse_account_id_from_slice(account_id, "Account") + } + + pub fn parse_account_id_from_access_key_key( + raw_key: &[u8], + ) -> Result { + let account_id_prefix = parse_account_id_prefix(col::ACCESS_KEY, raw_key)?; + if let Some(account_id) = next_token(account_id_prefix, ACCESS_KEY_SEPARATOR) { + parse_account_id_from_slice(account_id, "AccessKey") + } else { + Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "raw key does not have public key to be TrieKey::AccessKey", + )) + } + } + + pub fn parse_account_id_from_contract_code_key( + raw_key: &[u8], + ) -> Result { + let account_id = parse_account_id_prefix(col::CONTRACT_CODE, raw_key)?; + parse_account_id_from_slice(account_id, "ContractCode") + } + + pub fn parse_trie_key_access_key_from_raw_key( + raw_key: &[u8], + ) -> Result { + let account_id = parse_account_id_from_access_key_key(raw_key)?; + let public_key = parse_public_key_from_access_key_key(raw_key, &account_id)?; + Ok(TrieKey::AccessKey { account_id, public_key }) + } + + pub fn parse_account_id_from_raw_key( + raw_key: &[u8], + ) -> Result, std::io::Error> { + for (col, col_name) in col::NON_DELAYED_RECEIPT_COLUMNS { + if parse_account_id_prefix(col, raw_key).is_err() { + continue; + } + let account_id = match col { + col::ACCOUNT => parse_account_id_from_account_key(raw_key)?, + col::CONTRACT_CODE => parse_account_id_from_contract_code_key(raw_key)?, + col::ACCESS_KEY => parse_account_id_from_access_key_key(raw_key)?, + _ => parse_account_id_from_trie_key_with_separator(col, raw_key, col_name)?, + }; + return Ok(Some(account_id)); + } + Ok(None) + } + + pub fn parse_account_id_from_trie_key_with_separator( + col: u8, + raw_key: &[u8], + col_name: &str, + ) -> Result { + let account_id_prefix = parse_account_id_prefix(col, raw_key)?; + if let Some(account_id) = next_token(account_id_prefix, ACCOUNT_DATA_SEPARATOR) { + parse_account_id_from_slice(account_id, col_name) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!("raw key does not have ACCOUNT_DATA_SEPARATOR to be TrieKey::{}", col_name), + )) + } + } + + pub fn parse_account_id_from_received_data_key( + raw_key: &[u8], + ) -> Result { + parse_account_id_from_trie_key_with_separator(col::RECEIVED_DATA, raw_key, "ReceivedData") + } + + pub fn parse_data_id_from_received_data_key( + raw_key: &[u8], + account_id: &AccountId, + ) -> Result { + let prefix_len = col::ACCESS_KEY.len() * 2 + account_id.len(); + if raw_key.len() < prefix_len { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "raw key is too short for TrieKey::ReceivedData", + )); + } + CryptoHash::try_from(&raw_key[prefix_len..]).map_err(|_| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Can't parse CryptoHash for TrieKey::ReceivedData", + ) + }) + } + + pub fn get_raw_prefix_for_access_keys(account_id: &AccountId) -> Vec { + let mut res = Vec::with_capacity(col::ACCESS_KEY.len() * 2 + account_id.len()); + res.push(col::ACCESS_KEY); + res.extend(account_id.as_bytes()); + res.push(col::ACCESS_KEY); + res + } + + pub fn get_raw_prefix_for_contract_data(account_id: &AccountId, prefix: &[u8]) -> Vec { + let mut res = Vec::with_capacity( + col::CONTRACT_DATA.len() + + account_id.len() + + ACCOUNT_DATA_SEPARATOR.len() + + prefix.len(), + ); + res.push(col::CONTRACT_DATA); + res.extend(account_id.as_bytes()); + res.push(ACCOUNT_DATA_SEPARATOR); + res.extend(prefix); + res + } + + + pub fn parse_public_key_from_rsa_key_key( + raw_key: &[u8], + account_id: &AccountId, + ) -> Result { + let prefix_len = col::RSA2048_KEY.len() * 2 + account_id.len(); + if raw_key.len() < prefix_len { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "raw key is too short for TrieKey::Rsa2048Keys", + )); + } + PublicKey::try_from_slice(&raw_key[prefix_len..]) + } + + pub fn parse_account_id_from_rsa_key_key( + raw_key: &[u8], + ) -> Result { + let account_id_prefix = parse_account_id_prefix(col::RSA2048_KEY, raw_key)?; + if let Some(account_id) = next_token(account_id_prefix, RSA2048_KEY_SEPARATOR) { + parse_account_id_from_slice(account_id, "Rsa2048Keys") + } else { + Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "raw key does not have public key to be TrieKey::Rsa2048Keys", + )) + } + } + + pub fn parse_trie_key_rsa_key_from_raw_key( + raw_key: &[u8], + ) -> Result { + let account_id = parse_account_id_from_rsa_key_key(raw_key)?; + let public_key = parse_public_key_from_rsa_key_key(raw_key, &account_id)?; + Ok(TrieKey::Rsa2048Keys { account_id, public_key }) + } +} + +#[cfg(test)] +mod tests { + use unc_crypto::KeyType; + + use super::*; + + const OK_ACCOUNT_IDS: &[&str] = &[ + "aa", + "a-a", + "a-aa", + "100", + "0o", + "com", + "near", + "bowen", + "b-o_w_e-n", + "b.owen", + "bro.wen", + "a.ha", + "a.b-a.ra", + "system", + "over.9000", + "google.com", + "illia.cheapaccounts.near", + "0o0ooo00oo00o", + "alex-skidanov", + "10-4.8-2", + "b-o_w_e-n", + "no_lols", + "0123456789012345678901234567890123456789012345678901234567890123", + // Valid, but can't be created + "near.a", + ]; + + #[test] + fn test_key_for_account_consistency() { + for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::().unwrap()) { + let key = TrieKey::Account { account_id: account_id.clone() }; + let raw_key = key.to_vec(); + assert_eq!(raw_key.len(), key.len()); + assert_eq!( + trie_key_parsers::parse_account_id_from_account_key(&raw_key).unwrap(), + account_id + ); + assert_eq!( + trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(), + account_id + ); + } + } + + #[test] + fn test_key_for_access_key_consistency() { + let public_key = PublicKey::empty(KeyType::ED25519); + for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::().unwrap()) { + let key = TrieKey::AccessKey { + account_id: account_id.clone(), + public_key: public_key.clone(), + }; + let raw_key = key.to_vec(); + assert_eq!(raw_key.len(), key.len()); + assert_eq!( + trie_key_parsers::parse_trie_key_access_key_from_raw_key(&raw_key).unwrap(), + key + ); + assert_eq!( + trie_key_parsers::parse_account_id_from_access_key_key(&raw_key).unwrap(), + account_id + ); + assert_eq!( + trie_key_parsers::parse_public_key_from_access_key_key(&raw_key, &account_id) + .unwrap(), + public_key + ); + assert_eq!( + trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(), + account_id + ); + } + } + + #[test] + fn test_key_for_data_consistency() { + let data_key = b"0123456789" as &[u8]; + for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::().unwrap()) { + let key = + TrieKey::ContractData { account_id: account_id.clone(), key: data_key.to_vec() }; + let raw_key = key.to_vec(); + assert_eq!(raw_key.len(), key.len()); + assert_eq!( + trie_key_parsers::parse_account_id_from_contract_data_key(&raw_key).unwrap(), + account_id + ); + assert_eq!( + trie_key_parsers::parse_data_key_from_contract_data_key(&raw_key, &account_id) + .unwrap(), + data_key + ); + assert_eq!( + trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(), + account_id + ); + } + } + + #[test] + fn test_key_for_code_consistency() { + for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::().unwrap()) { + let key = TrieKey::ContractCode { account_id: account_id.clone() }; + let raw_key = key.to_vec(); + assert_eq!(raw_key.len(), key.len()); + assert_eq!( + trie_key_parsers::parse_account_id_from_contract_code_key(&raw_key).unwrap(), + account_id + ); + assert_eq!( + trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(), + account_id + ); + } + } + + #[test] + fn test_key_for_received_data_consistency() { + for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::().unwrap()) { + let key = TrieKey::ReceivedData { + receiver_id: account_id.clone(), + data_id: CryptoHash::default(), + }; + let raw_key = key.to_vec(); + assert_eq!(raw_key.len(), key.len()); + assert_eq!( + trie_key_parsers::parse_account_id_from_received_data_key(&raw_key).unwrap(), + account_id + ); + assert_eq!( + trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(), + account_id + ); + assert_eq!( + trie_key_parsers::parse_data_id_from_received_data_key(&raw_key, &account_id) + .unwrap(), + CryptoHash::default(), + ); + } + } + + #[test] + fn test_key_for_postponed_receipt_consistency() { + for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::().unwrap()) { + let key = TrieKey::PostponedReceipt { + receiver_id: account_id.clone(), + receipt_id: CryptoHash::default(), + }; + let raw_key = key.to_vec(); + assert_eq!(raw_key.len(), key.len()); + assert_eq!( + trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(), + account_id + ); + } + } + + #[test] + fn test_key_for_postponed_receipt_id_consistency() { + for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::().unwrap()) { + let key = TrieKey::PostponedReceiptId { + receiver_id: account_id.clone(), + data_id: CryptoHash::default(), + }; + let raw_key = key.to_vec(); + assert_eq!(raw_key.len(), key.len()); + assert_eq!( + trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(), + account_id + ); + } + } + + #[test] + fn test_key_for_pending_data_count_consistency() { + for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::().unwrap()) { + let key = TrieKey::PendingDataCount { + receiver_id: account_id.clone(), + receipt_id: CryptoHash::default(), + }; + let raw_key = key.to_vec(); + assert_eq!(raw_key.len(), key.len()); + assert_eq!( + trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(), + account_id + ); + } + } + + #[test] + fn test_key_for_delayed_receipts_consistency() { + let key = TrieKey::DelayedReceiptIndices; + let raw_key = key.to_vec(); + assert!(trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().is_none()); + let key = TrieKey::DelayedReceipt { index: 0 }; + let raw_key = key.to_vec(); + assert!(trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().is_none()); + } + + #[test] + fn test_account_id_from_trie_key() { + for account_id_str in OK_ACCOUNT_IDS { + let account_id = account_id_str.parse::().unwrap(); + + assert_eq!( + TrieKey::Account { account_id: account_id.clone() }.get_account_id(), + Some(account_id.clone()) + ); + assert_eq!( + TrieKey::ContractCode { account_id: account_id.clone() }.get_account_id(), + Some(account_id.clone()) + ); + assert_eq!( + TrieKey::AccessKey { + account_id: account_id.clone(), + public_key: PublicKey::empty(KeyType::ED25519) + } + .get_account_id(), + Some(account_id.clone()) + ); + assert_eq!( + TrieKey::ReceivedData { + receiver_id: account_id.clone(), + data_id: Default::default() + } + .get_account_id(), + Some(account_id.clone()) + ); + assert_eq!( + TrieKey::PostponedReceiptId { + receiver_id: account_id.clone(), + data_id: Default::default() + } + .get_account_id(), + Some(account_id.clone()) + ); + assert_eq!( + TrieKey::PendingDataCount { + receiver_id: account_id.clone(), + receipt_id: Default::default() + } + .get_account_id(), + Some(account_id.clone()) + ); + assert_eq!( + TrieKey::PostponedReceipt { + receiver_id: account_id.clone(), + receipt_id: Default::default() + } + .get_account_id(), + Some(account_id.clone()) + ); + assert_eq!( + TrieKey::DelayedReceipt { index: Default::default() }.get_account_id(), + None + ); + assert_eq!(TrieKey::DelayedReceiptIndices.get_account_id(), None); + assert_eq!( + TrieKey::ContractData { account_id: account_id.clone(), key: Default::default() } + .get_account_id(), + Some(account_id) + ); + } + } +} diff --git a/core/primitives/src/types.rs b/core/primitives/src/types.rs new file mode 100644 index 000000000..a892691ab --- /dev/null +++ b/core/primitives/src/types.rs @@ -0,0 +1,1519 @@ +use crate::account::{AccessKey, Account}; +use crate::action::RegisterRsa2048KeysAction; +use crate::challenge::ChallengesResult; +use crate::errors::EpochError; +pub use crate::hash::CryptoHash; +use crate::receipt::Receipt; +use crate::serialize::dec_format; +use crate::trie_key::TrieKey; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_crypto::PublicKey; +/// Reexport primitive types +pub use unc_primitives_core::types::*; +pub use unc_vm_runner::logic::TrieNodesCount; +use once_cell::sync::Lazy; +use serde_with::base64::Base64; +use serde_with::serde_as; +use std::sync::Arc; + +/// Hash used by to store state root. +pub type StateRoot = CryptoHash; + +/// Different types of finality. +#[derive( + serde::Serialize, serde::Deserialize, Default, Clone, Debug, PartialEq, Eq, arbitrary::Arbitrary, +)] +pub enum Finality { + #[serde(rename = "optimistic")] + None, + #[serde(rename = "unc-final")] + DoomSlug, + #[serde(rename = "final")] + #[default] + Final, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct AccountWithPublicKey { + pub account_id: AccountId, + pub public_key: PublicKey, +} + +/// Account info for validators +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Eq, PartialEq)] +pub struct AccountInfo { + pub account_id: AccountId, + pub public_key: PublicKey, + #[serde(with = "dec_format")] + pub amount: Balance, + #[serde(with = "dec_format")] + pub power: Power, + #[serde(with = "dec_format")] + pub locked: Balance, +} + +/// This type is used to mark keys (arrays of bytes) that are queried from store. +/// +/// NOTE: Currently, this type is only used in the view_client and RPC to be able to transparently +/// pretty-serialize the bytes arrays as base64-encoded strings (see `serialize.rs`). +#[serde_as] +#[derive( + serde::Serialize, + serde::Deserialize, + Clone, + Debug, + PartialEq, + Eq, + derive_more::Deref, + derive_more::From, + derive_more::Into, + BorshSerialize, + BorshDeserialize, +)] +#[serde(transparent)] +pub struct StoreKey(#[serde_as(as = "Base64")] Vec); + +/// This type is used to mark values returned from store (arrays of bytes). +/// +/// NOTE: Currently, this type is only used in the view_client and RPC to be able to transparently +/// pretty-serialize the bytes arrays as base64-encoded strings (see `serialize.rs`). +#[serde_as] +#[derive( + serde::Serialize, + serde::Deserialize, + Clone, + Debug, + PartialEq, + Eq, + derive_more::Deref, + derive_more::From, + derive_more::Into, + BorshSerialize, + BorshDeserialize, +)] +#[serde(transparent)] +pub struct StoreValue(#[serde_as(as = "Base64")] Vec); + +/// This type is used to mark function arguments. +/// +/// NOTE: The main reason for this to exist (except the type-safety) is that the value is +/// transparently serialized and deserialized as a base64-encoded string when serde is used +/// (serde_json). +#[serde_as] +#[derive( + serde::Serialize, + serde::Deserialize, + Clone, + Debug, + PartialEq, + Eq, + derive_more::Deref, + derive_more::From, + derive_more::Into, + BorshSerialize, + BorshDeserialize, +)] +#[serde(transparent)] +pub struct FunctionArgs(#[serde_as(as = "Base64")] Vec); + +/// A structure used to indicate the kind of state changes due to transaction/receipt processing, etc. +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +pub enum StateChangeKind { + AccountTouched { account_id: AccountId }, + AccessKeyTouched { account_id: AccountId }, + DataTouched { account_id: AccountId }, + ContractCodeTouched { account_id: AccountId }, + RsaKeyTouched { account_id: AccountId }, +} + +pub type StateChangesKinds = Vec; + +#[easy_ext::ext(StateChangesKindsExt)] +impl StateChangesKinds { + pub fn from_changes( + raw_changes: &mut dyn Iterator>, + ) -> Result { + raw_changes + .filter_map(|raw_change| { + let RawStateChangesWithTrieKey { trie_key, .. } = match raw_change { + Ok(p) => p, + Err(e) => return Some(Err(e)), + }; + match trie_key { + TrieKey::Account { account_id } => { + Some(Ok(StateChangeKind::AccountTouched { account_id })) + } + TrieKey::ContractCode { account_id } => { + Some(Ok(StateChangeKind::ContractCodeTouched { account_id })) + } + TrieKey::AccessKey { account_id, .. } => { + Some(Ok(StateChangeKind::AccessKeyTouched { account_id })) + } + TrieKey::ContractData { account_id, .. } => { + Some(Ok(StateChangeKind::DataTouched { account_id })) + } + TrieKey::Rsa2048Keys { account_id, .. } => { + Some(Ok(StateChangeKind::RsaKeyTouched { account_id })) + } + _ => None, + } + }) + .collect() + } +} + +/// A structure used to index state changes due to transaction/receipt processing and other things. +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq)] +pub enum StateChangeCause { + /// A type of update that does not get finalized. Used for verification and execution of + /// immutable smart contract methods. Attempt fo finalize a `TrieUpdate` containing such + /// change will lead to panic. + NotWritableToDisk, + /// A type of update that is used to mark the initial storage update, e.g. during genesis + /// or in tests setup. + InitialState, + /// Processing of a transaction. + TransactionProcessing { tx_hash: CryptoHash }, + /// Before the receipt is going to be processed, inputs get drained from the state, which + /// causes state modification. + ActionReceiptProcessingStarted { receipt_hash: CryptoHash }, + /// Computation of gas reward. + ActionReceiptGasReward { receipt_hash: CryptoHash }, + /// Processing of a receipt. + ReceiptProcessing { receipt_hash: CryptoHash }, + /// The given receipt was postponed. This is either a data receipt or an action receipt. + /// A `DataReceipt` can be postponed if the corresponding `ActionReceipt` is not received yet, + /// or other data dependencies are not satisfied. + /// An `ActionReceipt` can be postponed if not all data dependencies are received. + PostponedReceipt { receipt_hash: CryptoHash }, + /// Updated delayed receipts queue in the state. + /// We either processed previously delayed receipts or added more receipts to the delayed queue. + UpdatedDelayedReceipts, + /// State change that happens when we update validator accounts. Not associated with with any + /// specific transaction or receipt. + ValidatorAccountsUpdate, + /// State change that is happens due to migration that happens in first block of an epoch + /// after protocol upgrade + Migration, + /// State changes for building states for re-sharding + Resharding, +} + +/// This represents the committed changes in the Trie with a change cause. +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +pub struct RawStateChange { + pub cause: StateChangeCause, + pub data: Option>, +} + +/// List of committed changes with a cause for a given TrieKey +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +pub struct RawStateChangesWithTrieKey { + pub trie_key: TrieKey, + pub changes: Vec, +} + +/// Consolidate state change of trie_key and the final value the trie key will be changed to +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +pub struct ConsolidatedStateChange { + pub trie_key: TrieKey, + pub value: Option>, +} + +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +pub struct StateChangesForResharding { + pub changes: Vec, + // we need to store deleted receipts here because StateChanges will only include + // trie keys for removed values and account information can not be inferred from + // trie key for delayed receipts + pub processed_delayed_receipts: Vec, +} + +impl StateChangesForResharding { + pub fn from_raw_state_changes( + changes: &[RawStateChangesWithTrieKey], + processed_delayed_receipts: Vec, + ) -> Self { + let changes = changes + .iter() + .map(|RawStateChangesWithTrieKey { trie_key, changes }| { + let value = changes.last().expect("state_changes must not be empty").data.clone(); + ConsolidatedStateChange { trie_key: trie_key.clone(), value } + }) + .collect(); + Self { changes, processed_delayed_receipts } + } +} + +/// key that was updated -> list of updates with the corresponding indexing event. +pub type RawStateChanges = std::collections::BTreeMap, RawStateChangesWithTrieKey>; + +#[derive(Debug)] +pub enum StateChangesRequest { + AccountChanges { account_ids: Vec }, + SingleAccessKeyChanges { keys: Vec }, + AllAccessKeyChanges { account_ids: Vec }, + ContractCodeChanges { account_ids: Vec }, + DataChanges { account_ids: Vec, key_prefix: StoreKey }, +} + +#[derive(Debug)] +pub enum StateChangeValue { + AccountUpdate { account_id: AccountId, account: Account }, + AccountDeletion { account_id: AccountId }, + AccessKeyUpdate { account_id: AccountId, public_key: PublicKey, access_key: AccessKey }, + AccessKeyDeletion { account_id: AccountId, public_key: PublicKey }, + DataUpdate { account_id: AccountId, key: StoreKey, value: StoreValue }, + DataDeletion { account_id: AccountId, key: StoreKey }, + ContractCodeUpdate { account_id: AccountId, code: Vec }, + ContractCodeDeletion { account_id: AccountId }, + RsaKeyUpdate { account_id: AccountId, public_key: PublicKey, rsa_key: RegisterRsa2048KeysAction }, + RsaKeyDeletion { account_id: AccountId, public_key: PublicKey }, +} + +impl StateChangeValue { + pub fn affected_account_id(&self) -> &AccountId { + match &self { + StateChangeValue::AccountUpdate { account_id, .. } + | StateChangeValue::AccountDeletion { account_id } + | StateChangeValue::AccessKeyUpdate { account_id, .. } + | StateChangeValue::AccessKeyDeletion { account_id, .. } + | StateChangeValue::DataUpdate { account_id, .. } + | StateChangeValue::DataDeletion { account_id, .. } + | StateChangeValue::ContractCodeUpdate { account_id, .. } + | StateChangeValue::RsaKeyUpdate { account_id, .. } + | StateChangeValue::RsaKeyDeletion { account_id, .. } + | StateChangeValue::ContractCodeDeletion { account_id } => account_id, + } + } +} + +#[derive(Debug)] +pub struct StateChangeWithCause { + pub cause: StateChangeCause, + pub value: StateChangeValue, +} + +pub type StateChanges = Vec; + +#[easy_ext::ext(StateChangesExt)] +impl StateChanges { + pub fn from_changes( + raw_changes: impl Iterator>, + ) -> Result { + let mut state_changes = Self::new(); + + for raw_change in raw_changes { + let RawStateChangesWithTrieKey { trie_key, changes } = raw_change?; + + match trie_key { + TrieKey::Account { account_id } => state_changes.extend(changes.into_iter().map( + |RawStateChange { cause, data }| StateChangeWithCause { + cause, + value: if let Some(change_data) = data { + StateChangeValue::AccountUpdate { + account_id: account_id.clone(), + account: <_>::try_from_slice(&change_data).expect( + "Failed to parse internally stored account information", + ), + } + } else { + StateChangeValue::AccountDeletion { account_id: account_id.clone() } + }, + }, + )), + TrieKey::AccessKey { account_id, public_key } => { + state_changes.extend(changes.into_iter().map( + |RawStateChange { cause, data }| StateChangeWithCause { + cause, + value: if let Some(change_data) = data { + StateChangeValue::AccessKeyUpdate { + account_id: account_id.clone(), + public_key: public_key.clone(), + access_key: <_>::try_from_slice(&change_data) + .expect("Failed to parse internally stored access key"), + } + } else { + StateChangeValue::AccessKeyDeletion { + account_id: account_id.clone(), + public_key: public_key.clone(), + } + }, + }, + )) + } + TrieKey::ContractCode { account_id } => { + state_changes.extend(changes.into_iter().map( + |RawStateChange { cause, data }| StateChangeWithCause { + cause, + value: match data { + Some(change_data) => StateChangeValue::ContractCodeUpdate { + account_id: account_id.clone(), + code: change_data, + }, + None => StateChangeValue::ContractCodeDeletion { + account_id: account_id.clone(), + }, + }, + }, + )); + } + TrieKey::ContractData { account_id, key } => { + state_changes.extend(changes.into_iter().map( + |RawStateChange { cause, data }| StateChangeWithCause { + cause, + value: if let Some(change_data) = data { + StateChangeValue::DataUpdate { + account_id: account_id.clone(), + key: key.to_vec().into(), + value: change_data.into(), + } + } else { + StateChangeValue::DataDeletion { + account_id: account_id.clone(), + key: key.to_vec().into(), + } + }, + }, + )); + } + // The next variants considered as unnecessary as too low level + TrieKey::ReceivedData { .. } => {} + TrieKey::PostponedReceiptId { .. } => {} + TrieKey::PendingDataCount { .. } => {} + TrieKey::PostponedReceipt { .. } => {} + TrieKey::DelayedReceiptIndices => {} + TrieKey::DelayedReceipt { .. } => {} + TrieKey::Rsa2048Keys { account_id, public_key } => { + state_changes.extend(changes.into_iter().map( + |RawStateChange { cause, data }| StateChangeWithCause { + cause, + value: if let Some(change_data) = data { + StateChangeValue::AccessKeyUpdate { + account_id: account_id.clone(), + public_key: public_key.clone(), + access_key: <_>::try_from_slice(&change_data) + .expect("Failed to parse internally stored access key"), + } + } else { + StateChangeValue::AccessKeyDeletion { + account_id: account_id.clone(), + public_key: public_key.clone(), + } + }, + }, + )) + } + } + } + + Ok(state_changes) + } + pub fn from_account_changes( + raw_changes: impl Iterator>, + ) -> Result { + let state_changes = Self::from_changes(raw_changes)?; + + Ok(state_changes + .into_iter() + .filter(|state_change| { + matches!( + state_change.value, + StateChangeValue::AccountUpdate { .. } + | StateChangeValue::AccountDeletion { .. } + ) + }) + .collect()) + } + + pub fn from_access_key_changes( + raw_changes: impl Iterator>, + ) -> Result { + let state_changes = Self::from_changes(raw_changes)?; + + Ok(state_changes + .into_iter() + .filter(|state_change| { + matches!( + state_change.value, + StateChangeValue::AccessKeyUpdate { .. } + | StateChangeValue::AccessKeyDeletion { .. } + ) + }) + .collect()) + } + + pub fn from_contract_code_changes( + raw_changes: impl Iterator>, + ) -> Result { + let state_changes = Self::from_changes(raw_changes)?; + + Ok(state_changes + .into_iter() + .filter(|state_change| { + matches!( + state_change.value, + StateChangeValue::ContractCodeUpdate { .. } + | StateChangeValue::ContractCodeDeletion { .. } + ) + }) + .collect()) + } + + pub fn from_data_changes( + raw_changes: impl Iterator>, + ) -> Result { + let state_changes = Self::from_changes(raw_changes)?; + + Ok(state_changes + .into_iter() + .filter(|state_change| { + matches!( + state_change.value, + StateChangeValue::DataUpdate { .. } | StateChangeValue::DataDeletion { .. } + ) + }) + .collect()) + } +} + +#[derive(PartialEq, Eq, Clone, Debug, BorshSerialize, BorshDeserialize, serde::Serialize)] +pub struct StateRootNode { + /// In Nightshade, data is the serialized TrieNodeWithSize. + /// + /// Beware that hash of an empty state root (i.e. once who’s data is an + /// empty byte string) **does not** equal hash of an empty byte string. + /// Instead, an all-zero hash indicates an empty node. + pub data: Arc<[u8]>, + + /// In Nightshade, memory_usage is a field of TrieNodeWithSize. + pub memory_usage: u64, +} + +impl StateRootNode { + pub fn empty() -> Self { + static EMPTY: Lazy> = Lazy::new(|| Arc::new([])); + StateRootNode { data: EMPTY.clone(), memory_usage: 0 } + } +} + +/// Epoch identifier -- wrapped hash, to make it easier to distinguish. +/// EpochId of epoch T is the hash of last block in T-2 +/// EpochId of first two epochs is 0 +#[derive( + Debug, + Clone, + Default, + Hash, + Eq, + PartialEq, + PartialOrd, + Ord, + derive_more::AsRef, + BorshSerialize, + BorshDeserialize, + serde::Serialize, + serde::Deserialize, + arbitrary::Arbitrary, +)] +#[as_ref(forward)] +pub struct EpochId(pub CryptoHash); + +impl std::str::FromStr for EpochId { + type Err = Box; + + /// Decodes base58-encoded string into a 32-byte crypto hash. + fn from_str(epoch_id_str: &str) -> Result { + Ok(EpochId(CryptoHash::from_str(epoch_id_str)?)) + } +} +/// TODO + +/// TODO +/// Stores validator and its power for two consecutive epochs. +/// It is necessary because the blocks on the epoch boundary need to contain approvals from both +/// epochs. +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, PartialEq, Eq)] +pub struct ApprovalFrozen { + /// Account that has frozen. + pub account_id: AccountId, + /// Public key of the proposed validator. + pub public_key: PublicKey, + /// Frozen / weight of the validator. + pub frozen_this_epoch: Balance, + /// Stake of the validator. + pub frozen_next_epoch: Balance, +} + +pub mod validator_power_and_frozen { + use borsh::{BorshDeserialize, BorshSerialize}; + use unc_crypto::PublicKey; + use unc_primitives_core::types::{AccountId, Balance, Power}; + use serde::Serialize; + use crate::types::{ApprovalFrozen}; + + pub use super::ValidatorPowerAndFrozenV1; + + /// Stores validator and its power with frozen. + #[derive(BorshSerialize, BorshDeserialize, Serialize, Debug, Clone, PartialEq, Eq, PartialOrd)] + #[serde(tag = "validator_power_and_frozen_struct_version")] + pub enum ValidatorPowerAndFrozen { + V1(ValidatorPowerAndFrozenV1), + } + #[derive(Clone)] + pub struct ValidatorPowerAndFrozenIter<'a> { + collection: ValidatorPowerAndFrozenIterSource<'a>, + curr_index: usize, + len: usize, + } + + impl<'a> ValidatorPowerAndFrozenIter<'a> { + pub fn empty() -> Self { + Self { collection: ValidatorPowerAndFrozenIterSource::V2(&[]), curr_index: 0, len: 0 } + } + + pub fn v1(collection: &'a [ValidatorPowerAndFrozenV1]) -> Self { + Self { + collection: ValidatorPowerAndFrozenIterSource::V1(collection), + curr_index: 0, + len: collection.len(), + } + } + + pub fn new(collection: &'a [ValidatorPowerAndFrozen]) -> Self { + Self { + collection: ValidatorPowerAndFrozenIterSource::V2(collection), + curr_index: 0, + len: collection.len(), + } + } + + pub fn len(&self) -> usize { + self.len + } + } + + impl<'a> Iterator for ValidatorPowerAndFrozenIter<'a> { + type Item = ValidatorPowerAndFrozen; + + fn next(&mut self) -> Option { + if self.curr_index < self.len { + let item = match self.collection { + ValidatorPowerAndFrozenIterSource::V1(collection) => { + ValidatorPowerAndFrozen::V1(collection[self.curr_index].clone()) + } + ValidatorPowerAndFrozenIterSource::V2(collection) => { + collection[self.curr_index].clone() + } + }; + self.curr_index += 1; + Some(item) + } else { + None + } + } + } + #[derive(Clone)] + enum ValidatorPowerAndFrozenIterSource<'a> { + V1(&'a [ValidatorPowerAndFrozenV1]), + V2(&'a [ValidatorPowerAndFrozen]), + } + + impl ValidatorPowerAndFrozen { + pub fn new_v1( + account_id: AccountId, + public_key: PublicKey, + power: Power, + frozen: Balance, + ) -> Self { + Self::V1(ValidatorPowerAndFrozenV1 { account_id, public_key, power, frozen }) + } + + pub fn new( + account_id: AccountId, + public_key: PublicKey, + power: Power, + frozen: Balance, + ) -> Self { + Self::new_v1(account_id, public_key, power, frozen) + } + + pub fn into_v1(self) -> ValidatorPowerAndFrozenV1 { + match self { + Self::V1(v1) => v1, + } + } + + + #[inline] + pub fn account_and_frozen(self) -> (AccountId, Balance) { + match self { + Self::V1(v1) => (v1.account_id, v1.frozen), + } + } + + #[inline] + pub fn account_and_power(self) -> (AccountId, Power) { + match self { + Self::V1(v1) => (v1.account_id, v1.power), + } + } + + #[inline] + pub fn destructure(self) -> (AccountId, PublicKey, Power, Balance) { + match self { + Self::V1(v1) => (v1.account_id, v1.public_key, v1.power, v1.frozen), + } + } + + #[inline] + pub fn take_account_id(self) -> AccountId { + match self { + Self::V1(v1) => v1.account_id, + } + } + + #[inline] + pub fn account_id(&self) -> &AccountId { + match self { + Self::V1(v1) => &v1.account_id, + } + } + + #[inline] + pub fn take_public_key(self) -> PublicKey { + match self { + Self::V1(v1) => v1.public_key, + } + } + + #[inline] + pub fn public_key(&self) -> &PublicKey { + match self { + Self::V1(v1) => &v1.public_key, + } + } + + #[inline] + pub fn power(&self) -> Power { + match self { + Self::V1(v1) => v1.power, + } + } + + #[inline] + pub fn power_mut(&mut self) -> &mut Power { + match self { + Self::V1(v1) => &mut v1.power, + } + } + + #[inline] + pub fn frozen(&self) -> Balance { + match self { + Self::V1(v1) => v1.frozen, + } + } + + pub fn get_approval_frozen(&self, is_next_epoch: bool) -> ApprovalFrozen { + ApprovalFrozen { + account_id: self.account_id().clone(), + public_key: self.public_key().clone(), + frozen_this_epoch: if is_next_epoch { 0 } else { self.frozen() }, + frozen_next_epoch: if is_next_epoch { self.frozen() } else { 0 }, + } + } + + /// Returns the validator's number of mandates (rounded down) at `frozen_per_seat`. + /// + /// It returns `u16` since it allows infallible conversion to `usize` and with [`u16::MAX`] + /// equalling 65_535 it should be sufficient to hold the number of mandates per validator. + /// + /// # Why `u16` should be sufficient + /// + /// As of October 2023, a [recommended lower bound] for the frozen required per mandate is + /// 25k $NEAR. At this price, the validator with highest frozen would have 1_888 mandates, + /// which is well below `u16::MAX`. + /// + /// From another point of view, with more than `u16::MAX` mandates for validators, sampling + /// mandates might become computationally too expensive. This might trigger an increase in + /// the required power per mandate, bringing down the number of mandates per validator. + /// + /// [recommended lower bound]: https://near.zulipchat.com/#narrow/stream/407237-pagoda.2Fcore.2Fstateless-validation/topic/validator.20seat.20assignment/near/393792901 + /// + /// # Panics + /// + /// Panics if the number of mandates overflows `u16`. + pub fn num_mandates(&self, frozen_per_mandate: Balance) -> u16 { + // Integer division in Rust returns the floor as described here + // https://doc.rust-lang.org/std/primitive.u64.html#method.div_euclid + u16::try_from(self.frozen() / frozen_per_mandate) + .expect("number of mandats should fit u16") + } + + /// Returns the weight attributed to the validator's partial mandate. + /// + /// A validator has a partial mandate if its power cannot be divided evenly by + /// `frozen_per_mandate`. The remainder of that division is the weight of the partial + /// mandate. + /// + /// Due to this definintion a validator has exactly one partial mandate with `0 <= weight < + /// power_per_mandate`. + /// + /// # Example + /// + /// Let `V` be a validator with power of 12. If `frozen_per_mandate` equals 5 then the weight + /// of `V`'s partial mandate is `12 % 5 = 2`. + pub fn partial_mandate_weight(&self, frozen_per_mandate: Balance) -> Balance { + self.frozen() % frozen_per_mandate + } + } + +} + +pub mod validator_frozen { + use borsh::{BorshDeserialize, BorshSerialize}; + use unc_crypto::PublicKey; + use unc_primitives_core::types::{AccountId, Balance}; + use serde::Serialize; + + pub use super::ValidatorFrozenV1; + + /// Stores validator and its frozen. + #[derive(BorshSerialize, BorshDeserialize, Serialize, Debug, Clone, PartialEq, Eq)] + #[serde(tag = "validator_validator_struct_version")] + pub enum ValidatorFrozen { + V1(ValidatorFrozenV1), + } + + pub struct ValidatorFrozenIter<'a> { + collection: ValidatorFrozenIterSource<'a>, + curr_index: usize, + len: usize, + } + + impl<'a> ValidatorFrozenIter<'a> { + pub fn empty() -> Self { + Self { collection: ValidatorFrozenIterSource::V2(&[]), curr_index: 0, len: 0 } + } + + pub fn v1(collection: &'a [ValidatorFrozenV1]) -> Self { + Self { + collection: ValidatorFrozenIterSource::V1(collection), + curr_index: 0, + len: collection.len(), + } + } + + pub fn new(collection: &'a [ValidatorFrozen]) -> Self { + Self { + collection: ValidatorFrozenIterSource::V2(collection), + curr_index: 0, + len: collection.len(), + } + } + + pub fn len(&self) -> usize { + self.len + } + } + + impl<'a> Iterator for ValidatorFrozenIter<'a> { + type Item = ValidatorFrozen; + + fn next(&mut self) -> Option { + if self.curr_index < self.len { + let item = match self.collection { + ValidatorFrozenIterSource::V1(collection) => { + ValidatorFrozen::V1(collection[self.curr_index].clone()) + } + ValidatorFrozenIterSource::V2(collection) => collection[self.curr_index].clone(), + }; + self.curr_index += 1; + Some(item) + } else { + None + } + } + } + + enum ValidatorFrozenIterSource<'a> { + V1(&'a [ValidatorFrozenV1]), + V2(&'a [ValidatorFrozen]), + } + + impl ValidatorFrozen { + pub fn new_v1(account_id: AccountId, public_key: PublicKey, frozen: Balance) -> Self { + Self::V1(ValidatorFrozenV1 { account_id, public_key, frozen }) + } + + pub fn new(account_id: AccountId, public_key: PublicKey, frozen: Balance) -> Self { + Self::new_v1(account_id, public_key, frozen) + } + + pub fn into_v1(self) -> ValidatorFrozenV1 { + match self { + Self::V1(v1) => v1, + } + } + + #[inline] + pub fn account_and_frozen(self) -> (AccountId, Balance) { + match self { + Self::V1(v1) => (v1.account_id, v1.frozen), + } + } + + #[inline] + pub fn destructure(self) -> (AccountId, PublicKey, Balance) { + match self { + Self::V1(v1) => (v1.account_id, v1.public_key, v1.frozen), + } + } + + #[inline] + pub fn take_account_id(self) -> AccountId { + match self { + Self::V1(v1) => v1.account_id, + } + } + + #[inline] + pub fn account_id(&self) -> &AccountId { + match self { + Self::V1(v1) => &v1.account_id, + } + } + + #[inline] + pub fn take_public_key(self) -> PublicKey { + match self { + Self::V1(v1) => v1.public_key, + } + } + + #[inline] + pub fn public_key(&self) -> &PublicKey { + match self { + Self::V1(v1) => &v1.public_key, + } + } + + #[inline] + pub fn frozen(&self) -> Balance { + match self { + Self::V1(v1) => v1.frozen, + } + } + + #[inline] + pub fn frozen_mut(&mut self) -> &mut Balance { + match self { + Self::V1(v1) => &mut v1.frozen, + } + } + } + + +} +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, PartialEq, Eq)] +pub struct ApprovalPower { + /// Account that has power. + pub account_id: AccountId, + /// Public key of the proposed validator. + pub public_key: PublicKey, + /// Power / weight of the validator. + pub power_this_epoch: Power, + pub power_next_epoch: Power, +} + +pub mod validator_power { + use crate::types::ApprovalPower; + use borsh::{BorshDeserialize, BorshSerialize}; + use unc_crypto::PublicKey; + use unc_primitives_core::types::{AccountId, Power}; + use serde::Serialize; + + pub use super::ValidatorPowerV1; + + /// Stores validator and its power. + #[derive(BorshSerialize, BorshDeserialize, Serialize, Debug, Clone, PartialEq, Eq)] + #[serde(tag = "validator_power_struct_version")] + pub enum ValidatorPower { + V1(ValidatorPowerV1), + } + + pub struct ValidatorPowerIter<'a> { + collection: ValidatorPowerIterSource<'a>, + curr_index: usize, + len: usize, + } + + impl<'a> ValidatorPowerIter<'a> { + pub fn empty() -> Self { + Self { collection: ValidatorPowerIterSource::V2(&[]), curr_index: 0, len: 0 } + } + + pub fn v1(collection: &'a [ValidatorPowerV1]) -> Self { + Self { + collection: ValidatorPowerIterSource::V1(collection), + curr_index: 0, + len: collection.len(), + } + } + + pub fn new(collection: &'a [ValidatorPower]) -> Self { + Self { + collection: ValidatorPowerIterSource::V2(collection), + curr_index: 0, + len: collection.len(), + } + } + + pub fn len(&self) -> usize { + self.len + } + } + + impl<'a> Iterator for ValidatorPowerIter<'a> { + type Item = ValidatorPower; + + fn next(&mut self) -> Option { + if self.curr_index < self.len { + let item = match self.collection { + ValidatorPowerIterSource::V1(collection) => { + ValidatorPower::V1(collection[self.curr_index].clone()) + } + ValidatorPowerIterSource::V2(collection) => collection[self.curr_index].clone(), + }; + self.curr_index += 1; + Some(item) + } else { + None + } + } + } + + enum ValidatorPowerIterSource<'a> { + V1(&'a [ValidatorPowerV1]), + V2(&'a [ValidatorPower]), + } + + impl ValidatorPower { + pub fn new_v1(account_id: AccountId, public_key: PublicKey, power: Power) -> Self { + Self::V1(ValidatorPowerV1 { account_id, public_key, power}) + } + + pub fn new(account_id: AccountId, public_key: PublicKey, power: Power) -> Self { + Self::new_v1(account_id, public_key, power) + } + + pub fn into_v1(self) -> ValidatorPowerV1 { + match self { + Self::V1(v1) => v1, + } + } + + #[inline] + pub fn account_and_power(self) -> (AccountId, Power) { + match self { + Self::V1(v1) => (v1.account_id, v1.power), + } + } + + #[inline] + pub fn destructure(self) -> (AccountId, PublicKey, Power) { + match self { + Self::V1(v1) => (v1.account_id, v1.public_key, v1.power), + } + } + + #[inline] + pub fn take_account_id(self) -> AccountId { + match self { + Self::V1(v1) => v1.account_id, + } + } + + #[inline] + pub fn account_id(&self) -> &AccountId { + match self { + Self::V1(v1) => &v1.account_id, + } + } + + #[inline] + pub fn take_public_key(self) -> PublicKey { + match self { + Self::V1(v1) => v1.public_key, + } + } + + #[inline] + pub fn public_key(&self) -> &PublicKey { + match self { + Self::V1(v1) => &v1.public_key, + } + } + + #[inline] + pub fn power(&self) -> Power { + match self { + Self::V1(v1) => v1.power, + } + } + + #[inline] + pub fn power_mut(&mut self) -> &mut Power { + match self { + Self::V1(v1) => &mut v1.power, + } + } + + pub fn get_approval_power(&self, is_next_epoch: bool) -> ApprovalPower { + ApprovalPower { + account_id: self.account_id().clone(), + public_key: self.public_key().clone(), + power_this_epoch: if is_next_epoch { 0 } else { self.power() }, + power_next_epoch: if is_next_epoch { self.power() } else { 0 }, + } + } + + + } +} +/// Stores validator and its power with frozen. +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, PartialEq, Eq, PartialOrd)] +pub struct ValidatorPowerAndFrozenV1 { + /// Account that has power. + pub account_id: AccountId, + /// Public key of the proposed validator. + pub public_key: PublicKey, + /// Power / weight of the validator. + pub power: Power, + /// Frozen / weight of the validator. + pub frozen: Balance, +} + +/// Stores validator and its frozen. +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, PartialEq, Eq)] +pub struct ValidatorFrozenV1 { + /// Account that has frozen. + pub account_id: AccountId, + /// Public key of the proposed validator. + pub public_key: PublicKey, + /// Frozen / weight of the validator. + pub frozen: Balance, +} + +/// Stores validator and its power. +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, PartialEq, Eq)] +pub struct ValidatorPowerV1 { + /// Account that has power. + pub account_id: AccountId, + /// Public key of the proposed validator. + pub public_key: PublicKey, + /// Power / weight of the validator. + pub power: Power, +} + +/// Information after block was processed. +#[derive(Debug, PartialEq, BorshSerialize, BorshDeserialize, Clone, Eq)] +pub struct BlockExtra { + pub challenges_result: ChallengesResult, +} + +pub mod chunk_extra { + use crate::types::validator_power::{ValidatorPower, ValidatorPowerIter}; + use crate::types::validator_frozen::{ValidatorFrozen, ValidatorFrozenIter}; + use crate::types::StateRoot; + use borsh::{BorshDeserialize, BorshSerialize}; + use unc_primitives_core::hash::CryptoHash; + use unc_primitives_core::types::{Balance, Gas}; + + pub use super::ChunkExtraV1; + + /// Information after chunk was processed, used to produce or check next chunk. + #[derive(Debug, PartialEq, BorshSerialize, BorshDeserialize, Clone, Eq)] + pub enum ChunkExtra { + V1(ChunkExtraV1), + V2(ChunkExtraV2), + } + + #[derive(Debug, PartialEq, BorshSerialize, BorshDeserialize, Clone, Eq)] + pub struct ChunkExtraV2 { + /// Post state root after applying give chunk. + pub state_root: StateRoot, + /// Root of merklizing results of receipts (transactions) execution. + pub outcome_root: CryptoHash, + /// Validator proposals produced by given chunk. + pub validator_power_proposals: Vec, + /// Validator proposals produced by given chunk. + pub validator_frozen_proposals: Vec, + /// Actually how much gas were used. + pub gas_used: Gas, + /// Gas limit, allows to increase or decrease limit based on expected time vs real time for computing the chunk. + pub gas_limit: Gas, + /// Total balance burnt after processing the current chunk. + pub balance_burnt: Balance, + } + + impl ChunkExtra { + pub fn new_with_only_state_root(state_root: &StateRoot) -> Self { + Self::new(state_root, CryptoHash::default(), vec![], vec![], 0, 0,0) + } + + pub fn new( + state_root: &StateRoot, + outcome_root: CryptoHash, + validator_power_proposals: Vec, + validator_frozen_proposals: Vec, + gas_used: Gas, + gas_limit: Gas, + balance_burnt: Balance, + ) -> Self { + Self::V2(ChunkExtraV2 { + state_root: *state_root, + outcome_root, + validator_power_proposals, + validator_frozen_proposals, + gas_used, + gas_limit, + balance_burnt, + }) + } + + #[inline] + pub fn outcome_root(&self) -> &StateRoot { + match self { + Self::V1(v1) => &v1.outcome_root, + Self::V2(v2) => &v2.outcome_root, + } + } + + #[inline] + pub fn state_root(&self) -> &StateRoot { + match self { + Self::V1(v1) => &v1.state_root, + Self::V2(v2) => &v2.state_root, + } + } + + #[inline] + pub fn state_root_mut(&mut self) -> &mut StateRoot { + match self { + Self::V1(v1) => &mut v1.state_root, + Self::V2(v2) => &mut v2.state_root, + } + } + + #[inline] + pub fn validator_power_proposals(&self) -> ValidatorPowerIter { + match self { + Self::V1(v1) => ValidatorPowerIter::v1(&v1.validator_power_proposals), + Self::V2(v2) => ValidatorPowerIter::new(&v2.validator_power_proposals), + } + } + + #[inline] + pub fn validator_frozen_proposals(&self) -> ValidatorFrozenIter { + match self { + Self::V1(v1) => ValidatorFrozenIter::v1(&v1.validator_frozen_proposals), + Self::V2(v2) => ValidatorFrozenIter::new(&v2.validator_frozen_proposals), + } + } + + #[inline] + pub fn gas_limit(&self) -> Gas { + match self { + Self::V1(v1) => v1.gas_limit, + Self::V2(v2) => v2.gas_limit, + } + } + + #[inline] + pub fn gas_used(&self) -> Gas { + match self { + Self::V1(v1) => v1.gas_used, + Self::V2(v2) => v2.gas_used, + } + } + + #[inline] + pub fn balance_burnt(&self) -> Balance { + match self { + Self::V1(v1) => v1.balance_burnt, + Self::V2(v2) => v2.balance_burnt, + } + } + } +} + +/// Information after chunk was processed, used to produce or check next chunk. +#[derive(Debug, PartialEq, BorshSerialize, BorshDeserialize, Clone, Eq)] +pub struct ChunkExtraV1 { + /// Post state root after applying give chunk. + pub state_root: StateRoot, + /// Root of merklizing results of receipts (transactions) execution. + pub outcome_root: CryptoHash, + /// Validator proposals produced by given chunk. + pub validator_power_proposals: Vec, + /// Validator proposals produced by given chunk. + pub validator_frozen_proposals: Vec, + /// Actually how much gas were used. + pub gas_used: Gas, + /// Gas limit, allows to increase or decrease limit based on expected time vs real time for computing the chunk. + pub gas_limit: Gas, + /// Total balance burnt after processing the current chunk. + pub balance_burnt: Balance, +} + +#[derive( + Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, arbitrary::Arbitrary, +)] +#[serde(untagged)] +pub enum BlockId { + Height(BlockHeight), + Hash(CryptoHash), +} + +pub type MaybeBlockId = Option; + +#[derive( + Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, arbitrary::Arbitrary, +)] +#[serde(rename_all = "snake_case")] +pub enum SyncCheckpoint { + Genesis, + EarliestAvailable, +} + +#[derive( + Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, arbitrary::Arbitrary, +)] +#[serde(rename_all = "snake_case")] +pub enum BlockReference { + BlockId(BlockId), + Finality(Finality), + SyncCheckpoint(SyncCheckpoint), +} + +impl BlockReference { + pub fn latest() -> Self { + Self::Finality(Finality::None) + } +} + +impl From for BlockReference { + fn from(block_id: BlockId) -> Self { + Self::BlockId(block_id) + } +} + +impl From for BlockReference { + fn from(finality: Finality) -> Self { + Self::Finality(finality) + } +} + +#[derive(Default, BorshSerialize, BorshDeserialize, Clone, Debug, PartialEq, Eq)] +pub struct ValidatorStats { + pub produced: NumBlocks, + pub expected: NumBlocks, +} + +#[derive(Debug, BorshSerialize, BorshDeserialize, PartialEq, Eq)] +pub struct BlockChunkValidatorStats { + pub block_stats: ValidatorStats, + pub chunk_stats: ValidatorStats, +} + +#[derive(serde::Deserialize, Debug, arbitrary::Arbitrary, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum EpochReference { + EpochId(EpochId), + BlockId(BlockId), + Latest, +} + +impl serde::Serialize for EpochReference { + fn serialize(&self, s: S) -> Result + where + S: serde::Serializer, + { + match self { + EpochReference::EpochId(epoch_id) => { + s.serialize_newtype_variant("EpochReference", 0, "epoch_id", epoch_id) + } + EpochReference::BlockId(block_id) => { + s.serialize_newtype_variant("EpochReference", 1, "block_id", block_id) + } + EpochReference::Latest => { + s.serialize_newtype_variant("EpochReference", 2, "latest", &()) + } + } + } +} + +/// Either an epoch id or latest block hash. When `EpochId` variant is used it +/// must be an identifier of a past epoch. When `BlockHeight` is used it must +/// be hash of the latest block in the current epoch. Using current epoch id +/// with `EpochId` or arbitrary block hash in past or present epochs will result +/// in errors. +#[derive(Debug)] +pub enum ValidatorInfoIdentifier { + EpochId(EpochId), + BlockHash(CryptoHash), +} + +/// Reasons for removing a validator from the validator set. +#[derive( + BorshSerialize, + BorshDeserialize, + serde::Serialize, + serde::Deserialize, + Clone, + Debug, + PartialEq, + Eq, +)] +pub enum ValidatorKickoutReason { + /// Slashed validators are kicked out. + Slashed, + /// Validator didn't produce enough blocks. + NotEnoughBlocks { produced: NumBlocks, expected: NumBlocks }, + /// Validator didn't produce enough chunks. + NotEnoughChunks { produced: NumBlocks, expected: NumBlocks }, + /// Validator unpowered themselves. + Unpowered, + /// Validator power is now below threshold + NotEnoughPower { + #[serde(with = "dec_format", rename = "power_u128")] + power: Power, + #[serde(with = "dec_format", rename = "power_threshold_u128")] + threshold: Power, + }, + /// Validator unfrozen themselves. + Unfrozen, + /// Validator frozen is now below threshold + NotEnoughFrozen { + #[serde(with = "dec_format", rename = "frozen_u128")] + frozen: Balance, + #[serde(with = "dec_format", rename = "frozen_threshold_u128")] + threshold: Balance, + }, + /// Enough power but is not chosen because of seat limits. + DidNotGetASeat, +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum TransactionOrReceiptId { + Transaction { transaction_hash: CryptoHash, sender_id: AccountId }, + Receipt { receipt_id: CryptoHash, receiver_id: AccountId }, +} + +/// Provides information about current epoch validators. +/// Used to break dependency between epoch manager and runtime. +pub trait EpochInfoProvider { + /// Get current power of a validator in the given epoch. + /// If the account is not a validator, returns `None`. + fn validator_power( + &self, + epoch_id: &EpochId, + last_block_hash: &CryptoHash, + account_id: &AccountId, + ) -> Result, EpochError>; + + /// Get the total power of the given epoch. + fn validator_total_power( + &self, + epoch_id: &EpochId, + last_block_hash: &CryptoHash, + ) -> Result; + + fn minimum_power(&self, prev_block_hash: &CryptoHash) -> Result; + + /// Get current frozen of a validator in the given epoch. + /// If the account is not a validator, returns `None`. + fn validator_frozen( + &self, + epoch_id: &EpochId, + last_block_hash: &CryptoHash, + account_id: &AccountId, + ) -> Result, EpochError>; + + /// Get the total frozen of the given epoch. + fn validator_total_frozen( + &self, + epoch_id: &EpochId, + last_block_hash: &CryptoHash, + ) -> Result; + + fn minimum_frozen(&self, prev_block_hash: &CryptoHash) -> Result; +} + +/// Mode of the trie cache. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum TrieCacheMode { + /// In this mode we put each visited node to LRU cache to optimize performance. + /// Presence of any exact node is not guaranteed. + CachingShard, + /// In this mode we put each visited node to the chunk cache which is a hash map. + /// This is needed to guarantee that all nodes for which we charged a touching trie node cost are retrieved from DB + /// only once during a single chunk processing. Such nodes remain in cache until the chunk processing is finished, + /// and thus users (potentially different) are not required to pay twice for retrieval of the same node. + CachingChunk, +} + +/// State changes for a range of blocks. +/// Expects that a block is present at most once in the list. +#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] +pub struct StateChangesForBlockRange { + pub blocks: Vec, +} + +/// State changes for a single block. +/// Expects that a shard is present at most once in the list of state changes. +#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] +pub struct StateChangesForBlock { + pub block_hash: CryptoHash, + pub state_changes: Vec, +} + +/// Key and value of a StateChanges column. +#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)] +pub struct StateChangesForShard { + pub shard_id: ShardId, + pub state_changes: Vec, +} + +#[cfg(test)] +mod tests { + use unc_crypto::{KeyType, PublicKey}; + use unc_primitives_core::types::{Balance, Power}; + + use super::validator_power::ValidatorPower; + + fn new_validator_power(power: Power) -> ValidatorPower { + ValidatorPower::new( + "test_account".parse().unwrap(), + PublicKey::empty(KeyType::ED25519), + power, + ) + } + + #[test] + fn test_validator_power_num_mandates() { + assert_eq!(new_validator_power(0).num_mandates(5), 0); + assert_eq!(new_validator_power(10).num_mandates(5), 2); + assert_eq!(new_validator_power(12).num_mandates(5), 2); + } + + #[test] + fn test_validator_partial_mandate_weight() { + assert_eq!(new_validator_power(0).partial_mandate_weight(5), 0); + assert_eq!(new_validator_power(10).partial_mandate_weight(5), 0); + assert_eq!(new_validator_power(12).partial_mandate_weight(5), 2); + } +} \ No newline at end of file diff --git a/core/primitives/src/upgrade_schedule.rs b/core/primitives/src/upgrade_schedule.rs new file mode 100644 index 000000000..8847e73af --- /dev/null +++ b/core/primitives/src/upgrade_schedule.rs @@ -0,0 +1,245 @@ +use chrono::{DateTime, NaiveDateTime, ParseError, Utc}; +use unc_primitives_core::types::ProtocolVersion; +use std::env; + +/// Defines the point in time after which validators are expected to vote on the +/// new protocol version. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct ProtocolUpgradeVotingSchedule { + timestamp: chrono::DateTime, +} + +impl Default for ProtocolUpgradeVotingSchedule { + fn default() -> Self { + Self { timestamp: DateTime::::from_naive_utc_and_offset(Default::default(), Utc) } + } +} + +impl ProtocolUpgradeVotingSchedule { + pub fn is_in_future(&self) -> bool { + chrono::Utc::now() < self.timestamp + } + + pub fn timestamp(&self) -> i64 { + self.timestamp.timestamp() + } + + /// This method creates an instance of the ProtocolUpgradeVotingSchedule. + /// + /// It will first check if the unc_TESTS_IMMEDIATE_PROTOCOL_UPGRADE is + /// set in the environment and if so return the immediate upgrade schedule. + /// This should only be used in tests, in particular in tests the in some + /// way test uncd upgrades. + /// + /// Otherwise it will parse the given string and return the corresponding + /// upgrade schedule. + pub fn from_env_or_str(s: &str) -> Result { + let immediate_upgrade = env::var("unc_TESTS_IMMEDIATE_PROTOCOL_UPGRADE"); + if let Ok(_) = immediate_upgrade { + tracing::warn!("Setting immediate protocol upgrade. This is fine in tests but should be avoided otherwise"); + return Ok(Self::default()); + } + + Ok(Self { + timestamp: DateTime::::from_naive_utc_and_offset( + NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S")?, + Utc, + ), + }) + } +} + +pub(crate) fn get_protocol_version_internal( + // Protocol version that will be used in the next epoch. + next_epoch_protocol_version: ProtocolVersion, + // Latest protocol version supported by this client. + client_protocol_version: ProtocolVersion, + // Point in time when voting for client_protocol_version version is expected + // to start. Use `Default::default()` to start voting immediately. + voting_start: ProtocolUpgradeVotingSchedule, +) -> ProtocolVersion { + if next_epoch_protocol_version >= client_protocol_version { + client_protocol_version + } else if voting_start.is_in_future() { + // Don't announce support for the latest protocol version yet. + next_epoch_protocol_version + } else { + // The time has passed, announce the latest supported protocol version. + client_protocol_version + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // The tests call `get_protocol_version_internal()` with the following parameters: + // No schedule: (X-2,X), (X,X), (X+2,X) + // Before the scheduled upgrade: (X-2,X), (X,X), (X+2,X) + // After the scheduled upgrade: (X-2,X), (X,X), (X+2,X) + + #[test] + fn test_no_upgrade_schedule() { + // As no protocol upgrade voting schedule is set, always return the version supported by the client. + + let client_protocol_version = 100; + assert_eq!( + client_protocol_version, + get_protocol_version_internal( + client_protocol_version - 2, + client_protocol_version, + Default::default(), + ) + ); + assert_eq!( + client_protocol_version, + get_protocol_version_internal( + client_protocol_version, + client_protocol_version, + Default::default() + ) + ); + assert_eq!( + client_protocol_version, + get_protocol_version_internal( + client_protocol_version + 2, + client_protocol_version, + Default::default(), + ) + ); + } + + #[test] + fn test_none_upgrade_schedule() { + // As no protocol upgrade voting schedule is set, always return the version supported by the client. + + let client_protocol_version = 100; + + assert_eq!( + client_protocol_version, + get_protocol_version_internal( + client_protocol_version - 2, + client_protocol_version, + Default::default(), + ) + ); + assert_eq!( + client_protocol_version, + get_protocol_version_internal( + client_protocol_version, + client_protocol_version, + Default::default(), + ) + ); + assert_eq!( + client_protocol_version, + get_protocol_version_internal( + client_protocol_version + 2, + client_protocol_version, + Default::default(), + ) + ); + } + + #[test] + fn test_before_scheduled_time() { + let client_protocol_version = 100; + let schedule = + ProtocolUpgradeVotingSchedule::from_env_or_str("2050-01-01 00:00:00").unwrap(); + + // The client supports a newer version than the version of the next epoch. + // Upgrade voting will start in the far future, therefore don't announce the newest supported version. + let next_epoch_protocol_version = client_protocol_version - 2; + assert_eq!( + next_epoch_protocol_version, + get_protocol_version_internal( + next_epoch_protocol_version, + client_protocol_version, + schedule, + ) + ); + + // An upgrade happened before the scheduled time. + let next_epoch_protocol_version = client_protocol_version; + assert_eq!( + next_epoch_protocol_version, + get_protocol_version_internal( + next_epoch_protocol_version, + client_protocol_version, + schedule, + ) + ); + + // Several upgrades happened before the scheduled time. Announce only the currently supported protocol version. + let next_epoch_protocol_version = client_protocol_version + 2; + assert_eq!( + client_protocol_version, + get_protocol_version_internal( + next_epoch_protocol_version, + client_protocol_version, + schedule, + ) + ); + } + + #[test] + fn test_after_scheduled_time() { + let client_protocol_version = 100; + let schedule = + ProtocolUpgradeVotingSchedule::from_env_or_str("1900-01-01 00:00:00").unwrap(); + + // Regardless of the protocol version of the next epoch, return the version supported by the client. + assert_eq!( + client_protocol_version, + get_protocol_version_internal( + client_protocol_version - 2, + client_protocol_version, + schedule, + ) + ); + assert_eq!( + client_protocol_version, + get_protocol_version_internal( + client_protocol_version, + client_protocol_version, + schedule, + ) + ); + assert_eq!( + client_protocol_version, + get_protocol_version_internal( + client_protocol_version + 2, + client_protocol_version, + schedule, + ) + ); + } + + #[test] + fn test_parse() { + assert!(ProtocolUpgradeVotingSchedule::from_env_or_str("2001-02-03 23:59:59").is_ok()); + assert!(ProtocolUpgradeVotingSchedule::from_env_or_str("123").is_err()); + } + + #[test] + fn test_is_in_future() { + assert!(ProtocolUpgradeVotingSchedule::from_env_or_str("2999-02-03 23:59:59") + .unwrap() + .is_in_future()); + assert!(!ProtocolUpgradeVotingSchedule::from_env_or_str("1999-02-03 23:59:59") + .unwrap() + .is_in_future()); + } + + #[test] + fn test_env_overwrite() { + // The immediate protocol upgrade needs to be set for this test to pass in + // the release branch where the protocol upgrade date is set. + std::env::set_var("unc_TESTS_IMMEDIATE_PROTOCOL_UPGRADE", "1"); + + assert_eq!( + ProtocolUpgradeVotingSchedule::from_env_or_str("2999-02-03 23:59:59").unwrap(), + ProtocolUpgradeVotingSchedule::default() + ); + } +} diff --git a/core/primitives/src/utils.rs b/core/primitives/src/utils.rs new file mode 100644 index 000000000..6ace24d47 --- /dev/null +++ b/core/primitives/src/utils.rs @@ -0,0 +1,604 @@ +use std::cmp::max; +use std::convert::AsRef; +use std::fmt; + +use chrono; +use chrono::DateTime; +use rand::distributions::Alphanumeric; +use rand::{thread_rng, Rng}; +use serde; + +use crate::hash::{hash, CryptoHash}; +use crate::receipt::Receipt; +use crate::transaction::SignedTransaction; +use crate::types::{NumSeats, NumShards, ShardId}; +use crate::version::{ + ProtocolVersion, CORRECT_RANDOM_VALUE_PROTOCOL_VERSION, CREATE_HASH_PROTOCOL_VERSION, + CREATE_RECEIPT_ID_SWITCH_TO_CURRENT_BLOCK_VERSION, +}; + +use unc_crypto::{ED25519PublicKey, Secp256K1PublicKey}; +use unc_primitives_core::account::id::{AccountId, AccountType}; + +use std::mem::size_of; +use std::ops::Deref; + +pub mod min_heap; + +/// Number of nano seconds in a second. +const NS_IN_SECOND: u64 = 1_000_000_000; + +/// A data structure for tagging data as already being validated to prevent +/// redundant work. +/// +/// # Example +/// +/// ```ignore +/// struct Foo; +/// struct Error; +/// +/// /// Performs expensive validation of `foo`. +/// fn validate_foo(foo: &Foo) -> Result; +/// +/// fn do_stuff(foo: Foo) { +/// let foo = MaybeValidated::from(foo); +/// do_stuff_with_foo(&foo); +/// if foo.validate_with(validate_foo) { +/// println!("^_^"); +/// } +/// } +/// +/// fn do_stuff_with_foo(foo: &MaybeValidated { + validated: std::cell::Cell, + payload: T, +} + +impl MaybeValidated { + /// Creates new MaybeValidated object marking payload as validated. No + /// verification is performed; it’s caller’s responsibility to make sure the + /// payload has indeed been validated. + /// + /// # Example + /// + /// ``` + /// use unc_primitives::utils::MaybeValidated; + /// + /// let value = MaybeValidated::from_validated(42); + /// assert!(value.is_validated()); + /// assert_eq!(Ok(true), value.validate_with::<(), _>(|_| panic!())); + /// ``` + pub fn from_validated(payload: T) -> Self { + Self { validated: std::cell::Cell::new(true), payload } + } + + /// Validates payload with given `validator` function and returns result of + /// the validation. If payload has already been validated returns + /// `Ok(true)`. Note that this method changes the internal validated flag + /// so it’s probably incorrect to call it with different `validator` + /// functions. + /// + /// # Example + /// + /// ``` + /// use unc_primitives::utils::MaybeValidated; + /// + /// let value = MaybeValidated::from(42); + /// assert_eq!(Err(()), value.validate_with(|_| Err(()))); + /// assert_eq!(Ok(false), value.validate_with::<(), _>(|v| Ok(*v == 24))); + /// assert!(!value.is_validated()); + /// assert_eq!(Ok(true), value.validate_with::<(), _>(|v| Ok(*v == 42))); + /// assert!(value.is_validated()); + /// assert_eq!(Ok(true), value.validate_with::<(), _>(|_| panic!())); + /// ``` + pub fn validate_with Result>( + &self, + validator: F, + ) -> Result { + if self.validated.get() { + Ok(true) + } else { + let res = validator(&self.payload); + self.validated.set(*res.as_ref().unwrap_or(&false)); + res + } + } + + /// Marks the payload as valid. No verification is performed; it’s caller’s + /// responsibility to make sure the payload has indeed been validated. + pub fn mark_as_valid(&self) { + self.validated.set(true); + } + + /// Applies function to the payload (whether it’s been validated or not) and + /// returns new object with result of the function as payload. Validated + /// state is not changed. + /// + /// # Example + /// + /// ``` + /// use unc_primitives::utils::MaybeValidated; + /// + /// let value = MaybeValidated::from(42); + /// assert_eq!("42", value.map(|v| v.to_string()).into_inner()); + /// ``` + pub fn map U>(self, validator: F) -> MaybeValidated { + MaybeValidated { validated: self.validated, payload: validator(self.payload) } + } + + /// Returns a new object storing reference to this object’s payload. Note + /// that the two objects do not share the validated state so calling + /// `validate_with` on one of them does not affect the other. + /// + /// # Example + /// + /// ``` + /// use unc_primitives::utils::MaybeValidated; + /// + /// let value = MaybeValidated::from(42); + /// let value_as_ref = value.as_ref(); + /// assert_eq!(Ok(true), value_as_ref.validate_with::<(), _>(|&&v| Ok(v == 42))); + /// assert!(value_as_ref.is_validated()); + /// assert!(!value.is_validated()); + /// ``` + pub fn as_ref(&self) -> MaybeValidated<&T> { + MaybeValidated { validated: self.validated.clone(), payload: &self.payload } + } + + /// Returns whether the payload has been validated. + pub fn is_validated(&self) -> bool { + self.validated.get() + } + + /// Extracts the payload whether or not it’s been validated. + pub fn into_inner(self) -> T { + self.payload + } + + /// Returns a reference to the payload + pub fn get_inner(&self) -> &T { + &self.payload + } +} + +impl From for MaybeValidated { + /// Creates new MaybeValidated object marking payload as not validated. + fn from(payload: T) -> Self { + Self { validated: std::cell::Cell::new(false), payload } + } +} + +impl Deref for MaybeValidated { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.payload + } +} + +pub fn get_block_shard_id(block_hash: &CryptoHash, shard_id: ShardId) -> Vec { + let mut res = Vec::with_capacity(40); + res.extend_from_slice(block_hash.as_ref()); + res.extend_from_slice(&shard_id.to_le_bytes()); + res +} + +pub fn get_block_shard_id_rev( + key: &[u8], +) -> Result<(CryptoHash, ShardId), Box> { + if key.len() != 40 { + return Err( + std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid key length").into() + ); + } + let (block_hash_bytes, shard_id_bytes) = key.split_at(32); + let block_hash = CryptoHash::try_from(block_hash_bytes)?; + let shard_id = ShardId::from_le_bytes(shard_id_bytes.try_into()?); + Ok((block_hash, shard_id)) +} + +pub fn get_outcome_id_block_hash(outcome_id: &CryptoHash, block_hash: &CryptoHash) -> Vec { + let mut res = Vec::with_capacity(64); + res.extend_from_slice(outcome_id.as_ref()); + res.extend_from_slice(block_hash.as_ref()); + res +} + +pub fn get_outcome_id_block_hash_rev(key: &[u8]) -> std::io::Result<(CryptoHash, CryptoHash)> { + if key.len() != 64 { + return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid key length")); + } + let outcome_id = CryptoHash::try_from(&key[..32]).unwrap(); + let block_hash = CryptoHash::try_from(&key[32..]).unwrap(); + Ok((outcome_id, block_hash)) +} + +/// Creates a new Receipt ID from a given signed transaction and a block hash. +/// This method is backward compatible, so it takes the current protocol version. +pub fn create_receipt_id_from_transaction( + protocol_version: ProtocolVersion, + signed_transaction: &SignedTransaction, + prev_block_hash: &CryptoHash, + block_hash: &CryptoHash, +) -> CryptoHash { + create_hash_upgradable( + protocol_version, + &signed_transaction.get_hash(), + prev_block_hash, + block_hash, + 0, + ) +} + +/// Creates a new Receipt ID from a given receipt, a block hash and a new receipt index. +/// This method is backward compatible, so it takes the current protocol version. +pub fn create_receipt_id_from_receipt( + protocol_version: ProtocolVersion, + receipt: &Receipt, + prev_block_hash: &CryptoHash, + block_hash: &CryptoHash, + receipt_index: usize, +) -> CryptoHash { + create_hash_upgradable( + protocol_version, + &receipt.receipt_id, + prev_block_hash, + block_hash, + receipt_index as u64, + ) +} + +/// Creates a new action_hash from a given receipt, a block hash and an action index. +/// This method is backward compatible, so it takes the current protocol version. +pub fn create_action_hash( + protocol_version: ProtocolVersion, + receipt: &Receipt, + prev_block_hash: &CryptoHash, + block_hash: &CryptoHash, + action_index: usize, +) -> CryptoHash { + // Action hash uses the same input as a new receipt ID, so to avoid hash conflicts we use the + // salt starting from the `u64` going backward. + let salt = u64::MAX.wrapping_sub(action_index as u64); + create_hash_upgradable(protocol_version, &receipt.receipt_id, prev_block_hash, block_hash, salt) +} + +/// Creates a new `data_id` from a given action hash, a block hash and a data index. +/// This method is backward compatible, so it takes the current protocol version. +pub fn create_data_id( + protocol_version: ProtocolVersion, + action_hash: &CryptoHash, + prev_block_hash: &CryptoHash, + block_hash: &CryptoHash, + data_index: usize, +) -> CryptoHash { + create_hash_upgradable( + protocol_version, + action_hash, + prev_block_hash, + block_hash, + data_index as u64, + ) +} + +/// Creates a unique random seed to be provided to `VMContext` from a give `action_hash` and +/// a given `random_seed`. +/// This method is backward compatible, so it takes the current protocol version. +pub fn create_random_seed( + protocol_version: ProtocolVersion, + action_hash: CryptoHash, + random_seed: CryptoHash, +) -> Vec { + let res = if protocol_version < CORRECT_RANDOM_VALUE_PROTOCOL_VERSION { + action_hash + } else if protocol_version < CREATE_HASH_PROTOCOL_VERSION { + random_seed + } else { + // Generates random seed from random_seed and action_hash. + // Since every action hash is unique, the seed will be unique per receipt and even + // per action within a receipt. + const BYTES_LEN: usize = size_of::() + size_of::(); + let mut bytes: Vec = Vec::with_capacity(BYTES_LEN); + bytes.extend_from_slice(action_hash.as_ref()); + bytes.extend_from_slice(random_seed.as_ref()); + hash(&bytes) + }; + res.as_ref().to_vec() +} + +/// Creates a new CryptoHash ID based on the protocol version. +/// Before `CREATE_HASH_PROTOCOL_VERSION` it uses `create_nonce_with_nonce` with +/// just `base` and `salt`. But after `CREATE_HASH_PROTOCOL_VERSION` it uses +/// `extra_hash` in addition to the `base` and `salt`. +/// E.g. this `extra_hash` can be a block hash to distinguish receipts between forks. +fn create_hash_upgradable( + protocol_version: ProtocolVersion, + base: &CryptoHash, + extra_hash_old: &CryptoHash, + extra_hash: &CryptoHash, + salt: u64, +) -> CryptoHash { + if protocol_version < CREATE_HASH_PROTOCOL_VERSION { + create_nonce_with_nonce(base, salt) + } else { + const BYTES_LEN: usize = + size_of::() + size_of::() + size_of::(); + let mut bytes: Vec = Vec::with_capacity(BYTES_LEN); + bytes.extend_from_slice(base.as_ref()); + let extra_hash_used = + if protocol_version < CREATE_RECEIPT_ID_SWITCH_TO_CURRENT_BLOCK_VERSION { + extra_hash_old + } else { + extra_hash + }; + bytes.extend_from_slice(extra_hash_used.as_ref()); + bytes.extend(index_to_bytes(salt)); + hash(&bytes) + } +} + +/// Deprecated. Please use `create_hash_upgradable` +fn create_nonce_with_nonce(base: &CryptoHash, salt: u64) -> CryptoHash { + let mut nonce: Vec = base.as_ref().to_owned(); + nonce.extend(index_to_bytes(salt)); + hash(&nonce) +} + +pub fn index_to_bytes(index: u64) -> [u8; 8] { + index.to_le_bytes() +} + +/// A wrapper around Option that provides native Display trait. +/// Simplifies propagating automatic Display trait on parent structs. +pub struct DisplayOption(pub Option); + +impl fmt::Display for DisplayOption { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Some(ref v) => write!(f, "Some({})", v), + None => write!(f, "None"), + } + } +} + +impl DisplayOption { + pub fn into(self) -> Option { + self.0 + } +} + +impl AsRef> for DisplayOption { + fn as_ref(&self) -> &Option { + &self.0 + } +} + +impl From> for DisplayOption { + fn from(o: Option) -> Self { + DisplayOption(o) + } +} + +/// Macro to either return value if the result is Ok, or exit function logging error. +#[macro_export] +macro_rules! unwrap_or_return { + ($obj: expr, $ret: expr) => { + match $obj { + Ok(value) => value, + Err(err) => { + tracing::error!(target: "client", "Unwrap error: {}", err); + return $ret; + } + } + }; + ($obj: expr) => { + match $obj { + Ok(value) => value, + Err(err) => { + tracing::error!(target: "client", "Unwrap error: {}", err); + return; + } + } + }; +} + +/// Converts timestamp in ns into DateTime UTC time. +pub fn from_timestamp(timestamp: u64) -> DateTime { + let secs = (timestamp / NS_IN_SECOND) as i64; + let nsecs = (timestamp % NS_IN_SECOND) as u32; + DateTime::from_timestamp(secs, nsecs).unwrap() +} + +/// Converts DateTime UTC time into timestamp in ns. +pub fn to_timestamp(time: DateTime) -> u64 { + // The unwrap will be safe for all dates between 1678 and 2261. + time.timestamp_nanos_opt().unwrap() as u64 +} + +/// Compute number of seats per shard for given total number of seats and number of shards. +pub fn get_num_seats_per_shard(num_shards: NumShards, num_seats: NumSeats) -> Vec { + (0..num_shards) + .map(|shard_id| { + let remainder = + num_seats.checked_rem(num_shards).expect("num_shards ≠ 0 is guaranteed here"); + let quotient = + num_seats.checked_div(num_shards).expect("num_shards ≠ 0 is guaranteed here"); + let num = quotient + .checked_add(if shard_id < remainder { 1 } else { 0 }) + .expect("overflow is impossible here"); + max(num, 1) + }) + .collect() +} + +/// Generate random string of given length +pub fn generate_random_string(len: usize) -> String { + let bytes = thread_rng().sample_iter(&Alphanumeric).take(len).collect(); + String::from_utf8(bytes).unwrap() +} + +pub struct Serializable<'a, T>(&'a T); + +impl<'a, T> fmt::Display for Serializable<'a, T> +where + T: serde::Serialize, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", serde_json::to_string(&self.0).unwrap()) + } +} + +/// Wrap an object that implements Serialize into another object +/// that implements Display. When used display in this object +/// it shows its json representation. It is used to display complex +/// objects using tracing. +/// +/// tracing::debug!(target: "diagnostic", value=%ser(&object)); +pub fn ser(object: &T) -> Serializable<'_, T> +where + T: serde::Serialize, +{ + Serializable(object) +} + +/// From `unc-account-id` version `1.0.0-alpha.2`, `is_implicit` returns true for ETH-implicit accounts. +/// This function is a wrapper for `is_implicit` method so that we can easily differentiate its behavior +/// based on whether ETH-implicit accounts are enabled. +pub fn account_is_implicit(account_id: &AccountId, eth_implicit_accounts_enabled: bool) -> bool { + if eth_implicit_accounts_enabled { + account_id.get_account_type().is_implicit() + } else { + account_id.get_account_type() == AccountType::NearImplicitAccount + } +} + +/// Returns hex-encoded copy of the public key. +/// This is a NEAR-implicit account ID which can be controlled by the corresponding ED25519 private key. +pub fn derive_unc_implicit_account_id(public_key: &ED25519PublicKey) -> AccountId { + hex::encode(public_key).parse().unwrap() +} + +/// Returns '0x' + keccak256(public_key)[12:32].hex(). +/// This is an ETH-implicit account ID which can be controlled by the corresponding Secp256K1 private key. +pub fn derive_eth_implicit_account_id(public_key: &Secp256K1PublicKey) -> AccountId { + use sha3::Digest; + let pk_hash = sha3::Keccak256::digest(&public_key); + format!("0x{}", hex::encode(&pk_hash[12..32])).parse().unwrap() +} + +#[cfg(test)] +mod tests { + use super::*; + use unc_crypto::{KeyType, PublicKey}; + + #[test] + fn test_derive_unc_implicit_account_id() { + let public_key = PublicKey::from_seed(KeyType::ED25519, "test"); + let expected: AccountId = + "bb4dc639b212e075a751685b26bdcea5920a504181ff2910e8549742127092a0".parse().unwrap(); + let account_id = derive_unc_implicit_account_id(public_key.unwrap_as_ed25519()); + assert_eq!(account_id, expected); + } + + #[test] + fn test_derive_eth_implicit_account_id() { + let public_key = PublicKey::from_seed(KeyType::SECP256K1, "test"); + let expected: AccountId = "0x96791e923f8cf697ad9c3290f2c9059f0231b24c".parse().unwrap(); + let account_id = derive_eth_implicit_account_id(public_key.unwrap_as_secp256k1()); + assert_eq!(account_id, expected); + } + + #[test] + fn test_num_chunk_producers() { + for num_seats in 1..50 { + for num_shards in 1..50 { + let assignment = get_num_seats_per_shard(num_shards, num_seats); + assert_eq!(assignment.iter().sum::(), max(num_seats, num_shards)); + } + } + } + + #[test] + fn test_create_hash_upgradable() { + let base = hash(b"atata"); + let extra_base = hash(b"hohoho"); + let other_extra_base = hash(b"banana"); + let salt = 3; + assert_eq!( + create_nonce_with_nonce(&base, salt), + create_hash_upgradable( + CREATE_HASH_PROTOCOL_VERSION - 1, + &base, + &extra_base, + &extra_base, + salt, + ) + ); + assert_ne!( + create_nonce_with_nonce(&base, salt), + create_hash_upgradable( + CREATE_HASH_PROTOCOL_VERSION, + &base, + &extra_base, + &extra_base, + salt, + ) + ); + assert_ne!( + create_hash_upgradable( + CREATE_HASH_PROTOCOL_VERSION, + &base, + &extra_base, + &extra_base, + salt, + ), + create_hash_upgradable( + CREATE_HASH_PROTOCOL_VERSION, + &base, + &other_extra_base, + &other_extra_base, + salt, + ) + ); + assert_ne!( + create_hash_upgradable( + CREATE_RECEIPT_ID_SWITCH_TO_CURRENT_BLOCK_VERSION - 1, + &base, + &extra_base, + &other_extra_base, + salt, + ), + create_hash_upgradable( + CREATE_RECEIPT_ID_SWITCH_TO_CURRENT_BLOCK_VERSION, + &base, + &extra_base, + &other_extra_base, + salt, + ) + ); + assert_eq!( + create_hash_upgradable( + CREATE_RECEIPT_ID_SWITCH_TO_CURRENT_BLOCK_VERSION, + &base, + &extra_base, + &other_extra_base, + salt, + ), + create_hash_upgradable( + CREATE_RECEIPT_ID_SWITCH_TO_CURRENT_BLOCK_VERSION, + &base, + &other_extra_base, + &other_extra_base, + salt + ) + ); + } +} diff --git a/core/primitives/src/utils/min_heap.rs b/core/primitives/src/utils/min_heap.rs new file mode 100644 index 000000000..cf4b75713 --- /dev/null +++ b/core/primitives/src/utils/min_heap.rs @@ -0,0 +1,199 @@ +use std::cmp::Reverse; +use std::collections::binary_heap::{BinaryHeap, IntoIter}; + +/// Wrapper around `BinaryHeap` to be default min heap instead of max heap. +#[derive(Debug, Clone)] +pub struct MinHeap { + inner: BinaryHeap>, +} + +impl MinHeap { + pub fn push(&mut self, t: T) { + self.inner.push(Reverse(t)); + } + + pub fn pop(&mut self) -> Option { + self.inner.pop().map(|Reverse(t)| t) + } + + pub fn peek(&self) -> Option<&T> { + self.inner.peek().map(|Reverse(t)| t) + } + + pub fn peek_mut(&mut self) -> Option> { + self.inner.peek_mut().map(PeekMut) + } + + pub fn len(&self) -> usize { + self.inner.len() + } + + pub fn extend>(&mut self, iter: I) { + self.inner.extend(iter.into_iter().map(|item| Reverse(item))) + } +} + +impl Default for MinHeap +where + T: Default + Eq + PartialEq + PartialOrd + Ord, +{ + fn default() -> Self { + Self { inner: BinaryHeap::default() } + } +} + +impl FromIterator for MinHeap { + fn from_iter>(iter: I) -> Self { + let inner = iter.into_iter().map(|t| Reverse(t)).collect(); + Self { inner } + } +} + +impl IntoIterator for MinHeap { + type Item = T; + type IntoIter = std::iter::Map>, fn(Reverse) -> T>; + + fn into_iter(self) -> Self::IntoIter { + self.inner.into_iter().map(|Reverse(t)| t) + } +} + +pub struct PeekMut<'a, T: std::cmp::Ord>(std::collections::binary_heap::PeekMut<'a, Reverse>); + +impl<'a, T: std::cmp::Ord> PeekMut<'a, T> { + pub fn pop(this: Self) -> T { + std::collections::binary_heap::PeekMut::pop(this.0).0 + } +} + +impl<'a, T: std::cmp::Ord> std::ops::Deref for PeekMut<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + &self.0 .0 + } +} + +impl<'a, T: std::cmp::Ord> std::ops::DerefMut for PeekMut<'a, T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.0 .0 + } +} + +#[cfg(test)] +mod tests { + use super::{MinHeap, PeekMut}; + + #[test] + fn test_push_pop() { + // Elements pushed into the heap should be popped back in increasing order. + let mut heap = MinHeap::default(); + + heap.push(7); + heap.push(11); + heap.push(4); + heap.push(1); + + assert_eq!(heap.len(), 4); + assert_eq!(heap.pop(), Some(1)); + assert_eq!(heap.pop(), Some(4)); + assert_eq!(heap.pop(), Some(7)); + assert_eq!(heap.pop(), Some(11)); + assert_eq!(heap.len(), 0); + assert_eq!(heap.pop(), None); + } + + #[test] + fn test_push_pop_push() { + // Elements pushed into the heap should be popped back in increasing order. + let mut heap = MinHeap::default(); + + heap.push(8); + heap.push(16); + heap.push(2); + heap.push(1); + + assert_eq!(heap.len(), 4); + assert_eq!(heap.pop(), Some(1)); + assert_eq!(heap.pop(), Some(2)); + assert_eq!(heap.len(), 2); + + heap.push(4); + heap.push(32); + + assert_eq!(heap.len(), 4); + assert_eq!(heap.pop(), Some(4)); + assert_eq!(heap.pop(), Some(8)); + assert_eq!(heap.pop(), Some(16)); + assert_eq!(heap.pop(), Some(32)); + assert_eq!(heap.pop(), None); + } + + #[test] + fn test_collect_pop() { + // Elements collected into a heap should be popped back in increasing order. + let mut heap: MinHeap = [9, 3, 100, 10, 5].iter().copied().collect(); + + assert_eq!(heap.len(), 5); + assert_eq!(heap.pop(), Some(3)); + assert_eq!(heap.pop(), Some(5)); + assert_eq!(heap.pop(), Some(9)); + assert_eq!(heap.pop(), Some(10)); + assert_eq!(heap.pop(), Some(100)); + assert_eq!(heap.pop(), None); + } + + #[test] + fn test_peek() { + // Peek should reveal the smallest element, but not remove it + let mut heap = MinHeap::default(); + + heap.push(37); + heap.push(17); + heap.push(101); + + assert_eq!(Some(&17), heap.peek()); + assert_eq!(Some(17), heap.pop()); + + assert_eq!(Some(&37), heap.peek()); + assert_eq!(Some(37), heap.pop()); + + assert_eq!(Some(&101), heap.peek()); + assert_eq!(Some(101), heap.pop()); + + assert_eq!(None, heap.peek()); + assert_eq!(None, heap.pop()); + } + + #[test] + fn test_peek_mut() { + // Peek should reveal the smallest element, but not remove it + let mut heap = MinHeap::default(); + + heap.push(37); + heap.push(17); + heap.push(101); + + let top = heap.peek_mut().unwrap(); + assert_eq!(17, *top); + PeekMut::pop(top); + assert_eq!(Some(&37), heap.peek()); + + let mut top = heap.peek_mut().unwrap(); + assert_eq!(37, *top); + *top = 42; + // Drop so `top` no longer holds exclusive reference to `heap`. + core::mem::drop(top); + assert_eq!(Some(&42), heap.peek()); + + let mut top = heap.peek_mut().unwrap(); + assert_eq!(42, *top); + *top = 111; + // This time dropping will also update position of the element in the + // heap. + core::mem::drop(top); + + assert_eq!(Some(101), heap.pop()); + assert_eq!(Some(111), heap.pop()); + } +} diff --git a/core/primitives/src/validator_mandates.rs b/core/primitives/src/validator_mandates.rs new file mode 100644 index 000000000..889ce3b96 --- /dev/null +++ b/core/primitives/src/validator_mandates.rs @@ -0,0 +1,529 @@ +use std::collections::HashMap; + +use crate::types::{ValidatorId}; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_primitives_core::types::Balance; +use rand::{seq::SliceRandom, Rng}; +use crate::types::validator_power_and_frozen::ValidatorPowerAndFrozen; + +/// Represents the configuration of [`ValidatorMandates`]. Its parameters are expected to remain +/// valid for one epoch. +#[derive( + BorshSerialize, BorshDeserialize, Default, Copy, Clone, Debug, PartialEq, Eq, serde::Serialize, +)] +pub struct ValidatorMandatesConfig { + /// The amount of stake that corresponds to one mandate. + stake_per_mandate: Balance, + /// The minimum number of mandates required per shard. + min_mandates_per_shard: usize, + /// The number of shards for the referenced epoch. + num_shards: usize, +} + +impl ValidatorMandatesConfig { + /// Constructs a new configuration. + /// + /// # Panics + /// + /// Panics in the following cases: + /// + /// - If `stake_per_mandate` is 0 as this would lead to division by 0. + /// - If `num_shards` is zero. + pub fn new( + stake_per_mandate: Balance, + min_mandates_per_shard: usize, + num_shards: usize, + ) -> Self { + assert!(stake_per_mandate > 0, "stake_per_mandate of 0 would lead to division by 0"); + assert!(num_shards > 0, "there should be at least one shard"); + Self { stake_per_mandate, min_mandates_per_shard, num_shards } + } +} + +/// The mandates for a set of validators given a [`ValidatorMandatesConfig`]. +/// +/// A mandate is a liability for a validator to validate a shard. Depending on its stake and the +/// `stake_per_mandate` specified in `ValidatorMandatesConfig`, a validator may hold multiple +/// mandates. Each mandate may be assigned to a different shard. The assignment of mandates to +/// shards is calculated with [`Self::sample`], typically at every height. +/// +/// See #9983 for context and links to resources that introduce mandates. +#[derive( + BorshSerialize, BorshDeserialize, Default, Clone, Debug, PartialEq, Eq, serde::Serialize, +)] +pub struct ValidatorMandates { + /// The configuration applied to the mandates. + config: ValidatorMandatesConfig, + /// Each element represents a validator mandate held by the validator with the given id. + /// + /// The id of a validator who holds `n >= 0` mandates occurs `n` times in the vector. + mandates: Vec, + /// Each element represents a partial validator mandate held by the validator with the given id. + /// For example, an element `(1, 42)` represents the partial mandate of the validator with id 1 + /// which has a weight of 42. + /// + /// Validators whose stake can be distributed across mandates without remainder are not + /// represented in this vector. + partials: Vec<(ValidatorId, Balance)>, +} + +impl ValidatorMandates { + /// Initiates mandates corresponding to the provided `validators`. The validators must be sorted + /// by id in ascending order, so the validator with `ValidatorId` equal to `i` is given by + /// `validators[i]`. + /// + /// Only full mandates are assigned, partial mandates are dropped. For example, when the stake + /// required for a mandate is 5 and a validator has staked 12, then it will obtain 2 mandates. + pub fn new(config: ValidatorMandatesConfig, validators: &[ValidatorPowerAndFrozen]) -> Self { + let num_mandates_per_validator: Vec = + validators.iter().map(|v| v.num_mandates(config.stake_per_mandate)).collect(); + let num_total_mandates = + num_mandates_per_validator.iter().map(|&num| usize::from(num)).sum(); + let mut mandates: Vec = Vec::with_capacity(num_total_mandates); + + for i in 0..validators.len() { + for _ in 0..num_mandates_per_validator[i] { + // Each validator's position corresponds to its id. + mandates.push(i as ValidatorId); + } + } + + let required_mandates = config.min_mandates_per_shard * config.num_shards; + if mandates.len() < required_mandates { + // TODO(#10014) dynamically lower `stake_per_mandate` to reach enough mandates + panic!( + "not enough validator mandates: got {}, need {}", + mandates.len(), + required_mandates + ); + } + + // Not counting partials towards `required_mandates` as the weight of partials and its + // distribution across shards may vary widely. + // + // Construct vector with capacity as most likely some validators' stake will not be evenly + // divided by `config.stake_per_mandate`, i.e. some validators will have partials. + let mut partials = Vec::with_capacity(validators.len()); + for i in 0..validators.len() { + let partial_weight = validators[i].partial_mandate_weight(config.stake_per_mandate); + if partial_weight > 0 { + partials.push((i as ValidatorId, partial_weight)); + } + } + + Self { config, mandates, partials } + } + + /// Returns a validator assignment obtained by shuffling mandates and assigning them to shards. + /// Shard ids are shuffled as well in this process to avoid a bias lower shard ids, see + /// [`ShuffledShardIds`]. + /// + /// It clones mandates since [`ValidatorMandates`] is supposed to be valid for an epoch, while a + /// new assignment is calculated at every height. + pub fn sample(&self, rng: &mut R) -> ValidatorMandatesAssignment + where + R: Rng + ?Sized, + { + // Shuffling shard ids to avoid a bias towards lower ids, see [`ShuffledShardIds`]. We + // do two separate shuffes for full and partial mandates to reduce the likelihood of + // assigning fewer full _and_ partial mandates to the _same_ shard. + let shard_ids_for_mandates = ShuffledShardIds::new(rng, self.config.num_shards); + let shard_ids_for_partials = ShuffledShardIds::new(rng, self.config.num_shards); + + let shuffled_mandates = self.shuffled_mandates(rng); + let shuffled_partials = self.shuffled_partials(rng); + + // Distribute shuffled mandates and partials across shards. For each shard with `shard_id` + // in `[0, num_shards)`, we take the elements of the vector with index `i` such that `i % + // num_shards == shard_id`. + // + // Assume, for example, there are 10 mandates and 4 shards. Then for `shard_id = 1` we + // collect the mandates with indices 1, 5, and 9. + let mut mandates_per_shard: ValidatorMandatesAssignment = + vec![HashMap::new(); self.config.num_shards]; + for shard_id in 0..self.config.num_shards { + // Achieve shard id shuffling by writing to the position of the alias of `shard_id`. + let mandates_assignment = + &mut mandates_per_shard[shard_ids_for_mandates.get_alias(shard_id)]; + + // For the current `shard_id`, collect mandates with index `i` such that + // `i % num_shards == shard_id`. + for idx in (shard_id..shuffled_mandates.len()).step_by(self.config.num_shards) { + let validator_id = shuffled_mandates[idx]; + mandates_assignment + .entry(validator_id) + .and_modify(|assignment_weight| { + assignment_weight.num_mandates += 1; + }) + .or_insert(AssignmentWeight::new(1, 0)); + } + + // Achieve shard id shuffling by writing to the position of the alias of `shard_id`. + let partials_assignment = + &mut mandates_per_shard[shard_ids_for_partials.get_alias(shard_id)]; + + // For the current `shard_id`, collect partials with index `i` such that + // `i % num_shards == shard_id`. + for idx in (shard_id..shuffled_partials.len()).step_by(self.config.num_shards) { + let (validator_id, partial_weight) = shuffled_partials[idx]; + partials_assignment + .entry(validator_id) + .and_modify(|assignment_weight| { + assignment_weight.partial_weight += partial_weight; + }) + .or_insert(AssignmentWeight::new(0, partial_weight)); + } + } + + mandates_per_shard + } + + /// Clones the contained mandates and shuffles them. Cloning is required as a shuffle happens at + /// every height while the `ValidatorMandates` are to be valid for an epoch. + fn shuffled_mandates(&self, rng: &mut R) -> Vec + where + R: Rng + ?Sized, + { + let mut shuffled_mandates = self.mandates.clone(); + shuffled_mandates.shuffle(rng); + shuffled_mandates + } + + /// Clones the contained partials and shuffles them. Cloning is required as a shuffle happens at + /// every height while the `ValidatorMandates` are to be valid for an epoch. + fn shuffled_partials(&self, rng: &mut R) -> Vec<(ValidatorId, Balance)> + where + R: Rng + ?Sized, + { + let mut shuffled_partials = self.partials.clone(); + shuffled_partials.shuffle(rng); + shuffled_partials + } +} + +/// Represents an assignment of [`ValidatorMandates`] for a specific height. +/// +/// Contains one map per shard, with the position in the vector corresponding to `shard_id` in +/// `0..num_shards`. Each `HashMap` maps `ValidatorId`s to the number of mandates they have in the +/// corresponding shards. A validator whose id is not in a map has not been assigned to the shard. +/// +/// For example, `mandates_per_shard[0][1]` maps to the [`AssignmentWeights`] validator with +/// `ValidatorId` 1 holds in shard with id 0. +pub type ValidatorMandatesAssignment = Vec>; + +#[derive(Default, Clone, Debug, PartialEq, Eq)] +pub struct AssignmentWeight { + pub num_mandates: u16, + /// Stake assigned to this partial mandate. + pub partial_weight: Balance, +} + +impl AssignmentWeight { + pub fn new(num_mandates: u16, partial_weight: Balance) -> Self { + Self { num_mandates, partial_weight } + } +} + +/// When assigning mandates first to shards with lower ids, the shards with higher ids might end up +/// with fewer assigned mandates. +/// +/// Assumes shard ids are in `[0, num_shards)`. +/// +/// # Example +/// +/// Assume there are 3 shards and 5 mandates. Assigning to shards with lower ids first, the first +/// two shards get 2 mandates each. For the third shard only 1 mandate remains. +/// +/// # Shuffling to avoid bias +/// +/// When mandates cannot be distributed evenly across shards, some shards will be assigned one +/// mandata less than others. Shuffling shard ids prevents a bias towards lower shard ids, as it is +/// no longer predictable which shard(s) will be assigned one mandate less. +#[derive(Default, Clone, Debug, PartialEq, Eq)] +struct ShuffledShardIds { + /// Contains the shard ids `[0, num_shards)` in shuffled order. + shuffled_ids: Vec, +} + +impl ShuffledShardIds { + fn new(rng: &mut R, num_shards: usize) -> Self + where + R: Rng + ?Sized, + { + let mut shuffled_ids = (0..num_shards).collect::>(); + shuffled_ids.shuffle(rng); + Self { shuffled_ids } + } + + /// Gets the alias of `shard_id` corresponding to the current shuffling. + /// + /// # Panics + /// + /// Panics if `shard_id >= num_shards`. + fn get_alias(&self, shard_id: usize) -> usize { + self.shuffled_ids[shard_id] + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use unc_crypto::PublicKey; + use unc_primitives_core::types::Balance; + use rand::SeedableRng; + use rand_chacha::ChaCha8Rng; + + use crate::{ + types::validator_stake::ValidatorStake, types::ValidatorId, + validator_mandates::ValidatorMandatesConfig, + }; + + use super::{ + AssignmentWeight, ShuffledShardIds, ValidatorMandates, ValidatorMandatesAssignment, + }; + + /// Returns a new, fixed RNG to be used only in tests. Using a fixed RNG facilitates testing as + /// it makes outcomes based on that RNG deterministic. + fn new_fixed_rng() -> ChaCha8Rng { + ChaCha8Rng::seed_from_u64(42) + } + + #[test] + fn test_validator_mandates_config_new() { + let stake_per_mandate = 10; + let min_mandates_per_shard = 400; + let num_shards = 4; + assert_eq!( + ValidatorMandatesConfig::new(stake_per_mandate, min_mandates_per_shard, num_shards), + ValidatorMandatesConfig { stake_per_mandate, min_mandates_per_shard, num_shards }, + ) + } + + /// Constructs some `ValidatorStakes` for usage in tests. + /// + /// # Properties of the corresponding `ValidatorMandates` + /// + /// The mandates are (verified in [`test_validator_mandates_new`]): + /// `vec![0, 0, 0, 1, 1, 3, 4, 4, 4]` + /// + /// The partials are (verified in [`test_validator_mandates_new`]): + /// `vec![(1, 7), (2, 9), (3, 2), (4, 5), (5, 4), (6, 6)]` + fn new_validator_stakes() -> Vec { + let new_vs = |account_id: &str, balance: Balance| -> ValidatorStake { + ValidatorStake::new( + account_id.parse().unwrap(), + PublicKey::empty(unc_crypto::KeyType::ED25519), + balance, + ) + }; + + vec![ + new_vs("account_0", 30), + new_vs("account_1", 27), + new_vs("account_2", 9), + new_vs("account_3", 12), + new_vs("account_4", 35), + new_vs("account_5", 4), + new_vs("account_6", 6), + ] + } + + #[test] + fn test_validator_mandates_new() { + let validators = new_validator_stakes(); + let config = ValidatorMandatesConfig::new(10, 1, 4); + let mandates = ValidatorMandates::new(config, &validators); + + // At 10 stake per mandate, the first validator holds three mandates, and so on. + // Note that "account_2" holds no mandate as its stake is below the threshold. + let expected_mandates: Vec = vec![0, 0, 0, 1, 1, 3, 4, 4, 4]; + assert_eq!(mandates.mandates, expected_mandates); + + // At 10 stake per mandate, the first validator holds no partial mandate, the second + // validator holds a partial mandate with weight 7, and so on. + let expected_partials: Vec<(ValidatorId, Balance)> = + vec![(1, 7), (2, 9), (3, 2), (4, 5), (5, 4), (6, 6)]; + assert_eq!(mandates.partials, expected_partials); + } + + #[test] + fn test_validator_mandates_shuffled_mandates() { + // Testing with different `num_shards` values to verify the shuffles used in other tests. + assert_validator_mandates_shuffled_mandates(3, vec![0, 1, 4, 4, 3, 1, 4, 0, 0]); + assert_validator_mandates_shuffled_mandates(4, vec![0, 4, 1, 1, 0, 0, 4, 3, 4]); + } + + fn assert_validator_mandates_shuffled_mandates( + num_shards: usize, + expected_assignment: Vec, + ) { + let validators = new_validator_stakes(); + let config = ValidatorMandatesConfig::new(10, 1, num_shards); + let mandates = ValidatorMandates::new(config, &validators); + let mut rng = new_fixed_rng(); + + // Call methods that modify `rng` before shuffling mandates to emulate what happens in + // [`ValidatorMandates::sample`]. Then `expected_assignment` below equals the shuffled + // mandates assigned to shards in `test_validator_mandates_sample_*`. + let _shard_ids_for_mandates = ShuffledShardIds::new(&mut rng, config.num_shards); + let _shard_ids_for_partials = ShuffledShardIds::new(&mut rng, config.num_shards); + let assignment = mandates.shuffled_mandates(&mut rng); + assert_eq!( + assignment, expected_assignment, + "Unexpected shuffling for num_shards = {num_shards}" + ); + } + + #[test] + fn test_validator_mandates_shuffled_partials() { + // Testing with different `num_shards` values to verify the shuffles used in other tests. + assert_validator_mandates_shuffled_partials( + 3, + vec![(3, 2), (4, 5), (1, 7), (2, 9), (5, 4), (6, 6)], + ); + assert_validator_mandates_shuffled_partials( + 4, + vec![(5, 4), (4, 5), (1, 7), (3, 2), (2, 9), (6, 6)], + ); + } + + fn assert_validator_mandates_shuffled_partials( + num_shards: usize, + expected_assignment: Vec<(ValidatorId, Balance)>, + ) { + let validators = new_validator_stakes(); + let config = ValidatorMandatesConfig::new(10, 1, num_shards); + let mandates = ValidatorMandates::new(config, &validators); + let mut rng = new_fixed_rng(); + + // Call methods that modify `rng` before shuffling mandates to emulate what happens in + // [`ValidatorMandates::sample`]. Then `expected_assignment` below equals the shuffled + // partials assigned to shards in `test_validator_mandates_sample_*`. + let _shard_ids_for_mandates = ShuffledShardIds::new(&mut rng, config.num_shards); + let _shard_ids_for_partials = ShuffledShardIds::new(&mut rng, config.num_shards); + let _ = mandates.shuffled_mandates(&mut rng); + let assignment = mandates.shuffled_partials(&mut rng); + assert_eq!( + assignment, expected_assignment, + "Unexpected shuffling for num_shards = {num_shards}" + ); + } + + /// Test mandates per shard are collected correctly for `num_mandates % num_shards == 0` and + /// `num_partials % num_shards == 0`. + #[test] + fn test_validator_mandates_sample_even() { + // Choosing `num_shards` such that mandates and partials are distributed evenly. + // Assignments in `test_validator_mandates_shuffled_*` can be used to construct + // `expected_assignment` below. + // Note that shard ids are shuffled too, see `test_shuffled_shard_ids_new`. + let config = ValidatorMandatesConfig::new(10, 1, 3); + let expected_assignment: ValidatorMandatesAssignment = vec![ + HashMap::from([ + (4, AssignmentWeight::new(1, 0)), + (1, AssignmentWeight::new(1, 7)), + (0, AssignmentWeight::new(1, 0)), + (6, AssignmentWeight::new(0, 6)), + ]), + HashMap::from([ + (1, AssignmentWeight::new(1, 0)), + (3, AssignmentWeight::new(1, 0)), + (0, AssignmentWeight::new(1, 0)), + (4, AssignmentWeight::new(0, 5)), + (5, AssignmentWeight::new(0, 4)), + ]), + HashMap::from([ + (0, AssignmentWeight::new(1, 0)), + (4, AssignmentWeight::new(2, 0)), + (3, AssignmentWeight::new(0, 2)), + (2, AssignmentWeight::new(0, 9)), + ]), + ]; + assert_validator_mandates_sample(config, expected_assignment); + } + + /// Test mandates per shard are collected correctly for `num_mandates % num_shards != 0` and + /// `num_partials % num_shards != 0`. + #[test] + fn test_validator_mandates_sample_uneven() { + // Choosing `num_shards` such that mandates and partials are distributed unevenly. + // Assignments in `test_validator_mandates_shuffled_*` can be used to construct + // `expected_assignment` below. + // Note that shard ids are shuffled too, see `test_shuffled_shard_ids_new`. + let config = ValidatorMandatesConfig::new(10, 1, 4); + let expected_mandates_per_shards: ValidatorMandatesAssignment = vec![ + HashMap::from([ + (0, AssignmentWeight::new(2, 0)), + (4, AssignmentWeight::new(1, 0)), + (1, AssignmentWeight::new(0, 7)), + ]), + HashMap::from([ + (1, AssignmentWeight::new(1, 0)), + (4, AssignmentWeight::new(1, 0)), + (3, AssignmentWeight::new(0, 2)), + ]), + HashMap::from([ + (4, AssignmentWeight::new(1, 5)), + (0, AssignmentWeight::new(1, 0)), + (6, AssignmentWeight::new(0, 6)), + ]), + HashMap::from([ + (1, AssignmentWeight::new(1, 0)), + (3, AssignmentWeight::new(1, 0)), + (5, AssignmentWeight::new(0, 4)), + (2, AssignmentWeight::new(0, 9)), + ]), + ]; + assert_validator_mandates_sample(config, expected_mandates_per_shards); + } + + /// Asserts mandates per shard are collected correctly. + fn assert_validator_mandates_sample( + config: ValidatorMandatesConfig, + expected_assignment: ValidatorMandatesAssignment, + ) { + let validators = new_validator_stakes(); + let mandates = ValidatorMandates::new(config, &validators); + + let mut rng = new_fixed_rng(); + let assignment = mandates.sample(&mut rng); + + assert_eq!(assignment, expected_assignment); + } + + #[test] + fn test_shuffled_shard_ids_new() { + // Testing with different `num_shards` values to verify the shuffles used in other tests. + // Doing two shuffles for each `num_shards` with the same RNG since shard ids are shuffled + // twice (once for full and once for partial mandates). + let mut rng_3_shards = new_fixed_rng(); + assert_shuffled_shard_ids(&mut rng_3_shards, 3, vec![2, 1, 0], "3 shards, 1st shuffle"); + assert_shuffled_shard_ids(&mut rng_3_shards, 3, vec![2, 1, 0], "3 shards, 2nd shuffle"); + let mut rng_4_shards = new_fixed_rng(); + assert_shuffled_shard_ids(&mut rng_4_shards, 4, vec![0, 2, 1, 3], "4 shards, 1st shuffle"); + assert_shuffled_shard_ids(&mut rng_4_shards, 4, vec![3, 2, 0, 1], "4 shards, 2nd shuffle"); + } + + fn assert_shuffled_shard_ids( + rng: &mut ChaCha8Rng, + num_shards: usize, + expected_shuffling: Vec, + test_descriptor: &str, + ) { + let shuffled_ids_full_mandates = ShuffledShardIds::new(rng, num_shards); + assert_eq!( + shuffled_ids_full_mandates, + ShuffledShardIds { shuffled_ids: expected_shuffling }, + "Unexpected shuffling for {test_descriptor}", + ); + } + + #[test] + fn test_shuffled_shard_ids_get_alias() { + let mut rng = new_fixed_rng(); + let shuffled_ids = ShuffledShardIds::new(&mut rng, 4); + // See [`test_shuffled_shard_ids_new`] for the result of this shuffling. + assert_eq!(shuffled_ids.get_alias(1), 2); + } +} diff --git a/core/primitives/src/validator_signer.rs b/core/primitives/src/validator_signer.rs new file mode 100644 index 000000000..a1beea0ce --- /dev/null +++ b/core/primitives/src/validator_signer.rs @@ -0,0 +1,244 @@ +use std::path::Path; +use std::sync::Arc; + +use unc_crypto::{InMemorySigner, KeyType, PublicKey, Signature, Signer}; + +use crate::block::{Approval, ApprovalInner, BlockHeader}; +use crate::challenge::ChallengeBody; +use crate::chunk_validation::ChunkEndorsementInner; +use crate::hash::CryptoHash; +use crate::network::{AnnounceAccount, PeerId}; +use crate::sharding::ChunkHash; +use crate::telemetry::TelemetryInfo; +use crate::types::{AccountId, BlockHeight, EpochId}; + +/// Validator signer that is used to sign blocks and approvals. +pub trait ValidatorSigner: Sync + Send { + /// Account id of the given validator. + fn validator_id(&self) -> &AccountId; + + /// Public key that identifies this validator. + fn public_key(&self) -> PublicKey; + + /// Serializes telemetry info to JSON and signs it, returning JSON with "signature" field. + fn sign_telemetry(&self, info: &TelemetryInfo) -> serde_json::Value; + + /// Signs given parts of the header. + fn sign_block_header_parts( + &self, + prev_hash: CryptoHash, + inner_lite: &[u8], + inner_rest: &[u8], + ) -> (CryptoHash, Signature); + + /// Signs given inner of the chunk header. + fn sign_chunk_hash(&self, chunk_hash: &ChunkHash) -> Signature; + + /// Signs approval of given parent hash and reference hash. + fn sign_approval(&self, inner: &ApprovalInner, target_height: BlockHeight) -> Signature; + + /// Signs approval of the given chunk. + fn sign_chunk_endorsement(&self, inner: &ChunkEndorsementInner) -> Signature; + + /// Signs challenge body. + fn sign_challenge(&self, challenge_body: &ChallengeBody) -> (CryptoHash, Signature); + + /// Signs account announce. + fn sign_account_announce( + &self, + account_id: &AccountId, + peer_id: &PeerId, + epoch_id: &EpochId, + ) -> Signature; + + /// Signs a proto-serialized AccountKeyPayload (see + /// chain/network/src/network_protocol/network.proto). + /// Making it typesafe would require moving the definition of + /// AccountKeyPayload proto to this crate to avoid a dependency cycle, + /// so for now we are just signing an already-serialized byte sequence. + /// We are serializing a proto rather than borsh here (as an experiment, + /// to allow the network protocol to evolve faster than on-chain stuff), + /// but we can always revert that decision, because these signatures are + /// used only for networking purposes and are not persisted on chain. + /// Moving to proto serialization for stuff stored on chain would be way + /// harder. + fn sign_account_key_payload(&self, proto_bytes: &[u8]) -> Signature; + + fn compute_vrf_with_proof( + &self, + data: &[u8], + ) -> (unc_crypto::vrf::Value, unc_crypto::vrf::Proof); + + /// Used by test infrastructure, only implement if make sense for testing otherwise raise `unimplemented`. + fn write_to_file(&self, path: &Path) -> std::io::Result<()>; +} + +/// Test-only signer that "signs" everything with 0s. +/// Don't use in any production or code that requires signature verification. +#[derive(smart_default::SmartDefault)] +pub struct EmptyValidatorSigner { + #[default("test".parse().unwrap())] + account_id: AccountId, +} + +impl ValidatorSigner for EmptyValidatorSigner { + fn validator_id(&self) -> &AccountId { + &self.account_id + } + + fn public_key(&self) -> PublicKey { + PublicKey::empty(KeyType::ED25519) + } + + fn sign_telemetry(&self, _info: &TelemetryInfo) -> serde_json::Value { + serde_json::Value::default() + } + + fn sign_block_header_parts( + &self, + prev_hash: CryptoHash, + inner_lite: &[u8], + inner_rest: &[u8], + ) -> (CryptoHash, Signature) { + let hash = BlockHeader::compute_hash(prev_hash, inner_lite, inner_rest); + (hash, Signature::default()) + } + + fn sign_chunk_hash(&self, _chunk_hash: &ChunkHash) -> Signature { + Signature::default() + } + + fn sign_approval(&self, _inner: &ApprovalInner, _target_height: BlockHeight) -> Signature { + Signature::default() + } + + fn sign_chunk_endorsement(&self, _inner: &ChunkEndorsementInner) -> Signature { + Signature::default() + } + + fn sign_challenge(&self, challenge_body: &ChallengeBody) -> (CryptoHash, Signature) { + (CryptoHash::hash_borsh(challenge_body), Signature::default()) + } + + fn sign_account_announce( + &self, + _account_id: &AccountId, + _peer_id: &PeerId, + _epoch_id: &EpochId, + ) -> Signature { + Signature::default() + } + + fn sign_account_key_payload(&self, _proto_bytes: &[u8]) -> Signature { + Signature::default() + } + + fn compute_vrf_with_proof( + &self, + _data: &[u8], + ) -> (unc_crypto::vrf::Value, unc_crypto::vrf::Proof) { + unimplemented!() + } + + fn write_to_file(&self, _path: &Path) -> std::io::Result<()> { + unimplemented!() + } +} + +/// Signer that keeps secret key in memory and signs locally. +#[derive(Clone)] +pub struct InMemoryValidatorSigner { + account_id: AccountId, + signer: Arc, +} + +impl InMemoryValidatorSigner { + pub fn from_random(account_id: AccountId, key_type: KeyType) -> Self { + let signer = Arc::new(InMemorySigner::from_random(account_id.clone(), key_type)); + Self { account_id, signer } + } + + pub fn from_seed(account_id: AccountId, key_type: KeyType, seed: &str) -> Self { + let signer = Arc::new(InMemorySigner::from_seed(account_id.clone(), key_type, seed)); + Self { account_id, signer } + } + + pub fn public_key(&self) -> PublicKey { + self.signer.public_key() + } + + pub fn from_file(path: &Path) -> std::io::Result { + let signer = InMemorySigner::from_file(path)?; + Ok(Self { account_id: signer.account_id.clone(), signer: Arc::new(signer) }) + } +} + +impl ValidatorSigner for InMemoryValidatorSigner { + fn validator_id(&self) -> &AccountId { + &self.account_id + } + + fn public_key(&self) -> PublicKey { + self.signer.public_key() + } + + fn sign_telemetry(&self, info: &TelemetryInfo) -> serde_json::Value { + let mut value = serde_json::to_value(info).expect("Telemetry must serialize to JSON"); + let content = serde_json::to_string(&value).expect("Telemetry must serialize to JSON"); + value["signature"] = self.signer.sign(content.as_bytes()).to_string().into(); + value + } + + fn sign_block_header_parts( + &self, + prev_hash: CryptoHash, + inner_lite: &[u8], + inner_rest: &[u8], + ) -> (CryptoHash, Signature) { + let hash = BlockHeader::compute_hash(prev_hash, inner_lite, inner_rest); + (hash, self.signer.sign(hash.as_ref())) + } + + fn sign_chunk_hash(&self, chunk_hash: &ChunkHash) -> Signature { + self.signer.sign(chunk_hash.as_ref()) + } + + fn sign_approval(&self, inner: &ApprovalInner, target_height: BlockHeight) -> Signature { + self.signer.sign(&Approval::get_data_for_sig(inner, target_height)) + } + + fn sign_chunk_endorsement(&self, inner: &ChunkEndorsementInner) -> Signature { + self.signer.sign(&borsh::to_vec(inner).unwrap()) + } + + fn sign_challenge(&self, challenge_body: &ChallengeBody) -> (CryptoHash, Signature) { + let hash = CryptoHash::hash_borsh(challenge_body); + let signature = self.signer.sign(hash.as_ref()); + (hash, signature) + } + + fn sign_account_announce( + &self, + account_id: &AccountId, + peer_id: &PeerId, + epoch_id: &EpochId, + ) -> Signature { + let hash = AnnounceAccount::build_header_hash(account_id, peer_id, epoch_id); + self.signer.sign(hash.as_ref()) + } + + fn sign_account_key_payload(&self, proto_bytes: &[u8]) -> Signature { + self.signer.sign(proto_bytes) + } + + fn compute_vrf_with_proof( + &self, + data: &[u8], + ) -> (unc_crypto::vrf::Value, unc_crypto::vrf::Proof) { + self.signer.compute_vrf_with_proof(data) + } + + fn write_to_file(&self, path: &Path) -> std::io::Result<()> { + self.signer.write_to_file(path) + } +} diff --git a/core/primitives/src/version.rs b/core/primitives/src/version.rs new file mode 100644 index 000000000..8238a4b21 --- /dev/null +++ b/core/primitives/src/version.rs @@ -0,0 +1,75 @@ +use crate::types::Balance; +use once_cell::sync::Lazy; + +/// Data structure for semver version and github tag or commit. +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Default)] +pub struct Version { + pub version: String, + pub build: String, + #[serde(default)] + pub rustc_version: String, +} + +use crate::upgrade_schedule::{get_protocol_version_internal, ProtocolUpgradeVotingSchedule}; + +/// unc_primitives_core re-exports +pub use unc_primitives_core::checked_feature; +pub use unc_primitives_core::types::ProtocolVersion; +pub use unc_primitives_core::version::ProtocolFeature; +pub use unc_primitives_core::version::PEER_MIN_ALLOWED_PROTOCOL_VERSION; +pub use unc_primitives_core::version::PROTOCOL_VERSION; + +/// Minimum gas price proposed in NEP 92 and the associated protocol version +pub const MIN_GAS_PRICE_NEP_92: Balance = 1_000_000_000; +pub const MIN_PROTOCOL_VERSION_NEP_92: ProtocolVersion = 31; + +/// Minimum gas price proposed in NEP 92 (fixed) and the associated protocol version +pub const MIN_GAS_PRICE_NEP_92_FIX: Balance = 100_000_000; +pub const MIN_PROTOCOL_VERSION_NEP_92_FIX: ProtocolVersion = 32; + +pub const CORRECT_RANDOM_VALUE_PROTOCOL_VERSION: ProtocolVersion = 33; + +/// The protocol version that enables reward on mainnet. +pub const ENABLE_INFLATION_PROTOCOL_VERSION: ProtocolVersion = 36; + +/// Fix upgrade to use the latest voted protocol version instead of the current epoch protocol +/// version when there is no new change in protocol version. +pub const UPGRADABILITY_FIX_PROTOCOL_VERSION: ProtocolVersion = 37; + +/// Updates the way receipt ID, data ID and random seeds are constructed. +pub const CREATE_HASH_PROTOCOL_VERSION: ProtocolVersion = 38; + +/// Fix the storage usage of the delete key action. +pub const DELETE_KEY_STORAGE_USAGE_PROTOCOL_VERSION: ProtocolVersion = 40; + +pub const SHARD_CHUNK_HEADER_UPGRADE_VERSION: ProtocolVersion = 41; + +/// Updates the way receipt ID is constructed to use current block hash instead of last block hash +pub const CREATE_RECEIPT_ID_SWITCH_TO_CURRENT_BLOCK_VERSION: ProtocolVersion = 42; + +/// The points in time after which the voting for the latest protocol version +/// should start. +/// +/// In non-release builds this is typically a date in the far past (meaning that +/// nightly builds will vote for new protocols immediately). On release builds +/// it’s set according to the schedule for that protocol upgrade. Release +/// candidates usually have separate schedule to final releases. +pub const PROTOCOL_UPGRADE_SCHEDULE: Lazy = Lazy::new(|| { + // Update to according to schedule when making a release. + // Keep in mind that the protocol upgrade will happen 1-2 epochs (15h-30h) + // after the set date. Ideally that should be during working hours. + // e.g. ProtocolUpgradeVotingSchedule::from_env_or_str("2000-01-01 15:00:00").unwrap()); + + ProtocolUpgradeVotingSchedule::default() +}); + +/// Gives new clients an option to upgrade without announcing that they support +/// the new version. This gives non-validator nodes time to upgrade. See +/// +pub fn get_protocol_version(next_epoch_protocol_version: ProtocolVersion) -> ProtocolVersion { + get_protocol_version_internal( + next_epoch_protocol_version, + PROTOCOL_VERSION, + *PROTOCOL_UPGRADE_SCHEDULE, + ) +} diff --git a/core/primitives/src/views.rs b/core/primitives/src/views.rs new file mode 100644 index 000000000..8dcbe5c6f --- /dev/null +++ b/core/primitives/src/views.rs @@ -0,0 +1,2657 @@ +//! This module defines "stable" internal API to view internal data using view_client. +//! +//! These types should only change when we cannot avoid this. Thus, when the counterpart internal +//! type gets changed, the view should preserve the old shape and only re-map the necessary bits +//! from the source structure in the relevant `From` impl. +use crate::account::{AccessKey, AccessKeyPermission, Account, FunctionCallPermission}; +use crate::action::delegate::{DelegateAction, SignedDelegateAction}; +use crate::block::{Block, BlockHeader, Tip}; +use crate::block_header::{ + BlockHeaderInnerLite, BlockHeaderInnerRest, BlockHeaderInnerRestV2, BlockHeaderInnerRestV3, + BlockHeaderV1, BlockHeaderV2, BlockHeaderV3, +}; +use crate::block_header::{BlockHeaderInnerRestV4, BlockHeaderV4}; +use crate::challenge::{Challenge, ChallengesResult}; +use crate::checked_feature; +use crate::errors::TxExecutionError; +use crate::hash::{hash, CryptoHash}; +use crate::merkle::{combine_hash, MerklePath}; +use crate::network::PeerId; +use crate::receipt::{ActionReceipt, DataReceipt, DataReceiver, Receipt, ReceiptEnum}; +use crate::serialize::dec_format; +use crate::sharding::{ + ChunkHash, ShardChunk, ShardChunkHeader, ShardChunkHeaderInner, ShardChunkHeaderInnerV2, + ShardChunkHeaderV3, +}; +use crate::transaction::{ + Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, + DeployContractAction, ExecutionMetadata, ExecutionOutcome, ExecutionOutcomeWithIdAndProof, + ExecutionStatus, FunctionCallAction, PartialExecutionOutcome, PartialExecutionStatus, + SignedTransaction, StakeAction, TransferAction, +}; +use crate::types::{ + AccountId, AccountWithPublicKey, Balance, BlockHeight, EpochHeight, EpochId, FunctionArgs, Gas, + Nonce, NumBlocks, ShardId, StateChangeCause, StateChangeKind, StateChangeValue, + StateChangeWithCause, StateChangesRequest, StateRoot, StorageUsage, StoreKey, StoreValue, + ValidatorKickoutReason, Power, +}; + +use crate::action::{RegisterRsa2048KeysAction, CreateRsa2048ChallengeAction}; +use crate::version::{ProtocolVersion, Version}; +use borsh::{BorshDeserialize, BorshSerialize}; +use chrono::DateTime; +use unc_crypto::{PublicKey, Signature}; +use unc_fmt::{AbbrBytes, Slice}; +use unc_parameters::{ActionCosts, ExtCosts}; +use unc_vm_runner::logic::CompiledContractCache; +use unc_vm_runner::ContractCode; +use serde_with::base64::Base64; +use serde_with::serde_as; +use std::collections::HashMap; +use std::fmt; +use std::ops::Range; +use std::sync::Arc; +use strum::IntoEnumIterator; +use validator_power_view::ValidatorPowerView; +use crate::views::validator_frozen_view::ValidatorFrozenView; +use crate::views::validator_power_and_frozen_view::ValidatorPowerAndFrozenView; + +/// A view of the account +#[derive(serde::Serialize, serde::Deserialize, Debug, Eq, PartialEq, Clone)] +pub struct AccountView { + #[serde(with = "dec_format")] + pub amount: Balance, + #[serde(with = "dec_format")] + pub locked: Balance, + #[serde(with = "dec_format")] + pub power: Power, + pub code_hash: CryptoHash, + pub storage_usage: StorageUsage, + /// TODO(2271): deprecated. + #[serde(default)] + pub storage_paid_at: BlockHeight, +} + +/// A view of the contract code. +#[serde_as] +#[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Debug, Clone)] +pub struct ContractCodeView { + #[serde(rename = "code_base64")] + #[serde_as(as = "Base64")] + pub code: Vec, + pub hash: CryptoHash, +} + +/// State for the view call. +#[derive(Debug)] +pub struct ViewApplyState { + /// Currently building block height. + pub block_height: BlockHeight, + /// Prev block hash + pub prev_block_hash: CryptoHash, + /// Currently building block hash + pub block_hash: CryptoHash, + /// Current epoch id + pub epoch_id: EpochId, + /// Current epoch height + pub epoch_height: EpochHeight, + /// The current block timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC). + pub block_timestamp: u64, + /// Current Protocol version when we apply the state transition + pub current_protocol_version: ProtocolVersion, + /// Cache for compiled contracts. + pub cache: Option>, +} + +impl From<&Account> for AccountView { + fn from(account: &Account) -> Self { + AccountView { + amount: account.amount(), + locked: account.locked(), + power: account.power(), + code_hash: account.code_hash(), + storage_usage: account.storage_usage(), + storage_paid_at: 0, + } + } +} + +impl From for AccountView { + fn from(account: Account) -> Self { + (&account).into() + } +} + +impl From<&AccountView> for Account { + fn from(view: &AccountView) -> Self { + Account::new(view.amount, view.locked, view.power, view.code_hash, view.storage_usage) + } +} + +impl From for Account { + fn from(view: AccountView) -> Self { + (&view).into() + } +} + +impl From for ContractCodeView { + fn from(contract_code: ContractCode) -> Self { + let hash = *contract_code.hash(); + let code = contract_code.into_code(); + ContractCodeView { code, hash } + } +} + +impl From for ContractCode { + fn from(contract_code: ContractCodeView) -> Self { + ContractCode::new(contract_code.code, Some(contract_code.hash)) + } +} + +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + Eq, + PartialEq, + Clone, + serde::Serialize, + serde::Deserialize, +)] +pub enum AccessKeyPermissionView { + FunctionCall { + #[serde(with = "dec_format")] + allowance: Option, + receiver_id: String, + method_names: Vec, + }, + FullAccess, +} + +impl From for AccessKeyPermissionView { + fn from(permission: AccessKeyPermission) -> Self { + match permission { + AccessKeyPermission::FunctionCall(func_call) => AccessKeyPermissionView::FunctionCall { + allowance: func_call.allowance, + receiver_id: func_call.receiver_id, + method_names: func_call.method_names, + }, + AccessKeyPermission::FullAccess => AccessKeyPermissionView::FullAccess, + } + } +} + +impl From for AccessKeyPermission { + fn from(view: AccessKeyPermissionView) -> Self { + match view { + AccessKeyPermissionView::FunctionCall { allowance, receiver_id, method_names } => { + AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance, + receiver_id, + method_names, + }) + } + AccessKeyPermissionView::FullAccess => AccessKeyPermission::FullAccess, + } + } +} + +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + Eq, + PartialEq, + Clone, + serde::Serialize, + serde::Deserialize, +)] +pub struct AccessKeyView { + pub nonce: Nonce, + pub permission: AccessKeyPermissionView, +} + +impl From for AccessKeyView { + fn from(access_key: AccessKey) -> Self { + Self { nonce: access_key.nonce, permission: access_key.permission.into() } + } +} + +impl From for AccessKey { + fn from(view: AccessKeyView) -> Self { + Self { nonce: view.nonce, permission: view.permission.into() } + } +} + +/// Item of the state, key and value are serialized in base64 and proof for inclusion of given state item. +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct StateItem { + pub key: StoreKey, + pub value: StoreValue, +} + +#[serde_as] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct ViewStateResult { + pub values: Vec, + #[serde_as(as = "Vec")] + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub proof: Vec>, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone, Default)] +pub struct CallResult { + pub result: Vec, + pub logs: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct QueryError { + pub error: String, + pub logs: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct AccessKeyInfoView { + pub public_key: PublicKey, + pub access_key: AccessKeyView, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct AccessKeyList { + pub keys: Vec, +} + +impl FromIterator for AccessKeyList { + fn from_iter>(iter: I) -> Self { + Self { keys: iter.into_iter().collect() } + } +} + +#[cfg_attr(feature = "deepsize_feature", derive(deepsize::DeepSizeOf))] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct KnownPeerStateView { + pub peer_id: PeerId, + pub status: String, + pub addr: String, + pub first_seen: i64, + pub last_seen: i64, + pub last_attempt: Option<(i64, String)>, +} + +#[cfg_attr(feature = "deepsize_feature", derive(deepsize::DeepSizeOf))] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct ConnectionInfoView { + pub peer_id: PeerId, + pub addr: String, + pub time_established: i64, + pub time_connected_until: i64, +} + +#[cfg_attr(feature = "deepsize_feature", derive(deepsize::DeepSizeOf))] +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct SnapshotHostInfoView { + pub peer_id: PeerId, + pub sync_hash: CryptoHash, + pub epoch_height: u64, + pub shards: Vec, +} + +#[cfg_attr(feature = "deepsize_feature", derive(deepsize::DeepSizeOf))] +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum QueryResponseKind { + ViewAccount(AccountView), + ViewCode(ContractCodeView), + ViewState(ViewStateResult), + CallResult(CallResult), + AccessKey(AccessKeyView), + AccessKeyList(AccessKeyList), +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)] +#[serde(tag = "request_type", rename_all = "snake_case")] +pub enum QueryRequest { + ViewAccount { + account_id: AccountId, + }, + ViewCode { + account_id: AccountId, + }, + ViewState { + account_id: AccountId, + #[serde(rename = "prefix_base64")] + prefix: StoreKey, + #[serde(default, skip_serializing_if = "is_false")] + include_proof: bool, + }, + ViewAccessKey { + account_id: AccountId, + public_key: PublicKey, + }, + ViewAccessKeyList { + account_id: AccountId, + }, + CallFunction { + account_id: AccountId, + method_name: String, + #[serde(rename = "args_base64")] + args: FunctionArgs, + }, +} + +fn is_false(v: &bool) -> bool { + !*v +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct QueryResponse { + pub kind: QueryResponseKind, + pub block_height: BlockHeight, + pub block_hash: CryptoHash, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct StatusSyncInfo { + pub latest_block_hash: CryptoHash, + pub latest_block_height: BlockHeight, + pub latest_state_root: CryptoHash, + pub latest_block_time: DateTime, + pub syncing: bool, + pub earliest_block_hash: Option, + pub earliest_block_height: Option, + pub earliest_block_time: Option>, + pub epoch_id: Option, + pub epoch_start_height: Option, +} + +// TODO: add more information to ValidatorInfo +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct ValidatorInfo { + pub account_id: AccountId, + pub is_slashed: bool, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct PeerInfoView { + pub addr: String, + pub account_id: Option, + pub height: Option, + pub block_hash: Option, + pub is_highest_block_invalid: bool, + pub tracked_shards: Vec, + pub archival: bool, + pub peer_id: PublicKey, + pub received_bytes_per_sec: u64, + pub sent_bytes_per_sec: u64, + pub last_time_peer_requested_millis: u64, + pub last_time_received_message_millis: u64, + pub connection_established_time_millis: u64, + pub is_outbound_peer: bool, + /// Connection nonce. + pub nonce: u64, +} + +/// Information about a Producer: its account name, peer_id and a list of connected peers that +/// the node can use to send message for this producer. +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct KnownProducerView { + pub account_id: AccountId, + pub peer_id: PublicKey, + pub next_hops: Option>, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct Tier1ProxyView { + pub addr: std::net::SocketAddr, + pub peer_id: PublicKey, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct AccountDataView { + pub peer_id: PublicKey, + pub proxies: Vec, + pub account_key: PublicKey, + pub timestamp: DateTime, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct NetworkInfoView { + pub peer_max_count: u32, + pub num_connected_peers: usize, + pub connected_peers: Vec, + pub known_producers: Vec, + pub tier1_accounts_keys: Vec, + pub tier1_accounts_data: Vec, + pub tier1_connections: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub enum SyncStatusView { + /// Initial state. Not enough peers to do anything yet. + AwaitingPeers, + /// Not syncing / Done syncing. + NoSync, + /// Syncing using light-client headers to a recent epoch + // TODO #3488 + // Bowen: why do we use epoch ordinal instead of epoch id? + EpochSync { epoch_ord: u64 }, + /// Downloading block headers for fast sync. + HeaderSync { + start_height: BlockHeight, + current_height: BlockHeight, + highest_height: BlockHeight, + }, + /// State sync, with different states of state sync for different shards. + StateSync(CryptoHash, HashMap), + /// Sync state across all shards is done. + StateSyncDone, + /// Download and process blocks until the head reaches the head of the network. + BlockSync { + start_height: BlockHeight, + current_height: BlockHeight, + highest_height: BlockHeight, + }, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct PeerStoreView { + pub peer_states: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct RecentOutboundConnectionsView { + pub recent_outbound_connections: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct SnapshotHostsView { + pub hosts: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct EdgeView { + pub peer0: PeerId, + pub peer1: PeerId, + pub nonce: u64, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct NetworkGraphView { + pub edges: Vec, + pub next_hops: HashMap>, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct LabeledEdgeView { + pub peer0: u32, + pub peer1: u32, + pub nonce: u64, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct EdgeCacheView { + pub peer_labels: HashMap, + pub spanning_trees: HashMap>, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct PeerDistancesView { + pub distance: Vec>, + pub min_nonce: u64, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct NetworkRoutesView { + pub edge_cache: EdgeCacheView, + pub local_edges: HashMap, + pub peer_distances: HashMap, + pub my_distances: HashMap, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct ShardSyncDownloadView { + pub downloads: Vec, + pub status: String, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct DownloadStatusView { + pub error: bool, + pub done: bool, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct CatchupStatusView { + // This is the first block of the epoch that we are catching up + pub sync_block_hash: CryptoHash, + pub sync_block_height: BlockHeight, + // Status of all shards that need to sync + pub shard_sync_status: HashMap, + // Blocks that we need to catchup, if it is empty, it means catching up is done + pub blocks_to_catchup: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct RequestedStatePartsView { + // This is the first block of the epoch that was requested + pub block_hash: CryptoHash, + // All the part ids of the shards that were requested + pub shard_requested_parts: HashMap>, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)] +pub struct BlockStatusView { + pub height: BlockHeight, + pub hash: CryptoHash, +} + +impl BlockStatusView { + pub fn new(height: &BlockHeight, hash: &CryptoHash) -> BlockStatusView { + Self { height: *height, hash: *hash } + } +} + +impl From for BlockStatusView { + fn from(tip: Tip) -> Self { + Self { height: tip.height, hash: tip.last_block_hash } + } +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct PartElapsedTimeView { + pub part_id: u64, + pub elapsed_ms: u128, +} + +impl PartElapsedTimeView { + pub fn new(part_id: &u64, elapsed_ms: u128) -> PartElapsedTimeView { + Self { part_id: *part_id, elapsed_ms } + } +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct BlockByChunksView { + pub height: BlockHeight, + pub hash: CryptoHash, + pub block_status: String, + pub chunk_status: String, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct ChainProcessingInfo { + pub num_blocks_in_processing: usize, + pub num_orphans: usize, + pub num_blocks_missing_chunks: usize, + /// contains processing info of recent blocks, ordered by height high to low + pub blocks_info: Vec, + /// contains processing info of chunks that we don't know which block it belongs to yet + pub floating_chunks_info: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct BlockProcessingInfo { + pub height: BlockHeight, + pub hash: CryptoHash, + pub received_timestamp: DateTime, + /// Timestamp when block was received. + //pub received_timestamp: DateTime, + /// Time (in ms) between when the block was first received and when it was processed + pub in_progress_ms: u128, + /// Time (in ms) that the block spent in the orphan pool. If the block was never put in the + /// orphan pool, it is None. If the block is still in the orphan pool, it is since the time + /// it was put into the pool until the current time. + pub orphaned_ms: Option, + /// Time (in ms) that the block spent in the missing chunks pool. If the block was never put in the + /// missing chunks pool, it is None. If the block is still in the missing chunks pool, it is + /// since the time it was put into the pool until the current time. + pub missing_chunks_ms: Option, + pub block_status: BlockProcessingStatus, + /// Only contains new chunks that belong to this block, if the block doesn't produce a new chunk + /// for a shard, the corresponding item will be None. + pub chunks_info: Vec>, +} + +#[derive( + BorshSerialize, + BorshDeserialize, + Clone, + Debug, + PartialEq, + Eq, + serde::Serialize, + serde::Deserialize, +)] +pub enum BlockProcessingStatus { + Orphan, + WaitingForChunks, + InProcessing, + Accepted, + Error(String), + Dropped(DroppedReason), + Unknown, +} + +#[derive( + BorshSerialize, + BorshDeserialize, + Clone, + Debug, + PartialEq, + Eq, + serde::Serialize, + serde::Deserialize, +)] +pub enum DroppedReason { + // If the node has already processed a block at this height + HeightProcessed, + // If the block processing pool is full + TooManyProcessingBlocks, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct ChunkProcessingInfo { + pub height_created: BlockHeight, + pub shard_id: ShardId, + pub chunk_hash: ChunkHash, + pub prev_block_hash: CryptoHash, + /// Account id of the validator who created this chunk + /// Theoretically this field should never be None unless there is some database corruption. + pub created_by: Option, + pub status: ChunkProcessingStatus, + /// Timestamp of first time when we request for this chunk. + pub requested_timestamp: Option>, + /// Timestamp of when the chunk is complete + pub completed_timestamp: Option>, + /// Time (in millis) that it takes between when the chunk is requested and when it is completed. + pub request_duration: Option, + pub chunk_parts_collection: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct PartCollectionInfo { + pub part_owner: AccountId, + // Time when the part is received through any message + pub received_time: Option>, + // Time when we receive a PartialEncodedChunkForward containing this part + pub forwarded_received_time: Option>, + // Time when we receive the PartialEncodedChunk message containing this part + pub chunk_received_time: Option>, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub enum ChunkProcessingStatus { + NeedToRequest, + Requested, + Completed, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct DetailedDebugStatus { + pub network_info: NetworkInfoView, + pub sync_status: String, + pub catchup_status: Vec, + pub current_head_status: BlockStatusView, + pub current_header_head_status: BlockStatusView, + pub block_production_delay_millis: u64, +} + +// TODO: add more information to status. +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct StatusResponse { + /// Binary version. + pub version: Version, + /// Unique chain id. + pub chain_id: String, + /// Currently active protocol version. + pub protocol_version: u32, + /// Latest protocol version that this client supports. + pub latest_protocol_version: u32, + /// Address for RPC server. None if node doesn’t have RPC endpoint enabled. + #[serde(skip_serializing_if = "Option::is_none")] + pub rpc_addr: Option, + /// Current epoch validators. + pub validators: Vec, + /// Sync status of the node. + pub sync_info: StatusSyncInfo, + /// Validator id of the node + pub validator_account_id: Option, + /// Public key of the validator. + pub validator_public_key: Option, + /// Public key of the node. + pub node_public_key: PublicKey, + /// Deprecated; same as `validator_public_key` which you should use instead. + pub node_key: Option, + /// Uptime of the node. + pub uptime_sec: i64, + /// Information about last blocks, network, epoch and chain & chunk info. + #[serde(skip_serializing_if = "Option::is_none")] + pub detailed_debug_status: Option, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct ChallengeView { + // TODO: decide how to represent challenges in json. +} + +impl From for ChallengeView { + fn from(_challenge: Challenge) -> Self { + Self {} + } +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct ChipView { + pub power: Power, + pub serial_number: String, + pub bus_id: String, + pub p2: String, + pub public_key: String, + pub p2_size: usize, + pub public_key_size: usize, +} +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct BlockHeaderView { + pub height: BlockHeight, + pub prev_height: Option, + pub epoch_id: CryptoHash, + pub next_epoch_id: CryptoHash, + pub hash: CryptoHash, + pub prev_hash: CryptoHash, + pub prev_state_root: CryptoHash, + pub block_body_hash: Option, + pub chunk_receipts_root: CryptoHash, + pub chunk_headers_root: CryptoHash, + pub chunk_tx_root: CryptoHash, + pub outcome_root: CryptoHash, + pub chunks_included: u64, + pub challenges_root: CryptoHash, + /// Legacy json number. Should not be used. + pub timestamp: u64, + #[serde(with = "dec_format")] + pub timestamp_nanosec: u64, + pub random_value: CryptoHash, + pub validator_power_proposals: Vec, + pub validator_frozen_proposals: Vec, + pub chunk_mask: Vec, + #[serde(with = "dec_format")] + pub gas_price: Balance, + pub block_ordinal: Option, + /// TODO(2271): deprecated. + #[serde(with = "dec_format")] + pub rent_paid: Balance, + /// TODO(2271): deprecated. + #[serde(with = "dec_format")] + pub validator_reward: Balance, + #[serde(with = "dec_format")] + pub total_supply: Balance, + pub challenges_result: ChallengesResult, + pub last_final_block: CryptoHash, + pub last_ds_final_block: CryptoHash, + pub next_bp_hash: CryptoHash, + pub block_merkle_root: CryptoHash, + pub epoch_sync_data_hash: Option, + pub approvals: Vec>>, + pub signature: Signature, + pub latest_protocol_version: ProtocolVersion, +} + +impl From for BlockHeaderView { + fn from(header: BlockHeader) -> Self { + Self { + height: header.height(), + prev_height: header.prev_height(), + epoch_id: header.epoch_id().0, + next_epoch_id: header.next_epoch_id().0, + hash: *header.hash(), + prev_hash: *header.prev_hash(), + prev_state_root: *header.prev_state_root(), + block_body_hash: header.block_body_hash(), + chunk_receipts_root: *header.prev_chunk_outgoing_receipts_root(), + chunk_headers_root: *header.chunk_headers_root(), + chunk_tx_root: *header.chunk_tx_root(), + chunks_included: header.chunks_included(), + challenges_root: *header.challenges_root(), + outcome_root: *header.outcome_root(), + timestamp: header.raw_timestamp(), + timestamp_nanosec: header.raw_timestamp(), + random_value: *header.random_value(), + validator_power_proposals: header.prev_validator_power_proposals().map(Into::into).collect(), + validator_frozen_proposals: header.prev_validator_frozen_proposals().map(Into::into).collect(), + chunk_mask: header.chunk_mask().to_vec(), + block_ordinal: if header.block_ordinal() != 0 { + Some(header.block_ordinal()) + } else { + None + }, + gas_price: header.next_gas_price(), + rent_paid: 0, + validator_reward: 0, + total_supply: header.total_supply(), + challenges_result: header.challenges_result().clone(), + last_final_block: *header.last_final_block(), + last_ds_final_block: *header.last_ds_final_block(), + next_bp_hash: *header.next_bp_hash(), + block_merkle_root: *header.block_merkle_root(), + epoch_sync_data_hash: header.epoch_sync_data_hash(), + approvals: header.approvals().to_vec(), + signature: header.signature().clone(), + latest_protocol_version: header.latest_protocol_version(), + } + } +} + +impl From for BlockHeader { + fn from(view: BlockHeaderView) -> Self { + let inner_lite = BlockHeaderInnerLite { + height: view.height, + epoch_id: EpochId(view.epoch_id), + next_epoch_id: EpochId(view.next_epoch_id), + prev_state_root: view.prev_state_root, + prev_outcome_root: view.outcome_root, + timestamp: view.timestamp, + next_bp_hash: view.next_bp_hash, + block_merkle_root: view.block_merkle_root, + }; + const LAST_HEADER_V2_VERSION: ProtocolVersion = + crate::version::ProtocolFeature::BlockHeaderV3.protocol_version() - 1; + if view.latest_protocol_version <= 29 { + let validator_power_proposals = view + .validator_power_proposals + .into_iter() + .map(|v| v.into_validator_power().into_v1()) + .collect(); + let validator_frozen_proposals = view + .validator_frozen_proposals + .into_iter() + .map(|v| v.into_validator_frozen().into_v1()) + .collect(); + let mut header = BlockHeaderV1 { + prev_hash: view.prev_hash, + inner_lite, + inner_rest: BlockHeaderInnerRest { + prev_chunk_outgoing_receipts_root: view.chunk_receipts_root, + chunk_headers_root: view.chunk_headers_root, + chunk_tx_root: view.chunk_tx_root, + chunks_included: view.chunks_included, + challenges_root: view.challenges_root, + random_value: view.random_value, + prev_validator_power_proposals: validator_power_proposals, + prev_validator_frozen_proposals: validator_frozen_proposals, + chunk_mask: view.chunk_mask, + next_gas_price: view.gas_price, + total_supply: view.total_supply, + challenges_result: view.challenges_result, + last_final_block: view.last_final_block, + last_ds_final_block: view.last_ds_final_block, + approvals: view.approvals.clone(), + latest_protocol_version: view.latest_protocol_version, + }, + signature: view.signature, + hash: CryptoHash::default(), + }; + header.init(); + BlockHeader::BlockHeaderV1(Arc::new(header)) + } else if view.latest_protocol_version <= LAST_HEADER_V2_VERSION { + let validator_power_proposals = view + .validator_power_proposals + .into_iter() + .map(|v| v.into_validator_power().into_v1()) + .collect(); + let validator_frozen_proposals = view + .validator_frozen_proposals + .into_iter() + .map(|v| v.into_validator_frozen().into_v1()) + .collect(); + let mut header = BlockHeaderV2 { + prev_hash: view.prev_hash, + inner_lite, + inner_rest: BlockHeaderInnerRestV2 { + prev_chunk_outgoing_receipts_root: view.chunk_receipts_root, + chunk_headers_root: view.chunk_headers_root, + chunk_tx_root: view.chunk_tx_root, + challenges_root: view.challenges_root, + random_value: view.random_value, + prev_validator_power_proposals: validator_power_proposals, + prev_validator_frozen_proposals: validator_frozen_proposals, + chunk_mask: view.chunk_mask, + next_gas_price: view.gas_price, + total_supply: view.total_supply, + challenges_result: view.challenges_result, + last_final_block: view.last_final_block, + last_ds_final_block: view.last_ds_final_block, + approvals: view.approvals.clone(), + latest_protocol_version: view.latest_protocol_version, + }, + signature: view.signature, + hash: CryptoHash::default(), + }; + header.init(); + BlockHeader::BlockHeaderV2(Arc::new(header)) + } else if !checked_feature!("stable", BlockHeaderV4, view.latest_protocol_version) { + let mut header = BlockHeaderV3 { + prev_hash: view.prev_hash, + inner_lite, + inner_rest: BlockHeaderInnerRestV3 { + prev_chunk_outgoing_receipts_root: view.chunk_receipts_root, + chunk_headers_root: view.chunk_headers_root, + chunk_tx_root: view.chunk_tx_root, + challenges_root: view.challenges_root, + random_value: view.random_value, + prev_validator_power_proposals: view + .validator_power_proposals + .into_iter() + .map(Into::into) + .collect(), + prev_validator_frozen_proposals: view + .validator_frozen_proposals + .into_iter() + .map(Into::into) + .collect(), + chunk_mask: view.chunk_mask, + next_gas_price: view.gas_price, + block_ordinal: view.block_ordinal.unwrap_or(0), + total_supply: view.total_supply, + challenges_result: view.challenges_result, + last_final_block: view.last_final_block, + last_ds_final_block: view.last_ds_final_block, + prev_height: view.prev_height.unwrap_or_default(), + epoch_sync_data_hash: view.epoch_sync_data_hash, + approvals: view.approvals.clone(), + latest_protocol_version: view.latest_protocol_version, + }, + signature: view.signature, + hash: CryptoHash::default(), + }; + header.init(); + BlockHeader::BlockHeaderV3(Arc::new(header)) + } else { + let mut header = BlockHeaderV4 { + prev_hash: view.prev_hash, + inner_lite, + inner_rest: BlockHeaderInnerRestV4 { + block_body_hash: view.block_body_hash.unwrap_or_default(), + prev_chunk_outgoing_receipts_root: view.chunk_receipts_root, + chunk_headers_root: view.chunk_headers_root, + chunk_tx_root: view.chunk_tx_root, + challenges_root: view.challenges_root, + random_value: view.random_value, + prev_validator_power_proposals: view + .validator_power_proposals + .into_iter() + .map(Into::into) + .collect(), + prev_validator_frozen_proposals: view + .validator_frozen_proposals + .into_iter() + .map(Into::into) + .collect(), + chunk_mask: view.chunk_mask, + next_gas_price: view.gas_price, + block_ordinal: view.block_ordinal.unwrap_or(0), + total_supply: view.total_supply, + challenges_result: view.challenges_result, + last_final_block: view.last_final_block, + last_ds_final_block: view.last_ds_final_block, + prev_height: view.prev_height.unwrap_or_default(), + epoch_sync_data_hash: view.epoch_sync_data_hash, + approvals: view.approvals.clone(), + latest_protocol_version: view.latest_protocol_version, + }, + signature: view.signature, + hash: CryptoHash::default(), + }; + header.init(); + BlockHeader::BlockHeaderV4(Arc::new(header)) + } + } +} + +#[derive( + PartialEq, + Eq, + Debug, + Clone, + BorshDeserialize, + BorshSerialize, + serde::Serialize, + serde::Deserialize, +)] +pub struct BlockHeaderInnerLiteView { + pub height: BlockHeight, + pub epoch_id: CryptoHash, + pub next_epoch_id: CryptoHash, + pub prev_state_root: CryptoHash, + pub outcome_root: CryptoHash, + /// Legacy json number. Should not be used. + pub timestamp: u64, + #[serde(with = "dec_format")] + pub timestamp_nanosec: u64, + pub next_bp_hash: CryptoHash, + pub block_merkle_root: CryptoHash, +} + +impl From for BlockHeaderInnerLiteView { + fn from(header: BlockHeader) -> Self { + let inner_lite = match &header { + BlockHeader::BlockHeaderV1(header) => &header.inner_lite, + BlockHeader::BlockHeaderV2(header) => &header.inner_lite, + BlockHeader::BlockHeaderV3(header) => &header.inner_lite, + BlockHeader::BlockHeaderV4(header) => &header.inner_lite, + }; + BlockHeaderInnerLiteView { + height: inner_lite.height, + epoch_id: inner_lite.epoch_id.0, + next_epoch_id: inner_lite.next_epoch_id.0, + prev_state_root: inner_lite.prev_state_root, + outcome_root: inner_lite.prev_outcome_root, + timestamp: inner_lite.timestamp, + timestamp_nanosec: inner_lite.timestamp, + next_bp_hash: inner_lite.next_bp_hash, + block_merkle_root: inner_lite.block_merkle_root, + } + } +} + +impl From for BlockHeaderInnerLite { + fn from(view: BlockHeaderInnerLiteView) -> Self { + BlockHeaderInnerLite { + height: view.height, + epoch_id: EpochId(view.epoch_id), + next_epoch_id: EpochId(view.next_epoch_id), + prev_state_root: view.prev_state_root, + prev_outcome_root: view.outcome_root, + timestamp: view.timestamp_nanosec, + next_bp_hash: view.next_bp_hash, + block_merkle_root: view.block_merkle_root, + } + } +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct ChunkHeaderView { + pub chunk_hash: CryptoHash, + pub prev_block_hash: CryptoHash, + pub outcome_root: CryptoHash, + pub prev_state_root: StateRoot, + pub encoded_merkle_root: CryptoHash, + pub encoded_length: u64, + pub height_created: BlockHeight, + pub height_included: BlockHeight, + pub shard_id: ShardId, + pub gas_used: Gas, + pub gas_limit: Gas, + /// TODO(2271): deprecated. + #[serde(with = "dec_format")] + pub rent_paid: Balance, + /// TODO(2271): deprecated. + #[serde(with = "dec_format")] + pub validator_reward: Balance, + #[serde(with = "dec_format")] + pub balance_burnt: Balance, + pub outgoing_receipts_root: CryptoHash, + pub tx_root: CryptoHash, + pub validator_power_proposals: Vec, + pub validator_frozen_proposals: Vec, + pub signature: Signature, +} + +impl From for ChunkHeaderView { + fn from(chunk: ShardChunkHeader) -> Self { + let hash = chunk.chunk_hash(); + let signature = chunk.signature().clone(); + let height_included = chunk.height_included(); + let inner = chunk.take_inner(); + ChunkHeaderView { + chunk_hash: hash.0, + prev_block_hash: *inner.prev_block_hash(), + outcome_root: *inner.prev_outcome_root(), + prev_state_root: *inner.prev_state_root(), + encoded_merkle_root: *inner.encoded_merkle_root(), + encoded_length: inner.encoded_length(), + height_created: inner.height_created(), + height_included, + shard_id: inner.shard_id(), + gas_used: inner.prev_gas_used(), + gas_limit: inner.gas_limit(), + rent_paid: 0, + validator_reward: 0, + balance_burnt: inner.prev_balance_burnt(), + outgoing_receipts_root: *inner.prev_outgoing_receipts_root(), + tx_root: *inner.tx_root(), + validator_power_proposals: inner.prev_validator_power_proposals().map(Into::into).collect(), + validator_frozen_proposals: inner.prev_validator_frozen_proposals().map(Into::into).collect(), + signature, + } + } +} + +impl From for ShardChunkHeader { + fn from(view: ChunkHeaderView) -> Self { + let mut header = ShardChunkHeaderV3 { + inner: ShardChunkHeaderInner::V2(ShardChunkHeaderInnerV2 { + prev_block_hash: view.prev_block_hash, + prev_state_root: view.prev_state_root, + prev_outcome_root: view.outcome_root, + encoded_merkle_root: view.encoded_merkle_root, + encoded_length: view.encoded_length, + height_created: view.height_created, + shard_id: view.shard_id, + prev_gas_used: view.gas_used, + gas_limit: view.gas_limit, + prev_balance_burnt: view.balance_burnt, + prev_outgoing_receipts_root: view.outgoing_receipts_root, + tx_root: view.tx_root, + prev_validator_power_proposals: view + .validator_power_proposals + .into_iter() + .map(Into::into) + .collect(), + prev_validator_frozen_proposals: view + .validator_frozen_proposals + .into_iter() + .map(Into::into) + .collect(), + }), + height_included: view.height_included, + signature: view.signature, + hash: ChunkHash::default(), + }; + header.init(); + ShardChunkHeader::V3(header) + } +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct MinerChipsListView { + pub account_id: AccountId, + pub chips: Vec, +} +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct BlockView { + pub author: AccountId, + pub header: BlockHeaderView, + pub chunks: Vec, +} + +impl BlockView { + pub fn from_author_block(author: AccountId, block: Block) -> Self { + BlockView { + author, + header: block.header().clone().into(), + chunks: block.chunks().iter().cloned().map(Into::into).collect(), + } + } +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct ChunkView { + pub author: AccountId, + pub header: ChunkHeaderView, + pub transactions: Vec, + pub receipts: Vec, +} + +impl ChunkView { + pub fn from_author_chunk(author: AccountId, chunk: ShardChunk) -> Self { + match chunk { + ShardChunk::V1(chunk) => Self { + author, + header: ShardChunkHeader::V1(chunk.header).into(), + transactions: chunk.transactions.into_iter().map(Into::into).collect(), + receipts: chunk.prev_outgoing_receipts.into_iter().map(Into::into).collect(), + }, + ShardChunk::V2(chunk) => Self { + author, + header: chunk.header.into(), + transactions: chunk.transactions.into_iter().map(Into::into).collect(), + receipts: chunk.prev_outgoing_receipts.into_iter().map(Into::into).collect(), + }, + } + } +} + +#[serde_as] +#[derive( + BorshSerialize, + BorshDeserialize, + Clone, + Debug, + PartialEq, + Eq, + serde::Serialize, + serde::Deserialize, +)] +pub enum ActionView { + CreateAccount, + DeployContract { + #[serde_as(as = "Base64")] + code: Vec, + }, + FunctionCall { + method_name: String, + args: FunctionArgs, + gas: Gas, + #[serde(with = "dec_format")] + deposit: Balance, + }, + Transfer { + #[serde(with = "dec_format")] + deposit: Balance, + }, + Stake { + #[serde(with = "dec_format")] + stake: Balance, + public_key: PublicKey, + }, + AddKey { + public_key: PublicKey, + access_key: AccessKeyView, + }, + DeleteKey { + public_key: PublicKey, + }, + DeleteAccount { + beneficiary_id: AccountId, + }, + Delegate { + delegate_action: DelegateAction, + signature: Signature, + }, + RegisterRsa2048Keys { + public_key: PublicKey, + operation_type: u8, + #[serde_as(as = "Base64")] + args: Vec, + }, + CreateRsa2048Challenge { + public_key: PublicKey, + challenge_key: PublicKey, + #[serde_as(as = "Base64")] + args: Vec, + }, +} + +impl From for ActionView { + fn from(action: Action) -> Self { + match action { + Action::CreateAccount(_) => ActionView::CreateAccount, + Action::DeployContract(action) => { + let code = hash(&action.code).as_ref().to_vec(); + ActionView::DeployContract { code } + } + Action::FunctionCall(action) => ActionView::FunctionCall { + method_name: action.method_name, + args: action.args.into(), + gas: action.gas, + deposit: action.deposit, + }, + Action::Transfer(action) => ActionView::Transfer { deposit: action.deposit }, + Action::Stake(action) => { + ActionView::Stake { stake: action.stake, public_key: action.public_key } + } + Action::AddKey(action) => ActionView::AddKey { + public_key: action.public_key, + access_key: action.access_key.into(), + }, + Action::DeleteKey(action) => ActionView::DeleteKey { public_key: action.public_key }, + Action::DeleteAccount(action) => { + ActionView::DeleteAccount { beneficiary_id: action.beneficiary_id } + } + Action::Delegate(action) => ActionView::Delegate { + delegate_action: action.delegate_action, + signature: action.signature, + }, + Action::RegisterRsa2048Keys(action) => ActionView::RegisterRsa2048Keys { + public_key: action.public_key, + operation_type: action.operation_type, + args: action.args.into(), + }, + Action::CreateRsa2048Challenge(action) => ActionView::CreateRsa2048Challenge { + public_key: action.public_key, + challenge_key: action.challenge_key, + args: action.args.into(), + }, + } + } +} + +impl TryFrom for Action { + type Error = Box; + + fn try_from(action_view: ActionView) -> Result { + Ok(match action_view { + ActionView::CreateAccount => Action::CreateAccount(CreateAccountAction {}), + ActionView::DeployContract { code } => { + Action::DeployContract(DeployContractAction { code }) + } + ActionView::FunctionCall { method_name, args, gas, deposit } => { + Action::FunctionCall(Box::new(FunctionCallAction { + method_name, + args: args.into(), + gas, + deposit, + })) + } + ActionView::Transfer { deposit } => Action::Transfer(TransferAction { deposit }), + ActionView::Stake { stake, public_key } => { + Action::Stake(Box::new(StakeAction { stake, public_key })) + } + ActionView::AddKey { public_key, access_key } => { + Action::AddKey(Box::new(AddKeyAction { public_key, access_key: access_key.into() })) + } + ActionView::DeleteKey { public_key } => { + Action::DeleteKey(Box::new(DeleteKeyAction { public_key })) + } + ActionView::DeleteAccount { beneficiary_id } => { + Action::DeleteAccount(DeleteAccountAction { beneficiary_id }) + } + ActionView::Delegate { delegate_action, signature } => { + Action::Delegate(Box::new(SignedDelegateAction { delegate_action, signature })) + } + ActionView::RegisterRsa2048Keys { public_key, operation_type, args } => { + Action::RegisterRsa2048Keys(Box::new(RegisterRsa2048KeysAction { + public_key, + operation_type, + args: args.into(), + })) + } + ActionView::CreateRsa2048Challenge { public_key, challenge_key, args } => { + Action::CreateRsa2048Challenge(Box::new(CreateRsa2048ChallengeAction { + public_key, + challenge_key, + args: args.into(), + })) + } + }) + } +} + +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + PartialEq, + Eq, + Clone, + serde::Serialize, + serde::Deserialize, +)] +pub struct SignedTransactionView { + pub signer_id: AccountId, + pub public_key: PublicKey, + pub nonce: Nonce, + pub receiver_id: AccountId, + pub actions: Vec, + pub signature: Signature, + pub hash: CryptoHash, +} + +impl From for SignedTransactionView { + fn from(signed_tx: SignedTransaction) -> Self { + let hash = signed_tx.get_hash(); + SignedTransactionView { + signer_id: signed_tx.transaction.signer_id, + public_key: signed_tx.transaction.public_key, + nonce: signed_tx.transaction.nonce, + receiver_id: signed_tx.transaction.receiver_id, + actions: signed_tx + .transaction + .actions + .into_iter() + .map(|action| action.into()) + .collect(), + signature: signed_tx.signature, + hash, + } + } +} + +#[serde_as] +#[derive( + BorshSerialize, + BorshDeserialize, + serde::Serialize, + serde::Deserialize, + PartialEq, + Eq, + Clone, + Default, +)] +pub enum FinalExecutionStatus { + /// The execution has not yet started. + #[default] + NotStarted, + /// The execution has started and still going. + Started, + /// The execution has failed with the given error. + Failure(TxExecutionError), + /// The execution has succeeded and returned some value or an empty vec encoded in base64. + SuccessValue(#[serde_as(as = "Base64")] Vec), +} + +impl fmt::Debug for FinalExecutionStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + FinalExecutionStatus::NotStarted => f.write_str("NotStarted"), + FinalExecutionStatus::Started => f.write_str("Started"), + FinalExecutionStatus::Failure(e) => f.write_fmt(format_args!("Failure({:?})", e)), + FinalExecutionStatus::SuccessValue(v) => { + f.write_fmt(format_args!("SuccessValue({})", AbbrBytes(v))) + } + } + } +} + +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + PartialEq, + Eq, + Clone, + serde::Serialize, + serde::Deserialize, +)] +pub enum ServerError { + TxExecutionError(TxExecutionError), + Timeout, + Closed, +} + +#[serde_as] +#[derive( + BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, +)] +pub enum ExecutionStatusView { + /// The execution is pending or unknown. + Unknown, + /// The execution has failed. + Failure(TxExecutionError), + /// The final action succeeded and returned some value or an empty vec encoded in base64. + SuccessValue(#[serde_as(as = "Base64")] Vec), + /// The final action of the receipt returned a promise or the signed transaction was converted + /// to a receipt. Contains the receipt_id of the generated receipt. + SuccessReceiptId(CryptoHash), +} + +impl fmt::Debug for ExecutionStatusView { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ExecutionStatusView::Unknown => f.write_str("Unknown"), + ExecutionStatusView::Failure(e) => f.write_fmt(format_args!("Failure({:?})", e)), + ExecutionStatusView::SuccessValue(v) => { + f.write_fmt(format_args!("SuccessValue({})", AbbrBytes(v))) + } + ExecutionStatusView::SuccessReceiptId(receipt_id) => { + f.write_fmt(format_args!("SuccessReceiptId({})", receipt_id)) + } + } + } +} + +impl From for ExecutionStatusView { + fn from(outcome: ExecutionStatus) -> Self { + match outcome { + ExecutionStatus::Unknown => ExecutionStatusView::Unknown, + ExecutionStatus::Failure(e) => ExecutionStatusView::Failure(e), + ExecutionStatus::SuccessValue(v) => ExecutionStatusView::SuccessValue(v), + ExecutionStatus::SuccessReceiptId(receipt_id) => { + ExecutionStatusView::SuccessReceiptId(receipt_id) + } + } + } +} + +#[derive( + BorshSerialize, + BorshDeserialize, + PartialEq, + Clone, + Eq, + Debug, + serde::Serialize, + serde::Deserialize, +)] +pub struct CostGasUsed { + pub cost_category: String, + pub cost: String, + #[serde(with = "dec_format")] + pub gas_used: Gas, +} + +#[derive( + BorshSerialize, + BorshDeserialize, + PartialEq, + Clone, + Eq, + Debug, + serde::Serialize, + serde::Deserialize, +)] +pub struct ExecutionMetadataView { + pub version: u32, + pub gas_profile: Option>, +} + +impl Default for ExecutionMetadataView { + fn default() -> Self { + ExecutionMetadata::V1.into() + } +} + +impl From for ExecutionMetadataView { + fn from(metadata: ExecutionMetadata) -> Self { + let version = match metadata { + ExecutionMetadata::V1 => 1, + ExecutionMetadata::V2(_) => 2, + ExecutionMetadata::V3(_) => 3, + }; + let mut gas_profile = match metadata { + ExecutionMetadata::V1 => None, + ExecutionMetadata::V2(profile_data) => { + // Add actions, wasm op, and ext costs in groups. + + // actions should use the old format, since `ActionCosts` + // includes more detailed entries than were present in the old + // profile + let mut costs: Vec = profile_data + .legacy_action_costs() + .into_iter() + .filter(|&(_, gas)| gas > 0) + .map(|(name, gas)| CostGasUsed::action(name.to_string(), gas)) + .collect(); + + // wasm op is a single cost, for historical reasons it is inaccurately displayed as "wasm host" + costs.push(CostGasUsed::wasm_host( + "WASM_INSTRUCTION".to_string(), + profile_data.get_wasm_cost(), + )); + + // ext costs are 1-to-1, except for those added later which we will display as 0 + for ext_cost in ExtCosts::iter() { + costs.push(CostGasUsed::wasm_host( + format!("{:?}", ext_cost).to_ascii_uppercase(), + profile_data.get_ext_cost(ext_cost), + )); + } + + Some(costs) + } + ExecutionMetadata::V3(profile) => { + // Add actions, wasm op, and ext costs in groups. + // actions costs are 1-to-1 + let mut costs: Vec = ActionCosts::iter() + .flat_map(|cost| { + let gas_used = profile.get_action_cost(cost); + (gas_used > 0).then(|| { + CostGasUsed::action( + format!("{:?}", cost).to_ascii_uppercase(), + gas_used, + ) + }) + }) + .collect(); + + // wasm op is a single cost, for historical reasons it is inaccurately displayed as "wasm host" + let wasm_gas_used = profile.get_wasm_cost(); + if wasm_gas_used > 0 { + costs.push(CostGasUsed::wasm_host( + "WASM_INSTRUCTION".to_string(), + wasm_gas_used, + )); + } + + // ext costs are 1-to-1 + for ext_cost in ExtCosts::iter() { + let gas_used = profile.get_ext_cost(ext_cost); + if gas_used > 0 { + costs.push(CostGasUsed::wasm_host( + format!("{:?}", ext_cost).to_ascii_uppercase(), + gas_used, + )); + } + } + + Some(costs) + } + }; + if let Some(ref mut costs) = gas_profile { + // The order doesn't really matter, but the default one is just + // historical, which is especially unintuitive, so let's sort + // lexicographically. + // + // Can't `sort_by_key` here because lifetime inference in + // closures is limited. + costs.sort_by(|lhs, rhs| { + lhs.cost_category.cmp(&rhs.cost_category).then_with(|| lhs.cost.cmp(&rhs.cost)) + }); + } + ExecutionMetadataView { version, gas_profile } + } +} + +impl CostGasUsed { + pub fn action(cost: String, gas_used: Gas) -> Self { + Self { cost_category: "ACTION_COST".to_string(), cost, gas_used } + } + + pub fn wasm_host(cost: String, gas_used: Gas) -> Self { + Self { cost_category: "WASM_HOST_COST".to_string(), cost, gas_used } + } +} + +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + Clone, + PartialEq, + Eq, + serde::Serialize, + serde::Deserialize, +)] +pub struct ExecutionOutcomeView { + /// Logs from this transaction or receipt. + pub logs: Vec, + /// Receipt IDs generated by this transaction or receipt. + pub receipt_ids: Vec, + /// The amount of the gas burnt by the given transaction or receipt. + pub gas_burnt: Gas, + /// The amount of tokens burnt corresponding to the burnt gas amount. + /// This value doesn't always equal to the `gas_burnt` multiplied by the gas price, because + /// the prepaid gas price might be lower than the actual gas price and it creates a deficit. + #[serde(with = "dec_format")] + pub tokens_burnt: Balance, + /// The id of the account on which the execution happens. For transaction this is signer_id, + /// for receipt this is receiver_id. + pub executor_id: AccountId, + /// Execution status. Contains the result in case of successful execution. + pub status: ExecutionStatusView, + /// Execution metadata, versioned + #[serde(default)] + pub metadata: ExecutionMetadataView, +} + +impl From for ExecutionOutcomeView { + fn from(outcome: ExecutionOutcome) -> Self { + Self { + logs: outcome.logs, + receipt_ids: outcome.receipt_ids, + gas_burnt: outcome.gas_burnt, + tokens_burnt: outcome.tokens_burnt, + executor_id: outcome.executor_id, + status: outcome.status.into(), + metadata: outcome.metadata.into(), + } + } +} + +impl From<&ExecutionOutcomeView> for PartialExecutionOutcome { + fn from(outcome: &ExecutionOutcomeView) -> Self { + Self { + receipt_ids: outcome.receipt_ids.clone(), + gas_burnt: outcome.gas_burnt, + tokens_burnt: outcome.tokens_burnt, + executor_id: outcome.executor_id.clone(), + status: outcome.status.clone().into(), + } + } +} +impl From for PartialExecutionStatus { + fn from(status: ExecutionStatusView) -> PartialExecutionStatus { + match status { + ExecutionStatusView::Unknown => PartialExecutionStatus::Unknown, + ExecutionStatusView::Failure(_) => PartialExecutionStatus::Failure, + ExecutionStatusView::SuccessValue(value) => PartialExecutionStatus::SuccessValue(value), + ExecutionStatusView::SuccessReceiptId(id) => { + PartialExecutionStatus::SuccessReceiptId(id) + } + } + } +} + +impl ExecutionOutcomeView { + // Same behavior as ExecutionOutcomeWithId's to_hashes. + pub fn to_hashes(&self, id: CryptoHash) -> Vec { + let mut result = Vec::with_capacity(self.logs.len().saturating_add(2)); + result.push(id); + result.push(CryptoHash::hash_borsh(&PartialExecutionOutcome::from(self))); + result.extend(self.logs.iter().map(|log| hash(log.as_bytes()))); + result + } +} + +#[cfg_attr(feature = "deepsize_feature", derive(deepsize::DeepSizeOf))] +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + PartialEq, + Eq, + Clone, + serde::Serialize, + serde::Deserialize, +)] +pub struct ExecutionOutcomeWithIdView { + pub proof: MerklePath, + pub block_hash: CryptoHash, + pub id: CryptoHash, + pub outcome: ExecutionOutcomeView, +} + +impl From for ExecutionOutcomeWithIdView { + fn from(outcome_with_id_and_proof: ExecutionOutcomeWithIdAndProof) -> Self { + Self { + proof: outcome_with_id_and_proof.proof, + block_hash: outcome_with_id_and_proof.block_hash, + id: outcome_with_id_and_proof.outcome_with_id.id, + outcome: outcome_with_id_and_proof.outcome_with_id.outcome.into(), + } + } +} + +impl ExecutionOutcomeWithIdView { + pub fn to_hashes(&self) -> Vec { + self.outcome.to_hashes(self.id) + } +} +#[derive(Clone)] +pub struct TxStatusView { + pub execution_outcome: Option, + pub status: TxExecutionStatus, +} + +#[derive( + BorshSerialize, + BorshDeserialize, + serde::Serialize, + serde::Deserialize, + Clone, + Debug, + Default, + Eq, + PartialEq, + Ord, + PartialOrd, +)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum TxExecutionStatus { + /// Transaction is waiting to be included into the block + None, + /// Transaction is included into the block. The block may be not finalised yet + Included, + /// Transaction is included into finalised block + IncludedFinal, + /// Transaction is included into finalised block + + /// All the transaction receipts finished their execution. + /// The corresponding blocks for each receipt may be not finalised yet + Executed, + /// Transaction is included into finalised block + + /// Execution of transaction receipts is finalised + #[default] + Final, +} + +#[derive(BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize, Debug, Clone)] +#[serde(untagged)] +pub enum FinalExecutionOutcomeViewEnum { + FinalExecutionOutcome(FinalExecutionOutcomeView), + FinalExecutionOutcomeWithReceipt(FinalExecutionOutcomeWithReceiptView), +} + +impl FinalExecutionOutcomeViewEnum { + pub fn into_outcome(self) -> FinalExecutionOutcomeView { + match self { + Self::FinalExecutionOutcome(outcome) => outcome, + Self::FinalExecutionOutcomeWithReceipt(outcome) => outcome.final_outcome, + } + } +} + +impl TxStatusView { + pub fn into_outcome(self) -> Option { + self.execution_outcome.map(|outcome| match outcome { + FinalExecutionOutcomeViewEnum::FinalExecutionOutcome(outcome) => outcome, + FinalExecutionOutcomeViewEnum::FinalExecutionOutcomeWithReceipt(outcome) => { + outcome.final_outcome + } + }) + } +} + +/// Execution outcome of the transaction and all of subsequent the receipts. +/// Could be not finalised yet +#[derive( + BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone, +)] +pub struct FinalExecutionOutcomeView { + /// Execution status defined by chain.rs:get_final_transaction_result + /// FinalExecutionStatus::NotStarted - the tx is not converted to the receipt yet + /// FinalExecutionStatus::Started - we have at least 1 receipt, but the first leaf receipt_id (using dfs) hasn't finished the execution + /// FinalExecutionStatus::Failure - the result of the first leaf receipt_id + /// FinalExecutionStatus::SuccessValue - the result of the first leaf receipt_id + pub status: FinalExecutionStatus, + /// Signed Transaction + pub transaction: SignedTransactionView, + /// The execution outcome of the signed transaction. + pub transaction_outcome: ExecutionOutcomeWithIdView, + /// The execution outcome of receipts. + pub receipts_outcome: Vec, +} + +impl fmt::Debug for FinalExecutionOutcomeView { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FinalExecutionOutcome") + .field("status", &self.status) + .field("transaction", &self.transaction) + .field("transaction_outcome", &self.transaction_outcome) + .field("receipts_outcome", &Slice(&self.receipts_outcome)) + .finish() + } +} + +/// Final execution outcome of the transaction and all of subsequent the receipts. Also includes +/// the generated receipt. +#[derive( + BorshSerialize, + BorshDeserialize, + PartialEq, + Eq, + Clone, + Debug, + serde::Serialize, + serde::Deserialize, +)] +pub struct FinalExecutionOutcomeWithReceiptView { + /// Final outcome view without receipts + #[serde(flatten)] + pub final_outcome: FinalExecutionOutcomeView, + /// Receipts generated from the transaction + pub receipts: Vec, +} + +pub mod validator_frozen_view { + pub use super::ValidatorPowerViewV1; + use borsh::{BorshDeserialize, BorshSerialize}; + use unc_primitives_core::types::AccountId; + use serde::Deserialize; + use crate::types::validator_frozen::ValidatorFrozen; + + #[derive( + BorshSerialize, BorshDeserialize, serde::Serialize, Deserialize, Debug, Clone, Eq, PartialEq, + )] + #[serde(tag = "validator_frozen_struct_version")] + pub enum ValidatorFrozenView { + V1(crate::views::ValidatorFrozenViewV1), + } + + impl crate::views::validator_frozen_view::ValidatorFrozenView { + pub fn into_validator_frozen(self) -> ValidatorFrozen { + self.into() + } + + #[inline] + pub fn take_account_id(self) -> AccountId { + match self { + Self::V1(v1) => v1.account_id, + } + } + + #[inline] + pub fn account_id(&self) -> &AccountId { + match self { + Self::V1(v1) => &v1.account_id, + } + } + } + + impl From for crate::views::validator_frozen_view::ValidatorFrozenView { + fn from(frozen: ValidatorFrozen) -> Self { + match frozen { + ValidatorFrozen::V1(v1) => Self::V1(crate::views::ValidatorFrozenViewV1 { + account_id: v1.account_id, + public_key: v1.public_key, + frozen: v1.frozen, + }), + } + } + } + + impl From for ValidatorFrozen { + fn from(view: crate::views::validator_frozen_view::ValidatorFrozenView) -> Self { + match view { + crate::views::validator_frozen_view::ValidatorFrozenView::V1(v1) => Self::new_v1(v1.account_id, v1.public_key, v1.frozen), + } + } + } +} + +#[derive( +BorshSerialize, +BorshDeserialize, +Debug, +Clone, +Eq, +PartialEq, +serde::Serialize, +serde::Deserialize, +)] +pub struct ValidatorFrozenViewV1 { + pub account_id: AccountId, + pub public_key: PublicKey, + #[serde(with = "dec_format")] + pub frozen: Balance, +} +pub mod validator_power_view { + pub use super::ValidatorPowerViewV1; + use crate::types::validator_power::ValidatorPower; + use borsh::{BorshDeserialize, BorshSerialize}; + use unc_primitives_core::types::AccountId; + use serde::Deserialize; + + #[derive( + BorshSerialize, BorshDeserialize, serde::Serialize, Deserialize, Debug, Clone, Eq, PartialEq, + )] + #[serde(tag = "validator_power_struct_version")] + pub enum ValidatorPowerView { + V1(ValidatorPowerViewV1), + } + + impl ValidatorPowerView { + pub fn into_validator_power(self) -> ValidatorPower { + self.into() + } + + #[inline] + pub fn take_account_id(self) -> AccountId { + match self { + Self::V1(v1) => v1.account_id, + } + } + + #[inline] + pub fn account_id(&self) -> &AccountId { + match self { + Self::V1(v1) => &v1.account_id, + } + } + } + + impl From for ValidatorPowerView { + fn from(power: ValidatorPower) -> Self { + match power { + ValidatorPower::V1(v1) => Self::V1(ValidatorPowerViewV1 { + account_id: v1.account_id, + public_key: v1.public_key, + power: v1.power, + }), + } + } + } + + impl From for ValidatorPower { + fn from(view: ValidatorPowerView) -> Self { + match view { + ValidatorPowerView::V1(v1) => Self::new_v1(v1.account_id, v1.public_key, v1.power), + } + } + } +} + +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + Clone, + Eq, + PartialEq, + serde::Serialize, + serde::Deserialize, +)] +pub struct ValidatorPowerViewV1 { + pub account_id: AccountId, + pub public_key: PublicKey, + #[serde(with = "dec_format")] + pub power: Power, +} + +pub mod validator_power_and_frozen_view { + pub use super::ValidatorPowerViewV1; + use borsh::{BorshDeserialize, BorshSerialize}; + use unc_primitives_core::types::AccountId; + use serde::Deserialize; + use crate::types::validator_power_and_frozen::ValidatorPowerAndFrozen; + + #[derive( + BorshSerialize, BorshDeserialize, serde::Serialize, Deserialize, Debug, Clone, Eq, PartialEq, + )] + #[serde(tag = "validator_power_and_frozen_struct_version")] + pub enum ValidatorPowerAndFrozenView { + V1(crate::views::ValidatorPowerAndFrozenViewV1), + } + + impl crate::views::validator_power_and_frozen_view::ValidatorPowerAndFrozenView { + pub fn into_validator_power_and_frozen(self) -> ValidatorPowerAndFrozen { + self.into() + } + + #[inline] + pub fn take_account_id(self) -> AccountId { + match self { + Self::V1(v1) => v1.account_id, + } + } + + #[inline] + pub fn account_id(&self) -> &AccountId { + match self { + Self::V1(v1) => &v1.account_id, + } + } + } + + impl From for crate::views::validator_power_and_frozen_view::ValidatorPowerAndFrozenView { + fn from(power_frozen: ValidatorPowerAndFrozen) -> Self { + match power_frozen { + ValidatorPowerAndFrozen::V1(v1) => Self::V1(crate::views::ValidatorPowerAndFrozenViewV1 { + account_id: v1.account_id, + public_key: v1.public_key, + power: v1.power, + frozen: v1.frozen, + }), + } + } + } + + impl From for ValidatorPowerAndFrozen { + fn from(view: crate::views::validator_power_and_frozen_view::ValidatorPowerAndFrozenView) -> Self { + match view { + crate::views::validator_power_and_frozen_view::ValidatorPowerAndFrozenView::V1(v1) => Self::new_v1(v1.account_id, v1.public_key, v1.power, v1.frozen,), + } + } + } +} + +#[derive( +BorshSerialize, +BorshDeserialize, +Debug, +Clone, +Eq, +PartialEq, +serde::Serialize, +serde::Deserialize, +)] +pub struct ValidatorPowerAndFrozenViewV1 { + pub account_id: AccountId, + pub public_key: PublicKey, + #[serde(with = "dec_format")] + pub power: Power, + pub frozen: Balance, +} + +#[derive( + BorshSerialize, + BorshDeserialize, + Clone, + Debug, + PartialEq, + Eq, + serde::Serialize, + serde::Deserialize, +)] +pub struct ReceiptView { + pub predecessor_id: AccountId, + pub receiver_id: AccountId, + pub receipt_id: CryptoHash, + + pub receipt: ReceiptEnumView, +} + +#[derive( + BorshSerialize, + BorshDeserialize, + Clone, + Debug, + PartialEq, + Eq, + serde::Serialize, + serde::Deserialize, +)] +pub struct DataReceiverView { + pub data_id: CryptoHash, + pub receiver_id: AccountId, +} + +#[serde_as] +#[derive( + BorshSerialize, + BorshDeserialize, + Clone, + Debug, + PartialEq, + Eq, + serde::Serialize, + serde::Deserialize, +)] +pub enum ReceiptEnumView { + Action { + signer_id: AccountId, + signer_public_key: PublicKey, + #[serde(with = "dec_format")] + gas_price: Balance, + output_data_receivers: Vec, + input_data_ids: Vec, + actions: Vec, + }, + Data { + data_id: CryptoHash, + #[serde_as(as = "Option")] + data: Option>, + }, +} + +impl From for ReceiptView { + fn from(receipt: Receipt) -> Self { + ReceiptView { + predecessor_id: receipt.predecessor_id, + receiver_id: receipt.receiver_id, + receipt_id: receipt.receipt_id, + receipt: match receipt.receipt { + ReceiptEnum::Action(action_receipt) => ReceiptEnumView::Action { + signer_id: action_receipt.signer_id, + signer_public_key: action_receipt.signer_public_key, + gas_price: action_receipt.gas_price, + output_data_receivers: action_receipt + .output_data_receivers + .into_iter() + .map(|data_receiver| DataReceiverView { + data_id: data_receiver.data_id, + receiver_id: data_receiver.receiver_id, + }) + .collect(), + input_data_ids: action_receipt + .input_data_ids + .into_iter() + .map(Into::into) + .collect(), + actions: action_receipt.actions.into_iter().map(Into::into).collect(), + }, + ReceiptEnum::Data(data_receipt) => { + ReceiptEnumView::Data { data_id: data_receipt.data_id, data: data_receipt.data } + } + }, + } + } +} + +impl TryFrom for Receipt { + type Error = Box; + + fn try_from(receipt_view: ReceiptView) -> Result { + Ok(Receipt { + predecessor_id: receipt_view.predecessor_id, + receiver_id: receipt_view.receiver_id, + receipt_id: receipt_view.receipt_id, + receipt: match receipt_view.receipt { + ReceiptEnumView::Action { + signer_id, + signer_public_key, + gas_price, + output_data_receivers, + input_data_ids, + actions, + } => ReceiptEnum::Action(ActionReceipt { + signer_id, + signer_public_key, + gas_price, + output_data_receivers: output_data_receivers + .into_iter() + .map(|data_receiver_view| DataReceiver { + data_id: data_receiver_view.data_id, + receiver_id: data_receiver_view.receiver_id, + }) + .collect(), + input_data_ids: input_data_ids.into_iter().map(Into::into).collect(), + actions: actions + .into_iter() + .map(TryInto::try_into) + .collect::, _>>()?, + }), + ReceiptEnumView::Data { data_id, data } => { + ReceiptEnum::Data(DataReceipt { data_id, data }) + } + }, + }) + } +} + +/// Information about this epoch validators and next epoch validators +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct EpochValidatorInfo { + /// Validators for the current epoch + pub current_validators: Vec, + /// Validators for the next epoch + pub next_validators: Vec, + /// Fishermen for the current epoch + pub current_fishermen: Vec, + /// Fishermen for the next epoch + pub next_fishermen: Vec, + /// Power proposals in the current epoch + pub current_power_proposals: Vec, + /// Frozen proposals in the current epoch + pub current_frozen_proposals: Vec, + /// Kickout in the previous epoch + pub prev_epoch_kickout: Vec, + /// Epoch start block height + pub epoch_start_height: BlockHeight, + /// Epoch height + pub epoch_height: EpochHeight, +} + +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + PartialEq, + Eq, + Clone, + serde::Serialize, + serde::Deserialize, +)] +pub struct ValidatorKickoutView { + pub account_id: AccountId, + pub reason: ValidatorKickoutReason, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct CurrentEpochValidatorInfo { + pub account_id: AccountId, + pub public_key: PublicKey, + pub is_slashed: bool, + #[serde(with = "dec_format")] + pub power: Power, + #[serde(with = "dec_format")] + pub frozen: Balance, + pub shards: Vec, + pub num_produced_blocks: NumBlocks, + pub num_expected_blocks: NumBlocks, + #[serde(default)] + pub num_produced_chunks: NumBlocks, + #[serde(default)] + pub num_expected_chunks: NumBlocks, + // The following two fields correspond to the shards in the shard array. + #[serde(default)] + pub num_produced_chunks_per_shard: Vec, + #[serde(default)] + pub num_expected_chunks_per_shard: Vec, +} + +#[derive( + BorshSerialize, + BorshDeserialize, + Debug, + PartialEq, + Eq, + Clone, + serde::Serialize, + serde::Deserialize, +)] +pub struct NextEpochValidatorInfo { + pub account_id: AccountId, + pub public_key: PublicKey, + #[serde(with = "dec_format")] + pub power: Power, + #[serde(with = "dec_format")] + pub frozen: Balance, + pub shards: Vec, +} + +#[derive( + PartialEq, + Eq, + Debug, + Clone, + BorshDeserialize, + BorshSerialize, + serde::Serialize, + serde::Deserialize, +)] +pub struct LightClientBlockView { + pub prev_block_hash: CryptoHash, + pub next_block_inner_hash: CryptoHash, + pub inner_lite: BlockHeaderInnerLiteView, + pub inner_rest_hash: CryptoHash, + pub next_bps: Option>, + pub approvals_after_next: Vec>>, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, BorshDeserialize, BorshSerialize)] +pub struct LightClientBlockLiteView { + pub prev_block_hash: CryptoHash, + pub inner_rest_hash: CryptoHash, + pub inner_lite: BlockHeaderInnerLiteView, +} + +impl From for LightClientBlockLiteView { + fn from(header: BlockHeader) -> Self { + Self { + prev_block_hash: *header.prev_hash(), + inner_rest_hash: hash(&header.inner_rest_bytes()), + inner_lite: header.into(), + } + } +} +impl LightClientBlockLiteView { + pub fn hash(&self) -> CryptoHash { + let block_header_inner_lite: BlockHeaderInnerLite = self.inner_lite.clone().into(); + combine_hash( + &combine_hash( + &hash(&borsh::to_vec(&block_header_inner_lite).unwrap()), + &self.inner_rest_hash, + ), + &self.prev_block_hash, + ) + } +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct GasPriceView { + #[serde(with = "dec_format")] + pub gas_price: Balance, +} + +/// It is a [serializable view] of [`StateChangesRequest`]. +/// +/// [serializable view]: ./index.html +/// [`StateChangesRequest`]: ../types/struct.StateChangesRequest.html +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "changes_type", rename_all = "snake_case")] +pub enum StateChangesRequestView { + AccountChanges { + account_ids: Vec, + }, + SingleAccessKeyChanges { + keys: Vec, + }, + AllAccessKeyChanges { + account_ids: Vec, + }, + ContractCodeChanges { + account_ids: Vec, + }, + DataChanges { + account_ids: Vec, + #[serde(rename = "key_prefix_base64")] + key_prefix: StoreKey, + }, +} + +impl From for StateChangesRequest { + fn from(request: StateChangesRequestView) -> Self { + match request { + StateChangesRequestView::AccountChanges { account_ids } => { + Self::AccountChanges { account_ids } + } + StateChangesRequestView::SingleAccessKeyChanges { keys } => { + Self::SingleAccessKeyChanges { keys } + } + StateChangesRequestView::AllAccessKeyChanges { account_ids } => { + Self::AllAccessKeyChanges { account_ids } + } + StateChangesRequestView::ContractCodeChanges { account_ids } => { + Self::ContractCodeChanges { account_ids } + } + StateChangesRequestView::DataChanges { account_ids, key_prefix } => { + Self::DataChanges { account_ids, key_prefix } + } + } + } +} + +/// It is a [serializable view] of [`StateChangeKind`]. +/// +/// [serializable view]: ./index.html +/// [`StateChangeKind`]: ../types/struct.StateChangeKind.html +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "snake_case", tag = "type")] +pub enum StateChangeKindView { + AccountTouched { account_id: AccountId }, + AccessKeyTouched { account_id: AccountId }, + DataTouched { account_id: AccountId }, + ContractCodeTouched { account_id: AccountId }, + RsaKeyTouched { account_id: AccountId }, +} + +impl From for StateChangeKindView { + fn from(state_change_kind: StateChangeKind) -> Self { + match state_change_kind { + StateChangeKind::AccountTouched { account_id } => Self::AccountTouched { account_id }, + StateChangeKind::AccessKeyTouched { account_id } => { + Self::AccessKeyTouched { account_id } + } + StateChangeKind::RsaKeyTouched { account_id } => { + Self::RsaKeyTouched { account_id } + } + StateChangeKind::DataTouched { account_id } => Self::DataTouched { account_id }, + StateChangeKind::ContractCodeTouched { account_id } => { + Self::ContractCodeTouched { account_id } + } + } + } +} + +pub type StateChangesKindsView = Vec; + +/// See crate::types::StateChangeCause for details. +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "snake_case", tag = "type")] +pub enum StateChangeCauseView { + NotWritableToDisk, + InitialState, + TransactionProcessing { tx_hash: CryptoHash }, + ActionReceiptProcessingStarted { receipt_hash: CryptoHash }, + ActionReceiptGasReward { receipt_hash: CryptoHash }, + ReceiptProcessing { receipt_hash: CryptoHash }, + PostponedReceipt { receipt_hash: CryptoHash }, + UpdatedDelayedReceipts, + ValidatorAccountsUpdate, + Migration, + Resharding, +} + +impl From for StateChangeCauseView { + fn from(state_change_cause: StateChangeCause) -> Self { + match state_change_cause { + StateChangeCause::NotWritableToDisk => Self::NotWritableToDisk, + StateChangeCause::InitialState => Self::InitialState, + StateChangeCause::TransactionProcessing { tx_hash } => { + Self::TransactionProcessing { tx_hash } + } + StateChangeCause::ActionReceiptProcessingStarted { receipt_hash } => { + Self::ActionReceiptProcessingStarted { receipt_hash } + } + StateChangeCause::ActionReceiptGasReward { receipt_hash } => { + Self::ActionReceiptGasReward { receipt_hash } + } + StateChangeCause::ReceiptProcessing { receipt_hash } => { + Self::ReceiptProcessing { receipt_hash } + } + StateChangeCause::PostponedReceipt { receipt_hash } => { + Self::PostponedReceipt { receipt_hash } + } + StateChangeCause::UpdatedDelayedReceipts => Self::UpdatedDelayedReceipts, + StateChangeCause::ValidatorAccountsUpdate => Self::ValidatorAccountsUpdate, + StateChangeCause::Migration => Self::Migration, + StateChangeCause::Resharding => Self::Resharding, + } + } +} + +#[serde_as] +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "snake_case", tag = "type", content = "change")] +pub enum StateChangeValueView { + AccountUpdate { + account_id: AccountId, + #[serde(flatten)] + account: AccountView, + }, + AccountDeletion { + account_id: AccountId, + }, + AccessKeyUpdate { + account_id: AccountId, + public_key: PublicKey, + access_key: AccessKeyView, + }, + AccessKeyDeletion { + account_id: AccountId, + public_key: PublicKey, + }, + DataUpdate { + account_id: AccountId, + #[serde(rename = "key_base64")] + key: StoreKey, + #[serde(rename = "value_base64")] + value: StoreValue, + }, + DataDeletion { + account_id: AccountId, + #[serde(rename = "key_base64")] + key: StoreKey, + }, + ContractCodeUpdate { + account_id: AccountId, + #[serde(rename = "code_base64")] + #[serde_as(as = "Base64")] + code: Vec, + }, + ContractCodeDeletion { + account_id: AccountId, + }, + RsaKeyUpdate { + account_id: AccountId, + public_key: PublicKey, + rsa_key: RegisterRsa2048KeysAction, + }, + RsaKeyDeletion { + account_id: AccountId, + public_key: PublicKey, + }, +} + +impl From for StateChangeValueView { + fn from(state_change: StateChangeValue) -> Self { + match state_change { + StateChangeValue::AccountUpdate { account_id, account } => { + Self::AccountUpdate { account_id, account: account.into() } + } + StateChangeValue::AccountDeletion { account_id } => { + Self::AccountDeletion { account_id } + } + StateChangeValue::AccessKeyUpdate { account_id, public_key, access_key } => { + Self::AccessKeyUpdate { account_id, public_key, access_key: access_key.into() } + } + StateChangeValue::AccessKeyDeletion { account_id, public_key } => { + Self::AccessKeyDeletion { account_id, public_key } + } + StateChangeValue::DataUpdate { account_id, key, value } => { + Self::DataUpdate { account_id, key, value } + } + StateChangeValue::DataDeletion { account_id, key } => { + Self::DataDeletion { account_id, key } + } + StateChangeValue::ContractCodeUpdate { account_id, code } => { + Self::ContractCodeUpdate { account_id, code } + } + StateChangeValue::ContractCodeDeletion { account_id } => { + Self::ContractCodeDeletion { account_id } + } + StateChangeValue::RsaKeyUpdate { account_id, public_key, rsa_key } => { + Self::RsaKeyUpdate { account_id, public_key, rsa_key: rsa_key.into() } + } + StateChangeValue::RsaKeyDeletion { account_id, public_key } => { + Self::RsaKeyDeletion { account_id, public_key } + } + } + } +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct StateChangeWithCauseView { + pub cause: StateChangeCauseView, + #[serde(flatten)] + pub value: StateChangeValueView, +} + +impl From for StateChangeWithCauseView { + fn from(state_change_with_cause: StateChangeWithCause) -> Self { + let StateChangeWithCause { cause, value } = state_change_with_cause; + Self { cause: cause.into(), value: value.into() } + } +} + +pub type StateChangesView = Vec; + +/// Maintenance windows view are a vector of maintenance window. +pub type MaintenanceWindowsView = Vec>; + +/// Contains the split storage information. +#[derive(serde::Serialize, serde::Deserialize, Debug)] +pub struct SplitStorageInfoView { + pub head_height: Option, + pub final_head_height: Option, + pub cold_head_height: Option, + + pub hot_db_kind: Option, +} + +#[cfg(test)] +mod tests { + use super::ExecutionMetadataView; + use crate::transaction::ExecutionMetadata; + use unc_vm_runner::{ProfileDataV2, ProfileDataV3}; + + /// The JSON representation used in RPC responses must not remove or rename + /// fields, only adding fields is allowed or we risk breaking clients. + #[test] + #[cfg_attr(feature = "nightly", ignore)] + fn test_runtime_config_view() { + use unc_parameters::{RuntimeConfig, RuntimeConfigStore, RuntimeConfigView}; + use unc_primitives_core::version::PROTOCOL_VERSION; + + let config_store = RuntimeConfigStore::new(None); + let config = config_store.get_config(PROTOCOL_VERSION); + let view = RuntimeConfigView::from(RuntimeConfig::clone(config)); + insta::assert_json_snapshot!(&view, { ".wasm_config.vm_kind" => ""}); + } + + /// `ExecutionMetadataView` with profile V1 displayed on the RPC should not change. + #[test] + #[cfg_attr(feature = "nightly", ignore)] + fn test_exec_metadata_v1_view() { + let metadata = ExecutionMetadata::V1; + let view = ExecutionMetadataView::from(metadata); + insta::assert_json_snapshot!(view); + } + + /// `ExecutionMetadataView` with profile V2 displayed on the RPC should not change. + #[test] + #[cfg_attr(feature = "nightly", ignore)] + fn test_exec_metadata_v2_view() { + let metadata = ExecutionMetadata::V2(ProfileDataV2::test()); + let view = ExecutionMetadataView::from(metadata); + insta::assert_json_snapshot!(view); + } + + /// `ExecutionMetadataView` with profile V3 displayed on the RPC should not change. + #[test] + #[cfg_attr(feature = "nightly", ignore)] + fn test_exec_metadata_v3_view() { + let metadata = ExecutionMetadata::V3(ProfileDataV3::test().into()); + let view = ExecutionMetadataView::from(metadata); + insta::assert_json_snapshot!(view); + } +} diff --git a/core/store/Cargo.toml b/core/store/Cargo.toml new file mode 100644 index 000000000..a46a5fc52 --- /dev/null +++ b/core/store/Cargo.toml @@ -0,0 +1,98 @@ +[package] +name = "unc-store" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix-rt.workspace = true +actix.workspace = true +anyhow.workspace = true +borsh.workspace = true +bytesize.workspace = true +crossbeam.workspace = true +derive_more.workspace = true +elastic-array.workspace = true +enum-map.workspace = true +fs2.workspace = true +hex.workspace = true +itoa.workspace = true +itertools.workspace = true +lru.workspace = true +num_cpus.workspace = true +once_cell.workspace = true +rand.workspace = true +rayon.workspace = true +rlimit.workspace = true +rocksdb.workspace = true +serde.workspace = true +serde_json.workspace = true +stdx.workspace = true +strum.workspace = true +tempfile.workspace = true +thiserror.workspace = true +tokio.workspace = true +tracing.workspace = true + +unc-chain-configs = { workspace = true, features = ["metrics"] } +unc-crypto.workspace = true +unc-fmt.workspace = true +unc-o11y.workspace = true +unc-parameters.workspace = true +unc-primitives.workspace = true +unc-vm-runner.workspace = true + +[dev-dependencies] +assert_matches.workspace = true +bencher.workspace = true +insta.workspace = true +unc-chain.workspace = true +unc-chunks.workspace = true +rand.workspace = true +thiserror.workspace = true + +[[bench]] +name = "trie_bench" +harness = false + +[[bench]] +name = "store_bench" +harness = false + +[[bench]] +name = "finalize_bench" +harness = false + +[features] +default = [] +io_trace = [] +no_cache = [] +single_thread_rocksdb = [] # Deactivate RocksDB IO background threads +test_features = [] +serialize_all_state_changes = [] +new_epoch_sync = [] + +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-fmt/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-parameters/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-vm-runner/nightly_protocol", +] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-fmt/nightly", + "unc-o11y/nightly", + "unc-parameters/nightly", + "unc-primitives/nightly", + "unc-vm-runner/nightly", +] diff --git a/core/store/benches/finalize_bench.rs b/core/store/benches/finalize_bench.rs new file mode 100644 index 000000000..9a31212c2 --- /dev/null +++ b/core/store/benches/finalize_bench.rs @@ -0,0 +1,271 @@ +//! Benchmarks for the in-memory preparation of writes before the commit, aka +//! `finalize`. +//! +//! All writes are batched to a DB transaction before being committed. This +//! involves copying around the data and deserializing it. +//! +//! Since this is done on the hot path, whereas the actual commit can often be +//! done in a different thread, we have this benchmark to test the speed. +//! +//! Right now, the module only contains test for `ShardChunk` and +//! `PartialEncodedChunk`. These have been observed to become quite large and +//! have ab impact on the overall client performance. + +#[macro_use] +extern crate bencher; + +use bencher::{black_box, Bencher}; +use borsh::BorshSerialize; +use unc_chain::Chain; +use unc_chunks::ShardsManager; +use unc_crypto::{InMemorySigner, KeyType, Signer}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::{merklize, MerklePathItem}; +use unc_primitives::receipt::{ActionReceipt, DataReceipt, Receipt, ReceiptEnum}; +use unc_primitives::shard_layout::ShardLayout; +use unc_primitives::sharding::{ + ChunkHash, EncodedShardChunk, PartialEncodedChunk, PartialEncodedChunkPart, + PartialEncodedChunkV2, ReceiptProof, ReedSolomonWrapper, ShardChunk, ShardChunkHeader, + ShardChunkHeaderV3, ShardChunkV2, ShardProof, +}; +use unc_primitives::transaction::{Action, FunctionCallAction, SignedTransaction}; +use unc_primitives::types::AccountId; +use unc_primitives::validator_signer::InMemoryValidatorSigner; +use unc_store::DBCol; +use rand::prelude::SliceRandom; + +/// `ShardChunk` -> `StoreUpdate::insert_ser`. +/// +/// ~6ms or faster for ~24MB receipts +fn benchmark_write_shard_chunk(bench: &mut Bencher) { + let transactions = vec![]; + let receipts = create_benchmark_receipts(); + let chunk_hash: ChunkHash = CryptoHash::default().into(); + let chunk = create_shard_chunk(&chunk_hash, transactions, receipts); + + let chunks = spread_in_memory(chunk); + + let store = unc_store::test_utils::create_test_store(); + bench.iter(|| { + let mut update = store.store_update(); + update + .insert_ser(DBCol::Chunks, chunk_hash.as_ref(), &chunks.choose(&mut rand::thread_rng())) + .unwrap(); + black_box(update); + }); +} + +/// `PartialEncodedChunk` -> `StoreUpdate::insert_ser`. +/// +/// ~70ms for ~24MB receipts +fn benchmark_write_partial_encoded_chunk(bench: &mut Bencher) { + let transactions = vec![]; + let receipts = create_benchmark_receipts(); + let chunk_hash: ChunkHash = CryptoHash::default().into(); + + let (encoded_chunk, merkle_paths) = create_encoded_shard_chunk(transactions, &receipts); + let partial_chunk = + encoded_chunk_to_partial_encoded_chunk(encoded_chunk, receipts, merkle_paths); + let chunks = spread_in_memory(partial_chunk); + + let store = unc_store::test_utils::create_test_store(); + bench.iter(|| { + let mut update = store.store_update(); + update + .insert_ser( + DBCol::PartialChunks, + chunk_hash.as_ref(), + &chunks.choose(&mut rand::thread_rng()), + ) + .unwrap(); + black_box(update); + }); +} + +fn validator_signer() -> InMemoryValidatorSigner { + InMemoryValidatorSigner::from_random("test".parse().unwrap(), KeyType::ED25519) +} + +/// All receipts together are ~24MB +/// +/// 24 MB isn't the norm but certainly possible. +fn create_benchmark_receipts() -> Vec { + let account_id: AccountId = "test".parse().unwrap(); + let signer = InMemorySigner::from_random(account_id.clone(), KeyType::ED25519); + let action = Action::FunctionCall(Box::new(FunctionCallAction { + args: vec![42u8; 2_000_000], + method_name: "foo".to_owned(), + gas: 10_000_000_000_000u64, + deposit: 1, + })); + + vec![ + create_action_receipt(&account_id, &signer, vec![action.clone()], vec![]), + create_data_receipt(&account_id, CryptoHash([1; 32]), 3_000_000), + create_data_receipt(&account_id, CryptoHash([2; 32]), 3_000_001), + create_action_receipt(&account_id, &signer, vec![action.clone()], vec![]), + create_data_receipt(&account_id, CryptoHash([3; 32]), 3_000_002), + create_data_receipt(&account_id, CryptoHash([4; 32]), 3_000_003), + create_action_receipt(&account_id, &signer, vec![action], vec![]), + create_data_receipt(&account_id, CryptoHash([5; 32]), 3_000_004), + create_data_receipt(&account_id, CryptoHash([6; 32]), 3_000_005), + ] +} + +fn create_chunk_header(height: u64, shard_id: u64) -> ShardChunkHeader { + ShardChunkHeader::V3(ShardChunkHeaderV3::new( + CryptoHash::default(), + CryptoHash::default(), + CryptoHash::default(), + CryptoHash::default(), + 1, + height, + shard_id, + 0, + 0, + 0, + CryptoHash::default(), + CryptoHash::default(), + vec![], + &validator_signer(), + )) +} + +fn create_action_receipt( + account_id: &AccountId, + signer: &InMemorySigner, + actions: Vec, + input_data_ids: Vec, +) -> Receipt { + Receipt { + predecessor_id: account_id.clone(), + receiver_id: account_id.clone(), + receipt_id: CryptoHash::hash_borsh(actions.clone()), + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: account_id.clone(), + signer_public_key: signer.public_key(), + gas_price: 100_000_000, + output_data_receivers: vec![], + input_data_ids, + actions, + }), + } +} + +fn create_data_receipt(account_id: &AccountId, data_id: CryptoHash, data_size: usize) -> Receipt { + Receipt { + predecessor_id: account_id.clone(), + receiver_id: account_id.clone(), + receipt_id: CryptoHash::hash_borsh(data_id), + receipt: ReceiptEnum::Data(DataReceipt { data_id, data: Some(vec![77u8; data_size]) }), + } +} + +fn create_shard_chunk( + chunk_hash: &ChunkHash, + transactions: Vec, + receipts: Vec, +) -> ShardChunk { + ShardChunk::V2(ShardChunkV2 { + chunk_hash: chunk_hash.clone(), + header: create_chunk_header(0, 0), + transactions, + prev_outgoing_receipts: receipts, + }) +} + +fn create_encoded_shard_chunk( + transactions: Vec, + receipts: &[Receipt], +) -> (EncodedShardChunk, Vec>) { + let mut rs = ReedSolomonWrapper::new(33, 67); + ShardsManager::create_encoded_shard_chunk( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + transactions, + receipts, + Default::default(), + Default::default(), + &validator_signer(), + &mut rs, + 100, + ) + .unwrap() +} + +fn encoded_chunk_to_partial_encoded_chunk( + encoded_chunk: EncodedShardChunk, + receipts: Vec, + merkle_paths: Vec>, +) -> PartialEncodedChunk { + let header = encoded_chunk.cloned_header(); + let shard_id = header.shard_id(); + let shard_layout = ShardLayout::get_simple_nightshade_layout_v2(); + + let hashes = Chain::build_receipts_hashes(&receipts, &shard_layout); + let (_root, proofs) = merklize(&hashes); + + let mut receipts_by_shard = Chain::group_receipts_by_shard(receipts, &shard_layout); + let receipt_proofs = proofs + .into_iter() + .enumerate() + .map(move |(proof_shard_id, proof)| { + let proof_shard_id = proof_shard_id as u64; + let receipts = receipts_by_shard.remove(&proof_shard_id).unwrap_or_else(Vec::new); + let shard_proof = + ShardProof { from_shard_id: shard_id, to_shard_id: proof_shard_id, proof }; + ReceiptProof(receipts, shard_proof) + }) + .collect(); + + let partial_chunk = PartialEncodedChunk::V2(PartialEncodedChunkV2 { + header, + parts: encoded_chunk + .content() + .parts + .clone() + .into_iter() + .zip(merkle_paths) + .enumerate() + .map(|(part_ord, (part, merkle_proof))| { + let part_ord = part_ord as u64; + let part = part.unwrap(); + PartialEncodedChunkPart { part_ord, part, merkle_proof } + }) + .collect(), + receipts: receipt_proofs, + }); + partial_chunk +} + +/// Copies the data to use more memory. +/// +/// We want in-memory speed. Modern CPUs have large L3 caches and we want to spread it such that L3 hits are rare. +/// 13th gen intel-i7 has 30MB L3 +/// Sapphire Rapids Xeon processors have up to 112.5MB smart cache. +/// AMD Ryzen 7000 series goes up to 128MB L3 +/// AMD Epyc 9684X has 1152MB L3! +/// +/// The problem is, spreading data far apart in memory makes the benchmark awfully slow. +/// For now, we pick 200MB and assume the benchmark is not run on a beast of a server CPU. +/// If you are running this benchmark on a machine with >128MB L3, increase the number below. +fn spread_in_memory(chunk: T) -> Vec +where + T: Clone + BorshSerialize, +{ + let memory_range = 200_000_000; + let num_chunks = memory_range / borsh::object_length(&chunk).unwrap(); + let chunks: Vec<_> = std::iter::repeat(chunk).take(num_chunks).collect(); + chunks +} + +benchmark_group!(benches, benchmark_write_shard_chunk, benchmark_write_partial_encoded_chunk); + +benchmark_main!(benches); diff --git a/core/store/benches/store_bench.rs b/core/store/benches/store_bench.rs new file mode 100644 index 000000000..505abf2df --- /dev/null +++ b/core/store/benches/store_bench.rs @@ -0,0 +1,96 @@ +#[macro_use] +extern crate bencher; + +use bencher::{black_box, Bencher}; +use unc_primitives::errors::StorageError; +use unc_store::{DBCol, NodeStorage, Store}; +use std::time::{Duration, Instant}; + +/// Run a benchmark to generate `num_keys` keys, each of size `key_size`, then write then +/// in random order to column `col` in store, and then read keys back from `col` in random order. +/// Works only for column configured without reference counting, that is `.is_rc() == false`. +fn benchmark_write_then_read_successful( + bench: &mut Bencher, + num_keys: usize, + key_size: usize, + max_value_size: usize, + col: DBCol, +) { + let tmp_dir = tempfile::tempdir().unwrap(); + // Use default StoreConfig rather than NodeStorage::test_opener so we’re using the + // same configuration as in production. + let store = NodeStorage::opener(tmp_dir.path(), false, &Default::default(), None) + .open() + .unwrap() + .get_hot_store(); + let keys = generate_keys(num_keys, key_size); + write_to_db(&store, &keys, max_value_size, col); + + bench.iter(move || { + let start = Instant::now(); + + let read_records = read_from_db(&store, &keys, col); + let took = start.elapsed(); + println!( + "took on avg {:?} op per sec {} got {}/{}", + took / (num_keys as u32), + (num_keys as u128) * Duration::from_secs(1).as_nanos() / took.as_nanos(), + read_records, + keys.len() + ); + }); +} + +/// Generate `count` keys of `key_size` length. +fn generate_keys(count: usize, key_size: usize) -> Vec> { + let mut res: Vec> = Vec::new(); + for _k in 0..count { + let key: Vec = (0..key_size).map(|_| rand::random::()).collect(); + + res.push(key) + } + res +} + +/// Read from DB value for given `kyes` in random order for `col`. +/// Works only for column configured without reference counting, that is `.is_rc() == false`. +fn read_from_db(store: &Store, keys: &[Vec], col: DBCol) -> usize { + let mut read = 0; + for _k in 0..keys.len() { + let r = rand::random::() % (keys.len() as u32); + let key = &keys[r as usize]; + + let val = store.get(col, key.as_ref()).map_err(|_| StorageError::StorageInternalError); + + if let Ok(Some(x)) = val { + black_box(x); + read += 1; + } + } + read +} + +/// Write random value of size between `0` and `max_value_size` to given `keys` at specific column +/// `col.` +/// Works only for column configured without reference counting, that is `.is_rc() == false`. +fn write_to_db(store: &Store, keys: &[Vec], max_value_size: usize, col: DBCol) { + let mut store_update = store.store_update(); + for key in keys.iter() { + let x: usize = rand::random::() % max_value_size; + let val: Vec = (0..x).map(|_| rand::random::()).collect(); + // NOTE: this + store_update.set(col, &key, &val); + } + store_update.commit().unwrap(); +} + +fn benchmark_write_then_read_successful_10m(bench: &mut Bencher) { + // By adding logs, I've seen a lot of write to keys with size 40, an values with sizes + // between 10 .. 333. + // NOTE: DBCol::BlockMerkleTree was chosen to be a column, where `.is_rc() == false`. + benchmark_write_then_read_successful(bench, 10_000_000, 40, 333, DBCol::BlockMerkleTree); +} + +benchmark_group!(benches, benchmark_write_then_read_successful_10m); + +benchmark_main!(benches); diff --git a/core/store/benches/trie_bench.rs b/core/store/benches/trie_bench.rs new file mode 100644 index 000000000..5cb5c2928 --- /dev/null +++ b/core/store/benches/trie_bench.rs @@ -0,0 +1,58 @@ +#[macro_use] +extern crate bencher; + +use bencher::Bencher; +use rand::random; + +use unc_primitives::shard_layout::ShardUId; +use unc_store::test_utils::TestTriesBuilder; +use unc_store::Trie; + +fn rand_bytes() -> Vec { + (0..10).map(|_| random::()).collect() +} + +fn trie_lookup(bench: &mut Bencher) { + let (changed_keys, trie) = { + let tries = TestTriesBuilder::new().build(); + + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), Trie::EMPTY_ROOT); + let mut changes = vec![]; + for _ in 0..100 { + changes.push((rand_bytes(), Some(rand_bytes()))); + } + let changed_keys = + changes.iter().map(|(key, _value)| key.clone()).collect::>>(); + let trie_changes = trie.update(changes).unwrap(); + let mut state_update = tries.store_update(); + let root = tries.apply_all(&trie_changes, ShardUId::single_shard(), &mut state_update); + state_update.commit().expect("Failed to commit"); + + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), root); + (changed_keys, trie) + }; + + bench.iter(|| { + for _ in 0..1 { + for key in changed_keys.iter() { + trie.get(key).unwrap(); + } + } + }); +} + +fn trie_update(bench: &mut Bencher) { + let tries = TestTriesBuilder::new().build(); + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), Trie::EMPTY_ROOT); + let mut changes = vec![]; + for _ in 0..100 { + changes.push((rand_bytes(), Some(rand_bytes()))); + } + + bench.iter(|| { + let _ = trie.update(changes.iter().cloned()); + }); +} + +benchmark_group!(benches, trie_lookup, trie_update); +benchmark_main!(benches); diff --git a/core/store/src/cold_storage.rs b/core/store/src/cold_storage.rs new file mode 100644 index 000000000..0abb9fd7f --- /dev/null +++ b/core/store/src/cold_storage.rs @@ -0,0 +1,685 @@ +use crate::columns::DBKeyType; +use crate::db::{ColdDB, COLD_HEAD_KEY, HEAD_KEY}; +use crate::trie::TrieRefcountAddition; +use crate::{metrics, DBCol, DBTransaction, Database, Store, TrieChanges}; + +use borsh::BorshDeserialize; +use unc_primitives::block::{Block, BlockHeader, Tip}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardLayout; +use unc_primitives::sharding::ShardChunk; +use unc_primitives::types::BlockHeight; +use std::collections::{hash_map, HashMap}; +use std::io; +use strum::IntoEnumIterator; + +type StoreKey = Vec; +type StoreValue = Option>; +type StoreCache = HashMap<(DBCol, StoreKey), StoreValue>; + +struct StoreWithCache<'a> { + store: &'a Store, + cache: StoreCache, +} + +/// The BatchTransaction can be used to write multiple set operations to the cold db in batches. +/// [`write`] is called every time `transaction_size` overgrows `threshold_transaction_size`. +/// [`write`] should also be called manually before dropping BatchTransaction to write any leftovers. +struct BatchTransaction { + cold_db: std::sync::Arc, + transaction: DBTransaction, + /// Size of all values keys and values in `transaction` in bytes. + transaction_size: usize, + /// Minimum size, after which we write transaction + threshold_transaction_size: usize, +} + +/// Updates provided cold database from provided hot store with information about block at `height`. +/// Returns if the block was copied (false only if height is not present in `hot_store`). +/// Block as `height` has to be final. +/// Wraps hot store in `StoreWithCache` for optimizing reads. +/// +/// First, we read from hot store information necessary +/// to determine all the keys that need to be updated in cold db. +/// Then we write updates to cold db column by column. +/// +/// This approach is used, because a key for db often combines several parts, +/// and many of those parts are reused across several cold columns (block hash, shard id, chunk hash, tx hash, ...). +/// Rather than manually combining those parts in the right order for every cold column, +/// we define `DBCol::key_type` to determine how a key for the column is formed, +/// `get_keys_from_store` to determine all possible keys only for needed key parts, +/// and `combine_keys` to generated all possible whole keys for the column based on order of those parts. +/// +/// To add a new column to cold storage, we need to +/// 1. add it to `DBCol::is_cold` list +/// 2. define `DBCol::key_type` for it (if it isn't already defined) +/// 3. add new clause in `get_keys_from_store` for new key types used for this column (if there are any) +pub fn update_cold_db( + cold_db: &ColdDB, + hot_store: &Store, + shard_layout: &ShardLayout, + height: &BlockHeight, +) -> io::Result { + let _span = tracing::debug_span!(target: "cold_store", "update cold db", height = height); + let _timer = metrics::COLD_COPY_DURATION.start_timer(); + + let mut store_with_cache = StoreWithCache { store: hot_store, cache: StoreCache::new() }; + + if store_with_cache.get(DBCol::BlockHeight, &height.to_le_bytes())?.is_none() { + return Ok(false); + } + + let height_key = height.to_le_bytes(); + let block_hash_vec = store_with_cache.get_or_err(DBCol::BlockHeight, &height_key)?; + let block_hash_key = block_hash_vec.as_slice(); + + let key_type_to_keys = + get_keys_from_store(&mut store_with_cache, shard_layout, &height_key, block_hash_key)?; + for col in DBCol::iter() { + if !col.is_cold() { + continue; + } + + if col == DBCol::State { + copy_state_from_store(shard_layout, block_hash_key, cold_db, &mut store_with_cache)?; + continue; + } + + let keys = combine_keys(&key_type_to_keys, &col.key_type()); + copy_from_store(cold_db, &mut store_with_cache, col, keys)?; + } + + Ok(true) +} + +// Correctly set the key and value on DBTransaction, taking reference counting +// into account. For non-rc columns it just sets the value. For rc columns it +// appends rc = 1 to the value and sets it. +fn rc_aware_set( + transaction: &mut DBTransaction, + col: DBCol, + key: Vec, + mut value: Vec, +) -> usize { + const ONE: &[u8] = &1i64.to_le_bytes(); + match col.is_rc() { + false => { + let size = key.len() + value.len(); + transaction.set(col, key, value); + return size; + } + true => { + value.extend_from_slice(&ONE); + let size = key.len() + value.len(); + transaction.update_refcount(col, key, value); + return size; + } + }; +} + +// A specialized version of copy_from_store for the State column. Finds all the +// State nodes that were inserted at given height by reading from the +// TrieChanges and inserts them into the cold store. +// +// The generic implementation is not efficient for State because it would +// attempt to read every node from every shard. Here we know exactly what shard +// the node belongs to. +fn copy_state_from_store( + shard_layout: &ShardLayout, + block_hash_key: &[u8], + cold_db: &ColdDB, + hot_store: &mut StoreWithCache, +) -> io::Result<()> { + let col = DBCol::State; + let _span = tracing::debug_span!(target: "cold_store", "copy_state_from_store", %col); + let instant = std::time::Instant::now(); + + let mut transaction = DBTransaction::new(); + for shard_uid in shard_layout.shard_uids() { + debug_assert_eq!( + DBCol::TrieChanges.key_type(), + &[DBKeyType::BlockHash, DBKeyType::ShardUId] + ); + + let shard_uid_key = shard_uid.to_bytes(); + let key = join_two_keys(&block_hash_key, &shard_uid_key); + let trie_changes: Option = + hot_store.get_ser::(DBCol::TrieChanges, &key)?; + + let Some(trie_changes) = trie_changes else { continue }; + for op in trie_changes.insertions() { + hot_store.insert_state_to_cache_from_op(op, &shard_uid_key); + + let key = join_two_keys(&shard_uid_key, op.hash().as_bytes()); + let value = hot_store.get(DBCol::State, &key)?; + let value = + value.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, hex::encode(&key)))?; + + tracing::trace!(target: "cold_store", pretty_key=?unc_fmt::StorageKey(&key), "copying state node to colddb"); + rc_aware_set(&mut transaction, DBCol::State, key, value); + } + } + + let read_duration = instant.elapsed(); + + let instant = std::time::Instant::now(); + cold_db.write(transaction)?; + let write_duration = instant.elapsed(); + + tracing::trace!(target: "cold_store", ?read_duration, ?write_duration, "finished"); + + Ok(()) +} + +/// Gets values for given keys in a column from provided hot_store. +/// Creates a transaction based on that values with set DBOp s. +/// Writes that transaction to cold_db. +fn copy_from_store( + cold_db: &ColdDB, + hot_store: &mut StoreWithCache, + col: DBCol, + keys: Vec, +) -> io::Result<()> { + debug_assert!(col.is_cold()); + + // note this function should only be used for state in tests where it's + // needed to copy state records from genesis + + let _span = tracing::debug_span!(target: "cold_store", "copy_from_store", col = %col); + let instant = std::time::Instant::now(); + + let mut transaction = DBTransaction::new(); + let mut good_keys = 0; + let total_keys = keys.len(); + for key in keys { + // TODO: Look into using RocksDB’s multi_key function. It + // might speed things up. Currently our Database abstraction + // doesn’t offer interface for it so that would need to be + // added. + let data = hot_store.get(col, &key)?; + if let Some(value) = data { + // TODO: As an optimisation, we might consider breaking the + // abstraction layer. Since we’re always writing to cold database, + // rather than using `cold_db: &dyn Database` argument we could have + // `cold_db: &ColdDB` and then some custom function which lets us + // write raw bytes. This would also allow us to bypass stripping and + // re-adding the reference count. + + good_keys += 1; + rc_aware_set(&mut transaction, col, key, value); + } + } + + let read_duration = instant.elapsed(); + + let instant = std::time::Instant::now(); + cold_db.write(transaction)?; + let write_duration = instant.elapsed(); + + tracing::trace!(target: "cold_store", ?col, ?good_keys, ?total_keys, ?read_duration, ?write_duration, "finished"); + + return Ok(()); +} + +/// This function sets the cold head to the Tip that reflect provided height in two places: +/// - In cold storage in HEAD key in BlockMisc column. +/// - In hot storage in COLD_HEAD key in BlockMisc column. +/// This function should be used after all of the blocks from genesis to `height` inclusive had been copied. +/// +/// This method relies on the fact that BlockHeight and BlockHeader are not garbage collectable. +/// (to construct the Tip we query hot_store for block hash and block header) +/// If this is to change, caller should be careful about `height` not being garbage collected in hot storage yet. +pub fn update_cold_head( + cold_db: &ColdDB, + hot_store: &Store, + height: &BlockHeight, +) -> io::Result<()> { + tracing::debug!(target: "cold_store", "update HEAD of cold db to {}", height); + + let mut store = StoreWithCache { store: hot_store, cache: StoreCache::new() }; + + let height_key = height.to_le_bytes(); + let block_hash_key = store.get_or_err(DBCol::BlockHeight, &height_key)?.as_slice().to_vec(); + let tip_header = &store.get_ser_or_err::(DBCol::BlockHeader, &block_hash_key)?; + let tip = Tip::from_header(tip_header); + + // Write HEAD to the cold db. + { + let mut transaction = DBTransaction::new(); + transaction.set(DBCol::BlockMisc, HEAD_KEY.to_vec(), borsh::to_vec(&tip)?); + cold_db.write(transaction)?; + } + + // Write COLD_HEAD_KEY to the cold db. + { + let mut transaction = DBTransaction::new(); + transaction.set(DBCol::BlockMisc, COLD_HEAD_KEY.to_vec(), borsh::to_vec(&tip)?); + cold_db.write(transaction)?; + } + + // Write COLD_HEAD to the hot db. + { + let mut transaction = DBTransaction::new(); + transaction.set(DBCol::BlockMisc, COLD_HEAD_KEY.to_vec(), borsh::to_vec(&tip)?); + hot_store.storage.write(transaction)?; + + crate::metrics::COLD_HEAD_HEIGHT.set(*height as i64); + } + + return Ok(()); +} + +pub enum CopyAllDataToColdStatus { + EverythingCopied, + Interrupted, +} + +/// Copies all contents of all cold columns from `hot_store` to `cold_db`. +/// Does it column by column, and because columns can be huge, writes in batches of ~`batch_size`. +pub fn copy_all_data_to_cold( + cold_db: std::sync::Arc, + hot_store: &Store, + batch_size: usize, + keep_going: &std::sync::Arc, +) -> io::Result { + for col in DBCol::iter() { + if col.is_cold() { + tracing::info!(target: "cold_store", ?col, "Started column migration"); + let mut transaction = BatchTransaction::new(cold_db.clone(), batch_size); + for result in hot_store.iter(col) { + if !keep_going.load(std::sync::atomic::Ordering::Relaxed) { + tracing::debug!(target: "cold_store", "stopping copy_all_data_to_cold"); + return Ok(CopyAllDataToColdStatus::Interrupted); + } + let (key, value) = result?; + transaction.set_and_write_if_full(col, key.to_vec(), value.to_vec())?; + } + transaction.write()?; + tracing::info!(target: "cold_store", ?col, "Finished column migration"); + } + } + Ok(CopyAllDataToColdStatus::EverythingCopied) +} + +// The copy_state_from_store function depends on the state nodes to be present +// in the trie changes. This isn't the case for genesis so instead this method +// can be used to copy the genesis records from hot to cold. +// TODO - How did copying from genesis worked in the prod migration to split storage? +pub fn test_cold_genesis_update(cold_db: &ColdDB, hot_store: &Store) -> io::Result<()> { + let mut store_with_cache = StoreWithCache { store: hot_store, cache: StoreCache::new() }; + for col in DBCol::iter() { + if !col.is_cold() { + continue; + } + + // Note that we use the generic implementation of `copy_from_store` also + // for the State column that otherwise should be copied using the + // specialized `copy_state_from_store`. + copy_from_store( + cold_db, + &mut store_with_cache, + col, + hot_store.iter(col).map(|x| x.unwrap().0.to_vec()).collect(), + )?; + } + Ok(()) +} + +pub fn test_get_store_reads(column: DBCol) -> u64 { + crate::metrics::COLD_MIGRATION_READS.with_label_values(&[<&str>::from(column)]).get() +} + +pub fn test_get_store_initial_writes(column: DBCol) -> u64 { + crate::metrics::COLD_STORE_MIGRATION_BATCH_WRITE_COUNT + .with_label_values(&[<&str>::from(column)]) + .get() +} + +/// Returns HashMap from DBKeyType to possible keys of that type for provided height. +/// Only constructs keys for key types that are used in cold columns. +/// The goal is to capture all changes to db made during production of the block at provided height. +/// So, for every KeyType we need to capture all the keys that are related to that block. +/// For BlockHash it is just one key -- block hash of that height. +/// But for TransactionHash, for example, it is all of the tx hashes in that block. +fn get_keys_from_store( + store: &mut StoreWithCache, + shard_layout: &ShardLayout, + height_key: &[u8], + block_hash_key: &[u8], +) -> io::Result>> { + let mut key_type_to_keys = HashMap::new(); + + let block: Block = store.get_ser_or_err(DBCol::Block, &block_hash_key)?; + let chunks = block + .chunks() + .iter() + .map(|chunk_header| { + store.get_ser_or_err(DBCol::Chunks, chunk_header.chunk_hash().as_bytes()) + }) + .collect::>>()?; + + for key_type in DBKeyType::iter() { + if key_type == DBKeyType::TrieNodeOrValueHash { + // The TrieNodeOrValueHash is only used in the State column, which is handled separately. + continue; + } + + key_type_to_keys.insert( + key_type, + match key_type { + DBKeyType::TrieNodeOrValueHash => { + unreachable!(); + } + DBKeyType::BlockHeight => vec![height_key.to_vec()], + DBKeyType::BlockHash => vec![block_hash_key.to_vec()], + DBKeyType::PreviousBlockHash => { + vec![block.header().prev_hash().as_bytes().to_vec()] + } + DBKeyType::ShardId => shard_layout + .shard_ids() + .map(|shard_id| shard_id.to_le_bytes().to_vec()) + .collect(), + DBKeyType::ShardUId => shard_layout + .shard_uids() + .map(|shard_uid| shard_uid.to_bytes().to_vec()) + .collect(), + // TODO: write StateChanges values to colddb directly, not to cache. + DBKeyType::TrieKey => { + let mut keys = vec![]; + store.iter_prefix_with_callback( + DBCol::StateChanges, + &block_hash_key, + |full_key| { + let mut full_key = Vec::from(full_key); + full_key.drain(..block_hash_key.len()); + keys.push(full_key); + }, + )?; + keys + } + DBKeyType::TransactionHash => chunks + .iter() + .flat_map(|c| c.transactions().iter().map(|t| t.get_hash().as_bytes().to_vec())) + .collect(), + DBKeyType::ReceiptHash => chunks + .iter() + .flat_map(|c| { + c.prev_outgoing_receipts().iter().map(|r| r.get_hash().as_bytes().to_vec()) + }) + .collect(), + DBKeyType::ChunkHash => { + chunks.iter().map(|c| c.chunk_hash().as_bytes().to_vec()).collect() + } + DBKeyType::OutcomeId => { + debug_assert_eq!( + DBCol::OutcomeIds.key_type(), + &[DBKeyType::BlockHash, DBKeyType::ShardId] + ); + shard_layout + .shard_ids() + .map(|shard_id| { + store.get_ser( + DBCol::OutcomeIds, + &join_two_keys(&block_hash_key, &shard_id.to_le_bytes()), + ) + }) + .collect::>>>>()? + .into_iter() + .flat_map(|hashes| { + hashes + .unwrap_or_default() + .into_iter() + .map(|hash| hash.as_bytes().to_vec()) + }) + .collect() + } + _ => { + vec![] + } + }, + ); + } + + Ok(key_type_to_keys) +} + +pub fn join_two_keys(prefix_key: &[u8], suffix_key: &[u8]) -> StoreKey { + [prefix_key, suffix_key].concat() +} + +/// Returns all possible keys for a column with key represented by a specific sequence of key types. +/// `key_type_to_value` -- result of `get_keys_from_store`, mapping from KeyType to all possible keys of that type. +/// `key_types` -- description of a final key, what sequence of key types forms a key, result of `DBCol::key_type`. +/// Basically, returns all possible combinations of keys from `key_type_to_value` for given order of key types. +pub fn combine_keys( + key_type_to_value: &HashMap>, + key_types: &[DBKeyType], +) -> Vec { + combine_keys_with_stop(key_type_to_value, key_types, key_types.len()) +} + +/// Recursive method to create every combination of keys values for given order of key types. +/// stop: usize -- what length of key_types to consider. +/// first generates all the key combination for first stop - 1 key types +/// then adds every key value for the last key type to every key value generated by previous call. +fn combine_keys_with_stop( + key_type_to_keys: &HashMap>, + keys_order: &[DBKeyType], + stop: usize, +) -> Vec { + // if no key types are provided, return one empty key value + if stop == 0 { + return vec![StoreKey::new()]; + } + let last_kt = &keys_order[stop - 1]; + // if one of the key types has no keys, no need to calculate anything, the result is empty + if key_type_to_keys[last_kt].is_empty() { + return vec![]; + } + let all_smaller_keys = combine_keys_with_stop(key_type_to_keys, keys_order, stop - 1); + let mut result_keys = vec![]; + for prefix_key in &all_smaller_keys { + for suffix_key in &key_type_to_keys[last_kt] { + result_keys.push(join_two_keys(prefix_key, suffix_key)); + } + } + result_keys +} + +fn option_to_not_found(res: io::Result>, field_name: F) -> io::Result +where + F: std::string::ToString, +{ + match res { + Ok(Some(o)) => Ok(o), + Ok(None) => Err(io::Error::new(io::ErrorKind::NotFound, field_name.to_string())), + Err(e) => Err(e), + } +} + +impl StoreWithCache<'_> { + pub fn iter_prefix_with_callback( + &mut self, + col: DBCol, + key_prefix: &[u8], + mut callback: impl FnMut(Box<[u8]>), + ) -> io::Result<()> { + for iter_result in self.store.iter_prefix(col, key_prefix) { + let (key, value) = iter_result?; + self.cache.insert((col, key.to_vec()), Some(value.into())); + callback(key); + } + Ok(()) + } + + pub fn get(&mut self, column: DBCol, key: &[u8]) -> io::Result { + if let hash_map::Entry::Vacant(e) = self.cache.entry((column, key.to_vec())) { + crate::metrics::COLD_MIGRATION_READS.with_label_values(&[<&str>::from(column)]).inc(); + e.insert(self.store.get(column, key)?.map(|x| x.as_slice().to_vec())); + } + Ok(self.cache[&(column, key.to_vec())].clone()) + } + + pub fn get_ser( + &mut self, + column: DBCol, + key: &[u8], + ) -> io::Result> { + match self.get(column, key)? { + Some(bytes) => Ok(Some(T::try_from_slice(&bytes)?)), + None => Ok(None), + } + } + + pub fn get_or_err(&mut self, column: DBCol, key: &[u8]) -> io::Result> { + option_to_not_found(self.get(column, key), format_args!("{:?}: {:?}", column, key)) + } + + pub fn get_ser_or_err( + &mut self, + column: DBCol, + key: &[u8], + ) -> io::Result { + option_to_not_found(self.get_ser(column, key), format_args!("{:?}: {:?}", column, key)) + } + + pub fn insert_state_to_cache_from_op( + &mut self, + op: &TrieRefcountAddition, + shard_uid_key: &[u8], + ) { + debug_assert_eq!( + DBCol::State.key_type(), + &[DBKeyType::ShardUId, DBKeyType::TrieNodeOrValueHash] + ); + self.cache.insert( + (DBCol::State, join_two_keys(shard_uid_key, op.hash().as_bytes())), + Some(op.payload().to_vec()), + ); + } +} + +impl BatchTransaction { + pub fn new(cold_db: std::sync::Arc, batch_size: usize) -> Self { + Self { + cold_db, + transaction: DBTransaction::new(), + transaction_size: 0, + threshold_transaction_size: batch_size, + } + } + + /// Adds a set DBOp to `self.transaction`. Updates `self.transaction_size`. + /// If `self.transaction_size` becomes too big, calls for write. + pub fn set_and_write_if_full( + &mut self, + col: DBCol, + key: Vec, + value: Vec, + ) -> io::Result<()> { + let size = rc_aware_set(&mut self.transaction, col, key, value); + self.transaction_size += size; + + if self.transaction_size > self.threshold_transaction_size { + self.write()?; + } + Ok(()) + } + + /// Writes `self.transaction` and replaces it with new empty DBTransaction. + /// Sets `self.transaction_size` to 0. + fn write(&mut self) -> io::Result<()> { + if self.transaction.ops.is_empty() { + return Ok(()); + } + + let column_label = [<&str>::from(self.transaction.ops[0].col())]; + + crate::metrics::COLD_STORE_MIGRATION_BATCH_WRITE_COUNT + .with_label_values(&column_label) + .inc(); + let _timer = crate::metrics::COLD_STORE_MIGRATION_BATCH_WRITE_TIME + .with_label_values(&column_label) + .start_timer(); + + tracing::info!( + target: "cold_store", + ?column_label, + tx_size_in_megabytes = self.transaction_size as f64 / 1e6, + "Writing a Cold Store transaction"); + + let transaction = std::mem::take(&mut self.transaction); + self.cold_db.write(transaction)?; + self.transaction_size = 0; + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::{combine_keys, StoreKey}; + use crate::columns::DBKeyType; + use std::collections::{HashMap, HashSet}; + + #[test] + fn test_combine_keys() { + // What DBKeyType s are used here does not matter + let key_type_to_keys = HashMap::from([ + (DBKeyType::BlockHash, vec![vec![1, 2, 3], vec![2, 3]]), + (DBKeyType::BlockHeight, vec![vec![0, 1], vec![3, 4, 5]]), + (DBKeyType::ShardId, vec![]), + ]); + + assert_eq!( + HashSet::::from_iter(combine_keys( + &key_type_to_keys, + &[DBKeyType::BlockHash, DBKeyType::BlockHeight] + )), + HashSet::::from_iter(vec![ + vec![1, 2, 3, 0, 1], + vec![1, 2, 3, 3, 4, 5], + vec![2, 3, 0, 1], + vec![2, 3, 3, 4, 5] + ]) + ); + + assert_eq!( + HashSet::::from_iter(combine_keys( + &key_type_to_keys, + &[DBKeyType::BlockHeight, DBKeyType::BlockHash, DBKeyType::BlockHeight] + )), + HashSet::::from_iter(vec![ + vec![0, 1, 1, 2, 3, 0, 1], + vec![0, 1, 1, 2, 3, 3, 4, 5], + vec![0, 1, 2, 3, 0, 1], + vec![0, 1, 2, 3, 3, 4, 5], + vec![3, 4, 5, 1, 2, 3, 0, 1], + vec![3, 4, 5, 1, 2, 3, 3, 4, 5], + vec![3, 4, 5, 2, 3, 0, 1], + vec![3, 4, 5, 2, 3, 3, 4, 5] + ]) + ); + + assert_eq!( + HashSet::::from_iter(combine_keys( + &key_type_to_keys, + &[DBKeyType::ShardId, DBKeyType::BlockHeight] + )), + HashSet::::from_iter(vec![]) + ); + + assert_eq!( + HashSet::::from_iter(combine_keys( + &key_type_to_keys, + &[DBKeyType::BlockHash, DBKeyType::ShardId] + )), + HashSet::::from_iter(vec![]) + ); + + assert_eq!( + HashSet::::from_iter(combine_keys(&key_type_to_keys, &[])), + HashSet::::from_iter(vec![vec![]]) + ); + } +} diff --git a/core/store/src/columns.rs b/core/store/src/columns.rs new file mode 100644 index 000000000..04b752c3b --- /dev/null +++ b/core/store/src/columns.rs @@ -0,0 +1,600 @@ +use std::fmt; + +/// This enum holds the information about the columns that we use within the +/// RocksDB storage. +/// +/// You can think about our storage as 2-dimensional table (with key and column +/// as indexes/coordinates). +/// +/// Note that the names of the variants in this enumeration correspond to the +/// name of the RocksDB column families. As such, it is *not* safe to rename +/// a variant. +/// +/// The only exception is adding an underscore at the beginning of the name to +/// indicate that the column has been deprecated. Deprecated columns are not +/// used except for the database migration code which needs to deal with the +/// deprecation. Make sure to add `#[strum(serialize = "OriginalName")]` +/// attribute in front of the variant when you deprecate a column. +#[derive( + PartialEq, Copy, Clone, Debug, Hash, Eq, enum_map::Enum, strum::EnumIter, strum::IntoStaticStr, +)] +pub enum DBCol { + /// Column to indicate which version of database this is. + /// - *Rows*: single row `"VERSION"` + /// - *Content type*: The version of the database (u32), serialized as JSON. + DbVersion, + /// Column that stores miscellaneous block-related cells. + /// - *Rows*: multiple, for example `"GENESIS_JSON_HASH"`, `"HEAD_KEY"`, `"LATEST_KNOWN_KEY"` etc. + /// - *Content type*: cell specific. + BlockMisc, + /// Column that stores Block content. + /// - *Rows*: block hash (CryptHash) + /// - *Content type*: [unc_primitives::block::Block] + Block, + /// Column that stores Block headers. + /// - *Rows*: block hash (CryptoHash) + /// - *Content type*: [unc_primitives::block_header::BlockHeader] + BlockHeader, + /// Column that stores mapping from block height to block hash on the current canonical chain. + /// (if you want to see all the blocks that we got for a given height, for example due to double signing etc, + /// look at BlockPerHeight column). + /// - *Rows*: height (u64) + /// - *Content type*: block hash (CryptoHash) + BlockHeight, + /// Column that stores the Trie state. + /// - *Rows*: trie_node_or_value_hash (CryptoHash) + /// - *Content type*: Serializd RawTrieNodeWithSize or value () + State, + /// Mapping from BlockChunk to ChunkExtra + /// - *Rows*: BlockChunk (block_hash, shard_uid) + /// - *Content type*: [unc_primitives::types::chunk_extra::ChunkExtra] + ChunkExtra, + /// Deprecated. + #[strum(serialize = "TransactionResult")] + _TransactionResult, + /// Mapping from Block + Shard to list of outgoing receipts. + /// - *Rows*: block + shard + /// - *Content type*: Vec of [unc_primitives::receipt::Receipt] + OutgoingReceipts, + /// Mapping from Block + Shard to list of incoming receipt proofs. + /// Each proof might prove multiple receipts. + /// - *Rows*: (block, shard) + /// - *Content type*: Vec of [unc_primitives::sharding::ReceiptProof] + IncomingReceipts, + /// Deprecated. + #[strum(serialize = "Peers")] + _Peers, + /// List of recent outbound TIER2 connections. We'll attempt to re-establish + /// these connections after node restart or upon disconnection. + /// - *Rows*: single row (empty row name) + /// - *Content type*: Vec of [network_primitives::types::ConnectionInfo] + RecentOutboundConnections, + /// Mapping from EpochId to EpochInfo + /// - *Rows*: EpochId (CryptoHash) + /// - *Content type*: [unc_primitives::epoch_manager::epoch_info::EpochInfo] + EpochInfo, + /// Mapping from BlockHash to BlockInfo + /// - *Rows*: BlockHash (CryptoHash) + /// - *Content type*: [unc_primitives::epoch_manager::block_info::BlockInfo] + BlockInfo, + /// Mapping from ChunkHash to ShardChunk. + /// - *Rows*: ChunkHash (CryptoHash) + /// - *Content type*: [unc_primitives::sharding::ShardChunk] + Chunks, + /// Storage for PartialEncodedChunk. + /// - *Rows*: ChunkHash (CryptoHash) + /// - *Content type*: [unc_primitives::sharding::PartialEncodedChunk] + PartialChunks, + /// Blocks for which chunks need to be applied after the state is downloaded for a particular epoch + /// - *Rows*: BlockHash (CryptoHash) + /// - *Content type*: Vec of BlockHash (CryptoHash) + BlocksToCatchup, + /// Blocks for which the state is being downloaded + /// - *Rows*: First block of the epoch (CryptoHash) + /// - *Content type*: StateSyncInfo + StateDlInfos, + /// Blocks that were ever challenged. + /// - *Rows*: BlockHash (CryptoHash) + /// - *Content type*: 'true' (bool) + ChallengedBlocks, + /// Contains all the Shard State Headers. + /// - *Rows*: StateHeaderKey (ShardId || BlockHash) + /// - *Content type*: ShardStateSyncResponseHeader + StateHeaders, + /// Contains all the invalid chunks (that we had trouble decoding or verifying). + /// - *Rows*: ShardChunkHeader object + /// - *Content type*: EncodedShardChunk + InvalidChunks, + /// Contains 'BlockExtra' information that is computed after block was processed. + /// Currently it stores only challenges results. + /// - *Rows*: BlockHash (CryptoHash) + /// - *Content type*: BlockExtra + BlockExtra, + /// Store hash of all block per each height, to detect double signs. + /// In most cases, it is better to get the value from BlockHeight column instead (which + /// keeps the hash of the block from canonical chain) + /// - *Rows*: int (height of the block) + /// - *Content type*: Map: EpochId -> Set of BlockHash(CryptoHash) + BlockPerHeight, + /// Contains State parts that we've received. + /// - *Rows*: StatePartKey (BlockHash || ShardId || PartId (u64)) + /// - *Content type*: state part (bytes) + StateParts, + /// Contains mapping from epoch_id to epoch start (first block height of the epoch) + /// - *Rows*: EpochId (CryptoHash) -- TODO: where does the epoch_id come from? it looks like blockHash.. + /// - *Content type*: BlockHeight (int) + EpochStart, + /// Map account_id to announce_account (which peer has announced which account in the current epoch). // TODO: explain account announcement + /// - *Rows*: AccountId (str) + /// - *Content type*: AnnounceAccount + AccountAnnouncements, + /// Next block hashes in the sequence of the canonical chain blocks. + /// - *Rows*: BlockHash (CryptoHash) + /// - *Content type*: next block: BlockHash (CryptoHash) + NextBlockHashes, + /// `LightClientBlock`s corresponding to the last final block of each completed epoch. + /// - *Rows*: EpochId (CryptoHash) + /// - *Content type*: LightClientBlockView + EpochLightClientBlocks, + /// Mapping from Receipt id to destination Shard Id, i.e, the shard that this receipt is sent to. + /// - *Rows*: ReceiptId (CryptoHash) + /// - *Content type*: Shard Id || ref_count (u64 || u64) + ReceiptIdToShardId, + // Deprecated. + #[strum(serialize = "NextBlockWithNewChunk")] + _NextBlockWithNewChunk, + // Deprecated. + #[strum(serialize = "LastBlockWithNewChunk")] + _LastBlockWithNewChunk, + /// Network storage: + /// When given edge is removed (or we didn't get any ping from it for a while), we remove it from our 'in memory' + /// view and persist into storage. + /// + /// This is done, so that we prevent the attack, when someone tries to introduce the edge/peer again into the network, + /// but with the 'old' nonce. + /// + /// When we write things to storage, we do it in groups (here they are called 'components') - this naming is a little bit + /// unfortunate, as the peers/edges that we persist don't need to be connected or form any other 'component' (in a graph theory sense). + /// + /// Each such component gets a new identifier (here called 'nonce'). + /// + /// We store this info in the three columns below: + /// - LastComponentNonce: keeps info on what is the next identifier (nonce) that can be used. + /// - PeerComponent: keep information on mapping from the peer to the last component that it belonged to (so that if a new peer shows + /// up we know which 'component' to load) + /// - ComponentEdges: keep the info about the edges that were connecting these peers that were removed. + + /// Map each saved peer on disk with its component id (a.k.a. nonce). + /// - *Rows*: peer_id + /// - *Column type*: (nonce) u64 + PeerComponent, + /// Map component id (a.k.a. nonce) with all edges in this component. + /// These are all the edges that were purged and persisted to disk at the same time. + /// - *Rows*: nonce + /// - *Column type*: `Vec` + ComponentEdges, + /// Biggest component id (a.k.a nonce) used. + /// - *Rows*: single row (empty row name) + /// - *Column type*: (nonce) u64 + LastComponentNonce, + /// Map of transactions + /// - *Rows*: transaction hash + /// - *Column type*: SignedTransaction + Transactions, + /// Deprecated. + #[strum(serialize = "ChunkPerHeightShard")] + _ChunkPerHeightShard, + /// Changes to state (Trie) that we have recorded. + /// - *Rows*: BlockHash || TrieKey (TrieKey is written via custom to_vec) + /// - *Column type*: TrieKey, new value and reason for change (RawStateChangesWithTrieKey) + StateChanges, + /// Mapping from Block to its refcount (number of blocks that use this block as a parent). (Refcounts are used in handling chain forks). + /// In following example: + /// 1 -> 2 -> 3 -> 5 + /// \ --> 4 + /// The block '2' will have a refcount equal to 2. + /// + /// - *Rows*: BlockHash (CryptoHash) + /// - *Column type*: refcount (u64) + BlockRefCount, + /// Changes to Trie that we recorded during given block/shard processing. + /// - *Rows*: BlockHash || ShardId + /// - *Column type*: old root, new root, list of insertions, list of deletions (TrieChanges) + TrieChanges, + /// Mapping from a block hash to a merkle tree of block hashes that are in the chain before it. + /// - *Rows*: BlockHash + /// - *Column type*: PartialMerkleTree - MerklePath to the leaf + number of leaves in the whole tree. + BlockMerkleTree, + /// Mapping from height to the set of Chunk Hashes that were included in the block at that height. + /// - *Rows*: height (u64) + /// - *Column type*: Vec + ChunkHashesByHeight, + /// Mapping from block ordinal number (number of the block in the chain) to the BlockHash. + /// Note: that it can be different than BlockHeight - if we have skipped some heights when creating the blocks. + /// for example in chain 1->3, the second block has height 3, but ordinal 2. + /// - *Rows*: ordinal (u64) + /// - *Column type*: BlockHash (CryptoHash) + BlockOrdinal, + /// Deprecated. + #[strum(serialize = "GCCount")] + _GCCount, + /// All Outcome ids by block hash and shard id. For each shard it is ordered by execution order. + /// - *Rows*: BlockShardId (BlockHash || ShardId) - 40 bytes + /// - *Column type*: Vec + OutcomeIds, + /// Deprecated + #[strum(serialize = "TransactionRefCount")] + _TransactionRefCount, + /// Heights of blocks that have been processed. + /// - *Rows*: height (u64) + /// - *Column type*: empty + ProcessedBlockHeights, + /// Mapping from receipt hash to Receipt. Note that this doesn't store _all_ + /// receipts. Some receipts are ephemeral and get processed after creation + /// without getting into the database at all. + /// - *Rows*: receipt (CryptoHash) + /// - *Column type*: Receipt + Receipts, + /// Precompiled machine code of the contract, used by StoreCompiledContractCache. + /// - *Rows*: ContractCacheKey or code hash (not sure) + /// - *Column type*: unc-vm-runner CacheRecord + CachedContractCode, + /// Epoch validator information used for rpc purposes. + /// - *Rows*: epoch id (CryptoHash) + /// - *Column type*: EpochSummary + EpochValidatorInfo, + /// Header Hashes indexed by Height. + /// - *Rows*: height (u64) + /// - *Column type*: Vec + HeaderHashesByHeight, + /// State changes made by a chunk, used for resharding. Historically + /// resharding was also called State Splitting since the name. + /// TODO(resharding) rename to StateChangesForResharding if safe. + /// - *Rows*: BlockShardId (BlockHash || ShardId) - 40 bytes + /// - *Column type*: StateChangesForResharding + StateChangesForSplitStates, + /// Transaction or receipt outcome, by outcome ID (transaction or receipt hash) and block + /// hash. Multiple outcomes may be stored for the same outcome ID in case of forks. + /// *Rows*: OutcomeId (CryptoHash) || BlockHash (CryptoHash) + /// *Column type*: ExecutionOutcomeWithProof + TransactionResultForBlock, + /// Flat state contents. Used to get `ValueRef` by trie key faster than doing a trie lookup. + /// - *Rows*: `shard_uid` + trie key (Vec) + /// - *Column type*: FlatStateValue + FlatState, + /// Changes for flat state delta. Stores how flat state should be updated for the given shard and block. + /// - *Rows*: `KeyForFlatStateDelta { shard_uid, block_hash }` + /// - *Column type*: `FlatStateChanges` + FlatStateChanges, + /// Metadata for flat state delta. + /// - *Rows*: `KeyForFlatStateDelta { shard_uid, block_hash }` + /// - *Column type*: `FlatStateDeltaMetadata` + FlatStateDeltaMetadata, + /// Flat storage status for the corresponding shard. + /// - *Rows*: `shard_uid` + /// - *Column type*: `FlatStorageStatus` + FlatStorageStatus, + /// Column to persist pieces of miscellaneous small data. Should only be used to store + /// constant or small (for example per-shard) amount of data. + /// - *Rows*: arbitrary string, see `crate::db::FLAT_STATE_VALUES_INLINING_MIGRATION_STATUS_KEY` for example + /// - *Column type*: arbitrary bytes + Misc, + /// Column to store data for Epoch Sync. + /// Does not contain data for genesis epoch. + /// - *Rows*: `epoch_id` + /// - *Column type*: `EpochSyncInfo + #[cfg(feature = "new_epoch_sync")] + EpochSyncInfo, + /// Bad Validators AccountIds that not produce block in time. + /// - *Rows*: block height (int) + /// - *Column type*: Vec + BadValidator, +} + +/// Defines different logical parts of a db key. +/// To access a column you can use a concatenation of several key types. +/// This is needed to define DBCol::key_type. +/// Update this enum and DBCol::key_type accordingly when creating a new column. +/// Currently only used in cold storage continuous migration. +#[derive(PartialEq, Copy, Clone, Debug, Hash, Eq, strum::EnumIter)] +pub enum DBKeyType { + /// Empty row name. Used in DBCol::LastComponentNonce and DBCol::RecentOutboundConnections + Empty, + /// Set of predetermined strings. Used, for example, in DBCol::BlockMisc + StringLiteral, + BlockHash, + /// Hash of the previous block. Logically different from BlockHash. Used fro DBCol::NextBlockHashes. + PreviousBlockHash, + BlockHeight, + BlockOrdinal, + ShardId, + ShardUId, + ChunkHash, + EpochId, + Nonce, + PeerId, + AccountId, + TrieNodeOrValueHash, + TrieKey, + ReceiptHash, + TransactionHash, + OutcomeId, + ContractCacheKey, + PartId, + ColumnId, +} + +impl DBCol { + /// Whether data in this column is effectively immutable. + /// + /// Data in such columns is never overwriten, though it can be deleted by gc + /// eventually. Specifically, for a given key: + /// + /// * It's OK to insert a new value. + /// * It's also OK to insert a value which is equal to the one already + /// stored. + /// * Inserting a different value would crash the node in debug, but not in + /// release. + /// * GC (and only GC) is allowed to remove any value. + /// + /// In some sense, insert-only column acts as an rc-column, where rc is + /// always one. + pub const fn is_insert_only(&self) -> bool { + match self { + DBCol::Block + | DBCol::BlockHeader + | DBCol::BlockExtra + | DBCol::BlockInfo + | DBCol::Chunks + | DBCol::InvalidChunks + | DBCol::PartialChunks + | DBCol::TransactionResultForBlock => true, + _ => false, + } + } + + /// Whether this column is reference-counted. + /// + /// A reference-counted column is one where we store additional 8-byte value + /// at the end of the payload with the current reference counter value. For + /// such columns you must not use `set`, `set_ser` or `delete` operations, + /// but 'increment_refcount' and `decrement_refcount` instead. + /// + /// Under the hood, we’re using custom merge operator (see + /// [`crate::db::RocksDB::refcount_merge`]) to properly ‘join’ the + /// refcounted cells. This means that the 'value' for a given key must + /// never change. + /// + /// Example: + /// + /// ```ignore + /// increment_refcount("foo", "bar"); + /// // good - after this call, the RC will be equal to 3. + /// increment_refcount_by("foo", "bar", 2); + /// // bad - the value is still 'bar'. + /// increment_refcount("foo", "baz"); + /// // ok - the value will be removed now. (as rc == 0) + /// decrement_refcount_by("foo", "", 3) + /// ``` + /// + /// Quick note on negative refcounts: if we have a key that ends up having + /// a negative refcount, we have to store this value (negative ref) in the + /// database. + /// + /// Example: + /// + /// ```ignore + /// increment_refcount("a", "b"); + /// decrement_refcount_by("a", 3); + /// // Now we have the entry in the database with key "a", empty payload and + /// // refcount value of -2, + /// ``` + pub const fn is_rc(&self) -> bool { + match self { + DBCol::State | DBCol::Transactions | DBCol::Receipts | DBCol::ReceiptIdToShardId => { + true + } + _ => false, + } + } + + /// Whether this column should be copied to the cold storage. + /// + /// This doesn't include DbVersion and BlockMisc columns which are present + /// in the cold database but rather than being copied from hot database are + /// maintained separately. + pub const fn is_cold(&self) -> bool { + // Explicitly list all columns so that if new one is added it'll need to + // be added here as well. + match self { + // DBVersion and BlockMisc are maintained separately in the cold + // storage, they should not be copied from hot. + DBCol::DbVersion | DBCol::BlockMisc => false, + // Most of the GC-ed columns should be copied to the cold storage. + DBCol::Block + | DBCol::BlockExtra + | DBCol::BlockInfo + // TODO can be reconstruction from BlockHeight instead of saving to cold storage. + | DBCol::BlockPerHeight + | DBCol::ChunkExtra + // TODO can be changed to reconstruction from Block instead of saving to cold storage. + | DBCol::ChunkHashesByHeight + | DBCol::Chunks + | DBCol::IncomingReceipts + | DBCol::NextBlockHashes + | DBCol::OutcomeIds + | DBCol::OutgoingReceipts + // TODO can be changed to reconstruction on request instead of saving in cold storage. + | DBCol::PartialChunks + | DBCol::ReceiptIdToShardId + | DBCol::Receipts + | DBCol::State + | DBCol::StateChanges + // TODO StateChangesForSplitStates is not GC-ed, why is it here? + | DBCol::StateChangesForSplitStates + | DBCol::StateHeaders + | DBCol::TransactionResultForBlock + | DBCol::Transactions => true, + + // TODO + DBCol::ChallengedBlocks => false, + DBCol::Misc => false, + // BlockToCatchup is only needed while syncing and it is not immutable. + DBCol::BlocksToCatchup => false, + // BlockRefCount is only needed when handling forks and it is not immutable. + DBCol::BlockRefCount => false, + // InvalidChunks is only needed at head when accepting new chunks. + DBCol::InvalidChunks => false, + // StateParts is only needed while syncing. + DBCol::StateParts => false, + // TrieChanges is only needed for GC. + DBCol::TrieChanges => false, + // StateDlInfos is only needed when syncing and it is not immutable. + DBCol::StateDlInfos => false, + // TODO + DBCol::ProcessedBlockHeights => false, + // HeaderHashesByHeight is only needed for GC. + DBCol::HeaderHashesByHeight => false, + // + DBCol::BadValidator => false, + + // Columns that are not GC-ed need not be copied to the cold storage. + DBCol::BlockHeader + | DBCol::_GCCount + | DBCol::BlockHeight + | DBCol::_Peers + | DBCol::RecentOutboundConnections + | DBCol::BlockMerkleTree + | DBCol::AccountAnnouncements + | DBCol::EpochLightClientBlocks + | DBCol::PeerComponent + | DBCol::LastComponentNonce + | DBCol::ComponentEdges + | DBCol::EpochInfo + | DBCol::EpochStart + | DBCol::EpochValidatorInfo + | DBCol::BlockOrdinal + | DBCol::_ChunkPerHeightShard + | DBCol::_NextBlockWithNewChunk + | DBCol::_LastBlockWithNewChunk + | DBCol::_TransactionRefCount + | DBCol::_TransactionResult + // | DBCol::StateChangesForSplitStates + | DBCol::CachedContractCode + | DBCol::FlatState + | DBCol::FlatStateChanges + | DBCol::FlatStateDeltaMetadata + | DBCol::FlatStorageStatus => false, + #[cfg(feature = "new_epoch_sync")] + DBCol::EpochSyncInfo => false + } + } + + /// Whether this column exists in cold storage. + pub(crate) const fn is_in_colddb(&self) -> bool { + matches!(*self, DBCol::DbVersion | DBCol::BlockMisc) || self.is_cold() + } + + /// Vector of DBKeyType s concatenation of which results in key for the column. + pub fn key_type(&self) -> &'static [DBKeyType] { + match self { + DBCol::DbVersion => &[DBKeyType::StringLiteral], + DBCol::BlockMisc => &[DBKeyType::StringLiteral], + DBCol::Misc => &[DBKeyType::StringLiteral], + DBCol::Block => &[DBKeyType::BlockHash], + DBCol::BlockHeader => &[DBKeyType::BlockHash], + DBCol::BlockHeight => &[DBKeyType::BlockHeight], + DBCol::State => &[DBKeyType::ShardUId, DBKeyType::TrieNodeOrValueHash], + DBCol::ChunkExtra => &[DBKeyType::BlockHash, DBKeyType::ShardUId], + DBCol::_TransactionResult => &[DBKeyType::OutcomeId], + DBCol::OutgoingReceipts => &[DBKeyType::BlockHash, DBKeyType::ShardId], + DBCol::IncomingReceipts => &[DBKeyType::BlockHash, DBKeyType::ShardId], + DBCol::_Peers => &[DBKeyType::PeerId], + DBCol::RecentOutboundConnections => &[DBKeyType::Empty], + DBCol::EpochInfo => &[DBKeyType::EpochId], + DBCol::BlockInfo => &[DBKeyType::BlockHash], + DBCol::Chunks => &[DBKeyType::ChunkHash], + DBCol::PartialChunks => &[DBKeyType::ChunkHash], + DBCol::BlocksToCatchup => &[DBKeyType::BlockHash], + DBCol::StateDlInfos => &[DBKeyType::BlockHash], + DBCol::ChallengedBlocks => &[DBKeyType::BlockHash], + DBCol::StateHeaders => &[DBKeyType::ShardId, DBKeyType::BlockHash], + DBCol::InvalidChunks => &[DBKeyType::ChunkHash], + DBCol::BlockExtra => &[DBKeyType::BlockHash], + DBCol::BlockPerHeight => &[DBKeyType::BlockHeight], + DBCol::StateParts => &[DBKeyType::BlockHash, DBKeyType::ShardId, DBKeyType::PartId], + DBCol::EpochStart => &[DBKeyType::EpochId], + DBCol::AccountAnnouncements => &[DBKeyType::AccountId], + DBCol::NextBlockHashes => &[DBKeyType::PreviousBlockHash], + DBCol::EpochLightClientBlocks => &[DBKeyType::EpochId], + DBCol::ReceiptIdToShardId => &[DBKeyType::ReceiptHash], + DBCol::_NextBlockWithNewChunk => &[DBKeyType::BlockHash, DBKeyType::ShardId], + DBCol::_LastBlockWithNewChunk => &[DBKeyType::ShardId], + DBCol::PeerComponent => &[DBKeyType::PeerId], + DBCol::ComponentEdges => &[DBKeyType::Nonce], + DBCol::LastComponentNonce => &[DBKeyType::Empty], + DBCol::Transactions => &[DBKeyType::TransactionHash], + DBCol::_ChunkPerHeightShard => &[DBKeyType::BlockHeight, DBKeyType::ShardId], + DBCol::StateChanges => &[DBKeyType::BlockHash, DBKeyType::TrieKey], + DBCol::BlockRefCount => &[DBKeyType::BlockHash], + DBCol::TrieChanges => &[DBKeyType::BlockHash, DBKeyType::ShardUId], + DBCol::BlockMerkleTree => &[DBKeyType::BlockHash], + DBCol::ChunkHashesByHeight => &[DBKeyType::BlockHeight], + DBCol::BlockOrdinal => &[DBKeyType::BlockOrdinal], + DBCol::_GCCount => &[DBKeyType::ColumnId], + DBCol::OutcomeIds => &[DBKeyType::BlockHash, DBKeyType::ShardId], + DBCol::_TransactionRefCount => &[DBKeyType::TransactionHash], + DBCol::ProcessedBlockHeights => &[DBKeyType::BlockHeight], + DBCol::Receipts => &[DBKeyType::ReceiptHash], + DBCol::CachedContractCode => &[DBKeyType::ContractCacheKey], + DBCol::EpochValidatorInfo => &[DBKeyType::EpochId], + DBCol::HeaderHashesByHeight => &[DBKeyType::BlockHeight], + DBCol::StateChangesForSplitStates => &[DBKeyType::BlockHash, DBKeyType::ShardId], + DBCol::TransactionResultForBlock => &[DBKeyType::OutcomeId, DBKeyType::BlockHash], + DBCol::FlatState => &[DBKeyType::ShardUId, DBKeyType::TrieKey], + DBCol::FlatStateChanges => &[DBKeyType::ShardUId, DBKeyType::BlockHash], + DBCol::FlatStateDeltaMetadata => &[DBKeyType::ShardUId, DBKeyType::BlockHash], + DBCol::FlatStorageStatus => &[DBKeyType::ShardUId], + #[cfg(feature = "new_epoch_sync")] + DBCol::EpochSyncInfo => &[DBKeyType::EpochId], + DBCol::BadValidator => &[DBKeyType::BlockHeight], + } + } +} + +impl fmt::Display for DBCol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use strum::IntoEnumIterator; + + #[test] + fn column_props_sanity() { + for col in DBCol::iter() { + // Check that rc and write_once are mutually exclusive. + assert!((col.is_rc() as u32) + (col.is_insert_only() as u32) <= 1, "{col}") + } + } + + // In split storage archival nodes the State column and the + // TrieNodeOrValueHash db key type and handled separately. + // This implementation asserts that the TrieNodeOrValueHash key type is + // only use in the State column and in no other columns. + #[test] + fn key_type_split_storage_sanity() { + for col in DBCol::iter() { + if col == DBCol::State { + continue; + } + let key_types = col.key_type(); + for key_type in key_types { + assert_ne!(key_type, &DBKeyType::TrieNodeOrValueHash); + } + } + } +} diff --git a/core/store/src/config.rs b/core/store/src/config.rs new file mode 100644 index 000000000..2f4b58ce1 --- /dev/null +++ b/core/store/src/config.rs @@ -0,0 +1,370 @@ +use crate::trie::{ + DEFAULT_SHARD_CACHE_DELETIONS_QUEUE_CAPACITY, DEFAULT_SHARD_CACHE_TOTAL_SIZE_LIMIT, +}; +use crate::DBCol; +use unc_primitives::shard_layout::ShardUId; +use std::time::Duration; +use std::{collections::HashMap, iter::FromIterator}; + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[serde(default)] +pub struct StoreConfig { + /// Path to the database. If relative, resolved relative to uncd home + /// directory. This is useful if node runs with a separate disk holding the + /// database. + pub path: Option, + + /// Collect internal storage layer statistics. + /// Minor performance impact is expected. + pub enable_statistics: bool, + + /// Re-export storage layer statistics as prometheus metrics. + pub enable_statistics_export: bool, + + /// Maximum number of store files being opened simultaneously. + /// Default value: 512. + /// The underlying storage can require simultaneously opening a large number of files. + /// Increasing this value helps to prevent the storage constantly closing/opening files it + /// needs. + /// Increasing this value up to a value higher than 1024 also requires setting `ulimit -n` in + /// Linux. + pub max_open_files: u32, + + /// Cache size for DBCol::State column. + /// Increasing DBCol::State cache size helps making storage more efficient. On the other hand we + /// don't want to increase hugely requirements for running a node so currently we use a small + /// default value for it. + pub col_state_cache_size: bytesize::ByteSize, + + /// Cache size for DBCol::FlatState column. + pub col_flat_state_cache_size: bytesize::ByteSize, + + /// Block size used internally in RocksDB. + /// Default value: 16KiB. + /// We're still experimenting with this parameter and it seems decreasing its value can improve + /// the performance of the storage + pub block_size: bytesize::ByteSize, + + /// Trie cache configuration per shard for normal (non-view) caches. + pub trie_cache: TrieCacheConfig, + /// Trie cache configuration per shard for view caches. + pub view_trie_cache: TrieCacheConfig, + + /// Enable fetching account and access key data ahead of time to avoid IO latency. + pub enable_receipt_prefetching: bool, + + /// Configured accounts will be prefetched as SWEAT token account, if predecessor is listed as receiver. + /// This config option is temporary and will be removed once flat storage is implemented. + pub sweat_prefetch_receivers: Vec, + /// List of allowed predecessor accounts for SWEAT prefetching. + /// This config option is temporary and will be removed once flat storage is implemented. + pub sweat_prefetch_senders: Vec, + + /// List of shard UIDs for which we should load the tries in memory. + /// TODO(#9511): This does not automatically survive resharding. We may need to figure out a + /// strategy for that. + pub load_mem_tries_for_shards: Vec, + /// If true, load mem tries for all shards; this has priority over `load_mem_tries_for_shards`. + pub load_mem_tries_for_all_shards: bool, + + /// Path where to create RocksDB checkpoints during database migrations or + /// `false` to disable that feature. + /// + /// If this feature is enabled, when database migration happens a RocksDB + /// checkpoint will be created just before the migration starts. This way, + /// if there are any failures during migration, the database can be + /// recovered from the checkpoint. + /// + /// The field can be one of: + /// * an absolute path name → the snapshot will be created in specified + /// directory. No sub-directories will be created so for example you + /// probably don’t want `/tmp` but rather `/tmp/uncd-db-snapshot`; + /// * an relative path name → the snapshot will be created in a directory + /// inside of the RocksDB database directory (see `path` field); + /// * `true` (the default) → this is equivalent to setting the field to + /// `migration-snapshot`; and + /// * `false` → the snapshot will not be created. + /// + /// Note that if the snapshot is on a different file system than the + /// database, creating the snapshot may itself take time as data may need to + /// be copied between the databases. + #[serde(skip_serializing_if = "MigrationSnapshot::is_default")] + pub migration_snapshot: MigrationSnapshot, + + /// Number of threads to execute storage background migrations. + /// Needed to create flat storage which need to happen in parallel + /// with block processing. + /// TODO (#8826): remove, because creation successfully happened in 1.34. + pub background_migration_threads: usize, + + /// Enables background flat storage creation. + /// TODO (#8826): remove, because creation successfully happened in 1.34. + pub flat_storage_creation_enabled: bool, + + /// Duration to perform background flat storage creation step. Defines how + /// frequently we check creation status and execute work related to it in + /// main thread (scheduling and collecting state parts, catching up blocks, etc.). + /// TODO (#8826): remove, because creation successfully happened in 1.34. + pub flat_storage_creation_period: Duration, + + /// State Snapshot configuration + pub state_snapshot_config: StateSnapshotConfig, + + // TODO (#9989): To be phased out in favor of state_snapshot_config + pub state_snapshot_enabled: bool, + + // TODO (#9989): To be phased out in favor of state_snapshot_config + pub state_snapshot_compaction_enabled: bool, +} + +/// Config used to control state snapshot creation. This is used for state sync and resharding. +#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)] +#[serde(default)] +pub struct StateSnapshotConfig { + pub state_snapshot_type: StateSnapshotType, + /// State Snapshot compaction usually is a good thing but is heavy on IO and can take considerable + /// amount of time. + /// It makes state snapshots tiny (10GB) over the course of an epoch. + /// We may want to disable it for archival nodes during resharding + pub compaction_enabled: bool, +} + +#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)] +pub enum StateSnapshotType { + /// Consider this as the default "disabled" option. We need to have snapshotting enabled for resharding + /// State snapshots involve filesystem operations and costly IO operations. + #[default] + ForReshardingOnly, + /// This is the "enabled" option where we create a snapshot at the beginning of every epoch. + /// Needed if a node wants to be able to respond to state part requests. + EveryEpoch, +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum MigrationSnapshot { + Enabled(bool), + Path(std::path::PathBuf), +} + +/// Mode in which to open the storage. +#[derive(Clone, Copy)] +pub enum Mode { + /// Open an existing database in read-only mode. Fail if it doesn’t exist. + ReadOnly, + /// Open an existing database in read-write mode. Fail if it doesn’t exist. + ReadWriteExisting, + /// Open a database in read-write mode. create if it doesn’t exist. + ReadWrite, + /// Creates a new database in read-write mode. Fails if it exists. + Create, +} + +impl Mode { + pub const fn read_only(self) -> bool { + matches!(self, Mode::ReadOnly) + } + pub const fn read_write(self) -> bool { + !self.read_only() + } + pub const fn can_create(self) -> bool { + matches!(self, Mode::ReadWrite | Mode::Create) + } + pub const fn must_create(self) -> bool { + matches!(self, Mode::Create) + } + + /// Returns variant of the mode which prohibits creation of the database or + /// `None` if the mode requires creation of a new database. + pub const fn but_cannot_create(self) -> Option { + match self { + Self::ReadOnly | Self::ReadWriteExisting => Some(self), + Self::ReadWrite => Some(Self::ReadWriteExisting), + Self::Create => None, + } + } +} + +impl StoreConfig { + /// Returns configuration meant for tests. + /// + /// Since tests often operate with less data than real node, the test + /// configuration is adjusted to reduce resource use. For example, default + /// `max_open_files` limit is 512 which helps in situations when tests are + /// run in isolated environments with tighter resource limits. + pub fn test_config() -> Self { + Self { max_open_files: 512, ..Self::default() } + } + + /// Returns cache size for given column. + pub const fn col_cache_size(&self, col: DBCol) -> bytesize::ByteSize { + match col { + DBCol::State => self.col_state_cache_size, + DBCol::FlatState => self.col_flat_state_cache_size, + _ => bytesize::ByteSize::mib(32), + } + } +} + +impl Default for StoreConfig { + fn default() -> Self { + Self { + path: None, + enable_statistics: false, + enable_statistics_export: true, + + // We used to use value of 512 but we were hitting that limit often + // and store had to constantly close and reopen the same set of + // files. Running state viewer on a dense set of 500 blocks did + // almost 200k file opens (having less than 7K unique files opened, + // some files were opened 400+ times). Using 10k limit for + // max_open_files led to performance improvement of ~11%. + max_open_files: 10_000, + + // We used to have the same cache size for all columns, 32 MiB. + // When some RocksDB inefficiencies were found [`DBCol::State`] + // cache size was increased up to 512 MiB. This was done on 13th of + // Nov 2021 and we consider increasing the value. Tests have shown + // that increase to 25 GiB (we've used this big value to estimate + // performance improvement headroom) having `max_open_files` at 10k + // improved performance of state viewer by 60%. + col_state_cache_size: bytesize::ByteSize::mib(512), + + // This value was tuned in after we removed filter and index block from block cache + // and slightly improved read speed for FlatState and reduced memory footprint in + // #9389. + col_flat_state_cache_size: bytesize::ByteSize::mib(128), + + // This value was taken from the Openethereum default parameter and + // we use it since then. + block_size: bytesize::ByteSize::kib(16), + + trie_cache: TrieCacheConfig { + default_max_bytes: 500_000_000, + // TODO(resharding) The cache size needs to adjusted for every resharding. + // Make that automatic e.g. by defining the minimum cache size per account rather than shard. + per_shard_max_bytes: HashMap::from_iter([ + // Temporary solution to make contracts with heavy trie access + // patterns on shard 3 more stable. It was chosen by the estimation + // of the largest contract storage size we are aware as of 23/08/2022. + // Note: on >= 1.34 framework version use 1_000_000_000 if you have + // minimal hardware. + // In simple nightshade the heavy contract "token.sweat" is in shard 3 + (ShardUId { version: 1, shard_id: 3 }, 3_000_000_000), + // In simple nightshade v2 the heavy contract "token.sweat" is in shard 4 + (ShardUId { version: 2, shard_id: 4 }, 3_000_000_000), + // Shard 1 is dedicated to aurora and it had very few cache + // misses even with cache size of only 50MB + (ShardUId { version: 1, shard_id: 1 }, 50_000_000), + (ShardUId { version: 2, shard_id: 1 }, 50_000_000), + ]), + shard_cache_deletions_queue_capacity: DEFAULT_SHARD_CACHE_DELETIONS_QUEUE_CAPACITY, + }, + + // Use default sized caches for view calls, because they don't impact + // block processing. + view_trie_cache: TrieCacheConfig::default(), + + enable_receipt_prefetching: true, + sweat_prefetch_receivers: vec![ + "token.sweat".to_owned(), + "vfinal.token.sweat.testnet".to_owned(), + ], + sweat_prefetch_senders: vec![ + "oracle.sweat".to_owned(), + "sweat_the_oracle.testnet".to_owned(), + ], + + // TODO(#9511): Consider adding here shard id 3 or all shards after + // this feature will be tested. Until that, use at your own risk. + // Doesn't work for resharding. + // It will speed up processing of shards where it is enabled, but + // requires more RAM and takes several minutes on startup. + load_mem_tries_for_shards: Default::default(), + load_mem_tries_for_all_shards: false, + + migration_snapshot: Default::default(), + + // We checked that this number of threads doesn't impact + // regular block processing significantly. + background_migration_threads: 8, + + flat_storage_creation_enabled: true, + + // It shouldn't be very low, because on single flat storage creation step + // we do several disk reads from `FlatStateMisc` and `FlatStateDeltas`. + // One second should be enough to save deltas on start and catch up + // flat storage head quickly. State read work is much more expensive. + flat_storage_creation_period: Duration::from_secs(1), + + state_snapshot_config: Default::default(), + + // TODO: To be phased out in favor of state_snapshot_config + state_snapshot_enabled: false, + + // TODO: To be phased out in favor of state_snapshot_config + state_snapshot_compaction_enabled: false, + } + } +} + +impl MigrationSnapshot { + /// Returns path to the snapshot given path to the database. + /// + /// Returns `None` if migration snapshot is disabled. Relative paths are + /// resolved relative to `db_path`. + pub fn get_path<'a>(&'a self, db_path: &std::path::Path) -> Option { + let path = match &self { + Self::Enabled(false) => return None, + Self::Enabled(true) => std::path::Path::new("migration-snapshot"), + Self::Path(path) => path.as_path(), + }; + Some(db_path.join(path)) + } + + /// Checks whether the object equals its default value. + fn is_default(&self) -> bool { + matches!(self, Self::Enabled(true)) + } + + /// Formats an example of how to edit `config.json` to set migration path to + /// given value. + pub fn format_example(&self) -> String { + let value = serde_json::to_string(self).unwrap(); + format!( + " {{\n \"store\": {{\n \"migration_snapshot\": \ + {value}\n }}\n }}" + ) + } +} + +impl Default for MigrationSnapshot { + fn default() -> Self { + Self::Enabled(true) + } +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[serde(default)] +pub struct TrieCacheConfig { + /// Limit the memory consumption of the trie cache per shard. + /// + /// This is an approximate limit that attempts to factor in data structure + /// overhead also. It is supposed to be fairly accurate in the limit. + pub default_max_bytes: u64, + /// Overwrites `default_max_bytes` for specific shards. + pub per_shard_max_bytes: HashMap, + /// Limit the number of elements in caches deletions queue for specific + /// shard + pub shard_cache_deletions_queue_capacity: usize, +} + +impl Default for TrieCacheConfig { + fn default() -> Self { + Self { + default_max_bytes: DEFAULT_SHARD_CACHE_TOTAL_SIZE_LIMIT, + per_shard_max_bytes: Default::default(), + shard_cache_deletions_queue_capacity: DEFAULT_SHARD_CACHE_DELETIONS_QUEUE_CAPACITY, + } + } +} diff --git a/core/store/src/db.rs b/core/store/src/db.rs new file mode 100644 index 000000000..90eb033cb --- /dev/null +++ b/core/store/src/db.rs @@ -0,0 +1,257 @@ +use crate::DBCol; +use unc_fmt::{AbbrBytes, StorageKey}; +use std::io; + +pub(crate) mod rocksdb; + +mod colddb; +mod splitdb; + +pub mod refcount; +mod slice; +mod testdb; + +mod database_tests; + +pub use self::colddb::ColdDB; +pub use self::rocksdb::RocksDB; +pub use self::splitdb::SplitDB; + +pub use self::slice::DBSlice; +pub use self::testdb::TestDB; + +// `DBCol::BlockMisc` keys +pub const HEAD_KEY: &[u8; 4] = b"HEAD"; +pub const TAIL_KEY: &[u8; 4] = b"TAIL"; +pub const CHUNK_TAIL_KEY: &[u8; 10] = b"CHUNK_TAIL"; +pub const FORK_TAIL_KEY: &[u8; 9] = b"FORK_TAIL"; +pub const HEADER_HEAD_KEY: &[u8; 11] = b"HEADER_HEAD"; +pub const FINAL_HEAD_KEY: &[u8; 10] = b"FINAL_HEAD"; +pub const LATEST_KNOWN_KEY: &[u8; 12] = b"LATEST_KNOWN"; +pub const LARGEST_TARGET_HEIGHT_KEY: &[u8; 21] = b"LARGEST_TARGET_HEIGHT"; +pub const GENESIS_JSON_HASH_KEY: &[u8; 17] = b"GENESIS_JSON_HASH"; +pub const GENESIS_STATE_ROOTS_KEY: &[u8; 19] = b"GENESIS_STATE_ROOTS"; +pub const COLD_HEAD_KEY: &[u8; 9] = b"COLD_HEAD"; +pub const STATE_SYNC_DUMP_KEY: &[u8; 15] = b"STATE_SYNC_DUMP"; +pub const STATE_SNAPSHOT_KEY: &[u8; 18] = b"STATE_SNAPSHOT_KEY"; + +// `DBCol::Misc` keys +pub const FLAT_STATE_VALUES_INLINING_MIGRATION_STATUS_KEY: &[u8] = + b"FLAT_STATE_VALUES_INLINING_MIGRATION_STATUS"; + +#[derive(Default, Debug)] +pub struct DBTransaction { + pub(crate) ops: Vec, +} + +pub(crate) enum DBOp { + /// Sets `key` to `value`, without doing any checks. + Set { col: DBCol, key: Vec, value: Vec }, + /// Sets `key` to `value`, and additionally debug-checks that the value is + /// not overwritten. + Insert { col: DBCol, key: Vec, value: Vec }, + /// Modifies a reference-counted column. `value` includes both the value per + /// se and a refcount at the end. + UpdateRefcount { col: DBCol, key: Vec, value: Vec }, + /// Deletes sepecific `key`. + Delete { col: DBCol, key: Vec }, + /// Deletes all data from a column. + DeleteAll { col: DBCol }, + /// Deletes [`from`, `to`) key range, i.e. including `from` and excluding `to` + DeleteRange { col: DBCol, from: Vec, to: Vec }, +} + +impl DBOp { + pub fn col(&self) -> DBCol { + *match self { + DBOp::Set { col, .. } => col, + DBOp::Insert { col, .. } => col, + DBOp::UpdateRefcount { col, .. } => col, + DBOp::Delete { col, .. } => col, + DBOp::DeleteAll { col } => col, + DBOp::DeleteRange { col, .. } => col, + } + } +} + +impl std::fmt::Debug for DBOp { + // Mostly default implementation generated by vs code but with pretty keys and values. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Set { col, key, value } => f + .debug_struct("Set") + .field("col", col) + .field("key", &StorageKey(key)) + .field("value", &AbbrBytes(value)) + .finish(), + Self::Insert { col, key, value } => f + .debug_struct("Insert") + .field("col", col) + .field("key", &StorageKey(key)) + .field("value", &AbbrBytes(value)) + .finish(), + Self::UpdateRefcount { col, key, value } => f + .debug_struct("UpdateRefcount") + .field("col", col) + .field("key", &StorageKey(key)) + .field("value", &AbbrBytes(value)) + .finish(), + Self::Delete { col, key } => { + f.debug_struct("Delete").field("col", col).field("key", &StorageKey(key)).finish() + } + Self::DeleteAll { col } => f.debug_struct("DeleteAll").field("col", col).finish(), + Self::DeleteRange { col, from, to } => f + .debug_struct("DeleteRange") + .field("col", col) + .field("from", from) + .field("to", to) + .finish(), + } + } +} + +impl DBTransaction { + pub fn new() -> Self { + Self { ops: Vec::new() } + } + + pub fn set(&mut self, col: DBCol, key: Vec, value: Vec) { + self.ops.push(DBOp::Set { col, key, value }); + } + + pub fn insert(&mut self, col: DBCol, key: Vec, value: Vec) { + assert!(col.is_insert_only(), "can't insert: {col:?}"); + self.ops.push(DBOp::Insert { col, key, value }); + } + + pub fn update_refcount(&mut self, col: DBCol, key: Vec, value: Vec) { + assert!(col.is_rc(), "can't update refcount: {col:?}"); + self.ops.push(DBOp::UpdateRefcount { col, key, value }); + } + + pub fn delete(&mut self, col: DBCol, key: Vec) { + self.ops.push(DBOp::Delete { col, key }); + } + + pub fn delete_all(&mut self, col: DBCol) { + self.ops.push(DBOp::DeleteAll { col }); + } + + pub fn delete_range(&mut self, col: DBCol, from: Vec, to: Vec) { + self.ops.push(DBOp::DeleteRange { col, from, to }); + } + + pub fn merge(&mut self, other: DBTransaction) { + self.ops.extend(other.ops) + } +} + +pub type DBIteratorItem = io::Result<(Box<[u8]>, Box<[u8]>)>; +pub type DBIterator<'a> = Box + 'a>; + +pub trait Database: Sync + Send { + /// Returns raw bytes for given `key` ignoring any reference count decoding + /// if any. + /// + /// Note that when reading reference-counted column, the reference count + /// will not be decoded or stripped from the value. Similarly, cells with + /// non-positive reference count will be returned as existing. + /// + /// You most likely will want to use [`Self::get_with_rc_stripped`] to + /// properly handle reference-counted columns. + fn get_raw_bytes(&self, col: DBCol, key: &[u8]) -> io::Result>>; + + /// Returns value for given `key` forcing a reference count decoding. + /// + /// **Panics** if the column is not reference counted. + fn get_with_rc_stripped(&self, col: DBCol, key: &[u8]) -> io::Result>> { + assert!(col.is_rc()); + Ok(self.get_raw_bytes(col, key)?.and_then(DBSlice::strip_refcount)) + } + + /// Iterate over all items in given column in lexicographical order sorted + /// by the key. + /// + /// When reading reference-counted column, the reference count will be + /// correctly stripped. Furthermore, elements with non-positive reference + /// count will be treated as non-existing (i.e. they’re going to be + /// skipped). For all other columns, the value is returned directly from + /// the database. + fn iter<'a>(&'a self, col: DBCol) -> DBIterator<'a>; + + /// Iterate over items in given column whose keys start with given prefix. + /// + /// This is morally equivalent to [`Self::iter`] with a filter discarding + /// keys which do not start with given `key_prefix` (but faster). The items + /// are returned in lexicographical order sorted by the key. + fn iter_prefix<'a>(&'a self, col: DBCol, key_prefix: &'a [u8]) -> DBIterator<'a>; + + /// Iterate over items in given column whose keys are between [lower_bound, upper_bound) + /// + /// Upper_bound key is not included. + /// If lower_bound is None - the iterator starts from the first key. + /// If upper_bound is None - iterator continues to the last key. + fn iter_range<'a>( + &'a self, + col: DBCol, + lower_bound: Option<&[u8]>, + upper_bound: Option<&[u8]>, + ) -> DBIterator<'a>; + + /// Iterate over items in given column bypassing reference count decoding if + /// any. + /// + /// This is like [`Self::iter`] but it returns raw bytes as stored in the + /// database. For reference-counted columns this means that the reference + /// count will not be decoded or stripped from returned value and elements + /// with non-positive reference count will be included in the iterator. + /// + /// If in doubt, use [`Self::iter`] instead. Unless you’re doing something + /// low-level with the database (e.g. doing a migration), you probably don’t + /// want this method. + fn iter_raw_bytes<'a>(&'a self, col: DBCol) -> DBIterator<'a>; + + /// Atomically apply all operations in given batch at once. + fn write(&self, batch: DBTransaction) -> io::Result<()>; + + /// Flush all in-memory data to disk. + /// + /// This is a no-op for in-memory databases. + fn flush(&self) -> io::Result<()>; + + /// Compact database representation. + /// + /// If the database supports it a form of compaction, calling this function + /// is blocking until compaction finishes. Otherwise, this is a no-op. + fn compact(&self) -> io::Result<()>; + + /// Returns statistics about the database if available. + fn get_store_statistics(&self) -> Option; + + /// Create checkpoint in provided path + fn create_checkpoint(&self, path: &std::path::Path) -> anyhow::Result<()>; +} + +fn assert_no_overwrite(col: DBCol, key: &[u8], value: &[u8], old_value: &[u8]) { + assert!( + value == old_value, + "\ +write once column overwritten +col: {col} +key: {key:?} +" + ) +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum StatsValue { + Count(i64), + Sum(i64), + Percentile(u32, f64), + ColumnValue(DBCol, i64), +} + +#[derive(Debug, Clone, PartialEq)] +pub struct StoreStatistics { + pub data: Vec<(String, Vec)>, +} diff --git a/core/store/src/db/colddb.rs b/core/store/src/db/colddb.rs new file mode 100644 index 000000000..00716e570 --- /dev/null +++ b/core/store/src/db/colddb.rs @@ -0,0 +1,345 @@ +use unc_o11y::{log_assert, log_assert_fail}; + +use crate::db::refcount::set_refcount; +use crate::db::{DBIterator, DBOp, DBSlice, DBTransaction, Database}; +use crate::DBCol; + +/// A database which provides access to the cold storage. +/// +/// The data is stored in the same format as in the regular database but for the +/// reference counted columns the rc is always set to 1. This struct handles +/// setting the rc to one transparently to the user. +/// +/// Lastly, since no data is ever deleted from cold storage, trying to decrease +/// reference of a value count or delete data is ignored and if debug assertions +/// are enabled will cause a panic. +pub struct ColdDB { + cold: std::sync::Arc, +} + +impl ColdDB { + pub fn new(cold: std::sync::Arc) -> Self { + Self { cold } + } + + fn err_msg(col: DBCol) -> String { + format!("Reading from column missing from cold storage. {col:?}") + } + + // Checks if the column is is the cold db and returns an error if not. + fn check_is_in_colddb(col: DBCol) -> std::io::Result<()> { + if !col.is_in_colddb() { + return Err(std::io::Error::other(Self::err_msg(col))); + } + Ok(()) + } + + // Checks if the column is is the cold db and panics if not. + fn log_assert_is_in_colddb(col: DBCol) { + log_assert!(col.is_in_colddb(), "{}", Self::err_msg(col)); + } +} + +impl Database for ColdDB { + /// Returns raw bytes for given `key` ignoring any reference count decoding if any. + fn get_raw_bytes(&self, col: DBCol, key: &[u8]) -> std::io::Result>> { + Self::check_is_in_colddb(col)?; + self.cold.get_raw_bytes(col, key) + } + + /// Returns value for given `key` forcing a reference count decoding. + fn get_with_rc_stripped(&self, col: DBCol, key: &[u8]) -> std::io::Result>> { + Self::check_is_in_colddb(col)?; + self.cold.get_with_rc_stripped(col, key) + } + + /// Iterates over all values in a column. + fn iter<'a>(&'a self, col: DBCol) -> DBIterator<'a> { + Self::log_assert_is_in_colddb(col); + self.cold.iter(col) + } + + /// Iterates over values in a given column whose key has given prefix. + fn iter_prefix<'a>(&'a self, col: DBCol, key_prefix: &'a [u8]) -> DBIterator<'a> { + Self::log_assert_is_in_colddb(col); + self.cold.iter_prefix(col, key_prefix) + } + + /// Iterate over items in given column bypassing reference count decoding if any. + fn iter_raw_bytes<'a>(&'a self, col: DBCol) -> DBIterator<'a> { + Self::log_assert_is_in_colddb(col); + self.cold.iter_raw_bytes(col) + } + + /// Iterate over items in given column whose keys are between [lower_bound, upper_bound) + fn iter_range<'a>( + &'a self, + col: DBCol, + lower_bound: Option<&[u8]>, + upper_bound: Option<&[u8]>, + ) -> DBIterator<'a> { + Self::log_assert_is_in_colddb(col); + self.cold.iter_range(col, lower_bound, upper_bound) + } + + /// Atomically applies operations in given transaction. + /// + /// If debug assertions are enabled, panics if there are any delete + /// operations or operations decreasing reference count of a value. If + /// debug assertions are not enabled, such operations are filtered out. + fn write(&self, mut transaction: DBTransaction) -> std::io::Result<()> { + let mut idx = 0; + while idx < transaction.ops.len() { + if adjust_op(&mut transaction.ops[idx]) { + idx += 1; + } else { + transaction.ops.swap_remove(idx); + } + } + self.cold.write(transaction) + } + + fn compact(&self) -> std::io::Result<()> { + self.cold.compact() + } + + fn flush(&self) -> std::io::Result<()> { + self.cold.flush() + } + + fn get_store_statistics(&self) -> Option { + self.cold.get_store_statistics() + } + + fn create_checkpoint(&self, path: &std::path::Path) -> anyhow::Result<()> { + self.cold.create_checkpoint(path) + } +} + +/// Adjust database operation to be performed on cold storage. +/// +/// Returns whether the operation should be kept or dropped. Generally, dropped +/// columns indicate an unexpected operation which should have never been issued +/// for cold storage. +fn adjust_op(op: &mut DBOp) -> bool { + if !op.col().is_in_colddb() { + return false; + } + + match op { + DBOp::Set { .. } | DBOp::Insert { .. } => true, + DBOp::UpdateRefcount { col, key, value } => { + assert!(col.is_rc()); + // There is no point in keeping track of ref count in the cold store so + // just always hardcode it to 1. + let mut value = core::mem::take(value); + match set_refcount(&mut value, 1) { + Ok(_) => { + *op = DBOp::Set { col: *col, key: core::mem::take(key), value }; + return true; + } + Err(err) => { + log_assert_fail!( + "Failed to set refcount when writing to cold store. Error: {err}" + ); + return false; + } + }; + } + DBOp::Delete { col, key } => { + log_assert_fail!("Unexpected delete from {col} in cold store: {key:?}"); + false + } + DBOp::DeleteAll { col } => { + log_assert_fail!("Unexpected delete from {col} in cold store"); + false + } + DBOp::DeleteRange { col, from, to } => { + log_assert_fail!("Unexpected delete range from {col} in cold store: {from:?} {to:?}"); + false + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + const HEIGHT_LE: &[u8] = &42u64.to_le_bytes(); + const SHARD: &[u8] = "ShardUId".as_bytes(); + const HASH: &[u8] = [0u8; 32].as_slice(); + const VALUE: &[u8] = "FooBar".as_bytes(); + const ONE: &[u8] = &1i64.to_le_bytes(); + + /// Constructs test in-memory database. + fn create_test_cold_db() -> ColdDB { + let cold = crate::db::testdb::TestDB::new(); + ColdDB::new(cold) + } + + fn set(col: DBCol, key: &[u8]) -> DBOp { + DBOp::Set { col, key: key.to_vec(), value: VALUE.to_vec() } + } + + fn set_with_rc(col: DBCol, key: &[u8]) -> DBOp { + let value = [VALUE, ONE].concat(); + DBOp::Set { col, key: key.to_vec(), value: value } + } + + /// Prettifies raw key for display. + fn pretty_key(key: &[u8]) -> String { + fn chunk(chunk: &[u8]) -> String { + let num = u64::from_le_bytes(chunk.try_into().unwrap()); + if num < 256 { + format!("le({num})") + } else if num.swap_bytes() < 256 { + format!("be({})", num.swap_bytes()) + } else if let Ok(value) = std::str::from_utf8(chunk) { + value.to_string() + } else { + format!("{chunk:x?}") + } + } + fn hash(chunk: &[u8]) -> String { + crate::CryptoHash::from(chunk.try_into().unwrap()).to_string() + } + match key.len() { + 8 => chunk(key), + 16 => format!("`{} || {}`", chunk(&key[..8]), chunk(&key[8..])), + 32 => hash(key), + 40 => format!("`{} || {}`", chunk(&key[..8]), hash(&key[8..])), + _ => format!("{key:x?}"), + } + } + + /// Prettifies raw value for display. + fn pretty_value(value: Option<&[u8]>, refcount: bool) -> String { + match value { + None => "∅".to_string(), + Some(value) if refcount => { + let decoded = crate::db::refcount::decode_value_with_rc(value); + format!("{}; rc: {}", pretty_value(decoded.0, false), decoded.1) + } + Some(value) => std::str::from_utf8(value).unwrap().to_string(), + } + } + + /// Tests that keys are correctly adjusted when saved in cold store. + #[test] + fn test_adjust_key() { + let db = create_test_cold_db(); + + // Test on two columns, one with rc and one without. + // State -> rc + // Block -> no rc + + let ops = vec![set_with_rc(DBCol::State, &[SHARD, HASH].concat()), set(DBCol::Block, HASH)]; + db.write(DBTransaction { ops }).unwrap(); + + // Fetch data + let mut result = Vec::::new(); + let mut fetch = |col, key: &[u8], raw_only: bool| { + result.push(format!("{col} {}", pretty_key(key))); + let dbs: [(bool, &dyn Database); 2] = [(false, &db), (true, &*db.cold)]; + for (is_raw, db) in dbs { + if raw_only && !is_raw { + continue; + } + + let name = if is_raw { "raw " } else { "cold" }; + let value = db.get_raw_bytes(col, &key).unwrap(); + // When fetching reference counted column ColdDB adds reference + // count to it. + let value = pretty_value(value.as_deref(), col.is_rc()); + result.push(format!(" [{name}] get_raw_bytes → {value}")); + + if col.is_rc() { + let value = db.get_with_rc_stripped(col, &key).unwrap(); + let value = pretty_value(value.as_deref(), false); + result.push(format!(" [{name}] get_with_rc_stripped → {value}")); + } + } + }; + + fetch(DBCol::State, &[SHARD, HASH].concat(), false); + fetch(DBCol::State, &[HASH].concat(), true); + fetch(DBCol::Block, HASH, false); + + // Check expected value. Use cargo-insta to update the expected value: + // cargo install cargo-insta + // cargo insta test --accept -p unc-store -- db::colddb + insta::assert_display_snapshot!(result.join("\n"), @r###" + State `ShardUId || 11111111111111111111111111111111` + [cold] get_raw_bytes → FooBar; rc: 1 + [cold] get_with_rc_stripped → FooBar + [raw ] get_raw_bytes → FooBar; rc: 1 + [raw ] get_with_rc_stripped → FooBar + State 11111111111111111111111111111111 + [raw ] get_raw_bytes → ∅ + [raw ] get_with_rc_stripped → ∅ + Block 11111111111111111111111111111111 + [cold] get_raw_bytes → FooBar + [raw ] get_raw_bytes → FooBar + "###); + } + + /// Tests that iterator works correctly and adjusts keys when necessary. + #[test] + fn test_iterator() { + let db = create_test_cold_db(); + + // Test on two columns, one with rc and one without. + // State -> rc + // Block -> no rc + + // Populate data + let ops = vec![set_with_rc(DBCol::State, &[SHARD, HASH].concat()), set(DBCol::Block, HASH)]; + db.write(DBTransaction { ops }).unwrap(); + + let mut result = Vec::::new(); + for col in [DBCol::State, DBCol::Block] { + let dbs: [(bool, &dyn Database); 2] = [(false, &db), (true, &*db.cold)]; + result.push(col.to_string()); + for (is_raw, db) in dbs { + let name = if is_raw { "raw " } else { "cold" }; + for item in db.iter(col) { + let (key, value) = item.unwrap(); + let value = pretty_value(Some(value.as_ref()), false); + let key = pretty_key(&key); + result.push(format!("[{name}] ({key}, {value})")); + } + } + } + + // Check expected value. Use cargo-insta to update the expected value: + // cargo install cargo-insta + // cargo insta test --accept -p unc-store -- db::colddb + insta::assert_display_snapshot!(result.join("\n"), @r###" + State + [cold] (`ShardUId || 11111111111111111111111111111111`, FooBar) + [raw ] (`ShardUId || 11111111111111111111111111111111`, FooBar) + Block + [cold] (11111111111111111111111111111111, FooBar) + [raw ] (11111111111111111111111111111111, FooBar) + "###); + } + + #[test] + fn test_refcount() { + let db = create_test_cold_db(); + let col = DBCol::Transactions; + let key = HASH; + + let op = + DBOp::UpdateRefcount { col, key: key.to_vec(), value: [VALUE, HEIGHT_LE].concat() }; + db.write(DBTransaction { ops: vec![op] }).unwrap(); + + // Refcount is set to 1 in the underlying database. + let got = db.cold.get_raw_bytes(col, key).unwrap(); + assert_eq!(Some([VALUE, ONE].concat().as_slice()), got.as_deref()); + + // When fetching raw bytes, refcount of 1 is returned. + let got = db.get_raw_bytes(col, key).unwrap(); + assert_eq!(Some([VALUE, ONE].concat().as_slice()), got.as_deref()); + } +} diff --git a/core/store/src/db/database_tests.rs b/core/store/src/db/database_tests.rs new file mode 100644 index 000000000..f7322a0ea --- /dev/null +++ b/core/store/src/db/database_tests.rs @@ -0,0 +1,45 @@ +/// Set of tests over the 'Database' interface, that we can run over multiple implementations +/// to make sure that they are working correctly. + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use crate::{ + db::{DBTransaction, Database, TestDB}, + DBCol, NodeStorage, + }; + + // Returns test & rocksDB databases. + fn test_and_rocksdb() -> Vec> { + let (_tmp_dir, opener) = NodeStorage::test_opener(); + let store = opener.open().unwrap().get_hot_store(); + vec![TestDB::new(), store.storage.clone()] + } + + /// Tests the behavior of the iterators. Iterators don't really work over cold storage, so we're not testing it here. + #[test] + fn test_db_iter() { + for db in test_and_rocksdb() { + let mut transaction = DBTransaction::new(); + transaction.insert(DBCol::Block, "a".into(), "val_a".into()); + transaction.insert(DBCol::Block, "aa".into(), "val_aa".into()); + transaction.insert(DBCol::Block, "aa1".into(), "val_aa1".into()); + transaction.insert(DBCol::Block, "bb1".into(), "val_bb1".into()); + transaction.insert(DBCol::Block, "cc1".into(), "val_cc1".into()); + db.write(transaction).unwrap(); + + let keys: Vec<_> = db + .iter(DBCol::Block) + .map(|data| String::from_utf8(data.unwrap().0.to_vec()).unwrap()) + .collect(); + assert_eq!(keys, vec!["a", "aa", "aa1", "bb1", "cc1"]); + + let keys: Vec<_> = db + .iter_range(DBCol::Block, Some("aa".as_bytes()), Some("bb1".as_bytes())) + .map(|data| String::from_utf8(data.unwrap().0.to_vec()).unwrap()) + .collect(); + assert_eq!(keys, vec!["aa", "aa1"]); + } + } +} diff --git a/core/store/src/db/refcount.rs b/core/store/src/db/refcount.rs new file mode 100644 index 000000000..80278847a --- /dev/null +++ b/core/store/src/db/refcount.rs @@ -0,0 +1,353 @@ +//! Functions for handling reference counted columns. +//! +//! Some of the columns in the database are reference counted. Those are the +//! ones for which [`crate::DBCol::is_rc`] returns `true`. Inserting value to +//! such column increases the reference count and removing value decreases it. +//! The key is really removed from the database when reference count reaches +//! zero. +//! +//! Note that it is an error to try to store different values under the same +//! key. That is, when increasing the reference count of an existing key, the +//! same value must be inserted. If different values are inserted it’s not +//! defined which one will be later read from the database. +//! +//! The reference counts are stored together with the values by simply attaching +//! little-endian encoded 64-bit signed integer at the end of it. See +//! [`add_positive_refcount`] and [`encode_negative_refcount`] functions for +//! a way to encode reference count. During compaction, RocksDB merges the +//! values by adding the reference counts. When the reference count reaches +//! zero RocksDB removes the key from the database. + +use std::cmp::Ordering; +use std::io; + +use rocksdb::compaction_filter::Decision; + +use crate::db::RocksDB; +use crate::DBCol; + +/// Extracts reference count from raw value and returns it along with the value. +/// +/// Decodes the raw value extracting the actual value and reference count: +/// - rc > 0 ⇒ returns `(Some(value), rc)`, +/// - rc ≤ 0 ⇒ returns `(None, rc)`. +/// +/// In builds with debug assertions enabled, panics if `bytes` are non-empty but +/// too short to fit 64-bit reference count. +pub fn decode_value_with_rc(bytes: &[u8]) -> (Option<&[u8]>, i64) { + if bytes.len() < 8 { + debug_assert!(bytes.is_empty()); + return (None, 0); + } + let (head, tail) = stdx::rsplit_slice::<8>(bytes); + let rc = i64::from_le_bytes(*tail); + if rc <= 0 { + (None, rc) + } else { + (Some(head), rc) + } +} + +/// Strips refcount from an owned buffer. +/// +/// Works like [`decode_value_with_rc`] but operates on an owned vector thus +/// potentially avoiding memory allocations. Returns None if the refcount on +/// the argument is non-positive. +pub(crate) fn strip_refcount(mut bytes: Vec) -> Option> { + if let Some(len) = bytes.len().checked_sub(8) { + if i64::from_le_bytes(bytes[len..].try_into().unwrap()) > 0 { + bytes.truncate(len); + return Some(bytes); + } + } else { + debug_assert!(bytes.is_empty()); + } + None +} + +/// Sets the refcount to the given value. +/// +/// This method assumes that the data already contains a reference count stored +/// in the last 8 bytes. It overwrites this value with the new value. +/// +/// Returns None if the input bytes are too short to contain a refcount. +pub(crate) fn set_refcount(data: &mut Vec, refcount: i64) -> io::Result<()> { + const BYTE_COUNT: usize = std::mem::size_of::(); + + if let Some(len) = data.len().checked_sub(BYTE_COUNT) { + let refcount: [u8; BYTE_COUNT] = refcount.to_le_bytes(); + data[len..].copy_from_slice(&refcount); + Ok(()) + } else { + Err(io::Error::new( + std::io::ErrorKind::InvalidData, + "The data is too short to set refcount", + )) + } +} + +/// Encode a positive reference count into the value. +pub(crate) fn add_positive_refcount(data: &[u8], rc: std::num::NonZeroU32) -> Vec { + [data, &i64::from(rc.get()).to_le_bytes()].concat() +} + +/// Returns empty value with encoded negative reference count. +/// +/// `rc` gives the absolute value of the reference count. +pub(crate) fn encode_negative_refcount(rc: std::num::NonZeroU32) -> Vec { + (-i64::from(rc.get())).to_le_bytes().to_vec() +} + +/// Merge reference counted values together. +/// +/// Extracts reference count from all provided value and sums them together and +/// returns result depending on rc: +/// - rc = 0 ⇒ empty, +/// - rc < 0 ⇒ encoded reference count, +/// - rc > 0 ⇒ value with encoded reference count. +/// +/// Assumes that all provided values with positive reference count have the same +/// value so that the function is free to pick any of the values. In build with +/// debug assertions panics if this is not true. +pub(crate) fn refcount_merge<'a>( + existing: Option<&'a [u8]>, + operands: impl IntoIterator, +) -> Vec { + let (mut payload, mut rc) = existing.map_or((None, 0), decode_value_with_rc); + for (new_payload, delta) in operands.into_iter().map(decode_value_with_rc) { + if payload.is_none() { + payload = new_payload; + } else if new_payload.is_some() { + debug_assert_eq!(payload, new_payload); + } + rc += delta; + } + + match rc.cmp(&0) { + Ordering::Less => rc.to_le_bytes().to_vec(), + Ordering::Equal => Vec::new(), + Ordering::Greater => [payload.unwrap_or(b""), &rc.to_le_bytes()].concat(), + } +} + +/// Iterator treats empty value as no value and strips refcount +pub(crate) fn iter_with_rc_logic<'a>( + col: DBCol, + iterator: impl Iterator, Box<[u8]>)>> + 'a, +) -> crate::db::DBIterator<'a> { + if col.is_rc() { + Box::new(iterator.filter_map(|item| match item { + Err(err) => Some(Err(err)), + Ok((key, value)) => { + strip_refcount(value.into_vec()).map(|value| Ok((key, value.into_boxed_slice()))) + } + })) + } else { + Box::new(iterator) + } +} + +impl RocksDB { + /// Merge adds refcounts, zero refcount becomes empty value. + /// Empty values get filtered by get methods, and removed by compaction. + pub(crate) fn refcount_merge( + _new_key: &[u8], + existing: Option<&[u8]>, + operands: &rocksdb::MergeOperands, + ) -> Option> { + Some(self::refcount_merge(existing, operands)) + } + + /// Compaction filter for DBCol::State + pub(crate) fn empty_value_compaction_filter( + _level: u32, + _key: &[u8], + value: &[u8], + ) -> Decision { + if value.is_empty() { + Decision::Remove + } else { + Decision::Keep + } + } +} + +#[cfg(test)] +mod test { + use crate::DBCol; + + const MINUS_TWO: &[u8] = b"\xfe\xff\xff\xff\xff\xff\xff\xff"; + const MINUS_ONE: &[u8] = b"\xff\xff\xff\xff\xff\xff\xff\xff"; + const ZERO: &[u8] = b"\0\0\0\0\0\0\0\0"; + const PLUS_ONE: &[u8] = b"\x01\0\0\0\0\0\0\0"; + const PLUS_TWO: &[u8] = b"\x02\0\0\0\0\0\0\0"; + + fn check_debug_assert_or(callback: F, predicate: P) + where + F: FnOnce() -> R + std::panic::UnwindSafe, + P: FnOnce(R), + R: std::fmt::Debug, + { + let res = std::panic::catch_unwind(callback); + if cfg!(debug_assertions) { + assert!(res.is_err(), "{res:?}"); + } else { + predicate(res.unwrap()); + } + } + + #[test] + fn decode_value_with_rc() { + fn test(want_value: Option<&[u8]>, want_rc: i64, bytes: &[u8]) { + let got = super::decode_value_with_rc(bytes); + assert_eq!((want_value, want_rc), got); + + let got = super::strip_refcount(bytes.to_vec()); + assert_eq!(want_value, got.as_deref()); + } + + test(None, -2, MINUS_TWO); + test(None, -2, b"foobar\xfe\xff\xff\xff\xff\xff\xff\xff"); + test(None, 0, b""); + test(None, 0, ZERO); + test(None, 0, b"bar\0\0\0\0\0\0\0\0"); + test(Some(b""), 2, PLUS_TWO); + test(Some(b"baz"), 2, b"baz\x02\0\0\0\0\0\0\0"); + + check_debug_assert_or( + || super::decode_value_with_rc(b"short"), + |got| assert_eq!((None, 0), got), + ); + } + + #[test] + fn add_encode_refcount() { + fn test(want: &[u8], data: &[u8], rc: u32) { + let rc = std::num::NonZeroU32::new(rc).unwrap(); + assert_eq!(want, &super::add_positive_refcount(data, rc)); + } + + test(PLUS_TWO, b"", 2); + test(b"foo\x02\0\0\0\0\0\0\0", b"foo", 2); + + let rc = std::num::NonZeroU32::new(2).unwrap(); + assert_eq!(MINUS_TWO, &super::encode_negative_refcount(rc)); + } + + #[test] + fn set_refcount() { + fn test(want: Vec, data: &[u8], refcount: i64) { + let mut data = data.to_vec(); + let result = super::set_refcount(&mut data, refcount); + assert!(result.is_ok()); + assert_eq!(want, data); + } + + test(PLUS_TWO.to_vec(), b"\0\0\0\0\0\0\0\0", 2); + test(MINUS_TWO.to_vec(), b"\0\0\0\0\0\0\0\0", -2); + } + + #[test] + fn refcount_merge() { + fn test(want: &[u8], operands: &[&[u8]]) { + let it = operands.into_iter().copied(); + let got = super::refcount_merge(None, it); + assert_eq!(want, got.as_slice()); + + if !operands.is_empty() { + let it = operands[1..].into_iter().copied(); + let got = super::refcount_merge(Some(operands[0]), it); + assert_eq!(want, got.as_slice()); + } + } + + test(b"", &[]); + test(b"", &[ZERO]); + test(b"", &[PLUS_ONE, MINUS_ONE]); + test(b"", &[PLUS_TWO, MINUS_ONE, MINUS_ONE]); + test(b"", &[b"foo\x01\0\0\0\0\0\0\0", MINUS_ONE]); + test(b"", &[b"foo\x02\0\0\0\0\0\0\0", MINUS_ONE, MINUS_ONE]); + test(b"", &[b"foo\x02\0\0\0\0\0\0\0", MINUS_TWO]); + + test(MINUS_ONE, &[MINUS_ONE]); + test(MINUS_ONE, &[b"", MINUS_ONE]); + test(MINUS_ONE, &[ZERO, MINUS_ONE]); + test(MINUS_ONE, &[b"foo\x01\0\0\0\0\0\0\0", MINUS_TWO]); + test(MINUS_ONE, &[b"foo\x01\0\0\0\0\0\0\0", MINUS_ONE, MINUS_ONE]); + + test(b"foo\x02\0\0\0\0\0\0\0", &[b"foo\x01\0\0\0\0\0\0\0", b"foo\x01\0\0\0\0\0\0\0"]); + test(b"foo\x01\0\0\0\0\0\0\0", &[b"foo\x01\0\0\0\0\0\0\0"]); + test(b"foo\x01\0\0\0\0\0\0\0", &[b"foo\x02\0\0\0\0\0\0\0", MINUS_ONE]); + } + + #[test] + fn compaction_filter() { + use rocksdb::compaction_filter::Decision; + + fn test(want: Decision, value: &[u8]) { + let got = super::RocksDB::empty_value_compaction_filter(42, b"key", value); + assert_eq!(std::mem::discriminant(&want), std::mem::discriminant(&got)); + } + + test(Decision::Remove, b""); + test(Decision::Keep, PLUS_ONE); + test(Decision::Keep, MINUS_ONE); + test(Decision::Keep, b"foo\x01\0\0\0\0\0\0\0"); + test(Decision::Keep, b"foo\xff\xff\xff\xff\xff\xff\xff\xff"); + + // This never happens in production since the filter is only ever run on + // refcounted values which have length ≥ 8. + test(Decision::Keep, b"foo"); + // And this never happens because zero is encoded as empty value. + test(Decision::Keep, ZERO); + } + + #[test] + fn iter_with_rc_logic() { + fn into_box(data: &[u8]) -> Box<[u8]> { + data.to_vec().into_boxed_slice() + } + + fn test(want: &[&[u8]], col: DBCol, values: &[&[u8]]) { + use std::ops::Deref; + + const KEY: &[u8] = b"key"; + let iter = values.into_iter().map(|value| Ok((into_box(KEY), into_box(value)))); + let got = super::iter_with_rc_logic(col, iter) + .map(Result::unwrap) + .map(|(key, value)| { + assert_eq!(KEY, key.deref()); + value + }) + .collect::>(); + let got = got.iter().map(Box::deref).collect::>(); + assert_eq!(want, got.as_slice()); + } + + // Column without reference counting. ALl values are returned as is. + assert!(!DBCol::Block.is_rc()); + test( + &[&b""[..], &b"foo"[..], MINUS_ONE, ZERO, PLUS_ONE], + DBCol::Block, + &[&b""[..], &b"foo"[..], MINUS_ONE, ZERO, PLUS_ONE], + ); + + // Column with reference counting. Count is extracted. + const RC_COL: DBCol = DBCol::State; + assert!(RC_COL.is_rc()); + + test( + &[b"", b"foo"], + RC_COL, + &[ + MINUS_ONE, + b"foo\xff\xff\xff\xff\xff\xff\xff\xff", + b"", + ZERO, + b"foo\x00\0\0\0\0\0\0\0", + PLUS_ONE, + b"foo\x01\0\0\0\0\0\0\0", + ], + ); + } +} diff --git a/core/store/src/db/rocksdb.rs b/core/store/src/db/rocksdb.rs new file mode 100644 index 000000000..0a6e0a127 --- /dev/null +++ b/core/store/src/db/rocksdb.rs @@ -0,0 +1,829 @@ +use crate::config::Mode; +use crate::db::{refcount, DBIterator, DBOp, DBSlice, DBTransaction, Database, StatsValue}; +use crate::{metadata, metrics, DBCol, StoreConfig, StoreStatistics, Temperature}; +use ::rocksdb::{ + BlockBasedOptions, Cache, ColumnFamily, Env, IteratorMode, Options, ReadOptions, WriteBatch, DB, +}; +use once_cell::sync::Lazy; +use std::io; +use std::ops::Deref; +use std::path::Path; +use strum::IntoEnumIterator; +use tracing::warn; + +mod instance_tracker; +pub(crate) mod snapshot; + +/// List of integer RocskDB properties we’re reading when collecting statistics. +/// +/// In the end, they are exported as Prometheus metrics. +static CF_PROPERTY_NAMES: Lazy> = Lazy::new(|| { + use ::rocksdb::properties; + let mut ret = Vec::new(); + ret.extend_from_slice( + &[ + properties::LIVE_SST_FILES_SIZE, + properties::ESTIMATE_LIVE_DATA_SIZE, + properties::COMPACTION_PENDING, + properties::NUM_RUNNING_COMPACTIONS, + properties::ESTIMATE_PENDING_COMPACTION_BYTES, + properties::ESTIMATE_TABLE_READERS_MEM, + properties::BLOCK_CACHE_CAPACITY, + properties::BLOCK_CACHE_USAGE, + properties::CUR_SIZE_ACTIVE_MEM_TABLE, + properties::SIZE_ALL_MEM_TABLES, + ] + .map(std::ffi::CStr::to_owned), + ); + for level in 0..=6 { + ret.push(properties::num_files_at_level(level)); + } + ret +}); + +pub struct RocksDB { + db: DB, + db_opt: Options, + + /// Map from [`DBCol`] to a column family handler in the RocksDB. + /// + /// Rather than accessing this field directly, use [`RocksDB::cf_handle`] + /// method instead. It returns `&ColumnFamily` which is what you usually + /// want. + cf_handles: enum_map::EnumMap>>, + + // RAII-style of keeping track of the number of instances of RocksDB and + // counting total sum of max_open_files. + _instance_tracker: instance_tracker::InstanceTracker, +} + +// DB was already Send+Sync. cf and read_options are const pointers using only functions in +// this file and safe to share across threads. +unsafe impl Send for RocksDB {} +unsafe impl Sync for RocksDB {} + +impl RocksDB { + /// Opens the database. + /// + /// `path` specifies location of the database. It’s assumed that it has + /// been resolved based on configuration in `store_config` and thus path + /// configuration in `store_config` is ignored. + /// + /// `store_config` specifies other storage configuration such open files + /// limit or whether to enable collection of statistics. + /// + /// `mode` specifies whether to open the database in read/write or read-only + /// mode. In the latter case, the database will not be created if it + /// doesn’t exist nor any migrations will be performed if the database has + /// database version different than expected. + /// + /// `temp` specifies whether the database is cold or hot which affects + /// whether refcount merge operator is configured on reference counted + /// column. + pub fn open( + path: &Path, + store_config: &StoreConfig, + mode: Mode, + temp: Temperature, + ) -> io::Result { + let columns: Vec = DBCol::iter().collect(); + Self::open_with_columns(path, store_config, mode, temp, &columns) + } + + /// Opens the database with given set of column families configured. + /// + /// With cold storage, we will need to be able to configure the database + /// with only a subset of columns. The `columns` argument specifies which + /// columns to configure in the database. + /// + /// Note that RocksDB is weird. It’s not possible to open database in + /// read/write mode without specifying all the column families existing in + /// the database. On the other hand, it’s not possible to open database in + /// read-only mode while specifying column families which don’t exist. + /// + /// Furthermore, note that when opening in read/write mode, we configure + /// RocksDB to create missing columns. + /// + /// With all that, it’s actually quite messy if at some point we’ll end up + /// opening cold storage as hot since it’ll create all the missing columns. + fn open_with_columns( + path: &Path, + store_config: &StoreConfig, + mode: Mode, + temp: Temperature, + columns: &[DBCol], + ) -> io::Result { + let counter = instance_tracker::InstanceTracker::try_new(store_config.max_open_files) + .map_err(io::Error::other)?; + let (db, db_opt) = Self::open_db(path, store_config, mode, temp, columns)?; + let cf_handles = Self::get_cf_handles(&db, columns); + Ok(Self { db, db_opt, cf_handles, _instance_tracker: counter }) + } + + /// Opens the database with given column families configured. + fn open_db( + path: &Path, + store_config: &StoreConfig, + mode: Mode, + temp: Temperature, + columns: &[DBCol], + ) -> io::Result<(DB, Options)> { + let options = rocksdb_options(store_config, mode); + let cf_descriptors = columns + .iter() + .copied() + .map(|col| { + rocksdb::ColumnFamilyDescriptor::new( + col_name(col), + rocksdb_column_options(col, store_config, temp), + ) + }) + .collect::>(); + let db = if mode.read_only() { + DB::open_cf_descriptors_read_only(&options, path, cf_descriptors, false) + } else { + DB::open_cf_descriptors(&options, path, cf_descriptors) + } + .map_err(io::Error::other)?; + if cfg!(feature = "single_thread_rocksdb") { + // These have to be set after open db + let mut env = Env::new().unwrap(); + env.set_bottom_priority_background_threads(0); + env.set_high_priority_background_threads(0); + env.set_low_priority_background_threads(0); + env.set_background_threads(0); + println!("Disabled all background threads in rocksdb"); + } + Ok((db, options)) + } + + /// Returns mapping from [`DBCol`] to cf handle used with RocksDB calls. + /// + /// The mapping is created for column families given in the `columns` list + /// only. All other columns will map to `None`. + /// + /// ## Safety + /// + /// This function is safe but using the returned mapping safely requires + /// that it does not outlive `db` and that `db` is not modified. The safety + /// relies on `db` returning stable mapping for column families. + fn get_cf_handles( + db: &DB, + columns: &[DBCol], + ) -> enum_map::EnumMap>> { + let mut cf_handles = enum_map::EnumMap::default(); + for col in columns.iter().copied() { + let ptr = db + .cf_handle(&col_name(col)) + .and_then(|cf| std::ptr::NonNull::new(cf as *const _ as *mut _)) + .unwrap_or_else(|| panic!("Missing cf handle for {col}")); + cf_handles[col] = Some(ptr); + } + cf_handles + } + + /// Returns column family handler to use with RocsDB for given column. + /// + /// If the database has not been setup to access given column, panics if + /// debug assertions are enabled or returns an error otherwise. + /// + /// ## Safety + /// + /// This function is safe so long as `db` field has not been modified since + /// `cf_handles` mapping has been constructed. We technically should mark + /// this function unsafe but to improve ergonomy we didn’t. This is an + /// internal method so hopefully the implementation knows what it’s doing. + fn cf_handle(&self, col: DBCol) -> io::Result<&ColumnFamily> { + if let Some(ptr) = self.cf_handles[col] { + // SAFETY: The pointers are valid so long as self.db is valid. + Ok(unsafe { ptr.as_ref() }) + } else if cfg!(debug_assertions) { + panic!("The database instance isn’t setup to access {col}"); + } else { + Err(io::Error::other(format!("{col}: no such column"))) + } + } + + /// Returns iterator over all column families and their handles. + /// + /// This is kind of like iterating over all [`DBCol`] variants and calling + /// [`Self::cf_handle`] except this method takes care of properly filtering + /// out column families that the database instance isn’t setup to handle. + /// + /// ## Safety + /// + /// This function is safe so long as `db` field has not been modified since + /// `cf_handles` mapping has been constructed. We technically should mark + /// this function unsafe but to improve ergonomy we didn’t. This is an + /// internal method so hopefully the implementation knows what it’s doing. + fn cf_handles(&self) -> impl Iterator { + self.cf_handles.iter().filter_map(|(col, ptr)| { + if let Some(ptr) = *ptr { + // SAFETY: The pointers are valid so long as self.db is valid. + Some((col, unsafe { ptr.as_ref() })) + } else { + None + } + }) + } + + /// Iterates over rocksDB storage. + /// You can optionally specify the bounds to limit the range over which it will iterate. + /// You can specify EITHER the prefix, OR lower/upper bounds. + /// Upper bound value is not included in the iteration. + /// Specifying both prefix and lower & upper bounds will result in undetermined behavior. + fn iter_raw_bytes_internal<'a>( + &'a self, + col: DBCol, + prefix: Option<&[u8]>, + lower_bound: Option<&[u8]>, + upper_bound: Option<&[u8]>, + ) -> RocksDBIterator<'a> { + let cf_handle = self.cf_handle(col).unwrap(); + let mut read_options = rocksdb_read_options(); + if prefix.is_some() && (lower_bound.is_some() || upper_bound.is_some()) { + panic!("Cannot iterate both with prefix and lower/upper bounds at the same time."); + } + if let Some(prefix) = prefix { + read_options.set_iterate_range(::rocksdb::PrefixRange(prefix)); + // Note: prefix_same_as_start doesn’t do anything for us. It takes + // effect only if prefix extractor is configured for the column + // family which is something we’re not doing. Setting this option + // is therefore pointless. + // read_options.set_prefix_same_as_start(true); + } + if let Some(lower_bound) = lower_bound { + read_options.set_iterate_lower_bound(lower_bound); + } + if let Some(upper_bound) = upper_bound { + read_options.set_iterate_upper_bound(upper_bound); + } + let iter = self.db.iterator_cf_opt(cf_handle, read_options, IteratorMode::Start); + RocksDBIterator(iter) + } +} + +struct RocksDBIterator<'a>(rocksdb::DBIteratorWithThreadMode<'a, DB>); + +impl<'a> Iterator for RocksDBIterator<'a> { + type Item = io::Result<(Box<[u8]>, Box<[u8]>)>; + + fn next(&mut self) -> Option { + Some(self.0.next()?.map_err(io::Error::other)) + } +} + +impl<'a> std::iter::FusedIterator for RocksDBIterator<'a> {} + +impl RocksDB { + /// Returns ranges of keys in a given column family. + /// + /// In other words, returns the smallest and largest key in the column. If + /// the column is empty, returns `None`. + fn get_cf_key_range( + &self, + cf_handle: &ColumnFamily, + ) -> Result>>, ::rocksdb::Error> { + let range = { + let mut iter = self.db.iterator_cf(cf_handle, IteratorMode::Start); + let start = iter.next().transpose()?; + iter.set_mode(IteratorMode::End); + let end = iter.next().transpose()?; + (start, end) + }; + match range { + (Some(start), Some(end)) => Ok(Some(start.0..=end.0)), + (None, None) => Ok(None), + _ => unreachable!(), + } + } + + pub fn compact_column(&self, col: DBCol) -> io::Result<()> { + let none = Option::<&[u8]>::None; + tracing::info!(target: "db", column = %col, "Compact column"); + self.db.compact_range_cf(self.cf_handle(col)?, none, none); + Ok(()) + } +} + +impl Database for RocksDB { + fn get_raw_bytes(&self, col: DBCol, key: &[u8]) -> io::Result>> { + let timer = + metrics::DATABASE_OP_LATENCY_HIST.with_label_values(&["get", col.into()]).start_timer(); + let read_options = rocksdb_read_options(); + let result = self + .db + .get_pinned_cf_opt(self.cf_handle(col)?, key, &read_options) + .map_err(io::Error::other)? + .map(DBSlice::from_rocksdb_slice); + timer.observe_duration(); + Ok(result) + } + + fn iter_raw_bytes(&self, col: DBCol) -> DBIterator { + Box::new(self.iter_raw_bytes_internal(col, None, None, None)) + } + + fn iter(&self, col: DBCol) -> DBIterator { + refcount::iter_with_rc_logic(col, self.iter_raw_bytes_internal(col, None, None, None)) + } + + fn iter_prefix(&self, col: DBCol, key_prefix: &[u8]) -> DBIterator { + let iter = self.iter_raw_bytes_internal(col, Some(key_prefix), None, None); + refcount::iter_with_rc_logic(col, iter) + } + + fn iter_range<'a>( + &'a self, + col: DBCol, + lower_bound: Option<&[u8]>, + upper_bound: Option<&[u8]>, + ) -> DBIterator<'a> { + let iter = self.iter_raw_bytes_internal(col, None, lower_bound, upper_bound); + refcount::iter_with_rc_logic(col, iter) + } + + fn write(&self, transaction: DBTransaction) -> io::Result<()> { + let mut batch = WriteBatch::default(); + for op in transaction.ops { + match op { + DBOp::Set { col, key, value } => { + batch.put_cf(self.cf_handle(col)?, key, value); + } + DBOp::Insert { col, key, value } => { + if cfg!(debug_assertions) { + if let Ok(Some(old_value)) = self.get_raw_bytes(col, &key) { + super::assert_no_overwrite(col, &key, &value, &*old_value) + } + } + batch.put_cf(self.cf_handle(col)?, key, value); + } + DBOp::UpdateRefcount { col, key, value } => { + batch.merge_cf(self.cf_handle(col)?, key, value); + } + DBOp::Delete { col, key } => { + batch.delete_cf(self.cf_handle(col)?, key); + } + DBOp::DeleteAll { col } => { + let cf_handle = self.cf_handle(col)?; + let range = self.get_cf_key_range(cf_handle).map_err(io::Error::other)?; + if let Some(range) = range { + batch.delete_range_cf(cf_handle, range.start(), range.end()); + // delete_range_cf deletes ["begin_key", "end_key"), so need one more delete + batch.delete_cf(cf_handle, range.end()) + } + } + DBOp::DeleteRange { col, from, to } => { + batch.delete_range_cf(self.cf_handle(col)?, from, to); + } + } + } + self.db.write(batch).map_err(io::Error::other) + } + + fn compact(&self) -> io::Result<()> { + for col in DBCol::iter() { + self.compact_column(col)?; + } + Ok(()) + } + + fn flush(&self) -> io::Result<()> { + // Need to iterator over all CFs because the normal `flush()` only + // flushes the default column family. + for col in DBCol::iter() { + self.db.flush_cf(self.cf_handle(col)?).map_err(io::Error::other)?; + } + Ok(()) + } + + /// Trying to get + /// 1. RocksDB statistics + /// 2. Selected RockdDB properties for column families + fn get_store_statistics(&self) -> Option { + let mut result = StoreStatistics { data: vec![] }; + if let Some(stats_str) = self.db_opt.get_statistics() { + if let Err(err) = parse_statistics(&stats_str, &mut result) { + warn!(target: "store", "Failed to parse store statistics: {:?}", err); + } + } + self.get_cf_statistics(&mut result); + if result.data.is_empty() { + None + } else { + Some(result) + } + } + + fn create_checkpoint(&self, path: &std::path::Path) -> anyhow::Result<()> { + let _span = + tracing::info_span!(target: "state_snapshot", "create_checkpoint", ?path).entered(); + let cp = ::rocksdb::checkpoint::Checkpoint::new(&self.db)?; + cp.create_checkpoint(path)?; + Ok(()) + } +} + +/// DB level options +fn rocksdb_options(store_config: &StoreConfig, mode: Mode) -> Options { + let mut opts = Options::default(); + + set_compression_options(&mut opts); + opts.create_missing_column_families(mode.read_write()); + opts.create_if_missing(mode.can_create()); + opts.set_use_fsync(false); + opts.set_max_open_files(store_config.max_open_files.try_into().unwrap_or(i32::MAX)); + opts.set_keep_log_file_num(1); + opts.set_bytes_per_sync(bytesize::MIB); + opts.set_write_buffer_size(256 * bytesize::MIB as usize); + opts.set_max_bytes_for_level_base(256 * bytesize::MIB); + + if cfg!(feature = "single_thread_rocksdb") { + opts.set_disable_auto_compactions(true); + opts.set_max_background_jobs(0); + opts.set_stats_dump_period_sec(0); + opts.set_stats_persist_period_sec(0); + opts.set_level_zero_slowdown_writes_trigger(-1); + opts.set_level_zero_file_num_compaction_trigger(-1); + opts.set_level_zero_stop_writes_trigger(100000000); + } else { + opts.increase_parallelism(std::cmp::max(1, num_cpus::get() as i32 / 2)); + opts.set_max_total_wal_size(bytesize::GIB); + } + + // TODO(mina86): Perhaps enable statistics even in read-only mode? + if mode.read_write() && store_config.enable_statistics { + // Rust API doesn't permit choosing stats level. The default stats level + // is `kExceptDetailedTimers`, which is described as: "Collects all + // stats except time inside mutex lock AND time spent on compression." + opts.enable_statistics(); + // Disabling dumping stats to files because the stats are exported to + // Prometheus. + opts.set_stats_persist_period_sec(0); + opts.set_stats_dump_period_sec(0); + } + + opts +} + +fn rocksdb_read_options() -> ReadOptions { + let mut read_options = ReadOptions::default(); + read_options.set_verify_checksums(false); + read_options +} + +/// If true then we enable caching of index blocks inside block cache +fn use_block_cache_for_index_and_filter_blocks(db_col: DBCol) -> bool { + match db_col { + DBCol::FlatState => false, + _ => true, + } +} + +fn rocksdb_block_based_options(store_config: &StoreConfig, db_col: DBCol) -> BlockBasedOptions { + let cache_size = store_config.col_cache_size(db_col); + + let mut block_opts = BlockBasedOptions::default(); + block_opts.set_block_size(store_config.block_size.as_u64().try_into().unwrap()); + // We create block_cache for each of the columns, so the total cache size is (num_of_columns - 2) * 32MiB + // Plus the 128MiB from FlatState and 512MiB from State columns + block_opts.set_block_cache(&Cache::new_lru_cache(cache_size.as_u64().try_into().unwrap())); + if use_block_cache_for_index_and_filter_blocks(db_col) { + block_opts.set_pin_l0_filter_and_index_blocks_in_cache(true); + block_opts.set_cache_index_and_filter_blocks(true); + } else { + block_opts.set_cache_index_and_filter_blocks(false); + } + block_opts.set_bloom_filter(10.0, true); + + block_opts +} + +fn rocksdb_column_options(col: DBCol, store_config: &StoreConfig, temp: Temperature) -> Options { + let mut opts = Options::default(); + set_compression_options(&mut opts); + opts.set_level_compaction_dynamic_level_bytes(true); + opts.set_block_based_table_factory(&rocksdb_block_based_options(store_config, col)); + + // Note that this function changes a lot of rustdb parameters including: + // write_buffer_size = memtable_memory_budget / 4 + // min_write_buffer_number_to_merge = 2 + // max_write_buffer_number = 6 + // level0_file_num_compaction_trigger = 2 + // target_file_size_base = memtable_memory_budget / 8 + // max_bytes_for_level_base = memtable_memory_budget + // compaction_style = kCompactionStyleLevel + // Also it sets compression_per_level in a way that the first 2 levels have no compression and + // the rest use LZ4 compression. + // See the implementation here: + // https://github.com/facebook/rocksdb/blob/c18c4a081c74251798ad2a1abf83bad417518481/options/options.cc#L588. + let memtable_memory_budget = 128 * bytesize::MIB as usize; + opts.optimize_level_style_compaction(memtable_memory_budget); + + opts.set_target_file_size_base(64 * bytesize::MIB); + if temp == Temperature::Hot && col.is_rc() { + opts.set_merge_operator("refcount merge", RocksDB::refcount_merge, RocksDB::refcount_merge); + opts.set_compaction_filter("empty value filter", RocksDB::empty_value_compaction_filter); + } + opts +} + +fn set_compression_options(opts: &mut Options) { + opts.set_compression_type(rocksdb::DBCompressionType::Lz4); + opts.set_bottommost_compression_type(rocksdb::DBCompressionType::Zstd); + // RocksDB documenation says that 16KB is a typical dictionary size. + // We've empirically tuned the dicionary size to twice of that 'typical' size. + // Having train data size x100 from dictionary size is a recommendation from RocksDB. + // See: https://rocksdb.org/blog/2021/05/31/dictionary-compression.html?utm_source=dbplatz + let dict_size = 2 * 16384; + let max_train_bytes = dict_size * 100; + // We use default parameters of RocksDB here: + // window_bits is -14 and is unused (Zlib-specific parameter), + // compression_level is 32767 meaning the default compression level for ZSTD, + // compression_strategy is 0 and is unused (Zlib-specific parameter). + // See: https://github.com/facebook/rocksdb/blob/main/include/rocksdb/advanced_options.h#L176: + opts.set_bottommost_compression_options( + /*window_bits */ -14, /*compression_level */ 32767, + /*compression_strategy */ 0, dict_size, /*enabled */ true, + ); + opts.set_bottommost_zstd_max_train_bytes(max_train_bytes, true); +} + +impl RocksDB { + /// Blocks until all RocksDB instances (usually 0 or 1) gracefully shutdown. + pub fn block_until_all_instances_are_dropped() { + instance_tracker::block_until_all_instances_are_closed(); + } + + /// Returns metadata of the database or `None` if the db doesn’t exist. + pub(crate) fn get_metadata( + path: &Path, + config: &StoreConfig, + ) -> io::Result> { + if !path.join("CURRENT").is_file() { + return Ok(None); + } + // Specify only DBCol::DbVersion. It’s ok to open db in read-only mode + // without specifying all column families but it’s an error to provide + // a descriptor for a column family which doesn’t exist. This allows us + // to read the version without modifying the database before we figure + // out if there are any necessary migrations to perform. + let cols = [DBCol::DbVersion]; + let db = Self::open_with_columns(path, config, Mode::ReadOnly, Temperature::Hot, &cols)?; + Some(metadata::DbMetadata::read(&db)).transpose() + } + + /// Gets every int property in CF_PROPERTY_NAMES for every column in DBCol. + fn get_cf_statistics(&self, result: &mut StoreStatistics) { + for prop_name in CF_PROPERTY_NAMES.deref() { + let values = self + .cf_handles() + .filter_map(|(col, handle)| { + let prop = self.db.property_int_value_cf(handle, prop_name); + Some(StatsValue::ColumnValue(col, prop.ok()?? as i64)) + }) + .collect::>(); + if !values.is_empty() { + // TODO(mina86): Once const_str_from_utf8 is stabilised we might + // be able convert this runtime UTF-8 validation into const. + let stat_name = prop_name.to_str().unwrap(); + result.data.push((stat_name.to_string(), values)); + } + } + } +} + +impl Drop for RocksDB { + fn drop(&mut self) { + if cfg!(feature = "single_thread_rocksdb") { + // RocksDB with only one thread stuck on wait some condition var + // Turn on additional threads to proceed + let mut env = Env::new().unwrap(); + env.set_background_threads(4); + } + self.db.cancel_all_background_work(true); + } +} + +/// Parses a string containing RocksDB statistics. +/// Results are added into provided 'result' parameter. +fn parse_statistics( + statistics: &str, + result: &mut StoreStatistics, +) -> Result<(), Box> { + // Statistics are given one per line. + for line in statistics.lines() { + // Each line follows one of two formats: + // 1) COUNT : + // 2) P50 : P90 : COUNT : SUM : + // Each line gets split into words and we parse statistics according to this format. + if let Some((stat_name, words)) = line.split_once(' ') { + let mut values = vec![]; + let mut words = words.split(" : ").flat_map(|v| v.split(" ")); + while let (Some(key), Some(val)) = (words.next(), words.next()) { + match key { + "COUNT" => values.push(StatsValue::Count(val.parse::()?)), + "SUM" => values.push(StatsValue::Sum(val.parse::()?)), + p if p.starts_with("P") => values.push(StatsValue::Percentile( + key[1..].parse::()?, + val.parse::()?, + )), + _ => { + warn!(target: "stats", "Unsupported stats value: {key} in {line}"); + } + } + } + // We push some stats to result, even if later parsing will fail. + result.data.push((stat_name.to_string(), values)); + } + } + Ok(()) +} + +/// Returns name of a RocksDB column family corresponding to given column. +/// +/// Historically we used `col##` names (with `##` being index of the column). +/// We have since deprecated this convention. All future column families are +/// named the same as the variant of the [`DBCol`] enum. +fn col_name(col: DBCol) -> &'static str { + match col { + DBCol::DbVersion => "col0", + DBCol::BlockMisc => "col1", + DBCol::Block => "col2", + DBCol::BlockHeader => "col3", + DBCol::BlockHeight => "col4", + DBCol::State => "col5", + DBCol::ChunkExtra => "col6", + DBCol::_TransactionResult => "col7", + DBCol::OutgoingReceipts => "col8", + DBCol::IncomingReceipts => "col9", + DBCol::_Peers => "col10", + DBCol::EpochInfo => "col11", + DBCol::BlockInfo => "col12", + DBCol::Chunks => "col13", + DBCol::PartialChunks => "col14", + DBCol::BlocksToCatchup => "col15", + DBCol::StateDlInfos => "col16", + DBCol::ChallengedBlocks => "col17", + DBCol::StateHeaders => "col18", + DBCol::InvalidChunks => "col19", + DBCol::BlockExtra => "col20", + DBCol::BlockPerHeight => "col21", + DBCol::StateParts => "col22", + DBCol::EpochStart => "col23", + DBCol::AccountAnnouncements => "col24", + DBCol::NextBlockHashes => "col25", + DBCol::EpochLightClientBlocks => "col26", + DBCol::ReceiptIdToShardId => "col27", + DBCol::_NextBlockWithNewChunk => "col28", + DBCol::_LastBlockWithNewChunk => "col29", + DBCol::PeerComponent => "col30", + DBCol::ComponentEdges => "col31", + DBCol::LastComponentNonce => "col32", + DBCol::Transactions => "col33", + DBCol::_ChunkPerHeightShard => "col34", + DBCol::StateChanges => "col35", + DBCol::BlockRefCount => "col36", + DBCol::TrieChanges => "col37", + DBCol::BlockMerkleTree => "col38", + DBCol::ChunkHashesByHeight => "col39", + DBCol::BlockOrdinal => "col40", + DBCol::_GCCount => "col41", + DBCol::OutcomeIds => "col42", + DBCol::_TransactionRefCount => "col43", + DBCol::ProcessedBlockHeights => "col44", + DBCol::Receipts => "col45", + DBCol::CachedContractCode => "col46", + DBCol::EpochValidatorInfo => "col47", + DBCol::HeaderHashesByHeight => "col48", + DBCol::StateChangesForSplitStates => "col49", + // If you’re adding a new column, do *not* create a new case for it. + // All new columns are handled by this default case: + #[allow(unreachable_patterns)] + _ => <&str>::from(col), + } +} + +#[cfg(test)] +mod tests { + use crate::db::{Database, StatsValue}; + use crate::{DBCol, NodeStorage, StoreStatistics}; + use assert_matches::assert_matches; + + use super::*; + + #[test] + fn rocksdb_merge_sanity() { + let (_tmp_dir, opener) = NodeStorage::test_opener(); + let store = opener.open().unwrap().get_hot_store(); + let ptr = (&*store.storage) as *const (dyn Database + 'static); + let rocksdb = unsafe { &*(ptr as *const RocksDB) }; + assert_eq!(store.get(DBCol::State, &[1]).unwrap(), None); + { + let mut store_update = store.store_update(); + store_update.increment_refcount(DBCol::State, &[1], &[1]); + store_update.commit().unwrap(); + } + { + let mut store_update = store.store_update(); + store_update.increment_refcount(DBCol::State, &[1], &[1]); + store_update.commit().unwrap(); + } + assert_eq!(store.get(DBCol::State, &[1]).unwrap().as_deref(), Some(&[1][..])); + assert_eq!( + rocksdb.get_raw_bytes(DBCol::State, &[1]).unwrap().as_deref(), + Some(&[1, 2, 0, 0, 0, 0, 0, 0, 0][..]) + ); + { + let mut store_update = store.store_update(); + store_update.decrement_refcount(DBCol::State, &[1]); + store_update.commit().unwrap(); + } + assert_eq!(store.get(DBCol::State, &[1]).unwrap().as_deref(), Some(&[1][..])); + assert_eq!( + rocksdb.get_raw_bytes(DBCol::State, &[1]).unwrap().as_deref(), + Some(&[1, 1, 0, 0, 0, 0, 0, 0, 0][..]) + ); + { + let mut store_update = store.store_update(); + store_update.decrement_refcount(DBCol::State, &[1]); + store_update.commit().unwrap(); + } + // Refcount goes to 0 -> get() returns None + assert_eq!(store.get(DBCol::State, &[1]).unwrap(), None); + // Internally there is an empty value + assert_eq!(rocksdb.get_raw_bytes(DBCol::State, &[1]).unwrap().as_deref(), Some(&[][..])); + + // single_thread_rocksdb makes compact hang forever + if !cfg!(feature = "single_thread_rocksdb") { + let none = Option::<&[u8]>::None; + let cf = rocksdb.cf_handle(DBCol::State).unwrap(); + + // I’m not sure why but we need to run compaction twice. If we run + // it only once, we end up with an empty value for the key. This is + // surprising because I assumed that compaction filter would discard + // empty values. + rocksdb.db.compact_range_cf(cf, none, none); + assert_eq!( + rocksdb.get_raw_bytes(DBCol::State, &[1]).unwrap().as_deref(), + Some(&[][..]) + ); + assert_eq!(store.get(DBCol::State, &[1]).unwrap(), None); + + rocksdb.db.compact_range_cf(cf, none, none); + assert_eq!(rocksdb.get_raw_bytes(DBCol::State, &[1]).unwrap(), None); + assert_eq!(store.get(DBCol::State, &[1]).unwrap(), None); + } + } + + #[test] + fn test_parse_statistics() { + let statistics = "rocksdb.cold.file.read.count COUNT : 999\n\ + rocksdb.db.get.micros P50 : 9.171086 P95 : 222.678751 P99 : 549.611652 P100 : 45816.000000 COUNT : 917578 SUM : 38313754"; + let mut result = StoreStatistics { data: vec![] }; + let parse_result = parse_statistics(statistics, &mut result); + // We should be able to parse stats and the result should be Ok(()). + parse_result.unwrap(); + assert_eq!( + result, + StoreStatistics { + data: vec![ + ("rocksdb.cold.file.read.count".to_string(), vec![StatsValue::Count(999)]), + ( + "rocksdb.db.get.micros".to_string(), + vec![ + StatsValue::Percentile(50, 9.171086), + StatsValue::Percentile(95, 222.678751), + StatsValue::Percentile(99, 549.611652), + StatsValue::Percentile(100, 45816.0), + StatsValue::Count(917578), + StatsValue::Sum(38313754) + ] + ) + ] + } + ); + } + + #[test] + fn test_delete_range() { + let store = NodeStorage::test_opener().1.open().unwrap().get_hot_store(); + let keys = [vec![0], vec![1], vec![2], vec![3]]; + let column = DBCol::Block; + + let mut store_update = store.store_update(); + for key in &keys { + store_update.insert(column, key.clone(), vec![42]); + } + store_update.commit().unwrap(); + + let mut store_update = store.store_update(); + store_update.delete_range(column, &keys[1], &keys[3]); + store_update.commit().unwrap(); + + assert_matches!(store.exists(column, &keys[0]), Ok(true)); + assert_matches!(store.exists(column, &keys[1]), Ok(false)); + assert_matches!(store.exists(column, &keys[2]), Ok(false)); + assert_matches!(store.exists(column, &keys[3]), Ok(true)); + } +} diff --git a/core/store/src/db/rocksdb/instance_tracker.rs b/core/store/src/db/rocksdb/instance_tracker.rs new file mode 100644 index 000000000..31d188802 --- /dev/null +++ b/core/store/src/db/rocksdb/instance_tracker.rs @@ -0,0 +1,262 @@ +use std::sync::{Condvar, Mutex}; +use tracing::info; + +/// Describes number of RocksDB instances and sum of their max_open_files. +/// +/// This is modified by [`InstanceTracker`] which uses RAII to automatically +/// update the trackers when new RocksDB instances are opened and closed. +#[derive(Default)] +struct Inner { + /// Number of RocksDB instances open. + count: usize, + + /// Sum of max_open_files configuration option of all the RocksDB instances. + /// + /// This is used when opening multiple instances to guarantee that process + /// limit is high enough to allow all of the instances to open the maximum + /// number of files allowed in their respective configurations. + /// + /// For example, if NOFILE limit is set to 1024 and we try to open two + /// RocksDB instances with max_open_files set to 512 the second open should + /// fail since the limit is too low to accommodate both databases and other + /// file descriptor node is opening. + /// + /// Note that there is a bit of shenanigans around types. We’re using `u64` + /// here but `u32` in [`crate::config::StoreConfig`]. The latter uses `u32` + /// because we don’t want to deal with `-1` having special meaning in + /// RocksDB so we opted to for unsigned type but then needed to limit it + /// since RocksDB doesn’t accept full unsigned range. Here, we’re using + /// `u64` because that’s what ends up being passed to setrlimit so we would + /// need to cast anyway and it allows us not to worry about summing multiple + /// limits from StoreConfig. + max_open_files: u64, +} + +/// Synchronisation wrapper for accessing [`Inner`] singleton. +struct State { + inner: Mutex, + zero_cvar: Condvar, +} + +impl State { + /// Creates a new instance with counts initialised to zero. + const fn new() -> Self { + let inner = Inner { count: 0, max_open_files: 0 }; + Self { inner: Mutex::new(inner), zero_cvar: Condvar::new() } + } + + /// Registers a new RocksDB instance and checks if limits are enough for + /// given max_open_files configuration option. + /// + /// Returns an error if process’ resource limits are too low to allow + /// max_open_files open file descriptors for the new database instance. + fn try_new_instance(&self, max_open_files: u32) -> Result<(), String> { + let num_instances = { + let mut inner = self.inner.lock().unwrap(); + let max_open_files = inner.max_open_files + u64::from(max_open_files); + ensure_max_open_files_limit(RealNoFile, max_open_files)?; + inner.max_open_files = max_open_files; + inner.count += 1; + inner.count + }; + info!(target: "db", num_instances, "Opened a new RocksDB instance."); + Ok(()) + } + + fn close_instance(&self, max_open_files: u32) { + let num_instances = { + let mut inner = self.inner.lock().unwrap(); + inner.max_open_files = inner.max_open_files.saturating_sub(max_open_files.into()); + inner.count = inner.count.saturating_sub(1); + if inner.count == 0 { + self.zero_cvar.notify_all(); + } + inner.count + }; + info!(target: "db", num_instances, "Closed a RocksDB instance."); + } + + /// Blocks until all RocksDB instances (usually 0 or 1) shut down. + fn block_until_all_instances_are_closed(&self) { + let mut inner = self.inner.lock().unwrap(); + while inner.count != 0 { + info!(target: "db", num_instances=inner.count, + "Waiting for remaining RocksDB instances to close"); + inner = self.zero_cvar.wait(inner).unwrap(); + } + info!(target: "db", "All RocksDB instances closed."); + } +} + +/// The [`State`] singleton tracking all opened RocksDB instances. +static STATE: State = State::new(); + +/// Blocks until all RocksDB instances (usually 0 or 1) shut down. +pub(super) fn block_until_all_instances_are_closed() { + STATE.block_until_all_instances_are_closed(); +} + +/// RAII style object which updates the instance tracker stats. +/// +/// We’ve seen problems with RocksDB corruptions. InstanceTracker lets us +/// gracefully shutdown the process letting RocksDB to finish all operations and +/// leaving the instances in a valid non-corrupted state. +pub(super) struct InstanceTracker { + /// max_open_files configuration of given RocksDB instance. + max_open_files: u32, +} + +impl InstanceTracker { + /// Registers a new RocksDB instance and checks if limits are enough for + /// given max_open_files configuration option. + /// + /// Returns an error if process’ resource limits are too low to allow + /// max_open_files open file descriptors for the new database instance. On + /// success max_open_files descriptors are ‘reserved’ for this instance so + /// that creating more instances will take into account the sum of all the + /// max_open_files options. + /// + /// The instance is unregistered once this object is dropped. + pub(super) fn try_new(max_open_files: u32) -> Result { + STATE.try_new_instance(max_open_files)?; + Ok(Self { max_open_files }) + } +} + +impl Drop for InstanceTracker { + /// Deregisters a RocksDB instance and frees its reserved NOFILE limit. + /// + /// Returns an error if process’ resource limits are too low to allow + /// max_open_files open file descriptors for the new database instance. + /// + /// The instance is unregistered once this object is dropped. + fn drop(&mut self) { + STATE.close_instance(self.max_open_files); + } +} + +/// Ensures that NOFILE limit can accommodate `max_open_files` plus some small +/// margin of file descriptors. +/// +/// A RocksDB instance can keep up to the configured `max_open_files` number of +/// file descriptors. In addition, we need handful more for other processing +/// (such as network sockets to name just one example). If NOFILE is too small +/// opening files may start failing which would prevent us from operating +/// correctly. +/// +/// To avoid such failures, this method ensures that NOFILE limit is large +/// enough to accommodate `max_open_file` plus another 1000 file descriptors. +/// If current limit is too low, it will attempt to set it to a higher value. +/// +/// Returns error if NOFILE limit could not be read or set. In practice the +/// only thing that can happen is hard limit being too low such that soft limit +/// cannot be increased to required value. +fn ensure_max_open_files_limit(mut nofile: impl NoFile, max_open_files: u64) -> Result<(), String> { + let required = max_open_files.saturating_add(1000); + if required > i64::MAX as u64 { + return Err(format!( + "Unable to change limit for the number of open files (NOFILE): \ + Required limit of {required} is too high" + )); + }; + let (soft, hard) = nofile.get().map_err(|err| { + format!("Unable to get limit for the number of open files (NOFILE): {err}") + })?; + if required <= soft { + Ok(()) + } else if required <= hard { + nofile.set(required, hard).map_err(|err| { + format!( + "Unable to change limit for the number of open files (NOFILE) \ + from ({soft}, {hard}) to ({required}, {hard}): {err}" + ) + }) + } else { + Err(format!( + "Hard limit for the number of open files (NOFILE) is too low \ + ({hard}). At least {required} is required. Set ‘ulimit -Hn’ \ + accordingly and restart the node." + )) + } +} + +/// Interface for accessing the NOFILE resource limit. +/// +/// The essentially trait exists for testing only. It allows +/// [`ensure_max_open_files_limit`] to be parameterised such +/// that it access mock limits rather than real ones. +trait NoFile { + fn get(&self) -> std::io::Result<(u64, u64)>; + fn set(&mut self, soft: u64, hard: u64) -> std::io::Result<()>; +} + +/// Access to the real process NOFILE resource limit. +struct RealNoFile; + +impl NoFile for RealNoFile { + fn get(&self) -> std::io::Result<(u64, u64)> { + rlimit::Resource::NOFILE.get() + } + + fn set(&mut self, soft: u64, hard: u64) -> std::io::Result<()> { + rlimit::Resource::NOFILE.set(soft, hard) + } +} + +#[test] +fn test_ensure_max_open_files_limit() { + /// Mock implementation of NoFile interface. + struct MockNoFile<'a>(&'a mut (u64, u64)); + + impl<'a> NoFile for MockNoFile<'a> { + fn get(&self) -> std::io::Result<(u64, u64)> { + if self.0 .0 == 666 { + Err(std::io::ErrorKind::Other.into()) + } else { + Ok(*self.0) + } + } + + fn set(&mut self, soft: u64, hard: u64) -> std::io::Result<()> { + let (old_soft, old_hard) = self.get().unwrap(); + if old_hard == 666000 { + Err(std::io::ErrorKind::Other.into()) + } else { + assert!(soft != old_soft, "Pointless call to set"); + *self.0 = (soft, hard); + Ok(()) + } + } + } + + // We impose limit at i64::MAX and don’t even talk to the system if + // number above that is requested. + let msg = ensure_max_open_files_limit(MockNoFile(&mut (666, 666)), u64::MAX).unwrap_err(); + assert!(msg.contains("is too high"), "{msg}"); + + // Error on get + let msg = ensure_max_open_files_limit(MockNoFile(&mut (666, 666)), 1024).unwrap_err(); + assert!(msg.starts_with("Unable to get"), "{msg}"); + + // Everything’s fine, soft limit should stay as it is. + let mut state = (2048, 666000); + ensure_max_open_files_limit(MockNoFile(&mut state), 1024).unwrap(); + assert_eq!((2048, 666000), state); + + // Should rise the soft limit. + let mut state = (1024, 10000); + ensure_max_open_files_limit(MockNoFile(&mut state), 1024).unwrap(); + assert_eq!((2024, 10000), state); + + // Should recognise trying to rise is futile because hard limit is too low. + let mut state = (1024, 2000); + let msg = ensure_max_open_files_limit(MockNoFile(&mut state), 1024).unwrap_err(); + assert!(msg.starts_with("Hard limit"), "{msg}"); + assert_eq!((1024, 2000), state); + + // Error trying to change the limit. + let mut state = (1024, 666000); + let msg = ensure_max_open_files_limit(MockNoFile(&mut state), 1024).unwrap_err(); + assert!(msg.starts_with("Unable to change"), "{msg}"); + assert_eq!((1024, 666000), state); +} diff --git a/core/store/src/db/rocksdb/snapshot.rs b/core/store/src/db/rocksdb/snapshot.rs new file mode 100644 index 000000000..76be0806b --- /dev/null +++ b/core/store/src/db/rocksdb/snapshot.rs @@ -0,0 +1,206 @@ +use std::io; + +use ::rocksdb::checkpoint::Checkpoint; + +use crate::Temperature; + +/// Representation of a RocksDB checkpoint. +/// +/// Serves as kind of RAII type which logs information about the checkpoint when +/// object is dropped if the checkpoint hasn’t been removed beforehand by +/// [`Snapshot::remove`] method. +/// +/// The usage of the type is to create a checkpoint before a risky database +/// operation and then [`Snapshot::remove`] it when the operation finishes +/// successfully. In that scenario, the checkpoint will be gracefully deleted +/// from the file system. On the other hand, if the operation fails an the +/// checkpoint is not deleted, this type’s Drop implementation will log +/// informational messages pointing where the snapshot resides and how to +/// recover data from it. +#[derive(Debug)] +pub struct Snapshot(pub Option); + +/// Possible errors when creating a checkpoint. +#[derive(Debug)] +pub enum SnapshotError { + /// Snapshot at requested location already exists. + /// + /// More specifically, the specified path exists (since the code does only + /// rudimentary check whether the path is a checkpoint or not). + AlreadyExists(std::path::PathBuf), + + /// Error while creating a snapshot. + IOError(io::Error), +} + +/// Possible errors when removing a checkpoint. +#[derive(Debug)] +pub struct SnapshotRemoveError { + /// Path to the snapshot. + pub path: std::path::PathBuf, + + /// Error that caused the failure. + pub error: io::Error, +} + +impl std::convert::From for SnapshotError { + fn from(err: io::Error) -> Self { + Self::IOError(err) + } +} + +impl std::convert::From<::rocksdb::Error> for SnapshotError { + fn from(err: ::rocksdb::Error) -> Self { + io::Error::other(err).into() + } +} + +impl Snapshot { + /// Creates a Snapshot object which represents lack of a snapshot. + pub const fn none() -> Self { + Self(None) + } + + /// Possibly creates a new snapshot for given database. + /// + /// If the snapshot is disabled via `config.migration_snapshot` option, + /// a ‘no snapshot’ object is returned. It can be thought as `None` but + /// `remove` method can be called on it so it’s tiny bit more ergonomic. + /// + /// Otherwise, path to the snapshot is determined from `config` taking as + /// `db_path` as the base directory for relative paths. If the snapshot + /// already exists, the function returns [`SnapshotError::AlreadyExists`] + /// error. (More specifically, if the path already exists since the method + /// does not make any more sophisticated checks whether the path contains + /// a snapshot or not). + /// + /// `temp` specifies whether the database is cold or hot which affects + /// whether refcount merge operator is configured on reference counted + /// column. + pub fn new( + db_path: &std::path::Path, + config: &crate::StoreConfig, + temp: Temperature, + ) -> Result { + let snapshot_path = match config.migration_snapshot.get_path(db_path) { + Some(snapshot_path) => snapshot_path, + None => return Ok(Self::none()), + }; + + tracing::info!(target: "db", snapshot_path=%snapshot_path.display(), + "Creating database snapshot"); + if snapshot_path.exists() { + return Err(SnapshotError::AlreadyExists(snapshot_path)); + } + + let db = super::RocksDB::open(db_path, config, crate::Mode::ReadWriteExisting, temp)?; + let cp = Checkpoint::new(&db.db).map_err(io::Error::other)?; + cp.create_checkpoint(&snapshot_path)?; + + Ok(Self(Some(snapshot_path))) + } + + /// Deletes the checkpoint from the file system. + /// + /// Does nothing if the object has been created via [`Self::none`]. + pub fn remove(mut self) -> Result<(), SnapshotRemoveError> { + if let Some(path) = self.0.take() { + tracing::info!(target: "db", snapshot_path=%path.display(), + "Deleting the database snapshot"); + std::fs::remove_dir_all(&path).map_err(|error| SnapshotRemoveError { path, error }) + } else { + Ok(()) + } + } +} + +impl std::ops::Drop for Snapshot { + /// If the checkpoint hasn’t been deleted, log information about it. + /// + /// If the checkpoint hasn’t been deleted with [`Self::remove`] method, this + /// will log information about where the checkpoint resides in the file + /// system and how to recover data from it. + fn drop(&mut self) { + if let Some(path) = &self.0 { + tracing::info!(target: "db", snapshot_path=%path.display(), + "In case of issues, the database can be recovered \ + from the database snapshot"); + tracing::info!(target: "db", snapshot_path=%path.display(), + "To recover from the snapshot, delete files in the \ + database directory and replace them with contents \ + of the snapshot directory"); + } + } +} + +#[test] +fn test_snapshot_creation() { + use assert_matches::assert_matches; + + let (_tmpdir, opener) = crate::NodeStorage::test_opener(); + let new = || Snapshot::new(&opener.path(), &opener.config(), Temperature::Hot); + + // Creating snapshot fails if database doesn’t exist. + let err = format!("{:?}", new().unwrap_err()); + assert!(err.contains("create_if_missing is false"), "{err:?}"); + + // Create the database + core::mem::drop(opener.open().unwrap()); + + // Creating snapshot should work now. + let snapshot = new().unwrap(); + + // Snapshot already exists so cannot create a new one. + assert_matches!(new().unwrap_err(), SnapshotError::AlreadyExists(_)); + + snapshot.remove().unwrap(); + + // This should work correctly again since the snapshot has been removed. + core::mem::drop(new().unwrap()); + + // And this again should fail. We don’t remove the snapshot in + // Snapshot::drop. + assert_matches!(new().unwrap_err(), SnapshotError::AlreadyExists(_)); +} + +/// Tests that reading data from a snapshot is possible. +#[test] +fn test_snapshot_recovery() { + const KEY: &[u8] = b"key"; + const COL: crate::DBCol = crate::DBCol::BlockMisc; + + let (tmpdir, opener) = crate::NodeStorage::test_opener(); + + // Populate some data + { + let store = opener.open().unwrap().get_hot_store(); + let mut update = store.store_update(); + update.set_raw_bytes(COL, KEY, b"value"); + update.commit().unwrap(); + } + + // Create snapshot + let snapshot = Snapshot::new(&opener.path(), &opener.config(), Temperature::Hot).unwrap(); + let path = snapshot.0.clone().unwrap(); + + // Delete the data from the database. + { + let store = opener.open().unwrap().get_hot_store(); + let mut update = store.store_update(); + update.delete(COL, KEY); + update.commit().unwrap(); + + assert_eq!(None, store.get(COL, KEY).unwrap()); + } + + // Open snapshot. Deleted data should be there. + { + let mut config = opener.config().clone(); + config.path = Some(path); + let opener = crate::NodeStorage::opener(tmpdir.path(), false, &config, None); + let store = opener.open().unwrap().get_hot_store(); + assert_eq!(Some(&b"value"[..]), store.get(COL, KEY).unwrap().as_deref()); + } + + snapshot.remove().unwrap(); +} diff --git a/core/store/src/db/slice.rs b/core/store/src/db/slice.rs new file mode 100644 index 000000000..e04d9ed56 --- /dev/null +++ b/core/store/src/db/slice.rs @@ -0,0 +1,144 @@ +use std::sync::Arc; + +use super::refcount; + +/// Data returned from the database. +/// +/// Abstraction layer for data returned by [`super::Database::get_raw_bytes`] +/// and [`super::Database::get_with_rc_stripped`] methods. Operating on the +/// value as a slice is free while converting it to a vector on an arc may +/// requires an allocation and memory copy. +/// +/// This is essentially a reference into a buffer owned by the database with an +/// RAII guard which notifies the database when the buffer can be freed. +pub struct DBSlice<'a>(Inner<'a>); + +enum Inner<'a> { + /// Data held as a vector. + Vec(Vec), + + /// Data held as RocksDB-specific pinnable slice. + Rocks(RocksSlice<'a>), +} + +impl<'a> DBSlice<'a> { + /// Returns slice view of the data. + pub fn as_slice(&self) -> &[u8] { + match self.0 { + Inner::Vec(ref bytes) => bytes.as_slice(), + Inner::Rocks(ref rocks) => rocks.as_slice(), + } + } + + /// Constructs the object from a vector. + /// + /// In the current implementation, this is a zero-copy operation. + pub(super) fn from_vec(vec: Vec) -> Self { + Self(Inner::Vec(vec)) + } + + /// Constructs the object from RocskDB pinnable slice representation. + /// + /// This is internal API for the [`crate::db::rocksdb::RocksDB`] + /// implementation of the database interface. + pub(super) fn from_rocksdb_slice(db_slice: ::rocksdb::DBPinnableSlice<'a>) -> Self { + DBSlice(Inner::Rocks(RocksSlice::new(db_slice))) + } + + /// Decodes and strips reference count from the data. + /// + /// Returns `None` if the reference count is non-positive (or the value was + /// empty). See [`refcount::decode_value_with_rc`] for more detail about + /// decoding the refcount. + pub(super) fn strip_refcount(self) -> Option { + match self.0 { + Inner::Vec(bytes) => { + refcount::strip_refcount(bytes).map(|bytes| Self(Inner::Vec(bytes))) + } + Inner::Rocks(rocks) => rocks.strip_refcount().map(|rocks| Self(Inner::Rocks(rocks))), + } + } +} + +/// A slice owned by the RocksDB with RAII mechanism for letting RocksDB know +/// when it can be freed. +struct RocksSlice<'a> { + /// Pointer at the bytes. + /// + /// The data is held by `db_slice` and as such this field must not + /// outlive `db_slice`. Nor can `db_slice` be modified. + /// + /// We keep a separate pointer because we want to be able to modify + /// truncate the value without having to allocate new buffers. + data: *const [u8], + + /// This is what owns the data. + /// + /// This is never modified and has a stable deref which is why we can + /// have `data` field pointing at the buffer this object holds. + db_slice: ::rocksdb::DBPinnableSlice<'a>, +} + +impl<'a> RocksSlice<'a> { + pub fn new(db_slice: ::rocksdb::DBPinnableSlice<'a>) -> Self { + let data = &*db_slice as *const [u8]; + Self { data, db_slice } + } + + fn strip_refcount(self) -> Option { + let data = refcount::decode_value_with_rc(self.as_slice()).0?; + Some(Self { data: data as *const _, db_slice: self.db_slice }) + } + + pub fn as_slice(&self) -> &[u8] { + // SAFETY: data references a pinned buffer owned by db_slice which has + // not been modified since we got the pointer. + unsafe { &*self.data } + } +} + +impl<'a> std::ops::Deref for DBSlice<'a> { + type Target = [u8]; + fn deref(&self) -> &[u8] { + self.as_slice() + } +} + +impl<'a> From> for Arc<[u8]> { + /// Converts `DBSlice` into a thread-safe reference counted slice. + /// + /// This may need to allocate and copy data. + fn from(slice: DBSlice<'a>) -> Self { + slice.as_slice().into() + } +} + +impl<'a> From> for Vec { + /// Converts `DBSlice` into a vector. + /// + /// This may need to allocate and copy data. + fn from(slice: DBSlice<'a>) -> Self { + match slice.0 { + Inner::Vec(bytes) => bytes, + Inner::Rocks(rocks) => rocks.as_slice().to_vec(), + } + } +} + +impl<'a> std::fmt::Debug for DBSlice<'a> { + fn fmt(&self, fmtr: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self.as_slice(), fmtr) + } +} + +impl<'a> std::cmp::PartialEq> for DBSlice<'a> { + fn eq(&self, other: &DBSlice<'a>) -> bool { + self.as_slice() == other.as_slice() + } +} + +impl<'a> std::cmp::PartialEq<[u8]> for DBSlice<'a> { + fn eq(&self, other: &[u8]) -> bool { + self.as_slice() == other + } +} diff --git a/core/store/src/db/splitdb.rs b/core/store/src/db/splitdb.rs new file mode 100644 index 000000000..c23845193 --- /dev/null +++ b/core/store/src/db/splitdb.rs @@ -0,0 +1,440 @@ +use itertools::{self, EitherOrBoth}; +use std::cmp::Ordering; +use std::io; +use std::sync::Arc; + +use unc_o11y::log_assert_fail; + +use crate::db::{DBIterator, DBIteratorItem, DBSlice, DBTransaction, Database, StoreStatistics}; +use crate::DBCol; + +/// A database that provides access to the hot and cold databases. +/// +/// For hot-only columns it always reads from the hot database only. For cold +/// columns it reads from hot first and if the value is present it returns it. +/// If the value is not present it reads from the cold database. +/// +/// The iter* methods return a merge iterator of hot and cold iterators. +/// +/// This database should be treated as read-only but it is not enforced because +/// even the view client writes to the database in order to update caches. +pub struct SplitDB { + hot: Arc, + cold: Arc, +} + +impl SplitDB { + pub fn new(hot: Arc, cold: Arc) -> Arc { + return Arc::new(SplitDB { hot, cold }); + } + + /// The cmp function for the DBIteratorItems. + /// + /// Note that this does not implement total ordering because there isn't a + /// clear way to compare errors to errors or errors to values. Instead it + /// implements total order on values but always compares the error on the + /// left as lesser. This isn't even partial order. It is fine for merging + /// lists but should not be used for anything more complex like sorting. + fn db_iter_item_cmp(a: &DBIteratorItem, b: &DBIteratorItem) -> Ordering { + match (a, b) { + // Always put errors first. + (Err(_), _) => Ordering::Less, + (_, Err(_)) => Ordering::Greater, + // When comparing two (key, value) paris only compare the keys. + // - values in hot and cold may differ in rc but they are still the same + // - values written to cold should be immutable anyway + // - values writted to cold should be final + (Ok((a_key, _)), Ok((b_key, _))) => Ord::cmp(a_key, b_key), + } + } + + /// Returns merge iterator for the given two DBIterators. The returned + /// iterator will contain unique and sorted items from both input iterators. + /// + /// All errors from both inputs will be returned. + fn merge_iter<'a>(a: DBIterator<'a>, b: DBIterator<'a>) -> DBIterator<'a> { + // Merge the two iterators using the cmp function. The result will be an + // iter of EitherOrBoth. + let iter = itertools::merge_join_by(a, b, Self::db_iter_item_cmp); + // Flatten the EitherOrBoth while discarding duplicates. Each input + // iterator should only contain unique and sorted items. If any item is + // present in both iterors before the merge, it will get mapped to a + // EitherOrBoth::Both in the merged iter. Pick either and discard the + // other - this will guarantee uniqueness of the resulting iterator. + let iter = iter.map(|item| match item { + EitherOrBoth::Both(item, _) => item, + EitherOrBoth::Left(item) => item, + EitherOrBoth::Right(item) => item, + }); + Box::new(iter) + } +} + +impl Database for SplitDB { + /// Returns raw bytes for given `key` ignoring any reference count decoding + /// if any. + /// + /// First tries to read the data from the hot db and returns it if found. + /// Then it tries to read the data from the cold db and returns the result. + fn get_raw_bytes(&self, col: DBCol, key: &[u8]) -> io::Result>> { + if let Some(hot_result) = self.hot.get_raw_bytes(col, key)? { + return Ok(Some(hot_result)); + } + if col.is_cold() { + return self.cold.get_raw_bytes(col, key); + } + Ok(None) + } + + /// Returns value for given `key` forcing a reference count decoding. + /// + /// **Panics** if the column is not reference counted. + /// + /// First tries to read the data from the hot db and returns it if found. + /// Then it tries to read the data from the cold db and returns the result. + fn get_with_rc_stripped(&self, col: DBCol, key: &[u8]) -> io::Result>> { + assert!(col.is_rc()); + + if let Some(hot_result) = self.hot.get_with_rc_stripped(col, key)? { + return Ok(Some(hot_result)); + } + if col.is_cold() { + return self.cold.get_with_rc_stripped(col, key); + } + Ok(None) + } + + /// Iterate over all items in given column in lexicographical order sorted + /// by the key. + /// + /// The returned iterator will iterate through items in both the cold store + /// and the hot store. The items will be deduplicated and sorted. + fn iter<'a>(&'a self, col: DBCol) -> DBIterator<'a> { + if !col.is_cold() { + return self.hot.iter(col); + } + + Self::merge_iter(self.hot.iter(col), self.cold.iter(col)) + } + + /// Iterate over items in given column, whose keys start with given prefix, + /// in lexicographical order sorted by the key. + /// + /// The returned iterator will iterate through items in both the cold store + /// and the hot store. The items will be unique and sorted. + fn iter_prefix<'a>(&'a self, col: DBCol, key_prefix: &'a [u8]) -> DBIterator<'a> { + if !col.is_cold() { + return self.hot.iter_prefix(col, key_prefix); + } + + return Self::merge_iter( + self.hot.iter_prefix(col, key_prefix), + self.cold.iter_prefix(col, key_prefix), + ); + } + + /// Iterate over items in given column whose keys are between [lower_bound, upper_bound) + /// + /// Upper_bound key is not included. + /// If lower_bound is None - the iterator starts from the first key. + /// If upper_bound is None - iterator continues to the last key. + /// + /// The returned iterator will iterate through items in both the cold store + /// and the hot store. The items will be unique and sorted. + fn iter_range<'a>( + &'a self, + col: DBCol, + lower_bound: Option<&[u8]>, + upper_bound: Option<&[u8]>, + ) -> DBIterator<'a> { + if !col.is_cold() { + return self.hot.iter_range(col, lower_bound, upper_bound); + } + + return Self::merge_iter( + self.hot.iter_range(col, lower_bound, upper_bound), + self.cold.iter_range(col, lower_bound, upper_bound), + ); + } + + /// Iterate over items in given column bypassing reference count decoding if + /// any. + /// + /// The returned iterator will iterate through items in both the cold store + /// and the hot store. The items will be unique and sorted. + fn iter_raw_bytes<'a>(&'a self, col: DBCol) -> DBIterator<'a> { + if !col.is_cold() { + return self.hot.iter_raw_bytes(col); + } + + return Self::merge_iter(self.hot.iter_raw_bytes(col), self.cold.iter_raw_bytes(col)); + } + + /// The split db, in principle, should be read only and only used in view client. + /// However the view client *does* write to the db in order to update cache. + /// Hence we need to allow writing to the split db but only write to the hot db. + fn write(&self, batch: DBTransaction) -> io::Result<()> { + self.hot.write(batch) + } + + fn flush(&self) -> io::Result<()> { + let msg = "flush is not allowed - the split storage is read only."; + log_assert_fail!("{}", msg); + self.hot.flush()?; + self.cold.flush()?; + Ok(()) + } + + fn compact(&self) -> io::Result<()> { + let msg = "compact is not allowed - the split storage is read only."; + log_assert_fail!("{}", msg); + self.hot.compact()?; + self.cold.compact()?; + Ok(()) + } + + fn get_store_statistics(&self) -> Option { + log_assert_fail!("get_store_statistics is not allowed - the split storage has two stores"); + None + } + + fn create_checkpoint(&self, _path: &std::path::Path) -> anyhow::Result<()> { + log_assert_fail!("create_checkpoint is not allowed - the split storage has two stores"); + Ok(()) + } +} + +#[cfg(test)] +mod test { + use itertools::Itertools; + + use super::*; + + use crate::db::{testdb::TestDB, ColdDB, DBOp, DBTransaction}; + + const FOO: &[u8] = b"FOO"; + const BAR: &[u8] = b"BAR"; + const BAZ: &[u8] = b"BAZ"; + const NOT_FOO: &[u8] = b"NOT_FOO"; + + const FOO_VALUE: &[u8] = b"FOO_VALUE"; + const BAR_VALUE: &[u8] = b"BAR_VALUE"; + const BAZ_VALUE: &[u8] = b"BAZ_VALUE"; + + fn create_hot() -> Arc { + TestDB::new() + } + + fn create_cold() -> Arc { + Arc::new(ColdDB::new(TestDB::new())) + } + + fn set(db: &Arc, col: DBCol, key: &[u8], value: &[u8]) -> () { + let op = DBOp::Set { col, key: key.to_vec(), value: value.to_vec() }; + db.write(DBTransaction { ops: vec![op] }).unwrap(); + } + + fn set_rc(db: &Arc, col: DBCol, key: &[u8], value: &[u8]) -> () { + const ONE: &[u8] = &1i64.to_le_bytes(); + let op = DBOp::UpdateRefcount { col, key: key.to_vec(), value: [&value, ONE].concat() }; + db.write(DBTransaction { ops: vec![op] }).unwrap(); + } + + fn bx(literal: &[u8; SIZE]) -> Box<[u8]> { + Box::new(*literal) + } + + fn append_rc(value: &[u8]) -> Box<[u8]> { + const ONE: &[u8] = &1i64.to_le_bytes(); + [&value, ONE].concat().into() + } + + #[test] + fn test_get_raw_bytes() { + let hot = create_hot(); + let cold = create_cold(); + let split = SplitDB::new(hot.clone(), cold.clone()); + + // Block is a nice column for testing because is is a cold column but + // cold doesn't do anything funny to it. + let col = DBCol::Block; + + // Test 1: Write two different values to the hot db and to the cold db + // and verify we can read the hot value from the split db. + let key = FOO; + set(&hot, col, key, FOO); + set(&cold, col, key, NOT_FOO); + + let value = split.get_raw_bytes(col, key).unwrap(); + assert_eq!(value.as_deref(), Some(FOO)); + + // Test 2: Write to the cold db only and verify that we can read the + // value from the split db. + let key = BAR; + set(&cold, col, key, BAR); + + let value = split.get_raw_bytes(col, key).unwrap(); + assert_eq!(value.as_deref(), Some(BAR)); + + // Test 3: Try reading from a non-cold column and verify it returns None + // even if the value is set in the cold db. + let col = DBCol::BlockHeader; + let key = BAZ; + + set(&cold, col, key, BAZ); + let value = split.get_raw_bytes(col, key).unwrap(); + assert_eq!(value, None); + } + + #[test] + fn test_get_with_rc_stripped() { + let hot = create_hot(); + let cold = create_cold(); + let split = SplitDB::new(hot.clone(), cold.clone()); + + let col = DBCol::Transactions; + + // Test 1: Write two different values to the hot db and to the cold db + // and verify we can read the hot value from the split db. + let key = FOO; + set_rc(&hot, col, key, FOO); + set_rc(&cold, col, key, NOT_FOO); + + let value = split.get_with_rc_stripped(col, key).unwrap(); + assert_eq!(value.as_deref(), Some(FOO)); + + // Test 2: Write to the cold db only and verify that we can read the + // value from the split db. + let key = BAR; + set_rc(&cold, col, key, BAR); + + let value = split.get_with_rc_stripped(col, key).unwrap(); + assert_eq!(value.as_deref(), Some(BAR)); + + // Test 3: nothing, there aren't any non-cold reference counted columns. + } + + #[test] + fn test_iter() { + let hot = create_hot(); + let cold = create_cold(); + let split = SplitDB::new(hot.clone(), cold.clone()); + + let col = DBCol::Transactions; + + // Set values so that hot has foo and bar and cold has foo and baz. + + set_rc(&hot, col, FOO, FOO_VALUE); + set_rc(&hot, col, BAR, BAR_VALUE); + + set_rc(&cold, col, FOO, FOO_VALUE); + set_rc(&cold, col, BAZ, BAZ_VALUE); + + // Check that the resulting iterator is sorted and unique. + + let iter = split.iter(col); + let iter = iter.map(|item| item.unwrap()); + let result = iter.collect_vec(); + let expected_result: Vec<(Box<[u8]>, Box<[u8]>)> = vec![ + (BAR.into(), BAR_VALUE.into()), + (BAZ.into(), BAZ_VALUE.into()), + (FOO.into(), FOO_VALUE.into()), + ]; + assert_eq!(result, expected_result); + } + + #[test] + fn test_iter_raw_bytes() { + let hot = create_hot(); + let cold = create_cold(); + let split = SplitDB::new(hot.clone(), cold.clone()); + + let col = DBCol::Transactions; + + // Set values so that hot has foo and bar and cold has foo and baz. + + set_rc(&hot, col, FOO, FOO_VALUE); + set_rc(&hot, col, BAR, BAR_VALUE); + + set_rc(&cold, col, FOO, FOO_VALUE); + set_rc(&cold, col, BAZ, BAZ_VALUE); + + let iter = split.iter_raw_bytes(col); + let iter = iter.map(|item| item.unwrap()); + let result = iter.collect_vec(); + let expected_result: Vec<(Box<[u8]>, Box<[u8]>)> = vec![ + (BAR.into(), append_rc(BAR_VALUE)), + (BAZ.into(), append_rc(BAZ_VALUE)), + (FOO.into(), append_rc(FOO_VALUE)), + ]; + assert_eq!(result, expected_result); + } + + #[test] + fn test_iter_prefix() { + let hot = create_hot(); + let cold = create_cold(); + let split = SplitDB::new(hot.clone(), cold.clone()); + + let col = DBCol::Transactions; + + // Set values so that hot has foo and bar and cold has foo and baz. + + set_rc(&hot, col, FOO, FOO_VALUE); + set_rc(&hot, col, BAR, BAR_VALUE); + + set_rc(&cold, col, FOO, FOO_VALUE); + set_rc(&cold, col, BAZ, BAZ_VALUE); + + // Check that the resulting iterator contains only keys starting with + // the prefix. + + let key_prefix = b"BA"; + let iter = split.iter_prefix(col, key_prefix); + let iter = iter.map(|item| item.unwrap()); + let result = iter.collect_vec(); + let expected_result: Vec<(Box<[u8]>, Box<[u8]>)> = + vec![(BAR.into(), BAR_VALUE.into()), (BAZ.into(), BAZ_VALUE.into())]; + assert_eq!(result, expected_result); + } + + #[test] + fn test_iter_range() { + let hot = create_hot(); + let cold = create_cold(); + let split = SplitDB::new(hot.clone(), cold.clone()); + + let col = DBCol::Transactions; + + // Set values so that hot has the lower FOOs and cold has the higher + // FOOs, with some overlap. + + set_rc(&hot, col, b"FOO1", b"FOO1_VALUE"); + set_rc(&hot, col, b"FOO2", b"FOO2_VALUE"); + set_rc(&hot, col, b"FOO3", b"FOO3_VALUE"); + set_rc(&hot, col, b"FOO4", b"FOO4_VALUE"); + set_rc(&hot, col, b"FOO5", b"FOO5_VALUE"); + + set_rc(&cold, col, b"FOO4", b"FOO4_VALUE"); + set_rc(&cold, col, b"FOO5", b"FOO5_VALUE"); + set_rc(&cold, col, b"FOO6", b"FOO6_VALUE"); + set_rc(&cold, col, b"FOO7", b"FOO7_VALUE"); + + // Check that the resulting iterator contains only keys starting with + // the prefix from both hot and cold. + let lower_bound = Some(b"FOO2".as_slice()); + let upper_bound = Some(b"FOO7".as_slice()); + + let iter = split.iter_range(col, lower_bound, upper_bound); + let iter = iter.map(|item| item.unwrap()); + let result = iter.collect_vec(); + let expected_result: Vec<(Box<[u8]>, Box<[u8]>)> = vec![ + (bx(b"FOO2"), bx(b"FOO2_VALUE")), + (bx(b"FOO3"), bx(b"FOO3_VALUE")), + (bx(b"FOO4"), bx(b"FOO4_VALUE")), + (bx(b"FOO5"), bx(b"FOO5_VALUE")), + (bx(b"FOO6"), bx(b"FOO6_VALUE")), + ]; + assert_eq!(result, expected_result); + } +} diff --git a/core/store/src/db/testdb.rs b/core/store/src/db/testdb.rs new file mode 100644 index 000000000..635c8f503 --- /dev/null +++ b/core/store/src/db/testdb.rs @@ -0,0 +1,134 @@ +use std::collections::BTreeMap; +use std::io; +use std::ops::Bound; +use std::sync::{Arc, RwLock}; + +use crate::db::{refcount, DBIterator, DBOp, DBSlice, DBTransaction, Database}; +use crate::{DBCol, StoreStatistics}; + +/// An in-memory database intended for tests and IO-agnostic estimations. +#[derive(Default)] +pub struct TestDB { + // In order to ensure determinism when iterating over column's results + // a BTreeMap is used since it is an ordered map. A HashMap would + // give the aforementioned guarantee, and therefore is discarded. + db: RwLock, Vec>>>, + + // The store statistics. Can be set with the set_store_statistics. + // The TestDB doesn't produce any stats on its own, it's up to the user of + // this class to set the stats as they need it. + stats: RwLock>, +} + +impl TestDB { + pub fn new() -> Arc { + Arc::new(Self::default()) + } +} + +impl TestDB { + pub fn set_store_statistics(&self, stats: StoreStatistics) { + *self.stats.write().unwrap() = Some(stats); + } +} + +impl Database for TestDB { + fn get_raw_bytes(&self, col: DBCol, key: &[u8]) -> io::Result>> { + Ok(self.db.read().unwrap()[col].get(key).cloned().map(DBSlice::from_vec)) + } + + fn iter<'a>(&'a self, col: DBCol) -> DBIterator<'a> { + let iterator = self.iter_raw_bytes(col); + refcount::iter_with_rc_logic(col, iterator) + } + + fn iter_raw_bytes<'a>(&'a self, col: DBCol) -> DBIterator<'a> { + let iterator = self.db.read().unwrap()[col] + .clone() + .into_iter() + .map(|(k, v)| Ok((k.into_boxed_slice(), v.into_boxed_slice()))); + Box::new(iterator) + } + + fn iter_prefix<'a>(&'a self, col: DBCol, key_prefix: &'a [u8]) -> DBIterator<'a> { + let iterator = self.db.read().unwrap()[col] + .range(key_prefix.to_vec()..) + .take_while(move |(k, _)| k.starts_with(&key_prefix)) + .map(|(k, v)| Ok((k.clone().into_boxed_slice(), v.clone().into_boxed_slice()))) + .collect::>>(); + refcount::iter_with_rc_logic(col, iterator.into_iter()) + } + + fn iter_range<'a>( + &'a self, + col: DBCol, + lower_bound: Option<&[u8]>, + upper_bound: Option<&[u8]>, + ) -> DBIterator<'a> { + let lower = lower_bound.map_or(Bound::Unbounded, |f| Bound::Included(f.to_vec())); + let upper = upper_bound.map_or(Bound::Unbounded, |f| Bound::Excluded(f.to_vec())); + + let iterator = self.db.read().unwrap()[col] + .range((lower, upper)) + .map(|(k, v)| Ok((k.clone().into_boxed_slice(), v.clone().into_boxed_slice()))) + .collect::>>(); + refcount::iter_with_rc_logic(col, iterator.into_iter()) + } + + fn write(&self, transaction: DBTransaction) -> io::Result<()> { + let mut db = self.db.write().unwrap(); + for op in transaction.ops { + match op { + DBOp::Set { col, key, value } => { + db[col].insert(key, value); + } + DBOp::Insert { col, key, value } => { + if cfg!(debug_assertions) { + if let Some(old_value) = db[col].get(&key) { + super::assert_no_overwrite(col, &key, &value, &*old_value) + } + } + db[col].insert(key, value); + } + DBOp::UpdateRefcount { col, key, value } => { + let existing = db[col].get(&key).map(Vec::as_slice); + let operands = [value.as_slice()]; + let merged = refcount::refcount_merge(existing, operands); + if merged.is_empty() { + db[col].remove(&key); + } else { + debug_assert!( + refcount::decode_value_with_rc(&merged).1 > 0, + "Inserting value with non-positive refcount" + ); + db[col].insert(key, merged); + } + } + DBOp::Delete { col, key } => { + db[col].remove(&key); + } + DBOp::DeleteAll { col } => db[col].clear(), + DBOp::DeleteRange { col, from, to } => { + db[col].retain(|key, _| !(&from..&to).contains(&key)); + } + }; + } + Ok(()) + } + + fn flush(&self) -> io::Result<()> { + Ok(()) + } + + fn compact(&self) -> io::Result<()> { + Ok(()) + } + + fn get_store_statistics(&self) -> Option { + self.stats.read().unwrap().clone() + } + + fn create_checkpoint(&self, _path: &std::path::Path) -> anyhow::Result<()> { + Ok(()) + } +} diff --git a/core/store/src/flat/chunk_view.rs b/core/store/src/flat/chunk_view.rs new file mode 100644 index 000000000..ea8886435 --- /dev/null +++ b/core/store/src/flat/chunk_view.rs @@ -0,0 +1,63 @@ +use crate::flat::store_helper; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::state::FlatStateValue; + +use crate::Store; + +use super::types::FlatStateIterator; +use super::FlatStorage; + +/// Struct for getting value references from the flat storage, corresponding +/// to some block defined in `blocks_to_head`. +/// +/// The main interface is the `get_ref` method, which is called in `Trie::get` +/// and `Trie::get_ref` because they are the same for each shard and they are +/// requested only once during applying chunk. +// TODO (#7327): lock flat state when `get_ref` is called or head is being updated. Otherwise, `apply_chunks` and +// `postprocess_block` parallel execution may corrupt the state. +#[derive(Clone)] +pub struct FlatStorageChunkView { + /// Used to access flat state stored at the head of flat storage. + /// It should store all trie keys and values/value refs for the state on top of + /// flat_storage.head, except for delayed receipt keys. + store: Store, + /// The block for which key-value pairs of its state will be retrieved. The flat state + /// will reflect the state AFTER the block is applied. + block_hash: CryptoHash, + /// Stores the state of the flat storage, for example, where the head is at and which + /// blocks' state are stored in flat storage. + flat_storage: FlatStorage, +} + +impl FlatStorageChunkView { + pub fn new(store: Store, block_hash: CryptoHash, flat_storage: FlatStorage) -> Self { + Self { store, block_hash, flat_storage } + } + /// Returns value reference using raw trie key, taken from the state + /// corresponding to `FlatStorageChunkView::block_hash`. + /// + /// To avoid duplication, we don't store values themselves in flat state, + /// they are stored in `DBCol::State`. Also the separation is done so we + /// could charge users for the value length before loading the value. + // TODO (#7327): consider inlining small values, so we could use only one db access. + pub fn get_value(&self, key: &[u8]) -> Result, crate::StorageError> { + self.flat_storage.get_value(&self.block_hash, key) + } + + pub fn iter_flat_state_entries<'a>( + &'a self, + from: Option<&[u8]>, + to: Option<&[u8]>, + ) -> FlatStateIterator<'a> { + store_helper::iter_flat_state_entries(self.flat_storage.shard_uid(), &self.store, from, to) + } + + pub fn get_head_hash(&self) -> CryptoHash { + self.flat_storage.get_head_hash() + } + + pub fn shard_uid(&self) -> ShardUId { + self.flat_storage.shard_uid() + } +} diff --git a/core/store/src/flat/delta.rs b/core/store/src/flat/delta.rs new file mode 100644 index 000000000..4fcade795 --- /dev/null +++ b/core/store/src/flat/delta.rs @@ -0,0 +1,277 @@ +use borsh::{BorshDeserialize, BorshSerialize}; + +use unc_primitives::hash::hash; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::state::{FlatStateValue, ValueRef}; +use unc_primitives::types::{BlockHeight, RawStateChangesWithTrieKey}; +use std::collections::HashMap; +use std::sync::Arc; + +use super::{store_helper, BlockInfo}; +use crate::{CryptoHash, StoreUpdate}; + +#[derive(Debug)] +pub struct FlatStateDelta { + pub metadata: FlatStateDeltaMetadata, + pub changes: FlatStateChanges, +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Copy, serde::Serialize)] +pub struct BlockWithChangesInfo { + pub(crate) hash: CryptoHash, + pub(crate) height: BlockHeight, +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Copy, serde::Serialize)] +pub struct FlatStateDeltaMetadata { + pub block: BlockInfo, + /// `None` if the block itself has flat state changes. + /// `Some` if the block has no flat state changes, and contains + /// info of the last block with some flat state changes. + pub prev_block_with_changes: Option, +} + +#[derive(BorshSerialize, BorshDeserialize, Debug)] +pub struct KeyForFlatStateDelta { + pub shard_uid: ShardUId, + pub block_hash: CryptoHash, +} + +impl KeyForFlatStateDelta { + pub fn to_bytes(&self) -> [u8; 40] { + let mut res = [0; 40]; + res[..8].copy_from_slice(&self.shard_uid.to_bytes()); + res[8..].copy_from_slice(self.block_hash.as_bytes()); + res + } +} +/// Delta of the state for some shard and block, stores mapping from keys to values +/// or None, if key was removed in this block. +#[derive(BorshSerialize, BorshDeserialize, Clone, Default, PartialEq, Eq)] +pub struct FlatStateChanges(pub HashMap, Option>); + +impl From for FlatStateChanges +where + T: IntoIterator, Option)>, +{ + fn from(iter: T) -> Self { + Self(HashMap::from_iter(iter)) + } +} + +impl std::fmt::Debug for FlatStateChanges { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FlatStateChanges") + .field("changes", &unc_fmt::Slice(&Vec::from_iter(self.0.iter()))) + .finish() + } +} + +impl FlatStateChanges { + /// Returns `Some(Option)` from delta for the given key. If key is not present, returns None. + pub fn get(&self, key: &[u8]) -> Option> { + self.0.get(key).cloned() + } + + /// Inserts a key-value pair to delta. + pub fn insert( + &mut self, + key: Vec, + value: Option, + ) -> Option> { + self.0.insert(key, value) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + /// Merge two deltas. Values from `other` should override values from `self`. + pub fn merge(&mut self, other: Self) { + self.0.extend(other.0.into_iter()) + } + + /// Creates delta using raw state changes for some block. + pub fn from_state_changes(changes: &[RawStateChangesWithTrieKey]) -> Self { + let mut delta = HashMap::new(); + for change in changes.iter() { + let key = change.trie_key.to_vec(); + // `RawStateChangesWithTrieKey` stores all sequential changes for a key within a chunk, so it is sufficient + // to take only the last change. + let last_change = &change + .changes + .last() + .expect("Committed entry should have at least one change") + .data; + let flat_state_value = last_change.as_ref().map(|value| FlatStateValue::on_disk(value)); + delta.insert(key, flat_state_value); + } + Self(delta) + } + + pub fn from_raw_key_value(entries: &[(Vec, Option>)]) -> Self { + let mut delta = HashMap::new(); + for (key, raw_value) in entries { + let flat_state_value = raw_value.as_ref().map(|value| FlatStateValue::on_disk(value)); + delta.insert(key.to_vec(), flat_state_value); + } + Self(delta) + } + + /// Applies delta to the flat state. + pub fn apply_to_flat_state(self, store_update: &mut StoreUpdate, shard_uid: ShardUId) { + for (key, value) in self.0.into_iter() { + store_helper::set_flat_state_value(store_update, shard_uid, key, value); + } + } +} + +/// `FlatStateChanges` which uses hash of raw `TrieKey`s instead of keys themselves. +/// Used to reduce memory used by deltas and serves read queries. +#[derive(Debug)] +pub struct CachedFlatStateChanges(HashMap>); + +#[derive(Debug)] +pub struct CachedFlatStateDelta { + pub metadata: FlatStateDeltaMetadata, + pub changes: Arc, +} + +impl From for CachedFlatStateChanges { + fn from(delta: FlatStateChanges) -> Self { + Self( + delta + .0 + .into_iter() + .map(|(key, value)| (hash(&key), value.map(|v| v.to_value_ref()))) + .collect(), + ) + } +} + +impl CachedFlatStateChanges { + /// Size of cache entry in bytes. + const ENTRY_SIZE: usize = + std::mem::size_of::() + std::mem::size_of::>(); + + /// Returns `Some(Option)` from delta for the given key. If key is not present, returns None. + pub(crate) fn get(&self, key: &[u8]) -> Option> { + self.0.get(&hash(key)).cloned() + } + + /// Returns number of all entries. + pub(crate) fn len(&self) -> usize { + self.0.len() + } + + /// Total size in bytes consumed by delta. May be changed if we implement inlining of `ValueRef`s. + pub(crate) fn total_size(&self) -> u64 { + (self.0.capacity() as u64) * (Self::ENTRY_SIZE as u64) + } +} + +#[cfg(test)] +mod tests { + use super::FlatStateChanges; + use unc_primitives::state::FlatStateValue; + use unc_primitives::trie_key::TrieKey; + use unc_primitives::types::{RawStateChange, RawStateChangesWithTrieKey, StateChangeCause}; + + /// Check correctness of creating `FlatStateChanges` from state changes. + #[test] + fn flat_state_changes_creation() { + let alice_trie_key = TrieKey::ContractCode { account_id: "alice".parse().unwrap() }; + let bob_trie_key = TrieKey::ContractCode { account_id: "bob".parse().unwrap() }; + let carol_trie_key = TrieKey::ContractCode { account_id: "carol".parse().unwrap() }; + let delayed_trie_key = TrieKey::DelayedReceiptIndices; + let delayed_receipt_trie_key = TrieKey::DelayedReceipt { index: 1 }; + + let state_changes = vec![ + RawStateChangesWithTrieKey { + trie_key: delayed_trie_key.clone(), + changes: vec![RawStateChange { + cause: StateChangeCause::InitialState, + data: Some(vec![1]), + }], + }, + RawStateChangesWithTrieKey { + trie_key: delayed_receipt_trie_key.clone(), + changes: vec![RawStateChange { + cause: StateChangeCause::InitialState, + data: Some(vec![2]), + }], + }, + RawStateChangesWithTrieKey { + trie_key: alice_trie_key.clone(), + changes: vec![ + RawStateChange { + cause: StateChangeCause::InitialState, + data: Some(vec![1, 2]), + }, + RawStateChange { + cause: StateChangeCause::ReceiptProcessing { + receipt_hash: Default::default(), + }, + data: Some(vec![3, 4]), + }, + ], + }, + RawStateChangesWithTrieKey { + trie_key: bob_trie_key.clone(), + changes: vec![ + RawStateChange { + cause: StateChangeCause::InitialState, + data: Some(vec![5, 6]), + }, + RawStateChange { + cause: StateChangeCause::ReceiptProcessing { + receipt_hash: Default::default(), + }, + data: None, + }, + ], + }, + ]; + + let flat_state_changes = FlatStateChanges::from_state_changes(&state_changes); + assert_eq!( + flat_state_changes.get(&alice_trie_key.to_vec()), + Some(Some(FlatStateValue::inlined(&[3, 4]))) + ); + assert_eq!(flat_state_changes.get(&bob_trie_key.to_vec()), Some(None)); + assert_eq!(flat_state_changes.get(&carol_trie_key.to_vec()), None); + assert_eq!( + flat_state_changes.get(&delayed_trie_key.to_vec()), + Some(Some(FlatStateValue::inlined(&[1]))) + ); + assert_eq!( + flat_state_changes.get(&delayed_receipt_trie_key.to_vec()), + Some(Some(FlatStateValue::inlined(&[2]))) + ); + } + + /// Check that merge of `FlatStateChanges`s overrides the old changes for the same keys and doesn't conflict with + /// different keys. + #[test] + fn flat_state_changes_merge() { + let mut changes = FlatStateChanges::from([ + (vec![1], Some(FlatStateValue::value_ref(&[4]))), + (vec![2], Some(FlatStateValue::value_ref(&[5]))), + (vec![3], None), + (vec![4], Some(FlatStateValue::value_ref(&[6]))), + ]); + let changes_new = FlatStateChanges::from([ + (vec![2], Some(FlatStateValue::value_ref(&[7]))), + (vec![3], Some(FlatStateValue::value_ref(&[8]))), + (vec![4], None), + (vec![5], Some(FlatStateValue::value_ref(&[9]))), + ]); + changes.merge(changes_new); + + assert_eq!(changes.get(&[1]), Some(Some(FlatStateValue::value_ref(&[4])))); + assert_eq!(changes.get(&[2]), Some(Some(FlatStateValue::value_ref(&[7])))); + assert_eq!(changes.get(&[3]), Some(Some(FlatStateValue::value_ref(&[8])))); + assert_eq!(changes.get(&[4]), Some(None)); + assert_eq!(changes.get(&[5]), Some(Some(FlatStateValue::value_ref(&[9])))); + } +} diff --git a/core/store/src/flat/inlining_migration.rs b/core/store/src/flat/inlining_migration.rs new file mode 100644 index 000000000..69721d832 --- /dev/null +++ b/core/store/src/flat/inlining_migration.rs @@ -0,0 +1,481 @@ +use std::collections::HashMap; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; +use std::thread::JoinHandle; +use std::time::Duration; + +use borsh::BorshDeserialize; +use crossbeam::channel; +use itertools::Itertools; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::state::FlatStateValue; +use tracing::{debug, info}; + +use crate::flat::store_helper::set_flat_state_values_inlining_migration_status; +use crate::metrics::flat_state_metrics::inlining_migration::{ + FLAT_STATE_PAUSED_DURATION, INLINED_COUNT, INLINED_TOTAL_VALUES_SIZE, PROCESSED_COUNT, + PROCESSED_TOTAL_VALUES_SIZE, SKIPPED_COUNT, +}; +use crate::{DBCol, Store, TrieDBStorage, TrieStorage}; + +use super::store_helper::{ + decode_flat_state_db_key, get_flat_state_values_inlining_migration_status, +}; +use super::types::FlatStateValuesInliningMigrationStatus; +use super::FlatStorageManager; + +pub struct FlatStateValuesInliningMigrationHandle { + handle: JoinHandle<()>, + keep_running: Arc, +} + +const BACKGROUND_MIGRATION_BATCH_SIZE: usize = 50_000; + +impl FlatStateValuesInliningMigrationHandle { + pub fn start_background_migration( + store: Store, + flat_storage_manager: FlatStorageManager, + read_state_threads: usize, + ) -> Self { + let keep_running = Arc::new(AtomicBool::new(true)); + let keep_runnning_clone = keep_running.clone(); + let handle = std::thread::spawn(move || { + let status = get_flat_state_values_inlining_migration_status(&store) + .expect("failed to read fs migration status"); + info!(target: "store", ?status, "Read FlatState values inlining migration status"); + if status == FlatStateValuesInliningMigrationStatus::Finished { + return; + } + set_flat_state_values_inlining_migration_status( + &store, + FlatStateValuesInliningMigrationStatus::InProgress, + ) + .expect("failed to set fs migration in-progress status"); + let completed = inline_flat_state_values( + store.clone(), + &flat_storage_manager, + &keep_running, + read_state_threads, + BACKGROUND_MIGRATION_BATCH_SIZE, + ); + if completed { + set_flat_state_values_inlining_migration_status( + &store, + FlatStateValuesInliningMigrationStatus::Finished, + ) + .expect("failed to set fs migration finished status"); + } + }); + Self { handle, keep_running: keep_runnning_clone } + } + + pub fn stop(self) { + self.keep_running.store(false, std::sync::atomic::Ordering::Relaxed); + self.handle.join().expect("join should not fail here"); + } +} + +struct ReadValueRequest { + shard_uid: ShardUId, + value_hash: CryptoHash, +} + +struct ReadValueResponse { + value_hash: CryptoHash, + value_bytes: Option>, +} + +/// An abstraction that enables reading values from State in parallel using +/// multiple threads. +struct StateValueReader { + pending_requests: usize, + value_request_send: channel::Sender, + value_response_recv: channel::Receiver, + join_handles: Vec>, +} + +impl StateValueReader { + fn new(store: Store, num_threads: usize) -> Self { + let (value_request_send, value_request_recv) = channel::unbounded(); + let (value_response_send, value_response_recv) = channel::unbounded(); + let mut join_handles = Vec::new(); + for _ in 0..num_threads { + join_handles.push(Self::spawn_read_value_thread( + store.clone(), + value_request_recv.clone(), + value_response_send.clone(), + )); + } + Self { pending_requests: 0, value_request_send, value_response_recv, join_handles } + } + + fn submit(&mut self, shard_uid: ShardUId, value_hash: CryptoHash) { + let req = ReadValueRequest { shard_uid, value_hash }; + self.value_request_send.send(req).expect("send should not fail here"); + self.pending_requests += 1; + } + + fn receive_all(&mut self) -> HashMap> { + let mut ret = HashMap::new(); + while self.pending_requests > 0 { + let resp = self.value_response_recv.recv().expect("recv should not fail here"); + if let Some(value) = resp.value_bytes { + ret.insert(resp.value_hash, value); + } + self.pending_requests -= 1; + } + ret + } + + fn spawn_read_value_thread( + store: Store, + recv: channel::Receiver, + send: channel::Sender, + ) -> std::thread::JoinHandle<()> { + std::thread::spawn(move || { + while let Ok(req) = recv.recv() { + let trie_storage = TrieDBStorage::new(store.clone(), req.shard_uid); + let bytes = match trie_storage.retrieve_raw_bytes(&req.value_hash) { + Ok(bytes) => Some(bytes.to_vec()), + Err(err) => { + log_skipped("failed to read value from State", err); + None + } + }; + send.send(ReadValueResponse { value_hash: req.value_hash, value_bytes: bytes }) + .expect("send should not fail here"); + } + }) + } + + /// Note that we cannot use standard `drop` because it takes `&mut self` + /// as an argument which prevents manual drop of `self.value_request_send` + fn close(self) { + std::mem::drop(self.value_request_send); + for join_handle in self.join_handles { + join_handle.join().expect("join should not fail here"); + } + } +} + +/// Inlines all FlatState values having length below `FlatStateValue::INLINE_DISK_VALUE_THRESHOLD`. +/// Migration is safe to be executed in parallel with block processing, which +/// is achieved by temporary preventing FlatState updates with +/// `FlatStorageManager::set_flat_state_updates_mode`. +/// +/// * `read_state_threads` - number of threads for reading values from `State` in parallel. +/// * `batch_size` - number of values to be processed for inlining in one batch. +pub fn inline_flat_state_values( + store: Store, + flat_storage_manager: &FlatStorageManager, + keep_running: &AtomicBool, + read_state_threads: usize, + batch_size: usize, +) -> bool { + info!(target: "store", %read_state_threads, %batch_size, "Starting FlatState value inlining migration"); + let migration_start = std::time::Instant::now(); + let mut value_reader = StateValueReader::new(store.clone(), read_state_threads); + let mut inlined_total_count = 0; + let mut interrupted = false; + for (batch_index, batch) in + store.iter(DBCol::FlatState).chunks(batch_size).into_iter().enumerate() + { + if !keep_running.load(std::sync::atomic::Ordering::Relaxed) { + info!(target: "store", %batch_index, "FlatState value inlining migration was interrupted"); + interrupted = true; + break; + } + let (mut min_key, mut max_key) = (None, None); + for entry in batch { + PROCESSED_COUNT.inc(); + let (key, value) = match entry { + Ok(v) => v, + Err(err) => { + log_skipped("rocksdb iterator error", err); + continue; + } + }; + let shard_uid = match decode_flat_state_db_key(&key) { + Ok((shard_uid, _)) => shard_uid, + Err(err) => { + log_skipped("failed to decode FlatState key", err); + continue; + } + }; + let fs_value = match FlatStateValue::try_from_slice(&value) { + Ok(fs_value) => fs_value, + Err(err) => { + log_skipped("failed to deserialise FlatState value", err); + continue; + } + }; + let value_size = match &fs_value { + FlatStateValue::Ref(value_ref) => value_ref.length as u64, + FlatStateValue::Inlined(bytes) => bytes.len() as u64, + }; + PROCESSED_TOTAL_VALUES_SIZE.inc_by(value_size); + if let FlatStateValue::Ref(value_ref) = fs_value { + if value_ref.length as usize <= FlatStateValue::INLINE_DISK_VALUE_THRESHOLD { + if min_key.is_none() { + min_key = Some(key.to_vec()); + } + max_key = Some(key.to_vec()); + INLINED_TOTAL_VALUES_SIZE.inc_by(value_size); + value_reader.submit(shard_uid, value_ref.hash); + } + } + } + let hash_to_value = value_reader.receive_all(); + let mut inlined_batch_count = 0; + let mut batch_duration = std::time::Duration::ZERO; + if !hash_to_value.is_empty() { + // Possibly flat storage head can be locked. If that happens wait a little bit and try again. + // The number of attempts is infinite because flat storage head is supposed to be usually unlocked. + interrupted = lock_flat_head_blocking(flat_storage_manager, keep_running, batch_index); + if interrupted { + break; + } + tracing::debug!(target: "store", "Locked flat storage for the inlining migration"); + + // Here we need to re-read the latest FlatState values in `min_key..=max_key` range + // while updates are disabled. This way we prevent updating the values that + // were updated since migration start. + let batch_inlining_start = std::time::Instant::now(); + let mut store_update = store.store_update(); + // rockdb API accepts the exclusive end of the range, so we append + // `0u8` here to make sure `max_key` is included in the range + let upper_bound_key = max_key.map(|mut v| { + v.push(0u8); + v + }); + for (key, value) in store + .iter_range(DBCol::FlatState, min_key.as_deref(), upper_bound_key.as_deref()) + .flat_map(|v| v) + { + if let Ok(FlatStateValue::Ref(value_ref)) = FlatStateValue::try_from_slice(&value) { + if let Some(value) = hash_to_value.get(&value_ref.hash) { + store_update.set( + DBCol::FlatState, + &key, + &borsh::to_vec(&FlatStateValue::inlined(value)) + .expect("borsh should not fail here"), + ); + inlined_batch_count += 1; + INLINED_COUNT.inc(); + } + } + } + store_update.commit().expect("failed to commit inlined values"); + assert!(flat_storage_manager.set_flat_state_updates_mode(true)); + tracing::debug!(target: "store", "Unlocked flat storage after the inlining migration"); + inlined_total_count += inlined_batch_count; + batch_duration = batch_inlining_start.elapsed(); + FLAT_STATE_PAUSED_DURATION.observe(batch_duration.as_secs_f64()); + } + debug!(target: "store", %batch_index, %inlined_batch_count, %inlined_total_count, ?batch_duration, "Processed flat state value inlining batch"); + } + value_reader.close(); + let migration_elapsed = migration_start.elapsed(); + info!(target: "store", %inlined_total_count, ?migration_elapsed, %interrupted, "Finished FlatState value inlining migration"); + !interrupted +} + +/// Blocks until the flat head is locked or until the thread is interrupted. +/// Returns whether it was interrupted. +fn lock_flat_head_blocking( + flat_storage_manager: &FlatStorageManager, + keep_running: &AtomicBool, + batch_index: usize, +) -> bool { + loop { + if !keep_running.load(std::sync::atomic::Ordering::Relaxed) { + tracing::info!(target: "store", batch_index, "FlatState value inlining migration was interrupted"); + return true; + } + if flat_storage_manager.set_flat_state_updates_mode(false) { + return false; + } + tracing::debug!(target: "store", "Couldn't lock flat storage for the inlining migration, will retry locking."); + std::thread::sleep(Duration::from_secs(1)); + } +} + +fn log_skipped(reason: &str, err: impl std::error::Error) { + debug!(target: "store", %reason, %err, "Skipped value during FlatState inlining"); + SKIPPED_COUNT.inc(); +} + +#[cfg(test)] +mod tests { + use super::inline_flat_state_values; + use crate::flat::store_helper::encode_flat_state_db_key; + use crate::flat::{FlatStateValuesInliningMigrationHandle, FlatStorageManager}; + use crate::{DBCol, NodeStorage, Store, TrieCachingStorage}; + use borsh::BorshDeserialize; + use unc_o11y::testonly::init_test_logger; + use unc_primitives::hash::{hash, CryptoHash}; + use unc_primitives::shard_layout::{ShardLayout, ShardUId}; + use unc_primitives::state::FlatStateValue; + use std::sync::atomic::AtomicBool; + use std::time::Duration; + + #[test] + fn full_migration() { + let store = NodeStorage::test_opener().1.open().unwrap().get_hot_store(); + let shard_uid = ShardLayout::v0_single_shard().shard_uids().next().unwrap(); + let values = [ + vec![0], + vec![1], + vec![2; FlatStateValue::INLINE_DISK_VALUE_THRESHOLD + 1], + vec![3], + vec![4], + vec![5], + ]; + populate_flat_store(&store, shard_uid, &values); + let flat_storage_manager = create_flat_storage_for_genesis(&store, shard_uid); + inline_flat_state_values( + store.clone(), + &flat_storage_manager, + &AtomicBool::new(true), + 2, + 4, + ); + assert_eq!( + store + .iter(DBCol::FlatState) + .flat_map(|r| r.map(|(_, v)| FlatStateValue::try_from_slice(&v).unwrap())) + .collect::>(), + vec![ + FlatStateValue::inlined(&values[0]), + FlatStateValue::inlined(&values[1]), + FlatStateValue::value_ref(&values[2]), + FlatStateValue::inlined(&values[3]), + FlatStateValue::inlined(&values[4]), + FlatStateValue::inlined(&values[5]), + ] + ); + } + + #[test] + // Initializes several ref values. + // Locks flat storage head, and checks that the migration doesn't crash and doesn't proceed. + // Then it unlocks flat head and checks that the migration completes. + fn block_migration() { + init_test_logger(); + let store = NodeStorage::test_opener().1.open().unwrap().get_hot_store(); + let shard_uid = ShardLayout::v0_single_shard().shard_uids().next().unwrap(); + let values = [ + vec![0], + vec![1], + vec![2; FlatStateValue::INLINE_DISK_VALUE_THRESHOLD + 1], + vec![3], + vec![4], + vec![5], + ]; + populate_flat_store(&store, shard_uid, &values); + let flat_storage_manager = create_flat_storage_for_genesis(&store, shard_uid); + // Lock flat head. + assert!(flat_storage_manager.set_flat_state_updates_mode(false)); + // Start a separate thread that should block waiting for the flat head. + let _handle = FlatStateValuesInliningMigrationHandle::start_background_migration( + store.clone(), + flat_storage_manager.clone(), + 2, + ); + + // Give it time and check that no progress was made on the migration. + std::thread::sleep(Duration::from_secs(2)); + assert_eq!(count_inlined_values(&store), 0); + + // Unlock. + assert!(flat_storage_manager.set_flat_state_updates_mode(true)); + + // Give it more time. It should be unblocked now and the migration should complete. + std::thread::sleep(Duration::from_secs(2)); + assert_eq!(count_inlined_values(&store), 5); + } + + #[test] + // Initializes several ref values. + // Locks flat storage head, and checks that the migration doesn't crash and doesn't proceed. + // Interrupt the migration and check that the thread exits. + fn interrupt_blocked_migration() { + init_test_logger(); + let store = NodeStorage::test_opener().1.open().unwrap().get_hot_store(); + let shard_uid = ShardLayout::v0_single_shard().shard_uids().next().unwrap(); + let values = [ + vec![0], + vec![1], + vec![2; FlatStateValue::INLINE_DISK_VALUE_THRESHOLD + 1], + vec![3], + vec![4], + vec![5], + ]; + populate_flat_store(&store, shard_uid, &values); + let flat_storage_manager = create_flat_storage_for_genesis(&store, shard_uid); + // Lock flat head. + assert!(flat_storage_manager.set_flat_state_updates_mode(false)); + // Start a separate thread that should block waiting for the flat head. + let handle = FlatStateValuesInliningMigrationHandle::start_background_migration( + store.clone(), + flat_storage_manager, + 2, + ); + + // Give it time and check that no progress was made on the migration. + std::thread::sleep(Duration::from_secs(2)); + assert_eq!(count_inlined_values(&store), 0); + + // Interrupt. + handle.keep_running.store(false, std::sync::atomic::Ordering::Relaxed); + assert!(handle.handle.join().is_ok()); + // Check that no migration took place. + assert_eq!(count_inlined_values(&store), 0); + } + + fn populate_flat_store(store: &Store, shard_uid: ShardUId, values: &[Vec]) { + let mut store_update = store.store_update(); + for (i, value) in values.iter().enumerate() { + let trie_key = + TrieCachingStorage::get_key_from_shard_uid_and_hash(shard_uid, &hash(&value)); + store_update.increment_refcount(DBCol::State, &trie_key, &value); + let fs_key = encode_flat_state_db_key(shard_uid, &[i as u8]); + let fs_value = borsh::to_vec(&FlatStateValue::value_ref(&value)).unwrap(); + store_update.set(DBCol::FlatState, &fs_key, &fs_value); + } + store_update.commit().unwrap(); + } + + fn create_flat_storage_for_genesis(store: &Store, shard_uid: ShardUId) -> FlatStorageManager { + let flat_storage_manager = FlatStorageManager::new(store.clone()); + let mut store_update = store.store_update(); + flat_storage_manager.set_flat_storage_for_genesis( + &mut store_update, + shard_uid, + &CryptoHash::default(), + 0, + ); + store_update.commit().unwrap(); + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + flat_storage_manager + } + + fn count_inlined_values(store: &Store) -> u64 { + store + .iter(DBCol::FlatState) + .flat_map(|r| { + r.map(|(_, v)| { + if matches!( + FlatStateValue::try_from_slice(&v).unwrap(), + FlatStateValue::Inlined(_) + ) { + 1 + } else { + 0 + } + }) + }) + .sum::() + } +} diff --git a/core/store/src/flat/manager.rs b/core/store/src/flat/manager.rs new file mode 100644 index 000000000..a2b5c7c66 --- /dev/null +++ b/core/store/src/flat/manager.rs @@ -0,0 +1,271 @@ +use crate::flat::{ + store_helper, BlockInfo, FlatStorageReadyStatus, FlatStorageStatus, POISONED_LOCK_ERR, +}; +use unc_primitives::block::Block; +use unc_primitives::errors::StorageError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::types::{BlockHeight, RawStateChangesWithTrieKey}; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; +use tracing::debug; + +use crate::{get_genesis_hash, Store, StoreUpdate}; + +use super::chunk_view::FlatStorageChunkView; +use super::{ + FlatStateChanges, FlatStateDelta, FlatStateDeltaMetadata, FlatStorage, FlatStorageError, +}; + +/// `FlatStorageManager` provides a way to construct new flat state to pass to new tries. +/// It is owned by NightshadeRuntime, and thus can be owned by multiple threads, so the implementation +/// must be thread safe. +#[derive(Clone)] +pub struct FlatStorageManager(Arc); + +pub struct FlatStorageManagerInner { + store: Store, + /// Here we store the flat_storage per shard. The reason why we don't use the same + /// FlatStorage for all shards is that there are two modes of block processing, + /// normal block processing and block catchups. Since these are performed on different range + /// of blocks, we need flat storage to be able to support different range of blocks + /// on different shards. So we simply store a different state for each shard. + /// This may cause some overhead because the data like shards that the node is processing for + /// this epoch can share the same `head` and `tail`, similar for shards for the next epoch, + /// but such overhead is negligible comparing the delta sizes, so we think it's ok. + flat_storages: Mutex>, +} + +impl FlatStorageManager { + pub fn new(store: Store) -> Self { + Self(Arc::new(FlatStorageManagerInner { store, flat_storages: Default::default() })) + } + + /// When a node starts from an empty database, this function must be called to ensure + /// information such as flat head is set up correctly in the database. + /// Note that this function is different from `create_flat_storage_for_shard`, + /// it must be called before `create_flat_storage_for_shard` if the node starts from + /// an empty database. + pub fn set_flat_storage_for_genesis( + &self, + store_update: &mut StoreUpdate, + shard_uid: ShardUId, + genesis_block: &CryptoHash, + genesis_height: BlockHeight, + ) { + let flat_storages = self.0.flat_storages.lock().expect(POISONED_LOCK_ERR); + assert!(!flat_storages.contains_key(&shard_uid)); + store_helper::set_flat_storage_status( + store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { + flat_head: BlockInfo::genesis(*genesis_block, genesis_height), + }), + ); + } + + /// Creates flat storage instance for shard `shard_uid`. The function also checks that + /// the shard's flat storage state hasn't been set before, otherwise it panics. + /// TODO (#7327): this behavior may change when we implement support for state sync + /// and resharding. + pub fn create_flat_storage_for_shard(&self, shard_uid: ShardUId) -> Result<(), StorageError> { + let mut flat_storages = self.0.flat_storages.lock().expect(POISONED_LOCK_ERR); + let original_value = + flat_storages.insert(shard_uid, FlatStorage::new(self.0.store.clone(), shard_uid)?); + // TODO (#7327): maybe we should propagate the error instead of assert here + // assert is fine now because this function is only called at construction time, but we + // will need to be more careful when we want to implement flat storage for resharding + assert!(original_value.is_none()); + Ok(()) + } + + /// Update flat storage for given processed or caught up block, which includes: + /// - merge deltas from current flat storage head to new one; + /// - update flat storage head to the hash of final block visible from given one; + /// - remove info about unreachable blocks from memory. + pub fn update_flat_storage_for_shard( + &self, + shard_uid: ShardUId, + block: &Block, + ) -> Result<(), StorageError> { + if let Some(flat_storage) = self.get_flat_storage_for_shard(shard_uid) { + let mut new_flat_head = *block.header().last_final_block(); + if new_flat_head == CryptoHash::default() { + let genesis_hash = get_genesis_hash(&self.0.store) + .map_err(|e| FlatStorageError::StorageInternalError(e.to_string()))? + .expect("Genesis hash must exist. Consider initialization."); + new_flat_head = genesis_hash; + } + // Try to update flat head. + flat_storage.update_flat_head(&new_flat_head, false).unwrap_or_else(|err| { + match &err { + FlatStorageError::BlockNotSupported(_) => { + // It's possible that new head is not a child of current flat head, e.g. when we have a + // fork: + // + // (flat head) /-------> 6 + // 1 -> 2 -> 3 -> 4 + // \---> 5 + // + // where during postprocessing (5) we call `update_flat_head(3)` and then for (6) we can + // call `update_flat_head(2)` because (2) will be last visible final block from it. + // In such case, just log an error. + debug!( + target: "store", + ?new_flat_head, + ?err, + ?shard_uid, + block_hash = ?block.header().hash(), + "Cannot update flat head"); + } + _ => { + // All other errors are unexpected, so we panic. + panic!("Cannot update flat head of shard {shard_uid:?} to {new_flat_head:?}: {err:?}"); + } + } + }); + } + Ok(()) + } + + pub fn save_flat_state_changes( + &self, + block_hash: CryptoHash, + prev_hash: CryptoHash, + height: BlockHeight, + shard_uid: ShardUId, + state_changes: &[RawStateChangesWithTrieKey], + ) -> Result { + let prev_block_with_changes = if state_changes.is_empty() { + // The current block has no flat state changes. + // Find the last block with flat state changes by looking it up in + // the prev block. + store_helper::get_prev_block_with_changes( + &self.0.store, + shard_uid, + block_hash, + prev_hash, + ) + .map_err(|e| StorageError::from(e))? + } else { + // The current block has flat state changes. + None + }; + + let delta = FlatStateDelta { + changes: FlatStateChanges::from_state_changes(state_changes), + metadata: FlatStateDeltaMetadata { + block: BlockInfo { hash: block_hash, height, prev_hash }, + prev_block_with_changes, + }, + }; + + let store_update = if let Some(flat_storage) = self.get_flat_storage_for_shard(shard_uid) { + // If flat storage exists, we add a block to it. + flat_storage.add_delta(delta).map_err(|e| StorageError::from(e))? + } else { + let shard_id = shard_uid.shard_id(); + // Otherwise, save delta to disk so it will be used for flat storage creation later. + debug!(target: "store", %shard_id, "Add delta for flat storage creation"); + let mut store_update = self.0.store.store_update(); + store_helper::set_delta(&mut store_update, shard_uid, &delta); + store_update + }; + + Ok(store_update) + } + + pub fn get_flat_storage_status(&self, shard_uid: ShardUId) -> FlatStorageStatus { + store_helper::get_flat_storage_status(&self.0.store, shard_uid) + .expect("failed to read flat storage status") + } + + /// Creates `FlatStorageChunkView` to access state for `shard_uid` and block `block_hash`. + /// Note that: + /// * the state includes changes by the block `block_hash`; + /// * request to get value locks shared `FlatStorage` struct which may cause write slowdowns. + pub fn chunk_view( + &self, + shard_uid: ShardUId, + block_hash: CryptoHash, + ) -> Option { + let flat_storage = { + let flat_storages = self.0.flat_storages.lock().expect(POISONED_LOCK_ERR); + // It is possible that flat storage state does not exist yet because it is being created in + // background. + match flat_storages.get(&shard_uid) { + Some(flat_storage) => flat_storage.clone(), + None => { + debug!(target: "store", "FlatStorage is not ready"); + return None; + } + } + }; + Some(FlatStorageChunkView::new(self.0.store.clone(), block_hash, flat_storage)) + } + + pub fn get_shard_uids(&self) -> Vec { + let flat_storages = self.0.flat_storages.lock().expect(POISONED_LOCK_ERR); + flat_storages.keys().cloned().collect() + } + + // TODO (#7327): consider returning Result when we expect flat storage to exist + pub fn get_flat_storage_for_shard(&self, shard_uid: ShardUId) -> Option { + let flat_storages = self.0.flat_storages.lock().expect(POISONED_LOCK_ERR); + flat_storages.get(&shard_uid).cloned() + } + + /// Removes FlatStorage object from FlatStorageManager. + /// If FlatStorageManager did have that object, then removes all information about Flat State and returns Ok(true). + /// Otherwise does nothing and returns Ok(false). + pub fn remove_flat_storage_for_shard( + &self, + shard_uid: ShardUId, + store_update: &mut StoreUpdate, + ) -> Result { + let mut flat_storages = self.0.flat_storages.lock().expect(POISONED_LOCK_ERR); + if let Some(flat_store) = flat_storages.remove(&shard_uid) { + flat_store.clear_state(store_update)?; + tracing::info!(target: "store", ?shard_uid, "remove_flat_storage_for_shard successful"); + Ok(true) + } else { + Ok(false) + } + } + + /// Updates `move_head_enabled` for all shards and returns whether it succeeded. + /// If at least one of the shards fails to update move_head_enabled, then that operation is rolled back for all shards. + /// + /// Rollbacks should work, because we assume that this function is the only + /// entry point to locking/unlocking flat head updates in a system with + /// multiple FlatStorages running in parallel. + pub fn set_flat_state_updates_mode(&self, enabled: bool) -> bool { + let flat_storages = self.0.flat_storages.lock().expect(POISONED_LOCK_ERR); + let mut all_updated = true; + let mut updated_flat_storages = vec![]; + let mut updated_shard_uids = vec![]; + for (shard_uid, flat_storage) in flat_storages.iter() { + if flat_storage.set_flat_head_update_mode(enabled) { + updated_flat_storages.push(flat_storage); + updated_shard_uids.push(shard_uid); + } else { + all_updated = false; + tracing::error!(target: "store", rolling_back_shards = ?updated_shard_uids, enabled, ?shard_uid, "Locking/Unlocking of flat head updates failed for shard. Reverting."); + break; + } + } + if all_updated { + tracing::debug!(target: "store", enabled, "Locking/Unlocking of flat head updates succeeded"); + true + } else { + // Do rollback. + // It does allow for a data race if somebody updates move_head_enabled on individual shards. + // The assumption is that all shards get locked/unlocked at the same time. + for flat_storage in updated_flat_storages { + flat_storage.set_flat_head_update_mode(!enabled); + } + tracing::error!(target: "store", enabled, "Locking/Unlocking of flat head updates failed"); + false + } + } +} diff --git a/core/store/src/flat/metrics.rs b/core/store/src/flat/metrics.rs new file mode 100644 index 000000000..54b598ecc --- /dev/null +++ b/core/store/src/flat/metrics.rs @@ -0,0 +1,105 @@ +use crate::metrics::flat_state_metrics; +use unc_o11y::metrics::{IntCounter, IntGauge}; +use unc_primitives::types::{BlockHeight, ShardId}; + +use super::FlatStorageStatus; + +pub(crate) struct FlatStorageMetrics { + flat_head_height: IntGauge, + distance_to_head: IntGauge, + hops_to_head: IntGauge, + cached_deltas: IntGauge, + cached_changes_num_items: IntGauge, + cached_changes_size: IntGauge, +} + +impl FlatStorageMetrics { + pub(crate) fn new(shard_id: ShardId) -> Self { + let shard_id_label = shard_id.to_string(); + Self { + flat_head_height: flat_state_metrics::FLAT_STORAGE_HEAD_HEIGHT + .with_label_values(&[&shard_id_label]), + distance_to_head: flat_state_metrics::FLAT_STORAGE_DISTANCE_TO_HEAD + .with_label_values(&[&shard_id_label]), + hops_to_head: flat_state_metrics::FLAT_STORAGE_HOPS_TO_HEAD + .with_label_values(&[&shard_id_label]), + cached_deltas: flat_state_metrics::FLAT_STORAGE_CACHED_DELTAS + .with_label_values(&[&shard_id_label]), + cached_changes_num_items: flat_state_metrics::FLAT_STORAGE_CACHED_CHANGES_NUM_ITEMS + .with_label_values(&[&shard_id_label]), + cached_changes_size: flat_state_metrics::FLAT_STORAGE_CACHED_CHANGES_SIZE + .with_label_values(&[&shard_id_label]), + } + } + + pub(crate) fn set_distance_to_head(&self, distance: usize, height: Option) { + self.distance_to_head.set(height.unwrap_or(0) as i64); + self.hops_to_head.set(distance as i64); + } + + pub(crate) fn set_flat_head_height(&self, height: u64) { + self.flat_head_height.set(height as i64); + } + + pub(crate) fn set_cached_deltas( + &self, + cached_deltas: usize, + cached_changes_num_items: usize, + cached_changes_size: u64, + ) { + self.cached_deltas.set(cached_deltas as i64); + self.cached_changes_num_items.set(cached_changes_num_items as i64); + self.cached_changes_size.set(cached_changes_size as i64); + } +} + +/// Metrics reporting about flat storage creation progress on each status update. +pub struct FlatStorageCreationMetrics { + status: IntGauge, + flat_head_height: IntGauge, + remaining_state_parts: IntGauge, + fetched_state_parts: IntCounter, + fetched_state_items: IntCounter, + threads_used: IntGauge, +} + +impl FlatStorageCreationMetrics { + pub fn new(shard_id: ShardId) -> Self { + let shard_id_label = shard_id.to_string(); + Self { + status: flat_state_metrics::FLAT_STORAGE_CREATION_STATUS + .with_label_values(&[&shard_id_label]), + flat_head_height: flat_state_metrics::FLAT_STORAGE_HEAD_HEIGHT + .with_label_values(&[&shard_id_label]), + remaining_state_parts: flat_state_metrics::FLAT_STORAGE_CREATION_REMAINING_STATE_PARTS + .with_label_values(&[&shard_id_label]), + fetched_state_parts: flat_state_metrics::FLAT_STORAGE_CREATION_FETCHED_STATE_PARTS + .with_label_values(&[&shard_id_label]), + fetched_state_items: flat_state_metrics::FLAT_STORAGE_CREATION_FETCHED_STATE_ITEMS + .with_label_values(&[&shard_id_label]), + threads_used: flat_state_metrics::FLAT_STORAGE_CREATION_THREADS_USED + .with_label_values(&[&shard_id_label]), + } + } + + pub fn set_status(&self, status: &FlatStorageStatus) { + self.status.set(status.into()); + } + + pub fn set_flat_head_height(&self, height: u64) { + self.flat_head_height.set(height as i64); + } + + pub fn set_remaining_state_parts(&self, remaining_parts: u64) { + self.remaining_state_parts.set(remaining_parts as i64); + } + + pub fn threads_used(&self) -> IntGauge { + self.threads_used.clone() + } + + pub fn inc_fetched_state(&self, num_items: u64) { + self.fetched_state_items.inc_by(num_items); + self.fetched_state_parts.inc(); + } +} diff --git a/core/store/src/flat/mod.rs b/core/store/src/flat/mod.rs new file mode 100644 index 000000000..d195bf7e3 --- /dev/null +++ b/core/store/src/flat/mod.rs @@ -0,0 +1,56 @@ +//! FlatStorage is created as an additional representation of the state alongside with Tries. +//! It simply stores a mapping for all key value pairs stored in our Tries (leaves of the trie) +//! It is used for fast key value look up in the state. Reading a single key/value in trie +//! requires traversing the trie from the root, loading many nodes from the database. In flat storage, +//! we store a mapping from keys to value references so that key value lookup will only require two +//! db accesses - one to get value reference, one to get the value itself. In fact, in the case of small +//! values, flat storage only needs one read, because value will be stored in the mapping instead of +//! value ref. +//! +//! The main challenge in the flat storage implementation is that we need to able to handle forks, +//! so the flat storage API must support key value lookups for different blocks. +//! To achieve that, we store the key value pairs of the state on a block (head of the flat storage) +//! on disk and also store the change deltas for some other blocks in memory. With these deltas, +//! we can perform lookups for the other blocks. See comments in `FlatStorage` to see +//! which block should be the head of flat storage and which other blocks do flat storage support. +//! +//! This file contains the implementation of FlatStorage. It has three essential structs. +//! +//! `FlatStorageChunkView`: this provides an interface to get value or value references from flat storage. This +//! is the struct that will be stored as part of Trie. All trie reads will be directed +//! to the flat state. +//! `FlatStorageManager`: this is to construct flat state. +//! `FlatStorage`: this stores some information about the state of the flat storage itself, +//! for example, all block deltas that are stored in flat storage and a representation +//! of the chain formed by these blocks (because we can't access ChainStore +//! inside flat storage). + +mod chunk_view; +pub mod delta; +mod inlining_migration; +mod manager; +mod metrics; +mod storage; +pub mod store_helper; +#[cfg(test)] +pub mod test_utils; +mod types; + +pub use chunk_view::FlatStorageChunkView; +pub use delta::{FlatStateChanges, FlatStateDelta, FlatStateDeltaMetadata}; +pub use inlining_migration::{inline_flat_state_values, FlatStateValuesInliningMigrationHandle}; +pub use manager::FlatStorageManager; +pub use metrics::FlatStorageCreationMetrics; +pub use storage::FlatStorage; +pub use types::{ + BlockInfo, FetchingStateStatus, FlatStateIterator, FlatStorageCreationStatus, FlatStorageError, + FlatStorageReadyStatus, FlatStorageStatus, +}; + +pub(crate) const POISONED_LOCK_ERR: &str = "The lock was poisoned."; + +/// Number of traversed parts during a single step of fetching state. +pub const NUM_PARTS_IN_ONE_STEP: u64 = 20; + +/// Memory limit for state part being fetched. +pub const STATE_PART_MEMORY_LIMIT: bytesize::ByteSize = bytesize::ByteSize(10 * bytesize::MIB); diff --git a/core/store/src/flat/storage.rs b/core/store/src/flat/storage.rs new file mode 100644 index 000000000..086b7810f --- /dev/null +++ b/core/store/src/flat/storage.rs @@ -0,0 +1,1300 @@ +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; + +use unc_primitives::errors::StorageError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::state::FlatStateValue; +use unc_primitives::types::BlockHeight; +use tracing::{debug, warn}; + +use crate::flat::delta::{BlockWithChangesInfo, CachedFlatStateChanges}; +use crate::flat::BlockInfo; +use crate::flat::{FlatStorageReadyStatus, FlatStorageStatus}; +use crate::{Store, StoreUpdate}; + +use super::delta::{CachedFlatStateDelta, FlatStateDelta}; +use super::metrics::FlatStorageMetrics; +use super::store_helper; +use super::types::FlatStorageError; + +/// FlatStorage stores information on which blocks flat storage current supports key lookups on. +/// Note that this struct is shared by multiple threads, the chain thread, threads that apply chunks, +/// and view client, so the implementation here must be thread safe and must have interior mutability, +/// thus all methods in this class are with &self instead of &mut self. +#[derive(Clone)] +pub struct FlatStorage(pub(crate) Arc>); + +// FlatStorage need to support concurrent access and be consistent if node crashes or restarts, +// so we make sure to keep the following invariants in our implementation. +// - `flat_head` is stored on disk. The value of flat_head in memory and on disk should always +// be consistent with the flat state stored in `DbCol::FlatState` on disk. This means, updates to +// these values much be atomic from the outside. +// - All changes and metadata in `deltas` are stored on disk. And if a block is accepted by chain +// then its changes must be stored on disk as well, if the block is a child of `flat_head`. +// This makes sure that when a node restarts, FlatStorage can load changes for all blocks +// after the `flat_head` block successfully. +pub(crate) struct FlatStorageInner { + store: Store, + /// UId of the shard which state is accessed by this flat storage. + shard_uid: ShardUId, + /// The block for which we store the key value pairs of the state after it is applied. + /// For non catchup mode, it should be the last final block. + flat_head: BlockInfo, + /// Cached deltas for all blocks supported by this flat storage. + deltas: HashMap, + /// This flag enables skipping flat head moves, needed temporarily for FlatState + /// values inlining migration. + /// The flag has a numerical value and not a bool, to let us detect attempts + /// to disable move head multiple times. + move_head_enabled: bool, + metrics: FlatStorageMetrics, +} + +impl FlatStorageInner { + /// Expected limits for in-memory stored changes, under which flat storage must keep working. + /// If they are exceeded, warnings are displayed. Flat storage still will work, but its + /// performance will slow down, and eventually it can cause OOM error. + /// Limit for number of blocks needed to read. When over 100, introduces significant overhead. + /// https://github.com/utnet-org/utility/issues/8006#issuecomment-1473621334 + const HOPS_LIMIT: usize = 100; + /// Limit for total size of cached changes. We allocate 600 MiB for cached deltas, which + /// means 150 MiB per shards. + const CACHED_CHANGES_SIZE_LIMIT: bytesize::ByteSize = bytesize::ByteSize(150 * bytesize::MIB); + + const BLOCKS_WITH_CHANGES_FLAT_HEAD_GAP: BlockHeight = 2; + + /// Creates `BlockNotSupported` error for the given block. + /// In the context of updating the flat head, the error is handled gracefully. + fn create_block_not_supported_error(&self, block_hash: &CryptoHash) -> FlatStorageError { + FlatStorageError::BlockNotSupported((self.flat_head.hash, *block_hash)) + } + + /// Gets changes for the given block and shard `self.shard_uid`, assuming that they must exist. + fn get_block_changes( + &self, + block_hash: &CryptoHash, + ) -> Result, FlatStorageError> { + // TODO (#7327): add limitation on cached changes number to limit RAM usage + // and read single `ValueRef` from delta if it is not cached. + self.deltas + .get(block_hash) + .ok_or_else(|| missing_delta_error(block_hash)) + .map(|delta| delta.changes.clone()) + } + + /// Get sequence of blocks `target_block_hash` (inclusive) to flat head (exclusive) + /// in backwards chain order. Returns an error if there is no path between them. + fn get_blocks_to_head( + &self, + target_block_hash: &CryptoHash, + ) -> Result, FlatStorageError> { + let flat_head = &self.flat_head; + let mut block_hash = *target_block_hash; + let mut blocks = vec![]; + let mut first_height = None; + while block_hash != flat_head.hash { + let metadata = self + .deltas + .get(&block_hash) + .ok_or_else(|| self.create_block_not_supported_error(target_block_hash))? + .metadata; + if first_height.is_none() { + // Keep track of the height of the initial block. + first_height = Some(metadata.block.height); + } + + // Maybe skip to the previous block with changes. + block_hash = match metadata.prev_block_with_changes { + None => { + blocks.push(block_hash); + metadata.block.prev_hash + } + Some(prev_block_with_changes) => { + // Don't include blocks with no changes in the result, + // unless it's the target block. + if &block_hash == target_block_hash { + blocks.push(block_hash); + } + if prev_block_with_changes.height > flat_head.height { + prev_block_with_changes.hash + } else { + flat_head.hash + } + } + }; + } + self.metrics.set_distance_to_head( + blocks.len(), + first_height.map(|height| height - flat_head.height), + ); + if blocks.len() >= Self::HOPS_LIMIT { + warn!( + target: "chain", + shard_id = self.shard_uid.shard_id(), + flat_head_height = flat_head.height, + cached_deltas = self.deltas.len(), + num_hops = blocks.len(), + "Flat storage needs too many hops to access a block"); + } + + Ok(blocks) + } + + /// Updates metrics related to deltas, displays a warning if they are off. + fn update_delta_metrics(&self) { + let cached_deltas = self.deltas.len(); + let mut cached_changes_num_items = 0; + let mut cached_changes_size = 0; + for changes in self.deltas.values() { + cached_changes_num_items += changes.changes.len(); + cached_changes_size += changes.changes.total_size(); + } + + self.metrics.set_cached_deltas( + cached_deltas, + cached_changes_num_items, + cached_changes_size, + ); + + let cached_changes_size_bytes = bytesize::ByteSize(cached_changes_size); + if cached_changes_size_bytes >= Self::CACHED_CHANGES_SIZE_LIMIT { + warn!( + target: "chain", + shard_id = self.shard_uid.shard_id(), + flat_head_height = self.flat_head.height, + cached_deltas, + %cached_changes_size_bytes, + "Flat storage total size of cached deltas exceeded expected limits"); + } + } + + // Determine a block to make the new flat head. + // If `strict`, uses `block_hash` as the new flat head. + // If not `strict`, uses the second most recent block with flat state + // changes from `block_hash`, if it exists. + fn get_new_flat_head( + &self, + block_hash: CryptoHash, + strict: bool, + ) -> Result { + if strict { + return Ok(block_hash); + } + + let current_flat_head_hash = self.flat_head.hash; + let current_flat_head_height = self.flat_head.height; + + let mut new_head = block_hash; + let mut blocks_with_changes = 0; + // Delays updating flat head, keeps this many blocks with non-empty flat + // state changes between the requested flat head and the chosen head to + // make flat state snapshots function properly. + while blocks_with_changes < Self::BLOCKS_WITH_CHANGES_FLAT_HEAD_GAP { + if new_head == current_flat_head_hash { + return Ok(current_flat_head_hash); + } + let metadata = self + .deltas + .get(&new_head) + // BlockNotSupported error kind will be handled gracefully. + .ok_or(self.create_block_not_supported_error(&new_head))? + .metadata; + new_head = match metadata.prev_block_with_changes { + None => { + // The block has flat state changes. + blocks_with_changes += 1; + if blocks_with_changes == Self::BLOCKS_WITH_CHANGES_FLAT_HEAD_GAP { + break; + } + metadata.block.prev_hash + } + Some(BlockWithChangesInfo { hash, height, .. }) => { + // The block has no flat state changes. + if height <= current_flat_head_height { + return Ok(current_flat_head_hash); + } + hash + } + }; + } + Ok(new_head) + } + + #[cfg(test)] + pub fn test_get_new_flat_head( + &self, + block_hash: CryptoHash, + strict: bool, + ) -> Result { + self.get_new_flat_head(block_hash, strict) + } +} + +impl FlatStorage { + /// Create a new FlatStorage for `shard_uid` using flat head if it is stored on storage. + /// We also load all blocks with height between flat head to `latest_block_height` + /// including those on forks into the returned FlatStorage. + pub fn new(store: Store, shard_uid: ShardUId) -> Result { + let shard_id = shard_uid.shard_id(); + let flat_head = match store_helper::get_flat_storage_status(&store, shard_uid) { + Ok(FlatStorageStatus::Ready(ready_status)) => ready_status.flat_head, + status => { + return Err(StorageError::StorageInconsistentState(format!( + "Cannot create flat storage for shard {shard_id} with status {status:?}" + ))); + } + }; + let metrics = FlatStorageMetrics::new(shard_id); + metrics.set_flat_head_height(flat_head.height); + + let deltas_metadata = store_helper::get_all_deltas_metadata(&store, shard_uid) + .unwrap_or_else(|_| { + panic!("Cannot read flat state deltas metadata for shard {shard_id} from storage") + }); + let mut deltas = HashMap::new(); + for delta_metadata in deltas_metadata { + let block_hash = delta_metadata.block.hash; + let changes: CachedFlatStateChanges = + store_helper::get_delta_changes(&store, shard_uid, block_hash) + .expect("failed to read flat state delta changes") + .unwrap_or_else(|| { + panic!("cannot find block delta for block {block_hash:?} shard {shard_id}") + }) + .into(); + deltas.insert( + block_hash, + CachedFlatStateDelta { metadata: delta_metadata, changes: Arc::new(changes) }, + ); + } + + let inner = FlatStorageInner { + store, + shard_uid, + flat_head, + deltas, + move_head_enabled: true, + metrics, + }; + inner.update_delta_metrics(); + Ok(Self(Arc::new(RwLock::new(inner)))) + } + + /// Get sequence of blocks `target_block_hash` (inclusive) to flat head (exclusive) + /// in backwards chain order. Returns an error if there is no path between them. + #[cfg(test)] + pub(crate) fn get_blocks_to_head( + &self, + target_block_hash: &CryptoHash, + ) -> Result, FlatStorageError> { + let guard = self.0.read().expect(super::POISONED_LOCK_ERR); + guard.get_blocks_to_head(target_block_hash) + } + + pub fn get_value( + &self, + block_hash: &CryptoHash, + key: &[u8], + ) -> Result, crate::StorageError> { + let guard = self.0.read().expect(super::POISONED_LOCK_ERR); + let blocks_to_head = + guard.get_blocks_to_head(block_hash).map_err(|e| StorageError::from(e))?; + for block_hash in blocks_to_head.iter() { + // If we found a key in changes, we can return a value because it is the most recent key update. + let changes = guard.get_block_changes(block_hash)?; + match changes.get(key) { + Some(value_ref) => { + return Ok(value_ref.map(|value_ref| FlatStateValue::Ref(value_ref))); + } + None => {} + }; + } + + let value = store_helper::get_flat_state_value(&guard.store, guard.shard_uid, key)?; + Ok(value) + } + + /// Update the head of the flat storage, including updating the flat state + /// in memory and on disk and updating the flat state to reflect the state + /// at the new head. If updating to given head is not possible, returns an + /// error. + /// If `strict`, then unconditionally sets flat head to the given block. + /// If not `strict`, then it updates the flat head to the latest block X, + /// such that [X, block_hash] contains 2 blocks with flat state changes. If possible. + /// + /// The function respects the current flat head and will never try to + /// set flat head to a block older than the current flat head. + // + // Let's denote blocks with flat state changes as X, and blocks without + // flat state changes as O. + // + // block_hash + // | + // v + // ...-O-O-X-O-O-O-X-O-O-O-X-O-O-O-X-....->future + // ^ + // | + // new_head + // + // The segment [new_head, block_hash] contains two blocks with flat state changes. + pub fn update_flat_head( + &self, + block_hash: &CryptoHash, + strict: bool, + ) -> Result<(), FlatStorageError> { + let mut guard = self.0.write().expect(crate::flat::POISONED_LOCK_ERR); + if !guard.move_head_enabled { + return Ok(()); + } + + let new_head = guard.get_new_flat_head(*block_hash, strict)?; + if new_head == guard.flat_head.hash { + return Ok(()); + } + + let shard_uid = guard.shard_uid; + let shard_id = shard_uid.shard_id(); + + tracing::debug!(target: "store", flat_head = ?guard.flat_head.hash, ?new_head, shard_id, "Moving flat head"); + let blocks = guard.get_blocks_to_head(&new_head)?; + + for block_hash in blocks.into_iter().rev() { + let mut store_update = StoreUpdate::new(guard.store.storage.clone()); + // Delta must exist because flat storage is locked and we could retrieve + // path from old to new head. Otherwise we return internal error. + let changes = store_helper::get_delta_changes(&guard.store, shard_uid, block_hash)? + .ok_or_else(|| missing_delta_error(&block_hash))?; + changes.apply_to_flat_state(&mut store_update, guard.shard_uid); + let metadata = guard + .deltas + .get(&block_hash) + .ok_or_else(|| missing_delta_error(&block_hash))? + .metadata; + let block = metadata.block; + let block_height = block.height; + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head: block }), + ); + + guard.metrics.set_flat_head_height(block.height); + guard.flat_head = block; + + // Remove old deltas from disk and memory. + // Do it for each head update separately to ensure that old data is removed properly if node was + // interrupted in the middle. + // TODO (#7327): in case of long forks it can take a while and delay processing of some chunk. + // Consider avoid iterating over all blocks and make removals lazy. + let gc_height = metadata.block.height; + let hashes_to_remove: Vec<_> = guard + .deltas + .iter() + .filter(|(_, delta)| delta.metadata.block.height <= gc_height) + .map(|(block_hash, _)| block_hash) + .cloned() + .collect(); + for hash in hashes_to_remove { + store_helper::remove_delta(&mut store_update, shard_uid, hash); + guard.deltas.remove(&hash); + } + + store_update.commit().unwrap(); + debug!(target: "store", %shard_id, %block_hash, %block_height, "Moved flat storage head"); + } + guard.update_delta_metrics(); + + Ok(()) + } + + /// Adds a delta (including the changes and block info) to flat storage, + /// returns a StoreUpdate to store the delta on disk. Node that this StoreUpdate should be + /// committed to disk in one db transaction together with the rest of changes caused by block, + /// in case the node stopped or crashed in between and a block is on chain but its delta is not + /// stored or vice versa. + pub fn add_delta(&self, delta: FlatStateDelta) -> Result { + let mut guard = self.0.write().expect(super::POISONED_LOCK_ERR); + let shard_uid = guard.shard_uid; + let shard_id = shard_uid.shard_id(); + let block = &delta.metadata.block; + let block_hash = block.hash; + let block_height = block.height; + debug!(target: "store", %shard_id, %block_hash, %block_height, "Adding block to flat storage"); + if block.prev_hash != guard.flat_head.hash && !guard.deltas.contains_key(&block.prev_hash) { + return Err(guard.create_block_not_supported_error(&block_hash)); + } + let mut store_update = StoreUpdate::new(guard.store.storage.clone()); + store_helper::set_delta(&mut store_update, shard_uid, &delta); + let cached_changes: CachedFlatStateChanges = delta.changes.into(); + guard.deltas.insert( + block_hash, + CachedFlatStateDelta { metadata: delta.metadata, changes: Arc::new(cached_changes) }, + ); + guard.update_delta_metrics(); + + Ok(store_update) + } + + /// Clears all State key-value pairs from flat storage. + pub fn clear_state(&self, store_update: &mut StoreUpdate) -> Result<(), StorageError> { + let guard = self.0.write().expect(super::POISONED_LOCK_ERR); + let shard_uid = guard.shard_uid; + store_helper::remove_all_flat_state_values(store_update, shard_uid); + store_helper::remove_all_deltas(store_update, shard_uid); + store_helper::set_flat_storage_status(store_update, shard_uid, FlatStorageStatus::Empty); + guard.update_delta_metrics(); + Ok(()) + } + + pub(crate) fn get_head_hash(&self) -> CryptoHash { + let guard = self.0.read().expect(super::POISONED_LOCK_ERR); + guard.flat_head.hash + } + + pub(crate) fn shard_uid(&self) -> ShardUId { + let guard = self.0.read().expect(super::POISONED_LOCK_ERR); + guard.shard_uid + } + + /// Updates `move_head_enabled` and returns whether the change was done. + pub(crate) fn set_flat_head_update_mode(&self, enabled: bool) -> bool { + let mut guard = self.0.write().expect(crate::flat::POISONED_LOCK_ERR); + if enabled != guard.move_head_enabled { + guard.move_head_enabled = enabled; + true + } else { + false + } + } +} + +fn missing_delta_error(block_hash: &CryptoHash) -> FlatStorageError { + FlatStorageError::StorageInternalError(format!("delta does not exist for block {block_hash}")) +} + +#[cfg(test)] +mod tests { + use crate::flat::delta::{ + BlockWithChangesInfo, FlatStateChanges, FlatStateDelta, FlatStateDeltaMetadata, + }; + use crate::flat::manager::FlatStorageManager; + use crate::flat::storage::FlatStorageInner; + use crate::flat::test_utils::MockChain; + use crate::flat::types::FlatStorageError; + use crate::flat::{store_helper, FlatStorageReadyStatus, FlatStorageStatus}; + use crate::test_utils::create_test_store; + use crate::StorageError; + use assert_matches::assert_matches; + + use unc_o11y::testonly::init_test_logger; + use unc_primitives::hash::{hash, CryptoHash}; + use unc_primitives::shard_layout::ShardUId; + use unc_primitives::state::FlatStateValue; + use unc_primitives::types::BlockHeight; + use rand::{thread_rng, Rng}; + use std::collections::HashMap; + + #[test] + fn flat_storage_errors() { + // Create a chain with two forks. Set flat head to be at block 0. + let chain = MockChain::chain_with_two_forks(5); + let shard_uid = ShardUId::single_shard(); + let store = create_test_store(); + let mut store_update = store.store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head: chain.get_block(0) }), + ); + for i in 1..5 { + let delta = FlatStateDelta { + changes: FlatStateChanges::default(), + metadata: FlatStateDeltaMetadata { + block: chain.get_block(i), + prev_block_with_changes: None, + }, + }; + store_helper::set_delta(&mut store_update, shard_uid, &delta); + } + store_update.commit().unwrap(); + + let flat_storage_manager = FlatStorageManager::new(store.clone()); + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + let flat_storage = flat_storage_manager.get_flat_storage_for_shard(shard_uid).unwrap(); + + // Check `BlockNotSupported` errors which are fine to occur during regular block processing. + // First, check that flat head can be moved to block 1. + let flat_head_hash = chain.get_block_hash(1); + assert_eq!(flat_storage.update_flat_head(&flat_head_hash, true), Ok(())); + // Check that attempt to move flat head to block 2 results in error because it lays in unreachable fork. + let fork_block_hash = chain.get_block_hash(2); + assert_eq!( + flat_storage.update_flat_head(&fork_block_hash, true), + Err(FlatStorageError::BlockNotSupported((flat_head_hash, fork_block_hash))) + ); + // Check that attempt to move flat head to block 0 results in error because it is an unreachable parent. + let parent_block_hash = chain.get_block_hash(0); + assert_eq!( + flat_storage.update_flat_head(&parent_block_hash, true), + Err(FlatStorageError::BlockNotSupported((flat_head_hash, parent_block_hash))) + ); + // Check that attempt to move flat head to non-existent block results in the same error. + let not_existing_hash = hash(&[1, 2, 3]); + assert_eq!( + flat_storage.update_flat_head(¬_existing_hash, true), + Err(FlatStorageError::BlockNotSupported((flat_head_hash, not_existing_hash))) + ); + // Corrupt DB state for block 3 and try moving flat head to it. + // Should result in `StorageInternalError` indicating that flat storage is broken. + let mut store_update = store.store_update(); + store_helper::remove_delta(&mut store_update, shard_uid, chain.get_block_hash(3)); + store_update.commit().unwrap(); + assert_matches!( + flat_storage.update_flat_head(&chain.get_block_hash(3), true), + Err(FlatStorageError::StorageInternalError(_)) + ); + } + + #[test] + /// Builds a chain with occasional forks. + /// Checks that request to update flat head fail and are handled gracefully. + fn flat_storage_fork() { + init_test_logger(); + // Create a chain with two forks. Set flat head to be at block 0. + let num_blocks = 10 as BlockHeight; + + // Build a chain that looks lke this: + // 2 5 8 + // / / / + // 0-1---3-4---6-7---9 + // Note that forks [0,1,2], [3,4,5] and [6,7,8] form triples of consecutive blocks. + let chain = MockChain::build((0..num_blocks).collect(), |i| { + if i == 0 { + None + } else if i % 3 == 0 { + Some(i - 2) + } else { + Some(i - 1) + } + }); + let shard_uid = ShardUId::single_shard(); + let store = create_test_store(); + let mut store_update = store.store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head: chain.get_block(0) }), + ); + for i in 1..num_blocks { + let delta = FlatStateDelta { + changes: FlatStateChanges::default(), + metadata: FlatStateDeltaMetadata { + block: chain.get_block(i), + prev_block_with_changes: None, + }, + }; + store_helper::set_delta(&mut store_update, shard_uid, &delta); + } + store_update.commit().unwrap(); + + let flat_storage_manager = FlatStorageManager::new(store); + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + let flat_storage = flat_storage_manager.get_flat_storage_for_shard(shard_uid).unwrap(); + + // Simulates an actual sequence of calls to `update_flat_head()` in the + // presence of forks. + assert_eq!(flat_storage.update_flat_head(&chain.get_block_hash(0), false), Ok(())); + assert_eq!(flat_storage.update_flat_head(&chain.get_block_hash(3), false), Ok(())); + assert_matches!( + flat_storage.update_flat_head(&chain.get_block_hash(0), false), + Err(FlatStorageError::BlockNotSupported(_)) + ); + assert_eq!(flat_storage.update_flat_head(&chain.get_block_hash(6), false), Ok(())); + assert_matches!( + flat_storage.update_flat_head(&chain.get_block_hash(0), false), + Err(FlatStorageError::BlockNotSupported(_)) + ); + } + + #[test] + fn skipped_heights() { + // Create a linear chain where some heights are skipped. + let chain = MockChain::liunc_chain_with_skips(5); + let shard_uid = ShardUId::single_shard(); + let store = create_test_store(); + let mut store_update = store.store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head: chain.get_block(0) }), + ); + for i in 1..5 { + let delta = FlatStateDelta { + changes: FlatStateChanges::default(), + metadata: FlatStateDeltaMetadata { + block: chain.get_block(i * 2), + prev_block_with_changes: None, + }, + }; + store_helper::set_delta(&mut store_update, shard_uid, &delta); + } + store_update.commit().unwrap(); + + // Check that flat storage state is created correctly for chain which has skipped heights. + let flat_storage_manager = FlatStorageManager::new(store); + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + let flat_storage = flat_storage_manager.get_flat_storage_for_shard(shard_uid).unwrap(); + + // Check that flat head can be moved to block 8. + let flat_head_hash = chain.get_block_hash(8); + assert_eq!(flat_storage.update_flat_head(&flat_head_hash, false), Ok(())); + } + + // This tests basic use cases for FlatStorageChunkView and FlatStorage. + // We created a linear chain with no forks, start with flat head at the genesis block, then + // moves the flat head forward, which checking that chunk_view.get_ref() still returns the correct + // values and the state is being updated in store. + #[test] + fn flat_storage_sanity() { + // 1. Create a chain with 10 blocks with no forks. Set flat head to be at block 0. + // Block i sets value for key &[1] to &[i]. + let mut chain = MockChain::liunc_chain(10); + let shard_uid = ShardUId::single_shard(); + let store = create_test_store(); + let mut store_update = store.store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head: chain.get_block(0) }), + ); + store_helper::set_flat_state_value( + &mut store_update, + shard_uid, + vec![1], + Some(FlatStateValue::value_ref(&[0])), + ); + for i in 1..10 { + let delta = FlatStateDelta { + changes: FlatStateChanges::from([( + vec![1], + Some(FlatStateValue::value_ref(&[i as u8])), + )]), + metadata: FlatStateDeltaMetadata { + block: chain.get_block(i), + prev_block_with_changes: None, + }, + }; + store_helper::set_delta(&mut store_update, shard_uid, &delta); + } + store_update.commit().unwrap(); + + let flat_storage_manager = FlatStorageManager::new(store.clone()); + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + let flat_storage = flat_storage_manager.get_flat_storage_for_shard(shard_uid).unwrap(); + + // 2. Check that the chunk_view at block i reads the value of key &[1] as &[i] + for i in 0..10 { + let block_hash = chain.get_block_hash(i); + let blocks = flat_storage.get_blocks_to_head(&block_hash).unwrap(); + assert_eq!(blocks.len(), i as usize); + let chunk_view = flat_storage_manager.chunk_view(shard_uid, block_hash).unwrap(); + assert_eq!( + chunk_view.get_value(&[1]).unwrap(), + Some(FlatStateValue::value_ref(&[i as u8])) + ); + } + + // 3. Create a new block that deletes &[1] and add a new value &[2] + // Add the block to flat storage. + let hash = chain.create_block(); + let store_update = flat_storage + .add_delta(FlatStateDelta { + changes: FlatStateChanges::from([ + (vec![1], None), + (vec![2], Some(FlatStateValue::value_ref(&[1]))), + ]), + metadata: FlatStateDeltaMetadata { + block: chain.get_block_info(&hash), + prev_block_with_changes: None, + }, + }) + .unwrap(); + store_update.commit().unwrap(); + + // 4. Create a flat_state0 at block 10 and flat_state1 at block 4 + // Verify that they return the correct values + let blocks = flat_storage.get_blocks_to_head(&chain.get_block_hash(10)).unwrap(); + assert_eq!(blocks.len(), 10); + let chunk_view0 = + flat_storage_manager.chunk_view(shard_uid, chain.get_block_hash(10)).unwrap(); + let chunk_view1 = + flat_storage_manager.chunk_view(shard_uid, chain.get_block_hash(4)).unwrap(); + assert_eq!(chunk_view0.get_value(&[1]).unwrap(), None); + assert_eq!(chunk_view0.get_value(&[2]).unwrap(), Some(FlatStateValue::value_ref(&[1]))); + assert_eq!(chunk_view1.get_value(&[1]).unwrap(), Some(FlatStateValue::value_ref(&[4]))); + assert_eq!(chunk_view1.get_value(&[2]).unwrap(), None); + assert_matches!( + store_helper::get_delta_changes(&store, shard_uid, chain.get_block_hash(5)).unwrap(), + Some(_) + ); + assert_matches!( + store_helper::get_delta_changes(&store, shard_uid, chain.get_block_hash(10)).unwrap(), + Some(_) + ); + + // 5. Move the flat head to block 5, verify that chunk_view0 still returns the same values + // and chunk_view1 returns an error. Also check that DBCol::FlatState is updated correctly + flat_storage.update_flat_head(&chain.get_block_hash(5), true).unwrap(); + assert_eq!( + store_helper::get_flat_state_value(&store, shard_uid, &[1]).unwrap(), + Some(FlatStateValue::value_ref(&[5])) + ); + let blocks = flat_storage.get_blocks_to_head(&chain.get_block_hash(10)).unwrap(); + assert_eq!(blocks.len(), 5); + assert_eq!(chunk_view0.get_value(&[1]).unwrap(), None); + assert_eq!(chunk_view0.get_value(&[2]).unwrap(), Some(FlatStateValue::value_ref(&[1]))); + assert_matches!( + chunk_view1.get_value(&[1]), + Err(StorageError::FlatStorageBlockNotSupported(_)) + ); + assert_matches!( + store_helper::get_delta_changes(&store, shard_uid, chain.get_block_hash(5)).unwrap(), + None + ); + assert_matches!( + store_helper::get_delta_changes(&store, shard_uid, chain.get_block_hash(10)).unwrap(), + Some(_) + ); + + // 6. Move the flat head to block 10, verify that chunk_view0 still returns the same values + // Also checks that DBCol::FlatState is updated correctly. + flat_storage.update_flat_head(&chain.get_block_hash(10), true).unwrap(); + let blocks = flat_storage.get_blocks_to_head(&chain.get_block_hash(10)).unwrap(); + assert_eq!(blocks.len(), 0); + assert_eq!(store_helper::get_flat_state_value(&store, shard_uid, &[1]).unwrap(), None); + assert_eq!( + store_helper::get_flat_state_value(&store, shard_uid, &[2]).unwrap(), + Some(FlatStateValue::value_ref(&[1])) + ); + assert_eq!(chunk_view0.get_value(&[1]).unwrap(), None); + assert_eq!(chunk_view0.get_value(&[2]).unwrap(), Some(FlatStateValue::value_ref(&[1]))); + assert_matches!( + store_helper::get_delta_changes(&store, shard_uid, chain.get_block_hash(10)).unwrap(), + None + ); + } + + #[test] + fn flat_storage_with_hops() { + init_test_logger(); + // 1. Create a chain with no forks. Set flat head to be at block 0. + let num_blocks = 15; + let chain = MockChain::liunc_chain(num_blocks); + let shard_uid = ShardUId::single_shard(); + let store = create_test_store(); + let mut store_update = store.store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head: chain.get_block(0) }), + ); + store_helper::set_flat_state_value( + &mut store_update, + shard_uid, + vec![1], + Some(FlatStateValue::value_ref(&[0])), + ); + store_update.commit().unwrap(); + + for i in 1..num_blocks as BlockHeight { + let mut store_update = store.store_update(); + let changes = if i % 3 == 0 { + // Add a change. + FlatStateChanges::from([(vec![1], Some(FlatStateValue::value_ref(&[i as u8])))]) + } else { + // No changes. + FlatStateChanges::from([]) + }; + + // Simulates `Chain::save_flat_state_changes()`. + let prev_block_with_changes = if changes.0.is_empty() { + store_helper::get_prev_block_with_changes( + &store, + shard_uid, + chain.get_block(i).hash, + chain.get_block(i).prev_hash, + ) + .unwrap() + } else { + None + }; + let delta = FlatStateDelta { + changes, + metadata: FlatStateDeltaMetadata { + block: chain.get_block(i), + prev_block_with_changes, + }, + }; + tracing::info!(?i, ?delta); + store_helper::set_delta(&mut store_update, shard_uid, &delta); + store_update.commit().unwrap(); + } + + let flat_storage_manager = FlatStorageManager::new(store.clone()); + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + let flat_storage = flat_storage_manager.get_flat_storage_for_shard(shard_uid).unwrap(); + + // 2. Check that the chunk_view at block i reads the value of key &[1] as &[round_down_to_a_multiple_of_3(i)] + for i in 0..num_blocks as BlockHeight { + let block_hash = chain.get_block_hash(i); + let blocks = flat_storage.get_blocks_to_head(&block_hash).unwrap(); + let chunk_view = flat_storage_manager.chunk_view(shard_uid, block_hash).unwrap(); + let value = chunk_view.get_value(&[1]).unwrap(); + tracing::info!(?i, ?block_hash, ?value, blocks_to_head = ?blocks); + assert_eq!(value, Some(FlatStateValue::value_ref(&[((i / 3) * 3) as u8]))); + + // Don't check the first block because it may be a block with no changes. + for i in 1..blocks.len() { + let block_hash = blocks[i]; + let delta = store_helper::get_delta_changes(&store.clone(), shard_uid, block_hash) + .unwrap() + .unwrap(); + assert!( + !delta.0.is_empty(), + "i: {i}, block_hash: {block_hash:?}, delta: {delta:?}" + ); + } + } + + // 3. Simulate moving head forward with a delay of two from the tip. + // flat head is chosen to keep 2 blocks between the suggested head and the chosen head, + // resulting in <=4 blocks on the way from the tip to the chosen head. + for i in 2..num_blocks as BlockHeight { + let final_block_hash = chain.get_block_hash(i - 2); + flat_storage.update_flat_head(&final_block_hash, false).unwrap(); + + let block_hash = chain.get_block_hash(i); + let blocks = flat_storage.get_blocks_to_head(&block_hash).unwrap(); + + assert!( + blocks.len() <= 2 + FlatStorageInner::BLOCKS_WITH_CHANGES_FLAT_HEAD_GAP as usize + ); + } + } + + #[test] + /// Move flat storage to an exact height when flat storage has no changes. + fn flat_storage_with_no_changes() { + init_test_logger(); + let num_blocks = 10; + let chain = MockChain::liunc_chain(num_blocks); + let shard_uid = ShardUId::single_shard(); + let store = create_test_store(); + let mut store_update = store.store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head: chain.get_block(0) }), + ); + store_helper::set_flat_state_value( + &mut store_update, + shard_uid, + vec![1], + Some(FlatStateValue::value_ref(&[0])), + ); + store_update.commit().unwrap(); + + for i in 1..num_blocks as BlockHeight { + let mut store_update = store.store_update(); + // No changes. + let changes = FlatStateChanges::default(); + // Simulates `Chain::save_flat_state_changes()`. + let prev_block_with_changes = store_helper::get_prev_block_with_changes( + &store, + shard_uid, + chain.get_block(i).hash, + chain.get_block(i).prev_hash, + ) + .unwrap(); + let delta = FlatStateDelta { + changes, + metadata: FlatStateDeltaMetadata { + block: chain.get_block(i), + prev_block_with_changes, + }, + }; + tracing::info!(?i, ?delta); + store_helper::set_delta(&mut store_update, shard_uid, &delta); + store_update.commit().unwrap(); + } + + let flat_storage_manager = FlatStorageManager::new(store); + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + let flat_storage = flat_storage_manager.get_flat_storage_for_shard(shard_uid).unwrap(); + + let hashes = (0..num_blocks as BlockHeight) + .map(|height| (chain.get_block_hash(height), height)) + .collect::>(); + + let block_hash = chain.get_block_hash((num_blocks - 1) as BlockHeight); + flat_storage.update_flat_head(&block_hash, true).unwrap(); + + let flat_head_hash = flat_storage.get_head_hash(); + let flat_head_height = hashes.get(&flat_head_hash).unwrap(); + + assert_eq!(*flat_head_height, (num_blocks - 1) as BlockHeight); + } + + #[test] + fn flat_storage_with_hops_random() { + init_test_logger(); + // 1. Create a long chain with no forks. Set flat head to be at block 0. + let num_blocks = 1000; + let mut rng = thread_rng(); + let chain = MockChain::liunc_chain(num_blocks); + let shard_uid = ShardUId::single_shard(); + let store = create_test_store(); + let mut store_update = store.store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head: chain.get_block(0) }), + ); + store_helper::set_flat_state_value( + &mut store_update, + shard_uid, + vec![1], + Some(FlatStateValue::value_ref(&[0])), + ); + store_update.commit().unwrap(); + + for i in 1..num_blocks as BlockHeight { + let mut store_update = store.store_update(); + let changes = if rng.gen_bool(0.3) { + // Add a change. + FlatStateChanges::from([(vec![1], Some(FlatStateValue::value_ref(&[i as u8])))]) + } else { + // No changes. + FlatStateChanges::default() + }; + + // Simulates `Chain::save_flat_state_changes()`. + let prev_block_with_changes = if changes.0.is_empty() { + store_helper::get_prev_block_with_changes( + &store, + shard_uid, + chain.get_block(i).hash, + chain.get_block(i).prev_hash, + ) + .unwrap() + } else { + None + }; + let delta = FlatStateDelta { + changes, + metadata: FlatStateDeltaMetadata { + block: chain.get_block(i), + prev_block_with_changes, + }, + }; + tracing::info!(?i, ?delta); + store_helper::set_delta(&mut store_update, shard_uid, &delta); + store_update.commit().unwrap(); + } + + let flat_storage_manager = FlatStorageManager::new(store.clone()); + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + let flat_storage = flat_storage_manager.get_flat_storage_for_shard(shard_uid).unwrap(); + + let hashes = (0..num_blocks as BlockHeight) + .map(|height| (chain.get_block_hash(height), height)) + .collect::>(); + + // 2. Simulate moving head, with the suggested head lagging by 2 blocks behind the tip. + let mut max_lag = None; + for i in 2..num_blocks as BlockHeight { + let final_block_hash = chain.get_block_hash(i - 2); + flat_storage.update_flat_head(&final_block_hash, false).unwrap(); + + let block_hash = chain.get_block_hash(i); + let blocks = flat_storage.get_blocks_to_head(&block_hash).unwrap(); + assert!( + blocks.len() <= 2 + FlatStorageInner::BLOCKS_WITH_CHANGES_FLAT_HEAD_GAP as usize + ); + // Don't check the first block because it may be a block with no changes. + for i in 1..blocks.len() { + let block_hash = blocks[i]; + let delta = store_helper::get_delta_changes(&store.clone(), shard_uid, block_hash) + .unwrap() + .unwrap(); + assert!( + !delta.0.is_empty(), + "i: {i}, block_hash: {block_hash:?}, delta: {delta:?}" + ); + } + + let flat_head_hash = flat_storage.get_head_hash(); + let flat_head_height = hashes.get(&flat_head_hash).unwrap(); + + let flat_head_lag = i - flat_head_height; + let delta = store_helper::get_delta_changes(&store.clone(), shard_uid, block_hash) + .unwrap() + .unwrap(); + let has_changes = !delta.0.is_empty(); + tracing::info!(?i, has_changes, ?flat_head_lag); + max_lag = max_lag.max(Some(flat_head_lag)); + } + tracing::info!(?max_lag); + } + + #[test] + fn test_new_flat_head() { + init_test_logger(); + + let shard_uid = ShardUId::single_shard(); + + // Case 1. Each block has flat state changes. + { + tracing::info!("Case 1"); + let num_blocks = 10; + let chain = MockChain::liunc_chain(num_blocks); + let store = create_test_store(); + let mut store_update = store.store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head: chain.get_block(0) }), + ); + store_helper::set_flat_state_value( + &mut store_update, + shard_uid, + vec![1], + Some(FlatStateValue::value_ref(&[0])), + ); + store_update.commit().unwrap(); + + for i in 1..num_blocks as BlockHeight { + let mut store_update = store.store_update(); + // Add a change. + let changes = FlatStateChanges::from([( + vec![1], + Some(FlatStateValue::value_ref(&[i as u8])), + )]); + let delta = FlatStateDelta { + changes, + metadata: FlatStateDeltaMetadata { + block: chain.get_block(i), + prev_block_with_changes: None, + }, + }; + tracing::info!(?i, ?delta); + store_helper::set_delta(&mut store_update, shard_uid, &delta); + store_update.commit().unwrap(); + } + + let flat_storage_manager = FlatStorageManager::new(store); + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + let flat_storage = flat_storage_manager.get_flat_storage_for_shard(shard_uid).unwrap(); + let guard = flat_storage.0.write().expect(crate::flat::POISONED_LOCK_ERR); + + for i in 0..num_blocks as BlockHeight { + let block_hash = chain.get_block_hash(i); + let new_head = guard.test_get_new_flat_head(block_hash, false).unwrap(); + tracing::info!(i, ?block_hash, ?new_head); + if i == 0 { + assert_eq!(new_head, chain.get_block_hash(0)); + } else { + assert_eq!(new_head, chain.get_block_hash(i - 1)); + } + } + } + + // Case 2. Even-numbered blocks have flat changes + { + tracing::info!("Case 2"); + let num_blocks = 20; + let chain = MockChain::liunc_chain(num_blocks); + let store = create_test_store(); + let mut store_update = store.store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head: chain.get_block(0) }), + ); + store_helper::set_flat_state_value( + &mut store_update, + shard_uid, + vec![1], + Some(FlatStateValue::value_ref(&[0])), + ); + store_update.commit().unwrap(); + + for i in 1..num_blocks as BlockHeight { + let mut store_update = store.store_update(); + let (changes, prev_block_with_changes) = if i % 2 == 0 { + // Add a change. + ( + FlatStateChanges::from([( + vec![1], + Some(FlatStateValue::value_ref(&[i as u8])), + )]), + None, + ) + } else { + // No changes. + ( + FlatStateChanges::default(), + Some(BlockWithChangesInfo { + hash: chain.get_block_hash(i - 1), + height: i - 1, + }), + ) + }; + + let delta = FlatStateDelta { + changes, + metadata: FlatStateDeltaMetadata { + block: chain.get_block(i), + prev_block_with_changes, + }, + }; + tracing::info!(?i, ?delta); + store_helper::set_delta(&mut store_update, shard_uid, &delta); + store_update.commit().unwrap(); + } + + let flat_storage_manager = FlatStorageManager::new(store); + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + let flat_storage = flat_storage_manager.get_flat_storage_for_shard(shard_uid).unwrap(); + let guard = flat_storage.0.write().expect(crate::flat::POISONED_LOCK_ERR); + + // A chain looks like this: + // X-O-X-O-X-O-... + // where X is a block with flat state changes and O is a block without flat state changes. + for i in 0..num_blocks as BlockHeight { + let new_head = guard.get_new_flat_head(chain.get_block_hash(i), false).unwrap(); + if i <= 3 { + assert_eq!(new_head, chain.get_block_hash(0)); + } else { + // if i is odd, then it's pointing at an O, and the head is expected to be 3 blocks ago. + // i + // | + // v + // X-O-X-O-X-O-X-O + // ^ + // | + // new_head + // + // if i is even, then it's pointing at an X, and the head is expected to be the previous X: + // i + // | + // v + // X-O-X-O-X-O-X-O + // ^ + // | + // new_head + + // Both of these cases can be computed as rounding (i-2) down to a multiple of 2. + assert_eq!(new_head, chain.get_block_hash(((i - 2) / 2) * 2)); + } + } + } + + // Case 3. Triplets of blocks: HasChanges, NoChanges, HasChanges. + { + tracing::info!("Case 3"); + let num_blocks = 20; + let chain = MockChain::liunc_chain(num_blocks); + let store = create_test_store(); + let mut store_update = store.store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head: chain.get_block(0) }), + ); + store_helper::set_flat_state_value( + &mut store_update, + shard_uid, + vec![1], + Some(FlatStateValue::value_ref(&[0])), + ); + store_update.commit().unwrap(); + + for i in 1..num_blocks as BlockHeight { + let mut store_update = store.store_update(); + let (changes, prev_block_with_changes) = if i % 3 == 1 { + // No changes. + ( + FlatStateChanges::default(), + Some(BlockWithChangesInfo { + hash: chain.get_block_hash(i - 1), + height: i - 1, + }), + ) + } else { + // Add a change. + ( + FlatStateChanges::from([( + vec![1], + Some(FlatStateValue::value_ref(&[i as u8])), + )]), + None, + ) + }; + + let delta = FlatStateDelta { + changes, + metadata: FlatStateDeltaMetadata { + block: chain.get_block(i), + prev_block_with_changes, + }, + }; + tracing::info!(?i, ?delta); + store_helper::set_delta(&mut store_update, shard_uid, &delta); + store_update.commit().unwrap(); + } + + let flat_storage_manager = FlatStorageManager::new(store); + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + let flat_storage = flat_storage_manager.get_flat_storage_for_shard(shard_uid).unwrap(); + let guard = flat_storage.0.write().expect(crate::flat::POISONED_LOCK_ERR); + + for i in 0..num_blocks as BlockHeight { + let new_head = guard.get_new_flat_head(chain.get_block_hash(i), false).unwrap(); + // if i%3 == 0 + // i + // | + // v + // X-O-X-X-O-X-X-O-X + // ^ + // | + // new_head + // + // if i%3 == 1 + // i + // | + // v + // X-O-X-X-O-X-X-O-X + // ^ + // | + // new_head + // + // if i%3 == 2 + // i + // | + // v + // X-O-X-X-O-X-X-O-X + // ^ + // | + // new_head + if i <= 2 { + assert_eq!(new_head, chain.get_block_hash(0)); + } else if i % 3 == 0 { + assert_eq!(new_head, chain.get_block_hash(i - 1)); + } else { + assert_eq!(new_head, chain.get_block_hash(i - 2)); + } + } + } + } +} diff --git a/core/store/src/flat/store_helper.rs b/core/store/src/flat/store_helper.rs new file mode 100644 index 000000000..01e0802e3 --- /dev/null +++ b/core/store/src/flat/store_helper.rs @@ -0,0 +1,319 @@ +//! This file contains helper functions for accessing flat storage data in DB +//! TODO(#8577): remove this file and move functions to the corresponding structs + +use super::delta::{FlatStateDelta, FlatStateDeltaMetadata}; +use super::types::{ + FlatStateIterator, FlatStateValuesInliningMigrationStatus, FlatStorageResult, FlatStorageStatus, +}; +use crate::db::FLAT_STATE_VALUES_INLINING_MIGRATION_STATUS_KEY; +use crate::flat::delta::{BlockWithChangesInfo, FlatStateChanges, KeyForFlatStateDelta}; +use crate::flat::types::FlatStorageError; +use crate::flat::FlatStorageReadyStatus; +use crate::{DBCol, Store, StoreUpdate}; +use borsh::BorshDeserialize; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::state::FlatStateValue; +use std::io; + +pub fn get_delta_changes( + store: &Store, + shard_uid: ShardUId, + block_hash: CryptoHash, +) -> FlatStorageResult> { + let key = KeyForFlatStateDelta { shard_uid, block_hash }; + store.get_ser::(DBCol::FlatStateChanges, &key.to_bytes()).map_err(|err| { + FlatStorageError::StorageInternalError(format!( + "failed to read delta changes for {key:?}: {err}" + )) + }) +} + +pub fn get_all_deltas_metadata( + store: &Store, + shard_uid: ShardUId, +) -> FlatStorageResult> { + store + .iter_prefix_ser(DBCol::FlatStateDeltaMetadata, &shard_uid.to_bytes()) + .map(|res| { + res.map(|(_, value)| value).map_err(|err| { + FlatStorageError::StorageInternalError(format!( + "failed to read delta metadata: {err}" + )) + }) + }) + .collect() +} + +/// Retrieves a row of `FlatStateDeltaMetadata` for the given key. +fn get_delta_metadata( + store: &Store, + shard_uid: ShardUId, + block_hash: CryptoHash, +) -> FlatStorageResult> { + let key = KeyForFlatStateDelta { shard_uid, block_hash }.to_bytes(); + store.get_ser(DBCol::FlatStateDeltaMetadata, &key).map_err(|err| { + FlatStorageError::StorageInternalError(format!( + "failed to read delta metadata for {key:?}: {err}" + )) + }) +} + +pub fn get_prev_block_with_changes( + store: &Store, + shard_uid: ShardUId, + block_hash: CryptoHash, + prev_hash: CryptoHash, +) -> FlatStorageResult> { + let prev_delta_metadata = get_delta_metadata(store, shard_uid, prev_hash)?; + let prev_block_with_changes = match prev_delta_metadata { + None => { + // DeltaMetadata not found, which means the prev block is the flat head. + let flat_storage_status = get_flat_storage_status(store, shard_uid)?; + match flat_storage_status { + FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head }) => { + if flat_head.hash == prev_hash { + Some(BlockWithChangesInfo { hash: prev_hash, height: flat_head.height }) + } else { + tracing::error!(target: "store", ?block_hash, ?prev_hash, "Missing delta metadata"); + None + } + } + // Don't do any performance optimizations while flat storage is not ready. + _ => None, + } + } + Some(metadata) => { + // If the prev block contains `prev_block_with_changes`, then use that value. + // Otherwise reference the prev block. + Some(metadata.prev_block_with_changes.unwrap_or(BlockWithChangesInfo { + hash: metadata.block.hash, + height: metadata.block.height, + })) + } + }; + Ok(prev_block_with_changes) +} + +pub fn set_delta(store_update: &mut StoreUpdate, shard_uid: ShardUId, delta: &FlatStateDelta) { + let key = KeyForFlatStateDelta { shard_uid, block_hash: delta.metadata.block.hash }.to_bytes(); + store_update + .set_ser(DBCol::FlatStateChanges, &key, &delta.changes) + .expect("Borsh should not have failed here"); + store_update + .set_ser(DBCol::FlatStateDeltaMetadata, &key, &delta.metadata) + .expect("Borsh should not have failed here"); +} + +pub fn remove_delta(store_update: &mut StoreUpdate, shard_uid: ShardUId, block_hash: CryptoHash) { + let key = KeyForFlatStateDelta { shard_uid, block_hash }.to_bytes(); + store_update.delete(DBCol::FlatStateChanges, &key); + store_update.delete(DBCol::FlatStateDeltaMetadata, &key); +} + +fn remove_range_by_shard_uid(store_update: &mut StoreUpdate, shard_uid: ShardUId, col: DBCol) { + let key_from = shard_uid.to_bytes(); + let key_to = ShardUId::next_shard_prefix(&key_from); + store_update.delete_range(col, &key_from, &key_to); +} + +pub fn remove_all_deltas(store_update: &mut StoreUpdate, shard_uid: ShardUId) { + remove_range_by_shard_uid(store_update, shard_uid, DBCol::FlatStateChanges); + remove_range_by_shard_uid(store_update, shard_uid, DBCol::FlatStateDeltaMetadata); +} + +pub fn remove_all_flat_state_values(store_update: &mut StoreUpdate, shard_uid: ShardUId) { + remove_range_by_shard_uid(store_update, shard_uid, DBCol::FlatState); +} + +pub fn remove_all_state_values(store_update: &mut StoreUpdate, shard_uid: ShardUId) { + remove_range_by_shard_uid(store_update, shard_uid, DBCol::State); +} + +pub fn encode_flat_state_db_key(shard_uid: ShardUId, key: &[u8]) -> Vec { + let mut buffer = vec![]; + buffer.extend_from_slice(&shard_uid.to_bytes()); + buffer.extend_from_slice(key); + buffer +} + +pub fn decode_flat_state_db_key(key: &[u8]) -> io::Result<(ShardUId, Vec)> { + if key.len() < 8 { + return Err(io::Error::other(format!( + "expected FlatState key length to be at least 8: {key:?}" + ))); + } + let (shard_uid_bytes, trie_key) = key.split_at(8); + let shard_uid = shard_uid_bytes.try_into().map_err(|err| { + io::Error::other(format!("failed to decode shard_uid as part of FlatState key: {err}")) + })?; + Ok((shard_uid, trie_key.to_vec())) +} + +pub fn get_flat_state_values_inlining_migration_status( + store: &Store, +) -> FlatStorageResult { + store + .get_ser(DBCol::Misc, FLAT_STATE_VALUES_INLINING_MIGRATION_STATUS_KEY) + .map(|status| status.unwrap_or(FlatStateValuesInliningMigrationStatus::Empty)) + .map_err(|err| { + FlatStorageError::StorageInternalError(format!( + "failed to read FlatState values inlining migration status: {err}" + )) + }) +} + +pub fn set_flat_state_values_inlining_migration_status( + store: &Store, + status: FlatStateValuesInliningMigrationStatus, +) -> FlatStorageResult<()> { + let mut store_update = store.store_update(); + store_update + .set_ser(DBCol::Misc, FLAT_STATE_VALUES_INLINING_MIGRATION_STATUS_KEY, &status) + .expect("Borsh should not have failed here"); + store_update.commit().map_err(|err| { + FlatStorageError::StorageInternalError(format!( + "failed to commit FlatState values inlining migration status: {err}" + )) + }) +} + +pub(crate) fn get_flat_state_value( + store: &Store, + shard_uid: ShardUId, + key: &[u8], +) -> FlatStorageResult> { + let db_key = encode_flat_state_db_key(shard_uid, key); + store.get_ser(DBCol::FlatState, &db_key).map_err(|err| { + FlatStorageError::StorageInternalError(format!("failed to read FlatState value: {err}")) + }) +} + +// TODO(#8577): make pub(crate) once flat storage creator is moved inside `flat` module. +pub fn set_flat_state_value( + store_update: &mut StoreUpdate, + shard_uid: ShardUId, + key: Vec, + value: Option, +) { + let db_key = encode_flat_state_db_key(shard_uid, &key); + match value { + Some(value) => store_update + .set_ser(DBCol::FlatState, &db_key, &value) + .expect("Borsh should not have failed here"), + None => store_update.delete(DBCol::FlatState, &db_key), + } +} + +pub fn get_flat_storage_status( + store: &Store, + shard_uid: ShardUId, +) -> FlatStorageResult { + store + .get_ser(DBCol::FlatStorageStatus, &shard_uid.to_bytes()) + .map(|status| status.unwrap_or(FlatStorageStatus::Empty)) + .map_err(|err| { + FlatStorageError::StorageInternalError(format!( + "failed to read flat storage status: {err}" + )) + }) +} + +pub fn set_flat_storage_status( + store_update: &mut StoreUpdate, + shard_uid: ShardUId, + status: FlatStorageStatus, +) { + store_update + .set_ser(DBCol::FlatStorageStatus, &shard_uid.to_bytes(), &status) + .expect("Borsh should not have failed here") +} + +/// Returns iterator over flat storage entries for a given shard and range of +/// state keys. `None` means that there is no bound in respective direction. +/// It reads data only from `FlatState` column which represents the state at +/// flat storage head. Reads only commited changes. +pub fn iter_flat_state_entries<'a>( + shard_uid: ShardUId, + store: &'a Store, + from: Option<&[u8]>, + to: Option<&[u8]>, +) -> FlatStateIterator<'a> { + // If left direction is unbounded, encoded `shard_uid` serves as the + // smallest possible key in DB for the shard. + let db_key_from = match from { + Some(from) => encode_flat_state_db_key(shard_uid, from), + None => shard_uid.to_bytes().to_vec(), + }; + // If right direction is unbounded, `ShardUId::next_shard_prefix` serves as + // the key which is strictly bigger than all keys in DB for this shard and + // still doesn't include keys from other shards. + let db_key_to = match to { + Some(to) => encode_flat_state_db_key(shard_uid, to), + None => ShardUId::next_shard_prefix(&shard_uid.to_bytes()).to_vec(), + }; + let iter = + store.iter_range(DBCol::FlatState, Some(&db_key_from), Some(&db_key_to)).map(|result| { + match result { + Ok((key, value)) => Ok(( + decode_flat_state_db_key(&key) + .map_err(|err| { + FlatStorageError::StorageInternalError(format!( + "invalid FlatState key format: {err}" + )) + })? + .1, + FlatStateValue::try_from_slice(&value).map_err(|err| { + FlatStorageError::StorageInternalError(format!( + "invalid FlatState value format: {err}" + )) + })?, + )), + Err(err) => Err(FlatStorageError::StorageInternalError(format!( + "FlatState iterator error: {err}" + ))), + } + }); + Box::new(iter) +} + +#[cfg(test)] +mod tests { + use crate::flat::store_helper::set_flat_state_value; + use crate::test_utils::create_test_store; + use unc_primitives::shard_layout::ShardUId; + use unc_primitives::state::FlatStateValue; + + #[test] + fn iter_flat_state_entries() { + // Setup shards and store + let store = create_test_store(); + let shard_uids = [0, 1, 2].map(|id| ShardUId { version: 0, shard_id: id }); + + for (i, shard_uid) in shard_uids.iter().enumerate() { + let mut store_update = store.store_update(); + let key: Vec = vec![0, 1, i as u8]; + let val: Vec = vec![0, 1, 2, i as u8]; + + // Add value to FlatState + set_flat_state_value( + &mut store_update, + *shard_uid, + key.clone(), + Some(FlatStateValue::inlined(&val)), + ); + + store_update.commit().unwrap(); + } + + for (i, shard_uid) in shard_uids.iter().enumerate() { + let entries: Vec<_> = + super::iter_flat_state_entries(*shard_uid, &store, None, None).collect(); + assert_eq!(entries.len(), 1); + let key: Vec = vec![0, 1, i as u8]; + let val: Vec = vec![0, 1, 2, i as u8]; + + assert_eq!(entries, vec![Ok((key, FlatStateValue::inlined(&val)))]); + } + } +} diff --git a/core/store/src/flat/test_utils.rs b/core/store/src/flat/test_utils.rs new file mode 100644 index 000000000..f0f0e4318 --- /dev/null +++ b/core/store/src/flat/test_utils.rs @@ -0,0 +1,100 @@ +use super::BlockInfo; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::types::BlockHeight; +use std::collections::HashMap; + +pub struct MockChain { + height_to_hashes: HashMap, + blocks: HashMap, + head_height: BlockHeight, +} + +impl MockChain { + pub fn get_block_info(&self, block_hash: &CryptoHash) -> BlockInfo { + *self.blocks.get(block_hash).unwrap() + } + + pub fn block_hash(height: BlockHeight) -> CryptoHash { + hash(&borsh::to_vec(&height).unwrap()) + } + + /// Build a chain with given set of heights and a function mapping block heights to heights of their parents. + pub fn build( + heights: Vec, + get_parent: fn(BlockHeight) -> Option, + ) -> MockChain { + let height_to_hashes: HashMap<_, _> = + heights.iter().cloned().map(|height| (height, MockChain::block_hash(height))).collect(); + let blocks = heights + .iter() + .cloned() + .map(|height| { + let hash = *height_to_hashes.get(&height).unwrap(); + let prev_hash = match get_parent(height) { + None => CryptoHash::default(), + Some(parent_height) => *height_to_hashes.get(&parent_height).unwrap(), + }; + (hash, BlockInfo { hash, height, prev_hash }) + }) + .collect(); + MockChain { height_to_hashes, blocks, head_height: *heights.last().unwrap() } + } + + // Create a chain with no forks with length n. + pub fn liunc_chain(n: usize) -> MockChain { + Self::build((0..n as BlockHeight).collect(), |i| if i == 0 { None } else { Some(i - 1) }) + } + + // Create a linear chain of length n where blocks with odd numbers are skipped: + // 0 -> 2 -> 4 -> ... + pub fn liunc_chain_with_skips(n: usize) -> MockChain { + Self::build((0..n as BlockHeight).map(|i| i * 2).collect(), |i| { + if i == 0 { + None + } else { + Some(i - 2) + } + }) + } + + // Create a chain with two forks, where blocks 1 and 2 have a parent block 0, and each next block H + // has a parent block H-2: + // 0 |-> 1 -> 3 -> 5 -> ... + // --> 2 -> 4 -> 6 -> ... + pub fn chain_with_two_forks(n: usize) -> MockChain { + Self::build( + (0..n as BlockHeight).collect(), + |i| { + if i == 0 { + None + } else { + Some(i.max(2) - 2) + } + }, + ) + } + + pub fn get_block_hash(&self, height: BlockHeight) -> CryptoHash { + *self.height_to_hashes.get(&height).unwrap() + } + + pub fn get_block(&self, height: BlockHeight) -> BlockInfo { + self.blocks[&self.height_to_hashes[&height]] + } + + /// create a new block on top the current chain head, return the new block hash + pub fn create_block(&mut self) -> CryptoHash { + let hash = MockChain::block_hash(self.head_height + 1); + self.height_to_hashes.insert(self.head_height + 1, hash); + self.blocks.insert( + hash, + BlockInfo { + hash, + height: self.head_height + 1, + prev_hash: self.get_block_hash(self.head_height), + }, + ); + self.head_height += 1; + hash + } +} diff --git a/core/store/src/flat/types.rs b/core/store/src/flat/types.rs new file mode 100644 index 000000000..c8bff5e73 --- /dev/null +++ b/core/store/src/flat/types.rs @@ -0,0 +1,128 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_primitives::errors::StorageError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::state::FlatStateValue; +use unc_primitives::types::BlockHeight; + +#[derive(BorshSerialize, BorshDeserialize, Debug, Copy, Clone, PartialEq, Eq, serde::Serialize)] +pub struct BlockInfo { + pub hash: CryptoHash, + pub height: BlockHeight, + pub prev_hash: CryptoHash, +} + +impl BlockInfo { + pub fn genesis(hash: CryptoHash, height: BlockHeight) -> Self { + Self { hash, height, prev_hash: CryptoHash::default() } + } +} + +#[derive(strum::AsRefStr, strum::Display, Debug, PartialEq, Eq, thiserror::Error)] +pub enum FlatStorageError { + /// This means we can't find a path from `flat_head` to the block. Includes + /// `flat_head` hash and block hash, respectively. + /// Should not result in node panic, because flat head can move during processing + /// of some chunk. + BlockNotSupported((CryptoHash, CryptoHash)), + /// Internal error, caused by DB or in-memory data corruption. Should result + /// in panic, because correctness of flat storage is not guaranteed afterwards. + StorageInternalError(String), +} + +impl From for StorageError { + fn from(err: FlatStorageError) -> Self { + match err { + FlatStorageError::BlockNotSupported((head_hash, block_hash)) => { + StorageError::FlatStorageBlockNotSupported(format!( + "FlatStorage with head {:?} does not support this block {:?}", + head_hash, block_hash + )) + } + FlatStorageError::StorageInternalError(_) => StorageError::StorageInternalError, + } + } +} + +pub type FlatStorageResult = Result; + +#[derive(BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq)] +pub enum FlatStateValuesInliningMigrationStatus { + Empty, + InProgress, + Finished, +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, serde::Serialize)] +pub enum FlatStorageStatus { + /// Flat Storage is not supported. + Disabled, + /// Flat Storage is empty: either wasn't created yet or was deleted. + Empty, + /// Flat Storage is in the process of being created. + Creation(FlatStorageCreationStatus), + /// Flat Storage is ready to be used. + Ready(FlatStorageReadyStatus), +} + +impl Into for &FlatStorageStatus { + /// Converts status to integer to export to prometheus later. + /// Cast inside enum does not work because it is not fieldless. + fn into(self) -> i64 { + match self { + FlatStorageStatus::Disabled => 0, + FlatStorageStatus::Empty => 1, + FlatStorageStatus::Ready(_) => 2, + // 10..20 is reserved for creation statuses + FlatStorageStatus::Creation(creation_status) => match creation_status { + FlatStorageCreationStatus::SavingDeltas => 10, + FlatStorageCreationStatus::FetchingState(_) => 11, + FlatStorageCreationStatus::CatchingUp(_) => 12, + }, + } + } +} + +#[derive(BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, serde::Serialize)] +pub struct FlatStorageReadyStatus { + pub flat_head: BlockInfo, +} + +/// If a node has flat storage enabled but it didn't have flat storage data on disk, its creation should be initiated. +/// Because this is a heavy work requiring ~5h for testnet rpc node and ~10h for testnet archival node, we do it on +/// background during regular block processing. +/// This struct reveals what is the current status of creating flat storage data on disk. +#[derive(BorshSerialize, BorshDeserialize, Copy, Clone, Debug, PartialEq, Eq, serde::Serialize)] +pub enum FlatStorageCreationStatus { + /// Flat storage state does not exist. We are saving `FlatStorageDelta`s to disk. + /// During this step, we save current chain head, start saving all deltas for blocks after chain head and wait until + /// final chain head moves after saved chain head. + SavingDeltas, + /// Flat storage state misses key-value pairs. We need to fetch Trie state to fill flat storage for some final chain + /// head. It is the heaviest work, so it is done in multiple steps, see comment for `FetchingStateStatus` for more + /// details. + /// During each step we spawn background threads to fill some contiguous range of state keys. + /// Status contains block hash for which we fetch the shard state and number of current step. Progress of each step + /// is saved to disk, so if creation is interrupted during some step, we don't repeat previous steps, starting from + /// the saved step again. + FetchingState(FetchingStateStatus), + /// Flat storage data exists on disk but block which is corresponds to is earlier than chain final head. + /// We apply deltas from disk until the head reaches final head. + /// Includes block hash of flat storage head. + CatchingUp(CryptoHash), +} + +/// Current step of fetching state to fill flat storage. +#[derive(BorshSerialize, BorshDeserialize, Copy, Clone, Debug, PartialEq, Eq, serde::Serialize)] +pub struct FetchingStateStatus { + /// Hash of block on top of which we create flat storage. + pub block_hash: CryptoHash, + /// Number of the first state part to be fetched in this step. + pub part_id: u64, + /// Number of parts fetched in one step. + pub num_parts_in_step: u64, + /// Total number of state parts. + pub num_parts: u64, +} + +pub type FlatStateIterator<'a> = + Box, FlatStateValue)>> + 'a>; diff --git a/core/store/src/genesis/initialization.rs b/core/store/src/genesis/initialization.rs new file mode 100644 index 000000000..419fc67ce --- /dev/null +++ b/core/store/src/genesis/initialization.rs @@ -0,0 +1,162 @@ +//! This file contains helper functions for initialization of genesis data in store +//! We first check if store has the genesis hash and state_roots, if not, we go ahead with initialization + +use rayon::prelude::*; +use std::{collections::HashSet, fs, path::Path}; + +use borsh::BorshDeserialize; +use unc_chain_configs::{Genesis, GenesisContents}; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::{ + epoch_manager::EpochConfig, + shard_layout::{account_id_to_shard_id, ShardLayout}, + state_record::{state_record_to_account_id, StateRecord}, + types::{AccountId, NumShards, ShardId, StateRoot}, +}; +use tracing::{error, info, warn}; + +use crate::{ + flat::FlatStorageManager, genesis::GenesisStateApplier, get_genesis_hash, + get_genesis_state_roots, set_genesis_hash, set_genesis_state_roots, ShardTries, + StateSnapshotConfig, Store, TrieConfig, +}; + +const STATE_DUMP_FILE: &str = "state_dump"; +const GENESIS_ROOTS_FILE: &str = "genesis_roots"; + +pub fn initialize_genesis_state(store: Store, genesis: &Genesis, home_dir: Option<&Path>) { + // Ignore initialization if we already have genesis hash and state roots in store + let stored_hash = get_genesis_hash(&store).expect("Store failed on genesis intialization"); + if let Some(_hash) = stored_hash { + // TODO: re-enable this check (#4447) + //assert_eq!(hash, genesis_hash, "Storage already exists, but has a different genesis"); + get_genesis_state_roots(&store) + .expect("Store failed on genesis intialization") + .expect("Genesis state roots not found in storage"); + return; + } else { + let has_dump = home_dir.is_some_and(|dir| dir.join(STATE_DUMP_FILE).exists()); + let state_roots = if has_dump { + if let GenesisContents::Records { .. } = &genesis.contents { + warn!(target: "store", "Found both records in genesis config and the state dump file. Will ignore the records."); + } + genesis_state_from_dump(store.clone(), home_dir.unwrap()) + } else { + genesis_state_from_genesis(store.clone(), genesis) + }; + let genesis_hash = genesis.json_hash(); + let mut store_update = store.store_update(); + set_genesis_hash(&mut store_update, &genesis_hash); + set_genesis_state_roots(&mut store_update, &state_roots); + store_update.commit().expect("Store failed on genesis intialization"); + } + + let num_shards = genesis.config.shard_layout.shard_ids().count() as NumShards; + assert_eq!( + num_shards, + genesis.config.num_block_producer_seats_per_shard.len() as NumShards, + "genesis config shard_layout and num_block_producer_seats_per_shard indicate inconsistent number of shards {} vs {}", + num_shards, + genesis.config.num_block_producer_seats_per_shard.len() as NumShards, + ); +} + +fn genesis_state_from_dump(store: Store, home_dir: &Path) -> Vec { + error!(target: "near", "Loading genesis from a state dump file. Do not use this outside of genesis-tools"); + let mut state_file = home_dir.to_path_buf(); + state_file.push(STATE_DUMP_FILE); + store.load_state_from_file(state_file.as_path()).expect("Failed to read state dump"); + let mut roots_files = home_dir.to_path_buf(); + roots_files.push(GENESIS_ROOTS_FILE); + let data = fs::read(roots_files).expect("Failed to read genesis roots file."); + let state_roots: Vec = + BorshDeserialize::try_from_slice(&data).expect("Failed to deserialize genesis roots"); + state_roots +} + +fn genesis_state_from_genesis(store: Store, genesis: &Genesis) -> Vec { + match &genesis.contents { + GenesisContents::Records { records } => { + info!( + target: "runtime", + "genesis state has {} records, computing state roots", + records.0.len(), + ) + } + GenesisContents::RecordsFile { records_file } => { + info!( + target: "runtime", + path=%records_file.display(), + message="computing state roots from records", + ) + } + GenesisContents::StateRoots { state_roots } => { + return state_roots.clone(); + } + } + let runtime_config_store = RuntimeConfigStore::for_chain_id(&genesis.config.chain_id); + let runtime_config = runtime_config_store.get_config(genesis.config.protocol_version); + let storage_usage_config = &runtime_config.fees.storage_usage_config; + let initial_epoch_config = EpochConfig::from(&genesis.config); + let shard_layout = initial_epoch_config.shard_layout; + let shard_ids: Vec<_> = shard_layout.shard_ids().collect(); + let mut shard_account_ids: Vec> = + shard_ids.iter().map(|_| HashSet::new()).collect(); + let mut has_protocol_account = false; + info!(target: "store","distributing records to shards"); + + genesis.for_each_record(|record: &StateRecord| { + shard_account_ids[state_record_to_shard_id(record, &shard_layout) as usize] + .insert(state_record_to_account_id(record).clone()); + if let StateRecord::Account { account_id, .. } = record { + if account_id == &genesis.config.protocol_treasury_account { + has_protocol_account = true; + } + } + }); + assert!(has_protocol_account, "Genesis spec doesn't have protocol treasury account"); + let tries = ShardTries::new( + store.clone(), + TrieConfig::default(), + &genesis.config.shard_layout.shard_uids().collect::>(), + FlatStorageManager::new(store), + StateSnapshotConfig::default(), + ); + + let writers = std::sync::atomic::AtomicUsize::new(0); + shard_ids + .into_par_iter() + .map(|shard_id| { + let validators = genesis + .config + .validators + .iter() + .filter_map(|account_info| { + if account_id_to_shard_id(&account_info.account_id, &shard_layout) == shard_id { + Some(( + account_info.account_id.clone(), + account_info.public_key.clone(), + account_info.amount, + )) + } else { + None + } + }) + .collect::>(); + + GenesisStateApplier::apply( + &writers, + tries.clone(), + shard_id, + &validators, + storage_usage_config, + genesis, + shard_account_ids[shard_id as usize].clone(), + ) + }) + .collect() +} + +fn state_record_to_shard_id(state_record: &StateRecord, shard_layout: &ShardLayout) -> ShardId { + account_id_to_shard_id(state_record_to_account_id(state_record), shard_layout) +} diff --git a/core/store/src/genesis/mod.rs b/core/store/src/genesis/mod.rs new file mode 100644 index 000000000..d0b3c2d3b --- /dev/null +++ b/core/store/src/genesis/mod.rs @@ -0,0 +1,10 @@ +//! This module contains helper functions for initialization of genesis data in store +//! We first check if store has the genesis hash and state_roots, if not, we go ahead with initialization + +mod initialization; +mod state_applier; + +pub use initialization::initialize_genesis_state; +pub use state_applier::compute_genesis_storage_usage; +pub use state_applier::compute_storage_usage; +pub use state_applier::GenesisStateApplier; diff --git a/core/store/src/genesis/state_applier.rs b/core/store/src/genesis/state_applier.rs new file mode 100644 index 000000000..70787f8ff --- /dev/null +++ b/core/store/src/genesis/state_applier.rs @@ -0,0 +1,388 @@ +use crate::flat::FlatStateChanges; +use crate::{ + get_account, get_received_data, set, set_access_key, set_account, set_code, + set_delayed_receipt, set_postponed_receipt, set_received_data, ShardTries, TrieUpdate, +}; + +use unc_chain_configs::Genesis; +use unc_crypto::PublicKey; +use unc_parameters::StorageUsageConfig; +use unc_primitives::account::{AccessKey, Account}; +use unc_primitives::receipt::{DelayedReceiptIndices, Receipt, ReceiptEnum, ReceivedData}; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::state_record::{state_record_to_account_id, StateRecord}; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::{AccountId, Balance, ShardId, StateChangeCause, StateRoot}; +use unc_vm_runner::ContractCode; +use std::collections::{HashMap, HashSet}; +use std::sync::atomic; + +/// Computes the expected storage per account for a given stream of StateRecord(s). +/// For example: the storage for Contract depends on its length, we don't charge storage for receipts +/// and we compute a fixed (config-configured) number of bytes for each account (to store account id). +struct StorageComputer<'a> { + /// Map from account id to number of storage bytes used. + result: HashMap, + /// Configuration that keeps information like 'how many bytes should accountId consume' etc. + config: &'a StorageUsageConfig, +} + +impl<'a> StorageComputer<'a> { + fn new(config: &'a StorageUsageConfig) -> Self { + Self { result: HashMap::new(), config: &config } + } + + /// Updates user's storage info based on the StateRecord. + fn process_record(&mut self, record: &StateRecord) { + // Note: It's okay to use unsafe math here, because this method should only be called on the trusted + // state records (e.g. at launch from genesis) + let account_and_storage = match record { + StateRecord::Account { account_id, .. } => { + Some((account_id.clone(), self.config.num_bytes_account)) + } + StateRecord::Data { account_id, data_key, value } => { + let storage_usage = + self.config.num_extra_bytes_record + data_key.len() as u64 + value.len() as u64; + Some((account_id.clone(), storage_usage)) + } + StateRecord::Contract { account_id, code } => { + Some((account_id.clone(), code.len() as u64)) + } + StateRecord::AccessKey { account_id, public_key, access_key } => { + let public_key: PublicKey = public_key.clone(); + let access_key: AccessKey = access_key.clone(); + let storage_usage = self.config.num_extra_bytes_record + + borsh::object_length(&public_key).unwrap() as u64 + + borsh::object_length(&access_key).unwrap() as u64; + Some((account_id.clone(), storage_usage)) + } + StateRecord::PostponedReceipt(_) => None, + StateRecord::ReceivedData { .. } => None, + StateRecord::DelayedReceipt(_) => None, + }; + if let Some((account_id, storage_usage)) = account_and_storage { + *self.result.entry(account_id).or_default() += storage_usage; + } + } + + /// Adds multiple StateRecords to the users' storage info. + fn process_records(&mut self, records: &[StateRecord]) { + for record in records { + self.process_record(record); + } + } + + /// Returns the current storage use for each user. + fn finalize(self) -> HashMap { + self.result + } +} + +/// The number of buffered storage modifications stored in memory. +/// +/// Note that the actual number of changes stored in memory may be higher, and this is a very rough +/// limit on the memory usage. For instance – 100k writes of 4MiB (maximum size) contracts is still +/// going to be half a terabyte worth of memory, but in practice this does not seem to be a problem +/// and the memory usage stays fairly consistent throughout the process. +const TARGET_OUTSTANDING_WRITES: usize = 100_000; + +struct AutoFlushingTrieUpdate<'a> { + tries: &'a ShardTries, + shard_uid: ShardUId, + state_root: StateRoot, + state_update: Option, + changes: usize, + active_writers: &'a atomic::AtomicUsize, +} + +impl<'a> AutoFlushingTrieUpdate<'a> { + fn new( + active_writers: &'a atomic::AtomicUsize, + state_root: StateRoot, + tries: &'a ShardTries, + uid: ShardUId, + ) -> Self { + active_writers.fetch_add(1, atomic::Ordering::Relaxed); + Self { + active_writers, + state_root, + tries, + changes: 0, + state_update: Some(tries.new_trie_update(uid, state_root)), + shard_uid: uid, + } + } + + fn modify(&mut self, process_callback: impl FnOnce(&mut TrieUpdate) -> R) -> R { + let Self { ref mut changes, ref mut state_update, .. } = self; + // See if we should consider flushing. + let result = process_callback(state_update.as_mut().expect("state update should be set")); + let writers = self.active_writers.load(atomic::Ordering::Relaxed); + let target_updates = TARGET_OUTSTANDING_WRITES / writers; + *changes += 1; + if *changes >= target_updates { + self.flush(); + } + result + } + + fn flush(&mut self) -> StateRoot { + let Self { ref mut changes, ref mut state_root, ref mut state_update, .. } = self; + tracing::info!( + target: "runtime", + shard_uid=?self.shard_uid, + %state_root, + %changes, + "flushing changes" + ); + let mut old_state_update = state_update.take().expect("state update should be set"); + old_state_update.commit(StateChangeCause::InitialState); + let (_, trie_changes, state_changes) = + old_state_update.finalize().expect("Genesis state update failed"); + let mut store_update = self.tries.store_update(); + *state_root = self.tries.apply_all(&trie_changes, self.shard_uid, &mut store_update); + FlatStateChanges::from_state_changes(&state_changes) + .apply_to_flat_state(&mut store_update, self.shard_uid); + store_update.commit().expect("Store update failed on genesis initialization"); + *state_update = Some(self.tries.new_trie_update(self.shard_uid, *state_root)); + *changes = 0; + *state_root + } +} + +impl<'a> Drop for AutoFlushingTrieUpdate<'a> { + fn drop(&mut self) { + self.flush(); + self.active_writers.fetch_sub(1, atomic::Ordering::Relaxed); + } +} + +pub struct GenesisStateApplier {} + +impl GenesisStateApplier { + fn apply_batch( + storage: &mut AutoFlushingTrieUpdate, + delayed_receipts_indices: &mut DelayedReceiptIndices, + shard_uid: ShardUId, + validators: &[(AccountId, PublicKey, Balance)], + config: &StorageUsageConfig, + genesis: &Genesis, + account_ids: HashSet, + ) { + let mut postponed_receipts: Vec = vec![]; + let mut storage_computer = StorageComputer::new(config); + tracing::info!( + target: "runtime", + ?shard_uid, + "processing records…" + ); + genesis.for_each_record(|record: &StateRecord| { + if !account_ids.contains(state_record_to_account_id(record)) { + return; + } + storage_computer.process_record(record); + match record { + StateRecord::Account { account_id, account } => storage.modify(|state_update| { + set_account(state_update, account_id.clone(), account); + }), + StateRecord::Data { account_id, data_key, value } => { + storage.modify(|state_update| { + state_update.set( + TrieKey::ContractData { + key: data_key.clone().into(), + account_id: account_id.clone(), + }, + value.clone().into(), + ); + }) + } + StateRecord::Contract { account_id, code } => { + storage.modify(|state_update| { + // Recompute contract code hash. + let code = ContractCode::new(code.clone(), None); + if let Some(acc) = + get_account(state_update, account_id).expect("Failed to read state") + { + set_code(state_update, account_id.clone(), &code); + assert_eq!(*code.hash(), acc.code_hash()); + } else { + tracing::error!( + target: "runtime", + %account_id, + code_hash = %code.hash(), + message = "code for non-existent account", + ); + } + }) + } + StateRecord::AccessKey { account_id, public_key, access_key } => { + storage.modify(|state_update| { + set_access_key( + state_update, + account_id.clone(), + public_key.clone(), + access_key, + ); + }) + } + StateRecord::PostponedReceipt(receipt) => { + // Delaying processing postponed receipts, until we process all data first + postponed_receipts.push(*receipt.clone()); + } + StateRecord::ReceivedData { account_id, data_id, data } => { + storage.modify(|state_update| { + set_received_data( + state_update, + account_id.clone(), + *data_id, + // FIXME: this clone is not necessary! + &ReceivedData { data: data.clone() }, + ); + }) + } + StateRecord::DelayedReceipt(receipt) => storage.modify(|state_update| { + set_delayed_receipt(state_update, delayed_receipts_indices, &*receipt); + }), + } + }); + + tracing::info!( + target: "runtime", + ?shard_uid, + "processing account storage…" + ); + for (account_id, storage_usage) in storage_computer.finalize() { + storage.modify(|state_update| { + let mut account = get_account(state_update, &account_id) + .expect("Genesis storage error") + .expect("Account must exist"); + account.set_storage_usage(storage_usage); + set_account(state_update, account_id, &account); + }); + } + + // Processing postponed receipts after we stored all received data + tracing::info!( + target: "runtime", + ?shard_uid, + "processing postponed receipts…" + ); + for receipt in postponed_receipts { + let account_id = &receipt.receiver_id; + let action_receipt = match &receipt.receipt { + ReceiptEnum::Action(a) => a, + _ => panic!("Expected action receipt"), + }; + // Logic similar to `apply_receipt` + let mut pending_data_count: u32 = 0; + for data_id in &action_receipt.input_data_ids { + storage.modify(|state_update| { + if get_received_data(state_update, account_id, *data_id) + .expect("Genesis storage error") + .is_none() + { + pending_data_count += 1; + set( + state_update, + TrieKey::PostponedReceiptId { + receiver_id: account_id.clone(), + data_id: *data_id, + }, + &receipt.receipt_id, + ) + } + }); + } + if pending_data_count == 0 { + panic!("Postponed receipt should have pending data") + } else { + storage.modify(|state_update| { + set( + state_update, + TrieKey::PendingDataCount { + receiver_id: account_id.clone(), + receipt_id: receipt.receipt_id, + }, + &pending_data_count, + ); + set_postponed_receipt(state_update, &receipt); + }); + } + } + + for (account_id, _, amount) in validators { + if !account_ids.contains(account_id) { + continue; + } + storage.modify(|state_update| { + let mut account: Account = get_account(state_update, account_id) + .expect("Genesis storage error") + .expect("account must exist"); + account.set_locked(*amount); + set_account(state_update, account_id.clone(), &account); + }); + } + } + + fn apply_delayed_receipts( + storage: &mut AutoFlushingTrieUpdate, + delayed_receipts_indices: DelayedReceiptIndices, + ) { + if delayed_receipts_indices != DelayedReceiptIndices::default() { + storage.modify(|state_update| { + set(state_update, TrieKey::DelayedReceiptIndices, &delayed_receipts_indices); + }); + } + } + + pub fn apply( + op_limit: &atomic::AtomicUsize, + tries: ShardTries, + shard_id: ShardId, + validators: &[(AccountId, PublicKey, Balance)], + config: &StorageUsageConfig, + genesis: &Genesis, + shard_account_ids: HashSet, + ) -> StateRoot { + let mut delayed_receipts_indices = DelayedReceiptIndices::default(); + let shard_uid = + ShardUId { version: genesis.config.shard_layout.version(), shard_id: shard_id as u32 }; + let mut storage = + AutoFlushingTrieUpdate::new(op_limit, StateRoot::default(), &tries, shard_uid); + Self::apply_batch( + &mut storage, + &mut delayed_receipts_indices, + shard_uid, + validators, + config, + genesis, + shard_account_ids, + ); + Self::apply_delayed_receipts(&mut storage, delayed_receipts_indices); + // At this point we have written all we wanted, but there may be outstanding writes left. + // We flush those writes and return the new state root to the caller. + storage.flush() + } +} + +/// Computes the expected storage per account for a given set of StateRecord(s). +pub fn compute_storage_usage( + records: &[StateRecord], + config: &StorageUsageConfig, +) -> HashMap { + let mut storage_computer = StorageComputer::new(config); + storage_computer.process_records(records); + storage_computer.finalize() +} + +/// Compute the expected storage per account for genesis records. +pub fn compute_genesis_storage_usage( + genesis: &Genesis, + config: &StorageUsageConfig, +) -> HashMap { + let mut storage_computer = StorageComputer::new(config); + genesis.for_each_record(|record| { + storage_computer.process_record(record); + }); + storage_computer.finalize() +} diff --git a/core/store/src/lib.rs b/core/store/src/lib.rs new file mode 100644 index 000000000..bd2ae0fd0 --- /dev/null +++ b/core/store/src/lib.rs @@ -0,0 +1,1155 @@ +extern crate core; + +use std::fs::File; +use std::path::Path; +use std::str::FromStr; +use std::sync::Arc; +use std::{fmt, io}; + +use borsh::{BorshDeserialize, BorshSerialize}; +use metadata::{DbKind, DbVersion, KIND_KEY, VERSION_KEY}; +use unc_primitives::transaction::RegisterRsa2048KeysAction; +use once_cell::sync::Lazy; +use strum; + +pub use columns::DBCol; +pub use db::{ + CHUNK_TAIL_KEY, COLD_HEAD_KEY, FINAL_HEAD_KEY, FORK_TAIL_KEY, GENESIS_JSON_HASH_KEY, + GENESIS_STATE_ROOTS_KEY, HEADER_HEAD_KEY, HEAD_KEY, LARGEST_TARGET_HEIGHT_KEY, + LATEST_KNOWN_KEY, STATE_SNAPSHOT_KEY, STATE_SYNC_DUMP_KEY, TAIL_KEY, +}; +use unc_crypto::PublicKey; +use unc_fmt::{AbbrBytes, StorageKey}; +use unc_primitives::account::{AccessKey, Account}; +pub use unc_primitives::errors::{MissingTrieValueContext, StorageError}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::{DelayedReceiptIndices, Receipt, ReceivedData}; +pub use unc_primitives::shard_layout::ShardUId; +use unc_primitives::trie_key::{trie_key_parsers, TrieKey}; +use unc_primitives::types::{AccountId, StateRoot}; +use unc_vm_runner::logic::{CompiledContract, CompiledContractCache}; +use unc_vm_runner::ContractCode; + +use crate::db::{refcount, DBIterator, DBOp, DBSlice, DBTransaction, Database, StoreStatistics}; +pub use crate::trie::iterator::{TrieIterator, TrieTraversalItem}; +pub use crate::trie::update::{TrieUpdate, TrieUpdateIterator, TrieUpdateValuePtr}; +pub use crate::trie::{ + estimator, resharding, ApplyStatePartResult, KeyForStateChanges, KeyLookupMode, NibbleSlice, + PartialStorage, PrefetchApi, PrefetchError, RawTrieNode, RawTrieNodeWithSize, ShardTries, + StateSnapshot, StateSnapshotConfig, Trie, TrieAccess, TrieCache, TrieCachingStorage, + TrieChanges, TrieConfig, TrieDBStorage, TrieStorage, WrappedTrieChanges, +}; + +pub mod cold_storage; +mod columns; +pub mod config; +pub mod db; +pub mod flat; +pub mod genesis; +pub mod metadata; +pub mod metrics; +pub mod migrations; +mod opener; +mod rocksdb_metrics; +mod sync_utils; +pub mod test_utils; +pub mod trie; + +pub use crate::config::{Mode, StoreConfig}; +pub use crate::opener::{ + checkpoint_hot_storage_and_cleanup_columns, StoreMigrator, StoreOpener, StoreOpenerError, +}; + +/// Specifies temperature of a storage. +/// +/// Since currently only hot storage is implemented, this has only one variant. +/// In the future, certain parts of the code may need to access hot or cold +/// storage. Specifically, querying an old block will require reading it from +/// the cold storage. +#[derive(Clone, Copy, Debug, Eq, PartialEq, strum::IntoStaticStr)] +pub enum Temperature { + Hot, + Cold, +} + +impl FromStr for Temperature { + type Err = String; + + fn from_str(s: &str) -> Result { + let s = s.to_lowercase(); + match s.as_str() { + "hot" => Ok(Temperature::Hot), + "cold" => Ok(Temperature::Cold), + _ => Err(String::from(format!("invalid temperature string {s}"))), + } + } +} + +const STATE_COLUMNS: [DBCol; 2] = [DBCol::State, DBCol::FlatState]; +const STATE_FILE_END_MARK: u8 = 255; + +/// Node’s storage holding chain and all other necessary data. +/// +/// Provides access to hot storage, cold storage and split storage. Typically +/// users will want to use one of the above via the Store abstraction. +pub struct NodeStorage { + hot_storage: Arc, + cold_storage: Option>, +} + +/// Node’s single storage source. +/// +/// The Store holds one of the possible databases: +/// - The hot database - access to the hot database only +/// - The cold database - access to the cold database only +/// - The split database - access to both hot and cold databases +#[derive(Clone)] +pub struct Store { + storage: Arc, +} + +impl NodeStorage { + /// Initialises a new opener with given home directory and hot and cold + /// store config. + pub fn opener<'a>( + home_dir: &std::path::Path, + archive: bool, + config: &'a StoreConfig, + cold_config: Option<&'a StoreConfig>, + ) -> StoreOpener<'a> { + StoreOpener::new(home_dir, archive, config, cold_config) + } + + /// Constructs new object backed by given database. + fn from_rocksdb( + hot_storage: crate::db::RocksDB, + cold_storage: Option, + ) -> Self { + let hot_storage = Arc::new(hot_storage); + let cold_storage = cold_storage.map(|storage| Arc::new(storage)); + + let cold_db = if let Some(cold_storage) = cold_storage { + Some(Arc::new(crate::db::ColdDB::new(cold_storage))) + } else { + None + }; + + Self { hot_storage, cold_storage: cold_db } + } + + /// Initialises an opener for a new temporary test store. + /// + /// As per the name, this is meant for tests only. The created store will + /// use test configuration (which may differ slightly from default config). + /// The function **panics** if a temporary directory cannot be created. + /// + /// Note that the caller must hold the temporary directory returned as first + /// element of the tuple while the store is open. + pub fn test_opener() -> (tempfile::TempDir, StoreOpener<'static>) { + static CONFIG: Lazy = Lazy::new(StoreConfig::test_config); + let dir = tempfile::tempdir().unwrap(); + let opener = StoreOpener::new(dir.path(), false, &CONFIG, None); + (dir, opener) + } + + /// Constructs new object backed by given database. + /// + /// Note that you most likely don’t want to use this method. If you’re + /// opening an on-disk storage, you want to use [`Self::opener`] instead + /// which takes care of opening the on-disk database and applying all the + /// necessary configuration. If you need an in-memory database for testing, + /// you want either [`crate::test_utils::create_test_node_storage`] or + /// possibly [`crate::test_utils::create_test_store`] (depending whether you + /// need [`NodeStorage`] or [`Store`] object. + pub fn new(storage: Arc) -> Self { + Self { hot_storage: storage, cold_storage: None } + } +} + +impl NodeStorage { + /// Returns the hot store. The hot store is always available and it provides + /// direct access to the hot database. + /// + /// For RPC nodes this is the only store available and it should be used for + /// all the use cases. + /// + /// For archival nodes that do not have split storage configured this is the + /// only store available and it should be used for all the use cases. + /// + /// For archival nodes that do have split storage configured there are three + /// stores available: hot, cold and split. The client should use the hot + /// store, the view client should use the split store and the cold store + /// loop should use cold store. + pub fn get_hot_store(&self) -> Store { + Store { storage: self.hot_storage.clone() } + } + + /// Returns the cold store. The cold store is only available in archival + /// nodes with split storage configured. + /// + /// For archival nodes that do have split storage configured there are three + /// stores available: hot, cold and split. The client should use the hot + /// store, the view client should use the split store and the cold store + /// loop should use cold store. + pub fn get_cold_store(&self) -> Option { + match &self.cold_storage { + Some(cold_storage) => Some(Store { storage: cold_storage.clone() }), + None => None, + } + } + + /// Returns the split store. The split store is only available in archival + /// nodes with split storage configured. + /// + /// For archival nodes that do have split storage configured there are three + /// stores available: hot, cold and split. The client should use the hot + /// store, the view client should use the split store and the cold store + /// loop should use cold store. + pub fn get_split_store(&self) -> Option { + match &self.cold_storage { + Some(cold_storage) => Some(Store { + storage: crate::db::SplitDB::new(self.hot_storage.clone(), cold_storage.clone()), + }), + None => None, + } + } + + /// Returns underlying database for given temperature. + /// + /// This allows accessing underlying hot and cold databases directly + /// bypassing any abstractions offered by [`NodeStorage`] or [`Store`] interfaces. + /// + /// This is useful for certain data which only lives in hot storage and + /// interfaces which deal with it. For example, peer store uses hot + /// storage’s [`Database`] interface directly. + /// + /// Note that this is not appropriate for code which only ever accesses hot + /// storage but touches information kinds which live in cold storage as + /// well. For example, garbage collection only ever touches hot storage but + /// it should go through [`Store`] interface since data it manipulates + /// (e.g. blocks) are live in both databases. + /// + /// This method panics if trying to access cold store but it wasn't configured. + pub fn into_inner(self, temp: Temperature) -> Arc { + match temp { + Temperature::Hot => self.hot_storage, + Temperature::Cold => self.cold_storage.unwrap(), + } + } +} + +impl NodeStorage { + /// Returns whether the storage has a cold database. + pub fn has_cold(&self) -> bool { + self.cold_storage.is_some() + } + + /// Reads database metadata and returns whether the storage is archival. + pub fn is_archive(&self) -> io::Result { + if self.cold_storage.is_some() { + return Ok(true); + } + Ok(match metadata::DbMetadata::read(self.hot_storage.as_ref())?.kind.unwrap() { + metadata::DbKind::RPC => false, + metadata::DbKind::Archive => true, + metadata::DbKind::Hot | metadata::DbKind::Cold => todo!(), + }) + } + + pub fn new_with_cold(hot: Arc, cold: Arc) -> Self { + Self { hot_storage: hot, cold_storage: Some(Arc::new(crate::db::ColdDB::new(cold))) } + } + + pub fn cold_db(&self) -> Option<&Arc> { + self.cold_storage.as_ref() + } +} + +impl Store { + /// Fetches value from given column. + /// + /// If the key does not exist in the column returns `None`. Otherwise + /// returns the data as [`DBSlice`] object. The object dereferences into + /// a slice, for cases when caller doesn’t need to own the value, and + /// provides conversion into a vector or an Arc. + pub fn get(&self, column: DBCol, key: &[u8]) -> io::Result>> { + let value = if column.is_rc() { + self.storage.get_with_rc_stripped(column, key) + } else { + self.storage.get_raw_bytes(column, key) + }?; + tracing::trace!( + target: "store", + db_op = "get", + col = %column, + key = %StorageKey(key), + size = value.as_deref().map(<[u8]>::len) + ); + Ok(value) + } + + pub fn get_ser(&self, column: DBCol, key: &[u8]) -> io::Result> { + self.get(column, key)?.as_deref().map(T::try_from_slice).transpose() + } + + pub fn exists(&self, column: DBCol, key: &[u8]) -> io::Result { + self.get(column, key).map(|value| value.is_some()) + } + + pub fn store_update(&self) -> StoreUpdate { + StoreUpdate::new(Arc::clone(&self.storage)) + } + + pub fn iter<'a>(&'a self, col: DBCol) -> DBIterator<'a> { + self.storage.iter(col) + } + + /// Fetches raw key/value pairs from the database. + /// + /// Practically, this means that for rc columns rc is included in the value. + /// This method is a deliberate escape hatch, and shouldn't be used outside + /// of auxilary code like migrations which wants to hack on the database + /// directly. + pub fn iter_raw_bytes<'a>(&'a self, col: DBCol) -> DBIterator<'a> { + self.storage.iter_raw_bytes(col) + } + + pub fn iter_prefix<'a>(&'a self, col: DBCol, key_prefix: &'a [u8]) -> DBIterator<'a> { + self.storage.iter_prefix(col, key_prefix) + } + + /// Iterates over a range of keys. Upper bound key is not included. + pub fn iter_range<'a>( + &'a self, + col: DBCol, + lower_bound: Option<&[u8]>, + upper_bound: Option<&[u8]>, + ) -> DBIterator<'a> { + self.storage.iter_range(col, lower_bound, upper_bound) + } + + pub fn iter_prefix_ser<'a, T: BorshDeserialize>( + &'a self, + col: DBCol, + key_prefix: &'a [u8], + ) -> impl Iterator, T)>> + 'a { + self.storage + .iter_prefix(col, key_prefix) + .map(|item| item.and_then(|(key, value)| Ok((key, T::try_from_slice(value.as_ref())?)))) + } + + /// Saves state (`State` and `FlatState` columns) to given file. + /// + /// The format of the file is a list of `(column_index as u8, key_length as + /// u32, key, value_length as u32, value)` records terminated by a single + /// 255 byte. `column_index` refers to state columns listed in + /// `STATE_COLUMNS` array. + pub fn save_state_to_file(&self, filename: &Path) -> io::Result<()> { + let file = File::create(filename)?; + let mut file = std::io::BufWriter::new(file); + for (column_index, &column) in STATE_COLUMNS.iter().enumerate() { + assert!(column_index < STATE_FILE_END_MARK.into()); + let column_index: u8 = column_index.try_into().unwrap(); + for item in self.storage.iter_raw_bytes(column) { + let (key, value) = item?; + (column_index, key, value).serialize(&mut file)?; + } + } + STATE_FILE_END_MARK.serialize(&mut file) + } + + /// Loads state (`State` and `FlatState` columns) from given file. + /// + /// See [`Self::save_state_to_file`] for description of the file format. + pub fn load_state_from_file(&self, filename: &Path) -> io::Result<()> { + let file = File::open(filename)?; + let mut file = std::io::BufReader::new(file); + let mut transaction = DBTransaction::new(); + loop { + let column = u8::deserialize_reader(&mut file)?; + if column == STATE_FILE_END_MARK { + break; + } + let (key, value) = BorshDeserialize::deserialize_reader(&mut file)?; + transaction.set(STATE_COLUMNS[usize::from(column)], key, value); + } + self.storage.write(transaction) + } + + /// If the storage is backed by disk, flushes any in-memory data to disk. + pub fn flush(&self) -> io::Result<()> { + self.storage.flush() + } + + /// Blocking compaction request if supported by storage. + pub fn compact(&self) -> io::Result<()> { + self.storage.compact() + } + + pub fn get_store_statistics(&self) -> Option { + self.storage.get_store_statistics() + } +} + +impl Store { + pub fn get_db_version(&self) -> io::Result> { + metadata::DbMetadata::maybe_read_version(self.storage.as_ref()) + } + + pub fn set_db_version(&self, version: DbVersion) -> io::Result<()> { + let mut store_update = self.store_update(); + store_update.set(DBCol::DbVersion, VERSION_KEY, version.to_string().as_bytes()); + store_update.commit() + } + + pub fn get_db_kind(&self) -> io::Result> { + metadata::DbMetadata::maybe_read_kind(self.storage.as_ref()) + } + + pub fn set_db_kind(&self, kind: DbKind) -> io::Result<()> { + let mut store_update = self.store_update(); + store_update.set(DBCol::DbVersion, KIND_KEY, <&str>::from(kind).as_bytes()); + store_update.commit() + } +} + +/// Keeps track of current changes to the database and can commit all of them to the database. +pub struct StoreUpdate { + transaction: DBTransaction, + storage: Arc, +} + +impl StoreUpdate { + const ONE: std::num::NonZeroU32 = match std::num::NonZeroU32::new(1) { + Some(num) => num, + None => panic!(), + }; + + pub(crate) fn new(db: Arc) -> Self { + StoreUpdate { transaction: DBTransaction::new(), storage: db } + } + + /// Inserts a new value into the database. + /// + /// It is a programming error if `insert` overwrites an existing, different + /// value. Use it for insert-only columns. + pub fn insert(&mut self, column: DBCol, key: Vec, value: Vec) { + assert!(column.is_insert_only(), "can't insert: {column}"); + self.transaction.insert(column, key, value) + } + + /// Borsh-serializes a value and inserts it into the database. + /// + /// It is a programming error if `insert` overwrites an existing, different + /// value. Use it for insert-only columns. + /// + /// Note on performance: The key is always copied into a new allocation, + /// which is generally bad. However, the insert-only columns use + /// `CryptoHash` as key, which has the data in a small fixed-sized array. + /// Copying and allocating that is not prohibitively expensive and we have + /// to do it either way. Thus, we take a slice for the key for the nice API. + pub fn insert_ser( + &mut self, + column: DBCol, + key: &[u8], + value: &T, + ) -> io::Result<()> { + assert!(column.is_insert_only(), "can't insert_ser: {column}"); + let data = borsh::to_vec(&value)?; + self.insert(column, key.to_vec(), data); + Ok(()) + } + + /// Inserts a new reference-counted value or increases its reference count + /// if it’s already there. + /// + /// It is a programming error if `increment_refcount_by` supplies a different + /// value than the one stored in the database. It may lead to data + /// corruption or panics. + /// + /// Panics if this is used for columns which are not reference-counted + /// (see [`DBCol::is_rc`]). + pub fn increment_refcount_by( + &mut self, + column: DBCol, + key: &[u8], + data: &[u8], + increase: std::num::NonZeroU32, + ) { + assert!(column.is_rc(), "can't update refcount: {column}"); + let value = refcount::add_positive_refcount(data, increase); + self.transaction.update_refcount(column, key.to_vec(), value); + } + + /// Same as `self.increment_refcount_by(column, key, data, 1)`. + pub fn increment_refcount(&mut self, column: DBCol, key: &[u8], data: &[u8]) { + self.increment_refcount_by(column, key, data, Self::ONE) + } + + /// Decreases value of an existing reference-counted value. + /// + /// Since decrease of reference count is encoded without the data, only key + /// and reference count delta arguments are needed. + /// + /// Panics if this is used for columns which are not reference-counted + /// (see [`DBCol::is_rc`]). + pub fn decrement_refcount_by( + &mut self, + column: DBCol, + key: &[u8], + decrease: std::num::NonZeroU32, + ) { + assert!(column.is_rc(), "can't update refcount: {column}"); + let value = refcount::encode_negative_refcount(decrease); + self.transaction.update_refcount(column, key.to_vec(), value) + } + + /// Same as `self.decrement_refcount_by(column, key, 1)`. + pub fn decrement_refcount(&mut self, column: DBCol, key: &[u8]) { + self.decrement_refcount_by(column, key, Self::ONE) + } + + /// Modifies a value in the database. + /// + /// Unlike `insert`, `increment_refcount` or `decrement_refcount`, arbitrary + /// modifications are allowed, and extra care must be taken to avoid + /// consistency anomalies. + /// + /// Must not be used for reference-counted columns; use + /// ['Self::increment_refcount'] or [`Self::decrement_refcount`] instead. + pub fn set(&mut self, column: DBCol, key: &[u8], value: &[u8]) { + assert!(!(column.is_rc() || column.is_insert_only()), "can't set: {column}"); + self.transaction.set(column, key.to_vec(), value.to_vec()) + } + + /// Saves a BorshSerialized value. + /// + /// Must not be used for reference-counted columns; use + /// ['Self::increment_refcount'] or [`Self::decrement_refcount`] instead. + pub fn set_ser( + &mut self, + column: DBCol, + key: &[u8], + value: &T, + ) -> io::Result<()> { + assert!(!(column.is_rc() || column.is_insert_only()), "can't set_ser: {column}"); + let data = borsh::to_vec(&value)?; + self.set(column, key, &data); + Ok(()) + } + + /// Modify raw value stored in the database, without doing any sanity checks + /// for ref counts. + /// + /// This method is a deliberate escape hatch, and shouldn't be used outside + /// of auxilary code like migrations which wants to hack on the database + /// directly. + pub fn set_raw_bytes(&mut self, column: DBCol, key: &[u8], value: &[u8]) { + self.transaction.set(column, key.to_vec(), value.to_vec()) + } + + /// Deletes the given key from the database. + /// + /// Must not be used for reference-counted columns; use + /// ['Self::increment_refcount'] or [`Self::decrement_refcount`] instead. + pub fn delete(&mut self, column: DBCol, key: &[u8]) { + assert!(!column.is_rc(), "can't delete: {column}"); + self.transaction.delete(column, key.to_vec()); + } + + pub fn delete_all(&mut self, column: DBCol) { + self.transaction.delete_all(column); + } + + /// Deletes the given key range from the database including `from` + /// and excluding `to` keys. + pub fn delete_range(&mut self, column: DBCol, from: &[u8], to: &[u8]) { + self.transaction.delete_range(column, from.to_vec(), to.to_vec()); + } + + /// Merge another store update into this one. + /// + /// Panics if `self`’s and `other`’s storage are incompatible. + pub fn merge(&mut self, other: StoreUpdate) { + assert!(same_db(&self.storage, &other.storage)); + self.transaction.merge(other.transaction) + } + + pub fn commit(self) -> io::Result<()> { + debug_assert!( + { + let non_refcount_keys = self + .transaction + .ops + .iter() + .filter_map(|op| match op { + DBOp::Set { col, key, .. } + | DBOp::Insert { col, key, .. } + | DBOp::Delete { col, key } => Some((*col as u8, key)), + DBOp::UpdateRefcount { .. } + | DBOp::DeleteAll { .. } + | DBOp::DeleteRange { .. } => None, + }) + .collect::>(); + non_refcount_keys.len() + == non_refcount_keys.iter().collect::>().len() + }, + "Transaction overwrites itself: {:?}", + self + ); + let _span = tracing::trace_span!(target: "store", "commit").entered(); + for op in &self.transaction.ops { + match op { + DBOp::Insert { col, key, value } => { + tracing::trace!(target: "store", db_op = "insert", col = %col, key = %StorageKey(key), size = value.len(), value = %AbbrBytes(value),) + } + DBOp::Set { col, key, value } => { + tracing::trace!(target: "store", db_op = "set", col = %col, key = %StorageKey(key), size = value.len(), value = %AbbrBytes(value)) + } + DBOp::UpdateRefcount { col, key, value } => { + tracing::trace!(target: "store", db_op = "update_rc", col = %col, key = %StorageKey(key), size = value.len(), value = %AbbrBytes(value)) + } + DBOp::Delete { col, key } => { + tracing::trace!(target: "store", db_op = "delete", col = %col, key = %StorageKey(key)) + } + DBOp::DeleteAll { col } => { + tracing::trace!(target: "store", db_op = "delete_all", col = %col) + } + DBOp::DeleteRange { col, from, to } => { + tracing::trace!(target: "store", db_op = "delete_range", col = %col, from = %StorageKey(from), to = %StorageKey(to)) + } + } + } + self.storage.write(self.transaction) + } +} + +fn same_db(lhs: &Arc, rhs: &Arc) -> bool { + // Note: avoid comparing wide pointers here to work-around + // https://github.com/rust-lang/rust/issues/69757 + let addr = |arc| Arc::as_ptr(arc) as *const u8; + return addr(lhs) == addr(rhs); +} + +impl fmt::Debug for StoreUpdate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "Store Update {{")?; + for op in self.transaction.ops.iter() { + match op { + DBOp::Insert { col, key, .. } => writeln!(f, " + {col} {}", StorageKey(key))?, + DBOp::Set { col, key, .. } => writeln!(f, " = {col} {}", StorageKey(key))?, + DBOp::UpdateRefcount { col, key, .. } => { + writeln!(f, " ± {col} {}", StorageKey(key))? + } + DBOp::Delete { col, key } => writeln!(f, " - {col} {}", StorageKey(key))?, + DBOp::DeleteAll { col } => writeln!(f, " - {col} (all)")?, + DBOp::DeleteRange { col, from, to } => { + writeln!(f, " - {col} [{}, {})", StorageKey(from), StorageKey(to))? + } + } + } + writeln!(f, "}}") + } +} + +/// Reads an object from Trie. +/// # Errors +/// see StorageError +pub fn get( + trie: &dyn TrieAccess, + key: &TrieKey, +) -> Result, StorageError> { + match trie.get(key)? { + None => Ok(None), + Some(data) => match T::try_from_slice(&data) { + Err(_err) => { + Err(StorageError::StorageInconsistentState("Failed to deserialize".to_string())) + } + Ok(value) => Ok(Some(value)), + }, + } +} + +/// Writes an object into Trie. +pub fn set(state_update: &mut TrieUpdate, key: TrieKey, value: &T) { + let data = borsh::to_vec(&value).expect("Borsh serializer is not expected to ever fail"); + state_update.set(key, data); +} + +pub fn set_account(state_update: &mut TrieUpdate, account_id: AccountId, account: &Account) { + set(state_update, TrieKey::Account { account_id }, account) +} + +pub fn get_account( + trie: &dyn TrieAccess, + account_id: &AccountId, +) -> Result, StorageError> { + get(trie, &TrieKey::Account { account_id: account_id.clone() }) +} + +pub fn set_received_data( + state_update: &mut TrieUpdate, + receiver_id: AccountId, + data_id: CryptoHash, + data: &ReceivedData, +) { + set(state_update, TrieKey::ReceivedData { receiver_id, data_id }, data); +} + +pub fn get_received_data( + trie: &dyn TrieAccess, + receiver_id: &AccountId, + data_id: CryptoHash, +) -> Result, StorageError> { + get(trie, &TrieKey::ReceivedData { receiver_id: receiver_id.clone(), data_id }) +} + +pub fn set_postponed_receipt(state_update: &mut TrieUpdate, receipt: &Receipt) { + let key = TrieKey::PostponedReceipt { + receiver_id: receipt.receiver_id.clone(), + receipt_id: receipt.receipt_id, + }; + set(state_update, key, receipt); +} + +pub fn remove_postponed_receipt( + state_update: &mut TrieUpdate, + receiver_id: &AccountId, + receipt_id: CryptoHash, +) { + state_update.remove(TrieKey::PostponedReceipt { receiver_id: receiver_id.clone(), receipt_id }); +} + +pub fn get_postponed_receipt( + trie: &dyn TrieAccess, + receiver_id: &AccountId, + receipt_id: CryptoHash, +) -> Result, StorageError> { + get(trie, &TrieKey::PostponedReceipt { receiver_id: receiver_id.clone(), receipt_id }) +} + +pub fn get_delayed_receipt_indices( + trie: &dyn TrieAccess, +) -> Result { + Ok(get(trie, &TrieKey::DelayedReceiptIndices)?.unwrap_or_default()) +} + +// Adds the given receipt into the end of the delayed receipt queue in the state. +pub fn set_delayed_receipt( + state_update: &mut TrieUpdate, + delayed_receipts_indices: &mut DelayedReceiptIndices, + receipt: &Receipt, +) { + set( + state_update, + TrieKey::DelayedReceipt { index: delayed_receipts_indices.next_available_index }, + receipt, + ); + delayed_receipts_indices.next_available_index = delayed_receipts_indices + .next_available_index + .checked_add(1) + .expect("Next available index for delayed receipt exceeded the integer limit"); +} + +pub fn set_access_key( + state_update: &mut TrieUpdate, + account_id: AccountId, + public_key: PublicKey, + access_key: &AccessKey, +) { + set(state_update, TrieKey::AccessKey { account_id, public_key }, access_key); +} + +pub fn remove_access_key( + state_update: &mut TrieUpdate, + account_id: AccountId, + public_key: PublicKey, +) { + state_update.remove(TrieKey::AccessKey { account_id, public_key }); +} + +pub fn get_access_key( + trie: &dyn TrieAccess, + account_id: &AccountId, + public_key: &PublicKey, +) -> Result, StorageError> { + get( + trie, + &TrieKey::AccessKey { account_id: account_id.clone(), public_key: public_key.clone() }, + ) +} + +pub fn get_access_key_raw( + trie: &dyn TrieAccess, + raw_key: &[u8], +) -> Result, StorageError> { + get( + trie, + &trie_key_parsers::parse_trie_key_access_key_from_raw_key(raw_key) + .expect("access key in the state should be correct"), + ) +} + +pub fn set_rsa2048_keys( + state_update: &mut TrieUpdate, + account_id: AccountId, + public_key: PublicKey, + rsa_key: &RegisterRsa2048KeysAction, +) { + set(state_update, TrieKey::Rsa2048Keys { account_id, public_key }, rsa_key); +} + +pub fn remove_rsa2048_keys( + state_update: &mut TrieUpdate, + account_id: AccountId, + public_key: PublicKey, +) { + state_update.remove(TrieKey::Rsa2048Keys { account_id, public_key }); +} + +pub fn get_rsa2048_keys( + trie: &dyn TrieAccess, + account_id: &AccountId, + public_key: &PublicKey, +) -> Result, StorageError> { + get( + trie, + &TrieKey::Rsa2048Keys { account_id: account_id.clone(), public_key: public_key.clone() }, + ) +} + +pub fn get_rsa2048_keys_raw( + trie: &dyn TrieAccess, + raw_key: &[u8], +) -> Result, StorageError> { + get( + trie, + &trie_key_parsers::parse_trie_key_rsa_key_from_raw_key(raw_key) + .expect("rsa key in the state should be correct"), + ) +} + +pub fn set_code(state_update: &mut TrieUpdate, account_id: AccountId, code: &ContractCode) { + state_update.set(TrieKey::ContractCode { account_id }, code.code().to_vec()); +} + +pub fn get_code( + trie: &dyn TrieAccess, + account_id: &AccountId, + code_hash: Option, +) -> Result, StorageError> { + let key = TrieKey::ContractCode { account_id: account_id.clone() }; + trie.get(&key).map(|opt| opt.map(|code| ContractCode::new(code, code_hash))) +} + +/// Removes account, code and all access keys associated to it. +pub fn remove_account( + state_update: &mut TrieUpdate, + account_id: &AccountId, +) -> Result<(), StorageError> { + state_update.remove(TrieKey::Account { account_id: account_id.clone() }); + state_update.remove(TrieKey::ContractCode { account_id: account_id.clone() }); + + // Removing access keys + let public_keys = state_update + .iter(&trie_key_parsers::get_raw_prefix_for_access_keys(account_id))? + .map(|raw_key| { + trie_key_parsers::parse_public_key_from_access_key_key(&raw_key?, account_id).map_err( + |_e| { + StorageError::StorageInconsistentState( + "Can't parse public key from raw key for AccessKey".to_string(), + ) + }, + ) + }) + .collect::, _>>()?; + for public_key in public_keys { + state_update.remove(TrieKey::AccessKey { account_id: account_id.clone(), public_key }); + } + + // Removing contract data + let data_keys = state_update + .iter(&trie_key_parsers::get_raw_prefix_for_contract_data(account_id, &[]))? + .map(|raw_key| { + trie_key_parsers::parse_data_key_from_contract_data_key(&raw_key?, account_id) + .map_err(|_e| { + StorageError::StorageInconsistentState( + "Can't parse data key from raw key for ContractData".to_string(), + ) + }) + .map(Vec::from) + }) + .collect::, _>>()?; + for key in data_keys { + state_update.remove(TrieKey::ContractData { account_id: account_id.clone(), key }); + } + Ok(()) +} + +pub fn get_genesis_state_roots(store: &Store) -> io::Result>> { + store.get_ser::>(DBCol::BlockMisc, GENESIS_STATE_ROOTS_KEY) +} + +pub fn get_genesis_hash(store: &Store) -> io::Result> { + store.get_ser::(DBCol::BlockMisc, GENESIS_JSON_HASH_KEY) +} + +pub fn set_genesis_hash(store_update: &mut StoreUpdate, genesis_hash: &CryptoHash) { + store_update + .set_ser::(DBCol::BlockMisc, GENESIS_JSON_HASH_KEY, genesis_hash) + .expect("Borsh cannot fail"); +} + +pub fn set_genesis_state_roots(store_update: &mut StoreUpdate, genesis_roots: &[StateRoot]) { + store_update + .set_ser(DBCol::BlockMisc, GENESIS_STATE_ROOTS_KEY, genesis_roots) + .expect("Borsh cannot fail"); +} + +fn option_to_not_found(res: io::Result>, field_name: F) -> io::Result +where + F: std::string::ToString, +{ + match res { + Ok(Some(o)) => Ok(o), + Ok(None) => Err(io::Error::new(io::ErrorKind::NotFound, field_name.to_string())), + Err(e) => Err(e), + } +} + +pub struct StoreCompiledContractCache { + db: Arc, +} + +impl StoreCompiledContractCache { + pub fn new(store: &Store) -> Self { + Self { db: store.storage.clone() } + } +} + +/// Cache for compiled contracts code using Store for keeping data. +/// We store contracts in VM-specific format in DBCol::CachedContractCode. +/// Key must take into account VM being used and its configuration, so that +/// we don't cache non-gas metered binaries, for example. +impl CompiledContractCache for StoreCompiledContractCache { + fn put(&self, key: &CryptoHash, value: CompiledContract) -> io::Result<()> { + let mut update = crate::db::DBTransaction::new(); + // We intentionally use `.set` here, rather than `.insert`. We don't yet + // guarantee deterministic compilation, so, if we happen to compile the + // same contract concurrently on two threads, the `value`s might differ, + // but this doesn't matter. + update.set( + DBCol::CachedContractCode, + key.as_ref().to_vec(), + borsh::to_vec(&value).unwrap(), + ); + self.db.write(update) + } + + fn get(&self, key: &CryptoHash) -> io::Result> { + match self.db.get_raw_bytes(DBCol::CachedContractCode, key.as_ref()) { + Ok(Some(bytes)) => Ok(Some(CompiledContract::try_from_slice(&bytes)?)), + Ok(None) => Ok(None), + Err(err) => Err(err), + } + } + + fn has(&self, key: &CryptoHash) -> io::Result { + self.db.get_raw_bytes(DBCol::CachedContractCode, key.as_ref()).map(|entry| entry.is_some()) + } +} + +#[cfg(test)] +mod tests { + use unc_primitives::hash::CryptoHash; + + use super::{DBCol, NodeStorage, Store}; + + #[test] + fn test_no_cache_disabled() { + #[cfg(feature = "no_cache")] + panic!("no cache is enabled"); + } + + fn test_clear_column(store: Store) { + assert_eq!(store.get(DBCol::State, &[1]).unwrap(), None); + { + let mut store_update = store.store_update(); + store_update.increment_refcount(DBCol::State, &[1], &[1]); + store_update.increment_refcount(DBCol::State, &[2], &[2]); + store_update.increment_refcount(DBCol::State, &[3], &[3]); + store_update.commit().unwrap(); + } + assert_eq!(store.get(DBCol::State, &[1]).unwrap().as_deref(), Some(&[1][..])); + { + let mut store_update = store.store_update(); + store_update.delete_all(DBCol::State); + store_update.commit().unwrap(); + } + assert_eq!(store.get(DBCol::State, &[1]).unwrap(), None); + } + + #[test] + fn clear_column_rocksdb() { + let (_tmp_dir, opener) = NodeStorage::test_opener(); + test_clear_column(opener.open().unwrap().get_hot_store()); + } + + #[test] + fn clear_column_testdb() { + test_clear_column(crate::test_utils::create_test_store()); + } + + /// Asserts that elements in the vector are sorted. + #[track_caller] + fn assert_sorted(want_count: usize, keys: Vec>) { + assert_eq!(want_count, keys.len()); + for (pos, pair) in keys.windows(2).enumerate() { + let (fst, snd) = (&pair[0], &pair[1]); + assert!(fst <= snd, "{fst:?} > {snd:?} at {pos}"); + } + } + + /// Checks that keys are sorted when iterating. + fn test_iter_order_impl(store: Store) { + use rand::Rng; + + // An arbitrary non-rc non-insert-only column we can write data into. + const COLUMN: DBCol = DBCol::RecentOutboundConnections; + assert!(!COLUMN.is_rc()); + assert!(!COLUMN.is_insert_only()); + + const COUNT: usize = 10_000; + const PREFIXES: [[u8; 4]; 6] = + [*b"foo0", *b"foo1", *b"foo2", *b"foo\xff", *b"fop\0", *b"\xff\xff\xff\xff"]; + + // Fill column with random keys. We're inserting multiple sets of keys + // with different four-byte prefixes.. Each set is `COUNT` keys (for + // total of `PREFIXES.len()*COUNT` keys). + let mut rng: rand::rngs::StdRng = rand::SeedableRng::seed_from_u64(0x3243f6a8885a308d); + let mut update = store.store_update(); + let mut buf = [0u8; 20]; + for prefix in PREFIXES.iter() { + buf[..prefix.len()].clone_from_slice(prefix); + for _ in 0..COUNT { + rng.fill(&mut buf[prefix.len()..]); + update.set(COLUMN, &buf, &buf); + } + } + update.commit().unwrap(); + + fn collect<'a>(iter: crate::db::DBIterator<'a>) -> Vec> { + iter.map(Result::unwrap).map(|(key, _)| key).collect() + } + + // Check that full scan produces keys in proper order. + assert_sorted(PREFIXES.len() * COUNT, collect(store.iter(COLUMN))); + assert_sorted(PREFIXES.len() * COUNT, collect(store.iter_raw_bytes(COLUMN))); + assert_sorted(PREFIXES.len() * COUNT, collect(store.iter_prefix(COLUMN, b""))); + + // Check that prefix scan produces keys in proper order. + for prefix in PREFIXES.iter() { + let keys = collect(store.iter_prefix(COLUMN, prefix)); + for (pos, key) in keys.iter().enumerate() { + assert_eq!( + prefix, + &key[0..4], + "Expected {prefix:?} prefix but got {key:?} key at {pos}" + ); + } + assert_sorted(COUNT, keys); + } + } + + #[test] + fn rocksdb_iter_order() { + let (_tempdir, opener) = NodeStorage::test_opener(); + test_iter_order_impl(opener.open().unwrap().get_hot_store()); + } + + #[test] + fn testdb_iter_order() { + test_iter_order_impl(crate::test_utils::create_test_store()); + } + + /// Check StoreCompiledContractCache implementation. + #[test] + fn test_store_compiled_contract_cache() { + use unc_vm_runner::logic::{CompiledContract, CompiledContractCache}; + use std::str::FromStr; + + let store = crate::test_utils::create_test_store(); + let cache = super::StoreCompiledContractCache::new(&store); + let key = CryptoHash::from_str("75pAU4CJcp8Z9eoXcL6pSU8sRK5vn3NEpgvUrzZwQtr3").unwrap(); + + assert_eq!(None, cache.get(&key).unwrap()); + assert_eq!(false, cache.has(&key).unwrap()); + + let record = CompiledContract::Code(b"foo".to_vec()); + cache.put(&key, record.clone()).unwrap(); + assert_eq!(Some(record), cache.get(&key).unwrap()); + assert_eq!(true, cache.has(&key).unwrap()); + } + + /// Check saving and reading columns to/from a file. + #[test] + fn test_save_to_file() { + let mut tmp = tempfile::NamedTempFile::new().unwrap(); + + { + let store = crate::test_utils::create_test_store(); + let mut store_update = store.store_update(); + store_update.increment_refcount(DBCol::State, &[1], &[1]); + store_update.increment_refcount(DBCol::State, &[2], &[2]); + store_update.increment_refcount(DBCol::State, &[2], &[2]); + store_update.commit().unwrap(); + store.save_state_to_file(tmp.path()).unwrap(); + } + + // Verify expected encoding. + { + let mut buffer = Vec::new(); + std::io::Read::read_to_end(tmp.as_file_mut(), &mut buffer).unwrap(); + #[rustfmt::skip] + assert_eq!(&[ + /* column: */ 0, /* key len: */ 1, 0, 0, 0, /* key: */ 1, + /* val len: */ 9, 0, 0, 0, /* val: */ 1, 1, 0, 0, 0, 0, 0, 0, 0, + /* column: */ 0, /* key len: */ 1, 0, 0, 0, /* key: */ 2, + /* val len: */ 9, 0, 0, 0, /* val: */ 2, 2, 0, 0, 0, 0, 0, 0, 0, + /* end mark: */ 255, + ][..], buffer.as_slice()); + } + + { + // Fresh storage, should have no data. + let store = crate::test_utils::create_test_store(); + assert_eq!(None, store.get(DBCol::State, &[1]).unwrap()); + assert_eq!(None, store.get(DBCol::State, &[2]).unwrap()); + + // Read data from file. + store.load_state_from_file(tmp.path()).unwrap(); + assert_eq!(Some(&[1u8][..]), store.get(DBCol::State, &[1]).unwrap().as_deref()); + assert_eq!(Some(&[2u8][..]), store.get(DBCol::State, &[2]).unwrap().as_deref()); + + // Key &[2] should have refcount of two so once decreased it should + // still exist. + let mut store_update = store.store_update(); + store_update.decrement_refcount(DBCol::State, &[1]); + store_update.decrement_refcount(DBCol::State, &[2]); + store_update.commit().unwrap(); + assert_eq!(None, store.get(DBCol::State, &[1]).unwrap()); + assert_eq!(Some(&[2u8][..]), store.get(DBCol::State, &[2]).unwrap().as_deref()); + } + + // Verify detection of corrupt file. + let file = std::fs::File::options().write(true).open(tmp.path()).unwrap(); + let len = file.metadata().unwrap().len(); + file.set_len(len.saturating_sub(1)).unwrap(); + core::mem::drop(file); + let store = crate::test_utils::create_test_store(); + assert_eq!( + std::io::ErrorKind::InvalidData, + store.load_state_from_file(tmp.path()).unwrap_err().kind() + ); + } +} diff --git a/core/store/src/metadata.rs b/core/store/src/metadata.rs new file mode 100644 index 000000000..62a45901f --- /dev/null +++ b/core/store/src/metadata.rs @@ -0,0 +1,129 @@ +/// Database version type. +pub type DbVersion = u32; + +/// Current version of the database. +pub const DB_VERSION: DbVersion = 38; + +/// Database version at which point DbKind was introduced. +const DB_VERSION_WITH_KIND: DbVersion = 34; + +/// Key for the version entry in DBCol::DbVersion. +/// +/// The key holds [`DbVersion`] value serialised to a string. +/// +/// The version is strictly increasing. We bump it each time +/// a backwards-incompatible change to the database is required. Increasing the +/// version involves performing a migration since node can only open databases +/// with version they was built for (see [`DB_VERSION`]). +pub(super) const VERSION_KEY: &[u8; 7] = b"VERSION"; + +/// Key for the database kind entry in DBCol::DbVersion. +/// +/// The key holds a [`DbKind`] value serialised to a string. +pub(super) const KIND_KEY: &[u8; 4] = b"KIND"; + +/// Describes what kind the storage is. +#[derive( + Clone, Copy, Debug, Eq, PartialEq, strum::Display, strum::EnumString, strum::IntoStaticStr, +)] +pub enum DbKind { + /// The database is an RPC database meaning that it is garbage collected and + /// stores only a handful of epochs worth of data. + RPC, + /// The database is an archive database meaning that it is not garbage + /// collected and stores all chain data. + Archive, + /// The database is Hot meaning that the node runs in archival mode with + /// a paired Cold database. + Hot, + /// The database is Cold meaning that the node runs in archival mode with + /// a paired Hot database. + Cold, +} + +/// Metadata about a database. +#[derive(Clone, Copy)] +pub(super) struct DbMetadata { + /// Version of the database. + pub version: DbVersion, + + /// Kind of the database. + /// + /// This is always set if version is ≥ [`DB_VERSION_WITH_KIND`] and always + /// `None` otherwise. + pub kind: Option, +} + +impl DbMetadata { + /// Reads metadata from the database. This method enforces the invariant + /// that version and kind must alwasy be set. + /// + /// If the database version is not present, returns an error. Similarly, if + /// database version is ≥ [`DB_VERSION_WITH_KIND`] but the kind is not + /// specified, returns an error. + pub(super) fn read(db: &dyn crate::Database) -> std::io::Result { + let version = read("DbVersion", db, VERSION_KEY)?; + let kind = if version < DB_VERSION_WITH_KIND { + // If database is at version less than DB_VERSION_WITH_KIND then it + // doesn’t have kind. Return None as kind. + None + } else { + Some(read("DbKind", db, KIND_KEY)?) + }; + Ok(Self { version, kind }) + } + + /// Reads the version from the db. If version is not set returns None. This + /// method doesn't enforce the invariant that version must always be set so + /// it should only be used when setting the version for the first time. + pub(super) fn maybe_read_version( + db: &dyn crate::Database, + ) -> std::io::Result> { + maybe_read("DbVersion", db, VERSION_KEY) + } + + /// Reads the kind from the db. If kind is not set returns None. This method + /// doesn't enforce the invariant that kind must always be set so it should + /// only be used when setting the kind for the first time. + pub(super) fn maybe_read_kind(db: &dyn crate::Database) -> std::io::Result> { + maybe_read("DbKind", db, KIND_KEY) + } +} + +/// Reads value from DbVersion column and parses it using `FromStr`. +/// +/// Same as maybe_read but this method returns an error if the value is not set. +fn read( + what: &str, + db: &dyn crate::Database, + key: &[u8], +) -> std::io::Result { + let msg = "it’s not a uncd database or database is corrupted"; + let result = maybe_read::(what, db, key)?; + + match result { + Some(value) => Ok(value), + None => Err(std::io::Error::other(format!("missing {what}; {msg}"))), + } +} + +/// Reads value from DbVersion column and parses it using `FromStr`. +/// +/// Reads raw bytes for given `key` from [`DBCol::DbVersion`], verifies that +/// they’re valid UTF-8 and then converts into `T` using `from_str`. If the +/// value is missing or parsing fails returns None. +fn maybe_read( + what: &str, + db: &dyn crate::Database, + key: &[u8], +) -> std::io::Result> { + let msg = "it’s not a uncd database or database is corrupted"; + db.get_raw_bytes(crate::DBCol::DbVersion, key)? + .map(|bytes| { + let value = std::str::from_utf8(&bytes) + .map_err(|_err| format!("invalid {what}: {bytes:?}; {msg}"))?; + T::from_str(value).map_err(|_err| format!("invalid {what}: ‘{value}’; {msg}")) + }) + .transpose() + .map_err(std::io::Error::other) +} diff --git a/core/store/src/metrics.rs b/core/store/src/metrics.rs new file mode 100644 index 000000000..5a6c3159c --- /dev/null +++ b/core/store/src/metrics.rs @@ -0,0 +1,664 @@ +use crate::rocksdb_metrics::export_stats_as_metrics; +use crate::{NodeStorage, Store, Temperature}; +use actix_rt::ArbiterHandle; +use unc_o11y::metrics::{ + exponential_buckets, try_create_histogram, try_create_histogram_vec, + try_create_histogram_with_buckets, try_create_int_counter_vec, try_create_int_gauge, + try_create_int_gauge_vec, Histogram, HistogramVec, IntCounterVec, IntGauge, IntGaugeVec, +}; +use once_cell::sync::Lazy; + +pub(crate) static DATABASE_OP_LATENCY_HIST: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_database_op_latency_by_op_and_column", + "Database operations latency by operation and column.", + &["op", "column"], + Some(vec![0.00002, 0.0001, 0.0002, 0.0005, 0.0008, 0.001, 0.002, 0.004, 0.008, 0.1]), + ) + .unwrap() +}); + +// TODO(#9054): Rename the metric to be consistent with "accounting cache". +pub static CHUNK_CACHE_HITS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_chunk_cache_hits", + "Chunk cache hits", + &["shard_id", "is_view"], + ) + .unwrap() +}); + +// TODO(#9054): Rename the metric to be consistent with "accounting cache". +pub static CHUNK_CACHE_MISSES: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_chunk_cache_misses", + "Chunk cache misses", + &["shard_id", "is_view"], + ) + .unwrap() +}); + +pub static SHARD_CACHE_HITS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_shard_cache_hits", + "Shard cache hits", + &["shard_id", "is_view"], + ) + .unwrap() +}); + +pub static SHARD_CACHE_MISSES: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_shard_cache_misses", + "Shard cache misses", + &["shard_id", "is_view"], + ) + .unwrap() +}); + +pub static SHARD_CACHE_TOO_LARGE: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_shard_cache_too_large", + "Number of values to be inserted into shard cache is too large", + &["shard_id", "is_view"], + ) + .unwrap() +}); + +pub static SHARD_CACHE_SIZE: Lazy = Lazy::new(|| { + try_create_int_gauge_vec("unc_shard_cache_size", "Shard cache size", &["shard_id", "is_view"]) + .unwrap() +}); + +// TODO(#9054): Rename the metric to be consistent with "accounting cache". +pub static CHUNK_CACHE_SIZE: Lazy = Lazy::new(|| { + try_create_int_gauge_vec("unc_chunk_cache_size", "Chunk cache size", &["shard_id", "is_view"]) + .unwrap() +}); + +pub static SHARD_CACHE_CURRENT_TOTAL_SIZE: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_shard_cache_current_total_size", + "Shard cache current total size", + &["shard_id", "is_view"], + ) + .unwrap() +}); + +pub static SHARD_CACHE_POP_HITS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_shard_cache_pop_hits", + "Shard cache pop hits", + &["shard_id", "is_view"], + ) + .unwrap() +}); +pub static SHARD_CACHE_POP_MISSES: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_shard_cache_pop_misses", + "Shard cache pop misses", + &["shard_id", "is_view"], + ) + .unwrap() +}); +pub static SHARD_CACHE_POP_LRU: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_shard_cache_pop_lru", + "Shard cache LRU pops", + &["shard_id", "is_view"], + ) + .unwrap() +}); +pub static SHARD_CACHE_GC_POP_MISSES: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_shard_cache_gc_pop_misses", + "Shard cache gc pop misses", + &["shard_id", "is_view"], + ) + .unwrap() +}); +pub static SHARD_CACHE_DELETIONS_SIZE: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_shard_cache_deletions_size", + "Shard cache deletions size", + &["shard_id", "is_view"], + ) + .unwrap() +}); +pub static APPLIED_TRIE_DELETIONS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_applied_trie_deletions", + "Trie deletions applied to store", + &["shard_id"], + ) + .unwrap() +}); +pub static APPLIED_TRIE_INSERTIONS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_applied_trie_insertions", + "Trie insertions applied to store", + &["shard_id"], + ) + .unwrap() +}); +pub static REVERTED_TRIE_INSERTIONS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_reverted_trie_insertions", + "Trie insertions reverted due to GC of forks", + &["shard_id"], + ) + .unwrap() +}); +pub static PREFETCH_SENT: Lazy = Lazy::new(|| { + try_create_int_counter_vec("unc_prefetch_sent", "Prefetch requests sent to DB", &["shard_id"]) + .unwrap() +}); +pub static PREFETCH_HITS: Lazy = Lazy::new(|| { + try_create_int_counter_vec("unc_prefetch_hits", "Prefetched trie keys", &["shard_id"]).unwrap() +}); +pub static PREFETCH_PENDING: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_prefetch_pending", + "Prefetched trie keys that were still pending when main thread needed data", + &["shard_id"], + ) + .unwrap() +}); +pub static PREFETCH_FAIL: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_prefetch_fail", + "Prefetching trie key failed with an error", + &["shard_id"], + ) + .unwrap() +}); +pub static PREFETCH_NOT_REQUESTED: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_prefetch_not_requested", + "Number of values that had to be fetched without having been prefetched", + &["shard_id"], + ) + .unwrap() +}); +pub static PREFETCH_MEMORY_LIMIT_REACHED: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_prefetch_memory_limit_reached", + "Number of values that could not be prefetched due to prefetch staging area size limitations", + &["shard_id"], + ) + .unwrap() +}); +pub static PREFETCH_CONFLICT: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_prefetch_conflict", + "Main thread retrieved value from shard_cache after a conflict with another main thread from a fork.", + &["shard_id"], + ) + .unwrap() +}); +pub static PREFETCH_RETRY: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_prefetch_retries", + "Main thread was waiting for prefetched value but had to retry fetch afterwards.", + &["shard_id"], + ) + .unwrap() +}); +pub static PREFETCH_STAGED_BYTES: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_prefetch_staged_bytes", + "Upper bound on memory usage for holding prefetched data.", + &["shard_id"], + ) + .unwrap() +}); +pub static PREFETCH_STAGED_SLOTS: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_prefetch_staged_slots", + "Number of slots used in staging area.", + &["shard_id"], + ) + .unwrap() +}); +pub static COLD_MIGRATION_READS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_cold_migration_reads", + "Number of get calls to hot store made for every column during copying data to cold storage.", + &["col"], + ) + .unwrap() +}); +pub static COLD_HEAD_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_cold_head_height", "Height of the head of cold storage").unwrap() +}); +pub static COLD_COPY_DURATION: Lazy = Lazy::new(|| { + try_create_histogram( + "unc_cold_copy_duration", + "Time it takes to copy one height to cold storage", + ) + .unwrap() +}); + +pub(crate) static HAS_STATE_SNAPSHOT: Lazy = Lazy::new(|| { + try_create_int_gauge("unc_has_state_snapshot", "Whether a node has a state snapshot open") + .unwrap() +}); + +pub(crate) static CREATE_STATE_SNAPSHOT_ELAPSED: Lazy = Lazy::new(|| { + try_create_histogram_with_buckets( + "unc_make_state_snapshot_elapsed_sec", + "Latency of making a state snapshot, in seconds", + exponential_buckets(0.01, 1.3, 30).unwrap(), + ) + .unwrap() +}); + +pub(crate) static DELETE_STATE_SNAPSHOT_ELAPSED: Lazy = Lazy::new(|| { + try_create_histogram_with_buckets( + "unc_delete_state_snapshot_elapsed_sec", + "Latency of deleting a state snapshot, in seconds", + exponential_buckets(0.001, 1.6, 25).unwrap(), + ) + .unwrap() +}); + +pub(crate) static COMPACT_STATE_SNAPSHOT_ELAPSED: Lazy = Lazy::new(|| { + try_create_histogram_with_buckets( + "unc_compact_state_snapshot_elapsed_sec", + "Latency of compaction of a state snapshot, in seconds", + exponential_buckets(0.001, 1.6, 40).unwrap(), + ) + .unwrap() +}); + +pub(crate) static MOVE_STATE_SNAPSHOT_FLAT_HEAD_ELAPSED: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_move_state_snapshot_flat_head_elapsed_sec", + "Latency of moving flat head of state snapshot, in seconds", + &["shard_id"], + Some(exponential_buckets(0.001, 1.6, 25).unwrap()), + ) + .unwrap() +}); + +pub(crate) static GET_STATE_PART_NODES_WITH_FS_ELAPSED: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_get_state_part_nodes_with_fs_elapsed_sec", + "Latency of creating a state part using flat storage given the boundaries, in seconds", + &["shard_id"], + Some(exponential_buckets(0.001, 1.6, 25).unwrap()), + ) + .unwrap() +}); + +pub(crate) static GET_STATE_PART_BOUNDARIES_ELAPSED: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_get_state_part_boundaries_elapsed_sec", + "Latency of finding state part boundaries, in seconds", + &["shard_id"], + Some(exponential_buckets(0.001, 1.6, 25).unwrap()), + ) + .unwrap() +}); + +pub(crate) static GET_STATE_PART_READ_FS_ELAPSED: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_get_state_part_with_fs_read_fs_elapsed_sec", + "Latency of reading FS columns, in seconds", + &["shard_id"], + Some(exponential_buckets(0.001, 1.6, 25).unwrap()), + ) + .unwrap() +}); + +pub(crate) static GET_STATE_PART_LOOKUP_REF_VALUES_ELAPSED: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_get_state_part_with_fs_lookup_value_refs_elapsed_sec", + "Latency of looking references values, in seconds", + &["shard_id"], + Some(exponential_buckets(0.001, 1.6, 25).unwrap()), + ) + .unwrap() +}); + +pub(crate) static GET_STATE_PART_CREATE_TRIE_ELAPSED: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_get_state_part_with_fs_create_trie_elapsed_sec", + "Latency of creation of trie from the data read from FS, in seconds", + &["shard_id"], + Some(exponential_buckets(0.001, 1.6, 25).unwrap()), + ) + .unwrap() +}); + +pub(crate) static GET_STATE_PART_COMBINE_ELAPSED: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_get_state_part_with_fs_combine_elapsed_sec", + "Latency of combining part boundaries and in-memory created nodes, in seconds", + &["shard_id"], + Some(exponential_buckets(0.001, 1.6, 25).unwrap()), + ) + .unwrap() +}); + +pub(crate) static GET_STATE_PART_WITH_FS_VALUES_INLINED: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_get_state_part_with_fs_values_inlined_count", + "Number of FS values that were inlined", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static GET_STATE_PART_WITH_FS_VALUES_REF: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_get_state_part_with_fs_values_ref_count", + "Number of FS values that were references", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static GET_STATE_PART_WITH_FS_NODES_FROM_DISK: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_get_state_part_with_fs_nodes_from_disk_count", + "Number of nodes in state part that are state part boundaries", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static GET_STATE_PART_WITH_FS_NODES_IN_MEMORY: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_get_state_part_with_fs_nodes_in_memory_count", + "Number of nodes in state part that created based on FS values", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static GET_STATE_PART_WITH_FS_NODES: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_get_state_part_with_fs_nodes_count", + "Total number of nodes in state parts created", + &["shard_id"], + ) + .unwrap() +}); + +pub mod flat_state_metrics { + use super::*; + + pub static FLAT_STORAGE_CREATION_STATUS: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "flat_storage_creation_status", + "Integer representing status of flat storage creation", + &["shard_id"], + ) + .unwrap() + }); + pub static FLAT_STORAGE_CREATION_REMAINING_STATE_PARTS: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "flat_storage_creation_remaining_state_parts", + "Number of remaining state parts to fetch to fill flat storage in bytes", + &["shard_id"], + ) + .unwrap() + }); + pub static FLAT_STORAGE_CREATION_FETCHED_STATE_PARTS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "flat_storage_creation_fetched_state_parts", + "Number of fetched state parts to fill flat storage in bytes", + &["shard_id"], + ) + .unwrap() + }); + pub static FLAT_STORAGE_CREATION_FETCHED_STATE_ITEMS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "flat_storage_creation_fetched_state_items", + "Number of fetched items to fill flat storage", + &["shard_id"], + ) + .unwrap() + }); + pub static FLAT_STORAGE_CREATION_THREADS_USED: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "flat_storage_creation_threads_used", + "Number of currently used threads to fetch state", + &["shard_id"], + ) + .unwrap() + }); + pub static FLAT_STORAGE_HEAD_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "flat_storage_head_height", + "Height of flat storage head", + &["shard_id"], + ) + .unwrap() + }); + pub static FLAT_STORAGE_CACHED_DELTAS: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "flat_storage_cached_deltas", + "Number of cached deltas in flat storage", + &["shard_id"], + ) + .unwrap() + }); + pub static FLAT_STORAGE_CACHED_CHANGES_NUM_ITEMS: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "flat_storage_cached_changes_num_items", + "Number of items in all cached changes in flat storage", + &["shard_id"], + ) + .unwrap() + }); + pub static FLAT_STORAGE_CACHED_CHANGES_SIZE: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "flat_storage_cached_changes_size", + "Total size of cached changes in flat storage", + &["shard_id"], + ) + .unwrap() + }); + pub static FLAT_STORAGE_DISTANCE_TO_HEAD: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "flat_storage_distance_to_head", + "Height distance between processed block and flat storage head", + &["shard_id"], + ) + .unwrap() + }); + pub static FLAT_STORAGE_HOPS_TO_HEAD: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "flat_storage_hops_to_head", + "Number of blocks visited to flat storage head", + &["shard_id"], + ) + .unwrap() + }); + + pub mod inlining_migration { + use unc_o11y::metrics::{ + try_create_histogram, try_create_int_counter, Histogram, IntCounter, + }; + use once_cell::sync::Lazy; + + pub static PROCESSED_COUNT: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_flat_state_inlining_migration_processed_count", + "Total number of processed FlatState rows since the migration start.", + ) + .unwrap() + }); + pub static PROCESSED_TOTAL_VALUES_SIZE: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_flat_state_inlining_migration_processed_total_values_size", + "Total size processed FlatState values since the migration start.", + ) + .unwrap() + }); + pub static INLINED_COUNT: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_flat_state_inlining_migration_inlined_count", + "Total number of inlined FlatState values since the migration start.", + ) + .unwrap() + }); + pub static INLINED_TOTAL_VALUES_SIZE: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_flat_state_inlining_migration_inlined_total_values_size", + "Total size of inlined FlatState values since the migration start.", + ) + .unwrap() + }); + pub static SKIPPED_COUNT: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_flat_state_inlining_migration_skipped_count", + "Total number of FlatState values skipped since the migration start due to some kind of an issue while trying to read the value.", + ) + .unwrap() + }); + pub static FLAT_STATE_PAUSED_DURATION: Lazy = Lazy::new(|| { + try_create_histogram( + "unc_flat_state_inlining_migration_flat_state_paused_duration", + "FlatState inlining paused duration.", + ) + .unwrap() + }); + } +} +pub static COLD_STORE_MIGRATION_BATCH_WRITE_COUNT: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_cold_migration_initial_writes", + "Number of write calls to cold store made for every column during initial population of cold storage.", + &["col"], + ) + .unwrap() +}); +pub static COLD_STORE_MIGRATION_BATCH_WRITE_TIME: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_cold_migration_initial_writes_time", + "Time spent on writing initial migration batches by column.", + &["column"], + None, + ) + .unwrap() +}); + +fn export_store_stats(store: &Store, temperature: Temperature) { + if let Some(stats) = store.get_store_statistics() { + tracing::debug!(target:"metrics", "Exporting the db metrics for {temperature:?} store."); + export_stats_as_metrics(stats, temperature); + } else { + // TODO Does that happen under normal circumstances? + // Should this log be a warning or error instead? + tracing::debug!(target:"metrics", "Exporting the db metrics for {temperature:?} store failed. The statistics are missing."); + } +} + +pub fn spawn_db_metrics_loop( + storage: &NodeStorage, + period: std::time::Duration, +) -> anyhow::Result { + tracing::debug!(target:"metrics", "Spawning the db metrics loop."); + let db_metrics_arbiter = actix_rt::Arbiter::new(); + + let start = tokio::time::Instant::now(); + let mut interval = actix_rt::time::interval_at(start, period); + interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); + + let hot_store = storage.get_hot_store(); + let cold_store = storage.get_cold_store(); + + db_metrics_arbiter.spawn(async move { + tracing::debug!(target:"metrics", "Starting the db metrics loop."); + loop { + interval.tick().await; + + export_store_stats(&hot_store, Temperature::Hot); + if let Some(cold_store) = &cold_store { + export_store_stats(cold_store, Temperature::Cold); + } + } + }); + + Ok(db_metrics_arbiter.handle()) +} + +#[cfg(test)] +mod test { + use std::time::Duration; + + use actix; + + use crate::db::{StatsValue, StoreStatistics}; + use crate::metadata::{DbKind, DB_VERSION}; + use crate::test_utils::create_test_node_storage_with_cold; + + use unc_o11y::testonly::init_test_logger; + + use super::spawn_db_metrics_loop; + + fn stat(name: &str, count: i64) -> (String, Vec) { + (name.into(), vec![StatsValue::Count(count)]) + } + + async fn test_db_metrics_loop_impl() -> anyhow::Result<()> { + let (storage, hot, cold) = create_test_node_storage_with_cold(DB_VERSION, DbKind::Cold); + let period = Duration::from_millis(100); + + let handle = spawn_db_metrics_loop(&storage, period)?; + + let hot_column_name = "hot.colum".to_string(); + let cold_column_name = "cold.colum".to_string(); + + let hot_gauge_name = hot_column_name.clone() + ""; + let cold_gauge_name = cold_column_name.clone() + "_cold"; + + let hot_stats = StoreStatistics { data: vec![stat(&hot_column_name, 42)] }; + let cold_stats = StoreStatistics { data: vec![stat(&cold_column_name, 52)] }; + + hot.set_store_statistics(hot_stats); + cold.set_store_statistics(cold_stats); + + actix::clock::sleep(period).await; + for _ in 0..10 { + let int_gauges = crate::rocksdb_metrics::get_int_gauges(); + + let has_hot_gauge = int_gauges.contains_key(&hot_gauge_name); + let has_cold_gauge = int_gauges.contains_key(&cold_gauge_name); + if has_hot_gauge && has_cold_gauge { + break; + } + actix::clock::sleep(period / 10).await; + } + + let int_gauges = crate::rocksdb_metrics::get_int_gauges(); + tracing::debug!("int_gauges {int_gauges:#?}"); + + let hot_gauge = int_gauges.get(&hot_gauge_name); + let hot_gauge = hot_gauge.ok_or(anyhow::anyhow!("hot gauge is missing"))?; + + let cold_gauge = int_gauges.get(&cold_gauge_name); + let cold_gauge = cold_gauge.ok_or(anyhow::anyhow!("cold gauge is missing"))?; + + assert_eq!(hot_gauge.get(), 42); + assert_eq!(cold_gauge.get(), 52); + + handle.stop(); + + Ok(()) + } + + #[test] + fn test_db_metrics_loop() { + init_test_logger(); + + let sys = actix::System::new(); + sys.block_on(test_db_metrics_loop_impl()).expect("test impl failed"); + + actix::System::current().stop(); + sys.run().unwrap(); + } +} diff --git a/core/store/src/migrations.rs b/core/store/src/migrations.rs new file mode 100644 index 000000000..1d7f8935c --- /dev/null +++ b/core/store/src/migrations.rs @@ -0,0 +1,231 @@ +use crate::metadata::DbKind; +use crate::{DBCol, Store, StoreUpdate}; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_primitives::state::FlatStateValue; +use unc_primitives::transaction::{ExecutionOutcomeWithIdAndProof, ExecutionOutcomeWithProof}; +use unc_primitives::utils::get_outcome_id_block_hash; +use std::collections::HashMap; +use tracing::info; + +pub struct BatchedStoreUpdate<'a> { + batch_size_limit: usize, + batch_size: usize, + store: &'a Store, + store_update: Option, + total_size_written: u64, + printed_total_size_written: u64, +} + +const PRINT_PROGRESS_EVERY_BYTES: u64 = bytesize::GIB; + +impl<'a> BatchedStoreUpdate<'a> { + pub fn new(store: &'a Store, batch_size_limit: usize) -> Self { + Self { + batch_size_limit, + batch_size: 0, + store, + store_update: Some(store.store_update()), + total_size_written: 0, + printed_total_size_written: 0, + } + } + + fn commit(&mut self) -> std::io::Result<()> { + let store_update = self.store_update.take().unwrap(); + store_update.commit()?; + self.store_update = Some(self.store.store_update()); + self.batch_size = 0; + Ok(()) + } + + fn set_or_insert_ser( + &mut self, + col: DBCol, + key: &[u8], + value: &T, + insert: bool, + ) -> std::io::Result<()> { + let value_bytes = borsh::to_vec(&value)?; + let entry_size = key.as_ref().len() + value_bytes.len() + 8; + self.batch_size += entry_size; + self.total_size_written += entry_size as u64; + let update = self.store_update.as_mut().unwrap(); + if insert { + update.insert(col, key.to_vec(), value_bytes); + } else { + update.set(col, key.as_ref(), &value_bytes); + } + + if self.batch_size > self.batch_size_limit { + self.commit()?; + } + if self.total_size_written - self.printed_total_size_written > PRINT_PROGRESS_EVERY_BYTES { + info!( + target: "migrations", + "Migrations: {} written", + bytesize::to_string(self.total_size_written, true) + ); + self.printed_total_size_written = self.total_size_written; + } + + Ok(()) + } + + pub fn set_ser( + &mut self, + col: DBCol, + key: &[u8], + value: &T, + ) -> std::io::Result<()> { + self.set_or_insert_ser(col, key, value, false) + } + + pub fn insert_ser( + &mut self, + col: DBCol, + key: &[u8], + value: &T, + ) -> std::io::Result<()> { + self.set_or_insert_ser(col, key, value, true) + } + + pub fn finish(mut self) -> std::io::Result<()> { + if self.batch_size > 0 { + self.commit()?; + } + + Ok(()) + } +} + +/// Migrates the database from version 32 to 33. +/// +/// This removes the TransactionResult column and moves it to TransactionResultForBlock. +/// The new column removes the need for high-latency read-modify-write operations when committing +/// new blocks. +pub fn migrate_32_to_33(store: &Store) -> anyhow::Result<()> { + let mut update = BatchedStoreUpdate::new(&store, 10_000_000); + for row in + store.iter_prefix_ser::>(DBCol::_TransactionResult, &[]) + { + let (_, mut outcomes) = row?; + // It appears that it was possible that the same entry in the original column contained + // duplicate outcomes. We remove them here to avoid panicing due to issuing a + // self-overwriting transaction. + outcomes.sort_by_key(|outcome| (*outcome.id(), outcome.block_hash)); + outcomes.dedup_by_key(|outcome| (*outcome.id(), outcome.block_hash)); + for outcome in outcomes { + update.insert_ser( + DBCol::TransactionResultForBlock, + &get_outcome_id_block_hash(outcome.id(), &outcome.block_hash), + &ExecutionOutcomeWithProof { + proof: outcome.proof, + outcome: outcome.outcome_with_id.outcome, + }, + )?; + } + } + update.finish()?; + let mut delete_old_update = store.store_update(); + delete_old_update.delete_all(DBCol::_TransactionResult); + delete_old_update.commit()?; + Ok(()) +} + +/// Migrates the database from version 33 to 34. +/// +/// Most importantly, this involves adding KIND entry to DbVersion column, +/// removing IS_ARCHIVAL from BlockMisc column. Furthermore, migration deletes +/// GCCount column which is no longer used. +/// +/// If the database has IS_ARCHIVAL key in BlockMisc column set to true, this +/// overrides value of is_node_archival argument. Otherwise, the kind of the +/// resulting database is determined based on that argument. +pub fn migrate_33_to_34(store: &Store, mut is_node_archival: bool) -> anyhow::Result<()> { + const IS_ARCHIVE_KEY: &[u8; 10] = b"IS_ARCHIVE"; + + let is_store_archival = + store.get_ser::(DBCol::BlockMisc, IS_ARCHIVE_KEY)?.unwrap_or_default(); + + if is_store_archival != is_node_archival { + if is_store_archival { + tracing::info!(target: "migrations", "Opening an archival database."); + tracing::warn!(target: "migrations", "Ignoring `archive` client configuration and setting database kind to Archive."); + } else { + tracing::info!(target: "migrations", "Running node in archival mode (as per `archive` client configuration)."); + tracing::info!(target: "migrations", "Setting database kind to Archive."); + tracing::warn!(target: "migrations", "Starting node in non-archival mode will no longer be possible with this database."); + } + is_node_archival = true; + } + + let mut update = store.store_update(); + if is_store_archival { + update.delete(DBCol::BlockMisc, IS_ARCHIVE_KEY); + } + let kind = if is_node_archival { DbKind::Archive } else { DbKind::RPC }; + update.set(DBCol::DbVersion, crate::metadata::KIND_KEY, <&str>::from(kind).as_bytes()); + update.delete_all(DBCol::_GCCount); + update.commit()?; + Ok(()) +} + +/// Migrates the database from version 34 to 35. +/// +/// This involves deleting contents of Peers column which is now +/// deprecated and no longer used. +pub fn migrate_34_to_35(store: &Store) -> anyhow::Result<()> { + let mut update = store.store_update(); + update.delete_all(DBCol::_Peers); + update.commit()?; + Ok(()) +} + +/// Migrates the database from version 36 to 37. +/// +/// This involves rewriting all FlatStateChanges entries in the new format. +/// The size of that column should not exceed several dozens of entries. +pub fn migrate_36_to_37(store: &Store) -> anyhow::Result<()> { + #[derive(borsh::BorshDeserialize)] + struct LegacyFlatStateChanges(HashMap, Option>); + + let mut update = store.store_update(); + update.delete_all(DBCol::FlatStateChanges); + for result in store.iter(DBCol::FlatStateChanges) { + let (key, old_value) = result?; + let new_value = borsh::to_vec(&crate::flat::FlatStateChanges( + LegacyFlatStateChanges::try_from_slice(&old_value)? + .0 + .into_iter() + .map(|(key, value_ref)| (key, value_ref.map(|v| FlatStateValue::Ref(v)))) + .collect(), + ))?; + update.set(DBCol::FlatStateChanges, &key, &new_value); + } + update.commit()?; + Ok(()) +} + +/// Migrates the database from version 37 to 38. +/// +/// Rewrites FlatStateDeltaMetadata to add a bit to Metadata, `prev_block_with_changes`. +/// That bit is initialized with a `None` regardless of the corresponding flat state changes. +pub fn migrate_37_to_38(store: &Store) -> anyhow::Result<()> { + #[derive(borsh::BorshDeserialize)] + struct LegacyFlatStateDeltaMetadata { + block: crate::flat::BlockInfo, + } + + let mut update = store.store_update(); + update.delete_all(DBCol::FlatStateDeltaMetadata); + for result in store.iter(DBCol::FlatStateDeltaMetadata) { + let (key, old_value) = result?; + let LegacyFlatStateDeltaMetadata { block } = + LegacyFlatStateDeltaMetadata::try_from_slice(&old_value)?; + let new_value = + crate::flat::FlatStateDeltaMetadata { block, prev_block_with_changes: None }; + update.set(DBCol::FlatStateDeltaMetadata, &key, &borsh::to_vec(&new_value)?); + } + update.commit()?; + Ok(()) +} diff --git a/core/store/src/opener.rs b/core/store/src/opener.rs new file mode 100644 index 000000000..bc0d543f5 --- /dev/null +++ b/core/store/src/opener.rs @@ -0,0 +1,702 @@ +use crate::db::rocksdb::snapshot::{Snapshot, SnapshotError, SnapshotRemoveError}; +use crate::db::rocksdb::RocksDB; +use crate::metadata::{DbKind, DbMetadata, DbVersion, DB_VERSION}; +use crate::{DBCol, DBTransaction, Mode, NodeStorage, Store, StoreConfig, Temperature}; +use std::sync::Arc; +use strum::IntoEnumIterator; + +#[derive(Debug, thiserror::Error)] +pub enum StoreOpenerError { + /// I/O or RocksDB-level error while opening or accessing the database. + #[error("{0}")] + IO(#[from] std::io::Error), + + /// Database does not exist. + /// + /// This may happen when opening in ReadOnly or in ReadWriteExisting mode. + #[error("Database does not exist")] + DbDoesNotExist, + + /// Database already exists but requested creation of a new one. + /// + /// This may happen when opening in Create mode. + #[error("Database already exists")] + DbAlreadyExists, + + /// Hot database exists but cold doesn’t or the other way around. + #[error("Hot and cold databases must either both exist or not")] + HotColdExistenceMismatch, + + /// Hot and cold databases have different versions. + #[error( + "Hot database version ({hot_version}) doesn’t match \ + cold databases version ({cold_version})" + )] + HotColdVersionMismatch { hot_version: DbVersion, cold_version: DbVersion }, + + /// Database has incorrect kind. + /// + /// Specifically, this happens if node is running with a single database and + /// its kind is not RPC or Archive; or it’s running with two databases and + /// their types aren’t Hot and Cold respectively. + #[error("{which} database kind should be {want} but got {got:?}. Did you forget to set archive on your store opener?")] + DbKindMismatch { which: &'static str, got: Option, want: DbKind }, + + /// Unable to create a migration snapshot because one already exists. + #[error( + "Migration snapshot already exists at {0}; \ + unable to start new migration" + )] + SnapshotAlreadyExists(std::path::PathBuf), + + /// Creating the snapshot has failed. + #[error("Error creating migration snapshot: {0}")] + SnapshotError(std::io::Error), + + /// Deleting the snapshot after successful migration has failed. + #[error("Error cleaning up migration snapshot: {error}")] + SnapshotRemoveError { + path: std::path::PathBuf, + #[source] + error: std::io::Error, + }, + + /// The database was opened for reading but it’s version wasn’t what we + /// expect. + /// + /// This is an error because if the database is opened for reading we cannot + /// perform database migrations. + #[error( + "Database version {got} incompatible with expected {want}; \ + open in read-write mode (to run a migration) or use older uncd" + )] + DbVersionMismatchOnRead { got: DbVersion, want: DbVersion }, + + /// The database version isn’t what was expected and no migrator was + /// configured. + #[error( + "Database version {got} incompatible with expected {want}; \ + run node to perform migration or use older uncd" + )] + DbVersionMismatch { got: DbVersion, want: DbVersion }, + + /// The database version is missing. + #[error("The database version is missing.")] + DbVersionMissing {}, + + /// Database has version which is no longer supported. + /// + /// `latest_release` gives latest uncd release which still supports that + /// database version. + #[error( + "Database version {got} incompatible with expected {want}; \ + use uncd {latest_release} to perform database migration" + )] + DbVersionTooOld { got: DbVersion, want: DbVersion, latest_release: &'static str }, + + /// Database has version newer than what we support. + #[error( + "Database version {got} incompatible with expected {want}; \ + update uncd release" + )] + DbVersionTooNew { got: DbVersion, want: DbVersion }, + + /// Error while performing migration. + #[error("{0}")] + MigrationError(#[source] anyhow::Error), + + /// Checkpointing errors. + #[error("{0}")] + CheckpointError(#[source] anyhow::Error), +} + +impl From for StoreOpenerError { + fn from(err: SnapshotError) -> Self { + match err { + SnapshotError::AlreadyExists(snap_path) => Self::SnapshotAlreadyExists(snap_path), + SnapshotError::IOError(err) => Self::SnapshotError(err), + } + } +} + +impl From for StoreOpenerError { + fn from(err: SnapshotRemoveError) -> Self { + Self::SnapshotRemoveError { path: err.path, error: err.error } + } +} + +fn get_default_kind(archive: bool, temp: Temperature) -> DbKind { + match (temp, archive) { + (Temperature::Hot, false) => DbKind::RPC, + (Temperature::Hot, true) => DbKind::Archive, + (Temperature::Cold, _) => DbKind::Cold, + } +} + +fn is_valid_kind_temp(kind: DbKind, temp: Temperature) -> bool { + match (kind, temp) { + (DbKind::Cold, Temperature::Cold) => true, + (DbKind::RPC, Temperature::Hot) => true, + (DbKind::Hot, Temperature::Hot) => true, + (DbKind::Archive, Temperature::Hot) => true, + _ => false, + } +} + +fn is_valid_kind_archive(kind: DbKind, archive: bool) -> bool { + match (kind, archive) { + (DbKind::Archive, true) => true, + (DbKind::Cold, true) => true, + (DbKind::Hot, true) => true, + (DbKind::RPC, _) => true, + _ => false, + } +} + +/// Builder for opening node’s storage. +/// +/// Typical usage: +/// +/// ```ignore +/// let store = NodeStorage::opener(&unc_config.config.store) +/// .home(neard_home_dir) +/// .open(); +/// ``` +pub struct StoreOpener<'a> { + /// Opener for an instance of RPC or Hot RocksDB store. + hot: DBOpener<'a>, + + /// Opener for an instance of Cold RocksDB store if one was configured. + cold: Option>, + + /// Whether the opener should expect archival db or not. + archive: bool, + + /// A migrator which performs database migration if the database has old + /// version. + migrator: Option<&'a dyn StoreMigrator>, +} + +/// Opener for a single RocksDB instance. +struct DBOpener<'a> { + /// Path to the database. + /// + /// This is resolved from framework home directory and store configuration + /// passed to [`crate::NodeStorage::opener`]. + path: std::path::PathBuf, + + /// Configuration as provided by the user. + config: &'a StoreConfig, + + /// Temperature of the database. + /// + /// This affects whether refcount merge operator is configured on reference + /// counted column. It’s important that the value is correct. RPC and + /// Archive databases are considered hot. + temp: Temperature, +} + +impl<'a> StoreOpener<'a> { + /// Initialises a new opener with given home directory and store config. + pub(crate) fn new( + home_dir: &std::path::Path, + archive: bool, + config: &'a StoreConfig, + cold_config: Option<&'a StoreConfig>, + ) -> Self { + Self { + hot: DBOpener::new(home_dir, config, Temperature::Hot), + cold: cold_config.map(|config| DBOpener::new(home_dir, config, Temperature::Cold)), + archive: archive, + migrator: None, + } + } + + /// Configures the opener with specified [`StoreMigrator`]. + /// + /// If the migrator is not configured, the opener will fail to open + /// databases with older versions. With migrator configured, it will + /// attempt to perform migrations. + pub fn with_migrator(mut self, migrator: &'a dyn StoreMigrator) -> Self { + self.migrator = Some(migrator); + self + } + + /// Returns path to the underlying RocksDB database. + /// + /// Does not check whether the database actually exists. + pub fn path(&self) -> &std::path::Path { + &self.hot.path + } + + #[cfg(test)] + pub(crate) fn config(&self) -> &StoreConfig { + self.hot.config + } + + /// Opens the storage in read-write mode. + /// + /// Creates the database if missing. + pub fn open(&self) -> Result { + self.open_in_mode(Mode::ReadWrite) + } + + /// Opens the RocksDB database(s) for hot and cold (if configured) storages. + /// + /// When opening in read-only mode, verifies that the database version is + /// what the node expects and fails if it isn’t. If database doesn’t exist, + /// creates a new one unless mode is [`Mode::ReadWriteExisting`]. On the + /// other hand, if mode is [`Mode::Create`], fails if the database already + /// exists. + pub fn open_in_mode(&self, mode: Mode) -> Result { + { + let hot_path = self.hot.path.display().to_string(); + let cold_path = match &self.cold { + Some(cold) => cold.path.display().to_string(), + None => String::from("none"), + }; + tracing::info!(target: "db_opener", path=hot_path, cold_path=cold_path, "Opening NodeStorage"); + } + + let hot_snapshot = { + Self::ensure_created(mode, &self.hot)?; + Self::ensure_kind(mode, &self.hot, self.archive, Temperature::Hot)?; + Self::ensure_version(mode, &self.hot, &self.migrator)? + }; + + let cold_snapshot = if let Some(cold) = &self.cold { + Self::ensure_created(mode, cold)?; + Self::ensure_kind(mode, cold, self.archive, Temperature::Cold)?; + Self::ensure_version(mode, cold, &self.migrator)? + } else { + Snapshot::none() + }; + + let (hot_db, _) = self.hot.open(mode, DB_VERSION)?; + let cold_db = self + .cold + .as_ref() + .map(|cold| cold.open(mode, DB_VERSION)) + .transpose()? + .map(|(db, _)| db); + + let storage = NodeStorage::from_rocksdb(hot_db, cold_db); + + hot_snapshot.remove()?; + cold_snapshot.remove()?; + + Ok(storage) + } + + pub fn create_snapshots(&self, mode: Mode) -> Result<(Snapshot, Snapshot), StoreOpenerError> { + { + let hot_path = self.hot.path.display().to_string(); + let cold_path = match &self.cold { + Some(cold) => cold.path.display().to_string(), + None => String::from("none"), + }; + tracing::info!(target: "db_opener", path=hot_path, cold_path=cold_path, "Creating NodeStorage snapshots"); + } + + let hot_snapshot = { + Self::ensure_created(mode, &self.hot)?; + Self::ensure_kind(mode, &self.hot, self.archive, Temperature::Hot)?; + let snapshot = Self::ensure_version(mode, &self.hot, &self.migrator)?; + if snapshot.0.is_none() { + self.hot.snapshot()? + } else { + snapshot + } + }; + + let cold_snapshot = if let Some(cold) = &self.cold { + Self::ensure_created(mode, cold)?; + Self::ensure_kind(mode, cold, self.archive, Temperature::Cold)?; + let snapshot = Self::ensure_version(mode, cold, &self.migrator)?; + if snapshot.0.is_none() { + cold.snapshot()? + } else { + snapshot + } + } else { + Snapshot::none() + }; + + Ok((hot_snapshot, cold_snapshot)) + } + + // Creates the DB if it doesn't exist. + fn ensure_created(mode: Mode, opener: &DBOpener) -> Result<(), StoreOpenerError> { + let meta = opener.get_metadata()?; + match meta { + Some(_) if !mode.must_create() => { + tracing::info!(target: "db_opener", path=%opener.path.display(), "The database exists."); + return Ok(()); + } + Some(_) => { + return Err(StoreOpenerError::DbAlreadyExists); + } + None if mode.can_create() => { + tracing::info!(target: "db_opener", path=%opener.path.display(), "The database doesn't exist, creating it."); + + let db = opener.create()?; + let store = Store { storage: Arc::new(db) }; + store.set_db_version(DB_VERSION)?; + return Ok(()); + } + None => { + return Err(StoreOpenerError::DbDoesNotExist); + } + } + } + + /// Ensures that the db has correct kind. If the db doesn't have kind + /// it sets it, if the mode allows, or returns an error. + fn ensure_kind( + mode: Mode, + opener: &DBOpener, + archive: bool, + temp: Temperature, + ) -> Result<(), StoreOpenerError> { + let which: &'static str = temp.into(); + tracing::debug!(target: "db_opener", path = %opener.path.display(), archive, which, "Ensure db kind is correct and set."); + let store = Self::open_store_unsafe(mode, opener)?; + + let current_kind = store.get_db_kind()?; + let default_kind = get_default_kind(archive, temp); + let err = + Err(StoreOpenerError::DbKindMismatch { which, got: current_kind, want: default_kind }); + + // If kind is set check if it's the expected one. + if let Some(current_kind) = current_kind { + if !is_valid_kind_temp(current_kind, temp) { + return err; + } + if !is_valid_kind_archive(current_kind, archive) { + return err; + } + return Ok(()); + } + + // Kind is not set, set it. + if mode.read_write() { + tracing::info!(target: "db_opener", archive, which, "Setting the db DbKind to {default_kind:#?}"); + + store.set_db_kind(default_kind)?; + return Ok(()); + } + + return err; + } + + /// Ensures that the db has the correct - most recent - version. If the + /// version is lower, it performs migrations up until the most recent + /// version, if mode allows or returns an error. + fn ensure_version( + mode: Mode, + opener: &DBOpener, + migrator: &Option<&dyn StoreMigrator>, + ) -> Result { + tracing::debug!(target: "db_opener", path=%opener.path.display(), "Ensure db version"); + + let metadata = opener.get_metadata()?; + let metadata = metadata.ok_or(StoreOpenerError::DbDoesNotExist {})?; + let DbMetadata { version, .. } = metadata; + + if version == DB_VERSION { + return Ok(Snapshot::none()); + } + if version > DB_VERSION { + return Err(StoreOpenerError::DbVersionTooNew { got: version, want: DB_VERSION }); + } + + // If we’re opening for reading, we cannot perform migrations thus we + // must fail if the database has old version (even if we support + // migration from that version). + if mode.read_only() { + return Err(StoreOpenerError::DbVersionMismatchOnRead { + got: version, + want: DB_VERSION, + }); + } + + // Figure out if we have migrator which supports the database version. + let migrator = migrator + .ok_or(StoreOpenerError::DbVersionMismatch { got: version, want: DB_VERSION })?; + if let Err(release) = migrator.check_support(version) { + return Err(StoreOpenerError::DbVersionTooOld { + got: version, + want: DB_VERSION, + latest_release: release, + }); + } + + let snapshot = opener.snapshot()?; + + for version in version..DB_VERSION { + tracing::info!(target: "db_opener", path=%opener.path.display(), + "Migrating the database from version {} to {}", + version, version + 1); + + // Note: here we open the cold store as a regular Store object + // backed by RocksDB. It doesn't matter today as we don't expect any + // old migrations on the cold storage. In the future however it may + // be better to wrap it in the ColdDB object instead. + + let store = Self::open_store(mode, opener, version)?; + migrator.migrate(&store, version).map_err(StoreOpenerError::MigrationError)?; + store.set_db_version(version + 1)?; + } + + if cfg!(feature = "nightly") || cfg!(feature = "nightly_protocol") { + let version = 10000; + tracing::info!(target: "db_opener", path=%opener.path.display(), + "Setting the database version to {version} for nightly"); + + // Set some dummy value to avoid conflict with other migrations from + // nightly features. + let store = Self::open_store(mode, opener, DB_VERSION)?; + store.set_db_version(version)?; + } + + Ok(snapshot) + } + + fn open_store( + mode: Mode, + opener: &DBOpener, + version: DbVersion, + ) -> Result { + let (db, _) = opener.open(mode, version)?; + let store = Store { storage: Arc::new(db) }; + Ok(store) + } + + fn open_store_unsafe(mode: Mode, opener: &DBOpener) -> Result { + let db = opener.open_unsafe(mode)?; + let store = Store { storage: Arc::new(db) }; + Ok(store) + } +} + +impl<'a> DBOpener<'a> { + /// Constructs new opener for a single RocksDB builder. + /// + /// The path to the database is resolved based on the path in config with + /// given home_dir as base directory for resolving relative paths. + fn new(home_dir: &std::path::Path, config: &'a StoreConfig, temp: Temperature) -> Self { + let path = if temp == Temperature::Hot { "data" } else { "cold-data" }; + let path = config.path.as_deref().unwrap_or(std::path::Path::new(path)); + let path = home_dir.join(path); + Self { path, config, temp } + } + + /// Returns version and kind of the database or `None` if it doesn’t exist. + /// + /// If the database exists but doesn’t have version set, returns an error. + /// Similarly if the version key is set but to value which cannot be parsed. + /// + /// For database versions older than the point at which database kind was + /// introduced, the kind is returned as `None`. Otherwise, it’s also + /// fetched and if it’s not there error is returned. + fn get_metadata(&self) -> std::io::Result> { + RocksDB::get_metadata(&self.path, self.config) + } + + /// Opens the database in given mode checking expected version and kind. + /// + /// Fails if the database doesn’t have version given in `want_version` + /// argument. Note that the verification is meant as sanity checks. + /// Verification failure either indicates an internal logic error (since + /// caller is expected to know the version) or some strange file system + /// manipulations. + /// + /// The proper usage of this method is to first get the metadata of the + /// database and then open it knowing expected version and kind. Getting + /// the metadata is a safe operation which doesn’t modify the database. + /// This convoluted (one might argue) process is therefore designed to avoid + /// modifying the database if we’re opening something with a too old or too + /// new version. + /// + /// Use [`Self::create`] to create a new database. + fn open(&self, mode: Mode, want_version: DbVersion) -> std::io::Result<(RocksDB, DbMetadata)> { + let db = RocksDB::open(&self.path, &self.config, mode, self.temp)?; + let metadata = DbMetadata::read(&db)?; + if want_version != metadata.version { + let msg = format!("unexpected DbVersion {}; expected {want_version}", metadata.version); + Err(std::io::Error::other(msg)) + } else { + Ok((db, metadata)) + } + } + + /// Opens the database in given mode without checking the expected version and kind. + /// + /// This is only suitable when creating the database or setting the version + /// and kind for the first time. + fn open_unsafe(&self, mode: Mode) -> std::io::Result { + let db = RocksDB::open(&self.path, &self.config, mode, self.temp)?; + Ok(db) + } + + /// Creates a new database. + fn create(&self) -> std::io::Result { + RocksDB::open(&self.path, &self.config, Mode::Create, self.temp) + } + + /// Creates a new snapshot for the database. + fn snapshot(&self) -> Result { + Snapshot::new(&self.path, &self.config, self.temp) + } +} + +pub trait StoreMigrator { + /// Checks whether migrator supports database versions starting at given. + /// + /// If the `version` is too old and the migrator no longer supports it, + /// returns `Err` with the latest uncd release which supported that + /// version. Otherwise returns `Ok(())` indicating that the migrator + /// supports migrating the database from the given version up to the current + /// version [`DB_VERSION`]. + /// + /// **Panics** if `version` ≥ [`DB_VERSION`]. + fn check_support(&self, version: DbVersion) -> Result<(), &'static str>; + + /// Performs database migration from given version to the next one. + /// + /// The function only does single migration from `version` to `version + 1`. + /// It doesn’t update database’s metadata (i.e. what version is stored in + /// the database) which is responsibility of the caller. + /// + /// **Panics** if `version` is not supported (the caller is supposed to + /// check support via [`Self::check_support`] method) or if it’s greater or + /// equal to [`DB_VERSION`]. + fn migrate(&self, store: &Store, version: DbVersion) -> anyhow::Result<()>; +} + +/// Creates checkpoint of hot storage in `home_dir.join(checkpoint_relative_path)` +/// +/// If `columns_to_keep` is None doesn't cleanup columns. +/// Otherwise deletes all columns that are not in `columns_to_keep`. +/// +/// `store` must be the hot DB. +/// +/// Returns NodeStorage of checkpoint db. +/// `archive` -- is hot storage archival (needed to open checkpoint). +pub fn checkpoint_hot_storage_and_cleanup_columns( + hot_store: &Store, + checkpoint_base_path: &std::path::Path, + columns_to_keep: Option>, +) -> Result { + let _span = + tracing::info_span!(target: "state_snapshot", "checkpoint_hot_storage_and_cleanup_columns") + .entered(); + let checkpoint_path = checkpoint_base_path.join("data"); + std::fs::create_dir_all(&checkpoint_base_path)?; + + hot_store + .storage + .create_checkpoint(&checkpoint_path) + .map_err(StoreOpenerError::CheckpointError)?; + + // As only path from config is used in StoreOpener, default config with custom path will do. + let mut config = StoreConfig::default(); + config.path = Some(checkpoint_path); + let archive = hot_store.get_db_kind()? == Some(DbKind::Archive); + let opener = StoreOpener::new(checkpoint_base_path, archive, &config, None); + let node_storage = opener.open_in_mode(Mode::ReadWriteExisting)?; + + if let Some(columns_to_keep) = columns_to_keep { + let columns_to_keep_set: std::collections::HashSet = + std::collections::HashSet::from_iter(columns_to_keep.into_iter()); + let mut transaction = DBTransaction::new(); + // Force the checkpoint to be a Hot DB kind to simplify opening the snapshots later. + transaction.set( + DBCol::DbVersion, + crate::metadata::KIND_KEY.to_vec(), + <&str>::from(DbKind::RPC).as_bytes().to_vec(), + ); + + for col in DBCol::iter() { + if !columns_to_keep_set.contains(&col) { + transaction.delete_all(col); + } + } + + tracing::debug!(target: "state_snapshot", ?transaction, "Transaction ready"); + node_storage.hot_storage.write(transaction)?; + tracing::debug!(target: "state_snapshot", "Transaction written"); + } + + Ok(node_storage) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::path::PathBuf; + + fn check_keys_existence(store: &Store, column: &DBCol, keys: &Vec>, expected: bool) { + for key in keys { + assert_eq!(store.exists(*column, &key).unwrap(), expected, "Column {:?}", column); + } + } + + #[test] + fn test_checkpoint_hot_storage_and_cleanup_columns() { + let (home_dir, opener) = NodeStorage::test_opener(); + let node_storage = opener.open().unwrap(); + let hot_store = Store { storage: node_storage.hot_storage.clone() }; + + let keys = vec![vec![0], vec![1], vec![2], vec![3]]; + let columns = vec![DBCol::Block, DBCol::Chunks, DBCol::BlockHeader]; + + let mut store_update = node_storage.get_hot_store().store_update(); + for column in columns { + for key in &keys { + store_update.insert(column, key.clone(), vec![42]); + } + } + store_update.commit().unwrap(); + + let store = checkpoint_hot_storage_and_cleanup_columns( + &hot_store, + &home_dir.path().join(PathBuf::from("checkpoint_none")), + None, + ) + .unwrap(); + check_keys_existence(&store.get_hot_store(), &DBCol::Block, &keys, true); + check_keys_existence(&store.get_hot_store(), &DBCol::Chunks, &keys, true); + check_keys_existence(&store.get_hot_store(), &DBCol::BlockHeader, &keys, true); + + let store = checkpoint_hot_storage_and_cleanup_columns( + &hot_store, + &home_dir.path().join(PathBuf::from("checkpoint_some")), + Some(vec![DBCol::Block]), + ) + .unwrap(); + check_keys_existence(&store.get_hot_store(), &DBCol::Block, &keys, true); + check_keys_existence(&store.get_hot_store(), &DBCol::Chunks, &keys, false); + check_keys_existence(&store.get_hot_store(), &DBCol::BlockHeader, &keys, false); + + let store = checkpoint_hot_storage_and_cleanup_columns( + &hot_store, + &home_dir.path().join(PathBuf::from("checkpoint_all")), + Some(vec![DBCol::Block, DBCol::Chunks, DBCol::BlockHeader]), + ) + .unwrap(); + check_keys_existence(&store.get_hot_store(), &DBCol::Block, &keys, true); + check_keys_existence(&store.get_hot_store(), &DBCol::Chunks, &keys, true); + check_keys_existence(&store.get_hot_store(), &DBCol::BlockHeader, &keys, true); + + let store = checkpoint_hot_storage_and_cleanup_columns( + &hot_store, + &home_dir.path().join(PathBuf::from("checkpoint_empty")), + Some(vec![]), + ) + .unwrap(); + check_keys_existence(&store.get_hot_store(), &DBCol::Block, &keys, false); + check_keys_existence(&store.get_hot_store(), &DBCol::Chunks, &keys, false); + check_keys_existence(&store.get_hot_store(), &DBCol::BlockHeader, &keys, false); + } +} diff --git a/core/store/src/rocksdb_metrics.rs b/core/store/src/rocksdb_metrics.rs new file mode 100644 index 000000000..52fd4f1e0 --- /dev/null +++ b/core/store/src/rocksdb_metrics.rs @@ -0,0 +1,175 @@ +use crate::db::{StatsValue, StoreStatistics}; +use crate::Temperature; +use unc_o11y::metrics::{ + try_create_gauge_vec, try_create_int_gauge, try_create_int_gauge_vec, GaugeVec, IntGauge, + IntGaugeVec, +}; +use once_cell::sync::Lazy; +use std::collections::hash_map::Entry; +use std::collections::HashMap; +use std::sync::Mutex; +use tracing::warn; + +pub fn export_stats_as_metrics(stats: StoreStatistics, temperature: Temperature) { + match ROCKSDB_METRICS.lock().unwrap().export_stats_as_metrics(stats, temperature) { + Ok(_) => {} + Err(err) => { + warn!(target:"stats", "Failed to export {:?} store statistics: {:?}", temperature, err); + } + } +} + +/// Returns a copy of the int gauges. This method is needed because the gauges +/// are created on demand and owned by the RockDBMetrics. +/// +/// Only for unit test usage. +#[cfg(test)] +pub fn get_int_gauges() -> HashMap { + ROCKSDB_METRICS.lock().unwrap().int_gauges.clone() +} + +/// Wrapper for re-exporting RocksDB stats into Prometheus metrics. +static ROCKSDB_METRICS: Lazy> = + Lazy::new(|| Mutex::new(RocksDBMetrics::default())); + +#[derive(Default, Debug)] +/// Creates prometheus metrics on-demand for exporting RocksDB statistics. +pub(crate) struct RocksDBMetrics { + // Contains counters and sums, which are integer statistics in RocksDB. + int_gauges: HashMap, + // Contains integer statistics with labels. + int_vec_gauges: HashMap, + // Contains floating point statistics, such as quantiles of timings. + gauges: HashMap, +} + +impl RocksDBMetrics { + pub fn export_stats_as_metrics( + &mut self, + stats: StoreStatistics, + temperature: Temperature, + ) -> Result<(), Box> { + for (stat_name, values) in stats.data { + let stat_name = stat_name + get_temperature_str(&temperature); + if values.is_empty() { + continue; + } + if values.len() == 1 { + // A counter stats. + // A statistic 'a.b.c' creates the following prometheus metric: + // - unc_a_b_c + if let StatsValue::Count(value) = values[0] { + self.set_int_value( + |stat_name: &str| stat_name.to_string(), + |stat_name| get_prometheus_metric_name(stat_name), + &stat_name, + value, + )?; + continue; + } + } + for stats_value in values { + match stats_value { + StatsValue::Count(value) => { + self.set_int_value( + get_stats_summary_count_key, + get_metric_name_summary_count_gauge, + &stat_name, + value, + )?; + } + StatsValue::Sum(value) => { + self.set_int_value( + get_stats_summary_sum_key, + get_metric_name_summary_sum_gauge, + &stat_name, + value, + )?; + } + StatsValue::Percentile(percentile, value) => { + let key = &stat_name; + + let gauge = match self.gauges.entry(key.to_string()) { + Entry::Vacant(entry) => entry.insert(try_create_gauge_vec( + &get_prometheus_metric_name(&stat_name), + &stat_name, + &["quantile"], + )?), + Entry::Occupied(entry) => entry.into_mut(), + }; + gauge + .with_label_values(&[&format!("{:.2}", percentile as f64 * 0.01)]) + .set(value); + } + // Adding rocksdb property as labeled integer gauge metric. + // Label = column's verbose name. + StatsValue::ColumnValue(col, value) => { + let key = &stat_name; + + // Checking for metric to be present. + let gauge = match self.int_vec_gauges.entry(key.to_string()) { + // If not -> creating it. + Entry::Vacant(entry) => entry.insert(try_create_int_gauge_vec( + &get_prometheus_metric_name(&stat_name), + &stat_name, + &["col"], + )?), + Entry::Occupied(entry) => entry.into_mut(), + }; + // Writing value for column. + gauge.with_label_values(&[<&str>::from(col)]).set(value); + } + } + } + } + Ok(()) + } + + /// `stat_name` is the name of the statistics at the storage level. + /// `metric_fn` returns a name of the prometheus metric that re-exports that statistic. + /// `key_fn` returns a hashmap key for the hashmaps in the ROCKSDB_METRICS singleton. + fn set_int_value( + &mut self, + key_fn: fn(&str) -> String, + metric_fn: fn(&str) -> String, + stat_name: &str, + value: i64, + ) -> Result<(), Box> { + let key = key_fn(stat_name); + let gauge = match self.int_gauges.entry(key) { + Entry::Vacant(entry) => { + entry.insert(try_create_int_gauge(&metric_fn(stat_name), stat_name)?) + } + Entry::Occupied(entry) => entry.into_mut(), + }; + gauge.set(value); + Ok(()) + } +} + +fn get_temperature_str(temperature: &Temperature) -> &'static str { + match temperature { + Temperature::Hot => "", + Temperature::Cold => "_cold", + } +} + +fn get_prometheus_metric_name(stat_name: &str) -> String { + format!("unc_{}", stat_name.replace(&['.', '-'], "_")) +} + +fn get_metric_name_summary_count_gauge(stat_name: &str) -> String { + format!("unc_{}_count", stat_name.replace('.', "_")) +} + +fn get_metric_name_summary_sum_gauge(stat_name: &str) -> String { + format!("unc_{}_sum", stat_name.replace('.', "_")) +} + +fn get_stats_summary_count_key(stat_name: &str) -> String { + format!("{}.count", stat_name) +} + +fn get_stats_summary_sum_key(stat_name: &str) -> String { + format!("{}.sum", stat_name) +} diff --git a/core/store/src/sync_utils.rs b/core/store/src/sync_utils.rs new file mode 100644 index 000000000..c2abfccb9 --- /dev/null +++ b/core/store/src/sync_utils.rs @@ -0,0 +1,72 @@ +use std::ops::{Deref, DerefMut}; +use std::sync::{Condvar, Mutex, MutexGuard}; + +const POISONED_LOCK_ERR: &str = "The lock was poisoned."; + +/// A convenience wrapper around a Mutex and a Condvar. +/// +/// It enables blocking while waiting for the underlying value to be updated. +/// The implementation ensures that any modification results in all blocked +/// threads being notified. +pub(crate) struct Monitor { + cvar: Condvar, + mutex: Mutex, +} + +pub(crate) struct MonitorReadGuard<'a, T> { + guard: MutexGuard<'a, T>, +} + +pub(crate) struct MonitorWriteGuard<'a, T> { + guard: MutexGuard<'a, T>, + cvar: &'a Condvar, +} + +impl Monitor { + pub fn new(t: T) -> Self { + Self { mutex: Mutex::new(t), cvar: Condvar::new() } + } + + pub fn lock(&self) -> MonitorReadGuard<'_, T> { + let guard = self.mutex.lock().expect(POISONED_LOCK_ERR); + MonitorReadGuard { guard } + } + + pub fn lock_mut(&self) -> MonitorWriteGuard<'_, T> { + let guard = self.mutex.lock().expect(POISONED_LOCK_ERR); + MonitorWriteGuard { guard, cvar: &self.cvar } + } + + pub fn wait<'a>(&'a self, guard: MonitorReadGuard<'a, T>) -> MonitorReadGuard<'a, T> { + let guard = self.cvar.wait(guard.guard).expect(POISONED_LOCK_ERR); + MonitorReadGuard { guard } + } +} + +impl Deref for MonitorReadGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.guard.deref() + } +} + +impl Deref for MonitorWriteGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.guard.deref() + } +} + +impl DerefMut for MonitorWriteGuard<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.guard.deref_mut() + } +} + +impl Drop for MonitorWriteGuard<'_, T> { + fn drop(&mut self) { + self.cvar.notify_all(); + } +} diff --git a/core/store/src/test_utils.rs b/core/store/src/test_utils.rs new file mode 100644 index 000000000..61472ad85 --- /dev/null +++ b/core/store/src/test_utils.rs @@ -0,0 +1,345 @@ +use crate::db::TestDB; +use crate::flat::{ + store_helper, BlockInfo, FlatStorageManager, FlatStorageReadyStatus, FlatStorageStatus, +}; +use crate::metadata::{DbKind, DbVersion, DB_VERSION}; +use crate::{ + get, get_delayed_receipt_indices, DBCol, NodeStorage, ShardTries, StateSnapshotConfig, Store, + TrieConfig, +}; +use itertools::Itertools; +use unc_primitives::account::id::AccountId; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::{DataReceipt, Receipt, ReceiptEnum}; +use unc_primitives::shard_layout::{ShardUId, ShardVersion}; +use unc_primitives::state::FlatStateValue; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::{NumShards, StateRoot}; +use rand::seq::SliceRandom; +use rand::Rng; +use std::collections::HashMap; +use std::str::from_utf8; +use std::sync::Arc; + +/// Creates an in-memory node storage. +/// +/// In tests you’ll often want to use [`create_test_store`] instead. +pub fn create_test_node_storage(version: DbVersion, hot_kind: DbKind) -> NodeStorage { + let storage = NodeStorage::new(TestDB::new()); + + storage.get_hot_store().set_db_version(version).unwrap(); + storage.get_hot_store().set_db_kind(hot_kind).unwrap(); + + storage +} + +/// Creates an in-memory node storage. +/// +/// In tests you’ll often want to use [`create_test_store`] instead. +/// It initializes the db version and db kind to sensible defaults - +/// the current version and rpc kind. +pub fn create_test_node_storage_default() -> NodeStorage { + create_test_node_storage(DB_VERSION, DbKind::RPC) +} + +/// Creates an in-memory node storage with ColdDB +pub fn create_test_node_storage_with_cold( + version: DbVersion, + hot_kind: DbKind, +) -> (NodeStorage, Arc, Arc) { + let hot = TestDB::new(); + let cold = TestDB::new(); + let storage = NodeStorage::new_with_cold(hot.clone(), cold.clone()); + + storage.get_hot_store().set_db_version(version).unwrap(); + storage.get_hot_store().set_db_kind(hot_kind).unwrap(); + storage.get_cold_store().unwrap().set_db_version(version).unwrap(); + storage.get_cold_store().unwrap().set_db_kind(DbKind::Cold).unwrap(); + + (storage, hot, cold) +} + +/// Creates an in-memory database. +pub fn create_test_store() -> Store { + create_test_node_storage(DB_VERSION, DbKind::RPC).get_hot_store() +} + +pub struct TestTriesBuilder { + store: Option, + shard_version: ShardVersion, + num_shards: NumShards, + enable_flat_storage: bool, + enable_in_memory_tries: bool, +} + +impl TestTriesBuilder { + pub fn new() -> Self { + Self { + store: None, + shard_version: 0, + num_shards: 1, + enable_flat_storage: false, + enable_in_memory_tries: false, + } + } + + pub fn with_store(mut self, store: Store) -> Self { + self.store = Some(store); + self + } + + pub fn with_shard_layout(mut self, shard_version: ShardVersion, num_shards: NumShards) -> Self { + self.shard_version = shard_version; + self.num_shards = num_shards; + self + } + + pub fn with_flat_storage(mut self) -> Self { + self.enable_flat_storage = true; + self + } + + pub fn with_in_memory_tries(mut self) -> Self { + self.enable_in_memory_tries = true; + self + } + + pub fn build(self) -> ShardTries { + let store = self.store.unwrap_or_else(create_test_store); + let shard_uids = (0..self.num_shards) + .map(|shard_id| ShardUId { shard_id: shard_id as u32, version: self.shard_version }) + .collect::>(); + let flat_storage_manager = FlatStorageManager::new(store.clone()); + let tries = ShardTries::new( + store, + TrieConfig { + load_mem_tries_for_all_shards: self.enable_in_memory_tries, + ..Default::default() + }, + &shard_uids, + flat_storage_manager, + StateSnapshotConfig::default(), + ); + if self.enable_flat_storage { + let mut store_update = tries.store_update(); + for shard_id in 0..self.num_shards { + let shard_uid = ShardUId { + version: self.shard_version, + shard_id: shard_id.try_into().unwrap(), + }; + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { + flat_head: BlockInfo::genesis(CryptoHash::default(), 0), + }), + ); + } + store_update.commit().unwrap(); + + let flat_storage_manager = tries.get_flat_storage_manager(); + for shard_id in 0..self.num_shards { + let shard_uid = ShardUId { + version: self.shard_version, + shard_id: shard_id.try_into().unwrap(), + }; + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + } + } + if self.enable_in_memory_tries { + tries.load_mem_tries_for_enabled_shards(&shard_uids).unwrap(); + } + tries + } +} + +pub fn test_populate_trie( + tries: &ShardTries, + root: &CryptoHash, + shard_uid: ShardUId, + changes: Vec<(Vec, Option>)>, +) -> CryptoHash { + let trie = tries.get_trie_for_shard(shard_uid, *root); + let trie_changes = trie.update(changes.iter().cloned()).unwrap(); + let mut store_update = tries.store_update(); + let root = tries.apply_all(&trie_changes, shard_uid, &mut store_update); + store_update.commit().unwrap(); + let deduped = simplify_changes(&changes); + let trie = tries.get_trie_for_shard(shard_uid, root); + for (key, value) in deduped { + assert_eq!(trie.get(&key), Ok(value)); + } + root +} + +pub fn test_populate_flat_storage( + tries: &ShardTries, + shard_uid: ShardUId, + block_hash: &CryptoHash, + prev_block_hash: &CryptoHash, + changes: &Vec<(Vec, Option>)>, +) { + let mut store_update = tries.store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + crate::flat::FlatStorageStatus::Ready(FlatStorageReadyStatus { + flat_head: BlockInfo { hash: *block_hash, prev_hash: *prev_block_hash, height: 1 }, + }), + ); + for (key, value) in changes { + store_helper::set_flat_state_value( + &mut store_update, + shard_uid, + key.clone(), + value.as_ref().map(|value| FlatStateValue::on_disk(value)), + ); + } + store_update.commit().unwrap(); +} + +/// Insert values to non-reference-counted columns in the store. +pub fn test_populate_store(store: &Store, data: impl Iterator, Vec)>) { + let mut update = store.store_update(); + for (column, key, value) in data { + update.insert(column, key, value); + } + update.commit().expect("db commit failed"); +} + +/// Insert values to reference-counted columns in the store. +pub fn test_populate_store_rc(store: &Store, data: &[(DBCol, Vec, Vec)]) { + let mut update = store.store_update(); + for (column, key, value) in data { + update.increment_refcount(*column, key, value); + } + update.commit().expect("db commit failed"); +} + +fn gen_alphabet() -> Vec { + let alphabet = 'a'..='z'; + alphabet.map(|c| c as u8).collect_vec() +} + +fn gen_accounts_from_alphabet( + rng: &mut impl Rng, + min_size: usize, + max_size: usize, + alphabet: &[u8], +) -> Vec { + let size = rng.gen_range(min_size..=max_size); + std::iter::repeat_with(|| gen_account_from_alphabet(rng, alphabet)).take(size).collect() +} + +pub fn gen_account_from_alphabet(rng: &mut impl Rng, alphabet: &[u8]) -> AccountId { + let str_length = rng.gen_range(4..8); + let s: Vec = (0..str_length).map(|_| *alphabet.choose(rng).unwrap()).collect(); + from_utf8(&s).unwrap().parse().unwrap() +} + +pub fn gen_account(rng: &mut impl Rng) -> AccountId { + let alphabet = gen_alphabet(); + gen_account_from_alphabet(rng, &alphabet) +} + +pub fn gen_unique_accounts(rng: &mut impl Rng, min_size: usize, max_size: usize) -> Vec { + let alphabet = gen_alphabet(); + let mut accounts = gen_accounts_from_alphabet(rng, min_size, max_size, &alphabet); + accounts.sort(); + accounts.dedup(); + accounts.shuffle(rng); + accounts +} + +pub fn gen_receipts(rng: &mut impl Rng, max_size: usize) -> Vec { + let alphabet = gen_alphabet(); + let accounts = gen_accounts_from_alphabet(rng, 1, max_size, &alphabet); + accounts + .iter() + .map(|account_id| Receipt { + predecessor_id: account_id.clone(), + receiver_id: account_id.clone(), + receipt_id: CryptoHash::default(), + receipt: ReceiptEnum::Data(DataReceipt { data_id: CryptoHash::default(), data: None }), + }) + .collect() +} + +/// Generates up to max_size random sequence of changes: both insertion and deletions. +/// Deletions are represented as (key, None). +/// Keys are randomly constructed from alphabet, and they have max_length size. +fn gen_changes_helper( + rng: &mut impl Rng, + alphabet: &[u8], + max_size: usize, + max_length: u64, +) -> Vec<(Vec, Option>)> { + let mut state: HashMap, Vec> = HashMap::new(); + let mut result = Vec::new(); + let delete_probability = rng.gen_range(0.1..0.5); + let size = rng.gen_range(0..max_size) + 1; + for _ in 0..size { + let key_length = rng.gen_range(1..max_length); + let key: Vec = (0..key_length).map(|_| *alphabet.choose(rng).unwrap()).collect(); + + let delete = rng.gen_range(0.0..1.0) < delete_probability; + if delete { + let mut keys: Vec<_> = state.keys().cloned().collect(); + keys.push(key); + let key = keys.choose(rng).unwrap().clone(); + state.remove(&key); + result.push((key.clone(), None)); + } else { + let value_length = rng.gen_range(1..max_length); + let value: Vec = + (0..value_length).map(|_| *alphabet.choose(rng).unwrap()).collect(); + result.push((key.clone(), Some(value.clone()))); + state.insert(key, value); + } + } + result +} + +pub fn gen_changes(rng: &mut impl Rng, max_size: usize) -> Vec<(Vec, Option>)> { + let alphabet = gen_alphabet(); + let max_length = rng.gen_range(2..8); + gen_changes_helper(rng, &alphabet, max_size, max_length) +} + +pub fn gen_larger_changes(rng: &mut impl Rng, max_size: usize) -> Vec<(Vec, Option>)> { + let alphabet = gen_alphabet(); + let max_length = rng.gen_range(10..20); + gen_changes_helper(rng, &alphabet, max_size, max_length) +} + +pub fn simplify_changes(changes: &[(Vec, Option>)]) -> Vec<(Vec, Option>)> { + let mut state: HashMap, Vec> = HashMap::new(); + for (key, value) in changes.iter() { + if let Some(value) = value { + state.insert(key.clone(), value.clone()); + } else { + state.remove(key); + } + } + let mut result: Vec<_> = state.into_iter().map(|(k, v)| (k, Some(v))).collect(); + result.sort(); + result +} + +pub fn get_all_delayed_receipts( + tries: &ShardTries, + shard_uid: &ShardUId, + state_root: &StateRoot, +) -> Vec { + let state_update = &tries.new_trie_update(*shard_uid, *state_root); + let mut delayed_receipt_indices = get_delayed_receipt_indices(state_update).unwrap(); + + let mut receipts = vec![]; + while delayed_receipt_indices.first_index < delayed_receipt_indices.next_available_index { + let key = TrieKey::DelayedReceipt { index: delayed_receipt_indices.first_index }; + let receipt = get(state_update, &key).unwrap().unwrap(); + delayed_receipt_indices.first_index += 1; + receipts.push(receipt); + } + receipts +} diff --git a/core/store/src/trie/accounting_cache.rs b/core/store/src/trie/accounting_cache.rs new file mode 100644 index 000000000..9bcf05f65 --- /dev/null +++ b/core/store/src/trie/accounting_cache.rs @@ -0,0 +1,138 @@ +use unc_o11y::metrics::prometheus; +use unc_o11y::metrics::prometheus::core::{GenericCounter, GenericGauge}; +use unc_primitives::errors::StorageError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardUId; +use unc_vm_runner::logic::TrieNodesCount; +use std::collections::HashMap; +use std::sync::Arc; + +use crate::{metrics, TrieStorage}; + +/// Deterministic cache to store trie nodes that have been accessed so far +/// during the cache's lifetime. It is used for deterministic gas accounting +/// so that previously accessed trie nodes and values are charged at a +/// cheaper gas cost. +/// +/// This cache's correctness is critical as it contributes to the gas +/// accounting of storage operations during contract execution. For that +/// reason, a new TrieAccountingCache must be created at the beginning of a +/// chunk's execution, and the db_read_nodes and mem_read_nodes must be taken +/// into account whenever a storage operation is performed to calculate what +/// kind of operation it was. +/// +/// Note that we don't have a size limit for values in the accounting cache. +/// There are two reasons: +/// - for nodes, value size is an implementation detail. If we change +/// internal representation of a node (e.g. change `memory_usage` field +/// from `RawTrieNodeWithSize`), this would have to be a protocol upgrade. +/// - total size of all values is limited by the runtime fees. More +/// thoroughly: +/// - number of nodes is limited by receipt gas limit / touching trie +/// node fee ~= 500 Tgas / 16 Ggas = 31_250; +/// - size of trie keys and values is limited by receipt gas limit / +/// lowest per byte fee (`storage_read_value_byte`) ~= +/// (500 * 10**12 / 5611005) / 2**20 ~= 85 MB. +/// All values are given as of 16/03/2022. We may consider more precise limit +/// for the accounting cache as well. +/// +/// Note that in general, it is NOT true that all storage access is either a +/// db read or mem read. It can also be a flat storage read, which is not +/// tracked via TrieAccountingCache. +pub struct TrieAccountingCache { + /// Whether the cache is enabled. By default it is not, but it can be + /// turned on or off on the fly. + enable: bool, + /// Cache of trie node hash -> trie node body, or a leaf value hash -> + /// leaf value. + cache: HashMap>, + /// The number of times a key was accessed by reading from the underlying + /// storage. (This does not necessarily mean it was accessed from *disk*, + /// as the underlying storage layer may have a best-effort cache.) + db_read_nodes: u64, + /// The number of times a key was accessed when it was deterministically + /// already cached during the processing of this chunk. + mem_read_nodes: u64, + /// Prometheus metrics. It's optional - in testing it can be None. + metrics: Option, +} + +struct TrieAccountingCacheMetrics { + accounting_cache_hits: GenericCounter, + accounting_cache_misses: GenericCounter, + accounting_cache_size: GenericGauge, +} + +impl TrieAccountingCache { + /// Constructs a new accounting cache. By default it is not enabled. + /// The optional parameter is passed in if prometheus metrics are desired. + pub fn new(shard_uid_and_is_view: Option<(ShardUId, bool)>) -> Self { + let metrics = shard_uid_and_is_view.map(|(shard_uid, is_view)| { + let mut buffer = itoa::Buffer::new(); + let shard_id = buffer.format(shard_uid.shard_id); + + let metrics_labels: [&str; 2] = [&shard_id, if is_view { "1" } else { "0" }]; + TrieAccountingCacheMetrics { + accounting_cache_hits: metrics::CHUNK_CACHE_HITS.with_label_values(&metrics_labels), + accounting_cache_misses: metrics::CHUNK_CACHE_MISSES + .with_label_values(&metrics_labels), + accounting_cache_size: metrics::CHUNK_CACHE_SIZE.with_label_values(&metrics_labels), + } + }); + Self { enable: false, cache: HashMap::new(), db_read_nodes: 0, mem_read_nodes: 0, metrics } + } + + pub fn set_enabled(&mut self, enabled: bool) { + self.enable = enabled; + } + + /// Retrieve raw bytes from the cache if it exists, otherwise retrieve it + /// from the given storage, and count it as a db access. + pub fn retrieve_raw_bytes_with_accounting( + &mut self, + hash: &CryptoHash, + storage: &dyn TrieStorage, + ) -> Result, StorageError> { + if let Some(node) = self.cache.get(hash) { + self.mem_read_nodes += 1; + if let Some(metrics) = &self.metrics { + metrics.accounting_cache_hits.inc(); + } + Ok(node.clone()) + } else { + self.db_read_nodes += 1; + if let Some(metrics) = &self.metrics { + metrics.accounting_cache_misses.inc(); + } + let node = storage.retrieve_raw_bytes(hash)?; + + if self.enable { + self.cache.insert(*hash, node.clone()); + if let Some(metrics) = &self.metrics { + metrics.accounting_cache_size.set(self.cache.len() as i64); + } + } + Ok(node) + } + } + + /// Used to retroactively account for a node or value that was already accessed + /// through other means (e.g. flat storage read). + pub fn retroactively_account(&mut self, hash: CryptoHash, data: Arc<[u8]>) { + if self.cache.contains_key(&hash) { + self.mem_read_nodes += 1; + } else { + self.db_read_nodes += 1; + } + if self.enable { + self.cache.insert(hash, data); + if let Some(metrics) = &self.metrics { + metrics.accounting_cache_size.set(self.cache.len() as i64); + } + } + } + + pub fn get_trie_nodes_count(&self) -> TrieNodesCount { + TrieNodesCount { db_reads: self.db_read_nodes, mem_reads: self.mem_read_nodes } + } +} diff --git a/core/store/src/trie/config.rs b/core/store/src/trie/config.rs new file mode 100644 index 000000000..9994c9572 --- /dev/null +++ b/core/store/src/trie/config.rs @@ -0,0 +1,81 @@ +use crate::config::TrieCacheConfig; +use crate::StoreConfig; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::types::AccountId; +use std::str::FromStr; +use tracing::error; + +/// Default memory limit, if nothing else is configured. +/// It is chosen to correspond roughly to the old limit, which was +/// 50k entries * TRIE_LIMIT_CACHED_VALUE_SIZE. +pub(crate) const DEFAULT_SHARD_CACHE_TOTAL_SIZE_LIMIT: u64 = + if cfg!(feature = "no_cache") { 1 } else { 50_000_000 }; + +/// Capacity for the deletions queue. +/// It is chosen to fit all hashes of deleted nodes for 3 completely full blocks. +pub(crate) const DEFAULT_SHARD_CACHE_DELETIONS_QUEUE_CAPACITY: usize = + if cfg!(feature = "no_cache") { 1 } else { 100_000 }; + +/// Values above this size (in bytes) are never cached. +/// Note that most of Trie inner nodes are smaller than this - e.g. branches use around 32 * 16 = 512 bytes. +const TRIE_LIMIT_CACHED_VALUE_SIZE: usize = 1000; + +/// Stores necessary configuration for the creation of tries. +#[derive(Clone, Default)] +pub struct TrieConfig { + pub shard_cache_config: TrieCacheConfig, + pub view_shard_cache_config: TrieCacheConfig, + pub enable_receipt_prefetching: bool, + + /// Configured accounts will be prefetched as SWEAT token account, if predecessor is listed as sender. + pub sweat_prefetch_receivers: Vec, + /// List of allowed predecessor accounts for SWEAT prefetching. + pub sweat_prefetch_senders: Vec, + /// List of shards we will load into memory. + pub load_mem_tries_for_shards: Vec, + pub load_mem_tries_for_all_shards: bool, +} + +impl TrieConfig { + /// Create a new `TrieConfig` with default values or the values specified in `StoreConfig`. + pub fn from_store_config(config: &StoreConfig) -> Self { + let mut this = TrieConfig::default(); + + this.shard_cache_config = config.trie_cache.clone(); + this.view_shard_cache_config = config.view_trie_cache.clone(); + + this.enable_receipt_prefetching = config.enable_receipt_prefetching; + for account in &config.sweat_prefetch_receivers { + match AccountId::from_str(account) { + Ok(account_id) => this.sweat_prefetch_receivers.push(account_id), + Err(e) => error!(target: "config", "invalid account id {account}: {e}"), + } + } + for account in &config.sweat_prefetch_senders { + match AccountId::from_str(account) { + Ok(account_id) => this.sweat_prefetch_senders.push(account_id), + Err(e) => error!(target: "config", "invalid account id {account}: {e}"), + } + } + this.load_mem_tries_for_shards = config.load_mem_tries_for_shards.clone(); + this.load_mem_tries_for_all_shards = config.load_mem_tries_for_all_shards; + + this + } + + /// Size limit in bytes per single value for caching in shard caches. + pub fn max_cached_value_size() -> usize { + TRIE_LIMIT_CACHED_VALUE_SIZE + } + + /// Capacity for deletion queue in which nodes are after unforced eviction. + /// + /// The shard cache uses LRU eviction policy for forced evictions. But when a + /// trie value is overwritten or deleted, the associated nodes are no longer + /// useful, with the exception of forks. + /// Thus, deleted and overwritten values are evicted to the deletion queue which + /// delays the actual eviction. + pub fn deletions_queue_capacity(&self) -> usize { + self.shard_cache_config.shard_cache_deletions_queue_capacity + } +} diff --git a/core/store/src/trie/from_flat.rs b/core/store/src/trie/from_flat.rs new file mode 100644 index 000000000..341715776 --- /dev/null +++ b/core/store/src/trie/from_flat.rs @@ -0,0 +1,71 @@ +use crate::flat::{store_helper, FlatStorageError, FlatStorageManager}; +use crate::{ShardTries, StateSnapshotConfig, Store, Trie, TrieConfig, TrieDBStorage, TrieStorage}; +use unc_primitives::{shard_layout::ShardUId, state::FlatStateValue}; +use std::time::Instant; + +// This function creates a new trie from flat storage for a given shard_uid +// store: location of RocksDB store from where we read flatstore +// write_store: location of RocksDB store where we write the newly constructred trie +// shard_uid: The shard which we are recreating +// +// Please note that the trie is created for the block state with height equal to flat_head +// flat state can comtain deltas after flat_head and can be different from tip of the blockchain. +pub fn construct_trie_from_flat(store: Store, write_store: Store, shard_uid: ShardUId) { + let trie_storage = TrieDBStorage::new(store.clone(), shard_uid); + let flat_state_to_trie_kv = + |entry: Result<(Vec, FlatStateValue), FlatStorageError>| -> (Vec, Vec) { + let (key, value) = entry.unwrap(); + let value = match value { + FlatStateValue::Ref(ref_value) => { + trie_storage.retrieve_raw_bytes(&ref_value.hash).unwrap().to_vec() + } + FlatStateValue::Inlined(inline_value) => inline_value, + }; + (key, value) + }; + + let mut iter = store_helper::iter_flat_state_entries(shard_uid, &store, None, None) + .map(flat_state_to_trie_kv); + + // new ShardTries for write storage location + let tries = ShardTries::new( + write_store.clone(), + TrieConfig::default(), + &[shard_uid], + FlatStorageManager::new(write_store), + StateSnapshotConfig::default(), + ); + let mut trie_root = Trie::EMPTY_ROOT; + + let timer = Instant::now(); + while let Some(batch) = get_trie_update_batch(&mut iter) { + // Apply and commit changes + let batch_size = batch.len(); + let new_trie = tries.get_trie_for_shard(shard_uid, trie_root); + let trie_changes = new_trie.update(batch).unwrap(); + let mut store_update = tries.store_update(); + tries.apply_all(&trie_changes, shard_uid, &mut store_update); + store_update.commit().unwrap(); + trie_root = trie_changes.new_root; + + println!("{:.2?} : Processed {} entries", timer.elapsed(), batch_size); + } + + println!("{:.2?} : Completed building trie with root {}", timer.elapsed(), trie_root); +} + +fn get_trie_update_batch( + iter: &mut impl Iterator, Vec)>, +) -> Option, Option>)>> { + let size_limit = 500 * 1000_000; // 500 MB + let mut size = 0; + let mut entries = Vec::new(); + while let Some((key, value)) = iter.next() { + size += key.len() + value.len(); + entries.push((key, Some(value))); + if size > size_limit { + break; + } + } + (!entries.is_empty()).then_some(entries) +} diff --git a/core/store/src/trie/insert_delete.rs b/core/store/src/trie/insert_delete.rs new file mode 100644 index 000000000..9f31cc9d9 --- /dev/null +++ b/core/store/src/trie/insert_delete.rs @@ -0,0 +1,634 @@ +use super::TrieRefcountDeltaMap; +use crate::trie::nibble_slice::NibbleSlice; +use crate::trie::{ + Children, NodeHandle, RawTrieNode, RawTrieNodeWithSize, StorageHandle, StorageValueHandle, + TrieNode, TrieNodeWithSize, ValueHandle, +}; +use crate::{StorageError, Trie, TrieChanges}; +use borsh::BorshSerialize; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::state::ValueRef; + +pub(crate) struct NodesStorage { + nodes: Vec>, + values: Vec>>, + pub(crate) refcount_changes: TrieRefcountDeltaMap, +} + +const INVALID_STORAGE_HANDLE: &str = "invalid storage handle"; + +/// Local mutable storage that owns node objects. +impl NodesStorage { + pub fn new() -> NodesStorage { + NodesStorage { + nodes: Vec::new(), + refcount_changes: TrieRefcountDeltaMap::new(), + values: Vec::new(), + } + } + + fn destroy(&mut self, handle: StorageHandle) -> TrieNodeWithSize { + self.nodes + .get_mut(handle.0) + .expect(INVALID_STORAGE_HANDLE) + .take() + .expect(INVALID_STORAGE_HANDLE) + } + + pub fn node_ref(&self, handle: StorageHandle) -> &TrieNodeWithSize { + self.nodes + .get(handle.0) + .expect(INVALID_STORAGE_HANDLE) + .as_ref() + .expect(INVALID_STORAGE_HANDLE) + } + + fn node_mut(&mut self, handle: StorageHandle) -> &mut TrieNodeWithSize { + self.nodes + .get_mut(handle.0) + .expect(INVALID_STORAGE_HANDLE) + .as_mut() + .expect(INVALID_STORAGE_HANDLE) + } + + pub(crate) fn store(&mut self, node: TrieNodeWithSize) -> StorageHandle { + self.nodes.push(Some(node)); + StorageHandle(self.nodes.len() - 1) + } + + pub(crate) fn store_value(&mut self, value: Vec) -> StorageValueHandle { + self.values.push(Some(value)); + StorageValueHandle(self.values.len() - 1) + } + + pub(crate) fn value_ref(&self, handle: StorageValueHandle) -> &[u8] { + self.values + .get(handle.0) + .expect(INVALID_STORAGE_HANDLE) + .as_ref() + .expect(INVALID_STORAGE_HANDLE) + } + + fn store_at(&mut self, handle: StorageHandle, node: TrieNodeWithSize) { + debug_assert!(self.nodes.get(handle.0).expect(INVALID_STORAGE_HANDLE).is_none()); + self.nodes[handle.0] = Some(node); + } +} + +enum FlattenNodesCrumb { + Entering, + AtChild(Box, u8), + Exiting, +} + +impl Trie { + /// Allowed to mutate nodes in NodesStorage. + /// Insert while holding StorageHandles to NodesStorage is unsafe + pub(crate) fn insert( + &self, + memory: &mut NodesStorage, + node: StorageHandle, + partial: NibbleSlice<'_>, + value: Vec, + ) -> Result { + let root_handle = node; + let mut handle = node; + let mut value = Some(value); + let mut partial = partial; + let mut path = Vec::new(); + loop { + path.push(handle); + let TrieNodeWithSize { node, memory_usage } = memory.destroy(handle); + let children_memory_usage = memory_usage - node.memory_usage_direct(memory); + match node { + TrieNode::Empty => { + let value_handle = memory.store_value(value.take().unwrap()); + let leaf_node = TrieNode::Leaf( + partial.encoded(true).into_vec(), + ValueHandle::InMemory(value_handle), + ); + let memory_usage = leaf_node.memory_usage_direct(memory); + memory.store_at(handle, TrieNodeWithSize { node: leaf_node, memory_usage }); + break; + } + TrieNode::Branch(mut children, existing_value) => { + // If the key ends here, store the value in branch's value. + if partial.is_empty() { + if let Some(value) = &existing_value { + self.delete_value(memory, value)?; + } + let value_handle = memory.store_value(value.take().unwrap()); + let new_node = + TrieNode::Branch(children, Some(ValueHandle::InMemory(value_handle))); + let new_memory_usage = + children_memory_usage + new_node.memory_usage_direct(memory); + memory.store_at(handle, TrieNodeWithSize::new(new_node, new_memory_usage)); + break; + } else { + let child = &mut children[partial.at(0)]; + let new_handle = match child.take() { + Some(NodeHandle::Hash(hash)) => { + self.move_node_to_mutable(memory, &hash)? + } + Some(NodeHandle::InMemory(handle)) => handle, + None => memory.store(TrieNodeWithSize::empty()), + }; + *child = Some(NodeHandle::InMemory(new_handle)); + Trie::calc_memory_usage_and_store( + memory, + handle, + children_memory_usage, + TrieNode::Branch(children, existing_value), + Some(new_handle), + ); + handle = new_handle; + partial = partial.mid(1); + continue; + } + } + TrieNode::Leaf(key, existing_value) => { + let existing_key = NibbleSlice::from_encoded(&key).0; + let common_prefix = partial.common_prefix(&existing_key); + if common_prefix == existing_key.len() && common_prefix == partial.len() { + // Equivalent leaf. + self.delete_value(memory, &existing_value)?; + let value_handle = memory.store_value(value.take().unwrap()); + let node = TrieNode::Leaf(key, ValueHandle::InMemory(value_handle)); + let memory_usage = node.memory_usage_direct(memory); + memory.store_at(handle, TrieNodeWithSize { node, memory_usage }); + break; + } else if common_prefix == 0 { + let mut children = Default::default(); + let children_memory_usage; + let branch_node = if existing_key.is_empty() { + children_memory_usage = 0; + TrieNode::Branch(children, Some(existing_value)) + } else { + let idx = existing_key.at(0); + let new_leaf = TrieNode::Leaf( + existing_key.mid(1).encoded(true).into_vec(), + existing_value, + ); + let memory_usage = new_leaf.memory_usage_direct(memory); + children_memory_usage = memory_usage; + let handle = + memory.store(TrieNodeWithSize { node: new_leaf, memory_usage }); + children[idx] = Some(NodeHandle::InMemory(handle)); + TrieNode::Branch(children, None) + }; + let memory_usage = + branch_node.memory_usage_direct(memory) + children_memory_usage; + memory + .store_at(handle, TrieNodeWithSize { node: branch_node, memory_usage }); + path.pop(); + continue; + } else if common_prefix == existing_key.len() { + let branch_node = + TrieNode::Branch(Default::default(), Some(existing_value)); + let memory_usage = branch_node.memory_usage_direct(memory); + let child = + memory.store(TrieNodeWithSize { node: branch_node, memory_usage }); + let new_node = TrieNode::Extension( + existing_key.encoded(false).into_vec(), + NodeHandle::InMemory(child), + ); + let memory_usage = new_node.memory_usage_direct(memory); + memory.store_at(handle, TrieNodeWithSize { node: new_node, memory_usage }); + handle = child; + partial = partial.mid(common_prefix); + continue; + } else { + // Partially shared prefix: convert to leaf and call recursively to add a branch. + let leaf_node = TrieNode::Leaf( + existing_key.mid(common_prefix).encoded(true).into_vec(), + existing_value, + ); + let leaf_memory_usage = leaf_node.memory_usage_direct(memory); + let child = + memory.store(TrieNodeWithSize::new(leaf_node, leaf_memory_usage)); + let node = TrieNode::Extension( + partial.encoded_leftmost(common_prefix, false).into_vec(), + NodeHandle::InMemory(child), + ); + let mem = node.memory_usage_direct(memory); + memory.store_at(handle, TrieNodeWithSize::new(node, mem)); + handle = child; + partial = partial.mid(common_prefix); + continue; + } + } + TrieNode::Extension(key, child) => { + let existing_key = NibbleSlice::from_encoded(&key).0; + let common_prefix = partial.common_prefix(&existing_key); + if common_prefix == 0 { + let idx = existing_key.at(0); + let child_memory_usage; + let child = if existing_key.len() == 1 { + child_memory_usage = children_memory_usage; + child + } else { + let child = TrieNode::Extension( + existing_key.mid(1).encoded(false).into_vec(), + child, + ); + child_memory_usage = + children_memory_usage + child.memory_usage_direct(memory); + NodeHandle::InMemory( + memory.store(TrieNodeWithSize::new(child, child_memory_usage)), + ) + }; + let mut children = Box::>::default(); + children[idx] = Some(child); + let branch_node = TrieNode::Branch(children, None); + let memory_usage = + branch_node.memory_usage_direct(memory) + child_memory_usage; + memory.store_at(handle, TrieNodeWithSize::new(branch_node, memory_usage)); + path.pop(); + continue; + } else if common_prefix == existing_key.len() { + let child = match child { + NodeHandle::Hash(hash) => self.move_node_to_mutable(memory, &hash)?, + NodeHandle::InMemory(handle) => handle, + }; + let node = TrieNode::Extension(key, NodeHandle::InMemory(child)); + let memory_usage = node.memory_usage_direct(memory); + memory.store_at(handle, TrieNodeWithSize::new(node, memory_usage)); + handle = child; + partial = partial.mid(common_prefix); + continue; + } else { + // Partially shared prefix: covert to shorter extension and recursively add a branch. + let child_node = TrieNode::Extension( + existing_key.mid(common_prefix).encoded(false).into_vec(), + child, + ); + let child_memory_usage = + children_memory_usage + child_node.memory_usage_direct(memory); + let child = + memory.store(TrieNodeWithSize::new(child_node, child_memory_usage)); + let node = TrieNode::Extension( + existing_key.encoded_leftmost(common_prefix, false).into_vec(), + NodeHandle::InMemory(child), + ); + let memory_usage = node.memory_usage_direct(memory); + memory.store_at(handle, TrieNodeWithSize::new(node, memory_usage)); + handle = child; + partial = partial.mid(common_prefix); + continue; + } + } + } + } + for i in (0..path.len() - 1).rev() { + let node = path.get(i).unwrap(); + let child = path.get(i + 1).unwrap(); + let child_memory_usage = memory.node_ref(*child).memory_usage; + memory.node_mut(*node).memory_usage += child_memory_usage; + } + Ok(root_handle) + } + + /// On insert/delete, we want to recompute subtree sizes without touching nodes that aren't on + /// the path of the key inserted/deleted. This is relevant because reducing storage reads + /// saves time and makes fraud proofs smaller. + /// + /// Memory usage is recalculated in two steps: + /// 1. go down the trie, modify the node and subtract the next child on the path from memory usage + /// 2. go up the path and add new child's memory usage + fn calc_memory_usage_and_store( + memory: &mut NodesStorage, + handle: StorageHandle, + children_memory_usage: u64, + new_node: TrieNode, + old_child: Option, + ) { + let new_memory_usage = children_memory_usage + new_node.memory_usage_direct(memory) + - old_child.map(|child| memory.node_ref(child).memory_usage()).unwrap_or_default(); + memory.store_at(handle, TrieNodeWithSize::new(new_node, new_memory_usage)); + } + + /// Deletes a node from the trie which has key = `partial` given root node. + /// Returns (new root node or `None` if this was the node to delete, was it updated). + /// While deleting keeps track of all the removed / updated nodes in `death_row`. + pub(crate) fn delete( + &self, + memory: &mut NodesStorage, + node: StorageHandle, + partial: NibbleSlice<'_>, + ) -> Result { + let mut handle = node; + let mut partial = partial; + let root_node = handle; + let mut path: Vec = Vec::new(); + loop { + path.push(handle); + let TrieNodeWithSize { node, memory_usage } = memory.destroy(handle); + let children_memory_usage = memory_usage - node.memory_usage_direct(memory); + match node { + TrieNode::Empty => { + memory.store_at(handle, TrieNodeWithSize::empty()); + break; + } + TrieNode::Leaf(key, value) => { + if NibbleSlice::from_encoded(&key).0 == partial { + self.delete_value(memory, &value)?; + memory.store_at(handle, TrieNodeWithSize::empty()); + break; + } else { + let leaf_node = TrieNode::Leaf(key, value); + let memory_usage = leaf_node.memory_usage_direct(memory); + memory.store_at(handle, TrieNodeWithSize::new(leaf_node, memory_usage)); + break; + } + } + TrieNode::Branch(mut children, value) => { + if partial.is_empty() { + if let Some(value) = &value { + self.delete_value(memory, value)?; + } + if children.iter().count() == 0 { + memory.store_at(handle, TrieNodeWithSize::empty()); + } else { + Trie::calc_memory_usage_and_store( + memory, + handle, + children_memory_usage, + TrieNode::Branch(children, None), + None, + ); + } + break; + } else { + let child = &mut children[partial.at(0)]; + if let Some(node_or_hash) = child.take() { + let new_handle = match node_or_hash { + NodeHandle::Hash(hash) => { + self.move_node_to_mutable(memory, &hash)? + } + NodeHandle::InMemory(handle) => handle, + }; + *child = Some(NodeHandle::InMemory(new_handle)); + Trie::calc_memory_usage_and_store( + memory, + handle, + children_memory_usage, + TrieNode::Branch(children, value), + Some(new_handle), + ); + handle = new_handle; + partial = partial.mid(1); + continue; + } else { + memory.store_at( + handle, + TrieNodeWithSize::new( + TrieNode::Branch(children, value), + memory_usage, + ), + ); + break; + } + } + } + TrieNode::Extension(key, child) => { + let (common_prefix, existing_len) = { + let existing_key = NibbleSlice::from_encoded(&key).0; + (existing_key.common_prefix(&partial), existing_key.len()) + }; + if common_prefix == existing_len { + let child = match child { + NodeHandle::Hash(hash) => self.move_node_to_mutable(memory, &hash)?, + NodeHandle::InMemory(node) => node, + }; + Trie::calc_memory_usage_and_store( + memory, + handle, + children_memory_usage, + TrieNode::Extension(key, NodeHandle::InMemory(child)), + Some(child), + ); + partial = partial.mid(existing_len); + handle = child; + continue; + } else { + memory.store_at( + handle, + TrieNodeWithSize::new(TrieNode::Extension(key, child), memory_usage), + ); + break; + } + } + } + } + self.fix_nodes(memory, path)?; + Ok(root_node) + } + + fn fix_nodes( + &self, + memory: &mut NodesStorage, + path: Vec, + ) -> Result<(), StorageError> { + let mut child_memory_usage = 0; + for handle in path.into_iter().rev() { + let TrieNodeWithSize { node, memory_usage } = memory.destroy(handle); + let memory_usage = memory_usage + child_memory_usage; + match node { + TrieNode::Empty => { + memory.store_at(handle, TrieNodeWithSize::empty()); + } + TrieNode::Leaf(key, value) => { + memory.store_at( + handle, + TrieNodeWithSize::new(TrieNode::Leaf(key, value), memory_usage), + ); + } + TrieNode::Branch(mut children, value) => { + for child in children.0.iter_mut() { + if let Some(NodeHandle::InMemory(h)) = child { + if let TrieNode::Empty = memory.node_ref(*h).node { + *child = None + } + } + } + let num_children = children.iter().count(); + if num_children == 0 { + if let Some(value) = value { + let empty = NibbleSlice::new(&[]).encoded(true).into_vec(); + let leaf_node = TrieNode::Leaf(empty, value); + let memory_usage = leaf_node.memory_usage_direct(memory); + memory.store_at(handle, TrieNodeWithSize::new(leaf_node, memory_usage)); + } else { + memory.store_at(handle, TrieNodeWithSize::empty()); + } + } else if num_children == 1 && value.is_none() { + // Branch with one child becomes extension + // Extension followed by leaf becomes leaf + // Extension followed by extension becomes extension + let idx = children.iter().next().unwrap().0; + let child = children[idx].take().unwrap(); + let key = NibbleSlice::new(&[(idx << 4) as u8]) + .encoded_leftmost(1, false) + .into_vec(); + self.fix_extension_node(memory, handle, key, child)?; + } else { + let node = + TrieNodeWithSize::new(TrieNode::Branch(children, value), memory_usage); + memory.store_at(handle, node); + } + } + TrieNode::Extension(key, child) => { + self.fix_extension_node(memory, handle, key, child)?; + } + } + child_memory_usage = memory.node_ref(handle).memory_usage; + } + Ok(()) + } + + fn fix_extension_node( + &self, + memory: &mut NodesStorage, + handle: StorageHandle, + key: Vec, + child: NodeHandle, + ) -> Result<(), StorageError> { + let child = match child { + NodeHandle::Hash(hash) => self.move_node_to_mutable(memory, &hash)?, + NodeHandle::InMemory(h) => h, + }; + let TrieNodeWithSize { node, memory_usage } = memory.destroy(child); + let child_child_memory_usage = memory_usage - node.memory_usage_direct(memory); + match node { + TrieNode::Empty => { + memory.store_at(handle, TrieNodeWithSize::empty()); + } + TrieNode::Leaf(child_key, value) => { + let key = NibbleSlice::from_encoded(&key) + .0 + .merge_encoded(&NibbleSlice::from_encoded(&child_key).0, true) + .into_vec(); + let new_node = TrieNode::Leaf(key, value); + let memory_usage = new_node.memory_usage_direct(memory); + memory.store_at(handle, TrieNodeWithSize::new(new_node, memory_usage)); + } + TrieNode::Branch(children, value) => { + memory.store_at( + child, + TrieNodeWithSize::new(TrieNode::Branch(children, value), memory_usage), + ); + let new_node = TrieNode::Extension(key, NodeHandle::InMemory(child)); + let memory_usage = memory_usage + new_node.memory_usage_direct(memory); + memory.store_at(handle, TrieNodeWithSize::new(new_node, memory_usage)); + } + TrieNode::Extension(child_key, child_child) => { + let key = NibbleSlice::from_encoded(&key) + .0 + .merge_encoded(&NibbleSlice::from_encoded(&child_key).0, false) + .into_vec(); + let new_node = TrieNode::Extension(key, child_child); + let memory_usage = new_node.memory_usage_direct(memory) + child_child_memory_usage; + memory.store_at(handle, TrieNodeWithSize::new(new_node, memory_usage)); + } + } + Ok(()) + } + + pub(crate) fn flatten_nodes( + old_root: &CryptoHash, + memory: NodesStorage, + node: StorageHandle, + ) -> Result { + let mut stack: Vec<(StorageHandle, FlattenNodesCrumb)> = Vec::new(); + stack.push((node, FlattenNodesCrumb::Entering)); + let mut last_hash = CryptoHash::default(); + let mut buffer: Vec = Vec::new(); + let mut memory = memory; + 'outer: while let Some((node, position)) = stack.pop() { + let node_with_size = memory.node_ref(node); + let memory_usage = node_with_size.memory_usage; + let raw_node = match &node_with_size.node { + TrieNode::Empty => { + last_hash = Trie::EMPTY_ROOT; + continue; + } + TrieNode::Branch(children, value) => match position { + FlattenNodesCrumb::Entering => { + stack.push((node, FlattenNodesCrumb::AtChild(Default::default(), 0))); + continue; + } + FlattenNodesCrumb::AtChild(mut new_children, mut i) => { + if i > 0 && children[i - 1].is_some() { + new_children[i - 1] = Some(last_hash); + } + while i < 16 { + match children[i].clone() { + Some(NodeHandle::InMemory(handle)) => { + stack.push(( + node, + FlattenNodesCrumb::AtChild(new_children, i + 1), + )); + stack.push((handle, FlattenNodesCrumb::Entering)); + continue 'outer; + } + Some(NodeHandle::Hash(hash)) => new_children[i] = Some(hash), + None => {} + } + i += 1; + } + let new_value = + value.clone().map(|value| Trie::flatten_value(&mut memory, value)); + RawTrieNode::branch(*new_children, new_value) + } + FlattenNodesCrumb::Exiting => unreachable!(), + }, + TrieNode::Extension(key, child) => match position { + FlattenNodesCrumb::Entering => match child { + NodeHandle::InMemory(child) => { + stack.push((node, FlattenNodesCrumb::Exiting)); + stack.push((*child, FlattenNodesCrumb::Entering)); + continue; + } + NodeHandle::Hash(hash) => RawTrieNode::Extension(key.clone(), *hash), + }, + FlattenNodesCrumb::Exiting => RawTrieNode::Extension(key.clone(), last_hash), + _ => unreachable!(), + }, + TrieNode::Leaf(key, value) => { + let key = key.clone(); + let value = value.clone(); + let value = Trie::flatten_value(&mut memory, value); + RawTrieNode::Leaf(key, value) + } + }; + let raw_node_with_size = RawTrieNodeWithSize { node: raw_node, memory_usage }; + raw_node_with_size.serialize(&mut buffer).unwrap(); + let key = hash(&buffer); + + memory.refcount_changes.add(key, buffer.clone(), 1); + buffer.clear(); + last_hash = key; + } + let (insertions, deletions) = memory.refcount_changes.into_changes(); + Ok(TrieChanges { + old_root: *old_root, + new_root: last_hash, + insertions, + deletions, + mem_trie_changes: None, + }) + } + + fn flatten_value(memory: &mut NodesStorage, value: ValueHandle) -> ValueRef { + match value { + ValueHandle::InMemory(value_handle) => { + let value = memory.value_ref(value_handle).to_vec(); + let value_length = value.len() as u32; + let value_hash = hash(&value); + memory.refcount_changes.add(value_hash, value, 1); + ValueRef { length: value_length, hash: value_hash } + } + ValueHandle::HashAndSize(value) => value, + } + } +} diff --git a/core/store/src/trie/iterator.rs b/core/store/src/trie/iterator.rs new file mode 100644 index 000000000..a0d629309 --- /dev/null +++ b/core/store/src/trie/iterator.rs @@ -0,0 +1,685 @@ +use unc_primitives::hash::CryptoHash; + +use crate::trie::nibble_slice::NibbleSlice; +use crate::trie::{TrieNode, TrieNodeWithSize, ValueHandle}; +use crate::{MissingTrieValueContext, StorageError, Trie}; + +/// Crumb is a piece of trie iteration state. It describes a node on the trail and processing status of that node. +#[derive(Debug)] +struct Crumb { + node: TrieNodeWithSize, + status: CrumbStatus, + prefix_boundary: bool, +} + +/// The status of processing of a node during trie iteration. +/// Each node is processed in the following order: +/// Entering -> At -> AtChild(0) -> ... -> AtChild(15) -> Exiting +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub(crate) enum CrumbStatus { + Entering, + At, + AtChild(u8), + Exiting, +} + +impl Crumb { + fn increment(&mut self) { + if self.prefix_boundary { + self.status = CrumbStatus::Exiting; + return; + } + self.status = match (&self.status, &self.node.node) { + (_, &TrieNode::Empty) => CrumbStatus::Exiting, + (&CrumbStatus::Entering, _) => CrumbStatus::At, + (&CrumbStatus::At, &TrieNode::Branch(_, _)) => CrumbStatus::AtChild(0), + (&CrumbStatus::AtChild(x), &TrieNode::Branch(_, _)) if x < 15 => { + CrumbStatus::AtChild(x + 1) + } + _ => CrumbStatus::Exiting, + } + } +} + +/// Trie iteration is done using a stack based approach. +/// There are two stacks that we track while iterating: the trail and the key_nibbles. +/// The trail is a vector of trie nodes on the path from root node to the node that is +/// currently being processed together with processing status - the Crumb. +/// The key_nibbles is a vector of nibbles from the state root node to the node that is +/// currently being processed. +/// The trail and the key_nibbles may have different lengths e.g. an extension trie node +/// will add only a single item to the trail but may add multiple nibbles to the key_nibbles. +pub struct TrieIterator<'a> { + trie: &'a Trie, + trail: Vec, + pub(crate) key_nibbles: Vec, + + /// If not `None`, a list of all nodes that the iterator has visited. + visited_nodes: Option>>, + + /// Prune condition is an optional closure that given the key nibbles + /// decides if the given trie node should be pruned. + /// + /// If the prune conditions returns true for a given node, this node and the + /// whole sub-tree rooted at this node will be pruned and skipped in iteration. + /// + /// Please note that since the iterator supports seeking the prune condition + /// should have the property that if a prefix of a key should be pruned then + /// the key also should be pruned. Otherwise it would be possible to bypass + /// the pruning by seeking inside of the pruned sub-tree. + prune_condition: Option) -> bool>>, +} + +/// The TrieTiem is a tuple of (key, value) of the node. +pub type TrieItem = (Vec, Vec); + +/// Item extracted from Trie during depth first traversal, corresponding to some Trie node. +#[derive(Debug)] +pub struct TrieTraversalItem { + /// Hash of the node. + pub hash: CryptoHash, + /// Key of the node if it stores a value. + pub key: Option>, +} + +impl<'a> TrieIterator<'a> { + #![allow(clippy::new_ret_no_self)] + /// Create a new iterator. + pub(super) fn new( + trie: &'a Trie, + prune_condition: Option) -> bool>>, + ) -> Result { + let mut r = TrieIterator { + trie, + trail: Vec::with_capacity(8), + key_nibbles: Vec::with_capacity(64), + visited_nodes: None, + prune_condition, + }; + r.descend_into_node(&trie.root)?; + Ok(r) + } + + /// Position the iterator on the first element with key >= `key`. + pub fn seek_prefix>(&mut self, key: K) -> Result<(), StorageError> { + self.seek_nibble_slice(NibbleSlice::new(key.as_ref()), true).map(drop) + } + + /// Configures whether the iterator should remember all the nodes its + /// visiting. + /// + /// Use [`Self::into_visited_nodes`] to retrieve the list. + pub fn remember_visited_nodes(&mut self, remember: bool) { + self.visited_nodes = remember.then(|| Vec::new()); + } + + /// Consumes iterator and returns list of nodes it’s visited. + /// + /// By default the iterator *doesn’t* remember nodes it visits. To enable + /// that feature use [`Self::remember_visited_nodes`] method. If the + /// feature is disabled, this method returns an empty list. Otherwise + /// it returns list of nodes visited since the feature was enabled. + pub fn into_visited_nodes(self) -> Vec> { + self.visited_nodes.unwrap_or(Vec::new()) + } + + /// Returns the hash of the last node + pub(crate) fn seek_nibble_slice( + &mut self, + mut key: NibbleSlice<'_>, + is_prefix_seek: bool, + ) -> Result { + self.trail.clear(); + self.key_nibbles.clear(); + // Checks if a key in an extension or leaf matches our search query. + // + // When doing prefix seek, this checks whether `key` is a prefix of + // `ext_key`. When doing regular range seek, this checks whether `key` + // is no greater than `ext_key`. If those conditions aren’t met, the + // node with `ext_key` should not match our query. + let check_ext_key = |key: &NibbleSlice, ext_key: &NibbleSlice| { + if is_prefix_seek { + ext_key.starts_with(key) + } else { + ext_key >= key + } + }; + + let mut hash = self.trie.root; + let mut prev_prefix_boundary = &mut false; + loop { + *prev_prefix_boundary = is_prefix_seek; + self.descend_into_node(&hash)?; + let Crumb { status, node, prefix_boundary } = self.trail.last_mut().unwrap(); + prev_prefix_boundary = prefix_boundary; + match &node.node { + TrieNode::Empty => break, + TrieNode::Leaf(leaf_key, _) => { + let existing_key = NibbleSlice::from_encoded(leaf_key).0; + if !check_ext_key(&key, &existing_key) { + self.key_nibbles.extend(existing_key.iter()); + *status = CrumbStatus::Exiting; + } + break; + } + TrieNode::Branch(children, _) => { + if key.is_empty() { + break; + } + let idx = key.at(0); + self.key_nibbles.push(idx); + *status = CrumbStatus::AtChild(idx); + if let Some(ref child) = children[idx] { + hash = *child.unwrap_hash(); + key = key.mid(1); + } else { + *prefix_boundary = is_prefix_seek; + break; + } + } + TrieNode::Extension(ext_key, child) => { + let existing_key = NibbleSlice::from_encoded(ext_key).0; + if key.starts_with(&existing_key) { + key = key.mid(existing_key.len()); + hash = *child.unwrap_hash(); + *status = CrumbStatus::At; + self.key_nibbles.extend(existing_key.iter()); + } else { + if !check_ext_key(&key, &existing_key) { + *status = CrumbStatus::Exiting; + self.key_nibbles.extend(existing_key.iter()); + } + break; + } + } + } + } + Ok(hash) + } + + /// Fetches block by its hash and adds it to the trail. + /// + /// The node is stored as the last [`Crumb`] in the trail. If iterator is + /// configured to remember all the nodes its visiting (which can be enabled + /// with [`Self::remember_visited_nodes`]), the node will be added to the + /// list. + fn descend_into_node(&mut self, hash: &CryptoHash) -> Result<(), StorageError> { + let (bytes, node) = self.trie.retrieve_node(hash)?; + if let Some(ref mut visited) = self.visited_nodes { + visited.push(bytes.ok_or_else(|| { + StorageError::MissingTrieValue(MissingTrieValueContext::TrieIterator, *hash) + })?); + } + self.trail.push(Crumb { status: CrumbStatus::Entering, node, prefix_boundary: false }); + Ok(()) + } + + fn key(&self) -> Vec { + let mut result = >::with_capacity(self.key_nibbles.len() / 2); + for i in (1..self.key_nibbles.len()).step_by(2) { + result.push(self.key_nibbles[i - 1] * 16 + self.key_nibbles[i]); + } + result + } + + fn has_value(&self) -> bool { + match self.trail.last() { + Some(b) => match &b.status { + CrumbStatus::At => b.node.node.has_value(), + _ => false, + }, + None => false, // Trail finished + } + } + + fn iter_step(&mut self) -> Option { + let last = self.trail.last_mut()?; + last.increment(); + Some(match (last.status, &last.node.node) { + (CrumbStatus::Exiting, n) => { + match n { + TrieNode::Leaf(ref key, _) | TrieNode::Extension(ref key, _) => { + let existing_key = NibbleSlice::from_encoded(key).0; + let l = self.key_nibbles.len(); + self.key_nibbles.truncate(l - existing_key.len()); + } + TrieNode::Branch(_, _) => { + self.key_nibbles.pop(); + } + _ => {} + } + IterStep::PopTrail + } + (CrumbStatus::At, TrieNode::Branch(_, Some(value))) => { + let hash = match value { + ValueHandle::HashAndSize(value) => value.hash, + ValueHandle::InMemory(_node) => unreachable!(), + }; + IterStep::Value(hash) + } + (CrumbStatus::At, TrieNode::Branch(_, None)) => IterStep::Continue, + (CrumbStatus::At, TrieNode::Leaf(key, value)) => { + let hash = match value { + ValueHandle::HashAndSize(value) => value.hash, + ValueHandle::InMemory(_node) => unreachable!(), + }; + let key = NibbleSlice::from_encoded(key).0; + self.key_nibbles.extend(key.iter()); + IterStep::Value(hash) + } + (CrumbStatus::At, TrieNode::Extension(key, child)) => { + let hash = *child.unwrap_hash(); + let key = NibbleSlice::from_encoded(key).0; + self.key_nibbles.extend(key.iter()); + IterStep::Descend(hash) + } + (CrumbStatus::AtChild(i), TrieNode::Branch(children, _)) => { + if i == 0 { + self.key_nibbles.push(0); + } + if let Some(ref child) = children[i] { + if i != 0 { + *self.key_nibbles.last_mut().expect("Pushed child value before") = i; + } + IterStep::Descend(*child.unwrap_hash()) + } else { + IterStep::Continue + } + } + _ => panic!("Should never see Entering or AtChild without a Branch here."), + }) + } + + fn common_prefix(str1: &[u8], str2: &[u8]) -> usize { + let mut prefix = 0; + while prefix < str1.len() && prefix < str2.len() && str1[prefix] == str2[prefix] { + prefix += 1; + } + prefix + } + + // TODO(#9446) remove function when shifting to flat storage iteration for resharding + pub(crate) fn get_trie_items( + &mut self, + path_begin: &[u8], + path_end: &[u8], + ) -> Result, StorageError> { + let path_begin_encoded = NibbleSlice::encode_nibbles(path_begin, false); + self.seek_nibble_slice(NibbleSlice::from_encoded(&path_begin_encoded).0, false)?; + + let mut trie_items = vec![]; + for item in self { + let trie_item = item?; + let key_encoded: Vec<_> = NibbleSlice::new(&trie_item.0).iter().collect(); + if &key_encoded[..] >= path_end { + return Ok(trie_items); + } + trie_items.push(trie_item); + } + Ok(trie_items) + } + + /// Visits all nodes belonging to the interval [path_begin, path_end) in depth-first search + /// order and return TrieTraversalItem for each visited node. + /// Used to generate and apply state parts for state sync. + pub fn visit_nodes_interval( + &mut self, + path_begin: &[u8], + path_end: &[u8], + ) -> Result, StorageError> { + let _span = tracing::debug_span!( + target: "runtime", + "visit_nodes_interval") + .entered(); + let path_begin_encoded = NibbleSlice::encode_nibbles(path_begin, true); + let last_hash = + self.seek_nibble_slice(NibbleSlice::from_encoded(&path_begin_encoded).0, false)?; + let mut prefix = Self::common_prefix(path_end, &self.key_nibbles); + if self.key_nibbles[prefix..] >= path_end[prefix..] { + return Ok(vec![]); + } + let mut nodes_list = Vec::new(); + + // Actually (self.key_nibbles[..] == path_begin) always because path_begin always ends in a node + if &self.key_nibbles[..] >= path_begin { + nodes_list.push(TrieTraversalItem { + hash: last_hash, + key: self.has_value().then(|| self.key()), + }); + } + + loop { + let iter_step = match self.iter_step() { + Some(iter_step) => iter_step, + None => break, + }; + match iter_step { + IterStep::PopTrail => { + self.trail.pop(); + prefix = std::cmp::min(self.key_nibbles.len(), prefix); + } + IterStep::Descend(hash) => { + prefix += Self::common_prefix(&path_end[prefix..], &self.key_nibbles[prefix..]); + if self.key_nibbles[prefix..] >= path_end[prefix..] { + break; + } + self.descend_into_node(&hash)?; + nodes_list.push(TrieTraversalItem { hash, key: None }); + } + IterStep::Continue => {} + IterStep::Value(hash) => { + if self.key_nibbles[prefix..] >= path_end[prefix..] { + break; + } + self.trie.retrieve_value(&hash)?; + nodes_list.push(TrieTraversalItem { + hash, + key: self.has_value().then(|| self.key()), + }); + } + } + } + Ok(nodes_list) + } +} + +#[derive(Debug)] +enum IterStep { + Continue, + PopTrail, + Descend(CryptoHash), + Value(CryptoHash), +} + +impl<'a> Iterator for TrieIterator<'a> { + type Item = Result; + + fn next(&mut self) -> Option { + loop { + let iter_step = self.iter_step()?; + + let can_process = match &self.prune_condition { + Some(prune_condition) => !prune_condition(&self.key_nibbles), + None => true, + }; + + match (iter_step, can_process) { + (IterStep::Continue, _) => {} + (IterStep::PopTrail, _) => { + self.trail.pop(); + } + // Skip processing the node if can process is false. + (_, false) => {} + (IterStep::Descend(hash), true) => match self.descend_into_node(&hash) { + Ok(_) => (), + Err(err) => return Some(Err(err)), + }, + (IterStep::Value(hash), true) => { + return Some( + self.trie.retrieve_value(&hash).map(|value| (self.key(), value.to_vec())), + ) + } + } + } + } +} + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + + use itertools::Itertools; + use rand::seq::SliceRandom; + use rand::Rng; + + use crate::test_utils::{gen_changes, simplify_changes, test_populate_trie, TestTriesBuilder}; + use crate::trie::iterator::IterStep; + use crate::trie::nibble_slice::NibbleSlice; + use crate::Trie; + use unc_primitives::shard_layout::ShardUId; + + fn value() -> Option> { + Some(vec![0]) + } + + /// Checks that for visiting interval of trie nodes first state key is + /// included and the last one is excluded. + #[test] + fn test_visit_interval() { + let trie_changes = vec![(b"aa".to_vec(), Some(vec![1])), (b"abb".to_vec(), Some(vec![2]))]; + let tries = TestTriesBuilder::new().build(); + let state_root = + test_populate_trie(&tries, &Trie::EMPTY_ROOT, ShardUId::single_shard(), trie_changes); + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), state_root); + let path_begin: Vec<_> = NibbleSlice::new(b"aa").iter().collect(); + let path_end: Vec<_> = NibbleSlice::new(b"abb").iter().collect(); + let mut trie_iter = trie.iter().unwrap(); + let items = trie_iter.visit_nodes_interval(&path_begin, &path_end).unwrap(); + let trie_items: Vec<_> = items.into_iter().map(|item| item.key).flatten().collect(); + assert_eq!(trie_items, vec![b"aa"]); + } + + #[test] + fn test_iterator() { + let mut rng = rand::thread_rng(); + for _ in 0..100 { + let (trie_changes, map, trie) = gen_random_trie(&mut rng); + + { + let result1: Vec<_> = trie.iter().unwrap().map(Result::unwrap).collect(); + let result2: Vec<_> = map.iter().map(|(k, v)| (k.clone(), v.clone())).collect(); + assert_eq!(result1, result2); + } + test_seek_prefix(&trie, &map, &[]); + + for (seek_key, _) in trie_changes.iter() { + test_seek_prefix(&trie, &map, seek_key); + } + for _ in 0..20 { + let alphabet = &b"abcdefgh"[0..rng.gen_range(2..8)]; + let key_length = rng.gen_range(1..8); + let seek_key: Vec = + (0..key_length).map(|_| *alphabet.choose(&mut rng).unwrap()).collect(); + test_seek_prefix(&trie, &map, &seek_key); + } + } + } + + #[test] + fn test_iterator_with_prune_condition_base() { + let mut rng = rand::thread_rng(); + for _ in 0..100 { + let (trie_changes, map, trie) = gen_random_trie(&mut rng); + + // Check that pruning just one key (and it's subtree) works as expected. + for (prune_key, _) in &trie_changes { + let prune_key = prune_key.clone(); + let prune_key_nibbles = NibbleSlice::new(prune_key.as_slice()).iter().collect_vec(); + let prune_condition = + move |key_nibbles: &Vec| key_nibbles.starts_with(&prune_key_nibbles); + + let result1 = trie + .iter_with_prune_condition(Some(Box::new(prune_condition.clone()))) + .unwrap() + .map(Result::unwrap) + .collect_vec(); + + let result2 = map + .iter() + .filter(|(key, _)| { + !prune_condition(&NibbleSlice::new(key).iter().collect_vec()) + }) + .map(|(key, value)| (key.clone(), value.clone())) + .collect_vec(); + + assert_eq!(result1, result2); + } + } + } + + // Check that pruning a node doesn't descend into it's subtree. + // A buggy pruning implementation could still iterate over all the + // nodes but simply not return them. This test makes sure this is + // not the case. + #[test] + fn test_iterator_with_prune_condition_subtree() { + let mut rng = rand::thread_rng(); + for _ in 0..100 { + let (trie_changes, map, trie) = gen_random_trie(&mut rng); + + // Test pruning by all keys that are present in the trie. + for (prune_key, _) in &trie_changes { + // This prune condition is not valid in a sense that it only + // prunes a single node but not it's subtree. This is + // intentional to test that iterator won't descend into the + // subtree. + let prune_key_nibbles = NibbleSlice::new(prune_key.as_slice()).iter().collect_vec(); + let prune_condition = + move |key_nibbles: &Vec| key_nibbles == &prune_key_nibbles; + // This is how the prune condition should work. + let prune_key_nibbles = NibbleSlice::new(prune_key.as_slice()).iter().collect_vec(); + let proper_prune_condition = + move |key_nibbles: &Vec| key_nibbles.starts_with(&prune_key_nibbles); + + let result1 = trie + .iter_with_prune_condition(Some(Box::new(prune_condition.clone()))) + .unwrap() + .map(Result::unwrap) + .collect_vec(); + let result2 = map + .iter() + .filter(|(key, _)| { + !proper_prune_condition(&NibbleSlice::new(key).iter().collect_vec()) + }) + .map(|(key, value)| (key.clone(), value.clone())) + .collect_vec(); + + assert_eq!(result1, result2); + } + } + } + + // Utility function for testing trie iteration with the prune condition set. + // * `keys` is a list of keys to be inserted into the trie + // * `pruned_keys` is the expected list of keys that should be the result of iteration + fn test_prune_max_depth_impl( + keys: &Vec>, + pruned_keys: &Vec>, + max_depth: usize, + ) { + let shard_uid = ShardUId::single_shard(); + let tries = TestTriesBuilder::new().build(); + let trie_changes = keys.iter().map(|key| (key.clone(), value())).collect(); + let state_root = test_populate_trie(&tries, &Trie::EMPTY_ROOT, shard_uid, trie_changes); + let trie = tries.get_trie_for_shard(shard_uid, state_root); + let iter = trie.iter_with_max_depth(max_depth).unwrap(); + let keys: Vec<_> = iter.map(|item| item.unwrap().0).collect(); + + assert_eq!(&keys, pruned_keys); + } + + #[test] + fn test_prune_max_depth() { + // simple trie with an extension + // extension(11111) + // branch(5, 6) + // leaf(5) leaf(6) + let extension_keys = vec![vec![0x11, 0x11, 0x15], vec![0x11, 0x11, 0x16]]; + // max_depth is expressed in nibbles + // both leaf nodes are at depth 6 (11 11 15) and (11 11 16) + + // pruning by max depth 5 should return an empty result + test_prune_max_depth_impl(&extension_keys, &vec![], 5); + // pruning by max depth 6 should return both leaves + test_prune_max_depth_impl(&extension_keys, &extension_keys, 6); + + // long chain of branches + let chain_keys = vec![ + vec![0x11], + vec![0x11, 0x11], + vec![0x11, 0x11, 0x11], + vec![0x11, 0x11, 0x11, 0x11], + vec![0x11, 0x11, 0x11, 0x11, 0x11], + ]; + test_prune_max_depth_impl(&chain_keys, &vec![], 1); + test_prune_max_depth_impl(&chain_keys, &vec![vec![0x11]], 2); + test_prune_max_depth_impl(&chain_keys, &vec![vec![0x11]], 3); + test_prune_max_depth_impl(&chain_keys, &vec![vec![0x11], vec![0x11, 0x11]], 4); + test_prune_max_depth_impl(&chain_keys, &vec![vec![0x11], vec![0x11, 0x11]], 5); + } + + fn gen_random_trie( + rng: &mut rand::rngs::ThreadRng, + ) -> (Vec<(Vec, Option>)>, BTreeMap, Vec>, Trie) { + let tries = TestTriesBuilder::new().with_shard_layout(1, 2).build(); + let shard_uid = ShardUId { version: 1, shard_id: 0 }; + let trie_changes = gen_changes(rng, 10); + let trie_changes = simplify_changes(&trie_changes); + + let mut map = BTreeMap::new(); + for (key, value) in trie_changes.iter() { + if let Some(value) = value { + map.insert(key.clone(), value.clone()); + } + } + let state_root = + test_populate_trie(&tries, &Trie::EMPTY_ROOT, shard_uid, trie_changes.clone()); + let trie = tries.get_trie_for_shard(shard_uid, state_root); + (trie_changes, map, trie) + } + + fn test_seek_prefix(trie: &Trie, map: &BTreeMap, Vec>, seek_key: &[u8]) { + let mut iterator = trie.iter().unwrap(); + iterator.seek_prefix(&seek_key).unwrap(); + let mut got = Vec::with_capacity(5); + for item in iterator { + let (key, value) = item.unwrap(); + assert!(key.starts_with(seek_key), "‘{key:x?}’ does not start with ‘{seek_key:x?}’"); + if got.len() < 5 { + got.push((key, value)); + } + } + let want: Vec<_> = map + .range(seek_key.to_vec()..) + .map(|(k, v)| (k.clone(), v.clone())) + .take(5) + .filter(|(x, _)| x.starts_with(seek_key)) + .collect(); + assert_eq!(got, want); + } + + #[test] + fn test_has_value() { + let mut rng = rand::thread_rng(); + for _ in 0..100 { + let tries = TestTriesBuilder::new().build(); + let trie_changes = gen_changes(&mut rng, 10); + let trie_changes = simplify_changes(&trie_changes); + let state_root = test_populate_trie( + &tries, + &Trie::EMPTY_ROOT, + ShardUId::single_shard(), + trie_changes.clone(), + ); + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), state_root); + let mut iterator = trie.iter().unwrap(); + loop { + let iter_step = match iterator.iter_step() { + Some(iter_step) => iter_step, + None => break, + }; + match iter_step { + IterStep::Value(_) => assert!(iterator.has_value()), + _ => assert!(!iterator.has_value()), + } + match iter_step { + IterStep::PopTrail => { + iterator.trail.pop(); + } + IterStep::Descend(hash) => iterator.descend_into_node(&hash).unwrap(), + _ => {} + } + } + } + } +} diff --git a/core/store/src/trie/mem/arena/alloc.rs b/core/store/src/trie/mem/arena/alloc.rs new file mode 100644 index 000000000..200b5aff0 --- /dev/null +++ b/core/store/src/trie/mem/arena/alloc.rs @@ -0,0 +1,219 @@ +use unc_o11y::metrics::IntGauge; + +use super::metrics::MEM_TRIE_ARENA_ACTIVE_ALLOCS_COUNT; +use super::{ArenaMemory, ArenaPos, ArenaSliceMut}; +use crate::trie::mem::arena::metrics::{ + MEM_TRIE_ARENA_ACTIVE_ALLOCS_BYTES, MEM_TRIE_ARENA_MEMORY_USAGE_BYTES, +}; +use crate::trie::mem::flexible_data::encoding::BorshFixedSize; + +/// Simple bump allocator with freelists. +/// +/// Allocations are rounded up to its allocation class, so that deallocated +/// memory can be reused by a similarly sized allocation. Each allocation +/// class maintains a separate freelist. +/// +/// Allocations are first done by popping from a freelist, if available. If not, +/// we allocate a new region by bump `next_alloc_pos` forward. Deallocated +/// regions are added to their corresponding freelist. +/// +/// As a result, the memory usage of this allocator never decreases. In +/// practice, for in-memory tries, there is very little memory usage creep even +/// when tested over weeks of uptime. +pub struct Allocator { + /// The head of a linked list of freed allocations; one for each allocation + /// class. + /// The "next" pointer of a freed allocation is stored in the first + /// 8 bytes of the allocated memory itself. An empty freelist is represented + /// using `ArenaPos::invalid()`. + freelists: [ArenaPos; NUM_ALLOCATION_CLASSES], + /// The next position in the arena that a new allocation (which cannot be + /// satisfied by popping one from a freelist) will be allocated at. + /// This position would only ever move forward. De-allocating an allocation + /// does not affect this position; it only adds an entry to a freelist. + next_alloc_pos: ArenaPos, + + // Stats. Note that keep the bytes and count locally too because the + // gauges are process-wide, so stats-keeping directly with those may not be + // accurate in case multiple instances of the allocator share the same name. + active_allocs_bytes: usize, + active_allocs_count: usize, + active_allocs_bytes_gauge: IntGauge, + active_allocs_count_gauge: IntGauge, + memory_usage_gauge: IntGauge, +} + +const MAX_ALLOC_SIZE: usize = 16 * 1024; +const ROUND_UP_TO_8_BYTES_UNDER: usize = 256; +const ROUND_UP_TO_64_BYTES_UNDER: usize = 1024; +const CHUNK_SIZE: usize = 4 * 1024 * 1024; + +/// Calculates the allocation class (an index from 0 to NUM_ALLOCATION_CLASSES) +/// for the given size that we wish to allocate. +const fn allocation_class(size: usize) -> usize { + if size <= ROUND_UP_TO_8_BYTES_UNDER { + (size + 7) / 8 - 1 + } else if size <= ROUND_UP_TO_64_BYTES_UNDER { + (size - ROUND_UP_TO_8_BYTES_UNDER + 63) / 64 + allocation_class(ROUND_UP_TO_8_BYTES_UNDER) + } else { + ((ROUND_UP_TO_64_BYTES_UNDER - 1).leading_zeros() - (size - 1).leading_zeros()) as usize + + allocation_class(ROUND_UP_TO_64_BYTES_UNDER) + } +} + +/// Calculates the size of the actual allocation for the given size class. +const fn allocation_size(size_class: usize) -> usize { + if size_class <= allocation_class(ROUND_UP_TO_8_BYTES_UNDER) { + (size_class + 1) * 8 + } else if size_class <= allocation_class(ROUND_UP_TO_64_BYTES_UNDER) { + (size_class - allocation_class(ROUND_UP_TO_8_BYTES_UNDER)) * 64 + ROUND_UP_TO_8_BYTES_UNDER + } else { + ROUND_UP_TO_64_BYTES_UNDER << (size_class - allocation_class(ROUND_UP_TO_64_BYTES_UNDER)) + } +} + +const NUM_ALLOCATION_CLASSES: usize = allocation_class(MAX_ALLOC_SIZE) + 1; + +impl Allocator { + pub fn new(name: String) -> Self { + Self { + freelists: [ArenaPos::invalid(); NUM_ALLOCATION_CLASSES], + next_alloc_pos: ArenaPos::invalid(), + active_allocs_bytes: 0, + active_allocs_count: 0, + active_allocs_bytes_gauge: MEM_TRIE_ARENA_ACTIVE_ALLOCS_BYTES + .with_label_values(&[&name]), + active_allocs_count_gauge: MEM_TRIE_ARENA_ACTIVE_ALLOCS_COUNT + .with_label_values(&[&name]), + memory_usage_gauge: MEM_TRIE_ARENA_MEMORY_USAGE_BYTES.with_label_values(&[&name]), + } + } + + /// Adds a new chunk to the arena, and updates the next_alloc_pos to the beginning of + /// the new chunk. + fn new_chunk(&mut self, arena: &mut ArenaMemory) { + arena.chunks.push(vec![0; CHUNK_SIZE]); + self.next_alloc_pos = + ArenaPos { chunk: u32::try_from(arena.chunks.len() - 1).unwrap(), pos: 0 }; + self.memory_usage_gauge.set(arena.chunks.len() as i64 * CHUNK_SIZE as i64); + } + + /// Allocates a slice of the given size in the arena. + pub fn allocate<'a>(&mut self, arena: &'a mut ArenaMemory, size: usize) -> ArenaSliceMut<'a> { + assert!(size <= MAX_ALLOC_SIZE, "Cannot allocate {} bytes", size); + self.active_allocs_bytes += size; + self.active_allocs_count += 1; + self.active_allocs_bytes_gauge.set(self.active_allocs_bytes as i64); + self.active_allocs_count_gauge.set(self.active_allocs_count as i64); + let size_class = allocation_class(size); + let allocation_size = allocation_size(size_class); + if self.freelists[size_class].is_invalid() { + if self.next_alloc_pos.is_invalid() + || arena.chunks[self.next_alloc_pos.chunk()].len() + <= self.next_alloc_pos.pos() + allocation_size + { + self.new_chunk(arena); + } + let ptr = self.next_alloc_pos; + self.next_alloc_pos = self.next_alloc_pos.offset_by(allocation_size); + arena.slice_mut(ptr, size) + } else { + let pos = self.freelists[size_class]; + self.freelists[size_class] = arena.ptr(pos).read_pos(); + arena.slice_mut(pos, size) + } + } + + /// Deallocates the given slice from the arena; the slice's `pos` and `len` + /// must be the same as an allocation that was returned earlier. + pub fn deallocate(&mut self, arena: &mut ArenaMemory, pos: ArenaPos, len: usize) { + self.active_allocs_bytes -= len; + self.active_allocs_count -= 1; + self.active_allocs_bytes_gauge.set(self.active_allocs_bytes as i64); + self.active_allocs_count_gauge.set(self.active_allocs_count as i64); + let size_class = allocation_class(len); + arena.slice_mut(pos, ArenaPos::SERIALIZED_SIZE).write_pos_at(0, self.freelists[size_class]); + self.freelists[size_class] = pos; + } + + #[cfg(test)] + pub fn num_active_allocs(&self) -> usize { + self.active_allocs_count + } +} + +#[cfg(test)] +mod test { + use super::MAX_ALLOC_SIZE; + use crate::trie::mem::arena::alloc::CHUNK_SIZE; + use crate::trie::mem::arena::Arena; + use std::mem::size_of; + + #[test] + fn test_allocate_deallocate() { + let mut arena = Arena::new("".to_owned()); + // Repeatedly allocate and deallocate. + for i in 0..10 { + let mut slices = Vec::new(); + for size in (1..=16384).step_by(3) { + let mut alloc = arena.alloc(size); + // Check that the allocated length is large enough. + assert!(alloc.len >= size); + let region = alloc.raw_slice_mut(); + // Try writing some arbitrary bytes into the allocated space. + for j in 0..size { + region[j] = ((i + j) % 256) as u8; + } + slices.push((alloc.pos, alloc.len)); + } + slices.sort_by_key(|(pos, _)| *pos); + // Check that the allocated intervals don't overlap. + for i in 1..slices.len() { + assert!(slices[i - 1].0.offset_by(slices[i - 1].1) <= slices[i].0); + } + // Check that each allocated interval is valid. + for (pos, len) in &slices { + assert!((pos.chunk()) < arena.memory.chunks.len()); + assert!(pos.offset_by(*len).pos() <= CHUNK_SIZE); + } + for (pos, len) in slices { + arena.dealloc(pos, len); + } + } + } + + #[test] + fn test_allocation_reuse() { + let mut arena = Arena::new("".to_owned()); + // Repeatedly allocate and deallocate. New allocations should reuse + // old deallocated memory. + for _ in 0..10 { + let mut slices = Vec::new(); + for _ in 0..2000 { + let alloc = arena.alloc(8192); + slices.push((alloc.pos, alloc.len)); + } + for (pos, len) in slices { + arena.dealloc(pos, len); + } + } + assert_eq!(arena.num_active_allocs(), 0); + // 8192 * 2000 <= 16MB, so we should have allocated only 4 chunks. + assert_eq!(arena.memory.chunks.len(), 4); + } + + #[test] + fn test_size_classes() { + for i in 1..=MAX_ALLOC_SIZE { + let size_class = super::allocation_class(i); + assert!(size_class < super::NUM_ALLOCATION_CLASSES); + let size = super::allocation_size(size_class); + assert!(size >= i); // alloc must be large enough + assert!(size >= size_of::()); // needed for freelist pointers + if size_class > 0 { + // Allocation must be as small as possible to fit the size. + assert!(super::allocation_size(size_class - 1) < i); + } + } + } +} diff --git a/core/store/src/trie/mem/arena/metrics.rs b/core/store/src/trie/mem/arena/metrics.rs new file mode 100644 index 000000000..2e4034a52 --- /dev/null +++ b/core/store/src/trie/mem/arena/metrics.rs @@ -0,0 +1,29 @@ +use unc_o11y::metrics::{try_create_int_gauge_vec, IntGaugeVec}; +use once_cell::sync::Lazy; + +pub static MEM_TRIE_ARENA_ACTIVE_ALLOCS_BYTES: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_mem_trie_arena_active_allocs_bytes", + "Total size of active allocations on the in-memory trie arena", + &["shard_uid"], + ) + .unwrap() +}); + +pub static MEM_TRIE_ARENA_ACTIVE_ALLOCS_COUNT: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_mem_trie_arena_active_allocs_count", + "Total number of active allocations on the in-memory trie arena", + &["shard_uid"], + ) + .unwrap() +}); + +pub static MEM_TRIE_ARENA_MEMORY_USAGE_BYTES: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_mem_trie_arena_memory_usage_bytes", + "Memory usage of the in-memory trie arena", + &["shard_uid"], + ) + .unwrap() +}); diff --git a/core/store/src/trie/mem/arena/mod.rs b/core/store/src/trie/mem/arena/mod.rs new file mode 100644 index 000000000..614b5b733 --- /dev/null +++ b/core/store/src/trie/mem/arena/mod.rs @@ -0,0 +1,356 @@ +mod alloc; +mod metrics; +use self::alloc::Allocator; +use borsh::{BorshDeserialize, BorshSerialize}; +use std::fmt::{Debug, Display, Formatter}; +use std::hash::Hash; +use std::mem::size_of; + +use super::flexible_data::encoding::BorshFixedSize; + +/// Arena to store in-memory trie nodes. +/// Includes a bump allocator that can free nodes. +/// +/// To allocate, deallocate, or mutate any allocated memory, a mutable +/// reference to the `Arena` is needed. +pub struct Arena { + memory: ArenaMemory, + allocator: Allocator, +} + +/// Mmap-ed memory to host the in-memory trie nodes. +/// A mutable reference to `ArenaMemory` can be used to mutate allocated +/// memory, but not to allocate or deallocate memory. +/// +/// From an `ArenaMemory` one can obtain an `ArenaPtr` (single location) +/// or `ArenaSlice` (range of bytes) to read the actual memory, and the +/// mutable versions `ArenaPtrMut` and `ArenaSliceMut` to write memory. +pub struct ArenaMemory { + chunks: Vec>, +} + +#[derive( + Copy, + Clone, + Debug, + Hash, + PartialEq, + Eq, + Default, + PartialOrd, + Ord, + BorshSerialize, + BorshDeserialize, +)] +pub struct ArenaPos { + chunk: u32, + pos: u32, +} + +impl BorshFixedSize for ArenaPos { + const SERIALIZED_SIZE: usize = 8; +} + +impl Display for ArenaPos { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "c{:X}:p{:X}", self.chunk, self.pos) + } +} + +impl ArenaPos { + pub fn pos(&self) -> usize { + self.pos as usize + } + + pub fn chunk(&self) -> usize { + self.chunk as usize + } + + pub fn offset_by(self, offset: usize) -> Self { + Self { chunk: self.chunk, pos: self.pos + u32::try_from(offset).unwrap() } + } + + pub(crate) const fn invalid() -> Self { + Self { chunk: u32::MAX, pos: u32::MAX } + } + + pub(crate) const fn is_invalid(&self) -> bool { + self.chunk == u32::MAX && self.pos == u32::MAX + } +} + +impl ArenaMemory { + fn new() -> Self { + Self { chunks: Vec::new() } + } + + fn raw_slice(&self, pos: ArenaPos, len: usize) -> &[u8] { + &self.chunks[pos.chunk()][pos.pos()..pos.pos() + len] + } + + fn raw_slice_mut(&mut self, pos: ArenaPos, len: usize) -> &mut [u8] { + &mut self.chunks[pos.chunk()][pos.pos()..pos.pos() + len] + } + + /// Provides read access to a region of memory in the arena. + pub fn slice<'a>(&'a self, pos: ArenaPos, len: usize) -> ArenaSlice<'a> { + ArenaSlice { arena: self, pos, len } + } + + /// Provides write access to a region of memory in the arena. + pub fn slice_mut<'a>(&'a mut self, pos: ArenaPos, len: usize) -> ArenaSliceMut<'a> { + ArenaSliceMut { arena: self, pos, len } + } + + /// Represents some position in the arena but without a known length. + pub fn ptr<'a>(self: &'a Self, pos: ArenaPos) -> ArenaPtr<'a> { + ArenaPtr { arena: self, pos } + } + + /// Like `ptr` but with write access. + pub fn ptr_mut<'a>(self: &'a mut Self, pos: ArenaPos) -> ArenaPtrMut<'a> { + ArenaPtrMut { arena: self, pos } + } +} + +impl Arena { + /// Creates a new memory region of the given size to store trie nodes. + /// The `max_size_in_bytes` can be conservatively large as long as it + /// can fit into virtual memory (which there are terabytes of). The actual + /// memory usage will only be as much as is needed. + pub fn new(name: String) -> Self { + Self { memory: ArenaMemory::new(), allocator: Allocator::new(name) } + } + + /// Allocates a slice of the given size in the arena. + pub fn alloc<'a>(&'a mut self, size: usize) -> ArenaSliceMut<'a> { + self.allocator.allocate(&mut self.memory, size) + } + + /// Deallocates the given slice from the arena; the slice's `pos` and `len` + /// must be the same as an allocation that was returned earlier. + pub fn dealloc(&mut self, pos: ArenaPos, len: usize) { + self.allocator.deallocate(&mut self.memory, pos, len); + } + + /// Number of active allocations (alloc calls minus dealloc calls). + #[cfg(test)] + pub fn num_active_allocs(&self) -> usize { + self.allocator.num_active_allocs() + } + + pub fn memory(&self) -> &ArenaMemory { + &self.memory + } + + pub fn memory_mut(&mut self) -> &mut ArenaMemory { + &mut self.memory + } +} + +/// Represents some position in the arena but without a known length. +#[derive(Clone, Copy)] +pub struct ArenaPtr<'a> { + arena: &'a ArenaMemory, + pos: ArenaPos, +} + +impl<'a> Debug for ArenaPtr<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "arena[{}]", self.pos) + } +} + +impl<'a> Hash for ArenaPtr<'a> { + fn hash(&self, state: &mut H) { + self.pos.hash(state); + } +} + +impl<'a> PartialEq for ArenaPtr<'a> { + fn eq(&self, other: &Self) -> bool { + self.arena as *const ArenaMemory == other.arena as *const ArenaMemory + && self.pos == other.pos + } +} + +impl<'a> Eq for ArenaPtr<'a> {} + +impl<'a> ArenaPtr<'a> { + /// Returns a slice relative to this pointer. + pub fn slice(&self, offset: usize, len: usize) -> ArenaSlice<'a> { + ArenaSlice { arena: self.arena, pos: self.pos.offset_by(offset), len } + } + + /// Returns the raw position in the arena. + pub fn raw_pos(&self) -> ArenaPos { + self.pos + } + + pub fn arena(&self) -> &'a ArenaMemory { + self.arena + } + + /// Reads an ArenaPos at the memory pointed to by this pointer. + pub fn read_pos(&self) -> ArenaPos { + ArenaPos::try_from_slice(&self.arena.raw_slice(self.pos, size_of::())).unwrap() + } +} + +/// Represents a slice of memory in the arena. +#[derive(Clone)] +pub struct ArenaSlice<'a> { + arena: &'a ArenaMemory, + pos: ArenaPos, + len: usize, +} + +impl<'a> Debug for ArenaSlice<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "arena[{}..{:x}]", self.pos, self.pos.pos() + self.len) + } +} + +impl<'a> ArenaSlice<'a> { + pub fn len(&self) -> usize { + self.len + } + + /// Returns the pointer pointing to the beginning of this slice. + pub fn ptr(&self) -> ArenaPtr<'a> { + ArenaPtr { arena: self.arena, pos: self.pos } + } + + /// Provides direct access to the memory. + pub fn raw_slice(&self) -> &[u8] { + &self.arena.raw_slice(self.pos, self.len) + } + + /// Reads a usize at the given offset in the slice (bounds checked), + /// and returns it as a pointer. + pub fn read_ptr_at(&self, pos: usize) -> ArenaPtr<'a> { + let pos = ArenaPos::try_from_slice(&self.raw_slice()[pos..][..size_of::()]).unwrap(); + ArenaPtr { arena: self.arena, pos } + } + + /// Returns a slice within this slice. This checks bounds. + pub fn subslice(&self, start: usize, len: usize) -> ArenaSlice<'a> { + assert!(start + len <= self.len); + ArenaSlice { arena: self.arena, pos: self.pos.offset_by(start), len } + } +} + +/// Like `ArenaPtr` but allows writing to the memory. +pub struct ArenaPtrMut<'a> { + arena: &'a mut ArenaMemory, + pos: ArenaPos, +} + +impl<'a> ArenaPtrMut<'a> { + /// Makes a const copy of the pointer, which can only be used while also + /// holding a reference to the original pointer. + pub fn ptr<'b>(&'b self) -> ArenaPtr<'b> { + ArenaPtr { arena: self.arena, pos: self.pos } + } + + /// Makes a mutable copy of the pointer, which can only be used while also + /// holding a mutable reference to the original pointer. + pub fn ptr_mut<'b>(&'b mut self) -> ArenaPtrMut<'b> { + ArenaPtrMut { arena: self.arena, pos: self.pos } + } + + /// Makes a slice relative to the pointer, which can only be used while + /// also holding a mutable reference to the original pointer. + pub fn slice<'b>(&'b self, offset: usize, len: usize) -> ArenaSlice<'b> { + ArenaSlice { arena: self.arena, pos: self.pos.offset_by(offset), len } + } + + /// Like `slice` but makes a mutable slice. + pub fn slice_mut<'b>(&'b mut self, offset: usize, len: usize) -> ArenaSliceMut<'b> { + ArenaSliceMut { arena: self.arena, pos: self.pos.offset_by(offset), len } + } + + /// Provides mutable access to the whole memory, while holding a mutable + /// reference to the pointer. + pub fn arena_mut<'b>(&'b mut self) -> &'b mut ArenaMemory { + &mut self.arena + } +} + +/// Represents a mutable slice of memory in the arena. +pub struct ArenaSliceMut<'a> { + arena: &'a mut ArenaMemory, + pos: ArenaPos, + len: usize, +} + +impl<'a> ArenaSliceMut<'a> { + pub fn raw_pos(&self) -> ArenaPos { + self.pos + } + + pub fn len(&self) -> usize { + self.len + } + + /// Writes a ArenaPos at the given offset in the slice (bounds checked). + pub fn write_pos_at(&mut self, offset: usize, to_write: ArenaPos) { + assert!(offset + ArenaPos::SERIALIZED_SIZE <= self.len); + to_write + .serialize( + &mut &mut self + .arena + .raw_slice_mut(self.pos.offset_by(offset), ArenaPos::SERIALIZED_SIZE), + ) + .unwrap(); + } + + /// Provides mutable raw memory access. + pub fn raw_slice_mut(&mut self) -> &mut [u8] { + self.arena.raw_slice_mut(self.pos, self.len) + } + + /// Provides a subslice that is also mutable, which is only possible while + /// holding a mutable reference to the original slice. + pub fn subslice_mut<'b>(&'b mut self, start: usize, len: usize) -> ArenaSliceMut<'b> { + assert!(start + len <= self.len); + ArenaSliceMut { arena: self.arena, pos: self.pos.offset_by(start), len } + } +} + +#[cfg(test)] +mod tests { + use crate::trie::mem::arena::ArenaPos; + + #[test] + fn test_arena_ptr_and_slice() { + let mut arena = super::ArenaMemory::new(); + arena.chunks.push(vec![0; 1000]); + arena.chunks.push(vec![0; 1000]); + + let chunk1 = ArenaPos { chunk: 1, pos: 0 }; + + arena.ptr_mut(chunk1.offset_by(8)).slice_mut(4, 16).write_pos_at(6, chunk1.offset_by(123)); + assert_eq!( + arena.ptr(chunk1.offset_by(8)).slice(4, 16).read_ptr_at(6).raw_pos(), + chunk1.offset_by(123) + ); + assert_eq!( + arena.slice(chunk1.offset_by(18), 8).read_ptr_at(0).raw_pos(), + chunk1.offset_by(123) + ); + + arena + .slice_mut(chunk1.offset_by(10), 20) + .subslice_mut(1, 8) + .write_pos_at(0, chunk1.offset_by(234)); + assert_eq!( + arena.slice(chunk1.offset_by(10), 20).subslice(1, 8).read_ptr_at(0).raw_pos(), + chunk1.offset_by(234) + ); + assert_eq!( + arena.slice(chunk1.offset_by(11), 8).read_ptr_at(0).raw_pos(), + chunk1.offset_by(234) + ); + } +} diff --git a/core/store/src/trie/mem/construction.rs b/core/store/src/trie/mem/construction.rs new file mode 100644 index 000000000..d193a239f --- /dev/null +++ b/core/store/src/trie/mem/construction.rs @@ -0,0 +1,318 @@ +use super::arena::Arena; +use super::node::MemTrieNodeId; +use crate::trie::mem::node::InputMemTrieNode; +use crate::NibbleSlice; +use unc_primitives::state::FlatStateValue; + +/// Algorithm to construct a trie from a given stream of sorted leaf values. +/// +/// This is a bottom-up algorithm that avoids constructing trie nodes until +/// they are complete. The algorithm is only intended to be used when +/// constructing the initial trie; it does not work at all for incremental +/// updates. +/// +/// The algorithm maintains a list of segments, where each segment represents +/// a subpath of the last key (where path means a sequence of nibbles), and +/// the segments' subpaths join together to form the last key. +// +// To understand the algorithm, conceptually imagine a tree like this: +// +// X +// / \ +// O X +// / \ +// / \ +// O \ +// / \ \ +// O O X +// / \ +// O X +// Nodes marked with X are `TrieConstructionSegment`s, whereas nodes marked +// with O are already constructed, `MemTrieNodeId`s. As we build out the trie, +// X's will become O's. A `TrieConstructionSegment` represents an `X` along +// with the tail segment(s) immediately below it (as opposed to above). So +// the first segment in the above drawing would represent: +// +// X +// / \ +// O +// +// this is a branch node (possibly with a leaf value), with a single child +// on the left (so `children` array has a single element), with the current +// `trail` being the edge sticking out to the right. +// +// Notice that the X's always form the rightmost path of the trie. This is +// because anything to the left have already been determined so they are +// immutable. The algorithm assumes that keys are ingested in order, so we +// are always constructing further to the right. Suppose we encounter a new +// key that shares a prefix with the first segment but diverges in the middle +// of the second segment, like so: +// +// X X +// / \ / \ +// O X O X +// / \ / \ +// / \ <--- diverges here / X----+ +// O \ O \ \ +// / \ \ / \ \ \ +// O O X ====> O O O X +// / \ / \ +// O X O O +// +// As the bottom two segments are no longer part of the right-most path, they +// are converted to concrete TrieMemNodeId's. +pub struct TrieConstructor<'a> { + arena: &'a mut Arena, + segments: Vec, +} + +/// A segment of the rightmost path of the trie under construction, as +/// described above. Ultimately, a segment is turned into a node when it's +/// no longer part of the rightmost path. +struct TrieConstructionSegment { + /// Always determined at the beginning. If true, this is a branch node, + /// possibly with value; if not, this is either leaf or extension node. + is_branch: bool, + // The trail, an edge below this node. If this is a branch node, + // it is the rightmost child edge. It is an encoded NibbleSlice. + trail: Vec, + // If present, it is either a Leaf node or BranchWithValue. + value: Option, + // Only used if is_branch is true. The children that are already + // constructed. The last child currently being constructed is not in here. + children: Vec<(u8, MemTrieNodeId)>, + // Only used for extension nodes; the child that is already constructed. + child: Option, +} + +impl TrieConstructionSegment { + /// Prepares a segment that represents a branch node, possibly with value. + fn new_branch(initial_trail: Vec, value: Option) -> Self { + Self { is_branch: true, trail: initial_trail, value, children: Vec::new(), child: None } + } + + /// Prepares a segment that represents an extension node. + fn new_extension(trail: Vec) -> Self { + let nibbles = NibbleSlice::from_encoded(&trail); + assert!(!nibbles.1); // nibble slice is not leaf + assert!(!nibbles.0.is_empty()); // extension nibbles cannot be empty + Self { is_branch: false, trail, value: None, children: Vec::new(), child: None } + } + + /// Prepares a segment that represents a leaf node. + fn new_leaf(trail: Vec, value: FlatStateValue) -> Self { + let nibbles = NibbleSlice::from_encoded(&trail); + assert!(nibbles.1); + Self { is_branch: false, trail, value: Some(value), children: Vec::new(), child: None } + } + + fn is_leaf(&self) -> bool { + self.value.is_some() && !self.is_branch + } + + fn into_node(self, arena: &mut Arena) -> MemTrieNodeId { + let input_node = if self.is_branch { + assert!(!self.children.is_empty()); + assert!(self.child.is_none()); + let mut children = [None; 16]; + for (i, child) in self.children.into_iter() { + children[i as usize] = Some(child); + } + if let Some(value) = self.value { + InputMemTrieNode::BranchWithValue { children, value } + } else { + InputMemTrieNode::Branch { children } + } + } else if let Some(value) = self.value { + assert!(self.child.is_none()); + assert!(self.children.is_empty()); + InputMemTrieNode::Leaf { value, extension: self.trail.into_boxed_slice() } + } else { + assert!(self.child.is_some()); + assert!(self.children.is_empty()); + InputMemTrieNode::Extension { + extension: self.trail.into_boxed_slice(), + child: self.child.unwrap(), + } + }; + MemTrieNodeId::new(arena, input_node) + } +} + +impl<'a> TrieConstructor<'a> { + pub fn new(arena: &'a mut Arena) -> Self { + Self { arena, segments: vec![] } + } + + /// Encodes the bottom-most segment into a node, and pops it off the stack. + fn pop_segment(&mut self) { + let segment = self.segments.pop().unwrap(); + let node = segment.into_node(self.arena); + let parent = self.segments.last_mut().unwrap(); + if parent.is_branch { + parent.children.push((NibbleSlice::from_encoded(&parent.trail).0.at(0), node)); + } else { + assert!(parent.child.is_none()); + parent.child = Some(node); + } + } + + /// Adds a leaf to the trie. The key must be greater than all previous keys + /// inserted. + pub fn add_leaf(&mut self, key: &[u8], value: FlatStateValue) { + let mut nibbles = NibbleSlice::new(key); + let mut i = 0; + // We'll go down the segments to find where our nibbles deviate. + // If the deviation happens in the middle of a segment, we would split + // that segment. Then, we pop off any old segments and push new segments + // past the deviation point. + while i < self.segments.len() { + // It's not possible to exhaust the nibbles we're inserting while + // we're iterating through the segments, because that would mean + // this new key is a prefix of the previous key, which violates the + // assumed key ordering. + assert!(!nibbles.is_empty()); + + let segment = &self.segments[i]; + let (extension_nibbles, _) = NibbleSlice::from_encoded(&segment.trail); + let common_prefix_len = nibbles.common_prefix(&extension_nibbles); + if common_prefix_len == extension_nibbles.len() { + // This segment entirely matches a prefix of the nibbles we have + // so we continue matching. + nibbles = nibbles.mid(common_prefix_len); + i += 1; + continue; + } + + // At this point, we have a deviation somewhere in this segment. + // We can already pop off all the extra segments below it, as they + // have no chance to be relevant to the leaf we're inserting. + while i < self.segments.len() - 1 { + self.pop_segment(); + } + + // If the deviation happens in the middle of a segment, i.e. we have + // a non-zero common prefix, split that first. Such a segment is + // definitely an extension or leaf segment (since segment has more + // than 1 nibble). To do this split, we insert an extension segment + // on top for the common prefix, and shorten the current segment's + // trail. + if common_prefix_len > 0 { + let mut segment = self.segments.pop().unwrap(); + assert!(!segment.is_branch); + let (extension_nibbles, was_leaf) = NibbleSlice::from_encoded(&segment.trail); + assert_eq!(was_leaf, segment.is_leaf()); + assert_eq!(was_leaf, segment.child.is_none()); + + let top_segment = TrieConstructionSegment::new_extension( + extension_nibbles.encoded_leftmost(common_prefix_len, false).to_vec(), + ); + segment.trail = extension_nibbles.mid(common_prefix_len).encoded(was_leaf).to_vec(); + self.segments.push(top_segment); + self.segments.push(segment); + nibbles = nibbles.mid(common_prefix_len); + } + + // At this point, we know that the last segment deviates from our + // leaf and the deviation point is the beginning of the segment. + if self.segments.last().unwrap().is_branch { + // If the existing segment is a branch, we simply add another + // case of the branch. + self.segments.last_mut().unwrap().trail = + nibbles.encoded_leftmost(1, false).to_vec(); + nibbles = nibbles.mid(1); + break; + } else { + // Otherwise, the existing segment is an extension or leaf. We + // need to split the segment so that the first nibble is + // converted to a branch. + let mut segment = self.segments.pop().unwrap(); + let (extension_nibbles, was_leaf) = NibbleSlice::from_encoded(&segment.trail); + assert_eq!(was_leaf, segment.is_leaf()); + assert_eq!(was_leaf, segment.child.is_none()); + + // This is the branch node that handles the deviation. The trail + // for this branch node begins with the old trail's first nibble. + // We'll insert the new leaf later. + let mut top_segment = TrieConstructionSegment::new_branch( + extension_nibbles.encoded_leftmost(1, false).to_vec(), + None, + ); + if extension_nibbles.len() > 1 || was_leaf { + // If the old segment had more than 1 nibble, we need to + // keep that segment except without the first nibble. + // Similarly, if the old segment had just 1 nibble but was a + // leaf, we still need to keep the leaf but now with empty + // trail on the leaf. + segment.trail = extension_nibbles.mid(1).encoded(was_leaf).to_vec(); + self.segments.push(top_segment); + self.segments.push(segment); + // The bottom segment is no longer relevant to our new leaf, + // so pop that off. + self.pop_segment(); + } else { + // If the old segment was an extension with just 1 nibble, + // then that segment is no longer needed. We can add the old + // extension segment's child directly to the branch node. + top_segment.children.push((extension_nibbles.at(0), segment.child.unwrap())); + self.segments.push(top_segment); + } + // At this point we have popped the old case of the branch node, + // so we advance the branch node to point to our new leaf + // segment that we'll add below. + self.segments.last_mut().unwrap().trail = + nibbles.encoded_leftmost(1, false).to_vec(); + nibbles = nibbles.mid(1); + break; + } + } + // When we exit the loop, either we exited because we ran out of segments + // (in which case this leaf contains the previous leaf as a prefix) or we + // exited in the middle and we've just added a new branch. + if !self.segments.is_empty() && self.segments.last().unwrap().is_leaf() { + // This is the case where we ran out of segments. We definitely have + // some non-empty nibbles left or else we're trying insert a + // duplicate key. + assert!(!nibbles.is_empty()); + // In order for a leaf node to have another leaf below it, it needs + // to be converted to a branch node with value. + let segment = self.segments.pop().unwrap(); + let (extension_nibbles, was_leaf) = NibbleSlice::from_encoded(&segment.trail); + assert!(was_leaf); + if !extension_nibbles.is_empty() { + // If the original leaf node had an extension within it, we need + // to create an extension above the branch node. + let top_segment = TrieConstructionSegment::new_extension( + extension_nibbles.encoded(false).to_vec(), + ); + self.segments.push(top_segment); + } + // Now let's construct our branch node, and add our new leaf node below it. + let mid_segment = TrieConstructionSegment::new_branch( + nibbles.encoded_leftmost(1, false).to_vec(), + segment.value, + ); + let bottom_segment = + TrieConstructionSegment::new_leaf(nibbles.mid(1).encoded(true).to_vec(), value); + self.segments.push(mid_segment); + self.segments.push(bottom_segment); + } else { + // Otherwise we're at one branch of a branch node (or we're at root), + // so just append the leaf. + let segment = TrieConstructionSegment::new_leaf(nibbles.encoded(true).to_vec(), value); + self.segments.push(segment); + } + } + + /// Finishes the construction of the trie, returning the ID of the root + /// node. Note that the root node has a 0 refcount; the caller is + /// responsible for incrementing its refcount. + /// + /// None is returned iff add_leaf was never called. + pub fn finalize(mut self) -> Option { + while self.segments.len() > 1 { + self.pop_segment(); + } + self.segments.into_iter().next().map(|segment| segment.into_node(self.arena)) + } +} diff --git a/core/store/src/trie/mem/flexible_data/children.rs b/core/store/src/trie/mem/flexible_data/children.rs new file mode 100644 index 000000000..d1093deec --- /dev/null +++ b/core/store/src/trie/mem/flexible_data/children.rs @@ -0,0 +1,102 @@ +use std::mem::size_of; + +use borsh::{BorshDeserialize, BorshSerialize}; + +use super::encoding::BorshFixedSize; +use super::FlexibleDataHeader; +use crate::trie::mem::arena::{ArenaSlice, ArenaSliceMut}; +use crate::trie::mem::node::{MemTrieNodeId, MemTrieNodePtr}; +use crate::trie::Children; + +/// Flexibly-sized data header for a variable-sized list of children trie nodes. +/// The header contains a 16-bit mask of which children are present, and the +/// flexible part is one pointer for each present child. +#[derive(Clone, Copy, BorshSerialize, BorshDeserialize)] +pub struct EncodedChildrenHeader { + mask: u16, +} + +impl BorshFixedSize for EncodedChildrenHeader { + const SERIALIZED_SIZE: usize = std::mem::size_of::(); +} + +impl FlexibleDataHeader for EncodedChildrenHeader { + type InputData = [Option; 16]; + type View<'a> = ChildrenView<'a>; + + fn from_input(children: &[Option; 16]) -> EncodedChildrenHeader { + let mut mask = 0u16; + for i in 0..16 { + if children[i].is_some() { + mask |= 1 << i; + } + } + EncodedChildrenHeader { mask } + } + + fn flexible_data_length(&self) -> usize { + self.mask.count_ones() as usize * size_of::() + } + + fn encode_flexible_data( + &self, + children: [Option; 16], + target: &mut ArenaSliceMut<'_>, + ) { + let mut j = 0; + for (i, child) in children.into_iter().enumerate() { + if self.mask & (1 << i) != 0 { + target.write_pos_at(j, child.unwrap().pos); + j += size_of::(); + } else { + debug_assert!(child.is_none()); + } + } + } + + fn decode_flexible_data<'a>(&self, source: &ArenaSlice<'a>) -> ChildrenView<'a> { + ChildrenView { mask: self.mask, children: source.clone() } + } +} + +/// Efficient view of the encoded children data. +#[derive(Debug, Clone)] +pub struct ChildrenView<'a> { + mask: u16, + children: ArenaSlice<'a>, +} + +impl<'a> ChildrenView<'a> { + /// Gets the child at a specific index (0 to 15). + pub fn get(&self, i: usize) -> Option> { + assert!(i < 16); + let bit = 1u16 << (i as u16); + if self.mask & bit == 0 { + None + } else { + let lower_mask = self.mask & (bit - 1); + let index = lower_mask.count_ones() as usize; + Some(MemTrieNodePtr::from(self.children.read_ptr_at(index * size_of::()))) + } + } + + /// Converts to a Children struct used in RawTrieNode. + pub fn to_children(&self) -> Children { + let mut children = Children::default(); + let mut j = 0; + for i in 0..16 { + if self.mask & (1 << i) != 0 { + let child = MemTrieNodePtr::from(self.children.read_ptr_at(j)); + children.0[i] = Some(child.view().node_hash()); + j += size_of::(); + } + } + children + } + + /// Iterates only through existing children. + pub fn iter<'b>(&'b self) -> impl Iterator> + 'b { + (0..self.mask.count_ones() as usize) + .map(|i| MemTrieNodePtr::from(self.children.read_ptr_at(i * size_of::()))) + } +} diff --git a/core/store/src/trie/mem/flexible_data/encoding.rs b/core/store/src/trie/mem/flexible_data/encoding.rs new file mode 100644 index 000000000..db85f8a0d --- /dev/null +++ b/core/store/src/trie/mem/flexible_data/encoding.rs @@ -0,0 +1,140 @@ +use crate::trie::mem::arena::{Arena, ArenaPtr, ArenaPtrMut, ArenaSliceMut}; +use borsh::{BorshDeserialize, BorshSerialize}; +use std::io::Write; + +use super::FlexibleDataHeader; + +/// Implementing this trait indicates that the type can be borsh serialized and +/// the length of the serialization is a constant. +/// +/// TODO: it would be nice to generate this, or test the correctness of the +/// `SERIALIZED_SIZE` specification. For now, we rely on higher-level testing. +pub trait BorshFixedSize { + const SERIALIZED_SIZE: usize; +} + +/// Facilitates allocation and encoding of flexibly-sized data. +pub struct RawEncoder<'a> { + data: ArenaSliceMut<'a>, + pos: usize, +} + +// To make it easier to use borsh serialization. +impl<'a> Write for RawEncoder<'a> { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.data.raw_slice_mut()[self.pos..self.pos + buf.len()].copy_from_slice(buf); + self.pos += buf.len(); + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +impl<'a> RawEncoder<'a> { + /// Creates a new arena allocation of the given size, returning an encoder + /// that can be used to initialize the allocated memory. + pub fn new(arena: &'a mut Arena, n: usize) -> RawEncoder<'a> { + let data = arena.alloc(n); + RawEncoder { data, pos: 0 } + } + + /// Encodes the given fixed-size field to the current encoder position, + /// and then advances the position by the encoded size of the field. + pub fn encode(&mut self, data: T) { + data.serialize(self).unwrap(); + } + + /// Encodes the given flexibly-sized part of the data to the current + /// encoder position, and then advances the position by the size of the + /// flexibly-sized part, as returned by `header.flexible_data_length()`. + /// Note that the header itself is NOT encoded; only the flexible part is. + /// The header is expected to have been encoded earlier. + pub fn encode_flexible(&mut self, header: &T, data: T::InputData) { + let length = header.flexible_data_length(); + header.encode_flexible_data(data, &mut self.data.subslice_mut(self.pos, length)); + self.pos += length; + } + + /// Finishes the encoding process and returns a pointer to the allocated + /// memory. The caller is responsible for freeing the pointer later. + pub fn finish(self) -> ArenaSliceMut<'a> { + assert_eq!(self.pos, self.data.len()); + self.data + } +} + +/// Facilitates the decoding of flexibly-sized data. +pub struct RawDecoder<'a> { + data: ArenaPtr<'a>, + pos: usize, +} + +impl<'a> RawDecoder<'a> { + /// Starts decoding from the given memory position. The position should be + /// the beginning of an earlier slice returned by `RawEncoder::finish`. + pub fn new(data: ArenaPtr<'a>) -> RawDecoder<'a> { + RawDecoder { data, pos: 0 } + } + + /// Decodes a fixed-size field at the current decoder position, and then + /// advances the position by the size of the field. + pub fn decode(&mut self) -> T { + let slice = self.data.slice(self.pos, T::SERIALIZED_SIZE); + let result = T::try_from_slice(slice.raw_slice()).unwrap(); + self.pos += T::SERIALIZED_SIZE; + result + } + + /// Decodes a fixed-sized field at the current position, but does not + /// advance the position. + pub fn peek(&mut self) -> T { + let slice = self.data.slice(self.pos, T::SERIALIZED_SIZE); + T::try_from_slice(slice.raw_slice()).unwrap() + } + + /// Decodes a flexibly-sized part of the data at the current position, + /// and then advances the position by the size of the flexibly-sized part, + /// as returned by `header.flexible_data_length()`. + pub fn decode_flexible(&mut self, header: &T) -> T::View<'a> { + let length = header.flexible_data_length(); + let view = header.decode_flexible_data(&self.data.slice(self.pos, length)); + self.pos += length; + view + } +} + +/// Provides ability to decode, but also to overwrite some data. +pub struct RawDecoderMut<'a> { + data: ArenaPtrMut<'a>, + pos: usize, +} + +impl<'a> RawDecoderMut<'a> { + pub fn new(data: ArenaPtrMut<'a>) -> RawDecoderMut<'a> { + RawDecoderMut { data, pos: 0 } + } + + /// Same with `RawDecoder::decode`. + pub fn decode(&mut self) -> T { + let slice = self.data.slice(self.pos, T::SERIALIZED_SIZE); + let result = T::try_from_slice(slice.raw_slice()).unwrap(); + self.pos += T::SERIALIZED_SIZE; + result + } + + /// Same with `RawDecoder::peek`. + pub fn peek(&mut self) -> T { + let slice = self.data.slice(self.pos, T::SERIALIZED_SIZE); + T::try_from_slice(slice.raw_slice()).unwrap() + } + + /// Overwrites the data at the current position with the given data, + /// and advances the position by the size of the data. + pub fn overwrite(&mut self, data: T) { + let mut slice = self.data.slice_mut(self.pos, T::SERIALIZED_SIZE); + data.serialize(&mut slice.raw_slice_mut()).unwrap(); + self.pos += T::SERIALIZED_SIZE; + } +} diff --git a/core/store/src/trie/mem/flexible_data/extension.rs b/core/store/src/trie/mem/flexible_data/extension.rs new file mode 100644 index 000000000..3c67fc926 --- /dev/null +++ b/core/store/src/trie/mem/flexible_data/extension.rs @@ -0,0 +1,36 @@ +use crate::trie::mem::arena::{ArenaSlice, ArenaSliceMut}; + +use super::encoding::BorshFixedSize; +use super::FlexibleDataHeader; +use borsh::{BorshDeserialize, BorshSerialize}; + +/// Flexibly-sized data header for a trie extension path (which is simply +/// a byte array). +#[derive(Clone, Copy, BorshSerialize, BorshDeserialize)] +pub struct EncodedExtensionHeader { + length: u16, +} + +impl BorshFixedSize for EncodedExtensionHeader { + const SERIALIZED_SIZE: usize = std::mem::size_of::(); +} + +impl FlexibleDataHeader for EncodedExtensionHeader { + type InputData = Box<[u8]>; + type View<'a> = ArenaSlice<'a>; + fn from_input(extension: &Box<[u8]>) -> EncodedExtensionHeader { + EncodedExtensionHeader { length: extension.len() as u16 } + } + + fn flexible_data_length(&self) -> usize { + self.length as usize + } + + fn encode_flexible_data(&self, extension: Box<[u8]>, target: &mut ArenaSliceMut<'_>) { + target.raw_slice_mut().copy_from_slice(&extension); + } + + fn decode_flexible_data<'a>(&self, source: &ArenaSlice<'a>) -> ArenaSlice<'a> { + source.clone() + } +} diff --git a/core/store/src/trie/mem/flexible_data/mod.rs b/core/store/src/trie/mem/flexible_data/mod.rs new file mode 100644 index 000000000..58fb2b032 --- /dev/null +++ b/core/store/src/trie/mem/flexible_data/mod.rs @@ -0,0 +1,58 @@ +use super::arena::{ArenaSlice, ArenaSliceMut}; + +pub mod children; +pub mod encoding; +pub mod extension; +pub mod value; + +/// This trait simplifies our programming against flexibly-sized structures. +/// A flexibly-sized structure is one whose memory layout depends on runtime +/// values. For example, we may use a flexibly-sized structure to encode two +/// variable-sized strings, like this: +/// +/// 0 4 8 8+str1len 8+str1len+str2len +/// | str1 length | str2 length | str1 | str2 | +/// +/// Why do we do this? Because this is a lot more memory-efficient than the +/// alternative of storing two heap-allocated Strings; it is a single memory +/// allocation rather than three, and it saves the need to encode two pointers. +/// +/// In this string example, these two strings would each define a header struct, +/// say EncodedStringHeader, that implements FlexibleDataHeader. The struct +/// itself only contains the fixed-size part, i.e. the length of the string. +/// Then, we implement encode_flexible_data and decode_flexible_data to specify +/// how the flexibly-sized part of the data is encoded and decoded. These, of +/// course need to be consistent with each other. +/// +/// This trait allows us to then encode and decode a flexibly-sized structure +/// with multiple flexibly-sized parts with relative ease. +pub trait FlexibleDataHeader { + /// The type of the original form of data to be used for encoding. + type InputData; + /// The type of a view of the decoded data, which may reference the memory + /// that we are decoding from, and therefore having a lifetime. + type View<'a>; + + /// Derives the header (fixed-size part) from the original data. + fn from_input(data: &Self::InputData) -> Self; + + /// Calculates the length of the flexibly-sized part of the data. + /// This is used to allocate the right amount of memory for the containing + /// flexibly-sized structure. + fn flexible_data_length(&self) -> usize; + + /// Encodes the flexibly-sized part of the data into the given memory + /// slice. This function must be implemented in a way that writes + /// exactly `self.flexible_data_length()` bytes to the given memory + /// slice. The caller must ensure that the memory slice is large enough. + fn encode_flexible_data(&self, data: Self::InputData, target: &mut ArenaSliceMut<'_>); + + /// Decodes the flexibly-sized part of the data from the given memory + /// slice. This function must be implemented in a consistent manner + /// with `encode_flexible_data`. It, of course, must only read + /// `self.flexible_data_length()` bytes from the given memory slice, + /// and the caller must ensure that the memory slice is the same one + /// that was used to encode the data. The returned View has the same + /// lifetime as the memory slice, and so may reference data from it. + fn decode_flexible_data<'a>(&self, source: &ArenaSlice<'a>) -> Self::View<'a>; +} diff --git a/core/store/src/trie/mem/flexible_data/value.rs b/core/store/src/trie/mem/flexible_data/value.rs new file mode 100644 index 000000000..68ff9f56c --- /dev/null +++ b/core/store/src/trie/mem/flexible_data/value.rs @@ -0,0 +1,111 @@ +use crate::trie::mem::arena::{ArenaSlice, ArenaSliceMut}; + +use super::encoding::BorshFixedSize; +use super::FlexibleDataHeader; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::state::{FlatStateValue, ValueRef}; + +/// Flexibly-sized data header for a trie value, representing either an inline +/// value, or a reference to a value stored in the State column. +/// +/// The flexible part of the data is either the inlined value as a byte array, +/// or a CryptoHash representing the reference hash. +#[derive(Clone, Copy, BorshSerialize, BorshDeserialize)] +pub struct EncodedValueHeader { + // The high bit is 1 if the value is inlined, 0 if it is a reference. + // The lower bits are the length of the value. + length_and_inlined: u32, +} + +impl BorshFixedSize for EncodedValueHeader { + const SERIALIZED_SIZE: usize = std::mem::size_of::(); +} + +impl EncodedValueHeader { + const INLINED_MASK: u32 = 0x80000000; + + fn decode(&self) -> (u32, bool) { + ( + self.length_and_inlined & !Self::INLINED_MASK, + self.length_and_inlined & Self::INLINED_MASK != 0, + ) + } +} + +impl FlexibleDataHeader for EncodedValueHeader { + type InputData = FlatStateValue; + type View<'a> = ValueView<'a>; + + fn from_input(value: &FlatStateValue) -> Self { + match value { + FlatStateValue::Ref(value_ref) => { + debug_assert!(value_ref.length < Self::INLINED_MASK); + EncodedValueHeader { length_and_inlined: value_ref.length } + } + FlatStateValue::Inlined(v) => { + assert!(v.len() < Self::INLINED_MASK as usize); + EncodedValueHeader { length_and_inlined: Self::INLINED_MASK | v.len() as u32 } + } + } + } + + fn flexible_data_length(&self) -> usize { + let (length, inlined) = self.decode(); + if inlined { + length as usize + } else { + std::mem::size_of::() + } + } + + fn encode_flexible_data(&self, value: FlatStateValue, target: &mut ArenaSliceMut<'_>) { + let (length, inlined) = self.decode(); + match value { + FlatStateValue::Ref(value_ref) => { + assert!(!inlined); + assert_eq!(length, value_ref.length); + target.raw_slice_mut().copy_from_slice(&value_ref.hash.0); + } + FlatStateValue::Inlined(v) => { + assert!(inlined); + assert_eq!(length, v.len() as u32); + target.raw_slice_mut().copy_from_slice(&v); + } + } + } + + fn decode_flexible_data<'a>(&self, source: &ArenaSlice<'a>) -> ValueView<'a> { + let (length, inlined) = self.decode(); + if inlined { + ValueView::Inlined(source.clone()) + } else { + ValueView::Ref { length, hash: CryptoHash::try_from_slice(source.raw_slice()).unwrap() } + } + } +} + +// Efficient view of the encoded value. +#[derive(Debug, Clone)] +pub enum ValueView<'a> { + Ref { length: u32, hash: CryptoHash }, + Inlined(ArenaSlice<'a>), +} + +impl<'a> ValueView<'a> { + pub fn to_flat_value(&self) -> FlatStateValue { + match self { + Self::Ref { length, hash } => { + FlatStateValue::Ref(ValueRef { length: *length, hash: *hash }) + } + Self::Inlined(data) => FlatStateValue::Inlined(data.raw_slice().to_vec()), + } + } + + pub fn len(&self) -> usize { + match self { + Self::Ref { length, .. } => *length as usize, + Self::Inlined(data) => data.len(), + } + } +} diff --git a/core/store/src/trie/mem/loading.rs b/core/store/src/trie/mem/loading.rs new file mode 100644 index 000000000..1d1829321 --- /dev/null +++ b/core/store/src/trie/mem/loading.rs @@ -0,0 +1,538 @@ +use super::node::MemTrieNodeId; +use super::MemTries; +use crate::flat::store_helper::{ + decode_flat_state_db_key, get_all_deltas_metadata, get_delta_changes, get_flat_storage_status, +}; +use crate::flat::{FlatStorageError, FlatStorageStatus}; +use crate::trie::mem::construction::TrieConstructor; +use crate::trie::mem::updating::apply_memtrie_changes; +use crate::{DBCol, Store}; +use unc_primitives::errors::StorageError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::{get_block_shard_uid, ShardUId}; +use unc_primitives::state::FlatStateValue; +use unc_primitives::types::chunk_extra::ChunkExtra; +use unc_primitives::types::BlockHeight; +use rayon::prelude::{IntoParallelIterator, ParallelIterator}; +use std::collections::BTreeSet; +use std::time::Instant; +use tracing::{debug, info}; + +/// Loads a trie from the FlatState column. The returned `MemTries` contains +/// exactly one trie root. +pub fn load_trie_from_flat_state( + store: &Store, + shard_uid: ShardUId, + state_root: CryptoHash, + block_height: BlockHeight, +) -> Result { + let mut tries = MemTries::new(shard_uid); + + tries.construct_root(block_height, |arena| -> Result, StorageError> { + info!(target: "memtrie", shard_uid=%shard_uid, "Loading trie from flat state..."); + let load_start = Instant::now(); + let mut recon = TrieConstructor::new(arena); + let mut num_keys_loaded = 0; + for item in store + .iter_prefix_ser::(DBCol::FlatState, &borsh::to_vec(&shard_uid).unwrap()) + { + let (key, value) = item.map_err(|err| { + FlatStorageError::StorageInternalError(format!("Error iterating over FlatState: {err}")) + })?; + let (_, key) = decode_flat_state_db_key(&key).map_err(|err| { + FlatStorageError::StorageInternalError(format!( + "invalid FlatState key format: {err}" + ))})?; + recon.add_leaf(&key, value); + num_keys_loaded += 1; + if num_keys_loaded % 1000000 == 0 { + debug!( + target: "memtrie", + %shard_uid, + "Loaded {} keys, current key: {}", + num_keys_loaded, + hex::encode(&key) + ); + } + } + let root_id = match recon.finalize() { + Some(root_id) => root_id, + None => { + info!(target: "memtrie", shard_uid=%shard_uid, "No keys loaded, trie is empty"); + return Ok(None); + } + }; + + debug!( + target: "memtrie", + %shard_uid, + "Loaded {} keys; computing hash and memory usage...", + num_keys_loaded + ); + let mut subtrees = Vec::new(); + root_id.as_ptr_mut(arena.memory_mut()).take_small_subtrees(1024 * 1024, &mut subtrees); + subtrees.into_par_iter().for_each(|mut subtree| { + subtree.compute_hash_recursively(); + }); + root_id.as_ptr_mut(arena.memory_mut()).compute_hash_recursively(); + info!(target: "memtrie", shard_uid=%shard_uid, "Done loading trie from flat state, took {:?}", load_start.elapsed()); + + let root = root_id.as_ptr(arena.memory()); + assert_eq!( + root.view().node_hash(), state_root, + "In-memory trie for shard {} has incorrect state root", shard_uid); + Ok(Some(root.id())) + })?; + Ok(tries) +} + +fn get_state_root( + store: &Store, + block_hash: CryptoHash, + shard_uid: ShardUId, +) -> Result { + let chunk_extra = store + .get_ser::(DBCol::ChunkExtra, &get_block_shard_uid(&block_hash, &shard_uid)) + .map_err(|err| { + StorageError::StorageInconsistentState(format!( + "Cannot fetch ChunkExtra for block {} in shard {}: {:?}", + block_hash, shard_uid, err + )) + })? + .ok_or_else(|| { + StorageError::StorageInconsistentState(format!( + "No ChunkExtra for block {} in shard {}", + block_hash, shard_uid + )) + })?; + Ok(*chunk_extra.state_root()) +} + +/// Constructs in-memory tries for the given shard, so that they represent the +/// same information as the flat storage, including the final state and the +/// deltas. The returned tries would contain a root for each block that the +/// flat storage currently has, i.e. one for the final block, and one for each +/// block that flat storage has a delta for, possibly in more than one fork. +pub fn load_trie_from_flat_state_and_delta( + store: &Store, + shard_uid: ShardUId, +) -> Result { + debug!(target: "memtrie", %shard_uid, "Loading base trie from flat state..."); + let flat_head = match get_flat_storage_status(&store, shard_uid)? { + FlatStorageStatus::Ready(status) => status.flat_head, + other => { + return Err(StorageError::MemTrieLoadingError(format!( + "Cannot load memtries when flat storage is not ready for shard {}, actual status: {:?}", + shard_uid, other + ))); + } + }; + + let mut mem_tries = load_trie_from_flat_state( + &store, + shard_uid, + get_state_root(store, flat_head.hash, shard_uid)?, + flat_head.height, + ) + .unwrap(); + + debug!(target: "memtrie", %shard_uid, "Loading flat state deltas..."); + // We load the deltas in order of height, so that we always have the previous state root + // already loaded. + let mut sorted_deltas: BTreeSet<(BlockHeight, CryptoHash, CryptoHash)> = Default::default(); + for delta in get_all_deltas_metadata(&store, shard_uid).unwrap() { + sorted_deltas.insert((delta.block.height, delta.block.hash, delta.block.prev_hash)); + } + + debug!(target: "memtrie", %shard_uid, "{} deltas to apply", sorted_deltas.len()); + for (height, hash, prev_hash) in sorted_deltas.into_iter() { + let delta = get_delta_changes(&store, shard_uid, hash).unwrap(); + if let Some(changes) = delta { + let old_state_root = get_state_root(store, prev_hash, shard_uid)?; + let new_state_root = get_state_root(store, hash, shard_uid)?; + + let mut trie_update = mem_tries.update(old_state_root, false)?; + for (key, value) in changes.0 { + match value { + Some(value) => { + trie_update.insert_memtrie_only(&key, value); + } + None => trie_update.delete(&key), + }; + } + + let mem_trie_changes = trie_update.to_mem_trie_changes_only(); + let new_root_after_apply = + apply_memtrie_changes(&mut mem_tries, &mem_trie_changes, height); + assert_eq!(new_root_after_apply, new_state_root); + } + debug!(target: "memtrie", %shard_uid, "Applied memtrie changes for height {}", height); + } + + debug!(target: "memtrie", %shard_uid, "Done loading memtries for shard"); + Ok(mem_tries) +} + +#[cfg(test)] +mod tests { + use super::load_trie_from_flat_state_and_delta; + use crate::flat::test_utils::MockChain; + use crate::flat::{store_helper, BlockInfo, FlatStorageReadyStatus, FlatStorageStatus}; + use crate::test_utils::{ + create_test_store, simplify_changes, test_populate_flat_storage, test_populate_trie, + TestTriesBuilder, + }; + use crate::trie::mem::loading::load_trie_from_flat_state; + use crate::trie::mem::lookup::memtrie_lookup; + use crate::trie::OptimizedValueRef; + use crate::{DBCol, KeyLookupMode, NibbleSlice, ShardTries, Store, Trie, TrieUpdate}; + use unc_primitives::hash::CryptoHash; + use unc_primitives::shard_layout::{get_block_shard_uid, ShardUId}; + use unc_primitives::state::FlatStateValue; + use unc_primitives::trie_key::TrieKey; + use unc_primitives::types::chunk_extra::ChunkExtra; + use unc_primitives::types::StateChangeCause; + use rand::rngs::StdRng; + use rand::{Rng, SeedableRng}; + + fn check(keys: Vec>) { + let shard_tries = TestTriesBuilder::new().with_flat_storage().build(); + let shard_uid = ShardUId::single_shard(); + let changes = keys.iter().map(|key| (key.to_vec(), Some(key.to_vec()))).collect::>(); + let changes = simplify_changes(&changes); + test_populate_flat_storage( + &shard_tries, + shard_uid, + &CryptoHash::default(), + &CryptoHash::default(), + &changes, + ); + let state_root = test_populate_trie(&shard_tries, &Trie::EMPTY_ROOT, shard_uid, changes); + + eprintln!("Trie and flat storage populated"); + let in_memory_trie = + load_trie_from_flat_state(&shard_tries.get_store(), shard_uid, state_root, 123) + .unwrap(); + eprintln!("In memory trie loaded"); + + if keys.is_empty() { + assert_eq!(in_memory_trie.num_roots(), 0); + return; + } + + let trie_update = TrieUpdate::new(shard_tries.get_trie_with_block_hash_for_shard( + shard_uid, + state_root, + &CryptoHash::default(), + false, + )); + trie_update.set_trie_cache_mode(unc_primitives::types::TrieCacheMode::CachingChunk); + let trie = trie_update.trie(); + let root = in_memory_trie.get_root(&state_root).unwrap(); + + // Check access to each key to make sure the in-memory trie is consistent with + // real trie. Check non-existent keys too. + for key in keys.iter().chain([b"not in trie".to_vec()].iter()) { + let mut nodes_accessed = Vec::new(); + let actual_value_ref = memtrie_lookup(root, key, Some(&mut nodes_accessed)) + .map(OptimizedValueRef::from_flat_value); + let expected_value_ref = + trie.get_optimized_ref(key, KeyLookupMode::FlatStorage).unwrap(); + assert_eq!(actual_value_ref, expected_value_ref, "{:?}", NibbleSlice::new(key)); + + // Do another access with the trie to see how many nodes we're supposed to + // have accessed. + let temp_trie = shard_tries.get_trie_for_shard(shard_uid, state_root); + temp_trie.get_optimized_ref(key, crate::KeyLookupMode::Trie).unwrap(); + assert_eq!( + temp_trie.get_trie_nodes_count().db_reads, + nodes_accessed.len() as u64, + "Number of accessed nodes does not equal number of trie nodes along the way" + ); + + // Check that the accessed nodes are consistent with those from disk. + for (node_hash, serialized_node) in nodes_accessed { + let expected_serialized_node = + trie.internal_retrieve_trie_node(&node_hash, false).unwrap(); + assert_eq!(expected_serialized_node, serialized_node); + } + } + } + + fn check_random(max_key_len: usize, max_keys_count: usize, test_count: usize) { + let mut rng = StdRng::seed_from_u64(42); + for _ in 0..test_count { + let key_cnt = rng.gen_range(1..=max_keys_count); + let mut keys = Vec::new(); + for _ in 0..key_cnt { + let mut key = Vec::new(); + let key_len = rng.gen_range(0..=max_key_len); + for _ in 0..key_len { + let byte: u8 = rng.gen(); + key.push(byte); + } + keys.push(key); + } + check(keys); + } + } + + fn nibbles(hex: &str) -> Vec { + if hex == "_" { + return vec![]; + } + assert!(hex.len() % 2 == 0); + hex::decode(hex).unwrap() + } + + fn all_nibbles(hexes: &str) -> Vec> { + hexes.split_whitespace().map(|x| nibbles(x)).collect() + } + + #[test] + fn test_memtrie_empty() { + check(vec![]); + } + + #[test] + fn test_memtrie_root_is_leaf() { + check(all_nibbles("_")); + check(all_nibbles("00")); + check(all_nibbles("01")); + check(all_nibbles("ff")); + check(all_nibbles("0123456789abcdef")); + } + + #[test] + fn test_memtrie_root_is_extension() { + check(all_nibbles("1234 13 14")); + check(all_nibbles("12345678 1234abcd")); + } + + #[test] + fn test_memtrie_root_is_branch() { + check(all_nibbles("11 22")); + check(all_nibbles("12345678 22345678 32345678")); + check(all_nibbles("11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff")); + } + + #[test] + fn test_memtrie_root_is_branch_with_value() { + check(all_nibbles("_ 11")); + } + + #[test] + fn test_memtrie_prefix_patterns() { + check(all_nibbles("10 21 2210 2221 222210 222221 22222210 22222221")); + check(all_nibbles("11111112 11111120 111112 111120 1112 1120 12 20")); + check(all_nibbles("11 1111 111111 11111111 1111111111 111111111111")); + check(all_nibbles("_ 11 1111 111111 11111111 1111111111 111111111111")); + } + + #[test] + fn test_full_16ary_trees() { + check(all_nibbles( + " + 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f + 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f + 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f + 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f + 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f + 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f + 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f + 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f + 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f + 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f + a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af + b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf + c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf + d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df + e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef + f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff + ", + )) + } + + #[test] + fn test_memtrie_rand_small() { + check_random(3, 20, 10); + } + + #[test] + fn test_memtrie_rand_many_keys() { + check_random(5, 1000, 10); + } + + #[test] + fn test_memtrie_rand_long_keys() { + check_random(20, 100, 10); + } + + #[test] + fn test_memtrie_rand_long_long_keys() { + check_random(1000, 1000, 1); + } + + #[test] + fn test_memtrie_rand_large_data() { + check_random(32, 100000, 1); + } + + #[test] + fn test_memtrie_load_with_delta() { + let test_key = TrieKey::ContractData { + account_id: "test_account".parse().unwrap(), + key: b"test_key".to_vec(), + }; + let test_val0 = b"test_val0".to_vec(); + let test_val1 = b"test_val1".to_vec(); + let test_val2 = b"test_val2".to_vec(); + let test_val3 = b"test_val3".to_vec(); + let test_val4 = b"test_val4".to_vec(); + + // A chain with two forks. + // 0 |-> 1 -> 3 + // --> 2 -> 4 + let chain = MockChain::chain_with_two_forks(5); + let store = create_test_store(); + let shard_tries = TestTriesBuilder::new().with_store(store.clone()).build(); + let shard_uid = ShardUId { version: 1, shard_id: 1 }; + + // Populate the initial flat storage state at block 0. + let mut store_update = shard_tries.store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head: chain.get_block(0) }), + ); + store_helper::set_flat_state_value( + &mut store_update, + shard_uid, + test_key.to_vec(), + Some(FlatStateValue::inlined(&test_val0)), + ); + store_update.commit().unwrap(); + + // Populate the initial trie at block 0 too. + let state_root_0 = test_populate_trie( + &shard_tries, + &Trie::EMPTY_ROOT, + shard_uid, + vec![(test_key.to_vec(), Some(test_val0.clone()))], + ); + write_chunk_extra(&store, chain.get_block(0).hash, shard_uid, state_root_0); + + // Apply four changes to the trie, and for each, a flat storage delta. + let state_root_1 = apply_trie_changes( + &shard_tries, + shard_uid, + state_root_0, + chain.get_block(1), + vec![(test_key.clone(), test_val1.clone())], + ); + write_chunk_extra(&store, chain.get_block(1).hash, shard_uid, state_root_1); + + let state_root_2 = apply_trie_changes( + &shard_tries, + shard_uid, + state_root_0, + chain.get_block(2), + vec![(test_key.clone(), test_val2.clone())], + ); + write_chunk_extra(&store, chain.get_block(2).hash, shard_uid, state_root_2); + + let state_root_3 = apply_trie_changes( + &shard_tries, + shard_uid, + state_root_1, + chain.get_block(3), + vec![(test_key.clone(), test_val3.clone())], + ); + write_chunk_extra(&store, chain.get_block(3).hash, shard_uid, state_root_3); + + let state_root_4 = apply_trie_changes( + &shard_tries, + shard_uid, + state_root_2, + chain.get_block(4), + vec![(test_key.clone(), test_val4.clone())], + ); + write_chunk_extra(&store, chain.get_block(4).hash, shard_uid, state_root_4); + + // Load into memory. It should load the base flat state (block 0), plus all + // four deltas. We'll check against the state roots at each block; they should + // all exist in the loaded memtrie. + let mem_tries = load_trie_from_flat_state_and_delta(&store, shard_uid).unwrap(); + + assert_eq!( + memtrie_lookup(mem_tries.get_root(&state_root_0).unwrap(), &test_key.to_vec(), None), + Some(FlatStateValue::inlined(&test_val0)) + ); + assert_eq!( + memtrie_lookup(mem_tries.get_root(&state_root_1).unwrap(), &test_key.to_vec(), None), + Some(FlatStateValue::inlined(&test_val1)) + ); + assert_eq!( + memtrie_lookup(mem_tries.get_root(&state_root_2).unwrap(), &test_key.to_vec(), None), + Some(FlatStateValue::inlined(&test_val2)) + ); + assert_eq!( + memtrie_lookup(mem_tries.get_root(&state_root_3).unwrap(), &test_key.to_vec(), None), + Some(FlatStateValue::inlined(&test_val3)) + ); + assert_eq!( + memtrie_lookup(mem_tries.get_root(&state_root_4).unwrap(), &test_key.to_vec(), None), + Some(FlatStateValue::inlined(&test_val4)) + ); + } + + /// Makes the given changes to both the trie and flat storage. + fn apply_trie_changes( + tries: &ShardTries, + shard_uid: ShardUId, + old_state_root: CryptoHash, + block: BlockInfo, + changes: Vec<(TrieKey, Vec)>, + ) -> CryptoHash { + let mut trie_update = tries.new_trie_update(shard_uid, old_state_root); + for (key, value) in changes { + trie_update.set(key, value); + } + trie_update + .commit(StateChangeCause::TransactionProcessing { tx_hash: CryptoHash::default() }); + let (_, trie_changes, state_changes) = trie_update.finalize().unwrap(); + let mut store_update = tries.store_update(); + tries.apply_insertions(&trie_changes, shard_uid, &mut store_update); + store_update.merge( + tries + .get_flat_storage_manager() + .save_flat_state_changes( + block.hash, + block.prev_hash, + block.height, + shard_uid, + &state_changes, + ) + .unwrap(), + ); + store_update.commit().unwrap(); + + trie_changes.new_root + } + + /// Writes the state root into chunk extra for the given block and shard, + /// because the loading code needs it to look up the state roots. + fn write_chunk_extra( + store: &Store, + block_hash: CryptoHash, + shard_uid: ShardUId, + state_root: CryptoHash, + ) { + let chunk_extra = ChunkExtra::new(&state_root, CryptoHash::default(), Vec::new(), 0, 0, 0); + let mut store_update = store.store_update(); + store_update + .set_ser(DBCol::ChunkExtra, &get_block_shard_uid(&block_hash, &shard_uid), &chunk_extra) + .unwrap(); + store_update.commit().unwrap(); + } +} diff --git a/core/store/src/trie/mem/lookup.rs b/core/store/src/trie/mem/lookup.rs new file mode 100644 index 000000000..a37d6c38c --- /dev/null +++ b/core/store/src/trie/mem/lookup.rs @@ -0,0 +1,72 @@ +use std::sync::Arc; + +use super::metrics::MEM_TRIE_NUM_LOOKUPS; +use super::node::{MemTrieNodePtr, MemTrieNodeView}; +use crate::NibbleSlice; +use unc_primitives::hash::CryptoHash; +use unc_primitives::state::FlatStateValue; + +/// Performs a lookup in an in-memory trie, while taking care of cache +/// accounting for gas calculation purposes. +/// +/// If `nodes_accessed` is provided, each trie node along the lookup path +/// will be added to the vector as (node hash, serialized `RawTrieNodeWithSize`). +/// Even if the key is not found, the nodes that were accessed to make that +/// determination will be added to the vector. +pub fn memtrie_lookup( + root: MemTrieNodePtr<'_>, + key: &[u8], + mut nodes_accessed: Option<&mut Vec<(CryptoHash, Arc<[u8]>)>>, +) -> Option { + MEM_TRIE_NUM_LOOKUPS.inc(); + let mut nibbles = NibbleSlice::new(key); + let mut node = root; + + loop { + let view = node.view(); + if let Some(nodes_accessed) = &mut nodes_accessed { + let raw_node_serialized = borsh::to_vec(&view.to_raw_trie_node_with_size()).unwrap(); + nodes_accessed.push((view.node_hash(), raw_node_serialized.into())); + } + match view { + MemTrieNodeView::Leaf { extension, value } => { + if nibbles == NibbleSlice::from_encoded(extension.raw_slice()).0 { + return Some(value.to_flat_value()); + } else { + return None; + } + } + MemTrieNodeView::Extension { extension, child, .. } => { + let extension_nibbles = NibbleSlice::from_encoded(extension.raw_slice()).0; + if nibbles.starts_with(&extension_nibbles) { + nibbles = nibbles.mid(extension_nibbles.len()); + node = child; + } else { + return None; + } + } + MemTrieNodeView::Branch { children, .. } => { + if nibbles.is_empty() { + return None; + } + let first = nibbles.at(0); + nibbles = nibbles.mid(1); + node = match children.get(first as usize) { + Some(child) => child, + None => return None, + }; + } + MemTrieNodeView::BranchWithValue { children, value, .. } => { + if nibbles.is_empty() { + return Some(value.to_flat_value()); + } + let first = nibbles.at(0); + nibbles = nibbles.mid(1); + node = match children.get(first as usize) { + Some(child) => child, + None => return None, + }; + } + } + } +} diff --git a/core/store/src/trie/mem/metrics.rs b/core/store/src/trie/mem/metrics.rs new file mode 100644 index 000000000..55edaaa0e --- /dev/null +++ b/core/store/src/trie/mem/metrics.rs @@ -0,0 +1,31 @@ +use unc_o11y::metrics::{ + try_create_int_counter, try_create_int_counter_vec, try_create_int_gauge_vec, IntCounter, + IntCounterVec, IntGaugeVec, +}; +use once_cell::sync::Lazy; + +pub static MEM_TRIE_NUM_ROOTS: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_mem_trie_num_roots", + "Number of trie roots currently active in the in-memory trie", + &["shard_uid"], + ) + .unwrap() +}); + +pub static MEM_TRIE_NUM_NODES_CREATED_FROM_UPDATES: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_mem_trie_num_nodes_created_from_updates", + "Number of trie nodes created by applying updates", + &["shard_uid"], + ) + .unwrap() +}); + +pub static MEM_TRIE_NUM_LOOKUPS: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_mem_trie_num_lookups", + "Number of in-memory trie lookups (number of keys looked up)", + ) + .unwrap() +}); diff --git a/core/store/src/trie/mem/mod.rs b/core/store/src/trie/mem/mod.rs new file mode 100644 index 000000000..70e016ac7 --- /dev/null +++ b/core/store/src/trie/mem/mod.rs @@ -0,0 +1,257 @@ +use self::arena::Arena; +use self::metrics::MEM_TRIE_NUM_ROOTS; +use self::node::{MemTrieNodeId, MemTrieNodePtr}; +use self::updating::MemTrieUpdate; +use unc_primitives::errors::StorageError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::types::{BlockHeight, StateRoot}; +use std::collections::{BTreeMap, HashMap}; + +mod arena; +mod construction; +mod flexible_data; +pub mod loading; +pub mod lookup; +pub mod metrics; +pub mod node; +pub mod updating; + +/// Check this, because in the code we conveniently assume usize is 8 bytes. +/// In-memory trie can't possibly work under 32-bit anyway. +#[cfg(not(target_pointer_width = "64"))] +compile_error!("In-memory trie requires a 64 bit platform"); + +/// `MemTries` (logically) owns the memory of multiple tries. +/// Tries may share nodes with each other via refcounting. The way the +/// refcounting works is very similar to as if each node held a Rc of +/// its children nodes. The `roots` field of this struct logically +/// holds an Rc of the root of each trie. +pub struct MemTries { + arena: Arena, + /// Maps a state root to a list of nodes that have the same root hash. + /// The reason why this is a list is because we do not have a node + /// deduplication mechanism so we can't guarantee that nodes of the + /// same hash are unique. During lookup, any of these nodes can be provided + /// as they all logically represent the same trie. + roots: HashMap>, + /// Maps a block height to a list of state roots present at that height. + /// This is used for GC. The invariant is that for any state root, the + /// number of times the state root appears in this map is equal to the + /// sum of the refcounts of each `MemTrieNodeId`s in `roots[state hash]`. + heights: BTreeMap>, + /// Shard UID, for exporting metrics only. + shard_uid: ShardUId, +} + +impl MemTries { + pub fn new(shard_uid: ShardUId) -> Self { + Self { + arena: Arena::new(shard_uid.to_string()), + roots: HashMap::new(), + heights: Default::default(), + shard_uid, + } + } + + /// Inserts a new root into the trie. The given function should perform + /// the entire construction of the new trie, possibly based on some existing + /// trie nodes. This internally takes care of refcounting. + pub fn construct_root( + &mut self, + block_height: BlockHeight, + f: impl FnOnce(&mut Arena) -> Result, Error>, + ) -> Result { + let root = f(&mut self.arena)?; + if let Some(root) = root { + let state_root = root.as_ptr(self.arena.memory()).view().node_hash(); + self.insert_root(state_root, root, block_height); + Ok(state_root) + } else { + Ok(CryptoHash::default()) + } + } + + fn insert_root( + &mut self, + state_root: StateRoot, + mem_root: MemTrieNodeId, + block_height: BlockHeight, + ) { + assert_ne!(state_root, CryptoHash::default()); + let heights = self.heights.entry(block_height).or_default(); + heights.push(state_root); + let new_ref = mem_root.add_ref(&mut self.arena); + if new_ref == 1 { + self.roots.entry(state_root).or_default().push(mem_root); + } + MEM_TRIE_NUM_ROOTS + .with_label_values(&[&self.shard_uid.to_string()]) + .set(self.roots.len() as i64); + } + + /// Returns a root node corresponding to the given state root. + pub fn get_root<'a>(&'a self, state_root: &CryptoHash) -> Option> { + assert_ne!(state_root, &CryptoHash::default()); + self.roots.get(state_root).map(|ids| ids[0].as_ptr(self.arena.memory())) + } + + /// Expires all trie roots corresponding to a height smaller than + /// `block_height`. This internally manages refcounts. If a trie root + /// is expired but is still used at a higher height, it will still be + /// valid until all references to that root expires. + pub fn delete_until_height(&mut self, block_height: BlockHeight) { + let mut to_delete = vec![]; + self.heights.retain(|height, state_roots| { + if *height < block_height { + for state_root in state_roots { + to_delete.push(*state_root) + } + false + } else { + true + } + }); + for state_root in to_delete { + self.delete_root(&state_root); + } + } + + fn delete_root(&mut self, state_root: &CryptoHash) { + if let Some(ids) = self.roots.get_mut(state_root) { + let last_id = ids.last().unwrap(); + let new_ref = last_id.remove_ref(&mut self.arena); + if new_ref == 0 { + ids.pop(); + if ids.is_empty() { + self.roots.remove(state_root); + } + } + } else { + debug_assert!(false, "Deleting non-existent root: {}", state_root); + } + MEM_TRIE_NUM_ROOTS + .with_label_values(&[&self.shard_uid.to_string()]) + .set(self.roots.len() as i64); + } + + /// Used for unit testing and integration testing. + pub fn num_roots(&self) -> usize { + self.heights.iter().map(|(_, v)| v.len()).sum() + } + + pub fn update( + &self, + root: CryptoHash, + track_disk_changes: bool, + ) -> Result { + let root_id = if root == CryptoHash::default() { + None + } else { + let root_id = self + .get_root(&root) + .ok_or_else(|| { + StorageError::StorageInconsistentState(format!( + "Failed to find root node {:?} in memtrie", + root + )) + })? + .id(); + Some(root_id) + }; + Ok(MemTrieUpdate::new( + root_id, + &self.arena.memory(), + self.shard_uid.to_string(), + track_disk_changes, + )) + } +} + +#[cfg(test)] +mod tests { + use super::node::{InputMemTrieNode, MemTrieNodeId}; + use super::MemTries; + use crate::NibbleSlice; + use unc_primitives::hash::CryptoHash; + use unc_primitives::shard_layout::ShardUId; + use unc_primitives::state::FlatStateValue; + use unc_primitives::types::BlockHeight; + use rand::seq::SliceRandom; + use rand::Rng; + + #[test] + fn test_construct_empty_trie() { + let mut tries = MemTries::new(ShardUId::single_shard()); + let state_root = + tries.construct_root(123, |_| -> Result, ()> { Ok(None) }); + assert_eq!(state_root, Ok(CryptoHash::default())); + assert_eq!(tries.num_roots(), 0); + } + + #[test] + fn test_refcount() { + // Here we test multiple cases: + // - Each height possibly having multiple state roots (due to forks) + // (and possibly with the same state roots) + // - Each state root possibly having multiple actual nodes that have + // the same hash (as we don't deduplicate in general) + // - A state root being possibly the same as another of a different + // height. + // + // And we make sure that the GC refcounting works correctly. + let mut tries = MemTries::new(ShardUId::single_shard()); + let mut available_hashes: Vec<(BlockHeight, CryptoHash)> = Vec::new(); + for height in 100..=200 { + let num_roots_at_height = rand::thread_rng().gen_range(1..=4); + for _ in 0..num_roots_at_height { + match rand::thread_rng().gen_range(0..4) { + 0 if !available_hashes.is_empty() => { + // Reuse an existing root 25% of the time. + let (_, root) = available_hashes.choose(&mut rand::thread_rng()).unwrap(); + let root = tries.get_root(root).unwrap().id(); + let state_root = tries + .construct_root(height, |_| -> Result, ()> { + Ok(Some(root)) + }); + available_hashes.push((height, state_root.unwrap())); + } + _ => { + // Construct a new root 75% of the time. + let state_root = tries + .construct_root(height, |arena| -> Result, ()> { + let root = MemTrieNodeId::new( + arena, + InputMemTrieNode::Leaf { + value: FlatStateValue::Inlined( + format!("{}", height).into_bytes(), + ), + extension: NibbleSlice::new(&[]) + .encoded(true) + .to_vec() + .into_boxed_slice(), + }, + ); + root.as_ptr_mut(arena.memory_mut()).compute_hash_recursively(); + Ok(Some(root)) + }) + .unwrap(); + available_hashes.push((height, state_root)); + } + } + } + // Expire some roots. + tries.delete_until_height(height - 20); + available_hashes.retain(|(h, _)| *h >= height - 20); + // Sanity check that the roots that are supposed to exist still exist. + for (_, state_root) in &available_hashes { + let root = tries.get_root(state_root).unwrap().id(); + assert_eq!(root.as_ptr(tries.arena.memory()).view().node_hash(), *state_root); + } + } + // Expire all roots, and now the number of allocs should be zero. + tries.delete_until_height(201); + assert_eq!(tries.arena.num_active_allocs(), 0); + assert_eq!(tries.num_roots(), 0); + } +} diff --git a/core/store/src/trie/mem/node/encoding.rs b/core/store/src/trie/mem/node/encoding.rs new file mode 100644 index 000000000..43f3d0bb0 --- /dev/null +++ b/core/store/src/trie/mem/node/encoding.rs @@ -0,0 +1,351 @@ +use super::{InputMemTrieNode, MemTrieNodeId, MemTrieNodePtr, MemTrieNodeView}; +use crate::trie::mem::arena::{Arena, ArenaPos}; +use crate::trie::mem::flexible_data::children::EncodedChildrenHeader; +use crate::trie::mem::flexible_data::encoding::{BorshFixedSize, RawDecoder, RawEncoder}; +use crate::trie::mem::flexible_data::extension::EncodedExtensionHeader; +use crate::trie::mem::flexible_data::value::EncodedValueHeader; +use crate::trie::mem::flexible_data::FlexibleDataHeader; +use crate::trie::TRIE_COSTS; +use borsh::{BorshDeserialize, BorshSerialize}; +use elastic_array::ElasticArray16; +use unc_primitives::hash::CryptoHash; +use unc_primitives::state::FlatStateValue; + +#[derive(PartialEq, Eq, Clone, Copy, Debug, BorshSerialize, BorshDeserialize)] +pub(crate) enum NodeKind { + Leaf, + Extension, + Branch, + BranchWithValue, +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub(crate) struct CommonHeader { + refcount: u32, + pub(crate) kind: NodeKind, +} + +impl BorshFixedSize for CommonHeader { + const SERIALIZED_SIZE: usize = std::mem::size_of::() + std::mem::size_of::(); +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub(crate) struct NonLeafHeader { + pub(crate) hash: CryptoHash, + pub(crate) memory_usage: u64, +} + +impl NonLeafHeader { + pub(crate) fn new(memory_usage: u64, node_hash: Option) -> Self { + Self { hash: node_hash.unwrap_or_default(), memory_usage } + } +} + +impl BorshFixedSize for NonLeafHeader { + const SERIALIZED_SIZE: usize = std::mem::size_of::() + std::mem::size_of::(); +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub(crate) struct LeafHeader { + common: CommonHeader, + value: EncodedValueHeader, + extension: EncodedExtensionHeader, +} + +impl BorshFixedSize for LeafHeader { + const SERIALIZED_SIZE: usize = CommonHeader::SERIALIZED_SIZE + + EncodedValueHeader::SERIALIZED_SIZE + + EncodedExtensionHeader::SERIALIZED_SIZE; +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub(crate) struct ExtensionHeader { + common: CommonHeader, + nonleaf: NonLeafHeader, + child: ArenaPos, + extension: EncodedExtensionHeader, +} + +impl BorshFixedSize for ExtensionHeader { + const SERIALIZED_SIZE: usize = CommonHeader::SERIALIZED_SIZE + + NonLeafHeader::SERIALIZED_SIZE + + ArenaPos::SERIALIZED_SIZE + + EncodedExtensionHeader::SERIALIZED_SIZE; +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub(crate) struct BranchHeader { + common: CommonHeader, + nonleaf: NonLeafHeader, + children: EncodedChildrenHeader, +} + +impl BorshFixedSize for BranchHeader { + const SERIALIZED_SIZE: usize = CommonHeader::SERIALIZED_SIZE + + NonLeafHeader::SERIALIZED_SIZE + + EncodedChildrenHeader::SERIALIZED_SIZE; +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub(crate) struct BranchWithValueHeader { + common: CommonHeader, + nonleaf: NonLeafHeader, + value: EncodedValueHeader, + children: EncodedChildrenHeader, +} + +impl BorshFixedSize for BranchWithValueHeader { + const SERIALIZED_SIZE: usize = CommonHeader::SERIALIZED_SIZE + + NonLeafHeader::SERIALIZED_SIZE + + EncodedValueHeader::SERIALIZED_SIZE + + EncodedChildrenHeader::SERIALIZED_SIZE; +} + +impl MemTrieNodeId { + /// Encodes the data. + pub(crate) fn new_impl( + arena: &mut Arena, + node: InputMemTrieNode, + node_hash: Option, + ) -> Self { + // We add reference to all the children when creating the node. + // As for the refcount of this newly created node, it starts at 0. + // It is expected that either our parent will increment our own + // refcount when it is created, or that this node is a root node, + // and the refcount will be incremented by `MemTries`. + match &node { + InputMemTrieNode::Extension { child, .. } => { + child.add_ref(arena); + } + InputMemTrieNode::Branch { children } + | InputMemTrieNode::BranchWithValue { children, .. } => { + for child in children { + if let Some(child) = child { + child.add_ref(arena); + } + } + } + _ => {} + } + // Let's also compute the memory usage of the node. We only do this for + // non-leaf nodes, because for leaf node it is very easy to just + // compute it on demand, so there's no need to store it. + let memory_usage = match &node { + InputMemTrieNode::Leaf { .. } => 0, + InputMemTrieNode::Extension { extension, child } => { + TRIE_COSTS.node_cost + + extension.len() as u64 * TRIE_COSTS.byte_of_key + + child.as_ptr(arena.memory()).view().memory_usage() + } + InputMemTrieNode::Branch { children } => { + let mut memory_usage = TRIE_COSTS.node_cost; + for child in children.iter() { + if let Some(child) = child { + memory_usage += child.as_ptr(arena.memory()).view().memory_usage(); + } + } + memory_usage + } + InputMemTrieNode::BranchWithValue { children, value } => { + let value_len = match value { + FlatStateValue::Ref(value_ref) => value_ref.len(), + FlatStateValue::Inlined(value) => value.len(), + }; + let mut memory_usage = TRIE_COSTS.node_cost + + value_len as u64 * TRIE_COSTS.byte_of_value + + TRIE_COSTS.node_cost; + for child in children.iter() { + if let Some(child) = child { + memory_usage += child.as_ptr(arena.memory()).view().memory_usage(); + } + } + memory_usage + } + }; + // Finally, encode the data. We're still leaving the hash empty; that + // will be computed later in parallel. + let data = match node { + InputMemTrieNode::Leaf { value, extension } => { + let extension_header = EncodedExtensionHeader::from_input(&extension); + let value_header = EncodedValueHeader::from_input(&value); + let mut data = RawEncoder::new( + arena, + LeafHeader::SERIALIZED_SIZE + + extension_header.flexible_data_length() + + value_header.flexible_data_length(), + ); + data.encode(LeafHeader { + common: CommonHeader { refcount: 0, kind: NodeKind::Leaf }, + extension: extension_header, + value: value_header, + }); + data.encode_flexible(&extension_header, extension); + data.encode_flexible(&value_header, value); + data.finish() + } + InputMemTrieNode::Extension { extension, child } => { + let extension_header = EncodedExtensionHeader::from_input(&extension); + let mut data = RawEncoder::new( + arena, + ExtensionHeader::SERIALIZED_SIZE + extension_header.flexible_data_length(), + ); + data.encode(ExtensionHeader { + common: CommonHeader { refcount: 0, kind: NodeKind::Extension }, + nonleaf: NonLeafHeader::new(memory_usage, node_hash), + child: child.pos, + extension: extension_header, + }); + data.encode_flexible(&extension_header, extension); + data.finish() + } + InputMemTrieNode::Branch { children } => { + let children_header = EncodedChildrenHeader::from_input(&children); + let mut data = RawEncoder::new( + arena, + BranchHeader::SERIALIZED_SIZE + children_header.flexible_data_length(), + ); + data.encode(BranchHeader { + common: CommonHeader { refcount: 0, kind: NodeKind::Branch }, + nonleaf: NonLeafHeader::new(memory_usage, node_hash), + children: children_header, + }); + data.encode_flexible(&children_header, children); + data.finish() + } + InputMemTrieNode::BranchWithValue { children, value } => { + let children_header = EncodedChildrenHeader::from_input(&children); + let value_header = EncodedValueHeader::from_input(&value); + let mut data = RawEncoder::new( + arena, + BranchWithValueHeader::SERIALIZED_SIZE + + children_header.flexible_data_length() + + value_header.flexible_data_length(), + ); + data.encode(BranchWithValueHeader { + common: CommonHeader { refcount: 0, kind: NodeKind::BranchWithValue }, + nonleaf: NonLeafHeader::new(memory_usage, node_hash), + children: children_header, + value: value_header, + }); + data.encode_flexible(&children_header, children); + data.encode_flexible(&value_header, value); + data.finish() + } + }; + Self { pos: data.raw_pos() } + } + + /// Increments the refcount, returning the new refcount. + pub(crate) fn add_ref(&self, arena: &mut Arena) -> u32 { + let mut ptr = self.as_ptr_mut(arena.memory_mut()); + let mut decoder = ptr.decoder_mut(); + let mut header = decoder.peek::(); + let new_refcount = header.refcount + 1; + header.refcount = new_refcount; + decoder.overwrite(header); + new_refcount + } + + /// Decrements the refcount, deallocating the node if it reaches zero. + /// Returns the new refcount. + pub(crate) fn remove_ref(&self, arena: &mut Arena) -> u32 { + let mut ptr = self.as_ptr_mut(arena.memory_mut()); + let mut decoder = ptr.decoder_mut(); + let mut header = decoder.peek::(); + let new_refcount = header.refcount - 1; + header.refcount = new_refcount; + decoder.overwrite(header); + if new_refcount == 0 { + let mut children_to_unref = ElasticArray16::new(); + let node_ptr = self.as_ptr(arena.memory()); + for child in node_ptr.view().iter_children() { + children_to_unref.push(child.id().pos); + } + let alloc_size = node_ptr.size_of_allocation(); + arena.dealloc(self.pos, alloc_size); + for child in children_to_unref.iter() { + MemTrieNodeId { pos: *child }.remove_ref(arena); + } + } + new_refcount + } +} + +impl<'a> MemTrieNodePtr<'a> { + pub(crate) fn decoder(&self) -> RawDecoder<'a> { + RawDecoder::new(self.ptr) + } + + /// Decodes the data. + pub(crate) fn view_impl(&self) -> MemTrieNodeView<'a> { + let mut decoder = self.decoder(); + let kind = decoder.peek::().kind; + match kind { + NodeKind::Leaf => { + let header = decoder.decode::(); + let extension = decoder.decode_flexible(&header.extension); + let value = decoder.decode_flexible(&header.value); + MemTrieNodeView::Leaf { extension, value } + } + NodeKind::Extension => { + let header = decoder.decode::(); + let extension = decoder.decode_flexible(&header.extension); + MemTrieNodeView::Extension { + hash: header.nonleaf.hash, + memory_usage: header.nonleaf.memory_usage, + extension, + child: MemTrieNodePtr::from(self.ptr.arena().ptr(header.child)), + } + } + NodeKind::Branch => { + let header = decoder.decode::(); + let children = decoder.decode_flexible(&header.children); + MemTrieNodeView::Branch { + hash: header.nonleaf.hash, + memory_usage: header.nonleaf.memory_usage, + children, + } + } + NodeKind::BranchWithValue => { + let header = decoder.decode::(); + let children = decoder.decode_flexible(&header.children); + let value = decoder.decode_flexible(&header.value); + MemTrieNodeView::BranchWithValue { + hash: header.nonleaf.hash, + memory_usage: header.nonleaf.memory_usage, + children, + value, + } + } + } + } + + /// Calculates the size of the allocation with only a pointer to the start + /// of the trie node's allocation. + fn size_of_allocation(&self) -> usize { + let mut decoder = self.decoder(); + let kind = decoder.peek::().kind; + match kind { + NodeKind::Leaf => { + let header = decoder.decode::(); + LeafHeader::SERIALIZED_SIZE + + header.extension.flexible_data_length() + + header.value.flexible_data_length() + } + NodeKind::Extension => { + let header = decoder.decode::(); + ExtensionHeader::SERIALIZED_SIZE + header.extension.flexible_data_length() + } + NodeKind::Branch => { + let header = decoder.decode::(); + BranchHeader::SERIALIZED_SIZE + header.children.flexible_data_length() + } + NodeKind::BranchWithValue => { + let header = decoder.decode::(); + BranchWithValueHeader::SERIALIZED_SIZE + + header.children.flexible_data_length() + + header.value.flexible_data_length() + } + } + } +} diff --git a/core/store/src/trie/mem/node/mod.rs b/core/store/src/trie/mem/node/mod.rs new file mode 100644 index 000000000..0547e25bb --- /dev/null +++ b/core/store/src/trie/mem/node/mod.rs @@ -0,0 +1,114 @@ +mod encoding; +mod mutation; +#[cfg(test)] +mod tests; +mod view; + +use super::arena::{Arena, ArenaMemory, ArenaPos, ArenaPtr, ArenaPtrMut, ArenaSlice}; +use super::flexible_data::children::ChildrenView; +use super::flexible_data::value::ValueView; +use unc_primitives::hash::CryptoHash; +use unc_primitives::state::FlatStateValue; +use std::fmt::{Debug, Formatter}; + +/// The memory position of an encoded in-memory trie node. +/// With an `ArenaMemory`, this can be turned into a `MemTrieNodePtr` +/// to access to the node's contents. +/// +/// In-memory trie nodes are completely stored in an `Arena`. Allocation and +/// deallocation of these nodes are done on the arena. Nodes allow multiple +/// references in the case of multiple state roots (trie roots), and are +/// internally refcounted. See `MemTries` for more details on the lifecycle +/// of trie nodes. +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub struct MemTrieNodeId { + pub(crate) pos: ArenaPos, +} + +impl MemTrieNodeId { + pub fn new(arena: &mut Arena, input: InputMemTrieNode) -> Self { + Self::new_impl(arena, input, None) + } + + pub fn new_with_hash(arena: &mut Arena, input: InputMemTrieNode, hash: CryptoHash) -> Self { + Self::new_impl(arena, input, Some(hash)) + } + + pub fn as_ptr<'a>(&self, arena: &'a ArenaMemory) -> MemTrieNodePtr<'a> { + MemTrieNodePtr { ptr: arena.ptr(self.pos) } + } + + pub(crate) fn as_ptr_mut<'a>(&self, arena: &'a mut ArenaMemory) -> MemTrieNodePtrMut<'a> { + MemTrieNodePtrMut { ptr: arena.ptr_mut(self.pos) } + } +} + +/// Pointer to an in-memory trie node that allows read-only access to the node +/// and all its descendants. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct MemTrieNodePtr<'a> { + ptr: ArenaPtr<'a>, +} + +/// Pointer to an in-memory trie node that allows mutable access to the node +/// and all its descendants. This is only for computing hashes, and internal +/// reference counting. +pub struct MemTrieNodePtrMut<'a> { + ptr: ArenaPtrMut<'a>, +} + +impl<'a> Debug for MemTrieNodePtr<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.id().fmt(f) + } +} + +impl<'a> MemTrieNodePtr<'a> { + pub fn from(ptr: ArenaPtr<'a>) -> Self { + Self { ptr } + } + + pub fn view(&self) -> MemTrieNodeView<'a> { + self.view_impl() + } + + pub fn id(&self) -> MemTrieNodeId { + MemTrieNodeId { pos: self.ptr.raw_pos() } + } +} + +/// Used to construct a new in-memory trie node. +#[derive(PartialEq, Eq, Debug, Clone)] +pub enum InputMemTrieNode { + Leaf { value: FlatStateValue, extension: Box<[u8]> }, + Extension { extension: Box<[u8]>, child: MemTrieNodeId }, + Branch { children: [Option; 16] }, + BranchWithValue { children: [Option; 16], value: FlatStateValue }, +} + +/// A view of the encoded data of `MemTrieNode`, obtainable via +/// `MemTrieNode::view()`. +#[derive(Debug, Clone)] +pub enum MemTrieNodeView<'a> { + Leaf { + extension: ArenaSlice<'a>, + value: ValueView<'a>, + }, + Extension { + hash: CryptoHash, + memory_usage: u64, + extension: ArenaSlice<'a>, + child: MemTrieNodePtr<'a>, + }, + Branch { + hash: CryptoHash, + memory_usage: u64, + children: ChildrenView<'a>, + }, + BranchWithValue { + hash: CryptoHash, + memory_usage: u64, + children: ChildrenView<'a>, + value: ValueView<'a>, + }, +} diff --git a/core/store/src/trie/mem/node/mutation.rs b/core/store/src/trie/mem/node/mutation.rs new file mode 100644 index 000000000..c2d86b0fa --- /dev/null +++ b/core/store/src/trie/mem/node/mutation.rs @@ -0,0 +1,105 @@ +use crate::trie::mem::arena::ArenaMemory; +use crate::trie::mem::flexible_data::encoding::RawDecoderMut; + +use super::encoding::{CommonHeader, NodeKind, NonLeafHeader}; +use super::{MemTrieNodePtr, MemTrieNodePtrMut}; + +use unc_primitives::hash::{hash, CryptoHash}; + +impl<'a> MemTrieNodePtrMut<'a> { + fn as_const<'b>(&'b self) -> MemTrieNodePtr<'b> { + MemTrieNodePtr { ptr: self.ptr.ptr() } + } + + pub(crate) fn decoder_mut<'b>(&'b mut self) -> RawDecoderMut<'b> { + RawDecoderMut::new(self.ptr.ptr_mut()) + } + + /// Obtains a list of mutable references to the children of this node, + /// destroying this mutable reference. + /// + /// Despite being implemented with unsafe code, this is a safe operation + /// because the children subtrees are disjoint (even if there are multiple + /// roots). It is very similar to `split_at_mut` on mutable slices. + fn split_children_mut(mut self) -> Vec> { + let arena_mut = self.ptr.arena_mut() as *mut ArenaMemory; + let mut result = Vec::new(); + let view = self.as_const().view(); + for child in view.iter_children() { + let child_id = child.id(); + let arena_mut_ref = unsafe { &mut *arena_mut }; + result.push(child_id.as_ptr_mut(arena_mut_ref)); + } + result + } + + /// Like `split_children_mut`, but does not destroy the reference itself. + /// This is possible because of the returned references can only be used + /// while this reference is being mutably held, but it does result in a + /// different lifetime. + fn children_mut<'b>(&'b mut self) -> Vec> { + let arena_mut = self.ptr.arena_mut() as *mut ArenaMemory; + let mut result = Vec::new(); + let view = self.as_const().view(); + for child in view.iter_children() { + let child_id = child.id(); + let arena_mut_ref = unsafe { &mut *arena_mut }; + result.push(child_id.as_ptr_mut(arena_mut_ref)); + } + result + } + + /// Computes the hash for this node, assuming children nodes already have + /// computed hashes. + fn compute_hash(&mut self) { + let raw_trie_node_with_size = self.as_const().view().to_raw_trie_node_with_size(); + let mut decoder = self.decoder_mut(); + match decoder.decode::().kind { + NodeKind::Leaf => {} + _ => { + let mut nonleaf = decoder.peek::(); + nonleaf.hash = hash(&borsh::to_vec(&raw_trie_node_with_size).unwrap()); + decoder.overwrite(nonleaf); + } + } + } + + /// Whether the hash is computed for this node. + fn is_hash_computed(&self) -> bool { + let mut decoder = self.as_const().decoder(); + match decoder.decode::().kind { + NodeKind::Leaf => true, + _ => decoder.peek::().hash != CryptoHash::default(), + } + } + + /// Computes the hashes of this subtree recursively, stopping at any nodes + /// whose hashes are already computed. + pub(crate) fn compute_hash_recursively(&mut self) { + if self.is_hash_computed() { + return; + } + for mut child in self.children_mut() { + child.compute_hash_recursively(); + } + self.compute_hash(); + } + + /// Recursively expand the current subtree until we arrive at subtrees + /// that are small enough (by memory usage); we store these subtrees in + /// the provided vector. The returned subtrees cover all leaves but are + /// disjoint. + pub(crate) fn take_small_subtrees( + self, + threshold_memory_usage: u64, + trees: &mut Vec>, + ) { + if self.as_const().view().memory_usage() < threshold_memory_usage { + trees.push(self); + } else { + for child in self.split_children_mut() { + child.take_small_subtrees(threshold_memory_usage, trees); + } + } + } +} diff --git a/core/store/src/trie/mem/node/tests.rs b/core/store/src/trie/mem/node/tests.rs new file mode 100644 index 000000000..5cea4b298 --- /dev/null +++ b/core/store/src/trie/mem/node/tests.rs @@ -0,0 +1,297 @@ +use crate::trie::mem::arena::Arena; +use crate::trie::mem::node::{InputMemTrieNode, MemTrieNodeId, MemTrieNodeView}; +use crate::trie::Children; +use crate::{RawTrieNode, RawTrieNodeWithSize}; +use unc_primitives::hash::hash; +use unc_primitives::state::{FlatStateValue, ValueRef}; + +#[test] +fn test_basic_leaf_node_inlined() { + let mut arena = Arena::new("".to_owned()); + let node = MemTrieNodeId::new( + &mut arena, + InputMemTrieNode::Leaf { + extension: vec![0, 1, 2, 3, 4].into_boxed_slice(), + value: FlatStateValue::Inlined(vec![5, 6, 7, 8, 9]), + }, + ); + let view = node.as_ptr(arena.memory()).view(); + assert_eq!( + view.to_raw_trie_node_with_size(), + RawTrieNodeWithSize { + memory_usage: 115, + node: RawTrieNode::Leaf( + vec![0, 1, 2, 3, 4], + FlatStateValue::Inlined(vec![5, 6, 7, 8, 9]).to_value_ref() + ), + } + ); + assert_eq!(view.memory_usage(), 115); + assert_eq!(view.node_hash(), hash(&borsh::to_vec(&view.to_raw_trie_node_with_size()).unwrap())); + match view { + MemTrieNodeView::Leaf { extension, value } => { + assert_eq!(extension.raw_slice(), &[0, 1, 2, 3, 4]); + assert_eq!(value.to_flat_value(), FlatStateValue::Inlined(vec![5, 6, 7, 8, 9])); + } + _ => panic!("Unexpected view type: {:?}", view), + } +} + +#[test] +fn test_basic_leaf_node_ref() { + let mut arena = Arena::new("".to_owned()); + let test_hash = hash(&[5, 6, 7, 8, 9]); + let node = MemTrieNodeId::new( + &mut arena, + InputMemTrieNode::Leaf { + extension: vec![0, 1, 2, 3, 4].into_boxed_slice(), + value: FlatStateValue::Ref(ValueRef { hash: test_hash, length: 5 }), + }, + ); + let view = node.as_ptr(arena.memory()).view(); + assert_eq!( + view.to_raw_trie_node_with_size(), + RawTrieNodeWithSize { + memory_usage: 115, + node: RawTrieNode::Leaf(vec![0, 1, 2, 3, 4], ValueRef { hash: test_hash, length: 5 }), + } + ); + assert_eq!(view.memory_usage(), 115); + assert_eq!(view.node_hash(), hash(&borsh::to_vec(&view.to_raw_trie_node_with_size()).unwrap())); + match view { + MemTrieNodeView::Leaf { extension, value } => { + assert_eq!(extension.raw_slice(), &[0, 1, 2, 3, 4]); + assert_eq!( + value.to_flat_value(), + FlatStateValue::Ref(ValueRef { hash: test_hash, length: 5 }) + ); + } + _ => panic!("Unexpected view type: {:?}", view), + } +} + +#[test] +fn test_basic_leaf_node_empty_extension_empty_value() { + let mut arena = Arena::new("".to_owned()); + let node = MemTrieNodeId::new( + &mut arena, + InputMemTrieNode::Leaf { + extension: vec![].into_boxed_slice(), + value: FlatStateValue::Inlined(vec![]), + }, + ); + let view = node.as_ptr(arena.memory()).view(); + assert_eq!( + view.to_raw_trie_node_with_size(), + RawTrieNodeWithSize { + memory_usage: 100, + node: RawTrieNode::Leaf(vec![], FlatStateValue::Inlined(vec![]).to_value_ref()), + } + ); + assert_eq!(view.memory_usage(), 100); + assert_eq!(view.node_hash(), hash(&borsh::to_vec(&view.to_raw_trie_node_with_size()).unwrap())); + match view { + MemTrieNodeView::Leaf { extension, value } => { + assert!(extension.raw_slice().is_empty()); + assert_eq!(value.to_flat_value(), FlatStateValue::Inlined(vec![])); + } + _ => panic!("Unexpected view type: {:?}", view), + } +} + +#[test] +fn test_basic_extension_node() { + let mut arena = Arena::new("".to_owned()); + let child = MemTrieNodeId::new( + &mut arena, + InputMemTrieNode::Leaf { + extension: vec![0, 1, 2, 3, 4].into_boxed_slice(), + value: FlatStateValue::Inlined(vec![5, 6, 7, 8, 9]), + }, + ); + let node = MemTrieNodeId::new( + &mut arena, + InputMemTrieNode::Extension { extension: vec![5, 6, 7, 8, 9].into_boxed_slice(), child }, + ); + node.as_ptr_mut(arena.memory_mut()).compute_hash_recursively(); + let child_ptr = child.as_ptr(arena.memory()); + let node_ptr = node.as_ptr(arena.memory()); + assert_eq!( + node_ptr.view().to_raw_trie_node_with_size(), + RawTrieNodeWithSize { + memory_usage: child_ptr.view().memory_usage() + 60, + node: RawTrieNode::Extension(vec![5, 6, 7, 8, 9], child_ptr.view().node_hash()), + } + ); + assert_eq!(node_ptr.view().memory_usage(), child_ptr.view().memory_usage() + 60); + assert_eq!( + node_ptr.view().node_hash(), + hash(&borsh::to_vec(&node_ptr.view().to_raw_trie_node_with_size()).unwrap()) + ); + match node_ptr.view() { + MemTrieNodeView::Extension { hash, memory_usage, extension, child: actual_child } => { + assert_eq!(hash, node_ptr.view().node_hash()); + assert_eq!(memory_usage, node_ptr.view().memory_usage()); + assert_eq!(extension.raw_slice(), &[5, 6, 7, 8, 9]); + assert_eq!(actual_child, child_ptr); + } + _ => panic!("Unexpected view type: {:?}", node_ptr.view()), + } +} + +fn branch_array(children: Vec<(usize, MemTrieNodeId)>) -> [Option; 16] { + let mut result = [None; 16]; + for (idx, child) in children { + result[idx] = Some(child); + } + result +} + +#[test] +fn test_basic_branch_node() { + let mut arena = Arena::new("".to_owned()); + let child1 = MemTrieNodeId::new( + &mut arena, + InputMemTrieNode::Leaf { + extension: vec![].into_boxed_slice(), + value: FlatStateValue::Inlined(vec![1]), + }, + ); + let child2 = MemTrieNodeId::new( + &mut arena, + InputMemTrieNode::Leaf { + extension: vec![1].into_boxed_slice(), + value: FlatStateValue::Inlined(vec![2]), + }, + ); + let node = MemTrieNodeId::new( + &mut arena, + InputMemTrieNode::Branch { children: branch_array(vec![(3, child1), (5, child2)]) }, + ); + node.as_ptr_mut(arena.memory_mut()).compute_hash_recursively(); + let child1_ptr = child1.as_ptr(arena.memory()); + let child2_ptr = child2.as_ptr(arena.memory()); + let node_ptr = node.as_ptr(arena.memory()); + assert_eq!( + node_ptr.view().to_raw_trie_node_with_size(), + RawTrieNodeWithSize { + memory_usage: child1_ptr.view().memory_usage() + child2_ptr.view().memory_usage() + 50, + node: RawTrieNode::BranchNoValue(Children([ + None, + None, + None, + Some(child1_ptr.view().node_hash()), + None, + Some(child2_ptr.view().node_hash()), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None + ])), + } + ); + assert_eq!( + node_ptr.view().memory_usage(), + child1_ptr.view().memory_usage() + child2_ptr.view().memory_usage() + 50 + ); + assert_eq!( + node_ptr.view().node_hash(), + hash(&borsh::to_vec(&node_ptr.view().to_raw_trie_node_with_size()).unwrap()) + ); + match node_ptr.view() { + MemTrieNodeView::Branch { hash, memory_usage, children } => { + assert_eq!(hash, node_ptr.view().node_hash()); + assert_eq!(memory_usage, node_ptr.view().memory_usage()); + assert_eq!(children.iter().collect::>(), vec![child1_ptr, child2_ptr]); + assert_eq!(children.get(3), Some(child1_ptr)); + assert_eq!(children.get(1), None); + assert_eq!(children.get(5), Some(child2_ptr)); + } + _ => panic!("Unexpected view type: {:?}", node_ptr.view()), + } +} + +#[test] +fn test_basic_branch_with_value_node() { + let mut arena = Arena::new("".to_owned()); + let child1 = MemTrieNodeId::new( + &mut arena, + InputMemTrieNode::Leaf { + extension: vec![].into_boxed_slice(), + value: FlatStateValue::Inlined(vec![1]), + }, + ); + let child2 = MemTrieNodeId::new( + &mut arena, + InputMemTrieNode::Leaf { + extension: vec![1].into_boxed_slice(), + value: FlatStateValue::Inlined(vec![2]), + }, + ); + let node = MemTrieNodeId::new( + &mut arena, + InputMemTrieNode::BranchWithValue { + children: branch_array(vec![(0, child1), (15, child2)]), + value: FlatStateValue::Inlined(vec![3, 4, 5]), + }, + ); + + node.as_ptr_mut(arena.memory_mut()).compute_hash_recursively(); + + let child1_ptr = child1.as_ptr(arena.memory()); + let child2_ptr = child2.as_ptr(arena.memory()); + let node_ptr = node.as_ptr(arena.memory()); + assert_eq!( + node_ptr.view().to_raw_trie_node_with_size(), + RawTrieNodeWithSize { + memory_usage: child1_ptr.view().memory_usage() + child2_ptr.view().memory_usage() + 103, + node: RawTrieNode::BranchWithValue( + FlatStateValue::Inlined(vec![3, 4, 5]).to_value_ref(), + Children([ + Some(child1_ptr.view().node_hash()), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + Some(child2_ptr.view().node_hash()), + ]) + ), + } + ); + assert_eq!( + node_ptr.view().memory_usage(), + child1_ptr.view().memory_usage() + child2_ptr.view().memory_usage() + 103 + ); + assert_eq!( + node_ptr.view().node_hash(), + hash(&borsh::to_vec(&node_ptr.view().to_raw_trie_node_with_size()).unwrap()) + ); + match node_ptr.view() { + MemTrieNodeView::BranchWithValue { hash, memory_usage, children, value } => { + assert_eq!(hash, node_ptr.view().node_hash()); + assert_eq!(memory_usage, node_ptr.view().memory_usage()); + assert_eq!(children.iter().collect::>(), vec![child1_ptr, child2_ptr]); + assert_eq!(children.get(0), Some(child1_ptr)); + assert_eq!(children.get(1), None); + assert_eq!(children.get(15), Some(child2_ptr)); + assert_eq!(value.to_flat_value(), FlatStateValue::Inlined(vec![3, 4, 5])); + } + _ => panic!("Unexpected view type: {:?}", node_ptr.view()), + } +} diff --git a/core/store/src/trie/mem/node/view.rs b/core/store/src/trie/mem/node/view.rs new file mode 100644 index 000000000..8ec5d659a --- /dev/null +++ b/core/store/src/trie/mem/node/view.rs @@ -0,0 +1,78 @@ +use super::{MemTrieNodePtr, MemTrieNodeView}; +use crate::trie::TRIE_COSTS; +use crate::{RawTrieNode, RawTrieNodeWithSize}; + +use unc_primitives::hash::{hash, CryptoHash}; + +impl<'a> MemTrieNodeView<'a> { + /// Returns the node's hash. Requires that the hash is already computed. + pub fn node_hash(&self) -> CryptoHash { + match self { + Self::Leaf { .. } => { + let node = self.clone().to_raw_trie_node_with_size(); + hash(&borsh::to_vec(&node).unwrap()) + } + Self::Extension { hash, .. } + | Self::Branch { hash, .. } + | Self::BranchWithValue { hash, .. } => { + debug_assert_ne!(hash, &CryptoHash::default(), "Hash not computed"); + *hash + } + } + } + + /// Returns the memory usage of the node, in unc's trie cost terms, not + /// in terms of the physical memory usage. + pub fn memory_usage(&self) -> u64 { + match self { + Self::Leaf { value, extension } => { + TRIE_COSTS.node_cost + + extension.len() as u64 * TRIE_COSTS.byte_of_key + + value.len() as u64 * TRIE_COSTS.byte_of_value + + TRIE_COSTS.node_cost // yes, node_cost twice. + } + Self::Extension { memory_usage, .. } + | Self::Branch { memory_usage, .. } + | Self::BranchWithValue { memory_usage, .. } => *memory_usage, + } + } + + /// Converts this view into a `RawTrieNodeWithSize`, which is used to + /// compute hashes and for serialization to disk. + pub fn to_raw_trie_node_with_size(&self) -> RawTrieNodeWithSize { + match self { + Self::Leaf { value, extension } => { + let node = RawTrieNode::Leaf( + extension.raw_slice().to_vec(), + value.clone().to_flat_value().to_value_ref(), + ); + RawTrieNodeWithSize { node, memory_usage: self.memory_usage() } + } + Self::Extension { extension, child, .. } => { + let view = child.view(); + let node = RawTrieNode::Extension(extension.raw_slice().to_vec(), view.node_hash()); + RawTrieNodeWithSize { node, memory_usage: self.memory_usage() } + } + Self::Branch { children, .. } => { + let node = RawTrieNode::BranchNoValue(children.to_children()); + RawTrieNodeWithSize { node, memory_usage: self.memory_usage() } + } + Self::BranchWithValue { children, value, .. } => { + let node = RawTrieNode::BranchWithValue( + value.to_flat_value().to_value_ref(), + children.to_children(), + ); + RawTrieNodeWithSize { node, memory_usage: self.memory_usage() } + } + } + } + + pub(crate) fn iter_children<'b>(&'b self) -> Box> + 'b> { + match self { + MemTrieNodeView::Leaf { .. } => Box::new(std::iter::empty()), + MemTrieNodeView::Extension { child, .. } => Box::new(std::iter::once(*child)), + MemTrieNodeView::Branch { children, .. } + | MemTrieNodeView::BranchWithValue { children, .. } => Box::new(children.iter()), + } + } +} diff --git a/core/store/src/trie/mem/updating.rs b/core/store/src/trie/mem/updating.rs new file mode 100644 index 000000000..a6877540f --- /dev/null +++ b/core/store/src/trie/mem/updating.rs @@ -0,0 +1,1299 @@ +use super::arena::ArenaMemory; +use super::flexible_data::children::ChildrenView; +use super::metrics::MEM_TRIE_NUM_NODES_CREATED_FROM_UPDATES; +use super::node::{InputMemTrieNode, MemTrieNodeId, MemTrieNodeView}; +use super::MemTries; +use crate::trie::{Children, MemTrieChanges, TrieRefcountDeltaMap, TRIE_COSTS}; +use crate::{NibbleSlice, RawTrieNode, RawTrieNodeWithSize, TrieChanges}; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::state::FlatStateValue; +use unc_primitives::types::BlockHeight; +use std::collections::HashMap; + +/// An old node means a node in the current in-memory trie. An updated node means a +/// node we're going to store in the in-memory trie but have not constructed there yet. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum OldOrUpdatedNodeId { + Old(MemTrieNodeId), + Updated(UpdatedMemTrieNodeId), +} + +/// For updated nodes, the ID is simply the index into the array of updated nodes we keep. +pub type UpdatedMemTrieNodeId = usize; + +/// An updated node - a node that will eventually become an in-memory trie node. +/// It references children that are either old or updated nodes. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum UpdatedMemTrieNode { + /// Used for either an empty root node (indicating an empty trie), or as a temporary + /// node to ease implementation. + Empty, + Leaf { + extension: Box<[u8]>, + value: FlatStateValue, + }, + Extension { + extension: Box<[u8]>, + child: OldOrUpdatedNodeId, + }, + /// Corresponds to either a Branch or BranchWithValue node. + Branch { + children: Box<[Option; 16]>, + value: Option, + }, +} + +/// Structure to build an update to the in-memory trie. +pub struct MemTrieUpdate<'a> { + /// The original root before updates. It is None iff the original trie had no keys. + root: Option, + arena: &'a ArenaMemory, + shard_uid: String, // for metrics only + /// All the new nodes that are to be constructed. A node may be None if + /// (1) temporarily we take out the node from the slot to process it and put it back + /// later; or (2) the node is deleted afterwards. + pub updated_nodes: Vec>, + /// Refcount changes to on-disk trie nodes. + pub trie_refcount_changes: Option, +} + +impl UpdatedMemTrieNode { + /// Converts an existing in-memory trie node into an updated one that is + /// equivalent. + pub fn from_existing_node_view(view: MemTrieNodeView<'_>) -> Self { + match view { + MemTrieNodeView::Leaf { extension, value } => Self::Leaf { + extension: extension.raw_slice().to_vec().into_boxed_slice(), + value: value.to_flat_value(), + }, + MemTrieNodeView::Branch { children, .. } => Self::Branch { + children: Box::new(Self::convert_children_to_updated(children)), + value: None, + }, + MemTrieNodeView::BranchWithValue { children, value, .. } => Self::Branch { + children: Box::new(Self::convert_children_to_updated(children)), + value: Some(value.to_flat_value()), + }, + MemTrieNodeView::Extension { extension, child, .. } => Self::Extension { + extension: extension.raw_slice().to_vec().into_boxed_slice(), + child: OldOrUpdatedNodeId::Old(child.id()), + }, + } + } + + fn convert_children_to_updated(view: ChildrenView) -> [Option; 16] { + let mut children = [None; 16]; + for i in 0..16 { + if let Some(child) = view.get(i) { + children[i] = Some(OldOrUpdatedNodeId::Old(child.id())); + } + } + children + } +} + +impl<'a> MemTrieUpdate<'a> { + pub fn new( + root: Option, + arena: &'a ArenaMemory, + shard_uid: String, + track_disk_changes: bool, + ) -> Self { + let mut trie_update = Self { + root, + arena, + shard_uid, + updated_nodes: vec![], + trie_refcount_changes: if track_disk_changes { + Some(TrieRefcountDeltaMap::new()) + } else { + None + }, + }; + assert_eq!(trie_update.convert_existing_to_updated(root), 0usize); + trie_update + } + + /// Internal function to take a node from the array of updated nodes, setting it + /// to None. It is expected that place_node is then called to return the node to + /// the same slot. + fn take_node(&mut self, index: UpdatedMemTrieNodeId) -> UpdatedMemTrieNode { + self.updated_nodes.get_mut(index).unwrap().take().expect("Node taken twice") + } + + /// Does the opposite of take_node; returns the node to the specified ID. + fn place_node(&mut self, index: UpdatedMemTrieNodeId, node: UpdatedMemTrieNode) { + assert!(self.updated_nodes[index].is_none(), "Node placed twice"); + self.updated_nodes[index] = Some(node); + } + + /// Creates a new updated node, assigning it a new ID. + fn new_updated_node(&mut self, node: UpdatedMemTrieNode) -> UpdatedMemTrieNodeId { + let index = self.updated_nodes.len(); + self.updated_nodes.push(Some(node)); + index + } + + /// This is called when we need to mutate a subtree of the original trie. + /// It decrements the refcount of the original trie node (since logically + /// we are removing it), and creates a new node that is equivalent to the + /// original node. The ID of the new node is returned. + /// + /// If the original node is None, it is a marker for the root of an empty + /// trie. + fn convert_existing_to_updated(&mut self, node: Option) -> UpdatedMemTrieNodeId { + match node { + None => self.new_updated_node(UpdatedMemTrieNode::Empty), + Some(node) => { + if let Some(trie_refcount_changes) = self.trie_refcount_changes.as_mut() { + trie_refcount_changes.subtract(node.as_ptr(self.arena).view().node_hash(), 1); + } + self.new_updated_node(UpdatedMemTrieNode::from_existing_node_view( + node.as_ptr(self.arena).view(), + )) + } + } + } + + /// If the ID was old, converts it to an updated one. + fn ensure_updated(&mut self, node: OldOrUpdatedNodeId) -> UpdatedMemTrieNodeId { + match node { + OldOrUpdatedNodeId::Old(node_id) => self.convert_existing_to_updated(Some(node_id)), + OldOrUpdatedNodeId::Updated(node_id) => node_id, + } + } + + fn add_refcount_to_value(&mut self, hash: CryptoHash, value: Option>) { + if let Some(trie_refcount_changes) = self.trie_refcount_changes.as_mut() { + trie_refcount_changes.add(hash, value.unwrap(), 1); + } + } + + fn subtract_refcount_for_value(&mut self, hash: CryptoHash) { + if let Some(trie_refcount_changes) = self.trie_refcount_changes.as_mut() { + trie_refcount_changes.subtract(hash, 1); + } + } + + /// Inserts the given key value pair into the trie. + pub fn insert(&mut self, key: &[u8], value: Vec) { + self.insert_impl(key, FlatStateValue::on_disk(&value), Some(value)); + } + + /// Inserts the given key value pair into the trie, but the value may be a reference. + /// This is used to update the in-memory trie only, without caring about on-disk changes. + pub fn insert_memtrie_only(&mut self, key: &[u8], value: FlatStateValue) { + self.insert_impl(key, value, None); + } + + /// Insertion logic. We descend from the root down to whatever node corresponds to + /// the inserted value. We would need to split, modify, or transform nodes along + /// the way to achieve that. This takes care of refcounting changes for existing + /// nodes as well as values, but will not yet increment refcount for any newly + /// created nodes - that's done at the end. + /// + /// Note that `value` must be Some if we're keeping track of on-disk changes, but can + /// be None if we're only keeping track of in-memory changes. + fn insert_impl(&mut self, key: &[u8], flat_value: FlatStateValue, value: Option>) { + let mut node_id = 0; // root + let mut partial = NibbleSlice::new(key); + let value_ref = flat_value.to_value_ref(); + + loop { + // Take out the current node; we'd have to change it no matter what. + let node = self.take_node(node_id); + match node { + UpdatedMemTrieNode::Empty => { + // There was no node here, create a new leaf. + self.place_node( + node_id, + UpdatedMemTrieNode::Leaf { + extension: partial.encoded(true).into_vec().into_boxed_slice(), + value: flat_value, + }, + ); + self.add_refcount_to_value(value_ref.hash, value); + break; + } + UpdatedMemTrieNode::Branch { children, value: old_value } => { + if partial.is_empty() { + // This branch node is exactly where the value should be added. + if let Some(value) = old_value { + self.subtract_refcount_for_value(value.to_value_ref().hash); + } + self.place_node( + node_id, + UpdatedMemTrieNode::Branch { children, value: Some(flat_value) }, + ); + self.add_refcount_to_value(value_ref.hash, value); + break; + } else { + // Continue descending into the branch, possibly adding a new child. + let mut new_children = children; + let child = &mut new_children[partial.at(0) as usize]; + let new_node_id = match child.take() { + Some(node_id) => self.ensure_updated(node_id), + None => self.new_updated_node(UpdatedMemTrieNode::Empty), + }; + *child = Some(OldOrUpdatedNodeId::Updated(new_node_id)); + self.place_node( + node_id, + UpdatedMemTrieNode::Branch { children: new_children, value: old_value }, + ); + node_id = new_node_id; + partial = partial.mid(1); + continue; + } + } + UpdatedMemTrieNode::Leaf { extension, value: old_value } => { + let existing_key = NibbleSlice::from_encoded(&extension).0; + let common_prefix = partial.common_prefix(&existing_key); + if common_prefix == existing_key.len() && common_prefix == partial.len() { + // We're at the exact leaf. Rewrite the value at this leaf. + self.subtract_refcount_for_value(old_value.to_value_ref().hash); + self.place_node( + node_id, + UpdatedMemTrieNode::Leaf { extension, value: flat_value }, + ); + self.add_refcount_to_value(value_ref.hash, value); + break; + } else if common_prefix == 0 { + // Convert the leaf to an equivalent branch. We are not adding + // the new branch yet; that will be done in the next iteration. + let mut children = Box::<[_; 16]>::default(); + let branch_node = if existing_key.is_empty() { + // Existing key being empty means the old value now lives at the branch. + UpdatedMemTrieNode::Branch { children, value: Some(old_value) } + } else { + let branch_idx = existing_key.at(0) as usize; + let new_extension = existing_key.mid(1).encoded(true).into_vec(); + let new_node_id = self.new_updated_node(UpdatedMemTrieNode::Leaf { + extension: new_extension.into_boxed_slice(), + value: old_value, + }); + children[branch_idx] = Some(OldOrUpdatedNodeId::Updated(new_node_id)); + UpdatedMemTrieNode::Branch { children, value: None } + }; + self.place_node(node_id, branch_node); + continue; + } else { + // Split this leaf into an extension plus a leaf, and descend into the leaf. + let new_node_id = self.new_updated_node(UpdatedMemTrieNode::Leaf { + extension: existing_key + .mid(common_prefix) + .encoded(true) + .into_vec() + .into_boxed_slice(), + value: old_value, + }); + let node = UpdatedMemTrieNode::Extension { + extension: partial + .encoded_leftmost(common_prefix, false) + .into_vec() + .into_boxed_slice(), + child: OldOrUpdatedNodeId::Updated(new_node_id), + }; + self.place_node(node_id, node); + node_id = new_node_id; + partial = partial.mid(common_prefix); + continue; + } + } + UpdatedMemTrieNode::Extension { extension, child: old_child, .. } => { + let existing_key = NibbleSlice::from_encoded(&extension).0; + let common_prefix = partial.common_prefix(&existing_key); + if common_prefix == 0 { + // Split Extension to Branch. + let idx = existing_key.at(0); + let child = if existing_key.len() == 1 { + old_child + } else { + let inner_child = UpdatedMemTrieNode::Extension { + extension: existing_key + .mid(1) + .encoded(false) + .into_vec() + .into_boxed_slice(), + child: old_child, + }; + OldOrUpdatedNodeId::Updated(self.new_updated_node(inner_child)) + }; + + let mut children = Box::<[_; 16]>::default(); + children[idx as usize] = Some(child); + let branch_node = UpdatedMemTrieNode::Branch { children, value: None }; + self.place_node(node_id, branch_node); + // Start over from the same position. + continue; + } else if common_prefix == existing_key.len() { + // Dereference child and descend into it. + let child = self.ensure_updated(old_child); + let node = UpdatedMemTrieNode::Extension { + extension, + child: OldOrUpdatedNodeId::Updated(child), + }; + self.place_node(node_id, node); + node_id = child; + partial = partial.mid(common_prefix); + continue; + } else { + // Partially shared prefix. Convert to shorter extension and descend into it. + // On the next step, branch will be created. + let inner_child_node_id = + self.new_updated_node(UpdatedMemTrieNode::Extension { + extension: existing_key + .mid(common_prefix) + .encoded(false) + .into_vec() + .into_boxed_slice(), + child: old_child, + }); + let child_node = UpdatedMemTrieNode::Extension { + extension: existing_key + .encoded_leftmost(common_prefix, false) + .into_vec() + .into_boxed_slice(), + child: OldOrUpdatedNodeId::Updated(inner_child_node_id), + }; + self.place_node(node_id, child_node); + node_id = inner_child_node_id; + partial = partial.mid(common_prefix); + continue; + } + } + } + } + } + + /// Deletes a key from the trie. + /// + /// This will go down from the root of the trie to supposed location of the + /// key, deleting it if found. It will also keep the trie structure + /// consistent by changing the types of any nodes along the way. + /// + /// Deleting a non-existent key is allowed, and is a no-op. + pub fn delete(&mut self, key: &[u8]) { + let mut node_id = 0; // root + let mut partial = NibbleSlice::new(key); + let mut path = vec![]; // for squashing at the end. + + loop { + path.push(node_id); + let node = self.take_node(node_id); + + match node { + UpdatedMemTrieNode::Empty => { + // Nothing to delete. + self.place_node(node_id, UpdatedMemTrieNode::Empty); + return; + } + UpdatedMemTrieNode::Leaf { extension, value } => { + if NibbleSlice::from_encoded(&extension).0 == partial { + self.subtract_refcount_for_value(value.to_value_ref().hash); + self.place_node(node_id, UpdatedMemTrieNode::Empty); + break; + } else { + // Key being deleted doesn't exist. + self.place_node(node_id, UpdatedMemTrieNode::Leaf { extension, value }); + return; + } + } + UpdatedMemTrieNode::Branch { children: old_children, value } => { + if partial.is_empty() { + if value.is_none() { + // Key being deleted doesn't exist. + self.place_node( + node_id, + UpdatedMemTrieNode::Branch { children: old_children, value }, + ); + return; + }; + self.subtract_refcount_for_value(value.unwrap().to_value_ref().hash); + self.place_node( + node_id, + UpdatedMemTrieNode::Branch { children: old_children, value: None }, + ); + // if needed, branch will be squashed at the end of the function. + break; + } else { + let mut new_children = old_children.clone(); + let child = &mut new_children[partial.at(0) as usize]; + let old_child_id = match child.take() { + Some(node_id) => node_id, + None => { + // Key being deleted doesn't exist. + self.place_node( + node_id, + UpdatedMemTrieNode::Branch { children: old_children, value }, + ); + return; + } + }; + let new_child_id = self.ensure_updated(old_child_id); + *child = Some(OldOrUpdatedNodeId::Updated(new_child_id)); + self.place_node( + node_id, + UpdatedMemTrieNode::Branch { children: new_children, value }, + ); + + node_id = new_child_id; + partial = partial.mid(1); + continue; + } + } + UpdatedMemTrieNode::Extension { extension, child } => { + let (common_prefix, existing_len) = { + let extension_nibbles = NibbleSlice::from_encoded(&extension).0; + (extension_nibbles.common_prefix(&partial), extension_nibbles.len()) + }; + if common_prefix == existing_len { + let new_child_id = self.ensure_updated(child); + self.place_node( + node_id, + UpdatedMemTrieNode::Extension { + extension, + child: OldOrUpdatedNodeId::Updated(new_child_id), + }, + ); + + node_id = new_child_id; + partial = partial.mid(existing_len); + continue; + } else { + // Key being deleted doesn't exist. + self.place_node( + node_id, + UpdatedMemTrieNode::Extension { extension, child }, + ); + return; + } + } + } + } + + self.squash_nodes(path); + } + + /// As we delete a key, it may be necessary to change the types of the nodes + /// along the path from the root to the key, in order to keep the trie + /// structure unique. For example, if a branch node has only one child and + /// no value, it must be converted to an extension node. If that extension + /// node also has a parent that is an extension node, they must be combined + /// into a single extension node. This function takes care of all these + /// cases. + fn squash_nodes(&mut self, path: Vec) { + // Correctness can be shown by induction on path prefix. + for node_id in path.into_iter().rev() { + let node = self.take_node(node_id); + match node { + UpdatedMemTrieNode::Empty => { + // Empty node will be absorbed by its parent node, so defer that. + self.place_node(node_id, UpdatedMemTrieNode::Empty); + } + UpdatedMemTrieNode::Leaf { .. } => { + // It's impossible that we would squash a leaf node, because if we + // had deleted a leaf it would become Empty instead. + unreachable!(); + } + UpdatedMemTrieNode::Branch { mut children, value } => { + // Remove any children that are now empty (removed). + for child in children.iter_mut() { + if let Some(OldOrUpdatedNodeId::Updated(child_node_id)) = child { + if let UpdatedMemTrieNode::Empty = + self.updated_nodes[*child_node_id as usize].as_ref().unwrap() + { + *child = None; + } + } + } + let num_children = children.iter().filter(|node| node.is_some()).count(); + if num_children == 0 { + // Branch with zero children becomes leaf. It's not possible for it to + // become empty, because a branch had at least two children or a value + // and at least one child, so deleting a single value could not + // eliminate both of them. + let leaf_node = UpdatedMemTrieNode::Leaf { + extension: NibbleSlice::new(&[]) + .encoded(true) + .into_vec() + .into_boxed_slice(), + value: value.unwrap(), + }; + self.place_node(node_id, leaf_node); + } else if num_children == 1 && value.is_none() { + // Branch with 1 child but no value becomes extension. + let (idx, child) = children + .into_iter() + .enumerate() + .find_map(|(idx, node)| node.map(|node| (idx, node))) + .unwrap(); + let extension = NibbleSlice::new(&[(idx << 4) as u8]) + .encoded_leftmost(1, false) + .into_vec() + .into_boxed_slice(); + self.extend_child(node_id, extension, child); + } else { + // Branch with more than 1 children stays branch. + self.place_node(node_id, UpdatedMemTrieNode::Branch { children, value }); + } + } + UpdatedMemTrieNode::Extension { extension, child } => { + self.extend_child(node_id, extension, child); + } + } + } + } + + // Creates an extension node at `node_id`, but squashes the extension node according to + // its child; e.g. if the child is a leaf, the whole node becomes a leaf. + fn extend_child( + &mut self, + // The node being squashed. + node_id: UpdatedMemTrieNodeId, + // The current extension. + extension: Box<[u8]>, + // The current child. + child_id: OldOrUpdatedNodeId, + ) { + let child_id = self.ensure_updated(child_id); + let child_node = self.take_node(child_id); + match child_node { + UpdatedMemTrieNode::Empty => { + // This case is not possible. In a trie in general, an extension + // node could only have a child that is a branch (possibly with + // value) node. But a branch node either has a value and at least + // one child, or has at least two children. In either case, it's + // impossible for a single deletion to cause the child to become + // empty. + unreachable!(); + } + // If the child is a leaf (which could happen if a branch node lost + // all its branches and only had a value left, or is left with only + // one branch and that was squashed to a leaf). + UpdatedMemTrieNode::Leaf { extension: child_extension, value } => { + let child_extension = NibbleSlice::from_encoded(&child_extension).0; + let extension = NibbleSlice::from_encoded(&extension) + .0 + .merge_encoded(&child_extension, true) + .into_vec() + .into_boxed_slice(); + self.place_node(node_id, UpdatedMemTrieNode::Leaf { extension, value }) + } + // If the child is a branch, there's nothing to squash. + child_node @ UpdatedMemTrieNode::Branch { .. } => { + self.place_node(child_id, child_node); + self.place_node( + node_id, + UpdatedMemTrieNode::Extension { + extension, + child: OldOrUpdatedNodeId::Updated(child_id), + }, + ); + } + // If the child is an extension (which could happen if a branch node + // is left with only one branch), join the two extensions into one. + UpdatedMemTrieNode::Extension { extension: child_extension, child: inner_child } => { + let child_extension = NibbleSlice::from_encoded(&child_extension).0; + let merged_extension = NibbleSlice::from_encoded(&extension) + .0 + .merge_encoded(&child_extension, false) + .into_vec() + .into_boxed_slice(); + self.place_node( + node_id, + UpdatedMemTrieNode::Extension { + extension: merged_extension, + child: inner_child, + }, + ); + } + } + } + + /// To construct the new trie nodes, we need to create the new nodes in an + /// order such that children are created before their parents - essentially + /// a topological sort. We do this via a post-order traversal of the + /// updated nodes. After this function, `ordered_nodes` contains the IDs of + /// the updated nodes in the order they should be created. + fn post_order_traverse_updated_nodes( + node_id: UpdatedMemTrieNodeId, + updated_nodes: &Vec>, + ordered_nodes: &mut Vec, + ) { + let node = updated_nodes[node_id].as_ref().unwrap(); + match node { + UpdatedMemTrieNode::Empty => { + assert_eq!(node_id, 0); // only root can be empty + return; + } + UpdatedMemTrieNode::Branch { children, .. } => { + for child in children.iter() { + if let Some(OldOrUpdatedNodeId::Updated(child_node_id)) = child { + Self::post_order_traverse_updated_nodes( + *child_node_id, + updated_nodes, + ordered_nodes, + ); + } + } + } + UpdatedMemTrieNode::Extension { child, .. } => { + if let OldOrUpdatedNodeId::Updated(child_node_id) = child { + Self::post_order_traverse_updated_nodes( + *child_node_id, + updated_nodes, + ordered_nodes, + ); + } + } + _ => {} + } + ordered_nodes.push(node_id); + } + + /// For each node in `ordered_nodes`, computes its hash and serialized data. + /// The `ordered_nodes` is expected to come from `post_order_traverse_updated_nodes`, + /// and updated_nodes are indexed by the node IDs in `ordered_nodes`. + fn compute_hashes_and_serialized_nodes( + ordered_nodes: &Vec, + updated_nodes: &Vec>, + arena: &ArenaMemory, + ) -> Vec<(UpdatedMemTrieNodeId, CryptoHash, Vec)> { + let mut result = Vec::<(CryptoHash, u64, Vec)>::new(); + for _ in 0..updated_nodes.len() { + result.push((CryptoHash::default(), 0, Vec::new())); + } + let get_hash_and_memory_usage = |node: OldOrUpdatedNodeId, + result: &Vec<(CryptoHash, u64, Vec)>| + -> (CryptoHash, u64) { + match node { + OldOrUpdatedNodeId::Updated(node_id) => { + let (hash, memory_usage, _) = result[node_id]; + (hash, memory_usage) + } + OldOrUpdatedNodeId::Old(node_id) => { + let view = node_id.as_ptr(arena).view(); + (view.node_hash(), view.memory_usage()) + } + } + }; + + for node_id in ordered_nodes.iter() { + let node = updated_nodes[*node_id].as_ref().unwrap(); + let (raw_node, memory_usage) = match node { + UpdatedMemTrieNode::Empty => unreachable!(), + UpdatedMemTrieNode::Branch { children, value } => { + let mut memory_usage = TRIE_COSTS.node_cost; + let mut child_hashes = vec![]; + for child in children.iter() { + match child { + Some(child) => { + let (child_hash, child_memory_usage) = + get_hash_and_memory_usage(*child, &result); + child_hashes.push(Some(child_hash)); + memory_usage += child_memory_usage; + } + None => { + child_hashes.push(None); + } + } + } + let children = Children(child_hashes.as_slice().try_into().unwrap()); + let value_ref = value.as_ref().map(|value| value.to_value_ref()); + memory_usage += match &value_ref { + Some(value_ref) => { + value_ref.length as u64 * TRIE_COSTS.byte_of_value + + TRIE_COSTS.node_cost + } + None => 0, + }; + (RawTrieNode::branch(children, value_ref), memory_usage) + } + UpdatedMemTrieNode::Extension { extension, child } => { + let (child_hash, child_memory_usage) = + get_hash_and_memory_usage(*child, &result); + let memory_usage = TRIE_COSTS.node_cost + + extension.len() as u64 * TRIE_COSTS.byte_of_key + + child_memory_usage; + (RawTrieNode::Extension(extension.to_vec(), child_hash), memory_usage) + } + UpdatedMemTrieNode::Leaf { extension, value } => { + let memory_usage = TRIE_COSTS.node_cost + + extension.len() as u64 * TRIE_COSTS.byte_of_key + + value.value_len() as u64 * TRIE_COSTS.byte_of_value + + TRIE_COSTS.node_cost; + (RawTrieNode::Leaf(extension.to_vec(), value.to_value_ref()), memory_usage) + } + }; + + let raw_node_with_size = RawTrieNodeWithSize { node: raw_node, memory_usage }; + let node_serialized = borsh::to_vec(&raw_node_with_size).unwrap(); + let node_hash = hash(&node_serialized); + result[*node_id] = (node_hash, memory_usage, node_serialized); + } + + ordered_nodes + .iter() + .map(|node_id| { + let (hash, _, serialized) = &mut result[*node_id]; + (*node_id, *hash, std::mem::take(serialized)) + }) + .collect() + } + + /// Converts the changes to memtrie changes. Also returns the list of new nodes inserted, + /// in hash and serialized form. + fn to_mem_trie_changes_internal( + shard_uid: String, + arena: &ArenaMemory, + updated_nodes: Vec>, + ) -> (MemTrieChanges, Vec<(CryptoHash, Vec)>) { + MEM_TRIE_NUM_NODES_CREATED_FROM_UPDATES + .with_label_values(&[&shard_uid]) + .inc_by(updated_nodes.len() as u64); + let mut ordered_nodes = Vec::new(); + Self::post_order_traverse_updated_nodes(0, &updated_nodes, &mut ordered_nodes); + + let nodes_hashes_and_serialized = + Self::compute_hashes_and_serialized_nodes(&ordered_nodes, &updated_nodes, arena); + + let node_ids_with_hashes = nodes_hashes_and_serialized + .iter() + .map(|(node_id, hash, _)| (*node_id, *hash)) + .collect(); + ( + MemTrieChanges { node_ids_with_hashes, updated_nodes }, + nodes_hashes_and_serialized + .into_iter() + .map(|(_, hash, serialized)| (hash, serialized)) + .collect(), + ) + } + + /// Converts the updates to memtrie changes only. + pub fn to_mem_trie_changes_only(self) -> MemTrieChanges { + let Self { arena, updated_nodes, shard_uid, .. } = self; + let (mem_trie_changes, _) = + Self::to_mem_trie_changes_internal(shard_uid, arena, updated_nodes); + mem_trie_changes + } + + /// Converts the updates to trie changes as well as memtrie changes. + pub fn to_trie_changes(self) -> TrieChanges { + let Self { root, arena, shard_uid, trie_refcount_changes, updated_nodes } = self; + let mut trie_refcount_changes = + trie_refcount_changes.expect("Cannot to_trie_changes for memtrie changes only"); + let (mem_trie_changes, hashes_and_serialized) = + Self::to_mem_trie_changes_internal(shard_uid, arena, updated_nodes); + + // We've accounted for the dereferenced nodes, as well as value addition/subtractions. + // The only thing left is to increment refcount for all new nodes. + for (node_hash, node_serialized) in hashes_and_serialized { + trie_refcount_changes.add(node_hash, node_serialized, 1); + } + let (insertions, deletions) = trie_refcount_changes.into_changes(); + + TrieChanges { + old_root: root.map(|root| root.as_ptr(arena).view().node_hash()).unwrap_or_default(), + new_root: mem_trie_changes + .node_ids_with_hashes + .last() + .map(|(_, hash)| *hash) + .unwrap_or_default(), + insertions, + deletions, + mem_trie_changes: Some(mem_trie_changes), + } + } +} + +/// Applies the given memtrie changes to the in-memory trie data structure. +/// Returns the new root hash. +pub fn apply_memtrie_changes( + memtries: &mut MemTries, + changes: &MemTrieChanges, + block_height: BlockHeight, +) -> CryptoHash { + memtries + .construct_root(block_height, |arena| { + let mut last_node_id: Option = None; + let map_to_new_node_id = |node_id: OldOrUpdatedNodeId, + old_to_new_map: &HashMap< + UpdatedMemTrieNodeId, + MemTrieNodeId, + >| + -> MemTrieNodeId { + match node_id { + OldOrUpdatedNodeId::Updated(node_id) => *old_to_new_map.get(&node_id).unwrap(), + OldOrUpdatedNodeId::Old(node_id) => node_id, + } + }; + + let mut updated_to_new_map = HashMap::::new(); + let updated_nodes = &changes.updated_nodes; + let node_ids_with_hashes = &changes.node_ids_with_hashes; + for (node_id, node_hash) in node_ids_with_hashes.iter() { + let node = updated_nodes.get(*node_id).unwrap().clone().unwrap(); + let node = match node { + UpdatedMemTrieNode::Empty => unreachable!(), + UpdatedMemTrieNode::Branch { children, value } => { + let mut new_children = [None; 16]; + for i in 0..16 { + if let Some(child) = children[i] { + new_children[i] = + Some(map_to_new_node_id(child, &updated_to_new_map)); + } + } + match value { + Some(value) => { + InputMemTrieNode::BranchWithValue { children: new_children, value } + } + None => InputMemTrieNode::Branch { children: new_children }, + } + } + UpdatedMemTrieNode::Extension { extension, child } => { + InputMemTrieNode::Extension { + extension, + child: map_to_new_node_id(child, &updated_to_new_map), + } + } + UpdatedMemTrieNode::Leaf { extension, value } => { + InputMemTrieNode::Leaf { value, extension } + } + }; + let mem_node_id = MemTrieNodeId::new_with_hash(arena, node, *node_hash); + updated_to_new_map.insert(*node_id, mem_node_id); + last_node_id = Some(mem_node_id); + } + + Ok::, ()>(last_node_id) + }) + .unwrap() // cannot fail +} + +#[cfg(test)] +mod tests { + use crate::test_utils::TestTriesBuilder; + use crate::trie::mem::lookup::memtrie_lookup; + use crate::trie::mem::updating::apply_memtrie_changes; + use crate::trie::mem::MemTries; + use crate::trie::MemTrieChanges; + use crate::{KeyLookupMode, ShardTries, TrieChanges}; + use unc_primitives::hash::CryptoHash; + use unc_primitives::shard_layout::ShardUId; + use unc_primitives::state::{FlatStateValue, ValueRef}; + use rand::Rng; + use std::collections::{HashMap, HashSet}; + + struct TestTries { + mem: MemTries, + disk: ShardTries, + truth: HashMap, Option>, + state_root: CryptoHash, + check_deleted_keys: bool, + } + + impl TestTries { + fn new(check_deleted_keys: bool) -> Self { + let mem = MemTries::new(ShardUId::single_shard()); + let disk = TestTriesBuilder::new().build(); + Self { + mem, + disk, + truth: HashMap::new(), + state_root: CryptoHash::default(), + check_deleted_keys, + } + } + + fn make_all_changes(&mut self, changes: Vec<(Vec, Option>)>) -> TrieChanges { + let mut update = self.mem.update(self.state_root, true).unwrap_or_else(|_| { + panic!("Trying to update root {:?} but it's not in memtries", self.state_root) + }); + for (key, value) in changes { + if let Some(value) = value { + update.insert(&key, value); + } else { + update.delete(&key); + } + } + update.to_trie_changes() + } + + fn make_memtrie_changes_only( + &mut self, + changes: Vec<(Vec, Option>)>, + ) -> MemTrieChanges { + let mut update = self.mem.update(self.state_root, false).unwrap_or_else(|_| { + panic!("Trying to update root {:?} but it's not in memtries", self.state_root) + }); + + for (key, value) in changes { + if let Some(value) = value { + update.insert_memtrie_only(&key, FlatStateValue::on_disk(&value)); + } else { + update.delete(&key); + } + } + update.to_mem_trie_changes_only() + } + + fn make_disk_changes_only( + &mut self, + changes: Vec<(Vec, Option>)>, + ) -> TrieChanges { + let trie = self.disk.get_trie_for_shard(ShardUId::single_shard(), self.state_root); + trie.update(changes).unwrap() + } + + fn check_consistency_across_all_changes_and_apply( + &mut self, + changes: Vec<(Vec, Option>)>, + ) { + // First check consistency between the changes. + let memtrie_changes = self.make_memtrie_changes_only(changes.clone()); + let disk_changes = self.make_disk_changes_only(changes.clone()); + let mut all_changes = self.make_all_changes(changes.clone()); + + let mem_trie_changes_from_all_changes = all_changes.mem_trie_changes.take().unwrap(); + assert_eq!(memtrie_changes, mem_trie_changes_from_all_changes); + assert_eq!(disk_changes, all_changes); + + // Then apply the changes and check consistency of new state roots. + let new_state_root_from_mem = apply_memtrie_changes(&mut self.mem, &memtrie_changes, 0); + let mut store_update = self.disk.store_update(); + let new_state_root_from_disk = + self.disk.apply_all(&disk_changes, ShardUId::single_shard(), &mut store_update); + assert_eq!(new_state_root_from_mem, new_state_root_from_disk); + store_update.commit().unwrap(); + self.state_root = new_state_root_from_mem; + + // Update our truth. + for (key, value) in changes { + if let Some(value) = value { + self.truth.insert(key, Some(ValueRef::new(&value))); + } else { + if self.check_deleted_keys { + self.truth.insert(key, None); + } else { + self.truth.remove(&key); + } + } + } + + // Check the truth against both memtrie and on-disk trie. + for (key, value_ref) in &self.truth { + let memtrie_root = if self.state_root == CryptoHash::default() { + None + } else { + Some(self.mem.get_root(&self.state_root).unwrap()) + }; + let disk_trie = + self.disk.get_trie_for_shard(ShardUId::single_shard(), self.state_root); + let memtrie_result = + memtrie_root.and_then(|memtrie_root| memtrie_lookup(memtrie_root, key, None)); + let disk_result = disk_trie.get_optimized_ref(key, KeyLookupMode::Trie).unwrap(); + if let Some(value_ref) = value_ref { + let memtrie_value_ref = memtrie_result + .unwrap_or_else(|| { + panic!("Key {} is in truth but not in memtrie", hex::encode(key)) + }) + .to_value_ref(); + let disk_value_ref = disk_result + .unwrap_or_else(|| { + panic!("Key {} is in truth but not in disk trie", hex::encode(key)) + }) + .into_value_ref(); + assert_eq!( + memtrie_value_ref, + *value_ref, + "Value for key {} is incorrect for memtrie", + hex::encode(key) + ); + assert_eq!( + disk_value_ref, + *value_ref, + "Value for key {} is incorrect for disk trie", + hex::encode(key) + ); + } else { + assert!( + memtrie_result.is_none(), + "Key {} is not in truth but is in memtrie", + hex::encode(key) + ); + assert!( + disk_result.is_none(), + "Key {} is not in truth but is in disk trie", + hex::encode(key) + ); + } + } + } + } + + fn parse_changes(s: &str) -> Vec<(Vec, Option>)> { + s.split('\n') + .map(|s| s.split('#').next().unwrap().trim()) + .filter(|s| !s.is_empty()) + .map(|s| { + let mut parts = s.split(" = "); + let key = parts.next().unwrap(); + let value = parts.next().unwrap(); + let value = + if value == "delete" { None } else { Some(hex::decode(value).unwrap()) }; + (hex::decode(key).unwrap(), value) + }) + .collect() + } + + #[test] + fn test_meta_parse_changes() { + // Make sure that our test utility itself is fine. + let changes = parse_changes( + " + 00ff = 00000001 # comments + 01dd = delete + # comments + 02ac = 0003 + ", + ); + assert_eq!( + changes, + vec![ + (vec![0x00, 0xff], Some(vec![0x00, 0x00, 0x00, 0x01])), + (vec![0x01, 0xdd], None), + (vec![0x02, 0xac], Some(vec![0x00, 0x03])), + ] + ); + } + + // As of Oct 2023 this test by itself achieves 100% test coverage for the + // logic in this file (minus the unreachable cases). If you modify the code + // or the test, please check code coverage with e.g. tarpaulin. + #[test] + fn test_trie_consistency_manual() { + let mut tries = TestTries::new(true); + // Simple insertion from empty trie. + tries.check_consistency_across_all_changes_and_apply(parse_changes( + " + 00 = 0000 + 01 = 0001 + 02 = 0002 + ", + )); + // Prepare some more complex values. + tries.check_consistency_across_all_changes_and_apply(parse_changes( + " + 0000 = 0010 # extends a leaf + 0100 = 0011 # extends another leaf + 03 = 0012 # adds a branch + 0444 = 0013 # adds a branch with a longer leaf + 0500 = 0014 # adds a branch that has a branch underneath + 05100000 = 0015 + 05100001 = 0016 + 05200000 = 0017 + 05200001 = 0018 + 05300000 = 0019 + 05300001 = 001a + 05400000 = 001b + 05400001 = 001c + 05500000 = 001d + 05501000 = 001e + 05501001 = 001f + ", + )); + // Check insertion and deletion in a variety of cases. + // Code coverage is used to confirm we have covered all cases. + tries.check_consistency_across_all_changes_and_apply(parse_changes( + " + 00 = delete # turns a branch with value into an extension + 01 = 0027 # modifies the value at a branch + 0100 = delete # turns a branch with value into a leaf + 03 = delete # deletes a branch + 0444 = 0020 # overwrites a leaf + 0455 = 0022 # split leaf into branch at start + 0456 = 0023 # split (pending) leaf into branch + 05 = 0021 # turn branch into branch with value + 05110000 = 0024 # split extension node into branch at start + 05201000 = 0025 # split extension node into branch in the middle + 05300010 = 0026 # split extension node into branch at the end + 05400000 = delete # turn 2-branch node into leaf that squashes with extension + 05500000 = delete # turn 2-branch node into extension that squashes with another extension + ", + )); + + // sanity check here the truth is correct - i.e. our test itself is good. + let expected_truth = parse_changes( + " + 00 = delete + 0000 = 0010 + 01 = 0027 + 0100 = delete + 02 = 0002 + 03 = delete + 0444 = 0020 + 0455 = 0022 + 0456 = 0023 + 05 = 0021 + 0500 = 0014 + 05100000 = 0015 + 05100001 = 0016 + 05110000 = 0024 + 05200000 = 0017 + 05200001 = 0018 + 05201000 = 0025 + 05300000 = 0019 + 05300001 = 001a + 05300010 = 0026 + 05400000 = delete + 05400001 = 001c + 05500000 = delete + 05501000 = 001e + 05501001 = 001f + ", + ) + .into_iter() + .map(|(k, v)| (k, v.map(|v| ValueRef::new(&v)))) + .collect::>(); + assert_eq!( + tries.truth, + expected_truth, + "Differing keys: {:?}", + expected_truth + .keys() + .cloned() + .chain(tries.truth.keys().cloned()) + .collect::>() + .into_iter() + .filter(|k| { expected_truth.get(k) != tries.truth.get(k) }) + .collect::>() + ); + + // Delete some non-existent keys. + tries.check_consistency_across_all_changes_and_apply(parse_changes( + " + 00 = delete # non-existent branch + 04 = delete # branch without value + 0445 = delete # non-matching leaf + 055011 = delete # non-matching extension + ", + )); + + // Make no changes + tries.check_consistency_across_all_changes_and_apply(Vec::new()); + + // Finally delete all keys. + tries.check_consistency_across_all_changes_and_apply(parse_changes( + " + 0000 = delete + 01 = delete + 02 = delete + 03 = delete + 0444 = delete + 0455 = delete + 0456 = delete + 05 = delete + 0500 = delete + 05100000 = delete + 05100001 = delete + 05110000 = delete + 05200000 = delete + 05200001 = delete + 05201000 = delete + 05300000 = delete + 05300001 = delete + 05300010 = delete + 05400001 = delete + 05501000 = delete + 05501001 = delete + ", + )); + + // Check a corner case that deleting a non-existent key from + // an empty trie does not panic. + tries.check_consistency_across_all_changes_and_apply(parse_changes( + " + 08 = delete # non-existent key when whole trie is empty + ", + )); + + assert_eq!(tries.state_root, CryptoHash::default()); + // Garbage collect all roots we've added. This checks that the refcounts + // maintained by the in-memory tries are correct, because if any + // refcounts are too low this would panic, and if any refcounts are too + // high the number of allocs in the end would be non-zero. + tries.mem.delete_until_height(1); + assert_eq!(tries.mem.num_roots(), 0); + assert_eq!(tries.mem.arena.num_active_allocs(), 0); + } + + // As of Oct 2023 this randomized test was seen to cover all branches except + // deletion of keys from empty tries and deleting all keys from the trie. + #[test] + fn test_trie_consistency_random() { + const MAX_KEYS: usize = 100; + const SLOWDOWN: usize = 5; + let mut tries = TestTries::new(false); + for batch in 0..1000 { + println!("Batch {}:", batch); + let mut existing_keys = tries.truth.iter().map(|(k, _)| k.clone()).collect::>(); + // The more keys we have, the less we insert, the more we delete. + let num_insertions = + rand::thread_rng().gen_range(0..=(MAX_KEYS - existing_keys.len()) / SLOWDOWN); + let num_deletions = + rand::thread_rng().gen_range(0..=(existing_keys.len() + SLOWDOWN - 1) / SLOWDOWN); + let mut changes = Vec::new(); + for _ in 0..num_insertions { + let key_length = rand::thread_rng().gen_range(0..=10); + let existing_key = existing_keys + .get(rand::thread_rng().gen_range(0..existing_keys.len().max(1))) + .cloned() + .unwrap_or_default(); + let reuse_prefix_length = rand::thread_rng().gen_range(0..=existing_key.len()); + let mut key = Vec::::new(); + for i in 0..key_length { + if i < reuse_prefix_length { + key.push(existing_key[i]); + } else { + // Limit nibbles to 4, so that we can generate keys that relate to + // each other more frequently. + let nibble0 = rand::thread_rng().gen::() % 4; + let nibble1 = rand::thread_rng().gen::() % 4; + key.push(nibble0 << 4 | nibble1); + } + } + let mut value_length = rand::thread_rng().gen_range(0..=10); + if value_length == 10 { + value_length = 8000; // make a long value that is not inlined + } + let mut value = Vec::::new(); + for _ in 0..value_length { + value.push(rand::thread_rng().gen()); + } + println!( + " {} = {}", + hex::encode(&key), + if value.len() > 10 { + hex::encode(&value[0..10]) + "..." + } else { + hex::encode(&value) + } + ); + changes.push((key.clone(), Some(value.clone()))); + // Add it to existing keys so that we can insert more keys similar + // to this as well as delete some of these keys too. + existing_keys.push(key); + } + for _ in 0..num_deletions { + let key = existing_keys + .get(rand::thread_rng().gen_range(0..existing_keys.len())) + .cloned() + .unwrap_or_default(); + println!(" {} = delete", hex::encode(&key)); + changes.push((key.clone(), None)); + } + tries.check_consistency_across_all_changes_and_apply(changes); + } + } +} diff --git a/core/store/src/trie/mod.rs b/core/store/src/trie/mod.rs new file mode 100644 index 000000000..979a58663 --- /dev/null +++ b/core/store/src/trie/mod.rs @@ -0,0 +1,1987 @@ +use self::accounting_cache::TrieAccountingCache; +use self::mem::lookup::memtrie_lookup; +use self::mem::updating::{UpdatedMemTrieNode, UpdatedMemTrieNodeId}; +use self::mem::MemTries; +use self::trie_recording::TrieRecorder; +use self::trie_storage::TrieMemoryPartialStorage; +use crate::flat::{FlatStateChanges, FlatStorageChunkView}; +pub use crate::trie::config::TrieConfig; +pub(crate) use crate::trie::config::{ + DEFAULT_SHARD_CACHE_DELETIONS_QUEUE_CAPACITY, DEFAULT_SHARD_CACHE_TOTAL_SIZE_LIMIT, +}; +use crate::trie::insert_delete::NodesStorage; +use crate::trie::iterator::TrieIterator; +pub use crate::trie::nibble_slice::NibbleSlice; +pub use crate::trie::prefetching_trie_storage::{PrefetchApi, PrefetchError}; +pub use crate::trie::shard_tries::{KeyForStateChanges, ShardTries, WrappedTrieChanges}; +pub use crate::trie::state_snapshot::{SnapshotError, StateSnapshot, StateSnapshotConfig}; +pub use crate::trie::trie_storage::{TrieCache, TrieCachingStorage, TrieDBStorage, TrieStorage}; +use crate::StorageError; +use borsh::{BorshDeserialize, BorshSerialize}; +pub use from_flat::construct_trie_from_flat; +use unc_primitives::challenge::PartialState; +use unc_primitives::hash::{hash, CryptoHash}; +pub use unc_primitives::shard_layout::ShardUId; +use unc_primitives::state::{FlatStateValue, ValueRef}; +use unc_primitives::state_record::StateRecord; +use unc_primitives::trie_key::trie_key_parsers::parse_account_id_prefix; +use unc_primitives::trie_key::TrieKey; +pub use unc_primitives::types::TrieNodesCount; +use unc_primitives::types::{AccountId, StateRoot, StateRootNode}; +use unc_vm_runner::ContractCode; +pub use raw_node::{Children, RawTrieNode, RawTrieNodeWithSize}; +use std::cell::RefCell; +use std::collections::HashMap; +use std::fmt::Write; +use std::hash::Hash; +use std::rc::Rc; +use std::str; +use std::sync::{Arc, RwLock}; + +pub mod accounting_cache; +mod config; +mod from_flat; +mod insert_delete; +pub mod iterator; +pub mod mem; +mod nibble_slice; +mod prefetching_trie_storage; +mod raw_node; +pub mod resharding; +mod shard_tries; +mod state_parts; +mod state_snapshot; +mod trie_recording; +mod trie_storage; +#[cfg(test)] +mod trie_tests; +pub mod update; + +const POISONED_LOCK_ERR: &str = "The lock was poisoned."; + +/// For fraud proofs +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct PartialStorage { + pub nodes: PartialState, +} + +#[derive(Clone, Hash, Debug, Copy)] +pub(crate) struct StorageHandle(usize); + +#[derive(Clone, Hash, Debug, Copy)] +pub(crate) struct StorageValueHandle(usize); + +pub struct TrieCosts { + pub byte_of_key: u64, + pub byte_of_value: u64, + pub node_cost: u64, +} + +/// Whether a key lookup will be performed through flat storage or through iterating the trie +#[derive(PartialEq, Eq)] +pub enum KeyLookupMode { + FlatStorage, + Trie, +} + +const TRIE_COSTS: TrieCosts = TrieCosts { byte_of_key: 2, byte_of_value: 1, node_cost: 50 }; + +#[derive(Clone, Hash)] +enum NodeHandle { + InMemory(StorageHandle), + Hash(CryptoHash), +} + +impl NodeHandle { + fn unwrap_hash(&self) -> &CryptoHash { + match self { + Self::Hash(hash) => hash, + Self::InMemory(_) => unreachable!(), + } + } +} + +impl std::fmt::Debug for NodeHandle { + fn fmt(&self, fmtr: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Hash(hash) => write!(fmtr, "{hash}"), + Self::InMemory(handle) => write!(fmtr, "@{}", handle.0), + } + } +} + +#[derive(Clone, Hash)] +enum ValueHandle { + InMemory(StorageValueHandle), + HashAndSize(ValueRef), +} + +impl std::fmt::Debug for ValueHandle { + fn fmt(&self, fmtr: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::HashAndSize(value) => write!(fmtr, "{value:?}"), + Self::InMemory(StorageValueHandle(num)) => write!(fmtr, "@{num}"), + } + } +} + +#[derive(Clone, Hash)] +enum TrieNode { + /// Null trie node. Could be an empty root or an empty branch entry. + Empty, + /// Key and value of the leaf node. + Leaf(Vec, ValueHandle), + /// Branch of 16 possible children and value if key ends here. + Branch(Box>, Option), + /// Key and child of extension. + Extension(Vec, NodeHandle), +} + +#[derive(Clone, Debug)] +pub struct TrieNodeWithSize { + node: TrieNode, + memory_usage: u64, +} + +impl TrieNodeWithSize { + fn from_raw(rc_node: RawTrieNodeWithSize) -> TrieNodeWithSize { + TrieNodeWithSize::new(TrieNode::new(rc_node.node), rc_node.memory_usage) + } + + fn new(node: TrieNode, memory_usage: u64) -> TrieNodeWithSize { + TrieNodeWithSize { node, memory_usage } + } + + fn memory_usage(&self) -> u64 { + self.memory_usage + } + + fn empty() -> TrieNodeWithSize { + TrieNodeWithSize { node: TrieNode::Empty, memory_usage: 0 } + } +} + +impl TrieNode { + fn new(rc_node: RawTrieNode) -> TrieNode { + fn new_branch(children: Children, value: Option) -> TrieNode { + let children = children.0.map(|el| el.map(NodeHandle::Hash)); + let children = Box::new(Children(children)); + let value = value.map(ValueHandle::HashAndSize); + TrieNode::Branch(children, value) + } + + match rc_node { + RawTrieNode::Leaf(key, value) => TrieNode::Leaf(key, ValueHandle::HashAndSize(value)), + RawTrieNode::BranchNoValue(children) => new_branch(children, None), + RawTrieNode::BranchWithValue(value, children) => new_branch(children, Some(value)), + RawTrieNode::Extension(key, child) => TrieNode::Extension(key, NodeHandle::Hash(child)), + } + } + + #[cfg(test)] + fn print( + &self, + f: &mut dyn std::fmt::Write, + memory: &NodesStorage, + spaces: &mut String, + ) -> std::fmt::Result { + match self { + TrieNode::Empty => { + write!(f, "{}Empty", spaces)?; + } + TrieNode::Leaf(key, _value) => { + let slice = NibbleSlice::from_encoded(key); + write!(f, "{}Leaf({:?}, val)", spaces, slice.0)?; + } + TrieNode::Branch(children, value) => { + writeln!( + f, + "{}Branch({}){{", + spaces, + if value.is_some() { "Some" } else { "None" } + )?; + spaces.push(' '); + for (idx, child) in children.iter() { + write!(f, "{}{:01x}->", spaces, idx)?; + match child { + NodeHandle::Hash(hash) => { + write!(f, "{}", hash)?; + } + NodeHandle::InMemory(handle) => { + let child = &memory.node_ref(*handle).node; + child.print(f, memory, spaces)?; + } + } + writeln!(f)?; + } + spaces.remove(spaces.len() - 1); + write!(f, "{}}}", spaces)?; + } + TrieNode::Extension(key, child) => { + let slice = NibbleSlice::from_encoded(key); + writeln!(f, "{}Extension({:?})", spaces, slice)?; + spaces.push(' '); + match child { + NodeHandle::Hash(hash) => { + write!(f, "{}{}", spaces, hash)?; + } + NodeHandle::InMemory(handle) => { + let child = &memory.node_ref(*handle).node; + child.print(f, memory, spaces)?; + } + } + writeln!(f)?; + spaces.remove(spaces.len() - 1); + } + } + Ok(()) + } + + pub fn has_value(&self) -> bool { + match self { + Self::Branch(_, Some(_)) | Self::Leaf(_, _) => true, + _ => false, + } + } + + #[cfg(test)] + fn deep_to_string(&self, memory: &NodesStorage) -> String { + let mut buf = String::new(); + self.print(&mut buf, memory, &mut "".to_string()).expect("printing failed"); + buf + } + + fn memory_usage_for_value_length(value_length: u64) -> u64 { + value_length * TRIE_COSTS.byte_of_value + TRIE_COSTS.node_cost + } + + fn memory_usage_value(value: &ValueHandle, memory: Option<&NodesStorage>) -> u64 { + let value_length = match value { + ValueHandle::InMemory(handle) => memory + .expect("InMemory nodes exist, but storage is not provided") + .value_ref(*handle) + .len() as u64, + ValueHandle::HashAndSize(value) => u64::from(value.length), + }; + Self::memory_usage_for_value_length(value_length) + } + + fn memory_usage_direct_no_memory(&self) -> u64 { + self.memory_usage_direct_internal(None) + } + + fn memory_usage_direct(&self, memory: &NodesStorage) -> u64 { + self.memory_usage_direct_internal(Some(memory)) + } + + fn memory_usage_direct_internal(&self, memory: Option<&NodesStorage>) -> u64 { + match self { + TrieNode::Empty => { + // DEVNOTE: empty nodes don't exist in storage. + // In the in-memory implementation Some(TrieNode::Empty) and None are interchangeable as + // children of branch nodes which means cost has to be 0 + 0 + } + TrieNode::Leaf(key, value) => { + TRIE_COSTS.node_cost + + (key.len() as u64) * TRIE_COSTS.byte_of_key + + Self::memory_usage_value(value, memory) + } + TrieNode::Branch(_children, value) => { + TRIE_COSTS.node_cost + + value.as_ref().map_or(0, |value| Self::memory_usage_value(value, memory)) + } + TrieNode::Extension(key, _child) => { + TRIE_COSTS.node_cost + (key.len() as u64) * TRIE_COSTS.byte_of_key + } + } + } +} + +impl std::fmt::Debug for TrieNode { + /// Formats single trie node. + /// + /// Width can be used to specify indentation. + fn fmt(&self, fmtr: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let empty = ""; + let indent = fmtr.width().unwrap_or(0); + match self { + TrieNode::Empty => write!(fmtr, "{empty:indent$}Empty"), + TrieNode::Leaf(key, value) => write!( + fmtr, + "{empty:indent$}Leaf({:?}, {value:?})", + NibbleSlice::from_encoded(key).0 + ), + TrieNode::Branch(children, value) => { + match value { + Some(value) => write!(fmtr, "{empty:indent$}Branch({value:?}):"), + None => write!(fmtr, "{empty:indent$}Branch:"), + }?; + for (idx, child) in children.iter() { + write!(fmtr, "\n{empty:indent$} {idx:x}: {child:?}")?; + } + Ok(()) + } + TrieNode::Extension(key, child) => { + let key = NibbleSlice::from_encoded(key).0; + write!(fmtr, "{empty:indent$}Extension({key:?}, {child:?})") + } + } + } +} + +pub struct Trie { + storage: Rc, + memtries: Option>>, + root: StateRoot, + /// If present, flat storage is used to look up keys (if asked for). + /// Otherwise, we would crawl through the trie. + flat_storage_chunk_view: Option, + /// This is the deterministic accounting cache, meaning that for the + /// lifetime of this Trie struct, whenever the accounting cache is enabled + /// (which can be toggled on the fly), trie nodes that have been looked up + /// once will be guaranteed to be cached, and further reads to these nodes + /// will encounter less gas cost. + accounting_cache: RefCell, + /// If present, we're capturing all trie nodes that have been accessed + /// during the lifetime of this Trie struct. This is used to produce a + /// state proof so that the same access pattern can be replayed using only + /// the captured result. + recorder: Option>, + /// If true, access to trie nodes (not values) charges gas and affects the + /// accounting cache. If false, access to trie nodes will not charge gas or + /// affect the accounting cache. Value accesses always charge gas no matter + /// what, and lookups done via get_ref with `KeyLookupMode::Trie` will + /// also charge gas no matter what. + charge_gas_for_trie_node_access: bool, +} + +/// Trait for reading data from a trie. +pub trait TrieAccess { + /// Retrieves value with given key from the trie. + /// + /// This doesn’t allow to read data from different chunks (be it from + /// different shards or different blocks). That is, the shard and state + /// root are already known by the object rather than being passed as + /// argument. + fn get(&self, key: &TrieKey) -> Result>, StorageError>; +} + +/// Stores reference count addition for some key-value pair in DB. +#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct TrieRefcountAddition { + /// Hash of trie_node_or_value and part of the DB key. + /// Used for uniting with shard id to get actual DB key. + trie_node_or_value_hash: CryptoHash, + /// DB value. Can be either serialized RawTrieNodeWithSize or value corresponding to + /// some TrieKey. + trie_node_or_value: Vec, + /// Reference count difference which will be added to the total refcount. + rc: std::num::NonZeroU32, +} + +/// Stores reference count subtraction for some key in DB. +#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct TrieRefcountSubtraction { + /// Hash of trie_node_or_value and part of the DB key. + /// Used for uniting with shard id to get actual DB key. + trie_node_or_value_hash: CryptoHash, + /// Obsolete field but which we cannot remove because this data is persisted + /// to the database. + _ignored: IgnoredVecU8, + /// Reference count difference which will be subtracted to the total refcount. + rc: std::num::NonZeroU32, +} + +/// Struct that is borsh compatible with Vec but which is logically the unit type. +#[derive(Default, BorshSerialize, BorshDeserialize, Clone, Debug)] +struct IgnoredVecU8 { + _ignored: Vec, +} + +impl PartialEq for IgnoredVecU8 { + fn eq(&self, _other: &Self) -> bool { + true + } +} +impl Eq for IgnoredVecU8 {} +impl Hash for IgnoredVecU8 { + fn hash(&self, _state: &mut H) {} +} +impl PartialOrd for IgnoredVecU8 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl Ord for IgnoredVecU8 { + fn cmp(&self, _other: &Self) -> std::cmp::Ordering { + std::cmp::Ordering::Equal + } +} + +impl TrieRefcountAddition { + pub fn hash(&self) -> &CryptoHash { + &self.trie_node_or_value_hash + } + + pub fn payload(&self) -> &[u8] { + self.trie_node_or_value.as_slice() + } + + pub fn revert(&self) -> TrieRefcountSubtraction { + TrieRefcountSubtraction::new(self.trie_node_or_value_hash, self.rc) + } +} + +impl TrieRefcountSubtraction { + pub fn new(trie_node_or_value_hash: CryptoHash, rc: std::num::NonZeroU32) -> Self { + Self { trie_node_or_value_hash, _ignored: Default::default(), rc } + } +} + +/// Helps produce a list of additions and subtractions to the trie, +/// especially in the case where deletions don't carry the full value. +pub struct TrieRefcountDeltaMap { + map: HashMap>, i32)>, +} + +impl TrieRefcountDeltaMap { + pub fn new() -> Self { + Self { map: HashMap::new() } + } + + pub fn add(&mut self, hash: CryptoHash, data: Vec, refcount: u32) { + let (old_value, old_rc) = self.map.entry(hash).or_insert((None, 0)); + *old_value = Some(data); + *old_rc += refcount as i32; + } + + pub fn subtract(&mut self, hash: CryptoHash, refcount: u32) { + let (_, old_rc) = self.map.entry(hash).or_insert((None, 0)); + *old_rc -= refcount as i32; + } + + pub fn into_changes(self) -> (Vec, Vec) { + let mut insertions = Vec::new(); + let mut deletions = Vec::new(); + for (hash, (value, rc)) in self.map.into_iter() { + if rc > 0 { + insertions.push(TrieRefcountAddition { + trie_node_or_value_hash: hash, + trie_node_or_value: value.expect("value must be present"), + rc: std::num::NonZeroU32::new(rc as u32).unwrap(), + }); + } else if rc < 0 { + deletions.push(TrieRefcountSubtraction::new( + hash, + std::num::NonZeroU32::new((-rc) as u32).unwrap(), + )); + } + } + // Sort so that trie changes have unique representation. + insertions.sort(); + deletions.sort(); + (insertions, deletions) + } +} + +#[derive(Default, Clone, PartialEq, Eq, Debug)] +pub struct MemTrieChanges { + node_ids_with_hashes: Vec<(UpdatedMemTrieNodeId, CryptoHash)>, + updated_nodes: Vec>, +} + +/// +/// TrieChanges stores delta for refcount. +/// Multiple versions of the state work the following way: +/// __changes1___state1 +/// state0 / +/// \__changes2___state2 +/// +/// To store state0, state1 and state2, apply insertions from changes1 and changes2 +/// +/// Then, to discard state2, apply insertions from changes2 as deletions +/// +/// Then, to discard state0, apply deletions from changes1. +/// deleting state0 while both state1 and state2 exist is not possible. +/// Applying deletions from changes1 while state2 exists makes accessing state2 invalid. +/// +/// +/// create a fork -> apply insertions +/// resolve a fork -> apply opposite of insertions +/// discard old parent which has no forks from it -> apply deletions +/// +/// Having old_root and values in deletions allows to apply TrieChanges in reverse +/// +/// StoreUpdate are the changes from current state refcount to refcount + delta. +#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug)] +pub struct TrieChanges { + pub old_root: StateRoot, + pub new_root: StateRoot, + insertions: Vec, + deletions: Vec, + // If Some, in-memory changes are applied as well. + #[borsh(skip)] + pub mem_trie_changes: Option, +} + +impl TrieChanges { + pub fn empty(old_root: StateRoot) -> Self { + TrieChanges { + old_root, + new_root: old_root, + insertions: vec![], + deletions: vec![], + mem_trie_changes: Default::default(), + } + } + + pub fn insertions(&self) -> &[TrieRefcountAddition] { + self.insertions.as_slice() + } + + pub fn deletions(&self) -> &[TrieRefcountSubtraction] { + self.deletions.as_slice() + } +} + +/// Result of applying state part to Trie. +pub struct ApplyStatePartResult { + /// Trie changes after applying state part. + pub trie_changes: TrieChanges, + /// Flat state changes after applying state part, stored as delta. + pub flat_state_delta: FlatStateChanges, + /// Contract codes belonging to the state part. + pub contract_codes: Vec, +} + +enum NodeOrValue { + Node(Box), + Value(std::sync::Arc<[u8]>), +} + +/// Like a ValueRef, but allows for optimized retrieval of the value if the +/// value were already readily available when the ValueRef was retrieved. +/// +/// This can be the case if the value came from flat storage, for example, +/// when some values are inlined into the storage. +/// +/// Information-wise, this struct contains the same information as a +/// FlatStateValue; however, we make this a separate struct because +/// dereferencing a ValueRef (and likewise, OptimizedValueRef) requires proper +/// gas accounting; it is not a free operation. Therefore, while +/// OptimizedValueRef can be directly converted to a ValueRef, dereferencing +/// the value, even if the value is already available, can only be done via +/// `Trie::deref_optimized`. +#[derive(Debug, PartialEq, Eq)] +pub enum OptimizedValueRef { + Ref(ValueRef), + AvailableValue(ValueAccessToken), +} + +/// Opaque wrapper around Vec so that the value cannot be used directly and +/// must instead be dereferenced via `Trie::deref_optimized`, so that gas +/// accounting is never skipped. +#[derive(Debug, PartialEq, Eq)] +pub struct ValueAccessToken { + // Must stay private. + value: Vec, +} + +impl OptimizedValueRef { + fn from_flat_value(value: FlatStateValue) -> Self { + match value { + FlatStateValue::Ref(value_ref) => Self::Ref(value_ref), + FlatStateValue::Inlined(value) => Self::AvailableValue(ValueAccessToken { value }), + } + } + + pub fn len(&self) -> usize { + match self { + Self::Ref(value_ref) => value_ref.len(), + Self::AvailableValue(token) => token.value.len(), + } + } + + pub fn into_value_ref(self) -> ValueRef { + match self { + Self::Ref(value_ref) => value_ref, + Self::AvailableValue(token) => ValueRef::new(&token.value), + } + } +} + +impl Trie { + pub const EMPTY_ROOT: StateRoot = StateRoot::new(); + + /// Starts accessing a trie with the given storage. + /// By default, the accounting cache is not enabled. To enable or disable it + /// (only in this crate), call self.accounting_cache.borrow_mut().set_enabled(). + pub fn new( + storage: Rc, + root: StateRoot, + flat_storage_chunk_view: Option, + ) -> Self { + Self::new_with_memtries(storage, None, root, flat_storage_chunk_view) + } + + pub fn new_with_memtries( + storage: Rc, + memtries: Option>>, + root: StateRoot, + flat_storage_chunk_view: Option, + ) -> Self { + let accounting_cache = match storage.as_caching_storage() { + Some(caching_storage) => RefCell::new(TrieAccountingCache::new(Some(( + caching_storage.shard_uid, + caching_storage.is_view, + )))), + None => RefCell::new(TrieAccountingCache::new(None)), + }; + Trie { + storage, + memtries, + root, + charge_gas_for_trie_node_access: flat_storage_chunk_view.is_none(), + flat_storage_chunk_view, + accounting_cache, + recorder: None, + } + } + + /// Temporary helper, must be removed after stateless validation release. + pub fn dont_charge_gas_for_trie_node_access(&mut self) { + self.charge_gas_for_trie_node_access = false; + } + + /// Makes a new trie that has everything the same except that access + /// through that trie accumulates a state proof for all nodes accessed. + pub fn recording_reads(&self) -> Self { + let mut trie = Self::new_with_memtries( + self.storage.clone(), + self.memtries.clone(), + self.root, + self.flat_storage_chunk_view.clone(), + ); + trie.recorder = Some(RefCell::new(TrieRecorder::new())); + trie + } + + /// Takes the recorded state proof out of the trie. + pub fn recorded_storage(&self) -> Option { + self.recorder.as_ref().map(|recorder| recorder.borrow_mut().recorded_storage()) + } + + /// Constructs a Trie from the partial storage (i.e. state proof) that + /// was returned from recorded_storage(). If used to access the same trie + /// nodes as when the partial storage was generated, this trie will behave + /// identically. + /// + /// The flat_storage_used parameter should be true iff originally the trie + /// was accessed with flat storage present. It will be used to simulate the + /// same costs as if flat storage were present. + pub fn from_recorded_storage( + partial_storage: PartialStorage, + root: StateRoot, + flat_storage_used: bool, + ) -> Self { + let PartialState::TrieValues(nodes) = partial_storage.nodes; + let recorded_storage = nodes.into_iter().map(|value| (hash(&value), value)).collect(); + let storage = Rc::new(TrieMemoryPartialStorage::new(recorded_storage)); + let mut trie = Self::new(storage, root, None); + trie.charge_gas_for_trie_node_access = !flat_storage_used; + trie + } + + pub fn get_root(&self) -> &StateRoot { + &self.root + } + + pub fn has_flat_storage_chunk_view(&self) -> bool { + self.flat_storage_chunk_view.is_some() + } + + pub fn internal_get_storage_as_caching_storage(&self) -> Option<&TrieCachingStorage> { + self.storage.as_caching_storage() + } + + /// All access to trie nodes or values must go through this method, so it + /// can be properly cached and recorded. + /// + /// count_cost can be false to skip caching. This is used when we're + /// generating a state proof, but the value is supposed to fetched from + /// flat storage. + fn internal_retrieve_trie_node( + &self, + hash: &CryptoHash, + use_accounting_cache: bool, + ) -> Result, StorageError> { + let result = if use_accounting_cache { + self.accounting_cache + .borrow_mut() + .retrieve_raw_bytes_with_accounting(hash, &*self.storage)? + } else { + self.storage.retrieve_raw_bytes(hash)? + }; + if let Some(recorder) = &self.recorder { + recorder.borrow_mut().record(hash, result.clone()); + } + Ok(result) + } + + #[cfg(test)] + fn memory_usage_verify(&self, memory: &NodesStorage, handle: NodeHandle) -> u64 { + if self.recorder.is_some() { + return 0; + } + let TrieNodeWithSize { node, memory_usage } = match handle { + NodeHandle::InMemory(h) => memory.node_ref(h).clone(), + NodeHandle::Hash(h) => self.retrieve_node(&h).expect("storage failure").1, + }; + + let mut memory_usage_naive = node.memory_usage_direct(memory); + match &node { + TrieNode::Empty => {} + TrieNode::Leaf(_key, _value) => {} + TrieNode::Branch(children, _value) => { + memory_usage_naive += children + .iter() + .map(|(_, handle)| self.memory_usage_verify(memory, handle.clone())) + .sum::(); + } + TrieNode::Extension(_key, child) => { + memory_usage_naive += self.memory_usage_verify(memory, child.clone()); + } + }; + if memory_usage_naive != memory_usage { + eprintln!("Incorrectly calculated memory usage"); + eprintln!("Correct is {}", memory_usage_naive); + eprintln!("Computed is {}", memory_usage); + match handle { + NodeHandle::InMemory(h) => { + eprintln!("TRIE!!!!"); + eprintln!("{}", memory.node_ref(h).node.deep_to_string(memory)); + } + NodeHandle::Hash(_h) => { + eprintln!("Bad node in storage!"); + } + }; + assert_eq!(memory_usage_naive, memory_usage); + } + memory_usage + } + + fn delete_value( + &self, + memory: &mut NodesStorage, + value: &ValueHandle, + ) -> Result<(), StorageError> { + match value { + ValueHandle::HashAndSize(value) => { + self.internal_retrieve_trie_node(&value.hash, true)?; + memory.refcount_changes.subtract(value.hash, 1); + } + ValueHandle::InMemory(_) => { + // do nothing + } + } + Ok(()) + } + + /// Prints the trie nodes starting from `hash`, up to `max_depth` depth. The node hash can be any node in the trie. + /// Depending on arguments provided, can limit output to no more than `limit` entries, + /// show only subtree for a given `record_type`, or skip subtrees where `AccountId` is less than `from` or greater than `to`. + pub fn print_recursive( + &self, + f: &mut dyn std::io::Write, + hash: &CryptoHash, + max_depth: u32, + limit: Option, + record_type: Option, + from: &Option<&AccountId>, + to: &Option<&AccountId>, + ) { + match self.debug_retrieve_raw_node_or_value(hash) { + Ok(NodeOrValue::Node(_)) => { + let mut prefix: Vec = Vec::new(); + let mut limit = limit.unwrap_or(u32::MAX); + self.print_recursive_internal( + f, + hash, + &mut "".to_string(), + &mut prefix, + max_depth, + &mut limit, + record_type, + from, + to, + ) + .expect("write failed"); + } + Ok(NodeOrValue::Value(value_bytes)) => { + writeln!( + f, + "Given node is a value. Len: {}, Data: {:?} ", + value_bytes.len(), + &value_bytes[..std::cmp::min(10, value_bytes.len())] + ) + .expect("write failed"); + } + Err(err) => { + writeln!(f, "Error when reading: {}", err).expect("write failed"); + } + }; + } + + /// Prints the trie leaves starting from the state root node, up to max_depth depth. + /// This method can only iterate starting from the root node and it only prints the + /// leaf nodes but it shows output in more human friendly way. + /// Optional arguments `limit` and `record_type` limits the output to at most `limit` + /// entries and shows trie nodes of `record_type` type only. + /// `from` and `to` can be used skip leaves with `AccountId` less than `from` or greater than `to`. + pub fn print_recursive_leaves( + &self, + f: &mut dyn std::io::Write, + max_depth: u32, + limit: Option, + record_type: Option, + from: &Option<&AccountId>, + to: &Option<&AccountId>, + ) { + let mut limit = limit.unwrap_or(u32::MAX); + let from = from.cloned(); + let to = to.cloned(); + + let prune_condition = move |key_nibbles: &Vec| { + if key_nibbles.len() > max_depth as usize { + return true; + } + let (partial_key, _) = Self::nibbles_to_bytes(&key_nibbles); + Self::should_prune_view_trie(&partial_key, record_type, &from.as_ref(), &to.as_ref()) + }; + + let iter = match self.iter_with_prune_condition(Some(Box::new(prune_condition))) { + Ok(iter) => iter, + Err(err) => { + writeln!(f, "Error when getting the trie iterator: {}", err).expect("write failed"); + return; + } + }; + + for node in iter { + if limit == 0 { + break; + } + let (key, value) = match node { + Ok((key, value)) => (key, value), + Err(err) => { + writeln!(f, "Failed to iterate node with error: {err}").expect("write failed"); + continue; + } + }; + + // Try to parse the key in UTF8 which works only for the simplest keys (e.g. account), + // or get whitespace padding instead. + let key_string = match str::from_utf8(&key) { + Ok(value) => String::from(value), + Err(_) => " ".repeat(key.len()), + }; + let state_record = StateRecord::from_raw_key_value(key.clone(), value); + + limit -= 1; + writeln!(f, "{} {state_record:?}", key_string).expect("write failed"); + } + } + + /// Converts the list of Nibbles to `Vec` and remainder (in case the length of the input was odd). + fn nibbles_to_bytes(nibbles: &[u8]) -> (Vec, &[u8]) { + let (chunks, remainder) = stdx::as_chunks::<2, _>(nibbles); + let bytes = chunks.into_iter().map(|chunk| (chunk[0] * 16) + chunk[1]).collect::>(); + (bytes, remainder) + } + + // Converts the list of Nibbles to a readable string. + fn nibbles_to_string(prefix: &[u8]) -> String { + let (bytes, remainder) = Self::nibbles_to_bytes(prefix); + let mut result = bytes + .iter() + .flat_map(|ch| std::ascii::escape_default(*ch).map(char::from)) + .collect::(); + if let Some(final_nibble) = remainder.first() { + write!(&mut result, "\\x{:x}_", final_nibble).unwrap(); + } + result + } + + /// Checks whether the provided `account_id_prefix` is lexicographically greater than `to` (if provided), + /// or whether it is lexicographically less than `from` (if provided, except being a prefix of `from`). + /// Although prefix of `from` is lexicographically less than `from`, pruning such subtree would cut off `from`. + fn is_out_of_account_id_bounds( + account_id_prefix: &[u8], + from: &Option<&AccountId>, + to: &Option<&AccountId>, + ) -> bool { + if let Some(from) = from { + if !from.as_bytes().starts_with(account_id_prefix) + && from.as_bytes() > account_id_prefix + { + return true; + } + } + if let Some(to) = to { + return account_id_prefix > to.as_bytes(); + } + false + } + + /// Returns true if the node with key `node_key` and its subtree should be skipped based on provided arguments. + /// If `record_type` is provided and the node is of different type, returns true. + /// If `AccountId`s in the subtree will not fall in the range [`from`, `to`], returns true. + /// Otherwise returns false. + fn should_prune_view_trie( + node_key: &Vec, + record_type: Option, + from: &Option<&AccountId>, + to: &Option<&AccountId>, + ) -> bool { + if node_key.is_empty() { + return false; + } + + let column = node_key[0]; + if let Some(record_type) = record_type { + if column != record_type { + return true; + } + } + if let Ok(account_id_prefix) = parse_account_id_prefix(column, &node_key) { + if Self::is_out_of_account_id_bounds(account_id_prefix, from, to) { + return true; + } + } + false + } + + fn print_recursive_internal( + &self, + f: &mut dyn std::io::Write, + hash: &CryptoHash, + spaces: &mut String, + prefix: &mut Vec, + max_depth: u32, + limit: &mut u32, + record_type: Option, + from: &Option<&AccountId>, + to: &Option<&AccountId>, + ) -> std::io::Result<()> { + if max_depth == 0 || *limit == 0 { + return Ok(()); + } + *limit -= 1; + + let (bytes, raw_node, mem_usage) = match self.retrieve_raw_node(hash, true) { + Ok(Some((bytes, raw_node))) => (bytes, raw_node.node, raw_node.memory_usage), + Ok(None) => return writeln!(f, "{spaces}EmptyNode"), + Err(err) => return writeln!(f, "{spaces}error {err}"), + }; + + let children = match raw_node { + RawTrieNode::Leaf(key, value) => { + let (slice, _) = NibbleSlice::from_encoded(key.as_slice()); + prefix.extend(slice.iter()); + + let (leaf_key, remainder) = Self::nibbles_to_bytes(&prefix); + assert!(remainder.is_empty()); + + if !Self::should_prune_view_trie(&leaf_key, record_type, from, to) { + let state_record = StateRecord::from_raw_key_value(leaf_key, bytes.to_vec()); + + writeln!( + f, + "{spaces}Leaf {slice:?} {value:?} prefix:{} hash:{hash} mem_usage:{mem_usage} state_record:{:?}", + Self::nibbles_to_string(prefix), + state_record.map(|sr|format!("{}", sr)), + )?; + } + + prefix.truncate(prefix.len() - slice.len()); + return Ok(()); + } + RawTrieNode::BranchNoValue(children) => { + writeln!( + f, + "{spaces}Branch value:(none) prefix:{} hash:{hash} mem_usage:{mem_usage}", + Self::nibbles_to_string(prefix), + )?; + children + } + RawTrieNode::BranchWithValue(value, children) => { + writeln!( + f, + "{spaces}Branch value:{value:?} prefix:{} hash:{hash} mem_usage:{mem_usage}", + Self::nibbles_to_string(prefix), + )?; + children + } + RawTrieNode::Extension(key, child) => { + let (slice, _) = NibbleSlice::from_encoded(key.as_slice()); + let node_info = format!( + "{}Extension {:?} child_hash:{} prefix:{} hash:{hash} mem_usage:{mem_usage}", + spaces, + slice, + child, + Self::nibbles_to_string(prefix), + ); + spaces.push_str(" "); + prefix.extend(slice.iter()); + + let (partial_key, _) = Self::nibbles_to_bytes(&prefix); + + if !Self::should_prune_view_trie(&partial_key, record_type, from, to) { + writeln!(f, "{}", node_info)?; + + self.print_recursive_internal( + f, + &child, + spaces, + prefix, + max_depth - 1, + limit, + record_type, + from, + to, + )?; + } + + prefix.truncate(prefix.len() - slice.len()); + spaces.truncate(spaces.len() - 2); + return Ok(()); + } + }; + + for (idx, child) in children.iter() { + writeln!(f, "{spaces} {idx:01x}->")?; + spaces.push_str(" "); + prefix.push(idx); + self.print_recursive_internal( + f, + child, + spaces, + prefix, + max_depth - 1, + limit, + record_type, + from, + to, + )?; + prefix.pop(); + spaces.truncate(spaces.len() - 2); + } + + Ok(()) + } + + fn retrieve_raw_node( + &self, + hash: &CryptoHash, + use_accounting_cache: bool, + ) -> Result, RawTrieNodeWithSize)>, StorageError> { + if hash == &Self::EMPTY_ROOT { + return Ok(None); + } + let bytes = self.internal_retrieve_trie_node(hash, use_accounting_cache)?; + let node = RawTrieNodeWithSize::try_from_slice(&bytes).map_err(|err| { + StorageError::StorageInconsistentState(format!("Failed to decode node {hash}: {err}")) + })?; + Ok(Some((bytes, node))) + } + + // Similar to retrieve_raw_node but handles the case where there is a Value (and not a Node) in the database. + // This method is not safe to be used in any real scenario as it can incorrectly interpret a value as a trie node. + // It's only provided as a convenience for debugging tools. + fn debug_retrieve_raw_node_or_value( + &self, + hash: &CryptoHash, + ) -> Result { + let bytes = self.internal_retrieve_trie_node(hash, true)?; + match RawTrieNodeWithSize::try_from_slice(&bytes) { + Ok(node) => Ok(NodeOrValue::Node(Box::new(node))), + Err(_) => Ok(NodeOrValue::Value(bytes)), + } + } + + fn move_node_to_mutable( + &self, + memory: &mut NodesStorage, + hash: &CryptoHash, + ) -> Result { + match self.retrieve_raw_node(hash, true)? { + None => Ok(memory.store(TrieNodeWithSize::empty())), + Some((_, node)) => { + let result = memory.store(TrieNodeWithSize::from_raw(node)); + memory.refcount_changes.subtract(*hash, 1); + Ok(result) + } + } + } + + /// Retrieves decoded node alongside with its raw bytes representation. + /// + /// Note that because Empty nodes (those which are referenced by + /// [`Self::EMPTY_ROOT`] hash) aren’t stored in the database, they don’t + /// have a bytes representation. For those nodes the first return value + /// will be `None`. + fn retrieve_node( + &self, + hash: &CryptoHash, + ) -> Result<(Option>, TrieNodeWithSize), StorageError> { + match self.retrieve_raw_node(hash, true)? { + None => Ok((None, TrieNodeWithSize::empty())), + Some((bytes, node)) => Ok((Some(bytes), TrieNodeWithSize::from_raw(node))), + } + } + + pub fn retrieve_root_node(&self) -> Result { + match self.retrieve_raw_node(&self.root, true)? { + None => Ok(StateRootNode::empty()), + Some((bytes, node)) => { + Ok(StateRootNode { data: bytes, memory_usage: node.memory_usage }) + } + } + } + + /// Retrieves the value (inlined or reference) for the given key, from flat storage. + /// In general, flat storage may inline a value if the value is short, but otherwise + /// it would defer the storage of the value to the trie. This method will return + /// whatever the flat storage has. + /// + /// If an inlined value is returned, this method will charge the corresponding gas + /// as if the value were accessed from the trie storage. It will also insert the + /// value into the accounting cache, as well as recording the access to the value + /// if recording is enabled. In other words, if an inlined value is returned the + /// behavior is equivalent to if the trie were used to access the value reference + /// and then the reference were used to look up the full value. + /// + /// If `ref_only` is true, even if the flat storage gives us the inlined value, we + /// would still convert it to a reference. This is useful if making an access for + /// the value (thereby charging gas for it) is not desired. + fn lookup_from_flat_storage( + &self, + key: &[u8], + ) -> Result, StorageError> { + let flat_storage_chunk_view = self.flat_storage_chunk_view.as_ref().unwrap(); + let value = flat_storage_chunk_view.get_value(key)?; + if self.recorder.is_some() { + // If recording, we need to look up in the trie as well to record the trie nodes, + // as they are needed to prove the value. Also, it's important that this lookup + // is done even if the key was not found, because intermediate trie nodes may be + // needed to prove the non-existence of the key. + let value_ref_from_trie = + self.lookup_from_state_column(NibbleSlice::new(key), false)?; + debug_assert_eq!( + &value_ref_from_trie, + &value.as_ref().map(|value| value.to_value_ref()) + ); + } + Ok(value.map(OptimizedValueRef::from_flat_value)) + } + + /// Looks up the given key by walking the trie nodes stored in the + /// `DBCol::State` column in the database (but still going through + /// applicable caches). + /// + /// The `charge_gas_for_trie_node_access` parameter controls whether the + /// lookup incurs any gas. + fn lookup_from_state_column( + &self, + mut key: NibbleSlice<'_>, + charge_gas_for_trie_node_access: bool, + ) -> Result, StorageError> { + let mut hash = self.root; + loop { + let node = match self.retrieve_raw_node(&hash, charge_gas_for_trie_node_access)? { + None => return Ok(None), + Some((_bytes, node)) => node.node, + }; + match node { + RawTrieNode::Leaf(existing_key, value) => { + return Ok(if NibbleSlice::from_encoded(&existing_key).0 == key { + Some(value) + } else { + None + }); + } + RawTrieNode::Extension(existing_key, child) => { + let existing_key = NibbleSlice::from_encoded(&existing_key).0; + if key.starts_with(&existing_key) { + hash = child; + key = key.mid(existing_key.len()); + } else { + return Ok(None); + } + } + RawTrieNode::BranchNoValue(mut children) => { + if key.is_empty() { + return Ok(None); + } else if let Some(h) = children[key.at(0)].take() { + hash = h; + key = key.mid(1); + } else { + return Ok(None); + } + } + RawTrieNode::BranchWithValue(value, mut children) => { + if key.is_empty() { + return Ok(Some(value)); + } else if let Some(h) = children[key.at(0)].take() { + hash = h; + key = key.mid(1); + } else { + return Ok(None); + } + } + }; + } + } + + /// Retrieves an `OptimizedValueRef` (a hash of or inlined value) for the given + /// key from the in-memory trie. In general, in-memory tries may inline a value + /// if the value is short, but otherwise it would defer the storage of the value + /// to the state column. This method will return whichever the in-memory trie has. + /// Refer to `get_optimized_ref` for the semantics of using the returned type. + /// + /// `charge_gas_for_trie_node_access` is used to control whether Trie node + /// accesses incur any gas. Note that access to values is never charged here; + /// it is only charged when the returned ref is dereferenced. + fn lookup_from_memory( + &self, + key: &[u8], + charge_gas_for_trie_node_access: bool, + ) -> Result, StorageError> { + if self.root == Self::EMPTY_ROOT { + return Ok(None); + } + let lock = self.memtries.as_ref().unwrap().read().unwrap(); + let root = lock.get_root(&self.root).ok_or_else(|| { + StorageError::StorageInconsistentState(format!( + "Failed to find root node {} in memtrie", + self.root + )) + })?; + + let mut accessed_nodes = Vec::new(); + let flat_value = memtrie_lookup(root, key, Some(&mut accessed_nodes)); + if charge_gas_for_trie_node_access { + for (node_hash, serialized_node) in &accessed_nodes { + self.accounting_cache + .borrow_mut() + .retroactively_account(*node_hash, serialized_node.clone()); + } + } + if let Some(recorder) = &self.recorder { + for (node_hash, serialized_node) in accessed_nodes { + recorder.borrow_mut().record(&node_hash, serialized_node); + } + } + Ok(flat_value.map(OptimizedValueRef::from_flat_value)) + } + + /// For debugging only. Returns the raw node at the given path starting from the root. + /// The format of the nibbles parameter is that each element represents 4 bits of the + /// path. (Even though we use a u8 for each element, we only use the lower 4 bits.) + pub fn debug_get_node(&self, nibbles: &[u8]) -> Result, StorageError> { + // We need to construct an equivalent NibbleSlice so we can easily use it + // to traverse the trie. The tricky part is that the NibbleSlice implementation + // only allows *starting* from the middle of a byte, and always requires ending at + // the end of the internal array - this is sufficient because for production purposes + // we always have a complete leaf path to use. But for our debugging purposes we + // specify an incomplete trie path that may *end* at the middle of a byte, so to get + // around that, if the provided path length is odd, we prepend a 0 and then start in + // the middle of the first byte. + let odd = nibbles.len() % 2 == 1; + let mut nibble_array = Vec::new(); + if odd { + nibble_array.push(0); + } + for nibble in nibbles { + nibble_array.push(*nibble); + } + let mut nibble_data = Vec::new(); + for i in 0..nibble_array.len() / 2 { + let first = nibble_array[i * 2]; + let second = nibble_array[i * 2 + 1]; + nibble_data.push((first << 4) + second); + } + let mut key = NibbleSlice::new_offset(&nibble_data, if odd { 1 } else { 0 }); + + // The rest of the logic is very similar to the standard lookup() function, except + // we return the raw node and don't expect to hit a leaf. + let mut node = self.retrieve_raw_node(&self.root, true)?; + while !key.is_empty() { + match node { + Some((_, raw_node)) => match raw_node.node { + RawTrieNode::Leaf(_, _) => { + return Ok(None); + } + RawTrieNode::BranchNoValue(children) + | RawTrieNode::BranchWithValue(_, children) => { + let child = children[key.at(0)]; + match child { + Some(child) => { + node = self.retrieve_raw_node(&child, true)?; + key = key.mid(1); + } + None => return Ok(None), + } + } + RawTrieNode::Extension(existing_key, child) => { + let existing_key = NibbleSlice::from_encoded(&existing_key).0; + if key.starts_with(&existing_key) { + node = self.retrieve_raw_node(&child, true)?; + key = key.mid(existing_key.len()); + } else { + return Ok(None); + } + } + }, + None => return Ok(None), + } + } + match node { + Some((_, raw_node)) => Ok(Some(raw_node.node)), + None => Ok(None), + } + } + + /// Returns the raw bytes corresponding to a ValueRef that came from a node with + /// value (either Leaf or BranchWithValue). + pub fn retrieve_value(&self, hash: &CryptoHash) -> Result, StorageError> { + let bytes = self.internal_retrieve_trie_node(hash, true)?; + Ok(bytes.to_vec()) + } + + /// Retrieves an `OptimizedValueRef`` for the given key. See `OptimizedValueRef`. + /// + /// `mode`: whether we will try to perform the lookup through flat storage or trie. + /// Note that even if `mode == KeyLookupMode::FlatStorage`, we still may not use + /// flat storage if the trie is not created with a flat storage object in it. + /// Such double check may seem redundant but it is necessary for now. + /// Not all tries are created with flat storage, for example, we don't + /// enable flat storage for state-viewer. And we do not use flat + /// storage for key lookup performed in `storage_write`, so we need + /// the `use_flat_storage` to differentiate whether the lookup is performed for + /// storage_write or not. + pub fn get_optimized_ref( + &self, + key: &[u8], + mode: KeyLookupMode, + ) -> Result, StorageError> { + let charge_gas_for_trie_node_access = + mode == KeyLookupMode::Trie || self.charge_gas_for_trie_node_access; + if self.memtries.is_some() { + self.lookup_from_memory(key, charge_gas_for_trie_node_access) + } else if mode == KeyLookupMode::FlatStorage && self.flat_storage_chunk_view.is_some() { + self.lookup_from_flat_storage(key) + } else { + Ok(self + .lookup_from_state_column(NibbleSlice::new(key), charge_gas_for_trie_node_access)? + .map(OptimizedValueRef::Ref)) + } + } + + /// Dereferences an `OptimizedValueRef` into the full value, and properly + /// accounts for the gas, caching, and recording (if enabled). This may or + /// may not incur a on-disk lookup, depending on whether the + /// `OptimizedValueRef` contains an already available value. + pub fn deref_optimized( + &self, + optimized_value_ref: &OptimizedValueRef, + ) -> Result, StorageError> { + match optimized_value_ref { + OptimizedValueRef::Ref(value_ref) => self.retrieve_value(&value_ref.hash), + OptimizedValueRef::AvailableValue(ValueAccessToken { value }) => { + let value_hash = hash(value); + let arc_value: Arc<[u8]> = value.clone().into(); + self.accounting_cache + .borrow_mut() + .retroactively_account(value_hash, arc_value.clone()); + if let Some(recorder) = &self.recorder { + recorder.borrow_mut().record(&value_hash, arc_value); + } + Ok(value.clone()) + } + } + } + + /// Retrieves the full value for the given key. + pub fn get(&self, key: &[u8]) -> Result>, StorageError> { + match self.get_optimized_ref(key, KeyLookupMode::FlatStorage)? { + Some(optimized_ref) => Ok(Some(self.deref_optimized(&optimized_ref)?)), + None => Ok(None), + } + } + + pub fn update(&self, changes: I) -> Result + where + I: IntoIterator, Option>)>, + { + match &self.memtries { + Some(memtries) => { + // If we have in-memory tries, use it to construct the the changes entirely (for + // both in-memory and on-disk updates) because it's much faster. + let guard = memtries.read().unwrap(); + let mut trie_update = guard.update(self.root, true)?; + for (key, value) in changes { + match value { + Some(arr) => { + trie_update.insert(&key, arr); + } + None => trie_update.delete(&key), + } + } + Ok(trie_update.to_trie_changes()) + } + None => { + let mut memory = NodesStorage::new(); + let mut root_node = self.move_node_to_mutable(&mut memory, &self.root)?; + for (key, value) in changes { + let key = NibbleSlice::new(&key); + root_node = match value { + Some(arr) => self.insert(&mut memory, root_node, key, arr), + None => self.delete(&mut memory, root_node, key), + }?; + } + + #[cfg(test)] + { + self.memory_usage_verify(&memory, NodeHandle::InMemory(root_node)); + } + Trie::flatten_nodes(&self.root, memory, root_node) + } + } + } + + pub fn iter<'a>(&'a self) -> Result, StorageError> { + TrieIterator::new(self, None) + } + + pub fn iter_with_max_depth<'a>( + &'a self, + max_depth: usize, + ) -> Result, StorageError> { + TrieIterator::new( + self, + Some(Box::new(move |key_nibbles: &Vec| key_nibbles.len() > max_depth)), + ) + } + + pub fn iter_with_prune_condition<'a>( + &'a self, + prune_condition: Option) -> bool>>, + ) -> Result, StorageError> { + TrieIterator::new(self, prune_condition) + } + + pub fn get_trie_nodes_count(&self) -> TrieNodesCount { + self.accounting_cache.borrow().get_trie_nodes_count() + } +} + +impl TrieAccess for Trie { + fn get(&self, key: &TrieKey) -> Result>, StorageError> { + Trie::get(self, &key.to_vec()) + } +} + +/// Methods used in the runtime-parameter-estimator for measuring trie internal +/// operations. +pub mod estimator { + use borsh::BorshDeserialize; + use unc_primitives::hash::CryptoHash; + + /// Create an encoded extension node with the given value as the key. + /// This serves no purpose other than for the estimator. + pub fn encode_extension_node(key: Vec) -> Vec { + let hash = CryptoHash::hash_bytes(&key); + let node = super::RawTrieNode::Extension(key, hash); + let node = super::RawTrieNodeWithSize { node, memory_usage: 1 }; + borsh::to_vec(&node).unwrap() + } + /// Decode am extension node and return its inner key. + /// This serves no purpose other than for the estimator. + pub fn decode_extension_node(bytes: &[u8]) -> Vec { + let node = super::RawTrieNodeWithSize::try_from_slice(bytes).unwrap(); + match node.node { + super::RawTrieNode::Extension(v, _) => v, + _ => unreachable!(), + } + } +} + +#[cfg(test)] +mod tests { + use assert_matches::assert_matches; + use rand::Rng; + + use crate::test_utils::{ + create_test_store, gen_changes, simplify_changes, test_populate_trie, TestTriesBuilder, + }; + use crate::{DBCol, MissingTrieValueContext}; + + use super::*; + + type TrieChanges = Vec<(Vec, Option>)>; + const SHARD_VERSION: u32 = 1; + + fn test_clear_trie( + tries: &ShardTries, + root: &CryptoHash, + shard_uid: ShardUId, + changes: TrieChanges, + ) -> CryptoHash { + let delete_changes: TrieChanges = + changes.iter().map(|(key, _)| (key.clone(), None)).collect(); + let trie_changes = + tries.get_trie_for_shard(shard_uid, *root).update(delete_changes).unwrap(); + let mut store_update = tries.store_update(); + let root = tries.apply_all(&trie_changes, shard_uid, &mut store_update); + let trie = tries.get_trie_for_shard(shard_uid, root); + store_update.commit().unwrap(); + for (key, _) in changes { + assert_eq!(trie.get(&key), Ok(None)); + } + root + } + + #[test] + fn test_basic_trie() { + // test trie version > 0 + let tries = TestTriesBuilder::new().with_shard_layout(SHARD_VERSION, 2).build(); + let shard_uid = ShardUId { version: SHARD_VERSION, shard_id: 0 }; + let trie = tries.get_trie_for_shard(shard_uid, Trie::EMPTY_ROOT); + assert_eq!(trie.get(&[122]), Ok(None)); + let changes = vec![ + (b"doge".to_vec(), Some(b"coin".to_vec())), + (b"docu".to_vec(), Some(b"value".to_vec())), + (b"do".to_vec(), Some(b"verb".to_vec())), + (b"horse".to_vec(), Some(b"stallion".to_vec())), + (b"dog".to_vec(), Some(b"puppy".to_vec())), + (b"h".to_vec(), Some(b"value".to_vec())), + ]; + let root = test_populate_trie(&tries, &Trie::EMPTY_ROOT, shard_uid, changes.clone()); + let new_root = test_clear_trie(&tries, &root, shard_uid, changes); + assert_eq!(new_root, Trie::EMPTY_ROOT); + assert_eq!(trie.iter().unwrap().fold(0, |acc, _| acc + 1), 0); + } + + #[test] + fn test_trie_iter() { + let tries = TestTriesBuilder::new().with_shard_layout(SHARD_VERSION, 2).build(); + let shard_uid = ShardUId { version: SHARD_VERSION, shard_id: 0 }; + let pairs = vec![ + (b"a".to_vec(), Some(b"111".to_vec())), + (b"b".to_vec(), Some(b"222".to_vec())), + (b"x".to_vec(), Some(b"333".to_vec())), + (b"y".to_vec(), Some(b"444".to_vec())), + ]; + let root = test_populate_trie(&tries, &Trie::EMPTY_ROOT, shard_uid, pairs.clone()); + let trie = tries.get_trie_for_shard(shard_uid, root); + let mut iter_pairs = vec![]; + for pair in trie.iter().unwrap() { + let (key, value) = pair.unwrap(); + iter_pairs.push((key, Some(value.to_vec()))); + } + assert_eq!(pairs, iter_pairs); + + let assert_has_next = |want, other_iter: &mut TrieIterator| { + assert_eq!(Some(want), other_iter.next().map(|item| item.unwrap().0).as_deref()); + }; + + let mut other_iter = trie.iter().unwrap(); + other_iter.seek_prefix(b"r").unwrap(); + assert_eq!(other_iter.next(), None); + other_iter.seek_prefix(b"x").unwrap(); + assert_has_next(b"x", &mut other_iter); + assert_eq!(other_iter.next(), None); + other_iter.seek_prefix(b"y").unwrap(); + assert_has_next(b"y", &mut other_iter); + assert_eq!(other_iter.next(), None); + } + + #[test] + fn test_trie_leaf_into_branch() { + let tries = TestTriesBuilder::new().with_shard_layout(SHARD_VERSION, 2).build(); + let shard_uid = ShardUId { version: SHARD_VERSION, shard_id: 0 }; + let changes = vec![ + (b"dog".to_vec(), Some(b"puppy".to_vec())), + (b"dog2".to_vec(), Some(b"puppy".to_vec())), + (b"xxx".to_vec(), Some(b"puppy".to_vec())), + ]; + test_populate_trie(&tries, &Trie::EMPTY_ROOT, shard_uid, changes); + } + + #[test] + fn test_trie_same_node() { + let tries = TestTriesBuilder::new().build(); + let changes = vec![ + (b"dogaa".to_vec(), Some(b"puppy".to_vec())), + (b"dogbb".to_vec(), Some(b"puppy".to_vec())), + (b"cataa".to_vec(), Some(b"puppy".to_vec())), + (b"catbb".to_vec(), Some(b"puppy".to_vec())), + (b"dogax".to_vec(), Some(b"puppy".to_vec())), + ]; + test_populate_trie(&tries, &Trie::EMPTY_ROOT, ShardUId::single_shard(), changes); + } + + #[test] + fn test_trie_iter_seek_stop_at_extension() { + let tries = TestTriesBuilder::new().build(); + let changes = vec![ + (vec![0, 116, 101, 115, 116], Some(vec![0])), + (vec![2, 116, 101, 115, 116], Some(vec![0])), + ( + vec![ + 0, 116, 101, 115, 116, 44, 98, 97, 108, 97, 110, 99, 101, 115, 58, 98, 111, 98, + 46, 110, 101, 97, 114, + ], + Some(vec![0]), + ), + ( + vec![ + 0, 116, 101, 115, 116, 44, 98, 97, 108, 97, 110, 99, 101, 115, 58, 110, 117, + 108, 108, + ], + Some(vec![0]), + ), + ]; + let root = test_populate_trie(&tries, &Trie::EMPTY_ROOT, ShardUId::single_shard(), changes); + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), root); + let mut iter = trie.iter().unwrap(); + iter.seek_prefix(&[0, 116, 101, 115, 116, 44]).unwrap(); + let mut pairs = vec![]; + for pair in iter { + pairs.push(pair.unwrap().0); + } + assert_eq!( + pairs, + [ + vec![ + 0, 116, 101, 115, 116, 44, 98, 97, 108, 97, 110, 99, 101, 115, 58, 98, 111, 98, + 46, 110, 101, 97, 114 + ], + vec![ + 0, 116, 101, 115, 116, 44, 98, 97, 108, 97, 110, 99, 101, 115, 58, 110, 117, + 108, 108 + ], + ] + ); + } + + #[test] + fn test_trie_remove_non_existent_key() { + let tries = TestTriesBuilder::new().build(); + let initial = vec![ + (vec![99, 44, 100, 58, 58, 49], Some(vec![1])), + (vec![99, 44, 100, 58, 58, 50], Some(vec![1])), + (vec![99, 44, 100, 58, 58, 50, 51], Some(vec![1])), + ]; + let root = test_populate_trie(&tries, &Trie::EMPTY_ROOT, ShardUId::single_shard(), initial); + + let changes = vec![ + (vec![99, 44, 100, 58, 58, 45, 49], None), + (vec![99, 44, 100, 58, 58, 50, 52], None), + ]; + let root = test_populate_trie(&tries, &root, ShardUId::single_shard(), changes); + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), root); + for r in trie.iter().unwrap() { + r.unwrap(); + } + } + + #[test] + fn test_equal_leafs() { + let initial = vec![ + (vec![1, 2, 3], Some(vec![1])), + (vec![2, 2, 3], Some(vec![1])), + (vec![3, 2, 3], Some(vec![1])), + ]; + let tries = TestTriesBuilder::new().build(); + let root = test_populate_trie(&tries, &Trie::EMPTY_ROOT, ShardUId::single_shard(), initial); + tries.get_trie_for_shard(ShardUId::single_shard(), root).iter().unwrap().for_each( + |result| { + result.unwrap(); + }, + ); + + let changes = vec![(vec![1, 2, 3], None)]; + let root = test_populate_trie(&tries, &root, ShardUId::single_shard(), changes); + tries.get_trie_for_shard(ShardUId::single_shard(), root).iter().unwrap().for_each( + |result| { + result.unwrap(); + }, + ); + } + + #[test] + fn test_trie_unique() { + let mut rng = rand::thread_rng(); + for _ in 0..100 { + let tries = TestTriesBuilder::new().build(); + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), Trie::EMPTY_ROOT); + let trie_changes = gen_changes(&mut rng, 20); + let simplified_changes = simplify_changes(&trie_changes); + + let trie_changes1 = trie.update(trie_changes.iter().cloned()).unwrap(); + let trie_changes2 = trie.update(simplified_changes.iter().cloned()).unwrap(); + if trie_changes1.new_root != trie_changes2.new_root { + eprintln!("{:?}", trie_changes); + eprintln!("{:?}", simplified_changes); + eprintln!("root1: {:?}", trie_changes1.new_root); + eprintln!("root2: {:?}", trie_changes2.new_root); + panic!("MISMATCH!"); + } + // TODO: compare state updates? + } + } + + #[test] + fn test_iterator_seek_prefix() { + let mut rng = rand::thread_rng(); + for _test_run in 0..10 { + let tries = TestTriesBuilder::new().build(); + let trie_changes = gen_changes(&mut rng, 500); + let state_root = test_populate_trie( + &tries, + &Trie::EMPTY_ROOT, + ShardUId::single_shard(), + trie_changes.clone(), + ); + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), state_root); + + // Those known keys. + for (key, value) in trie_changes.into_iter().collect::>() { + if let Some(value) = value { + let want = Some(Ok((key.clone(), value))); + let mut iterator = trie.iter().unwrap(); + iterator.seek_prefix(&key).unwrap(); + assert_eq!(want, iterator.next(), "key: {key:x?}"); + } + } + + // Test some more random keys. + let queries = gen_changes(&mut rng, 500).into_iter().map(|(key, _)| key); + for query in queries { + let mut iterator = trie.iter().unwrap(); + iterator.seek_prefix(&query).unwrap(); + if let Some(Ok((key, _))) = iterator.next() { + assert!(key.starts_with(&query), "‘{key:x?}’ does not start with ‘{query:x?}’"); + } + } + } + } + + #[test] + fn test_refcounts() { + let mut rng = rand::thread_rng(); + for _test_run in 0..10 { + let num_iterations = rng.gen_range(1..20); + let tries = TestTriesBuilder::new().build(); + let store = tries.get_store(); + let mut state_root = Trie::EMPTY_ROOT; + for _ in 0..num_iterations { + let trie_changes = gen_changes(&mut rng, 20); + state_root = + test_populate_trie(&tries, &state_root, ShardUId::single_shard(), trie_changes); + let memory_usage = tries + .get_trie_for_shard(ShardUId::single_shard(), state_root) + .retrieve_root_node() + .unwrap() + .memory_usage; + println!("New memory_usage: {memory_usage}"); + } + + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), state_root); + let trie_changes = trie + .iter() + .unwrap() + .map(|item| { + let (key, _) = item.unwrap(); + (key, None) + }) + .collect::>(); + state_root = + test_populate_trie(&tries, &state_root, ShardUId::single_shard(), trie_changes); + assert_eq!(state_root, Trie::EMPTY_ROOT, "Trie must be empty"); + assert!(store.iter(DBCol::State).peekable().peek().is_none(), "Storage must be empty"); + } + } + + #[test] + fn test_trie_restart() { + let store = create_test_store(); + let tries = TestTriesBuilder::new().with_store(store.clone()).build(); + let empty_root = Trie::EMPTY_ROOT; + let changes = vec![ + (b"doge".to_vec(), Some(b"coin".to_vec())), + (b"docu".to_vec(), Some(b"value".to_vec())), + (b"do".to_vec(), Some(b"verb".to_vec())), + (b"horse".to_vec(), Some(b"stallion".to_vec())), + (b"dog".to_vec(), Some(b"puppy".to_vec())), + (b"h".to_vec(), Some(b"value".to_vec())), + ]; + let root = test_populate_trie(&tries, &empty_root, ShardUId::single_shard(), changes); + + let tries2 = TestTriesBuilder::new().with_store(store).build(); + let trie2 = tries2.get_trie_for_shard(ShardUId::single_shard(), root); + assert_eq!(trie2.get(b"doge"), Ok(Some(b"coin".to_vec()))); + } + + // TODO: somehow also test that we don't record unnecessary nodes + #[test] + fn test_trie_recording_reads() { + let tries = TestTriesBuilder::new().build(); + let empty_root = Trie::EMPTY_ROOT; + let changes = vec![ + (b"doge".to_vec(), Some(b"coin".to_vec())), + (b"docu".to_vec(), Some(b"value".to_vec())), + (b"do".to_vec(), Some(b"verb".to_vec())), + (b"horse".to_vec(), Some(b"stallion".to_vec())), + (b"dog".to_vec(), Some(b"puppy".to_vec())), + (b"h".to_vec(), Some(b"value".to_vec())), + ]; + let root = test_populate_trie(&tries, &empty_root, ShardUId::single_shard(), changes); + + let trie2 = tries.get_trie_for_shard(ShardUId::single_shard(), root).recording_reads(); + trie2.get(b"dog").unwrap(); + trie2.get(b"horse").unwrap(); + let partial_storage = trie2.recorded_storage(); + + let trie3 = Trie::from_recorded_storage(partial_storage.unwrap(), root, false); + + assert_eq!(trie3.get(b"dog"), Ok(Some(b"puppy".to_vec()))); + assert_eq!(trie3.get(b"horse"), Ok(Some(b"stallion".to_vec()))); + assert_matches!( + trie3.get(b"doge"), + Err(StorageError::MissingTrieValue( + MissingTrieValueContext::TrieMemoryPartialStorage, + _ + )) + ); + } + + #[test] + fn test_trie_recording_reads_update() { + let tries = TestTriesBuilder::new().build(); + let empty_root = Trie::EMPTY_ROOT; + let changes = vec![ + (b"doge".to_vec(), Some(b"coin".to_vec())), + (b"docu".to_vec(), Some(b"value".to_vec())), + ]; + let root = test_populate_trie(&tries, &empty_root, ShardUId::single_shard(), changes); + // Trie: extension -> branch -> 2 leaves + { + let trie2 = tries.get_trie_for_shard(ShardUId::single_shard(), root).recording_reads(); + trie2.get(b"doge").unwrap(); + // record extension, branch and one leaf with value, but not the other + assert_eq!(trie2.recorded_storage().unwrap().nodes.len(), 4); + } + + { + let trie2 = tries.get_trie_for_shard(ShardUId::single_shard(), root).recording_reads(); + let updates = vec![(b"doge".to_vec(), None)]; + trie2.update(updates).unwrap(); + // record extension, branch and both leaves (one with value) + assert_eq!(trie2.recorded_storage().unwrap().nodes.len(), 5); + } + + { + let trie2 = tries.get_trie_for_shard(ShardUId::single_shard(), root).recording_reads(); + let updates = vec![(b"dodo".to_vec(), Some(b"asdf".to_vec()))]; + trie2.update(updates).unwrap(); + // record extension and branch, but not leaves + assert_eq!(trie2.recorded_storage().unwrap().nodes.len(), 2); + } + } + + #[test] + fn test_dump_load_trie() { + let store = create_test_store(); + let tries = TestTriesBuilder::new().with_store(store.clone()).build(); + let empty_root = Trie::EMPTY_ROOT; + let changes = vec![ + (b"doge".to_vec(), Some(b"coin".to_vec())), + (b"docu".to_vec(), Some(b"value".to_vec())), + ]; + let root = test_populate_trie(&tries, &empty_root, ShardUId::single_shard(), changes); + let dir = tempfile::Builder::new().prefix("test_dump_load_trie").tempdir().unwrap(); + store.save_state_to_file(&dir.path().join("test.bin")).unwrap(); + let store2 = create_test_store(); + store2.load_state_from_file(&dir.path().join("test.bin")).unwrap(); + let tries2 = TestTriesBuilder::new().with_store(store2).build(); + let trie2 = tries2.get_trie_for_shard(ShardUId::single_shard(), root); + assert_eq!(trie2.get(b"doge").unwrap().unwrap(), b"coin"); + } +} + +#[cfg(test)] +mod borsh_compatibility_test { + use borsh::{BorshDeserialize, BorshSerialize}; + use unc_primitives::hash::{hash, CryptoHash}; + use unc_primitives::types::StateRoot; + + use crate::trie::{TrieRefcountAddition, TrieRefcountSubtraction}; + use crate::TrieChanges; + + #[test] + fn test_trie_changes_compatibility() { + #[derive(BorshSerialize)] + struct LegacyTrieRefcountChange { + trie_node_or_value_hash: CryptoHash, + trie_node_or_value: Vec, + rc: std::num::NonZeroU32, + } + + #[derive(BorshSerialize)] + struct LegacyTrieChanges { + old_root: StateRoot, + new_root: StateRoot, + insertions: Vec, + deletions: Vec, + } + + let changes = LegacyTrieChanges { + old_root: hash(b"a"), + new_root: hash(b"b"), + insertions: vec![LegacyTrieRefcountChange { + trie_node_or_value_hash: hash(b"c"), + trie_node_or_value: b"d".to_vec(), + rc: std::num::NonZeroU32::new(1).unwrap(), + }], + deletions: vec![LegacyTrieRefcountChange { + trie_node_or_value_hash: hash(b"e"), + trie_node_or_value: b"f".to_vec(), + rc: std::num::NonZeroU32::new(2).unwrap(), + }], + }; + + let serialized = borsh::to_vec(&changes).unwrap(); + let deserialized = TrieChanges::try_from_slice(&serialized).unwrap(); + assert_eq!( + deserialized, + TrieChanges { + old_root: hash(b"a"), + new_root: hash(b"b"), + insertions: vec![TrieRefcountAddition { + trie_node_or_value_hash: hash(b"c"), + trie_node_or_value: b"d".to_vec(), + rc: std::num::NonZeroU32::new(1).unwrap(), + }], + deletions: vec![TrieRefcountSubtraction::new( + hash(b"e"), + std::num::NonZeroU32::new(2).unwrap(), + )], + mem_trie_changes: None, + } + ); + } +} diff --git a/core/store/src/trie/nibble_slice.rs b/core/store/src/trie/nibble_slice.rs new file mode 100644 index 000000000..9e945fe7c --- /dev/null +++ b/core/store/src/trie/nibble_slice.rs @@ -0,0 +1,370 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. + +use elastic_array::ElasticArray36; +use std::cmp::*; +use std::fmt; + +/// Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. +/// +/// This is an immutable struct. No operations actually change it. +/// +/// # Example +/// ```snippet +/// use patricia_trie::nibbleslice::NibbleSlice; +/// { +/// let d1 = &[0x01u8, 0x23, 0x45]; +/// let d2 = &[0x34u8, 0x50, 0x12]; +/// let d3 = &[0x00u8, 0x12]; +/// let n1 = NibbleSlice::new(d1); // 0,1,2,3,4,5 +/// let n2 = NibbleSlice::new(d2); // 3,4,5,0,1,2 +/// let n3 = NibbleSlice::new_offset(d3, 1); // 0,1,2 +/// assert!(n1 > n3); // 0,1,2,... > 0,1,2 +/// assert!(n1 < n2); // 0,... < 3,... +/// assert!(n2.mid(3) == n3); // 0,1,2 == 0,1,2 +/// assert!(n1.starts_with(&n3)); +/// assert_eq!(n1.common_prefix(&n3), 3); +/// assert_eq!(n2.mid(3).common_prefix(&n1), 3); +/// } +/// ``` +#[derive(Copy, Clone, Eq)] +pub struct NibbleSlice<'a> { + data: &'a [u8], + offset: usize, +} + +/// Iterator type for a nibble slice. +pub struct NibbleSliceIterator<'a> { + p: &'a NibbleSlice<'a>, + i: usize, +} + +impl Iterator for NibbleSliceIterator<'_> { + type Item = u8; + + fn next(&mut self) -> Option { + self.i += 1; + if self.i <= self.p.len() { + Some(self.p.at(self.i - 1)) + } else { + None + } + } +} + +impl<'a> NibbleSlice<'a> { + /// Create a new nibble slice with the given byte-slice. + pub fn new(data: &'a [u8]) -> Self { + NibbleSlice::new_offset(data, 0) + } + + /// Create a new nibble slice with the given byte-slice with a nibble offset. + pub fn new_offset(data: &'a [u8], offset: usize) -> Self { + NibbleSlice { data, offset } + } + + /// Get an iterator for the series of nibbles. + pub fn iter(&'a self) -> NibbleSliceIterator<'a> { + NibbleSliceIterator { p: self, i: 0 } + } + + /// Create a new nibble slice from the given HPE encoded data (e.g. output of `encoded()`). + pub fn from_encoded(data: &'a [u8]) -> (Self, bool) { + (Self::new_offset(data, if data[0] & 16 == 16 { 1 } else { 2 }), data[0] & 32 == 32) + } + + /// Is this an empty slice? + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Get the length (in nibbles, naturally) of this slice. + #[inline] + pub fn len(&self) -> usize { + self.data.len() * 2 - self.offset + } + + /// Get the nibble at position `i`. + #[inline(always)] + pub fn at(&self, i: usize) -> u8 { + if (self.offset + i) & 1 == 1 { + self.data[(self.offset + i) / 2] & 15u8 + } else { + self.data[(self.offset + i) / 2] >> 4 + } + } + + /// Return object which represents a view on to this slice (further) offset by `i` nibbles. + pub fn mid(&self, i: usize) -> Self { + NibbleSlice { data: self.data, offset: self.offset + i } + } + + /// Do we start with the same nibbles as the whole of `them`? + pub fn starts_with(&self, them: &Self) -> bool { + self.common_prefix(them) == them.len() + } + + /// How many of the same nibbles at the beginning do we match with `them`? + pub fn common_prefix(&self, them: &Self) -> usize { + let s = min(self.len(), them.len()); + for i in 0..s { + if self.at(i) != them.at(i) { + return i; + } + } + s + } + + /// Encode while nibble slice in prefixed hex notation, noting whether it `is_leaf`. + #[inline] + pub fn encode_nibbles(nibbles: &[u8], is_leaf: bool) -> ElasticArray36 { + let l = nibbles.len(); + let mut r = ElasticArray36::new(); + let mut i = l % 2; + r.push(if i == 1 { 0x10 + nibbles[0] } else { 0 } + if is_leaf { 0x20 } else { 0 }); + while i < l { + r.push(nibbles[i] * 16 + nibbles[i + 1]); + i += 2; + } + r + } + + /// Encode while nibble slice in prefixed hex notation, noting whether it `is_leaf`. + #[inline] + pub fn encoded(&self, is_leaf: bool) -> ElasticArray36 { + let l = self.len(); + let mut r = ElasticArray36::new(); + let mut i = l % 2; + r.push(if i == 1 { 0x10 + self.at(0) } else { 0 } + if is_leaf { 0x20 } else { 0 }); + while i < l { + r.push(self.at(i) * 16 + self.at(i + 1)); + i += 2; + } + r + } + + pub fn merge_encoded(&self, other: &Self, is_leaf: bool) -> ElasticArray36 { + let l = self.len() + other.len(); + let mut r = ElasticArray36::new(); + let mut i = l % 2; + r.push(if i == 1 { 0x10 + self.at(0) } else { 0 } + if is_leaf { 0x20 } else { 0 }); + while i < l { + let bit1 = if i < self.len() { self.at(i) } else { other.at(i - self.len()) }; + let bit2 = if i + 1 < l { + if i + 1 < self.len() { + self.at(i + 1) + } else { + other.at(i + 1 - self.len()) + } + } else { + 0 + }; + + r.push(bit1 * 16 + bit2); + i += 2; + } + r + } + + /// Encode only the leftmost `n` bytes of the nibble slice in prefixed hex notation, + /// noting whether it `is_leaf`. + pub fn encoded_leftmost(&self, n: usize, is_leaf: bool) -> ElasticArray36 { + let l = min(self.len(), n); + let mut r = ElasticArray36::new(); + let mut i = l % 2; + r.push(if i == 1 { 0x10 + self.at(0) } else { 0 } + if is_leaf { 0x20 } else { 0 }); + while i < l { + r.push(self.at(i) * 16 + self.at(i + 1)); + i += 2; + } + r + } + + // Helper to convert nibbles to bytes. + pub fn nibbles_to_bytes(nibbles: &[u8]) -> Vec { + assert_eq!(nibbles.len() % 2, 0); + let encoded = NibbleSlice::encode_nibbles(&nibbles, false); + // Ignore first element returned by `encode_nibbles` because it contains only + // `is_leaf` info for even length. + encoded[1..].to_vec() + } +} + +impl PartialEq for NibbleSlice<'_> { + fn eq(&self, them: &Self) -> bool { + self.len() == them.len() && self.starts_with(them) + } +} + +impl Ord for NibbleSlice<'_> { + fn cmp(&self, them: &Self) -> Ordering { + let s = min(self.len(), them.len()); + for i in 0..s { + match self.at(i).cmp(&them.at(i)) { + Ordering::Less => return Ordering::Less, + Ordering::Greater => return Ordering::Greater, + _ => {} + } + } + self.len().cmp(&them.len()) + } +} + +impl PartialOrd for NibbleSlice<'_> { + fn partial_cmp(&self, them: &Self) -> Option { + Some(self.cmp(them)) + } +} + +impl fmt::Debug for NibbleSlice<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.is_empty() { + return Ok(()); + } + write!(f, "{:01x}", self.at(0))?; + for i in 1..self.len() { + write!(f, "'{:01x}", self.at(i))?; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::NibbleSlice; + use elastic_array::ElasticArray36; + use rand::{thread_rng, Rng}; + + static D: &[u8; 3] = &[0x01u8, 0x23, 0x45]; + + #[test] + fn basics() { + let n = NibbleSlice::new(D); + assert_eq!(n.len(), 6); + assert!(!n.is_empty()); + + let n = NibbleSlice::new_offset(D, 6); + assert!(n.is_empty()); + + let n = NibbleSlice::new_offset(D, 3); + assert_eq!(n.len(), 3); + for i in 0..3 { + assert_eq!(n.at(i), i as u8 + 3); + } + } + + #[test] + fn iterator() { + let n = NibbleSlice::new(D); + let mut nibbles: Vec = vec![]; + nibbles.extend(n.iter()); + assert_eq!(nibbles, (0u8..6).collect::>()) + } + + #[test] + fn mid() { + let n = NibbleSlice::new(D); + let m = n.mid(2); + for i in 0..4 { + assert_eq!(m.at(i), i as u8 + 2); + } + let m = n.mid(3); + for i in 0..3 { + assert_eq!(m.at(i), i as u8 + 3); + } + } + + #[test] + fn encoded() { + let n = NibbleSlice::new(D); + assert_eq!(n.encoded(false), ElasticArray36::from_slice(&[0x00, 0x01, 0x23, 0x45])); + assert_eq!(n.encoded(true), ElasticArray36::from_slice(&[0x20, 0x01, 0x23, 0x45])); + assert_eq!(n.mid(1).encoded(false), ElasticArray36::from_slice(&[0x11, 0x23, 0x45])); + assert_eq!(n.mid(1).encoded(true), ElasticArray36::from_slice(&[0x31, 0x23, 0x45])); + } + + #[test] + fn from_encoded() { + let n = NibbleSlice::new(D); + assert_eq!((n, false), NibbleSlice::from_encoded(&[0x00, 0x01, 0x23, 0x45])); + assert_eq!((n, true), NibbleSlice::from_encoded(&[0x20, 0x01, 0x23, 0x45])); + assert_eq!((n.mid(1), false), NibbleSlice::from_encoded(&[0x11, 0x23, 0x45])); + assert_eq!((n.mid(1), true), NibbleSlice::from_encoded(&[0x31, 0x23, 0x45])); + } + + fn encode_decode(nibbles: &[u8], is_leaf: bool) { + let n = NibbleSlice::encode_nibbles(nibbles, is_leaf); + let (n, is_leaf_decoded) = NibbleSlice::from_encoded(&n); + assert_eq!(&n.iter().collect::>(), nibbles); + assert_eq!(is_leaf_decoded, is_leaf) + } + + #[test] + fn test_encode_decode() { + encode_decode(&[15u8], false); + encode_decode(&[0u8], false); + encode_decode(&[15u8], true); + encode_decode(&[0u8], true); + let mut rng = thread_rng(); + for _ in 0..100 { + let l = rng.gen_range(0..10); + let nibbles: Vec<_> = (0..l).map(|_| rng.gen_range(0..16) as u8).collect(); + encode_decode(&nibbles, true); + encode_decode(&nibbles, false); + } + } + + #[test] + fn shared() { + let n = NibbleSlice::new(D); + + let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45, 0x67]; + let m = NibbleSlice::new(other); + + assert_eq!(n.common_prefix(&m), 4); + assert_eq!(m.common_prefix(&n), 4); + assert_eq!(n.mid(1).common_prefix(&m.mid(1)), 3); + assert_eq!(n.mid(1).common_prefix(&m.mid(2)), 0); + assert_eq!(n.common_prefix(&m.mid(4)), 6); + assert!(!n.starts_with(&m.mid(4))); + assert!(m.mid(4).starts_with(&n)); + } + + #[test] + fn compare() { + let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45]; + let n = NibbleSlice::new(D); + let m = NibbleSlice::new(other); + + assert!(n != m); + assert!(n > m); + assert!(m < n); + + assert!(n == m.mid(4)); + assert!(n >= m.mid(4)); + assert!(n <= m.mid(4)); + } + + #[test] + fn nibble_indexing() { + let encoded = vec![32, 116, 101, 115, 116]; + let n = NibbleSlice::from_encoded(&encoded).0; + let nibbles: Vec = (0..n.len()).map(|i| n.at(i)).collect(); + assert_eq!(nibbles, vec![7, 4, 6, 5, 7, 3, 7, 4]); + } +} diff --git a/core/store/src/trie/prefetching_trie_storage.rs b/core/store/src/trie/prefetching_trie_storage.rs new file mode 100644 index 000000000..34a49be7a --- /dev/null +++ b/core/store/src/trie/prefetching_trie_storage.rs @@ -0,0 +1,608 @@ +use crate::sync_utils::Monitor; +use crate::{ + metrics, DBCol, MissingTrieValueContext, StorageError, Store, Trie, TrieCache, + TrieCachingStorage, TrieConfig, TrieStorage, +}; +use crossbeam::select; +use unc_o11y::metrics::prometheus; +use unc_o11y::metrics::prometheus::core::GenericGauge; +use unc_o11y::tracing::error; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::{AccountId, ShardId, StateRoot}; +use std::collections::HashMap; +use std::rc::Rc; +use std::sync::Arc; +use std::thread; + +const MAX_QUEUED_WORK_ITEMS: usize = 16 * 1024; +const MAX_PREFETCH_STAGING_MEMORY: usize = 200 * 1024 * 1024; +/// How much memory capacity is reserved for each prefetch request before +/// sending it. Once the value is fetched, the actual size is used instead. +/// Set to 4MiB, the same as `max_length_storage_value`. +const PREFETCH_RESERVED_BYTES_PER_SLOT: usize = 4 * 1024 * 1024; +/// How many threads will be prefetching data, without the scheduler thread. +/// Because the storage driver is blocking, there is only one request per thread +/// at a time. +const NUM_IO_THREADS: usize = 8; + +/// Storage used by I/O threads to prefetch data. +/// +/// This implements `TrieStorage` and therefore can be used inside a `Trie`. +/// Prefetching runs through the normal trie lookup code and only the backing +/// trie storage behaves differently. +/// +/// `TriePrefetchingStorage` instances are always linked to a parent +/// `TrieCachingStorage`. They share a shard cache, to avoid reading anything +/// from the DB that is already cached. +/// They communicate through `PrefetchStagingArea` exclusively. +/// +/// Each I/O threads will have its own copy of `TriePrefetchingStorage`, so +/// this should remain a cheap object. +/// +/// Please note that this should only be used from the background threads +/// performing prefetching. The main thread uses `PrefetchStagingArea` +/// directly to look up prefetched values before going to the database. +#[derive(Clone)] +struct TriePrefetchingStorage { + /// Store is shared with parent `TrieCachingStorage`. + store: Store, + shard_uid: ShardUId, + /// Shard cache is shared with parent `TrieCachingStorage`. But the + /// pre-fetcher uses this in read-only mode to avoid premature evictions. + shard_cache: TrieCache, + /// Shared with parent `TrieCachingStorage`. + prefetching: PrefetchStagingArea, +} + +/// This type is shared between runtime crate and store crate. +/// +/// The former puts requests in, the latter serves requests. +/// With this API, the store does not know about receipts etc, and the runtime +/// does not know about the trie structure. The only thing they share is this object. +#[derive(Clone)] +pub struct PrefetchApi { + /// Bounded, shared queue for all IO threads to take work from. + /// + /// Work items are defined as `TrieKey` because currently the only + /// work is to prefetch a trie key. If other IO work is added, consider + /// changing the queue to an enum. + /// The state root is also included because multiple chunks could be applied + /// at the same time. + work_queue_tx: crossbeam::channel::Sender<(StateRoot, TrieKey)>, + work_queue_rx: crossbeam::channel::Receiver<(StateRoot, TrieKey)>, + /// Prefetching IO threads will insert fetched data here. This is also used + /// to mark what is already being fetched, to avoid fetching the same data + /// multiple times. + pub(crate) prefetching: PrefetchStagingArea, + + pub enable_receipt_prefetching: bool, + /// Configured accounts will be prefetched as SWEAT token account, if predecessor is listed as receiver. + pub sweat_prefetch_receivers: Vec, + /// List of allowed predecessor accounts for SWEAT prefetching. + pub sweat_prefetch_senders: Vec, + + pub shard_uid: ShardUId, +} + +#[derive(thiserror::Error, Debug)] +pub enum PrefetchError { + #[error("I/O scheduler input queue is full")] + QueueFull, + #[error("I/O scheduler input queue is disconnected")] + QueueDisconnected, +} + +/// Staging area for in-flight prefetch requests and a buffer for prefetched data. +/// +/// Before starting a pre-fetch, a slot is reserved for it. Once the data is +/// here, it will be put in that slot. The parent `TrieCachingStorage` needs +/// to take it out and move it to the shard cache. +/// +/// A shared staging area is the interface between `TrieCachingStorage` and +/// `TriePrefetchingStorage`. The parent simply checks the staging area before +/// going to the DB. Otherwise, no communication between the two is necessary. +/// +/// This design also ensures the shard cache works exactly the same with or +/// without the prefetcher, because the order in which it sees accesses is +/// independent of the prefetcher. +#[derive(Clone)] +pub(crate) struct PrefetchStagingArea(Arc>); + +struct InnerPrefetchStagingArea { + slots: SizeTrackedHashMap, +} + +/// Result when atomically accessing the prefetch staging area. +pub(crate) enum PrefetcherResult { + SlotReserved, + Pending, + Prefetched(Arc<[u8]>), + MemoryLimitReached, +} + +struct StagedMetrics { + prefetch_staged_bytes: GenericGauge, + prefetch_staged_items: GenericGauge, +} + +impl StagedMetrics { + fn new(shard_id: ShardId) -> Self { + Self { + prefetch_staged_bytes: metrics::PREFETCH_STAGED_BYTES + .with_label_values(&[&shard_id.to_string()]), + prefetch_staged_items: metrics::PREFETCH_STAGED_SLOTS + .with_label_values(&[&shard_id.to_string()]), + } + } +} + +/// Type used internally in the staging area to keep track of requests. +#[derive(Clone, Debug)] +enum PrefetchSlot { + PendingPrefetch, + PendingFetch, + Done(Arc<[u8]>), +} + +impl PrefetchSlot { + /// Returns amount of memory reserved for a value in the prefetching area. + fn reserved_memory(&self) -> usize { + match self { + PrefetchSlot::Done(value) => value.len(), + PrefetchSlot::PendingFetch | PrefetchSlot::PendingPrefetch => { + PREFETCH_RESERVED_BYTES_PER_SLOT + } + } + } +} + +struct SizeTrackedHashMap { + map: HashMap, + size_bytes: usize, + metrics: StagedMetrics, +} + +impl SizeTrackedHashMap { + fn new(shard_id: ShardId) -> Self { + let instance = + Self { map: Default::default(), size_bytes: 0, metrics: StagedMetrics::new(shard_id) }; + instance.update_metrics(); + instance + } + + fn insert(&mut self, k: CryptoHash, v: PrefetchSlot) -> Option { + self.size_bytes += v.reserved_memory(); + let dropped = self.map.insert(k, v); + if let Some(dropped) = &dropped { + self.size_bytes -= dropped.reserved_memory(); + } + self.update_metrics(); + dropped + } + + fn remove(&mut self, k: &CryptoHash) -> Option { + let dropped = self.map.remove(k); + if let Some(dropped) = &dropped { + self.size_bytes -= dropped.reserved_memory(); + } + self.update_metrics(); + dropped + } + + fn clear(&mut self) { + self.map.clear(); + self.size_bytes = 0; + self.update_metrics(); + } + + fn update_metrics(&self) { + self.metrics.prefetch_staged_bytes.set(self.size_bytes as i64); + self.metrics.prefetch_staged_items.set(self.map.len() as i64); + } + + fn get(&self, key: &CryptoHash) -> Option<&PrefetchSlot> { + self.map.get(key) + } +} + +impl TrieStorage for TriePrefetchingStorage { + // Note: This is the tricky bit of the implementation. + // We have to retrieve data only once in many threads, so all IO threads + // have to go though the staging area and check for inflight requests. + // The shard cache mutex plus the prefetch staging area mutex are used for + // that in combination. Let's call the first lock S and the second P. + // The rules for S and P are: + // 1. To avoid deadlocks, S must always be requested before P, if they are + // held at the same time. + // 2. When looking up if something is already in the shard cache, S must not + // be released until the staging area is updated by the current thread. + // Otherwise, there will be race conditions that could lead to multiple + // threads looking up the same value from DB. + // 3. IO threads should release S and P as soon as possible, as they can + // block the main thread otherwise. + fn retrieve_raw_bytes(&self, hash: &CryptoHash) -> Result, StorageError> { + // Try to get value from shard cache containing most recently touched nodes. + let mut shard_cache_guard = self.shard_cache.lock(); + if let Some(val) = shard_cache_guard.get(hash) { + return Ok(val); + } + + // If data is already being prefetched, wait for that instead of sending a new request. + let prefetch_state = + self.prefetching.get_and_set_if_empty(*hash, PrefetchSlot::PendingPrefetch); + // Keep lock until here to avoid race condition between shard cache insertion and reserving prefetch slot. + std::mem::drop(shard_cache_guard); + + match prefetch_state { + // Slot reserved for us, this thread should fetch it from DB. + PrefetcherResult::SlotReserved => { + let key = TrieCachingStorage::get_key_from_shard_uid_and_hash(self.shard_uid, hash); + match self.store.get(DBCol::State, key.as_ref()) { + Ok(Some(value)) => { + let value: Arc<[u8]> = value.into(); + self.prefetching.insert_fetched(*hash, value.clone()); + Ok(value) + } + Ok(None) => { + // This is an unrecoverable error, a hash found in the trie had no node. + // Releasing the lock here to unstuck main thread if it + // was blocking on this value, but it will also fail on its read. + self.prefetching.release(hash); + Err(StorageError::MissingTrieValue( + MissingTrieValueContext::TriePrefetchingStorage, + *hash, + )) + } + Err(e) => { + // This is an unrecoverable IO error. + // Releasing the lock here to unstuck main thread if it + // was blocking on this value, but it will also fail on its read. + self.prefetching.release(hash); + Err(StorageError::StorageInconsistentState(e.to_string())) + } + } + } + PrefetcherResult::Prefetched(value) => Ok(value), + PrefetcherResult::Pending => { + // yield once before calling `block_get` that will check for data to be present again. + thread::yield_now(); + self.prefetching + .blocking_get(*hash) + .or_else(|| { + // `blocking_get` will return None if the prefetch slot has been removed + // by the main thread and the value inserted into the shard cache. + self.shard_cache.get(hash) + }) + .ok_or_else(|| { + // This could only happen if this thread started prefetching a value + // while also another thread was already prefetching it. When the + // other thread finishes, the main thread takes it out, and moves it to + // the shard cache. And then this current thread gets delayed for long + // enough that the value gets evicted from the shard cache again before + // this thread has a chance to read it. + // In this rare occasion, we shall abort the current prefetch request and + // move on to the next. + StorageError::StorageInconsistentState(format!( + "Prefetcher failed on hash {hash}" + )) + }) + } + PrefetcherResult::MemoryLimitReached => Err(StorageError::StorageInconsistentState( + format!("Prefetcher failed due to memory limit hash {hash}"), + )), + } + } +} + +impl TriePrefetchingStorage { + pub(crate) fn new( + store: Store, + shard_uid: ShardUId, + shard_cache: TrieCache, + prefetching: PrefetchStagingArea, + ) -> Self { + Self { store, shard_uid, shard_cache, prefetching } + } +} + +impl PrefetchStagingArea { + fn new(shard_id: ShardId) -> Self { + let inner = InnerPrefetchStagingArea { slots: SizeTrackedHashMap::new(shard_id) }; + Self(Arc::new(Monitor::new(inner))) + } + + /// Release a slot in the prefetcher staging area. + /// + /// This must only be called after inserting the value to the shard cache. + /// Otherwise, the following scenario becomes possible: + /// 1: Main thread removes a value from the prefetch staging area. + /// 2: IO thread misses in the shard cache on the same key and starts fetching it again. + /// 3: Main thread value is inserted in shard cache. + pub(crate) fn release(&self, key: &CryptoHash) { + let mut guard = self.0.lock_mut(); + let _dropped = guard.slots.remove(key); + // `Done` is the result after a successful prefetch. + // `PendingFetch` means the value has been read without a prefetch. + // `None` means prefetching was stopped due to memory limits. + // debug_assert!( + // dropped.is_none() + // || prefetch_state_matches( + // PrefetchSlot::Done(Arc::new([])), + // dropped.as_ref().unwrap() + // ) + // || prefetch_state_matches(PrefetchSlot::PendingFetch, dropped.as_ref().unwrap()), + // ); + } + + /// Block until value is prefetched and then return it. + /// + /// Note: This function could return a future and become async. + /// DB requests are all blocking, unfortunately, so the benefit seems small. + /// The main benefit would be if many IO threads end up prefetching the + /// same data and thus are waiting on each other rather than the DB. + /// Of course, that would require prefetching to be moved into an async environment, + pub(crate) fn blocking_get(&self, key: CryptoHash) -> Option> { + let mut guard = self.0.lock(); + loop { + if let PrefetchSlot::Done(value) = guard.slots.get(&key)? { + return Some(value.clone()); + } + guard = self.0.wait(guard); + } + } + + /// Get prefetched value if available and otherwise atomically set + /// prefetcher state to being fetched by main thread. + pub(crate) fn get_or_set_fetching(&self, key: CryptoHash) -> PrefetcherResult { + self.get_and_set_if_empty(key, PrefetchSlot::PendingFetch) + } + + fn insert_fetched(&self, key: CryptoHash, value: Arc<[u8]>) { + self.0.lock_mut().slots.insert(key, PrefetchSlot::Done(value)); + } + + /// Get prefetched value if available and otherwise atomically insert the + /// given `PrefetchSlot` if no request is pending yet. + fn get_and_set_if_empty( + &self, + key: CryptoHash, + set_if_empty: PrefetchSlot, + ) -> PrefetcherResult { + let mut guard = self.0.lock_mut(); + let full = + guard.slots.size_bytes > MAX_PREFETCH_STAGING_MEMORY - PREFETCH_RESERVED_BYTES_PER_SLOT; + match guard.slots.map.get(&key) { + Some(value) => match value { + PrefetchSlot::Done(value) => PrefetcherResult::Prefetched(value.clone()), + PrefetchSlot::PendingPrefetch | PrefetchSlot::PendingFetch => { + PrefetcherResult::Pending + } + }, + None => { + if full { + return PrefetcherResult::MemoryLimitReached; + } + guard.slots.insert(key, set_if_empty); + PrefetcherResult::SlotReserved + } + } + } + + fn clear(&self) { + self.0.lock_mut().slots.clear(); + } +} + +impl PrefetchApi { + pub(crate) fn new( + store: Store, + shard_cache: TrieCache, + shard_uid: ShardUId, + trie_config: &TrieConfig, + ) -> (Self, PrefetchingThreadsHandle) { + let (work_queue_tx, work_queue_rx) = crossbeam::channel::bounded(MAX_QUEUED_WORK_ITEMS); + let sweat_prefetch_receivers = trie_config.sweat_prefetch_receivers.clone(); + let sweat_prefetch_senders = trie_config.sweat_prefetch_senders.clone(); + let enable_receipt_prefetching = trie_config.enable_receipt_prefetching; + + let this = Self { + work_queue_tx, + work_queue_rx, + prefetching: PrefetchStagingArea::new(shard_uid.shard_id()), + enable_receipt_prefetching, + sweat_prefetch_receivers, + sweat_prefetch_senders, + shard_uid, + }; + let (shutdown_tx, shutdown_rx) = crossbeam::channel::bounded(1); + let handles = (0..NUM_IO_THREADS) + .map(|_| { + this.start_io_thread( + store.clone(), + shard_cache.clone(), + shard_uid, + shutdown_rx.clone(), + ) + }) + .collect(); + let handle = PrefetchingThreadsHandle { shutdown_channel: Some(shutdown_tx), handles }; + (this, handle) + } + + pub fn prefetch_trie_key( + &self, + root: StateRoot, + trie_key: TrieKey, + ) -> Result<(), PrefetchError> { + self.work_queue_tx.try_send((root, trie_key)).map_err(|e| match e { + crossbeam::channel::TrySendError::Full(_) => PrefetchError::QueueFull, + crossbeam::channel::TrySendError::Disconnected(_) => PrefetchError::QueueDisconnected, + }) + } + + pub fn start_io_thread( + &self, + store: Store, + shard_cache: TrieCache, + shard_uid: ShardUId, + shutdown_rx: crossbeam::channel::Receiver<()>, + ) -> thread::JoinHandle<()> { + let prefetcher_storage = + TriePrefetchingStorage::new(store, shard_uid, shard_cache, self.prefetching.clone()); + let work_queue = self.work_queue_rx.clone(); + let metric_prefetch_sent = + metrics::PREFETCH_SENT.with_label_values(&[&shard_uid.shard_id.to_string()]); + let metric_prefetch_fail = + metrics::PREFETCH_FAIL.with_label_values(&[&shard_uid.shard_id.to_string()]); + thread::spawn(move || { + loop { + let selected = select! { + recv(shutdown_rx) -> _ => None, + recv(work_queue) -> maybe_work_item => maybe_work_item.ok(), + }; + + match selected { + None => return, + Some((trie_root, trie_key)) => { + // Since the trie root can change,and since the root is + // not known at the time when the IO threads starts, + // we need to redefine the trie before each request. + // Note that the constructor of `Trie` is trivial, and + // the clone only clones a few `Arc`s, so the performance + // hit is small. + let prefetcher_trie = + Trie::new(Rc::new(prefetcher_storage.clone()), trie_root, None); + let storage_key = trie_key.to_vec(); + metric_prefetch_sent.inc(); + if let Ok(_maybe_value) = prefetcher_trie.get(&storage_key) { + unc_o11y::io_trace!(count: "prefetch"); + } else { + // This may happen in rare occasions and can be ignored safely. + // See comments in `TriePrefetchingStorage::retrieve_raw_bytes`. + unc_o11y::io_trace!(count: "prefetch_failure"); + metric_prefetch_fail.inc(); + } + } + } + } + }) + } + + /// Remove queued up requests so IO threads will be paused after they finish their current task. + /// + /// Queued up work will not be finished. But trie keys that are already + /// being fetched will finish. + pub fn clear_queue(&self) { + while let Ok(_dropped) = self.work_queue_rx.try_recv() {} + } + + /// Clear prefetched staging area from data that has not been picked up by the main thread. + pub fn clear_data(&self) { + self.prefetching.clear(); + } +} + +// fn prefetch_state_matches(expected: PrefetchSlot, actual: &PrefetchSlot) -> bool { +// match (expected, actual) { +// (PrefetchSlot::PendingPrefetch, PrefetchSlot::PendingPrefetch) +// | (PrefetchSlot::PendingFetch, PrefetchSlot::PendingFetch) +// | (PrefetchSlot::Done(_), PrefetchSlot::Done(_)) => true, +// _ => false, +// } +// } + +/// Guard that owns the spawned prefetching IO threads. +#[must_use = "When dropping this handle, the IO threads will be aborted immediately."] +pub(crate) struct PrefetchingThreadsHandle { + /// Shutdown channel to all spawned threads. + shutdown_channel: Option>, + /// Join handles of spawned threads. + /// + /// Used to actively join all background threads after shutting them down. + handles: Vec>, +} + +impl Drop for PrefetchingThreadsHandle { + fn drop(&mut self) { + // Dropping the single sender will hang up the channel and stop + // background threads. + self.shutdown_channel.take(); + for handle in self.handles.drain(..) { + if let Err(e) = handle.join() { + error!("IO thread panicked joining failed, {e:?}"); + } + } + } +} + +/// Implementation to make testing from runtime possible. +/// +/// Prefetching by design has no visible side-effects. +/// To nevertheless test the functionality on the API level, +/// a minimal set of functions is required to check the inner +/// state of the prefetcher. +#[cfg(feature = "test_features")] +mod tests_utils { + use super::{PrefetchApi, PrefetchSlot}; + use crate::TrieCachingStorage; + + impl PrefetchApi { + /// Returns the number of prefetched values currently staged. + pub fn num_prefetched_and_staged(&self) -> usize { + self.prefetching + .0 + .lock() + .slots + .map + .iter() + .filter(|(_key, slot)| match slot { + PrefetchSlot::PendingPrefetch | PrefetchSlot::PendingFetch => false, + PrefetchSlot::Done(_) => true, + }) + .count() + } + + pub fn work_queued(&self) -> bool { + !self.work_queue_rx.is_empty() + } + } + + impl TrieCachingStorage { + pub fn clear_cache(&self) { + self.shard_cache.clear(); + } + } +} + +#[cfg(test)] +mod tests { + use super::{PrefetchStagingArea, PrefetcherResult}; + use unc_primitives::hash::CryptoHash; + + #[test] + fn test_prefetch_staging_area_blocking_get_after_update() { + let key = CryptoHash::hash_bytes(&[1, 2, 3]); + let value: std::sync::Arc<[u8]> = vec![4, 5, 6].into(); + let prefetch_staging_area = PrefetchStagingArea::new(0); + assert!(matches!( + prefetch_staging_area.get_or_set_fetching(key), + PrefetcherResult::SlotReserved + )); + let prefetch_staging_area2 = prefetch_staging_area.clone(); + let value2 = value.clone(); + // We need to execute `blocking_get` before `insert_fetched` so that + // it blocks while waiting for the value to be updated. Spawning + // a new thread + yielding should give enough time for the main + // thread to make progress. Please note that even if `insert_fetched` + // is executed before `blocking_get`, that still wouldn't result in + // any flakiness since the test would still pass, it just won't verify + // the synchronization part of `blocking_get`. + std::thread::spawn(move || { + std::thread::yield_now(); + prefetch_staging_area2.insert_fetched(key, value2); + }); + assert_eq!(prefetch_staging_area.blocking_get(key), Some(value)); + } +} diff --git a/core/store/src/trie/raw_node.rs b/core/store/src/trie/raw_node.rs new file mode 100644 index 000000000..7886c3dd1 --- /dev/null +++ b/core/store/src/trie/raw_node.rs @@ -0,0 +1,200 @@ +use borsh::{BorshDeserialize, BorshSerialize}; + +use unc_primitives::hash::CryptoHash; +use unc_primitives::state::ValueRef; + +/// Trie node with memory cost of its subtree. +/// +/// memory_usage is serialized, stored and contributes to hash. +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, PartialEq, Eq)] +pub struct RawTrieNodeWithSize { + pub node: RawTrieNode, + pub(super) memory_usage: u64, +} + +/// Trie node. +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, PartialEq, Eq)] +#[allow(clippy::large_enum_variant)] +pub enum RawTrieNode { + /// Leaf(key, value_length, value_hash) + Leaf(Vec, ValueRef), + /// Branch(children) + BranchNoValue(Children), + /// Branch(children, value) + BranchWithValue(ValueRef, Children), + /// Extension(key, child) + Extension(Vec, CryptoHash), +} + +impl RawTrieNode { + #[inline] + pub fn branch(children: Children, value: Option) -> Self { + match value { + Some(value) => Self::BranchWithValue(value, children), + None => Self::BranchNoValue(children), + } + } +} + +/// Children of a branch node. +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct Children(pub [Option; 16]); + +impl Children { + /// Iterates over existing children; `None` entries are omitted. + #[inline] + pub fn iter<'a>(&'a self) -> impl Iterator { + self.0.iter().enumerate().flat_map(|(i, el)| Some(i as u8).zip(el.as_ref())) + } +} + +impl Default for Children { + fn default() -> Self { + Self(Default::default()) + } +} + +impl std::ops::Index for Children { + type Output = Option; + fn index(&self, index: u8) -> &Option { + &self.0[usize::from(index)] + } +} + +impl std::ops::IndexMut for Children { + fn index_mut(&mut self, index: u8) -> &mut Option { + &mut self.0[usize::from(index)] + } +} + +impl BorshSerialize for Children { + fn serialize(&self, wr: &mut W) -> std::io::Result<()> { + let mut bitmap: u16 = 0; + let mut pos: u16 = 1; + for child in self.0.iter() { + if child.is_some() { + bitmap |= pos + } + pos <<= 1; + } + bitmap.serialize(wr)?; + self.0.iter().flat_map(Option::as_ref).map(|child| child.serialize(wr)).collect() + } +} + +impl BorshDeserialize for Children { + fn deserialize_reader(rd: &mut R) -> std::io::Result { + let mut bitmap = u16::deserialize_reader(rd)?; + let mut children = Self::default(); + while bitmap != 0 { + let idx = bitmap.trailing_zeros() as u8; + bitmap &= bitmap - 1; + children[idx] = Some(T::deserialize_reader(rd)?); + } + Ok(children) + } +} + +mod children { + struct Debug<'a, T>((u8, &'a T)); + + impl std::fmt::Debug for Debug<'_, T> { + fn fmt(&self, fmtr: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(fmtr, "{}: {:?}", self.0 .0, self.0 .1) + } + } + + impl std::fmt::Debug for super::Children { + fn fmt(&self, fmtr: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmtr.debug_list().entries(self.iter().map(Debug)).finish() + } + } +} + +#[test] +fn test_encode_decode() { + #[track_caller] + fn test(node: RawTrieNode, encoded: &[u8]) { + let node = RawTrieNodeWithSize { node, memory_usage: 42 }; + let mut buf = borsh::to_vec(&node).unwrap(); + assert_eq!(encoded, buf.as_slice()); + assert_eq!(node, RawTrieNodeWithSize::try_from_slice(&buf).unwrap()); + + // Test that adding garbage at the end fails decoding. + buf.push(b'!'); + let got = RawTrieNodeWithSize::try_from_slice(&buf); + assert!(got.is_err(), "got: {got:?}"); + } + + let value = ValueRef { length: 3, hash: CryptoHash::hash_bytes(&[123, 245, 255]) }; + let node = RawTrieNode::Leaf(vec![1, 2, 3], value.clone()); + #[rustfmt::skip] + let encoded = [ + /* node type: */ 0, + /* key: */ 3, 0, 0, 0, 1, 2, 3, + /* value: */ 3, 0, 0, 0, 194, 40, 8, 24, 64, 219, 69, 132, 86, 52, 110, 175, 57, 198, 165, 200, 83, 237, 211, 11, 194, 83, 251, 33, 145, 138, 234, 226, 7, 242, 186, 73, + /* memory usage: */ 42, 0, 0, 0, 0, 0, 0, 0, + ]; + test(node, &encoded); + + let mut children = Children::default(); + children[3] = Some(CryptoHash([1; 32])); + children[5] = Some(CryptoHash([2; 32])); + let node = RawTrieNode::BranchNoValue(children); + #[rustfmt::skip] + let encoded = [ + /* node type: */ 1, + /* bitmask: */ 40, 0, + /* child: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + /* child: */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + /* memory usage: */ 42, 0, 0, 0, 0, 0, 0, 0 + ]; + test(node, &encoded); + + let mut children = Children::default(); + children[0] = Some(CryptoHash([1; 32])); + let node = RawTrieNode::BranchNoValue(children); + #[rustfmt::skip] + let encoded = [ + /* node type: */ 1, + /* bitmask: */ 1, 0, + /* child: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + /* memory usage: */ 42, 0, 0, 0, 0, 0, 0, 0 + ]; + test(node, &encoded); + + let mut children = Children::default(); + children[15] = Some(CryptoHash([1; 32])); + let node = RawTrieNode::BranchNoValue(children); + #[rustfmt::skip] + let encoded = [ + /* node type: */ 1, + /* bitmask: */ 0, 128, + /* child: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + /* memory usage: */ 42, 0, 0, 0, 0, 0, 0, 0 + ]; + test(node, &encoded); + + let mut children = Children::default(); + children[3] = Some(CryptoHash([1; 32])); + let node = RawTrieNode::BranchWithValue(value, children); + #[rustfmt::skip] + let encoded = [ + /* node type: */ 2, + /* value: */ 3, 0, 0, 0, 194, 40, 8, 24, 64, 219, 69, 132, 86, 52, 110, 175, 57, 198, 165, 200, 83, 237, 211, 11, 194, 83, 251, 33, 145, 138, 234, 226, 7, 242, 186, 73, + /* bitmask: */ 8, 0, + /* child: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + /* memory usage: */ 42, 0, 0, 0, 0, 0, 0, 0 + ]; + test(node, &encoded); + + let node = RawTrieNode::Extension(vec![123, 245, 255], super::Trie::EMPTY_ROOT); + #[rustfmt::skip] + let encoded = [ + /* node type: */ 3, + /* key: */ 3, 0, 0, 0, 123, 245, 255, + /* node: */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* memory usage: */ 42, 0, 0, 0, 0, 0, 0, 0 + ]; + test(node, &encoded); +} diff --git a/core/store/src/trie/resharding.rs b/core/store/src/trie/resharding.rs new file mode 100644 index 000000000..7ec2c8096 --- /dev/null +++ b/core/store/src/trie/resharding.rs @@ -0,0 +1,508 @@ +use crate::flat::FlatStateChanges; +use crate::{get, get_delayed_receipt_indices, set, ShardTries, StoreUpdate, Trie, TrieUpdate}; +use borsh::BorshDeserialize; +use bytesize::ByteSize; +use unc_primitives::account::id::AccountId; +use unc_primitives::errors::StorageError; +use unc_primitives::receipt::Receipt; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::state_part::PartId; +use unc_primitives::trie_key::trie_key_parsers::parse_account_id_from_raw_key; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::{ + ConsolidatedStateChange, StateChangeCause, StateChangesForResharding, StateRoot, +}; +use std::collections::HashMap; + +use super::iterator::TrieItem; + +impl Trie { + // TODO(#9446) remove function when shifting to flat storage iteration for resharding + pub fn get_trie_items_for_part(&self, part_id: PartId) -> Result, StorageError> { + let path_begin = self.find_state_part_boundary(part_id.idx, part_id.total)?; + let path_end = self.find_state_part_boundary(part_id.idx + 1, part_id.total)?; + self.iter()?.get_trie_items(&path_begin, &path_end) + } +} + +impl ShardTries { + /// applies `changes` to children states during resharding + /// and returns the generated TrieUpdate for all children states + /// Note that this function is different from the function `add_values_to_children_states` + /// This function is used for applying updates to children states when processing blocks + /// `add_values_to_children_states` are used to generate the initial states for states split + /// from the original parent shard. + pub fn apply_state_changes_to_children_states( + &self, + state_roots: &HashMap, + changes: StateChangesForResharding, + account_id_to_shard_uid: &dyn Fn(&AccountId) -> ShardUId, + ) -> Result, StorageError> { + let mut trie_updates: HashMap<_, _> = self.get_trie_updates(state_roots); + let mut insert_receipts = Vec::new(); + for ConsolidatedStateChange { trie_key, value } in changes.changes { + match &trie_key { + TrieKey::DelayedReceiptIndices => {} + TrieKey::DelayedReceipt { index } => match value { + Some(value) => { + let receipt = Receipt::try_from_slice(&value).map_err(|err| { + StorageError::StorageInconsistentState(format!( + "invalid delayed receipt {:?}, err: {}", + value, err, + )) + })?; + insert_receipts.push((*index, receipt)); + } + None => {} + }, + TrieKey::Account { account_id } + | TrieKey::ContractCode { account_id } + | TrieKey::AccessKey { account_id, .. } + | TrieKey::ReceivedData { receiver_id: account_id, .. } + | TrieKey::PostponedReceiptId { receiver_id: account_id, .. } + | TrieKey::PendingDataCount { receiver_id: account_id, .. } + | TrieKey::PostponedReceipt { receiver_id: account_id, .. } + | TrieKey::Rsa2048Keys { account_id, .. } + | TrieKey::ContractData { account_id, .. } => { + let new_shard_uid = account_id_to_shard_uid(account_id); + // we can safely unwrap here because the caller of this function guarantees trie_updates + // contains all shard_uids for the new shards + let trie_update = trie_updates.get_mut(&new_shard_uid).unwrap(); + match value { + Some(value) => trie_update.set(trie_key, value), + None => trie_update.remove(trie_key), + } + } + } + } + for (_, update) in trie_updates.iter_mut() { + // StateChangeCause should always be Resharding for processing resharding. + // We do not want to commit the state_changes from resharding as they are already handled while + // processing parent shard + update.commit(StateChangeCause::Resharding); + } + + insert_receipts.sort_by_key(|it| it.0); + + let insert_receipts: Vec<_> = + insert_receipts.into_iter().map(|(_, receipt)| receipt).collect(); + + apply_delayed_receipts_to_children_states_impl( + &mut trie_updates, + &insert_receipts, + &changes.processed_delayed_receipts, + account_id_to_shard_uid, + )?; + + Ok(trie_updates) + } + + /// add `values` (key-value pairs of items stored in states) to build states for new shards + /// `state_roots` contains state roots for the new shards + /// The caller must guarantee that `state_roots` contains all shard_ids + /// that `key_to_shard_id` that may return + /// Ignore changes on DelayedReceipts or DelayedReceiptsIndices + /// Returns `store_update` and the new state_roots for children shards + pub fn add_values_to_children_states( + &self, + state_roots: &HashMap, + values: Vec<(Vec, Option>)>, + account_id_to_shard_id: &dyn Fn(&AccountId) -> ShardUId, + ) -> Result<(StoreUpdate, HashMap), StorageError> { + self.add_values_to_children_states_impl(state_roots, values, &|raw_key| { + // Here changes on DelayedReceipts or DelayedReceiptsIndices will be excluded + // This is because we cannot migrate delayed receipts part by part. They have to be + // reconstructed in the new states after all DelayedReceipts are ready in the original + // shard. + if let Some(account_id) = parse_account_id_from_raw_key(raw_key).map_err(|e| { + let err = format!("error parsing account id from trie key {:?}: {:?}", raw_key, e); + StorageError::StorageInconsistentState(err) + })? { + let new_shard_uid = account_id_to_shard_id(&account_id); + Ok(Some(new_shard_uid)) + } else { + Ok(None) + } + }) + } + + fn add_values_to_children_states_impl( + &self, + state_roots: &HashMap, + values: Vec<(Vec, Option>)>, + key_to_shard_id: &dyn Fn(&[u8]) -> Result, StorageError>, + ) -> Result<(StoreUpdate, HashMap), StorageError> { + let mut changes_by_shard: HashMap<_, Vec<_>> = HashMap::new(); + for (raw_key, value) in values.into_iter() { + if let Some(new_shard_uid) = key_to_shard_id(&raw_key)? { + changes_by_shard.entry(new_shard_uid).or_default().push((raw_key, value)); + } + } + let mut new_state_roots = state_roots.clone(); + let mut store_update = self.store_update(); + for (shard_uid, changes) in changes_by_shard { + FlatStateChanges::from_raw_key_value(&changes) + .apply_to_flat_state(&mut store_update, shard_uid); + // Here we assume that state_roots contains shard_uid, the caller of this method will guarantee that. + let trie_changes = + self.get_trie_for_shard(shard_uid, state_roots[&shard_uid]).update(changes)?; + let state_root = self.apply_all(&trie_changes, shard_uid, &mut store_update); + new_state_roots.insert(shard_uid, state_root); + } + Ok((store_update, new_state_roots)) + } + + fn get_trie_updates( + &self, + state_roots: &HashMap, + ) -> HashMap { + state_roots + .iter() + .map(|(shard_uid, state_root)| { + (*shard_uid, self.new_trie_update(*shard_uid, *state_root)) + }) + .collect() + } + + pub fn apply_delayed_receipts_to_children_states( + &self, + state_roots: &HashMap, + receipts: &[Receipt], + account_id_to_shard_uid: &dyn Fn(&AccountId) -> ShardUId, + ) -> Result<(StoreUpdate, HashMap), StorageError> { + let mut trie_updates: HashMap<_, _> = self.get_trie_updates(state_roots); + apply_delayed_receipts_to_children_states_impl( + &mut trie_updates, + receipts, + &[], + account_id_to_shard_uid, + )?; + self.finalize_and_apply_trie_updates(trie_updates) + } + + fn finalize_and_apply_trie_updates( + &self, + updates: HashMap, + ) -> Result<(StoreUpdate, HashMap), StorageError> { + let mut new_state_roots = HashMap::new(); + let mut store_update = self.store_update(); + for (shard_uid, update) in updates { + let (_, trie_changes, state_changes) = update.finalize()?; + let state_root = self.apply_all(&trie_changes, shard_uid, &mut store_update); + FlatStateChanges::from_state_changes(&state_changes) + .apply_to_flat_state(&mut store_update, shard_uid); + new_state_roots.insert(shard_uid, state_root); + } + Ok((store_update, new_state_roots)) + } +} + +fn apply_delayed_receipts_to_children_states_impl( + trie_updates: &mut HashMap, + insert_receipts: &[Receipt], + delete_receipts: &[Receipt], + account_id_to_shard_uid: &dyn Fn(&AccountId) -> ShardUId, +) -> Result<(), StorageError> { + let mut delayed_receipts_indices_by_shard = HashMap::new(); + for (shard_uid, update) in trie_updates.iter() { + delayed_receipts_indices_by_shard.insert(*shard_uid, get_delayed_receipt_indices(update)?); + } + + for receipt in insert_receipts { + let new_shard_uid: ShardUId = account_id_to_shard_uid(&receipt.receiver_id); + if !trie_updates.contains_key(&new_shard_uid) { + let err = format!( + "Account {} is in new shard {:?} but state_roots only contains {:?}", + receipt.receiver_id, + new_shard_uid, + trie_updates.keys(), + ); + return Err(StorageError::StorageInconsistentState(err)); + } + // we already checked that new_shard_uid is in trie_updates and delayed_receipts_indices + // so we can safely unwrap here + let delayed_receipts_indices = + delayed_receipts_indices_by_shard.get_mut(&new_shard_uid).unwrap(); + set( + trie_updates.get_mut(&new_shard_uid).unwrap(), + TrieKey::DelayedReceipt { index: delayed_receipts_indices.next_available_index }, + receipt, + ); + delayed_receipts_indices.next_available_index = + delayed_receipts_indices.next_available_index.checked_add(1).ok_or_else(|| { + StorageError::StorageInconsistentState( + "Next available index for delayed receipt exceeded the integer limit" + .to_string(), + ) + })?; + } + + for receipt in delete_receipts { + let new_shard_uid: ShardUId = account_id_to_shard_uid(&receipt.receiver_id); + if !trie_updates.contains_key(&new_shard_uid) { + let err = format!( + "Account {} is in new shard {:?} but state_roots only contains {:?}", + receipt.receiver_id, + new_shard_uid, + trie_updates.keys(), + ); + return Err(StorageError::StorageInconsistentState(err)); + } + let delayed_receipts_indices = + delayed_receipts_indices_by_shard.get_mut(&new_shard_uid).unwrap(); + + let trie_update = trie_updates.get_mut(&new_shard_uid).unwrap(); + let trie_key = TrieKey::DelayedReceipt { index: delayed_receipts_indices.first_index }; + + let stored_receipt = get::(trie_update, &trie_key)? + .expect("removed receipt does not exist in new state"); + // check that the receipt to remove is at the first of delayed receipt queue + assert_eq!(&stored_receipt, receipt); + trie_update.remove(trie_key); + delayed_receipts_indices.first_index += 1; + } + + // commit the trie_updates and update state_roots + for (shard_uid, trie_update) in trie_updates { + set( + trie_update, + TrieKey::DelayedReceiptIndices, + delayed_receipts_indices_by_shard.get(shard_uid).unwrap(), + ); + // StateChangeCause should always be Resharding for processing resharding. + // We do not want to commit the state_changes from resharding as they are already handled while + // processing parent shard + trie_update.commit(StateChangeCause::Resharding); + } + Ok(()) +} + +/// Retrieve delayed receipts starting with `start_index` until `memory_limit` is hit +/// return None if there is no delayed receipts with index >= start_index +pub fn get_delayed_receipts( + state_update: &TrieUpdate, + start_index: Option, + memory_limit: ByteSize, +) -> Result)>, StorageError> { + let mut delayed_receipt_indices = get_delayed_receipt_indices(state_update)?; + if let Some(start_index) = start_index { + if start_index >= delayed_receipt_indices.next_available_index { + return Ok(None); + } + delayed_receipt_indices.first_index = start_index.max(delayed_receipt_indices.first_index); + } + let mut used_memory = 0; + let mut receipts = vec![]; + + while used_memory < memory_limit.as_u64() + && delayed_receipt_indices.first_index < delayed_receipt_indices.next_available_index + { + let key = TrieKey::DelayedReceipt { index: delayed_receipt_indices.first_index }; + let data = state_update.get(&key)?.ok_or_else(|| { + StorageError::StorageInconsistentState(format!( + "Delayed receipt #{} should be in the state", + delayed_receipt_indices.first_index + )) + })?; + used_memory += data.len() as u64; + delayed_receipt_indices.first_index += 1; + + let receipt = Receipt::try_from_slice(&data).map_err(|_| { + StorageError::StorageInconsistentState("Failed to deserialize".to_string()) + })?; + receipts.push(receipt); + } + Ok(Some((delayed_receipt_indices.first_index, receipts))) +} + +#[cfg(test)] +mod tests { + use crate::resharding::{apply_delayed_receipts_to_children_states_impl, get_delayed_receipts}; + use crate::test_utils::{ + gen_changes, gen_receipts, get_all_delayed_receipts, test_populate_trie, TestTriesBuilder, + }; + + use crate::{set, ShardTries, ShardUId, Trie}; + use unc_primitives::account::id::AccountId; + + use unc_primitives::hash::hash; + use unc_primitives::receipt::{DelayedReceiptIndices, Receipt}; + use unc_primitives::trie_key::TrieKey; + use unc_primitives::types::{NumShards, StateChangeCause, StateRoot}; + use rand::Rng; + use std::collections::HashMap; + + #[test] + fn test_add_values_to_children_states() { + let mut rng = rand::thread_rng(); + + for _ in 0..20 { + let tries = TestTriesBuilder::new().build(); + // add 4 new shards for version 1 + let num_shards = 4; + let mut state_root = Trie::EMPTY_ROOT; + let mut state_roots: HashMap<_, _> = (0..num_shards) + .map(|x| (ShardUId { version: 1, shard_id: x as u32 }, Trie::EMPTY_ROOT)) + .collect(); + for _ in 0..10 { + let changes = gen_changes(&mut rng, 100); + state_root = test_populate_trie( + &tries, + &state_root, + ShardUId::single_shard(), + changes.clone(), + ); + + let (store_update, new_state_roots) = tries + .add_values_to_children_states_impl(&state_roots, changes, &|raw_key| { + Ok(Some(ShardUId { + version: 1, + shard_id: (hash(raw_key).0[0] as NumShards % num_shards) as u32, + })) + }) + .unwrap(); + store_update.commit().unwrap(); + state_roots = new_state_roots; + + // check that the 4 tries combined to the orig trie + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), state_root); + let trie_items: HashMap<_, _> = trie.iter().unwrap().map(Result::unwrap).collect(); + let mut combined_trie_items: HashMap, Vec> = HashMap::new(); + for (shard_uid, state_root) in state_roots.iter() { + let trie = tries.get_view_trie_for_shard(*shard_uid, *state_root); + combined_trie_items.extend(trie.iter().unwrap().map(Result::unwrap)); + } + assert_eq!(trie_items, combined_trie_items); + } + } + } + + #[test] + fn test_get_delayed_receipts() { + let mut rng = rand::thread_rng(); + for _ in 0..20 { + let memory_limit = bytesize::ByteSize::b(rng.gen_range(200..1000)); + let all_receipts = gen_receipts(&mut rng, 200); + + // push receipt to trie + let tries = TestTriesBuilder::new().build(); + let mut trie_update = tries.new_trie_update(ShardUId::single_shard(), Trie::EMPTY_ROOT); + let mut delayed_receipt_indices = DelayedReceiptIndices::default(); + + for (i, receipt) in all_receipts.iter().enumerate() { + set(&mut trie_update, TrieKey::DelayedReceipt { index: i as u64 }, receipt); + } + delayed_receipt_indices.next_available_index = all_receipts.len() as u64; + set(&mut trie_update, TrieKey::DelayedReceiptIndices, &delayed_receipt_indices); + trie_update.commit(StateChangeCause::Resharding); + let (_, trie_changes, _) = trie_update.finalize().unwrap(); + let mut store_update = tries.store_update(); + let state_root = + tries.apply_all(&trie_changes, ShardUId::single_shard(), &mut store_update); + store_update.commit().unwrap(); + + assert_eq!( + all_receipts, + get_all_delayed_receipts(&tries, &ShardUId::single_shard(), &state_root) + ); + let mut start_index = 0; + + let trie_update = tries.new_trie_update(ShardUId::single_shard(), state_root); + while let Some((next_index, receipts)) = + get_delayed_receipts(&trie_update, Some(start_index), memory_limit).unwrap() + { + assert_eq!(receipts, all_receipts[start_index as usize..next_index as usize]); + start_index = next_index; + + let total_memory_use: u64 = receipts + .iter() + .map(|receipt| borsh::object_length(&receipt).unwrap() as u64) + .sum(); + let memory_use_without_last_receipt: u64 = receipts[..receipts.len() - 1] + .iter() + .map(|receipt| borsh::object_length(&receipt).unwrap() as u64) + .sum(); + + assert!( + total_memory_use >= memory_limit.as_u64() + || next_index == all_receipts.len() as u64 + ); + assert!(memory_use_without_last_receipt < memory_limit.as_u64()); + } + } + } + + fn test_apply_delayed_receipts( + tries: &ShardTries, + new_receipts: &[Receipt], + delete_receipts: &[Receipt], + expected_all_receipts: &[Receipt], + state_roots: HashMap, + account_id_to_shard_id: &dyn Fn(&AccountId) -> ShardUId, + ) -> HashMap { + let mut trie_updates: HashMap<_, _> = tries.get_trie_updates(&state_roots); + apply_delayed_receipts_to_children_states_impl( + &mut trie_updates, + new_receipts, + delete_receipts, + account_id_to_shard_id, + ) + .unwrap(); + let (state_update, new_state_roots) = + tries.finalize_and_apply_trie_updates(trie_updates).unwrap(); + state_update.commit().unwrap(); + + let receipts_by_shard: HashMap<_, _> = new_state_roots + .iter() + .map(|(shard_uid, state_root)| { + let receipts = get_all_delayed_receipts(tries, shard_uid, state_root); + (shard_uid, receipts) + }) + .collect(); + + let mut expected_receipts_by_shard: HashMap<_, _> = + state_roots.iter().map(|(shard_uid, _)| (shard_uid, vec![])).collect(); + for receipt in expected_all_receipts { + let shard_uid = account_id_to_shard_id(&receipt.receiver_id); + expected_receipts_by_shard.get_mut(&shard_uid).unwrap().push(receipt.clone()); + } + assert_eq!(expected_receipts_by_shard, receipts_by_shard); + + new_state_roots + } + + #[test] + fn test_apply_delayed_receipts_to_new_states() { + let mut rng = rand::thread_rng(); + + let tries = TestTriesBuilder::new().build(); + let num_shards = 4; + + for _ in 0..10 { + let mut state_roots: HashMap<_, _> = (0..num_shards) + .map(|x| (ShardUId { version: 1, shard_id: x as u32 }, Trie::EMPTY_ROOT)) + .collect(); + let mut all_receipts = vec![]; + let mut start_index = 0; + for _ in 0..10 { + let receipts = gen_receipts(&mut rng, 100); + let new_start_index = rng.gen_range(start_index..all_receipts.len() + 1); + + all_receipts.extend_from_slice(&receipts); + state_roots = test_apply_delayed_receipts( + &tries, + &receipts, + &all_receipts[start_index..new_start_index], + &all_receipts[new_start_index..], + state_roots, + &|account_id| ShardUId { + shard_id: (hash(account_id.as_bytes()).0[0] as NumShards % num_shards) + as u32, + version: 1, + }, + ); + start_index = new_start_index; + } + } + } +} diff --git a/core/store/src/trie/shard_tries.rs b/core/store/src/trie/shard_tries.rs new file mode 100644 index 000000000..1a58925a2 --- /dev/null +++ b/core/store/src/trie/shard_tries.rs @@ -0,0 +1,886 @@ +use super::mem::MemTries; +use super::state_snapshot::{StateSnapshot, StateSnapshotConfig}; +use super::TrieRefcountSubtraction; +use crate::flat::store_helper::remove_all_state_values; +use crate::flat::{FlatStorageManager, FlatStorageStatus}; +use crate::trie::config::TrieConfig; +use crate::trie::mem::loading::load_trie_from_flat_state_and_delta; +use crate::trie::mem::updating::apply_memtrie_changes; +use crate::trie::prefetching_trie_storage::PrefetchingThreadsHandle; +use crate::trie::trie_storage::{TrieCache, TrieCachingStorage}; +use crate::trie::{TrieRefcountAddition, POISONED_LOCK_ERR}; +use crate::{metrics, DBCol, PrefetchApi}; +use crate::{Store, StoreUpdate, Trie, TrieChanges, TrieUpdate}; +use unc_primitives::errors::StorageError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::{self, ShardUId}; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::{ + BlockHeight, RawStateChange, RawStateChangesWithTrieKey, StateChangeCause, StateRoot, +}; +use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; +use std::collections::HashMap; +use std::rc::Rc; +use std::sync::{Arc, RwLock}; +use tracing::info; + +struct ShardTriesInner { + store: Store, + trie_config: TrieConfig, + mem_tries: RwLock>>>, + /// Cache reserved for client actor to use + caches: RwLock>, + /// Cache for readers. + view_caches: RwLock>, + flat_storage_manager: FlatStorageManager, + /// Prefetcher state, such as IO threads, per shard. + prefetchers: RwLock>, + /// Provides access to the snapshot of the DB at the beginning of an epoch. + // Needs a synchronization primitive because it can be concurrently accessed: + // * writes by StateSnapshotActor + // * reads by ViewClientActor + state_snapshot: Arc>>, + /// Configures how to make state snapshots. + state_snapshot_config: StateSnapshotConfig, +} + +#[derive(Clone)] +pub struct ShardTries(Arc); + +impl ShardTries { + pub fn new( + store: Store, + trie_config: TrieConfig, + shard_uids: &[ShardUId], + flat_storage_manager: FlatStorageManager, + state_snapshot_config: StateSnapshotConfig, + ) -> Self { + let caches = Self::create_initial_caches(&trie_config, &shard_uids, false); + let view_caches = Self::create_initial_caches(&trie_config, &shard_uids, true); + metrics::HAS_STATE_SNAPSHOT.set(0); + ShardTries(Arc::new(ShardTriesInner { + store, + trie_config, + mem_tries: RwLock::new(HashMap::new()), + caches: RwLock::new(caches), + view_caches: RwLock::new(view_caches), + flat_storage_manager, + prefetchers: Default::default(), + state_snapshot: Arc::new(RwLock::new(None)), + state_snapshot_config, + })) + } + + /// Create caches for all shards according to the trie config. + fn create_initial_caches( + config: &TrieConfig, + shard_uids: &[ShardUId], + is_view: bool, + ) -> HashMap { + shard_uids + .iter() + .map(|&shard_uid| (shard_uid, TrieCache::new(config, shard_uid, is_view))) + .collect() + } + + pub fn new_trie_update(&self, shard_uid: ShardUId, state_root: StateRoot) -> TrieUpdate { + TrieUpdate::new(self.get_trie_for_shard(shard_uid, state_root)) + } + + pub fn new_trie_update_view(&self, shard_uid: ShardUId, state_root: StateRoot) -> TrieUpdate { + TrieUpdate::new(self.get_view_trie_for_shard(shard_uid, state_root)) + } + + fn get_trie_for_shard_internal( + &self, + shard_uid: ShardUId, + state_root: StateRoot, + is_view: bool, + block_hash: Option, + ) -> Trie { + let caches_to_use = if is_view { &self.0.view_caches } else { &self.0.caches }; + let cache = { + let mut caches = caches_to_use.write().expect(POISONED_LOCK_ERR); + caches + .entry(shard_uid) + .or_insert_with(|| TrieCache::new(&self.0.trie_config, shard_uid, is_view)) + .clone() + }; + // Do not enable prefetching on view caches. + // 1) Performance of view calls is not crucial. + // 2) A lot of the prefetcher code assumes there is only one "main-thread" per shard active. + // If you want to enable it for view calls, at least make sure they don't share + // the `PrefetchApi` instances with the normal calls. + let prefetch_enabled = !is_view + && (self.0.trie_config.enable_receipt_prefetching + || (!self.0.trie_config.sweat_prefetch_receivers.is_empty() + && !self.0.trie_config.sweat_prefetch_senders.is_empty())); + let prefetch_api = prefetch_enabled.then(|| { + self.0 + .prefetchers + .write() + .expect(POISONED_LOCK_ERR) + .entry(shard_uid) + .or_insert_with(|| { + PrefetchApi::new( + self.0.store.clone(), + cache.clone(), + shard_uid, + &self.0.trie_config, + ) + }) + .0 + .clone() + }); + + let storage = Rc::new(TrieCachingStorage::new( + self.0.store.clone(), + cache, + shard_uid, + is_view, + prefetch_api, + )); + let flat_storage_chunk_view = block_hash + .and_then(|block_hash| self.0.flat_storage_manager.chunk_view(shard_uid, block_hash)); + Trie::new_with_memtries( + storage, + self.get_mem_tries(shard_uid), + state_root, + flat_storage_chunk_view, + ) + } + + pub fn get_trie_for_shard(&self, shard_uid: ShardUId, state_root: StateRoot) -> Trie { + self.get_trie_for_shard_internal(shard_uid, state_root, false, None) + } + + pub fn get_trie_with_block_hash_for_shard_from_snapshot( + &self, + shard_uid: ShardUId, + state_root: StateRoot, + block_hash: &CryptoHash, + ) -> Result { + let (store, flat_storage_manager) = self.get_state_snapshot(block_hash)?; + let cache = { + let mut caches = self.0.view_caches.write().expect(POISONED_LOCK_ERR); + caches + .entry(shard_uid) + .or_insert_with(|| TrieCache::new(&self.0.trie_config, shard_uid, true)) + .clone() + }; + let storage = Rc::new(TrieCachingStorage::new(store, cache, shard_uid, true, None)); + let flat_storage_chunk_view = flat_storage_manager.chunk_view(shard_uid, *block_hash); + + Ok(Trie::new(storage, state_root, flat_storage_chunk_view)) + } + + pub fn get_trie_with_block_hash_for_shard( + &self, + shard_uid: ShardUId, + state_root: StateRoot, + block_hash: &CryptoHash, + is_view: bool, + ) -> Trie { + self.get_trie_for_shard_internal(shard_uid, state_root, is_view, Some(*block_hash)) + } + + pub fn get_view_trie_for_shard(&self, shard_uid: ShardUId, state_root: StateRoot) -> Trie { + self.get_trie_for_shard_internal(shard_uid, state_root, true, None) + } + + pub fn store_update(&self) -> StoreUpdate { + StoreUpdate::new(self.get_db().clone()) + } + + pub fn get_store(&self) -> Store { + self.0.store.clone() + } + + pub(crate) fn get_db(&self) -> &Arc { + &self.0.store.storage + } + + pub fn get_flat_storage_manager(&self) -> FlatStorageManager { + self.0.flat_storage_manager.clone() + } + + pub fn state_snapshot_config(&self) -> &StateSnapshotConfig { + &self.0.state_snapshot_config + } + + pub(crate) fn state_snapshot(&self) -> &Arc>> { + &self.0.state_snapshot + } + + pub fn update_cache(&self, ops: Vec<(&CryptoHash, Option<&[u8]>)>, shard_uid: ShardUId) { + let mut caches = self.0.caches.write().expect(POISONED_LOCK_ERR); + let cache = caches + .entry(shard_uid) + .or_insert_with(|| TrieCache::new(&self.0.trie_config, shard_uid, false)) + .clone(); + cache.update_cache(ops); + } + + fn apply_deletions_inner( + &self, + deletions: &[TrieRefcountSubtraction], + shard_uid: ShardUId, + store_update: &mut StoreUpdate, + ) { + let mut ops = Vec::new(); + for TrieRefcountSubtraction { trie_node_or_value_hash, rc, .. } in deletions.iter() { + let key = TrieCachingStorage::get_key_from_shard_uid_and_hash( + shard_uid, + trie_node_or_value_hash, + ); + store_update.decrement_refcount_by(DBCol::State, key.as_ref(), *rc); + ops.push((trie_node_or_value_hash, None)); + } + + self.update_cache(ops, shard_uid); + } + + fn apply_insertions_inner( + &self, + insertions: &[TrieRefcountAddition], + shard_uid: ShardUId, + store_update: &mut StoreUpdate, + ) { + let mut ops = Vec::new(); + for TrieRefcountAddition { trie_node_or_value_hash, trie_node_or_value, rc } in + insertions.iter() + { + let key = TrieCachingStorage::get_key_from_shard_uid_and_hash( + shard_uid, + trie_node_or_value_hash, + ); + store_update.increment_refcount_by(DBCol::State, key.as_ref(), trie_node_or_value, *rc); + ops.push((trie_node_or_value_hash, Some(trie_node_or_value.as_slice()))); + } + self.update_cache(ops, shard_uid); + } + + fn apply_all_inner( + &self, + trie_changes: &TrieChanges, + shard_uid: ShardUId, + apply_deletions: bool, + store_update: &mut StoreUpdate, + ) -> StateRoot { + self.apply_insertions_inner(&trie_changes.insertions, shard_uid, store_update); + if apply_deletions { + self.apply_deletions_inner(&trie_changes.deletions, shard_uid, store_update); + } + trie_changes.new_root + } + + pub fn apply_insertions( + &self, + trie_changes: &TrieChanges, + shard_uid: ShardUId, + store_update: &mut StoreUpdate, + ) { + // `itoa` is much faster for printing shard_id to a string than trivial alternatives. + let mut buffer = itoa::Buffer::new(); + let shard_id = buffer.format(shard_uid.shard_id); + + metrics::APPLIED_TRIE_INSERTIONS + .with_label_values(&[&shard_id]) + .inc_by(trie_changes.insertions.len() as u64); + self.apply_insertions_inner(&trie_changes.insertions, shard_uid, store_update) + } + + pub fn apply_deletions( + &self, + trie_changes: &TrieChanges, + shard_uid: ShardUId, + store_update: &mut StoreUpdate, + ) { + // `itoa` is much faster for printing shard_id to a string than trivial alternatives. + let mut buffer = itoa::Buffer::new(); + let shard_id = buffer.format(shard_uid.shard_id); + + metrics::APPLIED_TRIE_DELETIONS + .with_label_values(&[&shard_id]) + .inc_by(trie_changes.deletions.len() as u64); + self.apply_deletions_inner(&trie_changes.deletions, shard_uid, store_update) + } + + pub fn revert_insertions( + &self, + trie_changes: &TrieChanges, + shard_uid: ShardUId, + store_update: &mut StoreUpdate, + ) { + // `itoa` is much faster for printing shard_id to a string than trivial alternatives. + let mut buffer = itoa::Buffer::new(); + let shard_id = buffer.format(shard_uid.shard_id); + + metrics::REVERTED_TRIE_INSERTIONS + .with_label_values(&[&shard_id]) + .inc_by(trie_changes.insertions.len() as u64); + self.apply_deletions_inner( + &trie_changes.insertions.iter().map(|insertion| insertion.revert()).collect::>(), + shard_uid, + store_update, + ) + } + + pub fn apply_all( + &self, + trie_changes: &TrieChanges, + shard_uid: ShardUId, + store_update: &mut StoreUpdate, + ) -> StateRoot { + self.apply_all_inner(trie_changes, shard_uid, true, store_update) + } + + pub fn apply_memtrie_changes( + &self, + trie_changes: &TrieChanges, + shard_uid: ShardUId, + block_height: BlockHeight, + ) { + if let Some(memtries) = self.get_mem_tries(shard_uid) { + apply_memtrie_changes( + &mut memtries.write().unwrap(), + trie_changes + .mem_trie_changes + .as_ref() + .expect("Memtrie changes must be present if memtrie is loaded"), + block_height, + ); + } else { + assert!( + trie_changes.mem_trie_changes.is_none(), + "Memtrie changes must not be present if memtrie is not loaded" + ); + } + } + + /// Returns the status of the given shard of flat storage in the state snapshot. + /// `sync_prev_prev_hash` needs to match the block hash that identifies that snapshot. + pub fn get_snapshot_flat_storage_status( + &self, + sync_prev_prev_hash: CryptoHash, + shard_uid: ShardUId, + ) -> Result { + let (_store, manager) = self.get_state_snapshot(&sync_prev_prev_hash)?; + Ok(manager.get_flat_storage_status(shard_uid)) + } + + /// Removes all trie state values from store for a given shard_uid + /// Useful when we are trying to delete state of parent shard after resharding + /// Note that flat storage needs to be handled separately + pub fn delete_trie_for_shard(&self, shard_uid: ShardUId, store_update: &mut StoreUpdate) { + // Clear both caches and remove state values from store + self.0.caches.write().expect(POISONED_LOCK_ERR).remove(&shard_uid); + self.0.view_caches.write().expect(POISONED_LOCK_ERR).remove(&shard_uid); + remove_all_state_values(store_update, shard_uid); + } + + /// Should be called upon startup to load in-memory tries for enabled shards. + pub fn load_mem_tries_for_enabled_shards( + &self, + shard_uids: &[ShardUId], + ) -> Result<(), StorageError> { + let trie_config = &self.0.trie_config; + let shard_uids_to_load = shard_uids + .iter() + .copied() + .filter(|shard_uid| { + trie_config.load_mem_tries_for_all_shards + || trie_config.load_mem_tries_for_shards.contains(shard_uid) + }) + .collect::>(); + let store = self.0.store.clone(); + info!(target: "memtrie", "Loading tries to memory for shards {:?}...", shard_uids_to_load); + shard_uids_to_load + .par_iter() + .map(|shard_uid| -> Result<(), StorageError> { + let mem_tries = load_trie_from_flat_state_and_delta(&store, *shard_uid)?; + self.0 + .mem_tries + .write() + .unwrap() + .insert(*shard_uid, Arc::new(RwLock::new(mem_tries))); + Ok(()) + }) + .collect::>>() + .into_iter() + .collect::>()?; + + info!(target: "memtrie", "Memtries loading complete for shards {:?}", shard_uids_to_load); + Ok(()) + } + + /// Retrieves the in-memory tries for the shard. + pub fn get_mem_tries(&self, shard_uid: ShardUId) -> Option>> { + let guard = self.0.mem_tries.write().unwrap(); + guard.get(&shard_uid).cloned() + } + + /// Garbage collects the in-memory tries for the shard up to (and including) the given + /// height. + pub fn delete_memtrie_roots_up_to_height(&self, shard_uid: ShardUId, height: BlockHeight) { + if let Some(memtries) = self.get_mem_tries(shard_uid) { + memtries.write().unwrap().delete_until_height(height); + } + } +} + +pub struct WrappedTrieChanges { + tries: ShardTries, + shard_uid: ShardUId, + trie_changes: TrieChanges, + state_changes: Vec, + block_hash: CryptoHash, + block_height: BlockHeight, +} + +// Partial implementation. Skips `tries` due to its complexity and +// `trie_changes` and `state_changes` due to their large volume. +impl std::fmt::Debug for WrappedTrieChanges { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("WrappedTrieChanges") + .field("tries", &"") + .field("shard_uid", &self.shard_uid) + .field("trie_changes", &"") + .field("state_changes", &"") + .field("block_hash", &self.block_hash) + .field("block_height", &self.block_height) + .finish() + } +} + +impl WrappedTrieChanges { + pub fn new( + tries: ShardTries, + shard_uid: ShardUId, + trie_changes: TrieChanges, + state_changes: Vec, + block_hash: CryptoHash, + block_height: BlockHeight, + ) -> Self { + WrappedTrieChanges { + tries, + shard_uid, + trie_changes, + state_changes, + block_hash, + block_height, + } + } + + pub fn state_changes(&self) -> &[RawStateChangesWithTrieKey] { + &self.state_changes + } + + pub fn apply_mem_changes(&self) { + self.tries.apply_memtrie_changes(&self.trie_changes, self.shard_uid, self.block_height); + } + + /// Save insertions of trie nodes into Store. + pub fn insertions_into(&self, store_update: &mut StoreUpdate) { + self.tries.apply_insertions(&self.trie_changes, self.shard_uid, store_update) + } + + /// Save deletions of trie nodes into Store. + pub fn deletions_into(&self, store_update: &mut StoreUpdate) { + self.tries.apply_deletions(&self.trie_changes, self.shard_uid, store_update) + } + + /// Save state changes into Store. + /// + /// NOTE: the changes are drained from `self`. + pub fn state_changes_into(&mut self, store_update: &mut StoreUpdate) { + for mut change_with_trie_key in self.state_changes.drain(..) { + assert!( + !change_with_trie_key.changes.iter().any(|RawStateChange { cause, .. }| matches!( + cause, + StateChangeCause::NotWritableToDisk + )), + "NotWritableToDisk changes must never be finalized." + ); + + // Resharding changes must not be finalized, however they may be introduced here when we are + // evaluating changes for resharding in process_resharding_results function + change_with_trie_key + .changes + .retain(|change| change.cause != StateChangeCause::Resharding); + if change_with_trie_key.changes.is_empty() { + continue; + } + + let storage_key = if cfg!(feature = "serialize_all_state_changes") { + // Serialize all kinds of state changes without any filtering. + // Without this it's not possible to replay state changes to get an identical state root. + + // This branch will become the default in the near future. + + match change_with_trie_key.trie_key.get_account_id() { + // If a TrieKey itself doesn't identify the Shard, then we need to add shard id to the row key. + None => KeyForStateChanges::delayed_receipt_key_from_trie_key( + &self.block_hash, + &change_with_trie_key.trie_key, + &self.shard_uid, + ), + // TrieKey has enough information to identify the shard it comes from. + _ => KeyForStateChanges::from_trie_key( + &self.block_hash, + &change_with_trie_key.trie_key, + ), + } + } else { + // This branch is the current uncd behavior. + // Only a subset of state changes get serialized. + + // Filtering trie keys for user facing RPC reporting. + // NOTE: If the trie key is not one of the account specific, it may cause key conflict + // when the node tracks multiple shards. See #2563. + match &change_with_trie_key.trie_key { + TrieKey::Account { .. } + | TrieKey::ContractCode { .. } + | TrieKey::AccessKey { .. } + | TrieKey::ContractData { .. } => {} + _ => continue, + }; + KeyForStateChanges::from_trie_key(&self.block_hash, &change_with_trie_key.trie_key) + }; + + store_update.set( + DBCol::StateChanges, + storage_key.as_ref(), + &borsh::to_vec(&change_with_trie_key).expect("Borsh serialize cannot fail"), + ); + } + } + + pub fn trie_changes_into(&mut self, store_update: &mut StoreUpdate) -> std::io::Result<()> { + store_update.set_ser( + DBCol::TrieChanges, + &shard_layout::get_block_shard_uid(&self.block_hash, &self.shard_uid), + &self.trie_changes, + ) + } +} + +#[derive(thiserror::Error, Debug)] +pub enum KeyForStateChangesError { + #[error("Row key of StateChange of kind DelayedReceipt or DelayedReceiptIndices doesn't contain ShardUId: row_key: {0:?} ; trie_key: {1:?}")] + DelayedReceiptRowKeyError(Vec, TrieKey), +} + +#[derive(derive_more::AsRef, derive_more::Into)] +pub struct KeyForStateChanges(Vec); + +impl KeyForStateChanges { + const PREFIX_LEN: usize = CryptoHash::LENGTH; + + fn new(block_hash: &CryptoHash, reserve_capacity: usize) -> Self { + let mut key_prefix = Vec::with_capacity(block_hash.as_bytes().len() + reserve_capacity); + key_prefix.extend(block_hash.as_bytes()); + Self(key_prefix) + } + + pub fn for_block(block_hash: &CryptoHash) -> Self { + Self::new(block_hash, 0) + } + + pub fn from_raw_key(block_hash: &CryptoHash, raw_key: &[u8]) -> Self { + let mut key = Self::new(block_hash, raw_key.len()); + key.0.extend(raw_key); + key + } + + pub fn from_trie_key(block_hash: &CryptoHash, trie_key: &TrieKey) -> Self { + let mut key = Self::new(block_hash, trie_key.len()); + trie_key.append_into(&mut key.0); + key + } + + /// Without changing the existing TrieKey format, encodes ShardUId into the row key. + /// See `delayed_receipt_key_from_trie_key` for decoding this row key. + pub fn delayed_receipt_key_from_trie_key( + block_hash: &CryptoHash, + trie_key: &TrieKey, + shard_uid: &ShardUId, + ) -> Self { + let mut key = Self::new(block_hash, trie_key.len() + std::mem::size_of::()); + trie_key.append_into(&mut key.0); + key.0.extend(shard_uid.to_bytes()); + key + } + + /// Extracts ShardUId from row key which contains ShardUId encoded. + /// See `delayed_receipt_key_from_trie_key` for encoding ShardUId into the row key. + pub fn delayed_receipt_key_decode_shard_uid( + row_key: &[u8], + block_hash: &CryptoHash, + trie_key: &TrieKey, + ) -> Result { + let prefix = KeyForStateChanges::from_trie_key(block_hash, trie_key); + let prefix = prefix.as_ref(); + + let suffix = &row_key[prefix.len()..]; + let shard_uid = ShardUId::try_from(suffix).map_err(|_err| { + KeyForStateChangesError::DelayedReceiptRowKeyError(row_key.to_vec(), trie_key.clone()) + })?; + Ok(shard_uid) + } + + /// Iterates over deserialized row values where row key matches `self`. + pub fn find_iter<'a>( + &'a self, + store: &'a Store, + ) -> impl Iterator> + 'a { + // Split off the irrelevant part of the key, so only the original trie_key is left. + self.find_rows_iter(store).map(|row| row.map(|kv| kv.1)) + } + + /// Iterates over deserialized row values where the row key matches `self` exactly. + pub fn find_exact_iter<'a>( + &'a self, + store: &'a Store, + ) -> impl Iterator> + 'a { + let trie_key_len = self.0.len() - Self::PREFIX_LEN; + self.find_iter(store).filter_map(move |result| match result { + Ok(changes) if changes.trie_key.len() == trie_key_len => { + debug_assert_eq!(changes.trie_key.to_vec(), &self.0[Self::PREFIX_LEN..]); + Some(Ok(changes)) + } + Ok(_) => None, + Err(err) => Some(Err(err)), + }) + } + + /// Iterates over pairs of `(row key, deserialized row value)` where row key matches `self`. + pub fn find_rows_iter<'a>( + &'a self, + store: &'a Store, + ) -> impl Iterator, RawStateChangesWithTrieKey), std::io::Error>> + 'a + { + debug_assert!( + self.0.len() >= Self::PREFIX_LEN, + "Key length: {}, prefix length: {}, key: {:?}", + self.0.len(), + Self::PREFIX_LEN, + self.0 + ); + store.iter_prefix_ser::(DBCol::StateChanges, &self.0).map( + move |change| { + // Split off the irrelevant part of the key, so only the original trie_key is left. + let (key, state_changes) = change?; + debug_assert!(key.starts_with(&self.0), "Key: {:?}, row key: {:?}", self.0, key); + Ok((key, state_changes)) + }, + ) + } +} + +#[cfg(test)] +mod test { + use crate::{ + config::TrieCacheConfig, test_utils::create_test_store, + trie::DEFAULT_SHARD_CACHE_TOTAL_SIZE_LIMIT, TrieConfig, + }; + + use super::*; + use std::{assert_eq, str::FromStr}; + + fn create_trie() -> ShardTries { + let store = create_test_store(); + let trie_cache_config = TrieCacheConfig { + default_max_bytes: DEFAULT_SHARD_CACHE_TOTAL_SIZE_LIMIT, + per_shard_max_bytes: Default::default(), + shard_cache_deletions_queue_capacity: 0, + }; + let trie_config = TrieConfig { + shard_cache_config: trie_cache_config.clone(), + view_shard_cache_config: trie_cache_config, + enable_receipt_prefetching: false, + sweat_prefetch_receivers: Vec::new(), + sweat_prefetch_senders: Vec::new(), + load_mem_tries_for_shards: Vec::new(), + load_mem_tries_for_all_shards: false, + }; + let shard_uids = Vec::from([ShardUId::single_shard()]); + ShardTries::new( + store.clone(), + trie_config, + &shard_uids, + FlatStorageManager::new(store), + StateSnapshotConfig::default(), + ) + } + + #[test] + fn test_delayed_receipt_row_key() { + let trie_key1 = TrieKey::DelayedReceipt { index: 1 }; + let trie_key2 = TrieKey::DelayedReceiptIndices {}; + let shard_uid = ShardUId { version: 10, shard_id: 5 }; + + // Random value. + let block_hash = + CryptoHash::from_str("32222222222233333333334444444444445555555777").unwrap(); + + for trie_key in [trie_key1.clone(), trie_key2] { + let row_key = KeyForStateChanges::delayed_receipt_key_from_trie_key( + &block_hash, + &trie_key, + &shard_uid, + ); + + let got_shard_uid = KeyForStateChanges::delayed_receipt_key_decode_shard_uid( + row_key.as_ref(), + &block_hash, + &trie_key, + ) + .unwrap(); + assert_eq!(shard_uid, got_shard_uid); + } + + // Forget to add a ShardUId to the key, fail to extra a ShardUId from the key. + let row_key_without_shard_uid = KeyForStateChanges::from_trie_key(&block_hash, &trie_key1); + assert!(KeyForStateChanges::delayed_receipt_key_decode_shard_uid( + row_key_without_shard_uid.as_ref(), + &block_hash, + &trie_key1 + ) + .is_err()); + + // Add an extra byte to the key, fail to extra a ShardUId from the key. + let mut row_key_extra_bytes = KeyForStateChanges::delayed_receipt_key_from_trie_key( + &block_hash, + &trie_key1, + &shard_uid, + ); + row_key_extra_bytes.0.extend([8u8]); + assert!(KeyForStateChanges::delayed_receipt_key_decode_shard_uid( + row_key_extra_bytes.as_ref(), + &block_hash, + &trie_key1 + ) + .is_err()); + + // This is the internal detail of how delayed_receipt_key_from_trie_key() works. + let mut row_key_with_single_shard_uid = + KeyForStateChanges::from_trie_key(&block_hash, &trie_key1); + row_key_with_single_shard_uid.0.extend(shard_uid.to_bytes()); + assert_eq!( + KeyForStateChanges::delayed_receipt_key_decode_shard_uid( + row_key_with_single_shard_uid.as_ref(), + &block_hash, + &trie_key1 + ) + .unwrap(), + shard_uid + ); + } + + //TODO(jbajic) Simplify logic for creating configuration + #[test] + fn test_insert_delete_trie_cache() { + let shard_uid = ShardUId::single_shard(); + let tries = create_trie(); + let trie_caches = &tries.0.caches; + // Assert only one cache for one shard exists + assert_eq!(trie_caches.read().unwrap().len(), 1); + // Assert the shard uid is correct + assert!(trie_caches.read().unwrap().get(&shard_uid).is_some()); + + // Read from cache + let key = CryptoHash::hash_borsh("alice"); + let val: Vec = Vec::from([0, 1, 2, 3, 4]); + + assert!(trie_caches.read().unwrap().get(&shard_uid).unwrap().get(&key).is_none()); + + let insert_ops = Vec::from([(&key, Some(val.as_slice()))]); + tries.update_cache(insert_ops, shard_uid); + assert_eq!( + trie_caches.read().unwrap().get(&shard_uid).unwrap().get(&key).unwrap().to_vec(), + val + ); + + let deletions_ops = Vec::from([(&key, None)]); + tries.update_cache(deletions_ops, shard_uid); + assert!(trie_caches.read().unwrap().get(&shard_uid).unwrap().get(&key).is_none()); + } + + #[test] + fn test_shard_cache_max_value() { + let store = create_test_store(); + let trie_cache_config = TrieCacheConfig { + default_max_bytes: DEFAULT_SHARD_CACHE_TOTAL_SIZE_LIMIT, + per_shard_max_bytes: Default::default(), + shard_cache_deletions_queue_capacity: 0, + }; + let trie_config = TrieConfig { + shard_cache_config: trie_cache_config.clone(), + view_shard_cache_config: trie_cache_config, + enable_receipt_prefetching: false, + sweat_prefetch_receivers: Vec::new(), + sweat_prefetch_senders: Vec::new(), + load_mem_tries_for_shards: Vec::new(), + load_mem_tries_for_all_shards: false, + }; + let shard_uids = Vec::from([ShardUId { shard_id: 0, version: 0 }]); + let shard_uid = *shard_uids.first().unwrap(); + + let trie = ShardTries::new( + store.clone(), + trie_config, + &shard_uids, + FlatStorageManager::new(store), + StateSnapshotConfig::default(), + ); + + let trie_caches = &trie.0.caches; + + // Insert into cache value at the configured maximum size + let key = CryptoHash::hash_borsh("alice"); + let val: Vec = vec![0; TrieConfig::max_cached_value_size() - 1]; + let insert_ops = Vec::from([(&key, Some(val.as_slice()))]); + trie.update_cache(insert_ops, shard_uid); + assert_eq!( + trie_caches.read().unwrap().get(&shard_uid).unwrap().get(&key).unwrap().to_vec(), + val + ); + + // Try to insert into cache value bigger then the maximum allowed size + let key = CryptoHash::from_str("32222222222233333333334444444444445555555777").unwrap(); + let val: Vec = vec![0; TrieConfig::max_cached_value_size()]; + let insert_ops = Vec::from([(&key, Some(val.as_slice()))]); + trie.update_cache(insert_ops, shard_uid); + assert!(trie_caches.read().unwrap().get(&shard_uid).unwrap().get(&key).is_none()); + } + + #[test] + fn test_delete_trie_for_shard() { + let shard_uid = ShardUId::single_shard(); + let tries = create_trie(); + + let key = CryptoHash::hash_borsh("alice").as_bytes().to_vec(); + let val: Vec = Vec::from([0, 1, 2, 3, 4]); + + // insert some data + let trie = tries.get_trie_for_shard(shard_uid, CryptoHash::default()); + let trie_changes = trie.update(vec![(key, Some(val))]).unwrap(); + let mut store_update = tries.store_update(); + tries.apply_insertions(&trie_changes, shard_uid, &mut store_update); + store_update.commit().unwrap(); + + // delete trie for shard_uid + let mut store_update = tries.store_update(); + tries.delete_trie_for_shard(shard_uid, &mut store_update); + store_update.commit().unwrap(); + + // verify if data and caches are deleted + assert!(tries.0.caches.read().unwrap().get(&shard_uid).is_none()); + assert!(tries.0.view_caches.read().unwrap().get(&shard_uid).is_none()); + let store = tries.get_store(); + let key_prefix = shard_uid.to_bytes(); + let mut iter = store.iter_prefix(DBCol::State, &key_prefix); + assert!(iter.next().is_none()); + } +} diff --git a/core/store/src/trie/state_parts.rs b/core/store/src/trie/state_parts.rs new file mode 100644 index 000000000..b0a949cf0 --- /dev/null +++ b/core/store/src/trie/state_parts.rs @@ -0,0 +1,1274 @@ +//! Logic for splitting state defined by `Trie` into parts. +//! +//! Needed when a node wants to download state from scratch. Single shard state size +//! is on the order of GBs, and state part size is expected to be on the order of MBs. +//! +//! State partition is defined using DFS traversal of trie. All parts are disjoint and +//! contiguous in this order. Left boundary of i-th part is determined by computing +//! prefix sums of memory usages of nodes. +//! A bit more precisely - memory usage threshold for i-th part is approximately +//! `total_size / num_parts * part_id`. The boundary itself is a first node which +//! corresponds to some key-value pair and prefix memory usage for it is greater +//! than a threshold. +//! +//! We include paths from root to both boundaries in state parts, because they are +//! necessary for receiver to prove existence of all nodes knowing just a state root. +//! Moreover, we include all left siblings for each path, because they are +//! necessary to prove its position in the list of prefix sums. + +use crate::flat::{FlatStateChanges, FlatStateIterator}; +use crate::trie::iterator::TrieTraversalItem; +use crate::trie::nibble_slice::NibbleSlice; +use crate::trie::trie_storage::TrieMemoryPartialStorage; +use crate::trie::{ + ApplyStatePartResult, NodeHandle, RawTrieNodeWithSize, TrieNode, TrieNodeWithSize, +}; +use crate::{metrics, PartialStorage, StorageError, Trie, TrieChanges}; +use borsh::BorshDeserialize; +use unc_primitives::challenge::PartialState; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::state::FlatStateValue; +use unc_primitives::state_part::PartId; +use unc_primitives::state_record::is_contract_code_key; +use unc_primitives::types::{ShardId, StateRoot}; +use unc_vm_runner::ContractCode; +use std::collections::{HashMap, HashSet}; +use std::rc::Rc; +use std::sync::Arc; + +use super::TrieRefcountDeltaMap; + +/// Trie key in nibbles corresponding to the right boundary for the last state part. +/// Guaranteed to be bigger than any existing trie key. +const LAST_STATE_PART_BOUNDARY: &[u8; 1] = &[16]; + +impl Trie { + /// Descends into node corresponding to `part_id`-th boundary node if state + /// is divided into `num_parts` parts. + /// Visits all the left siblings of nodes on the way to the boundary node. + /// Guarantees that the node corresponds to some KV pair or returns `StorageError`, + /// except corner cases: + /// - for part 0, boundary is empty key; + /// - for part `num_parts`, boundary is `LAST_STATE_PART_BOUNDARY`. + /// This guarantees that parts cover all nodes in trie. + /// Returns trie key in nibbles corresponding to this node. + /// + /// Note that it is used for both boundary generation and verifying. To verify + /// a boundary, it is enough to check that method doesn't return `StorageError` + /// and visits all provided nodes. + pub fn find_state_part_boundary( + &self, + part_id: u64, + num_parts: u64, + ) -> Result, StorageError> { + if part_id > num_parts { + return Err(StorageError::StorageInternalError); + } + if part_id == 0 { + return Ok(vec![]); + } + if part_id == num_parts { + return Ok(LAST_STATE_PART_BOUNDARY.to_vec()); + } + let root_node = self.retrieve_node(&self.root)?.1; + let total_size = root_node.memory_usage; + let size_start = total_size / num_parts * part_id + part_id.min(total_size % num_parts); + self.find_node_in_dfs_order(&root_node, size_start) + } + + /// Generates state parts using the trie storage (i.e. State) and not using + /// flat storage (i.e. FlatState). + pub fn get_trie_nodes_for_part_without_flat_storage( + &self, + part_id: PartId, + ) -> Result { + let with_recording = self.recording_reads(); + with_recording.visit_nodes_for_state_part(part_id)?; + let recorded = with_recording.recorded_storage().unwrap(); + Ok(recorded.nodes) + } + + /// Helper to create iterator over flat storage entries corresponding to + /// its head, shard for which trie was created and the range of keys given + /// in nibbles. + fn iter_flat_state_entries<'a>( + &'a self, + nibbles_begin: Vec, + nibbles_end: Vec, + ) -> Result, StorageError> { + let flat_storage_chunk_view = match &self.flat_storage_chunk_view { + None => { + return Err(StorageError::StorageInconsistentState( + "Flat storage chunk view not found".to_string(), + )); + } + Some(chunk_view) => chunk_view, + }; + + // If left key in nibbles is already the largest, return empty + // iterator. Otherwise convert it to key in bytes. + let key_begin = if nibbles_begin == LAST_STATE_PART_BOUNDARY { + return Ok(Box::new(std::iter::empty())); + } else { + Some(NibbleSlice::nibbles_to_bytes(&nibbles_begin)) + }; + + // Convert right key in nibbles to key in bytes. + let key_end = if nibbles_end == LAST_STATE_PART_BOUNDARY { + None + } else { + Some(NibbleSlice::nibbles_to_bytes(&nibbles_end)) + }; + + Ok(flat_storage_chunk_view + .iter_flat_state_entries(key_begin.as_deref(), key_end.as_deref())) + } + + /// Determines the boundaries of a state part by accessing the Trie (i.e. State column). + /// Returns the keys of the boundaries and also a set of Trie nodes needed to validate the state parts. + pub fn get_state_part_boundaries( + &self, + part_id: PartId, + ) -> Result<(PartialState, Vec, Vec), StorageError> { + let shard_id: ShardId = self.flat_storage_chunk_view.as_ref().map_or( + ShardId::MAX, // Fake value for metrics. + |chunk_view| chunk_view.shard_uid().shard_id as ShardId, + ); + let _span = tracing::debug_span!( + target: "state-parts", + "get_state_part_boundaries", + ?shard_id, + part_id = part_id.idx, + num_parts = part_id.total) + .entered(); + let _timer = metrics::GET_STATE_PART_BOUNDARIES_ELAPSED + .with_label_values(&[&shard_id.to_string()]) + .start_timer(); + + let PartId { idx, total } = part_id; + + // 1. Extract nodes corresponding to state part boundaries. + let recording_trie = self.recording_reads(); + let boundaries_read_timer = metrics::GET_STATE_PART_BOUNDARIES_ELAPSED + .with_label_values(&[&shard_id.to_string()]) + .start_timer(); + let nibbles_begin = recording_trie.find_state_part_boundary(part_id.idx, part_id.total)?; + let nibbles_end = + recording_trie.find_state_part_boundary(part_id.idx + 1, part_id.total)?; + let boundaries_read_duration = boundaries_read_timer.stop_and_record(); + let recorded_trie = recording_trie.recorded_storage().unwrap(); + + tracing::debug!( + target: "state-parts", + idx, + total, + ?boundaries_read_duration, + "Found state part boundaries", + ); + Ok((recorded_trie.nodes, nibbles_begin, nibbles_end)) + } + + /// Creates state part using only the flat storage (i.e. FlatState). + /// The boundaries of the state part must already be known. + /// The nodes representing the boundaries must already be provided. + /// + /// * part_id - number of the state part, mainly for metrics. + /// * partial_state - nodes needed to generate and proof state part boundaries. + /// * nibbles_begin and nibbles_end specify the range of flat storage to be read. + /// * state_trie - provides access to State for random lookups of values by hash. + pub fn get_trie_nodes_for_part_with_flat_storage( + &self, + part_id: PartId, + partial_state: PartialState, + nibbles_begin: Vec, + nibbles_end: Vec, + state_trie: &Trie, + ) -> Result { + let shard_id: ShardId = self.flat_storage_chunk_view.as_ref().map_or( + ShardId::MAX, // Fake value for metrics. + |chunk_view| chunk_view.shard_uid().shard_id as ShardId, + ); + let _span = tracing::debug_span!( + target: "state-parts", + "get_trie_nodes_for_part_with_flat_storage", + ?shard_id, + part_id = part_id.idx, + num_parts = part_id.total) + .entered(); + let _timer = metrics::GET_STATE_PART_NODES_WITH_FS_ELAPSED + .with_label_values(&[&shard_id.to_string()]) + .start_timer(); + // TODO(nikurt): Simplify. This is a long function with complex logic. + + let PartialState::TrieValues(path_boundary_nodes) = partial_state; + + // 1. Extract all key-value pairs in state part from flat storage. + let values_read_timer = metrics::GET_STATE_PART_READ_FS_ELAPSED + .with_label_values(&[&shard_id.to_string()]) + .start_timer(); + let flat_state_iter = self.iter_flat_state_entries(nibbles_begin, nibbles_end)?; + let mut value_refs = vec![]; + let mut values_inlined = 0; + let mut all_state_part_items: Vec<_> = flat_state_iter + .filter_map(|result| { + let (k, v) = result.expect("failed to read FlatState entry"); + match v { + FlatStateValue::Ref(value_ref) => { + value_refs.push((k, value_ref.hash)); + None + } + FlatStateValue::Inlined(value) => { + values_inlined += 1; + Some((k, Some(value))) + } + } + }) + .collect::>(); + let values_read_duration = values_read_timer.stop_and_record(); + + // 2. Lookup Referenced values in State. Note that FlatStorage snapshots don't have State. + let lookup_values_timer = metrics::GET_STATE_PART_LOOKUP_REF_VALUES_ELAPSED + .with_label_values(&[&shard_id.to_string()]) + .start_timer(); + let looked_up_value_refs: Vec<_> = value_refs + .iter() + .map(|(k, hash)| Ok((k.clone(), Some(state_trie.retrieve_value(hash)?.to_vec())))) + .collect::>() + .unwrap(); + all_state_part_items.extend(looked_up_value_refs.iter().cloned()); + let lookup_values_duration = lookup_values_timer.stop_and_record(); + + // 3. Create trie out of all key-value pairs. + let local_trie_creation_timer = metrics::GET_STATE_PART_CREATE_TRIE_ELAPSED + .with_label_values(&[&shard_id.to_string()]) + .start_timer(); + let local_state_part_trie = + Trie::new(Rc::new(TrieMemoryPartialStorage::default()), StateRoot::new(), None); + let local_state_part_nodes = + local_state_part_trie.update(all_state_part_items.into_iter())?.insertions; + let local_trie_creation_duration = local_trie_creation_timer.stop_and_record(); + + // 4. Unite all nodes in memory, traverse trie based on them, return set of visited nodes. + let final_part_creation_timer = metrics::GET_STATE_PART_COMBINE_ELAPSED + .with_label_values(&[&shard_id.to_string()]) + .start_timer(); + let boundary_nodes_storage: HashMap<_, _> = + path_boundary_nodes.iter().map(|entry| (hash(entry), entry.clone())).collect(); + let mut disk_read_hashes: HashSet<_> = boundary_nodes_storage.keys().cloned().collect(); + disk_read_hashes.extend(value_refs.iter().map(|(_, hash)| hash)); + let mut all_nodes: HashMap> = HashMap::new(); + all_nodes.extend(boundary_nodes_storage); + all_nodes.extend( + local_state_part_nodes + .iter() + .map(|entry| (*entry.hash(), entry.payload().to_vec().into())), + ); + let final_trie = + Trie::new(Rc::new(TrieMemoryPartialStorage::new(all_nodes)), self.root, None); + + final_trie.visit_nodes_for_state_part(part_id)?; + let final_trie_storage = final_trie.storage.as_partial_storage().unwrap(); + let final_state_part_nodes = final_trie_storage.partial_state(); + let PartialState::TrieValues(trie_values) = &final_state_part_nodes; + let final_part_creation_duration = final_part_creation_timer.stop_and_record(); + + // Compute how many nodes were recreated from memory. + let state_part_num_nodes = trie_values.len(); + let in_memory_created_nodes = + trie_values.iter().filter(|entry| !disk_read_hashes.contains(&hash(*entry))).count(); + tracing::debug!( + target: "state-parts", + ?part_id, + values_ref = value_refs.len(), + %values_inlined, + %in_memory_created_nodes, + %state_part_num_nodes, + ?values_read_duration, + ?lookup_values_duration, + ?local_trie_creation_duration, + ?final_part_creation_duration, + "Created state part", + ); + + metrics::GET_STATE_PART_WITH_FS_VALUES_INLINED + .with_label_values(&[&shard_id.to_string()]) + .inc_by(values_inlined); + metrics::GET_STATE_PART_WITH_FS_VALUES_REF + .with_label_values(&[&shard_id.to_string()]) + .inc_by(value_refs.len() as u64); + metrics::GET_STATE_PART_WITH_FS_NODES_FROM_DISK + .with_label_values(&[&shard_id.to_string()]) + .inc_by(disk_read_hashes.len() as u64); + metrics::GET_STATE_PART_WITH_FS_NODES_IN_MEMORY + .with_label_values(&[&shard_id.to_string()]) + .inc_by(in_memory_created_nodes as u64); + metrics::GET_STATE_PART_WITH_FS_NODES + .with_label_values(&[&shard_id.to_string()]) + .inc_by(state_part_num_nodes as u64); + + Ok(final_state_part_nodes) + } + + /// Assume we lay out all trie nodes in dfs order visiting children after the parent. + /// We take all node sizes (memory_usage_direct()) and take all nodes intersecting with + /// [size_start, size_end) interval, also all nodes necessary to prove it and some + /// additional nodes defined by the current implementation (TODO #1603 strict spec). + /// + /// Creating a StatePart takes all these nodes, validating a StatePart checks that it has the + /// right set of nodes. + fn visit_nodes_for_state_part(&self, part_id: PartId) -> Result<(), StorageError> { + let path_begin = self.find_state_part_boundary(part_id.idx, part_id.total)?; + let path_end = self.find_state_part_boundary(part_id.idx + 1, part_id.total)?; + + let mut iterator = self.iter()?; + let nodes_list = iterator.visit_nodes_interval(&path_begin, &path_end)?; + tracing::debug!( + target: "state-parts", + num_nodes = nodes_list.len()); + + Ok(()) + } + + /// Helper method for `find_node_in_dfs_order`. + /// Tells a child in which we should go to find a node in dfs order corresponding + /// to `memory_threshold`. + /// Accumulates `memory_skipped` as memory used by all skipped nodes. + /// Returns false if we already found desired node and should stop the process. + fn find_child_in_dfs_order( + &self, + memory_threshold: u64, + node: &mut TrieNodeWithSize, + memory_skipped: &mut u64, + key_nibbles: &mut Vec, + ) -> Result { + *memory_skipped += node.node.memory_usage_direct_no_memory(); + + match &node.node { + TrieNode::Empty => Ok(false), + TrieNode::Leaf(key, _) => { + let (slice, _) = NibbleSlice::from_encoded(key); + key_nibbles.extend(slice.iter()); + + // Leaf must contain value, so we found the boundary. + Ok(false) + } + TrieNode::Branch(children, value_handle) => { + if *memory_skipped > memory_threshold && value_handle.is_some() { + // If we skipped enough memory and found some value, we found the boundary. + return Ok(false); + } + + let mut iter = children.iter(); + while let Some((index, child)) = iter.next() { + let child = if let NodeHandle::Hash(h) = child { + self.retrieve_node(h)?.1 + } else { + unreachable!("only possible while mutating") + }; + if *memory_skipped + child.memory_usage > memory_threshold { + core::mem::drop(iter); + key_nibbles.push(index); + *node = child; + return Ok(true); + } + *memory_skipped += child.memory_usage; + } + // This line should be unreachable if we descended into current node. + // TODO (#8997): test this case properly by simulating trie data corruption. + Err(StorageError::StorageInconsistentState(format!( + "Skipped all children of node {node:?} while searching for memory \ + threshold {memory_threshold} and skipped {memory_skipped}" + ))) + } + TrieNode::Extension(key, child_handle) => { + let child = match child_handle { + NodeHandle::InMemory(_) => unreachable!("only possible while mutating"), + NodeHandle::Hash(h) => self.retrieve_node(h)?.1, + }; + let (slice, _) = NibbleSlice::from_encoded(key); + key_nibbles.extend(slice.iter()); + *node = child; + Ok(true) + } + } + } + + /// Finds first node corresponding to some key-value pair, for which prefix memory usage + /// is greater than a given threshold. + /// Returns trie key in nibbles corresponding to this node. + fn find_node_in_dfs_order( + &self, + root_node: &TrieNodeWithSize, + memory_threshold: u64, + ) -> Result, StorageError> { + if root_node.memory_usage <= memory_threshold { + return Ok(LAST_STATE_PART_BOUNDARY.to_vec()); + } + let mut key_nibbles: Vec = Vec::new(); + let mut node = root_node.clone(); + let mut memory_skipped = 0u64; + while self.find_child_in_dfs_order( + memory_threshold, + &mut node, + &mut memory_skipped, + &mut key_nibbles, + )? {} + Ok(key_nibbles) + } + + /// Validates state part for given state root. + /// Returns error if state part is invalid and Ok otherwise. + pub fn validate_state_part( + state_root: &StateRoot, + part_id: PartId, + partial_state: PartialState, + ) -> Result<(), StorageError> { + let PartialState::TrieValues(nodes) = &partial_state; + let num_nodes = nodes.len(); + let trie = Trie::from_recorded_storage( + PartialStorage { nodes: partial_state }, + *state_root, + false, + ); + + trie.visit_nodes_for_state_part(part_id)?; + let storage = trie.storage.as_partial_storage().unwrap(); + + if storage.visited_nodes.borrow().len() != num_nodes { + // As all nodes belonging to state part were visited, there is some + // unexpected data in downloaded state part. + return Err(StorageError::UnexpectedTrieValue); + } + Ok(()) + } + + fn apply_state_part_impl( + state_root: &StateRoot, + part_id: PartId, + part: PartialState, + ) -> Result { + if state_root == &Trie::EMPTY_ROOT { + return Ok(ApplyStatePartResult { + trie_changes: TrieChanges::empty(Trie::EMPTY_ROOT), + flat_state_delta: Default::default(), + contract_codes: vec![], + }); + } + let trie = Trie::from_recorded_storage(PartialStorage { nodes: part }, *state_root, false); + let path_begin = trie.find_state_part_boundary(part_id.idx, part_id.total)?; + let path_end = trie.find_state_part_boundary(part_id.idx + 1, part_id.total)?; + let mut iterator = trie.iter()?; + let trie_traversal_items = iterator.visit_nodes_interval(&path_begin, &path_end)?; + let mut refcount_changes = TrieRefcountDeltaMap::new(); + let mut flat_state_delta = FlatStateChanges::default(); + let mut contract_codes = Vec::new(); + for TrieTraversalItem { hash, key } in trie_traversal_items { + let value = trie.retrieve_value(&hash)?; + refcount_changes.add(hash, value.to_vec(), 1); + if let Some(trie_key) = key { + let flat_state_value = FlatStateValue::on_disk(&value); + flat_state_delta.insert(trie_key.clone(), Some(flat_state_value)); + if is_contract_code_key(&trie_key) { + contract_codes.push(ContractCode::new(value.to_vec(), None)); + } + } + } + let (insertions, deletions) = refcount_changes.into_changes(); + Ok(ApplyStatePartResult { + trie_changes: TrieChanges { + old_root: Trie::EMPTY_ROOT, + new_root: *state_root, + insertions, + deletions, + mem_trie_changes: None, + }, + flat_state_delta, + contract_codes, + }) + } + + /// Applies state part and returns the storage changes for the state part and all contract codes extracted from it. + /// Writing all storage changes gives the complete trie. + pub fn apply_state_part( + state_root: &StateRoot, + part_id: PartId, + part: PartialState, + ) -> ApplyStatePartResult { + Self::apply_state_part_impl(state_root, part_id, part) + .expect("apply_state_part is guaranteed to succeed when each part is valid") + } + + pub fn get_memory_usage_from_serialized(bytes: &[u8]) -> Result { + RawTrieNodeWithSize::try_from_slice(bytes).map(|raw_node| raw_node.memory_usage).map_err( + |err| StorageError::StorageInconsistentState(format!("Failed to decode node: {err}")), + ) + } +} + +#[cfg(test)] +mod tests { + use assert_matches::assert_matches; + use std::collections::{HashMap, HashSet}; + use std::fmt::Debug; + use std::hash::Hash; + use std::sync::Arc; + + use rand::prelude::ThreadRng; + use rand::seq::SliceRandom; + use rand::Rng; + + use unc_primitives::hash::{hash, CryptoHash}; + + use crate::test_utils::{gen_changes, test_populate_trie, TestTriesBuilder}; + use crate::trie::iterator::CrumbStatus; + use crate::trie::{ + TrieRefcountAddition, TrieRefcountDeltaMap, TrieRefcountSubtraction, ValueHandle, + }; + + use super::*; + use crate::{DBCol, MissingTrieValueContext, TrieCachingStorage}; + use unc_primitives::shard_layout::ShardUId; + + /// Checks that sampling state boundaries always gives valid state keys + /// even if trie contains intermediate nodes. + #[test] + fn boundary_is_state_key() { + // Trie should contain at least two intermediate branches for strings + // "a" and "b". + let trie_changes = vec![ + (b"after".to_vec(), Some(vec![1])), + (b"alley".to_vec(), Some(vec![2])), + (b"berry".to_vec(), Some(vec![3])), + (b"brave".to_vec(), Some(vec![4])), + ]; + + // Number of state parts. Must be larger than number of state items to ensure + // that boundaries are nontrivial. + let num_parts = 10u64; + + let tries = TestTriesBuilder::new().build(); + let state_root = + test_populate_trie(&tries, &Trie::EMPTY_ROOT, ShardUId::single_shard(), trie_changes); + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), state_root); + + let nibbles_boundary = trie.find_state_part_boundary(0, num_parts).unwrap(); + assert!(nibbles_boundary.is_empty()); + + // Check that all boundaries correspond to some state key by calling `Trie::get`. + // Note that some state parts can be trivial, which is not a concern. + for part_id in 1..num_parts { + let nibbles_boundary = trie.find_state_part_boundary(part_id, num_parts).unwrap(); + let key_boundary = NibbleSlice::nibbles_to_bytes(&nibbles_boundary); + assert_matches!(trie.get(&key_boundary), Ok(Some(_))); + } + + let nibbles_boundary = trie.find_state_part_boundary(num_parts, num_parts).unwrap(); + assert_eq!(nibbles_boundary, LAST_STATE_PART_BOUNDARY); + } + + /// Checks that on degenerate case when trie is a single path, state + /// parts are still distributed evenly. + #[test] + fn single_path_trie() { + // Values should be big enough to ensure that node and key overhead are + // not significant. + let value_len = 1000usize; + // Corner case when trie is a single path from empty string to "aaaa". + let trie_changes = vec![ + (b"a".to_vec(), Some(vec![1; value_len])), + (b"aa".to_vec(), Some(vec![2; value_len])), + (b"aaa".to_vec(), Some(vec![3; value_len])), + (b"aaaa".to_vec(), Some(vec![4; value_len])), + ]; + // We reshard into `num_keys + 1` parts for convenience of testing, + // because right boundaries are exclusive. This way first part is + // empty and other parts contain exactly one key. + let num_parts = trie_changes.len() + 1; + + let tries = TestTriesBuilder::new().build(); + let state_root = test_populate_trie( + &tries, + &Trie::EMPTY_ROOT, + ShardUId::single_shard(), + trie_changes.clone(), + ); + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), state_root); + + for part_id in 1..num_parts { + let nibbles_boundary = + trie.find_state_part_boundary(part_id as u64, num_parts as u64).unwrap(); + let key_boundary = NibbleSlice::nibbles_to_bytes(&nibbles_boundary); + assert_eq!(key_boundary, trie_changes[part_id - 1].0); + } + } + + impl Trie { + /// Combines all parts and returns TrieChanges that can be applied to storage. + /// + /// # Input + /// parts[i] has trie nodes for part i + /// + /// # Errors + /// StorageError if data is inconsistent. Should never happen if each part was validated. + pub fn combine_state_parts_naive( + state_root: &StateRoot, + parts: &[PartialState], + ) -> Result { + let nodes = PartialState::TrieValues( + parts + .iter() + .flat_map(|PartialState::TrieValues(nodes)| nodes.iter()) + .cloned() + .collect(), + ); + let trie = Trie::from_recorded_storage(PartialStorage { nodes }, *state_root, false); + let mut insertions = , u32)>>::new(); + trie.traverse_all_nodes(|hash| { + if let Some((_bytes, rc)) = insertions.get_mut(hash) { + *rc += 1; + } else { + let bytes = trie.retrieve_value(hash)?; + insertions.insert(*hash, (bytes.to_vec(), 1)); + } + Ok(()) + })?; + let mut insertions = insertions + .into_iter() + .map(|(k, (v, rc))| TrieRefcountAddition { + trie_node_or_value_hash: k, + trie_node_or_value: v, + rc: std::num::NonZeroU32::new(rc).unwrap(), + }) + .collect::>(); + insertions.sort(); + Ok(TrieChanges { + old_root: Default::default(), + new_root: *state_root, + insertions, + deletions: vec![], + mem_trie_changes: None, + }) + } + + /// on_enter is applied for nodes as well as values + fn traverse_all_nodes Result<(), StorageError>>( + &self, + mut on_enter: F, + ) -> Result<(), StorageError> { + if self.root == Trie::EMPTY_ROOT { + return Ok(()); + } + let mut stack: Vec<(CryptoHash, TrieNodeWithSize, CrumbStatus)> = Vec::new(); + let root_node = self.retrieve_node(&self.root)?.1; + stack.push((self.root, root_node, CrumbStatus::Entering)); + while let Some((hash, node, position)) = stack.pop() { + if let CrumbStatus::Entering = position { + on_enter(&hash)?; + } + let mut on_enter_value = |value: &ValueHandle| { + if let ValueHandle::HashAndSize(value) = value { + on_enter(&value.hash) + } else { + unreachable!("only possible while mutating") + } + }; + match &node.node { + TrieNode::Empty => { + continue; + } + TrieNode::Leaf(_, value) => { + on_enter_value(value)?; + continue; + } + TrieNode::Branch(children, value) => match position { + CrumbStatus::Entering => { + if let Some(ref value) = value { + on_enter_value(value)?; + } + stack.push((hash, node, CrumbStatus::AtChild(0))); + continue; + } + CrumbStatus::AtChild(mut i) => loop { + if i >= 16 { + stack.push((hash, node, CrumbStatus::Exiting)); + break; + } + if let Some(NodeHandle::Hash(ref h)) = children[i] { + let h = *h; + let child = self.retrieve_node(&h)?.1; + stack.push((hash, node, CrumbStatus::AtChild(i + 1))); + stack.push((h, child, CrumbStatus::Entering)); + break; + } + i += 1; + }, + CrumbStatus::Exiting => { + continue; + } + CrumbStatus::At => { + continue; + } + }, + TrieNode::Extension(_key, child) => { + if let CrumbStatus::Entering = position { + if let NodeHandle::Hash(h) = child.clone() { + let child = self.retrieve_node(&h)?.1; + stack.push((h, node, CrumbStatus::Exiting)); + stack.push((h, child, CrumbStatus::Entering)); + } else { + unreachable!("only possible while mutating") + } + } + } + } + } + Ok(()) + } + } + + #[test] + fn test_combine_empty_trie_parts() { + let state_root = Trie::EMPTY_ROOT; + let _ = Trie::combine_state_parts_naive(&state_root, &[]).unwrap(); + let _ = Trie::validate_state_part( + &state_root, + PartId::new(0, 1), + PartialState::TrieValues(vec![]), + ) + .unwrap(); + let _ = Trie::apply_state_part( + &state_root, + PartId::new(0, 1), + PartialState::TrieValues(vec![]), + ); + } + + fn construct_trie_for_big_parts_1( + rng: &mut ThreadRng, + max_key_length: u64, + big_value_length: u64, + ) -> Vec<(Vec, Option>)> { + // Test #1: a long path where every node on the path has a large value + let mut trie_changes = Vec::new(); + for i in 0..max_key_length { + // ([255,255,..,255], big_value) + let key = (0..(i + 1)).map(|_| 255u8).collect::>(); + let value = (0..big_value_length).map(|_| rng.gen::()).collect::>(); + trie_changes.push((key, Some(value))); + } + trie_changes + } + + fn construct_trie_for_big_parts_2( + rng: &mut ThreadRng, + max_key_length: u64, + big_value_length: u64, + ) -> Vec<(Vec, Option>)> { + // Test #2: a long path where every node on the path has a large value, is a branch + // and each of its children is a branch. + let mut trie_changes = + construct_trie_for_big_parts_1(rng, max_key_length, big_value_length); + let small_value_length = 20; + for i in 0..max_key_length { + for x in 0u8..15u8 { + for y in 0u8..15u8 { + { + // ([255,255,..,255]xy, small_value) + // this means every 000..000 node is a branch and all of its children are branches + let mut key = (0..(i + 1)).map(|_| 255u8).collect::>(); + key.push(x * 16 + y); + let value = + (0..small_value_length).map(|_| rng.gen::()).collect::>(); + trie_changes.push((key, Some(value))); + } + { + let mut key = (0..i).map(|_| 255u8).collect::>(); + key.push(16 * 15 + x); + key.push(y * 16); + let value = + (0..small_value_length).map(|_| rng.gen::()).collect::>(); + trie_changes.push((key, Some(value))); + } + { + let mut key = (0..i).map(|_| 255u8).collect::>(); + key.push(16 * x + 15); + key.push(y); + let value = + (0..small_value_length).map(|_| rng.gen::()).collect::>(); + trie_changes.push((key, Some(value))); + } + } + } + } + trie_changes + } + + /// Helper function checking that for given trie generator size of each + /// part is approximately bounded by `total_size / num_parts` with overhead + /// for proof and trie items irregularity. + /// TODO (#8997): run it on largest keys (2KB) and values (4MB) allowed in mainnet. + fn run_test_parts_not_huge(gen_trie_changes: F, big_value_length: u64) + where + F: FnOnce(&mut ThreadRng, u64, u64) -> Vec<(Vec, Option>)>, + { + let mut rng = rand::thread_rng(); + let max_key_length = 50u64; + let max_key_length_in_nibbles = max_key_length * 2; + let max_node_children = 16; + let max_node_serialized_size = 32 * max_node_children + 100; // Full branch node overhead. + let max_proof_overhead = + max_key_length_in_nibbles * max_node_children * max_node_serialized_size; + let max_part_overhead = + big_value_length.max(max_key_length_in_nibbles * max_node_serialized_size * 2); + let trie_changes = gen_trie_changes(&mut rng, max_key_length, big_value_length); + let tries = TestTriesBuilder::new().build(); + let state_root = + test_populate_trie(&tries, &Trie::EMPTY_ROOT, ShardUId::single_shard(), trie_changes); + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), state_root); + let memory_size = trie.retrieve_root_node().unwrap().memory_usage; + for num_parts in [2, 3, 5, 10, 50].iter().cloned() { + let part_size_limit = (memory_size + num_parts - 1) / num_parts; + + for part_id in 0..num_parts { + // Compute proof with size and check that it doesn't exceed theoretical boundary for + // the path with full set of left siblings of maximal possible size. + let trie_recording = trie.recording_reads(); + let left_nibbles_boundary = + trie_recording.find_state_part_boundary(part_id, num_parts).unwrap(); + let left_key_boundary = NibbleSlice::nibbles_to_bytes(&left_nibbles_boundary); + if part_id != 0 { + assert_matches!(trie.get(&left_key_boundary), Ok(Some(_))); + } + let PartialState::TrieValues(proof_nodes) = + trie_recording.recorded_storage().unwrap().nodes; + let proof_size = proof_nodes.iter().map(|node| node.len()).sum::() as u64; + assert!( + proof_size <= max_proof_overhead, + "For part {}/{} left boundary proof size {} exceeds limit {}", + part_id, + num_parts, + proof_size, + max_proof_overhead + ); + + let PartialState::TrieValues(part_nodes) = trie + .get_trie_nodes_for_part_without_flat_storage(PartId::new(part_id, num_parts)) + .unwrap(); + // TODO (#8997): it's a bit weird that raw lengths are compared to + // config values. Consider better defined assertion. + let total_size = part_nodes.iter().map(|node| node.len()).sum::() as u64; + assert!( + total_size <= part_size_limit + proof_size + max_part_overhead, + "Part {}/{} is too big. Size: {}, size limit: {}", + part_id, + num_parts, + total_size, + part_size_limit + proof_size + max_part_overhead, + ); + } + } + } + + #[test] + fn test_parts_not_huge_1() { + run_test_parts_not_huge(construct_trie_for_big_parts_1, 100_000); + } + + /// TODO (#8997): consider: + /// * adding more testcases for big and small key/value lengths, other trie structures; + /// * speeding this test up. + #[test] + fn test_parts_not_huge_2() { + run_test_parts_not_huge(construct_trie_for_big_parts_2, 100_000); + } + + fn merge_trie_changes(changes: Vec) -> TrieChanges { + if changes.is_empty() { + return TrieChanges::empty(Trie::EMPTY_ROOT); + } + let new_root = changes[0].new_root; + let mut map = TrieRefcountDeltaMap::new(); + for changes_set in changes { + assert!(changes_set.deletions.is_empty(), "state parts only have insertions"); + for TrieRefcountAddition { trie_node_or_value_hash, trie_node_or_value, rc } in + changes_set.insertions + { + map.add(trie_node_or_value_hash, trie_node_or_value, rc.get()); + } + for TrieRefcountSubtraction { trie_node_or_value_hash, rc, .. } in changes_set.deletions + { + map.subtract(trie_node_or_value_hash, rc.get()); + } + } + let (insertions, deletions) = map.into_changes(); + TrieChanges { + old_root: Default::default(), + new_root, + insertions, + deletions, + mem_trie_changes: None, + } + } + + #[test] + fn test_combine_state_parts() { + let mut rng = rand::thread_rng(); + for _ in 0..2000 { + let tries = TestTriesBuilder::new().build(); + let trie_changes = gen_changes(&mut rng, 20); + let state_root = test_populate_trie( + &tries, + &Trie::EMPTY_ROOT, + ShardUId::single_shard(), + trie_changes.clone(), + ); + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), state_root); + let root_memory_usage = trie.retrieve_root_node().unwrap().memory_usage; + { + // Test that combining all parts gets all nodes + let num_parts = rng.gen_range(2..10); + let parts = (0..num_parts) + .map(|part_id| { + trie.get_trie_nodes_for_part_without_flat_storage(PartId::new( + part_id, num_parts, + )) + .unwrap() + }) + .collect::>(); + + let trie_changes = check_combine_state_parts(trie.get_root(), num_parts, &parts); + + let mut nodes = >>::new(); + let sizes_vec = parts + .iter() + .map(|PartialState::TrieValues(nodes)| { + nodes.iter().map(|node| node.len()).sum::() + }) + .collect::>(); + + for part in parts { + let PartialState::TrieValues(part_nodes) = part; + for node in part_nodes { + nodes.insert(hash(&node), node); + } + } + let all_nodes = nodes.into_iter().map(|(_hash, node)| node).collect::>(); + assert_eq!(all_nodes.len(), trie_changes.insertions.len()); + let size_of_all = all_nodes.iter().map(|node| node.len()).sum::(); + let num_nodes = all_nodes.len(); + assert_eq!( + Trie::validate_state_part( + trie.get_root(), + PartId::new(0, 1), + PartialState::TrieValues(all_nodes), + ), + Ok(()) + ); + + let sum_of_sizes = sizes_vec.iter().sum::(); + // Manually check that sizes are reasonable + println!("------------------------------"); + println!("Number of nodes: {:?}", num_nodes); + println!("Sizes of parts: {:?}", sizes_vec); + println!( + "All nodes size: {:?}, sum_of_sizes: {:?}, memory_usage: {:?}", + size_of_all, sum_of_sizes, root_memory_usage + ); + // borsh serialize should be about this size + assert!(size_of_all + 8 * num_nodes <= root_memory_usage as usize); + } + } + } + + fn format_simple_trie_refcount_diff(left: &[T], right: &[T]) -> String { + let left_set: HashSet<_> = HashSet::from_iter(left.iter()); + let right_set: HashSet<_> = HashSet::from_iter(right.iter()); + format!( + "left: {:?} right: {:?}", + left_set.difference(&right_set), + right_set.difference(&left_set) + ) + } + + fn format_simple_trie_changes_diff(left: &TrieChanges, right: &TrieChanges) -> String { + format!( + "insertions diff: {}, deletions diff: {}", + format_simple_trie_refcount_diff(&left.insertions, &right.insertions), + format_simple_trie_refcount_diff(&left.deletions, &right.deletions) + ) + } + + /// Helper function checking that two ways of combining state parts are identical: + /// 1) Create partial storage over all nodes in state parts and traverse all + /// nodes in the storage; + /// 2) Traverse each part separately and merge all trie changes. + fn check_combine_state_parts( + state_root: &CryptoHash, + num_parts: u64, + parts: &[PartialState], + ) -> TrieChanges { + let trie_changes = Trie::combine_state_parts_naive(state_root, parts).unwrap(); + + let trie_changes_new = { + let changes = (0..num_parts) + .map(|part_id| { + Trie::apply_state_part( + state_root, + PartId::new(part_id, num_parts), + parts[part_id as usize].clone(), + ) + .trie_changes + }) + .collect::>(); + + merge_trie_changes(changes) + }; + assert_eq!( + trie_changes, + trie_changes_new, + "{}", + format_simple_trie_changes_diff(&trie_changes, &trie_changes_new) + ); + trie_changes + } + + /// Checks that state part with unexpected data or not enough data doesn't + /// pass validation. + /// Doesn't use FlatStorage. + #[test] + fn invalid_state_parts() { + let tries = TestTriesBuilder::new().build(); + let shard_uid = ShardUId::single_shard(); + let part_id = PartId::new(1, 2); + let trie = tries.get_trie_for_shard(shard_uid, Trie::EMPTY_ROOT); + + let state_items = vec![ + (b"a".to_vec(), vec![1]), + (b"aa".to_vec(), vec![2]), + (b"ab".to_vec(), vec![3]), + (b"b".to_vec(), vec![4]), + (b"ba".to_vec(), vec![5]), + ]; + + let changes_for_trie = state_items.iter().cloned().map(|(k, v)| (k, Some(v))); + let trie_changes = trie.update(changes_for_trie).unwrap(); + let mut store_update = tries.store_update(); + let root = tries.apply_all(&trie_changes, shard_uid, &mut store_update); + store_update.commit().unwrap(); + + let trie = tries.get_view_trie_for_shard(shard_uid, root); + let PartialState::TrieValues(trie_values) = trie + .get_trie_nodes_for_part_without_flat_storage(part_id) + .expect("State part generation using Trie must work"); + let num_trie_values = trie_values.len(); + assert!(num_trie_values >= 2); + + // Check that shuffled state part also passes validation. + let mut rng = rand::thread_rng(); + for _ in 0..5 { + let mut trie_values_shuffled = trie_values.clone(); + trie_values_shuffled.shuffle(&mut rng); + let state_part = PartialState::TrieValues(trie_values_shuffled); + assert_eq!(Trie::validate_state_part(&root, part_id, state_part), Ok(())); + } + + // Remove middle element from state part, check that validation fails. + let mut trie_values_missing = trie_values.clone(); + trie_values_missing.remove(num_trie_values / 2); + let wrong_state_part = PartialState::TrieValues(trie_values_missing); + + assert_matches!( + Trie::validate_state_part(&root, part_id, wrong_state_part), + Err(StorageError::MissingTrieValue( + MissingTrieValueContext::TrieMemoryPartialStorage, + _ + )) + ); + + // Add extra value to the state part, check that validation fails. + let mut trie_values_extra = trie_values.clone(); + trie_values_extra.push(vec![11].into()); + let wrong_state_part = PartialState::TrieValues(trie_values_extra); + assert_eq!( + Trie::validate_state_part(&root, part_id, wrong_state_part), + Err(StorageError::UnexpectedTrieValue) + ); + + // Duplicate a value in the state part, check that validation fails, because + // values in state part must be deduplicated. + let mut trie_values_extra_same = trie_values; + trie_values_extra_same + .push(trie_values_extra_same[trie_values_extra_same.len() / 2].clone()); + let wrong_state_part = PartialState::TrieValues(trie_values_extra_same); + assert_eq!( + Trie::validate_state_part(&root, part_id, wrong_state_part), + Err(StorageError::UnexpectedTrieValue) + ); + } + + /// Check on random samples that state parts can be validated independently + /// from the entire trie. + #[test] + fn test_get_trie_nodes_for_part() { + let mut rng = rand::thread_rng(); + for _ in 0..20 { + let tries = TestTriesBuilder::new().build(); + let trie_changes = gen_changes(&mut rng, 10); + + let state_root = test_populate_trie( + &tries, + &Trie::EMPTY_ROOT, + ShardUId::single_shard(), + trie_changes.clone(), + ); + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), state_root); + + for _ in 0..10 { + // Test that creating and validating are consistent + let num_parts: u64 = rng.gen_range(1..10); + let part_id = rng.gen_range(0..num_parts); + let trie_nodes = trie + .get_trie_nodes_for_part_without_flat_storage(PartId::new(part_id, num_parts)) + .unwrap(); + assert_eq!( + Trie::validate_state_part( + trie.get_root(), + PartId::new(part_id, num_parts), + trie_nodes, + ), + Ok(()) + ); + } + } + } + + /// Checks sanity of generating state part using flat storage. + #[test] + fn get_trie_nodes_for_part_with_flat_storage() { + let value_len = 1000usize; + + let tries = TestTriesBuilder::new().with_flat_storage().build(); + let shard_uid = ShardUId::single_shard(); + let block_hash = CryptoHash::default(); + let part_id = PartId::new(1, 3); + let trie = tries.get_trie_for_shard(shard_uid, Trie::EMPTY_ROOT); + + // Trie with three big independent children. + let state_items = vec![ + (b"a".to_vec(), vec![1; value_len]), + (b"aa".to_vec(), vec![2; value_len]), + (b"ab".to_vec(), vec![3; value_len]), + (b"b".to_vec(), vec![4; value_len]), + (b"ba".to_vec(), vec![5; value_len]), + (b"bb".to_vec(), vec![6; value_len]), + (b"c".to_vec(), vec![7; value_len]), + (b"ca".to_vec(), vec![8; value_len]), + (b"cb".to_vec(), vec![9; value_len]), + ]; + let changes_for_trie = state_items.iter().cloned().map(|(k, v)| (k, Some(v))); + let trie_changes = trie.update(changes_for_trie).unwrap(); + let mut store_update = tries.store_update(); + let root = tries.apply_all(&trie_changes, shard_uid, &mut store_update); + store_update.commit().unwrap(); + + // Get correct state part using trie without flat storage. + let trie_without_flat = tries.get_view_trie_for_shard(shard_uid, root); + let state_part = trie_without_flat + .get_trie_nodes_for_part_without_flat_storage(part_id) + .expect("State part generation using Trie must work"); + assert_eq!(Trie::validate_state_part(&root, part_id, state_part.clone()), Ok(())); + assert!(state_part.len() > 0); + + // Check that if we try to use flat storage but it is empty, state part + // creation fails. + let (partial_state, nibbles_begin, nibbles_end) = + trie_without_flat.get_state_part_boundaries(part_id).unwrap(); + + let view_chunk_trie = + tries.get_trie_with_block_hash_for_shard(shard_uid, root, &block_hash, true); + + assert_matches!( + view_chunk_trie.get_trie_nodes_for_part_with_flat_storage( + part_id, + partial_state, + nibbles_begin, + nibbles_end, + &trie_without_flat, + ), + Err(StorageError::MissingTrieValue( + MissingTrieValueContext::TrieMemoryPartialStorage, + _ + )) + ); + + // Fill flat storage and check that state part creation succeeds. + let changes_for_delta = + state_items.into_iter().map(|(k, v)| (k, Some(FlatStateValue::inlined(&v)))); + let delta = FlatStateChanges::from(changes_for_delta); + let mut store_update = tries.store_update(); + delta.apply_to_flat_state(&mut store_update, shard_uid); + store_update.commit().unwrap(); + + let (partial_state, nibbles_begin, nibbles_end) = + trie_without_flat.get_state_part_boundaries(part_id).unwrap(); + + let view_chunk_trie = + tries.get_trie_with_block_hash_for_shard(shard_uid, root, &block_hash, true); + let state_part_with_flat = view_chunk_trie.get_trie_nodes_for_part_with_flat_storage( + PartId::new(1, 3), + partial_state.clone(), + nibbles_begin.clone(), + nibbles_end.clone(), + &trie_without_flat, + ); + assert_eq!(state_part_with_flat, Ok(state_part.clone())); + + // Remove some key from state part from trie storage. + // Check that trie-only state part generation fails but trie & flat + // storage generation succeeds, as it doesn't access intermediate nodes. + let mut store_update = tries.store_update(); + let store_value = vec![5; value_len]; + let value_hash = hash(&store_value); + let store_key = TrieCachingStorage::get_key_from_shard_uid_and_hash(shard_uid, &value_hash); + store_update.decrement_refcount(DBCol::State, &store_key); + store_update.commit().unwrap(); + + assert_eq!( + trie_without_flat.get_trie_nodes_for_part_without_flat_storage(part_id), + Err(StorageError::MissingTrieValue(MissingTrieValueContext::TrieStorage, value_hash)), + ); + + assert_eq!( + view_chunk_trie.get_trie_nodes_for_part_with_flat_storage( + part_id, + partial_state.clone(), + nibbles_begin.clone(), + nibbles_end.clone(), + &trie_without_flat, + ), + Ok(state_part) + ); + + // Remove some key from state part from flat storage. + // Check that state part creation succeeds but generated state part + // is invalid. + let mut store_update = tries.store_update(); + let delta = FlatStateChanges::from(vec![(b"ba".to_vec(), None)]); + delta.apply_to_flat_state(&mut store_update, shard_uid); + store_update.commit().unwrap(); + + assert_matches!( + view_chunk_trie.get_trie_nodes_for_part_with_flat_storage( + part_id, + partial_state, + nibbles_begin, + nibbles_end, + &trie_without_flat, + ), + Err(StorageError::MissingTrieValue( + MissingTrieValueContext::TrieMemoryPartialStorage, + _ + )) + ); + } +} diff --git a/core/store/src/trie/state_snapshot.rs b/core/store/src/trie/state_snapshot.rs new file mode 100644 index 000000000..fed81aa5f --- /dev/null +++ b/core/store/src/trie/state_snapshot.rs @@ -0,0 +1,384 @@ +use crate::config::StateSnapshotType; +use crate::db::STATE_SNAPSHOT_KEY; +use crate::flat::{FlatStorageManager, FlatStorageStatus}; +use crate::Mode; +use crate::{checkpoint_hot_storage_and_cleanup_columns, metrics, DBCol, NodeStorage}; +use crate::{option_to_not_found, ShardTries}; +use crate::{Store, StoreConfig}; +use unc_primitives::block::Block; +use unc_primitives::errors::EpochError; +use unc_primitives::errors::StorageError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardUId; +use std::error::Error; +use std::io; +use std::path::{Path, PathBuf}; +use std::sync::TryLockError; + +#[derive(Debug, PartialEq, Eq)] +pub enum SnapshotError { + // The requested hash and the snapshot hash don't match. + IncorrectSnapshotRequested(CryptoHash, CryptoHash), + // Snapshot doesn't exist at all. + SnapshotNotFound(CryptoHash), + // Lock for snapshot acquired by another process. + // Most likely the StateSnapshotActor is creating a snapshot or doing compaction. + LockWouldBlock, + // Any other unexpected error + Other(String), +} + +impl std::fmt::Display for SnapshotError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SnapshotError::IncorrectSnapshotRequested(requested, available) => write!( + f, + "Wrong snapshot hash. Requested: {:?}, Available: {:?}", + requested, available + ), + SnapshotError::SnapshotNotFound(hash) => { + write!(f, "No state snapshot available. Requested: {:?}", hash) + } + SnapshotError::LockWouldBlock => { + write!(f, "Accessing state snapshot would block. Retry in a few seconds.") + } + SnapshotError::Other(err_msg) => write!(f, "{}", err_msg), + } + } +} + +impl Error for SnapshotError {} + +impl From for StorageError { + fn from(err: SnapshotError) -> Self { + StorageError::StorageInconsistentState(err.to_string()) + } +} + +impl From> for SnapshotError { + fn from(err: TryLockError) -> Self { + match err { + TryLockError::Poisoned(_) => SnapshotError::Other("Poisoned lock".to_string()), + TryLockError::WouldBlock => SnapshotError::LockWouldBlock, + } + } +} + +/// Snapshot of the state at the epoch boundary. +pub struct StateSnapshot { + /// The state snapshot represents the state including changes of the next block of this block. + prev_block_hash: CryptoHash, + /// Read-only store. + store: Store, + /// Access to flat storage in that store. + flat_storage_manager: FlatStorageManager, +} + +impl StateSnapshot { + /// Creates an object and also creates flat storage for the given shards. + pub fn new( + store: Store, + prev_block_hash: CryptoHash, + flat_storage_manager: FlatStorageManager, + shard_uids: &[ShardUId], + block: Option<&Block>, + ) -> Self { + tracing::debug!(target: "state_snapshot", ?shard_uids, ?prev_block_hash, "new StateSnapshot"); + for shard_uid in shard_uids { + if let Err(err) = flat_storage_manager.create_flat_storage_for_shard(*shard_uid) { + tracing::warn!(target: "state_snapshot", ?err, ?shard_uid, "Failed to create a flat storage for snapshot shard"); + continue; + } + if let Some(block) = block { + let flat_storage = + flat_storage_manager.get_flat_storage_for_shard(*shard_uid).unwrap(); + let current_flat_head = flat_storage.get_head_hash(); + tracing::debug!(target: "state_snapshot", ?shard_uid, ?current_flat_head, block_hash = ?block.header().hash(), block_height = block.header().height(), "Moving FlatStorage head of the snapshot"); + let _timer = metrics::MOVE_STATE_SNAPSHOT_FLAT_HEAD_ELAPSED + .with_label_values(&[&shard_uid.shard_id.to_string()]) + .start_timer(); + if let Some(chunk) = block.chunks().get(shard_uid.shard_id as usize) { + // Flat state snapshot needs to be at a height that lets it + // replay the last chunk of the shard. + let desired_flat_head = chunk.prev_block_hash(); + match flat_storage.update_flat_head(desired_flat_head, true) { + Ok(_) => { + tracing::debug!(target: "state_snapshot", ?shard_uid, ?current_flat_head, ?desired_flat_head, "Successfully moved FlatStorage head of the snapshot"); + } + Err(err) => { + tracing::error!(target: "state_snapshot", ?shard_uid, ?err, ?current_flat_head, ?desired_flat_head, "Failed to move FlatStorage head of the snapshot"); + } + } + } else { + tracing::error!(target: "state_snapshot", ?shard_uid, current_flat_head = ?flat_storage.get_head_hash(), ?prev_block_hash, "Failed to move FlatStorage head of the snapshot, no chunk"); + } + } + } + Self { prev_block_hash, store, flat_storage_manager } + } + + /// Returns the UIds for the shards included in the snapshot. + pub fn get_shard_uids(&self) -> Vec { + self.flat_storage_manager.get_shard_uids() + } + + /// Returns status of a shard of a flat storage in the state snapshot. + pub fn get_flat_storage_status(&self, shard_uid: ShardUId) -> FlatStorageStatus { + self.flat_storage_manager.get_flat_storage_status(shard_uid) + } +} + +/// Information needed to make a state snapshot. +/// Note that it's possible to override the `enabled` config and force create snapshot for resharding. +#[derive(Default)] +pub struct StateSnapshotConfig { + /// It's possible to override the `enabled` config and force create snapshot for resharding. + pub state_snapshot_type: StateSnapshotType, + pub home_dir: PathBuf, + pub hot_store_path: PathBuf, + pub state_snapshot_subdir: PathBuf, + pub compaction_enabled: bool, +} + +impl ShardTries { + pub fn get_state_snapshot( + &self, + block_hash: &CryptoHash, + ) -> Result<(Store, FlatStorageManager), SnapshotError> { + // Taking this lock can last up to 10 seconds, if the snapshot happens to be re-created. + let guard = self.state_snapshot().try_read()?; + let data = guard.as_ref().ok_or(SnapshotError::SnapshotNotFound(*block_hash))?; + if &data.prev_block_hash != block_hash { + return Err(SnapshotError::IncorrectSnapshotRequested( + *block_hash, + data.prev_block_hash, + )); + } + Ok((data.store.clone(), data.flat_storage_manager.clone())) + } + + /// Makes a snapshot of the current state of the DB, if one is not already available. + /// If a new snapshot is created, returns the ids of the included shards. + pub fn create_state_snapshot( + &self, + prev_block_hash: CryptoHash, + shard_uids: &[ShardUId], + block: &Block, + ) -> Result>, anyhow::Error> { + metrics::HAS_STATE_SNAPSHOT.set(0); + // The function returns an `anyhow::Error`, because no special handling of errors is done yet. The errors are logged and ignored. + let _span = + tracing::info_span!(target: "state_snapshot", "create_state_snapshot", ?prev_block_hash) + .entered(); + let _timer = metrics::CREATE_STATE_SNAPSHOT_ELAPSED.start_timer(); + + // `write()` lock is held for the whole duration of this function. + let mut state_snapshot_lock = self.state_snapshot().write().unwrap(); + let db_snapshot_hash = self.get_state_snapshot_hash(); + if let Some(state_snapshot) = &*state_snapshot_lock { + // only return Ok() when the hash stored in STATE_SNAPSHOT_KEY and in state_snapshot_lock and prev_block_hash are the same + if db_snapshot_hash.is_ok_and(|hash| hash == prev_block_hash) + && state_snapshot.prev_block_hash == prev_block_hash + { + tracing::warn!(target: "state_snapshot", ?prev_block_hash, "Requested a state snapshot but that is already available"); + return Ok(None); + } + tracing::error!(target: "state_snapshot", ?prev_block_hash, ?state_snapshot.prev_block_hash, "Requested a state snapshot but that is already available with a different hash"); + } + + let StateSnapshotConfig { home_dir, hot_store_path, state_snapshot_subdir, .. } = + self.state_snapshot_config(); + let storage = checkpoint_hot_storage_and_cleanup_columns( + &self.get_store(), + &Self::get_state_snapshot_base_dir( + &prev_block_hash, + home_dir, + hot_store_path, + state_snapshot_subdir, + ), + // TODO: Cleanup Changes and DeltaMetadata to avoid extra memory usage. + // Can't be cleaned up now because these columns are needed to `update_flat_head()`. + Some(vec![ + // Keep DbVersion and BlockMisc, otherwise you'll not be able to open the state snapshot as a Store. + DBCol::DbVersion, + DBCol::BlockMisc, + // Flat storage columns. + DBCol::FlatState, + DBCol::FlatStateChanges, + DBCol::FlatStateDeltaMetadata, + DBCol::FlatStorageStatus, + ]), + )?; + let store = storage.get_hot_store(); + // It is fine to create a separate FlatStorageManager, because + // it is used only for reading flat storage in the snapshot a + // doesn't introduce memory overhead. + let flat_storage_manager = FlatStorageManager::new(store.clone()); + *state_snapshot_lock = Some(StateSnapshot::new( + store, + prev_block_hash, + flat_storage_manager, + shard_uids, + Some(block), + )); + + // this will set the new hash for state snapshot in rocksdb. will retry until success. + let mut set_state_snapshot_in_db = false; + while !set_state_snapshot_in_db { + set_state_snapshot_in_db = match self.set_state_snapshot_hash(Some(prev_block_hash)) { + Ok(_) => true, + Err(err) => { + // This will be retried. + tracing::debug!(target: "state_snapshot", ?err, "Failed to set the new state snapshot for BlockMisc::STATE_SNAPSHOT_KEY in rocksdb"); + false + } + } + } + + metrics::HAS_STATE_SNAPSHOT.set(1); + tracing::info!(target: "state_snapshot", ?prev_block_hash, "Made a checkpoint"); + Ok(Some(state_snapshot_lock.as_ref().unwrap().get_shard_uids())) + } + + /// Runs compaction on the snapshot. + pub fn compact_state_snapshot(&self) -> Result<(), anyhow::Error> { + let _span = + tracing::info_span!(target: "state_snapshot", "compact_state_snapshot").entered(); + // It's fine if the access to state snapshot blocks. + let state_snapshot_lock = self.state_snapshot().read().unwrap(); + if let Some(state_snapshot) = &*state_snapshot_lock { + let _timer = metrics::COMPACT_STATE_SNAPSHOT_ELAPSED.start_timer(); + state_snapshot.store.compact()?; + } else { + tracing::error!(target: "state_snapshot", "Requested compaction but no state snapshot is available."); + }; + Ok(()) + } + + /// Deletes all snapshots and unsets the STATE_SNAPSHOT_KEY. + pub fn delete_state_snapshot(&self) { + let _span = + tracing::info_span!(target: "state_snapshot", "delete_state_snapshot").entered(); + let _timer = metrics::DELETE_STATE_SNAPSHOT_ELAPSED.start_timer(); + + // get snapshot_hash after acquiring write lock + let mut state_snapshot_lock = self.state_snapshot().write().unwrap(); + if state_snapshot_lock.is_some() { + // Drop Store before deleting the underlying data. + *state_snapshot_lock = None; + } + let StateSnapshotConfig { home_dir, hot_store_path, state_snapshot_subdir, .. } = + self.state_snapshot_config(); + + // This will delete all existing snapshots from file system. Will retry 3 times + for _ in 0..3 { + match self.delete_all_state_snapshots(home_dir, hot_store_path, state_snapshot_subdir) { + Ok(_) => break, + Err(err) => { + tracing::error!(target: "state_snapshot", ?err, "Failed to delete the old state snapshot from file system or from rocksdb") + } + } + } + + // this will delete the STATE_SNAPSHOT_KEY-value pair from db. Will retry 3 times + for _ in 0..3 { + match self.set_state_snapshot_hash(None) { + Ok(_) => break, + Err(err) => { + tracing::error!(target: "state_snapshot", ?err, "Failed to delete the old state snapshot for BlockMisc::STATE_SNAPSHOT_KEY in rocksdb") + } + } + } + + metrics::HAS_STATE_SNAPSHOT.set(0); + } + + /// Deletes all existing state snapshots in the parent directory + fn delete_all_state_snapshots( + &self, + home_dir: &Path, + hot_store_path: &Path, + state_snapshot_subdir: &Path, + ) -> Result<(), io::Error> { + let _span = + tracing::info_span!(target: "state_snapshot", "delete_all_state_snapshots").entered(); + let path = home_dir.join(hot_store_path).join(state_snapshot_subdir); + if path.exists() { + std::fs::remove_dir_all(&path)? + } + Ok(()) + } + + pub fn get_state_snapshot_base_dir( + prev_block_hash: &CryptoHash, + home_dir: &Path, + hot_store_path: &Path, + state_snapshot_subdir: &Path, + ) -> PathBuf { + // Assumptions: + // * RocksDB checkpoints are taken instantly and for free, because the filesystem supports hard links. + // * The best place for checkpoints is within the `hot_store_path`, because that directory is often a separate disk. + home_dir.join(hot_store_path).join(state_snapshot_subdir).join(format!("{prev_block_hash}")) + } + + /// Retrieves STATE_SNAPSHOT_KEY + pub fn get_state_snapshot_hash(&self) -> Result { + option_to_not_found( + self.get_store().get_ser(DBCol::BlockMisc, STATE_SNAPSHOT_KEY), + "STATE_SNAPSHOT_KEY", + ) + } + + /// Updates STATE_SNAPSHOT_KEY. + pub fn set_state_snapshot_hash(&self, value: Option) -> Result<(), io::Error> { + let mut store_update = self.store_update(); + let key = STATE_SNAPSHOT_KEY; + match value { + None => store_update.delete(DBCol::BlockMisc, key), + Some(value) => store_update.set_ser(DBCol::BlockMisc, key, &value)?, + } + store_update.commit().into() + } + + /// Read RocksDB for the latest available snapshot hash, if available, open base_path+snapshot_hash for the state snapshot + /// we don't deal with multiple snapshots here because we will deal with it whenever a new snapshot is created and saved to file system + pub fn maybe_open_state_snapshot( + &self, + get_shard_uids_fn: impl FnOnce(CryptoHash) -> Result, EpochError>, + ) -> Result<(), anyhow::Error> { + let _span = + tracing::info_span!(target: "state_snapshot", "maybe_open_state_snapshot").entered(); + metrics::HAS_STATE_SNAPSHOT.set(0); + let StateSnapshotConfig { home_dir, hot_store_path, state_snapshot_subdir, .. } = + self.state_snapshot_config(); + + // directly return error if no snapshot is found + let snapshot_hash = self.get_state_snapshot_hash()?; + + let snapshot_path = Self::get_state_snapshot_base_dir( + &snapshot_hash, + &home_dir, + &hot_store_path, + &state_snapshot_subdir, + ); + let parent_path = snapshot_path + .parent() + .ok_or(anyhow::anyhow!("{snapshot_path:?} needs to have a parent dir"))?; + tracing::debug!(target: "state_snapshot", ?snapshot_path, ?parent_path); + + let store_config = StoreConfig::default(); + + let opener = NodeStorage::opener(&snapshot_path, false, &store_config, None); + let storage = opener.open_in_mode(Mode::ReadOnly)?; + let store = storage.get_hot_store(); + let flat_storage_manager = FlatStorageManager::new(store.clone()); + + let shard_uids = get_shard_uids_fn(snapshot_hash)?; + let mut guard = self.state_snapshot().write().unwrap(); + *guard = + Some(StateSnapshot::new(store, snapshot_hash, flat_storage_manager, &shard_uids, None)); + metrics::HAS_STATE_SNAPSHOT.set(1); + tracing::info!(target: "runtime", ?snapshot_hash, ?snapshot_path, "Detected and opened a state snapshot."); + Ok(()) + } +} diff --git a/core/store/src/trie/trie_recording.rs b/core/store/src/trie/trie_recording.rs new file mode 100644 index 000000000..e2cc4a869 --- /dev/null +++ b/core/store/src/trie/trie_recording.rs @@ -0,0 +1,454 @@ +use crate::PartialStorage; +use unc_primitives::challenge::PartialState; +use unc_primitives::hash::CryptoHash; +use std::collections::HashMap; +use std::sync::Arc; + +/// A simple struct to capture a state proof as it's being accumulated. +pub struct TrieRecorder { + recorded: HashMap>, +} + +impl TrieRecorder { + pub fn new() -> Self { + Self { recorded: HashMap::new() } + } + + pub fn record(&mut self, hash: &CryptoHash, node: Arc<[u8]>) { + self.recorded.insert(*hash, node); + } + + pub fn recorded_storage(&mut self) -> PartialStorage { + let mut nodes: Vec<_> = self.recorded.drain().map(|(_key, value)| value).collect(); + nodes.sort(); + PartialStorage { nodes: PartialState::TrieValues(nodes) } + } +} + +#[cfg(test)] +mod trie_recording_tests { + use crate::db::refcount::decode_value_with_rc; + use crate::test_utils::{ + gen_larger_changes, simplify_changes, test_populate_flat_storage, test_populate_trie, + TestTriesBuilder, + }; + use crate::trie::mem::metrics::MEM_TRIE_NUM_LOOKUPS; + use crate::{DBCol, Store, Trie}; + use unc_primitives::hash::{hash, CryptoHash}; + use unc_primitives::shard_layout::{get_block_shard_uid, get_block_shard_uid_rev, ShardUId}; + use unc_primitives::state::ValueRef; + use unc_primitives::types::chunk_extra::ChunkExtra; + use unc_primitives::types::StateRoot; + use unc_vm_runner::logic::TrieNodesCount; + use rand::{thread_rng, Rng}; + use std::collections::{HashMap, HashSet}; + use std::num::NonZeroU32; + + const NUM_ITERATIONS_PER_TEST: usize = 100; + + /// Prepared on-disk trie and flat storage for testing. + struct PreparedTrie { + store: Store, + shard_uid: ShardUId, + /// All the data we've put into the trie. + data_in_trie: HashMap, Vec>, + /// The keys that we should be using to call get() on the trie with. + keys_to_get: Vec>, + /// The keys that we should be using to call get_optimized_ref() on the + /// trie with. + keys_to_get_ref: Vec>, + state_root: StateRoot, + } + + /// Prepare a trie for testing; this will prepare both a trie and a flat + /// storage with some dummy block info. If `use_missing_keys` is true, + /// the keys to test with will also include some keys that are not in the + /// trie. + fn prepare_trie(use_missing_keys: bool) -> PreparedTrie { + let tries_for_building = TestTriesBuilder::new().with_flat_storage().build(); + let shard_uid = ShardUId::single_shard(); + let trie_changes = gen_larger_changes(&mut thread_rng(), 50); + let trie_changes = simplify_changes(&trie_changes); + if trie_changes.is_empty() { + // try again + return prepare_trie(use_missing_keys); + } + let state_root = test_populate_trie( + &tries_for_building, + &Trie::EMPTY_ROOT, + shard_uid, + trie_changes.clone(), + ); + test_populate_flat_storage( + &tries_for_building, + shard_uid, + &CryptoHash::default(), + &CryptoHash::default(), + &trie_changes, + ); + + // ChunkExtra is needed for in-memory trie loading code to query state roots. + let chunk_extra = ChunkExtra::new(&state_root, CryptoHash::default(), Vec::new(), 0, 0, 0); + let mut update_for_chunk_extra = tries_for_building.store_update(); + update_for_chunk_extra + .set_ser( + DBCol::ChunkExtra, + &get_block_shard_uid(&CryptoHash::default(), &shard_uid), + &chunk_extra, + ) + .unwrap(); + update_for_chunk_extra.commit().unwrap(); + + let data_in_trie = trie_changes + .iter() + .map(|(key, value)| (key.clone(), value.clone().unwrap())) + .collect::>(); + let (keys_to_get, keys_to_get_ref) = trie_changes + .iter() + .map(|(key, _)| { + let mut key = key.clone(); + if use_missing_keys { + key.push(100); + } + key + }) + .partition::, _>(|_| thread_rng().gen()); + PreparedTrie { + store: tries_for_building.get_store(), + shard_uid, + data_in_trie, + keys_to_get, + keys_to_get_ref, + state_root, + } + } + + /// Delete state that we should not be relying on if in-memory tries are + /// loaded, to help make sure that in-memory tries are used. + /// + /// The only thing we don't delete are the values, which may not be + /// inlined. + fn destructively_delete_in_memory_state_from_disk( + store: &Store, + data_in_trie: &HashMap, Vec>, + ) { + let key_hashes_to_keep = data_in_trie.iter().map(|(_, v)| hash(&v)).collect::>(); + let mut update = store.store_update(); + for result in store.iter_raw_bytes(DBCol::State) { + let (key, value) = result.unwrap(); + let (_, refcount) = decode_value_with_rc(&value); + let (key_hash, _) = get_block_shard_uid_rev(&key).unwrap(); + if !key_hashes_to_keep.contains(&key_hash) { + update.decrement_refcount_by( + DBCol::State, + &key, + NonZeroU32::new(refcount as u32).unwrap(), + ); + } + } + update.delete_all(DBCol::FlatState); + update.commit().unwrap(); + } + + /// Verifies that when operating on a trie, the results are completely consistent + /// regardless of whether we're operating on the real storage (with or without chunk + /// cache), while recording reads, or when operating on recorded partial storage. + fn test_trie_recording_consistency( + enable_accounting_cache: bool, + use_missing_keys: bool, + use_in_memory_tries: bool, + ) { + for _ in 0..NUM_ITERATIONS_PER_TEST { + let PreparedTrie { + store, + shard_uid, + data_in_trie, + keys_to_get, + keys_to_get_ref, + state_root, + } = prepare_trie(use_missing_keys); + let tries = if use_in_memory_tries { + TestTriesBuilder::new().with_store(store.clone()).with_in_memory_tries().build() + } else { + TestTriesBuilder::new().with_store(store.clone()).build() + }; + let mem_trie_lookup_counts_before = MEM_TRIE_NUM_LOOKUPS.get(); + + if use_in_memory_tries { + // Delete the on-disk state so that we really know we're using + // in-memory tries. + destructively_delete_in_memory_state_from_disk(&store, &data_in_trie); + } + + // Let's capture the baseline node counts - this is what will happen + // in production. + let trie = tries.get_trie_for_shard(shard_uid, state_root); + trie.accounting_cache.borrow_mut().set_enabled(enable_accounting_cache); + for key in &keys_to_get { + assert_eq!(trie.get(key).unwrap(), data_in_trie.get(key).cloned()); + } + for key in &keys_to_get_ref { + assert_eq!( + trie.get_optimized_ref(key, crate::KeyLookupMode::Trie) + .unwrap() + .map(|value| value.into_value_ref()), + data_in_trie.get(key).map(|value| ValueRef::new(&value)) + ); + } + let baseline_trie_nodes_count = trie.get_trie_nodes_count(); + println!("Baseline trie nodes count: {:?}", baseline_trie_nodes_count); + + // Now let's do this again while recording, and make sure that the counters + // we get are exactly the same. + let trie = tries.get_trie_for_shard(shard_uid, state_root).recording_reads(); + trie.accounting_cache.borrow_mut().set_enabled(enable_accounting_cache); + for key in &keys_to_get { + assert_eq!(trie.get(key).unwrap(), data_in_trie.get(key).cloned()); + } + for key in &keys_to_get_ref { + assert_eq!( + trie.get_optimized_ref(key, crate::KeyLookupMode::Trie) + .unwrap() + .map(|value| value.into_value_ref()), + data_in_trie.get(key).map(|value| ValueRef::new(&value)) + ); + } + assert_eq!(trie.get_trie_nodes_count(), baseline_trie_nodes_count); + + // Now, let's check that when doing the same lookups with the captured partial storage, + // we still get the same counters. + let partial_storage = trie.recorded_storage().unwrap(); + println!( + "Partial storage has {} nodes from {} entries", + partial_storage.nodes.len(), + data_in_trie.len() + ); + let trie = Trie::from_recorded_storage(partial_storage, state_root, false); + trie.accounting_cache.borrow_mut().set_enabled(enable_accounting_cache); + for key in &keys_to_get { + assert_eq!(trie.get(key).unwrap(), data_in_trie.get(key).cloned()); + } + for key in &keys_to_get_ref { + assert_eq!( + trie.get_optimized_ref(key, crate::KeyLookupMode::Trie) + .unwrap() + .map(|value| value.into_value_ref()), + data_in_trie.get(key).map(|value| ValueRef::new(&value)) + ); + } + assert_eq!(trie.get_trie_nodes_count(), baseline_trie_nodes_count); + + if use_in_memory_tries { + // sanity check that we did indeed use in-memory tries. + assert!(MEM_TRIE_NUM_LOOKUPS.get() > mem_trie_lookup_counts_before); + } + } + } + + #[test] + fn test_trie_recording_consistency_no_accounting_cache() { + test_trie_recording_consistency(false, false, false); + } + + #[test] + fn test_trie_recording_consistency_with_accounting_cache() { + test_trie_recording_consistency(true, false, false); + } + + #[test] + fn test_trie_recording_consistency_no_accounting_cache_with_missing_keys() { + test_trie_recording_consistency(false, true, false); + } + + #[test] + fn test_trie_recording_consistency_with_accounting_cache_and_missing_keys() { + test_trie_recording_consistency(true, true, false); + } + + #[test] + fn test_trie_recording_consistency_memtrie_no_accounting_cache() { + test_trie_recording_consistency(false, false, true); + } + + #[test] + fn test_trie_recording_consistency_memtrie_with_accounting_cache() { + test_trie_recording_consistency(true, false, true); + } + + #[test] + fn test_trie_recording_consistency_memtrie_no_accounting_cache_with_missing_keys() { + test_trie_recording_consistency(false, true, true); + } + + #[test] + fn test_trie_recording_consistency_memtrie_with_accounting_cache_and_missing_keys() { + test_trie_recording_consistency(true, true, true); + } + + /// Verifies that when operating on a trie, the results are completely consistent + /// regardless of whether we're operating on the real storage (with or without chunk + /// cache), while recording reads, or when operating on recorded partial storage. + /// This test additionally verifies this when flat storage is used. + fn test_trie_recording_consistency_with_flat_storage( + enable_accounting_cache: bool, + use_missing_keys: bool, + use_in_memory_tries: bool, + ) { + for _ in 0..NUM_ITERATIONS_PER_TEST { + let PreparedTrie { + store, + shard_uid, + data_in_trie, + keys_to_get, + keys_to_get_ref, + state_root, + } = prepare_trie(use_missing_keys); + let tries = if use_in_memory_tries { + TestTriesBuilder::new() + .with_store(store.clone()) + .with_flat_storage() + .with_in_memory_tries() + .build() + } else { + TestTriesBuilder::new().with_store(store.clone()).with_flat_storage().build() + }; + let mem_trie_lookup_counts_before = MEM_TRIE_NUM_LOOKUPS.get(); + + if use_in_memory_tries { + // Delete the on-disk state so that we really know we're using + // in-memory tries. + destructively_delete_in_memory_state_from_disk(&store, &data_in_trie); + } + // Check that the trie is using flat storage, so that counters are all zero. + // Only use get_optimized_ref(), because get() will actually dereference values which can + // cause trie reads. + let trie = tries.get_trie_with_block_hash_for_shard( + shard_uid, + state_root, + &CryptoHash::default(), + false, + ); + for key in data_in_trie.keys() { + trie.get_optimized_ref(key, crate::KeyLookupMode::FlatStorage).unwrap(); + } + assert_eq!(trie.get_trie_nodes_count(), TrieNodesCount { db_reads: 0, mem_reads: 0 }); + + // Now, let's capture the baseline node counts - this is what will happen + // in production. + let trie = tries.get_trie_with_block_hash_for_shard( + shard_uid, + state_root, + &CryptoHash::default(), + false, + ); + trie.accounting_cache.borrow_mut().set_enabled(enable_accounting_cache); + for key in &keys_to_get { + assert_eq!(trie.get(key).unwrap(), data_in_trie.get(key).cloned()); + } + for key in &keys_to_get_ref { + assert_eq!( + trie.get_optimized_ref(key, crate::KeyLookupMode::FlatStorage) + .unwrap() + .map(|value| value.into_value_ref()), + data_in_trie.get(key).map(|value| ValueRef::new(&value)) + ); + } + let baseline_trie_nodes_count = trie.get_trie_nodes_count(); + println!("Baseline trie nodes count: {:?}", baseline_trie_nodes_count); + + // Let's do this again, but this time recording reads. We'll make sure + // the counters are exactly the same even when we're recording. + let trie = tries + .get_trie_with_block_hash_for_shard( + shard_uid, + state_root, + &CryptoHash::default(), + false, + ) + .recording_reads(); + trie.accounting_cache.borrow_mut().set_enabled(enable_accounting_cache); + for key in &keys_to_get { + assert_eq!(trie.get(key).unwrap(), data_in_trie.get(key).cloned()); + } + for key in &keys_to_get_ref { + assert_eq!( + trie.get_optimized_ref(key, crate::KeyLookupMode::FlatStorage) + .unwrap() + .map(|value| value.into_value_ref()), + data_in_trie.get(key).map(|value| ValueRef::new(&value)) + ); + } + assert_eq!(trie.get_trie_nodes_count(), baseline_trie_nodes_count); + + // Now, let's check that when doing the same lookups with the captured partial storage, + // we still get the same counters. + let partial_storage = trie.recorded_storage().unwrap(); + println!( + "Partial storage has {} nodes from {} entries", + partial_storage.nodes.len(), + data_in_trie.len() + ); + let trie = Trie::from_recorded_storage(partial_storage, state_root, true); + trie.accounting_cache.borrow_mut().set_enabled(enable_accounting_cache); + for key in &keys_to_get { + assert_eq!(trie.get(key).unwrap(), data_in_trie.get(key).cloned()); + } + for key in &keys_to_get_ref { + assert_eq!( + trie.get_optimized_ref(key, crate::KeyLookupMode::FlatStorage) + .unwrap() + .map(|value| value.into_value_ref()), + data_in_trie.get(key).map(|value| ValueRef::new(&value)) + ); + } + assert_eq!(trie.get_trie_nodes_count(), baseline_trie_nodes_count); + + if use_in_memory_tries { + // sanity check that we did indeed use in-memory tries. + assert!(MEM_TRIE_NUM_LOOKUPS.get() > mem_trie_lookup_counts_before); + } + } + } + + #[test] + fn test_trie_recording_consistency_with_flat_storage_no_accounting_cache() { + test_trie_recording_consistency_with_flat_storage(false, false, false); + } + + #[test] + fn test_trie_recording_consistency_with_flat_storage_with_accounting_cache() { + test_trie_recording_consistency_with_flat_storage(true, false, false); + } + + #[test] + fn test_trie_recording_consistency_with_flat_storage_no_accounting_cache_with_missing_keys() { + test_trie_recording_consistency_with_flat_storage(false, true, false); + } + + #[test] + fn test_trie_recording_consistency_with_flat_storage_with_accounting_cache_and_missing_keys() { + test_trie_recording_consistency_with_flat_storage(true, true, false); + } + + #[test] + fn test_trie_recording_consistency_with_flat_storage_memtrie_no_accounting_cache() { + test_trie_recording_consistency_with_flat_storage(false, false, true); + } + + #[test] + fn test_trie_recording_consistency_with_flat_storage_memtrie_with_accounting_cache() { + test_trie_recording_consistency_with_flat_storage(true, false, true); + } + + #[test] + fn test_trie_recording_consistency_with_flat_storage_memtrie_no_accounting_cache_with_missing_keys( + ) { + test_trie_recording_consistency_with_flat_storage(false, true, true); + } + + #[test] + fn test_trie_recording_consistency_with_flat_storage_memtrie_with_accounting_cache_and_missing_keys( + ) { + test_trie_recording_consistency_with_flat_storage(true, true, true); + } +} diff --git a/core/store/src/trie/trie_storage.rs b/core/store/src/trie/trie_storage.rs new file mode 100644 index 000000000..5561d36ff --- /dev/null +++ b/core/store/src/trie/trie_storage.rs @@ -0,0 +1,720 @@ +use crate::trie::config::TrieConfig; +use crate::trie::prefetching_trie_storage::PrefetcherResult; +use crate::trie::POISONED_LOCK_ERR; +use crate::{metrics, DBCol, MissingTrieValueContext, PrefetchApi, StorageError, Store}; +use lru::LruCache; +use unc_o11y::log_assert; +use unc_o11y::metrics::prometheus; +use unc_o11y::metrics::prometheus::core::{GenericCounter, GenericGauge}; +use unc_primitives::challenge::PartialState; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::types::ShardId; +use std::cell::RefCell; +use std::collections::{HashMap, HashSet, VecDeque}; +use std::sync::{Arc, Mutex}; + +pub(crate) struct BoundedQueue { + queue: VecDeque, + /// If queue size exceeds capacity, item from the tail is removed. + capacity: usize, +} + +impl BoundedQueue { + pub(crate) fn new(capacity: usize) -> Self { + // Reserve space for one extra element to simplify `put`. + Self { queue: VecDeque::with_capacity(capacity + 1), capacity } + } + + pub(crate) fn clear(&mut self) { + self.queue.clear(); + } + + pub(crate) fn pop(&mut self) -> Option { + self.queue.pop_front() + } + + pub(crate) fn put(&mut self, key: T) -> Option { + self.queue.push_back(key); + if self.queue.len() > self.capacity { + Some(self.pop().expect("Queue cannot be empty")) + } else { + None + } + } + + pub(crate) fn len(&self) -> usize { + self.queue.len() + } +} + +/// In-memory cache for trie items - nodes and values. All nodes are stored in the LRU cache with three modifications. +/// 1) Size of each value must not exceed `TRIE_LIMIT_CACHED_VALUE_SIZE`. +/// Needed to avoid caching large values like contract codes. +/// 2) If we put new value to LRU cache and total size of existing values exceeds `total_sizes_capacity`, we evict +/// values from it until that is no longer the case. So the actual total size should never exceed +/// `total_size_limit` + `TRIE_LIMIT_CACHED_VALUE_SIZE`. +/// Needed because value sizes generally vary from 1 B to 500 B and we want to count cache size precisely. +/// 3) If value is popped, it is put to the `deletions` queue with `deletions_queue_capacity` first. If popped value +/// doesn't fit in the queue, the last value is removed from the queue and LRU cache, and newly popped value is inserted +/// to the queue. +/// Needed to delay deletions when we have forks. In such case, many blocks can share same parent, and we want to keep +/// old nodes in cache for a while to process all new roots. For example, it helps to read old state root. +pub struct TrieCacheInner { + /// LRU cache keeping mapping from keys to values. + cache: LruCache>, + /// Queue of items which were popped, which postpones deletion of old nodes. + deletions: BoundedQueue, + /// Current total size of all values in the cache. + total_size: u64, + /// Upper bound for the total size. + total_size_limit: u64, + /// Shard id of the nodes being cached. + shard_id: ShardId, + /// Whether cache is used for view calls execution. + is_view: bool, + // Counters tracking operations happening inside the shard cache. + // Stored here to avoid overhead of looking them up on hot paths. + metrics: TrieCacheMetrics, +} + +struct TrieCacheMetrics { + shard_cache_too_large: GenericCounter, + shard_cache_pop_hits: GenericCounter, + shard_cache_pop_misses: GenericCounter, + shard_cache_pop_lru: GenericCounter, + shard_cache_gc_pop_misses: GenericCounter, + shard_cache_deletions_size: GenericGauge, +} + +impl TrieCacheInner { + /// Assumed number of bytes used to store an entry in the cache. + /// + /// 100 bytes is an approximation based on lru 0.7.5. + pub(crate) const PER_ENTRY_OVERHEAD: u64 = 100; + + pub(crate) fn new( + deletions_queue_capacity: usize, + total_size_limit: u64, + shard_id: ShardId, + is_view: bool, + ) -> Self { + assert!(total_size_limit > 0); + // `itoa` is much faster for printing shard_id to a string than trivial alternatives. + let mut buffer = itoa::Buffer::new(); + let shard_id_str = buffer.format(shard_id); + + let metrics_labels: [&str; 2] = [&shard_id_str, if is_view { "1" } else { "0" }]; + let metrics = TrieCacheMetrics { + shard_cache_too_large: metrics::SHARD_CACHE_TOO_LARGE + .with_label_values(&metrics_labels), + shard_cache_pop_hits: metrics::SHARD_CACHE_POP_HITS.with_label_values(&metrics_labels), + shard_cache_pop_misses: metrics::SHARD_CACHE_POP_MISSES + .with_label_values(&metrics_labels), + shard_cache_pop_lru: metrics::SHARD_CACHE_POP_LRU.with_label_values(&metrics_labels), + shard_cache_gc_pop_misses: metrics::SHARD_CACHE_GC_POP_MISSES + .with_label_values(&metrics_labels), + shard_cache_deletions_size: metrics::SHARD_CACHE_DELETIONS_SIZE + .with_label_values(&metrics_labels), + }; + Self { + cache: LruCache::unbounded(), + deletions: BoundedQueue::new(deletions_queue_capacity), + total_size: 0, + total_size_limit, + shard_id, + is_view, + metrics, + } + } + + pub(crate) fn get(&mut self, key: &CryptoHash) -> Option> { + self.cache.get(key).cloned() + } + + pub(crate) fn clear(&mut self) { + self.total_size = 0; + self.deletions.clear(); + self.cache.clear(); + } + + pub(crate) fn put(&mut self, key: CryptoHash, value: Arc<[u8]>) { + while self.total_size > self.total_size_limit || self.cache.len() == self.cache.cap() { + // First, try to evict value using the key from deletions queue. + match self.deletions.pop() { + Some(key) => match self.cache.pop(&key) { + Some(value) => { + self.metrics.shard_cache_pop_hits.inc(); + self.remove_value_of_size(value.len()); + continue; + } + None => { + self.metrics.shard_cache_pop_misses.inc(); + } + }, + None => {} + } + + // Second, pop LRU value. + self.metrics.shard_cache_pop_lru.inc(); + let (_, value) = + self.cache.pop_lru().expect("Cannot fail because total size capacity is > 0"); + self.remove_value_of_size(value.len()); + } + + // Add value to the cache. + self.add_value_of_size(value.len()); + match self.cache.push(key, value) { + Some((evicted_key, evicted_value)) => { + log_assert!(key == evicted_key, "LRU cache with shard_id = {}, is_view = {} can't be full before inserting key {}", self.shard_id, self.is_view, key); + self.remove_value_of_size(evicted_value.len()); + } + None => {} + }; + } + + // Adds key to the deletions queue if it is present in cache. + // Returns key-value pair which are popped if deletions queue is full. + pub(crate) fn pop(&mut self, key: &CryptoHash) -> Option<(CryptoHash, Arc<[u8]>)> { + self.metrics.shard_cache_deletions_size.set(self.deletions.len() as i64); + // Do nothing if key was removed before. + if self.cache.contains(key) { + // Put key to the queue of deletions and possibly remove another key we have to delete. + match self.deletions.put(*key) { + Some(key_to_delete) => match self.cache.pop(&key_to_delete) { + Some(evicted_value) => { + self.metrics.shard_cache_pop_hits.inc(); + self.remove_value_of_size(evicted_value.len()); + Some((key_to_delete, evicted_value)) + } + None => { + self.metrics.shard_cache_pop_misses.inc(); + None + } + }, + None => None, + } + } else { + self.metrics.shard_cache_gc_pop_misses.inc(); + None + } + } + + /// Number of currently cached entries. + pub fn len(&self) -> usize { + self.cache.len() + } + + /// Account consumed memory for a new entry in the cache. + pub(crate) fn add_value_of_size(&mut self, len: usize) { + self.total_size += Self::entry_size(len); + } + + /// Remove consumed memory for an entry in the cache. + pub(crate) fn remove_value_of_size(&mut self, len: usize) { + self.total_size -= Self::entry_size(len); + } + + /// Approximate memory consumption of LRU cache. + pub fn current_total_size(&self) -> u64 { + self.total_size + } + + fn entry_size(len: usize) -> u64 { + len as u64 + Self::PER_ENTRY_OVERHEAD + } +} + +/// Wrapper over LruCache to handle concurrent access. +#[derive(Clone)] +pub struct TrieCache(pub(crate) Arc>); + +impl TrieCache { + pub fn new(config: &TrieConfig, shard_uid: ShardUId, is_view: bool) -> Self { + let cache_config = + if is_view { &config.view_shard_cache_config } else { &config.shard_cache_config }; + let total_size_limit = cache_config + .per_shard_max_bytes + .get(&shard_uid) + .copied() + .unwrap_or(cache_config.default_max_bytes); + let queue_capacity = config.deletions_queue_capacity(); + Self(Arc::new(Mutex::new(TrieCacheInner::new( + queue_capacity, + total_size_limit, + shard_uid.shard_id(), + is_view, + )))) + } + + pub fn get(&self, key: &CryptoHash) -> Option> { + self.lock().get(key) + } + + pub fn clear(&self) { + self.lock().clear() + } + + pub fn update_cache(&self, ops: Vec<(&CryptoHash, Option<&[u8]>)>) { + let mut guard = self.lock(); + for (hash, opt_value) in ops { + if let Some(value) = opt_value { + if value.len() < TrieConfig::max_cached_value_size() { + guard.put(*hash, value.into()); + } else { + guard.pop(&hash); + guard.metrics.shard_cache_too_large.inc(); + } + } else { + guard.pop(&hash); + } + } + } + + pub(crate) fn lock(&self) -> std::sync::MutexGuard { + self.0.lock().expect(POISONED_LOCK_ERR) + } +} + +pub trait TrieStorage { + /// Get bytes of a serialized `TrieNode`. + /// + /// # Errors + /// + /// [`StorageError`] if the storage fails internally or the hash is not present. + fn retrieve_raw_bytes(&self, hash: &CryptoHash) -> Result, StorageError>; + + /// DEPRECATED. + /// Returns `TrieCachingStorage` if `TrieStorage` is implemented by it. + /// TODO (#9004) remove all remaining calls. + fn as_caching_storage(&self) -> Option<&TrieCachingStorage> { + None + } + + fn as_partial_storage(&self) -> Option<&TrieMemoryPartialStorage> { + None + } +} + +/// Storage for validating recorded partial storage. +/// visited_nodes are to validate that partial storage doesn't contain unnecessary nodes. +#[derive(Default)] +pub struct TrieMemoryPartialStorage { + pub(crate) recorded_storage: HashMap>, + pub(crate) visited_nodes: RefCell>, +} + +impl TrieStorage for TrieMemoryPartialStorage { + fn retrieve_raw_bytes(&self, hash: &CryptoHash) -> Result, StorageError> { + let result = + self.recorded_storage.get(hash).cloned().ok_or(StorageError::MissingTrieValue( + MissingTrieValueContext::TrieMemoryPartialStorage, + *hash, + )); + if result.is_ok() { + self.visited_nodes.borrow_mut().insert(*hash); + } + result + } + + fn as_partial_storage(&self) -> Option<&TrieMemoryPartialStorage> { + Some(self) + } +} + +impl TrieMemoryPartialStorage { + pub fn new(recorded_storage: HashMap>) -> Self { + Self { recorded_storage, visited_nodes: Default::default() } + } + + pub fn partial_state(&self) -> PartialState { + let touched_nodes = self.visited_nodes.borrow(); + let mut nodes: Vec<_> = + self.recorded_storage + .iter() + .filter_map(|(node_hash, value)| { + if touched_nodes.contains(node_hash) { + Some(value.clone()) + } else { + None + } + }) + .collect(); + + nodes.sort(); + PartialState::TrieValues(nodes) + } +} + +/// Storage for reading State nodes and values from DB which caches reads. +/// +/// Important: The TrieCachingStorage contains the shard cache, which is +/// different from the accounting cache. The former is a best-effort +/// optimization to speed up execution, whereas the latter is a deterministic +/// cache used for gas accounting during contract execution. +pub struct TrieCachingStorage { + pub(crate) store: Store, + pub(crate) shard_uid: ShardUId, + pub(crate) is_view: bool, + + /// Caches ever requested items for the shard `shard_uid`. Used to speed up DB operations, presence of any item is + /// not guaranteed. + pub(crate) shard_cache: TrieCache, + + /// The entry point for the runtime to submit prefetch requests. + pub(crate) prefetch_api: Option, + + // Counters tracking operations happening inside the shard cache. + // Stored here to avoid overhead of looking them up on hot paths. + metrics: TrieCacheInnerMetrics, +} + +struct TrieCacheInnerMetrics { + shard_cache_hits: GenericCounter, + shard_cache_misses: GenericCounter, + shard_cache_too_large: GenericCounter, + shard_cache_size: GenericGauge, + shard_cache_current_total_size: GenericGauge, + prefetch_hits: GenericCounter, + prefetch_pending: GenericCounter, + prefetch_not_requested: GenericCounter, + prefetch_memory_limit_reached: GenericCounter, + prefetch_retry: GenericCounter, + prefetch_conflict: GenericCounter, +} + +impl TrieCachingStorage { + pub fn new( + store: Store, + shard_cache: TrieCache, + shard_uid: ShardUId, + is_view: bool, + prefetch_api: Option, + ) -> TrieCachingStorage { + // `itoa` is much faster for printing shard_id to a string than trivial alternatives. + let mut buffer = itoa::Buffer::new(); + let shard_id = buffer.format(shard_uid.shard_id); + + let metrics_labels: [&str; 2] = [&shard_id, if is_view { "1" } else { "0" }]; + let metrics = TrieCacheInnerMetrics { + shard_cache_hits: metrics::SHARD_CACHE_HITS.with_label_values(&metrics_labels), + shard_cache_misses: metrics::SHARD_CACHE_MISSES.with_label_values(&metrics_labels), + shard_cache_too_large: metrics::SHARD_CACHE_TOO_LARGE + .with_label_values(&metrics_labels), + shard_cache_size: metrics::SHARD_CACHE_SIZE.with_label_values(&metrics_labels), + shard_cache_current_total_size: metrics::SHARD_CACHE_CURRENT_TOTAL_SIZE + .with_label_values(&metrics_labels), + prefetch_hits: metrics::PREFETCH_HITS.with_label_values(&metrics_labels[..1]), + prefetch_pending: metrics::PREFETCH_PENDING.with_label_values(&metrics_labels[..1]), + prefetch_not_requested: metrics::PREFETCH_NOT_REQUESTED + .with_label_values(&metrics_labels[..1]), + prefetch_memory_limit_reached: metrics::PREFETCH_MEMORY_LIMIT_REACHED + .with_label_values(&metrics_labels[..1]), + prefetch_retry: metrics::PREFETCH_RETRY.with_label_values(&metrics_labels[..1]), + prefetch_conflict: metrics::PREFETCH_CONFLICT.with_label_values(&metrics_labels[..1]), + }; + TrieCachingStorage { store, shard_uid, is_view, shard_cache, prefetch_api, metrics } + } + + pub fn get_key_from_shard_uid_and_hash(shard_uid: ShardUId, hash: &CryptoHash) -> [u8; 40] { + let mut key = [0; 40]; + key[0..8].copy_from_slice(&shard_uid.to_bytes()); + key[8..].copy_from_slice(hash.as_ref()); + key + } +} + +impl TrieStorage for TrieCachingStorage { + fn retrieve_raw_bytes(&self, hash: &CryptoHash) -> Result, StorageError> { + // Try to get value from shard cache containing most recently touched nodes. + let mut guard = self.shard_cache.lock(); + self.metrics.shard_cache_size.set(guard.len() as i64); + self.metrics.shard_cache_current_total_size.set(guard.current_total_size() as i64); + let val = match guard.get(hash) { + Some(val) => { + self.metrics.shard_cache_hits.inc(); + unc_o11y::io_trace!(count: "shard_cache_hit"); + val + } + None => { + self.metrics.shard_cache_misses.inc(); + unc_o11y::io_trace!(count: "shard_cache_miss"); + let val; + if let Some(prefetcher) = &self.prefetch_api { + let prefetch_state = prefetcher.prefetching.get_or_set_fetching(*hash); + // Keep lock until here to avoid race condition between shard cache lookup and reserving prefetch slot. + std::mem::drop(guard); + + val = match prefetch_state { + // Slot reserved for us, the main thread. + // `SlotReserved` for the main thread means, either we have not submitted a prefetch request for + // this value, or maybe it is just still queued up. Either way, prefetching is not going to help + // so the main thread should fetch data from DB on its own. + PrefetcherResult::SlotReserved => { + self.metrics.prefetch_not_requested.inc(); + self.read_from_db(hash)? + } + // `MemoryLimitReached` is not really relevant for the main thread, + // we always have to go to DB even if we could not stage a new prefetch. + // It only means we were not able to mark it as already being fetched, which in turn could lead to + // a prefetcher trying to fetch the same value before we can put it in the shard cache. + PrefetcherResult::MemoryLimitReached => { + self.metrics.prefetch_memory_limit_reached.inc(); + self.read_from_db(hash)? + } + PrefetcherResult::Prefetched(value) => { + unc_o11y::io_trace!(count: "prefetch_hit"); + self.metrics.prefetch_hits.inc(); + value + } + PrefetcherResult::Pending => { + unc_o11y::io_trace!(count: "prefetch_pending"); + self.metrics.prefetch_pending.inc(); + std::thread::yield_now(); + // If data is already being prefetched, wait for that instead of sending a new request. + match prefetcher.prefetching.blocking_get(*hash) { + Some(value) => value, + // Only main thread (this one) removes values from staging area, + // therefore blocking read will usually not return empty unless there + // was a storage error. Or in the case of forks and parallel chunk + // processing where one chunk cleans up prefetched data from the other. + // So first we need to check if the data was inserted to shard_cache + // by the main thread from another fork and only if that fails then + // fetch the data from the DB. + None => { + if let Some(value) = self.shard_cache.get(hash) { + self.metrics.prefetch_conflict.inc(); + value + } else { + self.metrics.prefetch_retry.inc(); + self.read_from_db(hash)? + } + } + } + } + }; + } else { + std::mem::drop(guard); + val = self.read_from_db(hash)?; + } + + // Insert value to shard cache, if its size is small enough. + // It is fine to have a size limit for shard cache and **not** have a limit for accounting cache, because key + // is always a value hash, so for each key there could be only one value, and it is impossible to have + // **different** values for the given key in shard and accounting caches. + if val.len() < TrieConfig::max_cached_value_size() { + let mut guard = self.shard_cache.lock(); + guard.put(*hash, val.clone()); + } else { + self.metrics.shard_cache_too_large.inc(); + unc_o11y::io_trace!(count: "shard_cache_too_large"); + } + + if let Some(prefetcher) = &self.prefetch_api { + // Only release after insertion in shard cache. See comment on fn release. + prefetcher.prefetching.release(hash); + } + + val + } + }; + + Ok(val) + } + + fn as_caching_storage(&self) -> Option<&TrieCachingStorage> { + Some(self) + } +} + +fn read_node_from_db( + store: &Store, + shard_uid: ShardUId, + hash: &CryptoHash, +) -> Result, StorageError> { + let key = TrieCachingStorage::get_key_from_shard_uid_and_hash(shard_uid, hash); + let val = store + .get(DBCol::State, key.as_ref()) + .map_err(|_| StorageError::StorageInternalError)? + .ok_or_else(|| { + StorageError::MissingTrieValue(MissingTrieValueContext::TrieStorage, *hash) + })?; + Ok(val.into()) +} + +impl TrieCachingStorage { + fn read_from_db(&self, hash: &CryptoHash) -> Result, StorageError> { + read_node_from_db(&self.store, self.shard_uid, hash) + } + + pub fn prefetch_api(&self) -> &Option { + &self.prefetch_api + } +} + +/// Storage for reading State nodes and values directly from DB. +/// +/// This `TrieStorage` implementation has no caches, it just goes to DB. +/// It is useful for background tasks that should not affect chunk processing and block each other. +pub struct TrieDBStorage { + pub(crate) store: Store, + pub(crate) shard_uid: ShardUId, +} + +impl TrieDBStorage { + pub fn new(store: Store, shard_uid: ShardUId) -> Self { + Self { store, shard_uid } + } +} + +impl TrieStorage for TrieDBStorage { + fn retrieve_raw_bytes(&self, hash: &CryptoHash) -> Result, StorageError> { + read_node_from_db(&self.store, self.shard_uid, hash) + } +} + +#[cfg(test)] +mod bounded_queue_tests { + use crate::trie::trie_storage::BoundedQueue; + + #[test] + fn test_put_pop() { + let mut queue = BoundedQueue::new(2); + assert_eq!(queue.put(1), None); + assert_eq!(queue.put(2), None); + assert_eq!(queue.put(3), Some(1)); + + assert_eq!(queue.pop(), Some(2)); + assert_eq!(queue.pop(), Some(3)); + assert_eq!(queue.pop(), None); + } + + #[test] + fn test_clear() { + let mut queue = BoundedQueue::new(2); + queue.put(1); + assert_eq!(queue.queue.get(0), Some(&1)); + queue.clear(); + assert!(queue.queue.is_empty()); + } + + #[test] + fn test_zero_capacity() { + let mut queue = BoundedQueue::new(0); + assert_eq!(queue.put(1), Some(1)); + assert!(queue.queue.is_empty()); + } +} + +#[cfg(test)] +mod trie_cache_tests { + use crate::trie::trie_storage::TrieCacheInner; + use crate::{StoreConfig, TrieCache, TrieConfig}; + use unc_primitives::hash::hash; + use unc_primitives::shard_layout::ShardUId; + use unc_primitives::types::ShardId; + + fn put_value(cache: &mut TrieCacheInner, value: &[u8]) { + cache.put(hash(value), value.into()); + } + + #[test] + fn test_size_limit() { + let value_size_sum = 5; + let memory_overhead = 2 * TrieCacheInner::PER_ENTRY_OVERHEAD; + let mut cache = TrieCacheInner::new(100, value_size_sum + memory_overhead, 0, false); + // Add three values. Before each put, condition on total size should not be triggered. + put_value(&mut cache, &[1, 1]); + assert_eq!(cache.current_total_size(), 2 + TrieCacheInner::PER_ENTRY_OVERHEAD); + put_value(&mut cache, &[1, 1, 1]); + assert_eq!(cache.current_total_size(), 5 + 2 * TrieCacheInner::PER_ENTRY_OVERHEAD); + put_value(&mut cache, &[1]); + assert_eq!(cache.current_total_size(), 6 + 3 * TrieCacheInner::PER_ENTRY_OVERHEAD); + + // Add one of previous values. 2 LRU values should be evicted. + put_value(&mut cache, &[1, 1, 1]); + assert_eq!(cache.current_total_size(), 4 + 2 * TrieCacheInner::PER_ENTRY_OVERHEAD); + assert_eq!(cache.cache.pop_lru(), Some((hash(&[1]), vec![1].into()))); + assert_eq!(cache.cache.pop_lru(), Some((hash(&[1, 1, 1]), vec![1, 1, 1].into()))); + } + + #[test] + fn test_deletions_queue() { + let mut cache = TrieCacheInner::new(2, 1000, 0, false); + // Add two values to the cache. + put_value(&mut cache, &[1]); + put_value(&mut cache, &[1, 1]); + + // Call pop for inserted values. Because deletions queue is not full, no elements should be deleted. + assert_eq!(cache.pop(&hash(&[1, 1])), None); + assert_eq!(cache.pop(&hash(&[1])), None); + + // Call pop two times for a value existing in cache. Because queue is full, both elements should be deleted. + assert_eq!(cache.pop(&hash(&[1])), Some((hash(&[1, 1]), vec![1, 1].into()))); + assert_eq!(cache.pop(&hash(&[1])), Some((hash(&[1]), vec![1].into()))); + } + + /// test implicit capacity limit imposed by memory limit + #[test] + fn test_cache_capacity() { + let capacity = 2; + let total_size_limit = TrieCacheInner::PER_ENTRY_OVERHEAD * capacity; + let mut cache = TrieCacheInner::new(100, total_size_limit, 0, false); + put_value(&mut cache, &[1]); + put_value(&mut cache, &[2]); + put_value(&mut cache, &[3]); + + assert!(!cache.cache.contains(&hash(&[1]))); + assert!(cache.cache.contains(&hash(&[2]))); + assert!(cache.cache.contains(&hash(&[3]))); + } + + #[test] + fn test_small_memory_limit() { + let total_size_limit = 1; + let mut cache = TrieCacheInner::new(100, total_size_limit, 0, false); + put_value(&mut cache, &[1, 2, 3]); + put_value(&mut cache, &[2, 3, 4]); + put_value(&mut cache, &[3, 4, 5]); + + assert!(!cache.cache.contains(&hash(&[1, 2, 3]))); + assert!(!cache.cache.contains(&hash(&[2, 3, 4]))); + assert!(cache.cache.contains(&hash(&[3, 4, 5]))); + } + + /// Check that setting from `StoreConfig` are applied. + #[test] + fn test_trie_config() { + let mut store_config = StoreConfig::default(); + + const DEFAULT_SIZE: u64 = 1; + const S0_SIZE: u64 = 2; + const DEFAULT_VIEW_SIZE: u64 = 3; + const S0_VIEW_SIZE: u64 = 4; + + let s0 = ShardUId::single_shard(); + store_config.trie_cache.default_max_bytes = DEFAULT_SIZE; + store_config.trie_cache.per_shard_max_bytes.insert(s0, S0_SIZE); + store_config.view_trie_cache.default_max_bytes = DEFAULT_VIEW_SIZE; + store_config.view_trie_cache.per_shard_max_bytes.insert(s0, S0_VIEW_SIZE); + let trie_config = TrieConfig::from_store_config(&store_config); + + check_cache_size(&trie_config, 1, false, DEFAULT_SIZE); + check_cache_size(&trie_config, 0, false, S0_SIZE); + check_cache_size(&trie_config, 1, true, DEFAULT_VIEW_SIZE); + check_cache_size(&trie_config, 0, true, S0_VIEW_SIZE); + } + + #[track_caller] + fn check_cache_size( + trie_config: &TrieConfig, + shard_id: ShardId, + is_view: bool, + expected_size: u64, + ) { + let shard_uid = ShardUId { version: 0, shard_id: shard_id as u32 }; + let trie_cache = TrieCache::new(&trie_config, shard_uid, is_view); + assert_eq!(expected_size, trie_cache.lock().total_size_limit,); + assert_eq!(is_view, trie_cache.lock().is_view,); + } +} diff --git a/core/store/src/trie/trie_tests.rs b/core/store/src/trie/trie_tests.rs new file mode 100644 index 000000000..108196c2e --- /dev/null +++ b/core/store/src/trie/trie_tests.rs @@ -0,0 +1,415 @@ +use crate::test_utils::{gen_changes, simplify_changes, test_populate_trie, TestTriesBuilder}; +use crate::trie::trie_storage::{TrieMemoryPartialStorage, TrieStorage}; +use crate::{PartialStorage, Trie, TrieUpdate}; +use assert_matches::assert_matches; +use unc_primitives::challenge::PartialState; +use unc_primitives::errors::{MissingTrieValueContext, StorageError}; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::types::TrieNodesCount; +use rand::seq::SliceRandom; +use rand::Rng; +use std::cell::RefCell; +use std::collections::{HashMap, HashSet}; +use std::fmt::Debug; +use std::rc::Rc; +use std::sync::Arc; + +/// TrieMemoryPartialStorage, but contains only the first n requested nodes. +pub struct IncompletePartialStorage { + pub(crate) recorded_storage: HashMap>, + pub(crate) visited_nodes: RefCell>, + pub node_count_to_fail_after: usize, +} + +impl IncompletePartialStorage { + pub fn new(partial_storage: PartialStorage, nodes_count_to_fail_at: usize) -> Self { + let PartialState::TrieValues(nodes) = partial_storage.nodes; + let recorded_storage = nodes.into_iter().map(|value| (hash(&value), value)).collect(); + Self { + recorded_storage, + visited_nodes: Default::default(), + node_count_to_fail_after: nodes_count_to_fail_at, + } + } +} + +impl TrieStorage for IncompletePartialStorage { + fn retrieve_raw_bytes(&self, hash: &CryptoHash) -> Result, StorageError> { + let result = self + .recorded_storage + .get(hash) + .cloned() + .expect("Recorded storage is missing the given hash"); + + self.visited_nodes.borrow_mut().insert(*hash); + + if self.visited_nodes.borrow().len() > self.node_count_to_fail_after { + Err(StorageError::MissingTrieValue( + MissingTrieValueContext::TrieMemoryPartialStorage, + *hash, + )) + } else { + Ok(result) + } + } + + fn as_partial_storage(&self) -> Option<&TrieMemoryPartialStorage> { + // Make sure it's not called - it pretends to be PartialStorage but is not + unimplemented!() + } +} + +fn setup_storage(trie: Trie, test: &mut F) -> (PartialStorage, Trie, Out) +where + F: FnMut(Trie) -> Result<(Trie, Out), StorageError>, + Out: PartialEq + Debug, +{ + let recording_trie = trie.recording_reads(); + let (recording_trie, output) = test(recording_trie).expect("should not fail"); + (recording_trie.recorded_storage().unwrap(), recording_trie, output) +} + +fn test_incomplete_storage(trie: Trie, mut test: F) +where + F: FnMut(Trie) -> Result<(Trie, Out), StorageError>, + Out: PartialEq + Debug, +{ + let (storage, trie, expected) = setup_storage(trie, &mut test); + let size = storage.nodes.len(); + println!("Test touches {} nodes, expected result {:?}...", size, expected); + for i in 0..(size + 1) { + let storage = IncompletePartialStorage::new(storage.clone(), i); + let new_trie = Trie::new(Rc::new(storage), *trie.get_root(), None); + let result = test(new_trie).map(|v| v.1); + if i < size { + assert_matches!( + result, + Err(StorageError::MissingTrieValue( + MissingTrieValueContext::TrieMemoryPartialStorage, + _ + )) + ); + } else { + assert_eq!(result.as_ref(), Ok(&expected)); + } + } + println!("Success"); +} + +#[test] +fn test_reads_with_incomplete_storage() { + let mut rng = rand::thread_rng(); + for _ in 0..50 { + let tries = TestTriesBuilder::new().with_shard_layout(1, 2).build(); + let shard_uid = ShardUId { version: 1, shard_id: 0 }; + let trie_changes = gen_changes(&mut rng, 20); + let trie_changes = simplify_changes(&trie_changes); + if trie_changes.is_empty() { + continue; + } + let state_root = + test_populate_trie(&tries, &Trie::EMPTY_ROOT, shard_uid, trie_changes.clone()); + let get_trie = || tries.get_trie_for_shard(shard_uid, state_root); + + { + let (key, _) = trie_changes.choose(&mut rng).unwrap(); + println!("Testing lookup {:?}", key); + let lookup_test = + |trie: Trie| -> Result<_, StorageError> { trie.get(key).map(move |v| (trie, v)) }; + test_incomplete_storage(get_trie(), lookup_test); + } + { + println!("Testing TrieIterator over whole trie"); + let trie_records = |trie: Trie| -> Result<_, StorageError> { + let iterator = trie.iter()?; + iterator.collect::, _>>().map(move |v| (trie, v)) + }; + test_incomplete_storage(get_trie(), trie_records); + } + { + let (key, _) = trie_changes.choose(&mut rng).unwrap(); + let key_prefix = &key[0..rng.gen_range(0..key.len() + 1)]; + println!("Testing TrieUpdateIterator over prefix {:?}", key_prefix); + let trie_update_keys = |trie: Trie| -> Result<_, StorageError> { + let trie_update = TrieUpdate::new(trie); + let keys = trie_update.iter(key_prefix)?.collect::, _>>()?; + Ok((trie_update.trie, keys)) + }; + test_incomplete_storage(get_trie(), trie_update_keys); + } + } +} + +#[cfg(test)] +mod nodes_counter_tests { + use super::*; + use crate::trie::nibble_slice::NibbleSlice; + + fn create_trie_key(nibbles: &[u8]) -> Vec { + NibbleSlice::encode_nibbles(&nibbles, false).into_vec() + } + + fn create_trie(items: &[(Vec, Option>)]) -> Rc { + let tries = TestTriesBuilder::new().build(); + let shard_uid = ShardUId { version: 1, shard_id: 0 }; + let trie_changes = simplify_changes(&items); + let state_root = test_populate_trie(&tries, &Trie::EMPTY_ROOT, shard_uid, trie_changes); + let trie = tries.get_trie_for_shard(shard_uid, state_root); + Rc::new(trie) + } + + // Get values corresponding to keys one by one, returning vector of numbers of touched nodes for each `get`. + fn get_touched_nodes_numbers(trie: Rc, items: &[(Vec, Option>)]) -> Vec { + items + .iter() + .map(|(key, value)| { + let initial_count = trie.get_trie_nodes_count().db_reads; + let got_value = trie.get(key).unwrap(); + assert_eq!(*value, got_value); + trie.get_trie_nodes_count().db_reads - initial_count + }) + .collect() + } + + // Test nodes counter and trie cache size on the sample of trie items. + #[test] + fn test_count() { + // For keys with nibbles [000, 011, 100], we expect 6 touched nodes to get value for the first key 000: + // Extension -> Branch -> Branch -> Leaf plus retrieving the value by its hash. + let trie_items = vec![ + (create_trie_key(&[0, 0, 0]), Some(vec![0])), + (create_trie_key(&[0, 1, 1]), Some(vec![1])), + (create_trie_key(&[1, 0, 0]), Some(vec![2])), + ]; + let trie = create_trie(&trie_items); + assert_eq!(get_touched_nodes_numbers(trie, &trie_items), vec![5, 5, 4]); + } + + // Check that same values are stored in the same trie node. + #[test] + fn test_repeated_values_count() { + // For these keys there will be 5 nodes with distinct hashes, because each path looks like + // Extension([0, 0]) -> Branch -> Leaf([48/49]) -> value. + // TODO: explain the exact values in path items here + let trie_items = vec![ + (create_trie_key(&[0, 0]), Some(vec![1])), + (create_trie_key(&[1, 1]), Some(vec![1])), + ]; + let trie = create_trie(&trie_items); + assert_eq!(get_touched_nodes_numbers(trie, &trie_items), vec![4, 4]); + } +} + +#[cfg(test)] +mod trie_storage_tests { + use super::*; + use crate::test_utils::create_test_store; + use crate::trie::accounting_cache::TrieAccountingCache; + use crate::trie::trie_storage::{TrieCache, TrieCachingStorage, TrieDBStorage}; + use crate::trie::TrieRefcountAddition; + use crate::{Store, TrieChanges, TrieConfig}; + use assert_matches::assert_matches; + use unc_primitives::hash::hash; + + fn create_store_with_values(values: &[Vec], shard_uid: ShardUId) -> Store { + let tries = TestTriesBuilder::new().build(); + let mut trie_changes = TrieChanges::empty(Trie::EMPTY_ROOT); + trie_changes.insertions = values + .iter() + .map(|value| TrieRefcountAddition { + trie_node_or_value_hash: hash(value), + trie_node_or_value: value.clone(), + rc: std::num::NonZeroU32::new(1).unwrap(), + }) + .collect(); + let mut store_update = tries.store_update(); + tries.apply_all(&trie_changes, shard_uid, &mut store_update); + store_update.commit().unwrap(); + tries.get_store() + } + + /// Put item into storage. Check that it is retrieved correctly. + #[test] + fn test_retrieve_db() { + let value = vec![1u8]; + let values = vec![value.clone()]; + let shard_uid = ShardUId::single_shard(); + let store = create_store_with_values(&values, shard_uid); + let trie_db_storage = TrieDBStorage::new(store, shard_uid); + let key = hash(&value); + assert_eq!(trie_db_storage.retrieve_raw_bytes(&key).unwrap().as_ref(), value); + let wrong_key = hash(&[2]); + assert_matches!(trie_db_storage.retrieve_raw_bytes(&wrong_key), Err(_)); + } + + /// Put item into storage. Check that getting it from cache returns the correct value. + #[test] + fn test_retrieve_caching() { + let value = vec![1u8]; + let values = vec![value.clone()]; + let shard_uid = ShardUId::single_shard(); + let store = create_store_with_values(&values, shard_uid); + let trie_cache = TrieCache::new(&TrieConfig::default(), shard_uid, false); + let trie_caching_storage = + TrieCachingStorage::new(store, trie_cache.clone(), shard_uid, false, None); + let mut accounting_cache = TrieAccountingCache::new(None); + let key = hash(&value); + assert_eq!(trie_cache.get(&key), None); + + for _ in 0..2 { + let count_before = accounting_cache.get_trie_nodes_count(); + let result = + accounting_cache.retrieve_raw_bytes_with_accounting(&key, &trie_caching_storage); + let count_delta = + accounting_cache.get_trie_nodes_count().checked_sub(&count_before).unwrap(); + assert_eq!(result.unwrap().as_ref(), value); + assert_eq!(count_delta.db_reads, 1); + assert_eq!(count_delta.mem_reads, 0); + assert_eq!(trie_cache.get(&key).unwrap().as_ref(), value); + } + } + + /// Check that if item is not present in a store, retrieval returns an error. + #[test] + fn test_retrieve_error() { + let shard_uid = ShardUId::single_shard(); + let store = create_test_store(); + let trie_caching_storage = TrieCachingStorage::new( + store, + TrieCache::new(&TrieConfig::default(), shard_uid, false), + shard_uid, + false, + None, + ); + let value = vec![1u8]; + let key = hash(&value); + + let result = trie_caching_storage.retrieve_raw_bytes(&key); + assert_matches!(result, Err(StorageError::MissingTrieValue(_, _))); + } + + /// Check that large values does not fall into shard cache, but fall into accounting cache. + #[test] + fn test_large_value() { + let value = [1u8].repeat(TrieConfig::max_cached_value_size() + 1); + let values = vec![value.clone()]; + let shard_uid = ShardUId::single_shard(); + let store = create_store_with_values(&values, shard_uid); + let trie_cache = TrieCache::new(&TrieConfig::default(), shard_uid, false); + let trie_caching_storage = + TrieCachingStorage::new(store, trie_cache.clone(), shard_uid, false, None); + let mut accounting_cache = TrieAccountingCache::new(None); + let key = hash(&value); + + accounting_cache.set_enabled(true); + let _ = accounting_cache.retrieve_raw_bytes_with_accounting(&key, &trie_caching_storage); + + let count_before: TrieNodesCount = accounting_cache.get_trie_nodes_count(); + let result = + accounting_cache.retrieve_raw_bytes_with_accounting(&key, &trie_caching_storage); + let count_delta = + accounting_cache.get_trie_nodes_count().checked_sub(&count_before).unwrap(); + assert_eq!(trie_cache.get(&key), None); + assert_eq!(result.unwrap().as_ref(), value); + assert_eq!(count_delta.db_reads, 0); + assert_eq!(count_delta.mem_reads, 1); + } + + /// Check that positions of item and costs of its retrieval are returned correctly. + #[test] + fn test_counter_with_caching() { + let values = vec![vec![1u8]]; + let shard_uid = ShardUId::single_shard(); + let store = create_store_with_values(&values, shard_uid); + let trie_cache = TrieCache::new(&TrieConfig::default(), shard_uid, false); + let trie_caching_storage = + TrieCachingStorage::new(store, trie_cache.clone(), shard_uid, false, None); + let mut accounting_cache = TrieAccountingCache::new(None); + let value = &values[0]; + let key = hash(&value); + + // In the beginning, we are in the CachingShard mode and item is not present in cache. + assert_eq!(trie_cache.get(&key), None); + + // Because we are in the CachingShard mode, item should be placed into shard cache. + let result = trie_caching_storage.retrieve_raw_bytes(&key); + assert_eq!(result.unwrap().as_ref(), value); + + // Move to CachingChunk mode. Retrieval should increment the counter, because it is the first time we accessed + // item while caching chunk. + accounting_cache.set_enabled(true); + let count_before = accounting_cache.get_trie_nodes_count(); + let result = + accounting_cache.retrieve_raw_bytes_with_accounting(&key, &trie_caching_storage); + let count_delta = + accounting_cache.get_trie_nodes_count().checked_sub(&count_before).unwrap(); + assert_eq!(result.unwrap().as_ref(), value); + assert_eq!(count_delta.db_reads, 1); + assert_eq!(count_delta.mem_reads, 0); + + // After previous retrieval, item must be copied to accounting cache. Retrieval shouldn't increment the counter. + let count_before = accounting_cache.get_trie_nodes_count(); + let result = + accounting_cache.retrieve_raw_bytes_with_accounting(&key, &trie_caching_storage); + let count_delta = + accounting_cache.get_trie_nodes_count().checked_sub(&count_before).unwrap(); + assert_eq!(result.unwrap().as_ref(), value); + assert_eq!(count_delta.db_reads, 0); + assert_eq!(count_delta.mem_reads, 1); + + // Even if we switch to caching shard, retrieval shouldn't increment the counter. Accounting cache only grows and is + // dropped only when trie caching storage is dropped. + accounting_cache.set_enabled(true); + let count_before = accounting_cache.get_trie_nodes_count(); + let result = + accounting_cache.retrieve_raw_bytes_with_accounting(&key, &trie_caching_storage); + let count_delta = + accounting_cache.get_trie_nodes_count().checked_sub(&count_before).unwrap(); + assert_eq!(result.unwrap().as_ref(), value); + assert_eq!(count_delta.db_reads, 0); + assert_eq!(count_delta.mem_reads, 1); + } + + /// Check that if an item present in accounting cache gets evicted from the shard cache, + /// it stays in the accounting cache. + #[test] + fn test_accounting_cache_presence() { + let shard_cache_size = 5; + let values: Vec> = (0..shard_cache_size as u8 + 1).map(|i| vec![i]).collect(); + let shard_uid = ShardUId::single_shard(); + let store = create_store_with_values(&values, shard_uid); + let mut trie_config = TrieConfig::default(); + trie_config.shard_cache_config.per_shard_max_bytes.insert(shard_uid, shard_cache_size); + let trie_cache = TrieCache::new(&trie_config, shard_uid, false); + let trie_caching_storage = + TrieCachingStorage::new(store, trie_cache.clone(), shard_uid, false, None); + let mut accounting_cache = TrieAccountingCache::new(None); + + let value = &values[0]; + let key = hash(&value); + + accounting_cache.set_enabled(true); + let result = + accounting_cache.retrieve_raw_bytes_with_accounting(&key, &trie_caching_storage); + assert_eq!(result.unwrap().as_ref(), value); + + accounting_cache.set_enabled(true); + for value in values[1..].iter() { + let result = accounting_cache + .retrieve_raw_bytes_with_accounting(&hash(value), &trie_caching_storage); + assert_eq!(result.unwrap().as_ref(), value); + } + + // Check that the first element gets evicted, but the counter is not incremented. + assert_eq!(trie_cache.get(&key), None); + let count_before = accounting_cache.get_trie_nodes_count(); + let result = + accounting_cache.retrieve_raw_bytes_with_accounting(&key, &trie_caching_storage); + let count_delta = + accounting_cache.get_trie_nodes_count().checked_sub(&count_before).unwrap(); + assert_eq!(result.unwrap().as_ref(), value); + assert_eq!(count_delta.db_reads, 0); + assert_eq!(count_delta.mem_reads, 1); + } +} diff --git a/core/store/src/trie/update.rs b/core/store/src/trie/update.rs new file mode 100644 index 000000000..b4b21a3e1 --- /dev/null +++ b/core/store/src/trie/update.rs @@ -0,0 +1,319 @@ +pub use self::iterator::TrieUpdateIterator; +use super::{OptimizedValueRef, Trie}; +use crate::trie::{KeyLookupMode, TrieChanges}; +use crate::StorageError; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::{ + RawStateChange, RawStateChanges, RawStateChangesWithTrieKey, StateChangeCause, StateRoot, + TrieCacheMode, +}; +use std::collections::BTreeMap; + +mod iterator; + +/// Key-value update. Contains a TrieKey and a value. +pub struct TrieKeyValueUpdate { + pub trie_key: TrieKey, + pub value: Option>, +} + +/// key that was updated -> the update. +pub type TrieUpdates = BTreeMap, TrieKeyValueUpdate>; + +/// Provides a way to access Storage and record changes with future commit. +/// TODO (#7327): rename to StateUpdate +pub struct TrieUpdate { + pub trie: Trie, + committed: RawStateChanges, + prospective: TrieUpdates, +} + +pub enum TrieUpdateValuePtr<'a> { + Ref(&'a Trie, OptimizedValueRef), + MemoryRef(&'a [u8]), +} + +impl<'a> TrieUpdateValuePtr<'a> { + pub fn len(&self) -> u32 { + match self { + TrieUpdateValuePtr::MemoryRef(value) => value.len() as u32, + TrieUpdateValuePtr::Ref(_, value_ref) => value_ref.len() as u32, + } + } + + pub fn deref_value(&self) -> Result, StorageError> { + match self { + TrieUpdateValuePtr::MemoryRef(value) => Ok(value.to_vec()), + TrieUpdateValuePtr::Ref(trie, value_ref) => Ok(trie.deref_optimized(value_ref)?), + } + } +} + +impl TrieUpdate { + pub fn new(trie: Trie) -> Self { + TrieUpdate { trie, committed: Default::default(), prospective: Default::default() } + } + + pub fn trie(&self) -> &Trie { + &self.trie + } + + pub fn get_ref( + &self, + key: &TrieKey, + mode: KeyLookupMode, + ) -> Result>, StorageError> { + let key = key.to_vec(); + if let Some(key_value) = self.prospective.get(&key) { + return Ok(key_value.value.as_deref().map(TrieUpdateValuePtr::MemoryRef)); + } else if let Some(changes_with_trie_key) = self.committed.get(&key) { + if let Some(RawStateChange { data, .. }) = changes_with_trie_key.changes.last() { + return Ok(data.as_deref().map(TrieUpdateValuePtr::MemoryRef)); + } + } + + let result = self + .trie + .get_optimized_ref(&key, mode)? + .map(|optimized_value_ref| TrieUpdateValuePtr::Ref(&self.trie, optimized_value_ref)); + + Ok(result) + } + + pub fn get(&self, key: &TrieKey) -> Result>, StorageError> { + let key = key.to_vec(); + if let Some(key_value) = self.prospective.get(&key) { + return Ok(key_value.value.as_ref().map(>::clone)); + } else if let Some(changes_with_trie_key) = self.committed.get(&key) { + if let Some(RawStateChange { data, .. }) = changes_with_trie_key.changes.last() { + return Ok(data.as_ref().map(>::clone)); + } + } + self.trie.get(&key) + } + + pub fn set(&mut self, trie_key: TrieKey, value: Vec) { + // NOTE: Converting `TrieKey` to a `Vec` is useful here for 2 reasons: + // - Using `Vec` for sorting `BTreeMap` in the same order as a `Trie` and + // avoid recomputing `Vec` every time. It helps for merging iterators. + // - Using `TrieKey` later for `RawStateChangesWithTrieKey` for State changes RPCs. + self.prospective + .insert(trie_key.to_vec(), TrieKeyValueUpdate { trie_key, value: Some(value) }); + } + + pub fn remove(&mut self, trie_key: TrieKey) { + self.prospective.insert(trie_key.to_vec(), TrieKeyValueUpdate { trie_key, value: None }); + } + + pub fn commit(&mut self, event: StateChangeCause) { + let prospective = std::mem::take(&mut self.prospective); + for (raw_key, TrieKeyValueUpdate { trie_key, value }) in prospective.into_iter() { + self.committed + .entry(raw_key) + .or_insert_with(|| RawStateChangesWithTrieKey { trie_key, changes: Vec::new() }) + .changes + .push(RawStateChange { cause: event.clone(), data: value }); + } + } + + pub fn rollback(&mut self) { + self.prospective.clear(); + } + + /// Prepare the accumulated state changes to be applied to the underlying storage. + /// + /// This Function returns the [`Trie`] with which the [`TrieUpdate`] has been initially + /// constructed. It can be reused to construct another `TrieUpdate` or to operate with `Trie` + /// in any other way as desired. + pub fn finalize( + self, + ) -> Result<(Trie, TrieChanges, Vec), StorageError> { + assert!(self.prospective.is_empty(), "Finalize cannot be called with uncommitted changes."); + let TrieUpdate { trie, committed, .. } = self; + let mut state_changes = Vec::with_capacity(committed.len()); + let trie_changes = + trie.update(committed.into_iter().map(|(k, changes_with_trie_key)| { + let data = changes_with_trie_key + .changes + .last() + .expect("Committed entry should have at least one change") + .data + .clone(); + state_changes.push(changes_with_trie_key); + (k, data) + }))?; + Ok((trie, trie_changes, state_changes)) + } + + /// Returns Error if the underlying storage fails + pub fn iter(&self, key_prefix: &[u8]) -> Result, StorageError> { + TrieUpdateIterator::new(self, key_prefix) + } + + pub fn get_root(&self) -> &StateRoot { + self.trie.get_root() + } + + pub fn set_trie_cache_mode(&self, state: TrieCacheMode) { + self.trie.accounting_cache.borrow_mut().set_enabled(state == TrieCacheMode::CachingChunk); + } +} + +impl crate::TrieAccess for TrieUpdate { + fn get(&self, key: &TrieKey) -> Result>, StorageError> { + TrieUpdate::get(self, key) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utils::TestTriesBuilder; + use crate::ShardUId; + use unc_primitives::hash::CryptoHash; + const SHARD_VERSION: u32 = 1; + const COMPLEX_SHARD_UID: ShardUId = ShardUId { version: SHARD_VERSION, shard_id: 0 }; + + fn test_key(key: Vec) -> TrieKey { + TrieKey::ContractData { account_id: "alice".parse().unwrap(), key } + } + + #[test] + fn trie() { + let tries = TestTriesBuilder::new().with_shard_layout(SHARD_VERSION, 2).build(); + let root = Trie::EMPTY_ROOT; + let mut trie_update = tries.new_trie_update(COMPLEX_SHARD_UID, root); + trie_update.set(test_key(b"dog".to_vec()), b"puppy".to_vec()); + trie_update.set(test_key(b"dog2".to_vec()), b"puppy".to_vec()); + trie_update.set(test_key(b"xxx".to_vec()), b"puppy".to_vec()); + trie_update + .commit(StateChangeCause::TransactionProcessing { tx_hash: CryptoHash::default() }); + let trie_changes = trie_update.finalize().unwrap().1; + let mut store_update = tries.store_update(); + let new_root = tries.apply_all(&trie_changes, COMPLEX_SHARD_UID, &mut store_update); + store_update.commit().unwrap(); + let trie_update2 = tries.new_trie_update(COMPLEX_SHARD_UID, new_root); + assert_eq!(trie_update2.get(&test_key(b"dog".to_vec())), Ok(Some(b"puppy".to_vec()))); + let values = trie_update2 + .iter(&test_key(b"dog".to_vec()).to_vec()) + .unwrap() + .collect::, _>>() + .unwrap(); + assert_eq!( + values, + vec![test_key(b"dog".to_vec()).to_vec(), test_key(b"dog2".to_vec()).to_vec()] + ); + } + + #[test] + fn trie_remove() { + let tries = TestTriesBuilder::new().with_shard_layout(SHARD_VERSION, 2).build(); + + // Delete non-existing element. + let mut trie_update = tries.new_trie_update(COMPLEX_SHARD_UID, Trie::EMPTY_ROOT); + trie_update.remove(test_key(b"dog".to_vec())); + trie_update.commit(StateChangeCause::TransactionProcessing { tx_hash: Trie::EMPTY_ROOT }); + let trie_changes = trie_update.finalize().unwrap().1; + let mut store_update = tries.store_update(); + let new_root = tries.apply_all(&trie_changes, COMPLEX_SHARD_UID, &mut store_update); + store_update.commit().unwrap(); + assert_eq!(new_root, Trie::EMPTY_ROOT); + + // Add and right away delete element. + let mut trie_update = tries.new_trie_update(COMPLEX_SHARD_UID, Trie::EMPTY_ROOT); + trie_update.set(test_key(b"dog".to_vec()), b"puppy".to_vec()); + trie_update.remove(test_key(b"dog".to_vec())); + trie_update + .commit(StateChangeCause::TransactionProcessing { tx_hash: CryptoHash::default() }); + let trie_changes = trie_update.finalize().unwrap().1; + let mut store_update = tries.store_update(); + let new_root = tries.apply_all(&trie_changes, COMPLEX_SHARD_UID, &mut store_update); + store_update.commit().unwrap(); + assert_eq!(new_root, Trie::EMPTY_ROOT); + + // Add, apply changes and then delete element. + let mut trie_update = tries.new_trie_update(COMPLEX_SHARD_UID, Trie::EMPTY_ROOT); + trie_update.set(test_key(b"dog".to_vec()), b"puppy".to_vec()); + trie_update + .commit(StateChangeCause::TransactionProcessing { tx_hash: CryptoHash::default() }); + let trie_changes = trie_update.finalize().unwrap().1; + let mut store_update = tries.store_update(); + let new_root = tries.apply_all(&trie_changes, COMPLEX_SHARD_UID, &mut store_update); + store_update.commit().unwrap(); + assert_ne!(new_root, Trie::EMPTY_ROOT); + let mut trie_update = tries.new_trie_update(COMPLEX_SHARD_UID, new_root); + trie_update.remove(test_key(b"dog".to_vec())); + trie_update + .commit(StateChangeCause::TransactionProcessing { tx_hash: CryptoHash::default() }); + let trie_changes = trie_update.finalize().unwrap().1; + let mut store_update = tries.store_update(); + let new_root = tries.apply_all(&trie_changes, COMPLEX_SHARD_UID, &mut store_update); + store_update.commit().unwrap(); + assert_eq!(new_root, Trie::EMPTY_ROOT); + } + + #[test] + fn trie_iter() { + let tries = TestTriesBuilder::new().build(); + let mut trie_update = tries.new_trie_update(ShardUId::single_shard(), Trie::EMPTY_ROOT); + trie_update.set(test_key(b"dog".to_vec()), b"puppy".to_vec()); + trie_update.set(test_key(b"aaa".to_vec()), b"puppy".to_vec()); + trie_update + .commit(StateChangeCause::TransactionProcessing { tx_hash: CryptoHash::default() }); + let trie_changes = trie_update.finalize().unwrap().1; + let mut store_update = tries.store_update(); + let new_root = tries.apply_all(&trie_changes, ShardUId::single_shard(), &mut store_update); + store_update.commit().unwrap(); + + let mut trie_update = tries.new_trie_update(ShardUId::single_shard(), new_root); + trie_update.set(test_key(b"dog2".to_vec()), b"puppy".to_vec()); + trie_update.set(test_key(b"xxx".to_vec()), b"puppy".to_vec()); + + let values: Result>, _> = + trie_update.iter(&test_key(b"dog".to_vec()).to_vec()).unwrap().collect(); + assert_eq!( + values.unwrap(), + vec![test_key(b"dog".to_vec()).to_vec(), test_key(b"dog2".to_vec()).to_vec()] + ); + + trie_update.rollback(); + + let values: Result>, _> = + trie_update.iter(&test_key(b"dog".to_vec()).to_vec()).unwrap().collect(); + assert_eq!(values.unwrap(), vec![test_key(b"dog".to_vec()).to_vec()]); + + let mut trie_update = tries.new_trie_update(ShardUId::single_shard(), new_root); + trie_update.remove(test_key(b"dog".to_vec())); + + let values: Result>, _> = + trie_update.iter(&test_key(b"dog".to_vec()).to_vec()).unwrap().collect(); + assert_eq!(values.unwrap().len(), 0); + + let mut trie_update = tries.new_trie_update(ShardUId::single_shard(), new_root); + trie_update.set(test_key(b"dog2".to_vec()), b"puppy".to_vec()); + trie_update + .commit(StateChangeCause::TransactionProcessing { tx_hash: CryptoHash::default() }); + trie_update.remove(test_key(b"dog2".to_vec())); + + let values: Result>, _> = + trie_update.iter(&test_key(b"dog".to_vec()).to_vec()).unwrap().collect(); + assert_eq!(values.unwrap(), vec![test_key(b"dog".to_vec()).to_vec()]); + + let mut trie_update = tries.new_trie_update(ShardUId::single_shard(), new_root); + trie_update.set(test_key(b"dog2".to_vec()), b"puppy".to_vec()); + trie_update + .commit(StateChangeCause::TransactionProcessing { tx_hash: CryptoHash::default() }); + trie_update.set(test_key(b"dog3".to_vec()), b"puppy".to_vec()); + + let values: Result>, _> = + trie_update.iter(&test_key(b"dog".to_vec()).to_vec()).unwrap().collect(); + assert_eq!( + values.unwrap(), + vec![ + test_key(b"dog".to_vec()).to_vec(), + test_key(b"dog2".to_vec()).to_vec(), + test_key(b"dog3".to_vec()).to_vec() + ] + ); + } +} diff --git a/core/store/src/trie/update/iterator.rs b/core/store/src/trie/update/iterator.rs new file mode 100644 index 000000000..953a6838a --- /dev/null +++ b/core/store/src/trie/update/iterator.rs @@ -0,0 +1,166 @@ +use std::iter::Peekable; +use std::ops::Bound; + +use crate::trie::update::*; +use crate::StorageError; + +use crate::trie::TrieIterator; + +struct MergeIter<'a> { + left: Peekable)> + 'a>>, + right: Peekable)> + 'a>>, +} + +impl<'a> Iterator for MergeIter<'a> { + type Item = (&'a [u8], Option<&'a [u8]>); + + fn next(&mut self) -> Option { + let res = match (self.left.peek(), self.right.peek()) { + (Some(&(ref left_key, _)), Some(&(ref right_key, _))) => left_key.cmp(right_key), + (Some(_), None) => std::cmp::Ordering::Less, + (None, Some(_)) => std::cmp::Ordering::Greater, + (None, None) => return None, + }; + + // Check which elements comes first and only advance the corresponding iterator. + // If two keys are equal, take the value from `right`. + match res { + std::cmp::Ordering::Less => self.left.next(), + std::cmp::Ordering::Greater => self.right.next(), + std::cmp::Ordering::Equal => { + self.left.next(); + self.right.next() + } + } + } +} + +pub struct TrieUpdateIterator<'a>(Option<(Peekable>, Peekable>)>); + +impl<'a> TrieUpdateIterator<'a> { + #![allow(clippy::new_ret_no_self)] + pub fn new(state_update: &'a TrieUpdate, prefix: &[u8]) -> Result { + let mut trie_iter = state_update.trie.iter()?; + trie_iter.seek_prefix(prefix)?; + + let end_bound = make_prefix_range_end_bound(prefix); + let end_bound = if let Some(end_bound) = &end_bound { + Bound::Excluded(end_bound.as_slice()) + } else { + Bound::Unbounded + }; + let range = (Bound::Included(prefix), end_bound); + + let committed_iter = state_update.committed.range::<[u8], _>(range).map( + |(raw_key, changes_with_trie_key)| { + let key = raw_key.as_slice(); + let value = changes_with_trie_key + .changes + .last() + .as_ref() + .expect("Committed entry should have at least one change.") + .data + .as_deref(); + (key, value) + }, + ); + let prospective_iter = state_update + .prospective + .range::<[u8], _>(range) + .map(|(raw_key, key_value)| (raw_key.as_slice(), key_value.value.as_deref())); + let overlay_iter = MergeIter { + left: (Box::new(committed_iter) as Box>).peekable(), + right: (Box::new(prospective_iter) as Box>).peekable(), + } + .peekable(); + Ok(TrieUpdateIterator(Some((trie_iter.peekable(), overlay_iter)))) + } +} + +impl<'a> Iterator for TrieUpdateIterator<'a> { + type Item = Result, StorageError>; + + fn next(&mut self) -> Option { + #[derive(Eq, PartialEq)] + enum Ordering { + Trie, + Overlay, + Both, + } + // Usually one iteration, unless need to skip None values in prospective / committed. + let iterators = self.0.as_mut()?; + loop { + let res = match (iterators.0.peek(), iterators.1.peek()) { + (Some(Err(_)), _) => { + let err = iterators.0.next().unwrap().unwrap_err(); + self.0 = None; + return Some(Err(err)); + } + + (Some(Ok((left_key, _))), Some((right_key, _))) => { + match left_key.as_slice().cmp(right_key) { + std::cmp::Ordering::Less => Ordering::Trie, + std::cmp::Ordering::Equal => Ordering::Both, + std::cmp::Ordering::Greater => Ordering::Overlay, + } + } + (Some(_), None) => Ordering::Trie, + (None, Some(_)) => Ordering::Overlay, + (None, None) => { + self.0 = None; + return None; + } + }; + + // Check which element comes first and advance the corresponding + // iterator only. If both keys are equal, check if overlay doesn’t + // delete the value. + let trie_item = if res != Ordering::Overlay { iterators.0.next() } else { None }; + if res == Ordering::Trie { + if let Some(Ok((key, _))) = trie_item { + return Some(Ok(key)); + } + } else if let Some((overlay_key, Some(_))) = iterators.1.next() { + return Some(Ok(if let Some(Ok((trie_key, _))) = trie_item { + debug_assert_eq!(trie_key.as_slice(), overlay_key); + trie_key + } else { + overlay_key.to_vec() + })); + } + } + } +} + +impl<'a> std::iter::FusedIterator for TrieUpdateIterator<'a> {} + +/// Returns an end bound for a range which corresponds to all values with +/// a given prefix. +/// +/// In other words, the smallest value larger than the `prefix` which does not +/// start with the `prefix`. If no such value exists, returns `None`. +fn make_prefix_range_end_bound(prefix: &[u8]) -> Option> { + let ffs = prefix.iter().rev().take_while(|&&byte| byte == u8::MAX).count(); + let next = &prefix[..(prefix.len() - ffs)]; + if next.is_empty() { + // Prefix consisted of \xff bytes. There is no key that follows it. + None + } else { + let mut next = next.to_vec(); + *next.last_mut().unwrap() += 1; + Some(next) + } +} + +#[test] +fn test_make_prefix_range_end_bound() { + fn test(want: Option<&[u8]>, prefix: &[u8]) { + assert_eq!(want, make_prefix_range_end_bound(prefix).as_deref()); + } + + test(None, b""); + test(None, b"\xff"); + test(None, b"\xff\xff\xff\xff"); + test(Some(b"b"), b"a"); + test(Some(b"b"), b"a\xff\xff\xff"); +} diff --git a/debug_scripts/Pipfile b/debug_scripts/Pipfile new file mode 100644 index 000000000..2bd5fa2a0 --- /dev/null +++ b/debug_scripts/Pipfile @@ -0,0 +1,13 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +boto3 = "==1.21.34" +requests = "==2.31.0" + +[dev-packages] + +[requires] +python_version = "3" diff --git a/debug_scripts/Pipfile.lock b/debug_scripts/Pipfile.lock new file mode 100644 index 000000000..db9ee53f7 --- /dev/null +++ b/debug_scripts/Pipfile.lock @@ -0,0 +1,199 @@ +{ + "_meta": { + "hash": { + "sha256": "07bcc80bbdf7ca6f53281f47da3893a22bcf724ad3238baa70c8abd2e9cc9a16" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "boto3": { + "hashes": [ + "sha256:6cbc3aae70af5f35869d7b2f114cc953edf925231febf892d609682b2ba3f9f0", + "sha256:9d2175ad0f5de587473f20b062b1ef462d1e5ee547bb22bdd36e28442823e0a9" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==1.21.34" + }, + "botocore": { + "hashes": [ + "sha256:663d8f02b98641846eb959c54c840cc33264d5f2dee5b8fc09ee8adbef0f8dcf", + "sha256:89a203bba3c8f2299287e48a9e112e2dbe478cf67eaac26716f0e7f176446146" + ], + "markers": "python_version >= '3.6'", + "version": "==1.24.46" + }, + "certifi": { + "hashes": [ + "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", + "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" + ], + "markers": "python_version >= '3.6'", + "version": "==2023.7.22" + }, + "charset-normalizer": { + "hashes": [ + "sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843", + "sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786", + "sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e", + "sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8", + "sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4", + "sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa", + "sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d", + "sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82", + "sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7", + "sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895", + "sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d", + "sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a", + "sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382", + "sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678", + "sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b", + "sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e", + "sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741", + "sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4", + "sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596", + "sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9", + "sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69", + "sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c", + "sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77", + "sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13", + "sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459", + "sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e", + "sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7", + "sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908", + "sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a", + "sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f", + "sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8", + "sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482", + "sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d", + "sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d", + "sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545", + "sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34", + "sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86", + "sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6", + "sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe", + "sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e", + "sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc", + "sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7", + "sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd", + "sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c", + "sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557", + "sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a", + "sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89", + "sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078", + "sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e", + "sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4", + "sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403", + "sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0", + "sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89", + "sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115", + "sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9", + "sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05", + "sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a", + "sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec", + "sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56", + "sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38", + "sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479", + "sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c", + "sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e", + "sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd", + "sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186", + "sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455", + "sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c", + "sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65", + "sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78", + "sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287", + "sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df", + "sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43", + "sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1", + "sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7", + "sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989", + "sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a", + "sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63", + "sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884", + "sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649", + "sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810", + "sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828", + "sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4", + "sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2", + "sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd", + "sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5", + "sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe", + "sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293", + "sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e", + "sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e", + "sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.3.0" + }, + "idna": { + "hashes": [ + "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", + "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" + ], + "markers": "python_version >= '3.5'", + "version": "==3.4" + }, + "jmespath": { + "hashes": [ + "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", + "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" + ], + "markers": "python_version >= '3.7'", + "version": "==1.0.1" + }, + "python-dateutil": { + "hashes": [ + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.8.2" + }, + "requests": { + "hashes": [ + "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.31.0" + }, + "s3transfer": { + "hashes": [ + "sha256:7a6f4c4d1fdb9a2b640244008e142cbc2cd3ae34b386584ef044dd0f27101971", + "sha256:95c58c194ce657a5f4fb0b9e60a84968c808888aed628cd98ab8771fe1db98ed" + ], + "markers": "python_version >= '3.6'", + "version": "==0.5.2" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "urllib3": { + "hashes": [ + "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07", + "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==1.26.18" + } + }, + "develop": {} +} diff --git a/debug_scripts/READEME.md b/debug_scripts/READEME.md new file mode 100644 index 000000000..e0cc3968c --- /dev/null +++ b/debug_scripts/READEME.md @@ -0,0 +1,36 @@ +# Debug Scripts +## Content +* request_chain_info.py + + This script can be used to request blockchain info +* send_validator_logs.py + + + This script can be used to send Validator logs to a Pagoda S3 bucket when issues are encountered. The pagoda team can use the logs to help the validators troubleshoot issues. + + +## Instruction to RUN + + ``` + cd /framework/debug_scripts + python3 -m pip install pipenv + python3 -m pipenv shell + python3 send_validator_logs.py --help + OR + python3 request_chain_info.py --help + ``` + +## Instruction to run test +Add framework/debug_scripts to your PYTHONPATH +``` +export PYTHONPATH="/framework/debug_scripts:$PYTHONPATH" +``` + +``` +cd /framework/debug_scripts +python3 -m pipenv sync +python3 -m pipenv shell +python3 -m unittest tests.send_validator_logs_test +``` + + diff --git a/debug_scripts/__init__.py b/debug_scripts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/debug_scripts/request_chain_info.py b/debug_scripts/request_chain_info.py new file mode 100755 index 000000000..d5fe22b56 --- /dev/null +++ b/debug_scripts/request_chain_info.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 + +import requests +import json +import argparse + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description='This is a script to request for blockchain info') + parser.add_argument('--chain', + choices=['mainnet', 'testnet', 'betanet'], + required=True) + parser.add_argument('--archive', + action='store_true', + help='whether to request from archival nodes') + parser.add_argument('--method', + choices=['block', 'chunk'], + required=True, + help='type of request') + parser.add_argument('--block_id', + type=str, + help='block id, can be either block height or hash') + parser.add_argument('--shard_id', type=int, help='shard id for the chunk') + parser.add_argument('--chunk_id', type=str, help='chunk hash') + parser.add_argument('--result_key', + type=str, + nargs='*', + help='filter results by these keys') + args = parser.parse_args() + + url = 'https://{}.{}.near.org'.format( + 'archival-rpc' if args.archive else 'rpc', args.chain) + + def get_block_id(block_id): + if block_id.isnumeric(): + return int(block_id) + return block_id + + if args.method == 'block': + if args.block_id is not None: + params = {'block_id': get_block_id(args.block_id)} + else: + params = {'finality': 'final'} + elif args.method == 'chunk': + if args.shard_id is not None: + assert args.block_id is not None + params = { + 'shard_id': args.shard_id, + 'block_id': get_block_id(args.block_id) + } + elif args.chunk_id is not None: + params = {'chunk_id': args.chunk_id} + else: + assert False + + payload = { + 'jsonrpc': '2.0', + 'id': 'dontcare', + 'method': args.method, + 'params': params + } + + response = requests.post(url, json=payload) + result = response.json()['result'] + if args.result_key is not None: + for key in args.result_key: + result = result[key] + print(json.dumps(result, indent=4)) diff --git a/debug_scripts/send_validator_logs.py b/debug_scripts/send_validator_logs.py new file mode 100644 index 000000000..8109ca45a --- /dev/null +++ b/debug_scripts/send_validator_logs.py @@ -0,0 +1,92 @@ +#!/bin/python3 +import argparse +import boto3 +import datetime +import gzip +import io +import sys +import urllib.parse + + +def filter_log_file(log_file: str, start_time: datetime.datetime, + end_time: datetime.datetime) -> list: + """ + Filter log file for a time range. + start_time: datetime.datetime + start time for logs + end_time: datetime.datetime + end time for logs + return: list + list of log lines + """ + print(f"Log time range: {start_time} \t {end_time}") + + filtered_logs = [] + + # filter logs for time range + with open(log_file) as f: + for line in f: + # [0m and [2m are ANSI shell color codes. Removing them to parse dates. + split_lines = line.split("[0m", 1)[0].replace("\x1b[2m", "") + dt = datetime.datetime.strptime( + split_lines[:-5], + "%b %d %H:%M:%S").replace(year=datetime.datetime.now().year) + if start_time <= dt <= end_time: + filtered_logs.append(line) + return filtered_logs + + +def upload_to_s3(file_lines: list, account: str) -> str: + """ + Upload File like object to S3 bucket unc-protocol-validator-logs-public. + file_obj: io.BytesIO + account: str + return string with S3 file path + """ + BUCKET = "unc-protocol-validator-logs-public" + current_time = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") + s3_destination = f"{account}/{current_time}.log.gzip" + file_string = io.StringIO() + for line in file_lines: + file_string.write(line) + + file_obj = io.BytesIO(file_string.getvalue().encode()) + gzipped_content = gzip.compress(file_obj.read()) + print( + f"uploading compressed file. File size is: {sys.getsizeof(gzipped_content)} Bytes" + ) + + s3 = boto3.resource('s3') + s3.Bucket(BUCKET).upload_fileobj(io.BytesIO(gzipped_content), + f"logs/{s3_destination}") + s3_link = f"https://{BUCKET}.s3.amazonaws.com/logs/{urllib.parse.quote(s3_destination)}" + print(f"Log File was uploaded to S3: {s3_link}") + file_obj.close() + return s3_link + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Send logs to near.') + parser.add_argument('--log_file', + type=str, + help='Absolute path to log file.', + required=True) + parser.add_argument('--account', + type=str, + help='unc account id.', + required=True) + parser.add_argument('--last_seconds', + type=int, + help='Filter logs for last x seconds.', + required=True) + args = parser.parse_args() + + log_file_path = args.log_file + end_timestamp = datetime.datetime.utcnow() + start_timestamp = end_timestamp - datetime.timedelta( + seconds=args.last_seconds) + + filtered_log_lines = filter_log_file(log_file=args.log_file, + start_time=start_timestamp, + end_time=end_timestamp) + upload_to_s3(file_lines=filtered_log_lines, account=args.account) diff --git a/debug_scripts/tests/__init__.py b/debug_scripts/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/debug_scripts/tests/data/node0.logs b/debug_scripts/tests/data/node0.logs new file mode 100644 index 000000000..880214968 --- /dev/null +++ b/debug_scripts/tests/data/node0.logs @@ -0,0 +1,79 @@ +Apr 04 23:42:15.731  INFO uncd: Version: 1.25.0-rc.3, Build: crates-0.12.0-30-g4ac008b4a, Latest Protocol: 52 +Apr 04 23:42:15.786  INFO near: Opening store database at "/root/.near/localnet/node0/data" +Apr 04 23:42:27.116  INFO unc_network::peer_manager::peer_manager_actor: Bandwidth stats total_bandwidth_used_by_all_peers=0 total_msg_received_count=0 max_max_record_num_messages_in_progress=0 +Apr 04 23:42:37.183  INFO stats: # 5857 Hg4YQjycDMrkioDVhAHyGbPS4jNxbvDZQ4E6epuSVBV4 Validator | 4 validators 3 peers ⬇ 0.8kiB/s ⬆ 1.1kiB/s 1.19 bps 0 gas/s +Apr 04 23:42:47.193  INFO stats: # 5875 4NLUbNjKpvQL4JMQ5dptuvUNQKk7m597rxcykvq6NkXE Validator | 4 validators 3 peers ⬇ 2.0kiB/s ⬆ 2.1kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:42:57.199  INFO stats: # 5894 FynB9bDQzBpoF3DNGdC1XvkrFshAu2gUyaKLFAHccGXP Validator | 4 validators 3 peers ⬇ 3.1kiB/s ⬆ 3.3kiB/s 1.90 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:43:07.203  INFO stats: # 5914 Etfd1xUeAhdK7HKDLZCN6qjGB8zSGLGtCc2X5JGMNxLH Validator | 4 validators 3 peers ⬇ 4.3kiB/s ⬆ 4.5kiB/s 2.00 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:43:17.207  INFO stats: # 5932 J6wrh6NXpTA3B1H9BBz9qRKaqHcYFYahi4UubHUMbsLS Validator | 4 validators 3 peers ⬇ 5.4kiB/s ⬆ 5.5kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:43:27.132  INFO unc_network::peer_manager::peer_manager_actor: Bandwidth stats total_bandwidth_used_by_all_peers=401874 total_msg_received_count=594 max_max_record_num_messages_in_progress=5 +Apr 04 23:43:27.210  INFO stats: # 5952 7wzVLX4WEKxJpGZfKp39QRU32m1rmh7mqsHpyFDFx3v6 Validator | 4 validators 3 peers ⬇ 6.5kiB/s ⬆ 6.9kiB/s 2.00 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:43:37.214  INFO stats: # 5972 5XR3yDVMX7YsSRuS6HyQ3tRpDCAz2QJkvbyVd3YGNUYj Validator | 4 validators 3 peers ⬇ 7.0kiB/s ⬆ 6.9kiB/s 2.00 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:43:47.217  INFO stats: # 5992 HEruDzu2eQspxXVRyo1FdB2VVh3ocH3e85mxFyeUU6ZN Validator | 4 validators 3 peers ⬇ 6.9kiB/s ⬆ 7.2kiB/s 2.00 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:43:57.220  INFO stats: # 6013 BMo9kuZWiKEyPRJekYCN2Zv6BCvK6M1dXLbxzQdH5agV Validator | 4 validators 3 peers ⬇ 7.0kiB/s ⬆ 7.3kiB/s 2.10 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:44:07.225  INFO stats: # 6033 ELmLN1brofFfC1ctULgGUUoWpNohLskWV3fs3SkasLi Validator | 4 validators 3 peers ⬇ 7.0kiB/s ⬆ 7.1kiB/s 2.00 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:44:17.231  INFO stats: # 6049 8EadxwDNJF7S1JKKJgCfx2imWVVMTgWmqdXELDEZLES5 Validator | 4 validators 3 peers ⬇ 6.8kiB/s ⬆ 7.2kiB/s 1.60 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:44:27.133  INFO unc_network::peer_manager::peer_manager_actor: Bandwidth stats total_bandwidth_used_by_all_peers=422820 total_msg_received_count=609 max_max_record_num_messages_in_progress=2 +Apr 04 23:44:27.236  INFO stats: # 6068 8V5KrpyJizP5Z5g8VtBqifRg1e51CycqpTrUK6Tosup1 Validator | 4 validators 3 peers ⬇ 6.9kiB/s ⬆ 7.2kiB/s 1.90 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:44:37.244  INFO stats: # 6088 2AYtzbnG6oJMxE5aaZKEYsUGeJL9NDMTLhhRMex44FhV Validator | 4 validators 3 peers ⬇ 6.7kiB/s ⬆ 7.2kiB/s 2.00 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:44:47.250  INFO stats: # 6107 8DTqUcwHmLf8d6ieYaHxuXN8BthFyxkigq4aQEi23knm Validator | 4 validators 3 peers ⬇ 6.6kiB/s ⬆ 7.2kiB/s 1.90 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:44:57.253  INFO stats: # 6125 3278dJxhvSRaWS4WZV9TDoBfwmyT7obJcHgcPUr8U2J8 Validator | 4 validators 3 peers ⬇ 6.5kiB/s ⬆ 7.0kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:45:07.258  INFO stats: # 6144 BAk2YhXh1rZnc7zVy29DKq1qmCBgSQVkcuyrc7dBzfdV Validator | 4 validators 3 peers ⬇ 6.5kiB/s ⬆ 7.0kiB/s 1.90 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:45:17.262  INFO stats: # 6164 2atSgzhx7uH658RLp79HmbPspxj59X7fPmqHG75SS8ce Validator | 4 validators 3 peers ⬇ 6.7kiB/s ⬆ 7.1kiB/s 2.00 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:45:27.134  INFO unc_network::peer_manager::peer_manager_actor: Bandwidth stats total_bandwidth_used_by_all_peers=409868 total_msg_received_count=588 max_max_record_num_messages_in_progress=3 +Apr 04 23:45:27.265  INFO stats: # 6182 7YhWre271ZanvLpkj7pZdhv2tAyXa8xtQj4qiD7Taua4 Validator | 4 validators 3 peers ⬇ 6.6kiB/s ⬆ 7.3kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:45:37.270  INFO stats: # 6202 3fAWZYTW6f7FoB6aRpoWRVH2AZMU16faN8YrwgdbSFpp Validator | 4 validators 3 peers ⬇ 6.8kiB/s ⬆ 7.1kiB/s 2.00 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:45:47.277  INFO stats: # 6220 2QXPEP3PNTKgskj9pMYN7GvJjKArXAZgPvvkhjPpch6v Validator | 4 validators 3 peers ⬇ 6.9kiB/s ⬆ 6.8kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:45:57.281  INFO stats: # 6240 HRPggo93hfnRr9tgD3utGd42G3DEoTANcjryiMEFSnND Validator | 4 validators 3 peers ⬇ 7.0kiB/s ⬆ 7.0kiB/s 2.00 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:46:07.289  INFO stats: # 6258 GcE2mJL23jrjcKmMTqsznWLP7nb1M9UMWmdnJSwMHThk Validator | 4 validators 3 peers ⬇ 6.6kiB/s ⬆ 7.2kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:46:17.294  INFO stats: # 6275 DH1zwNGPgDU3taVXXXsVQNCRGiL2m9zy9KSgR8MpEn6f Validator | 4 validators 3 peers ⬇ 6.6kiB/s ⬆ 7.2kiB/s 1.70 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:46:27.135  INFO unc_network::peer_manager::peer_manager_actor: Bandwidth stats total_bandwidth_used_by_all_peers=402010 total_msg_received_count=570 max_max_record_num_messages_in_progress=2 +Apr 04 23:46:27.299  INFO stats: # 6292 E7KMo3EbwjNiM72VULXVscyzen2RBM8ULaZrLs5g6wHj Validator | 4 validators 3 peers ⬇ 6.5kiB/s ⬆ 7.0kiB/s 1.70 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:46:37.319  INFO stats: # 6310 AJy3ocas8nVPgPC5zXkK9EC2o9N2pdp1WPKKAF8A4MXs Validator | 4 validators 3 peers ⬇ 6.2kiB/s ⬆ 6.8kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:46:47.324  INFO stats: # 6329 Amcma59rZpVCs2bWVJ5N7Ua8483WUADWdJrY43WEuAR9 Validator | 4 validators 3 peers ⬇ 6.1kiB/s ⬆ 7.0kiB/s 1.90 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:46:57.329  INFO stats: # 6348 EiwfEnRy6mq6ASxE5At2whaSzh68jxhAgZwS9ieqhtRS Validator | 4 validators 3 peers ⬇ 6.3kiB/s ⬆ 6.6kiB/s 1.90 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:47:07.334  INFO stats: # 6366 AEg6HAJHXm3Mim873tYXBgWZG2UZdTwazzgf9MyzghfF Validator | 4 validators 3 peers ⬇ 6.6kiB/s ⬆ 6.5kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:47:17.337  INFO stats: # 6386 2ThnDKef2c4v2szgHavoKkSxWxSfWsWtj2GSSMFzhNYY Validator | 4 validators 3 peers ⬇ 6.6kiB/s ⬆ 6.6kiB/s 2.00 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:47:27.138  INFO unc_network::peer_manager::peer_manager_actor: Bandwidth stats total_bandwidth_used_by_all_peers=424138 total_msg_received_count=586 max_max_record_num_messages_in_progress=2 +Apr 04 23:47:27.344  INFO stats: # 6405 8BVsYfqVqWct5amQQHH5Tb2EroiX3jusJTjy6JMmJWCm Validator | 4 validators 3 peers ⬇ 6.9kiB/s ⬆ 6.6kiB/s 1.90 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:47:37.348  INFO stats: # 6423 9e5VxuGtHm8zJFRNXC7w4gSCZjzjnpaxzEpWisFU5cLy Validator | 4 validators 3 peers ⬇ 7.0kiB/s ⬆ 6.8kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:47:47.353  INFO stats: # 6441 2bTvGG9gdD3fEqKfQsGhsMiwA7BC2QS1jbFwpbTbEiKq Validator | 4 validators 3 peers ⬇ 7.1kiB/s ⬆ 6.7kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:47:57.360  INFO stats: # 6459 2oGRPZ8K69SVTNJBDUqvuAUnyGAE8dordEF2P1NRaF4U Validator | 4 validators 3 peers ⬇ 7.0kiB/s ⬆ 6.7kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:48:07.364  INFO stats: # 6478 55Ma98AuuuQ5YrYY4Y71Q8RuBZTV5pG4FcACyEY3qkLj Validator | 4 validators 3 peers ⬇ 6.8kiB/s ⬆ 6.8kiB/s 1.90 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:48:17.367  INFO stats: # 6495 8GTzvtLRNXrqDTHNJDXVLAjggYLYSeBpznrrtJ5WnHBC Validator | 4 validators 3 peers ⬇ 6.9kiB/s ⬆ 6.6kiB/s 1.70 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:48:27.141  INFO unc_network::peer_manager::peer_manager_actor: Bandwidth stats total_bandwidth_used_by_all_peers=397017 total_msg_received_count=566 max_max_record_num_messages_in_progress=2 +Apr 04 23:48:27.372  INFO stats: # 6510 57ZR25pr8ZEPknqZfPiGiAm5pJiZuX1uDYwWTiXmbzr8 Validator | 4 validators 3 peers ⬇ 6.4kiB/s ⬆ 6.6kiB/s 1.50 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:48:37.376  INFO stats: # 6529 3dpDRN8k9xQLUtxmYRj5GnCFgyivGmtdepqCoC1ZGByC Validator | 4 validators 3 peers ⬇ 6.4kiB/s ⬆ 6.7kiB/s 1.90 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:48:47.380  INFO stats: # 6547 FteTJke4eJaK5ycspRJdJUrmvPsyTGaGKTABcyeKerkJ Validator | 4 validators 3 peers ⬇ 6.1kiB/s ⬆ 6.7kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:48:57.386  INFO stats: # 6565 GhgvVRswgqvvDkz9ecFWaykz2qqKKcWuigG6uytXgWA Validator | 4 validators 3 peers ⬇ 6.0kiB/s ⬆ 6.8kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:49:07.390  INFO stats: # 6584 33x91CxT8DPZd8VR9SD7jy2o3Md1oqxXk3P6xUiPp6MJ Validator | 4 validators 3 peers ⬇ 6.2kiB/s ⬆ 6.8kiB/s 1.90 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:49:17.395  INFO stats: # 6604 BtHwd4NHcdWZr1rWVE5A7MY1wbfGCnvtPoYCFfv9CfBw Validator | 4 validators 3 peers ⬇ 6.4kiB/s ⬆ 7.0kiB/s 2.00 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:49:27.141  INFO unc_network::peer_manager::peer_manager_actor: Bandwidth stats total_bandwidth_used_by_all_peers=413195 total_msg_received_count=598 max_max_record_num_messages_in_progress=2 +Apr 04 23:49:27.399  INFO stats: # 6622 B7U4TH7uXgLQ12UQePF4mBKUe2PqKDbwoY3nCepVRD1Y Validator | 4 validators 3 peers ⬇ 6.7kiB/s ⬆ 7.0kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:49:37.404  INFO stats: # 6639 7mNY36peGAYKvabQYNyMtmFY8nv2dcdgBjsWUYxhUPQ Validator | 4 validators 3 peers ⬇ 6.6kiB/s ⬆ 6.7kiB/s 1.70 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:49:47.408  INFO stats: # 6656 F7q7tQWJDoJdyZTSXraAiYW8yjdU2W5ibpCxEf8u8kDL Validator | 4 validators 3 peers ⬇ 6.7kiB/s ⬆ 6.7kiB/s 1.70 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:49:57.412  INFO stats: # 6673 9hwNAQVRzPXbKSoe8Jy7tfZQkpfdeb1CP7irk7zPMpnK Validator | 4 validators 3 peers ⬇ 6.9kiB/s ⬆ 6.6kiB/s 1.70 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:50:07.418  INFO stats: # 6692 8E1fuRHWbHjb5bTpueX12Ey2wNpGpCP8Ltyez3NwCbwT Validator | 4 validators 3 peers ⬇ 6.8kiB/s ⬆ 6.4kiB/s 1.90 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:50:17.423  INFO stats: # 6708 XdYig8V8MF783EPAum3ZAkFVRxBzNLuYju2g5fccBbt Validator | 4 validators 3 peers ⬇ 6.4kiB/s ⬆ 6.4kiB/s 1.60 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:50:27.143  INFO unc_network::peer_manager::peer_manager_actor: Bandwidth stats total_bandwidth_used_by_all_peers=387500 total_msg_received_count=539 max_max_record_num_messages_in_progress=2 +Apr 04 23:50:27.427  INFO stats: # 6724 9EoR9UxLbzbtKXQBbuUCJhQZrEd9hnMngBLWHD9XCVzw Validator | 4 validators 3 peers ⬇ 6.3kiB/s ⬆ 6.3kiB/s 1.60 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:50:37.433  INFO stats: # 6741 DoxwcpEKr9MXkJ6C7GW8aBw2TzqnAtxCEQAPVnRdfsHX Validator | 4 validators 3 peers ⬇ 6.5kiB/s ⬆ 6.3kiB/s 1.70 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:50:47.438  INFO stats: # 6759 4u42n6QXgmTsqceGa9HgxK4HKnVmht8Z8ShxYHzeQwkP Validator | 4 validators 3 peers ⬇ 6.7kiB/s ⬆ 6.2kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:50:57.442  INFO stats: # 6777 HQX7TaDsk3HHpstLvurteoPhQRnha7shGGAwxfBJLM7U Validator | 4 validators 3 peers ⬇ 6.7kiB/s ⬆ 6.1kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:51:07.448  INFO stats: # 6795 4R7oXdZrKpShc4GJsqT8UadFtHWirfJ2WHgkgPMzo5c7 Validator | 4 validators 3 peers ⬇ 6.6kiB/s ⬆ 6.1kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:51:17.452  INFO stats: # 6813 BrcA4GUrbLzT1Aa2NY3kneMWfEsuqjX9xmN2zQ92mdad Validator | 4 validators 3 peers ⬇ 6.9kiB/s ⬆ 6.1kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:51:27.143  INFO unc_network::peer_manager::peer_manager_actor: Bandwidth stats total_bandwidth_used_by_all_peers=433681 total_msg_received_count=573 max_max_record_num_messages_in_progress=2 +Apr 04 23:51:27.455  INFO stats: # 6829 5teJdAzFzG48eSftcrkm5rtxrnMEP1rVjBTEM35317nB Validator | 4 validators 3 peers ⬇ 7.0kiB/s ⬆ 6.0kiB/s 1.60 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:51:37.460  INFO stats: # 6846 pxkuRM3Gqk7ADJwFBMYkMRFh8PrredMxQDZn5ybcVSr Validator | 4 validators 3 peers ⬇ 6.7kiB/s ⬆ 6.2kiB/s 1.70 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:51:47.465  INFO stats: # 6864 2q8NAvV35wRgqBNUUAHjagGK94wSor1pZ5V4EbNeAj12 Validator | 4 validators 3 peers ⬇ 6.7kiB/s ⬆ 6.2kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:51:57.471  INFO stats: # 6881 EE8B45xMR336sg6HwtToLkzQ9aYrTyucjnCi1J1JDj45 Validator | 4 validators 3 peers ⬇ 6.7kiB/s ⬆ 6.3kiB/s 1.70 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:52:07.478  INFO stats: # 6897 HMtTwLEYEJj1YE7WgD5ee8c3Yeydukpzv4DBKunxCUEo Validator | 4 validators 3 peers ⬇ 6.5kiB/s ⬆ 6.2kiB/s 1.60 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:52:17.480  INFO stats: # 6916 BEZqv6AjJ9STQ9rNqZg8fZwxAvEMhsceSob7piL4fZEw Validator | 4 validators 3 peers ⬇ 6.6kiB/s ⬆ 6.3kiB/s 1.90 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:52:27.144  INFO unc_network::peer_manager::peer_manager_actor: Bandwidth stats total_bandwidth_used_by_all_peers=405669 total_msg_received_count=558 max_max_record_num_messages_in_progress=2 +Apr 04 23:52:27.484  INFO stats: # 6937 AtMFW43mkGMfSeGDRoNT5s6gzAycev3QnJrH6HyTUrxJ Validator | 4 validators 3 peers ⬇ 6.6kiB/s ⬆ 6.8kiB/s 2.10 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:52:37.490  INFO stats: # 6955 8UeRz4rTxRJq9rLqFxTgQvTPFU7HwszpZY5uKa6H42R3 Validator | 4 validators 3 peers ⬇ 6.7kiB/s ⬆ 6.9kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:52:47.492  INFO stats: # 6974 CVeptUKV5ir5aG28bjh49USDuKy3G6gabQFMp9cvX1eN Validator | 4 validators 3 peers ⬇ 6.7kiB/s ⬆ 6.8kiB/s 1.90 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:52:57.497  INFO stats: # 6992 35w6Gp4mLSjcPHXmj1FuuWQKNedjibbppebsbRxADUuY Validator | 4 validators 3 peers ⬇ 6.7kiB/s ⬆ 7.1kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:53:07.502  INFO stats: # 7010 3aCLLVKYZCqcUitqPEcxJd6dCvA9qaUbMGfq59ZsQcZg Validator | 4 validators 3 peers ⬇ 6.9kiB/s ⬆ 7.2kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:53:17.509  INFO stats: # 7028 HWwwpFAjCaKhW7QPx2CK6EUJ8GE2j67tKiSAqFgfXuuZ Validator | 4 validators 3 peers ⬇ 7.0kiB/s ⬆ 7.0kiB/s 1.80 bps 0 gas/s CPU: 0%, Mem: 0 B +Apr 04 23:53:27.147  INFO unc_network::peer_manager::peer_manager_actor: Bandwidth stats total_bandwidth_used_by_all_peers=419823 total_msg_received_count=582 max_max_record_num_messages_in_progress=2 diff --git a/debug_scripts/tests/send_validator_logs_test.py b/debug_scripts/tests/send_validator_logs_test.py new file mode 100644 index 000000000..ce7679d39 --- /dev/null +++ b/debug_scripts/tests/send_validator_logs_test.py @@ -0,0 +1,24 @@ +import unittest +import datetime +import io +import sys +from send_validator_logs import filter_log_file + + +class test_validator_log_filtering(unittest.TestCase): + + def test_time_filtering(self): + start_time = datetime.datetime(2022, 4, 4, 23, 42, 0, 0) + end_time = datetime.datetime(2022, 4, 4, 23, 49, 0, 0) + output_file_obj = filter_log_file("./tests/data/node0.logs", + start_time=start_time, + end_time=end_time) + self.assertIsInstance( + output_file_obj, list, + "Parsed file object should be of type io.BytesIO") + self.assertEqual(len(output_file_obj), 48, + "Filtered log should have 48 lines") + + +if __name__ == '__main__': + unittest.main() diff --git a/deny.toml b/deny.toml new file mode 100644 index 000000000..2ff3f6fdd --- /dev/null +++ b/deny.toml @@ -0,0 +1,162 @@ +targets = [ + { triple = "x86_64-unknown-linux-musl" }, + { triple = "x86_64-pc-windows-msvc" }, + { triple = "x86_64-apple-darwin" }, +] + +[licenses] +unlicensed = "deny" +allow = ["MIT", "Apache-2.0", "Apache-2.0 WITH LLVM-exception"] +deny = ["AGPL-1.0", "AGPL-3.0"] +copyleft = "warn" +allow-osi-fsf-free = "either" +default = "deny" +confidence-threshold = 0.8 +exceptions = [] +private = { ignore = false, registries = [] } + +[sources] +unknown-registry = "deny" +unknown-git = "deny" +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +allow-git = [] + +[bans] +multiple-versions = "deny" +deny = [ + # See: https://github.com/rust-random/rand/issues/645 + { name = "rand", version = "<0.6" }, + + # See: https://github.com/utnet-org/utility/pull/3595 + { name = "keccak-hash", version = "<0.4.1" }, + { name = "primitive-types", version = "<0.10.1" }, + { name = "uint", version = "<0.8.2" }, + + # https://github.com/utnet-org/utility/pull/8562 + { name = "borsh", version = "0.10, <0.10.2" }, +] + +skip = [ + { name = "tracing-log", version = "0.1.3" }, + # wasmer 0.17 and wasmtime 0.17 uses older versions of some crates + { name = "generic-array", version = "=0.12.4" }, + + # `sha2` uses it + { name = "cfg-if", version = "=1.0.0" }, + + # `sha2`, `blake3` and `curve25519-dalek` need to be updated. + { name = "digest", version = "=0.9.0" }, + + # Need this specific version of pwasm-utils for backwards-compatible + # stack limiting. + { name = "pwasm-utils", version = "=0.12.0" }, + { name = "parity-wasm", version = "=0.41.0" }, + # old version used by wasmer et al + { name = "dynasm", version = "=1.2.3" }, + { name = "dynasmrt", version = "=1.2.3" }, + + # wasmer and wasmtime + { name = "target-lexicon", version = "=0.10.0" }, + { name = "wasmparser", version = "=0.51.4" }, + { name = "wasmparser", version = "=0.78.2" }, + { name = "wasmparser", version = "=0.84.0" }, + { name = "wasmparser", version = "=0.99.0" }, + { name = "wasmparser", version = "=0.105.0" }, + { name = "wasm-encoder", version = "=0.27.0" }, + { name = "object", version = "=0.30.4" }, + { name = "memoffset", version = "=0.6.5" }, + { name = "memoffset", version = "=0.8.0" }, + { name = "linux-raw-sys", version = "=0.3.8" }, + { name = "addr2line", version = "=0.19.0" }, + { name = "gimli", version = "=0.27.2" }, + + # wasmer 0.17.x + { name = "parking_lot", version = "=0.10.2" }, + { name = "parking_lot_core", version = "=0.7.2" }, + { name = "lock_api", version = "=0.3.4" }, + { name = "digest", version = "=0.8.1" }, + + # wasmer 0.18 + { name = "nix", version = "=0.15.0" }, + + # many crates haven't updated to syn 2.0 yet. + { name = "syn", version = "=1.0.103" }, + + # old version of tokio, parking_lot + { name = "windows-sys", version = "=0.36.1" }, + # console + { name = "windows-sys", version = "=0.42.0" }, + { name = "windows_x86_64_msvc", version = "=0.36.1" }, + + # old version of rustix, wasmtime, is-terminal, etc. + { name = "rustix", version = "0.36.6" }, + { name = "rustix", version = "0.37.20" }, + { name = "linux-raw-sys", version = "0.1.4" }, + { name = "windows-sys", version = "=0.42.0" }, + { name = "windows-sys", version = "=0.45.0" }, + { name = "windows_x86_64_msvc", version = "=0.42.2" }, + { name = "windows-targets", version = "=0.42.1" }, + + # ed25519-dalek uses older versions of rand and rand_core + { name = "rand", version = "=0.7.3" }, + { name = "rand_core", version = "=0.5.1" }, + { name = "rand_chacha", version = "=0.2.2" }, + { name = "getrandom", version = "=0.1.16" }, + + # criterion and criterion-plot use conflicting versions + { name = "semver-parser", version = "=0.7.0" }, + { name = "semver", version = "=0.9.0" }, + + # wasmer-runtime-core-near and parity-secp256k1 use an older version + { name = "arrayvec", version = "=0.5.2" }, + + # borsh uses a very old version of proc-macro-crate + { name = "proc-macro-crate", version = "=0.1.5" }, + + # criterion and wasmer-runtime-core-near depend on this older + # version of the crate. + { name = "rustc_version", version = "=0.2.3" }, + { name = "errno", version = "=0.2.8" }, + + # paperclip-macros, strum_macros, walrus-macro depend on this while clap3.1.6 uses heck=0.4.0 + { name = "heck", version = "=0.3.3" }, + + # Bolero requires a newer version and the rest of the ecosystem hasn't caught up yet. + { name = "hashbrown", version = "0.11.0" }, + { name = "hashbrown", version = "0.12.0" }, + { name = "hashbrown", version = "0.13.2" }, + { name = "bitflags", version = "=1.3.2" }, + { name = "indexmap", version = "=1.9.2" }, + + # prometheus depends on an old version of protobuf + { name = "protobuf", version = "=2.27.1" }, + + # opentelemetry-otlp depends on an old version of tonic which depends on an old version of tokio-util + { name = "tokio-util", version = "=0.6.10" }, + + # rust-s3 is using an old version of smartstring + { name = "smartstring", version = "=0.2.10" }, + + # validator 0.12 ~ 0.16 is still using an old version of idna + { name = "idna", version = "=0.2.3" }, + + # zeropool-bn uses borsh 0.9 + { name = "borsh", version = "=0.9.3" }, + { name = "borsh-derive", version = "=0.9.3" }, + { name = "borsh-derive-internal", version = "=0.9.3" }, + { name = "borsh-schema-derive-internal", version = "=0.9.3" }, + + # unc-test-contracts + { name = "wasm-encoder", version = "=0.11.0" }, + + # Various packages haven’t upgraded to 0.base64 21 yet. + { name = "base64", version = "=0.13.0" }, + { name = "ahash", version = "=0.7.7" }, + + # actix-http hasn't upgraded iminz_oxide/flate2 yet. + { name = "miniz_oxide", version = "=0.5.1" }, + + # cloud-storage + { name = "base64", version = "=0.12.3" }, + { name = "num-bigint", version = "=0.2.6" }, +] diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..0e8155eda --- /dev/null +++ b/docs/README.md @@ -0,0 +1,30 @@ +# Introduction + +Welcome to the framework development guide! + +The target audience of this guide are developers of framework itself. If you are +a user of NEAR (either a contract developer, or validator running a node), +please refer to the user docs at . + +This guide is built with [mdBook](https://rust-lang.github.io/mdBook/) +from sources in the [framework repository](https://github.com/utnet-org/utility/). +You can edit it by pressing the "edit" icon in the top right corner, we welcome +all contributions. The guide is hosted at . + +The guide is organized as a collection of loosely coupled chapters -- you don't +need to read them in order, feel free to peruse the TOC, and focus on +the interesting bits. The chapters are classified into three parts: + +* [**Architecture**](./architecture/) talks about how the code works. + So, for example, if you are interested in how a transaction flows through the + system, look there! +* [**Practices**](./practices/) describe, broadly, how we write code. + For example, if you want to learn about code style, issue tracking, or + debugging performance problems, this is the chapter for you. +* Finally, the [**Misc**](./misc/) part holds various assorted bits + and pieces. We are trying to bias ourselves towards writing more docs, so, if + you want to document something and it doesn't cleanly map to a category above, + just put it in misc! + +If you are unsure, start with [Architecture Overview](./architecture/) and then +read [Run a Node](./practices/workflows/run_a_node.md) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md new file mode 100644 index 000000000..a8c77b409 --- /dev/null +++ b/docs/SUMMARY.md @@ -0,0 +1,65 @@ +# Summary + +[Introduction](README.md) + +# Architecture + +- [Overview](./architecture/README.md) +- [How uncd works](./architecture/how/README.md) + - [How Sync Works](./architecture/how/sync.md) + - [Garbage Collection](./architecture/how/gc.md) + - [How Epoch Works](./architecture/how/epoch.md) + - [Transaction Routing](./architecture/how/tx_routing.md) + - [Transactions And Receipts](./architecture/how/tx_receipts.md) + - [Cross shard transactions - deep dive](./architecture/how/cross-shard.md) + - [Gas](./architecture/how/gas.md) + - [Meta transactions](./architecture/how/meta-tx.md) + - [Serialization: Borsh, Json, ProtoBuf](./architecture/how/serialization.md) + - [Proofs](./architecture/how/proofs.md) + - [Resharding](./architecture/how/resharding.md) +- [How uncd will work](./architecture/next/README.md) + - [Catchup and state sync improvements](./architecture/next/catchup_and_state_sync.md) + - [Malicious producers and phase 2](./architecture/next/malicious_chunk_producer_and_phase2.md) +- [Storage](./architecture/storage.md) + - [Storage Request Flow](./architecture/storage/flow.md) + - [Trie](./architecture/storage/trie.md) + - [Database Format](./architecture/storage/database.md) + - [Flat Storage](./architecture/storage/flat_storage.md) +- [Network](./architecture/network.md) +- [Gas Cost Parameters](./architecture/gas/README.md) + - [Parameter Definitions](./architecture/gas/parameter_definition.md) + - [Gas Profile](./architecture/gas/gas_profile.md) + - [Runtime Parameter Estimator](./architecture/gas/estimator.md) + +# Practices + +- [Overview](./practices/README.md) +- [Rust 🦀](./practices/rust.md) +- [Workflows](./practices/workflows/README.md) + - [Run a Node](./practices/workflows/run_a_node.md) + - [Deploy a Contract](./practices/workflows/deploy_a_contract.md) + - [Run Gas Estimations](./practices/workflows/gas_estimations.md) + - [Localnet on many machines](./practices/workflows/localnet_on_many_machines.md) + - [IO tracing](./practices/workflows/io_trace.md) +- [Code Style](./practices/style.md) +- [Documentation](./practices/docs.md) +- [Tracking Issues](./practices/tracking_issues.md) +- [Security Vulnerabilities](./practices/security_vulnerabilities.md) +- [Fast Builds](./practices/fast_builds.md) +- [Testing](./practices/testing/README.md) + - [Python Tests](./practices/testing/python_tests.md) + - [Testing Utils](./practices/testing/test_utils.md) +- [Protocol Upgrade](./practices/protocol_upgrade.md) + +# Advanced configuration + +- [Networking](./advanced_configuration/networking.md) + +# Custom test networks + +- [Starting a network from mainnet state](./test_networks/mainnet_spoon.md) + +# Misc + +- [Overview](./misc/README.md) +- [State Sync Dump](./misc/state_sync_dump.md) diff --git a/docs/advanced_configuration/networking.md b/docs/advanced_configuration/networking.md new file mode 100644 index 000000000..7986698e6 --- /dev/null +++ b/docs/advanced_configuration/networking.md @@ -0,0 +1,60 @@ +This document describes the advanced network options that you can configure +by modifying the "network" section of your "config.json" file: + + +``` +{ + // ... + "network": { + // ... + "public_addrs": [], + "allow_private_ip_in_public_addrs": false, + "experimental": { + "inbound_disabled": false, + "connect_only_to_boot_nodes": false, + "skip_sending_tombstones_seconds": 0, + "tier1_enable_inbound": true, + "tier1_enable_outbound": false, + "tier1_connect_interval": { + "secs": 60, + "nanos": 0 + }, + "tier1_new_connections_per_attempt": 50 + } + }, + // ... +} +``` + +### TIER1 network + +Participants of the BFT consensus (block & chunk producers) now can establish +direct (aka TIER1) connections between each other, which will optimize the +communication latency and minimize the number of dropped chunks. If you are a +validator, you can enable TIER1 connections by setting the following fields in the config: + +* [public_addrs](https://github.com/utnet-org/utility/blob/d95a5f58d998c69cb8d4e965ad6b0a440cf3f233/chain/network/src/config_json.rs#L154) + * this is a list of the public addresses (in the format `"@:"`) + of trusted nodes, which are willing to route messages to your node + * this list will be broadcasted to the network so that other validator nodes can connect + to your node. + * if your node has a static public IP, set `public_addrs` to a list with a single entry + with the public key and address of your node, for example: + `"public_addrs": ["ed25519:86EtEy7epneKyrcJwSWP7zsisTkfDRH5CFVszt4qiQYw@31.192.22.209:24567"]`. + * if your node doesn't have a public IP (for example, it is hidden behind a NAT), set + `public_addrs` to a list (<=10 entries) of proxy nodes that you trust (arbitrary nodes + with static public IPs). + * support for nodes with dynamic public IPs is not implemented yet. +* [experimental.tier1_enable_outbound](https://github.com/utnet-org/utility/blob/d95a5f58d998c69cb8d4e965ad6b0a440cf3f233/chain/network/src/config_json.rs#L213) + * makes your node actively try to establish outbound TIER1 connections (recommended) + once it learns about the public addresses of other validator nodes. If disabled, your + node won't try to establish outbound TIER1 connections, but it still may accept + incoming TIER1 connections from other nodes. + * currently `false` by default, but will be changed to `true` by default in the future +* [experimental.tier1_enable_inbound](https://github.com/utnet-org/utility/blob/d95a5f58d998c69cb8d4e965ad6b0a440cf3f233/chain/network/src/config_json.rs#L209) + * makes your node accept inbound TIER1 connections from other validator nodes. + * disable both `tier1_enable_inbound` and `tier1_enable_outbound` if you want to opt-out + from the TIER1 communication entirely + * disable `tier1_enable_inbound` if you are not a validator AND you don't want your + node to act as a proxy for validators. + * `true` by default diff --git a/docs/architecture/README.md b/docs/architecture/README.md new file mode 100644 index 000000000..e9eb424db --- /dev/null +++ b/docs/architecture/README.md @@ -0,0 +1,302 @@ +# Overview + +This document describes the high-level architecture of framework. The focus here +is on the implementation of the blockchain protocol, not the protocol itself. +For reference documentation of the protocol, please refer to +[nomicon](https://nomicon.io/) + +Some parts of our architecture are also covered in this [video series on YouTube](https://www.youtube.com/playlist?list=PL9tzQn_TEuFV4qlts0tVgndnytFs4QSYo). + +## Bird's Eye View + +If we put the entirety of framework onto one picture, we get something like this: + +![](../images/architecture.svg) + +Don't worry if this doesn't yet make a lot of sense: hopefully, by the end of +this document the above picture would become much clearer! + +## Overall Operation + +`framework` is a blockchain node -- it's a single binary (`uncd`) which runs on +some machine and talks to other similar binaries running elsewhere. Together, +the nodes agree (using a distributed consensus algorithm) on a particular +sequence of transactions. Once transaction sequence is established, each node +applies transactions to the current state. Because transactions are fully +deterministic, each node in the network ends up with identical state. To allow +greater scalability, NEAR protocol uses sharding, which allows a node to hold +only a small subset (shard) of the whole state. + +`uncd` is a stateful, restartable process. When `uncd` starts, the node +connects to the network and starts processing blocks (block is a batch of +transactions, processed together; transactions are batched into blocks for +greater efficiency). The results of processing are persisted in the database. +RocksDB is used for storage. Usually, the node's data is found in the `~/.near` +directory. The node can be stopped at any moment and be restarted later. While +the node is offline it misses the block, so, after a restart, the sync process +kicks in which brings the node up-to-speed with the network by downloading the +missing bits of history from more up-to-date peer nodes. + +Major components of framework: + +* **JSON RPC**. This HTTP RPC interface is how `uncd` communicates with + non-blockchain outside world. For example, to submit a transaction, some + client sends an RPC request with it to some node in the network. From that + node, the transaction propagates through the network, until it is included in + some block. Similarly, a client can send an HTTP request to a node to learn + about current state of the blockchain. The **JSON RPC** interface is documented + [here](https://docs.near.org/api/rpc/introduction). + +* **Network**. If RPC is aimed "outside" the blockchain, "network" is how peer + `uncd` nodes communicate with each other within the blockchain. RPC carries + requests from users of the blockchain, while network carries various messages + needed to implement consensus. Two directly connected nodes communicate by + sending protobuf-encoded messages over TCP. A node also includes logic to + route messages for indirect peers through intermediaries. Oversimplifying a + lot, it's enough for a new node to know an IP address of just one other + network participant. From this bootstrap connection, the node learns how to + communicate with any other node in the network. + +* **Client**. Somewhat confusingly named, **client** is the logical state of the + blockchain. After receiving and decoding a request, both **RPC** and **network** + usually forward it in the parsed form to the **client**. Internally, **client** is + split in two somewhat independent components: **chain** and **runtime**. + +* **Chain**. The job of **chain**, in a nutshell, is to determine a global order of + transactions. **Chain** builds and maintains the blockchain data structure. This + includes block and chunk production and processing, consensus, and validator + selection. However, **chain** is not responsible for actually applying + transactions and receipts. + +* **Runtime**. If **chain** selects the _order_ of transactions, **Runtime** applies + transaction to the state. **Chain** guarantees that everyone agrees on the order + and content of transactions, and **Runtime** guarantees that each transaction is + fully deterministic. It follows that everyone agrees on the "current state" of + the blockchain. Some transactions are as simple as "transfer X tokens from + Alice to Bob". But a much more powerful class of transactions is supported: + "run this arbitrary WebAssembly code in the context of the current state of + the chain". Running such "smart contract" transactions securely and + efficiently is a major part of what **Runtime** does. Today, **Runtime** uses a JIT + compiler to do that. + +* **Storage**. **Storage** is more of a cross-cutting concern, than an isolated + component. Many parts of a node want to durably persist various bits of state + to disk. One notable case is the logical state of the blockchain, and, in + particular, data associated with each account. Logically, the state of an account + on a chain is a key-value map: `HashMap, Vec>`. But there is a + twist: it should be possible to provide a succinct proof that a particular key + indeed holds a particular value. To allow that internally the state is + implemented as a persistent (in both senses, "functional" and "on disk") + merkle-patricia trie. + +* **Parameter Estimator**. One kind of transaction we support is "run this + arbitrary, Turing-complete computation". To protect from a `loop {}` + transaction halting the whole network, **Runtime** implements resource limiting: + each transaction runs with a certain finite amount of "gas", and each + operation costs a certain amount of gas to perform. **Parameter estimator** is + essentially a set of benchmarks used to estimate relative gas costs of + various operations. + +## Entry Points + +`uncd/src/main.rs` contains the main function that starts a blockchain node. +However, this file mostly only contains the logic to parse arguments and +dispatch different commands. `start_with_config` in `framework/src/lib.rs` is the +actual entry point and it starts all the actors. + +`JsonRpcHandler::process` in the `jsonrpc` crate is the RPC entry point. It +implements the public API of a node, which is documented +[here](https://docs.near.org/api/rpc/introduction). + +`PeerManagerActor::spawn` in the `network` is an entry for the other point of +contract with the outside world -- the peer-to-peer network. + +`Runtime::apply` in the `runtime` crate is the entry point for transaction +processing logic. This is where state transitions actually happen, after chain +decided, according to distributed consensus, which transitions need to +happen. + +## Code Map + +This section contains some high-level overview of important crates and data +structures. + +### `core/primitives` + +This crate contains most of the types that are shared across different crates. + +### `core/primitives-core` + +This crate contains types needed for runtime. + +### `core/store/trie` + +This directory contains the MPT state implementation. Note that we usually use +`TrieUpdate` to interact with the state. + +### `chain/chain` + +This crate contains most of the chain logic (consensus, block processing, etc). +`ChainUpdate::process_block` is where most of the block processing logic +happens. + +**State update** + +The blockchain state of a node can be changed in the following two ways: + +* Applying a chunk. This is how the state is normally updated: through + `Runtime::apply`. +* State sync. State sync can happen in two cases: + * A node is far enough behind the most recent block and triggers state sync to + fast forward to the state of a very recent block without having to apply + blocks in the middle. + * A node is about to become validator for some shard in the next epoch, but it + does not yet have the state for that shard. In this case, it would run state + sync through the `catchup` routine. + +### `chain/chunks` + +This crate contains most of the sharding logic which includes chunk creation, +distribution, and processing. `ShardsManager` is the main struct that +orchestrates everything here. + +### `chain/client` + +This crate defines two important structs, `Client` and `ViewClient`. `Client` +includes everything necessary for the chain (without network and runtime) to +function and runs in a single thread. `ViewClient` is a "read-only" client that +answers queries without interfering with the operations of `Client`. +`ViewClient` runs in multiple threads. + +### `chain/network` + +This crate contains the entire implementation of the p2p network used by NEAR +blockchain nodes. + +Two important structs here: `PeerManagerActor` and `Peer`. Peer manager +orchestrates all the communications from network to other components and from +other components to network. `Peer` is responsible for low-level network +communications from and to a given peer (more details in +[this article](./network.md#23-peeractor)). Peer manager runs in one thread while each +`Peer` runs in its own thread. + +**Architecture Invariant:** Network communicates to `Client` through +`NetworkClientMessages` and to `ViewClient` through `NetworkViewClientMessages`. +Conversely, `Client` and `ViewClient` communicates to network through +`NetworkRequests`. + +### `chain/epoch_manager` + +This crate is responsible for determining validators and other epoch related +information such as epoch id for each epoch. + +**Note:** `EpochManager` is constructed in `NightshadeRuntime` rather than in +`Chain`, partially because we had this idea of making epoch manager a smart +contract. + +### `chain/jsonrpc` + +This crate implements [JSON-RPC](https://www.jsonrpc.org/) API server to enable +submission of new transactions and inspection of the blockchain data, the +network state, and the node status. When a request is processed, it generates a +message to either `ClientActor` or `ViewClientActor` to interact with the +blockchain. For queries of blockchain data, such as block, chunk, account, etc, +the request usually generates a message to `ViewClientActor`. Transactions, on +the other hand, are sent to `ClientActor` for further processing. + +### `runtime/runtime` + +This crate contains the main entry point to runtime -- `Runtime::apply`. This +function takes `ApplyState`, which contains necessary information passed from +chain to runtime, a list of `SignedTransaction` and a list of `Receipt`, and +returns a `ApplyResult`, which includes state changes, execution outcomes, etc. + +**Architecture Invariant:** The state update is only finalized at the end of +`apply`. During all intermediate steps state changes can be reverted. + +### `runtime/unc-vm-logic` + +`VMLogic` contains all the implementations of host functions and is the +interface between runtime and wasm. `VMLogic` is constructed when runtime +applies function call actions. In `VMLogic`, interaction with NEAR blockchain +happens in the following two ways: + +* `VMContext`, which contains lightweight information such as current block + hash, current block height, epoch id, etc. +* `External`, which is a trait that contains functions to interact with + blockchain by either reading some nontrivial data, or writing to the + blockchain. + +### `runtime/unc-vm-runner` + +`run` function in `runner.rs` is the entry point to the vm runner. This function +essentially spins up the vm and executes some function in a contract. It +supports different wasm compilers including wasmer0, wasmer2, and wasmtime +through compile-time feature flags. Currently we use wasmer0 and wasmer2 in +production. The `imports` module exposes host functions defined in +`unc-vm-logic` to WASM code. In other words, it defines the ABI of the +contracts on NEAR. + +### `uncd` + +As mentioned before, `uncd` is the crate that contains that main entry points. +All the actors are spawned in `start_with_config`. It is also worth noting that +`NightshadeRuntime` is the struct that implements `RuntimeAdapter`. + + +### `core/store/src/db.rs` + +This file contains the schema (DBCol) of our internal RocksDB storage - a good +starting point when reading the code base. + +## Cross Cutting Concerns + +### Observability + +The [tracing](https://tracing.rs) crate is used for structured, hierarchical +event output and logging. We also integrate [Prometheus](https://prometheus.io) +for light-weight metric output. See the [style](../practices/style.md) documentation for +more information on the usage. + +### Testing + +Rust has built-in support for writing unit tests by marking functions +with the `#[test]` directive. Take full advantage of that! Testing not +only confirms that what was written works the way it was intended to but +also helps during refactoring since it catches unintended behaviour +changes. + +Not all tests are created equal though and while some may only need +milliseconds to run, others may run for several seconds or even +minutes. Tests that take a long time should be marked as such with an +`expensive_tests` feature, for example: + +```rust +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_catchup_random_single_part_sync() { + test_catchup_random_single_part_sync_common(false, false, 13) +} +``` + +Such tests will be ignored by default and can be executed by using +`--ignored` or `--include-ignored` flag as in `cargo test -- +--ignored` or by compiling the tests with `expensive_tests` feature +enabled. + +Because expensive tests are not run by default, they are also not run +in CI. Instead, they are run nightly and need to be explicitly +included in `nightly/expensive.txt` file; for example: + +```text +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_random_single_part_sync +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_random_single_part_sync --features nightly +``` + +For more details regarding nightly tests see `nightly/README.md`. + +Note that what counts as a slow test isn’t exactly defined as of now. +If it takes just a couple seconds then it’s probably fine. Anything +slower should probably be classified as an expensive test. In +particular, if libtest complains the test takes more than 60 seconds +then it definitely is and expensive test. diff --git a/docs/architecture/gas/README.md b/docs/architecture/gas/README.md new file mode 100644 index 000000000..bd4227826 --- /dev/null +++ b/docs/architecture/gas/README.md @@ -0,0 +1,215 @@ +# Gas Cost Parameters + +Gas in NEAR Protocol solves two problems. + +1. To avoid spam, validator nodes only perform work if a user's tokens are + burned. Tokens are automatically converted to gas using the current gas + price. +2. To synchronize shards, they must all produce chunks following a strict + schedule of 1 second execution time. Gas is used to measure how heavy the + workload of a transaction is, so that the number of transactions that fit in + a block can be deterministically computed by all nodes. + +In other words, each transaction costs a fixed amount of gas. This gas cost +determines how much a user has to pay and how much time framework has to execute +the transaction. + +What happens if framework executes a transaction too slowly? Chunk production for +the shard gets delayed, which delays block production for the entire blockchain, +increasing latency and reducing throughput for everybody. If the chunk is really +late, the block producer will decide to not include the chunk at all and inserts +an empty chunk. The chunk may be included in the next block. + +By now, you probably wonder how we can know the time it takes to execute a +transaction, given that validators use hardware of their choice. Getting these +timings right is indeed a difficult problem. Or flipping the problem, assuming +the timings are already known, then we must implement framework such that it +guarantees to operate within the given time constraints. How we tackle this is +the topic of this chapter. + +If you want to learn more about Gas from a user perspective, +[Gas basic concepts](https://docs.near.org/concepts/basics/transactions/gas), +[Gas advanced concepts](https://docs.near.org/concepts/basics/transactions/gas-advanced), +and [the runtime fee specification](https://nomicon.io/RuntimeSpec/Fees/) are +good places to dig deeper. + +## Hardware and Timing Assumptions + +For timing to make sense at all, we must first define hardware constraints. The +official hardware requirements for a validator are published on +[unc-nodes.io/validator/hardware](https://unc-nodes.io/validator/hardware). They +may change over time but the main principle is that a moderately configured, +cloud-hosted virtual machine suffices. + +For our gas computation, we assume the minimum required hardware. Then we define +1015 gas to be executed in at most 1s. We commonly use 1 Tgas (= +1012 gas) in conversation, which corresponds to 1ms execution time. + +Obviously, this definition means that a validator running more powerful hardware +will execute the transactions faster. That is perfectly okay, as far as the +protocol is concerned we just need to make sure the chunk is available in time. +If it is ready in even less time, no problem. + +Less obviously, this means that even a minimally configured validator is often +idle. Why is that? Well, the hardware must be prepared to execute chunks that +are always full. But that is rarely the case, as the gas price increases +exponentially when chunks are full, which would cause traffic to go back +eventually. + +Furthermore, the hardware has to be ready for transactions of all types, +including transactions chosen by a malicious actor selecting only the most +complex transactions. Those transactions can also be unbalanced in what +bottlenecks they hit. For example, a chunk can be filled with transactions that +fully utilize the CPU's floating point units. Or they could be using all the +available disk IO bandwidth. + +Because the minimum required hardware needs to meet the timing requirements for +any of those scenarios, the typical, more balanced case is usually computed +faster than the gas rule states. + +## Transaction Gas Cost Model + +A transaction is essentially just a list of actions to be executed on the same +account. For example it could be `CreateAccount` combined with +`FunctionCall("hello_world")`. + +The [reference for available actions](https://nomicon.io/RuntimeSpec/Actions) +shows the conclusive list of possible actions. The protocol defines fixed fees +for each of them. More details on [actions fees](#action-costs) follow below. + +Fixed fees are an important design decision. It means that a given action will +always cost the exact same amount of gas, no matter on what hardware it +executes. But the content of the action can impact the cost, for example a +`DeployContract` action's cost scales with the size of the contract code. + +So, to be more precise, the protocol defines fixed gas cost *parameters* for +each action, together with a formula to compute the gas cost for the action. All +actions today either use a single fixed gas cost or they use a base cost and a +linear scaling parameter. With one important exception, `FunctionCall`, which +shall be discussed [further below](#fn-call-costs). + +There is an entire section on [Parameter Definitions](./parameter_definition.md) +that explains how to find the source of truth for parameter values in the +framework repository, how they can be referenced in code, and what steps are +necessary to add a new parameter. + +Let us dwell a bit more on the linear scaling factors. The fact that contract +deployment cost, which includes code compilation, scales linearly limits the +compiler to use only algorithms of linear complexity. Either that, or the +parameters must be set to match the 1ms = 1Tgas rule at the largest possible +contract size. Today, we limit ourselves to linear-time algorithms in the +compiler. + +Likewise, an action that has no scaling parameters must only use constant time +to execute. Taking the `CreateAcccount` action as an example, with a cost of 0.1 +Tgas, it has to execute within 0.1ms. Technically, the execution time depends +ever so slightly on the account name length. But there is a fairly low upper +limit on that length and it makes sense to absorb all the cost in the constant +base cost. + +This concept of picking parameters according to algorithmic complexity is key. +If you understand this, you know how to think about gas as a framework developer. +This should be enough background to understand what the estimator does. + +The [runtime parameter estimator](./estimator.md) is a separate binary within +the framework repository. It contains benchmarking-like code used to validate +existing parameter values against the 1ms = 1 Tgas rule. When implementing new +features, code should be added there to estimate the safe values of the new +parameters. This section is for you if you are adding new features such as a new +pre-compiled method or other host functions. + +Next up are more details on the specific costs that occur when executing NEAR +transactions, which help to understand existing parameters and how they are +organized. + +## Action Costs + +Actions are executed in two steps. First, an action is verified and inserted to +an action receipt, which is sent to the receiver of the action. The `send` fee +is paid for this. It is charged either in `fn process_transaction(..)` if the +action is part of a fresh transaction, or inside +[logic.rs](https://github.com/utnet-org/utility/blob/14b8ae2c7465444c9b672a23b044c00be98f6e34/runtime/unc-vm-logic/src/logic.rs) +through `fn pay_action_base(..)` if the action is generated by a function call. +The send fee is meant to cover the cost to validate an action and transmit it +over the network. + +The second step is action execution. It is charged in `fn apply_action(..)`. +The execution cost has to cover everything required to apply the action to the +blockchain's state. + +These two steps are done on the same shard for local receipts. Local receipts +are defined as those where the sender account is also the receiver, abbreviated +as `sir` which stands for "sender is receiver". + +For remote receipts, which is any receipt where the sender and receiver accounts +are different, we charge a different fee since sending between shards is extra +work. Notably, we charge that extra work even if the accounts are on the same +shard. In terms of gas costs, each account is conceptually its own shard. This +makes dynamic resharding possible without user-observable impact. + +When the send step is performed, the minimum required gas to start execution of +that action is known. Thus, if the receipt has not enough gas, it can be aborted +instead of forwarding it. Here we have to introduce the concept of used gas. + +`gas_used` is different from `gas_burnt`. The former includes the gas that needs +to be reserved for the execution step whereas the latter only includes the gas +that has been burnt in the current chunk. The difference between the two is +sometimes also called prepaid gas, as this amount of gas is paid for during the +send step and it is available in the execution step for free. + +If execution fails, the prepaid cost that has not been burned will be refunded. +But this is not the reason why it must burn on the receiver shard instead of the +sender shard. The reason is that we want to properly compute the gas limits on +the chunk that does the execution work. + +In conclusion, each action parameter is split into three costs, `send_sir`, +`send_not_sir`, and `execution`. Local receipts charge the first and last +parameters, remote receipts charge the second and third. They should be +estimated, defined, and charged separately. But the reality is that today almost +all actions are estimated as a whole and the parameters are split 50/50 between +send and execution cost, without discrimination on local vs remote receipts +i.e. `send_sir` cost is the same as `send_not_sir`. + +The [Gas Profile](./gas_profile.md) section goes into more details on how gas +costs of a transaction are tracked in framework. + +## Dynamic Function Call Costs + + +Costs that occur while executing a function call on a deployed WASM app (a.k.a. +smart contract) are charged only at the receiver. Thus, they have only one value +to define them, in contrast to action costs. + +The most fundamental dynamic gas cost is `wasm_regular_op_cost`. It is +multiplied with the exact number of WASM operations executed. You can read about +[Gas Instrumentation](https://nomicon.io/RuntimeSpec/Preparation#gas-instrumentation) +if you are curious how we count WASM ops. + +Currently, all operations are charged the same, although it could be more +efficient to charge less for opcodes like `i32.add` compared to `f64.sqrt`. + +The remaining dynamic costs are for work done during host function calls. Each +host function charges a base cost. Either the general `wasm_base` cost, or a +specific cost such as `wasm_utf8_decoding_base`, or sometimes both. New host +function calls should define a separate base cost and not charge `wasm_base`. + +Additional host-side costs can be scaled per input byte, such as +`wasm_sha256_byte`, or costs related to moving data between host and guest, or +any other cost that is specific to the host function. Each host function must +clearly define what its costs are and how they depend on the input. + +## Non-gas parameters + +Not all runtime parameters are directly related to gas costs. Here is a brief +overview. + +- **Gas economics config:** Defines the conversion rate when purchasing gas with + NEAR tokens and how gas rewards are split. +- **Storage usage config:** Costs in tokens, not gas, for storing data on chain. +- **Account creation config:** Rules for account creation. +- **Smart contract limits:** Rules for WASM execution. + +None of the above define any gas costs directly. But there can be interplay +between those parameters and gas costs. For example, the limits on smart +contracts changes the assumptions for how slow a contract compilation could be, +hence it affects the deploy action costs. diff --git a/docs/architecture/gas/estimator.md b/docs/architecture/gas/estimator.md new file mode 100644 index 000000000..641402d17 --- /dev/null +++ b/docs/architecture/gas/estimator.md @@ -0,0 +1,128 @@ +# Runtime Parameter Estimator + +The runtime parameter estimator is a byzantine benchmarking suite. Byzantine +benchmarking is not a commonly used term but I feel it describes it quite +well. It measures the performance assuming that up to a third of validators and +all users collude to make the system as slow as possible. + +This benchmarking suite is used to check that the gas parameters defined in the +protocol are correct. Correct in this context means, a chunk filled with 1 Pgas +(**P**eta gas) will take at most 1 second to be applied. Or more generally, +per 1 Tgas of execution, we spend no more than 1ms wall-clock time. + +For now, framework timing is the only one that matters. Things will become more +complicated once there are multiple client implementations. But knowing that +framework can serve requests fast enough proves that it is possible to be at +least as fast. However, we should be careful to not couple costs too tightly +with the specific implementation of framework to allow for innovation in new +clients. + +The estimator code is part of the framework repository in the directory +[runtime/runtime-params-estimator](https://github.com/utnet-org/utility/tree/master/runtime/runtime-params-estimator). + +For a practical guide on how to run the estimator, please take a look at +[Running the Estimator](../../practices/workflows/gas_estimations.md) in the +workflows chapter. + +## Code Structure + +The estimator contains a binary and a library module. The +[main.rs](https://github.com/utnet-org/utility/blob/e40863c9ba61a0de140c869583b2113358605771/runtime/runtime-params-estimator/src/main.rs) +contains the CLI arguments parsing code and logic to fill the test database. + +The interesting code lives in +[lib.rs](https://github.com/utnet-org/utility/blob/e40863c9ba61a0de140c869583b2113358605771/runtime/runtime-params-estimator/src/lib.rs) +and its submodules. The comments at the top of that file provide a +high-level overview of how estimations work. More details on specific +estimations are available as comments on the enum variants of `Cost` in +[costs.rs](https://github.com/utnet-org/utility/blob/e40863c9ba61a0de140c869583b2113358605771/runtime/runtime-params-estimator/src/cost.rs#L9). + +If you roughly understand the three files above, you already have a great +overview of the estimator. +[estimator_context.rs](https://github.com/utnet-org/utility/blob/e40863c9ba61a0de140c869583b2113358605771/runtime/runtime-params-estimator/src/estimator_context.rs) +is another central file. A full estimation run creates a single +`EstimatorContext`. Each estimation will use it to spawn a new `Testbed` +with a fresh database that contains the same data as the setup in the +estimator context. + +Most estimations fill blocks with transactions to be executed and hand them to +`Testbed::measure_blocks`. To allow for easy repetitions, the block is usually +filled by an instance of the +[`TransactionBuilder`](https://github.com/utnet-org/utility/blob/e40863c9ba61a0de140c869583b2113358605771/runtime/runtime-params-estimator/src/transaction_builder.rs), +which can be retrieved from a testbed. + +But even filling blocks with transactions becomes repetitive since many +parameters are estimated similarly. +[utils.rs](https://github.com/utnet-org/utility/blob/master/runtime/runtime-params-estimator/src/utils.rs) +has a collection of helpful functions that let you write estimations very +quickly. + +## Estimation Metrics + +The estimation code is generally not concerned with the metric used to estimate +gas. We use `let clock = GasCost::measure();` and `clock.elapsed()` to measure +the cost in whatever metric has been specified in the CLI argument `--metric`. +But when you run estimations and especially when you want to interpret the +results, you want to understand the metric used. Available metrics are `time` +and `icount`. + +Starting with `time`, this is a simple wall-clock time measurement. At the end +of the day, this is what counts in a validator setup. But unfortunately, this +metric is very dependent on the specific hardware and what else is running on +that hardware right now. Dynamic voltage and frequency scaling (DVFS) also plays +a role here. To a certain degree, all these factors can be controlled. But it +requires full control over a system (often not the case when running on +cloud-hosted VMs) and manual labor to set it up. + +The other supported metric `icount` is much more stable. It uses +[qemu](https://www.qemu.org/) to emulate an x86 CPU. We then insert a custom +[TCG plugin](https://www.qemu.org/docs/master/devel/tcg-plugins.html) +([counter.c](https://github.com/utnet-org/utility/blob/08c4a1bd4b16847eb1c2fccee36bf16f6efb71fd/runtime/runtime-params-estimator/emu-cost/counter_plugin/counter.c)) +that counts the number of executed x86 instructions. It also intercepts system +calls and counts the number of bytes seen in `sys_read`, `sys_write` and their +variations. This gives an approximation for IO bytes, as seen on the interface +between the operating system and framework. To convert to gas, we use three +constants to multiply with instruction count, read bytes, and write bytes. + +We run qemu inside a Docker container, to make sure the qemu and qemu plugin +versions match with system libraries. Make sure to add `--docker` when running +with `--metric icount`. + +The great thing about `icount` is that you can run it on different machines and +it will always return the same result. It is not 100% deterministic but very +close, so it can usually detect code changes that degrade performance in major +ways. + +The problem with `icount` is how unrepresentative it is for real-life +performance. First, `x86` instructions are not all equally complex. Second, how +many of them are executed per cycle depends on instruction level pipelining, +branch prediction, memory prefetching, and more CPU features like that which are +just not captured by an emulator like qemu. Third, the time it takes to serve +bytes in system calls depends less on the sum of all bytes and more on data +locality and how it can be cached in the OS page cache. But regardless of all +these inaccuracies, it can still be useful to compare different implementations +both measured using `icount`. + +## From Estimations to Parameter Values + +To calculate the final gas parameter values, there is more to be done than just +running a single command. After all, these parameters are part of the protocol +specification. They cannot be changed easily. And setting them to a wrong value +can cause severe system instability. + +Our current strategy is to run estimations with two different metrics and do so +on standardized cloud hardware. The output is then sanity checked manually by +several people. Based on that, the final gas parameter value is determined. +Usually, it will be the higher output of the two metrics rounded up. + +The PR [#8031](https://github.com/utnet-org/utility/pull/8031) to set the ed25519 +verification gas parameters is a good example of how such an analysis and +report could look like. + +More details on the process will be added to this document +in due time. + + + + + diff --git a/docs/architecture/gas/gas_profile.md b/docs/architecture/gas/gas_profile.md new file mode 100644 index 000000000..488a196ad --- /dev/null +++ b/docs/architecture/gas/gas_profile.md @@ -0,0 +1,172 @@ +# Gas Profile + +What if you want to understand the exact gas spending of a smart contract call? +It would be very complicated to predict exactly how much gas executing a piece +of WASM code will require, including all host function calls and actions. An +easier approach is to just run the code on testnet and see how much gas it +burns. Gas profiles allow one to dig deeper and understand the breakdown of the +gas costs per parameter. + +**Gas profiles are not very reliable**, in that they are often incomplete and the +details of how they are computed can change without a protocol version bump. + +## Example Transaction Gas Profile + +You can query the gas profile of a transaction with +[NEAR CLI](https://docs.near.org/tools/unc-cli). + +```bash +unc_ENV=mainnet near tx-status 8vYxsqYp5Kkfe8j9LsTqZRsEupNkAs1WvgcGcUE4MUUw \ + --accountId app.nearcrowd.near \ + --nodeUrl https://archival-rpc.mainnet.near.org # Allows to retrieve older transactions. +``` + +``` +Transaction app.nearcrowd.near:8vYxsqYp5Kkfe8j9LsTqZRsEupNkAs1WvgcGcUE4MUUw +{ + receipts_outcome: [ + { + block_hash: '2UVQKpxH6PhEqiKr6zMggqux4hwMrqqjpsbKrJG3vFXW', + id: '14bwmJF21PXY9YWGYN1jpjF3BRuyCKzgVWfhXhZBKH4u', + outcome: { + executor_id: 'app.nearcrowd.near', + gas_burnt: 5302170867180, + logs: [], + metadata: { + gas_profile: [ + { + cost: 'BASE', + cost_category: 'WASM_HOST_COST', + gas_used: '15091782327' + }, + { + cost: 'CONTRACT_LOADING_BASE', + cost_category: 'WASM_HOST_COST', + gas_used: '35445963' + }, + { + cost: 'CONTRACT_LOADING_BYTES', + cost_category: 'WASM_HOST_COST', + gas_used: '117474381750' + }, + { + cost: 'READ_CACHED_TRIE_NODE', + cost_category: 'WASM_HOST_COST', + gas_used: '615600000000' + }, + # ... + # skipping entries for presentation brevity + # ... + { + cost: 'WRITE_REGISTER_BASE', + cost_category: 'WASM_HOST_COST', + gas_used: '48713882262' + }, + { + cost: 'WRITE_REGISTER_BYTE', + cost_category: 'WASM_HOST_COST', + gas_used: '4797573768' + } + ], + version: 2 + }, + receipt_ids: [ '46Qsorkr6hy36ZzWmjPkjbgG28ko1iwz1NT25gvia51G' ], + status: { SuccessValue: 'ZmFsc2U=' }, + tokens_burnt: '530217086718000000000' + }, + proof: [ ... ] + }, + { ... } + ], + status: { SuccessValue: 'ZmFsc2U=' }, + transaction: { ... }, + transaction_outcome: { + block_hash: '7MgTTVi3aMG9LiGV8ezrNvoorUwQ7TwkJ4Wkbk3Fq5Uq', + id: '8vYxsqYp5Kkfe8j9LsTqZRsEupNkAs1WvgcGcUE4MUUw', + outcome: { + executor_id: 'evgeniya.near', + gas_burnt: 2428068571644, + ... + tokens_burnt: '242806857164400000000' + }, + } +} +``` + +The gas profile is in `receipts_outcome.outcome.metadata.gas_profile`. It shows +gas costs per parameter and with associated categories such as `WASM_HOST_COST` +or `ACTION_COST`. In the example, all costs are of the former category, which is +gas expended on smart contract execution. The latter is for gas spent on +actions. + +To be complete, the output above should also have a gas profile entry for the +function call action. But currently this is not included since gas profiles only +work properly on function call receipts. Improving this is planned, see +[framework#8261](https://github.com/utnet-org/utility/issues/8261). + +The `tx-status` query returns one gas profile for each receipt. The output above +contains a single gas profile because the transaction only spawned one receipt. +If there was a chain of cross contract calls, there would be multiple profiles. + +Besides receipts, also note the `transaction_outcome` in the output. It contains +the gas cost for converting the transaction into a receipt. To calculate the +full gas cost, add up the transaction cost with all receipt costs. + +The transaction outcome currently does not have a gas profile, it only shows the +total gas spent converting the transaction. Arguably, it is not necessary to +provide the gas profile since the costs only depend on the list of actions. With +sufficient understanding of the protocol, one could reverse-engineer the exact +breakdown simply by looking at the action list. But adding the profile would +still make sense to make it easier to understand. + +## Gas Profile Versions + +Depending on the version in `receipts_outcome.outcome.metadata.version`, you +should expect a different format of the gas profile. Version 1 has no profile +data at all. Version 2 has a detailed profile but some parameters are conflated, +so you cannot extract the exact gas spending in some cases. Version 3 will have +the cost exactly per parameter. + +Which version of the profile an RPC node returns depends on the version it had +when it first processed the transaction. The profiles are stored in the database +with one version and never updated. Therefore, older transactions will usually +only have old profiles. However, one could replay the chain from genesis with a +new framework client and generate the newest profile for all transactions in this +way. + +Note: Due to bugs, some nodes will claim they send version 1 but actually +send version 2. (Did I mention that profiles are unreliable?) + +## How Gas Profiles are Created + +The transaction runtime charges gas in various places around the code. +`ActionResult` keeps a summary of all costs for an action. The `gas_burnt` and +`gas_used` fields track the total gas burned and reserved for spawned receipts. +These two fields are crucial for the protocol to function correctly, as they are +used to determine when execution runs out of gas. + +Additionally, `ActionResult` also has a `profile` field which keeps a detailed +breakdown of the gas spending per parameter. Profiles are not stored on chain +but RPC nodes and archival nodes keep them in their databases. This is mostly a +debug tool and has no direct impact on the correct functioning of the protocol. + +## Charging Gas + +Generally speaking, gas is charged right before the computation that it pays for +is executed. It has to be before to avoid cheap resource exhaustion attacks. +Imagine the user has only 1 gas unit left, but if we start executing an expensive +step, we would waste a significant duration of computation on all validators +without anyone paying for it. + +When charging gas for an action, the `ActionResult` can be updated directly. But +when charging WASM costs, it would be too slow to do a context switch each time, +Therefore, a fast gas counter exists that can be updated from within the VM. +(See +[gas_counter.rs](https://github.com/utnet-org/utility/blob/06711f8460f946b8d2042aa1df6abe03c5184767/runtime/unc-vm-logic/src/gas_counter.rs)) +At the end of a function call execution, the gas counter is read by the host and +merged into the `ActionResult`. + + diff --git a/docs/architecture/gas/parameter_definition.md b/docs/architecture/gas/parameter_definition.md new file mode 100644 index 000000000..5d43b4d08 --- /dev/null +++ b/docs/architecture/gas/parameter_definition.md @@ -0,0 +1,102 @@ +# Parameter Definitions + +Gas parameters are a subset of runtime parameters that are defined in +[runtime_configs/parameters.yaml](../../../core/parameters/res/runtime_configs/parameters.yaml). +**IMPORTANT:** This is not the final list of parameters, it contains the base +values which can be overwritten per protocol version. For example, +[53.yaml](../../../core/parameters/res/runtime_configs/53.yaml) +changes several parameters starting from version 53. You can see the final list +of parameters in +[runtime_configs/parameters.snap](../../../core/parameters/res/runtime_configs/parameters.snap). +This file is automatically updated whenever any of the parameters changes. To +see all parameter values for a specific version, check out the list of JSON +snapshots generated in this directory: +[parameters/src/snapshots](../../../core/parameters/src/snapshots). + +## Using Parameters in Code + +As the introduction on this page already hints at it, parameter values are +versioned. In other words, they can change if the protocol version changes. A +framework binary has to support multiple versions and choose the correct +parameter value at runtime. + +To make this easy, there is +[`RuntimeConfigStore`](https://github.com/utnet-org/utility/blob/a8964d200b3938a63d389263bc39c1bcd75b1de4/core/primitives/src/runtime/config_store.rs#L43). +It contains a sparse map from protocol versions to complete runtime +configurations (`BTreeMap>`). +The runtime then uses `store.get_config(protocol_version)` to access a runtime +configuration for a specific version. + +It is crucial to always use this runtime config store. Never hard-code parameter +values. Never look them up in a different way. + +In practice, this usually translates to a `&RuntimeConfig` argument for any +function that depends on parameter values. This config object implicitly defines +the protocol version. It should therefore not be cached. It should be read from +the store once per chunk and then passed down to all functions that need it. + +## How to Add a New Parameter + +First and foremost, if you are feeling lost, open a topic in our Zulip chat +([pagoda/contract-runtime](https://near.zulipchat.com/#narrow/stream/295306-pagoda.2Fcontract-runtime)). +We are here to help. + +### Principles + +Before adding anything, please review the basic principles for gas parameters. + +- A parameter must correspond to a clearly defined workload. +- When the workload is scalable by a factor `N` that depends on user input, + it will likely require a base parameter and a second parameter that is + multiplied by `N`. (Example: `N` = number of bytes when reading a value from + storage.) +- Charge gas before executing the workload. +- Parameters should be independent of specific implementation choices in + framework. +- Ideally, contract developers can easily understand what the cost is simply by + reading the name in a gas profile. + +The section on [Gas Profiles](./gas_profile.md#charging-gas) explains how to +charge gas, please also consider that when defining a new parameter. + +### Necessary Code Changes + +Adding the parameter in code involves several steps. + +1. Define the parameter by adding it to the list in `core/primitives/res/runtime_configs/parameters.yaml.` +2. Update the Rust view of parameters by adding a variant to `enum Parameter` + in `core/primitives-core/src/parameter.rs`. In the same file, update + `enum FeeParameter` if you add an action cost or update `ext_costs()` + if you add a cost inside function calls. +3. Update `RuntimeConfig`, the configuration used to reference parameters in + code. Depending on the type of parameter, you will need to update + `RuntimeFeesConfig` (for action costs) or `ExtCostsConfig` (for gas costs). +4. Update the list used for gas profiles. This is defined by `enum Cost` in + `core/primitives-core/src/profile.rs`. You need to add a variant to either + `enum ActionCosts` or `enum ExtCost`. Please also update `fn index()` that + maps each profile entry to a unique position in serialized gas profiles. +5. The parameter should be available to use in the code section you need it in. + Now is a good time to ensure `cargo check` and `cargo test --no-run` pass. + Most likely you have to update some testing code, such as + `ExtCostsConfig::test()`. +6. To merge your changes into framework, you will have to hide your parameter + behind a feature flag. Add the feature to the `Cargo.toml` of each crate + touched in steps 3 and 4 and hide the code behind `#[cfg(feature = + "protocol_feature_MY_NEW_FEATURE")]`. Do not hide code in step 2 so that + non-nightly builds can still read `parameters.yaml`. Also, add your feature as + a dependency on `nightly` in `core/primitives/Cargo.toml` to make sure it + gets included when compiling for nightly. After that, check `cargo check` and + `cargo test --no-run` with and without `features=nightly`. + +### What Gas Value Should the Parameter Have? + +For a first draft, the exact gas value used in the parameter is not crucial. +Make sure the right set of parameters exists and try to set a number that roughly +makes sense. This should be enough to enable discussions on the NEP around the +feasibility and usefulness of the proposed feature. If you are not sure, a good +rule of thumb is 0.1 Tgas for each disk operation and at least 1 Tgas for each +ms of CPU time. Then round it up generously. + +The value will have to be refined later. This is usually the last step after +the implementation is complete and reviewed. Have a look at the section on +[estimating gas parameters](./estimator.md) in the book. diff --git a/docs/architecture/how/README.md b/docs/architecture/how/README.md new file mode 100644 index 000000000..7df3bda4a --- /dev/null +++ b/docs/architecture/how/README.md @@ -0,0 +1,204 @@ +# How uncd works + +This chapter describes how uncd works with a focus on implementation details +and practical scenarios. To get a better understanding of how the protocol +works, please refer to [nomicon](https://nomicon.io). For a high-level code map +of framework, please refer to this [document](../). + +## High level overview + +On the high level, uncd is a daemon that periodically receives messages from +the network and sends messages to peers based on different triggers. Uncd is +implemented using an [actor +framework](https://en.wikipedia.org/wiki/Actor_model) called +[actix](https://docs.rs/actix). + +**Note:** Using actix was decided in the early days of the implementation of +framework and by no means represents our confidence in actix. On the contrary, we +have noticed a number of issues with actix and are considering implementing an +actor framework in house. + +There are several important actors in uncd: + +* `PeerActor` - Each peer is represented by one peer actor and runs in a separate + thread. It is responsible for sending messages to and receiving messages from + a given peer. After `PeerActor` receives a message, it will route it to + `ClientActor`, `ViewClientActor`, or `PeerManagerActor` depending on the type + of the message. + +* `PeerManagerActor` - Peer Manager is responsible for receiving messages to send + to the network from either `ClientActor` or `ViewClientActor` and routing them to + the right `PeerActor` to send the bytes over the wire. It is also responsible for + handling some types of network messages received and routed through `PeerActor`. + For the purpose of this document, we only need to know that `PeerManagerActor` + handles `RoutedMessage`s. Peer manager would decide whether the `RoutedMessage`s + should be routed to `ClientActor` or `ViewClientActor`. + +* `ClientActor` - Client actor is the “core” of uncd. It contains all the main + logic including consensus, block and chunk processing, state transition, garbage + collection, etc. Client actor is single threaded. + +* `ViewClientActor` - View client actor can be thought of as a read-only interface + to **client**. It only accesses data stored in a node’s storage and does not mutate + any state. It is used for two purposes: + + * Answering RPC requests by fetching the relevant piece of data from storage. + * Handling some network requests that do not require any changes to the + storage, such as header sync, state sync, and block sync requests. + + `ViewClientActor` runs in four threads by default but this number is configurable. + +## Data flow within `uncd` + +Flow for incoming messages: + +![](https://user-images.githubusercontent.com/1711539/195619986-25798cde-8a91-4721-86bd-93fa924b483a.png) + + +Flow for outgoing messages: + +![](https://user-images.githubusercontent.com/1711539/195626792-7697129b-7f9c-4953-b939-0b9bcacaf72c.png) + + +## How uncd operates when it is fully synced + +When a node is fully synced, the main logic of the node operates in the +following way (the node is assumed to track all shards, as most nodes on mainnet +do today): + +1. A block is produced by some block producer and sent to the node through + broadcasting. +2. The node receives a block and tries to process it. If the node is synced it + presumably has the previous block and the state before the current block to + apply. It then checks whether it has all the chunks available. If the node is + not a validator node, it won’t have any chunk parts and therefore won’t have + the chunks available. If the node is a validator node, it may already have + chunk parts through chunk parts forwarding from other nodes and therefore may + have already reconstructed some chunks. Regardless, if the node doesn’t have all + chunks for all shards, it will request them from peers by parts. +3. The chunk requests are sent and the node waits for enough chunk parts to be + received to reconstruct the chunks. For each chunk, 1/3 of all the parts + + (100) is sufficient to reconstruct a chunk. If new blocks arrive while waiting + for chunk parts, they will be put into a `OrphanPool`, waiting to be processed. + If a chunk part request is not responded to within `chunk_request_retry_period`, + which is set to 400ms by default, then a request for the same chunk part + would be sent again. +4. After all chunks are reconstructed, the node processes the current block by + applying transactions and receipts from the chunks. Afterwards, it will + update the head according to the fork choice rule, which only looks at block + height. In other words, if the newly processed block is of higher height than + the current head of the node, the head is updated. +5. The node checks whether any blocks in the `OrphanPool` are ready to be + processed in a BFS order and processes all of them until none can be + processed anymore. Note that a block is put into the `OrphanPool` if and + only if its previous block is not accepted. +6. Upon acceptance of a block, the node would check whether it needs to run + garbage collection. If it needs to, it would garbage collect two blocks worth + of data at a time. The logic of garbage collection is complicated and could + be found [here](./gc.md). +7. If the node is a validator node, it would start a timer after the current + block is accepted. After `min_block_production_delay` which is currently + configured to be 1.3s on mainnet, it would send an approval to the block + producer of the next block (current block height + 1). + +The main logic is illustrated below: + +![](https://user-images.githubusercontent.com/1711539/195635652-f0c7ebae-a2e5-423f-8e62-b853b815fcec.png) + + +## How uncd works when it is synchronizing + +`PeerManagerActor` periodically sends a `NetworkInfo` message to `ClientActor` +to update it on the latest peer information, which includes the height of each +peer. Once `ClientActor` realizes that it is more than `sync_height_threshold` +(which by default is set to 1) behind the highest height among peers, it starts +to sync. The synchronization process is done in three steps: + +1. Header sync. The node first identifies the headers it needs to sync through a + [`get_locator`](https://github.com/utnet-org/utility/blob/279044f09a7e6e5e3f26db4898af3655dae6eda6/chain/client/src/sync.rs#L332) + calculation. This is essentially an exponential backoff computation that + tries to identify commonly known headers between the node and its peers. Then + it would request headers from different peers, at most + `MAX_BLOCK_HEADER_HASHES` (which is 512) headers at a time. +2. After the headers are synced, the node would determine whether it needs to + run state sync. The exact condition can be found + [here](https://github.com/utnet-org/utility/blob/279044f09a7e6e5e3f26db4898af3655dae6eda6/chain/client/src/sync.rs#L458) + but basically a node would do state sync if it is more than 2 epochs behind + the head of the network. State sync is a very complex process and warrants + its own section. We will give a high level overview here. + + 1. First, the node computes `sync_hash` which is the hash of the block that + identifies the state that the node wants to sync. This is guaranteed to be + the first block of the most recent epoch. In fact, there is a + [check](https://github.com/utnet-org/utility/blob/279044f09a7e6e5e3f26db4898af3655dae6eda6/chain/chain/src/chain.rs#L4292) + on the receiver side that this is indeed the case. The node would also + request the block whose hash is `sync_hash` + 2. The node [deletes basically all data (blocks, chunks, state) from its + storage](https://github.com/utnet-org/utility/blob/279044f09a7e6e5e3f26db4898af3655dae6eda6/chain/chain/src/chain.rs#L1809). + This is not an optimal solution, but it makes the implementation for + combining state easier when there is no stale data in storage. + 3. For the state of each shard that the node needs to download, it first + requests a + [header](https://github.com/utnet-org/utility/blob/279044f09a7e6e5e3f26db4898af3655dae6eda6/core/primitives/src/syncing.rs#L40) + that contains some metadata the node needs to know about. Then the node + computes the number of state parts it needs to download and requests those + parts from different peers who track the shard. + 4. After all parts are downloaded, the node [combines those state + parts](https://github.com/utnet-org/utility/blob/279044f09a7e6e5e3f26db4898af3655dae6eda6/chain/client/src/client_actor.rs#L1877) + and then + [finalizes](https://github.com/utnet-org/utility/blob/279044f09a7e6e5e3f26db4898af3655dae6eda6/chain/chain/src/chain.rs#L3065) + the state sync by applying the last chunk included in or before the sync + block so that the node has the state after applying sync block to be able + to apply the next block. + 5. The node [resets + heads](https://github.com/utnet-org/utility/blob/279044f09a7e6e5e3f26db4898af3655dae6eda6/chain/chain/src/chain.rs#L1874) + properly after state sync. + +3. Block Sync. The node first gets the block with highest height that is on the + canonical chain and request from there `MAX_BLOCK_REQUESTS` (which is set to 5) + blocks from different peers in a round robin order. The block sync routine + runs again if head has changed (progress is made) or if a timeout (which is + set to 2s) has happened. + +**Note:** when a block is received and its height is no more than 500 + the +node’s current head height, then the node would request its previous block +automatically. This is called orphan sync and helps to speed up the syncing +process. If, on the other hand, the height is more than 500 + the node’s current +head height, the block is simply dropped. + + +## How `ClientActor` works + +ClientActor has some periodically running routines that are worth noting: + +* [Doomslug + timer](https://github.com/utnet-org/utility/blob/fa78002a1b4119e5efe277c3073b3f333f451ffc/chain/client/src/client_actor.rs#L1198) - + This routine runs every `doosmslug_step_period` (set to 100ms by default) and + updates consensus information. If the node is a validator node, it also sends + approvals when necessary. +* [Block + production](https://github.com/utnet-org/utility/blob/fa78002a1b4119e5efe277c3073b3f333f451ffc/chain/client/src/client_actor.rs#L991) - + This routine runs every `block_production_tracking_delay` (which is set to + 100ms by default) and checks if the node should produce a block. +* [Log + summary](https://github.com/utnet-org/utility/blob/fa78002a1b4119e5efe277c3073b3f333f451ffc/chain/client/src/client_actor.rs#L1790) - + Prints a log line that summarizes block rate, average gas used, the height of + the node, etc. every 10 seconds. +* [Resend chunk + requests](https://github.com/utnet-org/utility/blob/fa78002a1b4119e5efe277c3073b3f333f451ffc/chain/chunks/src/lib.rs#L910) - + This routine runs every `chunk_request_retry_period` (which is set to 400ms). + It resends the chunk part requests for those that are not yet responded to. +* [Sync](https://github.com/utnet-org/utility/blob/fa78002a1b4119e5efe277c3073b3f333f451ffc/chain/client/src/client_actor.rs#L1629) - + This routine runs every `sync_step_period` (which is set to 10ms by default) + and checks whether the node needs to sync from its peers and, if needed, also + starts the syncing process. +* [Catch + up](https://github.com/utnet-org/utility/blob/fa78002a1b4119e5efe277c3073b3f333f451ffc/chain/client/src/client_actor.rs#L1581) - + This routine runs every `catchup_step_period` (which is set to 100ms by + default) and runs the catch up process. This only applies if a node validates + shard A in epoch X and is going to validate a different shard B in epoch X+1. + In this case, the node would start downloading the state for shard B at the + beginning of epoch X. After the state downloading is complete, it would apply + all blocks in the current epoch (epoch X) for shard B to ensure that the node + has the state needed to validate shard B when epoch X+1 starts. diff --git a/docs/architecture/how/cross-shard.md b/docs/architecture/how/cross-shard.md new file mode 100644 index 000000000..a99e5fd1b --- /dev/null +++ b/docs/architecture/how/cross-shard.md @@ -0,0 +1,340 @@ +# Cross shard transactions - deep dive + +In this article, we'll look deeper into how cross-shard transactions are working +on the simple example of user `shard0` transferring money to user `shard1`. + +These users are on separate shards (`shard0` is on shard 0 and `shard1` is on +shard 1). + +Imagine, we run the following command in the command line: + +```console +$ unc_ENV=local near send shard0 shard1 500 +``` + +What happens under the hood? How is this transaction changed into receipts and +processed by near? + +## From Explorer perspective + +If you look at a simple token transfer in explorer +([example](https://explorer.near.org/transactions/79gPsyYRG2xghr6oNLpMbdjP2jpafjVT35no9atS6zUf)), +you can see that it is broken into three separate sections: + +* convert transaction into receipt ( executed in block B ) +* receipt that transfers tokens ( executed in block B+1 ) +* receipt that refunds gas ( executed in block B+2 ) + +But under the hood, the situation is a little bit more complex, as there is +actually one more receipt (that is created after converting the transaction). +Let's take a deeper look. + +## Internal perspective (Transactions & Receipts) + +One important thing to remember is that NEAR is sharded - so in all our +designs, we have to assume that each account is on a separate shard. So that the +fact that some of them are colocated doesn't give any advantage. + +### Step 1 - Transaction + +This is the part which we receive from the user (`SignedTransaction`) - it has 3 +parts: + +* signer (account + key) who signed the transaction +* receiver (in which account context should we execute this) +* payload - a.k.a Actions to execute. + +As the first step, we want to change this transaction into a Receipt (a.k.a +'internal' message) - but before doing that, we must verify that: + +* the message signature matches (that is - that this message was actually signed + by this key) +* that this key is authorized to act on behalf of that account (so it is a full + access key to this account - or a valid function key). + +The last point above means, that we MUST execute this (Transaction to Receipt) +transition within the shard that the `signer` belongs to (as other shards don't +know the state that belongs to signer - so they don't know which keys it has). + +So actually if we look inside the chunk 0 (where `shard0` belongs) at block +B, we'll see the transaction: + +``` +Chunk: Ok( + V2( + ShardChunkV2 { + chunk_hash: ChunkHash( + 8mgtzxNxPeEKfvDcNdFisVq8TdeqpCcwfPMVk219zRfV, + ), + header: V3( + ShardChunkHeaderV3 { + inner: V2( + ShardChunkHeaderInnerV2 { + prev_block_hash: CgTJ7FFwmawjffrMNsJ5XhvoxRtQPXdrtAjrQjG91gkQ, + prev_state_root: 99pXnYjQbKE7bEf277urcxzG3TaN79t2NgFJXU5NQVHv, + outcome_root: 11111111111111111111111111111111, + encoded_merkle_root: 67zdyWTvN7kB61EgTqecaNgU5MzJaCiRnstynerRbmct, + encoded_length: 187, + height_created: 1676, + shard_id: 0, + gas_used: 0, + gas_limit: 1000000000000000, + balance_burnt: 0, + outgoing_receipts_root: 8s41rye686T2ronWmFE38ji19vgeb6uPxjYMPt8y8pSV, + tx_root: HyS6YfQbfBRniVSbWRnxsxEZi9FtLqHwyzNivrF6aNAM, + validator_proposals: [], + }, + ), + height_included: 0, + signature: ed25519:uUvmvDV2cRVf1XW93wxDU8zkYqeKRmjpat4UUrHesJ81mmr27X43gFvFuoiJHWXz47czgX68eyBN38ejwL1qQTD, + hash: ChunkHash( + 8mgtzxNxPeEKfvDcNdFisVq8TdeqpCcwfPMVk219zRfV, + ), + }, + ), + transactions: [ + SignedTransaction { + transaction: Transaction { + signer_id: AccountId( + "shard0", + ), + public_key: ed25519:Ht8EqXGUnY8B8x7YvARE1LRMEpragRinqA6wy5xSyfj5, + nonce: 11, + receiver_id: AccountId( + "shard1", + ), + block_hash: 6d5L1Vru2c4Cwzmbskm23WoUP4PKFxBHSP9AKNHbfwps, + actions: [ + Transfer( + TransferAction { + deposit: 500000000000000000000000000, + }, + ), + ], + }, + signature: ed25519:63ssFeMyS2N1khzNFyDqiwSELFaUqMFtAkRwwwUgrPbd1DU5tYKxz9YL2sg1NiSjaA71aG8xSB7aLy5VdwgpvfjR, + hash: 6NSJFsTTEQB4EKNKoCmvB1nLuQy4wgSKD51rfXhmgjLm, + size: 114, + }, + ], + receipts: [], + }, + ), +) +``` + +**Side note:** When we're converting the transaction into a receipt, we also use +this moment to deduct prepaid gas fees and transferred tokens from the 'signer' +account. The details on how much gas is charged can be found at https://nomicon.io/RuntimeSpec/Fees/. + +## Step 2 - cross shard receipt + +After transaction was changed into a receipt, this receipt must now be sent to +the shard where the `receiver` is (in our example `shard1` is on shard 1). + +We can actually see this in the chunk of the next block: + +``` +Chunk: Ok( + V2( + ShardChunkV2 { + chunk_hash: ChunkHash( + DoF7yoCzyBSNzB8R7anWwx6vrimYqz9ZbEmok4eqHZ3m, + ), + header: V3( + ShardChunkHeaderV3 { + inner: V2( + ShardChunkHeaderInnerV2 { + prev_block_hash: 82dKeRnE262qeVf31DXaxHvbYEugPUDvjGGiPkjm9Rbp, + prev_state_root: DpsigPFeVJDenQWVueGKyTLVYkQuQjeQ6e7bzNSC7JVN, + outcome_root: H34BZknAfWrPCcppcHSqbXwFvAiD9gknG8Vnrzhcc4w, + encoded_merkle_root: 3NDvQBrcRSAsWVPWkUTTrBomwdwEpHhJ9ofEGGaWsBv9, + encoded_length: 149, + height_created: 1677, + shard_id: 0, + gas_used: 223182562500, + gas_limit: 1000000000000000, + balance_burnt: 22318256250000000000, + outgoing_receipts_root: Co1UNMcKnuhXaHZz8ozMnSfgBKPqyTKLoC2oBtoSeKAy, + tx_root: 11111111111111111111111111111111, + validator_proposals: [], + }, + ), + height_included: 0, + signature: ed25519:32hozA7GMqNqJzscEWzYBXsTrJ9RDhW5Ly4sp7FXP1bmxoCsma8Usxry3cjvSuywzMYSD8HvGntVtJh34G2dKJpE, + hash: ChunkHash( + DoF7yoCzyBSNzB8R7anWwx6vrimYqz9ZbEmok4eqHZ3m, + ), + }, + ), + transactions: [], + receipts: [ + Receipt { + predecessor_id: AccountId( + "shard0", + ), + receiver_id: AccountId( + "shard1", + ), + receipt_id: 3EtEcg7QSc2CYzuv67i9xyZTyxBD3Dvx6X5yf2QgH83g, + receipt: Action( + ActionReceipt { + signer_id: AccountId( + "shard0", + ), + signer_public_key: ed25519:Ht8EqXGUnY8B8x7YvARE1LRMEpragRinqA6wy5xSyfj5, + gas_price: 103000000, + output_data_receivers: [], + input_data_ids: [], + actions: [ + Transfer( + TransferAction { + deposit: 500000000000000000000000000, + }, + ), + ], + }, + ), + }, + ], + }, + ), +) +``` + +**Side comment:** notice that the receipt itself no longer has a `signer` field, but +a `predecessor_id` one. + +Such a receipt is sent to the destination shard (we'll explain this process in a +separate article) where it can be executed. + + +## 3. Gas refund. + +When shard 1 processes the receipt above, it is then ready to refund the unused +gas to the original account (`shard0`). So it also creates the receipt, and puts +it inside the chunk. This time it is in shard 1 (as that's where it was +executed). + +``` +Chunk: Ok( + V2( + ShardChunkV2 { + chunk_hash: ChunkHash( + 8sPHYmBFp7cfnXDAKdcATFYfh9UqjpAyqJSBKAngQQxL, + ), + header: V3( + ShardChunkHeaderV3 { + inner: V2( + ShardChunkHeaderInnerV2 { + prev_block_hash: Fj7iu26Yy9t5e9k9n1fSSjh6ZoTafWyxcL2TgHHHskjd, + prev_state_root: 4y6VL9BoMJg92Z9a83iqKSfVUDGyaMaVU1RNvcBmvs8V, + outcome_root: 7V3xRUeWgQa7D9c8s5jTq4dwdRcyTuY4BENRmbWaHiS5, + encoded_merkle_root: BnCE9LZgnFEjhQv1fSYpxPNw56vpcLQW8zxNmoMS8H4u, + encoded_length: 149, + height_created: 1678, + shard_id: 1, + gas_used: 223182562500, + gas_limit: 1000000000000000, + balance_burnt: 22318256250000000000, + outgoing_receipts_root: HYjZzyTL5JBfe1Ar4C4qPKc5E6Vbo9xnLHBKLVAqsqG2, + tx_root: 11111111111111111111111111111111, + validator_proposals: [], + }, + ), + height_included: 0, + signature: ed25519:4FzcDw2ay2gAGosNpFdTyEwABJhhCwsi9g47uffi77N21EqEaamCg9p2tALbDt5fNeCXXoKxjWbHsZ1YezT2cL94, + hash: ChunkHash( + 8sPHYmBFp7cfnXDAKdcATFYfh9UqjpAyqJSBKAngQQxL, + ), + }, + ), + transactions: [], + receipts: [ + Receipt { + predecessor_id: AccountId( + "system", + ), + receiver_id: AccountId( + "shard0", + ), + receipt_id: 6eei79WLYHGfv5RTaee4kCmzFx79fKsX71vzeMjCe6rL, + receipt: Action( + ActionReceipt { + signer_id: AccountId( + "shard0", + ), + signer_public_key: ed25519:Ht8EqXGUnY8B8x7YvARE1LRMEpragRinqA6wy5xSyfj5, + gas_price: 0, + output_data_receivers: [], + input_data_ids: [], + actions: [ + Transfer( + TransferAction { + deposit: 669547687500000000, + }, + ), + ], + }, + ), + }, + ], + }, + ), +) +``` + +Such gas refund receipts are a little bit special - as we'll set the +`predecessor_id` to be `system` - but the receiver is what we expect (`shard0` +account). + +**Note:** `system` is a special account that doesn't really belong to any shard. +As you can see in this example, the receipt was created within shard 1. + +So putting it all together would look like this: + +![image](https://user-images.githubusercontent.com/91919554/200617392-00b9fa0c-2f15-40ad-9802-137ca9a5a15d.png) + +But wait - NEAR was saying that transfers are happening with 2 blocks - but here +I see that it took 3 blocks. What's wrong? + +The image above is a simplification, and reality is a little bit trickier - +especially as receipts in a given chunks are actually receipts received as a +result from running a PREVIOUS chunk from this shard. + +We'll explain it more in the next section. + +# Advanced: What's actually going on? + +As you could have read in [Transactions And Receipts](./tx_receipts.md) - the +'receipts' field in the chunk is actually representing 'outgoing' receipts +from the previous block. + +So our image should look more like this: + +![image](https://user-images.githubusercontent.com/91919554/200621066-a5d06f2d-ff43-44ce-a52b-47dc44d6f8ab.png) + +In this example, the black boxes are representing the 'processing' of the chunk, +and red arrows are cross-shard communication. + +So when we process Shard 0 from block 1676, we read the transaction, and output +the receipt - which later becomes the input for shard 1 in block 1677. + +But you might still be wondering - so why didn't we add the Receipt (transfer) +to the list of receipts of shard0 1676? + +That's because the shards & blocks are set BEFORE we do any computation. So the +more correct image would look like this: + +![image](https://user-images.githubusercontent.com/91919554/200621808-1ce78047-6968-4af5-9c2a-805a0f1643fc.png) + +Here you can clearly see that chunk processing (black box), is happening AFTER +the chunk is set. + +In this example, the blue arrows are showing the part where we persist the +result (receipt) into next block's chunk. + + +In a future article, we'll discuss how the actual cross-shard communication +works (red arrows) in the picture, and how we could guarantee that a given shard +really gets all the red arrows, before it starts processing. diff --git a/docs/architecture/how/epoch.md b/docs/architecture/how/epoch.md new file mode 100644 index 000000000..335c10730 --- /dev/null +++ b/docs/architecture/how/epoch.md @@ -0,0 +1,123 @@ +# How Epoch Works + +This short document will tell you all you need to know about Epochs in NEAR +protocol. + +You can also find additional information about epochs in +[nomicon](https://nomicon.io/BlockchainLayer/EpochManager/). + +## What is an Epoch? + +Epoch is a sequence of consecutive blocks. +Within one epoch, the set of validators is fixed, and validator rotation +happens at epoch boundaries. + +Basically almost all the changes that we do are happening at epoch boundaries: + +* sharding changes +* protocol version changes +* validator changes +* changing tracking shards +* state sync + +## Where does the Epoch Id come from? + +`EpochId` for epoch T+2 is the last hash of the block of epoch T. + +![image](https://user-images.githubusercontent.com/1711539/195907256-c4b1d956-632c-4c11-aa38-17603b1fcc40.png) + +Situation at genesis is interesting. We have three blocks: + +dummy ← genesis ← first-block + +## Where do we set the epoch length? + +Epoch length is set in the genesis config. Currently in mainnet it is set to 43200 blocks: + +```json + "epoch_length": 43200 +``` + +See [the mainnet genesis](https://s3-us-west-1.amazonaws.com/build.nearprotocol.com/framework-deploy/mainnet/genesis.json) for more details. + +This means that each epoch lasts around 15 hours. + +**Important:** sometimes there might be ‘troubles’ on the network, that might result +in epoch lasting a little bit longer (if we cannot get enough signatures on the +last blocks of the previous epoch). + +You can read specific details on our +[nomicon page](https://nomicon.io/BlockchainLayer/EpochManager/Epoch). + +## How do we pick the next validators? + +**TL;DR:** in the last block of the epoch T, we look at the accounts that have +highest stake and we pick them to become validators in **T+2**. + +We are deciding on validators for T+2 (and not T+1) as we want to make sure that +validators have enough time to prepare for block production and validation (they +have to download the state of shards etc). + +For more info on how we pick validators please look at +[nomicon](https://nomicon.io/Economics/Economic#validator-selection). + +## Epoch and Sharding + +Sharding changes happen only on epoch boundary - that’s why many of the requests +(like which shard does my account belong to), require also an `epoch_id` as a +parameter. + +As of April 2022 we don’t have dynamic sharding yet, so the whole chain is +simply using 4 shards. + +### How can I get more information about current/previous epochs? + +We don’t show much information about Epochs in Explorer. Today, you can use +`state_viewer` (if you have access to the network database). + +At the same time, we’re working on a small debug dashboard, to show you the +basic information about past epochs - stay tuned. + +## Technical details + +### Where do we store epoch info? + +We use a couple columns in the database to store epoch information: + +* **ColEpochInfo = 11** - is storing the mapping from EpochId to EpochInfo + structure that contains all the details. +* **ColEpochStart = 23** - has a mapping from EpochId to the first block height + of that epoch. +* **ColEpochValidatorInfo = 47** - contains validator statistics (blocks + produced etc.) for each epoch. + +### How does epoch info look like? + +Here’s the example epoch info from a localnet node. As you can see below, +EpochInfo mostly contains information about who is the validator and in which +order should they produce the blocks. + +``` +EpochInfo.V3( + epoch_height=7, + validators=ListContainer([ + validator_stake.V1(account_id='node0', public_key=public_key.ED25519(tuple_data=ListContainer([b'7PGseFbWxvYVgZ89K1uTJKYoKetWs7BJtbyXDzfbAcqX'])), stake=51084320187874404740382878961615), + validator_stake.V1(account_id='node2', public_key=public_key.ED25519(tuple_data=ListContainer([b'GkDv7nSMS3xcqA45cpMvFmfV1o4fRF6zYo1JRR6mNqg5'])), stake=51084320187874404740382878961615), + validator_stake.V1(account_id='node1', public_key=public_key.ED25519(tuple_data=ListContainer([b'6DSjZ8mvsRZDvFqFxo8tCKePG96omXW7eVYVSySmDk8e'])), stake=50569171534262067815663761517574)]), + + validator_to_index={'node0': 0, 'node1': 2, 'node2': 1}, + + block_producers_settlement=ListContainer([0, 1, 2]), + chunk_producers_settlement=ListContainer([ListContainer([0, 1, 2]), ListContainer([0, 1, 2]), ListContainer([0, 1, 2]), ListContainer([0, 1, 2]), ListContainer([0, 1, 2])]), + + hidden_validators_settlement=ListContainer([]), + fishermen=ListContainer([]), + fishermen_to_index={}, + stake_change={'node0': 51084320187874404740382878961615, 'node1': 50569171534262067815663761517574, 'node2': 51084320187874404740382878961615}, + validator_reward={'near': 37059603312899067633082436, 'node0': 111553789870214657675206177, 'node1': 110428850075662293347329569, 'node2': 111553789870214657675206177}, + validator_kickout={}, + minted_amount=370596033128990676330824359, + seat_price=24438049905601740367428723111, + protocol_version=52 +) +``` diff --git a/docs/architecture/how/gas.md b/docs/architecture/how/gas.md new file mode 100644 index 000000000..dd88ad0c2 --- /dev/null +++ b/docs/architecture/how/gas.md @@ -0,0 +1,275 @@ +# Gas + +This page describes the technical details around gas during the lifecycle of a +_transaction_(*) while giving an intuition for why things are the way they are +from a technical perspective. For a more practical, user-oriented angle, please +refer to the [gas section in the official protocol +documentation](https://docs.near.org/concepts/basics/transactions/gas). + +(*) _For this page, a transaction shall refer to the set of all recursively +generated receipts by a `SignedTransaction`. When referring to only the original +transaction object, we write `SignedTransaction`._ + +The topic is split into several sections. + +1. [Gas Flow](#gas-flow) + - [Buying Gas](#buying-gas-for-a-transaction): How are NEAR tokens converted to gas? + - [Burning Gas](#burning-gas): Who receives burnt tokens? + - [Gas in Contract Calls](#gas-in-contract-calls): How is gas attached to calls? + - [Contract Reward](#contract-reward): How smart contract earn a reward. +2. [Gas Price](#gas-price): + - [Block-Level Gas Price](#block-level-gas-price): How the block-level gas price is determined. + - [Pessimistic Gas Price](#pessimistic-gas-price): How worst-case gas pricing is estimated. + - [Effective Gas Purchase Cost](#effective-gas-purchase-cost): The cost paid for a receipt. +3. [Tracking Gas](#tracking-gas-in-receipts): How the system keeps track of purchased gas during the transaction execution. + +## Gas Flow + +On the highest level, gas is bought by the signer, burnt during execution, and +contracts receive a part of the burnt gas as a reward. We will discuss each step +in more details. + +### Buying Gas for a Transaction + +A signer pays all the gas required for a transaction upfront. However, there is +no explicit act of buying gas. Instead, the fee is subtracted directly in NEAR +tokens from the balance of the signer's account. If we ignore all the details +explained further down, the fee is calculated as `gas amount` * `gas price`. + +The `gas amount` is not a field of `SignedTransaction`, nor is it something the +signer can choose. It is only a virtual field that is computed on-chain following +the protocol's rules. + +The `gas price` is a variable that may change during the execution of the +transaction. The way it is implemented today, a single transaction can be +charged a different gas price for different receipts. + +Already we can see a fundamental problem: Gas is bought once at the beginning +but the gas price may change during execution. To solve this incompatibility, +the protocol calculates a pessimistic gas price for the initial purchase. Later +on, the delta between real and pessimistic gas prices is refunded at the end of +every receipt execution. + +An alternative implementation would instead charge the gas at every receipt, +instead of once at the start. However, remember that the execution may happen on +a different shard than the signer account. Therefore we cannot access the +signer's balance while executing. + +### Burning Gas + +Buying gas immediately removes a part of the signer's tokens from the total +supply. However, the equivalent value in gas still exists in the form of the +receipt and the unused gas will be converted back to tokens as a refund. + +The gas spent on execution on the other hand is burnt and removed from total +supply forever. Unlike gas in other chains, none of it goes to validators. This +is roughly equivalent to the base fee burning mechanism which Ethereum added in +[EIP-1559](https://eips.ethereum.org/EIPS/eip-1559). But in unc Protocol, the +entire fee is burnt because there is no [priority +fee](https://ethereum.org/en/developers/docs/gas/#priority-fee) that Ethereum +pays out to validators. + +The following diagram shows how gas flows through the execution of a +transaction. The transaction consists of a function call performing a cross +contract call, hence two function calls in sequence. (Note: This diagram is +heavily simplified, more accurate diagrams are further down.) + +![Very Simplified Gas Flow Diagram](https://github.com/utnet-org/utility/assets/6342444/f52c6e4b-6fca-4f61-8e6e-ac786076aa65) + + +### Gas in Contract Calls + +A function call has a fixed gas cost to be initiated. Then the execution itself +draws gas from the `attached_gas`, sometimes also called `prepaid_gas`, until it +reaches zero, at which point the function call aborts with a `GasExceeded` +error. No changes are persisted on chain. + +(*Note on naming: If you see `prepaid_fee: Balance` in the framework code base, +this is NOT only the fee for `prepaid_gas`. It also includes prepaid fees for +other gas costs. However, `prepaid_gas: Gas` is used the same in the code base +as described in this document.*) + +Attaching gas to function calls is the primary way for end-users and contract +developers to interact with gas. All other gas fees are implicitly computed and +are hidden from the users except for the fact that the equivalent in tokens is +removed from their account balance. + +To attach gas, the signer sets the gas field of the function call action. +Wallets and CLI tools expose this to the users in different ways. Usually just +as a `gas` field, which makes users believe this is the maximum gas the +transaction will consume. Which is not true, the maximum is the specified number +plus the fixed base cost. + +Contract developers also have to pick the attached gas values when their +contract calls another contract. They cannot buy additional gas, they have to +work with the unspent gas attached to the current call. They can check how much +gas is left by subtracting the `used_gas()` from the `prepaid_gas()` host +function results. But they cannot use all the available gas, since that would +prevent the current function call from executing to the end. + +The gas attached to a function can be at most `max_total_prepaid_gas`, which is +300 Tgas since the mainnet launch. Note that this limit is per +`SignedTransaction`, not per function call. In other words, batched function +calls share this limit. + +There is also a limit to how much single call can burn, `max_gas_burnt`, which +used to be 200 Tgas but has been increased to 300 Tgas in protocol version 52. +(Note: When attaching gas to an outgoing function call, this is not counted as +gas burnt.) However, given a call can never burn more than was attached anyway, +this second limit is obsolete with the current configuration where the two limits +are equal. + +Since protocol version 53, with the stabilization of +[NEP-264](https://github.com/near/NEPs/blob/master/neps/nep-0264.md), contract +developers do not have to specify the absolute amount of gas to attach to calls. +`promise_batch_action_function_call_weight` allows to specify a ratio of unspent +gas that is computed after the current call has finished. This allows attaching +100% of unspent gas to a call. If there are multiple calls, this allows +attaching an equal fraction to each, or any other split as defined by the weight +per call. + + +### Contract Reward + +A rather unique property of unc Protocol is that a part of the gas fee goes to +the contract owner. This "smart contract gets paid" model is pretty much the +opposite design choice from the "smart contract pays" model that for example +[Cycles in the Internet +Computer](https://internetcomputer.org/docs/current/developer-docs/gas-cost#details-cost-of-compute-and-storage-transactions-on-the-internet-computer) +implement. + +The idea is that it gives contract developers a source of income and hence an +incentive to create useful contracts that are commonly used. But there are also +downsides, such as when implementing a free meta-transaction relayer one has to +be careful not to be susceptible to faucet-draining attacks where an attacker +extracts funds from the relayer by making calls to a contract they own. + +How much contracts receive from execution depends on two things. + +1. How much gas is burnt on the function call execution itself. That is, only + the gas taken from the `attached_gas` of a function call is considered for + contract rewards. The base fees paid for creating the receipt, including the + `action_function_call` fee, are burnt 100%. +2. The remainder of the burnt gas is multiplied by the runtime configuration + parameter + [`burnt_gas_reward`](../../../core/parameters/res/runtime_configs/parameters.snap#L5C5-L5C5) + which currently is at 30%. + +During receipt execution, framework code tracks the `gas_burnt_for_function_call` +separately from other gas burning to enable this contract reward calculations. + +In the (still simplified) flow diagram, the contract reward looks like this. +For brevity, `gas_burnt_for_function_call` in the diagram is denoted as `wasm fee`. + +![Slightly Simplified Gas Flow Diagram](https://github.com/utnet-org/utility/assets/6342444/32600ef0-1475-43af-b196-576317787578) + + + +## Gas Price + +Gas pricing is a surprisingly deep and complicated topic. Usually, we only think +about the value of the `gas_price` field in the block header. However, to +understand the internals, this is not enough. + +### Block-Level Gas Price + +`gas_price` is a field in the block header. It determines how much it costs to +burn gas at the given block height. Confusingly, this is not the same price at +which gas is purchased. +(See [Effective Gas Purchase Price](#effective-gas-price).) + +The price is measured in NEAR tokens per unit of gas. It dynamically changes in +the range between 0.1 NEAR per Pgas and 2 NEAR per Pgas, based on demand. (1 +Pgas = 1000 Tgas corresponds to a full chunk.) + +The block producer has to set this field following the exact formula as defined +by the protocol. Otherwise, the produced block is invalid. + +Intuitively, the formula checks how much gas was used compared to the total +capacity. If it exceeds 50%, the gas price increases exponentially within the +limits. When the demand is below 50%, it decreases exponentially. In practice, +it stays at the bottom most of the time. + +Note that all shards share the same gas price. Hence, if one out of four shards +is at 100% capacity, this will not cause the price to increase. The 50% capacity +is calculated as an average across all shards. + +Going slightly off-topic, it should also be mentioned that chunk capacity is not +constant. Chunk producers can change it by 0.1% per chunk. The framework client +does not currently make use of this option, so it really is a nitpick only +relevant in theory. However, any client implementation such as framework must +compute the total capacity as the sum of gas limits stored in the chunk headers +to be compliant. Using a hard-coded `1000 Tgas * num_shards` would lead to +incorrect block header validation. + + +### Pessimistic Gas Price + +The pessimistic gas price calculation uses the fact that any transaction can +only have a limited depth in the generated receipt DAG. For most actions, the +depth is a constant 1 or 2. For function call actions, it is limited to a +hand-wavy `attached_gas` / `min gas per function call`. (Note: `attached_gas` is +a property of a single action and is only a part of the total gas costs of a +receipt.) + +Once the maximum depth is known, the protocol assumes that the gas price will +not change more than 3% per receipt. This is not a guarantee since receipts can +be delayed for virtually unlimited blocks. + +The final formula for the pessimistic gas price is the following. + +```txt +pessimistic(current_gas_price, max_depth) = current_gas_price × 1.03^max_depth +``` + +This still is not the price at which gas is purchased. But we are very close. + + +### Effective Gas Purchase Cost + +When a transaction is converted to its root action receipt, the gas costs are +calculated in two parts. + +Part one contains all the gas which is burnt immediately. Namely, the `send` +costs for a receipt and all the actions it includes. This is charged at the +current block-level gas price. + +Part two is everything else, from execution costs of actions that are statically +known such as `CreateAccount` all the way to `attached_gas` for function calls. +All of this is purchased at the same pessimistic gas price, even if some actions +inside might have a lower maximum call depth than others. + +The deducted tokens are the sum of these two parts. If the account has +insufficient balance to pay for this pessimistic pricing, it will fail with a +`NotEnoughBalance` error, with the required balance included in the error +message. + +Inserting the pessimistic gas pricing into the flow diagram, we finally have a +complete picture. Note how an additional refund receipt is required. Also, check +out the updated formula for the effective purchase price at the top left and the +resulting higher number. + +![Complete Gas Flow +Diagram](https://github.com/utnet-org/utility/assets/6342444/8341fb45-9beb-4808-8a89-8144fa075930) + + + +## Tracking Gas in Receipts + +The previous section explained how gas is bought and what determines its price. +This section details the tracking that enables correct refunds. + +First, when a `SignedTransaction` is converted to a receipt, the pessimistic gas +price is written to the receipt's `gas_price` field. + +Later on, when the receipt has been executed, a gas refund is created at the +value of `receipt.gas_burnt` * (`block_header.gas_price` - `receipt.gas_price`). + +Some gas goes attaches to outgoing receipts. We commonly refer to this as used +gas that was not burnt, yet. The refund excludes this gas. But it includes the +receipt send cost. + +Finally, unspent gas is refunded at the full `receipt.gas_price`. This refund is +merged with the refund for burnt gas of the same receipt outcome to reduce the +number of spawned system receipts. But it makes it a bit harder to interpret +refunds when backtracking for how much gas a specific refund receipt covers. diff --git a/docs/architecture/how/gc.md b/docs/architecture/how/gc.md new file mode 100644 index 000000000..9d22a975f --- /dev/null +++ b/docs/architecture/how/gc.md @@ -0,0 +1,71 @@ +# Garbage Collection + +This document covers the basics of Chain garbage collection. + +Currently we run garbage collection only in non-archival nodes, to keep the +size of the storage under control. Therefore, we remove blocks, chunks and state +that is ‘old’ enough - which in current configuration means 5 epochs ago. + +We run a single ‘round’ of GC after a new block is accepted to the chain - and +in order not to delay the chain too much, we make sure that each round removes +at most 2 blocks from the chain. + +For more details look at function `clear_data()` in file `chain/chain/src/chain.rs` + +## How it works: + +Imagine the following chain (with 2 forks) + +![](https://user-images.githubusercontent.com/1711539/195649805-e7997192-be3a-4bf0-992d-d35b2ad80847.png) + +In the pictures below, let’s assume that epoch length is 5 and we keep only 3 +epochs (rather than 5 that is currently set in production) - otherwise the image +becomes too large 😉. + +If head is in the middle of the epoch, the `gc_stop` will be set to the first +block of epoch T-2, and `tail` & `fork_tail` will be sitting at the last block of +epoch T-3. + +(and no GC is happening in this round - as tail is next to `gc_stop`). + +![](https://user-images.githubusercontent.com/1711539/195649850-95dee667-b88b-4ef6-b08c-77a17b8d4ae2.png) + +Next block was accepted on the chain (head jumped ahead), but still no GC +happening in this round: + +![](https://user-images.githubusercontent.com/1711539/195649879-e29cc826-dfd8-4cbc-a66d-72e42202d26a.png) + +Now interesting things will start happening once head ‘crosses’ over to the +next epoch. + +First, the `gc_stop` will jump to the beginning of the next epoch. + +![](https://user-images.githubusercontent.com/1711539/195649928-0401b221-b6b3-4986-8931-54fbdd1adda0.png) + +Then we’ll start the GC of the forks: by first moving the `fork_tail` to match +the `gc_stop` and going backwards from there. + +![](https://user-images.githubusercontent.com/1711539/195649966-dac6a4dd-f04b-4131-887a-58efe89d456a.png) + +It will start removing all the blocks that don’t have a successor (a.k.a the tip +of the fork). And then it will proceed to lower height. + +![](https://user-images.githubusercontent.com/1711539/195650003-90e1fde7-18a6-4343-b0dd-9a10a596f136.png) + +Will keep going until it ‘hits’ the tail. + +![](https://user-images.githubusercontent.com/1711539/195650059-dd6b3d30-7dd5-4324-8e65-80f955960c47.png) + +In order not to do too much in one go, we’d only remove up to 2 block in each +run (that happens after each head update). + +Now, the forks are gone, so we can proceed with GCing of the blocks from +the canonical chain: + +![](https://user-images.githubusercontent.com/1711539/195650101-dc6953a7-0d55-4db8-a78b-6a52310410b2.png) + +Same as before, we’d remove up to 2 blocks in each run: + +![](https://user-images.githubusercontent.com/1711539/195650127-b30865e1-d9c1-4950-8607-67d82a185b76.png) + +Until we catch up to the `gc_stop`. diff --git a/docs/architecture/how/meta-tx.md b/docs/architecture/how/meta-tx.md new file mode 100644 index 000000000..4f52cae9b --- /dev/null +++ b/docs/architecture/how/meta-tx.md @@ -0,0 +1,266 @@ +# Meta Transactions + +[NEP-366](https://github.com/near/NEPs/pull/366) introduced the concept of meta +transactions to unc Protocol. This feature allows users to execute transactions +on NEAR without owning any gas or tokens. In order to enable this, users +construct and sign transactions off-chain. A third party (the relayer) is used +to cover the fees of submitting and executing the transaction. + +The MVP for meta transactions is currently in the stabilization process. +Naturally, the MVP has some limitations, which are discussed in separate +sections below. Future iterations have the potential to make meta transactions +more flexible. + +## Overview + +![Flow chart of meta +transactions](https://raw.githubusercontent.com/near/NEPs/003e589e6aba24fc70dd91c9cf7ef0007ca50735/neps/assets/nep-0366/NEP-DelegateAction.png) +_Credits for the diagram go to the NEP authors Alexander Fadeev and Egor +Uleyskiy._ + + +The graphic shows an example use case for meta transactions. Alice owns an +amount of the fungible token $FT. She wants to transfer some to John. To do +that, she needs to call `ft_transfer("john", 10)` on an account named `FT`. + +In technical terms, ownership of $FT is an entry in the `FT` contract's storage +that tracks the balance for her account. Note that this is on the application +layer and thus not a part of unc Protocol itself. But `FT` relies on the +protocol to verify that the `ft_transfer` call actually comes from Alice. The +contract code checks that `predecessor_id` is `"Alice"` and if that is the case +then the call is legitimately from Alice, as only she could create such a +receipt according to the unc Protocol specification. + +The problem is, Alice has no NEAR tokens. She only has a NEAR account that +someone else funded for her and she owns the private keys. She could create a +signed transaction that would make the `ft_transfer("john", 10)` call. But +validator nodes will not accept it, because she does not have the necessary unc +token balance to purchase the gas. + +With meta transactions, Alice can create a `DelegateAction`, which is very +similar to a transaction. It also contains a list of actions to execute and a +single receiver for those actions. She signs the `DelegateAction` and forwards +it (off-chain) to a relayer. The relayer wraps it in a transaction, of which the +relayer is the signer and therefore pays the gas costs. If the inner actions +have an attached token balance, this is also paid for by the relayer. + +On chain, the `SignedDelegateAction` inside the transaction is converted to an +action receipt with the same `SignedDelegateAction` on the relayer's shard. The +receipt is forwarded to the account from `Alice`, which will unpacked the +`SignedDelegateAction` and verify that it is signed by Alice with a valid Nonce +etc. If all checks are successful, a new action receipt with the inner actions +as body is sent to `FT`. There, the `ft_transfer` call finally executes. + +## Relayer + +Meta transactions only work with a relayer. This is an application layer +concept, implemented off-chain. Think of it as a server that accepts a +`SignedDelegateAction`, does some checks on them and eventually forwards it +inside a transaction to the blockchain network. + +A relayer may choose to offer their service for free but that's not going to be +financially viable long-term. But they could easily have the user pay using +other means, outside of unc blockchain. And with some tricks, it can even be +paid using fungible tokens on unc. + +In the example visualized above, the payment is done using $FT. Together with +the transfer to John, Alice also adds an action to pay 0.1 $FT to the relayer. +The relayer checks the content of the `SignedDelegateAction` and only processes +it if this payment is included as the first action. In this way, the relayer +will be paid in the same transaction as John. + +Note that the payment to the relayer is still not guaranteed. It could be that +Alice does not have sufficient $FT and the transfer fails. To mitigate, the +relayer should check the $FT balance of Alice first. + +Unfortunately, this still does not guarantee that the balance will be high +enough once the meta transaction executes. The relayer could waste NEAR gas +without compensation if Alice somehow reduces her $FT balance in just the right +moment. Some level of trust between the relayer and its user is therefore +required. + +The vision here is that there will be mostly application-specific relayers. A +general-purpose relayer is difficult to implement with just the MVP. See +limitations below. + +## Limitation: Single receiver + +A meta transaction, like a normal transaction, can only have one receiver. It's +possible to chain additional receipts afterwards. But crucially, there is no +atomicity guarantee and no roll-back mechanism. + +For normal transactions, this has been widely accepted as a fact for how unc +Protocol works. For meta transactions, there was a discussion around allowing +multiple receivers with separate lists of actions per receiver. While this could +be implemented, it would only create a false sense of atomicity. Since each +receiver would require a separate action receipt, there is no atomicity, the +same as with chains of receipts. + +Unfortunately, this means the trick to compensate the relayer in the same meta +transaction as the serviced actions only works if both happen on the same +receiver. In the example, both happen on `FT` and this case works well. But it +would not be possible to send $FT1 and pay the relayer in $FT2. Nor could one +deploy a contract code on `Alice` and pay in $FT in one meta transaction. It +would require two separate meta transactions to do that. Due to timing problems, +this again requires some level of trust between the relayer and Alice. + +A potential solution could involve linear dependencies between the action +receipts spawned from a single meta transaction. Only if the first succeeds, +will the second start executing, and so on. But this quickly gets too complicated +for the MVP and is therefore left open for future improvements. + +## Constraints on the actions inside a meta transaction + +A transaction is only allowed to contain one single delegate action. Nested +delegate actions are disallowed and so are delegate actions next to each other +in the same receipt. + +Nested delegate actions have no known use case and it would be complicated to +implement. Consequently, it was omitted. + +For delegate actions beside each other, there was a bit of back and forth during +the NEP-366 design phase. The potential use case here is essentially the same as +having multiple receivers in a delegate action. Naturally, it runs into all the +same complications (false sense of atomicity) and ends with the same conclusion: +Omitted from the MVP and left open for future improvement. + +## Limitation: Accounts must be initialized + +Any transaction, including meta transactions, must use NONCEs to avoid replay +attacks. The NONCE must be chosen by Alice and compared to a NONCE stored on +chain. This NONCE is stored on the access key information that gets initialized +when creating an account. + +Implicit accounts don't need to be initialized in order to receive NEAR tokens, +or even $FT. This means users could own $FT but no NONCE is stored on chain for +them. This is problematic because we want to enable this exact use case with +meta transactions, but we have no NONCE to create a meta transaction. + +For the MVP, the proposed solution, or work-around, is that the relayer will +have to initialize the account of Alice once if it does not exist. Note that +this cannot be done as part of the meta transaction. Instead, it will be a +separate transaction that executes first. Only then can Alice even create a +`SignedDelegateAction` with a valid NONCE. + +Once again, some trust is required. If Alice wanted to abuse the relayer's +helpful service, she could ask the relayer to initialize her account. +Afterwards, she does not sign a meta transaction, instead she deletes her +account and cashes in the small token balance reserved for storage. If this +attack is repeated, a significant amount of tokens could be stolen from the +relayer. + +One partial solution suggested here was to remove the storage staking cost from +accounts. This means there is no financial incentive for Alice to delete her +account. But it does not solve the problem that the relayer has to pay for the +account creation and Alice can simply refuse to send a meta transaction +afterwards. In particular, anyone creating an account would have financial +incentive to let a relayer create it for them instead of paying out of the own +pockets. This would still be better than Alice stealing tokens but +fundamentally, there still needs to be some trust. + +An alternative solution discussed is to do NONCE checks on the relayer's access +key. This prevents replay attacks and allows implicit accounts to be used in +meta transactions without even initializing them. The downside is that meta +transactions share the same NONCE counter(s). That means, a meta transaction +sent by Bob may invalidate a meta transaction signed by Alice that was created +and sent to the relayer at the same time. Multiple access keys by the relayer +and coordination between relayer and user could potentially alleviate this +problem. But for the MVP, nothing along those lines has been approved. + +## Gas costs for meta transactions + +Meta transactions challenge the traditional ways of charging gas for actions. To +see why, let's first list the normal flow of gas, outside of meta transactions. + +1. Gas is purchased (by deducting NEAR from the transaction signer account), + when the transaction is converted into a receipt. The amount of gas is + implicitly defined by the content of the receipt. For function calls, the + caller decides explicitly how much gas is attached on top of the minimum + required amount. The NEAR token price per gas unit is dynamically adjusted on + the blockchain. In today's framework code base, this happens as part of + [`verify_and_charge_transaction`](https://github.com/utnet-org/utility/blob/4510472d69c059644bb2d2579837c6bd6d94f190/runtime/runtime/src/verifier.rs#L69) + which gets called in + [`process_transaction`](https://github.com/utnet-org/utility/blob/4510472d69c059644bb2d2579837c6bd6d94f190/runtime/runtime/src/lib.rs#L218). +2. For all actions listed inside the transaction, the `SEND` cost is burned + immediately. Depending on the condition `sender == receiver`, one of two + possible `SEND` costs is chosen. The `EXEC` cost is not burned, yet. But it + is implicitly part of the transaction cost. The third and last part of the + transaction cost is the gas attached to function calls. The attached gas is + also called prepaid gas. (Not to be confused with `total_prepaid_exec_fees` + which is the implicitly prepaid gas for `EXEC` action costs.) +3. On the receiver shard, `EXEC` costs are burned before the execution of an + action starts. Should the execution fail and abort the transaction, the + remaining gas will be refunded to the signer of the transaction. + +Ok, now adapt for meta transactions. Let's assume Alice uses a relayer to +execute actions with Bob as the receiver. + +1. The relayer purchases the gas for all inner actions, plus the gas for the + delegate action wrapping them. +2. The cost of sending the inner actions and the delegate action from the + relayer to Alice's shard will be burned immediately. The condition `relayer + == Alice` determines which action `SEND` cost is taken (`sir` or `not_sir`). + Let's call this `SEND(1)`. +3. On Alice's shard, the delegate action is executed, thus the `EXEC` gas cost + for it is burned. Alice sends the inner actions to Bob's shard. Therefore, we + burn the `SEND` fee again. This time based on `Alice == Bob` to figure out + `sir` or `not_sir`. Let's call this `SEND(2)`. +4. On Bob's shard, we execute all inner actions and burn their `EXEC` cost. + +Each of these steps should make sense and not be too surprising. But the +consequence is that the implicit costs paid at the relayer's shard are +`SEND(1)` + `SEND(2)` + `EXEC` for all inner actions plus `SEND(1)` + `EXEC` for +the delegate action. This might be surprising but hopefully with this +explanation it makes sense now! + +## Gas refunds in meta transactions + +Gas refund receipts work exactly like for normal transaction. At every step, the +difference between the pessimistic gas price and the actual gas price at that +height is computed and refunded. At the end of the last step, additionally all +remaining gas is also refunded at the original purchasing price. The gas refunds +go to the signer of the original transaction, in this case the relayer. This is +only fair, since the relayer also paid for it. + +## Balance refunds in meta transactions + +Unlike gas refunds, the protocol sends balance refunds to the predecessor +(a.k.a. sender) of the receipt. This makes sense, as we deposit the attached +balance to the receiver, who has to explicitly reattach a new balance to new +receipts they might spawn. + +In the world of meta transactions, this assumption is also challenged. If an +inner action requires an attached balance (for example a transfer action) then +this balance is taken from the relayer. + +The relayer can see what the cost will be before submitting the meta transaction +and agrees to pay for it, so nothing wrong so far. But what if the transaction +fails execution on Bob's shard? At this point, the predecessor is `Alice` and +therefore she receives the token balance refunded, not the relayer. This is +something relayer implementations must be aware of since there is a financial +incentive for Alice to submit meta transactions that have high balances attached +but will fail on Bob's shard. + +## Function access keys in meta transactions + +Assume alice sends a meta transaction and signs with a function access key. +How exactly are permissions applied in this case? + +Function access keys can limit the allowance, the receiving contract, and the +contract methods. The allowance limitation acts slightly strange with meta +transactions. + +But first, both the methods and the receiver will be checked as expected. That +is, when the delegate action is unwrapped on Alice's shard, the access key is +loaded from the DB and compared to the function call. If the receiver or method +is not allowed, the function call action fails. + +For allowance, however, there is no check. All costs have been covered by the +relayer. Hence, even if the allowance of the key is insufficient to make the call +directly, indirectly through meta transaction it will still work. + +This behavior is in the spirit of allowance limiting how much financial +resources the user can use from a given account. But if someone were to limit a +function access key to one trivial action by setting a very small allowance, +that is circumventable by going through a relayer. An interesting twist that +comes with the addition of meta transactions. diff --git a/docs/architecture/how/proofs.md b/docs/architecture/how/proofs.md new file mode 100644 index 000000000..2f5e0f205 --- /dev/null +++ b/docs/architecture/how/proofs.md @@ -0,0 +1,243 @@ +# Proofs + +“Don’t trust, but verify” - let’s talk about proofs + +## Was your transaction included? + +How do you know that your transaction was actually included in the blockchain? +Sure, you can “simply” ask the RPC node, and it might say “yes”, but is it +enough? + +The other option would be to ask many nodes - hoping that at least one of them +would be telling the truth. But what if that is not enough? + +The final solution would be to run your own node - this way you’d check all the +transactions yourself, and then you could be sure - but this can become a quite +expensive endeavour - especially when many shards are involved. + +But there is actually a better solution - that doesn’t require you to trust the +single (or many) RPC nodes, and to verify, by yourself, that your transaction was +actually executed. + +## Let’s talk about proofs (merkelization): + +Imagine you have 4 values that you’d like to store, in such a way, that you can +easily prove that a given value is present. + +![image](https://user-images.githubusercontent.com/1711539/198579560-923f1f97-a8df-486d-b68e-8796c6aaa300.png) + +One way to do it, would be to create a binary tree, where each node would hold a +hash: + +* leaves would hold the hashes that represent the hash of the respective value. +* internal nodes would hold the hash of “concatenation of hashes of their children” +* the top node would be called a root node (in this image it is the node n7) + +With such a setup, you can prove that a given value exists in this tree, by +providing a “path” from the corresponding leaf to the root, and including all +the siblings. + +For example to prove that value v[1] exists, we have to provide all the nodes +marked as green, with the information about which sibling (left or right) they +are: + +![image](https://user-images.githubusercontent.com/1711539/198579596-488c540b-cd24-4d38-bc07-4dc3378c53d0.png) + +``` +# information needed to verify that node v[1] is present in a tree +# with a given root (n7) +[(Left, n0), (Right, n6)] + +# Verification +assert_eq!(root, hash(hash(n0, hash(v[1])), n6)) +``` + +We use the technique above (called merkelization) in a couple of places in our +protocol, but for today’s article, I’d like to focus on receipts & outcome +roots. + +## Merkelization, receipts and outcomes + +In order to prove that a given receipt belongs to a given block, we will need to +fetch some additional information. + +As NEAR is sharded, the receipts actually belong to “Chunks” not Blocks +themselves, so the first step is to find the correct chunk and fetch its +`ChunkHeader`. + +``` +ShardChunkHeaderV3 { + inner: V2( + ShardChunkHeaderInnerV2 { + prev_block_hash: `C9WnNCbNvkQvnS7jdpaSGrqGvgM7Wwk5nQvkNC9aZFBH`, + prev_state_root: `5uExpfRqAoZv2dpkdTxp1ZMcids1cVDCEYAQwAD58Yev`, + outcome_root: `DBM4ZsoDE4rH5N1AvCWRXFE9WW7kDKmvcpUjmUppZVdS`, + encoded_merkle_root: `2WavX3DLzMCnUaqfKPE17S1YhwMUntYhAUHLksevGGfM`, + encoded_length: 425, + height_created: 417, + shard_id: 0, + gas_used: 118427363779280, + gas_limit: 1000000000000000, + balance_burnt: 85084341232595000000000, + outgoing_receipts_root: `4VczEwV9rryiVSmFhxALw5nCe9gSohtRpxP2rskP3m1s`, + tx_root: `11111111111111111111111111111111`, + validator_proposals: [], + }, + ), +} +``` + +The field that we care about is called `outcome_root`. This value represents the +root of the binary merkle tree, that is created based on all the receipts that +were processed in this chunk. + +**Note:** You can notice that we also have a field here called +`encoded_merkle_root` - this is another case where we use merkelization in our +chain - this field is a root of a tree that holds hashes of all the "partial +chunks" into which we split the chunk to be distributed over the network. + +So, in order to verify that a given receipt/transaction was really included, we +have to compute its hash (see details below), get the path to the root, and +voila, we can confirm that it was really included. + +But how do we get the siblings on the path to the root? This is actually +something that RPC nodes do return in their responses. + +If you ever looked closely at NEAR’s tx-status response, you can notice a +"proof" section there. For every receipt, you'd see something like this: + +``` +proof: [ + { + direction: 'Right', + hash: '2wTFCh2phFfANicngrhMV7Po7nV7pr6gfjDfPJ2QVwCN' + }, + { + direction: 'Right', + hash: '43ei4uFk8Big6Ce6LTQ8rotsMzh9tXZrjsrGTd6aa5o6' + }, + { + direction: 'Left', + hash: '3fhptxeChNrxWWCg8woTWuzdS277u8cWC9TnVgFviu3n' + }, + { + direction: 'Left', + hash: '7NTMqx5ydMkdYDFyNH9fxPNEkpgskgoW56Y8qLoVYZf7' + } +] +``` + +And the values in there are exactly the siblings (plus info on which side of the +tree the sibling is), on the path to the root. + +**Note:** proof section doesn’t contain the root itself and also doesn’t include +the hash of the receipt. + + +## [Advanced section]: Let’s look at a concrete example + +Imagine that we have the following receipt: + +``` +{ + block_hash: '7FtuLHR3VSNhVTDJ8HmrzTffFWoWPAxBusipYa2UfrND', + id: '6bdKUtGbybhYEQ2hb2BFCTDMrtPBw8YDnFpANZHGt5im', + outcome: { + executor_id: 'node0', + gas_burnt: 223182562500, + logs: [], + metadata: { gas_profile: [], version: 1 }, + receipt_ids: [], + status: { SuccessValue: '' }, + tokens_burnt: '0' + }, + proof: [ + { + direction: 'Right', + hash: 'BWwZ4wHuzaUxdDSrhAEPjFQtDgwzb8K4zoNzfX9A3SkK' + }, + { + direction: 'Left', + hash: 'Dpg4nQQwbkBZMmdNYcZiDPiihZPpsyviSTdDZgBRAn2z' + }, + { + direction: 'Right', + hash: 'BruTLiGx8f71ufoMKzD4H4MbAvWGd3FLL5JoJS3XJS3c' + } + ] +} +``` + +Remember that the outcomes of the execution will be added to the NEXT block, so +let’s find the next block hash, and the proper chunk. + +(in this example, I’ve used the `view-state chain` from uncd) + +``` +417 7FtuLHR3VSNhVTDJ8HmrzTffFWoWPAxBusipYa2UfrND | node0 | parent: 416 C9WnNCbNvkQvnS7jdpaSGrqGvgM7Wwk5nQvkNC9aZFBH | .... 0: E6pfD84bvHmEWgEAaA8USCn2X3XUJAbFfKLmYez8TgZ8 107 Tgas |1: Ch1zr9TECSjDVaCjupNogLcNfnt6fidtevvKGCx8c9aC 104 Tgas |2: 87CmpU6y7soLJGTVHNo4XDHyUdy5aj9Qqy4V7muF5LyF 0 Tgas |3: CtaPWEvtbV4pWem9Kr7Ex3gFMtPcKL4sxDdXD4Pc7wah 0 Tgas +418 J9WQV9iRJHG1shNwGaZYLEGwCEdTtCEEDUTHjboTLLmf | node0 | parent: 417 7FtuLHR3VSNhVTDJ8HmrzTffFWoWPAxBusipYa2UfrND | .... 0: 7APjALaoxc8ymqwHiozB5BS6mb3LjTgv4ofRkKx2hMZZ 0 Tgas |1: BoVf3mzDLLSvfvsZ2apPSAKjmqNEHz4MtPkmz9ajSUT6 0 Tgas |2: Auz4FzUCVgnM7RsQ2noXsHW8wuPPrFxZToyLaYq6froT 0 Tgas |3: 5ub8CZMQmzmZYQcJU76hDC3BsajJfryjyShxGF9rzpck 1 Tgas +``` + +I know that the receipt should belong to Shard 3 so let’s fetch +the chunk header: + +```console +$ uncd view-state chunks --chunk-hash 5ub8CZMQmzmZYQcJU76hDC3BsajJfryjyShxGF9rzpck +``` + +``` +ShardChunkHeaderV3 { + inner: V2( + ShardChunkHeaderInnerV2 { + prev_block_hash: `7FtuLHR3VSNhVTDJ8HmrzTffFWoWPAxBusipYa2UfrND`, + prev_state_root: `6rtfqVEXx5STLv5v4zwLVqAfq1aRAvLGXJzZPK84CPpa`, + outcome_root: `2sZ81kLj2cw5UHTjdTeMxmaWn2zFeyr5pFunxn6aGTNB`, + encoded_merkle_root: `6xxoqYzsgrudgaVRsTV29KvdTstNYVUxis55KNLg6XtX`, + encoded_length: 8, + height_created: 418, + shard_id: 3, + gas_used: 1115912812500, + gas_limit: 1000000000000000, + balance_burnt: 0, + outgoing_receipts_root: `8s41rye686T2ronWmFE38ji19vgeb6uPxjYMPt8y8pSV`, + tx_root: `11111111111111111111111111111111`, + validator_proposals: [], + }, + ), + height_included: 0, + signature: ed25519:492i57ZAPggqWEjuGcHQFZTh9tAKuQadMXLW7h5CoYBdMRnfY4g7A749YNXPfm6yXnJ3UaG1ahzcSePBGm74Uvz3, + hash: ChunkHash( + `5ub8CZMQmzmZYQcJU76hDC3BsajJfryjyShxGF9rzpck`, + ), +} +``` + +So the `outcome_root` is `2sZ81kLj2cw5UHTjdTeMxmaWn2zFeyr5pFunxn6aGTNB` - let’s +verify it then. + +Our first step is to compute the hash of the receipt, which is equal to +`hash([receipt_id, hash(borsh(receipt_payload)])` + +```python +# this is a borsh serialized ExecutionOutcome struct. +# computing this, we leave as an exercise for the reader :-) +receipt_payload_hash = "7PeGiDjssz65GMCS2tYPHUm6jYDeBCzpuPRZPmLNKSy7" + +receipt_hash = base58.b58encode(hashlib.sha256(struct.pack(" + +Screenshot 2023-12-01 at 10 10 50 + +Screenshot 2023-12-01 at 10 10 42 + + + +### Throttling + +The resharding process can be quite resource intensive and affect the regular operation of a node. In order to mitigate that as well as limit any need for increasing hardware specifications of the nodes throttling was added. Throttling slows down resharding to not have it impact other node operations. Throttling can be configured by adjusting the resharding_config in the node config file. + +* batch_size - controls the size of batches in which resharding moves data around. Setting a smaller batch size will slow down the resharding process and make it less resource consuming. +* batch_delay - controls the delay between processing of batches. Setting a smaller batch delay will speed up the resharding process and make it more resource consuming. + +The remainig fields in the ReshardingConfig are only intended for testing purposes and should remain set to their default values. + +The default configuration for ReshardingConfig should provide a good and safe setting for resharding in the production networks. There is no need for node operators to make any changes to it unless they observe issues. + +The resharding config can be adjusted at runtime, without restarting the node. The config needs to be updated first and then a SIGHUP signal should be sent to the uncd process. When received the signal uncd will update the config and print a log message showing what fields were changed. It's recommended to check the log to make sure the relevant config change was correctly picked up. + +## Future possibilities + +### Localize resharding to a single shard + +Currently when resharding we need to move the data for all shards even if only a single shard is being split. That is due to having the version field in the storage key that needs to be updated when changing shard layout version. + +This can be improved by changing how ShardUId works e.g. removing the version and instead using globally unique shard ids. + +### Dynamic resharding + +The current implementation relies on having the shard layout determined offline and manually added to the node implementation. + +The dynamic resharding would mean that the network itself can automatically determine that resharding is needed, what should be the new shard layout and schedule the resharding. + + +### Support different changes to shard layout + +The current implementation only supports splitting a shard. In the future we can consider adding support of other operations such as merging two shards or moving an existing boundary account. diff --git a/docs/architecture/how/serialization.md b/docs/architecture/how/serialization.md new file mode 100644 index 000000000..f06ecc3b0 --- /dev/null +++ b/docs/architecture/how/serialization.md @@ -0,0 +1,172 @@ +# Serialization: Borsh, Json, ProtoBuf + +If you spent some time looking at NEAR code, you’ll notice that we have +different methods of serializing structures into strings. So in this article, +we’ll compare these different approaches, and explain how and where we’re using +them. + +## JSON + +JSON doesn’t need much introduction. We’re using it for external APIs (jsonrpc) +and configuration. It is a very popular, flexible and human-readable format. + +## Proto (Protocol Buffers) + +We started using proto recently - and we plan to use it mostly for our network +communication. Protocol buffers are strongly typed - they require you to create +a .proto file, where you describe the contents of your message. + +For example: + +```proto +message HandshakeFailure { + // Reason for rejecting the Handshake. + Reason reason = 1; + + // Data about the peer. + PeerInfo peer_info = 2; + // GenesisId of the NEAR chain that the peer belongs to. + GenesisId genesis_id = 3; +} +``` + +Afterwards, such a proto file is fed to protoc ‘compiler’ that returns +auto-generated code (in our case Rust code) - that can be directly imported into +your library. + +The main benefit of protocol buffers is their backwards compatibility (as long +as you adhere to the rules and don’t reuse the same field ids). + +## Borsh + +Borsh is our custom serializer ([link](https://github.com/near/borsh)), that we use +mostly for things that have to be hashed. + +The main feature of Borsh is that, there are no two binary representations that +deserialize into the same object. + +You can read more on how Borsh serializes the data, by looking at the Specification +tab on [borsh.io](https://borsh.io). + +The biggest pitfall/risk of Borsh, is that any change to the structure, might +cause previous data to no longer be parseable. + +For example, inserting a new enum ‘in the middle’: + +```rust +pub enum MyCar { + Bmw, + Ford, +} + +If we change our enum to this: + +pub enum MyCar { + Bmw, + Citroen, + Ford, // !! WRONG - Ford objects cannot be deserialized anymore +} +``` + +This is especially tricky if we have conditional compilation: + +```rust +pub enum MyCar { + Bmw, + #[cfg(feature = "french_cars")] + Citroen, + Ford, +} +``` + +Is such a scenario - some of the objects created by binaries with this feature +enabled, will not be parseable by binaries without this feature. + +Removing and adding fields to structures is also dangerous. + +Basically - the only ‘safe’ thing that you can do with Borsh - is add a new Enum +value at the end. + +## Summary + +So to recap what we’ve learned: + +JSON - mostly used for external APIs - look for serde::Serialize/Deserialize + +Proto - currently being developed to be used for network connections - objects +have to be specified in proto file. + +Borsh - for things that we hash (and currently also for all the things that we +store on disk - but we might move to proto with this in the future). Look for +BorshSerialize/BorshDeserialize + +## Questions + +### Why don’t you use JSON for everything? + +While this is a tempting option, JSON has a few drawbacks: + +* size (json is self-describing, so all the field names etc are included every time) +* non-canonical: JSON doesn’t specify strict ordering of the fields, so we’d + have to do additional restrictions/rules on that - otherwise the same + ‘conceptual’ message would end up with different hashes. + +### Ok - so how about proto for everything? + +There are couple risks related with using proto for things that have to be +hashed. A Serialized protocol buffer can contain additional data (for example +fields with tag ids that you’re not using) and still successfully parse (that’s +how it achieves backward compatibility). + +For example, in this proto: + +```proto +message First { + string foo = 1; + string bar = 2; +} +message Second { + string foo = 1; +} +``` + +Every ‘First’ message will be successfully parsed as ‘Second’ message - which +could lead to some programmatic bugs. + +## Advanced section - RawTrieNode + +There is one more place in the code where we use a ‘custom’ encoding: +RawTrieNodeWithSize defined in store/src/trie/raw_node.rs. While the format +uses Borsh derives and API, there is a difference in how branch children +(`[Option; 16]`) are encoded. Standard Borsh encoding would +encode `Option` sixteen times. Instead, RawTrieNodeWithSize uses +a bitmap to indicate which elements are set resulting in a different layout. + +Imagine a children vector like this: + +```rust +[Some(0x11), None, Some(0x12), None, None, …] +``` + +Here, we have children at index 0 and 2 which has a bitmap of `101` + +Custom encoder: + +``` +// Number of children determined by the bitmask +[16 bits bitmask][32 bytes child][32 bytes child] +[5][0x11][0x12] +// Total size: 2 + 32 + 32 = 68 bytes +``` + +Borsh: + +``` +[8 bits - 0 or 1][32 bytes child][8 bits 0 or 1][8 bits ] +[1][0x11][0][1][0x11][0][0]... +// Total size: 16 + 32 + 32 = 80 bytes +``` + +Code for encoding children is given in BorshSerialize implementation for +ChildrenRef type and code for decoding in BorshDeserialize implementation for +Children. All of that is in aforementioned store/src/trie/raw_node.rs file. diff --git a/docs/architecture/how/sync.md b/docs/architecture/how/sync.md new file mode 100644 index 000000000..4d66d4c4b --- /dev/null +++ b/docs/architecture/how/sync.md @@ -0,0 +1,273 @@ +# How Sync Works + +## Basics + +While Sync and Catchup sounds similar - they are actually describing two +completely different things. + +**Sync** - is used when your node falls ‘behind’ other nodes in the network (for +example because it was down for some time or it took longer to process some +blocks etc). + +**Catchup** - is used when you want (or have to) start caring about (a.k.a. +tracking) additional shards in the future epochs. Currently it should be a no-op +for 99% of nodes (see below). + + +**Tracking shards:** as you know our system has multiple shards (currently 4). +Currently 99% of nodes are tracking all the shards: validators have to - as they +have to validate the chunks from all the shards, and normal nodes mostly also +track all the shards as this is default. + +But in the future - we will have more and more people tracking only a subset of +the shards, so the catchup will be increasingly important. + +## Sync + +If your node is behind the head - it will start the sync process (this code is +running periodically in the client_actor and if you’re behind for more than +`sync_height_threshold` (currently 50) blocks - it will enable the sync. + +The Sync behavior differs depending on whether you’re an archival node (which +means you care about the state of each block) or ‘normal’ node - where you care +mostly about the Tip of the network. + +### Step 1: Header Sync [archival node & normal node*] (“downloading headers”) + +The goal of the header sync is to get all the block headers from your current +HEAD all the way to the top of the chain. + +As headers are quite small, we try to request multiple of them in a single call +(currently we ask for 512 headers at once). + +![image](https://user-images.githubusercontent.com/1711539/195892312-2fbd8241-87ce-4241-a44d-ff3056b12bab.png) + +### Step 1a: Epoch Sync [normal node*] // not implemented yet + + +While currently normal nodes are using Header sync, we could actually allow them +to do something faster - “light client sync” a.k.a “epoch sync”. + +The idea of the epoch sync, is to read “just” a single block header from each +epoch - that has to contain additional information about validators. + +This way it would drastically reduce both the time needed for the sync and the +db resources. + +Implementation target date is TBD. + +![image](https://user-images.githubusercontent.com/1711539/195892336-cc117c08-d3ad-43f7-9304-3233b25e8bb1.png) + +Notice that in the image above - it is enough to only get the ‘last’ header from +each epoch. For the ‘current’ epoch, we still need to get all the headers. + +### Step 2: State sync [normal node] + +After header sync - if you notice that you’re too far behind, i.e. the chain +head is at least two epochs ahead of your local head - the node will try to do +the ‘state sync’. + +The idea of the state sync is - rather than trying to process all the blocks - +try to ‘jump’ ahead by downloading the freshest state instead - and continue +processing blocks from that place in the chain. As a side effect, it is going to +create a ‘gap’ in the chunks/state on this node (which is fine - as the data +will be garbage collected after 5 epochs anyway). State sync will ONLY sync to +the beginning of the epoch - it cannot sync to any random block. + +This step is never run on the archival nodes - as these nodes want to have whole +history and cannot have any gaps. + +![image](https://user-images.githubusercontent.com/1711539/195892354-cf2befed-98e9-40a2-9b81-b5cf738406e0.png) + +In this case, we can skip processing transactions that are in the blocks 124 - 128, and start from 129 (after sync state finishes) + +See [how-to](../../misc/state_sync_from_external_storage.md) to learn how to configure your node to state sync. + +### Step 3: Block sync [archival node, normal node] (“downloading blocks”) + +The final step is to start requesting and processing blocks as soon as possible, +hoping to catch up with the chain. + +Block sync will request up to 5 (`MAX_BLOCK_REQUESTS`) blocks at a time - sending +explicit Network BlockRequests for each one. + +After the response (Block) is received - the code will execute the ‘standard’ path +that tries to add this block to the chain (see section below). + +![image](https://user-images.githubusercontent.com/1711539/195892370-b177228b-2520-486a-94fc-67a91978cb58.png) + +In this case, we are processing each transaction for each block - until we catch +up with the chain. + +## Side topic: how blocks are added to the chain? + +A node can receive a Block in two ways: + +* Either by broadcasting - when a new block is produced, its contents are + broadcasted within the network by the nodes +* Or by explicitly sending a BlockRequest to another peer - and getting a Block + in return. + +(in case of broadcasting, the node will automatically reject any Blocks that are +more than 500 (`BLOCK_HORIZON`) blocks away from the current HEAD). + +When a given block is received, the node checks if it can be added to the +current chain. + +If block’s “parent” (`prev_block`) is not in the chain yet - the block gets added +to the orphan list. + +If the parent is already in the chain - we can try to add the block as the head +of the chain. + +Before adding the block, we want to download the chunks for the shards that we +are tracking - so in many cases, we’ll call `missing_chunks` functions that will +try to go ahead and request those chunks. + +**Note:** as an optimization, we’re also sometimes trying to fetch chunks for +the blocks that are in the orphan pool – but only if they are not more than 3 +(`NUM_ORPHAN_ANCESTORS_CHECK`) blocks away from our head. + +We also keep a separate job in client_actor that keeps retrying chunk fetching +from other nodes if the original request fails. + +After all the chunks for a given block are received (we have a separate HashMap +that checks how many chunks are missing for each block) - we’re ready to +process the block and attach it to the chain. + +Afterwards, we look at other entries in the orphan pool to see if any of them +are a direct descendant of the block that we just added - and if yes, we repeat +the process. + +## Catchup + +### The goal of catchup + +Catchup is needed when not all nodes in the network track all shards and nodes +can change the shard they are tracking during different epochs. + +For example, if a node tracks shard 0 at epoch T and tracks shard 1 at epoch T+1, +it actually needs to have the state of shard 1 ready before the beginning of +epoch T+1. We make sure this happens by making the node start downloading +the state for shard 1 at the beginning of epoch T and applying blocks during +epoch T to shard 1’s state. Because downloading state can take time, the +node may have already processed some blocks (for shard 0 at this epoch), so when +the state finishes downloading, the node needs to “catch up” processing these +blocks for shard 1. + +Right now, all nodes do track all shards, so technically we shouldn’t need the +catchup process, but it is still implemented for the future. + +Image below: Example of the node, that tracked only shard 0 in epoch T-1, and +will start tracking shard 0 & 1 in epoch T+1. + +At the beginning of the epoch T, it will initiate the state download (green) and +afterwards will try to ‘catchup’ the blocks (orange). After blocks are caught +up, it will continue processing as normal. + +![image](https://user-images.githubusercontent.com/1711539/195892395-2e12808e-002b-4c04-9505-611288386dc8.png) + +### How catchup interact with normal block processing + +The catchup process has two phases: downloading states for shards that we are +going to care about in epoch T+1 and catching up blocks that have already been +applied. + +When epoch T starts, the node will start downloading states of shards that it +will track for epoch T+1, which it doesn't track already. Downloading happens in +a different thread so `ClientActor` can still process new blocks. Before the +shard states for epoch T+1 are ready, processing new blocks only applies chunks +for the shards that the node is tracking in epoch T. When the shard states for +epoch T+1 finish downloading, the catchup process needs to reprocess the +blocks that have already been processed in epoch T to apply the chunks for the +shards in epoch T+1. We assume that it will be faster than regular block +processing, because blocks are not full and block production has its own delays, +so catchup can finish within an epoch. + +In other words, there are three modes for applying chunks and two code paths, +either through the normal `process_block` (blue) or through `catchup_blocks` +(orange). When `process_block`, either that the shard states for the next epoch +are ready, corresponding to `IsCaughtUp` and all shards the node is tracking in +this, or will be tracking in the next, epoch will be applied, or when the +states are not ready, corresponding to `NotCaughtUp`, then only the shards for +this epoch will be applied. When `catchup_blocks`, shards for the next epoch +will be applied. + + +```rust +enum ApplyChunksMode { + IsCaughtUp, + CatchingUp, + NotCaughtUp, +} +``` + +### How catchup works + +The catchup process is initiated by `process_block`, where we check if the block +is caught up and if we need to download states. The logic works as follows: + +* For the first block in an epoch T, we check if the previous block is caught + up, which signifies if the state of the new epoch is ready. If the previous + block is not caught up, the block will be orphaned and not processed for now + because it is not ready to be processed yet. Ideally, this case should never + happen, because the node will appear stalled until the blocks in the previous + epoch are catching up. +* Otherwise, we start processing blocks for the new epoch T. For the first + block, we always mark it as not caught up and will initiate the process + for downloading states for shards that we are going to care about in epoch + T+1. Info about downloading states is persisted in `DBCol::StateDlInfos`. +* For other blocks, we mark them as not caught up if the previous block is not + caught up. This info is persisted in `DBCol::BlocksToCatchup` which stores + mapping from previous block to vector of all child blocks to catch up. +* Chunks for already tracked shards will be applied during `process_block`, as + we said before mentioning `ApplyChunksMode`. +* Once we downloaded state, we start catchup. It will take blocks from + `DBCol::BlocksToCatchup` in breadth-first search order and apply chunks for + shards which have to be tracked in the next epoch. +* When catchup doesn't see any more blocks to process, `DBCol::BlocksToCatchup` + is cleared, which means that catchup process is finished. + + +The catchup process is implemented through the function `Client::run_catchup`. +`ClientActor` schedules a call to `run_catchup` every 100ms. However, the call +can be delayed if ClientActor has a lot of messages in its actix queue. + +Every time `run_catchup` is called, it checks `DBCol::StateDlInfos` to see +if there are any shard states that should be downloaded. If so, it +initiates the syncing process for these shards. After the state is downloaded, +`run_catchup` will start to apply blocks that need to be caught up. + +One thing to note is that `run_catchup` is located at `ClientActor`, but +intensive work such as applying state parts and applying blocks is actually +offloaded to `SyncJobsActor` in another thread, because we don’t want +`ClientActor` to be blocked by this. `run_catchup` is simply responsible for +scheduling `SyncJobsActor` to do the intensive job. Note that `SyncJobsActor` is +state-less, it doesn’t have write access to the chain. It will return the changes +that need to be made as part of the response to `ClientActor`, and `ClientActor` +is responsible for applying these changes. This is to ensure only one thread +(`ClientActor`) has write access to the chain state. However, this also adds a +lot of limits, for example, `SyncJobsActor` can only be scheduled to apply one +block at a time. Because `run_catchup` is only scheduled to run every 100ms, the +speed of catching up blocks is limited to 100ms per block, even when blocks +applying can be faster. Similar constraints happen to apply state parts. + +### Improvements + +There are three improvements we can make to the current code. + +First, currently we always initiate the state downloading process at the first +block of an epoch, even when there are no new states to be downloaded for the +new epoch. This is unnecessary. + +Second, even though `run_catchup` is scheduled to run every 100ms, the call can +be delayed if ClientActor has messages in its actix queue. A better way to do +this is to move the scheduling of `run_catchup` to `check_triggers`. + +Third, because of how `run_catchup` interacts with `SyncJobsActor`, `run_catchup` +can catch up at most one block every 100 ms. This is because we don’t want to +write to `ChainStore` in multiple threads. However, the changes that catching up +blocks make do not interfere with regular block processing and they can be +processed at the same time. However, to restructure this, we will need to +re-implement `ChainStore` to separate the parts that can be shared among threads +and the part that can’t. diff --git a/docs/architecture/how/tx_receipts.md b/docs/architecture/how/tx_receipts.md new file mode 100644 index 000000000..347206fc7 --- /dev/null +++ b/docs/architecture/how/tx_receipts.md @@ -0,0 +1,146 @@ +# Transactions, Receipts and Chunk Surprises + +We finished the previous article ([Transaction routing](./tx_routing.md)) +where a transaction was successfully added to the soon-to-be block +producer’s mempool. + +In this article, we’ll cover what happens next: +How it is changed into a receipt and executed, potentially creating even +more receipts in the process. + +First, let’s look at the ‘high level view’: + +![image](https://user-images.githubusercontent.com/1711539/198282472-3883dcc1-77ca-452c-b21e-0a7af1435ede.png) + +## Transaction vs Receipt + +As you can see from the image above: + +**Transactions** are ‘external’ communication - they are coming from the +outside. + +**Receipts** are used for ‘internal’ communication (cross shard, cross +contract) - they are created by the block/chunk producers. + +## Life of a Transaction + +If we ‘zoom-in', the chunk producer's work looks like this: + +![image](https://user-images.githubusercontent.com/1711539/198282518-cdeb375e-8f1c-4634-842c-6490020ad9c0.png) + +### Step 1: Process Transaction into receipt + +Once a chunk producer is ready to produce a chunk, it will fetch the +transactions from its mempool, check that they are valid, and if so, prepare to +process them into receipts. + +**Note:** There are additional restrictions (e.g. making sure that we take them in +the right order, that we don’t take too many, etc.) - that you can see in +nomicon’s [transaction page](https://nomicon.io/ChainSpec/Transactions). + +You can see this part in explorer: + +![image](https://user-images.githubusercontent.com/1711539/198282561-c97235a1-93a1-4dc8-b6bc-ee9983376b2c.png) + +### Step 2: Sending receipt to the proper destination + +Once we have a receipt, we have to send it to the proper destination - by adding +it to the `outgoing_receipt` list, which will be forwarded to the chunk +producers from the next block. + +**Note:** There is a special case here - if the sender of the receipt is the +same as the receiver, then the receipt will be added to the `local_receipts` +queue and executed in the same block. + +### Step 3: When an incoming receipt arrives + +(**Note:** this happens in the ‘next’ block) + +When a chunk producer receives an incoming receipt, it will try to execute its +actions (creating accounts, executing function calls etc). + +Such actions might generate additional receipts (for example a contract might +want to call other contracts). All these outputs are added to the outgoing +receipt queue to be executed in the next block. + +If the incoming receipt queue is too large to execute in the current chunk, +the producer will put the remaining receipts onto the ‘delayed’ queue. + +### Step 4: Profit + +When all the ‘dependant’ receipts are executed for a given transaction, we can +consider the transaction to be successful. + +### [Advanced] But reality is more complex + +**Caution:** In the section below, some things are simplified and do not match exactly +how the current code works. + +Let’s quickly also check what’s inside a Chunk: + +```rust +pub struct ShardChunkV2 { + pub chunk_hash: ChunkHash, + pub header: ShardChunkHeader, + pub transactions: Vec, + pub receipts: Vec, // outgoing receipts from 'previous' block +} +``` + +Yes, it is a little bit confusing, that receipts here are NOT the ‘incoming’ +ones for this chunk, but instead the ‘outgoing’ ones from the previous block, i.e. all receipts from shard 0, block B are actually found in shard 0, block B+1. Why?!?! + +This has to do with performance. + +The steps usually followed for producing a block are as follows +1. Chunk producer executes the receipts and creates a chunk. It sends the chunk to other validators. Note that it's the execution/processing of the receipts that usually takes the most time. +2. Validators receive the chunk and validate it before signing the chunk. Validation involves executing/processing of the receipts in the chunk. +3. Once the next block chunk producer receives the validation (signature), only then can it start producing the next chunk. + +#### Simple approach + +First, let’s imagine how the system would look like, if chunks contained things +that we’d expect: + +* list of transactions +* list of incoming receipts +* list of outgoing receipts +* hash of the final state + +This means, that the chunk producer has to compute all this information first, +before sending the chunk to other validators. + +![image](https://user-images.githubusercontent.com/1711539/198282601-383977f1-08dd-45fe-aa19-70556d585034.png) + +Once the other validators receive the chunk, they can start their own processing to +verify those outgoing receipts/final state - and then do the signing. Only then, +can the next chunk producer start creating the next chunk. + +While this approach does work, we can do it faster. + +#### Faster approach + +What if the chunk didn’t contain the ‘output’ state? This changes our ‘mental’ model +a little bit, as now when we’re singing the chunk, we’d actually be +verifying the previous chunk - but that’s the topic for the next article (to be added). + + +For now, imagine if the chunk only had: + +* a list of transactions +* a list of incoming receipts + +In this case, the chunk producer could send the chunk a lot earlier, and +validators (and chunk producer) could do their processing at the same time: + +![image](https://user-images.githubusercontent.com/1711539/198282641-1e728088-6f2b-4cb9-90c9-5eb09304e72a.png) + +Now the last mystery: +Why do we have ‘outgoing’ receipts from previous chunks rather than incoming +to the current one? + +This is yet another optimization. This way the chunk producer can send out the +chunk a little bit earlier - without having to wait for all the other shards. + +But that’s a topic for another article (to be added). + diff --git a/docs/architecture/how/tx_routing.md b/docs/architecture/how/tx_routing.md new file mode 100644 index 000000000..888e8fef1 --- /dev/null +++ b/docs/architecture/how/tx_routing.md @@ -0,0 +1,92 @@ +# Transaction Routing + +We all know that transactions are ‘added’ to the chain - but how do they get +there? + +Hopefully by the end of this article, the image below should make total sense. + +![image](https://user-images.githubusercontent.com/1711539/196204937-d6828382-16df-42bd-b59b-50eb2e6f07af.png) + +## Step 1: Transaction creator/author + +The journey starts with the author of the transaction - who creates the +transaction object (basically list of commands) - and signs them with their +private key. + +Basically, they prepare the payload that looks like this: + +```rust +pub struct SignedTransaction { + pub transaction: Transaction, + pub signature: Signature, +} +``` + +With such a payload, they can go ahead and send it as a JSON-RPC request to ANY +node in the system (they can choose between using ‘sync’ or ‘async’ options). + +From now on, they’ll also be able to query the status of the transaction - by +using the hash of this object. + +**Fun fact:** the `Transaction` object also contains some fields to prevent +attacks: like `nonce` to prevent replay attack, and `block_hash` to limit the +validity of the transaction (it must be added within +`transaction_validity_period` (defined in genesis) blocks of `block_hash`). + +## Step 2: Inside the node + +Our transaction has made it to a node in the system - but most of the nodes +are not validators - which means that they cannot mutate the chain. + +That’s why the node has to forward it to someone who can - the upcoming +validator. + +The node, roughly, does the following steps: + +* verify transaction’s metadata - check signatures etc. (we want to make sure + that we don’t forward bogus data) +* forward it to the ‘upcoming’ validator - currently we pick the validators that + would be a chunk creator in +2, +3, +4 and +8 blocks (this is controlled by + `TX_ROUTING_HEIGHT_HORIZON`) - and send the transaction to all of them. + +## Step 3: En-route to validator/producer + +Great, the node knows to send (forward) the transaction to the validator, but +how does the routing work? How do we know which peer is hosting a validator? + +Each validator is regularly (every `config.ttl_account_id_router`/2 seconds == 30 +minutes in production) broadcasting so called `AnnounceAccount`, which is +basically a pair of `(account_id, peer_id)`, to the whole network. This way each +node knows which `peer_id` to send the message to. + +Then it asks the routing table about the shortest path to the peer, and sends +the `ForwardTx` message to the peer. + +## Step 4: Chunk producer + +When a validator receives such a forwarded transaction, it double-checks that it is +about to produce the block, and if so, it adds the transaction to the mempool +(`TransactionPool`) for this shard, where it waits to be picked up when the chunk +is produced. + +What happens afterwards will be covered in future episodes/articles. + +## Additional notes: + +### Transaction being added multiple times + +But such an approach means, that we’re forwarding the same transaction to multiple +validators (currently 4) - so can it be added multiple times? + +No. Remember that a transaction has a concrete hash which is used as a global +identifier. If the validator sees that the transaction is present in the chain, +it removes it from its local mempool. + +### Can transaction get lost? + +Yes - they can and they do. Sometimes a node doesn’t have a path to a given +validator or it didn’t receive an `AnnouceAccount` for it, so it doesn’t know +where to forward the message. And if this happens to all 4 validators that we +try to send to, then the message can be silently dropped. + +We’re working on adding some monitoring to see how often this happens. diff --git a/docs/architecture/network.md b/docs/architecture/network.md new file mode 100644 index 000000000..2ee908bbc --- /dev/null +++ b/docs/architecture/network.md @@ -0,0 +1,524 @@ +This document describes how our network works. At this moment, it is known to be +somewhat outdated, as we are in the process of refactoring the network protocol +somewhat significantly. + +# 1. Overview + +unc Protocol uses its own implementation of a custom peer-to-peer network. Peers +who join the network are represented by nodes and connections between them by edges. + +The purpose of this document is to describe the inner workings of the `unc-network` +package; and to be used as reference by future engineers to understand the network +code without any prior knowledge. + +# 2. Code structure + +`unc-network` runs on top of the `actor` framework called +[`Actix`](https://actix.rs/docs/). Code structure is split between 4 actors +`PeerManagerActor`, `PeerActor`, `RoutingTableActor`, `EdgeValidatorActor` + +### 2.1 `EdgeValidatorActor` (currently called `EdgeVerifierActor` in the code) + +`EdgeValidatorActor` runs on separate thread. The purpose of this `actor` is to +validate `edges`, where each `edge` represents a connection between two peers, +and it's signed with a cryptographic signature of both parties. The process of +edge validation involves verifying cryptographic signatures, which can be quite +expensive, and therefore was moved to another thread. + +Responsibilities: +* Validating edges by checking whenever cryptographic signatures match. + +### 2.2 `RoutingTableActor` + +`RoutingTableActor` maintains a view of the `P2P network` represented by a set of +nodes and edges. + +In case a message needs to be sent between two nodes, that can be done directly +through a `TCP connection`. Otherwise, `RoutingTableActor` is responsible for pinging +the best path between them. + +Responsibilities: +* Keep set of all edges of `P2P network` called routing table. +* Connects to `EdgeValidatorActor`, and asks for edges to be validated, when + needed. +* Has logic related to exchanging edges between peers. + +### 2.3 `PeerActor` + +Whenever a new connection gets accepted, an instance of `PeerActor` gets +created. Each `PeerActor` keeps a physical `TCP connection` to exactly one +peer. + +Responsibilities: +* Maintaining physical connection. +* Reading messages from peers, decoding them, and then forwarding them to the + right place. +* Encoding messages, sending them to peers on physical layer. +* Routing messages between `PeerManagerActor` and other peers. + +### 2.4 `PeerManagerActor` + +`PeerManagerActor` is the main actor of `unc-network` crate. It acts as a +bridge connecting to the world outside, the other peers, and `ClientActor` and +`ClientViewActor`, which handle processing any operations on the chain. +`PeerManagerActor` maintains information about p2p network via `RoutingTableActor`, +and indirectly, through `PeerActor`, connections to all nodes on the network. +All messages going to other nodes, or coming from other nodes will be routed +through this `Actor`. `PeerManagerActor` is responsible for accepting incoming +connections from the outside world and creating `PeerActors` to manage them. + +Responsibilities: +* Accepting new connections. +* Maintaining the list of `PeerActors`, creating, deleting them. +* Routing information about new edges between `PeerActors` and + `RoutingTableManager`. +* Routing messages between `ViewClient`, `ViewClientActor` and `PeerActors`, and + consequently other peers. +* Maintains `RouteBack` structure, which has information on how to send replies to messages. + +# 3. Code flow - initialization + +First, the `PeerManagerActor` actor gets started. `PeerManagerActor` opens the +TCP server, which listens to incoming connections. It starts the +`RoutingTableActor`, which then starts the `EdgeValidatorActor`. When +an incoming connection gets accepted, it starts a new `PeerActor` +on its own thread. + +# 4. NetworkConfig + +`unc-network` reads configuration from `NetworkConfig`, which is a part of `client +config`. + +Here is a list of features read from config: +* `boot_nodes` - list of nodes to connect to on start. +* `addr` - listening address. +* `max_num_peers` - by default we connect up to 40 peers, current implementation + supports up to 128. + +# 5. Connecting to other peers. + +Each peer maintains a list of known peers. They are stored in the database. If +the database is empty, the list of peers, called boot nodes, will be read from +the `boot_nodes` option in the config. The peer to connect to is chosen at +random from a list of known nodes by the `PeerManagerActor::sample_random_peer` +method. + +# 6. Edges & network - in code representation + +`P2P network` is represented by a list of `peers`, where each `peer` is +represented by a structure `PeerId`, which is defined by the `peer`'s public key +`PublicKey`, and a list of edges, where each edge is represented by the +structure `Edge`. + +Both are defined below. + +# 6.1 PublicKey + +We use two types of public keys: +* a 256 bit `ED25519` public key. +* a 512 bit `Secp256K1` public key. + +Public keys are defined in the `PublicKey` enum, which consists of those two +variants. + +```rust +pub struct ED25519PublicKey(pub [u8; 32]); +pub struct Secp256K1PublicKey([u8; 64]); +pub enum PublicKey { + ED25519(ED25519PublicKey), + SECP256K1(Secp256K1PublicKey), +} +``` + +# 6.2 PeerId + +Each `peer` is uniquely defined by its `PublicKey`, and represented by `PeerId` +struct. + +```rust +pub struct PeerId(PublicKey); +``` + +# 6.3 Edge + +Each `edge` is represented by the `Edge` structure. It contains the following: +* pair of nodes represented by their public keys. +* `nonce` - a unique number representing the state of an edge. Starting with `1`. + Odd numbers represent an active edge. Even numbers represent an edge in which + one of the nodes, confirmed that the edge is removed. +* Signatures from both peers for active edges. +* Signature from one peer in case an edge got removed. + +# 6.4 Graph representation + +`RoutingTableActor` is responsible for storing and maintaining the set of all edges. +They are kept in the `edge_info` data structure of the type `HashSet`. + +```rust +pub struct RoutingTableActor { + /// Collection of edges representing P2P network. + /// It's indexed by `Edge::key()` key and can be search through by calling `get()` function + /// with `(PeerId, PeerId)` as argument. + pub edges_info: HashSet, + /// ... +} +``` + +# 7. Code flow - connecting to a peer - handshake + +When `PeerManagerActor` starts, it starts to listen to a specific port. + +## 7.1 - Step 1 - `monitor_peers_trigger` runs + +`PeerManager` checks if we need to connect to another peer by running the +`PeerManager::is_outbound_bootstrap_needed` method. If `true` we will try to +connect to a new node. Let's call the current node, node `A`. + +## 7.2 - Step 2 - choosing the node to connect to + +Method `PeerManager::sample_random_peer` will be called, and it returns node `B` +that we will try to connect to. + +## 7.3 - Step 3 - `OutboundTcpConnect` message + +`PeerManagerActor` will send itself a message `OutboundTcpConnect` in order +to connect to node `B`. + +```rust +pub struct OutboundTcpConnect { + /// Peer information of the outbound connection + pub target_peer_info: PeerInfo, +} +``` + +## 7.4 - Step 4 - `OutboundTcpConnect` message + +On receiving the message the `handle_msg_outbound_tcp_connect` method will be +called, which calls `TcpStream::connect` to create a new connection. + +## 7.5 - Step 5 - Connection gets established + +Once connection with the outgoing peer gets established. The `try_connect_peer` +method will be called. And then a new `PeerActor` will be created and started. Once +the `PeerActor` starts it will send a `Handshake` message to the outgoing node `B` +over a tcp connection. + +This message contains `protocol_version`, node `A`'s metadata, as well as all +information necessary to create an `Edge`. + +```rust +pub struct Handshake { + /// Current protocol version. + pub(crate) protocol_version: u32, + /// Oldest supported protocol version. + pub(crate) oldest_supported_version: u32, + /// Sender's peer id. + pub(crate) sender_peer_id: PeerId, + /// Receiver's peer id. + pub(crate) target_peer_id: PeerId, + /// Sender's listening addr. + pub(crate) sender_listen_port: Option, + /// Peer's chain information. + pub(crate) sender_chain_info: PeerChainInfoV2, + /// Represents new `edge`. Contains only `none` and `Signature` from the sender. + pub(crate) partial_edge_info: PartialEdgeInfo, +} +``` + +## 7.6 - Step 6 - `Handshake` arrives at node `B` + +Node `B` receives a `Handshake` message. Then it performs various validation +checks. That includes: +* Check signature of edge from the other peer. +* Whenever `nonce` is the edge, send matches. +* Check whether the protocol is above the minimum + `OLDEST_BACKWARD_COMPATIBLE_PROTOCOL_VERSION`. +* Other node `view of chain` state. + +If everything is successful, `PeerActor` will send a `RegisterPeer` message to +`PeerManagerActor`. This message contains everything needed to add `PeerActor` +to the list of active connections in `PeerManagerActor`. + +Otherwise, `PeerActor` will be stopped immediately or after some timeout. + +```rust +pub struct RegisterPeer { + pub(crate) actor: Addr, + pub(crate) peer_info: PeerInfo, + pub(crate) peer_type: PeerType, + pub(crate) chain_info: PeerChainInfoV2, + // Edge information from this node. + // If this is None it implies we are outbound connection, so we need to create our + // EdgeInfo part and send it to the other peer. + pub(crate) this_edge_info: Option, + // Edge information from other node. + pub(crate) other_edge_info: EdgeInfo, + // Protocol version of new peer. May be higher than ours. + pub(crate) peer_protocol_version: ProtocolVersion, +} +``` + +## 7.7 - Step 7 - `PeerManagerActor` receives `RegisterPeer` message - node `B` + +In the `handle_msg_consolidate` method, the `RegisterPeer` message will be validated. +If successful, the `register_peer` method will be called, which adds the `PeerActor` +to the list of connected peers. + +Each connected peer is represented in `PeerActorManager` in `ActivePeer` the data +structure. + + + +```rust +/// Contains information relevant to an active peer. +struct ActivePeer { // will be renamed to `ConnectedPeer` see #5428 + addr: Addr, + full_peer_info: FullPeerInfo, + /// Number of bytes we've received from the peer. + received_bytes_per_sec: u64, + /// Number of bytes we've sent to the peer. + sent_bytes_per_sec: u64, + /// Last time requested peers. + last_time_peer_requested: Instant, + /// Last time we received a message from this peer. + last_time_received_message: Instant, + /// Time where the connection was established. + connection_established_time: Instant, + /// Who started connection. Inbound (other) or Outbound (us). + peer_type: PeerType, +} +``` + +## 7.8 - Step 8 - Exchange routing table part 1 - node `B` + +At the end of the `register_peer` method node `B` will perform a +`RoutingTableSync` sync. Sending the list of known `edges` representing a +full graph, and a list of known `AnnounceAccount`. Those will be +covered later, in their dedicated sections see sections (to be added). + +```rust, ignore +message: PeerMessage::RoutingTableSync(SyncData::edge(new_edge)), +``` + +```rust +/// Contains metadata used for routing messages to particular `PeerId` or `AccountId`. +pub struct RoutingTableSync { // also known as `SyncData` (#5489) + /// List of known edges from `RoutingTableActor::edges_info`. + pub(crate) edges: Vec, + /// List of known `account_id` to `PeerId` mappings. + /// Useful for `send_message_to_account` method, to route message to particular account. + pub(crate) accounts: Vec, +} +``` + +## 7.9 - Step 9 - Exchange routing table part 2 - node `A` + +Upon receiving a `RoutingTableSync` message. Node `A` will reply with its own +`RoutingTableSync` message. + +## 7.10 - Step 10 - Exchange routing table part 2 - node `B` + +Node `B` will get the message from `A` and update its routing table. + +# 8. Adding new edges to routing tables + +This section covers the process of adding new edges, received from another +node, to the routing table. It consists of several steps covered below. + +## 8.1 Step 1 + +`PeerManagerActor` receives `RoutingTableSync` message containing list of new +`edges` to add. `RoutingTableSync` contains list of edges of the P2P network. +This message is then forwarded to `RoutingTableActor`. + +## 8.2 Step 2 + +`PeerManagerActor` forwards those edges to `RoutingTableActor` inside of +the `ValidateEdgeList` struct. + +`ValidateEdgeList` contains: +* list of edges to verify. +* peer who sent us the edges. + +## 8.3 Step 3 + +`RoutingTableActor` gets the `ValidateEdgeList` message. Filters out `edges` +that have already been verified, those that are already in +`RoutingTableActor::edges_info`. + +Then, it updates `edge_verifier_requests_in_progress` to mark that edge +verifications are in progress, and edges shouldn't be pruned from Routing Table +(see section (to be added)). + +Then, after removing already validated edges, the modified message is forwarded +to `EdgeValidatorActor`. + +## 8.4 Step 4 + +`EdgeValidatorActor` goes through the list of all edges. It checks whether all edges +are valid (their cryptographic signatures match, etc.). + +If any edge is not valid, the peer will be banned. + +Edges that are validated are written to a concurrent queue +`ValidateEdgeList::sender`. This queue is used to transfer edges from +`EdgeValidatorActor` back to `PeerManagerActor`. + +## 8.5 Step 5 + +`broadcast_validated_edges_trigger` runs, and gets validated edges from +`EdgeVerifierActor`. + +Every new edge will be broadcast to all connected peers. + +And then, all validated edges received from `EdgeVerifierActor` will be sent +again to `RoutingTableActor` inside `AddVerifiedEdges`. + +## 8.5 Step 6 + +When `RoutingTableActor` receives `RoutingTableMessages::AddVerifiedEdges`, the +method `add_verified_edges_to_routing_table` will be called. It will add edges to +`RoutingTableActor::edges_info` struct, and mark routing table, that it needs +a recalculation (see `RoutingTableActor::needs_routing_table_recalculation`). + +# 9 Routing table computation + +Routing table computation does a few things: +* For each peer `B`, calculates set of peers `|C_b|`, such that each peer is on + the shortest path to `B`. +* Removes unreachable edges from memory and stores them to disk. +* The distance is calculated as the minimum number of nodes on the path from + given node `A`, to each other node on the network. That is, `A` has a distance + of `0` to itself. Its neighbors will have a distance of `1`. The neighbors of + their neighbors will have a distance of `2`, etc. + +## 9.1 Step 1 + +`PeerManagerActor` runs a `update_routing_table_trigger` every +`UPDATE_ROUTING_TABLE_INTERVAL` seconds. + +`RoutingTableMessages::RoutingTableUpdate` message is sent to +`RoutingTableActor` to request routing table re-computation. + +## 9.2 Step 2 + +`RoutingTableActor` receives the message, and then: +* calls `recalculate_routing_table` method, which computes + `RoutingTableActor::peer_forwarding: HashMap>`. For each + `PeerId` on the network, gives a list of connected peers, which are on the + shortest path to the destination. It marks reachable peers in the + `peer_last_time_reachable` struct. +* calls `prune_edges` which removes from memory all the edges that were not + reachable for at least 1 hour, based on the `peer_last_time_reachable` data + structure. Those edges are then stored to disk. + +## 9.3 Step 3 + +`RoutingTableActor` sends a `RoutingTableUpdateResponse` message back to +`PeerManagerActor`. + +`PeerManagerActor` keeps a local copy of `edges_info`, called `local_edges_info` +containing only edges adjacent to current node. +* `RoutingTableUpdateResponse` contains a list of local edges, which + `PeerManagerActor` should remove. +* `peer_forwarding` which represents how to route messages in the P2P network +* `peers_to_ban` represents a list of peers to ban for sending us edges, which failed + validation in `EdgeVerifierActor`. + + +## 9.4 Step 4 + +`PeerManagerActor` receives `RoutingTableUpdateResponse` and then: +* updates local copy of `peer_forwarding`, used for routing messages. +* removes `local_edges_to_remove` from `local_edges_info`. +* bans peers, who sent us invalid edges. + +# 10. Message transportation layers. + +This section describes different protocols of sending messages currently used in +`unc`. + +## 10.1 Messages between Actors. + +`unc` is build on `Actix`'s `actor` +[framework](https://actix.rs/docs/actix/actor). Usually each actor +runs on its own dedicated thread. Some, like `PeerActor` have one thread per +each instance. Only messages implementing `actix::Message`, can be sent +using between threads. Each actor has its own queue; Processing of messages +happens asynchronously. + +We should not leak implementation details into the spec. + +Actix messages can be found by looking for `impl actix::Message`. + +## 10.2 Messages sent through TCP + +unc is using `borsh` serialization to exchange messages between nodes (See +[borsh.io](https://borsh.io/)). We should be careful when making changes to +them. We have to maintain backward compatibility. Only messages implementing +`BorshSerialize`, `BorshDeserialize` can be sent. We also use `borsh` for +database storage. + +## 10.3 Messages sent/received through `chain/jsonrpc` + +unc runs a `json REST server`. (See `actix_web::HttpServer`). All messages sent +and received must implement `serde::Serialize` and `serde::Deserialize`. + +# 11. Code flow - routing a message + +This is the example of the message that is being sent between nodes +[`RawRoutedMessage`](https://github.com/utnet-org/utility/blob/fa8749dc60fe0de8e94c3046571731c622326e9f/chain/network-primitives/src/types.rs#L362). + +Each of these methods have a `target` - that is either the `account_id` or `peer_id` +or hash (which seems to be used only for route back...). If target is the +account - it will be converted using `routing_table.account_owner` to the peer. + +Upon receiving the message, the `PeerManagerActor` +[will sign it](https://github.com/utnet-org/utility/blob/cadf11d5851be7611011b4e89542e11f41f3d827/chain/network/src/peer_manager/peer_manager_actor.rs) +and convert into RoutedMessage (which also have things like TTL etc.). + +Then it will use the `routing_table`, to find the route to the target peer (add +`route_back` if needed) and then send the message over the network as +`PeerMessage::Routed`. Details about routing table computations are covered in +[section 8](#8-adding-new-edges-to-routing-tables). + +When Peer receives this message (as `PeerMessage::Routed`), it will pass it to +PeerManager (as `RoutedMessageFrom`), which would then check if the message is +for the current `PeerActor`. (if yes, it would pass it for the client) and if +not - it would pass it along the network. + +All these messages are handled by `receive_client_message` in Peer. +(`NetworkClientMessags`) - and transferred to `ClientActor` in +(`chain/client/src/client_actor.rs`) + +`NetworkRequests` to `PeerManager` actor trigger the `RawRoutedMessage` for +messages that are meant to be sent to another `peer`. + +`lib.rs` (`ShardsManager`) has a `network_adapter` - coming from the client’s +`network_adapter` that comes from `ClientActor` that comes from the `start_client` call +that comes from `start_with_config` (that creates `PeerManagerActor` - that is +passed as target to `network_recipent`). + +# 12. Database + +### 12.1 Storage of deleted edges + +Every time a group of peers becomes unreachable at the same time; We store edges +belonging to them in components. We remove all of those edges from memory, and +save them to the database. If any of them were to be reachable again, we would +re-add them. This is useful in case there is a network split, to recover edges +if needed. + +Each component is assigned a unique `nonce`, where first one is assigned nonce +0. Each new component gets assigned a consecutive integer. + +To store components, we have the following columns in the DB. +* `DBCol::LastComponentNonce` Stores `component_nonce: u64`, which is the last + used nonce. +* `DBCol::ComponentEdges` Mapping from `component_nonce` to a list of edges. +* `DBCol::PeerComponent` Mapping from `peer_id` to the last component `nonce` it belongs to. + +### 12.2 Storage of `account_id` to `peer_id` mapping + +`ColAccountAnouncements` -> Stores a mapping from `account_id` to a tuple +(`account_id`, `peer_id`, `epoch_id`, `signature`). diff --git a/docs/architecture/next/README.md b/docs/architecture/next/README.md new file mode 100644 index 000000000..c8ca16b2a --- /dev/null +++ b/docs/architecture/next/README.md @@ -0,0 +1,6 @@ +# How uncd will work +The documents under this chapter are talking about the future of NEAR - what we're planning on improving and how. + +(This also means that they can get out of date quickly :-). + +If you have comments, suggestions or want to help us designing and implementing some of these things here - please reach out on Zulip or github. diff --git a/docs/architecture/next/catchup_and_state_sync.md b/docs/architecture/next/catchup_and_state_sync.md new file mode 100644 index 000000000..ac5d2a450 --- /dev/null +++ b/docs/architecture/next/catchup_and_state_sync.md @@ -0,0 +1,59 @@ +This document is still a DRAFT. + + + +This document covers our improvement plans for state sync and catchup. +**Before reading this doc, you should take a look at [How sync works](../how/sync.md)** + + + +State sync is used in two situations: +* when your node is behind for more than 2 epochs (and it is not an archival node) - then rather than trying to apply block by block (that can take hours) - you 'give up' and download the fresh state (a.k.a state sync) and apply blocks from there. +* when you're a block (or chunk) producer - and in the upcoming epoch, you'll have to track a shard that you are not currently tracking. + +In the past (and currently) - the state sync was mostly used in the first scenario (as all block & chunk producers had to track all the shards for security reasons - so they didn't actually have to do catchup at all). + +As we progress towards phase 2 and keep increasing number of shards - the catchup part starts being a lot more critical. When we're running a network with a 100 shards, the single machine is simply not capable of tracking (a.k.a applying all transactions) of all shards - so it will have to track just a subset. And it will have to change this subset almost every epoch (as protocol rebalances the shard-to-producer assignment based on the stakes). + +This means that we have to do some larger changes to the state sync design, as requirements start to differ a lot: +* catchups are high priority (the validator MUST catchup within 1 epoch - otherwise it will not be able to produce blocks for the new shards in the next epoch - and therefore it will not earn rewards). +* a lot more catchups in progress (with lots of shards basically every validator would have to catchup at least one shard at each epoch boundary) - this leads to a lot more potential traffic on the network +* malicious attacks & incentives - the state data can be large and can cause a lot of network traffic. At the same time it is quite critical (see point above), so we'll have to make sure that the nodes are incentivised to provide the state parts upon request. +* only a subset of peers will be available to request the state sync from (as not everyone from our peers will be tracking the shard that we're interested in). + + +## Things that we're actively analysing + +### Performance of state sync on the receiver side +We're looking at the performance of state sync: +* how long does it take to create the parts, +* pro-actively creating the parts as soon as epoch starts +* creating them in parallel +* allowing user to ask for many at once +* allowing user to provide a bitmask of parts that are required (therefore allowing the server to return only the ones that it already cached). + +### Better performance on the requestor side + +Currently the parts are applied only once all of them are downloaded - instead we should try to apply them in parallel - after each part is received. + +When we receive a part, we should announce this information to our peers - so that they know that they can request it from us if they need it. + +## Ideas - not actively working on them yet + +### Better networking (a.k.a Tier 3) +Currently our networking code is picking the peers to connect at random (as most of them are tracking all the shards). With phase2 it will no longer be the case, so we should work on improvements of our peer-selection mechanism. + +In general - we should make sure that we have direct connection to at least a few nodes that are tracking the same shards that we're tracking right now (or that we'll want to track in the near future). + +### Dedicated nodes optimized towards state sync responses +The idea is to create a set of nodes that would specialize in state sync responses (similar to how we have archival nodes today). + +The sub-idea of this, is to store such data on one of the cloud providers (AWS, GCP). + +### Sending deltas instead of full state syncs +In case of catchup, the requesting node might have tracked that shard in the past. So we could consider just sending a delta of the state rather than the whole state. + +While this helps with the amount of data being sent - it might require the receiver to do a lot more work (as the data that it is about to send cannot be easily cached). + + + diff --git a/docs/architecture/next/malicious_chunk_producer_and_phase2.md b/docs/architecture/next/malicious_chunk_producer_and_phase2.md new file mode 100644 index 000000000..7994d914f --- /dev/null +++ b/docs/architecture/next/malicious_chunk_producer_and_phase2.md @@ -0,0 +1,108 @@ +# Malicious producers in phase 2 of sharding. + +In this document, we'll compare the impact of the hypothetical malicious producer on the NEAR system (both in the current setup and how it will work when phase2 is implemented). + +## Current state (Phase 1) + +Let's assume that a malicious chunk producer ``C1`` has produced a bad chunk +and sent it to the block producer at this height ``B1``. + +The block producer IS going to add the chunk to the block (as we don't validate +the chunks before adding to blocks - but only when signing the block - see +[Transcations and receipts - last section](./../how/tx_receipts.md)). + +After this block is produced, it is sent to all the validators to get the +signatures. + +As currently all the validators are tracking all the shards - they will quickly +notice that the chunk is invalid, so they will not sign the block. + +Therefore the next block producer ``B2`` is going to ignore ``B1``'s block, and +select block from ``B0`` as a parent instead. + +So TL;DR - **a bad chunk would not be added to the chain.** + +## Phase 2 and sharding + +Unfortunately things get a lot more complicated, once we scale. + +Let's assume the same setup as above (a single chunk producer ``C1`` being +malicious). But this time, we have 100 shards - each validator is tracking just +a few (they cannot track all - as today - as they would have to run super +powerful machines with > 100 cores). + +So in the similar scenario as above - ``C1`` creates a malicious chunks, and +sends it to ``B1``, which includes it in the block. + +And here's where the complexity starts - as most of the validators will NOT +track the shard which ``C1`` was producing - so they will still sign the block. + +The validators that do track that shard will of course (assuming that they are non-malicious) refuse the sign. But overall, they will be a small majority - so the block is going to get enough signatures and be added to the chain. + +### Challenges, Slashing and Rollbacks + +So we're in a pickle - as a malicious chunk was just added to the chain. And +that's why need to have mechanisms to automatically recover from such situations: +Challenges, Slashing and Rollbacks. + +#### Challenge + +Challenge is a self-contained proof, that something went wrong in the chunk +processing. It must contain all the inputs (with their merkle proof), the code +that was executed, and the outputs (also with merkle proofs). + +Such a challenge allows anyone (even nodes that don't track that shard or have +any state) to verify the validity of the challenge. + +When anyone notices that a current chain contains a wrong transition - they +submit such challenge to the next block producer, which can easily verify it +and it to the next block. + +Then the validators do the verification themselves, and if successful, they +sign the block. + +When such block is successfully signed, the protocol automatically slashes +malicious nodes (more details below) and initiates the rollback to bring the +state back to the state before the bad chunk (so in our case, back to the block +produced by `B0`). + + +#### Slashing + +Slashing is the process of taking away the part of the stake from validators +that are considered malicious. + +In the example above, we'll definitely need to slash the ``C1`` - and potentially also any validators that were tracking that shard and did sign the bad block. + +Things that we'll have to figure out in the future: +* how much do we slash? all of the stake? some part? +* what happens to the slashed stake? is it burned? does it go to some pool? + +#### State rollbacks + +// TODO: add + + +## Problems with the current Phase 2 design + +### Is slashing painful enough? +In the example above, we'd successfully slash the ``C1`` producer - but was it +enough? + +Currently (with 4 shards) you need around 20k NEAR to become a chunk producer. +If we increase the number of shards to 100, it would drop the minimum stake to +around 1k NEAR. + +In such scenario, by sacrificing 1k NEAR, the malicious node can cause the +system to rollback a couple blocks (potentially having bad impact on the bridge +contracts etc). + +On the other side, you could be a non-malicious chunk producer with a corrupted +database (or a nasty bug in the code) - and the effect would be the same - the +chunk that you produced would be marked as malicious, and you'd lose your stake +(which will be a super-scary even for any legitimate validator). + + +So the open question is - can we do something 'smarter' in the protocol to +detect the case, where there is 'just a single' malicious (or buggy) chunk +producer and avoid the expensive rollback? diff --git a/docs/architecture/storage.md b/docs/architecture/storage.md new file mode 100644 index 000000000..5ae0e2d49 --- /dev/null +++ b/docs/architecture/storage.md @@ -0,0 +1,4 @@ +# Storage + +This is our work-in-progress storage documentation. Things are raw and +incomplete. You are encouraged to help improve it, in any capacity you can! \ No newline at end of file diff --git a/docs/architecture/storage/database.md b/docs/architecture/storage/database.md new file mode 100644 index 000000000..3642dab9f --- /dev/null +++ b/docs/architecture/storage/database.md @@ -0,0 +1,37 @@ +# On-Disk Database Format + +We store the database in RocksDB. This document is an attempt to give hints about how to navigate it. + +## RocksDB + +- The column families are defined in `DBCol`, defined in `core/store/src/columns.rs` +- The column families are seen on the rocksdb side as per the `col_name` function defined in `core/store/src/db/rocksdb.rs` + +## The Trie (col5) + +- The trie is stored in column family `State`, number 5 +- In this family, each key is of the form `ShardUId | CryptoHash` where `ShardUId: u64` and `CryptoHash: [u8; 32]` + +## All Historical State Changes (col35) + +- The state changes are stored in column family `StateChanges`, number 35 +- In this family, each key is of the form `BlockHash | Column | AdditionalInfo` where: + + `BlockHash: [u8; 32]` is the block hash for this change + + `Column: u8` is defined near the top of `core/primitives/src/trie_key.rs` + + `AdditionalInfo` depends on `Column` and it can be found in the code for the `TrieKey` struct, same file as `Column` + +### Contract Deployments + +- Contract deployments happen with `Column = 0x01` +- `AdditionalInfo` is the account id for which the contract is being deployed +- The key value contains the contract code alongside other pieces of data. It is possible to extract the contract code by removing everything until the wasm magic number, 0061736D01000000 +- As such, it is possible to dump all the contracts that were ever deployed on-chain using this command on an archival node: + ``` + ldb --db=~/.near/data scan --column_family=col35 --hex | \ + grep -E '^0x.{64}01' | \ + sed 's/0061736D01000000/x/' | \ + sed 's/^.*x/0061736D01000000/' | \ + grep -v ' : ' + ``` + (Note that the last grep is required because not every such value appears to contain contract code) + We should implement a feature to state-viewer that’d allow better visualization of this data, but in the meantime this seems to work. diff --git a/docs/architecture/storage/flat_storage.md b/docs/architecture/storage/flat_storage.md new file mode 100644 index 000000000..f75d5bee9 --- /dev/null +++ b/docs/architecture/storage/flat_storage.md @@ -0,0 +1,221 @@ +# High level overview + +When mainnet launched, the uncd client stored all the chain's state in a single +RocksDB column `DBCol::State`. This column embeds the entire [NEAR state +trie](./trie.md) directly in the key-value database, using roughly +`hash(borsh_encode(trie_node))` as the key to store a `trie_node`. This gives a +content-addressed storage system that can easily self-verify. + +Flat storage is a bit like a database index for the values stored in the trie. +It stores a copy of the data in a more accessible way to speed up the lookup +time. + +Drastically oversimplified, flat storage uses a hashmap instead of a trie. This +reduces the lookup time from `O(d)` to `O(1)` where `d` is the tree depth of the +value. + +But the devil is in the detail. Below is a high-level summary of implementation +challenges, which are the reasons why the above description is an +oversimplification. + +## Time dimension of the state + +The blockchain state is modified with every chunk. Uncd must be able to travel +back in time and resolve key lookups for older versions, as well as travel to +alternative universes to resolve requests for chunks that belong to a different +fork. + +Using the full trie embedding in RocksDB, this is almost trivial. We only need +to know the state root for a chunk and we can start traversing from the root to +any key. As long as we do not delete (garbage collect) unused trie nodes, the +data remains available. The overhead is minimal, too, since all the trie nodes +that have not been changed are shared between tries. + +Enter flat storage territory: A simple hashmap only stores a snapshot. When we +write a new value to the same key, the old value is overwritten and no longer +accessible. A solution to access different versions on each shard is required. + +The minimal solution only tracks the final head and all forks building on top. A +full implementation would also allow replaying older chunks and doing view +calls. But even this minimal solution pulls in all complicated details regarding +consensus and multiplies them with all the problems listed below. + +## Fear of data corruption + +State and FlatState keep a copy of the same data and need to be in sync at all +times. This is a source for errors which any implementation needs to test +properly. Ideally, there are also tools to quickly compare the two and verify +which is correct. + +Note: The trie storage is verifiable by construction of the hashes. The flat +state is not directly verifiable. But it is possible to reconstruct the full +trie just from the leaf values and use that for verification. + +## Gas accounting requires a protocol change + +The trie path we take in a trie lookup affects the gas costs, and hence the +balance subtracted from the user. In other words, the lookup algorithm leaks +into the protocol. Hence, we cannot switch between different ways of looking up +state without a protocol change. + +This makes things a whole lot more complicated. We have to do the data migration +and prepare flat storage, while still using trie storage. Keeping flat storage +up to date at this point is pure overhead. And then, at the epoch switch where +the new protocol version begins, we have to start using the new storage in all +clients simultaneously. Anyone that has not finished migration, yet, will fail +to produce a chunk due to invalid gas results. + +In an ideal future, we want to make gas costs independent of the position in the +trie and then this would no longer be a problem. + +## Performance reality check + +Going from `O(d)` to `O(1)` sounds great. But let's look at actual numbers. + +A flat state lookup requires exactly 2 database requests. One for finding the +`ValueRef` and one for dereferencing the value. (Dereferencing only happens if +the value is present and if enough gas was paid to cover reading the potentially +large value, otherwise we short-circuit.) + +A trie lookup requires exactly `d` trie node lookups where `d` is the depth in +the trie, plus one more for dereferencing the final value. + +Clearly, `d + 1` is worse than `1 + 1`, right? Well, as it turns out, we can +cache the trie nodes with surprisingly high effectiveness. In mainnet +workloads (which were often optimized to work well with the trie shape) we +observe a 99% cache hit rate in many cases. + +Combine that with the fact that a typical value for `d` is somewhere between 10 +and 20. Then we may conclude that, in expectation, a trie lookup (`d * 0.01 + 1` +requests) requires less DB requests than a flat state lookup (`1 + 1` requests). + +In practice, however, flat state still has an edge over accessing trie storage +directly. And that is due to two reasons. + +1. DB keys are in better order, leading to more cache hits in RocksDB's block cache. +2. The flat state column is much smaller than the state column. + +We observed a speedup of 100x and beyond for reading a single value from +`DBCol::FlatState` compared to reading it from `DBCol::State`. So that is +clearly a win. But one that is due to the data layout inside RocksDB, not due to +algorithmic improvements. + +## Updating the Merkle tree + +When we update a value in the blockchain state, all ancestor nodes change their +value due to the recursive nature of Merkle trees. + +Updating flat state is easy enough but then we would not know the new state +root. Annoyingly, it is rather important to have the state root in due time, as +it is included in the chunk header. + +To update the state root, we need to read all nodes between the root and all +changed values. At which point, we are doing all the same reads we were trying +to avoid in the first place. + +This makes flat state for writes algorithmically useless, as we still have to do +`O(d)` requests, no matter what. But there are potential benefits from data +locality, as flat state stores values sorted by a trie key instead of a +perfectly random hash. + +For that, we would need a fast index not only for the state value but also for +all intermediate trie nodes. At this point, we would be back to a full embedding +of the trie in a key-value store / hashmap. Just changing the keys we use for +the database. + +Note that we can enjoy the benefits of read-only flat storage to improve read +heavy contracts. But a read-modify-write pattern using this hybrid solution is +strictly worse than the original implementation without flat storage. + +# Implementation status and future ideas + +As of March 2023, we have implemented a read-only flat storage that only works +for the frontier of non-final blocks and the final block itself. Archival calls +and view calls still use the trie directly. + +## Things we have solved + +We are fairly confident we have solved the time-travel issues, the data +migration / protocol upgrade problems, and got a decent handle on avoiding +accidental data corruption. + +This improves the worst case (no cache hits) for reads dramatically. And it +paves the way for further improvements, as we have now a good understanding of +all these seemingly-simple but actually-really-hard problems. + +## Flat state for writes + +How to use flat storage for writes is not fully designed, yet, but we have some +rough ideas how to do it. But we don't know the performance we should expect. +Algorithmically, it can only get worse but the speedup on RocksDB we found with +the read-only flat storage is promising. But one has to wonder if there are not +also simpler ways to achieve better data locality in RocksDB. + +## Inlining values + +We hope to get a jump in performance by avoiding dereferencing `ValueRef` after +the flat state lookup. At least for small values we could store the value +itself also in flat state. (to be defined what small means) + +This seems very promising because the value dereferencing still happens in the +much slower `DBCol::State`. If we assume small values are the common case, we +would thus expect huge performance improvements for the average case. + +It is not clear yet, if we can also optimize large lookups somehow. If not, we +could at least charge them at a higher rate than we do today, to reflect the +real DB cost better. + +# Code guide + +Here we describe structures used for flat storage implementation. + +## FlatStorage + +This is the main structure which owns information about ValueRefs for all keys from some fixed +shard for some set of blocks. It is shared by multiple threads, so it is guarded by RwLock: + +* Chain thread, because it sends queries like: + * "new block B was processed by chain" - supported by add_block + * "flat storage head can be moved forward to block B" - supported by update_flat_head +* Thread that applies a chunk, because it sends read queries "what is the ValueRef for key for block B" +* View client (not fully decided) + +Requires ChainAccessForFlatStorage on creation because it needs to know the tree of blocks after +the flat storage head, to support get queries correctly. + +## FlatStorageManager + +It holds all FlatStorages which NightshadeRuntime knows about and: + +* provides views for flat storage for some fixed block - supported by new_flat_state_for_shard +* sets initial flat storage state for genesis block - set_flat_storage_for_genesis +* adds/removes/gets flat storage if we started/stopped tracking a shard or need to create a view - create_flat_storage_for_shard, etc. + +## FlatStorageChunkView + +Interface for getting ValueRefs from flat storage for some shard for some fixed block, supported +by get_ref method. + +## FlatStorageCreator + +Creates flat storages for all tracked shards or initiates process of background flat storage creation if for some shard we have only Trie but not FlatStorage. Supports update_status which checks background job results and updates creation statuses and should create flat storage when all jobs are finished. + +## Other notes + +### Chain dependency + +If storage is fully empty, then we need to create flat storage from scratch. FlatStorage is stored +inside NightshadeRuntime, and it itself is stored inside Chain, so we need to create them in the same order +and dependency hierarchy should be the same. But at the same time, we parse genesis file only during Chain +creation. That’s why FlatStorageManager has set_flat_storage_for_genesis method which is called +during Chain creation. + +### Regular block processing vs. catchups + +For these two usecases we have two different flows: first one is handled in Chain.postprocess_block, +the second one in Chain.block_catch_up_postprocess. Both, when results of applying chunk are ready, +should call Chain.process_apply_chunk_result → RuntimeAdapter.get_flat_storage_for_shard → +FlatStorage.add_block, and when results of applying ALL processed/postprocessed chunks are ready, +should call RuntimeAdapter.get_flat_storage_for_shard → FlatStorage.update_flat_head. + +(because applying some chunk may result in error and we may need to exit there without updating flat head - ?) diff --git a/docs/architecture/storage/flow.md b/docs/architecture/storage/flow.md new file mode 100644 index 000000000..d98c3c354 --- /dev/null +++ b/docs/architecture/storage/flow.md @@ -0,0 +1,9 @@ +# Read and Write Flow for Storage Requests + +The storage subsystem of framework is complex and has many layers. Here we +present the flow of a single read or write request from the transaction runtime +all the way to the OS. As you can see, there are many layers of read-caching and +write-buffering involved. + + +![Diagram with read and write request flow](https://user-images.githubusercontent.com/6342444/215088748-028b754f-16be-4f56-9edd-6ce58ff1c9ef.svg) \ No newline at end of file diff --git a/docs/architecture/storage/trie.md b/docs/architecture/storage/trie.md new file mode 100644 index 000000000..7f5b49ddf --- /dev/null +++ b/docs/architecture/storage/trie.md @@ -0,0 +1,125 @@ +# Trie + +We use Merkle-Patricia Trie to store blockchain state. Trie is persistent, which +means that insertion of new node actually leads to creation of a new path to this +node, and thus root of Trie after insertion will also be represented by a new +object. + +Here we describe its implementation details which are closely related to +Runtime. + +## Main structures + +### Trie + +Trie stores the state - accounts, contract codes, access keys, etc. Each state +item corresponds to the unique trie key. All types of trie keys are described in +the [TrieKey](#triekey) section. You can read more about this structure on +[Wikipedia](https://en.wikipedia.org/wiki/Trie). + +Trie is stored in the RocksDB, which is persistent across node restarts. Trie +communicates with database using `TrieStorage`. On the database level, data is +stored in key-value format in `DBCol::State` column. There are two kinds of +records: + +* trie nodes, for the which key is constructed from shard id and + `RawTrieNodeWithSize` hash, and value is a `RawTrieNodeWithSize` serialized by + a custom algorithm; +* values (encoded contract codes, postponed receipts, etc.), for which the key is + constructed from shard id and the hash of value, which maps to the encoded value. + +So, value can be obtained from `TrieKey` as follows: + +* start from the hash of `RawTrieNodeWithSize` corresponding to the root; +* descend to the needed node using nibbles from `TrieKey`; +* extract underlying `RawTrieNode`; +* if it is a `Leaf` or `Branch`, it should contain the hash of the value; +* get value from storage by its hash and shard id. + +Note that `Trie` is almost never called directly from `Runtime`, modifications +are made using `TrieUpdate`. + +### TrieUpdate + +Provides a way to access storage and record changes to commit in the future. +Update is prepared as follows: + +* changes are made using `set` and `remove` methods, which are added to + `prospective` field, +* call `commit` method which moves `prospective` changes to `committed`, +* call `finalize` method which prepares `TrieChanges` and state changes based on + `committed` field. + +Note that `finalize`, `Trie::insert` and `Trie::update` do not update the +database storage. These functions only modify trie nodes in memory. Instead, +these functions prepare the `TrieChanges` object, and `Trie` is actually updated +when `ShardTries::apply_insertions` is called, which puts new values to +`DBCol::State` part of the key-value database. + +### TrieStorage + +Stores all `Trie` nodes and allows to get serialized nodes by `TrieKey` hash +using the `retrieve_raw_bytes` method. + +There are two major implementations of `TrieStorage`: + +* `TrieCachingStorage` - caches all big values ever read by `retrieve_raw_bytes`. +* `TrieMemoryPartialStorage` - used for validating recorded partial storage. + +Note that these storages use database keys, which are retrieved using hashes of +trie nodes using the `get_key_from_shard_id_and_hash` method. + +### ShardTries + +This is the main struct that is used to access all Tries. There's usually only a single instance of this and it contains stores and caches. We use this to gain access to the `Trie` for a single shard by calling the `get_trie_for_shard` or equivalent methods. + +Each shard within `ShardTries` has their own `cache` and `view_cache`. The `cache` stores the most frequently accessed nodes and is usually used during block production. The `view_cache` is used to serve user request to get data, which usually come in via network. It is a good idea to have an independent cache for this as we can have patterns in accessing user data independent of block production. + +## Primitives + +### TrieKey + +Describes all keys which may be inserted to `Trie`: + +* `Account` +* `ContractCode` +* `AccessKey` +* `ReceivedData` +* `PostponedReceiptId` +* `PendingDataCount` +* `PostponedReceipt` +* `DelayedReceiptIndices` +* `DelayedReceipt` +* `ContractData` + +Each key is uniquely converted to `Vec`. Internally, each such vector is +converted to `NibbleSlice` (nibble is a half of a byte), and each its item +corresponds to one step down in `Trie`. + +### TrieChanges + +Stores result of updating `Trie`. + +* `old_root`: root before updating `Trie`, i.e. inserting new nodes and deleting + old ones, +* `new_root`: root after updating `Trie`, +* `insertions`, `deletions`: vectors of `TrieRefcountChange`, describing all + inserted and deleted nodes. + +### TrieRefcountChange + +Because we remove unused nodes during garbage collection, we need to track +the reference count (`rc`) for each node. Another reason is that we can dedup +values. If the same contract is deployed 1000 times, we only store one contract +binary in storage and track its count. + +This structure is used to update `rc` in the database: + +* `trie_node_or_value_hash` - hash of the trie node or value, used for uniting + with shard id to get DB key, +* `trie_node_or_value` - serialized trie node or value, +* `rc` - change of reference count. + +Note that for all reference-counted records, the actual value stored in DB is +the concatenation of `trie_node_or_value` and `rc`. The reference count is +updated using a custom merge operation `merge_refcounted_records`. diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 000000000..bc81a2f5d --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,13 @@ +[book] +language = "en" +multilingual = false +src = "." +title = "Guide to Nearcore Development" + +[build] +# Let's re-use the existing target dir for all build artifacts. +build-dir = "../target/book" + +[output.html] +git-repository-url = "https://github.com/utnet-org/utility/tree/master/docs" +edit-url-template = "https://github.com/utnet-org/utility/edit/master/docs/{path}" diff --git a/docs/images/architecture.svg b/docs/images/architecture.svg new file mode 100644 index 000000000..cc4aa796d --- /dev/null +++ b/docs/images/architecture.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/logo.gif b/docs/images/logo.gif new file mode 100644 index 000000000..c32f8f852 Binary files /dev/null and b/docs/images/logo.gif differ diff --git a/docs/misc/README.md b/docs/misc/README.md new file mode 100644 index 000000000..eaaf54ee5 --- /dev/null +++ b/docs/misc/README.md @@ -0,0 +1,36 @@ +# Overview + +This chapter holds various assorted bits of docs. If you want to document +something, but don't know where to put it, put it here! + +## Crate Versioning and Publishing + +While all the crates in the workspace are directly unversioned (`v0.0.0`), they +all share a unified variable version in the [workspace manifest](../../Cargo.toml). +This keeps versions consistent across the workspace and informs their versions +at the moment of publishing. + +We also have CI infrastructure set up to automate the publishing process to +crates.io. So, on every merge to master, if there's a version change, it is +automatically applied to all the crates in the workspace and it attempts to +publish the new versions of all non-private crates. All crates that should be +exempt from this process should be marked `private`. That is, they should have +the `publish = false` specification in their package manifest. + +This process is managed by +[cargo-workspaces](https://github.com/pksunkara/cargo-workspaces), with a +[bit of magic](https://github.com/pksunkara/cargo-workspaces/compare/master...miraclx:grouping-and-exclusion#files_bucket) +sprinkled on top. + +## Issue Labels + +Issue labels are of the following format `-` where `` is a +capital letter indicating the type of the label and `` is a hyphened +phrase indicating what this label is about. For example, in the label `C-bug`, +`C` means category and `bug` means that the label is about bugs. Common types +include `C`, which means category, `A`, which means area and `T`, which means team. + +An issue can have multiple labels including which area it touches, which team +should be responsible for the issue, and so on. Each issue should have at least +one label attached to it after it is triaged and the label could be a general +one, such as `C-enhancement` or `C-bug`. diff --git a/docs/misc/state_sync_dump.md b/docs/misc/state_sync_dump.md new file mode 100644 index 000000000..f80c204bb --- /dev/null +++ b/docs/misc/state_sync_dump.md @@ -0,0 +1,92 @@ +# Experimental: Dump State to External Storage + +## Purpose + +[State Sync](../architecture/how/sync.md#step-2-state-sync-normal-node) is being +reworked. + +A new version is available for experimental use. This version gets state parts +from external storage. The following kinds of external storage are supported: +* Local filesystem +* Google Cloud Storage +* Amazon S3 + +A new version of decentralized state sync is work in progress. + +## How-to + +uncd release `1.36.0-rc.1` adds an experimental option to sync state from +external storage. + +See [how-to](state_sync_from_external_storage.md) how to configure your node to +State Sync from External Storage. + +In case you would like to manage your own dumps of State, keep reading. + +### Google Cloud Storage +To enable Google Cloud Storage as your external storage, add this to your +`config.json` file: + +```json +"state_sync": { + "dump": { + "location": { + "GCS": { + "bucket": "my-gcs-bucket", + } + } + } +} +``` + +And run your node with an environment variable `SERVICE_ACCOUNT` or +`GOOGLE_APPLICATION_CREDENTIALS` pointing to the credentials json file +```shell +SERVICE_ACCOUNT=/path/to/file ./uncd run +``` + +### Amazon S3 +To enable Amazon S3 as your external storage, add this to your `config.json` +file: + +```json +"state_sync": { + "dump": { + "location": { + "S3": { + "bucket": "my-aws-bucket", + "region": "my-aws-region" + } + } + } +} +``` + +And run your node with environment variables `AWS_ACCESS_KEY_ID` and +`AWS_SECRET_ACCESS_KEY`: +```shell +AWS_ACCESS_KEY_ID="MY_ACCESS_KEY" AWS_SECRET_ACCESS_KEY="MY_AWS_SECRET_ACCESS_KEY" ./uncd run +``` + +## Dump to a local filesystem + +Add this to your `config.json` file to dump state of every epoch to local +filesystem: + +```json +"state_sync": { + "dump": { + "location": { + "Filesystem": { + "root_dir": "/tmp/state-dump" + } + } + } +} +``` + +In this case you don't need any extra environment variables. Simply run your +node: +```shell +./uncd run +``` diff --git a/docs/misc/state_sync_from_external_storage.md b/docs/misc/state_sync_from_external_storage.md new file mode 100644 index 000000000..87e5086b0 --- /dev/null +++ b/docs/misc/state_sync_from_external_storage.md @@ -0,0 +1,128 @@ +# Experimental: State Sync from External Storage + +## Purpose + +[State Sync](../architecture/how/sync.md#step-2-state-sync-normal-node) is being +reworked. + +A new version is available for experimental use. This version gets state parts +from external storage. The following kinds of external storage are supported: +* Local filesystem +* Google Cloud Storage +* Amazon S3 + +A new version of decentralized state sync is work in progress. + +## How-to + +uncd release `1.36.0-rc.1` adds an experimental option to sync state from +external storage. + +The reference `config.json` file by default enables state sync from external +storage and configures it to get state parts from a location managed by Pagoda. + +Note: to obtain the reference configuration file, download it by running the +following command: +```shell +uncd init --chain-id --download-config --download-genesis +``` + +To create your own State dumps to external storage, see the corresponding [how-to](state_sync_dump.md). + +### Google Cloud Storage + +To enable Google Cloud Storage as your external storage, add the following to +your `config.json` file: + +```json +"state_sync_enabled": true, +"state_sync": { + "sync": { + "ExternalStorage": { + "location": { + "GCS": { + "bucket": "my-gcs-bucket", + } + }, + "num_concurrent_requests": 4, + "num_concurrent_requests_during_catchup": 4, + } + } +}, +"consensus": { + "state_sync_timeout": { + "secs": 30, + "nanos": 0 + } +} +``` + +Then run the `uncd` binary and it will access GCS anonymously: +```shell +./uncd run +``` + +#### Extra Options + +The options suggested above will most likely work fine. + +* `num_concurrent_requests` determines the number of state parts across all +shards that can be downloaded in parallel during state sync. +* `num_concurrent_requests_during_catchup` determines the number of state parts +across all shards that can be downloaded in parallel during catchup. Generally, +this number should not be higher than `num_concurrent_requests`. Keep it +reasonably low to allow the node to process chunks of other shards. +* `consensus.state_sync_timeout` determines the max duration of an attempt to download a +state part. Setting it too low may cause too many unsuccessful attempts. + +### Amazon S3 + +To enable Amazon S3 as your external storage, add the following to your +`config.json` file. +You may add the other mentioned options too. + +```json +"state_sync_enabled": true, +"state_sync": { + "sync": { + "ExternalStorage": { + "location": { + "S3": { + "bucket": "my-aws-bucket", + "region": "my-aws-region" + } + } + } + } +}, +``` + +Then run the `uncd` binary and it will access Amazon S3 anonymously: +```shell +./uncd run +``` + +## Sync from a local filesystem + +To enable, add the following to your `config.json` file. +You may add the other mentioned options too. + +```json +"state_sync_enabled": true, +"state_sync": { + "sync": { + "ExternalStorage": { + "location": { + "Filesystem": { + "root_dir": "/tmp/state-parts" + } + } + } + } +} +``` + +Then run the `uncd` binary: +```shell +./uncd run +``` diff --git a/docs/practices/README.md b/docs/practices/README.md new file mode 100644 index 000000000..c60156bf1 --- /dev/null +++ b/docs/practices/README.md @@ -0,0 +1,4 @@ +# Overview + +This chapter describes various development processes and best practices employed +at framework. diff --git a/docs/practices/docs.md b/docs/practices/docs.md new file mode 100644 index 000000000..103b1b0cd --- /dev/null +++ b/docs/practices/docs.md @@ -0,0 +1,99 @@ +# Documentation + +This chapter describes framework's approach to documentation. There are three +primary types of documentation to keep in mind: + +* [**The NEAR Protocol Specification**](https://nomicon.io) + ([source code](https://github.com/near/NEPs)) is the formal description of + the NEAR protocol. The reference framework implementation and any other NEAR + client implementations must follow this specification. +* [**User docs**](https://docs.near.org) ([source code](https://github.com/near/docs)) + explain what is NEAR and how to participate in the network. In particular, + they contain information pertinent to the users of NEAR: validators and + smart contract developers. +* [**Documentation for framework developers**](https://near.github.io/framework/) + ([source code](https://github.com/utnet-org/utility/tree/master/docs)) is the + book you are reading right now! The target audience here is the contributors + to the main implementation of the NEAR protocol (framework). + +## Overview + +The bulk of the internal docs is within this book. If you want to write some +kind of document, add it here! The [architecture](../architecture/) and +[practices](../practices/) chapters are intended for somewhat up-to-date +normative documents. The [misc](../misc/) chapter holds everything else. + +This book is not intended for user-facing documentation, so don't worry about +proper English, typos, or beautiful diagrams -- just write stuff! It can easily +be improved over time with pull requests. For docs, we use a lightweight review +process and try to merge any improvement as quickly as possible. Rather than +blocking a PR on some stylistic changes, just merge it and submit a follow-up. + +Note the "edit" button at the top-right corner -- super useful for fixing any +typos you spot! + +In addition to the book, we also have some "inline" documentation in the code. +For Rust, it is customary to have a per-crate `README.md` file and include it as +a doc comment via `#![doc = include_str!("../README.md")]` in `lib.rs`. We don't +*require* every item to be documented, but we certainly encourage documenting as +much as possible. If you spend some time refactoring or fixing a function, +consider adding a doc comment (`///`) to it as a drive-by improvement. + +We currently don't render `rustdoc`, see [#7836](https://github.com/utnet-org/utility/issues/7836). + +## Book How To + +We use mdBook to render a bunch of markdown files as a static website with a table +of contents, search and themes. Full docs are [here](https://rust-lang.github.io/mdBook/), +but the basics are very simple. + +To add a new page to the book: + +1. Add a `.md` file somewhere in the + [`./docs`](https://github.com/utnet-org/utility/tree/master/docs) folder. +2. Add a link to that page to the + [`SUMMARY.md`](https://github.com/utnet-org/utility/blob/master/docs/SUMMARY.md). +3. Submit a PR (again, we promise to merge it without much ceremony). + +The doc itself is in vanilla markdown. + +To render documentation locally: + +```console +# Install mdBook +$ cargo install mdbook +$ mdbook serve --open ./docs +``` + +This will generate the book from the docs folder, open it in a browser and +start a file watcher to rebuild the book every time the source files change. + +Note that GitHub's default rendering mostly works just as well, so you don't +need to go out of your way to preview your changes when drafting a page or +reviewing pull requests to this book. + +The book is deployed via the +[book GitHub Action workflow](https://github.com/utnet-org/utility/blob/master/.github/workflows/book.yml). +This workflow runs mdBook and then deploys the result to +[GitHub Pages](https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages). + +For internal docs, you often want to have pretty pictures. We don't currently +have a recommended workflow, but here are some tips: + +* Don't add binary media files to Git to avoid inflating repository size. + Rather, upload images as comments to this super-secret issue + [#7821](https://github.com/utnet-org/utility/issues/7821), and then link to + the images as + + ``` + ![image](https://user-images.githubusercontent.com/1711539/195626792-7697129b-7f9c-4953-b939-0b9bcacaf72c.png) + ``` + + Use a single comment per page with multiple images. + +* Google Docs is an OK way to create technical drawings, you can add a link to + the doc with source to that secret issue as well. + +* There's some momentum around using mermaid.js for diagramming, and there's + an appropriate [plugin](https://github.com/badboy/mdbook-mermaid) for that. + Consider if that's something you might want to use. diff --git a/docs/practices/engaging_and_effective_communication.md b/docs/practices/engaging_and_effective_communication.md new file mode 100644 index 000000000..2445c0a5b --- /dev/null +++ b/docs/practices/engaging_and_effective_communication.md @@ -0,0 +1,70 @@ +## Goal of this doc + +We rely heavily on asynchronous conversation for our day-to-day work via Zulip/Slack/Github. As a result, how we communicate on these asynchronous collaboration tools plays a critical role on our productivity and efficiency. + +In this doc, we go over two scenarios we often face in our daily interaction, list outstanding problems and propose simple ways to resolve them. The proposal is not set in stone and any suggestions and criticisms are welcome as always. + + +## TL;DR; + +Before jumping into scenarios, here is TL;DR; of the proposal. + +* **‘Ack’ a message after reading it (with emoji or comment)**: I am sure you have watched a YouTube video, where a YouTuber asks you to ‘like’ their video. Set aside how YouTube algorithm works with number of likes on a video, knowing that other people saw my content can be very fulfilling and heart-warming. Acknowledging someone’s message with emoji is a simple yet powerful way to show your respect and appreciation to the author who must have given lots of thoughts for their message. + +* **Be explicit about ‘whom you want to get feedback from’**: All of us are busy with our own works and given a limited time, it is not feasible to check out every single on-going conversation. By tagging specific people, you not only help other people spend their time on something more relevant to them, but also can get the response faster. Besides, this is yet another way to send the gesture that their opinions are appreciated. + +* **As a topic owner, help facilitate the conversation**: + * Make sure the thread is razor focused on the main topic and cut the potential detour as early as possible. If a new discussion point is worth continuing, move the relevant messages to a new topic so the discussion can continue in a different channel. + * Occasionally summarize the thread so everyone can get up to speed quickly + * Set the timeline expectation on how you want to make a progress in the discussion and guide people accordingly. + * Once conclusion is reached, share it and resolve the topic. Do not leave it open unnecessarily. + + +## Scenario based deep-dive + +### Scenario 1. Walnut wants to share updates/announcements/news with team members. + +Walnut sends a message on Zulip/Slack. + +*“Hello everyone! we have a new guideline on our engineering practice and want to make sure everyone have a look. Here is the link! Don’t hesitate to leave a comment if you have any suggestion/question. Thanks!”* + +After seeing the message, team members opens the link and read the doc. As everything looks good, no one leaves a comment. +* **Potential issue**: As none of the tool we use has a way for Walnut to figure out who read their message, Walnut starts wondering whether their message was delivered. + * **Proposal**: Leave a comment or add an emoji to ‘ack’ the message. + +### Scenario 2. Nala has an idea and wants to have technical discussion with other engineers to collect feedback and reach to the conclusion. + +Nala starts a new topic on Zulip regarding a storage issue. + +*“I have a new idea to address the storage issue we have been having recently. Here is how it goes. …[idea overview]… But there are several outstanding problems with this approach. … [problem statement]… And here are the potential way to resolve them. …[solution]… I would like to get some feedback from the team and see if we can get aligned.”* +* **Potential issue**: potential stakeholders may not subscribe the stream where the message is posted and never participate in the discussion. + * **Proposal**: Tag people/team whom you want to participate in the discussion or who you think will find the discussion valuable. Pay special attention to the way you tag them though, to not make the topic ‘exclusive’ to those people. On the other hand, avoid using @ all or @ everyone tag as that can be spammy and too broad. + +The message sits there for two days, some people reads the message, but do not participate. +* **Potential issue**: As Nala’s message did not receive any response or confirmation of being read, Nala starts wondering whether no one has read the message or the topic is not interesting to anyone. + * **Proposal**: ‘Ack’ the message by adding an emoji or leave comments. You don’t need to add emoji for every single on-going conversation. Rather, adding an emoji at the last message of the latest thread will do. + +Eventually, Maukoo participates in the discussion. + +*“Hey! Thanks for the great suggestion. Overall it looks good, but I can see several corner cases. …[list of corner cases]… How about we try this? …[alternative suggestion]… This way, I think we can meet in the middle ground and everything should work as expected.”* + +Then network expert Coriander participates as well. + +*“Maukoo’s idea will mostly work, but it introduces additional loads on network. …[issue description]… In the past, we saw the similar issues from other flows and that caused this other problems. …[other problems caused by additional load on network]”* + +Another person Simba also contributes. + +*“Oh, I remember that network issue. I wonder if we can do this. …[suggesting solution]…”* +* **Potential issue**: Discussion is deviated from the original topic and the thread becomes harder to follow. + * **Proposal**: Point out that the conversation is starting to diverge and try to bring the crowd back to the main topic. If the newly presented topic is worth a discussion, it can be done in a separate thread. + +The topic discussion is ongoing for several days. Nala has to take care of other matters so decides to leave the discussion open until they are done with the other matters. +* **Potential issue**: As the discussion gets long, it can be overwhelming for a new person to participate if they have to read 50 previous messages. + * **Proposal**: Occasionally summarizing the discussion up to certain point can be helpful for everyone to quickly gather/refresh context. If it is an important discussion that better be documented, consider creating a Google doc so discussion points can be structured and archived. + +* **Potential issue**: As other participants also have their own jobs to do, they may quickly lose their interest and forget about the discussion. + * **Proposal**: As someone who started the topic, it is your responsibility to make sure that the topic thread stays alive. If you cannot get back to this anytime soon, let the stakeholders know and share when you plan to pick up the discussion again so everyone can set the right expectation. + +Some time later, Nala asynchronously works with other engineers and align on the solution. +* **Potential issue**: From the Zulip topic point of view, the topic is never resolved and discussion is dropped in the middle. As a result, people who were not part of the offline discussion do not know the final state of the topic. + * **Proposal**: Set the high level timeline on when the discussion needs to reach to the next stage (e.g. collect feedback in week 1, finalize solution in week 2, resolve the topic in week 3) and update the topic thread accordingly. Once the discussion is completed, share the outcome and resolve the topic thread. diff --git a/docs/practices/fast_builds.md b/docs/practices/fast_builds.md new file mode 100644 index 000000000..199b4a576 --- /dev/null +++ b/docs/practices/fast_builds.md @@ -0,0 +1,116 @@ +# Fast Builds + +framework is implemented in Rust and is a fairly sizable project, so it takes a +while to build. This chapter collects various tips to make the development +process faster. + +Optimizing build times is a bit of a black art, so please do benchmarks on your +machine to verify that the improvements work for you. Changing some configuration +and making a typo, which prevents it from improving build times is an +extremely common failure mode! + +[Rust Perf Book](https://nnethercote.github.io/perf-book/compile-times.html) +contains a section on compilation times as well! + +## Release Builds and Link Time Optimization + +`cargo build --release` is obviously slower than `cargo build`. We enable full +lto (link-time optimization), so our `-r` builds are very slow, use a lot of +RAM, and don't utilize the available parallelism fully. + +As debug builds are much too slow at runtime for many purposes, we have a custom +profile `--profile dev-release` which is equivalent to `-r`, except that the +time-consuming options such as LTO are disabled, and debug assertions are enabled. + +Use `--profile dev-release` for most local development, or when connecting a +locally built node to a network. Use `-r` for production, or if you want to get +absolute performance numbers. + +## Linker + +By default, `rustc` uses the default system linker, which tends to be quite +slow. Using `lld` (LLVM linker) or `mold` (very new, very fast linker) provides +big wins for many setups. + +I don't know what's the official source of truth for using alternative linkers, +I usually refer to [this +comment](https://github.com/rust-lang/rust/issues/39915#issuecomment-538049306). + +Usually, adding + +```toml +[build] +rustflags = ["-C", "link-arg=-fuse-ld=lld"] +``` + +to `~/.cargo/config` is the most convenient approach. + +`lld` itself can be installed with `sudo apt install lld` (or the equivalent in +the distro/package manager of your choice). + +## Prebuilt RocksDB + +By default, we compile RocksDB (a C++ project) from source during the uncd +build. By linking to a prebuilt copy of RocksDB this work can be avoided +entirely. This is a huge win, especially if you clean the `./target` directory +frequently. + +To use a prebuilt RocksDB, set the `ROCKSDB_LIB_DIR` environment variable to +a location containing `librocksdb.a`: + +```console +$ export ROCKSDB_LIB_DIR=/usr/lib/x86_64-linux-gnu +$ cargo build -p uncd +``` + +Note, that the system must provide a recent version of the library which, +depending on which operating system you’re using, may require installing packages +from a testing branch. For example, on Debian it requires installing +`librocksdb-dev` from the `experimental` repository: + +**Note:** Based on which distro you are using this process will look different. +Please refer to the documentation of the package manager you are using. + +```bash +echo 'deb http://ftp.debian.org/debian experimental main contrib non-free' | + sudo tee -a /etc/apt/sources.list +sudo apt update +sudo apt -t experimental install librocksdb-dev + +ROCKSDB_LIB_DIR=/usr/lib/x86_64-linux-gnu +export ROCKSDB_LIB_DIR +``` + +## Global Compilation Cache + +By default, Rust compiles incrementally, with the incremental cache and +intermediate outputs stored in the project-local `./target` directory. + +The [`sccache`](https://github.com/mozilla/sccache) utility can be used to share +these artifacts between machines or checkouts within the same machine. `sccache` +works by intercepting calls to `rustc` and will fetch the cached outputs from +the global cache whenever possible. This tool can be set up as such: + +```console +$ cargo install sccache +$ export RUSTC_WRAPPER="sccache" +$ export SCCACHE_CACHE_SIZE="30G" +$ cargo build -p uncd +``` + +Refer to the [project’s README](https://github.com/mozilla/sccache) for further +configuration options. + +## IDEs Are Bad For Environment Handling + +Generally, the knobs in this section are controlled either via global +configuration in `~/.cargo/config` or environment variables. + +Environment variables are notoriously easy to lose, especially if you are +working both from a command line and a graphical IDE. Double-check that the +environment within which builds are executed is identical to avoid nasty +failure modes such as full cache invalidation when switching from the +CLI to an IDE or vice-versa. + +[`direnv`](https://direnv.net) sometimes can be used to conveniently manage +project-specific environment variables. diff --git a/docs/practices/protocol_upgrade.md b/docs/practices/protocol_upgrade.md new file mode 100644 index 000000000..d17f772b5 --- /dev/null +++ b/docs/practices/protocol_upgrade.md @@ -0,0 +1,170 @@ +## Protocol Upgrade + +This document describes the entire cycle of how a protocol upgrade is done, from +the initial PR to the final release. It is important for everyone who +contributes to the development of the protocol and its client(s) to understand +this process. + +### Background + +At NEAR, we use the term protocol version to mean the version of the blockchain +protocol and is separate from the version of some specific client (such as framework), +since the protocol version defines the protocol rather than some specific +implementation of the protocol. More concretely, for each epoch, there is a +corresponding protocol version that is agreed upon by validators through +[a voting mechanism](https://github.com/near/NEPs/blob/master/specs/ChainSpec/Upgradability.md). +Our upgrade scheme dictates that protocol version X is backward compatible with +protocol version X-1 so that nodes in the network can seamlessly upgrade to +the new protocol. However, there is **no guarantee** that protocol version X is +backward compatible with protocol version X-2. + +Despite the upgrade mechanism, rolling out a protocol change can be scary, +especially if the change is invasive. For those changes, we may want to have +several months of testing before we are confident that the change itself works +and that it doesn't break other parts of the system. + +### Protocol version voting and upgrade + +When a new uncd version, containing a new protocol version, is released, all node maintainers need +to upgrade their binary. That typically means stopping uncd, downloading or compiling the new uncd +binary and restarting uncd. However the protocol version of the whole network is not immediately +bumped to the new protocol version. Instead a process called voting takes place and determines if and +when the protocol version upgrade will take place. + +Voting is a fully automated process in which all block producers across the network vote in support +or against upgrading the protocol version. The voting happens in the last block every epoch. Upgraded +nodes will begin voting in favour of the new protocol version after a predetermined date. The voting +date is configured by the release owner [like this](https://github.com/utnet-org/utility/commit/9b0275de057a01f87c259580f93e58f746da75aa). +Once at least 80% of the stake votes in favour of the protocol change in the last block of epoch X, the +protocol version will be upgraded in the first block of epoch X+2. + +For mainnet releases, the release on github typically happens on a Monday or Tuesday, the voting +typically happens a week later and the protocol version upgrade happens 1-2 epochs after the voting. This +gives the node maintainers enough time to upgrade their uncd nodes. The node maintainers can upgrade +their nodes at any time between the release and the voting but it is recommended to upgrade soon after the +release. This is to accommodate for any database migrations or miscellaneous delays. + +Starting a uncd node with protocol version voting in the future in a network that is already operating +at that protocol version is supported as well. This is useful in the scenario where there is a mainnet +security release where mainnet has not yet voted or upgraded to the new version. That same binary with +protocol voting date in the future can be released in testnet even though it has already upgraded to +the new protocol version. + +### Nightly Protocol features + +To make protocol upgrades more robust, we introduce the concept of a nightly +protocol version together with the protocol feature flags to allow easy testing +of the cutting-edge protocol changes without jeopardizing the stability of the +codebase overall. The use of the nightly and nightly_protocol for new features +is mandatory while the use of dedicated rust features for new protocol features +is optional and only recommended when necessary. Adding rust features leads to +conditional compilation which is generally not developer friendly. In `Cargo.toml` +file of the crates we have in framework, we introduce rust compile-time features +`nightly_protocol` and `nightly`: + +```toml +nightly_protocol = [] +nightly = [ + "nightly_protocol", + ... +] +``` + +where `nightly_protocol` is a marker feature that indicates that we are on +nightly protocol whereas `nightly` is a collection of new protocol features +which also implies `nightly_protocol`. + +When it is not necessary to use a rust feature for the new protocol feature +the Cargo.toml file will remain unchanged. + +When it is necessary to use a rust feature for the new protocol feature, it +can be added to the Cargo.toml, to the nightly features. For example, when +we introduce EVM as a new protocol change, suppose the current protocol +version is 40, then we would do the following change in Cargo.toml: + + +```toml +nightly_protocol = [] +nightly = [ + "nightly_protocol", + "protocol_features_evm", + ... +] +``` + +In [core/primitives/src/version.rs](https://github.com/utnet-org/utility/blob/master/core/primitives/src/version.rs), we would +change the protocol version by: + +```rust +#[cfg(feature = “nightly_protocol”)] +pub const PROTOCOL_VERSION: u32 = 100; +#[cfg(not(feature = “nightly_protocol”)] +pub const PROTOCOL_VERSION: u32 = 40; +``` + +This way the stable versions remain unaffected after the change. Note that +nightly protocol version intentionally starts at a much higher number to make +the distinction between the stable protocol and nightly protocol clearer. + +To determine whether a protocol feature is enabled, we do the following: + +* We maintain a `ProtocolFeature` enum where each variant corresponds to some + protocol feature. For nightly protocol features, the variant may optionally + be gated by the corresponding rust compile-time feature. +* We implement a function `protocol_version` to return, for each variant, the + corresponding protocol version in which the feature is enabled. +* When we need to decide whether to use the new feature based on the protocol + version of the current network, we can simply compare it to the protocol + version of the feature. To make this simpler, we also introduced a macro + `checked_feature` + +For more details, please refer to +[core/primitives/src/version.rs](https://github.com/utnet-org/utility/blob/master/core/primitives/src/version.rs). + +### Feature Gating + +It is worth mentioning that there are two types of checks related to protocol features: + +* Runtime checks that compare the protocol version of the current epoch and + the protocol version of the feature. Those runtime checks must be used for + both stable and nightly features. +* Compile time checks that check if the rust feature corresponding with the + protocol feature is enabled. This check is optional and can only be used for + nightly features. + + +### Testing + +Nightly protocol features allow us to enable the most bleeding-edge code in some +testing environments. We can choose to enable all nightly protocol features by + +```rust +cargo build -p uncd --release --features nightly +``` + +or enable some specific protocol feature by + +```rust +cargo build -p uncd --release --features nightly_protocol, +``` + +In practice, we have all nightly protocol features enabled for Nayduck tests and +on betanet, which is updated daily. + +### Feature Stabilization + +New protocol features are introduced first as nightly features and when the +author of the feature thinks that the feature is ready to be stabilized, they +should submit a pull request to stabilize the feature using +[this template](../../.github/PULL_REQUEST_TEMPLATE/feature_stabilization.md). +In this pull request, they should do the feature gating, increase the +`PROTOCOL_VERSION` constant (if it hasn't been increased since the last +release), and change the `protocol_version` implementation to map the +stabilized features to the new protocol version. + +A feature stabilization request must be approved by at least **two** +[framework code owners](https://github.com/orgs/near/teams/framework-codeowners). +Unless it is a security-related fix, a protocol feature cannot be included in +any release until at least **one** week after its stabilization. This is to ensure +that feature implementation and stabilization are not rushed. + diff --git a/docs/practices/rust.md b/docs/practices/rust.md new file mode 100644 index 000000000..830bef169 --- /dev/null +++ b/docs/practices/rust.md @@ -0,0 +1,84 @@ +# Rust 🦀 + +This short chapter collects various useful general resources about the Rust +programming language. If you are already familiar with Rust, skip this +chapter. Otherwise, this chapter is for you! + +## Getting Help + +Rust community actively encourages beginners to ask questions, take advantage of that! + +We have a dedicated stream for Rust questions on our Zulip: [Rust +🦀](https://near.zulipchat.com/#narrow/stream/300659-Rust-.F0.9F.A6.80). + +There's a general Rust forum at . + +For a more interactive chat, take a look at Discord: +. + +## Reference Material + +Rust is _very_ well documented. It's possible to learn the whole language and +most of the idioms by just reading the official docs. Starting points are + +* [The Rust Book](https://doc.rust-lang.org/book/) (any resemblance to "Guide to + Nearcore Development" is purely coincidental) +* [Standard Library API](https://doc.rust-lang.org/stable/std/) + +Alternatives are: + +* [Programming + Rust](https://www.amazon.com/Programming-Rust-Fast-Systems-Development/dp/1491927283) + is an alternative book that moves a bit faster. +* [Rust By Example](https://doc.rust-lang.org/rust-by-example/) is a great + resource for learning by doing. + +Rust has some great tooling, which is also documented: + +* [Cargo](https://doc.rust-lang.org/cargo/), the build system. Worth at least skimming through! +* For IDE support, see [IntelliJ Rust](https://www.jetbrains.com/rust/) if you + like JetBrains products or + [rust-analyzer](https://rust-analyzer.github.io/manual.html) if you use any + other editor (fun fact: NEAR was one of the sponsors of rust-analyzer!). +* [Rustup](https://rust-lang.github.io/rustup/) manages versions of Rust + itself. It's unobtrusive, so feel free to skip this. + +## Cheat Sheet + +This is a thing in its category, do check it out: + + + +## Language Mastery + +* [Rust for Rustaceans](https://nostarch.com/rust-rustaceans) — the book to read + after "The Book". +* [Tokio docs](https://tokio.rs/tokio/tutorial) explain asynchronous programming + in Rust (async/await). +* [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/about.html) + codify rules for idiomatic Rust APIs. Note that guidelines apply to _semver + surface_ of libraries, and most of the code in framework is not on the semver + boundary. Still, a lot of insight there! +* [Rustonomicon](https://doc.rust-lang.org/nomicon/) explains `unsafe`. (any + resemblance to is purely coincidental) + +## Selected Blog Posts + +A lot of finer knowledge is hidden away in various dusty corners of Web-2.0. +Here are some favorites: + +* +* +* +* +* + +And on the easiest topic of error handling specifically: + +* +* +* +* + +Finally, as a dessert, the first rust slide deck: +. diff --git a/docs/practices/security_vulnerabilities.md b/docs/practices/security_vulnerabilities.md new file mode 100644 index 000000000..04e7463c4 --- /dev/null +++ b/docs/practices/security_vulnerabilities.md @@ -0,0 +1,48 @@ +## Security Vulnerabilities + +
+ +The intended audience of the information presented here is developers working +on the implementation of NEAR. + +Are you a security researcher? Please report security vulnerabilities to +[security@near.org](mailto:security@near.org). + +
+ +As framework is open source, all of its issues and pull requests are also +publicly tracked on GitHub. However, from time to time, if a security-sensitive +issue is discovered, it cannot be tracked publicly on GitHub. However, we +should promote as similar a development process to work on such issues as +possible. To enable this, below is the high-level process for working on +security-sensitive issues. + +1. There is a [private fork of + framework](https://github.com/utnet-org/utility-private) on GitHub. Access to + this repository is restricted to the set of people who are trusted to work on + and have knowledge about security-sensitive issues in framework. + + This repository can be manually synced with the public framework repository + using the following commands: + + ```console + $ git remote add framework-public git@github.com:utnet-org/utility + $ git remote add framework-private git@github.com:utnet-org/utility-private + $ git fetch framework-public + $ git push framework-private framework-public/master:master + ``` +2. All security-sensitive issues must be created on the private framework + repository. You must also assign one of the `[P-S0, P-S1]` labels to the + issue to indicate the severity of the issue. The two criteria to use to help + you judge the severity are the ease of carrying out the attack and the impact + of the attack. An attack that is easy to do or can have a huge impact should + have the `P-S0` label and `P-S1` otherwise. + +3. All security-sensitive pull requests should also be created on the private + framework repository. Note that once a PR has been approved, it should not be + merged into the private repository. Instead, it should be first merged into + the public repository and then the private fork should be updated using the + steps above. + +4. Once work on a security issue is finished, it needs to be deployed to all the + impacted networks. Please contact the node team for help with this. diff --git a/docs/practices/style.md b/docs/practices/style.md new file mode 100644 index 000000000..22c61d162 --- /dev/null +++ b/docs/practices/style.md @@ -0,0 +1,467 @@ +# Code Style + +This document specifies the code style to use in the framework repository. The +primary goal here is to achieve consistency, maintain it over time, and cut down +on the mental overhead related to style choices. + +Right now, `framework` codebase is not perfectly consistent, and the style +acknowledges this. It guides newly written code and serves as a tie breaker for +decisions. Rewriting existing code to conform 100% to the style is not a goal. +Local consistency is more important: if new code is added to a specific file, +it's more important to be consistent with the file rather than with this style +guide. + +This is a live document, which intentionally starts in a minimal case. When +doing code-reviews, consider if some recurring advice you give could be moved +into this document. + +## Formatting + +Use `rustfmt` for minor code formatting decisions. This rule is enforced by CI + +**Rationale:** `rustfmt` style is almost always good enough, even if not always +perfect. The amount of bikeshedding saved by `rustfmt` far outweighs any +imperfections. + +## Idiomatic Rust + +While the most important thing is to solve the problem at hand, we strive to +implement the solution in idiomatic Rust, if possible. To learn what is +considered idiomatic Rust, a good start are the +[Rust API guidelines](https://rust-lang.github.io/api-guidelines/about.html) +(but keep in mind that `framework` is not a library with public API, not all +advice applies literally). + +When in doubt, ask question in the [Rust +🦀](https://near.zulipchat.com/#narrow/stream/300659-Rust-.F0.9F.A6.80) Zulip +stream or during code review. + +**Rationale:** +- *Consistency*: there's usually only one idiomatic solution amidst many + non-idiomatic ones. +- *Predictability*: you can use the APIs without consulting documentation. +- *Performance, ergonomics and correctness*: language idioms usually reflect + learned truths, which might not be immediately obvious. + +## Style + +This section documents all micro-rules which are not otherwise enforced by +`rustfmt`. + +### Avoid `AsRef::as_ref` + +When you have some concrete type, prefer `.as_str`, `.as_bytes`, `.as_path` over +generic `.as_ref`. Only use `.as_ref` when the type in question is a generic +`T: AsRef`. + +```rust +// GOOD +fn log_validator(account_id: AccountId) { + metric_for(account_id.as_str()) + .increment() +} + +// BAD +fn log_validator(account_id: AccountId) { + metric_for(account_id.as_ref()) + .increment() +} +``` + +Note that `Option::as_ref`, `Result::as_ref` are great, do use them! + +**Rationale:** Readability and churn-resistance. There might be more than one +`AsRef` implementation for a given type (with different `U`s). If a new +implementation is added, some of the `.as_ref()` calls might break. See also +this [issue](https://github.com/rust-lang/rust/issues/62586). + +### Avoid references to `Copy`-types + +Various generic APIs in Rust often return references to data (`&T`). When `T` is +a small `Copy` type like `i32`, you end up with `&i32` while many API expect +`i32`, so dereference has to happen _somewhere_. Prefer dereferencing as early +as possible, typically in a pattern: + +```rust +// GOOD +fn compute(map: HashMap<&'str, i32>) { + if let Some(&value) = map.get("key") { + process(value) + } +} +fn process(value: i32) { ... } + +// BAD +fn compute(map: HashMap<&'str, i32>) { + if let Some(value) = map.get("key") { + process(*value) + } +} +fn process(value: i32) { ... } +``` + +**Rationale:** If the value is used multiple times, dereferencing in the pattern +saves keystrokes. If the value is used exactly once, we just want to be +consistent. Additional benefit of early deref is reduced scope of borrow. + +Note that for some *big* `Copy` types, notably `CryptoHash`, we sometimes use +references for performance reasons. As a rule of thumb, `T` is considered *big* if +`size_of::() > 2 * size_of::()`. + +### Prefer for loops over `for_each` and `try_for_each` methods + +Iterators offer `for_each` and `try_for_each` methods which allow executing +a closure over all items of the iterator. This is similar to using a for loop +but comes with various complications and may lead to less readable code. Prefer +using a loop rather than those methods, for example: + +```rust +// GOOD +for outcome_with_id in result? { + *total_gas_burnt = + safe_add_gas(*total_gas_burnt, outcome_with_id.outcome.gas_burnt)?; + outcomes.push(outcome_with_id); +} + +// BAD +result?.into_iter().try_for_each( + |outcome_with_id: ExecutionOutcomeWithId| -> Result<(), RuntimeError> { + *total_gas_burnt = + safe_add_gas(*total_gas_burnt, outcome_with_id.outcome.gas_burnt)?; + outcomes.push(outcome_with_id); + Ok(()) + }, +)?; +``` + +**Rationale:** The `for_each` and `try_for_each` method don’t play nice with +`break` and `continue` statements nor do they mesh well with async IO (since +`.await` inside of the closure isn’t possible). And while `try_for_each` allows +for the use of question mark operator, one may end up having to uses it twice: +once inside the closure and second time outside the call to `try_for_each`. +Furthermore, usage of the functions often introduce some minor syntax noise. + +There are situations when those methods may lead to more readable code. Common +example are long call chains. Even then such code may evolve with the closure +growing and leading to less readable code. If advantages of using the methods +aren’t clear cut, it’s usually better to err on side of more imperative style. + +Lastly, anecdotally the methods (e.g. when used with `chain` or `flat_map`) may +lead to faster code. This intuitively makes sense but it’s worth to keep in +mind that compilers are pretty good at optimising and in practice may generate +optimal code anyway. Furthermore, optimising code for readability may be more +important (especially outside of hot path) than small performance gains. + +### Prefer `to_string` to `format!("{}")` + +Prefer calling `to_string` method on an object rather than passing it through +`format!("{}")` if all you’re doing is converting it to a `String`. + +```rust +// GOOD +let hash = block_hash.to_string(); +let msg = format!("{}: failed to open", path.display()); + +// BAD +let hash = format!("{block_hash}"); +let msg = path.display() + ": failed to open"; +``` + +**Rationale:** `to_string` is shorter to type and also faster. + +### Import Granularity + +Group imports by module, but not deeper: + +```rust +// GOOD +use std::collections::{hash_map, BTreeSet}; +use std::sync::Arc; + +// BAD - nested groups. +use std::{ + collections::{hash_map, BTreeSet}, + sync::Arc, +}; + +// BAD - not grouped together. +use std::collections::BTreeSet; +use std::collections::hash_map; +use std::sync::Arc; +``` + +This corresponds to `"rust-analyzer.assist.importGranularity": "module"` setting +in rust-analyzer +([docs](https://rust-analyzer.github.io/manual.html#rust-analyzer.assist.importGranularity)). + +**Rationale:** Consistency, matches existing practice. + +### Import Blocks + +Do not separate imports into groups with blank lines. Write a single block of +imports and rely on `rustfmt` to sort them. + +```rust +// GOOD +use crate::types::KnownPeerState; +use borsh::BorshSerialize; +use unc_primitives::utils::to_timestamp; +use unc_store::{DBCol::Peers, Store}; +use rand::seq::SliceRandom; +use std::collections::HashMap; +use std::net::SocketAddr; + +// BAD -- several groups of imports +use std::collections::HashMap; +use std::net::SocketAddr; + +use borsh::BorshSerialize; +use rand::seq::SliceRandom; + +use unc_primitives::utils::to_timestamp; +use unc_store::{DBCol::Peers, Store}; + +use crate::types::KnownPeerState; +``` + +**Rationale:** Consistency, ease of automatic enforcement. Today, stable rustfmt +can't split imports into groups automatically, and doing that manually +consistently is a chore. + +### Derives + +When deriving an implementation of a trait, specify a full path to the traits provided by the +external libraries: + +```rust +// GOOD +#[derive(Copy, Clone, serde::Serialize, thiserror::Error, strum::Display)] +struct Grapefruit; + +// BAD +use serde::Serialize; +use thiserror::Error; +use strum::Display; + +#[derive(Copy, Clone, Serialize, Error, Display)] +struct Banana; +``` + +As an exception to this rule, it is okay to use either style when the derived trait already +includes the name of the library (as would be the case for `borsh::BorshSerialize`.) + +**Rationale:** Specifying a full path to the externally provided derivations here makes it +straightforward to differentiate between the built-in derivations and those provided by the +external crates. The surprise factor for derivations sharing a name with the standard +library traits (`Display`) is reduced and it also acts as natural mechanism to tell apart names +prone to collision (`Serialize`), all without needing to look up the list of imports. + +### Arithmetic integer operations + +Use methods with an appropriate overflow handling over plain arithmetic operators (`+-*/%`) when +dealing with integers. + +``` +// GOOD +a.wrapping_add(b); +c.saturating_sub(2); +d.widening_mul(3); // NB: unstable at the time of writing +e.overflowing_div(5); +f.checked_rem(7); + +// BAD +a + b +c - 2 +d * 3 +e / 5 +f % 7 +``` + +If you’re confident the arithmetic operation cannot fail, +`x.checked_[add|sub|mul|div](y).expect("explanation why the operation is safe")` is a great +alternative, as it neatly documents not just the infallibility, but also _why_ that is the case. + +This convention may be enforced by the `clippy::arithmetic_side_effects` and +`clippy::integer_arithmetic` lints. + +**Rationale:** By default the outcome of an overflowing computation in Rust depends on a few +factors, most notably the compilation flags used. The quick explanation is that in debug mode the +computations may panic (cause side effects) if the result has overflowed, and when built with +optimizations enabled, these computations will wrap-around instead. + +For framework and uncd we have opted to enable the panicking behaviour regardless of the +optimization level. By doing it this we hope to prevent accidental stabilization of protocol +mis-features that depend on incorrect handling of these overflows or similarly scary silent bugs. +The downside to this approach is that any such arithmetic operation now may cause a node to crash, +much like indexing a vector with `a[idx]` may cause a crash when `idx` is out-of-bounds. Unlike +indexing, however, developers and reviewers are not used to treating integer arithmetic operations +with the due suspicion. Having to make a choice, and explicitly spell out, how an overflow case +ought to be handled will result in an easier to review and understand code and a more resilient +project overall. + +## Standard Naming + +* Use `-` rather than `_` in crate names and in corresponding folder names. +* Avoid single-letter variable names especially in long functions. Common `i`, + `j` etc. loop variables are somewhat of an exception but since Rust encourages + use of iterators those cases aren’t that common anyway. +* Follow standard [Rust naming patterns](https://rust-lang.github.io/api-guidelines/naming.html) such as: + * Don’t use `get_` prefix for getter methods. A getter method is one which + returns (a reference to) a field of an object. + * Use `set_` prefix for setter methods. An exception are builder objects + which may use different a naming style. + * Use `into_` prefix for methods which consume `self` and `to_` prefix for + methods which don’t. +* Use `get_block_header` rather than `get_header` for methods which return + a block header. +* Don’t use `_by_hash` suffix for methods which lookup chain objects (blocks, + chunks, block headers etc.) by their hash (i.e. their primary identifier). +* Use `_by_height` and similar suffixes for methods which lookup chain objects + (blocks, chunks, block headers etc.) by their height or other property which + is not their hash. + +**Rationale:** Consistency. + +## Documentation + +When writing documentation in `.md` files, wrap lines at approximately 80 +columns. + +```markdown + +Manually reflowing paragraphs is tedious. Luckily, most editors have this +functionality built in or available via extensions. For example, in Emacs you +can use `fill-paragraph` (M-q), (neo)vim allows rewrapping with `gq`, +and VS Code has `stkb.rewrap` extension. + + +One sentence per-line is also occasionally used for technical writing. +We avoid that format though. +While convenient for editing, it may be poorly legible in unrendered form + + +Definitely don't use soft-wrapping. While markdown mostly ignores source level line breaks, relying on soft wrap makes the source completely unreadable, especially on modern wide displays. +``` + +## [Tracing](https://tracing.rs) + +When emitting events and spans with `tracing` prefer adding variable data via +[`tracing`'s field mechanism](https://docs.rs/tracing/latest/tracing/#recording-fields). + +```rust +// GOOD +debug!( + target: "client", + validator_id = self.client.validator_signer.as_ref().map(|vs| { + tracing::field::display(vs.validator_id()) + }), + %hash, + "block.previous_hash" = %block.header().prev_hash(), + "block.height" = block.header().height(), + %peer_id, + was_requested, + "Received block", +); +``` + +Most apparent violation of this rule will be when the event message utilizes any +form of formatting, as seen in the following example: + +```rust +// BAD +debug!( + target: "client", + "{:?} Received block {} <- {} at {} from {}, requested: {}", + self.client.validator_signer.as_ref().map(|vs| vs.validator_id()), + hash, + block.header().prev_hash(), + block.header().height(), + peer_id, + was_requested +); +``` + +Always specify the `target` explicitly. A good default value to use is the crate +name, or the module path (e.g. `chain::client`) so that events and spans common +to a topic can be grouped together. This grouping can later be used for +customizing which events to output. + +**Rationale:** This makes the events structured – one of the major value add +propositions of the tracing ecosystem. Structured events allow for immediately +actionable data without additional post-processing, especially when using some +of the more advanced tracing subscribers. Of particular interest would be those +that output events as JSON, or those that publish data to distributed event +collection systems such as opentelemetry. Maintaining this rule will also +usually result in faster execution (when logs at the relevant level are enabled.) + +### Spans + +Use the [spans](https://docs.rs/tracing/latest/tracing/#spans) to introduce +context and grouping to and between events instead of manually adding such +information as part of the events themselves. Most of the subscribers ingesting +spans also provide a built-in timing facility, so prefer using spans for measuring +the amount of time a section of code needs to execute. + +Give spans simple names that make them both easy to trace back to code, and to +find a particular span in logs or other tools ingesting the span data. If a +span begins at the top of a function, prefer giving it a name of that function, +otherwise prefer a `snake_case` name. + +Use the regular span API over convenience macros such as `#[instrument]`, as +this allows instrumenting portions of a function without affecting the code +structure: + +```rust +fn compile_and_serialize_wasmer(code: &[u8]) -> Result { + let _span = tracing::debug_span!(target: "vm", "compile_and_serialize_wasmer").entered(); + // ... + // _span will be dropped when this scope ends, terminating the span created above. + // You can also `drop` it manually, to end the span early with `drop(_span)`. +} +``` + +**Rationale:** Much as with events, this makes the information provided by spans +structured and contextual. This information can then be output to tooling in an +industry standard format, and can be interpreted by an extensive ecosystem of +`tracing` subscribers. + +### Event and span levels + +The `INFO` level is enabled by default, use it for information useful for node +operators. The `DEBUG` level is enabled on the canary nodes, use it for +information useful in debugging testnet failures. The `TRACE` level is not +generally enabled, use it for arbitrary debug output. + +## Metrics + +Consider adding metrics to new functionality. For example, how often each type +of error was triggered, how often each message type was processed. + +**Rationale:** Metrics are cheap to increment, and they often provide a significant +insight into operation of the code, almost as much as logging. But unlike logging +metrics don't incur a significant runtime cost. + +### Naming + +Prefix all `framework` metrics with `unc_`. Follow the +[Prometheus naming convention](https://prometheus.io/docs/practices/naming/) +for new metrics. + +**Rationale:** The `unc_` prefix makes it trivial to separate metrics exported +by `framework` from other metrics, such as metrics about the state of the machine +that runs `uncd`. + +### Performance + +In most cases incrementing a metric is cheap enough never to give it a second +thought. However accessing a metric with labels on a hot path needs to be done +carefully. + +If a label is based on an integer, use a faster way of converting an integer +to the label, such as the `itoa` crate. + +For hot code paths, re-use results of `with_label_values()` as much as possible. + +**Rationale:** We've encountered issues caused by the runtime costs of +incrementing metrics before. Avoid runtime costs of incrementing metrics too +often. diff --git a/docs/practices/testing/README.md b/docs/practices/testing/README.md new file mode 100644 index 000000000..14e03dd75 --- /dev/null +++ b/docs/practices/testing/README.md @@ -0,0 +1,179 @@ +# General principles + +1. Every PR needs to have test coverage in place. Sending the code change and + deferring tests for a future change is not acceptable. +2. Tests need to either be sufficiently simple to follow or have good + documentation to explain why certain actions are made and conditions are + expected. +3. When implementing a PR, **make sure to run the new tests with the change + disabled and confirm that they fail**! It is extremely common to have tests + that pass without the change that is being tested. +4. The general rule of thumb for a reviewer is to first review the tests, and + ensure that they can convince themselves that the code change that passes the + tests must be correct. Only then the code should be reviewed. +5. Have the assertions in the tests as specific as possible, + however do not make the tests change-detectors of the concrete implementation. + (assert only properties which are required for correctness). + For example, do not do `assert!(result.is_err())`, expect the specific error instead. + +# Tests hierarchy + +In the NEAR Reference Client we largely split tests into three categories: + +1. **Relatively cheap sanity or fast fuzz tests:** It includes all the `#[test]` + Rust tests not decorated by features. Our repo is configured in such a way + that all such tests are run on every PR and failing at least one of them is + blocking the PR from being merged. + +To run such tests locally run `cargo nextest run --all`. +It requires nextest harness which can be installed by running `cargo install cargo-nextest` first. + +2. **Expensive tests:** This includes all the fuzzy tests that run many iterations, + as well as tests that spin up multiple nodes and run them until they reach a + certain condition. Such tests are decorated with + `#[cfg(feature="expensive-tests")]`. It is not trivial to enable features + that are not declared in the top-level crate, and thus the easiest way to run + such tests is to enable all the features by passing `--all-features` to + `cargo nextest run`, e.g: + +`cargo nextest run --package unc-client -E 'test(=tests::cross_shard_tx::test_cross_shard_tx)' --all-features` + +3. **Python tests:** We have an infrastructure to spin up nodes, both locally and + remotely, in python, and interact with them using RPC. The infrastructure and + the tests are located in the `pytest` folder. The infrastructure is relatively + straightforward, see for example `block_production.py` + [here](https://github.com/utnet-org/utility/blob/master/pytest/tests/sanity/block_production.py). + See the `Test infrastructure` section below for details. + +Expensive and python tests are not part of CI, and are run by a custom nightly +runner. The results of the latest runs are available +[here](https://nayduck.near.org/#/). Today, test runs launch approximately +every 5-6 hours. For the latest results look at the **second** run, since the +first one has some tests still scheduled to run. + +# Test infrastructure + +Different levels of the reference implementation have different infrastructures +available to test them. + +## Client + +The Client is separated from the runtime via a `RuntimeAdapter` trait. +In production, it uses `NightshadeRuntime` which uses real runtime and epoch managers. +To test the client without instantiating runtime and epoch manager, we have a mock runtime +`KeyValueRuntime`. + +Most of the tests in the client work by setting up either a single node (via +`setup_mock()`) or multiple nodes (via `setup_mock_all_validators()`) and then +launching the nodes and waiting for a particular message to occur, with a +predefined timeout. + +For the most basic example of using this infrastructure see `produce_two_blocks` +in +[`tests/process_blocks.rs`](https://github.com/utnet-org/utility/blob/master/chain/client/src/tests/process_blocks.rs). + +1. The callback (`Box::new(move |msg, _ctx, _| { ...`) is what is executed + whenever the client sends a message. The return value of the callback is sent + back to the client, which allows for testing relatively complex scenarios. The + tests generally expect a particular message to occur, in this case, the tests + expect two blocks to be produced. `System::current().stop();` is the way to + stop the test and mark it as passed. +2. `unc_network::test_utils::wait_or_panic(5000);` is how the timeout for the + test is set (in milliseconds). + +For an example of a test that launches multiple nodes, see +`chunks_produced_and_distributed_common` in +[tests/chunks_management.rs](https://github.com/utnet-org/utility/blob/master/chain/client/src/tests/chunks_management.rs). +The `setup_mock_all_validators` function is the key piece of infrastructure here. + +## Runtime + +Tests for Runtime are listed in +[tests/test_cases_runtime.rs](https://github.com/utnet-org/utility/blob/master/integration-tests/src/tests/standard_cases/runtime.rs). + +To run a test, usually, a mock `RuntimeNode` is created via `create_runtime_node()`. +In its constructor, the `Runtime` is created in the +`get_runtime_and_trie_from_genesis` function. + +Inside a test, an abstracted `User` is used for sending specific actions to the +runtime client. The helper functions `function_call`, `deploy_contract`, etc. +eventually lead to the `Runtime.apply` method call. + +For setting usernames during playing with transactions, use default names +`alice_account`, `bob_account`, `eve_dot_alice_account`, etc. + +## Network + + + +## Chain, Epoch Manager, Runtime and other low-level changes + +When building new features in the `chain`, `epoch_manager` and `network` crates, +make sure to build new components sufficiently abstract so that they can be tested +without relying on other components. + +For example, see tests for doomslug +[here](https://github.com/utnet-org/utility/blob/master/chain/chain/src/tests/doomslug.rs), +for network cache +[here](https://github.com/utnet-org/utility/blob/master/chain/network/src/routing/edge_cache/tests.rs), +or for promises in runtime +[here](https://github.com/utnet-org/utility/blob/master/runtime/unc-vm-runner/src/logic/tests/promises.rs). + +## Python tests + +See +[this page](python_tests.md) +for detailed coverage of how to write a python test. + +We have a python library that allows one to create and run python tests. + +To run python tests, from the `framework` repo the first time, do the following: + +```shell +cd pytest +virtualenv . --python=python3 +. .env/bin/activate +pip install -r requirements.txt +python tests/sanity/block_production.py +``` + +This will create a python virtual environment, activate the environment, install +all the required packages specified in the `requirements.txt` file and run the +`tests/sanity/block_production.py` file. After the first time, we only need to +activate the environment and can then run the tests: + +```shell +cd pytest +. .env/bin/activate +python tests/sanity/block_production.py +``` + +Use `pytest/tests/sanity/block_production.py` as the basic example of starting a +cluster with multiple nodes, and doing RPC calls. + +See `pytest/tests/sanity/deploy_call_smart_contract.py` to see how contracts can +be deployed, or transactions called. + +See `pytest/tests/sanity/staking1.py` to see how staking transactions can be +issued. + +See `pytest/tests/sanity/state_sync.py` to see how to delay the launch of the +whole cluster by using `init_cluster` instead of `start_cluster`, and then +launching nodes manually. + +### Enabling adversarial behavior + +To allow testing adversarial behavior, or generally, behaviors that a node should +not normally exercise, we have certain features in the code decorated with +`#[cfg(feature="adversarial")]`. The binary normally is compiled with the +feature disabled, and when compiled with the feature enabled, it traces a +warning on launch. + +The nightly runner runs all the python tests against the binary compiled with +the feature enabled, and thus the python tests can make the binary perform +actions that it normally would not perform. + +The actions can include lying about the known chain height, producing multiple +blocks for the same height, or disabling doomslug. + +See all the tests under `pytest/tests/adversarial` for some examples. diff --git a/docs/practices/testing/python_tests.md b/docs/practices/testing/python_tests.md new file mode 100644 index 000000000..19ac77de1 --- /dev/null +++ b/docs/practices/testing/python_tests.md @@ -0,0 +1,274 @@ +# Python Tests + +To simplify writing integration tests for framework we have a python +infrastructure that allows writing a large variety of tests that run small local +clusters, remove clusters, or run against full-scale live deployments. + +Such tests are written in python and not in Rust (in which the framework itself, +and most of the sanity and fuzz tests, are written) due to the availability of +libraries to easily connect to, remove nodes and orchestrate cloud instances. + +Nearcore itself has several features guarded by a +[feature flag](https://doc.rust-lang.org/1.29.0/book/first-edition/conditional-compilation.html) +that allows the python tests to invoke behaviors otherwise impossible to be +exercised by an honest actor. + +# Basics + +The infrastructure is located in `{framework}/pytest/lib` and the tests themselves +are in subdirectories of `{framework}/pytest/tests`. To prepare the local machine to +run the tests you'd need python3 (python 3.7), and have several dependencies +installed, for which we recommend using virtualenv: + +``` +cd pytest +virtualenv .env --python=python3 +. .env/bin/activate +pip install -r requirements.txt +``` + +The tests are expected to be ran from the `pytest` dir itself. For example, once +the virtualenv is configured: + +``` +cd pytest +. .env/bin/activate +python tests/sanity/block_production.py +``` + +This will run the most basic tests that spin up a small cluster locally and wait +until it produces several blocks. + +## Compiling the client for tests + +The local tests by default expect the binary to be in the default location for a +debug build (`{framework}/target/debug`). Some tests might also expect +test-specific features guarded by a feature flag to be available. To compile the +binary with such features run: + +``` +cargo build -p uncd --features=adversarial +``` + +The feature is called `adversarial` to highlight that the many functions it enables, +outside of tests, would constitute malicious behavior. The node compiled with +such a flag will not start unless an environment variable `ADVERSARY_CONSENT=1` +is set and prints a noticeable warning when it starts, thus minimizing the chance +that an honest participant accidentally launches a node compiled with such +functionality. + +You can change the way the tests run (locally or using Google Cloud), and where +the local tests look for the binary by supplying a config file. For example, if you +want to run tests against a release build, you can create a file with the +following config: + +```json +{"local": true, "unc_root": "../target/release/"} +``` + +and run the test with the following command: + +```shell +unc_PYTEST_CONFIG= python tests/sanity/block_production.py +``` + +# Writing tests + +We differentiate between "regular" tests, or tests that spin up their cluster, +either local or on the cloud, and "mocknet" tests, or tests that run against +an existing live deployment of NEAR. + +In both cases, the test starts by importing the infrastructure and starting or +connecting to a cluster + +## Starting a cluster + +In the simplest case a regular test starts by starting a cluster. The cluster +will run locally by default but can be spun up on the cloud by supplying the +corresponding config. + +```python +import sys +sys.path.append('lib') +from cluster import start_cluster + +nodes = start_cluster(4, 0, 4, None, [["epoch_length", 10], ["block_producer_kickout_threshold", 80]], {}) +``` + +In the example above the first three parameters are `num_validating_nodes`, +`num_observers` and `num_shards`. The third parameter is a config, which generally +should be `None`, in which case the config is picked up from the environment +variable as shown above. + +`start_cluster` will spin up `num_validating_nodes` nodes that are block +producers (with pre-staked tokens), `num_observers` non-validating nodes and +will configure the system to have `num_shards` shards. The fifth argument +changes the genesis config. Each element is a list of some length `n` where the +first `n-1` elements are a path in the genesis JSON file, and the last element +is the value. You'd often want to significantly reduce the epoch length, so that +your test triggers epoch switches, and reduce the kick-out threshold since with +shorter epochs it is easier for a block producer to get kicked out. + +The last parameter is a dictionary from the node ordinal to changes to their +local config. + +Note that `start_cluster` spins up all the nodes right away. Some tests (e.g. +tests that test syncing) might want to configure the nodes but delay their +start. In such a case you will initialize the cluster by calling +`init_cluster` and will run the nodes manually, for example, see +[`state_sync.py`](https://github.com/utnet-org/utility/blob/master/pytest/tests/sanity/state_sync.py) + +## Connecting to a mocknet + +Nodes that run against a mocknet would connect to an existing cluster instead of +running their own. + +```python +import sys +sys.path.append('lib') +from cluster import connect_to_mocknet + +nodes, accounts = connect_to_mocknet(None) +``` + +The only parameter is a config, with `None` meaning to use the config from the +environment variable. The config should have the following format: + +```json +{ + "nodes": [ + {"ip": "(some_ip)", "port": 3030}, + {"ip": "(some_ip)", "port": 3030}, + {"ip": "(some_ip)", "port": 3030}, + {"ip": "(some_ip)", "port": 3030} + ], + "accounts": [ + {"account_id": "node1", "pk": "ed25519:", "sk": "edd25519:"}, + {"account_id": "node2", "pk": "ed25519:", "sk": "edd25519:"} + ] +} +``` + +## Manipulating nodes + +The nodes returned by `start_cluster` and `init_cluster` have certain +convenience functions. You can see the full interface in +`{framework}/pytest/lib/cluster.py`. + +`start(boot_public_key, (boot_ip, boot_port))` starts the node. If both +arguments are `None`, the node will start as a boot node (note that the concept +of a "boot node" is relatively vague in a decentralized system, and from the +perspective of the tests the only requirement is that the graph of "node A +booted from node B" is connected). + +The particular way to get the `boot_ip` and `boot_port` when launching `node1` +with `node2` being its boot node is the following: + +```python +node1.start(node2.node_key.pk, node2.addr()) +``` + +`kill()` shuts down the node by sending it `SIGKILL` + +`reset_data()` cleans up the data dir, which could be handy between the calls to +`kill` and `start` to see if a node can start from a clean state. + +Nodes on the mocknet do not expose `start`, `kill` and `reset_data`. + +## Issuing RPC calls + +Nodes in both regular and mocknet tests expose an interface to issue RPC calls. +In the most generic case, one can just issue raw JSON RPC calls by calling the +`json_rpc` method: + +```python +validator_info = nodes[0].json_rpc('validators', []) +``` + +For the most popular calls, there are convenience functions: + +* `send_tx` sends a signed transaction asynchronously +* `send_tx_and_waits` sends a signed transaction synchronously +* `get_status` returns the current status (the output of the `/status/endpoint`), + which contains e.g. last block hash and height +* `get_tx` returns a transaction by the transaction hash and the recipient ID. + +See all the methods in `{framework}/pytest/lib/cluster.rs` after the definition +of the `json_rpc` method. + +### Signing and sending transactions + +There are two ways to send a transaction. A synchronous way (`send_tx_and_wait`) +sends a tx and blocks the test execution until either the TX is finished, or the +timeout is hit. An asynchronous way (`send_tx` + `get_tx`) sends a TX and then +verifies its result later. Here's an end-to-end example of sending a +transaction: + +```python +# the tx needs to include one of the recent hashes +last_block_hash = nodes[0].get_status()['sync_info']['latest_block_hash'] +last_block_hash_decoded = base58.b58decode(last_block_hash.encode('utf8')) + +# sign the actual transaction +# `fr` and `to` in this case are instances of class `Key`. +# In mocknet tests the list `Key`s for all the accounts are returned by `connect_to_mocknet` +# In regular tests each node is associated with a single account, and its key is stored in the +# `signer_key` field (e.g. `nodes[0].signer_key`) +# `15` in the example below is the nonce. Nonces needs to increase for consecutive transactions +# for the same sender account. +tx = sign_payment_tx(fr, to.account_id, 100, 15, last_block_hash_decoded) + +# Sending the transaction synchronously. `10` is the timeout in seconds. If after 10 seconds the +# outcome is not ready, throws an exception +if want_sync: + outcome = nodes[0].send_tx_and_wait(tx, 10) + +# Sending the transaction asynchronously. +if want_async: + tx_hash = nodes[from_ordinal % len(nodes)].send_tx(tx)['result'] + + # and then sometime later fetch the result... + resp = nodes[0].get_tx(tx_hash, to.account_id, timeout=1) + # and see if the tx has finished + finished = 'result' in resp and 'receipts_outcome' in resp['result'] and len(resp['result']['receipts_outcome']) > 0 +``` + +See +[rpc_tx_forwarding.py](https://github.com/utnet-org/utility/blob/master/pytest/tests/sanity/rpc_tx_forwarding.py) +for an example of signing and submitting a transaction. + +## Adversarial behavior + +Some tests need certain nodes in the cluster to exercise behavior that is +impossible to be invoked by an honest node. For such tests, we provide +functionality that is protected by an "adversarial" feature flag. + +It's an advanced feature, and more thorough documentation is a TODO. Most of the +tests that depend on the feature flag enabled are under +`{framework}/pytest/tests/adversarial`, refer to them for how such features can +be used. Search for code in the `framework` codebase guarded by the "adversarial" +feature flag for an example of how such features are added and exposed. + +## Interfering with the network + +We have a library that allows running a proxy in front of each node that would +intercept all the messages between nodes, deserialize them in python and run a +handler on each one. The handler can then either let the message pass (`return +True`), drop it (`return False`) or replace it (`return `). + +This technique can be used to both interfere with the network (by dropping or +replacing messages), and to inspect messages that flow through the network +without interfering with them. For the latter, note that the handler for each node +runs in a separate `Process`, and thus you need to use `multiprocessing` +primitives if you want the handlers to exchange information with the main test +process, or between each other. + +See the tests that match `tests/sanity/proxy_*.py` for examples. + +# Contributing tests + +We always welcome new tests, especially python tests that use the above +infrastructure. We have a list of test requests +[here](https://github.com/utnet-org/utility/issues?q=is%3Aissue+is%3Aopen+label%3A%22A-testing%22), +but also welcome any other tests that test aspects of the network we haven't +thought about. diff --git a/docs/practices/testing/test_utils.md b/docs/practices/testing/test_utils.md new file mode 100644 index 000000000..8e7b7f081 --- /dev/null +++ b/docs/practices/testing/test_utils.md @@ -0,0 +1,126 @@ +# Cheat sheet/overview of testing utils + +This page covers the different testing utils/libraries that we have for easier +unit testing in Rust. + +## Basics + +### CryptoHash + +To create a new crypto hash: + +```rust +"ADns6sqVyFLRZbSMCGdzUiUPaDDtjTmKCWzR8HxWsfDU".parse().unwrap(); +``` + +### Account + +Also, prefer doing parse + unwrap: + +```rust +let alice: AccountId = "alice.near".parse().unwrap(); +``` + +### Signatures + +In memory signer (generates the key based on a seed). There is a slight preference +to use the seed that is matching the account name. + +This will create a signer for account 'test' using 'test' as a seed. + +```rust +let signer: InMemoryValidatorSigner = create_test_signer("test"); +``` + +### Block + +Use ``TestBlockBuilder`` to create the block that you need. This class allows you to set custom values for most of the fields. + +```rust +let test_block = test_utils::TestBlockBuilder::new(prev, signer).height(33).build(); +``` + +## Store + +Use the in-memory test store in tests: + +```rust +let store = create_test_store(); +``` + +## EpochManager + +See usages of MockEpochManager. Note that this is deprecated. Try to use +EpochManager itself wherever possible. + +## Runtime + +You can use the KeyValueRuntime (instead of the Nightshade one): + +```rust +KeyValueRuntime::new(store, &epoch_manager); +``` + +## Chain + +No fakes or mocks. + +### Chain genesis + +We have a test method: + +```rust +ChainGenesis::test(); +``` + +## Client + +TestEnv - for testing multiple clients (without network): + +```rust +TestEnvBuilder::new(genesis).client(vec!["aa"]).validators(..).epoch_managers(..).build(); +``` + +## Network + +### PeerManager + +To create a PeerManager handler: + +```rust +let pm = peer_manager::testonly::start(...).await; +``` + +To connect to others: + +```rust +pm.connect_to(&pm2.peer_info).await; +``` + +### Events handling + +To wait/handle a given event (as a lot of network code is running in an async fashion): + +```rust +pm.events.recv_util(|event| match event {...}).await; +``` + +## End to End + +### chain, runtime, signer + +In chain/chain/src/test_utils.rs: + +```rust +// Creates 1-validator (test): chain, KVRuntime and a signer +let (chain, runtime, signer) = setup(); +``` + +### block, client actor, view client + +In chain/client/src/test_utils.rs + +```rust +let (block, client, view_client) = setup(MANY_FIELDS); +``` + diff --git a/docs/practices/tracking_issues.md b/docs/practices/tracking_issues.md new file mode 100644 index 000000000..63eace432 --- /dev/null +++ b/docs/practices/tracking_issues.md @@ -0,0 +1,56 @@ +# Tracking issues + +`framework` uses so-called "tracking issues" to coordinate larger pieces of work +(e.g. implementation of new NEPs). Such issues are tagged with the +[`C-tracking-issue` +label](https://github.com/utnet-org/utility/issues?q=is%3Aopen+is%3Aissue+label%3AC-tracking-issue). + +The goal of tracking issues is to serve as a coordination point. They can help +new contributors and other interested parties come up to speed with the current +state of projects. As such, they should link to things like design docs, +todo-lists of sub-issues, existing implementation PRs, etc. + +One can further use tracking issues to: + +- get a feeling for what's happening in `framework` by looking at the set of + open tracking issues. +- find larger efforts to contribute to as tracking issues usually contain + up-for-grab to-do lists. +- follow the progress of specific features by subscribing to the issue on GitHub. + +If you are leading or participating in a larger effort, please create a tracking +issue for your work. + +## Guidelines + +- Tracking issues should be maintained in the `framework` repository. If the + projects are security sensitive, then they should be maintained in the + `framework-private` repository. +- The issues should be kept up-to-date. At a minimum, all new context + should be added as comments, but preferably the original description should be + edited to reflect the current status. +- The issues should contain links to all the relevant design documents + which should also be kept up-to-date. +- The issues should link to any relevant NEP if applicable. +- The issues should contain a list of to-do tasks that should be kept + up-to-date as new work items are discovered and other items are done. This + helps others gauge progress and helps lower the barrier of entry for others to + participate. +- The issues should contain links to relevant Zulip discussions. Prefer + open forums like Zulip for discussions. When necessary, closed forums like + video calls can also be used but care should be taken to document a summary of + the discussions. +- For security-sensitive discussions, use the appropriate private Zulip streams. + +[This issue](https://github.com/utnet-org/utility/issues/7670) is a good example of +how tracking issues should be maintained. + +## Background + +The idea of tracking issues is also used to track project work in the Rust +language. See [this +post](https://internals.rust-lang.org/t/how-the-rust-issue-tracker-works/3951) +for a rough description and +[these](https://github.com/rust-lang/rust/issues/101840) +[issues](https://github.com/rust-lang/rust/issues/100717) for how they are used +in Rust. diff --git a/docs/practices/when_to_use_private_repository.md b/docs/practices/when_to_use_private_repository.md new file mode 100644 index 000000000..43e1fb153 --- /dev/null +++ b/docs/practices/when_to_use_private_repository.md @@ -0,0 +1,36 @@ +# When to use private Github repository + +
+ +The intended audience of the information presented here is developers working +on the implementation of the NEAR protocol. + +Are you a security researcher? Please report security vulnerabilities to +[security@near.org](mailto:security@near.org). + +
+ +## TL;DR; + +Limit usage of framework-private to sensitive issues that need to be kept confidential + + +## Details + +By nature, NEAR protocol ecosystem is community driven effort, welcoming everyone to make contribution; we believe in power of community and openness and bring a greater good. +In this regard, by default, contributors create an issue/PR in a public space, such as [framework](https://github.com/utnet-org/utility). + +However, from time to time, if a sensitive issue is discovered (e.g. security vulnerability), it cannot be tracked publicly; we need to limit the blast radius and conceal the issue from malicious actors. +For this purpose, we maintain a private fork, [framework-private](https://github.com/utnet-org/utility-private), of public repository, framework, without any changes on top, and track such issues/PRs, in essence all sensitive development. + +Due to criticality of its contents, the private repository has limited accessibility and special attention is needed on deciding when to use the private repository. + +Before creating an issue under framework-private, ask the following questions yourself: +* Does it contain information that can be exploited by malicious actors? +* Does it need special attention beyond what's provided from public repository due to its sensitivity? +* Does confidentiality of the issue overrule our principle of public transparency? + +Unless an answer to one of these questions is 'YES', the issue may better reside within public repository. If you are unsure, consult private repo auditors (e.g. @akhi3030, @mm-near) to get their thoughts. + +## Extra +To learn more about the process for creating an issue/PR on framework-private, please visit [link](https://github.com/utnet-org/utility/blob/c308df157bf64a528033b618b4f444d3b9c73f94/docs/practices/security_vulnerabilities.md). diff --git a/docs/practices/workflows/README.md b/docs/practices/workflows/README.md new file mode 100644 index 000000000..5b09f5192 --- /dev/null +++ b/docs/practices/workflows/README.md @@ -0,0 +1,4 @@ +# Workflows + +This chapter documents various ways you can run `uncd` during development: +running a local net, joining a test net, doing benchmarking and load testing. diff --git a/docs/practices/workflows/deploy_a_contract.md b/docs/practices/workflows/deploy_a_contract.md new file mode 100644 index 000000000..15b358860 --- /dev/null +++ b/docs/practices/workflows/deploy_a_contract.md @@ -0,0 +1,269 @@ +# Deploy a Contract + +In this chapter, we'll learn how to build, deploy, and call a minimal smart +contract on our local node. + +## Preparing Ground + +Let's start with creating a fresh local network with an account to which we'll +deploy a contract. You might want to re-read [how to run a node](./run_a_node.md) +to understand what's going one here: + +```console +$ cargo run --profile dev-release -p uncd -- init +$ cargo run --profile dev-release -p uncd -- run +$ unc_ENV=local near create-account alice.test.near --masterAccount test.near +``` + +As a sanity check, querying the state of `alice.test.near` account should work: + +```console +$ unc_ENV=local near state alice.test.near +Loaded master account test.near key from /home/matklad/.near/validator_key.json with public key = ed25519:7tU4NtFozPWLotcfhbT9KfBbR3TJHPfKJeCri8Me6jU7 +Account alice.test.near +{ + amount: '100000000000000000000000000', + block_hash: 'EEMiLrk4ZiRzjNJXGdhWPJfKXey667YBnSRoJZicFGy9', + block_height: 24, + code_hash: '11111111111111111111111111111111', + locked: '0', + storage_paid_at: 0, + storage_usage: 182, + formattedAmount: '100' +} +``` + +## Minimal Contract + +NEAR contracts are [WebAssembly](https://webassembly.org) blobs of bytes. To +create a contract, a contract developer typically uses an SDK for some +high-level programming language, such as JavaScript, which takes care of +producing the right `.wasm`. + +In this guide, we are interested in how things work under the hood, so we'll +do everything manually, and implement a contract in Rust without any help from +SDKs. + +As we are looking for something simple, let's create a contract with a single +"method", `hello`, which returns a `"hello world"` string. To "define a method", +a wasm module should export a function. To "return a value", the contract needs +to interact with the environment to say "hey, this is the value I am returning". +Such "interactions" are carried through host functions, which are quite a bit +like syscalls in traditional operating systems. + +The set of host functions that the contract can import is defined in +[`imports.rs`](https://github.com/utnet-org/utility/blob/aeccaaab334275f6d0a62deabd184675bc3c6a23/runtime/unc-vm-runner/src/imports.rs#L71-L242). + +In this particular case, we need the `value_return` function: + +``` +value_return<[value_len: u64, value_ptr: u64] -> []> +``` + +This means that the `value_return` function takes a pointer to a slice of bytes, +the length of the slice, and returns nothing. If the contract calls this function, +the slice would be considered a result of the function. + +To recap, we want to produce a `.wasm` file with roughly the following content: + +```wasm +(module + (import "env" "value_return" (func $value_return (param i64 i64))) + (func (export "hello") ... )) +``` + +## Cargo Boilerplate + +Armed with this knowledge, we can write Rust code to produce the required WASM. +Before we start doing that, some amount of setup code is required. + +Let's start with creating a new crate: + +```console +$ cargo new hello-near --lib +``` + +To compile to wasm, we also need to add a relevant rustup toolchain: + +```console +$ rustup toolchain add wasm32-unknown-unknown +``` + +Then, we need to tell Cargo that the final artifact we want to get is a +WebAssembly module. + +This requires the following cryptic spell in Cargo.toml: + +```toml +# hello-near/Cargo.toml + +[lib] +crate-type = ["cdylib"] +``` + +Here, we ask Cargo to build a "C dynamic library". When compiling for wasm, +that'll give us a `.wasm` module. This part is a bit confusing, sorry about +that :( + +Next, as we are aiming for minimalism here, we need to disable optional bits +of the Rust runtime. Namely, we want to make our crate `no_std` (this means +that we are not going to use the Rust standard library), set `panic=abort` +as our panic strategy and define a panic handler to abort execution. + +```toml +# hello-near/Cargo.toml + +[package] +name = "hello-near" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[profile.release] +panic = "abort" +``` + +```rust +// hello-near/src/lib.rs + +#![no_std] + +#[panic_handler] +fn panic_handler(_info: &core::panic::PanicInfo) -> ! { + core::arch::wasm32::unreachable() +} +``` + +At this point, we should be able to compile our code to wasm, and it should be +fairly small. Let's do that: + +```console +$ cargo b -r --target wasm32-unknown-unknown + Compiling hello-near v0.1.0 (~/hello-near) + Finished release [optimized] target(s) in 0.24s +$ ls target/wasm32-unknown-unknown/release/hello_near.wasm +.rwxr-xr-x 106 matklad 15 Nov 15:34 target/wasm32-unknown-unknown/release/hello_near.wasm +``` + +106 bytes is pretty small! Let's see what's inside. For that, we'll use +the `wasm-tools` suite of CLI utilities. + +```console +$ cargo install wasm-tools +λ wasm-tools print target/wasm32-unknown-unknown/release/hello_near.wasm +(module + (memory (;0;) 16) + (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) + (global (;1;) i32 i32.const 1048576) + (global (;2;) i32 i32.const 1048576) + (export "memory" (memory 0)) + (export "__data_end" (global 1)) + (export "__heap_base" (global 2)) +) +``` + +## Rust Contract + +Finally, let's implement an actual contract. We'll need an `extern "C"` block to +declare the `value_return` import, and a `#[no_mangle] extern "C"` function to +declare the `hello` export: + +```rust +// hello-near/src/lib.rs + +#![no_std] + +extern "C" { + fn value_return(len: u64, ptr: u64); +} + +#[no_mangle] +pub extern "C" fn hello() { + let msg = "hello world"; + unsafe { value_return(msg.len() as u64, msg.as_ptr() as u64) } +} + +#[panic_handler] +fn panic_handler(_info: &core::panic::PanicInfo) -> ! { + core::arch::wasm32::unreachable() +} +``` + +After building the contract, the output wasm shows us that it's roughly what we +want: + +```console +$ cargo b -r --target wasm32-unknown-unknown + Compiling hello-near v0.1.0 (/home/matklad/hello-near) + Finished release [optimized] target(s) in 0.05s +$ wasm-tools print target/wasm32-unknown-unknown/release/hello_near.wasm +(module + (type (;0;) (func (param i64 i64))) + (type (;1;) (func)) + (import "env" "value_return" (; <- Here's our import. ;) + (func $value_return (;0;) (type 0))) + (func $hello (;1;) (type 1) + i64.const 11 + i32.const 1048576 + i64.extend_i32_u + call $value_return + ) + (memory (;0;) 17) + (global $__stack_pointer (;0;) (mut i32) i32.const 1048576) + (global (;1;) i32 i32.const 1048587) + (global (;2;) i32 i32.const 1048592) + (export "memory" (memory 0)) + (export "hello" (func $hello)) (; <- And export! ;) + (export "__data_end" (global 1)) + (export "__heap_base" (global 2)) + (data $.rodata (;0;) (i32.const 1048576) "hello world") +) +``` + +## Deploying the Contract + +Now that we have the WASM, let's deploy it! + +```console +$ unc_ENV=local near deploy alice.test.near \ + ./target/wasm32-unknown-unknown/release/hello_near.wasm +Loaded master account test.near key from /home/matklad/.near/validator_key.json with public key = ed25519:ChLD1qYic3G9qKyzgFG3PifrJs49CDYeERGsG58yaSoL +Starting deployment. Account id: alice.test.near, node: http://127.0.0.1:3030, helper: http://localhost:3000, file: ./target/wasm32-unknown-unknown/release/hello_near.wasm +Transaction Id GDbTLUGeVaddhcdrQScVauYvgGXxSssEPGUSUVAhMWw8 +To see the transaction in the transaction explorer, please open this url in your browser +http://localhost:9001/transactions/GDbTLUGeVaddhcdrQScVauYvgGXxSssEPGUSUVAhMWw8 +Done deploying to alice.test.near +``` + +And, finally, let's call our contract: + + +```console +$ unc_ENV=local $near call alice.test.near hello --accountId alice.test.near +Scheduling a call: alice.test.near.hello() +Loaded master account test.near key from /home/matklad/.near/validator_key.json with public key = ed25519:ChLD1qYic3G9qKyzgFG3PifrJs49CDYeERGsG58yaSoL +Doing account.functionCall() +Transaction Id 9WMwmTf6pnFMtj1KBqjJtkKvdFXS4kt3DHnYRnbFpJ9e +To see the transaction in the transaction explorer, please open this url in your browser +http://localhost:9001/transactions/9WMwmTf6pnFMtj1KBqjJtkKvdFXS4kt3DHnYRnbFpJ9e +'hello world' +``` + +Note that we pass `alice.test.near` twice: the first time to specify which contract +we are calling, the second time to determine who calls the contract. That is, +the second account is the one that spends tokens. In the following example `bob` +spends NEAR to call the contact deployed to the `alice` account: + + +```console +$ unc_ENV=local $near call alice.test.near hello --accountId bob.test.near +Scheduling a call: alice.test.near.hello() +Loaded master account test.near key from /home/matklad/.near/validator_key.json with public key = ed25519:ChLD1qYic3G9qKyzgFG3PifrJs49CDYeERGsG58yaSoL +Doing account.functionCall() +Transaction Id 4vQKtP6zmcR4Xaebw8NLF6L5YS96gt5mCxc5BUqUcC41 +To see the transaction in the transaction explorer, please open this url in your browser +http://localhost:9001/transactions/4vQKtP6zmcR4Xaebw8NLF6L5YS96gt5mCxc5BUqUcC41 +'hello world' +``` diff --git a/docs/practices/workflows/gas_estimations.md b/docs/practices/workflows/gas_estimations.md new file mode 100644 index 000000000..8d55f8894 --- /dev/null +++ b/docs/practices/workflows/gas_estimations.md @@ -0,0 +1,83 @@ +# Running the Estimator + +This workflow describes how to run the gas estimator byzantine-benchmark suite. +To learn about its background and purpose, refer to [Runtime Parameter +Estimator](../../architecture/gas/estimator.md) in the architecture chapter. + +Type this in your console to quickly run estimations on a couple of action costs. + +```bash +cargo run -p runtime-params-estimator --features required -- \ + --accounts-num 20000 --additional-accounts-num 20000 \ + --iters 3 --warmup-iters 1 --metric time \ + --costs=ActionReceiptCreation,ActionTransfer,ActionCreateAccount,ActionFunctionCallBase +``` + +You should get an output like this. + +``` +[elapsed 00:00:17 remaining 00:00:00] Writing into storage ████████████████████ 20000/20000 +ActionReceiptCreation 4_499_673_502_000 gas [ 4.499674ms] (computed in 7.22s) +ActionTransfer 410_122_090_000 gas [ 410.122µs] (computed in 4.71s) +ActionCreateAccount 237_495_890_000 gas [ 237.496µs] (computed in 4.64s) +ActionFunctionCallBase 770_989_128_914 gas [ 770.989µs] (computed in 4.65s) + + +Finished in 40.11s, output saved to: + + /home/you/utnet-org/utility/costs-2022-11-11T11:11:11Z-e40863c9b.txt +``` + +This shows how much gas a parameter should cost to satisfy the 1ms = 1Tgas rule. +It also shows how much time that corresponds to and how long it took to compute +each of the estimations. + +Note that the above does not produce very accurate results and it can have high +variance as well. It runs an unoptimized binary, the state is small, and the +metric used is wall-clock time which is always prone to variance in hardware and +can be affected by other processes currently running on your system. + +Once your estimation code is ready, it is better to run it with a larger state +and an optimized binary. + +```bash +cargo run --release -p runtime-params-estimator --features required -- \ + --accounts-num 20000 --additional-accounts-num 2000000 \ + --iters 3 --warmup-iters 1 --metric time \ + --costs=ActionReceiptCreation,ActionTransfer,ActionCreateAccount,ActionFunctionCallBase +``` + +You might also want to run a hardware-agnostic estimation using the following +command. It uses `docker` and `qemu` under the hood, so it will be quite a bit +slower. You will need to install `docker` to run this command. + +```bash +cargo run --release -p runtime-params-estimator --features required -- \ + --accounts-num 20000 --additional-accounts-num 2000000 \ + --iters 3 --warmup-iters 1 --metric icount --docker \ + --costs=ActionReceiptCreation,ActionTransfer,ActionCreateAccount,ActionFunctionCallBase +``` + +Note how the output looks a bit different now. The `i`, `r` and `w` values show +instruction count, read IO bytes, and write IO bytes respectively. The IO byte +count is known to be inaccurate. + +``` ++ /host/framework/runtime/runtime-params-estimator/emu-cost/counter_plugin/qemu-x86_64 -plugin file=/host/framework/runtime/runtime-params-estimator/emu-cost/counter_plugin/libcounter.so -cpu Westmere-v1 /host/framework/target/release/runtime-params-estimator --home /.near --accounts-num 20000 --iters 3 --warmup-iters 1 --metric icount --costs=ActionReceiptCreation,ActionTransfer,ActionCreateAccount,ActionFunctionCallBase --skip-build-test-contract --additional-accounts-num 0 --in-memory-db +ActionReceiptCreation 214_581_685_500 gas [ 1716653.48i 0.00r 0.00w] (computed in 6.11s) +ActionTransfer 21_528_212_916 gas [ 172225.70i 0.00r 0.00w] (computed in 4.71s) +ActionCreateAccount 26_608_336_250 gas [ 212866.69i 0.00r 0.00w] (computed in 4.67s) +ActionFunctionCallBase 12_193_364_898 gas [ 97546.92i 0.00r 0.00w] (computed in 2.39s) + + +Finished in 17.92s, output saved to: + + /host/framework/costs-2022-11-01T16:27:36Z-e40863c9b.txt +``` + +The difference between the metrics is discussed in the [Estimation +Metrics](../../architecture/gas/estimator.md#estimation-metrics) chapter. + +You should now be all set up for running estimations on your local machine. Also, +check `cargo run -p runtime-params-estimator --features required -- --help` for +the list of available options. diff --git a/docs/practices/workflows/io_trace.md b/docs/practices/workflows/io_trace.md new file mode 100644 index 000000000..bf09c4946 --- /dev/null +++ b/docs/practices/workflows/io_trace.md @@ -0,0 +1,324 @@ +# IO tracing + +## When should I use IO traces? + +IO traces can be used to identify slow receipts and to understand why they +are slow. Or to detect general inefficiencies in how we use the DB. + +The results give you counts of DB requests and some useful statistics such as +trie node cache hits. It will NOT give you time measurements, use Graphana to +observe those. + +The main uses cases in the past were to estimate the performance of new storage +features (prefetcher, flat state) and to find out why specific contracts produce +slow receipts. + +## Setup + +When compiling uncd (or the paramater estimator) with `feature=io_trace` it +instruments the binary code with fine-grained database operations tracking. + +*Aside: We don't enable it by default because we are afraid the overhead could be +too much, since we store some information for very hot paths such as trie node +cache hits. Although we haven't properly evaluated if it really is a performance +problem.* + +This allows using the `--record-io-trace=/path/to/output.io_trace` CLI flag on +uncd. Run it in combination with the subcommands `uncd run`, `uncd +view-state`, or `runtime-params-estimator` and it will record an IO trace. Make +sure to provide the flag to `uncd` itself, however, not to the subcommands. +(See examples below) + +```bash +# Example command for normal node +# (Careful! This will quickly fill your disk if you let it run.) +cargo build --release -p uncd --features=io_trace +target/release/uncd \ + --record-io-trace=/mnt/disks/some_disk_with_enough_space/my.io_trace \ + run +``` + +```bash +# Example command for state viewer, applying a range of chunks in shard 0 +cargo build --release -p uncd --features=io_trace +target/release/uncd \ + --record-io-trace=75220100-75220101.s0.io_trace \ + view-state apply-range --start-index 75220100 --end-index 75220101 \ + --sequential --shard-id 0 +``` + +```bash +# Example command for params estimator +cargo run --release -p runtime-params-estimator --features=required,io_trace \ +-- --accounts-num 200000 --additional-accounts-num 200000 \ +--iters 3 --warmup-iters 1 --metric time \ +--record-io-trace=tmp.io \ +--costs ActionReceiptCreation +``` + +## IO trace content + +Once you have collected an IO trace, you can inspect its content manually, or +use existing tools to extract statistics. Let's start with the manual approach. + +### Simple example trace: Estimator +An estimator trace typical typically starts something like this: + +``` +commit + SET DbVersion "'VERSION'" size=2 +commit + SET DbVersion "'KIND'" size=3 +apply num_transactions=0 shard_cache_miss=7 + GET State "AAAAAAAAAAB3I0MYevRcExi1ql5PSQX+fuObsPH30yswS7ytGPCgyw==" size=46 + GET State "AAAAAAAAAACGDsmYvNoBGZnc8PzDKoF4F2Dvw3N6XoAlRrg8ezA8FA==" size=107 + GET State "AAAAAAAAAAB3I0MYevRcExi1ql5PSQX+fuObsPH30yswS7ytGPCgyw==" size=46 + GET State "AAAAAAAAAACGDsmYvNoBGZnc8PzDKoF4F2Dvw3N6XoAlRrg8ezA8FA==" size=107 + GET State "AAAAAAAAAAB3I0MYevRcExi1ql5PSQX+fuObsPH30yswS7ytGPCgyw==" size=46 + GET State "AAAAAAAAAACGDsmYvNoBGZnc8PzDKoF4F2Dvw3N6XoAlRrg8ezA8FA==" size=107 + GET State "AAAAAAAAAAB3I0MYevRcExi1ql5PSQX+fuObsPH30yswS7ytGPCgyw==" size=46 +... +``` + +Depending on the source, traces look a bit different at the start. But you +should always see some setup at the beginning and per-chunk workload later on. + +Indentation is used to display the call hierarchy. The `commit` keyword shows +when a commit starts, and all `SET` and `UPDATE_RC` commands that follow with +one level deeper indentation belong to that same database transaction commit. + +Later, you see a group that starts with an `apply` header. It groups all IO +requests that were performed for a call to [`fn +apply`](https://github.com/utnet-org/utility/blob/d38c94ac8e78a5a71c592125dfd47803beff58ce/runtime/runtime/src/lib.rs#L1172) +that applies transactions and receipts of a chunk to the previous state root. + +In the example, you see a list of `GET` requests that belong to that `apply`, +each with the DB key used and the size of the value read. Further, you can read +in the trace that this specific chunk had 0 transactions and that it +cache-missed all 7 of the DB requests it performed to apply this empty chunk. + +### Example trace: Full mainnet node + +Next let's look at an excerpt of an IO trace from a real node on mainnet. + +``` +... +GET State "AQAAAAMAAACm9DRx/dU8UFEfbumiRhDjbPjcyhE6CB1rv+8fnu81bw==" size=9 +GET State "AQAAAAAAAACLFgzRCUR3inMDpkApdLxFTSxRvprJ51eMvh3WbJWe0A==" size=203 +GET State "AQAAAAIAAACXlEo0t345S6PHsvX1BLaGw6NFDXYzeE+tlY2srjKv8w==" size=299 +apply_transactions shard_id=3 + process_state_update + apply num_transactions=3 shard_cache_hit=207 + process_transaction tx_hash=C4itKVLP5gBoAPsEXyEbi67Gg5dvQVugdMjrWBBLprzB shard_cache_hit=57 + GET FlatState "AGxlb25hcmRvX2RheS12aW5jaGlrLm5lYXI=" size=36 + GET FlatState "Amxlb25hcmRvX2RheS12aW5jaGlrLm5lYXICANLByB1merOzxcGB1HI9/L60QvONzOE6ovF3hjYUbhA8" size=36 + process_transaction tx_hash=GRSXC4QCBJHN4hmJiATAbFGt9g5PiksQDNNRaSk666WX shard_cache_miss=3 prefetch_pending=3 shard_cache_hit=35 + GET FlatState "AnJlbGF5LmF1cm9yYQIA5iq407bcLgisCKxQQi47TByaFNe9FOgQg5y2gpU4lEM=" size=36 + process_transaction tx_hash=6bDPeat12pGqA3KEyyg4tJ35kBtRCuFQ7HtCpWoxr8qx shard_cache_miss=2 prefetch_pending=1 shard_cache_hit=21 prefetch_hit=1 + GET FlatState "AnJlbGF5LmF1cm9yYQIAyKT1vEHVesMEvbp2ICA33x6zxfmBJiLzHey0ZxauO1k=" size=36 + process_receipt receipt_id=GRB3skohuShBvdGAoEoR3SdJJw7MwCxxscJHKLdPoYUC predecessor=1663adeba849fb7c26195678e1c5378278e5caa6325d4672246821d8e61bb160 receiver=token.sweat id=GRB3skohuShBvdGAoEoR3SdJJw7MwCxxscJHKLdPoYUC shard_cache_too_large=1 shard_cache_miss=1 shard_cache_hit=38 + GET FlatState "AXRva2VuLnN3ZWF0" size=36 + GET State "AQAAAAMAAADVYp4vtlIbDoVhji22CZOEaxVWVTJKASq3iMvpNEQVDQ==" size=206835 + input + register_len + read_register + storage_read READ key='STATE' size=70 tn_db_reads=20 tn_mem_reads=0 shard_cache_hit=21 + register_len + read_register + attached_deposit + predecessor_account_id + register_len + read_register + sha256 + read_register + storage_read READ key=dAAxagYMOEb01+56sl9vOM0yHbZRPSaYSL3zBXIfCOi7ow== size=16 tn_db_reads=10 tn_mem_reads=19 shard_cache_hit=11 + GET FlatState "CXRva2VuLnN3ZWF0LHQAMWoGDDhG9NfuerJfbzjNMh22UT0mmEi98wVyHwjou6M=" size=36 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dAAxagYMOEb01+56sl9vOM0yHbZRPSaYSL3zBXIfCOi7ow== size=16 tn_db_reads=0 tn_mem_reads=30 + ... +``` + +Maybe that's a bit much. Let's break it down into pieces. + +It start with a few DB get requests that are outside of applying a chunk. It's +quite common that we have these kinds of constant overhead requests that are +independent of what's inside a chunk. If we see too many such requests, we +should take a close look to see if we are wasting performance. + +``` +GET State "AQAAAAMAAACm9DRx/dU8UFEfbumiRhDjbPjcyhE6CB1rv+8fnu81bw==" size=9 +GET State "AQAAAAAAAACLFgzRCUR3inMDpkApdLxFTSxRvprJ51eMvh3WbJWe0A==" size=203 +GET State "AQAAAAIAAACXlEo0t345S6PHsvX1BLaGw6NFDXYzeE+tlY2srjKv8w==" size=299 +``` + +Next let's look at `apply_transactions` but limit the depth of items to 3 +levels. + +``` +apply_transactions shard_id=3 + process_state_update + apply num_transactions=3 shard_cache_hit=207 + process_transaction tx_hash=C4itKVLP5gBoAPsEXyEbi67Gg5dvQVugdMjrWBBLprzB shard_cache_hit=57 + process_transaction tx_hash=GRSXC4QCBJHN4hmJiATAbFGt9g5PiksQDNNRaSk666WX shard_cache_miss=3 prefetch_pending=3 shard_cache_hit=35 + process_transaction tx_hash=6bDPeat12pGqA3KEyyg4tJ35kBtRCuFQ7HtCpWoxr8qx shard_cache_miss=2 prefetch_pending=1 shard_cache_hit=21 prefetch_hit=1 + process_receipt receipt_id=GRB3skohuShBvdGAoEoR3SdJJw7MwCxxscJHKLdPoYUC predecessor=1663adeba849fb7c26195678e1c5378278e5caa6325d4672246821d8e61bb160 receiver=token.sweat id=GRB3skohuShBvdGAoEoR3SdJJw7MwCxxscJHKLdPoYUC shard_cache_too_large=1 shard_cache_miss=1 shard_cache_hit=38 +``` + +Here you can see that before we even get to `apply`, we go through +`apply_transactions` and `process_state_update`. The excerpt does not show it +but there are DB requests listed further below that belong to these levels but +not to `apply`. + +Inside `apply`, we see 3 transactions being converted to receipts as part of +this chunk, and one already existing action receipt getting processed. + +Cache hit statistics for each level are also displayed. For example, the first +transaction has 57 read requests and all of them hit in the shard cache. For +the second transaction, we miss the cache 3 times but the values were already in the +process of being prefetched. This would be account data which we fetch in +parallel for all transactions in the chunk. + +Finally, there are several `process_receipt` groups, although the excerpt was +cut to display only one. Here we see the receiving account +(`receiver=token.sweat`) and the receipt ID to potentially look it up on an +explorer, or dig deeper using state viewer commands. + +Again, cache hit statistics are included. Here you can see one value +missed the cache because it was too large. Usually that's a contract code. We do +not include it in the shard cache because it would take up too much space. + +Zooming in a bit further, let's look at the DB request at the start of the +receipt. + +``` + GET FlatState "AXRva2VuLnN3ZWF0" size=36 + GET State "AQAAAAMAAADVYp4vtlIbDoVhji22CZOEaxVWVTJKASq3iMvpNEQVDQ==" size=206835 + input + register_len + read_register + storage_read READ key='STATE' size=70 tn_db_reads=20 tn_mem_reads=0 shard_cache_hit=21 + register_len + read_register +``` + +`FlatState "AXRva2VuLnN3ZWF0"` reads the `ValueRef` of the contract code, which +is 36 bytes in its serialized format. Then, the `ValueRef` is dereferenced to +read the actual code, which happens to be 206kB in size. This happens in the +`State` column because at the time of writing, we still read the actual values +from the trie, not from flat state. + +What follows are host function calls performed by the SDK. It uses `input` to +check the function call arguments and copies it from a register into WASM +memory. + +Then the SDK reads the serialized contract state from the hardcoded key +`"STATE"`. Note that we charge 20 `tn_db_reads` for it, since we missed the +accounting cache, but we hit everything in the shard cache. Thus, there are no DB +requests. If there were DB requests for this `tn_db_reads`, you would see them +listed. + +The returned 70 bytes are again copied into WASM memory. Knowing the SDK code a +little bit, we can guess that the data is then deserialized into the struct used +by the contract for its root state. That's not visible on the trace though, as +this happens completely inside the WASM VM. + +Next we start executing the actual contract code, which again calls a bunch of +host functions. Apparently the code starts by reading the attached deposit and +the predecessor account id, presumably to perform some checks. + +The `sha256` call here is used to shorten implicit account ids. +([Link to code for comparison](https://github.com/sweatco/unc-sdk-rs/blob/af6ba3cb75e0bbfc26e346e61aa3a0d1d7f5ac7b/unc-contract-standards/src/fungible_token/core_impl.rs#L249-L259)). + +Afterwards, a value with 16 bytes (a `u128`) is fetched from the trie state. +To serve this, it required reading 30 trie nodes, 19 of them were cached in the +accounting cache and were not charged the full gas cost. And the remaining 11 +missed the accounting cache but they hit the shard cache. Nothing needed to be +fetched from DB because the Sweatcoin specific prefetcher has already loaded +everything into the shard cache. + +*Note: We see trie node requests despite flat state being used. This is because +the trace was collected with a binary that performed a read on both the trie and +flat state to do some correctness checks.* + +``` + attached_deposit + predecessor_account_id + register_len + read_register + sha256 + read_register + storage_read READ key=dAAxagYMOEb01+56sl9vOM0yHbZRPSaYSL3zBXIfCOi7ow== size=16 tn_db_reads=10 tn_mem_reads=19 shard_cache_hit=11 + GET FlatState "CXRva2VuLnN3ZWF0LHQAMWoGDDhG9NfuerJfbzjNMh22UT0mmEi98wVyHwjou6M=" size=36 +``` + +So that is how to read these traces and dig deep. But maybe you +want aggregated statistics instead? Then please continue reading. + + +## Evaluating an IO trace + +When you collect an IO trace over an hour of mainnet traffic, it can quickly be +above 1GB in uncompressed size. You might be able to sample a few receipts and +eyeball them to get a feeling for what's going on. But you can't understand the +whole trace without additional tooling. + +The parameter estimator command `replay` can help with that. (See also [this +readme](https://github.com/utnet-org/utility/tree/master/runtime/runtime-params-estimator/README.md)) +Run the following command to see an overview of available commands. + +```bash +# will print the help page for the IO trace replay command +cargo run --profile dev-release -p runtime-params-estimator -- \ + replay --help +``` + +All commands aggregate the information of a trace. Either globally, per chunk, +or per receipt. For example, below is the output that gives a list of RocksDB +columns that were accessed and how many times, aggregated by chunk. + + +```bash +cargo run --profile dev-release -p runtime-params-estimator -- \ + replay ./path/to/my.io_trace chunk-db-stats +``` + +``` +apply_transactions shard_id=3 block=DajBgxTgV8NewTJBsR5sTgPhVZqaEv9xGAKVnCiMiDxV + GET 12 FlatState 4 State + +apply_transactions shard_id=0 block=DajBgxTgV8NewTJBsR5sTgPhVZqaEv9xGAKVnCiMiDxV + GET 14 FlatState 8 State + +apply_transactions shard_id=2 block=HTptJFZKGfmeWs7y229df6WjMQ3FGfhiqsmXnbL2tpz8 + GET 2 FlatState + +apply_transactions shard_id=3 block=HTptJFZKGfmeWs7y229df6WjMQ3FGfhiqsmXnbL2tpz8 + GET 6 FlatState 2 State + +apply_transactions shard_id=1 block=HTptJFZKGfmeWs7y229df6WjMQ3FGfhiqsmXnbL2tpz8 + GET 50 FlatState 5 State + +... + +apply_transactions shard_id=3 block=AUcauGxisMqNmZu5Ln7LLu8Li31H1sYD7wgd7AP6nQZR + GET 17 FlatState 3 State + +top-level: + GET 8854 Block 981 BlockHeader 16556 BlockHeight 59155 BlockInfo 2 BlockMerkleTree 330009 BlockMisc 1 BlockOrdinal 31924 BlockPerHeight 863 BlockRefCount 1609 BlocksToCatchup 1557 ChallengedBlocks 4 ChunkExtra 5135 ChunkHashesByHeight 128788 Chunks 35 EpochInfo 1 EpochStart 98361 FlatState 1150 HeaderHashesByHeight 8113 InvalidChunks 263 NextBlockHashes 22 OutgoingReceipts 131114 PartialChunks 1116 ProcessedBlockHeights 968698 State + SET 865 BlockHeight 1026 BlockMerkleTree 12428 BlockMisc 1636 BlockOrdinal 865 BlockPerHeight 865 BlockRefCount 3460 ChunkExtra 3446 ChunkHashesByHeight 339142 FlatState 3460 FlatStateDeltas 3460 FlatStateMisc 865 HeaderHashesByHeight 3460 IncomingReceipts 865 NextBlockHashes 3442 OutcomeIds 3442 OutgoingReceipts 863 ProcessedBlockHeights 340093 StateChanges 3460 TrieChanges + UPDATE_RC 13517 ReceiptIdToShardId 13526 Receipts 1517322 State 6059 Transactions +``` + +The output contains one `apply_transactions` for each chunk, with the block hash +and the shard id. Then it prints one line for each DB operations observed +(GET,SET,...) together with a list of columns and an OP count. + +See the `top-level` output at the end? These are all the DB requests that could +not be assigned to specific chunks. The way we currently count write operations +(SET, UPDATE_RC) they are never assigned to a specific chunk and instead only +show up in the top-level list. Clearly, there is some room for improvement here. +So far we simply haven't worried about RocksDB write performance so the tooling +to debug write performance is naturally lacking. \ No newline at end of file diff --git a/docs/practices/workflows/localnet_on_many_machines.md b/docs/practices/workflows/localnet_on_many_machines.md new file mode 100644 index 000000000..dbe7aecc2 --- /dev/null +++ b/docs/practices/workflows/localnet_on_many_machines.md @@ -0,0 +1,67 @@ +# Running near localnet on 2 machines + +Quick instructions on how to run a localnet on 2 separate machines. + +## Setup + +* Machine1: "pc" - 192.168.0.1 +* Machine2: "laptop" - 192.168.0.2 + + +Run on both machines (make sure that they are using the same version of the code): +``` +cargo build -p uncd +``` + +Then on machine1 run the command below, which will generate the configurations: + +``` +./target/debug/uncd --home ~/.near/localnet_multi localnet --shards 3 --v 2 +``` + +This command has generated configuration for 3 shards and 2 validators (in directories ~/.near/localnet_multi/node0 and ~/.near/localnet_multi/node1). + +Now - copy the contents of node1 directory to the machine2 + +``` +rsync -r ~/.near/localnet_multi/node1 192.168.0.2:~/.near/localnet_multi/node1 +``` + +Now open the config.json file on both machines (node0/config.json on machine1 and node1/config.json on machine2) and: +* for rpc->addr and network->addr: + * Change the addres from 127.0.0.1 to 0.0.0.0 (this means that the port will be accessible from other computers) + * Remember the port numbers (they are generated randomly). +* Also write down the node0's node_key (it is probably: "ed25519:7PGseFbWxvYVgZ89K1uTJKYoKetWs7BJtbyXDzfbAcqX") + +## Running + +On machine1: +``` +./target/debug/uncd --home ~/.near/localnet_multi/node0 run +``` + +On machine2: +``` +./target/debug/uncd --home ~/.near/localnet_multi/node1 run --boot-nodes ed25519:7PGseFbWxvYVgZ89K1uTJKYoKetWs7BJtbyXDzfbAcqX@192.168.0.1:37665 +``` +The boot node address should be the IP of the machine1 + the network addr port **from the node0/config.json** + + +And if everything goes well, the nodes should communicate and start producing blocks. + +## Troubleshooting + +The debug mode is enabled by default, so you should be able to see what's going on by going to ``http://machine1:RPC_ADDR_PORT/debug`` + + +### If node keeps saying "waiting for peers" +See if you can see the machine1's debug page from machine2. (if not - there might be a firewall blocking the connection). + +Make sure that you set the right ports (it should use node0's NETWORK port) and that you set the ip add there to 0.0.0.0 + + +### Resetting the state +Simply stop both nodes, and remove the ``data`` subdirectory (~/.near/localnet_multi/node0/data and ~/.near/localnet_multi/node1/data). + +Then after restart, the nodes will start the blockchain from scratch. + diff --git a/docs/practices/workflows/run_a_node.md b/docs/practices/workflows/run_a_node.md new file mode 100644 index 000000000..837e413f8 --- /dev/null +++ b/docs/practices/workflows/run_a_node.md @@ -0,0 +1,477 @@ +# Run a Node + +This chapter focuses on the basics of running a node you've just built from +source. It tries to explain how the thing works under the hood and pays +relatively little attention to the various shortcuts we have. + +## Building the Node + +Start with the following command: + +```console +$ cargo run --profile dev-release -p uncd -- --help +``` + +This command builds `uncd` and asks it to show `--help`. Building `uncd` takes +a while, take a look at [Fast Builds](../fast_builds.md) chapter to learn how to +speed it up. + +Let's dissect the command: + +- `cargo run` asks `Cargo`, the package manager/build tool, to run our + application. If you don't have `cargo`, install it via +- `--profile dev-release` is our + [custom profile](https://doc.rust-lang.org/cargo/reference/profiles.html#custom-profiles) + to build a somewhat optimized version of the code. The default debug + profile is faster to compile, but produces a node that is too slow to + participate in a real network. The `--release` profile produces a fully + optimized node, but that's very slow to compile. So `--dev-release` + is a sweet spot for us! However, never use it for actual production nodes. +- `-p uncd` asks to build the `uncd` package. We use + [cargo workspaces](https://doc.rust-lang.org/cargo/reference/workspaces.html) + to organize our code. The `uncd` package in the top-level `/uncd` directory + is the final binary that ties everything together. +- `--` tells cargo to pass the rest of the arguments through to `uncd`. +- `--help` instructs `uncd` to list available CLI arguments and subcommands. + +**Note:** Building `uncd` might fail with an openssl or CC error. This means +that you lack some non-rust dependencies we use (openssl and rocksdb mainly). We +currently don't have docs on how to install those, but (basically) you want to +`sudo apt install` (or whichever distro/package manager you use) missing bits. + +## Preparing Tiny Network + +Typically, you want `uncd` to connect to some network, like `mainnet` or +`testnet`. We'll get there in time, but we'll start small. For the current +chapter, we will run a network consisting of just a single node -- our own. + +The first step there is creating the required configuration. Run the `init` +command to create config files: + +```console +$ cargo run --profile dev-release -p uncd -- init +INFO uncd: version="trunk" build="1.1.0-3091-ga8964d200-modified" latest_protocol=57 +INFO near: Using key ed25519:B41GMfqE2jWHVwrPLbD7YmjZxxeQE9WA9Ua2jffP5dVQ for test.near +INFO near: Using key ed25519:34d4aFJEmc2A96UXMa9kQCF8g2EfzZG9gCkBAPcsVZaz for node +INFO near: Generated node key, validator key, genesis file in ~/.near +``` + +As the log output says, we are just generating _some things_ in `~/.near`. +Let's take a look: + +```console +$ ls ~/.near +config.json +genesis.json +node_key.json +validator_key.json +``` + +The most interesting file here is perhaps `genesis.json` -- it specifies the +initial state of our blockchain. There are a bunch of hugely important fields +there, which we'll ignore here. The part we'll look at is the `.records`, which +contains the actual initial data: + +```console +$ cat ~/.near/genesis.json | jq '.records' +[ + { + "Account": { + "account_id": "test.near", + "account": { + "amount": "1000000000000000000000000000000000", + "locked": "50000000000000000000000000000000", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 0, + "version": "V1" + } + } + }, + { + "AccessKey": { + "account_id": "test.near", + "public_key": "ed25519:B41GMfqE2jWHVwrPLbD7YmjZxxeQE9WA9Ua2jffP5dVQ", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "Account": { + "account_id": "near", + "account": { + "amount": "1000000000000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 0, + "version": "V1" + } + } + }, + { + "AccessKey": { + "account_id": "near", + "public_key": "ed25519:546XB2oHhj7PzUKHiH9Xve3Ze5q1JiW2WTh6abXFED3c", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + } +``` + +(I am using the [jq](https://stedolan.github.io/jq/) utility here) + +We see that we have two accounts here, and we also see their public keys (but +not the private ones). + +One of these accounts is a validator: + +``` +$ cat ~/.near/genesis.json | jq '.validators' +[ + { + "account_id": "test.near", + "public_key": "ed25519:B41GMfqE2jWHVwrPLbD7YmjZxxeQE9WA9Ua2jffP5dVQ", + "amount": "50000000000000000000000000000000" + } +] +``` + +Now, if we + +```console +$ cat ~/.near/validator_key.json +``` + +we'll see + +```json +{ + "account_id": "test.near", + "public_key": "ed25519:B41GMfqE2jWHVwrPLbD7YmjZxxeQE9WA9Ua2jffP5dVQ", + "secret_key": "ed25519:3x2dUQgBoEqNvKwPjfDE8zDVJgM8ysqb641PYHV28mGPu61WWv332p8keMDKHUEdf7GVBm4f6z4D1XRgBxnGPd7L" +} +``` + +That is, we have a secret key for the sole validator in our network, how +convenient. + +To recap, `uncd init` without arguments creates a config for a new network +that starts with a single validator, for which we have the keys. + +You might be wondering what `~/.near/node_key.json` is. That's not too +important, but, in our network, there's no 1-1 correspondence between machines +participating in the peer-to-peer network and accounts on the blockchain. So the +`node_key` specifies the keypair we'll use when signing network packets. These +packets internally will contain messages signed with the validator's key, and +these internal messages will drive the evolution of the blockchain state. + +Finally, `~/.near/config.json` contains various configs for the node itself. +These are configs that don't affect the rules guiding the evolution of the +blockchain state, but rather things like timeouts, database settings and +such. + +The only field we'll look at is `boot_nodes`: + +```console +$ cat ~/.near/config.json | jq '.network.boot_nodes' +"" +``` + +It's empty! The `boot_nodes` specify IPs of the initial nodes our node will +try to connect to on startup. As we are looking into running a single-node +network, we want to leave it empty. But, if you would like to connect to +mainnet, you'd have to set this to some nodes from the mainnet you already know. +You'd also have to ensure that you use the same genesis as the mainnet though +-- if the node tries to connect to a network with a different genesis, it +is rejected. + +## Running the Network + +Finally, + +```console +$ cargo run --profile dev-release -p uncd -- run +INFO uncd: version="trunk" build="1.1.0-3091-ga8964d200-modified" latest_protocol=57 +INFO near: Creating a new RocksDB database path=/home/matklad/.near/data +INFO db: Created a new RocksDB instance. num_instances=1 +INFO stats: # 0 4xecSHqTKx2q8JNQNapVEi5jxzewjxAnVFhMd4v5LqNh Validator | 1 validator 0 peers ⬇ 0 B/s ⬆ 0 B/s NaN bps 0 gas/s CPU: 0%, Mem: 50.8 MB +INFO unc_chain::doomslug: ready to produce block @ 1, has enough approvals for 59.907µs, has enough chunks +INFO unc_chain::doomslug: ready to produce block @ 2, has enough approvals for 40.732µs, has enough chunks +INFO unc_chain::doomslug: ready to produce block @ 3, has enough approvals for 65.341µs, has enough chunks +INFO unc_chain::doomslug: ready to produce block @ 4, has enough approvals for 51.916µs, has enough chunks +INFO unc_chain::doomslug: ready to produce block @ 5, has enough approvals for 37.155µs, has enough chunks +... +``` + +🎉 it's alive! + +So, what's going on here? + +Our node is running a single-node network. As the network only has a single +validator, and the node has the keys for the validator, the node can produce +blocks by itself. Note the increasing `@ 1`, `@ 2`, ... numbers. That +means that our network grows. + +Let's stop the node with `^C` and look around + +```console +INFO unc_chain::doomslug: ready to produce block @ 42, has enough approvals for 56.759µs, has enough chunks +^C WARN uncd: SIGINT, stopping... this may take a few minutes. +INFO uncd: Waiting for RocksDB to gracefully shutdown +INFO db: Waiting for remaining RocksDB instances to shut down num_instances=1 +INFO db: All RocksDB instances shut down +$ +``` + +The main change now is that we have a `~/.near/data` directory which holds the +state of the network in various rocksdb tables: + +```console +$ ls ~/.near/data + 000004.log + CURRENT + IDENTITY + LOCK + LOG + MANIFEST-000005 + OPTIONS-000107 + OPTIONS-000109 +``` + +It doesn't matter what those are, "rocksdb stuff" is a fine level of understanding +here. The important bit here is that the node remembers the state of the network, +so, when we restart it, it continues from around the last block: + +```console +$ cargo run --profile dev-release -p uncd -- run +INFO uncd: version="trunk" build="1.1.0-3091-ga8964d200-modified" latest_protocol=57 +INFO db: Created a new RocksDB instance. num_instances=1 +INFO db: Dropped a RocksDB instance. num_instances=0 +INFO near: Opening an existing RocksDB database path=/home/matklad/.near/data +INFO db: Created a new RocksDB instance. num_instances=1 +INFO stats: # 5 Cfba39eH7cyNfKn9GoKTyRg8YrhoY1nQxQs66tLBYwRH Validator | 1 validator 0 peers ⬇ 0 B/s ⬆ 0 B/s NaN bps 0 gas/s CPU: 0%, Mem: 49.4 MB +INFO unc_chain::doomslug: not ready to produce block @ 43, need to wait 366.58789ms, has enough approvals for 78.776µs +INFO unc_chain::doomslug: not ready to produce block @ 43, need to wait 265.547148ms, has enough approvals for 101.119518ms +INFO unc_chain::doomslug: not ready to produce block @ 43, need to wait 164.509153ms, has enough approvals for 202.157513ms +INFO unc_chain::doomslug: not ready to produce block @ 43, need to wait 63.176926ms, has enough approvals for 303.48974ms +INFO unc_chain::doomslug: ready to produce block @ 43, has enough approvals for 404.41498ms, does not have enough chunks +INFO unc_chain::doomslug: ready to produce block @ 44, has enough approvals for 50.07µs, has enough chunks +INFO unc_chain::doomslug: ready to produce block @ 45, has enough approvals for 45.093µs, has enough chunks +``` + +## Interacting With the Node + +Ok, now our node is running, let's poke it! The node exposes a JSON RPC interface +which can be used to interact with the node itself (to, e.g., do a health check) +or with the blockchain (to query information about the blockchain state or to +submit a transaction). + +```console +$ http get http://localhost:3030/status +HTTP/1.1 200 OK +access-control-allow-credentials: true +access-control-expose-headers: accept-encoding, accept, connection, host, user-agent +content-length: 1010 +content-type: application/json +date: Tue, 15 Nov 2022 13:58:13 GMT +vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers + +{ + "chain_id": "test-chain-rR8Ct", + "latest_protocol_version": 57, + "node_key": "ed25519:71QRP9qKcYRUYXTLNnrmRc1NZSdBaBo9nKZ88DK5USNf", + "node_public_key": "ed25519:5A5QHyLayA9zksJZGBzveTgBRecpsVS4ohuxujMAFLLa", + "protocol_version": 57, + "rpc_addr": "0.0.0.0:3030", + "sync_info": { + "earliest_block_hash": "6gJLCnThQENYFbnFQeqQvFvRsTS5w87bf3xf8WN1CMUX", + "earliest_block_height": 0, + "earliest_block_time": "2022-11-15T13:45:53.062613669Z", + "epoch_id": "6gJLCnThQENYFbnFQeqQvFvRsTS5w87bf3xf8WN1CMUX", + "epoch_start_height": 501, + "latest_block_hash": "9JC9o3rZrDLubNxVr91qMYvaDiumzwtQybj1ZZR9dhbK", + "latest_block_height": 952, + "latest_block_time": "2022-11-15T13:58:13.185721125Z", + "latest_state_root": "9kEYQtWczrdzKCCuFzPDX3Vtar1pFPXMdLU5HJyF8Ght", + "syncing": false + }, + "uptime_sec": 570, + "validator_account_id": "test.near", + "validator_public_key": "ed25519:71QRP9qKcYRUYXTLNnrmRc1NZSdBaBo9nKZ88DK5USNf", + "validators": [ + { + "account_id": "test.near", + "is_slashed": false + } + ], + "version": { + "build": "1.1.0-3091-ga8964d200-modified", + "rustc_version": "1.65.0", + "version": "trunk" + } +} +``` + +(I am using [HTTPie here](https://httpie.io/cli)) + +Note how `"latest_block_height": 952` corresponds to `@ 952` we see in the logs. + +Let's query the blockchain state: + +``` +$ http post http://localhost:3030/ method=query jsonrpc=2.0 id=1 \ + params:='{"request_type": "view_account", "finality": "final", "account_id": "test.near"}' +λ http post http://localhost:3030/ method=query jsonrpc=2.0 id=1 \ + params:='{"request_type": "view_account", "finality": "final", "account_id": "test.near"}' + +HTTP/1.1 200 OK +access-control-allow-credentials: true +access-control-expose-headers: content-length, accept, connection, user-agent, accept-encoding, content-type, host +content-length: 294 +content-type: application/json +date: Tue, 15 Nov 2022 14:04:54 GMT +vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers + +{ + "id": "1", + "jsonrpc": "2.0", + "result": { + "amount": "1000000000000000000000000000000000", + "block_hash": "Hn4v5CpfWf141AJi166gdDK3e3khCxgfeDJ9dSXGpAVi", + "block_height": 1611, + "code_hash": "11111111111111111111111111111111", + "locked": "50003138579594550524246699058859", + "storage_paid_at": 0, + "storage_usage": 182 + } +} +``` + +Note how we use an HTTP `post` method when we interact with the blockchain RPC. +The full set of RPC endpoints is documented at + + + +## Sending Transactions + +Transactions are submitted via RPC as well. Submitting a transaction manually +with `http` is going to be cumbersome though — transactions are borsh encoded +to bytes, then signed, then encoded in base64 for JSON. + +So we will use the official [NEAR CLI] utility. + +[NEAR CLI]: https://docs.near.org/tools/unc-cli + +Install it via `npm`: + +```console +$ npm install -g unc-cli +$ near -h +Usage: near [options] + +Commands: + near create-account create a new developer account +.... +``` + +Note that, although you install `unc-cli`, the name of the utility is `near`. + +As a first step, let's redo the `view_account` call we did with raw `httpie` +with `unc-cli`: + +```console +$ unc_ENV=local near state test.near +Loaded master account test.near key from ~/.near/validator_key.json with public key = ed25519:71QRP9qKcYRUYXTLNnrmRc1NZSdBaBo9nKZ88DK5USNf +Account test.near +{ + amount: '1000000000000000000000000000000000', + block_hash: 'ESGN7H1kVLp566CTQ9zkBocooUFWNMhjKwqHg4uCh2Sg', + block_height: 2110, + code_hash: '11111111111111111111111111111111', + locked: '50005124762657986708532525400812', + storage_paid_at: 0, + storage_usage: 182, + formattedAmount: '1,000,000,000' +} +``` + +`unc_ENV=local` tells `unc-cli` to use our local network, rather than the +`mainnet`. + +Now, let's create a couple of accounts and send tokes between them: + +``` +$ unc_ENV=local near create-account alice.test.near --masterAccount test.near +NOTE: In most cases, when connected to network "local", masterAccount will end in ".node0" +Loaded master account test.near key from /home/matklad/.near/validator_key.json with public key = ed25519:71QRP9qKcYRUYXTLNnrmRc1NZSdBaBo9nKZ88DK5USNf +Saving key to 'undefined/local/alice.test.near.json' +Account alice.test.near for network "local" was created. + +$ unc_ENV=local near create-account bob.test.near --masterAccount test.near +NOTE: In most cases, when connected to network "local", masterAccount will end in ".node0" +Loaded master account test.near key from /home/matklad/.near/validator_key.json with public key = ed25519:71QRP9qKcYRUYXTLNnrmRc1NZSdBaBo9nKZ88DK5USNf +Saving key to 'undefined/local/bob.test.near.json' +Account bob.test.near for network "local" was created. + +$ unc_ENV=local near send alice.test.near bob.test.near 10 +Sending 10 NEAR to bob.test.near from alice.test.near +Loaded master account test.near key from /home/matklad/.near/validator_key.json with public key = ed25519:71QRP9qKcYRUYXTLNnrmRc1NZSdBaBo9nKZ88DK5USNf +Transaction Id BBPndo6gR4X8pzoDK7UQfoUXp5J8WDxkf8Sq75tK5FFT +To see the transaction in the transaction explorer, please open this url in your browser +http://localhost:9001/transactions/BBPndo6gR4X8pzoDK7UQfoUXp5J8WDxkf8Sq75tK5FFT +``` + +**Note:** You can export the variable `unc_ENV` in your shell if you are planning +to do multiple commands to avoid repetition: + +```console +$ export unc_ENV=local +``` + +NEAR CLI printouts are not always the most useful or accurate, but this seems to +work. + +Note that `near` automatically creates keypairs and stores them at +`.unc-credentials`: + +```console +$ ls ~/.unc-credentials/local + alice.test.near.json + bob.test.near.json +``` + +To verify that this did work, and that `unc-cli` didn't cheat us, let's +query the state of accounts manually: + +```console +$ http post http://localhost:3030/ method=query jsonrpc=2.0 id=1 \ + params:='{"request_type": "view_account", "finality": "final", "account_id": "alice.test.near"}' \ + | jq '.result.amount' +"89999955363487500000000000" + +14:30:52|~ +λ http post http://localhost:3030/ method=query jsonrpc=2.0 id=1 \ + params:='{"request_type": "view_account", "finality": "final", "account_id": "bob.test.near"}' \ + | jq '.result.amount' +"110000000000000000000000000" +``` + +Indeed, some amount of tokes was transferred from `alice` to `bob`, and then +some amount of tokens was deducted to account for transaction fees. + +## Recap + +Great! So we've learned how to run our very own single-node NEAR network using a +binary we've built from source. The steps are: + +- Create configs with `cargo run --profile dev-release -p uncd -- init` +- Run the node with `cargo run --profile dev-release -p uncd -- run` +- Poke the node with `httpie` or +- Install `unc-cli` via `npm install -g unc-cli` +- Submit transactions via `unc_ENV=local near create-account ...` + +In the [next chapter](./deploy_a_contract.md), we'll learn how to deploy a simple +WASM contract. diff --git a/docs/test_networks/mainnet_spoon.md b/docs/test_networks/mainnet_spoon.md new file mode 100644 index 000000000..22c653cc0 --- /dev/null +++ b/docs/test_networks/mainnet_spoon.md @@ -0,0 +1,223 @@ +# Starting a test chain from state taken from mainnet or testnet + +## Purpose + +For testing purposes, it is often desirable to start a test chain with +a starting state that looks like mainnet or testnet. This is usually +done for the purpose of testing changes to uncd itself, but it's also +possible to do this if you're a contract developer and want to see +what a change to your contract would look like on top of the current +mainnet state. At the end of the process described here, you'll have +a set of genesis records that can be used to start your own test chain, +that'll be like any other test chain like the ones generated by the +`uncd localnet` command, except with account balances and data taken +from mainnet + +## How-to + +The first step is to obtain an RPC node home directory for the chain +you'd like to spoon. So if you want to use mainnet state, you can +follow the instructions +[here](https://unc-nodes.io/rpc/run-rpc-node-without-nearup#5-get-data-backup-1) +to obtain a recent snapshot of a mainnet node's home directory. Once +you have your node's home directory set up, run the following +`state-viewer` command to generate a dump of the chain's state: + +```shell +$ uncd --home $unc_HOME_DIRECTORY view-state dump-state --stream +``` + +This command will take a while (possibly many hours) to run. But at the +end you should have `genesis.json` and `records.json` files in +`$unc_HOME_DIRECTORY/output`. This records file represents all of the +chain's current state, and is what we'll use to start our chain. + +From here, we need to make some changes to the `genesis.json` that was +generated in `$unc_HOME_DIRECTORY/output`. To see why, note that the +validators field of this genesis file lists all the current mainnet +validators and their keys. So that means if we were to try and start a +test chain from the generated genesis and records files as-is, it +would work, but our node would expect the current mainnet validators +to be producing blocks and chunks (which they definitely won't be! +Because we're the only ones who know or care about this new test +chain). + +So we need to select a new list of validators to start off our +chain. Suppose that we want our chain to have two validators, +`validator0.near` and `validator1.near`. Let's make a new directory +where we'll be storing intermediate files during this process: + +```shell +$ mkdir ~/test-chain-scratch +``` + +then using your favorite editor, lay out the validators you want in +the test chain as a JSON list in the same format as the `validators` +field in `genesis.json`, maybe in the file +`~/test-chain-scratch/validators.json` + +```json +[ + { + "account_id": "validator0.near", + "public_key": "ed25519:GRAFkrqEkJAbdbWUgc6fDnNpCTE83C3pzdJpjAHkMEhq", + "amount": "100000000000000000000000000000000" + }, + { + "account_id": "validator1.near", + "public_key": "ed25519:5FxQQTC9mk5kLAhTF9ffDMTXiyYrDXyGYskgz46kHMdd", + "amount": "100000000000000000000000000000000" + } +] +``` + +These validator keys should be keys you've already generated. So for +the rest of this document, we'll assume you've run: + +```shell +$ uncd --home ~/unc-test-chain/validator0 init --account-id validator0.near +$ uncd --home ~/unc-test-chain/validator1 init --account-id validator1.near +``` + +This is also a good time to think about what extra accounts you might +want in your test chain. Since all accounts in the test chain will +have the same keys as they do on mainnet, you'll only have access to +the accounts that you have access to on mainnet. If you want to add an +account with a large balance to properly test things out, you can +write them out in a file as a JSON list of state records (in the same +format as they appear in `records.json`). For example, you could put +the following in `~/test-chain-scratch/extra-records.json`: + +```json +[ + { + "Account": { + "account_id": "my-test-account.near", + "account": { + "amount": "10000000000000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182, + "version": "V1" + } + } + }, + { + "AccessKey": { + "account_id": "my-test-account.near", + "public_key": "ed25519:Eo9W44tRMwcYcoua11yM7Xfr1DjgR4EWQFM3RU27MEX8", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + } +] +``` + +You'll want to include an access key here, otherwise you won't be able +to do anything with the account. Note that here you can also add +access keys for any mainnet account you want, so you'll be able to +control it in the test chain. + +Now to make these changes to the genesis and records files, you can +use the `uncd amend-genesis` command like so: + +```shell +# mkdir ~/unc-test-chain/ +$ uncd amend-genesis --genesis-file-in $unc_HOME_DIRECTORY/output/genesis.json --records-file-in $unc_HOME_DIRECTORY/output/records.json --validators ~/test-chain-scratch/validators.json --extra-records ~/test-chain-scratch/extra-records.json --chain-id $TEST_CHAIN_ID --records-file-out ~/unc-test-chain/records.json --genesis-file-out ~/unc-test-chain/genesis.json +``` + +## Starting the network + +After running the previous steps you should have the files +`genesis.json` and `records.json` in `~/unc-test-chain/`. Assuming +you've started it with the two validators `validator0.near` and +`validator1.near` as described above, you'll want to run at least two +nodes, one for each of these validator accounts. If you're working +with multiple computers or VMs that can connect to each other over the +internet, you'll be able to run your test network over the internet as +is done with the "real" networks (mainnet, testnet, etc.). But for now +let's assume that you want to run this on only one machine. + +So assuming you've initialized home directories for each of the +validators with the `init` command described above, you'll want to +copy the records and genesis files generated in the previous step to +each of these: + +```shell +$ cp ~/unc-test-chain/records.json ~/unc-test-chain/validator0 +$ cp ~/unc-test-chain/genesis.json ~/unc-test-chain/validator0 +$ cp ~/unc-test-chain/records.json ~/unc-test-chain/validator1 +$ cp ~/unc-test-chain/genesis.json ~/unc-test-chain/validator1 +``` + +Now we'll need to make a few config changes to each of +`~/unc-test-chain/validator0/config.json` and +`~/unc-test-chain/validator1/config.json`: + +changes to `~/unc-test-chain/validator0/config.json`: + +```json +{ + "genesis_records_file": "records.json", + "rpc": { + "addr": "0.0.0.0:3030" + }, + "network": { + "addr": "0.0.0.0:24567", + "boot_nodes": "ed25519:Dk4A7NPBYFPwKWouiSUoyZ15igbLSrcPEJqUqDX4grb7@127.0.0.1:24568", + "skip_sync_wait": false, + }, + "consensus": { + "min_num_peers": 1 + }, + "tracked_shards": [0], +} +``` + +changes to `~/unc-test-chain/validator1/config.json`: + +```json +{ + "genesis_records_file": "records.json", + "rpc": { + "addr": "0.0.0.0:3031" + }, + "network": { + "addr": "0.0.0.0:24568", + "boot_nodes": "ed25519:6aR4xVQedQ7Z9URrASgwBY8bedpaYzgH8u5NqEHp2hBv@127.0.0.1:24567", + "skip_sync_wait": false, + }, + "consensus": { + "min_num_peers": 1 + }, + "tracked_shards": [0], +} +``` + +Here we make sure to have each node listen on different ports, while +telling each about the other via `network.boot_nodes`. In this +`boot_nodes` string, we set the public key not to the validator key, +but to whatever key is present in the `node_key.json` file you got +when you initialized the home directory. So for `validator0`'s config, +we set its boot node to `validator1`'s node key, followed by the +address of the socket it should be listening on. We also want to drop +the minimum required number of peers, since we're just running a small +test network locally. We set `skip_sync_wait` to `false`, because +otherwise we get strange behavior that will often make your network +stall. + +After making these changes, you can try running one uncd process for +each of your validators: + +```shell +$ uncd --home ~/unc-test-chain/validator0 run +$ uncd --home ~/unc-test-chain/validator1 run +``` + +Now these nodes will begin by taking the records laid out in +`records.json` and turning them into a genesis state. At the time of +this writing, using the latest framework version from the master +branch, this will take a couple hours. But your validators should +begin producing blocks after that's done. \ No newline at end of file diff --git a/framework/Cargo.toml b/framework/Cargo.toml new file mode 100644 index 000000000..c3a05cb2b --- /dev/null +++ b/framework/Cargo.toml @@ -0,0 +1,178 @@ +[package] +name = "framework" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix-rt.workspace = true +actix-web.workspace = true +actix.workspace = true +anyhow.workspace = true +awc.workspace = true +borsh.workspace = true +chrono.workspace = true +cloud-storage.workspace = true +dirs.workspace = true +easy-ext.workspace = true +futures.workspace = true +hex.workspace = true +hyper-tls.workspace = true +hyper.workspace = true +indicatif.workspace = true +num-rational.workspace = true +once_cell.workspace = true +rand.workspace = true +rayon.workspace = true +regex.workspace = true +reqwest.workspace = true +rlimit.workspace = true +rust-s3.workspace = true +serde.workspace = true +serde_ignored.workspace = true +serde_json.workspace = true +smart-default.workspace = true +strum.workspace = true +tempfile.workspace = true +thiserror.workspace = true +tokio.workspace = true +tracing.workspace = true +xz2.workspace = true + +unc-async.workspace = true +unc-chain.workspace = true +unc-chain-configs = { workspace = true, features = ["metrics"] } +unc-chunks.workspace = true +unc-client.workspace = true +unc-client-primitives.workspace = true +unc-crypto.workspace = true +unc-dyn-configs.workspace = true +unc-epoch-manager.workspace = true +unc-jsonrpc = { workspace = true, optional = true } +unc-jsonrpc-primitives = { workspace = true, optional = true } +unc-mainnet-res.workspace = true +unc-network.workspace = true +unc-o11y.workspace = true +unc-performance-metrics.workspace = true +unc-pool.workspace = true +unc-parameters.workspace = true +unc-primitives.workspace = true +unc-store.workspace = true +unc-telemetry.workspace = true +unc-vm-runner.workspace = true +node-runtime.workspace = true +unc-config-utils.workspace = true + +[dev-dependencies] +bencher.workspace = true +primitive-types.workspace = true + +unc-actix-test-utils.workspace = true +unc-jsonrpc-primitives.workspace = true +testlib.workspace = true + +[[bench]] +name = "store" +harness = false + +[features] +default = ["json_rpc", "calimero_zero_storage"] + +performance_stats = [ + "unc-performance-metrics/performance_stats", +] +c_memory_stats = ["unc-performance-metrics/c_memory_stats"] +test_features = [ + "unc-client/test_features", + "unc-network/test_features", + "unc-store/test_features", + "unc-jsonrpc/test_features" +] +expensive_tests = [ + "unc-client/expensive_tests", + "unc-epoch-manager/expensive_tests", + "unc-chain/expensive_tests", +] +no_cache = [ + "node-runtime/no_cache", + "unc-store/no_cache", + "unc-chain/no_cache", + "unc-epoch-manager/no_cache", +] +json_rpc = ["unc-jsonrpc", "unc-jsonrpc-primitives"] +protocol_feature_fix_staking_threshold = [ + "unc-primitives/protocol_feature_fix_staking_threshold", + "unc-epoch-manager/protocol_feature_fix_staking_threshold", +] +protocol_feature_fix_contract_loading_cost = [ + "unc-vm-runner/protocol_feature_fix_contract_loading_cost", +] +new_epoch_sync = [ + "unc-client/new_epoch_sync" +] + +serialize_all_state_changes = ["unc-store/serialize_all_state_changes"] +nightly = [ + "nightly_protocol", + "protocol_feature_fix_contract_loading_cost", + "protocol_feature_fix_staking_threshold", + "serialize_all_state_changes", + "unc-async/nightly", + "unc-chain-configs/nightly", + "unc-chain/nightly", + "unc-chunks/nightly", + "unc-client-primitives/nightly", + "unc-client/nightly", + "unc-dyn-configs/nightly", + "unc-epoch-manager/nightly", + "unc-jsonrpc-primitives/nightly", + "unc-jsonrpc/nightly", + "unc-mainnet-res/nightly", + "unc-network/nightly", + "unc-o11y/nightly", + "unc-parameters/nightly", + "unc-pool/nightly", + "unc-primitives/nightly", + "unc-store/nightly", + "unc-telemetry/nightly", + "unc-vm-runner/nightly", + "node-runtime/nightly", +] +nightly_protocol = [ + "unc-async/nightly_protocol", + "unc-chain-configs/nightly_protocol", + "unc-chain/nightly_protocol", + "unc-chunks/nightly_protocol", + "unc-client-primitives/nightly_protocol", + "unc-client/nightly_protocol", + "unc-dyn-configs/nightly_protocol", + "unc-epoch-manager/nightly_protocol", + "unc-jsonrpc-primitives/nightly_protocol", + "unc-jsonrpc/nightly_protocol", + "unc-mainnet-res/nightly_protocol", + "unc-network/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-parameters/nightly_protocol", + "unc-pool/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", + "unc-telemetry/nightly_protocol", + "unc-vm-runner/nightly_protocol", + "node-runtime/nightly_protocol", +] + +sandbox = [ + "unc-client/sandbox", + "node-runtime/sandbox", + "unc-jsonrpc/sandbox", +] +io_trace = ["unc-vm-runner/io_trace"] + +calimero_zero_storage = ["unc-primitives/calimero_zero_storage"] diff --git a/framework/benches/store.rs b/framework/benches/store.rs new file mode 100644 index 000000000..f21b82fbf --- /dev/null +++ b/framework/benches/store.rs @@ -0,0 +1,93 @@ +#[macro_use] +extern crate bencher; + +use bencher::Bencher; +use unc_chain::{types::RuntimeAdapter, ChainStore, ChainStoreAccess}; +use unc_chain_configs::GenesisValidationMode; +use unc_epoch_manager::EpochManager; +use unc_o11y::testonly::init_integration_logger; +use unc_primitives::types::StateRoot; +use unc_store::Mode; +use framework::{get_default_home, load_config, NightshadeRuntime}; +use std::time::{Duration, Instant}; + +/// Read `TrieItem`s - nodes containing values - using Trie iterator, stop when 10k items were read. +/// Note that the first run populates OS caches, so all next runs will be faster. You may want to run +/// `sudo sh -c "/usr/bin/echo 1 > /proc/sys/vm/drop_caches"` before running the benchmark. +/// As of 25/03/2022, it shows the following results for both read-only and read-write modes: +/// ``` +/// took on avg 6.169248ms op per sec 162 items read 10000 +/// took on avg 1.424615ms op per sec 701 items read 10000 +/// took on avg 1.416562ms op per sec 705 items read 10000 +/// ``` +fn read_trie_items(bench: &mut Bencher, shard_id: usize, mode: Mode) { + init_integration_logger(); + let home_dir = get_default_home(); + let unc_config = load_config(&home_dir, GenesisValidationMode::UnsafeFast) + .unwrap_or_else(|e| panic!("Error loading config: {:#}", e)); + let num_trie_items = 10_000; + + bench.iter(move || { + tracing::info!(target: "uncd", "{:?}", home_dir); + let store = unc_store::NodeStorage::opener( + &home_dir, + unc_config.config.archive, + &unc_config.config.store, + None, + ) + .open_in_mode(mode) + .unwrap() + .get_hot_store(); + + let chain_store = + ChainStore::new(store.clone(), unc_config.genesis.config.genesis_height, true); + + let epoch_manager = + EpochManager::new_arc_handle(store.clone(), &unc_config.genesis.config); + let runtime = NightshadeRuntime::from_config(&home_dir, store, &unc_config, epoch_manager); + let head = chain_store.head().unwrap(); + let last_block = chain_store.get_block(&head.last_block_hash).unwrap(); + let state_roots: Vec = + last_block.chunks().iter().map(|chunk| chunk.prev_state_root()).collect(); + let header = last_block.header(); + + let trie = runtime + .get_trie_for_shard(shard_id as u64, header.prev_hash(), state_roots[shard_id], false) + .unwrap(); + let start = Instant::now(); + let num_items_read = trie + .iter() + .unwrap() + .enumerate() + .map(|(i, _)| { + if i % 500 == 0 { + tracing::info!(target: "uncd", "{}", i) + } + }) + .take(num_trie_items) + .count(); + let took = start.elapsed(); + + println!( + "took on avg {:?} op per sec {} items read {}", + took / (num_items_read as u32), + (num_items_read as u128) * Duration::from_secs(1).as_nanos() / took.as_nanos(), + num_items_read + ); + }); +} + +/// Read first 10k trie items from shard 0. +fn read_trie_items_10k(bench: &mut Bencher) { + read_trie_items(bench, 0, Mode::ReadWrite); +} + +/// Read first 10k trie items from shard 0 in read-only mode. +fn read_trie_items_10k_read_only(bench: &mut Bencher) { + // Read trie items until 10k items found from shard 0. + read_trie_items(bench, 0, Mode::ReadOnly); +} + +benchmark_group!(benches, read_trie_items_10k, read_trie_items_10k_read_only); + +benchmark_main!(benches); diff --git a/framework/res/example-config-gc.json b/framework/res/example-config-gc.json new file mode 100644 index 000000000..97addfeeb --- /dev/null +++ b/framework/res/example-config-gc.json @@ -0,0 +1,123 @@ +{ + "genesis_file": "genesis.json", + "genesis_records_file": null, + "validator_key_file": "validator_key.json", + "node_key_file": "node_key.json", + "rpc": { + "addr": "0.0.0.0:3030", + "cors_allowed_origins": [ + "*" + ], + "polling_config": { + "polling_interval": { + "secs": 0, + "nanos": 500000000 + }, + "polling_timeout": { + "secs": 10, + "nanos": 0 + } + }, + "limits_config": { + "json_payload_max_size": 10485760 + } + }, + "telemetry": { + "endpoints": [ + "https://explorer.mainnet.near.org/api/nodes" + ] + }, + "network": { + "addr": "0.0.0.0:24567", + "boot_nodes": "ed25519:86EtEy7epneKyrcJwSWP7zsisTkfDRH5CFVszt4qiQYw@35.195.32.249:24567,ed25519:BFB78VTDBBfCY4jCP99zWxhXUcFAZqR22oSx2KEr8UM1@35.229.222.235:24567,ed25519:Cw1YyiX9cybvz3yZcbYdG7oDV6D7Eihdfc8eM1e1KKoh@35.195.27.104:24567,ed25519:33g3PZRdDvzdRpRpFRZLyscJdbMxUA3j3Rf2ktSYwwF8@34.94.132.112:24567,ed25519:CDQFcD9bHUWdc31rDfRi4ZrJczxg8derCzybcac142tK@35.196.209.192:24567", + "max_num_peers": 40, + "minimum_outbound_peers": 5, + "ideal_connections_lo": 30, + "ideal_connections_hi": 35, + "peer_recent_time_window": { + "secs": 600, + "nanos": 0 + }, + "safe_set_size": 20, + "handshake_timeout": { + "secs": 20, + "nanos": 0 + }, + "skip_sync_wait": false, + "ban_window": { + "secs": 10800, + "nanos": 0 + }, + "blacklist": [], + "ttl_account_id_router": { + "secs": 3600, + "nanos": 0 + }, + "peer_stats_period": { + "secs": 5, + "nanos": 0 + } + }, + "consensus": { + "min_num_peers": 3, + "block_production_tracking_delay": { + "secs": 0, + "nanos": 100000000 + }, + "min_block_production_delay": { + "secs": 1, + "nanos": 300000000 + }, + "max_block_production_delay": { + "secs": 3, + "nanos": 0 + }, + "max_block_wait_delay": { + "secs": 6, + "nanos": 0 + }, + "produce_empty_blocks": true, + "block_fetch_horizon": 50, + "block_header_fetch_horizon": 50, + "catchup_step_period": { + "secs": 0, + "nanos": 100000000 + }, + "chunk_request_retry_period": { + "secs": 0, + "nanos": 400000000 + }, + "header_sync_initial_timeout": { + "secs": 10, + "nanos": 0 + }, + "header_sync_progress_timeout": { + "secs": 2, + "nanos": 0 + }, + "header_sync_stall_ban_timeout": { + "secs": 120, + "nanos": 0 + }, + "header_sync_expected_height_per_second": 10, + "sync_check_period": { + "secs": 10, + "nanos": 0 + }, + "sync_step_period": { + "secs": 0, + "nanos": 10000000 + }, + "doomslug_step_period": { + "secs": 0, + "nanos": 100000000 + } + }, + "tracked_accounts": [], + "tracked_shards": [0], + "archive": false, + "gc_blocks_limit": 42, + "gc_fork_clean_step": 420, + "gc_num_epochs_to_keep": 24, + "view_client_threads": 4 +} diff --git a/framework/res/example-config-no-gc.json b/framework/res/example-config-no-gc.json new file mode 100644 index 000000000..64b0ea076 --- /dev/null +++ b/framework/res/example-config-no-gc.json @@ -0,0 +1,120 @@ +{ + "genesis_file": "genesis.json", + "genesis_records_file": null, + "validator_key_file": "validator_key.json", + "node_key_file": "node_key.json", + "rpc": { + "addr": "0.0.0.0:3030", + "cors_allowed_origins": [ + "*" + ], + "polling_config": { + "polling_interval": { + "secs": 0, + "nanos": 500000000 + }, + "polling_timeout": { + "secs": 10, + "nanos": 0 + } + }, + "limits_config": { + "json_payload_max_size": 10485760 + } + }, + "telemetry": { + "endpoints": [ + "https://explorer.mainnet.near.org/api/nodes" + ] + }, + "network": { + "addr": "0.0.0.0:24567", + "boot_nodes": "ed25519:86EtEy7epneKyrcJwSWP7zsisTkfDRH5CFVszt4qiQYw@35.195.32.249:24567,ed25519:BFB78VTDBBfCY4jCP99zWxhXUcFAZqR22oSx2KEr8UM1@35.229.222.235:24567,ed25519:Cw1YyiX9cybvz3yZcbYdG7oDV6D7Eihdfc8eM1e1KKoh@35.195.27.104:24567,ed25519:33g3PZRdDvzdRpRpFRZLyscJdbMxUA3j3Rf2ktSYwwF8@34.94.132.112:24567,ed25519:CDQFcD9bHUWdc31rDfRi4ZrJczxg8derCzybcac142tK@35.196.209.192:24567", + "max_num_peers": 40, + "minimum_outbound_peers": 5, + "ideal_connections_lo": 30, + "ideal_connections_hi": 35, + "peer_recent_time_window": { + "secs": 600, + "nanos": 0 + }, + "safe_set_size": 20, + "handshake_timeout": { + "secs": 20, + "nanos": 0 + }, + "skip_sync_wait": false, + "ban_window": { + "secs": 10800, + "nanos": 0 + }, + "blacklist": [], + "ttl_account_id_router": { + "secs": 3600, + "nanos": 0 + }, + "peer_stats_period": { + "secs": 5, + "nanos": 0 + } + }, + "consensus": { + "min_num_peers": 3, + "block_production_tracking_delay": { + "secs": 0, + "nanos": 100000000 + }, + "min_block_production_delay": { + "secs": 1, + "nanos": 300000000 + }, + "max_block_production_delay": { + "secs": 3, + "nanos": 0 + }, + "max_block_wait_delay": { + "secs": 6, + "nanos": 0 + }, + "produce_empty_blocks": true, + "block_fetch_horizon": 50, + "block_header_fetch_horizon": 50, + "catchup_step_period": { + "secs": 0, + "nanos": 100000000 + }, + "chunk_request_retry_period": { + "secs": 0, + "nanos": 400000000 + }, + "header_sync_initial_timeout": { + "secs": 10, + "nanos": 0 + }, + "header_sync_progress_timeout": { + "secs": 2, + "nanos": 0 + }, + "header_sync_stall_ban_timeout": { + "secs": 120, + "nanos": 0 + }, + "header_sync_expected_height_per_second": 10, + "sync_check_period": { + "secs": 10, + "nanos": 0 + }, + "sync_step_period": { + "secs": 0, + "nanos": 10000000 + }, + "doomslug_step_period": { + "secs": 0, + "nanos": 100000000 + } + }, + "tracked_accounts": [], + "tracked_shards": [0], + "archive": false, + "view_client_threads": 4 +} diff --git a/framework/src/append_only_map.rs b/framework/src/append_only_map.rs new file mode 100644 index 000000000..40bd2ed47 --- /dev/null +++ b/framework/src/append_only_map.rs @@ -0,0 +1,26 @@ +use std::collections::HashMap; +use std::hash::Hash; +use std::sync::{Arc, RwLock}; + +const POISONED_LOCK_ERR: &str = "The lock was poisoned."; + +pub struct AppendOnlyMap { + map: RwLock>>, +} + +impl AppendOnlyMap +where + K: Eq + Hash + Clone, +{ + pub fn new() -> Self { + Self { map: RwLock::new(HashMap::new()) } + } + + pub fn get_or_insert V>(&self, key: &K, value: F) -> Arc { + let mut map = self.map.write().expect(POISONED_LOCK_ERR); + if !map.contains_key(key) { + map.insert(key.clone(), Arc::new(value())); + } + map.get(key).unwrap().clone() + } +} diff --git a/framework/src/cold_storage.rs b/framework/src/cold_storage.rs new file mode 100644 index 000000000..e7daf2c27 --- /dev/null +++ b/framework/src/cold_storage.rs @@ -0,0 +1,365 @@ +use std::sync::{atomic::AtomicBool, Arc}; + +use unc_chain::types::Tip; +use unc_epoch_manager::{EpochManagerAdapter, EpochManagerHandle}; +use unc_primitives::{hash::CryptoHash, types::BlockHeight}; +use unc_store::cold_storage::{copy_all_data_to_cold, CopyAllDataToColdStatus}; +use unc_store::{ + cold_storage::{update_cold_db, update_cold_head}, + db::ColdDB, + DBCol, NodeStorage, Store, FINAL_HEAD_KEY, HEAD_KEY, TAIL_KEY, +}; + +use crate::config::SplitStorageConfig; +use crate::{metrics, UncConfig}; + +/// A handle that keeps the state of the cold store loop and can be used to stop it. +pub struct ColdStoreLoopHandle { + join_handle: std::thread::JoinHandle<()>, + keep_going: Arc, +} + +impl ColdStoreLoopHandle { + pub fn stop(self) { + self.keep_going.store(false, std::sync::atomic::Ordering::Relaxed); + match self.join_handle.join() { + Ok(_) => { + tracing::debug!(target : "cold_store", "Joined the cold store loop thread"); + } + Err(_) => { + tracing::error!(target : "cold_store", "Failed to join the cold store loop thread"); + } + } + } +} + +/// The ColdStoreCopyResult indicates if and what block was copied. +#[derive(Debug)] +enum ColdStoreCopyResult { + // No block was copied. The cold head is up to date with the final head. + NoBlockCopied, + /// The final head block was copied. This is the latest block + /// that could be copied until new block is finalized. + LatestBlockCopied, + /// A block older than the final head block was copied. There + /// are more blocks that can be copied immediately. + OtherBlockCopied, +} + +/// Checks if cold store head is behind the final head and if so copies data +/// for the next available produced block after current cold store head. +/// Updates cold store head after. +fn cold_store_copy( + hot_store: &Store, + cold_store: &Store, + cold_db: &Arc, + genesis_height: BlockHeight, + epoch_manager: &EpochManagerHandle, +) -> anyhow::Result { + // If COLD_HEAD is not set for hot storage we default it to genesis_height. + let cold_head = cold_store.get_ser::(DBCol::BlockMisc, HEAD_KEY)?; + let cold_head_height = cold_head.map_or(genesis_height, |tip| tip.height); + + // If FINAL_HEAD is not set for hot storage we default it to genesis_height. + let hot_final_head = hot_store.get_ser::(DBCol::BlockMisc, FINAL_HEAD_KEY)?; + let hot_final_head_height = hot_final_head.map_or(genesis_height, |tip| tip.height); + + // If TAIL is not set for hot storage we default it to genesis_height. + // TAIL not being set is not an error. + // Archive dbs don't have TAIL, that means that they have all data from genesis_height. + let hot_tail = hot_store.get_ser::(DBCol::BlockMisc, TAIL_KEY)?; + let hot_tail_height = hot_tail.unwrap_or(genesis_height); + + let _span = tracing::debug_span!(target: "cold_store", "cold_store_copy", cold_head_height, hot_final_head_height, hot_tail_height).entered(); + + if cold_head_height > hot_final_head_height { + return Err(anyhow::anyhow!( + "Cold head is ahead of final head. cold head height: {} final head height {}", + cold_head_height, + hot_final_head_height + )); + } + + // Cold and Hot storages need to overlap. + // Without this check we would skip blocks from cold_head_height to hot_tail_height. + // This will result in corrupted cold storage. + if cold_head_height < hot_tail_height { + return Err(anyhow::anyhow!( + "Cold head is behind hot tail. cold head height: {} hot tail height {}", + cold_head_height, + hot_tail_height + )); + } + + if cold_head_height >= hot_final_head_height { + return Ok(ColdStoreCopyResult::NoBlockCopied); + } + + // Here it should be sufficient to just read from hot storage. + // Because BlockHeight is never garbage collectable and is not even copied to cold. + let cold_head_hash = + hot_store.get_ser::(DBCol::BlockHeight, &cold_head_height.to_le_bytes())?; + let cold_head_hash = cold_head_hash + .ok_or(anyhow::anyhow!("Failed to read the cold head hash at height {cold_head_height}"))?; + + // The previous block is the cold head so we can use it to get epoch id. + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(&cold_head_hash)?; + let shard_layout = epoch_manager.get_shard_layout(&epoch_id)?; + + let mut next_height = cold_head_height + 1; + while !update_cold_db(cold_db, hot_store, &shard_layout, &next_height)? { + next_height += 1; + if next_height > hot_final_head_height { + return Err(anyhow::anyhow!( + "All blocks between cold head and next height were skipped, but next height > hot final head. cold head {} next height to copy: {} final head height {}", + cold_head_height, + next_height, + hot_final_head_height + )); + } + } + + update_cold_head(cold_db, hot_store, &next_height)?; + + let result = if next_height >= hot_final_head_height { + Ok(ColdStoreCopyResult::LatestBlockCopied) + } else { + Ok(ColdStoreCopyResult::OtherBlockCopied) + }; + + tracing::trace!(target: "cold_store", ?result, "ending"); + result +} + +fn cold_store_copy_result_to_string(result: &anyhow::Result) -> &str { + match result { + Err(_) => "error", + Ok(ColdStoreCopyResult::NoBlockCopied) => "no_block_copied", + Ok(ColdStoreCopyResult::LatestBlockCopied) => "latest_block_copied", + Ok(ColdStoreCopyResult::OtherBlockCopied) => "other_block_copied", + } +} + +#[derive(Debug)] +enum ColdStoreInitialMigrationResult { + /// Cold storage was already initialized + NoNeedForMigration, + /// Performed a successful cold storage migration + SuccessfulMigration, + /// Migration was interrupted by keep_going flag + MigrationInterrupted, +} + +/// This function performs initial population of cold storage if needed. +/// Migration can be interrupted via `keep_going` flag. +/// +/// First, checks that hot store is of kind `Archive`. If not, no migration needed. +/// Then, captures hot final head BEFORE the migration, as migration is performed during normal uncd run. +/// If hot final head is not set, returns Err. +/// Otherwise: +/// 1. performed migration +/// 2. updates head to saved hot final head +/// +/// Any Ok status means that this function should not be retried: +/// - either migration was performed (now or earlier) +/// - or migration was interrupted, which means the `keep_going` flag was set to `false` +/// which means that everything cold store thread related has to stop +/// +/// Error status means that for some reason migration cannot be performed. +fn cold_store_initial_migration( + split_storage_config: &SplitStorageConfig, + keep_going: &Arc, + hot_store: &Store, + cold_store: &Store, + cold_db: &Arc, +) -> anyhow::Result { + // We only need to perform the migration if hot store is of kind Archive and cold store doesn't have a head yet + if hot_store.get_db_kind()? != Some(unc_store::metadata::DbKind::Archive) + || cold_store.get(DBCol::BlockMisc, HEAD_KEY)?.is_some() + { + return Ok(ColdStoreInitialMigrationResult::NoNeedForMigration); + } + + tracing::info!(target: "cold_store", "Starting initial population of cold store"); + + // If FINAL_HEAD is not set for hot storage something isn't right and we will probably fail in `update_cold_head`. + // Let's fail early. + let hot_final_head = hot_store + .get_ser::(DBCol::BlockMisc, FINAL_HEAD_KEY)? + .ok_or_else(|| anyhow::anyhow!("FINAL_HEAD not found in hot storage"))?; + let hot_final_head_height = hot_final_head.height; + + let batch_size = split_storage_config.cold_store_initial_migration_batch_size; + match copy_all_data_to_cold(cold_db.clone(), hot_store, batch_size, keep_going)? { + CopyAllDataToColdStatus::EverythingCopied => { + tracing::info!(target: "cold_store", "Initial population was successful, writing cold head of height {}", hot_final_head_height); + update_cold_head(cold_db, hot_store, &hot_final_head_height)?; + Ok(ColdStoreInitialMigrationResult::SuccessfulMigration) + } + CopyAllDataToColdStatus::Interrupted => { + tracing::info!(target: "cold_store", "Initial population was interrupted"); + Ok(ColdStoreInitialMigrationResult::MigrationInterrupted) + } + } +} + +/// Runs a loop that tries to copy all data from hot store to cold (do initial migration). +/// If migration fails sleeps for 30s and tries again. +/// If migration returned any successful status (including interruption status) breaks the loop. +fn cold_store_initial_migration_loop( + split_storage_config: &SplitStorageConfig, + keep_going: &Arc, + hot_store: &Store, + cold_store: &Store, + cold_db: Arc, +) { + tracing::info!(target: "cold_store", "starting initial migration loop"); + loop { + if !keep_going.load(std::sync::atomic::Ordering::Relaxed) { + tracing::debug!(target: "cold_store", "stopping the initial migration loop"); + break; + } + match cold_store_initial_migration( + split_storage_config, + keep_going, + hot_store, + cold_store, + &cold_db, + ) { + // We can either stop the cold store thread or hope that next time migration will not fail. + // Here we pick the second option. + Err(err) => { + let dur = split_storage_config.cold_store_initial_migration_loop_sleep_duration; + tracing::error!(target: "cold_store", "initial migration failed with error {}, sleeping {}s and trying again", err, dur.as_secs()); + std::thread::sleep(dur); + } + // Any Ok status from `cold_store_initial_migration` function means that we can proceed to regular run. + Ok(status) => { + tracing::info!(target: "cold_store", "Initial migration status: {:?}. Moving on.", status); + break; + } + } + } +} + +// This method will copy data from hot storage to cold storage in a loop. +// It will try to copy blocks as fast as possible up until cold head = final head. +// Once the cold head reaches the final head it will sleep for one second before +// trying to copy data at the next height. +// TODO clean up the interface, currently we need to pass hot store, cold store and +// cold_db which is redundant. +fn cold_store_loop( + split_storage_config: &SplitStorageConfig, + keep_going: &Arc, + hot_store: Store, + cold_store: Store, + cold_db: Arc, + genesis_height: BlockHeight, + epoch_manager: &EpochManagerHandle, +) { + tracing::info!(target : "cold_store", "Starting the cold store loop"); + + loop { + if !keep_going.load(std::sync::atomic::Ordering::Relaxed) { + tracing::debug!(target : "cold_store", "Stopping the cold store loop"); + break; + } + + let instant = std::time::Instant::now(); + let result = + cold_store_copy(&hot_store, &cold_store, &cold_db, genesis_height, epoch_manager); + let duration = instant.elapsed(); + + let result_string = cold_store_copy_result_to_string(&result); + metrics::COLD_STORE_COPY_RESULT.with_label_values(&[result_string]).inc(); + if duration > std::time::Duration::from_secs(1) { + tracing::debug!(target : "cold_store", "cold_store_copy took {}s", duration.as_secs_f64()); + } + + let sleep_duration = split_storage_config.cold_store_loop_sleep_duration; + match result { + Err(err) => { + tracing::error!(target : "cold_store", error = format!("{err:#?}"), "cold_store_copy failed"); + std::thread::sleep(sleep_duration); + } + // If no block was copied the cold head is up to date with final head and + // this loop should sleep while waiting for a new block to get finalized. + Ok(ColdStoreCopyResult::NoBlockCopied) => { + std::thread::sleep(sleep_duration); + } + // The final head block was copied. There are no more blocks to be copied now + // this loop should sleep while waiting for a new block to get finalized. + Ok(ColdStoreCopyResult::LatestBlockCopied) => { + std::thread::sleep(sleep_duration); + } + // A block older than the final head was copied. We should continue copying + // until cold head reaches final head. + Ok(ColdStoreCopyResult::OtherBlockCopied) => { + continue; + } + } + } +} + +/// Spawns the cold store loop in a background thread and returns ColdStoreLoopHandle. +/// If cold store is not configured it does nothing and returns None. +/// The cold store loop is spawned in a rust native thread because it's quite heavy +/// and it is not suitable for async frameworks such as actix or tokio. It's not suitable +/// for async because RocksDB itself doesn't support async. Running this in an async +/// environment would just hog a thread while synchronously waiting for the IO operations +/// to finish. +pub fn spawn_cold_store_loop( + config: &UncConfig, + storage: &NodeStorage, + epoch_manager: Arc, +) -> anyhow::Result> { + if config.config.save_trie_changes != Some(true) { + tracing::debug!(target:"cold_store", "Not spawning cold store because TrieChanges are not saved"); + return Ok(None); + } + + let hot_store = storage.get_hot_store(); + let cold_store = match storage.get_cold_store() { + Some(cold_store) => cold_store, + None => { + tracing::debug!(target : "cold_store", "Not spawning the cold store loop because cold store is not configured"); + return Ok(None); + } + }; + let cold_db = match storage.cold_db() { + Some(cold_db) => cold_db.clone(), + None => { + tracing::debug!(target : "cold_store", "Not spawning the cold store loop because cold store is not configured"); + return Ok(None); + } + }; + + let genesis_height = config.genesis.config.genesis_height; + let keep_going = Arc::new(AtomicBool::new(true)); + let keep_going_clone = keep_going.clone(); + + let split_storage_config = config.config.split_storage.clone().unwrap_or_default(); + + tracing::info!(target : "cold_store", "Spawning the cold store loop"); + let join_handle = + std::thread::Builder::new().name("cold_store_copy".to_string()).spawn(move || { + cold_store_initial_migration_loop( + &split_storage_config, + &keep_going_clone, + &hot_store, + &cold_store, + cold_db.clone(), + ); + cold_store_loop( + &split_storage_config, + &keep_going_clone, + hot_store, + cold_store, + cold_db, + genesis_height, + epoch_manager.as_ref(), + ) + })?; + + Ok(Some(ColdStoreLoopHandle { join_handle, keep_going })) +} diff --git a/framework/src/config.rs b/framework/src/config.rs new file mode 100644 index 000000000..bdc727932 --- /dev/null +++ b/framework/src/config.rs @@ -0,0 +1,1673 @@ +use crate::download_file::{run_download_file, FileDownloadError}; +use crate::dyn_config::LOG_CONFIG_FILENAME; +use anyhow::{anyhow, bail, Context}; +use unc_chain_configs::{ + default_enable_multiline_logging, default_epoch_sync_enabled, + default_header_sync_expected_height_per_second, default_header_sync_initial_timeout, + default_header_sync_progress_timeout, default_header_sync_stall_ban_timeout, + default_log_summary_period, default_produce_chunk_add_transactions_time_limit, + default_state_sync, default_state_sync_enabled, default_state_sync_timeout, + default_sync_check_period, default_sync_height_threshold, default_sync_step_period, + default_transaction_pool_size_limit, default_trie_viewer_state_size_limit, + default_tx_routing_height_horizon, default_view_client_threads, + default_view_client_throttle_period, get_initial_supply, ClientConfig, GCConfig, Genesis, + GenesisConfig, GenesisValidationMode, LogSummaryStyle, MutableConfigValue, ReshardingConfig, + StateSyncConfig, +}; +use unc_config_utils::{ValidationError, ValidationErrors}; +use unc_crypto::{InMemorySigner, KeyFile, KeyType, PublicKey, Signer}; +#[cfg(feature = "json_rpc")] +use unc_jsonrpc::RpcConfig; +use unc_network::config::NetworkConfig; +use unc_network::tcp; +use unc_o11y::log_config::LogConfig; +use unc_primitives::account::{AccessKey, Account}; +use unc_primitives::hash::CryptoHash; +#[cfg(test)] +use unc_primitives::shard_layout::account_id_to_shard_id; +use unc_primitives::shard_layout::ShardLayout; +use unc_primitives::state_record::StateRecord; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::test_utils::create_test_signer; +use unc_primitives::types::{ + AccountId, AccountInfo, Balance, BlockHeight, BlockHeightDelta, Gas, NumBlocks, NumSeats, + NumShards, ShardId, Power, +}; +use unc_primitives::utils::{generate_random_string, get_num_seats_per_shard}; +use unc_primitives::validator_signer::{InMemoryValidatorSigner, ValidatorSigner}; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_telemetry::TelemetryConfig; +use num_rational::Rational32; +use std::fs; +use std::fs::File; +use std::io::{Read, Write}; +use std::path::Path; +use std::str::FromStr; +use std::sync::Arc; +use std::time::Duration; +#[cfg(test)] +use tempfile::tempdir; +use tracing::{info, warn}; + +/// Initial balance used in tests. +pub const TESTING_INIT_BALANCE: Balance = 1_000_000_000 * UNC_BASE; + +/// Validator's stake used in tests. +pub const TESTING_INIT_STAKE: Balance = 50_000_000 * UNC_BASE; + +/// +pub const TESTING_INIT_POWER: Power = 5; + +/// One NEAR, divisible by 10^24. +pub const UNC_BASE: Balance = 1_000_000_000_000_000_000_000_000; + +/// Millinear, 1/1000 of NEAR. +pub const MILLI_NEAR: Balance = UNC_BASE / 1000; + +/// Block production tracking delay. +pub const BLOCK_PRODUCTION_TRACKING_DELAY: u64 = 100; + +/// Expected block production time in ms. +pub const MIN_BLOCK_PRODUCTION_DELAY: u64 = 600; + +/// Mainnet and testnet validators are configured with a different value due to +/// performance values. +pub const MAINNET_MIN_BLOCK_PRODUCTION_DELAY: u64 = 1_300; +pub const TESTNET_MIN_BLOCK_PRODUCTION_DELAY: u64 = 1_000; + +/// Maximum time to delay block production without approvals is ms. +pub const MAX_BLOCK_PRODUCTION_DELAY: u64 = 2_000; + +/// Mainnet and testnet validators are configured with a different value due to +/// performance values. +pub const MAINNET_MAX_BLOCK_PRODUCTION_DELAY: u64 = 3_000; +pub const TESTNET_MAX_BLOCK_PRODUCTION_DELAY: u64 = 2_500; + +/// Maximum time until skipping the previous block is ms. +pub const MAX_BLOCK_WAIT_DELAY: u64 = 6_000; + +/// Horizon at which instead of fetching block, fetch full state. +const BLOCK_FETCH_HORIZON: BlockHeightDelta = 50; + +/// Behind this horizon header fetch kicks in. +const BLOCK_HEADER_FETCH_HORIZON: BlockHeightDelta = 50; + +/// Time between check to perform catchup. +const CATCHUP_STEP_PERIOD: u64 = 100; + +/// Time between checking to re-request chunks. +const CHUNK_REQUEST_RETRY_PERIOD: u64 = 400; + +/// Expected epoch length. +pub const EXPECTED_EPOCH_LENGTH: BlockHeightDelta = (5 * 60 * 1000) / MIN_BLOCK_PRODUCTION_DELAY; + +/// Criterion for kicking out block producers. +pub const BLOCK_PRODUCER_KICKOUT_THRESHOLD: u8 = 90; + +/// Criterion for kicking out chunk producers. +pub const CHUNK_PRODUCER_KICKOUT_THRESHOLD: u8 = 90; + +/// Fast mode constants for testing/developing. +pub const FAST_MIN_BLOCK_PRODUCTION_DELAY: u64 = 120; +pub const FAST_MAX_BLOCK_PRODUCTION_DELAY: u64 = 500; +pub const FAST_EPOCH_LENGTH: BlockHeightDelta = 60; + +/// Expected number of blocks per year +pub const NUM_BLOCKS_PER_YEAR: u64 = 365 * 24 * 60 * 60; + +/// Initial gas limit. +pub const INITIAL_GAS_LIMIT: Gas = 1_000_000_000_000_000; + +/// Initial and minimum gas price. +pub const MIN_GAS_PRICE: Balance = 100_000_000; + +/// Protocol treasury account +pub const PROTOCOL_TREASURY_ACCOUNT: &str = "unc"; + +/// Fishermen stake threshold. +pub const FISHERMEN_THRESHOLD: Balance = 10 * UNC_BASE; + +/// Number of blocks for which a given transaction is valid +pub const TRANSACTION_VALIDITY_PERIOD: NumBlocks = 100; + +/// Number of seats for block producers +pub const NUM_BLOCK_PRODUCER_SEATS: NumSeats = 50; + +/// The minimum stake required for staking is last seat price divided by this number. +pub const MINIMUM_STAKE_DIVISOR: u64 = 10; + +pub const CONFIG_FILENAME: &str = "config.json"; +pub const GENESIS_CONFIG_FILENAME: &str = "genesis.json"; +pub const NODE_KEY_FILE: &str = "node_key.json"; +pub const VALIDATOR_KEY_FILE: &str = "validator_key.json"; + +pub const NETWORK_TELEMETRY_URL: &str = "https://explorer.{}.near.org/api/nodes"; + +/// The rate at which the gas price can be adjusted (alpha in the formula). +/// The formula is +/// gas_price_t = gas_price_{t-1} * (1 + (gas_used/gas_limit - 1/2) * alpha)) +pub const GAS_PRICE_ADJUSTMENT_RATE: Rational32 = Rational32::new_raw(1, 100); + +/// Protocol treasury reward +pub const PROTOCOL_REWARD_RATE: Rational32 = Rational32::new_raw(1, 10); + +/// Maximum inflation rate per year +pub const MAX_INFLATION_RATE: Rational32 = Rational32::new_raw(1, 20); + +/// Protocol upgrade stake threshold. +pub const PROTOCOL_UPGRADE_STAKE_THRESHOLD: Rational32 = Rational32::new_raw(4, 5); + +fn default_doomslug_step_period() -> Duration { + Duration::from_millis(100) +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub struct Consensus { + /// Minimum number of peers to start syncing. + pub min_num_peers: usize, + /// Duration to check for producing / skipping block. + pub block_production_tracking_delay: Duration, + /// Minimum duration before producing block. + pub min_block_production_delay: Duration, + /// Maximum wait for approvals before producing block. + pub max_block_production_delay: Duration, + /// Maximum duration before skipping given height. + pub max_block_wait_delay: Duration, + /// Produce empty blocks, use `false` for testing. + pub produce_empty_blocks: bool, + /// Horizon at which instead of fetching block, fetch full state. + pub block_fetch_horizon: BlockHeightDelta, + /// Behind this horizon header fetch kicks in. + pub block_header_fetch_horizon: BlockHeightDelta, + /// Time between check to perform catchup. + pub catchup_step_period: Duration, + /// Time between checking to re-request chunks. + pub chunk_request_retry_period: Duration, + /// How much time to wait after initial header sync + #[serde(default = "default_header_sync_initial_timeout")] + pub header_sync_initial_timeout: Duration, + /// How much time to wait after some progress is made in header sync + #[serde(default = "default_header_sync_progress_timeout")] + pub header_sync_progress_timeout: Duration, + /// How much time to wait before banning a peer in header sync if sync is too slow + #[serde(default = "default_header_sync_stall_ban_timeout")] + pub header_sync_stall_ban_timeout: Duration, + /// How much to wait for a state sync response before re-requesting + #[serde(default = "default_state_sync_timeout")] + pub state_sync_timeout: Duration, + /// Expected increase of header head weight per second during header sync + #[serde(default = "default_header_sync_expected_height_per_second")] + pub header_sync_expected_height_per_second: u64, + /// How frequently we check whether we need to sync + #[serde(default = "default_sync_check_period")] + pub sync_check_period: Duration, + /// During sync the time we wait before reentering the sync loop + #[serde(default = "default_sync_step_period")] + pub sync_step_period: Duration, + /// Time between running doomslug timer. + #[serde(default = "default_doomslug_step_period")] + pub doomslug_step_period: Duration, + #[serde(default = "default_sync_height_threshold")] + pub sync_height_threshold: u64, +} + +impl Default for Consensus { + fn default() -> Self { + Consensus { + min_num_peers: 3, + block_production_tracking_delay: Duration::from_millis(BLOCK_PRODUCTION_TRACKING_DELAY), + min_block_production_delay: Duration::from_millis(MIN_BLOCK_PRODUCTION_DELAY), + max_block_production_delay: Duration::from_millis(MAX_BLOCK_PRODUCTION_DELAY), + max_block_wait_delay: Duration::from_millis(MAX_BLOCK_WAIT_DELAY), + produce_empty_blocks: true, + block_fetch_horizon: BLOCK_FETCH_HORIZON, + block_header_fetch_horizon: BLOCK_HEADER_FETCH_HORIZON, + catchup_step_period: Duration::from_millis(CATCHUP_STEP_PERIOD), + chunk_request_retry_period: Duration::from_millis(CHUNK_REQUEST_RETRY_PERIOD), + header_sync_initial_timeout: default_header_sync_initial_timeout(), + header_sync_progress_timeout: default_header_sync_progress_timeout(), + header_sync_stall_ban_timeout: default_header_sync_stall_ban_timeout(), + state_sync_timeout: default_state_sync_timeout(), + header_sync_expected_height_per_second: default_header_sync_expected_height_per_second( + ), + sync_check_period: default_sync_check_period(), + sync_step_period: default_sync_step_period(), + doomslug_step_period: default_doomslug_step_period(), + sync_height_threshold: default_sync_height_threshold(), + } + } +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +#[serde(default)] +pub struct Config { + pub genesis_file: String, + pub genesis_records_file: Option, + pub validator_key_file: String, + pub node_key_file: String, + #[cfg(feature = "json_rpc")] + #[serde(skip_serializing_if = "Option::is_none")] + pub rpc: Option, + pub telemetry: TelemetryConfig, + pub network: unc_network::config_json::Config, + pub consensus: Consensus, + pub tracked_accounts: Vec, + pub tracked_shards: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub tracked_shard_schedule: Option>>, + #[serde(skip_serializing_if = "is_false")] + pub archive: bool, + /// If save_trie_changes is not set it will get inferred from the `archive` field as follows: + /// save_trie_changes = !archive + /// save_trie_changes should be set to true iff + /// - archive if false - non-archival nodes need trie changes to perform garbage collection + /// - archive is true and cold_store is configured - node working in split storage mode + /// needs trie changes in order to do garbage collection on hot and populate cold State column. + #[serde(skip_serializing_if = "Option::is_none")] + pub save_trie_changes: Option, + pub log_summary_style: LogSummaryStyle, + pub log_summary_period: Duration, + // Allows more detailed logging, for example a list of orphaned blocks. + pub enable_multiline_logging: Option, + /// Garbage collection configuration. + #[serde(flatten)] + pub gc: GCConfig, + pub view_client_threads: usize, + pub epoch_sync_enabled: bool, + pub view_client_throttle_period: Duration, + pub trie_viewer_state_size_limit: Option, + /// If set, overrides value in genesis configuration. + #[serde(skip_serializing_if = "Option::is_none")] + pub max_gas_burnt_view: Option, + /// Different parameters to configure underlying storage. + pub store: unc_store::StoreConfig, + /// Different parameters to configure underlying cold storage. + /// This feature is under development, do not use in production. + #[serde(skip_serializing_if = "Option::is_none")] + pub cold_store: Option, + /// Configuration for the split storage. + #[serde(skip_serializing_if = "Option::is_none")] + pub split_storage: Option, + /// The node will stop after the head exceeds this height. + /// The node usually stops within several seconds after reaching the target height. + #[serde(skip_serializing_if = "Option::is_none")] + pub expected_shutdown: Option, + /// Whether to use state sync (unreliable and corrupts the DB if fails) or do a block sync instead. + pub state_sync_enabled: bool, + /// Options for syncing state. + #[serde(skip_serializing_if = "Option::is_none")] + pub state_sync: Option, + /// Limit of the size of per-shard transaction pool measured in bytes. If not set, the size + /// will be unbounded. + /// New transactions that bring the size of the pool over this limit will be rejected. This + /// guarantees that the node will use bounded resources to store incoming transactions. + /// Setting this value too low (<1MB) on the validator might lead to production of smaller + /// chunks and underutilizing the capacity of the network. + pub transaction_pool_size_limit: Option, + // Configuration for resharding. + pub resharding_config: ReshardingConfig, + /// If the node is not a chunk producer within that many blocks, then route + /// to upcoming chunk producers. + pub tx_routing_height_horizon: BlockHeightDelta, + /// Limit the time of adding transactions to a chunk. + /// A node produces a chunk by adding transactions from the transaction pool until + /// some limit is reached. This time limit ensures that adding transactions won't take + /// longer than the specified duration, which helps to produce the chunk quickly. + pub produce_chunk_add_transactions_time_limit: Option, +} + +fn is_false(value: &bool) -> bool { + !*value +} +impl Default for Config { + fn default() -> Self { + Config { + genesis_file: GENESIS_CONFIG_FILENAME.to_string(), + genesis_records_file: None, + validator_key_file: VALIDATOR_KEY_FILE.to_string(), + node_key_file: NODE_KEY_FILE.to_string(), + #[cfg(feature = "json_rpc")] + rpc: Some(RpcConfig::default()), + telemetry: TelemetryConfig::default(), + network: Default::default(), + consensus: Consensus::default(), + tracked_accounts: vec![], + tracked_shards: vec![], + tracked_shard_schedule: None, + archive: false, + save_trie_changes: None, + log_summary_style: LogSummaryStyle::Colored, + log_summary_period: default_log_summary_period(), + gc: GCConfig::default(), + epoch_sync_enabled: default_epoch_sync_enabled(), + view_client_threads: default_view_client_threads(), + view_client_throttle_period: default_view_client_throttle_period(), + trie_viewer_state_size_limit: default_trie_viewer_state_size_limit(), + max_gas_burnt_view: None, + store: unc_store::StoreConfig::default(), + cold_store: None, + split_storage: None, + expected_shutdown: None, + state_sync: default_state_sync(), + state_sync_enabled: default_state_sync_enabled(), + transaction_pool_size_limit: default_transaction_pool_size_limit(), + enable_multiline_logging: default_enable_multiline_logging(), + resharding_config: ReshardingConfig::default(), + tx_routing_height_horizon: default_tx_routing_height_horizon(), + produce_chunk_add_transactions_time_limit: + default_produce_chunk_add_transactions_time_limit(), + } + } +} + +fn default_enable_split_storage_view_client() -> bool { + false +} + +fn default_cold_store_initial_migration_batch_size() -> usize { + 500_000_000 +} + +fn default_cold_store_initial_migration_loop_sleep_duration() -> Duration { + Duration::from_secs(30) +} + +fn default_cold_store_loop_sleep_duration() -> Duration { + Duration::from_secs(1) +} + +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub struct SplitStorageConfig { + #[serde(default = "default_enable_split_storage_view_client")] + pub enable_split_storage_view_client: bool, + + #[serde(default = "default_cold_store_initial_migration_batch_size")] + pub cold_store_initial_migration_batch_size: usize, + #[serde(default = "default_cold_store_initial_migration_loop_sleep_duration")] + pub cold_store_initial_migration_loop_sleep_duration: Duration, + + #[serde(default = "default_cold_store_loop_sleep_duration")] + pub cold_store_loop_sleep_duration: Duration, +} + +impl Default for SplitStorageConfig { + fn default() -> Self { + SplitStorageConfig { + enable_split_storage_view_client: default_enable_split_storage_view_client(), + cold_store_initial_migration_batch_size: + default_cold_store_initial_migration_batch_size(), + cold_store_initial_migration_loop_sleep_duration: + default_cold_store_initial_migration_loop_sleep_duration(), + cold_store_loop_sleep_duration: default_cold_store_loop_sleep_duration(), + } + } +} + +impl Config { + /// load Config from config.json without panic. Do semantic validation on field values. + /// If config file issues occur, a ValidationError::ConfigFileError will be returned; + /// If config semantic checks failed, a ValidationError::ConfigSemanticError will be returned + pub fn from_file(path: &Path) -> Result { + Self::from_file_skip_validation(path).and_then(|config| { + config.validate()?; + Ok(config) + }) + } + + /// load Config from config.json without panic. + /// Skips semantic validation on field values. + /// This function should only return error for file issues. + pub fn from_file_skip_validation(path: &Path) -> Result { + let json_str = + std::fs::read_to_string(path).map_err(|_| ValidationError::ConfigFileError { + error_message: format!("Failed to read config from {}", path.display()), + })?; + let mut unrecognised_fields = Vec::new(); + let json_str_without_comments = unc_config_utils::strip_comments_from_json_str(&json_str) + .map_err(|_| ValidationError::ConfigFileError { + error_message: format!("Failed to strip comments from {}", path.display()), + })?; + let config: Config = serde_ignored::deserialize( + &mut serde_json::Deserializer::from_str(&json_str_without_comments), + |field| unrecognised_fields.push(field.to_string()), + ) + .map_err(|e| ValidationError::ConfigFileError { + error_message: format!("Failed to deserialize config from {}: {:?}", path.display(), e), + })?; + + if !unrecognised_fields.is_empty() { + let s = if unrecognised_fields.len() > 1 { "s" } else { "" }; + let fields = unrecognised_fields.join(", "); + warn!( + target: "uncd", + "{}: encountered unrecognised field{s}: {fields}", + path.display(), + ); + } + + Ok(config) + } + + fn validate(&self) -> Result<(), ValidationError> { + crate::config_validate::validate_config(self) + } + + pub fn write_to_file(&self, path: &Path) -> std::io::Result<()> { + let mut file = File::create(path)?; + let str = serde_json::to_string_pretty(self)?; + file.write_all(str.as_bytes()) + } + + pub fn rpc_addr(&self) -> Option { + #[cfg(feature = "json_rpc")] + if let Some(rpc) = &self.rpc { + return Some(rpc.addr.to_string()); + } + None + } + + pub fn set_rpc_addr(&mut self, addr: tcp::ListenerAddr) { + #[cfg(feature = "json_rpc")] + { + self.rpc.get_or_insert(Default::default()).addr = addr; + } + } +} + +#[easy_ext::ext(GenesisExt)] +impl Genesis { + // Creates new genesis with a given set of accounts and shard layout. + // The first num_validator_seats from accounts will be treated as 'validators'. + pub fn test_with_seeds( + accounts: Vec, + num_validator_seats: NumSeats, + num_validator_seats_per_shard: Vec, + shard_layout: ShardLayout, + ) -> Self { + let mut validators = vec![]; + let mut records = vec![]; + for (i, account) in accounts.into_iter().enumerate() { + let signer = + InMemorySigner::from_seed(account.clone(), KeyType::ED25519, account.as_ref()); + let i = i as u64; + if i < num_validator_seats { + validators.push(AccountInfo { + account_id: account.clone(), + public_key: signer.public_key.clone(), + amount: TESTING_INIT_STAKE, + power: 0, + locked: 0, + }); + } + add_account_with_key( + &mut records, + account, + &signer.public_key.clone(), + TESTING_INIT_BALANCE - if i < num_validator_seats { TESTING_INIT_STAKE } else { 0 }, + if i < num_validator_seats { TESTING_INIT_STAKE } else { 0 }, + TESTING_INIT_POWER, + CryptoHash::default(), + ); + } + add_protocol_account(&mut records); + let config = GenesisConfig { + protocol_version: PROTOCOL_VERSION, + genesis_time: StaticClock::utc(), + chain_id: random_chain_id(), + num_block_producer_seats: num_validator_seats, + num_block_producer_seats_per_shard: num_validator_seats_per_shard.clone(), + avg_hidden_validator_seats_per_shard: vec![0; num_validator_seats_per_shard.len()], + dynamic_resharding: false, + protocol_upgrade_stake_threshold: PROTOCOL_UPGRADE_STAKE_THRESHOLD, + epoch_length: FAST_EPOCH_LENGTH, + gas_limit: INITIAL_GAS_LIMIT, + gas_price_adjustment_rate: GAS_PRICE_ADJUSTMENT_RATE, + block_producer_kickout_threshold: BLOCK_PRODUCER_KICKOUT_THRESHOLD, + validators, + protocol_reward_rate: PROTOCOL_REWARD_RATE, + total_supply: get_initial_supply(&records), + max_inflation_rate: MAX_INFLATION_RATE, + num_blocks_per_year: NUM_BLOCKS_PER_YEAR, + protocol_treasury_account: PROTOCOL_TREASURY_ACCOUNT.parse().unwrap(), + transaction_validity_period: TRANSACTION_VALIDITY_PERIOD, + chunk_producer_kickout_threshold: CHUNK_PRODUCER_KICKOUT_THRESHOLD, + fishermen_threshold: FISHERMEN_THRESHOLD, + min_gas_price: MIN_GAS_PRICE, + shard_layout, + ..Default::default() + }; + Genesis::new(config, records.into()).unwrap() + } + + pub fn test(accounts: Vec, num_validator_seats: NumSeats) -> Self { + Self::test_with_seeds( + accounts, + num_validator_seats, + vec![num_validator_seats], + ShardLayout::v0_single_shard(), + ) + } + + pub fn test_sharded( + accounts: Vec, + num_validator_seats: NumSeats, + num_validator_seats_per_shard: Vec, + ) -> Self { + let num_shards = num_validator_seats_per_shard.len() as NumShards; + Self::test_with_seeds( + accounts, + num_validator_seats, + num_validator_seats_per_shard, + ShardLayout::v0(num_shards, 0), + ) + } + + pub fn test_sharded_new_version( + accounts: Vec, + num_validator_seats: NumSeats, + num_validator_seats_per_shard: Vec, + ) -> Self { + let num_shards = num_validator_seats_per_shard.len() as NumShards; + Self::test_with_seeds( + accounts, + num_validator_seats, + num_validator_seats_per_shard, + ShardLayout::v0(num_shards, 1), + ) + } +} + +#[derive(Clone)] +pub struct UncConfig { + pub config: Config, + pub client_config: ClientConfig, + pub network_config: NetworkConfig, + #[cfg(feature = "json_rpc")] + pub rpc_config: Option, + pub telemetry_config: TelemetryConfig, + pub genesis: Genesis, + pub validator_signer: Option>, +} + +impl UncConfig { + pub fn new( + config: Config, + genesis: Genesis, + network_key_pair: KeyFile, + validator_signer: Option>, + ) -> anyhow::Result { + Ok(UncConfig { + config: config.clone(), + client_config: ClientConfig { + version: Default::default(), + chain_id: genesis.config.chain_id.clone(), + rpc_addr: config.rpc_addr(), + expected_shutdown: MutableConfigValue::new( + config.expected_shutdown, + "expected_shutdown", + ), + block_production_tracking_delay: config.consensus.block_production_tracking_delay, + min_block_production_delay: config.consensus.min_block_production_delay, + max_block_production_delay: config.consensus.max_block_production_delay, + max_block_wait_delay: config.consensus.max_block_wait_delay, + skip_sync_wait: config.network.skip_sync_wait, + sync_check_period: config.consensus.sync_check_period, + sync_step_period: config.consensus.sync_step_period, + sync_height_threshold: config.consensus.sync_height_threshold, + header_sync_initial_timeout: config.consensus.header_sync_initial_timeout, + header_sync_progress_timeout: config.consensus.header_sync_progress_timeout, + header_sync_stall_ban_timeout: config.consensus.header_sync_stall_ban_timeout, + header_sync_expected_height_per_second: config + .consensus + .header_sync_expected_height_per_second, + state_sync_timeout: config.consensus.state_sync_timeout, + min_num_peers: config.consensus.min_num_peers, + log_summary_period: config.log_summary_period, + produce_empty_blocks: config.consensus.produce_empty_blocks, + epoch_length: genesis.config.epoch_length, + num_block_producer_seats: genesis.config.num_block_producer_seats, + ttl_account_id_router: config.network.ttl_account_id_router, + // TODO(1047): this should be adjusted depending on the speed of sync of state. + block_fetch_horizon: config.consensus.block_fetch_horizon, + block_header_fetch_horizon: config.consensus.block_header_fetch_horizon, + catchup_step_period: config.consensus.catchup_step_period, + chunk_request_retry_period: config.consensus.chunk_request_retry_period, + doosmslug_step_period: config.consensus.doomslug_step_period, + tracked_accounts: config.tracked_accounts, + tracked_shards: config.tracked_shards, + tracked_shard_schedule: config.tracked_shard_schedule.unwrap_or(vec![]), + archive: config.archive, + save_trie_changes: config.save_trie_changes.unwrap_or(!config.archive), + log_summary_style: config.log_summary_style, + gc: config.gc, + view_client_threads: config.view_client_threads, + epoch_sync_enabled: config.epoch_sync_enabled, + view_client_throttle_period: config.view_client_throttle_period, + trie_viewer_state_size_limit: config.trie_viewer_state_size_limit, + max_gas_burnt_view: config.max_gas_burnt_view, + enable_statistics_export: config.store.enable_statistics_export, + client_background_migration_threads: config.store.background_migration_threads, + flat_storage_creation_enabled: config.store.flat_storage_creation_enabled, + flat_storage_creation_period: config.store.flat_storage_creation_period, + state_sync_enabled: config.state_sync_enabled, + state_sync: config.state_sync.unwrap_or_default(), + transaction_pool_size_limit: config.transaction_pool_size_limit, + enable_multiline_logging: config.enable_multiline_logging.unwrap_or(true), + resharding_config: MutableConfigValue::new( + config.resharding_config, + "resharding_config", + ), + tx_routing_height_horizon: config.tx_routing_height_horizon, + produce_chunk_add_transactions_time_limit: MutableConfigValue::new( + config.produce_chunk_add_transactions_time_limit, + "produce_chunk_add_transactions_time_limit", + ), + }, + network_config: NetworkConfig::new( + config.network, + network_key_pair.secret_key, + validator_signer.clone(), + config.archive, + )?, + telemetry_config: config.telemetry, + #[cfg(feature = "json_rpc")] + rpc_config: config.rpc, + genesis, + validator_signer, + }) + } + + pub fn rpc_addr(&self) -> Option { + #[cfg(feature = "json_rpc")] + if let Some(rpc) = &self.rpc_config { + return Some(rpc.addr.to_string()); + } + None + } +} + +impl UncConfig { + /// Test tool to save configs back to the folder. + /// Useful for dynamic creating testnet configs and then saving them in different folders. + pub fn save_to_dir(&self, dir: &Path) { + fs::create_dir_all(dir).expect("Failed to create directory"); + + self.config.write_to_file(&dir.join(CONFIG_FILENAME)).expect("Error writing config"); + + if let Some(validator_signer) = &self.validator_signer { + validator_signer + .write_to_file(&dir.join(&self.config.validator_key_file)) + .expect("Error writing validator key file"); + } + + let network_signer = InMemorySigner::from_secret_key( + "node".parse().unwrap(), + self.network_config.node_key.clone(), + ); + network_signer + .write_to_file(&dir.join(&self.config.node_key_file)) + .expect("Error writing key file"); + + self.genesis.to_file(dir.join(&self.config.genesis_file)); + } +} + +fn add_protocol_account(records: &mut Vec) { + let signer = InMemorySigner::from_seed( + PROTOCOL_TREASURY_ACCOUNT.parse().unwrap(), + KeyType::ED25519, + PROTOCOL_TREASURY_ACCOUNT, + ); + add_account_with_key( + records, + PROTOCOL_TREASURY_ACCOUNT.parse().unwrap(), + &signer.public_key, + TESTING_INIT_BALANCE, + 0, + 0, + CryptoHash::default(), + ); +} + +fn random_chain_id() -> String { + format!("test-chain-{}", generate_random_string(5)) +} + +fn add_account_with_key( + records: &mut Vec, + account_id: AccountId, + public_key: &PublicKey, + amount: u128, + staked: u128, + power: u64, + code_hash: CryptoHash, +) { + records.push(StateRecord::Account { + account_id: account_id.clone(), + account: Account::new(amount, staked, power, code_hash, 0), + }); + records.push(StateRecord::AccessKey { + account_id, + public_key: public_key.clone(), + access_key: AccessKey::full_access(), + }); +} + +/// Generates or loads a signer key from given file. +/// +/// If the file already exists, loads the file (panicking if the file is +/// invalid), checks that account id in the file matches `account_id` if it’s +/// given and returns the key. `test_seed` is ignored in this case. +/// +/// If the file does not exist and `account_id` is not `None`, generates a new +/// key, saves it in the file and returns it. If `test_seed` is not `None`, the +/// key generation algorithm is seeded with given string making it fully +/// deterministic. +fn generate_or_load_key( + home_dir: &Path, + filename: &str, + account_id: Option, + test_seed: Option<&str>, +) -> anyhow::Result> { + let path = home_dir.join(filename); + if path.exists() { + let signer = InMemorySigner::from_file(&path) + .with_context(|| format!("Failed initializing signer from {}", path.display()))?; + if let Some(account_id) = account_id { + if account_id != signer.account_id { + return Err(anyhow!( + "‘{}’ contains key for {} but expecting key for {}", + path.display(), + signer.account_id, + account_id + )); + } + } + info!(target: "near", "Reusing key {} for {}", signer.public_key(), signer.account_id); + Ok(Some(signer)) + } else if let Some(account_id) = account_id { + let signer = if let Some(seed) = test_seed { + InMemorySigner::from_seed(account_id, KeyType::ED25519, seed) + } else { + InMemorySigner::from_random(account_id, KeyType::ED25519) + }; + info!(target: "near", "Using key {} for {}", signer.public_key(), signer.account_id); + signer + .write_to_file(&path) + .with_context(|| anyhow!("Failed saving key to ‘{}’", path.display()))?; + Ok(Some(signer)) + } else { + Ok(None) + } +} + +#[test] +fn test_generate_or_load_key() { + let tmp = tempfile::tempdir().unwrap(); + let home_dir = tmp.path(); + + let gen = move |filename: &str, account: &str, seed: &str| { + generate_or_load_key( + home_dir, + filename, + if account.is_empty() { None } else { Some(account.parse().unwrap()) }, + if seed.is_empty() { None } else { Some(seed) }, + ) + }; + + let test_ok = |filename: &str, account: &str, seed: &str| { + let result = gen(filename, account, seed); + let key = result.unwrap().unwrap(); + assert!(home_dir.join("key").exists()); + if !account.is_empty() { + assert_eq!(account, key.account_id.as_str()); + } + key + }; + + let test_err = |filename: &str, account: &str, seed: &str| { + let result = gen(filename, account, seed); + assert!(result.is_err()); + }; + + // account_id == None → do nothing, return None + assert!(generate_or_load_key(home_dir, "key", None, None).unwrap().is_none()); + assert!(!home_dir.join("key").exists()); + + // account_id == Some, file doesn’t exist → create new key + let key = test_ok("key", "fred", ""); + + // file exists → load key, compare account if given + assert!(key == test_ok("key", "", "")); + assert!(key == test_ok("key", "fred", "")); + test_err("key", "barney", ""); + + // test_seed == Some → the same key is generated + let k1 = test_ok("k1", "fred", "foo"); + let k2 = test_ok("k2", "barney", "foo"); + let k3 = test_ok("k3", "fred", "bar"); + + assert!(k1.public_key == k2.public_key && k1.secret_key == k2.secret_key); + assert!(k1 != k3); + + // file contains invalid JSON -> should return an error + { + let mut file = std::fs::File::create(&home_dir.join("bad_key")).unwrap(); + writeln!(file, "not JSON").unwrap(); + } + test_err("bad_key", "fred", ""); +} + +/// Checks that validator and node keys exist. +/// If a key is needed and doesn't exist, then it gets created. +fn generate_or_load_keys( + dir: &Path, + config: &Config, + chain_id: &str, + account_id: Option, + test_seed: Option<&str>, +) -> anyhow::Result<()> { + generate_or_load_key(dir, &config.node_key_file, Some("node".parse().unwrap()), None)?; + match chain_id { + unc_primitives::chains::MAINNET | unc_primitives::chains::TESTNET => { + generate_or_load_key(dir, &config.validator_key_file, account_id, None)?; + } + _ => { + let account_id = account_id.unwrap_or_else(|| "test.near".parse().unwrap()); + generate_or_load_key(dir, &config.validator_key_file, Some(account_id), test_seed)?; + } + } + Ok(()) +} + +fn set_block_production_delay(chain_id: &str, fast: bool, config: &mut Config) { + match chain_id { + unc_primitives::chains::MAINNET => { + config.consensus.min_block_production_delay = + Duration::from_millis(MAINNET_MIN_BLOCK_PRODUCTION_DELAY); + config.consensus.max_block_production_delay = + Duration::from_millis(MAINNET_MAX_BLOCK_PRODUCTION_DELAY); + } + unc_primitives::chains::TESTNET => { + config.consensus.min_block_production_delay = + Duration::from_millis(TESTNET_MIN_BLOCK_PRODUCTION_DELAY); + config.consensus.max_block_production_delay = + Duration::from_millis(TESTNET_MAX_BLOCK_PRODUCTION_DELAY); + } + _ => { + if fast { + config.consensus.min_block_production_delay = + Duration::from_millis(FAST_MIN_BLOCK_PRODUCTION_DELAY); + config.consensus.max_block_production_delay = + Duration::from_millis(FAST_MAX_BLOCK_PRODUCTION_DELAY); + } + } + } +} + +/// Initializes Genesis, client Config, node and validator keys, and stores in the specified folder. +/// +/// This method supports the following use cases: +/// * When no Config, Genesis or key files exist. +/// * When Config and Genesis files exist, but no keys exist. +/// * When all of Config, Genesis, and key files exist. +/// +/// Note that this method does not support the case where the configuration file exists but the genesis file does not exist. +pub fn init_configs( + dir: &Path, + chain_id: Option, + account_id: Option, + test_seed: Option<&str>, + num_shards: NumShards, + fast: bool, + genesis: Option<&str>, + should_download_genesis: bool, + download_genesis_url: Option<&str>, + download_records_url: Option<&str>, + should_download_config: bool, + download_config_url: Option<&str>, + boot_nodes: Option<&str>, + max_gas_burnt_view: Option, +) -> anyhow::Result<()> { + fs::create_dir_all(dir).with_context(|| anyhow!("Failed to create directory {:?}", dir))?; + + assert_ne!(chain_id, Some("".to_string())); + let chain_id = match chain_id { + Some(chain_id) => chain_id, + None => random_chain_id(), + }; + + // Check if config already exists in home dir. + if dir.join(CONFIG_FILENAME).exists() { + let config = Config::from_file(&dir.join(CONFIG_FILENAME)) + .with_context(|| anyhow!("Failed to read config {}", dir.display()))?; + let genesis_file = config.genesis_file.clone(); + let file_path = dir.join(&genesis_file); + // Check that Genesis exists and can be read. + // If `config.json` exists, but `genesis.json` doesn't exist, + // that isn't supported by the `init` command. + let _genesis = GenesisConfig::from_file(file_path).with_context(move || { + anyhow!("Failed to read genesis config {}/{}", dir.display(), genesis_file) + })?; + // Check that `node_key.json` and `validator_key.json` exist. + // Create if needed and they don't exist. + generate_or_load_keys(dir, &config, &chain_id, account_id, test_seed)?; + return Ok(()); + } + + let mut config = Config::default(); + // Make sure node tracks all shards, see + // https://github.com/utnet-org/utility/issues/7388 + config.tracked_shards = vec![0]; + // If a config gets generated, block production times may need to be updated. + set_block_production_delay(&chain_id, fast, &mut config); + + if let Some(url) = download_config_url { + download_config(url, &dir.join(CONFIG_FILENAME)) + .context(format!("Failed to download the config file from {}", url))?; + config = Config::from_file(&dir.join(CONFIG_FILENAME))?; + } else if should_download_config { + let url = get_config_url(&chain_id); + download_config(&url, &dir.join(CONFIG_FILENAME)) + .context(format!("Failed to download the config file from {}", url))?; + config = Config::from_file(&dir.join(CONFIG_FILENAME))?; + } + + if let Some(nodes) = boot_nodes { + config.network.boot_nodes = nodes.to_string(); + } + + if let Some(max_gas_burnt_view) = max_gas_burnt_view { + config.max_gas_burnt_view = Some(max_gas_burnt_view); + } + + // Before finalizing the Config and Genesis, make sure the node and validator keys exist. + generate_or_load_keys(dir, &config, &chain_id, account_id, test_seed)?; + match chain_id.as_ref() { + unc_primitives::chains::MAINNET | unc_primitives::chains::TESTNET => { + if test_seed.is_some() { + bail!("Test seed is not supported for {chain_id}"); + } + config.telemetry.endpoints.push(NETWORK_TELEMETRY_URL.replace("{}", &chain_id)); + } + _ => { + // Create new configuration, key files and genesis for one validator. + config.network.skip_sync_wait = true; + } + } + + config.write_to_file(&dir.join(CONFIG_FILENAME)).with_context(|| { + format!("Error writing config to {}", dir.join(CONFIG_FILENAME).display()) + })?; + + match chain_id.as_ref() { + unc_primitives::chains::MAINNET => { + let genesis = unc_mainnet_res::mainnet_genesis(); + genesis.to_file(dir.join(config.genesis_file)); + info!(target: "near", "Generated mainnet genesis file in {}", dir.display()); + } + unc_primitives::chains::TESTNET => { + if let Some(ref filename) = config.genesis_records_file { + let records_path = dir.join(filename); + + if let Some(url) = download_records_url { + download_records(url, &records_path) + .context(format!("Failed to download the records file from {}", url))?; + } else if should_download_genesis { + let url = get_records_url(&chain_id); + download_records(&url, &records_path) + .context(format!("Failed to download the records file from {}", url))?; + } + } + + // download genesis from s3 + let genesis_path = dir.join("genesis.json"); + let mut genesis_path_str = + genesis_path.to_str().with_context(|| "Genesis path must be initialized")?; + + if let Some(url) = download_genesis_url { + download_genesis(url, &genesis_path) + .context(format!("Failed to download the genesis file from {}", url))?; + } else if should_download_genesis { + let url = get_genesis_url(&chain_id); + download_genesis(&url, &genesis_path) + .context(format!("Failed to download the genesis file from {}", url))?; + } else { + genesis_path_str = match genesis { + Some(g) => g, + None => { + bail!("Genesis file is required for {chain_id}.\nUse <--genesis|--download-genesis>"); + } + }; + } + + let mut genesis = match &config.genesis_records_file { + Some(records_file) => { + let records_path = dir.join(records_file); + let records_path_str = records_path + .to_str() + .with_context(|| "Records path must be initialized")?; + Genesis::from_files( + genesis_path_str, + records_path_str, + GenesisValidationMode::Full, + ) + } + None => Genesis::from_file(genesis_path_str, GenesisValidationMode::Full), + }?; + + genesis.config.chain_id = chain_id.clone(); + + genesis.to_file(dir.join(config.genesis_file)); + info!(target: "near", "Generated for {chain_id} network node key and genesis file in {}", dir.display()); + } + _ => { + let validator_file = dir.join(&config.validator_key_file); + let signer = InMemorySigner::from_file(&validator_file).unwrap(); + + let mut records = vec![]; + add_account_with_key( + &mut records, + signer.account_id.clone(), + &signer.public_key(), + TESTING_INIT_BALANCE, + TESTING_INIT_STAKE, + TESTING_INIT_POWER, + CryptoHash::default(), + ); + add_protocol_account(&mut records); + let shards = if num_shards > 1 { + ShardLayout::v1( + (1..num_shards) + .map(|f| { + AccountId::from_str(format!("shard{f}.test.near").as_str()).unwrap() + }) + .collect(), + None, + 1, + ) + } else { + ShardLayout::v0_single_shard() + }; + + let genesis_config = GenesisConfig { + protocol_version: PROTOCOL_VERSION, + genesis_time: StaticClock::utc(), + chain_id, + genesis_height: 0, + num_block_producer_seats: NUM_BLOCK_PRODUCER_SEATS, + num_block_producer_seats_per_shard: get_num_seats_per_shard( + num_shards, + NUM_BLOCK_PRODUCER_SEATS, + ), + avg_hidden_validator_seats_per_shard: (0..num_shards).map(|_| 0).collect(), + dynamic_resharding: false, + protocol_upgrade_stake_threshold: PROTOCOL_UPGRADE_STAKE_THRESHOLD, + epoch_length: if fast { FAST_EPOCH_LENGTH } else { EXPECTED_EPOCH_LENGTH }, + gas_limit: INITIAL_GAS_LIMIT, + gas_price_adjustment_rate: GAS_PRICE_ADJUSTMENT_RATE, + block_producer_kickout_threshold: BLOCK_PRODUCER_KICKOUT_THRESHOLD, + chunk_producer_kickout_threshold: CHUNK_PRODUCER_KICKOUT_THRESHOLD, + online_max_threshold: Rational32::new(99, 100), + online_min_threshold: Rational32::new(BLOCK_PRODUCER_KICKOUT_THRESHOLD as i32, 100), + validators: vec![AccountInfo { + account_id: signer.account_id.clone(), + public_key: signer.public_key(), + amount: TESTING_INIT_STAKE, + power: 0, + locked: 0, + }], + transaction_validity_period: TRANSACTION_VALIDITY_PERIOD, + protocol_reward_rate: PROTOCOL_REWARD_RATE, + max_inflation_rate: MAX_INFLATION_RATE, + total_supply: get_initial_supply(&records), + num_blocks_per_year: NUM_BLOCKS_PER_YEAR, + protocol_treasury_account: signer.account_id, + fishermen_threshold: FISHERMEN_THRESHOLD, + shard_layout: shards, + min_gas_price: MIN_GAS_PRICE, + ..Default::default() + }; + let genesis = Genesis::new(genesis_config, records.into())?; + genesis.to_file(dir.join(config.genesis_file)); + info!(target: "near", "Generated node key, validator key, genesis file in {}", dir.display()); + } + } + Ok(()) +} + +pub fn create_testnet_configs_from_seeds( + seeds: Vec, + num_shards: NumShards, + num_non_validator_seats: NumSeats, + local_ports: bool, + archive: bool, + tracked_shards: Vec, +) -> (Vec, Vec, Vec, Genesis) { + let num_validator_seats = (seeds.len() - num_non_validator_seats as usize) as NumSeats; + let validator_signers = + seeds.iter().map(|seed| create_test_signer(seed.as_str())).collect::>(); + let network_signers = seeds + .iter() + .map(|seed| InMemorySigner::from_seed("node".parse().unwrap(), KeyType::ED25519, seed)) + .collect::>(); + + let shard_layout = ShardLayout::v0(num_shards, 0); + let accounts_to_add_to_genesis: Vec = + seeds.iter().map(|s| s.parse().unwrap()).collect(); + + let genesis = Genesis::test_with_seeds( + accounts_to_add_to_genesis, + num_validator_seats, + get_num_seats_per_shard(num_shards, num_validator_seats), + shard_layout, + ); + let mut configs = vec![]; + let first_node_addr = tcp::ListenerAddr::reserve_for_test(); + for i in 0..seeds.len() { + let mut config = Config::default(); + config.rpc.get_or_insert(Default::default()).enable_debug_rpc = true; + config.consensus.min_block_production_delay = Duration::from_millis(600); + config.consensus.max_block_production_delay = Duration::from_millis(2000); + if local_ports { + config.network.addr = if i == 0 { + first_node_addr.to_string() + } else { + tcp::ListenerAddr::reserve_for_test().to_string() + }; + config.set_rpc_addr(tcp::ListenerAddr::reserve_for_test()); + config.network.boot_nodes = if i == 0 { + "".to_string() + } else { + format!("{}@{}", network_signers[0].public_key, first_node_addr) + }; + config.network.skip_sync_wait = num_validator_seats == 1; + } + config.archive = archive; + config.tracked_shards = tracked_shards.clone(); + config.consensus.min_num_peers = + std::cmp::min(num_validator_seats as usize - 1, config.consensus.min_num_peers); + configs.push(config); + } + (configs, validator_signers, network_signers, genesis) +} + +/// Create testnet configuration. If `local_ports` is true, +/// sets up new ports for all nodes except the first one and sets boot node to it. +pub fn create_testnet_configs( + num_shards: NumShards, + num_validator_seats: NumSeats, + num_non_validator_seats: NumSeats, + prefix: &str, + local_ports: bool, + archive: bool, + tracked_shards: Vec, +) -> (Vec, Vec, Vec, Genesis, Vec) +{ + let shard_keys = vec![]; + let (configs, validator_signers, network_signers, genesis) = create_testnet_configs_from_seeds( + (0..(num_validator_seats + num_non_validator_seats)) + .map(|i| format!("{}{}", prefix, i)) + .collect::>(), + num_shards, + num_non_validator_seats, + local_ports, + archive, + tracked_shards, + ); + + (configs, validator_signers, network_signers, genesis, shard_keys) +} + +pub fn init_testnet_configs( + dir: &Path, + num_shards: NumShards, + num_validator_seats: NumSeats, + num_non_validator_seats: NumSeats, + prefix: &str, + local_ports: bool, + archive: bool, + tracked_shards: Vec, +) { + let (configs, validator_signers, network_signers, genesis, shard_keys) = create_testnet_configs( + num_shards, + num_validator_seats, + num_non_validator_seats, + prefix, + local_ports, + archive, + tracked_shards, + ); + let log_config = LogConfig::default(); + for i in 0..(num_validator_seats + num_non_validator_seats) as usize { + let config = &configs[i]; + let node_dir = dir.join(format!("{}{}", prefix, i)); + fs::create_dir_all(node_dir.clone()).expect("Failed to create directory"); + + validator_signers[i] + .write_to_file(&node_dir.join(&config.validator_key_file)) + .expect("Error writing validator key file"); + network_signers[i] + .write_to_file(&node_dir.join(&config.node_key_file)) + .expect("Error writing key file"); + for key in &shard_keys { + key.write_to_file(&node_dir.join(format!("{}_key.json", key.account_id))) + .expect("Error writing shard file"); + } + + genesis.to_file(&node_dir.join(&config.genesis_file)); + config.write_to_file(&node_dir.join(CONFIG_FILENAME)).expect("Error writing config"); + log_config + .write_to_file(&node_dir.join(LOG_CONFIG_FILENAME)) + .expect("Error writing log config"); + info!(target: "near", "Generated node key, validator key, genesis file in {}", node_dir.display()); + } +} + +pub fn get_genesis_url(chain_id: &str) -> String { + format!( + "https://unc-s3.jongun2038.win/{}/genesis.json.xz", + chain_id, + ) +} + +pub fn get_records_url(chain_id: &str) -> String { + format!( + "https://s3-us-west-1.amazonaws.com/build.nearprotocol.com/framework-deploy/{}/records.json.xz", + chain_id, + ) +} + +pub fn get_config_url(chain_id: &str) -> String { + format!( + "https://unc-s3.jongun2038.win/{}/config.json.xz", + chain_id, + ) +} + +pub fn download_genesis(url: &str, path: &Path) -> Result<(), FileDownloadError> { + info!(target: "near", "Downloading genesis file from: {} ...", url); + let result = run_download_file(url, path); + if result.is_ok() { + info!(target: "near", "Saved the genesis file to: {} ...", path.display()); + } + result +} + +pub fn download_records(url: &str, path: &Path) -> Result<(), FileDownloadError> { + info!(target: "near", "Downloading records file from: {} ...", url); + let result = run_download_file(url, path); + if result.is_ok() { + info!(target: "near", "Saved the records file to: {} ...", path.display()); + } + result +} + +pub fn download_config(url: &str, path: &Path) -> Result<(), FileDownloadError> { + info!(target: "near", "Downloading config file from: {} ...", url); + let result = run_download_file(url, path); + if result.is_ok() { + info!(target: "near", "Saved the config file to: {} ...", path.display()); + } + result +} + +#[derive(serde::Deserialize)] +struct NodeKeyFile { + account_id: String, + public_key: PublicKey, + secret_key: unc_crypto::SecretKey, +} + +impl NodeKeyFile { + // the file can be JSON with comments + fn from_file(path: &Path) -> std::io::Result { + let mut file = File::open(path)?; + let mut json_str = String::new(); + file.read_to_string(&mut json_str)?; + + let json_str_without_comments = unc_config_utils::strip_comments_from_json_str(&json_str)?; + + Ok(serde_json::from_str(&json_str_without_comments)?) + } +} + +impl From for KeyFile { + fn from(this: NodeKeyFile) -> Self { + Self { + account_id: if this.account_id.is_empty() { + "node".to_string() + } else { + this.account_id + } + .try_into() + .unwrap(), + public_key: this.public_key, + secret_key: this.secret_key, + } + } +} + +pub fn load_config( + dir: &Path, + genesis_validation: GenesisValidationMode, +) -> anyhow::Result { + let mut validation_errors = ValidationErrors::new(); + + // if config.json has file issues, the program will directly panic + let config = Config::from_file_skip_validation(&dir.join(CONFIG_FILENAME))?; + // do config.json validation later so that genesis_file, validator_file and genesis_file can be validated before program panic + if let Err(e) = config.validate() { + validation_errors.push_errors(e) + }; + + let validator_file = dir.join(&config.validator_key_file); + let validator_signer = if validator_file.exists() { + match InMemoryValidatorSigner::from_file(&validator_file) { + Ok(signer) => Some(Arc::new(signer) as Arc), + Err(_) => { + let error_message = format!( + "Failed initializing validator signer from {}", + validator_file.display() + ); + validation_errors.push_validator_key_file_error(error_message); + None + } + } + } else { + None + }; + + let node_key_path = dir.join(&config.node_key_file); + let network_signer_result = NodeKeyFile::from_file(&node_key_path); + let network_signer = match network_signer_result { + Ok(node_key_file) => Some(node_key_file), + Err(_) => { + let error_message = + format!("Failed reading node key file from {}", node_key_path.display()); + validation_errors.push_node_key_file_error(error_message); + None + } + }; + + let genesis_file = dir.join(&config.genesis_file); + let genesis_result = match &config.genesis_records_file { + // only load Genesis from file. Skip test for now. + // this allows us to know the chain_id in order to check tracked_shards even if semantics checks fail. + Some(records_file) => Genesis::from_files( + &genesis_file, + dir.join(records_file), + GenesisValidationMode::UnsafeFast, + ), + None => Genesis::from_file(&genesis_file, GenesisValidationMode::UnsafeFast), + }; + + let genesis = match genesis_result { + Ok(genesis) => { + if let Err(e) = genesis.validate(genesis_validation) { + validation_errors.push_errors(e) + }; + if validator_signer.is_some() + && matches!( + genesis.config.chain_id.as_ref(), + unc_primitives::chains::MAINNET | unc_primitives::chains::TESTNET + ) + && config.tracked_shards.is_empty() + { + // Make sure validators tracks all shards, see + // https://github.com/utnet-org/utility/issues/7388 + let error_message = "The `chain_id` field specified in genesis is among mainnet/betanet/testnet, so validator must track all shards. Please change `tracked_shards` field in config.json to be any non-empty vector"; + validation_errors.push_cross_file_semantics_error(error_message.to_string()); + } + Some(genesis) + } + Err(error) => { + validation_errors.push_errors(error); + None + } + }; + + validation_errors.return_ok_or_error()?; + + if genesis.is_none() || network_signer.is_none() { + panic!("Genesis and network_signer should not be None by now.") + } + let unc_config = UncConfig::new( + config, + genesis.unwrap(), + network_signer.unwrap().into(), + validator_signer, + )?; + Ok(unc_config) +} + +pub fn load_test_config(seed: &str, addr: tcp::ListenerAddr, genesis: Genesis) -> UncConfig { + let mut config = Config::default(); + config.network.addr = addr.to_string(); + config.set_rpc_addr(tcp::ListenerAddr::reserve_for_test()); + config.consensus.min_block_production_delay = + Duration::from_millis(FAST_MIN_BLOCK_PRODUCTION_DELAY); + config.consensus.max_block_production_delay = + Duration::from_millis(FAST_MAX_BLOCK_PRODUCTION_DELAY); + let (signer, validator_signer) = if seed.is_empty() { + let signer = + Arc::new(InMemorySigner::from_random("node".parse().unwrap(), KeyType::ED25519)); + (signer, None) + } else { + let signer = + Arc::new(InMemorySigner::from_seed(seed.parse().unwrap(), KeyType::ED25519, seed)); + let validator_signer = Arc::new(create_test_signer(seed)) as Arc; + (signer, Some(validator_signer)) + }; + UncConfig::new(config, genesis, signer.into(), validator_signer).unwrap() +} + +#[test] +fn test_init_config_localnet() { + // Check that we can initialize the config with multiple shards. + let temp_dir = tempdir().unwrap(); + init_configs( + &temp_dir.path(), + Some("localnet".to_string()), + None, + Some("seed1"), + 3, + false, + None, + false, + None, + None, + false, + None, + None, + None, + ) + .unwrap(); + let genesis = + Genesis::from_file(temp_dir.path().join("genesis.json"), GenesisValidationMode::UnsafeFast) + .unwrap(); + assert_eq!(genesis.config.chain_id, "localnet"); + assert_eq!(genesis.config.shard_layout.shard_ids().count(), 3); + assert_eq!( + account_id_to_shard_id( + &AccountId::from_str("foobar.near").unwrap(), + &genesis.config.shard_layout + ), + 0 + ); + assert_eq!( + account_id_to_shard_id( + &AccountId::from_str("shard1.test.near").unwrap(), + &genesis.config.shard_layout + ), + 1 + ); + assert_eq!( + account_id_to_shard_id( + &AccountId::from_str("shard2.test.near").unwrap(), + &genesis.config.shard_layout + ), + 2 + ); +} + +#[test] +// Tests that `init_configs()` works if both config and genesis file exists, but the node key and validator key files don't exist. +// Test does the following: +// * Initialize all config and key files +// * Check that the key files exist +// * Remove the key files +// * Run the initialization again +// * Check that the key files got created +fn test_init_config_localnet_keep_config_create_node_key() { + let temp_dir = tempdir().unwrap(); + // Initialize all config and key files. + init_configs( + &temp_dir.path(), + Some("localnet".to_string()), + Some(AccountId::from_str("account.near").unwrap()), + Some("seed1"), + 3, + false, + None, + false, + None, + None, + false, + None, + None, + None, + ) + .unwrap(); + + // Check that the key files exist. + let _genesis = + Genesis::from_file(temp_dir.path().join("genesis.json"), GenesisValidationMode::Full) + .unwrap(); + let config = Config::from_file(&temp_dir.path().join(CONFIG_FILENAME)).unwrap(); + let node_key_file = temp_dir.path().join(config.node_key_file); + let validator_key_file = temp_dir.path().join(config.validator_key_file); + assert!(node_key_file.exists()); + assert!(validator_key_file.exists()); + + // Remove the key files. + std::fs::remove_file(&node_key_file).unwrap(); + std::fs::remove_file(&validator_key_file).unwrap(); + + // Run the initialization again. + init_configs( + &temp_dir.path(), + Some("localnet".to_string()), + Some(AccountId::from_str("account.near").unwrap()), + Some("seed1"), + 3, + false, + None, + false, + None, + None, + false, + None, + None, + None, + ) + .unwrap(); + + // Check that the key files got created. + let _node_signer = InMemorySigner::from_file(&node_key_file).unwrap(); + let _validator_signer = InMemorySigner::from_file(&validator_key_file).unwrap(); +} + +/// Tests that loading a config.json file works and results in values being +/// correctly parsed and defaults being applied correctly applied. +/// We skip config validation since we only care about Config being correctly loaded from file. +#[test] +fn test_config_from_file_skip_validation() { + let base = Path::new(env!("CARGO_MANIFEST_DIR")); + + for (has_gc, path) in + [(true, "res/example-config-gc.json"), (false, "res/example-config-no-gc.json")] + { + let path = base.join(path); + let data = std::fs::read(path).unwrap(); + let tmp = tempfile::NamedTempFile::new().unwrap(); + tmp.as_file().write_all(&data).unwrap(); + + let config = Config::from_file_skip_validation(&tmp.into_temp_path()).unwrap(); + + // TODO(mina86): We might want to add more checks. Looking at all + // values is probably not worth it but there may be some other defaults + // we want to ensure that they happen. + let want_gc = if has_gc { + GCConfig { gc_blocks_limit: 42, gc_fork_clean_step: 420, gc_num_epochs_to_keep: 24 } + } else { + GCConfig { gc_blocks_limit: 2, gc_fork_clean_step: 100, gc_num_epochs_to_keep: 5 } + }; + assert_eq!(want_gc, config.gc); + + assert_eq!( + vec!["https://explorer.mainnet.near.org/api/nodes".to_string()], + config.telemetry.endpoints + ); + } +} + +#[test] +fn test_create_testnet_configs() { + let num_shards = 4; + let num_validator_seats = 4; + let num_non_validator_seats = 8; + let prefix = "node"; + let local_ports = true; + + // Set all supported options to true and verify config and genesis. + + let archive = true; + let tracked_shards: Vec = vec![0, 1, 3]; + + let (configs, _validator_signers, _network_signers, genesis, _shard_keys) = + create_testnet_configs( + num_shards, + num_validator_seats, + num_non_validator_seats, + prefix, + local_ports, + archive, + tracked_shards.clone(), + ); + + assert_eq!(configs.len() as u64, num_validator_seats + num_non_validator_seats); + + for config in configs { + assert_eq!(config.archive, true); + assert_eq!(config.tracked_shards, tracked_shards); + } + + assert_eq!(genesis.config.validators.len(), num_shards as usize); + assert_eq!(genesis.config.shard_layout.shard_ids().count() as NumShards, num_shards); + + // Set all supported options to false and verify config and genesis. + + let archive = false; + let tracked_shards: Vec = vec![]; + + let (configs, _validator_signers, _network_signers, genesis, _shard_keys) = + create_testnet_configs( + num_shards, + num_validator_seats, + num_non_validator_seats, + prefix, + local_ports, + archive, + tracked_shards.clone(), + ); + assert_eq!(configs.len() as u64, num_validator_seats + num_non_validator_seats); + + for config in configs { + assert_eq!(config.archive, false); + assert_eq!(config.tracked_shards, tracked_shards); + } + + assert_eq!(genesis.config.validators.len() as u64, num_shards); + assert_eq!(genesis.config.shard_layout.shard_ids().count() as NumShards, num_shards); +} diff --git a/framework/src/config_validate.rs b/framework/src/config_validate.rs new file mode 100644 index 000000000..f4455aea7 --- /dev/null +++ b/framework/src/config_validate.rs @@ -0,0 +1,253 @@ +use unc_chain_configs::{ExternalStorageLocation, SyncConfig}; +use unc_config_utils::{ValidationError, ValidationErrors}; +use std::collections::HashSet; +use std::path::Path; + +use crate::config::Config; + +/// Validate Config extracted from config.json. +/// This function does not panic. It returns the error if any validation fails. +pub fn validate_config(config: &Config) -> Result<(), ValidationError> { + let mut validation_errors = ValidationErrors::new(); + let mut config_validator = ConfigValidator::new(config, &mut validation_errors); + tracing::info!(target: "config", "Validating Config, extracted from config.json..."); + config_validator.validate() +} + +struct ConfigValidator<'a> { + config: &'a Config, + validation_errors: &'a mut ValidationErrors, +} + +impl<'a> ConfigValidator<'a> { + fn new(config: &'a Config, validation_errors: &'a mut ValidationErrors) -> Self { + Self { config, validation_errors } + } + + fn validate(&mut self) -> Result<(), ValidationError> { + self.validate_all_conditions(); + self.result_with_full_error() + } + + /// this function would check all conditions, and add all error messages to ConfigValidator.errors + fn validate_all_conditions(&mut self) { + if !self.config.archive && self.config.save_trie_changes == Some(false) { + let error_message = "Configuration with archive = false and save_trie_changes = false is not supported because non-archival nodes must save trie changes in order to do do garbage collection.".to_string(); + self.validation_errors.push_config_semantics_error(error_message); + } + + // Checking that if cold storage is configured, trie changes are definitely saved. + // Unlike in the previous case, None is not a valid option here. + if self.config.cold_store.is_some() && self.config.save_trie_changes != Some(true) { + let error_message = format!("cold_store is configured, but save_trie_changes is {:?}. Trie changes should be saved to support cold storage.", self.config.save_trie_changes); + self.validation_errors.push_config_semantics_error(error_message); + } + + if self.config.consensus.min_block_production_delay + > self.config.consensus.max_block_production_delay + { + let error_message = format!( + "min_block_production_delay: {:?} is greater than max_block_production_delay: {:?}", + self.config.consensus.min_block_production_delay, + self.config.consensus.max_block_production_delay + ); + self.validation_errors.push_config_semantics_error(error_message); + } + + if self.config.consensus.min_block_production_delay + > self.config.consensus.max_block_wait_delay + { + let error_message = format!( + "min_block_production_delay: {:?} is greater than max_block_wait_delay: {:?}", + self.config.consensus.min_block_production_delay, + self.config.consensus.max_block_wait_delay + ); + self.validation_errors.push_config_semantics_error(error_message); + } + + if self.config.consensus.header_sync_expected_height_per_second == 0 { + let error_message = + "consensus.header_sync_expected_height_per_second should not be 0".to_string(); + self.validation_errors.push_config_semantics_error(error_message); + } + + if self.config.gc.gc_blocks_limit == 0 + || self.config.gc.gc_fork_clean_step == 0 + || self.config.gc.gc_num_epochs_to_keep == 0 + { + let error_message = format!("gc config values should all be greater than 0, but gc_blocks_limit is {:?}, gc_fork_clean_step is {}, gc_num_epochs_to_keep is {}.", self.config.gc.gc_blocks_limit, self.config.gc.gc_fork_clean_step, self.config.gc.gc_num_epochs_to_keep); + self.validation_errors.push_config_semantics_error(error_message); + } + + if let Some(state_sync) = &self.config.state_sync { + if let Some(dump_config) = &state_sync.dump { + if let Some(restart_dump_for_shards) = &dump_config.restart_dump_for_shards { + let unique_values: HashSet<_> = restart_dump_for_shards.iter().collect(); + if unique_values.len() != restart_dump_for_shards.len() { + let error_message = format!("'config.state_sync.dump.restart_dump_for_shards' contains duplicate values."); + self.validation_errors.push_config_semantics_error(error_message); + } + } + + match &dump_config.location { + ExternalStorageLocation::S3 { bucket, region } => { + if bucket.is_empty() || region.is_empty() { + let error_message = format!("'config.state_sync.dump.location.S3.bucket' and 'config.state_sync.dump.location.S3.region' need to be specified when 'config.state_sync.dump.location.S3' is present."); + self.validation_errors.push_config_semantics_error(error_message); + } + } + ExternalStorageLocation::Filesystem { root_dir } => { + if root_dir.as_path() == Path::new("") { + let error_message = format!("'config.state_sync.dump.location.Filesystem.root_dir' needs to be specified when 'config.state_sync.dump.location.Filesystem' is present."); + self.validation_errors.push_config_semantics_error(error_message); + } + } + ExternalStorageLocation::GCS { bucket } => { + if bucket.is_empty() { + let error_message = format!("'config.state_sync.dump.location.GCS.bucket' needs to be specified when 'config.state_sync.dump.location.GCS' is present."); + self.validation_errors.push_config_semantics_error(error_message); + } + } + } + + if let Some(credentials_file) = &dump_config.credentials_file { + if !credentials_file.exists() || !credentials_file.is_file() { + let error_message = format!("'config.state_sync.dump.credentials_file' is provided but the specified file does not exist or is not a file."); + self.validation_errors.push_config_semantics_error(error_message); + } + } + } + match &state_sync.sync { + SyncConfig::Peers => {} + SyncConfig::ExternalStorage(config) => { + match &config.location { + ExternalStorageLocation::S3 { bucket, region } => { + if bucket.is_empty() || region.is_empty() { + let error_message = format!("'config.state_sync.sync.ExternalStorage.location.S3.bucket' and 'config.state_sync.sync.ExternalStorage.location.S3.region' need to be specified when 'config.state_sync.sync.ExternalStorage.location.S3' is present."); + self.validation_errors.push_config_semantics_error(error_message); + } + } + ExternalStorageLocation::Filesystem { root_dir } => { + if root_dir.as_path() == Path::new("") { + let error_message = format!("'config.state_sync.sync.ExternalStorage.location.Filesystem.root_dir' needs to be specified when 'config.state_sync.sync.ExternalStorage.location.Filesystem' is present."); + self.validation_errors.push_config_semantics_error(error_message); + } + } + ExternalStorageLocation::GCS { bucket } => { + if bucket.is_empty() { + let error_message = format!("'config.state_sync.sync.ExternalStorage.location.GCS.bucket' needs to be specified when 'config.state_sync.sync.ExternalStorage.location.GCS' is present."); + self.validation_errors.push_config_semantics_error(error_message); + } + } + } + if config.num_concurrent_requests == 0 { + let error_message = format!("'config.state_sync.sync.ExternalStorage.num_concurrent_requests' needs to be greater than 0"); + self.validation_errors.push_config_semantics_error(error_message); + } + } + } + } + + let tx_routing_height_horizon = self.config.tx_routing_height_horizon; + if tx_routing_height_horizon < 2 { + let error_message = format!("'config.tx_routing_height_horizon' needs to be at least 2, got {tx_routing_height_horizon}."); + self.validation_errors.push_config_semantics_error(error_message); + } + if tx_routing_height_horizon > 100 { + let error_message = format!("'config.tx_routing_height_horizon' can't be too high to avoid spamming the network. Keep it below 100. Got {tx_routing_height_horizon}."); + self.validation_errors.push_config_semantics_error(error_message); + } + } + + fn result_with_full_error(&self) -> Result<(), ValidationError> { + if self.validation_errors.is_empty() { + Ok(()) + } else { + let full_err_msg = self.validation_errors.generate_error_message_per_type().unwrap(); + Err(ValidationError::ConfigSemanticsError { error_message: full_err_msg }) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + #[should_panic(expected = "gc config values should all be greater than 0")] + fn test_gc_config_value_nonzero() { + let mut config = Config::default(); + config.gc.gc_blocks_limit = 0; + // set tracked_shards to be non-empty + config.tracked_shards.push(20); + validate_config(&config).unwrap(); + } + + #[test] + #[should_panic( + expected = "Configuration with archive = false and save_trie_changes = false is not supported" + )] + fn test_archive_false_save_trie_changes_false() { + let mut config = Config::default(); + config.archive = false; + config.save_trie_changes = Some(false); + // set tracked_shards to be non-empty + config.tracked_shards.push(20); + validate_config(&config).unwrap(); + } + + #[test] + #[should_panic( + expected = "\\nconfig.json semantic issue: Configuration with archive = false and save_trie_changes = false is not supported because non-archival nodes must save trie changes in order to do do garbage collection.\\nconfig.json semantic issue: gc config values should all be greater than 0" + )] + fn test_multiple_config_validation_errors() { + let mut config = Config::default(); + config.archive = false; + config.save_trie_changes = Some(false); + config.gc.gc_blocks_limit = 0; + // set tracked_shards to be non-empty + config.tracked_shards.push(20); + validate_config(&config).unwrap(); + } + + #[test] + #[should_panic( + expected = "\\nconfig.json semantic issue: cold_store is configured, but save_trie_changes is None. Trie changes should be saved to support cold storage." + )] + fn test_cold_store_without_save_trie_changes() { + let mut config = Config::default(); + config.cold_store = Some(config.store.clone()); + validate_config(&config).unwrap(); + } + + #[test] + #[should_panic( + expected = "\\nconfig.json semantic issue: cold_store is configured, but save_trie_changes is Some(false). Trie changes should be saved to support cold storage." + )] + fn test_cold_store_with_save_trie_changes_false() { + let mut config = Config::default(); + config.cold_store = Some(config.store.clone()); + config.save_trie_changes = Some(false); + validate_config(&config).unwrap(); + } + + #[test] + #[should_panic( + expected = "\\nconfig.json semantic issue: 'config.tx_routing_height_horizon' needs to be at least 2, got 1." + )] + fn test_tx_routing_height_horizon_too_low() { + let mut config = Config::default(); + config.tx_routing_height_horizon = 1; + validate_config(&config).unwrap(); + } + + #[test] + #[should_panic( + expected = "\\nconfig.json semantic issue: 'config.tx_routing_height_horizon' can't be too high to avoid spamming the network. Keep it below 100. Got 1000000000." + )] + fn test_tx_routing_height_horizon_too_high() { + let mut config = Config::default(); + config.tx_routing_height_horizon = 1_000_000_000; + validate_config(&config).unwrap(); + } +} diff --git a/framework/src/download_file.rs b/framework/src/download_file.rs new file mode 100644 index 000000000..edea51066 --- /dev/null +++ b/framework/src/download_file.rs @@ -0,0 +1,395 @@ +use hyper::body::HttpBody; +use indicatif::{ProgressBar, ProgressStyle}; +use std::path::{Path, PathBuf}; +use tokio::io::AsyncWriteExt; + +#[derive(thiserror::Error, Debug)] +pub enum FileDownloadError { + #[error("{0}")] + HttpError(hyper::Error), + #[error("Failed to open temporary file")] + OpenError(#[source] std::io::Error), + #[error("Failed to write to temporary file at {0:?}")] + WriteError(PathBuf, #[source] std::io::Error), + #[error("Failed to decompress XZ stream: {0}")] + XzDecodeError(#[from] xz2::stream::Error), + #[error("Failed to decompress XZ stream: internal error: unexpected status {0:?}")] + XzStatusError(String), + #[error("Failed to rename temporary file {0:?} to {1:?}")] + RenameError(PathBuf, PathBuf, #[source] std::io::Error), + #[error("Invalid URI")] + UriError(#[from] hyper::http::uri::InvalidUri), + #[error("Failed to remove temporary file: {0}. Download previously failed")] + RemoveTemporaryFileError(std::io::Error, #[source] Box), +} + +pub(crate) fn run_download_file(url: &str, path: &Path) -> Result<(), FileDownloadError> { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(async { download_file(url, path).await }) +} + +/// Downloads resource at given `uri` and saves it to `file`. On failure, +/// `file` may be left in inconsistent state (i.e. may contain partial data). +/// +/// If the downloaded file is an XZ stream (i.e. starts with the XZ 6-byte magic +/// number), transparently decompresses the file as it’s being downloaded. +async fn download_file_impl( + uri: hyper::Uri, + path: &std::path::Path, + file: tokio::fs::File, +) -> Result<(), FileDownloadError> { + let mut out = AutoXzDecoder::new(path, file); + let https_connector = hyper_tls::HttpsConnector::new(); + let client = hyper::Client::builder().build::<_, hyper::Body>(https_connector); + let mut resp = client.get(uri).await.map_err(FileDownloadError::HttpError)?; + let bar = if let Some(file_size) = resp.size_hint().upper() { + let bar = ProgressBar::new(file_size); + bar.set_style( + ProgressStyle::default_bar().template( + "{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} [{bytes_per_sec}] ({eta})" + ).progress_chars("#>-") + ); + bar + } else { + let bar = ProgressBar::new_spinner(); + bar.set_style( + ProgressStyle::default_bar() + .template("{spinner:.green} [{elapsed_precise}] {bytes} [{bytes_per_sec}]"), + ); + bar + }; + + #[cfg(test)] + let bar = { + drop(bar); + ProgressBar::hidden() + }; + + while let Some(next_chunk_result) = resp.data().await { + let next_chunk = next_chunk_result.map_err(FileDownloadError::HttpError)?; + out.write_all(next_chunk.as_ref()).await?; + bar.inc(next_chunk.len() as u64); + } + out.finish().await?; + bar.finish(); + Ok(()) +} + +/// Downloads a resource at given `url` and saves it to `path`. On success, if +/// file at `path` exists it will be overwritten. On failure, file at `path` is +/// left unchanged (if it exists). +async fn download_file(url: &str, path: &Path) -> Result<(), FileDownloadError> { + let uri = url.parse()?; + + let (tmp_file, tmp_path) = { + let tmp_dir = path.parent().unwrap_or(Path::new(".")); + tempfile::NamedTempFile::new_in(tmp_dir).map_err(FileDownloadError::OpenError)?.into_parts() + }; + + let result = match download_file_impl(uri, &tmp_path, tokio::fs::File::from_std(tmp_file)).await + { + Err(err) => Err((tmp_path, err)), + Ok(()) => tmp_path.persist(path).map_err(|e| { + let from = e.path.to_path_buf(); + let to = path.to_path_buf(); + (e.path, FileDownloadError::RenameError(from, to, e.error)) + }), + }; + + result.map_err(|(tmp_path, err)| match tmp_path.close() { + Ok(()) => err, + Err(close_err) => FileDownloadError::RemoveTemporaryFileError(close_err, Box::new(err)), + }) +} + +/// Object which allows transparent XZ decoding when saving data to a file. +/// It automatically detects whether the data being read is compressed by +/// looking at the magic at the beginning of the file. +struct AutoXzDecoder<'a> { + path: &'a std::path::Path, + file: tokio::fs::File, + state: AutoXzState, +} + +/// State in which of the AutoXzDecoder +enum AutoXzState { + /// Given number of bytes have been read so far and all of them match bytes + /// in [`XZ_HEADER_MAGIC`]. The object starts in `Probing(0)` state and the + /// number never reaches the length of the [`XZ_HEADER_MAGIC`] buffer. + Probing(usize), + + /// The header did not match XZ stream header and thus the data is passed + /// through. + PlainText, + + /// The header did match XZ stream header and thus the data is being + /// decompressed. + Compressed(xz2::stream::Stream, Box<[u8]>), +} + +/// Header that every XZ streams starts with. See +/// § 2.1.1.1. +static XZ_HEADER_MAGIC: [u8; 6] = [0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00]; + +impl<'a> AutoXzDecoder<'a> { + fn new(path: &'a std::path::Path, file: tokio::fs::File) -> Self { + Self { path, file, state: AutoXzState::Probing(0) } + } + + /// Writes data from the chunk to the output file automatically + /// decompressing it if the stream is XZ-compressed. Note that once all the + /// data has been written [`Self::finish`] function must be called to flush + /// internal buffers. + async fn write_all(&mut self, chunk: &[u8]) -> Result<(), FileDownloadError> { + if let Some(len) = self.probe(chunk) { + if len != 0 { + self.write_all_impl(&XZ_HEADER_MAGIC[..len]).await?; + } + self.write_all_impl(chunk).await?; + } + Ok(()) + } + + /// Flushes all internal buffers and closes the output file. + async fn finish(mut self) -> Result<(), FileDownloadError> { + match self.state { + AutoXzState::Probing(pos) => self.write_all_raw(&XZ_HEADER_MAGIC[..pos]).await?, + AutoXzState::PlainText => (), + AutoXzState::Compressed(ref mut stream, ref mut buffer) => { + Self::decompress(self.path, &mut self.file, stream, buffer, b"").await? + } + } + self.file + .flush() + .await + .map_err(|e| FileDownloadError::WriteError(self.path.to_path_buf(), e)) + } + + /// If object is still in `Probing` state, read more data from the input to + /// determine whether it’s XZ stream or not. Updates `state` accordingly. + /// If probing succeeded, returns number of bytes from XZ header magic that + /// need to be processed before `chunk` is processed. If the entire data + /// from `chunk` has been processed and it should be discarded by the + /// caller, returns `None`. + fn probe(&mut self, chunk: &[u8]) -> Option { + if chunk.is_empty() { + None + } else if let AutoXzState::Probing(pos) = self.state { + let len = std::cmp::min(XZ_HEADER_MAGIC.len() - pos, chunk.len()); + if XZ_HEADER_MAGIC[pos..(pos + len)] != chunk[..len] { + self.state = AutoXzState::PlainText; + Some(pos) + } else if pos + len == XZ_HEADER_MAGIC.len() { + let stream = xz2::stream::Stream::new_stream_decoder(u64::max_value(), 0).unwrap(); + // TODO(mina86): Once ‘new_uninit’ feature gets stabilised + // replaced buffer initialisation by: + // let buffer = Box::new_uninit_slice(64 << 10); + // let buffer = unsafe { buffer.assume_init() }; + let buffer = vec![0u8; 64 << 10].into_boxed_slice(); + self.state = AutoXzState::Compressed(stream, buffer); + Some(pos) + } else { + self.state = AutoXzState::Probing(pos + len); + None + } + } else { + Some(0) + } + } + + /// Writes data to the output file. Panics if the object is still in + /// probing stage. + async fn write_all_impl(&mut self, chunk: &[u8]) -> Result<(), FileDownloadError> { + match self.state { + AutoXzState::Probing(_) => unreachable!(), + AutoXzState::PlainText => self.write_all_raw(chunk).await, + AutoXzState::Compressed(ref mut stream, ref mut buffer) => { + Self::decompress(self.path, &mut self.file, stream, buffer, chunk).await + } + } + } + + /// Writes data to output file directly. + async fn write_all_raw(&mut self, chunk: &[u8]) -> Result<(), FileDownloadError> { + self.file + .write_all(chunk) + .await + .map_err(|e| FileDownloadError::WriteError(self.path.to_path_buf(), e)) + } + + /// Internal implementation for [`Self::write_all`] and [`Self::finish`] + /// methods used when performing decompression. Calling it with an empty + /// `chunk` indicates the end of the compressed data. + async fn decompress( + path: &std::path::Path, + file: &mut tokio::fs::File, + stream: &mut xz2::stream::Stream, + buffer: &mut [u8], + mut chunk: &[u8], + ) -> Result<(), FileDownloadError> { + let action = + if chunk.is_empty() { xz2::stream::Action::Finish } else { xz2::stream::Action::Run }; + loop { + let total_in = stream.total_in(); + let total_out = stream.total_out(); + let status = stream.process(chunk, buffer, action)?; + match status { + xz2::stream::Status::Ok => (), + xz2::stream::Status::StreamEnd => (), + status => { + let status = format!("{:?}", status); + tracing::error!(target: "near", "Got unexpected status ‘{}’ when decompressing downloaded file.", status); + return Err(FileDownloadError::XzStatusError(status)); + } + }; + let read = (stream.total_in() - total_in).try_into().unwrap(); + chunk = &chunk[read..]; + let out = (stream.total_out() - total_out).try_into().unwrap(); + file.write_all(&buffer[..out]) + .await + .map_err(|e| FileDownloadError::WriteError(path.to_path_buf(), e))?; + if chunk.is_empty() { + break Ok(()); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use hyper::service::{make_service_fn, service_fn}; + use hyper::{Body, Request, Response, Server}; + use std::convert::Infallible; + use std::sync::Arc; + + async fn check_file_download(payload: &[u8], expected: Result<&[u8], &str>) { + let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + let payload = Arc::new(payload.to_vec()); + tokio::task::spawn(async move { + let make_svc = make_service_fn(move |_conn| { + let payload = Arc::clone(&payload); + let handle_request = move |_: Request| { + let payload = Arc::clone(&payload); + async move { Ok::<_, Infallible>(Response::new(Body::from(payload.to_vec()))) } + }; + async move { Ok::<_, Infallible>(service_fn(handle_request)) } + }); + let server = Server::from_tcp(listener).unwrap().serve(make_svc); + if let Err(e) = server.await { + eprintln!("server error: {}", e); + } + }); + + let tmp_file = tempfile::NamedTempFile::new().unwrap(); + + let res = download_file(&format!("http://localhost:{}", port), tmp_file.path()) + .await + .map(|()| std::fs::read(tmp_file.path()).unwrap()); + + match (res, expected) { + (Ok(res), Ok(expected)) => assert_eq!(&res, expected), + (Ok(_), Err(_)) => panic!("expected an error"), + (Err(res), Ok(_)) => panic!("unexpected error: {res}"), + (Err(res), Err(expected)) => assert_eq!(res.to_string(), expected), + } + } + + #[tokio::test] + async fn test_file_download_plaintext() { + let data = &[42; 1024]; + check_file_download(data, Ok(data)).await; + + let data = b"A quick brown fox jumps over a lazy dog"; + check_file_download(data, Ok(data)).await; + + let payload = b"\xfd\x37\x7a\x58\x5a\x00\x00\x04\xe6\xd6\xb4\x46\ + \x02\x00\x21\x01\x1c\x00\x00\x00\x10\xcf\x58\xcc\ + \x01\x00\x19\x5a\x61\xc5\xbc\xc3\xb3\xc5\x82\xc4\ + \x87\x20\x67\xc4\x99\xc5\x9b\x6c\xc4\x85\x20\x6a\ + \x61\xc5\xba\xc5\x84\x00\x00\x00\x89\x4e\xdf\x72\ + \x66\xbe\xa9\x51\x00\x01\x32\x1a\x20\x18\x94\x30\ + \x1f\xb6\xf3\x7d\x01\x00\x00\x00\x00\x04\x59\x5a"; + check_file_download(payload, Ok("Zażółć gęślą jaźń".as_bytes())).await; + + let payload = b"\xfd\x37\x7a\x58\x5a\x00A quick brown fox"; + check_file_download(payload, Err("Failed to decompress XZ stream: lzma data error")).await; + } + + fn auto_xz_test_write_file( + buffer: &[u8], + chunk_size: usize, + ) -> Result, FileDownloadError> { + let (file, path) = tempfile::NamedTempFile::new().unwrap().into_parts(); + let mut out = AutoXzDecoder::new(&path, tokio::fs::File::from_std(file)); + tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on( + async move { + for chunk in buffer.chunks(chunk_size) { + out.write_all(chunk).await?; + } + out.finish().await + }, + )?; + Ok(std::fs::read(path).unwrap()) + } + + /// Tests writing plain text of varying lengths through [`AutoXzDecoder`]. + /// Includes test cases where prefix of a XZ header is present at the beginning + /// of the stream being written. That tests the object not being fooled by + /// partial prefix. + #[test] + fn test_auto_xz_decode_plain() { + let mut data: [u8; 39] = *b"A quick brown fox jumps over a lazy dog"; + // On first iteration we’re testing just a plain text data. On subsequent + // iterations, we’re testing uncompressed data whose first few bytes match + // the XZ header. + for (pos, &ch) in XZ_HEADER_MAGIC.iter().enumerate() { + for len in [0, 1, 2, 3, 4, 5, 6, 10, 20, data.len()] { + let buffer = &data[0..len]; + for chunk_size in 1..11 { + let got = auto_xz_test_write_file(&buffer, chunk_size).unwrap(); + assert_eq!(got, buffer, "pos={}, len={}, chunk_size={}", pos, len, chunk_size); + } + } + data[pos] = ch; + } + } + + /// Tests writing XZ stream through [`AutoXzDecoder`]. The stream should be + /// properly decompressed. + #[test] + fn test_auto_xz_decode_compressed() { + let buffer = b"\xfd\x37\x7a\x58\x5a\x00\x00\x04\xe6\xd6\xb4\x46\ + \x02\x00\x21\x01\x1c\x00\x00\x00\x10\xcf\x58\xcc\ + \x01\x00\x19\x5a\x61\xc5\xbc\xc3\xb3\xc5\x82\xc4\ + \x87\x20\x67\xc4\x99\xc5\x9b\x6c\xc4\x85\x20\x6a\ + \x61\xc5\xba\xc5\x84\x00\x00\x00\x89\x4e\xdf\x72\ + \x66\xbe\xa9\x51\x00\x01\x32\x1a\x20\x18\x94\x30\ + \x1f\xb6\xf3\x7d\x01\x00\x00\x00\x00\x04\x59\x5a"; + for chunk_size in 1..11 { + let got = auto_xz_test_write_file(buffer, chunk_size).unwrap(); + assert_eq!(got, "Zażółć gęślą jaźń".as_bytes()); + } + } + + /// Tests [`AutoXzDecoder`]’s handling of corrupt XZ streams. The data being + /// processed starts with a proper XZ header but what follows is an invalid XZ + /// data. This should result in [`FileDownloadError::XzDecodeError`]. + #[test] + fn test_auto_xz_decode_corrupted() { + let buffer = b"\xfd\x37\x7a\x58\x5a\x00A quick brown fox"; + for chunk_size in 1..11 { + let got = auto_xz_test_write_file(buffer, chunk_size); + assert!( + matches!(got, Err(FileDownloadError::XzDecodeError(xz2::stream::Error::Data))), + "got {:?}", + got + ); + } + } +} diff --git a/framework/src/dyn_config.rs b/framework/src/dyn_config.rs new file mode 100644 index 000000000..6123ab8ff --- /dev/null +++ b/framework/src/dyn_config.rs @@ -0,0 +1,91 @@ +use crate::config::Config; +use unc_chain_configs::UpdateableClientConfig; +use unc_dyn_configs::{UpdateableConfigLoaderError, UpdateableConfigs}; +use unc_o11y::log_config::LogConfig; +use serde::Deserialize; +use std::path::{Path, PathBuf}; + +pub const LOG_CONFIG_FILENAME: &str = "log_config.json"; + +/// This function gets called at the startup and each time a config needs to be reloaded. +pub fn read_updateable_configs( + home_dir: &Path, +) -> Result { + let mut errs = vec![]; + let log_config = match read_log_config(home_dir) { + Ok(config) => config, + Err(err) => { + errs.push(err); + None + } + }; + let updateable_client_config = + match Config::from_file(&home_dir.join(crate::config::CONFIG_FILENAME)) + .map(get_updateable_client_config) + { + Ok(config) => Some(config), + Err(err) => { + errs.push(UpdateableConfigLoaderError::ConfigFileError { + file: PathBuf::from(crate::config::CONFIG_FILENAME), + err: err.into(), + }); + None + } + }; + if errs.is_empty() { + crate::metrics::CONFIG_CORRECT.set(1); + Ok(UpdateableConfigs { log_config, client_config: updateable_client_config }) + } else { + tracing::warn!(target: "uncd", "Dynamically updateable configs are not valid. Please fix this ASAP otherwise the node will be unable to restart: {:?}", &errs); + crate::metrics::CONFIG_CORRECT.set(0); + Err(UpdateableConfigLoaderError::Errors(errs)) + } +} + +pub fn get_updateable_client_config(config: Config) -> UpdateableClientConfig { + // All fields that can be updated while the node is running should be explicitly set here. + // Keep this list in-sync with `core/dyn-configs/README.md`. + UpdateableClientConfig { + expected_shutdown: config.expected_shutdown, + resharding_config: config.resharding_config, + produce_chunk_add_transactions_time_limit: config.produce_chunk_add_transactions_time_limit, + } +} + +fn read_log_config(home_dir: &Path) -> Result, UpdateableConfigLoaderError> { + read_json_config::(&home_dir.join(LOG_CONFIG_FILENAME)) +} + +// the file can be JSON with comments +fn read_json_config( + path: &Path, +) -> Result, UpdateableConfigLoaderError> +where + for<'a> T: Deserialize<'a>, +{ + match std::fs::read_to_string(path) { + Ok(config_str) => match unc_config_utils::strip_comments_from_json_str(&config_str) { + Ok(config_str_without_comments) => { + match serde_json::from_str::(&config_str_without_comments) { + Ok(config) => { + tracing::info!(target: "uncd", config=?config, "Changing the config {path:?}."); + Ok(Some(config)) + } + Err(err) => { + Err(UpdateableConfigLoaderError::Parse { file: path.to_path_buf(), err }) + } + } + } + Err(err) => { + Err(UpdateableConfigLoaderError::OpenAndRead { file: path.to_path_buf(), err }) + } + }, + Err(err) => match err.kind() { + std::io::ErrorKind::NotFound => { + tracing::info!(target: "uncd", ?err, "Reset the config {path:?} because the config file doesn't exist."); + Ok(None) + } + _ => Err(UpdateableConfigLoaderError::OpenAndRead { file: path.to_path_buf(), err }), + }, + } +} diff --git a/framework/src/entity_debug.rs b/framework/src/entity_debug.rs new file mode 100644 index 000000000..a9928cddf --- /dev/null +++ b/framework/src/entity_debug.rs @@ -0,0 +1,411 @@ +use crate::entity_debug_serializer::serialize_entity; +use anyhow::anyhow; + +use unc_chain::types::RuntimeAdapter; +use unc_chain::{Block, BlockHeader}; +use unc_epoch_manager::EpochManagerAdapter; +use unc_jsonrpc_primitives::errors::RpcError; +use unc_jsonrpc_primitives::types::entity_debug::{ + EntityDataEntry, EntityDataStruct, EntityDataValue, EntityDebugHandler, EntityQuery, +}; +use unc_primitives::block::Tip; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::Receipt; +use unc_primitives::sharding::ShardChunk; +use unc_primitives::state::FlatStateValue; +use unc_primitives::transaction::{ExecutionOutcomeWithProof, SignedTransaction}; +use unc_primitives::utils::get_outcome_id_block_hash; +use unc_primitives::views::{ + BlockHeaderView, BlockView, ChunkView, ExecutionOutcomeView, ReceiptView, SignedTransactionView, +}; +use unc_store::flat::delta::KeyForFlatStateDelta; +use unc_store::flat::store_helper::encode_flat_state_db_key; +use unc_store::flat::{FlatStateChanges, FlatStateDeltaMetadata, FlatStorageStatus}; +use unc_store::{ + DBCol, NibbleSlice, ShardUId, Store, TrieCachingStorage, FINAL_HEAD_KEY, HEADER_HEAD_KEY, + HEAD_KEY, +}; +use serde::Serialize; +use std::sync::Arc; + +pub struct EntityDebugHandlerImpl { + pub epoch_manager: Arc, + pub runtime: Arc, + pub store: Store, +} + +impl EntityDebugHandlerImpl { + fn query_impl(&self, query: EntityQuery) -> anyhow::Result { + match query { + EntityQuery::AllShardsByEpochId { epoch_id } => { + let shard_layout = self.epoch_manager.get_shard_layout(&epoch_id)?; + Ok(serialize_entity(&shard_layout.shard_uids().collect::>())) + } + EntityQuery::BlockByHash { block_hash } => { + let block = self + .store + .get_ser::(DBCol::Block, &borsh::to_vec(&block_hash).unwrap())? + .ok_or_else(|| anyhow!("Block not found"))?; + let author = self + .epoch_manager + .get_block_producer(block.header().epoch_id(), block.header().height())?; + Ok(serialize_entity(&BlockView::from_author_block(author, block))) + } + EntityQuery::BlockHashByHeight { block_height } => { + let block_hash = self + .store + .get_ser::( + DBCol::BlockHeight, + &borsh::to_vec(&block_height).unwrap(), + )? + .ok_or_else(|| anyhow!("Block height not found"))?; + Ok(serialize_entity(&block_hash)) + } + EntityQuery::BlockHeaderByHash { block_hash } => { + let block_header = self + .store + .get_ser::( + DBCol::BlockHeader, + &borsh::to_vec(&block_hash).unwrap(), + )? + .ok_or_else(|| anyhow!("Block header not found"))?; + Ok(serialize_entity(&BlockHeaderView::from(block_header))) + } + EntityQuery::ChunkByHash { chunk_hash } => { + let chunk = self + .store + .get_ser::(DBCol::Chunks, &borsh::to_vec(&chunk_hash).unwrap())? + .ok_or_else(|| anyhow!("Chunk not found"))?; + let epoch_id = + self.epoch_manager.get_epoch_id_from_prev_block(chunk.prev_block())?; + let author = self.epoch_manager.get_chunk_producer( + &epoch_id, + chunk.height_created(), + chunk.shard_id(), + )?; + Ok(serialize_entity(&ChunkView::from_author_chunk(author, chunk))) + } + EntityQuery::EpochInfoByEpochId { epoch_id } => { + let epoch_info = self.epoch_manager.get_epoch_info(&epoch_id)?; + Ok(serialize_entity(&*epoch_info)) + } + EntityQuery::FlatStateByTrieKey { trie_key, shard_uid } => { + let state = self + .store + .get_ser::( + DBCol::FlatState, + &encode_flat_state_db_key(shard_uid, &hex::decode(&trie_key)?), + )? + .ok_or_else(|| anyhow!("Flat state not found"))?; + let data = self.deref_flat_state_value(state, shard_uid)?; + Ok(serialize_entity(&hex::encode(&data))) + } + EntityQuery::FlatStateChangesByBlockHash { block_hash, shard_uid } => { + let changes = self + .store + .get_ser::( + DBCol::FlatStateChanges, + &borsh::to_vec(&KeyForFlatStateDelta { block_hash, shard_uid }).unwrap(), + )? + .ok_or_else(|| anyhow!("Flat state changes not found"))?; + let mut changes_view = Vec::new(); + for (key, value) in changes.0.into_iter() { + let key = hex::encode(&key); + let value = match value { + Some(v) => Some(hex::encode(&self.deref_flat_state_value(v, shard_uid)?)), + None => None, + }; + changes_view.push(FlatStateChangeView { key, value }); + } + Ok(serialize_entity(&changes_view)) + } + EntityQuery::FlatStateDeltaMetadataByBlockHash { block_hash, shard_uid } => { + let metadata = self + .store + .get_ser::( + DBCol::FlatStateDeltaMetadata, + &borsh::to_vec(&KeyForFlatStateDelta { block_hash, shard_uid }).unwrap(), + )? + .ok_or_else(|| anyhow!("Flat state delta metadata not found"))?; + Ok(serialize_entity(&metadata)) + } + EntityQuery::FlatStorageStatusByShardUId { shard_uid } => { + let status = self + .store + .get_ser::( + DBCol::FlatStorageStatus, + &borsh::to_vec(&shard_uid).unwrap(), + )? + .ok_or_else(|| anyhow!("Flat storage status not found"))?; + Ok(serialize_entity(&status)) + } + EntityQuery::OutcomeByTransactionHash { transaction_hash: outcome_id } + | EntityQuery::OutcomeByReceiptId { receipt_id: outcome_id } => { + let (_, outcome) = self + .store + .iter_prefix_ser::( + DBCol::TransactionResultForBlock, + &borsh::to_vec(&outcome_id).unwrap(), + ) + .next() + .ok_or_else(|| anyhow!("Outcome not found"))??; + Ok(serialize_entity(&ExecutionOutcomeView::from(outcome.outcome))) + } + EntityQuery::OutcomeByTransactionHashAndBlockHash { + transaction_hash: outcome_id, + block_hash, + } + | EntityQuery::OutcomeByReceiptIdAndBlockHash { receipt_id: outcome_id, block_hash } => { + let outcome = self + .store + .get_ser::( + DBCol::TransactionResultForBlock, + &get_outcome_id_block_hash(&outcome_id, &block_hash), + )? + .ok_or_else(|| anyhow!("Outcome not found"))?; + Ok(serialize_entity(&ExecutionOutcomeView::from(outcome.outcome))) + } + EntityQuery::ReceiptById { receipt_id } => { + let receipt = self + .store + .get_ser::(DBCol::Receipts, &borsh::to_vec(&receipt_id).unwrap())? + .ok_or_else(|| anyhow!("Receipt not found"))?; + Ok(serialize_entity(&ReceiptView::from(receipt))) + } + EntityQuery::ShardIdByAccountId { account_id, epoch_id } => { + let shard_id = + self.epoch_manager.account_id_to_shard_id(&account_id.parse()?, &epoch_id)?; + Ok(serialize_entity(&shard_id)) + } + EntityQuery::ShardLayoutByEpochId { epoch_id } => { + let shard_layout = self.epoch_manager.get_shard_layout(&epoch_id)?; + Ok(serialize_entity(&shard_layout)) + } + EntityQuery::ShardUIdByShardId { shard_id, epoch_id } => { + let shard_layout = self.epoch_manager.get_shard_layout(&epoch_id)?; + let shard_uid = shard_layout + .shard_uids() + .nth(shard_id as usize) + .ok_or_else(|| anyhow!("Shard {} not found", shard_id))?; + Ok(serialize_entity(&shard_uid)) + } + EntityQuery::TipAtFinalHead(_) => { + let tip = self + .store + .get_ser::(DBCol::BlockMisc, FINAL_HEAD_KEY)? + .ok_or_else(|| anyhow!("Tip not found"))?; + Ok(serialize_entity(&tip)) + } + EntityQuery::TipAtHead(_) => { + let tip = self + .store + .get_ser::(DBCol::BlockMisc, HEAD_KEY)? + .ok_or_else(|| anyhow!("Tip not found"))?; + Ok(serialize_entity(&tip)) + } + EntityQuery::TipAtHeaderHead(_) => { + let tip = self + .store + .get_ser::(DBCol::BlockMisc, HEADER_HEAD_KEY)? + .ok_or_else(|| anyhow!("Tip not found"))?; + Ok(serialize_entity(&tip)) + } + EntityQuery::TransactionByHash { transaction_hash } => { + let transaction = self + .store + .get_ser::( + DBCol::Transactions, + &borsh::to_vec(&transaction_hash).unwrap(), + )? + .ok_or_else(|| anyhow!("Transaction not found"))?; + Ok(serialize_entity(&SignedTransactionView::from(transaction))) + } + EntityQuery::TrieNode { trie_path } => { + let trie_path = + TriePath::parse(trie_path).ok_or_else(|| anyhow!("Invalid path"))?; + let trie = self + .runtime + .get_tries() + .get_trie_for_shard(trie_path.shard_uid, trie_path.state_root); + let node = trie + .debug_get_node(&trie_path.path)? + .ok_or_else(|| anyhow!("Node not found"))?; + let mut entity_data = EntityDataStruct::new(); + entity_data.entries.push(EntityDataEntry { + name: "path".to_owned(), + value: EntityDataValue::String(TriePath::nibbles_to_hex(&trie_path.path)), + }); + match node { + unc_store::RawTrieNode::Leaf(extension, value) => { + let extension_nibbles = NibbleSlice::from_encoded(&extension); + let leaf_nibbles = trie_path + .path + .iter() + .copied() + .chain(extension_nibbles.0.iter()) + .collect::>(); + let data = trie.retrieve_value(&value.hash)?; + entity_data.entries.push(EntityDataEntry { + name: "leaf_path".to_owned(), + value: EntityDataValue::String(TriePath::nibbles_to_hex(&leaf_nibbles)), + }); + entity_data.entries.push(EntityDataEntry { + name: "value".to_owned(), + value: EntityDataValue::String(hex::encode(&data)), + }); + } + unc_store::RawTrieNode::BranchNoValue(children) => { + for index in 0..16 { + if let Some(_) = children[index] { + let path = TriePath { + shard_uid: trie_path.shard_uid, + state_root: trie_path.state_root, + path: [&trie_path.path[..], &[index]].concat(), + }; + entity_data.entries.push(EntityDataEntry { + name: format!("{:x}", index), + value: EntityDataValue::String(path.to_string()), + }); + } + } + } + unc_store::RawTrieNode::BranchWithValue(value, children) => { + let data = trie.retrieve_value(&value.hash)?; + entity_data.entries.push(EntityDataEntry { + name: "leaf_path".to_owned(), + value: EntityDataValue::String(TriePath::nibbles_to_hex( + &trie_path.path, + )), + }); + entity_data.entries.push(EntityDataEntry { + name: "value".to_owned(), + value: EntityDataValue::String(hex::encode(&data)), + }); + for index in 0..16 { + if let Some(_) = children[index] { + let path = TriePath { + shard_uid: trie_path.shard_uid, + state_root: trie_path.state_root, + path: [&trie_path.path[..], &[index]].concat(), + }; + entity_data.entries.push(EntityDataEntry { + name: format!("{:x}", index), + value: EntityDataValue::String(path.to_string()), + }); + } + } + } + unc_store::RawTrieNode::Extension(extension, _) => { + let extension_nibbles = NibbleSlice::from_encoded(&extension); + let child_nibbles = trie_path + .path + .iter() + .copied() + .chain(extension_nibbles.0.iter()) + .collect::>(); + let child_path = TriePath { + shard_uid: trie_path.shard_uid, + state_root: trie_path.state_root, + path: child_nibbles, + }; + entity_data.entries.push(EntityDataEntry { + name: "extension".to_owned(), + value: EntityDataValue::String(child_path.to_string()), + }); + } + } + Ok(EntityDataValue::Struct(Box::new(entity_data))) + } + EntityQuery::TrieRootByChunkHash { chunk_hash } => { + let chunk = self + .store + .get_ser::(DBCol::Chunks, &borsh::to_vec(&chunk_hash).unwrap())? + .ok_or_else(|| anyhow!("Chunk not found"))?; + let shard_layout = self + .epoch_manager + .get_shard_layout_from_prev_block(&chunk.cloned_header().prev_block_hash())?; + let shard_uid = shard_layout + .shard_uids() + .nth(chunk.shard_id() as usize) + .ok_or_else(|| anyhow!("Shard {} not found", chunk.shard_id()))?; + let path = + TriePath { path: vec![], shard_uid, state_root: chunk.prev_state_root() }; + Ok(serialize_entity(&path.to_string())) + } + EntityQuery::TrieRootByStateRoot { state_root, shard_uid } => { + let path = TriePath { path: vec![], shard_uid, state_root }; + Ok(serialize_entity(&path.to_string())) + } + } + } + + fn deref_flat_state_value( + &self, + state: FlatStateValue, + shard_uid: ShardUId, + ) -> anyhow::Result> { + Ok(match state { + FlatStateValue::Ref(value) => self + .store + .get( + DBCol::State, + &TrieCachingStorage::get_key_from_shard_uid_and_hash(shard_uid, &value.hash), + )? + .ok_or_else(|| anyhow!("ValueRef could not be dereferenced"))? + .to_vec(), + FlatStateValue::Inlined(data) => data, + }) + } +} + +impl EntityDebugHandler for EntityDebugHandlerImpl { + fn query(&self, query: EntityQuery) -> Result { + self.query_impl(query) + .map_err(|err| RpcError::new_internal_error(None, format!("{:?}", err))) + } +} + +/// A helper to represent the complete location of a trie node with a string. +/// The format is "shard_uid/state_root/path", where path is a hex-encoded +/// string of nibbles (which may be of odd length, because each nibble is 4 bits +/// which is a single hex character). +pub struct TriePath { + pub shard_uid: ShardUId, + pub state_root: CryptoHash, + pub path: Vec, +} + +impl TriePath { + pub fn to_string(&self) -> String { + format!("{}/{}/{}", self.shard_uid, self.state_root, Self::nibbles_to_hex(&self.path)) + } + + pub fn parse(encoded: String) -> Option { + let mut parts = encoded.split("/"); + let shard_uid = parts.next()?.parse().ok()?; + let state_root = parts.next()?.parse().ok()?; + let nibbles = parts.next()?; + let path = Self::nibbles_from_hex(nibbles)?; + Some(TriePath { shard_uid, state_root, path }) + } + + /// Format of nibbles is an array of 4-bit integers. + pub fn nibbles_to_hex(nibbles: &[u8]) -> String { + nibbles.iter().map(|x| format!("{:x}", x)).collect::>().join("") + } + + /// Format of returned value is an array of 4-bit integers, or None if parsing failed. + pub fn nibbles_from_hex(hex: &str) -> Option> { + let mut result = Vec::new(); + for nibble in hex.chars() { + result.push(u8::from_str_radix(&nibble.to_string(), 16).ok()?); + } + Some(result) + } +} + +#[derive(Serialize)] +struct FlatStateChangeView { + pub key: String, + pub value: Option, +} diff --git a/framework/src/entity_debug_serializer.rs b/framework/src/entity_debug_serializer.rs new file mode 100644 index 000000000..5dcf63712 --- /dev/null +++ b/framework/src/entity_debug_serializer.rs @@ -0,0 +1,606 @@ +/// This file implements a completely new serialization format which +/// transforms anything that implements serde::Serialize into an +/// EntityDataValue (i.e. a generic tree of string key-value pairs). +/// +/// This is used for the Entity Debug UI. The reason why we don't use +/// JSON serialization is because we would have to maintain an equivalent +/// schema on the UI side to be able to effectively render it, which is not +/// only a lot of work but also will get easily out of sync with any Rust +/// changes. (Frameworks to generate TypeScript typings from Rust structs +/// do exist, but they all have serious limitations at the moment.) Also, +/// it is discouraging if in order to support a new query one has to also +/// write TypeScript type definitions. +use unc_jsonrpc_primitives::types::entity_debug::{ + EntityDataEntry, EntityDataStruct, EntityDataValue, +}; +use serde::{ + ser::{ + SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, + SerializeTupleStruct, SerializeTupleVariant, + }, + Serialize, Serializer, +}; + +/// Root data for serialization output. +struct EntitySerializerData { + pub output: EntityDataStruct, +} + +/// Serializer for values. +struct EntitySerializer<'a> { + // Upon self.commit(value), the value will be written to the key at this + // parent. + parent: &'a mut EntityDataStruct, + key: String, +} + +/// Serializer for structs, maps, tuples, arrays, etc. +struct EntitySerializerStruct<'a> { + // Upon self.commit(), the children will be wrapped as an EntityDataValue + // and then committed to the parent. + parent: EntitySerializer<'a>, + children: EntityDataStruct, + // The next index, for arrays. + index: usize, + // If present, when committing, the value will be additionally wrapped in + // another struct with a single entry with this key. + outer_key: Option, +} + +impl EntitySerializerData { + pub fn new() -> EntitySerializerData { + EntitySerializerData { output: EntityDataStruct::new() } + } + + pub fn serializer<'a>(&'a mut self, key: String) -> EntitySerializer<'a> { + EntitySerializer { parent: &mut self.output, key } + } +} + +impl<'a> EntitySerializer<'a> { + /// Creates a child struct serializer that when committed will commit a + /// struct value to self. + pub fn child_struct(self, outer_key: Option) -> EntitySerializerStruct<'a> { + EntitySerializerStruct { + parent: self, + children: EntityDataStruct::new(), + index: 0, + outer_key, + } + } + + pub fn commit(self, value: EntityDataValue) { + self.parent.entries.push(EntityDataEntry { name: self.key, value }); + } +} + +impl<'a> EntitySerializerStruct<'a> { + /// Prepares to serialize a child array element. + /// The element is added to children when the returned serializer commits. + pub fn push<'b>(&'b mut self) -> EntitySerializer<'b> + where + 'a: 'b, + { + let result = + EntitySerializer { parent: &mut self.children, key: format!("{}", self.index) }; + self.index += 1; + result + } + + /// Prepares to serialize a child struct field. + /// The element is added to children when the returned serializer commits. + pub fn child<'b>(&'b mut self, key: String) -> EntitySerializer<'b> + where + 'a: 'b, + { + EntitySerializer { parent: &mut self.children, key } + } + + pub fn commit(self) { + let value = EntityDataValue::Struct(self.children.into()); + match self.outer_key { + Some(outer_key) => { + self.parent.commit(EntityDataValue::Struct(Box::new(EntityDataStruct { + entries: vec![EntityDataEntry { name: outer_key, value }], + }))) + } + None => self.parent.commit(value), + } + } +} + +impl<'a> Serializer for EntitySerializer<'a> { + type Ok = (); + + type Error = std::fmt::Error; // can be anything, we don't error + + type SerializeSeq = EntitySerializerStruct<'a>; + type SerializeTuple = EntitySerializerStruct<'a>; + type SerializeTupleStruct = EntitySerializerStruct<'a>; + type SerializeTupleVariant = EntitySerializerStruct<'a>; + type SerializeMap = EntitySerializerStruct<'a>; + type SerializeStruct = EntitySerializerStruct<'a>; + type SerializeStructVariant = EntitySerializerStruct<'a>; + + fn serialize_bool(self, v: bool) -> Result { + self.commit(EntityDataValue::String(format!("{}", v))); + Ok(()) + } + + fn serialize_i8(self, v: i8) -> Result { + self.commit(EntityDataValue::String(format!("{}", v))); + Ok(()) + } + + fn serialize_i16(self, v: i16) -> Result { + self.commit(EntityDataValue::String(format!("{}", v))); + Ok(()) + } + + fn serialize_i32(self, v: i32) -> Result { + self.commit(EntityDataValue::String(format!("{}", v))); + Ok(()) + } + + fn serialize_i64(self, v: i64) -> Result { + self.commit(EntityDataValue::String(format!("{}", v))); + Ok(()) + } + + fn serialize_i128(self, v: i128) -> Result { + self.commit(EntityDataValue::String(format!("{}", v))); + Ok(()) + } + + fn serialize_u8(self, v: u8) -> Result { + self.commit(EntityDataValue::String(format!("{}", v))); + Ok(()) + } + + fn serialize_u16(self, v: u16) -> Result { + self.commit(EntityDataValue::String(format!("{}", v))); + Ok(()) + } + + fn serialize_u32(self, v: u32) -> Result { + self.commit(EntityDataValue::String(format!("{}", v))); + Ok(()) + } + + fn serialize_u64(self, v: u64) -> Result { + self.commit(EntityDataValue::String(format!("{}", v))); + Ok(()) + } + + fn serialize_u128(self, v: u128) -> Result { + self.commit(EntityDataValue::String(format!("{}", v))); + Ok(()) + } + + fn serialize_f32(self, v: f32) -> Result { + self.commit(EntityDataValue::String(format!("{}", v))); + Ok(()) + } + + fn serialize_f64(self, v: f64) -> Result { + self.commit(EntityDataValue::String(format!("{}", v))); + Ok(()) + } + + fn serialize_char(self, v: char) -> Result { + self.commit(EntityDataValue::String(format!("{}", v))); + Ok(()) + } + + fn serialize_str(self, v: &str) -> Result { + self.commit(EntityDataValue::String(format!("{}", v))); + Ok(()) + } + + fn serialize_bytes(self, v: &[u8]) -> Result { + self.commit(EntityDataValue::String(hex::encode(v))); + Ok(()) + } + + fn serialize_none(self) -> Result { + self.commit(EntityDataValue::String("null".to_owned())); + Ok(()) + } + + fn serialize_some(self, value: &T) -> Result + where + T: Serialize, + { + value.serialize(self).unwrap(); + Ok(()) + } + + fn serialize_unit(self) -> Result { + self.commit(EntityDataValue::String("()".to_owned())); + Ok(()) + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result { + self.serialize_unit() + } + + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + self.serialize_str(variant).unwrap(); + Ok(()) + } + + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result + where + T: Serialize, + { + value.serialize(self).unwrap(); + Ok(()) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result + where + T: Serialize, + { + let mut child = self.child_struct(None); + value.serialize(child.child(variant.to_owned())).unwrap(); + child.commit(); + Ok(()) + } + + fn serialize_seq(self, _len: Option) -> Result { + Ok(self.child_struct(None)) + } + + fn serialize_tuple(self, _len: usize) -> Result { + Ok(self.child_struct(None)) + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Ok(self.child_struct(None)) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + _len: usize, + ) -> Result { + Ok(self.child_struct(Some(variant.to_owned()))) + } + + fn serialize_map(self, _len: Option) -> Result { + Ok(self.child_struct(None)) + } + + fn serialize_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Ok(self.child_struct(None)) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + _len: usize, + ) -> Result { + Ok(self.child_struct(Some(variant.to_owned()))) + } +} + +impl<'a> SerializeSeq for EntitySerializerStruct<'a> { + type Ok = (); + type Error = std::fmt::Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize, + { + value.serialize(self.push()).unwrap(); + Ok(()) + } + + fn end(self) -> Result { + self.commit(); + Ok(()) + } +} + +impl<'a> SerializeMap for EntitySerializerStruct<'a> { + type Ok = (); + + type Error = std::fmt::Error; + + fn serialize_entry( + &mut self, + key: &K, + value: &V, + ) -> Result<(), Self::Error> + where + K: Serialize, + V: Serialize, + { + let mut key_data = EntitySerializerData::new(); + key.serialize(key_data.serializer("".to_owned())).unwrap(); + let key = match key_data.output.entries.into_iter().next().unwrap().value { + EntityDataValue::String(key_str) => key_str, + EntityDataValue::Struct(_) => "invalid_key_type".to_owned(), + }; + value.serialize(self.child(key)).unwrap(); + Ok(()) + } + + fn end(self) -> Result { + self.commit(); + Ok(()) + } + + fn serialize_key(&mut self, _key: &T) -> Result<(), Self::Error> + where + T: Serialize, + { + unreachable!() + } + + fn serialize_value(&mut self, _value: &T) -> Result<(), Self::Error> + where + T: Serialize, + { + unreachable!() + } +} + +impl<'a> SerializeStruct for EntitySerializerStruct<'a> { + type Ok = (); + + type Error = std::fmt::Error; + + fn serialize_field( + &mut self, + key: &'static str, + value: &T, + ) -> Result<(), Self::Error> + where + T: Serialize, + { + value.serialize(self.child(key.to_owned())).unwrap(); + Ok(()) + } + + fn end(self) -> Result { + self.commit(); + Ok(()) + } +} + +impl<'a> SerializeTuple for EntitySerializerStruct<'a> { + type Ok = (); + + type Error = std::fmt::Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize, + { + value.serialize(self.push()).unwrap(); + Ok(()) + } + + fn end(self) -> Result { + self.commit(); + Ok(()) + } +} + +impl<'a> SerializeTupleStruct for EntitySerializerStruct<'a> { + type Ok = (); + + type Error = std::fmt::Error; + + fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize, + { + value.serialize(self.push()).unwrap(); + Ok(()) + } + + fn end(self) -> Result { + self.commit(); + Ok(()) + } +} + +impl<'a> SerializeTupleVariant for EntitySerializerStruct<'a> { + type Ok = (); + + type Error = std::fmt::Error; + + fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize, + { + value.serialize(self.push()).unwrap(); + Ok(()) + } + + fn end(self) -> Result { + self.commit(); + Ok(()) + } +} + +impl<'a> SerializeStructVariant for EntitySerializerStruct<'a> { + type Ok = (); + + type Error = std::fmt::Error; + + fn serialize_field( + &mut self, + key: &'static str, + value: &T, + ) -> Result<(), Self::Error> + where + T: Serialize, + { + value.serialize(self.child(key.to_owned())).unwrap(); + Ok(()) + } + fn end(self) -> Result { + self.commit(); + Ok(()) + } +} + +/// Serializes anything that implements Serialize into an EntityDataValue +/// that can be easily displayed by the Entity Debug UI. +pub fn serialize_entity(value: &T) -> EntityDataValue +where + T: Serialize, +{ + let mut data = EntitySerializerData::new(); + value.serialize(data.serializer(String::new())).unwrap(); + data.output.entries.into_iter().next().unwrap().value +} + +#[cfg(test)] +mod tests { + use super::serialize_entity; + use unc_jsonrpc_primitives::types::entity_debug::{ + EntityDataEntry, EntityDataStruct, EntityDataValue, + }; + use serde::Serialize; + + fn val(s: T) -> EntityDataValue { + EntityDataValue::String(s.to_string()) + } + + fn tree(entries: Vec<(&str, EntityDataValue)>) -> EntityDataValue { + EntityDataValue::Struct(Box::new(EntityDataStruct { + entries: entries + .into_iter() + .map(|(name, value)| EntityDataEntry { name: name.to_owned(), value }) + .collect(), + })) + } + + #[test] + fn test_serialize_primitives() { + assert_eq!(serialize_entity(&(123 as u64)), val("123")); + assert_eq!( + serialize_entity(&(10000000000000000000000000000000 as u128)), + val("10000000000000000000000000000000") + ); + assert_eq!(serialize_entity(&"abc"), val("abc")); + assert_eq!(serialize_entity(&true), val("true")); + } + + #[test] + fn test_serialize_structs() { + #[derive(Serialize)] + struct A { + a: u64, + b: String, + } + assert_eq!( + serialize_entity(&A { a: 123, b: "abc".to_owned() }), + tree(vec![("a", val("123")), ("b", val("abc"))]) + ); + + #[derive(Serialize)] + struct B { + x: A, + y: Box, + } + assert_eq!( + serialize_entity(&B { + x: A { a: 123, b: "abc".to_owned() }, + y: Box::new(A { a: 456, b: "def".to_owned() }), + }), + tree(vec![ + ("x", tree(vec![("a", val("123")), ("b", val("abc"))])), + ("y", tree(vec![("a", val("456")), ("b", val("def"))])), + ]) + ); + } + + #[test] + fn test_serialize_vecs() { + assert_eq!( + serialize_entity(&vec![1, 2, 3]), + tree(vec![("0", val("1")), ("1", val("2")), ("2", val("3"))]) + ); + + #[derive(Serialize)] + struct A { + x: u64, + } + assert_eq!( + serialize_entity(&vec![A { x: 1 }, A { x: 2 }]), + tree(vec![("0", tree(vec![("x", val("1"))])), ("1", tree(vec![("x", val("2"))])),]) + ); + } + + #[test] + fn test_serialize_tuples() { + assert_eq!( + serialize_entity(&(1, 2, 3)), + tree(vec![("0", val("1")), ("1", val("2")), ("2", val("3"))]) + ); + } + + #[test] + fn test_serialize_maps() { + use std::collections::HashMap; + let mut map = HashMap::new(); + map.insert("a".to_owned(), 1); + assert_eq!(serialize_entity(&map), tree(vec![("a", val("1"))])); + } + + #[test] + fn test_serialize_enums() { + #[derive(Serialize)] + enum A { + A1, + A2(u64), + A3 { x: u64 }, + A4(()), + A5((String, String)), + } + + assert_eq!(serialize_entity(&A::A1), val("A1")); + assert_eq!(serialize_entity(&A::A2(123)), tree(vec![("A2", val("123"))])); + assert_eq!( + serialize_entity(&A::A3 { x: 123 }), + tree(vec![("A3", tree(vec![("x", val("123"))]))]) + ); + assert_eq!(serialize_entity(&A::A4(())), tree(vec![("A4", val("()"))])); + assert_eq!( + serialize_entity(&A::A5(("abc".to_owned(), "def".to_owned()))), + tree(vec![("A5", tree(vec![("0", val("abc")), ("1", val("def"))]))]) + ); + } +} diff --git a/framework/src/lib.rs b/framework/src/lib.rs new file mode 100644 index 000000000..c939356db --- /dev/null +++ b/framework/src/lib.rs @@ -0,0 +1,564 @@ +pub use crate::config::{init_configs, load_config, load_test_config, UncConfig, UNC_BASE}; +use crate::entity_debug::EntityDebugHandlerImpl; +use crate::metrics::spawn_trie_metrics_loop; +pub use crate::runtime::NightshadeRuntime; + +use crate::cold_storage::spawn_cold_store_loop; +use crate::state_sync::{spawn_state_sync_dump, StateSyncDumpHandle}; +use actix::{Actor, Addr}; +use actix_rt::ArbiterHandle; +use anyhow::Context; +use cold_storage::ColdStoreLoopHandle; +use unc_async::actix::AddrWithAutoSpanContextExt; +use unc_async::messaging::{IntoSender, LateBoundSender}; +use unc_async::time; +use unc_chain::state_snapshot_actor::{ + get_delete_snapshot_callback, get_make_snapshot_callback, SnapshotCallbacks, StateSnapshotActor, +}; +use unc_chain::types::RuntimeAdapter; +use unc_chain::{Chain, ChainGenesis}; +use unc_chain_configs::ReshardingHandle; +use unc_chain_configs::SyncConfig; +use unc_chunks::shards_manager_actor::start_shards_manager; +use unc_client::sync::adapter::SyncAdapter; +use unc_client::{start_client, start_view_client, ClientActor, ConfigUpdater, ViewClientActor}; +use unc_epoch_manager::shard_tracker::{ShardTracker, TrackedConfig}; +use unc_epoch_manager::EpochManager; +use unc_network::PeerManagerActor; +use unc_primitives::block::GenesisId; +use unc_store::flat::FlatStateValuesInliningMigrationHandle; +use unc_store::genesis::initialize_genesis_state; +use unc_store::metadata::DbKind; +use unc_store::metrics::spawn_db_metrics_loop; +use unc_store::{DBCol, Mode, NodeStorage, Store, StoreOpenerError}; +use unc_telemetry::TelemetryActor; +use std::path::{Path, PathBuf}; +use std::sync::{Arc, RwLock}; +use tokio::sync::broadcast; +use tracing::info; + +pub mod append_only_map; +pub mod cold_storage; +pub mod config; +mod config_validate; +mod download_file; +pub mod dyn_config; +#[cfg(feature = "json_rpc")] +mod entity_debug; +mod entity_debug_serializer; +mod metrics; +pub mod migrations; +mod runtime; +pub mod state_sync; +pub mod test_utils; + +pub fn get_default_home() -> PathBuf { + if let Ok(unc_home) = std::env::var("unc_HOME") { + return unc_home.into(); + } + + if let Some(mut home) = dirs::home_dir() { + home.push(".unc"); + return home; + } + + PathBuf::default() +} + +/// Opens node’s storage performing migrations and checks when necessary. +/// +/// If opened storage is an RPC store and `unc_config.config.archive` is true, +/// converts the storage to archival node. Otherwise, if opening archival node +/// with that field being false, prints a warning and sets the field to `true`. +/// In other words, once store is archival, the node will act as archival nod +/// regardless of settings in `config.json`. +/// +/// The end goal is to get rid of `archive` option in `config.json` file and +/// have the type of the node be determined purely based on kind of database +/// being opened. +pub fn open_storage(home_dir: &Path, unc_config: &mut UncConfig) -> anyhow::Result { + let migrator = migrations::Migrator::new(unc_config); + let opener = NodeStorage::opener( + home_dir, + unc_config.client_config.archive, + &unc_config.config.store, + unc_config.config.cold_store.as_ref(), + ) + .with_migrator(&migrator); + let storage = match opener.open() { + Ok(storage) => Ok(storage), + Err(StoreOpenerError::IO(err)) => { + Err(anyhow::anyhow!("{err}")) + } + // Cannot happen with Mode::ReadWrite + Err(StoreOpenerError::DbDoesNotExist) => unreachable!(), + // Cannot happen with Mode::ReadWrite + Err(StoreOpenerError::DbAlreadyExists) => unreachable!(), + Err(StoreOpenerError::HotColdExistenceMismatch) => { + Err(anyhow::anyhow!( + "Hot and cold databases must either both exist or both not exist.\n\ + Note that at this moment it’s not possible to convert and RPC or legacy archive database into split hot+cold database.\n\ + To set up node in that configuration, start with neither of the databases existing.", + )) + }, + Err(err @ StoreOpenerError::HotColdVersionMismatch { .. }) => { + Err(anyhow::anyhow!("{err}")) + }, + Err(StoreOpenerError::DbKindMismatch { which, got, want }) => { + Err(if let Some(got) = got { + anyhow::anyhow!("{which} database kind should be {want} but got {got}") + } else { + anyhow::anyhow!("{which} database kind should be {want} but none was set") + }) + } + Err(StoreOpenerError::SnapshotAlreadyExists(snap_path)) => { + Err(anyhow::anyhow!( + "Detected an existing database migration snapshot at ‘{}’.\n\ + Probably a database migration got interrupted and your database is corrupted.\n\ + Please replace files in ‘{}’ with contents of the snapshot, delete the snapshot and try again.", + snap_path.display(), + opener.path().display(), + )) + }, + Err(StoreOpenerError::SnapshotError(err)) => { + use unc_store::config::MigrationSnapshot; + let path = std::path::PathBuf::from("/path/to/snapshot/dir"); + let on = MigrationSnapshot::Path(path).format_example(); + let off = MigrationSnapshot::Enabled(false).format_example(); + Err(anyhow::anyhow!( + "Failed to create a database migration snapshot: {err}.\n\ + To change the location of snapshot adjust \ + ‘store.migration_snapshot’ property in ‘config.json’:\n{on}\n\ + Alternatively, you can disable database migration snapshots \ + in `config.json`:\n{off}" + )) + }, + Err(StoreOpenerError::SnapshotRemoveError { path, error }) => { + let path = path.display(); + Err(anyhow::anyhow!( + "The DB migration has succeeded but deleting of the snapshot \ + at {path} has failed: {error}\n + Try renaming the snapshot directory to temporary name (e.g. \ + by adding tilde to its name) and starting the node. If that \ + works, the snapshot can be deleted.")) + } + // Cannot happen with Mode::ReadWrite + Err(StoreOpenerError::DbVersionMismatchOnRead { .. }) => unreachable!(), + // Cannot happen when migrator is specified. + Err(StoreOpenerError::DbVersionMismatch { .. }) => unreachable!(), + Err(StoreOpenerError::DbVersionMissing { .. }) => { + Err(anyhow::anyhow!("Database version is missing!")) + }, + Err(StoreOpenerError::DbVersionTooOld { got, latest_release, .. }) => { + Err(anyhow::anyhow!( + "Database version {got} is created by an old version \ + of uncd and is no longer supported, please migrate using \ + {latest_release} release" + )) + }, + Err(StoreOpenerError::DbVersionTooNew { got, want }) => { + Err(anyhow::anyhow!( + "Database version {got} is higher than the expected version {want}. \ + It was likely created by newer version of uncd. Please upgrade your uncd." + )) + }, + Err(StoreOpenerError::MigrationError(err)) => { + Err(err) + }, + Err(StoreOpenerError::CheckpointError(err)) => { + Err(err) + }, + }.with_context(|| format!("unable to open database at {}", opener.path().display()))?; + + unc_config.config.archive = storage.is_archive()?; + Ok(storage) +} + +// Safely get the split store while checking that all conditions to use it are met. +fn get_split_store(config: &UncConfig, storage: &NodeStorage) -> anyhow::Result> { + // SplitStore should only be used on archival nodes. + if !config.config.archive { + return Ok(None); + } + + // SplitStore should only be used if cold store is configured. + if config.config.cold_store.is_none() { + return Ok(None); + } + + // SplitStore should only be used in the view client if it is enabled. + if !config.config.split_storage.as_ref().map_or(false, |c| c.enable_split_storage_view_client) { + return Ok(None); + } + + // SplitStore should only be used if the migration is finished. The + // migration to cold store is finished when the db kind of the hot store is + // changed from Archive to Hot. + if storage.get_hot_store().get_db_kind()? != Some(DbKind::Hot) { + return Ok(None); + } + + Ok(storage.get_split_store()) +} + +pub struct NearNode { + pub client: Addr, + pub view_client: Addr, + pub arbiters: Vec, + pub rpc_servers: Vec<(&'static str, actix_web::dev::ServerHandle)>, + /// The cold_store_loop_handle will only be set if the cold store is configured. + /// It's a handle to a background thread that copies data from the hot store to the cold store. + pub cold_store_loop_handle: Option, + /// Contains handles to background threads that may be dumping state to S3. + pub state_sync_dump_handle: Option, + /// A handle to control background flat state values inlining migration. + /// Needed temporarily, will be removed after the migration is completed. + pub flat_state_migration_handle: FlatStateValuesInliningMigrationHandle, + // A handle that allows the main process to interrupt resharding if needed. + // This typically happens when the main process is interrupted. + pub resharding_handle: ReshardingHandle, +} + +pub fn start_with_config(home_dir: &Path, config: UncConfig) -> anyhow::Result { + start_with_config_and_synchronization(home_dir, config, None, None) +} + +pub fn start_with_config_and_synchronization( + home_dir: &Path, + mut config: UncConfig, + // 'shutdown_signal' will notify the corresponding `oneshot::Receiver` when an instance of + // `ClientActor` gets dropped. + shutdown_signal: Option>, + config_updater: Option, +) -> anyhow::Result { + let storage = open_storage(home_dir, &mut config)?; + let db_metrics_arbiter = if config.client_config.enable_statistics_export { + let period = config.client_config.log_summary_period; + let db_metrics_arbiter_handle = spawn_db_metrics_loop(&storage, period)?; + Some(db_metrics_arbiter_handle) + } else { + None + }; + + let trie_metrics_arbiter = spawn_trie_metrics_loop( + config.clone(), + storage.get_hot_store(), + config.client_config.log_summary_period, + )?; + + // Initialize genesis_state in store either from genesis config or dump before other components. + // We only initialize if the genesis state is not already initialized in store. + // This sets up genesis_state_roots and genesis_hash in store. + initialize_genesis_state(storage.get_hot_store(), &config.genesis, Some(home_dir)); + + let epoch_manager = + EpochManager::new_arc_handle(storage.get_hot_store(), &config.genesis.config); + let shard_tracker = + ShardTracker::new(TrackedConfig::from_config(&config.client_config), epoch_manager.clone()); + let runtime = NightshadeRuntime::from_config( + home_dir, + storage.get_hot_store(), + &config, + epoch_manager.clone(), + ); + + // Get the split store. If split store is some then create a new set of structures for + // the view client. Otherwise just re-use the existing ones. + let split_store = get_split_store(&config, &storage)?; + let (view_epoch_manager, view_shard_tracker, view_runtime) = + if let Some(split_store) = &split_store { + let view_epoch_manager = + EpochManager::new_arc_handle(split_store.clone(), &config.genesis.config); + let view_shard_tracker = ShardTracker::new( + TrackedConfig::from_config(&config.client_config), + epoch_manager.clone(), + ); + let view_runtime = NightshadeRuntime::from_config( + home_dir, + split_store.clone(), + &config, + view_epoch_manager.clone(), + ); + (view_epoch_manager, view_shard_tracker, view_runtime) + } else { + (epoch_manager.clone(), shard_tracker.clone(), runtime.clone()) + }; + + let cold_store_loop_handle = spawn_cold_store_loop(&config, &storage, epoch_manager.clone())?; + + let telemetry = TelemetryActor::new(config.telemetry_config.clone()).start(); + let chain_genesis = ChainGenesis::new(&config.genesis); + let genesis_block = + Chain::make_genesis_block(epoch_manager.as_ref(), runtime.as_ref(), &chain_genesis)?; + let genesis_id = GenesisId { + chain_id: config.client_config.chain_id.clone(), + hash: *genesis_block.header().hash(), + }; + + // State Sync actors + let client_adapter_for_sync = Arc::new(LateBoundSender::default()); + let network_adapter_for_sync = Arc::new(LateBoundSender::default()); + let sync_adapter = Arc::new(RwLock::new(SyncAdapter::new( + client_adapter_for_sync.as_sender(), + network_adapter_for_sync.as_sender(), + ))); + + let node_id = config.network_config.node_id(); + let network_adapter = Arc::new(LateBoundSender::default()); + let shards_manager_adapter = Arc::new(LateBoundSender::default()); + let client_adapter_for_shards_manager = Arc::new(LateBoundSender::default()); + let adv = unc_client::adversarial::Controls::new(config.client_config.archive); + + let view_client = start_view_client( + config.validator_signer.as_ref().map(|signer| signer.validator_id().clone()), + chain_genesis.clone(), + view_epoch_manager.clone(), + view_shard_tracker, + view_runtime.clone(), + network_adapter.clone().into(), + config.client_config.clone(), + adv.clone(), + ); + + let state_snapshot_actor = Arc::new( + StateSnapshotActor::new( + runtime.get_flat_storage_manager(), + network_adapter.clone().into(), + runtime.get_tries(), + ) + .start(), + ); + let delete_snapshot_callback = get_delete_snapshot_callback(state_snapshot_actor.clone()); + let make_snapshot_callback = + get_make_snapshot_callback(state_snapshot_actor, runtime.get_flat_storage_manager()); + let snapshot_callbacks = SnapshotCallbacks { make_snapshot_callback, delete_snapshot_callback }; + + let (client_actor, client_arbiter_handle, resharding_handle) = start_client( + config.client_config.clone(), + chain_genesis.clone(), + epoch_manager.clone(), + shard_tracker.clone(), + runtime.clone(), + node_id, + sync_adapter, + network_adapter.clone().into(), + shards_manager_adapter.as_sender(), + config.validator_signer.clone(), + telemetry, + Some(snapshot_callbacks), + shutdown_signal, + adv, + config_updater, + ); + if let SyncConfig::Peers = config.client_config.state_sync.sync { + client_adapter_for_sync.bind(client_actor.clone().with_auto_span_context()) + }; + client_adapter_for_shards_manager.bind(client_actor.clone().with_auto_span_context()); + let (shards_manager_actor, shards_manager_arbiter_handle) = start_shards_manager( + epoch_manager.clone(), + shard_tracker.clone(), + network_adapter.as_sender(), + client_adapter_for_shards_manager.as_sender(), + config.validator_signer.as_ref().map(|signer| signer.validator_id().clone()), + split_store.unwrap_or(storage.get_hot_store()), + config.client_config.chunk_request_retry_period, + ); + shards_manager_adapter.bind(shards_manager_actor.with_auto_span_context()); + + let flat_state_migration_handle = + FlatStateValuesInliningMigrationHandle::start_background_migration( + storage.get_hot_store(), + runtime.get_flat_storage_manager(), + config.client_config.client_background_migration_threads, + ); + + let state_sync_dump_handle = spawn_state_sync_dump( + &config.client_config, + chain_genesis, + epoch_manager, + shard_tracker, + runtime, + config.validator_signer.as_ref().map(|signer| signer.validator_id().clone()), + )?; + + let hot_store = storage.get_hot_store(); + + let mut rpc_servers = Vec::new(); + let network_actor = PeerManagerActor::spawn( + time::Clock::real(), + storage.into_inner(unc_store::Temperature::Hot), + config.network_config, + Arc::new(unc_client::adapter::Adapter::new(client_actor.clone(), view_client.clone())), + shards_manager_adapter.as_sender(), + genesis_id, + ) + .context("PeerManager::spawn()")?; + network_adapter.bind(network_actor.clone().with_auto_span_context()); + if let SyncConfig::Peers = config.client_config.state_sync.sync { + network_adapter_for_sync.bind(network_actor.clone().with_auto_span_context()) + } + #[cfg(feature = "json_rpc")] + if let Some(rpc_config) = config.rpc_config { + let entity_debug_handler = EntityDebugHandlerImpl { + epoch_manager: view_epoch_manager, + runtime: view_runtime, + store: hot_store, + }; + rpc_servers.extend(unc_jsonrpc::start_http( + rpc_config, + config.genesis.config.clone(), + client_actor.clone(), + view_client.clone(), + Some(network_actor), + Arc::new(entity_debug_handler), + )); + } + + rpc_servers.shrink_to_fit(); + + tracing::trace!(target: "diagnostic", key = "log", "Starting NEAR node with diagnostic activated"); + + let mut arbiters = + vec![client_arbiter_handle, shards_manager_arbiter_handle, trie_metrics_arbiter]; + if let Some(db_metrics_arbiter) = db_metrics_arbiter { + arbiters.push(db_metrics_arbiter); + } + + Ok(NearNode { + client: client_actor, + view_client, + rpc_servers, + arbiters, + cold_store_loop_handle, + state_sync_dump_handle, + flat_state_migration_handle, + resharding_handle, + }) +} + +pub struct RecompressOpts { + pub dest_dir: PathBuf, + pub keep_partial_chunks: bool, + pub keep_invalid_chunks: bool, + pub keep_trie_changes: bool, +} + +pub fn recompress_storage(home_dir: &Path, opts: RecompressOpts) -> anyhow::Result<()> { + use strum::IntoEnumIterator; + + let config_path = home_dir.join(config::CONFIG_FILENAME); + let config = config::Config::from_file(&config_path) + .map_err(|err| anyhow::anyhow!("{}: {}", config_path.display(), err))?; + let archive = config.archive; + let mut skip_columns = Vec::new(); + if archive && !opts.keep_partial_chunks { + skip_columns.push(DBCol::PartialChunks); + } + if archive && !opts.keep_invalid_chunks { + skip_columns.push(DBCol::InvalidChunks); + } + if archive && !opts.keep_trie_changes { + skip_columns.push(DBCol::TrieChanges); + } + + let src_opener = NodeStorage::opener(home_dir, archive, &config.store, None); + let src_path = src_opener.path(); + + let mut dst_config = config.store.clone(); + dst_config.path = Some(opts.dest_dir); + // Note: opts.dest_dir is resolved relative to current working directory + // (since it’s a command line option) which is why we set home to cwd. + let cwd = std::env::current_dir()?; + let dst_opener = NodeStorage::opener(&cwd, archive, &dst_config, None); + let dst_path = dst_opener.path(); + + info!(target: "recompress", + src = %src_path.display(), dest = %dst_path.display(), + "Recompressing database"); + + info!("Opening database at {}", src_path.display()); + let src_store = src_opener.open_in_mode(Mode::ReadOnly)?.get_hot_store(); + + let final_head_height = if skip_columns.contains(&DBCol::PartialChunks) { + let tip: Option = + src_store.get_ser(DBCol::BlockMisc, unc_store::FINAL_HEAD_KEY)?; + anyhow::ensure!( + tip.is_some(), + "{}: missing {}; is this a freshly set up node? note that recompress_storage makes no sense on those", + src_path.display(), + std::str::from_utf8(unc_store::FINAL_HEAD_KEY).unwrap(), + ); + tip.map(|tip| tip.height) + } else { + None + }; + + info!("Creating database at {}", dst_path.display()); + let dst_store = dst_opener.open_in_mode(Mode::Create)?.get_hot_store(); + + const BATCH_SIZE_BYTES: u64 = 150_000_000; + + for column in DBCol::iter() { + let skip = skip_columns.contains(&column); + info!( + target: "recompress", + column_id = column as usize, + %column, + "{}", + if skip { "Clearing " } else { "Processing" } + ); + if skip { + continue; + } + + let mut store_update = dst_store.store_update(); + let mut total_written: u64 = 0; + let mut batch_written: u64 = 0; + let mut count_keys: u64 = 0; + for item in src_store.iter_raw_bytes(column) { + let (key, value) = item.with_context(|| format!("scanning column {column}"))?; + store_update.set_raw_bytes(column, &key, &value); + total_written += value.len() as u64; + batch_written += value.len() as u64; + count_keys += 1; + if batch_written >= BATCH_SIZE_BYTES { + store_update.commit()?; + info!( + target: "recompress", + column_id = column as usize, + %count_keys, + %total_written, + "Processing", + ); + batch_written = 0; + store_update = dst_store.store_update(); + } + } + info!( + target: "recompress", + column_id = column as usize, + %count_keys, + %total_written, + "Done with " + ); + store_update.commit()?; + } + + // If we’re not keeping DBCol::PartialChunks, update chunk tail to point to + // current final block. If we don’t do that, the gc will try to work its + // way from the genesis even though chunks at those heights have been + // deleted. + if skip_columns.contains(&DBCol::PartialChunks) { + let chunk_tail = final_head_height.unwrap(); + info!(target: "recompress", %chunk_tail, "Setting chunk tail"); + let mut store_update = dst_store.store_update(); + store_update.set_ser(DBCol::BlockMisc, unc_store::CHUNK_TAIL_KEY, &chunk_tail)?; + store_update.commit()?; + } + + core::mem::drop(dst_store); + core::mem::drop(src_store); + + info!(target: "recompress", dest = %dst_path.display(), "Database recompressed"); + Ok(()) +} diff --git a/framework/src/metrics.rs b/framework/src/metrics.rs new file mode 100644 index 000000000..9a75f303b --- /dev/null +++ b/framework/src/metrics.rs @@ -0,0 +1,338 @@ +use std::rc::Rc; + +use actix_rt::ArbiterHandle; +use unc_chain::{Block, ChainStore, ChainStoreAccess}; +use unc_epoch_manager::EpochManager; +use unc_o11y::metrics::{ + exponential_buckets, linear_buckets, processing_time_buckets, try_create_histogram_vec, + try_create_int_counter_vec, try_create_int_gauge, try_create_int_gauge_vec, HistogramVec, + IntCounterVec, IntGauge, IntGaugeVec, +}; + +use unc_primitives::{shard_layout::ShardLayout, state_record::StateRecord, trie_key}; +use unc_store::{ShardUId, Store, Trie, TrieDBStorage}; +use once_cell::sync::Lazy; + +use crate::UncConfig; + +pub static APPLYING_CHUNKS_TIME: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_applying_chunks_time", + "Time taken to apply chunks per shard", + &["shard_id"], + Some(processing_time_buckets()), + ) + .unwrap() +}); + +pub(crate) static APPLY_CHUNK_DELAY: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_apply_chunk_delay_seconds", + "Time to process a chunk. Gas used by the chunk is a metric label, rounded up to 100 teragas.", + &["tgas_ceiling"], + Some(linear_buckets(0.0, 0.05, 50).unwrap()), + ) + .unwrap() +}); + +pub(crate) static DELAYED_RECEIPTS_COUNT: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_delayed_receipts_count", + "The count of the delayed receipts. Indicator of congestion.", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static POSTPONED_RECEIPTS_COUNT: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_postponed_receipts_count", + "The count of the postponed receipts. Indicator of congestion.", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static PREPARE_TX_SIZE: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_prepare_tx_size", + "Sum of transaction sizes per produced chunk, as a histogram", + &["shard_id"], + // Maximum is < 14MB, typical values are unknown right now so buckets + // might need to be adjusted later when we have collected data + Some(vec![1_000.0, 10_000., 100_000., 500_000., 1e6, 2e6, 4e6, 8e6, 12e6]), + ) + .unwrap() +}); + +pub(crate) static CONFIG_CORRECT: Lazy = Lazy::new(|| { + try_create_int_gauge( + "unc_config_correct", + "Are the current dynamically loadable configs correct", + ) + .unwrap() +}); + +pub(crate) static COLD_STORE_COPY_RESULT: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_cold_store_copy_result", + "The result of a cold store copy iteration in the cold store loop.", + &["copy_result"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_DUMP_ITERATION_ELAPSED: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_state_sync_dump_iteration_elapsed_sec", + "Time needed to obtain and write a part", + &["shard_id"], + Some(exponential_buckets(0.001, 1.6, 25).unwrap()), + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_DUMP_NUM_PARTS_TOTAL: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_state_sync_dump_num_parts_total", + "Total number of parts in the epoch that being dumped", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_DUMP_NUM_PARTS_DUMPED: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_state_sync_dump_num_parts_dumped", + "Number of parts dumped in the epoch that is being dumped", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_DUMP_SIZE_TOTAL: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_state_sync_dump_size_total", + "Total size of parts written to S3", + &["epoch_height", "shard_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_DUMP_EPOCH_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_state_sync_dump_epoch_height", + "Epoch Height of an epoch being dumped", + &["shard_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_APPLY_PART_DELAY: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_state_sync_apply_part_delay_sec", + "Latency of applying a state part", + &["shard_id"], + Some(exponential_buckets(0.001, 2.0, 20).unwrap()), + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_OBTAIN_PART_DELAY: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_state_sync_obtain_part_delay_sec", + "Latency of applying a state part", + &["shard_id", "result"], + Some(exponential_buckets(0.001, 2.0, 20).unwrap()), + ) + .unwrap() +}); + +fn log_trie_item(key: Vec, value: Vec) { + if !tracing::level_enabled!(tracing::Level::TRACE) { + return; + } + let state_record = StateRecord::from_raw_key_value_impl(key, value); + match state_record { + Ok(Some(StateRecord::PostponedReceipt(receipt))) => { + tracing::trace!( + target: "metrics", + "trie-stats - PostponedReceipt(predecessor_id: {:?}, receiver_id: {:?})", + receipt.predecessor_id, + receipt.receiver_id, + ); + } + _ => { + tracing::trace!(target: "metrics", "trie-stats - {state_record:?}" ); + } + } +} + +fn export_postponed_receipt_count(unc_config: &UncConfig, store: &Store) -> anyhow::Result<()> { + let chain_store = ChainStore::new( + store.clone(), + unc_config.genesis.config.genesis_height, + unc_config.client_config.save_trie_changes, + ); + let epoch_manager = + EpochManager::new_from_genesis_config(store.clone(), &unc_config.genesis.config)?; + + let head = chain_store.final_head()?; + let block = chain_store.get_block(&head.last_block_hash)?; + let shard_layout = epoch_manager.get_shard_layout(block.header().epoch_id())?; + + for chunk_header in block.chunks().iter() { + let shard_id = chunk_header.shard_id(); + if chunk_header.height_included() != block.header().height() { + tracing::trace!(target: "metrics", "trie-stats - chunk for shard {shard_id} is missing, skipping it."); + POSTPONED_RECEIPTS_COUNT.with_label_values(&[&shard_id.to_string()]).set(0); + continue; + } + + let count = get_postponed_receipt_count_for_shard( + shard_id, + &shard_layout, + &chain_store, + &block, + store, + ); + let count = match count { + Ok(count) => count, + Err(err) => { + tracing::trace!(target: "metrics", "trie-stats - error when getting the postponed receipt count {err:?}"); + 0 + } + }; + POSTPONED_RECEIPTS_COUNT.with_label_values(&[&shard_id.to_string()]).set(count); + } + + Ok(()) +} + +fn get_postponed_receipt_count_for_shard( + shard_id: u64, + shard_layout: &ShardLayout, + chain_store: &ChainStore, + block: &Block, + store: &Store, +) -> Result { + let shard_uid = ShardUId::from_shard_id_and_layout(shard_id, shard_layout); + let chunk_extra = chain_store.get_chunk_extra(block.hash(), &shard_uid)?; + let state_root = chunk_extra.state_root(); + let storage = TrieDBStorage::new(store.clone(), shard_uid); + let storage = Rc::new(storage); + let flat_storage_chunk_view = None; + let trie = Trie::new(storage, *state_root, flat_storage_chunk_view); + get_postponed_receipt_count_for_trie(trie) +} + +fn get_postponed_receipt_count_for_trie(trie: Trie) -> Result { + let mut iter = trie.iter()?; + iter.seek_prefix([trie_key::col::POSTPONED_RECEIPT])?; + let mut count = 0; + for item in iter { + let (key, value) = match item { + Ok(item) => item, + Err(err) => { + tracing::trace!(target: "metrics", "trie-stats - error when reading item {err:?}"); + continue; + } + }; + if !key.is_empty() && key[0] != trie_key::col::POSTPONED_RECEIPT { + tracing::trace!(target: "metrics", "trie-stats - stopping iteration as reached other col type."); + break; + } + count += 1; + log_trie_item(key, value); + } + tracing::trace!(target: "metrics", "trie-stats - postponed receipt count {count}"); + Ok(count) +} + +/// Spawns a background loop that will periodically log trie related metrics. +pub fn spawn_trie_metrics_loop( + unc_config: UncConfig, + store: Store, + period: std::time::Duration, +) -> anyhow::Result { + tracing::debug!(target:"metrics", "Spawning the trie metrics loop."); + let arbiter = actix_rt::Arbiter::new(); + + let start = tokio::time::Instant::now(); + let mut interval = actix_rt::time::interval_at(start, period); + interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); + + arbiter.spawn(async move { + tracing::debug!(target:"metrics", "Starting the spawn metrics loop."); + loop { + interval.tick().await; + + let start_time = std::time::Instant::now(); + let result = export_postponed_receipt_count(&unc_config, &store); + if let Err(err) = result { + tracing::error!(target: "metrics", "Error when exporting postponed receipts count {err}."); + }; + + tracing::trace!(target: "metrics", "exporting postponed receipt count took {:?}.", start_time.elapsed()); + } + }); + + Ok(arbiter.handle()) +} + +#[cfg(test)] +mod tests { + use super::*; + use unc_primitives::trie_key::col; + use unc_store::test_utils::simplify_changes; + use unc_store::test_utils::test_populate_trie; + use unc_store::test_utils::TestTriesBuilder; + + fn create_item(key: &[u8]) -> (Vec, Option>) { + (key.to_vec(), Some(vec![])) + } + + fn create_trie(items: &[(Vec, Option>)]) -> Trie { + let tries = TestTriesBuilder::new().build(); + let shard_uid = ShardUId { version: 1, shard_id: 0 }; + let trie_changes = simplify_changes(&items); + let state_root = test_populate_trie(&tries, &Trie::EMPTY_ROOT, shard_uid, trie_changes); + let trie = tries.get_trie_for_shard(shard_uid, state_root); + trie + } + + #[test] + fn test_get_postponed_receipt_count() { + // no postponed receipts + let count = get_postponed_receipt_count_for_trie(create_trie(&[])).unwrap(); + assert_eq!(count, 0); + + // one postponed receipts + let items = vec![create_item(&[col::POSTPONED_RECEIPT, 1, 2, 3])]; + let count = get_postponed_receipt_count_for_trie(create_trie(&items)).unwrap(); + assert_eq!(count, 1); + + // two postponed receipts + let items = vec![ + create_item(&[col::POSTPONED_RECEIPT, 1]), + create_item(&[col::POSTPONED_RECEIPT, 2]), + ]; + let count = get_postponed_receipt_count_for_trie(create_trie(&items)).unwrap(); + assert_eq!(count, 2); + + // three postponed receipts but also other records that are not + // postponed receipts and should not be counted + let items = vec![ + create_item(&[col::ACCOUNT, 1]), + create_item(&[col::ACCESS_KEY, 1]), + create_item(&[col::POSTPONED_RECEIPT_ID, 1]), + create_item(&[col::POSTPONED_RECEIPT, 1]), + create_item(&[col::POSTPONED_RECEIPT, 2]), + create_item(&[col::POSTPONED_RECEIPT, 3]), + create_item(&[col::DELAYED_RECEIPT_OR_INDICES, 1]), + create_item(&[col::CONTRACT_DATA, 1]), + ]; + let count = get_postponed_receipt_count_for_trie(create_trie(&items)).unwrap(); + assert_eq!(count, 3); + } +} diff --git a/framework/src/migrations.rs b/framework/src/migrations.rs new file mode 100644 index 000000000..ceada6141 --- /dev/null +++ b/framework/src/migrations.rs @@ -0,0 +1,155 @@ +use unc_chain::{ChainStore, ChainStoreAccess}; +use unc_primitives::receipt::ReceiptResult; +use unc_primitives::runtime::migration_data::MigrationData; +use unc_primitives::types::Gas; +use unc_primitives::utils::index_to_bytes; +use unc_store::metadata::{DbVersion, DB_VERSION}; +use unc_store::migrations::BatchedStoreUpdate; +use unc_store::{DBCol, Store}; + +/// Fix an issue with block ordinal (#5761) +// This migration takes at least 3 hours to complete on mainnet +pub fn migrate_30_to_31(store: &Store, unc_config: &crate::UncConfig) -> anyhow::Result<()> { + if unc_config.client_config.archive + && &unc_config.genesis.config.chain_id == unc_primitives::chains::MAINNET + { + do_migrate_30_to_31(store, &unc_config.genesis.config)?; + } + Ok(()) +} + +/// Migrates the database from version 30 to 31. +/// +/// Recomputes block ordinal due to a bug fixed in #5761. +pub fn do_migrate_30_to_31( + store: &Store, + genesis_config: &unc_chain_configs::GenesisConfig, +) -> anyhow::Result<()> { + let genesis_height = genesis_config.genesis_height; + let chain_store = ChainStore::new(store.clone(), genesis_height, false); + let head = chain_store.head()?; + let mut store_update = BatchedStoreUpdate::new(store, 10_000_000); + let mut count = 0; + // we manually checked mainnet archival data and the first block where the discrepancy happened is `47443088`. + for height in 47443088..=head.height { + if let Ok(block_hash) = chain_store.get_block_hash_by_height(height) { + let block_ordinal = chain_store.get_block_merkle_tree(&block_hash)?.size(); + let block_hash_from_block_ordinal = + chain_store.get_block_hash_from_ordinal(block_ordinal)?; + if block_hash_from_block_ordinal != block_hash { + println!( + "Inconsistency in block ordinal to block hash mapping found at block height {}", + height + ); + count += 1; + store_update + .set_ser(DBCol::BlockOrdinal, &index_to_bytes(block_ordinal), &block_hash) + .expect("BorshSerialize should not fail"); + } + } + } + println!("total inconsistency count: {}", count); + store_update.finish()?; + Ok(()) +} + +/// In test runs reads and writes here used 442 TGas, but in test on live net migration take +/// between 4 and 4.5s. We do not want to process any receipts in this block +const GAS_USED_FOR_STORAGE_USAGE_DELTA_MIGRATION: Gas = 1_000_000_000_000_000; + +pub fn load_migration_data(chain_id: &str) -> MigrationData { + let is_mainnet = chain_id == unc_primitives::chains::MAINNET; + MigrationData { + storage_usage_delta: if is_mainnet { + unc_mainnet_res::mainnet_storage_usage_delta() + } else { + Vec::new() + }, + storage_usage_fix_gas: if is_mainnet { + GAS_USED_FOR_STORAGE_USAGE_DELTA_MIGRATION + } else { + 0 + }, + restored_receipts: if is_mainnet { + unc_mainnet_res::mainnet_restored_receipts() + } else { + ReceiptResult::default() + }, + } +} + +pub(super) struct Migrator<'a> { + config: &'a crate::config::UncConfig, +} + +impl<'a> Migrator<'a> { + pub fn new(config: &'a crate::config::UncConfig) -> Self { + Self { config } + } +} + +impl<'a> unc_store::StoreMigrator for Migrator<'a> { + fn check_support(&self, version: DbVersion) -> Result<(), &'static str> { + // TODO(mina86): Once open ranges in match are stabilised, get rid of + // this constant and change the match to be 27..DB_VERSION. + const LAST_SUPPORTED: DbVersion = DB_VERSION - 1; + match version { + 0..=26 => Err("1.26"), + 27..=LAST_SUPPORTED => Ok(()), + _ => unreachable!(), + } + } + + fn migrate(&self, store: &Store, version: DbVersion) -> anyhow::Result<()> { + match version { + 0..=31 => unreachable!(), + 32 => unc_store::migrations::migrate_32_to_33(store), + 33 => { + unc_store::migrations::migrate_33_to_34(store, self.config.client_config.archive) + } + 34 => unc_store::migrations::migrate_34_to_35(store), + 35 => { + tracing::info!(target: "migrations", "Migrating DB version from 35 to 36. Flat storage data will be created on disk."); + tracing::info!(target: "migrations", "It will happen in parallel with regular block processing. ETA is 15h for RPC node and 2d for archival node."); + Ok(()) + } + 36 => unc_store::migrations::migrate_36_to_37(store), + 37 => unc_store::migrations::migrate_37_to_38(store), + DB_VERSION.. => unreachable!(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use unc_mainnet_res::mainnet_restored_receipts; + use unc_mainnet_res::mainnet_storage_usage_delta; + use unc_primitives::hash::hash; + + #[test] + fn test_migration_data() { + assert_eq!( + hash(serde_json::to_string(&mainnet_storage_usage_delta()).unwrap().as_bytes()) + .to_string(), + "2fEgaLFBBJZqgLQEvHPsck4NS3sFzsgyKaMDqTw5HVvQ" + ); + let mainnet_migration_data = load_migration_data(unc_primitives::chains::MAINNET); + assert_eq!(mainnet_migration_data.storage_usage_delta.len(), 3112); + let testnet_migration_data = load_migration_data(unc_primitives::chains::TESTNET); + assert_eq!(testnet_migration_data.storage_usage_delta.len(), 0); + } + + #[test] + fn test_restored_receipts_data() { + assert_eq!( + hash(serde_json::to_string(&mainnet_restored_receipts()).unwrap().as_bytes()) + .to_string(), + "48ZMJukN7RzvyJSW9MJ5XmyQkQFfjy2ZxPRaDMMHqUcT" + ); + let mainnet_migration_data = load_migration_data(unc_primitives::chains::MAINNET); + assert_eq!(mainnet_migration_data.restored_receipts.get(&0u64).unwrap().len(), 383); + let testnet_migration_data = load_migration_data(unc_primitives::chains::TESTNET); + assert!(testnet_migration_data.restored_receipts.is_empty()); + } +} diff --git a/framework/src/runtime/errors.rs b/framework/src/runtime/errors.rs new file mode 100644 index 000000000..0e0e57ad0 --- /dev/null +++ b/framework/src/runtime/errors.rs @@ -0,0 +1,111 @@ +use unc_chain::unc_chain_primitives::error::QueryError; + +#[easy_ext::ext(FromStateViewerErrors)] +impl QueryError { + pub fn from_call_function_error( + error: node_runtime::state_viewer::errors::CallFunctionError, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + ) -> Self { + match error { + node_runtime::state_viewer::errors::CallFunctionError::InvalidAccountId { + requested_account_id, + } => Self::InvalidAccount { requested_account_id, block_height, block_hash }, + node_runtime::state_viewer::errors::CallFunctionError::AccountDoesNotExist { + requested_account_id, + } => Self::UnknownAccount { requested_account_id, block_height, block_hash }, + node_runtime::state_viewer::errors::CallFunctionError::InternalError { + error_message, + } => Self::InternalError { error_message, block_height, block_hash }, + node_runtime::state_viewer::errors::CallFunctionError::VMError { error_message } => { + Self::ContractExecutionError { error_message, block_height, block_hash } + } + } + } + + pub fn from_view_account_error( + error: node_runtime::state_viewer::errors::ViewAccountError, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + ) -> Self { + match error { + node_runtime::state_viewer::errors::ViewAccountError::InvalidAccountId { + requested_account_id, + } => Self::InvalidAccount { requested_account_id, block_height, block_hash }, + node_runtime::state_viewer::errors::ViewAccountError::AccountDoesNotExist { + requested_account_id, + } => Self::UnknownAccount { requested_account_id, block_height, block_hash }, + node_runtime::state_viewer::errors::ViewAccountError::InternalError { + error_message, + } => Self::InternalError { error_message, block_height, block_hash }, + } + } + + pub fn from_view_contract_code_error( + error: node_runtime::state_viewer::errors::ViewContractCodeError, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + ) -> Self { + match error { + node_runtime::state_viewer::errors::ViewContractCodeError::InvalidAccountId { + requested_account_id, + } => Self::InvalidAccount { requested_account_id, block_height, block_hash }, + node_runtime::state_viewer::errors::ViewContractCodeError::AccountDoesNotExist { + requested_account_id, + } => Self::UnknownAccount { requested_account_id, block_height, block_hash }, + node_runtime::state_viewer::errors::ViewContractCodeError::InternalError { + error_message, + } => Self::InternalError { error_message, block_height, block_hash }, + node_runtime::state_viewer::errors::ViewContractCodeError::NoContractCode { + contract_account_id, + } => Self::NoContractCode { contract_account_id, block_height, block_hash }, + } + } + + pub fn from_view_state_error( + error: node_runtime::state_viewer::errors::ViewStateError, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + ) -> Self { + match error { + node_runtime::state_viewer::errors::ViewStateError::InvalidAccountId { + requested_account_id, + } => Self::InvalidAccount { requested_account_id, block_height, block_hash }, + node_runtime::state_viewer::errors::ViewStateError::InternalError { error_message } => { + Self::InternalError { error_message, block_height, block_hash } + } + node_runtime::state_viewer::errors::ViewStateError::AccountDoesNotExist { + requested_account_id, + } => Self::UnknownAccount { requested_account_id, block_height, block_hash }, + node_runtime::state_viewer::errors::ViewStateError::AccountStateTooLarge { + requested_account_id, + } => Self::TooLargeContractState { requested_account_id, block_height, block_hash }, + } + } + + pub fn from_view_access_key_error( + error: node_runtime::state_viewer::errors::ViewAccessKeyError, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + ) -> Self { + match error { + node_runtime::state_viewer::errors::ViewAccessKeyError::InvalidAccountId { + requested_account_id, + } => Self::InvalidAccount { requested_account_id, block_height, block_hash }, + node_runtime::state_viewer::errors::ViewAccessKeyError::AccessKeyDoesNotExist { + public_key, + } => Self::UnknownAccessKey { public_key, block_height, block_hash }, + node_runtime::state_viewer::errors::ViewAccessKeyError::InternalError { + error_message, + } => Self::InternalError { error_message, block_height, block_hash }, + } + } + + pub fn from_epoch_error( + error: unc_primitives::errors::EpochError, + block_height: unc_primitives::types::BlockHeight, + block_hash: unc_primitives::hash::CryptoHash, + ) -> Self { + Self::InternalError { error_message: error.to_string(), block_height, block_hash } + } +} diff --git a/framework/src/runtime/mod.rs b/framework/src/runtime/mod.rs new file mode 100644 index 000000000..5095ee13a --- /dev/null +++ b/framework/src/runtime/mod.rs @@ -0,0 +1,1465 @@ +use crate::metrics; +use crate::migrations::load_migration_data; +use crate::UncConfig; + +use borsh::BorshDeserialize; +use errors::FromStateViewerErrors; +use unc_chain::types::{ + ApplyChunkBlockContext, ApplyChunkResult, ApplyChunkShardContext, ApplyResultForResharding, + RuntimeAdapter, RuntimeStorageConfig, StorageDataSource, Tip, +}; +use unc_chain::Error; +use unc_chain_configs::{ + GenesisConfig, ProtocolConfig, DEFAULT_GC_NUM_EPOCHS_TO_KEEP, MIN_GC_NUM_EPOCHS_TO_KEEP, +}; +use unc_crypto::PublicKey; +use unc_epoch_manager::{EpochManagerAdapter, EpochManagerHandle}; +use unc_parameters::{ActionCosts, ExtCosts, RuntimeConfigStore}; +use unc_pool::types::PoolIterator; +use unc_primitives::account::{AccessKey, Account}; +use unc_primitives::errors::{InvalidTxError, RuntimeError, StorageError}; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::receipt::{DelayedReceiptIndices, Receipt}; +use unc_primitives::runtime::migration_data::{MigrationData, MigrationFlags}; +use unc_primitives::sandbox::state_patch::SandboxStatePatch; +use unc_primitives::shard_layout::{ + account_id_to_shard_id, account_id_to_shard_uid, ShardLayout, ShardUId, +}; +use unc_primitives::state_part::PartId; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::{ + AccountId, Balance, BlockHeight, EpochHeight, EpochId, EpochInfoProvider, Gas, MerkleHash, + ShardId, StateChangeCause, StateChangesForResharding, StateRoot, StateRootNode, +}; +use unc_primitives::version::ProtocolVersion; +use unc_primitives::views::{ + AccessKeyInfoView, CallResult, QueryRequest, QueryResponse, QueryResponseKind, ViewApplyState, + ViewStateResult, +}; +use unc_store::config::StateSnapshotType; +use unc_store::flat::FlatStorageManager; +use unc_store::metadata::DbKind; +use unc_store::{ + ApplyStatePartResult, DBCol, ShardTries, StateSnapshotConfig, Store, + StoreCompiledContractCache, Trie, TrieConfig, WrappedTrieChanges, COLD_HEAD_KEY, +}; +use unc_vm_runner::logic::CompiledContractCache; +use unc_vm_runner::precompile_contract; +use unc_vm_runner::ContractCode; +use node_runtime::adapter::ViewRuntimeAdapter; +use node_runtime::state_viewer::TrieViewer; +use node_runtime::{ + validate_transaction, verify_and_charge_transaction, ApplyState, Runtime, + ValidatorAccountsUpdate, +}; +use std::collections::HashMap; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use std::time::Duration; +use std::time::Instant; +use tracing::{debug, error, info}; + +pub mod errors; +#[cfg(test)] +mod tests; + +/// Defines Nightshade state transition and validator rotation. +/// TODO: this possibly should be merged with the runtime cargo or at least reconciled on the interfaces. +pub struct NightshadeRuntime { + genesis_config: GenesisConfig, + runtime_config_store: RuntimeConfigStore, + + store: Store, + tries: ShardTries, + trie_viewer: TrieViewer, + pub runtime: Runtime, + epoch_manager: Arc, + migration_data: Arc, + gc_num_epochs_to_keep: u64, +} + +impl NightshadeRuntime { + pub fn from_config( + home_dir: &Path, + store: Store, + config: &UncConfig, + epoch_manager: Arc, + ) -> Arc { + // TODO (#9989): directly use the new state snapshot config once the migration is done. + let mut state_snapshot_type = + config.config.store.state_snapshot_config.state_snapshot_type.clone(); + if config.config.store.state_snapshot_enabled { + state_snapshot_type = StateSnapshotType::EveryEpoch; + } + // TODO (#9989): directly use the new state snapshot config once the migration is done. + let compaction_enabled = config.config.store.state_snapshot_compaction_enabled + || config.config.store.state_snapshot_config.compaction_enabled; + let state_snapshot_config = StateSnapshotConfig { + state_snapshot_type, + home_dir: home_dir.to_path_buf(), + hot_store_path: config.config.store.path.clone().unwrap_or(PathBuf::from("data")), + state_snapshot_subdir: PathBuf::from("state_snapshot"), + compaction_enabled, + }; + Self::new( + store, + &config.genesis.config, + epoch_manager, + config.client_config.trie_viewer_state_size_limit, + config.client_config.max_gas_burnt_view, + None, + config.config.gc.gc_num_epochs_to_keep(), + TrieConfig::from_store_config(&config.config.store), + state_snapshot_config, + ) + } + + fn new( + store: Store, + genesis_config: &GenesisConfig, + epoch_manager: Arc, + trie_viewer_state_size_limit: Option, + max_gas_burnt_view: Option, + runtime_config_store: Option, + gc_num_epochs_to_keep: u64, + trie_config: TrieConfig, + state_snapshot_config: StateSnapshotConfig, + ) -> Arc { + let runtime_config_store = match runtime_config_store { + Some(store) => store, + None => RuntimeConfigStore::for_chain_id(&genesis_config.chain_id), + }; + + let runtime = Runtime::new(); + let trie_viewer = TrieViewer::new(trie_viewer_state_size_limit, max_gas_burnt_view); + let flat_storage_manager = FlatStorageManager::new(store.clone()); + let shard_uids: Vec<_> = genesis_config.shard_layout.shard_uids().collect(); + let tries = ShardTries::new( + store.clone(), + trie_config, + &shard_uids, + flat_storage_manager, + state_snapshot_config, + ); + if let Err(err) = tries.maybe_open_state_snapshot(|prev_block_hash: CryptoHash| { + let epoch_manager = epoch_manager.read(); + let epoch_id = epoch_manager.get_epoch_id(&prev_block_hash)?; + let shard_layout = epoch_manager.get_shard_layout(&epoch_id)?; + Ok(shard_layout.shard_uids().collect()) + }) { + tracing::error!(target: "runtime", ?err, "Failed to check if a state snapshot exists"); + } + + let migration_data = Arc::new(load_migration_data(&genesis_config.chain_id)); + Arc::new(NightshadeRuntime { + genesis_config: genesis_config.clone(), + runtime_config_store, + store, + tries, + runtime, + trie_viewer, + epoch_manager, + migration_data, + gc_num_epochs_to_keep: gc_num_epochs_to_keep.max(MIN_GC_NUM_EPOCHS_TO_KEEP), + }) + } + + pub fn test_with_runtime_config_store( + home_dir: &Path, + store: Store, + genesis_config: &GenesisConfig, + epoch_manager: Arc, + runtime_config_store: RuntimeConfigStore, + state_snapshot_type: StateSnapshotType, + ) -> Arc { + Self::new( + store, + genesis_config, + epoch_manager, + None, + None, + Some(runtime_config_store), + DEFAULT_GC_NUM_EPOCHS_TO_KEEP, + Default::default(), + StateSnapshotConfig { + state_snapshot_type, + home_dir: home_dir.to_path_buf(), + hot_store_path: PathBuf::from("data"), + state_snapshot_subdir: PathBuf::from("state_snapshot"), + compaction_enabled: false, + }, + ) + } + + pub fn test_with_trie_config( + home_dir: &Path, + store: Store, + genesis_config: &GenesisConfig, + epoch_manager: Arc, + trie_config: TrieConfig, + state_snapshot_type: StateSnapshotType, + ) -> Arc { + Self::new( + store, + genesis_config, + epoch_manager, + None, + None, + None, + DEFAULT_GC_NUM_EPOCHS_TO_KEEP, + trie_config, + StateSnapshotConfig { + state_snapshot_type, + home_dir: home_dir.to_path_buf(), + hot_store_path: PathBuf::from("data"), + state_snapshot_subdir: PathBuf::from("state_snapshot"), + compaction_enabled: false, + }, + ) + } + + pub fn test( + home_dir: &Path, + store: Store, + genesis_config: &GenesisConfig, + epoch_manager: Arc, + ) -> Arc { + Self::test_with_runtime_config_store( + home_dir, + store, + genesis_config, + epoch_manager, + RuntimeConfigStore::test(), + StateSnapshotType::ForReshardingOnly, + ) + } + + fn get_shard_uid_from_prev_hash( + &self, + shard_id: ShardId, + prev_hash: &CryptoHash, + ) -> Result { + let epoch_manager = self.epoch_manager.read(); + let epoch_id = + epoch_manager.get_epoch_id_from_prev_block(prev_hash).map_err(Error::from)?; + let shard_version = + epoch_manager.get_shard_layout(&epoch_id).map_err(Error::from)?.version(); + Ok(ShardUId { version: shard_version, shard_id: shard_id as u32 }) + } + + fn get_shard_uid_from_epoch_id( + &self, + shard_id: ShardId, + epoch_id: &EpochId, + ) -> Result { + let epoch_manager = self.epoch_manager.read(); + let shard_version = + epoch_manager.get_shard_layout(epoch_id).map_err(Error::from)?.version(); + Ok(ShardUId { version: shard_version, shard_id: shard_id as u32 }) + } + + fn account_id_to_shard_uid( + &self, + account_id: &AccountId, + epoch_id: &EpochId, + ) -> Result { + let epoch_manager = self.epoch_manager.read(); + let shard_layout = epoch_manager.get_shard_layout(epoch_id).map_err(Error::from)?; + let shard_id = account_id_to_shard_id(account_id, &shard_layout); + Ok(ShardUId::from_shard_id_and_layout(shard_id, &shard_layout)) + } + + /// Processes state update. + fn process_state_update( + &self, + trie: Trie, + chunk: ApplyChunkShardContext, + block: ApplyChunkBlockContext, + receipts: &[Receipt], + transactions: &[SignedTransaction], + state_patch: SandboxStatePatch, + ) -> Result { + let _span = tracing::debug_span!(target: "runtime", "process_state_update").entered(); + let ApplyChunkBlockContext { + height: block_height, + block_hash, + ref prev_block_hash, + block_timestamp, + gas_price, + challenges_result, + random_seed, + } = block; + let ApplyChunkShardContext { + shard_id, + last_validator_power_proposals, + last_validator_frozen_proposals, + gas_limit, + is_new_chunk, + is_first_block_with_chunk_of_version, + } = chunk; + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(prev_block_hash)?; + let validator_accounts_update = { + let epoch_manager = self.epoch_manager.read(); + let shard_layout = epoch_manager.get_shard_layout(&epoch_id)?; + debug!(target: "runtime", + "is next_block_epoch_start {}", + epoch_manager.is_next_block_epoch_start(prev_block_hash).unwrap() + ); + + let mut slashing_info: HashMap<_, _> = challenges_result + .iter() + .filter_map(|s| { + if account_id_to_shard_id(&s.account_id, &shard_layout) == shard_id + && !s.is_double_sign + { + Some((s.account_id.clone(), None)) + } else { + None + } + }) + .collect(); + + if epoch_manager.is_next_block_epoch_start(prev_block_hash)? { + // let (power_info, frozen_info, validator_reward, double_sign_slashing_info) = + // epoch_manager.compute_power_return_info(prev_block_hash)?; + // let power_info = power_info + // .into_iter() + // .filter(|(account_id, _)| { + // account_id_to_shard_id(account_id, &shard_layout) == shard_id + // }) + // .collect(); + // let frozen_info = frozen_info + // .into_iter() + // .filter(|(account_id, _)| { + // account_id_to_shard_id(account_id, &shard_layout) == shard_id + // }) + // .collect(); + // let validator_rewards = validator_reward + // .into_iter() + // .filter(|(account_id, _)| { + // account_id_to_shard_id(account_id, &shard_layout) == shard_id + // }) + // .collect(); + // let last_power_proposals = last_validator_power_proposals + // .filter(|v| account_id_to_shard_id(v.account_id(), &shard_layout) == shard_id) + // .fold(HashMap::new(), |mut acc, v| { + // let (account_id, stake) = v.account_and_power(); + // acc.insert(account_id, stake); + // acc + // }); + // let last_frozen_proposals = last_validator_frozen_proposals + // .filter(|v| account_id_to_shard_id(v.account_id(), &shard_layout) == shard_id) + // .fold(HashMap::new(), |mut acc, v| { + // let (account_id, stake) = v.account_and_frozen(); + // acc.insert(account_id, stake); + // acc + // }); + // let double_sign_slashing_info: HashMap<_, _> = double_sign_slashing_info + // .into_iter() + // .filter(|(account_id, _)| { + // account_id_to_shard_id(account_id, &shard_layout) == shard_id + // }) + // .map(|(account_id, stake)| (account_id, Some(stake))) + // .collect(); + // slashing_info.extend(double_sign_slashing_info); + // Some(ValidatorAccountsUpdate { + // power_info, + // frozen_info, + // validator_rewards, + // last_power_proposals, + // last_frozen_proposals, + // protocol_treasury_account_id: Some( + // self.genesis_config.protocol_treasury_account.clone(), + // ) + // .filter(|account_id| { + // account_id_to_shard_id(account_id, &shard_layout) == shard_id + // }), + // slashing_info, + // }) + // start customized by James Savechives + let (power_info, frozen_info, validator_reward, double_sign_slashing_info) = + epoch_manager.compute_power_return_info_for_block(prev_block_hash)?; + let power_info = power_info + .into_iter() + .filter(|(account_id, _)| { + account_id_to_shard_id(account_id, &shard_layout) == shard_id + }) + .collect(); + let frozen_info = frozen_info + .into_iter() + .filter(|(account_id, _)| { + account_id_to_shard_id(account_id, &shard_layout) == shard_id + }) + .collect(); + let validator_rewards = validator_reward + .into_iter() + .filter(|(account_id, _)| { + account_id_to_shard_id(account_id, &shard_layout) == shard_id + }) + .collect(); + let last_power_proposals = last_validator_power_proposals + .filter(|v| account_id_to_shard_id(v.account_id(), &shard_layout) == shard_id) + .fold(HashMap::new(), |mut acc, v| { + let (account_id, stake) = v.account_and_power(); + acc.insert(account_id, stake); + acc + }); + let last_frozen_proposals = last_validator_frozen_proposals + .filter(|v| account_id_to_shard_id(v.account_id(), &shard_layout) == shard_id) + .fold(HashMap::new(), |mut acc, v| { + let (account_id, stake) = v.account_and_frozen(); + acc.insert(account_id, stake); + acc + }); + let double_sign_slashing_info: HashMap<_, _> = double_sign_slashing_info + .into_iter() + .filter(|(account_id, _)| { + account_id_to_shard_id(account_id, &shard_layout) == shard_id + }) + .map(|(account_id, stake)| (account_id, Some(stake))) + .collect(); + slashing_info.extend(double_sign_slashing_info); + Some(ValidatorAccountsUpdate { + power_info, + frozen_info, + validator_rewards, + last_power_proposals, + last_frozen_proposals, + protocol_treasury_account_id: Some( + self.genesis_config.protocol_treasury_account.clone(), + ) + .filter(|account_id| { + account_id_to_shard_id(account_id, &shard_layout) == shard_id + }), + slashing_info, + }) + // end customized by James Savechives + } else if !challenges_result.is_empty() { + Some(ValidatorAccountsUpdate { + power_info: Default::default(), + frozen_info: Default::default(), + validator_rewards: Default::default(), + last_power_proposals: Default::default(), + last_frozen_proposals: Default::default(), + protocol_treasury_account_id: None, + slashing_info, + }) + } else { + // None + // start customized by James Savechives + let (power_info, frozen_info, validator_reward, double_sign_slashing_info) = + epoch_manager.compute_power_return_info_for_block(prev_block_hash)?; + let power_info = power_info + .into_iter() + .filter(|(account_id, _)| { + account_id_to_shard_id(account_id, &shard_layout) == shard_id + }) + .collect(); + let frozen_info = frozen_info + .into_iter() + .filter(|(account_id, _)| { + account_id_to_shard_id(account_id, &shard_layout) == shard_id + }) + .collect(); + let validator_rewards = validator_reward + .into_iter() + .filter(|(account_id, _)| { + account_id_to_shard_id(account_id, &shard_layout) == shard_id + }) + .collect(); + let last_power_proposals = last_validator_power_proposals + .filter(|v| account_id_to_shard_id(v.account_id(), &shard_layout) == shard_id) + .fold(HashMap::new(), |mut acc, v| { + let (account_id, stake) = v.account_and_power(); + acc.insert(account_id, stake); + acc + }); + let last_frozen_proposals = last_validator_frozen_proposals + .filter(|v| account_id_to_shard_id(v.account_id(), &shard_layout) == shard_id) + .fold(HashMap::new(), |mut acc, v| { + let (account_id, stake) = v.account_and_frozen(); + acc.insert(account_id, stake); + acc + }); + let double_sign_slashing_info: HashMap<_, _> = double_sign_slashing_info + .into_iter() + .filter(|(account_id, _)| { + account_id_to_shard_id(account_id, &shard_layout) == shard_id + }) + .map(|(account_id, stake)| (account_id, Some(stake))) + .collect(); + slashing_info.extend(double_sign_slashing_info); + Some(ValidatorAccountsUpdate { + power_info, + frozen_info, + validator_rewards, + last_power_proposals, + last_frozen_proposals, + protocol_treasury_account_id: Some( + self.genesis_config.protocol_treasury_account.clone(), + ) + .filter(|account_id| { + account_id_to_shard_id(account_id, &shard_layout) == shard_id + }), + slashing_info, + }) + // end customized by James Savechives + } + }; + + let epoch_height = self.epoch_manager.get_epoch_height_from_prev_block(prev_block_hash)?; + let prev_block_epoch_id = self.epoch_manager.get_epoch_id(prev_block_hash)?; + let current_protocol_version = self.epoch_manager.get_epoch_protocol_version(&epoch_id)?; + let prev_block_protocol_version = + self.epoch_manager.get_epoch_protocol_version(&prev_block_epoch_id)?; + let is_first_block_of_version = current_protocol_version != prev_block_protocol_version; + + debug!(target: "runtime", ?epoch_height, ?epoch_id, ?current_protocol_version, ?is_first_block_of_version); + + let apply_state = ApplyState { + block_height, + prev_block_hash: *prev_block_hash, + block_hash, + epoch_id, + epoch_height, + gas_price, + block_timestamp, + gas_limit: Some(gas_limit), + random_seed, + current_protocol_version, + config: self.runtime_config_store.get_config(current_protocol_version).clone(), + cache: Some(Box::new(StoreCompiledContractCache::new(&self.store))), + is_new_chunk, + migration_data: Arc::clone(&self.migration_data), + migration_flags: MigrationFlags { + is_first_block_of_version, + is_first_block_with_chunk_of_version, + }, + }; + + let instant = Instant::now(); + let apply_result = self + .runtime + .apply( + trie, + &validator_accounts_update, + &apply_state, + receipts, + transactions, + self.epoch_manager.as_ref(), + state_patch, + ) + .map_err(|e| match e { + RuntimeError::InvalidTxError(err) => { + tracing::warn!("Invalid tx {:?}", err); + Error::InvalidTransactions + } + // TODO(#2152): process gracefully + RuntimeError::BalanceMismatchError(e) => panic!("{}", e), + // TODO(#2152): process gracefully + RuntimeError::UnexpectedIntegerOverflow => { + panic!("RuntimeError::UnexpectedIntegerOverflow") + } + RuntimeError::StorageError(e) => Error::StorageError(e), + // TODO(#2152): process gracefully + RuntimeError::ReceiptValidationError(e) => panic!("{}", e), + RuntimeError::ValidatorError(e) => e.into(), + })?; + let elapsed = instant.elapsed(); + + let total_gas_burnt = + apply_result.outcomes.iter().map(|tx_result| tx_result.outcome.gas_burnt).sum(); + metrics::APPLY_CHUNK_DELAY + .with_label_values(&[&format_total_gas_burnt(total_gas_burnt)]) + .observe(elapsed.as_secs_f64()); + let shard_label = shard_id.to_string(); + metrics::DELAYED_RECEIPTS_COUNT + .with_label_values(&[&shard_label]) + .set(apply_result.delayed_receipts_count as i64); + if let Some(metrics) = apply_result.metrics { + metrics.report(&shard_label); + } + + let total_balance_burnt = apply_result + .stats + .tx_burnt_amount + .checked_add(apply_result.stats.other_burnt_amount) + .and_then(|result| result.checked_add(apply_result.stats.slashed_burnt_amount)) + .ok_or_else(|| { + Error::Other("Integer overflow during burnt balance summation".to_string()) + })?; + + let shard_uid = self.get_shard_uid_from_prev_hash(shard_id, prev_block_hash)?; + + let result = ApplyChunkResult { + trie_changes: WrappedTrieChanges::new( + self.get_tries(), + shard_uid, + apply_result.trie_changes, + apply_result.state_changes, + block_hash, + apply_state.block_height, + ), + new_root: apply_result.state_root, + outcomes: apply_result.outcomes, + outgoing_receipts: apply_result.outgoing_receipts, + validator_power_proposals: apply_result.validator_power_proposals, + validator_frozen_proposals: apply_result.validator_frozen_proposals, + total_gas_burnt, + total_balance_burnt, + proof: apply_result.proof, + processed_delayed_receipts: apply_result.processed_delayed_receipts, + }; + + Ok(result) + } + + fn precompile_contracts( + &self, + epoch_id: &EpochId, + contract_codes: Vec, + ) -> Result<(), Error> { + let _span = tracing::debug_span!( + target: "runtime", + "precompile_contracts", + num_contracts = contract_codes.len()) + .entered(); + let protocol_version = self.epoch_manager.get_epoch_protocol_version(epoch_id)?; + let runtime_config = self.runtime_config_store.get_config(protocol_version); + let compiled_contract_cache: Option> = + Some(Box::new(StoreCompiledContractCache::new(&self.store))); + // Execute precompile_contract in parallel but prevent it from using more than half of all + // threads so that node will still function normally. + rayon::scope(|scope| { + let (slot_sender, slot_receiver) = std::sync::mpsc::channel(); + // Use up-to half of the threads for the compilation. + let max_threads = std::cmp::max(rayon::current_num_threads() / 2, 1); + for _ in 0..max_threads { + slot_sender.send(()).expect("both sender and receiver are owned here"); + } + for code in contract_codes { + slot_receiver.recv().expect("could not receive a slot to compile contract"); + let contract_cache = compiled_contract_cache.as_deref(); + let slot_sender = slot_sender.clone(); + scope.spawn(move |_| { + precompile_contract(&code, &runtime_config.wasm_config, contract_cache).ok(); + // If this fails, it just means there won't be any more attempts to recv the + // slots + let _ = slot_sender.send(()); + }); + } + }); + Ok(()) + } + + fn get_gc_stop_height_impl(&self, block_hash: &CryptoHash) -> Result { + let epoch_manager = self.epoch_manager.read(); + // an epoch must have a first block. + let epoch_first_block = *epoch_manager.get_block_info(block_hash)?.epoch_first_block(); + let epoch_first_block_info = epoch_manager.get_block_info(&epoch_first_block)?; + // maintain pointers to avoid cloning. + let mut last_block_in_prev_epoch = *epoch_first_block_info.prev_hash(); + let mut epoch_start_height = epoch_first_block_info.height(); + for _ in 0..self.gc_num_epochs_to_keep - 1 { + let epoch_first_block = + *epoch_manager.get_block_info(&last_block_in_prev_epoch)?.epoch_first_block(); + let epoch_first_block_info = epoch_manager.get_block_info(&epoch_first_block)?; + epoch_start_height = epoch_first_block_info.height(); + last_block_in_prev_epoch = *epoch_first_block_info.prev_hash(); + } + + // An archival node with split storage should perform garbage collection + // on the hot storage but not beyond the COLD_HEAD. In order to determine + // if split storage is enabled *and* that the migration to split storage + // is finished we can check the store kind. It's only set to hot after the + // migration is finished. + let kind = self.store.get_db_kind()?; + let cold_head = self.store.get_ser::(DBCol::BlockMisc, COLD_HEAD_KEY)?; + + if let (Some(DbKind::Hot), Some(cold_head)) = (kind, cold_head) { + let cold_head_hash = cold_head.last_block_hash; + let cold_epoch_first_block = + *epoch_manager.get_block_info(&cold_head_hash)?.epoch_first_block(); + let cold_epoch_first_block_info = + epoch_manager.get_block_info(&cold_epoch_first_block)?; + return Ok(std::cmp::min(epoch_start_height, cold_epoch_first_block_info.height())); + } + Ok(epoch_start_height) + } + + fn obtain_state_part_impl( + &self, + shard_id: ShardId, + prev_hash: &CryptoHash, + state_root: &StateRoot, + part_id: PartId, + ) -> Result, Error> { + let _span = tracing::debug_span!( + target: "runtime", + "obtain_state_part", + part_id = part_id.idx, + shard_id, + %prev_hash, + num_parts = part_id.total) + .entered(); + tracing::debug!(target: "state-parts", ?shard_id, ?prev_hash, ?state_root, ?part_id, "obtain_state_part"); + + let epoch_id = self.epoch_manager.get_epoch_id_from_prev_block(prev_hash)?; + let shard_uid = self.get_shard_uid_from_epoch_id(shard_id, &epoch_id)?; + + let trie_with_state = + self.tries.get_trie_with_block_hash_for_shard(shard_uid, *state_root, &prev_hash, true); + let (partial_state, nibbles_begin, nibbles_end) = match trie_with_state + .get_state_part_boundaries(part_id) + { + Ok(res) => res, + Err(err) => { + error!(target: "runtime", ?err, part_id.idx, part_id.total, %prev_hash, %state_root, %shard_id, "Can't get trie nodes for state part boundaries"); + return Err(err.into()); + } + }; + + // TODO: Make it impossible for the snapshot data to be deleted while the snapshot is in use. + let snapshot_trie = self + .tries + .get_trie_with_block_hash_for_shard_from_snapshot(shard_uid, *state_root, &prev_hash) + .map_err(|err| Error::Other(err.to_string()))?; + let state_part = borsh::to_vec(&match snapshot_trie.get_trie_nodes_for_part_with_flat_storage(part_id, partial_state, nibbles_begin, nibbles_end, &trie_with_state) { + Ok(partial_state) => partial_state, + Err(err) => { + error!(target: "runtime", ?err, part_id.idx, part_id.total, %prev_hash, %state_root, %shard_id, "Can't get trie nodes for state part"); + return Err(err.into()); + } + }) + .expect("serializer should not fail"); + + Ok(state_part) + } +} + +fn format_total_gas_burnt(gas: Gas) -> String { + // Rounds up the amount of teragas to hundreds of Tgas. + // For example 123 Tgas gets rounded up to "200". + format!("{:.0}", ((gas as f64) / 1e14).ceil() * 100.0) +} + +impl RuntimeAdapter for NightshadeRuntime { + fn store(&self) -> &Store { + &self.store + } + + fn get_tries(&self) -> ShardTries { + self.tries.clone() + } + + fn get_trie_for_shard( + &self, + shard_id: ShardId, + prev_hash: &CryptoHash, + state_root: StateRoot, + use_flat_storage: bool, + ) -> Result { + let shard_uid = self.get_shard_uid_from_prev_hash(shard_id, prev_hash)?; + if use_flat_storage { + Ok(self + .tries + .get_trie_with_block_hash_for_shard(shard_uid, state_root, prev_hash, false)) + } else { + Ok(self.tries.get_trie_for_shard(shard_uid, state_root)) + } + } + + fn get_view_trie_for_shard( + &self, + shard_id: ShardId, + prev_hash: &CryptoHash, + state_root: StateRoot, + ) -> Result { + let shard_uid = self.get_shard_uid_from_prev_hash(shard_id, prev_hash)?; + Ok(self.tries.get_view_trie_for_shard(shard_uid, state_root)) + } + + fn get_flat_storage_manager(&self) -> FlatStorageManager { + self.tries.get_flat_storage_manager() + } + + fn validate_tx( + &self, + gas_price: Balance, + state_root: Option, + transaction: &SignedTransaction, + verify_signature: bool, + epoch_id: &EpochId, + current_protocol_version: ProtocolVersion, + ) -> Result, Error> { + let runtime_config = self.runtime_config_store.get_config(current_protocol_version); + + if let Some(state_root) = state_root { + let shard_uid = + self.account_id_to_shard_uid(&transaction.transaction.signer_id, epoch_id)?; + let mut state_update = self.tries.new_trie_update(shard_uid, state_root); + + match verify_and_charge_transaction( + runtime_config, + &mut state_update, + gas_price, + transaction, + verify_signature, + // here we do not know which block the transaction will be included + // and therefore skip the check on the nonce upper bound. + None, + current_protocol_version, + ) { + Ok(_) => Ok(None), + Err(RuntimeError::InvalidTxError(err)) => { + debug!(target: "runtime", "Tx {:?} validation failed: {:?}", transaction, err); + Ok(Some(err)) + } + Err(RuntimeError::StorageError(err)) => Err(Error::StorageError(err)), + Err(err) => unreachable!("Unexpected RuntimeError error {:?}", err), + } + } else { + // Doing basic validation without a state root + match validate_transaction( + runtime_config, + gas_price, + transaction, + verify_signature, + current_protocol_version, + ) { + Ok(_) => Ok(None), + Err(RuntimeError::InvalidTxError(err)) => { + debug!(target: "runtime", "Tx {:?} validation failed: {:?}", transaction, err); + Ok(Some(err)) + } + Err(RuntimeError::StorageError(err)) => Err(Error::StorageError(err)), + Err(err) => unreachable!("Unexpected RuntimeError error {:?}", err), + } + } + } + + fn prepare_transactions( + &self, + gas_price: Balance, + gas_limit: Gas, + epoch_id: &EpochId, + shard_id: ShardId, + state_root: StateRoot, + next_block_height: BlockHeight, + pool_iterator: &mut dyn PoolIterator, + chain_validate: &mut dyn FnMut(&SignedTransaction) -> bool, + current_protocol_version: ProtocolVersion, + time_limit: Option, + ) -> Result, Error> { + let start_time = std::time::Instant::now(); + let time_limit_reached = || match time_limit { + Some(limit_duration) => start_time.elapsed() >= limit_duration, + None => false, + }; + let shard_uid = self.get_shard_uid_from_epoch_id(shard_id, epoch_id)?; + let mut state_update = self.tries.new_trie_update(shard_uid, state_root); + + // Total amount of gas burnt for converting transactions towards receipts. + let mut total_gas_burnt = 0; + let mut total_size = 0u64; + // TODO: Update gas limit for transactions + let transactions_gas_limit = gas_limit / 2; + let mut transactions = vec![]; + let mut num_checked_transactions = 0; + + let runtime_config = self.runtime_config_store.get_config(current_protocol_version); + + // To avoid limiting the throughput of the network, we want to include enough receipts to + // saturate the capacity of the chunk even in case when all of these receipts end up using + // the smallest possible amount of gas, which is at least the cost of execution of action + // receipt. + // Currently, the min execution cost is ~100 GGas and the chunk capacity is 1 PGas, giving + // a bound of at most 10000 receipts processed in a chunk. + let delayed_receipts_indices: DelayedReceiptIndices = + unc_store::get(&state_update, &TrieKey::DelayedReceiptIndices)?.unwrap_or_default(); + let min_fee = runtime_config.fees.fee(ActionCosts::new_action_receipt).exec_fee(); + let new_receipt_count_limit = if min_fee > 0 { + // Round up to include at least one receipt. + let max_processed_receipts_in_chunk = (gas_limit + min_fee - 1) / min_fee; + // Allow at most 2 chunks worth of delayed receipts. This way under congestion, + // after processing a single chunk, we will still have at least 1 chunk worth of + // delayed receipts, ensuring the high throughput even if the next chunk producer + // does not include any receipts. + // This buffer size is a trade-off between the max queue size and system efficiency + // under congestion. + let delayed_receipt_count_limit = max_processed_receipts_in_chunk * 2; + delayed_receipt_count_limit.saturating_sub(delayed_receipts_indices.len()) as usize + } else { + usize::MAX + }; + + // In general, we limit the number of transactions via send_fees. + // However, as a second line of defense, we want to limit the byte size + // of transaction as well. Rather than introducing a separate config for + // the limit, we compute it heuristically from the gas limit and the + // cost of roundtripping a byte of data through disk. For today's value + // of parameters, this corresponds to about 13megs worth of + // transactions. from 13mega bytes limit to 4 mega bytes + let size_limit = transactions_gas_limit + / (runtime_config.wasm_config.ext_costs.gas_cost(ExtCosts::storage_write_value_byte) + + runtime_config.wasm_config.ext_costs.gas_cost(ExtCosts::storage_read_value_byte)) / 3; + + while total_gas_burnt < transactions_gas_limit + && total_size < size_limit + && transactions.len() < new_receipt_count_limit + && !time_limit_reached() + { + if let Some(iter) = pool_iterator.next() { + while let Some(tx) = iter.next() { + num_checked_transactions += 1; + // Verifying the transaction is on the same chain and hasn't expired yet. + if !chain_validate(&tx) { + tracing::trace!(target: "runtime", tx=?tx.get_hash(), "discarding transaction that failed chain validation"); + continue; + } + + // Verifying the validity of the transaction based on the current state. + match verify_and_charge_transaction( + runtime_config, + &mut state_update, + gas_price, + &tx, + false, + Some(next_block_height), + current_protocol_version, + ) { + Ok(verification_result) => { + tracing::trace!(target: "runtime", tx=?tx.get_hash(), "including transaction that passed validation"); + state_update.commit(StateChangeCause::NotWritableToDisk); + total_gas_burnt += verification_result.gas_burnt; + total_size += tx.get_size(); + transactions.push(tx); + break; + } + Err(RuntimeError::InvalidTxError(err)) => { + tracing::trace!(target: "runtime", tx=?tx.get_hash(), ?err, "discarding transaction that is invalid"); + state_update.rollback(); + } + Err(RuntimeError::StorageError(err)) => { + tracing::trace!(target: "runtime", tx=?tx.get_hash(), ?err, "discarding transaction due to storage error"); + return Err(Error::StorageError(err)); + } + Err(err) => unreachable!("Unexpected RuntimeError error {:?}", err), + } + } + } else { + break; + } + } + debug!(target: "runtime", "Transaction filtering results {} valid out of {} pulled from the pool", transactions.len(), num_checked_transactions); + metrics::PREPARE_TX_SIZE + .with_label_values(&[&shard_id.to_string()]) + .observe(total_size as f64); + Ok(transactions) + } + + fn get_gc_stop_height(&self, block_hash: &CryptoHash) -> BlockHeight { + let result = self.get_gc_stop_height_impl(block_hash); + match result { + Ok(gc_stop_height) => gc_stop_height, + Err(error) => { + info!(target: "runtime", "Error when getting the gc stop height. This error may naturally occur after the gc_num_epochs_to_keep config is increased. It should disappear as soon as the node builds up all epochs it wants. Error: {}", error); + self.genesis_config.genesis_height + } + } + } + + fn apply_chunk( + &self, + storage_config: RuntimeStorageConfig, + chunk: ApplyChunkShardContext, + block: ApplyChunkBlockContext, + receipts: &[Receipt], + transactions: &[SignedTransaction], + ) -> Result { + let shard_id = chunk.shard_id; + let _timer = + metrics::APPLYING_CHUNKS_TIME.with_label_values(&[&shard_id.to_string()]).start_timer(); + + let mut trie = match storage_config.source { + StorageDataSource::Db => self.get_trie_for_shard( + shard_id, + &block.prev_block_hash, + storage_config.state_root, + storage_config.use_flat_storage, + )?, + StorageDataSource::DbTrieOnly => { + // If there is no flat storage on disk, use trie but simulate costs with enabled + // flat storage by not charging gas for trie nodes. + let mut trie = self.get_trie_for_shard( + shard_id, + &block.prev_block_hash, + storage_config.state_root, + false, + )?; + trie.dont_charge_gas_for_trie_node_access(); + trie + } + StorageDataSource::Recorded(storage) => Trie::from_recorded_storage( + storage, + storage_config.state_root, + storage_config.use_flat_storage, + ), + }; + if storage_config.record_storage { + trie = trie.recording_reads(); + } + + match self.process_state_update( + trie, + chunk, + block, + receipts, + transactions, + storage_config.state_patch, + ) { + Ok(result) => Ok(result), + Err(e) => match e { + Error::StorageError(err) => match &err { + StorageError::FlatStorageBlockNotSupported(_) => Err(err.into()), + _ => panic!("{err}"), + }, + _ => Err(e), + }, + } + } + + fn query( + &self, + shard_uid: ShardUId, + state_root: &StateRoot, + block_height: BlockHeight, + block_timestamp: u64, + prev_block_hash: &CryptoHash, + block_hash: &CryptoHash, + epoch_id: &EpochId, + request: &QueryRequest, + ) -> Result { + match request { + QueryRequest::ViewAccount { account_id } => { + let account = self + .view_account(&shard_uid, *state_root, account_id) + .map_err(|err| { + unc_chain::unc_chain_primitives::error::QueryError::from_view_account_error( + err, + block_height, + *block_hash, + ) + })?; + Ok(QueryResponse { + kind: QueryResponseKind::ViewAccount(account.into()), + block_height, + block_hash: *block_hash, + }) + } + QueryRequest::ViewCode { account_id } => { + let contract_code = self + .view_contract_code(&shard_uid, *state_root, account_id) + .map_err(|err| unc_chain::unc_chain_primitives::error::QueryError::from_view_contract_code_error(err, block_height, *block_hash))?; + Ok(QueryResponse { + kind: QueryResponseKind::ViewCode(contract_code.into()), + block_height, + block_hash: *block_hash, + }) + } + QueryRequest::CallFunction { account_id, method_name, args } => { + let mut logs = vec![]; + let (epoch_height, current_protocol_version) = { + let epoch_manager = self.epoch_manager.read(); + let epoch_info = epoch_manager.get_epoch_info(epoch_id).map_err(|err| { + unc_chain::unc_chain_primitives::error::QueryError::from_epoch_error( + err, + block_height, + *block_hash, + ) + })?; + (epoch_info.epoch_height(), epoch_info.protocol_version()) + }; + + let call_function_result = self + .call_function( + &shard_uid, + *state_root, + block_height, + block_timestamp, + prev_block_hash, + block_hash, + epoch_height, + epoch_id, + account_id, + method_name, + args.as_ref(), + &mut logs, + self.epoch_manager.as_ref(), + current_protocol_version, + ) + .map_err(|err| unc_chain::unc_chain_primitives::error::QueryError::from_call_function_error(err, block_height, *block_hash))?; + Ok(QueryResponse { + kind: QueryResponseKind::CallResult(CallResult { + result: call_function_result, + logs, + }), + block_height, + block_hash: *block_hash, + }) + } + QueryRequest::ViewState { account_id, prefix, include_proof } => { + let view_state_result = self + .view_state( + &shard_uid, + *state_root, + account_id, + prefix.as_ref(), + *include_proof, + ) + .map_err(|err| { + unc_chain::unc_chain_primitives::error::QueryError::from_view_state_error( + err, + block_height, + *block_hash, + ) + })?; + Ok(QueryResponse { + kind: QueryResponseKind::ViewState(view_state_result), + block_height, + block_hash: *block_hash, + }) + } + QueryRequest::ViewAccessKeyList { account_id } => { + let access_key_list = + self.view_access_keys(&shard_uid, *state_root, account_id).map_err(|err| { + unc_chain::unc_chain_primitives::error::QueryError::from_view_access_key_error( + err, + block_height, + *block_hash, + ) + })?; + Ok(QueryResponse { + kind: QueryResponseKind::AccessKeyList( + access_key_list + .into_iter() + .map(|(public_key, access_key)| AccessKeyInfoView { + public_key, + access_key: access_key.into(), + }) + .collect(), + ), + block_height, + block_hash: *block_hash, + }) + } + QueryRequest::ViewAccessKey { account_id, public_key } => { + let access_key = self + .view_access_key(&shard_uid, *state_root, account_id, public_key) + .map_err(|err| { + unc_chain::unc_chain_primitives::error::QueryError::from_view_access_key_error( + err, + block_height, + *block_hash, + ) + })?; + Ok(QueryResponse { + kind: QueryResponseKind::AccessKey(access_key.into()), + block_height, + block_hash: *block_hash, + }) + } + } + } + + // Wrapper to get the metrics. + fn obtain_state_part( + &self, + shard_id: ShardId, + prev_hash: &CryptoHash, + state_root: &StateRoot, + part_id: PartId, + ) -> Result, Error> { + let _span = tracing::debug_span!( + target: "runtime", + "obtain_state_part", + part_id = part_id.idx, + shard_id, + %prev_hash, + ?state_root, + num_parts = part_id.total) + .entered(); + let instant = Instant::now(); + let res = self.obtain_state_part_impl(shard_id, prev_hash, state_root, part_id); + let elapsed = instant.elapsed(); + let is_ok = if res.is_ok() { "ok" } else { "error" }; + metrics::STATE_SYNC_OBTAIN_PART_DELAY + .with_label_values(&[&shard_id.to_string(), is_ok]) + .observe(elapsed.as_secs_f64()); + res + } + + fn validate_state_part(&self, state_root: &StateRoot, part_id: PartId, data: &[u8]) -> bool { + match BorshDeserialize::try_from_slice(data) { + Ok(trie_nodes) => { + match Trie::validate_state_part(state_root, part_id, trie_nodes) { + Ok(_) => true, + // Storage error should not happen + Err(err) => { + tracing::error!(target: "state-parts", ?err, "State part storage error"); + false + } + } + } + // Deserialization error means we've got the data from malicious peer + Err(err) => { + tracing::error!(target: "state-parts", ?err, "State part deserialization error"); + false + } + } + } + + fn apply_update_to_children_states( + &self, + block_hash: &CryptoHash, + block_height: BlockHeight, + state_roots: HashMap, + next_epoch_shard_layout: &ShardLayout, + state_changes_for_resharding: StateChangesForResharding, + ) -> Result, Error> { + let trie_updates = self.tries.apply_state_changes_to_children_states( + &state_roots, + state_changes_for_resharding, + &|account_id| account_id_to_shard_uid(account_id, next_epoch_shard_layout), + )?; + + let mut applied_resharding_results: Vec<_> = vec![]; + for (shard_uid, trie_update) in trie_updates { + let (_, trie_changes, state_changes) = trie_update.finalize()?; + // All state changes that are related to resharding should have StateChangeCause as Resharding + // We do not want to commit the state_changes from resharding as they are already handled while + // processing parent shard + debug_assert!(state_changes.iter().all(|raw_state_changes| raw_state_changes + .changes + .iter() + .all(|state_change| state_change.cause == StateChangeCause::Resharding))); + let new_root = trie_changes.new_root; + let wrapped_trie_changes = WrappedTrieChanges::new( + self.get_tries(), + shard_uid, + trie_changes, + state_changes, + *block_hash, + block_height, + ); + applied_resharding_results.push(ApplyResultForResharding { + shard_uid, + new_root, + trie_changes: wrapped_trie_changes, + }); + } + + Ok(applied_resharding_results) + } + + fn apply_state_part( + &self, + shard_id: ShardId, + state_root: &StateRoot, + part_id: PartId, + data: &[u8], + epoch_id: &EpochId, + ) -> Result<(), Error> { + let _timer = metrics::STATE_SYNC_APPLY_PART_DELAY + .with_label_values(&[&shard_id.to_string()]) + .start_timer(); + + let part = BorshDeserialize::try_from_slice(data) + .expect("Part was already validated earlier, so could never fail here"); + let ApplyStatePartResult { trie_changes, flat_state_delta, contract_codes } = + Trie::apply_state_part(state_root, part_id, part); + let tries = self.get_tries(); + let shard_uid = self.get_shard_uid_from_epoch_id(shard_id, epoch_id)?; + let mut store_update = tries.store_update(); + tries.apply_all(&trie_changes, shard_uid, &mut store_update); + debug!(target: "chain", %shard_id, "Inserting {} values to flat storage", flat_state_delta.len()); + // TODO: `apply_to_flat_state` inserts values with random writes, which can be time consuming. + // Optimize taking into account that flat state values always correspond to a consecutive range of keys. + flat_state_delta.apply_to_flat_state(&mut store_update, shard_uid); + self.precompile_contracts(epoch_id, contract_codes)?; + Ok(store_update.commit()?) + } + + /// `block_hash` is a block whose `prev_state_root` is `state_root` + fn get_state_root_node( + &self, + shard_id: ShardId, + block_hash: &CryptoHash, + state_root: &StateRoot, + ) -> Result { + let epoch_id = self.epoch_manager.get_epoch_id(block_hash)?; + let shard_uid = self.get_shard_uid_from_epoch_id(shard_id, &epoch_id)?; + self.tries + .get_view_trie_for_shard(shard_uid, *state_root) + .retrieve_root_node() + .map_err(Into::into) + } + + fn validate_state_root_node( + &self, + state_root_node: &StateRootNode, + state_root: &StateRoot, + ) -> bool { + if state_root == &Trie::EMPTY_ROOT { + return state_root_node == &StateRootNode::empty(); + } + if hash(&state_root_node.data) != *state_root { + return false; + } + match Trie::get_memory_usage_from_serialized(&state_root_node.data) { + Ok(memory_usage) => memory_usage == state_root_node.memory_usage, + Err(_) => false, // Invalid state_root_node + } + } + + fn get_protocol_config(&self, epoch_id: &EpochId) -> Result { + let protocol_version = self.epoch_manager.get_epoch_protocol_version(epoch_id)?; + let mut genesis_config = self.genesis_config.clone(); + genesis_config.protocol_version = protocol_version; + + let epoch_config = self.epoch_manager.get_epoch_config(epoch_id)?; + genesis_config.epoch_length = epoch_config.epoch_length; + genesis_config.num_block_producer_seats = epoch_config.num_block_producer_seats; + genesis_config.num_block_producer_seats_per_shard = + epoch_config.num_block_producer_seats_per_shard; + genesis_config.avg_hidden_validator_seats_per_shard = + epoch_config.avg_hidden_validator_seats_per_shard; + genesis_config.block_producer_kickout_threshold = + epoch_config.block_producer_kickout_threshold; + genesis_config.chunk_producer_kickout_threshold = + epoch_config.chunk_producer_kickout_threshold; + genesis_config.max_kickout_stake_perc = epoch_config.validator_max_kickout_stake_perc; + genesis_config.online_min_threshold = epoch_config.online_min_threshold; + genesis_config.online_max_threshold = epoch_config.online_max_threshold; + genesis_config.fishermen_threshold = epoch_config.fishermen_threshold; + genesis_config.minimum_stake_divisor = epoch_config.minimum_stake_divisor; + genesis_config.protocol_upgrade_stake_threshold = + epoch_config.protocol_upgrade_stake_threshold; + genesis_config.shard_layout = epoch_config.shard_layout; + genesis_config.num_chunk_only_producer_seats = + epoch_config.validator_selection_config.num_chunk_only_producer_seats; + genesis_config.minimum_validators_per_shard = + epoch_config.validator_selection_config.minimum_validators_per_shard; + genesis_config.minimum_stake_ratio = + epoch_config.validator_selection_config.minimum_stake_ratio; + + let runtime_config = + self.runtime_config_store.get_config(protocol_version).as_ref().clone(); + Ok(ProtocolConfig { genesis_config, runtime_config }) + } + + fn will_shard_layout_change_next_epoch(&self, parent_hash: &CryptoHash) -> Result { + let epoch_manager = self.epoch_manager.read(); + Ok(epoch_manager.will_shard_layout_change(parent_hash)?) + } + + fn load_mem_tries_on_startup(&self, shard_uids: &[ShardUId]) -> Result<(), StorageError> { + self.tries.load_mem_tries_for_enabled_shards(shard_uids) + } +} + +impl node_runtime::adapter::ViewRuntimeAdapter for NightshadeRuntime { + fn view_account( + &self, + shard_uid: &ShardUId, + state_root: MerkleHash, + account_id: &AccountId, + ) -> Result { + let state_update = self.tries.new_trie_update_view(*shard_uid, state_root); + self.trie_viewer.view_account(&state_update, account_id) + } + + fn view_contract_code( + &self, + shard_uid: &ShardUId, + state_root: MerkleHash, + account_id: &AccountId, + ) -> Result { + let state_update = self.tries.new_trie_update_view(*shard_uid, state_root); + self.trie_viewer.view_contract_code(&state_update, account_id) + } + + fn call_function( + &self, + shard_uid: &ShardUId, + state_root: MerkleHash, + height: BlockHeight, + block_timestamp: u64, + prev_block_hash: &CryptoHash, + block_hash: &CryptoHash, + epoch_height: EpochHeight, + epoch_id: &EpochId, + contract_id: &AccountId, + method_name: &str, + args: &[u8], + logs: &mut Vec, + epoch_info_provider: &dyn EpochInfoProvider, + current_protocol_version: ProtocolVersion, + ) -> Result, node_runtime::state_viewer::errors::CallFunctionError> { + let state_update = self.tries.new_trie_update_view(*shard_uid, state_root); + let view_state = ViewApplyState { + block_height: height, + prev_block_hash: *prev_block_hash, + block_hash: *block_hash, + epoch_id: epoch_id.clone(), + epoch_height, + block_timestamp, + current_protocol_version, + cache: Some(Box::new(StoreCompiledContractCache::new(&self.tries.get_store()))), + }; + self.trie_viewer.call_function( + state_update, + view_state, + contract_id, + method_name, + args, + logs, + epoch_info_provider, + ) + } + + fn view_access_key( + &self, + shard_uid: &ShardUId, + state_root: MerkleHash, + account_id: &AccountId, + public_key: &PublicKey, + ) -> Result { + let state_update = self.tries.new_trie_update_view(*shard_uid, state_root); + self.trie_viewer.view_access_key(&state_update, account_id, public_key) + } + + fn view_access_keys( + &self, + shard_uid: &ShardUId, + state_root: MerkleHash, + account_id: &AccountId, + ) -> Result, node_runtime::state_viewer::errors::ViewAccessKeyError> + { + let state_update = self.tries.new_trie_update_view(*shard_uid, state_root); + self.trie_viewer.view_access_keys(&state_update, account_id) + } + + fn view_state( + &self, + shard_uid: &ShardUId, + state_root: MerkleHash, + account_id: &AccountId, + prefix: &[u8], + include_proof: bool, + ) -> Result { + let state_update = self.tries.new_trie_update_view(*shard_uid, state_root); + self.trie_viewer.view_state(&state_update, account_id, prefix, include_proof) + } +} diff --git a/framework/src/runtime/tests.rs b/framework/src/runtime/tests.rs new file mode 100644 index 000000000..0deae537e --- /dev/null +++ b/framework/src/runtime/tests.rs @@ -0,0 +1,1446 @@ +use std::collections::BTreeSet; + +use unc_chain::types::RuntimeStorageConfig; +use unc_chain::{Chain, ChainGenesis}; +use unc_epoch_manager::types::BlockHeaderInfo; +use unc_epoch_manager::EpochManager; +use unc_primitives::test_utils::create_test_signer; +use unc_primitives::types::validator_stake::{ValidatorStake, ValidatorStakeIter}; +use unc_store::flat::{FlatStateChanges, FlatStateDelta, FlatStateDeltaMetadata}; +use unc_store::genesis::initialize_genesis_state; +use num_rational::Ratio; + +use crate::config::{GenesisExt, TESTING_INIT_BALANCE, TESTING_INIT_STAKE}; +use unc_chain_configs::{Genesis, DEFAULT_GC_NUM_EPOCHS_TO_KEEP}; +use unc_crypto::{InMemorySigner, KeyType, Signer}; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::block::Tip; +use unc_primitives::challenge::{ChallengesResult, SlashedValidator}; +use unc_primitives::transaction::{Action, DeleteAccountAction, StakeAction, TransferAction}; +use unc_primitives::types::{ + BlockHeightDelta, Nonce, ValidatorId, ValidatorInfoIdentifier, ValidatorKickoutReason, +}; +use unc_primitives::validator_signer::ValidatorSigner; +use unc_primitives::views::{ + AccountView, CurrentEpochValidatorInfo, EpochValidatorInfo, NextEpochValidatorInfo, + ValidatorKickoutView, +}; +use unc_store::{get_genesis_state_roots, NodeStorage}; + +use super::*; + +use unc_primitives::account::id::AccountIdRef; +use unc_primitives::trie_key::TrieKey; +use primitive_types::U256; + +fn stake( + nonce: Nonce, + signer: &dyn Signer, + sender: &dyn ValidatorSigner, + stake: Balance, +) -> SignedTransaction { + SignedTransaction::from_actions( + nonce, + sender.validator_id().clone(), + sender.validator_id().clone(), + &*signer, + vec![Action::Stake(Box::new(StakeAction { stake, public_key: sender.public_key() }))], + // runtime does not validate block history + CryptoHash::default(), + ) +} + +impl NightshadeRuntime { + fn update( + &self, + state_root: &StateRoot, + shard_id: ShardId, + height: BlockHeight, + block_timestamp: u64, + prev_block_hash: &CryptoHash, + block_hash: &CryptoHash, + receipts: &[Receipt], + transactions: &[SignedTransaction], + last_validator_proposals: ValidatorStakeIter, + gas_price: Balance, + gas_limit: Gas, + challenges_result: &ChallengesResult, + ) -> (StateRoot, Vec, Vec) { + let mut result = self + .apply_chunk( + RuntimeStorageConfig::new(*state_root, true), + ApplyChunkShardContext { + shard_id, + last_validator_proposals, + gas_limit, + is_new_chunk: true, + is_first_block_with_chunk_of_version: false, + }, + ApplyChunkBlockContext { + height, + block_hash: *block_hash, + prev_block_hash: *prev_block_hash, + block_timestamp, + gas_price, + challenges_result: challenges_result.clone(), + random_seed: CryptoHash::default(), + }, + receipts, + transactions, + ) + .unwrap(); + let mut store_update = self.store.store_update(); + let flat_state_changes = + FlatStateChanges::from_state_changes(&result.trie_changes.state_changes()); + result.trie_changes.insertions_into(&mut store_update); + result.trie_changes.state_changes_into(&mut store_update); + + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, &EpochId::default()).unwrap(); + if let Some(flat_storage) = + self.get_flat_storage_manager().get_flat_storage_for_shard(shard_uid) + { + let delta = FlatStateDelta { + changes: flat_state_changes, + metadata: FlatStateDeltaMetadata { + block: unc_store::flat::BlockInfo { + hash: *block_hash, + height, + prev_hash: *prev_block_hash, + }, + prev_block_with_changes: None, + }, + }; + let new_store_update = flat_storage.add_delta(delta).unwrap(); + store_update.merge(new_store_update); + } + store_update.commit().unwrap(); + + (result.new_root, result.validator_proposals, result.outgoing_receipts) + } +} + +/// Environment to test runtime behaviour separate from Chain. +/// Runtime operates in a mock chain where i-th block is attached to (i-1)-th one, has height `i` and hash +/// `hash([i])`. +struct TestEnv { + pub epoch_manager: Arc, + pub runtime: Arc, + pub head: Tip, + state_roots: Vec, + pub last_receipts: HashMap>, + pub last_shard_proposals: HashMap>, + pub last_proposals: Vec, + time: u64, +} + +impl TestEnv { + pub fn new( + validators: Vec>, + epoch_length: BlockHeightDelta, + has_reward: bool, + ) -> Self { + Self::new_with_minimum_stake_divisor(validators, epoch_length, has_reward, None) + } + + fn new_with_minimum_stake_divisor( + validators: Vec>, + epoch_length: BlockHeightDelta, + has_reward: bool, + minimum_stake_divisor: Option, + ) -> Self { + let (dir, opener) = NodeStorage::test_opener(); + let store = opener.open().unwrap().get_hot_store(); + let all_validators = validators.iter().fold(BTreeSet::new(), |acc, x| { + acc.union(&x.iter().cloned().collect()).cloned().collect() + }); + let validators_len = all_validators.len() as ValidatorId; + let mut genesis = Genesis::test_sharded_new_version( + all_validators.into_iter().collect(), + validators_len, + validators.iter().map(|x| x.len() as ValidatorId).collect(), + ); + // No fees mode. + genesis.config.epoch_length = epoch_length; + genesis.config.chunk_producer_kickout_threshold = + genesis.config.block_producer_kickout_threshold; + if !has_reward { + genesis.config.max_inflation_rate = Ratio::from_integer(0); + } + if let Some(minimum_stake_divisor) = minimum_stake_divisor { + genesis.config.minimum_stake_divisor = minimum_stake_divisor; + } + let genesis_total_supply = genesis.config.total_supply; + let genesis_protocol_version = genesis.config.protocol_version; + + initialize_genesis_state(store.clone(), &genesis, Some(dir.path())); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = NightshadeRuntime::new( + store.clone(), + &genesis.config, + epoch_manager.clone(), + None, + None, + Some(RuntimeConfigStore::free()), + DEFAULT_GC_NUM_EPOCHS_TO_KEEP, + Default::default(), + StateSnapshotConfig { + state_snapshot_type: StateSnapshotType::EveryEpoch, + home_dir: PathBuf::from(dir.path()), + hot_store_path: PathBuf::from("data"), + state_snapshot_subdir: PathBuf::from("state_snapshot"), + compaction_enabled: false, + }, + ); + let state_roots = get_genesis_state_roots(&store).unwrap().unwrap(); + let genesis_hash = hash(&[0]); + + // Create flat storage. Naturally it happens on Chain creation, but here we test only Runtime behaviour + // and use a mock chain, so we need to initialize flat storage manually. + let flat_storage_manager = runtime.get_flat_storage_manager(); + for shard_uid in epoch_manager.get_shard_layout(&EpochId::default()).unwrap().shard_uids() { + let mut store_update = store.store_update(); + flat_storage_manager.set_flat_storage_for_genesis( + &mut store_update, + shard_uid, + &genesis_hash, + 0, + ); + store_update.commit().unwrap(); + assert!(matches!( + flat_storage_manager.get_flat_storage_status(shard_uid), + unc_store::flat::FlatStorageStatus::Ready(_) + )); + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + } + + epoch_manager + .add_validator_proposals(BlockHeaderInfo { + prev_hash: CryptoHash::default(), + hash: genesis_hash, + random_value: [0; 32].as_ref().try_into().unwrap(), + height: 0, + last_finalized_height: 0, + last_finalized_block_hash: CryptoHash::default(), + proposals: vec![], + slashed_validators: vec![], + chunk_mask: vec![], + total_supply: genesis_total_supply, + latest_protocol_version: genesis_protocol_version, + timestamp_nanosec: 0, + }) + .unwrap() + .commit() + .unwrap(); + Self { + epoch_manager, + runtime, + head: Tip { + last_block_hash: genesis_hash, + prev_block_hash: CryptoHash::default(), + height: 0, + epoch_id: EpochId::default(), + next_epoch_id: Default::default(), + }, + state_roots, + last_receipts: HashMap::default(), + last_proposals: vec![], + last_shard_proposals: HashMap::default(), + time: 0, + } + } + + pub fn step( + &mut self, + transactions: Vec>, + chunk_mask: Vec, + challenges_result: ChallengesResult, + ) { + let new_hash = hash(&[(self.head.height + 1) as u8]); + let shard_ids = self.epoch_manager.shard_ids(&self.head.epoch_id).unwrap(); + assert_eq!(transactions.len(), shard_ids.len()); + assert_eq!(chunk_mask.len(), shard_ids.len()); + let mut all_proposals = vec![]; + let mut all_receipts = vec![]; + for shard_id in shard_ids { + let (state_root, proposals, receipts) = self.runtime.update( + &self.state_roots[shard_id as usize], + shard_id, + self.head.height + 1, + 0, + &self.head.last_block_hash, + &new_hash, + self.last_receipts.get(&shard_id).map_or(&[], |v| v.as_slice()), + &transactions[shard_id as usize], + ValidatorStakeIter::new( + self.last_shard_proposals.get(&shard_id).unwrap_or(&vec![]), + ), + self.runtime.genesis_config.min_gas_price, + u64::max_value(), + &challenges_result, + ); + self.state_roots[shard_id as usize] = state_root; + all_receipts.extend(receipts); + all_proposals.append(&mut proposals.clone()); + self.last_shard_proposals.insert(shard_id, proposals); + } + self.epoch_manager + .add_validator_proposals(BlockHeaderInfo { + prev_hash: self.head.last_block_hash, + hash: new_hash, + random_value: [0; 32].as_ref().try_into().unwrap(), + height: self.head.height + 1, + last_finalized_height: self.head.height.saturating_sub(1), + last_finalized_block_hash: self.head.last_block_hash, + proposals: self.last_proposals.clone(), + slashed_validators: challenges_result, + chunk_mask, + total_supply: self.runtime.genesis_config.total_supply, + latest_protocol_version: self.runtime.genesis_config.protocol_version, + timestamp_nanosec: self.time + 10u64.pow(9), + }) + .unwrap() + .commit() + .unwrap(); + let shard_layout = self.epoch_manager.get_shard_layout_from_prev_block(&new_hash).unwrap(); + let mut new_receipts = HashMap::<_, Vec>::new(); + for receipt in all_receipts { + let shard_id = account_id_to_shard_id(&receipt.receiver_id, &shard_layout); + new_receipts.entry(shard_id).or_default().push(receipt); + } + self.last_receipts = new_receipts; + self.last_proposals = all_proposals; + self.time += 10u64.pow(9); + + self.head = Tip { + last_block_hash: new_hash, + prev_block_hash: self.head.last_block_hash, + height: self.head.height + 1, + epoch_id: self + .epoch_manager + .get_epoch_id_from_prev_block(&self.head.last_block_hash) + .unwrap(), + next_epoch_id: self + .epoch_manager + .get_next_epoch_id_from_prev_block(&self.head.last_block_hash) + .unwrap(), + }; + } + + /// Step when there is only one shard + pub fn step_default(&mut self, transactions: Vec) { + self.step(vec![transactions], vec![true], ChallengesResult::default()); + } + + pub fn view_account(&self, account_id: &AccountId) -> AccountView { + let shard_id = + self.epoch_manager.account_id_to_shard_id(account_id, &self.head.epoch_id).unwrap(); + let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, &self.head.epoch_id).unwrap(); + self.runtime + .view_account(&shard_uid, self.state_roots[shard_id as usize], account_id) + .unwrap() + .into() + } + + /// Compute per epoch per validator reward and per epoch protocol treasury reward + pub fn compute_reward(&self, num_validators: usize, epoch_duration: u64) -> (Balance, Balance) { + let num_seconds_per_year = 60 * 60 * 24 * 365; + let num_ns_in_second = 1_000_000_000; + let per_epoch_total_reward = + (U256::from(*self.runtime.genesis_config.max_inflation_rate.numer() as u64) + * U256::from(self.runtime.genesis_config.total_supply) + * U256::from(epoch_duration) + / (U256::from(num_seconds_per_year) + * U256::from(*self.runtime.genesis_config.max_inflation_rate.denom() as u128) + * U256::from(num_ns_in_second))) + .as_u128(); + let per_epoch_protocol_treasury = per_epoch_total_reward + * *self.runtime.genesis_config.protocol_reward_rate.numer() as u128 + / *self.runtime.genesis_config.protocol_reward_rate.denom() as u128; + let per_epoch_per_validator_reward = + (per_epoch_total_reward - per_epoch_protocol_treasury) / num_validators as u128; + (per_epoch_per_validator_reward, per_epoch_protocol_treasury) + } +} + +/// Start with 2 validators with default stake X. +/// 1. Validator 0 stakes 2 * X +/// 2. Validator 0 creates new account Validator 2 with 3 * X in balance +/// 3. Validator 2 stakes 2 * X +/// 4. Validator 1 gets unstaked because not enough stake. +/// 5. At the end Validator 0 and 2 with 2 * X are validators. Validator 1 has stake returned to balance. +#[test] +fn test_validator_rotation() { + init_test_logger(); + let num_nodes = 2; + let validators = (0..num_nodes) + .map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()) + .collect::>(); + let mut env = TestEnv::new(vec![validators.clone()], 2, false); + let block_producers: Vec<_> = + validators.iter().map(|id| create_test_signer(id.as_str())).collect(); + let signer = + InMemorySigner::from_seed(validators[0].clone(), KeyType::ED25519, validators[0].as_ref()); + // test1 doubles stake and the new account stakes the same, so test2 will be kicked out.` + let staking_transaction = stake(1, &signer, &block_producers[0], TESTING_INIT_STAKE * 2); + let new_account = AccountId::try_from(format!("test{}", num_nodes + 1)).unwrap(); + let new_validator = create_test_signer(new_account.as_str()); + let new_signer = + InMemorySigner::from_seed(new_account.clone(), KeyType::ED25519, new_account.as_ref()); + let create_account_transaction = SignedTransaction::create_account( + 2, + block_producers[0].validator_id().clone(), + new_account, + TESTING_INIT_STAKE * 3, + new_signer.public_key(), + &signer, + CryptoHash::default(), + ); + let test2_stake_amount = 3600 * crate::UNC_BASE; + let transactions = { + // With the new validator selection algorithm, test2 needs to have less stake to + // become a fisherman. + let signer = InMemorySigner::from_seed( + validators[1].clone(), + KeyType::ED25519, + validators[1].as_ref(), + ); + vec![ + staking_transaction, + create_account_transaction, + stake(1, &signer, &block_producers[1], test2_stake_amount), + ] + }; + env.step_default(transactions); + env.step_default(vec![]); + let account = env.view_account(block_producers[0].validator_id()); + assert_eq!(account.locked, 2 * TESTING_INIT_STAKE); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE * 5); + + let stake_transaction = + stake(env.head.height * 1_000_000, &new_signer, &new_validator, TESTING_INIT_STAKE * 2); + env.step_default(vec![stake_transaction]); + env.step_default(vec![]); + + // Roll steps for 3 epochs to pass. + for _ in 5..=9 { + env.step_default(vec![]); + } + + let epoch_id = + env.epoch_manager.get_epoch_id_from_prev_block(&env.head.last_block_hash).unwrap(); + assert_eq!( + env.epoch_manager + .get_epoch_block_producers_ordered(&epoch_id, &env.head.last_block_hash) + .unwrap() + .iter() + .map(|x| (x.0.account_id().clone(), x.1)) + .collect::>(), + vec![("test3".parse().unwrap(), false), ("test1".parse().unwrap(), false)] + .into_iter() + .collect::>() + ); + + let test1_acc = env.view_account(&"test1".parse().unwrap()); + // Staked 2 * X, sent 3 * X to test3. + assert_eq!( + (test1_acc.amount, test1_acc.locked), + (TESTING_INIT_BALANCE - 5 * TESTING_INIT_STAKE, 2 * TESTING_INIT_STAKE) + ); + let test2_acc = env.view_account(&"test2".parse().unwrap()); + // Become fishermen instead + assert_eq!( + (test2_acc.amount, test2_acc.locked), + (TESTING_INIT_BALANCE - test2_stake_amount, test2_stake_amount) + ); + let test3_acc = env.view_account(&"test3".parse().unwrap()); + // Got 3 * X, staking 2 * X of them. + assert_eq!((test3_acc.amount, test3_acc.locked), (TESTING_INIT_STAKE, 2 * TESTING_INIT_STAKE)); +} + +/// One validator tries to decrease their stake in epoch T. Make sure that the stake return happens in epoch T+3. +#[test] +fn test_validator_stake_change() { + let num_nodes = 2; + let validators = (0..num_nodes) + .map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()) + .collect::>(); + let mut env = TestEnv::new(vec![validators.clone()], 2, false); + let block_producers: Vec<_> = + validators.iter().map(|id| create_test_signer(id.as_str())).collect(); + let signer = + InMemorySigner::from_seed(validators[0].clone(), KeyType::ED25519, validators[0].as_ref()); + + let desired_stake = 2 * TESTING_INIT_STAKE / 3; + let staking_transaction = stake(1, &signer, &block_producers[0], desired_stake); + env.step_default(vec![staking_transaction]); + let account = env.view_account(block_producers[0].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + assert_eq!(account.locked, TESTING_INIT_STAKE); + for _ in 2..=4 { + env.step_default(vec![]); + } + + let account = env.view_account(block_producers[0].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + assert_eq!(account.locked, TESTING_INIT_STAKE); + + for _ in 5..=7 { + env.step_default(vec![]); + } + + let account = env.view_account(block_producers[0].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - desired_stake); + assert_eq!(account.locked, desired_stake); +} + +#[test] +fn test_validator_stake_change_multiple_times() { + init_test_logger(); + let num_nodes = 4; + let validators = (0..num_nodes) + .map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()) + .collect::>(); + let mut env = TestEnv::new(vec![validators.clone()], 4, false); + let block_producers: Vec<_> = + validators.iter().map(|id| create_test_signer(id.as_str())).collect(); + let signers: Vec<_> = validators + .iter() + .map(|id| InMemorySigner::from_seed(id.clone(), KeyType::ED25519, id.as_ref())) + .collect(); + + let staking_transaction = stake(1, &signers[0], &block_producers[0], TESTING_INIT_STAKE - 1); + let staking_transaction1 = stake(2, &signers[0], &block_producers[0], TESTING_INIT_STAKE - 2); + let staking_transaction2 = stake(1, &signers[1], &block_producers[1], TESTING_INIT_STAKE + 1); + env.step_default(vec![staking_transaction, staking_transaction1, staking_transaction2]); + let account = env.view_account(block_producers[0].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + assert_eq!(account.locked, TESTING_INIT_STAKE); + + let staking_transaction = stake(3, &signers[0], &block_producers[0], TESTING_INIT_STAKE + 1); + let staking_transaction1 = stake(2, &signers[1], &block_producers[1], TESTING_INIT_STAKE + 2); + let staking_transaction2 = stake(3, &signers[1], &block_producers[1], TESTING_INIT_STAKE - 1); + let staking_transaction3 = stake(1, &signers[3], &block_producers[3], TESTING_INIT_STAKE - 1); + env.step_default(vec![ + staking_transaction, + staking_transaction1, + staking_transaction2, + staking_transaction3, + ]); + + for _ in 3..=8 { + env.step_default(vec![]); + } + + let account = env.view_account(block_producers[0].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE - 1); + assert_eq!(account.locked, TESTING_INIT_STAKE + 1); + + let account = env.view_account(block_producers[1].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + assert_eq!(account.locked, TESTING_INIT_STAKE); + + let account = env.view_account(block_producers[2].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + assert_eq!(account.locked, TESTING_INIT_STAKE); + + let account = env.view_account(block_producers[3].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + assert_eq!(account.locked, TESTING_INIT_STAKE); + + for _ in 9..=12 { + env.step_default(vec![]); + } + + let account = env.view_account(block_producers[0].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE - 1); + assert_eq!(account.locked, TESTING_INIT_STAKE + 1); + + let account = env.view_account(block_producers[1].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + assert_eq!(account.locked, TESTING_INIT_STAKE); + + let account = env.view_account(block_producers[2].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + assert_eq!(account.locked, TESTING_INIT_STAKE); + + let account = env.view_account(block_producers[3].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + assert_eq!(account.locked, TESTING_INIT_STAKE); + + for _ in 13..=16 { + env.step_default(vec![]); + } + + let account = env.view_account(block_producers[0].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE - 1); + assert_eq!(account.locked, TESTING_INIT_STAKE + 1); + + let account = env.view_account(block_producers[1].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE + 1); + assert_eq!(account.locked, TESTING_INIT_STAKE - 1); + + let account = env.view_account(block_producers[2].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + assert_eq!(account.locked, TESTING_INIT_STAKE); + + let account = env.view_account(block_producers[3].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE + 1); + assert_eq!(account.locked, TESTING_INIT_STAKE - 1); +} + +#[test] +fn test_stake_in_last_block_of_an_epoch() { + init_test_logger(); + let num_nodes = 4; + let validators = (0..num_nodes) + .map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()) + .collect::>(); + let mut env = TestEnv::new(vec![validators.clone()], 5, false); + let block_producers: Vec<_> = + validators.iter().map(|id| create_test_signer(id.as_str())).collect(); + let signers: Vec<_> = validators + .iter() + .map(|id| InMemorySigner::from_seed(id.clone(), KeyType::ED25519, id.as_ref())) + .collect(); + let staking_transaction = + stake(1, &signers[0], &block_producers[0], TESTING_INIT_STAKE + TESTING_INIT_STAKE / 6); + env.step_default(vec![staking_transaction]); + for _ in 2..10 { + env.step_default(vec![]); + } + let staking_transaction = + stake(2, &signers[0], &block_producers[0], TESTING_INIT_STAKE + TESTING_INIT_STAKE / 2); + env.step_default(vec![staking_transaction]); + env.step_default(vec![]); + let staking_transaction = stake(3, &signers[0], &block_producers[0], TESTING_INIT_STAKE); + env.step_default(vec![staking_transaction]); + for _ in 13..=16 { + env.step_default(vec![]); + } + let account = env.view_account(block_producers[0].validator_id()); + let return_stake = (TESTING_INIT_STAKE + TESTING_INIT_STAKE / 2) + - (TESTING_INIT_STAKE + TESTING_INIT_STAKE / 6); + assert_eq!( + account.amount, + TESTING_INIT_BALANCE - (TESTING_INIT_STAKE + TESTING_INIT_STAKE / 2) + return_stake + ); + assert_eq!(account.locked, TESTING_INIT_STAKE + TESTING_INIT_STAKE / 2 - return_stake); +} + +#[test] +fn test_verify_validator_signature() { + let validators = + (0..2).map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()).collect::>(); + let env = TestEnv::new(vec![validators.clone()], 2, true); + let data = [0; 32]; + let signer = + InMemorySigner::from_seed(validators[0].clone(), KeyType::ED25519, validators[0].as_ref()); + let signature = signer.sign(&data); + assert!(env + .epoch_manager + .verify_validator_signature( + &env.head.epoch_id, + &env.head.last_block_hash, + &validators[0], + &data, + &signature + ) + .unwrap()); +} + +// TODO (#7327): enable test when flat storage will support state sync. +#[ignore] +#[test] +fn test_state_sync() { + init_test_logger(); + let num_nodes = 2; + let validators = (0..num_nodes) + .map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()) + .collect::>(); + let mut env = TestEnv::new(vec![validators.clone()], 2, false); + let block_producers: Vec<_> = + validators.iter().map(|id| create_test_signer(id.as_str())).collect(); + let signer = + InMemorySigner::from_seed(validators[0].clone(), KeyType::ED25519, validators[0].as_ref()); + let staking_transaction = stake(1, &signer, &block_producers[0], TESTING_INIT_STAKE + 1); + env.step_default(vec![staking_transaction]); + env.step_default(vec![]); + let block_hash = hash(&[env.head.height as u8]); + let state_part = env + .runtime + .obtain_state_part(0, &block_hash, &env.state_roots[0], PartId::new(0, 1)) + .unwrap(); + let root_node = env.runtime.get_state_root_node(0, &block_hash, &env.state_roots[0]).unwrap(); + let mut new_env = TestEnv::new(vec![validators], 2, false); + for i in 1..=2 { + let prev_hash = hash(&[new_env.head.height as u8]); + let cur_hash = hash(&[(new_env.head.height + 1) as u8]); + let proposals = if i == 1 { + vec![ValidatorStake::new( + block_producers[0].validator_id().clone(), + block_producers[0].public_key(), + TESTING_INIT_STAKE + 1, + )] + } else { + vec![] + }; + new_env + .epoch_manager + .add_validator_proposals(BlockHeaderInfo { + prev_hash, + hash: cur_hash, + random_value: [0; 32].as_ref().try_into().unwrap(), + height: i, + last_finalized_height: i.saturating_sub(2), + last_finalized_block_hash: prev_hash, + proposals: new_env.last_proposals, + slashed_validators: vec![], + chunk_mask: vec![true], + total_supply: new_env.runtime.genesis_config.total_supply, + latest_protocol_version: new_env.runtime.genesis_config.protocol_version, + timestamp_nanosec: new_env.time, + }) + .unwrap() + .commit() + .unwrap(); + new_env.head.height = i; + new_env.head.last_block_hash = cur_hash; + new_env.head.prev_block_hash = prev_hash; + new_env.last_proposals = proposals; + new_env.time += 10u64.pow(9); + } + assert!(new_env.runtime.validate_state_root_node(&root_node, &env.state_roots[0])); + let mut root_node_wrong = root_node; + root_node_wrong.memory_usage += 1; + assert!(!new_env.runtime.validate_state_root_node(&root_node_wrong, &env.state_roots[0])); + root_node_wrong.data = std::sync::Arc::new([123]); + assert!(!new_env.runtime.validate_state_root_node(&root_node_wrong, &env.state_roots[0])); + assert!(!new_env.runtime.validate_state_part( + &Trie::EMPTY_ROOT, + PartId::new(0, 1), + &state_part + )); + new_env.runtime.validate_state_part(&env.state_roots[0], PartId::new(0, 1), &state_part); + let epoch_id = &new_env.head.epoch_id; + new_env + .runtime + .apply_state_part(0, &env.state_roots[0], PartId::new(0, 1), &state_part, epoch_id) + .unwrap(); + new_env.state_roots[0] = env.state_roots[0]; + for _ in 3..=5 { + new_env.step_default(vec![]); + } + + let account = new_env.view_account(block_producers[0].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE - 1); + assert_eq!(account.locked, TESTING_INIT_STAKE + 1); + + let account = new_env.view_account(block_producers[1].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + assert_eq!(account.locked, TESTING_INIT_STAKE); +} + +#[test] +fn test_get_validator_info() { + let num_nodes = 2; + let validators = (0..num_nodes) + .map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()) + .collect::>(); + let mut env = TestEnv::new(vec![validators.clone()], 2, false); + let block_producers: Vec<_> = + validators.iter().map(|id| create_test_signer(id.as_str())).collect(); + let signer = + InMemorySigner::from_seed(validators[0].clone(), KeyType::ED25519, validators[0].as_ref()); + let staking_transaction = stake(1, &signer, &block_producers[0], 0); + let mut expected_blocks = [0, 0]; + let mut expected_chunks = [0, 0]; + let update_validator_stats = + |env: &mut TestEnv, expected_blocks: &mut [u64], expected_chunks: &mut [u64]| { + let epoch_id = env.head.epoch_id.clone(); + let height = env.head.height; + let em = env.runtime.epoch_manager.read(); + let bp = em.get_block_producer_info(&epoch_id, height).unwrap(); + let cp = em.get_chunk_producer_info(&epoch_id, height, 0).unwrap(); + + if bp.account_id() == "test1" { + expected_blocks[0] += 1; + } else { + expected_blocks[1] += 1; + } + + if cp.account_id() == "test1" { + expected_chunks[0] += 1; + } else { + expected_chunks[1] += 1; + } + }; + env.step_default(vec![staking_transaction]); + update_validator_stats(&mut env, &mut expected_blocks, &mut expected_chunks); + assert!(env + .epoch_manager + .get_validator_info(ValidatorInfoIdentifier::EpochId(env.head.epoch_id.clone())) + .is_err()); + env.step_default(vec![]); + update_validator_stats(&mut env, &mut expected_blocks, &mut expected_chunks); + let mut current_epoch_validator_info = vec![ + CurrentEpochValidatorInfo { + account_id: "test1".parse().unwrap(), + public_key: block_producers[0].public_key(), + is_slashed: false, + stake: TESTING_INIT_STAKE, + shards: vec![0], + num_produced_blocks: expected_blocks[0], + num_expected_blocks: expected_blocks[0], + num_produced_chunks: expected_chunks[0], + num_expected_chunks: expected_chunks[0], + num_produced_chunks_per_shard: vec![expected_chunks[0]], + num_expected_chunks_per_shard: vec![expected_chunks[0]], + }, + CurrentEpochValidatorInfo { + account_id: "test2".parse().unwrap(), + public_key: block_producers[1].public_key(), + is_slashed: false, + stake: TESTING_INIT_STAKE, + shards: vec![0], + num_produced_blocks: expected_blocks[1], + num_expected_blocks: expected_blocks[1], + num_produced_chunks: expected_chunks[1], + num_expected_chunks: expected_chunks[1], + num_produced_chunks_per_shard: vec![expected_chunks[1]], + num_expected_chunks_per_shard: vec![expected_chunks[1]], + }, + ]; + let next_epoch_validator_info = vec![ + NextEpochValidatorInfo { + account_id: "test1".parse().unwrap(), + public_key: block_producers[0].public_key(), + stake: TESTING_INIT_STAKE, + shards: vec![0], + }, + NextEpochValidatorInfo { + account_id: "test2".parse().unwrap(), + public_key: block_producers[1].public_key(), + stake: TESTING_INIT_STAKE, + shards: vec![0], + }, + ]; + let response = env + .epoch_manager + .get_validator_info(ValidatorInfoIdentifier::BlockHash(env.head.last_block_hash)) + .unwrap(); + assert_eq!( + response, + EpochValidatorInfo { + current_validators: current_epoch_validator_info.clone(), + next_validators: next_epoch_validator_info, + current_fishermen: vec![], + next_fishermen: vec![], + current_proposals: vec![ValidatorStake::new( + "test1".parse().unwrap(), + block_producers[0].public_key(), + 0, + ) + .into()], + prev_epoch_kickout: Default::default(), + epoch_start_height: 1, + epoch_height: 1, + } + ); + expected_blocks = [0, 0]; + expected_chunks = [0, 0]; + env.step_default(vec![]); + update_validator_stats(&mut env, &mut expected_blocks, &mut expected_chunks); + let response = env + .epoch_manager + .get_validator_info(ValidatorInfoIdentifier::BlockHash(env.head.last_block_hash)) + .unwrap(); + + current_epoch_validator_info[0].num_produced_blocks = expected_blocks[0]; + current_epoch_validator_info[0].num_expected_blocks = expected_blocks[0]; + current_epoch_validator_info[0].num_produced_chunks = expected_chunks[0]; + current_epoch_validator_info[0].num_expected_chunks = expected_chunks[0]; + current_epoch_validator_info[0].num_produced_chunks_per_shard = vec![expected_chunks[0]]; + current_epoch_validator_info[0].num_expected_chunks_per_shard = vec![expected_chunks[0]]; + current_epoch_validator_info[1].num_produced_blocks = expected_blocks[1]; + current_epoch_validator_info[1].num_expected_blocks = expected_blocks[1]; + current_epoch_validator_info[1].num_produced_chunks = expected_chunks[1]; + current_epoch_validator_info[1].num_expected_chunks = expected_chunks[1]; + current_epoch_validator_info[1].num_produced_chunks_per_shard = vec![expected_chunks[1]]; + current_epoch_validator_info[1].num_expected_chunks_per_shard = vec![expected_chunks[1]]; + assert_eq!(response.current_validators, current_epoch_validator_info); + assert_eq!( + response.next_validators, + vec![NextEpochValidatorInfo { + account_id: "test2".parse().unwrap(), + public_key: block_producers[1].public_key(), + stake: TESTING_INIT_STAKE, + shards: vec![0], + }] + ); + assert!(response.current_proposals.is_empty()); + assert_eq!( + response.prev_epoch_kickout, + vec![ValidatorKickoutView { + account_id: "test1".parse().unwrap(), + reason: ValidatorKickoutReason::Unstaked + }] + ); + assert_eq!(response.epoch_start_height, 3); +} + +#[test] +fn test_challenges() { + let mut env = + TestEnv::new(vec![vec!["test1".parse().unwrap(), "test2".parse().unwrap()]], 2, true); + env.step( + vec![vec![]], + vec![true], + vec![SlashedValidator::new("test2".parse().unwrap(), false)], + ); + assert_eq!(env.view_account(&"test2".parse().unwrap()).locked, 0); + let mut bps = env + .epoch_manager + .get_epoch_block_producers_ordered(&env.head.epoch_id, &env.head.last_block_hash) + .unwrap() + .iter() + .map(|x| (x.0.account_id().clone(), x.1)) + .collect::>(); + bps.sort_unstable(); + assert_eq!(bps, vec![("test1".parse().unwrap(), false), ("test2".parse().unwrap(), true)]); + let msg = vec![0, 1, 2]; + let signer = InMemorySigner::from_seed("test2".parse().unwrap(), KeyType::ED25519, "test2"); + let signature = signer.sign(&msg); + assert!(!env + .epoch_manager + .verify_validator_signature( + &env.head.epoch_id, + &env.head.last_block_hash, + &"test2".parse().unwrap(), + &msg, + &signature, + ) + .unwrap()); + // Run for 3 epochs, to finalize the given block and make sure that slashed stake actually correctly propagates. + for _ in 0..6 { + env.step(vec![vec![]], vec![true], vec![]); + } +} + +/// Test that in case of a double sign, not all stake is slashed if the double signed stake is +/// less than 33% and all stake is slashed if the stake is more than 33% +#[test] +fn test_double_sign_challenge_not_all_slashed() { + init_test_logger(); + let num_nodes = 3; + let validators = (0..num_nodes) + .map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()) + .collect::>(); + let mut env = TestEnv::new(vec![validators.clone()], 3, false); + let block_producers: Vec<_> = + validators.iter().map(|id| create_test_signer(id.as_str())).collect(); + + let signer = + InMemorySigner::from_seed(validators[2].clone(), KeyType::ED25519, validators[2].as_ref()); + let staking_transaction = stake(1, &signer, &block_producers[2], TESTING_INIT_STAKE / 3); + env.step( + vec![vec![staking_transaction]], + vec![true], + vec![SlashedValidator::new("test2".parse().unwrap(), true)], + ); + assert_eq!(env.view_account(&"test2".parse().unwrap()).locked, TESTING_INIT_STAKE); + let mut bps = env + .epoch_manager + .get_epoch_block_producers_ordered(&env.head.epoch_id, &env.head.last_block_hash) + .unwrap() + .iter() + .map(|x| (x.0.account_id().clone(), x.1)) + .collect::>(); + bps.sort_unstable(); + assert_eq!( + bps, + vec![ + ("test1".parse().unwrap(), false), + ("test2".parse().unwrap(), true), + ("test3".parse().unwrap(), false) + ] + ); + let msg = vec![0, 1, 2]; + let signer = InMemorySigner::from_seed("test2".parse().unwrap(), KeyType::ED25519, "test2"); + let signature = signer.sign(&msg); + assert!(!env + .epoch_manager + .verify_validator_signature( + &env.head.epoch_id, + &env.head.last_block_hash, + &"test2".parse().unwrap(), + &msg, + &signature, + ) + .unwrap()); + + for _ in 2..11 { + env.step(vec![vec![]], vec![true], vec![]); + } + env.step(vec![vec![]], vec![true], vec![SlashedValidator::new("test3".parse().unwrap(), true)]); + let account = env.view_account(&"test3".parse().unwrap()); + assert_eq!(account.locked, TESTING_INIT_STAKE / 3); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE / 3); + + for _ in 11..14 { + env.step_default(vec![]); + } + let account = env.view_account(&"test3".parse().unwrap()); + let slashed = (TESTING_INIT_STAKE / 3) * 3 / 4; + let remaining = TESTING_INIT_STAKE / 3 - slashed; + assert_eq!(account.locked, remaining); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE / 3); + + for _ in 14..=20 { + env.step_default(vec![]); + } + + let account = env.view_account(&"test2".parse().unwrap()); + assert_eq!(account.locked, 0); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + + let account = env.view_account(&"test3".parse().unwrap()); + assert_eq!(account.locked, 0); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE / 3 + remaining); +} + +/// Test that double sign from multiple accounts may result in all of their stake slashed. +#[test] +fn test_double_sign_challenge_all_slashed() { + init_test_logger(); + let num_nodes = 5; + let validators = (0..num_nodes) + .map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()) + .collect::>(); + let mut env = TestEnv::new(vec![validators.clone()], 5, false); + let signers: Vec<_> = validators + .iter() + .map(|id| InMemorySigner::from_seed(id.clone(), KeyType::ED25519, id.as_ref())) + .collect(); + env.step(vec![vec![]], vec![true], vec![SlashedValidator::new("test1".parse().unwrap(), true)]); + env.step(vec![vec![]], vec![true], vec![SlashedValidator::new("test2".parse().unwrap(), true)]); + let msg = vec![0, 1, 2]; + for i in 0..=1 { + let signature = signers[i].sign(&msg); + assert!(!env + .epoch_manager + .verify_validator_signature( + &env.head.epoch_id, + &env.head.last_block_hash, + &AccountId::try_from(format!("test{}", i + 1)).unwrap(), + &msg, + &signature, + ) + .unwrap()); + } + + for _ in 3..17 { + env.step_default(vec![]); + } + let account = env.view_account(&"test1".parse().unwrap()); + assert_eq!(account.locked, 0); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + + let account = env.view_account(&"test2".parse().unwrap()); + assert_eq!(account.locked, 0); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); +} + +/// Test that if double sign occurs in the same epoch as other type of challenges all stake +/// is slashed. +#[test] +fn test_double_sign_with_other_challenges() { + init_test_logger(); + let num_nodes = 3; + let validators = (0..num_nodes) + .map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()) + .collect::>(); + let mut env = TestEnv::new(vec![validators], 5, false); + env.step( + vec![vec![]], + vec![true], + vec![ + SlashedValidator::new("test1".parse().unwrap(), true), + SlashedValidator::new("test2".parse().unwrap(), false), + ], + ); + env.step( + vec![vec![]], + vec![true], + vec![ + SlashedValidator::new("test1".parse().unwrap(), false), + SlashedValidator::new("test2".parse().unwrap(), true), + ], + ); + + for _ in 3..11 { + env.step_default(vec![]); + } + let account = env.view_account(&"test1".parse().unwrap()); + assert_eq!(account.locked, 0); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + + let account = env.view_account(&"test2".parse().unwrap()); + assert_eq!(account.locked, 0); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); +} + +/// Run 4 validators. Two of them first change their stake to below validator threshold but above +/// fishermen threshold. Make sure their balance is correct. Then one fisherman increases their +/// stake to become a validator again while the other one decreases to below fishermen threshold. +/// Check that the first one becomes a validator and the second one gets unstaked completely. +#[test] +fn test_fishermen_stake() { + init_test_logger(); + let num_nodes = 4; + let validators = (0..num_nodes) + .map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()) + .collect::>(); + let mut env = TestEnv::new_with_minimum_stake_divisor( + vec![validators.clone()], + 4, + false, + // We need to be able to stake enough to be fisherman, but not enough to be + // validator + Some(20000), + ); + let block_producers: Vec<_> = + validators.iter().map(|id| create_test_signer(id.as_str())).collect(); + let signers: Vec<_> = validators + .iter() + .map(|id| InMemorySigner::from_seed(id.clone(), KeyType::ED25519, id.as_ref())) + .collect(); + let fishermen_stake = 3300 * crate::UNC_BASE + 1; + + let staking_transaction = stake(1, &signers[0], &block_producers[0], fishermen_stake); + let staking_transaction1 = stake(1, &signers[1], &block_producers[1], fishermen_stake); + env.step_default(vec![staking_transaction, staking_transaction1]); + let account = env.view_account(block_producers[0].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + assert_eq!(account.locked, TESTING_INIT_STAKE); + for _ in 2..=13 { + env.step_default(vec![]); + } + let account0 = env.view_account(block_producers[0].validator_id()); + assert_eq!(account0.locked, fishermen_stake); + assert_eq!(account0.amount, TESTING_INIT_BALANCE - fishermen_stake); + let response = env + .epoch_manager + .get_validator_info(ValidatorInfoIdentifier::BlockHash(env.head.last_block_hash)) + .unwrap(); + assert_eq!( + response + .current_fishermen + .into_iter() + .map(|fishermen| fishermen.take_account_id()) + .collect::>(), + vec!["test1", "test2"] + ); + let staking_transaction = stake(2, &signers[0], &block_producers[0], TESTING_INIT_STAKE); + let staking_transaction2 = stake(2, &signers[1], &block_producers[1], 0); + env.step_default(vec![staking_transaction, staking_transaction2]); + + for _ in 13..=25 { + env.step_default(vec![]); + } + + let account0 = env.view_account(block_producers[0].validator_id()); + assert_eq!(account0.locked, TESTING_INIT_STAKE); + assert_eq!(account0.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + + let account1 = env.view_account(block_producers[1].validator_id()); + assert_eq!(account1.locked, 0); + assert_eq!(account1.amount, TESTING_INIT_BALANCE); + let response = env + .epoch_manager + .get_validator_info(ValidatorInfoIdentifier::BlockHash(env.head.last_block_hash)) + .unwrap(); + assert!(response.current_fishermen.is_empty()); +} + +/// Test that when fishermen unstake they get their tokens back. +#[test] +fn test_fishermen_unstake() { + init_test_logger(); + let num_nodes = 2; + let validators = (0..num_nodes) + .map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()) + .collect::>(); + let mut env = TestEnv::new_with_minimum_stake_divisor( + vec![validators.clone()], + 2, + false, + // We need to be able to stake enough to be fisherman, but not enough to be + // validator + Some(20000), + ); + let block_producers: Vec<_> = + validators.iter().map(|id| create_test_signer(id.as_str())).collect(); + let signers: Vec<_> = validators + .iter() + .map(|id| InMemorySigner::from_seed(id.clone(), KeyType::ED25519, id.as_ref())) + .collect(); + let fishermen_stake = 3300 * crate::UNC_BASE + 1; + + let staking_transaction = stake(1, &signers[0], &block_producers[0], fishermen_stake); + env.step_default(vec![staking_transaction]); + for _ in 2..9 { + env.step_default(vec![]); + } + + let account0 = env.view_account(block_producers[0].validator_id()); + assert_eq!(account0.locked, fishermen_stake); + assert_eq!(account0.amount, TESTING_INIT_BALANCE - fishermen_stake); + let response = env + .epoch_manager + .get_validator_info(ValidatorInfoIdentifier::BlockHash(env.head.last_block_hash)) + .unwrap(); + assert_eq!( + response + .current_fishermen + .into_iter() + .map(|fishermen| fishermen.take_account_id()) + .collect::>(), + vec![AccountIdRef::new_or_panic("test1")] + ); + let staking_transaction = stake(2, &signers[0], &block_producers[0], 0); + env.step_default(vec![staking_transaction]); + for _ in 10..17 { + env.step_default(vec![]); + } + + let account0 = env.view_account(block_producers[0].validator_id()); + assert_eq!(account0.locked, 0); + assert_eq!(account0.amount, TESTING_INIT_BALANCE); + let response = env + .epoch_manager + .get_validator_info(ValidatorInfoIdentifier::BlockHash(env.head.last_block_hash)) + .unwrap(); + assert!(response.current_fishermen.is_empty()); +} + +/// Enable reward and make sure that validators get reward proportional to their stake. +#[test] +fn test_validator_reward() { + init_test_logger(); + let num_nodes = 4; + let epoch_length = 40; + let validators = + (0..num_nodes).map(|i| format!("test{}", i + 1).parse().unwrap()).collect::>(); + let mut env = TestEnv::new(vec![validators.clone()], epoch_length, true); + let block_producers: Vec<_> = + validators.iter().map(|id| create_test_signer(id.as_str())).collect(); + + for _ in 0..(epoch_length + 1) { + env.step_default(vec![]); + } + + let (validator_reward, protocol_treasury_reward) = + env.compute_reward(num_nodes, epoch_length * 10u64.pow(9)); + for i in 0..4 { + let account = env.view_account(block_producers[i].validator_id()); + assert_eq!(account.locked, TESTING_INIT_STAKE + validator_reward); + } + + let protocol_treasury_account = + env.view_account(&env.runtime.genesis_config.protocol_treasury_account); + assert_eq!(protocol_treasury_account.amount, TESTING_INIT_BALANCE + protocol_treasury_reward); +} + +#[test] +fn test_delete_account_after_unstake() { + init_test_logger(); + let num_nodes = 2; + let validators = (0..num_nodes) + .map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()) + .collect::>(); + let mut env = TestEnv::new(vec![validators.clone()], 4, false); + let block_producers: Vec<_> = + validators.iter().map(|id| create_test_signer(id.as_str())).collect(); + let signers: Vec<_> = validators + .iter() + .map(|id| InMemorySigner::from_seed(id.clone(), KeyType::ED25519, id.as_ref())) + .collect(); + + let staking_transaction1 = stake(1, &signers[1], &block_producers[1], 0); + env.step_default(vec![staking_transaction1]); + let account = env.view_account(block_producers[1].validator_id()); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + assert_eq!(account.locked, TESTING_INIT_STAKE); + for _ in 2..=5 { + env.step_default(vec![]); + } + let staking_transaction2 = stake(2, &signers[1], &block_producers[1], 1); + env.step_default(vec![staking_transaction2]); + for _ in 7..=13 { + env.step_default(vec![]); + } + let account = env.view_account(block_producers[1].validator_id()); + assert_eq!(account.locked, 0); + + let delete_account_transaction = SignedTransaction::from_actions( + 4, + signers[1].account_id.clone(), + signers[1].account_id.clone(), + &signers[1] as &dyn Signer, + vec![Action::DeleteAccount(DeleteAccountAction { + beneficiary_id: signers[0].account_id.clone(), + })], + // runtime does not validate block history + CryptoHash::default(), + ); + env.step_default(vec![delete_account_transaction]); + for _ in 15..=17 { + env.step_default(vec![]); + } +} + +#[test] +fn test_proposal_deduped() { + let num_nodes = 2; + let validators = (0..num_nodes) + .map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()) + .collect::>(); + let mut env = TestEnv::new(vec![validators.clone()], 4, false); + let block_producers: Vec<_> = + validators.iter().map(|id| create_test_signer(id.as_str())).collect(); + let signers: Vec<_> = validators + .iter() + .map(|id| InMemorySigner::from_seed(id.clone(), KeyType::ED25519, id.as_ref())) + .collect(); + + let staking_transaction1 = stake(1, &signers[1], &block_producers[1], TESTING_INIT_STAKE - 100); + let staking_transaction2 = stake(2, &signers[1], &block_producers[1], TESTING_INIT_STAKE - 10); + env.step_default(vec![staking_transaction1, staking_transaction2]); + assert_eq!(env.last_proposals.len(), 1); + assert_eq!(env.last_proposals[0].stake(), TESTING_INIT_STAKE - 10); +} + +#[test] +fn test_insufficient_stake() { + let num_nodes = 2; + let validators = (0..num_nodes) + .map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()) + .collect::>(); + let mut env = TestEnv::new(vec![validators.clone()], 4, false); + let block_producers: Vec<_> = + validators.iter().map(|id| create_test_signer(id.as_str())).collect(); + let signers: Vec<_> = validators + .iter() + .map(|id| InMemorySigner::from_seed(id.clone(), KeyType::ED25519, id.as_ref())) + .collect(); + + let staking_transaction1 = stake(1, &signers[1], &block_producers[1], 100); + let staking_transaction2 = stake(2, &signers[1], &block_producers[1], 100 * crate::UNC_BASE); + env.step_default(vec![staking_transaction1, staking_transaction2]); + assert!(env.last_proposals.is_empty()); + let staking_transaction3 = stake(3, &signers[1], &block_producers[1], 0); + env.step_default(vec![staking_transaction3]); + assert_eq!(env.last_proposals.len(), 1); + assert_eq!(env.last_proposals[0].stake(), 0); +} + +/// Check that flat state is included into trie and is not included into view trie, because we can't apply flat +/// state optimization to view calls. +#[test] +fn test_flat_state_usage() { + let env = TestEnv::new(vec![vec!["test1".parse().unwrap()]], 4, false); + let trie = env + .runtime + .get_trie_for_shard(0, &env.head.prev_block_hash, Trie::EMPTY_ROOT, true) + .unwrap(); + assert!(trie.has_flat_storage_chunk_view()); + + let trie = env + .runtime + .get_view_trie_for_shard(0, &env.head.prev_block_hash, Trie::EMPTY_ROOT) + .unwrap(); + assert!(!trie.has_flat_storage_chunk_view()); +} + +/// Check that querying trie and flat state gives the same result. +#[test] +fn test_trie_and_flat_state_equality() { + let num_nodes = 2; + let validators = (0..num_nodes) + .map(|i| AccountId::try_from(format!("test{}", i + 1)).unwrap()) + .collect::>(); + let mut env = TestEnv::new(vec![validators.clone()], 4, false); + let signers: Vec<_> = validators + .iter() + .map(|id| InMemorySigner::from_seed(id.clone(), KeyType::ED25519, id.as_ref())) + .collect(); + + let transfer_tx = SignedTransaction::from_actions( + 4, + signers[0].account_id.clone(), + validators[1].clone(), + &signers[0] as &dyn Signer, + vec![Action::Transfer(TransferAction { deposit: 10 })], + // runtime does not validate block history + CryptoHash::default(), + ); + env.step_default(vec![transfer_tx]); + for _ in 1..=5 { + env.step_default(vec![]); + } + + // Extract account in two ways: + // - using state trie, which should use flat state after enabling it in the protocol + // - using view state, which should never use flat state + let head_prev_block_hash = env.head.prev_block_hash; + let state_root = env.state_roots[0]; + let state = env.runtime.get_trie_for_shard(0, &head_prev_block_hash, state_root, true).unwrap(); + let view_state = + env.runtime.get_view_trie_for_shard(0, &head_prev_block_hash, state_root).unwrap(); + let trie_key = TrieKey::Account { account_id: validators[1].clone() }; + let key = trie_key.to_vec(); + + let state_value = state.get(&key).unwrap().unwrap(); + let account = Account::try_from_slice(&state_value).unwrap(); + assert_eq!(account.amount(), TESTING_INIT_BALANCE - TESTING_INIT_STAKE + 10); + + let view_state_value = view_state.get(&key).unwrap().unwrap(); + assert_eq!(state_value, view_state_value); +} + +/// Check that mainnet genesis hash still matches, to make sure that we're still backwards compatible. +#[test] +fn test_genesis_hash() { + let genesis = unc_mainnet_res::mainnet_genesis(); + let chain_genesis = ChainGenesis::new(&genesis); + let store = unc_store::test_utils::create_test_store(); + + let tempdir = tempfile::tempdir().unwrap(); + initialize_genesis_state(store.clone(), &genesis, Some(tempdir.path())); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = NightshadeRuntime::test_with_runtime_config_store( + tempdir.path(), + store.clone(), + &genesis.config, + epoch_manager.clone(), + RuntimeConfigStore::new(None), + StateSnapshotType::EveryEpoch, + ); + + let block = Chain::make_genesis_block(epoch_manager.as_ref(), runtime.as_ref(), &chain_genesis) + .unwrap(); + assert_eq!(block.header().hash().to_string(), "EPnLgE7iEq9s7yTkos96M3cWymH5avBAPm3qx3NXqR8H"); + + let epoch_manager = EpochManager::new_from_genesis_config(store, &genesis.config).unwrap(); + let epoch_info = epoch_manager.get_epoch_info(&EpochId::default()).unwrap(); + // Verify the order of the block producers. + assert_eq!( + [ + 1, 0, 1, 0, 0, 3, 3, 2, 2, 3, 0, 2, 0, 0, 1, 1, 1, 1, 3, 2, 3, 2, 0, 3, 3, 3, 0, 3, 1, + 3, 1, 0, 1, 2, 3, 0, 1, 0, 0, 0, 2, 2, 2, 3, 3, 3, 3, 1, 2, 0, 1, 0, 1, 0, 3, 2, 1, 2, + 0, 1, 3, 3, 1, 2, 1, 2, 1, 0, 2, 3, 1, 2, 1, 2, 3, 2, 0, 3, 3, 2, 0, 0, 2, 3, 0, 3, 0, + 2, 3, 1, 1, 2, 1, 0, 1, 2, 2, 1, 2, 0 + ], + epoch_info.block_producers_settlement() + ); +} diff --git a/framework/src/state_sync.rs b/framework/src/state_sync.rs new file mode 100644 index 000000000..208830e60 --- /dev/null +++ b/framework/src/state_sync.rs @@ -0,0 +1,508 @@ +use crate::metrics; + +use unc_chain::types::RuntimeAdapter; +use unc_chain::{Chain, ChainGenesis, ChainStoreAccess, DoomslugThresholdMode, Error}; +use unc_chain_configs::{ClientConfig, ExternalStorageLocation}; +use unc_client::sync::external::{create_bucket_readwrite, external_storage_location}; +use unc_client::sync::external::{ + external_storage_location_directory, get_part_id_from_filename, is_part_filename, + ExternalConnection, +}; +use unc_client::sync::state::{StateSync, STATE_DUMP_ITERATION_TIME_LIMIT_SECS}; +use unc_epoch_manager::shard_tracker::ShardTracker; +use unc_epoch_manager::EpochManagerAdapter; +use unc_primitives::hash::CryptoHash; +use unc_primitives::state_part::PartId; +use unc_primitives::state_sync::{StatePartKey, StateSyncDumpProgress}; +use unc_primitives::types::{AccountId, EpochHeight, EpochId, ShardId, StateRoot}; +use unc_store::DBCol; +use rand::{thread_rng, Rng}; +use std::collections::HashSet; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; +use std::time::{Duration, Instant}; + +/// Starts one a thread per tracked shard. +/// Each started thread will be dumping state parts of a single epoch to external storage. +pub fn spawn_state_sync_dump( + client_config: &ClientConfig, + chain_genesis: ChainGenesis, + epoch_manager: Arc, + shard_tracker: ShardTracker, + runtime: Arc, + account_id: Option, +) -> anyhow::Result> { + let dump_config = if let Some(dump_config) = client_config.state_sync.dump.clone() { + dump_config + } else { + // Dump is not configured, and therefore not enabled. + tracing::debug!(target: "state_sync_dump", "Not spawning the state sync dump loop"); + return Ok(None); + }; + tracing::info!(target: "state_sync_dump", "Spawning the state sync dump loop"); + + let external = match dump_config.location { + ExternalStorageLocation::S3 { bucket, region } => ExternalConnection::S3{ + bucket: Arc::new(create_bucket_readwrite(&bucket, ®ion, Duration::from_secs(30), dump_config.credentials_file).expect( + "Failed to authenticate connection to S3. Please either provide AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in the environment, or create a credentials file and link it in config.json as 's3_credentials_file'.")) + }, + ExternalStorageLocation::Filesystem { root_dir } => ExternalConnection::Filesystem { root_dir }, + ExternalStorageLocation::GCS { bucket } => { + if let Some(credentials_file) = dump_config.credentials_file { + if let Ok(var) = std::env::var("SERVICE_ACCOUNT") { + tracing::warn!(target: "state_sync_dump", "Environment variable 'SERVICE_ACCOUNT' is set to {var}, but 'credentials_file' in config.json overrides it to '{credentials_file:?}'"); + println!("Environment variable 'SERVICE_ACCOUNT' is set to {var}, but 'credentials_file' in config.json overrides it to '{credentials_file:?}'"); + } + std::env::set_var("SERVICE_ACCOUNT", &credentials_file); + tracing::info!(target: "state_sync_dump", "Set the environment variable 'SERVICE_ACCOUNT' to '{credentials_file:?}'"); + } + ExternalConnection::GCS { + gcs_client: Arc::new(cloud_storage::Client::default()), + reqwest_client: Arc::new(reqwest::Client::default()), + bucket + } + }, + }; + + // Determine how many threads to start. + // TODO: Handle the case of changing the shard layout. + let shard_ids = { + // Sadly, `Chain` is not `Send` and each thread needs to create its own `Chain` instance. + let chain = Chain::new_for_view_client( + epoch_manager.clone(), + shard_tracker.clone(), + runtime.clone(), + &chain_genesis, + DoomslugThresholdMode::TwoThirds, + false, + )?; + let epoch_id = chain.head()?.epoch_id; + epoch_manager.shard_ids(&epoch_id) + }?; + + let chain_id = client_config.chain_id.clone(); + let keep_running = Arc::new(AtomicBool::new(true)); + // Start a thread for each shard. + let handles = shard_ids + .into_iter() + .map(|shard_id| { + let runtime = runtime.clone(); + let chain_genesis = chain_genesis.clone(); + let chain = Chain::new_for_view_client( + epoch_manager.clone(), + shard_tracker.clone(), + runtime.clone(), + &chain_genesis, + DoomslugThresholdMode::TwoThirds, + false, + ) + .unwrap(); + let arbiter_handle = actix_rt::Arbiter::new().handle(); + assert!(arbiter_handle.spawn(state_sync_dump( + shard_id as ShardId, + chain, + epoch_manager.clone(), + shard_tracker.clone(), + runtime.clone(), + chain_id.clone(), + dump_config.restart_dump_for_shards.clone().unwrap_or_default(), + external.clone(), + dump_config.iteration_delay.unwrap_or(Duration::from_secs(10)), + account_id.clone(), + keep_running.clone(), + ))); + arbiter_handle + }) + .collect(); + + Ok(Some(StateSyncDumpHandle { handles, keep_running })) +} + +/// Holds arbiter handles controlling the lifetime of the spawned threads. +pub struct StateSyncDumpHandle { + pub handles: Vec, + keep_running: Arc, +} + +impl Drop for StateSyncDumpHandle { + fn drop(&mut self) { + self.stop() + } +} + +impl StateSyncDumpHandle { + pub fn stop(&self) { + self.keep_running.store(false, std::sync::atomic::Ordering::Relaxed); + self.handles.iter().for_each(|handle| { + handle.stop(); + }); + } +} + +pub fn extract_part_id_from_part_file_name(file_name: &String) -> u64 { + assert!(is_part_filename(file_name)); + return get_part_id_from_filename(file_name).unwrap(); +} + +async fn get_missing_part_ids_for_epoch( + shard_id: ShardId, + chain_id: &String, + epoch_id: &EpochId, + epoch_height: u64, + total_parts: u64, + external: &ExternalConnection, +) -> Result, anyhow::Error> { + let directory_path = + external_storage_location_directory(chain_id, epoch_id, epoch_height, shard_id); + let file_names = external.list_state_parts(shard_id, &directory_path).await?; + if !file_names.is_empty() { + let existing_nums: HashSet<_> = file_names + .iter() + .map(|file_name| extract_part_id_from_part_file_name(file_name)) + .collect(); + let missing_nums: Vec = + (0..total_parts).filter(|i| !existing_nums.contains(i)).collect(); + let num_missing = missing_nums.len(); + tracing::debug!(target: "state_sync_dump", ?num_missing, ?directory_path, "Some parts have already been dumped."); + Ok(missing_nums) + } else { + tracing::debug!(target: "state_sync_dump", ?total_parts, ?directory_path, "No part has been dumped."); + let missing_nums = (0..total_parts).collect::>(); + Ok(missing_nums) + } +} + +fn select_random_part_id_with_index(parts_to_be_dumped: &Vec) -> (u64, usize) { + let mut rng = thread_rng(); + let selected_idx = rng.gen_range(0..parts_to_be_dumped.len()); + let selected_element = parts_to_be_dumped[selected_idx]; + tracing::debug!(target: "state_sync_dump", ?selected_element, "selected parts to dump: "); + (selected_element, selected_idx) +} + +fn get_current_state( + chain: &Chain, + shard_id: &ShardId, + shard_tracker: &ShardTracker, + account_id: &Option, + epoch_manager: Arc, +) -> Result, Error> { + let was_last_epoch_dumped = match chain.chain_store().get_state_sync_dump_progress(*shard_id) { + Ok(StateSyncDumpProgress::AllDumped { epoch_id, .. }) => Some(epoch_id), + _ => None, + }; + + match get_latest_epoch(shard_id, &chain, epoch_manager) { + Err(err) => { + tracing::debug!(target: "state_sync_dump", shard_id, ?err, "check_latest_epoch failed. Will retry."); + Err(err) + } + Ok((new_epoch_id, new_epoch_height, new_sync_hash)) => { + if Some(&new_epoch_id) == was_last_epoch_dumped.as_ref() { + tracing::debug!(target: "state_sync_dump", shard_id, ?was_last_epoch_dumped, ?new_epoch_id, new_epoch_height, ?new_sync_hash, "latest epoch is all dumped. No new epoch to dump. Idle"); + Ok(None) + } else if cares_about_shard( + chain, + shard_id, + &new_sync_hash, + &shard_tracker, + &account_id, + )? { + Ok(Some((new_epoch_id, new_epoch_height, new_sync_hash))) + } else { + tracing::debug!(target: "state_sync_dump", shard_id, ?new_epoch_id, new_epoch_height, ?new_sync_hash, "Doesn't care about the shard in the current epoch. Idle"); + Ok(None) + } + } + } +} + +const FAILURES_ALLOWED_PER_ITERATION: u32 = 10; + +async fn state_sync_dump( + shard_id: ShardId, + chain: Chain, + epoch_manager: Arc, + shard_tracker: ShardTracker, + runtime: Arc, + chain_id: String, + restart_dump_for_shards: Vec, + external: ExternalConnection, + iteration_delay: Duration, + account_id: Option, + keep_running: Arc, +) { + tracing::info!(target: "state_sync_dump", shard_id, "Running StateSyncDump loop"); + + if restart_dump_for_shards.contains(&shard_id) { + tracing::debug!(target: "state_sync_dump", shard_id, "Dropped existing progress"); + chain.chain_store().set_state_sync_dump_progress(shard_id, None).unwrap(); + } + + // Stop if the node is stopped. + // Note that without this check the state dumping thread is unstoppable, i.e. non-interruptable. + while keep_running.load(std::sync::atomic::Ordering::Relaxed) { + tracing::debug!(target: "state_sync_dump", shard_id, "Running StateSyncDump loop iteration"); + let current_state = get_current_state( + &chain, + &shard_id, + &shard_tracker, + &account_id, + epoch_manager.clone(), + ); + let next_state = match current_state { + Err(err) => { + tracing::error!(target: "state_sync_dump", ?err, ?shard_id, "Failed to get the current state"); + None + } + Ok(None) => None, + Ok(Some((epoch_id, epoch_height, sync_hash))) => { + let in_progress_data = get_in_progress_data(shard_id, sync_hash, &chain); + match in_progress_data { + Err(err) => { + tracing::error!(target: "state_sync_dump", ?err, ? shard_id, "Failed to get in progress data"); + None + } + Ok((state_root, num_parts, sync_prev_prev_hash)) => { + match get_missing_part_ids_for_epoch( + shard_id, + &chain_id, + &epoch_id, + epoch_height, + num_parts, + &external, + ) + .await + { + Err(err) => { + tracing::error!(target: "state_sync_dump", ?err, ?shard_id, "Failed to determine missing parts"); + None + } + Ok(missing_parts) if missing_parts.is_empty() => { + update_dumped_size_and_cnt_metrics( + &shard_id, + epoch_height, + None, + num_parts, + num_parts, + ); + Some(StateSyncDumpProgress::AllDumped { epoch_id, epoch_height }) + } + Ok(missing_parts) => { + let mut parts_to_dump = missing_parts.clone(); + let timer = Instant::now(); + let mut dumped_any_state_part = false; + let mut failures_cnt = 0; + // Stop if the node is stopped. + // Note that without this check the state dumping thread is unstoppable, i.e. non-interruptable. + while keep_running.load(std::sync::atomic::Ordering::Relaxed) + && timer.elapsed().as_secs() + <= STATE_DUMP_ITERATION_TIME_LIMIT_SECS + && !parts_to_dump.is_empty() + && failures_cnt < FAILURES_ALLOWED_PER_ITERATION + { + let _timer = metrics::STATE_SYNC_DUMP_ITERATION_ELAPSED + .with_label_values(&[&shard_id.to_string()]) + .start_timer(); + + let (part_id, selected_idx) = + select_random_part_id_with_index(&parts_to_dump); + + let state_part = obtain_and_store_state_part( + runtime.as_ref(), + shard_id, + sync_hash, + &sync_prev_prev_hash, + &state_root, + part_id, + num_parts, + &chain, + ); + let state_part = match state_part { + Ok(state_part) => state_part, + Err(err) => { + tracing::warn!(target: "state_sync_dump", shard_id, epoch_height, part_id, ?err, "Failed to obtain and store part. Will skip this part."); + failures_cnt += 1; + continue; + } + }; + + let location = external_storage_location( + &chain_id, + &epoch_id, + epoch_height, + shard_id, + part_id, + num_parts, + ); + if let Err(err) = external + .put_state_part(&state_part, shard_id, &location) + .await + { + // no need to break if there's an error, we should keep dumping other parts. + // reason is we are dumping random selected parts, so it's fine if we are not able to finish all of them + tracing::warn!(target: "state_sync_dump", shard_id, epoch_height, part_id, ?err, "Failed to put a store part into external storage. Will skip this part."); + failures_cnt += 1; + continue; + } + + // Remove the dumped part from parts_to_dump so that we draw without replacement. + parts_to_dump.swap_remove(selected_idx); + update_dumped_size_and_cnt_metrics( + &shard_id, + epoch_height, + Some(state_part.len()), + num_parts.checked_sub(parts_to_dump.len() as u64).unwrap(), + num_parts, + ); + dumped_any_state_part = true; + } + if parts_to_dump.is_empty() { + Some(StateSyncDumpProgress::AllDumped { + epoch_id, + epoch_height, + }) + } else if dumped_any_state_part { + Some(StateSyncDumpProgress::InProgress { + epoch_id, + epoch_height, + sync_hash, + }) + } else { + // No progress made. Wait before retrying. + None + } + } + } + } + } + } + }; + + // Record the next state of the state machine. + let has_progress = match next_state { + Some(next_state) => { + tracing::debug!(target: "state_sync_dump", shard_id, ?next_state); + match chain.chain_store().set_state_sync_dump_progress(shard_id, Some(next_state)) { + Ok(_) => true, + Err(err) => { + // This will be retried. + tracing::debug!(target: "state_sync_dump", shard_id, ?err, "Failed to set progress"); + false + } + } + } + None => { + // Nothing to do, will check again later. + tracing::debug!(target: "state_sync_dump", shard_id, "Idle"); + false + } + }; + + if !has_progress { + // Avoid a busy-loop when there is nothing to do. + actix_rt::time::sleep(tokio::time::Duration::from(iteration_delay)).await; + } + } + tracing::debug!(target: "state_sync_dump", shard_id, "Stopped state dump thread"); +} + +// Extracts extra data needed for obtaining state parts. +fn get_in_progress_data( + shard_id: ShardId, + sync_hash: CryptoHash, + chain: &Chain, +) -> Result<(StateRoot, u64, CryptoHash), Error> { + let state_header = chain.get_state_response_header(shard_id, sync_hash)?; + let state_root = state_header.chunk_prev_state_root(); + let num_parts = state_header.num_state_parts(); + + let sync_block_header = chain.get_block_header(&sync_hash)?; + let sync_prev_block_header = chain.get_previous_header(&sync_block_header)?; + let sync_prev_prev_hash = sync_prev_block_header.prev_hash(); + Ok((state_root, num_parts, *sync_prev_prev_hash)) +} + +fn update_dumped_size_and_cnt_metrics( + shard_id: &ShardId, + epoch_height: EpochHeight, + part_len: Option, + parts_dumped: u64, + num_parts: u64, +) { + if let Some(part_len) = part_len { + metrics::STATE_SYNC_DUMP_SIZE_TOTAL + .with_label_values(&[&epoch_height.to_string(), &shard_id.to_string()]) + .inc_by(part_len as u64); + } + + metrics::STATE_SYNC_DUMP_EPOCH_HEIGHT + .with_label_values(&[&shard_id.to_string()]) + .set(epoch_height as i64); + + metrics::STATE_SYNC_DUMP_NUM_PARTS_DUMPED + .with_label_values(&[&shard_id.to_string()]) + .set(parts_dumped as i64); + + metrics::STATE_SYNC_DUMP_NUM_PARTS_TOTAL + .with_label_values(&[&shard_id.to_string()]) + .set(num_parts as i64); +} + +/// Obtains and then saves the part data. +fn obtain_and_store_state_part( + runtime: &dyn RuntimeAdapter, + shard_id: ShardId, + sync_hash: CryptoHash, + sync_prev_prev_hash: &CryptoHash, + state_root: &StateRoot, + part_id: u64, + num_parts: u64, + chain: &Chain, +) -> Result, Error> { + let state_part = runtime.obtain_state_part( + shard_id, + sync_prev_prev_hash, + state_root, + PartId::new(part_id, num_parts), + )?; + + let key = borsh::to_vec(&StatePartKey(sync_hash, shard_id, part_id))?; + let mut store_update = chain.chain_store().store().store_update(); + store_update.set(DBCol::StateParts, &key, &state_part); + store_update.commit()?; + Ok(state_part) +} + +fn cares_about_shard( + chain: &Chain, + shard_id: &ShardId, + sync_hash: &CryptoHash, + shard_tracker: &ShardTracker, + account_id: &Option, +) -> Result { + let sync_header = chain.get_block_header(&sync_hash)?; + let sync_prev_hash = sync_header.prev_hash(); + Ok(shard_tracker.care_about_shard(account_id.as_ref(), sync_prev_hash, *shard_id, true)) +} + +/// return epoch_id and sync_hash of the latest complete epoch available locally. +fn get_latest_epoch( + shard_id: &ShardId, + chain: &Chain, + epoch_manager: Arc, +) -> Result<(EpochId, EpochHeight, CryptoHash), Error> { + let head = chain.head()?; + tracing::debug!(target: "state_sync_dump", shard_id, "Check if a new complete epoch is available"); + let hash = head.last_block_hash; + let header = chain.get_block_header(&hash)?; + let final_hash = header.last_final_block(); + let sync_hash = StateSync::get_epoch_start_sync_hash(chain, final_hash)?; + let final_block_header = chain.get_block_header(&final_hash)?; + let epoch_id = final_block_header.epoch_id().clone(); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id)?; + let epoch_height = epoch_info.epoch_height(); + tracing::debug!(target: "state_sync_dump", ?final_hash, ?sync_hash, ?epoch_id, epoch_height, "get_latest_epoch"); + + Ok((epoch_id, epoch_height, sync_hash)) +} diff --git a/framework/src/test_utils.rs b/framework/src/test_utils.rs new file mode 100644 index 000000000..2cce936ba --- /dev/null +++ b/framework/src/test_utils.rs @@ -0,0 +1,100 @@ +use unc_chain::types::RuntimeAdapter; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnvBuilder; +use unc_epoch_manager::EpochManagerHandle; +use unc_parameters::RuntimeConfigStore; +use unc_store::genesis::initialize_genesis_state; +use unc_store::{Store, TrieConfig}; +use std::path::PathBuf; +use std::sync::Arc; + +use crate::NightshadeRuntime; + +pub trait TestEnvNightshadeSetupExt { + fn nightshade_runtimes(self, genesis: &Genesis) -> Self; + fn nightshade_runtimes_with_runtime_config_store( + self, + genesis: &Genesis, + runtime_configs: Vec, + ) -> Self; + fn nightshade_runtimes_with_trie_config( + self, + genesis: &Genesis, + trie_configs: Vec, + ) -> Self; +} + +impl TestEnvNightshadeSetupExt for TestEnvBuilder { + fn nightshade_runtimes(self, genesis: &Genesis) -> Self { + let runtime_configs = vec![RuntimeConfigStore::test(); self.num_clients()]; + self.nightshade_runtimes_with_runtime_config_store(genesis, runtime_configs) + } + + fn nightshade_runtimes_with_runtime_config_store( + self, + genesis: &Genesis, + runtime_configs: Vec, + ) -> Self { + let state_snapshot_type = self.state_snapshot_type(); + let nightshade_runtime_creator = |home_dir: PathBuf, + store: Store, + epoch_manager: Arc, + runtime_config: RuntimeConfigStore, + _| + -> Arc { + // TODO: It's not ideal to initialize genesis state with the nightshade runtime here for tests + // Tests that don't use nightshade runtime have genesis initialized in kv_runtime. + // We should instead try to do this while configuring store. + let home_dir = home_dir.as_path(); + initialize_genesis_state(store.clone(), genesis, Some(home_dir)); + NightshadeRuntime::test_with_runtime_config_store( + home_dir, + store, + &genesis.config, + epoch_manager, + runtime_config, + state_snapshot_type.clone(), + ) + }; + let dummy_trie_configs = vec![TrieConfig::default(); self.num_clients()]; + self.internal_initialize_nightshade_runtimes( + runtime_configs, + dummy_trie_configs, + nightshade_runtime_creator, + ) + } + + fn nightshade_runtimes_with_trie_config( + self, + genesis: &Genesis, + trie_configs: Vec, + ) -> Self { + let state_snapshot_type = self.state_snapshot_type(); + let nightshade_runtime_creator = |home_dir: PathBuf, + store: Store, + epoch_manager: Arc, + _, + trie_config: TrieConfig| + -> Arc { + // TODO: It's not ideal to initialize genesis state with the nightshade runtime here for tests + // Tests that don't use nightshade runtime have genesis initialized in kv_runtime. + // We should instead try to do this while configuring store. + let home_dir = home_dir.as_path(); + initialize_genesis_state(store.clone(), genesis, Some(home_dir)); + NightshadeRuntime::test_with_trie_config( + home_dir, + store, + &genesis.config, + epoch_manager, + trie_config, + state_snapshot_type.clone(), + ) + }; + let dummy_runtime_configs = vec![RuntimeConfigStore::test(); self.num_clients()]; + self.internal_initialize_nightshade_runtimes( + dummy_runtime_configs, + trie_configs, + nightshade_runtime_creator, + ) + } +} diff --git a/framework/tests/economics.rs b/framework/tests/economics.rs new file mode 100644 index 000000000..2141c5187 --- /dev/null +++ b/framework/tests/economics.rs @@ -0,0 +1,136 @@ +/// Test economic edge cases. +use std::path::Path; + +use unc_client::ProcessTxResponse; +use unc_epoch_manager::EpochManager; +use num_rational::Ratio; + +use unc_chain::ChainGenesis; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_o11y::testonly::init_integration_logger; +use unc_primitives::transaction::SignedTransaction; +use unc_store::{genesis::initialize_genesis_state, test_utils::create_test_store}; +use framework::{config::GenesisExt, NightshadeRuntime}; +use testlib::fees_utils::FeeHelper; + +use unc_primitives::types::EpochId; +use primitive_types::U256; + +fn build_genesis() -> Genesis { + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = 2; + genesis.config.num_blocks_per_year = 2; + genesis.config.protocol_reward_rate = Ratio::new_raw(1, 10); + genesis.config.max_inflation_rate = Ratio::new_raw(1, 10); + genesis.config.chunk_producer_kickout_threshold = 30; + genesis.config.online_min_threshold = Ratio::new_raw(0, 1); + genesis.config.online_max_threshold = Ratio::new_raw(1, 1); + genesis +} + +fn setup_env(genesis: &Genesis) -> TestEnv { + init_integration_logger(); + let store = create_test_store(); + initialize_genesis_state(store.clone(), &genesis, None); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = NightshadeRuntime::test( + Path::new("."), + store.clone(), + &genesis.config, + epoch_manager.clone(), + ); + TestEnv::builder(ChainGenesis::new(&genesis)) + .stores(vec![store]) + .epoch_managers(vec![epoch_manager]) + .runtimes(vec![runtime]) + .build() +} + +fn calc_total_supply(env: &mut TestEnv) -> u128 { + ["near", "test0", "test1"] + .iter() + .map(|account_id| { + let account = env.query_account(account_id.parse().unwrap()); + account.amount + account.locked + }) + .sum() +} + +/// Test that node mints and burns tokens correctly with fees and epoch rewards. +/// This combines Client & NightshadeRuntime to also test EpochManager. +#[test] +fn test_burn_mint() { + let genesis = build_genesis(); + let mut env = setup_env(&genesis); + let config = env.clients[0] + .runtime_adapter + .get_protocol_config(&EpochId::default()) + .unwrap() + .runtime_config; + let fee_helper = FeeHelper::new(config, genesis.config.min_gas_price); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let initial_total_supply = env.chain_genesis.total_supply; + let genesis_hash = *env.clients[0].chain.genesis().hash(); + assert_eq!( + env.clients[0].process_tx( + SignedTransaction::send_money( + 1, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 1000, + genesis_hash, + ), + false, + false, + ), + ProcessTxResponse::ValidTx + ); + let unc_balance = env.query_balance("near".parse().unwrap()); + assert_eq!(calc_total_supply(&mut env), initial_total_supply); + for i in 1..6 { + env.produce_block(0, i); + // TODO: include receipts into total supply calculations and check with total supply in the next block? + // print_accounts(&mut env); + // assert_eq!( + // calc_total_supply(&mut env), + // env.clients[0].chain.get_block_by_height(i + 1).unwrap().header.total_supply() + // ); + } + + // Block 3: epoch ends, it gets it's 10% of total supply - transfer cost. + let block3 = env.clients[0].chain.get_block_by_height(3).unwrap(); + // We burn half of the cost when tx executed and the other half in the next block for the receipt processing. + let half_transfer_cost = fee_helper.transfer_cost() / 2; + let epoch_total_reward = { + let block0 = env.clients[0].chain.get_block_by_height(0).unwrap(); + let block2 = env.clients[0].chain.get_block_by_height(2).unwrap(); + let duration = block2.header().raw_timestamp() - block0.header().raw_timestamp(); + (U256::from(initial_total_supply) * U256::from(duration) + / U256::from(10u128.pow(9) * 24 * 60 * 60 * 365 * 10)) + .as_u128() + }; + assert_eq!( + block3.header().total_supply(), + // supply + 1% of protocol rewards + 3/4 * 9% of validator rewards. + initial_total_supply + epoch_total_reward * 775 / 1000 - half_transfer_cost + ); + assert_eq!(block3.chunks()[0].prev_balance_burnt(), half_transfer_cost); + // Block 4: subtract 2nd part of transfer. + let block4 = env.clients[0].chain.get_block_by_height(4).unwrap(); + assert_eq!(block4.header().total_supply(), block3.header().total_supply() - half_transfer_cost); + assert_eq!(block4.chunks()[0].prev_balance_burnt(), half_transfer_cost); + // Check that Protocol Treasury account got it's 1% as well. + assert_eq!(env.query_balance("near".parse().unwrap()), unc_balance + epoch_total_reward / 10); + // Block 5: reward from previous block. + let block5 = env.clients[0].chain.get_block_by_height(5).unwrap(); + let prev_total_supply = block4.header().total_supply(); + let block2 = env.clients[0].chain.get_block_by_height(2).unwrap(); + let epoch_total_reward = (U256::from(prev_total_supply) + * U256::from(block4.header().raw_timestamp() - block2.header().raw_timestamp()) + / U256::from(10u128.pow(9) * 24 * 60 * 60 * 365 * 10)) + .as_u128(); + assert_eq!(block5.header().total_supply(), prev_total_supply + epoch_total_reward); +} diff --git a/genesis-tools/README.md b/genesis-tools/README.md new file mode 100644 index 000000000..2fcb6dac5 --- /dev/null +++ b/genesis-tools/README.md @@ -0,0 +1,40 @@ +# Genesis Tools + +* `genesis-populate` -- tool for creating genesis state dump populated with large number of accounts; +* TODO `genesis-rebase`-- tool for rebasing the entire chain to a new genesis; +* TODO `genesis-mainnet` -- tool for creating the main genesis used at the mainnet launch; + +## `genesis-populate` + +Performance of our node varies drastically depending on the size of the trie it operates with. +As the baseline, we agreed to take the trie roughly equivalent to the trie of Ethereum (as of October 2019) in +its complexity. We achieve it by populating trie with 80M accounts and uploading a smart contract to each of them. +We also make sure the trie does not compress the subtrees due to similar account names. We then use this trie for +benchmarking, loadtesting, and estimating system parameters. + +To start node with 20k accounts first create configs: + +```bash +cargo run --package uncd --bin uncd -- init --test-seed=alice.near --account-id=test.near --fast +``` + +Then create state dump with how many accounts you want: + +```bash +cargo run --package genesis-populate --bin genesis-populate -- --additional-accounts-num=20000 +``` + +This will produce `state_dump` and `genesis_roots` files in home directory of the node, you can also +use `--home` on all commands here to specify an absolute path to a home directory to use. + +Then start a single local node: + +```bash +cargo run --package uncd --bin uncd -- run --boot-nodes= +``` + +## `keypair-generator` + +```bash +cargo run --package keypair-generator --bin keypair-generator -- --home=./ --account-id=miner --generate-config signer-keys --key-type=2 --num-keys=3 +``` diff --git a/genesis-tools/genesis-csv-to-json/Cargo.toml b/genesis-tools/genesis-csv-to-json/Cargo.toml new file mode 100644 index 000000000..67c504c73 --- /dev/null +++ b/genesis-tools/genesis-csv-to-json/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "genesis-csv-to-json" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +chrono.workspace = true +clap.workspace = true +csv.workspace = true +serde.workspace = true + +framework.workspace = true +unc-chain-configs.workspace = true +unc-crypto.workspace = true +unc-primitives.workspace = true +unc-network.workspace = true + +[dev-dependencies] +tempfile.workspace = true +serde_json.workspace = true +serde.workspace = true diff --git a/genesis-tools/genesis-csv-to-json/res/test_accounts.csv b/genesis-tools/genesis-csv-to-json/res/test_accounts.csv new file mode 100644 index 000000000..f49ffe175 --- /dev/null +++ b/genesis-tools/genesis-csv-to-json/res/test_accounts.csv @@ -0,0 +1,3 @@ +genesis_time,account_id,regular_pks,privileged_pks,foundation_pks,full_pks,amount,is_treasury,validator_stake,validator_key,peer_info,smart_contract,lockup,vesting_start,vesting_end,vesting_cliff +2019-12-21T23:00:00Z,alice_near,"ed25519:11111111111111111111111111111111,ed25519:11111111111111111111111111111111",ed25519:11111111111111111111111111111111,ed25519:11111111111111111111111111111111,,1000,false,100,ed25519:11111111111111111111111111111111,ed25519:11111111111111111111111111111111@127.0.0.1:8080,,2019-12-21T23:00:00Z,2019-12-21T22:00:00Z,2019-12-21T23:30:00Z,2019-12-21T22:30:20Z +,bob_near,,ed25519:11111111111111111111111111111111,ed25519:11111111111111111111111111111111,,2000,true,0,,,,2019-12-21T23:00:00Z,,, diff --git a/genesis-tools/genesis-csv-to-json/src/csv_parser.rs b/genesis-tools/genesis-csv-to-json/src/csv_parser.rs new file mode 100644 index 000000000..ab0423cc3 --- /dev/null +++ b/genesis-tools/genesis-csv-to-json/src/csv_parser.rs @@ -0,0 +1,355 @@ +//! Constructs state of token holders from the csv file. +use chrono::DateTime; +use chrono::Utc; +use csv::ReaderBuilder; +use unc_crypto::{KeyType, PublicKey}; +use unc_network::types::PeerInfo; +use unc_primitives::account::{AccessKey, AccessKeyPermission, Account, FunctionCallPermission}; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum}; +use unc_primitives::state_record::StateRecord; +use unc_primitives::transaction::{Action, FunctionCallAction}; +use unc_primitives::types::{AccountId, AccountInfo, Balance, Gas, Power}; +use std::fs::File; +use std::io::Read; +use std::path::PathBuf; + +/// Methods that can be called by a non-privileged access key. +const REGULAR_METHOD_NAMES: &[&str] = &["stake", "transfer"]; +/// Methods that can be called by a privileged access key. +const PRIVILEGED_METHOD_NAMES: &[&str] = &["add_access_key", "remove_access_key"]; +/// Methods that can be called by an access key owned by a "foundation". +const FOUNDATION_METHOD_NAMES: &[&str] = + &["add_access_key", "remove_access_key", "permanently_unstake", "terminate", "init"]; +/// Amount of gas that we pass to run the contract initialization function. +const INIT_GAS: Gas = 1_000_000; + +type Result = std::result::Result>; + +impl Row { + pub fn verify(&self) -> Result<()> { + if self.validator_stake > 0 && self.validator_key.is_none() { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Validator key must be specified if validator stake is not 0.", + ))); + } + if self.validator_stake == 0 && self.validator_key.is_some() { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Validator stake should be greater than 0 if validator stake is specified.", + ))); + } + + match (self.vesting_start, self.vesting_end) { + (None, None) => { + if self.vesting_cliff.is_some() { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Vesting cliff cannot be set without vesting start and vesting end dates.", + ))); + } + } + (Some(ref vesting_start), Some(ref vesting_end)) => { + if vesting_end <= vesting_start { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Vesting end date should be greater than vesting start date.", + ))); + } + if let Some(ref vesting_cliff) = self.vesting_cliff { + if vesting_cliff <= vesting_start { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Vesting cliff date should be greater than vesting start date.", + ))); + } + if vesting_cliff >= vesting_end { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Vesting cliff date should be less than vesting end date.", + ))); + } + } + if let Some(ref lockup) = self.lockup { + if lockup <= vesting_start { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Lockup date should be greater than vesting start date.", + ))); + } + } + } + _ => { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Vesting start and vesting end should be either both set or neither is set.", + ))); + } + } + Ok(()) + } +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +struct Row { + genesis_time: Option>, + account_id: AccountId, + #[serde(with = "crate::serde_with::pks_as_str")] + regular_pks: Vec, + #[serde(with = "crate::serde_with::pks_as_str")] + privileged_pks: Vec, + #[serde(with = "crate::serde_with::pks_as_str")] + foundation_pks: Vec, + #[serde(with = "crate::serde_with::pks_as_str")] + full_pks: Vec, + amount: Balance, + is_treasury: bool, + validator_stake: Balance, + validator_power: Power, + validator_key: Option, + #[serde(with = "crate::serde_with::peer_info_to_str")] + peer_info: Option, + smart_contract: Option, + lockup: Option>, + vesting_start: Option>, + vesting_end: Option>, + vesting_cliff: Option>, +} + +/// Given path to the csv file produces: +/// * `StateRecord`s that represent the state of the token holders; +/// * `AccountInfo`s that represent validators; +/// * `PeerInfo`s that represent boot nodes; +/// * `AccountId` of the treasury. +/// * Genesis time +pub fn keys_to_state_records( + reader: R, + gas_price: Balance, +) -> Result<(Vec, Vec, Vec, AccountId, DateTime)> +where + R: std::io::Read, +{ + let mut reader = ReaderBuilder::new().has_headers(true).from_reader(reader); + + let mut state_records = vec![]; + let mut initial_validators = vec![]; + let mut boot_nodes = vec![]; + let mut treasury = None; + let mut genesis_time = None; + for row in reader.deserialize() { + let row: Row = row?; + row.verify()?; + if row.is_treasury { + if treasury.is_none() { + treasury = Some(row.account_id.clone()); + } else { + panic!("Only one account can be marked as treasury."); + } + } + if row.genesis_time.is_some() { + genesis_time = row.genesis_time; + } + + state_records.extend(account_records(&row, gas_price)); + if let Some(ref validator_key) = row.validator_key { + initial_validators.push(AccountInfo { + account_id: row.account_id.clone(), + public_key: validator_key.clone(), + amount: row.amount, + power: row.validator_power, + locked: row.validator_stake, + }); + } + + if let Some(peer_info) = row.peer_info { + boot_nodes.push(peer_info); + } + } + let treasury = treasury.expect("At least one account should be marked as treasury"); + let genesis_time = genesis_time.expect("Genesis time must be set"); + Ok((state_records, initial_validators, boot_nodes, treasury, genesis_time)) +} + +/// Returns the records representing state of an individual token holder. +fn account_records(row: &Row, gas_price: Balance) -> Vec { + let smart_contract_hash; + let smart_contract_code; + if let Some(ref smart_contract) = row.smart_contract { + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push(smart_contract); + let mut f = File::open(path).expect("Failed to open smart contract file."); + let mut code = vec![]; + f.read_to_end(&mut code).expect("Failed reading smart contract file."); + smart_contract_hash = hash(&code); + smart_contract_code = Some(code); + } else { + smart_contract_hash = CryptoHash::default(); + smart_contract_code = None; + } + + let mut res = vec![StateRecord::Account { + account_id: row.account_id.clone(), + account: Account::new(row.amount, row.validator_stake, row.validator_power, smart_contract_hash, 0), + }]; + + // Add restricted access keys. + for (pks, method_names) in [ + (row.regular_pks.clone(), REGULAR_METHOD_NAMES), + (row.privileged_pks.clone(), PRIVILEGED_METHOD_NAMES), + (row.foundation_pks.clone(), FOUNDATION_METHOD_NAMES), + ] { + for pk in pks { + res.push(StateRecord::AccessKey { + account_id: row.account_id.clone(), + public_key: pk, + access_key: AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: row.account_id.to_string(), + method_names: method_names.iter().map(|x| (*x).to_string()).collect(), + }), + }, + }) + } + } + + // Add full access keys. + for pk in row.full_pks.clone() { + res.push(StateRecord::AccessKey { + account_id: row.account_id.clone(), + public_key: pk, + access_key: AccessKey::full_access(), + }) + } + + // Add smart contract code if was specified. + if let Some(code) = smart_contract_code { + res.push(StateRecord::Contract { account_id: row.account_id.clone(), code }); + } + + // Add init function call if smart contract was provided. + if let Some(ref smart_contract) = row.smart_contract { + let args = match smart_contract.as_str() { + "lockup.wasm" => { + let lockup = row.lockup.unwrap().timestamp_nanos_opt().unwrap(); + lockup.to_le_bytes().to_vec() + } + "lockup_and_vesting.wasm" => { + let mut res = vec![]; + // Encode four dates as timestamps as i64 with LE encoding. + for date in &[&row.lockup, &row.vesting_start, &row.vesting_end, &row.vesting_cliff] + { + let date = date.unwrap().timestamp_nanos_opt().unwrap(); + res.extend_from_slice(&date.to_le_bytes()); + } + res + } + _ => unimplemented!(), + }; + let receipt = Receipt { + predecessor_id: row.account_id.clone(), + receiver_id: row.account_id.clone(), + // `receipt_id` can be anything as long as it is unique. + receipt_id: hash(row.account_id.as_bytes()), + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: row.account_id.clone(), + // `signer_public_key` can be anything because the key checks are not applied when + // a transaction is already converted to a receipt. + signer_public_key: PublicKey::empty(KeyType::ED25519), + gas_price, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "init".to_string(), + args, + gas: INIT_GAS, + deposit: 0, + }))], + }), + }; + res.push(StateRecord::PostponedReceipt(Box::new(receipt))); + } + res +} + +#[cfg(test)] +mod tests { + use std::path::Path; + + use chrono::TimeZone; + use csv::WriterBuilder; + use tempfile::NamedTempFile; + + use unc_crypto::KeyType; + + use super::*; + use unc_primitives::network::PeerId; + + #[test] + fn test_with_file() { + fn timestamp(hour: u32, min: u32, sec: u32) -> Option> { + Some(Utc.with_ymd_and_hms(2019, 12, 21, hour, min, sec).single().unwrap()) + } + + let file = NamedTempFile::new().unwrap(); + let mut writer = WriterBuilder::new().has_headers(true).from_writer(file.reopen().unwrap()); + writer + .serialize(Row { + genesis_time: Some(Utc::now()), + account_id: "alice_near".parse().unwrap(), + regular_pks: vec![ + PublicKey::empty(KeyType::ED25519), + PublicKey::empty(KeyType::ED25519), + ], + privileged_pks: vec![PublicKey::empty(KeyType::ED25519)], + foundation_pks: vec![PublicKey::empty(KeyType::ED25519)], + full_pks: vec![], + amount: 1000, + lockup: timestamp(23, 0, 0), + vesting_start: timestamp(22, 0, 0), + vesting_end: timestamp(23, 30, 0), + vesting_cliff: timestamp(22, 30, 20), + validator_stake: 100, + validator_key: Some(PublicKey::empty(KeyType::ED25519)), + peer_info: Some(PeerInfo { + id: PeerId::new(PublicKey::empty(KeyType::ED25519)), + addr: Some("127.0.0.1:8080".parse().unwrap()), + account_id: None, + }), + is_treasury: false, + smart_contract: None, + }) + .unwrap(); + writer + .serialize(Row { + genesis_time: None, + account_id: "bob_near".parse().unwrap(), + regular_pks: vec![], + privileged_pks: vec![PublicKey::empty(KeyType::ED25519)], + foundation_pks: vec![PublicKey::empty(KeyType::ED25519)], + full_pks: vec![], + amount: 2000, + lockup: timestamp(23, 0, 0), + vesting_start: None, + vesting_end: None, + vesting_cliff: None, + validator_stake: 0, + validator_key: None, + peer_info: None, + is_treasury: true, + smart_contract: None, + }) + .unwrap(); + writer.flush().unwrap(); + keys_to_state_records(file.reopen().unwrap(), 1).unwrap(); + } + + #[test] + fn test_res_file() { + let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("res/test_accounts.csv"); + let res = std::fs::read(path).unwrap(); + keys_to_state_records(&res[..], 1).unwrap(); + } +} diff --git a/genesis-tools/genesis-csv-to-json/src/csv_to_json_configs.rs b/genesis-tools/genesis-csv-to-json/src/csv_to_json_configs.rs new file mode 100644 index 000000000..9b0be3d38 --- /dev/null +++ b/genesis-tools/genesis-csv-to-json/src/csv_to_json_configs.rs @@ -0,0 +1,91 @@ +use unc_chain_configs::{Genesis, GenesisConfig}; +use unc_primitives::types::{Balance, NumShards, ShardId}; +use unc_primitives::utils::get_num_seats_per_shard; +use unc_primitives::version::PROTOCOL_VERSION; +use framework::config::{ + Config, BLOCK_PRODUCER_KICKOUT_THRESHOLD, CHUNK_PRODUCER_KICKOUT_THRESHOLD, CONFIG_FILENAME, + EXPECTED_EPOCH_LENGTH, FISHERMEN_THRESHOLD, GAS_PRICE_ADJUSTMENT_RATE, GENESIS_CONFIG_FILENAME, + INITIAL_GAS_LIMIT, MAX_INFLATION_RATE, MIN_GAS_PRICE, NODE_KEY_FILE, NUM_BLOCKS_PER_YEAR, + NUM_BLOCK_PRODUCER_SEATS, PROTOCOL_REWARD_RATE, PROTOCOL_UPGRADE_STAKE_THRESHOLD, + TRANSACTION_VALIDITY_PERIOD, +}; +use framework::UNC_BASE; +use std::collections::HashSet; +use std::fs::File; +use std::path::Path; + +const ACCOUNTS_FILE: &str = "accounts.csv"; +const SHARDS: &'static [ShardId] = &[0, 1, 2, 3, 4, 5, 6, 7]; + +fn verify_total_supply(total_supply: Balance, chain_id: &str) { + if chain_id == unc_primitives::chains::MAINNET { + assert_eq!( + total_supply, + 1_000_000_000 * UNC_BASE, + "Total supply should be exactly 1 billion" + ); + } else if total_supply > 10_000_000_000 * UNC_BASE + && chain_id == unc_primitives::chains::TESTNET + { + panic!("Total supply should not be more than 10 billion"); + } +} + +/// Generates `config.json` and `genesis.config` from csv files. +/// Verifies that `validator_key.json`, and `node_key.json` are present. +pub fn csv_to_json_configs(home: &Path, chain_id: String, tracked_shards: Vec) { + // Verify that key files exist. + assert!(home.join(NODE_KEY_FILE).as_path().exists(), "Node key file should exist"); + + let shards_set: HashSet<_> = SHARDS.iter().collect(); + if tracked_shards.iter().any(|shard_id| !shards_set.contains(shard_id)) { + panic!("Trying to track a shard that does not exist"); + } + + // Construct `config.json`. + let mut config = Config::default(); + config.tracked_shards = tracked_shards; + + // Construct genesis config. + let (records, validators, peer_info, treasury, genesis_time) = + crate::csv_parser::keys_to_state_records( + File::open(home.join(ACCOUNTS_FILE)).expect("Error opening accounts file."), + MIN_GAS_PRICE, + ) + .expect("Error parsing accounts file."); + config.network.boot_nodes = + peer_info.into_iter().map(|x| x.to_string()).collect::>().join(","); + let genesis_config = GenesisConfig { + protocol_version: PROTOCOL_VERSION, + genesis_time, + chain_id: chain_id.clone(), + num_block_producer_seats: NUM_BLOCK_PRODUCER_SEATS, + num_block_producer_seats_per_shard: get_num_seats_per_shard( + SHARDS.len() as NumShards, + NUM_BLOCK_PRODUCER_SEATS, + ), + avg_hidden_validator_seats_per_shard: SHARDS.iter().map(|_| 0).collect(), + dynamic_resharding: false, + protocol_upgrade_stake_threshold: PROTOCOL_UPGRADE_STAKE_THRESHOLD, + epoch_length: EXPECTED_EPOCH_LENGTH, + gas_limit: INITIAL_GAS_LIMIT, + gas_price_adjustment_rate: GAS_PRICE_ADJUSTMENT_RATE, + block_producer_kickout_threshold: BLOCK_PRODUCER_KICKOUT_THRESHOLD, + validators, + transaction_validity_period: TRANSACTION_VALIDITY_PERIOD, + protocol_reward_rate: PROTOCOL_REWARD_RATE, + max_inflation_rate: MAX_INFLATION_RATE, + num_blocks_per_year: NUM_BLOCKS_PER_YEAR, + protocol_treasury_account: treasury, + chunk_producer_kickout_threshold: CHUNK_PRODUCER_KICKOUT_THRESHOLD, + min_gas_price: MIN_GAS_PRICE, + fishermen_threshold: FISHERMEN_THRESHOLD, + ..Default::default() + }; + let genesis = Genesis::new(genesis_config, records.into()).unwrap(); + verify_total_supply(genesis.config.total_supply, &chain_id); + + // Write all configs to files. + config.write_to_file(&home.join(CONFIG_FILENAME)).expect("Error writing config"); + genesis.to_file(&home.join(GENESIS_CONFIG_FILENAME)); +} diff --git a/genesis-tools/genesis-csv-to-json/src/main.rs b/genesis-tools/genesis-csv-to-json/src/main.rs new file mode 100644 index 000000000..0430f1c38 --- /dev/null +++ b/genesis-tools/genesis-csv-to-json/src/main.rs @@ -0,0 +1,48 @@ +use clap::{Arg, Command}; +use unc_primitives::types::ShardId; +use framework::get_default_home; +use std::collections::HashSet; +use std::path::PathBuf; + +pub mod csv_parser; +pub mod csv_to_json_configs; +pub mod serde_with; + +fn main() { + let matches = Command::new("Genesis CSV to JSON.") + .about("Converts accounts listed in CSV format to JSON configs") + .arg( + Arg::new("home") + .long("home") + .default_value(get_default_home().into_os_string()) + .value_parser(clap::value_parser!(PathBuf)) + .help("Directory for config and data (default \"~/.near\")") + .action(clap::ArgAction::Set), + ) + .arg(Arg::new("chain-id").long("chain-id").action(clap::ArgAction::Set)) + .arg( + Arg::new("tracked-shards") + .long("tracked-shards") + .action(clap::ArgAction::Set) + .help("Set of shards that this node wants to track (default empty)"), + ) + .get_matches(); + + let home_dir = matches.get_one::("home").unwrap(); + let chain_id = matches.get_one::("chain-id").expect("Chain id is requried"); + let tracked_shards: HashSet = match matches.get_one::("tracked-shards") { + Some(s) => { + if s.is_empty() { + HashSet::default() + } else { + s.split(',').map(|v| v.parse::().unwrap()).collect() + } + } + None => HashSet::default(), + }; + csv_to_json_configs::csv_to_json_configs( + home_dir, + chain_id.to_string(), + tracked_shards.into_iter().collect(), + ); +} diff --git a/genesis-tools/genesis-csv-to-json/src/serde_with.rs b/genesis-tools/genesis-csv-to-json/src/serde_with.rs new file mode 100644 index 000000000..e7fe8d977 --- /dev/null +++ b/genesis-tools/genesis-csv-to-json/src/serde_with.rs @@ -0,0 +1,72 @@ +/// `PeerInfo` as `str`. +pub mod peer_info_to_str { + use unc_network::types::PeerInfo; + use serde::{Deserialize, Deserializer, Serializer}; + use std::str::FromStr; + + pub fn serialize(peer_info: &Option, serializer: S) -> Result + where + S: Serializer, + { + match peer_info { + Some(peer_info) => serializer.serialize_str(peer_info.to_string().as_str()), + None => serializer.serialize_str(""), + } + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + if s.is_empty() { + Ok(None) + } else { + Ok(Some(PeerInfo::from_str(s.as_str()).expect("Error deserializing PeerInfo."))) + } + } +} + +/// `Vec` as str. +pub mod pks_as_str { + use unc_crypto::PublicKey; + use serde::{Deserialize, Deserializer, Serializer}; + + pub fn serialize(peer_info: &[PublicKey], serializer: S) -> Result + where + S: Serializer, + { + let res: Vec<_> = peer_info.iter().map(|pk| pk.to_string()).collect(); + serializer.serialize_str(&res.join(",")) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let mut s = String::deserialize(deserializer)?; + if s.is_empty() { + Ok(vec![]) + } else { + s.retain(|c| !c.is_whitespace()); + Ok(s.split(',').map(|c| c.parse().unwrap()).collect()) + } + } +} + +#[cfg(test)] +mod tests { + use unc_crypto::PublicKey; + + #[derive(serde::Serialize, serde::Deserialize)] + struct Test { + #[serde(with = "crate::serde_with::pks_as_str")] + keys: Vec, + } + + #[test] + fn test_deserialize_pubkeys_with_whitespace() { + let test_str = r#"{"keys": "ed25519:EsjyvmBb2ESGiyjPHMBUnTGCe1P6hPjmxxY2b2hrTBAv, ed25519:2djz3u3CjV4dpDZryudwA4JNDcGnVwNtphjZQbUzrhLE, ed25519:2f9Zv5kuyuPM5DCyEP5pSqg58NQ8Ct9uSRerZXnCS9fK,ed25519:3xCFas58RKvD5UpF9GqvEb6q9rvgfbEJPhLf85zc4HpC, ed25519:4588iQsoG9mWjDPLbipQvaGNqo9UCphGsgon8u2yXArE,ed25519:5Me9NjXh3br1Rp2zvqaTUo8qvXcDPZ3YxafewzUKW7zc,\ned25519:93A8upKEMoZG9bBFyXJjQhzcMJBvSHHtPjZP3173FARk"}"#; + serde_json::from_str::(test_str).unwrap(); + } +} diff --git a/genesis-tools/genesis-populate/Cargo.toml b/genesis-tools/genesis-populate/Cargo.toml new file mode 100644 index 000000000..2939c3dda --- /dev/null +++ b/genesis-tools/genesis-populate/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "genesis-populate" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +borsh.workspace = true +clap.workspace = true +indicatif.workspace = true +tempfile.workspace = true + +framework.workspace = true +unc-chain-configs.workspace = true +unc-crypto.workspace = true +unc-epoch-manager.workspace = true +unc-primitives.workspace = true +unc-store.workspace = true +unc-chain.workspace = true +unc-test-contracts.workspace = true +unc-vm-runner.workspace = true + +[features] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-chain/nightly_protocol", + "unc-epoch-manager/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", + "unc-vm-runner/nightly_protocol", + "framework/nightly_protocol", +] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-chain/nightly", + "unc-epoch-manager/nightly", + "unc-primitives/nightly", + "unc-store/nightly", + "unc-vm-runner/nightly", + "framework/nightly", +] diff --git a/genesis-tools/genesis-populate/src/lib.rs b/genesis-tools/genesis-populate/src/lib.rs new file mode 100644 index 000000000..1292aa9c8 --- /dev/null +++ b/genesis-tools/genesis-populate/src/lib.rs @@ -0,0 +1,322 @@ +//! Tools for creating a genesis block. + +pub mod state_dump; + +use crate::state_dump::StateDump; +use indicatif::{ProgressBar, ProgressStyle}; +use unc_chain::types::RuntimeAdapter; +use unc_chain::{Block, Chain, ChainStore}; +use unc_chain_configs::Genesis; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_epoch_manager::types::BlockHeaderInfo; +use unc_epoch_manager::{EpochManager, EpochManagerAdapter, EpochManagerHandle}; +use unc_primitives::account::{AccessKey, Account}; +use unc_primitives::block::{genesis_chunks, Tip}; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::shard_layout::{account_id_to_shard_id, ShardUId}; +use unc_primitives::state_record::StateRecord; +use unc_primitives::types::chunk_extra::ChunkExtra; +use unc_primitives::types::{AccountId, Balance, EpochId, ShardId, StateChangeCause, StateRoot}; +use unc_store::genesis::{compute_storage_usage, initialize_genesis_state}; +use unc_store::{ + get_account, get_genesis_state_roots, set_access_key, set_account, set_code, Store, TrieUpdate, +}; +use unc_vm_runner::ContractCode; +use framework::{UncConfig, NightshadeRuntime}; +use std::collections::BTreeMap; +use std::hash::{Hash, Hasher}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +/// Deterministically construct an account ID by index. +/// +/// This is used by the estimator to fill a DB with many accounts for which the +/// name can be constructed again during estimations. +/// +/// The ids are constructed to form a somewhat interesting shape in the trie. It +/// starts with a hash that will be different for each account, followed by a +/// string that is sufficiently long. The hash is supposed to produces a bunch +/// of branches, whereas the string after that will produce an extension. +/// +/// If anyone has a reason to change the format, there is no strong reason to +/// keep it exactly as it is. But keeping the length of the accounts the same +/// would be desired to avoid breaking tests and estimations. +/// +/// Note that existing estimator DBs need to be reconstructed when the format +/// changes. Daily estimations are not affected by this. +pub fn get_account_id(account_index: u64) -> AccountId { + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + account_index.hash(&mut hasher); + let hash = hasher.finish(); + // Some estimations rely on the account ID length being constant. + // Pad booth numbers to length 20, the longest decimal representation of an u64. + AccountId::try_from(format!("{hash:020}_unc_{account_index:020}")).unwrap() +} + +pub type Result = std::result::Result>; + +pub struct GenesisBuilder { + home_dir: PathBuf, + // We hold this temporary directory to avoid deletion through deallocation. + #[allow(dead_code)] + tmpdir: tempfile::TempDir, + genesis: Arc, + store: Store, + epoch_manager: Arc, + runtime: Arc, + unflushed_records: BTreeMap>, + roots: BTreeMap, + state_updates: BTreeMap, + + // Things that can be set. + additional_accounts_num: u64, + additional_accounts_code: Option>, + additional_accounts_code_hash: CryptoHash, + + print_progress: bool, +} + +impl GenesisBuilder { + pub fn from_config_and_store(home_dir: &Path, config: UncConfig, store: Store) -> Self { + let tmpdir = tempfile::Builder::new().prefix("storage").tempdir().unwrap(); + initialize_genesis_state(store.clone(), &config.genesis, Some(tmpdir.path())); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &config.genesis.config); + let runtime = NightshadeRuntime::from_config( + tmpdir.path(), + store.clone(), + &config, + epoch_manager.clone(), + ); + Self { + home_dir: home_dir.to_path_buf(), + tmpdir, + genesis: Arc::new(config.genesis), + store, + epoch_manager, + runtime, + unflushed_records: Default::default(), + roots: Default::default(), + state_updates: Default::default(), + additional_accounts_num: 0, + additional_accounts_code: None, + additional_accounts_code_hash: CryptoHash::default(), + print_progress: false, + } + } + + pub fn print_progress(mut self) -> Self { + self.print_progress = true; + self + } + + pub fn add_additional_accounts(mut self, num: u64) -> Self { + self.additional_accounts_num = num; + self + } + + pub fn add_additional_accounts_contract(mut self, contract_code: Vec) -> Self { + self.additional_accounts_code_hash = hash(&contract_code); + self.additional_accounts_code = Some(contract_code); + self + } + + pub fn build(mut self) -> Result { + // First, apply whatever is defined by the genesis config. + let roots = get_genesis_state_roots(self.runtime.store())? + .expect("genesis state roots not initialized."); + let genesis_shard_version = self.genesis.config.shard_layout.version(); + self.roots = roots.into_iter().enumerate().map(|(k, v)| (k as u64, v)).collect(); + self.state_updates = self + .roots + .iter() + .map(|(shard_idx, root)| { + ( + *shard_idx, + self.runtime.get_tries().new_trie_update( + ShardUId { version: genesis_shard_version, shard_id: *shard_idx as u32 }, + *root, + ), + ) + }) + .collect(); + self.unflushed_records = + self.roots.keys().cloned().map(|shard_idx| (shard_idx, vec![])).collect(); + + let shard_ids: Vec<_> = self.genesis.config.shard_layout.shard_ids().collect(); + let total_accounts_num = self.additional_accounts_num * shard_ids.len() as u64; + let bar = ProgressBar::new(total_accounts_num as _); + bar.set_style(ProgressStyle::default_bar().template( + "[elapsed {elapsed_precise} remaining {eta_precise}] Writing into storage {bar} {pos:>7}/{len:7}", + )); + // Add records in chunks of 3000 per shard for memory efficiency reasons. + for i in 0..total_accounts_num { + let account_id = get_account_id(i); + self.add_additional_account(account_id)?; + bar.inc(1); + } + + for shard_id in shard_ids { + self.flush_shard_records(shard_id)?; + } + bar.finish(); + self.write_genesis_block()?; + Ok(self) + } + + pub fn dump_state(self) -> Result { + let state_dump = + StateDump { store: self.store.clone(), roots: self.roots.values().cloned().collect() }; + state_dump.save_to_dir(self.home_dir.clone())?; + Ok(self) + } + + fn flush_shard_records(&mut self, shard_idx: ShardId) -> Result<()> { + let records = self.unflushed_records.insert(shard_idx, vec![]).unwrap_or_default(); + if records.is_empty() { + return Ok(()); + } + let mut state_update = + self.state_updates.remove(&shard_idx).expect("State updates are always available"); + let protocol_config = self.runtime.get_protocol_config(&EpochId::default())?; + let storage_usage_config = protocol_config.runtime_config.fees.storage_usage_config; + + // Compute storage usage and update accounts. + for (account_id, storage_usage) in compute_storage_usage(&records, &storage_usage_config) { + let mut account = + get_account(&state_update, &account_id)?.expect("We should've created account"); + account.set_storage_usage(storage_usage); + set_account(&mut state_update, account_id, &account); + } + let tries = self.runtime.get_tries(); + state_update.commit(StateChangeCause::InitialState); + let (_, trie_changes, state_changes) = state_update.finalize()?; + let genesis_shard_version = self.genesis.config.shard_layout.version(); + let shard_uid = ShardUId { version: genesis_shard_version, shard_id: shard_idx as u32 }; + let mut store_update = tries.store_update(); + let root = tries.apply_all(&trie_changes, shard_uid, &mut store_update); + unc_store::flat::FlatStateChanges::from_state_changes(&state_changes) + .apply_to_flat_state(&mut store_update, shard_uid); + store_update.commit()?; + + self.roots.insert(shard_idx, root); + self.state_updates.insert(shard_idx, tries.new_trie_update(shard_uid, root)); + Ok(()) + } + + fn write_genesis_block(&mut self) -> Result<()> { + let shard_ids: Vec<_> = self.genesis.config.shard_layout.shard_ids().collect(); + let genesis_chunks = genesis_chunks( + self.roots.values().cloned().collect(), + &shard_ids, + self.genesis.config.gas_limit, + self.genesis.config.genesis_height, + self.genesis.config.protocol_version, + ); + let genesis = Block::genesis( + self.genesis.config.protocol_version, + genesis_chunks.into_iter().map(|chunk| chunk.take_header()).collect(), + self.genesis.config.genesis_time, + self.genesis.config.genesis_height, + self.genesis.config.min_gas_price, + self.genesis.config.total_supply, + Chain::compute_bp_hash( + self.epoch_manager.as_ref(), + EpochId::default(), + EpochId::default(), + &CryptoHash::default(), + )?, + ); + + let mut store = + ChainStore::new(self.store.clone(), self.genesis.config.genesis_height, true); + let mut store_update = store.store_update(); + + store_update.merge( + self.epoch_manager + .add_validator_proposals(BlockHeaderInfo::new(genesis.header(), 0)) + .unwrap(), + ); + store_update + .save_block_header(genesis.header().clone()) + .expect("save genesis block header shouldn't fail"); + store_update.save_block(genesis.clone()); + + for (chunk_header, state_root) in genesis.chunks().iter().zip(self.roots.values()) { + store_update.save_chunk_extra( + genesis.hash(), + &ShardUId::from_shard_id_and_layout( + chunk_header.shard_id(), + &self.genesis.config.shard_layout, + ), + ChunkExtra::new( + state_root, + CryptoHash::default(), + vec![], + vec![], + 0, + self.genesis.config.gas_limit, + 0, + ), + ); + } + + let head = Tip::from_header(genesis.header()); + store_update.save_head(&head).unwrap(); + store_update.save_final_head(&head).unwrap(); + store_update.commit().unwrap(); + + Ok(()) + } + + fn add_additional_account(&mut self, account_id: AccountId) -> Result<()> { + let testing_init_balance: Balance = 10u128.pow(30); + let testing_init_stake: Balance = 0; + let shard_id = account_id_to_shard_id(&account_id, &self.genesis.config.shard_layout); + let mut records = self.unflushed_records.remove(&shard_id).unwrap_or_default(); + let mut state_update = + self.state_updates.remove(&shard_id).expect("State update should have been added"); + + let signer = + InMemorySigner::from_seed(account_id.clone(), KeyType::ED25519, account_id.as_ref()); + let account = Account::new( + testing_init_balance, + testing_init_stake, + 0, + self.additional_accounts_code_hash, + 0, + ); + set_account(&mut state_update, account_id.clone(), &account); + let account_record = StateRecord::Account { account_id: account_id.clone(), account }; + records.push(account_record); + let access_key_record = StateRecord::AccessKey { + account_id: account_id.clone(), + public_key: signer.public_key.clone(), + access_key: AccessKey::full_access(), + }; + set_access_key( + &mut state_update, + account_id.clone(), + signer.public_key, + &AccessKey::full_access(), + ); + records.push(access_key_record); + if let Some(wasm_binary) = self.additional_accounts_code.as_ref() { + let code = ContractCode::new(wasm_binary.clone(), None); + set_code(&mut state_update, account_id.clone(), &code); + let contract_record = StateRecord::Contract { account_id, code: wasm_binary.clone() }; + records.push(contract_record); + } + + // Add records in chunks of 3000 per shard for memory efficiency reasons. + const CHUNK_SIZE: usize = 3000; + let num_records_to_flush = records.len(); + let needs_flush = num_records_to_flush >= CHUNK_SIZE; + self.unflushed_records.insert(shard_id, records); + self.state_updates.insert(shard_id, state_update); + + if needs_flush { + self.flush_shard_records(shard_id)?; + } + Ok(()) + } +} diff --git a/genesis-tools/genesis-populate/src/main.rs b/genesis-tools/genesis-populate/src/main.rs new file mode 100644 index 000000000..37a77b79e --- /dev/null +++ b/genesis-tools/genesis-populate/src/main.rs @@ -0,0 +1,54 @@ +use clap::{Arg, Command}; +use genesis_populate::GenesisBuilder; +use unc_chain_configs::GenesisValidationMode; +use framework::{get_default_home, load_config}; +use std::path::PathBuf; + +fn main() { + let matches = Command::new("Genesis populator") + .arg( + Arg::new("home") + .long("home") + .default_value(get_default_home().into_os_string()) + .value_parser(clap::value_parser!(PathBuf)) + .help("Directory for config and data (default \"~/.near\")") + .action(clap::ArgAction::Set), + ) + .arg( + Arg::new("additional-accounts-num") + .long("additional-accounts-num") + .required(true) + .action(clap::ArgAction::Set) + .help( + "Number of additional accounts per shard to add directly to the trie \ + (TESTING ONLY)", + ), + ) + .get_matches(); + + let home_dir = matches.get_one::("home").unwrap(); + let additional_accounts_num = matches + .get_one::("additional-accounts-num") + .map(|x| x.parse::().expect("Failed to parse number of additional accounts.")) + .unwrap(); + let unc_config = load_config(home_dir, GenesisValidationMode::Full) + .unwrap_or_else(|e| panic!("Error loading config: {:#}", e)); + + let store = unc_store::NodeStorage::opener( + home_dir, + unc_config.config.archive, + &unc_config.config.store, + None, + ) + .open() + .unwrap() + .get_hot_store(); + GenesisBuilder::from_config_and_store(home_dir, unc_config, store) + .add_additional_accounts(additional_accounts_num) + .add_additional_accounts_contract(unc_test_contracts::trivial_contract().to_vec()) + .print_progress() + .build() + .unwrap() + .dump_state() + .unwrap(); +} diff --git a/genesis-tools/genesis-populate/src/state_dump.rs b/genesis-tools/genesis-populate/src/state_dump.rs new file mode 100644 index 000000000..7ea90bd4b --- /dev/null +++ b/genesis-tools/genesis-populate/src/state_dump.rs @@ -0,0 +1,51 @@ +use borsh::BorshDeserialize; +use unc_primitives::types::StateRoot; +use unc_store::db::TestDB; +use unc_store::Store; +use std::fs::File; +use std::io::Read; +use std::path::{Path, PathBuf}; + +const STATE_DUMP_FILE: &str = "state_dump"; +const GENESIS_ROOTS_FILE: &str = "genesis_roots"; + +pub struct StateDump { + pub store: Store, + pub roots: Vec, +} + +impl StateDump { + pub fn from_dir(dir: &Path, store_home_dir: &Path, in_memory_db: bool, archive: bool) -> Self { + let node_storage = if in_memory_db { + let storage = TestDB::new(); + unc_store::NodeStorage::new(storage) + } else { + unc_store::NodeStorage::opener(store_home_dir, archive, &Default::default(), None) + .open() + .unwrap() + }; + let store = node_storage.get_hot_store(); + let state_file = dir.join(STATE_DUMP_FILE); + store.load_state_from_file(state_file.as_path()).expect("Failed to read state dump"); + let roots_files = dir.join(GENESIS_ROOTS_FILE); + let mut file = File::open(roots_files).expect("Failed to open genesis roots file."); + let mut data = vec![]; + file.read_to_end(&mut data).expect("Failed to read genesis roots file."); + let roots: Vec = + BorshDeserialize::try_from_slice(&data).expect("Failed to deserialize genesis roots"); + Self { store, roots } + } + + pub fn save_to_dir(self, dir: PathBuf) -> std::result::Result<(), Box> { + let mut dump_path = dir.clone(); + dump_path.push(STATE_DUMP_FILE); + self.store.save_state_to_file(dump_path.as_path())?; + { + let mut roots_files = dir; + roots_files.push(GENESIS_ROOTS_FILE); + let file = File::create(roots_files)?; + borsh::to_writer(&file, &self.roots)?; + } + Ok(()) + } +} diff --git a/genesis-tools/keypair-generator/Cargo.toml b/genesis-tools/keypair-generator/Cargo.toml new file mode 100644 index 000000000..9e43659cd --- /dev/null +++ b/genesis-tools/keypair-generator/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "keypair-generator" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +clap.workspace = true + +framework.workspace = true +unc-crypto.workspace = true diff --git a/genesis-tools/keypair-generator/src/main.rs b/genesis-tools/keypair-generator/src/main.rs new file mode 100644 index 000000000..41138b199 --- /dev/null +++ b/genesis-tools/keypair-generator/src/main.rs @@ -0,0 +1,127 @@ +use std::fs; +use std::path::PathBuf; + +use clap::{Arg, Command}; + +use unc_crypto::{InMemorySigner, KeyType, SecretKey, Signer}; +use framework::get_default_home; + +fn generate_key_to_file(account_id: &str, key: SecretKey, path: &PathBuf) -> std::io::Result<()> { + let signer = InMemorySigner::from_secret_key(account_id.parse().unwrap(), key); + signer.write_to_file(path.as_path()) +} + +fn main() { + let matches = Command::new("Key-pairs generator") + .subcommand_required(true) + .arg_required_else_help(true) + .about("Generates: access key-pairs, validation key-pairs, network key-pairs") + .arg( + Arg::new("home") + .long("home") + .default_value(get_default_home().into_os_string()) + .value_parser(clap::value_parser!(PathBuf)) + .help("Directory for config and data (default \"~/.near\")") + .action(clap::ArgAction::Set) + ) + .arg( + Arg::new("account-id") + .long("account-id") + .action(clap::ArgAction::Set) + ) + .arg( + Arg::new("generate-config") + .long("generate-config") + .help("Whether to generate a config file when generating keys. Requires account-id to be specified.") + .action(clap::ArgAction::SetTrue), + ) + .subcommand( + Command::new("signer-keys").about("Generate signer keys.").arg( + Arg::new("num-keys") + .long("num-keys") + .action(clap::ArgAction::Set) + .help("Number of signer keys to generate. (default 3)"), + ) + .arg( + Arg::new("key-type") + .long("key-type") + .action(clap::ArgAction::Set) + .help("what keypairs key-type to generate. (default 0, a.k.a ed25519)"), + ) + ) + .subcommand( + Command::new("node-key").about("Generate key for the node communication."), + ) + .subcommand(Command::new("validator-key").about("Generate staking key.")) + .get_matches(); + + let home_dir = matches.get_one::("home").unwrap(); + fs::create_dir_all(home_dir).expect("Failed to create directory"); + let account_id = matches.get_one::("account-id"); + let generate_config = matches.get_flag("generate-config"); + match matches.subcommand() { + Some(("signer-keys", args)) => { + let key_type: u8 = args.get_one::("key-type") + .map(|x| x.parse().expect("Failed to parse number keys.")) + .unwrap_or(0u8); + let num_keys = args + .get_one::("num-keys") + .map(|x| x.parse().expect("Failed to parse number keys.")) + .unwrap_or(3usize); + let keys: Vec = + (0..num_keys).map(|_| SecretKey::from_random(KeyType::try_from(key_type).unwrap_or(KeyType::ED25519))).collect(); + let mut pks = vec![]; + for (i, key) in keys.into_iter().enumerate() { + println!("Key#{}", i); + println!("PK: {}", key.public_key()); + println!("SK: {}", key); + println!(); + if generate_config { + let account_id = account_id + .expect("Account id must be specified if --generate-config is used"); + let key_file_name = format!("signer{}_key.json", i); + let mut path = home_dir.to_path_buf(); + path.push(&key_file_name); + if let Err(e) = generate_key_to_file(account_id, key.clone(), &path) { + eprintln!("Error writing key to {}: {}", path.display(), e); + return; + } + } + + pks.push(key.public_key()); + } + let pks: Vec<_> = pks.into_iter().map(|pk| pk.to_string()).collect(); + println!("List of public keys:"); + println!("{}", pks.join(",")); + } + Some(("validator-key", _)) => { + let key = SecretKey::from_random(KeyType::ED25519); + println!("PK: {}", key.public_key()); + println!("SK: {}", key); + if generate_config { + let account_id = + account_id.expect("Account id must be specified if --generate-config is used"); + let mut path = home_dir.to_path_buf(); + path.push(framework::config::VALIDATOR_KEY_FILE); + if let Err(e) = generate_key_to_file(account_id, key, &path) { + eprintln!("Error writing key to {}: {}", path.display(), e); + return; + } + } + } + Some(("node-key", _args)) => { + let key = SecretKey::from_random(KeyType::ED25519); + println!("PK: {}", key.public_key()); + println!("PK: {}", key); + if generate_config { + let mut path = home_dir.to_path_buf(); + path.push(framework::config::NODE_KEY_FILE); + if let Err(e) = generate_key_to_file("node", key, &path) { + eprintln!("Error writing key to {}: {}", path.display(), e); + return; + } + } + } + _ => unreachable!(), + } +} diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml new file mode 100644 index 000000000..df75d167d --- /dev/null +++ b/integration-tests/Cargo.toml @@ -0,0 +1,147 @@ +[package] +name = "integration-tests" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix-rt.workspace = true +actix.workspace = true +anyhow.workspace = true +borsh.workspace = true +chrono.workspace = true +clap.workspace = true +futures.workspace = true +hex.workspace = true +itertools.workspace = true +once_cell.workspace = true +parking_lot.workspace = true +primitive-types.workspace = true +rand.workspace = true +rlp.workspace = true +serde.workspace = true +serde_json.workspace = true +smart-default.workspace = true +stdx.workspace = true +strum.workspace = true +tempfile.workspace = true +tokio.workspace = true +tracing.workspace = true +wat.workspace = true + +unc-actix-test-utils.workspace = true +unc-async.workspace = true +unc-chain.workspace = true +unc-chain-configs.workspace = true +unc-chunks.workspace = true +unc-client.workspace = true +unc-client-primitives.workspace = true +unc-crypto.workspace = true +unc-epoch-manager.workspace = true +unc-fmt.workspace = true +unc-jsonrpc.workspace = true +unc-jsonrpc-client.workspace = true +unc-jsonrpc-primitives.workspace = true +unc-network.workspace = true +unc-parameters.workspace = true +unc-primitives.workspace = true +unc-primitives-core.workspace = true +unc-store.workspace = true +unc-o11y.workspace = true +unc-telemetry.workspace = true +unc-test-contracts.workspace = true +unc-performance-metrics.workspace = true +unc-undo-block.workspace = true +unc-vm-runner.workspace = true +unc-wallet-contract.workspace = true +framework.workspace = true +node-runtime.workspace = true +testlib.workspace = true + +[dev-dependencies] +assert_matches.workspace = true +insta.workspace = true + +[features] +performance_stats = [ + "framework/performance_stats", + "unc-network/performance_stats", +] +expensive_tests = [] +test_features = ["framework/test_features", "unc-store/test_features"] +protocol_feature_fix_contract_loading_cost = [ + "framework/protocol_feature_fix_contract_loading_cost", +] +protocol_feature_reject_blocks_with_outdated_protocol_version = [ + "unc-primitives/protocol_feature_reject_blocks_with_outdated_protocol_version", + "unc-chain/protocol_feature_reject_blocks_with_outdated_protocol_version", +] + +nightly = [ + "nightly_protocol", + "protocol_feature_fix_contract_loading_cost", + "protocol_feature_reject_blocks_with_outdated_protocol_version", + "unc-actix-test-utils/nightly", + "unc-async/nightly", + "unc-chain-configs/nightly", + "unc-chain/nightly", + "unc-chunks/nightly", + "unc-client-primitives/nightly", + "unc-client/nightly", + "unc-epoch-manager/nightly", + "unc-fmt/nightly", + "unc-jsonrpc-client/nightly", + "unc-jsonrpc-primitives/nightly", + "unc-jsonrpc/nightly", + "unc-network/nightly", + "unc-o11y/nightly", + "unc-parameters/nightly", + "unc-primitives-core/nightly", + "unc-primitives/nightly", + "unc-store/nightly", + "unc-telemetry/nightly", + "unc-undo-block/nightly", + "unc-vm-runner/nightly", + "unc-wallet-contract/nightly", + "framework/nightly", + "node-runtime/nightly", + "testlib/nightly", +] +nightly_protocol = [ + "unc-actix-test-utils/nightly_protocol", + "unc-async/nightly_protocol", + "unc-chain-configs/nightly_protocol", + "unc-chain/nightly_protocol", + "unc-chunks/nightly_protocol", + "unc-client-primitives/nightly_protocol", + "unc-client/nightly_protocol", + "unc-epoch-manager/nightly_protocol", + "unc-fmt/nightly_protocol", + "unc-jsonrpc-client/nightly_protocol", + "unc-jsonrpc-primitives/nightly_protocol", + "unc-jsonrpc/nightly_protocol", + "unc-network/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-parameters/nightly_protocol", + "unc-primitives-core/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", + "unc-telemetry/nightly_protocol", + "unc-undo-block/nightly_protocol", + "unc-vm-runner/nightly_protocol", + "unc-wallet-contract/nightly_protocol", + "framework/nightly_protocol", + "node-runtime/nightly_protocol", + "testlib/nightly_protocol", +] +sandbox = ["unc-chain/sandbox", "node-runtime/sandbox", "unc-client/sandbox"] +no_cache = ["framework/no_cache"] +calimero_zero_storage = [] +new_epoch_sync = ["framework/new_epoch_sync"] diff --git a/integration-tests/src/genesis_helpers.rs b/integration-tests/src/genesis_helpers.rs new file mode 100644 index 000000000..81ff16c06 --- /dev/null +++ b/integration-tests/src/genesis_helpers.rs @@ -0,0 +1,63 @@ +use unc_epoch_manager::shard_tracker::ShardTracker; +use unc_epoch_manager::EpochManager; +use unc_store::genesis::initialize_genesis_state; +use tempfile::tempdir; + +use unc_chain::types::ChainConfig; +use unc_chain::{Chain, ChainGenesis, DoomslugThresholdMode}; +use unc_chain_configs::Genesis; +use unc_primitives::block::{Block, BlockHeader}; +use unc_primitives::hash::CryptoHash; +use unc_store::test_utils::create_test_store; +use framework::NightshadeRuntime; + +/// Compute genesis hash from genesis. +pub fn genesis_hash(genesis: &Genesis) -> CryptoHash { + *genesis_header(genesis).hash() +} + +/// Utility to generate genesis header from config for testing purposes. +pub fn genesis_header(genesis: &Genesis) -> BlockHeader { + let dir = tempdir().unwrap(); + let store = create_test_store(); + initialize_genesis_state(store.clone(), genesis, None); + let chain_genesis = ChainGenesis::new(genesis); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let shard_tracker = ShardTracker::new_empty(epoch_manager.clone()); + let runtime = + NightshadeRuntime::test(dir.path(), store, &genesis.config, epoch_manager.clone()); + let chain = Chain::new( + epoch_manager, + shard_tracker, + runtime, + &chain_genesis, + DoomslugThresholdMode::TwoThirds, + ChainConfig::test(), + None, + ) + .unwrap(); + chain.genesis().clone() +} + +/// Utility to generate genesis header from config for testing purposes. +pub fn genesis_block(genesis: &Genesis) -> Block { + let dir = tempdir().unwrap(); + let store = create_test_store(); + initialize_genesis_state(store.clone(), genesis, None); + let chain_genesis = ChainGenesis::new(genesis); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let shard_tracker = ShardTracker::new_empty(epoch_manager.clone()); + let runtime = + NightshadeRuntime::test(dir.path(), store, &genesis.config, epoch_manager.clone()); + let chain = Chain::new( + epoch_manager, + shard_tracker, + runtime, + &chain_genesis, + DoomslugThresholdMode::TwoThirds, + ChainConfig::test(), + None, + ) + .unwrap(); + chain.get_block(&chain.genesis().hash().clone()).unwrap() +} diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs new file mode 100644 index 000000000..b08014da4 --- /dev/null +++ b/integration-tests/src/lib.rs @@ -0,0 +1,9 @@ +pub mod genesis_helpers; +pub mod nearcore_utils; +pub mod node; +pub mod runtime_utils; +pub mod test_helpers; +pub mod user; + +#[cfg(test)] +mod tests; diff --git a/integration-tests/src/nearcore_utils.rs b/integration-tests/src/nearcore_utils.rs new file mode 100644 index 000000000..70a692308 --- /dev/null +++ b/integration-tests/src/nearcore_utils.rs @@ -0,0 +1,131 @@ +use crate::genesis_helpers::genesis_block; +use actix::Addr; +use unc_chain::Block; +use unc_chain_configs::Genesis; +use unc_client::{BlockResponse, ClientActor}; +use unc_network::tcp; +use unc_network::test_utils::convert_boot_nodes; +use unc_network::types::PeerInfo; +use unc_o11y::WithSpanContextExt; +use unc_primitives::block::Approval; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::PartialMerkleTree; +use unc_primitives::num_rational::{Ratio, Rational32}; +use unc_primitives::types::{BlockHeightDelta, EpochId}; +use unc_primitives::types::validator_power_and_frozen::ValidatorPowerAndFrozen; +use unc_primitives::validator_signer::ValidatorSigner; +use unc_primitives::version::PROTOCOL_VERSION; +use framework::config::{GenesisExt, TESTING_INIT_POWER, TESTING_INIT_STAKE}; +use framework::{load_test_config, UncConfig}; + +// This assumes that there is no height skipped. Otherwise epoch hash calculation will be wrong. +pub fn add_blocks( + mut blocks: Vec, + client: Addr, + num: usize, + epoch_length: BlockHeightDelta, + signer: &dyn ValidatorSigner, +) -> Vec { + let mut prev = &blocks[blocks.len() - 1]; + let mut block_merkle_tree = PartialMerkleTree::default(); + for block in blocks.iter() { + block_merkle_tree.insert(*block.hash()); + } + for _ in 0..num { + let prev_height = prev.header().height(); + let prev_epoch_height = prev_height / epoch_length; + let prev_epoch_last_block_height = prev_epoch_height * epoch_length; + + let height = prev_height + 1; + let epoch_id = if height <= epoch_length { + EpochId::default() + } else { + let prev_prev_epoch_height = prev_epoch_height - 1; + let prev_prev_epoch_last_block_height = prev_prev_epoch_height * epoch_length; + EpochId(*blocks[prev_prev_epoch_last_block_height as usize].hash()) + }; + + let next_epoch_id = EpochId(*blocks[prev_epoch_last_block_height as usize].hash()); + + let next_bp_hash = CryptoHash::hash_borsh_iter([ValidatorPowerAndFrozen::new( + "other".parse().unwrap(), + signer.public_key(), + TESTING_INIT_POWER, + TESTING_INIT_STAKE, + )]); + let block = Block::produce( + PROTOCOL_VERSION, + PROTOCOL_VERSION, + prev.header(), + prev.header().height() + 1, + prev.header().block_ordinal() + 1, + blocks[0].chunks().iter().cloned().collect(), + epoch_id, + next_epoch_id, + None, + vec![Some(Box::new( + Approval::new( + *prev.hash(), + prev.header().height(), + prev.header().height() + 1, + signer, + ) + .signature, + ))], + Ratio::from_integer(0), + 0, + 1000, + Some(0), + vec![], + vec![], + signer, + next_bp_hash, + block_merkle_tree.root(), + None, + ); + block_merkle_tree.insert(*block.hash()); + let _ = client.do_send( + BlockResponse { + block: block.clone(), + peer_id: PeerInfo::random().id, + was_requested: false, + } + .with_span_context(), + ); + blocks.push(block); + prev = &blocks[blocks.len() - 1]; + } + blocks +} + +pub fn setup_configs_with_epoch_length( + epoch_length: u64, +) -> (Genesis, Block, UncConfig, UncConfig) { + let mut genesis = Genesis::test(vec!["other".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + // Avoid InvalidGasPrice error. Blocks must contain accurate `total_supply` value. + // Accounting for the inflation in tests is hard. + // Disabling inflation in tests is much simpler. + genesis.config.max_inflation_rate = Rational32::from_integer(0); + let genesis_block = genesis_block(&genesis); + + let (port1, port2) = + (tcp::ListenerAddr::reserve_for_test(), tcp::ListenerAddr::reserve_for_test()); + let mut near1 = load_test_config("test1", port1, genesis.clone()); + near1.network_config.peer_store.boot_nodes = convert_boot_nodes(vec![("test2", *port2)]); + near1.client_config.min_num_peers = 1; + near1.client_config.epoch_sync_enabled = false; + near1.client_config.state_sync_enabled = true; + + let mut near2 = load_test_config("test2", port2, genesis.clone()); + near2.network_config.peer_store.boot_nodes = convert_boot_nodes(vec![("test1", *port1)]); + near2.client_config.min_num_peers = 1; + near2.client_config.epoch_sync_enabled = false; + near2.client_config.state_sync_enabled = true; + + (genesis, genesis_block, near1, near2) +} + +pub fn setup_configs() -> (Genesis, Block, UncConfig, UncConfig) { + setup_configs_with_epoch_length(5) +} diff --git a/integration-tests/src/node/mod.rs b/integration-tests/src/node/mod.rs new file mode 100644 index 000000000..342365904 --- /dev/null +++ b/integration-tests/src/node/mod.rs @@ -0,0 +1,193 @@ +use std::sync::Arc; +use std::sync::RwLock; + +pub use crate::node::process_node::ProcessNode; +pub use crate::node::runtime_node::RuntimeNode; +pub use crate::node::thread_node::ThreadNode; +use crate::user::{AsyncUser, User}; +use unc_chain_configs::Genesis; +use unc_crypto::{InMemorySigner, Signer}; +use unc_jsonrpc_primitives::errors::ServerError; +use unc_primitives::num_rational::Ratio; +use unc_primitives::state_record::StateRecord; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{AccountId, Balance, NumSeats}; +use unc_primitives::validator_signer::InMemoryValidatorSigner; +use unc_primitives::views::AccountView; +use unc_vm_runner::ContractCode; +use framework::config::{ + create_testnet_configs, create_testnet_configs_from_seeds, Config, GenesisExt, +}; +use framework::UncConfig; +use testlib::runtime_utils::{alice_account, bob_account}; + +mod process_node; +mod runtime_node; +mod thread_node; + +pub const TEST_BLOCK_FETCH_LIMIT: u64 = 5; +pub const TEST_BLOCK_MAX_SIZE: u32 = 1000; + +pub fn configure_chain_spec() -> Genesis { + Genesis::test(vec![alice_account(), bob_account()], 2) +} + +/// Config that can be used to start a node or connect to an existing node. +#[allow(clippy::large_enum_variant)] +pub enum NodeConfig { + /// A node with only runtime and state that is used to run runtime tests. + Runtime { account_id: AccountId }, + /// A complete node with network, RPC, client, consensus and all tasks running in a thead. + /// Should be the default choice for the tests, since it provides the most control through the + /// internal access. + Thread(UncConfig), + /// A complete noe running in a subprocess. Can be started and stopped, but besides that all + /// interactions are limited to what is exposed through RPC. + Process(UncConfig), +} + +pub trait Node: Send + Sync { + fn genesis(&self) -> &Genesis; + + fn account_id(&self) -> Option; + + fn start(&mut self); + + fn kill(&mut self); + + fn view_account(&self, account_id: &AccountId) -> Result { + self.user().view_account(account_id) + } + + fn get_access_key_nonce_for_signer(&self, account_id: &AccountId) -> Result { + self.user().get_access_key_nonce_for_signer(account_id) + } + + fn view_balance(&self, account_id: &AccountId) -> Result { + self.user().view_balance(account_id) + } + + fn add_transaction(&self, transaction: SignedTransaction) -> Result<(), ServerError> { + self.user().add_transaction(transaction) + } + + fn signer(&self) -> Arc; + + fn block_signer(&self) -> Arc { + unimplemented!() + } + + fn is_running(&self) -> bool; + + fn user(&self) -> Box; + + fn async_user(&self) -> Box { + unimplemented!() + } + + fn as_thread_ref(&self) -> &ThreadNode { + unimplemented!() + } + + fn as_thread_mut(&mut self) -> &mut ThreadNode { + unimplemented!() + } + + fn as_process_ref(&self) -> &ProcessNode { + unimplemented!() + } + + fn as_process_mut(&mut self) -> &mut ProcessNode { + unimplemented!() + } +} + +impl dyn Node { + pub fn new_sharable(config: NodeConfig) -> Arc> { + match config { + NodeConfig::Runtime { account_id } => { + Arc::new(RwLock::new(RuntimeNode::new(&account_id))) + } + NodeConfig::Thread(config) => Arc::new(RwLock::new(ThreadNode::new(config))), + NodeConfig::Process(config) => Arc::new(RwLock::new(ProcessNode::new(config))), + } + } + + pub fn new(config: NodeConfig) -> Box { + match config { + NodeConfig::Runtime { account_id } => Box::new(RuntimeNode::new(&account_id)), + NodeConfig::Thread(config) => Box::new(ThreadNode::new(config)), + NodeConfig::Process(config) => Box::new(ProcessNode::new(config)), + } + } +} + +fn unc_configs_to_node_configs( + configs: Vec, + validator_signers: Vec, + network_signers: Vec, + genesis: Genesis, +) -> Vec { + let mut result = vec![]; + for i in 0..configs.len() { + result.push(NodeConfig::Thread( + UncConfig::new( + configs[i].clone(), + genesis.clone(), + (&network_signers[i]).into(), + Some(Arc::new(validator_signers[i].clone())), + ) + .unwrap(), + )) + } + result +} + +pub fn create_nodes(num_nodes: usize, prefix: &str) -> Vec { + let (configs, validator_signers, network_signers, genesis, _) = + create_testnet_configs(1, num_nodes as NumSeats, 0, prefix, true, false, vec![]); + unc_configs_to_node_configs(configs, validator_signers, network_signers, genesis) +} + +pub fn create_nodes_from_seeds(seeds: Vec) -> Vec { + let code = unc_test_contracts::rs_contract(); + let (configs, validator_signers, network_signers, mut genesis) = + create_testnet_configs_from_seeds(seeds.clone(), 1, 0, true, false, vec![]); + genesis.config.gas_price_adjustment_rate = Ratio::from_integer(0); + for seed in seeds { + let mut found_account_record = false; + let records = genesis.force_read_records().as_mut(); + for record in records.iter_mut() { + if let StateRecord::Account { account_id, account } = record { + if account_id == &seed { + found_account_record = true; + account.set_code_hash(*ContractCode::new(code.to_vec(), None).hash()); + } + } + } + assert!(found_account_record); + records + .push(StateRecord::Contract { account_id: seed.parse().unwrap(), code: code.to_vec() }); + } + unc_configs_to_node_configs(configs, validator_signers, network_signers, genesis) +} + +pub fn sample_two_nodes(num_nodes: usize) -> (usize, usize) { + let i = rand::random::() % num_nodes; + // Should be a different node. + let mut j = rand::random::() % (num_nodes - 1); + if j >= i { + j += 1; + } + (i, j) +} + +/// Sample a node for sending a transaction/checking balance +pub fn sample_queryable_node(nodes: &[Arc>]) -> usize { + let num_nodes = nodes.len(); + let mut k = rand::random::() % num_nodes; + while !nodes[k].read().unwrap().is_running() { + k = rand::random::() % num_nodes; + } + k +} diff --git a/integration-tests/src/node/process_node.rs b/integration-tests/src/node/process_node.rs new file mode 100644 index 000000000..34472affc --- /dev/null +++ b/integration-tests/src/node/process_node.rs @@ -0,0 +1,160 @@ +use std::ops::ControlFlow; +use std::path::PathBuf; +use std::process::{Child, Command}; +use std::sync::Arc; +use std::time::Duration; +use std::{env, thread}; + +use rand::Rng; +use tracing::error; + +use unc_chain_configs::Genesis; +use unc_crypto::{InMemorySigner, KeyType, Signer}; +use unc_primitives::types::AccountId; +use framework::config::UncConfig; + +use crate::node::Node; +use crate::user::rpc_user::RpcUser; +use crate::user::User; +use actix::System; +use unc_jsonrpc_client::new_client; +use unc_network::test_utils::wait_or_timeout; + +pub enum ProcessNodeState { + Stopped, + Running(Child), +} + +pub struct ProcessNode { + pub work_dir: PathBuf, + pub config: UncConfig, + pub state: ProcessNodeState, + pub signer: Arc, +} + +impl Node for ProcessNode { + fn genesis(&self) -> &Genesis { + &self.config.genesis + } + + fn account_id(&self) -> Option { + self.config.validator_signer.as_ref().map(|vs| vs.validator_id().clone()) + } + + fn start(&mut self) { + match self.state { + ProcessNodeState::Stopped => { + std::env::set_var("ADVERSARY_CONSENT", "1"); + let child = + self.get_start_node_command().spawn().expect("start node command failed"); + self.state = ProcessNodeState::Running(child); + let client_addr = format!("http://{}", self.config.rpc_addr().unwrap()); + thread::sleep(Duration::from_secs(3)); + unc_actix_test_utils::run_actix(async move { + wait_or_timeout(1000, 30000, || async { + let res = new_client(&client_addr).status().await; + if res.is_err() { + return ControlFlow::Continue(()); + } + ControlFlow::Break(()) + }) + .await + .unwrap(); + System::current().stop() + }); + } + ProcessNodeState::Running(_) => panic!("Node is already running"), + } + } + + fn kill(&mut self) { + match self.state { + ProcessNodeState::Running(ref mut child) => { + child.kill().expect("kill failed"); + thread::sleep(Duration::from_secs(1)); + self.state = ProcessNodeState::Stopped; + } + ProcessNodeState::Stopped => panic!("Invalid state"), + } + } + + fn signer(&self) -> Arc { + self.signer.clone() + } + + fn is_running(&self) -> bool { + match self.state { + ProcessNodeState::Stopped => false, + ProcessNodeState::Running(_) => true, + } + } + + fn user(&self) -> Box { + let account_id = self.signer.account_id.clone(); + Box::new(RpcUser::new(&self.config.rpc_addr().unwrap(), account_id, self.signer.clone())) + } + + fn as_process_ref(&self) -> &ProcessNode { + self + } + + fn as_process_mut(&mut self) -> &mut ProcessNode { + self + } +} + +impl ProcessNode { + /// Side effect: reset_storage + pub fn new(config: UncConfig) -> ProcessNode { + let mut rng = rand::thread_rng(); + let work_dir = env::temp_dir().join(format!("process_node_{}", rng.gen::())); + let signer = Arc::new(InMemorySigner::from_seed( + config.validator_signer.as_ref().unwrap().validator_id().clone(), + KeyType::ED25519, + config.validator_signer.as_ref().unwrap().validator_id().as_ref(), + )); + let result = ProcessNode { config, work_dir, state: ProcessNodeState::Stopped, signer }; + result.reset_storage(); + result + } + + /// Clear storage directory and run keygen + pub fn reset_storage(&self) { + Command::new("rm").arg("-r").arg(&self.work_dir).spawn().unwrap().wait().unwrap(); + self.config.save_to_dir(&self.work_dir); + } + + /// Side effect: writes chain spec file + pub fn get_start_node_command(&self) -> Command { + if std::env::var("NIGHTLY_RUNNER").is_err() { + let mut command = Command::new("cargo"); + command.args(&["run", "-p", "uncd"]); + #[cfg(feature = "nightly_protocol")] + command.args(&["--features", "nightly_protocol"]); + #[cfg(feature = "nightly")] + command.args(&["--features", "nightly"]); + command.args(&["--bin", "uncd", "--", "--home"]); + command.arg(&self.work_dir); + command.arg("run"); + command + } else { + let mut command = Command::new("target/debug/uncd"); + command.arg("--home"); + command.arg(&self.work_dir); + command.arg("run"); + command + } + } +} + +impl Drop for ProcessNode { + fn drop(&mut self) { + match self.state { + ProcessNodeState::Running(ref mut child) => { + let _ = child.kill().map_err(|_| error!("child process died")); + std::fs::remove_dir_all(&self.work_dir).unwrap(); + } + ProcessNodeState::Stopped => {} + } + } +} diff --git a/integration-tests/src/node/runtime_node.rs b/integration-tests/src/node/runtime_node.rs new file mode 100644 index 000000000..6adbd98cb --- /dev/null +++ b/integration-tests/src/node/runtime_node.rs @@ -0,0 +1,118 @@ +use std::sync::{Arc, RwLock}; + +use unc_chain_configs::Genesis; +use unc_crypto::{InMemorySigner, KeyType, Signer}; +use unc_parameters::RuntimeConfig; +use unc_primitives::types::AccountId; +use framework::config::GenesisExt; +use testlib::runtime_utils::{add_test_contract, alice_account, bob_account, carol_account}; + +use crate::node::Node; +use crate::runtime_utils::get_runtime_and_trie_from_genesis; +use crate::user::runtime_user::MockClient; +use crate::user::{RuntimeUser, User}; + +pub struct RuntimeNode { + pub client: Arc>, + pub signer: Arc, + pub genesis: Genesis, +} + +impl RuntimeNode { + pub fn new(account_id: &AccountId) -> Self { + let mut genesis = Genesis::test(vec![alice_account(), bob_account(), carol_account()], 3); + add_test_contract(&mut genesis, &alice_account()); + add_test_contract(&mut genesis, &bob_account()); + add_test_contract(&mut genesis, &carol_account()); + Self::new_from_genesis(account_id, genesis) + } + + pub fn new_from_genesis_and_config( + account_id: &AccountId, + genesis: Genesis, + runtime_config: RuntimeConfig, + ) -> Self { + let signer = Arc::new(InMemorySigner::from_seed( + account_id.clone(), + KeyType::ED25519, + account_id.as_ref(), + )); + let (runtime, tries, root) = get_runtime_and_trie_from_genesis(&genesis); + let client = Arc::new(RwLock::new(MockClient { + runtime, + tries, + state_root: root, + epoch_length: genesis.config.epoch_length, + runtime_config, + })); + RuntimeNode { signer, client, genesis } + } + + pub fn new_from_genesis(account_id: &AccountId, genesis: Genesis) -> Self { + Self::new_from_genesis_and_config(account_id, genesis, RuntimeConfig::test()) + } + + pub fn free(account_id: &AccountId) -> Self { + let mut genesis = + Genesis::test(vec![alice_account(), bob_account(), "carol.near".parse().unwrap()], 3); + add_test_contract(&mut genesis, &bob_account()); + Self::new_from_genesis_and_config(account_id, genesis, RuntimeConfig::free()) + } +} + +impl Node for RuntimeNode { + fn genesis(&self) -> &Genesis { + &self.genesis + } + + fn account_id(&self) -> Option { + Some(self.signer.account_id.clone()) + } + + fn start(&mut self) {} + + fn kill(&mut self) {} + + fn signer(&self) -> Arc { + self.signer.clone() + } + + fn block_signer(&self) -> Arc { + self.signer.clone() + } + + fn is_running(&self) -> bool { + true + } + + fn user(&self) -> Box { + Box::new(RuntimeUser::new( + self.signer.account_id.clone(), + self.signer.clone(), + self.client.clone(), + )) + } +} + +#[cfg(test)] +mod tests { + use crate::node::runtime_node::RuntimeNode; + use crate::node::Node; + use testlib::fees_utils::FeeHelper; + use testlib::runtime_utils::{alice_account, bob_account}; + + #[test] + pub fn test_send_money() { + let (alice, bob) = (alice_account(), bob_account()); + let node = RuntimeNode::new(&alice); + let node_user = node.user(); + let (alice1, bob1) = (node.view_balance(&alice).unwrap(), node.view_balance(&bob).unwrap()); + node_user.send_money(alice.clone(), bob.clone(), 1).unwrap(); + let runtime_config = node.client.as_ref().read().unwrap().runtime_config.clone(); + let fee_helper = FeeHelper::new(runtime_config, node.genesis().config.min_gas_price); + let transfer_cost = fee_helper.transfer_cost(); + let (alice2, bob2) = (node.view_balance(&alice).unwrap(), node.view_balance(&bob).unwrap()); + assert_eq!(alice2, alice1 - 1 - transfer_cost); + assert_eq!(bob2, bob1 + 1); + } +} diff --git a/integration-tests/src/node/thread_node.rs b/integration-tests/src/node/thread_node.rs new file mode 100644 index 000000000..b0bb84c9a --- /dev/null +++ b/integration-tests/src/node/thread_node.rs @@ -0,0 +1,97 @@ +use std::path::PathBuf; +use std::sync::Arc; + +use unc_actix_test_utils::ShutdownableThread; +use unc_chain_configs::Genesis; +use unc_crypto::{InMemorySigner, KeyType, Signer}; +use unc_primitives::types::AccountId; +use framework::{start_with_config, UncConfig}; + +use crate::node::Node; +use crate::user::rpc_user::RpcUser; +use crate::user::User; + +pub enum ThreadNodeState { + Stopped, + Running(ShutdownableThread), +} + +pub struct ThreadNode { + pub config: UncConfig, + pub state: ThreadNodeState, + pub signer: Arc, + pub dir: tempfile::TempDir, +} + +fn start_thread(config: UncConfig, path: PathBuf) -> ShutdownableThread { + ShutdownableThread::start("test", move || { + start_with_config(&path, config).expect("start_with_config"); + }) +} + +impl Node for ThreadNode { + fn genesis(&self) -> &Genesis { + &self.config.genesis + } + + fn account_id(&self) -> Option { + self.config.validator_signer.as_ref().map(|vs| vs.validator_id().clone()) + } + + fn start(&mut self) { + let handle = start_thread(self.config.clone(), self.dir.path().to_path_buf()); + self.state = ThreadNodeState::Running(handle); + } + + fn kill(&mut self) { + let state = std::mem::replace(&mut self.state, ThreadNodeState::Stopped); + match state { + ThreadNodeState::Stopped => panic!("Node is not running"), + ThreadNodeState::Running(handle) => { + handle.shutdown(); + self.state = ThreadNodeState::Stopped; + } + } + } + + fn signer(&self) -> Arc { + self.signer.clone() + } + + fn is_running(&self) -> bool { + match self.state { + ThreadNodeState::Stopped => false, + ThreadNodeState::Running(_) => true, + } + } + + fn user(&self) -> Box { + let account_id = self.signer.account_id.clone(); + Box::new(RpcUser::new(&self.config.rpc_addr().unwrap(), account_id, self.signer.clone())) + } + + fn as_thread_ref(&self) -> &ThreadNode { + self + } + + fn as_thread_mut(&mut self) -> &mut ThreadNode { + self + } +} + +impl ThreadNode { + /// Side effects: create storage, open database, lock database + pub fn new(config: UncConfig) -> ThreadNode { + let signer = Arc::new(InMemorySigner::from_seed( + config.validator_signer.as_ref().unwrap().validator_id().clone(), + KeyType::ED25519, + config.validator_signer.as_ref().unwrap().validator_id().as_ref(), + )); + ThreadNode { + config, + state: ThreadNodeState::Stopped, + signer, + dir: tempfile::Builder::new().prefix("thread_node").tempdir().unwrap(), + } + } +} diff --git a/integration-tests/src/runtime_utils.rs b/integration-tests/src/runtime_utils.rs new file mode 100644 index 000000000..98745d8db --- /dev/null +++ b/integration-tests/src/runtime_utils.rs @@ -0,0 +1,70 @@ +use std::collections::HashSet; + +use unc_chain_configs::Genesis; +use unc_parameters::RuntimeConfig; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::state_record::{state_record_to_account_id, StateRecord}; +use unc_primitives::types::AccountId; +use unc_primitives::types::StateRoot; +use unc_primitives_core::types::NumShards; +use unc_store::genesis::GenesisStateApplier; +use unc_store::test_utils::TestTriesBuilder; +use unc_store::{ShardTries, TrieUpdate}; +use framework::config::GenesisExt; +use node_runtime::{state_viewer::TrieViewer, Runtime}; +use testlib::runtime_utils::{add_test_contract, alice_account, bob_account}; + +pub const TEST_SHARD_UID: ShardUId = ShardUId { version: 1, shard_id: 0 }; + +pub fn get_runtime_and_trie() -> (Runtime, ShardTries, StateRoot) { + let mut genesis = Genesis::test_sharded_new_version( + vec![alice_account(), bob_account(), "carol.near".parse().unwrap()], + 3, + vec![3], + ); + add_test_contract(&mut genesis, &"test.contract".parse().unwrap()); + get_runtime_and_trie_from_genesis(&genesis) +} + +pub fn get_test_trie_viewer() -> (TrieViewer, TrieUpdate) { + let (_, tries, root) = get_runtime_and_trie(); + let trie_viewer = TrieViewer::default(); + let state_update = tries.new_trie_update(TEST_SHARD_UID, root); + (trie_viewer, state_update) +} + +pub fn get_runtime_and_trie_from_genesis(genesis: &Genesis) -> (Runtime, ShardTries, StateRoot) { + let shard_layout = &genesis.config.shard_layout; + let tries = TestTriesBuilder::new() + .with_shard_layout(shard_layout.version(), shard_layout.shard_ids().count() as NumShards) + .with_flat_storage() + .build(); + let runtime = Runtime::new(); + let mut account_ids: HashSet = HashSet::new(); + genesis.for_each_record(|record: &StateRecord| { + account_ids.insert(state_record_to_account_id(record).clone()); + }); + let writers = std::sync::atomic::AtomicUsize::new(0); + let storage_usage_config = &RuntimeConfig::test().fees.storage_usage_config; + let genesis_root = GenesisStateApplier::apply( + &writers, + tries.clone(), + 0, + &genesis + .config + .validators + .iter() + .map(|account_info| { + ( + account_info.account_id.clone(), + account_info.public_key.clone(), + account_info.amount, + ) + }) + .collect::>(), + storage_usage_config, + genesis, + account_ids, + ); + (runtime, tries, genesis_root) +} diff --git a/integration-tests/src/test_helpers.rs b/integration-tests/src/test_helpers.rs new file mode 100644 index 000000000..d2a5a90d4 --- /dev/null +++ b/integration-tests/src/test_helpers.rs @@ -0,0 +1,62 @@ +use crate::node::Node; +use once_cell::sync::Lazy; +use std::process::Output; +use std::sync::{Arc, Mutex, RwLock}; +use std::thread; +use std::time::Duration; + +static HEAVY_TESTS_LOCK: Lazy> = Lazy::new(|| Mutex::new(())); + +pub fn heavy_test(f: F) +where + F: FnOnce(), +{ + let _guard = HEAVY_TESTS_LOCK.lock(); + f(); +} + +pub fn check_result(output: Output) -> Result { + let mut result = String::from_utf8_lossy(output.stdout.as_slice()); + if !output.status.success() { + if result.is_empty() { + result = String::from_utf8_lossy(output.stderr.as_slice()); + } + return Err(result.into_owned()); + } + Ok(result.into_owned()) +} + +pub fn wait(mut f: F, check_interval_ms: u64, max_wait_ms: u64) +where + F: FnMut() -> bool, +{ + let mut ms_slept = 0; + while !f() { + thread::sleep(Duration::from_millis(check_interval_ms)); + ms_slept += check_interval_ms; + if ms_slept > max_wait_ms { + println!("BBBB Slept {}; max_wait_ms {}", ms_slept, max_wait_ms); + panic!("Timed out waiting for the condition"); + } + } +} + +/// TODO it makes sense to have three types of wait checks: +/// Wait until sufficient number of nodes is caught up (> 2/3). This can be checked by looking at the block heights and verifying that the blocks are produced; +/// Wait until a certain node is caught up and participating in a consensus. Check first-layer BLS signatures; +/// Wait until all nodes are more-or-less caught up. Check that the max_height - min_height < threshold; +/// +pub fn wait_for_catchup(nodes: &[Arc>]) { + wait( + || { + let tips: Vec<_> = nodes + .iter() + .filter(|node| node.read().unwrap().is_running()) + .map(|node| node.read().unwrap().user().get_best_height()) + .collect(); + tips.iter().min() == tips.iter().max() + }, + 1000, + 10000, + ); +} diff --git a/integration-tests/src/tests/client/benchmarks.rs b/integration-tests/src/tests/client/benchmarks.rs new file mode 100644 index 000000000..0ed9e7059 --- /dev/null +++ b/integration-tests/src/tests/client/benchmarks.rs @@ -0,0 +1,65 @@ +//! High-level benchmarks for client. +//! +//! We are not using criterion or cargo-bench infrastructure because the things +//! we want to test here are pretty heavy and its enough to run them once and +//! note the wall-clock time. + +use unc_chain::ChainGenesis; +use unc_chain_configs::Genesis; +use unc_client::test_utils::{create_chunk_on_height, TestEnv}; +use unc_client::ProcessTxResponse; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_primitives::transaction::{Action, DeployContractAction, SignedTransaction}; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; + +/// How long does it take to produce a large chunk? +/// +/// Chunk production work does **not** include any transaction execution: it is +/// just about packing receipts and a bunch of txes from a pool together and +/// computing merkle proof and erasure-codes for this. This is still pretty +/// computationally intensive. +/// +/// In ths benchmark, we construct a large with a bunch of deploy_code txes +#[test] +fn benchmark_large_chunk_production_time() { + let mb = 1024usize.pow(2); + + let n_txes = 20; + let tx_size = 3 * mb; + + let genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let account_id = env.get_client_id(0).clone(); + let signer = + InMemorySigner::from_seed(account_id.clone(), KeyType::ED25519, account_id.as_ref()); + let last_block_hash = env.clients[0].chain.head().unwrap().last_block_hash; + for i in 0..n_txes { + let tx = SignedTransaction::from_actions( + i + 1, + account_id.clone(), + account_id.clone(), + &signer, + vec![Action::DeployContract(DeployContractAction { code: vec![92; tx_size] })], + last_block_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + let t = std::time::Instant::now(); + let (chunk, _, _) = create_chunk_on_height(&mut env.clients[0], 0); + let time = t.elapsed(); + + let size = borsh::object_length(&chunk).unwrap(); + eprintln!("chunk size: {}kb", size / 1024); + eprintln!("time to produce: {:0.2?}", time); + + // Check that we limit the size of the chunk and not include all `n_txes` + // transactions in the chunk. + assert!(30 * mb < size && size < 40 * mb, "{size}"); +} diff --git a/integration-tests/src/tests/client/block_corruption.rs b/integration-tests/src/tests/client/block_corruption.rs new file mode 100644 index 000000000..9e99dd503 --- /dev/null +++ b/integration-tests/src/tests/client/block_corruption.rs @@ -0,0 +1,284 @@ +use anyhow::Context; +use borsh::BorshDeserialize; +use unc_chain::{Block, ChainGenesis, Error, Provenance}; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_client::ProcessTxResponse; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::sharding::{ShardChunkHeader, ShardChunkHeaderInner}; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::validator_signer::InMemoryValidatorSigner; +use unc_primitives_core::types::BlockHeight; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; + +const NOT_BREAKING_CHANGE_MSG: &str = "Not a breaking change"; +const BLOCK_NOT_PARSED_MSG: &str = "Corrupt block didn't parse"; + +fn create_tx_load(height: BlockHeight, last_block: &Block) -> Vec { + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let tx = SignedTransaction::send_money( + height + 10000, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 10, + *last_block.hash(), + ); + vec![tx] +} + +/// Producing block with all `shard_id`s equal to 100. +/// Expecting it to fail processing. +#[test] +fn change_shard_id_to_invalid() { + init_test_logger(); + let epoch_length = 5000000; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let mut last_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + + // Produce normal block + + let txs = create_tx_load(1, &last_block); + for tx in txs { + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + let block = env.clients[0].produce_block(1).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + last_block = block; + + // Produce block for corruption + + let txs = create_tx_load(2, &last_block); + for tx in txs { + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + let mut block = env.clients[0].produce_block(2).unwrap().unwrap(); + + // 1. Corrupt chunks + let mut new_chunks = vec![]; + for chunk in block.chunks().iter() { + let mut new_chunk = chunk.clone(); + match &mut new_chunk { + ShardChunkHeader::V1(new_chunk) => new_chunk.inner.shard_id = 100, + ShardChunkHeader::V2(new_chunk) => new_chunk.inner.shard_id = 100, + ShardChunkHeader::V3(new_chunk) => match &mut new_chunk.inner { + ShardChunkHeaderInner::V1(inner) => inner.shard_id = 100, + ShardChunkHeaderInner::V2(inner) => inner.shard_id = 100, + }, + }; + new_chunks.push(new_chunk); + } + block.set_chunks(new_chunks); + + // 2. Rehash and resign + block.mut_header().get_mut().inner_rest.block_body_hash = + block.compute_block_body_hash().unwrap(); + block.mut_header().resign(&InMemoryValidatorSigner::from_seed( + "test0".parse().unwrap(), + KeyType::ED25519, + "test0", + )); + + // Try to process corrupt block and expect code to notice invalid shard_id + let res = env.clients[0].process_block_test(block.into(), Provenance::NONE); + match res { + Err(Error::InvalidShardId(100)) => { + tracing::debug!("process failed successfully"); + } + Err(e) => { + assert!(false, "Failed with error {:?} instead of Error::InvalidShardId(100).", e); + } + Ok(_) => { + assert!(false, "Block processing of a corrupt block succeeded."); + } + } +} + +/// We ensure there are SOME change between these blocks. +/// We check that they are NOT in fields that can be mutated and still pass validation by design. +fn is_breaking_block_change(original: &Block, corrupt: &Block) -> bool { + original.header().latest_protocol_version() == corrupt.header().latest_protocol_version() + && original.header().block_ordinal() == corrupt.header().block_ordinal() + && original.header().timestamp() == corrupt.header().timestamp() + && original != corrupt +} + +/// If the corrupt block can be parsed, we are trying to process it and expecting it to fail. +/// If it successfully fails, we are processing the correct block and expecting it to be ok. +/// +/// Returns `Ok(processing_error)` if corrupt block was parsed and its processing failed. +/// Returns `Ok(reason)` if corrupt block wasn't parsed or had changes that are not breaking by design. +/// Returns `Err(reason)` if corrupt block was processed or correct block wasn't processed afterwards. +fn check_corrupt_block( + mut env: TestEnv, + corrupt_block_vec: Vec, + correct_block: Block, + corrupted_bit_idx: usize, +) -> Result { + if let Ok(mut corrupt_block) = Block::try_from_slice(corrupt_block_vec.as_slice()) { + corrupt_block.mut_header().get_mut().inner_rest.block_body_hash = + corrupt_block.compute_block_body_hash().unwrap(); + corrupt_block.mut_header().resign(&InMemoryValidatorSigner::from_seed( + "test0".parse().unwrap(), + KeyType::ED25519, + "test0", + )); + + if !is_breaking_block_change(&correct_block, &corrupt_block) { + return Ok(anyhow::anyhow!(NOT_BREAKING_CHANGE_MSG)); + } + + match env.clients[0].process_block_test(corrupt_block.into(), Provenance::NONE) { + Ok(_) => Err(anyhow::anyhow!( + "Was able to process default block with {} bit switched.", + corrupted_bit_idx + )), + Err(e) => { + if let Err(e) = + env.clients[0].process_block_test(correct_block.into(), Provenance::NONE) + { + return Err(anyhow::anyhow!("Was unable to process default block after attempting to process default block with {} bit switched. {}", + corrupted_bit_idx, e) + ); + } + Ok(e.into()) + } + } + } else { + return Ok(anyhow::anyhow!(BLOCK_NOT_PARSED_MSG)); + } +} + +/// Each block contains one 'send' transaction. +/// First three blocks are produced without changes. +/// For the fourth block we are calculating two versions – correct and corrupt. +/// Corrupt block is produced by +/// - serializing correct block +/// - flipping one bit +/// - deserializing resulting string +/// - resigning the resulting block +/// If the corrupt block can be parsed, we are trying to process it and expecting it to fail. +/// If it successfully fails, we are processing the correct block and expecting it to be ok. +/// +/// Returns `Ok(processing_error)` if corrupt block was parsed and its processing failed. +/// Returns `Ok(reason)` if corrupt block wasn't parsed or had changes that are not breaking by design. +/// Returns `Err(reason)` if corrupt block was processed or correct block wasn't processed afterwards. +fn check_process_flipped_block_fails_on_bit( + corrupted_bit_idx: usize, +) -> Result { + let epoch_length = 5000000; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut env = TestEnv::builder(ChainGenesis::new(&genesis)) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let mut last_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + + let mid_height = 3; + for h in 1..=mid_height { + let txs = create_tx_load(h, &last_block); + for tx in txs { + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + let block = env.clients[0].produce_block(h).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + last_block = block; + } + + let h = mid_height + 1; + + let txs = create_tx_load(h, &last_block); + for tx in txs { + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + let correct_block = env.clients[0] + .produce_block(h) + .unwrap() + .with_context(|| format!("Failed to produce block at height {:?}", h)) + .unwrap(); + + let mut block_vec = borsh::to_vec(&correct_block).unwrap(); + + if corrupted_bit_idx >= block_vec.len() * 8 { + return Ok(anyhow::anyhow!("End")); + } + + // get index of BYTE that we are changing + let byte_idx = corrupted_bit_idx / 8; + // get index of BIT in that BYTE that we are flipping + let bit_idx = corrupted_bit_idx % 8; + // flip that BIT + block_vec[byte_idx] ^= 1 << bit_idx; + + check_corrupt_block(env, block_vec, correct_block, corrupted_bit_idx) +} + +/// Produce a block. Flip a bit in it. +/// Check that corrupt block cannot be processed. Check that correct block can be processed after. +/// Do it for every bit in the block independently. +/// +/// Checks are performed for all bits even if some of them fail. +/// Results are accumulated in `errs` and `oks` vectors, that are printed at the end of the test. +/// Test fails if +/// - `errs` is not empty which means that for some bit either corrupt block was processed, or correct block was not. +/// - `oks` only contain trivial errors which means that nothing was checked actually. +/// +/// `oks` are printed to check the sanity of the test. +/// This vector should include various validation errors that correspond to data changed with a bit flip. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn check_process_flipped_block_fails() { + init_test_logger(); + let mut corrupted_bit_idx = 0; + // List of reasons `check_process_flipped_block_fails_on_bit` returned `Err`. + // Should be empty. + let mut errs = vec![]; + // List of reasons `check_process_flipped_block_fails_on_bit` returned `Ok`. + // Should contain various validation errors. + let mut oks = vec![]; + loop { + let res = check_process_flipped_block_fails_on_bit(corrupted_bit_idx); + if let Ok(res) = &res { + if res.to_string() == "End" { + // `corrupted_bit_idx` is out of bounds for correct block length. Should stop iteration. + break; + } + } + match res { + Err(e) => errs.push(e), + Ok(o) => oks.push(o), + } + corrupted_bit_idx += 1; + } + tracing::info!("All of the Errors:"); + for err in &errs { + tracing::info!("{:?}", err); + } + tracing::info!("{}", ["-"; 100].concat()); + tracing::info!("All of the Oks:"); + for ok in &oks { + tracing::info!("{:?}", ok); + } + assert!(errs.is_empty()); + assert!( + oks.iter() + .filter(|e| e.to_string() != NOT_BREAKING_CHANGE_MSG + && e.to_string() != BLOCK_NOT_PARSED_MSG) + .count() + > 0 + ); +} diff --git a/integration-tests/src/tests/client/challenges.rs b/integration-tests/src/tests/client/challenges.rs new file mode 100644 index 000000000..612f5c6d4 --- /dev/null +++ b/integration-tests/src/tests/client/challenges.rs @@ -0,0 +1,525 @@ +use assert_matches::assert_matches; +use unc_chain::validate::validate_challenge; +use unc_chain::{Block, ChainGenesis, ChainStoreAccess, Error, Provenance}; +use unc_chain_configs::Genesis; +use unc_chunks::ShardsManager; +use unc_client::test_utils::{create_chunk, create_chunk_with_transactions, TestEnv}; +use unc_client::{Client, ProcessTxResponse}; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_network::types::NetworkRequests; +use unc_primitives::challenge::{ + BlockDoubleSign, Challenge, ChallengeBody, ChunkProofs, MaybeEncodedShardChunk, PartialState, + TrieValue, +}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::{MerklePath, PartialMerkleTree}; +use unc_primitives::num_rational::Ratio; +use unc_primitives::receipt::Receipt; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::sharding::{EncodedShardChunk, ReedSolomonWrapper}; +use unc_primitives::test_utils::create_test_signer; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::chunk_extra::ChunkExtra; +use unc_primitives::types::AccountId; +use unc_primitives::validator_signer::ValidatorSigner; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_store::Trie; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; +use std::sync::Arc; + +/// Check that block containing a challenge is rejected. +/// TODO (#2445): Enable challenges when they are working correctly. +#[test] +fn test_block_with_challenges() { + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + let genesis = env.clients[0].chain.get_block_by_height(0).unwrap(); + + let mut block = env.clients[0].produce_block(1).unwrap().unwrap(); + let signer = env.clients[0].validator_signer.as_ref().unwrap().clone(); + + { + let challenge_body = ChallengeBody::BlockDoubleSign(BlockDoubleSign { + left_block_header: borsh::to_vec(&genesis.header()).unwrap(), + right_block_header: borsh::to_vec(&genesis.header()).unwrap(), + }); + let challenge = Challenge::produce(challenge_body, &*signer); + let challenges = vec![challenge]; + match &mut block { + Block::BlockV1(_) => unreachable!(), + Block::BlockV2(body) => { + let body = Arc::make_mut(body); + body.challenges = challenges.clone(); + } + Block::BlockV3(body) => { + let body = Arc::make_mut(body); + body.body.challenges = challenges.clone(); + } + }; + let block_body_hash = block.compute_block_body_hash().unwrap(); + block.mut_header().get_mut().inner_rest.block_body_hash = block_body_hash; + block.mut_header().get_mut().inner_rest.challenges_root = + Block::compute_challenges_root(&challenges); + block.mut_header().resign(&*signer); + } + + let result = env.clients[0].process_block_test(block.into(), Provenance::NONE); + assert_matches!(result.unwrap_err(), Error::InvalidChallengeRoot); +} + +/// Check that attempt to process block on top of incorrect state root leads to InvalidChunkState error. +#[test] +fn test_invalid_chunk_state() { + let genesis = Genesis::test(vec!["test0".parse().unwrap()], 1); + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + env.produce_block(0, 1); + let block_hash = env.clients[0].chain.get_block_hash_by_height(1).unwrap(); + + { + let mut chunk_extra = ChunkExtra::clone( + &env.clients[0].chain.get_chunk_extra(&block_hash, &ShardUId::single_shard()).unwrap(), + ); + let store = env.clients[0].chain.mut_chain_store(); + let mut store_update = store.store_update(); + assert_ne!(chunk_extra.state_root(), &Trie::EMPTY_ROOT); + *chunk_extra.state_root_mut() = Trie::EMPTY_ROOT; + store_update.save_chunk_extra(&block_hash, &ShardUId::single_shard(), chunk_extra); + store_update.commit().unwrap(); + } + + let block = env.clients[0].produce_block(2).unwrap().unwrap(); + let result = env.clients[0].process_block_test(block.into(), Provenance::NONE); + assert_matches!(result.unwrap_err(), Error::InvalidChunkState(_)); +} + +#[test] +fn test_verify_block_double_sign_challenge() { + let mut env = TestEnv::builder(ChainGenesis::test()).clients_count(2).build(); + env.produce_block(0, 1); + let genesis = env.clients[0].chain.get_block_by_height(0).unwrap(); + let b1 = env.clients[0].produce_block(2).unwrap().unwrap(); + + env.process_block(0, b1.clone(), Provenance::NONE); + + let signer = create_test_signer("test0"); + let mut block_merkle_tree = PartialMerkleTree::default(); + block_merkle_tree.insert(*genesis.hash()); + let b2 = Block::produce( + PROTOCOL_VERSION, + PROTOCOL_VERSION, + genesis.header(), + 2, + genesis.header().block_ordinal() + 1, + genesis.chunks().iter().cloned().collect(), + b1.header().epoch_id().clone(), + b1.header().next_epoch_id().clone(), + None, + vec![], + Ratio::from_integer(0), + 0, + 100, + None, + vec![], + vec![], + &signer, + *b1.header().next_bp_hash(), + block_merkle_tree.root(), + None, + ); + let epoch_id = b1.header().epoch_id().clone(); + let valid_challenge = Challenge::produce( + ChallengeBody::BlockDoubleSign(BlockDoubleSign { + left_block_header: borsh::to_vec(&b2.header()).unwrap(), + right_block_header: borsh::to_vec(&b1.header()).unwrap(), + }), + &signer, + ); + assert_eq!( + &validate_challenge( + env.clients[1].chain.epoch_manager.as_ref(), + env.clients[1].chain.runtime_adapter.as_ref(), + &epoch_id, + genesis.hash(), + &valid_challenge + ) + .unwrap() + .0, + if b1.hash() > b2.hash() { b1.hash() } else { b2.hash() } + ); + let invalid_challenge = Challenge::produce( + ChallengeBody::BlockDoubleSign(BlockDoubleSign { + left_block_header: borsh::to_vec(&b1.header()).unwrap(), + right_block_header: borsh::to_vec(&b1.header()).unwrap(), + }), + &signer, + ); + assert!(validate_challenge( + env.clients[1].chain.epoch_manager.as_ref(), + env.clients[1].chain.runtime_adapter.as_ref(), + &epoch_id, + genesis.hash(), + &invalid_challenge, + ) + .is_err()); + let b3 = env.clients[0].produce_block(3).unwrap().unwrap(); + let invalid_challenge = Challenge::produce( + ChallengeBody::BlockDoubleSign(BlockDoubleSign { + left_block_header: borsh::to_vec(&b1.header()).unwrap(), + right_block_header: borsh::to_vec(&b3.header()).unwrap(), + }), + &signer, + ); + assert!(validate_challenge( + env.clients[1].chain.epoch_manager.as_ref(), + env.clients[1].chain.runtime_adapter.as_ref(), + &epoch_id, + genesis.hash(), + &invalid_challenge, + ) + .is_err()); + + let result = env.clients[0].process_block_test(b2.into(), Provenance::SYNC); + assert!(result.is_ok()); + + let mut seen_challenge = false; + while let Some(message) = env.network_adapters[0].pop() { + if let NetworkRequests::Challenge(network_challenge) = message.as_network_requests() { + assert_eq!(network_challenge, valid_challenge); + seen_challenge = true; + break; + } + } + assert!(seen_challenge); +} + +fn create_invalid_proofs_chunk( + client: &mut Client, +) -> (EncodedShardChunk, Vec, Vec, Block) { + create_chunk( + client, + None, + Some("F5SvmQcKqekuKPJgLUNFgjB4ZgVmmiHsbDhTBSQbiywf".parse::().unwrap()), + ) +} + +#[test] +fn test_verify_chunk_invalid_proofs_challenge() { + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + env.produce_block(0, 1); + let (chunk, _merkle_paths, _receipts, block) = create_invalid_proofs_chunk(&mut env.clients[0]); + + let shard_id = chunk.shard_id(); + let challenge_result = + challenge(env, shard_id as usize, MaybeEncodedShardChunk::Encoded(chunk).into(), &block); + assert_eq!(challenge_result.unwrap(), (*block.hash(), vec!["test0".parse().unwrap()])); +} + +#[test] +fn test_verify_chunk_invalid_proofs_challenge_decoded_chunk() { + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + env.produce_block(0, 1); + let (encoded_chunk, _merkle_paths, _receipts, block) = + create_invalid_proofs_chunk(&mut env.clients[0]); + let chunk = + encoded_chunk.decode_chunk(env.clients[0].chain.epoch_manager.num_data_parts()).unwrap(); + + let shard_id = chunk.shard_id(); + let challenge_result = + challenge(env, shard_id as usize, MaybeEncodedShardChunk::Decoded(chunk).into(), &block); + assert_eq!(challenge_result.unwrap(), (*block.hash(), vec!["test0".parse().unwrap()])); +} + +#[test] +fn test_verify_chunk_proofs_malicious_challenge_no_changes() { + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + env.produce_block(0, 1); + // Valid chunk + let (chunk, _merkle_paths, _receipts, block) = create_chunk(&mut env.clients[0], None, None); + + let shard_id = chunk.shard_id(); + let challenge_result = + challenge(env, shard_id as usize, MaybeEncodedShardChunk::Encoded(chunk).into(), &block); + assert_matches!(challenge_result.unwrap_err(), Error::MaliciousChallenge); +} + +#[test] +fn test_verify_chunk_proofs_malicious_challenge_valid_order_transactions() { + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + env.produce_block(0, 1); + + let genesis_hash = *env.clients[0].chain.genesis().hash(); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + + let (chunk, _merkle_paths, _receipts, block) = create_chunk_with_transactions( + &mut env.clients[0], + vec![ + SignedTransaction::send_money( + 1, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 1000, + genesis_hash, + ), + SignedTransaction::send_money( + 2, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 1000, + genesis_hash, + ), + ], + ); + + let shard_id = chunk.shard_id(); + let challenge_result = + challenge(env, shard_id as usize, MaybeEncodedShardChunk::Encoded(chunk).into(), &block); + assert_matches!(challenge_result.unwrap_err(), Error::MaliciousChallenge); +} + +#[test] +fn test_verify_chunk_proofs_challenge_transaction_order() { + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + env.produce_block(0, 1); + + let genesis_hash = *env.clients[0].chain.genesis().hash(); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + + let (chunk, _merkle_paths, _receipts, block) = create_chunk_with_transactions( + &mut env.clients[0], + vec![ + SignedTransaction::send_money( + 2, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 1000, + genesis_hash, + ), + SignedTransaction::send_money( + 1, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 1000, + genesis_hash, + ), + ], + ); + + let shard_id = chunk.shard_id(); + let challenge_result = + challenge(env, shard_id as usize, MaybeEncodedShardChunk::Encoded(chunk).into(), &block); + assert_eq!(challenge_result.unwrap(), (*block.hash(), vec!["test0".parse().unwrap()])); +} + +fn challenge( + env: TestEnv, + shard_id: usize, + chunk: Box, + block: &Block, +) -> Result<(CryptoHash, Vec), Error> { + let merkle_paths = Block::compute_chunk_headers_root(block.chunks().iter()).1; + let valid_challenge = Challenge::produce( + ChallengeBody::ChunkProofs(ChunkProofs { + block_header: borsh::to_vec(&block.header()).unwrap(), + chunk, + merkle_proof: merkle_paths[shard_id].clone(), + }), + &*env.clients[0].validator_signer.as_ref().unwrap().clone(), + ); + validate_challenge( + env.clients[0].chain.epoch_manager.as_ref(), + env.clients[0].chain.runtime_adapter.as_ref(), + block.header().epoch_id(), + block.header().prev_hash(), + &valid_challenge, + ) +} + +#[test] +fn test_verify_chunk_invalid_state_challenge() { + let genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let validator_signer = create_test_signer("test0"); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + env.produce_block(0, 1); + assert_eq!( + env.clients[0].process_tx( + SignedTransaction::send_money( + 1, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 1000, + genesis_hash, + ), + false, + false, + ), + ProcessTxResponse::ValidTx + ); + env.produce_block(0, 2); + + // Invalid chunk & block. + let last_block_hash = env.clients[0].chain.head().unwrap().last_block_hash; + let last_block = env.clients[0].chain.get_block(&last_block_hash).unwrap(); + let total_parts = env.clients[0].epoch_manager.num_total_parts(); + let data_parts = env.clients[0].epoch_manager.num_data_parts(); + let parity_parts = total_parts - data_parts; + let mut rs = ReedSolomonWrapper::new(data_parts, parity_parts); + let (mut invalid_chunk, merkle_paths) = ShardsManager::create_encoded_shard_chunk( + *last_block.hash(), + Trie::EMPTY_ROOT, + CryptoHash::default(), + last_block.header().height() + 1, + 0, + 0, + 1_000, + 0, + vec![], + vec![], + &[], + last_block.chunks()[0].prev_outgoing_receipts_root(), + CryptoHash::default(), + &validator_signer, + &mut rs, + PROTOCOL_VERSION, + ) + .unwrap(); + + let client = &mut env.clients[0]; + + // Receive invalid chunk to the validator. + client + .persist_and_distribute_encoded_chunk( + invalid_chunk.clone(), + merkle_paths, + vec![], + validator_signer.validator_id().clone(), + ) + .unwrap(); + + match &mut invalid_chunk { + EncodedShardChunk::V1(ref mut chunk) => { + chunk.header.height_included = last_block.header().height() + 1; + } + EncodedShardChunk::V2(ref mut chunk) => { + *chunk.header.height_included_mut() = last_block.header().height() + 1; + } + } + let block_merkle_tree = + client.chain.mut_chain_store().get_block_merkle_tree(last_block.hash()).unwrap(); + let mut block_merkle_tree = PartialMerkleTree::clone(&block_merkle_tree); + block_merkle_tree.insert(*last_block.hash()); + let block = Block::produce( + PROTOCOL_VERSION, + PROTOCOL_VERSION, + last_block.header(), + last_block.header().height() + 1, + last_block.header().block_ordinal() + 1, + vec![invalid_chunk.cloned_header()], + last_block.header().epoch_id().clone(), + last_block.header().next_epoch_id().clone(), + None, + vec![], + Ratio::from_integer(0), + 0, + 100, + None, + vec![], + vec![], + &validator_signer, + *last_block.header().next_bp_hash(), + block_merkle_tree.root(), + None, + ); + + let challenge_body = + client.chain.create_chunk_state_challenge(&last_block, &block, &block.chunks()[0]).unwrap(); + { + let prev_merkle_proofs = Block::compute_chunk_headers_root(last_block.chunks().iter()).1; + let merkle_proofs = Block::compute_chunk_headers_root(block.chunks().iter()).1; + assert_eq!(prev_merkle_proofs[0], challenge_body.prev_merkle_proof); + assert_eq!(merkle_proofs[0], challenge_body.merkle_proof); + // TODO (#6316): enable storage proof generation + assert_eq!(challenge_body.partial_state, PartialState::TrieValues(Vec::::new())); + // assert_eq!( + // challenge_body.partial_state.0, + // vec![ + // vec![ + // 1, 5, 0, 10, 178, 228, 151, 124, 13, 70, 6, 146, 31, 193, 111, 108, 60, 102, + // 227, 106, 220, 133, 45, 144, 104, 255, 30, 155, 129, 215, 15, 43, 202, 26, 122, + // 171, 30, 7, 228, 175, 99, 17, 113, 5, 94, 136, 200, 39, 136, 37, 110, 166, 241, + // 148, 128, 55, 131, 173, 97, 98, 201, 68, 82, 244, 223, 70, 86, 161, 5, 0, 0, 0, + // 0, 0, 0 + // ], + // vec![ + // 3, 1, 0, 0, 0, 16, 49, 233, 115, 11, 86, 10, 193, 50, 45, 253, 137, 126, 230, + // 236, 254, 86, 230, 148, 94, 141, 44, 46, 130, 154, 189, 73, 179, 223, 178, 17, + // 133, 232, 213, 5, 0, 0, 0, 0, 0, 0 + // ] + // ], + // ); + } + let challenge = + Challenge::produce(ChallengeBody::ChunkState(challenge_body), &validator_signer); + // Invalidate chunk state challenges because they are not supported yet. + // TODO (#2445): Enable challenges when they are working correctly. + assert_matches!( + validate_challenge( + client.chain.epoch_manager.as_ref(), + client.chain.runtime_adapter.as_ref(), + block.header().epoch_id(), + block.header().prev_hash(), + &challenge, + ) + .unwrap_err(), + Error::MaliciousChallenge + ); + // assert_eq!( + // validate_challenge( + // &*runtime_adapter, + // block.header().epoch_id(), + // block.header().prev_hash(), + // &challenge, + // ) + // .unwrap(), + // (*block.hash(), vec!["test0".parse().unwrap()]) + // ); + + // Process the block with invalid chunk and make sure it's marked as invalid at the end. + // And the same challenge created and sent out. + let result = client.process_block_test(block.into(), Provenance::NONE); + assert!(result.is_err()); + + let mut seen_challenge = false; + while let Some(message) = env.network_adapters[0].pop() { + if let NetworkRequests::Challenge(network_challenge) = message.as_network_requests() { + assert_eq!(network_challenge, challenge); + seen_challenge = true; + break; + } + } + assert!(seen_challenge); +} + +/// Receive invalid state transition in chunk as a validator / non-producer. +#[test] +fn test_receive_invalid_chunk_as_validator() {} + +/// Receive two different chunks from the same chunk producer. +#[test] +fn test_receive_two_chunks_from_one_producer() {} + +/// Receive two different blocks from the same block producer. +#[test] +fn test_receive_two_blocks_from_one_producer() {} diff --git a/integration-tests/src/tests/client/chunks_management.rs b/integration-tests/src/tests/client/chunks_management.rs new file mode 100644 index 000000000..87bd6f494 --- /dev/null +++ b/integration-tests/src/tests/client/chunks_management.rs @@ -0,0 +1,465 @@ +use crate::test_helpers::heavy_test; +use actix::System; +use futures::{future, FutureExt}; +use unc_actix_test_utils::run_actix; +use unc_async::time; +use unc_chain::test_utils::ValidatorSchedule; +use unc_chunks::{ + CHUNK_REQUEST_RETRY, CHUNK_REQUEST_SWITCH_TO_FULL_FETCH, CHUNK_REQUEST_SWITCH_TO_OTHERS, +}; +use unc_client::test_utils::{setup_mock_all_validators, ActorHandlesForTesting}; +use unc_client::{GetBlock, ProcessTxRequest}; +use unc_network::types::PeerManagerMessageRequest; +use unc_network::types::{AccountIdOrPeerTrackingShard, PeerInfo}; +use unc_network::types::{NetworkRequests, NetworkResponses}; +use unc_o11y::testonly::init_test_logger; +use unc_o11y::WithSpanContextExt; +use unc_primitives::hash::CryptoHash; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::AccountId; +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; +use std::time::Instant; +use tracing::info; + +struct Test { + validator_groups: u64, + chunk_only_producers: bool, + drop_to_4_from: &'static [&'static str], + drop_all_chunk_forward_msgs: bool, + block_timeout: time::Duration, +} + +impl Test { + fn run(self) { + heavy_test(move || run_actix(async move { self.run_impl() })) + } + + /// Runs block producing client and stops after network mock received seven blocks + /// Confirms that the blocks form a chain (which implies the chunks are distributed). + /// Confirms that the number of messages transmitting the chunks matches the expected number. + fn run_impl(self) { + init_test_logger(); + + let connectors: Arc>> = Arc::new(RwLock::new(vec![])); + let heights = Arc::new(RwLock::new(HashMap::new())); + let heights1 = heights; + + let height_to_hash = Arc::new(RwLock::new(HashMap::new())); + let height_to_epoch = Arc::new(RwLock::new(HashMap::new())); + + let check_heights = move |prev_hash: &CryptoHash, hash: &CryptoHash, height| { + let mut map = heights1.write().unwrap(); + // Note that height of the previous block is not guaranteed to be height + // - 1. All we know is that it’s less than height of the current block. + if let Some(prev_height) = map.get(prev_hash) { + assert!(*prev_height < height); + } + assert_eq!(*map.entry(*hash).or_insert(height), height); + }; + + let mut vs = ValidatorSchedule::new() + .num_shards(4) + .block_producers_per_epoch(vec![ + vec![ + "test1".parse().unwrap(), + "test2".parse().unwrap(), + "test3".parse().unwrap(), + "test4".parse().unwrap(), + ], + vec![ + "test5".parse().unwrap(), + "test6".parse().unwrap(), + "test7".parse().unwrap(), + "test8".parse().unwrap(), + ], + ]) + .validator_groups(self.validator_groups); + if self.chunk_only_producers { + vs = vs.chunk_only_producers_per_epoch_per_shard(vec![ + vec![ + vec!["cop1".parse().unwrap()], + vec!["cop2".parse().unwrap()], + vec!["cop3".parse().unwrap()], + vec!["cop4".parse().unwrap()], + ], + vec![ + vec!["cop5".parse().unwrap()], + vec!["cop6".parse().unwrap()], + vec!["cop7".parse().unwrap()], + vec!["cop8".parse().unwrap()], + ], + ]); + } + let archive = vec![false; vs.all_validators().count()]; + let epoch_sync_enabled = vec![true; vs.all_validators().count()]; + let key_pairs = + (0..vs.all_validators().count()).map(|_| PeerInfo::random()).collect::>(); + + let mut partial_chunk_msgs = 0; + let mut partial_chunk_request_msgs = 0; + + let (_, conn, _) = setup_mock_all_validators( + vs, + key_pairs, + true, + self.block_timeout.whole_milliseconds() as u64, // TODO: use Duration for callees + false, + false, + 5, + true, + archive, + epoch_sync_enabled, + false, + Box::new(move |_, from_whom: AccountId, msg: &PeerManagerMessageRequest| { + let msg = msg.as_network_requests_ref(); + match msg { + NetworkRequests::Block { block } => { + let h = block.header().height(); + check_heights(block.header().prev_hash(), block.hash(), h); + + let mut height_to_hash = height_to_hash.write().unwrap(); + height_to_hash.insert(h, *block.hash()); + + let mut height_to_epoch = height_to_epoch.write().unwrap(); + height_to_epoch.insert(h, block.header().epoch_id().clone()); + + println!( + "[{:?}]: BLOCK {} HEIGHT {}; HEADER HEIGHTS: {} / {} / {} / {};\nAPPROVALS: {:?}", + Instant::now(), + block.hash(), + block.header().height(), + block.chunks()[0].height_created(), + block.chunks()[1].height_created(), + block.chunks()[2].height_created(), + block.chunks()[3].height_created(), + block.header().approvals(), + ); + + if h > 1 { + // Make sure doomslug finality is computed correctly. + assert_eq!( + block.header().last_ds_final_block(), + height_to_hash.get(&(h - 1)).unwrap() + ); + + // Make sure epoch length actually corresponds to the desired epoch length + // The switches are expected at 0->1, 5->6 and 10->11 + let prev_epoch_id = height_to_epoch.get(&(h - 1)).unwrap().clone(); + assert_eq!(block.header().epoch_id() == &prev_epoch_id, h % 5 != 1); + + // Make sure that the blocks leading to the epoch switch have twice as + // many approval slots + assert_eq!( + block.header().approvals().len() == 8, + h % 5 == 0 || h % 5 == 4 + ); + } + if h > 2 { + // Make sure BFT finality is computed correctly + assert_eq!( + block.header().last_final_block(), + height_to_hash.get(&(h - 2)).unwrap() + ); + } + + if block.header().height() > 1 { + for shard_id in 0..4 { + // If messages from 1 to 4 are dropped, 4 at their heights will + // receive the block significantly later than the chunks, and + // thus would discard the chunks + if self.drop_to_4_from.is_empty() + || block.header().height() % 4 != 3 + { + assert_eq!( + block.header().height(), + block.chunks()[shard_id].height_created() + ); + } + } + } + + if block.header().height() >= 12 { + println!("PREV BLOCK HASH: {}", block.header().prev_hash()); + println!( + "STATS: responses: {} requests: {}", + partial_chunk_msgs, partial_chunk_request_msgs + ); + + System::current().stop(); + } + } + NetworkRequests::PartialEncodedChunkMessage { + account_id: to_whom, + partial_encoded_chunk: _, + } => { + partial_chunk_msgs += 1; + if self.drop_to_4_from.contains(&from_whom.as_str()) && to_whom == "test4" { + println!( + "Dropping Partial Encoded Chunk Message from {from_whom} to test4" + ); + return (NetworkResponses::NoResponse.into(), false); + } + } + NetworkRequests::PartialEncodedChunkForward { account_id: to_whom, .. } => { + if self.drop_all_chunk_forward_msgs { + println!("Dropping Partial Encoded Chunk Forward Message"); + return (NetworkResponses::NoResponse.into(), false); + } + if self.drop_to_4_from.contains(&from_whom.as_str()) && to_whom == "test4" { + println!( + "Dropping Partial Encoded Chunk Forward Message from {from_whom} to test4" + ); + return (NetworkResponses::NoResponse.into(), false); + } + } + NetworkRequests::PartialEncodedChunkResponse { route_back: _, response: _ } => { + partial_chunk_msgs += 1; + } + NetworkRequests::PartialEncodedChunkRequest { + target: AccountIdOrPeerTrackingShard { account_id: Some(to_whom), .. }, + .. + } => { + if self.drop_to_4_from.contains(&to_whom.as_str()) && from_whom == "test4" { + info!("Dropping Partial Encoded Chunk Request from test4 to {to_whom}"); + return (NetworkResponses::NoResponse.into(), false); + } + if !self.drop_to_4_from.is_empty() + && from_whom == "test4" + && to_whom == "test2" + { + info!("Observed Partial Encoded Chunk Request from test4 to test2"); + } + partial_chunk_request_msgs += 1; + } + _ => {} + }; + (NetworkResponses::NoResponse.into(), true) + }), + ); + *connectors.write().unwrap() = conn; + + let view_client = connectors.write().unwrap()[0].view_client_actor.clone(); + let actor = view_client.send(GetBlock::latest().with_span_context()); + let actor = actor.then(move |res| { + let block_hash = res.unwrap().unwrap().header.hash; + let connectors_ = connectors.write().unwrap(); + connectors_[0].client_actor.do_send( + ProcessTxRequest { + transaction: SignedTransaction::empty(block_hash), + is_forwarded: false, + check_only: false, + } + .with_span_context(), + ); + connectors_[1].client_actor.do_send( + ProcessTxRequest { + transaction: SignedTransaction::empty(block_hash), + is_forwarded: false, + check_only: false, + } + .with_span_context(), + ); + connectors_[2].client_actor.do_send( + ProcessTxRequest { + transaction: SignedTransaction::empty(block_hash), + is_forwarded: false, + check_only: false, + } + .with_span_context(), + ); + future::ready(()) + }); + actix::spawn(actor); + } +} + +#[test] +#[ignore] // TODO: #8855 +fn chunks_produced_and_distributed_all_in_all_shards() { + Test { + validator_groups: 1, + chunk_only_producers: false, + drop_to_4_from: &[], + drop_all_chunk_forward_msgs: false, + block_timeout: 15 * CHUNK_REQUEST_RETRY, + } + .run() +} + +#[test] +fn chunks_produced_and_distributed_2_vals_per_shard() { + Test { + validator_groups: 2, + chunk_only_producers: false, + drop_to_4_from: &[], + drop_all_chunk_forward_msgs: false, + block_timeout: CHUNK_REQUEST_RETRY * 15, + } + .run() +} + +#[test] +fn chunks_produced_and_distributed_one_val_per_shard() { + Test { + validator_groups: 4, + chunk_only_producers: false, + drop_to_4_from: &[], + drop_all_chunk_forward_msgs: false, + block_timeout: CHUNK_REQUEST_RETRY * 15, + } + .run() +} + +#[test] +#[ignore] // TODO: #8853 +fn chunks_produced_and_distributed_all_in_all_shards_should_succeed_even_without_forwarding() { + Test { + validator_groups: 1, + chunk_only_producers: false, + drop_to_4_from: &[], + drop_all_chunk_forward_msgs: true, + block_timeout: CHUNK_REQUEST_RETRY * 15, + } + .run() +} + +#[test] +fn chunks_produced_and_distributed_2_vals_per_shard_should_succeed_even_without_forwarding() { + Test { + validator_groups: 2, + chunk_only_producers: false, + drop_to_4_from: &[], + drop_all_chunk_forward_msgs: true, + block_timeout: CHUNK_REQUEST_RETRY * 15, + } + .run() +} + +#[test] +fn chunks_produced_and_distributed_one_val_per_shard_should_succeed_even_without_forwarding() { + Test { + validator_groups: 4, + chunk_only_producers: false, + drop_to_4_from: &[], + drop_all_chunk_forward_msgs: true, + block_timeout: CHUNK_REQUEST_RETRY * 15, + } + .run() +} + +/// The timeout for requesting chunk from others is 1s. 3000 block timeout means that a participant +/// that is otherwise ready to produce a block will wait for 3000/2 milliseconds for all the chunks. +/// We block all the communication from test1 to test4, and expect that in 1.5 seconds test4 will +/// give up on getting the part from test1 and will get it from test2 (who will have it because +/// `validator_groups=2`) +/// +/// Note that due to #7385 (which sends chunk forwarding messages irrespective of shard assignment), +/// we disable chunk forwarding messages for the following tests, so we can focus on chunk +/// requesting behavior. +/// TODO: this test is broken due to (#8395) - with fix in #8211 + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn chunks_recovered_from_others() { + // Test { + // validator_groups: 2, + // chunk_only_producers: false, + // drop_to_4_from: &["test1"], + // drop_all_chunk_forward_msgs: true, + // block_timeout: CHUNK_REQUEST_SWITCH_TO_OTHERS * 4, + // } + // .run() +} + +/// Same test as above, but the number of validator groups is four, therefore test2 doesn't have the +/// part test4 needs. The only way test4 can recover the part is by reconstructing the whole chunk, +/// but they won't do it for the first 3 seconds, and 3s block_timeout means that the block producers +/// only wait for 3000/2 milliseconds until they produce a block with some chunks missing +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +#[should_panic] +fn chunks_recovered_from_full_timeout_too_short() { + Test { + validator_groups: 4, + chunk_only_producers: false, + drop_to_4_from: &["test1"], + drop_all_chunk_forward_msgs: true, + block_timeout: CHUNK_REQUEST_SWITCH_TO_OTHERS * 2, + } + .run() +} + +/// Same test as above, but the timeout is sufficiently large for test4 now to reconstruct the full +/// chunk +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn chunks_recovered_from_full() { + Test { + validator_groups: 4, + chunk_only_producers: false, + drop_to_4_from: &["test1"], + drop_all_chunk_forward_msgs: true, + block_timeout: CHUNK_REQUEST_SWITCH_TO_FULL_FETCH * 2, + } + .run() +} + +// And now let's add chunk-only producers into the mix + +/// Happy case -- each shard is handled by one cop and one block producers. +#[test] +fn chunks_produced_and_distributed_one_val_shard_cop() { + Test { + validator_groups: 4, + chunk_only_producers: true, + drop_to_4_from: &[], + drop_all_chunk_forward_msgs: false, + block_timeout: CHUNK_REQUEST_RETRY * 15, + } + .run() +} + +/// `test4` can't talk to `test1`, so it'll fetch the chunk for first shard from `cop1`. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn chunks_recovered_from_others_cop() { + Test { + validator_groups: 1, + chunk_only_producers: true, + drop_to_4_from: &["test1"], + drop_all_chunk_forward_msgs: true, + block_timeout: CHUNK_REQUEST_SWITCH_TO_OTHERS * 4, + } + .run() +} + +/// `test4` can't talk neither to `cop1` nor to `test1`, so it can't fetch chunk +/// from chunk producers and has to reconstruct it. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +#[should_panic] +fn chunks_recovered_from_full_timeout_too_short_cop() { + Test { + validator_groups: 4, + chunk_only_producers: true, + drop_to_4_from: &["test1", "cop1"], + drop_all_chunk_forward_msgs: true, + block_timeout: CHUNK_REQUEST_SWITCH_TO_OTHERS * 2, + } + .run() +} + +/// Same as above, but with longer block production timeout which should allow for full reconstruction. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn chunks_recovered_from_full_cop() { + Test { + validator_groups: 4, + chunk_only_producers: true, + drop_to_4_from: &["test1", "cop1"], + drop_all_chunk_forward_msgs: true, + block_timeout: CHUNK_REQUEST_SWITCH_TO_FULL_FETCH * 2, + } + .run() +} diff --git a/integration-tests/src/tests/client/cold_storage.rs b/integration-tests/src/tests/client/cold_storage.rs new file mode 100644 index 000000000..77c7b09ed --- /dev/null +++ b/integration-tests/src/tests/client/cold_storage.rs @@ -0,0 +1,587 @@ +use borsh::BorshDeserialize; +use unc_chain::{ChainGenesis, Provenance}; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_client::ProcessTxResponse; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_epoch_manager::EpochManager; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::block::Tip; +use unc_primitives::sharding::{PartialEncodedChunk, ShardChunk}; +use unc_primitives::transaction::{ + Action, DeployContractAction, FunctionCallAction, SignedTransaction, +}; +use unc_primitives_core::types::AccountId; +use unc_store::cold_storage::{ + copy_all_data_to_cold, test_cold_genesis_update, test_get_store_initial_writes, + test_get_store_reads, update_cold_db, update_cold_head, +}; +use unc_store::metadata::DbKind; +use unc_store::metadata::DB_VERSION; +use unc_store::test_utils::create_test_node_storage_with_cold; +use unc_store::{DBCol, Store, COLD_HEAD_KEY, HEAD_KEY}; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; +use framework::{cold_storage::spawn_cold_store_loop, UncConfig}; +use std::collections::HashSet; +use std::str::FromStr; +use strum::IntoEnumIterator; + +fn check_key(first_store: &Store, second_store: &Store, col: DBCol, key: &[u8]) { + let pretty_key = unc_fmt::StorageKey(key); + tracing::debug!("Checking {:?} {:?}", col, pretty_key); + + let first_res = first_store.get(col, key).unwrap(); + let second_res = second_store.get(col, key).unwrap(); + + if col == DBCol::PartialChunks { + tracing::debug!("{:?}", first_store.get_ser::(col, key)); + } + + assert_eq!(first_res, second_res, "col: {:?} key: {:?}", col, pretty_key); +} + +fn check_iter( + first_store: &Store, + second_store: &Store, + col: DBCol, + no_check_rules: &Vec, &Box<[u8]>) -> bool>>, +) -> u64 { + let mut num_checks = 0; + for (key, value) in first_store.iter(col).map(Result::unwrap) { + let mut check = true; + for no_check in no_check_rules { + if no_check(col, &key, &value) { + check = false; + } + } + if check { + check_key(first_store, second_store, col, &key); + num_checks += 1; + } + } + num_checks +} + +/// Deploying test contract and calling write_random_value 5 times every block for 4 epochs. +/// Also doing 5 send transactions every block. +/// 4 epochs, because this test does not cover gc behaviour. +/// After every block updating a separate database with data from client's storage. +/// After 4 epochs we check that everything, that exists in cold columns +/// of the storage of the client also exists in the database to which we were writing. +#[test] +fn test_storage_after_commit_of_cold_update() { + init_test_logger(); + + let epoch_length = 5; + let max_height = epoch_length * 4; + + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let (storage, ..) = create_test_node_storage_with_cold(DB_VERSION, DbKind::Hot); + + let mut last_hash = *env.clients[0].chain.genesis().hash(); + + test_cold_genesis_update(&*storage.cold_db().unwrap(), &env.clients[0].runtime_adapter.store()) + .unwrap(); + + let state_reads = test_get_store_reads(DBCol::State); + let state_changes_reads = test_get_store_reads(DBCol::StateChanges); + + for h in 1..max_height { + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + if h == 1 { + let tx = SignedTransaction::from_actions( + h, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + vec![Action::DeployContract(DeployContractAction { + code: unc_test_contracts::rs_contract().to_vec(), + })], + last_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + // Don't send transactions in last two blocks. Because on last block production a chunk from + // the next block will be produced and information about these transactions will be written + // into db. And it is a PAIN to filter it out, especially for Receipts. + if h + 2 < max_height { + for i in 0..5 { + let tx = SignedTransaction::from_actions( + h * 10 + i, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "write_random_value".to_string(), + args: vec![], + gas: 100_000_000_000_000, + deposit: 0, + }))], + last_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + for i in 0..5 { + let tx = SignedTransaction::send_money( + h * 10 + i, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 1, + last_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + } + + let block = env.clients[0].produce_block(h).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + + update_cold_db( + &*storage.cold_db().unwrap(), + &env.clients[0].runtime_adapter.store(), + &env.clients[0] + .epoch_manager + .get_shard_layout( + &env.clients[0].epoch_manager.get_epoch_id_from_prev_block(&last_hash).unwrap(), + ) + .unwrap(), + &h, + ) + .unwrap(); + + last_hash = *block.hash(); + } + + // assert that we don't read State from db, but from TrieChanges + assert_eq!(state_reads, test_get_store_reads(DBCol::State)); + // assert that we don't read StateChanges from db again after iter_prefix + assert_eq!(state_changes_reads, test_get_store_reads(DBCol::StateChanges)); + + // We still need to filter out one chunk + let mut no_check_rules: Vec, &Box<[u8]>) -> bool>> = vec![]; + no_check_rules.push(Box::new(move |col, _key, value| -> bool { + if col == DBCol::Chunks { + let chunk = ShardChunk::try_from_slice(&*value).unwrap(); + if *chunk.prev_block() == last_hash { + return true; + } + } + false + })); + no_check_rules.push(Box::new(move |col, _key, value| -> bool { + if col == DBCol::PartialChunks { + let chunk = PartialEncodedChunk::try_from_slice(&*value).unwrap(); + if *chunk.prev_block() == last_hash { + return true; + } + } + false + })); + no_check_rules.push(Box::new(move |col, key, _value| -> bool { + if col == DBCol::ChunkHashesByHeight { + let height = u64::from_le_bytes(key[0..8].try_into().unwrap()); + if height == max_height { + return true; + } + } + false + })); + + for col in DBCol::iter() { + if col.is_cold() { + let num_checks = check_iter( + &env.clients[0].runtime_adapter.store(), + &storage.get_cold_store().unwrap(), + col, + &no_check_rules, + ); + // assert that this test actually checks something + // apart from StateChangesForSplitStates and StateHeaders, that are empty + assert!( + col == DBCol::StateChangesForSplitStates + || col == DBCol::StateHeaders + || num_checks > 0 + ); + } + } +} + +/// Producing 10 * 5 blocks and updating HEAD of cold storage after each one. +/// After every update checking that HEAD in cold db, COLD_HEAD in hot db and HEAD in hot store are equal. +#[test] +fn test_cold_db_head_update() { + init_test_logger(); + + let epoch_length = 5; + let max_height = epoch_length * 10; + + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let (store, ..) = create_test_node_storage_with_cold(DB_VERSION, DbKind::Hot); + let hot_store = &store.get_hot_store(); + let cold_store = &store.get_cold_store().unwrap(); + let mut env = TestEnv::builder(chain_genesis) + .stores(vec![hot_store.clone()]) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + for h in 1..max_height { + env.produce_block(0, h); + update_cold_head(&*store.cold_db().unwrap(), &env.clients[0].runtime_adapter.store(), &h) + .unwrap(); + + let head = &env.clients[0] + .runtime_adapter + .store() + .get_ser::(DBCol::BlockMisc, HEAD_KEY) + .unwrap(); + let cold_head_in_hot = hot_store.get_ser::(DBCol::BlockMisc, COLD_HEAD_KEY).unwrap(); + let cold_head_in_cold = cold_store.get_ser::(DBCol::BlockMisc, HEAD_KEY).unwrap(); + + assert_eq!(head, &cold_head_in_cold); + assert_eq!(head, &cold_head_in_hot); + } +} + +/// Very similar to `test_storage_after_commit_of_cold_update`, but has less transactions, +/// and more importantly SKIPS. +/// Here we are testing that `update_cold_db` handles itself correctly +/// if some heights are not present in blockchain. +#[test] +fn test_cold_db_copy_with_height_skips() { + init_test_logger(); + + let epoch_length = 5; + let max_height = epoch_length * 4; + + let skips = HashSet::from([1, 4, 5, 7, 11, 14, 16, 19]); + + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let (storage, ..) = create_test_node_storage_with_cold(DB_VERSION, DbKind::Hot); + + let mut last_hash = *env.clients[0].chain.genesis().hash(); + + test_cold_genesis_update(&*storage.cold_db().unwrap(), &env.clients[0].runtime_adapter.store()) + .unwrap(); + + for h in 1..max_height { + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + // It is still painful to filter out transactions in last two blocks. + // So, as block 19 is skipped, blocks 17 and 18 shouldn't contain any transactions. + // So, we shouldn't send any transactions between block 17 and the previous block. + // And as block 16 is skipped, the previous block to 17 is 15. + // Therefore, no transactions after block 15. + if h < 16 { + for i in 0..5 { + let tx = SignedTransaction::send_money( + h * 10 + i, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 1, + last_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + } + + let block = { + if !skips.contains(&h) { + let block = env.clients[0].produce_block(h).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + Some(block) + } else { + None + } + }; + + update_cold_db( + &*storage.cold_db().unwrap(), + &env.clients[0].runtime_adapter.store(), + &env.clients[0] + .epoch_manager + .get_shard_layout( + &env.clients[0].epoch_manager.get_epoch_id_from_prev_block(&last_hash).unwrap(), + ) + .unwrap(), + &h, + ) + .unwrap(); + + if block.is_some() { + last_hash = *block.unwrap().hash(); + } + } + + // We still need to filter out one chunk + let mut no_check_rules: Vec, &Box<[u8]>) -> bool>> = vec![]; + no_check_rules.push(Box::new(move |col, _key, value| -> bool { + if col == DBCol::Chunks { + let chunk = ShardChunk::try_from_slice(&*value).unwrap(); + if *chunk.prev_block() == last_hash { + return true; + } + } + false + })); + no_check_rules.push(Box::new(move |col, _key, value| -> bool { + if col == DBCol::PartialChunks { + let chunk = PartialEncodedChunk::try_from_slice(&*value).unwrap(); + if *chunk.prev_block() == last_hash { + return true; + } + } + false + })); + + for col in DBCol::iter() { + if col.is_cold() && col != DBCol::ChunkHashesByHeight { + let num_checks = check_iter( + &env.clients[0].runtime_adapter.store(), + &storage.get_cold_store().unwrap(), + col, + &no_check_rules, + ); + // assert that this test actually checks something + // apart from StateChangesForSplitStates and StateHeaders, that are empty + assert!( + col == DBCol::StateChangesForSplitStates + || col == DBCol::StateHeaders + || num_checks > 0 + ); + } + } +} + +/// Producing 4 epochs of blocks with some transactions. +/// Call copying full contents of cold columns to cold storage in batches of specified max_size. +/// Checks COLD_STORE_MIGRATION_BATCH_WRITE_COUNT metric for some batch_sizes: +/// - If batch_size = 0, check that every value was copied in a separate batch. +/// - If batch_size = usize::MAX, check that everything was copied in one batch. +/// Most importantly, checking that everything from cold columns was indeed copied into cold storage. +fn test_initial_copy_to_cold(batch_size: usize) { + init_test_logger(); + + let epoch_length = 5; + let max_height = epoch_length * 4; + + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let (store, ..) = create_test_node_storage_with_cold(DB_VERSION, DbKind::Archive); + + let mut last_hash = *env.clients[0].chain.genesis().hash(); + + for h in 1..max_height { + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + for i in 0..5 { + let tx = SignedTransaction::send_money( + h * 10 + i, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 1, + last_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + let block = env.clients[0].produce_block(h).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + last_hash = *block.hash(); + } + + let keep_going = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true)); + + copy_all_data_to_cold( + (*store.cold_db().unwrap()).clone(), + &env.clients[0].runtime_adapter.store(), + batch_size, + &keep_going, + ) + .unwrap(); + + for col in DBCol::iter() { + if !col.is_cold() { + continue; + } + let num_checks = check_iter( + &env.clients[0].runtime_adapter.store(), + &store.get_cold_store().unwrap(), + col, + &vec![], + ); + // StateChangesForSplitStates and StateHeaders are empty + if col == DBCol::StateChangesForSplitStates || col == DBCol::StateHeaders { + continue; + } + // assert that this test actually checks something + assert!(num_checks > 0); + if batch_size == 0 { + assert_eq!(num_checks, test_get_store_initial_writes(col)); + } else if batch_size == usize::MAX { + assert_eq!(1, test_get_store_initial_writes(col)); + } + } +} + +#[test] +fn test_initial_copy_to_cold_small_batch() { + test_initial_copy_to_cold(0); +} + +#[test] +fn test_initial_copy_to_cold_huge_batch() { + test_initial_copy_to_cold(usize::MAX); +} + +#[test] +fn test_initial_copy_to_cold_medium_batch() { + test_initial_copy_to_cold(5000); +} + +/// This test checks that garbage collection does not remove data needed for cold storage migration prematurely. +/// Test flow: +/// - Produce a lot of blocks. +/// - Manually perform initial migration. +/// - Produce a lot more blocks for hot tail to reach its boundary. +/// - Spawn a cold store loop (just like we do in uncd). +/// - Wait 10 seconds. +/// - Check that cold head progressed. +#[test] +fn test_cold_loop_on_gc_boundary() { + init_test_logger(); + + let epoch_length = 5; + + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + + let (store, ..) = create_test_node_storage_with_cold(DB_VERSION, DbKind::Hot); + let hot_store = &store.get_hot_store(); + let cold_store = &store.get_cold_store().unwrap(); + let mut env = TestEnv::builder(chain_genesis) + .archive(true) + .save_trie_changes(true) + .stores(vec![hot_store.clone()]) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let height_delta = env.clients[0].config.gc.gc_num_epochs_to_keep * epoch_length * 2; + + let mut last_hash = *env.clients[0].chain.genesis().hash(); + + for h in 1..height_delta { + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + for i in 0..5 { + let tx = SignedTransaction::send_money( + h * 10 + i, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 1, + last_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + let block = env.clients[0].produce_block(h).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + last_hash = *block.hash(); + } + + let keep_going = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true)); + + copy_all_data_to_cold((*store.cold_db().unwrap()).clone(), &hot_store, 1000000, &keep_going) + .unwrap(); + + update_cold_head(&*store.cold_db().unwrap(), &hot_store, &(height_delta - 1)).unwrap(); + + for h in height_delta..height_delta * 2 { + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + for i in 0..5 { + let tx = SignedTransaction::send_money( + h * 10 + i, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 1, + last_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + let block = env.clients[0].produce_block(h).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + last_hash = *block.hash(); + } + + let start_cold_head = + cold_store.get_ser::(DBCol::BlockMisc, COLD_HEAD_KEY).unwrap().unwrap().height; + + let signer = + InMemorySigner::from_random(AccountId::from_str("test").unwrap(), KeyType::ED25519); + + let mut unc_config = UncConfig::new( + framework::config::Config::default(), + genesis.clone(), + unc_crypto::KeyFile { + account_id: signer.account_id, + public_key: signer.public_key, + secret_key: signer.secret_key, + }, + None, + ) + .unwrap(); + unc_config.client_config = env.clients[0].config.clone(); + unc_config.config.save_trie_changes = Some(true); + + let epoch_manager = EpochManager::new_arc_handle(store.get_hot_store(), &genesis.config); + spawn_cold_store_loop(&unc_config, &store, epoch_manager).unwrap(); + std::thread::sleep(std::time::Duration::from_secs(1)); + + let end_cold_head = + cold_store.get_ser::(DBCol::BlockMisc, COLD_HEAD_KEY).unwrap().unwrap().height; + + assert!( + end_cold_head > start_cold_head, + "Start cold head is {}, end cold head is {}", + start_cold_head, + end_cold_head + ); +} diff --git a/integration-tests/src/tests/client/epoch_sync.rs b/integration-tests/src/tests/client/epoch_sync.rs new file mode 100644 index 000000000..09e60dcf9 --- /dev/null +++ b/integration-tests/src/tests/client/epoch_sync.rs @@ -0,0 +1,499 @@ +use crate::nearcore_utils::{add_blocks, setup_configs_with_epoch_length}; +use crate::test_helpers::heavy_test; +use actix::Actor; +use actix_rt::System; +use futures::{future, FutureExt}; +use unc_actix_test_utils::run_actix; +use unc_chain::{BlockProcessingArtifact, ChainStoreAccess}; +use unc_chain::{ChainGenesis, Provenance}; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_client::ProcessTxResponse; +use unc_client_primitives::types::GetBlock; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_network::test_utils::WaitOrTimeoutActor; +use unc_o11y::testonly::{init_integration_logger, init_test_logger}; +use unc_o11y::WithSpanContextExt; +use unc_primitives::epoch_manager::block_info::BlockInfo; +use unc_primitives::epoch_manager::epoch_sync::EpochSyncInfo; +use unc_primitives::state_part::PartId; +use unc_primitives::state_sync::get_num_state_parts; +use unc_primitives::test_utils::create_test_signer; +use unc_primitives::transaction::{ + Action, DeployContractAction, FunctionCallAction, SignedTransaction, +}; +use unc_primitives::types::EpochId; +use unc_primitives::utils::MaybeValidated; +use unc_primitives_core::hash::CryptoHash; +use unc_primitives_core::types::BlockHeight; +use unc_store::Mode::ReadOnly; +use unc_store::{DBCol, NodeStorage}; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; +use framework::{start_with_config, UncConfig}; +use std::collections::HashSet; +use std::path::Path; +use std::sync::{Arc, RwLock}; + +fn generate_transactions(last_hash: &CryptoHash, h: BlockHeight) -> Vec { + let mut txs = vec![]; + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + if h == 1 { + txs.push(SignedTransaction::from_actions( + h, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + vec![Action::DeployContract(DeployContractAction { + code: unc_test_contracts::rs_contract().to_vec(), + })], + *last_hash, + )); + } + + for i in 0..5 { + txs.push(SignedTransaction::from_actions( + h * 10 + i, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "write_random_value".to_string(), + args: vec![], + gas: 100_000_000_000_000, + deposit: 0, + }))], + *last_hash, + )); + } + + for i in 0..5 { + txs.push(SignedTransaction::send_money( + h * 10 + i, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 1, + *last_hash, + )); + } + txs +} + +/// Produce 4 epochs with some transactions. +/// When the first block of the next epoch is finalised check that `EpochSyncInfo` has been recorded. +#[test] +fn test_continuous_epoch_sync_info_population() { + init_test_logger(); + + let epoch_length = 5; + let max_height = epoch_length * 4 + 3; + + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let mut last_hash = *env.clients[0].chain.genesis().hash(); + let mut last_epoch_id = EpochId::default(); + + for h in 1..max_height { + for tx in generate_transactions(&last_hash, h) { + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + let block = env.clients[0].produce_block(h).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + last_hash = *block.hash(); + + let last_final_hash = block.header().last_final_block(); + if *last_final_hash == CryptoHash::default() { + continue; + } + let last_final_header = + env.clients[0].chain.chain_store().get_block_header(last_final_hash).unwrap(); + + if *last_final_header.epoch_id() != last_epoch_id { + let epoch_id = last_epoch_id.clone(); + + tracing::debug!("Checking epoch: {:?}", &epoch_id); + assert!(env.clients[0].chain.chain_store().get_epoch_sync_info(&epoch_id).is_ok()); + tracing::debug!("OK"); + } + + last_epoch_id = last_final_header.epoch_id().clone(); + } +} + +/// Produce 4 epochs + 10 blocks. +/// Start second node without epoch sync, but with state sync. +/// Sync second node to first node (at least headers). +/// Check that it has all EpochSyncInfo records and all of them are correct. +/// +/// The header sync part is based on `integration-tests::framework::sync_nodes::sync_nodes`. +#[test] +fn test_continuous_epoch_sync_info_population_on_header_sync() { + heavy_test(|| { + init_integration_logger(); + + let (genesis, genesis_block, mut near1_base, mut near2_base) = + setup_configs_with_epoch_length(50); + + let dir1_base = + tempfile::Builder::new().prefix("epoch_sync_info_in_header_sync_1").tempdir().unwrap(); + let dir2_base = + tempfile::Builder::new().prefix("epoch_sync_info_in_header_sync_2").tempdir().unwrap(); + let epoch_ids_base = Arc::new(RwLock::new(HashSet::new())); + + let near1 = near1_base.clone(); + let near2 = near2_base.clone(); + let dir1_path = dir1_base.path(); + let dir2_path = dir2_base.path(); + let epoch_ids = epoch_ids_base.clone(); + + run_actix(async move { + // Start first node + let framework::NearNode { client: client1, .. } = + start_with_config(dir1_path, near1).expect("start_with_config"); + + // Generate 4 epochs + 10 blocks + let signer = create_test_signer("other"); + let blocks = + add_blocks(vec![genesis_block], client1, 210, genesis.config.epoch_length, &signer); + + // Save all finished epoch_ids + let mut epoch_ids = epoch_ids.write().unwrap(); + for block in blocks[0..200].iter() { + epoch_ids.insert(block.header().epoch_id().clone()); + } + + // Start second node + let framework::NearNode { view_client: view_client2, .. } = + start_with_config(dir2_path, near2).expect("start_with_config"); + + // Wait for second node's headers to sync. + // Timeout here means that header sync is not working. + // Header sync is better debugged through other tests. + // For example, run `integration-tests::framework::sync_nodes::sync_nodes` test, + // on which this test's setup is based. + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let actor = view_client2.send(GetBlock::latest().with_span_context()); + let actor = actor.then(|res| { + match &res { + Ok(Ok(b)) if b.header.height == 210 => System::current().stop(), + Err(_) => return future::ready(()), + _ => {} + }; + future::ready(()) + }); + actix::spawn(actor); + }), + 100, + 120000, + ) + .start(); + }); + + // Open storages of both nodes + let open_read_only_storage = |home_dir: &Path, unc_config: &UncConfig| -> NodeStorage { + let opener = NodeStorage::opener(home_dir, false, &unc_config.config.store, None); + opener.open_in_mode(ReadOnly).unwrap() + }; + + let store1 = open_read_only_storage(dir1_base.path(), &mut near1_base).get_hot_store(); + let store2 = open_read_only_storage(dir2_base.path(), &mut near2_base).get_hot_store(); + + // Check that for every epoch second store has EpochSyncInfo. + // And that values in both stores are the same. + let epoch_ids = epoch_ids_base.read().unwrap(); + for epoch_id in epoch_ids.iter() { + // Check that we have a value for EpochSyncInfo in the synced node + assert!( + store2 + .get_ser::(DBCol::EpochSyncInfo, epoch_id.as_ref()) + .unwrap() + .is_some(), + "{:?}", + epoch_id + ); + // Check that it matches value in full node exactly + assert_eq!( + store1 + .get_ser::(DBCol::EpochSyncInfo, epoch_id.as_ref()) + .unwrap() + .unwrap(), + store2 + .get_ser::(DBCol::EpochSyncInfo, epoch_id.as_ref()) + .unwrap() + .unwrap(), + "{:?}", + epoch_id + ); + } + }); +} + +/// Check that we can reconstruct `BlockInfo` and `epoch_sync_data_hash` from `EpochSyncInfo`. +#[test] +fn test_epoch_sync_data_hash_from_epoch_sync_info() { + init_test_logger(); + + let epoch_length = 5; + let max_height = epoch_length * 4 + 3; + + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let mut last_hash = *env.clients[0].chain.genesis().hash(); + let mut last_epoch_id = EpochId::default(); + + for h in 1..max_height { + for tx in generate_transactions(&last_hash, h) { + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + let block = env.clients[0].produce_block(h).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + last_hash = *block.hash(); + + let last_final_hash = block.header().last_final_block(); + if *last_final_hash == CryptoHash::default() { + continue; + } + let last_final_header = + env.clients[0].chain.chain_store().get_block_header(last_final_hash).unwrap(); + + if *last_final_header.epoch_id() != last_epoch_id { + let epoch_id = last_epoch_id.clone(); + + let epoch_sync_info = + env.clients[0].chain.chain_store().get_epoch_sync_info(&epoch_id).unwrap(); + + tracing::debug!("Checking epoch sync info: {:?}", &epoch_sync_info); + + // Check that all BlockInfos needed for new epoch sync can be reconstructed. + // This also helps with debugging if `epoch_sync_data_hash` doesn't match. + for hash in &epoch_sync_info.headers_to_save { + let block_info = env.clients[0] + .chain + .chain_store() + .store() + .get_ser::(DBCol::BlockInfo, hash.as_ref()) + .unwrap() + .unwrap(); + let reconstructed_block_info = epoch_sync_info.get_block_info(hash).unwrap(); + assert_eq!(block_info, reconstructed_block_info); + } + + assert_eq!( + epoch_sync_info.calculate_epoch_sync_data_hash().unwrap(), + epoch_sync_info.get_epoch_sync_data_hash().unwrap().unwrap(), + ); + + tracing::debug!("OK"); + } + + last_epoch_id = last_final_header.epoch_id().clone(); + } +} + +/// This is an unreliable test that mocks/reimplements sync logic. +/// After epoch sync is integrated into sync process we can write a better test. +/// +/// The test simulates two clients, one of which is +/// - stopped after one epoch +/// - synced through epoch sync, header sync, state sync, and block sync +/// - in sync with other client for two more epochs +#[test] +#[ignore] +fn test_node_after_simulated_sync() { + init_test_logger(); + let num_clients = 2; + let epoch_length = 20; + let num_epochs = 5; + // Max height for clients[0] before sync. + let max_height_0 = epoch_length * num_epochs - 1; + // Max height for clients[1] before sync. + let max_height_1 = epoch_length; + + // TestEnv setup + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + + let mut env = TestEnv::builder(ChainGenesis::test()) + .clients_count(num_clients) + .real_stores() + .use_state_snapshots() + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + // Produce blocks + let mut last_hash = *env.clients[0].chain.genesis().hash(); + let mut blocks = vec![]; + + for h in 1..max_height_0 { + for tx in generate_transactions(&last_hash, h) { + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + let block = env.clients[0].produce_block(h).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + last_hash = *block.hash(); + blocks.push(block.clone()); + + if h < max_height_1 { + env.process_block(1, block.clone(), Provenance::NONE); + } + } + + // Do "epoch sync" up to last epoch + + // Current epoch for clients[0]. + let epoch_id0 = env.clients[0].chain.header_head().unwrap().epoch_id; + // Next epoch for clients[1]. + let mut epoch_id1 = env.clients[1].chain.header_head().unwrap().epoch_id; + + // We rely on the fact that epoch_id0 is not finished for clients[0]. + // So we need to "sync" all epochs in [epoch_id1, epoch_id0). + while epoch_id1 != epoch_id0 { + tracing::debug!("Syncing epoch {:?}", epoch_id1); + + let epoch_sync_data = + env.clients[0].chain.chain_store().get_epoch_sync_info(&epoch_id1).unwrap(); + env.clients[1].chain.validate_and_record_epoch_sync_info(&epoch_sync_data).unwrap(); + + epoch_id1 = env.clients[1] + .epoch_manager + .get_next_epoch_id(&env.clients[1].chain.header_head().unwrap().last_block_hash) + .unwrap(); + } + + // Do "header sync" for the current epoch for clients[0]. + tracing::debug!("Client 0 Header Head: {:?}", env.clients[0].chain.header_head()); + tracing::debug!("Client 1 Header Head Before: {:?}", env.clients[1].chain.header_head()); + + let mut last_epoch_headers = vec![]; + for block in &blocks { + if *block.header().epoch_id() == epoch_id0 { + last_epoch_headers.push(block.header().clone()); + } + } + env.clients[1].chain.sync_block_headers(last_epoch_headers, &mut vec![]).unwrap(); + + tracing::debug!("Client 0 Header Head: {:?}", env.clients[0].chain.header_head()); + tracing::debug!("Client 1 Header Head After: {:?}", env.clients[1].chain.header_head()); + + // Do "state sync" for the last epoch + // write last block of prev epoch + { + let mut store_update = env.clients[1].chain.chain_store().store().store_update(); + + let mut last_block = &blocks[0]; + for block in &blocks { + if *block.header().epoch_id() == epoch_id0 { + break; + } + last_block = block; + } + + tracing::debug!("Write block {:?}", last_block.header()); + + store_update.insert_ser(DBCol::Block, last_block.hash().as_ref(), last_block).unwrap(); + store_update.commit().unwrap(); + } + + let sync_hash = *env.clients[0] + .epoch_manager + .get_block_info(&env.clients[0].chain.header_head().unwrap().last_block_hash) + .unwrap() + .epoch_first_block(); + tracing::debug!("SYNC HASH: {:?}", sync_hash); + for shard_id in env.clients[0].epoch_manager.shard_ids(&epoch_id0).unwrap() { + tracing::debug!("Start syncing shard {:?}", shard_id); + let sync_block_header = env.clients[0].chain.get_block_header(&sync_hash).unwrap(); + let sync_prev_header = + env.clients[0].chain.get_previous_header(&sync_block_header).unwrap(); + let sync_prev_prev_hash = sync_prev_header.prev_hash(); + + let state_header = + env.clients[0].chain.compute_state_response_header(shard_id, sync_hash).unwrap(); + let state_root = state_header.chunk_prev_state_root(); + let num_parts = get_num_state_parts(state_header.state_root_node().memory_usage); + + for part_id in 0..num_parts { + tracing::debug!("Syncing part {:?} of {:?}", part_id, num_parts); + let state_part = env.clients[0] + .chain + .runtime_adapter + .obtain_state_part( + shard_id, + sync_prev_prev_hash, + &state_root, + PartId::new(part_id, num_parts), + ) + .unwrap(); + + env.clients[1] + .runtime_adapter + .apply_state_part( + shard_id, + &state_root, + PartId::new(part_id, num_parts), + state_part.as_ref(), + &epoch_id0, + ) + .unwrap(); + } + } + + env.clients[1] + .chain + .reset_heads_post_state_sync( + &None, + sync_hash, + &mut BlockProcessingArtifact::default(), + Arc::new(|_| {}), + ) + .unwrap(); + + tracing::debug!("Client 0 Head: {:?}", env.clients[0].chain.head()); + tracing::debug!("Client 1 Head: {:?}", env.clients[1].chain.head()); + + // Do "block sync" for the last epoch + + for block in &blocks { + if *block.header().epoch_id() == epoch_id0 { + tracing::debug!("Receive block {:?}", block.header()); + env.clients[1] + .process_block_test(MaybeValidated::from(block.clone()), Provenance::NONE) + .unwrap(); + } + } + + // Produce blocks on clients[0] and process them on clients[1] + for h in max_height_0..(max_height_0 + 2 * epoch_length) { + tracing::debug!("Produce and process block {}", h); + for tx in generate_transactions(&last_hash, h) { + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + let block = env.clients[0].produce_block(h).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + env.process_block(1, block.clone(), Provenance::NONE); + last_hash = *block.hash(); + } +} diff --git a/integration-tests/src/tests/client/features.rs b/integration-tests/src/tests/client/features.rs new file mode 100644 index 000000000..4ebb2b306 --- /dev/null +++ b/integration-tests/src/tests/client/features.rs @@ -0,0 +1,23 @@ +//! Collection of feature specific tests + +mod access_key_nonce_for_implicit_accounts; +mod account_id_in_function_call_permission; +mod adversarial_behaviors; +mod cap_max_gas_price; +mod chunk_nodes_cache; +mod chunk_validation; +mod delegate_action; +#[cfg(feature = "protocol_feature_fix_contract_loading_cost")] +mod fix_contract_loading_cost; +mod fix_storage_usage; +mod flat_storage; +mod in_memory_tries; +mod increase_deployment_cost; +mod increase_storage_compute_cost; +mod limit_contract_functions_number; +mod lower_storage_key_limit; +mod nearvm; +mod restore_receipts_after_fix_apply_chunks; +mod restrict_tla; +mod wallet_contract; +mod zero_balance_account; diff --git a/integration-tests/src/tests/client/features/access_key_nonce_for_implicit_accounts.rs b/integration-tests/src/tests/client/features/access_key_nonce_for_implicit_accounts.rs new file mode 100644 index 000000000..f8c06833a --- /dev/null +++ b/integration-tests/src/tests/client/features/access_key_nonce_for_implicit_accounts.rs @@ -0,0 +1,842 @@ +use crate::tests::client::process_blocks::produce_blocks_from_height; +use assert_matches::assert_matches; +use unc_async::messaging::CanSend; +use unc_chain::orphan::NUM_ORPHAN_ANCESTORS_CHECK; +use unc_chain::{ChainGenesis, Error, Provenance}; +use unc_chain_configs::Genesis; +use unc_chunks::metrics::PARTIAL_ENCODED_CHUNK_FORWARD_CACHED_WITHOUT_HEADER; +use unc_client::test_utils::{create_chunk_with_transactions, TestEnv}; +use unc_client::ProcessTxResponse; +use unc_crypto::{InMemorySigner, KeyType, SecretKey, Signer}; +use unc_network::shards_manager::ShardsManagerRequestFromNetwork; +use unc_network::types::{NetworkRequests, PeerManagerMessageRequest}; +use unc_o11y::testonly::init_test_logger; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::account::AccessKey; +use unc_primitives::errors::InvalidTxError; +use unc_primitives::shard_layout::ShardLayout; +use unc_primitives::sharding::ChunkHash; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{AccountId, BlockHeight}; +use unc_primitives::utils::derive_unc_implicit_account_id; +use unc_primitives::version::{ProtocolFeature, ProtocolVersion}; +use unc_primitives::views::FinalExecutionStatus; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; +use framework::UNC_BASE; +use rand::seq::SliceRandom; +use rand::{thread_rng, Rng}; +use std::collections::HashSet; +use std::sync::Arc; +use tracing::debug; + +/// Try to process tx in the next blocks, check that tx and all generated receipts succeed. +/// Return height of the next block. +fn check_tx_processing( + env: &mut TestEnv, + tx: SignedTransaction, + height: BlockHeight, + blocks_number: u64, +) -> BlockHeight { + let tx_hash = tx.get_hash(); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + let next_height = produce_blocks_from_height(env, blocks_number, height); + let final_outcome = env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap(); + assert_matches!(final_outcome.status, FinalExecutionStatus::SuccessValue(_)); + next_height +} + +/// Test that duplicate transactions are properly rejected. +#[test] +fn test_transaction_hash_collision() { + let epoch_length = 5; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + + let signer0 = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let signer1 = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let send_money_tx = SignedTransaction::send_money( + 1, + "test1".parse().unwrap(), + "test0".parse().unwrap(), + &signer1, + 100, + *genesis_block.hash(), + ); + let delete_account_tx = SignedTransaction::delete_account( + 2, + "test1".parse().unwrap(), + "test1".parse().unwrap(), + "test0".parse().unwrap(), + &signer1, + *genesis_block.hash(), + ); + + assert_eq!( + env.clients[0].process_tx(send_money_tx.clone(), false, false), + ProcessTxResponse::ValidTx + ); + assert_eq!( + env.clients[0].process_tx(delete_account_tx, false, false), + ProcessTxResponse::ValidTx + ); + + for i in 1..4 { + env.produce_block(0, i); + } + + let create_account_tx = SignedTransaction::create_account( + 1, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + UNC_BASE, + signer1.public_key(), + &signer0, + *genesis_block.hash(), + ); + assert_eq!( + env.clients[0].process_tx(create_account_tx, false, false), + ProcessTxResponse::ValidTx + ); + for i in 4..8 { + env.produce_block(0, i); + } + + assert_matches!( + env.clients[0].process_tx(send_money_tx, false, false), + ProcessTxResponse::InvalidTx(_) + ); +} + +/// Helper for checking that duplicate transactions from NEAR-implicit accounts are properly rejected. +/// It creates NEAR-implicit account, deletes it and creates again, so that nonce of the access +/// key is updated. Then it tries to send tx from NEAR-implicit account with invalid nonce, which +/// should fail since the protocol upgrade. +fn get_status_of_tx_hash_collision_for_unc_implicit_account( + protocol_version: ProtocolVersion, + unc_implicit_account_signer: InMemorySigner, +) -> ProcessTxResponse { + let epoch_length = 100; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = protocol_version; + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let deposit_for_account_creation = 10u128.pow(23); + let mut height = 1; + let blocks_number = 5; + let signer1 = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let unc_implicit_account_id = unc_implicit_account_signer.account_id.clone(); + + // Send money to NEAR-implicit account, invoking its creation. + let send_money_tx = SignedTransaction::send_money( + 1, + "test1".parse().unwrap(), + unc_implicit_account_id.clone(), + &signer1, + deposit_for_account_creation, + *genesis_block.hash(), + ); + height = check_tx_processing(&mut env, send_money_tx, height, blocks_number); + let block = env.clients[0].chain.get_block_by_height(height - 1).unwrap(); + + // Delete NEAR-implicit account. + let delete_account_tx = SignedTransaction::delete_account( + // Because AccessKeyNonceRange is enabled, correctness of this nonce is guaranteed. + (height - 1) * unc_primitives::account::AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER, + unc_implicit_account_id.clone(), + unc_implicit_account_id.clone(), + "test0".parse().unwrap(), + &unc_implicit_account_signer, + *block.hash(), + ); + height = check_tx_processing(&mut env, delete_account_tx, height, blocks_number); + let block = env.clients[0].chain.get_block_by_height(height - 1).unwrap(); + + // Send money to NEAR-implicit account again, invoking its second creation. + let send_money_again_tx = SignedTransaction::send_money( + 2, + "test1".parse().unwrap(), + unc_implicit_account_id.clone(), + &signer1, + deposit_for_account_creation, + *block.hash(), + ); + height = check_tx_processing(&mut env, send_money_again_tx, height, blocks_number); + let block = env.clients[0].chain.get_block_by_height(height - 1).unwrap(); + + // Send money from NEAR-implicit account with incorrect nonce. + let send_money_from_unc_implicit_account_tx = SignedTransaction::send_money( + 1, + unc_implicit_account_id.clone(), + "test0".parse().unwrap(), + &unc_implicit_account_signer, + 100, + *block.hash(), + ); + let response = + env.clients[0].process_tx(send_money_from_unc_implicit_account_tx, false, false); + + // Check that sending money from NEAR-implicit account with correct nonce is still valid. + let send_money_from_unc_implicit_account_tx = SignedTransaction::send_money( + (height - 1) * AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER, + unc_implicit_account_id, + "test0".parse().unwrap(), + &unc_implicit_account_signer, + 100, + *block.hash(), + ); + check_tx_processing(&mut env, send_money_from_unc_implicit_account_tx, height, blocks_number); + + response +} + +/// Test that duplicate transactions from NEAR-implicit accounts are properly rejected. +#[test] +fn test_transaction_hash_collision_for_unc_implicit_account_fail() { + let protocol_version = ProtocolFeature::AccessKeyNonceForImplicitAccounts.protocol_version(); + let secret_key = SecretKey::from_seed(KeyType::ED25519, "test"); + let public_key = secret_key.public_key(); + let unc_implicit_account_id = derive_unc_implicit_account_id(public_key.unwrap_as_ed25519()); + let unc_implicit_account_signer = + InMemorySigner::from_secret_key(unc_implicit_account_id, secret_key); + assert_matches!( + get_status_of_tx_hash_collision_for_unc_implicit_account( + protocol_version, + unc_implicit_account_signer + ), + ProcessTxResponse::InvalidTx(InvalidTxError::InvalidNonce { .. }) + ); +} + +/// Test that duplicate transactions from NEAR-implicit accounts are not rejected until protocol upgrade. +#[test] +fn test_transaction_hash_collision_for_unc_implicit_account_ok() { + let protocol_version = + ProtocolFeature::AccessKeyNonceForImplicitAccounts.protocol_version() - 1; + let secret_key = SecretKey::from_seed(KeyType::ED25519, "test"); + let public_key = secret_key.public_key(); + let unc_implicit_account_id = derive_unc_implicit_account_id(public_key.unwrap_as_ed25519()); + let unc_implicit_account_signer = + InMemorySigner::from_secret_key(unc_implicit_account_id, secret_key); + assert_matches!( + get_status_of_tx_hash_collision_for_unc_implicit_account( + protocol_version, + unc_implicit_account_signer + ), + ProcessTxResponse::ValidTx + ); +} + +/// Test that chunks with transactions that have expired are considered invalid. +#[test] +fn test_chunk_transaction_validity() { + let epoch_length = 5; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let tx = SignedTransaction::send_money( + 1, + "test1".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + 100, + *genesis_block.hash(), + ); + for i in 1..200 { + env.produce_block(0, i); + } + let (encoded_shard_chunk, merkle_path, receipts, block) = + create_chunk_with_transactions(&mut env.clients[0], vec![tx]); + let validator_id = env.clients[0].validator_signer.as_ref().unwrap().validator_id().clone(); + env.clients[0] + .persist_and_distribute_encoded_chunk( + encoded_shard_chunk, + merkle_path, + receipts, + validator_id, + ) + .unwrap(); + let res = env.clients[0].process_block_test(block.into(), Provenance::NONE); + assert_matches!(res.unwrap_err(), Error::InvalidTransactions); +} + +#[test] +fn test_transaction_nonce_too_large() { + let epoch_length = 5; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let large_nonce = AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER + 1; + let tx = SignedTransaction::send_money( + large_nonce, + "test1".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + 100, + *genesis_block.hash(), + ); + assert_matches!( + env.clients[0].process_tx(tx, false, false), + ProcessTxResponse::InvalidTx(InvalidTxError::InvalidAccessKeyError(_)) + ); +} + +/// This test tests the logic regarding requesting chunks for orphan. +/// The test tests the following scenario, there is one validator(test0) and one non-validator node(test1) +/// test0 produces and processes 20 blocks and test1 processes these blocks with some delays. We +/// want to test that test1 requests missing chunks for orphans ahead of time. +/// Note: this test assumes NUM_ORPHAN_ANCESTORS_CHECK <= 5 and >= 2 +/// +/// - test1 processes blocks 1, 2 successfully +/// - test1 processes blocks 3, 4, ..., 20, but it doesn't have chunks for these blocks, so block 3 +/// will be put to the missing chunks pool while block 4 - 20 will be orphaned +/// - check that test1 sends missing chunk requests for block 4 - 2 + NUM_ORPHAN_ANCESTORS_CHECK +/// - test1 processes partial chunk responses for block 4 - 2 + NUM_ORPHAN_ANCESTORS_CHECK +/// - test1 processes partial chunk responses for block 3 +/// - check that block 3 - 2 + NUM_ORPHAN_ANCESTORS_CHECK are accepted, this confirms that the missing chunk requests are sent +/// and processed successfully for block 4 - 2 + NUM_ORPHAN_ANCESTORS_CHECK +/// - process until block 8 and check that the node sends missing chunk requests for the new orphans +/// add unlocked +/// - check that test1 does not send missing chunk requests for block 10, because it breaks +/// the requirement that the block must be in the same epoch as the next block after its accepted ancestor +/// - test1 processes partial chunk responses for block 8 and 9 +/// - check that test1 sends missing chunk requests for block 11 to 10+NUM_ORPHAN_ANCESTORS+CHECK, +/// since now they satisfy the the requirements for requesting chunks for orphans +/// - process the rest of blocks +#[test] +fn test_request_chunks_for_orphan() { + init_test_logger(); + + // Skip the test if NUM_ORPHAN_ANCESTORS_CHECK is 1, which effectively disables + // fetching chunks for orphan + if NUM_ORPHAN_ANCESTORS_CHECK == 1 { + return; + } + + let num_clients = 2; + let num_validators = 1; + let epoch_length = 10; + + let accounts: Vec = + (0..num_clients).map(|i| format!("test{}", i).parse().unwrap()).collect(); + let mut genesis = Genesis::test(accounts, num_validators); + genesis.config.epoch_length = epoch_length; + // make the blockchain to 4 shards + genesis.config.shard_layout = ShardLayout::v1_test(); + genesis.config.num_block_producer_seats_per_shard = + vec![num_validators, num_validators, num_validators, num_validators]; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .clients_count(num_clients) + .validator_seats(num_validators as usize) + .real_epoch_managers(&genesis.config) + .track_all_shards() + .nightshade_runtimes_with_runtime_config_store( + &genesis, + vec![RuntimeConfigStore::test(), RuntimeConfigStore::test()], + ) + .build(); + + let mut blocks = vec![]; + // produce 20 blocks + for i in 1..=20 { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + blocks.push(block.clone()); + env.process_block(0, block, Provenance::PRODUCED); + } + + let _ = env.clients[1].process_block_test(blocks[0].clone().into(), Provenance::NONE).unwrap(); + // process blocks 1, 2 successfully + for i in 1..3 { + let res = env.clients[1].process_block_test(blocks[i].clone().into(), Provenance::NONE); + assert_matches!( + res.unwrap_err(), + unc_chain::Error::ChunksMissing(_) | unc_chain::Error::Orphan + ); + env.process_shards_manager_responses_and_finish_processing_blocks(1); + env.process_partial_encoded_chunks_requests(1); + } + env.process_shards_manager_responses_and_finish_processing_blocks(1); + + // process blocks 3 to 15 without processing missing chunks + // block 3 will be put into the blocks_with_missing_chunks pool + let res = env.clients[1].process_block_test(blocks[3].clone().into(), Provenance::NONE); + assert_matches!(res.unwrap_err(), unc_chain::Error::ChunksMissing(_)); + // remove the missing chunk request from the network queue because we want to process it later + let missing_chunk_request = env.network_adapters[1].pop().unwrap(); + // block 4-20 will be put to the orphan pool + for i in 4..20 { + let res = env.clients[1].process_block_test(blocks[i].clone().into(), Provenance::NONE); + assert_matches!(res.unwrap_err(), unc_chain::Error::Orphan); + } + // check that block 4-2+NUM_ORPHAN_ANCESTORS_CHECK requested partial encoded chunks already + for i in 4..3 + NUM_ORPHAN_ANCESTORS_CHECK { + assert!( + env.clients[1].chain.check_orphan_partial_chunks_requested(blocks[i as usize].hash()), + "{}", + i + ); + } + assert!(!env.clients[1].chain.check_orphan_partial_chunks_requested( + blocks[3 + NUM_ORPHAN_ANCESTORS_CHECK as usize].hash() + )); + assert!(!env.clients[1].chain.check_orphan_partial_chunks_requested( + blocks[4 + NUM_ORPHAN_ANCESTORS_CHECK as usize].hash() + )); + // process all the partial encoded chunk requests for block 4 - 2 + NUM_ORPHAN_ANCESTORS_CHECK + env.process_partial_encoded_chunks_requests(1); + env.process_shards_manager_responses_and_finish_processing_blocks(1); + + // process partial encoded chunk request for block 3, which will unlock block 4 - 2 + NUM_ORPHAN_ANCESTORS_CHECK + env.process_partial_encoded_chunk_request(1, missing_chunk_request); + env.process_shards_manager_responses_and_finish_processing_blocks(1); + assert_eq!( + &env.clients[1].chain.head().unwrap().last_block_hash, + blocks[2 + NUM_ORPHAN_ANCESTORS_CHECK as usize].hash() + ); + + // check that `check_orphans` will request PartialChunks for new orphans as new blocks are processed + // keep processing the partial encoded chunk requests in the queue, which will process + // block 3+NUM_ORPHAN_ANCESTORS to 8. + for i in 4 + NUM_ORPHAN_ANCESTORS_CHECK..10 { + assert!(env.clients[1] + .chain + .check_orphan_partial_chunks_requested(blocks[i as usize].hash())); + for _ in 0..4 { + let request = env.network_adapters[1].pop().unwrap(); + env.process_partial_encoded_chunk_request(1, request); + env.process_shards_manager_responses_and_finish_processing_blocks(1); + } + } + assert_eq!(&env.clients[1].chain.head().unwrap().last_block_hash, blocks[8].hash()); + // blocks[10] is at the new epoch, so we can't request partial chunks for it yet + assert!(!env.clients[1].chain.check_orphan_partial_chunks_requested(blocks[10].hash())); + + // process missing chunks for block 9, which has 4 chunks, so there are 4 requests in total + for _ in 0..4 { + let request = env.network_adapters[1].pop().unwrap(); + env.process_partial_encoded_chunk_request(1, request); + env.process_shards_manager_responses_and_finish_processing_blocks(1); + } + assert_eq!(&env.clients[1].chain.head().unwrap().last_block_hash, blocks[9].hash()); + + for i in 11..10 + NUM_ORPHAN_ANCESTORS_CHECK { + assert!(env.clients[1] + .chain + .check_orphan_partial_chunks_requested(blocks[i as usize].hash())); + } + + // process the rest of blocks + for i in 10..20 { + // process missing chunk requests for the 4 chunks in each block + for _ in 0..4 { + let request = env.network_adapters[1].pop().unwrap(); + env.process_partial_encoded_chunk_request(1, request); + } + env.process_shards_manager_responses_and_finish_processing_blocks(1); + assert_eq!(&env.clients[1].chain.head().unwrap().last_block_hash, blocks[i].hash()); + } +} + +/// This test tests that if a node's requests for chunks are eventually answered, +/// it can process blocks, which also means chunks and parts and processed correctly. +/// It can be seen as a sanity test for the logic in processing chunks, +/// while abstracting away the logic for requesting chunks by assuming chunks requests are +/// always answered (it does test for delayed response). +/// +/// This test tests the following scenario: there is one validator(test0) and one non-validator node(test1) +/// test0 produces and processes 21 blocks and test1 processes these blocks. +/// test1 processes the blocks in some random order, to simulate in production, a node may not +/// receive blocks in order. All of test1's requests for chunks are eventually answered, but +/// with some delays. In the end, we check that test1 processes all 21 blocks, and it only +/// requests for each chunk once +#[test] +fn test_processing_chunks_sanity() { + init_test_logger(); + + let num_clients = 2; + let num_validators = 1; + let epoch_length = 10; + + let accounts: Vec = + (0..num_clients).map(|i| format!("test{}", i).parse().unwrap()).collect(); + let mut genesis = Genesis::test(accounts, num_validators); + genesis.config.epoch_length = epoch_length; + // make the blockchain to 4 shards + genesis.config.shard_layout = ShardLayout::v1_test(); + genesis.config.num_block_producer_seats_per_shard = + vec![num_validators, num_validators, num_validators, num_validators]; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .clients_count(num_clients) + .validator_seats(num_validators as usize) + .real_epoch_managers(&genesis.config) + .track_all_shards() + .nightshade_runtimes(&genesis) + .build(); + + let mut blocks = vec![]; + // produce 21 blocks + for i in 1..=21 { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + let chunks = block + .chunks() + .iter() + .map(|chunk| format!("{:?}", chunk.chunk_hash())) + .collect::>(); + debug!(target: "chunks", "Block #{} has chunks {:?}", i, chunks.join(", ")); + blocks.push(block.clone()); + env.process_block(0, block, Provenance::PRODUCED); + } + + // make test1 process these blocks, while grouping blocks to groups of three + // and process blocks in each group in a random order. + // Verify that it can process the blocks successfully if all its requests for missing + // chunks are answered + let mut rng = thread_rng(); + let mut num_requests = 0; + for i in 0..=6 { + let mut next_blocks: Vec<_> = (3 * i..3 * i + 3).collect(); + next_blocks.shuffle(&mut rng); + for ind in next_blocks { + let _ = env.clients[1].start_process_block( + blocks[ind].clone().into(), + Provenance::NONE, + Arc::new(|_| {}), + ); + if rng.gen_bool(0.5) { + env.process_shards_manager_responses_and_finish_processing_blocks(1); + } + while let Some(request) = env.network_adapters[1].pop() { + // process the chunk request some times, otherwise keep it in the queue + // this is to simulate delays in the network + if rng.gen_bool(0.7) { + env.process_partial_encoded_chunk_request(1, request); + num_requests += 1; + } else { + env.network_adapters[1].send(request); + } + } + } + env.process_shards_manager_responses_and_finish_processing_blocks(1); + } + // process the remaining chunk requests + while let Some(request) = env.network_adapters[1].pop() { + env.process_partial_encoded_chunk_request(1, request); + env.process_shards_manager_responses_and_finish_processing_blocks(1); + num_requests += 1; + } + + assert_eq!(env.clients[1].chain.head().unwrap().height, 21); + + // Check each chunk is only requested once. + // There are 21 blocks in total, but the first block has no chunks, + assert_eq!(num_requests, 4 * 20); +} + +struct ChunkForwardingOptimizationTestData { + num_validators: usize, + env: TestEnv, + + num_part_ords_requested: usize, + num_part_ords_sent_as_partial_encoded_chunk: usize, + num_part_ords_forwarded: usize, + chunk_parts_that_must_be_known: HashSet<(ChunkHash, u64, usize)>, +} + +impl ChunkForwardingOptimizationTestData { + fn new() -> ChunkForwardingOptimizationTestData { + let num_clients = 4; + let num_validators = 4 as usize; + let num_block_producers = 1; + let epoch_length = 10; + + let accounts: Vec = + (0..num_clients).map(|i| format!("test{}", i).parse().unwrap()).collect(); + let mut genesis = Genesis::test(accounts, num_validators as u64); + { + let config = &mut genesis.config; + config.epoch_length = epoch_length; + config.shard_layout = ShardLayout::v1_test(); + config.num_block_producer_seats_per_shard = vec![ + num_block_producers as u64, + num_block_producers as u64, + num_block_producers as u64, + num_block_producers as u64, + ]; + config.num_block_producer_seats = num_block_producers as u64; + } + let chain_genesis = ChainGenesis::new(&genesis); + let env = TestEnv::builder(chain_genesis) + .clients_count(num_clients) + .validator_seats(num_validators as usize) + .real_epoch_managers(&genesis.config) + .track_all_shards() + .nightshade_runtimes(&genesis) + .build(); + + ChunkForwardingOptimizationTestData { + num_validators, + env, + num_part_ords_requested: 0, + num_part_ords_sent_as_partial_encoded_chunk: 0, + num_part_ords_forwarded: 0, + chunk_parts_that_must_be_known: HashSet::new(), + } + } + + fn process_one_peer_message(&mut self, client_id: usize, requests: NetworkRequests) { + match requests { + NetworkRequests::PartialEncodedChunkRequest { ref target, ref request, .. } => { + for part_ord in &request.part_ords { + assert!( + self.chunk_parts_that_must_be_known.insert(( + request.chunk_hash.clone(), + *part_ord, + client_id + )), + "chunk request from {} to {:?} for chunk {} with part_ords {:?}", + client_id, + target, + hex::encode(&request.chunk_hash.as_bytes()[..4]), + part_ord, + ); + } + debug!( + target: "test", + "chunk request from {} to {:?} for chunk {} with part_ords {:?}", + client_id, + target, + hex::encode(&request.chunk_hash.as_bytes()[..4]), + request.part_ords + ); + self.num_part_ords_requested += request.part_ords.len(); + self.env.process_partial_encoded_chunk_request( + client_id, + PeerManagerMessageRequest::NetworkRequests(requests), + ); + } + NetworkRequests::PartialEncodedChunkMessage { account_id, partial_encoded_chunk } => { + debug!( + target: "test", + "chunk msg from {} to {} height {} hash {} shard {} parts {:?}", + client_id, + account_id, + partial_encoded_chunk.header.height_created(), + hex::encode(&partial_encoded_chunk.header.chunk_hash().as_bytes()[..4]), + partial_encoded_chunk.header.shard_id(), + partial_encoded_chunk.parts.iter().map(|p| p.part_ord).collect::>() + ); + for part in &partial_encoded_chunk.parts { + self.chunk_parts_that_must_be_known.insert(( + partial_encoded_chunk.header.chunk_hash(), + part.part_ord, + client_id, + )); + } + self.num_part_ords_sent_as_partial_encoded_chunk += + partial_encoded_chunk.parts.len(); + self.env.shards_manager(&account_id).send( + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunk( + partial_encoded_chunk.into(), + ), + ); + } + NetworkRequests::PartialEncodedChunkForward { account_id, forward } => { + debug!( + target: "test", + "chunk forward from {} to {} hash {} parts {:?}", + client_id, + account_id, + hex::encode(&forward.chunk_hash.as_bytes()[..4]), + forward.parts.iter().map(|p| p.part_ord).collect::>() + ); + for part_ord in &forward.parts { + self.chunk_parts_that_must_be_known.insert(( + forward.chunk_hash.clone(), + part_ord.part_ord, + client_id, + )); + } + self.num_part_ords_forwarded += forward.parts.len(); + self.env.shards_manager(&account_id).send( + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkForward(forward), + ); + } + NetworkRequests::ChunkStateWitness(_, _) => {} + NetworkRequests::ChunkEndorsement(_, _) => {} + _ => { + panic!("Unexpected network request: {:?}", requests); + } + } + } + + fn process_network_messages(&mut self) { + loop { + let mut any_message_processed = false; + for i in 0..self.num_validators { + if let Some(msg) = self.env.network_adapters[i].pop() { + any_message_processed = true; + match msg { + PeerManagerMessageRequest::NetworkRequests(requests) => { + self.process_one_peer_message(i, requests); + } + _ => { + panic!("Unexpected message: {:?}", msg); + } + } + } + } + if !any_message_processed { + break; + } + } + } +} + +#[test] +fn test_chunk_forwarding_optimization() { + // Tests that a node should fully take advantage of forwarded chunk parts to never request + // a part that was already forwarded to it. We simulate four validator nodes, with one + // block producer and four chunk producers. + init_test_logger(); + PARTIAL_ENCODED_CHUNK_FORWARD_CACHED_WITHOUT_HEADER.reset(); + let mut test = ChunkForwardingOptimizationTestData::new(); + loop { + let height = test.env.clients[0].chain.head().unwrap().height; + if height >= 31 { + break; + } + debug!(target: "test", "======= Height {} ======", height + 1); + test.process_network_messages(); + test.env.process_shards_manager_responses(0); + + let block = test.env.clients[0].produce_block(height + 1).unwrap().unwrap(); + if block.header().height() > 1 { + // For any block except the first, the previous block's application at each + // current chunk producer should have produced a chunk and distributed the chunk. + // Since we've processed all network messages just now, the block producer should + // have all chunks and able to create a block with all chunks. So we check the + // heights. + for i in 0..4 { + assert_eq!(block.chunks()[i].height_created(), block.header().height()); + } + } + // The block producer of course has the complete block so we can process that. + for i in 0..test.num_validators { + debug!(target: "test", "Processing block {} as validator #{}", block.header().height(), i); + let _ = test.env.clients[i].start_process_block( + block.clone().into(), + if i == 0 { Provenance::PRODUCED } else { Provenance::NONE }, + Arc::new(|_| {}), + ); + let mut accepted_blocks = + test.env.clients[i].finish_block_in_processing(block.header().hash()); + // Process any chunk part requests that this client sent. Note that this would also + // process other network messages (such as production of the next chunk) which is OK. + test.process_network_messages(); + test.env.process_shards_manager_responses(i); + accepted_blocks.extend(test.env.clients[i].finish_blocks_in_processing()); + assert_eq!( + accepted_blocks.len(), + 1, + "Processing of block {} failed at validator #{}", + block.header().height(), + i + ); + assert_eq!(&accepted_blocks[0], block.header().hash()); + assert_eq!(test.env.clients[i].chain.head().unwrap().height, block.header().height()); + } + } + + // With very high probability we should've encountered some cases where forwarded parts + // could not be applied because the chunk header is not available. Assert this did indeed + // happen. + assert!(PARTIAL_ENCODED_CHUNK_FORWARD_CACHED_WITHOUT_HEADER.get() > 0.0); + debug!(target: "test", + "Counters for debugging: + num_part_ords_requested: {} + num_part_ords_sent_as_partial_encoded_chunk: {} + num_part_ords_forwarded: {} + num_forwards_with_missing_chunk_header: {}", + test.num_part_ords_requested, + test.num_part_ords_sent_as_partial_encoded_chunk, + test.num_part_ords_forwarded, + PARTIAL_ENCODED_CHUNK_FORWARD_CACHED_WITHOUT_HEADER.get(), + ); +} + +/// Test asynchronous block processing (start_process_block_async). +/// test0 produces 20 blocks. Shuffle the 20 blocks and make test1 process these blocks. +/// Verify that test1 can succesfully finish processing the 20 blocks +#[test] +fn test_processing_blocks_async() { + init_test_logger(); + + let num_clients = 2; + let num_validators = 1; + let epoch_length = 10; + + let accounts: Vec = + (0..num_clients).map(|i| format!("test{}", i).parse().unwrap()).collect(); + let mut genesis = Genesis::test(accounts, num_validators); + genesis.config.epoch_length = epoch_length; + // make the blockchain to 4 shards + genesis.config.shard_layout = ShardLayout::v1_test(); + genesis.config.num_block_producer_seats_per_shard = + vec![num_validators, num_validators, num_validators, num_validators]; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .clients_count(num_clients) + .validator_seats(num_validators as usize) + .real_epoch_managers(&genesis.config) + .track_all_shards() + .nightshade_runtimes(&genesis) + .build(); + + let mut blocks = vec![]; + // produce 20 blocks + for i in 1..=20 { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + blocks.push(block.clone()); + env.process_block(0, block, Provenance::PRODUCED); + } + + let mut rng = thread_rng(); + blocks.shuffle(&mut rng); + for ind in 0..blocks.len() { + let _ = env.clients[1].start_process_block( + blocks[ind].clone().into(), + Provenance::NONE, + Arc::new(|_| {}), + ); + } + + env.process_shards_manager_responses_and_finish_processing_blocks(1); + + while let Some(request) = env.network_adapters[1].pop() { + env.process_partial_encoded_chunk_request(1, request); + env.process_shards_manager_responses_and_finish_processing_blocks(1); + } + + assert_eq!(env.clients[1].chain.head().unwrap().height, 20); +} diff --git a/integration-tests/src/tests/client/features/account_id_in_function_call_permission.rs b/integration-tests/src/tests/client/features/account_id_in_function_call_permission.rs new file mode 100644 index 000000000..728de6874 --- /dev/null +++ b/integration-tests/src/tests/client/features/account_id_in_function_call_permission.rs @@ -0,0 +1,135 @@ +use unc_chain::ChainGenesis; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_client::ProcessTxResponse; +use unc_crypto::{InMemorySigner, KeyType, Signer}; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::account::{AccessKey, AccessKeyPermission, FunctionCallPermission}; +use unc_primitives::errors::{ActionsValidationError, InvalidTxError}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::transaction::{Action, AddKeyAction, Transaction}; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; + +#[test] +fn test_account_id_in_function_call_permission_upgrade() { + // The immediate protocol upgrade needs to be set for this test to pass in + // the release branch where the protocol upgrade date is set. + std::env::set_var("unc_TESTS_IMMEDIATE_PROTOCOL_UPGRADE", "1"); + + let old_protocol_version = + unc_primitives::version::ProtocolFeature::AccountIdInFunctionCallPermission + .protocol_version() + - 1; + let new_protocol_version = old_protocol_version + 1; + + // Prepare TestEnv with a contract at the old protocol version. + let mut env = { + let epoch_length = 5; + let mut genesis = + Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = old_protocol_version; + let chain_genesis = ChainGenesis::new(&genesis); + TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes_with_runtime_config_store( + &genesis, + vec![RuntimeConfigStore::new(None)], + ) + .build() + }; + + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let tx = Transaction { + signer_id: "test0".parse().unwrap(), + receiver_id: "test0".parse().unwrap(), + public_key: signer.public_key(), + actions: vec![Action::AddKey(Box::new(AddKeyAction { + public_key: signer.public_key(), + access_key: AccessKey { + nonce: 1, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: "#".to_string(), + method_names: vec![], + }), + }, + }))], + nonce: 0, + block_hash: CryptoHash::default(), + }; + + // Run the transaction, it should pass as we don't do validation at this protocol version. + { + let tip = env.clients[0].chain.head().unwrap(); + let signed_transaction = + Transaction { nonce: 10, block_hash: tip.last_block_hash, ..tx.clone() }.sign(&signer); + assert_eq!( + env.clients[0].process_tx(signed_transaction, false, false), + ProcessTxResponse::ValidTx + ); + for i in 0..3 { + env.produce_block(0, tip.height + i + 1); + } + }; + + env.upgrade_protocol(new_protocol_version); + + // Re-run the transaction, now it fails due to invalid account id. + { + let tip = env.clients[0].chain.head().unwrap(); + let signed_transaction = + Transaction { nonce: 11, block_hash: tip.last_block_hash, ..tx }.sign(&signer); + assert_eq!( + env.clients[0].process_tx(signed_transaction, false, false), + ProcessTxResponse::InvalidTx(InvalidTxError::ActionsValidation( + ActionsValidationError::InvalidAccountId { account_id: "#".to_string() } + )) + ) + }; +} + +#[test] +fn test_very_long_account_id() { + let mut env = { + let genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let chain_genesis = ChainGenesis::new(&genesis); + TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes_with_runtime_config_store( + &genesis, + vec![RuntimeConfigStore::new(None)], + ) + .build() + }; + + let tip = env.clients[0].chain.head().unwrap(); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let tx = Transaction { + signer_id: "test0".parse().unwrap(), + receiver_id: "test0".parse().unwrap(), + public_key: signer.public_key(), + actions: vec![Action::AddKey(Box::new(AddKeyAction { + public_key: signer.public_key(), + access_key: AccessKey { + nonce: 1, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: "A".repeat(1024), + method_names: vec![], + }), + }, + }))], + nonce: 0, + block_hash: tip.last_block_hash, + } + .sign(&signer); + + assert_eq!( + env.clients[0].process_tx(tx, false, false), + ProcessTxResponse::InvalidTx(InvalidTxError::ActionsValidation( + ActionsValidationError::InvalidAccountId { account_id: "A".repeat(128) } + )) + ) +} diff --git a/integration-tests/src/tests/client/features/adversarial_behaviors.rs b/integration-tests/src/tests/client/features/adversarial_behaviors.rs new file mode 100644 index 000000000..d3d5a284b --- /dev/null +++ b/integration-tests/src/tests/client/features/adversarial_behaviors.rs @@ -0,0 +1,342 @@ +use std::{collections::HashSet, sync::Arc}; + +use unc_async::messaging::CanSend; +use unc_chain::{ChainGenesis, Provenance}; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_network::{ + shards_manager::ShardsManagerRequestFromNetwork, + types::{NetworkRequests, PeerManagerMessageRequest}, +}; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::{ + shard_layout::ShardLayout, + types::{AccountId, EpochId, ShardId}, +}; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; +use tracing::log::debug; + +struct AdversarialBehaviorTestData { + num_validators: usize, + env: TestEnv, +} + +const EPOCH_LENGTH: u64 = 20; + +impl AdversarialBehaviorTestData { + fn new() -> AdversarialBehaviorTestData { + let num_clients = 8; + let num_validators = 8 as usize; + let num_block_producers = 4; + let epoch_length = EPOCH_LENGTH; + + let accounts: Vec = + (0..num_clients).map(|i| format!("test{}", i).parse().unwrap()).collect(); + let mut genesis = Genesis::test(accounts, num_validators as u64); + { + let config = &mut genesis.config; + config.epoch_length = epoch_length; + config.shard_layout = ShardLayout::v1_test(); + config.num_block_producer_seats_per_shard = vec![ + num_block_producers as u64, + num_block_producers as u64, + num_block_producers as u64, + num_block_producers as u64, + ]; + config.num_block_producer_seats = num_block_producers as u64; + // Configure kickout threshold at 50%. + config.block_producer_kickout_threshold = 50; + config.chunk_producer_kickout_threshold = 50; + } + let chain_genesis = ChainGenesis::new(&genesis); + let env = TestEnv::builder(chain_genesis) + .clients_count(num_clients) + .validator_seats(num_validators as usize) + .real_epoch_managers(&genesis.config) + .track_all_shards() + .nightshade_runtimes(&genesis) + .build(); + + AdversarialBehaviorTestData { num_validators, env } + } + + fn process_one_peer_message(&mut self, client_id: usize, requests: NetworkRequests) { + match requests { + NetworkRequests::PartialEncodedChunkRequest { .. } => { + self.env.process_partial_encoded_chunk_request( + client_id, + PeerManagerMessageRequest::NetworkRequests(requests), + ); + } + NetworkRequests::PartialEncodedChunkMessage { account_id, partial_encoded_chunk } => { + self.env.shards_manager(&account_id).send( + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunk( + partial_encoded_chunk.into(), + ), + ); + } + NetworkRequests::PartialEncodedChunkForward { account_id, forward } => { + self.env.shards_manager(&account_id).send( + ShardsManagerRequestFromNetwork::ProcessPartialEncodedChunkForward(forward), + ); + } + NetworkRequests::Challenge(_) => { + // challenges not enabled. + } + NetworkRequests::ChunkStateWitness(_, _) => { + // TODO(#10265). + } + NetworkRequests::ChunkEndorsement(_, _) => { + // TODO(#10265). + } + _ => { + panic!("Unexpected network request: {:?}", requests); + } + } + } + + fn process_all_actor_messages(&mut self) { + loop { + let mut any_message_processed = false; + for i in 0..self.num_validators { + if let Some(msg) = self.env.network_adapters[i].pop() { + any_message_processed = true; + match msg { + PeerManagerMessageRequest::NetworkRequests(requests) => { + self.process_one_peer_message(i, requests); + } + _ => { + panic!("Unexpected message: {:?}", msg); + } + } + } + } + for i in 0..self.env.clients.len() { + any_message_processed |= self.env.process_shards_manager_responses(i); + } + if !any_message_processed { + break; + } + } + } +} + +#[test] +fn test_non_adversarial_case() { + init_test_logger(); + let mut test = AdversarialBehaviorTestData::new(); + let epoch_manager = test.env.clients[0].epoch_manager.clone(); + for height in 1..=EPOCH_LENGTH * 4 + 5 { + debug!(target: "test", "======= Height {} ======", height); + test.process_all_actor_messages(); + let epoch_id = epoch_manager + .get_epoch_id_from_prev_block( + &test.env.clients[0].chain.head().unwrap().last_block_hash, + ) + .unwrap(); + let block_producer = epoch_manager.get_block_producer(&epoch_id, height).unwrap(); + + let block = test.env.client(&block_producer).produce_block(height).unwrap().unwrap(); + assert_eq!(block.header().height(), height); + + if height > 1 { + assert_eq!(block.header().prev_height().unwrap(), height - 1); + let prev_block = + test.env.clients[0].chain.get_block(&block.header().prev_hash()).unwrap(); + for i in 0..4 { + // TODO: mysteriously we might miss a chunk around epoch boundaries. + // Figure out why... + assert!( + block.chunks()[i].height_created() == prev_block.header().height() + 1 + || (height % EPOCH_LENGTH == 1 + && block.chunks()[i].chunk_hash() + == prev_block.chunks()[i].chunk_hash()) + ); + } + } + + for i in 0..test.num_validators { + debug!(target: "test", "Processing block {} as validator #{}", height, i); + let _ = test.env.clients[i].start_process_block( + block.clone().into(), + if i == 0 { Provenance::PRODUCED } else { Provenance::NONE }, + Arc::new(|_| {}), + ); + let mut accepted_blocks = + test.env.clients[i].finish_block_in_processing(block.header().hash()); + // Process any chunk part requests that this client sent. Note that this would also + // process other network messages (such as production of the next chunk) which is OK. + test.process_all_actor_messages(); + accepted_blocks.extend(test.env.clients[i].finish_blocks_in_processing()); + + assert_eq!( + accepted_blocks.len(), + 1, + "Processing of block {} failed at validator #{}", + height, + i + ); + assert_eq!(&accepted_blocks[0], block.header().hash()); + assert_eq!(test.env.clients[i].chain.head().unwrap().height, height); + } + } + + // Sanity check that the final chain head is what we expect + assert_eq!(test.env.clients[0].chain.head().unwrap().height, EPOCH_LENGTH * 4 + 5); + let final_prev_block_hash = test.env.clients[0].chain.head().unwrap().prev_block_hash; + let final_epoch_id = + epoch_manager.get_epoch_id_from_prev_block(&final_prev_block_hash).unwrap(); + let final_block_producers = epoch_manager + .get_epoch_block_producers_ordered(&final_epoch_id, &final_prev_block_hash) + .unwrap(); + // No producers should be kicked out. + assert_eq!(final_block_producers.len(), 4); + let final_chunk_producers = epoch_manager.get_epoch_chunk_producers(&final_epoch_id).unwrap(); + assert_eq!(final_chunk_producers.len(), 8); +} + +// Not marking this with test_features, because it's good to ensure this compiles, and also +// if we mark this with features we'd also have to mark a bunch of imports as features. +#[allow(dead_code)] +fn test_banning_chunk_producer_when_seeing_invalid_chunk_base( + mut test: AdversarialBehaviorTestData, +) { + let epoch_manager = test.env.clients[0].epoch_manager.clone(); + let bad_chunk_producer = + test.env.clients[7].validator_signer.as_ref().unwrap().validator_id().clone(); + let mut epochs_seen_invalid_chunk: HashSet = HashSet::new(); + let mut last_block_skipped = false; + for height in 1..=EPOCH_LENGTH * 4 + 5 { + debug!(target: "test", "======= Height {} ======", height); + test.process_all_actor_messages(); + let epoch_id = epoch_manager + .get_epoch_id_from_prev_block( + &test.env.clients[0].chain.head().unwrap().last_block_hash, + ) + .unwrap(); + let block_producer = epoch_manager.get_block_producer(&epoch_id, height).unwrap(); + + let block = test.env.client(&block_producer).produce_block(height).unwrap().unwrap(); + assert_eq!(block.header().height(), height); + + let mut invalid_chunks_in_this_block: HashSet = HashSet::new(); + let mut this_block_should_be_skipped = false; + if height > 1 { + if last_block_skipped { + assert_eq!(block.header().prev_height().unwrap(), height - 2); + } else { + assert_eq!(block.header().prev_height().unwrap(), height - 1); + } + for shard_id in 0..4 { + let chunk_producer = epoch_manager + .get_chunk_producer( + &epoch_id, + block.header().prev_height().unwrap() + 1, + shard_id, + ) + .unwrap(); + if &chunk_producer == &bad_chunk_producer { + invalid_chunks_in_this_block.insert(shard_id); + if !epochs_seen_invalid_chunk.contains(&epoch_id) { + this_block_should_be_skipped = true; + epochs_seen_invalid_chunk.insert(epoch_id.clone()); + } + } + } + } + debug!(target: "test", "Epoch id of new block: {:?}", epoch_id); + debug!(target: "test", "Block should be skipped: {}; previous block skipped: {}", + this_block_should_be_skipped, last_block_skipped); + + if height > 1 { + let prev_block = + test.env.clients[0].chain.get_block(&block.header().prev_hash()).unwrap(); + for i in 0..4 { + if invalid_chunks_in_this_block.contains(&(i as ShardId)) + && !this_block_should_be_skipped + { + assert_eq!(block.chunks()[i].chunk_hash(), prev_block.chunks()[i].chunk_hash()); + } else { + // TODO: mysteriously we might miss a chunk around epoch boundaries. + // Figure out why... + assert!( + block.chunks()[i].height_created() == prev_block.header().height() + 1 + || (height % EPOCH_LENGTH == 1 + && block.chunks()[i].chunk_hash() + == prev_block.chunks()[i].chunk_hash()) + ); + } + } + } + + // The block producer of course has the complete block so we can process that. + for i in 0..test.num_validators { + debug!(target: "test", "Processing block {} as validator #{}", height, i); + let _ = test.env.clients[i].start_process_block( + block.clone().into(), + if i == 0 { Provenance::PRODUCED } else { Provenance::NONE }, + Arc::new(|_| {}), + ); + let mut accepted_blocks = + test.env.clients[i].finish_block_in_processing(block.header().hash()); + // Process any chunk part requests that this client sent. Note that this would also + // process other network messages (such as production of the next chunk) which is OK. + test.process_all_actor_messages(); + accepted_blocks.extend(test.env.clients[i].finish_blocks_in_processing()); + + if this_block_should_be_skipped { + assert_eq!( + accepted_blocks.len(), + 0, + "Processing of block {} should have failed due to invalid chunk", + height + ); + } else { + assert_eq!( + accepted_blocks.len(), + 1, + "Processing of block {} failed at validator #{}", + height, + i + ); + assert_eq!(&accepted_blocks[0], block.header().hash()); + assert_eq!(test.env.clients[i].chain.head().unwrap().height, height); + } + } + last_block_skipped = this_block_should_be_skipped; + } + + // Sanity check that the final chain head is what we expect + assert_eq!(test.env.clients[0].chain.head().unwrap().height, EPOCH_LENGTH * 4 + 5); + // Bad validator should've been kicked out in the third epoch, so it only had two chances + // to produce bad chunks. Other validators should not be kicked out. + assert_eq!(epochs_seen_invalid_chunk.len(), 2); + let final_prev_block_hash = test.env.clients[0].chain.head().unwrap().prev_block_hash; + let final_epoch_id = + epoch_manager.get_epoch_id_from_prev_block(&final_prev_block_hash).unwrap(); + let final_block_producers = epoch_manager + .get_epoch_block_producers_ordered(&final_epoch_id, &final_prev_block_hash) + .unwrap(); + assert!(final_block_producers.len() >= 3); // 3 validators if the bad validator was a block producer + let final_chunk_producers = epoch_manager.get_epoch_chunk_producers(&final_epoch_id).unwrap(); + assert_eq!(final_chunk_producers.len(), 7); +} + +#[test] +#[cfg(feature = "test_features")] +fn test_banning_chunk_producer_when_seeing_invalid_chunk() { + init_test_logger(); + let mut test = AdversarialBehaviorTestData::new(); + test.env.clients[7].produce_invalid_chunks = true; + test_banning_chunk_producer_when_seeing_invalid_chunk_base(test); +} + +#[test] +#[cfg(feature = "test_features")] +fn test_banning_chunk_producer_when_seeing_invalid_tx_in_chunk() { + init_test_logger(); + let mut test = AdversarialBehaviorTestData::new(); + test.env.clients[7].produce_invalid_tx_in_chunks = true; + test_banning_chunk_producer_when_seeing_invalid_chunk_base(test); +} diff --git a/integration-tests/src/tests/client/features/cap_max_gas_price.rs b/integration-tests/src/tests/client/features/cap_max_gas_price.rs new file mode 100644 index 000000000..f7038417a --- /dev/null +++ b/integration-tests/src/tests/client/features/cap_max_gas_price.rs @@ -0,0 +1,36 @@ +use unc_primitives::num_rational::Ratio; +use unc_primitives::version::{ProtocolFeature, ProtocolVersion}; + +use crate::tests::client::process_blocks::prepare_env_with_congestion; + +fn does_gas_price_exceed_limit(protocol_version: ProtocolVersion) -> bool { + let mut env = prepare_env_with_congestion(protocol_version, Some(Ratio::new_raw(2, 1)), 7).0; + let mut was_congested = false; + let mut price_exceeded_limit = false; + + for i in 3..20 { + env.produce_block(0, i); + let block = env.clients[0].chain.get_block_by_height(i).unwrap().clone(); + let protocol_version = env.clients[0] + .epoch_manager + .get_epoch_protocol_version(block.header().epoch_id()) + .unwrap(); + let min_gas_price = + env.clients[0].chain.block_economics_config.min_gas_price(protocol_version); + was_congested |= block.chunks()[0].prev_gas_used() >= block.chunks()[0].gas_limit(); + price_exceeded_limit |= block.header().next_gas_price() > 20 * min_gas_price; + } + + assert!(was_congested); + price_exceeded_limit +} + +#[test] +fn test_not_capped_gas_price() { + assert!(does_gas_price_exceed_limit(ProtocolFeature::CapMaxGasPrice.protocol_version() - 1)); +} + +#[test] +fn test_capped_gas_price() { + assert!(!does_gas_price_exceed_limit(ProtocolFeature::CapMaxGasPrice.protocol_version())); +} diff --git a/integration-tests/src/tests/client/features/chunk_nodes_cache.rs b/integration-tests/src/tests/client/features/chunk_nodes_cache.rs new file mode 100644 index 000000000..c1604fdb4 --- /dev/null +++ b/integration-tests/src/tests/client/features/chunk_nodes_cache.rs @@ -0,0 +1,152 @@ +use crate::tests::client::process_blocks::{deploy_test_contract, set_block_protocol_version}; +use assert_matches::assert_matches; +use unc_chain::{ChainGenesis, Provenance}; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_client::ProcessTxResponse; +use unc_crypto::{InMemorySigner, KeyType, Signer}; +use unc_parameters::{ExtCosts, RuntimeConfigStore}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::test_utils::encode; +use unc_primitives::transaction::{ + Action, ExecutionMetadata, FunctionCallAction, SignedTransaction, +}; +use unc_primitives::types::{BlockHeightDelta, Gas, TrieNodesCount}; +use unc_primitives::version::{ProtocolFeature, ProtocolVersion}; +use unc_primitives::views::FinalExecutionStatus; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; + +fn process_transaction( + env: &mut TestEnv, + signer: &dyn Signer, + num_blocks: BlockHeightDelta, + protocol_version: ProtocolVersion, +) -> CryptoHash { + let tip = env.clients[0].chain.head().unwrap(); + let epoch_id = + env.clients[0].epoch_manager.get_epoch_id_from_prev_block(&tip.last_block_hash).unwrap(); + let block_producer = + env.clients[0].epoch_manager.get_block_producer(&epoch_id, tip.height).unwrap(); + let last_block_hash = *env.clients[0].chain.get_block_by_height(tip.height).unwrap().hash(); + let next_height = tip.height + 1; + let gas = 20_000_000_000_000; + let tx = SignedTransaction::from_actions( + next_height, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + signer, + vec![ + Action::FunctionCall(Box::new(FunctionCallAction { + args: encode(&[0u64, 10u64]), + method_name: "write_key_value".to_string(), + gas, + deposit: 0, + })), + Action::FunctionCall(Box::new(FunctionCallAction { + args: encode(&[1u64, 20u64]), + method_name: "write_key_value".to_string(), + gas, + deposit: 0, + })), + ], + last_block_hash, + ); + let tx_hash = tx.get_hash(); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + + for i in next_height..next_height + num_blocks { + let mut block = env.clients[0].produce_block(i).unwrap().unwrap(); + set_block_protocol_version(&mut block, block_producer.clone(), protocol_version); + env.process_block(0, block.clone(), Provenance::PRODUCED); + } + tx_hash +} + +/// Compare charged node accesses before and after protocol upgrade to the protocol version of `ChunkNodesCache`. +/// This upgrade during chunk processing saves each node for which we charge touching trie node cost to a special +/// accounting cache (used to be called "chunk cache"), and such cost is charged only once on the first access. +/// This effect doesn't persist across chunks. +/// +/// We run the same transaction 4 times and compare resulting costs. This transaction writes two different key-value +/// pairs to the contract storage. +/// 1st run establishes the trie structure. For our needs, the structure is: +/// +/// --> (Leaf) -> (Value 1) +/// (Extension) -> (Branch) -> (Extension) -> (Branch) | +/// --> (Leaf) -> (Value 2) +/// +/// 2nd run should count 12 regular db reads - for 6 nodes per each value, because protocol is not upgraded yet. +/// 3nd run follows the upgraded protocol and it should count 8 db and 4 memory reads, which comes from 6 db reads +/// for `Value 1` and only 2 db reads for `Value 2`, because first 4 nodes were already put into the accounting +/// cache. 4nd run should give the same results, because caching must not affect different chunks. +#[test] +fn compare_node_counts() { + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let epoch_length = 10; + let num_blocks = 5; + + let old_protocol_version = ProtocolFeature::ChunkNodesCache.protocol_version() - 1; + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = old_protocol_version; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes_with_runtime_config_store( + &genesis, + vec![RuntimeConfigStore::new(None)], + ) + .build(); + + deploy_test_contract( + &mut env, + "test0".parse().unwrap(), + unc_test_contracts::backwards_compatible_rs_contract(), + num_blocks, + 1, + ); + + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let tx_node_counts: Vec = (0..4) + .map(|i| { + let touching_trie_node_cost: Gas = 16_101_955_926; + let read_cached_trie_node_cost: Gas = 2_280_000_000; + + let tx_hash = if i < 1 { + process_transaction(&mut env, &signer, num_blocks, old_protocol_version) + } else { + process_transaction(&mut env, &signer, 2 * epoch_length, old_protocol_version + 1) + }; + + let final_result = env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap(); + assert_matches!(final_result.status, FinalExecutionStatus::SuccessValue(_)); + let transaction_outcome = env.clients[0].chain.get_execution_outcome(&tx_hash).unwrap(); + let receipt_ids = transaction_outcome.outcome_with_id.outcome.receipt_ids; + assert_eq!(receipt_ids.len(), 1); + let receipt_execution_outcome = + env.clients[0].chain.get_execution_outcome(&receipt_ids[0]).unwrap(); + let metadata = receipt_execution_outcome.outcome_with_id.outcome.metadata; + match metadata { + ExecutionMetadata::V1 => panic!("ExecutionMetadata cannot be empty"), + ExecutionMetadata::V2(_profile_data) => panic!("expected newest ExecutionMetadata"), + ExecutionMetadata::V3(profile_data) => TrieNodesCount { + db_reads: { + let cost = profile_data.get_ext_cost(ExtCosts::touching_trie_node); + assert_eq!(cost % touching_trie_node_cost, 0); + cost / touching_trie_node_cost + }, + mem_reads: { + let cost = profile_data.get_ext_cost(ExtCosts::read_cached_trie_node); + assert_eq!(cost % read_cached_trie_node_cost, 0); + cost / read_cached_trie_node_cost + }, + }, + } + }) + .collect(); + + assert_eq!(tx_node_counts[0], TrieNodesCount { db_reads: 4, mem_reads: 0 }); + assert_eq!(tx_node_counts[1], TrieNodesCount { db_reads: 12, mem_reads: 0 }); + assert_eq!(tx_node_counts[2], TrieNodesCount { db_reads: 8, mem_reads: 4 }); + assert_eq!(tx_node_counts[3], TrieNodesCount { db_reads: 8, mem_reads: 4 }); +} diff --git a/integration-tests/src/tests/client/features/chunk_validation.rs b/integration-tests/src/tests/client/features/chunk_validation.rs new file mode 100644 index 000000000..202a666dc --- /dev/null +++ b/integration-tests/src/tests/client/features/chunk_validation.rs @@ -0,0 +1,146 @@ +use unc_chain::{ChainGenesis, Provenance}; +use unc_chain_configs::{Genesis, GenesisConfig, GenesisRecords}; +use unc_client::test_utils::TestEnv; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::block::Tip; +use unc_primitives::shard_layout::ShardLayout; +use unc_primitives::state_record::StateRecord; +use unc_primitives::test_utils::create_test_signer; +use unc_primitives::types::AccountInfo; +use unc_primitives_core::account::Account; +use unc_primitives_core::checked_feature; +use unc_primitives_core::hash::CryptoHash; +use unc_primitives_core::types::AccountId; +use unc_primitives_core::version::PROTOCOL_VERSION; +use framework::test_utils::TestEnvNightshadeSetupExt; +use std::collections::HashSet; + +const ONE_NEAR: u128 = 1_000_000_000_000_000_000_000_000; + +#[test] +// TODO(#9292): This does not pass yet because state witness production +// needs to be implemented. +#[cfg_attr(feature = "nightly", should_panic)] +fn test_chunk_validation_basic() { + init_test_logger(); + + if !checked_feature!("stable", ChunkValidation, PROTOCOL_VERSION) { + println!("Test not applicable without ChunkValidation enabled"); + return; + } + + let validator_stake = 1000000 * ONE_NEAR; + let accounts = + (0..9).map(|i| format!("account{}", i).parse().unwrap()).collect::>(); + let mut genesis_config = GenesisConfig { + // Use the latest protocol version. Otherwise, the version may be too + // old that e.g. blocks don't even store previous heights. + protocol_version: PROTOCOL_VERSION, + // Some arbitrary starting height. Doesn't matter. + genesis_height: 10000, + // We'll use four shards for this test. + shard_layout: ShardLayout::get_simple_nightshade_layout(), + // Make 8 validators, which means 2 will be assigned as chunk validators + // for each chunk. + validators: accounts + .iter() + .take(8) + .map(|account_id| AccountInfo { + account_id: account_id.clone(), + public_key: create_test_signer(account_id.as_str()).public_key(), + amount: validator_stake, + }) + .collect(), + // We don't care about epoch transitions in this test. + epoch_length: 10000, + // The genesis requires this, so set it to something arbitrary. + protocol_treasury_account: accounts[8].clone(), + // Simply make all validators block producers. + num_block_producer_seats: 8, + // Make all validators produce chunks for all shards. + minimum_validators_per_shard: 8, + // Even though not used for the most recent protocol version, + // this must still have the same length as the number of shards, + // or else the genesis fails validation. + num_block_producer_seats_per_shard: vec![8, 8, 8, 8], + ..Default::default() + }; + + // Set up the records corresponding to the validator accounts. + let mut records = Vec::new(); + for (i, account) in accounts.iter().enumerate() { + // The staked amount must be consistent with validators from genesis. + let staked = if i < 8 { validator_stake } else { 0 }; + records.push(StateRecord::Account { + account_id: account.clone(), + account: Account::new(0, staked, CryptoHash::default(), 0), + }); + // The total supply must be correct to pass validation. + genesis_config.total_supply += staked; + } + let genesis = Genesis::new(genesis_config, GenesisRecords(records)).unwrap(); + let chain_genesis = ChainGenesis::new(&genesis); + + let mut env = TestEnv::builder(chain_genesis) + .clients(accounts.iter().take(8).cloned().collect()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + for round in 0..10 { + let heads = env + .clients + .iter() + .map(|client| client.chain.head().unwrap().last_block_hash) + .collect::>(); + assert_eq!(heads.len(), 1, "All clients should have the same head"); + let tip = env.clients[0].chain.head().unwrap(); + + let block_producer = get_block_producer(&env, &tip, 1); + println!("Producing block at height {} by {}", tip.height + 1, block_producer); + let block = env.client(&block_producer).produce_block(tip.height + 1).unwrap().unwrap(); + if round > 1 { + for i in 0..4 { + let chunks = block.chunks(); + let chunk = chunks.get(i).unwrap(); + assert_eq!(chunk.height_created(), chunk.height_included()); + } + } + + // Apply the block. + for i in 0..env.clients.len() { + println!( + " Applying block at height {} at {}", + block.header().height(), + env.get_client_id(i) + ); + let blocks_processed = + env.clients[i].process_block_test(block.clone().into(), Provenance::NONE).unwrap(); + assert_eq!(blocks_processed, vec![*block.hash()]); + } + + env.process_partial_encoded_chunks(); + for j in 0..env.clients.len() { + env.process_shards_manager_responses_and_finish_processing_blocks(j); + } + env.propagate_chunk_state_witnesses(); + } + + // Wait a bit and check that we've received at least some chunk approvals. + // TODO(#10265): We need to make this not time-based, and we need to assert + // exactly how many approvals (or total stake) we have. + std::thread::sleep(std::time::Duration::from_secs(1)); + let approvals = env.get_all_chunk_endorsements(); + assert!(!approvals.is_empty()); +} + +// Returns the block producer for the height of head + height_offset. +fn get_block_producer(env: &TestEnv, head: &Tip, height_offset: u64) -> AccountId { + let client = &env.clients[0]; + let epoch_manager = &client.epoch_manager; + let parent_hash = &head.last_block_hash; + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(parent_hash).unwrap(); + let height = head.height + height_offset; + let block_producer = epoch_manager.get_block_producer(&epoch_id, height).unwrap(); + block_producer +} diff --git a/integration-tests/src/tests/client/features/delegate_action.rs b/integration-tests/src/tests/client/features/delegate_action.rs new file mode 100644 index 000000000..a865956af --- /dev/null +++ b/integration-tests/src/tests/client/features/delegate_action.rs @@ -0,0 +1,952 @@ +//! DelegateAction is a type of action to support meta transactions. +//! +//! NEP: https://github.com/near/NEPs/pull/366 +//! This is the module for its integration tests. + +use crate::node::{Node, RuntimeNode}; +use crate::tests::standard_cases::fee_helper; +use unc_chain::ChainGenesis; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_crypto::{KeyType, PublicKey, Signer}; +use unc_parameters::ActionCosts; +use unc_primitives::account::{ + id::AccountType, AccessKey, AccessKeyPermission, FunctionCallPermission, +}; +use unc_primitives::checked_feature; +use unc_primitives::errors::{ + ActionError, ActionErrorKind, ActionsValidationError, InvalidAccessKeyError, InvalidTxError, + TxExecutionError, +}; +use unc_primitives::test_utils::{ + create_user_test_signer, eth_implicit_test_account, unc_implicit_test_account, +}; +use unc_primitives::transaction::{ + Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, + DeployContractAction, FunctionCallAction, StakeAction, TransferAction, +}; +use unc_primitives::types::{AccountId, Balance}; +use unc_primitives::version::{ProtocolFeature, ProtocolVersion, PROTOCOL_VERSION}; +use unc_primitives::views::{ + AccessKeyPermissionView, ExecutionStatusView, FinalExecutionOutcomeView, FinalExecutionStatus, +}; +use unc_test_contracts::{ft_contract, smallest_rs_contract}; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; +use framework::UNC_BASE; +use testlib::runtime_utils::{ + add_account_with_access_key, add_contract, add_test_contract, alice_account, bob_account, + carol_account, eve_dot_alice_account, +}; + +/// For test adding a function access key with allowance. +const INITIAL_ALLOWANCE: Balance = UNC_BASE; +/// Commonly used method in the test contract. +const TEST_METHOD: &str = "log_something"; +const TEST_METHOD_LEN: u64 = TEST_METHOD.len() as u64; + +fn exec_meta_transaction( + actions: Vec, + protocol_version: ProtocolVersion, +) -> FinalExecutionStatus { + unc_o11y::testonly::init_test_logger(); + let validator: AccountId = "test0".parse().unwrap(); + let user: AccountId = "alice.near".parse().unwrap(); + let receiver: AccountId = "bob.near".parse().unwrap(); + let relayer: AccountId = "relayer.near".parse().unwrap(); + let mut genesis = + Genesis::test(vec![validator, user.clone(), receiver.clone(), relayer.clone()], 1); + genesis.config.epoch_length = 1000; + genesis.config.protocol_version = protocol_version; + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let tx = env.meta_tx_from_actions(actions, user, relayer, receiver); + + match env.execute_tx(tx) { + Ok(outcome) => outcome.status, + Err(status) => FinalExecutionStatus::Failure(TxExecutionError::InvalidTxError(status)), + } +} + +/// Basic test to ensure the happy path works. +#[test] +fn accept_valid_meta_tx() { + let protocol_version = ProtocolFeature::DelegateAction.protocol_version(); + let status = exec_meta_transaction(vec![], protocol_version); + assert!(matches!(status, FinalExecutionStatus::SuccessValue(_)), "{status:?}",); +} + +/// During the protocol upgrade phase, before the voting completes, we must not +/// include meta transaction on the chain. +/// +/// Imagine a validator with an updated binary. A malicious node sends it a meta +/// transaction to execute before the upgrade has finished. We must ensure the +/// validator will not attempt adding it to the change unless the protocol +/// upgrade has completed. +/// +/// Note: This does not prevent problems on the network layer that might arise +/// by having different interpretation of what a valid `SignedTransaction` might +/// be. We must catch that earlier. +#[test] +fn reject_valid_meta_tx_in_older_versions() { + let protocol_version = ProtocolFeature::DelegateAction.protocol_version() - 1; + + let status = exec_meta_transaction(vec![], protocol_version); + assert!( + matches!( + &status, + FinalExecutionStatus::Failure( + TxExecutionError::InvalidTxError( + InvalidTxError::ActionsValidation( + ActionsValidationError::UnsupportedProtocolFeature{ protocol_feature, version } + ) + ) + ) + if protocol_feature == "DelegateAction" && *version == ProtocolFeature::DelegateAction.protocol_version() + ), + "{status:?}", + ); +} + +/// Take a list of actions and execute them as a meta transaction, check +/// everything executes successfully, return balance differences for the sender, +/// relayer, and receiver. +/// +/// This is a common checker function used by the tests below. +fn check_meta_tx_execution( + node: &impl Node, + actions: Vec, + sender: AccountId, + relayer: AccountId, + receiver: AccountId, +) -> (FinalExecutionOutcomeView, i128, i128, i128) { + let node_user = node.user(); + let protocol_version = node.genesis().config.protocol_version; + + assert_eq!( + relayer, + node.account_id().unwrap(), + "the relayer must be the signer in meta transactions" + ); + + let sender_before = node_user.view_balance(&sender).unwrap(); + let relayer_before = node_user.view_balance(&relayer).unwrap(); + let receiver_before = node_user.view_balance(&receiver).unwrap_or(0); + let relayer_nonce_before = node_user + .get_access_key(&relayer, &PublicKey::from_seed(KeyType::ED25519, relayer.as_ref())) + .unwrap() + .nonce; + let user_pubk = match sender.get_account_type() { + AccountType::NearImplicitAccount => PublicKey::from_unc_implicit_account(&sender).unwrap(), + AccountType::EthImplicitAccount => { + if checked_feature!("stable", EthImplicitAccounts, protocol_version) { + panic!("ETH-implicit accounts must not have access key"); + } else { + PublicKey::from_seed(KeyType::ED25519, sender.as_ref()) + } + } + AccountType::NamedAccount => PublicKey::from_seed(KeyType::ED25519, sender.as_ref()), + }; + let user_nonce_before = node_user.get_access_key(&sender, &user_pubk).unwrap().nonce; + + let tx_result = + node_user.meta_tx(sender.clone(), receiver.clone(), relayer.clone(), actions).unwrap(); + + // Execution of the transaction and all receipts should succeed + tx_result.assert_success(); + + // both nonces should be increased by 1 + let relayer_nonce = node_user + .get_access_key(&relayer, &PublicKey::from_seed(KeyType::ED25519, relayer.as_ref())) + .unwrap() + .nonce; + assert_eq!(relayer_nonce, relayer_nonce_before + 1); + // user key must be checked for existence (to test DeleteKey action) + if let Ok(user_nonce) = node_user + .get_access_key(&sender, &PublicKey::from_seed(KeyType::ED25519, sender.as_ref())) + .map(|key| key.nonce) + { + assert_eq!(user_nonce, user_nonce_before + 1); + } + + let sender_after = node_user.view_balance(&sender).unwrap_or(0); + let relayer_after = node_user.view_balance(&relayer).unwrap_or(0); + let receiver_after = node_user.view_balance(&receiver).unwrap_or(0); + + let sender_diff = sender_after as i128 - sender_before as i128; + let relayer_diff = relayer_after as i128 - relayer_before as i128; + let receiver_diff = receiver_after as i128 - receiver_before as i128; + (tx_result, sender_diff, relayer_diff, receiver_diff) +} + +/// Call `check_meta_tx_execution` and perform gas checks for non function call actions. +/// +/// This is a common checker function used by the tests below. +fn check_meta_tx_no_fn_call( + node: &impl Node, + actions: Vec, + normal_tx_cost: Balance, + tokens_transferred: Balance, + sender: AccountId, + relayer: AccountId, + receiver: AccountId, +) -> FinalExecutionOutcomeView { + let fee_helper = fee_helper(node); + let gas_cost = normal_tx_cost + fee_helper.meta_tx_overhead_cost(&actions, &receiver); + + let (tx_result, sender_diff, relayer_diff, receiver_diff) = + check_meta_tx_execution(node, actions, sender, relayer, receiver); + + assert_eq!(sender_diff, 0, "sender should not pay for anything"); + assert_eq!(receiver_diff, tokens_transferred as i128, "unexpected receiver balance"); + assert_eq!( + relayer_diff, + -((gas_cost + tokens_transferred) as i128), + "unexpected relayer balance" + ); + + tx_result +} + +/// Call `check_meta_tx_execution` and perform gas checks specific to function calls. +/// +/// This is a common checker function used by the tests below. +/// It works for action lists that consists multiple function calls but adding +/// other action will mess up the gas checks. +fn check_meta_tx_fn_call( + node: &impl Node, + actions: Vec, + msg_len: u64, + tokens_transferred: Balance, + sender: AccountId, + relayer: AccountId, + receiver: AccountId, +) -> FinalExecutionOutcomeView { + let fee_helper = fee_helper(node); + let num_fn_calls = actions.len(); + let meta_tx_overhead_cost = fee_helper.meta_tx_overhead_cost(&actions, &receiver); + + let (tx_result, sender_diff, relayer_diff, receiver_diff) = + check_meta_tx_execution(node, actions, sender, relayer, receiver); + + assert_eq!(sender_diff, 0, "sender should not pay for anything"); + + // Assertions on receiver and relayer are tricky because of dynamic gas + // costs and contract reward. We need to check in the function call receipt + // how much gas was spent and subtract the base cost that is not part of the + // dynamic cost. The contract reward can be inferred from that. + + // static send gas is paid and burnt upfront + let static_send_gas = fee_helper.cfg().fee(ActionCosts::new_action_receipt).send_fee(false) + + num_fn_calls as u64 + * fee_helper.cfg().fee(ActionCosts::function_call_base).send_fee(false) + + msg_len * fee_helper.cfg().fee(ActionCosts::function_call_byte).send_fee(false); + // static execution gas burnt in the same receipt as the function calls but + // it doesn't contribute to the contract reward + let static_exec_gas = fee_helper.cfg().fee(ActionCosts::new_action_receipt).exec_fee() + + num_fn_calls as u64 * fee_helper.cfg().fee(ActionCosts::function_call_base).exec_fee() + + msg_len * fee_helper.cfg().fee(ActionCosts::function_call_byte).exec_fee(); + + // calculate contract rewards as reward("gas burnt in fn call receipt" - "static exec costs") + let gas_burnt_for_function_call = + tx_result.receipts_outcome[1].outcome.gas_burnt - static_send_gas; + let dyn_cost = fee_helper.gas_to_balance(gas_burnt_for_function_call); + let contract_reward = fee_helper.gas_burnt_to_reward(gas_burnt_for_function_call); + + // the relayer pays all gas and tokens + let gas_cost = + meta_tx_overhead_cost + fee_helper.gas_to_balance(static_exec_gas + static_send_gas); + let expected_relayer_cost = (gas_cost + tokens_transferred + dyn_cost) as i128; + assert_eq!(relayer_diff, -expected_relayer_cost, "unexpected relayer balance"); + + // the receiver gains transferred tokens and the contract reward + let expected_receiver_gain = (tokens_transferred + contract_reward) as i128; + assert_eq!(receiver_diff, expected_receiver_gain, "unexpected receiver balance"); + + tx_result +} + +/// The simplest non-empty meta transaction: Transferring some NEAR tokens. +/// +/// Note: The expectation is that the relayer pays for the tokens sent, as +/// specified in NEP-366. +#[test] +fn meta_tx_unc_transfer() { + let sender = bob_account(); + let relayer = alice_account(); + let receiver = carol_account(); + let node = RuntimeNode::new(&relayer); + let fee_helper = fee_helper(&node); + + let amount = UNC_BASE; + let actions = vec![Action::Transfer(TransferAction { deposit: amount })]; + let tx_cost = fee_helper.transfer_cost(); + check_meta_tx_no_fn_call(&node, actions, tx_cost, amount, sender, relayer, receiver); +} + +/// Call a function on the test contract provided by default in the test environment. +#[test] +fn meta_tx_fn_call() { + let sender = bob_account(); + let relayer = alice_account(); + let receiver = carol_account(); + let node = RuntimeNode::new(&relayer); + + let actions = vec![log_something_fn_call()]; + let outcome = + check_meta_tx_fn_call(&node, actions, TEST_METHOD_LEN, 0, sender, relayer, receiver); + + // Check that the function call was executed as expected + let fn_call_logs = &outcome.receipts_outcome[1].outcome.logs; + assert_eq!(fn_call_logs, &vec!["hello".to_owned()]); +} + +/// Call a function in a meta tx where the user only has access through a +/// function call access key. +#[test] +fn meta_tx_fn_call_access_key() { + let sender = bob_account(); + let relayer = alice_account(); + let receiver = carol_account(); + let signer = create_user_test_signer(&sender); + let public_key = signer.public_key(); + + let node = setup_with_access_key( + &relayer, + &receiver, + &sender, + public_key.clone(), + INITIAL_ALLOWANCE, + TEST_METHOD, + ); + + // Check previous allowance is set as expected + let key = + node.user().get_access_key(&sender, &public_key).expect("failed looking up fn access key"); + let AccessKeyPermissionView::FunctionCall { allowance, .. } = key.permission else { + panic!("should be function access key") + }; + assert_eq!(allowance.unwrap(), INITIAL_ALLOWANCE); + + let actions = vec![log_something_fn_call()]; + let outcome = check_meta_tx_fn_call( + &node, + actions, + TEST_METHOD_LEN, + 0, + sender.clone(), + relayer, + receiver, + ); + + // Check that the function call was executed as expected + let fn_call_logs = &outcome.receipts_outcome[1].outcome.logs; + assert_eq!(fn_call_logs, &vec!["hello".to_owned()]); + + // Check allowance was not updated + let key = node + .user() + .get_access_key(&sender, &signer.public_key()) + .expect("failed looking up fn access key"); + let AccessKeyPermissionView::FunctionCall { allowance, .. } = key.permission else { + panic!("should be function access key") + }; + assert_eq!( + allowance.unwrap(), + INITIAL_ALLOWANCE, + "allowance should not change, we used the relayer's fund not the sender's" + ); +} + +/// Call a function in a meta tx where the user only has access through a +/// function call access that has too little allowance left. +#[test] +fn meta_tx_fn_call_access_key_insufficient_allowance() { + let sender = bob_account(); + let relayer = alice_account(); + let receiver = carol_account(); + + // 1 yocto near, that's less than 1 gas unit + let initial_allowance = 1; + let signer = create_user_test_signer(&sender); + + let node = setup_with_access_key( + &relayer, + &receiver, + &sender, + signer.public_key(), + initial_allowance, + TEST_METHOD, + ); + + let actions = vec![log_something_fn_call()]; + // this should still succeed because we use the gas of the relayer, not of the access key + let outcome = + check_meta_tx_fn_call(&node, actions, TEST_METHOD_LEN, 0, sender, relayer, receiver); + + // Check that the function call was executed as expected + let fn_call_logs = &outcome.receipts_outcome[1].outcome.logs; + assert_eq!(fn_call_logs, &vec!["hello".to_owned()]); +} + +/// Call a function in a meta tx where the user doesn't have the appropriate +/// access key, which must fail. +/// +/// This is quite to fail, method restricted access keys can give restricted +/// access to a contract. If meta transactions can be used to circumvent this +/// check, then someone with an access key could impersonate the account in +/// unintended ways. +#[test] +fn meta_tx_fn_call_access_wrong_method() { + let sender = bob_account(); + let relayer = alice_account(); + let receiver = carol_account(); + let signer = create_user_test_signer(&sender); + + let access_key_method_name = "log_something_else"; + let node = setup_with_access_key( + &relayer, + &receiver, + &sender, + signer.public_key(), + INITIAL_ALLOWANCE, + access_key_method_name, + ); + + let actions = vec![log_something_fn_call()]; + let tx_result = node.user().meta_tx(sender, receiver, relayer, actions).unwrap(); + // actual check has to be done in the receipt on the sender shard, not the + // relayer, so let's check the receipt is present with the appropriate error + let inner_status = &tx_result.receipts_outcome[0].outcome.status; + assert!( + matches!( + inner_status, + ExecutionStatusView::Failure(TxExecutionError::ActionError(ActionError { + kind: ActionErrorKind::DelegateActionAccessKeyError( + InvalidAccessKeyError::MethodNameMismatch { .. } + ), + .. + })), + ), + "expected MethodNameMismatch but found {inner_status:?}" + ); +} + +#[test] +fn meta_tx_deploy() { + let sender = bob_account(); + let relayer = alice_account(); + // Can only deploy on own account + let receiver = sender.clone(); + let node = RuntimeNode::new(&relayer); + let fee_helper = fee_helper(&node); + + let code = smallest_rs_contract().to_vec(); + let tx_cost = fee_helper.deploy_contract_cost(code.len() as u64); + let actions = vec![Action::DeployContract(DeployContractAction { code })]; + check_meta_tx_no_fn_call(&node, actions, tx_cost, 0, sender, relayer, receiver); +} + +#[test] +fn meta_tx_stake() { + let sender = bob_account(); + let relayer = alice_account(); + // Can only stake on own account + let receiver = sender.clone(); + let node = RuntimeNode::new(&relayer); + let fee_helper = fee_helper(&node); + + let tx_cost = fee_helper.stake_cost(); + let public_key = create_user_test_signer(&sender).public_key; + let actions = vec![Action::Stake(Box::new(StakeAction { public_key, stake: 0 }))]; + check_meta_tx_no_fn_call(&node, actions, tx_cost, 0, sender, relayer, receiver); +} + +#[test] +fn meta_tx_add_key() { + let sender = bob_account(); + let relayer = alice_account(); + // Can only add key on own account + let receiver = sender.clone(); + let node = RuntimeNode::new(&relayer); + let fee_helper = fee_helper(&node); + + let tx_cost = fee_helper.add_key_full_cost(); + // any public key works as long as it doesn't exists on the receiver, the + // relayer public key is just handy + let public_key = node.signer().public_key(); + let actions = vec![Action::AddKey(Box::new(AddKeyAction { + public_key: public_key.clone(), + access_key: AccessKey::full_access(), + }))]; + check_meta_tx_no_fn_call(&node, actions, tx_cost, 0, sender, relayer, receiver.clone()); + + let key_view = node + .user() + .get_access_key(&receiver, &public_key) + .expect("looking up key that was just added failed"); + assert_eq!( + key_view.permission, + AccessKeyPermissionView::FullAccess, + "wrong permissions for new key" + ); +} + +#[test] +fn meta_tx_delete_key() { + let sender = bob_account(); + let relayer = alice_account(); + // Can only delete keys on own account + let receiver = sender.clone(); + let node = RuntimeNode::new(&relayer); + let fee_helper = fee_helper(&node); + + let tx_cost = fee_helper.delete_key_cost(); + let public_key = PublicKey::from_seed(KeyType::ED25519, receiver.as_ref()); + let actions = + vec![Action::DeleteKey(Box::new(DeleteKeyAction { public_key: public_key.clone() }))]; + check_meta_tx_no_fn_call(&node, actions, tx_cost, 0, sender, relayer, receiver.clone()); + + let err = node + .user() + .get_access_key(&receiver, &public_key) + .expect_err("key should have been deleted"); + assert_eq!(err, "Access key for public key #ed25519:4mhK4txd8Z5r71iCZ41UguSHuHFKUeCXPHv646DbQPYi does not exist"); +} + +#[test] +fn meta_tx_delete_account() { + let relayer = alice_account(); + let sender = eve_dot_alice_account(); + let receiver = sender.clone(); + let node = RuntimeNode::new(&relayer); + + // setup: create new account because the standard accounts are validators (can't be deleted) + let balance = UNC_BASE; + node.user() + .create_account( + relayer.clone(), + sender.clone(), + PublicKey::from_seed(KeyType::ED25519, sender.as_ref()), + balance, + ) + .expect("account setup failed") + .assert_success(); + + let fee_helper = fee_helper(&node); + + let actions = + vec![Action::DeleteAccount(DeleteAccountAction { beneficiary_id: relayer.clone() })]; + + // special case balance check for deleting account + let gas_cost = fee_helper.prepaid_delete_account_cost() + + fee_helper.meta_tx_overhead_cost(&actions, &receiver); + let (_tx_result, sender_diff, relayer_diff, receiver_diff) = + check_meta_tx_execution(&node, actions, sender, relayer, receiver.clone()); + + assert_eq!( + sender_diff, + -(balance as i128), + "sender should be deleted and thus have zero balance" + ); + assert_eq!(sender_diff, receiver_diff); + assert_eq!(relayer_diff, balance as i128 - (gas_cost as i128), "unexpected relayer balance"); + let err = node.view_account(&receiver).expect_err("account should have been deleted"); + assert_eq!(err, "Account ID #eve.alice.near does not exist"); +} + +/// Test the canonical example for meta transactions: A fungible token transfer. +/// +/// Scenario: Bob sends some Carol-FT to David without requiring any NEAR tokens +/// to purchase gas. Alice acts as a relayer. +#[test] +fn meta_tx_ft_transfer() { + let relayer = alice_account(); + let sender = bob_account(); + let ft_contract = carol_account(); + let receiver = "david.near"; + + let mut genesis = Genesis::test(vec![alice_account(), bob_account(), carol_account()], 3); + add_contract(&mut genesis, &ft_contract, unc_test_contracts::ft_contract().to_vec()); + let node = RuntimeNode::new_from_genesis(&relayer, genesis); + + // A BUNCH OF TEST SETUP + // initialize the contract + node.user() + .function_call( + relayer.clone(), + ft_contract.clone(), + "new_default_meta", + // make the relayer (alice) owner, makes initialization easier + br#"{"owner_id": "alice.near", "total_supply": "1000000"}"#.to_vec(), + 30_000_000_000_000, + 0, + ) + .expect("FT contract initialization failed") + .assert_success(); + + // register sender & receiver FT accounts + let actions = vec![ft_register_action(sender.as_ref()), ft_register_action(&receiver)]; + node.user() + .sign_and_commit_actions(relayer.clone(), ft_contract.clone(), actions) + .expect("registering FT accounts") + .assert_success(); + // initialize sender balance + let actions = vec![ft_transfer_action(sender.as_ref(), 10_000).0]; + node.user() + .sign_and_commit_actions(relayer.clone(), ft_contract.clone(), actions) + .expect("initializing sender balance failed") + .assert_success(); + + // START OF META TRANSACTION + // 1% fee to the relayer + let (action0, bytes0) = ft_transfer_action(relayer.as_ref(), 10); + // the actual transfer + let (action1, bytes1) = ft_transfer_action(receiver, 1000); + let actions = vec![action0, action1]; + + let outcome = check_meta_tx_fn_call( + &node, + actions, + bytes0 + bytes1, + 2, + sender.clone(), + relayer.clone(), + ft_contract.clone(), + ); + + // Check that the function call was executed as expected, according to NEP-141 events. + let fn_call_logs = &outcome.receipts_outcome[1].outcome.logs; + + assert_eq!(2, fn_call_logs.len(), "expected 2 JSON events but found {fn_call_logs:?}"); + assert_eq!( + fn_call_logs[0], + ft_transfer_event(sender.as_ref(), relayer.as_ref(), 10), + "relayer event looks wrong" + ); + assert_eq!( + fn_call_logs[1], + ft_transfer_event(sender.as_str(), &receiver, 1000), + "receiver event looks wrong" + ); + + // Also check FT balances + assert_ft_balance(&node, &ft_contract, &receiver, 1000); + assert_ft_balance(&node, &ft_contract, sender.as_ref(), 10_000 - 1000 - 10); + assert_ft_balance(&node, &ft_contract, relayer.as_ref(), 1_000_000 - 10_000 + 10); +} + +/// Call the function "log_something" in the test contract. +fn log_something_fn_call() -> Action { + Action::FunctionCall(Box::new(FunctionCallAction { + method_name: TEST_METHOD.to_owned(), + args: vec![], + gas: 30_000_000_000_000, + deposit: 0, + })) +} + +/// Construct an function call action with a FT transfer. +/// +/// Returns the action and the number of bytes for gas charges. +fn ft_transfer_action(receiver: &str, amount: u128) -> (Action, u64) { + let args: Vec = format!( + r#"{{ + "receiver_id": "{receiver}", + "amount": "{amount}" + }}"# + ) + .bytes() + .collect(); + let method_name = "ft_transfer".to_owned(); + let num_bytes = method_name.len() + args.len(); + let action = Action::FunctionCall(Box::new(FunctionCallAction { + method_name, + args, + gas: 20_000_000_000_000, + deposit: 1, + })); + + (action, num_bytes as u64) +} + +/// Add NEAR token balance to maintain the storage of an account, which +/// registers the user in the fungible contract account. +fn ft_register_action(receiver: &str) -> Action { + let args: Vec = format!( + r#"{{ + "account_id": "{receiver}" + }}"# + ) + .bytes() + .collect(); + Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "storage_deposit".to_owned(), + args, + gas: 20_000_000_000_000, + deposit: UNC_BASE, + })) +} + +/// Format a NEP-141 event for an ft transfer +fn ft_transfer_event(sender: &str, receiver: &str, amount: u128) -> String { + // This part is valid JSON, I would like to use the json!() macro but it + // produces the fields out of order. This is valid for JSON but it will fail + // the string comparison. + // (Note: parsing the logs as JSON and comparing serde_json::Value instead + // of string is not possible because the logs are only partially valid + // JOSN...) + let data_json = format!( + r#"[{{"old_owner_id":"{sender}","new_owner_id":"{receiver}","amount":"{amount}"}}]"# + ); + let json = format!( + r#"{{"standard":"nep141","version":"1.0.0","event":"ft_transfer","data":{data_json}}}"# + ); + // this part isn't even valid JSON + format!("EVENT_JSON:{json}") +} + +/// Asserts an FT balance for an account. +fn assert_ft_balance( + node: &RuntimeNode, + ft_contract: &AccountId, + user: &str, + expected_balance: Balance, +) { + let response = node + .user() + .view_call(ft_contract, "ft_balance_of", format!(r#"{{"account_id":"{user}"}}"#).as_bytes()) + .expect("view call failed"); + let balance = std::str::from_utf8(&response.result).expect("invalid UTF8"); + assert_eq!(format!("\"{expected_balance}\""), balance); +} + +/// Create a test setup where a receiver has the general test contract +/// deployed and the sender has an access key for it's test method. +fn setup_with_access_key( + user: &AccountId, + receiver: &AccountId, + sender: &AccountId, + public_key: PublicKey, + allowance: Balance, + method: &str, +) -> RuntimeNode { + let access_key = fn_access_key(allowance, receiver.to_string(), vec![method.to_owned()]); + let mut genesis = Genesis::test(vec![user.clone(), receiver.clone()], 3); + add_test_contract(&mut genesis, &receiver); + add_account_with_access_key(&mut genesis, sender.clone(), UNC_BASE, public_key, access_key); + RuntimeNode::new_from_genesis(user, genesis) +} + +fn fn_access_key( + initial_allowance: u128, + receiver_id: String, + method_names: Vec, +) -> AccessKey { + AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(initial_allowance), + receiver_id, + method_names, + }), + } +} + +/// Test account creation scenarios with meta transactions. +/// +/// Named accounts aren't the primary use case for meta transactions but still +/// worth a test case. +#[test] +fn meta_tx_create_named_account() { + let relayer = bob_account(); + let sender = alice_account(); + let new_account = eve_dot_alice_account(); + let node = RuntimeNode::new(&relayer); + + let fee_helper = fee_helper(&node); + let amount = UNC_BASE; + + let public_key = PublicKey::from_seed(KeyType::ED25519, new_account.as_ref()); + + // That's the minimum to create a (useful) account. + let actions = vec![ + Action::CreateAccount(CreateAccountAction {}), + Action::Transfer(TransferAction { deposit: amount }), + Action::AddKey(Box::new(AddKeyAction { public_key, access_key: AccessKey::full_access() })), + ]; + + // Check the account doesn't exist, yet. We want to create it. + node.view_account(&new_account).expect_err("account already exists"); + + let tx_cost = fee_helper.create_account_transfer_full_key_cost(); + check_meta_tx_no_fn_call(&node, actions, tx_cost, amount, sender, relayer, new_account.clone()); + + // Check the account exists after we created it. + node.view_account(&new_account).expect("failed looking up account"); +} + +/// Try creating an implicit account with `CreateAction` which is not allowed in +/// or outside meta transactions and must fail with `OnlyImplicitAccountCreationAllowed`. +fn meta_tx_create_implicit_account_fails(new_account: AccountId) { + let relayer = bob_account(); + let sender = alice_account(); + let node = RuntimeNode::new(&relayer); + + let actions = vec![Action::CreateAccount(CreateAccountAction {})]; + let tx_result = node.user().meta_tx(sender, new_account, relayer, actions).unwrap(); + + let account_creation_result = &tx_result.receipts_outcome[1].outcome.status; + assert!(matches!( + account_creation_result, + unc_primitives::views::ExecutionStatusView::Failure(TxExecutionError::ActionError( + ActionError { kind: ActionErrorKind::OnlyImplicitAccountCreationAllowed { .. }, .. } + )), + )); +} + +#[test] +fn meta_tx_create_unc_implicit_account_fails() { + meta_tx_create_implicit_account_fails(unc_implicit_test_account()); +} + +#[test] +fn meta_tx_create_eth_implicit_account_fails() { + if !checked_feature!("stable", EthImplicitAccounts, PROTOCOL_VERSION) { + return; + } + meta_tx_create_implicit_account_fails(eth_implicit_test_account()); +} + +/// Try creating an implicit account with a meta tx transfer and use the account +/// in the same meta transaction. +/// +/// This is expected to fail with `AccountDoesNotExist`, known limitation of NEP-366. +/// In case of NEAR-implicit accounts it only works with accounts that already exist +/// because it needs to do a nonce check against the access key, +/// which can only exist if the account exists. +/// In case of ETH-implicit accounts the access key does not exist anyway. +fn meta_tx_create_and_use_implicit_account(new_account: AccountId) { + let relayer = bob_account(); + let sender = alice_account(); + let node = RuntimeNode::new(&relayer); + + // Check the account doesn't exist, yet. We will attempt creating it. + node.view_account(&new_account).expect_err("account already exists"); + + let initial_amount = UNC_BASE; + let actions = vec![ + Action::Transfer(TransferAction { deposit: initial_amount }), + Action::DeployContract(DeployContractAction { code: ft_contract().to_vec() }), + ]; + + // Execute and expect `AccountDoesNotExist`, as we try to call a meta + // transaction on a user that doesn't exist yet. + let tx_result = node.user().meta_tx(sender, new_account.clone(), relayer, actions).unwrap(); + let status = &tx_result.receipts_outcome[1].outcome.status; + assert!(matches!( + status, + unc_primitives::views::ExecutionStatusView::Failure(TxExecutionError::ActionError( + ActionError { kind: ActionErrorKind::AccountDoesNotExist { account_id }, .. } + )) if *account_id == new_account, + )); +} + +#[test] +fn meta_tx_create_and_use_unc_implicit_account() { + meta_tx_create_and_use_implicit_account(unc_implicit_test_account()); +} + +#[test] +fn meta_tx_create_and_use_eth_implicit_account() { + if !checked_feature!("stable", EthImplicitAccounts, PROTOCOL_VERSION) { + return; + } + meta_tx_create_and_use_implicit_account(eth_implicit_test_account()); +} + +/// Creating an implicit account with a meta tx transfer and try using the account in +/// a second meta transaction. +/// +/// Creation through a meta tx should work as normal, it's just that the relayer +/// pays for the storage and the user could delete the account and cash in, +/// hence this workflow is not ideal from all circumstances. +/// +/// Using the account should only work for NEAR-implicit accounts, +/// as ETH-implicit accounts do not have access keys +/// and they can only be used by calling associated smart contract. +fn meta_tx_create_implicit_account(new_account: AccountId) { + let relayer = bob_account(); + let sender = alice_account(); + let node = RuntimeNode::new(&relayer); + + // Check account doesn't exist, yet + node.view_account(&new_account).expect_err("account already exists"); + + let fee_helper = fee_helper(&node); + let initial_amount = match new_account.get_account_type() { + AccountType::NearImplicitAccount => UNC_BASE, + // ETH-implicit accounts fit within zero-balance account limit. + AccountType::EthImplicitAccount => 0u128, + AccountType::NamedAccount => panic!("must be implicit"), + }; + let actions = vec![Action::Transfer(TransferAction { deposit: initial_amount })]; + + let tx_cost = match new_account.get_account_type() { + AccountType::NearImplicitAccount => fee_helper.create_account_transfer_full_key_cost(), + AccountType::EthImplicitAccount => fee_helper.create_account_transfer_cost(), + AccountType::NamedAccount => panic!("must be implicit"), + }; + check_meta_tx_no_fn_call( + &node, + actions, + tx_cost, + initial_amount, + sender.clone(), + relayer.clone(), + new_account.clone(), + ); + + // Check account exists with expected balance + node.view_account(&new_account).expect("failed looking up account"); + let balance = node.view_balance(&new_account).expect("failed looking up balance"); + assert_eq!(balance, initial_amount); + + if new_account.get_account_type() == AccountType::EthImplicitAccount { + // ETH-implicit account must not have access key added. + assert!(node.user().is_locked(&new_account).unwrap()); + // We will not attempt to make a transfer from this account. + return; + } + + // Now test we can use this account in a meta transaction that sends back half the tokens to alice. + let transfer_amount = initial_amount / 2; + let actions = vec![Action::Transfer(TransferAction { deposit: transfer_amount })]; + let tx_cost = fee_helper.transfer_cost(); + check_meta_tx_no_fn_call( + &node, + actions, + tx_cost, + transfer_amount, + new_account, + relayer, + sender, + ) + .assert_success(); +} + +#[test] +fn meta_tx_create_unc_implicit_account() { + meta_tx_create_implicit_account(unc_implicit_test_account()); +} + +#[test] +fn meta_tx_create_eth_implicit_account() { + if !checked_feature!("stable", EthImplicitAccounts, PROTOCOL_VERSION) { + return; + } + meta_tx_create_implicit_account(eth_implicit_test_account()); +} diff --git a/integration-tests/src/tests/client/features/fix_contract_loading_cost.rs b/integration-tests/src/tests/client/features/fix_contract_loading_cost.rs new file mode 100644 index 000000000..1c8e0328e --- /dev/null +++ b/integration-tests/src/tests/client/features/fix_contract_loading_cost.rs @@ -0,0 +1,96 @@ +use super::super::process_blocks::deploy_test_contract; +use assert_matches::assert_matches; +use unc_chain::ChainGenesis; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_primitives::types::{AccountId, BlockHeight}; +use unc_primitives::views::FinalExecutionStatus; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; + +/// Create a `TestEnv` with an account and a contract deployed to that account. +fn prepare_env_with_contract( + epoch_length: u64, + protocol_version: u32, + account: AccountId, + contract: Vec, +) -> TestEnv { + let mut genesis = Genesis::test(vec![account.clone()], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = protocol_version; + let runtime_config = unc_parameters::RuntimeConfigStore::new(None); + let mut env = TestEnv::builder(ChainGenesis::new(&genesis)) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes_with_runtime_config_store(&genesis, vec![runtime_config]) + .build(); + deploy_test_contract(&mut env, account, &contract, epoch_length, 1); + env +} + +/// Check that normal execution has the same gas cost after FixContractLoadingCost. +#[test] +fn unchanged_gas_cost() { + let new_protocol_version = + unc_primitives::version::ProtocolFeature::FixContractLoadingCost.protocol_version(); + let old_protocol_version = new_protocol_version - 1; + + let contract_size = 4096; + let contract = unc_test_contracts::sized_contract(contract_size); + + let epoch_length: BlockHeight = 5; + + let account: AccountId = "test0".parse().unwrap(); + let mut env = + prepare_env_with_contract(epoch_length, old_protocol_version, account.clone(), contract); + + let old_result = env.call_main(&account); + let old_gas = old_result.receipts_outcome[0].outcome.gas_burnt; + assert_matches!(old_result.status, FinalExecutionStatus::SuccessValue(_)); + + env.upgrade_protocol(new_protocol_version); + + let new_result = env.call_main(&account); + let new_gas = new_result.receipts_outcome[0].outcome.gas_burnt; + assert_matches!(new_result.status, FinalExecutionStatus::SuccessValue(_)); + + assert_eq!(old_gas, new_gas); +} + +/// Check that execution that fails during contract preparation has the updated gas cost after the update. +#[test] +fn preparation_error_gas_cost() { + let new_protocol_version = + unc_primitives::version::ProtocolFeature::FixContractLoadingCost.protocol_version(); + let old_protocol_version = new_protocol_version - 1; + + let bad_contract = b"not-a-contract".to_vec(); + let contract_size = bad_contract.len(); + + let epoch_length: BlockHeight = 5; + + let account: AccountId = "test0".parse().unwrap(); + let mut env = prepare_env_with_contract( + epoch_length, + old_protocol_version, + account.clone(), + bad_contract, + ); + + let old_result = env.call_main(&account); + let old_gas = old_result.receipts_outcome[0].outcome.gas_burnt; + assert_matches!(old_result.status, FinalExecutionStatus::Failure(_)); + + env.upgrade_protocol(new_protocol_version); + + let new_result = env.call_main(&account); + let new_gas = new_result.receipts_outcome[0].outcome.gas_burnt; + assert_matches!(new_result.status, FinalExecutionStatus::Failure(_)); + + // Gas cost should be different because the upgrade pre-charges loading costs. + assert_ne!(old_gas, new_gas); + // Runtime parameter values for version of the protocol upgrade + let loading_base = 35_445_963; + let loading_byte = 216_750; + let loading_cost = loading_base + contract_size as u64 * loading_byte; + assert_eq!(old_gas + loading_cost, new_gas); +} diff --git a/integration-tests/src/tests/client/features/fix_storage_usage.rs b/integration-tests/src/tests/client/features/fix_storage_usage.rs new file mode 100644 index 000000000..9ba5b6f24 --- /dev/null +++ b/integration-tests/src/tests/client/features/fix_storage_usage.rs @@ -0,0 +1,85 @@ +use borsh::BorshDeserialize; +use unc_chain::{ChainGenesis, Provenance}; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::version::ProtocolFeature; +use unc_primitives::{trie_key::TrieKey, types::AccountId}; +use unc_store::{ShardUId, TrieUpdate}; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; + +use crate::tests::client::process_blocks::set_block_protocol_version; + +fn process_blocks_with_storage_usage_fix( + chain_id: String, + check_storage_usage: fn(AccountId, u64, u64), +) { + let epoch_length = 5; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap()], 1); + genesis.config.chain_id = chain_id; + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = ProtocolFeature::FixStorageUsage.protocol_version() - 1; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + for i in 1..=16 { + // We cannot just use TestEnv::produce_block as we are updating protocol version + let mut block = env.clients[0].produce_block(i).unwrap().unwrap(); + set_block_protocol_version( + &mut block, + "test0".parse().unwrap(), + ProtocolFeature::FixStorageUsage.protocol_version(), + ); + + let _ = env.clients[0].process_block_test(block.clone().into(), Provenance::NONE).unwrap(); + env.clients[0].finish_blocks_in_processing(); + + let root = *env.clients[0] + .chain + .get_chunk_extra(block.hash(), &ShardUId::single_shard()) + .unwrap() + .state_root(); + let trie = + env.clients[0].runtime_adapter.get_trie_for_shard(0, block.hash(), root, true).unwrap(); + let state_update = TrieUpdate::new(trie); + use unc_primitives::account::Account; + let mut account_unc_raw = state_update + .get(&TrieKey::Account { account_id: "near".parse().unwrap() }) + .unwrap() + .unwrap() + .clone(); + let account_near = Account::try_from_slice(&mut account_unc_raw).unwrap(); + let mut account_test0_raw = state_update + .get(&TrieKey::Account { account_id: "test0".parse().unwrap() }) + .unwrap() + .unwrap() + .clone(); + let account_test0 = Account::try_from_slice(&mut account_test0_raw).unwrap(); + check_storage_usage("near".parse().unwrap(), i, account_near.storage_usage()); + check_storage_usage("test0".parse().unwrap(), i, account_test0.storage_usage()); + } +} + +#[test] +fn test_fix_storage_usage_migration() { + init_test_logger(); + process_blocks_with_storage_usage_fix( + unc_primitives::chains::MAINNET.to_string(), + |account_id: AccountId, block_height: u64, storage_usage: u64| { + if account_id == "near" && block_height >= 11 { + assert_eq!(storage_usage, 4378); + } else { + assert_eq!(storage_usage, 182); + } + }, + ); + process_blocks_with_storage_usage_fix( + unc_primitives::chains::TESTNET.to_string(), + |_: AccountId, _: u64, storage_usage: u64| { + assert_eq!(storage_usage, 182); + }, + ); +} diff --git a/integration-tests/src/tests/client/features/flat_storage.rs b/integration-tests/src/tests/client/features/flat_storage.rs new file mode 100644 index 000000000..8fc965d76 --- /dev/null +++ b/integration-tests/src/tests/client/features/flat_storage.rs @@ -0,0 +1,153 @@ +use crate::tests::client::process_blocks::deploy_test_contract_with_protocol_version; +use unc_chain::ChainGenesis; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_client::ProcessTxResponse; +use unc_crypto::{InMemorySigner, KeyType, Signer}; +use unc_parameters::ExtCosts; +use unc_primitives::test_utils::encode; +use unc_primitives::transaction::{Action, ExecutionMetadata, FunctionCallAction, Transaction}; +use unc_primitives::version::ProtocolFeature; +use unc_primitives_core::hash::CryptoHash; +use unc_primitives_core::types::Gas; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; + +/// Check that after flat storage upgrade: +/// - value read from contract is the same; +/// - touching trie node cost for read decreases to zero. +#[test] +fn test_flat_storage_upgrade() { + // The immediate protocol upgrade needs to be set for this test to pass in + // the release branch where the protocol upgrade date is set. + std::env::set_var("unc_TESTS_IMMEDIATE_PROTOCOL_UPGRADE", "1"); + + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let epoch_length = 12; + let new_protocol_version = ProtocolFeature::FlatStorageReads.protocol_version(); + let old_protocol_version = new_protocol_version - 1; + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = old_protocol_version; + let chain_genesis = ChainGenesis::new(&genesis); + let runtime_config = unc_parameters::RuntimeConfigStore::new(None); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes_with_runtime_config_store(&genesis, vec![runtime_config]) + .build(); + + // We assume that it is enough to process 4 blocks to get a single txn included and processed. + // At the same time, once we process `>= 2 * epoch_length` blocks, protocol can get + // auto-upgraded to latest version. We use this value to process 3 transactions for older + // protocol version. So we choose this value to be `epoch_length / 3` and we process only + // `epoch_length` blocks in total. + // TODO (#8703): resolve this properly + let blocks_to_process_txn = epoch_length / 3; + + // Deploy contract to state. + deploy_test_contract_with_protocol_version( + &mut env, + "test0".parse().unwrap(), + unc_test_contracts::backwards_compatible_rs_contract(), + blocks_to_process_txn, + 1, + old_protocol_version, + ); + + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let gas = 20_000_000_000_000; + let tx = Transaction { + signer_id: "test0".parse().unwrap(), + receiver_id: "test0".parse().unwrap(), + public_key: signer.public_key(), + actions: vec![], + nonce: 0, + block_hash: CryptoHash::default(), + }; + + // Write key-value pair to state. + { + let write_value_action = vec![Action::FunctionCall(Box::new(FunctionCallAction { + args: encode(&[1u64, 10u64]), + method_name: "write_key_value".to_string(), + gas, + deposit: 0, + }))]; + let tip = env.clients[0].chain.head().unwrap(); + let signed_transaction = Transaction { + nonce: 10, + block_hash: tip.last_block_hash, + actions: write_value_action, + ..tx.clone() + } + .sign(&signer); + let tx_hash = signed_transaction.get_hash(); + assert_eq!( + env.clients[0].process_tx(signed_transaction, false, false), + ProcessTxResponse::ValidTx + ); + for i in 0..blocks_to_process_txn { + env.produce_block(0, tip.height + i + 1); + } + + env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap().assert_success(); + } + + let touching_trie_node_costs: Vec<_> = (0..2) + .map(|i| { + let read_value_action = vec![Action::FunctionCall(Box::new(FunctionCallAction { + args: encode(&[1u64]), + method_name: "read_value".to_string(), + gas, + deposit: 0, + }))]; + let tip = env.clients[0].chain.head().unwrap(); + let signed_transaction = Transaction { + nonce: 20 + i, + block_hash: tip.last_block_hash, + actions: read_value_action, + ..tx.clone() + } + .sign(&signer); + let tx_hash = signed_transaction.get_hash(); + assert_eq!( + env.clients[0].process_tx(signed_transaction, false, false), + ProcessTxResponse::ValidTx + ); + for i in 0..blocks_to_process_txn { + env.produce_block(0, tip.height + i + 1); + } + if i == 0 { + env.upgrade_protocol(new_protocol_version); + } + + let final_transaction_result = + env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap(); + final_transaction_result.assert_success(); + let receipt_id = final_transaction_result.receipts_outcome[0].id; + let metadata = env.clients[0] + .chain + .get_execution_outcome(&receipt_id) + .unwrap() + .outcome_with_id + .outcome + .metadata; + if let ExecutionMetadata::V3(profile_data) = metadata { + profile_data.get_ext_cost(ExtCosts::touching_trie_node) + } else { + panic!("Too old version of metadata: {metadata:?}"); + } + }) + .collect(); + + // Guaranteed touching trie node cost in all protocol versions until + // `ProtocolFeature::FlatStorageReads`, included. + let touching_trie_node_base_cost: Gas = 16_101_955_926; + + // For the first read, cost should be 3 TTNs because trie path is: + // (Branch) -> (Extension) -> (Leaf) -> (Value) + // but due to a bug in storage_read we don't charge for Value. + assert_eq!(touching_trie_node_costs[0], touching_trie_node_base_cost * 3); + + // For the second read, we don't go to Flat storage and don't charge TTN. + assert_eq!(touching_trie_node_costs[1], 0); +} diff --git a/integration-tests/src/tests/client/features/in_memory_tries.rs b/integration-tests/src/tests/client/features/in_memory_tries.rs new file mode 100644 index 000000000..b40c35831 --- /dev/null +++ b/integration-tests/src/tests/client/features/in_memory_tries.rs @@ -0,0 +1,390 @@ +use std::collections::{HashMap, HashSet}; + +use unc_chain::{ChainGenesis, Provenance}; +use unc_chain_configs::{Genesis, GenesisConfig, GenesisRecords}; +use unc_client::test_utils::TestEnv; +use unc_client::ProcessTxResponse; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::block::Tip; +use unc_primitives::shard_layout::ShardLayout; +use unc_primitives::state_record::StateRecord; +use unc_primitives::test_utils::{create_test_signer, create_user_test_signer}; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{AccountInfo, EpochId}; +use unc_primitives_core::account::{AccessKey, Account}; +use unc_primitives_core::hash::CryptoHash; +use unc_primitives_core::types::AccountId; +use unc_primitives_core::version::PROTOCOL_VERSION; +use unc_store::test_utils::create_test_store; +use unc_store::{ShardUId, TrieConfig}; +use framework::test_utils::TestEnvNightshadeSetupExt; +use rand::seq::IteratorRandom; +use rand::{thread_rng, Rng}; + +const ONE_NEAR: u128 = 1_000_000_000_000_000_000_000_000; + +#[test] +fn test_in_memory_trie_node_consistency() { + // Recommended to run with RUST_LOG=memtrie=debug,chunks=error,info + init_test_logger(); + let validator_stake = 1000000 * ONE_NEAR; + let initial_balance = 10000 * ONE_NEAR; + let accounts = + (0..100).map(|i| format!("account{}", i).parse().unwrap()).collect::>(); + let mut genesis_config = GenesisConfig { + // Use the latest protocol version. Otherwise, the version may be too + // old that e.g. blocks don't even store previous heights. + protocol_version: PROTOCOL_VERSION, + // Some arbitrary starting height. Doesn't matter. + genesis_height: 10000, + // We'll test with 4 shards. This can be any number, but we want to test + // the case when some shards are loaded into memory and others are not. + // We pick the boundaries so that each shard would get some transactions. + shard_layout: ShardLayout::v1( + vec!["account3", "account5", "account7"] + .into_iter() + .map(|a| a.parse().unwrap()) + .collect(), + None, + 1, + ), + // We're going to send NEAR between accounts and then assert at the end + // that these transactions have been processed correctly, so here we set + // the gas price to 0 so that we don't have to calculate gas cost. + min_gas_price: 0, + max_gas_price: 0, + // Set the block gas limit high enough so we don't have to worry about + // transactions being throttled. + gas_limit: 100000000000000, + // Set the validity period high enough so even if a transaction gets + // included a few blocks later it won't be rejected. + transaction_validity_period: 1000, + // Make two validators. In this test we don't care about validators but + // the TestEnv framework works best if all clients are validators. So + // since we are using two clients, make two validators. + validators: vec![ + AccountInfo { + account_id: accounts[0].clone(), + amount: validator_stake, + public_key: create_test_signer(accounts[0].as_str()).public_key(), + }, + AccountInfo { + account_id: accounts[1].clone(), + amount: validator_stake, + public_key: create_test_signer(accounts[1].as_str()).public_key(), + }, + ], + // We don't care about epoch transitions in this test, and epoch + // transitions means validator selection, which can kick out validators + // (due to our test purposefully skipping blocks to create forks), and + // that's annoying to deal with. So set this to a high value to stay + // within a single epoch. + epoch_length: 10000, + // The genesis requires this, so set it to something arbitrary. + protocol_treasury_account: accounts[2].clone(), + // Simply make all validators block producers. + num_block_producer_seats: 2, + // Make all validators produce chunks for all shards. + minimum_validators_per_shard: 2, + // Even though not used for the most recent protocol version, + // this must still have the same length as the number of shards, + // or else the genesis fails validation. + num_block_producer_seats_per_shard: vec![2, 2, 2, 2], + ..Default::default() + }; + + // We'll now create the initial records. We'll set up 100 accounts, each + // with some initial balance. We'll add an access key to each account so + // we can send transactions from them. + let mut records = Vec::new(); + for (i, account) in accounts.iter().enumerate() { + // The staked amount must be consistent with validators from genesis. + let staked = if i < 2 { validator_stake } else { 0 }; + records.push(StateRecord::Account { + account_id: account.clone(), + account: Account::new(initial_balance, staked, CryptoHash::default(), 0), + }); + records.push(StateRecord::AccessKey { + account_id: account.clone(), + public_key: create_user_test_signer(&account).public_key, + access_key: AccessKey::full_access(), + }); + // The total supply must be correct to pass validation. + genesis_config.total_supply += initial_balance + staked; + } + let genesis = Genesis::new(genesis_config, GenesisRecords(records)).unwrap(); + let chain_genesis = ChainGenesis::new(&genesis); + + // Create two stores, one for each node. We'll be reusing the stores later + // to emulate node restarts. + let stores = vec![create_test_store(), create_test_store()]; + let mut env = TestEnv::builder(chain_genesis.clone()) + .clients(vec!["account0".parse().unwrap(), "account1".parse().unwrap()]) + .stores(stores.clone()) + .real_epoch_managers(&genesis.config) + .track_all_shards() + .nightshade_runtimes_with_trie_config( + &genesis, + vec![ + TrieConfig::default(), // client 0 does not load in-memory tries + TrieConfig { + // client 1 loads two of four shards into in-memory tries + load_mem_tries_for_shards: vec![ + ShardUId { version: 1, shard_id: 0 }, + ShardUId { version: 1, shard_id: 2 }, + ], + ..Default::default() + }, + ], + ) + .build(); + + // Sanity check that we should have two block producers. + assert_eq!( + env.clients[0] + .epoch_manager + .get_epoch_block_producers_ordered( + &EpochId::default(), + &env.clients[0].chain.head().unwrap().last_block_hash + ) + .unwrap() + .len(), + 2 + ); + + // First, start up the nodes from genesis. This ensures that in-memory + // tries works correctly when starting up an empty node for the first time. + let mut nonces = + accounts.iter().map(|account| (account.clone(), 0)).collect::>(); + let mut balances = accounts + .iter() + .map(|account| (account.clone(), initial_balance)) + .collect::>(); + + run_chain_for_some_blocks_while_sending_money_around(&mut env, &mut nonces, &mut balances, 100); + // Sanity check that in-memory tries are loaded, and garbage collected properly. + // We should have 4 roots for each loaded shard, because we maintain in-memory + // roots until (and including) the prev block of the last final block. So if the + // head is N, then we have roots for N, N - 1, N - 2 (final), and N - 3. + assert_eq!(num_memtrie_roots(&env, 0, "s0.v1".parse().unwrap()), None); + assert_eq!(num_memtrie_roots(&env, 0, "s1.v1".parse().unwrap()), None); + assert_eq!(num_memtrie_roots(&env, 0, "s2.v1".parse().unwrap()), None); + assert_eq!(num_memtrie_roots(&env, 0, "s3.v1".parse().unwrap()), None); + assert_eq!(num_memtrie_roots(&env, 1, "s0.v1".parse().unwrap()), Some(4)); + assert_eq!(num_memtrie_roots(&env, 1, "s1.v1".parse().unwrap()), None); + assert_eq!(num_memtrie_roots(&env, 1, "s2.v1".parse().unwrap()), Some(4)); + assert_eq!(num_memtrie_roots(&env, 1, "s3.v1".parse().unwrap()), None); + + // Restart nodes, and change some configs. + drop(env); + let mut env = TestEnv::builder(chain_genesis.clone()) + .clients(vec!["account0".parse().unwrap(), "account1".parse().unwrap()]) + .stores(stores.clone()) + .real_epoch_managers(&genesis.config) + .track_all_shards() + .nightshade_runtimes_with_trie_config( + &genesis, + vec![ + TrieConfig::default(), + TrieConfig { + load_mem_tries_for_shards: vec![ + ShardUId { version: 1, shard_id: 0 }, + ShardUId { version: 1, shard_id: 1 }, // shard 2 changed to shard 1. + ], + ..Default::default() + }, + ], + ) + .build(); + run_chain_for_some_blocks_while_sending_money_around(&mut env, &mut nonces, &mut balances, 100); + assert_eq!(num_memtrie_roots(&env, 0, "s0.v1".parse().unwrap()), None); + assert_eq!(num_memtrie_roots(&env, 0, "s1.v1".parse().unwrap()), None); + assert_eq!(num_memtrie_roots(&env, 0, "s2.v1".parse().unwrap()), None); + assert_eq!(num_memtrie_roots(&env, 0, "s3.v1".parse().unwrap()), None); + assert_eq!(num_memtrie_roots(&env, 1, "s0.v1".parse().unwrap()), Some(4)); + assert_eq!(num_memtrie_roots(&env, 1, "s1.v1".parse().unwrap()), Some(4)); + assert_eq!(num_memtrie_roots(&env, 1, "s2.v1".parse().unwrap()), None); + assert_eq!(num_memtrie_roots(&env, 1, "s3.v1".parse().unwrap()), None); + + // Restart again, but this time flip the nodes. + drop(env); + let mut env = TestEnv::builder(chain_genesis) + .clients(vec!["account0".parse().unwrap(), "account1".parse().unwrap()]) + .stores(stores) + .real_epoch_managers(&genesis.config) + .track_all_shards() + .nightshade_runtimes_with_trie_config( + &genesis, + vec![ + // client 0 now loads in-memory tries + TrieConfig { + load_mem_tries_for_shards: vec![ + ShardUId { version: 1, shard_id: 1 }, + ShardUId { version: 1, shard_id: 3 }, + ], + ..Default::default() + }, + // client 1 no longer loads in-memory tries + TrieConfig::default(), + ], + ) + .build(); + run_chain_for_some_blocks_while_sending_money_around(&mut env, &mut nonces, &mut balances, 100); + assert_eq!(num_memtrie_roots(&env, 0, "s0.v1".parse().unwrap()), None); + assert_eq!(num_memtrie_roots(&env, 0, "s1.v1".parse().unwrap()), Some(4)); + assert_eq!(num_memtrie_roots(&env, 0, "s2.v1".parse().unwrap()), None); + assert_eq!(num_memtrie_roots(&env, 0, "s3.v1".parse().unwrap()), Some(4)); + assert_eq!(num_memtrie_roots(&env, 1, "s0.v1".parse().unwrap()), None); + assert_eq!(num_memtrie_roots(&env, 1, "s1.v1".parse().unwrap()), None); + assert_eq!(num_memtrie_roots(&env, 1, "s2.v1".parse().unwrap()), None); + assert_eq!(num_memtrie_roots(&env, 1, "s3.v1".parse().unwrap()), None); +} + +// Returns the block producer for the height of head + height_offset. +fn get_block_producer(env: &TestEnv, head: &Tip, height_offset: u64) -> AccountId { + let client = &env.clients[0]; + let epoch_manager = &client.epoch_manager; + let parent_hash = &head.last_block_hash; + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(parent_hash).unwrap(); + let height = head.height + height_offset; + let block_producer = epoch_manager.get_block_producer(&epoch_id, height).unwrap(); + block_producer +} + +/// Runs the chain for some number of blocks, sending money around randomly between +/// the test accounts, updating the corresponding nonces and balances. At the end, +/// check that the balances are correct, i.e. the transactions have been executed +/// correctly. If this runs successfully, it would also mean that the two nodes +/// being tested are consistent with each other. If, for example, there is a state +/// root mismatch issue, the two nodes would not be able to apply each others' +/// blocks because the block hashes would be different. +fn run_chain_for_some_blocks_while_sending_money_around( + env: &mut TestEnv, + nonces: &mut HashMap, + balances: &mut HashMap, + num_rounds: usize, +) { + // Run the chain for some extra blocks, to ensure that all transactions are + // included in the chain and are executed completely. + for round in 0..(num_rounds + 10) { + let heads = env + .clients + .iter() + .map(|client| client.chain.head().unwrap().last_block_hash) + .collect::>(); + assert_eq!(heads.len(), 1, "All clients should have the same head"); + let tip = env.clients[0].chain.head().unwrap(); + + if round < num_rounds { + // Make 50 random transactions that send money between random accounts. + for _ in 0..50 { + let sender = nonces.keys().choose(&mut thread_rng()).unwrap().clone(); + let receiver = nonces.keys().choose(&mut thread_rng()).unwrap().clone(); + let nonce = nonces.get_mut(&sender).unwrap(); + *nonce += 1; + + let txn = SignedTransaction::send_money( + *nonce, + sender.clone(), + receiver.clone(), + &create_user_test_signer(&sender), + ONE_NEAR, + tip.last_block_hash, + ); + match env.clients[0].process_tx(txn, false, false) { + ProcessTxResponse::NoResponse => panic!("No response"), + ProcessTxResponse::InvalidTx(err) => panic!("Invalid tx: {}", err), + _ => {} + } + *balances.get_mut(&sender).unwrap() -= ONE_NEAR; + *balances.get_mut(&receiver).unwrap() += ONE_NEAR; + } + } + + let cur_block_producer = get_block_producer(&env, &tip, 1); + let next_block_producer = get_block_producer(&env, &tip, 2); + println!("Producing block at height {} by {}", tip.height + 1, cur_block_producer); + let block = env.client(&cur_block_producer).produce_block(tip.height + 1).unwrap().unwrap(); + + // Let's produce some skip blocks too so that we test that in-memory tries are able to + // deal with forks. + // At the end, finish with a bunch of non-skip blocks so that we can test that in-memory + // trie garbage collection works properly (final block is N - 2 so we should keep no more + // than 3 roots). + let mut skip_block = None; + if cur_block_producer != next_block_producer + && round < num_rounds + && thread_rng().gen_bool(0.5) + { + println!( + "Producing skip block at height {} by {}", + tip.height + 2, + next_block_producer + ); + // Produce some skip blocks too so that we test that in-memory tries are able to deal + // with forks. + skip_block = Some( + env.client(&next_block_producer).produce_block(tip.height + 2).unwrap().unwrap(), + ); + } + + // Apply height + 1 block. + for i in 0..env.clients.len() { + println!( + " Applying block at height {} at {}", + block.header().height(), + env.get_client_id(i) + ); + let blocks_processed = + env.clients[i].process_block_test(block.clone().into(), Provenance::NONE).unwrap(); + assert_eq!(blocks_processed, vec![*block.hash()]); + } + // Apply skip block if one was produced. + if let Some(skip_block) = skip_block { + for i in 0..env.clients.len() { + println!( + " Applying skip block at height {} at {}", + skip_block.header().height(), + env.get_client_id(i) + ); + let blocks_processed = env.clients[i] + .process_block_test(skip_block.clone().into(), Provenance::NONE) + .unwrap(); + assert_eq!(blocks_processed, vec![*skip_block.hash()]); + } + } + + // Send partial encoded chunks around so that the newly produced chunks + // can be included and processed in the next block. Having to do this + // sucks, because this test has nothing to do with partial encoded + // chunks, but it is the unfortunate reality when using TestEnv with + // multiple nodes. + env.process_partial_encoded_chunks(); + for j in 0..env.clients.len() { + env.process_shards_manager_responses_and_finish_processing_blocks(j); + } + } + + for (account, balance) in balances { + assert_eq!( + env.query_balance(account.clone()), + *balance, + "Balance mismatch for {}", + account, + ); + } +} + +/// Returns the number of memtrie roots for the given client and shard, or +/// None if that shard does not load memtries. +fn num_memtrie_roots(env: &TestEnv, client_id: usize, shard: ShardUId) -> Option { + Some( + env.clients[client_id] + .runtime_adapter + .get_tries() + .get_mem_tries(shard)? + .read() + .unwrap() + .num_roots(), + ) +} diff --git a/integration-tests/src/tests/client/features/increase_deployment_cost.rs b/integration-tests/src/tests/client/features/increase_deployment_cost.rs new file mode 100644 index 000000000..7c7369823 --- /dev/null +++ b/integration-tests/src/tests/client/features/increase_deployment_cost.rs @@ -0,0 +1,66 @@ +use assert_matches::assert_matches; +use unc_chain::ChainGenesis; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_parameters::vm::VMKind; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::transaction::{Action, DeployContractAction}; +use unc_primitives::version::ProtocolFeature; +use unc_primitives::views::FinalExecutionStatus; +use unc_primitives_core::version::PROTOCOL_VERSION; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; + +/// Tests if the cost of deployment is higher after the protocol update 53 +#[test] +fn test_deploy_cost_increased() { + // The immediate protocol upgrade needs to be set for this test to pass in + // the release branch where the protocol upgrade date is set. + std::env::set_var("unc_TESTS_IMMEDIATE_PROTOCOL_UPGRADE", "1"); + + let new_protocol_version = ProtocolFeature::IncreaseDeploymentCost.protocol_version(); + let old_protocol_version = new_protocol_version - 1; + + let config_store = RuntimeConfigStore::new(None); + let config = &config_store.get_config(PROTOCOL_VERSION).wasm_config; + let contract_size = 1024 * 1024; + let test_contract = unc_test_contracts::sized_contract(contract_size); + // Run code through preparation for validation. (Deploying will succeed either way). + unc_vm_runner::prepare::prepare_contract(&test_contract, config, VMKind::Wasmer2).unwrap(); + + // Prepare TestEnv with a contract at the old protocol version. + let epoch_length = 5; + let mut env = { + let mut genesis = Genesis::test(vec!["test0".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = old_protocol_version; + let chain_genesis = ChainGenesis::new(&genesis); + TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes_with_runtime_config_store( + &genesis, + vec![RuntimeConfigStore::new(None)], + ) + .build() + }; + + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let actions = vec![Action::DeployContract(DeployContractAction { code: test_contract })]; + + let tx = env.tx_from_actions(actions.clone(), &signer, signer.account_id.clone()); + let old_outcome = env.execute_tx(tx).unwrap(); + + env.upgrade_protocol(new_protocol_version); + + let tx = env.tx_from_actions(actions, &signer, signer.account_id.clone()); + let new_outcome = env.execute_tx(tx).unwrap(); + + assert_matches!(old_outcome.status, FinalExecutionStatus::SuccessValue(_)); + assert_matches!(new_outcome.status, FinalExecutionStatus::SuccessValue(_)); + + let old_deploy_gas = old_outcome.receipts_outcome[0].outcome.gas_burnt; + let new_deploy_gas = new_outcome.receipts_outcome[0].outcome.gas_burnt; + assert!(new_deploy_gas > old_deploy_gas); + assert_eq!(new_deploy_gas - old_deploy_gas, contract_size as u64 * (64_572_944 - 6_812_999)); +} diff --git a/integration-tests/src/tests/client/features/increase_storage_compute_cost.rs b/integration-tests/src/tests/client/features/increase_storage_compute_cost.rs new file mode 100644 index 000000000..a4539ce2d --- /dev/null +++ b/integration-tests/src/tests/client/features/increase_storage_compute_cost.rs @@ -0,0 +1,415 @@ +//! Tests to verify the compute cost limit for storage operations are increased +//! for protocol version 61. See `core/primitives/res/runtime_configs/61.yaml` +//! for the exact changes. +//! +//! We test `storage_write` and `storage_remove` because they can easily be +//! tested through existing methods `insert_strings` and `delete_strings` in +//! `unc_test_contracts::rs_contract()`. This doesn't cover all the changed +//! costs individually but if it works for two parameters we can be reasonably +//! confident that it works for the others as well. +//! We also test unaffected cases to make sure compute costs only affect +//! parameters they should. + +use unc_chain::ChainGenesis; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_client::ProcessTxResponse; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_parameters::RuntimeConfigStore; +use unc_parameters::{ActionCosts, RuntimeConfig}; +use unc_primitives::sharding::ShardChunk; +use unc_primitives::transaction::{ + Action, DeployContractAction, FunctionCallAction, SignedTransaction, +}; +use unc_primitives::types::AccountId; +use unc_primitives::version::ProtocolFeature; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; + +/// Tracked in https://github.com/utnet-org/utility/issues/8938 +const INCREASED_STORAGE_COSTS_PROTOCOL_VERSION: u32 = 61; + +/// Test that `storage_write` compute limit is respected in new version. +#[test] +fn test_storage_write() { + // `insert_strings(from: u64, to: u64)` makes (`to` - `from`) `storage_write` calls. + let method_name = "insert_strings".to_owned(); + let num_writes = 100u64; + let method_args: Vec = + 0u64.to_le_bytes().into_iter().chain(num_writes.to_le_bytes()).collect(); + let num_transactions = 200; + let uses_storage = true; + let fails = false; + let gas_divider = 1; + assert_compute_limit_reached( + method_name, + method_args, + num_transactions, + uses_storage, + gas_divider, + fails, + ); +} + +/// Test that `storage_remove` compute limit is respected in new version. +#[test] +fn test_storage_remove() { + // `delete_strings(from: u64, to: u64)` makes (`to` - `from`) `storage_remove` calls. + let method_name = "delete_strings".to_owned(); + let num_deletes = 1000u64; + let method_args: Vec = + 0u64.to_le_bytes().into_iter().chain(num_deletes.to_le_bytes()).collect(); + let num_transactions = 10; + let uses_storage = true; + let fails = false; + let gas_divider = 10; + assert_compute_limit_reached( + method_name, + method_args, + num_transactions, + uses_storage, + gas_divider, + fails, + ); +} + +/// Test that `storage_write` compute limit is respected in new version, +/// specifically when running out of gas. +#[test] +fn test_storage_write_gas_exceeded() { + // `insert_strings(from: u64, to: u64)` makes (`to` - `from`) `storage_write` calls. + let method_name = "insert_strings".to_owned(); + // 10000 writes should be too much and result in gas exceeded + let num_writes = 10000u64; + let method_args: Vec = + 0u64.to_le_bytes().into_iter().chain(num_writes.to_le_bytes()).collect(); + let num_transactions = 10; + let uses_storage = true; + let fails = true; + let gas_divider = 1; + assert_compute_limit_reached( + method_name, + method_args, + num_transactions, + uses_storage, + gas_divider, + fails, + ); +} + +/// Check receipts that don't touch storage are unaffected by the new compute costs. +#[test] +fn test_non_storage() { + // `sum_n(u64)` just does some WASM computation. + // It should not be affected by compute costs, as it doesn't access storage. + let method_name = "sum_n".to_owned(); + // note: If we make `method_args` only 1M, then the gas cost increase due to + // finite-wasm change (also included in version 61) is large enough that we + // see the number of receipts fitting in a chunk going from 64 to 61 even + // without the compute costs. 10.03M is the sweet spot where the granularity is + // course enough that the finite-wasm change doesn't interfere. We can + // process exactly 8 receipts per chunk before and after finite-wasm. + // And if compute costs would additionally affect this, the test would fail + // because it is very close at the border between 7 or 8 receipts fitting + // (7 receipts with finite wasm require 999 Tgas, 8 receipts require 1142 Tgas). + let method_args: Vec = 10_030_000u64.to_le_bytes().to_vec(); + let num_transactions = 2; + let uses_storage = false; + let fails: bool = false; + let gas_divider = 10; + assert_compute_limit_reached( + method_name, + method_args, + num_transactions, + uses_storage, + gas_divider, + fails, + ); +} + +/// Test the case where a function call fails and the limit is unaffected by compute costs. +#[test] +fn test_non_storage_gas_exceeded() { + // `loop_forever()` loops until either gas is exhausted. + // It should not be affected by compute costs, as it doesn't access storage. + let method_name = "loop_forever".to_owned(); + let method_args: Vec = vec![]; + let num_transactions = 2; + let uses_storage = false; + let gas_divider = 10; + let fails = true; + assert_compute_limit_reached( + method_name, + method_args, + num_transactions, + uses_storage, + gas_divider, + fails, + ); +} + +/// Checks that a specific function call reaches the expected limits. +/// +/// (This is a helper function called by all tests above) +/// +/// The function call is on the test contract +/// (`unc_test_contracts::rs_contract()`). The gas limit is tested at version +/// 60 (before compute costs) and the limit is checked again with version 61 +/// (with compute costs). The boolean `uses_storage` defines +/// whether we expect the second limit to be more restrictive or if they should +/// both fill a chunk equally. +/// +/// Regarding `num_transactions`, this is how many function calls are queued up, +/// it should not be more than can be converted in a single chunk but more than can +/// be executed in a single chunk. Otherwise, the test doesn't work to check the +/// limits and consequently some assertions will fail. +fn assert_compute_limit_reached( + method_name: String, + method_args: Vec, + num_transactions: u64, + uses_storage: bool, + gas_divider: u64, + should_fail: bool, +) { + // The immediate protocol upgrade needs to be set for this test to pass in + // the release branch where the protocol upgrade date is set. + std::env::set_var("unc_TESTS_IMMEDIATE_PROTOCOL_UPGRADE", "1"); + unc_o11y::testonly::init_test_logger(); + + let new_protocol_version = INCREASED_STORAGE_COSTS_PROTOCOL_VERSION; + assert!( + new_protocol_version >= ProtocolFeature::ComputeCosts.protocol_version(), + "relies on compute costs feature" + ); + let old_protocol_version = new_protocol_version - 1; + + // Prepare TestEnv with a contract at the old protocol version. + let epoch_length = 100; + let contract_account: AccountId = "test0".parse().unwrap(); + let user_account: AccountId = "test1".parse().unwrap(); + let runtime_config_store = RuntimeConfigStore::new(None); + let old_config = runtime_config_store.get_config(old_protocol_version).clone(); + let new_config = runtime_config_store.get_config(new_protocol_version).clone(); + let mut env = { + let mut genesis = Genesis::test(vec![contract_account.clone(), user_account.clone()], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = old_protocol_version; + genesis.config.gas_limit = genesis.config.gas_limit / gas_divider; + let chain_genesis = ChainGenesis::new(&genesis); + TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes_with_runtime_config_store(&genesis, vec![runtime_config_store]) + .build() + }; + + // setup: deploy the contract + { + // This contract has a bunch of methods to invoke storage operations. + let code = unc_test_contracts::backwards_compatible_rs_contract().to_vec(); + let actions = vec![Action::DeployContract(DeployContractAction { code })]; + + let signer = InMemorySigner::from_seed( + contract_account.clone(), + KeyType::ED25519, + contract_account.as_ref(), + ); + let tx = env.tx_from_actions(actions, &signer, signer.account_id.clone()); + env.execute_tx(tx).unwrap().assert_success(); + } + + // Fetching the correct nonce from `env` is a bit fiddly, we would have to + // query the access key of the user. It's easier to keep a shared counter + // that starts at 1 and increases monotonically. + let mut nonce = 1; + + let old_chunk = produce_saturated_chunk( + &mut env, + &user_account, + &contract_account, + method_name.clone(), + method_args.clone(), + num_transactions, + should_fail, + old_config.as_ref(), + &mut nonce, + ); + let chunk_header = old_chunk.cloned_header(); + let gas_burnt = chunk_header.prev_gas_used(); + let gas_limit: u64 = chunk_header.gas_limit(); + assert!( + gas_burnt >= gas_limit, + "should saturate gas limit, only burnt {gas_burnt} when limit was {gas_limit}" + ); + + env.upgrade_protocol(new_protocol_version); + + let new_chunk = produce_saturated_chunk( + &mut env, + &user_account, + &contract_account, + method_name, + method_args, + num_transactions, + should_fail, + new_config.as_ref(), + &mut nonce, + ); + + let old_receipts_num = old_chunk.prev_outgoing_receipts().len(); + let new_receipts_num = new_chunk.prev_outgoing_receipts().len(); + if uses_storage { + assert!( + new_receipts_num < old_receipts_num, + "should reach compute limit before gas limit (receipts before: {} receipts now: {})", + old_receipts_num, + new_receipts_num, + ); + } else { + assert_eq!(old_receipts_num, new_receipts_num, "compute costs should not affect this test"); + } +} + +/// Saturate a chunk with function call receipts and returns that chunk. +/// +/// (This is a helper function called twice by the helper function above, once +/// before and once after the upgrade.) +/// +/// This function creates many function call receipts with the given signer, +/// receiver, method name, and method argument. Then it submits it to a client +/// and produces a few blocks. Then it returns the first chunk that executes +/// these receipts. This chunk should be saturated with receipts, so either the +/// gas limit or the compute limit were reached. +/// (depending on protocol version) +fn produce_saturated_chunk( + env: &mut TestEnv, + user_account: &AccountId, + contract_account: &AccountId, + method_name: String, + args: Vec, + num_transactions: u64, + should_fail: bool, + config: &RuntimeConfig, + nonce: &mut u64, +) -> std::sync::Arc { + let msg_len = (method_name.len() + args.len()) as u64; // needed for gas computation later + let gas = 300_000_000_000_000; + let actions = vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name, + args, + gas, + deposit: 0, + }))]; + let signer = + InMemorySigner::from_seed(user_account.clone(), KeyType::ED25519, user_account.as_ref()); + + let tip = env.clients[0].chain.head().unwrap(); + let mut tx_factory = || { + let tx = SignedTransaction::from_actions( + *nonce, + signer.account_id.clone(), + contract_account.clone(), + &signer, + actions.clone(), + tip.last_block_hash, + ); + *nonce += 1; + tx + }; + + // IMPORTANT: Run one warm-up round to set up the trie shape. Without this, + // the gas costs are all over the place, which usually results in the first + // chunk being much cheaper than everything that follows. Which makes it + // look like compute costs work even if they don't! + let result = env.execute_tx(tx_factory()).unwrap(); + if !should_fail { + result.assert_success(); + } + + let tip = env.clients[0].chain.head().unwrap(); + let mut tx_ids = vec![]; + for _ in 0..num_transactions { + let tx = tx_factory(); + tx_ids.push(tx.get_hash()); + + // add tx to the mempool but don't execute it yet + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + // process the queued transactions + env.produce_block(0, tip.height + 1); // this produces an empty chunk + env.produce_block(0, tip.height + 2); // transactions are included in the chunk + env.produce_block(0, tip.height + 3); // receipts are included in the chunk + env.produce_block(0, tip.height + 4); // receipts are executed, one refund receipt generated for each receipt executed + + // chunk with transactions accepted but not yet executed + { + let chunk = chunk_info(env, tip.height + 2); + assert_eq!( + 0, + chunk.prev_outgoing_receipts().len(), + "First chunk should only include transactions" + ); + assert_eq!( + num_transactions as usize, + chunk.transactions().len(), + "All created transactions should be accepted in one chunk" + ); + } + // chunk where transactions are converted to receipts + { + let chunk = chunk_info(env, tip.height + 3); + assert_eq!( + num_transactions as usize, + chunk.prev_outgoing_receipts().len(), + "Second chunk should include all receipts" + ); + assert_eq!(0, chunk.transactions().len(), "Second chunk shouldn't have new transactions"); + + // Note: Receipts are included in chunk, but executed are + // transactions from previous chunk, so the gas here is for + // transactions -> receipt conversion. + let gas_burnt = chunk.cloned_header().prev_gas_used(); + let want_gas_per_tx = config.fees.fee(ActionCosts::new_action_receipt).send_not_sir + + config.fees.fee(ActionCosts::function_call_base).send_not_sir + + config.fees.fee(ActionCosts::function_call_byte).send_not_sir * msg_len; + assert_eq!( + gas_burnt, + want_gas_per_tx * num_transactions, + "Didn't burn the expected amount of gas to convert all transactions to receipts" + ); + } + // chunk receipts start to execute, we are counting the refund receipts generated + let saturated_chunk = { + let chunk: std::sync::Arc = chunk_info(env, tip.height + 4); + assert_ne!( + num_transactions as usize, + chunk.prev_outgoing_receipts().len(), + "Not all receipts should fit in a single chunk" + ); + assert_ne!(0, chunk.prev_outgoing_receipts().len(), "No receipts executed"); + chunk + }; + + // execute more blocks to get all pending receipts flushed out + for i in 5..30 { + env.produce_block(0, tip.height + i); // receipts are executed + } + // check all transactions are successfully executed (unless the test + // explicitly wants failing receipts) + if !should_fail { + for id in tx_ids { + env.clients[0].chain.get_final_transaction_result(&id).unwrap().assert_success(); + } + } + + saturated_chunk +} + +/// fetch chunk for shard 0 and specified block height +fn chunk_info(env: &TestEnv, height: u64) -> std::sync::Arc { + let block = &env.clients[0].chain.get_block_by_height(height).unwrap(); + let chunks = &block.chunks(); + assert_eq!(chunks.len(), 1, "test assumes single shard"); + let chunk_header = chunks.get(0).unwrap().clone(); + env.clients[0].chain.get_chunk(&chunk_header.chunk_hash()).unwrap() +} diff --git a/integration-tests/src/tests/client/features/limit_contract_functions_number.rs b/integration-tests/src/tests/client/features/limit_contract_functions_number.rs new file mode 100644 index 000000000..3e30ccbc0 --- /dev/null +++ b/integration-tests/src/tests/client/features/limit_contract_functions_number.rs @@ -0,0 +1,95 @@ +use crate::tests::client::process_blocks::deploy_test_contract; +use assert_matches::assert_matches; +use unc_chain::ChainGenesis; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::errors::{ + ActionErrorKind, CompilationError, FunctionCallError, PrepareError, TxExecutionError, +}; +use unc_primitives::version::ProtocolFeature; +use unc_primitives::views::FinalExecutionStatus; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; + +fn verify_contract_limits_upgrade( + feature: ProtocolFeature, + function_limit: u32, + local_limit: u32, + expected_prepare_err: PrepareError, +) { + let old_protocol_version = feature.protocol_version() - 1; + let new_protocol_version = feature.protocol_version(); + + let epoch_length = 5; + // Prepare TestEnv with a contract at the old protocol version. + let mut env = { + let mut genesis = + Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = old_protocol_version; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes_with_runtime_config_store( + &genesis, + vec![RuntimeConfigStore::new(None)], + ) + .build(); + + deploy_test_contract( + &mut env, + "test0".parse().unwrap(), + &unc_test_contracts::LargeContract { + functions: function_limit + 1, + locals_per_function: local_limit + 1, + ..Default::default() + } + .make(), + epoch_length, + 1, + ); + env + }; + + let account = "test0".parse().unwrap(); + let old_outcome = env.call_main(&account); + + env.upgrade_protocol(new_protocol_version); + + let new_outcome = env.call_main(&account); + + assert_matches!(old_outcome.status, FinalExecutionStatus::SuccessValue(_)); + let e = match new_outcome.status { + FinalExecutionStatus::Failure(TxExecutionError::ActionError(e)) => e, + status => panic!("expected transaction to fail, got {:?}", status), + }; + match e.kind { + ActionErrorKind::FunctionCallError(FunctionCallError::CompilationError( + CompilationError::PrepareError(e), + )) if e == expected_prepare_err => (), + kind => panic!("got unexpected action error kind: {:?}", kind), + } +} + +// Check that we can't call a contract exceeding functions number limit after upgrade. +#[test] +fn test_function_limit_change() { + verify_contract_limits_upgrade( + ProtocolFeature::LimitContractFunctionsNumber, + 100_000, + 0, + PrepareError::TooManyFunctions, + ); +} + +// Check that we can't call a contract exceeding functions number limit after upgrade. +#[test] +fn test_local_limit_change() { + verify_contract_limits_upgrade( + ProtocolFeature::LimitContractLocals, + 64, + 15625, + PrepareError::TooManyLocals, + ); +} diff --git a/integration-tests/src/tests/client/features/lower_storage_key_limit.rs b/integration-tests/src/tests/client/features/lower_storage_key_limit.rs new file mode 100644 index 000000000..4920bc9b3 --- /dev/null +++ b/integration-tests/src/tests/client/features/lower_storage_key_limit.rs @@ -0,0 +1,155 @@ +use assert_matches::assert_matches; +use unc_chain::{ChainGenesis, Provenance}; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_client::ProcessTxResponse; +use unc_crypto::{InMemorySigner, KeyType, Signer}; +use unc_o11y::testonly::init_test_logger; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::errors::TxExecutionError; +use unc_primitives::hash::CryptoHash; +use unc_primitives::transaction::{Action, FunctionCallAction, Transaction}; +use unc_primitives::types::BlockHeight; +use unc_primitives::views::FinalExecutionStatus; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; + +use crate::tests::client::process_blocks::{ + deploy_test_contract_with_protocol_version, produce_blocks_from_height_with_protocol_version, +}; + +/// Check correctness of the protocol upgrade and ability to write 2 KB keys. +#[test] +fn protocol_upgrade() { + init_test_logger(); + + let old_protocol_version = + unc_primitives::version::ProtocolFeature::LowerStorageKeyLimit.protocol_version() - 1; + let new_protocol_version = old_protocol_version + 1; + let new_storage_key_limit = 2usize.pow(11); // 2 KB + let args: Vec = vec![1u8; new_storage_key_limit + 1] + .into_iter() + .chain(unc_primitives::test_utils::encode(&[10u64]).into_iter()) + .collect(); + let epoch_length: BlockHeight = 5; + + // The immediate protocol upgrade needs to be set for this test to pass in + // the release branch where the protocol upgrade date is set. + std::env::set_var("unc_TESTS_IMMEDIATE_PROTOCOL_UPGRADE", "1"); + + // Prepare TestEnv with a contract at the old protocol version. + let mut env = { + let mut genesis = + Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = old_protocol_version; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .track_all_shards() + .nightshade_runtimes_with_runtime_config_store( + &genesis, + vec![RuntimeConfigStore::new(None)], + ) + .build(); + + deploy_test_contract_with_protocol_version( + &mut env, + "test0".parse().unwrap(), + unc_test_contracts::backwards_compatible_rs_contract(), + epoch_length, + 1, + old_protocol_version, + ); + env + }; + + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let tx = Transaction { + signer_id: "test0".parse().unwrap(), + receiver_id: "test0".parse().unwrap(), + public_key: signer.public_key(), + actions: vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "write_key_value".to_string(), + args, + gas: 10u64.pow(14), + deposit: 0, + }))], + + nonce: 0, + block_hash: CryptoHash::default(), + }; + + // Run transaction writing storage key exceeding the limit. Check that execution succeeds. + { + let tip = env.clients[0].chain.head().unwrap(); + let signed_tx = + Transaction { nonce: tip.height + 1, block_hash: tip.last_block_hash, ..tx.clone() } + .sign(&signer); + let tx_hash = signed_tx.get_hash(); + assert_eq!(env.clients[0].process_tx(signed_tx, false, false), ProcessTxResponse::ValidTx); + produce_blocks_from_height_with_protocol_version( + &mut env, + epoch_length, + tip.height + 1, + old_protocol_version, + ); + let final_result = env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap(); + assert_matches!(final_result.status, FinalExecutionStatus::SuccessValue(_)); + } + + env.upgrade_protocol(new_protocol_version); + + // Re-run the transaction, check that execution fails. + { + let tip = env.clients[0].chain.head().unwrap(); + let signed_tx = + Transaction { nonce: tip.height + 1, block_hash: tip.last_block_hash, ..tx } + .sign(&signer); + let tx_hash = signed_tx.get_hash(); + assert_eq!(env.clients[0].process_tx(signed_tx, false, false), ProcessTxResponse::ValidTx); + for i in 0..epoch_length { + let block = env.clients[0].produce_block(tip.height + i + 1).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + } + let final_result = env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap(); + assert_matches!( + final_result.status, + FinalExecutionStatus::Failure(TxExecutionError::ActionError(_)) + ); + } + + // Run transaction where storage key exactly fits the new limit, check that execution succeeds. + { + let args: Vec = vec![1u8; new_storage_key_limit] + .into_iter() + .chain(unc_primitives::test_utils::encode(&[20u64]).into_iter()) + .collect(); + let tx = Transaction { + signer_id: "test0".parse().unwrap(), + receiver_id: "test0".parse().unwrap(), + public_key: signer.public_key(), + actions: vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "write_key_value".to_string(), + args, + gas: 10u64.pow(14), + deposit: 0, + }))], + + nonce: 0, + block_hash: CryptoHash::default(), + }; + let tip = env.clients[0].chain.head().unwrap(); + let signed_tx = + Transaction { nonce: tip.height + 1, block_hash: tip.last_block_hash, ..tx } + .sign(&signer); + let tx_hash = signed_tx.get_hash(); + assert_eq!(env.clients[0].process_tx(signed_tx, false, false), ProcessTxResponse::ValidTx); + for i in 0..epoch_length { + let block = env.clients[0].produce_block(tip.height + i + 1).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + } + let final_result = env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap(); + assert_matches!(final_result.status, FinalExecutionStatus::SuccessValue(_)); + } +} diff --git a/integration-tests/src/tests/client/features/nearvm.rs b/integration-tests/src/tests/client/features/nearvm.rs new file mode 100644 index 000000000..fb00fb7f1 --- /dev/null +++ b/integration-tests/src/tests/client/features/nearvm.rs @@ -0,0 +1,100 @@ +#![cfg_attr(not(feature = "nightly"), allow(unused_imports))] + +use crate::tests::client::process_blocks::deploy_test_contract; +use unc_chain::ChainGenesis; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_client::ProcessTxResponse; +use unc_crypto::{InMemorySigner, KeyType, Signer}; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::hash::CryptoHash; +use unc_primitives::transaction::{Action, FunctionCallAction, Transaction}; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; + +#[cfg_attr(all(target_arch = "aarch64", target_vendor = "apple"), ignore)] +#[test] +fn test_nearvm_upgrade() { + let mut capture = unc_o11y::testonly::TracingCapture::enable(); + + let old_protocol_version = + unc_primitives::version::ProtocolFeature::NearVmRuntime.protocol_version() - 1; + let new_protocol_version = old_protocol_version + 1; + + // Prepare TestEnv with a contract at the old protocol version. + let mut env = { + let epoch_length = 5; + let mut genesis = + Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = old_protocol_version; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes_with_runtime_config_store( + &genesis, + vec![RuntimeConfigStore::new(None)], + ) + .build(); + + deploy_test_contract( + &mut env, + "test0".parse().unwrap(), + unc_test_contracts::backwards_compatible_rs_contract(), + epoch_length, + 1, + ); + env + }; + + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let tx = Transaction { + signer_id: "test0".parse().unwrap(), + receiver_id: "test0".parse().unwrap(), + public_key: signer.public_key(), + actions: vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "log_something".to_string(), + args: Vec::new(), + gas: 100_000_000_000_000, + deposit: 0, + }))], + + nonce: 0, + block_hash: CryptoHash::default(), + }; + + // Run the transaction & collect the logs. + let logs_at_old_version = { + let tip = env.clients[0].chain.head().unwrap(); + let signed_transaction = + Transaction { nonce: 10, block_hash: tip.last_block_hash, ..tx.clone() }.sign(&signer); + assert_eq!( + env.clients[0].process_tx(signed_transaction, false, false), + ProcessTxResponse::ValidTx + ); + for i in 0..3 { + env.produce_block(0, tip.height + i + 1); + } + capture.drain() + }; + + env.upgrade_protocol(new_protocol_version); + + // Re-run the transaction. + let logs_at_new_version = { + let tip = env.clients[0].chain.head().unwrap(); + let signed_transaction = + Transaction { nonce: 11, block_hash: tip.last_block_hash, ..tx }.sign(&signer); + assert_eq!( + env.clients[0].process_tx(signed_transaction, false, false), + ProcessTxResponse::ValidTx + ); + for i in 0..3 { + env.produce_block(0, tip.height + i + 1); + } + capture.drain() + }; + + assert!(logs_at_old_version.iter().any(|l| l.contains(&"vm_kind=Wasmer2"))); + assert!(dbg!(logs_at_new_version).iter().any(|l| l.contains(&"vm_kind=NearVm"))); +} diff --git a/integration-tests/src/tests/client/features/restore_receipts_after_fix_apply_chunks.rs b/integration-tests/src/tests/client/features/restore_receipts_after_fix_apply_chunks.rs new file mode 100644 index 000000000..22ff982b3 --- /dev/null +++ b/integration-tests/src/tests/client/features/restore_receipts_after_fix_apply_chunks.rs @@ -0,0 +1,143 @@ +use crate::tests::client::process_blocks::set_block_protocol_version; +use unc_chain::{ChainGenesis, Provenance}; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::hash::CryptoHash; +use unc_primitives::runtime::migration_data::MigrationData; +use unc_primitives::types::BlockHeight; +use unc_primitives::version::ProtocolFeature; +use framework::config::GenesisExt; +use framework::migrations::load_migration_data; +use framework::test_utils::TestEnvNightshadeSetupExt; +use std::collections::HashSet; + +const EPOCH_LENGTH: u64 = 5; +const HEIGHT_TIMEOUT: u64 = 10; + +fn run_test( + chain_id: &str, + low_height_with_no_chunk: BlockHeight, + high_height_with_no_chunk: BlockHeight, + should_be_restored: bool, +) { + init_test_logger(); + + let protocol_version = + ProtocolFeature::RestoreReceiptsAfterFixApplyChunks.protocol_version() - 1; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.chain_id = String::from(chain_id); + genesis.config.epoch_length = EPOCH_LENGTH; + genesis.config.protocol_version = protocol_version; + let chain_genesis = ChainGenesis::new(&genesis); + // TODO #4305: get directly from NightshadeRuntime + let migration_data = load_migration_data(&genesis.config.chain_id); + + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let get_restored_receipt_hashes = |migration_data: &MigrationData| -> HashSet { + HashSet::from_iter( + migration_data + .restored_receipts + .get(&0u64) + .cloned() + .unwrap_or_default() + .iter() + .map(|receipt| receipt.receipt_id), + ) + }; + + let mut receipt_hashes_to_restore = get_restored_receipt_hashes(&migration_data); + let mut height: BlockHeight = 1; + let mut last_update_height: BlockHeight = 0; + + // Simulate several blocks to guarantee that they are produced successfully. + // Stop block production if all receipts were restored. Or, if some receipts are still not + // applied, upgrade already happened, and no new receipt was applied in some last blocks, + // consider the process stuck to avoid any possibility of infinite loop. + while height < 15 + || (!receipt_hashes_to_restore.is_empty() && height - last_update_height < HEIGHT_TIMEOUT) + { + let mut block = env.clients[0].produce_block(height).unwrap().unwrap(); + if low_height_with_no_chunk <= height && height < high_height_with_no_chunk { + let prev_block = env.clients[0].chain.get_block_by_height(height - 1).unwrap().clone(); + testlib::process_blocks::set_no_chunk_in_block(&mut block, &prev_block); + } + set_block_protocol_version( + &mut block, + "test0".parse().unwrap(), + ProtocolFeature::RestoreReceiptsAfterFixApplyChunks.protocol_version(), + ); + + env.process_block(0, block, Provenance::PRODUCED); + + let last_block = env.clients[0].chain.get_block_by_height(height).unwrap().clone(); + let protocol_version = env.clients[0] + .epoch_manager + .get_epoch_protocol_version(last_block.header().epoch_id()) + .unwrap(); + + for receipt_id in receipt_hashes_to_restore.clone().iter() { + if env.clients[0].chain.get_execution_outcome(receipt_id).is_ok() { + assert!( + protocol_version + >= ProtocolFeature::RestoreReceiptsAfterFixApplyChunks.protocol_version(), + "Restored receipt {} was executed before protocol upgrade", + receipt_id + ); + receipt_hashes_to_restore.remove(receipt_id); + last_update_height = height; + }; + } + + // Update last updated height anyway if upgrade did not happen + if protocol_version < ProtocolFeature::RestoreReceiptsAfterFixApplyChunks.protocol_version() + { + last_update_height = height; + } + height += 1; + } + + if should_be_restored { + assert!( + receipt_hashes_to_restore.is_empty(), + "Some of receipts were not executed, hashes: {:?}", + receipt_hashes_to_restore + ); + } else { + assert_eq!( + receipt_hashes_to_restore, + get_restored_receipt_hashes(&migration_data), + "If accidentally there are no chunks in first epoch with new protocol version, receipts should not be introduced" + ); + } +} + +#[test] +fn test_no_chunks_missing() { + // If there are no chunks missing, all receipts should be applied + run_test(unc_primitives::chains::MAINNET, 1, 0, true); +} + +#[test] +fn test_first_chunk_in_epoch_missing() { + // If the first chunk in the first epoch with needed protocol version is missing, + // all receipts should still be applied + run_test(unc_primitives::chains::MAINNET, 8, 12, true); +} + +#[test] +fn test_all_chunks_in_epoch_missing() { + // If all chunks are missing in the first epoch, no receipts should be applied + run_test(unc_primitives::chains::MAINNET, 11, 11 + EPOCH_LENGTH, false); +} + +#[test] +fn test_run_for_testnet() { + // Run the same process for chain other than mainnet to ensure that blocks are produced + // successfully during the protocol upgrade. + run_test(unc_primitives::chains::TESTNET, 1, 0, true); +} diff --git a/integration-tests/src/tests/client/features/restrict_tla.rs b/integration-tests/src/tests/client/features/restrict_tla.rs new file mode 100644 index 000000000..9e1fac335 --- /dev/null +++ b/integration-tests/src/tests/client/features/restrict_tla.rs @@ -0,0 +1,65 @@ +use super::super::process_blocks::create_account; +use unc_chain::ChainGenesis; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_primitives::errors::{ActionError, ActionErrorKind}; +use unc_primitives::types::{AccountId, BlockHeight}; +use unc_primitives::views::FinalExecutionStatus; +use unc_primitives_core::version::PROTOCOL_VERSION; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; + +#[test] +fn test_create_top_level_accounts() { + let epoch_length: BlockHeight = 5; + let account: AccountId = "test0".parse().unwrap(); + let mut genesis = Genesis::test(vec![account.clone()], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = PROTOCOL_VERSION; + let runtime_config = unc_parameters::RuntimeConfigStore::new(None); + let mut env = TestEnv::builder(ChainGenesis::new(&genesis)) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes_with_runtime_config_store(&genesis, vec![runtime_config]) + .build(); + + // These accounts cannot be created because they are top level accounts that are not implicit. + // Note that implicit accounts have to be 64 or 42 (if starts with '0x') characters long. + let top_level_accounts = [ + "0x06012c8cf97bead5deae237070f9587f8e7a266da", + "0a5e97870f263700f46aa00d967821199b9bc5a120", + "0x000000000000000000000000000000000000000", + "alice", + "thisisaveryverylongtoplevelaccount", + ]; + for (index, id) in top_level_accounts.iter().enumerate() { + let new_account_id = id.parse::().unwrap(); + let tx_hash = create_account( + &mut env, + account.clone(), + new_account_id.clone(), + epoch_length, + 1 + index as u64 * epoch_length, + PROTOCOL_VERSION, + ); + let transaction_result = + env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::CreateAccountOnlyByRegistrar { + account_id: new_account_id, + registrar_account_id: "registrar".parse().unwrap(), + predecessor_id: account.clone() + } + } + .into() + ) + //assert_eq!( + // transaction_result.status, + // FinalExecutionStatus::SuccessValue(Vec::new()) + //); + ); + } +} diff --git a/integration-tests/src/tests/client/features/wallet_contract.rs b/integration-tests/src/tests/client/features/wallet_contract.rs new file mode 100644 index 000000000..763b0845f --- /dev/null +++ b/integration-tests/src/tests/client/features/wallet_contract.rs @@ -0,0 +1,346 @@ +use assert_matches::assert_matches; +use unc_chain::ChainGenesis; +use unc_chain_configs::Genesis; +use unc_client::{test_utils::TestEnv, ProcessTxResponse}; +use unc_crypto::{InMemorySigner, KeyType, SecretKey}; +use unc_primitives::errors::{ + ActionError, ActionErrorKind, FunctionCallError, InvalidAccessKeyError, InvalidTxError, + TxExecutionError, +}; +use unc_primitives::test_utils::eth_implicit_test_account; +use unc_primitives::transaction::{ + Action, AddKeyAction, DeployContractAction, FunctionCallAction, SignedTransaction, + TransferAction, +}; +use unc_primitives::utils::derive_eth_implicit_account_id; +use unc_primitives::views::{ + FinalExecutionStatus, QueryRequest, QueryResponse, QueryResponseKind, +}; +use unc_primitives_core::{ + account::AccessKey, checked_feature, types::BlockHeight, version::PROTOCOL_VERSION, +}; +use unc_store::ShardUId; +use unc_vm_runner::ContractCode; +use unc_wallet_contract::{wallet_contract, wallet_contract_magic_bytes}; +use framework::{config::GenesisExt, test_utils::TestEnvNightshadeSetupExt, UNC_BASE}; +use node_runtime::ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT; +use rlp::RlpStream; +use testlib::runtime_utils::{alice_account, bob_account, carol_account}; + +use crate::{ + node::{Node, RuntimeNode}, + tests::client::process_blocks::produce_blocks_from_height, +}; + +/// Try to process tx in the next blocks, check that tx and all generated receipts succeed. +/// Return height of the next block. +fn check_tx_processing( + env: &mut TestEnv, + tx: SignedTransaction, + height: BlockHeight, + blocks_number: u64, +) -> BlockHeight { + let tx_hash = tx.get_hash(); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + let next_height = produce_blocks_from_height(env, blocks_number, height); + let final_outcome = env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap(); + assert_matches!(final_outcome.status, FinalExecutionStatus::SuccessValue(_)); + next_height +} + +fn view_request(env: &TestEnv, request: QueryRequest) -> QueryResponse { + let head = env.clients[0].chain.head().unwrap(); + let head_block = env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); + env.clients[0] + .runtime_adapter + .query( + ShardUId::single_shard(), + &head_block.chunks()[0].prev_state_root(), + head.height, + 0, + &head.prev_block_hash, + &head.last_block_hash, + head_block.header().epoch_id(), + &request, + ) + .unwrap() +} + +/// Tests that ETH-implicit account is created correctly, with Wallet Contract hash. +#[test] +fn test_eth_implicit_account_creation() { + if !checked_feature!("stable", EthImplicitAccounts, PROTOCOL_VERSION) { + return; + } + let genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let eth_implicit_account_id = eth_implicit_test_account(); + + // Make zero-transfer to ETH-implicit account, invoking its creation. + let transfer_tx = SignedTransaction::send_money( + 1, + signer.account_id.clone(), + eth_implicit_account_id.clone(), + &signer, + 0, + *genesis_block.hash(), + ); + assert_eq!(env.clients[0].process_tx(transfer_tx, false, false), ProcessTxResponse::ValidTx); + for i in 1..5 { + env.produce_block(0, i); + } + + let magic_bytes = wallet_contract_magic_bytes(); + + // Verify the ETH-implicit account has zero balance and appropriate code hash. + // Check that the account storage fits within zero balance account limit. + let request = QueryRequest::ViewAccount { account_id: eth_implicit_account_id.clone() }; + match view_request(&env, request).kind { + QueryResponseKind::ViewAccount(view) => { + assert_eq!(view.amount, 0); + assert_eq!(view.code_hash, *magic_bytes.hash()); + assert!(view.storage_usage <= ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT) + } + _ => panic!("wrong query response"), + } + + // Verify that contract code deployed to the ETH-implicit account is near[wallet contract hash]. + let request = QueryRequest::ViewCode { account_id: eth_implicit_account_id }; + match view_request(&env, request).kind { + QueryResponseKind::ViewCode(view) => { + let contract_code = ContractCode::new(view.code, None); + assert_eq!(contract_code.hash(), magic_bytes.hash()); + assert_eq!(contract_code.code(), magic_bytes.code()); + } + _ => panic!("wrong query response"), + } +} + +/// Test that transactions from ETH-implicit accounts are rejected. +#[test] +fn test_transaction_from_eth_implicit_account_fail() { + if !checked_feature!("stable", EthImplicitAccounts, PROTOCOL_VERSION) { + return; + } + let genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let deposit_for_account_creation = UNC_BASE; + let mut height = 1; + let blocks_number = 5; + let signer1 = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + + let secret_key = SecretKey::from_seed(KeyType::SECP256K1, "test"); + let public_key = secret_key.public_key(); + let eth_implicit_account_id = derive_eth_implicit_account_id(public_key.unwrap_as_secp256k1()); + let eth_implicit_account_signer = + InMemorySigner::from_secret_key(eth_implicit_account_id.clone(), secret_key); + + // Send money to ETH-implicit account, invoking its creation. + let send_money_tx = SignedTransaction::send_money( + 1, + "test1".parse().unwrap(), + eth_implicit_account_id.clone(), + &signer1, + deposit_for_account_creation, + *genesis_block.hash(), + ); + // Check for tx success status and get new block height. + height = check_tx_processing(&mut env, send_money_tx, height, blocks_number); + let block = env.clients[0].chain.get_block_by_height(height - 1).unwrap(); + + // Try to send money from ETH-implicit account using `(block_height - 1) * 1e6` as a nonce. + // That would be a good nonce for any access key, but the transaction should fail nonetheless because there is no access key. + let nonce = (height - 1) * AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER; + let send_money_from_eth_implicit_account_tx = SignedTransaction::send_money( + nonce, + eth_implicit_account_id.clone(), + "test0".parse().unwrap(), + ð_implicit_account_signer, + 100, + *block.hash(), + ); + let response = env.clients[0].process_tx(send_money_from_eth_implicit_account_tx, false, false); + let expected_tx_error = ProcessTxResponse::InvalidTx(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::AccessKeyNotFound { + account_id: eth_implicit_account_id.clone(), + public_key: public_key.clone().into(), + }, + )); + assert_eq!(response, expected_tx_error); + + // Try to delete ETH-implicit account. Should fail because there is no access key. + let delete_eth_implicit_account_tx = SignedTransaction::delete_account( + nonce, + eth_implicit_account_id.clone(), + eth_implicit_account_id.clone(), + "test0".parse().unwrap(), + ð_implicit_account_signer, + *block.hash(), + ); + let response = env.clients[0].process_tx(delete_eth_implicit_account_tx, false, false); + assert_eq!(response, expected_tx_error); + + // Try to add an access key to the ETH-implicit account. Should fail because there is no access key. + let add_access_key_to_eth_implicit_account_tx = SignedTransaction::from_actions( + nonce, + eth_implicit_account_id.clone(), + eth_implicit_account_id.clone(), + ð_implicit_account_signer, + vec![Action::AddKey(Box::new(AddKeyAction { + public_key, + access_key: AccessKey::full_access(), + }))], + *block.hash(), + ); + let response = + env.clients[0].process_tx(add_access_key_to_eth_implicit_account_tx, false, false); + assert_eq!(response, expected_tx_error); + + // Try to deploy the Wallet Contract again to the ETH-implicit account. Should fail because there is no access key. + let wallet_contract_code = wallet_contract().code().to_vec(); + let add_access_key_to_eth_implicit_account_tx = SignedTransaction::from_actions( + nonce, + eth_implicit_account_id.clone(), + eth_implicit_account_id, + ð_implicit_account_signer, + vec![Action::DeployContract(DeployContractAction { code: wallet_contract_code })], + *block.hash(), + ); + let response = + env.clients[0].process_tx(add_access_key_to_eth_implicit_account_tx, false, false); + assert_eq!(response, expected_tx_error); +} + +// TODO(eth-implicit) Remove this test and replace it with tests that directly call the `Wallet Contract` when it is ready. +/// Creating an ETH-implicit account with meta-transaction, then attempting to use it with another meta-transaction. +/// +/// The `create_account` parameter controls whether we create ETH-implicit account +/// before attempting to use it by making a function call. +/// Depending on `rlp_transaction` blob that is sent to the `Wallet Contract` +/// the transaction is either authorized or unauthorized. +/// The `authorized` parameter controls which case will be tested. +fn meta_tx_call_wallet_contract(create_account: bool, authorized: bool) { + if !checked_feature!("stable", EthImplicitAccounts, PROTOCOL_VERSION) { + return; + } + let genesis = Genesis::test(vec![alice_account(), bob_account(), carol_account()], 3); + let relayer = alice_account(); + let node = RuntimeNode::new_from_genesis(&relayer, genesis); + let sender = bob_account(); + + let secret_key = SecretKey::from_seed(KeyType::SECP256K1, "test"); + let public_key = secret_key.public_key(); + let eth_implicit_account = derive_eth_implicit_account_id(public_key.unwrap_as_secp256k1()); + let other_public_key = SecretKey::from_seed(KeyType::SECP256K1, "test2").public_key(); + + // Although ETH-implicit account can be zero-balance, we pick 1 here in order to make transfer later from this account. + let transfer_amount = 1u128; + let actions = vec![Action::Transfer(TransferAction { deposit: transfer_amount })]; + + if create_account { + // Create ETH-implicit account by funding it. + node.user() + .meta_tx(sender.clone(), eth_implicit_account.clone(), relayer.clone(), actions) + .unwrap() + .assert_success(); + } + + let target = carol_account(); + let initial_balance = node.view_balance(&target).expect("failed looking up balance"); + + // TODO(eth-implicit) Append appropriate values to the RLP stream when proper `Wallet Contract` is implemented. + let mut stream = RlpStream::new_list(3); + stream.append(&target.as_str()); + // The RLP trait `Encodable` is not implemented for `u128`. We must encode it as bytes. + // TODO(eth-implicit) Do not try to encode `u128` values directly, see https://github.com/utnet-org/utility/pull/10269#discussion_r1425585051. + stream.append(&transfer_amount.to_be_bytes().as_slice()); + if authorized { + stream.append(&public_key.key_data()); + } else { + stream.append(&other_public_key.key_data()); + } + let rlp_encoded_data = stream.out().to_vec(); + + let args = serde_json::json!({ + "target": target.to_string(), + "rlp_transaction": rlp_encoded_data, + }) + .to_string() + .into_bytes(); + + let actions = vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "execute_rlp".to_owned(), + args, + gas: 30_000_000_000_000, + deposit: 0, + }))]; + // Call Wallet Contract with JSON-encoded arguments: `target` and `rlp_transaction`. The `rlp_transaction`'s value is RLP-encoded. + let tx_result = + node.user().meta_tx(sender, eth_implicit_account.clone(), relayer, actions).unwrap(); + let wallet_contract_call_result = &tx_result.receipts_outcome[1].outcome.status; + + if create_account && authorized { + // If the public key recovered from the RLP transaction's signature is valid for this ETH-implicit account, + // the transaction will succeed. `target`'s balance will increase by `transfer_amount`. + tx_result.assert_success(); + let final_balance = node.view_balance(&target).expect("failed looking up balance"); + assert_eq!(final_balance, initial_balance + transfer_amount); + return; + } + + if create_account { + // The public key recovered from the RLP transaction's signature isn't valid for this ETH-implicit account. + // The Wallet Contract will reject this transaction. + let expected_error = unc_primitives::views::ExecutionStatusView::Failure( + TxExecutionError::ActionError( + ActionError { + index: Some(0), + kind: ActionErrorKind::FunctionCallError { + 0: FunctionCallError::ExecutionError( + "Smart contract panicked: Public key does not match the Wallet Contract address." + .to_string() + ) + } + } + ) + ); + assert_eq!(wallet_contract_call_result, &expected_error); + } else { + // The Wallet Contract function call is not executed because the account does not exist. + let expected_error = unc_primitives::views::ExecutionStatusView::Failure( + TxExecutionError::ActionError(ActionError { + index: Some(0), + kind: ActionErrorKind::AccountDoesNotExist { account_id: eth_implicit_account }, + }), + ); + assert_eq!(wallet_contract_call_result, &expected_error); + } +} + +/// Wallet Contract function call is rejected because the ETH-implicit account does not exist. +#[test] +fn meta_tx_call_wallet_contract_account_does_not_exist() { + meta_tx_call_wallet_contract(false, true); +} + +/// Wallet Contract function call fails because the provided public key does not match the ETH-implicit address. +#[test] +fn meta_tx_call_wallet_contract_unauthorized() { + meta_tx_call_wallet_contract(true, false); +} + +/// Wallet Contract function call is executed succesfully. +#[test] +fn meta_tx_call_wallet_contract_authorized() { + meta_tx_call_wallet_contract(true, true); +} diff --git a/integration-tests/src/tests/client/features/zero_balance_account.rs b/integration-tests/src/tests/client/features/zero_balance_account.rs new file mode 100644 index 000000000..d13e3c95a --- /dev/null +++ b/integration-tests/src/tests/client/features/zero_balance_account.rs @@ -0,0 +1,354 @@ +use assert_matches::assert_matches; + +use unc_chain::ChainGenesis; +use unc_chain_configs::Genesis; +use unc_client::adapter::ProcessTxResponse; +use unc_client::test_utils::TestEnv; +use unc_crypto::{InMemorySigner, KeyType, PublicKey}; +use unc_parameters::{ExtCostsConfig, RuntimeConfig, RuntimeConfigStore, StorageUsageConfig}; +use unc_primitives::account::id::AccountId; +use unc_primitives::account::{AccessKey, AccessKeyPermission, FunctionCallPermission}; +use unc_primitives::errors::{ActionError, ActionErrorKind, InvalidTxError, TxExecutionError}; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::transaction::Action::AddKey; +use unc_primitives::transaction::{Action, AddKeyAction, DeleteKeyAction, SignedTransaction}; +use unc_primitives::version::{ProtocolFeature, PROTOCOL_VERSION}; +use unc_primitives::views::{FinalExecutionStatus, QueryRequest, QueryResponseKind}; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; +use node_runtime::ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT; + +/// Assert that an account exists and has zero balance +fn assert_zero_balance_account(env: &TestEnv, account_id: &AccountId) { + let head = env.clients[0].chain.head().unwrap(); + let head_block = env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); + let response = env.clients[0] + .runtime_adapter + .query( + ShardUId::single_shard(), + &head_block.chunks()[0].prev_state_root(), + head.height, + 0, + &head.prev_block_hash, + &head.last_block_hash, + head_block.header().epoch_id(), + &QueryRequest::ViewAccount { account_id: account_id.clone() }, + ) + .unwrap(); + match response.kind { + QueryResponseKind::ViewAccount(view) => { + assert_eq!(view.amount, 0); + assert!(view.storage_usage <= ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT) + } + _ => panic!("wrong query response"), + } +} + +/// Test 2 things: 1) a valid zero balance account can be created and 2) a nonzero balance account +/// (one with a nontrivial contract deployed) cannot be created without maintaining an initial balance +#[test] +fn test_zero_balance_account_creation() { + let epoch_length = 1000; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = ProtocolFeature::ZeroBalanceAccount.protocol_version(); + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + + let new_account_id: AccountId = "hello.test0".parse().unwrap(); + let signer0 = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let new_signer = + InMemorySigner::from_seed(new_account_id.clone(), KeyType::ED25519, "hello.test0"); + + // create a valid zero balance account. Transaction should succeed + let create_account_tx = SignedTransaction::create_account( + 1, + signer0.account_id.clone(), + new_account_id.clone(), + 0, + new_signer.public_key.clone(), + &signer0, + *genesis_block.hash(), + ); + assert_eq!( + env.clients[0].process_tx(create_account_tx, false, false), + ProcessTxResponse::ValidTx + ); + for i in 1..5 { + env.produce_block(0, i); + } + // new account should have been created + assert_zero_balance_account(&mut env, &new_account_id); + + // create a zero balance account with contract deployed. The transaction should fail + let new_account_id: AccountId = "hell.test0".parse().unwrap(); + let contract = unc_test_contracts::sized_contract(ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT as usize); + let create_account_tx = SignedTransaction::create_contract( + 2, + signer0.account_id.clone(), + new_account_id, + contract.to_vec(), + 0, + new_signer.public_key, + &signer0, + *genesis_block.hash(), + ); + let tx_hash = create_account_tx.get_hash(); + assert_eq!( + env.clients[0].process_tx(create_account_tx, false, false), + ProcessTxResponse::ValidTx + ); + for i in 5..10 { + env.produce_block(0, i); + } + let outcome = env.clients[0].chain.get_final_transaction_result(&tx_hash).unwrap(); + assert_matches!( + outcome.status, + FinalExecutionStatus::Failure(TxExecutionError::ActionError(ActionError { + kind: ActionErrorKind::LackBalanceForState { .. }, + .. + })) + ); +} + +/// Test that if a zero balance account becomes a regular account (through adding more keys), +/// it has to pay for storage cost of the account structure and the keys that +/// it didn't have to pay while it was a zero balance account. +#[test] +fn test_zero_balance_account_add_key() { + let epoch_length = 1000; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = ProtocolFeature::ZeroBalanceAccount.protocol_version(); + // create free runtime config for transaction costs to make it easier to assert + // the exact amount of tokens on accounts + let mut runtime_config = RuntimeConfig::free(); + runtime_config.fees.storage_usage_config = StorageUsageConfig { + storage_amount_per_byte: 10u128.pow(19), + num_bytes_account: 100, + num_extra_bytes_record: 40, + }; + runtime_config.wasm_config.ext_costs = ExtCostsConfig::test(); + let runtime_config_store = RuntimeConfigStore::with_one_config(runtime_config); + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes_with_runtime_config_store(&genesis, vec![runtime_config_store]) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + + let new_account_id: AccountId = "hello.test0".parse().unwrap(); + let signer0 = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let new_signer = + InMemorySigner::from_seed(new_account_id.clone(), KeyType::ED25519, "hello.test0"); + + let amount = 10u128.pow(24); + let create_account_tx = SignedTransaction::create_account( + 1, + signer0.account_id.clone(), + new_account_id.clone(), + amount, + new_signer.public_key.clone(), + &signer0, + *genesis_block.hash(), + ); + assert_eq!( + env.clients[0].process_tx(create_account_tx, false, false), + ProcessTxResponse::ValidTx + ); + for i in 1..5 { + env.produce_block(0, i); + } + + // add four more full access keys and 2 more function call access keys + // so that the account is no longer a zero balance account + let mut actions = vec![]; + let mut keys = vec![]; + for i in 1..5 { + let new_key = PublicKey::from_seed(KeyType::ED25519, format!("{}", i).as_str()); + keys.push(new_key.clone()); + actions.push(AddKey(Box::new(AddKeyAction { + public_key: new_key, + access_key: AccessKey::full_access(), + }))); + } + for i in 0..2 { + let new_key = PublicKey::from_seed(KeyType::ED25519, format!("{}", i + 5).as_str()); + actions.push(AddKey(Box::new(AddKeyAction { + public_key: new_key, + access_key: AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(10u128.pow(12)), + receiver_id: "a".repeat(64), + method_names: vec![], + }), + }, + }))); + } + + let head = env.clients[0].chain.head().unwrap(); + let nonce = head.height * AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER + 1; + let add_key_tx = SignedTransaction::from_actions( + nonce, + new_account_id.clone(), + new_account_id.clone(), + &new_signer, + actions, + *genesis_block.hash(), + ); + assert_eq!(env.clients[0].process_tx(add_key_tx, false, false), ProcessTxResponse::ValidTx); + for i in 5..10 { + env.produce_block(0, i); + } + + // since the account is no longer zero balance account, it cannot transfer all its tokens out + // and must keep some amount for storage staking + let send_money_tx = SignedTransaction::send_money( + nonce + 10, + new_account_id.clone(), + signer0.account_id, + &new_signer, + amount, + *genesis_block.hash(), + ); + assert_matches!( + env.clients[0].process_tx(send_money_tx.clone(), false, false), + ProcessTxResponse::InvalidTx(InvalidTxError::LackBalanceForState { .. }) + ); + + let delete_key_tx = SignedTransaction::from_actions( + nonce + 1, + new_account_id.clone(), + new_account_id.clone(), + &new_signer, + vec![Action::DeleteKey(Box::new(DeleteKeyAction { + public_key: keys.last().unwrap().clone(), + }))], + *genesis_block.hash(), + ); + assert_eq!(env.clients[0].process_tx(delete_key_tx, false, false), ProcessTxResponse::ValidTx); + for i in 10..15 { + env.produce_block(0, i); + } + assert_eq!(env.clients[0].process_tx(send_money_tx, false, false), ProcessTxResponse::ValidTx); + for i in 15..20 { + env.produce_block(0, i); + } + assert_zero_balance_account(&mut env, &new_account_id); +} + +/// Test that zero balance accounts cannot be created before the upgrade but can succeed after +/// the protocol upgrade +#[test] +fn test_zero_balance_account_upgrade() { + // The immediate protocol upgrade needs to be set for this test to pass in + // the release branch where the protocol upgrade date is set. + std::env::set_var("unc_TESTS_IMMEDIATE_PROTOCOL_UPGRADE", "1"); + + let epoch_length = 5; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = ProtocolFeature::ZeroBalanceAccount.protocol_version() - 1; + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + + let new_account_id: AccountId = "hello.test0".parse().unwrap(); + let signer0 = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let new_signer = + InMemorySigner::from_seed(new_account_id.clone(), KeyType::ED25519, "hello.test0"); + + // before protocol upgrade, should not be possible to create a zero balance account + let first_create_account_tx = SignedTransaction::create_account( + 1, + signer0.account_id.clone(), + new_account_id.clone(), + 0, + new_signer.public_key.clone(), + &signer0, + *genesis_block.hash(), + ); + let first_tx_hash = first_create_account_tx.get_hash(); + assert_eq!( + env.clients[0].process_tx(first_create_account_tx, false, false), + ProcessTxResponse::ValidTx + ); + for i in 1..12 { + env.produce_block(0, i); + } + let outcome = env.clients[0].chain.get_final_transaction_result(&first_tx_hash).unwrap(); + assert_matches!( + outcome.status, + FinalExecutionStatus::Failure(TxExecutionError::ActionError(ActionError { + kind: ActionErrorKind::LackBalanceForState { .. }, + .. + })) + ); + + let second_create_account_tx = SignedTransaction::create_account( + 2, + signer0.account_id.clone(), + new_account_id, + 0, + new_signer.public_key, + &signer0, + *genesis_block.hash(), + ); + let second_tx_hash = second_create_account_tx.get_hash(); + assert_eq!( + env.clients[0].process_tx(second_create_account_tx, false, false), + ProcessTxResponse::ValidTx + ); + for i in 12..20 { + env.produce_block(0, i); + } + let outcome = env.clients[0].chain.get_final_transaction_result(&second_tx_hash).unwrap(); + assert_matches!(outcome.status, FinalExecutionStatus::SuccessValue(_)); +} + +#[test] +fn test_storage_usage_components() { + // confirm these numbers don't change, as the zero balance limit is derived from them + const PUBLIC_KEY_STORAGE_USAGE: usize = 33; + const FULL_ACCESS_PERMISSION_STORAGE_USAGE: usize = 9; + const FUNCTION_ACCESS_PERMISSION_STORAGE_USAGE: usize = 98; + + let edwards_public_key = PublicKey::from_seed(KeyType::ED25519, "seed"); + assert_eq!(PUBLIC_KEY_STORAGE_USAGE, borsh::object_length(&edwards_public_key).unwrap()); + + let full_access_key = AccessKey::full_access(); + assert_eq!( + FULL_ACCESS_PERMISSION_STORAGE_USAGE, + borsh::object_length(&full_access_key).unwrap() + ); + + let fn_access_key = AccessKey { + nonce: u64::MAX, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(u128::MAX), + receiver_id: "a".repeat(64), + method_names: vec![], + }), + }; + assert_eq!( + FUNCTION_ACCESS_PERMISSION_STORAGE_USAGE, + borsh::object_length(&fn_access_key).unwrap() + ); + + let config_store = RuntimeConfigStore::new(None); + let config = config_store.get_config(PROTOCOL_VERSION); + let account_overhead = config.fees.storage_usage_config.num_bytes_account as usize; + let record_overhead = config.fees.storage_usage_config.num_extra_bytes_record as usize; + // The NEP proposes to fit 4 full access keys + 2 fn access keys in an zero balance account + let full_access = + PUBLIC_KEY_STORAGE_USAGE + FULL_ACCESS_PERMISSION_STORAGE_USAGE + record_overhead; + let fn_access = + PUBLIC_KEY_STORAGE_USAGE + FUNCTION_ACCESS_PERMISSION_STORAGE_USAGE + record_overhead; + let total = account_overhead + 4 * full_access + 2 * fn_access; + assert_eq!(total as u64, ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT); +} diff --git a/integration-tests/src/tests/client/flat_storage.rs b/integration-tests/src/tests/client/flat_storage.rs new file mode 100644 index 000000000..8edc02a62 --- /dev/null +++ b/integration-tests/src/tests/client/flat_storage.rs @@ -0,0 +1,574 @@ +/// Tests which check correctness of background flat storage creation. +use assert_matches::assert_matches; +use unc_chain::{ChainGenesis, Provenance}; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_client::ProcessTxResponse; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::errors::StorageError; +use unc_primitives::shard_layout::{ShardLayout, ShardUId}; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::AccountId; +use unc_primitives_core::types::BlockHeight; +use unc_store::flat::{ + store_helper, FetchingStateStatus, FlatStorageCreationStatus, FlatStorageManager, + FlatStorageReadyStatus, FlatStorageStatus, NUM_PARTS_IN_ONE_STEP, +}; +use unc_store::test_utils::create_test_store; +use unc_store::{KeyLookupMode, Store, TrieTraversalItem}; +use unc_vm_runner::logic::TrieNodesCount; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; +use std::str::FromStr; +use std::thread; +use std::time::Duration; + +/// Height on which we start flat storage background creation. +const START_HEIGHT: BlockHeight = 7; + +/// Number of steps which should be enough to create flat storage. +const CREATION_TIMEOUT: BlockHeight = 30; + +/// Setup environment with one unc client for testing. +fn setup_env(genesis: &Genesis, store: Store) -> TestEnv { + let chain_genesis = ChainGenesis::new(genesis); + TestEnv::builder(chain_genesis) + .stores(vec![store]) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(genesis) + .build() +} + +/// Waits for flat storage creation on given shard for `CREATION_TIMEOUT` blocks. +/// We have a pause after processing each block because state data is being fetched in rayon threads, +/// but we expect it to finish in <30s because state is small and there is only one state part. +/// Returns next block height available to produce. +fn wait_for_flat_storage_creation( + env: &mut TestEnv, + start_height: BlockHeight, + shard_uid: ShardUId, + produce_blocks: bool, +) -> BlockHeight { + let store = env.clients[0].runtime_adapter.store().clone(); + let mut next_height = start_height; + let mut prev_status = store_helper::get_flat_storage_status(&store, shard_uid).unwrap(); + while next_height < start_height + CREATION_TIMEOUT { + if produce_blocks { + env.produce_block(0, next_height); + } + env.clients[0].run_flat_storage_creation_step().unwrap(); + + let status = store_helper::get_flat_storage_status(&store, shard_uid).unwrap(); + // Check validity of state transition for flat storage creation. + match &prev_status { + FlatStorageStatus::Empty => assert_matches!( + status, + FlatStorageStatus::Creation(FlatStorageCreationStatus::SavingDeltas) + ), + FlatStorageStatus::Creation(FlatStorageCreationStatus::SavingDeltas) => { + assert_matches!( + status, + FlatStorageStatus::Creation(FlatStorageCreationStatus::SavingDeltas) + | FlatStorageStatus::Creation(FlatStorageCreationStatus::FetchingState(_)) + ) + } + FlatStorageStatus::Creation(FlatStorageCreationStatus::FetchingState(_)) => { + assert_matches!( + status, + FlatStorageStatus::Creation(FlatStorageCreationStatus::FetchingState(_)) + | FlatStorageStatus::Creation(FlatStorageCreationStatus::CatchingUp(_)) + ) + } + FlatStorageStatus::Creation(FlatStorageCreationStatus::CatchingUp(_)) => { + assert_matches!( + status, + FlatStorageStatus::Creation(FlatStorageCreationStatus::CatchingUp(_)) + | FlatStorageStatus::Ready(_) + ) + } + _ => { + panic!("Invalid status {prev_status:?} observed during flat storage creation for height {next_height}"); + } + } + tracing::info!("Flat Creation status: {:?}", status); + + prev_status = status; + next_height += 1; + if matches!(prev_status, FlatStorageStatus::Ready(_)) { + break; + } + + thread::sleep(Duration::from_secs(1)); + } + let flat_storage_manager = get_flat_storage_manager(&env); + let status = flat_storage_manager.get_flat_storage_status(shard_uid); + assert_matches!( + status, + FlatStorageStatus::Ready(_), + "Client couldn't create flat storage until block {next_height}, status: {status:?}" + ); + assert!(flat_storage_manager.get_flat_storage_for_shard(shard_uid).is_some()); + + // We don't expect any forks in the chain after flat storage head, so the number of + // deltas stored on DB should be exactly 2, as there are only 2 blocks after + // the final block. + let deltas_in_metadata = + store_helper::get_all_deltas_metadata(&store, shard_uid).unwrap().len() as u64; + assert_eq!(deltas_in_metadata, 2); + + next_height +} + +/// Check correctness of flat storage creation. +#[test] +fn test_flat_storage_creation_sanity() { + init_test_logger(); + let genesis = Genesis::test(vec!["test0".parse().unwrap()], 1); + let shard_uid = genesis.config.shard_layout.shard_uids().next().unwrap(); + let store = create_test_store(); + + // Process some blocks with flat storage. Then remove flat storage data from disk. + { + let mut env = setup_env(&genesis, store.clone()); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + for height in 1..START_HEIGHT { + env.produce_block(0, height); + + let tx = SignedTransaction::send_money( + height, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + 1, + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + // If chain was initialized from scratch, flat storage state should be created. During block processing, flat + // storage head should be moved to block `START_HEIGHT - 4`. + let flat_head_height = START_HEIGHT - 4; + let expected_flat_storage_head = + env.clients[0].chain.get_block_hash_by_height(flat_head_height).unwrap(); + let status = store_helper::get_flat_storage_status(&store, shard_uid); + if let Ok(FlatStorageStatus::Ready(FlatStorageReadyStatus { flat_head })) = status { + assert_eq!(flat_head.hash, expected_flat_storage_head); + assert_eq!(flat_head.height, flat_head_height); + } else { + panic!("expected FlatStorageStatus::Ready status, got {status:?}"); + } + + // Deltas for blocks until `flat_head_height` should not exist. + for height in 0..=flat_head_height { + let block_hash = env.clients[0].chain.get_block_hash_by_height(height).unwrap(); + assert_eq!(store_helper::get_delta_changes(&store, shard_uid, block_hash), Ok(None)); + } + // Deltas for blocks until `START_HEIGHT` should still exist, + // because they come after flat storage head. + for height in flat_head_height + 1..START_HEIGHT { + let block_hash = env.clients[0].chain.get_block_hash_by_height(height).unwrap(); + assert_matches!( + store_helper::get_delta_changes(&store, shard_uid, block_hash), + Ok(Some(_)), + "height: {height}" + ); + } + + let mut store_update = store.store_update(); + get_flat_storage_manager(&env) + .remove_flat_storage_for_shard(shard_uid, &mut store_update) + .unwrap(); + store_update.commit().unwrap(); + } + + // Create new chain and runtime using the same store. It should produce next blocks normally, but now it should + // think that flat storage does not exist and background creation should be initiated. + let mut env = setup_env(&genesis, store.clone()); + for height in START_HEIGHT..START_HEIGHT + 2 { + env.produce_block(0, height); + } + assert!(get_flat_storage_manager(&env).get_flat_storage_for_shard(shard_uid).is_none()); + + assert_eq!( + store_helper::get_flat_storage_status(&store, shard_uid), + Ok(FlatStorageStatus::Empty) + ); + assert!(!env.clients[0].run_flat_storage_creation_step().unwrap()); + // At first, flat storage state should start saving deltas. Deltas for all newly processed blocks should be saved to + // disk. + assert_eq!( + store_helper::get_flat_storage_status(&store, shard_uid), + Ok(FlatStorageStatus::Creation(FlatStorageCreationStatus::SavingDeltas)) + ); + // Introduce fork block to check that deltas for it will be GC-d later. + let fork_block = env.clients[0].produce_block(START_HEIGHT + 2).unwrap().unwrap(); + let fork_block_hash = *fork_block.hash(); + let next_block = env.clients[0].produce_block(START_HEIGHT + 3).unwrap().unwrap(); + let next_block_hash = *next_block.hash(); + env.process_block(0, fork_block, Provenance::PRODUCED); + env.process_block(0, next_block, Provenance::PRODUCED); + + assert_matches!( + store_helper::get_delta_changes(&store, shard_uid, fork_block_hash), + Ok(Some(_)) + ); + assert_matches!( + store_helper::get_delta_changes(&store, shard_uid, next_block_hash), + Ok(Some(_)) + ); + + // Produce new block and run flat storage creation step. + // We started the node from height `START_HEIGHT - 1`, and now final head should move to height `START_HEIGHT`. + // Because final head height became greater than height on which node started, + // we must start fetching the state. + env.produce_block(0, START_HEIGHT + 4); + assert!(!env.clients[0].run_flat_storage_creation_step().unwrap()); + let final_block_hash = env.clients[0].chain.get_block_hash_by_height(START_HEIGHT).unwrap(); + assert_eq!( + store_helper::get_flat_storage_status(&store, shard_uid), + Ok(FlatStorageStatus::Creation(FlatStorageCreationStatus::FetchingState( + FetchingStateStatus { + block_hash: final_block_hash, + part_id: 0, + num_parts_in_step: NUM_PARTS_IN_ONE_STEP, + num_parts: 1, + } + ))) + ); + + wait_for_flat_storage_creation(&mut env, START_HEIGHT + 5, shard_uid, true); +} + +/// Check that client can create flat storage on some shard while it already exists on another shard. +#[test] +fn test_flat_storage_creation_two_shards() { + init_test_logger(); + let num_shards = 2; + let genesis = + Genesis::test_sharded_new_version(vec!["test0".parse().unwrap()], 1, vec![1; num_shards]); + let shard_uids: Vec<_> = genesis.config.shard_layout.shard_uids().collect(); + let store = create_test_store(); + + // Process some blocks with flat storages for two shards. Then remove flat storage data from disk for shard 0. + { + let mut env = setup_env(&genesis, store.clone()); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + for height in 1..START_HEIGHT { + env.produce_block(0, height); + + let tx = SignedTransaction::send_money( + height, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + 1, + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + for &shard_uid in shard_uids.iter() { + assert_matches!( + store_helper::get_flat_storage_status(&store, shard_uid), + Ok(FlatStorageStatus::Ready(_)) + ); + } + + let mut store_update = store.store_update(); + get_flat_storage_manager(&env) + .remove_flat_storage_for_shard(shard_uids[0], &mut store_update) + .unwrap(); + store_update.commit().unwrap(); + } + + // Check that flat storage is not ready for shard 0 but ready for shard 1. + let mut env = setup_env(&genesis, store.clone()); + assert!(get_flat_storage_manager(&env).get_flat_storage_for_shard(shard_uids[0]).is_none()); + assert_matches!( + store_helper::get_flat_storage_status(&store, shard_uids[0]), + Ok(FlatStorageStatus::Empty) + ); + assert!(get_flat_storage_manager(&env).get_flat_storage_for_shard(shard_uids[1]).is_some()); + assert_matches!( + store_helper::get_flat_storage_status(&store, shard_uids[1]), + Ok(FlatStorageStatus::Ready(_)) + ); + + wait_for_flat_storage_creation(&mut env, START_HEIGHT, shard_uids[0], true); +} + +/// Check that flat storage creation can be started from intermediate state where one +/// of state parts is already fetched. +#[test] +fn test_flat_storage_creation_start_from_state_part() { + init_test_logger(); + // Create several accounts to ensure that state is non-trivial. + let accounts = + (0..4).map(|i| AccountId::from_str(&format!("test{}", i)).unwrap()).collect::>(); + let genesis = Genesis::test(accounts, 1); + let shard_uid = genesis.config.shard_layout.shard_uids().next().unwrap(); + let store = create_test_store(); + + // Process some blocks with flat storage. + // Reshard into two parts and return trie keys corresponding to each part. + const NUM_PARTS: u64 = 2; + let trie_keys: Vec<_> = { + let mut env = setup_env(&genesis, store.clone()); + for height in 1..START_HEIGHT { + env.produce_block(0, height); + } + + assert_matches!( + store_helper::get_flat_storage_status(&store, shard_uid), + Ok(FlatStorageStatus::Ready(_)) + ); + + let block_hash = env.clients[0].chain.get_block_hash_by_height(START_HEIGHT - 1).unwrap(); + let state_root = + *env.clients[0].chain.get_chunk_extra(&block_hash, &shard_uid).unwrap().state_root(); + let trie = env.clients[0] + .chain + .runtime_adapter + .get_trie_for_shard(0, &block_hash, state_root, true) + .unwrap(); + (0..NUM_PARTS) + .map(|part_id| { + let path_begin = trie.find_state_part_boundary(part_id, NUM_PARTS).unwrap(); + let path_end = trie.find_state_part_boundary(part_id + 1, NUM_PARTS).unwrap(); + let mut trie_iter = trie.iter().unwrap(); + let mut keys = vec![]; + for item in trie_iter.visit_nodes_interval(&path_begin, &path_end).unwrap() { + if let TrieTraversalItem { key: Some(trie_key), .. } = item { + keys.push(trie_key); + } + } + keys + }) + .collect() + }; + assert!(!trie_keys[0].is_empty()); + assert!(!trie_keys[1].is_empty()); + + { + // Remove keys of part 1 from the flat state. + // Manually set flat storage creation status to the step when it should start from fetching part 1. + let status = store_helper::get_flat_storage_status(&store, shard_uid); + let flat_head = if let Ok(FlatStorageStatus::Ready(ready_status)) = status { + ready_status.flat_head.hash + } else { + panic!("expected FlatStorageStatus::Ready, got: {status:?}"); + }; + let mut store_update = store.store_update(); + for key in trie_keys[1].iter() { + store_helper::set_flat_state_value(&mut store_update, shard_uid, key.clone(), None); + } + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Creation(FlatStorageCreationStatus::FetchingState( + FetchingStateStatus { + block_hash: flat_head, + part_id: 1, + num_parts_in_step: 1, + num_parts: NUM_PARTS, + }, + )), + ); + store_update.commit().unwrap(); + + // Re-create runtime, check that flat storage is not created yet. + let mut env = setup_env(&genesis, store); + assert!(get_flat_storage_manager(&env).get_flat_storage_for_shard(shard_uid).is_none()); + + // Run chain for a couple of blocks and check that flat storage for shard 0 is eventually created. + let next_height = wait_for_flat_storage_creation(&mut env, START_HEIGHT, shard_uid, true); + + // Check that all the keys are present in flat storage. + let block_hash = env.clients[0].chain.get_block_hash_by_height(next_height - 1).unwrap(); + let state_root = + *env.clients[0].chain.get_chunk_extra(&block_hash, &shard_uid).unwrap().state_root(); + let trie = env.clients[0] + .chain + .runtime_adapter + .get_trie_for_shard(0, &block_hash, state_root, true) + .unwrap(); + for part_trie_keys in trie_keys.iter() { + for trie_key in part_trie_keys.iter() { + assert_matches!( + trie.get_optimized_ref(&trie_key, KeyLookupMode::FlatStorage), + Ok(Some(_)) + ); + } + } + assert_eq!(trie.get_trie_nodes_count(), TrieNodesCount { db_reads: 0, mem_reads: 0 }); + } +} + +/// Tests the scenario where we start flat storage migration, and get just a few new blocks. +/// (in this test we still generate 3 blocks in order to generate deltas). +#[test] +fn test_catchup_succeeds_even_if_no_new_blocks() { + init_test_logger(); + let genesis = Genesis::test(vec!["test0".parse().unwrap()], 1); + let store = create_test_store(); + let shard_uid = ShardLayout::v0_single_shard().shard_uids().next().unwrap(); + + // Process some blocks with flat storage. Then remove flat storage data from disk. + { + let mut env = setup_env(&genesis, store.clone()); + for height in 1..START_HEIGHT { + env.produce_block(0, height); + } + // Remove flat storage. + let mut store_update = store.store_update(); + get_flat_storage_manager(&env) + .remove_flat_storage_for_shard(shard_uid, &mut store_update) + .unwrap(); + store_update.commit().unwrap(); + } + let mut env = setup_env(&genesis, store.clone()); + assert!(get_flat_storage_manager(&env).get_flat_storage_for_shard(shard_uid).is_none()); + assert_eq!( + store_helper::get_flat_storage_status(&store, shard_uid), + Ok(FlatStorageStatus::Empty) + ); + // Create 3 more blocks (so that the deltas are generated) - and assume that no new blocks are received. + // In the future, we should also support the scenario where no new blocks are created. + + for block_height in START_HEIGHT + 1..=START_HEIGHT + 3 { + env.produce_block(0, block_height); + } + + assert!(!env.clients[0].run_flat_storage_creation_step().unwrap()); + wait_for_flat_storage_creation(&mut env, START_HEIGHT + 3, shard_uid, false); +} + +/// Tests the flat storage iterator. Running on a chain with 3 shards, and couple blocks produced. +#[test] +fn test_flat_storage_iter() { + init_test_logger(); + let num_shards = 3; + let shard_layout = + ShardLayout::v1(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], None, 0); + + let genesis = Genesis::test_with_seeds( + vec!["test0".parse().unwrap()], + 1, + vec![1; num_shards], + shard_layout.clone(), + ); + + let store = create_test_store(); + + let mut env = setup_env(&genesis, store.clone()); + for height in 1..START_HEIGHT { + env.produce_block(0, height); + } + + for shard_id in 0..3 { + let shard_uid = ShardUId::from_shard_id_and_layout(shard_id, &shard_layout); + let items: Vec<_> = + store_helper::iter_flat_state_entries(shard_uid, &store, None, None).collect(); + + match shard_id { + 0 => { + assert_eq!(2, items.len()); + // Two entries - one for 'near' system account, the other for the contract. + assert_eq!( + TrieKey::Account { account_id: "near".parse().unwrap() }.to_vec(), + items[0].as_ref().unwrap().0.to_vec() + ); + } + 1 => { + // Two entries - one for account, the other for contract. + assert_eq!(2, items.len()); + assert_eq!( + TrieKey::Account { account_id: "test0".parse().unwrap() }.to_vec(), + items[0].as_ref().unwrap().0.to_vec() + ); + } + 2 => { + // Test1 account was not created yet - so no entries. + assert_eq!(0, items.len()); + } + _ => { + panic!("Unexpected shard_id"); + } + } + } +} + +#[test] +/// Initializes flat storage, then creates a Trie to read the flat storage +/// exactly at the flat head block. +/// Add another block to the flat state, which moves flat head and makes the +/// state of the previous flat head inaccessible. +fn test_not_supported_block() { + init_test_logger(); + let genesis = Genesis::test(vec!["test0".parse().unwrap()], 1); + let shard_layout = ShardLayout::v0_single_shard(); + let shard_uid = shard_layout.shard_uids().next().unwrap(); + let store = create_test_store(); + + let mut env = setup_env(&genesis, store); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + + // Produce blocks up to `START_HEIGHT`. + for height in 1..START_HEIGHT { + env.produce_block(0, height); + let tx = SignedTransaction::send_money( + height, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + 1, + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + let flat_head_height = START_HEIGHT - 4; + // Trie key which must exist in the storage. + let trie_key_bytes = + unc_primitives::trie_key::TrieKey::Account { account_id: "test0".parse().unwrap() } + .to_vec(); + // Create trie, which includes creating chunk view, and get `ValueRef`s + // for post state roots for blocks `START_HEIGHT - 3` and `START_HEIGHT - 2`. + // After creating the first trie, produce block `START_HEIGHT` which moves flat storage + // head 1 block further and invalidates it. + let mut get_ref_results = vec![]; + for height in flat_head_height..START_HEIGHT - 1 { + let block_hash = env.clients[0].chain.get_block_hash_by_height(height).unwrap(); + let state_root = *env.clients[0] + .chain + .get_chunk_extra(&block_hash, &ShardUId::from_shard_id_and_layout(0, &shard_layout)) + .unwrap() + .state_root(); + + let trie = env.clients[0] + .runtime_adapter + .get_trie_for_shard(shard_uid.shard_id(), &block_hash, state_root, true) + .unwrap(); + if height == flat_head_height { + env.produce_block(0, START_HEIGHT); + } + get_ref_results.push(trie.get_optimized_ref(&trie_key_bytes, KeyLookupMode::FlatStorage)); + } + + // The first result should be FlatStorageError, because we can't read from first chunk view anymore. + // But the node must not panic as this is normal behaviour. + // Ideally it should be tested on chain level, but there is no easy way to + // postpone applying chunks reliably. + assert_matches!(get_ref_results[0], Err(StorageError::FlatStorageBlockNotSupported(_))); + // For the second result chunk view is valid, so result is Ok. + assert_matches!(get_ref_results[1], Ok(Some(_))); +} + +fn get_flat_storage_manager(env: &TestEnv) -> FlatStorageManager { + env.clients[0].chain.runtime_adapter.get_flat_storage_manager() +} diff --git a/integration-tests/src/tests/client/mod.rs b/integration-tests/src/tests/client/mod.rs new file mode 100644 index 000000000..fc3f454cb --- /dev/null +++ b/integration-tests/src/tests/client/mod.rs @@ -0,0 +1,18 @@ +mod benchmarks; +mod block_corruption; +mod challenges; +mod chunks_management; +mod cold_storage; +#[cfg(feature = "new_epoch_sync")] +mod epoch_sync; +mod features; +mod flat_storage; +mod process_blocks; +mod resharding; +mod runtimes; +#[cfg(feature = "sandbox")] +mod sandbox; +mod state_dump; +mod state_snapshot; +mod sync_state_nodes; +mod undo_block; diff --git a/integration-tests/src/tests/client/process_blocks.rs b/integration-tests/src/tests/client/process_blocks.rs new file mode 100644 index 000000000..55d0a1d82 --- /dev/null +++ b/integration-tests/src/tests/client/process_blocks.rs @@ -0,0 +1,3776 @@ +use std::collections::{HashSet, VecDeque}; +use std::str::FromStr; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, RwLock}; + +use actix::System; +use assert_matches::assert_matches; +use futures::{future, FutureExt}; +use unc_actix_test_utils::run_actix; +use unc_async::messaging::IntoSender; +use unc_chain::chain::ApplyStatePartsRequest; +use unc_chain::test_utils::ValidatorSchedule; +use unc_chain::types::{LatestKnown, RuntimeAdapter}; +#[cfg(not(feature = "nightly"))] +use unc_chain::validate::validate_chunk_with_chunk_extra; +#[cfg(not(feature = "nightly"))] +use unc_chain::ChainStore; +use unc_chain::{ + Block, BlockProcessingArtifact, ChainGenesis, ChainStoreAccess, Error, Provenance, +}; +use unc_chain_configs::{Genesis, DEFAULT_GC_NUM_EPOCHS_TO_KEEP}; +use unc_chunks::test_utils::MockClientAdapterForShardsManager; +use unc_client::test_utils::{ + create_chunk_on_height, setup_client_with_synchronous_shards_manager, setup_mock, + setup_mock_all_validators, TestEnv, +}; +use unc_client::{ + BlockApproval, BlockResponse, Client, GetBlockWithMerkleTree, ProcessTxResponse, SetNetworkInfo, +}; +use unc_crypto::{InMemorySigner, KeyType, PublicKey, Signature, Signer}; +use unc_network::test_utils::{wait_or_panic, MockPeerManagerAdapter}; +use unc_network::types::{ + BlockInfo, ConnectedPeerInfo, HighestHeightPeerInfo, NetworkInfo, PeerChainInfo, + PeerManagerMessageRequest, PeerManagerMessageResponse, PeerType, +}; +use unc_network::types::{FullPeerInfo, NetworkRequests, NetworkResponses}; +use unc_network::types::{PeerInfo, ReasonForBan}; +use unc_o11y::testonly::{init_integration_logger, init_test_logger}; +use unc_o11y::WithSpanContextExt; +use unc_parameters::{ActionCosts, ExtCosts}; +use unc_parameters::{RuntimeConfig, RuntimeConfigStore}; +use unc_primitives::block::Approval; +use unc_primitives::block_header::BlockHeader; +use unc_primitives::epoch_manager::RngSeed; +use unc_primitives::errors::TxExecutionError; +use unc_primitives::errors::{ActionError, ActionErrorKind, InvalidTxError}; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::merkle::{verify_hash, PartialMerkleTree}; +use unc_primitives::receipt::DelayedReceiptIndices; +use unc_primitives::shard_layout::{get_block_shard_uid, ShardUId}; +use unc_primitives::sharding::{ShardChunkHeader, ShardChunkHeaderInner, ShardChunkHeaderV3}; +use unc_primitives::state_part::PartId; +use unc_primitives::state_sync::StatePartKey; +use unc_primitives::test_utils::create_test_signer; +use unc_primitives::test_utils::TestBlockBuilder; +use unc_primitives::transaction::{ + Action, DeployContractAction, ExecutionStatus, FunctionCallAction, SignedTransaction, + Transaction, +}; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::validator_stake::ValidatorStake; +use unc_primitives::types::{AccountId, BlockHeight, EpochId, NumBlocks, ProtocolVersion}; +use unc_primitives::utils::to_timestamp; +use unc_primitives::validator_signer::ValidatorSigner; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_primitives::views::{ + BlockHeaderView, FinalExecutionStatus, QueryRequest, QueryResponseKind, +}; +use unc_primitives_core::num_rational::{Ratio, Rational32}; +use unc_primitives_core::types::ShardId; +use unc_store::cold_storage::{update_cold_db, update_cold_head}; +use unc_store::metadata::DbKind; +use unc_store::metadata::DB_VERSION; +use unc_store::test_utils::create_test_node_storage_with_cold; +use unc_store::test_utils::create_test_store; +use unc_store::NodeStorage; +use unc_store::{get, DBCol, TrieChanges}; +use framework::config::{GenesisExt, TESTING_INIT_BALANCE, TESTING_INIT_STAKE}; +use framework::test_utils::TestEnvNightshadeSetupExt; +use framework::UNC_BASE; +use rand::prelude::StdRng; +use rand::{Rng, SeedableRng}; + +pub fn set_block_protocol_version( + block: &mut Block, + block_producer: AccountId, + protocol_version: ProtocolVersion, +) { + let validator_signer = create_test_signer(block_producer.as_str()); + + block.mut_header().set_latest_protocol_version(protocol_version); + block.mut_header().resign(&validator_signer); +} + +/// Produce `blocks_number` block in the given environment, starting from the given height. +/// Returns the first unoccupied height in the chain after this operation. +pub(crate) fn produce_blocks_from_height_with_protocol_version( + env: &mut TestEnv, + blocks_number: u64, + height: BlockHeight, + protocol_version: ProtocolVersion, +) -> BlockHeight { + let next_height = height + blocks_number; + for i in height..next_height { + let mut block = env.clients[0].produce_block(i).unwrap().unwrap(); + block.mut_header().set_latest_protocol_version(protocol_version); + env.process_block(0, block.clone(), Provenance::PRODUCED); + for j in 1..env.clients.len() { + env.process_block(j, block.clone(), Provenance::NONE); + } + } + next_height +} + +pub(crate) fn produce_blocks_from_height( + env: &mut TestEnv, + blocks_number: u64, + height: BlockHeight, +) -> BlockHeight { + produce_blocks_from_height_with_protocol_version(env, blocks_number, height, PROTOCOL_VERSION) +} + +pub(crate) fn create_account( + env: &mut TestEnv, + old_account_id: AccountId, + new_account_id: AccountId, + epoch_length: u64, + height: BlockHeight, + protocol_version: ProtocolVersion, +) -> CryptoHash { + let block = env.clients[0].chain.get_block_by_height(height - 1).unwrap(); + let signer = InMemorySigner::from_seed( + old_account_id.clone(), + KeyType::ED25519, + old_account_id.as_ref(), + ); + + let tx = SignedTransaction::create_account( + height, + old_account_id, + new_account_id, + 10u128.pow(24), + signer.public_key(), + &signer, + *block.hash(), + ); + let tx_hash = tx.get_hash(); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + produce_blocks_from_height_with_protocol_version(env, epoch_length, height, protocol_version); + tx_hash +} + +pub(crate) fn deploy_test_contract_with_protocol_version( + env: &mut TestEnv, + account_id: AccountId, + wasm_code: &[u8], + epoch_length: u64, + height: BlockHeight, + protocol_version: ProtocolVersion, +) -> BlockHeight { + let block = env.clients[0].chain.get_block_by_height(height - 1).unwrap(); + let signer = + InMemorySigner::from_seed(account_id.clone(), KeyType::ED25519, account_id.as_ref()); + + let tx = SignedTransaction::from_actions( + height, + account_id.clone(), + account_id, + &signer, + vec![Action::DeployContract(DeployContractAction { code: wasm_code.to_vec() })], + *block.hash(), + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + produce_blocks_from_height_with_protocol_version(env, epoch_length, height, protocol_version) +} + +pub(crate) fn deploy_test_contract( + env: &mut TestEnv, + account_id: AccountId, + wasm_code: &[u8], + epoch_length: u64, + height: BlockHeight, +) -> BlockHeight { + deploy_test_contract_with_protocol_version( + env, + account_id, + wasm_code, + epoch_length, + height, + PROTOCOL_VERSION, + ) +} + +/// Create environment and set of transactions which cause congestion on the chain. +pub(crate) fn prepare_env_with_congestion( + protocol_version: ProtocolVersion, + gas_price_adjustment_rate: Option, + number_of_transactions: u64, +) -> (TestEnv, Vec) { + init_test_logger(); + let epoch_length = 100; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.protocol_version = protocol_version; + genesis.config.epoch_length = epoch_length; + genesis.config.gas_limit = 10_000_000_000_000; + if let Some(gas_price_adjustment_rate) = gas_price_adjustment_rate { + genesis.config.gas_price_adjustment_rate = gas_price_adjustment_rate; + } + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + + // Deploy contract to test0. + let tx = SignedTransaction::from_actions( + 1, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + vec![Action::DeployContract(DeployContractAction { + code: unc_test_contracts::backwards_compatible_rs_contract().to_vec(), + })], + *genesis_block.hash(), + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + for i in 1..3 { + env.produce_block(0, i); + } + + // Create function call transactions that generate promises. + let gas_1 = 9_000_000_000_000; + let gas_2 = gas_1 / 3; + let mut tx_hashes = vec![]; + + for i in 0..number_of_transactions { + let data = serde_json::json!([ + {"create": { + "account_id": "test0", + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": gas_2, + }, "id": 0 } + ]); + + let signed_transaction = SignedTransaction::from_actions( + i + 10, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "call_promise".to_string(), + args: serde_json::to_vec(&data).unwrap(), + gas: gas_1, + deposit: 0, + }))], + *genesis_block.hash(), + ); + tx_hashes.push(signed_transaction.get_hash()); + assert_eq!( + env.clients[0].process_tx(signed_transaction, false, false), + ProcessTxResponse::ValidTx + ); + } + + (env, tx_hashes) +} + +/// Runs block producing client and stops after network mock received two blocks. +#[test] +fn produce_two_blocks() { + init_test_logger(); + run_actix(async { + let count = Arc::new(AtomicUsize::new(0)); + setup_mock( + vec!["test".parse().unwrap()], + "test".parse().unwrap(), + true, + false, + Box::new(move |msg, _ctx, _| { + if let NetworkRequests::Block { .. } = msg.as_network_requests_ref() { + count.fetch_add(1, Ordering::Relaxed); + if count.load(Ordering::Relaxed) >= 2 { + System::current().stop(); + } + } + PeerManagerMessageResponse::NetworkResponses(NetworkResponses::NoResponse) + }), + ); + unc_network::test_utils::wait_or_panic(5000); + }); +} + +/// Runs client that receives a block from network and announces header to the network with approval. +/// Need 3 block producers, to receive approval. +#[test] +fn receive_network_block() { + init_test_logger(); + run_actix(async { + // The first header announce will be when the block is received. We don't immediately endorse + // it. The second header announce will happen with the endorsement a little later. + let first_header_announce = Arc::new(RwLock::new(true)); + let actor_handles = setup_mock( + vec!["test2".parse().unwrap(), "test1".parse().unwrap(), "test3".parse().unwrap()], + "test2".parse().unwrap(), + true, + false, + Box::new(move |msg, _ctx, _| { + if let NetworkRequests::Approval { .. } = msg.as_network_requests_ref() { + let mut first_header_announce = first_header_announce.write().unwrap(); + if *first_header_announce { + *first_header_announce = false; + } else { + System::current().stop(); + } + } + PeerManagerMessageResponse::NetworkResponses(NetworkResponses::NoResponse) + }), + ); + let actor = actor_handles + .view_client_actor + .send(GetBlockWithMerkleTree::latest().with_span_context()); + let actor = actor.then(move |res| { + let (last_block, block_merkle_tree) = res.unwrap().unwrap(); + let mut block_merkle_tree = PartialMerkleTree::clone(&block_merkle_tree); + block_merkle_tree.insert(last_block.header.hash); + let signer = create_test_signer("test1"); + let next_block_ordinal = last_block.header.block_ordinal.unwrap() + 1; + let block = Block::produce( + PROTOCOL_VERSION, + PROTOCOL_VERSION, + &last_block.header.clone().into(), + last_block.header.height + 1, + next_block_ordinal, + last_block.chunks.into_iter().map(Into::into).collect(), + EpochId::default(), + if last_block.header.prev_hash == CryptoHash::default() { + EpochId(last_block.header.hash) + } else { + EpochId(last_block.header.next_epoch_id) + }, + None, + vec![], + Ratio::from_integer(0), + 0, + 100, + None, + vec![], + vec![], + &signer, + last_block.header.next_bp_hash, + block_merkle_tree.root(), + None, + ); + actor_handles.client_actor.do_send( + BlockResponse { block, peer_id: PeerInfo::random().id, was_requested: false } + .with_span_context(), + ); + future::ready(()) + }); + actix::spawn(actor); + unc_network::test_utils::wait_or_panic(5000); + }); +} + +/// Include approvals to the next block in newly produced block. +#[test] +fn produce_block_with_approvals() { + init_test_logger(); + let validators: Vec<_> = + (1..=10).map(|i| AccountId::try_from(format!("test{}", i)).unwrap()).collect(); + run_actix(async { + let actor_handles = setup_mock( + validators.clone(), + "test1".parse().unwrap(), + true, + false, + Box::new(move |msg, _ctx, _| { + if let NetworkRequests::Block { block } = msg.as_network_requests_ref() { + // Below we send approvals from all the block producers except for test1 and test2 + // test1 will only create their approval for height 10 after their doomslug timer + // runs 10 iterations, which is way further in the future than them producing the + // block + if block.header().num_approvals() == validators.len() as u64 - 2 { + System::current().stop(); + } else if block.header().height() == 10 { + println!("{}", block.header().height()); + println!( + "{} != {} -2 (height: {})", + block.header().num_approvals(), + validators.len(), + block.header().height() + ); + + assert!(false); + } + } + PeerManagerMessageResponse::NetworkResponses(NetworkResponses::NoResponse) + }), + ); + let actor = actor_handles + .view_client_actor + .send(GetBlockWithMerkleTree::latest().with_span_context()); + let actor = actor.then(move |res| { + let (last_block, block_merkle_tree) = res.unwrap().unwrap(); + let mut block_merkle_tree = PartialMerkleTree::clone(&block_merkle_tree); + block_merkle_tree.insert(last_block.header.hash); + let signer1 = create_test_signer("test2"); + let next_block_ordinal = last_block.header.block_ordinal.unwrap() + 1; + let block = Block::produce( + PROTOCOL_VERSION, + PROTOCOL_VERSION, + &last_block.header.clone().into(), + last_block.header.height + 1, + next_block_ordinal, + last_block.chunks.into_iter().map(Into::into).collect(), + EpochId::default(), + if last_block.header.prev_hash == CryptoHash::default() { + EpochId(last_block.header.hash) + } else { + EpochId(last_block.header.next_epoch_id) + }, + None, + vec![], + Ratio::from_integer(0), + 0, + 100, + Some(0), + vec![], + vec![], + &signer1, + last_block.header.next_bp_hash, + block_merkle_tree.root(), + None, + ); + actor_handles.client_actor.do_send( + BlockResponse { + block: block.clone(), + peer_id: PeerInfo::random().id, + was_requested: false, + } + .with_span_context(), + ); + + for i in 3..11 { + let s = AccountId::try_from(if i > 10 { + "test1".to_string() + } else { + format!("test{}", i) + }) + .unwrap(); + let signer = create_test_signer(s.as_str()); + let approval = Approval::new( + *block.hash(), + block.header().height(), + 10, // the height at which "test1" is producing + &signer, + ); + actor_handles + .client_actor + .do_send(BlockApproval(approval, PeerInfo::random().id).with_span_context()); + } + + future::ready(()) + }); + actix::spawn(actor); + unc_network::test_utils::wait_or_panic(5000); + }); +} + +/// When approvals arrive early, they should be properly cached. +#[test] +fn produce_block_with_approvals_arrived_early() { + init_test_logger(); + let vs = ValidatorSchedule::new().num_shards(4).block_producers_per_epoch(vec![vec![ + "test1".parse().unwrap(), + "test2".parse().unwrap(), + "test3".parse().unwrap(), + "test4".parse().unwrap(), + ]]); + let archive = vec![false; vs.all_block_producers().count()]; + let epoch_sync_enabled = vec![true; vs.all_block_producers().count()]; + let key_pairs = + vec![PeerInfo::random(), PeerInfo::random(), PeerInfo::random(), PeerInfo::random()]; + let block_holder: Arc>> = Arc::new(RwLock::new(None)); + run_actix(async move { + let mut approval_counter = 0; + setup_mock_all_validators( + vs, + key_pairs, + true, + 2000, + false, + false, + 100, + true, + archive, + epoch_sync_enabled, + false, + Box::new( + move |conns, + _, + msg: &PeerManagerMessageRequest| + -> (PeerManagerMessageResponse, bool) { + let msg = msg.as_network_requests_ref(); + match msg { + NetworkRequests::Block { block } => { + if block.header().height() == 3 { + for (i, actor_handles) in conns.iter().enumerate() { + if i > 0 { + actor_handles.client_actor.do_send( + BlockResponse { + block: block.clone(), + peer_id: PeerInfo::random().id, + was_requested: false, + } + .with_span_context(), + ) + } + } + *block_holder.write().unwrap() = Some(block.clone()); + return (NetworkResponses::NoResponse.into(), false); + } else if block.header().height() == 4 { + System::current().stop(); + } + (NetworkResponses::NoResponse.into(), true) + } + NetworkRequests::Approval { approval_message } => { + if approval_message.target == "test1" + && approval_message.approval.target_height == 4 + { + approval_counter += 1; + } + if approval_counter == 3 { + let block = block_holder.read().unwrap().clone().unwrap(); + conns[0].client_actor.do_send( + BlockResponse { + block, + peer_id: PeerInfo::random().id, + was_requested: false, + } + .with_span_context(), + ); + } + (NetworkResponses::NoResponse.into(), true) + } + _ => (NetworkResponses::NoResponse.into(), true), + } + }, + ), + ); + + unc_network::test_utils::wait_or_panic(10000); + }); +} + +/// Sends one invalid block followed by one valid block, and checks that client announces only valid block. +/// and that the node bans the peer for invalid block header. +fn invalid_blocks_common(is_requested: bool) { + init_test_logger(); + run_actix(async move { + let mut ban_counter = 0; + let actor_handles = setup_mock( + vec!["test".parse().unwrap()], + "other".parse().unwrap(), + true, + false, + Box::new(move |msg, _ctx, _client_actor| { + match msg.as_network_requests_ref() { + NetworkRequests::Block { block } => { + if is_requested { + panic!("rebroadcasting requested block"); + } else { + assert_eq!(block.header().height(), 1); + assert_eq!(block.header().chunk_mask().len(), 1); + #[cfg(not( + feature = "protocol_feature_reject_blocks_with_outdated_protocol_version" + ))] + assert_eq!(ban_counter, 2); + #[cfg( + feature = "protocol_feature_reject_blocks_with_outdated_protocol_version" + )] + { + assert_eq!( + block.header().latest_protocol_version(), + PROTOCOL_VERSION + ); + assert_eq!(ban_counter, 3); + } + System::current().stop(); + } + } + NetworkRequests::BanPeer { ban_reason, .. } => { + assert_eq!(ban_reason, &ReasonForBan::BadBlockHeader); + ban_counter += 1; + #[cfg( + feature = "protocol_feature_reject_blocks_with_outdated_protocol_version" + )] + let expected_ban_counter = 4; + #[cfg(not( + feature = "protocol_feature_reject_blocks_with_outdated_protocol_version" + ))] + let expected_ban_counter = 3; + if ban_counter == expected_ban_counter && is_requested { + System::current().stop(); + } + } + _ => {} + }; + PeerManagerMessageResponse::NetworkResponses(NetworkResponses::NoResponse) + }), + ); + let actor = actor_handles + .view_client_actor + .send(GetBlockWithMerkleTree::latest().with_span_context()); + let actor = actor.then(move |res| { + let (last_block, block_merkle_tree) = res.unwrap().unwrap(); + let mut block_merkle_tree = PartialMerkleTree::clone(&block_merkle_tree); + block_merkle_tree.insert(last_block.header.hash); + let signer = create_test_signer("test"); + let next_block_ordinal = last_block.header.block_ordinal.unwrap() + 1; + let valid_block = Block::produce( + PROTOCOL_VERSION, + PROTOCOL_VERSION, + &last_block.header.clone().into(), + last_block.header.height + 1, + next_block_ordinal, + last_block.chunks.iter().cloned().map(Into::into).collect(), + EpochId::default(), + if last_block.header.prev_hash == CryptoHash::default() { + EpochId(last_block.header.hash) + } else { + EpochId(last_block.header.next_epoch_id) + }, + None, + vec![], + Ratio::from_integer(0), + 0, + 100, + Some(0), + vec![], + vec![], + &signer, + last_block.header.next_bp_hash, + block_merkle_tree.root(), + None, + ); + // Send block with invalid chunk mask + let mut block = valid_block.clone(); + block.mut_header().get_mut().inner_rest.chunk_mask = vec![]; + block.mut_header().get_mut().init(); + actor_handles.client_actor.do_send( + BlockResponse { + block: block.clone(), + peer_id: PeerInfo::random().id, + was_requested: is_requested, + } + .with_span_context(), + ); + + // Send blocks with invalid protocol version + #[cfg(feature = "protocol_feature_reject_blocks_with_outdated_protocol_version")] + { + let mut block = valid_block.clone(); + block.mut_header().get_mut().inner_rest.latest_protocol_version = + PROTOCOL_VERSION - 1; + block.mut_header().get_mut().init(); + actor_handles.client_actor.do_send( + BlockResponse { + block: block.clone(), + peer_id: PeerInfo::random().id, + was_requested: is_requested, + } + .with_span_context(), + ); + } + + // Send block with invalid chunk signature + let mut block = valid_block.clone(); + let mut chunks: Vec<_> = block.chunks().iter().cloned().collect(); + let some_signature = Signature::from_parts(KeyType::ED25519, &[1; 64]).unwrap(); + match &mut chunks[0] { + ShardChunkHeader::V1(chunk) => { + chunk.signature = some_signature; + } + ShardChunkHeader::V2(chunk) => { + chunk.signature = some_signature; + } + ShardChunkHeader::V3(chunk) => { + chunk.signature = some_signature; + } + }; + block.set_chunks(chunks); + actor_handles.client_actor.do_send( + BlockResponse { + block: block.clone(), + peer_id: PeerInfo::random().id, + was_requested: is_requested, + } + .with_span_context(), + ); + + // Send proper block. + let block2 = valid_block; + actor_handles.client_actor.do_send( + BlockResponse { + block: block2.clone(), + peer_id: PeerInfo::random().id, + was_requested: is_requested, + } + .with_span_context(), + ); + if is_requested { + let mut block3 = block2; + block3.mut_header().get_mut().inner_rest.chunk_headers_root = hash(&[1]); + block3.mut_header().get_mut().init(); + actor_handles.client_actor.do_send( + BlockResponse { + block: block3.clone(), + peer_id: PeerInfo::random().id, + was_requested: is_requested, + } + .with_span_context(), + ); + } + future::ready(()) + }); + actix::spawn(actor); + unc_network::test_utils::wait_or_panic(5000); + }); +} + +const TEST_SEED: RngSeed = [3; 32]; + +#[test] +fn test_invalid_blocks_not_requested() { + invalid_blocks_common(false); +} + +#[test] +fn test_invalid_blocks_requested() { + invalid_blocks_common(true); +} + +enum InvalidBlockMode { + /// Header is invalid + InvalidHeader, + /// Block is ill-formed (roots check fail) + IllFormed, + /// Block is invalid for other reasons + InvalidBlock, +} + +fn ban_peer_for_invalid_block_common(mode: InvalidBlockMode) { + init_test_logger(); + let vs = ValidatorSchedule::new().block_producers_per_epoch(vec![vec![ + "test1".parse().unwrap(), + "test2".parse().unwrap(), + "test3".parse().unwrap(), + "test4".parse().unwrap(), + ]]); + let validators = vs.all_block_producers().cloned().collect::>(); + let key_pairs = + vec![PeerInfo::random(), PeerInfo::random(), PeerInfo::random(), PeerInfo::random()]; + run_actix(async move { + let mut ban_counter = 0; + let mut sent_bad_blocks = false; + setup_mock_all_validators( + vs, + key_pairs, + true, + 100, + false, + false, + 100, + true, + vec![false; validators.len()], + vec![true; validators.len()], + false, + Box::new( + move |conns, + _, + msg: &PeerManagerMessageRequest| + -> (PeerManagerMessageResponse, bool) { + match msg.as_network_requests_ref() { + NetworkRequests::Block { block } => { + if block.header().height() >= 4 && !sent_bad_blocks { + let block_producer_idx = + block.header().height() as usize % validators.len(); + let block_producer = &validators[block_producer_idx]; + let validator_signer1 = create_test_signer(block_producer.as_str()); + sent_bad_blocks = true; + let mut block_mut = block.clone(); + match mode { + InvalidBlockMode::InvalidHeader => { + // produce an invalid block with invalid header. + block_mut.mut_header().get_mut().inner_rest.chunk_mask = + vec![]; + block_mut.mut_header().resign(&validator_signer1); + } + InvalidBlockMode::IllFormed => { + // produce an ill-formed block + block_mut + .mut_header() + .get_mut() + .inner_rest + .chunk_headers_root = hash(&[1]); + block_mut.mut_header().resign(&validator_signer1); + } + InvalidBlockMode::InvalidBlock => { + // produce an invalid block whose invalidity cannot be verified by just + // having its header. + let proposals = vec![ValidatorStake::new( + "test1".parse().unwrap(), + PublicKey::empty(KeyType::ED25519), + 0, + )]; + + block_mut + .mut_header() + .get_mut() + .inner_rest + .prev_validator_proposals = proposals; + block_mut.mut_header().resign(&validator_signer1); + } + } + + for (i, actor_handles) in conns.into_iter().enumerate() { + if i != block_producer_idx { + actor_handles.client_actor.do_send( + BlockResponse { + block: block_mut.clone(), + peer_id: PeerInfo::random().id, + was_requested: false, + } + .with_span_context(), + ) + } + } + + return ( + PeerManagerMessageResponse::NetworkResponses( + NetworkResponses::NoResponse, + ), + false, + ); + } + if block.header().height() > 20 { + match mode { + InvalidBlockMode::InvalidHeader + | InvalidBlockMode::IllFormed => { + assert_eq!(ban_counter, 3); + } + _ => {} + } + System::current().stop(); + } + ( + PeerManagerMessageResponse::NetworkResponses( + NetworkResponses::NoResponse, + ), + true, + ) + } + NetworkRequests::BanPeer { peer_id, ban_reason } => match mode { + InvalidBlockMode::InvalidHeader | InvalidBlockMode::IllFormed => { + assert_eq!(ban_reason, &ReasonForBan::BadBlockHeader); + ban_counter += 1; + if ban_counter > 3 { + panic!("more bans than expected"); + } + ( + PeerManagerMessageResponse::NetworkResponses( + NetworkResponses::NoResponse, + ), + true, + ) + } + InvalidBlockMode::InvalidBlock => { + panic!( + "banning peer {:?} unexpectedly for {:?}", + peer_id, ban_reason + ); + } + }, + _ => ( + PeerManagerMessageResponse::NetworkResponses( + NetworkResponses::NoResponse, + ), + true, + ), + } + }, + ), + ); + unc_network::test_utils::wait_or_panic(20000); + }); +} + +/// If a peer sends a block whose header is valid and passes basic validation, the peer is not banned. +#[test] +fn test_not_ban_peer_for_invalid_block() { + ban_peer_for_invalid_block_common(InvalidBlockMode::InvalidBlock); +} + +/// If a peer sends a block whose header is invalid, we should ban them and do not forward the block +#[test] +fn test_ban_peer_for_invalid_block_header() { + ban_peer_for_invalid_block_common(InvalidBlockMode::InvalidHeader); +} + +/// If a peer sends a block that is ill-formed, we should ban them and do not forward the block +#[test] +fn test_ban_peer_for_ill_formed_block() { + ban_peer_for_invalid_block_common(InvalidBlockMode::IllFormed); +} + +/// Runs two validators runtime with only one validator online. +/// Present validator produces blocks on it's height after deadline. +#[test] +fn skip_block_production() { + init_test_logger(); + run_actix(async { + setup_mock( + vec!["test1".parse().unwrap(), "test2".parse().unwrap()], + "test2".parse().unwrap(), + true, + false, + Box::new(move |msg, _ctx, _client_actor| { + match msg.as_network_requests_ref() { + NetworkRequests::Block { block } => { + if block.header().height() > 3 { + System::current().stop(); + } + } + _ => {} + }; + PeerManagerMessageResponse::NetworkResponses(NetworkResponses::NoResponse) + }), + ); + wait_or_panic(10000); + }); +} + +/// Runs client that requests syncing headers from peers. +#[test] +fn client_sync_headers() { + init_test_logger(); + run_actix(async { + let peer_info1 = PeerInfo::random(); + let peer_info2 = peer_info1.clone(); + let actor_handles = setup_mock( + vec!["test".parse().unwrap()], + "other".parse().unwrap(), + false, + false, + Box::new(move |msg, _ctx, _client_actor| match msg.as_network_requests_ref() { + NetworkRequests::BlockHeadersRequest { hashes, peer_id } => { + assert_eq!(*peer_id, peer_info1.id); + assert_eq!(hashes.len(), 1); + // TODO: check it requests correct hashes. + System::current().stop(); + + PeerManagerMessageResponse::NetworkResponses(NetworkResponses::NoResponse) + } + _ => PeerManagerMessageResponse::NetworkResponses(NetworkResponses::NoResponse), + }), + ); + actor_handles.client_actor.do_send( + SetNetworkInfo(NetworkInfo { + connected_peers: vec![ConnectedPeerInfo { + full_peer_info: FullPeerInfo { + peer_info: peer_info2.clone(), + chain_info: PeerChainInfo { + genesis_id: Default::default(), + last_block: Some(BlockInfo { height: 5, hash: hash(&[5]) }), + tracked_shards: vec![], + archival: false, + }, + }, + received_bytes_per_sec: 0, + sent_bytes_per_sec: 0, + last_time_peer_requested: unc_async::time::Instant::now(), + last_time_received_message: unc_async::time::Instant::now(), + connection_established_time: unc_async::time::Instant::now(), + peer_type: PeerType::Outbound, + nonce: 1, + }], + num_connected_peers: 1, + peer_max_count: 1, + highest_height_peers: vec![HighestHeightPeerInfo { + peer_info: peer_info2, + genesis_id: Default::default(), + highest_block_height: 5, + highest_block_hash: hash(&[5]), + tracked_shards: vec![], + archival: false, + }], + sent_bytes_per_sec: 0, + received_bytes_per_sec: 0, + known_producers: vec![], + tier1_connections: vec![], + tier1_accounts_keys: vec![], + tier1_accounts_data: vec![], + }) + .with_span_context(), + ); + wait_or_panic(2000); + }); +} + +#[test] +fn test_process_invalid_tx() { + init_test_logger(); + let mut genesis = Genesis::test(vec!["test0".parse().unwrap()], 1); + genesis.config.transaction_validity_period = 10; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test0"); + let tx = SignedTransaction::new( + Signature::empty(KeyType::ED25519), + Transaction { + signer_id: "test".parse().unwrap(), + public_key: signer.public_key(), + nonce: 0, + receiver_id: "test".parse().unwrap(), + block_hash: *env.clients[0].chain.genesis().hash(), + actions: vec![], + }, + ); + for i in 1..12 { + env.produce_block(0, i); + } + assert_eq!( + env.clients[0].process_tx(tx, false, false), + ProcessTxResponse::InvalidTx(InvalidTxError::Expired) + ); + let tx2 = SignedTransaction::new( + Signature::empty(KeyType::ED25519), + Transaction { + signer_id: "test".parse().unwrap(), + public_key: signer.public_key(), + nonce: 0, + receiver_id: "test".parse().unwrap(), + block_hash: hash(&[1]), + actions: vec![], + }, + ); + assert_eq!( + env.clients[0].process_tx(tx2, false, false), + ProcessTxResponse::InvalidTx(InvalidTxError::Expired) + ); +} + +/// If someone produce a block with Utc::now() + 1 min, we should produce a block with valid timestamp +#[test] +fn test_time_attack() { + init_test_logger(); + let store = create_test_store(); + let network_adapter = Arc::new(MockPeerManagerAdapter::default()); + let client_adapter = Arc::new(MockClientAdapterForShardsManager::default()); + let chain_genesis = ChainGenesis::test(); + let vs = + ValidatorSchedule::new().block_producers_per_epoch(vec![vec!["test1".parse().unwrap()]]); + let mut client = setup_client_with_synchronous_shards_manager( + store, + vs, + Some("test1".parse().unwrap()), + false, + network_adapter.into(), + client_adapter.as_sender(), + chain_genesis, + TEST_SEED, + false, + true, + ); + let signer = Arc::new(create_test_signer("test1")); + let genesis = client.chain.get_block_by_height(0).unwrap(); + let mut b1 = TestBlockBuilder::new(&genesis, signer.clone()).build(); + b1.mut_header().get_mut().inner_lite.timestamp = + to_timestamp(b1.header().timestamp() + chrono::Duration::seconds(60)); + b1.mut_header().resign(&*signer); + + let _ = client.process_block_test(b1.into(), Provenance::NONE).unwrap(); + + let b2 = client.produce_block(2).unwrap().unwrap(); + let _ = client.process_block_test(b2.into(), Provenance::PRODUCED).unwrap(); +} + +#[test] +fn test_no_double_sign() { + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + let _ = env.clients[0].produce_block(1).unwrap().unwrap(); + // Second time producing with the same height should fail. + assert_eq!(env.clients[0].produce_block(1).unwrap(), None); +} + +#[test] +fn test_invalid_gas_price() { + init_test_logger(); + let store = create_test_store(); + let network_adapter = Arc::new(MockPeerManagerAdapter::default()); + let client_adapter = Arc::new(MockClientAdapterForShardsManager::default()); + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.min_gas_price = 100; + let vs = + ValidatorSchedule::new().block_producers_per_epoch(vec![vec!["test1".parse().unwrap()]]); + let mut client = setup_client_with_synchronous_shards_manager( + store, + vs, + Some("test1".parse().unwrap()), + false, + network_adapter.into(), + client_adapter.as_sender(), + chain_genesis, + TEST_SEED, + false, + true, + ); + let signer = Arc::new(create_test_signer("test1")); + let genesis = client.chain.get_block_by_height(0).unwrap(); + let mut b1 = TestBlockBuilder::new(&genesis, signer.clone()).build(); + b1.mut_header().get_mut().inner_rest.next_gas_price = 0; + b1.mut_header().resign(&*signer); + + let res = client.process_block_test(b1.into(), Provenance::NONE); + assert_matches!(res.unwrap_err(), Error::InvalidGasPrice); +} + +#[test] +fn test_invalid_height_too_large() { + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + let b1 = env.clients[0].produce_block(1).unwrap().unwrap(); + let _ = env.clients[0].process_block_test(b1.clone().into(), Provenance::PRODUCED).unwrap(); + let signer = Arc::new(create_test_signer("test0")); + let b2 = TestBlockBuilder::new(&b1, signer).height(u64::MAX).build(); + let res = env.clients[0].process_block_test(b2.into(), Provenance::NONE); + assert_matches!(res.unwrap_err(), Error::InvalidBlockHeight(_)); +} + +/// Check that if block height is 5 epochs behind the head, it is not processed. +#[test] +fn test_invalid_height_too_old() { + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + for i in 1..4 { + env.produce_block(0, i); + } + let block = env.clients[0].produce_block(4).unwrap().unwrap(); + for i in 5..30 { + env.produce_block(0, i); + } + let res = env.clients[0].process_block_test(block.into(), Provenance::NONE); + assert_matches!(res.unwrap_err(), Error::InvalidBlockHeight(_)); +} + +#[test] +fn test_bad_orphan() { + let mut genesis = ChainGenesis::test(); + genesis.epoch_length = 100; + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + for i in 1..4 { + env.produce_block(0, i); + } + let block = env.clients[0].produce_block(5).unwrap().unwrap(); + let signer = env.clients[0].validator_signer.as_ref().unwrap().clone(); + { + // Orphan block with unknown epoch + let mut block = env.clients[0].produce_block(6).unwrap().unwrap(); + block.mut_header().get_mut().inner_lite.epoch_id = EpochId(CryptoHash([1; 32])); + block.mut_header().get_mut().prev_hash = CryptoHash([1; 32]); + block.mut_header().resign(&*signer); + let res = env.clients[0].process_block_test(block.clone().into(), Provenance::NONE); + match res { + Err(Error::EpochOutOfBounds(epoch_id)) => { + assert_eq!(&epoch_id, block.header().epoch_id()) + } + _ => panic!("expected EpochOutOfBounds error, got {res:?}"), + } + } + { + // Orphan block with invalid signature + let mut block = env.clients[0].produce_block(7).unwrap().unwrap(); + block.mut_header().get_mut().prev_hash = CryptoHash([1; 32]); + block.mut_header().get_mut().init(); + let res = env.clients[0].process_block_test(block.into(), Provenance::NONE); + assert_matches!(res.unwrap_err(), Error::InvalidSignature); + } + { + // Orphan block with a valid header, but garbage in body + let mut block = env.clients[0].produce_block(8).unwrap().unwrap(); + { + let block = block.get_mut(); + // Change the chunk in any way, chunk_headers_root won't match + let chunk = &mut block.body.chunks[0].get_mut(); + + match &mut chunk.inner { + ShardChunkHeaderInner::V1(inner) => inner.prev_outcome_root = CryptoHash([1; 32]), + ShardChunkHeaderInner::V2(inner) => inner.prev_outcome_root = CryptoHash([1; 32]), + } + chunk.hash = ShardChunkHeaderV3::compute_hash(&chunk.inner); + } + block.mut_header().get_mut().prev_hash = CryptoHash([3; 32]); + block.mut_header().resign(&*signer); + let res = env.clients[0].process_block_test(block.into(), Provenance::NONE); + assert_matches!(res.unwrap_err(), Error::InvalidChunkHeadersRoot); + } + { + // Orphan block with invalid approvals. Allowed for now. + let mut block = env.clients[0].produce_block(9).unwrap().unwrap(); + let some_signature = Signature::from_parts(KeyType::ED25519, &[1; 64]).unwrap(); + block.mut_header().get_mut().inner_rest.approvals = vec![Some(Box::new(some_signature))]; + block.mut_header().get_mut().prev_hash = CryptoHash([3; 32]); + block.mut_header().resign(&*signer); + let res = env.clients[0].process_block_test(block.into(), Provenance::NONE); + + assert_matches!(res.unwrap_err(), Error::Orphan); + } + { + // Orphan block with no chunk signatures. Allowed for now. + let mut block = env.clients[0].produce_block(10).unwrap().unwrap(); + let some_signature = Signature::from_parts(KeyType::ED25519, &[1; 64]).unwrap(); + { + // Change the chunk in any way, chunk_headers_root won't match + let chunk = block.get_mut().body.chunks[0].get_mut(); + chunk.signature = some_signature; + chunk.hash = ShardChunkHeaderV3::compute_hash(&chunk.inner); + } + block.mut_header().get_mut().prev_hash = CryptoHash([4; 32]); + block.mut_header().resign(&*signer); + let res = env.clients[0].process_block_test(block.into(), Provenance::NONE); + assert_matches!(res.unwrap_err(), Error::Orphan); + } + { + // Orphan block that's too far ahead: 20 * epoch_length + let mut block = block.clone(); + block.mut_header().get_mut().prev_hash = CryptoHash([3; 32]); + block.mut_header().get_mut().inner_lite.height += 2000; + block.mut_header().resign(&*signer); + let res = env.clients[0].process_block_test(block.into(), Provenance::NONE); + assert_matches!(res.unwrap_err(), Error::InvalidBlockHeight(_)); + } + env.clients[0].process_block_test(block.into(), Provenance::NONE).unwrap(); +} + +#[test] +fn test_bad_chunk_mask() { + init_test_logger(); + let chain_genesis = ChainGenesis::test(); + let validators = vec!["test0".parse().unwrap(), "test1".parse().unwrap()]; + let mut clients: Vec = validators + .iter() + .map(|account_id| { + let vs = ValidatorSchedule::new() + .num_shards(2) + .block_producers_per_epoch(vec![validators.clone()]); + setup_client_with_synchronous_shards_manager( + create_test_store(), + vs, + Some(account_id.clone()), + false, + Arc::new(MockPeerManagerAdapter::default()).into(), + MockClientAdapterForShardsManager::default().into_sender(), + chain_genesis.clone(), + TEST_SEED, + false, + true, + ) + }) + .collect(); + for height in 1..5 { + let block_producer = (height % 2) as usize; + let chunk_producer = ((height + 1) % 2) as usize; + + let (encoded_chunk, merkle_paths, receipts) = + create_chunk_on_height(&mut clients[chunk_producer], height); + for client in clients.iter_mut() { + client + .persist_and_distribute_encoded_chunk( + encoded_chunk.clone(), + merkle_paths.clone(), + receipts.clone(), + client.validator_signer.as_ref().unwrap().validator_id().clone(), + ) + .unwrap(); + } + + let mut block = clients[block_producer].produce_block(height).unwrap().unwrap(); + { + let mut chunk_header = encoded_chunk.cloned_header(); + *chunk_header.height_included_mut() = height; + let mut chunk_headers: Vec<_> = block.chunks().iter().cloned().collect(); + chunk_headers[0] = chunk_header; + block.set_chunks(chunk_headers.clone()); + block.mut_header().get_mut().inner_rest.chunk_headers_root = + Block::compute_chunk_headers_root(&chunk_headers).0; + block.mut_header().get_mut().inner_rest.chunk_tx_root = + Block::compute_chunk_tx_root(&chunk_headers); + block.mut_header().get_mut().inner_rest.prev_chunk_outgoing_receipts_root = + Block::compute_chunk_prev_outgoing_receipts_root(&chunk_headers); + block.mut_header().get_mut().inner_lite.prev_state_root = + Block::compute_state_root(&chunk_headers); + block.mut_header().get_mut().inner_rest.chunk_mask = vec![true, false]; + let mess_with_chunk_mask = height == 4; + if mess_with_chunk_mask { + block.mut_header().get_mut().inner_rest.chunk_mask = vec![false, true]; + } + block + .mut_header() + .resign(&*clients[block_producer].validator_signer.as_ref().unwrap().clone()); + let res1 = clients[chunk_producer] + .process_block_test_no_produce_chunk(block.clone().into(), Provenance::NONE); + let res2 = clients[block_producer] + .process_block_test_no_produce_chunk(block.clone().into(), Provenance::NONE); + if !mess_with_chunk_mask { + res1.unwrap(); + res2.unwrap(); + } else { + res1.unwrap_err(); + res2.unwrap_err(); + } + } + } +} + +#[test] +fn test_minimum_gas_price() { + let min_gas_price = 100; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.min_gas_price = min_gas_price; + chain_genesis.gas_price_adjustment_rate = Ratio::new(1, 10); + let mut env = TestEnv::builder(chain_genesis).build(); + for i in 1..=100 { + env.produce_block(0, i); + } + let block = env.clients[0].chain.get_block_by_height(100).unwrap(); + assert!(block.header().next_gas_price() >= min_gas_price); +} + +fn test_gc_with_epoch_length_common(epoch_length: NumBlocks) { + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let mut blocks = vec![]; + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + blocks.push(genesis_block); + for i in 1..=epoch_length * (DEFAULT_GC_NUM_EPOCHS_TO_KEEP + 1) { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + blocks.push(block); + } + for i in 0..=epoch_length * (DEFAULT_GC_NUM_EPOCHS_TO_KEEP + 1) { + println!("height = {}", i); + if i < epoch_length { + let block_hash = *blocks[i as usize].hash(); + assert_matches!( + env.clients[0].chain.get_block(&block_hash).unwrap_err(), + Error::DBNotFoundErr(missing_block_hash) if missing_block_hash == format!("BLOCK: {}", block_hash) + ); + assert_matches!( + env.clients[0].chain.get_block_by_height(i).unwrap_err(), + Error::DBNotFoundErr(missing_block_hash) if missing_block_hash == format!("BLOCK: {}", block_hash) + ); + assert!(env.clients[0] + .chain + .mut_chain_store() + .get_all_block_hashes_by_height(i as BlockHeight) + .unwrap() + .is_empty()); + } else { + assert!(env.clients[0].chain.get_block(blocks[i as usize].hash()).is_ok()); + assert!(env.clients[0].chain.get_block_by_height(i).is_ok()); + assert!(!env.clients[0] + .chain + .mut_chain_store() + .get_all_block_hashes_by_height(i as BlockHeight) + .unwrap() + .is_empty()); + } + } + assert_eq!(env.clients[0].chain.chain_store().chunk_tail().unwrap(), epoch_length - 1); +} + +#[test] +fn test_gc_with_epoch_length() { + for i in 3..20 { + test_gc_with_epoch_length_common(i); + } +} + +/// When an epoch is very long there should not be anything garbage collected unexpectedly +#[test] +fn test_gc_long_epoch() { + test_gc_with_epoch_length_common(200); +} + +/// Test that producing blocks works in archival mode with save_trie_changes enabled. +/// In that case garbage collection should not happen but trie changes should be saved to the store. +#[test] +fn test_archival_save_trie_changes() { + let epoch_length = 10; + + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .archive(true) + .save_trie_changes(true) + .build(); + + env.clients[0].chain.chain_store().store().set_db_kind(DbKind::Archive).unwrap(); + + let mut blocks = vec![]; + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + blocks.push(genesis_block); + for i in 1..=epoch_length * (DEFAULT_GC_NUM_EPOCHS_TO_KEEP + 1) { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + blocks.push(block); + } + // Go through all of the blocks and verify that the block are stored and that the trie changes were stored too. + for i in 0..=epoch_length * (DEFAULT_GC_NUM_EPOCHS_TO_KEEP + 1) { + let client = &env.clients[0]; + let chain = &client.chain; + let store = chain.chain_store(); + let block = &blocks[i as usize]; + let header = block.header(); + let epoch_id = header.epoch_id(); + let shard_layout = client.epoch_manager.get_shard_layout(epoch_id).unwrap(); + + assert!(chain.get_block(block.hash()).is_ok()); + assert!(chain.get_block_by_height(i).is_ok()); + assert!(!chain + .chain_store() + .get_all_block_hashes_by_height(i as BlockHeight) + .unwrap() + .is_empty()); + + // The genesis block does not contain trie changes. + if i == 0 { + continue; + } + + // Go through chunks and test that trie changes were correctly saved to the store. + let chunks = block.chunks(); + for chunk in chunks.iter() { + let shard_id = chunk.shard_id() as u32; + let version = shard_layout.version(); + + let shard_uid = ShardUId { version, shard_id }; + let key = get_block_shard_uid(&block.hash(), &shard_uid); + let trie_changes: Option = + store.store().get_ser(DBCol::TrieChanges, &key).unwrap(); + + if let Some(trie_changes) = trie_changes { + // We don't do any transactions in this test so the root should remain unchanged. + assert_eq!(trie_changes.old_root, trie_changes.new_root); + } + } + } +} + +fn test_archival_gc_common( + storage: NodeStorage, + epoch_length: u64, + max_height: BlockHeight, + max_cold_head_height: BlockHeight, + legacy: bool, +) { + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + + let hot_store = &storage.get_hot_store(); + + let mut env = TestEnv::builder(chain_genesis) + .stores(vec![hot_store.clone()]) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .archive(true) + .save_trie_changes(true) + .build(); + + let mut blocks = vec![]; + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + blocks.push(genesis_block); + + for i in 1..=max_height { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + + let header = block.header(); + let epoch_id = header.epoch_id(); + let shard_layout = env.clients[0].epoch_manager.get_shard_layout(epoch_id).unwrap(); + + blocks.push(block); + + if i <= max_cold_head_height { + update_cold_db(storage.cold_db().unwrap(), hot_store, &shard_layout, &i).unwrap(); + update_cold_head(storage.cold_db().unwrap(), &hot_store, &i).unwrap(); + } + } + + // All blocks up until max_gc_height, exclusively, should be garbage collected. + // In the '_current' test this will be max_height - 5 epochs + // In the '_behind' test this will be the cold head height. + // In the '_migration' test this will be 0. + let mut max_gc_height = 0; + if !legacy { + max_gc_height = std::cmp::min( + max_height - epoch_length * DEFAULT_GC_NUM_EPOCHS_TO_KEEP, + // Nice little way to round down to latest epoch start + max_cold_head_height / epoch_length * epoch_length, + ); + }; + + for i in 0..=max_height { + let client = &env.clients[0]; + let chain = &client.chain; + let block = &blocks[i as usize]; + + if i < max_gc_height { + assert!(chain.get_block(block.hash()).is_err()); + assert!(chain.get_block_by_height(i).is_err()); + } else { + assert!(chain.get_block(block.hash()).is_ok()); + assert!(chain.get_block_by_height(i).is_ok()); + assert!(!chain + .chain_store() + .get_all_block_hashes_by_height(i as BlockHeight) + .unwrap() + .is_empty()); + } + } +} + +/// This test verifies that archival node in split storage mode that is up to +/// date on the hot -> cold block copying is correctly garbage collecting +/// blocks older than 5 epochs. +#[test] +fn test_archival_gc_migration() { + // Split storage in the middle of migration has hot store kind set to archive. + let (storage, ..) = create_test_node_storage_with_cold(DB_VERSION, DbKind::Archive); + + let epoch_length = 10; + let max_height = epoch_length * (DEFAULT_GC_NUM_EPOCHS_TO_KEEP + 2); + let max_cold_head_height = 5; + + test_archival_gc_common(storage, epoch_length, max_height, max_cold_head_height, true); +} + +/// This test verifies that archival node in split storage mode that is up to +/// date on the hot -> cold block copying is correctly garbage collecting +/// blocks older than 5 epochs. +#[test] +fn test_archival_gc_split_storage_current() { + // Fully migrated split storage has each store configured with kind = temperature. + let (storage, ..) = create_test_node_storage_with_cold(DB_VERSION, DbKind::Hot); + + let epoch_length = 10; + let max_height = epoch_length * (DEFAULT_GC_NUM_EPOCHS_TO_KEEP + 2); + let max_cold_head_height = max_height - 2 * epoch_length; + + test_archival_gc_common(storage, epoch_length, max_height, max_cold_head_height, false); +} + +/// This test verifies that archival node in split storage mode that is behind +/// on the hot -> cold block copying is correctly garbage collecting blocks +/// older than the cold head. +#[test] +fn test_archival_gc_split_storage_behind() { + // Fully migrated split storage has each store configured with kind = temperature. + let (storage, ..) = create_test_node_storage_with_cold(DB_VERSION, DbKind::Hot); + + let epoch_length = 10; + let max_height = epoch_length * (DEFAULT_GC_NUM_EPOCHS_TO_KEEP + 2); + let max_cold_head_height = 5; + + test_archival_gc_common(storage, epoch_length, max_height, max_cold_head_height, false); +} + +#[test] +fn test_gc_block_skips() { + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = 5; + let mut env = TestEnv::builder(chain_genesis.clone()).build(); + for i in 1..=1000 { + if i % 2 == 0 { + env.produce_block(0, i); + } + } + let mut env = TestEnv::builder(chain_genesis.clone()).build(); + for i in 1..=1000 { + if i % 2 == 1 { + env.produce_block(0, i); + } + } + // Epoch skips + let mut env = TestEnv::builder(chain_genesis).build(); + for i in 1..=1000 { + if i % 9 == 7 { + env.produce_block(0, i); + } + } +} + +#[test] +fn test_gc_chunk_tail() { + let mut chain_genesis = ChainGenesis::test(); + let epoch_length = 100; + chain_genesis.epoch_length = epoch_length; + let mut env = TestEnv::builder(chain_genesis).build(); + let mut chunk_tail = 0; + for i in (1..10).chain(101..epoch_length * 6) { + env.produce_block(0, i); + let cur_chunk_tail = env.clients[0].chain.chain_store().chunk_tail().unwrap(); + assert!(cur_chunk_tail >= chunk_tail); + chunk_tail = cur_chunk_tail; + } +} + +#[test] +fn test_gc_execution_outcome() { + let epoch_length = 5; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let tx = SignedTransaction::send_money( + 1, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 100, + genesis_hash, + ); + let tx_hash = tx.get_hash(); + + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + for i in 1..epoch_length { + env.produce_block(0, i); + } + assert!(env.clients[0].chain.get_final_transaction_result(&tx_hash).is_ok()); + + for i in epoch_length..=epoch_length * 6 + 1 { + env.produce_block(0, i); + } + assert!(env.clients[0].chain.get_final_transaction_result(&tx_hash).is_err()); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_gc_after_state_sync() { + let epoch_length = 1024; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let mut env = TestEnv::builder(chain_genesis) + .clients_count(2) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + for i in 1..epoch_length * 4 + 2 { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + env.process_block(1, block, Provenance::NONE); + } + let sync_height = epoch_length * 4 + 1; + let sync_block = env.clients[0].chain.get_block_by_height(sync_height).unwrap(); + let sync_hash = *sync_block.hash(); + let prev_block_hash = *sync_block.header().prev_hash(); + // reset cache + for i in epoch_length * 3 - 1..sync_height - 1 { + let block_hash = *env.clients[0].chain.get_block_by_height(i).unwrap().hash(); + assert!(env.clients[1].chain.epoch_manager.get_epoch_start_height(&block_hash).is_ok()); + } + env.clients[1].chain.reset_data_pre_state_sync(sync_hash).unwrap(); + assert_eq!(env.clients[1].runtime_adapter.get_gc_stop_height(&sync_hash), 0); + // mimic what we do in possible_targets + assert!(env.clients[1].epoch_manager.get_epoch_id_from_prev_block(&prev_block_hash).is_ok()); + let tries = env.clients[1].runtime_adapter.get_tries(); + env.clients[1].chain.clear_data(tries, &Default::default()).unwrap(); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_process_block_after_state_sync() { + let epoch_length = 1024; + // test with shard_version > 0 + let mut genesis = Genesis::test_sharded_new_version( + vec!["test0".parse().unwrap(), "test1".parse().unwrap()], + 1, + vec![1], + ); + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + + let mut env = TestEnv::builder(chain_genesis) + .clients_count(1) + .use_state_snapshots() + .real_stores() + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let sync_height = epoch_length * 4 + 1; + for i in 1..=sync_height { + env.produce_block(0, i); + } + let sync_block = env.clients[0].chain.get_block_by_height(sync_height).unwrap(); + let sync_hash = *sync_block.hash(); + + let header = env.clients[0].chain.compute_state_response_header(0, sync_hash).unwrap(); + let state_root = header.chunk_prev_state_root(); + let sync_prev_header = env.clients[0].chain.get_previous_header(sync_block.header()).unwrap(); + let sync_prev_prev_hash = sync_prev_header.prev_hash(); + + let state_part = env.clients[0] + .runtime_adapter + .obtain_state_part(0, &sync_prev_prev_hash, &state_root, PartId::new(0, 1)) + .unwrap(); + // reset cache + for i in epoch_length * 3 - 1..sync_height - 1 { + let block_hash = *env.clients[0].chain.get_block_by_height(i).unwrap().hash(); + assert!(env.clients[0].chain.epoch_manager.get_epoch_start_height(&block_hash).is_ok()); + } + env.clients[0].chain.reset_data_pre_state_sync(sync_hash).unwrap(); + let epoch_id = env.clients[0].chain.get_block_header(&sync_hash).unwrap().epoch_id().clone(); + env.clients[0] + .runtime_adapter + .apply_state_part(0, &state_root, PartId::new(0, 1), &state_part, &epoch_id) + .unwrap(); + let block = env.clients[0].produce_block(sync_height + 1).unwrap().unwrap(); + env.clients[0].process_block_test(block.into(), Provenance::PRODUCED).unwrap(); +} + +#[test] +fn test_gc_fork_tail() { + let epoch_length = 101; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let mut env = TestEnv::builder(chain_genesis) + .clients_count(2) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let b1 = env.clients[0].produce_block(1).unwrap().unwrap(); + for i in 0..2 { + env.process_block(i, b1.clone(), Provenance::NONE); + } + // create 100 forks + for i in 2..102 { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + env.process_block(1, block, Provenance::NONE); + } + + let mut second_epoch_start = None; + for i in 102..epoch_length * DEFAULT_GC_NUM_EPOCHS_TO_KEEP + 5 { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + for j in 0..2 { + env.process_block(j, block.clone(), Provenance::NONE); + } + if second_epoch_start.is_none() && block.header().epoch_id() != &EpochId::default() { + second_epoch_start = Some(i); + } + } + let head = env.clients[1].chain.head().unwrap(); + assert!( + env.clients[1].runtime_adapter.get_gc_stop_height(&head.last_block_hash) > epoch_length + ); + let tail = env.clients[1].chain.chain_store().tail().unwrap(); + let fork_tail = env.clients[1].chain.chain_store().fork_tail().unwrap(); + assert!(tail <= fork_tail && fork_tail < second_epoch_start.unwrap()); +} + +#[test] +fn test_tx_forwarding() { + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = 100; + let mut env = TestEnv::builder(chain_genesis).clients_count(50).validator_seats(50).build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let genesis_hash = *genesis_block.hash(); + // forward to 2 chunk producers + assert_eq!( + env.clients[0].process_tx(SignedTransaction::empty(genesis_hash), false, false), + ProcessTxResponse::RequestRouted + ); + assert_eq!(env.network_adapters[0].requests.read().unwrap().len(), 4); +} + +#[test] +fn test_tx_forwarding_no_double_forwarding() { + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = 100; + let mut env = TestEnv::builder(chain_genesis).clients_count(50).validator_seats(50).build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let genesis_hash = *genesis_block.hash(); + // The transaction has already been forwarded, so it won't be forwarded again. + assert_eq!( + env.clients[0].process_tx(SignedTransaction::empty(genesis_hash), true, false), + ProcessTxResponse::NoResponse + ); + assert!(env.network_adapters[0].requests.read().unwrap().is_empty()); +} + +#[test] +fn test_tx_forward_around_epoch_boundary() { + let epoch_length = 4; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.num_block_producer_seats = 2; + genesis.config.num_block_producer_seats_per_shard = vec![2]; + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + chain_genesis.gas_limit = genesis.config.gas_limit; + let mut env = TestEnv::builder(chain_genesis) + .clients_count(3) + .validator_seats(2) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let tx = SignedTransaction::stake( + 1, + "test1".parse().unwrap(), + &signer, + TESTING_INIT_STAKE, + signer.public_key.clone(), + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + + for i in 1..epoch_length * 2 { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + for j in 0..3 { + if j != 1 { + let provenance = if j == 0 { Provenance::PRODUCED } else { Provenance::NONE }; + env.process_block(j, block.clone(), provenance); + } + } + } + let tx = SignedTransaction::send_money( + 1, + "test1".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + 1, + genesis_hash, + ); + assert_eq!(env.clients[2].process_tx(tx, false, false), ProcessTxResponse::RequestRouted); + let mut accounts_to_forward = HashSet::new(); + for request in env.network_adapters[2].requests.read().unwrap().iter() { + if let PeerManagerMessageRequest::NetworkRequests(NetworkRequests::ForwardTx( + account_id, + _, + )) = request + { + accounts_to_forward.insert(account_id.clone()); + } + } + assert_eq!( + accounts_to_forward, + HashSet::from_iter(vec!["test0".parse().unwrap(), "test1".parse().unwrap()]) + ); +} + +/// Blocks that have already been gc'ed should not be accepted again. +#[test] +fn test_not_resync_old_blocks() { + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let epoch_length = 5; + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let mut blocks = vec![]; + for i in 1..=epoch_length * (DEFAULT_GC_NUM_EPOCHS_TO_KEEP + 1) { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + blocks.push(block); + } + for i in 2..epoch_length { + let block = blocks[i as usize - 1].clone(); + assert!(env.clients[0].chain.get_block(block.hash()).is_err()); + let res = env.clients[0].process_block_test(block.into(), Provenance::NONE); + assert_matches!(res, Err(x) if matches!(x, Error::Orphan)); + assert_eq!(env.clients[0].chain.orphans_len(), 0); + } +} + +#[test] +fn test_gc_tail_update() { + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let epoch_length = 2; + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let mut env = TestEnv::builder(chain_genesis) + .clients_count(2) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let mut blocks = vec![]; + for i in 1..=epoch_length * (DEFAULT_GC_NUM_EPOCHS_TO_KEEP + 1) { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + blocks.push(block); + } + let headers = blocks.iter().map(|b| b.header().clone()).collect::>(); + env.clients[1].sync_block_headers(headers).unwrap(); + // simulate save sync hash block + let prev_sync_block = blocks[blocks.len() - 3].clone(); + let prev_sync_hash = *prev_sync_block.hash(); + let prev_sync_height = prev_sync_block.header().height(); + let sync_block = blocks[blocks.len() - 2].clone(); + env.clients[1].chain.reset_data_pre_state_sync(*sync_block.hash()).unwrap(); + env.clients[1].chain.save_block(prev_sync_block.into()).unwrap(); + let mut store_update = env.clients[1].chain.mut_chain_store().store_update(); + store_update.inc_block_refcount(&prev_sync_hash).unwrap(); + store_update.save_block(sync_block.clone()); + store_update.commit().unwrap(); + env.clients[1] + .chain + .reset_heads_post_state_sync( + &None, + *sync_block.hash(), + &mut BlockProcessingArtifact::default(), + Arc::new(|_| {}), + ) + .unwrap(); + env.process_block(1, blocks.pop().unwrap(), Provenance::NONE); + assert_eq!(env.clients[1].chain.chain_store().tail().unwrap(), prev_sync_height); +} + +/// Test that transaction does not become invalid when there is some gas price change. +#[test] +fn test_gas_price_change() { + init_test_logger(); + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let target_num_tokens_left = UNC_BASE / 10 + 1; + let transaction_costs = RuntimeConfig::test().fees; + + let send_money_total_gas = transaction_costs.fee(ActionCosts::transfer).send_fee(false) + + transaction_costs.fee(ActionCosts::new_action_receipt).send_fee(false) + + transaction_costs.fee(ActionCosts::transfer).exec_fee() + + transaction_costs.fee(ActionCosts::new_action_receipt).exec_fee(); + let min_gas_price = target_num_tokens_left / send_money_total_gas as u128; + let gas_limit = 1000000000000; + let gas_price_adjustment_rate = Ratio::new(1, 10); + + genesis.config.min_gas_price = min_gas_price; + genesis.config.gas_limit = gas_limit; + genesis.config.gas_price_adjustment_rate = gas_price_adjustment_rate; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let genesis_hash = *genesis_block.hash(); + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let tx = SignedTransaction::send_money( + 1, + "test1".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + TESTING_INIT_BALANCE + - target_num_tokens_left + - send_money_total_gas as u128 * min_gas_price, + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + env.produce_block(0, 1); + let tx = SignedTransaction::send_money( + 2, + "test1".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + 1, + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + for i in 2..=4 { + env.produce_block(0, i); + } +} + +#[test] +fn test_gas_price_overflow() { + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let min_gas_price = 1000000; + let max_gas_price = 10_u128.pow(20); + let gas_limit = 450000000000; + let gas_price_adjustment_rate = Ratio::from_integer(1); + genesis.config.min_gas_price = min_gas_price; + genesis.config.gas_limit = gas_limit; + genesis.config.gas_price_adjustment_rate = gas_price_adjustment_rate; + genesis.config.transaction_validity_period = 100000; + genesis.config.epoch_length = 43200; + genesis.config.max_gas_price = max_gas_price; + + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let genesis_hash = *genesis_block.hash(); + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + for i in 1..100 { + let tx = SignedTransaction::send_money( + i, + "test1".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + 1, + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + assert!(block.header().next_gas_price() <= max_gas_price); + env.process_block(0, block, Provenance::PRODUCED); + } +} + +#[test] +fn test_invalid_block_root() { + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + let mut b1 = env.clients[0].produce_block(1).unwrap().unwrap(); + let signer = create_test_signer("test0"); + b1.mut_header().get_mut().inner_lite.block_merkle_root = CryptoHash::default(); + b1.mut_header().resign(&signer); + let res = env.clients[0].process_block_test(b1.into(), Provenance::NONE); + assert_matches!(res.unwrap_err(), Error::InvalidBlockMerkleRoot); +} + +#[test] +fn test_incorrect_validator_key_produce_block() { + let genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 2); + let chain_genesis = ChainGenesis::new(&genesis); + + let mut env = TestEnv::builder(chain_genesis) + .clients_count(1) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .track_all_shards() + .build(); + + let res = env.clients[0].produce_block(1); + assert_matches!(res, Ok(None)); +} + +fn test_block_merkle_proof_with_len(n: NumBlocks, rng: &mut StdRng) { + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let mut blocks = vec![genesis_block.clone()]; + let mut cur_height = genesis_block.header().height() + 1; + while cur_height < n { + let should_fork = rng.gen_bool(0.5); + if should_fork { + let block = env.clients[0].produce_block(cur_height).unwrap().unwrap(); + let fork_block = env.clients[0].produce_block(cur_height + 1).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + let next_block = env.clients[0].produce_block(cur_height + 2).unwrap().unwrap(); + assert_eq!(next_block.header().prev_hash(), block.hash()); + // simulate blocks arriving in random order + if rng.gen_bool(0.5) { + env.process_block(0, fork_block, Provenance::PRODUCED); + env.process_block(0, next_block.clone(), Provenance::PRODUCED); + } else { + env.process_block(0, next_block.clone(), Provenance::PRODUCED); + env.process_block(0, fork_block, Provenance::PRODUCED); + } + blocks.push(block); + blocks.push(next_block); + cur_height += 3; + } else { + let block = env.clients[0].produce_block(cur_height).unwrap().unwrap(); + blocks.push(block.clone()); + env.process_block(0, block, Provenance::PRODUCED); + cur_height += 1; + } + } + + let head = blocks.pop().unwrap(); + let root = head.header().block_merkle_root(); + // verify that the mapping from block ordinal to block hash is correct + for h in 0..head.header().height() { + if let Ok(block) = env.clients[0].chain.get_block_by_height(h) { + let block_hash = *block.hash(); + let block_ordinal = env.clients[0] + .chain + .mut_chain_store() + .get_block_merkle_tree(&block_hash) + .unwrap() + .size(); + let block_hash1 = env.clients[0] + .chain + .mut_chain_store() + .get_block_hash_from_ordinal(block_ordinal) + .unwrap(); + assert_eq!(block_hash, block_hash1); + } + } + for block in blocks { + let proof = env.clients[0].chain.get_block_proof(block.hash(), head.hash()).unwrap(); + assert!(verify_hash(*root, &proof, *block.hash())); + } +} + +#[test] +fn test_block_merkle_proof() { + let mut rng = StdRng::seed_from_u64(0); + for i in 0..50 { + test_block_merkle_proof_with_len(i, &mut rng); + } +} + +#[test] +fn test_block_merkle_proof_same_hash() { + let env = TestEnv::builder(ChainGenesis::test()).build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let proof = + env.clients[0].chain.get_block_proof(genesis_block.hash(), genesis_block.hash()).unwrap(); + assert!(proof.is_empty()); +} + +#[test] +fn test_data_reset_before_state_sync() { + let mut genesis = Genesis::test(vec!["test0".parse().unwrap()], 1); + let epoch_length = 5; + genesis.config.epoch_length = epoch_length; + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let genesis_hash = *genesis_block.hash(); + let tx = SignedTransaction::create_account( + 1, + "test0".parse().unwrap(), + "test_account".parse().unwrap(), + UNC_BASE, + signer.public_key(), + &signer, + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + for i in 1..5 { + env.produce_block(0, i); + } + // check that the new account exists + let head = env.clients[0].chain.head().unwrap(); + let head_block = env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); + let response = env.clients[0] + .runtime_adapter + .query( + ShardUId::single_shard(), + &head_block.chunks()[0].prev_state_root(), + head.height, + 0, + &head.prev_block_hash, + &head.last_block_hash, + head_block.header().epoch_id(), + &QueryRequest::ViewAccount { account_id: "test_account".parse().unwrap() }, + ) + .unwrap(); + assert_matches!(response.kind, QueryResponseKind::ViewAccount(_)); + env.clients[0].chain.reset_data_pre_state_sync(*head_block.hash()).unwrap(); + // account should not exist after clearing state + let response = env.clients[0].runtime_adapter.query( + ShardUId::single_shard(), + &head_block.chunks()[0].prev_state_root(), + head.height, + 0, + &head.prev_block_hash, + &head.last_block_hash, + head_block.header().epoch_id(), + &QueryRequest::ViewAccount { account_id: "test_account".parse().unwrap() }, + ); + // TODO(#3742): ViewClient still has data in cache by current design. + assert!(response.is_ok()); +} + +#[test] +fn test_sync_hash_validity() { + let epoch_length = 5; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + for i in 1..19 { + env.produce_block(0, i); + } + for i in 0..19 { + let block_hash = *env.clients[0].chain.get_block_header_by_height(i).unwrap().hash(); + let res = env.clients[0].chain.check_sync_hash_validity(&block_hash); + println!("height {:?} -> {:?}", i, res); + assert_eq!(res.unwrap(), i == 0 || (i % epoch_length) == 1); + } + let bad_hash = CryptoHash::from_str("7tkzFg8RHBmMw1ncRJZCCZAizgq4rwCftTKYLce8RU8t").unwrap(); + let res = env.clients[0].chain.check_sync_hash_validity(&bad_hash); + println!("bad hash -> {:?}", res.is_ok()); + match res { + Ok(_) => assert!(false), + Err(e) => match e { + Error::DBNotFoundErr(_) => { /* the only expected error */ } + _ => assert!(false), + }, + } +} + +#[test] +fn test_block_height_processed_orphan() { + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + let block = env.clients[0].produce_block(1).unwrap().unwrap(); + let mut orphan_block = block; + let validator_signer = create_test_signer("test0"); + orphan_block.mut_header().get_mut().prev_hash = hash(&[1]); + orphan_block.mut_header().resign(&validator_signer); + let block_height = orphan_block.header().height(); + let res = env.clients[0].process_block_test(orphan_block.into(), Provenance::NONE); + assert_matches!(res.unwrap_err(), Error::Orphan); + assert!(env.clients[0].chain.mut_chain_store().is_height_processed(block_height).unwrap()); +} + +// Disabled until stateless validation release, because the test relies on +// logging which is impacted by the release process. +#[test] +#[cfg(not(feature = "nightly"))] +fn test_validate_chunk_extra() { + let mut capture = unc_o11y::testonly::TracingCapture::enable(); + + let epoch_length = 5; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let genesis_height = genesis_block.header().height(); + + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let tx = SignedTransaction::from_actions( + 1, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + vec![Action::DeployContract(DeployContractAction { + code: unc_test_contracts::rs_contract().to_vec(), + })], + *genesis_block.hash(), + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + let mut last_block = genesis_block; + for i in 1..3 { + last_block = env.clients[0].produce_block(i).unwrap().unwrap(); + env.process_block(0, last_block.clone(), Provenance::PRODUCED); + } + + // Construct a chunk that such when the receipts generated by this chunk are included + // in blocks of different heights, the state transitions are different. + let function_call_tx = SignedTransaction::from_actions( + 2, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "write_block_height".to_string(), + args: vec![], + gas: 100000000000000, + deposit: 0, + }))], + *last_block.hash(), + ); + assert_eq!( + env.clients[0].process_tx(function_call_tx, false, false), + ProcessTxResponse::ValidTx + ); + for i in 3..5 { + last_block = env.clients[0].produce_block(i).unwrap().unwrap(); + if i == 3 { + env.process_block(0, last_block.clone(), Provenance::PRODUCED); + } else { + let _ = env.clients[0] + .process_block_test_no_produce_chunk(last_block.clone().into(), Provenance::NONE) + .unwrap(); + } + } + + // Construct two blocks that contain the same chunk and make the chunk unavailable. + let validator_signer = create_test_signer("test0"); + let next_height = last_block.header().height() + 1; + let (encoded_chunk, merkle_paths, receipts) = + create_chunk_on_height(&mut env.clients[0], next_height); + let mut block1 = env.clients[0].produce_block(next_height).unwrap().unwrap(); + let mut block2 = env.clients[0].produce_block(next_height + 1).unwrap().unwrap(); + + // Process two blocks on two different forks that contain the same chunk. + for (i, block) in vec![&mut block1, &mut block2].into_iter().enumerate() { + let mut chunk_header = encoded_chunk.cloned_header(); + *chunk_header.height_included_mut() = i as BlockHeight + next_height; + let chunk_headers = vec![chunk_header]; + block.set_chunks(chunk_headers.clone()); + block.mut_header().get_mut().inner_rest.chunk_headers_root = + Block::compute_chunk_headers_root(&chunk_headers).0; + block.mut_header().get_mut().inner_rest.chunk_tx_root = + Block::compute_chunk_tx_root(&chunk_headers); + block.mut_header().get_mut().inner_rest.prev_chunk_outgoing_receipts_root = + Block::compute_chunk_prev_outgoing_receipts_root(&chunk_headers); + block.mut_header().get_mut().inner_lite.prev_state_root = + Block::compute_state_root(&chunk_headers); + block.mut_header().get_mut().inner_rest.chunk_mask = vec![true]; + block.mut_header().get_mut().inner_lite.prev_outcome_root = + Block::compute_outcome_root(block.chunks().iter()); + block.mut_header().get_mut().inner_rest.block_body_hash = + block.compute_block_body_hash().unwrap(); + block.mut_header().resign(&validator_signer); + let res = env.clients[0].process_block_test(block.clone().into(), Provenance::NONE); + assert_matches!(res.unwrap_err(), unc_chain::Error::ChunksMissing(_)); + } + + // Process the previously unavailable chunk. This causes two blocks to be + // accepted. Technically, the blocks are accepted concurrently, so we can + // observe either `block1 -> block2` reorg or `block2, block1` fork. We want + // to try to produce chunks on top of block1, so we force the reorg case + // using `capture` + + env.pause_block_processing(&mut capture, block2.hash()); + + let mut chain_store = + ChainStore::new(env.clients[0].chain.chain_store().store().clone(), genesis_height, true); + let chunk_header = encoded_chunk.cloned_header(); + let validator_id = env.clients[0].validator_signer.as_ref().unwrap().validator_id().clone(); + env.clients[0] + .persist_and_distribute_encoded_chunk(encoded_chunk, merkle_paths, receipts, validator_id) + .unwrap(); + env.clients[0].chain.blocks_with_missing_chunks.accept_chunk(&chunk_header.chunk_hash()); + env.clients[0].process_blocks_with_missing_chunks(Arc::new(|_| {})); + let accepted_blocks = env.clients[0].finish_block_in_processing(block1.hash()); + assert_eq!(accepted_blocks.len(), 1); + env.resume_block_processing(block2.hash()); + let accepted_blocks = env.clients[0].finish_block_in_processing(block2.hash()); + assert_eq!(accepted_blocks.len(), 1); + + // Produce a block on top of block1. + // Validate that result of chunk execution in `block1` is legit. + let b = env.clients[0].produce_block_on(next_height + 2, *block1.hash()).unwrap().unwrap(); + env.clients[0].process_block_test(b.into(), Provenance::PRODUCED).unwrap(); + let chunks = env.clients[0] + .get_chunk_headers_ready_for_inclusion(block1.header().epoch_id(), &block1.hash()); + let chunk_extra = + env.clients[0].chain.get_chunk_extra(block1.hash(), &ShardUId::single_shard()).unwrap(); + assert!(validate_chunk_with_chunk_extra( + &mut chain_store, + env.clients[0].epoch_manager.as_ref(), + block1.hash(), + &chunk_extra, + block1.chunks()[0].height_included(), + &chunks.get(&0).cloned().unwrap().0, + ) + .is_ok()); +} + +#[test] +fn test_catchup_gas_price_change() { + init_test_logger(); + let epoch_length = 5; + let min_gas_price = 10000; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.min_gas_price = min_gas_price; + genesis.config.gas_limit = 1000000000000; + let chain_genesis = ChainGenesis::new(&genesis); + + let mut env = TestEnv::builder(chain_genesis) + .clients_count(2) + .use_state_snapshots() + .real_stores() + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let mut blocks = vec![]; + for i in 1..3 { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + blocks.push(block.clone()); + env.process_block(0, block.clone(), Provenance::PRODUCED); + env.process_block(1, block, Provenance::NONE); + } + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + for i in 0..3 { + let tx = SignedTransaction::send_money( + i + 1, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 1, + *genesis_block.hash(), + ); + + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + for i in 3..=6 { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + blocks.push(block.clone()); + env.process_block(0, block.clone(), Provenance::PRODUCED); + tracing::error!("process_block:{i}:0"); + env.process_block(1, block, Provenance::NONE); + tracing::error!("process_block:{i}:1"); + } + + assert_ne!(blocks[3].header().next_gas_price(), blocks[4].header().next_gas_price()); + assert!(env.clients[1] + .chain + .get_chunk_extra(blocks[4].hash(), &ShardUId::single_shard()) + .is_err()); + + // Simulate state sync + let sync_hash = *blocks[5].hash(); + assert_ne!(blocks[4].header().epoch_id(), blocks[5].header().epoch_id()); + assert!(env.clients[0].chain.check_sync_hash_validity(&sync_hash).unwrap()); + let state_sync_header = env.clients[0].chain.get_state_response_header(0, sync_hash).unwrap(); + let num_parts = state_sync_header.num_state_parts(); + let state_sync_parts = (0..num_parts) + .map(|i| env.clients[0].chain.get_state_response_part(0, i, sync_hash).unwrap()) + .collect::>(); + + env.clients[1].chain.set_state_header(0, sync_hash, state_sync_header).unwrap(); + for i in 0..num_parts { + env.clients[1] + .chain + .set_state_part(0, sync_hash, PartId::new(i, num_parts), &state_sync_parts[i as usize]) + .unwrap(); + } + let rt = Arc::clone(&env.clients[1].runtime_adapter); + let f = move |msg: ApplyStatePartsRequest| { + let store = rt.store(); + + let shard_id = msg.shard_uid.shard_id as ShardId; + let mut store_update = store.store_update(); + assert!(rt + .get_flat_storage_manager() + .remove_flat_storage_for_shard(msg.shard_uid, &mut store_update) + .unwrap()); + store_update.commit().unwrap(); + for part_id in 0..msg.num_parts { + let key = borsh::to_vec(&StatePartKey(msg.sync_hash, shard_id, part_id)).unwrap(); + let part = store.get(DBCol::StateParts, &key).unwrap().unwrap(); + + rt.apply_state_part( + shard_id, + &msg.state_root, + PartId::new(part_id, msg.num_parts), + &part, + &msg.epoch_id, + ) + .unwrap(); + } + }; + env.clients[1].chain.schedule_apply_state_parts(0, sync_hash, num_parts, &f).unwrap(); + env.clients[1].chain.set_state_finalize(0, sync_hash, Ok(())).unwrap(); + let chunk_extra_after_sync = + env.clients[1].chain.get_chunk_extra(blocks[4].hash(), &ShardUId::single_shard()).unwrap(); + let expected_chunk_extra = + env.clients[0].chain.get_chunk_extra(blocks[4].hash(), &ShardUId::single_shard()).unwrap(); + // The chunk extra of the prev block of sync block should be the same as the node that it is syncing from + assert_eq!(chunk_extra_after_sync, expected_chunk_extra); +} + +#[test] +fn test_block_execution_outcomes() { + init_test_logger(); + + let epoch_length = 5; + let min_gas_price = 10000; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + genesis.config.min_gas_price = min_gas_price; + genesis.config.gas_limit = 1000000000000; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let mut tx_hashes = vec![]; + for i in 0..3 { + // send transaction to the same account to generate local receipts + let tx = SignedTransaction::send_money( + i + 1, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + 1, + *genesis_block.hash(), + ); + tx_hashes.push(tx.get_hash()); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + for i in 1..4 { + env.produce_block(0, i); + } + + let mut expected_outcome_ids = HashSet::new(); + let mut delayed_receipt_id = vec![]; + // Due to gas limit, the first two transaactions will create local receipts and they get executed + // in the same block. The last local receipt will become delayed receipt + for (i, id) in tx_hashes.into_iter().enumerate() { + let execution_outcome = env.clients[0].chain.get_execution_outcome(&id).unwrap(); + assert_eq!(execution_outcome.outcome_with_id.outcome.receipt_ids.len(), 1); + expected_outcome_ids.insert(id); + if i < 2 { + expected_outcome_ids.insert(execution_outcome.outcome_with_id.outcome.receipt_ids[0]); + } else { + delayed_receipt_id.push(execution_outcome.outcome_with_id.outcome.receipt_ids[0]) + } + } + let block = env.clients[0].chain.get_block_by_height(2).unwrap(); + let chunk = env.clients[0].chain.get_chunk(&block.chunks()[0].chunk_hash()).unwrap(); + assert_eq!(chunk.transactions().len(), 3); + let execution_outcomes_from_block = env.clients[0] + .chain + .chain_store() + .get_block_execution_outcomes(block.hash()) + .unwrap() + .remove(&0) + .unwrap(); + assert_eq!(execution_outcomes_from_block.len(), 5); + assert_eq!( + execution_outcomes_from_block + .into_iter() + .map(|execution_outcome| execution_outcome.outcome_with_id.id) + .collect::>(), + expected_outcome_ids + ); + + // Make sure the chunk outcomes contain the outcome from the delayed receipt. + let next_block = env.clients[0].chain.get_block_by_height(3).unwrap(); + let next_chunk = env.clients[0].chain.get_chunk(&next_block.chunks()[0].chunk_hash()).unwrap(); + assert!(next_chunk.transactions().is_empty()); + assert!(next_chunk.prev_outgoing_receipts().is_empty()); + let execution_outcomes_from_block = env.clients[0] + .chain + .chain_store() + .get_block_execution_outcomes(next_block.hash()) + .unwrap() + .remove(&0) + .unwrap(); + assert_eq!(execution_outcomes_from_block.len(), 1); + assert!(execution_outcomes_from_block[0].outcome_with_id.id == delayed_receipt_id[0]); +} + +// This test verifies that gas consumed for processing refund receipts is taken into account +// for the purpose of limiting the size of the chunk. +#[test] +fn test_refund_receipts_processing() { + init_test_logger(); + + let epoch_length = 5; + let min_gas_price = 10000; + let mut genesis = Genesis::test_sharded_new_version( + vec!["test0".parse().unwrap(), "test1".parse().unwrap()], + 1, + vec![1], + ); + genesis.config.epoch_length = epoch_length; + genesis.config.min_gas_price = min_gas_price; + // Set gas limit to be small enough to produce some delayed receipts, but large enough for + // transactions to get through. + genesis.config.gas_limit = 100_000_000; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let mut tx_hashes = vec![]; + // Send transactions to a non-existing account to generate refunds. + for i in 0..3 { + // Send transaction from the same account to generate local receipts. + let tx = SignedTransaction::send_money( + i + 1, + "test0".parse().unwrap(), + "random_account".parse().unwrap(), + &signer, + 1, + *genesis_block.hash(), + ); + tx_hashes.push(tx.get_hash()); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + // Make sure all transactions are processed. + for i in 3..16 { + env.produce_block(0, i); + } + + let test_shard_uid = ShardUId { version: 1, shard_id: 0 }; + for tx_hash in tx_hashes { + let tx_outcome = env.clients[0].chain.get_execution_outcome(&tx_hash).unwrap(); + assert_eq!(tx_outcome.outcome_with_id.outcome.receipt_ids.len(), 1); + if let ExecutionStatus::SuccessReceiptId(id) = tx_outcome.outcome_with_id.outcome.status { + let receipt_outcome = env.clients[0].chain.get_execution_outcome(&id).unwrap(); + assert_matches!( + receipt_outcome.outcome_with_id.outcome.status, + ExecutionStatus::Failure(TxExecutionError::ActionError(ActionError { + kind: ActionErrorKind::AccountDoesNotExist { .. }, + .. + })) + ); + let execution_outcomes_from_block = env.clients[0] + .chain + .chain_store() + .get_block_execution_outcomes(&receipt_outcome.block_hash) + .unwrap() + .remove(&0) + .unwrap(); + assert_eq!(execution_outcomes_from_block.len(), 1); + let chunk_extra = env.clients[0] + .chain + .get_chunk_extra(&receipt_outcome.block_hash, &test_shard_uid) + .unwrap() + .clone(); + assert!(chunk_extra.gas_used() >= chunk_extra.gas_limit()); + } else { + unreachable!("Transaction must succeed"); + } + } +} + +// Tests that the number of delayed receipts in each shard is bounded based on the gas limit of +// the chunk and any new receipts are not included if there are too many delayed receipts. +#[test] +fn test_delayed_receipt_count_limit() { + init_test_logger(); + + let epoch_length = 5; + let min_gas_price = 10000; + let mut genesis = Genesis::test_sharded_new_version(vec!["test0".parse().unwrap()], 1, vec![1]); + genesis.config.epoch_length = epoch_length; + genesis.config.min_gas_price = min_gas_price; + // Set gas limit to be small enough to produce some delayed receipts, but large enough for + // transactions to get through. + // This will result in delayed receipt count limit of 20. + let transaction_costs = RuntimeConfig::test().fees; + let chunk_gas_limit = 10 * transaction_costs.fee(ActionCosts::new_action_receipt).exec_fee(); + genesis.config.gas_limit = chunk_gas_limit; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + // Send enough transactions to saturate delayed receipts capacity. + let total_tx_count = 200usize; + for i in 0..total_tx_count { + let tx = SignedTransaction::from_actions( + (i + 1) as u64, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + vec![Action::DeployContract(DeployContractAction { code: vec![92; 10000] })], + *genesis_block.hash(), + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + let mut included_tx_count = 0; + let mut height = 1; + while included_tx_count < total_tx_count { + env.produce_block(0, height); + let block = env.clients[0].chain.get_block_by_height(height).unwrap(); + let chunk = env.clients[0].chain.get_chunk(&block.chunks()[0].chunk_hash()).unwrap(); + // These checks are useful to ensure that we didn't mess up the test setup. + assert!(chunk.prev_outgoing_receipts().len() <= 1); + assert!(chunk.transactions().len() <= 5); + + // Because all transactions are in the transactions pool, this means we have not included + // some transactions due to the delayed receipt count limit. + if included_tx_count > 0 && chunk.transactions().is_empty() { + break; + } + included_tx_count += chunk.transactions().len(); + height += 1; + } + assert!(included_tx_count < total_tx_count); +} + +#[test] +fn test_execution_metadata() { + // Prepare TestEnv with a very simple WASM contract. + let wasm_code = wat::parse_str( + r#" +(module + (import "env" "block_index" (func $block_index (result i64))) + (func (export "main") + (call $block_index) + drop + ) +)"#, + ) + .unwrap(); + + let mut env = { + let epoch_length = 5; + let mut genesis = + Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + deploy_test_contract(&mut env, "test0".parse().unwrap(), &wasm_code, epoch_length, 1); + env + }; + + // Call the contract and get the execution outcome. + let execution_outcome = env.call_main(&"test0".parse().unwrap()); + + // Now, let's assert that we get the cost breakdown we expect. + let config = RuntimeConfigStore::test().get_config(PROTOCOL_VERSION).clone(); + + // Total costs for creating a function call receipt. + let expected_receipt_cost = config.fees.fee(ActionCosts::new_action_receipt).execution + + config.fees.fee(ActionCosts::function_call_base).exec_fee() + + config.fees.fee(ActionCosts::function_call_byte).exec_fee() * "main".len() as u64; + + let expected_wasm_ops = match config.wasm_config.limit_config.contract_prepare_version { + unc_vm_runner::logic::ContractPrepareVersion::V0 => 2, + unc_vm_runner::logic::ContractPrepareVersion::V1 => 2, + // We spend two wasm instructions (call & drop), plus 8 ops for initializing function + // operand stack (8 bytes worth to hold the return value.) + unc_vm_runner::logic::ContractPrepareVersion::V2 => 10, + }; + + // Profile for what's happening *inside* wasm vm during function call. + let expected_profile = serde_json::json!([ + // Inside the contract, we called one host function. + { + "cost_category": "WASM_HOST_COST", + "cost": "BASE", + "gas_used": config.wasm_config.ext_costs.gas_cost(ExtCosts::base).to_string() + }, + // We include compilation costs into running the function. + { + "cost_category": "WASM_HOST_COST", + "cost": "CONTRACT_LOADING_BASE", + "gas_used": config.wasm_config.ext_costs.gas_cost(ExtCosts::contract_loading_base).to_string() + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "CONTRACT_LOADING_BYTES", + "gas_used": "18423750" + }, + { + "cost_category": "WASM_HOST_COST", + "cost": "WASM_INSTRUCTION", + "gas_used": (config.wasm_config.regular_op_cost as u64 * expected_wasm_ops).to_string() + } + ]); + let outcome = &execution_outcome.receipts_outcome[0].outcome; + let metadata = &outcome.metadata; + + let actual_profile = serde_json::to_value(&metadata.gas_profile).unwrap(); + assert_eq!(expected_profile, actual_profile); + + let actual_receipt_cost = outcome.gas_burnt + - metadata + .gas_profile + .clone() + .unwrap_or_default() + .into_iter() + .map(|it| it.gas_used) + .sum::(); + + assert_eq!(expected_receipt_cost, actual_receipt_cost) +} + +#[test] +fn test_epoch_protocol_version_change() { + init_test_logger(); + let epoch_length = 5; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 2); + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = PROTOCOL_VERSION - 1; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .clients_count(2) + .validator_seats(2) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + for i in 1..=16 { + let head = env.clients[0].chain.head().unwrap(); + let epoch_id = env.clients[0] + .epoch_manager + .get_epoch_id_from_prev_block(&head.last_block_hash) + .unwrap(); + let chunk_producer = + env.clients[0].epoch_manager.get_chunk_producer(&epoch_id, i, 0).unwrap(); + let index = if chunk_producer == "test0" { 0 } else { 1 }; + let (encoded_chunk, merkle_paths, receipts) = + create_chunk_on_height(&mut env.clients[index], i); + + for j in 0..2 { + let validator_id = + env.clients[j].validator_signer.as_ref().unwrap().validator_id().clone(); + env.clients[j] + .persist_and_distribute_encoded_chunk( + encoded_chunk.clone(), + merkle_paths.clone(), + receipts.clone(), + validator_id, + ) + .unwrap(); + } + + let epoch_id = env.clients[0] + .epoch_manager + .get_epoch_id_from_prev_block(&head.last_block_hash) + .unwrap(); + let block_producer = env.clients[0].epoch_manager.get_block_producer(&epoch_id, i).unwrap(); + let index = if block_producer == "test0" { 0 } else { 1 }; + let mut block = env.clients[index].produce_block(i).unwrap().unwrap(); + // upgrade to new protocol version but in the second epoch one node vote for the old version. + if i != 10 { + set_block_protocol_version(&mut block, block_producer.clone(), PROTOCOL_VERSION); + } + for j in 0..2 { + env.clients[j].process_block_test(block.clone().into(), Provenance::NONE).unwrap(); + } + } + let last_block = env.clients[0].chain.get_block_by_height(16).unwrap(); + let protocol_version = env.clients[0] + .epoch_manager + .get_epoch_protocol_version(last_block.header().epoch_id()) + .unwrap(); + assert_eq!(protocol_version, PROTOCOL_VERSION); +} + +#[test] +fn test_discard_non_finalizable_block() { + let genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let first_block = env.clients[0].produce_block(1).unwrap().unwrap(); + env.process_block(0, first_block.clone(), Provenance::PRODUCED); + // Produce, but not process test block on top of block (1). + let non_finalizable_block = env.clients[0].produce_block(6).unwrap().unwrap(); + env.clients[0] + .chain + .mut_chain_store() + .save_latest_known(LatestKnown { + height: first_block.header().height(), + seen: first_block.header().raw_timestamp(), + }) + .unwrap(); + + let second_block = env.clients[0].produce_block(2).unwrap().unwrap(); + env.process_block(0, second_block.clone(), Provenance::PRODUCED); + // Produce, but not process test block on top of block (2). + let finalizable_block = env.clients[0].produce_block(7).unwrap().unwrap(); + env.clients[0] + .chain + .mut_chain_store() + .save_latest_known(LatestKnown { + height: second_block.header().height(), + seen: second_block.header().raw_timestamp(), + }) + .unwrap(); + + // Produce and process two more blocks. + for i in 3..5 { + env.produce_block(0, i); + } + + assert_eq!(env.clients[0].chain.final_head().unwrap().height, 2); + // Check that the first test block can't be finalized, because it is produced behind final head. + assert_matches!( + env.clients[0] + .process_block_test(non_finalizable_block.into(), Provenance::NONE) + .unwrap_err(), + Error::CannotBeFinalized + ); + // Check that the second test block still can be finalized. + assert_matches!( + env.clients[0].process_block_test(finalizable_block.into(), Provenance::NONE), + Ok(_) + ); +} + +/// Final state should be consistent when a node switches between forks in the following scenario +/// /-----------h+2 +/// h-2 ---- h-1 ------ h +/// \------h+1 +/// even though from the perspective of h+2 the last final block is h-2. +#[test] +fn test_query_final_state() { + let epoch_length = 10; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let tx = SignedTransaction::send_money( + 1, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 100, + *genesis_block.hash(), + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + + let mut blocks = vec![]; + + for i in 1..5 { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + blocks.push(block.clone()); + env.process_block(0, block.clone(), Provenance::PRODUCED); + } + + let query_final_state = + |chain: &mut unc_chain::Chain, runtime: Arc, account_id: AccountId| { + let final_head = chain.chain_store().final_head().unwrap(); + let last_final_block = chain.get_block(&final_head.last_block_hash).unwrap(); + let response = runtime + .query( + ShardUId::single_shard(), + &last_final_block.chunks()[0].prev_state_root(), + last_final_block.header().height(), + last_final_block.header().raw_timestamp(), + &final_head.prev_block_hash, + last_final_block.hash(), + last_final_block.header().epoch_id(), + &QueryRequest::ViewAccount { account_id }, + ) + .unwrap(); + match response.kind { + QueryResponseKind::ViewAccount(account_view) => account_view, + _ => panic!("Wrong return value"), + } + }; + + let fork1_block = env.clients[0].produce_block(5).unwrap().unwrap(); + env.clients[0] + .chain + .mut_chain_store() + .save_latest_known(LatestKnown { + height: blocks.last().unwrap().header().height(), + seen: blocks.last().unwrap().header().raw_timestamp(), + }) + .unwrap(); + let fork2_block = env.clients[0].produce_block(6).unwrap().unwrap(); + assert_eq!(fork1_block.header().prev_hash(), fork2_block.header().prev_hash()); + env.process_block(0, fork1_block, Provenance::NONE); + assert_eq!(env.clients[0].chain.head().unwrap().height, 5); + + let runtime = env.clients[0].runtime_adapter.clone(); + let account_state1 = + query_final_state(&mut env.clients[0].chain, runtime.clone(), "test0".parse().unwrap()); + + env.process_block(0, fork2_block, Provenance::NONE); + assert_eq!(env.clients[0].chain.head().unwrap().height, 6); + + let runtime = env.clients[0].runtime_adapter.clone(); + let account_state2 = + query_final_state(&mut env.clients[0].chain, runtime.clone(), "test0".parse().unwrap()); + + assert_eq!(account_state1, account_state2); + assert!(account_state1.amount < TESTING_INIT_BALANCE - TESTING_INIT_STAKE); +} + +// Check that if the same receipt is executed twice in forked chain, both outcomes are recorded +// but child receipt ids are different. +#[test] +fn test_fork_receipt_ids() { + let (mut env, tx_hash) = prepare_env_with_transaction(); + + let produced_block = env.clients[0].produce_block(1).unwrap().unwrap(); + env.process_block(0, produced_block.clone(), Provenance::PRODUCED); + + // Construct two blocks that contain the same chunk and make the chunk unavailable. + let validator_signer = create_test_signer("test0"); + let last_height = produced_block.header().height(); + let (encoded_chunk, _, _) = create_chunk_on_height(&mut env.clients[0], last_height + 1); + let mut block1 = env.clients[0].produce_block(last_height + 1).unwrap().unwrap(); + let mut block2 = env.clients[0].produce_block(last_height + 2).unwrap().unwrap(); + + // Process two blocks on two different forks that contain the same chunk. + for block in vec![&mut block2, &mut block1].into_iter() { + let mut chunk_header = encoded_chunk.cloned_header(); + *chunk_header.height_included_mut() = block.header().height(); + let chunk_headers = vec![chunk_header]; + block.set_chunks(chunk_headers.clone()); + block.mut_header().get_mut().inner_rest.chunk_headers_root = + Block::compute_chunk_headers_root(&chunk_headers).0; + block.mut_header().get_mut().inner_rest.chunk_tx_root = + Block::compute_chunk_tx_root(&chunk_headers); + block.mut_header().get_mut().inner_rest.prev_chunk_outgoing_receipts_root = + Block::compute_chunk_prev_outgoing_receipts_root(&chunk_headers); + block.mut_header().get_mut().inner_lite.prev_state_root = + Block::compute_state_root(&chunk_headers); + block.mut_header().get_mut().inner_rest.chunk_mask = vec![true]; + block.mut_header().resign(&validator_signer); + env.clients[0].process_block_test(block.clone().into(), Provenance::NONE).unwrap(); + } + + // Ensure that in stateless validation protocol receipts in fork blocks are executed. + let block3 = env.clients[0].produce_block_on(last_height + 3, *block1.hash()).unwrap().unwrap(); + let block4 = env.clients[0].produce_block_on(last_height + 4, *block2.hash()).unwrap().unwrap(); + env.clients[0].process_block_test(block3.into(), Provenance::NONE).unwrap(); + env.clients[0].process_block_test(block4.into(), Provenance::NONE).unwrap(); + + let transaction_execution_outcome = + env.clients[0].chain.mut_chain_store().get_outcomes_by_id(&tx_hash).unwrap(); + assert_eq!(transaction_execution_outcome.len(), 2); + let receipt_id0 = transaction_execution_outcome[0].outcome_with_id.outcome.receipt_ids[0]; + let receipt_id1 = transaction_execution_outcome[1].outcome_with_id.outcome.receipt_ids[0]; + assert_ne!(receipt_id0, receipt_id1); +} + +// Check that in if receipt is executed twice in different forks, two execution +// outcomes are recorded, canonical chain outcome is correct and GC cleanups +// all outcomes. +#[test] +fn test_fork_execution_outcome() { + init_test_logger(); + + let (mut env, tx_hash) = prepare_env_with_transaction(); + + let mut last_height = 0; + for i in 1..3 { + let last_block = env.clients[0].produce_block(i).unwrap().unwrap(); + env.process_block(0, last_block.clone(), Provenance::PRODUCED); + last_height = last_block.header().height(); + } + + // Construct two blocks that contain the same chunk and make the chunk unavailable. + let validator_signer = create_test_signer("test0"); + let next_height = last_height + 1; + let (encoded_chunk, _, _) = create_chunk_on_height(&mut env.clients[0], next_height); + let mut block1 = env.clients[0].produce_block(last_height + 1).unwrap().unwrap(); + let mut block2 = env.clients[0].produce_block(last_height + 2).unwrap().unwrap(); + + // Process two blocks on two different forks that contain the same chunk. + for block in vec![&mut block2, &mut block1].into_iter() { + let mut chunk_header = encoded_chunk.cloned_header(); + *chunk_header.height_included_mut() = block.header().height(); + let chunk_headers = vec![chunk_header]; + block.set_chunks(chunk_headers.clone()); + block.mut_header().get_mut().inner_rest.chunk_headers_root = + Block::compute_chunk_headers_root(&chunk_headers).0; + block.mut_header().get_mut().inner_rest.chunk_tx_root = + Block::compute_chunk_tx_root(&chunk_headers); + block.mut_header().get_mut().inner_rest.prev_chunk_outgoing_receipts_root = + Block::compute_chunk_prev_outgoing_receipts_root(&chunk_headers); + block.mut_header().get_mut().inner_lite.prev_state_root = + Block::compute_state_root(&chunk_headers); + block.mut_header().get_mut().inner_rest.chunk_mask = vec![true]; + block.mut_header().resign(&validator_signer); + env.clients[0].process_block_test(block.clone().into(), Provenance::NONE).unwrap(); + } + + let block3 = env.clients[0].produce_block_on(last_height + 3, *block1.hash()).unwrap().unwrap(); + let block4 = env.clients[0].produce_block_on(last_height + 4, *block2.hash()).unwrap().unwrap(); + env.clients[0].process_block_test(block3.into(), Provenance::NONE).unwrap(); + env.clients[0].process_block_test(block4.into(), Provenance::NONE).unwrap(); + + let transaction_execution_outcome = + env.clients[0].chain.mut_chain_store().get_outcomes_by_id(&tx_hash).unwrap(); + assert_eq!(transaction_execution_outcome.len(), 1); + let receipt_id = transaction_execution_outcome[0].outcome_with_id.outcome.receipt_ids[0]; + let receipt_execution_outcomes = + env.clients[0].chain.mut_chain_store().get_outcomes_by_id(&receipt_id).unwrap(); + assert_eq!(receipt_execution_outcomes.len(), 2); + let canonical_chain_outcome = env.clients[0].chain.get_execution_outcome(&receipt_id).unwrap(); + assert_eq!(canonical_chain_outcome.block_hash, *block2.hash()); + + // Make sure that GC cleanups execution outcomes. + let epoch_length = env.clients[0].config.epoch_length; + for i in last_height + 5..last_height + 5 + epoch_length * 6 { + env.produce_block(0, i); + } + let transaction_execution_outcome = + env.clients[0].chain.chain_store().get_outcomes_by_id(&tx_hash).unwrap(); + assert!(transaction_execution_outcome.is_empty()); + let receipt_execution_outcomes = + env.clients[0].chain.chain_store().get_outcomes_by_id(&receipt_id).unwrap(); + assert!(receipt_execution_outcomes.is_empty()); +} + +fn prepare_env_with_transaction() -> (TestEnv, CryptoHash) { + let epoch_length = 5; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let tx = SignedTransaction::send_money( + 1, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 100, + *genesis_block.hash(), + ); + let tx_hash = tx.get_hash(); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + (env, tx_hash) +} + +#[test] +fn test_not_broadcast_block_on_accept() { + let epoch_length = 5; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let network_adapter = Arc::new(MockPeerManagerAdapter::default()); + let mut env = TestEnv::builder(ChainGenesis::test()) + .clients_count(2) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .network_adapters(vec![ + Arc::new(MockPeerManagerAdapter::default()), + network_adapter.clone(), + ]) + .build(); + let b1 = env.clients[0].produce_block(1).unwrap().unwrap(); + for i in 0..2 { + env.process_block(i, b1.clone(), Provenance::NONE); + } + assert!(network_adapter.requests.read().unwrap().is_empty()); +} + +#[test] +fn test_header_version_downgrade() { + init_test_logger(); + + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = 5; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let validator_signer = create_test_signer("test0"); + for i in 1..10 { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + env.process_block(0, block, Provenance::NONE); + } + let block = { + let mut block = env.clients[0].produce_block(10).unwrap().unwrap(); + // Convert header to BlockHeaderV1 + let mut header_view: BlockHeaderView = block.header().clone().into(); + header_view.latest_protocol_version = 1; + let mut header = header_view.into(); + + // BlockHeaderV1, but protocol version is newest + match &mut header { + BlockHeader::BlockHeaderV1(header) => { + let header = Arc::make_mut(header); + header.inner_rest.latest_protocol_version = PROTOCOL_VERSION; + let (hash, signature) = validator_signer.sign_block_header_parts( + header.prev_hash, + &borsh::to_vec(&header.inner_lite).expect("Failed to serialize"), + &borsh::to_vec(&header.inner_rest).expect("Failed to serialize"), + ); + header.hash = hash; + header.signature = signature; + } + _ => { + unreachable!(); + } + } + *block.mut_header() = header; + block + }; + let res = env.clients[0].process_block_test(block.into(), Provenance::NONE); + assert!(!res.is_ok()); +} + +#[test] +#[should_panic( + expected = "The client protocol version is older than the protocol version of the network" +)] +fn test_node_shutdown_with_old_protocol_version() { + let epoch_length = 5; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let validator_signer = create_test_signer("test0"); + for i in 1..=5 { + let mut block = env.clients[0].produce_block(i).unwrap().unwrap(); + block.mut_header().get_mut().inner_rest.latest_protocol_version = PROTOCOL_VERSION + 1; + block.mut_header().resign(&validator_signer); + env.process_block(0, block, Provenance::NONE); + } + for i in 6..=10 { + env.produce_block(0, i); + } + env.produce_block(0, 11); +} + +#[test] +fn test_block_ordinal() { + let mut env = TestEnv::builder(ChainGenesis::test()).build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + assert_eq!(genesis_block.header().block_ordinal(), 1); + let mut ordinal = 1; + + // Test no skips + for i in 1..=5 { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + ordinal += 1; + assert_eq!(block.header().block_ordinal(), ordinal); + } + + // Test skips + for i in 1..=5 { + let block = env.clients[0].produce_block(i * 10).unwrap().unwrap(); + env.process_block(0, block.clone(), Provenance::PRODUCED); + ordinal += 1; + assert_eq!(block.header().block_ordinal(), ordinal); + } + + // Test forks + let last_block = env.clients[0].produce_block(99).unwrap().unwrap(); + env.process_block(0, last_block.clone(), Provenance::PRODUCED); + ordinal += 1; + assert_eq!(last_block.header().block_ordinal(), ordinal); + let fork1_block = env.clients[0].produce_block(100).unwrap().unwrap(); + env.clients[0] + .chain + .mut_chain_store() + .save_latest_known(LatestKnown { + height: last_block.header().height(), + seen: last_block.header().raw_timestamp(), + }) + .unwrap(); + let fork2_block = env.clients[0].produce_block(101).unwrap().unwrap(); + assert_eq!(fork1_block.header().prev_hash(), fork2_block.header().prev_hash()); + env.process_block(0, fork1_block.clone(), Provenance::NONE); + let next_block = env.clients[0].produce_block(102).unwrap().unwrap(); + assert_eq!(next_block.header().prev_hash(), fork1_block.header().hash()); + env.process_block(0, fork2_block.clone(), Provenance::NONE); + ordinal += 1; + let fork_ordinal = ordinal - 1; + assert_eq!(fork1_block.header().block_ordinal(), ordinal); + assert_eq!(fork2_block.header().block_ordinal(), ordinal); + assert_eq!(env.clients[0].chain.head().unwrap().height, fork2_block.header().height()); + // Next block on top of fork + env.process_block(0, next_block.clone(), Provenance::PRODUCED); + ordinal += 1; + assert_eq!(env.clients[0].chain.head().unwrap().height, next_block.header().height()); + assert_eq!(next_block.header().block_ordinal(), ordinal); + + // make sure that the old ordinal maps to what is on the canonical chain + let fork_ordinal_block_hash = + env.clients[0].chain.mut_chain_store().get_block_hash_from_ordinal(fork_ordinal).unwrap(); + assert_eq!(fork_ordinal_block_hash, *fork1_block.hash()); +} + +#[test] +fn test_congestion_receipt_execution() { + let (mut env, tx_hashes) = prepare_env_with_congestion(PROTOCOL_VERSION, None, 3); + + // Produce block with no new chunk. + env.produce_block(0, 3); + let height = 4; + env.produce_block(0, height); + let prev_block = env.clients[0].chain.get_block_by_height(height).unwrap(); + let chunk_extra = + env.clients[0].chain.get_chunk_extra(prev_block.hash(), &ShardUId::single_shard()).unwrap(); + assert!(chunk_extra.gas_used() >= chunk_extra.gas_limit()); + let state_update = env.clients[0] + .runtime_adapter + .get_tries() + .new_trie_update(ShardUId::single_shard(), *chunk_extra.state_root()); + let delayed_indices: DelayedReceiptIndices = + get(&state_update, &TrieKey::DelayedReceiptIndices).unwrap().unwrap(); + assert!(delayed_indices.next_available_index > 0); + let mut block = env.clients[0].produce_block(height + 1).unwrap().unwrap(); + testlib::process_blocks::set_no_chunk_in_block(&mut block, &prev_block); + env.process_block(0, block.clone(), Provenance::NONE); + + // let all receipts finish + for i in height + 2..height + 7 { + env.produce_block(0, i); + } + + for tx_hash in &tx_hashes { + let final_outcome = env.clients[0].chain.get_final_transaction_result(tx_hash).unwrap(); + assert_matches!(final_outcome.status, FinalExecutionStatus::SuccessValue(_)); + + // Check that all receipt ids have corresponding execution outcomes. This means that all receipts generated are executed. + let transaction_outcome = env.clients[0].chain.get_execution_outcome(tx_hash).unwrap(); + let mut receipt_ids: VecDeque<_> = + transaction_outcome.outcome_with_id.outcome.receipt_ids.into(); + while !receipt_ids.is_empty() { + let receipt_id = receipt_ids.pop_front().unwrap(); + let receipt_outcome = env.clients[0].chain.get_execution_outcome(&receipt_id).unwrap(); + match receipt_outcome.outcome_with_id.outcome.status { + ExecutionStatus::SuccessValue(_) | ExecutionStatus::SuccessReceiptId(_) => {} + ExecutionStatus::Failure(_) | ExecutionStatus::Unknown => { + panic!("unexpected receipt execution outcome") + } + } + receipt_ids.extend(receipt_outcome.outcome_with_id.outcome.receipt_ids); + } + } +} + +#[test] +fn test_validator_stake_host_function() { + init_test_logger(); + let epoch_length = 5; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let block_height = deploy_test_contract( + &mut env, + "test0".parse().unwrap(), + unc_test_contracts::rs_contract(), + epoch_length, + 1, + ); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let signed_transaction = SignedTransaction::from_actions( + 10, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "ext_validator_stake".to_string(), + args: b"test0".to_vec(), + gas: 100_000_000_000_000, + deposit: 0, + }))], + *genesis_block.hash(), + ); + assert_eq!( + env.clients[0].process_tx(signed_transaction, false, false), + ProcessTxResponse::ValidTx + ); + for i in 0..3 { + env.produce_block(0, block_height + i); + } +} + +#[test] +/// Test that if a node's shard assignment will not change in the next epoch, the node +/// does not need to catch up. +fn test_catchup_no_sharding_change() { + init_integration_logger(); + let mut genesis = Genesis::test(vec!["test0".parse().unwrap()], 1); + genesis.config.epoch_length = 5; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .clients_count(1) + .validator_seats(1) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + // run the chain to a few epochs and make sure no catch up is triggered and the chain still + // functions + for h in 1..20 { + let block = env.clients[0].produce_block(h).unwrap().unwrap(); + let _ = + env.clients[0].process_block_test(block.clone().into(), Provenance::PRODUCED).unwrap(); + assert_eq!(env.clients[0].chain.chain_store().iterate_state_sync_infos().unwrap(), vec![]); + assert_eq!( + env.clients[0] + .chain + .chain_store() + .get_blocks_to_catchup(block.header().prev_hash()) + .unwrap(), + vec![] + ); + } +} + +/// Run `gc_num_epochs_to_keep` epochs + several blocks. +/// Start a second env from the "snapshot" of the first. +/// Run one more epoch. +/// "Restart from the snapshot" is to ensure that we can continue producing blocks without relying on caches. +#[test] +fn test_long_chain_with_restart_from_snapshot() { + init_test_logger(); + + let epoch_length = 25; + + let mut genesis = Genesis::test(vec!["test0".parse().unwrap()], 1); + + genesis.config.epoch_length = epoch_length; + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let mut env1 = TestEnv::builder(chain_genesis.clone()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .archive(false) + .build(); + + // In TestEnv `gc_blocks_limit` defaults to 100. + // That means that whole epoch will be garbage collected in one block. + // We want this test to have "more realistic" snapshot setup, where last kept epoch is only partially garbage collected. + // + // `gc_blocks_limit = 2` is the default setup in production. + env1.clients[0].config.gc.gc_blocks_limit = 2; + + let max_height = epoch_length * env1.clients[0].config.gc.gc_num_epochs_to_keep + 3; + + for h in 1..max_height { + let block = env1.clients[0].produce_block(h).unwrap().unwrap(); + env1.process_block(0, block.clone(), Provenance::PRODUCED); + } + + let mut env2 = TestEnv::builder(chain_genesis) + .stores(vec![env1.clients[0].chain.chain_store().store().clone()]) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .archive(false) + .build(); + + for h in max_height..(max_height + epoch_length) { + let block = env2.clients[0].produce_block(h).unwrap().unwrap(); + env2.process_block(0, block.clone(), Provenance::PRODUCED); + } +} + +/// These tests fail on aarch because the WasmtimeVM::precompile method doesn't populate the cache. +mod contract_precompilation_tests { + use super::*; + use unc_primitives::test_utils::MockEpochInfoProvider; + use unc_primitives::views::ViewApplyState; + use unc_store::{StoreCompiledContractCache, TrieUpdate}; + use unc_vm_runner::logic::CompiledContractCache; + use unc_vm_runner::{get_contract_cache_key, ContractCode}; + use node_runtime::state_viewer::TrieViewer; + + const EPOCH_LENGTH: u64 = 25; + + fn state_sync_on_height(env: &TestEnv, height: BlockHeight) { + let sync_block = env.clients[0].chain.get_block_by_height(height).unwrap(); + let sync_hash = *sync_block.hash(); + let state_sync_header = + env.clients[0].chain.get_state_response_header(0, sync_hash).unwrap(); + let state_root = state_sync_header.chunk_prev_state_root(); + let epoch_id = + env.clients[0].chain.get_block_header(&sync_hash).unwrap().epoch_id().clone(); + let sync_prev_header = + env.clients[0].chain.get_previous_header(sync_block.header()).unwrap(); + let sync_prev_prev_hash = sync_prev_header.prev_hash(); + let state_part = env.clients[0] + .runtime_adapter + .obtain_state_part(0, &sync_prev_prev_hash, &state_root, PartId::new(0, 1)) + .unwrap(); + env.clients[1] + .runtime_adapter + .apply_state_part(0, &state_root, PartId::new(0, 1), &state_part, &epoch_id) + .unwrap(); + } + + #[test] + #[cfg_attr(all(target_arch = "aarch64", target_vendor = "apple"), ignore)] + fn test_sync_and_call_cached_contract() { + init_integration_logger(); + let num_clients = 2; + let mut genesis = + Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = EPOCH_LENGTH; + + let mut env = TestEnv::builder(ChainGenesis::test()) + .clients_count(num_clients) + .use_state_snapshots() + .real_stores() + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let start_height = 1; + + // Process test contract deployment on the first client. + let wasm_code = unc_test_contracts::rs_contract().to_vec(); + let height = deploy_test_contract( + &mut env, + "test0".parse().unwrap(), + &wasm_code, + EPOCH_LENGTH + 1, + start_height, + ); + + // Perform state sync for the second client. + state_sync_on_height(&mut env, height - 1); + + // Check existence of contract in both caches. + let mut caches: Vec = env + .clients + .iter() + .map(|client| StoreCompiledContractCache::new(client.chain.chain_store().store())) + .collect(); + let contract_code = ContractCode::new(wasm_code.clone(), None); + let epoch_id = env.clients[0] + .chain + .get_block_by_height(height - 1) + .unwrap() + .header() + .epoch_id() + .clone(); + let runtime_config = env.get_runtime_config(0, epoch_id); + let key = get_contract_cache_key(&contract_code, &runtime_config.wasm_config); + for i in 0..num_clients { + caches[i] + .get(&key) + .unwrap_or_else(|_| panic!("Failed to get cached result for client {}", i)) + .unwrap_or_else(|| { + panic!("Compilation result should be non-empty for client {}", i) + }); + } + + // Check that contract function may be successfully called on the second client. + // Note that we can't test that behaviour is the same on two clients, because + // compile_module_cached_wasmer0 is cached by contract key via macro. + let block = env.clients[0].chain.get_block_by_height(EPOCH_LENGTH).unwrap(); + let chunk_extra = + env.clients[0].chain.get_chunk_extra(block.hash(), &ShardUId::single_shard()).unwrap(); + let state_root = *chunk_extra.state_root(); + + let viewer = TrieViewer::default(); + // TODO (#7327): set use_flat_storage to true when we implement support for state sync for FlatStorage + let trie = env.clients[1] + .runtime_adapter + .get_trie_for_shard(0, block.header().prev_hash(), state_root, false) + .unwrap(); + let state_update = TrieUpdate::new(trie); + + let mut logs = vec![]; + let view_state = ViewApplyState { + block_height: EPOCH_LENGTH, + prev_block_hash: *block.header().prev_hash(), + block_hash: *block.hash(), + epoch_id: block.header().epoch_id().clone(), + epoch_height: 1, + block_timestamp: block.header().raw_timestamp(), + current_protocol_version: PROTOCOL_VERSION, + cache: Some(Box::new(caches.swap_remove(1))), + }; + viewer + .call_function( + state_update, + view_state, + &"test0".parse().unwrap(), + "log_something", + &[], + &mut logs, + &MockEpochInfoProvider::default(), + ) + .unwrap(); + } + + #[test] + #[cfg_attr(all(target_arch = "aarch64", target_vendor = "apple"), ignore)] + fn test_two_deployments() { + let num_clients = 2; + let mut genesis = + Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = EPOCH_LENGTH; + + let mut env = TestEnv::builder(ChainGenesis::test()) + .clients_count(num_clients) + .use_state_snapshots() + .real_stores() + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let mut height = 1; + + // Process tiny contract deployment on the first client. + let tiny_wasm_code = unc_test_contracts::trivial_contract().to_vec(); + height = deploy_test_contract( + &mut env, + "test0".parse().unwrap(), + &tiny_wasm_code, + EPOCH_LENGTH, + height, + ); + + // Wait 3 epochs. + height = produce_blocks_from_height(&mut env, 3 * EPOCH_LENGTH, height); + + // Process test contract deployment on the first client. + let wasm_code = unc_test_contracts::rs_contract().to_vec(); + height = deploy_test_contract( + &mut env, + "test0".parse().unwrap(), + &wasm_code, + EPOCH_LENGTH + 1, + height, + ); + + // Perform state sync for the second client on the last produced height. + state_sync_on_height(&mut env, height - 1); + + let caches: Vec = env + .clients + .iter() + .map(|client| StoreCompiledContractCache::new(client.chain.chain_store().store())) + .collect(); + let epoch_id = env.clients[0] + .chain + .get_block_by_height(height - 1) + .unwrap() + .header() + .epoch_id() + .clone(); + let runtime_config = env.get_runtime_config(0, epoch_id); + let tiny_contract_key = get_contract_cache_key( + &ContractCode::new(tiny_wasm_code.clone(), None), + &runtime_config.wasm_config, + ); + let test_contract_key = get_contract_cache_key( + &ContractCode::new(wasm_code.clone(), None), + &runtime_config.wasm_config, + ); + + // Check that both deployed contracts are presented in cache for client 0. + assert!(caches[0].get(&tiny_contract_key).unwrap().is_some()); + assert!(caches[0].get(&test_contract_key).unwrap().is_some()); + + // Check that only last contract is presented in cache for client 1. + assert!(caches[1].get(&tiny_contract_key).unwrap().is_none()); + assert!(caches[1].get(&test_contract_key).unwrap().is_some()); + } + + #[test] + #[cfg_attr(all(target_arch = "aarch64", target_vendor = "apple"), ignore)] + fn test_sync_after_delete_account() { + init_test_logger(); + let num_clients = 3; + let mut genesis = Genesis::test( + vec!["test0".parse().unwrap(), "test1".parse().unwrap(), "test2".parse().unwrap()], + 1, + ); + genesis.config.epoch_length = EPOCH_LENGTH; + + let mut env = TestEnv::builder(ChainGenesis::test()) + .clients_count(num_clients) + .use_state_snapshots() + .real_stores() + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let mut height = 1; + + // Process test contract deployment on the first client. + let wasm_code = unc_test_contracts::rs_contract().to_vec(); + height = deploy_test_contract( + &mut env, + "test2".parse().unwrap(), + &wasm_code, + EPOCH_LENGTH, + height, + ); + + // Delete account on which test contract is stored. + let block = env.clients[0].chain.get_block_by_height(height - 1).unwrap(); + let signer = InMemorySigner::from_seed("test2".parse().unwrap(), KeyType::ED25519, "test2"); + let delete_account_tx = SignedTransaction::delete_account( + 2, + "test2".parse().unwrap(), + "test2".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + *block.hash(), + ); + assert_eq!( + env.clients[0].process_tx(delete_account_tx, false, false), + ProcessTxResponse::ValidTx + ); + height = produce_blocks_from_height(&mut env, EPOCH_LENGTH + 1, height); + + // Perform state sync for the second client. + state_sync_on_height(&mut env, height - 1); + + let caches: Vec = env + .clients + .iter() + .map(|client| StoreCompiledContractCache::new(client.chain.chain_store().store())) + .collect(); + + let epoch_id = env.clients[0] + .chain + .get_block_by_height(height - 1) + .unwrap() + .header() + .epoch_id() + .clone(); + let runtime_config = env.get_runtime_config(0, epoch_id); + let contract_key = get_contract_cache_key( + &ContractCode::new(wasm_code.clone(), None), + &runtime_config.wasm_config, + ); + + // Check that contract is cached for client 0 despite account deletion. + assert!(caches[0].get(&contract_key).unwrap().is_some()); + + // Check that contract is not cached for client 1 because of late state sync. + assert!(caches[1].get(&contract_key).unwrap().is_none()); + } +} diff --git a/integration-tests/src/tests/client/resharding.rs b/integration-tests/src/tests/client/resharding.rs new file mode 100644 index 000000000..d066c8cbc --- /dev/null +++ b/integration-tests/src/tests/client/resharding.rs @@ -0,0 +1,1674 @@ +use crate::tests::client::process_blocks::set_block_protocol_version; +use assert_matches::assert_matches; +use unc_chain::unc_chain_primitives::Error; +use unc_chain::test_utils::wait_for_all_blocks_in_processing; +use unc_chain::{ChainGenesis, ChainStoreAccess, Provenance}; +use unc_chain_configs::Genesis; +use unc_client::test_utils::{run_catchup, TestEnv}; +use unc_client::{Client, ProcessTxResponse}; +use unc_crypto::{InMemorySigner, KeyType, Signer}; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::account::id::AccountId; +use unc_primitives::block::{Block, Tip}; +use unc_primitives::epoch_manager::{AllEpochConfig, AllEpochConfigTestOverrides, EpochConfig}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::serialize::to_base64; +use unc_primitives::shard_layout::{account_id_to_shard_id, account_id_to_shard_uid, ShardLayout}; +use unc_primitives::transaction::{ + Action, DeployContractAction, FunctionCallAction, SignedTransaction, +}; +use unc_primitives::types::{BlockHeight, NumShards, ProtocolVersion, ShardId}; +use unc_primitives::utils::MaybeValidated; +use unc_primitives::version::ProtocolFeature; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_primitives::views::{ExecutionStatusView, FinalExecutionStatus, QueryRequest}; +use unc_primitives_core::num_rational::Rational32; +use unc_store::flat::FlatStorageStatus; +use unc_store::test_utils::{gen_account, gen_unique_accounts}; +use unc_store::trie::SnapshotError; +use unc_store::{DBCol, ShardUId}; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; +use framework::UNC_BASE; +use rand::rngs::StdRng; +use rand::seq::{IteratorRandom, SliceRandom}; +use rand::{Rng, SeedableRng}; +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::sync::Arc; +use tracing::debug; + +const SIMPLE_NIGHTSHADE_PROTOCOL_VERSION: ProtocolVersion = + ProtocolFeature::SimpleNightshade.protocol_version(); + +const SIMPLE_NIGHTSHADE_V2_PROTOCOL_VERSION: ProtocolVersion = + ProtocolFeature::SimpleNightshadeV2.protocol_version(); + +const P_CATCHUP: f64 = 0.2; + +#[derive(Clone, Copy)] +enum ReshardingType { + // In the V0->V1 resharding outgoing receipts are reassigned to receiver. + V1, + // In the V1->V2 resharding outgoing receipts are reassigned to lowest index child. + V2, +} + +fn get_target_protocol_version(resharding_type: &ReshardingType) -> ProtocolVersion { + match resharding_type { + ReshardingType::V1 => SIMPLE_NIGHTSHADE_PROTOCOL_VERSION, + ReshardingType::V2 => SIMPLE_NIGHTSHADE_V2_PROTOCOL_VERSION, + } +} + +fn get_genesis_protocol_version(resharding_type: &ReshardingType) -> ProtocolVersion { + match resharding_type { + ReshardingType::V1 => SIMPLE_NIGHTSHADE_PROTOCOL_VERSION - 1, + ReshardingType::V2 => SIMPLE_NIGHTSHADE_V2_PROTOCOL_VERSION - 1, + } +} + +fn get_parent_shard_uids(resharding_type: &ReshardingType) -> Vec { + let shard_layout = match resharding_type { + ReshardingType::V1 => ShardLayout::v0_single_shard(), + ReshardingType::V2 => ShardLayout::get_simple_nightshade_layout(), + }; + shard_layout.shard_uids().collect() +} + +// Return the expected number of shards. +fn get_expected_shards_num( + epoch_length: u64, + height: BlockHeight, + resharding_type: &ReshardingType, +) -> u64 { + match resharding_type { + ReshardingType::V1 => { + if height <= 2 * epoch_length { + return 1; + } else { + return 4; + } + } + ReshardingType::V2 => { + if height <= 2 * epoch_length { + return 4; + } else { + return 5; + } + } + }; +} + +/// The condition that determines if a chunk should be produced of dropped. +/// Can be probabilistic, predefined based on height and shard id or both. +struct DropChunkCondition { + probability: f64, + by_height_shard_id: HashSet<(BlockHeight, ShardId)>, +} + +impl DropChunkCondition { + fn should_drop_chunk( + &self, + rng: &mut StdRng, + height: BlockHeight, + shard_ids: &Vec, + ) -> bool { + if rng.gen_bool(self.probability) { + return true; + } + + // One chunk producer may be responsible for producing multiple chunks. + // Ensure that the by_height_shard_id is consistent in that it doesn't + // try to produce one chunk and drop another chunk, produced by the same + // chunk producer. + // It shouldn't happen in setups where num_validators >= num_shards. + let mut all_true = true; + let mut all_false = true; + for shard_id in shard_ids { + match self.by_height_shard_id.contains(&(height, *shard_id)) { + true => all_false = false, + false => all_true = false, + } + } + if all_false { + return false; + } + if all_true { + return true; + } + + tracing::warn!("Inconsistent test setup. Chunk producer configured to produce one of its chunks and to skip another. This is not supported, skipping all. height {} shard_ids {:?}", height, shard_ids); + return true; + } + + /// Returns a DropChunkCondition that doesn't drop any chunks. + fn new() -> Self { + Self { probability: 0.0, by_height_shard_id: HashSet::new() } + } + + fn with_probability(probability: f64) -> Self { + Self { probability, by_height_shard_id: HashSet::new() } + } + + fn with_by_height_shard_id(by_height_shard_id: HashSet<(u64, ShardId)>) -> Self { + Self { probability: 0.0, by_height_shard_id: by_height_shard_id } + } +} + +/// Test environment prepared for testing the sharding upgrade. +/// Epoch 0, blocks 1-5 : genesis shard layout +/// Epoch 1, blocks 6-10 : genesis shard layout, resharding happens +/// Epoch 2: blocks 10-15: target shard layout, shard layout is upgraded +/// Epoch 3: blocks 16-20: target shard layout, +/// +/// Note: if the test is extended to more epochs, garbage collection will +/// kick in and delete data that is checked at the end of the test. +struct TestReshardingEnv { + env: TestEnv, + initial_accounts: Vec, + init_txs: Vec, + txs_by_height: BTreeMap>, + epoch_length: u64, + num_clients: usize, + rng: StdRng, + resharding_type: Option, +} + +impl TestReshardingEnv { + fn new( + epoch_length: u64, + num_validators: usize, + num_clients: usize, + num_init_accounts: usize, + gas_limit: Option, + genesis_protocol_version: ProtocolVersion, + rng_seed: u64, + state_snapshot_enabled: bool, + resharding_type: Option, + ) -> Self { + let mut rng = SeedableRng::seed_from_u64(rng_seed); + let validators: Vec = + (0..num_validators).map(|i| format!("test{}", i).parse().unwrap()).collect(); + let other_accounts = gen_unique_accounts(&mut rng, num_init_accounts, num_init_accounts); + let initial_accounts = [validators, other_accounts].concat(); + let genesis = setup_genesis( + epoch_length, + num_validators as u64, + initial_accounts.clone(), + gas_limit, + genesis_protocol_version, + ); + let chain_genesis = ChainGenesis::new(&genesis); + let builder = if state_snapshot_enabled { + TestEnv::builder(chain_genesis).use_state_snapshots() + } else { + TestEnv::builder(chain_genesis) + }; + // Set the kickout thresholds to zero. In some tests we have chunk + // producers missing chunks but we don't want any of the clients to get + // kicked. + // One of the reasons for not kicking is that the current test infra + // doesn't support requesting chunks and non-validators wouldn't be able + // to obtain the chunks at all. + // Same needs to be set in the genesis. + let epoch_config_test_overrides = Some(AllEpochConfigTestOverrides { + block_producer_kickout_threshold: Some(0), + chunk_producer_kickout_threshold: Some(0), + }); + let env = builder + .clients_count(num_clients) + .validator_seats(num_validators) + .real_stores() + .real_epoch_managers_with_test_overrides(&genesis.config, epoch_config_test_overrides) + .nightshade_runtimes(&genesis) + .track_all_shards() + .build(); + assert_eq!(env.validators.len(), num_validators); + Self { + env, + initial_accounts, + epoch_length, + num_clients, + init_txs: vec![], + txs_by_height: BTreeMap::new(), + rng, + resharding_type, + } + } + + /// `init_txs` are added before any block is produced + fn set_init_tx(&mut self, init_txs: Vec) { + self.init_txs = init_txs; + } + + /// `txs_by_height` is a hashmap from block height to transactions to be + /// included at block at that height + fn set_tx_at_height(&mut self, height: u64, txs: Vec) { + debug!(target: "test", "setting txs at height {} txs: {:?}", height, txs.iter().map(|x|x.get_hash()).collect::>()); + self.txs_by_height.insert(height, txs); + } + + fn step( + &mut self, + drop_chunk_condition: &DropChunkCondition, + protocol_version: ProtocolVersion, + ) { + self.step_err(drop_chunk_condition, protocol_version).unwrap(); + } + + /// produces and processes the next block also checks that all accounts in + /// initial_accounts are intact + /// + /// allows for changing the protocol version in the middle of the test the + /// testing_v2 argument means whether the test should expect the sharding + /// layout V2 to be used once the appropriate protocol version is reached + fn step_err( + &mut self, + drop_chunk_condition: &DropChunkCondition, + protocol_version: ProtocolVersion, + ) -> Result<(), unc_client::Error> { + let env = &mut self.env; + let head = env.clients[0].chain.head().unwrap(); + let next_height = head.height + 1; + let expected_num_shards = self + .resharding_type + .map(|t| get_expected_shards_num(self.epoch_length, next_height, &t)); + + tracing::debug!(target: "test", next_height, expected_num_shards, "step"); + + // add transactions for the next block + if next_height == 1 { + for tx in self.init_txs.iter() { + Self::process_tx(env, tx); + } + } + + // At every step, chunks for the next block are produced after the current block is processed + // (inside env.process_block) + // Therefore, if we want a transaction to be included at the block at `height+1`, we must add + // it when we are producing the block at `height` + if let Some(txs) = self.txs_by_height.get(&(next_height + 1)) { + for tx in txs { + Self::process_tx(env, tx); + } + } + + // produce block + let block = { + let block_producer = get_block_producer(env, &head); + let _span = tracing::debug_span!(target: "test", "", client=?block_producer).entered(); + let block_producer_client = env.client(&block_producer); + let mut block = block_producer_client.produce_block(next_height).unwrap().unwrap(); + set_block_protocol_version(&mut block, block_producer.clone(), protocol_version); + + block + }; + + // Mapping from the chunk producer account id to the list of chunks that + // it should produce. When processing block at height `next_height` the + // chunk producers will produce chunks at height `next_height + 1`. + let mut chunk_producer_to_shard_id: HashMap> = HashMap::new(); + for shard_id in 0..block.chunks().len() { + let shard_id = shard_id as ShardId; + let validator_id = get_chunk_producer(env, &block, shard_id); + chunk_producer_to_shard_id.entry(validator_id).or_default().push(shard_id); + } + + let should_catchup = Self::should_catchup(&mut self.rng, self.epoch_length, next_height); + + // process block, this also triggers chunk producers for the next block to produce chunks + for j in 0..self.num_clients { + let client = &mut env.clients[j]; + let _span = tracing::debug_span!(target: "test", "process block", client=j).entered(); + + let shard_ids = chunk_producer_to_shard_id + .get(client.validator_signer.as_ref().unwrap().validator_id()) + .cloned() + .unwrap_or_default(); + let should_produce_chunk = + !drop_chunk_condition.should_drop_chunk(&mut self.rng, next_height + 1, &shard_ids); + tracing::info!(target: "test", ?next_height, ?should_produce_chunk, ?shard_ids, "should produce chunk"); + + // Here we don't just call self.clients[i].process_block_sync_with_produce_chunk_options + // because we want to call run_catchup before finish processing this block. This simulates + // that catchup and block processing run in parallel. + let block = MaybeValidated::from(block.clone()); + client.start_process_block(block, Provenance::NONE, Arc::new(|_| {})).unwrap(); + if should_catchup { + run_catchup(client, &[])?; + } + while wait_for_all_blocks_in_processing(&mut client.chain) { + let (_, errors) = + client.postprocess_ready_blocks(Arc::new(|_| {}), should_produce_chunk); + assert!(errors.is_empty(), "unexpected errors: {:?}", errors); + } + if should_catchup { + run_catchup(&mut env.clients[j], &[])?; + } + } + + { + if let Some(expected_num_shards) = expected_num_shards { + let num_shards = env.clients[0] + .epoch_manager + .get_shard_layout_from_prev_block(block.header().prev_hash()) + .unwrap() + .shard_ids() + .count() as NumShards; + assert_eq!(num_shards, expected_num_shards); + } + } + + env.process_partial_encoded_chunks(); + for j in 0..self.num_clients { + env.process_shards_manager_responses_and_finish_processing_blocks(j); + } + + // after resharding, check chunk extra exists and the states are correct + for account_id in self.initial_accounts.iter() { + check_account(env, account_id, &block); + } + + Ok(()) + } + + fn should_catchup(rng: &mut StdRng, epoch_length: u64, next_height: u64) -> bool { + // Always do catchup before the end of the epoch to ensure it happens at + // least once. Doing it in the very last block triggers some error conditions. + if (next_height + 1) % epoch_length == 0 { + return true; + } + + // Don't do catchup in the very first block of the epoch. This is + // primarily for the error handling test where we want to corrupt the + // databse before catchup happens. + if next_height % epoch_length == 1 { + return false; + } + + rng.gen_bool(P_CATCHUP) + } + + // Submit the tx to all clients for processing and checks: + // Clients that track the relevant shard should return ValidTx + // Clients that do not track the relevenat shard should return RequestRouted + // At least one client should process it and return ValidTx. + fn process_tx(env: &mut TestEnv, tx: &SignedTransaction) { + let mut response_valid_count = 0; + let mut response_routed_count = 0; + for j in 0..env.validators.len() { + let response = env.clients[j].process_tx(tx.clone(), false, false); + tracing::trace!(target: "test", client=j, tx=?tx.get_hash(), ?response, "process tx"); + match response { + ProcessTxResponse::ValidTx => response_valid_count += 1, + ProcessTxResponse::RequestRouted => response_routed_count += 1, + response => { + panic!("invalid tx response {response:?} {tx:?}"); + } + } + } + assert_ne!(response_valid_count, 0); + assert_eq!(response_valid_count + response_routed_count, env.validators.len()); + } + + /// check that all accounts in `accounts` exist in the current state + fn check_accounts(&mut self, accounts: Vec<&AccountId>) { + tracing::debug!(target: "test", "checking accounts"); + + let head = self.env.clients[0].chain.head().unwrap(); + let block = self.env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); + for account_id in accounts { + check_account(&mut self.env, account_id, &block) + } + } + + /// Check that chain.get_next_block_hash_with_new_chunk function returns the expected + /// result with sharding upgrade + /// Specifically, the function calls `get_next_block_with_new_chunk` for the block at height + /// `height` for all shards in this block, and verifies that the returned result + /// 1) If it is not empty (`new_block_hash`, `target_shard_id`), + /// - the chunk at `target_shard_id` is a new chunk + /// - `target_shard_id` is either the original shard or a split shard of the original shard + /// - all blocks before the returned `new_block_hash` do not have new chunk for the corresponding + /// shards + /// 2) If it is empty + /// - all blocks after the block at `height` in the current canonical chain do not have + /// new chunks for the corresponding shards + fn check_next_block_with_new_chunk(&mut self, height: BlockHeight) { + let client = &self.env.clients[0]; + let block = client.chain.get_block_by_height(height).unwrap(); + let block_hash = block.hash(); + let num_shards = block.chunks().len(); + for shard_id in 0..num_shards { + // get hash of the last block that we need to check that it has empty chunks for the shard + // if `get_next_block_hash_with_new_chunk` returns None, that would be the lastest block + // on chain, otherwise, that would be the block before the `block_hash` that the function + // call returns + let next_block_hash_with_new_chunk = client + .chain + .get_next_block_hash_with_new_chunk(block_hash, shard_id as ShardId) + .unwrap(); + + let mut last_block_hash_with_empty_chunk = match next_block_hash_with_new_chunk { + Some((new_block_hash, target_shard_id)) => { + let new_block = client.chain.get_block(&new_block_hash).unwrap().clone(); + let chunks = new_block.chunks(); + // check that the target chunk in the new block is new + assert_eq!( + chunks.get(target_shard_id as usize).unwrap().height_included(), + new_block.header().height(), + ); + if chunks.len() == num_shards { + assert_eq!(target_shard_id, shard_id as ShardId); + } + *new_block.header().prev_hash() + } + None => client.chain.head().unwrap().last_block_hash, + }; + + // Check that the target chunks are not new for all blocks between + // last_block_hash_with_empty_chunk (inclusive) and + // block_hash (exclusive). + while &last_block_hash_with_empty_chunk != block_hash { + let last_block_hash = last_block_hash_with_empty_chunk; + let last_block = client.chain.get_block(&last_block_hash).unwrap().clone(); + let chunks = last_block.chunks(); + + let target_shard_ids = if chunks.len() == num_shards { + // same shard layout between block and last_block + vec![shard_id as ShardId] + } else { + // different shard layout between block and last_block + let shard_layout = client + .epoch_manager + .get_shard_layout_from_prev_block(&last_block_hash) + .unwrap(); + + shard_layout.get_children_shards_ids(shard_id as ShardId).unwrap() + }; + + for target_shard_id in target_shard_ids { + let chunk = chunks.get(target_shard_id as usize).unwrap(); + assert_ne!(chunk.height_included(), last_block.header().height()); + } + + last_block_hash_with_empty_chunk = *last_block.header().prev_hash(); + } + } + } + + /// This functions checks that the outcomes of all transactions and associated receipts + /// have successful status + /// If `allow_not_started` is true, allow transactions status to be NotStarted + /// Return successful transaction hashes + fn check_tx_outcomes(&mut self, allow_not_started: bool) -> Vec { + tracing::debug!(target: "test", "checking tx outcomes"); + let env = &mut self.env; + let head = env.clients[0].chain.head().unwrap(); + let block = env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); + // check execution outcomes + let shard_layout = env.clients[0] + .epoch_manager + .get_shard_layout_from_prev_block(&head.last_block_hash) + .unwrap(); + let mut txs_to_check = vec![]; + txs_to_check.extend(&self.init_txs); + for (_, txs) in self.txs_by_height.iter() { + txs_to_check.extend(txs); + } + + let mut successful_txs = Vec::new(); + for tx in txs_to_check { + let id = &tx.get_hash(); + + let signer_account_id = &tx.transaction.signer_id; + let shard_uid = account_id_to_shard_uid(signer_account_id, &shard_layout); + + tracing::trace!(target: "test", tx=?id, ?signer_account_id, ?shard_uid, "checking tx"); + + for (i, validator_account_id) in env.validators.iter().enumerate() { + let client = &env.clients[i]; + + let cares_about_shard = client.shard_tracker.care_about_shard( + Some(validator_account_id), + block.header().prev_hash(), + shard_uid.shard_id(), + true, + ); + if !cares_about_shard { + continue; + } + let execution_outcomes = client.chain.get_transaction_execution_result(id).unwrap(); + if execution_outcomes.is_empty() { + tracing::error!(target: "test", tx=?id, client=i, "tx not processed"); + assert!(allow_not_started, "tx {:?} not processed", id); + continue; + } + let final_outcome = client.chain.get_final_transaction_result(id).unwrap(); + + let outcome_status = final_outcome.status.clone(); + if matches!(outcome_status, FinalExecutionStatus::SuccessValue(_)) { + successful_txs.push(tx.get_hash()); + } else { + tracing::error!(target: "test", tx=?id, client=i, "tx failed"); + panic!("tx failed {:?}", final_outcome); + } + for outcome in final_outcome.receipts_outcome { + assert_matches!(outcome.outcome.status, ExecutionStatusView::SuccessValue(_)); + } + } + } + successful_txs + } + + // Check the receipt_id_to_shard_id mappings are correct for all outgoing receipts in the + // latest block + fn check_receipt_id_to_shard_id(&mut self) { + let env = &mut self.env; + let head = env.clients[0].chain.head().unwrap(); + let shard_layout = env.clients[0] + .epoch_manager + .get_shard_layout_from_prev_block(&head.last_block_hash) + .unwrap(); + let block = env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); + + for (shard_id, chunk_header) in block.chunks().iter().enumerate() { + if chunk_header.height_included() != block.header().height() { + continue; + } + let shard_id = shard_id as ShardId; + + for (i, me) in env.validators.iter().enumerate() { + let client = &mut env.clients[i]; + let care_about_shard = client.shard_tracker.care_about_shard( + Some(me), + &head.prev_block_hash, + shard_id, + true, + ); + if !care_about_shard { + continue; + } + + let outgoing_receipts = client + .chain + .mut_chain_store() + .get_outgoing_receipts(&head.last_block_hash, shard_id) + .unwrap() + .clone(); + for receipt in outgoing_receipts.iter() { + let target_shard_id = + client.chain.get_shard_id_for_receipt_id(&receipt.receipt_id).unwrap(); + assert_eq!( + target_shard_id, + account_id_to_shard_id(&receipt.receiver_id, &shard_layout) + ); + } + } + } + } + + /// Check that after resharding is finished, the artifacts stored in storage is removed + fn check_resharding_artifacts(&mut self) { + tracing::debug!(target: "test", "checking resharding artifacts"); + + let env = &mut self.env; + let head = env.clients[0].chain.head().unwrap(); + for height in 0..head.height { + let (block_hash, num_shards) = { + let block = env.clients[0].chain.get_block_by_height(height).unwrap(); + (*block.hash(), block.chunks().len() as NumShards) + }; + for shard_id in 0..num_shards { + let res = env.clients[0] + .chain + .chain_store() + .get_state_changes_for_resharding(&block_hash, shard_id); + assert_matches!(res, Err(error) => { + assert_matches!(error, Error::DBNotFoundErr(_)); + }) + } + } + } + + fn check_outgoing_receipts_reassigned(&self, resharding_type: &ReshardingType) { + tracing::debug!(target: "test", "checking outgoing receipts reassigned"); + let env = &self.env; + + // height 20 is after the resharding is finished + let num_shards = get_expected_shards_num(self.epoch_length, 20, resharding_type); + + // last height before resharding took place + let last_height_included = 10; + for client in &env.clients { + for shard_id in 0..num_shards { + check_outgoing_receipts_reassigned_impl( + client, + shard_id, + last_height_included, + resharding_type, + ); + } + } + } + + // function to check whether the state snapshot exists or not and whether it exists at the correct + // hash for both cases when state snapshot is enabled and disabled + fn check_snapshot(&mut self, state_snapshot_enabled: bool) { + let env = &mut self.env; + let head = env.clients[0].chain.head().unwrap(); + let block_header = env.clients[0].chain.get_block_header(&head.prev_block_hash).unwrap(); + let snapshot = env.clients[0] + .chain + .runtime_adapter + .get_tries() + .get_state_snapshot(&block_header.prev_hash()); + + // There are two main cases we would like to check, when state_snapshot is enabled and disabled + // 1. with state snapshot enabled, we expect to create a new snapshot with every epoch boundry + // 2. with state snapshot disabled, we should ONLY be creating the snapshot at the first epoch boundry + // i.e. when resharding happens, and subsequently, at the next epoch boundry, the state snapshot + // must be deleted. + if head.height <= self.epoch_length { + // No snapshot for height less than epoch length, i.e. first epoch + assert!(snapshot + .is_err_and(|e| e == SnapshotError::SnapshotNotFound(*block_header.prev_hash()))); + } else if head.height == self.epoch_length + 1 { + // At the epoch boundary, right before resharding, snapshot should exist + assert!(snapshot.is_ok()); + } else if head.height <= 2 * self.epoch_length { + // All blocks in the epoch while resharding is going on, snapshot should exist but we should get + // IncorrectSnapshotRequested as snapshot exists at hash as of the last block of the previous epoch + let snapshot_block_header = + env.clients[0].chain.get_block_header_by_height(self.epoch_length).unwrap(); + assert!(snapshot.is_err_and(|e| e + == SnapshotError::IncorrectSnapshotRequested( + *block_header.prev_hash(), + *snapshot_block_header.prev_hash(), + ))); + } else if (head.height - 1) % self.epoch_length == 0 { + // At other epoch boundries, snapshot should exist only if state_snapshot_enabled is true + assert_eq!(state_snapshot_enabled, snapshot.is_ok()); + } else { + // And for the rest of the height which are not at the epoch boundary, snapshot should + // 1. Either not exist if state_snapshot_enabled is false OR + // 2. snapshot should exist (but with hash as of last block of prev epoch) if state_snapshot is enabled + assert!(snapshot.is_err_and(|err| { + match err { + SnapshotError::SnapshotNotFound(_) => !state_snapshot_enabled, + SnapshotError::IncorrectSnapshotRequested(_, _) => state_snapshot_enabled, + _ => false, + } + })); + } + } + + fn check_trie_and_flat_state(&self, expect_deleted: bool) { + let tries = self.env.clients[0].chain.runtime_adapter.get_tries(); + let flat_storage_manager = + self.env.clients[0].chain.runtime_adapter.get_flat_storage_manager(); + let store = tries.get_store(); + for shard_uid in get_parent_shard_uids(&self.resharding_type.unwrap()) { + // verify we have no keys in State and FlatState column + let key_prefix = shard_uid.to_bytes(); + if expect_deleted { + assert!(store.iter_prefix(DBCol::State, &key_prefix).next().is_none()); + assert!(store.iter_prefix(DBCol::FlatState, &key_prefix).next().is_none()); + } + // verify that flat storage status says Empty + let status = flat_storage_manager.get_flat_storage_status(shard_uid); + match status { + FlatStorageStatus::Empty => assert!(expect_deleted, "flat storage status Empty"), + _ => assert!(!expect_deleted, "unexpected flat storage status: {:?}", status), + } + } + } +} + +// Returns the block producer for the next block after the current head. +fn get_block_producer(env: &TestEnv, head: &Tip) -> AccountId { + let client = &env.clients[0]; + let epoch_manager = &client.epoch_manager; + let parent_hash = &head.last_block_hash; + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(parent_hash).unwrap(); + let height = head.height + 1; + let block_producer = epoch_manager.get_block_producer(&epoch_id, height).unwrap(); + block_producer +} + +// Returns the chunk producer for the next chunk after the given block. +fn get_chunk_producer(env: &TestEnv, block: &Block, shard_id: ShardId) -> AccountId { + let client = &env.clients[0]; + let epoch_manager = &client.epoch_manager; + let parent_hash = block.header().prev_hash(); + let epoch_id = epoch_manager.get_epoch_id_from_prev_block(parent_hash).unwrap(); + let height = block.header().height() + 1; + let chunk_producer = epoch_manager.get_chunk_producer(&epoch_id, height, shard_id).unwrap(); + chunk_producer +} + +fn check_outgoing_receipts_reassigned_impl( + client: &Client, + shard_id: ShardId, + last_height_included: BlockHeight, + resharding_type: &ReshardingType, +) { + let chain = &client.chain; + let prev_block = chain.get_block_by_height(last_height_included).unwrap(); + let prev_block_hash = *prev_block.hash(); + + let outgoing_receipts = chain + .get_outgoing_receipts_for_shard(prev_block_hash, shard_id, last_height_included) + .unwrap(); + let shard_layout = + client.epoch_manager.get_shard_layout_from_prev_block(&prev_block_hash).unwrap(); + + match resharding_type { + ReshardingType::V1 => { + // In V0->V1 resharding the outgoing receipts should be reassigned + // to the receipt receiver's shard id. + for receipt in outgoing_receipts { + let receiver = receipt.receiver_id; + let receiver_shard_id = account_id_to_shard_id(&receiver, &shard_layout); + assert_eq!(receiver_shard_id, shard_id); + } + } + ReshardingType::V2 => { + // In V1->V2 resharding the outgoing receipts should be reassigned + // to the lowest index child of the parent shard. + // We can't directly check that here but we can check that the + // non-lowest-index shards are not assigned any receipts. + // We check elsewhere that no receipts are lost so this should be sufficient. + if shard_id == 4 { + assert!(outgoing_receipts.is_empty()); + } + } + } +} + +/// Checks that account exists in the state after `block` is processed +/// This function checks both state_root from chunk extra and state root from chunk header, if +/// the corresponding chunk is included in the block +fn check_account(env: &TestEnv, account_id: &AccountId, block: &Block) { + tracing::trace!(target: "test", ?account_id, block_height=block.header().height(), "checking account"); + let prev_hash = block.header().prev_hash(); + let shard_layout = + env.clients[0].epoch_manager.get_shard_layout_from_prev_block(prev_hash).unwrap(); + let shard_uid = account_id_to_shard_uid(account_id, &shard_layout); + let shard_id = shard_uid.shard_id(); + for (i, me) in env.validators.iter().enumerate() { + let client = &env.clients[i]; + let care_about_shard = + client.shard_tracker.care_about_shard(Some(me), prev_hash, shard_id, true); + if !care_about_shard { + continue; + } + let chunk_extra = &client.chain.get_chunk_extra(block.hash(), &shard_uid).unwrap(); + let state_root = *chunk_extra.state_root(); + client + .runtime_adapter + .query( + shard_uid, + &state_root, + block.header().height(), + 0, + prev_hash, + block.hash(), + block.header().epoch_id(), + &QueryRequest::ViewAccount { account_id: account_id.clone() }, + ) + .unwrap(); + + let chunk = &block.chunks()[shard_id as usize]; + if chunk.height_included() == block.header().height() { + client + .runtime_adapter + .query( + shard_uid, + &chunk.prev_state_root(), + block.header().height(), + 0, + block.header().prev_hash(), + block.hash(), + block.header().epoch_id(), + &QueryRequest::ViewAccount { account_id: account_id.clone() }, + ) + .unwrap(); + } + } +} + +fn setup_genesis( + epoch_length: u64, + num_validators: u64, + initial_accounts: Vec, + gas_limit: Option, + genesis_protocol_version: ProtocolVersion, +) -> Genesis { + let mut genesis = Genesis::test(initial_accounts, num_validators); + // No kickout, since we are going to test missing chunks. + // Same needs to be set in the AllEpochConfigTestOverrides. + genesis.config.block_producer_kickout_threshold = 0; + genesis.config.chunk_producer_kickout_threshold = 0; + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = genesis_protocol_version; + genesis.config.use_production_config = true; + if let Some(gas_limit) = gas_limit { + genesis.config.gas_limit = gas_limit; + } + + // The block producer assignment often happens to be unlucky enough to not + // include one of the validators in the first epoch. When that happens the + // new protocol version gets only 75% of the votes which is lower that the + // default 80% threshold for upgrading. Delaying the upgrade also delays + // resharding and makes it harder to predict. The threshold is set slightly + // lower here to always ensure that upgrade takes place as soon as possible. + // This was not fun to debug. + genesis.config.protocol_upgrade_stake_threshold = Rational32::new(7, 10); + + let default_epoch_config = EpochConfig::from(&genesis.config); + let all_epoch_config = AllEpochConfig::new(true, default_epoch_config, "test-chain"); + let epoch_config = all_epoch_config.for_protocol_version(genesis_protocol_version); + + genesis.config.shard_layout = epoch_config.shard_layout; + genesis.config.num_block_producer_seats_per_shard = + epoch_config.num_block_producer_seats_per_shard; + genesis.config.avg_hidden_validator_seats_per_shard = + epoch_config.avg_hidden_validator_seats_per_shard; + + genesis +} + +fn generate_create_accounts_txs( + mut rng: &mut StdRng, + genesis_hash: CryptoHash, + initial_accounts: &Vec, + accounts_to_check: &mut Vec, + all_accounts: &mut HashSet, + nonce: &mut u64, + max_size: usize, + check_accounts: bool, +) -> Vec { + let size = rng.gen_range(0..max_size) + 1; + std::iter::repeat_with(|| loop { + let signer_account = initial_accounts.choose(&mut rng).unwrap(); + let signer0 = InMemorySigner::from_seed( + signer_account.clone(), + KeyType::ED25519, + signer_account.as_ref(), + ); + let account_id = gen_account(&mut rng); + if all_accounts.insert(account_id.clone()) { + let signer = InMemorySigner::from_seed( + account_id.clone(), + KeyType::ED25519, + account_id.as_ref(), + ); + let tx = SignedTransaction::create_account( + *nonce, + signer_account.clone(), + account_id.clone(), + UNC_BASE, + signer.public_key(), + &signer0, + genesis_hash, + ); + if check_accounts { + accounts_to_check.push(account_id.clone()); + } + *nonce += 1; + tracing::trace!(target: "test", ?account_id, tx=?tx.get_hash(), "adding create account tx"); + return tx; + } + }) + .take(size) + .collect() +} + +fn test_shard_layout_upgrade_simple_impl( + resharding_type: ReshardingType, + rng_seed: u64, + state_snapshot_enabled: bool, +) { + init_test_logger(); + tracing::info!(target: "test", "test_shard_layout_upgrade_simple_impl starting"); + + let genesis_protocol_version = get_genesis_protocol_version(&resharding_type); + let target_protocol_version = get_target_protocol_version(&resharding_type); + + // setup + let epoch_length = 5; + let mut test_env = TestReshardingEnv::new( + epoch_length, + 2, + 2, + 100, + None, + genesis_protocol_version, + rng_seed, + state_snapshot_enabled, + Some(resharding_type), + ); + test_env.set_init_tx(vec![]); + + let mut nonce = 100; + let genesis_hash = *test_env.env.clients[0].chain.genesis_block().hash(); + let mut all_accounts: HashSet<_> = test_env.initial_accounts.clone().into_iter().collect(); + let mut accounts_to_check: Vec<_> = vec![]; + let initial_accounts = test_env.initial_accounts.clone(); + + // add transactions until after sharding upgrade finishes + for height in 2..3 * epoch_length { + let txs = generate_create_accounts_txs( + &mut test_env.rng, + genesis_hash, + &initial_accounts, + &mut accounts_to_check, + &mut all_accounts, + &mut nonce, + 10, + true, + ); + + test_env.set_tx_at_height(height, txs); + } + + let drop_chunk_condition = DropChunkCondition::new(); + for _ in 1..4 * epoch_length { + test_env.step(&drop_chunk_condition, target_protocol_version); + test_env.check_receipt_id_to_shard_id(); + test_env.check_snapshot(state_snapshot_enabled); + } + + test_env.check_tx_outcomes(false); + test_env.check_accounts(accounts_to_check.iter().collect()); + test_env.check_resharding_artifacts(); + test_env.check_outgoing_receipts_reassigned(&resharding_type); + tracing::info!(target: "test", "test_shard_layout_upgrade_simple_impl finished"); +} + +#[test] +fn test_shard_layout_upgrade_simple_v1() { + test_shard_layout_upgrade_simple_impl(ReshardingType::V1, 42, false); +} + +#[test] +fn test_shard_layout_upgrade_simple_v1_with_snapshot_enabled() { + test_shard_layout_upgrade_simple_impl(ReshardingType::V1, 42, true); +} + +#[test] +fn test_shard_layout_upgrade_simple_v2_seed_42() { + test_shard_layout_upgrade_simple_impl(ReshardingType::V2, 42, false); +} + +#[test] +fn test_shard_layout_upgrade_simple_v2_seed_43() { + test_shard_layout_upgrade_simple_impl(ReshardingType::V2, 43, false); +} + +#[test] +fn test_shard_layout_upgrade_simple_v2_seed_44() { + test_shard_layout_upgrade_simple_impl(ReshardingType::V2, 44, false); +} + +/// In this test we are checking whether we are properly deleting trie state and flat state +/// from the old shard layout after resharding. This is handled as a part of Garbage Collection (GC) +fn test_shard_layout_upgrade_gc_impl(resharding_type: ReshardingType, rng_seed: u64) { + init_test_logger(); + tracing::info!(target: "test", "test_shard_layout_upgrade_gc_impl starting"); + + let genesis_protocol_version = get_genesis_protocol_version(&resharding_type); + let target_protocol_version = get_target_protocol_version(&resharding_type); + + let epoch_length = 5; + let mut test_env: TestReshardingEnv = TestReshardingEnv::new( + epoch_length, + 2, + 2, + 100, + None, + genesis_protocol_version, + rng_seed, + false, + Some(resharding_type), + ); + + // GC period is about 5 epochs. We should expect to see state deleted at the end of the 7th epoch + // Epoch 0, blocks 1-5 : genesis shard layout + // Epoch 1, blocks 6-10 : genesis shard layout, resharding happens + // Epoch 2: blocks 10-15: target shard layout, shard layout is upgraded + // Epoch 3-7: target shard layout, waiting for GC to happen + // Epoch 8: block 37: GC happens, state is deleted + let drop_chunk_condition = DropChunkCondition::new(); + for _ in 0..7 * epoch_length + 1 { + // we expect the trie state and flat state to NOT be deleted before GC + test_env.check_trie_and_flat_state(false); + test_env.step(&drop_chunk_condition, target_protocol_version); + } + // Once GC is done, we expect the trie state and flat state to be deleted + test_env.check_trie_and_flat_state(true); +} + +#[test] +fn test_shard_layout_upgrade_gc() { + test_shard_layout_upgrade_gc_impl(ReshardingType::V1, 44); +} + +#[test] +fn test_shard_layout_upgrade_gc_v2() { + test_shard_layout_upgrade_gc_impl(ReshardingType::V2, 44); +} + +const GAS_1: u64 = 300_000_000_000_000; +const GAS_2: u64 = GAS_1 / 3; + +fn create_test_env_for_cross_contract_test( + genesis_protocol_version: ProtocolVersion, + epoch_length: u64, + rng_seed: u64, + resharding_type: Option, +) -> TestReshardingEnv { + TestReshardingEnv::new( + epoch_length, + 4, + 4, + 100, + Some(100_000_000_000_000), + genesis_protocol_version, + rng_seed, + false, + resharding_type, + ) +} + +/// Return test_env and a map from tx hash to the new account that will be added by this transaction +fn setup_test_env_with_cross_contract_txs( + test_env: &mut TestReshardingEnv, + epoch_length: u64, +) -> HashMap { + let genesis_hash = *test_env.env.clients[0].chain.genesis_block().hash(); + // Use test0, test1 and two random accounts to deploy contracts because we want accounts on + // different shards. + let indices = (4..test_env.initial_accounts.len()).choose_multiple(&mut test_env.rng, 2); + let contract_accounts = vec![ + test_env.initial_accounts[0].clone(), + test_env.initial_accounts[1].clone(), + test_env.initial_accounts[indices[0]].clone(), + test_env.initial_accounts[indices[1]].clone(), + ]; + let init_txs = contract_accounts + .iter() + .map(|account_id| { + let signer = InMemorySigner::from_seed( + account_id.clone(), + KeyType::ED25519, + account_id.as_ref(), + ); + SignedTransaction::from_actions( + 1, + account_id.clone(), + account_id.clone(), + &signer, + vec![Action::DeployContract(DeployContractAction { + code: unc_test_contracts::backwards_compatible_rs_contract().to_vec(), + })], + genesis_hash, + ) + }) + .collect(); + test_env.set_init_tx(init_txs); + + let mut nonce = 100; + let mut all_accounts: HashSet<_> = test_env.initial_accounts.clone().into_iter().collect(); + let mut new_accounts = HashMap::new(); + + // add a bunch of transactions before the two epoch boundaries + for height in [ + epoch_length - 2, + epoch_length - 1, + epoch_length, + 2 * epoch_length - 2, + 2 * epoch_length - 1, + 2 * epoch_length, + ] { + let txs = generate_cross_contract_txs( + &mut test_env.rng, + genesis_hash, + &contract_accounts, + &mut all_accounts, + &mut new_accounts, + &mut nonce, + 5, + 8, + ); + + test_env.set_tx_at_height(height, txs); + } + + // adds some transactions after sharding change finishes + // but do not add too many because I want all transactions to + // finish processing before epoch 5 + for height in 2 * epoch_length + 1..3 * epoch_length { + if test_env.rng.gen_bool(0.3) { + let txs = generate_cross_contract_txs( + &mut test_env.rng, + genesis_hash, + &contract_accounts, + &mut all_accounts, + &mut new_accounts, + &mut nonce, + 5, + 8, + ); + test_env.set_tx_at_height(height, txs); + } + } + + new_accounts +} + +fn generate_cross_contract_txs( + rng: &mut StdRng, + genesis_hash: CryptoHash, + contract_accounts: &Vec, + all_accounts: &mut HashSet, + new_accounts: &mut HashMap, + nonce: &mut u64, + min_size: usize, + max_size: usize, +) -> Vec { + let size = rng.gen_range(min_size..max_size + 1); + let mut transactions = vec![]; + for _ in 0..size { + if let Some(tx) = generate_cross_contract_tx( + rng, + genesis_hash, + contract_accounts, + all_accounts, + new_accounts, + nonce, + ) { + transactions.push(tx); + } + } + transactions +} + +fn generate_cross_contract_tx( + rng: &mut StdRng, + genesis_hash: CryptoHash, + contract_accounts: &Vec, + all_accounts: &mut HashSet, + new_accounts: &mut HashMap, + nonce: &mut u64, +) -> Option { + let account_id = gen_account(rng); + if !all_accounts.insert(account_id.clone()) { + return None; + } + *nonce += 1; + // randomly shuffle contract accounts + // so that transactions are send to different shards + let mut contract_accounts = contract_accounts.clone(); + contract_accounts.shuffle(rng); + let tx = gen_cross_contract_tx_impl( + &contract_accounts[0], + &contract_accounts[1], + &contract_accounts[2], + &contract_accounts[3], + &account_id, + *nonce, + &genesis_hash, + ); + new_accounts.insert(tx.get_hash(), account_id); + tracing::trace!(target: "test", tx=?tx.get_hash(), "generated tx"); + Some(tx) +} + +// create a transaction signed by `account0` and calls a contract on `account1` +// the contract creates a promise that executes a cross contract call on "account2" +// then executes another contract call on "account3" that creates a new account +fn gen_cross_contract_tx_impl( + account0: &AccountId, + account1: &AccountId, + account2: &AccountId, + account3: &AccountId, + new_account: &AccountId, + nonce: u64, + block_hash: &CryptoHash, +) -> SignedTransaction { + let signer0 = InMemorySigner::from_seed(account0.clone(), KeyType::ED25519, account0.as_ref()); + let signer_new_account = + InMemorySigner::from_seed(new_account.clone(), KeyType::ED25519, new_account.as_ref()); + let data = serde_json::json!([ + {"create": { + "account_id": account2.to_string(), + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": GAS_2, + }, "id": 0 }, + {"then": { + "promise_index": 0, + "account_id": account3.to_string(), + "method_name": "call_promise", + "arguments": [ + {"batch_create": { "account_id": new_account.to_string() }, "id": 0 }, + {"action_create_account": { + "promise_index": 0, }, + "id": 0 }, + {"action_transfer": { + "promise_index": 0, + "amount": UNC_BASE.to_string(), + }, "id": 0 }, + {"action_add_key_with_full_access": { + "promise_index": 0, + "public_key": to_base64(&borsh::to_vec(&signer_new_account.public_key).unwrap()), + "nonce": 0, + }, "id": 0 } + ], + "amount": UNC_BASE.to_string(), + "gas": GAS_2, + }, "id": 1} + ]); + + SignedTransaction::from_actions( + nonce, + account0.clone(), + account1.clone(), + &signer0, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "call_promise".to_string(), + args: serde_json::to_vec(&data).unwrap(), + gas: GAS_1, + deposit: 0, + }))], + *block_hash, + ) +} + +// Test cross contract calls +// This test case tests postponed receipts and delayed receipts +fn test_shard_layout_upgrade_cross_contract_calls_impl( + resharding_type: ReshardingType, + rng_seed: u64, +) { + init_test_logger(); + + // setup + let epoch_length = 5; + let genesis_protocol_version = get_genesis_protocol_version(&resharding_type); + let target_protocol_version = get_target_protocol_version(&resharding_type); + + let mut test_env = create_test_env_for_cross_contract_test( + genesis_protocol_version, + epoch_length, + rng_seed, + Some(resharding_type), + ); + + let new_accounts = setup_test_env_with_cross_contract_txs(&mut test_env, epoch_length); + + let drop_chunk_condition = DropChunkCondition::new(); + for _ in 1..5 * epoch_length { + test_env.step(&drop_chunk_condition, target_protocol_version); + test_env.check_receipt_id_to_shard_id(); + } + + let successful_txs = test_env.check_tx_outcomes(false); + let new_accounts = + successful_txs.iter().flat_map(|tx_hash| new_accounts.get(tx_hash)).collect(); + + test_env.check_accounts(new_accounts); + + test_env.check_resharding_artifacts(); +} + +// Test cross contract calls +// This test case tests postponed receipts and delayed receipts +#[test] +fn test_shard_layout_upgrade_cross_contract_calls_v1() { + test_shard_layout_upgrade_cross_contract_calls_impl(ReshardingType::V1, 42); +} + +// Test cross contract calls +// This test case tests postponed receipts and delayed receipts +#[test] +fn test_shard_layout_upgrade_cross_contract_calls_v2_seed_42() { + test_shard_layout_upgrade_cross_contract_calls_impl(ReshardingType::V2, 42); +} + +// Test cross contract calls +// This test case tests postponed receipts and delayed receipts +#[test] +fn test_shard_layout_upgrade_cross_contract_calls_v2_seed_43() { + test_shard_layout_upgrade_cross_contract_calls_impl(ReshardingType::V2, 43); +} + +// Test cross contract calls +// This test case tests postponed receipts and delayed receipts +#[test] +fn test_shard_layout_upgrade_cross_contract_calls_v2_seed_44() { + test_shard_layout_upgrade_cross_contract_calls_impl(ReshardingType::V2, 44); +} + +fn test_shard_layout_upgrade_incoming_receipts_impl( + resharding_type: ReshardingType, + rng_seed: u64, +) { + init_test_logger(); + + // setup + let epoch_length = 5; + let genesis_protocol_version = get_genesis_protocol_version(&resharding_type); + let target_protocol_version = get_target_protocol_version(&resharding_type); + + let mut test_env = create_test_env_for_cross_contract_test( + genesis_protocol_version, + epoch_length, + rng_seed, + Some(resharding_type), + ); + + let new_accounts = setup_test_env_with_cross_contract_txs(&mut test_env, epoch_length); + + // Drop one of the chunks in the last block before switching to the new + // shard layout. + let drop_height = 2 * epoch_length; + let old_shard_num = get_expected_shards_num(epoch_length, drop_height, &resharding_type); + let new_shard_num = get_expected_shards_num(epoch_length, drop_height + 1, &resharding_type); + assert_ne!(old_shard_num, new_shard_num); + + // Drop the chunk from the shard with the highest shard id since it's the + // one that is split in both V1 and V2 reshardings. + let drop_shard_id = old_shard_num - 1; + + let by_height_shard_id = HashSet::from([(drop_height, drop_shard_id)]); + let drop_chunk_condition = DropChunkCondition::with_by_height_shard_id(by_height_shard_id); + for _ in 1..5 * epoch_length { + test_env.step(&drop_chunk_condition, target_protocol_version); + test_env.check_receipt_id_to_shard_id(); + } + + let successful_txs = test_env.check_tx_outcomes(false); + let new_accounts = + successful_txs.iter().flat_map(|tx_hash| new_accounts.get(tx_hash)).collect(); + + test_env.check_accounts(new_accounts); + test_env.check_resharding_artifacts(); +} + +// This test doesn't make much sense for the V1 resharding. That is because in +// V1 resharding there is only one shard before resharding. Even if that chunk +// is missing there aren't any other chunks so there aren't any incoming +// receipts at all. +#[test] +fn test_shard_layout_upgrade_incoming_receipts_v1() { + test_shard_layout_upgrade_incoming_receipts_impl(ReshardingType::V1, 42); +} + +#[test] +fn test_shard_layout_upgrade_incoming_receipts_v2_seed_42() { + test_shard_layout_upgrade_incoming_receipts_impl(ReshardingType::V2, 42); +} + +#[test] +fn test_shard_layout_upgrade_incoming_receipts_v2_seed_43() { + test_shard_layout_upgrade_incoming_receipts_impl(ReshardingType::V2, 43); +} + +#[test] +fn test_shard_layout_upgrade_incoming_receipts_v2_seed_44() { + test_shard_layout_upgrade_incoming_receipts_impl(ReshardingType::V2, 44); +} + +// Test cross contract calls +// This test case tests when there are missing chunks in the produced blocks +// This is to test that all the chunk management logic is correct, e.g. inclusion of outgoing +// receipts into next chunks +fn test_missing_chunks( + test_env: &mut TestReshardingEnv, + p_missing: f64, + target_protocol_version: ProtocolVersion, + epoch_length: u64, +) { + // setup + let new_accounts = setup_test_env_with_cross_contract_txs(test_env, epoch_length); + + // randomly dropping chunks at the first few epochs when sharding splits happens + // make sure initial txs (deploy smart contracts) are processed succesfully + let drop_chunk_condition = DropChunkCondition::new(); + for _ in 1..3 { + test_env.step(&drop_chunk_condition, target_protocol_version); + } + + let drop_chunk_condition = DropChunkCondition::with_probability(p_missing); + for _ in 3..3 * epoch_length { + test_env.step(&drop_chunk_condition, target_protocol_version); + let last_height = test_env.env.clients[0].chain.head().unwrap().height; + for height in last_height - 3..=last_height { + test_env.check_next_block_with_new_chunk(height); + } + test_env.check_receipt_id_to_shard_id(); + } + + // make sure all included transactions finished processing + let drop_chunk_condition = DropChunkCondition::new(); + for _ in 3 * epoch_length..5 * epoch_length { + test_env.step(&drop_chunk_condition, target_protocol_version); + let last_height = test_env.env.clients[0].chain.head().unwrap().height; + for height in last_height - 3..=last_height { + test_env.check_next_block_with_new_chunk(height); + } + test_env.check_receipt_id_to_shard_id(); + } + + let successful_txs = test_env.check_tx_outcomes(true); + let new_accounts: Vec<_> = + successful_txs.iter().flat_map(|tx_hash| new_accounts.get(tx_hash)).collect(); + test_env.check_accounts(new_accounts); + + test_env.check_resharding_artifacts(); +} + +fn test_shard_layout_upgrade_missing_chunks( + resharding_type: ReshardingType, + p_missing: f64, + rng_seed: u64, +) { + init_test_logger(); + + let genesis_protocol_version = get_genesis_protocol_version(&resharding_type); + let target_protocol_version = get_target_protocol_version(&resharding_type); + let epoch_length = 10; + + let mut test_env = create_test_env_for_cross_contract_test( + genesis_protocol_version, + epoch_length, + rng_seed, + Some(resharding_type), + ); + test_missing_chunks(&mut test_env, p_missing, target_protocol_version, epoch_length) +} + +// Use resharding setup to run protocol for couple epochs with given probability +// of missing chunk. In particular, checks that all txs generated in env have +// final outcome in the end. +// TODO: remove logical dependency on resharding. +fn test_latest_protocol_missing_chunks(p_missing: f64, rng_seed: u64) { + init_test_logger(); + let epoch_length = 10; + let mut test_env = + create_test_env_for_cross_contract_test(PROTOCOL_VERSION, epoch_length, rng_seed, None); + test_missing_chunks(&mut test_env, p_missing, PROTOCOL_VERSION, epoch_length) +} + +#[test] +fn test_shard_layout_upgrade_missing_chunks_low_missing_prob_v1() { + test_shard_layout_upgrade_missing_chunks(ReshardingType::V1, 0.1, 42); +} + +#[test] +fn test_shard_layout_upgrade_missing_chunks_mid_missing_prob_v1() { + test_shard_layout_upgrade_missing_chunks(ReshardingType::V1, 0.5, 42); +} + +#[test] +fn test_shard_layout_upgrade_missing_chunks_high_missing_prob_v1() { + test_shard_layout_upgrade_missing_chunks(ReshardingType::V1, 0.9, 42); +} + +// V2, low missing prob + +#[test] +fn test_shard_layout_upgrade_missing_chunks_low_missing_prob_v2_seed_42() { + test_shard_layout_upgrade_missing_chunks(ReshardingType::V2, 0.1, 42); +} + +#[test] +fn test_shard_layout_upgrade_missing_chunks_low_missing_prob_v2_seed_43() { + test_shard_layout_upgrade_missing_chunks(ReshardingType::V2, 0.1, 43); +} + +#[test] +fn test_shard_layout_upgrade_missing_chunks_low_missing_prob_v2_seed_44() { + test_shard_layout_upgrade_missing_chunks(ReshardingType::V2, 0.1, 44); +} + +// V2, mid missing prob + +#[test] +fn test_shard_layout_upgrade_missing_chunks_mid_missing_prob_v2_seed_42() { + test_shard_layout_upgrade_missing_chunks(ReshardingType::V2, 0.5, 42); +} + +#[test] +fn test_shard_layout_upgrade_missing_chunks_mid_missing_prob_v2_seed_43() { + test_shard_layout_upgrade_missing_chunks(ReshardingType::V2, 0.5, 43); +} + +#[test] +fn test_shard_layout_upgrade_missing_chunks_mid_missing_prob_v2_seed_44() { + test_shard_layout_upgrade_missing_chunks(ReshardingType::V2, 0.5, 44); +} + +// V2, high missing prob + +#[test] +fn test_shard_layout_upgrade_missing_chunks_high_missing_prob_v2_seed_42() { + test_shard_layout_upgrade_missing_chunks(ReshardingType::V2, 0.9, 42); +} + +#[test] +fn test_shard_layout_upgrade_missing_chunks_high_missing_prob_v2_seed_43() { + test_shard_layout_upgrade_missing_chunks(ReshardingType::V2, 0.9, 43); +} + +#[test] +fn test_shard_layout_upgrade_missing_chunks_high_missing_prob_v2_seed_44() { + test_shard_layout_upgrade_missing_chunks(ReshardingType::V2, 0.9, 44); +} + +#[test] +fn test_latest_protocol_missing_chunks_low_missing_prob() { + test_latest_protocol_missing_chunks(0.1, 25); +} + +#[test] +fn test_latest_protocol_missing_chunks_mid_missing_prob() { + test_latest_protocol_missing_chunks(0.5, 26); +} + +#[test] +fn test_latest_protocol_missing_chunks_high_missing_prob() { + test_latest_protocol_missing_chunks(0.9, 27); +} + +fn test_shard_layout_upgrade_error_handling_impl( + resharding_type: ReshardingType, + rng_seed: u64, + state_snapshot_enabled: bool, +) { + init_test_logger(); + tracing::info!(target: "test", "test_shard_layout_upgrade_simple_impl starting"); + + let genesis_protocol_version = get_genesis_protocol_version(&resharding_type); + let target_protocol_version = get_target_protocol_version(&resharding_type); + + // setup + let epoch_length = 5; + let mut test_env = TestReshardingEnv::new( + epoch_length, + 2, + 2, + 100, + None, + genesis_protocol_version, + rng_seed, + state_snapshot_enabled, + Some(resharding_type), + ); + test_env.set_init_tx(vec![]); + + let mut nonce = 100; + let genesis_hash = *test_env.env.clients[0].chain.genesis_block().hash(); + let mut all_accounts: HashSet<_> = test_env.initial_accounts.clone().into_iter().collect(); + let mut accounts_to_check: Vec<_> = vec![]; + let initial_accounts = test_env.initial_accounts.clone(); + + // add transactions until after sharding upgrade finishes + for height in 2..3 * epoch_length { + let txs = generate_create_accounts_txs( + &mut test_env.rng, + genesis_hash, + &initial_accounts, + &mut accounts_to_check, + &mut all_accounts, + &mut nonce, + 10, + true, + ); + + test_env.set_tx_at_height(height, txs); + } + + let drop_chunk_condition = DropChunkCondition::new(); + for _ in 1..4 * epoch_length { + let result = test_env.step_err(&drop_chunk_condition, target_protocol_version); + if let Err(err) = result { + tracing::info!(target: "test", ?err, "step failed, as expected"); + return; + } + + // corrupt the state snapshot if available to make resharding fail + currupt_state_snapshot(&test_env); + } + + assert!(false, "no error was recorded, something is wrong in error handling"); +} + +fn currupt_state_snapshot(test_env: &TestReshardingEnv) { + let tries = test_env.env.clients[0].runtime_adapter.get_tries(); + let Ok(snapshot_hash) = tries.get_state_snapshot_hash() else { return }; + let (store, flat_storage_manager) = tries.get_state_snapshot(&snapshot_hash).unwrap(); + let shard_uids = flat_storage_manager.get_shard_uids(); + let mut store_update = store.store_update(); + for shard_uid in shard_uids { + flat_storage_manager.remove_flat_storage_for_shard(shard_uid, &mut store_update).unwrap(); + } + store_update.commit().unwrap(); +} + +#[test] +fn test_shard_layout_upgrade_error_handling_v1() { + test_shard_layout_upgrade_error_handling_impl(ReshardingType::V1, 42, false); +} + +#[test] +fn test_shard_layout_upgrade_error_handling_v2() { + test_shard_layout_upgrade_error_handling_impl(ReshardingType::V2, 42, false); +} + +// TODO(resharding) add a test with missing blocks +// TODO(resharding) add a test with deleting accounts and delayed receipts check diff --git a/integration-tests/src/tests/client/runtimes.rs b/integration-tests/src/tests/client/runtimes.rs new file mode 100644 index 000000000..be5aa56fd --- /dev/null +++ b/integration-tests/src/tests/client/runtimes.rs @@ -0,0 +1,94 @@ +//! Client is responsible for tracking the chain, chunks, and producing them when needed. +//! This client works completely synchronously and must be operated by some async actor outside. + +use unc_chain::ChainGenesis; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_crypto::KeyType; +use unc_network::test_utils::MockPeerManagerAdapter; +use unc_primitives::block::{Approval, ApprovalInner}; +use unc_primitives::block_header::ApprovalType; +use unc_primitives::hash::hash; +use unc_primitives::network::PeerId; +use unc_primitives::test_utils::create_test_signer; +use unc_primitives::validator_signer::InMemoryValidatorSigner; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; +use std::collections::HashMap; +use std::sync::Arc; + +#[test] +fn test_pending_approvals() { + let genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let signer = create_test_signer("test0"); + let parent_hash = hash(&[1]); + let approval = Approval::new(parent_hash, 0, 1, &signer); + let peer_id = PeerId::random(); + env.clients[0].collect_block_approval(&approval, ApprovalType::PeerApproval(peer_id.clone())); + let approvals = env.clients[0].pending_approvals.pop(&ApprovalInner::Endorsement(parent_hash)); + let expected = + vec![("test0".parse().unwrap(), (approval, ApprovalType::PeerApproval(peer_id)))] + .into_iter() + .collect::>(); + assert_eq!(approvals, Some(expected)); +} + +#[test] +fn test_invalid_approvals() { + let genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let network_adapter = Arc::new(MockPeerManagerAdapter::default()); + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .network_adapters(vec![network_adapter]) + .build(); + let signer = create_test_signer("random"); + let parent_hash = hash(&[1]); + // Approval not from a validator. Should be dropped + let approval = Approval::new(parent_hash, 1, 3, &signer); + let peer_id = PeerId::random(); + env.clients[0].collect_block_approval(&approval, ApprovalType::PeerApproval(peer_id.clone())); + assert_eq!(env.clients[0].pending_approvals.len(), 0); + // Approval with invalid signature. Should be dropped + let signer = + InMemoryValidatorSigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "random"); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + let approval = Approval::new(genesis_hash, 0, 1, &signer); + env.clients[0].collect_block_approval(&approval, ApprovalType::PeerApproval(peer_id)); + assert_eq!(env.clients[0].pending_approvals.len(), 0); +} + +#[test] +fn test_cap_max_gas_price() { + use unc_chain::Provenance; + use unc_primitives::version::ProtocolFeature; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let epoch_length = 5; + genesis.config.min_gas_price = 1_000; + genesis.config.max_gas_price = 1_000_000; + genesis.config.protocol_version = ProtocolFeature::CapMaxGasPrice.protocol_version(); + genesis.config.epoch_length = epoch_length; + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + for i in 1..epoch_length { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + env.process_block(0, block, Provenance::PRODUCED); + } + + let last_block = env.clients[0].chain.get_block_by_height(epoch_length - 1).unwrap(); + let protocol_version = env.clients[0] + .epoch_manager + .get_epoch_protocol_version(last_block.header().epoch_id()) + .unwrap(); + let min_gas_price = env.clients[0].chain.block_economics_config.min_gas_price(protocol_version); + let max_gas_price = env.clients[0].chain.block_economics_config.max_gas_price(protocol_version); + assert!(max_gas_price <= 20 * min_gas_price); +} diff --git a/integration-tests/src/tests/client/sandbox.rs b/integration-tests/src/tests/client/sandbox.rs new file mode 100644 index 000000000..f2f873928 --- /dev/null +++ b/integration-tests/src/tests/client/sandbox.rs @@ -0,0 +1,110 @@ +use unc_chain::{ChainGenesis, Provenance}; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_client::ProcessTxResponse; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_primitives::account::Account; +use unc_primitives::sandbox::state_patch::SandboxStatePatch; +use unc_primitives::state_record::StateRecord; +use unc_primitives::transaction::{ + Action, DeployContractAction, FunctionCallAction, SignedTransaction, +}; +use unc_primitives::types::{AccountId, BlockHeight, Nonce}; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; + +fn test_setup() -> (TestEnv, InMemorySigner) { + let epoch_length = 5; + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut env = TestEnv::builder(ChainGenesis::test()) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + assert_eq!( + send_tx( + &mut env, + 1, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + vec![Action::DeployContract(DeployContractAction { + code: unc_test_contracts::rs_contract().to_vec(), + })], + ), + ProcessTxResponse::ValidTx + ); + do_blocks(&mut env, 1, 3); + + assert_eq!( + send_tx( + &mut env, + 2, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "write_random_value".to_string(), + args: vec![], + gas: 100000000000000, + deposit: 0, + }))], + ), + ProcessTxResponse::ValidTx + ); + do_blocks(&mut env, 3, 9); + (env, signer) +} + +fn do_blocks(env: &mut TestEnv, start: BlockHeight, end: BlockHeight) { + for i in start..end { + let last_block = env.clients[0].produce_block(i).unwrap().unwrap(); + env.process_block(0, last_block.clone(), Provenance::PRODUCED); + } +} + +fn send_tx( + env: &mut TestEnv, + nonce: Nonce, + signer_id: AccountId, + receiver_id: AccountId, + signer: &InMemorySigner, + actions: Vec, +) -> ProcessTxResponse { + let hash = env.clients[0].chain.head().unwrap().last_block_hash; + let tx = SignedTransaction::from_actions(nonce, signer_id, receiver_id, signer, actions, hash); + env.clients[0].process_tx(tx, false, false) +} + +#[test] +fn test_patch_state() { + let (mut env, _signer) = test_setup(); + + let state_item = env.query_state("test0".parse().unwrap()).swap_remove(0); + env.clients[0].chain.patch_state(SandboxStatePatch::new(vec![StateRecord::Data { + account_id: "test0".parse().unwrap(), + data_key: state_item.key, + value: b"world".to_vec().into(), + }])); + + do_blocks(&mut env, 9, 20); + let state = env.query_state("test0".parse().unwrap()); + assert_eq!(state.len(), 1); + assert_eq!(state[0].value.as_slice(), b"world"); +} + +#[test] +fn test_patch_account() { + let (mut env, _signer) = test_setup(); + let mut test1: Account = env.query_account("test1".parse().unwrap()).into(); + test1.set_amount(10); + + env.clients[0].chain.patch_state(SandboxStatePatch::new(vec![StateRecord::Account { + account_id: "test1".parse().unwrap(), + account: test1, + }])); + do_blocks(&mut env, 9, 20); + let test1_after = env.query_account("test1".parse().unwrap()); + assert_eq!(test1_after.amount, 10); +} diff --git a/integration-tests/src/tests/client/state_dump.rs b/integration-tests/src/tests/client/state_dump.rs new file mode 100644 index 000000000..45c0c19e9 --- /dev/null +++ b/integration-tests/src/tests/client/state_dump.rs @@ -0,0 +1,402 @@ +use assert_matches::assert_matches; + +use unc_chain::unc_chain_primitives::error::QueryError; +use unc_chain::{ChainGenesis, ChainStoreAccess, Provenance}; +use unc_chain_configs::ExternalStorageLocation::Filesystem; +use unc_chain_configs::{DumpConfig, Genesis}; +use unc_client::sync::external::external_storage_location; +use unc_client::test_utils::TestEnv; +use unc_client::ProcessTxResponse; +use unc_crypto::{InMemorySigner, KeyType, Signer}; +use unc_network::test_utils::wait_or_timeout; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::block::Tip; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::state::FlatStateValue; +use unc_primitives::state_part::PartId; +use unc_primitives::state_sync::StatePartKey; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::BlockHeight; +use unc_primitives::views::{QueryRequest, QueryResponseKind}; +use unc_store::flat::store_helper; +use unc_store::DBCol; +use unc_store::Store; +use framework::config::GenesisExt; +use framework::state_sync::spawn_state_sync_dump; +use framework::test_utils::TestEnvNightshadeSetupExt; +use framework::UNC_BASE; +use std::ops::ControlFlow; +use std::sync::Arc; +use std::time::Duration; + +#[test] +/// Produce several blocks, wait for the state dump thread to notice and +/// write files to a temp dir. +fn test_state_dump() { + init_test_logger(); + + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = 25; + + unc_actix_test_utils::run_actix(async { + let chain_genesis = ChainGenesis::new(&genesis); + let mut env = TestEnv::builder(chain_genesis.clone()) + .clients_count(1) + .use_state_snapshots() + .real_stores() + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let chain = &env.clients[0].chain; + let epoch_manager = env.clients[0].epoch_manager.clone(); + let runtime = env.clients[0].runtime_adapter.clone(); + let shard_tracker = chain.shard_tracker.clone(); + let mut config = env.clients[0].config.clone(); + let root_dir = tempfile::Builder::new().prefix("state_dump").tempdir().unwrap(); + config.state_sync.dump = Some(DumpConfig { + location: Filesystem { root_dir: root_dir.path().to_path_buf() }, + restart_dump_for_shards: None, + iteration_delay: Some(Duration::ZERO), + credentials_file: None, + }); + + let _state_sync_dump_handle = spawn_state_sync_dump( + &config, + chain_genesis, + epoch_manager.clone(), + shard_tracker, + runtime, + Some("test0".parse().unwrap()), + ) + .unwrap(); + + const MAX_HEIGHT: BlockHeight = 37; + for i in 1..=MAX_HEIGHT { + let block = env.clients[0].produce_block(i as u64).unwrap().unwrap(); + env.process_block(0, block, Provenance::PRODUCED); + } + let head = &env.clients[0].chain.head().unwrap(); + let epoch_id = head.clone().epoch_id; + let epoch_info = epoch_manager.get_epoch_info(&epoch_id).unwrap(); + let epoch_height = epoch_info.epoch_height(); + + wait_or_timeout(100, 10000, || async { + let mut all_parts_present = true; + + let shard_ids = epoch_manager.shard_ids(&epoch_id).unwrap(); + assert_ne!(shard_ids.len(), 0); + + for shard_id in shard_ids { + let num_parts = 1; + for part_id in 0..num_parts { + let path = root_dir.path().join(external_storage_location( + "unittest", + &epoch_id, + epoch_height, + shard_id, + part_id, + num_parts, + )); + if std::fs::read(&path).is_err() { + tracing::info!("Missing {:?}", path); + all_parts_present = false; + } + } + } + if all_parts_present { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .await + .unwrap(); + actix_rt::System::current().stop(); + }); +} + +/// This function tests that after a node does state sync, it has the data that corresponds to the state of the epoch previous to the dumping node's final block. +/// The way the test works: +/// set up 2 nodes: env.client[0] dumps state parts, env.client[1] state syncs with the dumped state parts. +/// A new account will be created in the epoch at account_creation_at_epoch_height, specifically at 2nd block of the epoch. +/// if is_final_block_in_new_epoch = true, dumping node's final block and head will both be in the next epoch after account creation; +/// otherwise, dumping node's head will be in the next epoch while its final block would still be in the epoch of account creation. +/// The test verifies that if dumping node's final block is in the next epoch after account creation, then the syncing node will have the account information after state sync; +/// otherwise, e.g. the dumping node's final block is in the same epoch as account creation, the syncing node should not have the account info after state sync. +fn run_state_sync_with_dumped_parts( + is_final_block_in_new_epoch: bool, + account_creation_at_epoch_height: u64, + epoch_length: u64, +) { + init_test_logger(); + if is_final_block_in_new_epoch { + tracing::info!("Testing for case when both head and final block of the dumping node are in new epoch..."); + } else { + tracing::info!("Testing for case when head is in new epoch, but final block isn't for the dumping node..."); + } + unc_actix_test_utils::run_actix(async { + let mut genesis = Genesis::test(vec!["test0".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let chain_genesis = ChainGenesis::new(&genesis); + let num_clients = 2; + let mut env = TestEnv::builder(chain_genesis.clone()) + .clients_count(num_clients) + .use_state_snapshots() + .real_stores() + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let genesis_hash = *genesis_block.hash(); + + let mut blocks = vec![]; + let chain = &env.clients[0].chain; + let epoch_manager = env.clients[0].epoch_manager.clone(); + let runtime = env.clients[0].runtime_adapter.clone(); + let shard_tracker = chain.shard_tracker.clone(); + let mut config = env.clients[0].config.clone(); + let root_dir = tempfile::Builder::new().prefix("state_dump").tempdir().unwrap(); + config.state_sync.dump = Some(DumpConfig { + location: Filesystem { root_dir: root_dir.path().to_path_buf() }, + restart_dump_for_shards: None, + iteration_delay: Some(Duration::ZERO), + credentials_file: None, + }); + let _state_sync_dump_handle = spawn_state_sync_dump( + &config, + chain_genesis, + epoch_manager.clone(), + shard_tracker, + runtime, + Some("test0".parse().unwrap()), + ) + .unwrap(); + + let account_creation_at_height = (account_creation_at_epoch_height - 1) * epoch_length + 2; + + let dump_node_head_height = if is_final_block_in_new_epoch { + (1 + account_creation_at_epoch_height) * epoch_length + } else { + account_creation_at_epoch_height * epoch_length + 1 + }; + + for i in 1..=dump_node_head_height { + if i == account_creation_at_height { + let tx = SignedTransaction::create_account( + 1, + "test0".parse().unwrap(), + "test_account".parse().unwrap(), + UNC_BASE, + signer.public_key(), + &signer, + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + blocks.push(block.clone()); + env.process_block(0, block.clone(), Provenance::PRODUCED); + env.process_block(1, block.clone(), Provenance::NONE); + } + + // check that the new account exists + let head = env.clients[0].chain.head().unwrap(); + let head_block = env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); + let response = env.clients[0] + .runtime_adapter + .query( + ShardUId::single_shard(), + &head_block.chunks()[0].prev_state_root(), + head.height, + 0, + &head.prev_block_hash, + &head.last_block_hash, + head_block.header().epoch_id(), + &QueryRequest::ViewAccount { account_id: "test_account".parse().unwrap() }, + ) + .unwrap(); + assert_matches!(response.kind, QueryResponseKind::ViewAccount(_)); + + let header = env.clients[0].chain.get_block_header(&head.last_block_hash).unwrap(); + let final_block_hash = header.last_final_block(); + let final_block_header = env.clients[0].chain.get_block_header(final_block_hash).unwrap(); + + tracing::info!( + dump_node_head_height, + final_block_height = final_block_header.height(), + "Dumping node state" + ); + + // check if final block is in the same epoch as head for dumping node + if is_final_block_in_new_epoch { + assert_eq!(header.epoch_id().clone(), final_block_header.epoch_id().clone()) + } else { + assert_ne!(header.epoch_id().clone(), final_block_header.epoch_id().clone()) + } + + let epoch_id = final_block_header.epoch_id().clone(); + let epoch_info = epoch_manager.get_epoch_info(&epoch_id).unwrap(); + let epoch_height = epoch_info.epoch_height(); + + let sync_block_height = (epoch_length * epoch_height + 1) as usize; + let sync_hash = *blocks[sync_block_height - 1].hash(); + + // the block at sync_block_height should be the start of an epoch + assert_ne!( + blocks[sync_block_height - 1].header().epoch_id(), + blocks[sync_block_height - 2].header().epoch_id() + ); + assert!(env.clients[0].chain.check_sync_hash_validity(&sync_hash).unwrap()); + let state_sync_header = + env.clients[0].chain.get_state_response_header(0, sync_hash).unwrap(); + let state_root = state_sync_header.chunk_prev_state_root(); + let num_parts = state_sync_header.num_state_parts(); + + wait_or_timeout(100, 10000, || async { + let mut all_parts_present = true; + + let shard_ids = epoch_manager.shard_ids(&epoch_id).unwrap(); + assert_ne!(shard_ids.len(), 0); + + for shard_id in shard_ids { + for part_id in 0..num_parts { + let path = root_dir.path().join(external_storage_location( + &config.chain_id, + &epoch_id, + epoch_height, + shard_id, + part_id, + num_parts, + )); + if std::fs::read(&path).is_err() { + tracing::info!("dumping node: Missing {:?}", path); + all_parts_present = false; + } else { + tracing::info!("dumping node: Populated {:?}", path); + } + } + } + if all_parts_present { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .await + .unwrap(); + + // Simulate state sync + tracing::info!("syncing node: simulating state sync.."); + env.clients[1].chain.set_state_header(0, sync_hash, state_sync_header).unwrap(); + let runtime_client_1 = Arc::clone(&env.clients[1].runtime_adapter); + let runtime_client_0 = Arc::clone(&env.clients[0].runtime_adapter); + let client_0_store = runtime_client_0.store(); + let mut store_update = runtime_client_1.store().store_update(); + assert!(runtime_client_1 + .get_flat_storage_manager() + .remove_flat_storage_for_shard(ShardUId::single_shard(), &mut store_update) + .unwrap()); + store_update.commit().unwrap(); + + for part_id in 0..num_parts { + let key = borsh::to_vec(&StatePartKey(sync_hash, 0, part_id)).unwrap(); + let part = client_0_store.get(DBCol::StateParts, &key).unwrap().unwrap(); + + runtime_client_1 + .apply_state_part(0, &state_root, PartId::new(part_id, num_parts), &part, &epoch_id) + .unwrap(); + } + env.clients[1].chain.set_state_finalize(0, sync_hash, Ok(())).unwrap(); + tracing::info!("syncing node: state sync finished."); + + let synced_block = env.clients[1].chain.get_block(&sync_hash).unwrap(); + let synced_block_header = env.clients[1].chain.get_block_header(&sync_hash).unwrap(); + let synced_block_tip = Tip::from_header(&synced_block_header); + let response = env.clients[1].runtime_adapter.query( + ShardUId::single_shard(), + &synced_block.chunks()[0].prev_state_root(), + synced_block_tip.height, + 0, + &synced_block_tip.prev_block_hash, + &synced_block_tip.last_block_hash, + synced_block_header.epoch_id(), + &QueryRequest::ViewAccount { account_id: "test_account".parse().unwrap() }, + ); + + if is_final_block_in_new_epoch { + tracing::info!(?response, "New Account should exist"); + assert_matches!( + response.unwrap().kind, + QueryResponseKind::ViewAccount(_), + "the synced node should have information about the created account" + ); + + // Check that inlined flat state values remain inlined. + { + let store0 = env.clients[0].chain.chain_store().store(); + let store1 = env.clients[1].chain.chain_store().store(); + let (num_inlined_before, num_ref_before) = count_flat_state_value_kinds(store0); + let (num_inlined_after, num_ref_after) = count_flat_state_value_kinds(store1); + // Nothing new created, number of flat state values should be identical. + assert_eq!(num_inlined_before, num_inlined_after); + assert_eq!(num_ref_before, num_ref_after); + } + } else { + tracing::info!(?response, "New Account shouldn't exist"); + assert!(response.is_err()); + assert_matches!( + response.unwrap_err(), + QueryError::UnknownAccount { .. }, + "the synced node should not have information about the created account" + ); + + // Check that inlined flat state values remain inlined. + { + let store0 = env.clients[0].chain.chain_store().store(); + let store1 = env.clients[1].chain.chain_store().store(); + let (num_inlined_before, _num_ref_before) = count_flat_state_value_kinds(store0); + let (num_inlined_after, _num_ref_after) = count_flat_state_value_kinds(store1); + // Created a new entry, but inlined values should stay inlinedNothing new created, number of flat state values should be identical. + assert!(num_inlined_before >= num_inlined_after); + assert!(num_inlined_after > 0); + } + } + actix_rt::System::current().stop(); + }); +} + +#[test] +/// This test verifies that after state sync, the syncing node has the data that corresponds to the state of the epoch previous to the dumping node's final block. +/// Specifically, it tests that the above holds true in both conditions: +/// - the dumping node's head is in new epoch but final block is not; +/// - the dumping node's head and final block are in same epoch +fn test_state_sync_w_dumped_parts() { + init_test_logger(); + let epoch_length = 5; + // excluding account_creation_at_epoch_height=1 because first epoch's epoch_id not being block hash of its first block cause issues + for account_creation_at_epoch_height in 2..=4 as u64 { + tracing::info!("account_creation_at_epoch_height = {}", account_creation_at_epoch_height); + run_state_sync_with_dumped_parts(false, account_creation_at_epoch_height, epoch_length); + run_state_sync_with_dumped_parts(true, account_creation_at_epoch_height, epoch_length); + } +} + +fn count_flat_state_value_kinds(store: &Store) -> (u64, u64) { + let mut num_inlined_values = 0; + let mut num_ref_values = 0; + for item in store_helper::iter_flat_state_entries(ShardUId::single_shard(), store, None, None) { + match item { + Ok((_, FlatStateValue::Ref(_))) => { + num_ref_values += 1; + } + Ok((_, FlatStateValue::Inlined(_))) => { + num_inlined_values += 1; + } + _ => {} + } + } + (num_inlined_values, num_ref_values) +} diff --git a/integration-tests/src/tests/client/state_snapshot.rs b/integration-tests/src/tests/client/state_snapshot.rs new file mode 100644 index 000000000..a35972cae --- /dev/null +++ b/integration-tests/src/tests/client/state_snapshot.rs @@ -0,0 +1,266 @@ +use unc_chain::{ChainGenesis, ChainStoreAccess, Provenance}; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_client::ProcessTxResponse; +use unc_crypto::{InMemorySigner, KeyType, Signer}; +use unc_o11y::testonly::init_test_logger; +use unc_primitives::block::Block; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::transaction::SignedTransaction; +use unc_store::config::StateSnapshotType; +use unc_store::flat::FlatStorageManager; +use unc_store::{ + config::TrieCacheConfig, test_utils::create_test_store, Mode, ShardTries, StateSnapshotConfig, + StoreConfig, TrieConfig, +}; +use unc_store::{NodeStorage, Store}; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; +use framework::UNC_BASE; +use std::path::PathBuf; + +struct StateSnaptshotTestEnv { + home_dir: PathBuf, + hot_store_path: PathBuf, + state_snapshot_subdir: PathBuf, + shard_tries: ShardTries, +} + +impl StateSnaptshotTestEnv { + fn new( + home_dir: PathBuf, + hot_store_path: PathBuf, + state_snapshot_subdir: PathBuf, + store: &Store, + ) -> Self { + let trie_cache_config = TrieCacheConfig { + default_max_bytes: 50_000_000, + per_shard_max_bytes: Default::default(), + shard_cache_deletions_queue_capacity: 0, + }; + let trie_config = TrieConfig { + shard_cache_config: trie_cache_config.clone(), + view_shard_cache_config: trie_cache_config, + enable_receipt_prefetching: false, + sweat_prefetch_receivers: Vec::new(), + sweat_prefetch_senders: Vec::new(), + load_mem_tries_for_shards: Vec::new(), + load_mem_tries_for_all_shards: false, + }; + let flat_storage_manager = FlatStorageManager::new(store.clone()); + let shard_uids = [ShardUId::single_shard()]; + let state_snapshot_config = StateSnapshotConfig { + state_snapshot_type: StateSnapshotType::EveryEpoch, + home_dir: home_dir.clone(), + hot_store_path: hot_store_path.clone(), + state_snapshot_subdir: state_snapshot_subdir.clone(), + compaction_enabled: true, + }; + let shard_tries = ShardTries::new( + store.clone(), + trie_config, + &shard_uids, + flat_storage_manager, + state_snapshot_config, + ); + Self { home_dir, hot_store_path, state_snapshot_subdir, shard_tries } + } +} + +fn set_up_test_env_for_state_snapshots(store: &Store) -> StateSnaptshotTestEnv { + let home_dir = + tempfile::Builder::new().prefix("storage").tempdir().unwrap().path().to_path_buf(); + let hot_store_path = PathBuf::from("data"); + let state_snapshot_subdir = PathBuf::from("state_snapshot"); + StateSnaptshotTestEnv::new(home_dir, hot_store_path, state_snapshot_subdir, store) +} + +#[test] +// there's no entry in rocksdb for STATE_SNAPSHOT_KEY, maybe_open_state_snapshot should return error instead of panic +fn test_maybe_open_state_snapshot_no_state_snapshot_key_entry() { + init_test_logger(); + let store = create_test_store(); + let test_env = set_up_test_env_for_state_snapshots(&store); + let result = + test_env.shard_tries.maybe_open_state_snapshot(|_| Ok(vec![ShardUId::single_shard()])); + assert!(result.is_err()); +} + +#[test] +// there's no file present in the path for state snapshot, maybe_open_state_snapshot should return error instead of panic +fn test_maybe_open_state_snapshot_file_not_exist() { + init_test_logger(); + let store = create_test_store(); + let test_env = set_up_test_env_for_state_snapshots(&store); + let snapshot_hash = CryptoHash::new(); + test_env.shard_tries.set_state_snapshot_hash(Some(snapshot_hash)).unwrap(); + let result = + test_env.shard_tries.maybe_open_state_snapshot(|_| Ok(vec![ShardUId::single_shard()])); + assert!(result.is_err()); +} + +#[test] +// there's garbage in the path for state snapshot, maybe_open_state_snapshot should return error instead of panic +fn test_maybe_open_state_snapshot_garbage_snapshot() { + use std::fs::{create_dir_all, File}; + use std::io::Write; + use std::path::Path; + init_test_logger(); + let store = create_test_store(); + let test_env = set_up_test_env_for_state_snapshots(&store); + let snapshot_hash = CryptoHash::new(); + test_env.shard_tries.set_state_snapshot_hash(Some(snapshot_hash)).unwrap(); + let snapshot_path = ShardTries::get_state_snapshot_base_dir( + &snapshot_hash, + &test_env.home_dir, + &test_env.hot_store_path, + &test_env.state_snapshot_subdir, + ); + if let Some(parent) = Path::new(&snapshot_path).parent() { + create_dir_all(parent).unwrap(); + } + let mut file = File::create(snapshot_path).unwrap(); + // write some garbage + let data: Vec = vec![1, 2, 3, 4]; + file.write_all(&data).unwrap(); + + let result = + test_env.shard_tries.maybe_open_state_snapshot(|_| Ok(vec![ShardUId::single_shard()])); + assert!(result.is_err()); +} + +fn verify_make_snapshot( + state_snapshot_test_env: &StateSnaptshotTestEnv, + block_hash: CryptoHash, + block: &Block, +) -> Result<(), anyhow::Error> { + state_snapshot_test_env.shard_tries.delete_state_snapshot(); + state_snapshot_test_env.shard_tries.create_state_snapshot( + block_hash, + &[ShardUId::single_shard()], + block, + )?; + // check that make_state_snapshot does not panic or err out + // assert!(res.is_ok()); + let snapshot_path = ShardTries::get_state_snapshot_base_dir( + &block_hash, + &state_snapshot_test_env.home_dir, + &state_snapshot_test_env.hot_store_path, + &state_snapshot_test_env.state_snapshot_subdir, + ); + // check that the snapshot just made can be opened + state_snapshot_test_env + .shard_tries + .maybe_open_state_snapshot(|_| Ok(vec![ShardUId::single_shard()]))?; + // check that the entry of STATE_SNAPSHOT_KEY is the latest block hash + let db_state_snapshot_hash = state_snapshot_test_env.shard_tries.get_state_snapshot_hash()?; + if db_state_snapshot_hash != block_hash { + return Err(anyhow::Error::msg( + "the entry of STATE_SNAPSHOT_KEY does not equal to the prev block hash", + )); + } + // check that the stored snapshot in file system is an actual snapshot + let store_config = StoreConfig::default(); + let opener = NodeStorage::opener(&snapshot_path, false, &store_config, None); + let _storage = opener.open_in_mode(Mode::ReadOnly)?; + // check that there's only one snapshot at the parent directory of snapshot path + let parent_path = snapshot_path + .parent() + .ok_or(anyhow::anyhow!("{snapshot_path:?} needs to have a parent dir"))?; + let parent_path_result = std::fs::read_dir(parent_path)?; + if vec![parent_path_result.filter_map(Result::ok)].len() > 1 { + return Err(anyhow::Error::msg( + "there are more than 1 snapshot file in the snapshot parent directory", + )); + } + return Ok(()); +} + +fn delete_content_at_path(path: &str) -> std::io::Result<()> { + let metadata = std::fs::metadata(path)?; + if metadata.is_dir() { + std::fs::remove_dir_all(path)?; + } else { + std::fs::remove_file(path)?; + } + Ok(()) +} + +#[test] +// Runs a validator node. +// Makes a state snapshot after processing every block. Each block contains a +// transaction creating an account. +fn test_make_state_snapshot() { + init_test_logger(); + let genesis = Genesis::test(vec!["test0".parse().unwrap()], 1); + let mut env = TestEnv::builder(ChainGenesis::test()) + .clients_count(1) + .use_state_snapshots() + .real_stores() + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let genesis_hash = *genesis_block.hash(); + + let mut blocks = vec![]; + + let store = env.clients[0].chain.chain_store().store(); + let state_snapshot_test_env = set_up_test_env_for_state_snapshots(store); + + for i in 1..=5 { + let new_account_id = format!("test_account_{i}"); + let nonce = i; + let tx = SignedTransaction::create_account( + nonce, + "test0".parse().unwrap(), + new_account_id.parse().unwrap(), + UNC_BASE, + signer.public_key(), + &signer, + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + blocks.push(block.clone()); + env.process_block(0, block.clone(), Provenance::PRODUCED); + assert_eq!( + format!("{:?}", Ok::<(), anyhow::Error>(())), + format!("{:?}", verify_make_snapshot(&state_snapshot_test_env, *block.hash(), &block)) + ); + } + + // check that if the entry in DBCol::STATE_SNAPSHOT_KEY was missing while snapshot file exists, an overwrite of snapshot can succeed + state_snapshot_test_env.shard_tries.set_state_snapshot_hash(None).unwrap(); + let head = env.clients[0].chain.head().unwrap(); + let head_block_hash = head.last_block_hash; + let head_block = env.clients[0].chain.get_block(&head_block_hash).unwrap(); + assert_eq!( + format!("{:?}", Ok::<(), anyhow::Error>(())), + format!( + "{:?}", + verify_make_snapshot(&state_snapshot_test_env, head_block_hash, &head_block) + ) + ); + + // check that if the snapshot is deleted from file system while there's entry in DBCol::STATE_SNAPSHOT_KEY + // recreating the snapshot will succeed + let snapshot_hash = head.last_block_hash; + let snapshot_path = ShardTries::get_state_snapshot_base_dir( + &snapshot_hash, + &state_snapshot_test_env.home_dir, + &state_snapshot_test_env.hot_store_path, + &state_snapshot_test_env.state_snapshot_subdir, + ); + delete_content_at_path(snapshot_path.to_str().unwrap()).unwrap(); + assert_eq!( + format!("{:?}", Ok::<(), anyhow::Error>(())), + format!( + "{:?}", + verify_make_snapshot(&state_snapshot_test_env, head.last_block_hash, &head_block) + ) + ); +} diff --git a/integration-tests/src/tests/client/sync_state_nodes.rs b/integration-tests/src/tests/client/sync_state_nodes.rs new file mode 100644 index 000000000..6d9204996 --- /dev/null +++ b/integration-tests/src/tests/client/sync_state_nodes.rs @@ -0,0 +1,1076 @@ +use crate::test_helpers::heavy_test; +use actix::{Actor, System}; +use futures::{future, FutureExt}; +use unc_actix_test_utils::run_actix; +use unc_chain::chain::ApplyStatePartsRequest; +use unc_chain::{ChainGenesis, Provenance}; +use unc_chain_configs::ExternalStorageLocation::Filesystem; +use unc_chain_configs::{DumpConfig, ExternalStorageConfig, Genesis, SyncConfig}; +use unc_client::adapter::{StateRequestHeader, StateRequestPart, StateResponse}; +use unc_client::test_utils::TestEnv; +use unc_client::{GetBlock, ProcessTxResponse}; +use unc_client_primitives::types::GetValidatorInfo; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_network::tcp; +use unc_network::test_utils::{convert_boot_nodes, wait_or_timeout, WaitOrTimeoutActor}; +use unc_o11y::testonly::{init_integration_logger, init_test_logger}; +use unc_o11y::WithSpanContextExt; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::state_part::PartId; +use unc_primitives::state_sync::{CachedParts, StatePartKey}; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{BlockId, BlockReference, EpochId, EpochReference}; +use unc_primitives::utils::MaybeValidated; +use unc_primitives_core::types::ShardId; +use unc_store::DBCol; +use framework::test_utils::TestEnvNightshadeSetupExt; +use framework::{config::GenesisExt, load_test_config, start_with_config}; +use std::ops::ControlFlow; +use std::sync::{Arc, RwLock}; +use std::time::Duration; + +/// One client is in front, another must sync to it using state (fast) sync. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn sync_state_nodes() { + heavy_test(|| { + init_integration_logger(); + + let genesis = Genesis::test(vec!["test1".parse().unwrap()], 1); + + let (port1, port2) = + (tcp::ListenerAddr::reserve_for_test(), tcp::ListenerAddr::reserve_for_test()); + let mut near1 = load_test_config("test1", port1, genesis.clone()); + near1.network_config.peer_store.boot_nodes = convert_boot_nodes(vec![]); + near1.client_config.min_num_peers = 0; + near1.client_config.epoch_sync_enabled = false; + run_actix(async move { + let dir1 = tempfile::Builder::new().prefix("sync_nodes_1").tempdir().unwrap(); + let framework::NearNode { view_client: view_client1, .. } = + start_with_config(dir1.path(), near1).expect("start_with_config"); + + let view_client2_holder = Arc::new(RwLock::new(None)); + let arbiters_holder = Arc::new(RwLock::new(vec![])); + let arbiters_holder2 = arbiters_holder; + + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + if view_client2_holder.read().unwrap().is_none() { + let view_client2_holder2 = view_client2_holder.clone(); + let arbiters_holder2 = arbiters_holder2.clone(); + let genesis2 = genesis.clone(); + + let actor = view_client1.send(GetBlock::latest().with_span_context()); + let actor = actor.then(move |res| { + match &res { + Ok(Ok(b)) if b.header.height >= 101 => { + let mut view_client2_holder2 = + view_client2_holder2.write().unwrap(); + let mut arbiters_holder2 = arbiters_holder2.write().unwrap(); + + if view_client2_holder2.is_none() { + let mut near2 = + load_test_config("test2", port2, genesis2.clone()); + near2.client_config.skip_sync_wait = false; + near2.client_config.min_num_peers = 1; + near2.network_config.peer_store.boot_nodes = + convert_boot_nodes(vec![("test1", *port1)]); + near2.client_config.epoch_sync_enabled = false; + + let dir2 = tempfile::Builder::new() + .prefix("sync_nodes_2") + .tempdir() + .unwrap(); + let framework::NearNode { + view_client: view_client2, + arbiters, + .. + } = start_with_config(dir2.path(), near2) + .expect("start_with_config"); + *view_client2_holder2 = Some(view_client2); + *arbiters_holder2 = arbiters; + } + } + Ok(Ok(b)) if b.header.height < 101 => { + println!("FIRST STAGE {}", b.header.height) + } + Err(_) => return future::ready(()), + _ => {} + }; + future::ready(()) + }); + actix::spawn(actor); + } + + if let Some(view_client2) = &*view_client2_holder.write().unwrap() { + let actor = view_client2.send(GetBlock::latest().with_span_context()); + let actor = actor.then(|res| { + match &res { + Ok(Ok(b)) if b.header.height >= 101 => System::current().stop(), + Ok(Ok(b)) if b.header.height < 101 => { + println!("SECOND STAGE {}", b.header.height) + } + Err(_) => return future::ready(()), + _ => {} + }; + future::ready(()) + }); + actix::spawn(actor); + } else { + } + }), + 100, + 60000, + ) + .start(); + }); + }); +} + +/// One client is in front, another must sync to it using state (fast) sync. +#[cfg(feature = "expensive_tests")] +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn sync_state_nodes_multishard() { + heavy_test(|| { + init_integration_logger(); + + let mut genesis = Genesis::test_sharded_new_version( + vec![ + "test1".parse().unwrap(), + "test2".parse().unwrap(), + "test3".parse().unwrap(), + "test4".parse().unwrap(), + ], + 4, + vec![2, 2], + ); + genesis.config.epoch_length = 150; // so that by the time test2 joins it is not kicked out yet + + run_actix(async move { + let (port1, port2, port3, port4) = ( + tcp::ListenerAddr::reserve_for_test(), + tcp::ListenerAddr::reserve_for_test(), + tcp::ListenerAddr::reserve_for_test(), + tcp::ListenerAddr::reserve_for_test(), + ); + + let mut near1 = load_test_config("test1", port1, genesis.clone()); + near1.network_config.peer_store.boot_nodes = + convert_boot_nodes(vec![("test3", *port3), ("test4", *port4)]); + near1.client_config.min_num_peers = 2; + near1.client_config.min_block_production_delay = Duration::from_millis(200); + near1.client_config.max_block_production_delay = Duration::from_millis(400); + near1.client_config.epoch_sync_enabled = false; + + let mut near3 = load_test_config("test3", port3, genesis.clone()); + near3.network_config.peer_store.boot_nodes = + convert_boot_nodes(vec![("test1", *port1), ("test4", *port4)]); + near3.client_config.min_num_peers = 2; + near3.client_config.min_block_production_delay = + near1.client_config.min_block_production_delay; + near3.client_config.max_block_production_delay = + near1.client_config.max_block_production_delay; + near3.client_config.epoch_sync_enabled = false; + + let mut near4 = load_test_config("test4", port4, genesis.clone()); + near4.network_config.peer_store.boot_nodes = + convert_boot_nodes(vec![("test1", *port1), ("test3", *port3)]); + near4.client_config.min_num_peers = 2; + near4.client_config.min_block_production_delay = + near1.client_config.min_block_production_delay; + near4.client_config.max_block_production_delay = + near1.client_config.max_block_production_delay; + near4.client_config.epoch_sync_enabled = false; + + let dir1 = tempfile::Builder::new().prefix("sync_nodes_1").tempdir().unwrap(); + let framework::NearNode { view_client: view_client1, .. } = + start_with_config(dir1.path(), near1).expect("start_with_config"); + + let dir3 = tempfile::Builder::new().prefix("sync_nodes_3").tempdir().unwrap(); + start_with_config(dir3.path(), near3).expect("start_with_config"); + + let dir4 = tempfile::Builder::new().prefix("sync_nodes_4").tempdir().unwrap(); + start_with_config(dir4.path(), near4).expect("start_with_config"); + + let view_client2_holder = Arc::new(RwLock::new(None)); + let arbiter_holder = Arc::new(RwLock::new(vec![])); + let arbiter_holder2 = arbiter_holder; + + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + if view_client2_holder.read().unwrap().is_none() { + let view_client2_holder2 = view_client2_holder.clone(); + let arbiter_holder2 = arbiter_holder2.clone(); + let genesis2 = genesis.clone(); + + let actor = view_client1.send(GetBlock::latest().with_span_context()); + let actor = actor.then(move |res| { + match &res { + Ok(Ok(b)) if b.header.height >= 101 => { + let mut view_client2_holder2 = + view_client2_holder2.write().unwrap(); + let mut arbiter_holder2 = arbiter_holder2.write().unwrap(); + + if view_client2_holder2.is_none() { + let mut near2 = load_test_config("test2", port2, genesis2); + near2.client_config.skip_sync_wait = false; + near2.client_config.min_num_peers = 3; + near2.client_config.min_block_production_delay = + Duration::from_millis(200); + near2.client_config.max_block_production_delay = + Duration::from_millis(400); + near2.network_config.peer_store.boot_nodes = + convert_boot_nodes(vec![ + ("test1", *port1), + ("test3", *port3), + ("test4", *port4), + ]); + near2.client_config.epoch_sync_enabled = false; + + let dir2 = tempfile::Builder::new() + .prefix("sync_nodes_2") + .tempdir() + .unwrap(); + let framework::NearNode { + view_client: view_client2, + arbiters, + .. + } = start_with_config(dir2.path(), near2) + .expect("start_with_config"); + *view_client2_holder2 = Some(view_client2); + *arbiter_holder2 = arbiters; + } + } + Ok(Ok(b)) if b.header.height < 101 => { + println!("FIRST STAGE {}", b.header.height) + } + Err(_) => return future::ready(()), + _ => {} + }; + future::ready(()) + }); + actix::spawn(actor); + } + + if let Some(view_client2) = &*view_client2_holder.write().unwrap() { + let actor = view_client2.send(GetBlock::latest().with_span_context()); + let actor = actor.then(|res| { + match &res { + Ok(Ok(b)) if b.header.height >= 101 => System::current().stop(), + Ok(Ok(b)) if b.header.height < 101 => { + println!("SECOND STAGE {}", b.header.height) + } + Ok(Err(e)) => { + println!("SECOND STAGE ERROR1: {:?}", e); + return future::ready(()); + } + Err(e) => { + println!("SECOND STAGE ERROR2: {:?}", e); + return future::ready(()); + } + _ => { + assert!(false); + } + }; + future::ready(()) + }); + actix::spawn(actor); + } + }), + 100, + 600000, + ) + .start(); + }); + }); +} + +/// Start a validator that validators four shards. Since we only have 3 accounts one shard must have +/// empty state. Start another node that does state sync. Check state sync on empty state works. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn sync_empty_state() { + heavy_test(|| { + init_integration_logger(); + + let mut genesis = Genesis::test_sharded_new_version( + vec!["test1".parse().unwrap(), "test2".parse().unwrap()], + 1, + vec![1, 1, 1, 1], + ); + genesis.config.epoch_length = 20; + + run_actix(async move { + let (port1, port2) = + (tcp::ListenerAddr::reserve_for_test(), tcp::ListenerAddr::reserve_for_test()); + // State sync triggers when header head is two epochs in the future. + // Produce more blocks to make sure that state sync gets triggered when the second node starts. + let state_sync_horizon = 10; + let block_header_fetch_horizon = 1; + let block_fetch_horizon = 1; + + let mut near1 = load_test_config("test1", port1, genesis.clone()); + near1.client_config.min_num_peers = 0; + near1.client_config.min_block_production_delay = Duration::from_millis(200); + near1.client_config.max_block_production_delay = Duration::from_millis(400); + near1.client_config.epoch_sync_enabled = false; + + let dir1 = tempfile::Builder::new().prefix("sync_nodes_1").tempdir().unwrap(); + let framework::NearNode { view_client: view_client1, .. } = + start_with_config(dir1.path(), near1).expect("start_with_config"); + let dir2 = Arc::new(tempfile::Builder::new().prefix("sync_nodes_2").tempdir().unwrap()); + + let view_client2_holder = Arc::new(RwLock::new(None)); + let arbiters_holder = Arc::new(RwLock::new(vec![])); + let arbiters_holder2 = arbiters_holder; + + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + if view_client2_holder.read().unwrap().is_none() { + let view_client2_holder2 = view_client2_holder.clone(); + let arbiters_holder2 = arbiters_holder2.clone(); + let genesis2 = genesis.clone(); + let dir2 = dir2.clone(); + + let actor = view_client1.send(GetBlock::latest().with_span_context()); + let actor = actor.then(move |res| { + match &res { + Ok(Ok(b)) if b.header.height >= state_sync_horizon + 1 => { + let mut view_client2_holder2 = + view_client2_holder2.write().unwrap(); + let mut arbiters_holder2 = arbiters_holder2.write().unwrap(); + + if view_client2_holder2.is_none() { + let mut near2 = load_test_config("test2", port2, genesis2); + near2.network_config.peer_store.boot_nodes = + convert_boot_nodes(vec![("test1", *port1)]); + near2.client_config.min_num_peers = 1; + near2.client_config.min_block_production_delay = + Duration::from_millis(200); + near2.client_config.max_block_production_delay = + Duration::from_millis(400); + near2.client_config.block_header_fetch_horizon = + block_header_fetch_horizon; + near2.client_config.block_fetch_horizon = + block_fetch_horizon; + near2.client_config.tracked_shards = vec![0, 1, 2, 3]; + near2.client_config.epoch_sync_enabled = false; + + let framework::NearNode { + view_client: view_client2, + arbiters, + .. + } = start_with_config(dir2.path(), near2) + .expect("start_with_config"); + *view_client2_holder2 = Some(view_client2); + *arbiters_holder2 = arbiters; + } + } + Ok(Ok(b)) if b.header.height <= state_sync_horizon => { + println!("FIRST STAGE {}", b.header.height) + } + Err(_) => return future::ready(()), + _ => {} + }; + future::ready(()) + }); + actix::spawn(actor); + } + + if let Some(view_client2) = &*view_client2_holder.write().unwrap() { + let actor = view_client2.send(GetBlock::latest().with_span_context()); + let actor = actor.then(|res| { + match &res { + Ok(Ok(b)) if b.header.height >= 40 => System::current().stop(), + Ok(Ok(b)) if b.header.height < 40 => { + println!("SECOND STAGE {}", b.header.height) + } + Ok(Err(e)) => { + println!("SECOND STAGE ERROR1: {:?}", e); + return future::ready(()); + } + Err(e) => { + println!("SECOND STAGE ERROR2: {:?}", e); + return future::ready(()); + } + _ => { + assert!(false); + } + }; + future::ready(()) + }); + actix::spawn(actor); + } + }), + 100, + 600000, + ) + .start(); + }); + }); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +// FIXME(#9650): locks should not be held across await points, allowed currently only because the +// lint started triggering during a toolchain bump. +#[allow(clippy::await_holding_lock)] +/// Runs one node for some time, which dumps state to a temp directory. +/// Start the second node which gets state parts from that temp directory. +fn sync_state_dump() { + heavy_test(|| { + init_integration_logger(); + + let mut genesis = Genesis::test_sharded_new_version( + vec!["test1".parse().unwrap(), "test2".parse().unwrap()], + 1, + vec![1], + ); + // Needs to be long enough to give enough time to the second node to + // start, sync headers and find a dump of state. + genesis.config.epoch_length = 30; + + run_actix(async move { + let (port1, port2) = + (tcp::ListenerAddr::reserve_for_test(), tcp::ListenerAddr::reserve_for_test()); + // Produce more blocks to make sure that state sync gets triggered when the second node starts. + let state_sync_horizon = 50; + let block_header_fetch_horizon = 1; + let block_fetch_horizon = 1; + + let mut near1 = load_test_config("test1", port1, genesis.clone()); + near1.client_config.min_num_peers = 0; + // An epoch passes in about 9 seconds. + near1.client_config.min_block_production_delay = Duration::from_millis(300); + near1.client_config.max_block_production_delay = Duration::from_millis(600); + near1.client_config.epoch_sync_enabled = false; + near1.client_config.tracked_shards = vec![0]; // Track all shards. + let dump_dir = tempfile::Builder::new().prefix("state_dump_1").tempdir().unwrap(); + near1.client_config.state_sync.dump = Some(DumpConfig { + location: Filesystem { root_dir: dump_dir.path().to_path_buf() }, + restart_dump_for_shards: None, + iteration_delay: Some(Duration::from_millis(500)), + credentials_file: None, + }); + near1.config.store.state_snapshot_enabled = true; + near1.config.store.state_snapshot_compaction_enabled = false; + + let dir1 = tempfile::Builder::new().prefix("sync_nodes_1").tempdir().unwrap(); + let framework::NearNode { + view_client: view_client1, + state_sync_dump_handle: _state_sync_dump_handle, + .. + } = start_with_config(dir1.path(), near1).expect("start_with_config"); + let dir2 = tempfile::Builder::new().prefix("sync_nodes_2").tempdir().unwrap(); + + let view_client2_holder = Arc::new(RwLock::new(None)); + let arbiters_holder = Arc::new(RwLock::new(vec![])); + let arbiters_holder2 = arbiters_holder; + + wait_or_timeout(1000, 60000, || async { + if view_client2_holder.read().unwrap().is_none() { + let view_client2_holder2 = view_client2_holder.clone(); + let arbiters_holder2 = arbiters_holder2.clone(); + let genesis2 = genesis.clone(); + + match view_client1.send(GetBlock::latest().with_span_context()).await { + Ok(Ok(b)) if b.header.height >= genesis.config.epoch_length + 2 => { + let mut view_client2_holder2 = view_client2_holder2.write().unwrap(); + let mut arbiters_holder2 = arbiters_holder2.write().unwrap(); + + if view_client2_holder2.is_none() { + let mut near2 = load_test_config("test2", port2, genesis2); + near2.network_config.peer_store.boot_nodes = + convert_boot_nodes(vec![("test1", *port1)]); + near2.client_config.min_num_peers = 1; + near2.client_config.min_block_production_delay = + Duration::from_millis(300); + near2.client_config.max_block_production_delay = + Duration::from_millis(600); + near2.client_config.block_header_fetch_horizon = + block_header_fetch_horizon; + near2.client_config.block_fetch_horizon = block_fetch_horizon; + near2.client_config.tracked_shards = vec![0]; // Track all shards. + near2.client_config.epoch_sync_enabled = false; + near2.client_config.state_sync_enabled = true; + near2.client_config.state_sync_timeout = Duration::from_secs(2); + near2.client_config.state_sync.sync = + SyncConfig::ExternalStorage(ExternalStorageConfig { + location: Filesystem { + root_dir: dump_dir.path().to_path_buf(), + }, + num_concurrent_requests: 1, + num_concurrent_requests_during_catchup: 1, + }); + + let framework::NearNode { + view_client: view_client2, arbiters, .. + } = start_with_config(dir2.path(), near2) + .expect("start_with_config"); + *view_client2_holder2 = Some(view_client2); + *arbiters_holder2 = arbiters; + } + } + Ok(Ok(b)) if b.header.height <= state_sync_horizon => { + tracing::info!("FIRST STAGE {}", b.header.height); + } + Err(_) => {} + _ => {} + }; + return ControlFlow::Continue(()); + } + + if let Some(view_client2) = &*view_client2_holder.write().unwrap() { + match view_client2.send(GetBlock::latest().with_span_context()).await { + Ok(Ok(b)) if b.header.height >= 40 => { + return ControlFlow::Break(()); + } + Ok(Ok(b)) if b.header.height < 40 => { + tracing::info!("SECOND STAGE {}", b.header.height) + } + Ok(Err(e)) => { + tracing::info!("SECOND STAGE ERROR1: {:?}", e); + } + Err(e) => { + tracing::info!("SECOND STAGE ERROR2: {:?}", e); + } + _ => { + assert!(false); + } + }; + return ControlFlow::Continue(()); + } + + panic!("Unexpected"); + }) + .await + .unwrap(); + System::current().stop(); + }); + }); +} + +#[test] +// Test that state sync behaves well when the chunks are absent at the end of the epoch. +// The test actually fails and the code needs fixing. +fn test_dump_epoch_missing_chunk_in_last_block() { + heavy_test(|| { + init_test_logger(); + let epoch_length = 10; + + for num_last_chunks_missing in 0..6 { + assert!(num_last_chunks_missing < epoch_length); + let mut genesis = + Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + let mut env = TestEnv::builder(ChainGenesis::new(&genesis)) + .clients_count(2) + .use_state_snapshots() + .real_stores() + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build(); + + let genesis_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + let mut blocks = vec![genesis_block.clone()]; + let signer = + InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let target_height = epoch_length + 1; + for i in 1..=target_height { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + blocks.push(block.clone()); + if (i % epoch_length) != 0 + && epoch_length - (i % epoch_length) <= num_last_chunks_missing + { + // Don't produce chunks for the last blocks of an epoch. + env.clients[0] + .process_block_test_no_produce_chunk( + MaybeValidated::from(block.clone()), + Provenance::PRODUCED, + ) + .unwrap(); + tracing::info!( + "Block {i}: {:?} -- produced no chunk", + block.header().epoch_id() + ); + } else { + env.process_block(0, block.clone(), Provenance::PRODUCED); + tracing::info!( + "Block {i}: {:?} -- also produced a chunk", + block.header().epoch_id() + ); + } + env.process_block(1, block, Provenance::NONE); + + let tx = SignedTransaction::send_money( + i + 1, + "test0".parse().unwrap(), + "test1".parse().unwrap(), + &signer, + 1, + *genesis_block.hash(), + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + + // Simulate state sync + + // No blocks were skipped, therefore we can compute the block height of the first block of the current epoch. + let sync_hash_height = ((target_height / epoch_length) * epoch_length + 1) as usize; + let sync_hash = *blocks[sync_hash_height].hash(); + assert_ne!( + blocks[sync_hash_height].header().epoch_id(), + blocks[sync_hash_height - 1].header().epoch_id() + ); + + let state_sync_header = + env.clients[0].chain.get_state_response_header(0, sync_hash).unwrap(); + let num_parts = state_sync_header.num_state_parts(); + let state_root = state_sync_header.chunk_prev_state_root(); + // Check that state parts can be obtained. + let state_sync_parts: Vec<_> = (0..num_parts) + .map(|i| { + // This should obviously not fail, aka succeed. + env.clients[0].chain.get_state_response_part(0, i, sync_hash).unwrap() + }) + .collect(); + + env.clients[1].chain.reset_data_pre_state_sync(sync_hash).unwrap(); + let epoch_id = blocks.last().unwrap().header().epoch_id(); + for i in 0..num_parts { + env.clients[1] + .runtime_adapter + .apply_state_part( + 0, + &state_root, + PartId::new(i, num_parts), + &state_sync_parts[i as usize], + &epoch_id, + ) + .unwrap(); + } + + env.clients[1].chain.set_state_header(0, sync_hash, state_sync_header).unwrap(); + for i in 0..num_parts { + env.clients[1] + .chain + .set_state_part( + 0, + sync_hash, + PartId::new(i, num_parts), + &state_sync_parts[i as usize], + ) + .unwrap(); + } + let rt = Arc::clone(&env.clients[1].runtime_adapter); + let f = move |msg: ApplyStatePartsRequest| { + let store = rt.store(); + + let shard_id = msg.shard_uid.shard_id as ShardId; + let mut store_update = store.store_update(); + assert!(rt + .get_flat_storage_manager() + .remove_flat_storage_for_shard(msg.shard_uid, &mut store_update) + .unwrap()); + store_update.commit().unwrap(); + + for part_id in 0..msg.num_parts { + let key = + borsh::to_vec(&StatePartKey(msg.sync_hash, shard_id, part_id)).unwrap(); + let part = store.get(DBCol::StateParts, &key).unwrap().unwrap(); + + rt.apply_state_part( + shard_id, + &msg.state_root, + PartId::new(part_id, msg.num_parts), + &part, + &msg.epoch_id, + ) + .unwrap(); + } + }; + env.clients[1].chain.schedule_apply_state_parts(0, sync_hash, num_parts, &f).unwrap(); + env.clients[1].chain.set_state_finalize(0, sync_hash, Ok(())).unwrap(); + let last_chunk_height = epoch_length - num_last_chunks_missing; + for height in 1..epoch_length { + if height < last_chunk_height { + assert!(env.clients[1] + .chain + .get_chunk_extra(blocks[height as usize].hash(), &ShardUId::single_shard()) + .is_err()); + } else { + let chunk_extra = env.clients[1] + .chain + .get_chunk_extra(blocks[height as usize].hash(), &ShardUId::single_shard()) + .unwrap(); + let expected_chunk_extra = env.clients[0] + .chain + .get_chunk_extra( + blocks[last_chunk_height as usize].hash(), + &ShardUId::single_shard(), + ) + .unwrap(); + // The chunk extra of the prev block of sync block should be the same as the node that it is syncing from + assert_eq!(chunk_extra, expected_chunk_extra); + } + } + } + }); +} + +#[test] +// Tests StateRequestHeader and StateRequestPart. +fn test_state_sync_headers() { + heavy_test(|| { + init_test_logger(); + + run_actix(async { + let mut genesis = Genesis::test(vec!["test1".parse().unwrap()], 1); + // Increase epoch_length if the test is flaky. + genesis.config.epoch_length = 50; + + let mut near1 = + load_test_config("test1", tcp::ListenerAddr::reserve_for_test(), genesis.clone()); + near1.client_config.min_num_peers = 0; + near1.client_config.epoch_sync_enabled = false; + near1.client_config.tracked_shards = vec![0]; // Track all shards. + let dir1 = + tempfile::Builder::new().prefix("test_state_sync_headers").tempdir().unwrap(); + near1.config.store.state_snapshot_enabled = true; + near1.config.store.state_snapshot_compaction_enabled = false; + + let framework::NearNode { view_client: view_client1, .. } = + start_with_config(dir1.path(), near1).expect("start_with_config"); + + // First we need to find sync_hash. That is done in 3 steps: + // 1. Get the latest block + // 2. Query validators for the epoch_id of that block. + // 3. Get a block at 'epoch_start_height' that is found in the response of the validators method. + // + // Second, we request state sync header. + // Third, we request state sync part with part_id = 0. + wait_or_timeout(1000, 110000, || async { + let epoch_id = match view_client1.send(GetBlock::latest().with_span_context()).await + { + Ok(Ok(b)) => Some(b.header.epoch_id), + _ => None, + }; + // async is hard, will use this construct to reduce nestedness. + let epoch_id = match epoch_id { + Some(x) => x, + None => return ControlFlow::Continue(()), + }; + tracing::info!(?epoch_id, "got epoch_id"); + + let epoch_start_height = match view_client1 + .send( + GetValidatorInfo { + epoch_reference: EpochReference::EpochId(EpochId(epoch_id)), + } + .with_span_context(), + ) + .await + { + Ok(Ok(v)) => Some(v.epoch_start_height), + _ => None, + }; + let epoch_start_height = match epoch_start_height { + Some(x) => x, + None => return ControlFlow::Continue(()), + }; + tracing::info!(epoch_start_height, "got epoch_start_height"); + + let sync_hash_and_num_shards = match view_client1 + .send( + GetBlock(BlockReference::BlockId(BlockId::Height(epoch_start_height))) + .with_span_context(), + ) + .await + { + Ok(Ok(b)) => Some((b.header.hash, b.chunks.len())), + _ => None, + }; + let (sync_hash, num_shards) = match sync_hash_and_num_shards { + Some(x) => x, + None => return ControlFlow::Continue(()), + }; + tracing::info!(?sync_hash, num_shards, "got sync_hash"); + + for shard_id in 0..num_shards { + // Make StateRequestHeader and expect that the response contains a header and `can_generate` is true. + let state_response_info = match view_client1 + .send( + StateRequestHeader { shard_id: shard_id as ShardId, sync_hash } + .with_span_context(), + ) + .await + { + Ok(Some(StateResponse(state_response_info))) => Some(state_response_info), + _ => None, + }; + let state_response_info = match state_response_info { + Some(x) => x, + None => return ControlFlow::Continue(()), + }; + let state_response = state_response_info.take_state_response(); + let cached_parts = state_response.cached_parts().clone(); + let can_generate = state_response.can_generate(); + assert!(state_response.part().is_none()); + if let Some(_header) = state_response.take_header() { + if !can_generate { + tracing::info!( + ?sync_hash, + shard_id, + ?cached_parts, + can_generate, + "got header but cannot generate" + ); + return ControlFlow::Continue(()); + } + tracing::info!( + ?sync_hash, + shard_id, + ?cached_parts, + can_generate, + "got header" + ); + } else { + tracing::info!( + ?sync_hash, + shard_id, + ?cached_parts, + can_generate, + "got no header" + ); + return ControlFlow::Continue(()); + } + + // Make StateRequestPart and expect that the response contains a part and `can_generate` is true and part_id = 0 and the node has all parts cached. + let state_response_info = match view_client1 + .send( + StateRequestPart { + shard_id: shard_id as ShardId, + sync_hash, + part_id: 0, + } + .with_span_context(), + ) + .await + { + Ok(Some(StateResponse(state_response_info))) => Some(state_response_info), + _ => None, + }; + let state_response_info = match state_response_info { + Some(x) => x, + None => return ControlFlow::Continue(()), + }; + let state_response = state_response_info.take_state_response(); + let cached_parts = state_response.cached_parts().clone(); + let can_generate = state_response.can_generate(); + let part = state_response.part().clone(); + assert!(state_response.take_header().is_none()); + if let Some((part_id, _part)) = part { + if !can_generate + || cached_parts != Some(CachedParts::AllParts) + || part_id != 0 + { + tracing::info!( + ?sync_hash, + shard_id, + ?cached_parts, + can_generate, + part_id, + "got part but shard info is unexpected" + ); + return ControlFlow::Continue(()); + } + tracing::info!( + ?sync_hash, + shard_id, + ?cached_parts, + can_generate, + part_id, + "got part" + ); + } else { + tracing::info!( + ?sync_hash, + shard_id, + ?cached_parts, + can_generate, + "got no part" + ); + return ControlFlow::Continue(()); + } + } + return ControlFlow::Break(()); + }) + .await + .unwrap(); + System::current().stop(); + }); + }); +} + +#[test] +// Tests StateRequestHeader and StateRequestPart. +fn test_state_sync_headers_no_tracked_shards() { + heavy_test(|| { + init_test_logger(); + + run_actix(async { + let mut genesis = Genesis::test(vec!["test1".parse().unwrap()], 1); + // Increase epoch_length if the test is flaky. + let epoch_length = 50; + genesis.config.epoch_length = epoch_length; + + let port1 = tcp::ListenerAddr::reserve_for_test(); + let mut near1 = load_test_config("test1", port1, genesis.clone()); + near1.client_config.min_num_peers = 0; + near1.client_config.epoch_sync_enabled = false; + near1.client_config.tracked_shards = vec![0]; // Track all shards, it is a validator. + let dir1 = tempfile::Builder::new() + .prefix("test_state_sync_headers_no_tracked_shards_1") + .tempdir() + .unwrap(); + near1.config.store.state_snapshot_enabled = false; + near1.config.store.state_snapshot_compaction_enabled = false; + near1.config.state_sync_enabled = false; + near1.client_config.state_sync_enabled = false; + + let _node1 = start_with_config(dir1.path(), near1).expect("start_with_config"); + + let mut near2 = + load_test_config("test2", tcp::ListenerAddr::reserve_for_test(), genesis.clone()); + near2.network_config.peer_store.boot_nodes = + convert_boot_nodes(vec![("test1", *port1)]); + near2.client_config.min_num_peers = 0; + near2.client_config.epoch_sync_enabled = false; + near2.client_config.tracked_shards = vec![]; // Track no shards. + let dir2 = tempfile::Builder::new() + .prefix("test_state_sync_headers_no_tracked_shards_2") + .tempdir() + .unwrap(); + near2.config.store.state_snapshot_enabled = true; + near2.config.store.state_snapshot_compaction_enabled = false; + near2.config.state_sync_enabled = false; + near2.client_config.state_sync_enabled = false; + + let framework::NearNode { view_client: view_client2, .. } = + start_with_config(dir2.path(), near2).expect("start_with_config"); + + // First we need to find sync_hash. That is done in 3 steps: + // 1. Get the latest block + // 2. Query validators for the epoch_id of that block. + // 3. Get a block at 'epoch_start_height' that is found in the response of the validators method. + // + // Second, we request state sync header. + // Third, we request state sync part with part_id = 0. + wait_or_timeout(1000, 110000, || async { + let epoch_id = match view_client2.send(GetBlock::latest().with_span_context()).await + { + Ok(Ok(b)) => Some(b.header.epoch_id), + _ => None, + }; + // async is hard, will use this construct to reduce nestedness. + let epoch_id = match epoch_id { + Some(x) => x, + None => return ControlFlow::Continue(()), + }; + tracing::info!(?epoch_id, "got epoch_id"); + + let epoch_start_height = match view_client2 + .send( + GetValidatorInfo { + epoch_reference: EpochReference::EpochId(EpochId(epoch_id)), + } + .with_span_context(), + ) + .await + { + Ok(Ok(v)) => Some(v.epoch_start_height), + _ => None, + }; + let epoch_start_height = match epoch_start_height { + Some(x) => x, + None => return ControlFlow::Continue(()), + }; + tracing::info!(epoch_start_height, "got epoch_start_height"); + if epoch_start_height < 2 * epoch_length { + return ControlFlow::Continue(()); + } + + let sync_hash_and_num_shards = match view_client2 + .send( + GetBlock(BlockReference::BlockId(BlockId::Height(epoch_start_height))) + .with_span_context(), + ) + .await + { + Ok(Ok(b)) => Some((b.header.hash, b.chunks.len())), + _ => None, + }; + let (sync_hash, num_shards) = match sync_hash_and_num_shards { + Some(x) => x, + None => return ControlFlow::Continue(()), + }; + tracing::info!(?sync_hash, num_shards, "got sync_hash"); + + for shard_id in 0..num_shards { + // Make StateRequestHeader and expect that the response contains a header and `can_generate` is true. + let state_response_info = match view_client2 + .send( + StateRequestHeader { shard_id: shard_id as ShardId, sync_hash } + .with_span_context(), + ) + .await + { + Ok(Some(StateResponse(state_response_info))) => Some(state_response_info), + _ => None, + }; + let state_response_info = match state_response_info { + Some(x) => x, + None => return ControlFlow::Continue(()), + }; + tracing::info!(?state_response_info, "got header state response"); + let state_response = state_response_info.take_state_response(); + assert_eq!(state_response.cached_parts(), &None); + assert!(!state_response.can_generate()); + assert!(state_response.part().is_none()); + assert_eq!(state_response.take_header(), None); + + // Make StateRequestPart and expect that the response contains a part and `can_generate` is true and part_id = 0 and the node has all parts cached. + let state_response_info = match view_client2 + .send( + StateRequestPart { + shard_id: shard_id as ShardId, + sync_hash, + part_id: 0, + } + .with_span_context(), + ) + .await + { + Ok(Some(StateResponse(state_response_info))) => Some(state_response_info), + _ => None, + }; + let state_response_info = match state_response_info { + Some(x) => x, + None => return ControlFlow::Continue(()), + }; + tracing::info!(?state_response_info, "got state part response"); + let state_response = state_response_info.take_state_response(); + assert_eq!(state_response.cached_parts(), &None); + assert!(!state_response.can_generate()); + assert!(state_response.part().is_none()); + assert_eq!(state_response.take_header(), None); + } + return ControlFlow::Break(()); + }) + .await + .unwrap(); + System::current().stop(); + }); + }); +} diff --git a/integration-tests/src/tests/client/undo_block.rs b/integration-tests/src/tests/client/undo_block.rs new file mode 100644 index 000000000..2e537d61f --- /dev/null +++ b/integration-tests/src/tests/client/undo_block.rs @@ -0,0 +1,77 @@ +use unc_chain::{ChainGenesis, ChainStore, ChainStoreAccess, Provenance}; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_epoch_manager::EpochManagerAdapter; +use unc_o11y::testonly::init_test_logger; +use unc_store::test_utils::create_test_store; +use unc_store::Store; +use unc_undo_block::undo_block; +use framework::config::GenesisExt; +use framework::test_utils::TestEnvNightshadeSetupExt; +use std::sync::Arc; + +/// Setup environment with one unc client for testing. +fn setup_env(genesis: &Genesis, store: Store) -> (TestEnv, Arc) { + let chain_genesis = ChainGenesis::new(genesis); + let env = TestEnv::builder(chain_genesis) + .stores(vec![store]) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(genesis) + .build(); + let epoch_manager = env.clients[0].epoch_manager.clone(); + (env, epoch_manager) +} + +// Checks that unc client can successfully undo block on given height and then produce and process block normally after restart +fn test_undo_block(epoch_length: u64, stop_height: u64) { + init_test_logger(); + + let save_trie_changes = true; + + let mut genesis = Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.epoch_length = epoch_length; + + let store = create_test_store(); + let (mut env, epoch_manager) = setup_env(&genesis, store.clone()); + + for i in 1..=stop_height { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + env.process_block(0, block, Provenance::PRODUCED); + } + + let mut chain_store = + ChainStore::new(store.clone(), genesis.config.genesis_height, save_trie_changes); + + let current_head = chain_store.head().unwrap(); + let prev_block_hash = current_head.prev_block_hash; + + undo_block(&mut chain_store, &*epoch_manager).unwrap(); + + // after undo, the current head should be the prev_block_hash + assert_eq!(chain_store.head().unwrap().last_block_hash.as_bytes(), prev_block_hash.as_bytes()); + assert_eq!(chain_store.head().unwrap().height, stop_height - 1); + + // set up an environment again with the same store + let (mut env, _) = setup_env(&genesis, store); + // the new env should be able to produce block normally + let block = env.clients[0].produce_block(stop_height).unwrap().unwrap(); + env.process_block(0, block, Provenance::PRODUCED); + + // after processing the new block, the head should now be at stop_height + assert_eq!(chain_store.head().unwrap().height, stop_height); +} + +#[test] +fn test_undo_block_middle_of_epoch() { + test_undo_block(5, 3) +} + +#[test] +fn test_undo_block_end_of_epoch() { + test_undo_block(5, 5) +} + +#[test] +fn test_undo_block_start_of_epoch() { + test_undo_block(5, 6) +} diff --git a/integration-tests/src/tests/mod.rs b/integration-tests/src/tests/mod.rs new file mode 100644 index 000000000..47ca63425 --- /dev/null +++ b/integration-tests/src/tests/mod.rs @@ -0,0 +1,10 @@ +mod client; +mod framework; +mod network; +mod runtime; +mod standard_cases; +mod test_catchup; +mod test_errors; +mod test_overflows; +mod test_simple; +mod test_tps_regression; diff --git a/integration-tests/src/tests/nearcore/mod.rs b/integration-tests/src/tests/nearcore/mod.rs new file mode 100644 index 000000000..f0195acdc --- /dev/null +++ b/integration-tests/src/tests/nearcore/mod.rs @@ -0,0 +1,7 @@ +mod node_cluster; +mod rpc_error_structs; +mod rpc_nodes; +mod run_nodes; +mod stake_nodes; +mod sync_nodes; +mod track_shards; diff --git a/integration-tests/src/tests/nearcore/node_cluster.rs b/integration-tests/src/tests/nearcore/node_cluster.rs new file mode 100644 index 000000000..8787778ee --- /dev/null +++ b/integration-tests/src/tests/nearcore/node_cluster.rs @@ -0,0 +1,153 @@ +use crate::test_helpers::heavy_test; +use actix::Addr; +use actix_rt::ArbiterHandle; +use futures::future; +use unc_actix_test_utils::{run_actix, spawn_interruptible}; +use unc_chain_configs::Genesis; +use unc_client::{ClientActor, ViewClientActor}; +use unc_network::tcp; +use unc_network::test_utils::convert_boot_nodes; +use unc_o11y::testonly::init_integration_logger; +use unc_primitives::types::{BlockHeight, BlockHeightDelta, NumSeats, NumShards}; +use framework::{config::GenesisExt, load_test_config, start_with_config}; + +fn start_nodes( + temp_dir: &std::path::Path, + num_shards: NumShards, + num_nodes: NumSeats, + num_validator_seats: NumSeats, + num_lightclient: NumSeats, + epoch_length: BlockHeightDelta, + genesis_height: BlockHeight, +) -> (Genesis, Vec, Vec<(Addr, Addr, Vec)>) { + init_integration_logger(); + + let num_tracking_nodes = num_nodes - num_lightclient; + let seeds = (0..num_nodes).map(|i| format!("near.{}", i)).collect::>(); + let mut genesis = Genesis::test_sharded_new_version( + seeds.iter().map(|s| s.parse().unwrap()).collect(), + num_validator_seats, + (0..num_shards).map(|_| num_validator_seats).collect(), + ); + genesis.config.epoch_length = epoch_length; + genesis.config.genesis_height = genesis_height; + + let validators = (0..num_validator_seats).map(|i| format!("near.{}", i)).collect::>(); + let mut unc_configs = vec![]; + let first_node = tcp::ListenerAddr::reserve_for_test(); + let mut rpc_addrs = vec![]; + for i in 0..num_nodes { + let mut unc_config = load_test_config( + validators.get(i as usize).map(String::as_str).unwrap_or(""), + if i == 0 { first_node } else { tcp::ListenerAddr::reserve_for_test() }, + genesis.clone(), + ); + rpc_addrs.push(unc_config.rpc_addr().unwrap().to_owned()); + unc_config.client_config.min_num_peers = (num_nodes as usize) - 1; + if i > 0 { + unc_config.network_config.peer_store.boot_nodes = + convert_boot_nodes(vec![("near.0", *first_node)]); + } + // if non validator, track all shards + if i >= num_validator_seats && i < num_tracking_nodes { + unc_config.client_config.tracked_shards = vec![0]; + } + unc_config.client_config.epoch_sync_enabled = false; + unc_configs.push(unc_config); + } + + let mut res = vec![]; + for (i, unc_config) in unc_configs.into_iter().enumerate() { + let dir = temp_dir.join(format!("node{i}")); + std::fs::create_dir(&dir).unwrap(); + let framework::NearNode { client, view_client, arbiters, .. } = + start_with_config(&dir, unc_config).expect("start_with_config"); + res.push((client, view_client, arbiters)) + } + (genesis, rpc_addrs, res) +} + +#[derive(Debug, Default)] +pub struct NodeCluster { + num_shards: Option, + num_nodes: Option, + num_validator_seats: Option, + num_lightclient: Option, + epoch_length: Option, + genesis_height: Option, +} + +impl NodeCluster { + pub fn set_num_shards(mut self, n: NumShards) -> Self { + self.num_shards = Some(n); + self + } + + pub fn set_num_nodes(mut self, n: NumSeats) -> Self { + self.num_nodes = Some(n); + self + } + + pub fn set_num_validator_seats(mut self, n: NumSeats) -> Self { + self.num_validator_seats = Some(n); + self + } + + pub fn set_num_lightclients(mut self, n: NumSeats) -> Self { + self.num_lightclient = Some(n); + self + } + + pub fn set_epoch_length(mut self, l: BlockHeightDelta) -> Self { + self.epoch_length = Some(l); + self + } + + pub fn set_genesis_height(mut self, h: BlockHeight) -> Self { + self.genesis_height = Some(h); + self + } + + pub fn exec_until_stop(self, f: F) + where + R: future::Future + 'static, + F: FnOnce( + unc_chain_configs::Genesis, + Vec, + Vec<( + actix::Addr, + actix::Addr, + Vec, + )>, + ) -> R, + { + let (num_shards, num_validator_seats, num_lightclient, epoch_length, genesis_height) = ( + self.num_shards.expect("cluster config: [num_shards] undefined"), + self.num_validator_seats.expect("cluster config: [num_validator_seats] undefined"), + self.num_lightclient.expect("cluster config: [num_lightclient] undefined"), + self.epoch_length.expect("cluster config: [epoch_length] undefined"), + self.genesis_height.expect("cluster config: [genesis_height] undefined"), + ); + let min_num_nodes = num_validator_seats + num_lightclient; + let num_nodes = self.num_nodes.unwrap_or(min_num_nodes); + assert!( + min_num_nodes <= num_nodes, + "cluster config: [num_nodes] must be at least {min_num_nodes} but got {num_nodes}" + ); + heavy_test(|| { + let temp_dir = tempfile::tempdir().unwrap(); + run_actix(async { + let (genesis, rpc_addrs, clients) = start_nodes( + temp_dir.path(), + num_shards, + num_nodes, + num_validator_seats, + num_lightclient, + epoch_length, + genesis_height, + ); + spawn_interruptible(f(genesis, rpc_addrs, clients)); + }); + }); + } +} diff --git a/integration-tests/src/tests/nearcore/rpc_error_structs.rs b/integration-tests/src/tests/nearcore/rpc_error_structs.rs new file mode 100644 index 000000000..6e387a735 --- /dev/null +++ b/integration-tests/src/tests/nearcore/rpc_error_structs.rs @@ -0,0 +1,458 @@ +use std::str::FromStr; + +use actix::{Actor, System}; + +use futures::{future, FutureExt, TryFutureExt}; + +use crate::genesis_helpers::genesis_block; +use crate::tests::framework::node_cluster::NodeCluster; +use unc_actix_test_utils::spawn_interruptible; +use unc_client::GetBlock; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_jsonrpc::client::new_client; +use unc_network::test_utils::WaitOrTimeoutActor; +use unc_o11y::testonly::init_integration_logger; +use unc_o11y::WithSpanContextExt; +use unc_primitives::hash::CryptoHash; +use unc_primitives::serialize::to_base64; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::BlockId; + +// Queries json-rpc block that doesn't exists +// Checks if the struct is expected and contains the proper data +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_block_unknown_block_error() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(4) + .set_num_validator_seats(2) + .set_num_lightclients(2) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|_genesis, rpc_addrs, clients| async move { + let view_client = clients[0].1.clone(); + + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let rpc_addrs_copy = rpc_addrs.clone(); + + // We are sending this tx unstop, just to get over the warm up period. + // Probably make sense to stop after 1 time though. + let actor = view_client.send(GetBlock::latest().with_span_context()); + let actor = actor.then(move |res| { + if let Ok(Ok(block)) = res { + if block.header.height > 1 { + let client = new_client(&format!("http://{}", rpc_addrs_copy[2])); + spawn_interruptible( + client + .block_by_id(BlockId::Height(block.header.height + 100)) + .map_err(|err| { + let error_json = serde_json::to_value(err).unwrap(); + assert_eq!( + error_json["name"], + serde_json::json!("HANDLER_ERROR") + ); + assert_eq!( + error_json["cause"]["name"], + serde_json::json!("UNKNOWN_BLOCK") + ); + System::current().stop(); + }) + .map_ok(|_| panic!("The block mustn't be found")) + .map(drop), + ); + } + } + future::ready(()) + }); + spawn_interruptible(actor); + }), + 100, + 40000, + ) + .start(); + }); +} + +// Queries json-rpc chunk that doesn't exists +// (randomish chunk hash, we hope it won't happen in test case) +// Checks if the struct is expected and contains the proper data +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_chunk_unknown_chunk_error() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(4) + .set_num_validator_seats(2) + .set_num_lightclients(2) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|_genesis, rpc_addrs, clients| async move { + let view_client = clients[0].1.clone(); + + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let rpc_addrs_copy = rpc_addrs.clone(); + + // We are sending this tx unstop, just to get over the warm up period. + // Probably make sense to stop after 1 time though. + let actor = view_client.send(GetBlock::latest().with_span_context()); + let actor = actor.then(move |res| { + if let Ok(Ok(block)) = res { + if block.header.height > 1 { + let client = new_client(&format!("http://{}", rpc_addrs_copy[2])); + spawn_interruptible( + client + .chunk(unc_jsonrpc::client::ChunkId::Hash( + CryptoHash::from_str( + "3tMcx4KU2KvkwJPMWPXqK2MUU1FDVbigPFNiAeuVa7Tu", + ) + .unwrap(), + )) + .map_err(|err| { + let error_json = serde_json::to_value(err).unwrap(); + assert_eq!( + error_json["name"], + serde_json::json!("HANDLER_ERROR") + ); + assert_eq!( + error_json["cause"]["name"], + serde_json::json!("UNKNOWN_CHUNK") + ); + assert_eq!( + error_json["cause"]["info"]["chunk_hash"], + serde_json::json!( + "3tMcx4KU2KvkwJPMWPXqK2MUU1FDVbigPFNiAeuVa7Tu" + ) + ); + System::current().stop(); + }) + .map_ok(|_| panic!("The chunk mustn't be found")) + .map(drop), + ); + } + } + future::ready(()) + }); + spawn_interruptible(actor); + }), + 100, + 40000, + ) + .start(); + }); +} + +// Queries json-rpc EXPERIMENTAL_protocol_config that doesn't exists +// Checks if the struct is expected and contains the proper data +#[test] +fn test_protocol_config_unknown_block_error() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(4) + .set_num_validator_seats(2) + .set_num_lightclients(2) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|_genesis, rpc_addrs, clients| async move { + let view_client = clients[0].1.clone(); + + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let rpc_addrs_copy = rpc_addrs.clone(); + + // We are sending this tx unstop, just to get over the warm up period. + // Probably make sense to stop after 1 time though. + let actor = view_client.send(GetBlock::latest().with_span_context()); + let actor = actor.then(move |res| { + if let Ok(Ok(block)) = res { + if block.header.height > 1 { + let client = new_client(&format!("http://{}", rpc_addrs_copy[2])); + spawn_interruptible( + client + .EXPERIMENTAL_protocol_config( + unc_jsonrpc_primitives::types::config::RpcProtocolConfigRequest { + block_reference: unc_primitives::types::BlockReference::BlockId(BlockId::Height(block.header.height + 100)) + } + ) + .map_err(|err| { + let error_json = serde_json::to_value(err).unwrap(); + + assert_eq!( + error_json["name"], + serde_json::json!("HANDLER_ERROR") + ); + assert_eq!( + error_json["cause"]["name"], + serde_json::json!("UNKNOWN_BLOCK") + ); + System::current().stop(); + }) + .map_ok(|_| panic!("The block mustn't be found")) + .map(drop), + ); + } + } + future::ready(()) + }); + spawn_interruptible(actor); + }), + 100, + 40000, + ) + .start(); + }); +} + +// Queries json-rpc gas_price that doesn't exists +// Checks if the struct is expected and contains the proper data +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_gas_price_unknown_block_error() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(4) + .set_num_validator_seats(2) + .set_num_lightclients(2) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|_genesis, rpc_addrs, clients| async move { + let view_client = clients[0].1.clone(); + + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let rpc_addrs_copy = rpc_addrs.clone(); + + // We are sending this tx unstop, just to get over the warm up period. + // Probably make sense to stop after 1 time though. + let actor = view_client.send(GetBlock::latest().with_span_context()); + let actor = actor.then(move |res| { + if let Ok(Ok(block)) = res { + if block.header.height > 1 { + let client = new_client(&format!("http://{}", rpc_addrs_copy[2])); + spawn_interruptible( + client + .gas_price(Some(BlockId::Height(block.header.height + 100))) + .map_err(|err| { + let error_json = serde_json::to_value(err).unwrap(); + + assert_eq!( + error_json["name"], + serde_json::json!("HANDLER_ERROR") + ); + assert_eq!( + error_json["cause"]["name"], + serde_json::json!("UNKNOWN_BLOCK") + ); + System::current().stop(); + }) + .map_ok(|_| panic!("The block mustn't be found")) + .map(drop), + ); + } + } + future::ready(()) + }); + spawn_interruptible(actor); + }), + 100, + 40000, + ) + .start(); + }); +} + +// Queries json-rpc EXPERIMENTAL_receipt that doesn't exists +// Checks if the struct is expected and contains the proper data +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_receipt_id_unknown_receipt_error() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(4) + .set_num_validator_seats(2) + .set_num_lightclients(2) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|_genesis, rpc_addrs, clients| async move { + let view_client = clients[0].1.clone(); + + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let rpc_addrs_copy = rpc_addrs.clone(); + + // We are sending this tx unstop, just to get over the warm up period. + // Probably make sense to stop after 1 time though. + let actor = view_client.send(GetBlock::latest().with_span_context()); + let actor = actor.then(move |res| { + if let Ok(Ok(block)) = res { + if block.header.height > 1 { + let client = new_client(&format!("http://{}", rpc_addrs_copy[2])); + spawn_interruptible( + client + .EXPERIMENTAL_receipt( + unc_jsonrpc_primitives::types::receipts::RpcReceiptRequest { + receipt_reference: unc_jsonrpc_primitives::types::receipts::ReceiptReference { + receipt_id: CryptoHash::from_str("3tMcx4KU2KvkwJPMWPXqK2MUU1FDVbigPFNiAeuVa7Tu").unwrap() + } + } + ) + .map_err(|err| { + let error_json = serde_json::to_value(err).unwrap(); + + assert_eq!( + error_json["name"], + serde_json::json!("HANDLER_ERROR") + ); + assert_eq!( + error_json["cause"]["name"], + serde_json::json!("UNKNOWN_RECEIPT") + ); + assert_eq!( + error_json["cause"]["info"]["receipt_id"], + serde_json::json!( + "3tMcx4KU2KvkwJPMWPXqK2MUU1FDVbigPFNiAeuVa7Tu" + ) + ); + System::current().stop(); + }) + .map_ok(|_| panic!("The block mustn't be found")) + .map(drop), + ); + } + } + future::ready(()) + }); + spawn_interruptible(actor); + }), + 100, + 40000, + ) + .start(); + }); +} + +/// Starts 2 validators and 2 light clients (not tracking anything). +/// Sends tx to first light client through `broadcast_tx_commit` and checks that the transaction has failed. +/// Checks if the struct is expected and contains the proper data +#[test] +#[ignore = "Invalid test setup. broadcast_tx_commit times out because we haven't implemented forwarding logic. Fix and reenable."] +// #[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_tx_invalid_tx_error() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(4) + .set_num_validator_seats(2) + .set_num_lightclients(2) + .set_epoch_length(1000) + .set_genesis_height(0); + + cluster.exec_until_stop(|genesis, rpc_addrs, clients| async move { + let view_client = clients[0].1.clone(); + + let genesis_hash = *genesis_block(&genesis).hash(); + let signer = + InMemorySigner::from_seed("near.5".parse().unwrap(), KeyType::ED25519, "near.5"); + let transaction = SignedTransaction::send_money( + 1, + "near.5".parse().unwrap(), + "near.2".parse().unwrap(), + &signer, + 10000, + genesis_hash, + ); + + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let rpc_addrs_copy = rpc_addrs.clone(); + let transaction_copy = transaction.clone(); + let tx_hash = transaction_copy.get_hash(); + + let actor = view_client.send(GetBlock::latest().with_span_context()); + let actor = actor.then(move |res| { + if let Ok(Ok(block)) = res { + if block.header.height > 10 { + let client = new_client(&format!("http://{}", rpc_addrs_copy[2])); + let bytes = borsh::to_vec(&transaction_copy).unwrap(); + spawn_interruptible( + client + .broadcast_tx_commit(to_base64(&bytes)) + .map_err(move |err| { + let error_json = serde_json::to_value(err).unwrap(); + + assert_eq!( + error_json["name"], + serde_json::json!("HANDLER_ERROR") + ); + assert_eq!( + error_json["cause"]["name"], + serde_json::json!("REQUEST_ROUTED") + ); + assert_eq!( + error_json["cause"]["info"]["transaction_hash"], + serde_json::json!(tx_hash) + ); + System::current().stop(); + }) + .map_ok(|_| panic!("The transaction mustn't succeed")) + .map(drop), + ); + } + } + future::ready(()) + }); + spawn_interruptible(actor); + }), + 100, + 40000, + ) + .start(); + }); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_query_rpc_account_view_unknown_block_must_return_error() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(1) + .set_num_validator_seats(1) + .set_num_lightclients(0) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|_, rpc_addrs, _| async move { + let client = new_client(&format!("http://{}", rpc_addrs[0])); + let query_response = client + .query(unc_jsonrpc_primitives::types::query::RpcQueryRequest { + block_reference: unc_primitives::types::BlockReference::BlockId(BlockId::Height( + 1, + )), + request: unc_primitives::views::QueryRequest::ViewAccount { + account_id: "near.0".parse().unwrap(), + }, + }) + .await; + + let error = match query_response { + Ok(result) => panic!("expected error but received Ok: {:?}", result.kind), + Err(err) => serde_json::to_value(err).unwrap(), + }; + + assert_eq!(error["cause"]["name"], serde_json::json!("UNKNOWN_BLOCK"),); + System::current().stop(); + }); +} diff --git a/integration-tests/src/tests/nearcore/rpc_nodes.rs b/integration-tests/src/tests/nearcore/rpc_nodes.rs new file mode 100644 index 000000000..63c4a8c1e --- /dev/null +++ b/integration-tests/src/tests/nearcore/rpc_nodes.rs @@ -0,0 +1,579 @@ +use crate::genesis_helpers::genesis_block; +use crate::tests::framework::node_cluster::NodeCluster; +use actix::clock::sleep; +use actix::{Actor, System}; +use assert_matches::assert_matches; + +use futures::future::join_all; +use futures::{future, FutureExt, TryFutureExt}; +use unc_actix_test_utils::spawn_interruptible; +use unc_client::{GetBlock, GetExecutionOutcome, GetValidatorInfo}; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_jsonrpc::client::new_client; +use unc_jsonrpc_primitives::types::transactions::{RpcTransactionStatusRequest, TransactionInfo}; +use unc_network::test_utils::WaitOrTimeoutActor; +use unc_o11y::testonly::init_integration_logger; +use unc_o11y::WithSpanContextExt; +use unc_parameters::{RuntimeConfigStore, RuntimeConfigView}; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::merkle::{compute_root_from_path_and_item, verify_path}; +use unc_primitives::serialize::to_base64; +use unc_primitives::transaction::{PartialExecutionStatus, SignedTransaction}; +use unc_primitives::types::{ + BlockId, BlockReference, EpochId, EpochReference, Finality, TransactionOrReceiptId, +}; +use unc_primitives::version::ProtocolVersion; +use unc_primitives::views::{ExecutionOutcomeView, ExecutionStatusView, TxExecutionStatus}; +use std::time::Duration; + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_get_validator_info_rpc() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(1) + .set_num_validator_seats(1) + .set_num_lightclients(0) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|_, rpc_addrs, clients| async move { + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let rpc_addrs_copy = rpc_addrs.clone(); + let view_client = clients[0].1.clone(); + spawn_interruptible(async move { + let block_view = + view_client.send(GetBlock::latest().with_span_context()).await.unwrap(); + if let Err(err) = block_view { + println!("Failed to get the latest block: {:?}", err); + return; + } + let block_view = block_view.unwrap(); + if block_view.header.height > 1 { + let client = new_client(&format!("http://{}", rpc_addrs_copy[0])); + let block_hash = block_view.header.hash; + let invalid_res = client + .validators(Some(EpochReference::BlockId(BlockId::Hash(block_hash)))) + .await; + assert!(invalid_res.is_err()); + let res = client.validators(None).await.unwrap(); + assert_eq!(res.current_validators.len(), 1); + assert!(res.current_validators.iter().any(|r| r.account_id == "near.0")); + System::current().stop(); + } + }); + }), + 100, + 40000, + ) + .start(); + }); +} + +fn outcome_view_to_hashes(outcome: &ExecutionOutcomeView) -> Vec { + let status = match &outcome.status { + ExecutionStatusView::Unknown => PartialExecutionStatus::Unknown, + ExecutionStatusView::SuccessValue(s) => PartialExecutionStatus::SuccessValue(s.clone()), + ExecutionStatusView::Failure(_) => PartialExecutionStatus::Failure, + ExecutionStatusView::SuccessReceiptId(id) => PartialExecutionStatus::SuccessReceiptId(*id), + }; + let mut result = vec![CryptoHash::hash_borsh(( + outcome.receipt_ids.clone(), + outcome.gas_burnt, + outcome.tokens_burnt, + outcome.executor_id.clone(), + status, + ))]; + for log in outcome.logs.iter() { + result.push(hash(log.as_bytes())); + } + result +} + +fn test_get_execution_outcome(is_tx_successful: bool) { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(1) + .set_num_validator_seats(1) + .set_num_lightclients(1) + .set_epoch_length(1000) + .set_genesis_height(0); + + cluster.exec_until_stop(|genesis, rpc_addrs, clients| async move { + let view_client = clients[0].1.clone(); + + let genesis_hash = *genesis_block(&genesis).hash(); + let signer = + InMemorySigner::from_seed("near.0".parse().unwrap(), KeyType::ED25519, "near.0"); + let transaction = if is_tx_successful { + SignedTransaction::send_money( + 1, + "near.0".parse().unwrap(), + "near.1".parse().unwrap(), + &signer, + 10000, + genesis_hash, + ) + } else { + SignedTransaction::create_account( + 1, + "near.0".parse().unwrap(), + "near.1".parse().unwrap(), + 10, + signer.public_key.clone(), + &signer, + genesis_hash, + ) + }; + + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let client = new_client(&format!("http://{}", rpc_addrs[0])); + let bytes = borsh::to_vec(&transaction).unwrap(); + let view_client1 = view_client.clone(); + spawn_interruptible(client.broadcast_tx_commit(to_base64(&bytes)).then( + move |res| { + let final_transaction_outcome = match res { + Ok(outcome) => outcome.final_execution_outcome.unwrap().into_outcome(), + Err(_) => return future::ready(()), + }; + spawn_interruptible(sleep(Duration::from_secs(1)).then(move |_| { + let mut futures = vec![]; + for id in vec![TransactionOrReceiptId::Transaction { + transaction_hash: final_transaction_outcome.transaction_outcome.id, + sender_id: "near.0".parse().unwrap(), + }] + .into_iter() + .chain( + final_transaction_outcome.receipts_outcome.into_iter().map(|r| { + TransactionOrReceiptId::Receipt { + receipt_id: r.id, + receiver_id: "near.1".parse().unwrap(), + } + }), + ) { + let view_client2 = view_client1.clone(); + let fut = view_client1 + .send(GetExecutionOutcome { id }.with_span_context()); + let fut = fut.then(move |res| { + let execution_outcome_response = res.unwrap().unwrap(); + view_client2 + .send( + GetBlock(BlockReference::BlockId(BlockId::Hash( + execution_outcome_response.outcome_proof.block_hash, + ))) + .with_span_context(), + ) + .then(move |res| { + let res = res.unwrap().unwrap(); + let mut outcome_with_id_to_hash = + vec![execution_outcome_response.outcome_proof.id]; + outcome_with_id_to_hash.extend(outcome_view_to_hashes( + &execution_outcome_response.outcome_proof.outcome, + )); + let chunk_outcome_root = + compute_root_from_path_and_item( + &execution_outcome_response.outcome_proof.proof, + &outcome_with_id_to_hash, + ); + assert!(verify_path( + res.header.outcome_root, + &execution_outcome_response.outcome_root_proof, + &chunk_outcome_root + )); + future::ready(()) + }) + }); + futures.push(fut); + } + spawn_interruptible(join_all(futures).then(|_| { + System::current().stop(); + future::ready(()) + })); + future::ready(()) + })); + + future::ready(()) + }, + )); + }), + 100, + 40000, + ) + .start(); + }); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_get_execution_outcome_tx_success() { + test_get_execution_outcome(true); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_get_execution_outcome_tx_failure() { + test_get_execution_outcome(false); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_protocol_config_rpc() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(1) + .set_num_validator_seats(1) + .set_num_lightclients(0) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|_, rpc_addrs, _| async move { + let client = new_client(&format!("http://{}", rpc_addrs[0])); + let config_response = client + .EXPERIMENTAL_protocol_config( + unc_jsonrpc_primitives::types::config::RpcProtocolConfigRequest { + block_reference: unc_primitives::types::BlockReference::Finality( + Finality::None, + ), + }, + ) + .await + .unwrap(); + + let runtime_config_store = RuntimeConfigStore::new(None); + let intial_runtime_config = runtime_config_store.get_config(ProtocolVersion::MIN); + let latest_runtime_config = + runtime_config_store.get_config(unc_primitives::version::PROTOCOL_VERSION); + assert_ne!( + config_response.config_view.runtime_config.storage_amount_per_byte, + intial_runtime_config.storage_amount_per_byte() + ); + // compare JSON view + assert_eq!( + serde_json::json!(config_response.config_view.runtime_config), + serde_json::json!(RuntimeConfigView::from(latest_runtime_config.as_ref().clone())) + ); + System::current().stop(); + }); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_query_rpc_account_view_must_succeed() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(1) + .set_num_validator_seats(1) + .set_num_lightclients(0) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|_, rpc_addrs, _| async move { + let client = new_client(&format!("http://{}", rpc_addrs[0])); + let query_response = client + .query(unc_jsonrpc_primitives::types::query::RpcQueryRequest { + block_reference: unc_primitives::types::BlockReference::Finality(Finality::Final), + request: unc_primitives::views::QueryRequest::ViewAccount { + account_id: "near.0".parse().unwrap(), + }, + }) + .await + .unwrap(); + let account = + if let unc_jsonrpc_primitives::types::query::QueryResponseKind::ViewAccount(account) = + query_response.kind + { + account + } else { + panic!( + "expected a account view result, but received something else: {:?}", + query_response.kind + ); + }; + assert_matches!(account, unc_primitives::views::AccountView { .. }); + System::current().stop(); + }); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_query_rpc_account_view_account_doesnt_exist_must_return_error() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(1) + .set_num_validator_seats(1) + .set_num_lightclients(0) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|_, rpc_addrs, _| async move { + let client = new_client(&format!("http://{}", rpc_addrs[0])); + let error_message = loop { + let query_response = client + .query(unc_jsonrpc_primitives::types::query::RpcQueryRequest { + block_reference: unc_primitives::types::BlockReference::Finality(Finality::Final), + request: unc_primitives::views::QueryRequest::ViewAccount { + account_id: "accountdoesntexist.0".parse().unwrap(), + }, + }) + .await; + + break match query_response { + Ok(result) => panic!("expected error but received Ok: {:?}", result.kind), + Err(err) => { + let value = err.data.unwrap(); + if value == serde_json::to_value("Block either has never been observed on the node or has been garbage collected: Finality(Final)").unwrap() { + println!("No blocks are produced yet, retry."); + sleep(std::time::Duration::from_millis(100)).await; + continue; + } + value + } + }; + }; + + assert!( + error_message + .to_string() + .contains("account accountdoesntexist.0 does not exist while viewing"), + "{}", + error_message + ); + + System::current().stop(); + }); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_tx_not_enough_balance_must_return_error() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(1) + .set_num_validator_seats(2) + .set_num_lightclients(0) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|genesis, rpc_addrs, clients| async move { + let view_client = clients[0].1.clone(); + + let genesis_hash = *genesis_block(&genesis).hash(); + let signer = + InMemorySigner::from_seed("near.0".parse().unwrap(), KeyType::ED25519, "near.0"); + let transaction = SignedTransaction::send_money( + 1, + "near.0".parse().unwrap(), + "near.1".parse().unwrap(), + &signer, + 1100000000000000000000000000000000, + genesis_hash, + ); + + let client = new_client(&format!("http://{}", rpc_addrs[0])); + let bytes = borsh::to_vec(&transaction).unwrap(); + + spawn_interruptible(async move { + loop { + let res = view_client.send(GetBlock::latest().with_span_context()).await; + if let Ok(Ok(block)) = res { + if block.header.height > 10 { + break; + } + } + sleep(std::time::Duration::from_millis(500)).await; + } + let _ = client + .broadcast_tx_commit(to_base64(&bytes)) + .map_err(|err| { + assert_eq!( + err.data.unwrap(), + serde_json::json!({"TxExecutionError": { + "InvalidTxError": { + "NotEnoughBalance": { + "signer_id": "near.0", + "balance": "950000000000000000000000000000000", // If something changes in setup just update this value + "cost": "1100000000000045306060187500000000", + } + } + }}) + ); + System::current().stop(); + }) + .map_ok(|_| panic!("Transaction must not succeed")) + .await; + }); + }); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_check_unknown_tx_must_return_error() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(1) + .set_num_nodes(1) + .set_num_validator_seats(1) + .set_num_lightclients(0) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|genesis, rpc_addrs, clients| async move { + let view_client = clients[0].1.clone(); + + let genesis_hash = *genesis_block(&genesis).hash(); + let signer = + InMemorySigner::from_seed("near.0".parse().unwrap(), KeyType::ED25519, "near.0"); + let transaction = SignedTransaction::send_money( + 1, + "near.0".parse().unwrap(), + "near.0".parse().unwrap(), + &signer, + 10000, + genesis_hash, + ); + + let client = new_client(&format!("http://{}", rpc_addrs[0])); + let tx_hash = transaction.get_hash(); + let bytes = borsh::to_vec(&transaction).unwrap(); + + spawn_interruptible(async move { + loop { + let res = view_client.send(GetBlock::latest().with_span_context()).await; + if let Ok(Ok(block)) = res { + if block.header.height > 10 { + let _ = client + .EXPERIMENTAL_tx_status(to_base64(&bytes)) + .map_err(|err| { + assert_eq!( + err.data.unwrap(), + serde_json::json!(format!( + "Transaction {} doesn't exist", + tx_hash + )) + ); + System::current().stop(); + }) + .map_ok(|_| panic!("Transaction must be unknown")) + .await; + break; + } + } + sleep(std::time::Duration::from_millis(500)).await; + } + }); + }); +} + +#[test] +#[ignore = "Need to implement forwarding and fix the test"] +// #[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_tx_status_on_lightclient_must_return_does_not_track_shard() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(1) + .set_num_validator_seats(1) + .set_num_lightclients(1) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|genesis, rpc_addrs, clients| async move { + let view_client = clients[0].1.clone(); + + let genesis_hash = *genesis_block(&genesis).hash(); + let signer = InMemorySigner::from_seed("near.1".parse().unwrap(), KeyType::ED25519, "near.1"); + let transaction = SignedTransaction::send_money( + 1, + "near.1".parse().unwrap(), + "near.1".parse().unwrap(), + &signer, + 10000, + genesis_hash, + ); + + let client = new_client(&format!("http://{}", rpc_addrs[1])); + + spawn_interruptible(async move { + loop { + let res = view_client.send(GetBlock::latest().with_span_context()).await; + if let Ok(Ok(block)) = res { + if block.header.height > 10 { + let request = RpcTransactionStatusRequest { + transaction_info: TransactionInfo::from_signed_tx(transaction), + wait_until: TxExecutionStatus::None, + }; + let _ = client + .tx(request) + .map_err(|err| { + assert_eq!( + err.data.unwrap(), + serde_json::json!("Node doesn't track this shard. Cannot determine whether the transaction is valid") + ); + System::current().stop(); + }) + .map_ok(|_| panic!("Must not track shard")) + .await; + break; + } + } + sleep(std::time::Duration::from_millis(500)).await; + } + }); + }); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_validators_by_epoch_id_current_epoch_not_fails() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(1) + .set_num_validator_seats(1) + .set_num_lightclients(0) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|_genesis, _rpc_addrs, clients| async move { + let view_client = clients[0].1.clone(); + + spawn_interruptible(async move { + let final_block = loop { + let res = view_client.send(GetBlock::latest().with_span_context()).await; + if let Ok(Ok(block)) = res { + if block.header.height > 1 { + break block; + } + } + }; + + let res = view_client + .send( + GetValidatorInfo { + epoch_reference: EpochReference::EpochId(EpochId( + final_block.header.epoch_id, + )), + } + .with_span_context(), + ) + .await; + + match res { + Ok(Ok(validators)) => { + assert_eq!(validators.current_validators.len(), 1); + System::current().stop(); + } + err => panic!("Validators list by EpochId must succeed: {:?}", err), + } + }); + }); +} diff --git a/integration-tests/src/tests/nearcore/run_nodes.rs b/integration-tests/src/tests/nearcore/run_nodes.rs new file mode 100644 index 000000000..b03eb3092 --- /dev/null +++ b/integration-tests/src/tests/nearcore/run_nodes.rs @@ -0,0 +1,82 @@ +use crate::tests::framework::node_cluster::NodeCluster; +use actix::System; +use unc_client::GetBlock; +use unc_network::test_utils::wait_or_timeout; +use unc_o11y::WithSpanContextExt; +use unc_primitives::types::{BlockHeightDelta, NumSeats, NumShards}; +use rand::{thread_rng, Rng}; +use std::ops::ControlFlow; + +fn run_heavy_nodes( + num_shards: NumShards, + num_nodes: NumSeats, + num_validators: NumSeats, + epoch_length: BlockHeightDelta, + num_blocks: BlockHeightDelta, +) { + let mut rng = thread_rng(); + let genesis_height = rng.gen_range(0..10000); + + let cluster = NodeCluster::default() + .set_num_shards(num_shards) + .set_num_nodes(num_nodes) + .set_num_validator_seats(num_validators) + .set_num_lightclients(0) + .set_epoch_length(epoch_length) + .set_genesis_height(genesis_height); + + cluster.exec_until_stop(|_, _, clients| async move { + let view_client = clients.last().unwrap().1.clone(); + + wait_or_timeout(100, 40000, || async { + let res = view_client.send(GetBlock::latest().with_span_context()).await; + match &res { + Ok(Ok(b)) if b.header.height > num_blocks => return ControlFlow::Break(()), + Err(_) => return ControlFlow::Continue(()), + _ => {} + }; + ControlFlow::Continue(()) + }) + .await + .unwrap(); + System::current().stop() + }); + + // See https://github.com/utnet-org/utility/issues/3925 for why it is here. + // + // The TL;DR is that actix doesn't allow to cleanly shut down multi-arbiter + // actor systems, and that might cause RocksDB destructors to run when the + // test binary exits, breaking stuff. This sleep here is a best-effort to + // let those destructors finish. This should make the tests less flaky. + // Hopefully, we'll be able to fix this properly by replacing actix actor + // framework with something that handles cancellation gracefully. + std::thread::sleep(std::time::Duration::from_millis(250)); +} + +/// Runs two nodes that should produce blocks one after another. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn run_nodes_1_2_2() { + run_heavy_nodes(1, 2, 2, 10, 30); +} + +/// Runs two nodes, where only one is a validator. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn run_nodes_1_2_1() { + run_heavy_nodes(1, 2, 1, 10, 30); +} + +/// Runs 4 nodes that should produce blocks one after another. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn run_nodes_1_4_4() { + run_heavy_nodes(1, 4, 4, 8, 32); +} + +/// Run 4 nodes, 4 shards, 2 validators, other two track 2 shards. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn run_nodes_4_4_2() { + run_heavy_nodes(4, 4, 2, 8, 32); +} diff --git a/integration-tests/src/tests/nearcore/stake_nodes.rs b/integration-tests/src/tests/nearcore/stake_nodes.rs new file mode 100644 index 000000000..1261357ae --- /dev/null +++ b/integration-tests/src/tests/nearcore/stake_nodes.rs @@ -0,0 +1,617 @@ +use std::path::Path; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; + +use actix::{Actor, Addr, System}; +use futures::{future, FutureExt}; +use unc_primitives::num_rational::Ratio; +use rand::Rng; + +use crate::genesis_helpers::genesis_hash; +use crate::test_helpers::heavy_test; +use unc_actix_test_utils::run_actix; +use unc_chain_configs::Genesis; +use unc_client::{ClientActor, GetBlock, ProcessTxRequest, Query, Status, ViewClientActor}; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_network::tcp; +use unc_network::test_utils::{convert_boot_nodes, WaitOrTimeoutActor}; +use unc_o11y::testonly::init_integration_logger; +use unc_primitives::hash::CryptoHash; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{AccountId, BlockHeightDelta, BlockReference, NumSeats}; +use unc_primitives::views::{QueryRequest, QueryResponseKind, ValidatorInfo}; +use framework::config::{GenesisExt, TESTING_INIT_BALANCE, TESTING_INIT_STAKE}; +use framework::{load_test_config, start_with_config, UncConfig, UNC_BASE}; + +use unc_o11y::WithSpanContextExt; +use {unc_primitives::types::BlockId, primitive_types::U256}; + +#[derive(Clone)] +struct TestNode { + account_id: AccountId, + signer: Arc, + config: UncConfig, + client: Addr, + view_client: Addr, + genesis_hash: CryptoHash, +} + +fn init_test_staking( + paths: Vec<&Path>, + num_node_seats: NumSeats, + num_validator_seats: NumSeats, + epoch_length: BlockHeightDelta, + enable_rewards: bool, + minimum_stake_divisor: u64, + state_snapshot_enabled: bool, + track_all_shards: bool, +) -> Vec { + init_integration_logger(); + + let seeds = (0..num_node_seats).map(|i| format!("near.{}", i)).collect::>(); + let mut genesis = + Genesis::test(seeds.iter().map(|s| s.parse().unwrap()).collect(), num_validator_seats); + genesis.config.epoch_length = epoch_length; + genesis.config.num_block_producer_seats = num_node_seats; + genesis.config.block_producer_kickout_threshold = 20; + genesis.config.chunk_producer_kickout_threshold = 20; + genesis.config.minimum_stake_divisor = minimum_stake_divisor; + if !enable_rewards { + genesis.config.max_inflation_rate = Ratio::from_integer(0); + genesis.config.min_gas_price = 0; + } + let first_node = tcp::ListenerAddr::reserve_for_test(); + + let configs = (0..num_node_seats).map(|i| { + let mut config = load_test_config( + &format!("near.{}", i), + if i == 0 { first_node } else { tcp::ListenerAddr::reserve_for_test() }, + genesis.clone(), + ); + // Disable state snapshots, because they don't work with epochs that are too short. + // And they are not needed in these tests. + config.config.store.state_snapshot_enabled = state_snapshot_enabled; + if track_all_shards { + config.config.tracked_shards = vec![0]; + config.client_config.tracked_shards = vec![0]; + } + if i != 0 { + config.network_config.peer_store.boot_nodes = + convert_boot_nodes(vec![("near.0", *first_node)]); + } + config.client_config.min_num_peers = num_node_seats as usize - 1; + config.client_config.epoch_sync_enabled = false; + config + }); + configs + .enumerate() + .map(|(i, config)| { + let genesis_hash = genesis_hash(&config.genesis); + let framework::NearNode { client, view_client, .. } = + start_with_config(paths[i], config.clone()).expect("start_with_config"); + let account_id = format!("near.{}", i).parse::().unwrap(); + let signer = Arc::new(InMemorySigner::from_seed( + account_id.clone(), + KeyType::ED25519, + account_id.as_ref(), + )); + TestNode { account_id, signer, config, client, view_client, genesis_hash } + }) + .collect() +} + +/// Runs one validator network, sends staking transaction for the second node and +/// waits until it becomes a validator. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_stake_nodes() { + heavy_test(|| { + let num_nodes = 2; + let dirs = (0..num_nodes) + .map(|i| { + tempfile::Builder::new().prefix(&format!("stake_node_{}", i)).tempdir().unwrap() + }) + .collect::>(); + run_actix(async { + let test_nodes = init_test_staking( + dirs.iter().map(|dir| dir.path()).collect::>(), + num_nodes, + 1, + 10, + false, + 10, + false, + false, + ); + + let tx = SignedTransaction::stake( + 1, + test_nodes[1].account_id.clone(), + // &*test_nodes[1].config.block_producer.as_ref().unwrap().signer, + &*test_nodes[1].signer, + TESTING_INIT_STAKE, + test_nodes[1].config.validator_signer.as_ref().unwrap().public_key(), + test_nodes[1].genesis_hash, + ); + actix::spawn( + test_nodes[0] + .client + .send( + ProcessTxRequest { + transaction: tx, + is_forwarded: false, + check_only: false, + } + .with_span_context(), + ) + .map(drop), + ); + + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let actor = test_nodes[0].client.send( + Status { is_health_check: false, detailed: false }.with_span_context(), + ); + let actor = actor.then(|res| { + let res = res.unwrap(); + if res.is_err() { + return future::ready(()); + } + let mut validators = res.unwrap().validators; + validators.sort_unstable_by(|a, b| a.account_id.cmp(&b.account_id)); + if validators + == vec![ + ValidatorInfo { + account_id: "near.0".parse().unwrap(), + is_slashed: false, + }, + ValidatorInfo { + account_id: "near.1".parse().unwrap(), + is_slashed: false, + }, + ] + { + System::current().stop(); + } + future::ready(()) + }); + actix::spawn(actor); + }), + 100, + 40000, + ) + .start(); + }); + }); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_validator_kickout() { + heavy_test(|| { + let num_nodes = 4; + let dirs = (0..num_nodes) + .map(|i| { + tempfile::Builder::new() + .prefix(&format!("validator_kickout_{}", i)) + .tempdir() + .unwrap() + }) + .collect::>(); + run_actix(async { + let test_nodes = init_test_staking( + dirs.iter().map(|dir| dir.path()).collect::>(), + num_nodes, + 4, + 15, + false, + (TESTING_INIT_STAKE / UNC_BASE) as u64 + 1, + false, + false, + ); + let mut rng = rand::thread_rng(); + let stakes = (0..num_nodes / 2).map(|_| UNC_BASE + rng.gen_range(1..100)); + let stake_transactions = stakes.enumerate().map(|(i, stake)| { + let test_node = &test_nodes[i]; + let signer = Arc::new(InMemorySigner::from_seed( + test_node.account_id.clone(), + KeyType::ED25519, + test_node.account_id.as_ref(), + )); + SignedTransaction::stake( + 1, + test_node.account_id.clone(), + &*signer, + stake, + test_node.config.validator_signer.as_ref().unwrap().public_key(), + test_node.genesis_hash, + ) + }); + + for (i, stake_transaction) in stake_transactions.enumerate() { + let test_node = &test_nodes[i]; + actix::spawn( + test_node + .client + .send( + ProcessTxRequest { + transaction: stake_transaction, + is_forwarded: false, + check_only: false, + } + .with_span_context(), + ) + .map(drop), + ); + } + + let finalized_mark: Arc> = + Arc::new((0..num_nodes).map(|_| Arc::new(AtomicBool::new(false))).collect()); + + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let test_nodes = test_nodes.clone(); + let test_node1 = test_nodes[(num_nodes / 2) as usize].clone(); + let finalized_mark1 = finalized_mark.clone(); + + let actor = test_node1.client.send( + Status { is_health_check: false, detailed: false }.with_span_context(), + ); + let actor = actor.then(move |res| { + let expected: Vec<_> = (num_nodes / 2..num_nodes) + .map(|i| ValidatorInfo { + account_id: AccountId::try_from(format!("near.{}", i)).unwrap(), + is_slashed: false, + }) + .collect(); + let res = res.unwrap(); + if res.is_err() { + return future::ready(()); + } + if res.unwrap().validators == expected { + for i in 0..num_nodes / 2 { + let mark = finalized_mark1[i as usize].clone(); + let actor = test_node1.view_client.send( + Query::new( + BlockReference::latest(), + QueryRequest::ViewAccount { + account_id: test_nodes[i as usize].account_id.clone(), + }, + ) + .with_span_context(), + ); + let actor = + actor.then(move |res| match res.unwrap().unwrap().kind { + QueryResponseKind::ViewAccount(result) => { + if result.locked == 0 + || result.amount == TESTING_INIT_BALANCE + { + mark.store(true, Ordering::SeqCst); + } + future::ready(()) + } + _ => panic!("wrong return result"), + }); + actix::spawn(actor); + } + for i in num_nodes / 2..num_nodes { + let mark = finalized_mark1[i as usize].clone(); + + let actor = test_node1.view_client.send( + Query::new( + BlockReference::latest(), + QueryRequest::ViewAccount { + account_id: test_nodes[i as usize].account_id.clone(), + }, + ) + .with_span_context(), + ); + let actor = + actor.then(move |res| match res.unwrap().unwrap().kind { + QueryResponseKind::ViewAccount(result) => { + assert_eq!(result.locked, TESTING_INIT_STAKE); + assert_eq!( + result.amount, + TESTING_INIT_BALANCE - TESTING_INIT_STAKE + ); + mark.store(true, Ordering::SeqCst); + future::ready(()) + } + _ => panic!("wrong return result"), + }); + actix::spawn(actor); + } + + if finalized_mark1.iter().all(|mark| mark.load(Ordering::SeqCst)) { + System::current().stop(); + } + } + future::ready(()) + }); + actix::spawn(actor); + }), + 100, + 70000, + ) + .start(); + }); + }) +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +/// Starts 4 nodes, genesis has 2 validator seats. +/// Node1 unstakes, Node2 stakes. +/// Submit the transactions via Node1 and Node2. +/// Poll `/status` until you see the change of validator assignments. +/// Afterwards check that `locked` amount on accounts Node1 and Node2 are 0 and TESTING_INIT_STAKE. +fn test_validator_join() { + heavy_test(|| { + let num_nodes = 4; + let dirs = (0..num_nodes) + .map(|i| { + tempfile::Builder::new().prefix(&format!("validator_join_{}", i)).tempdir().unwrap() + }) + .collect::>(); + run_actix(async { + let test_nodes = init_test_staking( + dirs.iter().map(|dir| dir.path()).collect::>(), + num_nodes, + 2, + 30, + false, + 10, + false, + true, + ); + let signer = Arc::new(InMemorySigner::from_seed( + test_nodes[1].account_id.clone(), + KeyType::ED25519, + test_nodes[1].account_id.as_ref(), + )); + let unstake_transaction = SignedTransaction::stake( + 1, + test_nodes[1].account_id.clone(), + &*signer, + 0, + test_nodes[1].config.validator_signer.as_ref().unwrap().public_key(), + test_nodes[1].genesis_hash, + ); + + let signer = Arc::new(InMemorySigner::from_seed( + test_nodes[2].account_id.clone(), + KeyType::ED25519, + test_nodes[2].account_id.as_ref(), + )); + let stake_transaction = SignedTransaction::stake( + 1, + test_nodes[2].account_id.clone(), + &*signer, + TESTING_INIT_STAKE, + test_nodes[2].config.validator_signer.as_ref().unwrap().public_key(), + test_nodes[2].genesis_hash, + ); + + actix::spawn( + test_nodes[1] + .client + .send( + ProcessTxRequest { + transaction: unstake_transaction, + is_forwarded: false, + check_only: false, + } + .with_span_context(), + ) + .map(drop), + ); + actix::spawn( + test_nodes[0] + .client + .send( + ProcessTxRequest { + transaction: stake_transaction, + is_forwarded: false, + check_only: false, + } + .with_span_context(), + ) + .map(drop), + ); + + let (done1, done2) = + (Arc::new(AtomicBool::new(false)), Arc::new(AtomicBool::new(false))); + let (done1_copy1, done2_copy1) = (done1, done2); + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let test_nodes = test_nodes.clone(); + let test_node1 = test_nodes[0].clone(); + let (done1_copy2, done2_copy2) = (done1_copy1.clone(), done2_copy1.clone()); + let actor = test_node1.client.send( + Status { is_health_check: false, detailed: false }.with_span_context(), + ); + let actor = actor.then(move |res| { + let expected = vec![ + ValidatorInfo { + account_id: "near.0".parse().unwrap(), + is_slashed: false, + }, + ValidatorInfo { + account_id: "near.2".parse().unwrap(), + is_slashed: false, + }, + ]; + let res = res.unwrap(); + if res.is_err() { + return future::ready(()); + } + if res.unwrap().validators == expected { + let actor = test_node1.view_client.send( + Query::new( + BlockReference::latest(), + QueryRequest::ViewAccount { + account_id: test_nodes[1].account_id.clone(), + }, + ) + .with_span_context(), + ); + let actor = actor.then(move |res| match res.unwrap().unwrap().kind { + QueryResponseKind::ViewAccount(result) => { + if result.locked == 0 { + done1_copy2.store(true, Ordering::SeqCst); + } + future::ready(()) + } + _ => panic!("wrong return result"), + }); + actix::spawn(actor); + let actor = test_node1.view_client.send( + Query::new( + BlockReference::latest(), + QueryRequest::ViewAccount { + account_id: test_nodes[2].account_id.clone(), + }, + ) + .with_span_context(), + ); + let actor = actor.then(move |res| match res.unwrap().unwrap().kind { + QueryResponseKind::ViewAccount(result) => { + if result.locked == TESTING_INIT_STAKE { + done2_copy2.store(true, Ordering::SeqCst); + } + + future::ready(()) + } + _ => panic!("wrong return result"), + }); + actix::spawn(actor); + } + + future::ready(()) + }); + actix::spawn(actor); + if done1_copy1.load(Ordering::SeqCst) && done2_copy1.load(Ordering::SeqCst) { + System::current().stop(); + } + }), + 1000, + 60000, + ) + .start(); + }); + }); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +/// Checks that during the first epoch, total_supply matches total_supply in genesis. +/// Checks that during the second epoch, total_supply matches the expected inflation rate. +fn test_inflation() { + heavy_test(|| { + let num_nodes = 1; + let dirs = (0..num_nodes) + .map(|i| { + tempfile::Builder::new().prefix(&format!("stake_node_{}", i)).tempdir().unwrap() + }) + .collect::>(); + let epoch_length = 10; + run_actix(async { + let test_nodes = init_test_staking( + dirs.iter().map(|dir| dir.path()).collect::>(), + num_nodes, + 1, + epoch_length, + true, + 10, + false, + false, + ); + let initial_total_supply = test_nodes[0].config.genesis.config.total_supply; + let max_inflation_rate = test_nodes[0].config.genesis.config.max_inflation_rate; + + let (done1, done2) = + (Arc::new(AtomicBool::new(false)), Arc::new(AtomicBool::new(false))); + let (done1_copy1, done2_copy1) = (done1, done2); + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let (done1_copy2, done2_copy2) = (done1_copy1.clone(), done2_copy1.clone()); + let actor = + test_nodes[0].view_client.send(GetBlock::latest().with_span_context()); + let actor = actor.then(move |res| { + if let Ok(Ok(block)) = res { + if block.header.height >= 2 && block.header.height <= epoch_length { + tracing::info!(?block.header.total_supply, ?block.header.height, ?initial_total_supply, epoch_length, "Step1: epoch1"); + if block.header.total_supply == initial_total_supply { + done1_copy2.store(true, Ordering::SeqCst); + } + } else { + tracing::info!("Step1: not epoch1"); + } + } + future::ready(()) + }); + actix::spawn(actor); + let view_client = test_nodes[0].view_client.clone(); + actix::spawn(async move { + if let Ok(Ok(block)) = + view_client.send(GetBlock::latest().with_span_context()).await + { + if block.header.height > epoch_length + && block.header.height < epoch_length * 2 + { + tracing::info!(?block.header.total_supply, ?block.header.height, ?initial_total_supply, epoch_length, "Step2: epoch2"); + // It's expected that validator will miss first chunk, hence will only be 95% online, getting 5/9 of their reward. + // +10% of protocol reward = 60% of max inflation are allocated. + let base_reward = { + let genesis_block_view = view_client + .send( + GetBlock(BlockReference::BlockId(BlockId::Height(0))) + .with_span_context(), + ) + .await + .unwrap() + .unwrap(); + let epoch_end_block_view = view_client + .send( + GetBlock(BlockReference::BlockId(BlockId::Height( + epoch_length, + ))) + .with_span_context(), + ) + .await + .unwrap() + .unwrap(); + (U256::from(initial_total_supply) + * U256::from( + epoch_end_block_view.header.timestamp_nanosec + - genesis_block_view.header.timestamp_nanosec, + ) + * U256::from(*max_inflation_rate.numer() as u64) + / (U256::from(10u64.pow(9) * 365 * 24 * 60 * 60) + * U256::from(*max_inflation_rate.denom() as u64))) + .as_u128() + }; + // To match rounding, split into protocol reward and validator reward. + let protocol_reward = base_reward * 1 / 10; + let inflation = + base_reward * 1 / 10 + (base_reward - protocol_reward) * 5 / 9; + tracing::info!(?block.header.total_supply, ?block.header.height, ?initial_total_supply, epoch_length, ?inflation, "Step2: epoch2"); + if block.header.total_supply == initial_total_supply + inflation { + done2_copy2.store(true, Ordering::SeqCst); + } + } else { + tracing::info!("Step2: not epoch2"); + } + } + }); + if done1_copy1.load(Ordering::SeqCst) && done2_copy1.load(Ordering::SeqCst) { + System::current().stop(); + } + }), + 100, + 20000, + ) + .start(); + }); + }); +} diff --git a/integration-tests/src/tests/nearcore/sync_nodes.rs b/integration-tests/src/tests/nearcore/sync_nodes.rs new file mode 100644 index 000000000..0e1e65631 --- /dev/null +++ b/integration-tests/src/tests/nearcore/sync_nodes.rs @@ -0,0 +1,238 @@ +use crate::genesis_helpers::genesis_block; +use crate::nearcore_utils::{add_blocks, setup_configs}; +use crate::test_helpers::heavy_test; +use actix::{Actor, System}; +use futures::{future, FutureExt}; +use unc_actix_test_utils::run_actix; +use unc_chain_configs::Genesis; +use unc_client::{GetBlock, ProcessTxRequest}; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_network::tcp; +use unc_network::test_utils::{convert_boot_nodes, WaitOrTimeoutActor}; +use unc_o11y::testonly::init_integration_logger; +use unc_o11y::WithSpanContextExt; +use unc_primitives::test_utils::create_test_signer; +use unc_primitives::transaction::SignedTransaction; +use framework::config::{GenesisExt, TESTING_INIT_STAKE}; +use framework::{load_test_config, start_with_config}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, RwLock}; +use std::time::Duration; + +/// One client is in front, another must sync to it before they can produce blocks. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn sync_nodes() { + heavy_test(|| { + init_integration_logger(); + + let (genesis, genesis_block, near1, near2) = setup_configs(); + + run_actix(async move { + let dir1 = tempfile::Builder::new().prefix("sync_nodes_1").tempdir().unwrap(); + let framework::NearNode { client: client1, .. } = + start_with_config(dir1.path(), near1).expect("start_with_config"); + + let signer = create_test_signer("other"); + let _ = + add_blocks(vec![genesis_block], client1, 13, genesis.config.epoch_length, &signer); + + let dir2 = tempfile::Builder::new().prefix("sync_nodes_2").tempdir().unwrap(); + let framework::NearNode { view_client: view_client2, .. } = + start_with_config(dir2.path(), near2).expect("start_with_config"); + + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let actor = view_client2.send(GetBlock::latest().with_span_context()); + let actor = actor.then(|res| { + match &res { + Ok(Ok(b)) if b.header.height == 13 => System::current().stop(), + Err(_) => return future::ready(()), + _ => {} + }; + future::ready(()) + }); + actix::spawn(actor); + }), + 100, + 60000, + ) + .start(); + }); + }); +} + +/// Clients connect and then one of them becomes in front. The other one must then sync to it. +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn sync_after_sync_nodes() { + heavy_test(|| { + init_integration_logger(); + + let (genesis, genesis_block, near1, near2) = setup_configs(); + + run_actix(async move { + let dir1 = tempfile::Builder::new().prefix("sync_nodes_1").tempdir().unwrap(); + let framework::NearNode { client: client1, .. } = + start_with_config(dir1.path(), near1).expect("start_with_config"); + + let dir2 = tempfile::Builder::new().prefix("sync_nodes_2").tempdir().unwrap(); + let framework::NearNode { view_client: view_client2, .. } = + start_with_config(dir2.path(), near2).expect("start_with_config"); + + let signer = create_test_signer("other"); + let blocks = add_blocks( + vec![genesis_block], + client1.clone(), + 13, + genesis.config.epoch_length, + &signer, + ); + + let next_step = Arc::new(AtomicBool::new(false)); + let epoch_length = genesis.config.epoch_length; + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let blocks1 = blocks.clone(); + let client11 = client1.clone(); + let signer1 = signer.clone(); + let next_step1 = next_step.clone(); + let actor = view_client2.send(GetBlock::latest().with_span_context()); + let actor = actor.then(move |res| { + match &res { + Ok(Ok(b)) if b.header.height == 13 => { + if !next_step1.load(Ordering::Relaxed) { + let _ = + add_blocks(blocks1, client11, 10, epoch_length, &signer1); + next_step1.store(true, Ordering::Relaxed); + } + } + Ok(Ok(b)) if b.header.height > 20 => System::current().stop(), + Err(_) => return future::ready(()), + _ => {} + }; + future::ready(()) + }); + actix::spawn(actor); + }), + 100, + 60000, + ) + .start(); + }); + }); +} + +/// Starts one validation node, it reduces it's stake to 1/2 of the stake. +/// Second node starts after 1s, needs to catchup & state sync and then make sure it's +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn sync_state_stake_change() { + heavy_test(|| { + init_integration_logger(); + + let mut genesis = Genesis::test(vec!["test1".parse().unwrap()], 1); + let epoch_length = 20; + genesis.config.epoch_length = epoch_length; + genesis.config.block_producer_kickout_threshold = 80; + + let (port1, port2) = + (tcp::ListenerAddr::reserve_for_test(), tcp::ListenerAddr::reserve_for_test()); + let mut near1 = load_test_config("test1", port1, genesis.clone()); + near1.network_config.peer_store.boot_nodes = convert_boot_nodes(vec![("test2", *port2)]); + near1.client_config.min_num_peers = 0; + near1.client_config.min_block_production_delay = Duration::from_millis(200); + near1.client_config.epoch_sync_enabled = false; + let mut near2 = load_test_config("test2", port2, genesis.clone()); + near2.network_config.peer_store.boot_nodes = convert_boot_nodes(vec![("test1", *port1)]); + near2.client_config.min_block_production_delay = Duration::from_millis(200); + near2.client_config.min_num_peers = 1; + near2.client_config.skip_sync_wait = false; + near2.client_config.epoch_sync_enabled = false; + + let dir1 = tempfile::Builder::new().prefix("sync_state_stake_change_1").tempdir().unwrap(); + let dir2 = tempfile::Builder::new().prefix("sync_state_stake_change_2").tempdir().unwrap(); + run_actix(async { + let framework::NearNode { client: client1, view_client: view_client1, .. } = + start_with_config(dir1.path(), near1.clone()).expect("start_with_config"); + + let genesis_hash = *genesis_block(&genesis).hash(); + let signer = Arc::new(InMemorySigner::from_seed( + "test1".parse().unwrap(), + KeyType::ED25519, + "test1", + )); + let unstake_transaction = SignedTransaction::stake( + 1, + "test1".parse().unwrap(), + &*signer, + TESTING_INIT_STAKE / 2, + near1.validator_signer.as_ref().unwrap().public_key(), + genesis_hash, + ); + actix::spawn( + client1 + .send( + ProcessTxRequest { + transaction: unstake_transaction, + is_forwarded: false, + check_only: false, + } + .with_span_context(), + ) + .map(drop), + ); + + let started = Arc::new(AtomicBool::new(false)); + let dir2_path = dir2.path().to_path_buf(); + let arbiters_holder = Arc::new(RwLock::new(vec![])); + let arbiters_holder2 = arbiters_holder; + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let started_copy = started.clone(); + let near2_copy = near2.clone(); + let dir2_path_copy = dir2_path.clone(); + let arbiters_holder2 = arbiters_holder2.clone(); + let actor = view_client1.send(GetBlock::latest().with_span_context()); + let actor = actor.then(move |res| { + let latest_height = + if let Ok(Ok(block)) = res { block.header.height } else { 0 }; + if !started_copy.load(Ordering::SeqCst) && latest_height > 2 * epoch_length + { + started_copy.store(true, Ordering::SeqCst); + let framework::NearNode { view_client: view_client2, arbiters, .. } = + start_with_config(&dir2_path_copy, near2_copy) + .expect("start_with_config"); + *arbiters_holder2.write().unwrap() = arbiters; + + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + actix::spawn( + view_client2 + .send(GetBlock::latest().with_span_context()) + .then(move |res| { + if let Ok(Ok(block)) = res { + if block.header.height > latest_height + 1 { + System::current().stop() + } + } + future::ready(()) + }), + ); + }), + 100, + 30000, + ) + .start(); + } + future::ready(()) + }); + actix::spawn(actor); + }), + 100, + 35000, + ) + .start(); + }); + }); +} diff --git a/integration-tests/src/tests/nearcore/track_shards.rs b/integration-tests/src/tests/nearcore/track_shards.rs new file mode 100644 index 000000000..994e18802 --- /dev/null +++ b/integration-tests/src/tests/nearcore/track_shards.rs @@ -0,0 +1,60 @@ +use std::ops::ControlFlow; +use std::sync::{Arc, RwLock}; + +use actix::System; + +use unc_client::{GetBlock, GetChunk}; +use unc_network::test_utils::wait_or_timeout; +use unc_o11y::testonly::init_integration_logger; +use unc_o11y::WithSpanContextExt; +use unc_primitives::hash::CryptoHash; + +use crate::tests::framework::node_cluster::NodeCluster; + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn track_shards() { + init_integration_logger(); + + let cluster = NodeCluster::default() + .set_num_shards(4) + .set_num_nodes(4) + .set_num_validator_seats(2) + .set_num_lightclients(0) + .set_epoch_length(10) + .set_genesis_height(0); + + cluster.exec_until_stop(|_, _, clients| async move { + let view_client = clients[clients.len() - 1].1.clone(); + let last_block_hash: Arc>> = Arc::new(RwLock::new(None)); + wait_or_timeout(100, 30000, || async { + let bh = *last_block_hash.read().unwrap(); + if let Some(block_hash) = bh { + let res = + view_client.send(GetChunk::BlockHash(block_hash, 3).with_span_context()).await; + match &res { + Ok(Ok(_)) => { + return ControlFlow::Break(()); + } + _ => { + return ControlFlow::Continue(()); + } + } + } else { + let last_block_hash1 = last_block_hash.clone(); + let res = view_client.send(GetBlock::latest().with_span_context()).await; + match &res { + Ok(Ok(b)) if b.header.height > 10 => { + *last_block_hash1.write().unwrap() = Some(b.header.hash); + } + Err(_) => return ControlFlow::Continue(()), + _ => {} + }; + ControlFlow::Continue(()) + } + }) + .await + .unwrap(); + System::current().stop() + }); +} diff --git a/integration-tests/src/tests/network/churn_attack.rs b/integration-tests/src/tests/network/churn_attack.rs new file mode 100644 index 000000000..7e85de75a --- /dev/null +++ b/integration-tests/src/tests/network/churn_attack.rs @@ -0,0 +1,23 @@ +use crate::tests::network::runner::*; + +/// Spin up four nodes and connect them in a square. +/// Each node will have at most two connections. +/// Turn off two non adjacent nodes, and check other two nodes create +/// a connection among them. +#[test] +fn churn_attack() -> anyhow::Result<()> { + let mut runner = Runner::new(4, 4).enable_outbound().max_num_peers(2).ideal_connections(2, 2); + + runner.push(Action::AddEdge { from: 0, to: 1, force: true }); + runner.push(Action::AddEdge { from: 2, to: 3, force: true }); + runner.push(Action::AddEdge { from: 3, to: 0, force: true }); + runner.push(Action::AddEdge { from: 1, to: 2, force: true }); + runner.push(Action::CheckRoutingTable(0, vec![(1, vec![1]), (3, vec![3]), (2, vec![1, 3])])); + runner.push(Action::CheckRoutingTable(2, vec![(1, vec![1]), (3, vec![3]), (0, vec![1, 3])])); + runner.push(Action::Stop(1)); + runner.push(Action::Stop(3)); + runner.push(Action::CheckRoutingTable(0, vec![(2, vec![2])])); + runner.push(Action::CheckRoutingTable(2, vec![(0, vec![0])])); + + start_test(runner) +} diff --git a/integration-tests/src/tests/network/full_network.rs b/integration-tests/src/tests/network/full_network.rs new file mode 100644 index 000000000..acbb5ddc9 --- /dev/null +++ b/integration-tests/src/tests/network/full_network.rs @@ -0,0 +1,109 @@ +use crate::tests::network::runner::*; +use unc_async::time; +use std::cmp::min; + +/// Check that a node is able to connect to the network, even if the number +/// of active peers of every node is high. +/// +/// Spawn a network with `num_node` nodes and with top connections allowed +/// for every peer `max_num_peers`. Wait until every peer has `expected_connections` +/// active peers and start new node. Wait until new node is connected. +pub fn connect_at_max_capacity( + num_node: usize, + ideal_lo: u32, + ideal_hi: u32, + max_num_peers: u32, + expected_connections: usize, + extra_nodes: usize, +) -> anyhow::Result<()> { + // Use at most 4 boot nodes + let total_boot_nodes = min(num_node, 4); + + let boot_nodes = (0..total_boot_nodes).collect::>(); + + let mut runner = Runner::new(num_node + extra_nodes, num_node) + .enable_outbound() + .max_num_peers(max_num_peers) + .use_boot_nodes(boot_nodes) + .safe_set_size(1) + .minimum_outbound_peers(0) + .ideal_connections(ideal_lo, ideal_hi); + + let total_nodes = num_node + extra_nodes; + + // Stop all extra nodes + for node_id in num_node..total_nodes { + runner.push(Action::Stop(node_id)); + } + + // Wait until all running nodes have expected connections + for node_id in 0..num_node { + runner.push_action(check_expected_connections(node_id, Some(expected_connections), None)); + } + + // Restart stopped nodes + for node_id in num_node..total_nodes { + runner.push_action(restart(node_id)); + } + + // Wait until all nodes have expected connections + for node_id in 0..total_nodes { + runner.push_action(check_expected_connections(node_id, Some(expected_connections), None)); + } + + start_test(runner) +} + +/// Check that two nodes are able to connect if they only know themselves from boot list. +#[test] +fn simple_network() -> anyhow::Result<()> { + connect_at_max_capacity(2, 1, 1, 1, 1, 0) +} + +/// Start 4 nodes and connect them all with each other. +/// Create new node, it should be able to connect since max allowed peers is 4. +#[test] +fn connect_on_almost_full_network() -> anyhow::Result<()> { + connect_at_max_capacity(4, 1, 3, 4, 1, 1) +} + +/// Start 4 nodes and connect them all with each other. +/// Create new node, it should be able to connect even if max allowed peers is 3. +#[test] +fn connect_on_full_network() -> anyhow::Result<()> { + connect_at_max_capacity(5, 2, 3, 4, 2, 1) +} + +#[test] +fn connect_whitelisted() -> anyhow::Result<()> { + let nodes = 4; + let validators = 2; + let mut runner = Runner::new(nodes, validators) + .enable_outbound() + .safe_set_size(1) + .minimum_outbound_peers(0) + .max_num_peers(2) + .ideal_connections(2, 2) + // 3 is whitelisted by 0. + .add_to_whitelist(0, 3); + // Fill completely the capacity of the first 3 nodes. + runner.push(Action::AddEdge { from: 0, to: 1, force: true }); + runner.push(Action::AddEdge { from: 1, to: 2, force: true }); + runner.push(Action::AddEdge { from: 2, to: 0, force: true }); + // Try to establish an extra non-whitelisted connection. + runner.push(Action::AddEdge { from: 3, to: 1, force: false }); + runner.push(Action::AddEdge { from: 3, to: 2, force: false }); + // Establish an extra whitelisted connection. + runner.push(Action::AddEdge { from: 3, to: 0, force: true }); + // Wait for the topology to stabilize. + // - 3 shouldn't be able to establish a connection to 1,2 + // - 1 shouldn't drop connections to 0,2,3, even though the + // connection limit is 2, since 0<->3 connection doesn't + // count towards this limit. + runner.push(Action::Wait(time::Duration::milliseconds(200))); + runner.push_action(assert_expected_peers(0, vec![1, 2, 3])); + runner.push_action(assert_expected_peers(1, vec![0, 2])); + runner.push_action(assert_expected_peers(2, vec![0, 1])); + runner.push_action(assert_expected_peers(3, vec![0])); + start_test(runner) +} diff --git a/integration-tests/src/tests/network/mod.rs b/integration-tests/src/tests/network/mod.rs new file mode 100644 index 000000000..af8102428 --- /dev/null +++ b/integration-tests/src/tests/network/mod.rs @@ -0,0 +1,5 @@ +mod churn_attack; +mod full_network; +mod peer_handshake; +mod runner; +mod stress_network; diff --git a/integration-tests/src/tests/network/peer_handshake.rs b/integration-tests/src/tests/network/peer_handshake.rs new file mode 100644 index 000000000..828dc82ff --- /dev/null +++ b/integration-tests/src/tests/network/peer_handshake.rs @@ -0,0 +1,223 @@ +use crate::tests::network::runner::*; +use actix::Actor; +use actix::System; +use futures::{future, FutureExt}; +use unc_actix_test_utils::run_actix; +use unc_async::time; +use unc_network::config; +use unc_network::tcp; +use unc_network::test_utils::{ + convert_boot_nodes, wait_or_timeout, GetInfo, StopSignal, WaitOrTimeoutActor, +}; +use unc_network::PeerManagerActor; +use unc_o11y::testonly::init_test_logger; +use unc_o11y::WithSpanContextExt; +use unc_primitives::block::GenesisId; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::Arc; + +#[cfg(test)] +fn make_peer_manager( + seed: &str, + node_addr: tcp::ListenerAddr, + boot_nodes: Vec<(&str, std::net::SocketAddr)>, + peer_max_count: u32, +) -> actix::Addr { + use unc_async::messaging::Sender; + + let mut config = config::NetworkConfig::from_seed(seed, node_addr); + config.peer_store.boot_nodes = convert_boot_nodes(boot_nodes); + config.max_num_peers = peer_max_count; + config.ideal_connections_hi = peer_max_count; + config.ideal_connections_lo = peer_max_count; + + PeerManagerActor::spawn( + time::Clock::real(), + unc_store::db::TestDB::new(), + config, + Arc::new(unc_network::client::Noop), + Sender::noop(), + GenesisId::default(), + ) + .unwrap() +} + +#[test] +fn peer_handshake() { + init_test_logger(); + + run_actix(async { + let addr1 = tcp::ListenerAddr::reserve_for_test(); + let addr2 = tcp::ListenerAddr::reserve_for_test(); + let pm1 = make_peer_manager("test1", addr1, vec![("test2", *addr2)], 10); + let _pm2 = make_peer_manager("test2", addr2, vec![("test1", *addr1)], 10); + wait_or_timeout(100, 2000, || async { + let info = pm1.send(GetInfo {}.with_span_context()).await.unwrap(); + if info.num_connected_peers == 1 { + return ControlFlow::Break(()); + } + ControlFlow::Continue(()) + }) + .await + .unwrap(); + System::current().stop() + }); +} + +#[test] +fn peers_connect_all() { + init_test_logger(); + + run_actix(async { + let addr = tcp::ListenerAddr::reserve_for_test(); + let _pm = make_peer_manager("test", addr, vec![], 10); + let mut peers = vec![]; + + let num_peers = 5; + for i in 0..num_peers { + let pm = make_peer_manager( + &format!("test{}", i), + tcp::ListenerAddr::reserve_for_test(), + vec![("test", *addr)], + 10, + ); + peers.push(pm); + } + let flags = Arc::new(AtomicUsize::new(0)); + WaitOrTimeoutActor::new( + Box::new(move |_| { + for i in 0..num_peers { + let flags1 = flags.clone(); + let actor = peers[i].send(GetInfo {}.with_span_context()); + let actor = actor.then(move |res| { + let info = res.unwrap(); + if info.num_connected_peers > num_peers - 1 + && (flags1.load(Ordering::Relaxed) >> i) % 2 == 0 + { + flags1.fetch_add(1 << i, Ordering::Relaxed); + } + future::ready(()) + }); + actix::spawn(actor); + } + // Stop if all connected to all after exchanging peers. + if flags.load(Ordering::Relaxed) == (1 << num_peers) - 1 { + System::current().stop(); + } + }), + 100, + 10000, + ) + .start(); + }); +} + +/// Check network is able to recover after node restart. +#[test] +fn peer_recover() { + init_test_logger(); + + run_actix(async { + let addr0 = tcp::ListenerAddr::reserve_for_test(); + let pm0 = Arc::new(make_peer_manager("test0", addr0, vec![], 2)); + let _pm1 = make_peer_manager( + "test1", + tcp::ListenerAddr::reserve_for_test(), + vec![("test0", *addr0)], + 1, + ); + + let mut pm2 = Arc::new(make_peer_manager( + "test2", + tcp::ListenerAddr::reserve_for_test(), + vec![("test0", *addr0)], + 1, + )); + + let state = Arc::new(AtomicUsize::new(0)); + let flag = Arc::new(AtomicBool::new(false)); + + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + if state.load(Ordering::Relaxed) == 0 { + // Wait a small timeout for connection to be active. + state.store(1, Ordering::Relaxed); + } else if state.load(Ordering::Relaxed) == 1 { + // Stop node2. + let _ = pm2.do_send(StopSignal::default().with_span_context()); + state.store(2, Ordering::Relaxed); + } else if state.load(Ordering::Relaxed) == 2 { + // Wait until node0 removes node2 from active validators. + if !flag.load(Ordering::Relaxed) { + let flag1 = flag.clone(); + let actor = pm0.send(GetInfo {}.with_span_context()); + let actor = actor.then(move |res| { + if let Ok(info) = res { + if info.connected_peers.len() == 1 { + flag1.store(true, Ordering::Relaxed); + } + } + future::ready(()) + }); + actix::spawn(actor); + } else { + state.store(3, Ordering::Relaxed); + } + } else if state.load(Ordering::Relaxed) == 3 { + // Start node2 from scratch again. + pm2 = Arc::new(make_peer_manager( + "test2", + tcp::ListenerAddr::reserve_for_test(), + vec![("test0", *addr0)], + 1, + )); + + state.store(4, Ordering::Relaxed); + } else if state.load(Ordering::Relaxed) == 4 { + // Wait until node2 is connected with node0 + let actor = pm2.send(GetInfo {}.with_span_context()); + let actor = actor.then(|res| { + if let Ok(info) = res { + if info.connected_peers.len() == 1 { + System::current().stop(); + } + } + future::ready(()) + }); + actix::spawn(actor); + } + }), + 100, + 10000, + ) + .start(); + }); +} + +/// Create two nodes A and B and connect them. +/// Stop node B, change its identity (PeerId) and spawn it again. +/// B knows nothing about A (since store is wiped) and A knows old information from B. +/// A should learn new information from B and connect with it. +#[test] +fn check_connection_with_new_identity() -> anyhow::Result<()> { + let mut runner = Runner::new(2, 2).enable_outbound(); + + // This is needed, because even if outbound is enabled, there is no booting nodes, + // so A and B doesn't know each other yet. + runner.push(Action::AddEdge { from: 0, to: 1, force: true }); + + runner.push(Action::CheckRoutingTable(0, vec![(1, vec![1])])); + runner.push(Action::CheckRoutingTable(1, vec![(0, vec![0])])); + + runner.push(Action::Stop(1)); + runner.push_action(change_account_id(1, "far".parse().unwrap())); + runner.push(Action::CheckRoutingTable(0, vec![])); + runner.push_action(restart(1)); + + runner.push(Action::CheckRoutingTable(0, vec![(1, vec![1])])); + runner.push(Action::CheckRoutingTable(1, vec![(0, vec![0])])); + + runner.push(Action::Wait(time::Duration::milliseconds(2000))); + + start_test(runner) +} diff --git a/integration-tests/src/tests/network/runner.rs b/integration-tests/src/tests/network/runner.rs new file mode 100644 index 000000000..3f7e5441c --- /dev/null +++ b/integration-tests/src/tests/network/runner.rs @@ -0,0 +1,565 @@ +use actix::{Actor, Addr}; +use anyhow::{anyhow, bail, Context}; +use unc_async::actix::AddrWithAutoSpanContextExt; +use unc_async::messaging::{IntoSender, LateBoundSender, Sender}; +use unc_async::time; +use unc_chain::test_utils::{KeyValueRuntime, MockEpochManager, ValidatorSchedule}; +use unc_chain::types::RuntimeAdapter; +use unc_chain::{Chain, ChainGenesis}; +use unc_chain_configs::ClientConfig; +use unc_chunks::shards_manager_actor::start_shards_manager; +use unc_client::{start_client, start_view_client, SyncAdapter}; +use unc_epoch_manager::shard_tracker::ShardTracker; +use unc_network::actix::ActixSystem; +use unc_network::blacklist; +use unc_network::config; +use unc_network::tcp; +use unc_network::test_utils::{expected_routing_tables, peer_id_from_seed, GetInfo}; +use unc_network::types::{ + PeerInfo, PeerManagerMessageRequest, PeerManagerMessageResponse, ROUTED_MESSAGE_TTL, +}; +use unc_network::PeerManagerActor; +use unc_o11y::testonly::init_test_logger; +use unc_o11y::WithSpanContextExt; +use unc_primitives::block::GenesisId; +use unc_primitives::network::PeerId; +use unc_primitives::test_utils::create_test_signer; +use unc_primitives::types::{AccountId, ValidatorId}; +use unc_primitives::validator_signer::ValidatorSigner; +use unc_telemetry::{TelemetryActor, TelemetryConfig}; +use std::collections::HashSet; +use std::future::Future; +use std::iter::Iterator; +use std::net::{Ipv6Addr, SocketAddr}; +use std::pin::Pin; +use std::sync::{Arc, RwLock}; +use tracing::debug; + +pub(crate) type ControlFlow = std::ops::ControlFlow<()>; +pub(crate) type BoxFuture<'a, T> = Pin + 'a>>; +pub(crate) type ActionFn = + Box Fn(&'a mut RunningInfo) -> BoxFuture<'a, anyhow::Result>>; + +/// Sets up a node with a valid Client, Peer +fn setup_network_node( + account_id: AccountId, + validators: Vec, + chain_genesis: ChainGenesis, + config: config::NetworkConfig, +) -> Addr { + let store = unc_store::test_utils::create_test_node_storage_default(); + + let num_validators = validators.len() as ValidatorId; + + let vs = ValidatorSchedule::new().block_producers_per_epoch(vec![validators]); + let epoch_manager = MockEpochManager::new_with_validators(store.get_hot_store(), vs, 5); + let shard_tracker = ShardTracker::new_empty(epoch_manager.clone()); + let runtime = KeyValueRuntime::new(store.get_hot_store(), epoch_manager.as_ref()); + let signer = Arc::new(create_test_signer(account_id.as_str())); + let telemetry_actor = TelemetryActor::new(TelemetryConfig::default()).start(); + + let db = store.into_inner(unc_store::Temperature::Hot); + let mut client_config = + ClientConfig::test(false, 100, 200, num_validators, false, true, true, true); + client_config.archive = config.archive; + client_config.ttl_account_id_router = config.ttl_account_id_router.try_into().unwrap(); + let genesis_block = + Chain::make_genesis_block(epoch_manager.as_ref(), runtime.as_ref(), &chain_genesis) + .unwrap(); + let genesis_id = GenesisId { + chain_id: client_config.chain_id.clone(), + hash: *genesis_block.header().hash(), + }; + let network_adapter = Arc::new(LateBoundSender::default()); + let shards_manager_adapter = Arc::new(LateBoundSender::default()); + let adv = unc_client::adversarial::Controls::default(); + let state_sync_adapter = + Arc::new(RwLock::new(SyncAdapter::new(Sender::noop(), Sender::noop()))); + let client_actor = start_client( + client_config.clone(), + chain_genesis.clone(), + epoch_manager.clone(), + shard_tracker.clone(), + runtime.clone(), + config.node_id(), + state_sync_adapter, + network_adapter.clone().into(), + shards_manager_adapter.as_sender(), + Some(signer.clone()), + telemetry_actor, + None, + None, + adv.clone(), + None, + ) + .0; + let view_client_actor = start_view_client( + config.validator.as_ref().map(|v| v.account_id()), + chain_genesis, + epoch_manager.clone(), + shard_tracker.clone(), + runtime.clone(), + network_adapter.clone().into(), + client_config.clone(), + adv, + ); + let (shards_manager_actor, _) = start_shards_manager( + epoch_manager, + shard_tracker, + network_adapter.as_sender(), + client_actor.clone().with_auto_span_context().into_sender(), + Some(signer.validator_id().clone()), + runtime.store().clone(), + client_config.chunk_request_retry_period, + ); + shards_manager_adapter.bind(shards_manager_actor.with_auto_span_context()); + let peer_manager = PeerManagerActor::spawn( + time::Clock::real(), + db.clone(), + config, + Arc::new(unc_client::adapter::Adapter::new(client_actor, view_client_actor)), + shards_manager_adapter.as_sender(), + genesis_id, + ) + .unwrap(); + network_adapter.bind(peer_manager.clone().with_auto_span_context()); + peer_manager +} + +// TODO: Deprecate this in favor of separate functions. +#[derive(Debug, Clone)] +pub(crate) enum Action { + AddEdge { from: usize, to: usize, force: bool }, + CheckRoutingTable(usize, Vec<(usize, Vec)>), + // Send stop signal to some node. + Stop(usize), + // Wait time in milliseconds + Wait(time::Duration), +} + +pub(crate) struct RunningInfo { + runner: Runner, + nodes: Vec>, +} + +struct StateMachine { + actions: Vec, +} + +async fn check_routing_table( + info: &RunningInfo, + u: usize, + want: Vec<(usize, Vec)>, +) -> anyhow::Result { + let want_rt: Vec<_> = want + .into_iter() + .map(|(target, routes)| { + let peers = + routes.into_iter().map(|hop| info.runner.test_config[hop].peer_id()).collect(); + (info.runner.test_config[target].peer_id(), peers) + }) + .collect(); + let pm = info.get_node(u)?.actix.addr.clone(); + let resp = pm.send(PeerManagerMessageRequest::FetchRoutingTable.with_span_context()).await?; + let rt = match resp { + PeerManagerMessageResponse::FetchRoutingTable(rt) => rt, + _ => bail!("bad response"), + }; + if expected_routing_tables(&rt.next_hops, &want_rt) { + return Ok(ControlFlow::Break(())); + } + Ok(ControlFlow::Continue(())) +} + +impl StateMachine { + fn new() -> Self { + Self { actions: vec![] } + } + + pub fn push_action(&mut self, action: ActionFn) { + self.actions.push(action); + } + + pub fn push(&mut self, action: Action) { + let num_prev_actions = self.actions.len(); + let action_clone = 0; // action.clone(); + match action { + Action::AddEdge { from, to, force } => { + self.actions.push(Box::new(move |info: &mut RunningInfo| Box::pin(async move { + debug!(target: "test", num_prev_actions, action = ?action_clone, "runner.rs: Action"); + let pm = info.get_node(from)?.actix.addr.clone(); + let peer_info = info.runner.test_config[to].peer_info(); + match tcp::Stream::connect(&peer_info, tcp::Tier::T2).await { + Ok(stream) => { pm.send(PeerManagerMessageRequest::OutboundTcpConnect(stream).with_span_context()).await?; }, + Err(err) => tracing::debug!("tcp::Stream::connect({peer_info}): {err}"), + } + if !force { + return Ok(ControlFlow::Break(())) + } + let peer_id = peer_info.id.clone(); + let res = pm.send(GetInfo{}.with_span_context()).await?; + for peer in &res.connected_peers { + if peer.full_peer_info.peer_info.id==peer_id { + return Ok(ControlFlow::Break(())) + } + } + Ok(ControlFlow::Continue(())) + }))); + } + Action::CheckRoutingTable(u, expected) => { + self.actions.push(Box::new(move |info| { + Box::pin(check_routing_table(info, u, expected.clone())) + })); + } + Action::Stop(source) => { + self.actions.push(Box::new(move |info: &mut RunningInfo| Box::pin(async move { + debug!(target: "test", num_prev_actions, action = ?action_clone, "runner.rs: Action"); + info.stop_node(source); + Ok(ControlFlow::Break(())) + }))); + } + Action::Wait(t) => { + self.actions.push(Box::new(move |_info: &mut RunningInfo| Box::pin(async move { + debug!(target: "test", num_prev_actions, action = ?action_clone, "runner.rs: Action"); + tokio::time::sleep(t.try_into().unwrap()).await; + Ok(ControlFlow::Break(())) + }))); + } + } + } +} + +struct TestConfig { + max_num_peers: u32, + routed_message_ttl: u8, + boot_nodes: Vec, + blacklist: HashSet>, + whitelist: HashSet, + outbound_disabled: bool, + ban_window: time::Duration, + ideal_connections: Option<(u32, u32)>, + minimum_outbound_peers: Option, + safe_set_size: Option, + archive: bool, + + account_id: AccountId, + node_addr: tcp::ListenerAddr, +} + +impl TestConfig { + fn new(id: usize) -> Self { + Self { + max_num_peers: 100, + routed_message_ttl: ROUTED_MESSAGE_TTL, + boot_nodes: vec![], + blacklist: HashSet::new(), + whitelist: HashSet::new(), + outbound_disabled: true, + ban_window: time::Duration::seconds(1), + ideal_connections: None, + minimum_outbound_peers: None, + safe_set_size: None, + archive: false, + + account_id: format!("test{}", id).parse().unwrap(), + node_addr: tcp::ListenerAddr::reserve_for_test(), + } + } + + fn addr(&self) -> SocketAddr { + *self.node_addr + } + + fn peer_id(&self) -> PeerId { + peer_id_from_seed(self.account_id.as_ref()) + } + + fn peer_info(&self) -> PeerInfo { + PeerInfo::new(self.peer_id(), self.addr()) + } +} + +pub(crate) struct Runner { + test_config: Vec, + state_machine: StateMachine, + validators: Vec, + chain_genesis: ChainGenesis, +} + +struct NodeHandle { + actix: ActixSystem, +} + +impl Runner { + pub fn new(num_nodes: usize, num_validators: usize) -> Self { + let test_config: Vec<_> = (0..num_nodes).map(TestConfig::new).collect(); + let validators = + test_config[0..num_validators].iter().map(|c| c.account_id.clone()).collect(); + Self { + test_config, + validators, + state_machine: StateMachine::new(), + chain_genesis: ChainGenesis::test(), + } + } + + /// Add node `v` to the whitelist of node `u`. + /// If passed `v` an entry of the following form is added to the whitelist: + /// PEER_ID_OF_NODE_V@localhost:PORT_OF_NODE_V + pub fn add_to_whitelist(mut self, u: usize, v: usize) -> Self { + self.test_config[u].whitelist.insert(v); + self + } + + /// Specify boot nodes. By default there are no boot nodes. + pub fn use_boot_nodes(mut self, boot_nodes: Vec) -> Self { + self.apply_all(move |test_config| { + test_config.boot_nodes = boot_nodes.clone(); + }); + self + } + + pub fn ideal_connections( + mut self, + ideal_connections_lo: u32, + ideal_connections_hi: u32, + ) -> Self { + self.apply_all(move |test_config| { + test_config.ideal_connections = Some((ideal_connections_lo, ideal_connections_hi)); + }); + self + } + + pub fn minimum_outbound_peers(mut self, minimum_outbound_peers: u32) -> Self { + self.apply_all(move |test_config| { + test_config.minimum_outbound_peers = Some(minimum_outbound_peers); + }); + self + } + + pub fn safe_set_size(mut self, safe_set_size: u32) -> Self { + self.apply_all(move |test_config| { + test_config.safe_set_size = Some(safe_set_size); + }); + self + } + + pub fn max_num_peers(mut self, max_num_peers: u32) -> Self { + self.apply_all(move |test_config| { + test_config.max_num_peers = max_num_peers; + }); + self + } + + /// Allow message to connect among themselves without triggering new connections. + pub fn enable_outbound(mut self) -> Self { + self.apply_all(|test_config| { + test_config.outbound_disabled = false; + }); + self + } + + /// Add an action to be executed by the Runner. Actions are executed sequentially. + /// Each action is executed after the previous action succeed. + pub fn push(&mut self, action: Action) { + self.state_machine.push(action); + } + + /// Add an action to be executed by the Runner. Actions are executed sequentially. + /// Each action is executed after the previous action succeed. + pub fn push_action(&mut self, action: ActionFn) { + self.state_machine.push_action(action); + } + + fn apply_all(&mut self, mut apply: F) + where + F: FnMut(&mut TestConfig) -> (), + { + for test_config in self.test_config.iter_mut() { + apply(test_config); + } + } + + async fn setup_node(&self, node_id: usize) -> anyhow::Result { + tracing::debug!("starting {node_id}"); + let config = &self.test_config[node_id]; + + let boot_nodes = + config.boot_nodes.iter().map(|ix| self.test_config[*ix].peer_info()).collect(); + let blacklist: blacklist::Blacklist = config + .blacklist + .iter() + .map(|x| match x { + Some(x) => blacklist::Entry::from_addr(self.test_config[*x].addr()), + None => blacklist::Entry::from_ip(Ipv6Addr::LOCALHOST.into()), + }) + .collect(); + let whitelist = + config.whitelist.iter().map(|ix| self.test_config[*ix].peer_info()).collect(); + + let mut network_config = + config::NetworkConfig::from_seed(config.account_id.as_ref(), config.node_addr); + network_config.peer_store.ban_window = config.ban_window; + network_config.max_num_peers = config.max_num_peers; + network_config.ttl_account_id_router = time::Duration::seconds(5); + network_config.routed_message_ttl = config.routed_message_ttl; + network_config.peer_store.blacklist = blacklist; + network_config.whitelist_nodes = whitelist; + network_config.outbound_disabled = config.outbound_disabled; + network_config.peer_store.boot_nodes = boot_nodes; + network_config.archive = config.archive; + + config.ideal_connections.map(|(lo, hi)| { + network_config.ideal_connections_lo = lo; + network_config.ideal_connections_hi = hi; + }); + config.safe_set_size.map(|sss| { + network_config.safe_set_size = sss; + }); + config.minimum_outbound_peers.map(|mop| { + network_config.minimum_outbound_peers = mop; + }); + + let account_id = config.account_id.clone(); + let validators = self.validators.clone(); + let chain_genesis = self.chain_genesis.clone(); + + Ok(NodeHandle { + actix: ActixSystem::spawn(|| { + setup_network_node(account_id, validators, chain_genesis, network_config) + }) + .await, + }) + } + + async fn build(self) -> anyhow::Result { + let mut nodes = vec![]; + for node_id in 0..self.test_config.len() { + nodes.push(Some(self.setup_node(node_id).await?)); + } + Ok(RunningInfo { runner: self, nodes }) + } +} + +/// Executes the test. +/// It will fail if it doesn't solve all actions. +/// start_test will block until test is complete. +pub(crate) fn start_test(runner: Runner) -> anyhow::Result<()> { + init_test_logger(); + let r = tokio::runtime::Builder::new_current_thread().enable_all().build()?; + r.block_on(async { + let mut info = runner.build().await?; + let actions = std::mem::take(&mut info.runner.state_machine.actions); + let actions_count = actions.len(); + + let timeout = tokio::time::Duration::from_secs(15); + let step = tokio::time::Duration::from_millis(10); + let start = tokio::time::Instant::now(); + for (i, a) in actions.into_iter().enumerate() { + tracing::debug!(target: "test", "[starting action {i}]"); + loop { + let done = + tokio::time::timeout_at(start + timeout, a(&mut info)).await.with_context( + || format!("timeout while executing action {i}/{actions_count}"), + )??; + match done { + ControlFlow::Break(()) => break, + ControlFlow::Continue(()) => {} + } + tokio::time::sleep(step).await; + } + } + // Stop the running nodes. + for i in 0..info.nodes.len() { + info.stop_node(i); + } + Ok(()) + }) +} + +impl RunningInfo { + fn get_node(&self, node_id: usize) -> anyhow::Result<&NodeHandle> { + self.nodes[node_id].as_ref().ok_or(anyhow!("node is down")) + } + fn stop_node(&mut self, node_id: usize) { + tracing::debug!("stopping {node_id}"); + self.nodes[node_id].take(); + } + + async fn start_node(&mut self, node_id: usize) -> anyhow::Result<()> { + self.stop_node(node_id); + self.nodes[node_id] = Some(self.runner.setup_node(node_id).await?); + Ok(()) + } + fn change_account_id(&mut self, node_id: usize, account_id: AccountId) { + self.runner.test_config[node_id].account_id = account_id; + } +} + +pub(crate) fn assert_expected_peers(node_id: usize, peers: Vec) -> ActionFn { + Box::new(move |info: &mut RunningInfo| { + let peers = peers.clone(); + Box::pin(async move { + let pm = &info.get_node(node_id)?.actix.addr; + let network_info = pm.send(GetInfo {}.with_span_context()).await?; + let got: HashSet<_> = network_info + .connected_peers + .into_iter() + .map(|i| i.full_peer_info.peer_info.id) + .collect(); + let want: HashSet<_> = + peers.iter().map(|i| info.runner.test_config[*i].peer_id()).collect(); + if got != want { + bail!("node {node_id} has peers {got:?}, want {want:?}"); + } + return Ok(ControlFlow::Break(())); + }) + }) +} + +/// Check that the number of connections of `node_id` is in the range: +/// [expected_connections_lo, expected_connections_hi] +/// Use None to denote semi-open interval +pub(crate) fn check_expected_connections( + node_id: usize, + expected_connections_lo: Option, + expected_connections_hi: Option, +) -> ActionFn { + Box::new(move |info: &mut RunningInfo| { + Box::pin(async move { + debug!(target: "test", node_id, expected_connections_lo, ?expected_connections_hi, "runner.rs: check_expected_connections"); + let pm = &info.get_node(node_id)?.actix.addr; + let res = pm.send(GetInfo {}.with_span_context()).await?; + if expected_connections_lo.map_or(false, |l| l > res.num_connected_peers) { + return Ok(ControlFlow::Continue(())); + } + if expected_connections_hi.map_or(false, |h| h < res.num_connected_peers) { + return Ok(ControlFlow::Continue(())); + } + Ok(ControlFlow::Break(())) + }) + }) +} + +/// Restart a node that was already stopped. +pub(crate) fn restart(node_id: usize) -> ActionFn { + Box::new(move |info: &mut RunningInfo| { + Box::pin(async move { + debug!(target: "test", ?node_id, "runner.rs: restart"); + info.start_node(node_id).await?; + Ok(ControlFlow::Break(())) + }) + }) +} + +/// Change account id from a stopped peer. Notice this will also change its peer id, since +/// peer_id is derived from account id with NetworkConfig::from_seed +pub(crate) fn change_account_id(node_id: usize, account_id: AccountId) -> ActionFn { + Box::new(move |info: &mut RunningInfo| { + let account_id = account_id.clone(); + Box::pin(async move { + info.change_account_id(node_id, account_id); + Ok(ControlFlow::Break(())) + }) + }) +} diff --git a/integration-tests/src/tests/network/stress_network.rs b/integration-tests/src/tests/network/stress_network.rs new file mode 100644 index 000000000..e0e83a6df --- /dev/null +++ b/integration-tests/src/tests/network/stress_network.rs @@ -0,0 +1,149 @@ +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::Arc; +use std::time::Duration; + +use actix::{Actor, AsyncContext, System}; +use futures::FutureExt; +use unc_async::messaging::Sender; +use tracing::info; + +use unc_actix_test_utils::run_actix; +use unc_async::time; +use unc_network::tcp; +use unc_o11y::testonly::init_test_logger_allow_panic; +use unc_primitives::block::GenesisId; + +use unc_network::config; +use unc_network::test_utils::{convert_boot_nodes, GetInfo, StopSignal, WaitOrTimeoutActor}; +use unc_network::PeerManagerActor; +use unc_o11y::WithSpanContextExt; + +fn make_peer_manager( + seed: &str, + addr: tcp::ListenerAddr, + boot_nodes: Vec<(&str, std::net::SocketAddr)>, +) -> actix::Addr { + let mut config = config::NetworkConfig::from_seed(seed, addr); + config.peer_store.boot_nodes = convert_boot_nodes(boot_nodes); + PeerManagerActor::spawn( + time::Clock::real(), + unc_store::db::TestDB::new(), + config, + Arc::new(unc_network::client::Noop), + Sender::noop(), + GenesisId::default(), + ) + .unwrap() +} + +/// This test spawns several (7) nodes but node 0 crash very frequently and restart. +/// Other nodes should not panic because node 0 behavior. +/// +/// If everything goes well this test should panic after the timeout triggered by WaitOrTimeout. +/// The test is stopped gracefully (no panic) if some node other than node0 panicked. +/// +/// This was fixed in (#1954). To reproduce this bug: +/// ``` +/// git checkout 1f5eab0344235960dfcf767d143fb90a02c7c567 +/// cargo test --package unc-network --test stress_network stress_test -- --exact --ignored +/// ``` +/// +/// Logs observed on failing commit: +/// ``` +/// thread 'stress_test' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 107, kind: NotConnected, message: "Transport endpoint is not connected" }', src/libcore/result.rs:1165:5 +/// thread 'stress_test' panicked at 'Decoder error: Os { code: 104, kind: ConnectionReset, message: "Connection reset by peer" }', src/libcore/result.rs:1165:5 +/// ``` +#[test] +#[should_panic] +#[ignore] +fn stress_test() { + init_test_logger_allow_panic(); + + run_actix(async { + let num_nodes = 7; + let addrs: Vec<_> = (0..num_nodes).map(|_| tcp::ListenerAddr::reserve_for_test()).collect(); + + let boot_nodes: Vec<_> = + addrs.iter().enumerate().map(|(ix, addr)| (format!("test{}", ix), **addr)).collect(); + + let mut pms: Vec<_> = (0..num_nodes) + .map(|ix| { + Arc::new(make_peer_manager( + format!("test{}", ix).as_str(), + addrs[ix], + boot_nodes.iter().map(|(acc, addr)| (acc.as_str(), *addr)).collect(), + )) + }) + .collect(); + + pms[0].do_send(StopSignal::should_panic().with_span_context()); + + // States: + // 0 -> Check other nodes health. + // 1 -> Spawn node0 and schedule crash. + // 2 -> Timeout. + let state = Arc::new(AtomicUsize::new(0)); + let flags: Vec<_> = (0..num_nodes).map(|_| Arc::new(AtomicBool::new(false))).collect(); + let round = Arc::new(AtomicUsize::new(0)); + + WaitOrTimeoutActor::new( + Box::new(move |ctx| { + let s = state.load(Ordering::Relaxed); + if s == 0 { + info!(target: "test", "Start round: {}", round.fetch_add(1, Ordering::Relaxed)); + + for (ix, flag) in flags.iter().enumerate().skip(1) { + if !flag.load(Ordering::Relaxed) { + let flag1 = flag.clone(); + + let actor = pms[ix].send(GetInfo {}.with_span_context()); + let actor = actor.then(move |info| { + if let Ok(info) = info { + if info.num_connected_peers == num_nodes - 2 { + flag1.store(true, Ordering::Relaxed); + } + } else { + info!(target: "test", "Node {} have failed", ix); + System::current().stop(); + } + + futures::future::ready(()) + }); + actix::spawn(actor); + } + } + + if flags.iter().skip(1).all(|flag| flag.load(Ordering::Relaxed)) { + state.store(1, Ordering::Relaxed); + } + } else if s == 1 { + state.store(2, Ordering::Relaxed); + + for flag in flags.iter() { + flag.store(false, Ordering::Relaxed); + } + + pms[0] = Arc::new(make_peer_manager( + "test0", + addrs[0], + boot_nodes.iter().map(|(acc, addr)| (acc.as_str(), *addr)).collect(), + )); + + let pm0 = pms[0].clone(); + + ctx.run_later(Duration::from_millis(10), move |_, _| { + pm0.do_send(StopSignal::should_panic().with_span_context()); + }); + + let state1 = state.clone(); + ctx.run_later(Duration::from_millis(100), move |_, _| { + state1.store(0, Ordering::Relaxed); + }); + } + }), + 100, + 10000, + ) + .start(); + }); +} diff --git a/integration-tests/src/tests/runtime/deployment.rs b/integration-tests/src/tests/runtime/deployment.rs new file mode 100644 index 000000000..5c2d235f4 --- /dev/null +++ b/integration-tests/src/tests/runtime/deployment.rs @@ -0,0 +1,75 @@ +use crate::node::{Node, RuntimeNode}; +use unc_chain_configs::Genesis; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::transaction::{Action, DeployContractAction, SignedTransaction}; +use unc_primitives::types::AccountId; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_primitives::views::FinalExecutionStatus; +use framework::config::GenesisExt; + +const ONE_NEAR: u128 = 10u128.pow(24); + +/// Tests if the maximum allowed contract can be deployed with current gas limits +#[test] +fn test_deploy_max_size_contract() { + let account_id: AccountId = "alice.near".parse().unwrap(); + let test_contract_id: AccountId = "test_contract.alice.near".parse().unwrap(); + let runtime_config_store = RuntimeConfigStore::new(None); + let config = runtime_config_store.get_config(PROTOCOL_VERSION); + + let genesis = Genesis::test(vec![account_id.clone()], 1); + let node = + RuntimeNode::new_from_genesis_and_config(&account_id, genesis, config.as_ref().clone()); + let node_user = node.user(); + + // Compute size of a deployment transaction with an almost empty contract payload + let block_hash = node_user.get_best_block_hash().unwrap_or_default(); + let signed_transaction = SignedTransaction::from_actions( + node_user.get_access_key_nonce_for_signer(&account_id).unwrap_or_default() + 1, + test_contract_id.clone(), + test_contract_id.clone(), + &*node_user.signer(), + vec![Action::DeployContract(DeployContractAction { code: vec![0u8] })], + block_hash, + ); + let tx_overhead = signed_transaction.get_size(); + + // Testable max contract size is limited by both `max_contract_size` and by `max_transaction_size` + let max_contract_size = config.wasm_config.limit_config.max_contract_size; + let max_transaction_size = config.wasm_config.limit_config.max_transaction_size; + let contract_size = max_contract_size.min(max_transaction_size - tx_overhead); + // Enough token to store contract + 1 NEAR for account + let token_balance = config.storage_amount_per_byte() * contract_size as u128 + ONE_NEAR; + + // Create test account + let transaction_result = node_user + .create_account( + account_id, + test_contract_id.clone(), + node.signer().public_key(), + token_balance, + ) + .unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + + // Deploy contract + let wasm_binary = unc_test_contracts::sized_contract(contract_size as usize); + // Run code through preparation for validation. (Deploying will succeed either way). + unc_vm_runner::prepare::prepare_contract( + &wasm_binary, + &config.wasm_config, + config.wasm_config.vm_kind, + ) + .unwrap(); + let transaction_result = + node_user.deploy_contract(test_contract_id, wasm_binary.to_vec()).unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + + // Check total TX gas is in limit + let tx_conversion_gas_burnt = transaction_result.transaction_outcome.outcome.gas_burnt; + let deployment_gas_burnt = transaction_result.receipts_outcome[0].outcome.gas_burnt; + let total_gas_burnt = tx_conversion_gas_burnt + deployment_gas_burnt; + assert!(total_gas_burnt <= config.wasm_config.limit_config.max_gas_burnt,); +} diff --git a/integration-tests/src/tests/runtime/mod.rs b/integration-tests/src/tests/runtime/mod.rs new file mode 100644 index 000000000..219fbb997 --- /dev/null +++ b/integration-tests/src/tests/runtime/mod.rs @@ -0,0 +1,4 @@ +mod deployment; +mod sanity_checks; +mod state_viewer; +mod test_evil_contracts; diff --git a/integration-tests/src/tests/runtime/sanity_checks.rs b/integration-tests/src/tests/runtime/sanity_checks.rs new file mode 100644 index 000000000..31331b98f --- /dev/null +++ b/integration-tests/src/tests/runtime/sanity_checks.rs @@ -0,0 +1,345 @@ +use crate::node::{Node, RuntimeNode}; +use unc_chain_configs::Genesis; +use unc_parameters::{ExtCosts, RuntimeConfig, RuntimeConfigStore}; +use unc_primitives::serialize::to_base64; +use unc_primitives::types::AccountId; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_primitives::views::{ + CostGasUsed, ExecutionOutcomeWithIdView, ExecutionStatusView, FinalExecutionStatus, +}; +use framework::config::GenesisExt; +use std::collections::HashSet; +use std::mem::size_of; +use testlib::runtime_utils::{add_test_contract, alice_account, bob_account}; + +/// Initial balance used in tests. +const TESTING_INIT_BALANCE: u128 = 1_000_000_000 * UNC_BASE; + +/// One NEAR, divisible by 10^24. +const UNC_BASE: u128 = 1_000_000_000_000_000_000_000_000; + +/// Max prepaid amount of gas. +const MAX_GAS: u64 = 300_000_000_000_000; + +/// Costs whose amount of `gas_used` may depend on environmental factors. +/// +/// In particular, compiling our test contract with different versions of +/// compiler can lead to slightly different WASM output. +const NONDETERMINISTIC_COSTS: [&str; 2] = ["CONTRACT_LOADING_BYTES", "WASM_INSTRUCTION"]; + +fn is_nondeterministic_cost(cost: &str) -> bool { + NONDETERMINISTIC_COSTS.iter().find(|&&ndt_cost| ndt_cost == cost).is_some() +} + +fn test_contract_account() -> AccountId { + format!("test-contract.{}", alice_account().as_str()).parse().unwrap() +} + +fn setup_runtime_node_with_contract(wasm_binary: &[u8]) -> RuntimeNode { + // Create a `RuntimeNode`. Load `RuntimeConfig` from `RuntimeConfigStore` + // to ensure we are using the latest configuration. + let mut genesis = + Genesis::test(vec![alice_account(), bob_account(), "carol.near".parse().unwrap()], 3); + add_test_contract(&mut genesis, &alice_account()); + add_test_contract(&mut genesis, &bob_account()); + let runtime_config_store = RuntimeConfigStore::new(None); + let runtime_config = runtime_config_store.get_config(PROTOCOL_VERSION); + let node = RuntimeNode::new_from_genesis_and_config( + &alice_account(), + genesis, + RuntimeConfig::clone(runtime_config), + ); + + let account_id = node.account_id().unwrap(); + let node_user = node.user(); + let tx_result = node_user + .create_account( + account_id, + test_contract_account(), + node.signer().public_key(), + TESTING_INIT_BALANCE / 2, + ) + .unwrap(); + assert_eq!(tx_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(tx_result.receipts_outcome.len(), 2); + + let tx_result = + node_user.deploy_contract(test_contract_account(), wasm_binary.to_vec()).unwrap(); + assert_eq!(tx_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(tx_result.receipts_outcome.len(), 1); + + node +} + +fn get_receipts_status_with_clear_hash( + outcomes: &[ExecutionOutcomeWithIdView], +) -> Vec { + outcomes + .iter() + .map(|outcome| { + match outcome.outcome.status { + ExecutionStatusView::SuccessReceiptId(_) => { + // We don’t control the hash of the receipt so clear it. + ExecutionStatusView::SuccessReceiptId(Default::default()) + } + ref status => status.clone(), + } + }) + .collect::>() +} + +/// Calls method `sanity_check` on `test-contract-rs` and verifies that the +/// resulting gas profile matches expectations. +/// +/// This test intends to catch accidental configuration changes, see #4961. +#[test] +fn test_cost_sanity() { + let test_contract = if cfg!(feature = "nightly") { + unc_test_contracts::nightly_rs_contract() + } else { + unc_test_contracts::rs_contract() + }; + let node = setup_runtime_node_with_contract(test_contract); + + let args = format!( + r#"{{ + "contract_code": {:?}, + "method_name": "main", + "method_args": "", + "validator_id": {:?} + }}"#, + to_base64(unc_test_contracts::trivial_contract()), + bob_account().as_str() + ); + eprintln!("{args}"); + + let res = node + .user() + .function_call( + alice_account(), + test_contract_account(), + "sanity_check", + args.into_bytes(), + MAX_GAS, + 0, + ) + .unwrap(); + assert_eq!(res.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(res.transaction_outcome.outcome.metadata.gas_profile, None); + + let receipts_status = get_receipts_status_with_clear_hash(&res.receipts_outcome); + insta::assert_yaml_snapshot!("receipts_status", receipts_status); + + let receipts_gas_profile = res + .receipts_outcome + .iter() + .map(|outcome| outcome.outcome.metadata.gas_profile.as_ref().unwrap()) + .map(|gas_profile| { + gas_profile + .iter() + .cloned() + .map(|cost| { + if is_nondeterministic_cost(&cost.cost) { + // Ignore `gas_used` of nondeterministic costs. + CostGasUsed { gas_used: 0, ..cost } + } else { + cost + } + }) + .collect::>() + }) + .collect::>(); + + insta::assert_debug_snapshot!( + if cfg!(feature = "nightly") { + "receipts_gas_profile_nightly" + } else { + "receipts_gas_profile" + }, + receipts_gas_profile + ); +} + +/// Verifies the sanity of nondeterministic costs using a trivial contract. +/// +/// Some costs are nondeterministic since they depend on environmental factors, +/// see [`NONDETERMINISTIC_COSTS`]. For a trivial contract, however, there are +/// no such differences expected. +#[test] +fn test_cost_sanity_nondeterministic() { + let contract = unc_test_contracts::wat_contract( + r#"(module (func (export "main") (i32.const 92) (drop)))"#, + ); + let node = setup_runtime_node_with_contract(&contract); + let res = node + .user() + .function_call(alice_account(), test_contract_account(), "main", vec![], MAX_GAS, 0) + .unwrap(); + assert_eq!(res.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(res.transaction_outcome.outcome.metadata.gas_profile, None); + + let receipts_status = get_receipts_status_with_clear_hash(&res.receipts_outcome); + insta::assert_yaml_snapshot!("receipts_status_nondeterministic", receipts_status); + + let receipts_gas_profile = res + .receipts_outcome + .iter() + .map(|outcome| outcome.outcome.metadata.gas_profile.as_ref().unwrap()) + .collect::>(); + + insta::assert_debug_snapshot!( + if cfg!(feature = "nightly") { + "receipts_gas_profile_nondeterministic_nightly" + } else { + "receipts_gas_profile_nondeterministic" + }, + receipts_gas_profile + ); + + // Verify that all nondeterministic costs are covered. + let all_costs = { + let all_costs = receipts_gas_profile + .iter() + .flat_map(|gas_profile| gas_profile.iter().map(|cost| cost.cost.as_str())); + HashSet::from_iter(all_costs) + }; + let ndt_costs = HashSet::from(NONDETERMINISTIC_COSTS); + let missing_costs = ndt_costs.difference(&all_costs).collect::>(); + assert!( + missing_costs.is_empty(), + "some nondeterministic costs are not covered: {:?}", + missing_costs, + ); +} + +/// Verifies the operation of host function `used_gas` according to +/// [gas instrumentation]. +/// +/// [gas instrumentation]: https://nomicon.io/RuntimeSpec/Preparation#gas-instrumentation +#[test] +fn test_sanity_used_gas() { + use unc_parameters::vm::ContractPrepareVersion; + let node = setup_runtime_node_with_contract(&contract_sanity_check_used_gas()); + let res = node + .user() + .function_call(alice_account(), test_contract_account(), "main", vec![], MAX_GAS, 0) + .unwrap(); + + let num_return_values = 4; + let returned_bytes = match res.status { + FinalExecutionStatus::SuccessValue(v) => v, + _ => panic!("Unexpected status: {:?}", res.status), + }; + assert_eq!(returned_bytes.len(), num_return_values * size_of::()); + + let used_gas = stdx::as_chunks_exact::<{ size_of::() }, _>(&returned_bytes) + .unwrap() + .iter() + .map(|bytes| u64::from_le_bytes(*bytes)) + .collect::>(); + + let runtime_config = node.client.read().unwrap().runtime_config.clone(); + let base_cost = runtime_config.wasm_config.ext_costs.gas_cost(ExtCosts::base); + let op_cost = match runtime_config.wasm_config.limit_config.contract_prepare_version { + // In old implementations of preparation, all of the contained instructions are paid + // for upfront when entering a new metered block, + // + // It fails to be precise in that it does not consider calls to `used_gas` as + // breaking up a metered block and so none of the gas cost to execute instructions between + // calls to this function will be observable from within wasm code. + // + // In this test we account for this by setting `op_cost` to zero, but if future tests + // change test WASM in significant ways, this approach may become incorrect. + ContractPrepareVersion::V0 | ContractPrepareVersion::V1 => 0, + // Gas accounting is precise and instructions executed between calls to the side-effectful + // `used_gas` host function calls will be observbable. + ContractPrepareVersion::V2 => u64::from(runtime_config.wasm_config.regular_op_cost), + }; + + // Executing `used_gas` costs `base_cost` plus an instruction to execute the `call` itself. + // When executing `used_gas` twice within a metered block, the returned values should differ by + // that amount. + assert_eq!(used_gas[1] - used_gas[0], base_cost + op_cost); + // Between these two observations additional arithmetics have been executed. + assert_eq!(used_gas[2] - used_gas[1], base_cost + 8 * op_cost); + assert!(used_gas[3] - used_gas[2] > base_cost); +} + +/// Returns a contract which calls host function `used_gas` multiple times, both +/// within the same [metered block] and in different metered blocks. The value +/// returned by the contract is an array of values returned by calls of +/// `used_gas`. +/// +/// This contract is written in `wat` to avoid depending on the output generated +/// by a compiler (e.g. `rustc`). +/// +/// [metered block]: https://nomicon.io/RuntimeSpec/Preparation#gas-instrumentation +fn contract_sanity_check_used_gas() -> Vec { + wat::parse_str( + r#" + (module + (type $t0 (func (result i64))) + (type $t1 (func (param i64 i64))) + (type $t2 (func)) + + (import "env" "used_gas" (func $env.used_gas (type $t0))) + (import "env" "value_return" (func $env.value_return (type $t1))) + + (memory 1) + + (func $main (export "main") (type $t2) + (local $used_0 i64) (local $used_1 i64) (local $used_2 i64) (local $used_3 i64) + + ;; Call used_gas twice in metered block, without instructions in between. + (call $env.used_gas) + (call $env.used_gas) + (local.set $used_1) + (local.set $used_0) + + ;; In the same metered block, call used_gas again after executing other + ;; instructions. + (i64.add + (local.get $used_0) + (i64.const 1)) + drop + nop + + (call $env.used_gas) + (local.set $used_2) + + ;; Push a new metered block on the stack via br_if. The condition is false, + ;; so we will _not_ branch out of the block. + (block $b0 + (br_if $b0 + (i64.eq + (local.get $used_0) + (i64.const 0))) + (local.set $used_3 + call $env.used_gas) + nop ;; ensure there is more than used_gas to pay for in this block + ) + + ;; Prepare bytes passed to value_return. + (i64.store + (i32.const 0) + (local.get $used_0)) + (i64.store + (i32.const 8) + (local.get $used_1)) + (i64.store + (i32.const 16) + (local.get $used_2)) + (i64.store + (i32.const 24) + (local.get $used_3)) + + (call $env.value_return + (i64.const 32) + (i64.extend_i32_u + (i32.const 0))) + ) + ) + "#, + ) + .unwrap() +} diff --git a/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile.snap b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile.snap new file mode 100644 index 000000000..8cfca701a --- /dev/null +++ b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile.snap @@ -0,0 +1,500 @@ +--- +source: integration-tests/src/tests/runtime/sanity_checks.rs +expression: receipts_gas_profile +--- +[ + [ + CostGasUsed { + cost_category: "ACTION_COST", + cost: "ADD_FULL_ACCESS_KEY", + gas_used: 101765125000, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "ADD_FUNCTION_CALL_KEY_BASE", + gas_used: 102217625000, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "ADD_FUNCTION_CALL_KEY_BYTE", + gas_used: 9626655, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "CREATE_ACCOUNT", + gas_used: 7700000000000, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "DELETE_ACCOUNT", + gas_used: 147489000000, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "DELETE_KEY", + gas_used: 94946625000, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "DEPLOY_CONTRACT_BASE", + gas_used: 184765750000, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "DEPLOY_CONTRACT_BYTE", + gas_used: 231641966, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "FUNCTION_CALL_BASE", + gas_used: 20878753500000, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "FUNCTION_CALL_BYTE", + gas_used: 207941862, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "NEW_ACTION_RECEIPT", + gas_used: 1480548358496, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "STAKE", + gas_used: 141715687500, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "TRANSFER", + gas_used: 230246125000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "ALT_BN128_G1_MULTIEXP_BASE", + gas_used: 713000000000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "ALT_BN128_G1_MULTIEXP_ELEMENT", + gas_used: 320000000000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "ALT_BN128_G1_SUM_BASE", + gas_used: 3000000000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "ALT_BN128_G1_SUM_ELEMENT", + gas_used: 5000000000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "ALT_BN128_PAIRING_CHECK_BASE", + gas_used: 9686000000000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "ALT_BN128_PAIRING_CHECK_ELEMENT", + gas_used: 5102000000000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "BASE", + gas_used: 17209927215, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "LOG_BASE", + gas_used: 7086626100, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "LOG_BYTE", + gas_used: 131987910, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "PROMISE_AND_BASE", + gas_used: 1465013400, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "PROMISE_AND_PER_PROMISE", + gas_used: 87234816, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "PROMISE_RETURN", + gas_used: 560152386, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "READ_CACHED_TRIE_NODE", + gas_used: 4560000000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "READ_MEMORY_BASE", + gas_used: 182690424000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "READ_MEMORY_BYTE", + gas_used: 4744063584, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "READ_REGISTER_BASE", + gas_used: 7551495558, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "READ_REGISTER_BYTE", + gas_used: 25034748, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "RIPEMD160_BASE", + gas_used: 853675086, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "RIPEMD160_BLOCK", + gas_used: 680107584, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "SHA256_BASE", + gas_used: 4540970250, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "SHA256_BYTE", + gas_used: 120586755, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_HAS_KEY_BASE", + gas_used: 108079793250, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_HAS_KEY_BYTE", + gas_used: 277117605, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_READ_BASE", + gas_used: 112713691500, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_READ_KEY_BYTE", + gas_used: 278572797, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_READ_VALUE_BYTE", + gas_used: 28055025, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_REMOVE_BASE", + gas_used: 106946061000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_REMOVE_KEY_BYTE", + gas_used: 343983456, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_REMOVE_RET_VALUE_BYTE", + gas_used: 57657780, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_WRITE_BASE", + gas_used: 128393472000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_WRITE_EVICTED_BYTE", + gas_used: 160586535, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_WRITE_KEY_BYTE", + gas_used: 281931468, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_WRITE_VALUE_BYTE", + gas_used: 310185390, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "TOUCHING_TRIE_NODE", + gas_used: 32203911852, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "UTF16_DECODING_BASE", + gas_used: 3543313050, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "UTF16_DECODING_BYTE", + gas_used: 1635774930, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "UTF8_DECODING_BASE", + gas_used: 46676685915, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "UTF8_DECODING_BYTE", + gas_used: 98262621423, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "VALIDATOR_STAKE_BASE", + gas_used: 911834726400, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "VALIDATOR_TOTAL_STAKE_BASE", + gas_used: 911834726400, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WRITE_MEMORY_BASE", + gas_used: 19626564027, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WRITE_MEMORY_BYTE", + gas_used: 866159496, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WRITE_REGISTER_BASE", + gas_used: 37251792318, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WRITE_REGISTER_BYTE", + gas_used: 1904583564, + }, + ], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "BASE", + gas_used: 264768111, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + ], + [], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "BASE", + gas_used: 264768111, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "READ_MEMORY_BASE", + gas_used: 2609863200, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "READ_MEMORY_BYTE", + gas_used: 11403999, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "UTF8_DECODING_BASE", + gas_used: 3111779061, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "UTF8_DECODING_BYTE", + gas_used: 874741437, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + ], + [], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + ], + [], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + ], + [], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + ], + [], + [], + [], + [], + [], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 70891926, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + ], + [], + [], + [], + [], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + ], + [], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "BASE", + gas_used: 529536222, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WRITE_REGISTER_BASE", + gas_used: 2865522486, + }, + ], + [], + [], +] diff --git a/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nightly.snap b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nightly.snap new file mode 100644 index 000000000..8cfca701a --- /dev/null +++ b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nightly.snap @@ -0,0 +1,500 @@ +--- +source: integration-tests/src/tests/runtime/sanity_checks.rs +expression: receipts_gas_profile +--- +[ + [ + CostGasUsed { + cost_category: "ACTION_COST", + cost: "ADD_FULL_ACCESS_KEY", + gas_used: 101765125000, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "ADD_FUNCTION_CALL_KEY_BASE", + gas_used: 102217625000, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "ADD_FUNCTION_CALL_KEY_BYTE", + gas_used: 9626655, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "CREATE_ACCOUNT", + gas_used: 7700000000000, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "DELETE_ACCOUNT", + gas_used: 147489000000, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "DELETE_KEY", + gas_used: 94946625000, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "DEPLOY_CONTRACT_BASE", + gas_used: 184765750000, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "DEPLOY_CONTRACT_BYTE", + gas_used: 231641966, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "FUNCTION_CALL_BASE", + gas_used: 20878753500000, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "FUNCTION_CALL_BYTE", + gas_used: 207941862, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "NEW_ACTION_RECEIPT", + gas_used: 1480548358496, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "STAKE", + gas_used: 141715687500, + }, + CostGasUsed { + cost_category: "ACTION_COST", + cost: "TRANSFER", + gas_used: 230246125000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "ALT_BN128_G1_MULTIEXP_BASE", + gas_used: 713000000000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "ALT_BN128_G1_MULTIEXP_ELEMENT", + gas_used: 320000000000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "ALT_BN128_G1_SUM_BASE", + gas_used: 3000000000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "ALT_BN128_G1_SUM_ELEMENT", + gas_used: 5000000000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "ALT_BN128_PAIRING_CHECK_BASE", + gas_used: 9686000000000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "ALT_BN128_PAIRING_CHECK_ELEMENT", + gas_used: 5102000000000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "BASE", + gas_used: 17209927215, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "LOG_BASE", + gas_used: 7086626100, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "LOG_BYTE", + gas_used: 131987910, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "PROMISE_AND_BASE", + gas_used: 1465013400, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "PROMISE_AND_PER_PROMISE", + gas_used: 87234816, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "PROMISE_RETURN", + gas_used: 560152386, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "READ_CACHED_TRIE_NODE", + gas_used: 4560000000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "READ_MEMORY_BASE", + gas_used: 182690424000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "READ_MEMORY_BYTE", + gas_used: 4744063584, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "READ_REGISTER_BASE", + gas_used: 7551495558, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "READ_REGISTER_BYTE", + gas_used: 25034748, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "RIPEMD160_BASE", + gas_used: 853675086, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "RIPEMD160_BLOCK", + gas_used: 680107584, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "SHA256_BASE", + gas_used: 4540970250, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "SHA256_BYTE", + gas_used: 120586755, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_HAS_KEY_BASE", + gas_used: 108079793250, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_HAS_KEY_BYTE", + gas_used: 277117605, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_READ_BASE", + gas_used: 112713691500, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_READ_KEY_BYTE", + gas_used: 278572797, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_READ_VALUE_BYTE", + gas_used: 28055025, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_REMOVE_BASE", + gas_used: 106946061000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_REMOVE_KEY_BYTE", + gas_used: 343983456, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_REMOVE_RET_VALUE_BYTE", + gas_used: 57657780, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_WRITE_BASE", + gas_used: 128393472000, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_WRITE_EVICTED_BYTE", + gas_used: 160586535, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_WRITE_KEY_BYTE", + gas_used: 281931468, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "STORAGE_WRITE_VALUE_BYTE", + gas_used: 310185390, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "TOUCHING_TRIE_NODE", + gas_used: 32203911852, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "UTF16_DECODING_BASE", + gas_used: 3543313050, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "UTF16_DECODING_BYTE", + gas_used: 1635774930, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "UTF8_DECODING_BASE", + gas_used: 46676685915, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "UTF8_DECODING_BYTE", + gas_used: 98262621423, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "VALIDATOR_STAKE_BASE", + gas_used: 911834726400, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "VALIDATOR_TOTAL_STAKE_BASE", + gas_used: 911834726400, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WRITE_MEMORY_BASE", + gas_used: 19626564027, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WRITE_MEMORY_BYTE", + gas_used: 866159496, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WRITE_REGISTER_BASE", + gas_used: 37251792318, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WRITE_REGISTER_BYTE", + gas_used: 1904583564, + }, + ], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "BASE", + gas_used: 264768111, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + ], + [], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "BASE", + gas_used: 264768111, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "READ_MEMORY_BASE", + gas_used: 2609863200, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "READ_MEMORY_BYTE", + gas_used: 11403999, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "UTF8_DECODING_BASE", + gas_used: 3111779061, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "UTF8_DECODING_BYTE", + gas_used: 874741437, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + ], + [], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + ], + [], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + ], + [], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + ], + [], + [], + [], + [], + [], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 70891926, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + ], + [], + [], + [], + [], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + ], + [], + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "BASE", + gas_used: 529536222, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WRITE_REGISTER_BASE", + gas_used: 2865522486, + }, + ], + [], + [], +] diff --git a/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nondeterministic.snap b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nondeterministic.snap new file mode 100644 index 000000000..ed45e9748 --- /dev/null +++ b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nondeterministic.snap @@ -0,0 +1,24 @@ +--- +source: integration-tests/src/tests/runtime/sanity_checks.rs +expression: receipts_gas_profile +--- +[ + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 8236500, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 8227560, + }, + ], + [], +] diff --git a/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nondeterministic_nightly.snap b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nondeterministic_nightly.snap new file mode 100644 index 000000000..ed45e9748 --- /dev/null +++ b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nondeterministic_nightly.snap @@ -0,0 +1,24 @@ +--- +source: integration-tests/src/tests/runtime/sanity_checks.rs +expression: receipts_gas_profile +--- +[ + [ + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BASE", + gas_used: 35445963, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "CONTRACT_LOADING_BYTES", + gas_used: 8236500, + }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 8227560, + }, + ], + [], +] diff --git a/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_status.snap b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_status.snap new file mode 100644 index 000000000..906eafd23 --- /dev/null +++ b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_status.snap @@ -0,0 +1,40 @@ +--- +source: integration-tests/src/tests/runtime/sanity_checks.rs +expression: receipts_status + +--- +- SuccessReceiptId: "11111111111111111111111111111111" +- Failure: + ActionError: + index: 0 + kind: + FunctionCallError: + ExecutionError: "Smart contract panicked: explicit guest panic" +- SuccessValue: "" +- Failure: + ActionError: + index: 0 + kind: + FunctionCallError: + ExecutionError: "Smart contract panicked: xyz" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" +- SuccessValue: "" diff --git a/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_status_nondeterministic.snap b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_status_nondeterministic.snap new file mode 100644 index 000000000..b0d1e43f5 --- /dev/null +++ b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_status_nondeterministic.snap @@ -0,0 +1,8 @@ +--- +source: integration-tests/src/tests/runtime/sanity_checks.rs +expression: receipts_status + +--- +- SuccessValue: "" +- SuccessValue: "" + diff --git a/integration-tests/src/tests/runtime/state_viewer.rs b/integration-tests/src/tests/runtime/state_viewer.rs new file mode 100644 index 000000000..4abdfa381 --- /dev/null +++ b/integration-tests/src/tests/runtime/state_viewer.rs @@ -0,0 +1,413 @@ +use std::{collections::HashMap, io, sync::Arc}; + +use borsh::BorshDeserialize; + +use crate::runtime_utils::{get_runtime_and_trie, get_test_trie_viewer, TEST_SHARD_UID}; +use unc_primitives::{ + account::Account, + hash::hash as sha256, + hash::CryptoHash, + serialize::to_base64, + trie_key::trie_key_parsers, + types::{AccountId, StateRoot}, + views::{StateItem, ViewApplyState}, +}; +use unc_primitives::{ + test_utils::MockEpochInfoProvider, + trie_key::TrieKey, + types::{EpochId, StateChangeCause}, + version::PROTOCOL_VERSION, +}; +use unc_store::{set_account, NibbleSlice, RawTrieNode, RawTrieNodeWithSize}; +use node_runtime::state_viewer::errors; +use node_runtime::state_viewer::*; +use testlib::runtime_utils::alice_account; + +struct ProofVerifier { + nodes: HashMap, +} + +impl ProofVerifier { + fn new(proof: Vec>) -> Result { + let nodes = proof + .into_iter() + .map(|bytes| { + let hash = CryptoHash::hash_bytes(&bytes); + let node = RawTrieNodeWithSize::try_from_slice(&bytes)?; + Ok((hash, node)) + }) + .collect::, io::Error>>()?; + Ok(Self { nodes }) + } + + fn verify( + &self, + state_root: &StateRoot, + account_id: &AccountId, + key: &[u8], + expected: Option<&[u8]>, + ) -> bool { + let query = trie_key_parsers::get_raw_prefix_for_contract_data(account_id, key); + let mut key = NibbleSlice::new(&query); + + let mut expected_hash = state_root; + while let Some(node) = self.nodes.get(expected_hash) { + match &node.node { + RawTrieNode::Leaf(node_key, value) => { + let nib = &NibbleSlice::from_encoded(&node_key).0; + return if &key != nib { + expected.is_none() + } else { + expected.map_or(false, |expected| value == expected) + }; + } + RawTrieNode::Extension(node_key, child_hash) => { + expected_hash = child_hash; + + // To avoid unnecessary copy + let nib = NibbleSlice::from_encoded(&node_key).0; + if !key.starts_with(&nib) { + return expected.is_none(); + } + key = key.mid(nib.len()); + } + RawTrieNode::BranchNoValue(children) => { + if key.is_empty() { + return expected.is_none(); + } + match children[key.at(0)] { + Some(ref child_hash) => { + key = key.mid(1); + expected_hash = child_hash; + } + None => return expected.is_none(), + } + } + RawTrieNode::BranchWithValue(value, children) => { + if key.is_empty() { + return expected.map_or(false, |exp| value == exp); + } + match children[key.at(0)] { + Some(ref child_hash) => { + key = key.mid(1); + expected_hash = child_hash; + } + None => return expected.is_none(), + } + } + } + } + false + } +} + +#[test] +fn test_view_call() { + let (viewer, root) = get_test_trie_viewer(); + + let mut logs = vec![]; + let view_state = ViewApplyState { + block_height: 1, + prev_block_hash: CryptoHash::default(), + block_hash: CryptoHash::default(), + epoch_id: EpochId::default(), + epoch_height: 0, + block_timestamp: 1, + current_protocol_version: PROTOCOL_VERSION, + cache: None, + }; + let result = viewer.call_function( + root, + view_state, + &"test.contract".parse().unwrap(), + "run_test", + &[], + &mut logs, + &MockEpochInfoProvider::default(), + ); + + assert_eq!(result.unwrap(), (10i32).to_le_bytes()); +} + +#[test] +fn test_view_call_try_changing_storage() { + let (viewer, root) = get_test_trie_viewer(); + + let mut logs = vec![]; + let view_state = ViewApplyState { + block_height: 1, + prev_block_hash: CryptoHash::default(), + block_hash: CryptoHash::default(), + epoch_id: EpochId::default(), + epoch_height: 0, + block_timestamp: 1, + current_protocol_version: PROTOCOL_VERSION, + cache: None, + }; + let result = viewer.call_function( + root, + view_state, + &"test.contract".parse().unwrap(), + "run_test_with_storage_change", + &[], + &mut logs, + &MockEpochInfoProvider::default(), + ); + let err = result.unwrap_err(); + assert!( + err.to_string().contains(r#"ProhibitedInView { method_name: "storage_write" }"#), + "Got different error that doesn't match: {}", + err + ); +} + +#[test] +fn test_view_call_with_args() { + let (viewer, root) = get_test_trie_viewer(); + let args: Vec<_> = [1u64, 2u64].iter().flat_map(|x| (*x).to_le_bytes().to_vec()).collect(); + let mut logs = vec![]; + let view_state = ViewApplyState { + block_height: 1, + prev_block_hash: CryptoHash::default(), + block_hash: CryptoHash::default(), + epoch_id: EpochId::default(), + epoch_height: 0, + block_timestamp: 1, + current_protocol_version: PROTOCOL_VERSION, + cache: None, + }; + let view_call_result = viewer.call_function( + root, + view_state, + &"test.contract".parse().unwrap(), + "sum_with_input", + &args, + &mut logs, + &MockEpochInfoProvider::default(), + ); + assert_eq!(view_call_result.unwrap(), 3u64.to_le_bytes().to_vec()); +} + +fn assert_view_state( + trie_viewer: &TrieViewer, + state_update: &unc_store::TrieUpdate, + prefix: &[u8], + want_values: &[(&[u8], &[u8])], + want_proof: &[&'static str], +) -> ProofVerifier { + let alice = alice_account(); + let alina = "alina".parse().unwrap(); + + let values = want_values + .iter() + .map(|(key, value)| StateItem { key: key.to_vec().into(), value: value.to_vec().into() }) + .collect::>(); + + let view_state = + |include_proof| trie_viewer.view_state(&state_update, &alice, prefix, include_proof); + + // Test without proof + let result = view_state(false).unwrap(); + assert_eq!(values, result.values); + assert_eq!(Vec::>::new(), result.proof); + + // Test with proof included + let result = view_state(true).unwrap(); + assert_eq!(values, result.values); + let got = result.proof.iter().map(|bytes| to_base64(bytes)).collect::>(); + let got = got.iter().map(String::as_str).collect::>(); + // The proof isn’t deterministic because the state contains test contracts + // which aren’t built hermetically. Fortunately, only the first two items + // in the proof are affected. First one is the state root which is an + // Extension("0x0", child_hash) node and the second one is child hash + // pointing to a Branch node which splits into four: 0x0 (accounts), 0x1 + // (contract code; that’s what’s nondeterministic), 0x2 (access keys) and + // 0x9 (contract data; that’s what we care about). + assert_eq!(&want_proof[..], &got[2..]); + + // Verify proofs for all the expected values. + let proof_verifier = ProofVerifier::new(result.proof).unwrap(); + let root = state_update.get_root(); + for (key, value) in want_values { + // Proof for known (key, value) should succeed. + assert!( + proof_verifier.verify(root, &alice, key, Some(value)), + "key: alice / {key:x?}; value: {value:x?}" + ); + // The key exist, so proof for non-existence should fail. + assert!( + !proof_verifier.verify(root, &alice, key, None), + "key: alice / {key:x?}; value: None" + ); + // Proof for different value should fail. + assert!( + !proof_verifier.verify(root, &alice, key, Some(b"bogus")), + "key: alice / {key:x?}; value: None" + ); + // Proofs for different account should fail. + assert!( + !proof_verifier.verify(root, &alina, key, Some(value)), + "key: alice / {key:x?}; value: {value:x?}" + ); + assert!( + !proof_verifier.verify(root, &alina, key, None), + "key: alice / {key:x?}; value: {value:x?}" + ); + } + + proof_verifier +} + +#[test] +fn test_view_state() { + // in order to ensure determinism under all conditions (compiler, build output, etc) + // avoid deploying a test contract. See issue #7238 + let (_, tries, root) = get_runtime_and_trie(); + let shard_uid = TEST_SHARD_UID; + let mut state_update = tries.new_trie_update(shard_uid, root); + state_update.set( + TrieKey::ContractData { account_id: alice_account(), key: b"test123".to_vec() }, + b"123".to_vec(), + ); + state_update.set( + TrieKey::ContractData { account_id: alice_account(), key: b"test321".to_vec() }, + b"321".to_vec(), + ); + state_update.set( + TrieKey::ContractData { account_id: "alina".parse().unwrap(), key: b"qqq".to_vec() }, + b"321".to_vec(), + ); + state_update.set( + TrieKey::ContractData { account_id: "alex".parse().unwrap(), key: b"qqq".to_vec() }, + b"321".to_vec(), + ); + state_update.commit(StateChangeCause::InitialState); + let trie_changes = state_update.finalize().unwrap().1; + let mut db_changes = tries.store_update(); + let new_root = tries.apply_all(&trie_changes, shard_uid, &mut db_changes); + db_changes.commit().unwrap(); + + let state_update = tries.new_trie_update(shard_uid, new_root); + let trie_viewer = TrieViewer::default(); + + let proof = [ + "AwMAAAAWFsbwm2TFX4GHLT5G1LSpF8UkG7zQV1ohXBMR/OQcUAKZ3gwDAAAAAAAA", + "ASAC7S1KwgLNl0HPdSo8soL8sGOmPhL7O0xTSR8sDDR5pZrzu0ty3UPYJ5UKrFGKxXoyyyNG75AF9hnJHO3xxFkf5NQCAAAAAAAA", + "AwEAAAAW607KPj2q3O8dF6XkfALiIrd9mqGir2UlYIcZuLNksTsvAgAAAAAAAA==", + "AQhAP4sMdbiWZPtV6jz8hYKzRFSgwaSlQKiGsQXogAmMcrLOl+SJfiCOXMTEZ2a1ebmQOEGkRYa30FaIlB46sLI2IPsBAAAAAAAA", + "AwwAAAAWUubmVhcix0ZXN0PKtrEndk0LxM+qpzp0PVtjf+xlrzz4TT0qA+hTtm6BLlYBAAAAAAAA", + "AQoAVWCdny7wv/M1LvZASC3Fw0D/NNhI1NYwch9Ux+KZ2qRdQXPC1rNsCGRJ7nd66SfcNmRUVVvQY6EYCbsIiugO6gwBAAAAAAAA", + "AAMAAAAgMjMDAAAApmWkWSBCL51Bfkhn79xPuKBKHz//H6B+mY6G9/eieuNtAAAAAAAAAA==", + "AAMAAAAgMjEDAAAAjSPPbIboNKeqbt7VTCbOK7LnSQNTjGG91dIZeZerL3JtAAAAAAAAAA==", + ]; + let values = [(&b"test123"[..], &b"123"[..]), (&b"test321"[..], &b"321"[..])]; + assert_view_state(&trie_viewer, &state_update, b"", &values, &proof); + assert_view_state(&trie_viewer, &state_update, b"test", &values, &proof); + + assert_view_state(&trie_viewer, &state_update, b"xyz", &[], &[ + "AwMAAAAWFsbwm2TFX4GHLT5G1LSpF8UkG7zQV1ohXBMR/OQcUAKZ3gwDAAAAAAAA", + "ASAC7S1KwgLNl0HPdSo8soL8sGOmPhL7O0xTSR8sDDR5pZrzu0ty3UPYJ5UKrFGKxXoyyyNG75AF9hnJHO3xxFkf5NQCAAAAAAAA", + "AwEAAAAW607KPj2q3O8dF6XkfALiIrd9mqGir2UlYIcZuLNksTsvAgAAAAAAAA==", + "AQhAP4sMdbiWZPtV6jz8hYKzRFSgwaSlQKiGsQXogAmMcrLOl+SJfiCOXMTEZ2a1ebmQOEGkRYa30FaIlB46sLI2IPsBAAAAAAAA", + "AwwAAAAWUubmVhcix0ZXN0PKtrEndk0LxM+qpzp0PVtjf+xlrzz4TT0qA+hTtm6BLlYBAAAAAAAA", + ][..]); + + let proof_verifier = assert_view_state( + &trie_viewer, + &state_update, + b"test123", + &[(&b"test123"[..], &b"123"[..])], + &[ + "AwMAAAAWFsbwm2TFX4GHLT5G1LSpF8UkG7zQV1ohXBMR/OQcUAKZ3gwDAAAAAAAA", + "ASAC7S1KwgLNl0HPdSo8soL8sGOmPhL7O0xTSR8sDDR5pZrzu0ty3UPYJ5UKrFGKxXoyyyNG75AF9hnJHO3xxFkf5NQCAAAAAAAA", + "AwEAAAAW607KPj2q3O8dF6XkfALiIrd9mqGir2UlYIcZuLNksTsvAgAAAAAAAA==", + "AQhAP4sMdbiWZPtV6jz8hYKzRFSgwaSlQKiGsQXogAmMcrLOl+SJfiCOXMTEZ2a1ebmQOEGkRYa30FaIlB46sLI2IPsBAAAAAAAA", + "AwwAAAAWUubmVhcix0ZXN0PKtrEndk0LxM+qpzp0PVtjf+xlrzz4TT0qA+hTtm6BLlYBAAAAAAAA", + "AQoAVWCdny7wv/M1LvZASC3Fw0D/NNhI1NYwch9Ux+KZ2qRdQXPC1rNsCGRJ7nd66SfcNmRUVVvQY6EYCbsIiugO6gwBAAAAAAAA", + "AAMAAAAgMjMDAAAApmWkWSBCL51Bfkhn79xPuKBKHz//H6B+mY6G9/eieuNtAAAAAAAAAA==", + ] + ); + + let root = state_update.get_root(); + let account = alice_account(); + for (want, key, value) in [ + (true, b"test123".as_ref(), Some(b"123".as_ref())), + (false, b"test123".as_ref(), Some(b"321".as_ref())), + (false, b"test123".as_ref(), Some(b"1234".as_ref())), + (false, b"test123".as_ref(), None), + // Shorter key: + (false, b"test12".as_ref(), Some(b"123".as_ref())), + (true, b"test12", None), + // Longer key: + (false, b"test1234", Some(b"123")), + (true, b"test1234", None), + ] { + let got = proof_verifier.verify(&root, &account, key, value); + assert_eq!( + want, + got, + "key: {:?}; value: {:?}", + std::str::from_utf8(key).unwrap(), + value.map(|value| std::str::from_utf8(value).unwrap()) + ); + } +} + +#[test] +fn test_view_state_too_large() { + let (_, tries, root) = get_runtime_and_trie(); + let mut state_update = tries.new_trie_update(TEST_SHARD_UID, root); + set_account( + &mut state_update, + alice_account(), + &Account::new(0, 0, CryptoHash::default(), 50_001), + ); + let trie_viewer = TrieViewer::new(Some(50_000), None); + let result = trie_viewer.view_state(&state_update, &alice_account(), b"", false); + assert!(matches!(result, Err(errors::ViewStateError::AccountStateTooLarge { .. }))); +} + +#[test] +fn test_view_state_with_large_contract() { + let (_, tries, root) = get_runtime_and_trie(); + let mut state_update = tries.new_trie_update(TEST_SHARD_UID, root); + let contract_code = [0; Account::MAX_ACCOUNT_DELETION_STORAGE_USAGE as usize].to_vec(); + set_account( + &mut state_update, + alice_account(), + &Account::new(0, 0, sha256(&contract_code), 50_001), + ); + state_update.set(TrieKey::ContractCode { account_id: alice_account() }, contract_code); + let trie_viewer = TrieViewer::new(Some(50_000), None); + let result = trie_viewer.view_state(&state_update, &alice_account(), b"", false); + assert!(result.is_ok()); +} + +#[test] +fn test_log_when_panic() { + let (viewer, root) = get_test_trie_viewer(); + let view_state = ViewApplyState { + block_height: 1, + prev_block_hash: CryptoHash::default(), + block_hash: CryptoHash::default(), + epoch_id: EpochId::default(), + epoch_height: 0, + block_timestamp: 1, + current_protocol_version: PROTOCOL_VERSION, + cache: None, + }; + let mut logs = vec![]; + viewer + .call_function( + root, + view_state, + &"test.contract".parse().unwrap(), + "panic_after_logging", + &[], + &mut logs, + &MockEpochInfoProvider::default(), + ) + .unwrap_err(); + + assert_eq!(logs, vec!["hello".to_string()]); +} diff --git a/integration-tests/src/tests/runtime/test_evil_contracts.rs b/integration-tests/src/tests/runtime/test_evil_contracts.rs new file mode 100644 index 000000000..4cb062f1d --- /dev/null +++ b/integration-tests/src/tests/runtime/test_evil_contracts.rs @@ -0,0 +1,140 @@ +use crate::node::{Node, RuntimeNode}; +use unc_primitives::errors::{ActionError, ActionErrorKind, FunctionCallError}; +use unc_primitives::views::FinalExecutionStatus; +use std::mem::size_of; + +use assert_matches::assert_matches; + +/// Initial balance used in tests. +pub const TESTING_INIT_BALANCE: u128 = 1_000_000_000 * UNC_BASE; + +/// One NEAR, divisible by 10^24. +pub const UNC_BASE: u128 = 1_000_000_000_000_000_000_000_000; + +/// Max prepaid amount of gas. +const MAX_GAS: u64 = 300_000_000_000_000; + +fn setup_test_contract(wasm_binary: &[u8]) -> RuntimeNode { + let node = RuntimeNode::new(&"alice.near".parse().unwrap()); + let account_id = node.account_id().unwrap(); + let node_user = node.user(); + let transaction_result = node_user + .create_account( + account_id, + "test_contract".parse().unwrap(), + node.signer().public_key(), + TESTING_INIT_BALANCE / 2, + ) + .unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + + let transaction_result = + node_user.deploy_contract("test_contract".parse().unwrap(), wasm_binary.to_vec()).unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + + node +} + +#[test] +fn test_evil_deep_trie() { + let node = setup_test_contract(unc_test_contracts::rs_contract()); + for i in 0..50 { + println!("insertStrings #{}", i); + let from = i * 10 as u64; + let to = (i + 1) * 10 as u64; + let mut input_data = [0u8; 2 * size_of::()]; + input_data[..size_of::()].copy_from_slice(&from.to_le_bytes()); + input_data[size_of::()..].copy_from_slice(&to.to_le_bytes()); + let res = node + .user() + .function_call( + "alice.near".parse().unwrap(), + "test_contract".parse().unwrap(), + "insert_strings", + input_data.to_vec(), + MAX_GAS, + 0, + ) + .unwrap(); + println!("Gas burnt: {}", res.receipts_outcome[0].outcome.gas_burnt); + assert_eq!(res.status, FinalExecutionStatus::SuccessValue(Vec::new()), "{:?}", res); + } + for i in (0..50).rev() { + println!("deleteStrings #{}", i); + let from = i * 10 as u64; + let to = (i + 1) * 10 as u64; + let mut input_data = [0u8; 2 * size_of::()]; + input_data[..size_of::()].copy_from_slice(&from.to_le_bytes()); + input_data[size_of::()..].copy_from_slice(&to.to_le_bytes()); + let res = node + .user() + .function_call( + "alice.near".parse().unwrap(), + "test_contract".parse().unwrap(), + "delete_strings", + input_data.to_vec(), + MAX_GAS, + 0, + ) + .unwrap(); + println!("Gas burnt: {}", res.receipts_outcome[0].outcome.gas_burnt); + assert_eq!(res.status, FinalExecutionStatus::SuccessValue(Vec::new()), "{:?}", res); + } +} + +#[test] +fn test_evil_deep_recursion() { + let node = setup_test_contract(unc_test_contracts::rs_contract()); + for n in [100u64, 1000, 10000, 100000, 1000000] { + println!("{}", n); + let n_bytes = n.to_le_bytes().to_vec(); + let res = node + .user() + .function_call( + "alice.near".parse().unwrap(), + "test_contract".parse().unwrap(), + "recurse", + n_bytes.clone(), + MAX_GAS, + 0, + ) + .unwrap(); + if n <= 1000 { + assert_eq!(res.status, FinalExecutionStatus::SuccessValue(n_bytes), "{:?}", res); + } else { + assert_matches!(res.status, FinalExecutionStatus::Failure(_), "{:?}", res); + } + } +} + +#[test] +fn test_evil_abort() { + let node = setup_test_contract(unc_test_contracts::rs_contract()); + let res = node + .user() + .function_call( + "alice.near".parse().unwrap(), + "test_contract".parse().unwrap(), + "abort_with_zero", + vec![], + MAX_GAS, + 0, + ) + .unwrap(); + assert_eq!( + res.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::FunctionCallError(FunctionCallError::ExecutionError( + "String encoding is bad UTF-16 sequence.".to_string() + )) + } + .into() + ), + "{:?}", + res + ); +} diff --git a/integration-tests/src/tests/standard_cases/mod.rs b/integration-tests/src/tests/standard_cases/mod.rs new file mode 100644 index 000000000..e7084645d --- /dev/null +++ b/integration-tests/src/tests/standard_cases/mod.rs @@ -0,0 +1,1600 @@ +use std::sync::Arc; + +mod rpc; +mod runtime; + +use assert_matches::assert_matches; +use unc_crypto::{InMemorySigner, KeyType, PublicKey}; +use unc_jsonrpc_primitives::errors::ServerError; +use unc_parameters::{ActionCosts, ExtCosts}; +use unc_primitives::account::{ + id::AccountType, AccessKey, AccessKeyPermission, FunctionCallPermission, +}; +use unc_primitives::errors::{ + ActionError, ActionErrorKind, FunctionCallError, InvalidAccessKeyError, InvalidTxError, + MethodResolveError, TxExecutionError, +}; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::types::{AccountId, Balance, TrieNodesCount}; +use unc_primitives::utils::{derive_eth_implicit_account_id, derive_unc_implicit_account_id}; +use unc_primitives::views::{ + AccessKeyView, AccountView, ExecutionMetadataView, FinalExecutionOutcomeView, + FinalExecutionStatus, +}; +use framework::config::{UNC_BASE, TESTING_INIT_BALANCE, TESTING_INIT_STAKE}; + +use crate::node::Node; +use crate::user::User; +use unc_parameters::RuntimeConfig; +use unc_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum}; +use unc_primitives::test_utils; +use unc_primitives::transaction::{Action, DeployContractAction, FunctionCallAction}; +use testlib::fees_utils::FeeHelper; +use testlib::runtime_utils::{ + alice_account, bob_account, eve_dot_alice_account, x_dot_y_dot_alice_account, +}; + +/// The amount to send with function call. +const FUNCTION_CALL_AMOUNT: Balance = TESTING_INIT_BALANCE / 10; + +pub(crate) fn fee_helper(node: &impl Node) -> FeeHelper { + FeeHelper::new(RuntimeConfig::test(), node.genesis().config.min_gas_price) +} + +/// Adds given access key to the given account_id using signer2. +fn add_access_key( + node: &impl Node, + node_user: &dyn User, + access_key: &AccessKey, + signer2: &InMemorySigner, +) -> FinalExecutionOutcomeView { + let root = node_user.get_state_root(); + let account_id = &node.account_id().unwrap(); + let transaction_result = node_user + .add_key(account_id.clone(), signer2.public_key.clone(), access_key.clone()) + .unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + transaction_result +} + +pub fn test_smart_contract_simple(node: impl Node) { + let node_user = node.user(); + let root = node_user.get_state_root(); + let transaction_result = node_user + .function_call(alice_account(), bob_account(), "run_test", vec![], 10u64.pow(14), 0) + .unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::SuccessValue(10i32.to_le_bytes().to_vec()) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); +} + +pub fn test_smart_contract_panic(node: impl Node) { + let node_user = node.user(); + let transaction_result = node_user + .function_call( + alice_account(), + alice_account(), + "panic_with_message", + vec![], + 10u64.pow(14), + 0, + ) + .unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::FunctionCallError(FunctionCallError::ExecutionError( + "Smart contract panicked: WAT?".to_string() + )) + } + .into() + ) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 2); +} + +pub fn test_smart_contract_self_call(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let transaction_result = node_user + .function_call(account_id.clone(), account_id.clone(), "run_test", vec![], 10u64.pow(14), 0) + .unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::SuccessValue(10i32.to_le_bytes().to_vec()) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); +} + +pub fn test_smart_contract_bad_method_name(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let transaction_result = node_user + .function_call(account_id.clone(), bob_account(), "_run_test", vec![], 10u64.pow(14), 0) + .unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::FunctionCallError(FunctionCallError::MethodResolveError( + MethodResolveError::MethodNotFound + )) + } + .into() + ) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); +} + +pub fn test_smart_contract_empty_method_name_with_no_tokens(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let transaction_result = node_user + .function_call(account_id.clone(), bob_account(), "", vec![], 10u64.pow(14), 0) + .unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::FunctionCallError(FunctionCallError::MethodResolveError( + MethodResolveError::MethodEmptyName + )) + } + .into() + ) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); +} + +pub fn test_smart_contract_empty_method_name_with_tokens(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let transaction_result = node_user + .function_call(account_id.clone(), bob_account(), "", vec![], 10u64.pow(14), 10) + .unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::FunctionCallError(FunctionCallError::MethodResolveError( + MethodResolveError::MethodEmptyName + )) + } + .into() + ) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 3); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); +} + +pub fn test_smart_contract_with_args(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let transaction_result = node_user + .function_call( + account_id.clone(), + bob_account(), + "sum_with_input", + (2u64..4).flat_map(|x| x.to_le_bytes().to_vec()).collect(), + 10u64.pow(14), + 0, + ) + .unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::SuccessValue(5u64.to_le_bytes().to_vec()) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); +} + +pub fn test_async_call_with_logs(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let transaction_result = node_user + .function_call(account_id.clone(), bob_account(), "log_something", vec![], 10u64.pow(14), 0) + .unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + assert_eq!(transaction_result.receipts_outcome[0].outcome.logs[0], "hello".to_string()); +} + +pub fn test_nonce_update_when_deploying_contract(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let wasm_binary = b"test_binary"; + let node_user = node.user(); + let root = node_user.get_state_root(); + let transaction_result = + node_user.deploy_contract(account_id.clone(), wasm_binary.to_vec()).unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 1); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); +} + +pub fn test_nonce_updated_when_tx_failed(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + node_user.send_money(account_id.clone(), bob_account(), TESTING_INIT_BALANCE + 1).unwrap_err(); + assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 0); + let new_root = node_user.get_state_root(); + assert_eq!(root, new_root); +} + +pub fn test_upload_contract(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let transaction_result = node_user + .create_account( + account_id.clone(), + eve_dot_alice_account(), + node.signer().public_key(), + TESTING_INIT_BALANCE / 2, + ) + .unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + + node_user.view_contract_code(&eve_dot_alice_account()).expect_err( + "RpcError { code: -32000, message: \"Server error\", data: Some(String(\"contract code of account eve.alice.near does not exist while viewing\")) }"); + + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + let wasm_binary = b"test_binary"; + let transaction_result = + node_user.deploy_contract(eve_dot_alice_account(), wasm_binary.to_vec()).unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + let account = node_user.view_account(&eve_dot_alice_account()).unwrap(); + assert_eq!(account.code_hash, hash(wasm_binary)); + + let code = node_user.view_contract_code(&eve_dot_alice_account()).unwrap(); + assert_eq!(code.code, wasm_binary.to_vec()); +} + +pub fn test_redeploy_contract(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let test_binary = b"test_binary"; + let transaction_result = + node_user.deploy_contract(account_id.clone(), test_binary.to_vec()).unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + let account = node_user.view_account(account_id).unwrap(); + assert_eq!(account.code_hash, hash(test_binary)); +} + +pub fn test_send_money(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let money_used = 10000; + let fee_helper = fee_helper(&node); + let transfer_cost = fee_helper.transfer_cost(); + let transaction_result = + node_user.send_money(account_id.clone(), bob_account(), money_used).unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 1); + + let AccountView { amount, locked, .. } = node_user.view_account(account_id).unwrap(); + assert_eq!( + (amount, locked), + ( + TESTING_INIT_BALANCE - money_used - TESTING_INIT_STAKE - transfer_cost, + TESTING_INIT_STAKE + ) + ); + let AccountView { amount, locked, .. } = node_user.view_account(&bob_account()).unwrap(); + assert_eq!( + (amount, locked), + (TESTING_INIT_BALANCE + money_used - TESTING_INIT_STAKE, TESTING_INIT_STAKE,) + ); +} + +pub fn transfer_tokens_to_implicit_account(node: impl Node, public_key: PublicKey) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let tokens_used = 10u128.pow(25); + let fee_helper = fee_helper(&node); + + let receiver_id = match public_key.key_type() { + KeyType::ED25519 => derive_unc_implicit_account_id(public_key.unwrap_as_ed25519()), + KeyType::SECP256K1 => derive_eth_implicit_account_id(public_key.unwrap_as_secp256k1()), + KeyType::RSA2048 => panic!("RSA keys not supported"), + }; + + let transfer_cost = match receiver_id.get_account_type() { + AccountType::NearImplicitAccount => fee_helper.create_account_transfer_full_key_cost(), + AccountType::EthImplicitAccount => fee_helper.create_account_transfer_cost(), + AccountType::NamedAccount => std::panic!("must be implicit"), + }; + + let transaction_result = + node_user.send_money(account_id.clone(), receiver_id.clone(), tokens_used).unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 1); + + let AccountView { amount, locked, .. } = node_user.view_account(account_id).unwrap(); + assert_eq!( + (amount, locked), + ( + TESTING_INIT_BALANCE - tokens_used - TESTING_INIT_STAKE - transfer_cost, + TESTING_INIT_STAKE + ) + ); + + let AccountView { amount, locked, .. } = node_user.view_account(&receiver_id).unwrap(); + assert_eq!((amount, locked), (tokens_used, 0)); + + let view_access_key = node_user.get_access_key(&receiver_id, &public_key); + match receiver_id.get_account_type() { + AccountType::NearImplicitAccount => { + assert_eq!(view_access_key.unwrap(), AccessKey::full_access().into()); + } + AccountType::EthImplicitAccount => { + // A transfer to ETH-implicit address does not create access key. + assert!(view_access_key.is_err()); + } + AccountType::NamedAccount => std::panic!("must be implicit"), + } + + let transaction_result = + node_user.send_money(account_id.clone(), receiver_id.clone(), tokens_used).unwrap(); + + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 2); + + let AccountView { amount, locked, .. } = node_user.view_account(account_id).unwrap(); + assert_eq!( + (amount, locked), + ( + TESTING_INIT_BALANCE - 2 * tokens_used - TESTING_INIT_STAKE - 2 * transfer_cost, + TESTING_INIT_STAKE + ) + ); + + let AccountView { amount, locked, .. } = node_user.view_account(&receiver_id).unwrap(); + assert_eq!((amount, locked), (tokens_used * 2, 0)); +} + +pub fn trying_to_create_implicit_account(node: impl Node, public_key: PublicKey) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let tokens_used = 10u128.pow(25); + let fee_helper = fee_helper(&node); + + let receiver_id = match public_key.key_type() { + KeyType::ED25519 => derive_unc_implicit_account_id(public_key.unwrap_as_ed25519()), + KeyType::SECP256K1 => derive_eth_implicit_account_id(public_key.unwrap_as_secp256k1()), + KeyType::RSA2048 => panic!("RSA keys not supported"), + }; + + let transaction_result = node_user + .create_account(account_id.clone(), receiver_id.clone(), public_key, tokens_used) + .unwrap(); + + let create_account_fee = fee_helper.cfg().fee(ActionCosts::create_account).send_fee(false); + let add_access_key_fee = fee_helper.cfg().fee(ActionCosts::add_full_access_key).send_fee(false); + + let cost = match receiver_id.get_account_type() { + AccountType::NearImplicitAccount => { + fee_helper.create_account_transfer_full_key_cost_fail_on_create_account() + + fee_helper.gas_to_balance(create_account_fee + add_access_key_fee) + } + AccountType::EthImplicitAccount => { + // This test uses `node_user.create_account` method that is normally used for NamedAccounts and should fail here. + fee_helper.create_account_transfer_full_key_cost_fail_on_create_account() + // We add this fee analogously to the NEAR-implicit match arm above (without `add_access_key_fee`). + + fee_helper.gas_to_balance(create_account_fee) + } + AccountType::NamedAccount => std::panic!("must be implicit"), + }; + + assert_eq!( + transaction_result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::OnlyImplicitAccountCreationAllowed { + account_id: receiver_id.clone(), + } + } + .into() + ) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 3); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 1); + + let result1 = node_user.view_account(account_id).unwrap(); + assert_eq!( + (result1.amount, result1.locked), + (TESTING_INIT_BALANCE - TESTING_INIT_STAKE - cost, TESTING_INIT_STAKE) + ); + + let result2 = node_user.view_account(&receiver_id); + assert!(result2.is_err()); +} + +pub fn test_smart_contract_reward(node: impl Node) { + let node_user = node.user(); + let root = node_user.get_state_root(); + let bob = node_user.view_account(&bob_account()).unwrap(); + assert_eq!(bob.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + let transaction_result = node_user + .function_call(alice_account(), bob_account(), "run_test", vec![], 10u64.pow(14), 0) + .unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::SuccessValue(10i32.to_le_bytes().to_vec()) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + + let fee_helper = fee_helper(&node); + let bob = node_user.view_account(&bob_account()).unwrap(); + let gas_burnt_for_function_call = transaction_result.receipts_outcome[0].outcome.gas_burnt + - fee_helper.function_call_exec_gas(b"run_test".len() as u64); + let reward = fee_helper.gas_burnt_to_reward(gas_burnt_for_function_call); + assert_eq!(bob.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE + reward); +} + +pub fn test_send_money_over_balance(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let money_used = TESTING_INIT_BALANCE + 1; + node_user.send_money(account_id.clone(), bob_account(), money_used).unwrap_err(); + let new_root = node_user.get_state_root(); + assert_eq!(root, new_root); + let result1 = node_user.view_account(account_id).unwrap(); + assert_eq!( + (result1.amount, result1.locked), + (TESTING_INIT_BALANCE - TESTING_INIT_STAKE, TESTING_INIT_STAKE) + ); + assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 0); + + let result2 = node_user.view_account(&bob_account()).unwrap(); + assert_eq!( + (result2.amount, result2.locked), + (TESTING_INIT_BALANCE - TESTING_INIT_STAKE, TESTING_INIT_STAKE) + ); +} + +pub fn test_refund_on_send_money_to_non_existent_account(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let money_used = 10; + // Successful atomic transfer has the same cost as failed atomic transfer. + let fee_helper = fee_helper(&node); + let transfer_cost = fee_helper.transfer_cost(); + let transaction_result = + node_user.send_money(account_id.clone(), eve_dot_alice_account(), money_used).unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::AccountDoesNotExist { account_id: eve_dot_alice_account() } + } + .into() + ) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 3); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + let result1 = node_user.view_account(account_id).unwrap(); + assert_eq!( + (result1.amount, result1.locked), + (TESTING_INIT_BALANCE - TESTING_INIT_STAKE - transfer_cost, TESTING_INIT_STAKE) + ); + assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 1); + let result2 = node_user.view_account(&eve_dot_alice_account()); + assert!(result2.is_err()); +} + +pub fn test_create_account(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let money_used = UNC_BASE; + let transaction_result = node_user + .create_account( + account_id.clone(), + eve_dot_alice_account(), + node.signer().public_key(), + money_used, + ) + .unwrap(); + + let fee_helper = fee_helper(&node); + let create_account_cost = fee_helper.create_account_transfer_full_key_cost(); + + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 1); + + let result1 = node_user.view_account(account_id).unwrap(); + assert_eq!( + (result1.amount, result1.locked), + ( + TESTING_INIT_BALANCE - money_used - TESTING_INIT_STAKE - create_account_cost, + TESTING_INIT_STAKE + ) + ); + + let result2 = node_user.view_account(&eve_dot_alice_account()).unwrap(); + assert_eq!((result2.amount, result2.locked), (money_used, 0)); +} + +pub fn test_create_account_again(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let money_used = UNC_BASE; + let transaction_result = node_user + .create_account( + account_id.clone(), + eve_dot_alice_account(), + node.signer().public_key(), + money_used, + ) + .unwrap(); + + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + let fee_helper = fee_helper(&node); + let create_account_cost = fee_helper.create_account_transfer_full_key_cost(); + + let result1 = node_user.view_account(account_id).unwrap(); + let new_expected_balance = + TESTING_INIT_BALANCE - money_used - TESTING_INIT_STAKE - create_account_cost; + assert_eq!((result1.amount, result1.locked), (new_expected_balance, TESTING_INIT_STAKE)); + assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 1); + + let result2 = node_user.view_account(&eve_dot_alice_account()).unwrap(); + assert_eq!((result2.amount, result2.locked), (money_used, 0)); + + let transaction_result = node_user + .create_account( + account_id.clone(), + eve_dot_alice_account(), + node.signer().public_key(), + money_used, + ) + .unwrap(); + + assert_eq!( + transaction_result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::AccountAlreadyExists { account_id: eve_dot_alice_account() } + } + .into() + ) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 3); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 2); + + // Additional cost for trying to create an account with repeated name. Will fail after + // the first action. + let additional_cost = fee_helper.create_account_transfer_full_key_cost_fail_on_create_account(); + + let result1 = node_user.view_account(account_id).unwrap(); + assert_eq!( + (result1.amount, result1.locked), + (new_expected_balance - additional_cost, TESTING_INIT_STAKE) + ); +} + +pub fn test_create_account_failure_no_funds(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let transaction_result = node_user + .create_account(account_id.clone(), eve_dot_alice_account(), node.signer().public_key(), 0) + .unwrap(); + assert_matches!(transaction_result.status, FinalExecutionStatus::SuccessValue(_)); +} + +pub fn test_create_account_failure_already_exists(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let money_used = 1000; + + let transaction_result = node_user + .create_account(account_id.clone(), bob_account(), node.signer().public_key(), money_used) + .unwrap(); + let fee_helper = fee_helper(&node); + let create_account_cost = + fee_helper.create_account_transfer_full_key_cost_fail_on_create_account(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::AccountAlreadyExists { account_id: bob_account() } + } + .into() + ) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 3); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 1); + + let result1 = node_user.view_account(account_id).unwrap(); + assert_eq!( + (result1.amount, result1.locked), + (TESTING_INIT_BALANCE - TESTING_INIT_STAKE - create_account_cost, TESTING_INIT_STAKE) + ); + + let result2 = node_user.view_account(&bob_account()).unwrap(); + assert_eq!( + (result2.amount, result2.locked), + (TESTING_INIT_BALANCE - TESTING_INIT_STAKE, TESTING_INIT_STAKE) + ); +} + +pub fn test_swap_key(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let signer2 = InMemorySigner::from_random("test".parse().unwrap(), KeyType::ED25519); + let node_user = node.user(); + let root = node_user.get_state_root(); + let money_used = TESTING_INIT_BALANCE / 2; + node_user + .create_account( + account_id.clone(), + eve_dot_alice_account(), + node.signer().public_key(), + money_used, + ) + .unwrap(); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + + let transaction_result = node_user + .swap_key( + eve_dot_alice_account(), + node.signer().public_key(), + signer2.public_key.clone(), + AccessKey::full_access(), + ) + .unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + let new_root1 = node_user.get_state_root(); + assert_ne!(new_root, new_root1); + + assert!(node_user + .get_access_key(&eve_dot_alice_account(), &node.signer().public_key()) + .is_err()); + assert!(node_user.get_access_key(&eve_dot_alice_account(), &signer2.public_key).is_ok()); +} + +pub fn test_add_key(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let signer2 = InMemorySigner::from_random("test".parse().unwrap(), KeyType::ED25519); + let node_user = node.user(); + + add_access_key(&node, node_user.as_ref(), &AccessKey::full_access(), &signer2); + + assert!(node_user.get_access_key(account_id, &node.signer().public_key()).is_ok()); + assert!(node_user.get_access_key(account_id, &signer2.public_key).is_ok()); +} + +pub fn test_add_existing_key(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + let transaction_result = node_user + .add_key(account_id.clone(), node.signer().public_key(), AccessKey::full_access()) + .unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::AddKeyAlreadyExists { + account_id: account_id.clone(), + public_key: node.signer().public_key().into() + } + } + .into() + ) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + + assert!(node_user.get_access_key(account_id, &node.signer().public_key()).is_ok()); +} + +pub fn test_delete_key(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let signer2 = InMemorySigner::from_random("test".parse().unwrap(), KeyType::ED25519); + let node_user = node.user(); + add_access_key(&node, node_user.as_ref(), &AccessKey::full_access(), &signer2); + + assert!(node_user.get_access_key(account_id, &node.signer().public_key()).is_ok()); + assert!(node_user.get_access_key(account_id, &signer2.public_key).is_ok()); + + let root = node_user.get_state_root(); + let transaction_result = + node_user.delete_key(account_id.clone(), node.signer().public_key()).unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + let new_root = node_user.get_state_root(); + assert_ne!(new_root, root); + + assert!(node_user.get_access_key(account_id, &node.signer().public_key()).is_err()); + assert!(node_user.get_access_key(account_id, &signer2.public_key).is_ok()); +} + +pub fn test_delete_key_not_owned(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let signer2 = InMemorySigner::from_random("test".parse().unwrap(), KeyType::ED25519); + let node_user = node.user(); + + assert!(node_user.get_access_key(account_id, &node.signer().public_key()).is_ok()); + assert!(node_user.get_access_key(account_id, &signer2.public_key).is_err()); + + let root = node_user.get_state_root(); + let transaction_result = + node_user.delete_key(account_id.clone(), signer2.public_key.clone()).unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::DeleteKeyDoesNotExist { + account_id: account_id.clone(), + public_key: signer2.public_key.clone().into() + } + } + .into() + ) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + let new_root = node_user.get_state_root(); + assert_ne!(new_root, root); + + assert!(node_user.get_access_key(account_id, &node.signer().public_key()).is_ok()); + assert!(node_user.get_access_key(account_id, &signer2.public_key).is_err()); +} + +pub fn test_delete_key_last(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let node_user = node.user(); + let root = node_user.get_state_root(); + + assert!(node_user.get_access_key(account_id, &node.signer().public_key()).is_ok()); + let transaction_result = node_user.delete_key(account_id.clone(), node.signer().public_key()); + + match transaction_result { + Ok(transaction_result) => { + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + } + Err(err) => { + // TODO(#6724): This is a wrong error, the transaction actually + // succeeds. We get an error here when we retry the tx and the second + // time around it fails. Normally, retries are handled by nonces, but we + // forget the nonce when we delete a key! + assert_eq!( + err, + ServerError::TxExecutionError(TxExecutionError::InvalidTxError( + InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::AccessKeyNotFound { + account_id: account_id.clone(), + public_key: node.signer().public_key().into(), + }, + ) + )) + ) + } + } + + let new_root = node_user.get_state_root(); + assert_ne!(new_root, root); + + assert!(node_user.get_access_key(account_id, &node.signer().public_key()).is_err()); +} + +#[track_caller] +fn assert_access_key( + access_key: &AccessKey, + access_key_view: AccessKeyView, + result: &FinalExecutionOutcomeView, + user: &dyn User, +) { + let mut key = access_key.clone(); + let block = user.get_block(result.transaction_outcome.block_hash); + if let Some(b) = block { + key.nonce = (b.header.height - 1) * AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER; + } + assert_eq!(access_key_view, key.into()); +} + +pub fn test_add_access_key_function_call(node: impl Node) { + let node_user = node.user(); + let account_id = &node.account_id().unwrap(); + let access_key = AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: account_id.to_string(), + method_names: vec![], + }), + }; + let signer2 = InMemorySigner::from_random("test".parse().unwrap(), KeyType::ED25519); + let result = add_access_key(&node, node_user.as_ref(), &access_key, &signer2); + + assert!(node_user.get_access_key(account_id, &node.signer().public_key()).is_ok()); + + let view_access_key = node_user.get_access_key(account_id, &signer2.public_key).unwrap(); + assert_access_key(&access_key, view_access_key, &result, node_user.as_ref()); +} + +pub fn test_delete_access_key(node: impl Node) { + let node_user = node.user(); + let account_id = &node.account_id().unwrap(); + let access_key = AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: account_id.to_string(), + method_names: vec![], + }), + }; + let signer2 = InMemorySigner::from_random("test".parse().unwrap(), KeyType::ED25519); + add_access_key(&node, node_user.as_ref(), &access_key, &signer2); + + assert!(node_user.get_access_key(account_id, &node.signer().public_key()).is_ok()); + assert!(node_user.get_access_key(account_id, &signer2.public_key).is_ok()); + + let root = node_user.get_state_root(); + let transaction_result = + node_user.delete_key(account_id.clone(), signer2.public_key.clone()).unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + let new_root = node_user.get_state_root(); + assert_ne!(new_root, root); + + assert!(node_user.get_access_key(account_id, &node.signer().public_key()).is_ok()); + assert!(node_user.get_access_key(account_id, &signer2.public_key).is_err()); +} + +pub fn test_add_access_key_with_allowance(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let access_key = AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(10), + receiver_id: account_id.to_string(), + method_names: vec![], + }), + }; + let node_user = node.user(); + let signer2 = InMemorySigner::from_random("test".parse().unwrap(), KeyType::ED25519); + let account = node_user.view_account(account_id).unwrap(); + let initial_balance = account.amount; + let fee_helper = fee_helper(&node); + let add_access_key_cost = fee_helper.add_key_cost(0); + let result = add_access_key(&node, node_user.as_ref(), &access_key, &signer2); + + let account = node_user.view_account(account_id).unwrap(); + assert_eq!(account.amount, initial_balance - add_access_key_cost); + + assert!(node_user.get_access_key(account_id, &node.signer().public_key()).is_ok()); + let view_access_key = node_user.get_access_key(account_id, &signer2.public_key).unwrap(); + assert_access_key(&access_key, view_access_key, &result, node_user.as_ref()); +} + +pub fn test_delete_access_key_with_allowance(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let access_key = AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(10), + receiver_id: account_id.to_string(), + method_names: vec![], + }), + }; + let node_user = node.user(); + let signer2 = InMemorySigner::from_random("test".parse().unwrap(), KeyType::ED25519); + let account = node_user.view_account(account_id).unwrap(); + let initial_balance = account.amount; + let fee_helper = fee_helper(&node); + let add_access_key_cost = fee_helper.add_key_cost(0); + add_access_key(&node, node_user.as_ref(), &access_key, &signer2); + + assert!(node_user.get_access_key(account_id, &node.signer().public_key()).is_ok()); + assert!(node_user.get_access_key(account_id, &signer2.public_key).is_ok()); + + let root = node_user.get_state_root(); + let delete_access_key_cost = fee_helper.delete_key_cost(); + let transaction_result = + node_user.delete_key(account_id.clone(), signer2.public_key.clone()).unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + let new_root = node_user.get_state_root(); + assert_ne!(new_root, root); + + let account = node_user.view_account(account_id).unwrap(); + assert_eq!(account.amount, initial_balance - add_access_key_cost - delete_access_key_cost); + + assert!(node_user.get_access_key(account_id, &node.signer().public_key()).is_ok()); + assert!(node_user.get_access_key(account_id, &signer2.public_key).is_err()); +} + +pub fn test_access_key_smart_contract(node: impl Node) { + let access_key = AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(FUNCTION_CALL_AMOUNT), + receiver_id: bob_account().into(), + method_names: vec![], + }), + }; + let mut node_user = node.user(); + let account_id = &node.account_id().unwrap(); + let signer2 = Arc::new(InMemorySigner::from_random("test".parse().unwrap(), KeyType::ED25519)); + add_access_key(&node, node_user.as_ref(), &access_key, &signer2); + node_user.set_signer(signer2.clone()); + + let method_name = "run_test"; + let prepaid_gas = 10u64.pow(14); + let fee_helper = fee_helper(&node); + let function_call_cost = + fee_helper.function_call_cost(method_name.as_bytes().len() as u64, prepaid_gas); + let exec_gas = fee_helper.function_call_exec_gas(method_name.as_bytes().len() as u64); + let root = node_user.get_state_root(); + let transaction_result = node_user + .function_call(account_id.clone(), bob_account(), method_name, vec![], prepaid_gas, 0) + .unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::SuccessValue(10i32.to_le_bytes().to_vec()) + ); + let gas_refund = fee_helper.gas_to_balance( + prepaid_gas + exec_gas - transaction_result.receipts_outcome[0].outcome.gas_burnt, + ); + + assert_eq!(transaction_result.receipts_outcome.len(), 2); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + + let view_access_key = node_user.get_access_key(account_id, &signer2.public_key).unwrap(); + assert_eq!( + view_access_key, + AccessKey { + nonce: view_access_key.nonce, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(FUNCTION_CALL_AMOUNT - function_call_cost + gas_refund), + receiver_id: bob_account().into(), + method_names: vec![], + }), + } + .into() + ); +} + +pub fn test_access_key_smart_contract_reject_method_name(node: impl Node) { + let access_key = AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(FUNCTION_CALL_AMOUNT), + receiver_id: bob_account().into(), + method_names: vec!["log_something".to_string()], + }), + }; + let mut node_user = node.user(); + let account_id = &node.account_id().unwrap(); + let signer2 = InMemorySigner::from_random("test".parse().unwrap(), KeyType::ED25519); + add_access_key(&node, node_user.as_ref(), &access_key, &signer2); + node_user.set_signer(Arc::new(signer2)); + + let transaction_result = node_user + .function_call(account_id.clone(), bob_account(), "run_test", vec![], 10u64.pow(14), 0) + .unwrap_err(); + assert_eq!( + transaction_result, + ServerError::TxExecutionError(TxExecutionError::InvalidTxError( + InvalidTxError::InvalidAccessKeyError(InvalidAccessKeyError::MethodNameMismatch { + method_name: "run_test".to_string() + }) + )) + ); +} + +pub fn test_access_key_smart_contract_reject_contract_id(node: impl Node) { + let access_key = AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(FUNCTION_CALL_AMOUNT), + receiver_id: bob_account().into(), + method_names: vec![], + }), + }; + let mut node_user = node.user(); + let account_id = &node.account_id().unwrap(); + let signer2 = InMemorySigner::from_random("test".parse().unwrap(), KeyType::ED25519); + add_access_key(&node, node_user.as_ref(), &access_key, &signer2); + node_user.set_signer(Arc::new(signer2)); + + let transaction_result = node_user + .function_call( + account_id.clone(), + eve_dot_alice_account(), + "run_test", + vec![], + 10u64.pow(14), + 0, + ) + .unwrap_err(); + assert_eq!( + transaction_result, + ServerError::TxExecutionError(TxExecutionError::InvalidTxError( + InvalidTxError::InvalidAccessKeyError(InvalidAccessKeyError::ReceiverMismatch { + tx_receiver: eve_dot_alice_account(), + ak_receiver: bob_account().into() + }) + )) + ); +} + +pub fn test_access_key_reject_non_function_call(node: impl Node) { + let account_id = &node.account_id().unwrap(); + let access_key = AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(FUNCTION_CALL_AMOUNT), + receiver_id: account_id.to_string(), + method_names: vec![], + }), + }; + let mut node_user = node.user(); + let signer2 = InMemorySigner::from_random("test".parse().unwrap(), KeyType::ED25519); + add_access_key(&node, node_user.as_ref(), &access_key, &signer2); + node_user.set_signer(Arc::new(signer2)); + + let transaction_result = + node_user.delete_key(account_id.clone(), node.signer().public_key()).unwrap_err(); + assert_eq!( + transaction_result, + ServerError::TxExecutionError(TxExecutionError::InvalidTxError( + InvalidTxError::InvalidAccessKeyError(InvalidAccessKeyError::RequiresFullAccess) + )) + ); +} + +pub fn test_increase_stake(node: impl Node) { + let node_user = node.user(); + let root = node_user.get_state_root(); + let account_id = &node.account_id().unwrap(); + let amount_staked = TESTING_INIT_STAKE + 1; + let fee_helper = fee_helper(&node); + let stake_cost = fee_helper.stake_cost(); + let transaction_result = node_user + .stake(account_id.clone(), node.block_signer().public_key(), amount_staked) + .unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + let node_user = node.user(); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + + let account = node_user.view_account(account_id).unwrap(); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE - 1 - stake_cost); + assert_eq!(account.locked, amount_staked) +} + +pub fn test_decrease_stake(node: impl Node) { + let node_user = node.user(); + let root = node_user.get_state_root(); + let amount_staked = 10; + let account_id = &node.account_id().unwrap(); + let transaction_result = node_user + .stake(account_id.clone(), node.block_signer().public_key(), amount_staked) + .unwrap(); + let fee_helper = fee_helper(&node); + let stake_cost = fee_helper.stake_cost(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + + let account = node_user.view_account(account_id).unwrap(); + assert_eq!(account.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE - stake_cost); + assert_eq!(account.locked, TESTING_INIT_STAKE); +} + +pub fn test_unstake_while_not_staked(node: impl Node) { + let node_user = node.user(); + let transaction_result = node_user + .create_account( + alice_account(), + eve_dot_alice_account(), + node.signer().public_key(), + TESTING_INIT_BALANCE / 2, + ) + .unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + let transaction_result = + node_user.stake(eve_dot_alice_account(), node.block_signer().public_key(), 0).unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::TriesToUnstake { account_id: eve_dot_alice_account() } + } + .into() + ) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 1); +} + +/// Account must have enough balance to cover storage of the account. +pub fn test_fail_not_enough_balance_for_storage(node: impl Node) { + let mut node_user = node.user(); + let account_id = bob_account(); + let signer = Arc::new(InMemorySigner::from_seed( + account_id.clone(), + KeyType::ED25519, + account_id.as_ref(), + )); + node_user.set_signer(signer); + node_user.send_money(account_id, alice_account(), 10).unwrap_err(); +} + +pub fn test_delete_account_ok(node: impl Node) { + let money_used = TESTING_INIT_BALANCE / 2; + let node_user = node.user(); + let _ = node_user.create_account( + alice_account(), + eve_dot_alice_account(), + node.signer().public_key(), + money_used, + ); + let transaction_result = + node_user.delete_account(eve_dot_alice_account(), eve_dot_alice_account()).unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert!(node.user().view_account(&eve_dot_alice_account()).is_err()); +} + +pub fn test_creating_invalid_subaccount_fail(node: impl Node) { + let money_used = TESTING_INIT_BALANCE / 2; + let node_user = node.user(); + let result = node_user + .create_account( + alice_account(), + x_dot_y_dot_alice_account(), + node.signer().public_key(), + money_used, + ) + .unwrap(); + assert_eq!( + result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::CreateAccountNotAllowed { + account_id: x_dot_y_dot_alice_account(), + predecessor_id: alice_account() + } + } + .into() + ) + ); +} + +pub fn test_delete_account_fail(node: impl Node) { + let money_used = TESTING_INIT_BALANCE / 2; + let node_user = node.user(); + let _ = node_user.create_account( + alice_account(), + eve_dot_alice_account(), + node.signer().public_key(), + money_used, + ); + let initial_amount = node_user.view_account(&node.account_id().unwrap()).unwrap().amount; + let fee_helper = fee_helper(&node); + let delete_account_cost = fee_helper.prepaid_delete_account_cost(); + + let transaction_result = + node_user.delete_account(alice_account(), eve_dot_alice_account()).unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::ActorNoPermission { + account_id: eve_dot_alice_account(), + actor_id: alice_account() + } + } + .into() + ) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + assert!(node.user().view_account(&bob_account()).is_ok()); + assert_eq!( + node.user().view_account(&node.account_id().unwrap()).unwrap().amount, + initial_amount - delete_account_cost + ); +} + +pub fn test_delete_account_no_account(node: impl Node) { + let node_user = node.user(); + let transaction_result = + node_user.delete_account(alice_account(), eve_dot_alice_account()).unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::AccountDoesNotExist { account_id: eve_dot_alice_account() } + } + .into() + ) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 2); +} + +pub fn test_delete_account_while_staking(node: impl Node) { + let money_used = TESTING_INIT_BALANCE / 2; + let node_user = node.user(); + let _ = node_user.create_account( + alice_account(), + eve_dot_alice_account(), + node.signer().public_key(), + money_used, + ); + let fee_helper = fee_helper(&node); + let stake_fee = fee_helper.stake_cost(); + let delete_account_fee = fee_helper.prepaid_delete_account_cost(); + let transaction_result = node_user + .stake( + eve_dot_alice_account(), + node.block_signer().public_key(), + money_used - stake_fee - delete_account_fee, + ) + .unwrap(); + assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new())); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + let transaction_result = + node_user.delete_account(eve_dot_alice_account(), eve_dot_alice_account()).unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::Failure( + ActionError { + index: Some(0), + kind: ActionErrorKind::DeleteAccountStaking { account_id: eve_dot_alice_account() } + } + .into() + ) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + assert!(node.user().view_account(&eve_dot_alice_account()).is_ok()); +} + +pub fn test_smart_contract_free(node: impl Node) { + let node_user = node.user(); + let root = node_user.get_state_root(); + let transaction_result = node_user + .function_call(alice_account(), bob_account(), "run_test", vec![], 10u64.pow(14), 0) + .unwrap(); + assert_eq!( + transaction_result.status, + FinalExecutionStatus::SuccessValue(10i32.to_le_bytes().to_vec()) + ); + assert_eq!(transaction_result.receipts_outcome.len(), 1); + + let total_gas_burnt = transaction_result.transaction_outcome.outcome.gas_burnt + + transaction_result.receipts_outcome.iter().map(|t| t.outcome.gas_burnt).sum::(); + assert_eq!(total_gas_burnt, 0); + + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); +} + +/// Get number of charged trie node accesses from the execution metadata. +fn get_trie_nodes_count( + metadata: &ExecutionMetadataView, + runtime_config: &RuntimeConfig, +) -> TrieNodesCount { + let mut count = TrieNodesCount { db_reads: 0, mem_reads: 0 }; + for cost in metadata.gas_profile.clone().unwrap_or_default().iter() { + match cost.cost.as_str() { + "TOUCHING_TRIE_NODE" => { + count.db_reads += cost.gas_used + / runtime_config.wasm_config.ext_costs.gas_cost(ExtCosts::touching_trie_node); + } + "READ_CACHED_TRIE_NODE" => { + count.mem_reads += cost.gas_used + / runtime_config + .wasm_config + .ext_costs + .gas_cost(ExtCosts::read_cached_trie_node); + } + _ => {} + }; + } + count +} + +/// Checks correctness of touching trie node cost for writing value into contract storage. +/// First call should touch 2 nodes (Extension and Branch), because before it contract storage is empty. +/// The second call should touch 4 nodes, because the first call adds Leaf and Value nodes to trie. +pub fn test_contract_write_key_value_cost(node: impl Node) { + let node_user = node.user(); + let results: Vec<_> = vec![ + TrieNodesCount { db_reads: 2, mem_reads: 0 }, + TrieNodesCount { db_reads: 4, mem_reads: 0 }, + ]; + for i in 0..2 { + let transaction_result = node_user + .function_call( + alice_account(), + bob_account(), + "write_key_value", + test_utils::encode(&[10u64, 20u64]), + 10u64.pow(14), + 0, + ) + .unwrap(); + assert_matches!(transaction_result.status, FinalExecutionStatus::SuccessValue(_)); + assert_eq!(transaction_result.receipts_outcome.len(), 2); + + let trie_nodes_count = get_trie_nodes_count( + &transaction_result.receipts_outcome[0].outcome.metadata, + &RuntimeConfig::test(), + ); + assert_eq!(trie_nodes_count, results[i]); + } +} + +fn make_write_key_value_action(key: Vec, value: Vec) -> Action { + let args: Vec = key.into_iter().chain(value.into_iter()).collect(); + FunctionCallAction { + method_name: "write_key_value".to_string(), + args: test_utils::encode(&args), + gas: 10u64.pow(14), + deposit: 0, + } + .into() +} + +fn make_receipt(node: &impl Node, actions: Vec, receiver_id: AccountId) -> Receipt { + let receipt_enum = ReceiptEnum::Action(ActionReceipt { + signer_id: alice_account(), + signer_public_key: node.signer().as_ref().public_key(), + gas_price: 0, + output_data_receivers: vec![], + input_data_ids: vec![], + actions, + }); + Receipt { + predecessor_id: alice_account(), + receiver_id, + receipt_id: CryptoHash::hash_borsh(&receipt_enum), + receipt: receipt_enum, + } +} + +/// Check that numbers of charged trie node accesses during execution of the given receipts matches the provided +/// results. +/// Runs the list of receipts 2 times. 1st run establishes the state structure, 2nd run is used to get node counts. +fn check_trie_nodes_count( + node: &impl Node, + runtime_config: &RuntimeConfig, + receipts: Vec, + results: Vec, + use_flat_storage: bool, +) { + let node_user = node.user(); + let mut node_touches: Vec<_> = vec![]; + let receipt_hashes: Vec = + receipts.iter().map(|receipt| receipt.receipt_id).collect(); + + for i in 0..2 { + node_user.add_receipts(receipts.clone(), use_flat_storage).unwrap(); + + if i == 1 { + node_touches = receipt_hashes + .iter() + .map(|receipt_hash| { + let result = node_user.get_transaction_result(receipt_hash); + get_trie_nodes_count(&result.metadata, runtime_config) + }) + .collect(); + } + } + + assert_eq!(node_touches, results); +} + +/// Check correctness of charging for trie node accesses with enabled chunk nodes cache. +/// We run the same set of receipts 2 times and compare resulting trie node counts. Each receipt writes some key-value +/// pair to the contract storage. +/// 1st run establishes the trie structure. For our needs, the structure is: +/// +/// --> (Leaf) -> (Value 1) +/// (Extension) -> (Branch) -> (Extension) -> (Branch) |-> (Leaf) -> (Value 2) +/// --> (Leaf) -> (Value 3) +/// +/// 1st receipt should count 6 db reads. +/// 2nd and 3rd receipts should count 2 db and 4 memory reads, because for them first 4 nodes were already put into the +/// accounting cache. +pub fn test_accounting_cache_common_parent(node: impl Node, runtime_config: RuntimeConfig) { + let receipts: Vec = (0..3) + .map(|i| { + make_receipt( + &node, + vec![make_write_key_value_action(vec![i], vec![10u64 + i])], + bob_account(), + ) + }) + .collect(); + + let results = vec![ + TrieNodesCount { db_reads: 6, mem_reads: 0 }, + TrieNodesCount { db_reads: 2, mem_reads: 4 }, + TrieNodesCount { db_reads: 2, mem_reads: 4 }, + ]; + check_trie_nodes_count(&node, &runtime_config, receipts, results, true); +} + +/// This test is similar to `test_accounting_cache_common_parent` but checks another trie structure: +/// +/// --> (Value 1) +/// (Extension) -> (Branch) -> (Extension) -> (Branch) |-> (Leaf) -> (Value 2) +/// +/// 1st receipt should count 5 db reads. +/// 2nd receipt should count 2 db and 4 memory reads. +pub fn test_accounting_cache_branch_value(node: impl Node, runtime_config: RuntimeConfig) { + let receipts: Vec = (0..2) + .map(|i| { + make_receipt( + &node, + vec![make_write_key_value_action(vec![1; i + 1], vec![10u64 + i as u64])], + bob_account(), + ) + }) + .collect(); + + let results = vec![ + TrieNodesCount { db_reads: 5, mem_reads: 0 }, + TrieNodesCount { db_reads: 2, mem_reads: 4 }, + ]; + check_trie_nodes_count(&node, &runtime_config, receipts, results, true); +} + +/// This test is similar to `test_accounting_cache_common_parent` but checks another trie structure: +/// +/// --> (Leaf) -> (Value 1) +/// (Extension) -> (Branch) --> (Extension) -> (Branch) |-> (Leaf) -> (Value 2) +/// |-> (Leaf) -> (Value 2) +/// +/// Here we check that accounting cache is enabled *only during function calls execution*. +/// 1st receipt writes `Value 1` and should count 6 db reads. +/// 2nd receipt deploys a new contract which *code* is the same as `Value 2`. But this value shouldn't be put into the +/// accounting cache. +/// 3rd receipt writes `Value 2` and should count 2 db and 4 memory reads. +/// +/// We have checked manually that if accounting cache mode is not disabled, then the following scenario happens: +/// - 1st receipt enables accounting cache mode but doesn't disable it +/// - 2nd receipt triggers insertion of `Value 2` into the accounting cache +/// - 3rd receipt reads it from the accounting cache, so it incorrectly charges user for 1 db and 5 memory reads. +pub fn test_accounting_cache_mode(node: impl Node, runtime_config: RuntimeConfig) { + let receipts: Vec = vec![ + make_receipt(&node, vec![make_write_key_value_action(vec![1], vec![1])], bob_account()), + make_receipt( + &node, + vec![DeployContractAction { code: test_utils::encode(&[2]) }.into()], + alice_account(), + ), + make_receipt(&node, vec![make_write_key_value_action(vec![2], vec![2])], bob_account()), + ]; + + let results = vec![ + TrieNodesCount { db_reads: 6, mem_reads: 0 }, + TrieNodesCount { db_reads: 0, mem_reads: 0 }, + TrieNodesCount { db_reads: 2, mem_reads: 4 }, + ]; + check_trie_nodes_count(&node, &runtime_config, receipts, results, true); +} + +/// Checks costs for subsequent `storage_read` and `storage_write` for the same key. +/// Depth for the storage key is 4. +/// Without flat storage, cost for reading Value is skipped due to a bug, so we count +/// 3 DB reads during read. During write, all nodes get cached, so we count 4 mem reads. +/// With flat storage, neither DB nor memory reads should be counted, as we skip Trie +/// node reads and don't charge for Value read due to the same bug. For write we go +/// to Trie and count 3 DB node reads and 1 mem read for Value. +pub fn test_storage_read_write_costs(node: impl Node, runtime_config: RuntimeConfig) { + let receipts: Vec = vec![ + make_receipt( + &node, + vec![FunctionCallAction { + args: test_utils::encode(&[1]), + method_name: "read_value".to_string(), + gas: 10u64.pow(14), + deposit: 0, + } + .into()], + bob_account(), + ), + make_receipt(&node, vec![make_write_key_value_action(vec![1], vec![20])], bob_account()), + ]; + + let results = vec![ + TrieNodesCount { db_reads: 3, mem_reads: 0 }, + TrieNodesCount { db_reads: 0, mem_reads: 4 }, + ]; + check_trie_nodes_count(&node, &runtime_config, receipts.clone(), results, false); + + let results = vec![ + TrieNodesCount { db_reads: 0, mem_reads: 0 }, + TrieNodesCount { db_reads: 3, mem_reads: 1 }, + ]; + check_trie_nodes_count(&node, &runtime_config, receipts, results, true); +} diff --git a/integration-tests/src/tests/standard_cases/rpc.rs b/integration-tests/src/tests/standard_cases/rpc.rs new file mode 100644 index 000000000..75371e5aa --- /dev/null +++ b/integration-tests/src/tests/standard_cases/rpc.rs @@ -0,0 +1,217 @@ +//! Runs standard test cases against testnet with several nodes running in separate threads. +//! The communication is performed through `RPCUser` that uses the standard RPC API to communicate. + +use crate::node::{create_nodes_from_seeds, Node, NodeConfig, ThreadNode}; +use crate::test_helpers::heavy_test; +use crate::tests::standard_cases::*; +use unc_o11y::testonly::init_test_module_logger; +use std::thread; +use std::time::Duration; +use testlib::runtime_utils::alice_account; + +fn create_thread_nodes_rpc() -> Vec { + init_test_module_logger("runtime"); + let nodes = create_nodes_from_seeds(vec![ + "alice.near".to_string(), + "bob.near".to_string(), + "carol.near".to_string(), + "dan.near".to_string(), + ]); + let mut nodes: Vec<_> = nodes + .into_iter() + .map(|cfg| match cfg { + NodeConfig::Thread(config) => ThreadNode::new(config), + _ => unreachable!(), + }) + .collect(); + let account_names: Vec<_> = nodes.iter().map(|node| node.account_id().unwrap()).collect(); + + assert_eq!(account_names[0], alice_account()); + for i in 0..nodes.len() { + nodes[i].start(); + } + // Let the nodes boot up a bit. + for _ in 0..100 { + let height = nodes[0].user().get_best_height(); + if height.is_some() && height.unwrap() > 1 { + break; + } + thread::sleep(Duration::from_millis(100)); + } + + nodes +} + +/// Macro for running testnet tests using ThreadNode and RPCUser. +/// Guard each test with heavy_test mutex. +macro_rules! run_testnet_test { + ($f:expr) => { + heavy_test(|| { + let mut nodes = create_thread_nodes_rpc(); + let node = nodes.remove(0); + $f(node) + }); + }; +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_smart_contract_simple_testnet() { + run_testnet_test!(test_smart_contract_simple); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_smart_contract_self_call_testnet() { + run_testnet_test!(test_smart_contract_self_call); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_smart_contract_bad_method_name_testnet() { + run_testnet_test!(test_smart_contract_bad_method_name); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_smart_contract_empty_method_name_with_no_tokens_testnet() { + run_testnet_test!(test_smart_contract_empty_method_name_with_no_tokens); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_smart_contract_empty_method_name_with_tokens_testnet() { + run_testnet_test!(test_smart_contract_empty_method_name_with_tokens); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_smart_contract_with_args_testnet() { + run_testnet_test!(test_smart_contract_with_args); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_nonce_update_when_deploying_contract_testnet() { + run_testnet_test!(test_nonce_update_when_deploying_contract); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_nonce_updated_when_tx_failed_testnet() { + run_testnet_test!(test_nonce_updated_when_tx_failed); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_upload_contract_testnet() { + run_testnet_test!(test_upload_contract); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_redeploy_contract_testnet() { + run_testnet_test!(test_redeploy_contract); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_send_money_testnet() { + run_testnet_test!(test_send_money); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_send_money_over_balance_testnet() { + run_testnet_test!(test_send_money_over_balance); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_refund_on_send_money_to_non_existent_account_testnet() { + run_testnet_test!(test_refund_on_send_money_to_non_existent_account); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_create_account_testnet() { + run_testnet_test!(test_create_account); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_create_account_again_testnet() { + run_testnet_test!(test_create_account_again); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_create_account_failure_already_exists_testnet() { + run_testnet_test!(test_create_account_failure_already_exists); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_swap_key_testnet() { + run_testnet_test!(test_swap_key); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_add_access_key_function_call_testnet() { + run_testnet_test!(test_add_access_key_function_call); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_add_existing_key_testnet() { + run_testnet_test!(test_add_existing_key); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_delete_key_testnet() { + run_testnet_test!(test_delete_key); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_delete_key_not_owned_testnet() { + run_testnet_test!(test_delete_key_not_owned); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_delete_key_last_testnet() { + run_testnet_test!(test_delete_key_last); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_add_key_testnet() { + run_testnet_test!(test_add_key); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_delete_access_key_testnet() { + run_testnet_test!(test_delete_access_key); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_add_access_key_with_allowance_testnet() { + run_testnet_test!(test_add_access_key_with_allowance); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_delete_access_key_with_allowance_testnet() { + run_testnet_test!(test_delete_access_key_with_allowance); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_access_key_smart_contract_testnet() { + run_testnet_test!(test_access_key_smart_contract); +} diff --git a/integration-tests/src/tests/standard_cases/runtime.rs b/integration-tests/src/tests/standard_cases/runtime.rs new file mode 100644 index 000000000..e98c21b50 --- /dev/null +++ b/integration-tests/src/tests/standard_cases/runtime.rs @@ -0,0 +1,371 @@ +use crate::node::RuntimeNode; +use crate::tests::standard_cases::*; +use unc_chain_configs::Genesis; +use unc_crypto::SecretKey; +use unc_primitives::checked_feature; +use unc_primitives::state_record::StateRecord; +use unc_primitives::version::PROTOCOL_VERSION; +use framework::config::{GenesisExt, TESTING_INIT_BALANCE}; +use testlib::runtime_utils::{add_test_contract, alice_account, bob_account}; + +fn create_runtime_node() -> RuntimeNode { + RuntimeNode::new(&alice_account()) +} + +fn create_free_runtime_node() -> RuntimeNode { + RuntimeNode::free(&alice_account()) +} + +fn create_runtime_with_expensive_storage() -> RuntimeNode { + let mut genesis = + Genesis::test(vec![alice_account(), bob_account(), "carol.near".parse().unwrap()], 1); + add_test_contract(&mut genesis, &bob_account()); + // Set expensive state requirements and add alice more money. + let mut runtime_config = RuntimeConfig::test(); + runtime_config.fees.storage_usage_config.storage_amount_per_byte = TESTING_INIT_BALANCE / 1000; + let records = genesis.force_read_records().as_mut(); + match &mut records[0] { + StateRecord::Account { account, .. } => account.set_amount(TESTING_INIT_BALANCE * 10000), + _ => { + panic!("the first record is expected to be alice account creation!"); + } + } + records.push(StateRecord::Data { + account_id: bob_account(), + data_key: b"test".to_vec().into(), + value: b"123".to_vec().into(), + }); + RuntimeNode::new_from_genesis_and_config(&alice_account(), genesis, runtime_config) +} + +#[test] +fn test_smart_contract_simple_runtime() { + let node = create_runtime_node(); + test_smart_contract_simple(node); +} + +#[test] +fn test_smart_contract_panic_runtime() { + let node = create_runtime_node(); + test_smart_contract_panic(node); +} + +#[test] +fn test_smart_contract_self_call_runtime() { + let node = create_runtime_node(); + test_smart_contract_self_call(node); +} + +#[test] +fn test_smart_contract_bad_method_name_runtime() { + let node = create_runtime_node(); + test_smart_contract_bad_method_name(node); +} + +#[test] +fn test_smart_contract_empty_method_name_with_no_tokens_runtime() { + let node = create_runtime_node(); + test_smart_contract_empty_method_name_with_no_tokens(node); +} + +#[test] +fn test_smart_contract_empty_method_name_with_tokens_runtime() { + let node = create_runtime_node(); + test_smart_contract_empty_method_name_with_tokens(node); +} + +#[test] +fn test_smart_contract_with_args_runtime() { + let node = create_runtime_node(); + test_smart_contract_with_args(node); +} + +#[test] +fn test_async_call_with_logs_runtime() { + let node = create_runtime_node(); + test_async_call_with_logs(node); +} + +#[test] +fn test_nonce_update_when_deploying_contract_runtime() { + let node = create_runtime_node(); + test_nonce_update_when_deploying_contract(node); +} + +#[test] +fn test_nonce_updated_when_tx_failed_runtime() { + let node = create_runtime_node(); + test_nonce_updated_when_tx_failed(node); +} + +#[test] +fn test_upload_contract_runtime() { + let node = create_runtime_node(); + test_upload_contract(node); +} + +#[test] +fn test_redeploy_contract_runtime() { + let node = create_runtime_node(); + test_redeploy_contract(node); +} + +#[test] +fn test_send_money_runtime() { + let node = create_runtime_node(); + test_send_money(node); +} + +#[test] +fn test_transfer_tokens_unc_implicit_account_runtime() { + let node = create_runtime_node(); + let public_key = node.user().signer().public_key(); + transfer_tokens_to_implicit_account(node, public_key); +} + +#[test] +fn test_transfer_tokens_eth_implicit_account_runtime() { + if !checked_feature!("stable", EthImplicitAccounts, PROTOCOL_VERSION) { + return; + } + let node = create_runtime_node(); + let secret_key = SecretKey::from_seed(KeyType::SECP256K1, "test"); + transfer_tokens_to_implicit_account(node, secret_key.public_key()); +} + +#[test] +fn test_trying_to_create_unc_implicit_account_runtime() { + let node = create_runtime_node(); + let public_key = node.user().signer().public_key(); + trying_to_create_implicit_account(node, public_key); +} + +#[test] +fn test_trying_to_create_eth_implicit_account_runtime() { + if !checked_feature!("stable", EthImplicitAccounts, PROTOCOL_VERSION) { + return; + } + let node = create_runtime_node(); + let secret_key = SecretKey::from_seed(KeyType::SECP256K1, "test"); + trying_to_create_implicit_account(node, secret_key.public_key()); +} + +#[test] +fn test_smart_contract_reward_runtime() { + let node = create_runtime_node(); + test_smart_contract_reward(node); +} + +#[test] +fn test_send_money_over_balance_runtime() { + let node = create_runtime_node(); + test_send_money_over_balance(node); +} + +#[test] +fn test_refund_on_send_money_to_non_existent_account_runtime() { + let node = create_runtime_node(); + test_refund_on_send_money_to_non_existent_account(node); +} + +#[test] +fn test_create_account_runtime() { + let node = create_runtime_node(); + test_create_account(node); +} + +#[test] +fn test_create_account_again_runtime() { + let node = create_runtime_node(); + test_create_account_again(node); +} + +#[test] +fn test_create_account_failure_no_funds_runtime() { + let node = create_runtime_node(); + test_create_account_failure_no_funds(node); +} + +#[test] +fn test_create_account_failure_already_exists_runtime() { + let node = create_runtime_node(); + test_create_account_failure_already_exists(node); +} + +#[test] +fn test_swap_key_runtime() { + let node = create_runtime_node(); + test_swap_key(node); +} + +#[test] +fn test_add_key_runtime() { + let node = create_runtime_node(); + test_add_key(node); +} + +#[test] +fn test_add_existing_key_runtime() { + let node = create_runtime_node(); + test_add_existing_key(node); +} + +#[test] +fn test_delete_key_runtime() { + let node = create_runtime_node(); + test_delete_key(node); +} + +#[test] +fn test_delete_key_not_owned_runtime() { + let node = create_runtime_node(); + test_delete_key_not_owned(node); +} + +#[test] +fn test_delete_key_last_runtime() { + let node = create_runtime_node(); + test_delete_key_last(node); +} + +#[test] +fn test_add_access_key_function_call_runtime() { + let node = create_runtime_node(); + test_add_access_key_function_call(node); +} + +#[test] +fn test_delete_access_key_runtime() { + let node = create_runtime_node(); + test_delete_access_key(node); +} + +#[test] +fn test_add_access_key_with_allowance_runtime() { + let node = create_runtime_node(); + test_add_access_key_with_allowance(node); +} + +#[test] +fn test_delete_access_key_with_allowance_runtime() { + let node = create_runtime_node(); + test_delete_access_key_with_allowance(node); +} + +#[test] +fn test_access_key_smart_contract_runtime() { + let node = create_runtime_node(); + test_access_key_smart_contract(node); +} + +#[test] +fn test_access_key_smart_contract_reject_method_name_runtime() { + let node = create_runtime_node(); + test_access_key_smart_contract_reject_method_name(node); +} + +#[test] +fn test_access_key_smart_contract_reject_contract_id_runtime() { + let node = create_runtime_node(); + test_access_key_smart_contract_reject_contract_id(node); +} + +#[test] +fn test_access_key_reject_non_function_call_runtime() { + let node = create_runtime_node(); + test_access_key_reject_non_function_call(node); +} + +#[test] +fn test_increase_stake_runtime() { + let node = create_runtime_node(); + test_increase_stake(node); +} + +#[test] +fn test_decrease_stake_runtime() { + let node = create_runtime_node(); + test_decrease_stake(node); +} + +#[test] +fn test_unstake_while_not_staked_runtime() { + let node = create_runtime_node(); + test_unstake_while_not_staked(node); +} + +#[test] +fn test_fail_not_enough_balance_for_storage_runtime() { + let node = create_runtime_with_expensive_storage(); + test_fail_not_enough_balance_for_storage(node); +} + +#[test] +fn test_delete_account_signer_is_receiver() { + let node = create_runtime_node(); + test_delete_account_ok(node); +} + +#[test] +fn test_creating_invalid_subaccount() { + let node = create_runtime_node(); + test_creating_invalid_subaccount_fail(node); +} + +#[test] +fn test_delete_account_has_enough_money_runtime() { + let node = create_runtime_node(); + test_delete_account_fail(node); +} + +#[test] +fn test_delete_account_no_account_runtime() { + let node = create_runtime_node(); + test_delete_account_no_account(node); +} + +#[test] +fn test_delete_account_while_staking_runtime() { + let node = create_runtime_node(); + test_delete_account_while_staking(node); +} + +#[test] +fn test_smart_contract_free_runtime() { + let node = create_free_runtime_node(); + test_smart_contract_free(node); +} + +#[test] +fn test_contract_write_key_value_cost_runtime() { + let node = create_runtime_node(); + test_contract_write_key_value_cost(node); +} + +#[test] +fn test_accounting_cache_same_common_parent() { + let node = create_runtime_node(); + let runtime_config = node.client.as_ref().read().unwrap().runtime_config.clone(); + test_accounting_cache_common_parent(node, runtime_config); +} + +#[test] +fn test_accounting_cache_branch_value_runtime() { + let node = create_runtime_node(); + let runtime_config = node.client.as_ref().read().unwrap().runtime_config.clone(); + test_accounting_cache_branch_value(node, runtime_config); +} + +#[test] +fn test_accounting_cache_mode_runtime() { + let node = create_runtime_node(); + let runtime_config = node.client.as_ref().read().unwrap().runtime_config.clone(); + test_accounting_cache_mode(node, runtime_config); +} + +#[test] +fn test_storage_read_write_costs_runtime() { + let node = create_runtime_node(); + let runtime_config = node.client.as_ref().read().unwrap().runtime_config.clone(); + test_storage_read_write_costs(node, runtime_config); +} diff --git a/integration-tests/src/tests/test_catchup.rs b/integration-tests/src/tests/test_catchup.rs new file mode 100644 index 000000000..f2a3f27cc --- /dev/null +++ b/integration-tests/src/tests/test_catchup.rs @@ -0,0 +1,66 @@ +//! Checks that late validator can catch-up and start validating. +use std::time::Duration; + +use crate::node::{create_nodes, Node}; +use crate::test_helpers::{heavy_test, wait}; +use std::sync::{Arc, RwLock}; + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_catchup() { + /// Creates a network of `num_nodes` nodes, but starts only `num_nodes - 1`. After + /// `num_blocks_to_wait` starts the last node and verifies that it can start validating within + /// `catchup_timeout`. + fn run_multiple_nodes( + num_nodes: usize, + num_blocks_to_wait: usize, + catchup_timeout: Duration, + block_generation_timeout: Duration, + test_prefix: &str, + ) { + let nodes = create_nodes(num_nodes, test_prefix); + + let mut nodes: Vec>> = + nodes.into_iter().map(|cfg| ::new_sharable(cfg)).collect(); + + let late_node = nodes.pop().unwrap(); + // Start all but one. + for node in &mut nodes { + node.write().unwrap().start(); + } + + // Wait for the blocks to be produced. + wait( + || { + if let Some(ind) = nodes[0].read().unwrap().user().get_best_height() { + ind > (num_blocks_to_wait as u64) + } else { + false + } + }, + 100, + block_generation_timeout.as_millis() as u64, + ); + + // Start the late node. + late_node.write().unwrap().start(); + + // Wait for it to have the same block height as other nodes. + wait( + || { + if let ind @ Some(_) = nodes[0].read().unwrap().user().get_best_height() { + late_node.read().unwrap().user().get_best_height() == ind + } else { + false + } + }, + 400, + catchup_timeout.as_millis() as u64, + ); + } + + heavy_test(|| { + // `num_min_peers` defaults to 3, and since the last node is not initially up and running, we need 5 peers total (4 to have 3 peers each launching initially + 1 launching later) + run_multiple_nodes(5, 20, Duration::from_secs(120), Duration::from_secs(60), "4_20") + }); +} diff --git a/integration-tests/src/tests/test_errors.rs b/integration-tests/src/tests/test_errors.rs new file mode 100644 index 000000000..31b5facd1 --- /dev/null +++ b/integration-tests/src/tests/test_errors.rs @@ -0,0 +1,104 @@ +use std::sync::Arc; + +use crate::node::{Node, ThreadNode}; +use unc_chain_configs::Genesis; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_jsonrpc::RpcInto; +use unc_network::tcp; +use unc_o11y::testonly::init_integration_logger; +use unc_parameters::{RuntimeConfig, RuntimeConfigStore}; +use unc_primitives::account::AccessKey; +use unc_primitives::errors::{InvalidAccessKeyError, InvalidTxError}; +use unc_primitives::transaction::{ + Action, AddKeyAction, CreateAccountAction, SignedTransaction, TransferAction, +}; +use unc_primitives::version::PROTOCOL_VERSION; +use framework::config::{GenesisExt, TESTING_INIT_BALANCE, TESTING_INIT_STAKE}; +use framework::load_test_config; +use testlib::runtime_utils::{alice_account, bob_account}; + +fn start_node() -> ThreadNode { + init_integration_logger(); + let genesis = Genesis::test(vec![alice_account(), bob_account()], 1); + let mut unc_config = + load_test_config("alice.near", tcp::ListenerAddr::reserve_for_test(), genesis); + unc_config.client_config.skip_sync_wait = true; + + let mut node = ThreadNode::new(unc_config); + node.start(); + node +} + +#[test] +fn test_check_tx_error_log() { + let node = start_node(); + let signer = + Arc::new(InMemorySigner::from_seed(alice_account(), KeyType::ED25519, "alice.near")); + let block_hash = node.user().get_best_block_hash().unwrap(); + let tx = SignedTransaction::from_actions( + 1, + bob_account(), + "test.near".parse().unwrap(), + &*signer, + vec![ + Action::CreateAccount(CreateAccountAction {}), + Action::Transfer(TransferAction { deposit: 1_000 }), + Action::AddKey(Box::new(AddKeyAction { + public_key: signer.public_key.clone(), + access_key: AccessKey::full_access(), + })), + ], + block_hash, + ); + + let tx_result = node.user().commit_transaction(tx).unwrap_err(); + assert_eq!( + tx_result, + InvalidTxError::InvalidAccessKeyError(InvalidAccessKeyError::AccessKeyNotFound { + account_id: bob_account(), + public_key: signer.public_key.clone().into() + }) + .rpc_into() + ); +} + +#[test] +fn test_deliver_tx_error_log() { + let node = start_node(); + let runtime_config_store = RuntimeConfigStore::new(None); + let runtime_config = runtime_config_store.get_config(PROTOCOL_VERSION); + let fee_helper = testlib::fees_utils::FeeHelper::new( + RuntimeConfig::clone(&runtime_config), + node.genesis().config.min_gas_price, + ); + let signer = + Arc::new(InMemorySigner::from_seed(alice_account(), KeyType::ED25519, "alice.near")); + let block_hash = node.user().get_best_block_hash().unwrap(); + let cost = fee_helper.create_account_transfer_full_key_cost_no_reward(); + let tx = SignedTransaction::from_actions( + 1, + alice_account(), + "test.near".parse().unwrap(), + &*signer, + vec![ + Action::CreateAccount(CreateAccountAction {}), + Action::Transfer(TransferAction { deposit: TESTING_INIT_BALANCE + 1 }), + Action::AddKey(Box::new(AddKeyAction { + public_key: signer.public_key.clone(), + access_key: AccessKey::full_access(), + })), + ], + block_hash, + ); + + let tx_result = node.user().commit_transaction(tx).unwrap_err(); + assert_eq!( + tx_result, + InvalidTxError::NotEnoughBalance { + signer_id: alice_account(), + balance: TESTING_INIT_BALANCE - TESTING_INIT_STAKE, + cost: TESTING_INIT_BALANCE + 1 + cost + } + .rpc_into() + ); +} diff --git a/integration-tests/src/tests/test_overflows.rs b/integration-tests/src/tests/test_overflows.rs new file mode 100644 index 000000000..8fdf1d317 --- /dev/null +++ b/integration-tests/src/tests/test_overflows.rs @@ -0,0 +1,21 @@ +#[test] +fn test_overflow() { + let a = u64::MAX; + let b = 5u64; + std::panic::catch_unwind(move || { + let c = u128::from(a + b); + println!("{} + {} = {}", a, b, c); + }) + .unwrap_err(); +} + +#[test] +fn test_underflow() { + let a = 10u64; + let b = 5u64; + std::panic::catch_unwind(move || { + let c = u128::from(b - a); + println!("{} - {} = {}", b, a, c); + }) + .unwrap_err(); +} diff --git a/integration-tests/src/tests/test_simple.rs b/integration-tests/src/tests/test_simple.rs new file mode 100644 index 000000000..b668ace45 --- /dev/null +++ b/integration-tests/src/tests/test_simple.rs @@ -0,0 +1,85 @@ +//! Simply starts and runs testnet for a while. +use crate::node::{create_nodes, sample_two_nodes, Node}; +use crate::test_helpers::{heavy_test, wait}; +use unc_o11y::testonly::init_integration_logger; +use unc_primitives::static_clock::StaticClock; +use unc_primitives::transaction::SignedTransaction; +use std::time::Duration; + +fn run_multiple_nodes(num_nodes: usize, num_trials: usize, test_prefix: &str) { + init_integration_logger(); + + let nodes = create_nodes(num_nodes, test_prefix); + let nodes: Vec<_> = nodes.into_iter().map(|cfg| ::new_sharable(cfg)).collect(); + let account_names: Vec<_> = + nodes.iter().map(|node| node.read().unwrap().account_id().unwrap()).collect(); + + for i in 0..num_nodes { + nodes[i].write().unwrap().start(); + } + + // waiting for nodes to be synced + let started = StaticClock::instant(); + loop { + if started.elapsed() > Duration::from_secs(10) { + panic!("nodes are not synced in 10s"); + } + let all_synced = + nodes.iter().all(|node| node.read().unwrap().view_account(&account_names[0]).is_ok()); + if all_synced { + break; + } + std::thread::sleep(Duration::from_millis(100)); + } + + // Execute N trials. In each trial we submit a transaction to a random node i, that sends + // 1 token to a random node j. We send transaction to node Then we wait for the balance change to propagate by checking + // the balance of j on node k. + let trial_duration = 60_000; + let amount_to_send = 100 * 10u128.pow(24); + for trial in 0..num_trials { + println!("TRIAL #{}", trial); + let (i, j) = sample_two_nodes(num_nodes); + let (k, r) = sample_two_nodes(num_nodes); + let nonce_i = + nodes[i].read().unwrap().get_access_key_nonce_for_signer(&account_names[i]).unwrap(); + let account_j = nodes[k].read().unwrap().view_account(&account_names[j]).unwrap(); + let transaction = SignedTransaction::send_money( + nonce_i + 1, + account_names[i].clone(), + account_names[j].clone(), + &*nodes[i].read().unwrap().signer(), + amount_to_send, + nodes[k].read().unwrap().user().get_best_block_hash().unwrap(), + ); + nodes[k].read().unwrap().add_transaction(transaction).unwrap(); + + wait( + || { + account_j.amount + < nodes[r].read().unwrap().view_balance(&account_names[j]).unwrap() + - amount_to_send * 9 / 10 + }, + 100, + trial_duration, + ); + } +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_2_10_multiple_nodes() { + heavy_test(|| run_multiple_nodes(2, 10, "2_10")); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_4_10_multiple_nodes() { + heavy_test(|| run_multiple_nodes(4, 10, "4_10")); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_7_10_multiple_nodes() { + heavy_test(|| run_multiple_nodes(7, 10, "7_10")); +} diff --git a/integration-tests/src/tests/test_tps_regression.rs b/integration-tests/src/tests/test_tps_regression.rs new file mode 100644 index 000000000..44cca43d9 --- /dev/null +++ b/integration-tests/src/tests/test_tps_regression.rs @@ -0,0 +1,166 @@ +//! Measures the input and the output transactions-per-seconds, compares it with the expected tps, +//! and verifies that the output tps is not much different from the input tps (makes sure there is +//! no choking on transactions). The input tps -- is how fast the nodes can be accepting +//! transactions. The output tps -- is how fast the nodes propagate transactions into the blocks. +use crate::node::{create_nodes, sample_queryable_node, sample_two_nodes, Node}; +use crate::test_helpers::heavy_test; +use unc_primitives::transaction::SignedTransaction; +use std::io::stdout; +use std::io::Write; +use std::sync::{Arc, RwLock}; +use std::thread; +use std::time::{Duration, Instant}; + +/// Creates and sends a random transaction. +/// Args: +/// `nodes`: node to submit to; +/// `nonces`: tracker of the nonces for the corresponding accounts. +/// `submitted_transactions`: (number of transactions, when these transactions were submitted). +fn send_transaction( + nodes: Vec>>, + nonces: Arc>>, + submitted_transactions: Arc>>, +) { + let (money_sender, money_receiver) = sample_two_nodes(nodes.len()); + let tx_receiver = money_sender; + // Update nonces. + let mut nonces = nonces.write().unwrap(); + nonces[money_sender] += 1; + let nonce = nonces[money_sender]; + + let sender_acc = nodes[money_sender].read().unwrap().account_id().unwrap(); + let receiver_acc = nodes[money_receiver].read().unwrap().account_id().unwrap(); + if let Some(block_hash) = nodes[tx_receiver].read().unwrap().user().get_best_block_hash() { + let transaction = SignedTransaction::send_money( + nonce, + sender_acc, + receiver_acc, + &*nodes[money_sender].read().unwrap().signer(), + 1, + block_hash, + ); + nodes[tx_receiver].read().unwrap().add_transaction(transaction).unwrap(); + submitted_transactions.write().unwrap().push(1); + } +} + +/// Creates a network of nodes and submits a large number of transactions to them. +/// Args: +/// * `num_nodes`: number of nodes to create; +/// * `tps`: transactions-per-second rate with which we submit transactions at even intervals; +/// * `target_tps`: the target output transactions-per-seconds of the network; +/// * `timeout`: how long this test should run. +fn run_multiple_nodes( + num_nodes: usize, + tps: usize, + target_tps: usize, + timeout: Duration, + test_prefix: &str, +) { + let nodes = create_nodes(num_nodes, test_prefix); + + let nodes: Vec>> = + nodes.into_iter().map(|cfg| ::new_sharable(cfg)).collect(); + for i in 0..num_nodes { + nodes[i].write().unwrap().start(); + } + + // Collection that stores #num of transactions -> when these transaction were submitted. + let submitted_transactions = Arc::new(RwLock::new(vec![])); + + // Create thread that submits transactions with high tps. + let transaction_handler = { + // Delay between transactions. + let tx_delay = + Duration::from_nanos((Duration::from_secs(1).as_nanos() as u64) / (tps as u64)); + let timeout = Instant::now() + timeout; + let nodes = nodes.to_vec(); + let submitted_transactions = submitted_transactions.clone(); + + thread::spawn(move || { + let nonces = vec![0u64; nodes.len()]; + let nonces = Arc::new(RwLock::new(nonces)); + while Instant::now() < timeout { + { + let nodes = nodes.to_vec(); + let nonces = nonces.clone(); + let submitted_transactions = submitted_transactions.clone(); + thread::spawn(move || send_transaction(nodes, nonces, submitted_transactions)); + } + thread::sleep(tx_delay); + } + }) + }; + + // Delay between checking the nodes. + let check_delay = Duration::from_millis(100); + // Collection that stores #num of transactions in a block -> when this block was observed. + let observed_transactions = Arc::new(RwLock::new(vec![])); + + // Create thread that observes new blocks and counts new transactions in them. + let observer_handler = { + let timeout = Instant::now() + timeout; + let observed_transactions = observed_transactions.clone(); + thread::spawn(move || { + let mut prev_ind = 0; + while Instant::now() < timeout { + // Get random node. + let node = &nodes[sample_queryable_node(&nodes)]; + if let Some(new_ind) = node.read().unwrap().user().get_best_height() { + if new_ind > prev_ind { + let blocks = ((prev_ind + 1)..=new_ind) + .filter_map(|idx| node.read().unwrap().user().get_block_by_height(idx)) + .collect::>(); + for b in &blocks { + let tx_num = b.chunks.iter().fold(0, |acc, chunk| { + if chunk.height_included == b.header.height { + let chunk = node + .read() + .unwrap() + .user() + .get_chunk_by_height(b.header.height, chunk.shard_id) + .unwrap(); + acc + chunk.transactions.len() + } else { + acc + } + }); + observed_transactions.write().unwrap().push(tx_num as u64); + } + prev_ind = new_ind; + } + } + thread::sleep(check_delay); + } + }) + }; + transaction_handler.join().unwrap(); + observer_handler.join().unwrap(); + + let submitted_xacts_num = submitted_transactions.read().unwrap().iter().sum::(); + let observed_xacts_num = observed_transactions.read().unwrap().iter().sum::(); + + let _ = + stdout().write(format!("Submitted transactions: {:?}; ", submitted_xacts_num).as_bytes()); + let _ = stdout().write(format!("Observed transactions: {:?}", observed_xacts_num).as_bytes()); + let _ = stdout().flush(); + + // Test that the network does not choke. The choke can be observed when the number of submitted + // transactions is not approx. the same the number of observed. + + // The difference is within 20%. + assert!( + (submitted_xacts_num as f64 - observed_xacts_num as f64).abs() + < u64::max(submitted_xacts_num, observed_xacts_num) as f64 * 0.2 + ); + + // Also verify that the average tps is within 20% of the target. + assert!((target_tps as f64) * 0.8 < (observed_xacts_num as f64 / timeout.as_secs_f64())); +} + +#[test] +#[cfg_attr(not(feature = "expensive_tests"), ignore)] +fn test_highload() { + // Run 4 nodes with 20 input tps and check the output tps to be 20. + heavy_test(|| run_multiple_nodes(4, 20, 20, Duration::from_secs(120), "4_20")); +} diff --git a/integration-tests/src/user/mod.rs b/integration-tests/src/user/mod.rs new file mode 100644 index 000000000..9b26018b9 --- /dev/null +++ b/integration-tests/src/user/mod.rs @@ -0,0 +1,347 @@ +use std::sync::Arc; + +use futures::{future::LocalBoxFuture, FutureExt}; + +use unc_crypto::{PublicKey, Signer}; +use unc_jsonrpc_primitives::errors::ServerError; +use unc_primitives::account::AccessKey; +use unc_primitives::action::delegate::{DelegateAction, NonDelegateAction, SignedDelegateAction}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::Receipt; +use unc_primitives::test_utils::create_user_test_signer; +use unc_primitives::transaction::{ + Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, + DeployContractAction, ExecutionOutcome, FunctionCallAction, SignedTransaction, StakeAction, + TransferAction, +}; +use unc_primitives::types::{AccountId, Balance, BlockHeight, Gas, MerkleHash, ShardId}; +use unc_primitives::views::{ + AccessKeyView, AccountView, BlockView, CallResult, ChunkView, ContractCodeView, + ExecutionOutcomeView, FinalExecutionOutcomeView, ViewStateResult, +}; + +pub use crate::user::runtime_user::RuntimeUser; + +pub mod rpc_user; +pub mod runtime_user; + +const POISONED_LOCK_ERR: &str = "The lock was poisoned."; + +pub trait User { + fn view_account(&self, account_id: &AccountId) -> Result; + + fn view_balance(&self, account_id: &AccountId) -> Result { + Ok(self.view_account(account_id)?.amount) + } + + fn view_contract_code(&self, account_id: &AccountId) -> Result; + + fn view_state(&self, account_id: &AccountId, prefix: &[u8]) -> Result; + + /// Returns whether the account is locked (has no access keys). + fn is_locked(&self, account_id: &AccountId) -> Result; + + fn view_call( + &self, + account_id: &AccountId, + method_name: &str, + args: &[u8], + ) -> Result; + + fn add_transaction(&self, signed_transaction: SignedTransaction) -> Result<(), ServerError>; + + fn commit_transaction( + &self, + signed_transaction: SignedTransaction, + ) -> Result; + + fn add_receipts( + &self, + receipts: Vec, + _use_flat_storage: bool, + ) -> Result<(), ServerError>; + + fn get_access_key_nonce_for_signer(&self, account_id: &AccountId) -> Result { + self.get_access_key(account_id, &self.signer().public_key()) + .map(|access_key| access_key.nonce) + } + + fn get_best_height(&self) -> Option; + + fn get_best_block_hash(&self) -> Option; + + fn get_block_by_height(&self, height: BlockHeight) -> Option; + + fn get_block(&self, block_hash: CryptoHash) -> Option; + + fn get_chunk_by_height(&self, height: BlockHeight, shard_id: ShardId) -> Option; + + fn get_transaction_result(&self, hash: &CryptoHash) -> ExecutionOutcomeView; + + fn get_transaction_final_result(&self, hash: &CryptoHash) -> FinalExecutionOutcomeView; + + fn get_state_root(&self) -> CryptoHash; + + fn get_access_key( + &self, + account_id: &AccountId, + public_key: &PublicKey, + ) -> Result; + + fn signer(&self) -> Arc; + + fn set_signer(&mut self, signer: Arc); + + fn sign_and_commit_actions( + &self, + signer_id: AccountId, + receiver_id: AccountId, + actions: Vec, + ) -> Result { + let block_hash = self.get_best_block_hash().unwrap_or_default(); + let signed_transaction = SignedTransaction::from_actions( + self.get_access_key_nonce_for_signer(&signer_id).unwrap_or_default() + 1, + signer_id, + receiver_id, + &*self.signer(), + actions, + block_hash, + ); + self.commit_transaction(signed_transaction) + } + + fn send_money( + &self, + signer_id: AccountId, + receiver_id: AccountId, + amount: Balance, + ) -> Result { + self.sign_and_commit_actions( + signer_id, + receiver_id, + vec![Action::Transfer(TransferAction { deposit: amount })], + ) + } + + fn deploy_contract( + &self, + signer_id: AccountId, + code: Vec, + ) -> Result { + self.sign_and_commit_actions( + signer_id.clone(), + signer_id, + vec![Action::DeployContract(DeployContractAction { code })], + ) + } + + fn function_call( + &self, + signer_id: AccountId, + contract_id: AccountId, + method_name: &str, + args: Vec, + gas: Gas, + deposit: Balance, + ) -> Result { + self.sign_and_commit_actions( + signer_id, + contract_id, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: method_name.to_string(), + args, + gas, + deposit, + }))], + ) + } + + fn create_account( + &self, + signer_id: AccountId, + new_account_id: AccountId, + public_key: PublicKey, + amount: Balance, + ) -> Result { + self.sign_and_commit_actions( + signer_id, + new_account_id, + vec![ + Action::CreateAccount(CreateAccountAction {}), + Action::Transfer(TransferAction { deposit: amount }), + Action::AddKey(Box::new(AddKeyAction { + public_key, + access_key: AccessKey::full_access(), + })), + ], + ) + } + + fn add_key( + &self, + signer_id: AccountId, + public_key: PublicKey, + access_key: AccessKey, + ) -> Result { + self.sign_and_commit_actions( + signer_id.clone(), + signer_id, + vec![Action::AddKey(Box::new(AddKeyAction { public_key, access_key }))], + ) + } + + fn delete_key( + &self, + signer_id: AccountId, + public_key: PublicKey, + ) -> Result { + self.sign_and_commit_actions( + signer_id.clone(), + signer_id, + vec![Action::DeleteKey(Box::new(DeleteKeyAction { public_key }))], + ) + } + + fn swap_key( + &self, + signer_id: AccountId, + old_public_key: PublicKey, + new_public_key: PublicKey, + access_key: AccessKey, + ) -> Result { + self.sign_and_commit_actions( + signer_id.clone(), + signer_id, + vec![ + Action::DeleteKey(Box::new(DeleteKeyAction { public_key: old_public_key })), + Action::AddKey(Box::new(AddKeyAction { public_key: new_public_key, access_key })), + ], + ) + } + + fn delete_account_with_beneficiary_set( + &self, + signer_id: AccountId, + receiver_id: AccountId, + beneficiary_id: AccountId, + ) -> Result { + self.sign_and_commit_actions( + signer_id, + receiver_id, + vec![Action::DeleteAccount(DeleteAccountAction { beneficiary_id })], + ) + } + + fn delete_account( + &self, + signer_id: AccountId, + receiver_id: AccountId, + ) -> Result { + self.delete_account_with_beneficiary_set(signer_id.clone(), receiver_id, signer_id) + } + + fn stake( + &self, + signer_id: AccountId, + public_key: PublicKey, + stake: Balance, + ) -> Result { + self.sign_and_commit_actions( + signer_id.clone(), + signer_id, + vec![Action::Stake(Box::new(StakeAction { stake, public_key }))], + ) + } + + /// Wrap the given actions in a delegate action and execute them. + /// + /// The signer signs the delegate action to be sent to the receiver. The + /// relayer packs that in a transaction and signs it . + fn meta_tx( + &self, + signer_id: AccountId, + receiver_id: AccountId, + relayer_id: AccountId, + actions: Vec, + ) -> Result { + let inner_signer = create_user_test_signer(&signer_id); + let user_nonce = self + .get_access_key(&signer_id, &inner_signer.public_key) + .expect("failed reading user's nonce for access key") + .nonce; + let delegate_action = DelegateAction { + sender_id: signer_id.clone(), + receiver_id, + actions: actions + .into_iter() + .map(|action| NonDelegateAction::try_from(action).unwrap()) + .collect(), + nonce: user_nonce + 1, + max_block_height: 100, + public_key: inner_signer.public_key(), + }; + let signature = inner_signer.sign(delegate_action.get_nep461_hash().as_bytes()); + let signed_delegate_action = SignedDelegateAction { delegate_action, signature }; + + self.sign_and_commit_actions( + relayer_id, + signer_id, + vec![Action::Delegate(Box::new(signed_delegate_action))], + ) + } +} + +/// Same as `User` by provides async API that can be used inside tokio. +pub trait AsyncUser: Send + Sync { + fn view_account( + &self, + account_id: &AccountId, + ) -> LocalBoxFuture<'static, Result>; + + fn view_balance( + &self, + account_id: &AccountId, + ) -> LocalBoxFuture<'static, Result> { + self.view_account(account_id).map(|res| res.map(|acc| acc.amount)).boxed_local() + } + + fn view_state( + &self, + account_id: &AccountId, + ) -> LocalBoxFuture<'static, Result>; + + fn add_transaction( + &self, + transaction: SignedTransaction, + ) -> LocalBoxFuture<'static, Result<(), ServerError>>; + + fn add_receipts( + &self, + receipts: &[Receipt], + ) -> LocalBoxFuture<'static, Result<(), ServerError>>; + + fn get_account_nonce( + &self, + account_id: &AccountId, + ) -> LocalBoxFuture<'static, Result>; + + fn get_best_height(&self) -> LocalBoxFuture<'static, Result>; + + fn get_transaction_result( + &self, + hash: &CryptoHash, + ) -> LocalBoxFuture<'static, Result>; + + fn get_transaction_final_result( + &self, + hash: &CryptoHash, + ) -> LocalBoxFuture<'static, Result>; + + fn get_state_root(&self) -> LocalBoxFuture<'static, Result>; + + fn get_access_key( + &self, + account_id: &AccountId, + public_key: &PublicKey, + ) -> LocalBoxFuture<'static, Result, ServerError>>; +} diff --git a/integration-tests/src/user/rpc_user.rs b/integration-tests/src/user/rpc_user.rs new file mode 100644 index 000000000..d34157d79 --- /dev/null +++ b/integration-tests/src/user/rpc_user.rs @@ -0,0 +1,232 @@ +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +use futures::{Future, TryFutureExt}; + +use unc_client::StatusResponse; +use unc_crypto::{PublicKey, Signer}; +use unc_jsonrpc::client::{new_client, JsonRpcClient}; +use unc_jsonrpc_client::ChunkId; +use unc_jsonrpc_primitives::errors::ServerError; +use unc_jsonrpc_primitives::types::query::{QueryResponseKind, RpcQueryRequest, RpcQueryResponse}; +use unc_jsonrpc_primitives::types::transactions::{RpcTransactionStatusRequest, TransactionInfo}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::Receipt; +use unc_primitives::serialize::to_base64; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{ + AccountId, BlockHeight, BlockId, BlockReference, EpochReference, ShardId, +}; +use unc_primitives::views::{ + AccessKeyView, AccountView, BlockView, CallResult, ChunkView, ContractCodeView, + EpochValidatorInfo, ExecutionOutcomeView, FinalExecutionOutcomeView, QueryRequest, + TxExecutionStatus, ViewStateResult, +}; + +use crate::user::User; + +pub struct RpcUser { + account_id: AccountId, + signer: Arc, + addr: String, +} + +impl RpcUser { + fn actix(&self, f: F) -> R + where + Fut: Future + 'static, + F: FnOnce(JsonRpcClient) -> Fut + 'static, + { + let addr = self.addr.clone(); + actix::System::new() + .block_on(async move { f(new_client(&format!("http://{}", addr))).await }) + } + + pub fn new(addr: &str, account_id: AccountId, signer: Arc) -> RpcUser { + RpcUser { account_id, addr: addr.to_owned(), signer } + } + + pub fn get_status(&self) -> Option { + self.actix(|client| client.status()).ok() + } + + pub fn query(&self, request: QueryRequest) -> Result { + let request = RpcQueryRequest { request, block_reference: BlockReference::latest() }; + self.actix(move |client| client.query(request).map_err(|err| err.to_string())) + } + + pub fn validators( + &self, + epoch_id_or_block_id: Option, + ) -> Result { + self.actix(move |client| { + client.validators(epoch_id_or_block_id).map_err(|err| err.to_string()) + }) + } +} + +impl User for RpcUser { + fn view_account(&self, account_id: &AccountId) -> Result { + let query = QueryRequest::ViewAccount { account_id: account_id.clone() }; + match self.query(query)?.kind { + QueryResponseKind::ViewAccount(account_view) => Ok(account_view), + _ => Err("Invalid type of response".into()), + } + } + + fn view_state(&self, account_id: &AccountId, prefix: &[u8]) -> Result { + let query = QueryRequest::ViewState { + account_id: account_id.clone(), + prefix: prefix.to_vec().into(), + include_proof: false, + }; + match self.query(query)?.kind { + QueryResponseKind::ViewState(view_state_result) => Ok(view_state_result), + _ => Err("Invalid type of response".into()), + } + } + + fn is_locked(&self, account_id: &AccountId) -> Result { + let query = QueryRequest::ViewAccessKeyList { account_id: account_id.clone() }; + match self.query(query)?.kind { + QueryResponseKind::AccessKeyList(access_keys) => Ok(access_keys.keys.is_empty()), + _ => Err("Invalid type of response".into()), + } + } + + fn view_contract_code(&self, account_id: &AccountId) -> Result { + let query = QueryRequest::ViewCode { account_id: account_id.clone() }; + match self.query(query)?.kind { + QueryResponseKind::ViewCode(contract_code_view) => Ok(contract_code_view), + _ => Err("Invalid type of response".into()), + } + } + + fn view_call( + &self, + account_id: &AccountId, + method_name: &str, + args: &[u8], + ) -> Result { + let query = QueryRequest::CallFunction { + account_id: account_id.clone(), + method_name: method_name.to_string(), + args: args.to_vec().into(), + }; + match self.query(query)?.kind { + QueryResponseKind::CallResult(call_result) => Ok(call_result), + _ => Err("Invalid type of response".into()), + } + } + + fn add_transaction(&self, transaction: SignedTransaction) -> Result<(), ServerError> { + let bytes = borsh::to_vec(&transaction).unwrap(); + let _ = self.actix(move |client| client.broadcast_tx_async(to_base64(&bytes))).map_err( + |err| { + serde_json::from_value::( + err.data.expect("server error must carry data"), + ) + .expect("deserialize server error must be ok") + }, + )?; + Ok(()) + } + + fn commit_transaction( + &self, + transaction: SignedTransaction, + ) -> Result { + let bytes = borsh::to_vec(&transaction).unwrap(); + let result = self.actix(move |client| client.broadcast_tx_commit(to_base64(&bytes))); + // Wait for one more block, to make sure all nodes actually apply the state transition. + let height = self.get_best_height().unwrap(); + while height == self.get_best_height().unwrap() { + thread::sleep(Duration::from_millis(50)); + } + match result { + Ok(outcome) => Ok(outcome.final_execution_outcome.unwrap().into_outcome()), + Err(err) => Err(serde_json::from_value::(err.data.unwrap()).unwrap()), + } + } + + fn add_receipts( + &self, + _receipts: Vec, + _use_flat_storage: bool, + ) -> Result<(), ServerError> { + // TDDO: figure out if rpc will support this + unimplemented!() + } + + fn get_best_height(&self) -> Option { + self.get_status().map(|status| status.sync_info.latest_block_height) + } + + fn get_best_block_hash(&self) -> Option { + self.get_status().map(|status| status.sync_info.latest_block_hash) + } + + fn get_block_by_height(&self, height: BlockHeight) -> Option { + self.actix(move |client| client.block(BlockReference::BlockId(BlockId::Height(height)))) + .ok() + } + + fn get_block(&self, block_hash: CryptoHash) -> Option { + self.actix(move |client| client.block(BlockReference::BlockId(BlockId::Hash(block_hash)))) + .ok() + } + + fn get_chunk_by_height(&self, height: BlockHeight, shard_id: ShardId) -> Option { + self.actix(move |client| { + client.chunk(ChunkId::BlockShardId(BlockId::Height(height), shard_id)) + }) + .ok() + } + + fn get_transaction_result(&self, _hash: &CryptoHash) -> ExecutionOutcomeView { + unimplemented!() + } + + fn get_transaction_final_result(&self, hash: &CryptoHash) -> FinalExecutionOutcomeView { + let request = RpcTransactionStatusRequest { + transaction_info: TransactionInfo::TransactionId { + tx_hash: *hash, + sender_account_id: self.account_id.clone(), + }, + wait_until: TxExecutionStatus::Final, + }; + self.actix(move |client| client.tx(request)) + .unwrap() + .final_execution_outcome + .unwrap() + .into_outcome() + } + + fn get_state_root(&self) -> CryptoHash { + self.get_status().map(|status| status.sync_info.latest_state_root).unwrap() + } + + fn get_access_key( + &self, + account_id: &AccountId, + public_key: &PublicKey, + ) -> Result { + let query = QueryRequest::ViewAccessKey { + account_id: account_id.clone(), + public_key: public_key.clone(), + }; + match self.query(query)?.kind { + QueryResponseKind::AccessKey(access_key) => Ok(access_key), + _ => Err("Invalid type of response".into()), + } + } + + fn signer(&self) -> Arc { + self.signer.clone() + } + + fn set_signer(&mut self, signer: Arc) { + self.signer = signer; + } +} diff --git a/integration-tests/src/user/runtime_user.rs b/integration-tests/src/user/runtime_user.rs new file mode 100644 index 000000000..7eaf5230a --- /dev/null +++ b/integration-tests/src/user/runtime_user.rs @@ -0,0 +1,372 @@ +use std::cell::RefCell; +use std::collections::{HashMap, HashSet}; +use std::sync::{Arc, RwLock}; + +use unc_crypto::{PublicKey, Signer}; +use unc_jsonrpc_primitives::errors::ServerError; +use unc_parameters::RuntimeConfig; +use unc_primitives::errors::{RuntimeError, TxExecutionError}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::Receipt; +use unc_primitives::runtime::migration_data::{MigrationData, MigrationFlags}; +use unc_primitives::test_utils::MockEpochInfoProvider; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{AccountId, BlockHeightDelta, MerkleHash}; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_primitives::views::{ + AccessKeyView, AccountView, BlockView, CallResult, ChunkView, ContractCodeView, + ExecutionOutcomeView, ExecutionOutcomeWithIdView, ExecutionStatusView, + FinalExecutionOutcomeView, FinalExecutionStatus, ViewApplyState, ViewStateResult, +}; +use unc_store::{ShardTries, TrieUpdate}; +use framework::config::MIN_GAS_PRICE; +use node_runtime::state_viewer::TrieViewer; +use node_runtime::{ApplyState, Runtime}; + +use crate::user::{User, POISONED_LOCK_ERR}; +use unc_primitives::shard_layout::ShardUId; + +/// Mock client without chain, used in RuntimeUser and RuntimeNode +pub struct MockClient { + pub runtime: Runtime, + pub tries: ShardTries, + pub state_root: MerkleHash, + pub epoch_length: BlockHeightDelta, + pub runtime_config: RuntimeConfig, +} + +impl MockClient { + pub fn get_state_update(&self) -> TrieUpdate { + self.tries.new_trie_update(ShardUId::single_shard(), self.state_root) + } +} + +pub struct RuntimeUser { + pub account_id: AccountId, + pub signer: Arc, + pub trie_viewer: TrieViewer, + pub client: Arc>, + // Store results of applying transactions/receipts + pub transaction_results: RefCell>, + // store receipts generated when applying transactions + pub receipts: RefCell>, + pub transactions: RefCell>, + pub epoch_info_provider: MockEpochInfoProvider, + pub runtime_config: Arc, +} + +impl RuntimeUser { + pub fn new( + account_id: AccountId, + signer: Arc, + client: Arc>, + ) -> Self { + let runtime_config = Arc::new(client.read().unwrap().runtime_config.clone()); + RuntimeUser { + signer, + trie_viewer: TrieViewer::default(), + account_id, + client, + transaction_results: Default::default(), + receipts: Default::default(), + transactions: RefCell::new(Default::default()), + epoch_info_provider: MockEpochInfoProvider::default(), + runtime_config, + } + } + + pub fn apply_all( + &self, + apply_state: ApplyState, + prev_receipts: Vec, + transactions: Vec, + use_flat_storage: bool, + ) -> Result<(), ServerError> { + let mut receipts = prev_receipts; + for transaction in transactions.iter() { + self.transactions.borrow_mut().insert(transaction.clone()); + } + let mut txs = transactions; + loop { + let mut client = self.client.write().expect(POISONED_LOCK_ERR); + let trie = if use_flat_storage { + client.tries.get_trie_with_block_hash_for_shard( + ShardUId::single_shard(), + client.state_root, + &CryptoHash::default(), + false, + ) + } else { + client.tries.get_trie_for_shard(ShardUId::single_shard(), client.state_root) + }; + let apply_result = client + .runtime + .apply( + trie, + &None, + &apply_state, + &receipts, + &txs, + &self.epoch_info_provider, + Default::default(), + ) + .map_err(|e| match e { + RuntimeError::InvalidTxError(e) => { + ServerError::TxExecutionError(TxExecutionError::InvalidTxError(e)) + } + RuntimeError::BalanceMismatchError(e) => panic!("{}", e), + RuntimeError::StorageError(e) => panic!("Storage error {:?}", e), + RuntimeError::UnexpectedIntegerOverflow => { + panic!("UnexpectedIntegerOverflow error") + } + RuntimeError::ReceiptValidationError(e) => panic!("{}", e), + RuntimeError::ValidatorError(e) => panic!("{}", e), + })?; + for outcome_with_id in apply_result.outcomes { + self.transaction_results + .borrow_mut() + .insert(outcome_with_id.id, outcome_with_id.outcome.into()); + } + let mut update = client.tries.store_update(); + client.tries.apply_all( + &apply_result.trie_changes, + ShardUId::single_shard(), + &mut update, + ); + if use_flat_storage { + unc_store::flat::FlatStateChanges::from_state_changes(&apply_result.state_changes) + .apply_to_flat_state(&mut update, ShardUId::single_shard()); + } + update.commit().unwrap(); + client.state_root = apply_result.state_root; + if apply_result.outgoing_receipts.is_empty() { + return Ok(()); + } + for receipt in apply_result.outgoing_receipts.iter() { + self.receipts.borrow_mut().insert(receipt.receipt_id, receipt.clone()); + } + receipts = apply_result.outgoing_receipts; + txs = vec![]; + } + } + + fn apply_state(&self) -> ApplyState { + ApplyState { + block_height: 1, + prev_block_hash: Default::default(), + block_hash: Default::default(), + block_timestamp: 0, + epoch_height: 0, + gas_price: MIN_GAS_PRICE, + gas_limit: None, + random_seed: Default::default(), + epoch_id: Default::default(), + current_protocol_version: PROTOCOL_VERSION, + config: self.runtime_config.clone(), + cache: None, + is_new_chunk: true, + migration_data: Arc::new(MigrationData::default()), + migration_flags: MigrationFlags::default(), + } + } + + fn get_recursive_transaction_results( + &self, + hash: &CryptoHash, + ) -> Vec { + let outcome = self.get_transaction_result(hash); + let receipt_ids = outcome.receipt_ids.clone(); + let mut transactions = vec![ExecutionOutcomeWithIdView { + id: *hash, + outcome, + proof: vec![], + block_hash: Default::default(), + }]; + for hash in &receipt_ids { + transactions.extend(self.get_recursive_transaction_results(hash).into_iter()); + } + transactions + } + + fn get_final_transaction_result(&self, hash: &CryptoHash) -> FinalExecutionOutcomeView { + let mut outcomes = self.get_recursive_transaction_results(hash); + let mut looking_for_id = *hash; + let num_outcomes = outcomes.len(); + let status = outcomes + .iter() + .find_map(|outcome_with_id| { + if outcome_with_id.id == looking_for_id { + match &outcome_with_id.outcome.status { + ExecutionStatusView::Unknown if num_outcomes == 1 => { + Some(FinalExecutionStatus::NotStarted) + } + ExecutionStatusView::Unknown => Some(FinalExecutionStatus::Started), + ExecutionStatusView::Failure(e) => { + Some(FinalExecutionStatus::Failure(e.clone())) + } + ExecutionStatusView::SuccessValue(v) => { + Some(FinalExecutionStatus::SuccessValue(v.clone())) + } + ExecutionStatusView::SuccessReceiptId(id) => { + looking_for_id = *id; + None + } + } + } else { + None + } + }) + .expect("results should resolve to a final outcome"); + let receipts = outcomes.split_off(1); + let transaction = self.transactions.borrow().get(hash).unwrap().clone().into(); + FinalExecutionOutcomeView { + status, + transaction, + transaction_outcome: outcomes.pop().unwrap(), + receipts_outcome: receipts, + } + } +} + +impl User for RuntimeUser { + fn view_account(&self, account_id: &AccountId) -> Result { + let state_update = self.client.read().expect(POISONED_LOCK_ERR).get_state_update(); + self.trie_viewer + .view_account(&state_update, account_id) + .map(|account| account.into()) + .map_err(|err| err.to_string()) + } + + fn view_contract_code(&self, account_id: &AccountId) -> Result { + let state_update = self.client.read().expect(POISONED_LOCK_ERR).get_state_update(); + self.trie_viewer + .view_contract_code(&state_update, account_id) + .map(|contract_code| contract_code.into()) + .map_err(|err| err.to_string()) + } + + fn view_state(&self, account_id: &AccountId, prefix: &[u8]) -> Result { + let state_update = self.client.read().expect(POISONED_LOCK_ERR).get_state_update(); + self.trie_viewer + .view_state(&state_update, account_id, prefix, false) + .map_err(|err| err.to_string()) + } + + fn is_locked(&self, account_id: &AccountId) -> Result { + let state_update = self.client.read().expect(POISONED_LOCK_ERR).get_state_update(); + self.trie_viewer + .view_access_keys(&state_update, account_id) + .map(|access_keys| access_keys.is_empty()) + .map_err(|err| err.to_string()) + } + + fn view_call( + &self, + account_id: &AccountId, + method_name: &str, + args: &[u8], + ) -> Result { + let apply_state = self.apply_state(); + let client = self.client.read().expect(POISONED_LOCK_ERR); + let state_update = client.get_state_update(); + let mut result = CallResult::default(); + let view_state = ViewApplyState { + block_height: apply_state.block_height, + prev_block_hash: apply_state.prev_block_hash, + block_hash: apply_state.block_hash, + epoch_id: apply_state.epoch_id, + epoch_height: apply_state.epoch_height, + block_timestamp: apply_state.block_timestamp, + current_protocol_version: PROTOCOL_VERSION, + cache: apply_state.cache, + }; + result.result = self + .trie_viewer + .call_function( + state_update, + view_state, + account_id, + method_name, + args, + &mut result.logs, + &self.epoch_info_provider, + ) + .map_err(|err| err.to_string())?; + Ok(result) + } + + fn add_transaction(&self, transaction: SignedTransaction) -> Result<(), ServerError> { + self.apply_all(self.apply_state(), vec![], vec![transaction], true)?; + Ok(()) + } + + fn commit_transaction( + &self, + transaction: SignedTransaction, + ) -> Result { + self.apply_all(self.apply_state(), vec![], vec![transaction.clone()], true)?; + Ok(self.get_transaction_final_result(&transaction.get_hash())) + } + + fn add_receipts( + &self, + receipts: Vec, + use_flat_storage: bool, + ) -> Result<(), ServerError> { + self.apply_all(self.apply_state(), receipts, vec![], use_flat_storage)?; + Ok(()) + } + + fn get_best_height(&self) -> Option { + unimplemented!("get_best_height should not be implemented for RuntimeUser"); + } + + // This function is needed to sign transactions + fn get_best_block_hash(&self) -> Option { + Some(CryptoHash::default()) + } + + fn get_block_by_height(&self, _height: u64) -> Option { + unimplemented!("get_block should not be implemented for RuntimeUser"); + } + + fn get_block(&self, _block_hash: CryptoHash) -> Option { + None + } + + fn get_chunk_by_height(&self, _height: u64, _shard_id: u64) -> Option { + unimplemented!("get_chunk should not be implemented for RuntimeUser"); + } + + fn get_transaction_result(&self, hash: &CryptoHash) -> ExecutionOutcomeView { + self.transaction_results.borrow().get(hash).cloned().unwrap() + } + + fn get_transaction_final_result(&self, hash: &CryptoHash) -> FinalExecutionOutcomeView { + self.get_final_transaction_result(hash) + } + + fn get_state_root(&self) -> CryptoHash { + self.client.read().expect(POISONED_LOCK_ERR).state_root + } + + fn get_access_key( + &self, + account_id: &AccountId, + public_key: &PublicKey, + ) -> Result { + let state_update = self.client.read().expect(POISONED_LOCK_ERR).get_state_update(); + self.trie_viewer + .view_access_key(&state_update, account_id, public_key) + .map(|access_key| access_key.into()) + .map_err(|err| err.to_string()) + } + + fn signer(&self) -> Arc { + self.signer.clone() + } + + fn set_signer(&mut self, signer: Arc) { + self.signer = signer; + } +} diff --git a/lychee.toml b/lychee.toml new file mode 100644 index 000000000..4ff1a596c --- /dev/null +++ b/lychee.toml @@ -0,0 +1,13 @@ +exclude = [ + # these are private repositories, presumably any link to it are in internal documentation only. + "^https://github.com/utnet-org/utility-private", + + # localhost is linked to refer to services supposed to be running on the host. + "^http://localhost", + + # jsonrpc internal links + "^file://.*/chain/jsonrpc/res/debug/", + + # used as placeholders for a template + "^https://github.com/utnet-org/utility/pull/XXXX$", +] diff --git a/new_script.sh b/new_script.sh new file mode 100755 index 000000000..8e974b62e --- /dev/null +++ b/new_script.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# run nodes +sleep 2 +target/debug/uncd --home "/Users/jameswalstonn/.unc/Unc5" run --boot-nodes "ed25519:9e9JtarsJc3JR1PcnU6ykQgUmEf6LCAQi4ZjZjMuxrip@127.0.0.1:24567" > "/Users/jameswalstonn/.unc/logfiles/logfile5" 2>&1 & + +# create new account +sleep 2 +near create-account node5-validator --masterAccount jamesavechives --publicKey ed25519:3xtwP9QUaUrjZyH4HXYhamZ357rWEFJZGw4bR1Z6peYm + +## send money to new accounts +sleep 2 +near send jamesavechives node5-validator 20000000 + +## stake new accounts +sleep 2 +near stake node5-validator ed25519:3xtwP9QUaUrjZyH4HXYhamZ357rWEFJZGw4bR1Z6peYm 10000000 --keyPath /Users/jameswalstonn/.unc/Unc5/validator_key.json + + +# Register Rsa2048 keys +sleep 5 +#/Library/WebServer/Documents/utility-cli-rs/target/debug/near extensions register-rsa-keys unc use-file /Users/jameswalstonn/.unc/Unc1/signer3_key.json with-init-call json-args '{"power": "8000000000000"}' network-config my-private-chain-id sign-with-plaintext-private-key --signer-public-key ed25519:8varMxFYfy9MprpoK2wCeYCBdLs9j4tTFWwS31haQfTL --signer-private-key ed25519:4xwMSJ5rRjWPLRn28uzoyJGpzSUSBPsHfLBkhcFnhU3YdXewCWxQEUqjjqciqJbisrXfZUJ3KBn1kXntKpkq8BkS send + + +# Chanllenge Rsa2048 keys +sleep 5 +#/Library/WebServer/Documents/utility-cli-rs/target/debug/near extensions create-challenge-rsa node5-validator use-file /Users/jameswalstonn/.unc/Unc1/signer3_key.json without-init-call network-config my-private-chain-id sign-with-plaintext-private-key --signer-public-key ed25519:3xtwP9QUaUrjZyH4HXYhamZ357rWEFJZGw4bR1Z6peYm --signer-private-key ed25519:bVBPVrBNY1doZXezeFn9PPJyqAh8Wke9rASdqGoSt6GcBJMTTv2v8UjASqjWBGFTHvw2wqNg9vj3y3J4vyc2ebF send + diff --git a/nightly/README.md b/nightly/README.md new file mode 100644 index 000000000..b66d7092d --- /dev/null +++ b/nightly/README.md @@ -0,0 +1,201 @@ +# Nightly tests lists + +The directory contains test list files which can be sent to NayDuck to +request a run of the tests. Most notably, `nightly.txt` file contains +all the tests that NayDuck runs once a day on the head of the master +branch of the repository. + +Nightly build results are available on [NayDuck](https://nayduck.near.org/). + +## List file format + +Each list file is read line-by line. Empty lines and lines starting +with a hash are ignored. The rest either specifies a test to run or +a list file to include. The general syntax of a line defining a test +is: + + [--skip-build] [--timeout=] [--release] [--remote] ... [--features ] + +`` specifies the category of the test and can be `pytest` or +`expensive`. The meaning of `` depends on the test category. + +### pytest tests + +The `pytest` tests are run by executing a file within `pytest/tests` +directory. The file is executed via `python` interpreter (and +confusingly not via `pytest` command) so it must actually run the +tests rather than only defining them as test functions. + +In the test specification path to the file needs to be given +(excluding the `pytest/tests` prefix) and anything that follows is +passed as arguments to the script. For example: + + pytest sanity/lightclnt.py + pytest sanity/state_sync_routed.py manytx 115 + +Note: NayDuck also handles `mocknet` test category. It is now +deprecated and is treated like `pytest` with `--skip-build` flag +implicitly set. + +### expensive tests + +The `expensive` tests run a test binary and execute specific test in +it. (Test binaries are those built via `cargo test --no-run`). While +this can be used to run any Rust test, the intention is to run +expensive tests only. Those are the tests which are ignored unless +`expensive_tests` crate feature is enabled. Such tests should be +marked with a `cfg_attr` macro, e.g.: + + #[test] + #[cfg_attr(not(feature = "expensive_tests"), ignore)] + fn test_gc_boundaries_large() { + /* ... */ + } + +The arguments of an expensive test specify package in which the test +is defined, test binary name and the full path to the test function. +For example: + + expensive framework test_tps_regression test::test_highload + +(Currently the package name is ignored but it may change in the future +so make sure it’s set correctly). The path to the test function must +match exactly an the test binary is called with `--exact` argument. + +### Other arguments + +As mentioned, there are additional arguments that can go between the +test category and the test specification arguments. Those are +`--skip-build`, `--timeout`, `--release` and `--remote`. + +`--skip-build` causes build step to be skipped for the test. This +means that the test doesn’t have access to build artefacts (located in +`target/debug` or `target/release`) but also doesn’t need to wait for +the build to finish and thus can start faster. + +`--timeout=` specifies the time after which the test will be +stopped and considered failed. `` is an integer with an +optional `s`, `m` or `h` suffix. If no suffix is given, `s` is +assumed. The default timeout is three minutes. For example, the +following increases timeout for a test to four minutes: + + pytest --timeout=4m sanity/validator_switch.py + +`--release` makes the build use a release profile rather than a dev +profile. In other words, all `cargo` invocations are passed +additional `--release` argument. This is supported but currently not +used by any of the nightly tests. + +`--remote` configures pytest tests to use unc test nodes started on +spot GCP machines rather than executing a small cluster on host where +the test is running. No nightly test uses this feature and to be +honest I can’t vouch whether it even works. + +Lastly, at the end of the test specification line additional features +can be given in the form of `--features ` arguments. +Similarly to `--release`, this results in given features being enabled +in builds. Note that the `test_features` Cargo feature is always +enabled so there's no need to specify it explicitly. + +Note that with `--skip-build` switch the `--release` and `--features` +flags are essentially ignored since they only affect the build and are +not passed to the test. + +### Include directive + +To help organise tests, the file format also supports `./` +syntax for including contents of other files in the list. The +includes are handled recursively though at the moment there’s a limit +of three levels before the parser starts ignoring the includes. + +For example, `nightly.txt` file may just look as follows: + + ./sandbox.txt + ./pytest.txt + ./expensive.txt + +with individual tests listed in each of the referenced files. This +makes the files more tidy. + +Note that any includes accessible from `nightly.txt` file must live +within the `nightly` directory and use `.txt` file extension. Using +arbitrary paths and extensions will work locally but it will break +NayDuck’s nightly runs. + + +## Scheduling a run + +Every 24 hours NayDuck checks if master branch has changed and if it +has schedules a new run including all tests listed in the +`nightly.txt` file. It’s also possible to request a run manually in +which case arbitrary set of tests can be run on an arbitrary commit +(so long as it exists in the utnet-org/utility repository). + +This can be done with `nayduck.py` script which takes the list file as +an argument. For example, to run spec tests one might invoke: + + ./scripts/nayduck.py -t nightly/pytest-spec.txt + +With no other arguments the tests will be run against checked out +commit in the current branch. It is possible to specify which branch +and commit to run against with `--branch` and `--sha` arguments. For +full usage refer to `./scripts/nayduck.py --help` output. + +NayDuck cannot run tests against local working directory or even +commits in a private fork of the framework repository. To schedule +a NayDuck run, the commit must first be pushed to framework. The +commit does not need to be on master branch; testing a feature branch +is supported. + +On success the script outputs link to the page which can be used to +see status of the run. Depending on which tests were scheduled the +run can take over an hour to finish. + + +## Creating new tests + +New tests can be created either as a Rust test or a pytest. + +If a Rust test is long-running (or otherwise requires a lot of +resources) and intended to be run as a nightly test on NayDuck it +should be marked with a `#[cfg(feature = "expensive_tests")]` +directive (either on the test function or module containing it). With +that, the tests will not be part of a `cargo test` run performed on +every commit, but NayDuck will be able to execute them. Apart from +that, expensive Rust tests work exactly the same as any other Rust +tests. + +pytests are defined as scripts in the `pytest/tests` directory. As +previously mentioned, even though the directory is called pytest, when +run on NayDuck they scripts are run directly via `python`. This means +that they need to execute the tests when run as the main module rather +than just defining the tests function. To make that happen it’s best +to define `test_` functions with test bodies and than execute all +those functions in a code fragment guarded by `if __name__ == +'__main__'` condition. + +### Check scripts + +Unless tests are included (potentially transitively) in `nightly.txt` +file, NayDuck won’t run them. As part of pull request checks, +verification is performed to make sure that no test is forgotten and +all new tests are included in the nightly list. That’s done by +`scripts/check_nightly.txt` and `scripts/check_pytest.txt` scripts. +The list all the expensive and pytest tests defined in the repository +and then check whether they are all mentioned in the nightly list. + +The scripts recognise commented out tests so if a test is broken it +can be removed from the list by commenting it out. However, such +a test must be proceeded by a TODO comment mentioning an issue which +tracks the breakage. For example: + + # TODO(#2411): Enable them when we fix the issue with proxy shutdown + #pytest --timeout=900 sanity/sync_ban.py true + #pytest --timeout=900 sanity/sync_ban.py false + +The include directive can be commented out like that as well though +crucially there must be no space between the hash sign and dot. For +example: + + # TODO(#2411): Working on a fix. + #./bridge.txt diff --git a/nightly/expensive.txt b/nightly/expensive.txt new file mode 100644 index 000000000..ac6e01703 --- /dev/null +++ b/nightly/expensive.txt @@ -0,0 +1,227 @@ +# catchup tests +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_receipts_sync_third_epoch +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_receipts_sync_third_epoch --features nightly +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_receipts_sync_last_block +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_receipts_sync_last_block --features nightly +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_receipts_sync_distant_epoch +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_receipts_sync_distant_epoch --features nightly +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_random_single_part_sync +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_random_single_part_sync --features nightly +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_random_single_part_sync_skip_15 +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_random_single_part_sync_skip_15 --features nightly +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_random_single_part_sync_send_15 +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_random_single_part_sync_send_15 --features nightly +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_random_single_part_sync_non_zero_amounts +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_random_single_part_sync_non_zero_amounts --features nightly +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_random_single_part_sync_height_6 +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_random_single_part_sync_height_6 --features nightly +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_sanity_blocks_produced +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_sanity_blocks_produced --features nightly +expensive --timeout=3600 unc-client unc_client tests::catching_up::test_all_chunks_accepted_1000 +expensive --timeout=3600 unc-client unc_client tests::catching_up::test_all_chunks_accepted_1000 --features nightly +expensive --timeout=7200 unc-client unc_client tests::catching_up::test_all_chunks_accepted_1000_slow +expensive --timeout=7200 unc-client unc_client tests::catching_up::test_all_chunks_accepted_1000_slow --features nightly +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_all_chunks_accepted_1000_rare_epoch_changing +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_all_chunks_accepted_1000_rare_epoch_changing --features nightly +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_receipts_sync_hold +expensive --timeout=1800 unc-client unc_client tests::catching_up::test_catchup_receipts_sync_hold --features nightly + +expensive integration-tests integration_tests tests::test_catchup::test_catchup +expensive integration-tests integration_tests tests::test_catchup::test_catchup --features nightly + +# cross-shard transactions tests +# TODO(#4618): Those tests are currently broken. Comment out while we’re +# working on a fix / deciding whether to remove them. +# expensive --timeout=3000 unc-client unc_client tests::cross_shard_tx::test_cross_shard_tx +# expensive --timeout=3000 unc-client unc_client tests::cross_shard_tx::test_cross_shard_tx --features nightly +expensive --timeout=3000 unc-client unc_client tests::cross_shard_tx::test_cross_shard_tx_doomslug +expensive --timeout=3000 unc-client unc_client tests::cross_shard_tx::test_cross_shard_tx_doomslug --features nightly +expensive --timeout=3000 unc-client unc_client tests::cross_shard_tx::test_cross_shard_tx_drop_chunks +expensive --timeout=3000 unc-client unc_client tests::cross_shard_tx::test_cross_shard_tx_drop_chunks --features nightly +# TODO(#4618): Those tests are currently broken. Comment out while we’re +# working on a fix / deciding whether to remove them. +# expensive --timeout=5400 unc-client unc_client tests::cross_shard_tx::test_cross_shard_tx_with_validator_rotation_1 +# expensive --timeout=5400 unc-client unc_client tests::cross_shard_tx::test_cross_shard_tx_with_validator_rotation_1 --features nightly +# expensive --timeout=5400 unc-client unc_client tests::cross_shard_tx::test_cross_shard_tx_with_validator_rotation_2 +# expensive --timeout=5400 unc-client unc_client tests::cross_shard_tx::test_cross_shard_tx_with_validator_rotation_2 --features nightly +# expensive --timeout=4800 unc-client unc_client tests::cross_shard_tx::test_cross_shard_tx_8_iterations +# expensive --timeout=4800 unc-client unc_client tests::cross_shard_tx::test_cross_shard_tx_8_iterations_drop_chunks + +# consensus tests +expensive --timeout=3000 unc-chain unc_chain tests::doomslug::test_fuzzy_doomslug_liveness_and_safety +expensive --timeout=3000 unc-chain unc_chain tests::doomslug::test_fuzzy_doomslug_liveness_and_safety --features nightly +expensive --timeout=500 unc-client unc_client tests::consensus::test_consensus_with_epoch_switches +expensive --timeout=500 unc-client unc_client tests::consensus::test_consensus_with_epoch_switches --features nightly + +# testnet rpc +expensive integration-tests integration_tests tests::test_tps_regression::test_highload +expensive integration-tests integration_tests tests::test_tps_regression::test_highload --features nightly + +expensive integration-tests integration_tests tests::standard_cases::rpc::test_access_key_smart_contract_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_access_key_smart_contract_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_add_access_key_function_call_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_add_access_key_function_call_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_add_access_key_with_allowance_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_add_access_key_with_allowance_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_add_existing_key_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_add_existing_key_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_add_key_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_add_key_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_create_account_again_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_create_account_again_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_create_account_failure_already_exists_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_create_account_failure_already_exists_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_create_account_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_create_account_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_delete_access_key_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_delete_access_key_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_delete_access_key_with_allowance_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_delete_access_key_with_allowance_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_delete_key_last_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_delete_key_last_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_delete_key_not_owned_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_delete_key_not_owned_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_delete_key_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_delete_key_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_nonce_update_when_deploying_contract_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_nonce_update_when_deploying_contract_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_nonce_updated_when_tx_failed_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_nonce_updated_when_tx_failed_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_redeploy_contract_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_redeploy_contract_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_refund_on_send_money_to_non_existent_account_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_refund_on_send_money_to_non_existent_account_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_send_money_over_balance_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_send_money_over_balance_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_send_money_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_send_money_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_smart_contract_bad_method_name_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_smart_contract_bad_method_name_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_smart_contract_empty_method_name_with_no_tokens_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_smart_contract_empty_method_name_with_no_tokens_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_smart_contract_empty_method_name_with_tokens_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_smart_contract_empty_method_name_with_tokens_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_smart_contract_self_call_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_smart_contract_self_call_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_smart_contract_simple_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_smart_contract_simple_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_smart_contract_with_args_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_smart_contract_with_args_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_swap_key_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_swap_key_testnet --features nightly +expensive integration-tests integration_tests tests::standard_cases::rpc::test_upload_contract_testnet +expensive integration-tests integration_tests tests::standard_cases::rpc::test_upload_contract_testnet --features nightly + +# GC tests +expensive --timeout=900 unc-chain unc_chain tests::gc::test_gc_remove_fork_large +expensive --timeout=900 unc-chain unc_chain tests::gc::test_gc_remove_fork_large --features nightly +expensive --timeout=1200 unc-chain unc_chain tests::gc::test_gc_not_remove_fork_large +expensive --timeout=1200 unc-chain unc_chain tests::gc::test_gc_not_remove_fork_large --features nightly +expensive --timeout=1200 unc-chain unc_chain tests::gc::test_gc_boundaries_large +expensive --timeout=1200 unc-chain unc_chain tests::gc::test_gc_boundaries_large --features nightly +expensive --timeout=900 unc-chain unc_chain tests::gc::test_gc_random_large +expensive --timeout=900 unc-chain unc_chain tests::gc::test_gc_random_large --features nightly +expensive --timeout=600 unc-chain unc_chain tests::gc::test_gc_pine +expensive --timeout=600 unc-chain unc_chain tests::gc::test_gc_pine --features nightly +expensive --timeout=700 unc-chain unc_chain tests::gc::test_gc_star_large +expensive --timeout=700 unc-chain unc_chain tests::gc::test_gc_star_large --features nightly + +expensive --timeout=1200 integration-tests integration_tests tests::client::block_corruption::check_process_flipped_block_fails +expensive --timeout=1200 integration-tests integration_tests tests::client::block_corruption::check_process_flipped_block_fails --features nightly +expensive integration-tests integration_tests tests::client::chunks_management::chunks_recovered_from_full +expensive integration-tests integration_tests tests::client::chunks_management::chunks_recovered_from_full --features nightly +expensive integration-tests integration_tests tests::client::chunks_management::chunks_recovered_from_full_cop +expensive integration-tests integration_tests tests::client::chunks_management::chunks_recovered_from_full_cop --features nightly +expensive integration-tests integration_tests tests::client::chunks_management::chunks_recovered_from_full_timeout_too_short +expensive integration-tests integration_tests tests::client::chunks_management::chunks_recovered_from_full_timeout_too_short --features nightly +expensive integration-tests integration_tests tests::client::chunks_management::chunks_recovered_from_full_timeout_too_short_cop +expensive integration-tests integration_tests tests::client::chunks_management::chunks_recovered_from_full_timeout_too_short_cop --features nightly +expensive integration-tests integration_tests tests::client::chunks_management::chunks_recovered_from_others +expensive integration-tests integration_tests tests::client::chunks_management::chunks_recovered_from_others --features nightly +expensive integration-tests integration_tests tests::client::chunks_management::chunks_recovered_from_others_cop +expensive integration-tests integration_tests tests::client::chunks_management::chunks_recovered_from_others_cop --features nightly +expensive integration-tests integration_tests tests::client::process_blocks::test_gc_after_state_sync +expensive integration-tests integration_tests tests::client::process_blocks::test_gc_after_state_sync --features nightly +expensive integration-tests integration_tests tests::client::process_blocks::test_process_block_after_state_sync +expensive integration-tests integration_tests tests::client::process_blocks::test_process_block_after_state_sync --features nightly +expensive integration-tests integration_tests tests::client::sync_state_nodes::sync_empty_state +expensive integration-tests integration_tests tests::client::sync_state_nodes::sync_empty_state --features nightly +expensive integration-tests integration_tests tests::client::sync_state_nodes::sync_state_dump +expensive integration-tests integration_tests tests::client::sync_state_nodes::sync_state_dump --features nightly +expensive integration-tests integration_tests tests::client::sync_state_nodes::sync_state_nodes +expensive integration-tests integration_tests tests::client::sync_state_nodes::sync_state_nodes --features nightly +expensive integration-tests integration_tests tests::client::sync_state_nodes::sync_state_nodes_multishard +expensive integration-tests integration_tests tests::client::sync_state_nodes::sync_state_nodes_multishard --features nightly + +# other tests +expensive --timeout=300 unc-chain unc_chain store::tests::test_clear_old_data_too_many_heights + +expensive integration-tests integration_tests tests::test_simple::test_2_10_multiple_nodes +expensive integration-tests integration_tests tests::test_simple::test_2_10_multiple_nodes --features nightly +expensive integration-tests integration_tests tests::test_simple::test_4_10_multiple_nodes +expensive integration-tests integration_tests tests::test_simple::test_4_10_multiple_nodes --features nightly +expensive integration-tests integration_tests tests::test_simple::test_7_10_multiple_nodes +expensive integration-tests integration_tests tests::test_simple::test_7_10_multiple_nodes --features nightly + +expensive integration-tests integration_tests tests::framework::sync_nodes::sync_after_sync_nodes +expensive integration-tests integration_tests tests::framework::sync_nodes::sync_after_sync_nodes --features nightly +expensive integration-tests integration_tests tests::framework::sync_nodes::sync_nodes +expensive integration-tests integration_tests tests::framework::sync_nodes::sync_nodes --features nightly +expensive integration-tests integration_tests tests::framework::sync_nodes::sync_state_stake_change +expensive integration-tests integration_tests tests::framework::sync_nodes::sync_state_stake_change --features nightly + +expensive integration-tests integration_tests tests::framework::rpc_error_structs::test_block_unknown_block_error +expensive integration-tests integration_tests tests::framework::rpc_error_structs::test_block_unknown_block_error --features nightly +expensive integration-tests integration_tests tests::framework::rpc_error_structs::test_chunk_unknown_chunk_error +expensive integration-tests integration_tests tests::framework::rpc_error_structs::test_chunk_unknown_chunk_error --features nightly +expensive integration-tests integration_tests tests::framework::rpc_error_structs::test_gas_price_unknown_block_error +expensive integration-tests integration_tests tests::framework::rpc_error_structs::test_gas_price_unknown_block_error --features nightly +expensive integration-tests integration_tests tests::framework::rpc_error_structs::test_protocol_config_unknown_block_error +expensive integration-tests integration_tests tests::framework::rpc_error_structs::test_protocol_config_unknown_block_error --features nightly +expensive integration-tests integration_tests tests::framework::rpc_error_structs::test_query_rpc_account_view_unknown_block_must_return_error +expensive integration-tests integration_tests tests::framework::rpc_error_structs::test_query_rpc_account_view_unknown_block_must_return_error --features nightly +expensive integration-tests integration_tests tests::framework::rpc_error_structs::test_receipt_id_unknown_receipt_error +expensive integration-tests integration_tests tests::framework::rpc_error_structs::test_receipt_id_unknown_receipt_error --features nightly +expensive integration-tests integration_tests tests::framework::rpc_error_structs::test_tx_invalid_tx_error +expensive integration-tests integration_tests tests::framework::rpc_error_structs::test_tx_invalid_tx_error --features nightly + +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_tx_status_on_lightclient_must_return_does_not_track_shard +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_tx_status_on_lightclient_must_return_does_not_track_shard --features nightly +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_check_unknown_tx_must_return_error +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_check_unknown_tx_must_return_error --features nightly +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_get_execution_outcome_tx_failure +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_get_execution_outcome_tx_failure --features nightly +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_get_execution_outcome_tx_success +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_get_execution_outcome_tx_success --features nightly +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_get_validator_info_rpc +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_get_validator_info_rpc --features nightly +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_protocol_config_rpc +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_protocol_config_rpc --features nightly +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_query_rpc_account_view_account_doesnt_exist_must_return_error +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_query_rpc_account_view_account_doesnt_exist_must_return_error --features nightly +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_query_rpc_account_view_must_succeed +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_query_rpc_account_view_must_succeed --features nightly +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_tx_not_enough_balance_must_return_error +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_tx_not_enough_balance_must_return_error --features nightly +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_validators_by_epoch_id_current_epoch_not_fails +expensive integration-tests integration_tests tests::framework::rpc_nodes::test_validators_by_epoch_id_current_epoch_not_fails --features nightly +expensive integration-tests integration_tests tests::framework::run_nodes::run_nodes_1_2_1 +expensive integration-tests integration_tests tests::framework::run_nodes::run_nodes_1_2_1 --features nightly +expensive integration-tests integration_tests tests::framework::run_nodes::run_nodes_1_2_2 +expensive integration-tests integration_tests tests::framework::run_nodes::run_nodes_1_2_2 --features nightly +expensive integration-tests integration_tests tests::framework::run_nodes::run_nodes_1_4_4 +expensive integration-tests integration_tests tests::framework::run_nodes::run_nodes_1_4_4 --features nightly +expensive integration-tests integration_tests tests::framework::run_nodes::run_nodes_4_4_2 +expensive integration-tests integration_tests tests::framework::run_nodes::run_nodes_4_4_2 --features nightly + +expensive integration-tests integration_tests tests::framework::stake_nodes::test_inflation +expensive integration-tests integration_tests tests::framework::stake_nodes::test_inflation --features nightly +expensive integration-tests integration_tests tests::framework::stake_nodes::test_stake_nodes +expensive integration-tests integration_tests tests::framework::stake_nodes::test_stake_nodes --features nightly +expensive integration-tests integration_tests tests::framework::stake_nodes::test_validator_join +expensive integration-tests integration_tests tests::framework::stake_nodes::test_validator_join --features nightly +expensive integration-tests integration_tests tests::framework::stake_nodes::test_validator_kickout +expensive integration-tests integration_tests tests::framework::stake_nodes::test_validator_kickout --features nightly + +expensive integration-tests integration_tests tests::framework::track_shards::track_shards +expensive integration-tests integration_tests tests::framework::track_shards::track_shards --features nightly diff --git a/nightly/nayduck.py b/nightly/nayduck.py new file mode 120000 index 000000000..8935d3d58 --- /dev/null +++ b/nightly/nayduck.py @@ -0,0 +1 @@ +../scripts/nayduck.py \ No newline at end of file diff --git a/nightly/nightly.txt b/nightly/nightly.txt new file mode 100644 index 000000000..d99d8445f --- /dev/null +++ b/nightly/nightly.txt @@ -0,0 +1,3 @@ +./sandbox.txt +./pytest.txt +./expensive.txt diff --git a/nightly/pytest-adversarial.txt b/nightly/pytest-adversarial.txt new file mode 100644 index 000000000..aaedcfb77 --- /dev/null +++ b/nightly/pytest-adversarial.txt @@ -0,0 +1,20 @@ +pytest --timeout=600 adversarial/fork_sync.py +pytest --timeout=600 adversarial/fork_sync.py --features nightly +pytest adversarial/malicious_chain.py +pytest adversarial/malicious_chain.py --features nightly +pytest adversarial/malicious_chain.py valid_blocks_only +pytest adversarial/malicious_chain.py valid_blocks_only --features nightly +pytest adversarial/start_from_genesis.py +pytest adversarial/start_from_genesis.py --features nightly +pytest adversarial/start_from_genesis.py overtake +pytest adversarial/start_from_genesis.py overtake --features nightly +pytest adversarial/start_from_genesis.py doomslug_off +pytest adversarial/start_from_genesis.py doomslug_off --features nightly +# TODO(#6421): Currently broken. +#pytest adversarial/start_from_genesis.py overtake doomslug_off +#pytest adversarial/start_from_genesis.py overtake doomslug_off --features nightly + +# TODO(#4618): Those tests are currently broken. Comment out while we’re +# working on a fix. +#pytest adversarial/gc_rollback.py +#pytest adversarial/gc_rollback.py --features nightly diff --git a/nightly/pytest-contracts.txt b/nightly/pytest-contracts.txt new file mode 100644 index 000000000..c722066db --- /dev/null +++ b/nightly/pytest-contracts.txt @@ -0,0 +1,7 @@ +# python tests for smart contract deployment and invocation +pytest contracts/deploy_call_smart_contract.py +pytest contracts/deploy_call_smart_contract.py --features nightly +pytest --timeout=10m contracts/gibberish.py +pytest --timeout=10m contracts/gibberish.py --features nightly +pytest --timeout=400 contracts/infinite_loops.py +pytest --timeout=400 contracts/infinite_loops.py --features nightly diff --git a/nightly/pytest-sanity.txt b/nightly/pytest-sanity.txt new file mode 100644 index 000000000..a435517d5 --- /dev/null +++ b/nightly/pytest-sanity.txt @@ -0,0 +1,156 @@ +# python sanity tests +pytest sanity/simple.py +pytest sanity/simple.py --features nightly +pytest sanity/block_production.py +pytest sanity/block_production.py --features nightly +pytest sanity/transactions.py +pytest sanity/transactions.py --features nightly +pytest sanity/staking1.py +pytest sanity/staking1.py --features nightly +pytest --timeout=800 sanity/staking2.py +pytest --timeout=800 sanity/staking2.py --features nightly +pytest --timeout=800 sanity/staking_repro1.py +pytest --timeout=800 sanity/staking_repro1.py --features nightly +pytest --timeout=800 sanity/staking_repro2.py +pytest --timeout=800 sanity/staking_repro2.py --features nightly +pytest sanity/epoch_switches.py +pytest sanity/epoch_switches.py --features nightly +pytest sanity/state_sync.py manytx 30 +pytest sanity/state_sync.py manytx 30 --features nightly +pytest --timeout=600 sanity/state_sync.py manytx 265 +pytest --timeout=600 sanity/state_sync.py manytx 265 --features nightly +pytest sanity/state_sync.py onetx 30 +pytest sanity/state_sync.py onetx 30 --features nightly +pytest --timeout=600 sanity/state_sync.py onetx 265 +pytest --timeout=600 sanity/state_sync.py onetx 265 --features nightly +pytest --timeout=240 sanity/state_sync1.py +pytest --timeout=240 sanity/state_sync1.py --features nightly +# TODO(#4618): Those tests are currently broken. Comment out while we’re +# working on a fix / deciding whether to remove them. +#pytest --timeout=900 sanity/state_sync2.py +#pytest --timeout=900 sanity/state_sync2.py nightly --features nightly +pytest --timeout=1200 sanity/state_sync3.py +pytest --timeout=1200 sanity/state_sync3.py --features nightly +pytest --timeout=240 sanity/state_sync4.py +pytest --timeout=240 sanity/state_sync4.py --features nightly +pytest --timeout=240 sanity/state_sync5.py +pytest --timeout=240 sanity/state_sync5.py --features nightly +pytest --timeout=600 sanity/state_sync_routed.py manytx 115 +pytest --timeout=600 sanity/state_sync_routed.py manytx 115 --features nightly +# TODO(#4618): Those tests are currently broken. Comment out while we’re +# working on a fix / deciding whether to remove them. +#pytest --timeout=300 sanity/state_sync_late.py notx +#pytest --timeout=300 sanity/state_sync_late.py notx --features nightly + +pytest --timeout=3600 sanity/state_sync_massive.py +pytest --timeout=3600 sanity/state_sync_massive_validator.py +pytest --timeout=3600 sanity/state_sync_massive.py --features nightly +pytest --timeout=3600 sanity/state_sync_massive_validator.py --features nightly + +pytest sanity/sync_chunks_from_archival.py +pytest sanity/sync_chunks_from_archival.py --features nightly +pytest sanity/rpc_tx_forwarding.py +pytest sanity/rpc_tx_forwarding.py --features nightly +pytest --timeout=240 sanity/skip_epoch.py +pytest --timeout=240 sanity/skip_epoch.py --features nightly +pytest --timeout=240 sanity/one_val.py +pytest --timeout=240 sanity/one_val.py nightly --features nightly +pytest --timeout=240 sanity/lightclnt.py +pytest --timeout=240 sanity/lightclnt.py --features nightly +pytest sanity/rpc_light_client_execution_outcome_proof.py +pytest sanity/rpc_light_client_execution_outcome_proof.py --features nightly +pytest --timeout=240 sanity/block_sync.py +pytest --timeout=240 sanity/block_sync.py --features nightly +pytest --timeout=10m sanity/block_sync_archival.py +pytest --timeout=10m sanity/block_sync_archival.py --features nightly +pytest --timeout=120 sanity/block_sync_flat_storage.py +pytest --timeout=120 sanity/block_sync_flat_storage.py --features nightly +pytest --timeout=240 sanity/state_sync_epoch_boundary.py +pytest --timeout=240 sanity/state_sync_epoch_boundary.py --features nightly +pytest --timeout=240 sanity/state_sync_then_catchup.py +pytest --timeout=240 sanity/state_sync_then_catchup.py --features nightly +pytest --timeout=120 sanity/catchup_flat_storage_deletions.py +pytest --timeout=120 sanity/catchup_flat_storage_deletions.py --features nightly +pytest --timeout=240 sanity/validator_switch.py +pytest --timeout=240 sanity/validator_switch.py --features nightly +pytest --timeout=240 sanity/rpc_state_changes.py +pytest --timeout=240 sanity/rpc_state_changes.py --features nightly +pytest sanity/rpc_max_gas_burnt.py +pytest sanity/rpc_max_gas_burnt.py --features nightly +pytest sanity/rpc_tx_status.py +pytest sanity/rpc_tx_status.py --features nightly +pytest --timeout=120 sanity/garbage_collection.py +pytest --timeout=120 sanity/garbage_collection.py --features nightly +pytest --timeout=120 sanity/garbage_collection1.py +pytest --timeout=120 sanity/garbage_collection1.py --features nightly +pytest --timeout=300 sanity/gc_after_sync.py +pytest --timeout=300 sanity/gc_after_sync.py --features nightly +pytest --timeout=300 sanity/gc_after_sync1.py +pytest --timeout=300 sanity/gc_after_sync1.py --features nightly +pytest --timeout=300 sanity/gc_sync_after_sync.py +pytest --timeout=300 sanity/gc_sync_after_sync.py --features nightly +pytest --timeout=300 sanity/gc_sync_after_sync.py swap_nodes +pytest --timeout=300 sanity/gc_sync_after_sync.py swap_nodes --features nightly +pytest --timeout=300 sanity/large_messages.py +pytest --timeout=300 sanity/large_messages.py --features nightly +pytest --timeout=120 sanity/handshake_tie_resolution.py +pytest --timeout=120 sanity/handshake_tie_resolution.py --features nightly +pytest sanity/repro_2916.py +pytest sanity/repro_2916.py --features nightly +pytest --timeout=240 sanity/switch_node_key.py +pytest --timeout=240 sanity/switch_node_key.py --features nightly +pytest --timeout=240 sanity/validator_switch_key.py +pytest --timeout=240 sanity/validator_switch_key.py --features nightly +pytest sanity/proxy_simple.py +pytest sanity/proxy_simple.py --features nightly +pytest sanity/proxy_restart.py +pytest sanity/proxy_restart.py --features nightly +pytest sanity/network_drop_package.py +pytest sanity/network_drop_package.py --features nightly +pytest --timeout=900 sanity/sync_ban.py true +pytest --timeout=900 sanity/sync_ban.py true --features nightly +pytest --timeout=900 sanity/sync_ban.py false +pytest --timeout=900 sanity/sync_ban.py false --features nightly +pytest sanity/block_chunk_signature.py +pytest sanity/block_chunk_signature.py --features nightly +pytest sanity/concurrent_function_calls.py +pytest sanity/concurrent_function_calls.py --features nightly +pytest sanity/proxy_example.py +pytest sanity/proxy_example.py --features nightly +pytest sanity/rpc_tx_submission.py +pytest sanity/rpc_tx_submission.py --features nightly +pytest sanity/state_sync_fail.py +pytest sanity/state_sync_fail.py --features nightly + +pytest sanity/restart.py +pytest sanity/restart.py --features nightly +pytest sanity/rpc_finality.py +pytest sanity/rpc_finality.py --features nightly + +pytest sanity/rpc_hash.py +pytest sanity/rpc_hash.py --features nightly + +# Rosetta RPC tests +pytest sanity/rosetta.py +pytest sanity/rosetta.py --features nightly + +# Make sure Docker image can be build and run +pytest --skip-build --timeout=1h sanity/docker.py + +pytest sanity/recompress_storage.py +pytest sanity/recompress_storage.py --features nightly + +# This is the test for meta transactions. +pytest sanity/meta_tx.py --features nightly + +# Tests for split storage and split storage migration +pytest --timeout=600 sanity/split_storage.py +pytest --timeout=600 sanity/split_storage.py --features nightly + +# Test for resharding +pytest --timeout=120 sanity/resharding.py +pytest --timeout=120 sanity/resharding.py --features nightly +pytest --timeout=120 sanity/resharding_error_handling.py +pytest --timeout=120 sanity/resharding_error_handling.py --features nightly +pytest --timeout=120 sanity/resharding_rpc_tx.py +pytest --timeout=120 sanity/resharding_rpc_tx.py --features nightly diff --git a/nightly/pytest-spec.txt b/nightly/pytest-spec.txt new file mode 100644 index 000000000..2f03d6790 --- /dev/null +++ b/nightly/pytest-spec.txt @@ -0,0 +1,3 @@ +# python spec tests +pytest spec/network/peers_request.py +pytest spec/network/peers_request.py --features nightly diff --git a/nightly/pytest.txt b/nightly/pytest.txt new file mode 100644 index 000000000..254455786 --- /dev/null +++ b/nightly/pytest.txt @@ -0,0 +1,4 @@ +./pytest-adversarial.txt +./pytest-contracts.txt +./pytest-sanity.txt +./pytest-spec.txt diff --git a/nightly/sandbox.txt b/nightly/sandbox.txt new file mode 100644 index 000000000..677abae68 --- /dev/null +++ b/nightly/sandbox.txt @@ -0,0 +1,4 @@ +# python sandbox node tests +pytest sandbox/patch_state.py --features sandbox +pytest sandbox/fast_forward.py --features sandbox +pytest sandbox/fast_forward_epoch_boundary.py --features sandbox diff --git a/pytest/README.md b/pytest/README.md new file mode 100644 index 000000000..d4212f233 --- /dev/null +++ b/pytest/README.md @@ -0,0 +1,222 @@ +# Python-based tests + +The directory contains Python-based tests. The tests are run as part +of nightly testing on NayDuck though they can be run locally as well. + +There is no set format of what the tests do but they typical start +a local test cluster using uncd binary at `../target/debug/uncd`. +There is also some capacity of starting the cluster on remote +machines. + + +## Running tests + +### Running tests locally + +To run tests locally first compile a debug build of the framework +package, make sure that all required Python packages are installed and +then execute the test file using python. For example: + + cargo build + cd pytest + python3 -m pip install -U -r requirements.txt + python3 tests/sanity/one_val.py + +After the test finishes, log files and other result data from running +each node will be located in a `~/.near/test#_finished` directory +(where `#` is index of the node starting with zero). + +Note that running the tests using `pytest` command is not supported +and won’t work reliably. + +Furthermore, running multiple tests at once is not supported either +because tests often use hard-coded paths (e.g. `~/.node/test#` for +node home directories) and port numbers + +### Ruining tests on NayDuck + +As mentioned, the tests are normally run nightly on NayDuck. To +schedule a run on NayDuck manual the `../scripts/nayduck.py` script is +used. The `../nightly/README.md` file describes this is more detail. + +### Running pytest remotely + +The test library has code for executing tests while running the nodes +on remote Google Cloud machines. Presumably that code worked in the +past but I, mina86, haven’t tried it and am a bit sceptical as to +whether it is still functional. Regardless, for anyone who wants to +try it out, the instructions are as follows: + +Prerequisites: + +1. Same as local pytest +2. gcloud cli in PATH + +Steps: + +1. Choose or upload a near binary here: https://console.cloud.google.com/storage/browser/nearprotocol_nearcore_release?project=unc-core +2. Fill the binary filename in remote.json. Modify zones as needed, + they’ll be used in round-robin manner. +3. `unc_PYTEST_CONFIG=remote.json python tests/...` +4. Run `python tests/delete_remote_nodes.py` to make sure the remote + nodes are shut down properly (especially if tests failed early). + + +## Creating new tests + +To add a test simply create a Python script inside of the `tests` +directory and add it to a test set file in `../nightly` directory. +See `../nightly/README.md` file for detailed documentation of the test +set files. Note that if you add a test file but don’t include it in +nightly test set the pull request check will fail. + +Even though this directory is called `pytest`, the tests need to work +when executed via `python3`. This means that they need to execute the +tests when run as the main module rather than just defining the tests +function. To make that happen it’s best to implement the tests using +the python's unittest framework but trigger them manually from within +the `__main__` condition like so: + + if __name__ == "__main__": + unittest.main() + +Alternatively, using the legacy way, the tests can be defined as +`test_` functions with test bodies and than executed in +a code fragment guarded by `if __name__ == '__main__'` condition. + +If the test operates on the nodes running in a cluster, it will very +likely want to make use of `start_cluster` function defined in the +`lib/cluster.py` module. + +Rather than assuming location a temporary directory, well-behaved test +should use `tempfile` module instead which will automatically take +`TEMPDIR` variable into consideration. This is especially important +for NayDuck which will automatically cleanup after a test which +respects `TEMPDIR` directory even if that tests ends up not cleaning +up its temporary files. + +For example, a simple test for checking implementation of +`max_gas_burnt_view` could be located in +`tests/sanity/rpc_max_gas_burnt.py` and look as follows: + + """Test max_gas_burnt_view client configuration. + + Spins up two nodes with different max_gas_burnt_view client + configuration, deploys a smart contract and finally calls a view + function against both nodes expecting the one with low + max_gas_burnt_view limit to fail. + """ + + import sys + import base58 + import base64 + import json + import pathlib + + sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + from cluster import start_cluster + from utils import load_binary_file + import transaction + + + def test_max_gas_burnt_view(): + nodes = start_cluster(2, 0, 1, + config=None, + genesis_config_changes=[], + client_config_changes={ + 1: {'max_gas_burnt_view': int(5e10)} + }) + + contract_key = nodes[0].signer_key + contract = load_binary_file( + '../runtime/unc-test-contracts/res/test_contract_rs.wasm') + + # Deploy the fib smart contract + latest_block_hash = nodes[0].get_latest_block().hash + deploy_contract_tx = transaction.sign_deploy_contract_tx( + contract_key, contract, 10, + base58.b58decode(latest_block_hash.encode('utf8'))) + deploy_contract_response = ( + nodes[0].send_tx_and_wait(deploy_contract_tx, 10)) + + def call_fib(node, n): + args = base64.b64encode(bytes([n])).decode('ascii') + return node.call_function( + contract_key.account_id, 'fibonacci', args, + timeout=10 + ).get('result') + + # Call view function of the smart contract via the first + # node. This should succeed. + result = call_fib(nodes[0], 25) + assert 'result' in result and 'error' not in result, ( + 'Expected "result" and no "error" in response, got: {}' + .format(result)) + + # Same but against the second node. This should fail. + result = call_fib(nodes[1], 25) + assert 'result' not in result and 'error' in result, ( + 'Expected "error" and no "result" in response, got: {}' + .format(result)) + error = result['error'] + assert 'HostError(GasLimitExceeded)' in error, ( + 'Expected error due to GasLimitExceeded but got: {}'.format(error)) + + + if __name__ == '__main__': + test_max_gas_burnt_view() + +### NayDuck environment + +When executed on NayDuck, tests have access to `uncd`, +`genesis-populate` and `restaked` binaries in `../target/debug` or +`../target/release` directory (depending if the test has been +scheduled with `--release` flag) just as if they were executed on +local machine. Similarly, freshly built NEAR test contracts will be +located in `../runtime/unc-test-contracts/res` directory. + +The `NAYDUCK=1`, `NIGHTLY_RUNNER=1` and `NAYDUCK_TIMEOUT=` +environment variables are set when tests are run on NayDuck. If +necessary and no other option exists, the first two can be used to +change test’s behaviour to accommodate it running on the testing +infrastructure as opposed to local machine. Meanwhile, +`NAYDUCK_TIMEOUT` specifies how much time in seconds test has to run +before NayDuck decides the test failed. + +### Code Style + +To automate formatting and avoid excessive bike shedding, we're using +YAPF to format Python source code in the pytest directory. It can be +installed from Python Package Index (PyPI) using `pip` tool: + + python3 -m pip install yapf + +Once installed, it can be run either on a single file, for example +with the following command: + + python3 -m yapf -pi lib/cluster.py + +or the entire directory with command as seen below: + + python3 -m yapf -pir . + +The `-p` switch enables parallelism and `-i` applies the changes in +place. Without the latter switch the tool will write formatted file +to standard output instead. + +The command should be executed in the `pytest` directory so that it’ll +pick up configuration from the `.style.yapf` file. + +### Productivity tips + +- The nayduck tests often rely on utilities +located in pytest/lib and are imported using the following statement: +`sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib'))` +In order to make VSCode see that import you can add this path to the python +extra paths config. In order to do so add the following into the settings file: +``` + "python.analysis.extraPaths": [ + "pytest/lib" + ] +``` + diff --git a/pytest/__init__.py b/pytest/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pytest/add_and_delete_state.wasm b/pytest/add_and_delete_state.wasm new file mode 100644 index 000000000..887276917 Binary files /dev/null and b/pytest/add_and_delete_state.wasm differ diff --git a/pytest/betanet_state.wasm b/pytest/betanet_state.wasm new file mode 100755 index 000000000..8b16cd2f7 Binary files /dev/null and b/pytest/betanet_state.wasm differ diff --git a/pytest/endtoend/__init__.py b/pytest/endtoend/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pytest/endtoend/endtoend.py b/pytest/endtoend/endtoend.py new file mode 100644 index 000000000..b021d5c6e --- /dev/null +++ b/pytest/endtoend/endtoend.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +""" +End-to-end test for canary nodes. +Assuming each canary node has an account with positive balance. +Periodically (1 minute) each account sends itself 0 tokens. Some tokens get burned in the process. +Account balances get exported to prometheus and can be used to detect when transactions stop affecting the world, aka the chain of testnet or mainnet. +We observe effects on the chain using public RPC endpoints to ensure canaries are running a fork. + +python3 endtoend/endtoend.py + --ips + --accounts + --interval-sec 60 + --port 3030 + --public-key + --private-key + --rpc-server-addr rpc.testnet.near.org + --rpc-server-port 80 + --metrics-port 3040 +""" +import argparse +import random +import sys +import time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[1] / 'lib')) + +import account as account_mod +import key as key_mod +import mocknet_helpers +from concurrent.futures import ThreadPoolExecutor + +from configured_logger import logger +import prometheus_client + +balance_gauge = prometheus_client.Gauge( + 'unc_e2e_account_balance', + 'Balance of the test accounts running an end-to-end test. Measured in yoctonear.', + ['account']) + + +def do_ping(account, rpc_server): + account_id = account.key.account_id + base_block_hash = mocknet_helpers.get_latest_block_hash(addr=rpc_server[0], + port=rpc_server[1]) + balance = mocknet_helpers.retry_and_ignore_errors( + lambda: mocknet_helpers.get_amount_yoctonear( + account_id, addr=rpc_server[0], port=rpc_server[1])) + if balance is not None: + balance_gauge.labels(account=account_id).set(balance) + logger.info(f'Sending 0 tokens from {account_id} to itself') + tx_res = mocknet_helpers.retry_and_ignore_errors( + lambda: account.send_transfer_tx( + account_id, transfer_amount=0, base_block_hash=base_block_hash)) + logger.info( + f'Sending 0 tokens from {account_id} to itself, tx result: {tx_res}') + + +def pinger(account, interval, rpc_server): + account_id = account.key.account_id + logger.info(f'pinger {account_id}') + time.sleep(random.random() * interval) + logger.info(f'pinger {account_id} woke up') + while True: + try: + do_ping(account, rpc_server) + except Exception as ex: + logger.warning(f'Error pinging account {account_id}', exc_info=ex) + time.sleep(interval) + + +if __name__ == '__main__': + logger.info('Starting end-to-end test.') + parser = argparse.ArgumentParser(description='Run an end-to-end test') + parser.add_argument('--ips', required=True) + parser.add_argument('--accounts', required=True) + parser.add_argument('--interval-sec', type=float, required=True) + parser.add_argument('--port', type=int, required=True) + parser.add_argument('--public-key', required=True) + parser.add_argument('--private-key', required=True) + parser.add_argument('--rpc-server-addr', required=True) + parser.add_argument('--rpc-server-port', required=True) + parser.add_argument('--metrics-port', type=int, required=True) + + args = parser.parse_args() + + assert args.ips + ips = args.ips.split(',') + assert args.accounts + account_ids = args.accounts.split(',') + assert len(account_ids) == len( + ips), 'List of test accounts must match the list of node IP addresses' + interval_sec = args.interval_sec + assert interval_sec > 1, 'Need at least 1 second between pings' + port = args.port + pk, sk = args.public_key, args.private_key + rpc_server = (args.rpc_server_addr, args.rpc_server_port) + + keys = [key_mod.Key(account_id, pk, sk) for account_id in account_ids] + base_block_hash = mocknet_helpers.get_latest_block_hash(addr=rpc_server[0], + port=rpc_server[1]) + accounts = [] + for ip, key in zip(ips, keys): + nonce = mocknet_helpers.get_nonce_for_pk(key.account_id, + key.pk, + addr=rpc_server[0], + port=rpc_server[1]) + accounts.append( + account_mod.Account(key, + nonce, + base_block_hash, + rpc_infos=[(ip, port)])) + + prometheus_client.start_http_server(args.metrics_port) + + with ThreadPoolExecutor() as executor: + for account in accounts: + executor.submit(pinger, account, interval_sec, rpc_server) diff --git a/pytest/lib/__init__.py b/pytest/lib/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pytest/lib/account.py b/pytest/lib/account.py new file mode 100644 index 000000000..c9c975d37 --- /dev/null +++ b/pytest/lib/account.py @@ -0,0 +1,143 @@ +import base64 +import json +import requests +import random +import time + +from transaction import ( + sign_payment_tx, sign_deploy_contract_tx, sign_function_call_tx, + sign_create_account_with_full_access_key_and_balance_tx, sign_staking_tx) +from key import Key +from utils import load_binary_file +from configured_logger import logger + +# Constant for 1 NEAR +UNC_BASE = 10**24 +TGAS = 10**12 + + +class Account: + + def __init__(self, + key, + init_nonce, + base_block_hash, + rpc_info=None, + rpc_infos=None): + """ + `rpc_info` takes precedence over `rpc_infos` for compatibility. One of them must be set. + If `rpc_info` is set, only this RPC node will be contacted. + Otherwise if `rpc_infos` is set, an RPC node will be selected randomly + from that set for each transaction attempt. + """ + self.key = key + self.nonce = init_nonce + self.base_block_hash = base_block_hash + assert rpc_info or rpc_infos + if rpc_info: + assert not rpc_infos + rpc_infos = [rpc_info] + self.rpc_infos = rpc_infos + assert key.account_id + self.tx_timestamps = [] + logger.debug( + f'Creating Account {key.account_id} {init_nonce} {self.rpc_infos[0]} {key.pk} {key.sk}' + ) + + # Returns an address of a random known RPC node. + def get_rpc_node_address(self): + rpc_addr, rpc_port = random.choice(self.rpc_infos) + return f'http://{rpc_addr}:{rpc_port}' + + def json_rpc(self, method, params): + j = { + 'method': method, + 'params': params, + 'id': 'dontcare', + 'jsonrpc': '2.0' + } + r = requests.post(self.get_rpc_node_address(), json=j, timeout=30) + return json.loads(r.content) + + def send_tx(self, signed_tx): + return self.json_rpc('broadcast_tx_async', + [base64.b64encode(signed_tx).decode('utf-8')]) + + def send_tx_sync(self, signed_tx): + return self.json_rpc('broadcast_tx_commit', + [base64.b64encode(signed_tx).decode('utf-8')]) + + def prep_tx(self): + self.tx_timestamps.append(time.time()) + self.nonce += 1 + + def send_transfer_tx(self, + dest_account_id, + transfer_amount=100, + base_block_hash=None): + self.prep_tx() + tx = sign_payment_tx(self.key, dest_account_id, transfer_amount, + self.nonce, base_block_hash or + self.base_block_hash) + return self.send_tx(tx) + + def send_deploy_contract_tx(self, wasm_filename, base_block_hash=None): + wasm_binary = load_binary_file(wasm_filename) + self.prep_tx() + tx = sign_deploy_contract_tx(self.key, wasm_binary, self.nonce, + base_block_hash or self.base_block_hash) + return self.send_tx(tx) + + def send_call_contract_tx(self, method_name, args, base_block_hash=None): + return self.send_call_contract_raw_tx(self.key.account_id, + method_name, + args, + 0, + base_block_hash=base_block_hash) + + def send_call_contract_raw_tx(self, + contract_id, + method_name, + args, + deposit, + base_block_hash=None): + self.prep_tx() + tx = sign_function_call_tx(self.key, contract_id, method_name, args, + 300 * TGAS, deposit, self.nonce, + base_block_hash or self.base_block_hash) + return self.send_tx(tx) + + def send_call_contract_raw_tx_sync(self, + contract_id, + method_name, + args, + deposit, + base_block_hash=None): + self.prep_tx() + tx = sign_function_call_tx(self.key, contract_id, method_name, args, + 300 * TGAS, deposit, self.nonce, + base_block_hash or self.base_block_hash) + return self.send_tx_sync(tx) + + def send_create_account_tx(self, new_account_id, base_block_hash=None): + self.prep_tx() + new_key = Key(new_account_id, self.key.pk, self.key.sk) + tx = sign_create_account_with_full_access_key_and_balance_tx( + self.key, new_account_id, new_key, 100 * UNC_BASE, self.nonce, + base_block_hash or self.base_block_hash) + return self.send_tx(tx) + + def send_stake_tx(self, stake_amount, base_block_hash=None): + self.prep_tx() + tx = sign_staking_tx(self.key, self.key, stake_amount, self.nonce, + base_block_hash or self.base_block_hash) + return self.send_tx(tx) + + def get_amount_yoctonear(self): + j = self.json_rpc( + 'query', { + 'request_type': 'view_account', + 'finality': 'optimistic', + 'account_id': self.key.account_id + }) + return int(j.get('result', {}).get('amount', 0)) diff --git a/pytest/lib/branches.py b/pytest/lib/branches.py new file mode 100644 index 000000000..81081c2c7 --- /dev/null +++ b/pytest/lib/branches.py @@ -0,0 +1,276 @@ +import os +import pathlib +import subprocess +import sys +import tempfile +import typing + +import requests +import semver +from configured_logger import logger + +_UNAME = os.uname()[0] +_IS_DARWIN = _UNAME == 'Darwin' +_BASEHREF = 'https://s3-us-west-1.amazonaws.com/build.nearprotocol.com' +_REPO_DIR = pathlib.Path(__file__).resolve().parents[2] +_OUT_DIR = _REPO_DIR / 'target/debug' +_IS_NAYDUCK = bool(os.getenv('NAYDUCK')) + + +def current_branch() -> str: + """Returns checked out branch name or sha if we’re on detached head.""" + branch = os.environ.get('BUILDKITE_BRANCH') + if branch: + return branch + try: + return subprocess.check_output( + ('git', 'symbolic-ref', '--short', '-q', 'HEAD')).strip().decode() + except subprocess.CalledProcessError as ex: + if ex.returncode != 1: + raise + # We’re on detached HEAD + return subprocess.check_output(('git', 'rev-parse', '@')).strip().decode() + + +def __get_latest_deploy(chain_id: str) -> typing.Tuple[str, str]: + """Returns latest (release, deploy) for given chain. + + Gets latest release and deploy identifier from S3 for given chain. Those + can be used to uniquely identify a uncd executable running on the chain. + """ + + def download(url: str) -> str: + logger.info(f"download {url}") + res = requests.get(url) + res.raise_for_status() + return res.text + + basehref = f'{_BASEHREF}/framework-deploy/{chain_id}' + release = download(f'{basehref}/latest_release') + deploy = download(f'{basehref}/latest_deploy') + + if release != 'master': + # Make sure it parses as a version + release = str(semver.VersionInfo.parse(release).finalize_version()) + + return release, deploy + + +class Executables(typing.NamedTuple): + root: pathlib.Path + uncd: pathlib.Path + + def node_config(self) -> typing.Dict[str, typing.Any]: + return { + 'local': True, + 'neard_root': self.root, + 'binary_name': self.uncd.name + } + + +def _compile_binary(branch: str) -> Executables: + """For given branch, compile binary. + + Stashes current changes, switches branch and then returns everything back. + """ + # TODO: download pre-compiled binary from github for beta/stable? + prev_branch = current_branch() + stash_output = subprocess.check_output(['git', 'stash']) + try: + subprocess.check_output([ + 'git', + # When checking out old releases we end up in a detached head state + # and git prints a scary warning about that. This config silences it. + '-c', + 'advice.detachedHead=false', + 'checkout', + str(branch), + ]) + try: + subprocess.check_output(['git', 'pull', 'origin', str(branch)]) + result = _compile_current(branch) + finally: + subprocess.check_output(['git', 'checkout', prev_branch]) + finally: + if stash_output != b"No local changes to save\n": + subprocess.check_output(['git', 'stash', 'pop']) + return result + + +def escaped(branch): + return branch.replace('/', '-') + + +def _compile_current(branch: str) -> Executables: + """Compile current branch.""" + prebuilt_neard = os.environ.get("CURRENT_NEARD") + if prebuilt_neard is not None: + logger.info( + f'Using `CURRENT_NEARD={prebuilt_neard}` uncd for branch {branch}') + try: + path = pathlib.Path(prebuilt_neard).resolve() + return Executables(path.parent, path) + except OSError as e: + logger.exception('Could not use `CURRENT_NEARD`, will build…') + + logger.info(f'Building uncd for branch {branch}') + subprocess.check_call(['cargo', 'build', '-p', 'uncd', '--bin', 'uncd'], + cwd=_REPO_DIR) + subprocess.check_call(['cargo', 'build', '-p', 'unc-test-contracts'], + cwd=_REPO_DIR) + branch = escaped(branch) + uncd = _OUT_DIR / f'uncd-{branch}' + (_OUT_DIR / 'uncd').rename(uncd) + return Executables(_OUT_DIR, uncd) + + +def patch_binary(binary: pathlib.Path) -> None: + """ + Patch a specified external binary if doing so is necessary for the host + system to be able to run it. + + Currently only supports NixOS. + """ + # Are we running on NixOS and require patching…? + try: + with open('/etc/os-release', 'r') as f: + if not any(line.strip() == 'ID=nixos' for line in f): + return + except FileNotFoundError: + return + if os.path.exists('/lib'): + return + # Build an output with patchelf and interpreter in it + nix_expr = ''' + with (import {}); + symlinkJoin { + name = "framework-dependencies"; + paths = [patchelf stdenv.cc.bintools gcc.cc.lib]; + } + ''' + path = subprocess.run(('nix-build', '-E', nix_expr), + capture_output=True, + encoding='utf-8').stdout.strip() + # Set the interpreter for the binary to NixOS'. + patchelf = f'{path}/bin/patchelf' + linker = (pathlib.Path(path) / "nix-support" / + "dynamic-linker").read_text().strip() + cmd = (patchelf, '--set-interpreter', linker, binary) + logger.debug('Patching NixOS interpreter {}'.format(cmd)) + subprocess.check_call(cmd) + cmd = (patchelf, '--set-rpath', '$ORIGIN:{}/lib'.format(path), binary) + logger.debug('Patching DSO rpath {}'.format(cmd)) + subprocess.check_call(cmd) + + +def __download_file_if_missing(filename: pathlib.Path, url: str) -> None: + """Downloads a file from given URL if it does not exist already. + + Does nothing if file `filename` already exists. Otherwise, downloads data + from `url` and saves them in `filename`. Downloading is done with `curl` + tool and on failure (i.e. if it returns non-zero exit code) `filename` is + not created. On success, the file’s mode is set to 0x555 (i.e. readable and + executable by anyone). + + Args: + filename: Path to the file. + url: URL of the file to download (if the file is missing). + """ + if filename.exists(): + if not filename.is_file(): + sys.exit(f'{filename} exists but is not a file') + return + + proto = '"=https"' if _IS_DARWIN else '=https' + cmd = ('curl', '--proto', proto, '--tlsv1.2', '-sSfL', url) + name = None + filename.parent.mkdir(parents=True, exist_ok=True) + try: + with tempfile.NamedTemporaryFile(dir=filename.parent, + delete=False) as tmp: + name = pathlib.Path(tmp.name) + logger.debug('Executing ' + ' '.join(cmd)) + subprocess.check_call(cmd, stdout=tmp) + patch_binary(name) + name.chmod(0o555) + name.rename(filename) + name = None + finally: + if name: + name.unlink() + + +def __download_binary(release: str, deploy: str) -> Executables: + """Download binary for given release and deploy.""" + logger.info(f'Getting uncd for {release}@{_UNAME} (deploy={deploy})') + uncd = _OUT_DIR / f'uncd-{release}-{deploy}' + basehref = f'{_BASEHREF}/framework/{_UNAME}/{release}/{deploy}' + __download_file_if_missing(uncd, f'{basehref}/uncd') + return Executables(_OUT_DIR, uncd) + + +class ABExecutables(typing.NamedTuple): + stable: Executables + current: Executables + release: str + deploy: str + + +def prepare_ab_test(chain_id: str = 'mainnet') -> ABExecutables: + """Prepares executable at HEAD and latest deploy at given chain. + + Args: + chain_id: Chain id to get latest deployed executable for. Can be + ‘master’, ‘testnet’ or ‘betanet’. + Returns: + An ABExecutables object where `current` describes executable built at + current HEAD while `stable` points at executable which is deployed in + production at given chain. `release` and `deploy` of the returned + object specify, well, the latest release and deploy running in + production at the chain. + """ + release, deploy, stable = __get_executables_for(chain_id) + + if _IS_NAYDUCK: + # On NayDuck the file is fetched from a builder host so there’s no need + # to build it. + current = Executables(_OUT_DIR, _OUT_DIR / 'uncd') + else: + current = _compile_current(current_branch()) + + return ABExecutables(stable=stable, + current=current, + release=release, + deploy=deploy) + + +def __get_executables_for(chain_id: str) -> typing.Tuple[str, str, Executables]: + """Returns latest deploy at given chain.""" + if chain_id not in ('mainnet', 'testnet', 'betanet'): + raise ValueError(f'Unexpected chain_id: {chain_id}; ' + 'expected mainnet, testnet or betanet') + + release, deploy = __get_latest_deploy(chain_id) + try: + executable = __download_binary(release, deploy) + except Exception as e: + if _IS_NAYDUCK: + logger.exception('RC binary should be downloaded for NayDuck.', e) + else: + logger.exception(e) + executable = _compile_binary(release) + return release, deploy, executable + + +def get_executables_for(chain_id: str) -> Executables: + """Prepares executable at HEAD and latest deploy at given chain. + + Args: + chain_id: Chain id to get latest deployed executable for. Can be + ‘master’, ‘testnet’ or ‘betanet’. + Returns: + An Executables object where pointing at executable which is deployed in + production at given chain. + """ + _, _, executable = __get_executables_for(chain_id) + return executable diff --git a/pytest/lib/cluster.py b/pytest/lib/cluster.py new file mode 100644 index 000000000..8d5794ed4 --- /dev/null +++ b/pytest/lib/cluster.py @@ -0,0 +1,1009 @@ +import atexit +import base64 +import json +import os +import pathlib +import rc +import requests +import shutil +import signal +import subprocess +import sys +import threading +import time +import traceback +import typing +import uuid +from rc import gcloud +from retrying import retry + +import base58 + +import network +from configured_logger import logger +from key import Key +from proxy import NodesProxy + +os.environ["ADVERSARY_CONSENT"] = "1" + +remote_nodes = [] +remote_nodes_lock = threading.Lock() +cleanup_remote_nodes_atexit_registered = False + + +class DownloadException(Exception): + pass + + +def atexit_cleanup(node): + logger.info("Cleaning up node %s:%s on script exit" % node.addr()) + logger.info("Executed store validity tests: %s" % node.store_tests) + try: + node.cleanup() + except: + logger.info("Cleaning failed!") + traceback.print_exc() + pass + + +def atexit_cleanup_remote(): + with remote_nodes_lock: + if remote_nodes: + rc.pmap(atexit_cleanup, remote_nodes) + + +# custom retry that is used in wait_for_rpc() and get_status() +def nretry(fn, timeout): + started = time.time() + delay = 0.05 + while True: + try: + return fn() + except: + if time.time() - started >= timeout: + raise + time.sleep(delay) + delay *= 1.2 + + +BootNode = typing.Union[None, 'BaseNode', typing.Iterable['BaseNode']] + + +def make_boot_nodes_arg(boot_node: BootNode) -> typing.Tuple[str]: + """Converts `boot_node` argument to `--boot-nodes` command line argument. + + If the argument is `None` returns an empty tuple. Otherwise, returns + a tuple representing arguments to be added to `uncd` invocation for setting + boot nodes according to `boot_node` argument. + + Apart from `None` as described above, `boot_node` can be a [`BaseNode`] + object, or an iterable (think list) of [`BaseNode`] objects. The boot node + address of a BaseNode object is contstructed using [`BaseNode.addr_with_pk`] + method. + + If iterable of nodes is given, the `uncd` is going to be configured with + multiple boot nodes. + + Args: + boot_node: Specification of boot node(s). + Returns: + A tuple to add to `uncd` invocation specifying boot node(s) if any + specified. + """ + if not boot_node: + return () + try: + it = iter(boot_node) + except TypeError: + it = iter((boot_node,)) + nodes = ','.join(node.addr_with_pk() for node in it) + if not nodes: + return () + return ('--boot-nodes', nodes) + + +class BlockId(typing.NamedTuple): + """Stores block’s height and hash. + + The values can be accessed either through properties or by structural + deconstruction, e.g.: + + block_height, block_hash = block_id + assert block_height == block_id.height + assert block_hash == block_id.hash + + Attributes: + height: Block’s height. + hash: Block’s hash encoding using base58. + hash_bytes: Block’s hash decoded as raw bytes. Note that this attribute + cannot be accessed through aforementioned deconstruction. + """ + height: int + hash: str + + @classmethod + def from_header(cls, header: typing.Dict[str, typing.Any]) -> 'BlockId': + return cls(height=int(header['height']), hash=header['hash']) + + @property + def hash_bytes(self) -> bytes: + return base58.b58decode(self.hash.encode('ascii')) + + def __str__(self) -> str: + return f'#{self.height} {self.hash}' + + def __eq__(self, rhs) -> bool: + return (isinstance(rhs, BlockId) and self.height == rhs.height and + self.hash == rhs.hash) + + +class BaseNode(object): + + def __init__(self): + self._start_proxy = None + self._proxy_local_stopped = None + self.proxy = None + self.store_tests = 0 + self.is_check_store = True + + def change_config(self, overrides: typing.Dict[str, typing.Any]) -> None: + """Change client config.json of a node by applying given overrides. + + Changes to the configuration need to be made while the node is stopped. + More precisely, while the changes may be made at any point, the node + reads the time at startup only. + + The overrides are a dictionary specifying new values for configuration + keys. Non-dictionary values are applied directly, while dictionaries + are non-recursively merged. For example if the original config is: + + { + 'foo': 42, + 'bar': {'a': 1, 'b': 2, 'c': {'A': 3}}, + } + + and overrides are: + + { + 'foo': 24, + 'bar': {'a': -1, 'c': {'D': 3}, 'd': 1}, + } + + then resulting configuration file will be: + + { + 'foo': 24, + 'bar': {'a': -1, 'b': 2, 'c': {'D': 3}, 'd': 1}, + } + + Args: + overrides: A dictionary of config overrides. Non-dictionary values + are set as is, dictionaries are non-recursively merged. + Raises: + NotImplementedError: Currently changing the configuration is + supported on local node only. + """ + name = type(self).__name__ + raise NotImplementedError('change_config not supported by ' + name) + + def _get_command_line(self, + unc_root, + node_dir, + boot_node: BootNode, + binary_name='uncd'): + cmd = (os.path.join(unc_root, binary_name), '--home', node_dir, 'run') + return cmd + make_boot_nodes_arg(boot_node) + + def addr_with_pk(self) -> str: + pk_hash = self.node_key.pk.split(':')[1] + host, port = self.addr() + return '{}@{}:{}'.format(pk_hash, host, port) + + def wait_for_rpc(self, timeout=1): + nretry(lambda: self.get_status(), timeout=timeout) + + def json_rpc(self, method, params, timeout=2): + j = { + 'method': method, + 'params': params, + 'id': 'dontcare', + 'jsonrpc': '2.0' + } + r = requests.post("http://%s:%s" % self.rpc_addr(), + json=j, + timeout=timeout) + r.raise_for_status() + return json.loads(r.content) + + def send_tx(self, signed_tx): + return self.json_rpc('broadcast_tx_async', + [base64.b64encode(signed_tx).decode('utf8')]) + + def send_tx_and_wait(self, signed_tx, timeout): + return self.json_rpc('broadcast_tx_commit', + [base64.b64encode(signed_tx).decode('utf8')], + timeout=timeout) + + def send_tx_rpc(self, signed_tx, timeout, wait_until='FINAL'): + return self.json_rpc('send_tx', { + 'signed_tx_base64': base64.b64encode(signed_tx).decode('utf8'), + 'wait_until': wait_until + }, + timeout=timeout) + + def get_status(self, + check_storage: bool = True, + timeout: float = 4, + verbose: bool = False): + r = requests.get("http://%s:%s/status" % self.rpc_addr(), + timeout=timeout) + r.raise_for_status() + status = json.loads(r.content) + if verbose: + logger.info(f'Status: {status}') + if check_storage and status['sync_info']['syncing'] == False: + # Storage is not guaranteed to be in consistent state while syncing + self.check_store() + if verbose: + logger.info(status) + return status + + def get_latest_block(self, **kw) -> BlockId: + sync_info = self.get_status(**kw)['sync_info'] + return BlockId(height=sync_info['latest_block_height'], + hash=sync_info['latest_block_hash']) + + def get_all_heights(self): + hash_ = self.get_latest_block().hash + heights = [] + + while True: + block = self.get_block(hash_) + if 'error' in block and 'data' in block[ + 'error'] and 'DB Not Found Error: BLOCK:' in block['error'][ + 'data']: + break + elif 'result' not in block: + logger.info(block) + + height = block['result']['header']['height'] + if height == 0: + break + heights.append(height) + hash_ = block['result']['header']['prev_hash'] + + return reversed(heights) + + def get_validators(self, epoch_id=None): + if epoch_id is None: + args = [None] + else: + args = {'epoch_id': epoch_id} + return self.json_rpc('validators', args) + + def get_account(self, acc, finality='optimistic', do_assert=True, **kwargs): + res = self.json_rpc('query', { + "request_type": "view_account", + "account_id": acc, + "finality": finality + }, **kwargs) + if do_assert: + assert 'error' not in res, res + + return res + + def call_function(self, + acc, + method, + args, + finality='optimistic', + timeout=2): + return self.json_rpc('query', { + "request_type": "call_function", + "account_id": acc, + "method_name": method, + "args_base64": args, + "finality": finality + }, + timeout=timeout) + + def get_access_key_list(self, acc, finality='optimistic'): + return self.json_rpc( + 'query', { + "request_type": "view_access_key_list", + "account_id": acc, + "finality": finality + }) + + def get_nonce_for_pk(self, acc, pk, finality='optimistic'): + for access_key in self.get_access_key_list(acc, + finality)['result']['keys']: + if access_key['public_key'] == pk: + return access_key['access_key']['nonce'] + return None + + def get_block(self, block_id, **kwargs): + return self.json_rpc('block', [block_id], **kwargs) + + def get_block_by_height(self, block_height, **kwargs): + return self.json_rpc('block', {'block_id': block_height}, **kwargs) + + def get_final_block(self, **kwargs): + return self.json_rpc('block', {'finality': 'final'}, **kwargs) + + def get_chunk(self, chunk_id): + return self.json_rpc('chunk', [chunk_id]) + + def get_tx(self, tx_hash, tx_recipient_id): + return self.json_rpc('tx', [tx_hash, tx_recipient_id]) + + def get_changes_in_block(self, changes_in_block_request): + return self.json_rpc('EXPERIMENTAL_changes_in_block', + changes_in_block_request) + + def get_changes(self, changes_request): + return self.json_rpc('EXPERIMENTAL_changes', changes_request) + + def validators(self): + return set( + map(lambda v: v['account_id'], + self.get_status()['validators'])) + + def stop_checking_store(self): + logger.warning("Stopping checking Storage for inconsistency for %s:%s" % + self.addr()) + self.is_check_store = False + + def check_store(self): + if self.is_check_store: + res = self.json_rpc('adv_check_store', []) + if not 'result' in res: + # cannot check Storage Consistency for the node, possibly not Adversarial Mode is running + pass + else: + if res['result'] == 0: + logger.error( + "Storage for %s:%s in inconsistent state, stopping" % + self.addr()) + self.kill() + self.store_tests += res['result'] + + +class RpcNode(BaseNode): + """ A running node only interact by rpc queries """ + + def __init__(self, host, rpc_port): + super(RpcNode, self).__init__() + self.host = host + self.rpc_port = rpc_port + + def rpc_addr(self): + return (self.host, self.rpc_port) + + +class LocalNode(BaseNode): + + def __init__(self, + port, + rpc_port, + unc_root, + node_dir, + blacklist, + binary_name=None, + single_node=False): + super(LocalNode, self).__init__() + self.port = port + self.rpc_port = rpc_port + self.unc_root = str(unc_root) + self.node_dir = node_dir + self.binary_name = binary_name or 'uncd' + self.cleaned = False + self.validator_key = Key.from_json_file( + os.path.join(node_dir, "validator_key.json")) + self.node_key = Key.from_json_file( + os.path.join(node_dir, "node_key.json")) + self.signer_key = Key.from_json_file( + os.path.join(node_dir, "validator_key.json")) + self._process = None + + self.change_config({ + 'network': { + 'addr': f'0.0.0.0:{port}', + 'blacklist': blacklist + }, + 'rpc': { + 'addr': f'0.0.0.0:{rpc_port}', + }, + 'consensus': { + 'min_num_peers': int(not single_node) + }, + }) + + atexit.register(atexit_cleanup, self) + + def change_config(self, overrides: typing.Dict[str, typing.Any]) -> None: + apply_config_changes(self.node_dir, overrides) + + def addr(self): + return ("127.0.0.1", self.port) + + def rpc_addr(self): + return ("127.0.0.1", self.rpc_port) + + def start_proxy_if_needed(self): + if self._start_proxy is not None: + self._proxy_local_stopped = self._start_proxy() + + def output_logs(self): + stdout = pathlib.Path(self.node_dir) / 'stdout' + stderr = pathlib.Path(self.node_dir) / 'stderr' + if os.environ.get('BUILDKITE'): + logger.info('=== stdout: ') + logger.info(stdout.read_text('utf-8', 'replace')) + logger.info('=== stderr: ') + logger.info(stderr.read_text('utf-8', 'replace')) + else: + logger.info(f'=== stdout: available at {stdout}') + logger.info(f'=== stderr: available at {stderr}') + + def start(self, + *, + boot_node: BootNode = None, + skip_starting_proxy=False, + extra_env: typing.Dict[str, str] = dict()): + if self._proxy_local_stopped is not None: + while self._proxy_local_stopped.value != 2: + logger.info(f'Waiting for previous proxy instance to close') + time.sleep(1) + + env = os.environ.copy() + env["RUST_BACKTRACE"] = "1" + env["RUST_LOG"] = "actix_web=warn,mio=warn,tokio_util=warn,actix_server=warn,actix_http=warn," + env.get( + "RUST_LOG", "debug") + env.update(extra_env) + + cmd = self._get_command_line( + self.unc_root, + self.node_dir, + boot_node, + self.binary_name, + ) + node_dir = pathlib.Path(self.node_dir) + self.stdout_name = node_dir / 'stdout' + self.stderr_name = node_dir / 'stderr' + with open(self.stdout_name, 'ab') as stdout, \ + open(self.stderr_name, 'ab') as stderr: + self._process = subprocess.Popen(cmd, + stdin=subprocess.DEVNULL, + stdout=stdout, + stderr=stderr, + env=env) + self._pid = self._process.pid + + if not skip_starting_proxy: + self.start_proxy_if_needed() + + try: + self.wait_for_rpc(10) + except: + logger.error( + '=== failed to start node, rpc does not ready in 10 seconds') + + def kill(self, *, gentle=False): + """Kills the process. If `gentle` sends SIGINT before killing.""" + if self._proxy_local_stopped is not None: + self._proxy_local_stopped.value = 1 + if self._process and gentle: + self._process.send_signal(signal.SIGINT) + try: + self._process.wait(5) + self._process = None + except subprocess.TimeoutExpired: + pass + if self._process: + self._process.kill() + self._process.wait(5) + self._process = None + + def reset_data(self): + shutil.rmtree(os.path.join(self.node_dir, "data")) + + def reset_validator_key(self, new_key): + self.validator_key = new_key + with open(os.path.join(self.node_dir, "validator_key.json"), 'w+') as f: + json.dump(new_key.to_json(), f) + + def reset_node_key(self, new_key): + self.node_key = new_key + with open(os.path.join(self.node_dir, "node_key.json"), 'w+') as f: + json.dump(new_key.to_json(), f) + + def cleanup(self): + if self.cleaned: + return + + try: + self.kill() + except: + logger.critical('Kill failed on cleanup!', exc_info=sys.exc_info()) + + # move the node dir to avoid weird interactions with multiple serial test invocations + target_path = self.node_dir + '_finished' + if os.path.exists(target_path) and os.path.isdir(target_path): + shutil.rmtree(target_path) + os.rename(self.node_dir, target_path) + self.node_dir = target_path + self.output_logs() + self.cleaned = True + + def stop_network(self): + logger.info(f'Stopping network for process {self._pid}') + network.stop(self._pid) + + def resume_network(self): + logger.info(f'Resuming network for process {self._pid}') + network.resume_network(self._pid) + + +class GCloudNode(BaseNode): + + def __init__(self, *args, username=None, project=None, ssh_key_path=None): + if len(args) == 1: + name = args[0] + # Get existing instance assume it's ready to run. + self.instance_name = name + self.port = 24567 + self.rpc_port = 3030 + self.machine = gcloud.get(name, + username=username, + project=project, + ssh_key_path=ssh_key_path) + self.ip = self.machine.ip + elif len(args) == 4: + # Create new instance from scratch + instance_name, zone, node_dir, binary = args + self.instance_name = instance_name + self.port = 24567 + self.rpc_port = 3030 + self.node_dir = node_dir + self.machine = gcloud.create( + name=instance_name, + machine_type='n1-standard-2', + disk_size='50G', + image_project='gce-uefi-images', + image_family='ubuntu-1804-lts', + zone=zone, + firewall_allows=['tcp:3030', 'tcp:24567'], + min_cpu_platform='Intel Skylake', + preemptible=False, + ) + # self.ip = self.machine.ip + self._upload_config_files(node_dir) + self._download_binary(binary) + with remote_nodes_lock: + global cleanup_remote_nodes_atexit_registered + if not cleanup_remote_nodes_atexit_registered: + atexit.register(atexit_cleanup_remote) + cleanup_remote_nodes_atexit_registered = True + else: + raise Exception() + + def _upload_config_files(self, node_dir): + self.machine.run('bash', input='mkdir -p ~/.near') + self.machine.upload(os.path.join(node_dir, '*.json'), + f'/home/{self.machine.username}/.near/') + self.validator_key = Key.from_json_file( + os.path.join(node_dir, "validator_key.json")) + self.node_key = Key.from_json_file( + os.path.join(node_dir, "node_key.json")) + self.signer_key = Key.from_json_file( + os.path.join(node_dir, "validator_key.json")) + + @retry(wait_fixed=1000, stop_max_attempt_number=3) + def _download_binary(self, binary): + p = self.machine.run('bash', + input=f''' +/snap/bin/gsutil cp gs://nearprotocol_nearcore_release/{binary} uncd +chmod +x uncd +''') + if p.returncode != 0: + raise DownloadException(p.stderr) + + def addr(self): + return (self.ip, self.port) + + def rpc_addr(self): + return (self.ip, self.rpc_port) + + def start(self, + *, + boot_node: BootNode = None, + extra_env: typing.Dict[str, str] = dict()): + if "RUST_BACKTRACE" not in extra_env: + extra_env["RUST_BACKTRACE"] = "1" + extra_env = [f"{k}={v}" for (k, v) in extra_env] + extra_env = " ".join(extra_env) + self.machine.run_detach_tmux( + extra_env + + " ".join(self._get_command_line('.', '.near', boot_node))) + self.wait_for_rpc(timeout=30) + + def kill(self): + self.machine.run('tmux send-keys -t python-rc C-c') + time.sleep(3) + self.machine.kill_detach_tmux() + + def destroy_machine(self): + self.machine.delete() + + def cleanup(self): + self.kill() + # move the node dir to avoid weird interactions with multiple serial test invocations + target_path = self.node_dir + '_finished' + if os.path.exists(target_path) and os.path.isdir(target_path): + shutil.rmtree(target_path) + os.rename(self.node_dir, target_path) + + # Get log and delete machine + rc.run(f'mkdir -p /tmp/pytest_remote_log') + self.machine.download( + '/tmp/python-rc.log', + f'/tmp/pytest_remote_log/{self.machine.name}.log') + self.destroy_machine() + + def json_rpc(self, method, params, timeout=15): + return super().json_rpc(method, params, timeout=timeout) + + def get_status(self): + r = nretry(lambda: requests.get("http://%s:%s/status" % self.rpc_addr(), + timeout=15), + timeout=45) + r.raise_for_status() + return json.loads(r.content) + + def stop_network(self): + rc.run( + f'gcloud compute firewall-rules create {self.machine.name}-stop --direction=EGRESS --priority=1000 --network=default --action=DENY --rules=all --target-tags={self.machine.name}' + ) + + def resume_network(self): + rc.run(f'gcloud compute firewall-rules delete {self.machine.name}-stop', + input='yes\n') + + def reset_validator_key(self, new_key): + self.validator_key = new_key + with open(os.path.join(self.node_dir, "validator_key.json"), 'w+') as f: + json.dump(new_key.to_json(), f) + self.machine.upload(os.path.join(self.node_dir, 'validator_key.json'), + f'/home/{self.machine.username}/.near/') + + +def spin_up_node(config, + unc_root, + node_dir, + ordinal, + *, + boot_node: BootNode = None, + blacklist=[], + proxy=None, + skip_starting_proxy=False, + single_node=False): + is_local = config['local'] + + args = make_boot_nodes_arg(boot_node) + logger.info("Starting node %s %s" % + (ordinal, + ('with ' + '='.join(args) if args else 'as BOOT NODE'))) + if is_local: + blacklist = [ + "127.0.0.1:%s" % (24567 + 10 + bl_ordinal) + for bl_ordinal in blacklist + ] + node = LocalNode(24567 + 10 + ordinal, 3030 + 10 + ordinal, + unc_root, node_dir, blacklist, + config.get('binary_name'), single_node) + else: + # TODO: Figure out how to know IP address beforehand for remote deployment. + assert len( + blacklist) == 0, "Blacklist is only supported in LOCAL deployment." + + instance_name = '{}-{}-{}'.format( + config['remote'].get('instance_name', 'unc-pytest'), ordinal, + uuid.uuid4()) + zones = config['remote']['zones'] + zone = zones[ordinal % len(zones)] + node = GCloudNode(instance_name, zone, node_dir, + config['remote']['binary']) + with remote_nodes_lock: + remote_nodes.append(node) + logger.info(f"node {ordinal} machine created") + + if proxy is not None: + proxy.proxify_node(node) + + node.start(boot_node=boot_node, skip_starting_proxy=skip_starting_proxy) + time.sleep(3) + logger.info(f"node {ordinal} started") + return node + + +def init_cluster(num_nodes, + num_observers, + num_shards, + config, + genesis_config_changes, + client_config_changes, + prefix="test"): + """ + Create cluster configuration + """ + if 'local' not in config and 'nodes' in config: + logger.critical( + "Attempt to launch a regular test with a mocknet config") + sys.exit(1) + + if not prefix.startswith("test"): + logger.critical(f"The prefix must begin with 'test'. prefix = {prefix}") + sys.exit(1) + + is_local = config['local'] + unc_root = config['unc_root'] + binary_name = config.get('binary_name', 'uncd') + + logger.info("Creating %s cluster configuration with %s nodes" % + ("LOCAL" if is_local else "REMOTE", num_nodes + num_observers)) + + binary_path = os.path.join(unc_root, binary_name) + process = subprocess.Popen( + [ + binary_path, + "localnet", + "--validators", + str(num_nodes), + "--non-validators", + str(num_observers), + "--shards", + str(num_shards), + "--tracked-shards", + "none", + "--prefix", + prefix, + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + out, err = process.communicate() + assert 0 == process.returncode, err + + node_dirs = [ + line.split()[-1] + for line in err.decode('utf8').split('\n') + if '/test' in line + ] + assert len( + node_dirs + ) == num_nodes + num_observers, "node dirs: %s num_nodes: %s num_observers: %s" % ( + len(node_dirs), num_nodes, num_observers) + + logger.info("Search for stdout and stderr in %s" % node_dirs) + # apply config changes + for i, node_dir in enumerate(node_dirs): + apply_genesis_changes(node_dir, genesis_config_changes) + overrides = client_config_changes.get(i) + if overrides: + apply_config_changes(node_dir, overrides) + + return unc_root, node_dirs + + +def apply_genesis_changes(node_dir, genesis_config_changes): + # apply genesis.json changes + fname = os.path.join(node_dir, 'genesis.json') + with open(fname) as fd: + genesis_config = json.load(fd) + for change in genesis_config_changes: + cur = genesis_config + for s in change[:-2]: + cur = cur[s] + assert change[-2] in cur + cur[change[-2]] = change[-1] + with open(fname, 'w') as fd: + json.dump(genesis_config, fd, indent=2) + + +def apply_config_changes(node_dir, client_config_change): + # apply config.json changes + fname = os.path.join(node_dir, 'config.json') + with open(fname) as fd: + config_json = json.load(fd) + + # ClientConfig keys which are valid but may be missing from the config.json + # file. Those are often Option types which are not stored in JSON file + # when None. + allowed_missing_configs = ( + 'archive', + 'consensus.block_fetch_horizon', + 'consensus.min_block_production_delay', + 'consensus.state_sync_timeout', + 'expected_shutdown', + 'log_summary_period', + 'max_gas_burnt_view', + 'rosetta_rpc', + 'save_trie_changes', + 'split_storage', + 'state_sync', + 'state_sync_enabled', + 'store.state_snapshot_enabled', + 'tracked_shard_schedule', + ) + + for k, v in client_config_change.items(): + if not (k in allowed_missing_configs or k in config_json): + raise ValueError(f'Unknown configuration option: {k}') + if k in config_json and isinstance(v, dict): + config_json[k].update(v) + else: + # Support keys in the form of "a.b.c". + parts = k.split('.') + current = config_json + for part in parts[:-1]: + if part not in current: + raise ValueError( + f'{part} is not found in config.json. Key={k}, Value={v}' + ) + current = current[part] + current[parts[-1]] = v + + with open(fname, 'w') as fd: + json.dump(config_json, fd, indent=2) + + +def get_config_json(node_dir): + fname = os.path.join(node_dir, 'config.json') + with open(fname) as fd: + return json.load(fd) + + +def set_config_json(node_dir, config_json): + fname = os.path.join(node_dir, 'config.json') + with open(fname, 'w') as fd: + json.dump(config_json, fd, indent=2) + + +def start_cluster(num_nodes, + num_observers, + num_shards, + config, + genesis_config_changes, + client_config_changes, + message_handler=None): + if not config: + config = load_config() + + dot_near = pathlib.Path.home() / '.near' + if (dot_near / 'test0').exists(): + unc_root = config['unc_root'] + node_dirs = [ + str(dot_near / name) + for name in os.listdir(dot_near) + if name.startswith('test') and not name.endswith('_finished') + ] + else: + unc_root, node_dirs = init_cluster(num_nodes, num_observers, + num_shards, config, + genesis_config_changes, + client_config_changes) + + proxy = NodesProxy(message_handler) if message_handler is not None else None + ret = [] + + def spin_up_node_and_push(i, boot_node: BootNode): + single_node = (num_nodes == 1) and (num_observers == 0) + node = spin_up_node(config, + unc_root, + node_dirs[i], + i, + boot_node=boot_node, + proxy=proxy, + skip_starting_proxy=True, + single_node=single_node) + ret.append((i, node)) + return node + + boot_node = spin_up_node_and_push(0, None) + + handles = [] + for i in range(1, num_nodes + num_observers): + handle = threading.Thread(target=spin_up_node_and_push, + args=(i, boot_node)) + handle.start() + handles.append(handle) + + for handle in handles: + handle.join() + + nodes = [node for _, node in sorted(ret)] + for node in nodes: + node.start_proxy_if_needed() + + return nodes + + +ROOT_DIR = pathlib.Path(__file__).resolve().parents[2] + + +def get_unc_root(): + cargo_target_dir = os.environ.get('CARGO_TARGET_DIR', 'target') + default_root = (ROOT_DIR / cargo_target_dir / 'debug').resolve() + return os.environ.get('unc_ROOT', str(default_root)) + + +DEFAULT_CONFIG = { + 'local': True, + 'unc_root': get_unc_root(), + 'binary_name': 'uncd', + 'release': False, +} +CONFIG_ENV_VAR = 'unc_PYTEST_CONFIG' + + +def load_config(): + config = DEFAULT_CONFIG + + config_file = os.environ.get(CONFIG_ENV_VAR, '') + if config_file: + try: + with open(config_file) as f: + new_config = json.load(f) + config.update(new_config) + logger.info(f"Load config from {config_file}, config {config}") + except FileNotFoundError: + logger.info( + f"Failed to load config file, use default config {config}") + else: + logger.info(f"Use default config {config}") + return config + + +# Returns the protocol version of the binary. +def get_binary_protocol_version(config) -> typing.Optional[int]: + binary_name = config.get('binary_name', 'uncd') + unc_root = config.get('unc_root') + binary_path = os.path.join(unc_root, binary_name) + + # Get the protocol version of the binary + # The --version output looks like this: + # uncd (release trunk) (build 1.1.0-3884-ge93793a61-modified) (rustc 1.71.0) (protocol 137) (db 37) + out = subprocess.check_output([binary_path, "--version"], text=True) + out = out.replace('(', '') + out = out.replace(')', '') + tokens = out.split() + n = len(tokens) + for i in range(n): + if tokens[i] == "protocol" and i + 1 < n: + return int(tokens[i + 1]) + return None + + +def corrupt_state_snapshot(config, node_dir, shard_layout_version): + unc_root = config['unc_root'] + binary_name = config.get('binary_name', 'uncd') + binary_path = os.path.join(unc_root, binary_name) + + cmd = [ + binary_path, + "--home", + node_dir, + "database", + "corrupt-state-snapshot", + "--shard-layout-version", + str(shard_layout_version), + ] + + env = os.environ.copy() + env["RUST_BACKTRACE"] = "1" + env["RUST_LOG"] = "db=warn,db_opener=warn," + env.get("RUST_LOG", "debug") + + out = subprocess.check_output(cmd, text=True, env=env) + + return out diff --git a/pytest/lib/configured_logger.py b/pytest/lib/configured_logger.py new file mode 100644 index 000000000..138a50ca2 --- /dev/null +++ b/pytest/lib/configured_logger.py @@ -0,0 +1,51 @@ +import logging +import uuid +import sys + +from typing import Optional + +# LogLevel type since logging lib doesn't define its own enum/type for it +LogLevel = int + + +def new_logger( + name: Optional[str] = None, + level: LogLevel = logging.INFO, + outfile: Optional[str] = None, + stderr: Optional[bool] = None, +) -> logging.Logger: + """ + Create a new configured logger. Used mainly by pytests. + + :param name: The name of the logger. Defaults to "test". + :param level: The logging level. Defaults to DEBUG to log everything. + :param outfile: Optional to set. When set, will log to a file instead of stdout. + :param stderr: Optional to set. If outfile is not set, and stderr is set to True, then will log to stderr instead of stdout. + :return: The configured logger. + """ + # If name is not specified, create one so that this can be a separate logger. + if name is None: + name = f"logger_{uuid.uuid1()}" + + log = logging.getLogger(name) + log.setLevel(level) + fmt = logging.Formatter('[%(asctime)s] %(levelname)s: %(message)s', + '%Y-%m-%d %H:%M:%S') + + if outfile is not None: + handler = logging.FileHandler(outfile) + elif stderr: + handler = logging.StreamHandler(sys.stderr) + else: + handler = logging.StreamHandler(sys.stdout) + handler.setLevel(level) + handler.setFormatter(fmt) + + log.addHandler(handler) + log.propagate = False + + return log + + +# global logger for testing purposes: +logger = new_logger("test") diff --git a/pytest/lib/data.py b/pytest/lib/data.py new file mode 100644 index 000000000..0c00aae2f --- /dev/null +++ b/pytest/lib/data.py @@ -0,0 +1,51 @@ +import numpy as np +from sklearn.liunc_model import LinearRegression + + +def flatten(ll): + ''' + Flattens a list of lists into a single list + ''' + return [item for sublist in ll for item in sublist] + + +def compute_cumulative(xs): + ''' + Computes a running total (i.e. cumulative sum) + for the given list. + E.g. given [1, 2, 3, 4] the return value will be + [1, 3, 6, 10]. + ''' + total = xs[0] + result = [total] + for x in xs[1:]: + total += x + result.append(total) + return result + + +def liunc_regression(xs, ys): + ''' + Fits a line `y = mx + b` to the given data points + ''' + x = np.array(xs).reshape((-1, 1)) + y = np.array(ys) + model = LinearRegression().fit(x, y) + return { + 'slope': model.coef_[0], + 'intercept': model.intercept_, + 'R^2': model.score(x, y), + } + + +def compute_rate(timestamps): + ''' + Given a list of timestamps indicating the times + some event occured, returns the average rate at + which the events happen. If the units of the + timestamps are seconds, then the output units will + be `events/s`. + ''' + cumulative_events = [i for i in range(1, len(timestamps) + 1)] + fit = liunc_regression(timestamps, cumulative_events) + return fit['slope'] diff --git a/pytest/lib/key.py b/pytest/lib/key.py new file mode 100644 index 000000000..32a42e3d7 --- /dev/null +++ b/pytest/lib/key.py @@ -0,0 +1,78 @@ +import base58 +import json +import os +import typing + +import ed25519 + + +class Key: + account_id: str + pk: str + sk: str + + def __init__(self, account_id: str, pk: str, sk: str) -> None: + super(Key, self).__init__() + self.account_id = account_id + self.pk = pk + self.sk = sk + + @classmethod + def from_random(cls, account_id: str) -> 'Key': + keys = ed25519.create_keypair(entropy=os.urandom) + return cls.from_keypair(account_id, keys) + + @classmethod + def implicit_account(cls) -> 'Key': + keys = ed25519.create_keypair(entropy=os.urandom) + account_id = keys[1].to_bytes().hex() + return cls.from_keypair(account_id, keys) + + @classmethod + def from_json(cls, j: typing.Dict[str, str]): + return cls(j['account_id'], j['public_key'], j['secret_key']) + + @classmethod + def from_json_file(cls, filename: str): + with open(filename) as rd: + return cls.from_json(json.load(rd)) + + @classmethod + def from_seed_testonly(cls, account_id: str, seed: str = None) -> 'Key': + """ + Deterministically produce an **insecure** signer pair from a seed. + + If no seed is provided, the account id is used as seed. + """ + if seed is None: + seed = account_id + # use the repeated seed string as secret key by injecting fake entropy + fake_entropy = lambda length: (seed * (1 + int(length / len(seed))) + ).encode()[:length] + keys = ed25519.create_keypair(entropy=fake_entropy) + return cls.from_keypair(account_id, keys) + + @classmethod + def from_keypair(cls, account_id, keys): + sk = 'ed25519:' + base58.b58encode(keys[0].to_bytes()).decode('ascii') + pk = 'ed25519:' + base58.b58encode(keys[1].to_bytes()).decode('ascii') + return cls(account_id, pk, sk) + + def decoded_pk(self) -> bytes: + key = self.pk.split(':')[1] if ':' in self.pk else self.pk + return base58.b58decode(key.encode('ascii')) + + def decoded_sk(self) -> bytes: + key = self.sk.split(':')[1] if ':' in self.sk else self.sk + return base58.b58decode(key.encode('ascii')) + + def to_json(self): + return { + 'account_id': self.account_id, + 'public_key': self.pk, + 'secret_key': self.sk + } + + def sign_bytes(self, data: typing.Union[bytes, bytearray]) -> bytes: + sk = self.decoded_sk() + return ed25519.SigningKey(sk).sign(bytes(data)) diff --git a/pytest/lib/lightclient.py b/pytest/lib/lightclient.py new file mode 100644 index 000000000..f359022cb --- /dev/null +++ b/pytest/lib/lightclient.py @@ -0,0 +1,158 @@ +from serializer import BinarySerializer +import hashlib, base58 +import nacl.signing +from utils import combine_hash + +ED_PREFIX = "ed25519:" + + +class BlockHeaderInnerLite: + pass + + +inner_lite_schema = dict([ + [ + BlockHeaderInnerLite, { + 'kind': + 'struct', + 'fields': [ + ['height', 'u64'], + ['epoch_id', [32]], + ['next_epoch_id', [32]], + ['prev_state_root', [32]], + ['outcome_root', [32]], + ['timestamp', 'u64'], + ['next_bp_hash', [32]], + ['block_merkle_root', [32]], + ] + } + ], +]) + + +def compute_block_hash(inner_lite_view, inner_rest_hash, prev_hash): + inner_rest_hash = base58.b58decode(inner_rest_hash) + prev_hash = base58.b58decode(prev_hash) + + inner_lite = BlockHeaderInnerLite() + inner_lite.height = inner_lite_view['height'] + inner_lite.epoch_id = base58.b58decode(inner_lite_view['epoch_id']) + inner_lite.next_epoch_id = base58.b58decode( + inner_lite_view['next_epoch_id']) + inner_lite.prev_state_root = base58.b58decode( + inner_lite_view['prev_state_root']) + inner_lite.outcome_root = base58.b58decode(inner_lite_view['outcome_root']) + inner_lite.timestamp = int(inner_lite_view['timestamp_nanosec']) + inner_lite.next_bp_hash = base58.b58decode(inner_lite_view['next_bp_hash']) + inner_lite.block_merkle_root = base58.b58decode( + inner_lite_view['block_merkle_root']) + + msg = BinarySerializer(inner_lite_schema).serialize(inner_lite) + inner_lite_hash = hashlib.sha256(msg).digest() + inner_hash = combine_hash(inner_lite_hash, inner_rest_hash) + final_hash = combine_hash(inner_hash, prev_hash) + + return base58.b58encode(final_hash) + + +# follows the spec from NEP 25 (https://github.com/nearprotocol/NEPs/pull/25) +def validate_light_client_block(last_known_block, + new_block, + block_producers_map, + panic=False): + new_block_hash = compute_block_hash(new_block['inner_lite'], + new_block['inner_rest_hash'], + new_block['prev_block_hash']) + next_block_hash_decoded = combine_hash( + base58.b58decode(new_block['next_block_inner_hash']), + base58.b58decode(new_block_hash)) + + if new_block['inner_lite']['epoch_id'] not in [ + last_known_block['inner_lite']['epoch_id'], + last_known_block['inner_lite']['next_epoch_id'] + ]: + if panic: + assert False + return False + + block_producers = block_producers_map[new_block['inner_lite']['epoch_id']] + if len(new_block['approvals_after_next']) != len(block_producers): + if panic: + assert False + return False + + total_stake = 0 + approved_stake = 0 + + for approval, stake in zip(new_block['approvals_after_next'], + block_producers): + total_stake += int(stake['stake']) + + if approval is None: + continue + + approved_stake += int(stake['stake']) + + public_key = stake['public_key'] + + signature = base58.b58decode(approval[len(ED_PREFIX):]) + verify_key = nacl.signing.VerifyKey( + base58.b58decode(public_key[len(ED_PREFIX):])) + + approval_message = bytearray() + approval_message.append(0) + approval_message += next_block_hash_decoded + approval_message.append(new_block['inner_lite']['height'] + 2) + for i in range(7): + approval_message.append(0) + approval_message = bytes(approval_message) + verify_key.verify(approval_message, signature) + + threshold = total_stake * 2 // 3 + if approved_stake <= threshold: + if panic: + assert False + return False + + if new_block['inner_lite']['epoch_id'] == last_known_block['inner_lite'][ + 'next_epoch_id']: + if new_block['next_bps'] is None: + if panic: + assert False + return False + + print(new_block['next_bps']) + serialized_next_bp = bytearray() + serialized_next_bp.append(len(new_block['next_bps'])) + for i in range(3): + serialized_next_bp.append(0) + for bp in new_block['next_bps']: + version = 0 + if 'validator_stake_struct_version' in bp: + # version of ValidatorStake enum + version = int(bp['validator_stake_struct_version'][1:]) - 1 + serialized_next_bp.append(version) + serialized_next_bp.append(5) + for i in range(3): + serialized_next_bp.append(0) + serialized_next_bp += bp['account_id'].encode('utf-8') + serialized_next_bp.append(0) # public key type + serialized_next_bp += base58.b58decode( + bp['public_key'][len(ED_PREFIX):]) + stake = int(bp['stake']) + for i in range(16): + serialized_next_bp.append(stake & 255) + stake >>= 8 + + serialized_next_bp = bytes(serialized_next_bp) + + computed_hash = base58.b58encode( + hashlib.sha256(serialized_next_bp).digest()) + if computed_hash != new_block['inner_lite']['next_bp_hash'].encode( + 'utf-8'): + if panic: + assert False + return False + + block_producers_map[new_block['inner_lite'] + ['next_epoch_id']] = new_block['next_bps'] diff --git a/pytest/lib/messages/__init__.py b/pytest/lib/messages/__init__.py new file mode 100644 index 000000000..b2f15d68b --- /dev/null +++ b/pytest/lib/messages/__init__.py @@ -0,0 +1,9 @@ +from .block import block_schema +from .bridge import bridge_schema +from .crypto import crypto_schema +from .network import network_schema +from .shard import shard_schema +from .tx import tx_schema + +schema = dict(block_schema + bridge_schema + crypto_schema + network_schema + + shard_schema + tx_schema) diff --git a/pytest/lib/messages/block.py b/pytest/lib/messages/block.py new file mode 100644 index 000000000..ffbb3f517 --- /dev/null +++ b/pytest/lib/messages/block.py @@ -0,0 +1,744 @@ +from messages.crypto import PublicKey, Signature, MerklePath, ShardProof +from messages.tx import Receipt, SignedTransaction + + +class Block: + + def header(self): + if self.enum == 'BlockV1': + return self.BlockV1.header + elif self.enum == 'BlockV2': + return self.BlockV2.header + elif self.enum == 'BlockV3': + return self.BlockV3.header + assert False, "header is called on Block, but the enum variant `%s` is unknown" % self.enum + + def chunks(self): + if self.enum == 'BlockV1': + return self.BlockV1.chunks + elif self.enum == 'BlockV2': + return self.BlockV2.chunks + elif self.enum == 'BlockV3': + return self.BlockV3.body.chunks + assert False, "chunks is called on Block, but the enum variant `%s` is unknown" % self.enum + + +class BlockV1: + pass + + +class BlockV2: + pass + + +class BlockV3: + pass + + +class BlockBody: + pass + + +class BlockHeader: + + def inner_lite(self): + if self.enum == 'BlockHeaderV4': + return self.BlockHeaderV4.inner_lite + elif self.enum == 'BlockHeaderV3': + return self.BlockHeaderV3.inner_lite + elif self.enum == 'BlockHeaderV2': + return self.BlockHeaderV2.inner_lite + elif self.enum == 'BlockHeaderV1': + return self.BlockHeaderV1.inner_lite + assert False, "inner_lite is called on BlockHeader, but the enum variant `%s` is unknown" % self.enum + + +class BlockHeaderV1: + pass + + +class BlockHeaderV2: + pass + + +class BlockHeaderV3: + pass + + +class BlockHeaderV4: + pass + + +class BlockHeaderInnerLite: + pass + + +class BlockHeaderInnerRest: + pass + + +class BlockHeaderInnerRestV2: + pass + + +class BlockHeaderInnerRestV3: + pass + + +class BlockHeaderInnerRestV4: + pass + + +class ShardChunk: + pass + + +class ShardChunkV1: + pass + + +class ShardChunkV2: + pass + + +class ShardChunkHeader: + + @property + def signature(self): + if self.enum == 'V1': + return self.V1.signature + elif self.enum == 'V2': + return self.V2.signature + elif self.enum == 'V3': + return self.V3.signature + assert False, "signature is called on ShardChunkHeader, but the enum variant `%s` is unknown" % self.enum + + +class ShardChunkHeaderV1: + + @staticmethod + def chunk_hash(inner): + import hashlib + from messages.crypto import crypto_schema + from serializer import BinarySerializer + inner_serialized = BinarySerializer( + dict(block_schema + crypto_schema)).serialize(inner) + return hashlib.sha256(inner_serialized).digest() + + +class ShardChunkHeaderV2: + + @staticmethod + def chunk_hash(inner): + import hashlib + from messages.crypto import crypto_schema + from serializer import BinarySerializer + inner_serialized = BinarySerializer( + dict(block_schema + crypto_schema)).serialize(inner) + inner_hash = hashlib.sha256(inner_serialized).digest() + + return hashlib.sha256(inner_hash + inner.encoded_merkle_root).digest() + + +class ShardChunkHeaderV3: + + @staticmethod + def chunk_hash(inner): + import hashlib + from messages.crypto import crypto_schema + from serializer import BinarySerializer + inner_serialized = BinarySerializer( + dict(block_schema + crypto_schema)).serialize(inner) + inner_hash = hashlib.sha256(inner_serialized).digest() + + return hashlib.sha256(inner_hash + + inner.V2.encoded_merkle_root).digest() + + +class ShardChunkHeaderInner: + pass + + +class ShardChunkHeaderInnerV1: + pass + + +class ShardChunkHeaderInnerV2: + pass + + +class PartialEncodedChunkPart: + pass + + +class ReceiptProof: + pass + + +class PartialEncodedChunk: + + def inner_header(self): + version = self.enum + if version == 'V1': + return self.V1.header.inner + elif version == 'V2': + header = self.V2.header + header_version = header.enum + if header_version == 'V1': + return header.V1.inner + elif header_version == 'V2': + return header.V2.inner + elif header_version == 'V3': + return header.V3.inner + assert False, "unknown header version" + + def header_version(self): + version = self.enum + if version == 'V1': + return version + elif version == 'V2': + return self.V2.header.enum + assert False, "unknown partial encoded chunk version" + + +class PartialEncodedChunkV1: + pass + + +class PartialEncodedChunkV2: + pass + + +class PartialEncodedChunkRequestMsg: + pass + + +class PartialEncodedChunkResponseMsg: + pass + + +class PartialEncodedChunkForwardMsg: + pass + + +class ValidatorStake: + pass + + +class ValidatorStakeV1: + pass + + +class ValidatorStakeV2: + pass + + +class Approval: + pass + + +class ApprovalInner: + pass + + +block_schema = [ + [ + Block, { + 'kind': + 'enum', + 'field': + 'enum', + 'values': [ + ['BlockV1', BlockV1], + ['BlockV2', BlockV2], + ['BlockV3', BlockV3], + ] + } + ], + [ + BlockV1, + { + 'kind': + 'struct', + 'fields': [ + ['header', BlockHeader], + ['chunks', [ShardChunkHeaderV1]], + ['challenges', [()]], # TODO + ['vrf_value', [32]], + ['vrf_proof', [64]], + ] + } + ], + [ + BlockV2, + { + 'kind': + 'struct', + 'fields': [ + ['header', BlockHeader], + ['chunks', [ShardChunkHeader]], + ['challenges', [()]], # TODO + ['vrf_value', [32]], + ['vrf_proof', [64]], + ] + } + ], + [ + BlockV3, { + 'kind': 'struct', + 'fields': [ + ['header', BlockHeader], + ['body', BlockBody], + ] + } + ], + [ + BlockBody, + { + 'kind': + 'struct', + 'fields': [ + ['chunks', [ShardChunkHeader]], + ['challenges', [()]], # TODO + ['vrf_value', [32]], + ['vrf_proof', [64]], + ] + } + ], + [ + BlockHeader, { + 'kind': + 'enum', + 'field': + 'enum', + 'values': [['BlockHeaderV1', BlockHeaderV1], + ['BlockHeaderV2', BlockHeaderV2], + ['BlockHeaderV3', BlockHeaderV3], + ['BlockHeaderV4', BlockHeaderV4]] + } + ], + [ + BlockHeaderV1, { + 'kind': + 'struct', + 'fields': [ + ['prev_hash', [32]], + ['inner_lite', BlockHeaderInnerLite], + ['inner_rest', BlockHeaderInnerRest], + ['signature', Signature], + ] + } + ], + [ + BlockHeaderV2, { + 'kind': + 'struct', + 'fields': [ + ['prev_hash', [32]], + ['inner_lite', BlockHeaderInnerLite], + ['inner_rest', BlockHeaderInnerRestV2], + ['signature', Signature], + ] + } + ], + [ + BlockHeaderV3, { + 'kind': + 'struct', + 'fields': [ + ['prev_hash', [32]], + ['inner_lite', BlockHeaderInnerLite], + ['inner_rest', BlockHeaderInnerRestV3], + ['signature', Signature], + ] + } + ], + [ + BlockHeaderV4, { + 'kind': + 'struct', + 'fields': [ + ['prev_hash', [32]], + ['inner_lite', BlockHeaderInnerLite], + ['inner_rest', BlockHeaderInnerRestV4], + ['signature', Signature], + ] + } + ], + [ + BlockHeaderInnerLite, { + 'kind': + 'struct', + 'fields': [ + ['height', 'u64'], + ['epoch_id', [32]], + ['next_epoch_id', [32]], + ['prev_state_root', [32]], + ['outcome_root', [32]], + ['timestamp', 'u64'], + ['next_bp_hash', [32]], + ['block_merkle_root', [32]], + ] + } + ], + [ + BlockHeaderInnerRest, + { + 'kind': + 'struct', + 'fields': [ + ['chunk_receipts_root', [32]], + ['chunk_headers_root', [32]], + ['chunk_tx_root', [32]], + ['chunks_included', 'u64'], + ['challenges_root', [32]], + ['random_value', [32]], + ['validator_proposals', [ValidatorStakeV1]], + ['chunk_mask', ['u8']], + ['gas_price', 'u128'], + ['total_supply', 'u128'], + ['challenges_result', [()]], # TODO + ['last_final_block', [32]], + ['last_ds_final_block', [32]], + ['approvals', [{ + 'kind': 'option', + 'type': Signature + }]], + ['latest_protocol_version', 'u32'], + ] + } + ], + [ + BlockHeaderInnerRestV2, + { + 'kind': + 'struct', + 'fields': [ + ['chunk_receipts_root', [32]], + ['chunk_headers_root', [32]], + ['chunk_tx_root', [32]], + ['challenges_root', [32]], + ['random_value', [32]], + ['validator_proposals', [ValidatorStakeV1]], + ['chunk_mask', ['u8']], + ['gas_price', 'u128'], + ['total_supply', 'u128'], + ['challenges_result', [()]], # TODO + ['last_final_block', [32]], + ['last_ds_final_block', [32]], + ['approvals', [{ + 'kind': 'option', + 'type': Signature + }]], + ['latest_protocol_version', 'u32'], + ] + } + ], + [ + BlockHeaderInnerRestV3, + { + 'kind': + 'struct', + 'fields': [ + ['chunk_receipts_root', [32]], + ['chunk_headers_root', [32]], + ['chunk_tx_root', [32]], + ['challenges_root', [32]], + ['random_value', [32]], + ['validator_proposals', [ValidatorStake]], + ['chunk_mask', ['u8']], + ['gas_price', 'u128'], + ['total_supply', 'u128'], + ['challenges_result', [()]], # TODO + ['last_final_block', [32]], + ['last_ds_final_block', [32]], + ['block_ordinal', 'u64'], + ['prev_height', 'u64'], + ['epoch_sync_data_hash', { + 'kind': 'option', + 'type': [32] + }], + ['approvals', [{ + 'kind': 'option', + 'type': Signature + }]], + ['latest_protocol_version', 'u32'], + ] + } + ], + [ + BlockHeaderInnerRestV4, + { + 'kind': + 'struct', + 'fields': [ + ['block_body_hash', [32]], + ['chunk_receipts_root', [32]], + ['chunk_headers_root', [32]], + ['chunk_tx_root', [32]], + ['challenges_root', [32]], + ['random_value', [32]], + ['validator_proposals', [ValidatorStake]], + ['chunk_mask', ['u8']], + ['gas_price', 'u128'], + ['total_supply', 'u128'], + ['challenges_result', [()]], # TODO + ['last_final_block', [32]], + ['last_ds_final_block', [32]], + ['block_ordinal', 'u64'], + ['prev_height', 'u64'], + ['epoch_sync_data_hash', { + 'kind': 'option', + 'type': [32] + }], + ['approvals', [{ + 'kind': 'option', + 'type': Signature + }]], + ['latest_protocol_version', 'u32'], + ] + } + ], + [ + ShardChunkHeader, { + 'kind': + 'enum', + 'field': + 'enum', + 'values': [['V1', ShardChunkHeaderV1], ['V2', ShardChunkHeaderV2], + ['V3', ShardChunkHeaderV3]] + } + ], + [ + ShardChunkHeaderV1, { + 'kind': + 'struct', + 'fields': [ + ['inner', ShardChunkHeaderInnerV1], + ['height_included', 'u64'], + ['signature', Signature], + ] + } + ], + [ + ShardChunkHeaderV2, { + 'kind': + 'struct', + 'fields': [ + ['inner', ShardChunkHeaderInnerV1], + ['height_included', 'u64'], + ['signature', Signature], + ] + } + ], + [ + ShardChunkHeaderV3, { + 'kind': + 'struct', + 'fields': [ + ['inner', ShardChunkHeaderInner], + ['height_included', 'u64'], + ['signature', Signature], + ] + } + ], + [ + ShardChunkHeaderInner, { + 'kind': + 'enum', + 'field': + 'enum', + 'values': [['V1', ShardChunkHeaderInnerV1], + ['V2', ShardChunkHeaderInnerV2]] + } + ], + [ + ShardChunkHeaderInnerV1, { + 'kind': + 'struct', + 'fields': [ + ['prev_block_hash', [32]], + ['prev_state_root', [32]], + ['outcome_root', [32]], + ['encoded_merkle_root', [32]], + ['encoded_length', 'u64'], + ['height_created', 'u64'], + ['shard_id', 'u64'], + ['gas_used', 'u64'], + ['gas_limit', 'u64'], + ['balance_burnt', 'u128'], + ['outgoing_receipt_root', [32]], + ['tx_root', [32]], + ['validator_proposals', [ValidatorStakeV1]], + ] + } + ], + [ + ShardChunkHeaderInnerV2, { + 'kind': + 'struct', + 'fields': [ + ['prev_block_hash', [32]], + ['prev_state_root', [32]], + ['outcome_root', [32]], + ['encoded_merkle_root', [32]], + ['encoded_length', 'u64'], + ['height_created', 'u64'], + ['shard_id', 'u64'], + ['gas_used', 'u64'], + ['gas_limit', 'u64'], + ['balance_burnt', 'u128'], + ['outgoing_receipt_root', [32]], + ['tx_root', [32]], + ['validator_proposals', [ValidatorStake]], + ] + } + ], + [ + ShardChunk, { + 'kind': 'enum', + 'field': 'enum', + 'values': [['V1', ShardChunkV1], ['V2', ShardChunkV2]] + } + ], + [ + ShardChunkV1, { + 'kind': + 'struct', + 'fields': [ + ['chunk_hash', [32]], + ['header', ShardChunkHeaderV1], + ['transactions', [SignedTransaction]], + ['receipts', [Receipt]], + ] + } + ], + [ + ShardChunkV2, { + 'kind': + 'struct', + 'fields': [ + ['chunk_hash', [32]], + ['header', ShardChunkHeader], + ['transactions', [SignedTransaction]], + ['receipts', [Receipt]], + ] + } + ], + [ + PartialEncodedChunkPart, { + 'kind': + 'struct', + 'fields': [ + ['part_ord', 'u64'], + ['part', ['u8']], + ['merkle_proof', MerklePath], + ] + } + ], + [ + ReceiptProof, { + 'kind': 'struct', + 'fields': [ + ['f1', [Receipt]], + ['f2', ShardProof], + ] + } + ], + [ + PartialEncodedChunk, { + 'kind': + 'enum', + 'field': + 'enum', + 'values': [['V1', PartialEncodedChunkV1], + ['V2', PartialEncodedChunkV2]] + } + ], + [ + PartialEncodedChunkV1, { + 'kind': + 'struct', + 'fields': [['header', ShardChunkHeaderV1], + ['parts', [PartialEncodedChunkPart]], + ['receipts', [ReceiptProof]]] + } + ], + [ + PartialEncodedChunkV2, { + 'kind': + 'struct', + 'fields': [['header', ShardChunkHeader], + ['parts', [PartialEncodedChunkPart]], + ['receipts', [ReceiptProof]]] + } + ], + [ + PartialEncodedChunkRequestMsg, { + 'kind': + 'struct', + 'fields': [['chunk_hash', [32]], ['part_ords', ['u64']], + ['tracking_shards', ['u64']]] + } + ], + [ + PartialEncodedChunkResponseMsg, { + 'kind': + 'struct', + 'fields': [['chunk_hash', [32]], + ['parts', [PartialEncodedChunkPart]], + ['receipts', [ReceiptProof]]] + } + ], + [ + PartialEncodedChunkForwardMsg, { + 'kind': + 'struct', + 'fields': [['chunk_hash', [32]], ['inner_header_hash', [32]], + ['merkle_root', [32]], ['signature', Signature], + ['prev_block_hash', [32]], ['height_created', 'u64'], + ['shard_id', 'u64'], + ['parts', [PartialEncodedChunkPart]]] + } + ], + [ + ValidatorStake, { + 'kind': 'enum', + 'field': 'enum', + 'values': [['V1', ValidatorStakeV1], ['V2', ValidatorStakeV2]] + } + ], + [ + ValidatorStakeV1, { + 'kind': + 'struct', + 'fields': [ + ['account_id', 'string'], + ['public_key', PublicKey], + ['stake', 'u128'], + ] + } + ], + [ + Approval, { + 'kind': + 'struct', + 'fields': [ + ['inner', ApprovalInner], + ['target_height', 'u64'], + ['signature', Signature], + ['account_id', 'string'], + ] + } + ], + [ + ApprovalInner, { + 'kind': 'enum', + 'field': 'enum', + 'values': [ + ['Endorsement', [32]], + ['Skip', 'u64'], + ] + } + ], +] diff --git a/pytest/lib/messages/bridge.py b/pytest/lib/messages/bridge.py new file mode 100644 index 000000000..5d8593a48 --- /dev/null +++ b/pytest/lib/messages/bridge.py @@ -0,0 +1,18 @@ +class Proof: + pass + + +bridge_schema = [[ + Proof, { + 'kind': + 'struct', + 'fields': [ + ['log_index', 'u64'], + ['log_entry_data', ['u8']], + ['receipt_index', 'u64'], + ['receipt_data', ['u8']], + ['header_data', ['u8']], + ['proof', [['u8']]], + ] + } +]] diff --git a/pytest/lib/messages/crypto.py b/pytest/lib/messages/crypto.py new file mode 100644 index 000000000..65115cdd3 --- /dev/null +++ b/pytest/lib/messages/crypto.py @@ -0,0 +1,122 @@ +import typing + +import base58 + + +class Signature: + _KEY_TYPES = { + 'ed25519': 0, + 'secp256k1': 1, + } + + def __init__(self, signature: typing.Optional[str] = None) -> None: + if signature: + keyType, data = signature.split(':') + self.keyType = self._KEY_TYPES[keyType] + self.data = base58.b58decode(data) + + +class PublicKey: + pass + + +class AccessKey: + pass + + +class AccessKeyPermission: + pass + + +class FunctionCallPermission: + pass + + +class FullAccessPermission: + pass + + +class Direction: + pass + + +class MerklePath: + pass + + +class ShardProof: + pass + + +crypto_schema = [ + [ + Signature, { + 'kind': 'struct', + 'fields': [['keyType', 'u8'], ['data', [64]]] + } + ], + [ + PublicKey, { + 'kind': 'struct', + 'fields': [['keyType', 'u8'], ['data', [32]]] + } + ], + [ + AccessKey, { + 'kind': 'struct', + 'fields': [ + ['nonce', 'u64'], + ['permission', AccessKeyPermission], + ] + } + ], + [ + AccessKeyPermission, { + 'kind': + 'enum', + 'field': + 'enum', + 'values': [ + ['functionCall', FunctionCallPermission], + ['fullAccess', FullAccessPermission], + ] + } + ], + [ + FunctionCallPermission, { + 'kind': + 'struct', + 'fields': [ + ['allowance', { + 'kind': 'option', + 'type': 'u128' + }], + ['receiverId', 'string'], + ['methodNames', ['string']], + ] + } + ], + [FullAccessPermission, { + 'kind': 'struct', + 'fields': [] + }], + [ + Direction, { + 'kind': 'enum', + 'field': 'enum', + 'values': [['Left', ()], ['Right', ()]], + } + ], + [MerklePath, { + 'kind': 'struct', + 'fields': [['f1', [([32], Direction)]]], + }], + [ + ShardProof, { + 'kind': + 'struct', + 'fields': [['from_shard_id', 'u64'], ['to_shard_id', 'u64'], + ['proof', MerklePath]], + } + ], +] diff --git a/pytest/lib/messages/network.py b/pytest/lib/messages/network.py new file mode 100644 index 000000000..72cf45105 --- /dev/null +++ b/pytest/lib/messages/network.py @@ -0,0 +1,552 @@ +from messages.crypto import Signature, PublicKey, MerklePath, ShardProof +from messages.tx import SignedTransaction, Receipt +from messages.block import Block, Approval, PartialEncodedChunk, PartialEncodedChunkV1, PartialEncodedChunkRequestMsg, PartialEncodedChunkResponseMsg, PartialEncodedChunkForwardMsg, BlockHeader, ShardChunk, ShardChunkHeader, ShardChunkHeaderV1 +from messages.shard import StateRootNode + + +class SocketAddr: + pass + + +class PeerMessage: + pass + + +class Handshake: + pass + + +class HandshakeV2: + pass + + +class HandshakeFailureReason: + pass + + +class ProtocolVersionMismatch: + pass + + +class PeerInfo: + pass + + +class PeerChainInfo: + pass + + +class PeerChainInfoV2: + pass + + +class EdgeInfo: + pass + + +class GenesisId: + pass + + +class Edge: + pass + + +class SyncData: + pass + + +class AnnounceAccount: + pass + + +class RoutedMessage: + pass + + +class PeerIdOrHash: + pass + + +class RoutedMessageBody: + pass + + +class PingPong: + pass + + +class StateResponseInfo: + pass + + +class StateResponseInfoV1: + pass + + +class StateResponseInfoV2: + pass + + +class ShardStateSyncResponse: + pass + + +class ShardStateSyncResponseV1: + pass + + +class ShardStateSyncResponseV2: + pass + + +class ShardStateSyncResponseHeader: + pass + + +class ShardStateSyncResponseHeaderV1: + pass + + +class ShardStateSyncResponseHeaderV2: + pass + + +class RoutingTableSyncV2: + pass + + +class IbfElem: + pass + + +class RoutingVersion2: + pass + + +class RoutingState: + pass + + +class PartialSync: + pass + + +network_schema = [ + [ + SocketAddr, { + 'kind': 'enum', + 'field': 'enum', + 'values': [['V4', ([4], 'u16')], ['V6', ([16], 'u16')]] + } + ], + [ + PeerMessage, + { + 'kind': + 'enum', + 'field': + 'enum', + 'values': [ + ['Handshake', Handshake], + ['HandshakeFailure', (PeerInfo, HandshakeFailureReason)], + ['LastEdge', Edge], + ['Sync', SyncData], + ['RequestUpdateNonce', EdgeInfo], + ['ResponseUpdateNonce', Edge], + ['PeersRequest', ()], + ['PeersResponse', [PeerInfo]], + ['BlockHeadersRequest', [[32]]], + ['BlockHeaders', [BlockHeader]], + ['BlockRequest', [32]], + ['Block', Block], + ['Transaction', SignedTransaction], + ['Routed', RoutedMessage], + ['Disconnect'], + ['Challenge', None], # TODO + ['HandshakeV2', HandshakeV2], + ['EpochSyncRequest', None], # TODO + ['EpochSyncResponse', None], # TODO + ['EpochSyncFinalizationRequest', None], # TODO + ['EpochSyncFinalizationResponse', None], # TODO + ['RoutingTableSyncV2', RoutingTableSyncV2], + ] + } + ], + [ + Handshake, { + 'kind': + 'struct', + 'fields': [ + ['version', 'u32'], + ['oldest_supported_version', 'u32'], + ['peer_id', PublicKey], + ['target_peer_id', PublicKey], + ['listen_port', { + 'kind': 'option', + 'type': 'u16' + }], + ['chain_info', PeerChainInfoV2], + ['edge_info', EdgeInfo], + ] + } + ], + [ + HandshakeV2, { + 'kind': + 'struct', + 'fields': [ + ['version', 'u32'], + ['oldest_supported_version', 'u32'], + ['peer_id', PublicKey], + ['target_peer_id', PublicKey], + ['listen_port', { + 'kind': 'option', + 'type': 'u16' + }], + ['chain_info', PeerChainInfo], + ['edge_info', EdgeInfo], + ] + } + ], + [ + HandshakeFailureReason, { + 'kind': + 'enum', + 'field': + 'enum', + 'values': [ + ['ProtocolVersionMismatch', ProtocolVersionMismatch], + ['GenesisMismatch', GenesisId], + ['InvalidTarget', ()], + ] + } + ], + [ + ProtocolVersionMismatch, { + 'kind': 'struct', + 'fields': [ + ['version', 'u32'], + ['oldest_supported_version', 'u32'], + ] + } + ], + [ + PeerInfo, { + 'kind': + 'struct', + 'fields': [['id', PublicKey], + ['addr', { + 'kind': 'option', + 'type': SocketAddr + }], ['account_id', { + 'kind': 'option', + 'type': 'string' + }]] + } + ], + [ + PeerChainInfo, { + 'kind': + 'struct', + 'fields': [['genesis_id', GenesisId], ['height', 'u64'], + ['tracked_shards', ['u64']]] + } + ], + [ + PeerChainInfoV2, { + 'kind': + 'struct', + 'fields': [['genesis_id', GenesisId], ['height', 'u64'], + ['tracked_shards', ['u64']], ['archival', 'bool']] + } + ], + [ + Edge, { + 'kind': + 'struct', + 'fields': [ + ['peer0', PublicKey], + ['peer1', PublicKey], + ['nonce', 'u64'], + ['signature0', Signature], + ['signature1', Signature], + ['removal_info', { + 'kind': 'option', + 'type': ('u8', Signature) + }], + ] + } + ], + [ + SyncData, { + 'kind': 'struct', + 'fields': [ + ['edges', [Edge]], + ['accounts', [AnnounceAccount]], + ] + } + ], + [ + EdgeInfo, { + 'kind': 'struct', + 'fields': [ + ['nonce', 'u64'], + ['signature', Signature], + ] + } + ], + [ + GenesisId, { + 'kind': 'struct', + 'fields': [ + ['chain_id', 'string'], + ['hash', [32]], + ] + } + ], + [ + AnnounceAccount, { + 'kind': + 'struct', + 'fields': [ + ['account_id', 'string'], + ['peer_id', PublicKey], + ['epoch_id', [32]], + ['signature', Signature], + ] + } + ], + [ + RoutedMessage, { + 'kind': + 'struct', + 'fields': [ + ['target', PeerIdOrHash], + ['author', PublicKey], + ['signature', Signature], + ['ttl', 'u8'], + ['body', RoutedMessageBody], + ] + } + ], + [ + PeerIdOrHash, { + 'kind': 'enum', + 'field': 'enum', + 'values': [ + ['PeerId', PublicKey], + ['Hash', [32]], + ] + } + ], + [ + RoutedMessageBody, + { + 'kind': + 'enum', + 'field': + 'enum', + 'values': [ + ['BlockApproval', Approval], + ['ForwardTx', SignedTransaction], + ['TxStatusRequest', ('string', [32])], + ['TxStatusResponse', None], # TODO + ['QueryRequest', None], # TODO + ['QueryResponse', None], # TODO + ['ReceiptOutcomeRequest', [32]], + ['ReceiptOutcomeResponse', None], # TODO + ['StateRequestHeader', ('u64', [32])], + ['StateRequestPart', ('u64', [32], 'u64')], + ['StateResponseInfo', StateResponseInfoV1], + ['PartialEncodedChunkRequest', PartialEncodedChunkRequestMsg], + ['PartialEncodedChunkResponse', PartialEncodedChunkResponseMsg], + ['PartialEncodedChunk', PartialEncodedChunkV1], + ['Ping', PingPong], + ['Pong', PingPong], + ['VersionedPartialEncodedChunk', PartialEncodedChunk], + ['VersionedStateResponse', StateResponseInfo], + ['PartialEncodedChunkForward', PartialEncodedChunkForwardMsg] + ] + } + ], + [ + PingPong, { + 'kind': 'struct', + 'fields': [['nonce', 'u64'], ['source', PublicKey]] + } + ], + [ + StateResponseInfo, { + 'kind': 'enum', + 'field': 'enum', + 'values': [['V1', StateResponseInfoV1], ['V2', StateResponseInfoV2]] + } + ], + [ + StateResponseInfoV1, { + 'kind': + 'struct', + 'fields': [['shard_id', 'u64'], ['sync_hash', [32]], + ['state_response', ShardStateSyncResponseV1]] + } + ], + [ + StateResponseInfoV2, { + 'kind': + 'struct', + 'fields': [['shard_id', 'u64'], ['sync_hash', [32]], + ['state_response', ShardStateSyncResponse]] + } + ], + [ + ShardStateSyncResponse, { + 'kind': + 'enum', + 'field': + 'enum', + 'values': [['V1', ShardStateSyncResponseV1], + ['V2', ShardStateSyncResponseV2]] + } + ], + [ + ShardStateSyncResponseV1, { + 'kind': + 'struct', + 'fields': [[ + 'header', { + 'kind': 'option', + 'type': ShardStateSyncResponseHeaderV1 + } + ], ['part', { + 'kind': 'option', + 'type': ('u64', ['u8']) + }]] + } + ], + [ + ShardStateSyncResponseV2, { + 'kind': + 'struct', + 'fields': [[ + 'header', { + 'kind': 'option', + 'type': ShardStateSyncResponseHeaderV2 + } + ], ['part', { + 'kind': 'option', + 'type': ('u64', ['u8']) + }]] + } + ], + [ + ShardStateSyncResponseHeader, { + 'kind': + 'enum', + 'field': + 'enum', + 'values': [['V1', ShardStateSyncResponseHeaderV1], + ['V2', ShardStateSyncResponseHeaderV2]] + } + ], + [ + ShardStateSyncResponseHeaderV1, { + 'kind': + 'struct', + 'fields': [['chunk', ShardChunk], ['chunk_proof', MerklePath], + [ + 'prev_chunk_header', { + 'kind': 'option', + 'type': ShardChunkHeaderV1 + } + ], + [ + 'prev_chunk_proof', { + 'kind': 'option', + 'type': MerklePath + } + ], + [ + 'incoming_receipts_proofs', + [([32], [([Receipt], ShardProof)])] + ], ['root_proofs', [[([32], MerklePath)]]], + ['state_root_node', StateRootNode]] + } + ], + [ + ShardStateSyncResponseHeaderV2, { + 'kind': + 'struct', + 'fields': [['chunk', ShardChunk], ['chunk_proof', MerklePath], + [ + 'prev_chunk_header', { + 'kind': 'option', + 'type': ShardChunkHeader + } + ], + [ + 'prev_chunk_proof', { + 'kind': 'option', + 'type': MerklePath + } + ], + [ + 'incoming_receipts_proofs', + [([32], [([Receipt], ShardProof)])] + ], ['root_proofs', [[([32], MerklePath)]]], + ['state_root_node', StateRootNode]] + } + ], + [ + RoutingTableSyncV2, { + 'kind': 'enum', + 'field': 'enum', + 'values': [['Version2', RoutingVersion2]] + } + ], + [ + IbfElem, { + 'kind': 'struct', + 'fields': [ + ['xor_elem', 'u64'], + ['xor_hash', 'u64'], + ] + } + ], + [ + RoutingVersion2, { + 'kind': + 'struct', + 'fields': [ + ['known_edges', 'u64'], + ['seed', 'u64'], + ['edges', [Edge]], + ['routing_state', RoutingState], + ] + } + ], + [ + RoutingState, { + 'kind': + 'enum', + 'field': + 'enum', + 'values': [ + ['PartialSync', PartialSync], + ['RequestAllEdges', ()], + ['Done', ()], + ['RequestMissingEdges', ['u64']], + ['InitializeIbf', ()], + ] + } + ], + [ + PartialSync, { + 'kind': 'struct', + 'fields': [ + ['ibf_level', 'u64'], + ['ibf', [IbfElem]], + ] + } + ] +] diff --git a/pytest/lib/messages/shard.py b/pytest/lib/messages/shard.py new file mode 100644 index 000000000..b291fc133 --- /dev/null +++ b/pytest/lib/messages/shard.py @@ -0,0 +1,10 @@ +class StateRootNode: + pass + + +shard_schema = [[ + StateRootNode, { + 'kind': 'struct', + 'fields': [['data', ['u8']], ['memory_usage', 'u64']] + } +]] diff --git a/pytest/lib/messages/tx.py b/pytest/lib/messages/tx.py new file mode 100644 index 000000000..214e4fede --- /dev/null +++ b/pytest/lib/messages/tx.py @@ -0,0 +1,228 @@ +from messages.crypto import Signature, PublicKey, AccessKey + + +class SignedTransaction: + pass + + +class Transaction: + pass + + +class Action: + pass + + +class CreateAccount: + pass + + +class DeployContract: + pass + + +class FunctionCall: + pass + + +class Transfer: + pass + + +class Stake: + pass + + +class AddKey: + pass + + +class DeleteKey: + pass + + +class DeleteAccount: + pass + + +class SignedDelegate: + pass + + +class DelegateAction: + pass + + +class Receipt: + pass + + +class ReceiptEnum: + pass + + +class ActionReceipt: + pass + + +class DataReceipt: + pass + + +class DataReceiver: + pass + + +tx_schema = [ + [ + SignedTransaction, { + 'kind': 'struct', + 'fields': [['transaction', Transaction], ['signature', Signature]] + } + ], + [ + Transaction, { + 'kind': + 'struct', + 'fields': [['signerId', 'string'], ['publicKey', PublicKey], + ['nonce', 'u64'], ['receiverId', 'string'], + ['blockHash', [32]], ['actions', [Action]]] + } + ], + [ + Action, { + 'kind': + 'enum', + 'field': + 'enum', + 'values': [ + ['createAccount', CreateAccount], + ['deployContract', DeployContract], + ['functionCall', FunctionCall], + ['transfer', Transfer], + ['stake', Stake], + ['addKey', AddKey], + ['deleteKey', DeleteKey], + ['deleteAccount', DeleteAccount], + ['delegate', SignedDelegate], + ] + } + ], + [CreateAccount, { + 'kind': 'struct', + 'fields': [] + }], + [DeployContract, { + 'kind': 'struct', + 'fields': [['code', ['u8']]] + }], + [ + FunctionCall, { + 'kind': + 'struct', + 'fields': [['methodName', 'string'], ['args', ['u8']], + ['gas', 'u64'], ['deposit', 'u128']] + } + ], + [ + SignedDelegate, { + 'kind': + 'struct', + 'fields': [['delegateAction', DelegateAction], + ['signature', Signature]] + } + ], + [ + DelegateAction, { + 'kind': + 'struct', + 'fields': [['senderId', 'string'], ['receiverId', 'string'], + ['actions', [Action]], ['nonce', 'u64'], + ['maxBlockHeight', 'u64'], ['publicKey', PublicKey]] + } + ], + [Transfer, { + 'kind': 'struct', + 'fields': [['deposit', 'u128']] + }], + [ + Stake, { + 'kind': 'struct', + 'fields': [['stake', 'u128'], ['publicKey', PublicKey]] + } + ], + [ + AddKey, { + 'kind': 'struct', + 'fields': [['publicKey', PublicKey], ['accessKey', AccessKey]] + } + ], + [DeleteKey, { + 'kind': 'struct', + 'fields': [['publicKey', PublicKey]] + }], + [ + DeleteAccount, { + 'kind': 'struct', + 'fields': [['beneficiaryId', 'string']] + } + ], + [ + Receipt, { + 'kind': + 'struct', + 'fields': [ + ['predecessor_id', 'string'], + ['receiver_id', 'string'], + ['receipt_id', [32]], + ['receipt', ReceiptEnum], + ] + } + ], + [ + ReceiptEnum, { + 'kind': 'enum', + 'field': 'enum', + 'values': [ + ['Action', ActionReceipt], + ['Data', DataReceipt], + ] + } + ], + [ + ActionReceipt, { + 'kind': + 'struct', + 'fields': [ + ['signer_id', 'string'], + ['signer_public_key', PublicKey], + ['gas_price', 'u128'], + ['output_data_receivers', [DataReceiver]], + ['input_data_ids', [[32]]], + ['actions', [Action]], + ], + } + ], + [ + DataReceipt, { + 'kind': + 'struct', + 'fields': [ + ['data_id', [32]], + ['data', { + 'kind': 'option', + 'type': ['u8'] + }], + ] + } + ], + [ + DataReceiver, { + 'kind': 'struct', + 'fields': [ + ['data_id', [32]], + ['receiver_id', 'string'], + ] + } + ], +] diff --git a/pytest/lib/metrics.py b/pytest/lib/metrics.py new file mode 100644 index 000000000..0744736d9 --- /dev/null +++ b/pytest/lib/metrics.py @@ -0,0 +1,78 @@ +from prometheus_client import parser +import requests +import time + +BLOCK_TIME_BINS = [ + '0.005', '0.01', '0.025', '0.05', '0.1', '0.25', '0.5', '1', '2.5', '5', + '10', '+Inf' +] + + +def fold(collection, key, f, default): + if key in collection: + return f(collection[key]) + else: + return default + + +class Metrics: + + def __init__(self, total_blocks, memory_usage, total_transactions, + block_processing_time, timestamp, blocks_per_second): + self.total_blocks = total_blocks + self.memory_usage = memory_usage + self.total_transactions = total_transactions + self.block_processing_time = block_processing_time + self.timestamp = timestamp + self.blocks_per_second = blocks_per_second + + @classmethod + def from_url(cls, metrics_url): + response = requests.get(metrics_url, timeout=10) + timestamp = time.time() + response.raise_for_status() + prometheus_string = response.content.decode('utf8') + prometheus_metrics = dict( + map(lambda m: (m.name, m), + parser.text_string_to_metric_families(prometheus_string))) + + fold_sample = lambda key: fold(prometheus_metrics, key, lambda m: int( + m.samples[0].value), 0) + + total_blocks = fold_sample('unc_block_processed') + memory_usage = fold_sample('unc_memory_usage_bytes') + total_transactions = fold_sample('unc_transaction_processed') + blocks_per_second = fold_sample('unc_blocks_per_minute') / 60.0 + + def extract_block_processing_time(m): + block_processing_time_samples = m.samples + block_processing_time = {} + for sample in block_processing_time_samples: + if 'le' in sample.labels: + bound = sample.labels['le'] + block_processing_time[f'le {bound}'] = int(sample.value) + return block_processing_time + + block_processing_time = fold( + prometheus_metrics, 'unc_block_processing_time', + extract_block_processing_time, + dict(map(lambda bin: ('le ' + bin, 0), BLOCK_TIME_BINS))) + + return cls(total_blocks, memory_usage, total_transactions, + block_processing_time, timestamp, blocks_per_second) + + @classmethod + def diff(cls, final_metrics, initial_metrics): + total_blocks = final_metrics.total_blocks - initial_metrics.total_blocks + memory_usage = final_metrics.memory_usage - initial_metrics.memory_usage + total_transactions = final_metrics.total_transactions - initial_metrics.total_transactions + timestamp = final_metrics.timestamp - initial_metrics.timestamp + blocks_per_second = (final_metrics.blocks_per_second + + initial_metrics.blocks_per_second) / 2.0 + block_processing_time = {} + for sample in final_metrics.block_processing_time.keys(): + block_processing_time[sample] = final_metrics.block_processing_time[ + sample] - initial_metrics.block_processing_time[sample] + + return cls(total_blocks, memory_usage, total_transactions, + block_processing_time, timestamp, blocks_per_second) diff --git a/pytest/lib/mocknet.py b/pytest/lib/mocknet.py new file mode 100644 index 000000000..211981653 --- /dev/null +++ b/pytest/lib/mocknet.py @@ -0,0 +1,1473 @@ +import json +import os +import random +import os.path +import shlex +import subprocess +import tempfile +import time +import functools + +import base58 +import requests +from rc import run, pmap, gcloud + +import data +from cluster import GCloudNode +from configured_logger import logger +from key import Key +from metrics import Metrics +from transaction import sign_payment_tx_and_get_hash, sign_staking_tx_and_get_hash + +DEFAULT_KEY_TARGET = '/tmp/mocknet' +KEY_TARGET_ENV_VAR = 'unc_PYTEST_KEY_TARGET' +# NODE_SSH_KEY_PATH = '~/.ssh/unc_ops' +NODE_SSH_KEY_PATH = None +NODE_USERNAME = 'ubuntu' +NUM_ACCOUNTS = 26 * 2 +PROJECT = 'unc-mocknet' +PUBLIC_KEY = 'ed25519:76NVkDErhbP1LGrSAf5Db6BsFJ6LBw6YVA4BsfTBohmN' +SECRET_KEY = 'ed25519:3cCk8KUWBySGCxBcn1syMoY5u73wx5eaPLRbQcMi23LwBA3aLsqEbA33Ww1bsJaFrchmDciGe9otdn45SrDSkow2' +TX_OUT_FILE = '/home/ubuntu/tx_events' +WASM_FILENAME = 'simple_contract.wasm' + +TREASURY_ACCOUNT = 'test.near' +MASTER_ACCOUNT = 'near' +SKYWARD_ACCOUNT = 'skyward.near' +SKYWARD_TOKEN_ACCOUNT = 'token.skyward.near' +TOKEN1_ACCOUNT = 'token1.near' +TOKEN2_ACCOUNT = 'token2.near' +TOKEN2_OWNER_ACCOUNT = 'account.token2.near' +ACCOUNT1_ACCOUNT = 'account1.near' + +TMUX_STOP_SCRIPT = ''' +while tmux has-session -t near; do +tmux kill-session -t near || true +done +''' + +PYTHON_DIR = '/home/ubuntu/.near/pytest/' + +PYTHON_SETUP_SCRIPT = f''' +rm -rf {PYTHON_DIR} +mkdir -p {PYTHON_DIR} +python3 -m pip install pip --upgrade +python3 -m pip install virtualenv --upgrade +cd {PYTHON_DIR} +python3 -m virtualenv venv -p $(which python3) +''' + +INSTALL_PYTHON_REQUIREMENTS = f''' +cd {PYTHON_DIR} +./venv/bin/pip install -r requirements.txt +''' + +ONE_NEAR = 10**24 +MIN_STAKE = 64 * (10**3) +STAKE_STEP = 15 * (10**3) +OTHER_STAKE = 10**6 +MAINNET_STAKES = [ + 43566361, 20091202, 19783811, 18990335, 18196731, 12284685, 10770734, + 10769428, 9858038, 9704977, 8871933, 8296476, 7731153, 7499051, 7322703, + 7307458, 6477856, 6293083, 6242196, 6093107, 6085802, 5553788, 5508664, + 5286843, 5056137, 4944414, 4859235, 4732286, 4615542, 4565243, 4468179, + 4451510, 4444888, 4412264, 4221909, 4219451, 4210541, 4161553, 4116102, + 4085627, 4075090, 3988387, 3932601, 3923842, 3921959, 3915353, 3907857, + 3905980, 3898791, 3886957, 3851553, 3831536, 3790646, 3784485, 3777647, + 3760931, 3746129, 3741225, 3727313, 3699201, 3620341 +] + +ACCOUNTS = { + TREASURY_ACCOUNT: (10**7) * ONE_NEAR, + MASTER_ACCOUNT: (10**7) * ONE_NEAR, + SKYWARD_ACCOUNT: (10**6) * ONE_NEAR, + TOKEN1_ACCOUNT: (10**6) * ONE_NEAR, + TOKEN2_ACCOUNT: (10**6) * ONE_NEAR, + TOKEN2_OWNER_ACCOUNT: (10**6) * ONE_NEAR, + ACCOUNT1_ACCOUNT: (10**6) * ONE_NEAR, +} + + +def get_node(hostname): + instance_name = hostname + n = GCloudNode( + instance_name, + username=NODE_USERNAME, + project=PROJECT, + ssh_key_path=NODE_SSH_KEY_PATH, + ) + return n + + +def get_nodes(pattern=None): + machines = gcloud.list( + pattern=pattern, + project=PROJECT, + username=NODE_USERNAME, + ssh_key_path=NODE_SSH_KEY_PATH, + ) + nodes = pmap( + lambda machine: GCloudNode( + machine.name, + username=NODE_USERNAME, + project=PROJECT, + ssh_key_path=NODE_SSH_KEY_PATH, + ), + machines, + ) + return nodes + + +# Needs to be in-sync with init.sh.tmpl in terraform. +def node_account_name(node_name): + # Assuming node_name is a hostname and looks like + # 'mocknet-betanet-spoon-abcd' or 'mocknet-zxcv'. + parts = node_name.split('-') + return f'{parts[-1]}-load-test.near' + + +# Constructs an account name given the basic account name. +# Accounts are generated in this order: +# `a00.base`, `b00.base`, .., `z00.base`, `a01.base`, ... +def load_testing_account_id(node_account_id, i): + NUM_LETTERS = 26 + letter = i % NUM_LETTERS + num = i // NUM_LETTERS + return '%s%02d.%s' % (chr(ord('a') + letter), num, node_account_id) + + +def get_validator_account(node): + return Key.from_json( + download_and_read_json(node, '/home/ubuntu/.near/validator_key.json')) + + +def list_validators(node): + validators = node.get_validators()['result'] + validator_accounts = set( + map(lambda v: v['account_id'], validators['current_validators'])) + return validator_accounts + + +def setup_python_environment(node, wasm_contract): + m = node.machine + logger.info(f'Setting up python environment on {m.name}') + m.run('bash', input=PYTHON_SETUP_SCRIPT) + m.upload('lib', PYTHON_DIR, switch_user='ubuntu') + m.upload('requirements.txt', PYTHON_DIR, switch_user='ubuntu') + m.upload(wasm_contract, + os.path.join(PYTHON_DIR, WASM_FILENAME), + switch_user='ubuntu') + m.upload('tests/mocknet/helpers/*.py', PYTHON_DIR, switch_user='ubuntu') + m.run('bash', input=INSTALL_PYTHON_REQUIREMENTS) + logger.info(f'{m.name} python setup complete') + + +def setup_python_environments(nodes, wasm_contract): + pmap(lambda n: setup_python_environment(n, wasm_contract), nodes) + + +def start_load_test_helper_script( + script, + node_account_id, + rpc_nodes, + num_nodes, + max_tps, + test_timeout, + contract_deploy_time, +): + s = ''' + cd {dir} + nohup ./venv/bin/python {script} \\ + --node-account-id {node_account_id} \\ + --rpc-nodes {rpc_nodes} \\ + --num-nodes {num_nodes} \\ + --max-tps {max_tps} \\ + --test-timeout {test_timeout} \\ + --contract-deploy-time {contract_deploy_time} \\ + 1>load_test.out 2>load_test.err < /dev/null & + '''.format( + dir=shlex.quote(PYTHON_DIR), + script=shlex.quote(script), + node_account_id=shlex.quote(node_account_id), + rpc_nodes=shlex.quote(rpc_nodes), + num_nodes=shlex.quote(str(num_nodes)), + max_tps=shlex.quote(str(max_tps)), + test_timeout=shlex.quote(str(test_timeout)), + contract_deploy_time=shlex.quote(str(contract_deploy_time)), + ) + logger.info( + f'Starting load test helper. Node accound id: {node_account_id}.') + logger.debug(f'The load test helper script is:{s}') + return s + + +def start_load_test_helper( + script, + node, + rpc_nodes, + num_nodes, + max_tps, + test_timeout, + contract_deploy_time, +): + logger.info(f'Starting load_test_helper on {node.instance_name}') + rpc_node_ips = ','.join([rpc_node.ip for rpc_node in rpc_nodes]) + node.machine.run( + 'bash', + input=start_load_test_helper_script( + script, + node_account_name(node.instance_name), + rpc_node_ips, + num_nodes, + max_tps, + test_timeout, + contract_deploy_time, + ), + ) + + +def start_load_test_helpers( + script, + validator_nodes, + rpc_nodes, + max_tps, + test_timeout, + contract_deploy_time, +): + pmap( + lambda node: start_load_test_helper( + script, + node, + rpc_nodes, + len(validator_nodes), + max_tps, + test_timeout, + contract_deploy_time, + ), + validator_nodes, + ) + + +def get_log(node): + target_file = f'./logs/{node.instance_name}.log' + node.machine.download('/home/ubuntu/near.log', target_file) + + +def get_logs(nodes): + pmap(get_log, nodes) + + +def get_epoch_length_in_blocks(node): + config = download_and_read_json(node, '/home/ubuntu/.near/genesis.json') + epoch_length_in_blocks = config['epoch_length'] + return epoch_length_in_blocks + + +def get_metrics(node): + (addr, port) = node.rpc_addr() + metrics_url = f'http://{addr}:{port}/metrics' + return Metrics.from_url(metrics_url) + + +def get_timestamp(block): + return block['header']['timestamp'] / 1e9 + + +def get_chunk_txn(index, chunks, archival_node, result): + chunk = chunks[index] + chunk_hash = chunk['chunk_hash'] + result[index] = len( + archival_node.get_chunk(chunk_hash)['result']['transactions']) + + +# Measure bps and tps by directly checking block timestamps and number of transactions +# in each block. +def chain_measure_bps_and_tps( + archival_node, + start_time, + end_time, + duration=None, +): + latest_block_hash = archival_node.get_latest_block().hash + curr_block = archival_node.get_block(latest_block_hash)['result'] + curr_time = get_timestamp(curr_block) + + if end_time is None: + end_time = curr_time + if start_time is None: + start_time = end_time - duration + logger.info( + f'Measuring BPS and TPS in the time range {start_time} to {end_time}') + + # One entry per block, equal to the timestamp of that block. + block_times = [] + # One entry per block, containing the count of transactions in all chunks of the block. + tx_count = [] + while curr_time > start_time: + if curr_time < end_time: + block_times.append(curr_time) + gas_per_chunk = [] + for chunk in curr_block['chunks']: + gas_per_chunk.append(chunk['gas_used'] * 1e-12) + gas_block = sum(gas_per_chunk) + tx_per_chunk = [None] * len(curr_block['chunks']) + pmap( + lambda i: get_chunk_txn(i, curr_block['chunks'], archival_node, + tx_per_chunk), + range(len(curr_block['chunks']))) + txs = sum(tx_per_chunk) + tx_count.append(txs) + logger.info( + f'Processed block at time {curr_time} height #{curr_block["header"]["height"]}, # txs in a block: {txs}, per chunk: {tx_per_chunk}, gas in block: {gas_block}, gas per chunk: {gas_per_chunk}' + ) + prev_hash = curr_block['header']['prev_hash'] + curr_block = archival_node.get_block(prev_hash)['result'] + curr_time = get_timestamp(curr_block) + block_times.reverse() + tx_count.reverse() + assert block_times + tx_cumulative = data.compute_cumulative(tx_count) + bps = data.compute_rate(block_times) + tps_fit = data.liunc_regression(block_times, tx_cumulative) + logger.info( + f'Num blocks: {len(block_times)}, num transactions: {len(tx_count)}, bps: {bps}, tps_fit: {tps_fit}' + ) + return {'bps': bps, 'tps': tps_fit['slope']} + + +def get_tx_events_single_node(node, tx_filename): + try: + target_file = f'./logs/{node.instance_name}_txs' + node.machine.download(tx_filename, target_file) + with open(target_file, 'r') as f: + return [float(line.strip()) for line in f.readlines()] + except Exception as e: + logger.exception(f'Getting tx_events from {node.instance_name} failed') + return [] + + +def get_tx_events(nodes, tx_filename): + run('mkdir ./logs/') + run('rm -rf ./logs/*_txs') + all_events = pmap( + lambda node: get_tx_events_single_node(node, tx_filename), + nodes, + ) + return sorted(data.flatten(all_events)) + + +# Sends the transaction to the network via `node` and checks for success. +# Some retrying is done when the node returns a Timeout error. +def send_transaction(node, tx, tx_hash, account_id, timeout=120): + response = node.send_tx_and_wait(tx, timeout) + loop_start = time.time() + missing_count = 0 + while 'error' in response.keys(): + error_data = response['error']['data'] + if 'timeout' in error_data.lower(): + logger.warning( + f'transaction {tx_hash} returned Timout, checking status again.' + ) + time.sleep(5) + response = node.get_tx(tx_hash, account_id) + elif 'does not exist' in error_data: + missing_count += 1 + logger.warning( + f'transaction {tx_hash} failed to be received by the node, checking again.' + ) + if missing_count < 20: + time.sleep(5) + response = node.get_tx(tx_hash, account_id) + else: + logger.warning(f're-sending transaction {tx_hash}.') + response = node.send_tx_and_wait(tx, timeout) + missing_count = 0 + else: + raise RuntimeError( + f'Error in processing transaction {tx_hash}: {response}') + if time.time() - loop_start > timeout: + raise TimeoutError( + f'Transaction {tx_hash} did not complete successfully within the timeout' + ) + + if 'SuccessValue' not in response['result']['status']: + raise RuntimeError( + f'ERROR: Failed transaction {tx_hash}. Response: {response}') + + +def transfer_between_nodes(nodes): + logger.info('Testing transfer between mocknet validators') + node = nodes[0] + alice = get_validator_account(nodes[1]) + bob = get_validator_account(nodes[0]) + transfer_amount = 100 + get_balance = lambda account: int( + node.get_account(account.account_id)['result']['amount']) + + alice_initial_balance = get_balance(alice) + alice_nonce = node.get_nonce_for_pk(alice.account_id, alice.pk) + bob_initial_balance = get_balance(bob) + logger.info(f'Alice initial balance: {alice_initial_balance}') + logger.info(f'Bob initial balance: {bob_initial_balance}') + + last_block_hash = node.get_latest_block().hash_bytes + + tx, tx_hash = sign_payment_tx_and_get_hash(alice, bob.account_id, + transfer_amount, alice_nonce + 1, + last_block_hash) + send_transaction(node, tx, tx_hash, alice.account_id) + + alice_final_balance = get_balance(alice) + bob_final_balance = get_balance(bob) + logger.info(f'Alice final balance: {alice_final_balance}') + logger.info(f'Bob final balance: {bob_final_balance}') + + # Check mod 1000 to ignore the cost of the transaction itself + assert (alice_initial_balance - + alice_final_balance) % 1000 == transfer_amount + assert bob_final_balance - bob_initial_balance == transfer_amount + + +def stake_node(node): + account = get_validator_account(node) + logger.info(f'Staking {account.account_id}.') + nonce = node.get_nonce_for_pk(account.account_id, account.pk) + + validators = node.get_validators(timeout=None)['result'] + if account.account_id in validators['current_validators']: + return + stake_amount = max( + map(lambda v: int(v['stake']), validators['current_validators'])) + + latest_block_hash = node.get_status()['sync_info']['latest_block_hash'] + last_block_hash_decoded = base58.b58decode(latest_block_hash.encode('utf8')) + + staking_tx, staking_tx_hash = sign_staking_tx_and_get_hash( + account, account, stake_amount, nonce + 1, last_block_hash_decoded) + send_transaction(node, staking_tx, staking_tx_hash, account.account_id) + + +def accounts_from_nodes(nodes): + return pmap(get_validator_account, nodes) + + +def kill_proccess_script(pid): + return f''' + sudo kill {pid} + while kill -0 {pid}; do + sleep 1 + done + ''' + + +def get_unc_pid(machine): + p = machine.run( + "ps aux | grep 'near.* run' | grep -v grep | awk '{print $2}'") + return p.stdout.strip() + + +def is_binary_running(binary_name: str, node) -> bool: + result = node.machine.run(f'ps -A -o comm= | grep {binary_name}') + return result.returncode == 0 + + +def is_binary_running_all_nodes(binary_name: str, all_nodes) -> bool: + return pmap(functools.partial(is_binary_running, binary_name), all_nodes) + + +def stop_node(node): + m = node.machine + logger.info(f'Stopping node {m.name}') + pids = get_unc_pid(m).split() + + for pid in pids: + m.run('bash', input=kill_proccess_script(pid)) + m.run('sudo -u ubuntu -i', input=TMUX_STOP_SCRIPT) + + +def upload_and_extract(node, src_filename, dst_filename): + node.machine.upload(f'{src_filename}.gz', + f'{dst_filename}.gz', + switch_user='ubuntu') + node.machine.run('gunzip -f {dst_filename}.gz'.format( + dst_filename=shlex.quote(dst_filename))) + + +def compress_and_upload(nodes, src_filename, dst_filename): + res = run(f'gzip {src_filename}') + assert res.returncode == 0 + pmap(lambda node: upload_and_extract(node, src_filename, dst_filename), + nodes) + + +def redownload_neard(nodes, binary_url): + pmap( + lambda node: node.machine. + run('sudo -u ubuntu -i', + input='wget -O /home/ubuntu/uncd {}; chmod +x /home/ubuntu/uncd'. + format(binary_url)), nodes) + + +# Check /home/ubuntu/uncd.upgrade to see whether the amend-genesis command is +# available. Returns the path to the uncd binary or throws an exception. +def neard_amend_genesis_path(node): + r = node.machine.run( + '/home/ubuntu/uncd.upgrade amend-genesis --help', + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + if r.exitcode == 0: + return '/home/ubuntu/uncd.upgrade' + raise Exception(f'`uncd.upgrade amend-genesis` not available') + + +# We assume that the nodes already have the .near directory with the files +# node_key.json, validator_key.json and config.json. +def create_and_upload_genesis( + validator_nodes, + chain_id, + rpc_nodes=None, + epoch_length=20000, + node_pks=None, + increasing_stakes=0.0, + num_seats=100, + single_shard=False, + all_node_pks=None, + node_ips=None, +): + logger.info('Uploading genesis and config files') + assert chain_id + logger.info( + 'Assuming that genesis_updater.py is available on the instances.') + validator_keys = dict(pmap(get_validator_key, validator_nodes)) + rpc_node_names = [node.instance_name for node in rpc_nodes] + assert '-spoon' in chain_id, f'Expecting chain_id like "testnet-spoon" or "mainnet-spoon", got {chain_id}' + chain_id_in = chain_id.split('-spoon')[0] + genesis_filename_in = f'/home/ubuntu/.near/{chain_id_in}-genesis/genesis.json' + records_filename_in = f'/home/ubuntu/.near/{chain_id_in}-genesis/records.json' + config_filename_in = f'/home/ubuntu/.near/{chain_id_in}-genesis/config.json' + stamp = time.strftime('%Y%m%d-%H%M%S', time.gmtime()) + done_filename = f'/home/ubuntu/genesis_update_done_{stamp}.txt' + uncd = neard_amend_genesis_path(validator_nodes[1]) + pmap( + lambda node: start_genesis_updater( + node=node, + script='genesis_updater.py', + genesis_filename_in=genesis_filename_in, + records_filename_in=records_filename_in, + config_filename_in=config_filename_in, + out_dir='/home/ubuntu/.near/', + chain_id=chain_id, + validator_keys=validator_keys, + rpc_nodes=rpc_node_names, + done_filename=done_filename, + epoch_length=epoch_length, + node_pks=node_pks, + increasing_stakes=increasing_stakes, + num_seats=num_seats, + single_shard=single_shard, + all_node_pks=all_node_pks, + node_ips=node_ips, + uncd=uncd, + ), + validator_nodes + rpc_nodes, + ) + pmap(lambda node: wait_genesis_updater_done(node, done_filename), + validator_nodes + rpc_nodes) + + +def extra_genesis_records(validator_keys, rpc_node_names, node_pks, + seen_accounts, num_seats, increasing_stakes): + records = [] + + VALIDATOR_BALANCE = (10**2) * ONE_NEAR + RPC_BALANCE = (10**1) * ONE_NEAR + LOAD_TESTER_BALANCE = (10**4) * ONE_NEAR + + for account_id, balance in ACCOUNTS.items(): + if account_id not in seen_accounts: + records.append({ + 'Account': { + 'account_id': account_id, + 'account': { + 'amount': str(balance), + 'locked': '0', + 'code_hash': '11111111111111111111111111111111', + 'storage_usage': 0, + 'version': 'V1' + } + } + }) + pkeys = [PUBLIC_KEY] + if node_pks: + pkeys += node_pks + for pk in pkeys: + records.append({ + 'AccessKey': { + 'account_id': account_id, + 'public_key': pk, + 'access_key': { + 'nonce': 0, + 'permission': 'FullAccess' + } + } + }) + + stakes = [] + prev_stake = None + for i, account_id in enumerate(validator_keys): + logger.info(f'Adding account {account_id}') + if increasing_stakes: + if i * 5 < num_seats * 3 and i < len(MAINNET_STAKES): + staked = MAINNET_STAKES[i] * ONE_NEAR + elif prev_stake is None: + prev_stake = MIN_STAKE - STAKE_STEP + staked = prev_stake * ONE_NEAR + else: + prev_stake = prev_stake + STAKE_STEP + staked = prev_stake * ONE_NEAR + else: + staked = MIN_STAKE + stakes.append((staked, account_id)) + records.append({ + 'Account': { + 'account_id': account_id, + 'account': { + 'amount': str(VALIDATOR_BALANCE), + 'locked': str(staked), + 'code_hash': '11111111111111111111111111111111', + 'storage_usage': 0, + 'version': 'V1' + } + } + }) + records.append({ + 'AccessKey': { + 'account_id': account_id, + 'public_key': PUBLIC_KEY, + 'access_key': { + 'nonce': 0, + 'permission': 'FullAccess' + } + } + }) + for i in range(NUM_ACCOUNTS): + load_testing_account = load_testing_account_id(account_id, i) + logger.info(f'Adding load testing account {load_testing_account}') + records.append({ + 'Account': { + 'account_id': load_testing_account, + 'account': { + 'amount': str(LOAD_TESTER_BALANCE), + 'locked': str(0), + 'code_hash': '11111111111111111111111111111111', + 'storage_usage': 0, + 'version': 'V1' + } + } + }) + records.append({ + 'AccessKey': { + 'account_id': load_testing_account, + 'public_key': PUBLIC_KEY, + 'access_key': { + 'nonce': 0, + 'permission': 'FullAccess' + } + } + }) + for node_name in rpc_node_names: + account_id = node_account_name(node_name) + logger.info(f'Adding rpc node account {account_id}') + records.append({ + 'Account': { + 'account_id': account_id, + 'account': { + 'amount': str(RPC_BALANCE), + 'locked': str(0), + 'code_hash': '11111111111111111111111111111111', + 'storage_usage': 0, + 'version': 'V1' + } + } + }) + records.append({ + 'AccessKey': { + 'account_id': account_id, + 'public_key': PUBLIC_KEY, + 'access_key': { + 'nonce': 0, + 'permission': 'FullAccess' + } + } + }) + + validators = [] + seats = compute_seats(stakes, num_seats) + seats_taken = 0 + for seats, staked, account_id in seats: + if seats + seats_taken > num_seats: + break + validators.append({ + 'account_id': account_id, + 'public_key': validator_keys[account_id], + 'amount': str(staked), + }) + seats_taken += seats + + return records, validators + + +def neard_amend_genesis(uncd, validator_keys, genesis_filename_in, + records_filename_in, out_dir, rpc_node_names, chain_id, + epoch_length, node_pks, increasing_stakes, num_seats, + single_shard): + extra_records, validators = extra_genesis_records(validator_keys, + rpc_node_names, node_pks, + set(), num_seats, + increasing_stakes) + + validators_filename = os.path.join(out_dir, 'validators.json') + extra_records_filename = os.path.join(out_dir, 'extra-records.json') + genesis_filename_out = os.path.join(out_dir, 'genesis.json') + records_filename_out = os.path.join(out_dir, 'records.json') + + with open(validators_filename, 'w') as f: + json.dump(validators, f) + with open(extra_records_filename, 'w') as f: + json.dump(extra_records, f) + + cmd = [ + uncd, + 'amend-genesis', + '--genesis-file-in', + genesis_filename_in, + '--records-file-in', + records_filename_in, + '--extra-records', + extra_records_filename, + '--validators', + validators_filename, + '--genesis-file-out', + genesis_filename_out, + '--records-file-out', + records_filename_out, + '--num-seats', + str(int(num_seats)), + '--transaction-validity-period', + str(10**9), + '--protocol-version', + '49', + '--protocol-reward-rate', + '1/10', + '--block-producer-kickout-threshold', + '10', + ] + if chain_id is not None: + cmd.extend(['--chain-id', chain_id]) + if epoch_length is not None: + cmd.extend(['--epoch-length', str(epoch_length)]) + if single_shard: + shard_layout_filename = os.path.join(out_dir, 'shard_layout.json') + with open(shard_layout_filename, 'w') as f: + json.dump({'V0': {'num_shards': 1, 'version': 0}}, f) + + cmd.extend(['--shard-layout-file', shard_layout_filename]) + + subprocess.run(cmd, text=True) + + +def create_and_upload_genesis_file_from_empty_genesis( + validator_node_and_stakes, + rpc_nodes, + chain_id=None, + epoch_length=None, + num_seats=None, +): + node0 = validator_node_and_stakes[0][0] + node0.machine.run( + 'rm -rf /home/ubuntu/.unc-tmp && mkdir /home/ubuntu/.unc-tmp && /home/ubuntu/uncd --home /home/ubuntu/.unc-tmp init --chain-id {}' + .format(chain_id)) + genesis_config = download_and_read_json( + node0, "/home/ubuntu/.unc-tmp/genesis.json") + records = [] + + VALIDATOR_BALANCE = (10**2) * ONE_NEAR + RPC_BALANCE = (10**1) * ONE_NEAR + TREASURY_ACCOUNT = 'test.near' + TREASURY_BALANCE = (10**7) * ONE_NEAR + LOAD_TESTER_BALANCE = (10**4) * ONE_NEAR + + SKYWARD_CONTRACT_BALANCE = (10**6) * ONE_NEAR + TOKEN1_BALANCE = (10**6) * ONE_NEAR + TOKEN2_BALANCE = (10**6) * ONE_NEAR + TOKEN2_OWNER_BALANCE = (10**6) * ONE_NEAR + ACCOUNT1_BALANCE = (10**6) * ONE_NEAR + + genesis_config['chain_id'] = chain_id + + master_balance = 10**7 + assert master_balance > 0 + accounts = { + TREASURY_ACCOUNT: TREASURY_BALANCE, + MASTER_ACCOUNT: master_balance, + SKYWARD_ACCOUNT: SKYWARD_CONTRACT_BALANCE, + TOKEN1_ACCOUNT: TOKEN1_BALANCE, + TOKEN2_ACCOUNT: TOKEN2_BALANCE, + TOKEN2_OWNER_ACCOUNT: TOKEN2_OWNER_BALANCE, + ACCOUNT1_ACCOUNT: ACCOUNT1_BALANCE + } + + for account_id, balance in accounts.items(): + records.append({ + 'Account': { + 'account_id': account_id, + 'account': { + 'amount': str(balance), + 'locked': '0', + 'code_hash': '11111111111111111111111111111111', + 'storage_usage': 0, + 'version': 'V1' + } + } + }) + records.append({ + 'AccessKey': { + 'account_id': account_id, + 'public_key': PUBLIC_KEY, + 'access_key': { + 'nonce': 0, + 'permission': 'FullAccess' + } + } + }) + + stakes = [] + account_id_to_validator_pk = {} + for i, (node, stake_multiplier) in enumerate(validator_node_and_stakes): + validator = get_validator_account(node) + logger.info(f'Adding account {validator.account_id}') + account_id_to_validator_pk[validator.account_id] = validator.pk + staked = MIN_STAKE * stake_multiplier + stakes.append((staked, validator.account_id)) + records.append({ + 'Account': { + 'account_id': validator.account_id, + 'account': { + 'amount': str(VALIDATOR_BALANCE), + 'locked': str(staked), + 'code_hash': '11111111111111111111111111111111', + 'storage_usage': 0, + 'version': 'V1' + } + } + }) + records.append({ + 'AccessKey': { + 'account_id': validator.account_id, + 'public_key': PUBLIC_KEY, + 'access_key': { + 'nonce': 0, + 'permission': 'FullAccess' + } + } + }) + for i in range(NUM_ACCOUNTS): + load_testing_account = load_testing_account_id( + validator.account_id, i) + logger.info(f'Adding load testing account {load_testing_account}') + records.append({ + 'Account': { + 'account_id': load_testing_account, + 'account': { + 'amount': str(LOAD_TESTER_BALANCE), + 'locked': str(0), + 'code_hash': '11111111111111111111111111111111', + 'storage_usage': 0, + 'version': 'V1' + } + } + }) + records.append({ + 'AccessKey': { + 'account_id': load_testing_account, + 'public_key': PUBLIC_KEY, + 'access_key': { + 'nonce': 0, + 'permission': 'FullAccess' + } + } + }) + + genesis_config['validators'] = [] + seats = compute_seats(stakes, num_seats) + seats_taken = 0 + for seats, staked, account_id in seats: + if seats + seats_taken > num_seats: + break + genesis_config['validators'].append({ + 'account_id': account_id, + 'public_key': account_id_to_validator_pk[account_id], + 'amount': str(staked), + }) + seats_taken += seats + + total_supply = 0 + for record in records: + account = record.get('Account', {}).get('account', {}) + total_supply += int(account.get('locked', 0)) + total_supply += int(account.get('amount', 0)) + genesis_config['total_supply'] = str(total_supply) + genesis_config['protocol_version'] = 57 + genesis_config['epoch_length'] = int(epoch_length) + genesis_config['num_block_producer_seats'] = int(num_seats) + genesis_config['protocol_reward_rate'] = [1, 10] + # Loadtest helper signs all transactions using the same block. + # Extend validity period to allow the same hash to be used for the whole duration of the test. + genesis_config['transaction_validity_period'] = 10**9 + # Protocol upgrades require downtime, therefore make it harder to kickout validators. + # The default value of this parameter is 90. + genesis_config['block_producer_kickout_threshold'] = 10 + + genesis_config['shard_layout'] = {'V0': {'num_shards': 4, 'version': 0}} + genesis_config['simple_nightshade_shard_layout'] = {} + genesis_config['num_block_producer_seats_per_shard'] = [int(num_seats)] * 4 + + genesis_config['records'] = records + for node in [node for (node, _) in validator_node_and_stakes] + rpc_nodes: + upload_json(node, '/home/ubuntu/.near/genesis.json', genesis_config) + + +def download_and_read_json(node, filename): + try: + tmp_file = tempfile.NamedTemporaryFile(mode='r+', delete=False) + node.machine.download(filename, tmp_file.name) + tmp_file.close() + with open(tmp_file.name, 'r') as f: + return json.load(f) + except: + logger.error( + f'Failed to download json file. Node: {node.instance_name}. Filename: {filename}' + ) + raise + + +def upload_json(node, filename, data): + try: + logger.info(f'Upload file {filename} to {node.instance_name}') + tmp_file = tempfile.NamedTemporaryFile(mode='r+', delete=False) + with open(tmp_file.name, 'w') as f: + json.dump(data, f, indent=2) + node.machine.upload(tmp_file.name, filename) + tmp_file.close() + except: + logger.error( + f'Failed to upload json file. Node: {node.instance_name}. Filename: {filename}' + ) + raise + + +def get_node_addr(node, port): + node_key_json = download_and_read_json(node, + '/home/ubuntu/.near/node_key.json') + return f'{node_key_json["public_key"]}@{node.ip}:{port}' + + +def get_validator_account_id(node): + node_key_json = download_and_read_json( + node, '/home/ubuntu/.near/validator_key.json') + return node_key_json["account_id"] + + +def get_validator_key(node): + node_key_json = download_and_read_json( + node, '/home/ubuntu/.near/validator_key.json') + return node_key_json["account_id"], node_key_json["public_key"] + + +def get_node_keys(node): + logger.info(f'get_node_keys from {node.instance_name}') + node_key_json = download_and_read_json( + node, + '/home/ubuntu/.near/node_key.json', + ) + return node_key_json['public_key'], node_key_json['secret_key'] + + +def init_validator_key(node): + account_id = node_account_name(node.instance_name) + node.machine.run( + f'dir=$(mktemp -d) && /home/ubuntu/uncd --home $dir init --account-id {account_id} && mv $dir/validator_key.json /home/ubuntu/.near/validator_key.json' + ) + + +def update_config_file( + config_filename_in, + config_filename_out, + all_node_pks, + node_ips, +): + with open(config_filename_in) as f: + config_json = json.load(f) + + # Usually the port is 24567 + port = config_json['network']['addr'].split(':')[1] + node_addresses = [ + f'{node_key}@{node_ip}:{port}' + for node_key, node_ip in zip(all_node_pks, node_ips) + ] + + config_json['tracked_shards'] = [0] + config_json['archive'] = True + config_json['archival_peer_connections_lower_bound'] = 1 + config_json['network']['boot_nodes'] = ','.join(node_addresses) + config_json['rpc']['addr'] = '0.0.0.0:3030' + if 'telemetry' in config_json: + config_json['telemetry']['endpoints'] = [] + + with open(config_filename_out, 'w') as f: + json.dump(config_json, f, indent=2) + + +def create_and_upload_config_file_from_default(nodes, chain_id, overrider=None): + nodes[0].machine.run( + 'rm -rf /home/ubuntu/.unc-tmp && mkdir /home/ubuntu/.unc-tmp && /home/ubuntu/uncd --home /home/ubuntu/.unc-tmp init --chain-id {}' + .format(chain_id)) + config_json = download_and_read_json( + nodes[0], + '/home/ubuntu/.unc-tmp/config.json', + ) + config_json['tracked_shards'] = [0, 1, 2, 3] + config_json['archive'] = True + config_json['archival_peer_connections_lower_bound'] = 1 + node_addresses = [get_node_addr(node, 24567) for node in nodes] + config_json['network']['boot_nodes'] = ','.join(node_addresses) + config_json['network']['skip_sync_wait'] = False + config_json['rpc']['addr'] = '0.0.0.0:3030' + config_json['rpc']['enable_debug_rpc'] = True + if 'telemetry' in config_json: + config_json['telemetry']['endpoints'] = [] + + for node in nodes: + copied_config = json.loads(json.dumps(config_json)) + if overrider: + overrider(node, copied_config) + upload_json(node, '/home/ubuntu/.near/config.json', copied_config) + + +def update_existing_config_file(nodes, overrider=None): + for node in nodes: + config_json = download_and_read_json( + nodes[0], + '/home/ubuntu/.near/config.json', + ) + overrider(node, config_json) + upload_json(node, '/home/ubuntu/.near/config.json', config_json) + + +def start_nodes(nodes, upgrade_schedule=None): + pmap( + lambda node: start_node(node, upgrade_schedule=upgrade_schedule), + nodes, + ) + + +def stop_nodes(nodes): + pmap(stop_node, nodes) + + +def clear_data(nodes): + pmap(lambda node: node.machine.run('rm -rf /home/ubuntu/.near/data'), nodes) + + +def neard_start_script(node, upgrade_schedule=None, epoch_height=None): + if upgrade_schedule and upgrade_schedule.get(node.instance_name, + 0) <= epoch_height: + neard_binary = '/home/ubuntu/uncd.upgrade' + else: + neard_binary = '/home/ubuntu/uncd' + return ''' + sudo mv /home/ubuntu/near.log /home/ubuntu/near.log.1 2>/dev/null + sudo mv /home/ubuntu/near.upgrade.log /home/ubuntu/near.upgrade.log.1 2>/dev/null + sudo rm -rf /home/ubuntu/.near/data + tmux new -s near -d bash + tmux send-keys -t near 'RUST_BACKTRACE=full RUST_LOG=debug,actix_web=info {neard_binary} run 2>&1 | tee -a {neard_binary}.log' C-m + '''.format(neard_binary=shlex.quote(neard_binary)) + + +def start_node(node, upgrade_schedule=None): + m = node.machine + logger.info(f'Starting node {m.name}') + attempt = 0 + success = False + while attempt < 3: + pid = get_unc_pid(m) + if pid != '': + success = True + break + start_process = m.run( + 'sudo -u ubuntu -i', + input=neard_start_script( + node, + upgrade_schedule=upgrade_schedule, + epoch_height=0, + ), + ) + if start_process.returncode == 0: + success = True + break + logger.warn( + f'Failed to start process, returncode: {start_process.returncode}\n{node.instance_name}\n{start_process.stderr}' + ) + attempt += 1 + time.sleep(1) + if not success: + raise Exception(f'Could not start node {node.instance_name}') + + +def reset_data(node, retries=0): + try: + m = node.machine + stop_node(node) + logger.info(f'Clearing data directory of node {m.name}') + start_process = m.run('bash', input='rm -r /home/ubuntu/.near') + assert start_process.returncode == 0, m.name + '\n' + start_process.stderr + except: + if retries < 3: + logger.warning( + 'And error occurred while clearing data directory, retrying') + reset_data(node, retries=retries + 1) + else: + raise Exception( + f'ERROR: Could not clear data directory for {node.machine.name}' + ) + + +def start_genesis_updater_script( + script, + genesis_filename_in, + records_filename_in, + config_filename_in, + out_dir, + chain_id, + validator_keys, + rpc_nodes, + done_filename, + epoch_length, + node_pks, + increasing_stakes, + num_seats, + single_shard, + all_node_pks, + node_ips, + uncd, +): + validators = ','.join( + [f'{account_id}={key}' for (account_id, key) in validator_keys.items()]) + cmd = ' '.join([ + shlex.quote(str(arg)) for arg in [ + 'nohup', + './venv/bin/python', + script, + genesis_filename_in, + records_filename_in, + config_filename_in, + out_dir, + chain_id, + validators, + ','.join(rpc_nodes), + done_filename, + epoch_length, + ','.join(node_pks), + increasing_stakes, + num_seats, + single_shard, + ','.join(all_node_pks), + ','.join(node_ips), + uncd if uncd is not None else 'None', + ] + ]) + return ''' + cd {dir} + {cmd} 1> genesis_updater.out 2> genesis_updater.err < /dev/null & + '''.format(dir=shlex.quote(PYTHON_DIR), cmd=cmd) + + +def start_genesis_updater(node, script, genesis_filename_in, + records_filename_in, config_filename_in, out_dir, + chain_id, validator_keys, rpc_nodes, done_filename, + epoch_length, node_pks, increasing_stakes, num_seats, + single_shard, all_node_pks, node_ips, uncd): + logger.info(f'Starting genesis_updater on {node.instance_name}') + node.machine.run( + 'bash', + input=start_genesis_updater_script( + script, + genesis_filename_in, + records_filename_in, + config_filename_in, + out_dir, + chain_id, + validator_keys, + rpc_nodes, + done_filename, + epoch_length, + node_pks, + increasing_stakes, + num_seats, + single_shard, + all_node_pks, + node_ips, + uncd, + ), + ) + + +def wait_genesis_updater_done(node, done_filename): + msg = f'Waiting for the genesis updater on {node.instance_name}' + logger.info(msg) + + # Wait until the uncd process stops running. + while True: + time.sleep(5) + if not is_binary_running('uncd', node): + break + + # Check if the done_filename was produced. + result = node.machine.run(f'ls {done_filename}') + if result.returncode != 0: + # Get the stderr of the genesis updater. + script = f'cat {shlex.quote(PYTHON_DIR)}/genesis_updater.err' + stderr = node.machine.run(script).stdout + error_msg = f'The {done_filename} is missing! The stderr is \n\n\n{stderr}\n' + raise Exception(f'{msg} - failed. {error_msg}') + + logger.info(f'{msg} -- done') + + +# Waits until the node becomes responsive to RPC requests. It works the same as +# list_validators but assumes that the node can still be starting and expects +# connection errors and the data not being available. +def wait_node_up(node): + msg = f'Waiting for node {node.instance_name} to start' + logger.info(msg) + attempt = 0 + while True: + try: + if not is_binary_running('uncd', node): + raise Exception(f'{msg} - failed. The uncd process crashed.') + + response = node.get_validators() + + if 'error' in response: + attempt += 1 + logger.info(f'{msg}, attempt {attempt} error response.') + continue + if 'result' not in response: + attempt += 1 + logger.info(f'{msg}, attempt {attempt} result missing.') + continue + + logger.info(f'{msg} - done.') + return True + except (ConnectionRefusedError, requests.exceptions.ConnectionError): + attempt += 1 + logger.info(f'{msg}, attempt {attempt} connection refused.') + time.sleep(30) + + +def wait_all_nodes_up(all_nodes): + pmap(lambda node: wait_node_up(node), all_nodes) + + +def create_upgrade_schedule( + rpc_nodes, + validator_nodes, + progressive_upgrade, + increasing_stakes, + num_block_producer_seats, +): + schedule = {} + if progressive_upgrade: + # Re-create stakes assignment. + stakes = [] + if increasing_stakes: + prev_stake = None + for i, node in enumerate(validator_nodes): + if (i * 5 < num_block_producer_seats * 3 and + i < len(MAINNET_STAKES)): + staked = MAINNET_STAKES[i] * ONE_NEAR + elif prev_stake is None: + prev_stake = MIN_STAKE - STAKE_STEP + staked = prev_stake * ONE_NEAR + else: + prev_stake = prev_stake + STAKE_STEP + staked = prev_stake * ONE_NEAR + stakes.append((staked, node.instance_name)) + + else: + for node in validator_nodes: + stakes.append((MIN_STAKE, node.instance_name)) + logger.info(f'create_upgrade_schedule') + for stake, instance_name in stakes: + logger.debug(f'instance_name: {instance_name} - stake: {stake}') + + # Compute seat assignments. + seats = compute_seats(stakes, num_block_producer_seats) + + seats_upgraded = 0 + for seat, stake, instance_name in seats: + # As the protocol upgrade takes place after 80% of the nodes are + # upgraded, stop a bit earlier to start in a non-upgraded state. + if (seats_upgraded + seat) * 100 > 75 * num_block_producer_seats: + break + schedule[instance_name] = 0 + logger.info( + f'validator node {node.instance_name} will start upgraded') + seats_upgraded += seat + + # Upgrade the remaining validators during 4 epochs. + for node in validator_nodes: + if node.instance_name not in schedule: + schedule[node.instance_name] = random.randint(1, 4) + logger.info( + f'validator node {node.instance_name} will upgrade at {schedule[node.instance_name]}' + ) + + for node in rpc_nodes: + schedule[node.instance_name] = random.randint(0, 4) + if not schedule[node.instance_name]: + logger.info( + f'rpc node {node.instance_name} will start upgraded') + else: + logger.info( + f'rpc node {node.instance_name} will upgrade at {schedule[node.instance_name]}' + ) + else: + # Start all nodes upgraded. + for node in rpc_nodes: + schedule[node.instance_name] = 0 + for node in validator_nodes: + schedule[node.instance_name] = 0 + + return schedule + + +def compute_seats(stakes, num_block_producer_seats): + max_stake = 0 + for i in stakes: + max_stake = max(max_stake, i[0]) + + # Compute seats assignment. + l = 0 + r = max_stake + 1 + seat_price = -1 + while r - l > 1: + tmp_seat_price = (l + r) // 2 + num_seats = 0 + for i in range(len(stakes)): + num_seats += stakes[i][0] // tmp_seat_price + if num_seats <= num_block_producer_seats: + r = tmp_seat_price + else: + l = tmp_seat_price + seat_price = r + logger.info(f'compute_seats seat_price: {seat_price}') + + seats = [] + for stake, item in stakes: + seats.append((stake // seat_price, stake, item)) + seats.sort(reverse=True) + return seats + + +def upgrade_nodes(epoch_height, upgrade_schedule, all_nodes): + logger.info(f'Upgrading nodes for epoch height {epoch_height}') + for node in all_nodes: + if upgrade_schedule.get(node.instance_name, 0) == epoch_height: + upgrade_node(node) + + +def get_epoch_height(rpc_nodes, prev_epoch_height): + nodes = rpc_nodes.copy() + random.shuffle(nodes) + max_height = prev_epoch_height + for node in nodes: + (addr, port) = node.rpc_addr() + j = { + 'method': 'validators', + 'params': [None], + 'id': 'dontcare', + 'jsonrpc': '2.0' + } + try: + r = requests.post('http://%s:%s' % (addr, port), json=j, timeout=15) + if r.ok: + response = r.json() + max_height = max( + max_height, + int(response.get('result', {}).get('epoch_height', 0)), + ) + except Exception as e: + continue + return max_height + + +def neard_restart_script(node): + neard_binary = '/home/ubuntu/uncd.upgrade' + return ''' + tmux send-keys -t near C-c + sudo mv /home/ubuntu/near.log /home/ubuntu/near.log.1 2>/dev/null + sudo mv /home/ubuntu/near.upgrade.log /home/ubuntu/near.upgrade.log.1 2>/dev/null + tmux send-keys -t near 'RUST_BACKTRACE=full RUST_LOG=debug,actix_web=info {neard_binary} run 2>&1 | tee -a {neard_binary}.log' C-m + '''.format(neard_binary=shlex.quote(neard_binary)) + + +def upgrade_node(node): + logger.info(f'Upgrading node {node.instance_name}') + attempt = 0 + success = False + while attempt < 3: + start_process = node.machine.run( + 'sudo -u ubuntu -i', + input=neard_restart_script(node), + ) + if start_process.returncode == 0: + success = True + break + logger.warn( + f'Failed to upgrade uncd, return code: {start_process.returncode}\n{node.instance_name}\n{start_process.stderr}' + ) + attempt += 1 + time.sleep(1) + if not success: + raise Exception(f'Could not upgrade node {node.instance_name}') + + +STAKING_TIMEOUT = 60 + + +# If the available amount of whole NEAR tokens is above 10**3, then stakes all available amount. +# Runs only if `last_staking` is at least `STAKING_TIMEOUT` seconds in the past. +def stake_available_amount(node_account, last_staking): + # Repeat the staking transactions in case the validator selection algorithm changes. + # Don't query the balance too often, avoid overloading the RPC node. + if time.time() - last_staking > STAKING_TIMEOUT: + # Make several attempts just in case the RPC node doesn't respond. + for attempt in range(3): + try: + stake_amount = node_account.get_amount_yoctonear() + logger.info( + f'Amount of {node_account.key.account_id} is {stake_amount}' + ) + if stake_amount > (10**3) * ONE_NEAR: + logger.info( + f'Staking {stake_amount} for {node_account.key.account_id}' + ) + node_account.send_stake_tx(stake_amount) + logger.info( + f'Staked {stake_amount} for {node_account.key.account_id}') + return time.time() + except Exception as e: + logger.info('Failed to stake') + return None diff --git a/pytest/lib/mocknet_helpers.py b/pytest/lib/mocknet_helpers.py new file mode 100644 index 000000000..9c8e27751 --- /dev/null +++ b/pytest/lib/mocknet_helpers.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +import time +import base58 +import requests +from configured_logger import logger +from key import Key + +LOCAL_ADDR = '127.0.0.1' +RPC_PORT = '3030' + + +def get_status(addr=LOCAL_ADDR, port=RPC_PORT): + r = requests.get(f'http://{addr}:{port}/status', timeout=10) + r.raise_for_status() + return r.json() + + +def json_rpc(method, params, addr=LOCAL_ADDR, port=RPC_PORT): + j = {'method': method, 'params': params, 'id': 'dontcare', 'jsonrpc': '2.0'} + r = requests.post(f'http://{addr}:{port}', json=j, timeout=10) + return r.json() + + +def get_nonce_for_key(key: Key, **kwargs) -> int: + return get_nonce_for_pk(key.account_id, key.pk, **kwargs) + + +def get_nonce_for_pk(account_id, + pk, + finality='optimistic', + addr=LOCAL_ADDR, + port=RPC_PORT, + logger=logger): + access_keys = json_rpc( + 'query', + { + 'request_type': 'view_access_key_list', + 'account_id': account_id, + 'finality': finality + }, + addr=addr, + port=port, + ) + logger.info(f'get_nonce_for_pk {account_id}') + logger.info(access_keys) + if not access_keys['result']['keys']: + raise KeyError(account_id) + + nonce = next((key['access_key']['nonce'] + for key in access_keys['result']['keys'] + if key['public_key'] == pk), None) + if nonce is None: + raise KeyError(f'Nonce for {account_id} {pk} not found') + return nonce + + +def get_latest_block_hash(addr=LOCAL_ADDR, port=RPC_PORT): + last_block_hash = get_status( + addr=addr, + port=port, + )['sync_info']['latest_block_hash'] + return base58.b58decode(last_block_hash.encode('utf-8')) + + +def throttle_txns(send_txns, total_tx_sent, elapsed_time, test_state): + start_time = time.monotonic() + send_txns(test_state) + duration = time.monotonic() - start_time + total_tx_sent += test_state.num_test_accounts() + + excess_transactions = total_tx_sent - (test_state.max_tps_per_node * + (elapsed_time + duration)) + if excess_transactions > 0: + delay = excess_transactions / test_state.max_tps_per_node + logger.info(f'Sleeping for {delay} seconds to throttle transactions') + time.sleep(delay) + + return total_tx_sent + + +def retry_and_ignore_errors(f): + for attempt in range(3): + try: + return f() + except Exception as e: + time.sleep(0.1 * 2**attempt) + return None + + +def wait_at_least_one_block(): + status = get_status() + start_height = status['sync_info']['latest_block_height'] + timeout_sec = 5 + started = time.monotonic() + while time.monotonic() - started < timeout_sec: + status = get_status() + height = status['sync_info']['latest_block_height'] + if height > start_height: + break + time.sleep(1.0) + + +def get_amount_yoctonear(account_id, addr=LOCAL_ADDR, port=RPC_PORT): + j = json_rpc('query', { + 'request_type': 'view_account', + 'finality': 'optimistic', + 'account_id': account_id + }, + addr=addr, + port=port) + return int(j.get('result', {}).get('amount', 0)) + + +# Returns the transaction result for the given txn_id. +# Might return None - if transaction is not present and wait_for_success is false. +def tx_result(txn_id, account_id, wait_for_success=False, **kwargs): + while True: + status = json_rpc("EXPERIMENTAL_tx_status", [txn_id, account_id], + **kwargs) + if 'error' in status: + print("tx error: tx not ready yet") + if not wait_for_success: + return None + time.sleep(3) + else: + return status['result'] diff --git a/pytest/lib/network.py b/pytest/lib/network.py new file mode 100644 index 000000000..978995190 --- /dev/null +++ b/pytest/lib/network.py @@ -0,0 +1,71 @@ +import subprocess, sys +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) +from configured_logger import logger + + +def _run_process(cmd): + process = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, err = process.communicate() + return (process.returncode, out, err) + + +def init_network_pillager(): + _run_process(["mkdir", "-p", "/sys/fs/cgroup/net_cls/block"]) + try: + with open("/sys/fs/cgroup/net_cls/block/net_cls.classid", 'w') as f: + f.write("42") + except IOError as e: + if e[0] == 13: + logger.critical( + "Failed to modify `/sys/fs/cgroup/net_cls/block/net_cls.classid`." + ) + logger.critical( + "Make sure the current user has access to it, e.g. by changing the owner:\n" + ) + logger.critical( + " chown . /sys/fs/cgroup/net_cls/block/net_cls.classid" + ) + sys.exit(1) + _run_process([ + "iptables", "-A", "OUTPUT", "-m", "cgroup", "--cgroup", "42", "-j", + "DROP" + ]) + + +def stop_network(pid): + with open('/sys/fs/cgroup/net_cls/block/tasks', 'w') as f: + f.write(str(pid)) + + +def resume_network(pid): + try: + with open('/sys/fs/cgroup/net_cls/tasks', 'w') as f: + f.write(str(pid)) + except ProcessLookupError: + # the process was killed in the meantime + pass + + +if __name__ == "__main__": + import time + init_network_pillager() + handle = subprocess.Popen(["ping", "8.8.8.8"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + logger.info(handle.pid) + time.sleep(3) + stop_network(handle.pid) + time.sleep(3) + resume_network(handle.pid) + time.sleep(3) + handle.kill() + out, err = handle.communicate() + logger.info("STDOUT (expect ~6 entries if all goes well):") + logger.info(out) + logger.info("STDERR (expect ~3 entries if all goes well):") + logger.info(err) diff --git a/pytest/lib/peer.py b/pytest/lib/peer.py new file mode 100644 index 000000000..71015e9a9 --- /dev/null +++ b/pytest/lib/peer.py @@ -0,0 +1,226 @@ +import asyncio +import concurrent +import hashlib +import struct + +import base58 + +from configured_logger import logger +from messages import schema +from messages.crypto import PublicKey, Signature +from messages.network import (EdgeInfo, GenesisId, Handshake, PeerChainInfoV2, + PeerMessage, RoutedMessage, PeerIdOrHash) +from serializer import BinarySerializer +from nacl.signing import SigningKey +from typing import Optional + +ED_PREFIX = "ed25519:" + + +class Connection: + + def __init__(self, reader: asyncio.StreamReader, + writer: asyncio.StreamWriter): + self.reader = reader + self.writer = writer + self.is_closed = False + + async def send(self, message): + raw_message = BinarySerializer(schema).serialize(message) + await self.send_raw(raw_message) + + async def send_raw(self, raw_message): + length = struct.pack('I', len(raw_message)) + self.writer.write(length) + self.writer.write(raw_message) + await self.writer.drain() + + # returns None on timeout + async def recv(self, expected=None): + while True: + response_raw = await self.recv_raw() + + # Connection was closed on the other side + if response_raw is None: + return None + # TODO(CP-85): when removing borsh support, fix this to use protobufs, + # (or preferably reimplement the test in rust). + try: + response = BinarySerializer(schema).deserialize( + response_raw, PeerMessage) + except IndexError: + # unparsable message, ignore. + continue + + if expected is None or response.enum == expected or ( + callable(expected) and expected(response)): + return response + + async def recv_raw(self): + length = await self.reader.read(4) + + if len(length) == 0: + self.is_closed = True + return None + else: + length = struct.unpack('I', length)[0] + response = b'' + + while len(response) < length: + response += await self.reader.read(length - len(response)) + if len(response) < length: + logger.info(f"Downloading message {len(response)}/{length}") + + return response + + async def close(self): + self.writer.close() + await self.writer.wait_closed() + + def do_send(self, message): + loop = asyncio.get_event_loop() + loop.create_task(self.send(message)) + + def do_send_raw(self, raw_message): + loop = asyncio.get_event_loop() + loop.create_task(self.send_raw(raw_message)) + + +async def connect(addr) -> Connection: + reader, writer = await asyncio.open_connection(*addr) + conn = Connection(reader, writer) + return conn + + +def create_handshake(my_key_pair_nacl, + their_pk_serialized, + listen_port, + version=0): + """ + Create handshake message but with placeholders in: + - version + - genesis_id.chain_id + - genesis_id.hash + - edge_info.signature + """ + handshake = Handshake() + handshake.version = version + handshake.oldest_supported_version = version + handshake.peer_id = PublicKey() + handshake.target_peer_id = PublicKey() + handshake.listen_port = listen_port + handshake.chain_info = PeerChainInfoV2() + handshake.edge_info = EdgeInfo() + + handshake.peer_id.keyType = 0 + handshake.peer_id.data = bytes(my_key_pair_nacl.verify_key) + + handshake.target_peer_id.keyType = 0 + handshake.target_peer_id.data = base58.b58decode( + their_pk_serialized[len(ED_PREFIX):]) + + handshake.chain_info.genesis_id = GenesisId() + handshake.chain_info.height = 0 + handshake.chain_info.tracked_shards = [] + handshake.chain_info.archival = False + + handshake.chain_info.genesis_id.chain_id = 'moo' + handshake.chain_info.genesis_id.hash = bytes([0] * 32) + + handshake.edge_info.nonce = 1 + handshake.edge_info.signature = Signature() + + handshake.edge_info.signature.keyType = 0 + handshake.edge_info.signature.data = bytes([0] * 64) + + peer_message = PeerMessage() + peer_message.enum = 'Handshake' + peer_message.Handshake = handshake + + return peer_message + + +def create_peer_request(): + peer_message = PeerMessage() + peer_message.enum = 'PeersRequest' + peer_message.PeersRequest = () + return peer_message + + +def sign_handshake(my_key_pair_nacl, handshake): + peer0 = handshake.peer_id + peer1 = handshake.target_peer_id + if peer1.data < peer0.data: + peer0, peer1 = peer1, peer0 + + arr = bytes( + bytearray([0]) + peer0.data + bytearray([0]) + peer1.data + + struct.pack('Q', handshake.edge_info.nonce)) + handshake.edge_info.signature.data = my_key_pair_nacl.sign( + hashlib.sha256(arr).digest()).signature + + +async def run_handshake(conn: Connection, + target_public_key: PublicKey, + key_pair: SigningKey, + listen_port=12345): + handshake = create_handshake(key_pair, target_public_key, listen_port) + + async def send_handshake(): + sign_handshake(key_pair, handshake.Handshake) + await conn.send(handshake) + # The peer might sent us an unsolicited message before replying to + # a successful handshake. This is because node is multi-threaded and + # peers are added to PeerManager before the reply is sent. Since we + # don’t care about those messages, ignore them and wait for some kind of + # Handshake reply. + return await conn.recv(lambda msg: msg.enum.startswith('Handshake')) + + response = await send_handshake() + + if response.enum == 'HandshakeFailure' and response.HandshakeFailure[ + 1].enum == 'ProtocolVersionMismatch': + pvm = response.HandshakeFailure[1].ProtocolVersionMismatch.version + handshake.Handshake.version = pvm + response = await send_handshake() + + if response.enum == 'HandshakeFailure' and response.HandshakeFailure[ + 1].enum == 'GenesisMismatch': + gm = response.HandshakeFailure[1].GenesisMismatch + handshake.Handshake.chain_info.genesis_id.chain_id = gm.chain_id + handshake.Handshake.chain_info.genesis_id.hash = gm.hash + response = await send_handshake() + + assert response.enum == 'Handshake', response.enum if response.enum != 'HandshakeFailure' else response.HandshakeFailure[ + 1].enum + + +def create_and_sign_routed_peer_message(routed_msg_body, target_node, + my_key_pair_nacl): + routed_msg = RoutedMessage() + routed_msg.target = PeerIdOrHash() + routed_msg.target.enum = 'PeerId' + routed_msg.target.PeerId = PublicKey() + routed_msg.target.PeerId.keyType = 0 + routed_msg.target.PeerId.data = base58.b58decode( + target_node.node_key.pk[len(ED_PREFIX):]) + routed_msg.author = PublicKey() + routed_msg.author.keyType = 0 + routed_msg.author.data = bytes(my_key_pair_nacl.verify_key) + routed_msg.ttl = 100 + routed_msg.body = routed_msg_body + routed_msg.signature = Signature() + routed_msg.signature.keyType = 0 + + routed_msg_arr = bytes( + bytearray([0, 0]) + routed_msg.target.PeerId.data + bytearray([0]) + + routed_msg.author.data + + BinarySerializer(schema).serialize(routed_msg.body)) + routed_msg_hash = hashlib.sha256(routed_msg_arr).digest() + routed_msg.signature.data = my_key_pair_nacl.sign(routed_msg_hash).signature + + peer_message = PeerMessage() + peer_message.enum = 'Routed' + peer_message.Routed = routed_msg + + return peer_message diff --git a/pytest/lib/populate.py b/pytest/lib/populate.py new file mode 100644 index 000000000..35c9cb83d --- /dev/null +++ b/pytest/lib/populate.py @@ -0,0 +1,24 @@ +import subprocess +from os.path import join +from shutil import copy2, rmtree + + +def genesis_populate(unc_root, additional_accounts, node_dir): + subprocess.check_call( + (join(unc_root, 'genesis-populate'), '--additional-accounts-num', + str(additional_accounts), '--home', node_dir)) + rmtree(join(node_dir, 'data'), ignore_errors=True) + + +def copy_genesis(node_dir_source, node_dir_target): + for file_name in ['genesis.json', 'genesis_roots', 'state_dump']: + source_file = join(node_dir_source, file_name) + target_file = join(node_dir_target, file_name) + copy2(source_file, target_file) + + +def genesis_populate_all(unc_root, additional_accounts, node_dirs): + genesis_populate(unc_root, additional_accounts, node_dirs[0]) + for node_dir in node_dirs[1:]: + copy_genesis(node_dirs[0], node_dir) + rmtree(join(node_dir, 'data'), ignore_errors=True) diff --git a/pytest/lib/proxy.py b/pytest/lib/proxy.py new file mode 100644 index 000000000..7e81571e2 --- /dev/null +++ b/pytest/lib/proxy.py @@ -0,0 +1,410 @@ +# A library providing a node wrapper that intercepts all the incoming messages and +# outgoing responses (but not the initiated outgoing messages and incoming responses) +# from a node, and calls a handler on them. The handler can then decide to drop the +# message, deliver the message, or change the message. +# +# Usage: +# 1. Create a proxy = NodesProxy(handler). +# handler takes two arguments, the message (deserialized), the ordinal of the sending +# peer and the ordinal of the receiving peer. The ordinal is derived from the port, +# and assumes no tampering with ports was done +# 2. Call proxy.proxify_node(node) before the node is started. +# proxify_node will start a process that receives the connections on the port 100 +# larger than the original node port. It will change the port of the `Node` object +# passed to it. +# +# Note that since the handler is called on incoming messages and outgoing responses, if +# all the nodes in the cluster are proxified, each message exchanged in the cluster will +# have the handler called exactly once. +# +# The proxy will register an atexit function that will gracefully shut down all the +# processes, and fail the test if any of the processes failed. +# +# `start_cluster` accepts a handler as its last argument, and automatically proxifies +# all the nodes if the parameter is not None +# +# See `tests/sanity/nodes_proxy.py` for an example usage + +import asyncio +import atexit +import functools +import multiprocessing +import random +import select +import socket +import struct +import time +import logging + +from configured_logger import logger +from messages import schema +from messages.crypto import PublicKey, Signature +from messages.network import PeerIdOrHash, PeerMessage +from serializer import BinarySerializer + +MSG_TIMEOUT = 10 +_MY_PORT = [None] + +logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO) + + +def proxy_cleanup(proxy): + logging.debug(f'Cleaning process for {_MY_PORT}') + proxy.global_stopped.value = 1 + for p in proxy.ps: + p.terminate() + logging.debug(f'Finish cleanup for {_MY_PORT}') + if proxy.error.value != 0: + assert False, "One of the proxy processes failed, search for the stacktraces above" + + +def port_holder_to_node_ord(holder): + return None if holder[0] is None else (holder[0] - 24477) % 100 + + +class ProxyHandler: + + def __init__(self, ordinal): + self.ordinal = ordinal + self.recv_from_map = {} + self.send_to_map = {} + self.loop = asyncio.get_event_loop() + + @property + def me(self): + return self.ordinal + + def other(self, ordinal_a, ordinal_b): + assert self.me == ordinal_a or self.me == ordinal_b + if ordinal_a == self.me: + return ordinal_b + else: + return ordinal_a + + async def _handle(self, raw_message, *, writer, sender_port_holder, + receiver_port_holder, ordinal_to_writer): + sender_ordinal = port_holder_to_node_ord(sender_port_holder) + receiver_ordinal = port_holder_to_node_ord(receiver_port_holder) + try: + # TODO(CP-85): when removing borsh support, fix this to use protobufs, + # (or preferably reimplement the test in rust). + try: + message = BinarySerializer(schema).deserialize( + raw_message, PeerMessage) + except IndexError: + # unparsable message, ignore. + logging.warn( + f"Warning: could not proxy message {raw_message.hex()}") + return + assert BinarySerializer(schema).serialize(message) == raw_message + + if message.enum == 'Handshake': + message.Handshake.listen_port += 100 + if sender_port_holder[0] is None: + sender_port_holder[0] = message.Handshake.listen_port + + other_ordinal = self.other(sender_ordinal, receiver_ordinal) + + if other_ordinal is not None and not other_ordinal in ordinal_to_writer: + ordinal_to_writer[other_ordinal] = writer + + decision = await self.handle(message, sender_ordinal, + receiver_ordinal) + + if decision is True and message.enum == 'Handshake': + decision = message + + if not isinstance(decision, bool): + decision = BinarySerializer(schema).serialize(decision) + + return decision + except: + # TODO: Remove this + if raw_message[0] == 13: + # raw_message[0] == 13 is RoutedMessage. Skip leading fields to get to the RoutedMessageBody + ser = BinarySerializer(schema) + ser.array = bytearray(raw_message) + ser.offset = 1 + ser.deserialize_field(PeerIdOrHash) + ser.deserialize_field(PublicKey) + ser.deserialize_field(Signature) + ser.deserialize_field('u8') + + # The next byte is the variant ordinal of the `RoutedMessageBody`. + # Skip if it's the ordinal of a variant for which the schema is not ported yet + if raw_message[ser.offset] in [3, 4, 5, 7]: + # Allow the handler determine if the message should be passed even when it couldn't be deserialized + return await self.handle(None, sender_ordinal, + receiver_ordinal) is not False + logger.info(f"ERROR 13 {int(raw_message[ser.offset])}") + + else: + logger.info(f"ERROR {int(raw_message[0])}") + + raise + + return True + + def get_writer(self, to, fr=None): + if to == self.ordinal: + if fr is None and len(self.recv_from_map) > 0: + fr = next(iter(self.recv_from_map.keys())) + return self.recv_from_map.get(fr) + else: + return self.send_to_map.get(to) + + async def send_binary(self, raw_message, to, fr=None): + writer = self.get_writer(to, fr) + if writer is None: + logger.info( + f"Writer not known: to={to}, fr={fr}, send={self.send_to_map.keys()}, recv={self.recv_from_map.keys()}" + ) + else: + writer.write(struct.pack('I', len(raw_message))) + writer.write(raw_message) + await writer.drain() + + async def send_message(self, message, to, fr=None): + raw_message = BinarySerializer(schema).serialize(message) + await self.send_binary(raw_message, to, fr) + + def do_send_binary(self, raw_message, to, fr=None): + self.loop.create_task(self.send_binary(raw_message, to, fr)) + + def do_send_message(self, msg, to, fr=None): + self.loop.create_task(self.send_message(msg, to, fr)) + + async def handle(self, msg, fr, to): + return True + + +class NodesProxy: + + def __init__(self, handler): + assert handler is not None + self.handler = handler + self.global_stopped = multiprocessing.Value('i', 0) + self.error = multiprocessing.Value('i', 0) + self.ps = [] + atexit.register(proxy_cleanup, self) + + def proxify_node(self, node): + proxify_node(node, self.ps, self.handler, self.global_stopped, + self.error, self) + + +async def stop_server(server): + server.close() + await server.wait_closed() + + +def check_finish(server, global_stopped, local_stopped, error): + loop = asyncio.get_running_loop() + if 0 == global_stopped.value and 0 >= local_stopped.value and 0 == error.value: + loop.call_later(1, check_finish, server, global_stopped, local_stopped, + error) + else: + logging.debug( + f"Stopping server. port={_MY_PORT}, global_stopped={global_stopped.value}, local_stopped={local_stopped.value}, error={error.value}" + ) + server.close() + local_stopped.value = 2 + + +async def _read_exact(reader, length, *, allow_eof=False): + """Reads exactly `length` bytes from reader.""" + data = await reader.read(length) + if data or not allow_eof: + while len(data) < length: + data += await reader.read(length - len(data)) + return data + + +async def bridge(reader, writer, handler_fn, global_stopped, local_stopped, + bridge_stopped, error): + bridge_id = random.randint(0, 10**10) + logging.debug(f"Start bridge. port={_MY_PORT} bridge_id={bridge_id}") + + try: + while 0 == global_stopped.value and 0 >= local_stopped.value and 0 == error.value and 0 == bridge_stopped[ + 0]: + header = await _read_exact(reader, 4, allow_eof=True) + if not header: + logging.debug( + f"Endpoint closed (Reader). port={_MY_PORT} bridge_id={bridge_id}" + ) + break + + raw_message_len = struct.unpack('I', header)[0] + raw_message = await _read_exact(reader, raw_message_len) + + logging.debug( + f"Message size={len(raw_message)} port={_MY_PORT} bridge_id={bridge_id}" + ) + decision = await handler_fn(raw_message) + + if isinstance(decision, bytes): + raw_message = decision + decision = True + + if decision: + writer.write(struct.pack('I', len(raw_message))) + writer.write(raw_message) + await writer.drain() + + bridge_stopped[0] = 1 + writer.close() + await writer.wait_closed() + + logging.debug( + f"Gracefully close bridge. port={_MY_PORT} bridge_id={bridge_id}") + except (ConnectionResetError, BrokenPipeError): + bridge_stopped[0] = 1 + try: + writer.close() + await writer.wait_closed() + except: + pass + logging.debug( + f"Endpoint closed (Writer). port={_MY_PORT} bridge_id={bridge_id}") + + +async def handle_connection(outer_reader, outer_writer, inner_port, outer_port, + handler, global_stopped, local_stopped, error): + connection_id = random.randint(0, 10**10) + logging.debug( + f"New connection. port={_MY_PORT} connection_id={connection_id}") + try: + inner_reader, inner_writer = await asyncio.open_connection( + '127.0.0.1', inner_port) + + my_port = [outer_port] + peer_port_holder = [None] + bridge_stopped = [0] + + inner_to_outer = bridge( + inner_reader, outer_writer, + functools.partial( + handler._handle, + writer=inner_writer, + sender_port_holder=my_port, + receiver_port_holder=peer_port_holder, + ordinal_to_writer=handler.recv_from_map, + ), global_stopped, local_stopped, bridge_stopped, error) + + outer_to_inner = bridge( + outer_reader, inner_writer, + functools.partial( + handler._handle, + writer=outer_writer, + sender_port_holder=peer_port_holder, + receiver_port_holder=my_port, + ordinal_to_writer=handler.send_to_map, + ), global_stopped, local_stopped, bridge_stopped, error) + + await asyncio.gather(inner_to_outer, outer_to_inner) + logging.debug( + f"End of connection. port={_MY_PORT} peer_port={peer_port_holder} connection_id={connection_id}" + ) + except asyncio.CancelledError: + logging.debug( + f"Cancelled Error (handle_connection). port={_MY_PORT} connection_id={connection_id} global_stopped={global_stopped.value} local_stopped={local_stopped.value} error={error.value}" + ) + if local_stopped.value <= 0: + global_stopped.value = 1 + except ConnectionRefusedError: + logging.debug( + f"ConnectionRefusedError (handle_connection). port={_MY_PORT} connection_id={connection_id} global_stopped={global_stopped.value} local_stopped={local_stopped.value} error={error.value}" + ) + except: + logging.debug( + f"Other Error (handle_connection). port={_MY_PORT} connection_id={connection_id} global_stopped={global_stopped.value} local_stopped={local_stopped.value} error={error.value}" + ) + error.value = 1 + raise + + +async def listener(inner_port, outer_port, handler_ctr, global_stopped, + local_stopped, error): + logging.debug(f"Starting listener... port={_MY_PORT}") + try: + handler = handler_ctr(port_holder_to_node_ord([outer_port])) + + async def start_connection(reader, writer): + await handle_connection(reader, writer, inner_port, outer_port, + handler, global_stopped, local_stopped, + error) + + attempts = 3 + + # Possibly need to wait 1 second to start listener if node was killed and previous listener is on yet. + while attempts > 0: + try: + server = await asyncio.start_server(start_connection, + '127.0.0.1', outer_port) + logging.debug(f"Listener started. port={_MY_PORT}") + break + except OSError: + attempts -= 1 + logging.debug( + f"Fail starting listener. Remaining attempts: {attempts} port={_MY_PORT}" + ) + if attempts == 0: + raise + await asyncio.sleep(1) + + check_finish(server, global_stopped, local_stopped, error) + local_stopped.value = 0 + + async with server: + await server.serve_forever() + except asyncio.CancelledError: + logging.debug( + f"Cancelled Error (listener). port={_MY_PORT} global_stopped={global_stopped.value} local_stopped={local_stopped.value} error={error.value}" + ) + if local_stopped.value <= 0: + global_stopped.value = 1 + except: + logging.debug( + f"Other Error (listener). port={_MY_PORT} global_stopped={global_stopped.value} local_stopped={local_stopped.value} error={error.value}" + ) + error.value = 1 + raise + + +def start_server(inner_port, outer_port, handler_ctr, global_stopped, + local_stopped, error): + _MY_PORT[0] = outer_port + asyncio.run( + listener(inner_port, outer_port, handler_ctr, global_stopped, + local_stopped, error)) + + +def proxify_node(node, ps, handler, global_stopped, error, proxy): + inner_port = node.port + outer_port = inner_port + 100 + + def start_proxy(): + # local_stopped denotes the current of state of the proxy: + # -1: The proxy hasn't started yet + # 0: The proxy is running + # 1: The proxy is running but should be closed soon + # 2: The proxy is closed + local_stopped = multiprocessing.Value('i', -1) + p = multiprocessing.Process(target=start_server, + args=(inner_port, outer_port, handler, + global_stopped, local_stopped, error)) + p.start() + ps.append(p) + for attempt in range(3): + if local_stopped.value == 0: + break + time.sleep(1) + else: + error.value = 1 + assert False, "The proxy failed to start after 3 seconds" + return local_stopped + + node.port = outer_port + node._start_proxy = start_proxy + node.proxy = proxy diff --git a/pytest/lib/proxy_instances.py b/pytest/lib/proxy_instances.py new file mode 100644 index 000000000..6609caa43 --- /dev/null +++ b/pytest/lib/proxy_instances.py @@ -0,0 +1,42 @@ +import logging, multiprocessing, random +from proxy import ProxyHandler, NodesProxy + + +class RejectListHandler(ProxyHandler): + + def __init__(self, reject_list, drop_probability, ordinal): + super().__init__(ordinal) + self.reject_list = reject_list + self.drop_probability = drop_probability + + async def handle(self, msg, fr, to): + msg_type = msg.enum if msg.enum != 'Routed' else msg.Routed.body.enum + + if (self.drop_probability > 0 and 'Handshake' not in msg_type and + random.uniform(0, 1) < self.drop_probability): + logging.info( + f'NODE {self.ordinal} dropping message {msg_type} from {fr} to {to}' + ) + return False + + if fr in self.reject_list or to in self.reject_list: + logging.info( + f'NODE {self.ordinal} blocking message {msg_type} from {fr} to {to}' + ) + return False + else: + return True + + +class RejectListProxy(NodesProxy): + + def __init__(self, reject_list, drop_probability): + self.reject_list = reject_list + self.drop_probability = drop_probability + handler = lambda ordinal: RejectListHandler(reject_list, + drop_probability, ordinal) + super().__init__(handler) + + @staticmethod + def create_reject_list(size): + return multiprocessing.Array('i', [-1 for _ in range(size)]) diff --git a/pytest/lib/resharding_lib.py b/pytest/lib/resharding_lib.py new file mode 100644 index 000000000..6fa23b90f --- /dev/null +++ b/pytest/lib/resharding_lib.py @@ -0,0 +1,165 @@ +# A library with the common constants and functions for testing resharding. + +# TODO(resharding) The resharding V2 is now stabilized so the V1 code is no +# longer exercised and can be removed. +V1_PROTOCOL_VERSION = 48 +V2_PROTOCOL_VERSION = 64 + +V0_SHARD_LAYOUT = { + "V0": { + "num_shards": 1, + "version": 0 + }, +} +V1_SHARD_LAYOUT = { + "V1": { + "boundary_accounts": [ + "aurora", "aurora-0", "kkuuue2akv_1630967379.near" + ], + "shards_split_map": [[0, 1, 2, 3]], + "to_parent_shard_map": [0, 0, 0, 0], + "version": 1 + } +} + + +def get_genesis_config_changes(epoch_length, + binary_protocol_version, + logger=None): + genesis_config_changes = [ + ["epoch_length", epoch_length], + ] + append_shard_layout_config_changes( + genesis_config_changes, + binary_protocol_version, + logger, + ) + return genesis_config_changes + + +# Append the genesis config changes that are required for testing resharding. +# This method will set the protocol version, shard layout and a few other +# configs so that it matches the protocol configuration as of right before the +# protocol version of the binary under test. +def append_shard_layout_config_changes( + genesis_config_changes, + binary_protocol_version, + logger=None, +): + genesis_config_changes.append(["use_production_config", True]) + + if binary_protocol_version >= V2_PROTOCOL_VERSION: + if logger: + logger.info("Testing migration from V1 to V2.") + # Set the initial protocol version to a version just before V2. + genesis_config_changes.append([ + "protocol_version", + V2_PROTOCOL_VERSION - 1, + ]) + genesis_config_changes.append([ + "shard_layout", + V1_SHARD_LAYOUT, + ]) + genesis_config_changes.append([ + "num_block_producer_seats_per_shard", + [1, 1, 1, 1], + ]) + genesis_config_changes.append([ + "avg_hidden_validator_seats_per_shard", + [0, 0, 0, 0], + ]) + return + + if binary_protocol_version >= V1_PROTOCOL_VERSION: + if logger: + logger.info("Testing migration from V0 to V1.") + # Set the initial protocol version to a version just before V1. + genesis_config_changes.append([ + "protocol_version", + V1_PROTOCOL_VERSION - 1, + ]) + genesis_config_changes.append([ + "shard_layout", + V0_SHARD_LAYOUT, + ]) + genesis_config_changes.append([ + "num_block_producer_seats_per_shard", + [100], + ]) + genesis_config_changes.append([ + "avg_hidden_validator_seats_per_shard", + [0], + ]) + return + + assert False + + +def get_genesis_shard_layout_version(binary_protocol_version): + if binary_protocol_version >= V2_PROTOCOL_VERSION: + return 1 + if binary_protocol_version >= V1_PROTOCOL_VERSION: + return 0 + + assert False + + +def get_target_shard_layout_version(binary_protocol_version): + if binary_protocol_version >= V2_PROTOCOL_VERSION: + return 2 + if binary_protocol_version >= V1_PROTOCOL_VERSION: + return 1 + + assert False + + +def get_genesis_num_shards(binary_protocol_version): + if binary_protocol_version >= V2_PROTOCOL_VERSION: + return 4 + if binary_protocol_version >= V1_PROTOCOL_VERSION: + return 1 + + assert False + + +def get_target_num_shards(binary_protocol_version): + if binary_protocol_version >= V2_PROTOCOL_VERSION: + return 5 + if binary_protocol_version >= V1_PROTOCOL_VERSION: + return 4 + + assert False + + +def get_epoch_offset(binary_protocol_version): + if binary_protocol_version >= V2_PROTOCOL_VERSION: + return 1 + if binary_protocol_version >= V1_PROTOCOL_VERSION: + return 0 + + assert False + + +def get_client_config_changes(num_nodes, initial_delay=None): + single = { + "tracked_shards": [0], + "resharding_config": { + "batch_size": 1000000, + # don't throttle resharding + "batch_delay": { + "secs": 0, + "nanos": 0, + }, + # retry often to start resharding as fast as possible + "retry_delay": { + "secs": 0, + "nanos": 100_000_000 + } + } + } + if initial_delay is not None: + single["resharding_config"]["initial_delay"] = { + "secs": initial_delay, + "nanos": 0 + } + return {i: single for i in range(num_nodes)} diff --git a/pytest/lib/serializer.py b/pytest/lib/serializer.py new file mode 100644 index 000000000..60808ba0c --- /dev/null +++ b/pytest/lib/serializer.py @@ -0,0 +1,168 @@ +class BinarySerializer: + + def __init__(self, schema): + self.array = bytearray() + self.schema = schema + + def read_bytes(self, n): + assert n + self.offset <= len( + self.array + ), f'n: {n} offset: {self.offset}, length: {len(self.array)}' + ret = self.array[self.offset:self.offset + n] + self.offset += n + return ret + + def serialize_num(self, value, n_bytes): + assert value >= 0 + for i in range(n_bytes): + self.array.append(value & 255) + value //= 256 + assert value == 0 + + def deserialize_num(self, n_bytes): + value = 0 + bytes_ = self.read_bytes(n_bytes) + for b in bytes_[::-1]: + value = value * 256 + b + return value + + def serialize_field(self, value, fieldType): + if type(fieldType) == tuple: + if len(fieldType) == 0: + pass + else: + assert len(value) == len(fieldType) + for (v, t) in zip(value, fieldType): + self.serialize_field(v, t) + elif type(fieldType) == str: + if fieldType == 'bool': + assert isinstance(value, bool), str(type(value)) + self.serialize_num(int(value), 1) + elif fieldType[0] == 'u': + self.serialize_num(value, int(fieldType[1:]) // 8) + elif fieldType == 'string': + b = value.encode('utf8') + self.serialize_num(len(b), 4) + self.array += b + else: + assert False, fieldType + elif type(fieldType) == list: + assert len(fieldType) == 1 + if type(fieldType[0]) == int: + assert type(value) == bytes + assert len(value) == fieldType[0], "len(%s) = %s != %s" % ( + value, len(value), fieldType[0]) + self.array += bytearray(value) + else: + self.serialize_num(len(value), 4) + for el in value: + self.serialize_field(el, fieldType[0]) + elif type(fieldType) == dict: + assert fieldType['kind'] == 'option' + if value is None: + self.serialize_num(0, 1) + else: + self.serialize_num(1, 1) + self.serialize_field(value, fieldType['type']) + elif type(fieldType) == type: + assert type(value) == fieldType, "%s != type(%s)" % (fieldType, + value) + self.serialize_struct(value) + else: + assert False, type(fieldType) + + def deserialize_field(self, fieldType): + if type(fieldType) == tuple: + if len(fieldType) == 0: + return None + else: + return tuple(self.deserialize_field(t) for t in fieldType) + + elif type(fieldType) == str: + if fieldType == 'bool': + value = self.deserialize_num(1) + assert 0 <= value <= 1, f"Fail to deserialize bool: {value}" + return bool(value) + elif fieldType[0] == 'u': + return self.deserialize_num(int(fieldType[1:]) // 8) + elif fieldType == 'string': + len_ = self.deserialize_num(4) + return self.read_bytes(len_).decode('utf8') + else: + assert False, fieldType + elif type(fieldType) == list: + assert len(fieldType) == 1 + if type(fieldType[0]) == int: + return bytes(self.read_bytes(fieldType[0])) + else: + len_ = self.deserialize_num(4) + return [ + self.deserialize_field(fieldType[0]) for _ in range(len_) + ] + elif type(fieldType) == dict: + assert fieldType['kind'] == 'option' + is_none = self.deserialize_num(1) == 0 + if is_none: + return None + else: + return self.deserialize_field(fieldType['type']) + elif type(fieldType) == type: + return self.deserialize_struct(fieldType) + else: + assert False, type(fieldType) + + def serialize_struct(self, obj): + structSchema = self.schema[type(obj)] + if structSchema['kind'] == 'struct': + for fieldName, fieldType in structSchema['fields']: + try: + self.serialize_field(getattr(obj, fieldName), fieldType) + except AssertionError as exc: + raise AssertionError(f"Error in field {fieldName}") from exc + elif structSchema['kind'] == 'enum': + name = getattr(obj, structSchema['field']) + for idx, (fieldName, + fieldType) in enumerate(structSchema['values']): + if fieldName == name: + self.serialize_num(idx, 1) + try: + self.serialize_field(getattr(obj, fieldName), fieldType) + except AssertionError as exc: + raise AssertionError( + f"Error in field {fieldName}") from exc + break + else: + assert False, name + else: + assert False, structSchema + + def deserialize_struct(self, type_): + structSchema = self.schema[type_] + if structSchema['kind'] == 'struct': + ret = type_() + for fieldName, fieldType in structSchema['fields']: + setattr(ret, fieldName, self.deserialize_field(fieldType)) + return ret + elif structSchema['kind'] == 'enum': + ret = type_() + value_ord = self.deserialize_num(1) + value_schema = structSchema['values'][value_ord] + setattr(ret, structSchema['field'], value_schema[0]) + setattr(ret, value_schema[0], + self.deserialize_field(value_schema[1])) + + return ret + else: + assert False, structSchema + + def serialize(self, obj): + self.serialize_struct(obj) + return bytes(self.array) + + def deserialize(self, bytes_, type_): + self.array = bytearray(bytes_) + self.offset = 0 + ret = self.deserialize_field(type_) + assert self.offset == len(bytes_), "%s != %s" % (self.offset, + len(bytes_)) + return ret diff --git a/pytest/lib/state_sync_lib.py b/pytest/lib/state_sync_lib.py new file mode 100644 index 000000000..bf100fee7 --- /dev/null +++ b/pytest/lib/state_sync_lib.py @@ -0,0 +1,91 @@ +import pathlib +import tempfile + + +def approximate_epoch_height(block_height, epoch_length): + if block_height == 0: + return 0 + if block_height <= epoch_length: + # According to the protocol specifications, there are two epochs with height 1. + return "1*" + return int((block_height - 1) / epoch_length) + + +def get_state_sync_configs_pair(): + state_parts_dir = str(pathlib.Path(tempfile.gettempdir()) / "state_parts") + + config_dump = { + "state_sync": { + "dump": { + "location": { + "Filesystem": { + "root_dir": state_parts_dir + } + }, + "iteration_delay": { + "secs": 0, + "nanos": 100000000 + }, + } + }, + "store.state_snapshot_enabled": True, + "tracked_shards": [0], # Track all shards + } + config_sync = { + "consensus.state_sync_timeout": { + "secs": 0, + "nanos": 500000000 + }, + "state_sync": { + "sync": { + "ExternalStorage": { + "location": { + "Filesystem": { + "root_dir": state_parts_dir + } + } + } + } + }, + "state_sync_enabled": True, + "tracked_shards": [0], # Track all shards + } + + return (config_dump, config_sync) + + +def get_state_sync_config_combined(): + state_parts_dir = str(pathlib.Path(tempfile.gettempdir()) / "state_parts") + config = { + "consensus.state_sync_timeout": { + "secs": 0, + "nanos": 500000000 + }, + "state_sync": { + "dump": { + "location": { + "Filesystem": { + "root_dir": state_parts_dir + } + }, + "iteration_delay": { + "secs": 0, + "nanos": 100000000 + }, + }, + "sync": { + "ExternalStorage": { + "location": { + "Filesystem": { + "root_dir": state_parts_dir + } + } + } + } + }, + "state_sync_enabled": True, + "store.state_snapshot_enabled": True, + "tracked_shards": [0], # Track all shards + } + + return config diff --git a/pytest/lib/transaction.py b/pytest/lib/transaction.py new file mode 100644 index 000000000..873677bcc --- /dev/null +++ b/pytest/lib/transaction.py @@ -0,0 +1,275 @@ +from serializer import BinarySerializer +import hashlib +from ed25519 import SigningKey +import base58 + +from messages.tx import * +from messages.crypto import * +from messages.bridge import * + +schema = dict(tx_schema + crypto_schema + bridge_schema) + + +def compute_tx_hash(receiverId, nonce, actions, blockHash, accountId, pk): + tx = Transaction() + tx.signerId = accountId + tx.publicKey = PublicKey() + tx.publicKey.keyType = 0 + tx.publicKey.data = pk + tx.nonce = nonce + tx.receiverId = receiverId + tx.actions = actions + tx.blockHash = blockHash + + msg = BinarySerializer(schema).serialize(tx) + hash_ = hashlib.sha256(msg).digest() + + return tx, hash_ + + +def sign_and_serialize_transaction(receiverId, nonce, actions, blockHash, + accountId, pk, sk): + tx, hash_ = compute_tx_hash(receiverId, nonce, actions, blockHash, + accountId, pk) + + signature = Signature() + signature.keyType = 0 + signature.data = SigningKey(sk).sign(hash_) + + signedTx = SignedTransaction() + signedTx.transaction = tx + signedTx.signature = signature + + return BinarySerializer(schema).serialize(signedTx) + + +def compute_delegated_action_hash(senderId, receiverId, actions, nonce, + maxBlockHeight, publicKey): + delegateAction = DelegateAction() + delegateAction.senderId = senderId + delegateAction.receiverId = receiverId + delegateAction.actions = actions + delegateAction.nonce = nonce + delegateAction.maxBlockHeight = maxBlockHeight + delegateAction.publicKey = PublicKey() + delegateAction.publicKey.keyType = 0 + delegateAction.publicKey.data = publicKey + signableMessageDiscriminant = 2**30 + 366 + serializer = BinarySerializer(schema) + serializer.serialize_num(signableMessageDiscriminant, 4) + msg = serializer.serialize(delegateAction) + hash_ = hashlib.sha256(msg).digest() + + return delegateAction, hash_ + + +# Used by meta-transactions. +# Creates a SignedDelegate that is later put into the DelegateAction by relayer. +def create_signed_delegated_action(senderId, receiverId, actions, nonce, + maxBlockHeight, publicKey, sk): + delegated_action, hash_ = compute_delegated_action_hash( + senderId, receiverId, actions, nonce, maxBlockHeight, publicKey) + + signature = Signature() + signature.keyType = 0 + signature.data = SigningKey(sk).sign(hash_) + + signedDA = SignedDelegate() + signedDA.delegateAction = delegated_action + signedDA.signature = signature + return signedDA + + +def create_create_account_action(): + createAccount = CreateAccount() + action = Action() + action.enum = 'createAccount' + action.createAccount = createAccount + return action + + +def create_full_access_key_action(pk): + permission = AccessKeyPermission() + permission.enum = 'fullAccess' + permission.fullAccess = FullAccessPermission() + accessKey = AccessKey() + accessKey.nonce = 0 + accessKey.permission = permission + publicKey = PublicKey() + publicKey.keyType = 0 + publicKey.data = pk + addKey = AddKey() + addKey.accessKey = accessKey + addKey.publicKey = publicKey + action = Action() + action.enum = 'addKey' + action.addKey = addKey + return action + + +def create_delete_access_key_action(pk): + publicKey = PublicKey() + publicKey.keyType = 0 + publicKey.data = pk + deleteKey = DeleteKey() + deleteKey.publicKey = publicKey + action = Action() + action.enum = 'deleteKey' + action.deleteKey = deleteKey + return action + + +def create_payment_action(amount): + transfer = Transfer() + transfer.deposit = amount + action = Action() + action.enum = 'transfer' + action.transfer = transfer + return action + + +def create_staking_action(amount, pk): + stake = Stake() + stake.stake = amount + stake.publicKey = PublicKey() + stake.publicKey.keyType = 0 + stake.publicKey.data = pk + action = Action() + action.enum = 'stake' + action.stake = stake + return action + + +def create_deploy_contract_action(code): + deployContract = DeployContract() + deployContract.code = code + action = Action() + action.enum = 'deployContract' + action.deployContract = deployContract + return action + + +def create_function_call_action(methodName, args, gas, deposit): + functionCall = FunctionCall() + functionCall.methodName = methodName + functionCall.args = args + functionCall.gas = gas + functionCall.deposit = deposit + action = Action() + action.enum = 'functionCall' + action.functionCall = functionCall + return action + + +def create_delete_account_action(beneficiary): + deleteAccount = DeleteAccount() + deleteAccount.beneficiaryId = beneficiary + action = Action() + action.enum = 'deleteAccount' + action.deleteAccount = deleteAccount + return action + + +def create_delegate_action(signedDelegate): + action = Action() + action.enum = 'delegate' + action.delegate = signedDelegate + return action + + +def sign_delegate_action(signedDelegate, signer_key, contract_id, nonce, + blockHash): + action = create_delegate_action(signedDelegate) + return sign_and_serialize_transaction(contract_id, nonce, [action], + blockHash, signer_key.account_id, + signer_key.decoded_pk(), + signer_key.decoded_sk()) + + +def sign_create_account_tx(creator_key, new_account_id, nonce, block_hash): + action = create_create_account_action() + return sign_and_serialize_transaction(new_account_id, nonce, [action], + block_hash, creator_key.account_id, + creator_key.decoded_pk(), + creator_key.decoded_sk()) + + +def sign_create_account_with_full_access_key_and_balance_tx( + creator_key, new_account_id, new_key, balance, nonce, block_hash): + create_account_action = create_create_account_action() + full_access_key_action = create_full_access_key_action(new_key.decoded_pk()) + payment_action = create_payment_action(balance) + actions = [create_account_action, full_access_key_action, payment_action] + return sign_and_serialize_transaction(new_account_id, nonce, actions, + block_hash, creator_key.account_id, + creator_key.decoded_pk(), + creator_key.decoded_sk()) + + +def sign_delete_access_key_tx(signer_key, target_account_id, key_for_deletion, + nonce, block_hash): + action = create_delete_access_key_action(key_for_deletion.decoded_pk()) + return sign_and_serialize_transaction(target_account_id, nonce, [action], + block_hash, signer_key.account_id, + signer_key.decoded_pk(), + signer_key.decoded_sk()) + + +def sign_payment_tx(key, to, amount, nonce, blockHash): + action = create_payment_action(amount) + return sign_and_serialize_transaction(to, nonce, [action], blockHash, + key.account_id, key.decoded_pk(), + key.decoded_sk()) + + +def sign_payment_tx_and_get_hash(key, to, amount, nonce, block_hash): + action = create_payment_action(amount) + _, hash_bytes = compute_tx_hash(to, nonce, [action], block_hash, + key.account_id, key.decoded_pk()) + signed_tx = sign_payment_tx(key, to, amount, nonce, block_hash) + return signed_tx, base58.b58encode(hash_bytes).decode('utf8') + + +def sign_staking_tx(signer_key, validator_key, amount, nonce, blockHash): + action = create_staking_action(amount, validator_key.decoded_pk()) + return sign_and_serialize_transaction(signer_key.account_id, nonce, + [action], blockHash, + signer_key.account_id, + signer_key.decoded_pk(), + signer_key.decoded_sk()) + + +def sign_staking_tx_and_get_hash(signer_key, validator_key, amount, nonce, + block_hash): + action = create_staking_action(amount, validator_key.decoded_pk()) + _, hash_bytes = compute_tx_hash(signer_key.account_id, nonce, [action], + block_hash, signer_key.account_id, + signer_key.decoded_pk()) + signed_tx = sign_staking_tx(signer_key, validator_key, amount, nonce, + block_hash) + return signed_tx, base58.b58encode(hash_bytes).decode('utf8') + + +def sign_deploy_contract_tx(signer_key, code, nonce, blockHash): + action = create_deploy_contract_action(code) + return sign_and_serialize_transaction(signer_key.account_id, nonce, + [action], blockHash, + signer_key.account_id, + signer_key.decoded_pk(), + signer_key.decoded_sk()) + + +def sign_function_call_tx(signer_key, contract_id, methodName, args, gas, + deposit, nonce, blockHash): + action = create_function_call_action(methodName, args, gas, deposit) + return sign_and_serialize_transaction(contract_id, nonce, [action], + blockHash, signer_key.account_id, + signer_key.decoded_pk(), + signer_key.decoded_sk()) + + +def sign_delete_account_tx(key, to, beneficiary, nonce, block_hash): + action = create_delete_account_action(beneficiary) + return sign_and_serialize_transaction(to, nonce, [action], block_hash, + key.account_id, key.decoded_pk(), + key.decoded_sk()) diff --git a/pytest/lib/utils.py b/pytest/lib/utils.py new file mode 100644 index 000000000..1009c9abc --- /dev/null +++ b/pytest/lib/utils.py @@ -0,0 +1,525 @@ +import base58 +import hashlib +import json +import os +import pathlib +import random +import re +import shutil +import sys +import tempfile +import time +import typing +import requests +from prometheus_client.parser import text_string_to_metric_families +from retrying import retry +from rc import gcloud + +import cluster +from configured_logger import logger +from transaction import sign_payment_tx + + +class TxContext: + + def __init__(self, act_to_val, nodes): + self.next_nonce = 2 + self.num_nodes = len(nodes) + self.nodes = nodes + self.act_to_val = act_to_val + self.expected_balances = self.get_balances() + assert len(act_to_val) == self.num_nodes + assert self.num_nodes >= 2 + + @retry(stop_max_attempt_number=10, wait_exponential_multiplier=1.2) + def get_balance(self, whose): + r = self.nodes[self.act_to_val[whose]].get_account("test%s" % whose) + assert 'result' in r, r + return int(r['result']['amount']) + int(r['result']['locked']) + + def get_balances(self): + return [self.get_balance(i) for i in range(self.num_nodes)] + + def send_moar_txs(self, last_block_hash, num, use_routing): + last_balances = [x for x in self.expected_balances] + for i in range(num): + while True: + from_ = random.randint(0, self.num_nodes - 1) + if self.nodes[from_] is not None: + break + to = random.randint(0, self.num_nodes - 2) + if to >= from_: + to += 1 + amt = random.randint(0, 500) + if self.expected_balances[from_] >= amt: + logger.info("Sending a tx from %s to %s for %s" % + (from_, to, amt)) + tx = sign_payment_tx( + self.nodes[from_].signer_key, 'test%s' % to, amt, + self.next_nonce, + base58.b58decode(last_block_hash.encode('utf8'))) + if use_routing: + self.nodes[0].send_tx(tx) + else: + self.nodes[self.act_to_val[from_]].send_tx(tx) + self.expected_balances[from_] -= amt + self.expected_balances[to] += amt + self.next_nonce += 1 + + +class LogTracker: + """Opens up a log file, scrolls to the end and allows to check for patterns. + + The tracker works only on local nodes. + + PLEASE AVOID USING THE TRACKER IN NEW TESTS. + As depending on the exact log wording is making tests very fragile. + Try depending on a metric instead. + """ + + def __init__(self, node: cluster.BaseNode) -> None: + """Initialises the tracker for given local node. + + Args: + node: Node to create tracker for. + Raises: + NotImplementedError: If trying to create a tracker for non-local + node. + """ + if not isinstance(node, cluster.LocalNode): + raise NotImplementedError() + self.fname = node.stderr_name + with open(self.fname) as f: + f.seek(0, 2) + self.offset = f.tell() + + # Pattern matching ANSI escape codes starting with a Control Sequence + # Introducer (CSI) sequence. Most notably Select Graphic Rendition (SGR) + # such as ‘\x1b[35;41m’. + _CSI_RE = re.compile('\x1b\\[[^\x40-\x7E]*[\x40-\x7E]') + + def _read_file(self) -> str: + """Returns data from the file starting from the offset.""" + with open(self.fname) as rd: + rd.seek(self.offset) + data = rd.read() + self.offset = rd.tell() + # Strip ANSI codes + return self._CSI_RE.sub('', data) + + def check(self, pattern: str) -> bool: + """Check whether the pattern can be found in the logs.""" + return pattern in self._read_file() + + def check_re(self, pattern: str) -> bool: + """Check whether the regex pattern can be found in the logs.""" + return re.search(pattern, self._read_file()) != None + + def reset(self) -> None: + """Resets log offset to beginning of the file.""" + self.offset = 0 + + def count(self, pattern: str) -> int: + """Count number of occurrences of pattern in new logs.""" + return self._read_file().count(pattern) + + +class MetricsTracker: + """Helper class to collect prometheus metrics from the node. + + Usage: + tracker = MetricsTracker(node) + assert tracker.get_int_metric_value("unc-connections") == 2 + """ + + def __init__(self, node: cluster.BaseNode) -> None: + if not isinstance(node, cluster.LocalNode): + raise NotImplementedError() + host, port = node.rpc_addr() + + self.addr = f"http://{host}:{port}/metrics" + + def get_all_metrics(self) -> str: + response = requests.get(self.addr) + if not response.ok: + raise RuntimeError( + f"Could not fetch metrics from {self.addr}: {response}") + return response.content.decode('utf-8') + + def get_metric_all_values( + self, metric_name: str) -> typing.List[typing.Tuple[str, str]]: + for family in text_string_to_metric_families(self.get_all_metrics()): + if family.name == metric_name: + return [ + (sample.labels, sample.value) for sample in family.samples + ] + return [] + + def get_metric_value( + self, + metric_name: str, + labels: typing.Optional[typing.Dict[str, str]] = None + ) -> typing.Optional[str]: + all_samples = self.get_metric_all_values(metric_name) + if not labels: + if len(all_samples) > 1: + raise AssertionError( + f"Too many metric values ({len(all_samples)}) for {metric_name} - please specify a label" + ) + if not all_samples: + return None + (sample_labels, sample_value) = all_samples[0] + return sample_value + for (sample_labels, sample_value) in all_samples: + if sample_labels == labels: + return sample_value + return None + + def get_int_metric_value( + self, + metric_name: str, + labels: typing.Optional[typing.Dict[str, str]] = None + ) -> typing.Optional[int]: + """Helper function to return the integer value of the metric (as function above returns strings).""" + value = self.get_metric_value(metric_name, labels) + if value is None: + return None + return round(float(value)) + + +def chain_query(node, block_handler, *, block_hash=None, max_blocks=-1): + """ + Query chain block approvals and chunks preceding of block of block_hash. + If block_hash is None, it query latest block hash + It query at most max_blocks, or if it's -1, all blocks back to genesis + """ + block_hash = block_hash or node.get_latest_block().hash + initial_validators = node.validators() + + if max_blocks == -1: + while True: + validators = node.validators() + if validators != initial_validators: + logger.critical( + f'Fatal: validator set of node {node} changes, from {initial_validators} to {validators}' + ) + sys.exit(1) + block = node.get_block(block_hash)['result'] + block_handler(block) + block_hash = block['header']['prev_hash'] + block_height = block['header']['height'] + if block_height == 0: + break + else: + for _ in range(max_blocks): + validators = node.validators() + if validators != initial_validators: + logger.critical( + f'Fatal: validator set of node {node} changes, from {initial_validators} to {validators}' + ) + sys.exit(1) + block = node.get_block(block_hash)['result'] + block_handler(block) + block_hash = block['header']['prev_hash'] + block_height = block['header']['height'] + if block_height == 0: + break + + +def get_unc_tempdir(subdir=None, *, clean=False): + tempdir = pathlib.Path(tempfile.gettempdir()) / 'near' + if subdir: + tempdir = tempdir / subdir + if clean and tempdir.exists(): + shutil.rmtree(tempdir) + tempdir.mkdir(parents=True, exist_ok=True) + return tempdir + + +def load_binary_file(filepath): + with open(filepath, "rb") as binaryfile: + return bytearray(binaryfile.read()) + + +def load_test_contract( + filename: str = 'backwards_compatible_rs_contract.wasm') -> bytearray: + """Loads a WASM file from unc-test-contracts package. + + This is just a convenience function around load_binary_file which loads + files from ../runtime/unc-test-contracts/res directory. By default + test_contract_rs.wasm is loaded. + """ + repo_dir = pathlib.Path(__file__).resolve().parents[2] + path = repo_dir / 'runtime/unc-test-contracts/res' / filename + return load_binary_file(path) + + +def user_name(): + username = os.getlogin() + if username == 'root': # digitalocean + username = gcloud.list()[0].username.replace('_nearprotocol_com', '') + return username + + +def collect_gcloud_config(num_nodes): + tempdir = get_unc_tempdir() + keys = [] + for i in range(num_nodes): + node_dir = tempdir / f'node{i}' + if not node_dir.exists(): + # TODO: avoid hardcoding the username + logger.info(f'downloading node{i} config from gcloud') + node_dir.mkdir(parents=True, exist_ok=True) + host = gcloud.get(f'pytest-node-{user_name()}-{i}') + for filename in ('config.json', 'signer0_key.json', + 'validator_key.json', 'node_key.json'): + host.download(f'/home/bowen_nearprotocol_com/.near/{filename}', + str(node_dir)) + with open(node_dir / 'signer0_key.json') as f: + key = json.load(f) + keys.append(key) + with open(tempdir / 'node0' / 'config.json') as f: + config = json.load(f) + ip_addresses = map(lambda x: x.split('@')[-1], + config['network']['boot_nodes'].split(',')) + res = { + 'nodes': + list( + map(lambda x: { + 'ip': x.split(':')[0], + 'port': 3030 + }, ip_addresses)), + 'accounts': + keys + } + outfile = tempdir / 'gcloud_config.json' + with open(outfile, 'w') as f: + json.dump(res, f) + os.environ[cluster.CONFIG_ENV_VAR] = str(outfile) + + +def obj_to_string(obj, extra=' ', full=False): + if type(obj) in [tuple, list]: + return "tuple" + '\n' + '\n'.join( + (extra + obj_to_string(x, extra + ' ')) for x in obj) + elif hasattr(obj, "__dict__"): + return str(obj.__class__) + '\n' + '\n'.join( + extra + (str(item) + ' = ' + + obj_to_string(obj.__dict__[item], extra + ' ')) + for item in sorted(obj.__dict__)) + elif isinstance(obj, bytes): + if not full: + if len(obj) > 10: + obj = obj[:7] + b"..." + return str(obj) + else: + return str(obj) + + +def combine_hash(hash1, hash2): + return hashlib.sha256(hash1 + hash2).digest() + + +def compute_merkle_root_from_path(path, leaf_hash): + res = base58.b58decode(leaf_hash) if type(leaf_hash) is str else leaf_hash + for node in path: + if node['direction'] == 'Left': + res = combine_hash(base58.b58decode(node['hash']), res) + else: + res = combine_hash(res, base58.b58decode(node['hash'])) + return res + + +def poll_epochs(node: cluster.LocalNode, + *, + epoch_length, + num_blocks_per_year: int = 31536000, + timeout: float = 300) -> typing.Iterable[int]: + """Polls a node about the latest epoch and yields it when it changes. + + The function continues yielding epoch heights indefinitely (so long as the node + continues reporting them) until timeout is reached or the caller stops + reading yielded values. Reaching the timeout is considered to be a failure + condition and thus it results in an `AssertionError`. The expected usage is + that caller reads epoch heights until some condition is met at which point it stops + iterating over the generator. + + Args: + node: Node to query about its latest epoch. + timeout: Total timeout from the first status request sent to the node. + epoch_length: epoch_length genesis config value + num_blocks_per_year: num_blocks_per_year genesis config value + Yields: + An int for each new epoch height reported. Note that there + is no guarantee that there will be no skipped epochs. + Raises: + AssertionError: If more than `timeout` seconds passes from the start of + the iteration, or the response from the node is not as expected. + """ + end = time.time() + timeout + start_height = -1 + epoch_start = -1 + count = 0 + previous = -1 + + while time.time() < end: + response = node.get_validators() + assert 'error' not in response, response + + latest = response['result'] + height = latest['epoch_height'] + assert isinstance(height, int) and height >= 1, height + + if start_height == -1: + start_height = height + + if previous != height: + yield height + + count += 1 + previous = height + epoch_start = latest['epoch_start_height'] + assert isinstance(epoch_start, + int) and epoch_start >= 1, epoch_start + + blocks_left = epoch_start + epoch_length - node.get_latest_block( + ).height + seconds_left = blocks_left / (num_blocks_per_year / 31536000) + time.sleep(max(seconds_left, 2)) + + msg = 'Timed out polling epochs from a node\n' + if count: + msg += (f'First epoch: {start_height}; last epoch: {previous}\n' + f'Total epochs returned: {count}') + else: + msg += 'No epochs were returned' + raise AssertionError(msg) + + +def poll_blocks(node: cluster.LocalNode, + *, + timeout: float = 120, + poll_interval: float = 0.25, + __target: typing.Optional[int] = None, + **kw) -> typing.Iterable[cluster.BlockId]: + """Polls a node about the latest block and yields it when it changes. + + The function continues yielding blocks indefinitely (so long as the node + continues reporting its status) until timeout is reached or the caller stops + reading yielded values. Reaching the timeout is considered to be a failure + condition and thus it results in an `AssertionError`. The expected usage is + that caller reads blocks until some condition is met at which point it stops + iterating over the generator. + + Args: + node: Node to query about its latest block. + timeout: Total timeout from the first status request sent to the node. + poll_interval: How long to wait in seconds between each status request + sent to the node. + kw: Keyword arguments passed to `BaseDone.get_latest_block` method. + Yields: + A `cluster.BlockId` object for each each time node’s latest block + changes including the first block when function starts. Note that there + is no guarantee that there will be no skipped blocks. + Raises: + AssertionError: If more than `timeout` seconds passes from the start of + the iteration. + """ + end = time.monotonic() + timeout + start_height = -1 + count = 0 + previous = -1 + + while time.monotonic() < end: + latest = node.get_latest_block(**kw) + if latest.height != previous: + if __target: + msg = f'{latest} (waiting for #{__target})' + else: + msg = str(latest) + logger.info(msg) + yield latest + previous = latest.height + if start_height == -1: + start_height = latest.height + count += 1 + time.sleep(poll_interval) + + msg = 'Timed out polling blocks from a node\n' + if count > 0: + msg += (f'First block: {start_height}; last block: {previous}\n' + f'Total blocks returned: {count}') + else: + msg += 'No blocks were returned' + if __target: + msg += f'\nWaiting for block: {__target}' + raise AssertionError(msg) + + +def wait_for_blocks(node: cluster.LocalNode, + *, + target: typing.Optional[int] = None, + count: typing.Optional[int] = None, + timeout: typing.Optional[float] = None, + **kw) -> cluster.BlockId: + """Waits until given node reaches expected target block height. + + Exactly one of `target` or `count` arguments must be specified. Specifying + `count` is equivalent to setting `target` to node’s current height plus the + given count. + + Args: + node: Node to query about its latest block. + target: Target height of the latest block known by the node. + count: How many new blocks to wait for. If this argument is given, + target is calculated as node’s current block height plus the given + count. + timeout: Total timeout from the first status request sent to the node. + If not specified, the default is to assume that overall each block + takes no more than five seconds to generate. + kw: Keyword arguments passed to `poll_blocks`. `timeout` and + `poll_interval` are likely of most interest. + Returns: + A `cluster.BlockId` of the block at target height. + Raises: + AssertionError: If the node does not reach given block height before + timeout passes. + """ + if target is None: + if count is None: + raise TypeError('Expected `count` or `target` keyword argument') + target = node.get_latest_block().height + count + else: + if count is not None: + raise TypeError( + 'Expected at most one of `count` or `target` arguments') + if timeout is None: + count = max(0, target - node.get_latest_block().height) + if timeout is None: + timeout = max(10, count * 5) + for latest in poll_blocks(node, timeout=timeout, __target=target, **kw): + if latest.height >= target: + return latest + + +def figure_out_sandbox_binary(): + config = { + 'local': True, + 'release': False, + } + repo_dir = pathlib.Path(__file__).resolve().parents[2] + # When run on NayDuck we end up with a binary called uncd in target/debug + # but when run locally the binary might be uncd-sandbox or unc-sandbox + # instead. Try to figure out whichever binary is available and use that. + for release in ('release', 'debug'): + root = repo_dir / 'target' / release + for exe in ('uncd-sandbox', 'unc-sandbox', 'uncd'): + if (root / exe).exists(): + logger.info( + f'Using {(root / exe).relative_to(repo_dir)} binary') + config['unc_root'] = str(root) + config['binary_name'] = exe + return config + + assert False, ('Unable to figure out location of uncd-sandbox binary; ' + 'Did you forget to run `make sandbox`?') diff --git a/pytest/remote.json b/pytest/remote.json new file mode 100644 index 000000000..9be573831 --- /dev/null +++ b/pytest/remote.json @@ -0,0 +1,13 @@ +{ + "local": false, + "unc_root": "../target/debug/", + "remote": { + "instance_name": "unc-pytest", + "binary": "unc-staging-200f8d5650932291df9e61c16059b8723e503920-debug", + "zones": [ + "us-east1-d", + "us-central1-c", + "us-east1-b" + ] + } +} \ No newline at end of file diff --git a/pytest/requirements.txt b/pytest/requirements.txt new file mode 100644 index 000000000..c5b87763f --- /dev/null +++ b/pytest/requirements.txt @@ -0,0 +1,18 @@ +PyGithub +base58 +cython +deepdiff +ed25519 +numpy +prometheus-client +psutil +pynacl +python-rc==0.3.9 +requests +retrying +scikit-learn +scipy +semver +toml +tqdm +urllib3<2 diff --git a/pytest/tests/__init__.py b/pytest/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pytest/tests/adversarial/fork_sync.py b/pytest/tests/adversarial/fork_sync.py new file mode 100755 index 000000000..6a88cf468 --- /dev/null +++ b/pytest/tests/adversarial/fork_sync.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +# Spins up four validating nodes. Wait until they produce 20 blocks. +# Kill the first two nodes, let the rest two produce 30 blocks. +# Kill the remaining two and restart the first two. Let them produce also 30 blocks +# Restart the two that were killed and make sure they can sync with the other chain +# and produce blocks + +import sys, time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +import utils + +TIMEOUT = 120 +FIRST_STEP_WAIT = 20 +SECOND_STEP_WAIT = 30 +FINAL_HEIGHT_THRESHOLD = 80 + +nodes = start_cluster( + 4, 0, 4, None, + [["epoch_length", 200], ["block_producer_kickout_threshold", 10]], {}) +time.sleep(3) +cur_height = 0 +fork1_height = 0 +fork2_height = 0 + +for i in range(0, 4): + res = nodes[i].json_rpc('adv_disable_doomslug', []) + assert 'result' in res, res + +# step 1, let nodes run for some time +utils.wait_for_blocks(nodes[0], target=FIRST_STEP_WAIT) + +for i in range(2): + nodes[i].kill() + +logger.info("killing node 0 and 1") +utils.wait_for_blocks(nodes[2], target=FIRST_STEP_WAIT + SECOND_STEP_WAIT) + +for i in range(2, 4): + nodes[i].kill() + +logger.info("killing node 2 and 3") + +for i in range(2): + nodes[i].start(boot_node=nodes[0]) + res = nodes[i].json_rpc('adv_disable_doomslug', []) + assert 'result' in res, res + +time.sleep(1) + +utils.wait_for_blocks(nodes[0], target=FIRST_STEP_WAIT + SECOND_STEP_WAIT) + +for i in range(2, 4): + nodes[i].start(boot_node=nodes[0]) + res = nodes[i].json_rpc('adv_disable_doomslug', []) + assert 'result' in res, res + +time.sleep(1) + +logger.info("all nodes restarted") + +while cur_height < TIMEOUT: + statuses = sorted((enumerate(node.get_latest_block() for node in nodes)), + key=lambda element: element[1].height) + last = statuses.pop() + cur_height = last[1].height + node = nodes[last[0]] + succeed = True + for _, block in statuses: + try: + node.get_block(block.hash) + except Exception: + succeed = False + break + if statuses[0][1].height > FINAL_HEIGHT_THRESHOLD and succeed: + sys.exit(0) + time.sleep(0.5) + +assert False, "timed out waiting for forks to resolve" diff --git a/pytest/tests/adversarial/gc_rollback.py b/pytest/tests/adversarial/gc_rollback.py new file mode 100755 index 000000000..59b7ce07c --- /dev/null +++ b/pytest/tests/adversarial/gc_rollback.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# Builds the following graph: +# ------- +# \ +# ------ +# \ +# -------- +# \ +# ---------- +# checks that GC not failing + +import sys, time +import random +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger + +EPOCH_LENGTH = 30 +NUM_BLOCKS_TOTAL = 200 +FORK_EACH_BLOCKS = 10 + +consensus_config = {"consensus": {"min_num_peers": 0}} +nodes = start_cluster(2, 0, 1, None, [["epoch_length", EPOCH_LENGTH]], + {0: consensus_config}) +time.sleep(2) + +res = nodes[0].json_rpc('adv_disable_doomslug', []) +assert 'result' in res, res +res = nodes[1].json_rpc('adv_disable_doomslug', []) +assert 'result' in res, res + +cur_height = 0 +last_fork = FORK_EACH_BLOCKS * 2 +while cur_height < NUM_BLOCKS_TOTAL: + cur_height = nodes[0].get_latest_block(verbose=True).height + if cur_height > last_fork: + new_height = cur_height - random.randint(1, FORK_EACH_BLOCKS) + nodes[1].kill() + nodes[1].reset_data() + + logger.info("Rolling back from %d to %d" % (cur_height, new_height)) + res = nodes[0].json_rpc('adv_switch_to_height', [new_height]) + assert 'result' in res, res + #res = nodes[1].json_rpc('adv_switch_to_height', [new_height]) + #assert 'result' in res, res + + nodes[1].start(boot_node=nodes[0]) + res = nodes[1].json_rpc('adv_disable_doomslug', []) + assert 'result' in res, res + + last_fork += FORK_EACH_BLOCKS + time.sleep(0.9) + +saved_blocks = nodes[0].json_rpc('adv_get_saved_blocks', []) +logger.info(saved_blocks) diff --git a/pytest/tests/adversarial/malicious_chain.py b/pytest/tests/adversarial/malicious_chain.py new file mode 100755 index 000000000..47b4a5ea0 --- /dev/null +++ b/pytest/tests/adversarial/malicious_chain.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# starts two validators and one extra RPC node, and directs one of the validators +# to produce invalid blocks. Then we check that the other two nodes have banned this peer. + +import sys, time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +import utils + +valid_blocks_only = False # creating invalid blocks, should be banned instantly +if "valid_blocks_only" in sys.argv: + valid_blocks_only = True # creating valid blocks, should be fixed by doom slug + +BLOCKS = 25 +MALICIOUS_BLOCKS = 50 + +nodes = start_cluster( + 2, 1, 2, None, + [["epoch_length", 1000], ["block_producer_kickout_threshold", 80]], {}) + +started = time.time() + +logger.info(f'Waiting for {BLOCKS} blocks...') +height, _ = utils.wait_for_blocks(nodes[1], target=BLOCKS) +logger.info(f'Got to {height} blocks, getting to fun stuff') + +# first check that nodes 0 and 2 have two peers each, before we check later that +# they've dropped to just one peer +network_info0 = nodes[0].json_rpc('network_info', {})['result'] +network_info2 = nodes[2].json_rpc('network_info', {})['result'] +assert network_info0['num_active_peers'] == 2, network_info0['num_active_peers'] +assert network_info2['num_active_peers'] == 2, network_info2['num_active_peers'] + +nodes[1].get_status(verbose=True) + +res = nodes[1].json_rpc('adv_produce_blocks', + [MALICIOUS_BLOCKS, valid_blocks_only]) +assert 'result' in res, res +logger.info("Generated %s malicious blocks" % MALICIOUS_BLOCKS) + +time.sleep(10) + +height = nodes[0].get_latest_block(verbose=True).height + +assert height < 40 + +network_info0 = nodes[0].json_rpc('network_info', {})['result'] +network_info2 = nodes[2].json_rpc('network_info', {})['result'] + +# Since we have 3 nodes, they should all have started with 2 peers. After the above +# invalid blocks sent by node1, the other two should have banned it, leaving them +# with one active peer + +assert network_info0['num_active_peers'] == 1, network_info0['num_active_peers'] +assert network_info2['num_active_peers'] == 1, network_info2['num_active_peers'] + +logger.info("Epic") diff --git a/pytest/tests/adversarial/start_from_genesis.py b/pytest/tests/adversarial/start_from_genesis.py new file mode 100755 index 000000000..b6b77e2ea --- /dev/null +++ b/pytest/tests/adversarial/start_from_genesis.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +import sys, time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +import utils + +overtake = False # create a new chain which is shorter than current one +if "overtake" in sys.argv: + overtake = True # create a new chain which is longer than current one + +doomslug = True +if "doomslug_off" in sys.argv: + doomslug = False # turn off doomslug + +BLOCKS = 30 + +# Low sync_check_period to sync from a new peer with greater height +client_config_change = { + "consensus": { + "sync_check_period": { + "secs": 0, + "nanos": 100000000 + } + } +} + +nodes = start_cluster( + 2, 0, 2, None, + [["epoch_length", 100], ["block_producer_kickout_threshold", 80]], + {0: client_config_change}) +if not doomslug: + # we expect inconsistency in store in node 0 + # because we're going to turn off doomslug + # and allow applying blocks without proper validation + nodes[0].stop_checking_store() + +started = time.time() + +time.sleep(2) +logger.info(f'Waiting for {BLOCKS} blocks...') +height, _ = utils.wait_for_blocks(nodes[0], target=BLOCKS) +logger.info(f'Got to {height} blocks, getting to fun stuff') + +status = nodes[0].get_status() +logger.info(f"STATUS OF HONEST {status}") +saved_blocks = nodes[0].json_rpc('adv_get_saved_blocks', []) +logger.info(f"SAVED BLOCKS {saved_blocks}") + +nodes[0].kill() # to disallow syncing +nodes[1].kill() + +# Switch node1 to an adversarial chain +nodes[1].reset_data() +nodes[1].start(boot_node=nodes[0]) + +num_produce_blocks = BLOCKS // 2 - 5 +if overtake: + num_produce_blocks += 10 + +res = nodes[1].json_rpc('adv_produce_blocks', [num_produce_blocks, True]) +assert 'result' in res, res +time.sleep(2) +nodes[1].kill() + +# Restart both nodes. +# Disabling doomslug must happen before starting node1 +nodes[0].start(boot_node=nodes[0]) +if not doomslug: + res = nodes[0].json_rpc('adv_disable_doomslug', []) + assert 'result' in res, res +nodes[1].start(boot_node=nodes[0]) + +time.sleep(3) +status = nodes[1].get_status() +logger.info(f"STATUS OF MALICIOUS {status}") + +status = nodes[0].get_status() +logger.info(f"STATUS OF HONEST AFTER {status}") +height = status['sync_info']['latest_block_height'] + +saved_blocks_2 = nodes[0].json_rpc('adv_get_saved_blocks', []) +logger.info(f"SAVED BLOCKS AFTER MALICIOUS INJECTION {saved_blocks_2}") +logger.info(f"HEIGHT {height}") + +assert saved_blocks['result'] < BLOCKS + 10 +if overtake and not doomslug: + # node 0 should accept additional blocks from node 1 because of new chain is longer and doomslug is turned off + assert saved_blocks_2['result'] >= BLOCKS + num_produce_blocks +else: + assert saved_blocks_2['result'] < saved_blocks['result'] + 10 + +logger.info("Epic") diff --git a/pytest/tests/contracts/deploy_call_smart_contract.py b/pytest/tests/contracts/deploy_call_smart_contract.py new file mode 100755 index 000000000..3cb338203 --- /dev/null +++ b/pytest/tests/contracts/deploy_call_smart_contract.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""Deploy a smart contract on one node and call it on another.""" + +import base58 +import sys +import time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) +from cluster import start_cluster +from transaction import sign_deploy_contract_tx, sign_function_call_tx +from utils import load_test_contract + + +def test_deploy_contract(): + nodes = start_cluster( + 2, 0, 1, None, + [["epoch_length", 10], ["block_producer_kickout_threshold", 80]], {}) + + last_block_hash = nodes[0].get_latest_block().hash_bytes + tx = sign_deploy_contract_tx(nodes[0].signer_key, load_test_contract(), 10, + last_block_hash) + nodes[0].send_tx(tx) + time.sleep(3) + + last_block_hash = nodes[1].get_latest_block().hash_bytes + tx = sign_function_call_tx(nodes[0].signer_key, + nodes[0].signer_key.account_id, 'log_something', + [], 100000000000, 100000000000, 20, + last_block_hash) + res = nodes[1].send_tx_and_wait(tx, 20) + import json + print(json.dumps(res, indent=2)) + assert res['result']['receipts_outcome'][0]['outcome']['logs'][0] == 'hello' + + +if __name__ == '__main__': + test_deploy_contract() diff --git a/pytest/tests/contracts/gibberish.py b/pytest/tests/contracts/gibberish.py new file mode 100755 index 000000000..32042896d --- /dev/null +++ b/pytest/tests/contracts/gibberish.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# Experiments with deploying gibberish contracts. Specifically, +# 1. Deploys completely gibberish contracts +# 2. Gets an existing wasm contract, and tries to arbitrarily pertrurb bytes in it + +import sys, time, random +import base58 +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) +from cluster import start_cluster +from configured_logger import logger +from transaction import sign_deploy_contract_tx, sign_function_call_tx +from utils import load_test_contract + +nodes = start_cluster( + 3, 0, 4, None, + [["epoch_length", 1000], ["block_producer_kickout_threshold", 80]], {}) + +wasm_blob_1 = load_test_contract() + +hash_ = nodes[0].get_latest_block().hash_bytes + +for iter_ in range(10): + logger.info("Deploying garbage contract #%s" % iter_) + wasm_blob = bytes( + [random.randint(0, 255) for _ in range(random.randint(200, 500))]) + tx = sign_deploy_contract_tx(nodes[0].signer_key, wasm_blob, 10 + iter_, + hash_) + nodes[0].send_tx_and_wait(tx, 20) + +for iter_ in range(10): + hash_ = nodes[0].get_latest_block().hash_bytes + logger.info("Deploying perturbed contract #%s" % iter_) + + new_name = '%s_mething' % iter_ + new_output = '%s_llo' % iter_ + + wasm_blob = wasm_blob_1.replace(bytes('something', 'utf8'), + bytes(new_name, 'utf8')).replace( + bytes('hello', 'utf8'), + bytes(new_output, 'utf8')) + assert len(wasm_blob) == len(wasm_blob_1) + + pos = random.randint(0, len(wasm_blob_1) - 1) + val = random.randint(0, 255) + wasm_blob = wasm_blob[:pos] + bytes([val]) + wasm_blob[pos + 1:] + tx = sign_deploy_contract_tx(nodes[0].signer_key, wasm_blob, 20 + iter_ * 2, + hash_) + res = nodes[0].send_tx_and_wait(tx, 20) + assert 'result' in res + logger.info(res) + + logger.info("Invoking perturbed contract #%s" % iter_) + + tx2 = sign_function_call_tx(nodes[0].signer_key, + nodes[0].signer_key.account_id, new_name, [], + 3000000000000, 100000000000, 20 + iter_ * 2 + 1, + hash_) + # don't have any particular expectation for the call result, but the transaction should at least go through + res = nodes[1].send_tx_and_wait(tx2, 20) + assert 'result' in res + +hash_ = nodes[0].get_latest_block().hash_bytes + +logger.info("Real thing!") +tx = sign_deploy_contract_tx(nodes[0].signer_key, wasm_blob_1, 60, hash_) +nodes[0].send_tx(tx) + +time.sleep(3) + +hash_2 = nodes[1].get_latest_block().hash_bytes +tx2 = sign_function_call_tx(nodes[0].signer_key, nodes[0].signer_key.account_id, + 'log_something', [], 3000000000000, 100000000000, + 62, hash_2) +res = nodes[1].send_tx_and_wait(tx2, 20) +logger.info(res) +assert res['result']['receipts_outcome'][0]['outcome']['logs'][0] == 'hello' diff --git a/pytest/tests/contracts/infinite_loops.py b/pytest/tests/contracts/infinite_loops.py new file mode 100755 index 000000000..f639ffc26 --- /dev/null +++ b/pytest/tests/contracts/infinite_loops.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# Spins up four nodes, deploy an smart contract to one node, +# Call a smart contract method in another node +import sys, time +import base58 +import concurrent.futures +import requests +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) +from cluster import start_cluster +from transaction import sign_deploy_contract_tx, sign_function_call_tx +from utils import load_test_contract + +nodes = start_cluster( + 4, 0, 1, None, + [["epoch_length", 40], ["block_producer_kickout_threshold", 80], + ["transaction_validity_period", 10000]], { + 0: { + "tracked_shards": [0] + }, + 1: { + "tracked_shards": [0] + }, + 2: { + "tracked_shards": [0] + }, + 3: { + "tracked_shards": [0] + }, + }) + +hash_ = nodes[0].get_latest_block().hash_bytes +tx = sign_deploy_contract_tx(nodes[0].signer_key, load_test_contract(), 10, + hash_) +nodes[0].send_tx(tx) + +time.sleep(3) + + +# send num_tx function calls from node[i]'s account +def send_transactions(i, num_tx): + print("Sending Transactions {}".format(i)) + hash_2 = nodes[i].get_latest_block().hash_bytes + nonce = 20 + for _ in range(num_tx): + tx = sign_function_call_tx(nodes[i].signer_key, + nodes[0].signer_key.account_id, + 'loop_forever', [], 300000000000, 0, nonce, + hash_2) + nonce += 1 + res = nodes[i].send_tx_and_wait(tx, 20) + assert 'result' in res, res + assert res['result']['status']['Failure']['ActionError']['kind'][ + 'FunctionCallError'][ + 'ExecutionError'] == 'Exceeded the prepaid gas.', "result: {}".format( + res) + + +with concurrent.futures.ThreadPoolExecutor() as executor: + futures = [] + for i in range(len(nodes)): + futures.append(executor.submit(send_transactions, i, 20)) + for future in concurrent.futures.as_completed(futures): + future.result() diff --git a/pytest/tests/delete_remote_nodes.py b/pytest/tests/delete_remote_nodes.py new file mode 100755 index 000000000..3b1e46af2 --- /dev/null +++ b/pytest/tests/delete_remote_nodes.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 + +# When script exit with traceback, remote node is not deleted. This script is +# to delete remote machines so test can be rerun +# DANGER: make sure not delete production nodes! + +from rc import gcloud, pmap +from distutils.util import strtobool +import sys +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[1] / 'lib')) +from utils import user_name +from configured_logger import logger + +machines = gcloud.list() +to_delete_prefix = sys.argv[1] if len( + sys.argv) >= 2 else f"pytest-node-{user_name()}-" +to_delete = list(filter(lambda m: m.name.startswith(to_delete_prefix), + machines)) + +if to_delete: + a = input( + f"going to delete {list(map(lambda m: m.name, to_delete))}\ny/n: ") + if strtobool(a): + + def delete_machine(m): + logger.info(f'deleting {m.name}') + m.delete() + logger.info(f'{m.name} deleted') + + pmap(delete_machine, to_delete) diff --git a/pytest/tests/loadtest/README.md b/pytest/tests/loadtest/README.md new file mode 100644 index 000000000..bf9489925 --- /dev/null +++ b/pytest/tests/loadtest/README.md @@ -0,0 +1,45 @@ +# Loadtest + +This test requires a few steps. Firstly, build the binary: + +```shell +make uncd-release +``` + +Secondly, initialise your own localnet: + +```shell +./target/release/uncd --home ~/.unc_tmp init --chain-id localnet --num-shards=5 +``` + +Thirdly, create accounts and deploy the contract: + +```shell +python3 pytest/tests/loadtest/setup.py --home ~/.unc_tmp --num_accounts=5 +``` + +And lastly, run the test: + +```shell +python3 pytest/tests/loadtest/loadtest.py --home ~/.unc_tmp --num_accounts=5 --num_requests=1000 +``` + +# Load Test version 2 + +The newer loadtest2.py script currently runs an intense load test with the FT contract. + +Much like with the earlier version you will want to build a `uncd`. This script can set up a (2 +node) cluster for you (nice for testing): + +``` +env unc_ROOT=../target/release/ python3 tests/loadtest/loadtest2.py --fungible-token-wasm=$PWD/../../FT/res/fungible_token.wasm --setup-cluster --accounts=1000 --executors=4 +``` + +Or, you can set up a network yourself, and point the script at your local node’s RPC endpoint: + +``` +env unc_ROOT=../target/release/ python3 tests/stress/perf_ft_transfer.py --fungible-token-wasm=$PWD/../../FT/res/fungible_token.wasm --accounts=1000 --executors=4 --contract-key=~/.near/node.json +``` + +As seen in commands above, you will need a fungible token contract to test with. There's one you +can get from the `near/unc-examples` repository. diff --git a/pytest/tests/loadtest/contract/Cargo.lock b/pytest/tests/loadtest/contract/Cargo.lock new file mode 100644 index 000000000..81250a10f --- /dev/null +++ b/pytest/tests/loadtest/contract/Cargo.lock @@ -0,0 +1,611 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + +[[package]] +name = "ahash" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "borsh" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a7111f797cc721407885a323fb071636aee57f750b1a4ddc27397eba168a74" +dependencies = [ + "borsh-derive", + "hashbrown 0.9.1", +] + +[[package]] +name = "borsh-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307f3740906bac2c118a8122fe22681232b244f1369273e45f1156b45c43d2dd" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate", + "proc-macro2", + "syn", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2104c73179359431cc98e016998f2f23bc7a05bc53e79741bcba705f30047bc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae29eb8418fcd46f723f8691a2ac06857d31179d33d2f2d91eb13967de97c728" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown 0.11.2", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" + +[[package]] +name = "loadtest-contract" +version = "0.1.0" +dependencies = [ + "unc-sdk", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "unc-primitives-core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2b3fb5acf3a494aed4e848446ef2d6ebb47dbe91c681105d4d1786c2ee63e52" +dependencies = [ + "base64", + "borsh", + "bs58", + "derive_more", + "hex", + "lazy_static", + "num-rational", + "serde", + "serde_json", + "sha2", +] + +[[package]] +name = "unc-rpc-error-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffa8dbf8437a28ac40fcb85859ab0d0b8385013935b000c7a51ae79631dd74d9" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", +] + +[[package]] +name = "unc-rpc-error-macro" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6111d713e90c7c551dee937f4a06cb9ea2672243455a4454cc7566387ba2d9" +dependencies = [ + "unc-rpc-error-core", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", +] + +[[package]] +name = "unc-runtime-utils" +version = "4.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a48d80c4ca1d4cf99bc16490e1e3d49826c150dfc4410ac498918e45c7d98e07" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "unc-sdk" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7383e242d3e07bf0951e8589d6eebd7f18bb1c1fc5fbec3fad796041a6aebd1" +dependencies = [ + "base64", + "borsh", + "bs58", + "unc-primitives-core", + "unc-sdk-macros", + "unc-vm-logic", + "serde", + "serde_json", + "wee_alloc", +] + +[[package]] +name = "unc-sdk-core" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284a78d9eb8eda58330462fa0023a6d7014c941df1f0387095e7dfd1dc0f2bce" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unc-sdk-macros" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2037337438f97d1ce5f7c896cf229dc56dacd5c01142d1ef95a7d778cde6ce7d" +dependencies = [ + "unc-sdk-core", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unc-vm-errors" +version = "4.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e281d8730ed8cb0e3e69fb689acee6b93cdb43824cd69a8ffd7e1bfcbd1177d7" +dependencies = [ + "borsh", + "hex", + "unc-rpc-error-macro", + "serde", +] + +[[package]] +name = "unc-vm-logic" +version = "4.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11cb28a2d07f37680efdaf860f4c9802828c44fc50c08009e7884de75d982c5" +dependencies = [ + "base64", + "borsh", + "bs58", + "byteorder", + "unc-primitives-core", + "unc-runtime-utils", + "unc-vm-errors", + "serde", + "sha2", + "sha3", +] + +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "semver" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d" + +[[package]] +name = "serde" +version = "1.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer", + "cfg-if 1.0.0", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer", + "digest", + "keccak", + "opaque-debug", +] + +[[package]] +name = "syn" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/pytest/tests/loadtest/contract/Cargo.toml b/pytest/tests/loadtest/contract/Cargo.toml new file mode 100644 index 000000000..1dec639a1 --- /dev/null +++ b/pytest/tests/loadtest/contract/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "loadtest-contract" +version = "0.1.0" +authors = ["Hello Inc "] +edition = "2018" + +[lints] +workspace = true + +[workspace] +members = [] + + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +unc-sdk = "3.1.0" + +[profile.release] +codegen-units = 1 +# Tell `rustc` to optimize for small code size. +opt-level = "z" +lto = true +debug = false +panic = "abort" +# Opt into extra safety checks on arithmetic operations https://stackoverflow.com/a/64136471/249801 +overflow-checks = true diff --git a/pytest/tests/loadtest/contract/build.sh b/pytest/tests/loadtest/contract/build.sh new file mode 100755 index 000000000..95f50570d --- /dev/null +++ b/pytest/tests/loadtest/contract/build.sh @@ -0,0 +1 @@ +cargo build --target wasm32-unknown-unknown --release diff --git a/pytest/tests/loadtest/contract/src/lib.rs b/pytest/tests/loadtest/contract/src/lib.rs new file mode 100644 index 000000000..91f67b5a7 --- /dev/null +++ b/pytest/tests/loadtest/contract/src/lib.rs @@ -0,0 +1,72 @@ +//! Contract that can be used for different types of loadtesting. +//! Based on the rust-counter example. + +use unc_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; +use unc_sdk::collections::LookupMap; +use unc_sdk::{env, unc_bindgen}; + +unc_sdk::setup_alloc!(); + +#[unc_bindgen] +#[derive(BorshDeserialize, BorshSerialize)] +pub struct Counter { + val: u64, + records: LookupMap, +} + +impl Default for Counter { + fn default() -> Self { + Self { val: 0, records: LookupMap::new(b"r".to_vec()) } + } +} +#[unc_bindgen] +impl Counter { + pub fn get_num(&self) -> u64 { + return self.val; + } + + pub fn increment(&mut self) { + self.val += 1; + let log_message = format!("Increased number to {}", self.val); + env::log(log_message.as_bytes()); + } + + pub fn reset(&mut self) { + self.val = 0; + } + + fn get_previous_val(&self, i: u64) -> u64 { + match self.records.get(&i.to_string()) { + Some(value) => value.parse::().unwrap(), + None => 0, + } + } + + // Similar to the methods above, but updating many fields (therefore using a lot more gas). + pub fn increment_many(&mut self, how_many: u64) { + for i in 1..how_many { + let previous_val = self.get_previous_val(i); + self.records.insert(&i.to_string(), &(previous_val + 1).to_string()); + } + } + + pub fn reset_increment_many(&mut self, how_many: u64) { + for i in 1..how_many { + self.records.insert(&i.to_string(), &(0).to_string()); + } + } + pub fn get_increment_many(&self) -> u64 { + self.get_previous_val(1) + } + + pub fn infinite_loop(&self) { + loop {} + } + + pub fn write_many(&mut self, how_many: u64) { + for i in self.val..self.val + how_many { + self.records.insert(&i.to_string(), &"a".to_string()); + } + self.val += how_many; + } +} diff --git a/pytest/tests/loadtest/loadtest.py b/pytest/tests/loadtest/loadtest.py new file mode 100644 index 000000000..106d167e6 --- /dev/null +++ b/pytest/tests/loadtest/loadtest.py @@ -0,0 +1,104 @@ +from tqdm import tqdm +import mocknet_helpers +import account +import key +import base64 +import argparse +from os.path import join + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Setup loadtest') + + parser.add_argument('--home', type=str, required=True) + parser.add_argument( + '--sender_key_file', + type=str, + default="validator_key.json", + help= + "File in home directory that contains the key for the account that should be used to send all the requests." + ) + parser.add_argument( + '--num_accounts', + type=int, + default=5, + help= + "Accounts that contain the contract to run (either set the --num_accounts or comma separated list in --account_ids)" + ) + parser.add_argument('--account_ids', type=str, default=None) + + parser.add_argument('--num_requests', type=int, default=50) + parser.add_argument('--host', type=str, default='127.0.0.1') + # Contract types: + # - storage - tries to do maximum amount of reads & writes (increments a large vector) + # - compute - tries to do maximum amount of compute (infinite loop) + parser.add_argument('--contract_type', + type=str, + default='storage', + help="""# Contract types: + # - storage - tries to do maximum amount of reads & writes (increments a large vector) + # - compute - tries to do maximum amount of compute (infinite loop) + # - write - tries to write a lot of data to the contract state.""") + args = parser.parse_args() + + accounts = [args.account_ids.split(",")] if args.account_ids else [ + f"shard{i}" for i in range(args.num_accounts) + ] + + validator_key = key.Key.from_json_file(join(args.home, + args.sender_key_file)) + + base_block_hash = mocknet_helpers.get_latest_block_hash(addr=args.host) + nonce = mocknet_helpers.get_nonce_for_key(validator_key, addr=args.host) + + my_account = account.Account(validator_key, + init_nonce=nonce, + base_block_hash=base_block_hash, + rpc_infos=[(args.host, "3030")]) + + # First - 'reset' the counters in the contract. + for y in accounts: + my_account.send_call_contract_raw_tx( + contract_id=y, + method_name="reset_increment_many", + args=f'{{"how_many": 400}}'.encode("utf-8"), + deposit=0) + + results = [] + + contract_type = args.contract_type + assert (contract_type in ['storage', 'compute', 'write']) + + if contract_type == "storage": + method_name = "increment_many" + if contract_type == "write": + method_name = "write_many" + if contract_type == "compute": + method_name = "infinite_loop" + + for i in tqdm(range(args.num_requests)): + for y in accounts: + result = my_account.send_call_contract_raw_tx( + contract_id=y, + method_name=method_name, + args=f'{{"how_many": {min(400 + i, 400)}}}'.encode("utf-8"), + deposit=0) + results.append(result) + + if contract_type == 'storage': + # For 'storage' contracts - we can also check that all were executed successfully. + for y in accounts: + res = my_account.send_call_contract_raw_tx( + contract_id=y, + method_name="get_increment_many", + args='', + deposit=0) + print(f"Shard {y} asking for result: {res}") + result = mocknet_helpers.tx_result(res["result"], + validator_key.account_id, + addr=args.host, + wait_for_success=True) + outcome = base64.b64decode(result['status']['SuccessValue']) + if int(outcome) == args.num_requests: + print(f"Shard {y}: PASS") + else: + print(f"Shard {y} : FAIL {outcome} vs {args.num_requests}") diff --git a/pytest/tests/loadtest/loadtest2.py b/pytest/tests/loadtest/loadtest2.py new file mode 100644 index 000000000..20c8172a7 --- /dev/null +++ b/pytest/tests/loadtest/loadtest2.py @@ -0,0 +1,525 @@ +#!/usr/bin/env python3 +""" +This is a benchmark in which a network with a single fungible_token contract is +deployed, and then a variable number of users (see `N_ACCOUNTS`) send each other +those fungible tokens. + +At the time this benchmark is written, the intent is to observe the node metrics +and traces for the block duration, potentially adding any additional +instrumentation as needed. + +To run: + +``` +env unc_ROOT=../target/release/ \ + python3 tests/loadtest/loadtest2.py \ + --fungible-token-wasm=$PWD/../../FT/res/fungible_token.wasm \ + --setup-cluster --accounts=1000 --executors=4 +``` +""" + +import argparse +import sys +import os +import time +import pathlib +import base58 +import requests +import random +import logging +import json +import multiprocessing +import multiprocessing.queues +import ctypes +import ed25519 +import queue +import string + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import cluster +import utils +import account +import transaction +import key +import account +import mocknet_helpers +from configured_logger import new_logger + +DEFAULT_TRANSACTION_TTL_SECONDS = 10 +MAX_INFLIGHT_TRANSACTIONS_PER_EXECUTOR = 1000 +SEED = random.uniform(0, 0xFFFFFFFF) +logger = new_logger(level=logging.INFO) + + +class Transaction: + """ + A transaction future. + """ + + ID = 0 + + def __init__(self): + self.id = Transaction.ID + Transaction.ID += 1 + + # Number of times we are going to check this transaction for completion before retrying + # submission + self.ttl = 0 + self.expiration = 0 + # The transaction id hash + # + # str if the transaction has been submitted and may eventually conclude. + self.transaction_id = None + # The transaction caller (used for checking the transaction status. + # + # `str` if the transaction has been submitted and may eventually conclude. + self.caller = None + # The outcome of a successful transaction execution + self.outcome = None + + def poll(self, node, block_hash): + """ + Returns True if transaction has completed. + """ + if self.is_complete(): + return True + # Send the transaction if the previous expired or we didn't send one in the first place. + if self.transaction_id is None or self.ttl <= 0: + if self.transaction_id is not None: + logger.warning( + f"transaction {self.transaction_id} expired, submitting a new one!" + ) + (self.transaction_id, self.caller) = self.send(node, block_hash) + self.expiration = time.time() + DEFAULT_TRANSACTION_TTL_SECONDS + self.ttl = DEFAULT_TRANSACTION_TTL_SECONDS + return False # almost guaranteed to not produce any results right now. + caller = ACCOUNTS[self.caller] + logger.debug( + f"checking {self.transaction_id} from {caller.key.account_id}") + tx_result = node.json_rpc('tx', + [self.transaction_id, caller.key.account_id]) + if self.is_success(tx_result): + self.outcome = tx_result + return True + return False + + def send(self, block_hash): + return (self.transaction_id, self.caller) + + def is_complete(self): + return self.outcome is not None + + def is_success(self, tx_result): + success = 'error' not in tx_result + if not success: + logger.debug( + f"transaction {self.transaction_id} for {self.caller} is not successful: {tx_result}" + ) + # only set TTL if we managed to check for success or failure... + self.ttl = self.expiration - time.time() + return success + + +class DeployFT(Transaction): + + def __init__(self, account, contract): + super().__init__() + self.account = account + self.contract = contract + + def send(self, node, block_hash): + account = ACCOUNTS[self.account] + logger.warning(f"deploying FT to {account.key.account_id}") + wasm_binary = utils.load_binary_file(self.contract) + tx = transaction.sign_deploy_contract_tx(account.key, wasm_binary, + account.use_nonce(), + block_hash) + result = node.send_tx(tx) + return (result["result"], self.account) + + +class TransferFT(Transaction): + + def __init__(self, ft, sender, recipient, how_much=1, tgas=300): + super().__init__() + self.ft = ft + self.sender = sender + self.recipient = recipient + self.how_much = how_much + self.tgas = tgas + + def send(self, node, block_hash): + (ft, sender, recipient + ) = ACCOUNTS[self.ft], ACCOUNTS[self.sender], ACCOUNTS[self.recipient] + logger.debug( + f"sending {self.how_much} FT from {sender.key.account_id} to {recipient.key.account_id}" + ) + args = { + "receiver_id": recipient.key.account_id, + "amount": str(int(self.how_much)), + } + tx = transaction.sign_function_call_tx( + sender.key, + ft.key.account_id, + "ft_transfer", + json.dumps(args).encode('utf-8'), + # About enough gas per call to fit N such transactions into an average block. + self.tgas * account.TGAS, + # Gotta attach exactly 1 yoctoNEAR according to NEP-141 to avoid calls from restricted access keys + 1, + sender.use_nonce(), + block_hash) + result = node.send_tx(tx) + return (result["result"], self.sender) + + +class TransferNear(Transaction): + + def __init__(self, sender, recipient_id, how_much=2.0): + super().__init__() + self.recipient_id = recipient_id + self.sender = sender + self.how_much = how_much + + def send(self, node, block_hash): + sender = ACCOUNTS[self.sender] + logger.debug( + f"sending {self.how_much} NEAR from {sender.key.account_id} to {self.recipient_id}" + ) + tx = transaction.sign_payment_tx(sender.key, self.recipient_id, + int(self.how_much * 1E24), + sender.use_nonce(), block_hash) + result = node.send_tx(tx) + return (result["result"], self.sender) + + +class CreateSubAccount(Transaction): + + def __init__(self, sender, sub, balance=50.0): + super().__init__() + self.sender = sender + self.sub = sub + self.balance = balance + + def send(self, node, block_hash): + sender = ACCOUNTS[self.sender] + sub = ACCOUNTS[self.sub] + new_account_id = f"{sub.key.account_id}.{sender.key.account_id}" + logger.debug(f"creating {new_account_id}") + tx = transaction.sign_create_account_with_full_access_key_and_balance_tx( + sender.key, sub.key.account_id, sub.key, int(self.balance * 1E24), + sender.use_nonce(), block_hash) + result = node.send_tx(tx) + return (result["result"], self.sender) + + +class InitFT(Transaction): + + def __init__(self, contract): + super().__init__() + self.contract = contract + + def send(self, node, block_hash): + contract = ACCOUNTS[self.contract] + args = json.dumps({ + "owner_id": contract.key.account_id, + "total_supply": str(10**33) + }) + tx = transaction.sign_function_call_tx(contract.key, + contract.key.account_id, + "new_default_meta", + args.encode('utf-8'), int(3E14), + 0, contract.use_nonce(), + block_hash) + result = node.send_tx(tx) + return (result["result"], self.contract) + + +class InitFTAccount(Transaction): + + def __init__(self, contract, account): + super().__init__() + self.contract = contract + self.account = account + + def send(self, node, block_hash): + contract, account = ACCOUNTS[self.contract], ACCOUNTS[self.account] + args = json.dumps({"account_id": account.key.account_id}) + tx = transaction.sign_function_call_tx(contract.key, + contract.key.account_id, + "storage_deposit", + args.encode('utf-8'), int(3E14), + int(1E23), contract.use_nonce(), + block_hash) + result = node.send_tx(tx) + return (result["result"], self.contract) + + +class TxQueue(multiprocessing.queues.Queue): + + def __init__(self, size, *args, **kwargs): + super().__init__(size, + ctx=multiprocessing.get_context(), + *args, + **kwargs) + self.pending = multiprocessing.Value(ctypes.c_ulong, 0) + + def add(self, tx): + with self.pending.get_lock(): + self.pending.value += 1 + self.put(tx) + + def complete(self): + with self.pending.get_lock(): + self.pending.value -= 1 + + +class Account: + + def __init__(self, key): + self.key = key + self.nonce = multiprocessing.Value(ctypes.c_ulong, 0) + + def refresh_nonce(self, node): + with self.nonce.get_lock(): + self.nonce.value = mocknet_helpers.get_nonce_for_key( + self.key, + addr=node.rpc_addr()[0], + port=node.rpc_addr()[1], + ) + + def use_nonce(self): + with self.nonce.get_lock(): + new_nonce = self.nonce.value + 1 + self.nonce.value = new_nonce + return new_nonce + + +def transaction_executor(nodes, tx_queue, accounts): + global ACCOUNTS + ACCOUNTS = accounts + last_block_hash_update = 0 + my_transactions = queue.SimpleQueue() + rng = random.Random() + while True: + node = rng.choice(nodes) + try: + now = time.time() + if now - last_block_hash_update >= 0.5: + block_hash = base58.b58decode(node.get_latest_block().hash) + last_block_hash_update = now + except (requests.exceptions.ReadTimeout, + requests.exceptions.ConnectionError): + continue + + while my_transactions.qsize() < MAX_INFLIGHT_TRANSACTIONS_PER_EXECUTOR: + try: + tx = tx_queue.get_nowait() + except queue.Empty: + break + # Send out the transaction immediately. + try: + tx.poll(node, block_hash) + except (requests.exceptions.ReadTimeout, + requests.exceptions.ConnectionError): + pass + my_transactions.put(tx) + + try: + tx = my_transactions.get_nowait() + except queue.Empty: + time.sleep(0.1) + continue + try: + poll_result = tx.poll(node, block_hash) + except (requests.exceptions.ReadTimeout, + requests.exceptions.ConnectionError): + my_transactions.put(tx) + continue + if not poll_result: + my_transactions.put(tx) + if tx.ttl != DEFAULT_TRANSACTION_TTL_SECONDS: + time.sleep(0.1) # don't spam RPC too hard... + else: + tx_queue.complete() + + +def main(): + parser = argparse.ArgumentParser(description='FT transfer benchmark.') + parser.add_argument('--fungible-token-wasm', + required=True, + help='Path to the compiled Fungible Token contract') + parser.add_argument( + '--setup-cluster', + default=False, + help= + 'Whether to start a dedicated cluster instead of connecting to an existing local node', + action='store_true') + parser.add_argument( + '--contracts', + default='0,2,4,6,8,a,c,e', + help= + 'Number of contract accounts, or alternatively list of subnames, separated by commas' + ) + parser.add_argument( + '--contract-key', + required='--setup-cluster' not in sys.argv, + help= + 'account to deploy contract to and use as source of NEAR for account creation' + ) + parser.add_argument('--accounts', + default=1000, + help='Number of accounts to use') + parser.add_argument( + '--no-account-topup', + default=False, + action='store_true', + help='Fill accounts with additional NEAR prior to testing') + parser.add_argument('--shards', default=10, help='number of shards') + parser.add_argument('--executors', + default=2, + help='number of transaction executors') + parser.add_argument('--tx-tgas', + default=30, + help='amount of Tgas to attach to each transaction') + args = parser.parse_args() + + logger.warning(f"SEED is {SEED}") + rng = random.Random(SEED) + + if args.setup_cluster: + config = cluster.load_config() + nodes = cluster.start_cluster( + 2, 0, args.shards, config, [["epoch_length", 100]], { + shard: { + "tracked_shards": list(range(args.shards)) + } for shard in range(args.shards + 1) + }) + if args.contract_key is None: + signer_key = nodes[0].signer_key + else: + signer_key = key.Key.from_json_file(args.contract_key) + + else: + nodes = [ + cluster.RpcNode("127.0.0.1", 3030), + ] + # The `nearup` localnet setup stores the keys in this directory. + key_path = args.contract_key + signer_key = key.Key.from_json_file(key_path) + + ACCOUNTS = [] + ACCOUNTS.append(Account(signer_key)) + ACCOUNTS[0].refresh_nonce(nodes[0]) + funding_account = 0 + start_of_accounts = len(ACCOUNTS) - 1 + contract_accounts = [] + + try: + for sub in sorted( + rng.sample(string.ascii_lowercase + string.digits, + k=int(args.contracts))): + funding_key = ACCOUNTS[funding_account].key + sub_key = key.Key(f"{sub}.{funding_key.account_id}", funding_key.pk, + funding_key.sk) + contract_accounts.append(len(ACCOUNTS)) + ACCOUNTS.append(Account(sub_key)) + except ValueError: + for sub in args.contracts.split(","): + funding_key = ACCOUNTS[funding_account].key + sub_key = key.Key(f"{sub}.{funding_key.account_id}", funding_key.pk, + funding_key.sk) + contract_accounts.append(len(ACCOUNTS)) + ACCOUNTS.append(Account(sub_key)) + + for i in range(int(args.accounts)): + keys = ed25519.create_keypair(entropy=rng.randbytes) + account_id = keys[1].to_bytes().hex() + sk = 'ed25519:' + base58.b58encode(keys[0].to_bytes()).decode('ascii') + pk = 'ed25519:' + base58.b58encode(keys[1].to_bytes()).decode('ascii') + ACCOUNTS.append(Account(key.Key(account_id, pk, sk))) + + executors = int(args.executors) + queue_size = 16 + max(MAX_INFLIGHT_TRANSACTIONS_PER_EXECUTOR, + int(args.accounts) * len(contract_accounts)) + tx_queue = TxQueue(queue_size) + subargs = ( + nodes, + tx_queue, + ACCOUNTS, + ) + for executor in range(executors): + multiprocessing.Process(target=transaction_executor, + args=subargs, + daemon=True).start() + + for contract_account in contract_accounts: + tx_queue.add(CreateSubAccount(funding_account, contract_account)) + wait_empty(tx_queue, "creating contract sub accounts") + for contract_account in contract_accounts: + ACCOUNTS[contract_account].refresh_nonce(nodes[0]) + tx_queue.add(DeployFT(contract_account, args.fungible_token_wasm)) + wait_empty(tx_queue, "deployment") + for contract_account in contract_accounts: + tx_queue.add(InitFT(contract_account)) + wait_empty(tx_queue, "contract initialization") + + if not args.no_account_topup: + for test_account in ACCOUNTS[start_of_accounts:]: + tx_queue.add( + TransferNear(funding_account, test_account.key.account_id, 2.0)) + wait_empty(tx_queue, "account creation and top-up") + + for contract_account in contract_accounts: + for test_account_idx in range(start_of_accounts, len(ACCOUNTS)): + # Initialize nonces for all real accounts. But we only want to do that for the first + # iteration... Otherwise there's a risk of a race. And O(n^2) doesn't help... + if contract_account == contract_accounts[0]: + ACCOUNTS[test_account_idx].refresh_nonce(nodes[0]) + tx_queue.add(InitFTAccount(contract_account, test_account_idx)) + wait_empty(tx_queue, "registeration of accounts with the FT contracts") + + for contract_account in contract_accounts: + for test_account_idx in range(start_of_accounts, len(ACCOUNTS)): + tx_queue.add( + TransferFT(contract_account, + contract_account, + test_account_idx, + how_much=1E8)) + wait_empty(tx_queue, "distribution of initial FT") + + transfers = 0 + while True: + sender_idx, receiver_idx = rng.sample(range(start_of_accounts, + len(ACCOUNTS)), + k=2) + ft_contract = rng.choice(contract_accounts) + tgas = int(args.tx_tgas) + tx_queue.add( + TransferFT(ft_contract, + sender_idx, + receiver_idx, + how_much=1, + tgas=tgas)) + transfers += 1 + if transfers % 10000 == 0: + logger.info( + f"{transfers} so far ({tx_queue.pending.value} pending)") + while tx_queue.pending.value >= MAX_INFLIGHT_TRANSACTIONS_PER_EXECUTOR * executors: + time.sleep(0.25) + + +def wait_empty(queue, why): + with queue.pending.get_lock(): + remaining = queue.pending.value + while remaining != 0: + logger.info(f"waiting for {why} ({remaining} remain)") + time.sleep(0.5) + with queue.pending.get_lock(): + remaining = queue.pending.value + logger.info(f"wait for {why} completed!") + + +if __name__ == "__main__": + main() diff --git a/pytest/tests/loadtest/locust/README.md b/pytest/tests/loadtest/locust/README.md new file mode 100644 index 000000000..302e69ac6 --- /dev/null +++ b/pytest/tests/loadtest/locust/README.md @@ -0,0 +1,169 @@ +# Locust based load testing + +TLDR: Use [locust](https://locust.io/) to generate transactions against a unc chain and produce statistics. + +Locust is a python library which we are using to send load to an RPC node. How +to set up the network under test is outside the scope of this document. This is +only about generating the load. + +## Install +```sh +pip3 install locust +# Run in framework directory. +pip3 install -r pytest/requirements.txt +``` + +*Note: You will need a working python3 / pip3 environment. While the code is +written in a backwards compatible way, modern OSs with modern python are +preferred. Completely independent of locust, you may run into problems with +PyOpenSSL with an old OS, `pip3 install pyopenssl --upgrade` may help if you see +error messages involving `X509_V_FLAG_CB_ISSUER_CHECK`.* + +## Run a first load test + +The load generator needs access to an account key with plenty of tokens. +For a local test setup, this works just fine. +```sh +# This assumes you are running against localnet +KEY=~/.near/localnet/node0/validator_key.json +``` + +For a quick demo, you can also run a localnet using [nearup](https://github.com/near/nearup). +```sh +nearup run localnet --binary-path ../framework/target/release/ --num-nodes 4 --num-shards 4 --override +``` + +Then to actually run it, this is the command. (Update ports and IP according to your localnet, nearup will print it.) +```sh +cd pytest/tests/loadtest/locust/ +locust -H 127.0.0.1:3030 \ + -f locustfiles/ft.py \ + --funding-key=$KEY +``` + +This will print a link to a web UI where the loadtest can be started and statistics & graphs can be observed. +But if you only want the pure numbers, it can also be run headless. +(add `--headless` and something like `-t 120s -u 1000` to the command to specify total time and max concurrent users) + +## Running with full load (multi-threaded or even multi-machine) + +Each locust process will only use a single core. This leads to unwanted +throttling on the load generator side if you try to use more than a couple +hundred of users. + +In the Locust UI, check the "Workers" tab to see CPU and memory usage. If this +approaches anything close to 100%, you should use more workers. + +Luckily, locust has the ability to swarm the load generation across many processes. +To use it, start one process with the `--master` argument and as many as you +like with `--worker`. (If they run on different machines, you also need to +provide `--master-host` and `--master-port`, if running on the same machine it +will work automagically.) + +Start the master: + +```sh +locust -H 127.0.0.1:3030 \ + -f locustfiles/ft.py \ + --funding-key=$KEY \ + --master +``` + +On the worker: + +```sh +# Increase soft limit of open files for the current OS user +# (each locust user opens a separate socket = file) +ulimit -S -n 100000 +# Run the worker, key must include the same account id as used on master +locust -H 127.0.0.1:3030 \ + -f locustfiles/ft.py \ + --funding-key=$KEY \ + --worker +``` + +Note that you have to start each worker process individually. But you can, for +example, use the `&` operator to spawn N workers in a single shell. + +```sh +for i in {1..16} +do + locust -H 127.0.0.1:3030 \ + -f locustfiles/ft.py \ + --funding-key=$KEY \ + --worker & +done +``` + +Use `pkill -P $$` to stop all workers spawned in the current shell. +Stopping the master will also terminate all workers. + +### Funding Key + +The funding key (`--funding-key=$KEY`) and the account associated with it are +used to pay for all on-chain transactions. When running in the distributed mode, +it will first create a funding account for each worker instance and those are +used to pay for transactions. + +As the load generator is currently configured, it will consume 1M NEAR from the +master funding account for each worker instance spawned. So, if you run with 64 +workers with 1000 users, it will consume 64M unc. It's recommended to have a +funding key with billions of unc. + +Also, the account name associated with this account should not bee too long. +Keep it below 20 characters, and it should be fine. The reason is that we create +sub accounts on this account, which become Sweat users. If the parent account +name is too long, children names will breach the 64 bytes max length. + +# Available load types + +Different locust files can be passed as an argument to `locust` to run a specific load test. +All available workloads can be found in `locustfiles` folder. + +Currently supported load types: + +| load type | file | args | description | +|---|---|---|---| +| Fungible Token | ft.py | (`--fungible-token-wasm $WASM_PATH`)
(`--num-ft-contracts $N`) | Creates `$N` FT contracts per worker, registers each user in one of them. Users transfer FTs between each other. | +| Social DB | social.py | (`--social-db-wasm $WASM_PATH`) | Creates a single instance of SocialDB and registers users to it. Users post messages and follow other users. (More workload TBD) | +| Congestion | congestion.py | (`--congestion-wasm $WASM_PATH`) | Creates a single instance of Congestion contract. Users run large and long transactions. | +| Sweat (normal load) | sweat.py | (`--sweat-wasm $WASM_PATH`) | Creates a single instance of the SWEAT contract. A mix of FT transfers and batch minting with batch sizes comparable to mainnet observations in summer 2023. | +| Sweat (storage stress test) | sweat.py | `--tags=storage-stress-test`
(`--sweat-wasm $WASM_PATH`) | Creates a single instance of the SWEAT contract. Sends maximally large batches to mint more tokens, thereby touching many storage nodes per receipt. This load will take a while to initialize enough Sweat users on chain. | + +## Notes on Storage Stress Test + +The Sweat based storage stress test is special. While the other workloads send +innocent traffic with many assumptions, this test pushes the storage accesses +per chunk to the limit. As such, it is a bit more fragile. + +### Slow Start + +First, you will notice that for several minutes after start, it will only be +registering new accounts to the Sweat contract on chain. You will see these +requests show up as "Init FT Account" in the statistics tab of the Locust UI. + +To make sure you are not waiting forever, you want to have enough locust users +per worker instance. For example, 100 users in one worker will be 100 times +faster than 100 users distributed over 100 workers. + +Once enough accounts have been registered, large batches of users get new steps +added, which translates to new tokens being minted for them. You will see them +as `Sweat record batch (stress test)` requests. + +When you restart your workers, they reset their known registered user accounts. +Hence on restart they add new accounts again. And you have to wait again. To +avoid this, you can stop and restart tests from within the UI. This way, they +will remember the account list and start the next test immediately, without long +setup. + + +### Master Key Requirements + +The `--funding-key` provided must always have enough balance to fund many users. +But this is even more extreme for this load, as we are creating many accounts +per worker. + +Also, the account name limits are even tighter for this load. Other loads +probably work with lengths up to 40 character, here it really has to be below 20 +characters or else we hit the log output limit when writing all the JSON events +for updated Sweat balances. diff --git a/pytest/tests/loadtest/locust/common/base.py b/pytest/tests/loadtest/locust/common/base.py new file mode 100644 index 000000000..8053333dc --- /dev/null +++ b/pytest/tests/loadtest/locust/common/base.py @@ -0,0 +1,832 @@ +from datetime import datetime, timedelta +from locust import User, events, runners +from retrying import retry +import abc +import base64 +import json +import base58 +import ctypes +import logging +import multiprocessing +import pathlib +import requests +import sys +import threading +import time +import typing +import unittest + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[4] / 'lib')) + +from account import TGAS +from common.sharding import AccountGenerator +from configured_logger import new_logger +import cluster +import key +import mocknet_helpers +import transaction +import utils + +DEFAULT_TRANSACTION_TTL = timedelta(minutes=30) +logger = new_logger(level=logging.WARN) + + +def is_key_error(exception): + return isinstance(exception, KeyError) + + +def is_tx_unknown_error(exception): + return isinstance(exception, TxUnknownError) + + +class Account: + + def __init__(self, key): + self.key = key + self.current_nonce = multiprocessing.Value(ctypes.c_ulong, 0) + + # Race condition: maybe the account was created but the RPC interface + # doesn't display it yet, which returns an empty result and raises a + # `KeyError`. + # (not quite sure how this happens but it did happen to me on localnet) + @retry(wait_exponential_multiplier=500, + wait_exponential_max=10000, + stop_max_attempt_number=5, + retry_on_exception=is_key_error) + def refresh_nonce(self, node): + with self.current_nonce.get_lock(): + self.current_nonce.value = mocknet_helpers.get_nonce_for_key( + self.key, + addr=node.rpc_addr()[0], + port=node.rpc_addr()[1], + logger=logger, + ) + + def use_nonce(self): + with self.current_nonce.get_lock(): + new_nonce = self.current_nonce.value + 1 + self.current_nonce.value = new_nonce + return new_nonce + + def fast_forward_nonce(self, ak_nonce): + with self.current_nonce.get_lock(): + self.current_nonce.value = max(self.current_nonce.value, + ak_nonce + 1) + + +class Transaction: + """ + A transaction future. + """ + + ID = 0 + + def __init__(self): + self.id = Transaction.ID + Transaction.ID += 1 + + # The transaction id hash + # + # str if the transaction has been submitted and may eventually conclude. + # FIXME: this is currently not set in some cases + self.transaction_id = None + + @abc.abstractmethod + def sign_and_serialize(self, block_hash) -> bytes: + """ + Each transaction class is supposed to define this method to serialize and + sign the transaction and return the raw message to be sent. + """ + + @abc.abstractmethod + def sender_account(self) -> Account: + """ + Account of the sender that signs the tx, which must be known to map + the tx-result request to the right shard. + """ + + +class FunctionCall(Transaction): + + def __init__(self, + sender: Account, + receiver_id: str, + method: str, + balance: int = 0): + super().__init__() + self.sender = sender + self.receiver_id = receiver_id + self.method = method + # defensive cast to avoid serialization bugs when float balance is + # provided despite type hint + self.balance = int(balance) + + @abc.abstractmethod + def args(self) -> typing.Union[dict, typing.List[dict]]: + """ + Function call arguments to be serialized and sent with the call. + Return a single dict for `FunctionCall` but a list of dict for `MultiFunctionCall`. + """ + + def sign_and_serialize(self, block_hash) -> bytes: + return transaction.sign_function_call_tx( + self.sender.key, self.receiver_id, self.method, + json.dumps(self.args()).encode('utf-8'), 300 * TGAS, self.balance, + self.sender.use_nonce(), block_hash) + + def sender_account(self) -> Account: + return self.sender + + +class MultiFunctionCall(FunctionCall): + """ + Batches multiple function calls into a single transaction. + """ + + def __init__(self, + sender: Account, + receiver_id: str, + method: str, + balance: int = 0): + super().__init__(sender, receiver_id, method, balance=balance) + + def sign_and_serialize(self, block_hash) -> bytes: + all_args = self.args() + gas = 300 * TGAS // len(all_args) + + def create_action(args): + return transaction.create_function_call_action( + self.method, + json.dumps(args).encode('utf-8'), gas, int(self.balance)) + + actions = [create_action(args) for args in all_args] + return transaction.sign_and_serialize_transaction( + self.receiver_id, self.sender.use_nonce(), actions, block_hash, + self.sender.key.account_id, self.sender.key.decoded_pk(), + self.sender.key.decoded_sk()) + + +class Deploy(Transaction): + + def __init__(self, account, contract, name): + super().__init__() + self.account = account + self.contract = contract + self.name = name + + def sign_and_serialize(self, block_hash) -> bytes: + account = self.account + logger.info(f"deploying {self.name} to {account.key.account_id}") + wasm_binary = utils.load_binary_file(self.contract) + return transaction.sign_deploy_contract_tx(account.key, wasm_binary, + account.use_nonce(), + block_hash) + + def sender_account(self) -> Account: + return self.account + + +class CreateSubAccount(Transaction): + + def __init__(self, sender, sub_key, balance: float = 50): + super().__init__() + self.sender = sender + self.sub_key = sub_key + self.balance = balance + + def sign_and_serialize(self, block_hash) -> bytes: + sender = self.sender + sub = self.sub_key + logger.debug(f"creating {sub.account_id}") + return transaction.sign_create_account_with_full_access_key_and_balance_tx( + sender.key, sub.account_id, sub, int(self.balance * 1E24), + sender.use_nonce(), block_hash) + + def sender_account(self) -> Account: + return self.sender + + +class AddFullAccessKey(Transaction): + + def __init__(self, parent: Account, new_key: key.Key): + super().__init__() + self.sender = parent + self.new_key = new_key + + def sign_and_serialize(self, block_hash) -> bytes: + action = transaction.create_full_access_key_action( + self.new_key.decoded_pk()) + return transaction.sign_and_serialize_transaction( + self.sender.key.account_id, self.sender.use_nonce(), + [action], block_hash, self.sender.key.account_id, + self.sender.key.decoded_pk(), self.sender.key.decoded_sk()) + + def sender_account(self) -> Account: + return self.sender + + +class NearNodeProxy: + """ + Wrapper around a RPC node connection that tracks requests on locust. + """ + + def __init__(self, environment): + self.request_event = environment.events.request + [url, port] = environment.host.rsplit(":", 1) + self.node = cluster.RpcNode(url, port) + self.session = requests.Session() + + def send_tx_retry(self, tx: Transaction, locust_name) -> dict: + """ + Send a transaction and retry until it succeeds + + This method retries no matter the kind of error, but it tries to be + smart about what to do depending on the error. + + Expected error: UnknownTransactionError means TX has not been executed yet. + Expected error: InvalidNonceError means we are using an outdated nonce. + Other errors: Probably bugs in the test setup (e.g. invalid signer). + """ + while True: + meta = self.send_tx(tx, locust_name) + error = meta["exception"] + if error is None: + return meta + elif isinstance(error, InvalidNonceError): + logger.debug( + f"{error} for {tx.sender_account().key.account_id}, updating nonce and retrying" + ) + tx.sender_account().fast_forward_nonce(error.ak_nonce) + else: + logger.warn( + f"transaction {tx.transaction_id} failed: {error}, retrying in 0.25s" + ) + time.sleep(0.25) + + def send_tx(self, tx: Transaction, locust_name: str) -> dict: + """ + Send a transaction and return the result, no retry attempted. + """ + block_hash = self.final_block_hash() + signed_tx = tx.sign_and_serialize(block_hash) + + meta = self.new_locust_metadata(locust_name) + start_perf_counter = time.perf_counter() + + try: + try: + # To get proper errors on invalid transaction, we need to use sync api first + result = self.post_json( + "broadcast_tx_commit", + [base64.b64encode(signed_tx).decode('utf8')]) + evaluate_rpc_result(result.json()) + except TxUnknownError as err: + # This means we time out in one way or another. + # In that case, the stateless transaction validation was + # successful, we can now use async API without missing errors. + submit_raw_response = self.post_json( + "broadcast_tx_async", + [base64.b64encode(signed_tx).decode('utf8')]) + meta["response_length"] = len(submit_raw_response.text) + submit_response = submit_raw_response.json() + # extract transaction ID from response, it should be "{ "result": "id...." }" + if not "result" in submit_response: + meta["exception"] = RpcError(message="Didn't get a TX ID", + details=submit_response) + meta["response"] = submit_response.content + else: + tx.transaction_id = submit_response["result"] + # using retrying lib here to poll until a response is ready + self.poll_tx_result(meta, tx) + except NearError as err: + logging.warn(f"marking an error {err.message}, {err.details}") + meta["exception"] = err + + meta["response_time"] = (time.perf_counter() - + start_perf_counter) * 1000 + + # Track request + response in Locust + self.request_event.fire(**meta) + return meta + + def final_block_hash(self): + return base58.b58decode( + self.node.get_final_block()['result']['header']['hash']) + + def new_locust_metadata(self, locust_name: str): + return { + "request_type": "unc-rpc", + "name": locust_name, + "start_time": time.time(), + "response_time": 0, # overwritten later with end-to-end time + "response_length": 0, # overwritten later + "response": None, # overwritten later + "context": {}, # not used right now + "exception": None, # maybe overwritten later + } + + def post_json(self, method: str, params: typing.List[str]): + j = { + "method": method, + "params": params, + "id": "dontcare", + "jsonrpc": "2.0" + } + return self.session.post(url="http://%s:%s" % self.node.rpc_addr(), + json=j) + + @retry(wait_fixed=500, + stop_max_delay=DEFAULT_TRANSACTION_TTL / timedelta(milliseconds=1), + retry_on_exception=is_tx_unknown_error) + def poll_tx_result(self, meta: dict, tx: Transaction): + params = [tx.transaction_id, tx.sender_account().key.account_id] + # poll for tx result, using "EXPERIMENTAL_tx_status" which waits for + # all receipts to finish rather than just the first one, as "tx" would do + result_response = self.post_json("EXPERIMENTAL_tx_status", params) + # very verbose, but very useful to see what's happening when things are stuck + logger.debug( + f"polling, got: {result_response.status_code} {result_response.json()}" + ) + + try: + meta["response"] = evaluate_rpc_result(result_response.json()) + except: + # Store raw response to improve error-reporting. + meta["response"] = result_response.content + raise + + def account_exists(self, account_id: str) -> bool: + return "error" not in self.node.get_account(account_id, do_assert=False) + + def prepare_account(self, account: Account, parent: Account, balance: float, + msg: str) -> bool: + """ + Creates the account if it doesn't exist and refreshes the nonce. + """ + exists = self.account_exists(account.key.account_id) + if not exists: + self.send_tx_retry( + CreateSubAccount(parent, account.key, balance=balance), msg) + account.refresh_nonce(self.node) + return exists + + def prepare_accounts(self, + accounts: typing.List[Account], + parent: Account, + balance: float, + msg: str, + timeout: timedelta = timedelta(minutes=3)): + """ + Creates accounts if they don't exist and refreshes their nonce. + Accounts must share the parent account. + + This implementation attempts on-chain parallelization, hence it should + be faster than calling `prepare_account` in a loop. + + Note that error-handling in this variant isn't quite as smooth. Errors + that are only reported by the sync API of RPC nodes will not be caught + here. Instead, we do a best-effort retry and stop after a fixed timeout. + """ + + # To avoid blocking, each account goes though a FSM independently. + # + # FSM outline: + # + # [INIT] -----------------(account exists already)------------- + # | | + # V V + # [TO_CREATE] --(post tx)--> [INFLIGHT] --(poll result)--> [TO_REFRESH] + # ^ | | + # | | | + # |--(fail to submit)<-- (refresh) + # | + # V + # [DONE] + # + to_create: typing.List[Account] = [] + inflight: typing.List[Transaction, dict, Account] = [] + to_refresh: typing.List[Account] = [] + + for account in accounts: + if self.account_exists(account.key.account_id): + to_refresh.append(account) + else: + to_create.append(account) + + block_hash = self.final_block_hash() + start = datetime.now() + while len(to_create) + len(to_refresh) + len(inflight) > 0: + logger.info( + f"preparing {len(accounts)} accounts, {len(to_create)} to create, {len(to_refresh)} to refresh, {len(inflight)} inflight" + ) + if start + timeout < datetime.now(): + raise SystemExit("Account preparation timed out") + try_again = [] + for account in to_create: + meta = self.new_locust_metadata(msg) + tx = CreateSubAccount(parent, account.key, balance=balance) + signed_tx = tx.sign_and_serialize(block_hash) + submit_raw_response = self.post_json( + "broadcast_tx_async", + [base64.b64encode(signed_tx).decode('utf8')]) + meta["response_length"] = len(submit_raw_response.text) + submit_response = submit_raw_response.json() + if not "result" in submit_response: + # something failed, let's not block, just try again later + logger.debug( + f"couldn't submit account creation TX, got {submit_raw_response.text}" + ) + try_again.append(account) + else: + tx.transaction_id = submit_response["result"] + inflight.append((tx, meta, account)) + to_create = try_again + + # while requests are processed on-chain, refresh nonces for existing accounts + for account in to_refresh: + account.refresh_nonce(self.node) + to_refresh.clear() + + # poll all pending requests + for tx, meta, account in inflight: + # Using retrying lib here to poll until a response is ready. + # This is blocking on a single request, but we expect requests + # to be processed in order so it shouldn't matter. + self.poll_tx_result(meta, tx) + meta["response_time"] = (time.time() - + meta["start_time"]) * 1000 + to_refresh.append(account) + # Locust tracking + self.request_event.fire(**meta) + inflight.clear() + + logger.info(f"done preparing {len(accounts)} accounts") + + +class NearUser(User): + abstract = True + id_counter = 0 + INIT_BALANCE = 100.0 + funding_account: Account + + @classmethod + def get_next_id(cls): + cls.id_counter += 1 + return cls.id_counter + + @classmethod + def generate_account_id(cls, account_generator, id) -> str: + return account_generator.random_account_id( + cls.funding_account.key.account_id, f'_user{id}') + + def __init__(self, environment): + super().__init__(environment) + assert self.host is not None, "unc user requires the RPC node address" + self.node = NearNodeProxy(environment) + self.id = NearUser.get_next_id() + user_suffix = f"{self.id}_run{environment.parsed_options.run_id}" + self.account_id = NearUser.generate_account_id( + environment.account_generator, user_suffix) + + def on_start(self): + """ + Called once per user, creating the account on chain + """ + self.account = Account(key.Key.from_random(self.account_id)) + if not self.node.account_exists(self.account_id): + self.send_tx_retry( + CreateSubAccount(NearUser.funding_account, + self.account.key, + balance=NearUser.INIT_BALANCE)) + self.account.refresh_nonce(self.node.node) + + def send_tx(self, tx: Transaction, locust_name="generic send_tx"): + """ + Send a transaction and return the result, no retry attempted. + """ + return self.node.send_tx(tx, locust_name)["response"] + + def send_tx_retry(self, + tx: Transaction, + locust_name="generic send_tx_retry"): + """ + Send a transaction and retry until it succeeds + """ + return self.node.send_tx_retry(tx, locust_name=locust_name)["response"] + + +class NearError(Exception): + + def __init__(self, message, details): + """ + The `message` is used in locust to aggregate errors and is also displayed in the UI. + The `details` are logged as additional information in the console. + """ + self.message = message + self.details = details + super().__init__(message) + + +class RpcError(NearError): + + def __init__(self, message="RPC returned an error", details=None): + super().__init__(message, details) + + +class TxUnknownError(RpcError): + + def __init__( + self, + message="RPC does not know the result of this TX, probably it is not executed yet" + ): + super().__init__(message=message) + + +class InvalidNonceError(RpcError): + + def __init__( + self, + used_nonce, + ak_nonce, + ): + super().__init__( + message="Nonce too small", + details= + f"Tried to use nonce {used_nonce} but access key nonce is {ak_nonce}" + ) + self.ak_nonce = ak_nonce + + +class TxError(NearError): + + def __init__(self, + status, + message="Transaction to receipt conversion failed"): + super().__init__(message, status) + + +class ReceiptError(NearError): + + def __init__(self, status, receipt_id, message="Receipt execution failed"): + super().__init__(message, f"id={receipt_id} {status}") + + +class SmartContractPanic(ReceiptError): + + def __init__(self, status, receipt_id, message="Smart contract panicked"): + super().__init__(status, receipt_id, message) + + +class FunctionExecutionError(ReceiptError): + + def __init__(self, + status, + receipt_id, + message="Smart contract function execution failed"): + super().__init__(status, receipt_id, message) + + +def evaluate_rpc_result(rpc_result): + """ + Take the json RPC response and translate it into success + and failure cases. Failures are raised as exceptions. + """ + if "error" in rpc_result: + err_name = rpc_result["error"]["cause"]["name"] + # The sync API returns "UNKNOWN_TRANSACTION" after a timeout. + # The async API returns "TIMEOUT_ERROR" if the tx was not accepted in the chain after 10s. + # In either case, the identical transaction should be retried. + if err_name in ["UNKNOWN_TRANSACTION", "TIMEOUT_ERROR"]: + raise TxUnknownError(err_name) + # When reusing keys across test runs, the nonce is higher than expected. + elif err_name == "INVALID_TRANSACTION": + err_description = rpc_result["error"]["data"]["TxExecutionError"][ + "InvalidTxError"] + if "InvalidNonce" in err_description: + raise InvalidNonceError( + err_description["InvalidNonce"]["tx_nonce"], + err_description["InvalidNonce"]["ak_nonce"]) + raise RpcError(details=rpc_result["error"]) + + result = rpc_result["result"] + transaction_outcome = result["transaction_outcome"] + if not "SuccessReceiptId" in transaction_outcome["outcome"]["status"]: + raise TxError(transaction_outcome["outcome"]["status"]) + + receipt_outcomes = result["receipts_outcome"] + for receipt in receipt_outcomes: + # For each receipt, we get + # `{ "outcome": { ..., "status": { : "..." } } }` + # and the key for `ExecutionStatusView` tells us whether it was successful + status = list(receipt["outcome"]["status"].keys())[0] + + if status == "Unknown": + raise ReceiptError(receipt["outcome"], + receipt["id"], + message="Unknown receipt result") + if status == "Failure": + failure = receipt["outcome"]["status"]["Failure"] + panic_msg = as_smart_contract_panic_message(failure) + if panic_msg: + raise SmartContractPanic(receipt["outcome"], + receipt["id"], + message=panic_msg) + exec_failed = as_execution_error(failure) + if exec_failed: + raise FunctionExecutionError(receipt["outcome"], + receipt["id"], + message=exec_failed) + raise ReceiptError(receipt["outcome"], receipt["id"]) + if not status in ["SuccessReceiptId", "SuccessValue"]: + raise ReceiptError(receipt["outcome"], + receipt["id"], + message="Unexpected status") + + return result + + +def as_action_error(failure: dict) -> typing.Optional[dict]: + return failure.get("ActionError", None) + + +def as_function_call_error(failure: dict) -> typing.Optional[dict]: + action_error = as_action_error(failure) + if action_error and "FunctionCallError" in action_error["kind"]: + return action_error["kind"]["FunctionCallError"] + return None + + +def as_execution_error(failure: dict) -> typing.Optional[dict]: + function_call_error = as_function_call_error(failure) + if function_call_error and "ExecutionError" in function_call_error: + return function_call_error["ExecutionError"] + return None + + +def as_smart_contract_panic_message(failure: dict) -> typing.Optional[str]: + execution_error = as_execution_error(failure) + known_prefix = "Smart contract panicked: " + if execution_error and execution_error.startswith(known_prefix): + return execution_error[len(known_prefix):] + return None + + +def init_account_generator(parsed_options): + if parsed_options.shard_layout_file is not None: + with open(parsed_options.shard_layout_file, 'r') as f: + shard_layout = json.load(f) + elif parsed_options.shard_layout_chain_id is not None: + if parsed_options.shard_layout_chain_id not in ['mainnet', 'testnet']: + sys.exit( + f'unexpected --shard-layout-chain-id: {parsed_options.shard_layout_chain_id}' + ) + + shard_layout = { + "V1": { + "fixed_shards": [], + "boundary_accounts": [ + "aurora", "aurora-0", "kkuuue2akv_1630967379.near" + ], + "shards_split_map": [[0, 1, 2, 3]], + "to_parent_shard_map": [0, 0, 0, 0], + "version": 1 + } + } + else: + shard_layout = { + "V0": { + "num_shards": 1, + "version": 0, + }, + } + + return AccountGenerator(shard_layout) + + +# called once per process before user initialization +def do_on_locust_init(environment): + node = NearNodeProxy(environment) + + master_funding_key = key.Key.from_json_file( + environment.parsed_options.funding_key) + master_funding_account = Account(master_funding_key) + + if not node.account_exists(master_funding_account.key.account_id): + raise SystemExit( + f"account {master_funding_account.key.account_id} of the provided master funding key does not exist" + ) + master_funding_account.refresh_nonce(node.node) + + environment.account_generator = init_account_generator( + environment.parsed_options) + funding_account = None + # every worker needs a funding account to create its users, eagerly create them in the master + if isinstance(environment.runner, runners.MasterRunner): + num_funding_accounts = environment.parsed_options.max_workers + funding_balance = 10000 * NearUser.INIT_BALANCE + + def create_account(id): + account_id = f"funds_worker_{id}.{master_funding_account.key.account_id}" + return Account(key.Key.from_seed_testonly(account_id)) + + funding_accounts = [ + create_account(id) for id in range(num_funding_accounts) + ] + node.prepare_accounts(funding_accounts, master_funding_account, + funding_balance, "create funding account") + funding_account = master_funding_account + elif isinstance(environment.runner, runners.WorkerRunner): + worker_id = environment.runner.worker_index + worker_account_id = f"funds_worker_{worker_id}.{master_funding_account.key.account_id}" + worker_key = key.Key.from_seed_testonly(worker_account_id) + funding_account = Account(worker_key) + funding_account.refresh_nonce(node.node) + elif isinstance(environment.runner, runners.LocalRunner): + funding_account = master_funding_account + else: + raise SystemExit( + f"unexpected runner class {environment.runner.__class__.__name__}") + + NearUser.funding_account = funding_account + environment.master_funding_account = master_funding_account + + +INIT_DONE = threading.Event() + + +@events.init.add_listener +def on_locust_init(environment, **kwargs): + do_on_locust_init(environment) + INIT_DONE.set() + + +# Add custom CLI args here, will be available in `environment.parsed_options` +@events.init_command_line_parser.add_listener +def _(parser): + parser.add_argument( + "--funding-key", + required=True, + help="account to use as source of NEAR for account creation") + parser.add_argument( + "--max-workers", + type=int, + required=False, + default=16, + help="How many funding accounts to generate for workers") + parser.add_argument( + "--shard-layout-file", + required=False, + help="file containing a shard layout JSON object for the target chain") + parser.add_argument( + "--shard-layout-chain-id", + required=False, + help= + "chain ID whose shard layout we should consult when generating account IDs. Convenience option to avoid using --shard-layout-file for mainnet and testnet" + ) + parser.add_argument( + "--run-id", + default="", + help="Unique index to append to static account ids. " + "Change between runs if you need a new state. Keep at default if you want to reuse the old state" + ) + + +class TestEvaluateRpcResult(unittest.TestCase): + + def test_smart_contract_panic(self): + input = """{ + "result": { + "transaction_outcome": { "outcome": {"status": { "SuccessReceiptId": "" } } }, + "receipts_outcome": [ { + "id": "J3EVpgJXgLQ5f33ammArtewYBAg3KmDgVf47HtapBtua", + "outcome": { + "logs": [], + "receipt_ids": [ + "HxL55zV91tEgpPKg8QPkoWo53Ue1x9yhfRQTgdfQ11mc", + "467VVuaNz9igj74Zs9wFpeYmtfpRorbZugJKDHymCN1Q" + ], + "gas_burnt": 2658479078129, + "tokens_burnt": "265847907812900000000", + "executor_id": "vy0zxd_ft.funds_worker_3.node0", + "status": { + "Failure": { + "ActionError": { + "index": 0, + "kind": { + "FunctionCallError": { + "ExecutionError": "Smart contract panicked: The account doesnt have enough balance" + } + } + } + } + }, + "metadata": { + "version": 3, + "gas_profile": [] + } + } + } ] + } + }""" + self.assertRaises(SmartContractPanic, evaluate_rpc_result, + json.loads(input)) diff --git a/pytest/tests/loadtest/locust/common/congestion.py b/pytest/tests/loadtest/locust/common/congestion.py new file mode 100644 index 000000000..781d1372c --- /dev/null +++ b/pytest/tests/loadtest/locust/common/congestion.py @@ -0,0 +1,102 @@ +import json +import pathlib +import sys + +from locust import events, runners + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[4] / "lib")) + +import account +import common.base as base +import key +import transaction + + +class ComputeSha256(base.FunctionCall): + """Transaction with a large input size.""" + + def __init__( + self, + contract_account_id: str, + sender: base.Account, + size_bytes: int, + ): + super().__init__(sender, contract_account_id, "ext_sha256") + self.contract_account_id = contract_account_id + self.sender = sender + self.size_bytes = size_bytes + + def args(self) -> dict: + return ["a" * self.size_bytes] + + def sender_account(self) -> base.Account: + return self.sender + + +class ComputeSum(base.Transaction): + """Large computation that consumes a specified amount of gas.""" + + def __init__( + self, + contract_account_id: str, + sender: base.Account, + usage_tgas: int, + ): + super().__init__() + self.contract_account_id = contract_account_id + self.sender = sender + self.usage_tgas = usage_tgas + + def sign_and_serialize(self, block_hash) -> bytes: + return transaction.sign_function_call_tx( + self.sender.key, + self.contract_account_id, + "sum_n", + # 1000000 is around 12 TGas. + ((1000000 * self.usage_tgas) // 12).to_bytes(8, byteorder="little"), + 300 * account.TGAS, + 0, + self.sender.use_nonce(), + block_hash, + ) + + def sender_account(self) -> base.Account: + return self.sender + + +@events.init.add_listener +def on_locust_init(environment, **kwargs): + base.INIT_DONE.wait() + # `master_funding_account` is the same on all runners, allowing to share a + # single instance of congestion contract. + funding_account = environment.master_funding_account + environment.congestion_account_id = f"congestion.{funding_account.key.account_id}" + + # Only create congestion contract on master. + if isinstance(environment.runner, runners.WorkerRunner): + return + + node = base.NearNodeProxy(environment) + funding_account = base.NearUser.funding_account + funding_account.refresh_nonce(node.node) + + account = base.Account( + key.Key.from_seed_testonly(environment.congestion_account_id)) + node.prepare_account(account, funding_account, 50000, + "create contract account") + node.send_tx_retry( + base.Deploy( + account, + environment.parsed_options.congestion_wasm, + "Congestion", + ), "deploy congestion contract") + + +# Congestion specific CLI args +@events.init_command_line_parser.add_listener +def _(parser): + parser.add_argument( + "--congestion-wasm", + default="res/congestion.wasm", + help="Path to the compiled congestion contract", + ) diff --git a/pytest/tests/loadtest/locust/common/ft.py b/pytest/tests/loadtest/locust/common/ft.py new file mode 100644 index 000000000..9630638f5 --- /dev/null +++ b/pytest/tests/loadtest/locust/common/ft.py @@ -0,0 +1,197 @@ +import random +import string +import sys +import pathlib +import typing +from locust import events + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[4] / 'lib')) + +import key +from common.base import Account, Deploy, NearNodeProxy, NearUser, FunctionCall, INIT_DONE + + +class FTContract: + # NEAR balance given to contracts, doesn't have to be much since users are + # going to pay for storage + INIT_BALANCE = NearUser.INIT_BALANCE + + def __init__(self, account: Account, ft_distributor: Account, code: str): + self.account = account + self.ft_distributor = ft_distributor + self.registered_users = [] + self.code = code + + def install(self, node: NearNodeProxy, parent: Account): + """ + Deploy and initialize the contract on chain. + The account is created if it doesn't exist yet. + """ + existed = node.prepare_account(self.account, parent, + FTContract.INIT_BALANCE, + "create contract account") + if not existed: + node.send_tx_retry(Deploy(self.account, self.code, "FT"), + "deploy ft") + self.init_contract(node) + + def init_contract(self, node: NearNodeProxy): + node.send_tx_retry(InitFT(self.account), "init ft") + + def register_user(self, user: NearUser): + user.send_tx_retry(InitFTAccount(self.account, user.account), + locust_name="Init FT Account") + user.send_tx_retry(TransferFT(self.account, + self.ft_distributor, + user.account_id, + how_much=10**8), + locust_name="FT Funding") + self.registered_users.append(user.account_id) + + def register_passive_user(self, node: NearNodeProxy, account: Account): + """ + Passive users are only used as receiver, not as signer. + """ + node.send_tx_retry(InitFTAccount(self.account, account), + locust_name="Init FT Account") + self.registered_users.append(account.key.account_id) + + def random_receiver(self, sender: str) -> str: + return self.random_receivers(sender, 1)[0] + + def random_receivers(self, sender: str, num) -> typing.List[str]: + rng = random.Random() + receivers = rng.sample(self.registered_users, num) + # Sender must be != receiver but maybe there is no other registered user + # yet, so we just send to the ft_distributor account which is registered + # from the start + return list( + map(lambda a: a.replace(sender, self.ft_distributor.key.account_id), + receivers)) + + def create_passive_users(self, + num: int, + node: NearNodeProxy, + parent: Account, + max_account_id_len=64): + """ + Create on-chain accounts and register them as FT users. + Note that these are not locust users and they are not able to sign + transactions. They are only used as targets of transactions and as a + side-effect they also increase the state size of the contract. + """ + prefix_len = max_account_id_len - len(parent.key.account_id) - 1 + assert prefix_len > 4, f"user key {parent.key.account_id} is too long" + chars = string.ascii_lowercase + string.digits + + def create_account(): + prefix = ''.join(random.choices(chars, k=prefix_len)) + account_id = f"{prefix}.{parent.key.account_id}" + return Account(key.Key.from_seed_testonly(account_id)) + + accounts = [create_account() for _ in range(num)] + node.prepare_accounts(accounts, + parent, + balance=0.3, + msg="create passive user") + # TODO: this could also be done in parallel, actually in very simple + # ways since there are no nonce conflicts (transactions are signed by + # different users) + for account in accounts: + self.register_passive_user(node, account) + + +class TransferFT(FunctionCall): + + def __init__(self, + ft: Account, + sender: Account, + recipient_id: str, + how_much=1): + # Attach exactly 1 yoctoNEAR according to NEP-141 to avoid calls from restricted access keys + super().__init__(sender, ft.key.account_id, "ft_transfer", balance=1) + self.ft = ft + self.sender = sender + self.recipient_id = recipient_id + self.how_much = how_much + + def args(self) -> dict: + return { + "receiver_id": self.recipient_id, + "amount": str(int(self.how_much)), + } + + def sender_account(self) -> Account: + return self.sender + + +class InitFT(FunctionCall): + + def __init__(self, contract: Account): + super().__init__(contract, contract.key.account_id, "new_default_meta") + self.contract = contract + + def args(self) -> dict: + return { + "owner_id": self.contract.key.account_id, + "total_supply": str(10**33) + } + + def sender_account(self) -> Account: + return self.contract + + +class InitFTAccount(FunctionCall): + + def __init__(self, contract: Account, account: Account): + super().__init__(account, + contract.key.account_id, + "storage_deposit", + balance=int(1E23)) + self.contract = contract + self.account = account + + def args(self) -> dict: + return {"account_id": self.account.key.account_id} + + def sender_account(self) -> Account: + return self.account + + +@events.init.add_listener +def on_locust_init(environment, **kwargs): + INIT_DONE.wait() + node = NearNodeProxy(environment) + ft_contract_code = environment.parsed_options.fungible_token_wasm + num_ft_contracts = environment.parsed_options.num_ft_contracts + funding_account = NearUser.funding_account + parent_id = funding_account.key.account_id + + funding_account.refresh_nonce(node.node) + + environment.ft_contracts = [] + # TODO: Create accounts in parallel + for i in range(num_ft_contracts): + account_id = environment.account_generator.random_account_id( + parent_id, '_ft') + contract_key = key.Key.from_random(account_id) + ft_account = Account(contract_key) + ft_contract = FTContract(ft_account, ft_account, ft_contract_code) + ft_contract.install(node, funding_account) + environment.ft_contracts.append(ft_contract) + + +# FT specific CLI args +@events.init_command_line_parser.add_listener +def _(parser): + parser.add_argument("--fungible-token-wasm", + default="res/fungible_token.wasm", + help="Path to the compiled Fungible Token contract") + parser.add_argument( + "--num-ft-contracts", + type=int, + required=False, + default=4, + help= + "How many different FT contracts to spawn from this worker (FT contracts are never shared between workers)" + ) diff --git a/pytest/tests/loadtest/locust/common/sharding.py b/pytest/tests/loadtest/locust/common/sharding.py new file mode 100644 index 000000000..39895b2cf --- /dev/null +++ b/pytest/tests/loadtest/locust/common/sharding.py @@ -0,0 +1,361 @@ +"""Account name generator + +Provides tools to generate account names that are distributed evenly across the shards. + +For account naming rules and conventions see https://nomicon.io/DataStructures/Account +""" + +import os +import sys +import random +import re +import unittest + + +def char_range(lower, upper, upper_inclusive=True): + l = ord(lower) + u = ord(upper) + 1 if upper_inclusive else ord(upper) + return [chr(i) for i in range(l, u)] + + +def alpha_num_str(length): + return ''.join( + random.choices(char_range('0', '9') + char_range('a', 'z'), k=length)) + + +def random_char_below(upper, upper_inclusive): + if upper >= 'a': + chars = char_range('0', '9') + char_range('a', upper, upper_inclusive) + elif upper == '_': + chars = char_range('0', '9') + if upper_inclusive: + chars.append('_') + elif upper >= '1': + chars = char_range('0', upper, upper_inclusive) + elif upper == '0': + # here just return a - if upper_inclusive is False, since that's called + # only when we want to finish a prefix of the upper string. Otherwise, don't bother + # with returning a - to avoid handling account ID validity in that case + if upper_inclusive: + chars = ['0'] + else: + chars = ['-'] + else: + assert upper == '-' or upper == '.' + return upper + return random.choice(chars) + + +def random_char_above(lower): + if lower >= 'a': + chars = char_range(lower, 'z') + elif lower == '_': + chars = ['_'] + char_range('a', 'z') + elif lower >= '0': + chars = char_range(lower, '9') + char_range('a', 'z') + elif lower == '.': + chars = ['.'] + char_range('0', '9') + char_range('a', 'z') + else: + assert lower == '-' + chars = ['-'] + char_range(lower, '9') + char_range('a', 'z') + return random.choice(chars) + + +def random_valid_char(lower, upper, upper_inclusive=True): + if lower is None and upper is None: + chars = char_range('0', '9') + char_range('a', 'z') + return random.choice(chars) + if lower is None: + return random_char_below(upper, upper_inclusive) + if upper is None: + return random_char_above(lower) + + assert (upper_inclusive and lower <= upper) or (not upper_inclusive and + lower < upper) + + if lower >= 'a': + assert lower <= 'z' + chars = char_range(lower, upper, upper_inclusive) + elif lower == '_': + chars = ['_'] + if upper != '_': + chars += char_range('a', upper, upper_inclusive) + elif lower >= '0': + if upper <= '9': + chars = char_range(lower, upper, upper_inclusive) + elif upper == '_': + chars = char_range(lower, '9') + if upper_inclusive: + chars += ['_'] + else: + chars = char_range(lower, '9') + char_range('a', upper, + upper_inclusive) + elif lower == '.': + if upper == '.': + # upper_inclusive must be false here because of the assert above + chars = ['.'] + elif upper <= '9': + assert upper >= '0' + chars = ['.'] + char_range('0', upper, upper_inclusive) + elif upper == '_': + chars = ['.'] + char_range('0', '9') + if upper_inclusive: + chars += '_' + else: + assert upper >= 'a' and upper <= 'z' + chars = ['.'] + char_range('0', '9') + char_range( + 'a', upper, upper_inclusive) + else: + assert lower == '-' + if upper == '-' or upper == '.': + chars = ['-'] + # if upper == '.', we don't include the '.' in the list of chars even if upper_inclusive + # is True, because we want to make it easy for ourselves in the case we're generating a prefix + # between b-0 and b.0, where if we choose a '.' in position 2, we're out of luck because the next + # char is a 0 + elif upper <= '9': + assert upper >= '0' + chars = ['-'] + char_range('0', upper, upper_inclusive) + elif upper == '_': + chars = ['-'] + char_range('0', '9') + if upper_inclusive: + chars += '_' + else: + assert upper >= 'a' and upper <= 'z' + chars = ['-'] + char_range('0', '9') + char_range( + 'a', upper, upper_inclusive) + return random.choice(chars) + + +def char_at(s, i): + if s is None or i >= len(s): + return None + return s[i] + + +# when picking a random char between two chars, we'll consider it as counting toward the number +# of free chars we wanted to generate if the range is large. We could try and be clever and add fractional +# "freeness" but it's not a big deal, and the initial choice of free_chars=6 is arbitrary anyway +def char_range_is_large(l, u): + return (l is None or l < '4') and (u is None or u > 't') + + +# if we so far generated a prefix exactly equal to the upper boundary string up to its second to last char, +# handle it specially here, because we have to make sure we generate a prefix strictly lower than it +def finish_upper(lower, upper, prefix, free_chars, free_length): + if len(upper) > 1: + # e.g. lower = "aurora" and upper = "aurora-0". In that case "aurora" is the only valid account ID in the shard + assert upper[-2] != '-' or upper[ + -1] != '0', f'Cannot build account ID prefix less than {upper}' + else: + assert upper[ + 0] > '0', f'Cannot build account ID prefix less than {upper}' + + l = char_at(lower, len(upper) - 1) + c = random_valid_char(l, upper[-1], upper_inclusive=False) + prefix += c + if char_range_is_large(l, upper[-1]): + free_chars += 1 + if l is not None and c != l: + # we've generated a prefix strictly greater than lower, so no need to consider it anymore + lower = None + extra_chars_needed = c in ['.', '-', '_'] or lower is not None + if lower is not None: + for i in range(len(upper), len(lower)): + c = random_char_above(lower[i]) + prefix += c + if char_range_is_large(lower[i], None): + free_chars += 1 + if c != lower[i]: + extra_chars_needed = False + break + if extra_chars_needed: + extra_len = max(1, free_length - free_chars) + else: + extra_len = max(0, free_length - free_chars) + if extra_len > 0: + prefix += alpha_num_str(extra_len) + return prefix + + +# generate a string that's a valid account ID between lower and upper +# free_length refers to the number of characters in the result that are free +# to be chosen from a large range. For example, if lower='aaa', upper='aaa0', +# then we want a string of length 4 + free_length, because the first 4 characters +# are constricted +# TODO: This could hopefully be made simpler by successively appending either an +# alphanumeric character or one of ['-', '.', '_'] followed by an alphanumeric character, +# choosing one of the ones that keeps us between the bounds each time. +# See https://github.com/utnet-org/utility/pull/9194#pullrequestreview-1488492798 +def random_prefix_between(lower, upper, free_length=6): + assert lower is None or upper is None or lower < upper, (lower, upper) + + # 1 shard case + if lower is None and upper is None: + return alpha_num_str(free_length) + + prefix = '' + free_chars = 0 + max_len = 0 + if upper is not None: + max_len = len(upper) + if lower is not None: + max_len = max(max_len, len(lower)) + + for i in range(max_len): + l = char_at(lower, i) + u = char_at(upper, i) + + if l is not None and l == u: + prefix += l + continue + + if l is None and u is None: + # we get here when lower is shorter than upper, and we have generated + # a string equal to lower. Just add anything at the end + extra_len = max(1, free_length - free_chars) + prefix += alpha_num_str(extra_len) + return prefix + + if upper is not None and i == len(upper) - 1: + return finish_upper(lower, upper, prefix, free_chars, free_length) + + c = random_valid_char(l, u) + prefix += c + if char_range_is_large(l, u): + free_chars += 1 + if c != u and c != l: + if free_chars < free_length: + prefix += alpha_num_str(free_length - free_chars) + return prefix + + if c != u: + upper = None + if c != l: + lower = None + + if free_chars >= free_length: + # here only one of lower/upper is not None, meaning we have a prefix of it so far + # if it's a prefix of upper, it's strictly smaller, since we call finish_upper() before hitting + # the end of upper in this loop. + # If it's a prefix of lower, we add chars here to make it larger. Just add z's + # to be lazy and make the code simpler, since we already got the number of free chars we wanted + if lower is not None: + prefix += 'z' + for j in range(i + 1, len(lower)): + if lower[j] != 'z': + break + prefix += 'z' + return prefix + + assert lower is not None and upper is None + # here we happened to generate a prefix equal to lower, but still strictly less than upper + # no matter what we do, so just add anything to the end. + extra_len = max(1, free_length - free_chars) + prefix += alpha_num_str(extra_len) + return prefix + + +def random_account_between(base_name, suffix, lower, upper, free_length=6): + prefix = random_prefix_between(lower, upper, free_length) + return f'{prefix}{suffix}.{base_name}' + + +# Given a shard layout, generates accounts distributed evenly across the shards +class AccountGenerator: + + def __init__(self, shard_layout): + assert len(shard_layout) == 1 + assert 'V0' in shard_layout or 'V1' in shard_layout + + self.shard_map = {} + + # If the shard layout is V0, we can just skip this, and random_account_id() + # will see an empty self.shard_map and generate a random prefix, which should + # distribute the accounts evenly across shards in that case + shard_layout_v1 = shard_layout.get('V1') + if shard_layout_v1 is not None: + # taken from a comment in core/account-id/src/lib.rs + account_regex = re.compile( + r'^(([a-z\d]+[-_])*[a-z\d]+\.)*([a-z\d]+[-_])*[a-z\d]+$') + # doesn't actually matter that much to get the shard IDs right, since we're just + # picking one at random, and not actually doing anything with the shard ID itself, but + # add the right offset to the shard IDs below just for cleanliness, and in case we + # want to print out shard IDs or something + shard_offset = len(shard_layout_v1['fixed_shards']) + accounts = shard_layout_v1['boundary_accounts'] + if len(accounts) == 0: + self.shard_map[shard_offset] = (None, None) + return + + if accounts[0] != '00': + self.shard_map[shard_offset] = (None, accounts[0]) + + for i, account_id in enumerate(accounts): + # should of course be true, but assert it since we let the user pass a shard layout file, so + # verify it at least a little bit, along with the check below that the list is increasing + assert account_regex.fullmatch(account_id) is not None + + if i + 1 < len(accounts): + next_account = accounts[i + 1] + assert account_id < next_account + + # like "aurora" and "aurora-0", in this case we can't generate any accounts there + if next_account != account_id + '-0': + self.shard_map[shard_offset + i + 1] = (account_id, + next_account) + else: + if account_id != 'z' * 64: + self.shard_map[shard_offset + i + 1] = (account_id, + None) + + # This should be true no matter what boundary accounts we have, but just sanity check it + assert len(self.shard_map) > 0 + + # generate a valid subaccount ID of `base_name`` between lower and upper, with the first part of + # the account ID ending with `suffix` + # TODO: check the resulting length somewhere. Right now it's not checked and could be too large + # if `base_name` is large + def random_account_id(self, base_name, suffix): + if len(self.shard_map) == 0: + return random_account_between(base_name, suffix, None, None) + else: + shard_id, (lower, + upper) = random.choice(list(self.shard_map.items())) + return random_account_between(base_name, suffix, lower, upper) + + +class TestRandomAccount(unittest.TestCase): + + def test_random_account(self): + account_regex = re.compile( + r'^(([a-z\d]+[-_])*[a-z\d]+\.)*([a-z\d]+[-_])*[a-z\d]+$') + test_cases = [ + (None, None), + ('aa', None), + (None, 'aa'), + ('aa', 'bb'), + ('56', 'bb'), + ('a-1', 'a-1-1'), + ('a-0', 'a-01'), + ('a-0', 'a-00'), + ('b-b', 'bb'), + ('aa', 'aa000'), + ('b-0', 'b.0'), + ] + for (lower, upper) in test_cases: + # sanity check the test case itself + if lower is not None: + assert account_regex.fullmatch(lower) is not None + if upper is not None: + assert account_regex.fullmatch(upper) is not None + + for _ in range(10): + account_id = random_account_between('foo.near', '_ft', lower, + upper) + assert account_regex.fullmatch(account_id) is not None, ( + account_id, lower, upper) + if lower is not None: + assert account_id >= lower, (account_id, lower, upper) + if upper is not None: + assert account_id < upper, (account_id, lower, upper) diff --git a/pytest/tests/loadtest/locust/common/social.py b/pytest/tests/loadtest/locust/common/social.py new file mode 100644 index 000000000..08acb1ae2 --- /dev/null +++ b/pytest/tests/loadtest/locust/common/social.py @@ -0,0 +1,291 @@ +from abc import abstractmethod +import json +import sys +import pathlib +import typing +import unittest + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[4] / 'lib')) +sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) + +import transaction + +from account import TGAS, UNC_BASE +import key +from common.base import Account, Deploy, NearNodeProxy, Transaction, FunctionCall, INIT_DONE +from locust import events, runners +from transaction import create_function_call_action + + +class SocialDbSet(FunctionCall): + + def __init__(self, contract_id: str, sender: Account): + super().__init__(sender, contract_id, "set") + self.contract_id = contract_id + self.sender = sender + + def sender_account(self) -> Account: + return self.sender + + +class SubmitPost(SocialDbSet): + + def __init__(self, contract_id: str, sender: Account, content: str): + super().__init__(contract_id, sender) + self.content = content + + def args(self) -> dict: + return social_post_args(self.sender.key.account_id, self.content) + + +class Follow(SocialDbSet): + + def __init__(self, contract_id: str, sender: Account, + follow_list: typing.List[str]): + super().__init__(contract_id, sender) + self.follow_list = follow_list + + def args(self) -> dict: + follow_list = self.follow_list + sender = self.sender.key.account_id + return social_follow_args(sender, follow_list) + + +class InitSocialDB(Transaction): + + def __init__(self, contract: Account): + super().__init__() + self.contract = contract + + def sign_and_serialize(self, block_hash) -> bytes: + # Call the #[init] function, no arguments + call_new_action = create_function_call_action("new", "", 100 * TGAS, 0) + + # Set the status to "Live" to enable normal usage + args = json.dumps({"status": "Live"}).encode('utf-8') + call_set_status_action = create_function_call_action( + "set_status", args, 100 * TGAS, 0) + + # Batch the two actions above into one transaction + nonce = self.contract.use_nonce() + key = self.contract.key + return transaction.sign_and_serialize_transaction( + key.account_id, nonce, [call_new_action, call_set_status_action], + block_hash, key.account_id, key.decoded_pk(), key.decoded_sk()) + + def sender_account(self) -> Account: + return self.contract + + +class InitSocialDbAccount(FunctionCall): + """ + Send initial storage balance to ensure the account can use social DB. + + Technically, we could also rely on lazy initialization and just send enough + balance with each request. But a typical user sends balance ahead of time. + """ + + def __init__(self, contract_id: str, account: Account): + super().__init__(account, + contract_id, + "storage_deposit", + balance=1 * UNC_BASE) + self.contract_id = contract_id + self.account = account + + def args(self) -> dict: + return {"account_id": self.account.key.account_id} + + def sender_account(self) -> Account: + return self.account + + +def social_db_build_index_obj(key_list_pairs: dict) -> dict: + """ + JSON serializes the key - value-list pairs to be included in a SocialDB set message. + + To elaborate a bit more, SocialDB expects for example + ```json + "index": { "graph": value_string } + ``` + where value_string = + ```json + "[{\"key\":\"follow\",\"value\":{\"type\":\"follow\",\"accountId\":\"pagodaplatform.near\"}}]" + ``` + So it's really JSON nested inside a JSON string. + And worse, the nested JSON is always a list of objects with "key" and "value" fields. + This method unfolds this format from a leaner definition, using a list of pairs to + define each `value_string`. + A dict instead of a list of tuples doesn't work because keys can be duplicated. + """ + + def serialize_values(values: typing.List[typing.Tuple[str, dict]]): + return json.dumps([{"key": k, "value": v} for k, v in values]) + + return { + key: serialize_values(values) for key, values in key_list_pairs.items() + } + + +def social_db_set_msg(sender: str, values: dict, index: dict) -> dict: + """ + Construct a SocialDB `set` function argument. + + The output will be of the form: + ```json + { + "data": { + "key1": value1, + "key3": value2, + "key4": value3, + "index": { + "index_key1": "[{\"index_key_1_key_A\":\"index_key_1_value_A\"}]", + "index_key2": "[{\"index_key_2_key_A\":\"index_key_2_value_A\"},{\"index_key_2_key_B\":\"index_key_2_value_B\"}]", + } + } + } + ``` + """ + updates = values.copy() + updates["index"] = social_db_build_index_obj(index) + msg = {"data": {sender: updates}} + return msg + + +def social_follow_args(sender: str, follow_list: typing.List[str]) -> dict: + follow_map = {} + graph = [] + notify = [] + for user in follow_list: + follow_map[user] = "" + graph.append(("follow", {"type": "follow", "accountId": user})) + notify.append((user, {"type": "follow"})) + + values = { + "graph": { + "follow": follow_map + }, + } + index = {"graph": graph, "notify": notify} + return social_db_set_msg(sender, values, index) + + +def social_post_args(sender: str, text: str) -> dict: + values = {"post": {"main": json.dumps({"type": "md", "text": text})}} + index = {"post": [("main", {"type": "md"})]} + msg = social_db_set_msg(sender, values, index) + return msg + + +class TestSocialDbSetMsg(unittest.TestCase): + + def test_follow(self): + sender = "alice.near" + follow_list = ["bob.near"] + parsed_msg = social_follow_args(sender, follow_list) + expected_msg = { + "data": { + "alice.near": { + "graph": { + "follow": { + "bob.near": "" + } + }, + "index": { + "graph": + "[{\"key\": \"follow\", \"value\": {\"type\": \"follow\", \"accountId\": \"bob.near\"}}]", + "notify": + "[{\"key\": \"bob.near\", \"value\": {\"type\": \"follow\"}}]" + } + } + } + } + self.maxDiff = 2000 # print large diffs + self.assertEqual(parsed_msg, expected_msg) + + def test_mass_follow(self): + sender = "alice.near" + follow_list = ["bob.near", "caroline.near", "david.near"] + parsed_msg = social_follow_args(sender, follow_list) + expected_msg = { + "data": { + "alice.near": { + "graph": { + "follow": { + "bob.near": "", + "caroline.near": "", + "david.near": "", + } + }, + "index": { + "graph": + "[{\"key\": \"follow\", \"value\": {\"type\": \"follow\", \"accountId\": \"bob.near\"}}," + " {\"key\": \"follow\", \"value\": {\"type\": \"follow\", \"accountId\": \"caroline.near\"}}," + " {\"key\": \"follow\", \"value\": {\"type\": \"follow\", \"accountId\": \"david.near\"}}]", + "notify": + "[{\"key\": \"bob.near\", \"value\": {\"type\": \"follow\"}}," + " {\"key\": \"caroline.near\", \"value\": {\"type\": \"follow\"}}," + " {\"key\": \"david.near\", \"value\": {\"type\": \"follow\"}}]" + } + } + } + } + self.maxDiff = 2000 # print large diffs + self.assertEqual(parsed_msg, expected_msg) + + def test_post(self): + sender = "alice.near" + text = "#Title\n\nbody" + parsed_msg = social_post_args(sender, text) + expected_msg = { + "data": { + "alice.near": { + "post": { + "main": + "{\"type\": \"md\", \"text\": \"#Title\\n\\nbody\"}" + }, + "index": { + "post": + "[{\"key\": \"main\", \"value\": {\"type\": \"md\"}}]" + } + } + } + } + self.maxDiff = 2000 # print large diffs + self.assertEqual(parsed_msg, expected_msg) + + +@events.init.add_listener +def on_locust_init(environment, **kwargs): + INIT_DONE.wait() + # `master_funding_account` is the same on all runners, allowing to share a + # single instance of SocialDB in its `social` sub account + funding_account = environment.master_funding_account + environment.social_account_id = f"social{environment.parsed_options.run_id}.{funding_account.key.account_id}" + + # Create SocialDB account, unless we are a worker, in which case the master already did it + if not isinstance(environment.runner, runners.WorkerRunner): + social_contract_code = environment.parsed_options.social_db_wasm + contract_key = key.Key.from_seed_testonly(environment.social_account_id) + social_account = Account(contract_key) + + node = NearNodeProxy(environment) + existed = node.prepare_account(social_account, funding_account, 50000, + "create contract account") + if not existed: + node.send_tx_retry( + Deploy(social_account, social_contract_code, "Social DB"), + "deploy socialDB contract") + node.send_tx_retry(InitSocialDB(social_account), + "init socialDB contract") + + +# Social specific CLI args +@events.init_command_line_parser.add_listener +def _(parser): + parser.add_argument( + "--social-db-wasm", + default="res/social_db.wasm", + help= + "Path to the compiled SocialDB contract, get it from https://github.com/NearSocial/social-db/tree/aa7fafaac92a7dd267993d6c210246420a561370/res" + ) diff --git a/pytest/tests/loadtest/locust/common/sweat.py b/pytest/tests/loadtest/locust/common/sweat.py new file mode 100644 index 000000000..88d9d0368 --- /dev/null +++ b/pytest/tests/loadtest/locust/common/sweat.py @@ -0,0 +1,193 @@ +import typing +from common.ft import FTContract, InitFTAccount +from common.base import Account, NearNodeProxy, NearUser, FunctionCall, MultiFunctionCall, INIT_DONE +import locust +import sys +import pathlib +from locust import events +from collections import namedtuple + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[4] / 'lib')) + +import key + +RecipientSteps = namedtuple('RecipientSteps', ['account', 'steps']) + + +class SweatContract(FTContract): + + def __init__(self, main_account: Account, oracle_account: Account, + code: str): + super().__init__(main_account, oracle_account, code) + self.oracle = oracle_account + + def init_contract(self, node: NearNodeProxy): + node.send_tx_retry(InitSweat(self.account), "init sweat") + # Unlike FT initialization that starts with a total supply and assigns + # it to the user, the sweat main account doesn't start with tokens. We + # need to register an account and then mint the tokens, so that it can + # distribute them later. + node.send_tx_retry(InitFTAccount(self.account, self.ft_distributor), + locust_name="Init Sweat Account") + self.register_oracle(node, self.oracle.key.account_id) + self.top_up(node, self.ft_distributor.key.account_id) + + def top_up(self, node: NearNodeProxy, receiver_id: str): + """ + Adds a large amount of tokens to an account. + Note: This should only be called on the master runner, as it requires + the private key of the sweat contract account. (Multiple runners using + this leads to frequent nonce invalidations.) + """ + node.send_tx_retry( + SweatMint(self.account, receiver_id, 1_000_000_000_000), + "top up sweat") + + def register_oracle(self, node: NearNodeProxy, oracle_id: str): + """ + Register an account as oracle to give it the power to mint tokens for steps. + """ + node.send_tx_retry(SweatAddOracle(self.account, oracle_id), + "add sweat oracle") + + +class InitSweat(FunctionCall): + + def __init__(self, sweat_account: Account): + super().__init__(sweat_account, sweat_account.key.account_id, "new") + + def args(self) -> dict: + # Technical details about Sweat contract initialization: + # + # A postfix is used by the smart contract to decide whether or not to + # hash an account id. It's an optimization that makes storage keys for + # implicit accounts shorter, while preserving short keys of named + # accounts. + # + # More specifically, any account id that matches .* will be + # stored in the trie normally, like in a unmodified FT contract. Any + # other account ids are hashed. + # + # As an example, the postfix can be `.u.sweat.testnet`. + # + # Source code for reference: + # https://github.com/sweatco/unc-sdk-rs/blob/af6ba3cb75e0bbfc26e346e61aa3a0d1d7f5ac7b/unc-contract-standards/src/fungible_token/core_impl.rs#L249-L259 + # + # Here we don't provide a postfix, so everything will be hashed. This is + # fine for new contracts we create. And when we reuse a contract, we + # won't need to initialise it at all. + return {"postfix": None} + + +class SweatAddOracle(FunctionCall): + """ + Oracle accounts are allowed to mint new tokens and can only be added by the + account id of the contract itself. + """ + + def __init__(self, sweat_account: Account, oracle_id: str): + super().__init__(sweat_account, sweat_account.key.account_id, + "add_oracle") + self.oracle_id = oracle_id + + def args(self) -> dict: + return {"account_id": self.oracle_id} + + +class SweatMint(FunctionCall): + """ + A call to `tge_mint`. + Token Generation Event (TGE) was day 0 when SWEAT launched. + This is the transaction to get initial balance into accounts. + """ + + def __init__(self, sweat: Account, user_id: str, amount: int): + super().__init__(sweat, sweat.key.account_id, "tge_mint") + self.user_id = user_id + self.amount = amount + + def args(self) -> dict: + return { + "account_id": self.user_id, + "amount": f"{self.amount}", + } + + +class SweatMintBatch(MultiFunctionCall): + """ + A call to `record_batch`. + Mints new tokens for walked steps for a batch of users. + Might get split into multiple function calls to avoid log output limits. + """ + + def __init__(self, sweat_id: str, oracle: Account, + recipient_step_pairs: typing.List[RecipientSteps]): + super().__init__(oracle, sweat_id, "record_batch") + self.recipient_step_pairs = recipient_step_pairs + + def args(self) -> typing.List[dict]: + # above a threshold, we hit the log output limit of 16kB + # this depends a bit on the exact account id names + chunk_len = 150 + chunks = [ + self.recipient_step_pairs[s:s + chunk_len] + for s in range(0, len(self.recipient_step_pairs), chunk_len) + ] + + return [{"steps_batch": chunk} for chunk in chunks] + + +@events.init.add_listener +def on_locust_init(environment, **kwargs): + INIT_DONE.wait() + node = NearNodeProxy(environment) + worker_id = getattr(environment.runner, "worker_index", "_master") + run_id = environment.parsed_options.run_id + + funding_account = NearUser.funding_account + funding_account.refresh_nonce(node.node) + sweat_contract_code = environment.parsed_options.sweat_wasm + sweat_account_id = f"sweat{run_id}.{environment.master_funding_account.key.account_id}" + oracle_account_id = worker_oracle_id(worker_id, run_id, + environment.master_funding_account) + + sweat_account = Account(key.Key.from_seed_testonly(sweat_account_id)) + oracle_account = Account(key.Key.from_seed_testonly(oracle_account_id)) + + environment.sweat = SweatContract(sweat_account, oracle_account, + sweat_contract_code) + + # Create Sweat contract, unless we are a worker, in which case the master already did it + if not isinstance(environment.runner, locust.runners.WorkerRunner): + node.prepare_account(oracle_account, environment.master_funding_account, + FTContract.INIT_BALANCE, "create contract account") + environment.sweat.install(node, environment.master_funding_account) + + # on master, register oracles for workers + if isinstance(environment.runner, locust.runners.MasterRunner): + num_oracles = int(environment.parsed_options.max_workers) + oracle_accounts = [ + Account( + key.Key.from_seed_testonly( + worker_oracle_id(id, run_id, + environment.master_funding_account))) + for id in range(num_oracles) + ] + node.prepare_accounts(oracle_accounts, + environment.master_funding_account, 100000, + "create contract account") + for oracle in oracle_accounts: + id = oracle.key.account_id + environment.sweat.register_oracle(node, id) + environment.sweat.top_up(node, id) + + +def worker_oracle_id(worker_id, run_id, funding_account): + return f"sweat{run_id}_oracle{worker_id}.{funding_account.key.account_id}" + + +@events.init_command_line_parser.add_listener +def _(parser): + parser.add_argument("--sweat-wasm", + default="res/sweat.wasm", + help="Path to the compiled Sweat contract") diff --git a/pytest/tests/loadtest/locust/download_contracts.sh b/pytest/tests/loadtest/locust/download_contracts.sh new file mode 100755 index 000000000..45579a0d7 --- /dev/null +++ b/pytest/tests/loadtest/locust/download_contracts.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# +# Downloads the WASM contracts necessary for all workloads and stores them in "res" folder. + +cd res +wget https://raw.githubusercontent.com/NearSocial/social-db/master/res/social_db_release.wasm -O social_db.wasm +wget https://raw.githubusercontent.com/sweatco/sweat-near/main/res/sweat.wasm -O sweat.wasm +ln -s ../../../../../runtime/unc-test-contracts/res/fungible_token.wasm fungible_token.wasm +ln -s ../../../../../runtime/unc-test-contracts/res/backwards_compatible_rs_contract.wasm congestion.wasm diff --git a/pytest/tests/loadtest/locust/locustfiles/congestion.py b/pytest/tests/loadtest/locust/locustfiles/congestion.py new file mode 100644 index 000000000..fd5e504ac --- /dev/null +++ b/pytest/tests/loadtest/locust/locustfiles/congestion.py @@ -0,0 +1,38 @@ +""" +A workload that generates congestion. +""" + +import logging +import pathlib +import sys + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[4] / 'lib')) + +from configured_logger import new_logger +from locust import between, task +from common.base import NearUser +from common.congestion import ComputeSha256, ComputeSum + +logger = new_logger(level=logging.WARN) + + +class CongestionUser(NearUser): + """ + Runs a resource-heavy workload that is likely to cause congestion. + """ + wait_time = between(1, 3) # random pause between transactions + + @task + def compute_sha256(self): + self.send_tx(ComputeSha256(self.contract_account_id, self.account, + 100000), + locust_name="SHA256, 100 KiB") + + @task + def compute_sum(self): + self.send_tx(ComputeSum(self.contract_account_id, self.account, 250), + locust_name="Sum, 250 TGas") + + def on_start(self): + super().on_start() + self.contract_account_id = self.environment.congestion_account_id diff --git a/pytest/tests/loadtest/locust/locustfiles/ft.py b/pytest/tests/loadtest/locust/locustfiles/ft.py new file mode 100644 index 000000000..1df43c058 --- /dev/null +++ b/pytest/tests/loadtest/locust/locustfiles/ft.py @@ -0,0 +1,39 @@ +""" +A workload with Fungible Token operations. +""" + +import logging +import pathlib +import random +import sys + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[4] / 'lib')) + +from configured_logger import new_logger +from locust import between, task +from common.base import NearUser +from common.ft import TransferFT + +logger = new_logger(level=logging.WARN) + + +class FTTransferUser(NearUser): + """ + Registers itself on an FT contract in the setup phase, then just sends FTs to + random users. + """ + wait_time = between(1, 3) # random pause between transactions + + @task + def ft_transfer(self): + receiver = self.ft.random_receiver(self.account_id) + tx = TransferFT(self.ft.account, self.account, receiver, how_much=1) + self.send_tx(tx, locust_name="FT transfer") + + def on_start(self): + super().on_start() + self.ft = random.choice(self.environment.ft_contracts) + self.ft.register_user(self) + logger.debug( + f"{self.account_id} ready to use FT contract {self.ft.account.key.account_id}" + ) diff --git a/pytest/tests/loadtest/locust/locustfiles/social.py b/pytest/tests/loadtest/locust/locustfiles/social.py new file mode 100644 index 000000000..07f2b4e41 --- /dev/null +++ b/pytest/tests/loadtest/locust/locustfiles/social.py @@ -0,0 +1,67 @@ +""" +A workload that simulates SocialDB traffic. +""" + +import logging +import pathlib +import random +import sys + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[4] / 'lib')) + +from configured_logger import new_logger +from locust import between, task +from common.base import NearUser +from common.social import Follow, InitSocialDbAccount, SubmitPost + +logger = new_logger(level=logging.WARN) + + +class SocialDbUser(NearUser): + """ + Registers itself on near.social in the setup phase, then starts posting, + following, and liking posts. + """ + wait_time = between(1, 3) # random pause between transactions + registered_users = [] + + @task + def follow(self): + users_to_follow = [random.choice(SocialDbUser.registered_users)] + self.send_tx(Follow(self.contract_account_id, self.account, + users_to_follow), + locust_name="Social Follow") + + @task + def post(self): + seed = random.randrange(2**32) + len = random.randrange(100, 1000) + post = self.generate_post(len, seed) + self.send_tx(SubmitPost(self.contract_account_id, self.account, post), + locust_name="Social Post") + + def on_start(self): + super().on_start() + self.contract_account_id = self.environment.social_account_id + + self.send_tx(InitSocialDbAccount(self.contract_account_id, + self.account), + locust_name="Init Social Account") + logger.debug( + f"user {self.account_id} ready to use SocialDB on {self.contract_account_id}" + ) + + SocialDbUser.registered_users.append(self.account_id) + + def generate_post(self, length: int, seed: int) -> str: + sample_quotes = [ + "Despite the constant negative press covfefe", + "Sorry losers and haters, but my I.Q. is one of the highest - and you all know it! Please don't feel so stupid or insecure, it's not your fault", + "Windmills are the greatest threat in the US to both bald and golden eagles. Media claims fictional 'global warming' is worse.", + ] + quote = sample_quotes[seed % len(sample_quotes)] + post = f"I, {self.account.key.account_id}, cannot resists to declare with pride: \n_{quote}_" + while length > len(post): + post = f"{post}\nI'll say it again: \n**{quote}**" + + return post[:length] diff --git a/pytest/tests/loadtest/locust/locustfiles/sweat.py b/pytest/tests/loadtest/locust/locustfiles/sweat.py new file mode 100644 index 000000000..863950e26 --- /dev/null +++ b/pytest/tests/loadtest/locust/locustfiles/sweat.py @@ -0,0 +1,104 @@ +""" +A workload with Sweat operations. + +Sweat is a slightly modified version of the standard fungible token contract. + - The lookup map is slightly modified to make storage keys shorter + - There is a record_batch method which can update many users' balances at once + - The "oracles" concept was added, a list of privileged accounts that can mint tokens + +This workload is similar to the FT workload with 2 major differences: + - Single account with larger state (larger state still TODO) + - Periodic batches that adds steps (mints new tokens) +""" + +from common.sweat import RecipientSteps, SweatContract, SweatMintBatch +from common.ft import TransferFT +from common.base import Account, AddFullAccessKey, NearUser +from locust import between, tag, task +import copy +import logging +import pathlib +import random +import sys + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[4] / 'lib')) + +from configured_logger import new_logger +import key + +logger = new_logger(level=logging.WARN) + + +class SweatUser(NearUser): + """ + Registers itself on an FT contract in the setup phase, then just sends Sweat to + random users. + + Also includes a task to mint and distribute tokens in batches. + """ + wait_time = between(1, 3) # random pause between transactions + + @task(3) + def ft_transfer(self): + receiver = self.sweat.random_receiver(self.account_id) + tx = TransferFT(self.sweat.account, self.account, receiver) + self.send_tx(tx, locust_name="Sweat transfer") + + @task(1) + def record_single_batch(self): + rng = random.Random() + # just around the log limit + batch_size = min(rng.randint(100, 150), + len(self.sweat.registered_users)) + receivers = self.sweat.random_receivers(self.account_id, batch_size) + tx = SweatMintBatch(self.sweat.account.key.account_id, self.oracle, [ + RecipientSteps(account_id, steps=rng.randint(1000, 3000)) + for account_id in receivers + ]) + self.send_tx(tx, locust_name="Sweat record batch") + + @tag("storage-stress-test") + @task + def record_batch_of_large_batches(self): + # create more sweat users to allow for a decent record_batch size + while len(self.sweat.registered_users) < 1000: + # creating 20 accounts in parallel, about 200 should fit in a chunk + # but we don't want to assume we get to use all the chunk space for + # ourself. Also, other SweatUsers will be in the same loop and at + # some point the local CPU becomes a bottleneck, too. + self.sweat.create_passive_users( + 20, + self.node, + self.account, + # protocol enforced max length is 64 but we want shorter names to + # not hit the log limits too soon + max_account_id_len=48) + + rng = random.Random() + # just around 300Tgas + batch_size = rng.randint(700, 750) + receivers = self.sweat.random_receivers(self.account_id, batch_size) + tx = SweatMintBatch( + self.sweat.account.key.account_id, self.oracle, + [[account_id, rng.randint(1000, 3000)] for account_id in receivers]) + self.send_tx(tx, locust_name="Sweat record batch (stress test)") + + def on_start(self): + super().on_start() + # We have one oracle account per worker. Sharing a single access key + # means potential conflicts in nonces when we mint new tokens through + # batches. Hence, let's add a new access key to the oracle account for + # each sweat user. + self.sweat = self.environment.sweat + oracle = self.environment.sweat.oracle + user_oracle_key = key.Key.from_random(oracle.key.account_id) + self.send_tx_retry(AddFullAccessKey(oracle, user_oracle_key), + "add user key to oracle") + + self.oracle = Account(user_oracle_key) + self.oracle.refresh_nonce(self.node.node) + + self.sweat.register_user(self) + logger.debug( + f"{self.account_id} ready to use Sweat contract {self.sweat.account.key.account_id}" + ) diff --git a/pytest/tests/loadtest/locust/res/congestion.wasm b/pytest/tests/loadtest/locust/res/congestion.wasm new file mode 120000 index 000000000..79da668f0 --- /dev/null +++ b/pytest/tests/loadtest/locust/res/congestion.wasm @@ -0,0 +1 @@ +../../../../../runtime/near-test-contracts/res/backwards_compatible_rs_contract.wasm \ No newline at end of file diff --git a/pytest/tests/loadtest/locust/res/fungible_token.wasm b/pytest/tests/loadtest/locust/res/fungible_token.wasm new file mode 120000 index 000000000..b6b17669a --- /dev/null +++ b/pytest/tests/loadtest/locust/res/fungible_token.wasm @@ -0,0 +1 @@ +../../../../../runtime/near-test-contracts/res/fungible_token.wasm \ No newline at end of file diff --git a/pytest/tests/loadtest/locust/res/social_db.wasm b/pytest/tests/loadtest/locust/res/social_db.wasm new file mode 100644 index 000000000..37fcb1638 Binary files /dev/null and b/pytest/tests/loadtest/locust/res/social_db.wasm differ diff --git a/pytest/tests/loadtest/locust/res/sweat.wasm b/pytest/tests/loadtest/locust/res/sweat.wasm new file mode 100644 index 000000000..948c1a725 Binary files /dev/null and b/pytest/tests/loadtest/locust/res/sweat.wasm differ diff --git a/pytest/tests/loadtest/setup.py b/pytest/tests/loadtest/setup.py new file mode 100644 index 000000000..abb7cb814 --- /dev/null +++ b/pytest/tests/loadtest/setup.py @@ -0,0 +1,43 @@ +import subprocess +import mocknet_helpers +import account +import key +import argparse +from os.path import join + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Setup loadtest') + + parser.add_argument('--home', type=str, required=True) + parser.add_argument('--num_accounts', type=int, default=5) + parser.add_argument('--host', type=str, default='127.0.0.1') + parser.add_argument('--account_id', type=str, default=None) + parser.add_argument('--contract_dir', + type=str, + default='pytest/tests/loadtest/contract') + args = parser.parse_args() + + print("Compiling contract") + subprocess.check_call(args=[ + "cargo", "build", "--target", "wasm32-unknown-unknown", "--release" + ], + cwd=args.contract_dir) + + for i in range(args.num_accounts): + account_name = args.account_id or f"shard{i}" + + shard_key = key.Key.from_json_file( + join(args.home, f"{account_name}_key.json")) + + base_block_hash = mocknet_helpers.get_latest_block_hash(addr=args.host) + nonce = mocknet_helpers.get_nonce_for_key(shard_key, addr=args.host) + + shard_account = account.Account(shard_key, + init_nonce=nonce, + base_block_hash=base_block_hash, + rpc_infos=[(args.host, "3030")]) + + shard_account.send_deploy_contract_tx( + join( + args.contract_dir, + "target/wasm32-unknown-unknown/release/loadtest_contract.wasm")) diff --git a/pytest/tests/mocknet/README.md b/pytest/tests/mocknet/README.md new file mode 100644 index 000000000..71e2e153c --- /dev/null +++ b/pytest/tests/mocknet/README.md @@ -0,0 +1,14 @@ +Mirror transactions from a given network into a custom mocktest network and add load + +1. Setup a custom mocknet network following the README in the `provisioning/terraform/network/mocknet/mirror/` directory of the [unc-ops](https://github.com/near/unc-ops) repo. +- you'll need the `unique_id`, `chain_id`, and `start_height` from this setup when running the mirror.py test script in 2. +2. Run `python3 tests/mocknet/mirror.py --chain-id {chain_id} --start-height {start_height} --unique-id {unique_id} init-uncd-runner`, replacing the `{}`s with appropriate values from the `framework/pytest` directory. This starts a helper program on each node that will be in charge of the test state and uncd process. +3. Run `python3 tests/mocknet/mirror.py --chain-id {chain_id} --start-height {start_height} --unique-id {unique_id} new-test`. This will take a few hours. +4. Run `python3 tests/mocknet/mirror.py --chain-id {chain_id} --start-height {start_height} --unique-id {unique_id} start-traffic` replacing the `{}`s with appropriate values +5. Monitoring +- See metrics on grafana mocknet https://grafana.near.org/d/jHbiNgSnz/mocknet?orgId=1&refresh=30s&var-chain_id=All&var-node_id=.*unique_id.*&var-account_id=All replacing the "unique_id" with the value from earlier + +If there's ever a problem with the uncd runners on each node, for example if you get a connection error running the `status` command, run the `restart-uncd-runner` command to restart them, which should be safe to do. + +To run a locust load test on the mocknet network, run `python3 tests/mocknet/locust.py init --instance-names {}`, where +the instance names are VMs that have been prepared for this purpose, and then run `python3 tests/mocknet/locust.py run --master {master_instance_name} --workers {worker_instance_name0,worker_instance_name1,etc...} --funding-key {key.json} --node-ip-port {mocknet_node_ip}:3030`, where `mocknet_node_ip` is an IP address of a node that's been setup by the mirror.py script, and `key.json` is an account key that contains lots of NEAR for this load test. TODO: add extra accounts for load testing purposes during the mocknet setup step diff --git a/pytest/tests/mocknet/__init__.py b/pytest/tests/mocknet/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pytest/tests/mocknet/cmd_utils.py b/pytest/tests/mocknet/cmd_utils.py new file mode 100644 index 000000000..dd654eee0 --- /dev/null +++ b/pytest/tests/mocknet/cmd_utils.py @@ -0,0 +1,30 @@ +import sys + +LOG_DIR = '/home/ubuntu/logs' +STATUS_DIR = '/home/ubuntu/logs/status' + + +def run_cmd(node, cmd): + r = node.machine.run(cmd) + if r.exitcode != 0: + sys.exit( + f'failed running {cmd} on {node.instance_name}:\nstdout: {r.stdout}\nstderr: {r.stderr}' + ) + return r + + +def run_in_background(node, cmd, log_filename, env='', pre_cmd=None): + setup_cmd = f'truncate --size 0 {STATUS_DIR}/{log_filename} ' + setup_cmd += f'&& for i in {{8..0}}; do if [ -f {LOG_DIR}/{log_filename}.$i ]; then mv {LOG_DIR}/{log_filename}.$i {LOG_DIR}/{log_filename}.$((i+1)); fi done' + if pre_cmd is not None: + pre_cmd += ' && ' + else: + pre_cmd = '' + run_cmd( + node, + f'( {pre_cmd}{setup_cmd} && {env} nohup {cmd} > {LOG_DIR}/{log_filename}.0 2>&1; nohup echo "$?" ) > {STATUS_DIR}/{log_filename} 2>&1 &' + ) + + +def init_node(node): + run_cmd(node, f'mkdir -p {LOG_DIR} && mkdir -p {STATUS_DIR}') diff --git a/pytest/tests/mocknet/helpers/__init__.py b/pytest/tests/mocknet/helpers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pytest/tests/mocknet/helpers/genesis_updater.py b/pytest/tests/mocknet/helpers/genesis_updater.py new file mode 100755 index 000000000..f0721accf --- /dev/null +++ b/pytest/tests/mocknet/helpers/genesis_updater.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +""" +Creates a genesis file from a template. +This file is uploaded to each mocknet node and run on the node, producing identical genesis files across all nodes. +This approach is significantly faster than the alternative, of uploading the genesis file to all mocknet nodes. +Currently testnet state is a 17GB json file, and uploading that file to 100 machines over a 1Gbit/s connection would +need at 4 hours. +""" + +import os +import pathlib +import sys + +# Don't use the pathlib magic because this file runs on a remote machine. +sys.path.append('lib') +import mocknet +from configured_logger import logger + + +def str_to_bool(arg): + return arg.lower() == 'true' + + +def main(argv): + logger.info(argv) + assert len(argv) == 17 + + genesis_filename_in = argv[1] + records_filename_in = argv[2] + config_filename_in = argv[3] + out_dir = argv[4] + + chain_id = argv[5] + validator_keys = None + if argv[6]: + validator_keys = dict(map(lambda x: x.split('='), argv[6].split(','))) + rpc_node_names = None + if argv[7]: + rpc_node_names = argv[7].split(',') + done_filename = argv[8] + epoch_length = int(argv[9]) + node_pks = None + if argv[10]: + node_pks = argv[10].split(',') + increasing_stakes = float(argv[11]) + num_seats = float(argv[12]) + single_shard = str_to_bool(argv[13]) + all_node_pks = None + if argv[14]: + all_node_pks = argv[14].split(',') + node_ips = None + if argv[15]: + node_ips = argv[15].split(',') + if argv[16].lower() == 'none': + uncd = None + else: + uncd = argv[16] + + assert genesis_filename_in + assert records_filename_in + assert config_filename_in + assert out_dir + assert chain_id + assert validator_keys + assert done_filename + assert epoch_length + assert node_pks + assert rpc_node_names + assert num_seats + assert all_node_pks + assert node_ips + + mocknet.neard_amend_genesis( + uncd=uncd, + validator_keys=validator_keys, + genesis_filename_in=genesis_filename_in, + records_filename_in=records_filename_in, + out_dir=out_dir, + rpc_node_names=rpc_node_names, + chain_id=chain_id, + epoch_length=epoch_length, + node_pks=node_pks, + increasing_stakes=increasing_stakes, + num_seats=num_seats, + single_shard=single_shard, + ) + config_filename_out = os.path.join(out_dir, 'config.json') + mocknet.update_config_file( + config_filename_in, + config_filename_out, + all_node_pks, + node_ips, + ) + + logger.info(f'done_filename: {done_filename}') + pathlib.Path(done_filename).write_text('DONE') + + +if __name__ == '__main__': + main(sys.argv) diff --git a/pytest/tests/mocknet/helpers/load_test_spoon_helper.py b/pytest/tests/mocknet/helpers/load_test_spoon_helper.py new file mode 100755 index 000000000..8349a8c8a --- /dev/null +++ b/pytest/tests/mocknet/helpers/load_test_spoon_helper.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +""" +Generates transactions on a mocknet node. +This file is uploaded to each mocknet node and run there. +""" + +import random +import sys +import time +import argparse +import requests + +# Don't use the pathlib magic because this file runs on a remote machine. +sys.path.append('lib') +import account +import key as key_mod +import load_test_utils +import mocknet +import mocknet_helpers + +from configured_logger import logger + + +def parse_args(): + parser = argparse.ArgumentParser( + description='Generates transactions on a mocknet node.') + parser.add_argument('--node-account-id', required=True, type=str) + parser.add_argument('--rpc-nodes', required=True, type=str) + parser.add_argument('--num-nodes', required=True, type=int) + parser.add_argument('--max-tps', required=True, type=float) + + parser.add_argument('--test-timeout', type=int, default=12 * 60 * 60) + parser.add_argument( + '--contract-deploy-time', + type=int, + default=10 * mocknet.NUM_ACCOUNTS, + help= + 'We need to slowly deploy contracts, otherwise we stall out the nodes', + ) + + return parser.parse_args() + + +def get_test_accounts_from_args(args): + + node_account_id = args.node_account_id + rpc_nodes = args.rpc_nodes.split(',') + num_nodes = args.num_nodes + max_tps = args.max_tps + + logger.info(f'node_account_id: {node_account_id}') + logger.info(f'rpc_nodes: {rpc_nodes}') + logger.info(f'num_nodes: {num_nodes}') + logger.info(f'max_tps: {max_tps}') + + node_account_key = key_mod.Key( + node_account_id, + mocknet.PUBLIC_KEY, + mocknet.SECRET_KEY, + ) + test_account_keys = [ + key_mod.Key( + mocknet.load_testing_account_id(node_account_id, i), + mocknet.PUBLIC_KEY, + mocknet.SECRET_KEY, + ) for i in range(mocknet.NUM_ACCOUNTS) + ] + + base_block_hash = mocknet_helpers.get_latest_block_hash() + + rpc_infos = [(rpc_addr, mocknet_helpers.RPC_PORT) for rpc_addr in rpc_nodes] + node_account = account.Account( + node_account_key, + mocknet_helpers.get_nonce_for_pk( + node_account_key.account_id, + node_account_key.pk, + ), + base_block_hash, + rpc_infos=rpc_infos, + ) + accounts = [ + account.Account( + key, + mocknet_helpers.get_nonce_for_pk( + key.account_id, + key.pk, + ), + base_block_hash, + rpc_infos=rpc_infos, + ) for key in test_account_keys + ] + max_tps_per_node = max_tps / num_nodes + return load_test_utils.TestState( + node_account, + accounts, + max_tps_per_node, + rpc_infos, + ) + + +def main(): + logger.info(" ".join(sys.argv)) + + args = parse_args() + test_state = get_test_accounts_from_args(args) + + # Ensure load testing contract is deployed to all accounts before + # starting to send random transactions (ensures we do not try to + # call the contract before it is deployed). + delay = args.contract_deploy_time / test_state.num_test_accounts() + logger.info(f'Start deploying, delay between deployments: {delay}') + assert delay >= 1 + + time.sleep(random.random() * delay) + start_time = time.monotonic() + load_test_utils.init_ft(test_state.node_account) + for i, account in enumerate(test_state.test_accounts): + logger.info(f'Deploying contract for account {account.key.account_id}') + mocknet_helpers.retry_and_ignore_errors( + lambda: account.send_deploy_contract_tx(mocknet.WASM_FILENAME)) + load_test_utils.init_ft_account(test_state.node_account, account) + balance = mocknet_helpers.retry_and_ignore_errors( + lambda: account.get_amount_yoctonear()) + logger.info( + f'Account {account.key.account_id} balance after initialization: {balance}' + ) + time.sleep(max(1.0, start_time + (i + 1) * delay - time.monotonic())) + + logger.info('Done deploying') + + # begin with only transfers for TPS measurement + total_tx_sent, elapsed_time = 0, 0 + logger.info( + f'Start the test, expected TPS {test_state.max_tps_per_node} over the next {args.test_timeout} seconds' + ) + last_staking = 0 + start_time = time.monotonic() + while time.monotonic() - start_time < args.test_timeout: + try: + # Repeat the staking transactions in case the validator selection algorithm changes. + staked_time = mocknet.stake_available_amount( + test_state.node_account, + last_staking, + ) + if staked_time is not None: + last_staking = staked_time + + elapsed_time = time.monotonic() - start_time + total_tx_sent = mocknet_helpers.throttle_txns( + load_test_utils.send_random_transactions, + total_tx_sent, + elapsed_time, + test_state, + ) + except ( + ConnectionRefusedError, + requests.exceptions.ConnectionError, + ) as e: + # If this happens only occasionally the loop will retry immediately, + # eventually pick a healthy rpc node and all will be fine. If it + # happens every time, it indicates that something is wrong and + # should be visible in grafana tx rate. + logger.warning( + f'Error when staking or sending tx. This may happen when the ' + f'selected RPC node is being upgraded. The exception is {e}') + logger.info('Stop the test') + + +if __name__ == '__main__': + main() diff --git a/pytest/tests/mocknet/helpers/load_test_utils.py b/pytest/tests/mocknet/helpers/load_test_utils.py new file mode 100644 index 000000000..e7f5dbc58 --- /dev/null +++ b/pytest/tests/mocknet/helpers/load_test_utils.py @@ -0,0 +1,238 @@ +import random +import sys +import time + +import base58 +from rc import pmap + +# Don't use the pathlib magic because this file runs on a remote machine. +sys.path.append('lib') +import mocknet_helpers +import mocknet +import transaction + +from configured_logger import logger + + +class TestState: + + def __init__( + self, + node_account, + test_accounts, + max_tps_per_node, + rpc_infos, + ): + self.node_account = node_account + self.test_accounts = test_accounts + self.max_tps_per_node = max_tps_per_node + self.rpc_infos = rpc_infos + self.function_call_state = [[]] * len(self.test_accounts) + + def random_account(self): + return random.choice(self.test_accounts) + + def num_test_accounts(self): + return len(self.test_accounts) + + +def send_transfer(account, test_state, base_block_hash=None): + dest_account = test_state.random_account() + amount = 1 + mocknet_helpers.retry_and_ignore_errors( + lambda: account.send_transfer_tx(dest_account.key.account_id, + transfer_amount=amount, + base_block_hash=base_block_hash)) + logger.info( + f'Account {account.key.account_id} transfers {amount} yoctoNear to {dest_account.key.account_id}' + ) + + +def function_call_set_state_then_delete_state(i, + test_state, + base_block_hash=None): + if not test_state.function_call_state[i]: + action = "add" + elif len(test_state.function_call_state[i]) >= 100: + action = "delete" + else: + action = random.choice(["add", "delete"]) + + account = test_state.test_accounts[i] + if action == "add": + next_id = random.randrange(test_state.num_test_accounts()) + next_val = random.randint(0, 1000) + next_account_id = mocknet.load_testing_account_id( + test_state.node_account.key.account_id, next_id) + s = f'{{"account_id": "account_{next_val}", "message":"{next_val}"}}' + logger.info( + f'Calling function "set_state" of account {next_account_id} with arguments {s} from account {account.key.account_id}' + ) + tx_res = mocknet_helpers.retry_and_ignore_errors( + lambda: account.send_call_contract_raw_tx(next_account_id, + 'set_state', + s.encode('utf-8'), + 0, + base_block_hash= + base_block_hash)) + logger.info( + f'{account.key.account_id} set_state on {next_account_id} {tx_res}') + test_state.function_call_state[i].append((next_id, next_val)) + else: + assert test_state.function_call_state[i] + item = random.choice(test_state.function_call_state[i]) + next_id, next_val = item + next_account_id = mocknet.load_testing_account_id( + test_state.node_account.key.account_id, next_id) + s = f'{{"account_id": "account_{next_val}"}}' + logger.info( + f'Calling function "delete_state" of account {next_account_id} with arguments {s} from account {account.key.account_id}' + ) + tx_res = mocknet_helpers.retry_and_ignore_errors( + lambda: account.send_call_contract_raw_tx(next_account_id, + 'delete_state', + s.encode('utf-8'), + 0, + base_block_hash= + base_block_hash)) + logger.info( + f'{account.key.account_id} delete_state on {next_account_id} {tx_res}' + ) + if item in test_state.function_call_state[i]: + test_state.function_call_state[i].remove(item) + logger.info( + f'Successfully removed {item} from function_call_state. New #items: {len(test_state.function_call_state[i])}' + ) + else: + logger.info( + f'{item} is not in function_call_state even though this is impossible. #Items: {len(test_state.function_call_state[i])}' + ) + + +def function_call_ft_transfer_call(account, test_state, base_block_hash=None): + dest_account = test_state.random_account() + contract = test_state.node_account.key.account_id + + s = f'{{"receiver_id": "{dest_account.key.account_id}", "amount": "3", "msg": "\\"hi\\""}}' + logger.info( + f'Calling function "ft_transfer_call" with arguments {s} on account {account.key.account_id} contract {contract} with destination {dest_account.key.account_id}' + ) + tx_res = mocknet_helpers.retry_and_ignore_errors( + lambda: account.send_call_contract_raw_tx(contract, + 'ft_transfer_call', + s.encode('utf-8'), + 1, + base_block_hash= + base_block_hash)) + logger.info( + f'{account.key.account_id} ft_transfer to {dest_account.key.account_id} {tx_res}' + ) + + +# See https://near.github.io/framework/architecture/how/meta-tx.html to understand what is going on. +# Alice pays the costs of Relayer sending 1 yoctoNear to Receiver +def meta_transaction_transfer(alice_account, test_state, base_block_hash, + base_block_height): + relayer_account = test_state.random_account() + receiver_account = test_state.random_account() + + yocto_unc_amount = 1 + transfer_action = transaction.create_payment_action(yocto_unc_amount) + # Use (relayer_account.nonce + 2) as a nonce to deal with the case of Alice + # and Relayer being the same account. The outer transaction needs to have + # a lower nonce. + # Make the delegated action valid for 10**6 blocks to avoid DelegateActionExpired errors. + # Value of 10 should probably be enough. + # DelegateAction is signed with the keys of the Relayer. + signed_meta_tx = transaction.create_signed_delegated_action( + relayer_account.key.account_id, receiver_account.key.account_id, + [transfer_action], relayer_account.nonce + 2, base_block_height + 10**6, + relayer_account.key.decoded_pk(), relayer_account.key.decoded_sk()) + + # Outer transaction is signed with the keys of Alice. + meta_tx = transaction.sign_delegate_action(signed_meta_tx, + alice_account.key, + relayer_account.key.account_id, + alice_account.nonce + 1, + base_block_hash) + alice_account.send_tx(meta_tx) + alice_account.nonce += 1 + relayer_account.nonce += 2 + + logger.info( + f'meta-transaction from {alice_account.key.account_id} to transfer {yocto_unc_amount} yoctoNear from {relayer_account.key.account_id} to {receiver_account.key.account_id}' + ) + + +def random_transaction(i, test_state, base_block_hash, base_block_height): + account = test_state.test_accounts[i] + time.sleep(random.random() * test_state.num_test_accounts() / + test_state.max_tps_per_node / 3) + choice = random.randint(0, 3) + if choice == 0: + send_transfer(account, test_state, base_block_hash=base_block_hash) + elif choice == 1: + function_call_set_state_then_delete_state( + i, test_state, base_block_hash=base_block_hash) + elif choice == 2: + function_call_ft_transfer_call(account, + test_state, + base_block_hash=base_block_hash) + elif choice == 3: + meta_transaction_transfer(account, test_state, base_block_hash, + base_block_height) + + +def send_random_transactions(test_state): + logger.info("===========================================") + logger.info("New iteration of 'send_random_transactions'") + addr = random.choice(test_state.rpc_infos)[0] + status = mocknet_helpers.get_status(addr=addr) + base_block_hash = base58.b58decode( + status['sync_info']['latest_block_hash'].encode('utf-8')) + base_block_height = status['sync_info']['latest_block_height'] + pmap( + lambda index_and_account: random_transaction( + index_and_account[0], + test_state, + base_block_hash=base_block_hash, + base_block_height=base_block_height), + enumerate(test_state.test_accounts)) + + +def init_ft(node_account): + tx_res = node_account.send_deploy_contract_tx( + '/home/ubuntu/fungible_token.wasm') + logger.info(f'ft deployment {tx_res}') + mocknet_helpers.wait_at_least_one_block() + + s = f'{{"owner_id": "{node_account.key.account_id}", "total_supply": "{10**33}"}}' + tx_res = node_account.send_call_contract_raw_tx(node_account.key.account_id, + 'new_default_meta', + s.encode('utf-8'), 0) + logger.info(f'ft new_default_meta {tx_res}') + + +def init_ft_account(node_account, account): + s = f'{{"account_id": "{account.key.account_id}"}}' + tx_res = account.send_call_contract_raw_tx(node_account.key.account_id, + 'storage_deposit', + s.encode('utf-8'), + (10**24) // 800) + logger.info(f'Account {account.key.account_id} storage_deposit {tx_res}') + + # The next transaction depends on the previous transaction succeeded. + # Sleeping for 1 second is the poor man's solution for waiting for that transaction to succeed. + # This works because the contracts are being deployed slow enough to keep block production above 1 bps. + mocknet_helpers.wait_at_least_one_block() + + s = f'{{"receiver_id": "{account.key.account_id}", "amount": "{10**18}"}}' + logger.info( + f'Calling function "ft_transfer" with arguments {s} on account {account.key.account_id}' + ) + tx_res = node_account.send_call_contract_raw_tx(node_account.key.account_id, + 'ft_transfer', + s.encode('utf-8'), 1) + logger.info( + f'{node_account.key.account_id} ft_transfer to {account.key.account_id} {tx_res}' + ) diff --git a/pytest/tests/mocknet/helpers/neard_runner.py b/pytest/tests/mocknet/helpers/neard_runner.py new file mode 100644 index 000000000..1dca3121f --- /dev/null +++ b/pytest/tests/mocknet/helpers/neard_runner.py @@ -0,0 +1,887 @@ +# TODO: reimplement this logic using standard tooling like systemd instead of relying on this +# python script to handle uncd process management. + +import argparse +from enum import Enum +import fcntl +import json +import jsonrpc +import logging +import os +import psutil +import requests +import shutil +import signal +import socket +import subprocess +import sys +import threading +import time +import http +import http.server + + +def get_lock(home): + lock_file = os.path.join(home, 'LOCK') + + fd = os.open(lock_file, os.O_CREAT | os.O_RDWR) + try: + fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except BlockingIOError: + raise Exception(f'{lock_file} is currently locked by another process') + return fd + + +def http_code(jsonrpc_error): + if jsonrpc_error is None: + return http.HTTPStatus.OK + + if jsonrpc_error['code'] == -32700 or jsonrpc_error[ + 'code'] == -32600 or jsonrpc_error['code'] == -32602: + return http.HTTPStatus.BAD_REQUEST + elif jsonrpc_error['code'] == -32601: + return http.HTTPStatus.NOT_FOUND + else: + return http.HTTPStatus.INTERNAL_SERVER_ERROR + + +class JSONHandler(http.server.BaseHTTPRequestHandler): + + def __init__(self, request, client_address, server): + self.dispatcher = jsonrpc.Dispatcher() + self.dispatcher.add_method(server.neard_runner.do_new_test, + name="new_test") + self.dispatcher.add_method(server.neard_runner.do_network_init, + name="network_init") + self.dispatcher.add_method(server.neard_runner.do_update_config, + name="update_config") + self.dispatcher.add_method(server.neard_runner.do_ready, name="ready") + self.dispatcher.add_method(server.neard_runner.do_start, name="start") + self.dispatcher.add_method(server.neard_runner.do_stop, name="stop") + self.dispatcher.add_method(server.neard_runner.do_reset, name="reset") + self.dispatcher.add_method(server.neard_runner.do_update_binaries, + name="update_binaries") + super().__init__(request, client_address, server) + + def do_GET(self): + if self.path == '/status': + body = 'OK\n'.encode('UTF-8') + self.send_response(http.HTTPStatus.OK) + self.send_header("Content-Type", 'application/json') + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + else: + self.send_error(http.HTTPStatus.NOT_FOUND) + + def do_POST(self): + l = self.headers.get('content-length') + if l is None: + self.send_error(http.HTTPStatus.BAD_REQUEST, + "Content-Length missing") + return + + body = self.rfile.read(int(l)) + response = jsonrpc.JSONRPCResponseManager.handle(body, self.dispatcher) + response_body = response.json.encode('UTF-8') + + self.send_response(http_code(response.error)) + self.send_header("Content-Type", 'application/json') + self.send_header("Content-Length", str(len(response_body))) + self.end_headers() + self.wfile.write(response_body) + + +class RpcServer(http.server.HTTPServer): + + def __init__(self, addr, neard_runner): + self.neard_runner = neard_runner + super().__init__(addr, JSONHandler) + + +class TestState(Enum): + NONE = 1 + AWAITING_NETWORK_INIT = 2 + AMEND_GENESIS = 3 + STATE_ROOTS = 4 + RUNNING = 5 + STOPPED = 6 + RESETTING = 7 + + +class NeardRunner: + + def __init__(self, args): + self.home = args.home + self.neard_home = args.neard_home + self.neard_logs_dir = args.neard_logs_dir + try: + os.mkdir(self.neard_logs_dir) + except FileExistsError: + pass + with open(self.home_path('config.json'), 'r') as f: + self.config = json.load(f) + self.uncd = None + self.num_restarts = 0 + self.last_start = time.time() + # self.data will contain data that we want to persist so we can + # start where we left off if this process is killed and restarted + # for now we save info on the uncd binaries (their paths and epoch + # heights where we want to run them), a bool that tells whether uncd + # should be running, and info on the currently running uncd process + try: + with open(self.home_path('data.json'), 'r') as f: + self.data = json.load(f) + self.data['binaries'].sort(key=lambda x: x['epoch_height']) + except FileNotFoundError: + self.data = { + 'binaries': [], + 'neard_process': None, + 'current_neard_path': None, + 'state': TestState.NONE.value, + } + # protects self.data, and its representation on disk, + # because both the rpc server and the main loop touch them concurrently + self.lock = threading.Lock() + + def is_traffic_generator(self): + return self.config.get('is_traffic_generator', False) + + def save_data(self): + with open(self.home_path('data.json'), 'w') as f: + json.dump(self.data, f) + + def parse_binaries_config(self): + if 'binaries' not in self.config: + sys.exit('config does not have a "binaries" section') + + if not isinstance(self.config['binaries'], list): + sys.exit('config "binaries" section not a list') + + if len(self.config['binaries']) == 0: + sys.exit('no binaries in the config') + + self.config['binaries'].sort(key=lambda x: x['epoch_height']) + last_epoch_height = -1 + + binaries = [] + for i, b in enumerate(self.config['binaries']): + epoch_height = b['epoch_height'] + if not isinstance(epoch_height, int) or epoch_height < 0: + sys.exit(f'bad epoch height in config: {epoch_height}') + if last_epoch_height == -1: + if epoch_height != 0: + # TODO: maybe it could make sense to allow this, meaning don't run any binary + # on this node until the network reaches that epoch, then we bring this node online + sys.exit( + f'config should contain one binary with epoch_height 0') + else: + if epoch_height == last_epoch_height: + sys.exit(f'repeated epoch height in config: {epoch_height}') + last_epoch_height = epoch_height + binaries.append({ + 'url': b['url'], + 'epoch_height': b['epoch_height'], + 'system_path': self.home_path('binaries', f'uncd{i}') + }) + return binaries + + def reset_current_neard_path(self): + self.data['current_neard_path'] = self.data['binaries'][0][ + 'system_path'] + + # tries to download the binaries specified in config.json, saving them in $home/binaries/ + # if force is set to true all binaries will be downloaded, otherwise only the missing ones + def download_binaries(self, force): + binaries = self.parse_binaries_config() + + try: + os.mkdir(self.home_path('binaries')) + except FileExistsError: + pass + + if force: + # always start from 0 and download all binaries + start_index = 0 + else: + # start at the index of the first missing binary + # typically it's all or nothing + with self.lock: + start_index = len(self.data['binaries']) + + # for now we assume that the binaries recorded in data.json as having been + # dowloaded are still valid and were not touched. Also this assumes that their + # filenames are neard0, neard1, etc. in the right order and with nothing skipped + for i in range(start_index, len(binaries)): + b = binaries[i] + logging.info(f'downloading binary from {b["url"]}') + with open(b['system_path'], 'wb') as f: + r = requests.get(b['url'], stream=True) + r.raise_for_status() + for chunk in r.iter_content(chunk_size=8192): + f.write(chunk) + os.chmod(b['system_path'], 0o755) + logging.info(f'downloaded binary from {b["url"]}') + + with self.lock: + self.data['binaries'].append(b) + if self.data['current_neard_path'] is None: + self.reset_current_neard_path() + self.save_data() + + def target_unc_home_path(self, *args): + if self.is_traffic_generator(): + args = ('target',) + args + return os.path.join(self.neard_home, *args) + + def home_path(self, *args): + return os.path.join(self.home, *args) + + def tmp_unc_home_path(self, *args): + args = ('tmp-unc-home',) + args + return os.path.join(self.home, *args) + + def neard_init(self): + # We make uncd init save files to self.tmp_unc_home_path() just to make it + # a bit cleaner, so we can init to a non-existent directory and then move + # the files we want to the real near home without having to remove it first + cmd = [ + self.data['binaries'][0]['system_path'], '--home', + self.tmp_unc_home_path(), 'init' + ] + if not self.is_traffic_generator(): + cmd += ['--account-id', f'{socket.gethostname()}.near'] + subprocess.check_call(cmd) + + with open(self.tmp_unc_home_path('config.json'), 'r') as f: + config = json.load(f) + self.data['neard_addr'] = config['rpc']['addr'] + config['tracked_shards'] = [0, 1, 2, 3] + config['log_summary_style'] = 'plain' + config['network']['skip_sync_wait'] = False + config['genesis_records_file'] = 'records.json' + config['rpc']['enable_debug_rpc'] = True + if self.is_traffic_generator(): + config['archive'] = True + with open(self.tmp_unc_home_path('config.json'), 'w') as f: + json.dump(config, f, indent=2) + + def move_init_files(self): + try: + os.mkdir(self.target_unc_home_path()) + except FileExistsError: + pass + for p in os.listdir(self.target_unc_home_path()): + filename = self.target_unc_home_path(p) + if os.path.isfile(filename): + os.remove(filename) + try: + shutil.rmtree(self.target_unc_home_path('data')) + except FileNotFoundError: + pass + paths = ['config.json', 'node_key.json'] + if not self.is_traffic_generator(): + paths.append('validator_key.json') + for path in paths: + shutil.move(self.tmp_unc_home_path(path), + self.target_unc_home_path(path)) + + # This RPC method tells to stop uncd and re-initialize its home dir. This returns the + # validator and node key that resulted from the initialization. We can't yet call amend-genesis + # and compute state roots, because the caller of this method needs to hear back from + # each node before it can build the list of initial validators. So after this RPC method returns, + # we'll be waiting for the network_init RPC. + # TODO: add a binaries argument that tells what binaries we want to use in the test. Before we do + # this, it is pretty mandatory to implement some sort of client authentication, because without it, + # anyone would be able to get us to download and run arbitrary code + def do_new_test(self): + with self.lock: + self.kill_neard() + try: + shutil.rmtree(self.tmp_unc_home_path()) + except FileNotFoundError: + pass + try: + os.remove(self.home_path('validators.json')) + except FileNotFoundError: + pass + try: + os.remove(self.home_path('network_init.json')) + except FileNotFoundError: + pass + + self.neard_init() + self.move_init_files() + + with open(self.target_unc_home_path('config.json'), 'r') as f: + config = json.load(f) + with open(self.target_unc_home_path('node_key.json'), 'r') as f: + node_key = json.load(f) + if not self.is_traffic_generator(): + with open(self.target_unc_home_path('validator_key.json'), + 'r') as f: + validator_key = json.load(f) + validator_account_id = validator_key['account_id'] + validator_public_key = validator_key['public_key'] + else: + validator_account_id = None + validator_public_key = None + + self.set_state(TestState.AWAITING_NETWORK_INIT) + self.save_data() + + return { + 'validator_account_id': validator_account_id, + 'validator_public_key': validator_public_key, + 'node_key': node_key['public_key'], + 'listen_port': config['network']['addr'].split(':')[1], + } + + # After the new_test RPC, we wait to get this RPC that gives us the list of validators + # and boot nodes for the test network. After this RPC call, we run amend-genesis and + # start uncd to compute genesis state roots. + def do_network_init(self, + validators, + boot_nodes, + epoch_length=1000, + num_seats=100, + protocol_version=None): + if not isinstance(validators, list): + raise jsonrpc.exceptions.JSONRPCDispatchException( + code=-32600, message='validators argument not a list') + if not isinstance(boot_nodes, list): + raise jsonrpc.exceptions.JSONRPCDispatchException( + code=-32600, message='boot_nodes argument not a list') + + # TODO: maybe also check validity of these arguments? + if len(validators) == 0: + raise jsonrpc.exceptions.JSONRPCDispatchException( + code=-32600, message='validators argument must not be empty') + if len(boot_nodes) == 0: + raise jsonrpc.exceptions.JSONRPCDispatchException( + code=-32600, message='boot_nodes argument must not be empty') + + with self.lock: + state = self.get_state() + if state != TestState.AWAITING_NETWORK_INIT: + raise jsonrpc.exceptions.JSONRPCDispatchException( + code=-32600, + message='Can only call network_init after a call to init') + + if len(validators) < 3: + with open(self.target_unc_home_path('config.json'), 'r') as f: + config = json.load(f) + config['consensus']['min_num_peers'] = len(validators) - 1 + with open(self.target_unc_home_path('config.json'), 'w') as f: + json.dump(config, f) + with open(self.home_path('validators.json'), 'w') as f: + json.dump(validators, f) + with open(self.home_path('network_init.json'), 'w') as f: + json.dump( + { + 'boot_nodes': boot_nodes, + 'epoch_length': epoch_length, + 'num_seats': num_seats, + 'protocol_version': protocol_version, + }, f) + + def do_update_config(self, key_value): + with self.lock: + logging.info(f'updating config with {key_value}') + with open(self.target_unc_home_path('config.json'), 'r') as f: + config = json.load(f) + + [key, value] = key_value.split("=", 1) + key_item_list = key.split(".") + + object = config + for key_item in key_item_list[:-1]: + if key_item not in object: + object[key_item] = {} + object = object[key_item] + + value = json.loads(value) + + object[key_item_list[-1]] = value + + with open(self.target_unc_home_path('config.json'), 'w') as f: + json.dump(config, f, indent=2) + + return True + + def do_start(self): + with self.lock: + state = self.get_state() + if state == TestState.STOPPED: + self.start_neard() + self.set_state(TestState.RUNNING) + self.save_data() + elif state != TestState.RUNNING: + raise jsonrpc.exceptions.JSONRPCDispatchException( + code=-32600, + message= + 'Cannot start node as test state has not been initialized yet' + ) + + # right now only has an effect if the test setup has been initialized. Should it also mean stop setting up + # the test if we're in the middle of initializing it? + def do_stop(self): + with self.lock: + state = self.get_state() + if state == TestState.RUNNING: + self.kill_neard() + self.set_state(TestState.STOPPED) + self.save_data() + + def do_reset(self): + with self.lock: + state = self.get_state() + logging.info(f"do_reset {state}") + if state == TestState.RUNNING: + self.kill_neard() + self.set_state(TestState.RESETTING) + self.reset_current_neard_path() + self.save_data() + elif state == TestState.STOPPED: + self.set_state(TestState.RESETTING) + self.reset_current_neard_path() + self.save_data() + else: + raise jsonrpc.exceptions.JSONRPCDispatchException( + code=-32600, + message= + 'Cannot reset node as test state has not been initialized yet' + ) + + def do_update_binaries(self): + logging.info('update binaries') + self.download_binaries(force=True) + logging.info('update binaries finished') + + def do_ready(self): + with self.lock: + state = self.get_state() + return state == TestState.RUNNING or state == TestState.STOPPED + + # check the current epoch height, and return the binary path that we should + # be running given the epoch heights specified in config.json + # TODO: should we update it at a random time in the middle of the + # epoch instead of the beginning? + def wanted_neard_path(self): + j = { + 'method': 'validators', + 'params': [None], + 'id': 'dontcare', + 'jsonrpc': '2.0' + } + try: + r = requests.post(f'http://{self.data["neard_addr"]}', + json=j, + timeout=5) + r.raise_for_status() + response = json.loads(r.content) + epoch_height = response['result']['epoch_height'] + path = self.data['binaries'][0]['system_path'] + for b in self.data['binaries']: + # this logic assumes the binaries are sorted by epoch height + if b['epoch_height'] <= epoch_height: + path = b['system_path'] + else: + break + return path + except (requests.exceptions.ConnectionError, KeyError): + return self.data['current_neard_path'] + + def run_neard(self, cmd, out_file=None): + assert (self.uncd is None) + assert (self.data['neard_process'] is None) + env = os.environ.copy() + if 'RUST_LOG' not in env: + env['RUST_LOG'] = 'actix_web=warn,mio=warn,tokio_util=warn,actix_server=warn,actix_http=warn,indexer=info,debug' + logging.info(f'running {" ".join(cmd)}') + self.uncd = subprocess.Popen( + cmd, + stdin=subprocess.DEVNULL, + stdout=out_file, + stderr=out_file, + env=env, + ) + # we save the create_time so we can tell if the process with pid equal + # to the one we saved is the same process. It's not that likely, but + # if we don't do this (or maybe something else like it), then it's possble + # that if this process is killed and restarted and we see a process with that PID, + # it could actually be a different process that was started later after uncd exited + try: + create_time = int(psutil.Process(self.uncd.pid).create_time()) + except psutil.NoSuchProcess: + # not that likely, but if it already exited, catch the exception so + # at least this process doesn't die + create_time = 0 + + self.data['neard_process'] = { + 'pid': self.uncd.pid, + 'create_time': create_time, + 'path': cmd[0], + } + self.save_data() + + def poll_neard(self): + if self.uncd is not None: + code = self.uncd.poll() + path = self.data['neard_process']['path'] + running = code is None + if not running: + self.uncd = None + self.data['neard_process'] = None + self.save_data() + return path, running, code + elif self.data['neard_process'] is not None: + path = self.data['neard_process']['path'] + # we land here if this process previously died and is now restarted, + # and the old uncd process is still running + try: + p = psutil.Process(self.data['neard_process']['pid']) + if int(p.create_time() + ) == self.data['neard_process']['create_time']: + return path, True, None + except psutil.NoSuchProcess: + self.uncd = None + self.data['neard_process'] = None + self.save_data() + return path, False, None + else: + return None, False, None + + def kill_neard(self): + if self.uncd is not None: + logging.info('stopping uncd') + self.uncd.send_signal(signal.SIGINT) + self.uncd.wait() + self.uncd = None + self.data['neard_process'] = None + self.save_data() + return + + if self.data['neard_process'] is None: + return + + try: + p = psutil.Process(self.data['neard_process']['pid']) + if int(p.create_time() + ) == self.data['neard_process']['create_time']: + logging.info('stopping uncd') + p.send_signal(signal.SIGINT) + p.wait() + except psutil.NoSuchProcess: + pass + self.uncd = None + self.data['neard_process'] = None + self.save_data() + + # If this is a regular node, starts uncd run. If it's a traffic generator, starts uncd mirror run + def start_neard(self): + for i in range(20, -1, -1): + old_log = os.path.join(self.neard_logs_dir, f'log-{i}.txt') + new_log = os.path.join(self.neard_logs_dir, f'log-{i+1}.txt') + try: + os.rename(old_log, new_log) + except FileNotFoundError: + pass + + with open(os.path.join(self.neard_logs_dir, 'log-0.txt'), 'ab') as out: + if self.is_traffic_generator(): + cmd = [ + self.data['current_neard_path'], + 'mirror', + 'run', + '--source-home', + self.neard_home, + '--target-home', + self.target_unc_home_path(), + '--no-secret', + ] + else: + cmd = [ + self.data['current_neard_path'], '--log-span-events', + '--home', self.neard_home, '--unsafe-fast-startup', 'run' + ] + self.run_neard( + cmd, + out_file=out, + ) + self.last_start = time.time() + + # returns a bool that tells whether we should attempt a restart + def on_neard_died(self): + if self.is_traffic_generator(): + # TODO: This is just a lazy way to deal with the fact + # that the mirror command is expected to exit after it finishes sending all the traffic. + # For now just don't restart uncd on the traffic generator. Here we should be smart + # about restarting only if it makes sense, and we also shouldn't restart over and over. + # Right now we can't just check the exit_code because there's a bug that makes the + # uncd mirror run command not exit cleanly when it's finished + return False + + now = time.time() + if now - self.last_start > 600: + self.num_restarts = 1 + return True + if self.num_restarts >= 5: + self.set_state(TestState.STOPPED) + self.save_data() + return False + else: + self.num_restarts += 1 + return True + + def check_upgrade_neard(self): + neard_path = self.wanted_neard_path() + + path, running, exit_code = self.poll_neard() + if path is None: + start_neard = self.on_neard_died() + elif not running: + if exit_code is not None: + logging.info(f'uncd exited with code {exit_code}.') + start_neard = self.on_neard_died() + else: + if path == neard_path: + start_neard = False + else: + logging.info('upgrading uncd upon new epoch') + self.kill_neard() + start_neard = True + + if start_neard: + self.data['current_neard_path'] = neard_path + self.start_neard() + + def get_state(self): + return TestState(self.data['state']) + + def set_state(self, state): + self.data['state'] = state.value + + def network_init(self): + # wait til we get a network_init RPC + if not os.path.exists(self.home_path('validators.json')): + return + + with open(self.home_path('network_init.json'), 'r') as f: + n = json.load(f) + with open(self.target_unc_home_path('node_key.json'), 'r') as f: + node_key = json.load(f) + with open(self.target_unc_home_path('config.json'), 'r') as f: + config = json.load(f) + boot_nodes = [] + for b in n['boot_nodes']: + if node_key['public_key'] != b.split('@')[0]: + boot_nodes.append(b) + + config['network']['boot_nodes'] = ','.join(boot_nodes) + with open(self.target_unc_home_path('config.json'), 'w') as f: + config = json.dump(config, f, indent=2) + + cmd = [ + self.data['binaries'][0]['system_path'], + 'amend-genesis', + '--genesis-file-in', + os.path.join(self.neard_home, 'setup', 'genesis.json'), + '--records-file-in', + os.path.join(self.neard_home, 'setup', 'records.json'), + '--genesis-file-out', + self.target_unc_home_path('genesis.json'), + '--records-file-out', + self.target_unc_home_path('records.json'), + '--validators', + self.home_path('validators.json'), + '--chain-id', + 'mocknet', + '--transaction-validity-period', + '10000', + '--epoch-length', + str(n['epoch_length']), + '--num-seats', + str(n['num_seats']), + ] + if n['protocol_version'] is not None: + cmd.append('--protocol-version') + cmd.append(str(n['protocol_version'])) + + self.run_neard(cmd) + self.set_state(TestState.AMEND_GENESIS) + self.save_data() + + def check_amend_genesis(self): + path, running, exit_code = self.poll_neard() + if path is None: + logging.error( + 'state is AMEND_GENESIS, but no amend-genesis process is known') + self.set_state(TestState.AWAITING_NETWORK_INIT) + self.save_data() + elif not running: + if exit_code is not None and exit_code != 0: + logging.error( + f'uncd amend-genesis exited with code {exit_code}') + # for now just set the state to None, and if this ever happens, the + # test operator will have to intervene manually. Probably shouldn't + # really happen in practice + self.set_state(TestState.NONE) + self.save_data() + else: + # TODO: if exit_code is None then we were interrupted and restarted after starting + # the amend-genesis command. We assume here that the command was successful. Ok for now since + # the command probably won't fail. But should somehow check that it was OK + + logging.info('setting use_production_config to true') + genesis_path = self.target_unc_home_path('genesis.json') + with open(genesis_path, 'r') as f: + genesis_config = json.load(f) + with open(genesis_path, 'w') as f: + genesis_config['use_production_config'] = True + # protocol_versions in range [56, 63] need to have these + # genesis parameters, otherwise nodes get stuck because at + # some point it produces an incompatible EpochInfo. + # TODO: Make so that the node always constructs EpochInfo + # using `AllEpochConfig::for_protocol_version()`. + genesis_config['num_block_producer_seats'] = 100 + genesis_config['num_block_producer_seats_per_shard'] = [ + 100, 100, 100, 100 + ] + genesis_config['block_producer_kickout_threshold'] = 80 + genesis_config['chunk_producer_kickout_threshold'] = 80 + genesis_config['shard_layout'] = { + 'V1': { + 'boundary_accounts': [ + 'aurora', 'aurora-0', + 'kkuuue2akv_1630967379.near' + ], + 'shards_split_map': [[0, 1, 2, 3]], + 'to_parent_shard_map': [0, 0, 0, 0], + 'version': 1 + } + } + genesis_config['num_chunk_only_producer_seats'] = 200 + genesis_config['max_kickout_stake_perc'] = 30 + json.dump(genesis_config, f, indent=2) + initlog_path = os.path.join(self.neard_logs_dir, 'initlog.txt') + with open(initlog_path, 'ab') as out: + cmd = [ + self.data['binaries'][0]['system_path'], + '--home', + self.target_unc_home_path(), + '--unsafe-fast-startup', + 'run', + ] + self.run_neard( + cmd, + out_file=out, + ) + self.set_state(TestState.STATE_ROOTS) + self.save_data() + + def check_genesis_state(self): + path, running, exit_code = self.poll_neard() + if not running: + logging.error( + f'uncd exited with code {exit_code} on the first run') + # For now just exit, because if this happens, there is something pretty wrong with + # the setup, so a human needs to investigate and fix the bug + sys.exit(1) + try: + r = requests.get(f'http://{self.data["neard_addr"]}/status', + timeout=5) + if r.status_code == 200: + logging.info('uncd finished computing state roots') + self.kill_neard() + + try: + shutil.rmtree(self.home_path('backups')) + except FileNotFoundError: + pass + os.mkdir(self.home_path('backups')) + # Right now we save the backup to backups/start and in the future + # it would be nice to support a feature that lets you stop all the nodes and + # make another backup to restore to + backup_dir = self.home_path('backups', 'start') + logging.info(f'copying data dir to {backup_dir}') + shutil.copytree(self.target_unc_home_path('data'), backup_dir) + self.set_state(TestState.STOPPED) + self.save_data() + except requests.exceptions.ConnectionError: + pass + + def reset_unc_home(self): + try: + logging.info("removing the old directory") + shutil.rmtree(self.target_unc_home_path('data')) + except FileNotFoundError: + pass + logging.info('restoring data dir from backup') + shutil.copytree(self.home_path('backups', 'start'), + self.target_unc_home_path('data')) + logging.info('data dir restored') + self.set_state(TestState.STOPPED) + self.save_data() + + # periodically check if we should update uncd after a new epoch + def main_loop(self): + while True: + with self.lock: + state = self.get_state() + if state == TestState.AWAITING_NETWORK_INIT: + self.network_init() + elif state == TestState.AMEND_GENESIS: + self.check_amend_genesis() + elif state == TestState.STATE_ROOTS: + self.check_genesis_state() + elif state == TestState.RUNNING: + self.check_upgrade_neard() + elif state == TestState.RESETTING: + self.reset_unc_home() + time.sleep(10) + + def serve(self, port): + # TODO: maybe use asyncio? kind of silly to use multiple threads for + # something so lightweight + main_loop = threading.Thread(target=self.main_loop) + main_loop.start() + # this will listen only on the loopback interface and won't be accessible + # over the internet. If connecting to another machine, we can SSH and then make + # the request locally + s = RpcServer(('localhost', port), self) + s.serve_forever() + + +def main(): + parser = argparse.ArgumentParser(description='run uncd') + parser.add_argument('--home', type=str, required=True) + parser.add_argument('--uncd-home', type=str, required=True) + parser.add_argument('--uncd-logs-dir', type=str, required=True) + parser.add_argument('--port', type=int, required=True) + args = parser.parse_args() + + logging.basicConfig(format='[%(asctime)s] %(levelname)s: %(message)s', + level=logging.INFO) + + config_path = os.path.join(args.home, 'config.json') + if not os.path.isdir(args.home) or not os.path.exists(config_path): + sys.exit( + f'please create the directory at {args.home} and write a config file at {config_path}' + ) + + # only let one instance of this code run at a time + _fd = get_lock(args.home) + + logging.info("creating uncd runner") + runner = NeardRunner(args) + + logging.info("downloading binaries") + runner.download_binaries(force=False) + + logging.info("serve") + runner.serve(args.port) + + +if __name__ == '__main__': + main() diff --git a/pytest/tests/mocknet/helpers/requirements.txt b/pytest/tests/mocknet/helpers/requirements.txt new file mode 100644 index 000000000..e9fbac23b --- /dev/null +++ b/pytest/tests/mocknet/helpers/requirements.txt @@ -0,0 +1,3 @@ +json-rpc +psutil +requests \ No newline at end of file diff --git a/pytest/tests/mocknet/helpers/state_contract.rs b/pytest/tests/mocknet/helpers/state_contract.rs new file mode 100644 index 000000000..976cf647c --- /dev/null +++ b/pytest/tests/mocknet/helpers/state_contract.rs @@ -0,0 +1,53 @@ +// Very simple contract that can: +// - write to the state +// - delete from the state +// Independently from that the same contract can be used as a receiver for `ft_transfer_call`. +use unc_contract_standards::fungible_token::receiver::FungibleTokenReceiver; +use unc_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; +use unc_sdk::collections::LookupMap; +use unc_sdk::json_types::{ValidAccountId, U128}; +use unc_sdk::unc_bindgen; +use unc_sdk::PromiseOrValue; + +unc_sdk::setup_alloc!(); + +#[unc_bindgen] +#[derive(BorshDeserialize, BorshSerialize)] +pub struct StatusMessage { + records: LookupMap, +} + +impl Default for StatusMessage { + fn default() -> Self { + Self { records: LookupMap::new(b"r".to_vec()) } + } +} + +#[unc_bindgen] +impl StatusMessage { + pub fn set_state(&mut self, account_id: String, message: String) { + self.records.insert(&account_id, &message); + } + + pub fn delete_state(&mut self, account_id: String) { + self.records.remove(&account_id); + } + + pub fn get_state(&mut self, account_id: String) -> Option { + return self.records.get(&account_id); + } +} + +// Implements a callback which makes it possible to use `ft_transfer_call` with this contract as the +// receiver. The callback simply returns `1`. +#[unc_bindgen] +impl FungibleTokenReceiver for StatusMessage { + fn ft_on_transfer( + &mut self, + sender_id: ValidAccountId, + amount: U128, + msg: String, + ) -> PromiseOrValue { + PromiseOrValue::Value(1.into()) + } +} diff --git a/pytest/tests/mocknet/load_test_betanet.py b/pytest/tests/mocknet/load_test_betanet.py new file mode 100755 index 000000000..e3e49a857 --- /dev/null +++ b/pytest/tests/mocknet/load_test_betanet.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +import pathlib +import random +import sys +import time + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) +import account as account_mod +import key as key_mod +import mocknet +import mocknet_helpers +from helpers import load_test_utils + +from configured_logger import logger + +NUM_LETTERS = 26 +NUM_ACCOUNTS = NUM_LETTERS * 5 + + +def get_test_accounts_from_args(argv): + assert len(argv) == 7 + node_account_id = argv[1] + pk = argv[2] + sk = argv[3] + assert argv[4] + rpc_nodes = argv[4].split(',') + logger.info(f'rpc_nodes: {rpc_nodes}') + max_tps = float(argv[5]) + need_deploy = (argv[6] == 'deploy') + + logger.info(f'need_deploy: {need_deploy}') + + rpc_infos = [(rpc_addr, mocknet_helpers.RPC_PORT) for rpc_addr in rpc_nodes] + + node_account_key = key_mod.Key(node_account_id, pk, sk) + test_account_keys = [ + key_mod.Key(mocknet.load_testing_account_id(node_account_id, i), pk, sk) + for i in range(NUM_ACCOUNTS) + ] + + base_block_hash = mocknet_helpers.get_latest_block_hash( + addr=random.choice(rpc_nodes)) + node_account = account_mod.Account(node_account_key, + mocknet_helpers.get_nonce_for_pk( + node_account_key.account_id, + node_account_key.pk, + addr=random.choice(rpc_nodes)), + base_block_hash, + rpc_infos=rpc_infos) + + test_accounts = [] + for key in test_account_keys: + account = account_mod.Account(key, + mocknet_helpers.get_nonce_for_pk( + key.account_id, + key.pk, + addr=random.choice(rpc_nodes)), + base_block_hash, + rpc_infos=rpc_infos) + test_accounts.append(account) + + return load_test_utils.TestState(node_account, test_accounts, max_tps, + rpc_infos), need_deploy + + +def main(argv): + logger.info(argv) + test_state, need_deploy = get_test_accounts_from_args(argv) + + if need_deploy: + load_test_utils.init_ft(test_state.node_account) + for account in test_state.test_accounts: + logger.info( + f'Deploying contract for account {account.key.account_id}') + tx_res = account.send_deploy_contract_tx('betanet_state.wasm') + logger.info(f'Deploying result: {tx_res}') + load_test_utils.init_ft_account(test_state.node_account, account) + logger.info( + f'Account {account.key.account_id} balance after initialization: {account.get_amount_yoctonear()}' + ) + mocknet_helpers.wait_at_least_one_block() + + total_tx_sent = 0 + start_time = time.monotonic() + while True: + elapsed_time = time.monotonic() - start_time + total_tx_sent = mocknet_helpers.throttle_txns( + load_test_utils.send_random_transactions, total_tx_sent, + elapsed_time, test_state) + + +if __name__ == '__main__': + main(sys.argv) diff --git a/pytest/tests/mocknet/load_test_spoon.py b/pytest/tests/mocknet/load_test_spoon.py new file mode 100755 index 000000000..342b91382 --- /dev/null +++ b/pytest/tests/mocknet/load_test_spoon.py @@ -0,0 +1,420 @@ +#!/usr/bin/env python3 +""" +Runs a loadtest on mocknet. + +The setup requires you to have a few nodes that will be validators and at least one node that will be an RPC node. +Specify the number of validator nodes using the `--num-nodes` flag. + +Use https://github.com/near/unc-ops/tree/master/provisioning/terraform/network/mocknet to bring up a set of VM +instances for the test. It also takes care of downloading necessary binaries and smart contracts. + +Depending on the `--chain-id` flag, the nodes will be initialized with an empty state, or with the most recent state +dump of `mainnet`, `testnet` and `betanet`. Note that `mainnet` and `testnet` state dumps are huge. Starting a node with +`testnet` state dump takes about 1.5 hours. + +You can run multiple tests in parallel, use `--pattern` to disambiguate. + +Configure the desirable generated load with the `--max-tps` flag, or disable load altogether with `--skip-load`. + +Example from the recent loadtest run: +1) terraform apply -var="chain_id=mainnet" -var="size=big" -var="override_chain_id=rc3-22" -var="neard_binary_url=https://s3.us-west-1.amazonaws.com/build.nearprotocol.com/framework/Linux/1.23.0/1eaa01d6abc76757b2ef50a1a127f98576b750c4/uncd" -var="upgrade_neard_binary_url=https://unc-protocol-public.s3.ca-central-1.amazonaws.com/mocknet/uncd.rc3-22" +2) python3 tests/mocknet/load_test_spoon.py --chain-id=mainnet-spoon --pattern=rc3-22 --epoch-length=1000 --num-nodes=120 --max-tps=100 --script=add_and_delete --increasing-stakes=1 --progressive-upgrade --num-seats=100 + +Things to look out for when running the test: +1) Test init phase completes before any binary upgrades start. +2) At the beginning of each of the first epochs some nodes get upgraded. +3) If the protocol upgrades becomes effective at epoch T, check that binaries upgraded at epochs T-1, T-2 have started successfully. +4) BPS and TPS need to be at some sensible values. +5) CPU usage and RAM usage need to be reasonable as well. +6) Ideally we should check the percentage of generated transactions that succeed, but there is no easy way to do that. Maybe replay some blocks using `uncd view_state apply_range --help`. + +Other notes: +1) This grafana dashboard can help: https://grafana.near.org/d/jHbiNgSnz/mocknet +2) Logs are in /home/ubuntu/uncd.log and /home/ubuntu/uncd.upgrade.log + +For the terraform command to work, please do the following: +1) Install terraform cli: https://learn.hashicorp.com/tutorials/terraform/install-cli +2) git clone https://github.com/near/unc-ops +3) cd provisioning/terraform/network/mocknet +4) Run `terraform init` +5) Run `terraform apply` as specified above and check the output. +6) If you don't have permissions for the GCP project `unc-mocknet`, please ask your friendly SREs to give you permissions. +7) Make the pair of uncd binaries available for download. The pair of binaries is the baseline version, e.g. the current mainnet binary version, and your experimental binary. The simplest way to make the binaries available is to upload them to https://s3.console.aws.amazon.com/s3/buckets/unc-protocol-public?region=ca-central-1&prefix=mocknet/ +8) If you don't have access to S3 AWS, please ask your friendly SREs to give you access. +9) A reliable way to make an experimental binary compatible with mocknet instances, is to build it on machine "builder" in project "unc-mocknet". You may need to "Resume" the instance in the GCP console: https://console.cloud.google.com/compute/instancesDetail/zones/us-central1-a/instances/builder?project=unc-mocknet + +For the `load_test_spoon.py` to work, please do the following: +1) git clone https://github.com/utnet-org/utility +2) cd pytest +3) Follow instructions in https://github.com/utnet-org/utility/blob/master/pytest/README.md to setup your Python environment +4) Run load_test_spoon.py as specified above. + +""" +import argparse +import random +import sys +import time +from rc import pmap +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import mocknet +import data + +from metrics import Metrics +from configured_logger import logger + + +def measure_tps_bps(nodes, tx_filename): + input_tx_events = mocknet.get_tx_events(nodes, tx_filename) + # drop first and last 5% of events to avoid edges of test + n = int(0.05 * len(input_tx_events)) + input_tx_events = input_tx_events[n:-n] + input_tps = data.compute_rate(input_tx_events) + measurement = mocknet.chain_measure_bps_and_tps( + nodes[-1], + input_tx_events[0], + input_tx_events[-1], + ) + result = { + 'bps': measurement['bps'], + 'in_tps': input_tps, + 'out_tps': measurement['tps'] + } + logger.info(f'{result}') + return result + + +def check_tps(measurement, expected_in, expected_out=None, tolerance=0.05): + if expected_out is None: + expected_out = expected_in + almost_equal = lambda x, y: (abs(x - y) / y) <= tolerance + return (almost_equal(measurement['in_tps'], expected_in) and + almost_equal(measurement['out_tps'], expected_out)) + + +def check_memory_usage(node): + metrics = mocknet.get_metrics(node) + mem_usage = metrics.memory_usage / 1e6 + logger.info(f'Memory usage (MB) = {mem_usage}') + return mem_usage < 4500 + + +def check_slow_blocks(initial_metrics, final_metrics): + delta = Metrics.diff(final_metrics, initial_metrics) + slow_process_blocks = delta.block_processing_time[ + 'le +Inf'] - delta.block_processing_time['le 1'] + logger.info( + f'Number of blocks processing for more than 1s: {slow_process_blocks}') + return slow_process_blocks == 0 + + +class LoadTestSpoon: + EPOCH_HEIGHT_CHECK_DELAY = 30 + + def run(self): + logger.info('Starting load test spoon.') + + self.__parse_args() + + self.__prepare_nodes() + + self.__prepare_upgrade_schedule() + + if not self.skip_setup: + self.__setup_remote_python_environments() + + if not self.skip_restart: + self.__upload_genesis_and_restart() + + mocknet.wait_all_nodes_up(self.all_nodes) + + initial_metrics = mocknet.get_metrics(self.archival_node) + initial_validator_accounts = mocknet.list_validators(self.archival_node) + + if not self.skip_load: + self.__load() + + time.sleep(5) + + final_metrics = mocknet.get_metrics(self.archival_node) + final_validator_accounts = mocknet.list_validators(self.archival_node) + + self.__final_checks( + initial_metrics, + final_metrics, + initial_validator_accounts, + final_validator_accounts, + ) + + logger.info('Load test complete.') + + def __parse_args(self): + parser = argparse.ArgumentParser(description='Run a load test') + parser.add_argument('--chain-id', required=True) + parser.add_argument('--pattern', required=False) + parser.add_argument('--epoch-length', type=int, required=True) + parser.add_argument('--num-nodes', type=int, required=True) + parser.add_argument('--max-tps', type=float, required=True) + parser.add_argument('--increasing-stakes', type=float, default=0.0) + parser.add_argument('--progressive-upgrade', action='store_true') + + parser.add_argument('--skip-load', action='store_true') + parser.add_argument('--skip-setup', action='store_true') + parser.add_argument('--skip-restart', action='store_true') + parser.add_argument('--no-sharding', action='store_true') + parser.add_argument('--num-seats', type=int, required=True) + + parser.add_argument('--test-timeout', type=int, default=12 * 60 * 60) + parser.add_argument( + '--contract-deploy-time', + type=int, + default=10 * mocknet.NUM_ACCOUNTS, + help= + 'We need to slowly deploy contracts, otherwise we stall out the nodes', + ) + + parser.add_argument('--log-level', default="INFO") + + # The flag is no longer needed but is kept for backwards-compatibility. + parser.add_argument('--script', required=False) + + args = parser.parse_args() + + logger.setLevel(args.log_level) + + self.chain_id = args.chain_id + self.pattern = args.pattern + self.epoch_length = args.epoch_length + self.num_nodes = args.num_nodes + self.max_tps = args.max_tps + + self.increasing_stakes = args.increasing_stakes + self.progressive_upgrade = args.progressive_upgrade + self.num_seats = args.num_seats + + self.skip_setup = args.skip_setup + self.skip_restart = args.skip_restart + self.skip_load = args.skip_load + self.no_sharding = args.no_sharding + + self.test_timeout = args.test_timeout + self.contract_deploy_time = args.contract_deploy_time + + self.log_level = args.log_level + + assert self.epoch_length > 0 + assert self.num_nodes > 0 + assert self.max_tps > 0 + + def __prepare_nodes(self): + all_nodes = mocknet.get_nodes(pattern=self.pattern) + random.shuffle(all_nodes) + assert len(all_nodes) > self.num_nodes, \ + f'Need at least one RPC node. ' \ + f'Nodes available in mocknet: {len(all_nodes)} ' \ + f'Requested validator nodes num: {self.num_nodes}' + + self.all_nodes = all_nodes + self.validator_nodes = all_nodes[:self.num_nodes] + self.rpc_nodes = all_nodes[self.num_nodes:] + self.archival_node = self.rpc_nodes[0] + + logger.info( + f'validator_nodes: {[n.instance_name for n in self.validator_nodes]}' + ) + logger.info( + f'rpc_nodes : {[n.instance_name for n in self.rpc_nodes]}') + logger.info(f'archival node : {self.archival_node.instance_name}') + + def __prepare_upgrade_schedule(self): + logger.info(f'Preparing upgrade schedule') + self.upgrade_schedule = mocknet.create_upgrade_schedule( + self.rpc_nodes, + self.validator_nodes, + self.progressive_upgrade, + self.increasing_stakes, + self.num_seats, + ) + logger.info(f'Preparing upgrade schedule -- done.') + + def __setup_remote_python_environments(self): + logger.info('Setting remote python environments') + mocknet.setup_python_environments( + self.all_nodes, + 'add_and_delete_state.wasm', + ) + logger.info('Setting remote python environments -- done') + + def __upload_genesis_and_restart(self): + logger.info(f'Restarting') + # Make sure nodes are running by restarting them. + mocknet.stop_nodes(self.all_nodes) + time.sleep(10) + validator_node_pks = pmap( + lambda node: mocknet.get_node_keys(node)[0], + self.validator_nodes, + ) + all_node_pks = pmap( + lambda node: mocknet.get_node_keys(node)[0], + self.all_nodes, + ) + pmap(lambda node: mocknet.init_validator_key(node), self.all_nodes) + node_ips = [node.machine.ip for node in self.all_nodes] + mocknet.create_and_upload_genesis( + self.validator_nodes, + self.chain_id, + rpc_nodes=self.rpc_nodes, + epoch_length=self.epoch_length, + node_pks=validator_node_pks, + increasing_stakes=self.increasing_stakes, + num_seats=self.num_seats, + single_shard=self.no_sharding, + all_node_pks=all_node_pks, + node_ips=node_ips, + ) + mocknet.start_nodes(self.all_nodes, self.upgrade_schedule) + time.sleep(60) + + logger.info(f'Restarting -- done') + + def __start_load_test_helpers(self): + logger.info('Starting transaction spamming scripts.') + script = 'load_test_spoon_helper.py' + mocknet.start_load_test_helpers( + script, + self.validator_nodes, + self.rpc_nodes, + self.max_tps, + # Make the helper run a bit longer - there is significant delay + # between the time when the first helper is started and when this + # scipts begins the test. + self.test_timeout + 4 * self.EPOCH_HEIGHT_CHECK_DELAY, + self.contract_deploy_time, + ) + logger.info('Starting transaction spamming scripts -- done.') + + def __check_neard_and_helper_are_running(self): + neard_running = mocknet.is_binary_running_all_nodes( + 'uncd', + self.all_nodes, + ) + helper_running = mocknet.is_binary_running_all_nodes( + 'load_test_spoon_helper.py', + self.validator_nodes, + ) + + logger.debug( + f'uncd is running on {neard_running.count(True)}/{len(neard_running)} nodes' + ) + logger.debug( + f'helper is running on {helper_running.count(True)}/{len(helper_running)} validator nodes' + ) + + for node, is_running in zip(self.all_nodes, neard_running): + if not is_running: + raise Exception( + 'The uncd process is not running on some of the nodes! ' + f'The uncd is not running on {node.instance_name}') + + for node, is_running in zip(self.validator_nodes, helper_running): + if not is_running: + raise Exception( + 'The helper process is not running on some of the nodes! ' + f'The helper is not running on {node.instance_name}') + + def __wait_to_deploy_contracts(self): + msg = 'Waiting for the helper to deploy contracts' + logger.info(f'{msg}: {self.contract_deploy_time}s') + + start_time = time.monotonic() + while True: + self.__check_neard_and_helper_are_running() + + now = time.monotonic() + remaining_time = self.contract_deploy_time + start_time - now + logger.info(f'{msg}: {round(remaining_time)}s') + if remaining_time < 0: + break + + time.sleep(self.EPOCH_HEIGHT_CHECK_DELAY) + + def __wait_to_complete(self): + msg = 'Waiting for the loadtest to complete' + logger.info(f'{msg}: {self.test_timeout}s') + + initial_epoch_height = mocknet.get_epoch_height(self.rpc_nodes, -1) + logger.info(f'initial_epoch_height: {initial_epoch_height}') + assert initial_epoch_height >= 0 + + prev_epoch_height = initial_epoch_height + start_time = time.monotonic() + while True: + self.__check_neard_and_helper_are_running() + + epoch_height = mocknet.get_epoch_height( + self.rpc_nodes, + prev_epoch_height, + ) + if epoch_height > prev_epoch_height: + mocknet.upgrade_nodes( + epoch_height - initial_epoch_height, + self.upgrade_schedule, + self.all_nodes, + ) + prev_epoch_height = epoch_height + + remaining_time = self.test_timeout + start_time - time.monotonic() + logger.info(f'{msg}: {round(remaining_time)}s') + if remaining_time < 0: + break + + time.sleep(self.EPOCH_HEIGHT_CHECK_DELAY) + + logger.info(f'{msg} -- done') + + def __load(self): + msg = 'Running load test' + logger.info(f'{msg}') + + self.__start_load_test_helpers() + + self.__wait_to_deploy_contracts() + + self.__wait_to_complete() + + logger.info(f'{msg} -- done') + + def __final_checks( + self, + initial_metrics, + final_metrics, + initial_validator_accounts, + final_validator_accounts, + ): + msg = 'Running final checks' + logger.info(msg) + + test_passed = True + if not check_memory_usage(self.validator_nodes[0]): + test_passed = False + logger.error('Memory usage too large') + if not check_slow_blocks(initial_metrics, final_metrics): + test_passed = False + logger.error('Too many slow blocks') + + if set(initial_validator_accounts) != set(final_validator_accounts): + logger.warning( + f'Mismatching set of validators:\n' + f'initial_validator_accounts: {initial_validator_accounts}\n' + f'final_validator_accounts : {final_validator_accounts}') + + assert test_passed + + logger.info(f'{msg} -- done.') + + +if __name__ == '__main__': + load_test_spoon = LoadTestSpoon() + load_test_spoon.run() diff --git a/pytest/tests/mocknet/locust.py b/pytest/tests/mocknet/locust.py new file mode 100644 index 000000000..b9681bda0 --- /dev/null +++ b/pytest/tests/mocknet/locust.py @@ -0,0 +1,237 @@ +import argparse +import cmd_utils +import pathlib +from rc import pmap, run +import sys + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import mocknet + +from configured_logger import logger + + +def init_locust_node(instance_name): + node = mocknet.get_node(instance_name) + if node is None: + sys.exit(f'could not find node {instance_name}') + cmd_utils.init_node(node) + commands = [ + 'sudo apt update', + 'sudo apt-get install -y git virtualenv build-essential python3-dev', + 'git clone https://github.com/utnet-org/utility /home/ubuntu/framework', + 'mkdir /home/ubuntu/locust', + 'cd /home/ubuntu/locust && python3 -m virtualenv venv -p $(which python3)', + './venv/bin/pip install -r /home/ubuntu/framework/pytest/requirements.txt', + './venv/bin/pip install locust', + ] + init_command = ' && '.join(commands) + cmd_utils.run_cmd(node, init_command) + + +def init_cmd(args): + nodes = [x for x in args.instance_names.split(',') if len(x) > 0] + pmap(init_locust_node, nodes) + + +def parse_instsance_names(args): + if args.master is None: + print('master instance name?: ') + args.master = sys.stdin.readline().strip() + + if args.workers is None: + print(''' +worker instance names? Give a comma separated list. It is also valid to +have the machine used for the master process in this list as well:''') + args.workers = sys.stdin.readline().strip() + + master = mocknet.get_node(args.master) + if master is None: + sys.exit(f'could not find node {args.master}') + + worker_names = [x for x in args.workers.split(',') if len(x) > 0] + workers = [ + mocknet.get_node(instance_name) for instance_name in worker_names + ] + for (name, node) in zip(worker_names, workers): + if node is None: + sys.exit(f'could not find node {name}') + + return master, workers + + +def upload_key(node, filename): + node.machine.upload(args.funding_key, + '/home/ubuntu/locust/funding_key.json', + switch_user='ubuntu') + + +def run_master(args, node, num_workers): + upload_key(node, args.funding_key) + cmd = f'/home/ubuntu/locust/venv/bin/python3 -m locust --web-port 3030 --master-bind-port 3000 -H {args.node_ip_port} -f locustfiles/{args.locustfile} --shard-layout-chain-id mainnet --funding-key=/home/ubuntu/locust/funding_key.json --max-workers {args.max_workers} --master' + if args.num_users is not None: + cmd += f' --users {args.num_users}' + if args.run_time is not None: + cmd += f' --run-time {args.run_time}' + if not args.web_ui: + cmd += f' --headless --expect-workers {num_workers}' + + logger.info(f'running "{cmd}" on master node {node.instance_name}') + cmd_utils.run_in_background( + node, + cmd, + 'locust-master.txt', + pre_cmd= + 'ulimit -S -n 100000 && cd /home/ubuntu/framework/pytest/tests/loadtest/locust' + ) + + +def wait_locust_inited(node, log_filename): + # We want to wait for the locust process to finish the initialization steps. Is there a better way than + # just waiting for the string "Starting Locust" to appear in the logs? + cmd_utils.run_cmd( + node, + f'tail -f {cmd_utils.LOG_DIR}/{log_filename}.0 | grep --line-buffered -m 1 -q "Starting Locust"' + ) + + +def wait_master_inited(node): + wait_locust_inited(node, 'locust-master.txt') + logger.info(f'master locust node initialized') + + +def wait_worker_inited(node): + wait_locust_inited(node, 'locust-worker.txt') + logger.info(f'worker locust node {node.instance_name} initialized') + + +def run_worker(args, node, master_ip): + cmd = f'/home/ubuntu/locust/venv/bin/python3 -m locust --web-port 3030 -H {args.node_ip_port} -f locustfiles/{args.locustfile} --shard-layout-chain-id mainnet --funding-key=/home/ubuntu/locust/funding_key.json --worker --master-port 3000' + if master_ip != node.machine.ip: + # if this node is also the master node, the key has already been uploaded + upload_key(node, args.funding_key) + cmd += f' --master-host {master_ip}' + logger.info(f'running "{cmd}" on worker node {node.instance_name}') + cmd_utils.run_in_background( + node, + cmd, + 'locust-worker.txt', + pre_cmd= + 'ulimit -S -n 100000 && cd /home/ubuntu/framework/pytest/tests/loadtest/locust' + ) + + +def run_cmd(args): + if not args.web_ui and args.num_users is None: + sys.exit('unless you pass --web-ui, --num-users must be set') + + master, workers = parse_instsance_names(args) + + run_master(args, master, len(workers)) + if args.web_ui: + wait_master_inited(master) + pmap(lambda n: run_worker(args, n, master.machine.ip), workers) + if args.web_ui: + pmap(wait_worker_inited, workers) + logger.info( + f'All locust workers initialized. Visit http://{master.machine.ip}:3030/ to start and control the test' + ) + else: + logger.info('All workers started.') + + +def stop_cmd(args): + master, workers = parse_instsance_names(args) + # TODO: this feels kind of imprecise and heavy-handed, since we're just looking for a command that matches "python3.*locust.*master" and killing it, + # instead of remembering what the process' IP was. Should be possible to do this right, but this will work for now + cmd_utils.run_cmd( + master, + 'pids=$(ps -C python3 -o pid=,cmd= | grep "locust" | cut -d " " -f 2) && if [ ! -z "$pids" ]; then kill $pids; fi' + ) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Run a locust load test') + + subparsers = parser.add_subparsers(title='subcommands', + description='valid subcommands', + help='additional help') + + init_parser = subparsers.add_parser('init', + help=''' + Sets up the python environment and downloads the code on each node. + ''') + init_parser.add_argument('--instance-names', type=str) + init_parser.set_defaults(func=init_cmd) + + run_parser = subparsers.add_parser('run', + help=''' + Runs the locust load test on each node. + ''') + run_parser.add_argument('--master', + type=str, + required=True, + help='instance name of master node') + run_parser.add_argument( + '--workers', + type=str, + required=True, + help='comma-separated list of instance names of worker nodes') + run_parser.add_argument( + '--node-ip-port', + type=str, + required=True, + help='IP address and port of a node in the network under test') + run_parser.add_argument( + '--funding-key', + type=str, + required=True, + help= + 'local path to a key file for the base account to be used in the test') + run_parser.add_argument( + '--locustfile', + type=str, + default='ft.py', + help= + 'locustfile name in framework/pytest/tests/loadtest/locust/locustfiles') + run_parser.add_argument( + '--max-workers', + type=int, + default=16, + help='max number of workers the test should support') + run_parser.add_argument( + '--web-ui', + action='store_true', + help= + 'if given, sets up a web UI to control the test, otherwise starts automatically' + ) + run_parser.add_argument( + '--num-users', + type=int, + help= + 'number of users to run the test with. Required unless --web-ui is given.' + ) + run_parser.add_argument( + '--run-time', + type=str, + help= + 'A string specifying the total run time of the test, passed to the locust --run-time argument. e.g. (300s, 20m, 3h, 1h30m, etc.)' + ) + run_parser.set_defaults(func=run_cmd) + + stop_parser = subparsers.add_parser('stop', + help=''' + Stops the locust load test on each node. + ''') + stop_parser.add_argument('--master', + type=str, + help='instance name of master node') + stop_parser.add_argument( + '--workers', + type=str, + help='comma-separated list of instance names of worker nodes') + stop_parser.set_defaults(func=stop_cmd) + + args = parser.parse_args() + + args.func(args) diff --git a/pytest/tests/mocknet/mirror.py b/pytest/tests/mocknet/mirror.py new file mode 100755 index 000000000..3002b0860 --- /dev/null +++ b/pytest/tests/mocknet/mirror.py @@ -0,0 +1,540 @@ +#!/usr/bin/env python3 +""" + +""" +from argparse import ArgumentParser, BooleanOptionalAction +import cmd_utils +import pathlib +import json +import random +from rc import pmap, run +import requests +import sys +import time + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import mocknet + +from configured_logger import logger + + +def get_nodes(args): + pattern = args.chain_id + '-' + str( + args.start_height) + '-' + args.unique_id + all_nodes = mocknet.get_nodes(pattern=pattern) + if len(all_nodes) < 1: + sys.exit(f'no known nodes matching {pattern}') + + traffic_generator = None + nodes = [] + for n in all_nodes: + if n.instance_name.endswith('traffic'): + if traffic_generator is not None: + sys.exit( + f'more than one traffic generator instance found. {traffic_generator.instance_name} and {n.instance_name}' + ) + traffic_generator = n + else: + nodes.append(n) + + if traffic_generator is None: + sys.exit(f'no traffic generator instance found') + return traffic_generator, nodes + + +def wait_node_up(node): + while True: + try: + res = node.get_validators() + if 'error' not in res: + assert 'result' in res + logger.info(f'Node {node.instance_name} is up') + return + except (ConnectionRefusedError, + requests.exceptions.ConnectionError) as e: + pass + time.sleep(10) + + +def prompt_setup_flags(args): + if not args.yes: + print( + 'this will reset all nodes\' home dirs and initialize them with new state. continue? [yes/no]' + ) + if sys.stdin.readline().strip() != 'yes': + sys.exit() + + if args.epoch_length is None: + print('epoch length for the initialized genesis file?: ') + args.epoch_length = int(sys.stdin.readline().strip()) + + if args.num_validators is None: + print('number of validators?: ') + args.num_validators = int(sys.stdin.readline().strip()) + + if args.num_seats is None: + print('number of block producer seats?: ') + args.num_seats = int(sys.stdin.readline().strip()) + + if args.genesis_protocol_version is None: + print('genesis protocol version?: ') + args.genesis_protocol_version = int(sys.stdin.readline().strip()) + + +def start_neard_runner(node): + cmd_utils.run_in_background(node, f'/home/ubuntu/uncd-runner/venv/bin/python /home/ubuntu/uncd-runner/neard_runner.py ' \ + '--home /home/ubuntu/uncd-runner --uncd-home /home/ubuntu/.near ' \ + '--uncd-logs /home/ubuntu/uncd-logs --port 3000', 'uncd-runner.txt') + + +def upload_neard_runner(node): + node.machine.upload('tests/mocknet/helpers/neard_runner.py', + '/home/ubuntu/uncd-runner', + switch_user='ubuntu') + node.machine.upload('tests/mocknet/helpers/requirements.txt', + '/home/ubuntu/uncd-runner', + switch_user='ubuntu') + + +def init_neard_runner(node, config, remove_home_dir=False): + stop_neard_runner(node) + cmd_utils.init_node(node) + if remove_home_dir: + cmd_utils.run_cmd( + node, + 'rm -rf /home/ubuntu/uncd-runner && mkdir -p /home/ubuntu/uncd-runner' + ) + else: + cmd_utils.run_cmd(node, 'mkdir -p /home/ubuntu/uncd-runner') + upload_neard_runner(node) + mocknet.upload_json(node, '/home/ubuntu/uncd-runner/config.json', config) + cmd = 'cd /home/ubuntu/uncd-runner && python3 -m virtualenv venv -p $(which python3)' \ + ' && ./venv/bin/pip install -r requirements.txt' + cmd_utils.run_cmd(node, cmd) + start_neard_runner(node) + + +def stop_neard_runner(node): + # it's probably fine for now, but this is very heavy handed/not precise + node.machine.run('kill $(ps -C python -o pid=)') + + +def prompt_init_flags(args): + if args.neard_binary_url is None: + print('uncd binary URL?: ') + args.neard_binary_url = sys.stdin.readline().strip() + assert len(args.neard_binary_url) > 0 + + if args.neard_upgrade_binary_url is None: + print( + 'add a second uncd binary URL to upgrade to mid-test? enter nothing here to skip: ' + ) + url = sys.stdin.readline().strip() + if len(url) > 0: + args.neard_upgrade_binary_url = url + + +def init_neard_runners(args, traffic_generator, nodes, remove_home_dir=False): + prompt_init_flags(args) + if args.neard_upgrade_binary_url is None: + configs = [{ + "is_traffic_generator": False, + "binaries": [{ + "url": args.neard_binary_url, + "epoch_height": 0 + }] + }] * len(nodes) + traffic_generator_config = { + "is_traffic_generator": True, + "binaries": [{ + "url": args.neard_binary_url, + "epoch_height": 0 + }] + } + else: + # for now this test starts all validators with the same stake, so just make the upgrade + # epoch random. If we change the stakes, we should change this to choose how much stake + # we want to upgrade during each epoch + configs = [] + for i in range(len(nodes)): + configs.append({ + "is_traffic_generator": + False, + "binaries": [{ + "url": args.neard_binary_url, + "epoch_height": 0 + }, { + "url": args.neard_upgrade_binary_url, + "epoch_height": random.randint(1, 4) + }] + }) + traffic_generator_config = { + "is_traffic_generator": + True, + "binaries": [{ + "url": args.neard_upgrade_binary_url, + "epoch_height": 0 + }] + } + + init_neard_runner(traffic_generator, traffic_generator_config, + remove_home_dir) + pmap(lambda x: init_neard_runner(x[0], x[1], remove_home_dir), + zip(nodes, configs)) + + +def init_cmd(args, traffic_generator, nodes): + init_neard_runners(args, traffic_generator, nodes, remove_home_dir=False) + + +def hard_reset_cmd(args, traffic_generator, nodes): + print(""" + WARNING!!!! + WARNING!!!! + This will undo all chain state, which will force a restart from the beginning, + icluding the genesis state computation which takes several hours. + Continue? [yes/no]""") + if sys.stdin.readline().strip() != 'yes': + return + all_nodes = nodes + [traffic_generator] + pmap(stop_neard_runner, all_nodes) + mocknet.stop_nodes(all_nodes) + init_neard_runners(args, traffic_generator, nodes, remove_home_dir=True) + + +def restart_cmd(args, traffic_generator, nodes): + all_nodes = nodes + [traffic_generator] + pmap(stop_neard_runner, all_nodes) + if args.upload_program: + pmap(upload_neard_runner, all_nodes) + pmap(start_neard_runner, all_nodes) + + +# returns boot nodes and validators we want for the new test network +def get_network_nodes(new_test_rpc_responses, num_validators): + validators = [] + boot_nodes = [] + for ip_addr, response in new_test_rpc_responses: + if len(validators) < num_validators: + if response['validator_account_id'] is not None: + # we assume here that validator_account_id is not null, validator_public_key + # better not be null either + validators.append({ + 'account_id': response['validator_account_id'], + 'public_key': response['validator_public_key'], + 'amount': str(10**33), + }) + if len(boot_nodes) < 20: + boot_nodes.append( + f'{response["node_key"]}@{ip_addr}:{response["listen_port"]}') + + if len(validators) >= num_validators and len(boot_nodes) >= 20: + break + # neither of these should happen, since we check the number of available nodes in new_test(), and + # only the traffic generator will respond with null validator_account_id and validator_public_key + if len(validators) == 0: + sys.exit('no validators available after new_test RPCs') + if len(validators) < num_validators: + logger.warning( + f'wanted {num_validators} validators, but only {len(validators)} available' + ) + return validators, boot_nodes + + +def new_test(args, traffic_generator, nodes): + prompt_setup_flags(args) + + if args.epoch_length <= 0: + sys.exit(f'--epoch-length should be positive') + if args.num_validators <= 0: + sys.exit(f'--num-validators should be positive') + if len(nodes) < args.num_validators: + sys.exit( + f'--num-validators is {args.num_validators} but only found {len(nodes)} under test' + ) + + all_nodes = nodes + [traffic_generator] + + logger.info(f'resetting/initializing home dirs') + test_keys = pmap(neard_runner_new_test, all_nodes) + + validators, boot_nodes = get_network_nodes( + zip([n.machine.ip for n in all_nodes], test_keys), args.num_validators) + + logger.info("""setting validators: {0} +Then running uncd amend-genesis on all nodes, and starting uncd to compute genesis \ +state roots. This will take a few hours. Run `status` to check if the nodes are \ +ready. After they're ready, you can run `start-traffic`""".format(validators)) + pmap( + lambda node: neard_runner_network_init( + node, validators, boot_nodes, args.epoch_length, args.num_seats, + args.genesis_protocol_version), all_nodes) + + +def status_cmd(args, traffic_generator, nodes): + all_nodes = nodes + [traffic_generator] + statuses = pmap(neard_runner_ready, all_nodes) + num_ready = 0 + not_ready = [] + for ready, node in zip(statuses, all_nodes): + if not ready: + not_ready.append(node.instance_name) + + if len(not_ready) == 0: + print(f'all {len(all_nodes)} nodes ready') + else: + print( + f'{len(all_nodes)-len(not_ready)}/{len(all_nodes)} ready. Nodes not ready: {not_ready[:3]}' + ) + + +def reset_cmd(args, traffic_generator, nodes): + if not args.yes: + print( + 'this will reset all nodes\' home dirs to their initial states right after test initialization finished. continue? [yes/no]' + ) + if sys.stdin.readline().strip() != 'yes': + sys.exit() + all_nodes = nodes + [traffic_generator] + pmap(neard_runner_reset, all_nodes) + logger.info( + 'Data dir reset in progress. Run the `status` command to see when this is finished. Until it is finished, uncd runners may not respond to HTTP requests.' + ) + + +def stop_nodes_cmd(args, traffic_generator, nodes): + pmap(neard_runner_stop, nodes + [traffic_generator]) + + +def stop_traffic_cmd(args, traffic_generator, nodes): + neard_runner_stop(traffic_generator) + + +def neard_runner_jsonrpc(node, method, params=[]): + body = { + 'method': method, + 'params': params, + 'id': 'dontcare', + 'jsonrpc': '2.0' + } + body = json.dumps(body) + # '"'"' will be interpreted as ending the first quote and then concatenating it with "'", + # followed by a new quote started with ' and the rest of the string, to get any single quotes + # in method or params into the command correctly + body = body.replace("'", "'\"'\"'") + r = cmd_utils.run_cmd(node, f'curl localhost:3000 -d \'{body}\'') + response = json.loads(r.stdout) + if 'error' in response: + # TODO: errors should be handled better here in general but just exit for now + sys.exit( + f'bad response trying to send {method} JSON RPC to uncd runner on {node.instance_name}:\n{response}' + ) + return response['result'] + + +def neard_runner_start(node): + neard_runner_jsonrpc(node, 'start') + + +def neard_runner_stop(node): + neard_runner_jsonrpc(node, 'stop') + + +def neard_runner_new_test(node): + return neard_runner_jsonrpc(node, 'new_test') + + +def neard_runner_network_init(node, validators, boot_nodes, epoch_length, + num_seats, protocol_version): + return neard_runner_jsonrpc(node, + 'network_init', + params={ + 'validators': validators, + 'boot_nodes': boot_nodes, + 'epoch_length': epoch_length, + 'num_seats': num_seats, + 'protocol_version': protocol_version, + }) + + +def neard_update_config(node, key_value): + return neard_runner_jsonrpc( + node, + 'update_config', + params={ + "key_value": key_value, + }, + ) + + +def update_config_cmd(args, traffic_generator, nodes): + nodes = nodes + [traffic_generator] + results = pmap( + lambda node: neard_update_config( + node, + args.set, + ), + nodes, + ) + if not all(results): + logger.warn('failed to update configs for some nodes') + return + + +def neard_runner_ready(node): + return neard_runner_jsonrpc(node, 'ready') + + +def neard_runner_reset(node): + return neard_runner_jsonrpc(node, 'reset') + + +def start_nodes_cmd(args, traffic_generator, nodes): + if not all(pmap(neard_runner_ready, nodes)): + logger.warn( + 'not all nodes are ready to start yet. Run the `status` command to check their statuses' + ) + return + pmap(neard_runner_start, nodes) + pmap(wait_node_up, nodes) + + +def start_traffic_cmd(args, traffic_generator, nodes): + if not all(pmap(neard_runner_ready, nodes + [traffic_generator])): + logger.warn( + 'not all nodes are ready to start yet. Run the `status` command to check their statuses' + ) + return + pmap(neard_runner_start, nodes) + logger.info("waiting for validators to be up") + pmap(wait_node_up, nodes) + logger.info( + "waiting a bit after validators started before starting traffic") + time.sleep(10) + neard_runner_start(traffic_generator) + logger.info( + f'test running. to check the traffic sent, try running "curl http://{traffic_generator.machine.ip}:3030/metrics | grep mirror"' + ) + + +def neard_runner_update_binaries(node): + neard_runner_jsonrpc(node, 'update_binaries') + + +def update_binaries_cmd(args, traffic_generator, nodes): + pmap(neard_runner_update_binaries, nodes + [traffic_generator]) + + +if __name__ == '__main__': + parser = ArgumentParser(description='Run a load test') + parser.add_argument('--chain-id', type=str, required=True) + parser.add_argument('--start-height', type=int, required=True) + parser.add_argument('--unique-id', type=str, required=True) + + subparsers = parser.add_subparsers(title='subcommands', + description='valid subcommands', + help='additional help') + + init_parser = subparsers.add_parser('init-uncd-runner', + help=''' + Sets up the helper servers on each of the nodes. Doesn't start initializing the test + state, which is done with the `new-test` command. + ''') + init_parser.add_argument('--uncd-binary-url', type=str) + init_parser.add_argument('--uncd-upgrade-binary-url', type=str) + init_parser.set_defaults(func=init_cmd) + + update_config_parser = subparsers.add_parser( + 'update-config', + help='''Update config.json with given flags for all nodes.''') + update_config_parser.add_argument( + '--set', + help=''' + A key value pair to set in the config. The key will be interpreted as a + json path to the config to be updated. The value will be parsed as json. + e.g. + --set 'aaa.bbb.ccc=5' + --set 'aaa.bbb.ccc="5"' + --set 'aaa.bbb.ddd={"eee":6,"fff":"7"}' # no spaces! + ''', + ) + update_config_parser.set_defaults(func=update_config_cmd) + + restart_parser = subparsers.add_parser( + 'restart-uncd-runner', + help='''Restarts the uncd runner on all nodes.''') + restart_parser.add_argument('--upload-program', action='store_true') + restart_parser.set_defaults(func=restart_cmd, upload_program=False) + + hard_reset_parser = subparsers.add_parser( + 'hard-reset', + help='''Stops uncd and clears all test state on all nodes.''') + hard_reset_parser.add_argument('--uncd-binary-url', type=str) + hard_reset_parser.add_argument('--uncd-upgrade-binary-url', type=str) + hard_reset_parser.set_defaults(func=hard_reset_cmd) + + new_test_parser = subparsers.add_parser('new-test', + help=''' + Sets up new state from the prepared records and genesis files with the number + of validators specified. This calls uncd amend-genesis to create the new genesis + and records files, and then starts the uncd nodes and waits for them to be online + after computing the genesis state roots. This step takes a long time (a few hours). + ''') + new_test_parser.add_argument('--epoch-length', type=int) + new_test_parser.add_argument('--num-validators', type=int) + new_test_parser.add_argument('--num-seats', type=int) + new_test_parser.add_argument('--genesis-protocol-version', type=int) + new_test_parser.add_argument('--yes', action='store_true') + new_test_parser.set_defaults(func=new_test) + + status_parser = subparsers.add_parser( + 'status', + help='''Checks the status of test initialization on each node''') + status_parser.set_defaults(func=status_cmd) + + start_traffic_parser = subparsers.add_parser( + 'start-traffic', + help= + 'Starts all nodes and starts uncd mirror run on the traffic generator.' + ) + start_traffic_parser.set_defaults(func=start_traffic_cmd) + + start_nodes_parser = subparsers.add_parser( + 'start-nodes', + help='Starts all nodes, but does not start the traffic generator.') + start_nodes_parser.set_defaults(func=start_nodes_cmd) + + stop_parser = subparsers.add_parser('stop-nodes', + help='kill all uncd processes') + stop_parser.set_defaults(func=stop_nodes_cmd) + + stop_parser = subparsers.add_parser( + 'stop-traffic', + help='stop the traffic generator, but leave the other nodes running') + stop_parser.set_defaults(func=stop_traffic_cmd) + + reset_parser = subparsers.add_parser('reset', + help=''' + The new_test command saves the data directory after the genesis state roots are computed so that + the test can be reset from the start without having to do that again. This command resets all nodes' + data dirs to what was saved then, so that start-traffic will start the test all over again. + ''') + reset_parser.add_argument('--yes', action='store_true') + reset_parser.set_defaults(func=reset_cmd) + + # It re-uses the same binary urls because it's quite easy to do it with the + # framework-release buildkite and urls in the following format without commit + # but only with the branch name: + # https://s3-us-west-1.amazonaws.com/build.nearprotocol.com/framework/Linux//uncd" + update_binaries_parser = subparsers.add_parser( + 'update-binaries', + help= + 'Update the uncd binaries by re-downloading them. The same urls are used.' + ) + update_binaries_parser.set_defaults(func=update_binaries_cmd) + + args = parser.parse_args() + + traffic_generator, nodes = get_nodes(args) + args.func(args, traffic_generator, nodes) diff --git a/pytest/tests/mocknet/run_adversenet.py b/pytest/tests/mocknet/run_adversenet.py new file mode 100755 index 000000000..df1e0d3b8 --- /dev/null +++ b/pytest/tests/mocknet/run_adversenet.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 + +help_str = """ +This script starts or updates a network of adversenet, in which some validators may be malicious. + +The setup requires you to have a few nodes that will be validators and at least one node that will be an RPC node. +The script will recognize any node that has "validator" in its name as a validator, of which any node that has a +"bad" in its name as an adversarial node. + +Use https://github.com/near/unc-ops/tree/master/provisioning/terraform/network/adversenet to bring up a set of VM +instances for the test. +""" + +import argparse +import sys +import time +from enum import Enum +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import mocknet +import data + +from metrics import Metrics +from configured_logger import logger + + +def measure_tps_bps(nodes, tx_filename): + input_tx_events = mocknet.get_tx_events(nodes, tx_filename) + # drop first and last 5% of events to avoid edges of test + n = int(0.05 * len(input_tx_events)) + input_tx_events = input_tx_events[n:-n] + input_tps = data.compute_rate(input_tx_events) + measurement = mocknet.chain_measure_bps_and_tps(nodes[-1], + input_tx_events[0], + input_tx_events[-1]) + result = { + 'bps': measurement['bps'], + 'in_tps': input_tps, + 'out_tps': measurement['tps'] + } + logger.info(f'{result}') + return result + + +def check_tps(measurement, expected_in, expected_out=None, tolerance=0.05): + if expected_out is None: + expected_out = expected_in + almost_equal = lambda x, y: (abs(x - y) / y) <= tolerance + return (almost_equal(measurement['in_tps'], expected_in) and + almost_equal(measurement['out_tps'], expected_out)) + + +def check_memory_usage(node): + metrics = mocknet.get_metrics(node) + mem_usage = metrics.memory_usage / 1e6 + logger.info(f'Memory usage (MB) = {mem_usage}') + return mem_usage < 4500 + + +def check_slow_blocks(initial_metrics, final_metrics): + delta = Metrics.diff(final_metrics, initial_metrics) + slow_process_blocks = delta.block_processing_time[ + 'le +Inf'] - delta.block_processing_time['le 1'] + logger.info( + f'Number of blocks processing for more than 1s: {slow_process_blocks}') + return slow_process_blocks == 0 + + +def override_config(node, config): + # Add config here depending on the specific node build. + pass + """ + if "bad" in node.instance_name: + config["adversarial"] = { + "produce_duplicate_blocks": True + } + """ + + +class Role(Enum): + Rpc = 0 + GoodValidator = 1 + BadValidator = 2 + + +def get_role(node): + if "validator" not in node.instance_name: + return Role.Rpc + elif "bad" in node.instance_name: + return Role.BadValidator + else: + return Role.GoodValidator + + +if __name__ == '__main__': + logger.info('Starting adversenet.') + parser = argparse.ArgumentParser(description=help_str) + parser.add_argument( + 'mode', + choices=["new", "update"], + help= + "new: start a new network from scratch, update: update existing network" + ) + parser.add_argument('--chain-id', required=False, default="adversenet") + parser.add_argument('--pattern', + required=False, + default="adversenet-node-", + help="pattern to filter the gcp instance names") + parser.add_argument( + '--epoch-length', + type=int, + required=False, + default=60, + help="epoch length of the network. Only used when mode == new") + parser.add_argument( + '--num-seats', + type=int, + required=False, + default=100, + help="number of validator seats. Only used when mode == new") + parser.add_argument('--bad-stake', + required=False, + default=5, + type=int, + help="Total stake percentage for bad validators") + parser.add_argument('--binary-url', + required=False, + help="url to download uncd binary") + + args = parser.parse_args() + + chain_id = args.chain_id + pattern = args.pattern + epoch_length = args.epoch_length + assert epoch_length > 0 + + all_nodes = mocknet.get_nodes(pattern=pattern) + rpcs = [n.instance_name for n in all_nodes if get_role(n) == Role.Rpc] + good_validators = [ + n.instance_name for n in all_nodes if get_role(n) == Role.GoodValidator + ] + bad_validators = [ + n.instance_name for n in all_nodes if get_role(n) == Role.BadValidator + ] + TOTAL_STAKE = 1000000 + bad_validator_stake = int(TOTAL_STAKE * args.bad_stake / + (100 * len(bad_validators))) + good_validator_stake = int(TOTAL_STAKE * (100 - args.bad_stake) / + (100 * len(good_validators))) + + logger.info(f'Starting chain {chain_id} with {len(all_nodes)} nodes. \n\ + Good validators: {good_validators} each with stake {good_validator_stake} NEAR\n\ + Bad validators: {bad_validators} each with stake {bad_validator_stake} NEAR\n\ + RPC nodes: {rpcs}\n') + + answer = input("Enter y to continue: ") + if answer != "y": + exit(0) + + mocknet.stop_nodes(all_nodes) + time.sleep(10) + validator_nodes = [n for n in all_nodes if get_role(n) != Role.Rpc] + rpc_nodes = [n for n in all_nodes if get_role(n) == Role.Rpc] + if args.binary_url: + mocknet.redownload_neard(all_nodes, args.binary_url) + if args.mode == "new": + logger.info(f'Configuring nodes from scratch') + mocknet.clear_data(all_nodes) + mocknet.create_and_upload_genesis_file_from_empty_genesis( + # Give bad validators less stake. + [(node, bad_validator_stake * mocknet.ONE_NEAR if get_role(node) + == Role.BadValidator else good_validator_stake * mocknet.ONE_NEAR) + for node in validator_nodes], + rpc_nodes, + chain_id, + epoch_length=epoch_length, + num_seats=args.num_seats) + mocknet.create_and_upload_config_file_from_default( + all_nodes, chain_id, override_config) + else: + mocknet.update_existing_config_file(all_nodes, override_config) + mocknet.start_nodes(all_nodes) + mocknet.wait_all_nodes_up(all_nodes) + + # TODO: send loadtest traffic. diff --git a/pytest/tests/mocknet/stop.py b/pytest/tests/mocknet/stop.py new file mode 100644 index 000000000..8678d1213 --- /dev/null +++ b/pytest/tests/mocknet/stop.py @@ -0,0 +1,13 @@ +# Stop all mocknet nodes, wait 1s, then start all nodes again. +# Nodes should be responsive again after this operation. + +import sys +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) +import mocknet + +nodes = mocknet.get_nodes() + +# stop nodes +mocknet.stop_nodes(nodes) diff --git a/pytest/tests/replay/README.md b/pytest/tests/replay/README.md new file mode 100644 index 000000000..381c60c0a --- /dev/null +++ b/pytest/tests/replay/README.md @@ -0,0 +1,40 @@ +# Replay + +The replay script is able to take a dumped genesis file and set up a new localnet directory for local replays. + +Once the localnet is running (launched separately), it can also send to the localnet node a dumped tranaction trace, generated by `dump_tx`. + +Prerequisites: + +* `[PREREQ1]` path of a dumped genesis file representing a state (generated by `dump_state`, docs: https://github.com/utnet-org/utility/blob/master/tools/state-viewer/README.md) + +* `[PREREQ2]` path of a dumped transaction trace (generated by `dump_tx`, docs: https://github.com/utnet-org/utility/blob/master/tools/state-viewer/README.md) + +* `[PREREQ3]` a localnet home path containing directory `node0/`, under which a valid `config.json` file exists for localnet use + + +In order to set up and launch replay, we take the following steps: + +Make sure you have the right enviroment variables: +```shell +export PYTHONPATH=./pytest/lib +``` + +First, build the binary. +```shell +cargo build -p uncd --release +``` + +Second, run `generate`: +```shell +python3 pytest/tests/replay/replay.py generate --genesis [PREREQ1] --home-dir [PREREQ3] +``` + +Third, launch the localnet node using outputs from `generate` under `--home-dir`. Hints on the screen. + +Fourth, run `send`: +```shell +python3 pytest/tests/replay/replay.py send --tx-json [PREREQ2] --home-dir [PREREQ3] +``` + +Now the localnet db contains a dense trace of the txs dumped, replayed on the dumped genesis. diff --git a/pytest/tests/replay/replay.py b/pytest/tests/replay/replay.py new file mode 100644 index 000000000..c26cd5045 --- /dev/null +++ b/pytest/tests/replay/replay.py @@ -0,0 +1,213 @@ +# TODO(#7132): Convert this test to a nightly regression test. + +from account import Account +from collections import OrderedDict +from key import Key +from messages.tx import * +from messages.crypto import AccessKey, crypto_schema, PublicKey, Signature +from messages.bridge import bridge_schema +from serializer import BinarySerializer +import mocknet_helpers + +import argparse +import base58 +import base64 +import hashlib +import json +import os + +LOCALHOST = '127.0.0.1' + + +def generate_new_key(): + return Key.implicit_account() + + +def save_genesis_with_new_key_pair(genesis_path, key_pair, output_path): + node0_dir = os.path.join(output_path, 'node0/') + if not os.path.exists(node0_dir): + os.makedirs(node0_dir) + with open(genesis_path) as fin: + genesis = json.load(fin) + + new_key = key_pair.pk.split(':')[1] if ':' in key_pair.pk else key_pair.pk + # TODO(jc-near): Use different key pairs for different accounts. + for i in range(len(genesis['validators'])): + genesis['validators'][i]['public_key'] = new_key + for record in genesis['records']: + if 'AccessKey' in record: + record['AccessKey']['public_key'] = new_key + with open(os.path.join(node0_dir, 'genesis.json'), 'w') as fout: + json.dump(genesis, fout, indent=2) + + key_pair.account_id = genesis['validators'][0]['account_id'] + key_json = dict() + key_json['account_id'] = genesis['validators'][0]['account_id'] + key_json['public_key'] = key_pair.pk + key_json['secret_key'] = key_pair.sk + with open(os.path.join(node0_dir, 'node_key.json'), 'w') as fout: + json.dump(key_json, fout, indent=2) + with open(os.path.join(node0_dir, 'validator_key.json'), 'w') as fout: + json.dump(key_json, fout, indent=2) + + +def prompt_to_launch_localnet(hint_dir): + print('New genesis ready.') + print('Please launch your localnet node now.') + print('Hint: nearup run localnet --home %s --num-nodes 1' % hint_dir) + + +def convert_snack_case_to_camel_case(s): + words = s.split('_') + return words[0] + ''.join(w.title() for w in words[1:]) + + +def convert_json_rust_instance_to_py_object(ordered_dict_instance, class_name): + ans = class_name() + for attr_key, attr_value in ordered_dict_instance.items(): + setattr(ans, convert_snack_case_to_camel_case(attr_key), attr_value) + return ans + + +def convert_transaction_type_string_to_class(name): + tx_class_by_type = { + "CreateAccount": CreateAccount, + "DeleteAccount": DeleteAccount, + "DeployContract": DeployContract, + "FunctionCall": FunctionCall, + "Transfer": Transfer, + "Stake": Stake, + "AddKey": AddKey, + "DeleteKey": DeleteKey, + } + if name in tx_class_by_type: + return tx_class_by_type[name] + raise ValueError('Unknown tx type: %s' % name) + + +def convert_json_public_key_to_py_public_key(json_public_key): + pk_str = json_public_key.split( + ':')[1] if ':' in json_public_key else json_public_key + ans = PublicKey() + ans.keyType = 0 + ans.data = base58.b58decode(pk_str.encode('ascii')) + return ans + + +''' +Get tx fields read from json ready to be serialized. +See `pytest/lib/messages/` for tx schemas desired. +''' + + +def fix_json_fields_by_tx_type(py_tx, tx_type): + if tx_type == "CreateAccount": + pass + elif tx_type == "DeleteAccount": + pass + elif tx_type == "DeployContract": + py_tx.code = base64.b64decode(py_tx.code.encode('ascii')) + elif tx_type == "FunctionCall": + py_tx.args = base64.b64decode(py_tx.args.encode('ascii')) + py_tx.deposit = int(py_tx.deposit) + elif tx_type == "Transfer": + py_tx.deposit = int(py_tx.deposit) + elif tx_type == "Stake": + py_tx.stake = int(py_tx.stake) + py_tx.publicKey = convert_json_public_key_to_py_public_key( + py_tx.publicKey) + else: + raise ValueError('Unsupported tx type: %s' % tx_type) + return py_tx + + +def convert_json_action_to_py_action(action_dict): + assert (len(action_dict) == 1) + for tx_type, value in action_dict.items(): + contents = convert_json_rust_instance_to_py_object( + value, convert_transaction_type_string_to_class(tx_type)) + contents = fix_json_fields_by_tx_type(contents, tx_type) + action = Action() + action.enum = tx_type[0].lower() + tx_type[1:] + setattr(action, action.enum, contents) + return action + + +def send_resigned_transactions(tx_path, home_dir): + with open(os.path.join(home_dir, 'node0/', 'node_key.json'), 'r') as fin: + key_pair_json = json.load(fin) + key_pair = Key(key_pair_json['account_id'], key_pair_json['public_key'], + key_pair_json['secret_key']) + base_block_hash = mocknet_helpers.get_latest_block_hash(addr=LOCALHOST) + my_account = Account(key_pair, + init_nonce=0, + base_block_hash=base_block_hash, + rpc_infos=[(LOCALHOST, "3030")]) + + schema = dict(tx_schema + crypto_schema + bridge_schema) + with open(tx_path) as fin: + txs = json.load(fin, object_pairs_hook=OrderedDict) + for original_signed_tx in txs: + tx = convert_json_rust_instance_to_py_object( + original_signed_tx['transaction'], Transaction) + if hasattr(tx, 'blockHash'): + tx.blockHash = base_block_hash + if hasattr(tx, 'actions'): + try: + tx.actions = [ + convert_json_action_to_py_action(action_dict) + for action_dict in tx.actions + ] + except ValueError: + continue + tx.publicKey = PublicKey() + tx.publicKey.keyType = 0 + tx.publicKey.data = key_pair.decoded_pk() + msg = BinarySerializer(schema).serialize(tx) + hash_ = hashlib.sha256(msg).digest() + signature = Signature() + signature.keyType = 0 + signature.data = key_pair.sign_bytes(hash_) + resigned_tx = SignedTransaction() + resigned_tx.transaction = tx + resigned_tx.signature = signature + resigned_tx.hash = hash_ + my_account.send_tx(BinarySerializer(schema).serialize(resigned_tx)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Setup replay') + parser.add_argument('operation', + type=str, + help="choose between [generate/send]") + parser.add_argument('--tx-json', + type=str, + required=False, + help="Path of tx history json") + parser.add_argument('--genesis', + type=str, + required=False, + help="Path of genesis") + parser.add_argument('--home-dir', + type=str, + required=True, + help="Path of the new home directory") + args = parser.parse_args() + + if args.operation == 'generate': + if args.genesis: + key_pair = generate_new_key() + save_genesis_with_new_key_pair(args.genesis, key_pair, + args.home_dir) + prompt_to_launch_localnet(args.home_dir) + else: + parser.error('Cannot run generate without genesis') + elif args.operation == 'send': + if args.tx_json: + send_resigned_transactions(args.tx_json, args.home_dir) + else: + parser.error('Cannot run send without tx history') + else: + parser.error( + 'Unsupported positional argument: replay [operation] where operation = %s' + % args.opeartion) diff --git a/pytest/tests/sandbox/fast_forward.py b/pytest/tests/sandbox/fast_forward.py new file mode 100644 index 000000000..a4e9cbd79 --- /dev/null +++ b/pytest/tests/sandbox/fast_forward.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# test fast fowarding by a specific block height within a sandbox node. This will +# fail if the block height is not past the forwarded height. Also we will test +# for the timestamps and epoch height being adjusted correctly after the block +# height is changed. + +import datetime +import sys +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import utils +from cluster import start_cluster + +# startup a RPC node +MIN_BLOCK_PROD_TIME = 1 # seconds +MAX_BLOCK_PROD_TIME = 2 # seconds +EPOCH_LENGTH = 100 +BLOCKS_TO_FASTFORWARD = 4 * EPOCH_LENGTH +CONFIG = utils.figure_out_sandbox_binary() +CONFIG.update({ + "consensus": { + "min_block_production_delay": { + "secs": MIN_BLOCK_PROD_TIME, + "nanos": 0, + }, + "max_block_production_delay": { + "secs": MAX_BLOCK_PROD_TIME, + "nanos": 0, + }, + } +}) + +nodes = start_cluster(1, 0, 1, CONFIG, [["epoch_length", EPOCH_LENGTH]], {}) +sync_info = nodes[0].get_status()['sync_info'] +pre_forward_block_hash = sync_info['latest_block_hash'] + +# request to fast forward +nodes[0].json_rpc('sandbox_fast_forward', + {"delta_height": BLOCKS_TO_FASTFORWARD}, + timeout=60) + +# wait a little for it to fast forward +# if this call times out, then the fast_forward failed somewhere +utils.wait_for_blocks(nodes[0], target=BLOCKS_TO_FASTFORWARD + 10, timeout=10) + +# Assert that we're within the bounds of fast forward timestamp between range of min and max: +sync_info = nodes[0].get_status()['sync_info'] +earliest = datetime.datetime.strptime(sync_info['earliest_block_time'][:-4], + '%Y-%m-%dT%H:%M:%S.%f') +latest = datetime.datetime.strptime(sync_info['latest_block_time'][:-4], + '%Y-%m-%dT%H:%M:%S.%f') + +min_forwarded_secs = datetime.timedelta( + 0, BLOCKS_TO_FASTFORWARD * MIN_BLOCK_PROD_TIME) +max_forwarded_secs = datetime.timedelta( + 0, BLOCKS_TO_FASTFORWARD * MAX_BLOCK_PROD_TIME) +min_forwarded_time = earliest + min_forwarded_secs +max_forwarded_time = earliest + max_forwarded_secs + +assert min_forwarded_time < latest < max_forwarded_time + +# Check to see that the epoch height has been updated correctly: +epoch_height = nodes[0].get_validators()['result']['epoch_height'] +assert epoch_height == BLOCKS_TO_FASTFORWARD // EPOCH_LENGTH + +# Check if queries aren't failing after fast forwarding: +resp = nodes[0].json_rpc("block", {"finality": "optimistic"}) +assert resp['result']['chunks'][0]['height_created'] > BLOCKS_TO_FASTFORWARD +resp = nodes[0].json_rpc("block", {"finality": "final"}) +assert resp['result']['chunks'][0]['height_created'] > BLOCKS_TO_FASTFORWARD + +# Not necessarily a requirement, but current implementation should be able to retrieve +# one of the blocks before fast-forwarding: +resp = nodes[0].json_rpc("block", {"block_id": pre_forward_block_hash}) +assert resp['result']['chunks'][0]['height_created'] < BLOCKS_TO_FASTFORWARD + +# do one more fast forward request just so we make sure consecutive requests +# don't crash anything on the node +nodes[0].json_rpc('sandbox_fast_forward', + {"delta_height": BLOCKS_TO_FASTFORWARD}, + timeout=60) +resp = nodes[0].json_rpc("block", {"finality": "optimistic"}) +assert resp['result']['chunks'][0]['height_created'] > 2 * BLOCKS_TO_FASTFORWARD diff --git a/pytest/tests/sandbox/fast_forward_epoch_boundary.py b/pytest/tests/sandbox/fast_forward_epoch_boundary.py new file mode 100644 index 000000000..5d0e87c40 --- /dev/null +++ b/pytest/tests/sandbox/fast_forward_epoch_boundary.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# test fast fowarding on epoch boundaries just so we can see that epoch heights +# are being updated accordingly once we get near the boundary. + +import datetime +import sys +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import utils +from cluster import start_cluster + +# startup a RPC node +MIN_BLOCK_PROD_TIME = 1 # seconds +MAX_BLOCK_PROD_TIME = 2 # seconds +EPOCH_LENGTH = 100 +CONFIG = utils.figure_out_sandbox_binary() +CONFIG.update({ + "consensus": { + "min_block_production_delay": { + "secs": MIN_BLOCK_PROD_TIME, + "nanos": 0, + }, + "max_block_production_delay": { + "secs": MAX_BLOCK_PROD_TIME, + "nanos": 0, + }, + } +}) + +nodes = start_cluster(1, 0, 1, CONFIG, [["epoch_length", EPOCH_LENGTH]], {}) + +# start at block_height = 10 +utils.wait_for_blocks(nodes[0], target=10) +# fast forward to about block_height=190 and then test for boundaries +nodes[0].json_rpc('sandbox_fast_forward', {"delta_height": 180}, timeout=60) +for i in range(20): + utils.wait_for_blocks(nodes[0], target=190 + i) + block_height = nodes[0].get_latest_block().height + epoch_height = nodes[0].get_validators()['result']['epoch_height'] + assert epoch_height == 2 if block_height > 200 else 1 + +# check that we still have correct epoch heights after consecutive fast forwards: +utils.wait_for_blocks(nodes[0], target=220) +nodes[0].json_rpc('sandbox_fast_forward', {"delta_height": 70}, timeout=60) +for i in range(20): + utils.wait_for_blocks(nodes[0], target=290 + i) + block_height = nodes[0].get_latest_block().height + epoch_height = nodes[0].get_validators()['result']['epoch_height'] + assert epoch_height == 3 if block_height > 300 else 2 diff --git a/pytest/tests/sandbox/patch_state.py b/pytest/tests/sandbox/patch_state.py new file mode 100755 index 000000000..aaa7a2501 --- /dev/null +++ b/pytest/tests/sandbox/patch_state.py @@ -0,0 +1,747 @@ +#!/usr/bin/env python3 +# Patch contract states in a sandbox node + +import sys, time +import base64 +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import utils +from cluster import start_cluster +from transaction import sign_deploy_contract_tx, sign_function_call_tx + +CONFIG = utils.figure_out_sandbox_binary() + +# start node +nodes = start_cluster(1, 0, 1, CONFIG, [["epoch_length", 10]], {}) + +# deploy contract +hash_ = nodes[0].get_latest_block().hash_bytes +tx = sign_deploy_contract_tx(nodes[0].signer_key, utils.load_test_contract(), + 10, hash_) +nodes[0].send_tx(tx) +time.sleep(3) + +# store a key value +hash_ = nodes[0].get_latest_block().hash_bytes +k = (10).to_bytes(8, byteorder="little") +v = (20).to_bytes(8, byteorder="little") +tx2 = sign_function_call_tx(nodes[0].signer_key, nodes[0].signer_key.account_id, + 'write_key_value', k + v, 1000000000000, 0, 20, + hash_) +res = nodes[0].send_tx_and_wait(tx2, 20) +assert ('SuccessValue' in res['result']['status']) +res = nodes[0].call_function("test0", "read_value", + base64.b64encode(k).decode('ascii')) +assert (res['result']['result'] == list(v)) + +# patch it +new_v = (30).to_bytes(8, byteorder="little") +res = nodes[0].json_rpc( + 'sandbox_patch_state', { + "records": [{ + 'Data': { + 'account_id': "test0", + 'data_key': base64.b64encode(k).decode('ascii'), + 'value': base64.b64encode(new_v).decode('ascii'), + } + }, { + "Account": { + "account_id": "01.near", + "account": { + "amount": "49999999958035075000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 264 + } + } + }, { + "Account": { + "account_id": "alex.near", + "account": { + "amount": "9999000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "bo.near", + "account": { + "amount": "50000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "bot.pulse.near", + "account": { + "amount": "791373397694044304600000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "bowen.near", + "account": { + "amount": "49999999506363398300200000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "contributors.near", + "account": { + "amount": "418000000000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "erik.near", + "account": { + "amount": "10000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "foundation.near", + "account": { + "amount": "581779979999999955363487500000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "illia.near", + "account": { + "amount": "9909124991408763970627200000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 321 + } + } + }, { + "Account": { + "account_id": "kendall.near", + "account": { + "amount": "49998999710140992484400000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 462 + } + } + }, { + "Account": { + "account_id": "ledger.vlad.near", + "account": { + "amount": "999999957937258742200000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 327 + } + } + }, { + "Account": { + "account_id": "mike.near", + "account": { + "amount": "30999999915088987500000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "mikemikemikemikemikemikemikemike", + "account": { + "amount": "19000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "near", + "account": { + "amount": "8700003991476791004803600000", + "locked": "0", + "code_hash": "23tqXYRdbJVuvpLB14Pe9Su9bQBwfn3njKN6EBbKTQwh", + "storage_usage": 197868 + } + } + }, { + "Account": { + "account_id": "nfvalidator1.near", + "account": { + "amount": "0", + "locked": "50000000000000000000000000000", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "nfvalidator2.near", + "account": { + "amount": "0", + "locked": "50000000000000000000000000000", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "nfvalidator3.near", + "account": { + "amount": "0", + "locked": "50000000000000000000000000000", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "nfvalidator4.near", + "account": { + "amount": "0", + "locked": "50000000000000000000000000000", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "patrick.near", + "account": { + "amount": "9998999875468925000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 263 + } + } + }, { + "Account": { + "account_id": "peter.near", + "account": { + "amount": "1000874999955363487500000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "pulse.near", + "account": { + "amount": "48001118054588063403800000", + "locked": "0", + "code_hash": "2pMwiHggCBQAv3eFEPtJozDpbHpD8KkL3o3qRv6qs6DT", + "storage_usage": 26061 + } + } + }, { + "Account": { + "account_id": "registrar", + "account": { + "amount": "10000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "treasury.near", + "account": { + "amount": "10000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Account": { + "account_id": "vlad.near", + "account": { + "amount": "8998999831159137500000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 346 + } + } + }, { + "Account": { + "account_id": "wallet.pulse.near", + "account": { + "amount": "999899913398562500000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 264 + } + } + }, { + "Account": { + "account_id": "yifang.near", + "account": { + "amount": "50000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, { + "Contract": { + "account_id": + "near", + "code": + "AGFzbQEAAAAB8gNBYAJ/fwF/YAF/AGAAAX9gBX9+fn5+AGACf34AYAR/fn5+AX5gCH9+fn5+fn5+AGADf35+AGAKf35+fn5+fn5+fgBgBn9+fn5+fgF+YAF/AX5gBH9+fn4AYAN/fn4BfmACf34BfmACf38AYAN/f38Bf2ACfn4AYAF+AX5gAX4AYAABfmADfn5+AGAAAGAIfn5+fn5+fn4BfmAJfn5+fn5+fn5+AX5gAn5+AX5gA35+fgF+YAd+fn5+fn5+AGAEfn5+fgBgCX5+fn5+fn5+fgBgBX5+fn5+AX5gA39/fwBgAX8Bf2AEf39/fgBgBX9/f35/AGAFf39/f38AYAR/f39/AGAFf39/f38Bf2AEf39/fwF/YAV/fn5+fwBgBn9/f39/fwBgAn9/AX5gBX9/fn9/AGADf39+AGAJf35+fn5+fn5+AX5gCn9+fn5+fn5+fn4BfmAEf39+fgF/YAN/fn4Bf2AHf39/f39/fwBgA39/fwF+YAJ+fwF+YAN+f38AYAh+f39/f35+fgBgBH5+fn8AYAN+f34AYAh+f35+fn9/fwBgAn5/AGAHf39/f35+fgBgBH9/fn4AYAd/f39+fn9/AGAEf3x/fwF/YAN+f38Bf2AGf39/f39/AX9gBH5+f38Bf2AEf35+fwBgBn9+fn5+fwAC5QguA2Vudg1yZWFkX3JlZ2lzdGVyABADZW52DHJlZ2lzdGVyX2xlbgARA2VudhJjdXJyZW50X2FjY291bnRfaWQAEgNlbnYRc2lnbmVyX2FjY291bnRfaWQAEgNlbnYRc2lnbmVyX2FjY291bnRfcGsAEgNlbnYWcHJlZGVjZXNzb3JfYWNjb3VudF9pZAASA2VudgVpbnB1dAASA2VudgtibG9ja19pbmRleAATA2Vudg9ibG9ja190aW1lc3RhbXAAEwNlbnYMZXBvY2hfaGVpZ2h0ABMDZW52DXN0b3JhZ2VfdXNhZ2UAEwNlbnYPYWNjb3VudF9iYWxhbmNlABIDZW52FmFjY291bnRfbG9ja2VkX2JhbGFuY2UAEgNlbnYQYXR0YWNoZWRfZGVwb3NpdAASA2VudgtwcmVwYWlkX2dhcwATA2Vudgh1c2VkX2dhcwATA2VudgtyYW5kb21fc2VlZAASA2VudgZzaGEyNTYAFANlbnYJa2VjY2FrMjU2ABQDZW52CWtlY2NhazUxMgAUA2Vudgx2YWx1ZV9yZXR1cm4AEANlbnYFcGFuaWMAFQNlbnYKcGFuaWNfdXRmOAAQA2Vudghsb2dfdXRmOAAQA2Vudglsb2dfdXRmMTYAEANlbnYOcHJvbWlzZV9jcmVhdGUAFgNlbnYMcHJvbWlzZV90aGVuABcDZW52C3Byb21pc2VfYW5kABgDZW52FHByb21pc2VfYmF0Y2hfY3JlYXRlABgDZW52EnByb21pc2VfYmF0Y2hfdGhlbgAZA2VudiNwcm9taXNlX2JhdGNoX2FjdGlvbl9jcmVhdGVfYWNjb3VudAASA2VudiRwcm9taXNlX2JhdGNoX2FjdGlvbl9kZXBsb3lfY29udHJhY3QAFANlbnYicHJvbWlzZV9iYXRjaF9hY3Rpb25fZnVuY3Rpb25fY2FsbAAaA2Vudh1wcm9taXNlX2JhdGNoX2FjdGlvbl90cmFuc2ZlcgAQA2Vudhpwcm9taXNlX2JhdGNoX2FjdGlvbl9zdGFrZQAbA2Vudi1wcm9taXNlX2JhdGNoX2FjdGlvbl9hZGRfa2V5X3dpdGhfZnVsbF9hY2Nlc3MAGwNlbnYvcHJvbWlzZV9iYXRjaF9hY3Rpb25fYWRkX2tleV93aXRoX2Z1bmN0aW9uX2NhbGwAHANlbnYfcHJvbWlzZV9iYXRjaF9hY3Rpb25fZGVsZXRlX2tleQAUA2VudiNwcm9taXNlX2JhdGNoX2FjdGlvbl9kZWxldGVfYWNjb3VudAAUA2VudhVwcm9taXNlX3Jlc3VsdHNfY291bnQAEwNlbnYOcHJvbWlzZV9yZXN1bHQAGANlbnYOcHJvbWlzZV9yZXR1cm4AEgNlbnYNc3RvcmFnZV93cml0ZQAdA2VudgxzdG9yYWdlX3JlYWQAGQNlbnYOc3RvcmFnZV9yZW1vdmUAGQNlbnYPc3RvcmFnZV9oYXNfa2V5ABgDzgbMBh4OHg4eDh4OHg4eDg4fAQEBDg4BDg4OAA8OAQAAHwEOHx4BDiAOIQEhAQEhHiEfHw4hDx8fHyIBIgEBDg4ADh8OIw4OAQAAAAAAAAAAAAAAAAAAAAAADgEOAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ8kJSMiIw4BDiImIg4eDg4ODh4eDh4OHg4OHg8ADiUPDyUAHh4fAA4OHg4eDh4OHg4eDh4ODg4ODh8fDg4OAAAOJyMKHh4oKSMqKiojDg4eHiMOIx4qKioODh8fAQEOHh4eDg4OJycnJycnHgIAHiUABw0EBAQEBAoKCgoEBAQKCgQLCwsHAQcHByssDAwFBAsGBwMDCAsLCgwECQUFDAAAHh4jIy0uFRUVFRUVAB4lACMiHg4OIx4jIwEiHgAODgofAAAAAAAlHh4AAQEBAQEBAQEBAQEBAR4lAQ4BDi8eIyUeDg4BHh8AHg4ADx8eAAEODh4OHh4VJx4OAQEODw8ODg8OAB8fHh4eIwAeHx8ADg4eDh4eHwAeAB8fAR8fAQEfIwAfJA8jDgEOAA4ODgofDgABGAEfDgAoHg4AHg8eDw4ADgABHzAjCg4OACgeHg8oHgEfAR8wIx4PDgAOACgeDgAwIx4PHg8oHg4AAQ4BAQEAAAAOAR4ODgEjAA4BDg4BAQEBAQEBCgEBAQEBAQEBAQEBASUfDh8OHiIiIh4eHw4ODh4eDg4BJyMBDgEODhUEBAEBAQEBKAoxEjIzFDQ1Njc3EwQSDiUeAAEADh4OODkeOh4eAQ4ODh4BAgcHAA4eAAABASMiDicAAB4OIwAAHh4eFR4fHh4eJx8eDhUeCh4BAQ4OHgAAFR8OAAAADx4OIwAAAAEBASUlHgAeAA8OFR4eDgEOHiMeHh4VJwEOAQoKCgAAAAAAHgEBAQAfAAAPABUAAA4ACh8ODgAAHgAAIxUODg4BIw4ODg4OHwAAAAEOAQ4OFRUeDgABHh4ADg4PIwAOAA8jIyIjIgAjAA8AAAoAAB4fJB4AIgAfHwAAAB8PIx8PHx4PHzsAOwAADwAAADwAPSUADwAfHyMjDgAPAQ8AACIAHh4AAAAjHgAAAAAAAAAAPgAAAAAAAAAfHw4ADw8PPz8DA0AEBwFwAcsBywEFAwEAEQYZA38BQYCAwAALfwBBsPrCAAt/AEGw+sIACwecAQkGbWVtb3J5AgAEc2VuZADHAgVjbGFpbQDIAhhjcmVhdGVfYWNjb3VudF9hbmRfY2xhaW0AyQIOY3JlYXRlX2FjY291bnQAygISb25fYWNjb3VudF9jcmVhdGVkAMsCHm9uX2FjY291bnRfY3JlYXRlZF9hbmRfY2xhaW1lZADMAgpfX2RhdGFfZW5kAwELX19oZWFwX2Jhc2UDAgmTAwEAQQELygGQBYMBggGcBuQCgQGoBcQGswWNAe8FiwHoBp8BpAZYpAORAd4GjgF+mQF2mwF0ngF6iQF4jAF/nAF8mgGRApICkwKUApUClgKXApgCmQKaApsCnAKdAp4CnwKgAqECogKjAqQCpQKmAqcCqAKpAqoCqwKsAq0CrgKvArACsQKyArMCtAK1ArYCtwK4ArkCugK7ArwCvQK+AvsB+gGPAa4FtwOtBeICjwPlAvYC3wLgAuEC6wXeAuwF7QXuAowDiAOLA+wC9QLxBrwDwgPDA8QDlAT7BKIEzgPPA9ADzAPNA4oFiwWtBKcEowayBOUFswSxBIoG/ASsBJ0EnwSgBNMGhAWCBYUFgwWPBaMFpQWkBaIFoAXPBtoG5QbfBtQGpwW0BbcFvwW8Bb4FuAXkBuQF1wXzBdoF4QXfBeAF0gXcBeoF6AXpBdYF2AXiBdsF+AX5BdMF+gX7BdEF1QXUBYEG/gWABv8FqQagBpMGwgarBq8GsAaLBqIGsga9Br4G7Aa/BsAGwQbpBuoG6wYK9/QIzAY2AQF/I4CAgIAAQRBrIgMkgICAgAAgAyABIAIQp4OAgAAgACADEK+AgIAAIANBEGokgICAgAAL8wECAX8CfiOAgICAAEHAAGsiAiSAgICAACACQShqQQhqIAFBCGooAgA2AgAgAiABKQIANwMoIAJBCGogAkEoahCdg4CAACACQShqIAJBCGoQxICAgAACQAJAIAIoAihBAUcNACAAIAIoAiw2AgQgAEEBNgIADAELIAJBKGpBEGopAwAhAyACKQMwIQQgAiACQQhqELuAgIAAIgE2AigCQCABRQ0AIABBATYCACAAIAE2AgQMAQsgAkEoahC8gICAACAAQRBqIAM3AwAgAEEIaiAENwMAIABBADYCAAsgAkEUahC+gICAACACQcAAaiSAgICAAAs2AQF/I4CAgIAAQRBrIgMkgICAgAAgAyABIAIQp4OAgAAgACADELGAgIAAIANBEGokgICAgAALhAIBAX8jgICAgABBwABrIgIkgICAgAAgAkEwakEIaiABQQhqKAIANgIAIAIgASkCADcDMCACIAJBMGoQnYOAgAAgAkEwaiACEL+AgIAAAkACQAJAIAIoAjBBAUYNACACQSBqQQhqIAJBMGpBDGooAgA2AgAgAiACKQI0NwMgIAIgAhC7gICAACIBNgIwIAENASACQTBqELyAgIAAIAAgAikDIDcCBCAAQQA2AgAgAEEMaiACQShqKAIANgIADAILIAAgAigCNDYCBCAAQQE2AgAMAQsgAEEBNgIAIAAgATYCBCACQSBqEL6AgIAACyACQQxqEL6AgIAAIAJBwABqJICAgIAACzYBAX8jgICAgABBEGsiAySAgICAACADIAEgAhCng4CAACAAIAMQs4CAgAAgA0EQaiSAgICAAAuEAgEBfyOAgICAAEHAAGsiAiSAgICAACACQTBqQQhqIAFBCGooAgA2AgAgAiABKQIANwMwIAIgAkEwahCdg4CAACACQTBqIAIQwoCAgAACQAJAAkAgAigCMEEBRg0AIAJBIGpBCGogAkEwakEMaigCADYCACACIAIpAjQ3AyAgAiACELuAgIAAIgE2AjAgAQ0BIAJBMGoQvICAgAAgACACKQMgNwIEIABBADYCACAAQQxqIAJBKGooAgA2AgAMAgsgACACKAI0NgIEIABBATYCAAwBCyAAQQE2AgAgACABNgIEIAJBIGoQvoCAgAALIAJBDGoQvoCAgAAgAkHAAGokgICAgAALNgEBfyOAgICAAEEQayIDJICAgIAAIAMgASACEKeDgIAAIAAgAxC1gICAACADQRBqJICAgIAAC7UCAQJ/I4CAgIAAQeAAayICJICAgIAAIAJBwABqQQhqIAFBCGooAgA2AgAgAiABKQIANwNAIAJBCGogAkHAAGoQnYOAgAAgAkHAAGogAkEIahC6gICAAAJAAkACQCACKAJAQQFGDQAgAkE4aiIDIAJBwABqQRRqKQIANwMAIAJBKGpBCGogAkHAAGpBDGopAgA3AwAgAiACKQJENwMoIAIgAkEIahC7gICAACIBNgJAIAENASACQcAAahC8gICAACAAIAIpAyg3AgQgAEEANgIAIABBFGogAykDADcCACAAQQxqIAJBMGopAwA3AgAMAgsgACACKAJENgIEIABBATYCAAwBCyAAQQE2AgAgACABNgIEIAJBKGoQvYCAgAALIAJBFGoQvoCAgAAgAkHgAGokgICAgAALNgEBfyOAgICAAEEQayIDJICAgIAAIAMgASACEKeDgIAAIAAgAxC3gICAACADQRBqJICAgIAAC+ECAQN/I4CAgIAAQfAAayICJICAgIAAIAJByABqQQhqIAFBCGooAgA2AgAgAiABKQIANwNIIAJBCGogAkHIAGoQnYOAgAAgAkHIAGogAkEIahDDgICAAAJAAkACQCACKAJIQQFGDQAgAkEoakEYaiIDIAJByABqQSBqKQMANwMAIAJBKGpBEGoiBCACQcgAakEYaikDADcDACACQShqQQhqIAJByABqQRBqKQMANwMAIAIgAikDUDcDKCACIAJBCGoQu4CAgAAiATYCSCABDQEgAkHIAGoQvICAgAAgAEEIaiACKQMoNwMAIABBADYCACAAQSBqIAMpAwA3AwAgAEEYaiAEKQMANwMAIABBEGogAkEoakEIaikDADcDAAwCCyAAIAIoAkw2AgQgAEEBNgIADAELIABBATYCACAAIAE2AgQgBBC+gICAAAsgAkEUahC+gICAACACQfAAaiSAgICAAAs2AQF/I4CAgIAAQRBrIgMkgICAgAAgAyABIAIQp4OAgAAgACADELmAgIAAIANBEGokgICAgAALtQIBAn8jgICAgABB4ABrIgIkgICAgAAgAkHAAGpBCGogAUEIaigCADYCACACIAEpAgA3A0AgAkEIaiACQcAAahCdg4CAACACQcAAaiACQQhqEMCAgIAAAkACQAJAIAIoAkBBAUYNACACQThqIgMgAkHAAGpBFGopAgA3AwAgAkEoakEIaiACQcAAakEMaikCADcDACACIAIpAkQ3AyggAiACQQhqELuAgIAAIgE2AkAgAQ0BIAJBwABqELyAgIAAIAAgAikDKDcCBCAAQQA2AgAgAEEUaiADKQMANwIAIABBDGogAkEwaikDADcCAAwCCyAAIAIoAkQ2AgQgAEEBNgIADAELIABBATYCACAAIAE2AgQgAkEoahDBgICAAAsgAkEUahC+gICAACACQeAAaiSAgICAAAsUACAAIAEgACAAIAAgABCKgoCAAAt6AQJ/I4CAgIAAQSBrIgEkgICAgAAgAUEIaiAAEM2AgIAAAkACQCABLQAIQQFGDQBBACECAkAgAS0ACUEBRw0AIAFBEzYCECAAIAFBEGoQxYCAgAAhAgsgAUEIahDIgICAAAwBCyABKAIMIQILIAFBIGokgICAgAAgAgsVAAJAIAAoAgBFDQAgABDYgICAAAsLFQAgABC+gICAACAAQQxqEL6AgIAACxIAIAAQm4OAgAAgABCcg4CAAAsUACAAIAEgACAAIAAgABCIgoCAAAsUACAAIAEgACAAIAAgABCJgoCAAAsVACAAEL6AgIAAIABBDGoQvoCAgAALFAAgACABIAAgACAAIAAQhoKAgAALFAAgACABIAAgACAAIAAQhYKAgAALFAAgACABIAAgACAAIAAQh4KAgAALawECfyOAgICAAEEgayICJICAgIAAIAJBCGogABCjg4CAACACKAIMIQAgAigCCCEDIAJBEGpBCGogAUEIaigCADYCACACIAEpAgA3AxAgAkEQaiADIAAQooOAgAAhASACQSBqJICAgIAAIAEL4QEBAX8jgICAgABBMGsiAySAgICAACADIAEgAhCCg4CAACADKAIEIQIgAygCACEBAkACQANAAkAgAiABRw0AQQAhAQwDCyADQQhqIAAQx4CAgAACQCADLQAIQQFGDQACQCADLQAJQQFGDQBBBSECIANBEGohAQwDCwJAIAMtAAogAS0AAEYNAEEJIQIgA0EgaiEBDAMLIAFBAWohASADQQhqEMiAgIAADAELCyADKAIMIQEMAQsgASACNgIAIAAgARDJgICAACEBIANBCGoQyICAgAALIANBMGokgICAgAAgAQtNAQN/QQAhAgJAIAEoAggiAyABKAIETw0AIAEoAgAgA2otAAAhBEEBIQIgASADQQFqNgIICyAAIAI6AAEgAEEAOgAAIABBAmogBDoAAAsYAAJAIAAtAABFDQAgAEEEahDYgICAAAsLawECfyOAgICAAEEgayICJICAgIAAIAJBCGogABChg4CAACACKAIMIQAgAigCCCEDIAJBEGpBCGogAUEIaigCADYCACACIAEpAgA3AxAgAkEQaiADIAAQooOAgAAhASACQSBqJICAgIAAIAELZAEBfyOAgICAAEEQayICJICAgIAAAkACQCAAKAIMRQ0AIAAhAQwBCyACQQhqIABBCGooAgA2AgAgAiAAKQIANwMAIAEgAhDJgICAACEBIAAQ8oCAgAALIAJBEGokgICAgAAgAQulCgEGfyOAgICAAEHAAGsiASSAgICAACAAQQxqIgIQzICAgABBACEDAkACQAJAAkADQCABQSBqIAAQzYCAgAACQAJAIAEtACBBAUYNAAJAAkACQAJAAkACQAJAAkAgAS0AIUEBRw0AIAEtACIhBCABQSBqEMiAgIAAIARBIkYNBCAEQS1GDQMCQAJAIARB2wBGDQAgBEHmAEYNBCAEQe4ARg0BIARB9ABGDQMgBEH7AEYNACAEQVBqQf8BcUEKTw0HIAEgABDOgICAACIENgIwIAFBMGohBSAERQ0IDBALQQAhBSACIANB//8DcUEARyAGEM+AgIAAIAAQ0ICAgAAgBCEGDAgLIAAQ0ICAgAAgASAAQYeAwIAAQQMQxoCAgAAiBDYCMCABQTBqIQUgBA0ODAYLIAFBBTYCMCAAIAFBMGoQxYCAgAAhBCABQSBqEMiAgIAADA0LIAAQ0ICAgAAgASAAQYSAwIAAQQMQxoCAgAAiBDYCMCABQTBqIQUgBEUNBAwMCyAAENCAgIAAIAEgAEGAgMCAAEEEEMaAgIAAIgQ2AjAgAUEwaiEFIARFDQMMCwsgABDQgICAACABIAAQzoCAgAAiBDYCMCABQTBqIQUgBEUNAgwKCyAAENCAgIAAIAEgABCtg4CAACIENgIwIAFBMGohBSAERQ0BDAkLIAFBCjYCMCAAIAFBMGoQxYCAgAAhBAwICyAFELyAgIAAQQEhBSADQf//A3ENACABQRBqIAIQ0YCAgAAgAS0AEEEBcUUNBiABLQARIQYLAkADQCABQSBqIAAQzYCAgAAgAS0AIEEBRg0CAkACQAJAAkACQAJAAkACQAJAAkACQCABLQAhQQFGDQAgBkH/AXEiBkHbAEcNAUECIQQgAUEwaiEGDAgLIAEtACIiBEHdAEYNASAEQf0ARg0DIARBLEcNBCAFQQFxRQ0FIAAQ0ICAgAAMBQsgBkH7AEcNAUEDIQQgAUEwaiEGDAYLIAZB/wFxQdsARw0CDAYLQYqAwIAAQShB0IDAgAAQoYWAgAAACyAGQf8BcUH7AEYNBAsgBUEBcUUNACAGQf8BcSIGQdsARw0BQQchBCABQTBqIQYMAgsgAUEgahDIgICAAEEBIQMgBkH/AXFB+wBHDQggAUEYaiAAEM2AgIAAIAEtABhBAUYNBSABLQAZQQFGDQNBAyEEIAFBMGohBgwKCwJAIAZB+wBHDQBBCCEEIAFBMGohBgwBC0GKgMCAAEEoQdCAwIAAEKGFgIAAAAsgBiAENgIAIAAgBhDFgICAACEEIAFBIGoQyICAgAAMCgsgAUEgahDIgICAACAAENCAgIAAIAFBCGogAhDRgICAACABLQAIQQFxRQ0IIAEtAAkhBkEBIQUMAQsLAkAgAS0AGkEiRg0AQRAhBCABQSBqIQYMBgsgABDQgICAACABQRhqEMiAgIAAIAEgABCtg4CAACIENgIwIAQNByABQTBqELyAgIAAIAFBGGogABDNgICAACABLQAYQQFGDQACQCABLQAZQQFGDQBBAyEEIAFBMGohBgwFCyABLQAaQTpGDQJBBiEEIAFBIGohBgwECyABKAIcIQQMBgsgASgCJCEEDAULIAAQ0ICAgAAgAUEYahDIgICAAAwACwsgBiAENgIAIAAgBhDFgICAACEEIAFBGGoQyICAgAAMAgsgBiAENgIAIAAgBhDFgICAACEEIAFBGGoQyICAgAAMAQtBACEECyABQcAAaiSAgICAACAECwwAIABBABCUg4CAAAu4AQEDfyOAgICAAEEQayICJICAgIAAAkADQAJAIAEoAggiAyABKAIESQ0AQQAhASACQQA7AQgMAgsgAkGAAjsBCCACIAEoAgAgA2otAAAiAzoACgJAIANBd2oiBEEXSw0AQQEgBHRBk4CABHFFDQAgARDQgICAACACQQhqEMiAgIAADAELC0EBIQELIAAgAToAASAAQQA6AAAgAEECaiADOgAAIAJBCGoQyICAgAAgAkEQaiSAgICAAAvVAwEDfyOAgICAAEEgayIBJICAgIAAIAEgABDegICAAAJAAkACQAJAAkACQCABLQAAIgJBAUYNAAJAAkACQCABLQABIgNBMEYNACADQU9qQf8BcUEISw0EDAELIAFBCGogABDTgICAACABLQAIQQFGDQQgAUEIaiEDIAEtAAlBUGpB/wFxQQpPDQEgAUEMNgIQIAAgAUEQahDFgICAACEDIAFBCGoQ14CAgAAMBgsDQCABQRBqIAAQ04CAgAAgAS0AEEEBRg0FAkAgAS0AEUFQakH/AXFBCUsNACAAENCAgIAAIAFBEGoQ14CAgAAMAQsLIAFBEGohAwsgAxDXgICAACABENeAgIAAIAFBEGogABDTgICAAAJAAkACQCABLQAQQQFGDQAgAS0AESICQS5GDQECQCACQcUARg0AQQAhAyACQeUARw0DCyAAEN2AgIAAIQMMAgsgASgCFCEDDAcLIAAQ3ICAgAAhAwsgAUEQahDXgICAAAwFCyABKAIEIQMMBAsgAUEMNgIQIAAgAUEQahDJgICAACEDDAILIAEoAgwhAwwBCyABKAIUIQMgAkUNACABQQRyENiAgIAADAELIAEQ14CAgAALIAFBIGokgICAgAAgAwuEAQEDfyOAgICAAEEQayIDJICAgIAAIAAgARCNhYCAACAAEJuFgIAAIQQgACgCCCEFIAMgAjoACSADIAE6AAgCQANAIAMgA0EIahCwgYCAACADLQAAQQFxRQ0BIAQgBWogAy0AAToAACAFQQFqIQUMAAsLIAAgBTYCCCADQRBqJICAgIAACzQBAn8CQCAAKAIIIgFBAWoiAiABTw0AQbCCwIAAQRxB6InAgAAQjYaAgAAACyAAIAI2AggLbgECfyOAgICAAEEQayICJICAgIAAAkACQCABKAIIIgMNAEEAIQEMAQsgASADQX9qNgIIIAJBCGogARDPhYCAACACKAIIIAEoAghqLQAAIQNBASEBCyAAIAM6AAEgACABOgAAIAJBEGokgICAgAALjQMCAn8CfiOAgICAAEEgayIEJICAgIAAIARBCGogARDTgICAAAJAAkACQCAELQAIQQFGDQACQAJAAkACQCAELQAJIgVBLkYNACAFQcUARg0BIAVB5QBGDQFCASEGIAJFDQIgAyEHDAMLIARBEGogASACIANBABDUgICAACAEQRBqIQEgBCgCEEEBRg0EIAQpAxghByAEQRBqENWAgIAAQgAhBgwCCyAEQRBqIAEgAiADQQAQ1oCAgAAgBEEQaiEBIAQoAhBBAUYNAyAEKQMYIQcgBEEQahDVgICAAEIAIQYMAQtCACEGAkBCgICAgICAgICAf0IAIAN9IANCgICAgICAgICAf1EbIgdCAVkNAEICIQYMAQsgA7q9QoCAgICAgICAgH+FIQcLIABBADYCACAAQRBqIAc3AwAgAEEIaiAGNwMAIARBCGoQ14CAgAAMAgsgACAEKAIMNgIEIABBATYCAAwBCyAAQQE2AgAgACABKAIENgIEIARBCGoQ14CAgAALIARBIGokgICAgAALewECfyOAgICAAEEQayICJICAgIAAAkACQCABKAIIIgMgASgCBE8NACACQQE6AAkgAiABKAIAIANqLQAAIgE6AAoMAQtBACEBIAJBADoACQsgACABOgABIABBADoAACACQQA6AAggAkEIahDIgICAACACQRBqJICAgIAAC7cFAgV/AX4jgICAgABBMGsiBSSAgICAACABENCAgIAAQQAhBgJAAkACQAJAAkACQAJAAkACQAJAA0AgBUEQaiABENOAgIAAIAUtABAiB0EBRg0CIAUtABFBUGoiCEH/AXEiCUEKTw0BIAEQ0ICAgAACQAJAIANCmbPmzJmz5swZVA0AAkAgA0KZs+bMmbPmzBlSDQAgCUEGSQ0BCwNAIAVBIGogARDTgICAACAFLQAgQQFGDQYgBS0AIUFQakH/AXFBCUsNAiABENCAgIAAIAVBIGoQ14CAgAAMAAsLIANCCn4iCiAIrUL/AYN8IgMgClQNByAEQQBIIARBf0ogBEF/aiIEQX9KR3ENCCAFQRBqENeAgIAAQQEhBiAEIQQMAQsLIAVBIGoQ14CAgAAgBUEQahDXgICAAAwICyAFQRBqENeAgIAAIAZBAXENByABKAIIIgQgASgCBEkNAiAFQQA6AAlBBSEIIAVBIGohBAwDCyAAIAUoAhQ2AgQgAEEBNgIADAcLIAAgBSgCJDYCBCAAQQE2AgAgBw0EIAVBEGoQ14CAgAAMBgsgBUEBOgAJIAUgASgCACAEai0AADoACkEMIQggBUEQaiEECyAFQQA6AAggBCAINgIAIAEgBBDFgICAACEEIABBATYCACAAIAQ2AgQgBUEIahDIgICAAAwEC0GwgsCAAEEcQeSBwIAAEI2GgIAAAAtBwIHAgABBIUHMgsCAABCNhoCAAAALIAVBEGpBBHIQ2ICAgAAMAQsgBUEgaiABENOAgIAAAkAgBS0AIEEBRw0AIAAgBSgCJDYCBCAAQQE2AgAMAQsCQAJAIAUtACFBIHJB5QBGDQAgACABIAIgAyAEENmAgIAADAELIAAgASACIAMgBBDWgICAAAsgBUEgahDXgICAAAsgBUEwaiSAgICAAAsYAAJAIAAoAgBFDQAgAEEEahDYgICAAAsLwAUCBn8BfiOAgICAAEEgayIFJICAgIAAIAEQ0ICAgAAgBUEQaiABENOAgIAAQQEhBgJAAkAgBS0AEEEBRg0AAkAgBS0AEUFVaiIHQQJLDQACQAJAIAcOAwECAAELQQAhBgsgARDQgICAAAsgBUEQahDXgICAACAFQQhqIAEQx4CAgAACQCAFLQAIQQFGDQACQCAFLQAJQQFHDQAgBS0ACiEHIAVBCGoQyICAgAACQCAHQVBqQf8BcSIHQQpJDQAgBUEMNgIQIAEgBUEQahDJgICAACEBIABBATYCACAAIAE2AgQMBAsDQCAFQRBqIAEQ04CAgAACQAJAAkAgBS0AEEEBRg0AIAUtABFBUGpB/wFxIghBCUsNASABENCAgIAAIAdBy5mz5gBMDQICQCAHQcyZs+YARw0AIAhBB00NAwsgACABIAIgAyAGEN+AgIAAIAVBEGoQ14CAgAAMBwsgACAFKAIUNgIEIABBATYCAAwGCyAFQRBqENeAgIAAIAAgASACIANB/////wdBgICAgHggBCAHaiIJQQBIGyAJIARBf0oiCCAHQX9KIgpGIAggCUF/SkdxG0H/////B0GAgICAeCAEIAdrIgdBAEgbIAcgCCAKRyAIIAdBf0pHcRsgBhsQ2YCAgAAMBQsCQCAHrEIKfiILQiCIpyALpyIHQR91Rg0AQYCCwIAAQSFB7ILAgAAQjYaAgAAACwJAIAdBf0oiCSAIQX9KRiAJIAcgCGoiB0F/SkdxDQAgBUEQahDXgICAAAwBCwtBsILAgABBHEHsgsCAABCNhoCAAAALIAVBBTYCECABIAVBEGoQyYCAgAAhASAAQQE2AgAgACABNgIEIAVBCGoQyICAgAAMAgsgACAFKAIMNgIEIABBATYCAAwBCyAAIAUoAhQ2AgQgAEEBNgIACyAFQSBqJICAgIAACxgAAkAgAC0AAEUNACAAQQRqENiAgIAACwtZAQJ/AkAgACgCACIBKAIAIgJBAUsNAAJAAkAgAg4CAAEACyABQQhqKAIAIgJFDQEgASgCBCACQQEQzoKAgAAMAQsgAUEEahCNgYCAAAsgACgCABDygICAAAupAgQBfwF8AX8BfCOAgICAAEEQayIFJICAgIAAIAO6IQYCQAJAA0AgBCEHAkACQAJAAkACQCAEQX9KDQAgBEGAgICAeEYNAUEAIARrIQcLIAdBtQJJDQELIAZEAAAAAAAAAABhDQQgBEEASA0CIAVBDTYCACAAIAEgBRDJgICAADYCBAwBCyAHQQN0QeCjwIAAaisDACEIAkAgBEF/Sg0AIAYgCKMhBgwECyAGIAiiIga9Qv///////////wCDv0QAAAAAAADwf2INAyAFQQ02AgAgACABIAUQyYCAgAA2AgQLQQEhBAwDCyAGRKDI64XzzOF/oyEGIARBtAJqIQQMAAsLIABBCGogBiAGmiACGzkDAEEAIQQLIAAgBDYCACAFQRBqJICAgIAAC7IFBAJ/AX4BfwF+I4CAgIAAQSBrIgMkgICAgAAgA0EIaiABEMeAgIAAAkACQAJAAkACQAJAAkACQAJAAkAgAy0ACEEBRg0AIAMtAAlBAUcNASADLQAKIQQgA0EIahDIgICAAAJAAkAgBEH/AXFBMEYNACAEQU9qQf8BcUEJSQ0BIANBDDYCECABIANBEGoQyYCAgAAhASAAQQE2AgAgACABNgIEDAsLIANBCGogARDTgICAACADLQAIQQFGDQMCQAJAIAMtAAlBUGpB/wFxQQlLDQAgA0EMNgIQIAEgA0EQahDFgICAACEBIABBATYCACAAIAE2AgQMAQsgACABIAJCABDSgICAAAsgA0EIahDXgICAAAwKCyAEQVBqrUL/AYMhBQNAIANBCGogARDTgICAACADLQAIQQFGDQQgAy0ACUFQaiIEQf8BcSIGQQlLDQUgARDQgICAAAJAIAVCmLPmzJmz5swZWA0AAkAgBUKZs+bMmbPmzBlSDQAgBkEGSQ0BC0EBIQQgA0EQaiABIAIgBUEBENuAgIAAIAMoAhBBAUcNByAAIAMoAhQ2AgQMCAsgBUIKfiIHIAStQv8Bg3wiBSAHVA0IIANBCGoQ14CAgAAMAAsLIAAgAygCDDYCBCAAQQE2AgAMCAsgA0EFNgIQIAEgA0EQahDJgICAACEBIABBATYCACAAIAE2AgQgA0EIahDIgICAAAwHCyAAIAMoAgw2AgQgAEEBNgIADAYLIAAgAygCDDYCBCAAQQE2AgAMBQsgACABIAIgBRDSgICAAAwDCyAAQRBqIAMpAxg3AwAgAEEIakIANwMAIANBEGoQ1YCAgABBACEECyAAIAQ2AgAMAQtBsILAgABBHEHcgsCAABCNhoCAAAALIANBCGoQ14CAgAALIANBIGokgICAgAALjQIBAn8jgICAgABBEGsiBSSAgICAAAJAAkACQAJAAkADQCAFQQhqIAEQ04CAgAAgBS0ACEEBRg0BAkAgBS0ACSIGQVBqQf8BcUEJSw0AIAEQ0ICAgAAgBEF/SiIGIAYgBEEBaiIEQX9KR3ENBiAFQQhqENeAgIAADAELCyAGQS5GDQECQCAGQcUARg0AIAZB5QBGDQAgACABIAIgAyAEENmAgIAADAMLIAAgASACIAMgBBDWgICAAAwCCyAAIAUoAgw2AgQgAEEBNgIADAILIAAgASACIAMgBBDUgICAAAsgBUEIahDXgICAAAsgBUEQaiSAgICAAA8LQbCCwIAAQRxB/ILAgAAQjYaAgAAAC+UBAQJ/I4CAgIAAQRBrIgEkgICAgAAgABDQgICAAEEBIQICQAJAA0AgASAAENOAgIAAIAEtAABBAUYNAQJAIAEtAAFBUGpB/wFxQQpPDQAgABDQgICAACABENeAgIAAQQAhAgwBCwsgARDXgICAAAJAIAJBAXENACABIAAQ04CAgAAgAS0AAEEBRg0BAkACQCABLQABQSByQeUARg0AQQAhAAwBCyAAEN2AgIAAIQALIAEQ14CAgAAMAgsgAUEMNgIAIAAgARDFgICAACEADAELIAEoAgQhAAsgAUEQaiSAgICAACAAC7cCAQJ/I4CAgIAAQSBrIgEkgICAgAAgABDQgICAACABQRBqIAAQ04CAgAACQAJAIAEtABBBAUYNAAJAIAEtABFBVWoiAkECSw0AAkAgAg4DAAEAAAsgABDQgICAAAsgAUEQahDXgICAACABQQhqIAAQ3oCAgAACQAJAIAEtAAhBAUYNACABLQAJQVBqQf8BcUEJSw0BIAFBCGoQ14CAgAADQCABQRBqIAAQ04CAgAAgAS0AEEEBRg0DAkAgAS0AEUFQakH/AXFBCk8NACAAENCAgIAAIAFBEGoQ14CAgAAMAQsLIAFBEGoQ14CAgABBACEADAMLIAEoAgwhAAwCCyABQQw2AhAgACABQRBqEMmAgIAAIQAgAUEIahDXgICAAAwBCyABKAIUIQALIAFBIGokgICAgAAgAAt4AQF/I4CAgIAAQRBrIgIkgICAgAAgAkEIaiABEMeAgIAAQQEhAQJAAkAgAi0ACEEBRg0AQQAhASAAIAItAApBACACLQAJGzoAASACQQhqEMiAgIAADAELIABBBGogAigCDDYCAAsgACABOgAAIAJBEGokgICAgAAL0gEBAX8jgICAgABBEGsiBSSAgICAAAJAAkAgA1ANACAERQ0AIAVBDTYCACABIAUQyYCAgAAhASAAQQE2AgAgACABNgIEDAELAkADQCAFIAEQ04CAgAAgBS0AAEEBRg0BAkAgBS0AAUFQakH/AXFBCUsNACABENCAgIAAIAUQ14CAgAAMAQsLIAUQ14CAgAAgAEEANgIAIABBCGpEAAAAAAAAAABEAAAAAAAAAIAgAhs5AwAMAQsgACAFKAIENgIEIABBATYCAAsgBUEQaiSAgICAAAvxBQEDfyOAgICAAEEwayIDJICAgIAAIANBCGogABDTgICAAEEAIAMtAAkgAy0ACCIEQQFGGyEFAkAgBEUNACADQQhqENeAgIAACwJAAkACQAJAAkACQAJAAkAgBUH/AXEiBEEiRg0AAkACQCAEQS1GDQACQCAEQeYARg0AAkACQCAEQe4ARg0AIARB9ABGDQEgBEHbAEYNBiAEQfsARg0HIAVBUGpB/wFxQQpPDQogA0EIaiAAQQEQ2oCAgAAgAygCCEEBRg0EIANBKGogA0EYaikDADcDACADIAMpAxA3AyAgA0EgaiABIAIQnoOAgAAhBAwLCyAAENCAgIAAIAMgAEGHgMCAAEEDEMaAgIAAIgQ2AgggBA0LIANBCGoQvICAgAAgA0EHOgAIIANBCGogASACEJ+DgIAAIQQMCgsgABDQgICAACADIABBhIDAgABBAxDGgICAACIENgIIIAQNCiADQQhqELyAgIAAIANBgAI7AQggA0EIaiABIAIQn4OAgAAhBAwJCyAAENCAgIAAIAMgAEGAgMCAAEEEEMaAgIAAIgQ2AgggBA0JIANBCGoQvICAgAAgA0EAOwEIIANBCGogASACEJ+DgIAAIQQMCAsgABDQgICAACADQQhqIABBABDagICAACADKAIIQQFHDQQLIAMoAgwhBAwHCyAAENCAgIAAIABBDGoiBBDMgICAACADQSBqIAAgBBCpg4CAACADKAIgQQFHDQMgAygCJCEEDAYLIANBCjoACCADQQhqIAEgAhCfg4CAACEEDAQLIANBCzoACCADQQhqIAEgAhCfg4CAACEEDAMLIANBKGogA0EYaikDADcDACADIAMpAxA3AyAgA0EgaiABIAIQnoOAgAAhBAwCCyADIANBKGopAwA3AgwgA0EFOgAIIANBCGogASACEJ+DgIAAIQQMAQsgA0EKNgIIIAAgA0EIahDFgICAACEECyAEIAAQyoCAgAAhBAsgA0EwaiSAgICAACAEC6wBAQF/I4CAgIAAQSBrIgEkgICAgAAgAUEIaiAAEM2AgIAAAkACQAJAAkAgAS0ACEEBRg0AIAEtAAlBAUYNASABQQM2AhAgACABQRBqEMWAgIAAIQAMAgsgASgCDCEADAILAkAgAS0ACkE6Rw0AIAAQ0ICAgABBACEADAELIAFBBjYCECAAIAFBEGoQxYCAgAAhAAsgAUEIahDIgICAAAsgAUEgaiSAgICAACAAC9EBAQJ/I4CAgIAAQSBrIgEkgICAgAAgAUEIaiAAEM2AgIAAAkACQAJAAkAgAS0ACEEBRg0AIAEtAAlBAUYNASABQQM2AhAgACABQRBqEMWAgIAAIQAMAgsgASgCDCEADAILAkACQCABLQAKIgJB/QBGDQAgAkEsRw0BIAFBEjYCECAAIAFBEGoQxYCAgAAhAAwCCyAAENCAgIAAQQAhAAwBCyABQRM2AhAgACABQRBqEMWAgIAAIQALIAFBCGoQyICAgAALIAFBIGokgICAgAAgAAugAgEDfyOAgICAAEEwayIBJICAgIAAIAEgABDNgICAAAJAAkACQAJAIAEtAABBAUYNACABLQABQQFGDQEgAUECNgIgIAAgAUEgahDFgICAACEADAILIAEoAgQhAAwCCwJAIAEtAAIiAkHdAEYNAAJAIAJBLEcNACAAENCAgIAAIAFBCGogABDNgICAAAJAAkAgAS0ACA0AIAEtAAlFDQAgAS0ACkHdAEcNAEESIQMgAUEQaiECDAELQRMhAyABQSBqIQILIAIgAzYCACAAIAIQxYCAgAAhACABQQhqEMiAgIAADAILIAFBEzYCICAAIAFBIGoQxYCAgAAhAAwBCyAAENCAgIAAQQAhAAsgARDIgICAAAsgAUEwaiSAgICAACAAC6sBAQF/I4CAgIAAQRBrIgUkgICAgAAgASgCAEGNg8CAAEEBEJiFgIAAIAVBAzoACCAFQQhqEOWAgIAAIAVBCGogASAFIAMgBBDmgICAAAJAAkAgBS0ACEEDRw0AIAVBCGoQ5YCAgAAgASgCAEGNg8CAAEEBEJiFgIAAIAVBAzoACCAFQQhqEOWAgIAAIABBAzoAAAwBCyAAIAUpAwg3AgALIAVBEGokgICAgAALFwACQCAALQAAQQNGDQAgABCNgYCAAAsL+QYBDX8jgICAgABBMGsiBSSAgICAACAFQSBqIAMgAyAEahD/goCAACADQX9qIQYgBEF/cyEHIAUgBUEgahCag4CAAEEAIQggBSgCCCEJIAUoAgQhCiAFKAIAIQsDQCAKIAtrIQxBACENAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQANAIAwgDUYNASAJIA1qIg5BAWoiDyAOSQ0CIAsgDWohDiANQQFqIQ0gDi0AACIQQZy9wIAAai0AACIORQ0ACyAJIA1qIgxBf2oiESAITQ0EIAUgBDYCFCAFIAM2AhAgBSAINgIYIAUgETYCHCAIRQ0DIAggBEYNAyAIIARPDQUgAyAIaiwAAEG/f0oNAwwFCxCehYCAACAIIARGDQEgBSAENgIEIAUgAzYCACAFIAg2AhwgBSAENgIQAkACQCAIRQ0AIAggBE8NASADIAhqLAAAQb9/TA0BCyABKAIAIAMgCGogBCAIaxCYhYCAACAFQQM6ACAgBUEgahDlgICAAAwCCyAFIAVBEGo2AiggBSAFQRxqNgIkIAUgBTYCICAFQSBqEOeAgIAAAAtBsILAgABBHEHwh8CAABCNhoCAAAALIABBAzoAACAFQTBqJICAgIAADwsCQCAHIAlqIA1qRQ0AIBEgBE8NAiAGIAlqIA1qLAAAQb9/TA0CCyABKAIAIAMgCGogCSAIayANakF/ahCYhYCAACAFQQM6ACAgBUEgahDlgICAAAsgDkGSf2oiCUEHTQ0BIA5Bnn9qIglBBE0NAkGpg8CAACEJIA5BIkYNCiAOQdwARw0DQaeDwIAAIQkMCgsgBSAFQRxqNgIoIAUgBUEYajYCJCAFIAVBEGo2AiAgBUEgahDogICAAAALIAkOCAYBAQEFAQQDBgsgCQ4FAQAAAAYBC0GKgMCAAEEoQdCAwIAAEKGFgIAAAAtBpYPAgAAhCQwFCyAFQdzqwYEDNgAQIAUgEEEPcUGMvcCAAGotAAA6ABUgBSAQQQR2QYy9wIAAai0AADoAFCABKAIAIAVBEGpBBhCYhYCAAAwFC0Gdg8CAACEJDAMLQZ+DwIAAIQkMAgtBoYPAgAAhCQwBC0Gjg8CAACEJCyABKAIAIAlBAhCYhYCAAAsgBUEDOgAgIAsgDWohCyAFQSBqEOWAgIAAIA8hCSAMIQgMAAsLKgEBfyAAKAIAIgEoAgAgASgCBCAAKAIEKAIAIAAoAggoAgAQkoaAgAAACyoBAX8gACgCACIBKAIAIAEoAgQgACgCBCgCACAAKAIIKAIAEJKGgIAAAAuvAQEBfyOAgICAAEEgayICJICAgIAAIAJBCGpBgAEQ6oCAgAAgAiACQQhqNgIYIAIgASACQRhqEOuAgIAAIgE2AhwCQAJAIAENACACQRxqELyAgIAAIAJBADYCFCACQRRqELyAgIAAIABBDGogAkEQaigCADYCACAAIAIpAwg3AgQgAEEANgIADAELIABBATYCACAAIAE2AgQgAkEIahC+gICAAAsgAkEgaiSAgICAAAtGAgF/AX4jgICAgABBEGsiAiSAgICAACACQQhqIAFBABCWg4CAACACKQMIIQMgAEEANgIIIAAgAzcCACACQRBqJICAgIAAC7YBAQJ/I4CAgIAAQSBrIgIkgICAgAACQCAAKAIIDQAgAEEIaiIDQX8Q/4SAgAAaIABBDGpBAToAACADIAAoAghBAWoQ/4SAgAAaIAEoAgBBl4PAgABBBBCYhYCAACACQQM6ABAgAiACQRBqEO2AgIAAIgA2AgwCQCAADQAgAkEMahC8gICAAAsgAkEgaiSAgICAACAADwtBxITAgABBECACQRhqQciFwIAAQZiFwIAAEKqGgIAAAAvZAQEBfyOAgICAAEEgayICJICAgIAAIAJBgAEQ6oCAgAAgAkGOg8CAAEGSg8CAACABLQAAIgEbQQRBBSABGxCYhYCAACACQQM6ABggAiACQRhqEO2AgIAAIgE2AhQCQAJAIAENACACQRRqELyAgIAAIAJBADYCECACQRBqELyAgIAAIAJBADYCDCACQQxqELyAgIAAIABBDGogAkEIaigCADYCACAAIAIpAwA3AgQgAEEANgIADAELIABBATYCACAAIAE2AgQgAhC+gICAAAsgAkEgaiSAgICAAAtUAQF/I4CAgIAAQRBrIgEkgICAgAACQCAALQAAQQNGDQAgASAAKQIANwMIIAFBCGoQh4OAgAAhACABQRBqJICAgIAAIAAPCyABQRBqJICAgIAAQQALFAAgACABQYyDwIAAQQEQ74CAgAALGAAgASgCACACIAMQmIWAgAAgAEEDOgAACxQAIAAgAUGbg8CAAEEBEO+AgIAACxQAIAAgAUGrg8CAAEEBEO+AgIAACw4AIABBFEEEEM6CgIAAC4ABAQF/I4CAgIAAQTBrIgIkgICAgAAgAiABNgIMIAIgADYCCCACIAJBCGpBgYCAgAAQk4WAgAAgAkEkakEBNgIAIAJCAjcCFCACQayIwIAANgIQIAIgAikDADcDKCACIAJBKGo2AiAgAkEQahClg4CAACEAIAJBMGokgICAgAAgAAsMACABIAEQ9YCAgAALEgAgAUHsk8CAAEEMEMiGgIAACwwAIAEgARD3gICAAAsSACABQeyTwIAAQQwQyIaAgAALDAAgASABEPmAgIAACxIAIAFB7JPAgABBDBDIhoCAAAsMACABIAEQ+4CAgAALEgAgAUHsk8CAAEEMEMiGgIAACwwAIAEgARD9gICAAAsSACABQeyTwIAAQQwQyIaAgAALDAAgACABEKyFgIAACwwAIAEgARCAgYCAAAsSACABQeyTwIAAQQwQyIaAgAALDwAgACgCACABEOeGgIAACw8AIAAoAgAgARCehoCAAAsdACAAKAIAIgAoAgAgASAAKAIEKAIgEYCAgIAAAAuqAQEBfyOAgICAAEHAAGsiAiSAgICAACACIAE2AgwgAkEANgIYIAJCATcDECACQTRqQQE2AgAgAkIBNwIkIAJBrIPAgAA2AiAgAkGCgICAADYCPCACIAJBOGo2AjAgAiACQQxqNgI4IAJBEGogAkEgahDqgoCAABCFgYCAACACQRBqEJCDgIAAIABBCGogAigCGDYCACAAIAIpAxA3AgAgAkHAAGokgICAgAALSAEBfyOAgICAAEEQayIBJICAgIAAAkAgAEUNAEG0g8CAAEE3IAFBCGpBuIXAgABBtITAgAAQqoaAgAAACyABQRBqJICAgIAAC6oBAQF/I4CAgIAAQcAAayICJICAgIAAIAIgATYCDCACQQA2AhggAkIBNwMQIAJBNGpBATYCACACQgE3AiQgAkGsg8CAADYCICACQYOAgIAANgI8IAIgAkE4ajYCMCACIAJBDGo2AjggAkEQaiACQSBqEOqCgIAAEIWBgIAAIAJBEGoQkIOAgAAgAEEIaiACKAIYNgIAIAAgAikDEDcCACACQcAAaiSAgICAAAsYAAJAIAAoAgBFDQAgAEEEahDYgICAAAsLFQACQCAAKAIARQ0AIAAQvoCAgAALCwIACzwBAn8gACgCACAAKAIEKAIAEYGAgIAAAAJAIAAoAgQiASgCBCICRQ0AIAAoAgAgAiABKAIIEM6CgIAACwsCAAsCAAtgAQN/AkAgAC0AAEECSQ0AIABBBGooAgAiASgCACABKAIEKAIAEYGAgIAAAAJAIAEoAgQiAigCBCIDRQ0AIAEoAgAgAyACKAIIEM6CgIAACyAAKAIEQQxBBBDOgoCAAAsLAgALAgALGAACQCAAKAIARQ0AIABBBGoQjYGAgAALCwIACxgAAkAgACgCAEUNACAAQQRqEI2BgIAACwsVAAJAIAAoAgBFDQAgABDYgICAAAsLIwAgAEEwahC+gICAACAAQQhqEL6AgIAAIABBIGoQvoCAgAALFQACQCAAKAIARQ0AIAAQvoCAgAALCxgAAkAgACgCAEUNACAAQQRqENiAgIAACwsYAAJAIAAoAgBFDQAgAEEEahDYgICAAAsLLgEBfyAAEPaEgIAAIABBBGohAQJAIAAoAgANACABEKmEgIAADwsgARCqhICAAAsCAAsCAAsCAAsCAAsVAAJAIAAoAgBFDQAgABC+gICAAAsLAgALAgALJQACQCAAIAEgAhDGg4CAACIARQ0AIABBACABEPKGgIAAGgsgAAs9AQF/AkAgACAEIAMQxoOAgAAiBUUNACAFIAEgBCACIAIgBEsbEPOGgIAAGiAAIAEgAiADEMeDgIAACyAFCxMAIAAgASACIAMQ54KAgABBAXMLPQEBfyOAgICAAEEQayIEJICAgIAAIARBCGpBACADIAEgAhCkgYCAACAAIAQpAwg3AgAgBEEQaiSAgICAAAtAAAJAAkAgAiABSQ0AIAQgAk8NASACIAQQj4aAgAAACyABIAIQkIaAgAAACyAAIAIgAWs2AgQgACADIAFqNgIACz0BAX8jgICAgABBEGsiBCSAgICAACAEQQhqIAMgAiABIAIQpIGAgAAgACAEKQMINwIAIARBEGokgICAgAALOQACQAJAIAEoAghFDQAgACABQcAAEPOGgIAAGgwBCyAAEKeBgIAAIAEoAghFDQAgARCUgYCAAA8LCzIBAX8jgICAgABBEGsiASSAgICAACABEM2EgIAAIAAgARDggYCAACABQRBqJICAgIAACyYBAX8gAS8AACECIAFBADsAACAAIAJBCHY6AAEgACACQQFxOgAACzMAAkAgASgCAA0AIAIgAyAEEKiGgIAAAAsgACABKQIANwIAIABBCGogAUEIaigCADYCAAspAAJAIAGnDQBB5JXAgABBFSAEEKiGgIAAAAsgACACNwMAIAAgAzcDCAtpAQF/I4CAgIAAQRBrIgUkgICAgAACQCABKAIAQQFHDQAgBSABKAIENgIMIAIgAyAFQQxqQdiFwIAAIAQQqoaAgAAACyAAIAEpAgQ3AgAgAEEIaiABQQxqKAIANgIAIAVBEGokgICAgAALRAEBfyOAgICAAEEQayICJICAgIAAIAJBCGogAUEIaigCADYCACACIAEpAgA3AwAgACACEPiEgIAAIAJBEGokgICAgAALQAEBfyOAgICAAEEQayIDJICAgIAAIAMgASkDADcDCCACIANBCGpBCBCYhYCAACAAQQM6AAAgA0EQaiSAgICAAAvwAQICfwJ+I4CAgIAAQTBrIgIkgICAgAACQAJAAkAgASgCBCIDQQhJDQAgAkEQaiABKAIAIANBCBCjgYCAACACQRhqIAIoAhAgAigCFBCXhYCAACACLQAYQQFGDQIgAikAGSEEIAJBCGogASgCACABKAIEQQgQpYGAgAAgAikDCCEFIABBCGogBDcDACAAQQA2AgAgASAFNwIADAELIAJBGGpBC0GkhsCAAEEaENmCgIAAIABBATYCACAAIAIpAxg3AgQLIAJBMGokgICAgAAPC0HohcCAAEErIAJBKGpBlIbAgABBmIfAgAAQqoaAgAAAC0QBAX8jgICAgABBEGsiAiSAgICAACACQQhqIAFBCGooAgA2AgAgAiABKQIANwMAIAAgAhD5hICAACACQRBqJICAgIAAC0gBAX8jgICAgABBEGsiAiSAgICAACACQQhqIAEQqIGAgAAgAi0ACCEBIAAgAi0ACToAASAAIAFBAXE6AAAgAkEQaiSAgICAAAs8AAJAIAAoAgggACgCBEcNACAAQQEQjYWAgAALIAAQm4WAgAAgACgCCGogAToAACAAIAAoAghBAWo2AggLUAEBfyOAgICAAEEQayIDJICAgIAAIAMgAhDqgICAACADIAEgAhCYhYCAACAAQQhqIANBCGooAgA2AgAgACADKQMANwIAIANBEGokgICAgAAL8QEBAX8jgICAgABBoAFrIgMkgICAgAAgAyACNgIMIAMgATYCCCADQdAAaiADQQhqELSBgIAAAkACQCADKAJQQQFHDQAgAyADKQJUNwMQIANBmAFqIANBEGoQwIWAgAAgACADKQOYATcCBCAAQQE2AgAMAQsgA0EQaiADQdAAakEIakHAABDzhoCAABoCQCADKAIMDQAgAEEIaiADQRBqQcAAEPOGgIAAGiAAQQA2AgAMAQsgA0HQAGpBDEGIiMCAAEESENmCgIAAIABBATYCACAAIAMpA1A3AgQgA0EQahCUgYCAAAsgA0GgAWokgICAgAALjAEBAX8jgICAgABB4ABrIgIkgICAgAAgAkEIaiABEIKCgIAAAkACQCACKAIIQQFHDQAgAiACKQIMNwNYIAJB0ABqIAJB2ABqEMCFgIAAIAAgAikDUDcCBCAAQQE2AgAMAQsgAEEIaiACQQhqQQhqQcAAEPOGgIAAGiAAQQA2AgALIAJB4ABqJICAgIAAC6kDAgF/An4jgICAgABB0ABrIgMkgICAgAACQAJAAkACQAJAIAJBEEkNACADQRBqIAEgAkEQEKOBgIAAIANBMGogAygCECADKAIUEL+EgIAAIAMtADBBAUYNAiADQTlqKQAAIQQgAykAMSEFIANBCGogASACQRAQpYGAgAAgA0E8aiAENwIAIAMgBTcCNEEAIQEgAygCDCECDAELIANBMGpBC0GkhsCAAEEaENmCgIAAQQEhAQsgA0EkaiADQThqKQMANwIAIANBLGogA0EwakEQaigCADYCACADIAE2AhggAyADKQMwNwIcAkAgAUUNACADIAMpAhw3AzAgA0HIAGogA0EwahDAhYCAACAAIAMpA0g3AgQgAEEBNgIADAMLIAINASADQRhqQRBqKQMAIQQgAykDICEFIABBADYCACAAQRBqIAQ3AwAgAEEIaiAFNwMADAILQeiFwIAAQSsgA0HIAGpBlIbAgABBmIfAgAAQqoaAgAAACyADQRhqQQxBiIjAgABBEhDZgoCAACAAQQE2AgAgACADKQMYNwIECyADQdAAaiSAgICAAAvCAQIBfwF+I4CAgIAAQTBrIgIkgICAgAAgAkEIakGACBDqgICAACACQShqIAEgAkEIahC3gYCAACACIAIpAygiAzcDGAJAAkAgA6dB/wFxQQNHDQAgAkEYahDlgICAACAAIAIpAwg3AgQgAEEMaiACQRBqKAIANgIAQQAhAQwBCyACIAM3AyggAkEgaiACQShqEMCFgIAAIAAgAikDIDcCBCACQQhqEL6AgIAAQQEhAQsgACABNgIAIAJBMGokgICAgAALhAECAX8BfiOAgICAAEEgayIDJICAgIAAIANBGGogASACEP+BgIAAIAMgAykDGCIENwMIAkACQCAEp0H/AXFBA0cNACADQQhqEOWAgIAAIABBAzoAAAwBCyADIAQ3AxggA0EQaiADQRhqEMCFgIAAIAAgAykDEDcCAAsgA0EgaiSAgICAAAuNAQEBfyOAgICAAEEwayICJICAgIAAIAJBCGpBgAgQ6oCAgAAgAiABQQhqKQMANwMoIAIgASkDADcDICACQQhqIAJBIGpBEBCYhYCAACACQgM3AxggAkEYahDlgICAACAAQQxqIAJBCGpBCGooAgA2AgAgACACKQMINwIEIABBADYCACACQTBqJICAgIAAC8IBAgF/AX4jgICAgABBMGsiAiSAgICAACACQQhqQYAIEOqAgIAAIAJBKGogASACQQhqELqBgIAAIAIgAikDKCIDNwMYAkACQCADp0H/AXFBA0cNACACQRhqEOWAgIAAIAAgAikDCDcCBCAAQQxqIAJBEGooAgA2AgBBACEBDAELIAIgAzcDKCACQSBqIAJBKGoQwIWAgAAgACACKQMgNwIEIAJBCGoQvoCAgABBASEBCyAAIAE2AgAgAkEwaiSAgICAAAt5AQF/I4CAgIAAQRBrIgMkgICAgAAgAyABKAIINgIEIAIgA0EEakEEEJiFgIAAIANCAzcDCCADQQhqEOWAgIAAIAIgARCbhYCAACABKAIIEJiFgIAAIANCAzcDCCADQQhqEOWAgIAAIABBAzoAACADQRBqJICAgIAAC7MBAgF/AX4jgICAgABB0ABrIgMkgICAgAAgAyACNgIkIAMgATYCICADIAA2AhwgA0EQaiADQRxqQYSAgIAAEJGFgIAAIAMpAxAhBCADQQhqIANBIGpBhYCAgAAQ6IKAgAAgA0E8akECNgIAIAMgBDcDQCADQgI3AiwgA0HYiMCAADYCKCADIAMpAwg3A0ggAyADQcAAajYCOCADQShqEKWDgIAAIQAgA0HQAGokgICAgAAgAAuAAQEBfyOAgICAAEEwayICJICAgIAAIAIgATYCDCACIAA2AgggAiACQQhqQYGAgIAAEJOFgIAAIAJBJGpBATYCACACQgI3AhQgAkH8iMCAADYCECACIAIpAwA3AyggAiACQShqNgIgIAJBEGoQpYOAgAAhACACQTBqJICAgIAAIAAL7wMBBH8jgICAgABBMGsiAiSAgICAACACQQhqIAEQzYCAgAACQAJAAkACQAJAIAItAAhBAUYNACACLQAJQQFHDQEgAi0ACiEDIAJBCGoQyICAgAACQCADQSJGDQAgASACQShqQbCQwIAAEOCAgIAAIQMgAkEBNgIIIAIgAzYCDAwECyABENCAgIAAIAFBDGoiAxDMgICAACACQRhqIAEgAxCpg4CAACACKAIcIQMCQAJAAkAgAigCGEEBRg0AIAJBGGpBDGooAgAhBCACQSBqKAIAIQUgAw0BIAJBCGogBSAEEJmDgIAADAILIABBATYCACAAIAM2AgQMBAsgAkEIaiAFIAQQmYOAgAALQQEhAyACKAIIQQFGDQMgACACKQIMNwIEIABBDGogAkEIakEMaigCADYCAEEAIQEMBAsgACACKAIMNgIEIABBATYCAAwBCyACQQU2AhggASACQRhqEMWAgIAAIQEgAEEBNgIAIAAgATYCBCACQQhqEMiAgIAACyACQTBqJICAgIAADwsgACACKAIMIAEQyoCAgAA2AgRBASEBQQAhAwsgACABNgIAAkACQCACKAIIDQAgAUUNASACQQhqQQRyEL6AgIAADAELIANFDQAgAkEIakEEchDYgICAAAsgAkEwaiSAgICAAAvvAQEBfyOAgICAAEEgayIEJICAgIAAIAQgACABIAIQv4GAgAAiATYCGAJAAkAgAQ0AIARBGGoQvICAgAAgBEEYaiAAKAIAEPGAgIAAIAQgBEEYahDtgICAACIBNgIUIAENASAEQRRqELyAgIAAIAAoAgAhASAEQQhqIAMQz4WAgAAgBCABIAQoAgggBCgCDBDAgYCAACIBNgIYIAENASAEQRhqELyAgIAAIARBAzoAGCAEIARBGGoQ7YCAgAAiATYCFCABDQEgBEEUahC8gICAAEEAIQEMAQsgARCNg4CAACEBCyAEQSBqJICAgIAAIAELyAEBAn8jgICAgABBEGsiAySAgICAAAJAIAAtAARBAUYNACAAKAIAKAIAQZyDwIAAQQEQmIWAgAALIANBAzoACCADIANBCGoQ7YCAgAAiBDYCBAJAIAQNACADQQRqELyAgIAAIABBAjoABCADIAAoAgAgASACEMCBgIAAIgQ2AgggBA0AIANBCGoQvICAgAAgA0EDOgAIIAMgA0EIahDtgICAACIENgIEIAQNACADQQRqELyAgIAAQQAhBAsgA0EQaiSAgICAACAEC1kBAX8jgICAgABBEGsiAySAgICAACADQQhqIAAgACABIAIQ5ICAgAAgAyADQQhqEO2AgIAAIgA2AgQCQCAADQAgA0EEahC8gICAAAsgA0EQaiSAgICAACAAC9YBAQF/I4CAgIAAQRBrIgQkgICAgAAgBCAAIAEgAhC/gYCAACIBNgIIAkACQCABDQAgBEEIahC8gICAACAEQQhqIAAoAgAQ8YCAgAAgBCAEQQhqEO2AgIAAIgE2AgQgAQ0BIARBBGoQvICAgAAgBCADIAAoAgAQwoGAgAAiATYCCCABDQEgBEEIahC8gICAACAEQQM6AAggBCAEQQhqEO2AgIAAIgE2AgQgAQ0BIARBBGoQvICAgABBACEBDAELIAEQjYOAgAAhAQsgBEEQaiSAgICAACABC9gBAQF/I4CAgIAAQcAAayICJICAgIAAIAIgADYCDCACQQA2AhggAkIBNwMQIAJBNGpBATYCACACQgE3AiQgAkGsg8CAADYCICACQYaAgIAANgI8IAIgAkE4ajYCMCACIAJBDGo2AjggAkEQaiACQSBqEOqCgIAAEIWBgIAAIAJBEGoQkIOAgAAgAkEoaiACKAIYNgIAIAIgAikDEDcDICACIAJBIGoQz4WAgAAgASACKAIAIAIoAgQQwIGAgAAhASACQSBqEL6AgIAAIAJBwABqJICAgIAAIAELIgAgASACEPOAgIAAEI2DgIAAIQEgAEEBNgIAIAAgATYCBAsiACABIAIQ84CAgAAQjYOAgAAhASAAQQE2AgAgACABNgIECz4BAn8jgICAgABBEGsiASSAgICAACABIAAQiYaAgAAgARCmg4CAACECIAAQvoCAgAAgAUEQaiSAgICAACACC10BAn8jgICAgABBEGsiAiSAgICAACACQQhqIAAQz4WAgAAgAigCDCEAIAIoAgghAyACIAEQz4WAgAAgAyAAIAIoAgAgAigCBBCBg4CAACEBIAJBEGokgICAgAAgAQuXBAEDfyOAgICAAEHQAGsiAiSAgICAAAJAAkACQAJAIAEoAgQiA0EESQ0AIAJBGGogASgCACADQQQQo4GAgAAgAkEwaiACKAIYIAIoAhwQw4WAgAAgAi0AMEEBRg0CIAIoADEhAyACQRBqIAEoAgAgASgCBEEEEKWBgIAAIAEgAikDEDcCACACIAOtNwJEIAJBADYCQCACQcAAahCSgYCAACADRQ0BAkAgASgCBCIEIANJDQAgAkEIaiABKAIAIAQgAxCjgYCAACACQSBqIAIoAgggAigCDBCygYCAACACIAEoAgAgASgCBCADEKWBgIAAIAEgAikDADcCACACQcAAakEIaiIBIAJBIGpBCGooAgA2AgAgAiACKQMgNwNAIAJBMGogAkHAAGoQm4WAgAAgASgCACACKAJEEMiFgIAAIABBDGogAkEwakEIaigCADYCACAAIAIpAzA3AgQgAEEANgIADAQLIAJBwABqQQtBpIbAgABBGhDZgoCAACAAQQE2AgAgACACKQNANwIEDAMLIAJBMGpBC0GkhsCAAEEaENmCgIAAIAIgAikDMDcDMCACQSBqIAJBMGoQwIWAgAAgACACKQMgNwIEIABBATYCAAwCCyAAQoCAgIAQNwIAIABBCGpCADcCAAwBC0HohcCAAEErIAJBIGpBlIbAgABBmIfAgAAQqoaAgAAACyACQdAAaiSAgICAAAvWBQEDfyOAgICAAEEgayICJICAgIAAIAJBCGogASgCABDNgICAAAJAAkACQAJAAkACQCACLQAIQQFGDQACQCACLQAJQQFGDQAgASgCACEBIAJBAzYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAULAkACQAJAAkAgAi0ACiIDQSxGDQAgA0H9AEcNASAAQYAEOwEADAgLIAEtAAQNASABKAIAENCAgIAAIAJBEGogASgCABDNgICAACACLQAQQQFGDQQgAi0AEiEDIAItABEhBCACQRBqEMiAgIAAIAJBCGoQyICAgAAgBA0CIAEoAgAhASACQQU2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwICyABLQAEDQAgASgCACEBIAJBCDYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAYLIAFBADoABCACQQhqEMiAgIAACyADQf8BcSIDQSJGDQMgA0H9AEcNAiABKAIAIQEgAkESNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMBQsgAEEBOgAAIABBBGogAigCDDYCAAwECyAAQQE6AAAgAEEEaiACKAIUNgIADAILIAEoAgAhASACQRA2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwCCyABKAIAIgEQ0ICAgAAgAUEMaiIDEMyAgIAAIAJBEGogASADEKmDgIAAAkACQAJAAkAgAigCEEEBRw0AIAIgAigCFCIBNgIMIAJBAToACAwBCyACQQhqIAJBGGooAgAgAkEQakEMaigCABDJgYCAACACLQAIQQFHDQEgAigCDCEBCyAAQQRqIAE2AgBBASEBDAELIAAgAi0ACToAAUEAIQELIAAgAToAAAwBCyACQQhqEMiAgIAACyACQSBqJICAgIAACycAIAEgAkHmk8CAAEEGEIGDgIAAIQEgAEEAOgAAIAAgAUEBczoAAQvWBQEDfyOAgICAAEEgayICJICAgIAAIAJBCGogASgCABDNgICAAAJAAkACQAJAAkACQCACLQAIQQFGDQACQCACLQAJQQFGDQAgASgCACEBIAJBAzYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAULAkACQAJAAkAgAi0ACiIDQSxGDQAgA0H9AEcNASAAQYAGOwEADAgLIAEtAAQNASABKAIAENCAgIAAIAJBEGogASgCABDNgICAACACLQAQQQFGDQQgAi0AEiEDIAItABEhBCACQRBqEMiAgIAAIAJBCGoQyICAgAAgBA0CIAEoAgAhASACQQU2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwICyABLQAEDQAgASgCACEBIAJBCDYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAYLIAFBADoABCACQQhqEMiAgIAACyADQf8BcSIDQSJGDQMgA0H9AEcNAiABKAIAIQEgAkESNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMBQsgAEEBOgAAIABBBGogAigCDDYCAAwECyAAQQE6AAAgAEEEaiACKAIUNgIADAILIAEoAgAhASACQRA2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwCCyABKAIAIgEQ0ICAgAAgAUEMaiIDEMyAgIAAIAJBEGogASADEKmDgIAAAkACQAJAAkAgAigCEEEBRw0AIAIgAigCFCIBNgIMIAJBAToACAwBCyACQQhqIAJBGGooAgAgAkEQakEMaigCABDLgYCAACACLQAIQQFHDQEgAigCDCEBCyAAQQRqIAE2AgBBASEBDAELIAAgAi0ACToAAUEAIQELIAAgAToAAAwBCyACQQhqEMiAgIAACyACQSBqJICAgIAAC1UAAkACQCABIAJB3JvAgABBFhCBg4CAAEUNACAAQQA6AAEMAQsCQCABIAJB5pPAgABBBhCBg4CAAEUNACAAQQE6AAEMAQsgAEECOgABCyAAQQA6AAAL1gUBA38jgICAgABBIGsiAiSAgICAACACQQhqIAEoAgAQzYCAgAACQAJAAkACQAJAAkAgAi0ACEEBRg0AAkAgAi0ACUEBRg0AIAEoAgAhASACQQM2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwFCwJAAkACQAJAIAItAAoiA0EsRg0AIANB/QBHDQEgAEGABDsBAAwICyABLQAEDQEgASgCABDQgICAACACQRBqIAEoAgAQzYCAgAAgAi0AEEEBRg0EIAItABIhAyACLQARIQQgAkEQahDIgICAACACQQhqEMiAgIAAIAQNAiABKAIAIQEgAkEFNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMCAsgAS0ABA0AIAEoAgAhASACQQg2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwGCyABQQA6AAQgAkEIahDIgICAAAsgA0H/AXEiA0EiRg0DIANB/QBHDQIgASgCACEBIAJBEjYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAULIABBAToAACAAQQRqIAIoAgw2AgAMBAsgAEEBOgAAIABBBGogAigCFDYCAAwCCyABKAIAIQEgAkEQNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMAgsgASgCACIBENCAgIAAIAFBDGoiAxDMgICAACACQRBqIAEgAxCpg4CAAAJAAkACQAJAIAIoAhBBAUcNACACIAIoAhQiATYCDCACQQE6AAgMAQsgAkEIaiACQRhqKAIAIAJBEGpBDGooAgAQzYGAgAAgAi0ACEEBRw0BIAIoAgwhAQsgAEEEaiABNgIAQQEhAQwBCyAAIAItAAk6AAFBACEBCyAAIAE6AAAMAQsgAkEIahDIgICAAAsgAkEgaiSAgICAAAsnACABIAJB35rAgABBChCBg4CAACEBIABBADoAACAAIAFBAXM6AAEL1gUBA38jgICAgABBIGsiAiSAgICAACACQQhqIAEoAgAQzYCAgAACQAJAAkACQAJAAkAgAi0ACEEBRg0AAkAgAi0ACUEBRg0AIAEoAgAhASACQQM2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwFCwJAAkACQAJAIAItAAoiA0EsRg0AIANB/QBHDQEgAEGABDsBAAwICyABLQAEDQEgASgCABDQgICAACACQRBqIAEoAgAQzYCAgAAgAi0AEEEBRg0EIAItABIhAyACLQARIQQgAkEQahDIgICAACACQQhqEMiAgIAAIAQNAiABKAIAIQEgAkEFNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMCAsgAS0ABA0AIAEoAgAhASACQQg2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwGCyABQQA6AAQgAkEIahDIgICAAAsgA0H/AXEiA0EiRg0DIANB/QBHDQIgASgCACEBIAJBEjYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAULIABBAToAACAAQQRqIAIoAgw2AgAMBAsgAEEBOgAAIABBBGogAigCFDYCAAwCCyABKAIAIQEgAkEQNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMAgsgASgCACIBENCAgIAAIAFBDGoiAxDMgICAACACQRBqIAEgAxCpg4CAAAJAAkACQAJAIAIoAhBBAUcNACACIAIoAhQiATYCDCACQQE6AAgMAQsgAkEIaiACQRhqKAIAIAJBEGpBDGooAgAQz4GAgAAgAi0ACEEBRw0BIAIoAgwhAQsgAEEEaiABNgIAQQEhAQwBCyAAIAItAAk6AAFBACEBCyAAIAE6AAAMAQsgAkEIahDIgICAAAsgAkEgaiSAgICAAAsnACABIAJB3JPAgABBChCBg4CAACEBIABBADoAACAAIAFBAXM6AAEL1gUBA38jgICAgABBIGsiAiSAgICAACACQQhqIAEoAgAQzYCAgAACQAJAAkACQAJAAkAgAi0ACEEBRg0AAkAgAi0ACUEBRg0AIAEoAgAhASACQQM2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwFCwJAAkACQAJAIAItAAoiA0EsRg0AIANB/QBHDQEgAEGABjsBAAwICyABLQAEDQEgASgCABDQgICAACACQRBqIAEoAgAQzYCAgAAgAi0AEEEBRg0EIAItABIhAyACLQARIQQgAkEQahDIgICAACACQQhqEMiAgIAAIAQNAiABKAIAIQEgAkEFNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMCAsgAS0ABA0AIAEoAgAhASACQQg2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwGCyABQQA6AAQgAkEIahDIgICAAAsgA0H/AXEiA0EiRg0DIANB/QBHDQIgASgCACEBIAJBEjYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAULIABBAToAACAAQQRqIAIoAgw2AgAMBAsgAEEBOgAAIABBBGogAigCFDYCAAwCCyABKAIAIQEgAkEQNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMAgsgASgCACIBENCAgIAAIAFBDGoiAxDMgICAACACQRBqIAEgAxCpg4CAAAJAAkACQAJAIAIoAhBBAUcNACACIAIoAhQiATYCDCACQQE6AAgMAQsgAkEIaiACQRhqKAIAIAJBEGpBDGooAgAQ0YGAgAAgAi0ACEEBRw0BIAIoAgwhAQsgAEEEaiABNgIAQQEhAQwBCyAAIAItAAk6AAFBACEBCyAAIAE6AAAMAQsgAkEIahDIgICAAAsgAkEgaiSAgICAAAtVAAJAAkAgASACQZybwIAAQQ4QgYOAgABFDQAgAEEAOgABDAELAkAgASACQaqbwIAAQQ4QgYOAgABFDQAgAEEBOgABDAELIABBAjoAAQsgAEEAOgAAC9YFAQN/I4CAgIAAQSBrIgIkgICAgAAgAkEIaiABKAIAEM2AgIAAAkACQAJAAkACQAJAIAItAAhBAUYNAAJAIAItAAlBAUYNACABKAIAIQEgAkEDNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMBQsCQAJAAkACQCACLQAKIgNBLEYNACADQf0ARw0BIABBgAY7AQAMCAsgAS0ABA0BIAEoAgAQ0ICAgAAgAkEQaiABKAIAEM2AgIAAIAItABBBAUYNBCACLQASIQMgAi0AESEEIAJBEGoQyICAgAAgAkEIahDIgICAACAEDQIgASgCACEBIAJBBTYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAgLIAEtAAQNACABKAIAIQEgAkEINgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMBgsgAUEAOgAEIAJBCGoQyICAgAALIANB/wFxIgNBIkYNAyADQf0ARw0CIAEoAgAhASACQRI2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwFCyAAQQE6AAAgAEEEaiACKAIMNgIADAQLIABBAToAACAAQQRqIAIoAhQ2AgAMAgsgASgCACEBIAJBEDYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAILIAEoAgAiARDQgICAACABQQxqIgMQzICAgAAgAkEQaiABIAMQqYOAgAACQAJAAkACQCACKAIQQQFHDQAgAiACKAIUIgE2AgwgAkEBOgAIDAELIAJBCGogAkEYaigCACACQRBqQQxqKAIAENOBgIAAIAItAAhBAUcNASACKAIMIQELIABBBGogATYCAEEBIQEMAQsgACACLQAJOgABQQAhAQsgACABOgAADAELIAJBCGoQyICAgAALIAJBIGokgICAgAALVQACQAJAIAEgAkGcm8CAAEEOEIGDgIAARQ0AIABBADoAAQwBCwJAIAEgAkGqm8CAAEEOEIGDgIAARQ0AIABBAToAAQwBCyAAQQI6AAELIABBADoAAAtkAQJ/I4CAgIAAQRBrIgIkgICAgAAgAiABKAIAEOGAgIAAIgM2AgwCQAJAIAMNACACQQxqELyAgIAAIAAgASgCABC9gYCAAAwBCyAAQQE2AgAgACADNgIECyACQRBqJICAgIAAC2QBAn8jgICAgABBEGsiAiSAgICAACACIAEoAgAQ4YCAgAAiAzYCDAJAAkAgAw0AIAJBDGoQvICAgAAgACABKAIAENaBgIAADAELIABBATYCACAAIAM2AgQLIAJBEGokgICAgAALmAICAn8BfiOAgICAAEHAAGsiAiSAgICAACACQRhqIAEQvYGAgABBASEBAkACQCACKAIYQQFHDQAgACACKAIcEI2DgIAANgIEDAELIAJBCGpBCGogAkEYakEEciIBQQhqKAIAIgM2AgAgAiABKQIAIgQ3AwggAkEwakEIaiADNgIAIAIgBDcDMCACQRhqIAJBMGoQr4GAgAACQCACKAIYQQFHDQAgAiACKQIcNwMoIAJBMGogAkEoahCEgYCAACACQTBqEMWBgIAAIQEgAkEoahCKgYCAACAAIAE2AgRBASEBDAELIAAgAikCHDcCBCAAQQxqIAJBGGpBDGooAgA2AgBBACEBCyAAIAE2AgAgAkHAAGokgICAgAALZAECfyOAgICAAEEQayICJICAgIAAIAIgASgCABDhgICAACIDNgIMAkACQCADDQAgAkEMahC8gICAACAAIAEoAgAQ2IGAgAAMAQsgAEEBNgIAIAAgAzYCBAsgAkEQaiSAgICAAAuFAwMBfwJ+AX8jgICAgABB4ABrIgIkgICAgAAgAkEwaiABEL2BgIAAAkACQCACKAIwQQFHDQAgAigCNBCNg4CAACEBIABBATYCACAAIAE2AgQMAQsgAkEIakEIaiACQTBqQQRyIgFBCGooAgA2AgAgAiABKQIANwMIIAIgAkEIahDPhYCAACACQTBqIAIoAgAgAigCBEEKENyGgIAAAkACQCACLQAwQQFGDQAgAkHAAGopAwAhAyACQTBqQQhqKQMAIQRBACEBDAELIAIgAi0AMToATyACQdAAaiACQc8AahCGgYCAACACQdAAahDFgYCAACEFQQEhAQsgAkEYakEQaiADNwMAIAIgBDcDICACIAU2AhwgAiABNgIYAkAgAUUNACAFEI2DgIAAIQEgAEEBNgIAIAAgATYCBCACQQhqEL6AgIAADAELIABBADYCACAAQRBqIAM3AwAgAEEIaiAENwMAIAJBCGoQvoCAgAAgAkEYahCHgYCAAAsgAkHgAGokgICAgAALUwECfyOAgICAAEEQayIBJICAgIAAIAEgACgCABDhgICAACICNgIMAkAgAg0AIAFBDGoQvICAgAAgACgCABDagYCAACECCyABQRBqJICAgIAAIAILQwEBfyOAgICAAEEQayIBJICAgIAAIAEgABDLgICAACIANgIMAkAgAA0AIAFBDGoQvICAgAALIAFBEGokgICAgAAgAAu+BAEDfyOAgICAAEEgayICJICAgIAAIAIgASgCABDNgICAAAJAAkACQAJAAkAgAi0AAEEBRg0AAkAgAi0AAUEBRg0AIAEoAgAhASACQQI2AgggASACQQhqEMWAgIAAIQEgAEEBNgIAIAAgATYCBAwECwJAAkACQAJAIAItAAIiA0EsRg0AIANB3QBHDQEgAEEANgIAIABBCGpCADcDAAwHCyABLQAEDQEgASgCABDQgICAACACQQhqIAEoAgAQzYCAgAAgAi0ACEEBRg0EIAItAAohAyACLQAJIQQgAkEIahDIgICAACACEMiAgIAAIAQNAiABKAIAIQEgAkEFNgIIIAEgAkEIahDFgICAACEBIABBATYCACAAIAE2AgQMBwsgAS0ABA0AIAEoAgAhASACQQc2AgggASACQQhqEMWAgIAAIQEgAEEBNgIAIAAgATYCBAwFCyABQQA6AAQgAhDIgICAAAsCQCADQf8BcUHdAEcNACABKAIAIQEgAkESNgIIIAEgAkEIahDFgICAACEBIABBATYCACAAIAE2AgQMBQsgAkEIaiABKAIAENiBgIAAIAIoAghBAUcNAiAAIAIoAgw2AgQgAEEBNgIADAQLIAAgAigCBDYCBCAAQQE2AgAMAwsgACACKAIMNgIEIABBATYCAAwBCyAAQQA2AgAgAEEQaiACKQMQNwMAIABBCGpCATcDACAAQRhqIAJBCGpBEGopAwA3AwAMAQsgAhDIgICAAAsgAkEgaiSAgICAAAuzBAEDfyOAgICAAEEgayICJICAgIAAIAJBCGogASgCABDNgICAAAJAAkACQAJAAkAgAi0ACEEBRg0AAkAgAi0ACUEBRg0AIAEoAgAhASACQQI2AhAgASACQRBqEMWAgIAAIQEgAEEBNgIAIAAgATYCBAwECwJAAkACQAJAIAItAAoiA0EsRg0AIANB3QBHDQEgAEIANwIADAcLIAEtAAQNASABKAIAENCAgIAAIAJBEGogASgCABDNgICAACACLQAQQQFGDQQgAi0AEiEDIAItABEhBCACQRBqEMiAgIAAIAJBCGoQyICAgAAgBA0CIAEoAgAhASACQQU2AhAgASACQRBqEMWAgIAAIQEgAEEBNgIAIAAgATYCBAwHCyABLQAEDQAgASgCACEBIAJBBzYCECABIAJBEGoQxYCAgAAhASAAQQE2AgAgACABNgIEDAULIAFBADoABCACQQhqEMiAgIAACyADQf8BcUHdAEcNAiABKAIAIQEgAkESNgIQIAEgAkEQahDFgICAACEBIABBATYCACAAIAE2AgQMBAsgACACKAIMNgIEIABBATYCAAwDCyAAIAIoAhQ2AgQgAEEBNgIADAELIAJBEGogASgCABDWgYCAAAJAIAIoAhBBAUcNACAAIAIoAhQ2AgQgAEEBNgIADAILIAAgAikCFDcCBCAAQQA2AgAgAEEMaiACQRBqQQxqKAIANgIADAELIAJBCGoQyICAgAALIAJBIGokgICAgAALswQBA38jgICAgABBIGsiAiSAgICAACACQQhqIAEoAgAQzYCAgAACQAJAAkACQAJAIAItAAhBAUYNAAJAIAItAAlBAUYNACABKAIAIQEgAkECNgIQIAEgAkEQahDFgICAACEBIABBATYCACAAIAE2AgQMBAsCQAJAAkACQCACLQAKIgNBLEYNACADQd0ARw0BIABCADcCAAwHCyABLQAEDQEgASgCABDQgICAACACQRBqIAEoAgAQzYCAgAAgAi0AEEEBRg0EIAItABIhAyACLQARIQQgAkEQahDIgICAACACQQhqEMiAgIAAIAQNAiABKAIAIQEgAkEFNgIQIAEgAkEQahDFgICAACEBIABBATYCACAAIAE2AgQMBwsgAS0ABA0AIAEoAgAhASACQQc2AhAgASACQRBqEMWAgIAAIQEgAEEBNgIAIAAgATYCBAwFCyABQQA6AAQgAkEIahDIgICAAAsgA0H/AXFB3QBHDQIgASgCACEBIAJBEjYCECABIAJBEGoQxYCAgAAhASAAQQE2AgAgACABNgIEDAQLIAAgAigCDDYCBCAAQQE2AgAMAwsgACACKAIUNgIEIABBATYCAAwBCyACQRBqIAEoAgAQvYGAgAACQCACKAIQQQFHDQAgACACKAIUNgIEIABBATYCAAwCCyAAIAIpAhQ3AgQgAEEANgIAIABBDGogAkEQakEMaigCADYCAAwBCyACQQhqEMiAgIAACyACQSBqJICAgIAACxQAIABB5pPAgABBBiABEMGBgIAAC2MBAX8jgICAgABBEGsiAiSAgICAAAJAAkAgAUH/AXFFDQAgAkEIaiAAEO6AgIAAIAIgAkEIahDtgICAACIBNgIEIAENASACQQRqELyAgIAAC0EAIQELIAJBEGokgICAgAAgAQv4AgEDfyOAgICAAEEwayICJICAgIAAAkACQAJAIAEoAggiA0EBaiIEIANJDQAgAiAEEOqAgIAAIAIgARDygYCAACACQekAELGBgIAAIAEoAggiA0EBaiIEIANJDQEgAkEQaiAEEOqAgIAAIAJBEGogARDygYCAACACQRBqQesAELGBgIAAIAEoAggiA0EBaiIEIANJDQIgAkEgaiAEEOqAgIAAIAJBIGogARDygYCAACACQSBqQfYAELGBgIAAIABCADcDACAAQgA3AxggAEE4aiACQQhqKAIANgIAIAAgAikDADcCMCAAIAIpAxA3AgggAEEQaiACQRBqQQhqKAIANgIAIABBIGogAikDIDcCACAAQShqIAJBIGpBCGooAgA2AgAgARC+gICAACACQTBqJICAgIAADwtBsILAgABBHEHIjMCAABCNhoCAAAALQbCCwIAAQRxB2IzAgAAQjYaAgAAAC0GwgsCAAEEcQeiMwIAAEI2GgIAAAAvcAgEBfyOAgICAAEHQAGsiBiSAgICAACAGQSBqIAEgAiADEOKBgIAAIAZBGGogBkEgahDPhYCAACAGQTBqIAYoAhggBigCHBDphICAAAJAAkAgBigCMA0AIAYgARDjgYCAADcDQCAGQQhqIAZBIGoQz4WAgAAgBigCCCAGKAIMIAZBwABqQQgQ6ISAgAAaIAEgAiADEOSBgIAAIAFBGGogBCAFEOWBgIAAIABBADYCAEEBIQEMAQsgBkHAAGpBCGogBkEwakEIaigCADYCACAGIAYpAzA3A0AgBkEQaiAGQcAAahDPhYCAACAAIAFBGGogBigCECAGKAIUEOaBgIAAIAQgBRDngYCAACAGQcAAahC+gICAAEEAIQELIAZBIGoQvoCAgAACQAJAIAYoAjBFDQAgAUUNASAGQTBqEL6AgIAADAELIAZBMGoQiIGAgAALIAZB0ABqJICAgIAAC6sBAQN/I4CAgIAAQSBrIgQkgICAgAACQCABQThqKAIAIgUgA2oiBiAFSQ0AIARBEGogBhDqgICAACAEQQhqIAFBMGoQz4WAgAAgBEEQaiAEKAIIIAQoAgwQmIWAgAAgBEEQaiACIAMQmIWAgAAgAEEIaiAEQRBqQQhqKAIANgIAIAAgBCkDEDcCACAEQSBqJICAgIAADwtBsILAgABBHEG4jMCAABCNhoCAAAALKQEBfgJAIAApAwAiASAAKQMYUg0AIAEPC0HsisCAAEHmABDPhICAAAALlQECAX8CfiOAgICAAEEgayIDJICAgIAAIANBEGogACAAKQMAEPeBgIAAAkAgACkDACIEQgF8IgUgBFQNACAAIAU3AwAgA0EIaiADQRBqEM+FgIAAIAMoAgggAygCDCABIAIQ6ISAgAAaIANBEGoQvoCAgAAgA0EgaiSAgICAAA8LQbCCwIAAQRxBzI7AgAAQjYaAgAAAC5UBAgF/An4jgICAgABBIGsiAySAgICAACADQRBqIAAgACkDABD2gYCAAAJAIAApAwAiBEIBfCIFIARUDQAgACAFNwMAIANBCGogA0EQahDPhYCAACADKAIIIAMoAgwgASACEOiEgIAAGiADQRBqEL6AgIAAIANBIGokgICAgAAPC0GwgsCAAEEcQcyOwIAAEI2GgIAAAAtDAgF/AX4jgICAgABBEGsiAiSAgICAACACQgA3AwggAkEIakEIIAAgARCOhYCAACACKQMIIQMgAkEQaiSAgICAACADC9QBAQF/I4CAgIAAQTBrIgUkgICAgAACQAJAAkAgASkDACACWA0AIAVBEGogASACEPaBgIAAIAVBCGogBUEQahDPhYCAACAFKAIIIAUoAgwgAyAEEOiEgIAARQ0BIAVBIGoQ64SAgAAgBSgCIEUNAiAAIAUpAyA3AgAgAEEIaiAFQSBqQQhqKAIANgIAIAVBEGoQvoCAgAAgBUEwaiSAgICAAA8LQfiMwIAAQRMQz4SAgAAAC0HsisCAAEHmABDPhICAAAALQeyKwIAAQeYAEM+EgIAAAAujBQIBfwJ+I4CAgIAAQZABayIEJICAgIAAIARBwABqIAEgAiADEOKBgIAAIARBOGogBEHAAGoQz4WAgAAgBEHQAGogBCgCOCAEKAI8EOmEgIAAAkACQAJAIAQoAlANACAAQQA2AgAgBEHAAGoQvoCAgAAMAQsgBEHgAGpBCGogBEHQAGpBCGooAgA2AgAgBCAEKQNQNwNgAkACQAJAIAEQ44GAgABCAVENAAJAIAEQ44GAgAAiBUJ/fCIGIAVWDQAgBEGAAWogASAGEOmBgIAAIAQoAoABRQ0CIARB8ABqQQhqIARBgAFqQQhqKAIANgIAIAQgBCkDgAE3A3AgBEEwaiAEQcAAahDPhYCAACAEKAIwIAQoAjQQ6oSAgAAaIARBKGogBEHwAGoQz4WAgAACQCAEKAIoIAQoAiwgAiADEKKBgIAARQ0AIARBIGogBEHwAGoQz4WAgAAgBEGAAWogASAEKAIgIAQoAiQQ4oGAgAAgBEEYaiAEQYABahDPhYCAACAEKAIcIQIgBCgCGCEDIARBEGogBEHgAGoQz4WAgAAgAyACIAQoAhAgBCgCFBDohICAABogBEGAAWoQvoCAgAALIARB8ABqEL6AgIAADAMLQcCBwIAAQSFB3IrAgAAQjYaAgAAACyAEQQhqIARBwABqEM+FgIAAIAQoAgggBCgCDBDqhICAABoMAQtB7IrAgABB5gAQz4SAgAAACyAEIARB4ABqEM+FgIAAIARBgAFqIAEgBCgCACAEKAIEEOaBgIAAIgUQ6oGAgAAgBEGAAWoQvoCAgAAgACABQRhqIAUQ64GAgAAgBEHgAGoQvoCAgAAgBCgCUCEBIARBwABqEL6AgIAAIAENAQsgBEHQAGoQiIGAgAALIARBkAFqJICAgIAAC68BAQF/I4CAgIAAQTBrIgMkgICAgAACQAJAAkAgASkDACACWA0AIANBEGogASACEPeBgIAAIANBCGogA0EQahDPhYCAACADQSBqIAMoAgggAygCDBDphICAACADKAIgDQFB7IrAgABB5gAQz4SAgAAACyAAQQA2AgAMAQsgACADKQMgNwIAIABBCGogA0EgakEIaigCADYCACADQRBqEL6AgIAACyADQTBqJICAgIAAC4EDAwF/AX4BfyOAgICAAEHAAGsiAySAgICAAAJAIAEpAwAiBCACWA0AAkACQAJAAkACQCACQgF8IARRDQAgA0EQaiABIAIQ94GAgAAgA0EwaiABEPiBgIAAIANBIGogA0EwakHyjcCAAEEpQZyOwIAAEKmBgIAAIANBCGogA0EQahDPhYCAACADKAIMIQEgAygCCCEFIAMgA0EgahDPhYCAACAFIAEgAygCACADKAIEEOiEgIAADQFB7IrAgABB5gAQz4SAgAAACyADQTBqIAEQ+IGAgAAgAygCMA0BQeyKwIAAQeYAEM+EgIAAAAsgA0EwahDrhICAACADKAIwDQFB7IrAgABB5gAQz4SAgAAACyAAIAMpAzA3AgAgAEEIaiADQTBqQQhqKAIANgIADAELIAAgAykDMDcCACAAQQhqIANBMGpBCGooAgA2AgAgA0EgahC+gICAACADQRBqEL6AgIAACyADQcAAaiSAgICAAA8LQfiMwIAAQRMQz4SAgAAAC4EDAwF/AX4BfyOAgICAAEHAAGsiAySAgICAAAJAIAEpAwAiBCACWA0AAkACQAJAAkACQCACQgF8IARRDQAgA0EQaiABIAIQ9oGAgAAgA0EwaiABEPmBgIAAIANBIGogA0EwakHyjcCAAEEpQZyOwIAAEKmBgIAAIANBCGogA0EQahDPhYCAACADKAIMIQEgAygCCCEFIAMgA0EgahDPhYCAACAFIAEgAygCACADKAIEEOiEgIAADQFB7IrAgABB5gAQz4SAgAAACyADQTBqIAEQ+YGAgAAgAygCMA0BQeyKwIAAQeYAEM+EgIAAAAsgA0EwahDrhICAACADKAIwDQFB7IrAgABB5gAQz4SAgAAACyAAIAMpAzA3AgAgAEEIaiADQTBqQQhqKAIANgIADAELIAAgAykDMDcCACAAQQhqIANBMGpBCGooAgA2AgAgA0EgahC+gICAACADQRBqEL6AgIAACyADQcAAaiSAgICAAA8LQfiMwIAAQRMQz4SAgAAAC+kBAgF/An4jgICAgABBwABrIgQkgICAgAAgBEEQaiABIAIgAxDigYCAACAEQQhqIARBEGoQz4WAgAAgBEEgaiAEKAIIIAQoAgwQ6YSAgAACQAJAAkAgBCgCIA0AQgAhBQwBCyAEQTBqQQhqIARBIGpBCGooAgA2AgAgBCAEKQMgNwMwIAQgBEEwahDPhYCAACAEKAIAIAQoAgQQ5oGAgAAhBiAEQTBqEL6AgIAAQgEhBSAEKAIgDQELIARBIGoQiIGAgAALIARBEGoQvoCAgAAgACAGNwMIIAAgBTcDACAEQcAAaiSAgICAAAtgAQF/I4CAgIAAQRBrIgIkgICAgAAgAiABELmBgIAAAkAgAigCAEEBRw0AQdKLwIAAQR8Qz4SAgAAACyAAIAIpAgQ3AgAgAEEIaiACQQxqKAIANgIAIAJBEGokgICAgAALYAEBfyOAgICAAEEQayICJICAgIAAIAIgARC4gYCAAAJAIAIoAgBBAUcNAEHxi8CAAEEhEM+EgIAAAAsgACACKQIENwIAIABBCGogAkEMaigCADYCACACQRBqJICAgIAAC2gCAX8BfiOAgICAAEEgayIDJICAgIAAIANBCGogASACELWBgIAAAkAgAygCCEEBRw0AQZKMwIAAQSMQz4SAgAAACyADKQMQIQQgACADQRhqKQMANwMIIAAgBDcDACADQSBqJICAgIAAC4gCAgF/A34jgICAgABB0ABrIgMkgICAgAAgA0EwaiACEO2BgIAAIANBGGogA0EwahDPhYCAACADQSBqIAEgAygCGCADKAIcEPGBgIAAAkACQAJAIAMoAiANAEIAIQQMAQsgA0HAAGpBCGogA0EgakEIaigCADYCACADIAMpAyA3A0AgA0EQaiADQcAAahDPhYCAACADIAMoAhAgAygCFBDvgYCAACADQQhqKQMAIQUgAykDACEGIANBwABqEL6AgIAAQgEhBCADKAIgDQELIANBIGoQiIGAgAALIANBMGoQvoCAgAAgAEEQaiAFNwMAIAAgBjcDCCAAIAQ3AwAgA0HQAGokgICAgAALkwEBAX8jgICAgABBIGsiBCSAgICAACAEIAEgAiADEOyBgIAAAkACQAJAIAQpAwCnDQAgAEEANgIADAELIARBEGogAUEYaiAEKQMIEPWBgIAAIAQoAhBFDQEgACAEKQMQNwIAIABBCGogBEEQakEIaigCADYCAAsgBEEgaiSAgICAAA8LQeyKwIAAQeYAEM+EgIAAAAs/AQF/I4CAgIAAQRBrIgIkgICAgAAgAkEIaiABEP6BgIAAIAAgAigCCCACKAIMEIyFgIAAIAJBEGokgICAgAALzgICAn8DfiOAgICAAEHwAGsiBCSAgICAACAEQcAAaiACEO2BgIAAIARBKGogBEHAAGoQz4WAgAAgBCgCLCECIAQoAighBSAEQdAAaiADEO6BgIAAIARBIGogBEHQAGoQz4WAgAAgBEEwaiABIAUgAiAEKAIgIAQoAiQQ4YGAgAACQAJAAkAgBCgCMA0AQgAhBgwBCyAEQeAAakEIaiAEQTBqQQhqKAIANgIAIAQgBCkDMDcDYCAEQRhqIARB4ABqEM+FgIAAIARBCGogBCgCGCAEKAIcEO+BgIAAIARBCGpBCGopAwAhByAEKQMIIQggBEHgAGoQvoCAgABCASEGIAQoAjANAQsgBEEwahCIgYCAAAsgBEHQAGoQvoCAgAAgBEHAAGoQvoCAgAAgAEEQaiAHNwMAIAAgCDcDCCAAIAY3AwAgBEHwAGokgICAgAALiAICAX8DfiOAgICAAEHQAGsiAySAgICAACADQTBqIAIQ7YGAgAAgA0EYaiADQTBqEM+FgIAAIANBIGogASADKAIYIAMoAhwQ6IGAgAACQAJAAkAgAygCIA0AQgAhBAwBCyADQcAAakEIaiADQSBqQQhqKAIANgIAIAMgAykDIDcDQCADQRBqIANBwABqEM+FgIAAIAMgAygCECADKAIUEO+BgIAAIANBCGopAwAhBSADKQMAIQYgA0HAAGoQvoCAgABCASEEIAMoAiANAQsgA0EgahCIgYCAAAsgA0EwahC+gICAACAAQRBqIAU3AwAgACAGNwMIIAAgBDcDACADQdAAaiSAgICAAAuvAQEBfyOAgICAAEEwayIDJICAgIAAAkACQAJAIAEpAwAgAlgNACADQRBqIAEgAhD2gYCAACADQQhqIANBEGoQz4WAgAAgA0EgaiADKAIIIAMoAgwQ6YSAgAAgAygCIA0BQeyKwIAAQeYAEM+EgIAAAAsgAEEANgIADAELIAAgAykDIDcCACAAQQhqIANBIGpBCGooAgA2AgAgA0EQahC+gICAAAsgA0EwaiSAgICAAAuyAQEDfyOAgICAAEEgayIDJICAgIAAAkAgAUEQaigCACIEQQhqIgUgBEkNACADQQhqIAUQ6oCAgAAgAyABQQhqEM+FgIAAIANBCGogAygCACADKAIEEJiFgIAAIAMgAjcDGCADQQhqIANBGGpBCBCYhYCAACAAQQhqIANBCGpBCGooAgA2AgAgACADKQMINwIAIANBIGokgICAgAAPC0GwgsCAAEEcQayOwIAAEI2GgIAAAAuyAQEDfyOAgICAAEEgayIDJICAgIAAAkAgAUEQaigCACIEQQhqIgUgBEkNACADQQhqIAUQ6oCAgAAgAyABQQhqEM+FgIAAIANBCGogAygCACADKAIEEJiFgIAAIAMgAjcDGCADQQhqIANBGGpBCBCYhYCAACAAQQhqIANBCGpBCGooAgA2AgAgACADKQMINwIAIANBIGokgICAgAAPC0GwgsCAAEEcQayOwIAAEI2GgIAAAAuEAgIBfwJ+I4CAgIAAQTBrIgIkgICAgAACQAJAAkACQAJAIAEpAwAiA0IAUg0AIABBADYCAAwBCyACQRBqIAEgA0J/fBD3gYCAACABKQMAIgNCf3wiBCADVg0BIAEgBDcDACACQQhqIAJBEGoQz4WAgAAgAigCCCACKAIMEOqEgIAARQ0CIAJBIGoQ64SAgAAgAigCIEUNAyAAIAIpAyA3AgAgAEEIaiACQSBqQQhqKAIANgIAIAJBEGoQvoCAgAALIAJBMGokgICAgAAPC0HAgcCAAEEhQbyOwIAAEI2GgIAAAAtB7IrAgABB5gAQz4SAgAAAC0HsisCAAEHmABDPhICAAAALhAICAX8CfiOAgICAAEEwayICJICAgIAAAkACQAJAAkACQCABKQMAIgNCAFINACAAQQA2AgAMAQsgAkEQaiABIANCf3wQ9oGAgAAgASkDACIDQn98IgQgA1YNASABIAQ3AwAgAkEIaiACQRBqEM+FgIAAIAIoAgggAigCDBDqhICAAEUNAiACQSBqEOuEgIAAIAIoAiBFDQMgACACKQMgNwIAIABBCGogAkEgakEIaigCADYCACACQRBqEL6AgIAACyACQTBqJICAgIAADwtBwIHAgABBIUG8jsCAABCNhoCAAAALQeyKwIAAQeYAEM+EgIAAAAtB7IrAgABB5gAQz4SAgAAACwQAQQALBABBAAv9AQEBfyOAgICAAEGAAWsiASSAgICAACABQRBqQdyOwIAAQQUQ6YSAgAACQAJAAkACQCABKAIQDQAgAEEANgIIDAELIAFBIGpBCGogAUEQakEIaigCADYCACABIAEpAxA3AyAgAUEIaiABQSBqEM+FgIAAIAFBMGogASgCCCABKAIMELOBgIAAIAEoAjBBAUYNAiAAIAFBMGpBCGpBwAAQ84aAgAAaIAFBIGoQvoCAgAAgASgCEA0BCyABQRBqEIiBgIAACyABQYABaiSAgICAAA8LIAEgASkCNDcDeEHhjsCAAEEmIAFB+ABqQaiFwIAAQeyPwIAAEKqGgIAAAAuyAQEBfyOAgICAAEEwayIBJICAgIAAIAFBIGogABC2gYCAAAJAIAEoAiBBAUcNACABIAEpAiQ3AxBB/I/AgABBJCABQRBqQaiFwIAAQaCQwIAAEKqGgIAAAAsgAUEYaiABQSxqKAIANgIAIAEgASkCJDcDECABQQhqIAFBEGoQz4WAgABB3I7AgABBBSABKAIIIAEoAgwQ6ISAgAAaIAFBEGoQvoCAgAAgAUEwaiSAgICAAAtKAQJ/I4CAgIAAQRBrIgIkgICAgAAgAkEIaiABEM+FgIAAIAIoAgwhASAAIAIoAggiAzYCACAAIAMgAWo2AgQgAkEQaiSAgICAAAu6AgIBfwF+I4CAgIAAQSBrIgMkgICAgAAgA0EYaiABQTBqIAIQuoGAgAAgAyADKQMYIgQ3AwgCQAJAAkACQCAEp0H/AXFBA0cNACADQQhqEOWAgIAAIANBGGogASACEICCgIAAIAMgAykDGCIENwMIIASnQf8BcUEDRw0BIANBCGoQ5YCAgAAgA0EYaiABQRhqIAIQgYKAgAAgAyADKQMYIgQ3AwggBKdB/wFxQQNHDQIgA0EIahDlgICAACAAQQM6AAAMAwsgAyAENwMYIANBEGogA0EYahDAhYCAACAAIAMpAxA3AgAMAgsgAyAENwMYIANBEGogA0EYahDAhYCAACAAIAMpAxA3AgAMAQsgAyAENwMYIANBEGogA0EYahDAhYCAACAAIAMpAxA3AgALIANBIGokgICAgAAL3wECAX8BfiOAgICAAEEgayIDJICAgIAAIANBGGogASACEK2BgIAAIAMgAykDGCIENwMIAkACQAJAIASnQf8BcUEDRw0AIANBCGoQ5YCAgAAgA0EYaiABQQhqIAIQuoGAgAAgAyADKQMYIgQ3AwggBKdB/wFxQQNHDQEgA0EIahDlgICAACAAQQM6AAAMAgsgAyAENwMYIANBEGogA0EYahDAhYCAACAAIAMpAxA3AgAMAQsgAyAENwMYIANBEGogA0EYahDAhYCAACAAIAMpAxA3AgALIANBIGokgICAgAAL3wECAX8BfiOAgICAAEEgayIDJICAgIAAIANBGGogASACEK2BgIAAIAMgAykDGCIENwMIAkACQAJAIASnQf8BcUEDRw0AIANBCGoQ5YCAgAAgA0EYaiABQQhqIAIQuoGAgAAgAyADKQMYIgQ3AwggBKdB/wFxQQNHDQEgA0EIahDlgICAACAAQQM6AAAMAgsgAyAENwMYIANBEGogA0EYahDAhYCAACAAIAMpAxA3AgAMAQsgAyAENwMYIANBEGogA0EYahDAhYCAACAAIAMpAxA3AgALIANBIGokgICAgAALlAUBA38jgICAgABB0AFrIgIkgICAgAAgAkHYAGogARDHgYCAAAJAAkACQAJAAkACQAJAIAIoAlhBAUYNACACQcgAakEIaiACQeQAaigCADYCACACIAIpAlw3A0ggAkGAAWogARCDgoCAACACQdgAakEEciEDIAIoAoABQQFGDQEgAkHoAGpBEGoiBCACQYABakEYaikDADcDACACQegAakEIaiACQYABakEQaikDADcDACACIAIpA4gBNwNoIAJBoAFqIAEQhIKAgAAgAigCoAFBAUcNAyACIAIpAqQBNwPIASACQcABaiACQcgBahDAhYCAACAAIAIpA8ABNwIEIABBATYCACACQfAAahC+gICAACACQcgAahC+gICAACACKAKAAQ0CDAQLIAIgAikCXDcDoAEgAkGAAWogAkGgAWoQwIWAgAAgACACKQOAATcCBCAAQQE2AgAMBQsgAiACKQKEATcDoAEgAkHIAWogAkGgAWoQwIWAgAAgACACKQPIATcCBCAAQQE2AgAgAkHIAGoQvoCAgAAgAigCWEUNBAwDCyACQYABakEEchCNgYCAAAwBCyACQcAAaiACQcgAakEIaigCADYCACACQQhqQQhqIAJB6ABqQQhqKQMANwMAIAJBCGpBEGogBCkDADcDACACQShqIAJBoAFqQRBqKQMANwMAIAJBMGogAkGgAWpBGGopAwA3AwAgAiACKQNINwM4IAIgAikDaDcDCCACIAIpA6gBNwMgIABBCGogAkEIakHAABDzhoCAABogAEEANgIAAkAgAigCgAFFDQAgAkGAAWpBBHIQjYGAgAALIAIoAlgNAQwCCyACKAJYRQ0BCyADEI2BgIAACyACQdABaiSAgICAAAv4AQIBfwF+I4CAgIAAQTBrIgIkgICAgAAgAiABEK6BgIAAAkACQCACKAIAQQFGDQAgAikDCCEDIAJBEGogARDHgYCAAAJAAkAgAigCEEEBRw0AIAIgAikCFDcDKCACQSBqIAJBKGoQwIWAgAAgACACKQMgNwIEIABBATYCAAwBCyAAQQA2AgAgAEEIaiADNwMAIABBEGogAkEQakEEciIBKQIANwIAIABBGGogAUEIaigCADYCAAsgAhCQgYCAAAwBCyACIAIpAgQ3AxAgAkEoaiACQRBqEMCFgIAAIAAgAikDKDcCBCAAQQE2AgALIAJBMGokgICAgAAL+AECAX8BfiOAgICAAEEwayICJICAgIAAIAIgARCugYCAAAJAAkAgAigCAEEBRg0AIAIpAwghAyACQRBqIAEQx4GAgAACQAJAIAIoAhBBAUcNACACIAIpAhQ3AyggAkEgaiACQShqEMCFgIAAIAAgAikDIDcCBCAAQQE2AgAMAQsgAEEANgIAIABBCGogAzcDACAAQRBqIAJBEGpBBHIiASkCADcCACAAQRhqIAFBCGooAgA2AgALIAIQkIGAgAAMAQsgAiACKQIENwMQIAJBKGogAkEQahDAhYCAACAAIAIpAyg3AgQgAEEBNgIACyACQTBqJICAgIAAC+IRBQN/AX4BfwJ+A38jgICAgABBoAFrIgYkgICAgAAgBkEIaiABEM2AgIAAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAGLQAIQQFGDQAgBi0ACUEBRw0BIAYtAAohByAGQQhqEMiAgIAAAkACQCAHQdsARg0AIAdB+wBGDQEgASAGQZgBakHAkMCAABDggICAACEHIAZBATYCCCAGIAc2AgwMEAsgAS0AGEF/aiIHQf8BcSIIIAdHDQQgASAHOgAYAkAgCA0AIAZBiAFqIQcMEQsgARDQgICAACAGIAE2AnAgBkEBOgB0IAZBwABqIAZB8ABqEN2BgIAAAkACQCAGKAJAQQFGDQAgBkGIAWpBCGoiByAGQcwAaigCADYCACAGIAYpAkQiCTcDiAEgCacNAUEAQdSbwIAAQYybwIAAELuBgIAAIQogBkGIAWoQlYGAgAAMDwsgBigCRCEKDA4LIAZB+ABqQQhqIAcoAgA2AgAgBiAGKQOIATcDeCAGQcAAaiAGQfAAahDbgYCAACAGKAJAQQFHDQsgBigCRCEKDAwLIAEtABhBf2oiB0H/AXEiCCAHRw0CIAEgBzoAGAJAIAgNACAGQcAAaiEHDBALIAEQ0ICAgAAgBkEBOgB0IAYgATYCcCAGQQA2AnggBkHAAGpBBHIhCEIAIQkgBkHQAGohCgJAA0AgBkGIAWogBkHwAGoQyoGAgAACQAJAIAYtAIgBQQFGDQAgBi0AiQEiB0EDRg0DAkAgB0EBSw0AAkACQCAHDgIAAQALAkAgBigCeA0AIAZBwABqIAZB8ABqENSBgIAAIAYoAkBBAUcNBAwNC0Hcm8CAAEEWELyBgIAAIQcMDQsgCUIBUQ0JIAZBwABqIAZB8ABqENeBgIAAIAYoAkBBAUYNCyAKKQMAIQsgBikDSCEMIAZBwABqEJaBgIAAQgEhCQwDCyAGIAZB8ABqENmBgIAAIgc2AkAgBw0LIAZBwABqEJOBgIAADAILIAYoAowBIQcMCgsgBkH4AGoQlYGAgAAgBkH4AGpBCGogCEEIaigCADYCACAGIAgpAgA3A3gMAAsLIAYoAngNBUHcm8CAAEEWEPOAgIAAIQcMBwsgACAGKAIMNgIEIABBATYCAAwPCyAGQQU2AkAgASAGQcAAahDFgICAACEBIABBATYCACAAIAE2AgQgBkEIahDIgICAAAwOC0HAgcCAAEEhQdCQwIAAEI2GgIAAAAtBwIHAgABBIUHQkMCAABCNhoCAAAALQeaTwIAAQQYQvIGAgAAhBwwCCyAGQYgBakEIaiIHIAZB+ABqQQhqKAIANgIAIAYgBikDeDcDiAECQCAJQgFSDQAgBkEwakEIaiAHKAIANgIAIAYgBikDiAE3AzBBACEKDAQLIAZBwABqQeaTwIAAQQYQw4GAgAACQCAGKAJAQQFGDQAgBkHQAGopAwAhCyAGKQNIIQwgBkHAAGoQloGAgAAgBkEwakEIaiAGQYgBakEIaigCADYCACAGIAYpA4gBNwMwQQAhCgJAIAYoAngNACAGQfgAahCVgYCAAAsMBAsgBigCRCEHIAZBiAFqEL6AgIAAQQAhCAwCCyAGKAJEIQcLQQEhCAsCQAJAAkAgBigCeEUNAEEBIQogCA0BDAILIAZB+ABqEJWBgIAAQQEhCgwBCyAGQfgAahC+gICAAAsLAkACQAJAIAEtABhBAWoiCEH/AXEgCEcNACABIAg6ABggARDigICAACEIIAZB0ABqIAs3AwAgBkHAAGpBCGoiDSAMNwMAIAZB2ABqIg4gBikDMDcDACAGQeAAaiAGQTBqQQhqKAIANgIAIAYgBzYCRCAGIAo2AkAgBiAINgJoIAZB6ABqIQ8CQCAKDQACQCAIDQAgBkEoaiANQRhqKQMANwMAIAZBCGpBGGogDUEQaikDADcDACAGQQhqQRBqIA1BCGopAwA3AwAgBkEANgIIIAYgDSkDADcDEAwECyAGQQE2AgggBiAINgIMIA4QvoCAgABBACEKIAYoAmghCAwCC0EBIQogBkEBNgIIIAYgBzYCDAwBC0GwgsCAAEEcQeCQwIAAEI2GgIAAAAsgCEUNACAKRQ0FIA8Q2ICAgAAMBQsgDxC8gICAAAwECwJAIAYpA0hCAVINACAGQdgAaikDACEJIAZB0ABqKQMAIQwgBkHAAGoQl4GAgAAgBkEwakEIaiAGQfgAakEIaigCADYCACAGIAYpA3g3AzBBACEIDAMLQQFB1JvAgABBjJvAgAAQu4GAgAAhCiAGQcAAahCXgYCAAAsgBkH4AGoQvoCAgAALQQEhCAsCQCABLQAYQQFqIgdB/wFxIAdGDQBBsILAgABBHEHgkMCAABCNhoCAAAALIAEgBzoAGCABEOOAgIAAIQcgBkHQAGogCTcDACAGQcAAakEIaiINIAw3AwAgBkHYAGoiDiAGKQMwNwMAIAZB4ABqIAZBMGpBCGooAgA2AgAgBiAKNgJEIAYgCDYCQCAGIAc2AmggBkHoAGohDwJAAkACQCAIDQACQCAHDQAgBkEoaiANQRhqKQMANwMAIAZBCGpBGGogDUEQaikDADcDACAGQQhqQRBqIA1BCGopAwA3AwAgBkEANgIIIAYgDSkDADcDEAwDCyAGQQE2AgggBiAHNgIMIA4QvoCAgABBACEIIAYoAmghBwwBC0EBIQggBkEBNgIIIAYgCjYCDAsgB0UNACAIRQ0BIA8Q2ICAgAAMAQsgDxC8gICAAAsCQAJAIAYoAghBAUcNACAAIAYoAgwgARDKgICAADYCBCAAQQE2AgAgBigCCA0BIAZBIGoQvoCAgAAMAQsgAEEANgIAIABBCGogBikDEDcDACAAQSBqIAZBCGpBIGopAwA3AwAgAEEYaiAGQQhqQRhqKQMANwMAIABBEGogBkEIakEQaikDADcDAAsgBkGgAWokgICAgAAPCyAHQRU2AgAgASAHEMWAgIAAIQEgAEEBNgIAIAAgATYCBAsgBkGgAWokgICAgAALvAsDA38BfgJ/I4CAgIAAQdAAayIGJICAgIAAIAYgARDNgICAAAJAAkACQAJAAkACQAJAAkACQAJAAkAgBi0AAEEBRg0AIAYtAAFBAUcNASAGLQACIQcgBhDIgICAAAJAAkAgB0HbAEYNACAHQfsARg0BIAEgBkHIAGpB8JDAgAAQ4ICAgAAhByAGQQE2AgAgBiAHNgIEDAoLIAEtABhBf2oiB0H/AXEiCCAHRw0EIAEgBzoAGAJAIAgNACAGQTBqIQcMCwsgARDQgICAACAGIAE2AkBBASEIIAZBAToARCAGQRBqIAZBwABqEN2BgIAAIAYoAhBBAUYNBSAGQThqIAZBHGooAgA2AgAgBiAGKQIUIgk3AzACQCAJpyIKDQBBAEGEm8CAAEGMm8CAABC7gYCAACEKIAZBMGoQlYGAgABBASEIDAkLQQAhCCAGKQI0IQkMCAsgAS0AGEF/aiIHQf8BcSIIIAdHDQIgASAHOgAYAkAgCA0AIAZBEGohBwwKCyABENCAgIAAIAZBAToALCAGIAE2AiggBkEANgIwIAZBEGpBBHIhCAJAA0AgBkHAAGogBkEoahDOgYCAAAJAAkAgBi0AQEEBRg0AIAYtAEEiB0ECRw0BIAYoAjAiBw0DQdyTwIAAQQoQ84CAgAAhBwwJCyAGKAJEIQcMCAsCQCAHQQFxRQ0AIAYgBkEoahDZgYCAACIHNgIQIAcNCCAGQRBqEJOBgIAADAELAkACQCAGKAIwDQAgBkEQaiAGQShqENSBgIAAIAYoAhBBAUcNASAGKAIUIQcMCQtB3JPAgABBChC8gYCAACEHDAgLIAZBMGoQlYGAgAAgBkEwakEIaiAIQQhqKAIANgIAIAYgCCkCADcDMAwACwtBACEKIAYpAjQhCQwGCyAAIAYoAgQ2AgQgAEEBNgIADAkLIAZBBTYCECABIAZBEGoQxYCAgAAhASAAQQE2AgAgACABNgIEIAYQyICAgAAMCAtBwIHAgABBIUHQkMCAABCNhoCAAAALQcCBwIAAQSFB0JDAgAAQjYaAgAAACyAGKAIUIQoMAgsgBkEwahCVgYCAAEEBIQoLAkACQAJAIAEtABhBAWoiCEH/AXEgCEcNACABIAg6ABggARDigICAACEIIAZBGGogCTcDACAGIAg2AiAgBiAHNgIUIAYgCjYCECAGQSBqIQsCQCAKDQACQCAIDQAgBkEMaiAGQRBqQQxqKAIANgIAIAYgBikCFDcCBCAGQQA2AgAMBAsgBkEBNgIAIAYgCDYCBCAGQRBqQQRyEL6AgIAAQQAhCiAGKAIgIQgMAgtBASEKIAZBATYCACAGIAc2AgQMAQtBsILAgABBHEHgkMCAABCNhoCAAAALIAhFDQAgCkUNAiALENiAgIAADAILIAsQvICAgAAMAQsCQAJAAkAgAS0AGEEBaiIHQf8BcSAHRw0AIAEgBzoAGCABEOOAgIAAIQcgBkEYaiAJNwMAIAYgBzYCICAGIAo2AhQgBiAINgIQIAZBIGohCwJAIAgNAAJAIAcNACAGQQxqIAZBEGpBDGooAgA2AgAgBiAGKQIUNwIEIAZBADYCAAwECyAGQQE2AgAgBiAHNgIEIAZBEGpBBHIQvoCAgABBACEIIAYoAiAhBwwCC0EBIQggBkEBNgIAIAYgCjYCBAwBC0GwgsCAAEEcQeCQwIAAEI2GgIAAAAsgB0UNACAIRQ0BIAsQ2ICAgAAMAQsgCxC8gICAAAsCQAJAIAYoAgBBAUcNACAAIAYoAgQgARDKgICAADYCBCAAQQE2AgAgBigCAA0BIAZBBHIQvoCAgAAMAQsgAEEANgIAIAAgBkEEciIBKQIANwIEIABBDGogAUEIaigCADYCAAsgBkHQAGokgICAgAAPCyAHQRU2AgAgASAHEMWAgIAAIQEgAEEBNgIAIAAgATYCBAsgBkHQAGokgICAgAALoQoEA38CfgJ/AX4jgICAgABBwABrIgYkgICAgAAgBkEwaiABEM2AgIAAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgBi0AMEEBRg0AIAYtADFBAUcNASAGLQAyIQcgBkEwahDIgICAAAJAAkAgB0HbAEYNACAHQfsARg0BIAEgBkE4akGAkcCAABDggICAACEHDBMLIAEtABhBf2oiB0H/AXEiCCAHRw0GIAEgBzoAGCAIRQ0IIAEQ0ICAgAAgBiABNgIwQQEhCCAGQQE6ADQgBkEIaiAGQTBqENuBgIAAAkACQAJAAkAgBigCCEEBRg0AIAYpAxBCAVINASAGQSBqKQMAIQkgBkEYaikDACEKIAZBCGoQl4GAgABBACEIDAMLIAYoAgwhBwwBC0EAQYSbwIAAQYybwIAAELuBgIAAIQcgBkEIahCXgYCAAEEBIQgLCyABLQAYQQFqIgtB/wFxIAtHDQcgASALOgAYIAEQ44CAgAAhCyAGQRhqIAk3AwAgBkEQaiAKNwMAIAYgCzYCICAGIAc2AgwgBiAINgIIIAZBIGohDCAIDQRBASEIIAtFDQMgCyEHDBELIAEtABhBf2oiB0H/AXEiCCAHRw0EIAEgBzoAGAJAIAhFDQAgARDQgICAACAGQQE6ACwgBiABNgIoQgAhDSAGQRhqIQgCQAJAA0AgBkEwaiAGQShqEMiBgIAAIAYtADBBAUYNASAGLQAxIgdBAkYNAgJAIAdBAXFFDQAgBiAGQShqENmBgIAAIgc2AgggBw0TIAZBCGoQk4GAgAAMAQsgDUIBUQ0MIAZBCGogBkEoahDXgYCAACAGKAIIQQFGDREgCCkDACEJIAYpAxAhCiAGQQhqEJaBgIAAQgEhDQwACwsgBigCNCEHDBALQQAhCCANQgFSDQ0MEAsgBkEVNgIIIAEgBkEIahDFgICAACEBIABBATYCACAAIAE2AgQMCwsgACAGKAI0NgIEIABBATYCAAwKCyAGQQU2AgggASAGQQhqEMWAgIAAIQEgAEEBNgIAIAAgATYCBCAGQTBqEMiAgIAADAkLQQAhCAwGCyALDQZBASEIDAULQcCBwIAAQSFB0JDAgAAQjYaAgAAAC0HAgcCAAEEhQdCQwIAAEI2GgIAAAAtBsILAgABBHEHgkMCAABCNhoCAAAALIAZBFTYCCCABIAZBCGoQxYCAgAAhASAAQQE2AgAgACABNgIEDAMLQeaTwIAAQQYQvIGAgAAhBwwFCyAMELyAgIAADAYLIAwQ2ICAgABBASEIDAULIAZBwABqJICAgIAADwsgBkEIakHmk8CAAEEGEMOBgIAAIAYoAghBAUYNACAGQRhqKQMAIQkgBikDECEKIAZBCGoQloGAgAAMAgsgBigCDCEHC0EBIQgLAkAgAS0AGEEBaiILQf8BcSALRg0AQbCCwIAAQRxB4JDAgAAQjYaAgAAACyABIAs6ABggARDigICAACELIAZBGGogCTcDACAGQRBqIAo3AwAgBiALNgIgIAYgBzYCDCAGIAg2AgggBkEgaiEMAkACQAJAIAgNAEEBIQgCQCALRQ0AIAshBwwEC0EAIQgMAQsgCw0BQQEhCAsgDBC8gICAAAwBCyAMENiAgIAAQQEhCAsgCA0AIABBEGogCTcDACAAQQhqIAo3AwBBACEBDAELIAAgByABEMqAgIAANgIEQQEhAQsgACABNgIAIAZBwABqJICAgIAAC4UMAwN/AX4CfyOAgICAAEHQAGsiBiSAgICAACAGIAEQzYCAgAACQAJAAkACQAJAAkACQAJAAkACQAJAIAYtAABBAUYNACAGLQABQQFHDQEgBi0AAiEHIAYQyICAgAACQAJAIAdB2wBGDQAgB0H7AEYNASABIAZByABqQZCRwIAAEOCAgIAAIQcgBkEBNgIAIAYgBzYCBAwKCyABLQAYQX9qIgdB/wFxIgggB0cNBCABIAc6ABgCQCAIDQAgBkEwaiEHDAsLIAEQ0ICAgAAgBiABNgJAQQEhCCAGQQE6AEQgBkEQaiAGQcAAahDcgYCAACAGKAIQQQFGDQUgBkE4aiAGQRxqKAIANgIAIAYgBikCFCIJNwMwAkAgCaciCg0AQQBBhJvAgABBjJvAgAAQu4GAgAAhCiAGQTBqEJ2BgIAAQQEhCAwJC0EAIQggBikCNCEJDAgLIAEtABhBf2oiB0H/AXEiCCAHRw0CIAEgBzoAGAJAIAgNACAGQRBqIQcMCgsgARDQgICAACAGQQE6ACwgBiABNgIoIAZBADYCMCAGQRBqQQRyIQgCQAJAA0AgBkHAAGogBkEoahDMgYCAAAJAAkACQAJAIAYtAEBBAUYNACAGLQBBIgdBAkcNASAGKAIwIgdFDQJBACEKIAYpAjQhCQwNCyAGKAJEIQcMCwsCQCAHQQFxRQ0AIAYgBkEoahDZgYCAACIHNgIQIAcNCyAGQRBqEJOBgIAADAMLAkAgBigCMA0AIAZBEGogBkEoahDVgYCAACAGKAIQQQFHDQIgBigCFCEHDAsLQd+awIAAQQoQvIGAgAAhBwwKCyAGQRBqQd+awIAAQQoQxIGAgAAgBigCFCEHIAYoAhBBAUYNCSAGQRhqKQMAIQkgBigCMEUNAiAGQTBqEL6AgIAADAMLIAZBMGoQnYGAgAAgBkEwakEIaiAIQQhqKAIANgIAIAYgCCkCADcDMAwACwsgBkEwahCdgYCAAAtBACEKDAYLIAAgBigCBDYCBCAAQQE2AgAMCQsgBkEFNgIQIAEgBkEQahDFgICAACEBIABBATYCACAAIAE2AgQgBhDIgICAAAwIC0HAgcCAAEEhQdCQwIAAEI2GgIAAAAtBwIHAgABBIUHQkMCAABCNhoCAAAALIAYoAhQhCgwCCyAGQTBqEJ2BgIAAQQEhCgsCQAJAAkAgAS0AGEEBaiIIQf8BcSAIRw0AIAEgCDoAGCABEOKAgIAAIQggBkEYaiAJNwMAIAYgCDYCICAGIAc2AhQgBiAKNgIQIAZBIGohCwJAIAoNAAJAIAgNACAGQQxqIAZBEGpBDGooAgA2AgAgBiAGKQIUNwIEIAZBADYCAAwECyAGQQE2AgAgBiAINgIEIAZBEGpBBHIQvoCAgABBACEKIAYoAiAhCAwCC0EBIQogBkEBNgIAIAYgBzYCBAwBC0GwgsCAAEEcQeCQwIAAEI2GgIAAAAsgCEUNACAKRQ0CIAsQ2ICAgAAMAgsgCxC8gICAAAwBCwJAAkACQCABLQAYQQFqIgdB/wFxIAdHDQAgASAHOgAYIAEQ44CAgAAhByAGQRhqIAk3AwAgBiAHNgIgIAYgCjYCFCAGIAg2AhAgBkEgaiELAkAgCA0AAkAgBw0AIAZBDGogBkEQakEMaigCADYCACAGIAYpAhQ3AgQgBkEANgIADAQLIAZBATYCACAGIAc2AgQgBkEQakEEchC+gICAAEEAIQggBigCICEHDAILQQEhCCAGQQE2AgAgBiAKNgIEDAELQbCCwIAAQRxB4JDAgAAQjYaAgAAACyAHRQ0AIAhFDQEgCxDYgICAAAwBCyALELyAgIAACwJAAkAgBigCAEEBRw0AIAAgBigCBCABEMqAgIAANgIEIABBATYCACAGKAIADQEgBkEEchC+gICAAAwBCyAAQQA2AgAgACAGQQRyIgEpAgA3AgQgAEEMaiABQQhqKAIANgIACyAGQdAAaiSAgICAAA8LIAdBFTYCACABIAcQxYCAgAAhASAAQQE2AgAgACABNgIECyAGQdAAaiSAgICAAAuREwMDfwF+A38jgICAgABB4AFrIgYkgICAgAAgBkEIaiABEM2AgIAAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAYtAAhBAUYNACAGLQAJQQFHDQEgBi0ACiEHIAZBCGoQyICAgAACQAJAIAdB2wBGDQAgB0H7AEYNASABIAZB2AFqQaCRwIAAEOCAgIAAIQcgBkEBNgIIIAYgBzYCDAwQCyABLQAYQX9qIgdB/wFxIgggB0cNBCABIAc6ABgCQCAIDQAgBkEoaiEHDA4LIAEQ0ICAgAAgBiABNgKAASAGQQE6AIQBIAZBwABqIAZBgAFqEN2BgIAAIAYoAkBBAUYNBSAGQbABakEIaiIHIAZBzABqKAIANgIAIAYgBikCRCIJNwOwAQJAIAmnDQBBAEHUm8CAAEGMm8CAABC7gYCAACEKIAZBsAFqEJWBgIAADAwLIAZBkAFqQQhqIAcoAgA2AgAgBiAGKQOwATcDkAEgBkHAAGogBkGAAWoQ3IGAgAACQAJAAkAgBigCQEEBRw0AIAYoAkQhCgwBCyAGQbABakEIaiIHIAZBzABqKAIANgIAIAYgBikCRCIJNwOwASAJpw0BQQFB1JvAgABBjJvAgAAQu4GAgAAhCiAGQbABahCdgYCAAAsgBkGQAWoQvoCAgAAMDAsgBkGgAWpBCGogBygCACIHNgIAIAZBKGpBCGogBikDsAE3AwAgBkE4aiAHNgIAIAYgBikClAE3AyggBigCkAEhCkEAIQcMDAsgAS0AGEF/aiIHQf8BcSIIIAdHDQIgASAHOgAYAkAgCA0AIAZBwABqIQcMDQsgARDQgICAACAGQQE6AHwgBiABNgJ4IAZBADYCgAEgBkEANgKQASAGQcAAakEEciEIAkADQCAGQbABaiAGQfgAahDSgYCAAAJAAkAgBi0AsAFBAUYNACAGLQCxASIHQQNGDQMCQCAHQQFLDQACQAJAIAcOAgABAAsCQCAGKAKAAQ0AIAZBwABqIAZB+ABqENSBgIAAIAYoAkBBAUcNBAwNC0Gcm8CAAEEOELyBgIAAIQcMDQsCQCAGKAKQAQ0AIAZBwABqIAZB+ABqENWBgIAAIAYoAkBBAUYNDCAGQZABahCdgYCAACAGQZABakEIaiAIQQhqKAIANgIAIAYgCCkCADcDkAEMBAtBqpvAgABBDhC8gYCAACEHDAwLIAYgBkH4AGoQ2YGAgAAiBzYCQCAHDQsgBkHAAGoQk4GAgAAMAgsgBigCtAEhBwwKCyAGQYABahCVgYCAACAGQYABakEIaiAIQQhqKAIANgIAIAYgCCkCADcDgAEMAAsLIAYoAoABDQVBnJvAgABBDhDzgICAACEHDAcLIAAgBigCDDYCBCAAQQE2AgAMDAsgBkEFNgJAIAEgBkHAAGoQxYCAgAAhASAAQQE2AgAgACABNgIEIAZBCGoQyICAgAAMCwtBwIHAgABBIUHQkMCAABCNhoCAAAALQcCBwIAAQSFB0JDAgAAQjYaAgAAACyAGKAJEIQoMBQsgBkGgAWpBCGogBkGAAWpBCGooAgA2AgAgBiAGKQOAATcDoAECQAJAAkACQAJAIAYoApABIggNACAGQcAAakGqm8CAAEEOEMSBgIAAQQEhCiAGKAJAQQFGDQIgBkG4AWogBkHMAGooAgA2AgAgBiAGKQJENwOwASAGKAKQASEIDAELIAZBsAFqQQhqIAZBkAFqQQhqKAIANgIAIAYgBikDkAE3A7ABQQAhCgsgBkEoakEIaiAGKQOwATcDACAGQThqIAZBsAFqQQhqKAIANgIAIAYgBikCpAE3AyggBigCoAEhByAIRQ0BIApFDQIgBkGQAWoQvoCAgAAMAgsgBigCRCEHIAZBoAFqEL6AgIAAQQAhCgwECyAGQZABahCdgYCAAAtBACEIIAYoAoABDQMgBkGAAWoQlYGAgAAMAwsgBigCRCEHC0EBIQoLIAZBkAFqEJ2BgIAAAkAgBigCgAFFDQBBASEIIApFDQEgBkGAAWoQvoCAgAAMAQsgBkGAAWoQlYGAgABBASEICwJAAkAgAS0AGEEBaiIKQf8BcSAKRw0AIAEgCjoAGCABEOKAgIAAIQogBkHAAGpBCGogBikDKDcDACAGQcAAakEQaiAGQShqQQhqKQMANwMAIAZB2ABqIAZBKGpBEGooAgA2AgAgBiAHNgJEIAYgCDYCQCAGIAo2AlwCQAJAAkACQCAIDQAgCkUNAkEAIQtBASEMDAELQQEhC0EAIQwgByEKCyAGQQE2AgggBiAKNgIMIAgNASAGQcAAakEEchDBgICAAAwDCyAGQQhqQRRqIAZBwABqQRRqKQIANwIAIAZBCGpBDGogBkHAAGpBDGopAgA3AgAgBiAGKQJENwIMIAZBADYCCEEBIQsMAgsgDEUNASAGQcAAakEEchDYgICAAAwBC0GwgsCAAEEcQeCQwIAAEI2GgIAAAAsgBkHcAGohBwJAIAYoAlwNACAHELyAgIAADAULIAtFDQQgBxDYgICAAAwEC0EBIQcLAkACQCABLQAYQQFqIghB/wFxIAhHDQAgASAIOgAYIAEQ44CAgAAhCCAGQcAAakEIaiAGKQMoNwMAIAZBwABqQRBqIAZBKGpBCGopAwA3AwAgBkHYAGogBkEoakEQaigCADYCACAGIAo2AkQgBiAHNgJAIAYgCDYCXAJAAkACQAJAIAcNACAIRQ0CQQAhC0EBIQwMAQtBASELQQAhDCAKIQgLIAZBATYCCCAGIAg2AgwgBw0BIAZBwABqQQRyEMGAgIAADAMLIAZBCGpBFGogBkHAAGpBFGopAgA3AgAgBkEIakEMaiAGQcAAakEMaikCADcCACAGIAYpAkQ3AgwgBkEANgIIQQEhCwwCCyAMRQ0BIAZBwABqQQRyENiAgIAADAELQbCCwIAAQRxB4JDAgAAQjYaAgAAACyAGQdwAaiEHAkAgBigCXA0AIAcQvICAgAAMAwsgC0UNAiAHENiAgIAADAILIAdBFTYCACABIAcQxYCAgAAhASAAQQE2AgAgACABNgIECyAGQeABaiSAgICAAA8LAkACQCAGKAIIQQFHDQAgACAGKAIMIAEQyoCAgAA2AgQgAEEBNgIAIAYoAggNASAGQQhqQQRyEMGAgIAADAELIABBADYCACAAIAZBCGpBBHIiASkCADcCBCAAQRRqIAFBEGopAgA3AgAgAEEMaiABQQhqKQIANwIACyAGQeABaiSAgICAAAuREwMDfwF+A38jgICAgABB4AFrIgYkgICAgAAgBkEIaiABEM2AgIAAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAYtAAhBAUYNACAGLQAJQQFHDQEgBi0ACiEHIAZBCGoQyICAgAACQAJAIAdB2wBGDQAgB0H7AEYNASABIAZB2AFqQbCRwIAAEOCAgIAAIQcgBkEBNgIIIAYgBzYCDAwQCyABLQAYQX9qIgdB/wFxIgggB0cNBCABIAc6ABgCQCAIDQAgBkEoaiEHDA4LIAEQ0ICAgAAgBiABNgKAASAGQQE6AIQBIAZBwABqIAZBgAFqEN2BgIAAIAYoAkBBAUYNBSAGQbABakEIaiIHIAZBzABqKAIANgIAIAYgBikCRCIJNwOwAQJAIAmnDQBBAEHUm8CAAEGMm8CAABC7gYCAACEKIAZBsAFqEJWBgIAADAwLIAZBkAFqQQhqIAcoAgA2AgAgBiAGKQOwATcDkAEgBkHAAGogBkGAAWoQ3IGAgAACQAJAAkAgBigCQEEBRw0AIAYoAkQhCgwBCyAGQbABakEIaiIHIAZBzABqKAIANgIAIAYgBikCRCIJNwOwASAJpw0BQQFB1JvAgABBjJvAgAAQu4GAgAAhCiAGQbABahCdgYCAAAsgBkGQAWoQvoCAgAAMDAsgBkGgAWpBCGogBygCACIHNgIAIAZBKGpBCGogBikDsAE3AwAgBkE4aiAHNgIAIAYgBikClAE3AyggBigCkAEhCkEAIQcMDAsgAS0AGEF/aiIHQf8BcSIIIAdHDQIgASAHOgAYAkAgCA0AIAZBwABqIQcMDQsgARDQgICAACAGQQE6AHwgBiABNgJ4IAZBADYCgAEgBkEANgKQASAGQcAAakEEciEIAkADQCAGQbABaiAGQfgAahDQgYCAAAJAAkAgBi0AsAFBAUYNACAGLQCxASIHQQNGDQMCQCAHQQFLDQACQAJAIAcOAgABAAsCQCAGKAKAAQ0AIAZBwABqIAZB+ABqENSBgIAAIAYoAkBBAUcNBAwNC0Gcm8CAAEEOELyBgIAAIQcMDQsCQCAGKAKQAQ0AIAZBwABqIAZB+ABqENWBgIAAIAYoAkBBAUYNDCAGQZABahCdgYCAACAGQZABakEIaiAIQQhqKAIANgIAIAYgCCkCADcDkAEMBAtBqpvAgABBDhC8gYCAACEHDAwLIAYgBkH4AGoQ2YGAgAAiBzYCQCAHDQsgBkHAAGoQk4GAgAAMAgsgBigCtAEhBwwKCyAGQYABahCVgYCAACAGQYABakEIaiAIQQhqKAIANgIAIAYgCCkCADcDgAEMAAsLIAYoAoABDQVBnJvAgABBDhDzgICAACEHDAcLIAAgBigCDDYCBCAAQQE2AgAMDAsgBkEFNgJAIAEgBkHAAGoQxYCAgAAhASAAQQE2AgAgACABNgIEIAZBCGoQyICAgAAMCwtBwIHAgABBIUHQkMCAABCNhoCAAAALQcCBwIAAQSFB0JDAgAAQjYaAgAAACyAGKAJEIQoMBQsgBkGgAWpBCGogBkGAAWpBCGooAgA2AgAgBiAGKQOAATcDoAECQAJAAkACQAJAIAYoApABIggNACAGQcAAakGqm8CAAEEOEMSBgIAAQQEhCiAGKAJAQQFGDQIgBkG4AWogBkHMAGooAgA2AgAgBiAGKQJENwOwASAGKAKQASEIDAELIAZBsAFqQQhqIAZBkAFqQQhqKAIANgIAIAYgBikDkAE3A7ABQQAhCgsgBkEoakEIaiAGKQOwATcDACAGQThqIAZBsAFqQQhqKAIANgIAIAYgBikCpAE3AyggBigCoAEhByAIRQ0BIApFDQIgBkGQAWoQvoCAgAAMAgsgBigCRCEHIAZBoAFqEL6AgIAAQQAhCgwECyAGQZABahCdgYCAAAtBACEIIAYoAoABDQMgBkGAAWoQlYGAgAAMAwsgBigCRCEHC0EBIQoLIAZBkAFqEJ2BgIAAAkAgBigCgAFFDQBBASEIIApFDQEgBkGAAWoQvoCAgAAMAQsgBkGAAWoQlYGAgABBASEICwJAAkAgAS0AGEEBaiIKQf8BcSAKRw0AIAEgCjoAGCABEOKAgIAAIQogBkHAAGpBCGogBikDKDcDACAGQcAAakEQaiAGQShqQQhqKQMANwMAIAZB2ABqIAZBKGpBEGooAgA2AgAgBiAHNgJEIAYgCDYCQCAGIAo2AlwCQAJAAkACQCAIDQAgCkUNAkEAIQtBASEMDAELQQEhC0EAIQwgByEKCyAGQQE2AgggBiAKNgIMIAgNASAGQcAAakEEchC9gICAAAwDCyAGQQhqQRRqIAZBwABqQRRqKQIANwIAIAZBCGpBDGogBkHAAGpBDGopAgA3AgAgBiAGKQJENwIMIAZBADYCCEEBIQsMAgsgDEUNASAGQcAAakEEchDYgICAAAwBC0GwgsCAAEEcQeCQwIAAEI2GgIAAAAsgBkHcAGohBwJAIAYoAlwNACAHELyAgIAADAULIAtFDQQgBxDYgICAAAwEC0EBIQcLAkACQCABLQAYQQFqIghB/wFxIAhHDQAgASAIOgAYIAEQ44CAgAAhCCAGQcAAakEIaiAGKQMoNwMAIAZBwABqQRBqIAZBKGpBCGopAwA3AwAgBkHYAGogBkEoakEQaigCADYCACAGIAo2AkQgBiAHNgJAIAYgCDYCXAJAAkACQAJAIAcNACAIRQ0CQQAhC0EBIQwMAQtBASELQQAhDCAKIQgLIAZBATYCCCAGIAg2AgwgBw0BIAZBwABqQQRyEL2AgIAADAMLIAZBCGpBFGogBkHAAGpBFGopAgA3AgAgBkEIakEMaiAGQcAAakEMaikCADcCACAGIAYpAkQ3AgwgBkEANgIIQQEhCwwCCyAMRQ0BIAZBwABqQQRyENiAgIAADAELQbCCwIAAQRxB4JDAgAAQjYaAgAAACyAGQdwAaiEHAkAgBigCXA0AIAcQvICAgAAMAwsgC0UNAiAHENiAgIAADAILIAdBFTYCACABIAcQxYCAgAAhASAAQQE2AgAgACABNgIECyAGQeABaiSAgICAAA8LAkACQCAGKAIIQQFHDQAgACAGKAIMIAEQyoCAgAA2AgQgAEEBNgIAIAYoAggNASAGQQhqQQRyEL2AgIAADAELIABBADYCACAAIAZBCGpBBHIiASkCADcCBCAAQRRqIAFBEGopAgA3AgAgAEEMaiABQQhqKQIANwIACyAGQeABaiSAgICAAAugAgEBfyOAgICAAEEQayIDJICAgIAAAkACQAJAAkAgAkUNACADQQhqIAEQ8ICAgAAgAyADQQhqEO2AgIAAIgI2AgQgAg0BIANBBGoQvICAgAAgAEEIakEBOgAAIAAgATYCBCAAQQA2AgAMAwsgA0EIaiABEPCAgIAAIAMgA0EIahDtgICAACICNgIEIAINASADQQRqELyAgIAAIANBCGogARDugICAACADIANBCGoQ7YCAgAAiAjYCBAJAIAINACADQQRqELyAgIAAIABBCGpBADoAACAAIAE2AgQgAEEANgIADAMLIABBATYCACAAIAI2AgQMAgsgAEEBNgIAIAAgAjYCBAwBCyAAQQE2AgAgACACNgIECyADQRBqJICAgIAAC9QCBAF/AX4BfwF+I4CAgIAAQfAAayIAJICAgIAAIAAQ5ISAgAAiATcDGAJAIAFCAVINACAAQdgAakIAEOWEgIAAAkAgACgCWCICQQFHDQAgAEHYAGpBBHIQvoCAgAALIABB8ABqJICAgIAAIAJBAUYPCyAAIABBGGo2AlAgAEGAiMCAADYCVCAAQdgAakEUakEANgIAIABBrIPAgAA2AmggAEIBNwJcIABBwJLAgAA2AlggAEEQaiAAQdAAakGHgICAABDpgoCAACAAKQMQIQEgAEEIaiAAQdQAakGHgICAABDpgoCAACAAKQMIIQMgACAAQdgAakGIgICAABCShYCAACAAQSBqQRRqQQM2AgAgACADNwNAIAAgATcDOCAAQgM3AiQgAEH8kcCAADYCICAAIAApAwA3A0ggACAAQThqNgIwIABBIGpB1JLAgAAQ5oWAgAAACxIAQfj5woAAIAAgARDGg4CAAAsUAEH4+cKAACAAIAEgAhDHg4CAAAsWAEH4+cKAACAAIAEgAiADEKGBgIAACxIAQfj5woAAIAAgARCggYCAAAsMACABIAIQgICAgAALCgAgARCBgICAAAsKACABEIKAgIAACwoAIAEQg4CAgAALCgAgARCEgICAAAsKACABEIWAgIAACwoAIAEQhoCAgAALCAAQh4CAgAALCAAQiICAgAALCAAQiYCAgAALCAAQioCAgAALCgAgARCLgICAAAsKACABEIyAgIAACwoAIAEQjYCAgAALCAAQjoCAgAALCAAQj4CAgAALCgAgARCQgICAAAsOACABIAIgAxCRgICAAAsOACABIAIgAxCSgICAAAsOACABIAIgAxCTgICAAAsMACABIAIQlICAgAALCAAQlYCAgAALDAAgASACEJaAgIAACwwAIAEgAhCXgICAAAsMACABIAIQmICAgAALGAAgASACIAMgBCAFIAYgByAIEJmAgIAACxoAIAEgAiADIAQgBSAGIAcgCCAJEJqAgIAACwwAIAEgAhCbgICAAAsMACABIAIQnICAgAALDgAgASACIAMQnYCAgAALCgAgARCegICAAAsOACABIAIgAxCfgICAAAsWACABIAIgAyAEIAUgBiAHEKCAgIAACwwAIAEgAhChgICAAAsQACABIAIgAyAEEKKAgIAACxAAIAEgAiADIAQQo4CAgAALGgAgASACIAMgBCAFIAYgByAIIAkQpICAgAALDgAgASACIAMQpYCAgAALDgAgASACIAMQpoCAgAALCAAQp4CAgAALDAAgASACEKiAgIAACwoAIAEQqYCAgAALEgAgASACIAMgBCAFEKqAgIAACw4AIAEgAiADEKuAgIAACw4AIAEgAiADEKyAgIAACwwAIAEgAhCtgICAAAvEAQEBfyOAgICAAEEgayICJICAgIAAIAJBEGogAUECEIuCgIAAAkACQCACKAIQQQFHDQAgAigCFCEBDAELIAIgAigCFDYCCCACIAJBGGotAAA6AAwgAiACQQhqQdyTwIAAQQogAEEQahC+gYCAACIBNgIQIAENACACQRBqELyAgIAAIAIgAkEIaiAAEN6BgIAAIgE2AhAgAQ0AIAJBEGoQvICAgAAgAigCCCACLQAMEN+BgIAAIQELIAJBIGokgICAgAAgAQuWAQEBfyOAgICAAEEgayICJICAgIAAIAJBEGogAUEBEIuCgIAAAkACQCACKAIQQQFHDQAgAigCFCEBDAELIAIgAigCFDYCCCACIAJBGGotAAA6AAwgAiACQQhqIAAQ3oGAgAAiATYCECABDQAgAkEQahC8gICAACACKAIIIAItAAwQ34GAgAAhAQsgAkEgaiSAgICAACABC6kEAwF/A34BfyOAgICAAEGgAWsiAySAgICAACADQcAAahDXhICAAAJAIAMpA0BCgYCA9d246+Q1VCADQcAAakEIaikDACIEQjZUIARCNlEbDQAgA0HgAGpBCGogAkEIaigCADYCACADIAIpAgA3A2AgA0HQAGogA0HgAGoQrIGAgAAgA0EoaiABIANB0ABqEPCBgIAAIAMoAighAiADKQMwIQQgA0E4aikDACEFIANBGGoQ14SAgAACQAJAIARCACACGyIEIAMpAxh8IgYgBFQiByAFQgAgAhsiBSADQRhqQQhqKQMAfCAHrXwiBCAFVCAEIAVRGw0AIAZCgICAi6LHlJtKfCIFIAZWIAQgBkKAgID13bjr5DVUrX1CSnwiBiAEViAGIARRGw0BIAMgBTcDYCADIAY3A2ggAyABIANB0ABqIANB4ABqEPOBgIAAIANBkAFqENOEgIAAIANB4ABqIANBkAFqEO2EgIAAIANB8ABqQQhqIANB0ABqQQhqKAIANgIAIAMgAykDUDcDcCADQYABahDThICAACADQZABakHklMCAAEEeELKBgIAAIAAgA0HgAGogA0HwAGpCgICA9d246+Q1QjYgA0GAAWogA0GQAWoQ84SAgAAgA0GgAWokgICAgAAPC0GwgsCAAEEcQcSUwIAAEI2GgIAAAAtBwIHAgABBIUHUlMCAABCNhoCAAAALQfiTwIAAQTpBtJTAgAAQoYWAgAAAC4sFAgF/An4jgICAgABBwAFrIgMkgICAgAAgA0HQAGoQ1YSAgAAgA0HgAGoQ04SAgAACQAJAIANB0ABqIANB4ABqEMaBgIAARQ0AIANB4ABqEL6AgIAAIANB0ABqEL6AgIAAIANBMGogAhDPhYCAACADKAIwIAMoAjQQ7ISAgAANAUHAlcCAAEESQdSVwIAAEKGFgIAAAAsgAyADQdAAajYCoAEgAyADQeAAajYCpAEgA0GoAWpBFGpBADYCACADQayDwIAANgK4ASADQgE3AqwBIANBqJXAgAA2AqgBIANByABqIANBoAFqQYmAgIAAEKaFgIAAIAMpA0ghBCADQcAAaiADQaQBakGJgICAABCmhYCAACADKQNAIQUgA0E4aiADQagBakGIgICAABCShYCAACADQfAAakEUakEDNgIAIAMgBTcDkAEgAyAENwOIASADQgM3AnQgA0H8kcCAADYCcCADIAMpAzg3A5gBIAMgA0GIAWo2AoABIANB8ABqQbCVwIAAEOaFgIAAAAsgA0GoAWoQ1ISAgAAgA0EYaiABIANBqAFqEPSBgIAAIANBCGogAykDGCADKQMgIANBKGopAwBB/JXAgAAQqoGAgAAgA0EIakEIaikDACEEIAMpAwghBSADQagBahC+gICAACADQYgBahDThICAACADQagBaiADQYgBahDthICAACADQfAAahDUhICAACADQYgBaiADQagBaiADQfAAahD0hICAACADQYgBahCYgYCAACADQYgBakEIaiACQQhqKAIANgIAIAMgAikCADcDiAEgA0GoAWogA0GIAWoQ7YSAgAAgACADQagBaiAFIAQQ8YSAgAAgA0HAAWokgICAgAALvQgCAX8CfiOAgICAAEHwAWsiBCSAgICAACAEQeABahDVhICAACAEQaABahDThICAAAJAIARB4AFqIARBoAFqEMaBgIAARQ0AIARBoAFqEL6AgIAAIARB4AFqEL6AgIAAIARBOGogAhDPhYCAAAJAIAQoAjggBCgCPBDshICAAEUNACAEQYgBahDUhICAACAEQSBqIAEgBEGIAWoQ9IGAgAAgBEEQaiAEKQMgIAQpAyggBEEwaikDAEHslsCAABCqgYCAACAEQRBqQQhqKQMAIQUgBCkDECEGIARBiAFqEL6AgIAAIARB8ABqQQhqIAJBCGooAgA2AgAgBCACKQIANwNwIARBiAFqIARB8ABqEO2EgIAAIARB8ABqIARBiAFqEO+EgIAAIARBiAFqQQhqIANBCGooAgA2AgAgBCADKQIANwOIASAEQdgAaiAEQYgBahCsgYCAACAEQYgBaiAEQfAAaiAEQdgAahDyhICAACAEQaABaiAEQYgBaiAGIAUQ8YSAgAAgBCAGIAUQ/YSAgAAgBEEIaikDACEFIAQpAwAhBiAEQbABahDThICAACAEIAU3A3ggBCAGNwNwIARB2ABqQYABEOqAgIAAIAQgBEHYAGo2AtABIAQgBEHwAGogBEHQAWoQwIKAgAAiAjYC4AECQAJAIAINACAEQeABahC8gICAAEEAIQIgBEEANgLAASAEQcABahC8gICAACAEQZQBaiAEQdgAakEIaigCADYCACAEIAQpA1g3AowBDAELIAQgAjYCjAEgBEHYAGoQvoCAgABBASECCyAEIAI2AogBIARBwAFqIARBiAFqQeSSwIAAQTdBnJPAgAAQq4GAgAAgBEHYAGogBEGwAWoQiYaAgAAgBEGIAWogBEHYAGoQ7YSAgAAgBEHQAWpBvpPAgABBHhCygYCAACAEQeABakEIaiAEQcABakEIaigCADYCACAEIAQpA8ABNwPgASAEQdgAaiAEQYgBaiAEQdABaiAEQeABakIAQgBCgIDpg7HeFhDwhICAACAAIARBoAFqIARB2ABqEPWEgIAAIARBsAFqEL6AgIAAIARB8AFqJICAgIAADwtBwJXAgABBEkHclsCAABChhYCAAAALIAQgBEHgAWo2AsABIAQgBEGgAWo2AtABIARBiAFqQRRqQQA2AgAgBEGsg8CAADYCmAEgBEIBNwKMASAEQcSWwIAANgKIASAEQdAAaiAEQcABakGJgICAABCmhYCAACAEKQNQIQUgBEHIAGogBEHQAWpBiYCAgAAQpoWAgAAgBCkDSCEGIARBwABqIARBiAFqQYiAgIAAEJKFgIAAIARB2ABqQRRqQQM2AgAgBCAGNwN4IAQgBTcDcCAEQgM3AlwgBEH8kcCAADYCWCAEIAQpA0A3A4ABIAQgBEHwAGo2AmggBEHYAGpBzJbAgAAQ5oWAgAAAC/8FAgF/An4jgICAgABB0AFrIgQkgICAgAAgBEEoaiACEM+FgIAAAkAgBCgCKCAEKAIsEOyEgIAARQ0AIARBGGoQ14SAgAAgBEEYakEIaikDACEFIAQpAxghBiAEQaABakEIaiACQQhqKAIANgIAIAQgAikCADcDoAEgBEHwAGogBEGgAWoQ7YSAgAAgBEGgAWogBEHwAGoQ74SAgAAgBEHwAGpBCGogA0EIaigCADYCACAEIAMpAgA3A3AgBEHAAGogBEHwAGoQrIGAgAAgBEHwAGogBEGgAWogBEHAAGoQ8oSAgAAgBEEwaiAEQfAAaiAGIAUQ8YSAgAAgBEHQAGoQ1YSAgAAgBEEIaiAGIAUQ/YSAgAAgBEEIakEIaikDACEFIAQpAwghBiAEQeAAahDThICAACAEQYgBaiAEQdAAakEIaigCADYCACAEIAU3A3ggBCAGNwNwIAQgBCkDUDcDgAEgBEHAAGpBgAEQ6oCAgAAgBCAEQcAAajYCsAEgBCAEQfAAaiAEQbABahC/goCAACICNgLAASAEQYABaiEDAkACQCACDQAgBEHAAWoQvICAgABBACECIARBADYCkAEgBEGQAWoQvICAgAAgBEGsAWogBEHAAGpBCGooAgA2AgAgBCAEKQNANwKkAQwBCyAEIAI2AqQBIARBwABqEL6AgIAAQQEhAgsgBCACNgKgASAEQZABaiAEQaABakHkksCAAEE3QZyTwIAAEKuBgIAAIARBwABqIARB4ABqEImGgIAAIARBoAFqIARBwABqEO2EgIAAIARBsAFqQayTwIAAQRIQsoGAgAAgBEHAAWpBCGogBEGQAWpBCGooAgA2AgAgBCAEKQOQATcDwAEgBEHAAGogBEGgAWogBEGwAWogBEHAAWpCAEIAQoCA6YOx3hYQ8ISAgAAgAxC+gICAACAAIARBMGogBEHAAGoQ9YSAgAAgBEHgAGoQvoCAgAAgBEHQAWokgICAgAAPC0HAlcCAAEESQfyWwIAAEKGFgIAAAAvgAwECfyOAgICAAEGgAWsiBCSAgICAACAEQTBqENWEgIAAIARBwABqENOEgIAAAkACQCAEQTBqIARBwABqEMaBgIAARQ0AIARBwABqEL6AgIAAIARBMGoQvoCAgAACQBCMgoCAACIFDQAgBEHoAGpBCGogAUEIaigCADYCACAEIAEpAgA3A2ggBEGIAWogBEHoAGoQ7YSAgAAgBEEIaiACIAMQ/oSAgAAgBEHoAGogBEGIAWogBCkDCCAEQQhqQQhqKQMAEPGEgIAAIARB6ABqEJiBgIAADAILIAEQvoCAgAAMAQsgBCAEQTBqNgKAASAEIARBwABqNgKEASAEQYgBakEUakEANgIAIARBrIPAgAA2ApgBIARCATcCjAEgBEG8l8CAADYCiAEgBEEoaiAEQYABakGJgICAABCmhYCAACAEKQMoIQIgBEEgaiAEQYQBakGJgICAABCmhYCAACAEKQMgIQMgBEEYaiAEQYgBakGIgICAABCShYCAACAEQdAAakEUakEDNgIAIAQgAzcDcCAEIAI3A2ggBEIDNwJUIARB/JHAgAA2AlAgBCAEKQMYNwN4IAQgBEHoAGo2AmAgBEHQAGpBxJfAgAAQ5oWAgAAACyAEQaABaiSAgICAACAFC5QEAQJ/I4CAgIAAQbABayIDJICAgIAAIANBwABqENWEgIAAIANB0ABqENOEgIAAAkAgA0HAAGogA0HQAGoQxoGAgABFDQAgA0HQAGoQvoCAgAAgA0HAAGoQvoCAgAACQAJAEIyCgIAAIgQNACADQfgAahDUhICAACADQRhqIAEgAhD+hICAACADIANBIGopAwA3A6ABIAMgAykDGDcDmAEgAyAAIANB+ABqIANBmAFqEPOBgIAAIANB+ABqEL6AgIAADAELIANB+ABqENOEgIAAIANBmAFqIANB+ABqEO2EgIAAIANB4ABqENSEgIAAIANB+ABqIANBmAFqIANB4ABqEPSEgIAAIANB+ABqEJiBgIAACyADQbABaiSAgICAACAEDwsgAyADQcAAajYCkAEgAyADQdAAajYClAEgA0GYAWpBFGpBADYCACADQayDwIAANgKoASADQgE3ApwBIANBvJfAgAA2ApgBIANBOGogA0GQAWpBiYCAgAAQpoWAgAAgAykDOCEBIANBMGogA0GUAWpBiYCAgAAQpoWAgAAgAykDMCECIANBKGogA0GYAWpBiICAgAAQkoWAgAAgA0HgAGpBFGpBAzYCACADIAI3A4ABIAMgATcDeCADQgM3AmQgA0H8kcCAADYCYCADIAMpAyg3A4gBIAMgA0H4AGo2AnAgA0HgAGpB1JfAgAAQ5oWAgAAAC9YDAwJ/AX4BfyOAgICAAEHAAWsiACSAgICAABDQhICAAEEBQeSXwIAAEM6EgIAAIABB4ABqENaEgIAAIABBIGogAEHgAGpBsJnAgABBKkHcmcCAABCpgYCAACAAQQhqIABBIGoQz4WAgAAgAEHgAGogACgCCCAAKAIMELCAgIAAAkAgACgCYEEBRw0AIAAgACgCZDYCoAFB7JnAgABBJiAAQaABakHYhcCAAEHcmcCAABCqhoCAAAALIABBoAFqQQhqIABB7ABqKAIAIgE2AgAgACAAKQJkIgI3A6ABIABBEGpBCGoiAyABNgIAIAAgAjcDECAAQSBqEL6AgIAAIABB4ABqEPyBgIAAIABBIGogAEHgAGoQpoGAgAAgAEHgAGpBCGogAygCADYCACAAIAApAxA3A2AgAEGgAWogAEEgaiAAQeAAahDBgoCAACAAQeAAaiAAQaABahDpgICAACAAQbABaiAAQeAAakGSmsCAAEEwQdyZwIAAEKuBgIAAIAAgAEGwAWoQz4WAgAAgACgCACAAKAIEEOeEgIAAIABBIGoQ/YGAgAAgAEGwAWoQvoCAgAAgAEGgAWoQmIGAgAAgAEEgahCUgYCAACAAQcABaiSAgICAAAuHBAMCfwF+AX8jgICAgABB0AFrIgAkgICAgAAQ0ISAgABBAUHkl8CAABDOhICAACAAQRBqENeEgIAAAkACQCAAKQMQIABBGGopAwCEQgBSDQAgAEHwAGoQ1oSAgAAgAEEwaiAAQfAAakGwmcCAAEEqQdyZwIAAEKmBgIAAIABBCGogAEEwahDPhYCAACAAQfAAaiAAKAIIIAAoAgwQsoCAgAAgACgCcEEBRg0BIABBsAFqQQhqIABB/ABqKAIAIgE2AgAgACAAKQJ0IgI3A7ABIABBIGpBCGoiAyABNgIAIAAgAjcDICAAQTBqEL6AgIAAIABB8ABqEPyBgIAAIABBMGogAEHwAGoQpoGAgAAgAEHwAGpBCGogAygCADYCACAAIAApAyA3A3AgAEGwAWogAEEwaiAAQfAAahDCgoCAACAAQfAAaiAAQbABahDpgICAACAAQcABaiAAQfAAakGSmsCAAEEwQdyZwIAAEKuBgIAAIAAgAEHAAWoQz4WAgAAgACgCACAAKAIEEOeEgIAAIABBMGoQ/YGAgAAgAEHAAWoQvoCAgAAgAEGwAWoQmIGAgAAgAEEwahCUgYCAACAAQdABaiSAgICAAA8LQcKawIAAQR0Qz4SAgAAACyAAIAAoAnQ2ArABQeyZwIAAQSYgAEGwAWpB2IXAgABB3JnAgAAQqoaAgAAAC+oEAwF/An4CfyOAgICAAEHgAWsiACSAgICAABDQhICAAEEBQeSXwIAAEM6EgIAAIABBEGoQ14SAgAACQAJAIAApAxAgAEEYaikDAIRCAFINACAAQYABahDWhICAACAAQcABaiAAQYABakGwmcCAAEEqQdyZwIAAEKmBgIAAIABBCGogAEHAAWoQz4WAgAAgAEGAAWogACgCCCAAKAIMELSAgIAAIAAoAoABQQFGDQEgAEHQAGogAEGAAWpBFGopAgA3AwAgAEHAAGpBCGogAEGMAWopAgAiATcDACAAIAApAoQBIgI3A0AgAEEgakEIaiIDIAE+AgAgACACNwMgIABBMGpBCGoiBCAAQcAAakEUaigCADYCACAAIAApAkw3AzAgAEHAAWoQvoCAgAAgAEGAAWoQ/IGAgAAgAEHAAGogAEGAAWoQpoGAgAAgAEHQAWpBCGogAygCADYCACAAIAApAyA3A9ABIABBgAFqQQhqIAQoAgA2AgAgACAAKQMwNwOAASAAQcABaiAAQcAAaiAAQdABaiAAQYABahDDgoCAACAAQYABaiAAQcABahDpgICAACAAQdABaiAAQYABakGSmsCAAEEwQdyZwIAAEKuBgIAAIAAgAEHQAWoQz4WAgAAgACgCACAAKAIEEOeEgIAAIABBwABqEP2BgIAAIABB0AFqEL6AgIAAIABBwAFqEJiBgIAAIABBwABqEJSBgIAAIABB4AFqJICAgIAADwtBwprAgABBHRDPhICAAAALIAAgACgChAE2AkBB7JnAgABBJiAAQcAAakHYhcCAAEHcmcCAABCqhoCAAAALqwQDAX8CfgJ/I4CAgIAAQdABayIAJICAgIAAENCEgIAAQQFB5JfAgAAQzoSAgAAgAEHwAGoQ1oSAgAAgAEGwAWogAEHwAGpBsJnAgABBKkHcmcCAABCpgYCAACAAQQhqIABBsAFqEM+FgIAAIABB8ABqIAAoAgggACgCDBC4gICAAAJAIAAoAnBBAUcNACAAIAAoAnQ2AjBB7JnAgABBJiAAQTBqQdiFwIAAQdyZwIAAEKqGgIAAAAsgAEHAAGogAEHwAGpBFGopAgA3AwAgAEEwakEIaiAAQfwAaikCACIBNwMAIAAgACkCdCICNwMwIABBEGpBCGoiAyABPgIAIAAgAjcDECAAQSBqQQhqIgQgAEEwakEUaigCADYCACAAIAApAjw3AyAgAEGwAWoQvoCAgAAgAEHwAGoQ/IGAgAAgAEEwaiAAQfAAahCmgYCAACAAQcABakEIaiADKAIANgIAIAAgACkDEDcDwAEgAEHwAGpBCGogBCgCADYCACAAIAApAyA3A3AgAEGwAWogACAAQcABaiAAQfAAahDEgoCAACAAQfAAaiAAQbABahDpgICAACAAQcABaiAAQfAAakGSmsCAAEEwQdyZwIAAEKuBgIAAIAAgAEHAAWoQz4WAgAAgACgCACAAKAIEEOeEgIAAIABBMGoQ/YGAgAAgAEHAAWoQvoCAgAAgAEGwAWoQmIGAgAAgAEEwahCUgYCAACAAQdABaiSAgICAAAubBAUCfwJ+AX8BfgF/I4CAgIAAQdABayIAJICAgIAAENCEgIAAQQFB5JfAgAAQzoSAgAAgAEEYahDXhICAAAJAAkAgACkDGCAAQSBqKQMAhEIAUg0AIABB+ABqENaEgIAAIABBOGogAEH4AGpBsJnAgABBKkHcmcCAABCpgYCAACAAQRBqIABBOGoQz4WAgAAgAEH4AGogACgCECAAKAIUELaAgIAAIAAoAnhBAUYNASAAQcABakEIaiAAQZgBaigCACIBNgIAIAAgAEGQAWopAwAiAjcDwAEgAEGIAWopAwAhAyAAQfgAakEIaiIEKQMAIQUgAEEoakEIaiIGIAE2AgAgACACNwMoIABBOGoQvoCAgAAgAEH4AGoQ/IGAgAAgAEE4aiAAQfgAahCmgYCAACAEIAYoAgA2AgAgACAAKQMoNwN4IAAgACAAQfgAaiAFIAMQxYKAgAA6AL8BIABB+ABqIABBvwFqEOyAgIAAIABBwAFqIABB+ABqQZKawIAAQTBB3JnAgAAQq4GAgAAgAEEIaiAAQcABahDPhYCAACAAKAIIIAAoAgwQ54SAgAAgAEE4ahD9gYCAACAAQcABahC+gICAACAAQThqEJSBgIAAIABB0AFqJICAgIAADwtBwprAgABBHRDPhICAAAALIAAgACgCfDYCwAFB7JnAgABBJiAAQcABakHYhcCAAEHcmcCAABCqhoCAAAALuwMCAX8CfiOAgICAAEHAAWsiACSAgICAABDQhICAAEEBQeSXwIAAEM6EgIAAIABBGGoQ14SAgAACQAJAIAApAxggAEEgaikDAIRCAFINACAAQegAahDWhICAACAAQShqIABB6ABqQbCZwIAAQSpB3JnAgAAQqYGAgAAgAEEQaiAAQShqEM+FgIAAIABB6ABqIAAoAhAgACgCFBCugICAACAAKAJoQQFGDQEgAEH4AGopAwAhASAAQfAAaikDACECIABBKGoQvoCAgAAgAEHoAGoQ/IGAgAAgAEEoaiAAQegAahCmgYCAACAAIABBKGogAiABEMaCgIAAOgCvASAAQegAaiAAQa8BahDsgICAACAAQbABaiAAQegAakGSmsCAAEEwQdyZwIAAEKuBgIAAIABBCGogAEGwAWoQz4WAgAAgACgCCCAAKAIMEOeEgIAAIABBKGoQ/YGAgAAgAEGwAWoQvoCAgAAgAEEoahCUgYCAACAAQcABaiSAgICAAA8LQcKawIAAQR0Qz4SAgAAACyAAIAAoAmw2ArABQeyZwIAAQSYgAEGwAWpB2IXAgABB3JnAgAAQqoaAgAAACxMBAX8gACABEI2CgIAAIQIgAg8LDwAgACABIAIQjoKAgAAPCxcBAX8gACABIAIgAxCPgoCAACEEIAQPCxMBAX8gACABEJCCgIAAIQIgAg8LTQEBfyOAgICAAEEQayIEJICAgIAAIARBCGogARCrhYCAACAEIAQoAgggBCgCDCACIAMQ0oKAgAAgACAEKQMANwIAIARBEGokgICAgAALygEBAX8jgICAgABBIGsiBSSAgICAACAFIAI2AgQgBSABNgIAIAUgAzYCCCAFIAQ2AgwCQCAEIANJDQACQCADRQ0AIAIgA0YNACACIANNDQEgASADaiwAAEG/f0wNAQsCQCAERQ0AIAIgBEYNACACIARNDQEgASAEaiwAAEG/f0wNAQsgACAEIANrNgIEIAAgASADajYCACAFQSBqJICAgIAADwsgBSAFQQxqNgIYIAUgBUEIajYCFCAFIAU2AhAgBUEQahD6goCAAAALZQECfyOAgICAAEEQayIDJICAgIAAIAAgAiABayICENSCgIAAIAAgACgCCCIEIAJqNgIIIANBCGogABDVgoCAACAEIAMoAghqIAMoAgwgBGsgASACENaCgIAAIANBEGokgICAgAALEQAgACAAKAIIIAEQlYOAgAALGQAgACABEKqFgIAANgIAIAAgASgCCDYCBAuTAgEBfyOAgICAAEHgAGsiBCSAgICAACAEIAE2AgggBCADNgIMAkAgASADRw0AIAAgAiABEPOGgIAAGiAEQeAAaiSAgICAAA8LIARBKGpBFGpBiICAgAA2AgAgBEE0akHVgICAADYCACAEQRBqQRRqQQM2AgAgBCAEQQhqNgJAIAQgBEEMajYCRCAEQcgAakEUakEANgIAIARCAzcCFCAEQbygwIAANgIQIARB1YCAgAA2AiwgBEGcnMCAADYCWCAEQgE3AkwgBEGIocCAADYCSCAEIARBKGo2AiAgBCAEQcgAajYCOCAEIARBxABqNgIwIAQgBEHAAGo2AiggBEEQakHwn8CAABCxhoCAABCUhoCAAAALSwEBfyOAgICAAEEQayIDJICAgIAAIANBCGogARCrhYCAACADIAMoAgggAygCDCACENiCgIAAIAAgAykDADcCACADQRBqJICAgIAAC54BAQF/I4CAgIAAQSBrIgQkgICAgAAgBCACNgIEIAQgATYCACAEIAM2AgggBCACNgIMAkAgA0UNACACIANGDQACQCACIANNDQAgASADaiwAAEG/f0oNAQsgBCAEQQxqNgIYIAQgBEEIajYCFCAEIAQ2AhAgBEEQahD8goCAAAALIAAgAiADazYCBCAAIAEgA2o2AgAgBEEgaiSAgICAAAtEAQF/I4CAgIAAQRBrIgQkgICAgAAgBEEIaiACIAMQ3IKAgAAgACABIAQoAghB9JvAgAAQ8YWAgAAgBEEQaiSAgICAAAtgAQN/AkAgAC0AAEECSQ0AIABBBGooAgAiASgCACABKAIEKAIAEYGAgIAAAAJAIAEoAgQiAigCBCIDRQ0AIAEoAgAgAyACKAIIEM6CgIAACyAAKAIEQQxBBBDOgoCAAAsLQAACQAJAIAIgAUkNACAEIAJPDQEgAiAEEI+GgIAAAAsgASACEJCGgIAAAAsgACACIAFrNgIEIAAgAyABajYCAAtiAQF/I4CAgIAAQRBrIgMkgICAgAAgAyABIAIQjoOAgABBDEEEEN2CgIAAIgFBCGogA0EIaigCADYCACABIAMpAwA3AgAgAEH0m8CAADYCBCAAIAE2AgAgA0EQaiSAgICAAAsuAQF/AkACQCAARQ0AIAAgARDNgoCAACICDQEgACABEIWGgIAAAAsgASECCyACCwkAIABBADYCAAsJACAAQQA2AgALDQBCz7iP7NPB+cS/fwsEAEEACw8AIAAoAgAgARDjgoCAAAvZAwACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAAKAIADhYBAgMEBQYHCAkKCwwNDg8QERITFBUAAQsgAUHlt8CAAEEYEMiGgIAADwsgASAAKAIEIABBCGooAgAQyIaAgAAPCyAAQQRqIAEQ54WAgAAPCyABQZW7wIAAQRgQyIaAgAAPCyABQfq6wIAAQRsQyIaAgAAPCyABQeC6wIAAQRoQyIaAgAAPCyABQce6wIAAQRkQyIaAgAAPCyABQbu6wIAAQQwQyIaAgAAPCyABQai6wIAAQRMQyIaAgAAPCyABQZW6wIAAQRMQyIaAgAAPCyABQYe6wIAAQQ4QyIaAgAAPCyABQfm5wIAAQQ4QyIaAgAAPCyABQeu5wIAAQQ4QyIaAgAAPCyABQd25wIAAQQ4QyIaAgAAPCyABQcq5wIAAQRMQyIaAgAAPCyABQbC5wIAAQRoQyIaAgAAPCyABQfK4wIAAQT4QyIaAgAAPCyABQd64wIAAQRQQyIaAgAAPCyABQbq4wIAAQSQQyIaAgAAPCyABQay4wIAAQQ4QyIaAgAAPCyABQZm4wIAAQRMQyIaAgAAPCyABQf23wIAAQRwQyIaAgAALFAAgACgCACAAKAIEIAEQr4WAgAALDwAgACgCACABEMSGgIAACxwAIAAoAgAgACgCBCABKAIAIAEoAgQQ54KAgAALMAEBf0EAIQQCQAJAIAEgA0cNACAAIAJHDQFBASEECyAEDwsgACACIAEQ9IaAgABFCxAAIAAgAjYCBCAAIAE2AgALEAAgACACNgIEIAAgATYCAAtxAQF/I4CAgIAAQSBrIgIkgICAgAAgAiAANgIEIAJBCGpBEGogAUEQaikCADcDACACQQhqQQhqIAFBCGopAgA3AwAgAiABKQIANwMIIAJBBGpBhJ7AgAAgAkEIahCfhoCAACEBIAJBIGokgICAgAAgAQs1AQF/I4CAgIAAQRBrIgEkgICAgAAgAUEIaiAAENWCgIAAIAAQ8oKAgAAgAUEQaiSAgICAAAsCAAtIAQF/AkAgACgCACIBQQFLDQACQAJAIAEOAgABAAsgAEEIaigCACIBRQ0BIAAoAgQgAUEBEM6CgIAADwsgAEEEahDagoCAAAsLAgALGAAgACgCABDtgoCAACAAKAIAEPCCgIAACw4AIABBFEEEEM6CgIAACxgAAkAgAC0AAEUNACAAQQRqEO+CgIAACwsgAQF/AkAgACgCBCIBRQ0AIAAoAgAgAUEBEM6CgIAACwsYAAJAIAAvAQBFDQAgAEEEahDvgoCAAAsLFQACQCAAKAIARQ0AIAAQ74KAgAALCwIACwoAIAAQ64KAgAALGAACQCAALQAARQ0AIABBBGoQ74KAgAALC98BAAJAAkACQAJAIAFBgAFJDQAgAUGAEEkNASABQYCABE8NAiACIAFBP3FBgAFyOgACIAIgAUEGdkE/cUGAAXI6AAEgAiABQQx2QQ9xQeABcjoAAEEDIQEMAwsgAiABOgAAQQEhAQwCCyACIAFBP3FBgAFyOgABIAIgAUEGdkEfcUHAAXI6AABBAiEBDAELIAIgAUE/cUGAAXI6AAMgAiABQRJ2QfABcjoAACACIAFBBnZBP3FBgAFyOgACIAIgAUEMdkE/cUGAAXI6AAFBBCEBCyAAIAE2AgQgACACNgIAC3IBAX8jgICAgABBIGsiBCSAgICAACAEIAM2AhQgBCACNgIQAkACQCABIANJDQAgBEEIaiAAIAEgAxCAg4CAACAEIAQpAwg3AxhBASEDIARBEGogBEEYahDmgoCAAA0BC0EAIQMLIARBIGokgICAgAAgAwsqAQF/IAAoAgAiASgCACABKAIEIAAoAgQoAgAgACgCCCgCABCShoCAAAALGgAgACgCACAAKAIEQQAgASgCABCShoCAAAALKgEBfyAAKAIAIgEoAgAgASgCBCAAKAIEKAIAIAAoAggoAgAQkoaAgAAAC1EBBH9BACECAkACQCABKAIAIgMgASgCBEkNAAwBCyADQQEQtoOAgABqIgQgA0kNACABKAIAIQUgASAENgIAQQEhAgsgACAFNgIEIAAgAjYCAAupBAEJfyOAgICAAEEQayIHJICAgIAAIAIgBWshCCABKAIYIQkCQAJAA0ACQCAJIAVrIgogA0kNAEEAIQsgAUEANgIYDAMLAkBCASACIApqMQAAQj+DhiABKQMAg1BFDQAgASAKNgIYIAohCSAGDQEgASAFNgIgIAohCQwBCyABKAIMIgwhCwJAIAYNACABKAIgIgsgDCAMIAtLGyELCyAIIAlqIQ0gC0F/aiELAkADQAJAIAtBf0cNACAHIAUgASgCICAGGzYCDCAHIAw2AggCQAJAA0AgByAHQQhqEP2CgIAAAkAgBygCAA0AIAEgASgCGCILIAVrIg42AhgCQCAGDQAgASAFNgIgCyAAIA42AgQgAEEIaiALNgIAQQEhCwwJCyAHKAIEIgsgBU8NASABKAIYIg8gBWsgC2oiDiADTw0CIAQgC2otAAAgAiAOai0AAEYNAAsgASAPIAEoAhAiC2siCTYCGCAGDQUgASALNgIgDAULQdCfwIAAIAsgBRCMhoCAAAALQeCfwIAAIA4gAxCMhoCAAAALIAsgBU8NASAKIAtqIANPDQMgDSALaiEOIAQgC2ohDyALQX9qIQsgDy0AACAOLQAARg0ACyABIAkgDGsgC2pBAWoiCTYCGCAGDQEgASAFNgIgDAELC0Gwn8CAACALIAUQjIaAgAAAC0HAn8CAACAJIAVrIAtqIAMQjIaAgAAACyAAIAs2AgAgB0EQaiSAgICAAAsXACAAQQA2AgggACACNgIEIAAgATYCAAs9AQF/I4CAgIAAQRBrIgQkgICAgAAgBEEIakEAIAMgASACENuCgIAAIAAgBCkDCDcCACAEQRBqJICAgIAACxAAIAAgASACIAMQ54KAgAALEwAgACABIAJqNgIEIAAgATYCAAtMAQF/IAEoAgQhAgJAIAEoAgBBAUcNACAAQQE2AgAgACACNgIEDwsgAEKAgICAEDcCACAAQQhqIAI2AgAgAEEMaiABQQhqKAIANgIAC0gBAX8gASgCBCECAkAgASgCAEEBRw0AIABBATYCACAAIAI2AgQPCyAAQgA3AgAgAEEIaiACNgIAIABBDGogAUEIaigCADYCAAtIAQF/I4CAgIAAQRBrIgEkgICAgAACQCAARQ0AQaScwIAAQTcgAUEIakGQocCAAEGkncCAABCqhoCAAAALIAFBEGokgICAgAALVwEBfyOAgICAAEEQayIDJICAgIAAAkAgAkUNACAAIAI2AgQgACABNgIAIANBEGokgICAgAAPC0GgocCAAEErIANBCGpBzKHAgABBhKPAgAAQqoaAgAAACywBAX4gACkCACEBQRRBBBDdgoCAACIAQgA3AgwgACABNwIEIABBATYCACAAC2sBAX8jgICAgABBEGsiAiSAgICAACAAKAIAIQACQAJAIAFBgAFJDQAgAkEANgIMIAIgASACQQxqEPiCgIAAIAAgAigCACACKAIEEImDgIAADAELIAAgARCKg4CAAAsgAkEQaiSAgICAAEEACxEAIAAgASABIAJqENOCgIAACzwAAkAgACgCCCAAKAIERw0AIABBARDUgoCAAAsgABCqhYCAACAAKAIIaiABOgAAIAAgACgCCEEBajYCCAtoAQF/I4CAgIAAQSBrIgIkgICAgAAgACgCACEAIAJBCGpBEGogAUEQaikCADcDACACQQhqQQhqIAFBCGopAgA3AwAgAiABKQIANwMIIAAgAkEIahDqgoCAACEBIAJBIGokgICAgAAgAQsTACAAKAIAIAEgAhCJg4CAAEEACwQAIAALZgEBfyOAgICAAEEgayIDJICAgIAAIANBCGogAkEAEJaDgIAAIANBADYCGCADIAMpAwg3AxAgA0EQaiABIAIQiYOAgAAgAEEIaiADKAIYNgIAIAAgAykDEDcCACADQSBqJICAgIAAC0MBAX8jgICAgABBEGsiAiSAgICAACACQQhqIAAQq4WAgAAgAigCCCACKAIMIAEQ0IaAgAAhASACQRBqJICAgIAAIAELIAEBfwJAIAAoAgQgACgCCCIBRg0AIAAgARCRg4CAAAsLgwEBAX8CQAJAAkACQCAAKAIEIgIgAUkNAAJAIAFFDQAgAiABRg0EIAAoAgAgAkEBIAEQz4KAgAAiAg0CIAFBARCFhoCAAAALIAAQ8oKAgAAgAEEBNgIAQQAhAQwCC0G8o8CAAEEkQayiwIAAEI2GgIAAAAsgACACNgIACyAAIAE2AgQLC0cBAX8jgICAgABBEGsiAiSAgICAACABEJCDgIAAIAJBCGogASgCACABKAIEEJODgIAAIAAgAikDCDcCACACQRBqJICAgIAAC0gBAX8jgICAgABBEGsiAySAgICAACADIAI2AgwgAyABNgIIIAAgA0EIahCqhYCAADYCACAAIAMoAgw2AgQgA0EQaiSAgICAAAtBAQF/I4CAgIAAQRBrIgIkgICAgAACQCAAKAIIIAFJDQAgAkEIaiAAENWCgIAAIAAgATYCCAsgAkEQaiSAgICAAAtqAQF/I4CAgIAAQRBrIgMkgICAgAAgAyAAIAEgAkEBQQEQmIOAgAACQAJAIAMoAgBBAUcNACADQQhqKAIARQ0BQZSjwIAAQShBrKLAgAAQjYaAgAAACyADQRBqJICAgIAADwsQhoaAgAAAC6QBAQN/I4CAgIAAQRBrIgMkgICAgAACQAJAIAFBf0wNAAJAAkAgAQ0AQQEhAgwBCyADQQhqIAFBARCGg4CAACADKAIMIQQgAygCCCEFAkACQCACDQAgBSAEEM2CgIAAIQIMAQsgBSAEENCCgIAAIQILIAJFDQILIAAgATYCBCAAIAI2AgAgA0EQaiSAgICAAA8LEJeDgIAAAAsgBSAEEIWGgIAAAAsJABCGhoCAAAALhQIBAn9BACEGAkAgASgCBCIHIAJrIANPDQAgAiADaiIDIAJJIQICQAJAAkACQCAFRQ0AIAJFDQEgACADNgIEIABBCGpBADYCAAwDCyACRQ0BIAAgAzYCBCAAQQhqQQA2AgAMAgsgB0EBdCICIAMgAiADSxshAwsCQCADQX9KDQAgAEEIakEANgIADAELAkACQCAHDQAgA0EBEM2CgIAAIQIMAQsgASgCACAHQQEgAxDPgoCAACECCwJAAkAgAg0AIARFDQEgA0EBEIWGgIAAAAsgASADNgIEIAEgAjYCAAwCCyAAIAM2AgRBASEGIABBCGpBATYCAAwBC0EBIQYLIAAgBjYCAAtNAQF/I4CAgIAAQRBrIgMkgICAgAAgAyABIAIQjoOAgAAgAEEANgIAIABBDGogA0EIaigCADYCACAAIAMpAwA3AgQgA0EQaiSAgICAAAscACAAIAEpAgA3AgAgAEEIaiABQQhqKAIANgIACy0BAX8jgICAgABBEGsiASSAgICAACABQQhqIAAQ1YKAgAAgAUEQaiSAgICAAAsKACAAEPKCgIAACzUAIABBgAE6ABggAEIBNwIMIAAgASkCADcCACAAQRRqQQA2AgAgAEEIaiABQQhqKAIANgIAC4wBAgF/AX4jgICAgABBEGsiAySAgICAAAJAAkACQAJAIAAoAgAOAwABAgALIAApAwghBCADQQM6AAAgAyAENwMIDAILIAApAwghBCADQQE6AAAgAyAENwMIDAELIAApAwghBCADQQI6AAAgAyAENwMICyADIAEgAhCfg4CAACEAIANBEGokgICAgAAgAAvhAQEBfyOAgICAAEEwayIDJICAgIAAIAMgAjYCBCADIAE2AgACQAJAIAAtAABBB0YNACADQSxqQYWAgIAANgIAIANBHGpBAjYCACADQgI3AgwgA0GUvMCAADYCCCADQdaAgIAANgIkIAMgADYCICADIANBIGo2AhggAyADNgIoIANBCGoQpYOAgAAhAAwBCyADQRxqQQE2AgAgA0IBNwIMIANBxLzAgAA2AgggA0GFgICAADYCJCADIANBIGo2AhggAyADNgIgIANBCGoQpYOAgAAhAAsgA0EwaiSAgICAACAAC00BA39BACECAkAgASgCCCIDIAEoAgRPDQAgASgCACADai0AACEEQQEhAiABIANBAWo2AggLIAAgAjoAASAAQQA6AAAgAEECaiAEOgAACzwBAX8jgICAgABBEGsiAiSAgICAACACQQhqIAEgASgCCBCog4CAACAAIAIpAwg3AgAgAkEQaiSAgICAAAs4AQF/QRRBBBDdgoCAACIDIAI2AhAgAyABNgIMIANBCGogAEEIaigCADYCACADIAApAgA3AgAgAwtwAQN/I4CAgIAAQRBrIgIkgICAgAACQCABKAIIIgNBAWoiBCADSQ0AIAJBCGogASAEIAEoAgQiAyADIARLGxCog4CAACAAIAIpAwg3AgAgAkEQaiSAgICAAA8LQcCdwIAAQRxB2MDAgAAQjYaAgAAAC88CAgF/AX4jgICAgABB0ABrIgIkgICAgAAgAiAAKAIAIgA2AjQgAkEANgIYIAJCATcDECACQThqQRRqQQE2AgAgAkIBNwI8IAJBnJzAgAA2AjggAkHXgICAADYCLCACIAJBKGo2AkggAiACQTRqNgIoIAJBEGogAkE4ahDqgoCAABCFg4CAACACQRBqEJCDgIAAIAJBMGogAigCGDYCACACIAIpAxA3AyggAkEIaiAAQQxqQYSAgIAAELiDgIAAIAIpAwghAyACIABBEGpBhICAgAAQuIOAgAAgAkEQakEUakEDNgIAIAIgAzcDQCACQdiAgIAANgI8IAJCBDcCFCACQdi7wIAANgIQIAIgAikDADcDSCACIAJBKGo2AjggAiACQThqNgIgIAEgAkEQahDJhoCAACEAIAJBKGoQ64KAgAAgAkHQAGokgICAgAAgAAu5AQEBfyOAgICAAEHAAGsiASSAgICAACABIAA2AgwgAUEANgIYIAFCATcDECABQTRqQQE2AgAgAUIBNwIkIAFBnJzAgAA2AiAgAUHZgICAADYCPCABIAFBOGo2AjAgASABQQxqNgI4IAFBEGogAUEgahDqgoCAABCFg4CAACABQRBqEJCDgIAAIAFBKGogASgCGDYCACABIAEpAxA3AyAgAUEgahCmg4CAACEAIAFBwABqJICAgIAAIAALgwsCCn8BfiOAgICAAEGgAWsiASSAgICAACABQcAAaiAAEKuFgIAAIAFB2ABqIAEoAkAgASgCREGtu8CAAEEJENWGgIAAAkACQAJAAkACQCABKAJYQQFGDQAgAUHlAGotAAAhAiABQeAAaigCACEDIAFBjAFqKAIAIQQgASgCiAEhBQJAA0AgASAENgJMIAEgBTYCSCABIAM2ApwBAkAgA0UNACAEIANGDQAgBCADTQ0FIAUgA2osAABBv39MDQULIAJB/wFxIQYCQAJAIANFDQAgBSADaiIHQX9qIgItAAAiCEEYdEEYdSIJQQBODQECQAJAIAUgAkcNAEEAIQgMAQsCQCAHQX5qIgItAAAiCEHAAXFBgAFGDQAgCEEfcSEIDAELAkACQCAFIAJHDQBBACECDAELAkAgB0F9aiIKLQAAIgJBwAFxQYABRg0AIAJBD3EhAgwBCwJAAkAgBSAKRw0AQQAhBwwBCyAHQXxqLQAAQQdxQQZ0IQcLIAcgAkE/cXIhAgsgAkEGdCAIQT9xciEICyAIQQZ0IAlBP3FyIQgMAQsQqYWAgABBgIDEACEICyAGRSECAkAgBg0AIAhBgIDEAEYNAkEBIQYCQCAIQYABSQ0AQQIhBiAIQYAQSQ0AQQNBBCAIQYCABEkbIQYLIAMgBmshAwwBCwsgAUHQAGogAzYCACABIAM2AmAgASACOgBlIAEgAzYCTCABQQE2AkgMAgsgASADNgJgIAEgAjoAZSABQQA2AkgMAQsgAUHgAGohAyABQZQBaigCACEIIAFBjAFqKAIAIQYgASgCkAEhBSABKAKIASEEAkAgAUH8AGooAgBBf0cNACABQcgAaiADIAQgBiAFIAhBARD+goCAAAwBCyABQcgAaiADIAQgBiAFIAhBABD+goCAAAtBACEGQQAhCCABKAJIRQ0CAkACQAJAAkAgASgCTCIFQQlqIgIgBUkNACAFQRFqIQMCQANAIAFBOGogACADQXhqIggQ14KAgAACQAJAIAEoAjxFDQAgASgCOC0AAEFQakH/AXFBCkkNAQsgAUEwaiAAIANBeGoiBBDXgoCAAEEAIQZBACEIIAEoAjAgASgCNEG2u8CAAEEIEPmCgIAADQIMCQsgCEEBaiAISQ0DIANBAWohAwwACwsgBEEIaiIJIARJDQICQANAIAFBKGogACADENeCgIAAAkACQCABKAIsRQ0AIAEoAigtAABBUGpB/wFxQQpJDQELQQAhBkEAIQggAyAAKAIISQ0JIAFBIGogACACIAQQ0YKAgAAgAUHYAGogASgCICABKAIkEN2GgIAAIAEtAFhBAUcNAgwICyADQQFqIgggA0kNBSAIIQMMAAsLIAEoAlwhCCABQRhqIAAgCSADENGCgIAAIAFB2ABqIAEoAhggASgCHBDdhoCAACABLQBYQQFGDQUgASgCXCEGIAAoAgggBUkNBiABQRBqIAAQq4WAgAACQAJAIAVFDQAgASgCFCIDIAVGDQAgAyAFTQ0BIAEoAhAgBWosAABBQEgNAQsgACAFEJSDgIAADAcLQdyhwIAAQTBBrKLAgAAQjYaAgAAAC0HAncCAAEEcQcy8wIAAEI2GgIAAAAtBwJ3AgABBHEHcvMCAABCNhoCAAAALQcCdwIAAQRxB7LzAgAAQjYaAgAAAC0HAncCAAEEcQfy8wIAAEI2GgIAAAAsgAUHIAGogAUGcAWoQ+4KAgAAAC0EAIQZBACEICyABQdgAakEIaiAAQQhqKAIANgIAIAEgACkCADcDWCABQQhqIAFB2ABqEJKDgIAAIAEpAwghC0EUQQQQ3YKAgAAiAyAGNgIQIAMgCDYCDCADIAs3AgQgA0EANgIAIAFBoAFqJICAgIAAIAMLFwAgAEEANgIIIAAgAjYCBCAAIAE2AgAL4AEBBX8jgICAgABBEGsiAySAgICAACADQQhqIAEoAgAgASgCBCACEICDgIAAIAMoAggiBCADKAIMaiEFQQEhBgJAAkADQCAFIARrIQdBACEBAkADQCAHIAFGDQQCQCAEIAFqIgItAABBCkYNACABQQFqIgIgAUkNAiACIQEMAQsLIAZBAWoiASAGSQ0CIAJBAWohBCABIQYMAQsLQcCdwIAAQRxB+L/AgAAQjYaAgAAAC0HAncCAAEEcQYjAwIAAEI2GgIAAAAsgACABNgIEIAAgBjYCACADQRBqJICAgIAAC/AEAQZ/I4CAgIAAQTBrIgMkgICAgAADQCABKAIEIQQgASgCCCIFIQYDQAJAAkACQCAGIARJDQBBACEHDAELIAEoAgAgBmotAABBqMHAgABqLQAARQ0BQQEhBwsCQAJAIAQgBkYNAAJAAkACQAJAIAdFDQACQAJAAkAgASgCACIHIAZqLQAAIghB3ABGDQAgCEEiRw0BIAIoAgghCCADQRBqIAUgBiAHIAQQ24KAgAAgAygCFCEGIAMoAhAhBAJAIAhFDQAgAiAEIAYQiYOAgAAgASgCCCIGQQFqIgQgBkkNByABIAQ2AgggA0EIaiACEKuFgIAAIANBIGogASADKAIIIAMoAgwQqoOAgAAgACADQSBqEIODgIAADAkLIAEoAggiAkEBaiIHIAJJDQUgASAHNgIIIANBIGogASAEIAYQqoOAgAAgACADQSBqEISDgIAADAgLIANBGGogBSAGIAcgBBDbgoCAACACIAMoAhggAygCHBCJg4CAACABKAIIIgZBAWoiBCAGSQ0DIAEgBDYCCCADIAEgAhCrg4CAACIGNgIgIAYNASADQSBqEPSCgIAADAoLIAEgBkEBajYCCCADQQ82AiAgACABIANBIGoQrIOAgAAMBgsgAEEBNgIAIAAgBjYCBAwFC0GYwMCAACAGIAQQjIaAgAAAC0HAncCAAEEcQajAwIAAEI2GgIAAAAtBwJ3AgABBHEG4wMCAABCNhoCAAAALQcCdwIAAQRxByMDAgAAQjYaAgAAACyADQQQ2AiAgACABIANBIGoQrIOAgAALIANBMGokgICAgAAPCyABIAZBAWoiBjYCCAwACwsLZQEBfyOAgICAAEEgayIEJICAgIAAIAQgAiADENeGgIAAAkACQCAEKAIAQQFGDQAgACAEKQIENwIEIABBADYCAAwBCyAEQQ42AhAgACABIARBEGoQtYOAgAALIARBIGokgICAgAALxAcBA38jgICAgABBIGsiAiSAgICAACACQRBqIAAQs4OAgAACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAItABBBAUYNACACLQARIQMgAkEQahD3goCAACADQZJ/aiIEQQdNDQQgA0Gef2oiBEEETQ0FIANBIkYNASADQS9GDQMgA0HcAEYNAgwMCyACKAIUIQAMDAsgAUEiEIqDgIAAQQAhAAwLCyABQdwAEIqDgIAAQQAhAAwKCyABQS8QioOAgABBACEADAkLIAQOCAQHBwcDBwIBBAsgBA4FBQYGBgQFCyACQQhqIAAQsIOAgAACQAJAAkACQAJAAkACQAJAIAIvAQhBAUYNAAJAAkACQCACLwEKIgRBgPgDcSIDQYCwA0YNACADQYC4A0cNASACQRE2AhAgACACQRBqEK+DgIAAIQAMCgsgAkEQaiAAELODgIAAIAItABBBAUYNByACLQARIQMgAkEQahD3goCAACADQdwARw0DIAJBEGogABCzg4CAACACLQAQQQFGDQcgAi0AESEDIAJBEGoQ94KAgAAgA0H1AEcNBiACQRBqIAAQsIOAgAAgAi8BEEEBRg0HIAIvARIhAyACQRBqEPOCgIAAIANBgPgDcUGAuANHDQggBEGA0HxqIgRB//8DcSAERw0EIANBgMh8aiIDQf//A3EgA0cNBQJAIARB//8DcUEKdCADQf//A3FyQYCABGoiBEH//8MASw0AIARBgPD/P3FBgLADRw0CCyACQQ42AhAgACACQRBqEK+DgIAAIQAMCQsgBEGA8ANxQYCwA0cNACACQQ42AhAgACACQRBqEK+DgIAAIQAMCAsgAkEIahDzgoCAAEEAIQAgAkEANgIQIAIgBCACQRBqEPiCgIAAIAEgAigCACACKAIEEImDgIAADA4LIAIoAgwhAAwNCyACQRQ2AhAgACACQRBqEK+DgIAAIQAMBQtB4J3AgABBIUGow8CAABCNhoCAAAALQeCdwIAAQSFBuMPAgAAQjYaAgAAACyACQRQ2AhAgACACQRBqEK+DgIAAIQAMAgsgAigCFCEADAELIAJBETYCECAAIAJBEGoQr4OAgAAhAAsgAkEIahDzgoCAAAwGCyABQQkQioOAgABBACEADAULIAFBDRCKg4CAAEEAIQAMBAsgAUEKEIqDgIAAQQAhAAwDCyABQQwQioOAgABBACEADAILIAFBCBCKg4CAAEEAIQAMAQsgAkELNgIQIAAgAkEQahCvg4CAACEACyACQSBqJICAgIAAIAALdwECfyOAgICAAEEgayIDJICAgIAAIANBCGogARChg4CAACADKAIMIQEgAygCCCEEIANBEGpBCGogAkEIaigCADYCACADIAIpAgA3AxAgA0EQaiAEIAEQooOAgAAhAiAAQQE2AgAgACACNgIEIANBIGokgICAgAALkwIBBH8jgICAgABBEGsiASSAgICAAAJAA0AgACgCBCECIAAoAgghAwJAA0ACQAJAAkAgAyACSQ0AQQAhBAwBCyAAKAIAIANqLQAAQajBwIAAai0AAEUNAUEBIQQLAkACQCACIANGDQAgBA0BQejAwIAAIAMgAhCMhoCAAAALIAFBBDYCAAwDCwJAIAAoAgAgA2otAAAiAkHcAEYNAAJAIAJBIkYNACABQQ82AgAMBAsgACADQQFqNgIIQQAhAwwFCyAAIANBAWo2AgggASAAEK6DgIAAIgM2AgAgAw0EIAEQ9IKAgAAMAwsgACADQQFqIgM2AggMAAsLCyAAIAEQr4OAgAAhAwsgAUEQaiSAgICAACADC9oFAQR/I4CAgIAAQSBrIgEkgICAgAAgAUEQaiAAELODgIAAAkACQAJAAkACQAJAIAEtABBBAUYNACABLQARIQIgAUEQahD3goCAAEEAIQMgAkGSf2oiBEEHTQ0BIAJBnn9qIgRBBE0NAiACQSJGDQUgAkEvRg0FIAJB3ABGDQUMBAsgASgCFCEDDAQLIAQOCAMCAgIDAgMBAwsgBA4FAgEBAQICCyABQQhqIAAQsIOAgAACQAJAAkACQAJAAkACQAJAIAEvAQhBAUYNAAJAAkAgAS8BCiIEQYD4A3EiAkGAsANGDQAgAkGAuANHDQEgAUERNgIQIAAgAUEQahCvg4CAACEDDAkLIAFBEGogABCzg4CAACABLQAQQQFGDQYgAS0AESECIAFBEGoQ94KAgAAgAkHcAEcNAiABQRBqIAAQs4OAgAAgAS0AEEEBRg0GIAEtABEhAiABQRBqEPeCgIAAIAJB9QBHDQUgAUEQaiAAELCDgIAAIAEvARBBAUYNBiABLwESIQIgAUEQahDzgoCAACACQYD4A3FBgLgDRw0HIARBgNB8aiIEQf//A3EgBEcNAyACQYDIfGoiAkH//wNxIAJHDQQgBEH//wNxQQp0IAJB//8DcXJBgIAEaiEECyABQQhqEPOCgIAAAkAgBEH//8MASw0AIARBgPD/P3FBgLADRw0KCyABQQ42AhAgACABQRBqEK+DgIAAIQMMCQsgASgCDCEDDAgLIAFBFDYCECAAIAFBEGoQr4OAgAAhAwwFC0HgncCAAEEhQcjDwIAAEI2GgIAAAAtB4J3AgABBIUHYw8CAABCNhoCAAAALIAFBFDYCECAAIAFBEGoQr4OAgAAhAwwCCyABKAIUIQMMAQsgAUERNgIQIAAgAUEQahCvg4CAACEDCyABQQhqEPOCgIAADAELIAFBCzYCECAAIAFBEGoQr4OAgAAhAwsgAUEgaiSAgICAACADC2sBAn8jgICAgABBIGsiAiSAgICAACACQQhqIAAQoYOAgAAgAigCDCEAIAIoAgghAyACQRBqQQhqIAFBCGooAgA2AgAgAiABKQIANwMQIAJBEGogAyAAEKKDgIAAIQEgAkEgaiSAgICAACABC8wCAQh/I4CAgIAAQSBrIgIkgICAgAACQAJAAkACQAJAIAEoAggiA0EEaiIEIANJDQAgBCABKAIEIgVLDQNBACEEQQAhBgNAAkAgBEEERw0AIABBADsBACAAIAY7AQIMBgsgAyAEaiIHIAVPDQIgAkEIaiABKAIAIANqIARqLQAAELGDgIAAIAIvAQohCCACLwEIIQkgASAHQQFqNgIIAkAgCQ0AIAJBCzYCECAAIAEgAkEQahCyg4CAAAwGCyAGQQR0QfD/A3EgCEH//wNxaiIGQf//A3EgBkcNAyAEQQFqIQQMAAsLQcCdwIAAQRxB+MDAgAAQjYaAgAAAC0GIwcCAACAHIAUQjIaAgAAAC0HAncCAAEEcQZjBwIAAEI2GgIAAAAsgASAFNgIIIAJBBDYCECAAIAEgAkEQahCyg4CAAAsgAkEgaiSAgICAAAskACAAIAFB/wFxQejDwIAAai0AACIBOwECIAAgAUH/AUc7AQALegECfyOAgICAAEEgayIDJICAgIAAIANBCGogARChg4CAACADKAIMIQEgAygCCCEEIANBEGpBCGogAkEIaigCADYCACADIAIpAgA3AxAgA0EQaiAEIAEQooOAgAAhAiAAQQE7AQAgAEEEaiACNgIAIANBIGokgICAgAALlwEBAX8jgICAgABBIGsiAiSAgICAACACQQhqIAEQoIOAgAACQAJAIAItAAhBAUYNAAJAAkAgAi0ACUEBRg0AIAJBBDYCECAAIAEgAkEQahC0g4CAAAwBCyAAIAItAAo6AAEgAEEAOgAACyACQQhqEPGCgIAADAELIABBAToAACAAQQRqIAIoAgw2AgALIAJBIGokgICAgAALegECfyOAgICAAEEgayIDJICAgIAAIANBCGogARChg4CAACADKAIMIQEgAygCCCEEIANBEGpBCGogAkEIaigCADYCACADIAIpAgA3AxAgA0EQaiAEIAEQooOAgAAhAiAAQQE6AAAgAEEEaiACNgIAIANBIGokgICAgAALdwECfyOAgICAAEEgayIDJICAgIAAIANBCGogARChg4CAACADKAIMIQEgAygCCCEEIANBEGpBCGogAkEIaigCADYCACADIAIpAgA3AxAgA0EQaiAEIAEQooOAgAAhAiAAQQE2AgAgACACNgIEIANBIGokgICAgAALBAAgAAtDACAAKAIAIQACQCABEMqGgIAADQACQCABEMuGgIAADQAgACABEJyGgIAADwsgACABEOGGgIAADwsgACABENuGgIAACxAAIAAgAjYCBCAAIAE2AgALUgEBfwJAAkAgACABaiICIABJDQAgAkF/aiIAIAJLDQEgACABbg8LQeDGwIAAQRxBxMbAgAAQjYaAgAAAC0GQx8CAAEEhQfzGwIAAEI2GgIAAAAsOACAAQYCABBC5g4CAAAsMACAAQQQQuYOAgAALAgALKwACQCAAQf//A3EgAEYNAEHQx8CAAEEhQbTHwIAAEI2GgIAAAAsgAEEQdAstAAJAIABB/////wNxIABGDQBB0MfAgABBIUG0x8CAABCNhoCAAAALIABBAnQLJQACQCAAQXhqIABLDQAPC0GQx8CAAEEhQZTIwIAAEI2GgIAAAAt5AQJ/AkAgACgCACIBQXxxIgJFDQAgAUECcQ0AIAIgAigCBEEDcSAAKAIEQXxxcjYCBAsCQCAAKAIEIgJBfHEiAUUNACABIAEoAgBBA3EgACgCAEF8cXI2AgAgACgCBCECCyAAIAJBA3E2AgQgACAAKAIAQQNxNgIACzYBAX8CQCAAKAIAQXxxIgEgAGtBeGoiACABSw0AIAAPC0GQx8CAAEEhQYDJwIAAEI2GgIAAAAuSAgEBfyACEL6DgIAAIQQCQAJAAkACQCADQcAAaiICIANJDQAgAkH/////AXEgAkcNASAEIAJBA3QiAiAEIAJLGyICQQhqIgMgAkkNAgJAAkAgAxC6g4CAACIDQAAiAkF/Rw0AQQEhAwwBCyACQf//A3EgAkcNBCADEL2DgIAAIgQQv4OAgAAgAkEQdCICQgA3AwBBACEDIAJBADYCCCACIAIgBGpBAnI2AgALIAAgAjYCBCAAIAM2AgAPC0HgxsCAAEEcQfTHwIAAEI2GgIAAAAtB0MfAgABBIUGEyMCAABCNhoCAAAALQeDGwIAAQRxB9MfAgAAQjYaAgAAAC0HQx8CAAEEhQdjKwIAAEI2GgIAAAAsEAEEQCwQAQQELpgUBCX8gAUF/aiIFQX9zIQYgBSABSyEHIAIoAgAhAUEAIQgCQANAIAFFDQECQANAAkAgASgCCCIJQQFxDQAgAUEIaiEJIAAQvoOAgAAhCiABEMGDgIAAIApJDQICQAJAAkACQAJAAkAgASgCAEF8cSILIAprIgwgC0sNACAHDQEgAyAAIAQoAhARgICAgAAAEL6DgIAAIQ0gCUEIaiIKIAlJDQIgCiANaiINIApJDQMCQAJAIA0gDCAGcSIKTQ0AIAUgCXENCiACIAEoAghBfHE2AgAgASEJDAELIApBeGoiCSAKSw0FIAsgCWsiAiALSw0GIAIQv4OAgAAgCUEANgIIIAlCADcCACAJIAEoAgBBfHE2AgACQCABKAIAIgpBfHEiAkUNACAKQQJxDQAgAiACKAIEQQNxIAlyNgIECyAJIAkoAgRBA3EgAXI2AgQgASABKAIIQX5xNgIIIAEgASgCAEEDcSAJciICNgIAIAJBAnFFDQAgASACQX1xNgIAIAkgCSgCAEECcjYCAAsgCSAJKAIAQQFyNgIAIAlBCGohCAwKC0GQx8CAAEEhQZDJwIAAEI2GgIAAAAtBkMfAgABBIUGgycCAABCNhoCAAAALQeDGwIAAQRxBsMnAgAAQjYaAgAAAC0HgxsCAAEEcQbDJwIAAEI2GgIAAAAtBkMfAgABBIUHAycCAABCNhoCAAAALQZDHwIAAQSFB0MnAgAAQjYaAgAAACyABIAlBfnE2AggCQAJAIAEoAgRBfHEiCQ0AQQAhCQwBC0EAIAkgCS0AAEEBcRshCQsgARDAg4CAAAJAIAEtAABBAnFFDQAgCSAJKAIAQQJyNgIACyACIAk2AgAgCRDBg4CAABogCSEBDAALCyACIAkoAgAiATYCAAwACwsgCAvMAQECfyOAgICAAEEQayIDJICAgIAAIAJBASACGyECAkACQCABRQ0AIAEQu4OAgAAhBCADIAAoAgA2AgwCQCAEIAIgA0EMakHgycCAAEHgycCAABDFg4CAACIBDQAgA0HgycCAACAEIAIQwoOAgABBACEBIAMoAgANACADKAIEIgEgAygCDDYCCCADIAE2AgwgBCACIANBDGpB4MnAgABB4MnAgAAQxYOAgAAhAQsgACADKAIMNgIADAELIAIhAQsgA0EQaiSAgICAACABC+cBAQN/AkAgAUUNACACRQ0AIAIQu4OAgAAaIAAoAgAhBCABQXhqIgIgAigCAEF+cTYCACACEMGDgIAAGiABQQA2AgACQAJAAkACQCABQXxqKAIAQXxxIgVFDQAgBS0AAEEBcUUNAQsgAigCACIFQXxxIgZFDQEgBUECcQ0BIAYtAABBAXENASABIAYoAghBfHE2AgAgBiACQQFyNgIIIAQhAgwCCyACEMCDgIAAAkAgAi0AAEECcUUNACAFIAUoAgBBAnI2AgALIAUQwYOAgAAaIAQhAgwBCyABIAQ2AgALIAAgAjYCAAsLUwECfyAAIAEoAgwgASgCCCICayIDEI2FgIAAIAAoAgggABCbhYCAAGogAiADEPOGgIAAGiABIAEoAgw2AgggACAAKAIIIANqNgIIIAEQyYOAgAALbgEBfyOAgICAAEEgayIBJICAgIAAIAEgADYCGANAIAFBEGogAUEYahDMhICAACABLQAQQQFxDQALIAFBCGogACgCACAAKAIEEMuFgIAAIAEgASkDCDcDGCABQRhqENCFgIAAIAFBIGokgICAgAALOgEBf0EMQQQQy4OAgAAiAkEIaiABQQhqKAIANgIAIAIgASkCADcCACAAQejKwIAANgIEIAAgAjYCAAsuAQF/AkACQCAARQ0AIAAgARDNgoCAACICDQEgACABEIWGgIAAAAsgASECCyACCxQAIABBKDYCBCAAQZDLwIAANgIACwkAIABBADYCAAsJACAAQQA2AgALDQBCor2vyfH32uXXAAsEAEEAC0oBAX8jgICAgABBEGsiAiSAgICAACACQQhqIAFBCGooAgA2AgAgAiABKQIANwMAIAAgAhDSg4CAABDTg4CAACACQRBqJICAgIAAC9gBAwF/AX4DfyOAgICAAEEQayICJICAgIAAAkACQCAAKAIAEYKAgIAAACIADQAQwYWAgABBASEBDAELIAE1AgQhAyABKAIIIQQgASgCACEFIAIgABCLhICAACACIAIoAgQ2AgwgAiACKAIAIgA2AghBACEBIABBACAAKAIAG0HY18CAABCMhICAACIAKAIEIQYgACgCACAFKQMAIAMgBCgCACIANQIIIAAQm4WAgACtIAYoApQBEYOAgIAAACACQQhqQQRyEI2EgIAACyACQRBqJICAgIAAIAELSQEBfyOAgICAAEEQayIBJICAgIAAAkAgAEUNAEG4y8CAAEHGACABQQhqQbzRwIAAQcjMwIAAEKqGgIAAAAsgAUEQaiSAgICAAAtMAQF/I4CAgIAAQRBrIgIkgICAgAACQCAApw0AIAJBEGokgICAgAAgAQ8LQbjLwIAAQcYAIAJBCGpBvNHAgABByMzAgAAQqoaAgAAACxAAIAAQ1oOAgAAQ04OAgAALogEBAn8jgICAgABBEGsiASSAgICAAAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAQQEhAAwBCyABIAAQi4SAgAAgASABKAIENgIMIAEgASgCACICNgIIQQAhACACQQAgAigCABtBiNbAgAAQjISAgAAiAigCAEIAIAIoAgQoAiQRhICAgAAAIAFBCGpBBHIQjYSAgAALIAFBEGokgICAgAAgAAsSACAAIAEQ2IOAgAAQ04OAgAALpgEBAn8jgICAgABBEGsiAiSAgICAAAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAQQEhAAwBCyACIAAQi4SAgAAgAiACKAIENgIMIAIgAigCACIDNgIIQQAhACADQQAgAygCABtBmNfAgAAQjISAgAAiAygCACABKQMAIAMoAgQoAoQBEYSAgIAAACACQQhqQQRyEI2EgIAACyACQRBqJICAgIAAIAALQgIBfwF+I4CAgIAAQRBrIgIkgICAgAAgAiAAIAEQ2oOAgAAgAikDACACKQMIENSDgIAAIQMgAkEQaiSAgICAACADC70BAgF/An4jgICAgABBEGsiAySAgICAAAJAAkAgASgCABGCgICAAAAiAQ0AEMGFgIAAQgEhBAwBCyADIAEQi4SAgAAgAyADKAIENgIMIAMgAygCACIBNgIIQgAhBCABQQAgASgCABtBsNrAgAAQjISAgAAiASgCACACNQIEIAI1AgBCACABKAIEKAK4ARGFgICAAAAhBSADQQhqQQRyEI2EgIAACyAAIAU3AwggACAENwMAIANBEGokgICAgAALYwEBfyOAgICAAEEgayICJICAgIAAIAJBCGpBEGogAUEQaigCADYCACACQQhqQQhqIAFBCGopAgA3AwAgAiABKQIANwMIIAAgAkEIahDcg4CAABDTg4CAACACQSBqJICAgIAAC+gBAwJ/AX4DfyOAgICAAEEQayICJICAgIAAAkACQCAAKAIAEYKAgIAAACIDDQAQwYWAgABBASEBDAELIAE1AgwhBCABKAIAIQUgASgCBCEAIAEoAgghBiABKAIQIQcgAiADEIuEgIAAIAIgAigCBDYCDCACIAIoAgAiAzYCCEEAIQEgA0EAIAMoAgAbQbjXwIAAEIyEgIAAIgMoAgAgBSkDACAANQIEIAA1AgAgBjUCBCAGNQIAIAQgBykDACADKAIEKAKMARGGgICAAAAgAkEIakEEchCNhICAAAsgAkEQaiSAgICAACABCxQAIAAgASACEN6DgIAAENODgIAAC6kBAQJ/I4CAgIAAQRBrIgMkgICAgAACQAJAIAAoAgARgoCAgAAAIgANABDBhYCAAEEBIQAMAQsgAyAAEIuEgIAAIAMgAygCBDYCDCADIAMoAgAiBDYCCEEAIQAgBEEAIAQoAgAbQcjXwIAAEIyEgIAAIgQoAgAgASkDACACrSAEKAIEKAKQARGHgICAAAAgA0EIakEEchCNhICAAAsgA0EQaiSAgICAACAACxQAIAAgASACEOCDgIAAENODgIAAC6gBAQF/I4CAgIAAQSBrIgMkgICAgAAgAyACNgIUIAMgATYCEAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAIANBEGoQkISAgABBASEBDAELIANBCGogABCOhICAACADIAMoAgw2AhwgAyADKAIIIgA2AhggABCRhICAACAAIAI2AgQgACABNgIAIANBGGpBBHIQj4SAgABBACEBCyADQSBqJICAgIAAIAELYwEBfyOAgICAAEEgayICJICAgIAAIAJBCGpBEGogAUEQaikCADcDACACQQhqQQhqIAFBCGopAgA3AwAgAiABKQIANwMIIAAgAkEIahDig4CAABDTg4CAACACQSBqJICAgIAAC7UCBAJ/AX4FfwR+I4CAgIAAQSBrIgIkgICAgAACQAJAIAAoAgARgoCAgAAAIgMNABDBhYCAAEEBIQEMAQsgATUCDCEEIAEoAhQhACABKAIQIQUgASgCCCEGIAEoAgQhByABKAIAIQggAkEQaiADEIuEgIAAIAIgAigCFDYCHCACIAIoAhAiAzYCGEEAIQEgA0EAIAMoAgAbQfjXwIAAEIyEgIAAIgMoAgAhCSADKAIEIQMgCCkDACEKIAcoAgAiBzUCCCELIAcQm4WAgAAhByAGKQMAIQwgBSgCACIFNQIIIQ0gAkEIaiAFEM+FgIAAIAkgCiALIAetIAwgBCANIAI1AgggADUCBCAANQIAIAMoApwBEYiAgIAAACACQRhqQQRyEI2EgIAACyACQSBqJICAgIAAIAELSgEBfyOAgICAAEEQayICJICAgIAAIAJBCGogAUEIaigCADYCACACIAEpAgA3AwAgACACEOSDgIAAENODgIAAIAJBEGokgICAgAAL1wEBBX8jgICAgABBEGsiAiSAgICAAAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAQQEhAQwBCyABKAIIIQMgASgCBCEEIAEoAgAhBSACIAAQi4SAgAAgAiACKAIENgIMIAIgAigCACIANgIIQQAhASAAQQAgACgCABtB6NfAgAAQjISAgAAiACgCBCEGIAAoAgAgBSkDACAEKAIAIgA1AgggABCbhYCAAK0gAykDACAGKAKYARGDgICAAAAgAkEIakEEchCNhICAAAsgAkEQaiSAgICAACABCxAAIAAQ5oOAgAAQ04OAgAALogEBAn8jgICAgABBEGsiASSAgICAAAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAQQEhAAwBCyABIAAQi4SAgAAgASABKAIENgIMIAEgASgCACICNgIIQQAhACACQQAgAigCABtB6NXAgAAQjISAgAAiAigCAEIAIAIoAgQoAiARhICAgAAAIAFBCGpBBHIQjYSAgAALIAFBEGokgICAgAAgAAtEAgF/AX4jgICAgABBEGsiAySAgICAACADIAAgASACEOiDgIAAIAMpAwAgAykDCBDUg4CAACEEIANBEGokgICAgAAgBAvHAQIBfwJ+I4CAgIAAQRBrIgQkgICAgAACQAJAIAEoAgARgoCAgAAAIgENABDBhYCAAEIBIQUMAQsgBCABEIuEgIAAIAQgBCgCBDYCDCAEIAQoAgAiATYCCCABQQAgASgCABtBgNrAgAAQjISAgAAiASgCACACNQIEIAI1AgAgAzUCBCADNQIAQn4gASgCBCgCtAERiYCAgAAAIQYgBEEIakEEchCNhICAAEIAIQULIAAgBjcDCCAAIAU3AwAgBEEQaiSAgICAAAtAAgF/AX4jgICAgABBEGsiASSAgICAACABIAAQ6oOAgAAgASkDACABKQMIENSDgIAAIQIgAUEQaiSAgICAACACC7EBAgF/An4jgICAgABBEGsiAiSAgICAAAJAAkAgASgCABGCgICAAAAiAQ0AEMGFgIAAQgEhAwwBCyACIAEQi4SAgAAgAiACKAIENgIMIAIgAigCACIBNgIIIAFBACABKAIAG0Go2MCAABCMhICAACIBKAIAIAEoAgQoAqgBEYqAgIAAACEEIAJBCGpBBHIQjYSAgABCACEDCyAAIAQ3AwggACADNwMAIAJBEGokgICAgAALEgAgACABEOyDgIAAENODgIAAC6MBAQJ/I4CAgIAAQRBrIgIkgICAgAACQAJAIAAoAgARgoCAgAAAIgANABDBhYCAAEEBIQAMAQsgAiAAEIuEgIAAIAIgAigCBDYCDCACIAIoAgAiAzYCCEEAIQAgA0EAIAMoAgAbQZjWwIAAEIyEgIAAIgMoAgAgAa0gAygCBCgCQBGEgICAAAAgAkEIakEEchCNhICAAAsgAkEQaiSAgICAACAAC0ICAX8BfiOAgICAAEEQayICJICAgIAAIAIgACABEO6DgIAAIAIpAwAgAikDCBDUg4CAACEDIAJBEGokgICAgAAgAwu9AQIBfwJ+I4CAgIAAQRBrIgMkgICAgAACQAJAIAEoAgARgoCAgAAAIgENABDBhYCAAEIBIQQMAQsgAyABEIuEgIAAIAMgAygCBDYCDCADIAMoAgAiATYCCCABQQAgASgCABtB0NrAgAAQjISAgAAiASgCACACNQIEIAI1AgBCfiABKAIEKAK8ARGFgICAAAAhBSADQQhqQQRyEI2EgIAAQgAhBAsgACAFNwMIIAAgBDcDACADQRBqJICAgIAACxQAIAAgASACEPCDgIAAENODgIAAC9cBAgN/An4jgICAgABBIGsiAySAgICAAAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAQQEhAAwBCyADQRBqIAAQi4SAgAAgAyADKAIUNgIcIAMgAygCECIENgIYQQAhACAEQQAgBCgCABtBmNjAgAAQjISAgAAiBCgCACEFIAQoAgQhBCABKQMAIQYgAigCACIBNQIIIQcgA0EIaiABEM+FgIAAIAUgBiAHIAM1AgggBCgCpAERi4CAgAAAIANBGGpBBHIQjYSAgAALIANBIGokgICAgAAgAAtCAgF/AX4jgICAgABBEGsiAiSAgICAACACIAAgARDyg4CAACACKQMAIAIpAwgQ1IOAgAAhAyACQRBqJICAgIAAIAMLuAECAX8CfiOAgICAAEEQayIDJICAgIAAAkACQCABKAIAEYKAgIAAACIBDQAQwYWAgABCASEEDAELIAMgARCLhICAACADIAMoAgQ2AgwgAyADKAIAIgE2AghCACEEIAFBACABKAIAG0Gg2cCAABCMhICAACIBKAIAIAIpAwBCACABKAIEKAKsARGMgICAAAAhBSADQQhqQQRyEI2EgIAACyAAIAU3AwggACAENwMAIANBEGokgICAgAALEAAgABD0g4CAABDTg4CAAAuiAQECfyOAgICAAEEQayIBJICAgIAAAkACQCAAKAIAEYKAgIAAACIADQAQwYWAgABBASEADAELIAEgABCLhICAACABIAEoAgQ2AgwgASABKAIAIgI2AghBACEAIAJBACACKAIAG0G41cCAABCMhICAACICKAIAQgAgAigCBCgCFBGEgICAAAAgAUEIakEEchCNhICAAAsgAUEQaiSAgICAACAACxAAIAAQ9oOAgAAQ04OAgAALogEBAn8jgICAgABBEGsiASSAgICAAAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAQQEhAAwBCyABIAAQi4SAgAAgASABKAIENgIMIAEgASgCACICNgIIQQAhACACQQAgAigCABtB2NXAgAAQjISAgAAiAigCAEIAIAIoAgQoAhwRhICAgAAAIAFBCGpBBHIQjYSAgAALIAFBEGokgICAgAAgAAtEAgF/AX4jgICAgABBEGsiAySAgICAACADIAAgASACEPiDgIAAIAMpAwAgAykDCBDUg4CAACEEIANBEGokgICAgAAgBAvGAQQBfwF+AX8BfiOAgICAAEEQayIEJICAgIAAAkACQCABKAIAEYKAgIAAACIBDQAQwYWAgABCASEFDAELIAQgARCLhICAACAEIAQoAgQ2AgwgBCAEKAIAIgE2AgggAUEAIAEoAgAbQejWwIAAEIyEgIAAIgEoAgQhBiABKAIAIAIQm4WAgACtIAM1AgQgBigCeBGMgICAAAAhByAEQQhqQQRyEI2EgIAAQgAhBQsgACAHNwMIIAAgBTcDACAEQRBqJICAgIAACxQAIAAgASACEPqDgIAAENODgIAAC7IBAQN/I4CAgIAAQRBrIgMkgICAgAACQAJAIAAoAgARgoCAgAAAIgANABDBhYCAAEEBIQAMAQsgAyAAEIuEgIAAIAMgAygCBDYCDCADIAMoAgAiBDYCCEEAIQAgBEEAIAQoAgAbQdTUwIAAEIyEgIAAIgQoAgQhBSAEKAIAIAEpAwAgAhCbhYCAAK0gBSgCDBGHgICAAAAgA0EIakEEchCNhICAAAsgA0EQaiSAgICAACAACxIAIAAgARD8g4CAABDTg4CAAAuqAQECfyOAgICAAEEQayICJICAgIAAAkACQCAAKAIAEYKAgIAAACIADQAQwYWAgABBASEADAELIAIgABCLhICAACACIAIoAgQ2AgwgAiACKAIAIgM2AghBACEAIANBACADKAIAG0HA2cCAABCMhICAACIDKAIAIAE1AgQgATUCACADKAIEKAJcEYeAgIAAACACQQhqQQRyEI2EgIAACyACQRBqJICAgIAAIAALEgAgACABEP6DgIAAENODgIAAC6oBAQJ/I4CAgIAAQRBrIgIkgICAgAACQAJAIAAoAgARgoCAgAAAIgANABDBhYCAAEEBIQAMAQsgAiAAEIuEgIAAIAIgAigCBDYCDCACIAIoAgAiAzYCCEEAIQAgA0EAIAMoAgAbQeDZwIAAEIyEgIAAIgMoAgAgATUCBCABNQIAIAMoAgQoAmQRh4CAgAAAIAJBCGpBBHIQjYSAgAALIAJBEGokgICAgAAgAAtCAgF/AX4jgICAgABBEGsiAiSAgICAACACIAAgARCAhICAACACKQMAIAIpAwgQ1IOAgAAhAyACQRBqJICAgIAAIAML3wEEAX8BfgF/AX4jgICAgABBIGsiAySAgICAAAJAAkAgASgCABGCgICAAAAiAQ0AEMGFgIAAQgEhBAwBCyADQRBqIAEQi4SAgAAgAyADKAIUNgIcIAMgAygCECIBNgIYIAFBACABKAIAG0H41sCAABCMhICAACIBKAIAIQUgASgCBCEBIAIoAgAiAjUCCCEEIANBCGogAhDPhYCAACAFIAQgAzUCCCABKAJ8EYyAgIAAACEGIANBGGpBBHIQjYSAgABCACEECyAAIAY3AwggACAENwMAIANBIGokgICAgAALEgAgACABEIKEgIAAENODgIAAC6YBAQJ/I4CAgIAAQRBrIgIkgICAgAACQAJAIAAoAgARgoCAgAAAIgANABDBhYCAAEEBIQAMAQsgAiAAEIuEgIAAIAIgAigCBDYCDCACIAIoAgAiAzYCCEEAIQAgA0EAIAMoAgAbQbDZwIAAEIyEgIAAIgMoAgAgASkDACADKAIEKAKwARGEgICAAAAgAkEIakEEchCNhICAAAsgAkEQaiSAgICAACAAC0QCAX8BfiOAgICAAEEQayIDJICAgIAAIAMgACABIAIQhISAgAAgAykDACADKQMIENSDgIAAIQQgA0EQaiSAgICAACAEC+kBBAF/AX4BfwF+I4CAgIAAQSBrIgQkgICAgAACQAJAIAEoAgARgoCAgAAAIgENABDBhYCAAEIBIQUMAQsgBEEQaiABEIuEgIAAIAQgBCgCFDYCHCAEIAQoAhAiATYCGCABQQAgASgCABtBiNfAgAAQjISAgAAiASgCACEGIAEoAgQhASACKQMAIQUgAygCACICNQIIIQcgBEEIaiACEM+FgIAAIAYgBSAHIAQ1AgggASgCgAERhYCAgAAAIQcgBEEYakEEchCNhICAAEIAIQULIAAgBzcDCCAAIAU3AwAgBEEgaiSAgICAAAsUACAAIAEgAhCGhICAABDTg4CAAAuwAQECfyOAgICAAEEQayIDJICAgIAAAkACQCAAKAIAEYKAgIAAACIADQAQwYWAgABBASEADAELIAMgABCLhICAACADIAMoAgQ2AgwgAyADKAIAIgQ2AghBACEAIARBACAEKAIAG0Go18CAABCMhICAACIEKAIAIAEpAwAgAjUCBCACNQIAIAQoAgQoAogBEYuAgIAAACADQQhqQQRyEI2EgIAACyADQRBqJICAgIAAIAALFAAgACABIAIQiISAgAAQ04OAgAALvQEBA38jgICAgABBEGsiAySAgICAAAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAQQEhAAwBCyADIAAQi4SAgAAgAyADKAIENgIMIAMgAygCACIENgIIQQAhACAEQQAgBCgCABtBiNjAgAAQjISAgAAiBCgCBCEFIAQoAgAgASkDACACKAIAIgQ1AgggBBCbhYCAAK0gBSgCoAERi4CAgAAAIANBCGpBBHIQjYSAgAALIANBEGokgICAgAAgAAtCAgF/AX4jgICAgABBEGsiAiSAgICAACACIAAgARCKhICAACACKQMAIAIpAwgQ1IOAgAAhAyACQRBqJICAgIAAIAMLtQECAX8CfiOAgICAAEEQayIDJICAgIAAAkACQCABKAIAEYKAgIAAACIBDQAQwYWAgABCASEEDAELIAMgARCLhICAACADIAMoAgQ2AgwgAyADKAIAIgE2AgggAUEAIAEoAgAbQeTUwIAAEIyEgIAAIgEoAgAgAikDACABKAIEKAIQEY2AgIAAACEFIANBCGpBBHIQjYSAgABCACEECyAAIAU3AwggACAENwMAIANBEGokgICAgAALcgECfyOAgICAAEEQayICJICAgIAAIAIgARC4hICAACIDNgIEAkAgAw0AIAJBBGoQroSAgABB9NDAgABBGCACQQhqQazRwIAAQYzRwIAAEKqGgIAAAAsgACADNgIEIAAgAUEEajYCACACQRBqJICAgIAACxwAAkAgAA0AQdDTwIAAQR0gARCohoCAAAALIAALGAAgACgCACIAIAAoAgBBf2oQ/4SAgAAaC3IBAn8jgICAgABBEGsiAiSAgICAACACIAEQtoSAgAAiAzYCBAJAIAMNACACQQRqEKaEgIAAQZHQwIAAQRAgAkEIakGc0cCAAEHk0MCAABCqhoCAAAALIAAgAzYCBCAAIAFBBGo2AgAgAkEQaiSAgICAAAsYACAAKAIAIgAgACgCAEEBahD/hICAABoLPAECfyAAKAIAIAAoAgQoAgARgYCAgAAAAkAgACgCBCIBKAIEIgJFDQAgACgCACACIAEoAggQzoKAgAALCxUAAkAgACgCAEUNACAAEJCEgIAACwuhAQMBfwF+AX8jgICAgABBMGsiAiSAgICAACACIAERgYCAgAAAIAApAgQhAyAAIAIpAwA3AgQgAEEMaiIBKAIAIQQgASACQQhqKAIANgIAIAAoAgAhASAAQQE2AgAgAkEQakEMaiAENgIAIAIgATYCECACIAM3AhQgAEEEaiEAAkAgAUUNACACQRBqQQhqEJGEgIAACyACQTBqJICAgIAAIAALHwACQCAAKAIAQQFGDQAgACABEJKEgIAADwsgAEEEagsPACAAKAIAIAEQroaAgAALqgEBAX8jgICAgABBwABrIgIkgICAgAAgAiABNgIMIAJBADYCGCACQgE3AxAgAkE0akEBNgIAIAJCATcCJCACQdjMwIAANgIgIAJB7YCAgAA2AjwgAiACQThqNgIwIAIgAkEMajYCOCACQRBqIAJBIGoQtYWAgAAQloSAgAAgAkEQahDFhYCAACAAQQhqIAIoAhg2AgAgACACKQMQNwIAIAJBwABqJICAgIAAC0gBAX8jgICAgABBEGsiASSAgICAAAJAIABFDQBB4MzAgABBNyABQQhqQczRwIAAQeDNwIAAEKqGgIAAAAsgAUEQaiSAgICAAAu9AQEDfyOAgICAAEEwayIDJICAgIAAAkAgAS0ADA0AIAEoAgQhBCABKAIAIQUgAyACEJ2FgIAAIAAgBSAEIAMoAgAgAygCBCABKAIIEImFgIAAIANBMGokgICAgAAPCyADQQhqQYTPwIAAQYGAgIAAEJOFgIAAIANBJGpBATYCACADQgE3AhQgA0HUzsCAADYCECADIAMpAwg3AyggAyADQShqNgIgIANBEGpBrM/AgAAQsYaAgAAQlIaAgAAAC6QCAgR/AX4jgICAgABB0ABrIgIkgICAgABBACEDIAJBACABKAIEEIGFgIAAIAJBIGpBCGoiBCABQQhqKQIANwMAIAIgASkCADcDICACQRBqIAJBIGogAhCXhICAACACQTBqQQhqIgEgAkEIaigCADYCACACIAIpAwA3AzACQAJAIAIoAhBBAUYNACACKAIUIQUgBCABKAIANgIAIAIgAikDMDcDICACQSBqIAUQmYSAgAAgAkHAAGpBCGogBCgCACIBNgIAIAIgAikDICIGNwNAIABBDGogATYCACAAIAY3AgQMAQsgACACKQIUNwIEIABBDGogAkEQakEMaigCADYCACACQTBqEJqEgIAAQQEhAwsgACADNgIAIAJB0ABqJICAgIAAC0EBAX8jgICAgABBEGsiAiSAgICAAAJAIAAoAgggAUkNACACQQhqIAAQnYWAgAAgACABNgIICyACQRBqJICAgIAACxIAIAAQzoWAgAAgABDQhYCAAAs9AQF/I4CAgIAAQRBrIgQkgICAgAAgBEEIakEAIAMgASACELyEgIAAIAAgBCkDCDcCACAEQRBqJICAgIAACxwAIAAoAgAgACgCBCABKAIAIAEoAgQQuYWAgAALCwAgARCehICAAAALQwEBfyOAgICAAEEgayIBJICAgIAAIAFBEGogABCVhICAACABQQhqIAFBEGoQxoSAgAAgASgCCCABKAIMEM+EgIAAAAsLACABEJ6EgIAAAAsLACABEKGEgIAAAAsLACAAEJ6EgIAAAAsCAAsYACAAQRhqEKSEgIAAIABBKGoQpISAgAALLwEBfyAAEKiEgIAAGiAAQQRqIQECQCAAKAIADQAgARCphICAAA8LIAEQqoSAgAALFwACQCAAKAIADQAgAEEEahCahICAAAsLFQACQCAAKAIARQ0AIAAQj4SAgAALCwIAC84IBAZ/AX4EfwJ+I4CAgIAAQdAAayIBJICAgIAAAkACQAJAAkACQCAAKAIAQQFGDQAgAUEoaiAAKAIEIgJBCGoQt4SAgAAgASABKAIsNgJEIAEgASgCKCIDNgJAIAFBwABqQQRyIQQgAykDAEIBUQ0CIAEgAkE8ahC4hICAACIFNgJMIAVFDQEgASAFNgI0IAEgAkHAAGoiBTYCMCABQTBqQQRyIQYCQAJAIAUoAgBBAkcNACACQSBqENmEgIAAIQcMAQsgBRCohICAACACQSBqENqEgIAAIQcLIAYQjYSAgAAgASACQSxqELiEgIAAIgU2AkwgBUUNAyABIAU2AjQgAkE4aigCAEE4bCEGIAEgAkEwaiICNgIwIAIoAgAhCCABQTBqQQRyIQlBACECA0ACQCAGIAJHDQAgAyAHNwMIIANCATcDACAJEI2EgIAADAYLAkACQAJAAkACQAJAAkACQAJAAkAgCCACaiIFKAIADgkAAQIDBAUGBwgACyAHENuEgIAADAgLIAFBCGogBUEEahDPhYCAACAHIAEoAgggASgCDBDchICAAAwHCyABQRhqIAVBBGoQz4WAgAAgASgCHCEKIAEoAhghCyABQRBqIAVBEGoQz4WAgAAgByALIAogASgCECABKAIUIAVBIGopAwAgBUEoaikDACAFQTBqKQMAEN2EgIAADAYLIAcgBUEIaikDACAFQRBqKQMAEN6EgIAADAULIAcgBUEQaikDACAFQRhqKQMAIAVBBGoQ34SAgAAMBAsgByAFQQRqQgAQ4ISAgAAMAwsgBUEwaikDACEMIAVBKGopAwAhDSABQSBqIAVBHGoQz4WAgAAgByAFQQRqQgAgDSAMIAVBEGogASgCICABKAIkEOGEgIAADAILIAcgBUEEahDihICAAAwBCyAHIAVBBGoQ44SAgAALIAJBOGohAgwACwsgASAAKAIEIgVBCGoQt4SAgAAgASABKAIENgJEIAEgASgCACICNgJAIAFBwABqQQRyIQQCQCACKQMAQgFRDQAgBUEgahCohICAACEHIAEgBUEwahCohICAADcDOCABIAc3AzAgAiABQTBqQQIQ2ISAgAAiBzcDCCACQgE3AwAMBAsgAikDCCEHDAMLIAFBzABqEK6EgIAAQfTQwIAAQRggAUEwakGs0cCAAEGM0cCAABCqhoCAAAALIAMpAwghBwwBCyABQcwAahCuhICAAEH00MCAAEEYIAFBMGpBrNHAgABBjNHAgAAQqoaAgAAACyAEEI+EgIAAIAEgAEEIahC4hICAACICNgJAAkAgAkUNACABIAI2AjQgASAAQQxqIgI2AjAgAi0AACECIAFBMGpBBHIQjYSAgAACQCACRQ0AIAcQ5oSAgAALIAFB0ABqJICAgIAAIAcPCyABQcAAahCuhICAAEH00MCAAEEYIAFBMGpBrNHAgABBjNHAgAAQqoaAgAAAC3wBAn8gACgCACIBIAEoAgBBf2o2AgACQCAAKAIAIgEoAgANACABQSBqEJqEgIAAIAFBMGoiAhDLhICAACACEMiEgIAAIAFBwABqEKuEgIAAIAAoAgAiASABKAIEQX9qNgIEIAAoAgAiACgCBA0AIABB0ABBCBDOgoCAAAsLWwEBfyAAKAIAIgEgASgCAEF/ajYCAAJAIAAoAgAiASgCAA0AIAFBCGoQo4SAgAAgACgCACIBIAEoAgRBf2o2AgQgACgCACIAKAIEDQAgAEHAAEEIEM6CgIAACwsXAAJAIAAoAgBBAkYNACAAEKSEgIAACwsCAAsCAAsVAAJAIAAoAgBFDQAgABCNhICAAAsLGAACQCAAKAIARQ0AIABBBGoQsISAgAALCzwBAn8gACgCACAAKAIEKAIAEYGAgIAAAAJAIAAoAgQiASgCBCICRQ0AIAAoAgAgAiABKAIIEM6CgIAACwsKACAAEJqEgIAACwIACwIACyoBAX8gACgCACIBKAIAIAEoAgQgACgCBCgCACAAKAIIKAIAEJKGgIAAAAtNAQF/I4CAgIAAQRBrIgQkgICAgAAgBCABNgIEIAQgADYCACAEIAM2AgwgBCACNgIIIAQgBEEIahCchICAACECIARBEGokgICAgAAgAgsjAQF/QQAhAQJAIAAoAgANACAAQX8Q/4SAgAAaIAAhAQsgAQtyAQJ/I4CAgIAAQRBrIgIkgICAgAAgAiABELaEgIAAIgM2AgQCQCADDQAgAkEEahCmhICAAEGR0MCAAEEQIAJBCGpBnNHAgABB5NDAgAAQqoaAgAAACyAAIAM2AgQgACABQQhqNgIAIAJBEGokgICAgAALJgEBfwJAIAAoAgBBAWoiAUEBTg0AQQAPCyAAIAEQ/4SAgAAaIAALUQEEf0EAIQICQAJAIAEoAgAiAyABKAIESQ0ADAELIANBARCWhYCAAGoiBCADSQ0AIAEoAgAhBSABIAQ2AgBBASECCyAAIAU2AgQgACACNgIAC1gCAn8BfkEAIQMCQAJAIAEoAgAgASgCBCIBakF/akEAIAFrcSIErSACrX4iBUIgiKdFDQAQlIWAgAAMAQsgACAENgIIIAAgBac2AgAgASEDCyAAIAM2AgQLQAACQAJAIAIgAUkNACAEIAJPDQEgAiAEEI+GgIAAAAsgASACEJCGgIAAAAsgACACIAFrNgIEIAAgAyABajYCAAtAAAJAAkAgAiABSQ0AIAQgAk8NASACIAQQj4aAgAAACyABIAIQkIaAgAAACyAAIAIgAWs2AgQgACADIAFqNgIACzMAAkAgASgCAA0AIAIgAyAEEKiGgIAAAAsgACABKQIANwIAIABBCGogAUEIaigCADYCAAuNAQEBfyOAgICAAEEgayIDJICAgIAAAkAgASgCAEEBRw0AIANBGGogAUEUaigCADYCACADQRBqIAFBDGopAgA3AwAgAyABKQIENwMIQdzRwIAAQSsgA0EIakGI0sCAACACEKqGgIAAAAsgACABKQIENwIAIABBCGogAUEMaigCADYCACADQSBqJICAgIAACzcBAX9BASEDAkAgAkEQRw0AIAAgASkAADcAASAAQQlqIAFBCGopAAA3AABBACEDCyAAIAM6AAALLQEBf0HQAEEIEMuDgIAAIgFCgYCAgBA3AwAgAUEIaiAAQcgAEPOGgIAAGiABC0YCAX8BfiOAgICAAEEQayICJICAgIAAIAJBCGogAUEAEMeFgIAAIAIpAwghAyAAQQA2AgggACADNwIAIAJBEGokgICAgAALPAACQCAAKAIIIAAoAgRHDQAgAEEBEI2FgIAACyAAEJuFgIAAIAAoAghqIAE6AAAgACAAKAIIQQFqNgIICxEAIAAgACgCCCABEMSEgIAAC2oBAX8jgICAgABBEGsiAySAgICAACADIAAgASACQQFBARDJhICAAAJAAkAgAygCAEEBRw0AIANBCGooAgBFDQFBmNLAgABBKEG8z8CAABCNhoCAAAALIANBEGokgICAgAAPCxCGhoCAAAALUAEBfyOAgICAAEEQayIDJICAgIAAIAMgAhDBhICAACADIAEgAhCYhYCAACAAQQhqIANBCGooAgA2AgAgACADKQMANwIAIANBEGokgICAgAALNwEBfyOAgICAAEEQayICJICAgIAAIAJBCGogARDPhYCAACAAIAIpAwg3AgAgAkEQaiSAgICAAAukAQECfyOAgICAAEEgayICJICAgIAAIAJBCGogARDPhYCAACACQRBqIAIoAgggAigCDBDXhoCAAEEBIQMCQAJAIAIoAhBBAUYNACAAIAEpAgA3AgQgAEEMaiABQQhqKAIANgIAQQAhAwwBCyAAIAEpAgA3AgQgAEEQaiACKQIUNwIAIABBDGogAUEIaigCADYCAAsgACADNgIAIAJBIGokgICAgAALIwEBfwJAIAAoAgQiAUUNACAAKAIAIAFBOGxBCBDOgoCAAAsLoAMBAn8jgICAgABBwABrIgYkgICAgABBACEHAkAgASgCBCACayADTw0AAkACQAJAIAVFDQAgBkEoaiABIAIgAxDKhICAAEEBIQcgBigCLCEDIAYoAihBAUcNASAGQQhqIAMgBkEwaigCABCVhYCAACAAIAYpAwg3AgQMAwsgAiADaiIDIAJPDQAgBkEgaiADQQAQlYWAgAAgACAGKQMgNwIEDAELIAZCuICAgIABNwM4IAZBKGogBkE4aiADELqEgIAAIAYoAighAgJAIAYoAiwiBw0AIAZBEGogAkEAEJWFgIAAIAAgBikDEDcCBAwBCwJAIAJBf0oNACAGQRhqIAZBABCVhYCAACAAIAYpAxg3AgQMAQsCQAJAIAEoAgQiBQ0AIAIgBxDNgoCAACEFDAELIAEoAgAgBUE4bEEIIAIQz4KAgAAhBQsCQAJAIAUNACAERQ0BIAIgBxCFhoCAAAALIAEgAzYCBCABIAU2AgBBACEHDAILIAAgAjYCBCAAQQhqIAc2AgALQQEhBwsgACAHNgIAIAZBwABqJICAgIAAC4EBAQF/I4CAgIAAQRBrIgQkgICAgAACQAJAIAIgA2oiAyACTw0AIARBCGogA0EAEJWFgIAAIAQoAgghAiAAQQhqIAQoAgw2AgBBASEDDAELIAEoAgRBAXQiAiADIAIgA0sbIQJBACEDCyAAIAM2AgAgACACNgIEIARBEGokgICAgAAL2wEBAn8gACgCCEE4bCEBIAAoAgAhAAJAA0AgAUUNAQJAAkAgACgCACICQQdLDQACQAJAAkACQAJAAkAgAg4IBwABBwIDBAUHCyAAQQRqEJqEgIAADAYLIABBBGoQmoSAgAAgAEEQahCahICAAAwFCyAAQQRqEJqEgIAADAQLIABBBGoQmoSAgAAMAwsgAEEEahCahICAACAAQRBqEJqEgIAAIABBHGoQmoSAgAAMAgsgAEEEahCahICAAAwBCyAAQQRqEJqEgIAACyAAQThqIQAgAUFIaiEBDAALCwtHAQJ/AkACQCABKAIAIgEoAggiAiABKAIMRw0AQQAhAwwBC0EBIQMgASACQQFqNgIIIAItAAAhAQsgACABOgABIAAgAzoAAAtxAgF/An4jgICAgABBEGsiASSAgICAAAJAQQApA4D6woAAIgJCAXwiAyACVA0AQQAgAzcDgPrCgAAgASACNwMIIAAgAUEIakEIEMWEgIAAIAFBEGokgICAgAAPC0HQz8CAAEEcQaTTwIAAEI2GgIAAAAsSAEG008CAACAAIAEQ34OAgAALSQEBfyOAgICAAEEQayICJICAgIAAIAIgATYCDCACIAA2AghBtNPAgAAgAkEIahD9g4CAAEGY0sCAAEEoQdDZwIAAEKGFgIAAAAsQAEEBQbjTwIAAEPWFgIAAC5cBAQF/I4CAgIAAQTBrIgIkgICAgAAgAiABNwMYIAJBCGogARDShICAAAJAAkAgAikDCKcNABCehYCAACAAQQA2AgAMAQsgAkEgakEAIAIpAxCnEIGFgIAAQbTTwIAAIAJBGGogAkEgahD5g4CAACAAQQhqIAJBIGpBCGooAgA2AgAgACACKQMgNwIACyACQTBqJICAgIAAC0oBAX8jgICAgABBEGsiAiSAgICAACACIAE3AwggAEG008CAACACQQhqEImEgIAAIgE3AwggACABQn9SrTcDACACQRBqJICAgIAAC3sBAX8jgICAgABBMGsiASSAgICAAEG008CAABDzg4CAACABQQhqQgAQ0YSAgAAgAUEgaiABQQhqQfTUwIAAQcQAQbjVwIAAEL2EgIAAIAFBCGogAUEgahDHhICAACAAIAFBCGpByNXAgAAQvoSAgAAgAUEwaiSAgICAAAtPAQF/I4CAgIAAQRBrIgEkgICAgABBtNPAgAAQ9YOAgAAgAUIAENGEgIAAIAAgAUH01MCAAEHEAEHY1cCAABC9hICAACABQRBqJICAgIAAC3sBAX8jgICAgABBMGsiASSAgICAAEG008CAABDlg4CAACABQQhqQgAQ0YSAgAAgAUEgaiABQQhqQfTUwIAAQcQAQejVwIAAEL2EgIAAIAFBCGogAUEgahDHhICAACAAIAFBCGpB+NXAgAAQvoSAgAAgAUEwaiSAgICAAAsYAEG008CAABDVg4CAACAAQgAQ0YSAgAALWwICfwF+I4CAgIAAQRBrIgEkgICAgAAgAUEIaiICQgA3AwAgAUIANwMAQbTTwIAAIAEQ64OAgAAgASkDACEDIAAgAikDADcDCCAAIAM3AwAgAUEQaiSAgICAAAuYAwIEfwF+I4CAgIAAQdAAayICJICAgIAAIAIgATYCLCACIAA2AigCQAJAAkACQAJAIAFB/////wFxIAFHDQAgAkEwakEAIAFBA3QQgYWAgAAgAkEgakEAIAEQnIWAgAAgAiACKQMgNwNAA0AgAkEYaiACQcAAahC5hICAACACKAIYRQ0FIAIoAhwiA0H/////AXEgA0cNAiADQQFqIgRB/////wFxIARHDQMgAkEQaiACQTBqEJ2FgIAAIAJBCGogA0EDdCIFIARBA3QgAigCECACKAIUELyEgIAAIAMgAU8NBCACKAIMIQMgAigCCCEEIAIgACAFaikDADcDSCAEIAMgAkHIAGpBCBCOhYCAAAwACwtB8M/AgABBIUGo1sCAABCNhoCAAAALQfDPwIAAQSFBuNbAgAAQjYaAgAAAC0Hwz8CAAEEhQcjWwIAAEI2GgIAAAAtB2NbAgAAgAyABEIyGgIAAAAtBtNPAgAAgAkEwaiACQShqEPeDgIAAIQYgAkEwahCahICAACACQdAAaiSAgICAACAGCz4CAX8BfiOAgICAAEEQayIBJICAgIAAIAEgADYCDEG008CAACABQQxqEP+DgIAAIQIgAUEQaiSAgICAACACC0UBAX8jgICAgABBEGsiAiSAgICAACACIAE2AgwgAiAANwMAQbTTwIAAIAIgAkEMahCDhICAACEAIAJBEGokgICAgAAgAAs4AQF/I4CAgIAAQRBrIgEkgICAgAAgASAANwMIQbTTwIAAIAFBCGoQ14OAgAAgAUEQaiSAgICAAAtIAQF/I4CAgIAAQRBrIgMkgICAgAAgAyACNgIMIAMgATYCCCADIAA3AwBBtNPAgAAgAyADQQhqEIWEgIAAIANBEGokgICAgAALnQEBAX8jgICAgABB0ABrIggkgICAgAAgCCAGNwMoIAggBTcDICAIIAI2AhQgCCABNgIQIAggADcDCCAIIAQ2AhwgCCADNgIYIAggBzcDMCAIIAhBMGo2AkggCCAIQSBqNgJEIAggCEEYajYCQCAIIAhBEGo2AjwgCCAIQQhqNgI4QbTTwIAAIAhBOGoQ24OAgAAgCEHQAGokgICAgAALSwEBfyOAgICAAEEgayIDJICAgIAAIAMgAjcDGCADIAE3AxAgAyAANwMIQbTTwIAAIANBCGogA0EQahDdg4CAACADQSBqJICAgIAAC2gBAX8jgICAgABBMGsiBCSAgICAACAEIAI3AxAgBCABNwMIIAQgADcDACAEIAM2AhwgBCAEQRxqNgIoIAQgBEEIajYCJCAEIAQ2AiBBtNPAgAAgBEEgahDRg4CAACAEQTBqJICAgIAAC2QBAX8jgICAgABBMGsiAySAgICAACADIAE2AhQgAyAANwMIIAMgAjcDGCADIANBGGo2AiggAyADQRRqNgIkIAMgA0EIajYCIEG008CAACADQSBqEOODgIAAIANBMGokgICAgAALpAEBAX8jgICAgABB0ABrIggkgICAgAAgCCAENwMgIAggAzcDGCAIIAE2AgwgCCAANwMAIAggAjcDECAIIAU2AiwgCCAHNgI0IAggBjYCMCAIIAhBMGo2AkwgCCAIQSxqNgJIIAggCEEYajYCRCAIIAhBEGo2AkAgCCAIQQxqNgI8IAggCDYCOEG008CAACAIQThqEOGDgIAAIAhB0ABqJICAgIAAC0EBAX8jgICAgABBEGsiAiSAgICAACACIAE2AgwgAiAANwMAQbTTwIAAIAIgAkEMahCHhICAACACQRBqJICAgIAAC0EBAX8jgICAgABBEGsiAiSAgICAACACIAE2AgwgAiAANwMAQbTTwIAAIAIgAkEMahDvg4CAACACQRBqJICAgIAACw4AQbTTwIAAEOmDgIAAC8oBAQF/I4CAgIAAQTBrIgIkgICAgAAgAiABNwMIAkACQAJAAkACQEG008CAACACQQhqEPGDgIAAIgFCAlYNACABpw4DAQIDAQtBuNjAgABBF0HQ2MCAABChhYCAAAALIABBADYCAAwCCyACQSBqQgAQ0YSAgAAgAkEQaiACQSBqQeDYwIAAQTBBkNnAgAAQvYSAgAAgAEEMaiACQRhqKAIANgIAIAAgAikDEDcCBCAAQQE2AgAMAQsgAEECNgIACyACQTBqJICAgIAACzgBAX8jgICAgABBEGsiASSAgICAACABIAA3AwhBtNPAgAAgAUEIahCBhICAACABQRBqJICAgIAACz8BAX8jgICAgABBEGsiAiSAgICAACACIAE2AgwgAiAANgIIQbTTwIAAIAJBCGoQ+4OAgAAgAkEQaiSAgICAAAuIAQIBfwF+I4CAgIAAQRBrIgQkgICAgAAgBCABNgIEIAQgADYCACAEIAM2AgwgBCACNgIIAkACQAJAQbTTwIAAIAQgBEEIahDng4CAACIFQgFWDQBBACECIAWnDgICAQILQbjYwIAAQRdB8NnAgAAQoYWAgAAAC0EBIQILIARBEGokgICAgAAgAgujAQIBfwF+I4CAgIAAQSBrIgMkgICAgAAgAyACNgIMIAMgATYCCAJAAkACQAJAQbTTwIAAIANBCGoQ2YOAgAAiBEIBVg0AIASnDgIBAgELQbjYwIAAQRdBkNrAgAAQoYWAgAAACyAAQQA2AgAMAQsgA0EQakIAENGEgIAAIAAgA0EQakH01MCAAEHEAEGg2sCAABC9hICAAAsgA0EgaiSAgICAAAt4AgF/AX4jgICAgABBEGsiAiSAgICAACACIAE2AgwgAiAANgIIAkACQAJAQbTTwIAAIAJBCGoQ7YOAgAAiA0IBVg0AQQAhACADpw4CAgECC0G42MCAAEEXQcDawIAAEKGFgIAAAAtBASEACyACQRBqJICAgIAAIAALDAAgAEJ+ENGEgIAAC80BAQV/I4CAgIAAQRBrIgIkgICAgABBACEDAkAgAUF+akE+Sw0AIAJBCGogACABEJ+FgIAAIAIoAgwhBCACKAIIIQBBASEBA0AgASEFAkAgBCAARw0AIAVBAXMhAwwCC0EAIQNBACEBAkAgAC0AACIGQZ9/akH/AXFBGkkNAEEAIQEgBkFQakH/AXFBCkkNAEEBIQEgBkFTakECSQ0AIAZB3wBGDQBBACEDDAILIABBAWohACAFIAFxRQ0ACwsgAkEQaiSAgICAACADQQFxC5cBAQF/I4CAgIAAQdAAayICJICAgIAAIAJBNGpCADcCACACQShqIAFBCGooAgA2AgAgAkKAgICAIDcCPCACQoCAgICAATcCLCACQgA3AxAgAkEANgIIIAIgASkCADcDICACQQhqEMCEgIAAIQEgAEEMakEAOgAAIABBADYCCCAAIAE2AgQgAEEANgIAIAJB0ABqJICAgIAAC6oCAQR/I4CAgIAAQcAAayIDJICAgIAAAkACQCABKAIAQQFGDQAgAyABKAIEIgRBLGoQtoSAgAAiBTYCCCAFRQ0BIAMgBTYCBCADIARBMGoiBjYCACADQQhqIAJBOBDzhoCAABogA0EEciECAkAgBEE4aigCACIFIARBNGooAgBHDQAgBkEBEMOEgIAAIAQoAjghBQsgBCgCMCAFQThsaiADQQhqQTgQ84aAgAAaIAQgBCgCOEEBajYCOCACEI+EgIAAIABBCGogAUEIaikCADcCACAAIAEpAgA3AgAgA0HAAGokgICAgAAPC0Hg2sCAAEElQeTbwIAAEKGFgIAAAAsgA0EIahCmhICAAEGR0MCAAEEQIANBCGpBnNHAgABB5NDAgAAQqoaAgAAAC1gBAX8jgICAgABB0ABrIgIkgICAgAAgAkEIakEIaiABQQhqKQIANwMAIAIgASkCADcDCCACQQA2AhggACACQQhqIAJBGGoQ7oSAgAAgAkHQAGokgICAgAALpQEBAX8jgICAgABB0ABrIgckgICAgAAgB0EIakEIaiABQQhqKQIANwMAIAcgASkCADcDCCAHQcAAaiAFNwMAIAdBJGogAkEIaigCADYCACAHQTBqIANBCGooAgA2AgAgByAENwM4IAcgBjcDSCAHQQI2AhggByACKQIANwIcIAcgAykCADcDKCAAIAdBCGogB0EYahDuhICAACAHQdAAaiSAgICAAAtvAQF/I4CAgIAAQdAAayIEJICAgIAAIARBCGpBCGogAUEIaikCADcDACAEIAEpAgA3AwggBEEoaiADNwMAIARBGGpBCGogAjcDACAEQQM2AhggACAEQQhqIARBGGoQ7oSAgAAgBEHQAGokgICAgAALcgEBfyOAgICAAEHQAGsiAySAgICAACADQQhqQQhqIAFBCGopAgA3AwAgAyABKQIANwMIIANBJGogAkEIaigCADYCACADQQU2AhggAyACKQIANwIcIAAgA0EIaiADQRhqEO6EgIAAIANB0ABqJICAgIAAC7gBAQF/I4CAgIAAQdAAayIHJICAgIAAIAdBCGpBCGogAUEIaikCADcDACAHIAEpAgA3AwggB0HIAGogBDcDACAHQSRqIAJBCGooAgA2AgAgB0EwaiAFQQhqKAIANgIAIAdBPGogBkEIaigCADYCACAHIAM3A0AgB0EGNgIYIAcgAikCADcCHCAHIAUpAgA3AyggByAGKQIANwI0IAAgB0EIaiAHQRhqEO6EgIAAIAdB0ABqJICAgIAAC3IBAX8jgICAgABB0ABrIgMkgICAgAAgA0EIakEIaiABQQhqKQIANwMAIAMgASkCADcDCCADQSRqIAJBCGooAgA2AgAgA0EHNgIYIAMgAikCADcCHCAAIANBCGogA0EYahDuhICAACADQdAAaiSAgICAAAuFAgEDfyOAgICAAEEgayIDJICAgIAAAkACQCACKAIAQQFGDQAgA0EIaiIEIAFBCGopAgA3AwAgAyABKQIANwMAIAMgAigCBCIBQTxqELaEgIAAIgU2AhwgBUUNASADIAU2AhQgAyABQcAAaiIFNgIQIAUQq4SAgAAgAUHIAGogBCkDADcCACAFIAMpAwA3AgAgA0EQakEEchCPhICAACAAQQhqIAJBCGopAgA3AgAgACACKQIANwIAIANBIGokgICAgAAPC0H028CAAEEeQZTcwIAAEKGFgIAAAAsgA0EcahCmhICAAEGR0MCAAEEQIANBEGpBnNHAgABB5NDAgAAQqoaAgAAACwsAIAAQqISAgAAaC+cBAQN/I4CAgIAAQTBrIgIkgICAgAAgAkEYaiABEM+FgIAAIAJBIGogAigCGCACKAIcEIiGgIAAIAJBEGogAkEgahDPhYCAAAJAAkAgAigCECIDIAIoAhQiBEGk3MCAAEEHELWEgIAARQ0AQQAhAyAAQQA6AAEMAQsCQCADIARBq9zAgABBCRC1hICAAEUNACAAQQE6AAFBACEDDAELIAJBCGpBtNzAgABBEhDuhYCAACAAQQRqIAIpAwg3AgBBASEDCyAAIAM6AAAgAkEgahCahICAACABEJqEgIAAIAJBMGokgICAgAALHAAgACABKQIANwIAIABBCGogAUEIaigCADYCAAtHAQF/I4CAgIAAQRBrIgIkgICAgAAgAkEIaiABEM+FgIAAIAAgAigCCCACKAIMEPqEgIAAIAEQmoSAgAAgAkEQaiSAgICAAAu6CQIFfwF+I4CAgIAAQYABayIDJICAgIAAIANBOjYCSCADQSBqIANByABqQQRBARCbhICAACADQTo2AmggAyACNgJkIANBADYCYCADIAI2AlwgAyABNgJYIAMgAygCSDYCcCADIAMoAiQ2AmwgA0HwAGohBCACIQVBACEGIAIhBwJAAkACQAJAAkADQAJAAkACQAJAIAUgBkkNACAHIAVPDQELEJ6FgIAADAELIANBGGogAygCbCADQdgAampBF2otAAAgAygCWCAGaiAFIAZrELOGgIAAIAMoAhhBAUYNASADIAMoAmQ2AmALQQAhBkEAIQdBACEFDAILIAMgAygCHCADKAJgakEBaiIGNgJgAkACQCAGIAMoAmwiBUkNACADKAJcIAZJDQAgAygCWCEHIAMgBTYCLCADIAcgBiAFayIGajYCKCADQRBqQQAgBSAEQQQQu4SAgAAgAyADKQMQNwNIIANBKGogA0HIAGoQnISAgAANASADKAJgIQYLIAMoAmQhBSADKAJcIQcMAQsLAkAgBkUNACAGIAJGDQAgBiACTw0CIAEgBmosAABBv39MDQILIANB2ABqIAEgBhDKhYCAACADQcgAaiADQdgAahD3hICAAEEBIQUCQCADLQBIQQFGDQAgAy0ASSEHIAMgAiAGayIFNgIsIAMgASAGaiIGNgIoIANBATYCeCADIAU2AnwCQAJAIAVBAUsNACAFDgIFAQULIAYsAAFBv39MDQQLIAZBAWohASAFQX9qIQJBACEGQQAhBQwBCyADKAJMIgdBgH5xIQYgA0HQAGooAgAhAQsgA0HEAGogAjYCACADQThqQQhqIAE2AgAgAyAFNgI4IAMgBiAHQf8BcXIiBjYCPAJAAkAgBQ0AIANBOGoQr4SAgAAgA0EAOwFkIANB8M3AgAA2AmAgAyACNgJcIAMgATYCWCADQThqIANB2ABqEJiEgIAAIANBOGpBBHIhBiADKAI4QQFHDQEgA0HYAGpBCGogBkEIaigCADYCACADIAYpAgA3A1ggAyADQdgAahDKg4CAACADKAIAIQYgAEEIakHoysCAADYCACAAIAY2AgQgAEEBNgIAIANBOGoQpYSAgAAMBQsgACAGNgIEIABBATYCACAAQQhqIAE2AgAMBAsgA0EoakEIaiICIAZBCGooAgAiBTYCACADIAYpAgA3AyggBUHAAEEgIAdBAXEiBhsiB0cNAiADQcgAaiAHQQFyEMGEgIAAIANByABqIAYQwoSAgAAgA0E4akEIaiIFIAIoAgA2AgAgAyADKQMoNwM4IAMgA0E4ahCbhYCAACIGNgJgIAMgBjYCWCADIAMoAjw2AlwgAyAGIAUoAgBqNgJkIANByABqIANB2ABqEMiDgIAAIABBDGogA0HIAGpBCGooAgA2AgAgACADKQNINwIEIABBADYCAAwDCyABIAJBACAGEJKGgIAAAAsgAyADQfwAajYCYCADIANB+ABqNgJcIAMgA0EoajYCWCADQdgAahC0hICAAAALIANBCGpBxtzAgABBIBDuhYCAACADKQMIIQggAEEBNgIAIAAgCDcCBCADQShqEJqEgIAACyADQYABaiSAgICAAAsJACAAQgA3AgALFABBiPrCgABB7oCAgAAQk4SAgAALEAAgACACNwMIIAAgATcDAAsQACAAIAI3AwggACABNwMACxQBAX8gACgCACECIAAgATYCACACC0YCAX8BfiOAgICAAEEQayICJICAgIAAIAJBCGogAUEAEMeFgIAAIAIpAwghAyAAQQA2AgggACADNwIAIAJBEGokgICAgAALjgECAX8BfiOAgICAAEEgayIDJICAgIAAAkACQCABQf8BcUUNACADQRBqIAIQgIWAgAAgA0EQahCbhYCAACABIAIQ8oaAgAAaIABBCGogAjYCACAAIAMpAxA3AgAMAQsgA0EIaiACQQEQx4WAgAAgAykDCCEEIAAgAjYCCCAAIAQ3AgALIANBIGokgICAgAALQwAgACgCACEAAkAgARDKhoCAAA0AAkAgARDLhoCAAA0AIAAgARCchoCAAA8LIAAgARDhhoCAAA8LIAAgARDbhoCAAAsPACAAKAIAIAEQ04aAgAALAgALAgALPQEBfyOAgICAAEEQayIEJICAgIAAIARBCGpBACADIAEgAhCHhYCAACAAIAQpAwg3AgAgBEEQaiSAgICAAAtAAAJAAkAgAiABSQ0AIAQgAk8NASACIAQQj4aAgAAACyABIAIQkIaAgAAACyAAIAIgAWs2AgQgACADIAFqNgIAC2cBA38CQAJAIAEoAgAiAiABKAIERw0AQQAhAgwBCyABIAJBAWo2AgACQCABKAIIIgNBAWoiBCADTw0AQcDdwIAAQRxBsN3AgAAQjYaAgAAACyABIAQ2AggLIAAgAjYCBCAAIAM2AgALwAUBBX8jgICAgABBoAJrIgYkgICAgAAgBS0AACEHIAZBoAFqQf8BQYABEPKGgIAAGiAGQQA2AiggBiAFQTpqNgIkIAYgBTYCIANAIAZBGGogBkEgahCIhYCAAAJAIAYoAhwiBQ0AIAZBIGogBkGgAWpBgAEQ84aAgAAaIAZBADYCqAEgBiABNgKgASAGIAEgAmo2AqQBQQAhCAJAAkADQCAGQRBqIAZBoAFqEIiFgIAAAkAgBigCFCIFDQAgB0H/AXEhBQNAAkACQCACRQ0AIAEtAAAgBUYNAQsgBiADIAQgCBCGhYCAACAGKAIEIglBAXYhCiAJIAYoAgAiBWpBf2ohCQJAA0AgCkUNASAFLQAAIQQgBSAJLQAAOgAAIAkgBDoAACAKQX9qIQogBUEBaiEFIAlBf2ohCQwACwsgAEEANgIAIAAgCDYCBAwFCwJAIAggBEkNACAAQgE3AgAMBQsgAUEBaiEBIAMgCGpBADoAACACQX9qIQIgCEEBaiEIDAALCyAGKAIQIQkCQAJAAkAgBSwAACIFQQBIDQAgBkEgaiAFQf8BcSIKai0AACIFQf8BRg0BIAZBCGogAyAEIAgQhoWAgAAgBigCCCEJIAYoAgwhCgNAIApFDQMgCSAJLQAAQTpsIAVqIgU6AAAgCkF/aiEKIAlBAWohCSAFQQh2IQUMAAsLIABBAjYCBCAAQQhqIAk2AgAMAwsgAEEBNgIEIABBDGogCTYCACAAQQhqIAo2AgAMAgsDQCAFRQ0BAkAgCCAETw0AIAMgCGogBToAACAFQQh2IQUgCEEBaiEIDAELCwsgAEEANgIECyAAQQE2AgALIAZBoAJqJICAgIAADwsgBSwAACIFQf8BcSEJAkAgBUEASA0AIAZBoAFqIAlqIAYoAhg6AAAMAQsLQbTewIAAIAlBgAEQjIaAgAAAC9sCAQF/I4CAgIAAQcAAayICJICAgIAAAkACQAJAAkACQCAAKAIADgQBAgMAAQtBxN7AgABBKEHs3sCAABCNhoCAAAALIAJBPGpBADYCACACQdjgwIAANgI4IAJCATcCLCACQdDgwIAANgIoIAEgAkEoahDJhoCAACEADAILIAIgACgCBDYCDCACIABBCGooAgA2AiQgAkE8akECNgIAIAJBHGpBhICAgAA2AgAgAkICNwIsIAJB/N/AgAA2AiggAkGEgYCAADYCFCACIAJBEGo2AjggAiACQSRqNgIYIAIgAkEMajYCECABIAJBKGoQyYaAgAAhAAwBCyACIAAoAgQ2AiQgAkE8akEBNgIAIAJCATcCLCACQbzfwIAANgIoIAJBhICAgAA2AhQgAiACQRBqNgI4IAIgAkEkajYCECABIAJBKGoQyYaAgAAhAAsgAkHAAGokgICAgAAgAAvGAgEBfyOAgICAAEEgayICJICAgIAAAkACQAJAAkACQCAAKAIADgQBAgMAAQsgAkEQaiABQdjgwIAAQQ8QzYaAgAAgAkEQahC2hoCAACEADAMLIAJBEGogAUG84cCAAEEOEM2GgIAAIAJBEGoQtoaAgAAhAAwCCyACQRBqIAFBkOHAgABBEBDMhoCAACACIABBBGo2AgwgAkEQakGg4cCAAEEJIAJBDGpBrOHAgAAQp4aAgAAaIAIgAEEIajYCDCACQRBqQfjgwIAAQQUgAkEMakGA4cCAABCnhoCAABogAkEQahC0hoCAACEADAELIAJBEGogAUHn4MCAAEEREMyGgIAAIAIgAEEEajYCDCACQRBqQfjgwIAAQQUgAkEMakGA4cCAABCnhoCAABogAkEQahC0hoCAACEACyACQSBqJICAgIAAIAALNgEBfyAAIAIgAWsiAhCNhYCAACAAIAAoAggiAyACajYCCCADIAAoAgBqIAIgASACEI6FgIAACxEAIAAgACgCCCABEJmFgIAAC5MCAQF/I4CAgIAAQeAAayIEJICAgIAAIAQgATYCCCAEIAM2AgwCQCABIANHDQAgACACIAEQ84aAgAAaIARB4ABqJICAgIAADwsgBEEoakEUakGIgICAADYCACAEQTRqQYmBgIAANgIAIARBEGpBFGpBAzYCACAEIARBCGo2AkAgBCAEQQxqNgJEIARByABqQRRqQQA2AgAgBEIDNwIUIARBiOLAgAA2AhAgBEGJgYCAADYCLCAEQdziwIAANgJYIARCATcCTCAEQdTiwIAANgJIIAQgBEEoajYCICAEIARByABqNgI4IAQgBEHEAGo2AjAgBCAEQcAAajYCKCAEQRBqQajjwIAAELGGgIAAEJSGgIAAAAtDACAAKAIAIQACQCABEMqGgIAADQACQCABEMuGgIAADQAgACABEJyGgIAADwsgACABEOGGgIAADwsgACABENuGgIAACxQAIAAoAgAgACgCBCABENKGgIAACxAAIAAgAjYCBCAAIAE2AgALEAAgACACNgIEIAAgATYCAAsQACAAIAI2AgQgACABNgIACwIACxAAIAAgAjYCBCAAIAE2AgALBAAgAAsnAQF/QQEhAwJAIAJBCEcNACAAIAEpAAA3AAFBACEDCyAAIAM6AAALEQAgACABIAEgAmoQjIWAgAALagEBfyOAgICAAEEQayIDJICAgIAAIAMgACABIAJBAUEBEJqFgIAAAkACQCADKAIAQQFHDQAgA0EIaigCAEUNAUG448CAAEEoQYDkwIAAEI2GgIAAAAsgA0EQaiSAgICAAA8LEIaGgIAAAAuFAgECf0EAIQYCQCABKAIEIgcgAmsgA08NACACIANqIgMgAkkhAgJAAkACQAJAIAVFDQAgAkUNASAAIAM2AgQgAEEIakEANgIADAMLIAJFDQEgACADNgIEIABBCGpBADYCAAwCCyAHQQF0IgIgAyACIANLGyEDCwJAIANBf0oNACAAQQhqQQA2AgAMAQsCQAJAIAcNACADQQEQzYKAgAAhAgwBCyABKAIAIAdBASADEM+CgIAAIQILAkACQCACDQAgBEUNASADQQEQhYaAgAAACyABIAM2AgQgASACNgIADAILIAAgAzYCBEEBIQYgAEEIakEBNgIADAELQQEhBgsgACAGNgIACwcAIAAoAgALEAAgACACNgIEIAAgATYCAAsWACAAIAEoAgg2AgQgACABKAIANgIACwIACxMAIAAgASACajYCBCAAIAE2AgALDABC5K7ChZebpYgRCz8BAX8jgICAgABBEGsiAySAgICAACADIAE2AgwgAyAANgIIIANBCGpBkOTAgABBACACELGGgIAAEPeFgIAAAAsCAAsCAAslAAJAIAEoAgANABDyhYCAAAALIABBpOTAgAA2AgQgACABNgIAC2YBAn8gASgCACECIAFBADYCAAJAAkAgAkUNACABKAIEIQNBCEEEEM2CgIAAIgFFDQEgASADNgIEIAEgAjYCACAAQaTkwIAANgIEIAAgATYCAA8LEPKFgIAAAAtBCEEEEIWGgIAAAAsQACAAIAI2AgQgACABNgIACxQAIAAoAgAgACgCBCABENCGgIAAC0MAIAAoAgAhAAJAIAEQyoaAgAANAAJAIAEQy4aAgAANACAAIAEQ2oaAgAAPCyAAIAEQ44aAgAAPCyAAIAEQ4oaAgAALAgALBwAgACgCAAsWACAAIAEoAgg2AgQgACABKAIANgIACxIAIAFBtOTAgABBCBDIhoCAAAuKCgEBfyOAgICAAEEwayICJICAgIAAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAC0AAA4SAAECAwQFBgcICQoLDA0ODxARAAsgAiAALQABOgAIIAJBLGpBATYCACACQgI3AhwgAkGE58CAADYCGCACQY+BgIAANgIUIAIgAkEQajYCKCACIAJBCGo2AhAgASACQRhqEMmGgIAAIQAMEQsgAiAAQQhqKQMANwMIIAJBLGpBATYCACACQgI3AhwgAkHo5sCAADYCGCACQZCBgIAANgIUIAIgAkEQajYCKCACIAJBCGo2AhAgASACQRhqEMmGgIAAIQAMEAsgAiAAQQhqKQMANwMIIAJBLGpBATYCACACQgI3AhwgAkHo5sCAADYCGCACQZGBgIAANgIUIAIgAkEQajYCKCACIAJBCGo2AhAgASACQRhqEMmGgIAAIQAMDwsgAiAAQQhqKQMANwMIIAJBLGpBATYCACACQgI3AhwgAkHM5sCAADYCGCACQZKBgIAANgIUIAIgAkEQajYCKCACIAJBCGo2AhAgASACQRhqEMmGgIAAIQAMDgsgAiAAQQRqKAIANgIIIAJBLGpBATYCACACQgI3AhwgAkGs5sCAADYCGCACQZOBgIAANgIUIAIgAkEQajYCKCACIAJBCGo2AhAgASACQRhqEMmGgIAAIQAMDQsgAiAAQQRqKQIANwMIIAJBLGpBATYCACACQgE3AhwgAkGY5sCAADYCGCACQZSBgIAANgIUIAIgAkEQajYCKCACIAJBCGo2AhAgASACQRhqEMmGgIAAIQAMDAsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJBiObAgAA2AhggASACQRhqEMmGgIAAIQAMCwsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJBgObAgAA2AhggASACQRhqEMmGgIAAIQAMCgsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJB7OXAgAA2AhggASACQRhqEMmGgIAAIQAMCQsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJB2OXAgAA2AhggASACQRhqEMmGgIAAIQAMCAsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJBwOXAgAA2AhggASACQRhqEMmGgIAAIQAMBwsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJBsOXAgAA2AhggASACQRhqEMmGgIAAIQAMBgsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJBpOXAgAA2AhggASACQRhqEMmGgIAAIQAMBQsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJBmOXAgAA2AhggASACQRhqEMmGgIAAIQAMBAsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJBhOXAgAA2AhggASACQRhqEMmGgIAAIQAMAwsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJB7OTAgAA2AhggASACQRhqEMmGgIAAIQAMAgsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJB1OTAgAA2AhggASACQRhqEMmGgIAAIQAMAQsgASAAQQRqKAIAIABBCGooAgAQyIaAgAAhAAsgAkEwaiSAgICAACAACxQAIAEgACgCACAAKAIEEMiGgIAACxIAIAAgAiABKAIMEYCAgIAAAAs2AQF/IAAgAiABayICELGFgIAAIAAgACgCCCIDIAJqNgIIIAMgACgCAGogAiABIAIQsoWAgAALEQAgACAAKAIIIAEQyYWAgAALkwIBAX8jgICAgABB4ABrIgQkgICAgAAgBCABNgIIIAQgAzYCDAJAIAEgA0cNACAAIAIgARDzhoCAABogBEHgAGokgICAgAAPCyAEQShqQRRqQYiAgIAANgIAIARBNGpBlYGAgAA2AgAgBEEQakEUakEDNgIAIAQgBEEIajYCQCAEIARBDGo2AkQgBEHIAGpBFGpBADYCACAEQgM3AhQgBEHE6MCAADYCECAEQZWBgIAANgIsIARBlOfAgAA2AlggBEIBNwJMIARBkOnAgAA2AkggBCAEQShqNgIgIAQgBEHIAGo2AjggBCAEQcQAajYCMCAEIARBwABqNgIoIARBEGpB+OfAgAAQsYaAgAAQlIaAgAAACxkAIAAoAgAiACgCACAAKAIIIAEQ0IaAgAALQwAgACgCACEAAkAgARDKhoCAAA0AAkAgARDLhoCAAA0AIAAgARCchoCAAA8LIAAgARDhhoCAAA8LIAAgARDbhoCAAAtxAQF/I4CAgIAAQSBrIgIkgICAgAAgAiAANgIEIAJBCGpBEGogAUEQaikCADcDACACQQhqQQhqIAFBCGopAgA3AwAgAiABKQIANwMIIAJBBGpBlOfAgAAgAkEIahCfhoCAACEBIAJBIGokgICAgAAgAQsgAQF/AkAgACgCBCIBRQ0AIAAoAgAgAUEBEM6CgIAACwsCAAsCAAsQACAAIAEgAiADELqFgIAACzABAX9BACEEAkACQCABIANHDQAgACACRw0BQQEhBAsgBA8LIAAgAiABEPSGgIAARQtXAQF/I4CAgIAAQRBrIgMkgICAgAACQCACRQ0AIAAgAjYCBCAAIAE2AgAgA0EQaiSAgICAAA8LQZjpwIAAQSsgA0EIakHE6cCAAEHM6sCAABCqhoCAAAALzgIBAn8jgICAgABBEGsiAiSAgICAACAAKAIAIQACQAJAAkACQCABQYABSQ0AIAJBADYCDCABQYAQSQ0BIAJBDGohAwJAIAFBgIAETw0AIAIgAUE/cUGAAXI6AA4gAiABQQZ2QT9xQYABcjoADSACIAFBDHZBD3FB4AFyOgAMQQMhAQwDCyACIAFBP3FBgAFyOgAPIAIgAUESdkHwAXI6AAwgAiABQQZ2QT9xQYABcjoADiACIAFBDHZBP3FBgAFyOgANQQQhAQwCCwJAIAAoAggiAyAAKAIERw0AIABBARCxhYCAACAAKAIIIQMLIAAoAgAgA2ogAToAACAAIAAoAghBAWo2AggMAgsgAiABQT9xQYABcjoADSACIAFBBnZBH3FBwAFyOgAMIAJBDGohA0ECIQELIAAgAyABEL2FgIAACyACQRBqJICAgIAAQQALEQAgACABIAEgAmoQsIWAgAALaAEBfyOAgICAAEEgayICJICAgIAAIAAoAgAhACACQQhqQRBqIAFBEGopAgA3AwAgAkEIakEIaiABQQhqKQIANwMAIAIgASkCADcDCCAAIAJBCGoQtYWAgAAhASACQSBqJICAgIAAIAELEwAgACgCACABIAIQvYWAgABBAAsMACAAIAEpAgA3AgALAgALUAEBfyOAgICAAEEQayIDJICAgIAAIAMgAhDEhYCAACADIAEgAhC9hYCAACAAQQhqIANBCGooAgA2AgAgACADKQMANwIAIANBEGokgICAgAALJwEBf0EBIQMCQCACQQRHDQAgACABKAAANgABQQAhAwsgACADOgAAC0YCAX8BfiOAgICAAEEQayICJICAgIAAIAJBCGogAUEAEMeFgIAAIAIpAwghAyAAQQA2AgggACADNwIAIAJBEGokgICAgAALIAEBfwJAIAAoAgQgACgCCCIBRg0AIAAgARDGhYCAAAsLgwEBAX8CQAJAAkACQCAAKAIEIgIgAUkNAAJAIAFFDQAgAiABRg0EIAAoAgAgAkEBIAEQz4KAgAAiAg0CIAFBARCFhoCAAAALIAAQtoWAgAAgAEEBNgIAQQAhAQwCC0Hc6sCAAEEkQfTpwIAAEI2GgIAAAAsgACACNgIACyAAIAE2AgQLC6QBAQN/I4CAgIAAQRBrIgMkgICAgAACQAJAIAFBf0wNAAJAAkAgAQ0AQQEhAgwBCyADQQhqIAFBARC7hYCAACADKAIMIQQgAygCCCEFAkACQCACDQAgBSAEEM2CgIAAIQIMAQsgBSAEENCCgIAAIQILIAJFDQILIAAgATYCBCAAIAI2AgAgA0EQaiSAgICAAA8LEMyFgIAAAAsgBSAEEIWGgIAAAAsXACAAIAI2AgggACADNgIEIAAgATYCAAtqAQF/I4CAgIAAQRBrIgMkgICAgAAgAyAAIAEgAkEBQQEQzYWAgAACQAJAIAMoAgBBAUcNACADQQhqKAIARQ0BQYDrwIAAQShB9OnAgAAQjYaAgAAACyADQRBqJICAgIAADwsQhoaAgAAACw4AIAAgASACEMKFgIAACxAAIAAgAjYCBCAAIAE2AgALCQAQhoaAgAAAC4UCAQJ/QQAhBgJAIAEoAgQiByACayADTw0AIAIgA2oiAyACSSECAkACQAJAAkAgBUUNACACRQ0BIAAgAzYCBCAAQQhqQQA2AgAMAwsgAkUNASAAIAM2AgQgAEEIakEANgIADAILIAdBAXQiAiADIAIgA0sbIQMLAkAgA0F/Sg0AIABBCGpBADYCAAwBCwJAAkAgBw0AIANBARDNgoCAACECDAELIAEoAgAgB0EBIAMQz4KAgAAhAgsCQAJAIAINACAERQ0BIANBARCFhoCAAAALIAEgAzYCBCABIAI2AgAMAgsgACADNgIEQQEhBiAAQQhqQQE2AgAMAQtBASEGCyAAIAY2AgALAgALFgAgACABKAIINgIEIAAgASgCADYCAAsKACAAELaFgIAACwwAQuSuwoWXm6WIEQsMAEKY3Iamxs6tyQgLDQBCmfbtpaTq/5TFAAsdACAAKAIAIgAoAgAgASAAKAIEKAIkEYCAgIAAAAsPACAAKAIAIAEQ1oWAgAAL5QMBAX8jgICAgABBEGsiAiSAgICAAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAAtAAAOEgECAwQFBgcICQoLDA0ODxARAAELIAIgAUHA8cCAAEENEM2GgIAADBELIAIgAUGM88CAAEEIEM2GgIAADBALIAIgAUH88sCAAEEQEM2GgIAADA8LIAIgAUHr8sCAAEEREM2GgIAADA4LIAIgAUHc8sCAAEEPEM2GgIAADA0LIAIgAUHL8sCAAEEREM2GgIAADAwLIAIgAUG/8sCAAEEMEM2GgIAADAsLIAIgAUG28sCAAEEJEM2GgIAADAoLIAIgAUGm8sCAAEEQEM2GgIAADAkLIAIgAUGc8sCAAEEKEM2GgIAADAgLIAIgAUGP8sCAAEENEM2GgIAADAcLIAIgAUGF8sCAAEEKEM2GgIAADAYLIAIgAUH58cCAAEEMEM2GgIAADAULIAIgAUHu8cCAAEELEM2GgIAADAQLIAIgAUHm8cCAAEEIEM2GgIAADAMLIAIgAUHd8cCAAEEJEM2GgIAADAILIAIgAUHS8cCAAEELEM2GgIAADAELIAIgAUHN8cCAAEEFEM2GgIAACyACELaGgIAAIQEgAkEQaiSAgICAACABCxQAIAAoAgAgACgCBCABENKGgIAACzwAAkAgARDKhoCAAA0AAkAgARDLhoCAAA0AIAAgARDkhoCAAA8LIAAgARDhhoCAAA8LIAAgARDbhoCAAAs/AQF/I4CAgIAAQRBrIgMkgICAgAAgAyABNgIMIAMgADYCCCADQQhqQfDwwIAAQQAgAhCxhoCAABD3hYCAAAALAgALKgEBfwJAIAAoAgQiAUUNACAAQQhqKAIAIgBFDQAgASAAQQEQzoKAgAALCyMBAX8CQCAAQQRqKAIAIgFFDQAgACgCACABQQEQzoKAgAALCxwAAkAgAA0AQdDrwIAAQSsgARCNhoCAAAALIAALIAACQCAADQBB0OvAgABBK0G88MCAABCNhoCAAAALIAALgwUBBX8jgICAgABBEGsiAiSAgICAACAAKAIAIQACQAJAAkACQAJAAkACQCABQYABSQ0AIAJBADYCDCABQYAQSQ0BIAJBDGohAwJAIAFBgIAETw0AIAIgAUE/cUGAAXI6AA4gAiABQQZ2QT9xQYABcjoADSACIAFBDHZBD3FB4AFyOgAMQQMhAQwECyACIAFBP3FBgAFyOgAPIAIgAUESdkHwAXI6AAwgAiABQQZ2QT9xQYABcjoADiACIAFBDHZBP3FBgAFyOgANQQQhAQwDCwJAAkAgACgCCCIEIABBBGooAgBGDQAgACgCACEFDAELIARBAWoiBSAESQ0GIARBAXQiAyAFIAMgBUsbIgNBAEgNBgJAAkAgBA0AIANBARDNgoCAACEFDAELIAAoAgAgBEEBIAMQz4KAgAAhBQsgBUUNAiAAIAU2AgAgAEEEaiADNgIAIAAoAgghBAsgBSAEaiABOgAAIAAgACgCCEEBajYCCAwDCyACIAFBP3FBgAFyOgANIAIgAUEGdkEfcUHAAXI6AAwgAkEMaiEDQQIhAQwBCyADQQEQhYaAgAAACwJAAkAgAEEEaigCACIFIABBCGooAgAiBGsgAUkNACAAKAIAIQUMAQsgBCABaiIGIARJDQMgBUEBdCIEIAYgBCAGSxsiBEEASA0DAkACQCAFDQAgBEEBEM2CgIAAIQUMAQsgACgCACAFQQEgBBDPgoCAACEFCyAFRQ0CIAAgBTYCACAAQQRqIAQ2AgAgAEEIaigCACEECyAAQQhqIAQgAWo2AgAgBSAEaiADIAEQ84aAgAAaCyACQRBqJICAgIAAQQAPCyAEQQEQhYaAgAAACxCGhoCAAAALdAEBfyOAgICAAEEgayICJICAgIAAIAIgACgCADYCBCACQQhqQRBqIAFBEGopAgA3AwAgAkEIakEIaiABQQhqKQIANwMAIAIgASkCADcDCCACQQRqQajrwIAAIAJBCGoQn4aAgAAhASACQSBqJICAgIAAIAEL1wEBA38CQAJAAkACQCAAKAIAIgBBBGooAgAiAyAAQQhqKAIAIgRrIAJJDQAgACgCACEDDAELIAQgAmoiBSAESQ0CIANBAXQiBCAFIAQgBUsbIgRBAEgNAgJAAkAgAw0AIARBARDNgoCAACEDDAELIAAoAgAgA0EBIAQQz4KAgAAhAwsgA0UNASAAIAM2AgAgAEEEaiAENgIAIABBCGooAgAhBAsgAEEIaiAEIAJqNgIAIAMgBGogASACEPOGgIAAGkEADwsgBEEBEIWGgIAAAAsQhoaAgAAACxQAIAAoAgAgACgCCCABENCGgIAACwkAEIaGgIAAAAsUACAAKAIAIAAoAgggARDShoCAAAtEAQF/I4CAgIAAQRBrIgIkgICAgAAgAkEIaiABQfvrwIAAQQsQzIaAgAAgAkEIahC0hoCAACEBIAJBEGokgICAgAAgAQtIAQF/I4CAgIAAQRBrIgIkgICAgAAgAiABELGGgIAANgIMIAIgADYCCCACQcDrwIAANgIEIAJBwOvAgAA2AgAgAhD2hYCAAAAL6gUBA38jgICAgABBwABrIgIkgICAgAACQAJAAkACQAJAIAAtAAAOAwACAQALIAIgAEEEaigCADYCBEEUQQEQzYKAgAAiAEUNAyAAQRBqQQAoAKTzwIAANgAAIABBCGpBACkAnPPAgAA3AAAgAEEAKQCU88CAADcAACACQpSAgIDAAjcCDCACIAA2AgggAkEoakEUakECNgIAIAJBJGpBm4GAgAA2AgAgAkIDNwIsIAJBuO/AgAA2AiggAkGcgYCAADYCHCACIAJBGGo2AjggAiACQQRqNgIgIAIgAkEIajYCGCABIAJBKGoQyYaAgAAhACACKAIMIgFFDQIgAigCCCABQQEQzoKAgAAMAgsgAEEEaigCACIAKAIAIAEgACgCBCgCIBGAgICAAAAhAAwBC0Gw7MCAACEDQRYhBAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAAtAAEOEgABAgMEBQYHCAkKCwwNDg8QEgALQcnuwIAAIQNBECEEDBELQbjuwIAAIQNBESEEDBALQabuwIAAIQNBEiEEDA8LQZbuwIAAIQNBECEEDA4LQYTuwIAAIQNBEiEEDA0LQfftwIAAIQNBDSEEDAwLQentwIAAIQMMCgtB1O3AgAAhA0EVIQQMCgtBye3AgAAhA0ELIQQMCQtBtO3AgAAhA0EVIQQMCAtBn+3AgAAhA0EVIQQMBwtBiO3AgAAhA0EXIQQMBgtB/OzAgAAhA0EMIQQMBQtB8+zAgAAhA0EJIQQMBAtB6ezAgAAhA0EKIQQMAwtB1OzAgAAhA0EVIQQMAgtBxuzAgAAhAwtBDiEECyACQTxqQQE2AgAgAiAENgIcIAIgAzYCGCACQZ2BgIAANgIMIAJCATcCLCACQaTvwIAANgIoIAIgAkEYajYCCCACIAJBCGo2AjggASACQShqEMmGgIAAIQALIAJBwABqJICAgIAAIAAPC0EUQQEQhYaAgAAACw0AQs+4j+zTwfnEv38LBABBAAsJACAAQQA2AgALFgAgACABKAIINgIEIAAgASgCADYCAAsUACAAKAIAIAAoAgggARDShoCAAAsUACAAKAIAIAAoAgggARDQhoCAAAuVAQEBfwJAAkACQCACQX9MDQACQAJAIAINAEEBIQMMAQsgAkEBEM2CgIAAIgNFDQILIAMgASACEPOGgIAAIQNBDEEEEM2CgIAAIgFFDQIgASACNgIIIAEgAjYCBCABIAM2AgAgAEGI7MCAADYCBCAAIAE2AgAPCxDjhYCAAAALIAJBARCFhoCAAAALQQxBBBCFhoCAAAALDAAgACABEPCFgIAAC/gDAQF/I4CAgIAAQTBrIgIkgICAgAACQAJAAkACQAJAIAAtAAAOAwACAQALIAIgAEEEaigCADYCDCACQRBqIAFB8O7AgABBAhDMhoCAACACQRBqQfLuwIAAQQQgAkEMakH47sCAABCnhoCAACEAIAJBEDoAHyAAQYjvwIAAQQQgAkEfakHg7sCAABCnhoCAACEBQRRBARDNgoCAACIARQ0DIABBEGpBACgApPPAgAA2AAAgAEEIakEAKQCc88CAADcAACAAQQApAJTzwIAANwAAIAJClICAgMACNwIkIAIgADYCICABQYzvwIAAQQcgAkEgakGU78CAABCnhoCAABC0hoCAACEAIAIoAiQiAUUNAiACKAIgIAFBARDOgoCAAAwCCyAAQQRqKAIAIQAgAkEgaiABQZnxwIAAQQYQzIaAgAAgAiAAQQhqNgIQIAJBIGpBiO/AgABBBCACQRBqQaDxwIAAEKeGgIAAGiACIAA2AhAgAkEgakGU8cCAAEEFIAJBEGpBsPHAgAAQp4aAgAAaIAJBIGoQtIaAgAAhAAwBCyACIAAtAAE6ABAgAkEgaiABQdnuwIAAQQQQzYaAgAAgAkEgaiACQRBqQeDuwIAAELWGgIAAELaGgIAAIQALIAJBMGokgICAgAAgAA8LQRRBARCFhoCAAAALnAEBAn8jgICAgABBEGsiBCSAgICAAAJAQQxBBBDNgoCAACIFDQBBDEEEEIWGgIAAAAsgBSABOgAIIAUgAzYCBCAFIAI2AgAgBSAELwANOwAJIAVBC2ogBEENakECai0AADoAACAAQQI6AAAgACAELwAKOwABIABBA2ogBEEKakECai0AADoAACAAQQRqIAU2AgAgBEEQaiSAgICAAAsEAAAACwIACyUBAX8gACABQQAoApj6woAAIgJBnoGAgAAgAhsRjoCAgAAAAAALvwEBAX8CQAJAAkBBACgCqPrCgABBAUYNAEEAQgE3A6j6woAADAELQQAoAqz6woAADQELAkBBACgCnPrCgAANAEEAKAKk+sKAACECQQAgATYCpPrCgABBACgCoPrCgAAhAUEAIAA2AqD6woAAQQBBADYCnPrCgAACQCACRQ0AIAEgAigCABGBgICAAAAgAigCBCIARQ0AIAEgACACKAIIEM6CgIAACw8LAAALQdDvwIAAQTRBnPDAgAAQ2YWAgAAAC2IBA38jgICAgABBEGsiASSAgICAACAAEK2GgIAAQazwwIAAEN2FgIAAIQIgABCshoCAABDehYCAACEDIAFBADYCBCABIAM2AgAgAUHM8MCAACAAEKyGgIAAIAIQ94WAgAAAC58CAQJ/I4CAgIAAQSBrIgQkgICAgABBASEFAkACQAJAAkBBACgCqPrCgABBAUYNAEEAQoGAgIAQNwOo+sKAAAwBC0EAQQAoAqz6woAAQQFqIgU2Aqz6woAAIAVBAksNAQsgBCADNgIcIAQgAjYCGCAEQcDrwIAANgIUIARBwOvAgAA2AhBBACgCnPrCgAAiAkF/TA0AQQAgAkEBaiICNgKc+sKAAAJAQQAoAqT6woAAIgNFDQBBACgCoPrCgAAhAiAEQQhqIAAgASgCEBGOgICAAAAgBCAEKQMINwMQIAIgBEEQaiADKAIMEY6AgIAAAEEAKAKc+sKAACECC0EAIAJBf2o2Apz6woAAIAVBAU0NAQsAAAsgACABEPyFgIAAAAviAgEFfyOAgICAAEHAAGsiAiSAgICAAAJAIAEoAgQiAw0AIAFBBGohAyABKAIAIQQgAkEANgIgIAJCATcDGCACIAJBGGo2AiQgAkEoakEQaiAEQRBqKQIANwMAIAJBKGpBCGogBEEIaikCADcDACACIAQpAgA3AyggAkEkakGo68CAACACQShqEJ+GgIAAGiACQQhqQQhqIgQgAigCIDYCACACIAIpAxg3AwgCQCABKAIEIgVFDQAgAUEIaigCACIGRQ0AIAUgBkEBEM6CgIAACyADIAIpAwg3AgAgA0EIaiAEKAIANgIAIAMoAgAhAwsgAUEBNgIEIAFBDGooAgAhBCABQQhqIgEoAgAhBSABQgA3AgACQEEMQQQQzYKAgAAiAQ0AQQxBBBCFhoCAAAALIAEgBDYCCCABIAU2AgQgASADNgIAIABB4PDAgAA2AgQgACABNgIAIAJBwABqJICAgIAAC4QCAQR/I4CAgIAAQcAAayICJICAgIAAIAFBBGohAwJAIAEoAgQNACABKAIAIQQgAkEANgIgIAJCATcDGCACIAJBGGo2AiQgAkEoakEQaiAEQRBqKQIANwMAIAJBKGpBCGogBEEIaikCADcDACACIAQpAgA3AyggAkEkakGo68CAACACQShqEJ+GgIAAGiACQQhqQQhqIgQgAigCIDYCACACIAIpAxg3AwgCQCABKAIEIgVFDQAgAUEIaigCACIBRQ0AIAUgAUEBEM6CgIAACyADIAIpAwg3AgAgA0EIaiAEKAIANgIACyAAQeDwwIAANgIEIAAgAzYCACACQcAAaiSAgICAAAthAQJ/IAEoAgAhAiABQQA2AgACQAJAIAJFDQAgASgCBCEDQQhBBBDNgoCAACIBRQ0BIAEgAzYCBCABIAI2AgAgAEGE8cCAADYCBCAAIAE2AgAPCwAAC0EIQQQQhYaAgAAACyAAAkAgASgCAA0AAAALIABBhPHAgAA2AgQgACABNgIACzEBAX8jgICAgABBEGsiAiSAgICAACACIAE2AgwgAiAANgIIIAJBCGoQ/YWAgAAaAAALBAAAAAtDACAAKAIAIQACQCABEMqGgIAADQACQCABEMuGgIAADQAgACABENmGgIAADwsgACABEOCGgIAADwsgACABENaGgIAACw8AIAAoAgAgARDthoCAAAt/AQJ/I4CAgIAAQRBrIgIkgICAgAAgACgCACIAKAIIIQMgACgCACEAIAIgARDOhoCAAAJAIANFDQADQCACIAA2AgwgAiACQQxqQajzwIAAELiGgIAAGiAAQQFqIQAgA0F/aiIDDQALCyACELmGgIAAIQAgAkEQaiSAgICAACAACwIACxcAIAAoAgAgACgCBEEAIAEQkoaAgAAACyoBAX8gACgCACIBKAIAIAEoAgQgACgCBCgCACAAKAIIKAIAEJKGgIAAAAv6BAEFfyOAgICAAEEQayICJICAgIAAAkACQAJAAkACQAJAAkAgAUGAAUkNACACQQA2AgwgAUGAEEkNASACQQxqIQMCQCABQYCABE8NACACIAFBP3FBgAFyOgAOIAIgAUEGdkE/cUGAAXI6AA0gAiABQQx2QQ9xQeABcjoADEEDIQEMBAsgAiABQT9xQYABcjoADyACIAFBEnZB8AFyOgAMIAIgAUEGdkE/cUGAAXI6AA4gAiABQQx2QT9xQYABcjoADUEEIQEMAwsCQAJAIAAoAggiBCAAQQRqKAIARg0AIAAoAgAhBQwBCyAEQQFqIgUgBEkNBiAEQQF0IgMgBSADIAVLGyIDQQBIDQYCQAJAIAQNACADQQEQzYKAgAAhBQwBCyAAKAIAIARBASADEM+CgIAAIQULIAVFDQIgACAFNgIAIABBBGogAzYCACAAKAIIIQQLIAUgBGogAToAACAAIAAoAghBAWo2AggMAwsgAiABQT9xQYABcjoADSACIAFBBnZBH3FBwAFyOgAMIAJBDGohA0ECIQEMAQsgA0EBEIWGgIAAAAsCQAJAIABBBGooAgAiBSAAQQhqKAIAIgRrIAFJDQAgACgCACEFDAELIAQgAWoiBiAESQ0DIAVBAXQiBCAGIAQgBksbIgRBAEgNAwJAAkAgBQ0AIARBARDNgoCAACEFDAELIAAoAgAgBUEBIAQQz4KAgAAhBQsgBUUNAiAAIAU2AgAgAEEEaiAENgIAIABBCGooAgAhBAsgAEEIaiAEIAFqNgIAIAUgBGogAyABEPOGgIAAGgsgAkEQaiSAgICAAA8LIARBARCFhoCAAAALEIaGgIAAAAsNACAAIAEQ9IWAgAAACxcAQc/zwIAAQRFB4PPAgAAQjYaAgAAACwkAEIaGgIAAAAvADQELfyOAgICAAEEwayIDJICAgIAAAkACQAJAAkACQAJAAkAgAkF/TA0AAkACQCACDQBBASEEDAELIAJBARDNgoCAACIERQ0CCyADQQA2AgggAyACNgIEIAMgBDYCAAJAIAJFDQAgASACaiEFIAEhBkEAIQcDQCAGIQggBkEBaiEJAkACQAJAIAYsAAAiBEF/Sg0AAkACQCAJIAVHDQBBACEKIAUhCwwBCyAGLQABQT9xIQogBkECaiIJIQsLIARBH3EhDCAEQf8BcSIEQd8BSw0BIAogDEEGdHIhBCAJIQYMAgsgBEH/AXEhBCAJIQYMAQsCQAJAIAsgBUcNAEEAIQYgBSELDAELIAstAABBP3EhBiALQQFqIgkhCwsgBiAKQQZ0ciEKAkAgBEHwAU8NACAKIAxBDHRyIQQgCSEGDAELAkACQCALIAVHDQBBACEEIAkhBgwBCyALQQFqIQYgCy0AAEE/cSEECyAKQQZ0IAxBEnRBgIDwAHFyIARyIgRBgIDEAEYNAgsCQAJAAkACQCAEQaMHRg0AIARBgIDEAEYNBSADQSBqIAQQ8IaAgAAgAygCJCIJRQ0BIAMoAighBCADIAMoAiAQhIaAgAAgAyAJEISGgIAAIAQNAgwDCyADIAI2AiQgAyABNgIgAkAgB0UNACAHIAJGDQAgByACTw0IIAEgB2osAABBv39MDQgLIAEgB2ohBAJAAkADQCAEIAFGDQECQAJAIARBf2oiCy0AACIJQRh0QRh1IgpBAEgNACALIQQMAQsCQAJAIAsgAUcNAEEAIQkgASEEDAELAkAgBEF+aiILLQAAIglBwAFxQYABRg0AIAlBH3EhCSALIQQMAQsCQAJAIAsgAUcNAEEAIQsgASEEDAELAkAgBEF9aiIMLQAAIgtBwAFxQYABRg0AIAtBD3EhCyAMIQQMAQsCQAJAIAwgAUcNAEEAIQwgASEEDAELIARBfGoiBC0AAEEHcUEGdCEMCyAMIAtBP3FyIQsLIAtBBnQgCUE/cXIhCQsgCUEGdCAKQT9xciIJQYCAxABGDQILIAkQ7oaAgAANAAsgCRDvhoCAAEUNACADIAI2AhQgAyABNgIQIAMgB0ECaiIENgIYIAMgAjYCHAJAIARFDQAgBCACRg0AIAQgAk8NCyABIARqLAAAQb9/TA0LCyABIARqIQRBACELA0AgBCAFRg0CIARBAWohCgJAAkAgBCwAACIJQX9MDQAgCUH/AXEhCSAKIQQMAQsCQAJAIAogBUcNAEEAIQwgBSEKDAELIARBAmohCiAELQABQT9xIQwLIAlBH3EhDQJAIAlB/wFxIglB3wFLDQAgDCANQQZ0ciEJIAohBAwBCwJAAkAgCiAFRw0AQQAhCiAFIQQMAQsgCkEBaiEEIAotAABBP3EhCgsgCiAMQQZ0ciEKAkAgCUHwAU8NACAKIA1BDHRyIQkMAQsCQAJAIAQgBUcNAEEAIQkgBSEEDAELIAQtAABBP3EhCSAEQQFqIQQLIApBBnQgDUESdEGAgPAAcXIgCXIiCUGAgMQARg0DCwJAAkAgC0H/AXENACAJEO6GgIAARQ0AQYCAxAAhCUEAIQsMAQtBASELCyAJQYCAxABGDQALIAkQ74aAgABFDQELAkACQCADKAIEIgkgAygCCCIEa0ECSQ0AIAMoAgAhCQwBCyAEQQJqIgsgBEkNDSAJQQF0IgogCyAKIAtLGyILQQBIDQ0CQAJAIAkNACALQQEQzYKAgAAhCQwBCyADKAIAIAlBASALEM+CgIAAIQkLIAlFDQsgAyALNgIEIAMgCTYCAAsgAyAEQQJqNgIIIAkgBGpBz4cCOwAADAMLAkACQCADKAIEIgkgAygCCCIEa0ECSQ0AIAMoAgAhCQwBCyAEQQJqIgsgBEkNDCAJQQF0IgogCyAKIAtLGyILQQBIDQwCQAJAIAkNACALQQEQzYKAgAAhCQwBCyADKAIAIAlBASALEM+CgIAAIQkLIAlFDQsgAyALNgIEIAMgCTYCAAsgAyAEQQJqNgIIIAkgBGpBz4UCOwAADAILIAMoAiAhBAsgAyAEEISGgIAACyAHIAhrIAZqIQcgBSAGRw0ACwsgACADKQMANwIAIABBCGogA0EIaigCADYCACADQTBqJICAgIAADwsQh4aAgAAACyACQQEQhYaAgAAACyADQSBqIAcQgoaAgAAACyADIANBHGo2AiggAyADQRhqNgIkIAMgA0EQajYCICADQSBqEIOGgIAAAAsgC0EBEIWGgIAAAAsgC0EBEIWGgIAAAAsQhoaAgAAAC3IBAn8CQAJAIAEoAggiAkF/TA0AIAEoAgAhAQJAAkAgAg0AQQEhAwwBCyACQQEQzYKAgAAiA0UNAgsgAyABIAIQ84aAgAAhASAAIAI2AgggACACNgIEIAAgATYCAA8LEIeGgIAAAAsgAkEBEIWGgIAAAAuHAQEBfyOAgICAAEEQayICJICAgIAAIAIgAUHw88CAAEENEMyGgIAAIAIgADYCDCACQf3zwIAAQQUgAkEMakGE9MCAABCnhoCAABogAiAAQQxqNgIMIAJBlPTAgABBBSACQQxqQZz0wIAAEKeGgIAAGiACELSGgIAAIQAgAkEQaiSAgICAACAACwIAC4EBAQF/I4CAgIAAQTBrIgMkgICAgAAgAyACNgIEIAMgATYCACADQRxqQQI2AgAgA0EsakGEgICAADYCACADQgI3AgwgA0Hwj8GAADYCCCADQYSAgIAANgIkIAMgA0EgajYCGCADIAM2AiggAyADQQRqNgIgIANBCGogABCUhoCAAAALVAEBfyOAgICAAEEgayIDJICAgIAAIANBFGpBADYCACADQaz0wIAANgIQIANCATcCBCADIAE2AhwgAyAANgIYIAMgA0EYajYCACADIAIQlIaAgAAAC7IEAQd/AkAgAUH/CUsNACABQQV2IQICQAJAAkACQAJAAkACQCAAKAIAIgNFDQAgA0F/aiEEIAAgA0ECdGohBSAAIAMgAmpBAnRqIQMDQCAEQSdLDQIgAiAEaiIGQSdLDQMgAyAFKAIANgIAIAVBfGohBSADQXxqIQMgBEF/aiIEQX9HDQALCwJAIAJFDQAgAEEEaiEFIAJBAnQhA0EAIQQDQCAEQaABRg0EIAUgBGpBADYCACADIARBBGoiBEcNAAsLIAAoAgAiBCACaiEFAkAgAUEfcSIGDQAgACAFNgIAIAAPCyAFQX9qIgNBJ0sNAyAFIQcCQCAAIANBAnRqQQRqKAIAIgNBACABa0EfcSIBdiIIRQ0AIAVBJ0sNBSAAIAVBAnRqQQRqIAg2AgAgBUEBaiEHCwJAIAJBAWoiCCAFTw0AIAQgAmpBAnQgAGpBfGohBANAIAVBfmpBJ0sNByAEQQRqIAMgBnQgBCgCACIDIAF2cjYCACAEQXxqIQQgCCAFQX9qIgVJDQALCyAAIAJBAnRqQQRqIgQgBCgCACAGdDYCACAAIAc2AgAgAA8LQYilwYAAIARBKBCMhoCAAAALQYilwYAAIAZBKBCMhoCAAAALQYilwYAAQShBKBCMhoCAAAALQYilwYAAIANBKBCMhoCAAAALQYilwYAAIAVBKBCMhoCAAAALQYilwYAAIAVBfmpBKBCMhoCAAAALQbKlwYAAQR1BiKXBgAAQjYaAgAAAC4UBAQF/I4CAgIAAQTBrIgIkgICAgAAgAiABNgIEIAIgADYCACACQRxqQQI2AgAgAkEsakGEgICAADYCACACQgI3AgwgAkHok8GAADYCCCACQYSAgIAANgIkIAIgAkEgajYCGCACIAJBBGo2AiggAiACNgIgIAJBCGpB+JPBgAAQlIaAgAAAC4UBAQF/I4CAgIAAQTBrIgIkgICAgAAgAiABNgIEIAIgADYCACACQRxqQQI2AgAgAkEsakGEgICAADYCACACQgI3AgwgAkGslMGAADYCCCACQYSAgIAANgIkIAIgAkEgajYCGCACIAJBBGo2AiggAiACNgIgIAJBCGpBvJTBgAAQlIaAgAAAC6sHAQx/IABBEGooAgAhAwJAAkACQAJAIABBCGooAgAiBEEBRg0AIANBAUYNASAAKAIYIAEgAiAAQRxqKAIAKAIMEY+AgIAAACEDDAMLIANBAUcNAQsCQAJAIAINAEEAIQIMAQsgASACaiEFIABBFGooAgBBAWohBkEAIQcgASEDIAEhCANAIANBAWohCQJAAkACQCADLAAAIgpBf0oNAAJAAkAgCSAFRw0AQQAhCyAFIQMMAQsgAy0AAUE/cSELIANBAmoiCSEDCyAKQR9xIQwCQCAKQf8BcSIKQd8BSw0AIAsgDEEGdHIhCgwCCwJAAkAgAyAFRw0AQQAhDSAFIQ4MAQsgAy0AAEE/cSENIANBAWoiCSEOCyANIAtBBnRyIQsCQCAKQfABTw0AIAsgDEEMdHIhCgwCCwJAAkAgDiAFRw0AQQAhCiAJIQMMAQsgDkEBaiEDIA4tAABBP3EhCgsgC0EGdCAMQRJ0QYCA8ABxciAKciIKQYCAxABHDQIMBAsgCkH/AXEhCgsgCSEDCwJAIAZBf2oiBkUNACAHIAhrIANqIQcgAyEIIAUgA0cNAQwCCwsgCkGAgMQARg0AAkACQCAHRQ0AIAcgAkYNAEEAIQMgByACTw0BIAEgB2osAABBQEgNAQsgASEDCyAHIAIgAxshAiADIAEgAxshAQsgBEEBRg0AIAAoAhggASACIABBHGooAgAoAgwRj4CAgAAADwtBACEJAkAgAkUNACACIQogASEDA0AgCSADLQAAQcABcUGAAUZqIQkgA0EBaiEDIApBf2oiCg0ACwsCQCACIAlrIAAoAgwiBkkNACAAKAIYIAEgAiAAQRxqKAIAKAIMEY+AgIAAAA8LQQAhB0EAIQkCQCACRQ0AQQAhCSACIQogASEDA0AgCSADLQAAQcABcUGAAUZqIQkgA0EBaiEDIApBf2oiCg0ACwsgCSACayAGaiIJIQoCQAJAAkBBACAALQAgIgMgA0EDRhsOBAIBAAECCyAJQQF2IQcgCUEBakEBdiEKDAELQQAhCiAJIQcLIAdBAWohAwJAA0AgA0F/aiIDRQ0BIAAoAhggACgCBCAAKAIcKAIQEYCAgIAAAEUNAAtBAQ8LIAAoAgQhCUEBIQMgACgCGCABIAIgACgCHCgCDBGPgICAAAANACAKQQFqIQMgACgCHCEKIAAoAhghAANAAkAgA0F/aiIDDQBBAA8LIAAgCSAKKAIQEYCAgIAAAEUNAAtBAQ8LIAMLpgkBBn8jgICAgABB8ABrIgQkgICAgAAgBCADNgIMIAQgAjYCCEEBIQUgASEGAkAgAUGBAkkNAEEAIAFrIQdBgAIhCANAAkAgCCABTw0AIAAgCGosAABBv39MDQBBACEFIAghBgwCCyAIQX9qIQZBACEFIAhBAUYNASAHIAhqIQkgBiEIIAlBAUcNAAsLIAQgBjYCFCAEIAA2AhAgBEEAQQUgBRs2AhwgBEGs9MCAAEGul8GAACAFGzYCGAJAAkACQAJAIAIgAUsiCA0AIAMgAUsNACACIANLDQECQAJAIAJFDQAgASACRg0AIAEgAk0NASAAIAJqLAAAQUBIDQELIAMhAgsgBCACNgIgIAJFDQIgAiABRg0CIAFBAWohCQNAAkAgAiABTw0AIAAgAmosAABBQE4NBAsgAkF/aiEIIAJBAUYNBCAJIAJGIQYgCCECIAZFDQAMBAsLIAQgAiADIAgbNgIoIARBMGpBFGpBAzYCACAEQcgAakEUakG4gYCAADYCACAEQdQAakG4gYCAADYCACAEQgM3AjQgBEHUl8GAADYCMCAEQYSAgIAANgJMIAQgBEHIAGo2AkAgBCAEQRhqNgJYIAQgBEEQajYCUCAEIARBKGo2AkggBEEwakHsl8GAABCUhoCAAAALIARB5ABqQbiBgIAANgIAIARByABqQRRqQbiBgIAANgIAIARB1ABqQYSAgIAANgIAIARBMGpBFGpBBDYCACAEQgQ3AjQgBEGgmMGAADYCMCAEQYSAgIAANgJMIAQgBEHIAGo2AkAgBCAEQRhqNgJgIAQgBEEQajYCWCAEIARBDGo2AlAgBCAEQQhqNgJIIARBMGpBwJjBgAAQlIaAgAAACyACIQgLAkAgCCABRg0AQQEhBgJAAkACQAJAIAAgCGoiCSwAACICQX9KDQBBACEFIAAgAWoiBiEBAkAgCUEBaiAGRg0AIAlBAmohASAJLQABQT9xIQULIAJBH3EhCSACQf8BcUHfAUsNASAFIAlBBnRyIQEMAgsgBCACQf8BcTYCJCAEQShqIQIMAgtBACEAIAYhBwJAIAEgBkYNACABQQFqIQcgAS0AAEE/cSEACyAAIAVBBnRyIQECQCACQf8BcUHwAU8NACABIAlBDHRyIQEMAQtBACECAkAgByAGRg0AIActAABBP3EhAgsgAUEGdCAJQRJ0QYCA8ABxciACciIBQYCAxABGDQILIAQgATYCJEEBIQYgBEEoaiECIAFBgAFJDQBBAiEGIAFBgBBJDQBBA0EEIAFBgIAESRshBgsgBCAINgIoIAQgBiAIajYCLCAEQTBqQRRqQQU2AgAgBEHsAGpBuIGAgAA2AgAgBEHkAGpBuIGAgAA2AgAgBEHIAGpBFGpBuYGAgAA2AgAgBEHUAGpBhIGAgAA2AgAgBEIFNwI0IARBlJnBgAA2AjAgBCACNgJYIARBhICAgAA2AkwgBCAEQcgAajYCQCAEIARBGGo2AmggBCAEQRBqNgJgIAQgBEEkajYCUCAEIARBIGo2AkggBEEwakG8mcGAABCUhoCAAAALQamOwYAAQStB0JjBgAAQjYaAgAAAC+ECAgJ/AX4jgICAgABBgAFrIgIkgICAgAAgACgCACEAAkACQAJAAkACQCABKAIAIgNBEHENACAAKQMAIQQgA0EgcQ0BIARBASABEMOGgIAAIQAMAgsgACkDACEEQQAhAANAIAIgAGpB/wBqIASnQQ9xIgNBMHIgA0HXAGogA0EKSRs6AAAgAEF/aiEAIARCBIgiBEIAUg0ACyAAQYABaiIDQYEBTw0CIAFBAUHBkMGAAEECIAIgAGpBgAFqQQAgAGsQxYaAgAAhAAwBC0EAIQADQCACIABqQf8AaiAEp0EPcSIDQTByIANBN2ogA0EKSRs6AAAgAEF/aiEAIARCBIgiBEIAUg0ACyAAQYABaiIDQYEBTw0CIAFBAUHBkMGAAEECIAIgAGpBgAFqQQAgAGsQxYaAgAAhAAsgAkGAAWokgICAgAAgAA8LIANBgAEQkIaAgAAACyADQYABEJCGgIAAAAtCAQF/I4CAgIAAQRBrIgIkgICAgAAgAiABNgIMIAIgADYCCCACQfCOwYAANgIEIAJBrPTAgAA2AgAgAhD2hYCAAAALnQQEAn8BfgJ/AX4CQAJAAkACQAJAIAFBB3EiAkUNACAAKAIAIgNBKU8NAQJAAkAgAw0AQQAhAwwBCyACQQJ0Qdz1wIAAajUCACEEIAAgA0ECdGpBBGohBSADQQJ0IQYgAEEEaiECQgAhBwNAIAIgAjUCACAEfiAHfCIHPgIAIAJBBGohAiAHQiCIIQcgBkF8aiIGDQALIAenIgJFDQAgA0EnSw0DIAUgAjYCACADQQFqIQMLIAAgAzYCAAsCQCABQQhxRQ0AIAAoAgAiA0EpTw0DAkACQCADDQBBACEDDAELIAAgA0ECdCIGakEEaiEFIABBBGohAkIAIQcDQCACIAI1AgBCgMLXL34gB3wiBz4CACACQQRqIQIgB0IgiCEHIAZBfGoiBg0ACyAHpyICRQ0AIANBJ0sNBSAFIAI2AgAgA0EBaiEDCyAAIAM2AgALAkAgAUEQcUUNACAAQaz2wIAAQQIQloaAgAAaCwJAIAFBIHFFDQAgAEG09sCAAEEEEJaGgIAAGgsCQCABQcAAcUUNACAAQcT2wIAAQQcQloaAgAAaCwJAIAFBgAFxRQ0AIABB4PbAgABBDhCWhoCAABoLAkAgAUGAAnFFDQAgAEGY98CAAEEbEJaGgIAAGgsgAA8LIANBKBCPhoCAAAALQYilwYAAIANBKBCMhoCAAAALIANBKBCPhoCAAAALQYilwYAAIANBKBCMhoCAAAALoAYDDH8CfgF/I4CAgIAAQaABayIDJICAgIAAIANBAEGgARDyhoCAACEEAkACQCAAKAIAIgVBKU8NACAAQQRqIQYCQCAFIAJJDQAgASACQQJ0aiEHAkACQAJAIAVFDQAgBUEBaiEIIAVBAnQhAkEAIQlBACEKA0AgBCAJQQJ0aiELA0AgCSEMIAshAyABIAdGDQcgA0EEaiELIAxBAWohCSABKAIAIQ0gAUEEaiIOIQEgDUUNAAsgDa0hD0IAIRAgAiENIAwhASAGIQsDQCABQSdLDQMgAyAQIAM1AgB8IAs1AgAgD358IhA+AgAgEEIgiCEQIANBBGohAyABQQFqIQEgC0EEaiELIA1BfGoiDQ0ACyAFIQMCQCAQpyIBRQ0AIAwgBWoiA0EnSw0EIAQgA0ECdGogATYCACAIIQMLIAMgDGoiAyAKIAogA0kbIQogDiEBDAALC0EAIQpBACEDA0AgByABRg0FIANBAWohAyABKAIAIQsgAUEEaiIJIQEgC0UNACADQX9qIgEgCiAKIAFJGyEKIAkhAQwACwtBiKXBgAAgAUEoEIyGgIAAAAtBiKXBgAAgA0EoEIyGgIAAAAsgAkECdCEIIAJBAWohESAAIAVBAnRqQQRqIQ5BACEMIAYhC0EAIQoCQANAIAQgDEECdGohCQNAIAwhDSAJIQMgCyAORg0EIANBBGohCSANQQFqIQwgCygCACEHIAtBBGoiBSELIAdFDQALIAetIQ9CACEQIAghByANIQsgASEJAkADQCALQSdLDQEgAyAQIAM1AgB8IAk1AgAgD358IhA+AgAgEEIgiCEQIANBBGohAyALQQFqIQsgCUEEaiEJIAdBfGoiBw0ACyACIQMCQCAQpyILRQ0AIA0gAmoiA0EnSw0DIAQgA0ECdGogCzYCACARIQMLIAMgDWoiAyAKIAogA0kbIQogBSELDAELC0GIpcGAACALQSgQjIaAgAAAC0GIpcGAACADQSgQjIaAgAAACyAFQSgQj4aAgAAACyAGIARBoAEQ84aAgAAaIAAgCjYCACAEQaABaiSAgICAACAAC6ojAwF/BH4ZfyOAgICAAEHQCmsiBCSAgICAAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAEpAwAiBVANACABKQMIIgZQDQEgASkDECIHUA0CIAUgB3wiCCAFVA0DIAUgBn0gBVYNBCADQRFJDQggASwAGiEJIAEuARghCkEAIQEgBEGoCWpBAEGgARDyhoCAABogCq1CMIZCMIcgCEJ/fHl9QsKawegEfkKAoc2gtAJ8QiCIp0EQdEEQdSELIARBqAlqIQwDQCABQShGDQYgDCAFPgIAIAxBBGohDCABQQFqIQEgBUIgiCIFUEUNAAsgBCABNgIQIARBEGpBBHIgBEGoCWpBoAEQ84aAgAAhDUEAIQEgBEGoCWpBAEGgARDyhoCAABogBEGoCWohDANAIAFBKEYNByAMIAY+AgAgDEEEaiEMIAFBAWohASAGQiCIIgZQRQ0ACyAEIAE2ArgBIARBuAFqQQRyIARBqAlqQaABEPOGgIAAGkEAIQEgBEGoCWpBAEGgARDyhoCAABogBEGoCWohDANAIAFBKEYNCCAMIAc+AgAgDEEEaiEMIAFBAWohASAHQiCIIgdQRQ0ACyAEIAE2AuACIARB4AJqQQRyIARBqAlqQaABEPOGgIAAGiAEQoGAgIAQNwOIBCAEQZAEakEAQZwBEPKGgIAAGgJAAkAgCkEASA0AIARBEGogChCOhoCAABogBEG4AWogChCOhoCAABogBEHgAmogChCOhoCAABoMAQsgBEGIBGpBACAKa0EQdEEQdRCOhoCAABoLAkACQCALQX9KDQAgBEEQakEAIAtrQRB0QRB1IgEQlYaAgAAaIARBuAFqIAEQlYaAgAAaIARB4AJqIAEQlYaAgAAaDAELIARBiARqIAsQlYaAgAAaCyAEIAQoAhAiDjYCqAkgBEGoCWpBBHIgDUGgARDzhoCAABoCQAJAAkAgDiAEKALgAiIPIA4gD0sbIhBBKEsNACAQDQFBACEQDAILIBBBKBCPhoCAAAALIARBqAlqQQRyIQEgBEHgAmpBBHIhDEEAIRFBACESA0AgASABKAIAIhMgDCgCAGoiCiARQQFxaiIRNgIAIAogE0kgESAKSXIhESABQQRqIQEgDEEEaiEMIBJBAWoiEiAQSQ0ACyARRQ0AIBBBJ0sNCiAEQagJaiAQQQJ0akEEakEBNgIAIBBBAWohEAsgBCAQNgKoCSAEKAKIBCIRIBAgESAQSxsiAUEpTw0KIAFBAnQhAQJAAkACQAJAA0ACQCABDQBBf0EAIAEbIQwMAgsgAUUNAiAEQYgEaiABaiEMIARBqAlqIAFqIQogAUF8aiEBQX8gDCgCACIMIAooAgAiCkcgDCAKSRsiDEUNAAsLIAwgCUgNAQsgDkEpTw0NAkACQCAODQBBACEODAELIARBEGogDkECdCIMakEEaiEKIARBEGpBBHIhAUIAIQUDQCABIAE1AgBCCn4gBXwiBT4CACABQQRqIQEgBUIgiCEFIAxBfGoiDA0ACyAFpyIBRQ0AIA5BJ0sNDyAKIAE2AgAgDkEBaiEOCyAEIA42AhAgBCgCuAEiCkEpTw0PAkACQCAKDQBBACEKDAELIARBuAFqIApBAnQiDGpBBGohEiAEQbgBakEEciEBQgAhBQNAIAEgATUCAEIKfiAFfCIFPgIAIAFBBGohASAFQiCIIQUgDEF8aiIMDQALIAWnIgFFDQAgCkEnSw0RIBIgATYCACAKQQFqIQoLIAQgCjYCuAEgD0EpTw0RAkAgDw0AIARBADYC4AIMAgsgBEHgAmogD0ECdCIMakEEaiEKIARB4AJqQQRyIQFCACEFA0AgASABNQIAQgp+IAV8IgU+AgAgAUEEaiEBIAVCIIghBSAMQXxqIgwNAAsCQCAFpyIBRQ0AIA9BJ0sNEyAKIAE2AgAgD0EBaiEPCyAEIA82AuACDAELIAtBAWohCwsgBCARNgKwBSAEQbAFakEEciAEQYgEakEEciIUQaABEPOGgIAAIRUgBEGwBWpBARCOhoCAABogBCAEKAKIBDYC2AYgBEHYBmpBBHIgFEGgARDzhoCAACEWIARB2AZqQQIQjoaAgAAaIAQgBCgCiAQ2AoAIIARBgAhqQQRyIBRBoAEQ84aAgAAhFyAEQYAIakEDEI6GgIAAGgJAAkACQAJAIAQoAhAiECAEKAKACCIYIBAgGEsbIg5BKEsNACAEQagJakEEciEZIARB4AJqQQRyIRogBEEQakEEciEbIARBuAFqQQRyIRxBACEdA0AgHSEeIA5BAnQhAQJAAkACQANAAkAgAQ0AQX9BACABGyEMDAILIAFFDQIgBEEQaiABaiEMIARBgAhqIAFqIQogAUF8aiEBQX8gDCgCACIMIAooAgAiCkcgDCAKSRsiDEUNAAsLQQAhHyAMQf8BcUEBSw0BCwJAIA5FDQBBASERIA4hEiAbIQEgFyEMA0AgASABKAIAIhMgDCgCAEF/c2oiCiARQQFxaiIRNgIAIAogE0kgESAKSXIhESABQQRqIQEgDEEEaiEMIBJBf2oiEg0ACyARRQ0YCyAEIA42AhBBCCEfIA4hEAsgECAEKALYBiIBIBAgAUsbIg5BKU8NFyAOQQJ0IQECQAJAAkADQAJAIAENAEF/QQAgARshDAwCCyABRQ0CIARBEGogAWohDCAEQdgGaiABaiEKIAFBfGohAUF/IAwoAgAiDCAKKAIAIgpHIAwgCkkbIgxFDQALCyAMQf8BcUEBTQ0AIBAhDgwBCwJAIA5FDQBBACESQQEhESAbIQEgFiEMA0AgASABKAIAIhMgDCgCAEF/c2oiCiARQQFxaiIRNgIAIAogE0kgESAKSXIhESABQQRqIQEgDEEEaiEMIBJBAWoiEiAOSQ0ACyARRQ0aCyAEIA42AhAgH0EEciEfCyAOIAQoArAFIgEgDiABSxsiD0EpTw0ZIA9BAnQhAQJAAkACQANAAkAgAQ0AQX9BACABGyEMDAILIAFFDQIgBEEQaiABaiEMIARBsAVqIAFqIQogAUF8aiEBQX8gDCgCACIMIAooAgAiCkcgDCAKSRsiDEUNAAsLIAxB/wFxQQFNDQAgDiEPDAELAkAgD0UNAEEAIRJBASERIBshASAVIQwDQCABIAEoAgAiEyAMKAIAQX9zaiIKIBFBAXFqIhE2AgAgCiATSSARIApJciERIAFBBGohASAMQQRqIQwgEkEBaiISIA9JDQALIBFFDRwLIAQgDzYCECAfQQJqIR8LIA8gBCgCiAQiICAPICBLGyIQQSlPDRsgEEECdCEBAkACQAJAA0ACQCABDQBBf0EAIAEbIQwMAgsgAUUNAiAEQRBqIAFqIQwgBEGIBGogAWohCiABQXxqIQFBfyAMKAIAIgwgCigCACIKRyAMIApJGyIMRQ0ACwsgDEH/AXFBAU0NACAPIRAMAQsCQCAQRQ0AQQAhEkEBIREgGyEBIBQhDANAIAEgASgCACITIAwoAgBBf3NqIgogEUEBcWoiETYCACAKIBNJIBEgCklyIREgAUEEaiEBIAxBBGohDCASQQFqIhIgEEkNAAsgEUUNHgsgBCAQNgIQIB9BAWohHwsgHiADRg0DIAIgHmogH0EwajoAACAQIAQoArgBIh8gECAfSxsiAUEpTw0dIB5BAWohHSABQQJ0IQECQANAAkAgAQ0AQX9BACABGyEPDAILAkAgAQ0AQQEhDwwCCyAEQRBqIAFqIQwgBEG4AWogAWohCiABQXxqIQFBfyAMKAIAIgwgCigCACIKRyAMIApJGyIPRQ0ACwsgBCAQNgKoCSAZIA1BoAEQ84aAgAAhAQJAAkACQCAQIAQoAuACIiEgECAhSxsiDkEoSw0AIA4NAUEAIQ4MAgsgDkEoEI+GgIAAAAtBACERIAEhASAaIQxBACESA0AgASABKAIAIhMgDCgCAGoiCiARQQFxaiIRNgIAIAogE0kgESAKSXIhESABQQRqIQEgDEEEaiEMIBJBAWoiEiAOSQ0ACyARRQ0AIA5BJ0sNHyAEQagJaiAOQQJ0akEEakEBNgIAIA5BAWohDgsgBCAONgKoCSAgIA4gICAOSxsiAUEpTw0fIAFBAnQhAQJAA0ACQCABDQBBf0EAIAEbIQwMAgsCQCABDQBBASEMDAILIARBiARqIAFqIQwgBEGoCWogAWohCiABQXxqIQFBfyAMKAIAIgwgCigCACIKRyAMIApJGyIMRQ0ACwsgDyAJSA0CIAwgCUgNAiAQQSlPDSACQAJAIBANAEEAIRAMAQsgBEEQaiAQQQJ0IgxqQQRqIQpCACEFIBshAQNAIAEgATUCAEIKfiAFfCIFPgIAIAFBBGohASAFQiCIIQUgDEF8aiIMDQALIAWnIgFFDQAgEEEnSw0iIAogATYCACAQQQFqIRALIAQgEDYCECAfQSlPDSICQAJAIB8NAEEAIR8MAQsgBEG4AWogH0ECdCIMakEEaiEKQgAhBSAcIQEDQCABIAE1AgBCCn4gBXwiBT4CACABQQRqIQEgBUIgiCEFIAxBfGoiDA0ACyAFpyIBRQ0AIB9BJ0sNJCAKIAE2AgAgH0EBaiEfCyAEIB82ArgBICFBKU8NJAJAAkAgIQ0AQQAhIQwBCyAEQeACaiAhQQJ0IgxqQQRqIQpCACEFIBohAQNAIAEgATUCAEIKfiAFfCIFPgIAIAFBBGohASAFQiCIIQUgDEF8aiIMDQALIAWnIgFFDQAgIUEnSw0mIAogATYCACAhQQFqISELIAQgITYC4AIgECAYIBAgGEsbIg5BKE0NAAsLIA5BKBCPhoCAAAALAkAgDCAJTg0AAkAgDyAJTg0AIARBEGpBARCOhoCAACgCACIBIAQoAogEIgwgASAMSxsiAUEpTw0lIAFBAnQhAQJAA0ACQCABDQBBf0EAIAEbIQwMAgsgAUUNAiAEQRBqIAFqIQwgBEGIBGogAWohCiABQXxqIQFBfyAMKAIAIgwgCigCACIKRyAMIApJGyIMRQ0ACwsgDEH/AXFBAUsNAQsgBEEIaiACIAMgHRCYhoCAACAELQAIQQFxRQ0AIB0gA08NAiACIB1qIAQtAAk6AAAgC0EBaiELIB5BAmohHQsgACALOwEEIAAgHTYCACAEQdAKaiSAgICAAA8LQYj7wIAAIAMgAxCMhoCAAAALQZj7wIAAIB0gAxCMhoCAAAALQa74wIAAQRxBzPjAgAAQjYaAgAAAC0Hc+MCAAEEdQfz4wIAAEI2GgIAAAAtBjPnAgABBHEGo+cCAABCNhoCAAAALQbj5wIAAQTZB8PnAgAAQjYaAgAAAC0GA+sCAAEE3Qbj6wIAAEI2GgIAAAAtBiKXBgABBKEEoEIyGgIAAAAtBiKXBgABBKEEoEIyGgIAAAAtBiKXBgABBKEEoEIyGgIAAAAtByPrAgABBLUH4+sCAABCNhoCAAAALQYilwYAAIBBBKBCMhoCAAAALIAFBKBCPhoCAAAALIA5BKBCPhoCAAAALQYilwYAAIA5BKBCMhoCAAAALIApBKBCPhoCAAAALQYilwYAAIApBKBCMhoCAAAALIA9BKBCPhoCAAAALQYilwYAAIA9BKBCMhoCAAAALQZilwYAAQRpBiKXBgAAQjYaAgAAACyAOQSgQj4aAgAAAC0GYpcGAAEEaQYilwYAAEI2GgIAAAAsgD0EoEI+GgIAAAAtBmKXBgABBGkGIpcGAABCNhoCAAAALIBBBKBCPhoCAAAALQZilwYAAQRpBiKXBgAAQjYaAgAAACyABQSgQj4aAgAAAC0GIpcGAACAOQSgQjIaAgAAACyABQSgQj4aAgAAACyAQQSgQj4aAgAAAC0GIpcGAACAQQSgQjIaAgAAACyAfQSgQj4aAgAAAC0GIpcGAACAfQSgQjIaAgAAACyAhQSgQj4aAgAAAC0GIpcGAACAhQSgQjIaAgAAACyABQSgQj4aAgAAAC+8CAQV/AkAgAiADSQ0AIAMgAWoiBEF/aiEFQQAhBgJAAkACQAJAAkACQANAIAMgBmpFDQEgBSAGaiEHIAZBf2oiCCEGIActAABBOUYNAAsgAyAIaiIGIAJPDQIgBCAIaiIHIActAABBAWo6AABBACEFAkAgBkEBaiADSQ0ADAILIAhBAWohBgNAIAMgBmoiByACTw0EIAQgBmpBMDoAACAGQQFqIgcgBkkhCCAHIQYgCEUNAAsMAQtBASEFAkAgAw0AQTEhBwwBCyACRQ0DIAFBMToAAEEwIQcgA0ECSQ0AQQEhBgNAIAIgBkYNBUEwIQcgASAGakEwOgAAQQEhBSADIAZBAWoiBkcNAAsLIAAgBzoAASAAIAU6AAAPC0GkisGAACAGIAIQjIaAgAAAC0G0isGAACAHIAIQjIaAgAAAC0HEisGAAEEAQQAQjIaAgAAAC0HUisGAACACIAIQjIaAgAAACyADIAIQj4aAgAAAC6kcAwF/A34TfyOAgICAAEHQBmsiBSSAgICAAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgASkDACIGUA0AIAEpAwgiB1ANASABKQMQIghQDQIgBiAIfCAGVA0DIAYgB30gBlYNBSABLgEYIQlBACEBIAVBqAVqQQBBoAEQ8oaAgAAaIAmtQjCGQjCHIAZCf3x5fULCmsHoBH5CgKHNoLQCfEIgiKdBEHRBEHUhCiAFQagFaiELA0AgAUEoRg0FIAsgBj4CACALQQRqIQsgAUEBaiEBIAZCIIgiBlBFDQALIAUgATYCCCAFQQhqQQRyIAVBqAVqQaABEPOGgIAAGiAFQoGAgIAQNwOwASAFQbgBakEAQZwBEPKGgIAAGiAFQbABaiAFQQhqIAlBAEgbIAkgCUEfdSIBaiABc0EQdEEQdRCOhoCAABogBUGwAWogBUEIaiAKQX9KGyAKIApBH3UiAWogAXNBEHRBEHUQlYaAgAAaIAUgBSgCsAEiATYCqAUgBUGoBWpBBHIgBUGwAWpBBHIiDEGgARDzhoCAABogAyEJAkAgA0EJTQ0AAkAgAUEoSw0AIAMhCQNAAkAgAUUNACABQQJ0IQFCACEGA0AgBUGoBWogAWoiCyAGQiCGIAs1AgCEIgZCgJTr3AOAIgc+AgAgBiAHQoCU69wDfn0hBiABQXxqIgENAAsLIAlBd2oiCUEKSQ0CIAUoAqgFIgFBKE0NAAsLIAFBKBCPhoCAAAALAkACQAJAIAlBAnRBhPbAgABqKAIAIgtFDQAgBSgCqAUiAUEpTw0JIAENAUEAIQEMAgtBz6XBgABBG0GIpcGAABCNhoCAAAALIAFBAnQhASALrSEGQgAhBwNAIAVBqAVqIAFqIgsgB0IghiALNQIAhCIHIAaAIgg+AgAgByAIIAZ+fSEHIAFBfGoiAQ0ACyAFKAKoBSEBCwJAAkACQCABIAUoAggiDSABIA1LGyIOQShLDQAgDg0BQQAhDgwCCyAOQSgQj4aAgAAACyAFQagFakEEciEBIAVBCGpBBHIhC0EAIQ9BACEQA0AgASABKAIAIhEgCygCAGoiCSAPQQFxaiIPNgIAIAkgEUkgDyAJSXIhDyABQQRqIQEgC0EEaiELIBBBAWoiECAOSQ0ACyAPRQ0AIA5BJ0sNCCAFQagFaiAOQQJ0akEEakEBNgIAIA5BAWohDgsgBSAONgKoBSAOIAUoArABIhIgDiASSxsiAUEpTw0IIAFBAnQhAQJAAkACQANAAkAgAQ0AQX9BACABGyELDAILIAFFDQIgBUGoBWogAWohCyAFQbABaiABaiEJIAFBfGohAUF/IAsoAgAiCyAJKAIAIglHIAsgCUkbIgtFDQALCyALQf8BcUECSQ0AIA1BKU8NCwJAIA0NACAFQQA2AggMAgsgBUEIaiANQQJ0IgtqQQRqIQkgBUEIakEEciEBQgAhBgNAIAEgATUCAEIKfiAGfCIGPgIAIAFBBGohASAGQiCIIQYgC0F8aiILDQALAkAgBqciAUUNACANQSdLDQ0gCSABNgIAIA1BAWohDQsgBSANNgIIDAELIApBAWohCgtBASEPAkACQAJAAkAgCkEQdEEQdSIBIARBEHRBEHUiC04NAEEAIRMMAQsCQCAKIARrQRB0QRB1IAMgASALayADSRsiEw0AQQAhEwwBCyAFIBI2AtgCIAVB2AJqQQRyIAxBoAEQ84aAgAAhFCAFQdgCakEBEI6GgIAAGiAFIAUoArABNgKABCAFQYAEakEEciAMQaABEPOGgIAAIRUgBUGABGpBAhCOhoCAABogBSAFKAKwATYCqAUgBUGoBWpBBHIgDEGgARDzhoCAACEWIAVBqAVqQQMQjoaAgAAaIAVBsAFqQQRyIRcgBUEIakEEciEYIAUoAgghD0EAIRkDQCAZIRogD0EpTw0PIBpBAWohGSAPQQJ0IQEgGCELA0AgAUUNHiABQXxqIQEgCygCACEJIAtBBGohCyAJRQ0ACyAPIAUoAqgFIgEgDyABSxsiDkEpTw0QIA5BAnQhAQJAAkACQANAAkAgAQ0AQX9BACABGyELDAILIAFFDQIgBUEIaiABaiELIAVBqAVqIAFqIQkgAUF8aiEBQX8gCygCACILIAkoAgAiCUcgCyAJSRsiC0UNAAsLQQAhGyALQf8BcUECTw0BCwJAIA5FDQBBACEQQQEhDyAYIQEgFiELA0AgASABKAIAIhEgCygCAEF/c2oiCSAPQQFxaiIPNgIAIAkgEUkgDyAJSXIhDyABQQRqIQEgC0EEaiELIBBBAWoiECAOSQ0ACyAPRQ0TCyAFIA42AghBCCEbIA4hDwsgDyAFKAKABCIBIA8gAUsbIg5BKU8NEiAOQQJ0IQECQAJAAkADQAJAIAENAEF/QQAgARshCwwCCyABRQ0CIAVBCGogAWohCyAFQYAEaiABaiEJIAFBfGohAUF/IAsoAgAiCyAJKAIAIglHIAsgCUkbIgtFDQALCyALQf8BcUEBTQ0AIA8hDgwBCwJAIA5FDQBBACEQQQEhDyAYIQEgFSELA0AgASABKAIAIhEgCygCAEF/c2oiCSAPQQFxaiIPNgIAIAkgEUkgDyAJSXIhDyABQQRqIQEgC0EEaiELIBBBAWoiECAOSQ0ACyAPRQ0VCyAFIA42AgggG0EEciEbCyAOIAUoAtgCIgEgDiABSxsiDUEpTw0UIA1BAnQhAQJAAkACQANAAkAgAQ0AQX9BACABGyELDAILIAFFDQIgBUEIaiABaiELIAVB2AJqIAFqIQkgAUF8aiEBQX8gCygCACILIAkoAgAiCUcgCyAJSRsiC0UNAAsLIAtB/wFxQQFNDQAgDiENDAELAkAgDUUNAEEAIRBBASEPIBghASAUIQsDQCABIAEoAgAiESALKAIAQX9zaiIJIA9BAXFqIg82AgAgCSARSSAPIAlJciEPIAFBBGohASALQQRqIQsgEEEBaiIQIA1JDQALIA9FDRcLIAUgDTYCCCAbQQJqIRsLIA0gBSgCsAEiEiANIBJLGyIPQSlPDRYgD0ECdCEBAkACQAJAA0ACQCABDQBBf0EAIAEbIQsMAgsgAUUNAiAFQQhqIAFqIQsgBUGwAWogAWohCSABQXxqIQFBfyALKAIAIgsgCSgCACIJRyALIAlJGyILRQ0ACwsgC0H/AXFBAU0NACANIQ8MAQsCQCAPRQ0AQQAhEUEBIRAgGCEBIBchCwNAIAEgASgCACIOIAsoAgBBf3NqIgkgEEEBcWoiEDYCACAJIA5JIBAgCUlyIRAgAUEEaiEBIAtBBGohCyARQQFqIhEgD0kNAAsgEEUNGQsgBSAPNgIIIBtBAWohGwsgGiADRg0CIAIgGmogG0EwajoAACAPQSlPDRgCQAJAIA8NAEEAIQ8MAQsgBUEIaiAPQQJ0IgtqQQRqIQlCACEGIBghAQNAIAEgATUCAEIKfiAGfCIGPgIAIAFBBGohASAGQiCIIQYgC0F8aiILDQALIAanIgFFDQAgD0EnSw0aIAkgATYCACAPQQFqIQ8LIAUgDzYCCCAZIBNHDQALQQAhDwsgEkEpTw0YAkACQCASDQBBACESDAELIAVBsAFqIBJBAnQiAWpBBGohC0IAIQYDQCAMIAw1AgBCBX4gBnwiBj4CACAMQQRqIQwgBkIgiCEGIAFBfGoiAQ0ACyAGpyIBRQ0AIBJBJ0sNGiALIAE2AgAgEkEBaiESCyAFIBI2ArABIAUoAggiASASIAEgEksbIgFBKU8NGiABQQJ0IQECQAJAA0AgAUUNASABRQ0CIAVBCGogAWohCyAFQbABaiABaiEJIAFBfGohAUF/IAsoAgAiCyAJKAIAIglHIAsgCUkbIgtFDQALIAtB/wFxQQFHDR4MAQsgAQ0dIA8NACATQX9qIgEgA08NAiACIAFqLQAAQQFxRQ0dCyAFIAIgAyATEJiGgIAAIAUtAABBAXFFDRwgCkEQdEGAgARqQRB1IgogBEEQdEEQdUwNHCATIANPDRwgAiATaiAFLQABOgAAIBNBAWohEwwcC0H4+8CAACADIAMQjIaAgAAAC0GI/MCAACABIAMQjIaAgAAAC0Gu+MCAAEEcQaj7wIAAEI2GgIAAAAtB3PjAgABBHUG4+8CAABCNhoCAAAALQYz5wIAAQRxByPvAgAAQjYaAgAAAC0G4+cCAAEE2Qdj7wIAAEI2GgIAAAAtBiKXBgABBKEEoEIyGgIAAAAtBgPrAgABBN0Ho+8CAABCNhoCAAAALIAFBKBCPhoCAAAALQYilwYAAIA5BKBCMhoCAAAALIAFBKBCPhoCAAAALIA1BKBCPhoCAAAALQYilwYAAIA1BKBCMhoCAAAALIA9BKBCPhoCAAAALIA5BKBCPhoCAAAALQZilwYAAQRpBiKXBgAAQjYaAgAAACyAOQSgQj4aAgAAAC0GYpcGAAEEaQYilwYAAEI2GgIAAAAsgDUEoEI+GgIAAAAtBmKXBgABBGkGIpcGAABCNhoCAAAALIA9BKBCPhoCAAAALQZilwYAAQRpBiKXBgAAQjYaAgAAACyAPQSgQj4aAgAAAC0GIpcGAACAPQSgQjIaAgAAACyASQSgQj4aAgAAAC0GIpcGAACASQSgQjIaAgAAACyABQSgQj4aAgAAACyATIBpJDQEgEyADSw0CIBMgGkYNACACIBpqQTAgEyAaaxDyhoCAABoLIAAgCjsBBCAAIBM2AgAgBUHQBmokgICAgAAPCyAaIBMQkIaAgAAACyATIAMQj4aAgAAAC/QSBgF/BH4CfxR+BX8BfiOAgICAAEHQAGsiBCSAgICAAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgASkDACIFUA0AIAEpAwgiBlANASABKQMQIgdQDQIgBSAHfCIHIAVUDQMgBSAGfSIIIAVWDQQgA0ERSQ0FIAdC//////////8fVg0KIAQgAS8BGCIBOwEQIAQgCDcDCCABIAFBYGogASAHQoCAgIAQVCIJGyIKQXBqIAogB0IghiAHIAkbIgdCgICAgICAwABUIgkbIgpBeGogCiAHQhCGIAcgCRsiB0KAgICAgICAgAFUIgkbIgpBfGogCiAHQgiGIAcgCRsiB0KAgICAgICAgBBUIgkbIgpBfmogCiAHQgSGIAcgCRsiB0KAgICAgICAgMAAVCIJGyAHQgKGIAcgCRsiC0I/h6dBf3NqIglrQRB0QRB1IgpBAEgNBiAEQn8gCq1CP4MiDIgiByAIgzcDKCAIIAdWDQcgBCABOwEQIAQgBTcDCCAEIAcgBYM3AyggBSAHVg0IQaB/IAlrQRB0QRB1QdAAbEGwpwVqQc4QbSIBQdEATw0JIAFBBHQiAUGY/MCAAGopAwAiB0L/////D4MiBiAFIAyGIgVCIIgiDX4iDkIgiCIPIAdCIIgiECANfnwgECAFQv////8PgyIFfiIHQiCIIhF8IRIgDkL/////D4MgBiAFfkIgiHwgB0L/////D4N8QoCAgIAIfEIgiCETQgFBACAJIAFBoPzAgABqLwEAamtBP3GtIgeGIg5Cf3whFCAGIAggDIYiBUIgiCIVfiIIQv////8PgyAGIAVC/////w+DIgV+QiCIfCAQIAV+IgVC/////w+DfEKAgICACHxCIIghFiAQIBV+IQwgBUIgiCEXIAhCIIghGCABQaL8wIAAai8BACEBAkACQAJAAkAgECALIAtCf4VCP4iGIgVCIIgiGX4iGiAGIBl+IghCIIgiG3wgECAFQv////8PgyIFfiILQiCIIhx8IAhC/////w+DIAYgBX5CIIh8IAtC/////w+DfEKAgICACHxCIIgiHXxCAXwiHiAHiKciCkGQzgBJDQAgCkHAhD1JDQEgCkGAwtcvSQ0CQQhBCSAKQYCU69wDSSIJGyEfQYDC1y9BgJTr3AMgCRshCQwDCwJAIApB5ABJDQBBAkEDIApB6AdJIgkbIR9B5ABB6AcgCRshCQwDC0EBQQogCkEKSRshCSAKQQlLIR8MAgtBBEEFIApBoI0GSSIJGyEfQZDOAEGgjQYgCRshCQwBC0EGQQcgCkGAreIESSIJGyEfQcCEPUGAreIEIAkbIQkLIBIgE3whEiAeIBSDIQUgHyABa0EBaiEgIB4gDCAYfCAXfCAWfH1CAXwiCyAUgyEIQQAhAQJAAkADQCAKIAluISEgAyABRg0BIAIgAWoiIiAhQTBqIiM6AAACQAJAAkAgCyAKICEgCWxrIgqtIAeGIgwgBXwiBlYNACAfIAFHDQIgAUEBaiEBQgEhBgNAIAYhCyAIIQwgASADTw0GIAtCCn4hBiACIAFqIAVCCn4iBSAHiKdBMGoiCToAACABQQFqIQEgDEIKfiIIIAUgFIMiBVgNAAsgBiAeIBJ9fiIHIAZ8IQ0gCCAFfSAOVCIKDRQgByAGfSIUIAVWDQEMFAsgAUEBaiEKIAEgA08NECALIAZ9Ig4gCa0gB4YiB1QhASAeIBJ9IghCAXwhJCAIQn98IhQgBlgNESAOIAdUDREgHCAbfCAdfCIIIBp8IBJ9IAwgBXx9IR4gBSAPfCARfCATfCAQIA0gGX1+fCAbfSAcfSAdfSAMfCEOIAggECAZIBV9fnwgF30gGH0gFn0gBSAHfCAMfH1CAnwhEEIAIQUDQAJAIAYgB3wiCCAUVA0AIB4gBXwgByAOfFoNAEEAIQEMEwsgIiAjQX9qIiM6AAAgECAFfCIMIAdUIQEgCCAUWg0TIA4gB3whDiAFIAd9IQUgCCEGIAwgB1QNEwwACwsgAiABakF/aiEhIAxCCn4gDiAFfH0hECAOIBJCCn4gGyAcfCAdfCAafEIKfn0gC358IR4gFCAFfSEZQgAhBwNAAkAgBSAOfCIGIBRUDQAgGSAHfCAeIAV8Wg0AQQAhCgwUCyAhIAlBf2oiCToAACAQIAd8IgwgDlQhCiAGIBRaDRQgByAOfSEHIAYhBSAMIA5UDRQMAAsLIAFBAWohASAJQQpJISEgCUEKbiEJICFFDQALQaCIwYAAQRlBhIjBgAAQjYaAgAAAC0G8iMGAACADIAMQjIaAgAAAC0HMiMGAACABIAMQjIaAgAAAC0Gu+MCAAEEcQeSGwYAAEI2GgIAAAAtB3PjAgABBHUH0hsGAABCNhoCAAAALQYz5wIAAQRxBhIfBgAAQjYaAgAAAC0G4+cCAAEE2QZSHwYAAEI2GgIAAAAtBgPrAgABBN0Gkh8GAABCNhoCAAAALQcj6wIAAQS1BtIfBgAAQjYaAgAAAC0Gt9MCAAEEdQej0wIAAEI2GgIAAAAsgBEEkakG6gYCAADYCACAEQcQAakECNgIAIARCAzcCNCAEQbT1wIAANgIwIARBuoGAgAA2AhwgBCAEQShqNgJIIAQgBEEYajYCQCAEIARBzABqNgIgIAQgBEHIAGo2AhggBCAEQQhqNgJMIARBMGpBzPXAgAAQlIaAgAAACyAEQSRqQbqBgIAANgIAIARBxABqQQI2AgAgBEIDNwI0IARBtPXAgAA2AjAgBEG6gYCAADYCHCAEIARBKGo2AkggBCAEQRhqNgJAIAQgBEHMAGo2AiAgBCAEQcgAajYCGCAEIARBCGo2AkwgBEEwakHM9cCAABCUhoCAAAALQdSGwYAAIAFB0QAQjIaAgAAAC0HEh8GAAEEtQfSHwYAAEI2GgIAAAAsgCiADEI+GgIAAAAsgBiEICwJAICQgCFgNACABDQBBACEJIAggB3wiBSAkVA0DICQgCH0gBSAkfVoNAwtBACEJIAhCAlQNAiAIIAtCfHxWDQIgACAKNgIEIABBCGogIDsBAEEBIQkMAgsgBSEGCwJAIA0gBlgNACAKDQBBACEJIAYgDnwiBSANVA0BIA0gBn0gBSANfVoNAQtBACEJIAtCFH4gBlYNACAGIAtCWH4gCHxWDQAgACABNgIEIABBCGogIDsBAEEBIQkLIAAgCTYCACAEQdAAaiSAgICAAAuODQoBfwF+An8EfgN/AX4CfwF+An8BfiOAgICAAEEQayIFJICAgIAAAkACQAJAAkACQAJAAkACQAJAIAEpAwAiBlANACAGQv//////////H1YNASADRQ0DQaB/IAEvARgiAUFgaiABIAZCgICAgBBUIgcbIgFBcGogASAGQiCGIAYgBxsiBkKAgICAgIDAAFQiBxsiAUF4aiABIAZCEIYgBiAHGyIGQoCAgICAgICAAVQiBxsiAUF8aiABIAZCCIYgBiAHGyIGQoCAgICAgICAEFQiBxsiAUF+aiABIAZCBIYgBiAHGyIGQoCAgICAgICAwABUIgcbIAZCAoYgBiAHGyIGQj+Hp0F/c2oiB2tBEHRBEHVB0ABsQbCnBWpBzhBtIgFB0QBPDQIgAUEEdCIBQaL8wIAAai8BACEIAkACQAJAAkAgAUGY/MCAAGopAwAiCUL/////D4MiCiAGIAZCf4VCP4iGIgZCIIgiC34iDEIgiCAJQiCIIgkgC358IAkgBkL/////D4MiBn4iCUIgiHwgDEL/////D4MgCiAGfkIgiHwgCUL/////D4N8QoCAgIAIfEIgiHwiBkFAIAcgAUGg/MCAAGovAQBqa0EQdEEQdSINQT9xrSIMiKciDkGQzgBJDQAgDkHAhD1JDQEgDkGAwtcvSQ0CQQhBCSAOQYCU69wDSSIBGyEPQYDC1y9BgJTr3AMgARshBwwDCwJAIA5B5ABJDQBBAkEDIA5B6AdJIgEbIQ9B5ABB6AcgARshBwwDC0EBQQogDkEKSRshByAOQQlLIQ8MAgtBBEEFIA5BoI0GSSIBGyEPQZDOAEGgjQYgARshBwwBC0EGQQcgDkGAreIESSIBGyEPQcCEPUGAreIEIAEbIQcLQgEgDIYhEAJAAkACQAJAAkAgDyAIayIRQRB0QYCABGpBEHUiEiAEQRB0QRB1IgFMDQAgBiAQQn98IhODIQYgEiAEa0EQdEEQdSADIBIgAWsgA0kbIhRBf2ohFUEAIQEDQCAOIAduIQggAyABRg0EIA4gCCAHbGshDiACIAFqIAhBMGo6AAAgFSABRg0CIA8gAUYNAyABQQFqIQEgB0EKSSEIIAdBCm4hByAIRQ0AC0GgiMGAAEEZQdSJwYAAEI2GgIAAAAsgBkIKgCEGQQAhCCAHrSAMhiIJIBBYDQwgCSAQfSAQWA0MAkAgCSAGfSAGWA0AIAkgBkIBhn0gEEIBhloNCQsgBiAQWA0MIAkgBiAQfSIGfSAGVg0MQQAhAQJAIBFBEHRBgIAIakEQdSIHIARBEHRBEHVMDQAgAkExOgAAQQEhAQsgACABNgIEIABBCGogBzsBAAwLC0EAIQggB60gDIYiCSAQWA0LIAkgEH0gEFgNCwJAIAkgDq0gDIYgBnwiBn0gBlgNACAJIAZCAYZ9IBBCAYZaDQkLIAYgEFgNCyAJIAYgEH0iBn0gBlYNCyAFIAIgAyAUEJiGgIAAQQEhCAJAIAUtAABBAXFFDQAgEUEQdEGAgAhqQRB1IhIgBEEQdEEQdUwNACAUIANPDQAgAiAUaiAFLQABOgAAIBRBAWohFAsgACAUNgIEIABBCGogEjsBAAwLCyABQQFqIQEgDUF/akE/ca0hFkIBIQkDQEEAIQggCSIKIBaIQgBSDQsgASADTw0CIApCCn4hCSAGQgp+IgsgE4MhBiACIAFqIAsgDIinQTBqOgAAIBQgAUEBaiIBRw0ACyAQIAlYDQogECAJfSAJWA0KAkAgECAGfSAGWA0AIBAgBkIBhn0gCkIUfloNCQsgBiAJWA0KIBAgBiAJfSIGfSAGVg0KIAVBCGogAiADIBQQmIaAgABBASEIAkAgBS0ACEEBcUUNACARQRB0QYCACGpBEHUiEiAEQRB0QRB1TA0AIBQgA08NACACIBRqIAUtAAk6AAAgFEEBaiEUCyAAIBQ2AgQgAEEIaiASOwEADAoLQeSJwYAAIAMgAxCMhoCAAAALQfSJwYAAIAEgAxCMhoCAAAALQa74wIAAQRxBgInBgAAQjYaAgAAAC0GQicGAAEEkQbSJwYAAEI2GgIAAAAtB1IbBgAAgAUHRABCMhoCAAAALQdyIwYAAQSFBxInBgAAQjYaAgAAACyAAQQA2AgQgAEEIaiASOwEADAILIAAgFDYCBCAAQQhqIBI7AQAMAQsgACAUNgIEIABBCGogEjsBAAtBASEICyAAIAg2AgAgBUEQaiSAgICAAAsRACAANQIAQQEgARDDhoCAAAv1BQQDfwJ+AX8CfiOAgICAAEGQAWsiBCSAgICAACAEIAM2AmwCQAJAAkACQAJAAkACQAJAAkAgA0F+akEiSw0AAkAgAg0AIABBADoAAQwICwJAIAEtAABBK0cNAEEBIQUgAkF/aiICRQ0CIAFBAWohAQsgA0ELSQ0CIARB4ABqIQZCACEHQgAhCANAIAJFDQcCQCABLQAAIglBUGoiBUEKSQ0AAkAgCUGff2pBGkkNACAJQb9/akEaTw0GIAlBSWohBQwBCyAJQal/aiEFCyAFIANPDQQgBEHIAGogCEIAIAOtIgpCABD3hoCAACAEQdgAaiAHQgAgCkIAEPeGgIAAIARBOGpCAEIAIAdCABD3hoCAACAEKQNQIAQpA0CEQgBSIAYpAwAiByAEKQNIIAQpAzh8fCIKIAdUckEBRg0FIAFBAWohASACQX9qIQIgBCkDWCILIAWtfCIHIAtUIgUgCiAFrXwiCCAKVCAHIAtaG0EBRw0ADAYLCyAEQYQBakEBNgIAIARCATcCdCAEQaCMwYAANgJwIARBhICAgAA2AowBIAQgBEGIAWo2AoABIAQgBEHsAGo2AogBIARB8ABqQcCMwYAAEJSGgIAAAAsgAEEAOgABDAYLIARBMGohCUIAIQdCACEIA0AgAkUNBCABLQAAQVBqIgVBCUsNASAFIANPDQEgBEEYaiAIQgAgA60iCkIAEPeGgIAAIARBKGogB0IAIApCABD3hoCAACAEQQhqQgBCACAHQgAQ94aAgAAgBCkDICAEKQMQhEIAUiAJKQMAIgcgBCkDGCAEKQMIfHwiCiAHVHINAiABQQFqIQEgAkF/aiECIAQpAygiCyAFrXwiByALVCIFIAogBa18IgggClQgByALWhtFDQAMAwsLQQEhBSAAQQE6AAEMBAsgAEECOgABDAILIABBAjoAAQwBCyAAQRBqIAg3AwAgAEEIaiAHNwMAQQAhBQwBC0EBIQULIAAgBToAACAEQZABaiSAgICAAAtvAQF/QdCMwYAAIQICQAJAAkACQAJAIAAtAAAOBQABAgMEAAsgAUHfjcGAAEEmEJGGgIAADwsgAUHCjcGAAEEdEJGGgIAADwsgAUGcjcGAAEEmEJGGgIAADwtB9ozBgAAhAgsgASACQSYQkYaAgAAL/gYBCn8jgICAgABBMGsiAySAgICAACADQSRqIAE2AgAgA0EDOgAoIANCgICAgIAENwMIIAMgADYCIEEAIQQgA0EANgIYIANBADYCEAJAAkACQAJAAkACQCACKAIIIgUNACACKAIAIQYgAigCBCIHIAJBFGooAgAiBSAFIAdLGyIIRQ0BIAIoAhAhBUEBIQkgACAGKAIAIAYoAgQgASgCDBGPgICAAAANBSAGQQxqIQJBASEEA0ACQCAFKAIAIANBCGogBUEEaigCABGAgICAAABFDQBBASEJDAcLIAQgCE8NAiACQXxqIQAgAigCACEBIAJBCGohAiAFQQhqIQVBASEJIARBAWohBCADKAIgIAAoAgAgASADKAIkKAIMEY+AgIAAAEUNAAwGCwsgAigCACEGIAIoAgQiByACQQxqKAIAIgkgCSAHSxsiCkUNACACQRRqKAIAIQggAigCECELQQEhCSAAIAYoAgAgBigCBCABKAIMEY+AgIAAAA0EIAZBDGohAkEBIQQDQCADIAVBBGooAgA2AgwgAyAFQRxqLQAAOgAoIAMgBUEIaigCADYCCCAFQRhqKAIAIQlBACEBQQAhAAJAAkACQCAFQRRqKAIADgMBAAIBCyAJIAhPDQUgCUEDdCEMQQAhACALIAxqIgwoAgRBu4GAgABHDQEgDCgCACgCACEJC0EBIQALIAMgCTYCFCADIAA2AhAgBUEQaigCACEJAkACQAJAIAVBDGooAgAOAwEAAgELIAkgCE8NBiAJQQN0IQAgCyAAaiIAKAIEQbuBgIAARw0BIAAoAgAoAgAhCQtBASEBCyADIAk2AhwgAyABNgIYIAUoAgAiCSAITw0CAkAgCyAJQQN0aiIJKAIAIANBCGogCSgCBBGAgICAAABFDQBBASEJDAYLIAQgCk8NASACQXxqIQAgAigCACEBIAJBCGohAiAFQSBqIQVBASEJIARBAWohBCADKAIgIAAoAgAgASADKAIkKAIMEY+AgIAAAEUNAAwFCwsCQCAHIARNDQBBASEJIAMoAiAgBiAEQQN0aiIFKAIAIAUoAgQgAygCJCgCDBGPgICAAAANBAtBACEJDAMLQbySwYAAIAkgCBCMhoCAAAALQcySwYAAIAkgCBCMhoCAAAALQcySwYAAIAkgCBCMhoCAAAALIANBMGokgICAgAAgCQuXAQEDfyOAgICAAEEgayICJICAgIAAAkAgACABEKGGgIAADQAgAUEcaigCACEDIAEoAhghBCACQRxqQQA2AgAgAkGs9MCAADYCGCACQgE3AgwgAkGIjsGAADYCCCAEIAMgAkEIahCfhoCAAA0AIABBBGogARChhoCAACEBIAJBIGokgICAgAAgAQ8LIAJBIGokgICAgABBAQvRAgEDfyOAgICAAEGAAWsiAiSAgICAAAJAAkACQAJAAkAgASgCACIDQRBxDQAgACgCACEEIANBIHENASAErUEBIAEQw4aAgAAhAAwCCyAAKAIAIQRBACEAA0AgAiAAakH/AGogBEEPcSIDQTByIANB1wBqIANBCkkbOgAAIABBf2ohACAEQQR2IgQNAAsgAEGAAWoiBEGBAU8NAiABQQFBwZDBgABBAiACIABqQYABakEAIABrEMWGgIAAIQAMAQtBACEAA0AgAiAAakH/AGogBEEPcSIDQTByIANBN2ogA0EKSRs6AAAgAEF/aiEAIARBBHYiBA0ACyAAQYABaiIEQYEBTw0CIAFBAUHBkMGAAEECIAIgAGpBgAFqQQAgAGsQxYaAgAAhAAsgAkGAAWokgICAgAAgAA8LIARBgAEQkIaAgAAACyAEQYABEJCGgIAAAAsMAEKY3Iamxs6tyQgLIQAgASgCGEGQjsGAAEELIAFBHGooAgAoAgwRj4CAgAAACyEAIAEoAhhBm47BgABBDiABQRxqKAIAKAIMEY+AgIAAAAv6AgIDfwF+QQIhAwJAAkACQAJAAkAgAUF3aiIEQR5NDQAgAUHcAEcNAQwCC0H0ACEFAkACQCAEDh8FAQICAAICAgICAgICAgICAgICAgICAgICAwICAgIDBQtB8gAhBQwEC0HuACEFDAMLAkAgAkUNACABQQp2IQUCQAJAAkACQCABQYDYB0kNAEEeIQMgBUGAB0cNBAwBCyAFQcC+wYAAai0AACIDQR5LDQELIANBBHQgAUEGdkEPcXJBu7/BgABqLQAAIgVBiwFPDQFBAyEDIAVBA3RBsMPBgABqKQMAQgEgAUE/ca2Gg1ANAiABQQFyZ0ECdkEHc61CgICAgNAAhCEGDAQLQcykwYAAIANBHxCMhoCAAAALQdykwYAAIAVBiwEQjIaAgAAACwJAIAEQpoaAgABFDQBBASEDDAILIAFBAXJnQQJ2QQdzrUKAgICA0ACEIQZBAyEDDAELCyABIQULIAAgBTYCBCAAIAM2AgAgAEEIaiAGNwIAC/AGAQd/AkACQAJAAkACQAJAAkACQAJAAkAgAEGAgARJDQAgAEGAgAhJDQFBACEBIABB4ot0akHijSxJDQggAEGfqHRqQZ8YSQ0IIABB3uJ0akEOSQ0IIABB/v//AHFBnvAKRg0IIABBqbJ1akEpSQ0IIABBy5F1akELSQ0IIABBkPxHakGP/AtLDwsgAEGA/gNxQQh2IQJB/JnBgAAhA0EAIQQgAEH/AXEhBQNAIANBAmohBiAEIAMtAAEiAWohBwJAIAMtAAAiAyACRg0AIAMgAksNCCAHIQQgBiEDIAZBzprBgABHDQEMCAsgByAESQ0CIAdBpQJLDQMgBEHOmsGAAGohAwJAA0AgAUUNASABQX9qIQEgAy0AACEEIANBAWohAyAEIAVHDQALQQAhAQwJCyAHIQQgBiEDIAZBzprBgABHDQAMBwsLIABBgP4DcUEIdiECQa2fwYAAIQNBACEEIABB/wFxIQUDQCADQQJqIQYgBCADLQABIgFqIQcCQCADLQAAIgMgAkYNACADIAJLDQYgByEEIAYhAyAGQfOfwYAARw0BDAYLIAcgBEkNAyAHQaYBSw0EIARB85/BgABqIQMCQANAIAFFDQEgAUF/aiEBIAMtAAAhBCADQQFqIQMgBCAFRw0AC0EAIQEMCAsgByEEIAYhAyAGQfOfwYAARw0ADAULCyAEIAcQkIaAgAAACyAHQaUCEI+GgIAAAAsgBCAHEJCGgIAAAAsgB0GmARCPhoCAAAALIABB//8DcSEFQZmhwYAAIQNBASEBAkADQCADQQFqIQACQAJAIAMtAAAiBEEYdEEYdSIHQQBIDQAgACEDDAELIABBsaTBgABGDQIgB0H/AHFBCHQgAy0AAXIhBCADQQJqIQMLIAUgBGsiBUEASA0DIAFBAXMhASADQbGkwYAARw0ADAMLC0GpjsGAAEErQeyZwYAAEI2GgIAAAAsgAEH//wNxIQVB85zBgAAhA0EBIQEDQCADQQFqIQACQAJAIAMtAAAiBEEYdEEYdSIHQQBIDQAgACEDDAELIABBrZ/BgABGDQMgB0H/AHFBCHQgAy0AAXIhBCADQQJqIQMLIAUgBGsiBUEASA0BIAFBAXMhASADQa2fwYAARw0ACwsgAUEBcQ8LQamOwYAAQStB7JnBgAAQjYaAgAAAC/IDAgR/An4jgICAgABBwABrIgUkgICAgABBASEGAkAgAC0ABA0AIAAtAAUhBwJAIAAoAgAiCC0AAEEEcQ0AQQEhBiAIKAIYQaGQwYAAQaOQwYAAIAdB/wFxIgcbQQJBAyAHGyAIQRxqKAIAKAIMEY+AgIAAAA0BQQEhBiAAKAIAIggoAhggASACIAhBHGooAgAoAgwRj4CAgAAADQFBASEGIAAoAgAiCCgCGEHcjsGAAEECIAhBHGooAgAoAgwRj4CAgAAADQEgAyAAKAIAIAQoAgwRgICAgAAAIQYMAQsCQCAHQf8BcQ0AQQEhBiAIKAIYQZyQwYAAQQMgCEEcaigCACgCDBGPgICAAAANASAAKAIAIQgLQQEhBiAFQQE6ABcgBUE0akGAkMGAADYCACAFIAgpAhg3AwggBSAFQRdqNgIQIAgpAgghCSAIKQIQIQogBSAILQAgOgA4IAUgCjcDKCAFIAk3AyAgBSAIKQIANwMYIAUgBUEIajYCMCAFQQhqIAEgAhCyhoCAAA0AIAVBCGpB3I7BgABBAhCyhoCAAA0AIAMgBUEYaiAEKAIMEYCAgIAAAA0AIAUoAjBBn5DBgABBAiAFKAI0KAIMEY+AgIAAACEGCyAAQQE6AAUgACAGOgAEIAVBwABqJICAgIAAIAALbAEBfyOAgICAAEEwayIDJICAgIAAIAMgATYCDCADIAA2AgggA0EkakEBNgIAIANCATcCFCADQdSOwYAANgIQIANBuIGAgAA2AiwgAyADQShqNgIgIAMgA0EIajYCKCADQRBqIAIQlIaAgAAACxQAIAEgACgCACAAKAIEEJGGgIAAC5MBAQF/I4CAgIAAQcAAayIFJICAgIAAIAUgATYCDCAFIAA2AgggBSADNgIUIAUgAjYCECAFQSxqQQI2AgAgBUE8akG8gYCAADYCACAFQgI3AhwgBUHgjsGAADYCGCAFQbiBgIAANgI0IAUgBUEwajYCKCAFIAVBEGo2AjggBSAFQQhqNgIwIAVBGGogBBCUhoCAAAALGAAgACgCACABIAAoAgQoAgwRgICAgAAACwcAIAAoAggLBwAgACgCDAvhAwEEfyOAgICAAEHAAGsiAiSAgICAAEEBIQMCQCABKAIYQYCPwYAAQQwgAUEcaigCACgCDBGPgICAAAANAAJAAkAgACgCCCIDDQAgACgCACIDIAAoAgQoAgwRioCAgAAAQuSuwoWXm6WIEVINASACIAM2AgwgAkG9gYCAADYCFCACIAJBDGo2AhAgASgCGCEEIAEoAhwhBUEBIQMgAkE8akEBNgIAIAJCAjcCLCACQZCPwYAANgIoIAIgAkEQajYCOCAEIAUgAkEoahCfhoCAAA0CDAELIAIgAzYCDCACQb6BgIAANgIUIAIgAkEMajYCECABKAIYIQQgASgCHCEFQQEhAyACQTxqQQE2AgAgAkICNwIsIAJBkI/BgAA2AiggAiACQRBqNgI4IAQgBSACQShqEJ+GgIAADQELIAAoAgwhAyACQRBqQRRqQYSAgIAANgIAIAJBEGpBDGpBhICAgAA2AgAgAiADQQxqNgIgIAIgA0EIajYCGCACQbiBgIAANgIUIAIgAzYCECABKAIYIQMgASgCHCEBIAJBKGpBFGpBAzYCACACQgM3AiwgAkGkj8GAADYCKCACIAJBEGo2AjggAyABIAJBKGoQn4aAgAAhAwsgAkHAAGokgICAgAAgAwsZACABIAAoAgAiACgCACAAKAIEEJGGgIAAC3kBA38jgICAgABBIGsiAiSAgICAACABQRxqKAIAIQMgASgCGCEEIAJBCGpBEGogACgCACIBQRBqKQIANwMAIAJBCGpBCGogAUEIaikCADcDACACIAEpAgA3AwggBCADIAJBCGoQn4aAgAAhASACQSBqJICAgIAAIAELBAAgAAvOBAEHfyOAgICAAEEwayIDJICAgIAAAkACQCACDQBBACEEDAELIANBKGohBQJAAkACQAJAA0ACQCAAKAIILQAARQ0AIAAoAgBBmJDBgABBBCAAKAIEKAIMEY+AgIAAAA0FCyADQQo2AiggA0KKgICAEDcDICADIAI2AhwgA0EANgIYIAMgAjYCFCADIAE2AhAgA0EIakEKIAEgAhCzhoCAAAJAAkACQAJAIAMoAghBAUcNACADKAIMIQQDQCADIAQgAygCGGpBAWoiBDYCGAJAAkAgBCADKAIkIgZPDQAgAygCFCEHDAELIAMoAhQiByAESQ0AIAZBBU8NByADKAIQIAQgBmsiCGoiCSAFRg0EIAkgBSAGEPSGgIAARQ0ECyADKAIcIgkgBEkNAiAHIAlJDQIgAyAGIANBEGpqQRdqLQAAIAMoAhAgBGogCSAEaxCzhoCAACADKAIEIQQgAygCAEEBRg0ACwsgAyADKAIcNgIYCyAAKAIIQQA6AAAgAiEEDAELIAAoAghBAToAACAIQQFqIQQLIAAoAgQhCSAAKAIAIQYCQCAERSACIARGciIHDQAgAiAETQ0DIAEgBGosAABBv39MDQMLIAYgASAEIAkoAgwRj4CAgAAADQQCQCAHDQAgAiAETQ0EIAEgBGosAABBv39MDQQLIAEgBGohASACIARrIgINAAtBACEEDAQLIAZBBBCPhoCAAAALIAEgAkEAIAQQkoaAgAAACyABIAIgBCACEJKGgIAAAAtBASEECyADQTBqJICAgIAAIAQL9wIBBn9BACEEAkACQCACQQNxIgVFDQBBBCAFayIFRQ0AIAMgBSAFIANLGyEEQQAhBSABQf8BcSEGA0AgBCAFRg0BIAIgBWohByAFQQFqIQUgBy0AACIHIAZHDQALQQEhAyAHIAFB/wFxRkEBakEBcSAFakF/aiEFDAELIAFB/wFxIQYCQAJAIANBCEkNACAEIANBeGoiCEsNACAGQYGChAhsIQUCQANAIAIgBGoiB0EEaigCACAFcyIJQX9zIAlB//37d2pxIAcoAgAgBXMiB0F/cyAHQf/9+3dqcXJBgIGChHhxDQEgBEEIaiIEIAhNDQALCyAEIANLDQELIAIgBGohCSADIARrIQJBACEDQQAhBQJAA0AgAiAFRg0BIAkgBWohByAFQQFqIQUgBy0AACIHIAZHDQALQQEhAyAHIAFB/wFxRkEBakEBcSAFakF/aiEFCyAFIARqIQUMAQsgBCADEJCGgIAAAAsgACAFNgIEIAAgAzYCAAuLAQEDfyAALQAEIQECQCAALQAFRQ0AIAFB/wFxIQJBASEBAkAgAg0AIAAoAgAiAUEcaigCACgCDCECIAEoAhghAwJAIAEtAABBBHENACADQaeQwYAAQQIgAhGPgICAAAAhAQwBCyADQaaQwYAAQQEgAhGPgICAAAAhAQsgACABOgAECyABQf8BcUEARwv4AgIEfwJ+I4CAgIAAQcAAayIDJICAgIAAQQEhBAJAIAAtAAgNACAAKAIEIQUCQCAAKAIAIgYtAABBBHENAEEBIQQgBigCGEGhkMGAAEGrkMGAACAFG0ECQQEgBRsgBkEcaigCACgCDBGPgICAAAANASABIAAoAgAgAigCDBGAgICAAAAhBAwBCwJAIAUNAEEBIQQgBigCGEGpkMGAAEECIAZBHGooAgAoAgwRj4CAgAAADQEgACgCACEGC0EBIQQgA0EBOgAXIANBNGpBgJDBgAA2AgAgAyAGKQIYNwMIIAMgA0EXajYCECAGKQIIIQcgBikCECEIIAMgBi0AIDoAOCADIAg3AyggAyAHNwMgIAMgBikCADcDGCADIANBCGo2AjAgASADQRhqIAIoAgwRgICAgAAADQAgAygCMEGfkMGAAEECIAMoAjQoAgwRj4CAgAAAIQQLIAAgBDoACCAAIAAoAgRBAWo2AgQgA0HAAGokgICAgAAgAAunAQEDfyAALQAIIQECQCAAKAIEIgJFDQAgAUH/AXEhA0EBIQECQCADDQACQCACQQFHDQAgAC0ACUUNACAAKAIAIgMtAABBBHENAEEBIQEgAygCGEGskMGAAEEBIANBHGooAgAoAgwRj4CAgAAADQELIAAoAgAiASgCGEGtkMGAAEEBIAFBHGooAgAoAgwRj4CAgAAAIQELIAAgAToACAsgAUH/AXFBAEcL9gICA38CfiOAgICAAEHAAGsiAySAgICAAEEBIQQCQCAALQAEDQAgAC0ABSEEAkAgACgCACIFLQAAQQRxDQACQCAEQf8BcUUNAEEBIQQgBSgCGEGhkMGAAEECIAVBHGooAgAoAgwRj4CAgAAADQIgACgCACEFCyABIAUgAigCDBGAgICAAAAhBAwBCwJAIARB/wFxDQBBASEEIAUoAhhBrpDBgABBASAFQRxqKAIAKAIMEY+AgIAAAA0BIAAoAgAhBQtBASEEIANBAToAFyADQTRqQYCQwYAANgIAIAMgBSkCGDcDCCADIANBF2o2AhAgBSkCCCEGIAUpAhAhByADIAUtACA6ADggAyAHNwMoIAMgBjcDICADIAUpAgA3AxggAyADQQhqNgIwIAEgA0EYaiACKAIMEYCAgIAAAA0AIAMoAjBBn5DBgABBAiADKAI0KAIMEY+AgIAAACEECyAAQQE6AAUgACAEOgAEIANBwABqJICAgIAACxAAIAAgASACELeGgIAAIAALOgEBf0EBIQECQCAALQAEDQAgACgCACIAKAIYQcCQwYAAQQEgAEEcaigCACgCDBGPgICAAAAhAQsgAQuaDAUBfwJ+AX8CfgR/I4CAgIAAQfAIayIEJICAgIAAIAG9IgVC/////////weDIgZCgICAgICAgAiEIAZCAYYgBUI0iKdB/w9xIgcbIghCAYMhCSAGUCEKQQIhCwJAAkACQAJAAkAgBUKAgICAgICA+P8AgyIGUEUNAEECQQMgChsOBAQBAgMECwJAIAZCgICAgICAgPj/AFINACAKDgQEAQIDBAtCgICAgICAgCAgCEIBhiAIQoCAgICAgIAIUSIMGyEIQgJCASAMGyEGIAmnQQFzIQtBy3dBzHcgDBsgB2ohDAwDC0EDIQsMAgtBBCELDAELIAdBzXdqIQwgCadBAXMhC0IBIQYLIARBkAhqQQRqIARBEGpBBGotAAAiCjoAACAEIAQoABAiBzYCkAggBEHvCGogCjoAACAEIAw7AegIIAQgBjcD4AggBEIBNwPYCCAEIAg3A9AIIAQgBzYA6wggBCALOgDqCAJAAkACQAJAAkAgC0F+aiILQQMgC0H/AXEiDUEDSRtB/wFxIgtBAksNAAJAAkAgCw4DAQIAAQtBrPTAgAAhCkEAIQcCQCACQf8BcQ4EBQAEAwULQaeLwYAAQaz0wIAAIAVCAFMbIQogBUI/iKchBwwECyAEQQM2ApgIIARBrIvBgAA2ApQIIARBAjsBkAhBASELQQAhB0Gs9MCAACEKDAQLQaeLwYAAQaz0wIAAIAVCAFMiBxtBp4vBgABBqIvBgAAgBxsgAkH/AXEiAkECSRshCkEBIQsgByACQQFLciEHAkAgDUECTQ0AAkACQAJAAkBBdEEFIAxBEHRBEHUiC0EASBsgC2xBBHZBFWoiC0GACEsNACAEQZAIaiAEQdAIaiAEQRBqIAtBACADa0GAgH4gA0GAgAJJGyIMEJuGgIAAIAxBEHRBEHUhAgJAAkAgBCgCkAhBAUYNACAEQQhqIARB0AhqIARBEGogCyACEJmGgIAAIAQvAQwhCyAEKAIIIQwMAQsgBEGYCGovAQAhCyAEKAKUCCEMCwJAIAtBEHRBEHUgAkwNACAMQYEITw0CIAxFDQMgBC0AEEExSQ0EAkACQCALQRB0QRB1IgJBAUgNAEECIQsgBEECOwGQCCAEIARBEGo2ApQIIAwgAk0NASAEQQI7AagIIARBATYCpAggBEGmi8GAADYCoAggBEECOwGcCCAEIAI2ApgIIAQgBEEQaiACajYCrAggBCAMIAJrIg02ArAIQQMhCyANIANPDQsgBEEAOwG0CCAEIAMgDGsgAmo2ArgIQQQhCwwLCyAEQQI7AagIIARBAjYCmAggBEGki8GAADYClAggBEECOwGQCCAEQQA7AZwIIARBACACayINNgKgCCAEIAw2ArAIIAQgBEEQajYCrAhBAyELIAwgA08NCiADIAxrIgwgDU0NCiAEQQA7AbQIIAQgDCACajYCuAhBBCELDAoLIARBADsBnAggBCAMNgKYCCAEIAIgDGs2AqAIIANFDQkgBCADNgK4CCAEQQA7AbQIIARBATYCsAggBEGmi8GAADYCrAggBEECOwGoCEEEIQsMCQtBAiELIARBAjsBkAgCQCADRQ0AIAQgAzYCoAggBEEAOwGcCCAEQQI2ApgIIARBpIvBgAA2ApQIDAkLQQEhCyAEQQE2ApgIIARBrPTAgAA2ApQIDAgLQa+LwYAAQSVB1IvBgAAQjYaAgAAACyAMQYAIEI+GgIAAAAtB3IjBgABBIUHkisGAABCNhoCAAAALQfSKwYAAQR9BlIvBgAAQjYaAgAAACyAEQQM2ApgIIARBqYvBgAA2ApQIIARBAjsBkAgMAwtBp4vBgABBqIvBgAAgBUIAUxshCkEBIQcMAQtBqIvBgAAhCkEBIQcLQQIhCyAEQQI7AZAIAkAgA0UNACAEIAM2AqAIIARBADsBnAggBEECNgKYCCAEQaSLwYAANgKUCAwBC0EBIQsgBEEBNgKYCCAEQaz0wIAANgKUCAsgBEHMCGogCzYCACAEIAc2AsQIIAQgCjYCwAggBCAEQZAIajYCyAggACAEQcAIahC7hoCAACELIARB8AhqJICAgIAAIAsLkQUBCX8jgICAgABBEGsiAiSAgICAAAJAAkAgACgCCEEBRg0AIAAgARDHhoCAACEDDAELIABBDGooAgAhBCACQQxqIAFBDGooAgAiBTYCACACIAFBCGooAgAiAzYCCCACIAFBBGooAgAiBjYCBCACIAEoAgAiATYCACAALQAgIQcgACgCBCEIAkACQAJAIAAtAABBCHENACAGIQkgByEKDAELIAAoAhggASAGIABBHGooAgAoAgwRj4CAgAAADQFBASEKIABBAToAICAAQTA2AgRBACEJIAJBADYCBCACQaz0wIAANgIAQQAgBCAGayIBIAEgBEsbIQQLAkAgBUUNACADIAVBDGxqIQYDQCADIgFBDGohAwJAAkACQAJAIAEvAQAOAwABAgALIAFBBGooAgAhAQwCCwJAIAFBAmovAQAiBUHoB0kNAEEEQQUgBUGQzgBJGyEBDAILQQEhASAFQQpJDQFBAkEDIAVB5ABJGyEBDAELIAFBCGooAgAhAQsgASAJaiEJIAYgA0cNAAsLAkACQAJAIAQgCU0NAEEAIQMgBCAJayIBIQkCQAJAAkAgCkEDcQ4EAgEAAQILIAFBAXYhAyABQQFqQQF2IQkMAQtBACEJIAEhAwsgA0EBaiEDA0AgA0F/aiIDRQ0CIAAoAhggACgCBCAAKAIcKAIQEYCAgIAAAA0EDAALCyAAIAIQx4aAgAAhAwwBCyAAKAIEIQEgACACEMeGgIAADQEgCUEBaiEDIAAoAhwhCSAAKAIYIQYDQAJAIANBf2oiAw0AQQAhAwwCCyAGIAEgCSgCEBGAgICAAABFDQALQQEhAwsgACAHOgAgIAAgCDYCBAwBC0EBIQMLIAJBEGokgICAgAAgAwuYCgUBfwJ+AX8CfgR/I4CAgIAAQYABayIEJICAgIAAIAG9IgVC/////////weDIgZCgICAgICAgAiEIAZCAYYgBUI0iKdB/w9xIgcbIghCAYMhCSAGUCEKQQIhCwJAAkACQAJAAkAgBUKAgICAgICA+P8AgyIGUEUNAEECQQMgChsOBAQBAgMECwJAIAZCgICAgICAgPj/AFINACAKDgQEAQIDBAtCgICAgICAgCAgCEIBhiAIQoCAgICAgIAIUSIMGyEIQgJCASAMGyEGIAmnQQFzIQtBy3dBzHcgDBsgB2ohDAwDC0EDIQsMAgtBBCELDAELIAdBzXdqIQwgCadBAXMhC0IBIQYLIARBCGpBBGogBEEgakEEai0AACIKOgAAIAQgBCgAICIHNgIIIARB/wBqIAo6AAAgBCAMOwF4IAQgBjcDcCAEQgE3A2ggBCAINwNgIAQgBzYAeyAEIAs6AHoCQAJAAkACQAJAIAtBfmoiC0EDIAtB/wFxIgdBA0kbQf8BcSILQQJLDQACQAJAIAsOAwECAAELQaz0wIAAIQxBACEKAkAgAkH/AXEOBAUABAMFC0Gni8GAAEGs9MCAACAFQgBTGyEMIAVCP4inIQoMBAsgBEEDNgIoIARBrIvBgAA2AiQgBEECOwEgQQEhC0EAIQpBrPTAgAAhDAwEC0Gni8GAAEGs9MCAACAFQgBTIgobQaeLwYAAQaiLwYAAIAobIAJB/wFxIgJBAkkbIQxBASELIAogAkEBS3IhCgJAIAdBAk0NACAEQSBqIARB4ABqIARBCGpBERCahoCAAAJAAkAgBCgCIEEBRg0AIAQgBEHgAGogBEEIakEREJeGgIAAIAQvAQQhCyAEKAIAIQcMAQsgBEEoai8BACELIAQoAiQhBwsCQAJAAkAgB0ESTw0AIAdFDQEgBC0ACEExSQ0CAkACQAJAIAtBEHRBEHUiAkEBSA0AQQIhCyAEQQI7ASAgBCAEQQhqNgIkIAcgAk0NASAEQQI7ATggBEEBNgI0IARBpovBgAA2AjAgBEECOwEsIAQgAjYCKCAEIARBCGogAmo2AjwgBCAHIAJrIg02AkBBAyELIA0gA08NCiAEQQA7AUQgBCADIAdrIAJqNgJIDAILIARBAjsBOCAEQQI2AiggBEGki8GAADYCJCAEQQI7ASAgBEEAOwEsIARBACACayINNgIwIAQgBzYCQCAEIARBCGo2AjxBAyELIAcgA08NCSADIAdrIgcgDU0NCSAEQQA7AUQgBCAHIAJqNgJIDAELIARBADsBLCAEIAc2AiggBCACIAdrNgIwIANFDQggBCADNgJIIARBADsBRCAEQQE2AkAgBEGmi8GAADYCPCAEQQI7ATgLQQQhCwwHCyAHQREQj4aAgAAAC0HciMGAAEEhQeSKwYAAEI2GgIAAAAtB9IrBgABBH0GUi8GAABCNhoCAAAALIARBAzYCKCAEQamLwYAANgIkIARBAjsBIAwDC0Gni8GAAEGoi8GAACAFQgBTGyEMQQEhCgwBC0Goi8GAACEMQQEhCgtBAiELIARBAjsBIAJAIANFDQAgBCADNgIwIARBADsBLCAEQQI2AiggBEGki8GAADYCJAwBC0EBIQsgBEEBNgIoIARBrPTAgAA2AiQLIARB3ABqIAs2AgAgBCAKNgJUIAQgDDYCUCAEIARBIGo2AlggACAEQdAAahC7hoCAACELIARBgAFqJICAgIAAIAsLmwIBAn8jgICAgABBEGsiAiSAgICAACACQQA2AgwCQAJAAkACQCABQYABSQ0AIAFBgBBJDQEgAkEMaiEDIAFBgIAETw0CIAIgAUE/cUGAAXI6AA4gAiABQQZ2QT9xQYABcjoADSACIAFBDHZBD3FB4AFyOgAMQQMhAQwDCyACIAE6AAwgAkEMaiEDQQEhAQwCCyACIAFBP3FBgAFyOgANIAIgAUEGdkEfcUHAAXI6AAwgAkEMaiEDQQIhAQwBCyACIAFBP3FBgAFyOgAPIAIgAUESdkHwAXI6AAwgAiABQQZ2QT9xQYABcjoADiACIAFBDHZBP3FBgAFyOgANQQQhAQsgACADIAEQsoaAgAAhASACQRBqJICAgIAAIAELcQEBfyOAgICAAEEgayICJICAgIAAIAIgADYCBCACQQhqQRBqIAFBEGopAgA3AwAgAkEIakEIaiABQQhqKQIANwMAIAIgASkCADcDCCACQQRqQYySwYAAIAJBCGoQn4aAgAAhASACQSBqJICAgIAAIAELEQAgACgCACABIAIQsoaAgAALDwAgACgCACABEL2GgIAAC3QBAX8jgICAgABBIGsiAiSAgICAACACIAAoAgA2AgQgAkEIakEQaiABQRBqKQIANwMAIAJBCGpBCGogAUEIaikCADcDACACIAEpAgA3AwggAkEEakGMksGAACACQQhqEJ+GgIAAIQEgAkEgaiSAgICAACABCxEAIAA1AgBBASABEMOGgIAAC+kCAwJ/AX4DfyOAgICAAEEwayIDJICAgIAAQSchBAJAAkAgAEKQzgBaDQAgACEFDAELQSchBANAIANBCWogBGoiBkF8aiAAIABCkM4AgCIFQpDOAH59pyIHQf//A3FB5ABuIghBAXRBw5DBgABqLwAAOwAAIAZBfmogByAIQeQAbGtB//8DcUEBdEHDkMGAAGovAAA7AAAgBEF8aiEEIABC/8HXL1YhBiAFIQAgBg0ACwsCQCAFpyIGQeMATA0AIANBCWogBEF+aiIEaiAFpyIGIAZB//8DcUHkAG4iBkHkAGxrQf//A3FBAXRBw5DBgABqLwAAOwAACwJAAkAgBkEKSA0AIANBCWogBEF+aiIEaiAGQQF0QcOQwYAAai8AADsAAAwBCyADQQlqIARBf2oiBGogBkEwajoAAAsgAiABQaz0wIAAQQAgA0EJaiAEakEnIARrEMWGgIAAIQQgA0EwaiSAgICAACAEC3QBAn8jgICAgABBIGsiAiSAgICAACABQRxqKAIAIQMgASgCGCEBIAJBCGpBEGogAEEQaikCADcDACACQQhqQQhqIABBCGopAgA3AwAgAiAAKQIANwMIIAEgAyACQQhqEJ+GgIAAIQAgAkEgaiSAgICAACAAC7EGAQZ/AkACQCABRQ0AQStBgIDEACAAKAIAIgZBAXEiARshByABIAVqIQgMAQsgBUEBaiEIIAAoAgAhBkEtIQcLAkACQCAGQQRxDQBBACECDAELQQAhCQJAIANFDQAgAyEKIAIhAQNAIAkgAS0AAEHAAXFBgAFGaiEJIAFBAWohASAKQX9qIgoNAAsLIAggA2ogCWshCAtBASEBAkACQCAAKAIIQQFGDQAgACAHIAIgAxDGhoCAAA0BIAAoAhggBCAFIABBHGooAgAoAgwRj4CAgAAADwsCQCAAQQxqKAIAIgkgCEsNACAAIAcgAiADEMaGgIAADQEgACgCGCAEIAUgAEEcaigCACgCDBGPgICAAAAPCwJAAkAgBkEIcQ0AQQAhASAJIAhrIgkhCAJAAkACQEEBIAAtACAiCiAKQQNGGw4EAgEAAQILIAlBAXYhASAJQQFqQQF2IQgMAQtBACEIIAkhAQsgAUEBaiEBA0AgAUF/aiIBRQ0CIAAoAhggACgCBCAAKAIcKAIQEYCAgIAAAEUNAAtBAQ8LIAAoAgQhBiAAQTA2AgQgAC0AICELQQEhASAAQQE6ACAgACAHIAIgAxDGhoCAAA0BQQAhASAJIAhrIgohAwJAAkACQEEBIAAtACAiCSAJQQNGGw4EAgEAAQILIApBAXYhASAKQQFqQQF2IQMMAQtBACEDIAohAQsgAUEBaiEBAkADQCABQX9qIgFFDQEgACgCGCAAKAIEIAAoAhwoAhARgICAgAAARQ0AC0EBDwsgACgCBCEKQQEhASAAKAIYIAQgBSAAKAIcKAIMEY+AgIAAAA0BIANBAWohCSAAKAIcIQMgACgCGCECAkADQCAJQX9qIglFDQFBASEBIAIgCiADKAIQEYCAgIAAAEUNAAwDCwsgACALOgAgIAAgBjYCBEEADwsgACgCBCEKQQEhASAAIAcgAiADEMaGgIAADQAgACgCGCAEIAUgACgCHCgCDBGPgICAAAANACAIQQFqIQkgACgCHCEDIAAoAhghAANAAkAgCUF/aiIJDQBBAA8LQQEhASAAIAogAygCEBGAgICAAABFDQALCyABC1wBAX8CQAJAIAFBgIDEAEYNAEEBIQQgACgCGCABIABBHGooAgAoAhARgICAgAAADQELAkAgAg0AQQAPCyAAKAIYIAIgAyAAQRxqKAIAKAIMEY+AgIAAACEECyAEC/wEAQh/I4CAgIAAQRBrIgIkgICAgAACQAJAIAEoAgQiA0UNAEEBIQQgACgCGCABKAIAIAMgAEEcaigCACgCDBGPgICAAAANAQsCQCABQQxqKAIAIgQNAEEAIQQMAQsgASgCCCIFIARBDGxqIQYgAkEIakF/aiEHIAJBCGpBBGohCANAAkACQAJAAkACQAJAAkACQAJAIAUvAQAOAwABAgALAkACQCAFKAIEIgFBwQBPDQAgAQ0BDAkLA0AgACgCGEHcksGAAEHAACAAKAIcKAIMEY+AgIAAAA0IIAFBQGoiAUHAAEsNAAsLQcAAIQQgACgCHCEDIAAoAhghCQJAIAFBwABGDQAgAUHcksGAAGosAABBv39MDQMgASEECyAJQdySwYAAIAQgAygCDBGPgICAAABFDQcMBgsgBS8BAiEBIAhBADoAACACQQA2AghBASEEAkACQAJAIAUvAQAOAwIAAQILAkAgBS8BAiIEQegHSQ0AQQRBBSAEQZDOAEkbIQkMBgtBASEJIARBCkkNBUECQQMgBEHkAEkbIQkMBQtBAiEECyAFIARBAnRqKAIAIglBBk8NAiAJDQNBACEJDAQLIAAoAhggBSgCBCAFKAIIIAAoAhwoAgwRj4CAgAAARQ0FDAQLQdySwYAAQcAAQQAgARCShoCAAAALIAlBBRCPhoCAAAALIAkhBANAIAcgBGogASABQf//A3FBCm4iA0EKbGtBMHI6AAAgAyEBIARBf2oiBA0ACwsgACgCGCACQQhqIAkgACgCHCgCDBGPgICAAABFDQELQQEhBAwCCyAGIAVBDGoiBUcNAAtBACEECyACQRBqJICAgIAAIAQLHQAgACgCGCABIAIgAEEcaigCACgCDBGPgICAAAALdAECfyOAgICAAEEgayICJICAgIAAIABBHGooAgAhAyAAKAIYIQAgAkEIakEQaiABQRBqKQIANwMAIAJBCGpBCGogAUEIaikCADcDACACIAEpAgA3AwggACADIAJBCGoQn4aAgAAhASACQSBqJICAgIAAIAELDQAgAC0AAEEQcUEEdgsNACAALQAAQSBxQQV2CzQAIAEoAhggAiADIAFBHGooAgAoAgwRj4CAgAAAIQIgAEEAOgAFIAAgAjoABCAAIAE2AgALOAAgACABKAIYIAIgAyABQRxqKAIAKAIMEY+AgIAAADoACCAAIAE2AgAgACADRToACSAAQQA2AgQLOgEBfyABKAIYQa+QwYAAQQEgAUEcaigCACgCDBGPgICAAAAhAiAAQQA6AAUgACACOgAEIAAgATYCAAstAAJAIAAtAAANACABQaCTwYAAQQUQkYaAgAAPCyABQZyTwYAAQQQQkYaAgAALvgkBDX8jgICAgABBMGsiAySAgICAAEEBIQQCQAJAIAIoAhhBIiACQRxqKAIAKAIQEYCAgIAAAA0AAkACQCABDQBBACEFDAELIAAgAWohBiAAIQdBACEFQQAhCAJAA0AgByEJIAdBAWohCgJAAkACQCAHLAAAIgtBf0oNAAJAAkAgCiAGRw0AQQAhDCAGIQcMAQsgBy0AAUE/cSEMIAdBAmoiCiEHCyALQR9xIQ0CQCALQf8BcSILQd8BSw0AIAwgDUEGdHIhCwwCCwJAAkAgByAGRw0AQQAhDiAGIQ8MAQsgBy0AAEE/cSEOIAdBAWoiCiEPCyAOIAxBBnRyIQwCQCALQfABTw0AIAwgDUEMdHIhCwwCCwJAAkAgDyAGRw0AQQAhCyAKIQcMAQsgD0EBaiEHIA8tAABBP3EhCwsgDEEGdCANQRJ0QYCA8ABxciALciILQYCAxABHDQIMBAsgC0H/AXEhCwsgCiEHCyADIAtBARClhoCAAAJAAkACQAJAIAMoAgAiCg4EAQIBAAELIAMoAgggAy0ADGpBAUYNAQsgAyABNgIUIAMgADYCECADIAU2AhggAyAINgIcAkACQCAIIAVJDQACQCAFRQ0AIAUgAUYNACAFIAFPDQEgACAFaiwAAEG/f0wNAQsCQCAIRQ0AIAggAUYNACAIIAFPDQEgACAIaiwAAEG/f0wNAQsgAigCGCAAIAVqIAggBWsgAigCHCgCDBGPgICAAABFDQEMAwsgAyADQRxqNgIoIAMgA0EYajYCJCADIANBEGo2AiAgA0EgahDRhoCAAAALIAMtAAwhDSADKAIIIQ8CQAJAIAMoAgQiDkGAgMQARw0AA0AgCiEFQQEhCkHcACEMAkACQCAFDgQEBAEABAsgDUH/AXEhBUEDIQpBBCENAkACQAJAAkAgBQ4GBwMCAQAEBwtBAyENQfUAIQxBAyEKDAMLQQIhDUH7ACEMDAILQQJBASAPGyENQYCAxAAgD0ECdEEccXZBD3FBMHIhDCAPQX9qQQAgDxshDwwBC0EAIQ1B/QAhDAsgAigCGCAMIAIoAhwoAhARgICAgAAADQQMAAsLA0AgCiEMQQEhCkHcACEFAkACQAJAAkAgDA4EBQEDAAULIA1B/wFxIQxBAyEKQQQhDQJAAkACQCAMDgYHAgEABAUHC0ECIQ1B+wAhBQwECyAOIA9BAnRBHHF2QQ9xIgVBMHIgBUHXAGogBUEKSRshBUECQQEgDxshDSAPQX9qQQAgDxshDwwDC0EAIQ1B/QAhBQwCC0EAIQogDiEFDAELQQMhDUH1ACEFQQMhCgsgAigCGCAFIAIoAhwoAhARgICAgAAADQMMAAsLQQEhBQJAIAtBgAFJDQBBAiEFIAtBgBBJDQBBA0EEIAtBgIAESRshBQsgBSAIaiEFCyAIIAlrIAdqIQggBiAHRw0BDAILC0EBIQQMAgsgBUUNACAFIAFGDQAgBSABTw0CIAAgBWosAABBv39MDQILIAIoAhggACAFaiABIAVrIAIoAhwoAgwRj4CAgAAADQAgAigCGEEiIAIoAhwoAhARgICAgAAAIQQLIANBMGokgICAgAAgBA8LIAAgASAFIAEQkoaAgAAACyoBAX8gACgCACIBKAIAIAEoAgQgACgCBCgCACAAKAIIKAIAEJKGgIAAAAsOACACIAAgARCRhoCAAAudBAEHfyOAgICAAEEQayICJICAgIAAQQEhAwJAIAEoAhhBJyABQRxqKAIAKAIQEYCAgIAAAA0AIAIgACgCAEEBEKWGgIAAIAJBDGotAAAhBCACQQhqKAIAIQUgAigCACEAAkACQCACKAIEIgZBgIDEAEYNAANAIAAhB0EBIQNB3AAhCEEBIQACQAJAAkACQCAHDgQGAQMABgsgBEH/AXEhB0EEIQRBAyEAAkACQAJAIAcOBggCAQAEBQgLQQIhBEH7ACEIDAQLIAYgBUECdEEccXZBD3EiCEEwciAIQdcAaiAIQQpJGyEIQQJBASAFGyEEIAVBf2pBACAFGyEFDAMLQQAhBEH9ACEIDAILQQAhACAGIQgMAQtBAyEAQfUAIQhBAyEECyABKAIYIAggASgCHCgCEBGAgICAAABFDQAMAwsLA0AgACEIQQEhA0HcACEHQQEhAAJAAkAgCA4EAwMBAAMLIARB/wFxIQhBBCEEQQMhAAJAAkACQAJAIAgOBgYDAgEABAYLQQMhAEH1ACEHQQMhBAwDC0ECIQRB+wAhBwwCC0ECQQEgBRshBEGAgMQAIAVBAnRBHHF2QQ9xQTByIQcgBUF/akEAIAUbIQUMAQtBACEEQf0AIQcLIAEoAhggByABKAIcKAIQEYCAgIAAAEUNAAwCCwsgASgCGEEnIAEoAhwoAhARgICAgAAAIQMLIAJBEGokgICAgAAgAwv0AgECfyOAgICAAEEQayICJICAgIAAAkACQAJAIAFBCGooAgBBAUYNACABQRBqKAIAQQFHDQELIAAoAgAhACACQQA2AgwCQAJAAkAgAEGAAUkNACAAQYAQSQ0BIAJBDGohAyAAQYCABE8NAiACIABBP3FBgAFyOgAOIAIgAEEGdkE/cUGAAXI6AA0gAiAAQQx2QQ9xQeABcjoADCABIANBAxCRhoCAACEBDAQLIAIgADoADCABIAJBDGpBARCRhoCAACEBDAMLIAIgAEE/cUGAAXI6AA0gAiAAQQZ2QR9xQcABcjoADCABIAJBDGpBAhCRhoCAACEBDAILIAIgAEE/cUGAAXI6AA8gAiAAQRJ2QfABcjoADCACIABBBnZBP3FBgAFyOgAOIAIgAEEMdkE/cUGAAXI6AA0gASADQQQQkYaAgAAhAQwBCyABKAIYIAAoAgAgAUEcaigCACgCEBGAgICAAAAhAQsgAkEQaiSAgICAACABC+UKAwl/AX4BfwJAAkACQAJAAkACQAJAAkAgBEEBSw0AQQAhBSAEIQYgBCEHQQAhCCAEDgICAQILQQEhCUEAIQVBASEKQQAhC0EBIQYDQCAKIQwCQAJAIAsgBWoiCiAETw0AAkAgAyAJai0AAEH/AXEiCSADIApqLQAAIgpJDQACQCAJIApGDQBBASEGIAxBAWohCkEAIQsgDCEFDAMLQQAgC0EBaiIKIAogBkYiCRshCyAKQQAgCRsgDGohCgwCCyAMIAtqQQFqIgogBWshBkEAIQsMAQtB6JTBgAAgCiAEEIyGgIAAAAsgCiALaiIJIARJDQALQQEhCUEAIQhBASEKQQAhC0EBIQcDQCAKIQwCQAJAIAsgCGoiCiAETw0AAkAgAyAJai0AAEH/AXEiCSADIApqLQAAIgpLDQACQCAJIApGDQBBASEHIAxBAWohCkEAIQsgDCEIDAMLQQAgC0EBaiIKIAogB0YiCRshCyAKQQAgCRsgDGohCgwCCyAMIAtqQQFqIgogCGshB0EAIQsMAQtB6JTBgAAgCiAEEIyGgIAAAAsgCiALaiIJIARJDQALCwJAAkACQCAFIAggBSAISyILGyINIARLDQAgBiAHIAsbIgogDWoiCyAKSQ0BIAsgBEsNAiAKRQ0EIAMgAyAKaiANEPSGgIAARQ0EIA0gBCANayILIA0gC0sbIQxCACEOIAQhCiADIQsDQEIBIAsxAABCP4OGIA6EIQ4gC0EBaiELIApBf2oiCg0ACyAMQQFqIQpBfyEMIA0hCUF/IQsMBQsgDSAEEI+GgIAAAAsgCiALEJCGgIAAAAsgCyAEEI+GgIAAAAsgACADNgI4IAAgATYCMCAAQgA3AwAgAEE8akEANgIAIABBNGogAjYCACAAQQxqQYECOwEAIABBCGogAjYCAA8LQQEhBUEAIQtBASEJQQAhBgJAA0AgCSIMIAtqIgcgBE8NASAEIAtrIAxBf3NqIgkgBE8NBSALQX9zIARqIAZrIgggBE8NBAJAAkACQCADIAlqLQAAQf8BcSIJIAMgCGotAAAiCEkNACAJIAhGDQEgDEEBaiEJQQAhC0EBIQUgDCEGDAILIAdBAWoiCSAGayEFQQAhCwwBC0EAIAtBAWoiCSAJIAVGIggbIQsgCUEAIAgbIAxqIQkLIAUgCkcNAAsLQQEhBUEAIQtBASEJQQAhBwJAAkACQAJAAkADQCAJIgwgC2oiDyAETw0BIAQgC2sgDEF/c2oiCSAETw0CIAtBf3MgBGogB2siCCAETw0DAkACQAJAIAMgCWotAABB/wFxIgkgAyAIai0AACIISw0AIAkgCEYNASAMQQFqIQlBACELQQEhBSAMIQcMAgsgD0EBaiIJIAdrIQVBACELDAELQQAgC0EBaiIJIAkgBUYiCBshCyAJQQAgCBsgDGohCQsgBSAKRw0ACwsgCiAESw0FIAQgBiAHIAYgB0sbayEJQgAhDiAKDQJBACEKQQAhDAwDC0H4lMGAACAJIAQQjIaAgAAAC0GIlcGAACAIIAQQjIaAgAAAC0EAIQxBACELA0BCASADIAtqMQAAQj+DhiAOhCEOIAogC0EBaiILRw0ACwsgBCELCyAAIAM2AjggACABNgIwIABBATYCACAAQTxqIAQ2AgAgAEE0aiACNgIAIABBKGogCzYCACAAQSRqIAw2AgAgAEEgaiACNgIAIABBHGpBADYCACAAQRhqIAo2AgAgAEEUaiAJNgIAIABBEGogDTYCACAAQQhqIA43AgAPCyAKIAQQj4aAgAAAC0GIlcGAACAIIAQQjIaAgAAAC0H4lMGAACAJIAQQjIaAgAAAC6YBAQN/I4CAgIAAQYABayICJICAgIAAIAAtAAAhA0EAIQADQCACIABqQf8AaiADQQ9xIgRBMHIgBEHXAGogBEEKSRs6AAAgAEF/aiEAIANBBHZBD3EiAw0ACwJAIABBgAFqIgNBgQFJDQAgA0GAARCQhoCAAAALIAFBAUHBkMGAAEECIAIgAGpBgAFqQQAgAGsQxYaAgAAhACACQYABaiSAgICAACAAC3gCAX8BfiOAgICAAEEQayIDJICAgIAAIANBCGogASACENiGgIAAAkACQCADKQMIIgRCgICAgPAfg0KAgICAIFENACAAIAQ3AgRBASEBDAELIAAgATYCBCAAQQhqIAI2AgBBACEBCyAAIAE2AgAgA0EQaiSAgICAAAulBwEGfwJAAkACQCACRQ0AQQAgAWtBACABQQNxGyEDIAJBeWpBACACQQdLGyEEQQAhBQNAAkACQAJAAkACQCABIAVqLQAAIgZBGHRBGHUiB0F/Sg0AAkACQAJAAkAgBkGulcGAAGotAABBfmoiCEECSw0AIAgOAwECAwELIABBgQI7AQQgACAFNgIADwsCQCAFQQFqIgYgAkkNACAAQQA6AAQgACAFNgIADwsgASAGai0AAEHAAXFBgAFGDQMgAEGBAjsBBCAAIAU2AgAPCwJAIAVBAWoiCCACSQ0AIABBADoABCAAIAU2AgAPCyABIAhqLQAAIQgCQAJAIAZBoH5qIgZBDUsNAAJAAkAgBg4OAAICAgICAgICAgICAgEACyAIQeABcUGgAUcNDAwCCyAIQRh0QRh1QX9KDQsgCEH/AXFBoAFJDQEMCwsCQCAHQR9qQf8BcUELSw0AIAhBGHRBGHVBf0oNCyAIQf8BcUHAAU8NCwwBCyAIQf8BcUG/AUsNCiAHQf4BcUHuAUcNCiAIQRh0QRh1QX9KDQoLAkAgBUECaiIGIAJJDQAgAEEAOgAEIAAgBTYCAA8LIAEgBmotAABBwAFxQYABRg0CIABBgQQ7AQQgACAFNgIADwsCQCAFQQFqIgggAkkNACAAQQA6AAQgACAFNgIADwsgASAIai0AACEIAkACQCAGQZB+aiIGQQRLDQACQAJAIAYOBQACAgIBAAsgCEHwAGpB/wFxQTBPDQoMAgsgCEEYdEEYdUF/Sg0JIAhB/wFxQZABSQ0BDAkLIAhB/wFxQb8BSw0IIAdBD2pB/wFxQQJLDQggCEEYdEEYdUF/Sg0ICwJAIAVBAmoiBiACSQ0AIABBADoABCAAIAU2AgAPCyABIAZqLQAAQcABcUGAAUcNAgJAIAVBA2oiBiACSQ0AIABBADoABCAAIAU2AgAPCyABIAZqLQAAQcABcUGAAUYNASAAQYEGOwEEIAAgBTYCAA8LIAMgBWtBA3ENAgJAIAUgBE8NAANAIAEgBWoiBkEEaigCACAGKAIAckGAgYKEeHENASAFQQhqIgUgBEkNAAsLIAUgAk8NAwNAIAEgBWosAABBAEgNBCACIAVBAWoiBUcNAAwGCwsgBkEBaiEFDAILIABBgQQ7AQQgACAFNgIADwsgBUEBaiEFCyAFIAJJDQALCyAAQQI6AAQPCyAAQYECOwEEIAAgBTYCAA8LIABBgQI7AQQgACAFNgIACxEAIAAxAABBASABEMOGgIAACxEAIAApAwBBASABEMOGgIAAC6MBAQN/I4CAgIAAQYABayICJICAgIAAIAAoAgAhA0EAIQADQCACIABqQf8AaiADQQ9xIgRBMHIgBEHXAGogBEEKSRs6AAAgAEF/aiEAIANBBHYiAw0ACwJAIABBgAFqIgNBgQFJDQAgA0GAARCQhoCAAAALIAFBAUHBkMGAAEECIAIgAGpBgAFqQQAgAGsQxYaAgAAhACACQYABaiSAgICAACAACxAAIAAgASACIAMQnYaAgAAL2wEDAn8BfgF/AkAgAg0AIABBADoAASAAQQE6AAAPCwJAAkAgAS0AAEErRw0AIAJBf2oiAkUNASABQQFqIQELQQAhAwJAAkACQANAIAJFDQMgAS0AAEFQaiIEQQlLDQEgA61CCn4iBUIgiKcNAiABQQFqIQEgAkF/aiECIAWnIgYgBGoiAyAGTw0ACyAAQQI6AAEgAEEBOgAADwsgAEEBOgABIABBAToAAA8LIABBAjoAASAAQQE6AAAPCyAAQQRqIAM2AgAgAEEAOgAADwsgAEEAOgABIABBAToAAAubAgECfyOAgICAAEEQayICJICAgIAAIAIgASgCGEGMpsGAAEERIAFBHGooAgAoAgwRj4CAgAAAOgAIIAIgATYCACACQQA6AAkgAkEANgIEIAIgADYCDCACIAJBDGpB/KXBgAAQtYaAgAAaIAItAAghAQJAIAIoAgQiA0UNACABQf8BcSEAQQEhAQJAIAANAAJAIANBAUcNACACLQAJQf8BcUUNACACKAIAIgAtAABBBHENAEEBIQEgACgCGEGskMGAAEEBIABBHGooAgAoAgwRj4CAgAAADQELIAIoAgAiASgCGEGtkMGAAEEBIAFBHGooAgAoAgwRj4CAgAAAIQELIAIgAToACAsgAkEQaiSAgICAACABQf8BcUEARwtKAgF/AXwgAS0AAEEBdEECcSECIAArAwAhAwJAIAEoAhBBAUYNACABIAMgAkEAELyGgIAADwsgASADIAIgAUEUaigCABC6hoCAAAulAQEDfyOAgICAAEGAAWsiAiSAgICAACAALQAAIQNBACEAA0AgAiAAakH/AGogA0EPcSIEQTByIARBN2ogBEEKSRs6AAAgAEF/aiEAIANBBHZBD3EiAw0ACwJAIABBgAFqIgNBgQFJDQAgA0GAARCQhoCAAAALIAFBAUHBkMGAAEECIAIgAGpBgAFqQQAgAGsQxYaAgAAhACACQYABaiSAgICAACAAC6IBAQN/I4CAgIAAQYABayICJICAgIAAIAAoAgAhA0EAIQADQCACIABqQf8AaiADQQ9xIgRBMHIgBEE3aiAEQQpJGzoAACAAQX9qIQAgA0EEdiIDDQALAkAgAEGAAWoiA0GBAUkNACADQYABEJCGgIAAAAsgAUEBQcGQwYAAQQIgAiAAakGAAWpBACAAaxDFhoCAACEAIAJBgAFqJICAgIAAIAALqwEDAX8BfgF/I4CAgIAAQYABayICJICAgIAAIAApAwAhA0EAIQADQCACIABqQf8AaiADp0EPcSIEQTByIARB1wBqIARBCkkbOgAAIABBf2ohACADQgSIIgNCAFINAAsCQCAAQYABaiIEQYEBSQ0AIARBgAEQkIaAgAAACyABQQFBwZDBgABBAiACIABqQYABakEAIABrEMWGgIAAIQAgAkGAAWokgICAgAAgAAuqAQMBfwF+AX8jgICAgABBgAFrIgIkgICAgAAgACkDACEDQQAhAANAIAIgAGpB/wBqIAOnQQ9xIgRBMHIgBEE3aiAEQQpJGzoAACAAQX9qIQAgA0IEiCIDQgBSDQALAkAgAEGAAWoiBEGBAUkNACAEQYABEJCGgIAAAAsgAUEBQcGQwYAAQQIgAiAAakGAAWpBACAAaxDFhoCAACEAIAJBgAFqJICAgIAAIAALKQEBfiAAKAIAIgCsIgIgAkI/hyICfCAChSAAQX9zQR92IAEQw4aAgAALIwECfiAAKQMAIgIgAkI/hyIDfCADhSACQn9VIAEQw4aAgAALwgMDBH8CfgJ/I4CAgIAAQdAAayIEJICAgIAAAkACQCAAQpDOAFRBACABUBtFDQAgAKchBUEnIQYMAQtBJyEGIARBIGohBwNAIARBGGogACABQpDOAEIAEPiGgIAAIARBCGogBCkDGCIIIAcpAwAiCUKQzgBCABD3hoCAACAEQSlqIAZqIgVBfGogACAEKQMIfaciCkH//wNxQeQAbiILQQF0QcOQwYAAai8AADsAACAFQX5qIAogC0HkAGxrQf//A3FBAXRBw5DBgABqLwAAOwAAIABC/8HXL1YhBSABQgBSIQogAVAhCyAGQXxqIQYgCCEAIAkhASAFIAogCxsNAAsgCKchBQsCQAJAIAVB4wBKDQAgBSEKDAELIARBKWogBkF+aiIGaiAFIAVB//8DcUHkAG4iCkHkAGxrQf//A3FBAXRBw5DBgABqLwAAOwAACwJAAkAgCkEKSA0AIARBKWogBkF+aiIGaiAKQQF0QcOQwYAAai8AADsAAAwBCyAEQSlqIAZBf2oiBmogCkEwajoAAAsgAyACQaz0wIAAQQAgBEEpaiAGakEnIAZrEMWGgIAAIQYgBEHQAGokgICAgAAgBgsZACAAKQMAIABBCGopAwBBASABEOaGgIAACyEAIAEoAhhBpabBgABBBSABQRxqKAIAKAIMEY+AgIAAAAsPACAAKAIAIAEQoYaAgAALEgAgAUGlk8GAAEECEJGGgIAAC9oCAQJ/I4CAgIAAQRBrIgIkgICAgAACQAJAIAAoAgAiAC0AAEEBRg0AIAEoAhhBoabBgABBBCABQRxqKAIAKAIMEY+AgIAAACEBDAELIAIgASgCGEGdpsGAAEEEIAFBHGooAgAoAgwRj4CAgAAAOgAIIAIgATYCACACQQA6AAkgAkEANgIEIAIgAEEBajYCDCACIAJBDGpBsJDBgAAQtYaAgAAaIAItAAghAQJAIAIoAgQiA0UNACABQf8BcSEAQQEhAQJAIAANAAJAIANBAUcNACACLQAJQf8BcUUNACACKAIAIgAtAABBBHENAEEBIQEgACgCGEGskMGAAEEBIABBHGooAgAoAgwRj4CAgAAADQELIAIoAgAiASgCGEGtkMGAAEEBIAFBHGooAgAoAgwRj4CAgAAAIQELIAIgAToACAsgAUH/AXFBAEchAQsgAkEQaiSAgICAACABC+ICAQN/I4CAgIAAQYABayICJICAgIAAIAAoAgAhAAJAAkACQAJAAkAgASgCACIDQRBxDQAgAC0AACEEIANBIHENASAErUL/AYNBASABEMOGgIAAIQAMAgsgAC0AACEEQQAhAANAIAIgAGpB/wBqIARBD3EiA0EwciADQdcAaiADQQpJGzoAACAAQX9qIQAgBEEEdkEPcSIEDQALIABBgAFqIgRBgQFPDQIgAUEBQcGQwYAAQQIgAiAAakGAAWpBACAAaxDFhoCAACEADAELQQAhAANAIAIgAGpB/wBqIARBD3EiA0EwciADQTdqIANBCkkbOgAAIABBf2ohACAEQQR2QQ9xIgQNAAsgAEGAAWoiBEGBAU8NAiABQQFBwZDBgABBAiACIABqQYABakEAIABrEMWGgIAAIQALIAJBgAFqJICAgIAAIAAPCyAEQYABEJCGgIAAAAsgBEGAARCQhoCAAAALpgIBAn8jgICAgABBEGsiAiSAgICAACABKAIYQaqmwYAAQQkgAUEcaigCACgCDBGPgICAAAAhAyACQQA6AAUgAiADOgAEIAIgATYCACACIAA2AgwgAkGzpsGAAEELIAJBDGpB7KXBgAAQp4aAgAAaIAIgAEEEajYCDCACQb6mwYAAQQkgAkEMakHIpsGAABCnhoCAABogAi0ABCEBAkAgAi0ABUUNACABQf8BcSEAQQEhAQJAIAANACACKAIAIgFBHGooAgAoAgwhACABKAIYIQMCQCABLQAAQQRxDQAgA0GnkMGAAEECIAARj4CAgAAAIQEMAQsgA0GmkMGAAEEBIAARj4CAgAAAIQELIAIgAToABAsgAkEQaiSAgICAACABQf8BcUEARwuiAQECfyAAQQp2IQECQAJAAkACQCAAQYDoB0kNAEEhIQIgAUGAB0YNAUEADwsgAUHYpsGAAGotAAAiAkEhSw0BCyACQQR0IABBBnZBD3FyQdWnwYAAai0AACIBQbMBSw0BIAFBA3RB+KvBgABqKQMAQgEgAEE/ca2Gg0IAUg8LQcykwYAAIAJBIhCMhoCAAAALQdykwYAAIAFBtAEQjIaAgAAAC6ABAQJ/IABBCnYhAQJAAkACQAJAIABBgNgHSQ0AQQYhAiABQfwARg0BQQAPCyABQZi3wYAAai0AACICQRJLDQELIAJBBHQgAEEGdkEPcXJBk7jBgABqLQAAIgFBPksNASABQQN0Qci6wYAAaikDAEIBIABBP3GthoNCAFIPC0HMpMGAACACQRMQjIaAgAAAC0HcpMGAACABQT8QjIaAgAAAC/YCAQF/AkBBAEG3BSABQew8SRsiAiACQdsCaiICIAJBBHRBiMzBgABqKAIAIAFLGyICIAJBrgFqIgIgAkEEdEGIzMGAAGooAgAgAUsbIgIgAkHXAGoiAiACQQR0QYjMwYAAaigCACABSxsiAiACQStqIgIgAkEEdEGIzMGAAGooAgAgAUsbIgIgAkEWaiICIAJBBHRBiMzBgABqKAIAIAFLGyICIAJBC2oiAiACQQR0QYjMwYAAaigCACABSxsiAiACQQVqIgIgAkEEdEGIzMGAAGooAgAgAUsbIgIgAkEDaiICIAJBBHRBiMzBgABqKAIAIAFLGyICIAJBAWoiAiACQQR0QYjMwYAAaigCACABSxsiAiACQQFqIgIgAkEEdEGIzMGAAGooAgAgAUsbQQR0IgJBiMzBgABqKAIAIAFGDQAgAEIANwIEIAAgATYCAA8LIABBCGogAkGUzMGAAGooAgA2AgAgACACQYzMwYAAaikCADcCAAuAAgECfyOAgICAAEEQayICJICAgIAAIAEoAhhB6PnCgABBCSABQRxqKAIAKAIMEY+AgIAAACEDIAJBADoABSACIAM6AAQgAiABNgIAIAIgADYCDCACQfH5woAAQQcgAkEMakH8pcGAABCnhoCAABogAi0ABCEBAkAgAi0ABUUNACABQf8BcSEAQQEhAQJAIAANACACKAIAIgFBHGooAgAoAgwhACABKAIYIQMCQCABLQAAQQRxDQAgA0GnkMGAAEECIAARj4CAgAAAIQEMAQsgA0GmkMGAAEEBIAARj4CAgAAAIQELIAIgAToABAsgAkEQaiSAgICAACABQf8BcUEARwssAQF/AkAgAkUNACAAIQMDQCADIAE6AAAgA0EBaiEDIAJBf2oiAg0ACwsgAAs2AQF/AkAgAkUNACAAIQMDQCADIAEtAAA6AAAgA0EBaiEDIAFBAWohASACQX9qIgINAAsLIAALSgEDf0EAIQMCQCACRQ0AAkADQCAALQAAIgQgAS0AACIFRw0BIABBAWohACABQQFqIQEgAkF/aiICRQ0CDAALCyAEIAVrIQMLIAMLVwEBfgJAAkAgA0HAAHENACADRQ0BIAFBACADa0E/ca2IIAIgA0E/ca0iBIaEIQIgASAEhiEBDAELIAEgA0E/ca2GIQJCACEBCyAAIAE3AwAgACACNwMIC1cBAX4CQAJAIANBwABxDQAgA0UNASABIANBP3GtIgSIIAJBACADa0E/ca2GhCEBIAIgBIghAgwBCyACIANBP3GtiCEBQgAhAgsgACABNwMAIAAgAjcDCAt1AQJ+IAAgA0IgiCIFIAFCIIgiBn4gAyACfnwgBCABfnwgA0L/////D4MiAyABQv////8PgyIBfiIEQiCIIAMgBn58IgNCIIh8IANC/////w+DIAUgAX58IgNCIIh8NwMIIAAgA0IghiAEQv////8Pg4Q3AwALTQEBfyOAgICAAEEQayIFJICAgIAAIAUgASACIAMgBEEAEPmGgIAAIAUpAwAhASAAIAVBCGopAwA3AwggACABNwMAIAVBEGokgICAgAAL9wUCA38GfiOAgICAAEEwayIGJICAgIAAAkACQAJAAkACQAJAAkACQAJAAkAgAlANACADUA0BIARQDQIgBHmnIAJ5p2siB0E/Sw0DQf8AIAdrIQggB0EBaiEHDAgLAkAgBFANACAFDQQMBgsCQAJAIAVFDQAgA0IAUQ0GIAVCADcDCCAFIAEgA4I3AwAMAQsgA0IAUQ0FCyABIAOAIQEMBgsgBFANAwJAAkACQCABUA0AIAR7QgFRDQEgBHmnIAJ5p2siB0E+Sw0CQf8AIAdrIQggB0EBaiEHDAkLAkAgBUUNACAFQgA3AwAgBSACIASCNwMICyACIASAIQEMBwsCQCAFRQ0AIAUgATcDACAFIARCf3wgAoM3AwgLIAIgBHpCP4OIIQEMBgsgBUUNBAwCCwJAIAN7QgFRDQBBv38gA3mnIAJ5p2siB2shCCAHQcEAaiEHDAYLAkAgBUUNACAFQgA3AwggBSADQn98IAGDNwMACyADQgFRDQYgBkEgaiABIAIgA3qnEPaGgIAAIAZBKGopAwAhAiAGKQMgIQEMBgsgBUUNAgsgBSABNwMAIAUgAjcDCEIAIQEMAgsAAAtCACEBC0IAIQIMAQsgBiABIAIgCEH/AHEQ9YaAgAAgBkEQaiABIAIgB0H/AHEQ9oaAgAAgBkEIaikDACECIAZBEGpBCGopAwAhCSAGKQMAIQEgBikDECEKAkACQCAHDQBCACELQgAhDAwBC0IAIQxCACENA0AgCUIBhiAKQj+IhCILIAtCf4UgBHwgCkIBhiACQj+IhCIKQn+FIgsgA3wgC1StfEI/hyILIASDfSAKIAsgA4MiDlStfSEJIAogDn0hCkIAIAJCAYYgAUI/iISEIQIgDSABQgGGhCEBIAtCAYMiCyENIAdBf2oiBw0ACwsCQCAFRQ0AIAUgCjcDACAFIAk3AwgLIAwgAkIBhiABQj+IhIQhAiALIAFCAYaEIQELIAAgATcDACAAIAI3AwggBkEwaiSAgICAAAsLw/oCAgBBgIDAAAv4+QJhbHNlcnVldWxsaW50ZXJuYWwgZXJyb3I6IGVudGVyZWQgdW5yZWFjaGFibGUgY29kZTw6OnN0ZDo6bWFjcm9zOjpwYW5pYyBtYWNyb3M+ADIAEAAdAAAAAgAAAAQAAAAvVXNlcnMvY3lwcmVzcy8uY2FyZ28vcmVnaXN0cnkvc3JjL2dpdGh1Yi5jb20tMWVjYzYyOTlkYjllYzgyMy9zZXJkZV9qc29uLTEuMC40OC9zcmMvZGUucnMAAAAAAABhdHRlbXB0IHRvIHN1YnRyYWN0IHdpdGggb3ZlcmZsb3cAAABgABAAWgAAAPYBAAAbAAAAAAAAAAAAAAAAAAAAYXR0ZW1wdCB0byBtdWx0aXBseSB3aXRoIG92ZXJmbG93AAAAAAAAAAAAAAAAAAAAYXR0ZW1wdCB0byBhZGQgd2l0aCBvdmVyZmxvd2AAEABaAAAA9wEAAA0AAABgABAAWgAAAKMBAAAjAAAAYAAQAFoAAAAyAgAAEwAAAGAAEABaAAAAuwEAABUAAAB9InRydWVmYWxzZW51bGx7LFx0XHJcblxmXGJcXFwiOqwBEAAAAAAAYSBEaXNwbGF5IGltcGxlbWVudGF0aW9uIHJldHVybmVkIGFuIGVycm9yIHVuZXhwZWN0ZWRseS9ydXN0Yy9jZDFlZjM5MGU3MzFlZDc3YjkwYjExYjFmNzdlMmM1Y2E2NDFiMjYxL3NyYy9saWJhbGxvYy9zdHJpbmcucnMAAADrARAARgAAAHwIAAAJAAAAYWxyZWFkeSBib3Jyb3dlZC9ydXN0Yy9jZDFlZjM5MGU3MzFlZDc3YjkwYjExYjFmNzdlMmM1Y2E2NDFiMjYxL3NyYy9saWJjb3JlL2NlbGwucnMAVAIQAEMAAABuAwAACQAAAAoAAAAIAAAABAAAAAsAAAAMAAAAAAAAAAEAAAANAAAADgAAAAAAAAABAAAADwAAABAAAAAEAAAABAAAABEAAABjYWxsZWQgYFJlc3VsdDo6dW53cmFwKClgIG9uIGFuIGBFcnJgIHZhbHVlABIAAAAAAAAAAQAAABMAAABVbmV4cGVjdGVkIGxlbmd0aCBvZiBpbnB1dC9Vc2Vycy9jeXByZXNzLy5jYXJnby9yZWdpc3RyeS9zcmMvZ2l0aHViLmNvbS0xZWNjNjI5OWRiOWVjODIzL2JvcnNoLTAuNi4xL3NyYy9kZS9tb2QucnMAAD4DEABYAAAASAAAADAAAAAvcnVzdGMvY2QxZWYzOTBlNzMxZWQ3N2I5MGIxMWIxZjc3ZTJjNWNhNjQxYjI2MS9zcmMvbGliY29yZS9vcHMvYXJpdGgucnOoAxAASAAAALsCAAAzAAAAAQAAAAAAAABOb3QgYWxsIGJ5dGVzIHJlYWRtaXNzaW5nIGZpZWxkIGBgAAAaBBAADwAAACkEEAABAAAAaW52YWxpZCBsZW5ndGggLCBleHBlY3RlZCAAADwEEAAPAAAASwQQAAsAAABkdXBsaWNhdGUgZmllbGQgYAAAAGgEEAARAAAAKQQQAAEAAAAvVXNlcnMvY3lwcmVzcy8uY2FyZ28vcmVnaXN0cnkvc3JjL2dpdGh1Yi5jb20tMWVjYzYyOTlkYjllYzgyMy9zZXJkZV9qc29uLTEuMC40OC9zcmMvcmVhZC5yc4wEEABcAAAA8wEAAAkAAAAvVXNlcnMvY3lwcmVzcy8uY2FyZ28vcmVnaXN0cnkvc3JjL2dpdGh1Yi5jb20tMWVjYzYyOTlkYjllYzgyMy9uZWFyLXNkay0wLjkuMi9zcmMvY29sbGVjdGlvbnMvbWFwLnJz+AQQAGQAAACAAAAAQAAAAFRoZSBjb2xsZWN0aW9uIGlzIGFuIGluY29uc2lzdGVudCBzdGF0ZS4gRGlkIHByZXZpb3VzIHNtYXJ0IGNvbnRyYWN0IGV4ZWN1dGlvbiB0ZXJtaW5hdGUgdW5leHBlY3RlZGx5P0Nhbm5vdCBzZXJpYWxpemUga2V5IHdpdGggQm9yc2hDYW5ub3Qgc2VyaWFsaXplIHZhbHVlIHdpdGggQm9yc2hDYW5ub3QgZGVzZXJpYWxpemUgdmFsdWUgd2l0aCBCb3JzaAAAAPgEEABkAAAARwAAACoAAAD4BBAAZAAAACkAAAA3AAAA+AQQAGQAAAAtAAAAMwAAAPgEEABkAAAAMQAAADUAAABJbmRleCBvdXQgb2YgYm91bmRzL1VzZXJzL2N5cHJlc3MvLmNhcmdvL3JlZ2lzdHJ5L3NyYy9naXRodWIuY29tLTFlY2M2Mjk5ZGI5ZWM4MjMvbmVhci1zZGstMC45LjIvc3JjL2NvbGxlY3Rpb25zL3ZlY3Rvci5yc2NoZWNrZWQgYGluZGV4IDwgbGVuYCBhYm92ZSwgc28gYGxlbiA+IDBgAIsGEABnAAAATAAAACIAAACLBhAAZwAAACkAAAAxAAAAiwYQAGcAAABnAAAADQAAAIsGEABnAAAAWwAAAAkAAABTVEFURUNhbm5vdCBkZXNlcmlhbGl6ZSB0aGUgY29udHJhY3Qgc3RhdGUuL1VzZXJzL2N5cHJlc3MvLmNhcmdvL3JlZ2lzdHJ5L3NyYy9naXRodWIuY29tLTFlY2M2Mjk5ZGI5ZWM4MjMvbmVhci1zZGstMC45LjIvc3JjL2Vudmlyb25tZW50L2Vudi5ycwCHBxAAZAAAANUCAAAVAAAAQ2Fubm90IHNlcmlhbGl6ZSB0aGUgY29udHJhY3Qgc3RhdGUuhwcQAGQAAADZAgAAEAAAABQAAAAAAAAAAQAAABUAAAAWAAAAAAAAAAEAAAAXAAAAYAAQAFoAAABBBAAAEwAAAGAAEABaAAAASgQAABMAAAAYAAAAAAAAAAEAAAAZAAAAGgAAAAAAAAABAAAAGwAAABwAAAAAAAAAAQAAAB0AAAAeAAAAAAAAAAEAAAAfAAAAIAAAAAAAAAABAAAAIQAAAGFzc2VydGlvbiBmYWlsZWQ6IGAobGVmdCA9PSByaWdodClgCiAgbGVmdDogYGAsCiByaWdodDogYGA6IMAIEAAtAAAA7QgQAAwAAAD5CBAAAwAAAENvbnRyYWN0IGV4cGVjdGVkIGEgcmVzdWx0IG9uIHRoZSBjYWxsYmFjawAAFAkQACoAAABzcmMvbGliLnJzAABICRAACgAAACQAAAAFAAAARmFpbGVkIHRvIHNlcmlhbGl6ZSB0aGUgY3Jvc3MgY29udHJhY3QgYXJncyB1c2luZyBKU09OLgBICRAACgAAABoAAAABAAAAb25fYWNjb3VudF9jcmVhdGVkb25fYWNjb3VudF9jcmVhdGVkX2FuZF9jbGFpbWVkYWNjb3VudF9pZGFtb3VudHN0cnVjdCBJbnB1dEF0dGFjaGVkIGRlcG9zaXQgbXVzdCBiZSBncmVhdGVyIHRoYW4gQUNDRVNTX0tFWV9BTExPV0FOQ0UAAEgJEAAKAAAANQAAAAkAAABICRAACgAAADkAAAAcAAAASAkQAAoAAAA5AAAAGwAAAGNsYWltLGNyZWF0ZV9hY2NvdW50X2FuZF9jbGFpbUNsYWltIG9ubHkgY2FuIGNvbWUgZnJvbSB0aGlzIGFjY291bnQAggoQACUAAABICRAACgAAAEQAAAAJAAAASW52YWxpZCBhY2NvdW50IGlkAABICRAACgAAAEkAAAAJAAAAVW5leHBlY3RlZCBwdWJsaWMga2V5AAAASAkQAAoAAABNAAAAFgAAAENyZWF0ZSBhY2NvdW50IGFuZCBjbGFpbSBvbmx5IGNhbiBjb21lIGZyb20gdGhpcyBhY2NvdW50DAsQADgAAABICRAACgAAAFsAAAAJAAAASAkQAAoAAABgAAAACQAAAEgJEAAKAAAAZAAAABYAAABICRAACgAAAHsAAAAJAAAAQ2FsbGJhY2sgY2FuIG9ubHkgYmUgY2FsbGVkIGZyb20gdGhlIGNvbnRyYWN0AAAAjAsQAC0AAABICRAACgAAAIkAAAAJAAAASAkQAAoAAACYAAAACQAAACIAAAAAAAAAAQAAACMAAAAkAAAAJQAAACYAAAAnAAAAKAAAACkAAAAqAAAAKwAAACwAAAAtAAAALgAAAC8AAAAwAAAAMQAAADIAAAAzAAAANAAAADUAAAA2AAAANwAAADgAAAA5AAAAOgAAADsAAAA8AAAAPQAAAD4AAAA/AAAAQAAAAEEAAABCAAAAQwAAAEQAAABFAAAARgAAAEcAAABIAAAASQAAAEoAAABLAAAATAAAAE0AAABOAAAATwAAAFAAAABRAAAAUgAAAEV4cGVjdGVkIGlucHV0IHNpbmNlIG1ldGhvZCBoYXMgYXJndW1lbnRzLgAASAkQAAoAAAAvAAAAAQAAAEZhaWxlZCB0byBkZXNlcmlhbGl6ZSBpbnB1dCBmcm9tIEpTT04uRmFpbGVkIHRvIHNlcmlhbGl6ZSB0aGUgcmV0dXJuIHZhbHVlIHVzaW5nIEpTT04uTWV0aG9kIGRvZXNuJ3QgYWNjZXB0IGRlcG9zaXRwdWJsaWNfa2V5c3RydWN0IElucHV0IHdpdGggMSBlbGVtZW50aQ0QABsAAABTAAAACAAAAAQAAABUAAAAbmV3X2FjY291bnRfaWRuZXdfcHVibGljX2tleXN0cnVjdCBJbnB1dCB3aXRoIDIgZWxlbWVudHO4DRAAHAAAAHByZWRlY2Vzc29yX2FjY291bnRfaWQAAFoAAAAMAAAABAAAAFsAAABcAAAAXQAAAF4AAABfAAAAYAAAAGEAAAAcDhAAAAAAAGEgRGlzcGxheSBpbXBsZW1lbnRhdGlvbiByZXR1cm5lZCBhbiBlcnJvciB1bmV4cGVjdGVkbHkvcnVzdGMvY2QxZWYzOTBlNzMxZWQ3N2I5MGIxMWIxZjc3ZTJjNWNhNjQxYjI2MS9zcmMvbGliYWxsb2Mvc3RyaW5nLnJzAAAAWw4QAEYAAAB8CAAACQAAAAAAAAAAAAAAAAAAAGF0dGVtcHQgdG8gYWRkIHdpdGggb3ZlcmZsb3cAAAAAYXR0ZW1wdCB0byBzdWJ0cmFjdCB3aXRoIG92ZXJmbG93AAAAYgAAAAQAAAAEAAAAYwAAAGQAAABlAAAAL3J1c3RjL2NkMWVmMzkwZTczMWVkNzdiOTBiMTFiMWY3N2UyYzVjYTY0MWIyNjEvc3JjL2xpYmNvcmUvbWFjcm9zL21vZC5ycy9ydXN0Yy9jZDFlZjM5MGU3MzFlZDc3YjkwYjExYjFmNzdlMmM1Y2E2NDFiMjYxL3NyYy9saWJjb3JlL3N0ci9wYXR0ZXJuLnJzAGUPEABKAAAA2QQAABQAAABlDxAASgAAANkEAAAhAAAAZQ8QAEoAAADlBAAAFAAAAGUPEABKAAAA5QQAACEAAAAcDxAASQAAABIAAAANAAAAYXNzZXJ0aW9uIGZhaWxlZDogYChsZWZ0ID09IHJpZ2h0KWAKICBsZWZ0OiBgYCwKIHJpZ2h0OiBgYDogABAQAC0AAAAtEBAADAAAADkQEAADAAAAZGVzdGluYXRpb24gYW5kIHNvdXJjZSBzbGljZXMgaGF2ZSBkaWZmZXJlbnQgbGVuZ3Roc1QQEAA0AAAAZgAAAAAAAAABAAAADQAAAGNhbGxlZCBgUmVzdWx0Ojp1bndyYXAoKWAgb24gYW4gYEVycmAgdmFsdWUAZwAAAAAAAAABAAAAaAAAAGFzc2VydGlvbiBmYWlsZWQ6IHNlbGYuaXNfY2hhcl9ib3VuZGFyeShuZXdfbGVuKTw6OmNvcmU6Om1hY3Jvczo6cGFuaWMgbWFjcm9zPgAADBEQAB4AAAACAAAAAgAAAC9ydXN0Yy9jZDFlZjM5MGU3MzFlZDc3YjkwYjExYjFmNzdlMmM1Y2E2NDFiMjYxL3NyYy9saWJhbGxvYy9yYXdfdmVjLnJzADwREABHAAAAVwAAAB4AAABpbnRlcm5hbCBlcnJvcjogZW50ZXJlZCB1bnJlYWNoYWJsZSBjb2RlVHJpZWQgdG8gc2hyaW5rIHRvIGEgbGFyZ2VyIGNhcGFjaXR5AAAAAAAA8D8AAAAAAAAkQAAAAAAAAFlAAAAAAABAj0AAAAAAAIjDQAAAAAAAavhAAAAAAICELkEAAAAA0BJjQQAAAACE15dBAAAAAGXNzUEAAAAgX6ACQgAAAOh2SDdCAAAAopQabUIAAEDlnDCiQgAAkB7EvNZCAAA0JvVrDEMAgOA3ecNBQwCg2IVXNHZDAMhOZ23Bq0MAPZFg5FjhQ0CMtXgdrxVEUO/i1uQaS0SS1U0Gz/CARPZK4ccCLbVEtJ3ZeUN46kSRAigsKosgRTUDMrf0rVRFAoT+5HHZiUWBEh8v5yfARSHX5vrgMfRF6oygOVk+KUYksAiI741fRhduBbW1uJNGnMlGIuOmyEYDfNjqm9D+RoJNx3JhQjNH4yB5z/kSaEcbaVdDuBeeR7GhFirTztJHHUqc9IeCB0ilXMPxKWM9SOcZGjf6XXJIYaDgxHj1pkh5yBj21rLcSEx9z1nG7xFJnlxD8LdrRknGM1TspQZ8SVygtLMnhLFJc8ihoDHl5UmPOsoIfl4bSppkfsUOG1FKwP3ddtJhhUowfZUUR7q6Sj5u3WxstPBKzskUiIfhJEtB/Blq6RlaS6k9UOIxUJBLE03kWj5kxEtXYJ3xTX35S224BG6h3C9MRPPC5OTpY0wVsPMdXuSYTBuccKV1Hc9MkWFmh2lyA031+T/pA084TXL4j+PEYm5NR/s5Drv9ok0ZesjRKb3XTZ+YOkZ0rA1OZJ/kq8iLQk49x93Wui53Tgw5lYxp+qxOp0Pd94Ec4k6RlNR1oqMWT7W5SROLTExPERQO7NavgU8WmRGnzBu2T1v/1dC/outPmb+F4rdFIVB/LyfbJZdVUF/78FHv/IpQG502kxXewFBiRAT4mhX1UHtVBbYBWypRbVXDEeF4YFHIKjRWGZeUUXo1wavfvMlRbMFYywsWAFLH8S6+jhs0Ujmuum1yImlSx1kpCQ9rn1Id2Lll6aLTUiROKL+jiwhTrWHyroyuPlMMfVftFy1zU09crehd+KdTY7PYYnX23VMecMddCboSVCVMObWLaEdULp+Hoq5CfVR9w5QlrUmyVFz0+W4Y3OZUc3G4ih6THFXoRrMW89tRVaIYYNzvUoZVyh5406vnu1U/Eytky3DxVQ7YNT3+zCVWEk6DzD1AW1bLENKfJgiRVv6UxkcwSsVWPTq4Wbyc+lZmJBO49aEwV4DtFyZzymRX4Oid7w/9mVeMscL1KT7QV+9dM3O0TQRYazUAkCFhOVjFQgD0ablvWLspgDji06NYKjSgxtrI2Fg1QUh4EfsOWcEoLevqXENZ8XL4pSU0eFmtj3YPL0GuWcwZqmm96OJZP6AUxOyiF1pPyBn1p4tNWjIdMPlId4JafiR8NxsVt1qeLVsFYtrsWoL8WEN9CCJbozsvlJyKVluMCju5Qy2MW5fmxFNKnMFbPSC26FwD9ltNqOMiNIQrXDBJzpWgMmFcfNtBu0h/lVxbUhLqGt/KXHlzS9JwywBdV1DeBk3+NF1t5JVI4D1qXcSuXS2sZqBddRq1OFeA1F0SYeIGbaAJXqt8TSREBEBe1ttgLVUFdF7MErl4qgapXn9X5xZVSN9er5ZQLjWNE19bvOR5gnBIX3LrXRijjH5fJ7M67+UXs1/xXwlr393nX+23y0VX1R1g9FKfi1alUmCxJ4curE6HYJ3xKDpXIr1gApdZhHY18mDD/G8l1MImYfT7yy6Jc1xheH0/vTXIkWHWXI8sQzrGYQw0s/fTyPthhwDQeoRdMWKpAISZ5bRlYtQA5f8eIptihCDvX1P10GKl6Oo3qDIFY8+i5UVSfzpjwYWva5OPcGMyZ5tGeLOkY/5AQlhW4Nljn2gp9zUsEGTGwvN0QzdEZHizMFIURXlkVuC8ZlmWr2Q2DDbg973jZEOPQ9h1rRhlFHNUTtPYTmXsx/QQhEeDZej5MRVlGbhlYXh+Wr4f7mU9C4/41tMiZgzOsrbMiFdmj4Ff5P9qjWb5sLvu32LCZjidauqX+/ZmhkQF5X26LGfUSiOvjvRhZ4kd7FqycZZn6ySn8R4OzGcTdwhX04gBaNeUyiwI6zVoDTr9N8pla2hIRP5inh+haFrVvfuFZ9VosUqtemfBCmmvTqys4LhAaVpi19cY53Rp8TrNDd8gqmnWRKBoi1TgaQxWyEKuaRRqj2t60xmESWpzBllIIOV/agikNy0077NqCo2FOAHr6GpM8KaGwSUfazBWKPSYd1Nru2syMX9ViGuqBn/93mq+aypkb17LAvNrNT0LNn7DJ2yCDI7DXbRdbNHHOJq6kJJsxvnGQOk0x2w3uPiQIwL9bCNzmzpWITJt609CyaupZm3m45K7FlScbXDOOzWOtNFtDMKKwrEhBm6Pci0zHqo7bpln/N9SSnFuf4H7l+ecpW7fYfp9IQTbbix9vO6U4hBvdpxrKjobRW+Ugwa1CGJ6bz0SJHFFfbBvzBZtzZac5G9/XMiAvMMZcM85fdBVGlBwQ4icROsghHBUqsMVJim5cOmUNJtvc+9wEd0AwSWoI3FWFEExL5JYcWtZkf26to5x49d63jQyw3HcjRkWwv73cVPxn5ty/i1y1PZDoQe/YnKJ9JSJyW6Xcqsx+ut7Ss1yC198c41OAnPNdlvQMOI2c4FUcgS9mmxz0HTHIrbgoXMEUnmr41jWc4amV5Yc7wt0FMj23XF1QXQYenRVztJ1dJ6Y0eqBR6t0Y//CMrEM4XQ8v3N/3U8VdQuvUN/Uo0p1Z22SC2WmgHXACHdO/s+0dfHKFOL9A+p11v5MrX5CIHaMPqBYHlNUdi9OyO7lZ4l2u2F6at/Bv3YVfYyiK9nzdlqcL4t2zyh3cIP7LVQDX3cmMr2cFGKTd7B+7MOZOsh3XJ7nNEBJ/nf5whAhyO0yeLjzVCk6qWd4pTCqs4iTnXhnXkpwNXzSeAH2XMxCGwd5gjN0fxPiPHkxoKgvTA1yeT3IkjufkKZ5TXp3Csc03HlwrIpm/KAReoxXLYA7CUZ6b604YIqLe3plbCN8Njexen9HLBsEheV6Xln3IUXmGnvblzo1689Qe9I9iQLmA4V7Ro0rg99EuntMOPuxC2vwe18Gep7OhSR89ocYRkKnWXz6VM9riQiQfDgqw8arCsR8x/RzuFYN+Xz48ZBmrFAvfTuXGsBrkmN9Cj0hsAZ3mH1MjClcyJTOfbD3mTn9HAN+nHUAiDzkN34DkwCqS91tfuJbQEpPqqJ+2nLQHONU136QjwTkGyoNf7rZgm5ROkJ/KZAjyuXIdn8zdKw8H3usf6DI64XzzOF/L1VzZXJzL2N5cHJlc3MvLmNhcmdvL3JlZ2lzdHJ5L3NyYy9naXRodWIuY29tLTFlY2M2Mjk5ZGI5ZWM4MjMvc2VyZGVfanNvbi0xLjAuNDgvc3JjL2Vycm9yLnJzcmVjdXJzaW9uIGxpbWl0IGV4Y2VlZGVkdW5leHBlY3RlZCBlbmQgb2YgaGV4IGVzY2FwZXRyYWlsaW5nIGNoYXJhY3RlcnN0cmFpbGluZyBjb21tYWxvbmUgbGVhZGluZyBzdXJyb2dhdGUgaW4gaGV4IGVzY2FwZWtleSBtdXN0IGJlIGEgc3RyaW5nY29udHJvbCBjaGFyYWN0ZXIgKFx1MDAwMC1cdTAwMUYpIGZvdW5kIHdoaWxlIHBhcnNpbmcgYSBzdHJpbmdpbnZhbGlkIHVuaWNvZGUgY29kZSBwb2ludG51bWJlciBvdXQgb2YgcmFuZ2VpbnZhbGlkIG51bWJlcmludmFsaWQgZXNjYXBlZXhwZWN0ZWQgdmFsdWVleHBlY3RlZCBpZGVudGV4cGVjdGVkIGAsYCBvciBgfWBleHBlY3RlZCBgLGAgb3IgYF1gZXhwZWN0ZWQgYDpgRU9GIHdoaWxlIHBhcnNpbmcgYSB2YWx1ZUVPRiB3aGlsZSBwYXJzaW5nIGEgc3RyaW5nRU9GIHdoaWxlIHBhcnNpbmcgYW4gb2JqZWN0RU9GIHdoaWxlIHBhcnNpbmcgYSBsaXN0IGF0IGxpbmUgIGNvbHVtbiBFcnJvcigsIGxpbmU6ICwgY29sdW1uOiApAL4dEAAGAAAAxB0QAAgAAADMHRAACgAAANYdEAABAAAAaW52YWxpZCB0eXBlOiAsIGV4cGVjdGVkIAAAAPgdEAAOAAAABh4QAAsAAABpbnZhbGlkIHR5cGU6IG51bGwsIGV4cGVjdGVkIAAAACQeEAAdAAAAiBsQAF0AAACUAQAAGQAAAIgbEABdAAAAlwEAAAkAAACIGxAAXQAAAJ8BAAAbAAAAiBsQAF0AAACiAQAACQAAADAxMjM0NTY3ODlhYmNkZWZ1dXV1dXV1dWJ0bnVmcnV1dXV1dXV1dXV1dXV1dXV1dQAAIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL1VzZXJzL2N5cHJlc3MvLmNhcmdvL3JlZ2lzdHJ5L3NyYy9naXRodWIuY29tLTFlY2M2Mjk5ZGI5ZWM4MjMvc2VyZGVfanNvbi0xLjAuNDgvc3JjL3JlYWQucnOcHxAAXAAAAJkBAAAVAAAAnB8QAFwAAACVAQAAFQAAAJwfEABcAAAAtwEAABMAAACcHxAAXAAAAMcBAAAVAAAAnB8QAFwAAAC9AQAAGQAAAJwfEABcAAAAwQEAABkAAACcHxAAXAAAAP0BAAA7AAAAnB8QAFwAAAAXAgAAEwAAAJwfEABcAAAAKAIAAAwAAACcHxAAXAAAAC8CAAAlAAAAnB8QAFwAAAA0AgAAGQAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcHxAAXAAAAPoCAAAfAAAAnB8QAFwAAAD6AgAAPQAAAJwfEABcAAAAMwMAABcAAACcHxAAXAAAADMDAAA1AAAA////////////////////////////////////////////////////////////////AAECAwQFBgcICf////////8KCwwNDg///////////////////////////////////woLDA0OD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////y9Vc2Vycy9jeXByZXNzLy5jYXJnby9yZWdpc3RyeS9zcmMvZ2l0aHViLmNvbS0xZWNjNjI5OWRiOWVjODIzL21lbW9yeV91bml0cy0wLjQuMC9zcmMvbGliLnJz6CIQAFwAAACPAAAABgAAAAAAAAAAAAAAAAAAAGF0dGVtcHQgdG8gYWRkIHdpdGggb3ZlcmZsb3foIhAAXAAAAI8AAAAFAAAAAAAAAGF0dGVtcHQgdG8gc3VidHJhY3Qgd2l0aCBvdmVyZmxvdwAAAOgiEABcAAAAFgAAABcAAAAAAAAAAAAAAAAAAABhdHRlbXB0IHRvIG11bHRpcGx5IHdpdGggb3ZlcmZsb3cAAADoIhAAXAAAACoAAAAXAAAA6CIQAFwAAAA8AAAAFwAAAOgiEABcAAAAMwAAABcAAAAvVXNlcnMvY3lwcmVzcy8uY2FyZ28vcmVnaXN0cnkvc3JjL2dpdGh1Yi5jb20tMWVjYzYyOTlkYjllYzgyMy93ZWVfYWxsb2MtMC40LjUvc3JjL2xpYi5ycwAAACQkEABZAAAAngEAAA8AAAAkJBAAWQAAABkCAAAhAAAAJCQQAFkAAAAZAgAANAAAACQkEABZAAAAHAIAAAwAAAAkJBAAWQAAAB0CAAAjAAAAJCQQAFkAAAAhAgAAGwAAAGkAAAAAAAAAAQAAAGoAAABrAAAAbAAAAC9Vc2Vycy9jeXByZXNzLy5jYXJnby9yZWdpc3RyeS9zcmMvZ2l0aHViLmNvbS0xZWNjNjI5OWRiOWVjODIzL3dlZV9hbGxvYy0wLjQuNS9zcmMvaW1wX3dhc20zMi5yc/gkEABgAAAADAAAABMAAABvAAAADAAAAAQAAABwAAAAcQAAAHIAAABzAAAAdAAAAHUAAAB2AAAAZGVzY3JpcHRpb24oKSBpcyBkZXByZWNhdGVkOyB1c2UgRGlzcGxheWNhbm5vdCBhY2Nlc3MgYSBUaHJlYWQgTG9jYWwgU3RvcmFnZSB2YWx1ZSBkdXJpbmcgb3IgYWZ0ZXIgZGVzdHJ1Y3Rpb24vcnVzdGMvY2QxZWYzOTBlNzMxZWQ3N2I5MGIxMWIxZjc3ZTJjNWNhNjQxYjI2MS9zcmMvbGlic3RkL3RocmVhZC9sb2NhbC5yc/4lEABKAAAA7wAAAAkAAABYJhAAAAAAAGEgRGlzcGxheSBpbXBsZW1lbnRhdGlvbiByZXR1cm5lZCBhbiBlcnJvciB1bmV4cGVjdGVkbHkvcnVzdGMvY2QxZWYzOTBlNzMxZWQ3N2I5MGIxMWIxZjc3ZTJjNWNhNjQxYjI2MS9zcmMvbGliYWxsb2Mvc3RyaW5nLnJzAAAAlyYQAEYAAAB8CAAACQAAADEyMzQ1Njc4OUFCQ0RFRkdISktMTU5QUVJTVFVWV1hZWmFiY2RlZmdoaWprbW5vcHFyc3R1dnd4eXppbnRlcm5hbCBlcnJvcjogZW50ZXJlZCB1bnJlYWNoYWJsZSBjb2RlOiAqJxAAKgAAAFRoaXMgZnVuY3Rpb24gcmVxdWlyZXMgJ2NoZWNrJyBmZWF0dXJlAABcJxAAJgAAADw6OmNvcmU6Om1hY3Jvczo6cGFuaWMgbWFjcm9zPgAAjCcQAB4AAAAFAAAAMgAAAIwnEAAeAAAAAgAAAAIAAAAAAAAAYXR0ZW1wdCB0byBhZGQgd2l0aCBvdmVyZmxvdwAAAABhdHRlbXB0IHRvIG11bHRpcGx5IHdpdGggb3ZlcmZsb3dhbHJlYWR5IGJvcnJvd2VkL3J1c3RjL2NkMWVmMzkwZTczMWVkNzdiOTBiMTFiMWY3N2UyYzVjYTY0MWIyNjEvc3JjL2xpYmNvcmUvY2VsbC5ycyEoEABDAAAAbgMAAAkAAABhbHJlYWR5IG11dGFibHkgYm9ycm93ZWQhKBAAQwAAAB4DAAAJAAAAdwAAAAAAAAABAAAADwAAAHgAAAAAAAAAAQAAAHkAAAB6AAAAAAAAAAEAAAB7AAAAfAAAAAAAAAABAAAADQAAAGNhbGxlZCBgUmVzdWx0Ojp1bndyYXAoKWAgb24gYW4gYEVycmAgdmFsdWUAfQAAABQAAAAEAAAAfgAAAGludGVybmFsIGVycm9yOiBlbnRlcmVkIHVucmVhY2hhYmxlIGNvZGUvVXNlcnMvY3lwcmVzcy8uY2FyZ28vcmVnaXN0cnkvc3JjL2dpdGh1Yi5jb20tMWVjYzYyOTlkYjllYzgyMy9uZWFyLXNkay0wLjkuMi9zcmMvY29sbGVjdGlvbnMvbW9kLnJzQCkQAGQAAAA5AAAACQAAAH8AAACAAAAAAAAAAAEAAACBAAAAggAAAIMAAABCbG9ja2NoYWluIGludGVyZmFjZSBub3Qgc2V0Li9Vc2Vycy9jeXByZXNzLy5jYXJnby9yZWdpc3RyeS9zcmMvZ2l0aHViLmNvbS0xZWNjNjI5OWRiOWVjODIzL25lYXItc2RrLTAuOS4yL3NyYy9lbnZpcm9ubWVudC9lbnYucnMAAADtKRAAZAAAAIEAAAAJAAAA7SkQAGQAAACLAAAACQAAAFJlZ2lzdGVyIHdhcyBleHBlY3RlZCB0byBoYXZlIGRhdGEgYmVjYXVzZSB3ZSBqdXN0IHdyb3RlIGl0IGludG8gaXQu7SkQAGQAAACZAAAAFwAAAO0pEABkAAAAmQAAAAUAAADtKRAAZAAAAKMAAAAFAAAA7SkQAGQAAACoAAAAFwAAAO0pEABkAAAAqAAAAAUAAADtKRAAZAAAAKwAAAAFAAAA7SkQAGQAAADwAAAADQAAAO0pEABkAAAAbgEAAB4AAADtKRAAZAAAAHABAAAOAAAA7SkQAGQAAABwAQAALQAAAO0pEABkAAAAcQEAAB8AAADtKRAAZAAAAHUBAAANAAAA7SkQAGQAAAB/AQAADQAAAO0pEABkAAAAiQEAAA0AAADtKRAAZAAAAJQBAAANAAAA7SkQAGQAAACeAQAADQAAAO0pEABkAAAAsgEAAA0AAADtKRAAZAAAAMQBAAANAAAA7SkQAGQAAADSAQAADQAAAO0pEABkAAAA4gEAAA0AAADtKRAAZAAAAPgBAAANAAAA7SkQAGQAAAAMAgAADQAAAO0pEABkAAAAHQIAAA0AAADtKRAAZAAAAC8CAAANAAAAVW5leHBlY3RlZCByZXR1cm4gY29kZS4A7SkQAGQAAABFAgAADgAAAFByb21pc2UgcmVzdWx0IHNob3VsZCd2ZSByZXR1cm5lZCBpbnRvIHJlZ2lzdGVyLu0pEABkAAAAQAIAABgAAADtKRAAZAAAADgCAAANAAAA7SkQAGQAAABNAgAADQAAAO0pEABkAAAAWQIAAA0AAADtKRAAZAAAAGoCAAAFAAAA7SkQAGQAAABkAgAADQAAAO0pEABkAAAAiwIAAA4AAADtKRAAZAAAAIACAAANAAAA7SkQAGQAAACbAgAADgAAAO0pEABkAAAAmgIAABMAAADtKRAAZAAAAJICAAANAAAA7SkQAGQAAACsAgAADgAAAO0pEABkAAAAowIAAA0AAABDYW5ub3QgYWRkIGFjdGlvbiB0byBhIGpvaW50IHByb21pc2UuL1VzZXJzL2N5cHJlc3MvLmNhcmdvL3JlZ2lzdHJ5L3NyYy9naXRodWIuY29tLTFlY2M2Mjk5ZGI5ZWM4MjMvbmVhci1zZGstMC45LjIvc3JjL3Byb21pc2UucnMAAACFLRAAXAAAAOAAAAApAAAAQ2Fubm90IGNhbGxiYWNrIGpvaW50IHByb21pc2UuAACFLRAAXAAAAE8BAAApAAAAZWQyNTUxOXNlY3AyNTZrMVVua25vd24gY3VydmUga2luZEludmFsaWQgbGVuZ3RoIG9mIHRoZSBwdWJsaWMga2V5L3J1c3RjL2NkMWVmMzkwZTczMWVkNzdiOTBiMTFiMWY3N2UyYzVjYTY0MWIyNjEvc3JjL2xpYmNvcmUvb3BzL2FyaXRoLnJzAABmLhAASAAAALsCAAAzAAAAYXR0ZW1wdCB0byBhZGQgd2l0aCBvdmVyZmxvdy9Vc2Vycy9jeXByZXNzLy5jYXJnby9yZWdpc3RyeS9zcmMvZ2l0aHViLmNvbS0xZWNjNjI5OWRiOWVjODIzL2JzNTgtMC4zLjAvc3JjL2RlY29kZS5ycwDcLhAAVwAAAMwAAAANAAAAaW50ZXJuYWwgZXJyb3I6IGVudGVyZWQgdW5yZWFjaGFibGUgY29kZdwuEABXAAAATQEAACcAAABwcm92aWRlZCBzdHJpbmcgY29udGFpbmVkIG5vbi1hc2NpaSBjaGFyYWN0ZXIgc3RhcnRpbmcgYXQgYnl0ZSAAfC8QAD8AAABwcm92aWRlZCBzdHJpbmcgY29udGFpbmVkIGludmFsaWQgY2hhcmFjdGVyICBhdCBieXRlIAAAAMQvEAAsAAAA8C8QAAkAAABidWZmZXIgcHJvdmlkZWQgdG8gZGVjb2RlIGJhc2U1OCBlbmNvZGVkIHN0cmluZyBpbnRvIHdhcyB0b28gc21hbGwAAAwwEABCAAAAX19Ob25FeGhhdXN0aXZlTm9uQXNjaWlDaGFyYWN0ZXJpbmRleAAAAIUAAAAEAAAABAAAAIYAAABJbnZhbGlkQ2hhcmFjdGVyY2hhcmFjdGVyAAAAhwAAAAQAAAAEAAAAiAAAAEJ1ZmZlclRvb1NtYWxsYXNzZXJ0aW9uIGZhaWxlZDogYChsZWZ0ID09IHJpZ2h0KWAKICBsZWZ0OiBgYCwKIHJpZ2h0OiBgYDogAADKMBAALQAAAPcwEAAMAAAAAzEQAAMAAABkZXN0aW5hdGlvbiBhbmQgc291cmNlIHNsaWNlcyBoYXZlIGRpZmZlcmVudCBsZW5ndGhzIDEQADQAAAAvcnVzdGMvY2QxZWYzOTBlNzMxZWQ3N2I5MGIxMWIxZjc3ZTJjNWNhNjQxYjI2MS9zcmMvbGliY29yZS9tYWNyb3MvbW9kLnJzAAAAXDEQAEkAAAASAAAADQAAAGludGVybmFsIGVycm9yOiBlbnRlcmVkIHVucmVhY2hhYmxlIGNvZGU8Ojpjb3JlOjptYWNyb3M6OnBhbmljIG1hY3Jvcz4AAOAxEAAeAAAAAgAAAAIAAACKAAAACAAAAAQAAACLAAAAjAAAAI0AAAAIAAAABAAAAI4AAABhIHN0cmluZ2J5dGUgYXJyYXlzdHJ1Y3QgdmFyaWFudEYyEAAOAAAAdHVwbGUgdmFyaWFudAAAAFwyEAANAAAAbmV3dHlwZSB2YXJpYW50AHQyEAAPAAAAdW5pdCB2YXJpYW50jDIQAAwAAABlbnVtoDIQAAQAAABtYXAArDIQAAMAAABzZXF1ZW5jZbgyEAAIAAAAbmV3dHlwZSBzdHJ1Y3QAAMgyEAAOAAAAT3B0aW9uIHZhbHVl4DIQAAwAAAB1bml0IHZhbHVlAAD0MhAACgAAADwyEAAKAAAAc3RyaW5nIAAQMxAABwAAAGNoYXJhY3RlciBgYCAzEAALAAAAKzMQAAEAAABmbG9hdGluZyBwb2ludCBgPDMQABAAAAArMxAAAQAAAGludGVnZXIgYAAAAFwzEAAJAAAAKzMQAAEAAABib29sZWFuIGAAAAB4MxAACQAAACszEAABAAAAlgAAAAQAAAAEAAAAlwAAAJgAAACZAAAAL3J1c3RjL2NkMWVmMzkwZTczMWVkNzdiOTBiMTFiMWY3N2UyYzVjYTY0MWIyNjEvc3JjL2xpYmNvcmUvbWFjcm9zL21vZC5ycwAAAKwzEABJAAAAEgAAAA0AAABhc3NlcnRpb24gZmFpbGVkOiBgKGxlZnQgPT0gcmlnaHQpYAogIGxlZnQ6IGBgLAogcmlnaHQ6IGBgOiAINBAALQAAADU0EAAMAAAAQTQQAAMAAABkZXN0aW5hdGlvbiBhbmQgc291cmNlIHNsaWNlcyBoYXZlIGRpZmZlcmVudCBsZW5ndGhzXDQQADQAAABjYWxsZWQgYFJlc3VsdDo6dW53cmFwKClgIG9uIGFuIGBFcnJgIHZhbHVlAJoAAAAAAAAAAQAAAGgAAAA8Ojpjb3JlOjptYWNyb3M6OnBhbmljIG1hY3Jvcz4AANQ0EAAeAAAAAgAAAAIAAAAvcnVzdGMvY2QxZWYzOTBlNzMxZWQ3N2I5MGIxMWIxZjc3ZTJjNWNhNjQxYjI2MS9zcmMvbGliYWxsb2MvcmF3X3ZlYy5ycwAENRAARwAAAFcAAAAeAAAAVHJpZWQgdG8gc2hyaW5rIHRvIGEgbGFyZ2VyIGNhcGFjaXR5aW50ZXJuYWwgZXJyb3I6IGVudGVyZWQgdW5yZWFjaGFibGUgY29kZZ8AAAAEAAAABAAAAKAAAAChAAAAogAAAJ8AAAAAAAAAAQAAAKMAAABjYWxsZWQgYE9wdGlvbjo6dW53cmFwKClgIG9uIGEgYE5vbmVgIHZhbHVlQWNjZXNzRXJyb3IAAKQAAAAMAAAABAAAAKUAAACmAAAApwAAAF4AAAClAAAAYAAAAGEAAAB1bmV4cGVjdGVkIGVuZCBvZiBmaWxlb3RoZXIgb3MgZXJyb3JvcGVyYXRpb24gaW50ZXJydXB0ZWR3cml0ZSB6ZXJvdGltZWQgb3V0aW52YWxpZCBkYXRhaW52YWxpZCBpbnB1dCBwYXJhbWV0ZXJvcGVyYXRpb24gd291bGQgYmxvY2tlbnRpdHkgYWxyZWFkeSBleGlzdHNicm9rZW4gcGlwZWFkZHJlc3Mgbm90IGF2YWlsYWJsZWFkZHJlc3MgaW4gdXNlbm90IGNvbm5lY3RlZGNvbm5lY3Rpb24gYWJvcnRlZGNvbm5lY3Rpb24gcmVzZXRjb25uZWN0aW9uIHJlZnVzZWRwZXJtaXNzaW9uIGRlbmllZGVudGl0eSBub3QgZm91bmRLaW5kAAAAnwAAAAEAAAABAAAAqAAAAE9zY29kZQAAnwAAAAQAAAAEAAAAqQAAAGtpbmRtZXNzYWdlAKQAAAAMAAAABAAAAKoAAADANRAAAAAAACAob3MgZXJyb3IgKcA1EAAAAAAArDcQAAsAAAC3NxAAAQAAAGNhbm5vdCBtb2RpZnkgdGhlIHBhbmljIGhvb2sgZnJvbSBhIHBhbmlja2luZyB0aHJlYWRzcmMvbGlic3RkL3Bhbmlja2luZy5ycwAEOBAAFwAAAHAAAAAJAAAABDgQABcAAAB6AQAADwAAAAQ4EAAXAAAAewEAAA8AAACrAAAAEAAAAAQAAACsAAAArQAAAKQAAAAMAAAABAAAAK4AAACfAAAACAAAAAQAAACvAAAAsAAAAJ8AAAAIAAAABAAAALEAAABlcnJvckN1c3RvbQCfAAAABAAAAAQAAACyAAAAnwAAAAQAAAAEAAAAswAAAFVuZXhwZWN0ZWRFb2ZPdGhlckludGVycnVwdGVkV3JpdGVaZXJvVGltZWRPdXRJbnZhbGlkRGF0YUludmFsaWRJbnB1dFdvdWxkQmxvY2tBbHJlYWR5RXhpc3RzQnJva2VuUGlwZUFkZHJOb3RBdmFpbGFibGVBZGRySW5Vc2VOb3RDb25uZWN0ZWRDb25uZWN0aW9uQWJvcnRlZENvbm5lY3Rpb25SZXNldENvbm5lY3Rpb25SZWZ1c2VkUGVybWlzc2lvbkRlbmllZE5vdEZvdW5kb3BlcmF0aW9uIHN1Y2Nlc3NmdWy0AAAABAAAAAQAAAC1AAAAc3JjL2xpYmFsbG9jL3Jhd192ZWMucnNjYXBhY2l0eSBvdmVyZmxvd7g5EAAXAAAA6gIAAAUAAABGcm9tVXRmOEVycm9yYnl0ZXMAALQAAAAEAAAABAAAALYAAABlcnJvcgAAALQAAAAEAAAABAAAALcAAAAwYXNzZXJ0aW9uIGZhaWxlZDogZWRlbHRhID49IDBzcmMvbGliY29yZS9udW0vZGl5X2Zsb2F0LnJzAABKOhAAHAAAAEwAAAAJAAAAYXNzZXJ0aW9uIGZhaWxlZDogYChsZWZ0ID09IHJpZ2h0KWAKICBsZWZ0OiBgYCwKIHJpZ2h0OiBgYAAAeDoQAC0AAAClOhAADAAAALE6EAABAAAASjoQABwAAABOAAAACQAAAAEAAAAKAAAAZAAAAOgDAAAQJwAAoIYBAEBCDwCAlpgAAOH1BQDKmjsCAAAAFAAAAMgAAADQBwAAIE4AAEANAwCAhB4AAC0xAQDC6wsAlDV3AADBb/KGIwAAAAAAge+shVtBbS3uBAAAAAAAAAAAAAABH2q/ZO04bu2Xp9r0+T/pA08YAAAAAAAAAAAAAAAAAAAAAAABPpUuCZnfA/04FQ8v5HQj7PXP0wjcBMTasM28GX8zpgMmH+lOAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfC6YW4fTvnKf2diHLxUSxlDea3BuSs8P2JXVbnGyJrBmxq0kNhUdWtNCPA5U/2PAc1XMF+/5ZfIovFX3x9yA3O1u9M7v3F/3UwUAc3JjL2xpYmNvcmUvbnVtL2ZsdDJkZWMvc3RyYXRlZ3kvZHJhZ29uLnJzYXNzZXJ0aW9uIGZhaWxlZDogZC5tYW50ID4gMAAABDwQACoAAABxAAAABQAAAGFzc2VydGlvbiBmYWlsZWQ6IGQubWludXMgPiAwAAAABDwQACoAAAByAAAABQAAAGFzc2VydGlvbiBmYWlsZWQ6IGQucGx1cyA+IDAEPBAAKgAAAHMAAAAFAAAAYXNzZXJ0aW9uIGZhaWxlZDogZC5tYW50LmNoZWNrZWRfYWRkKGQucGx1cykuaXNfc29tZSgpAAAEPBAAKgAAAHQAAAAFAAAAYXNzZXJ0aW9uIGZhaWxlZDogZC5tYW50LmNoZWNrZWRfc3ViKGQubWludXMpLmlzX3NvbWUoKQAEPBAAKgAAAHUAAAAFAAAAYXNzZXJ0aW9uIGZhaWxlZDogYnVmLmxlbigpID49IE1BWF9TSUdfRElHSVRTAAAABDwQACoAAAB2AAAABQAAAAQ8EAAqAAAAvQAAAAkAAAAEPBAAKgAAAPUAAAANAAAABDwQACoAAAAAAQAABQAAAAQ8EAAqAAAAAQEAAAUAAAAEPBAAKgAAAAIBAAAFAAAABDwQACoAAAADAQAABQAAAAQ8EAAqAAAABAEAAAUAAAAEPBAAKgAAAFoBAAANAAAABDwQACoAAABkAQAANgAAAN9FGj0DzxrmwfvM/gAAAADKxprHF/5wq9z71P4AAAAAT9y8vvyxd//2+9z+AAAAAAzWa0HvkVa+Efzk/gAAAAA8/H+QrR/QjSz87P4AAAAAg5pVMShcUdNG/PT+AAAAALXJpq2PrHGdYfz8/gAAAADLi+4jdyKc6nv8BP8AAAAAbVN4QJFJzK6W/Az/AAAAAFfOtl15EjyCsfwU/wAAAAA3VvtNNpQQwsv8HP8AAAAAT5hIOG/qlpDm/CT/AAAAAMc6giXLhXTXAP0s/wAAAAD0l7+Xzc+GoBv9NP8AAAAA5awqF5gKNO81/Tz/AAAAAI6yNSr7ZziyUP1E/wAAAAA7P8bS39TIhGv9TP8AAAAAus3TGidE3cWF/VT/AAAAAJbJJbvOn2uToP1c/wAAAACEpWJ9JGys27r9ZP8AAAAA9tpfDVhmq6PV/Wz/AAAAACbxw96T+OLz7/10/wAAAAC4gP+qqK21tQr+fP8AAAAAi0p8bAVfYocl/oT/AAAAAFMwwTRg/7zJP/6M/wAAAABVJrqRjIVOllr+lP8AAAAAvX4pcCR3+d90/pz/AAAAAI+45bifvd+mj/6k/wAAAACUfXSIz1+p+Kn+rP8AAAAAz5uoj5NwRLnE/rT/AAAAAGsVD7/48AiK3/68/wAAAAC2MTFlVSWwzfn+xP8AAAAArH970MbiP5kU/8z/AAAAAAY7KyrEEFzkLv/U/wAAAADTknNpmSQkqkn/3P8AAAAADsoAg/K1h/1j/+T/AAAAAOsaEZJkCOW8fv/s/wAAAADMiFBvCcy8jJn/9P8AAAAALGUZ4lgXt9Gz//z/AAAAAAAAAAAAAECczv8EAAAAAAAAAAAAEKXU6Oj/DAAAAAAAAABirMXreK0DABQAAAAAAIQJlPh4OT+BHgAcAAAAAACzFQfJe86XwDgAJAAAAAAAcFzqe84yfo9TACwAAAAAAGiA6aukONLVbQA0AAAAAABFIpoXJidPn4gAPAAAAAAAJ/vE1DGiY+2iAEQAAAAAAKityIw4Zd6wvQBMAAAAAADbZasajgjHg9gAVAAAAAAAmh1xQvkdXcTyAFwAAAAAAFjnG6YsaU2SDQFkAAAAAADqjXAaZO4B2icBbAAAAAAASnfvmpmjbaJCAXQAAAAAAIVrfbR7eAnyXAF8AAAAAAB3GN15oeRUtHcBhAAAAAAAwsWbW5KGW4aSAYwAAAAAAD1dlsjFUzXIrAGUAAAAAACzoJf6XLQqlccBnAAAAAAA41+gmb2fRt7hAaQAAAAAACWMOds0wpul/AGsAAAAAABcn5ijcprG9hYCtAAAAAAAzr7pVFO/3LcxArwAAAAAAOJBIvIX8/yITALEAAAAAACleFzTm84gzGYCzAAAAAAA31Mhe/NaFpiBAtQAAAAAADowH5fctaDimwLcAAAAAACWs+NcU9HZqLYC5AAAAAAAPESnpNl8m/vQAuwAAAAAABBEpKdMTHa76wL0AAAAAAAanEC2746riwYD/AAAAAAALIRXphDvH9AgAwQBAAAAACkxkenlpBCbOwMMAQAAAACdDJyh+5sQ51UDFAEAAAAAKfQ7YtkgKKxwAxwBAAAAAIXPp3peS0SAiwMkAQAAAAAt3awDQOQhv6UDLAEAAAAAj/9EXi+cZ47AAzQBAAAAAEG4jJydFzPU2gM8AQAAAACpG+O0ktsZnvUDRAEAAAAA2Xffum6/lusPBEwBAAAAAHNyYy9saWJjb3JlL251bS9mbHQyZGVjL3N0cmF0ZWd5L2dyaXN1LnJzAAAAKEMQACkAAAB8AAAAFQAAAChDEAApAAAAqAAAAAUAAAAoQxAAKQAAAKkAAAAFAAAAKEMQACkAAACqAAAABQAAAChDEAApAAAAqwAAAAUAAAAoQxAAKQAAAKwAAAAFAAAAKEMQACkAAACtAAAABQAAAGFzc2VydGlvbiBmYWlsZWQ6IGQubWFudCArIGQucGx1cyA8ICgxIDw8IDYxKQAAAChDEAApAAAArgAAAAUAAAAoQxAAKQAAAAoBAAARAAAAAAAAAAAAAAAAAAAAYXR0ZW1wdCB0byBkaXZpZGUgYnkgemVybwAAAChDEAApAAAADQEAAAkAAAAoQxAAKQAAADkBAAAJAAAAYXNzZXJ0aW9uIGZhaWxlZDogIWJ1Zi5pc19lbXB0eSgpAAAAKEMQACkAAADTAQAABQAAAGFzc2VydGlvbiBmYWlsZWQ6IGQubWFudCA8ICgxIDw8IDYxKShDEAApAAAA1AEAAAUAAAAoQxAAKQAAANUBAAAFAAAAKEMQACkAAAAWAgAAEQAAAChDEAApAAAAGQIAAAkAAAAoQxAAKQAAAEwCAAAJAAAAc3JjL2xpYmNvcmUvbnVtL2ZsdDJkZWMvbW9kLnJzAAAERRAAHgAAAJcAAAANAAAABEUQAB4AAACZAAAAEQAAAARFEAAeAAAAnwAAAA0AAAAERRAAHgAAAKEAAAARAAAABEUQAB4AAAAfAQAABQAAAGFzc2VydGlvbiBmYWlsZWQ6IGJ1ZlswXSA+IGInMCcABEUQAB4AAAAgAQAABQAAADAuLi0raW5mTmFOYXNzZXJ0aW9uIGZhaWxlZDogYnVmLmxlbigpID49IG1heGxlbgRFEAAeAAAAzAIAAA0AAABmcm9tX3N0cl9yYWRpeF9pbnQ6IG11c3QgbGllIGluIHRoZSByYW5nZSBgWzIsIDM2XWAgLSBmb3VuZCDkRRAAPAAAAHNyYy9saWJjb3JlL251bS9tb2QucnMAAChGEAAWAAAAPhMAAAUAAABudW1iZXIgd291bGQgYmUgemVybyBmb3Igbm9uLXplcm8gdHlwZW51bWJlciB0b28gc21hbGwgdG8gZml0IGluIHRhcmdldCB0eXBlbnVtYmVyIHRvbyBsYXJnZSB0byBmaXQgaW4gdGFyZ2V0IHR5cGVpbnZhbGlkIGRpZ2l0IGZvdW5kIGluIHN0cmluZ2Nhbm5vdCBwYXJzZSBpbnRlZ2VyIGZyb20gZW1wdHkgc3RyaW5nLi4ABUcQAAIAAABCb3Jyb3dFcnJvckJvcnJvd011dEVycm9yY2FsbGVkIGBPcHRpb246OnVud3JhcCgpYCBvbiBhIGBOb25lYCB2YWx1ZSw6EAAAAAAAOiAAACw6EAAAAAAAXEcQAAIAAAC/AAAAAAAAAAEAAADAAAAAcGFuaWNrZWQgYXQgJycsIIxHEAABAAAAjUcQAAMAAAA6AAAALDoQAAAAAACgRxAAAQAAAKBHEAABAAAAaW5kZXggb3V0IG9mIGJvdW5kczogdGhlIGxlbiBpcyAgYnV0IHRoZSBpbmRleCBpcyAAALxHEAAgAAAA3EcQABIAAAC/AAAADAAAAAQAAADBAAAAwgAAAMMAAAAgICAgIHsKLAosICB7IH0gfSgKKCwpClu/AAAABAAAAAQAAADEAAAAXTB4MDAwMTAyMDMwNDA1MDYwNzA4MDkxMDExMTIxMzE0MTUxNjE3MTgxOTIwMjEyMjIzMjQyNTI2MjcyODI5MzAzMTMyMzMzNDM1MzYzNzM4Mzk0MDQxNDI0MzQ0NDU0NjQ3NDg0OTUwNTE1MjUzNTQ1NTU2NTc1ODU5NjA2MTYyNjM2NDY1NjY2NzY4Njk3MDcxNzI3Mzc0NzU3Njc3Nzg3OTgwODE4MjgzODQ4NTg2ODc4ODg5OTA5MTkyOTM5NDk1OTY5Nzk4OTkAvwAAAAQAAAAEAAAAxQAAAMYAAADHAAAAc3JjL2xpYmNvcmUvZm10L21vZC5ycwAAJEkQABYAAABEBAAADQAAACRJEAAWAAAAUAQAACQAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwdHJ1ZWZhbHNlKClzcmMvbGliY29yZS9zbGljZS9tb2QucnNpbmRleCAgb3V0IG9mIHJhbmdlIGZvciBzbGljZSBvZiBsZW5ndGggAL9JEAAGAAAAxUkQACIAAACnSRAAGAAAAHIKAAAFAAAAc2xpY2UgaW5kZXggc3RhcnRzIGF0ICBidXQgZW5kcyBhdCAACEoQABYAAAAeShAADQAAAKdJEAAYAAAAeAoAAAUAAABzcmMvbGliY29yZS9zdHIvcGF0dGVybi5ycwAATEoQABoAAAAQBQAAFQAAAExKEAAaAAAAPgUAABUAAABMShAAGgAAAD8FAAAVAAAAc3JjL2xpYmNvcmUvc3RyL21vZC5ycwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwMDAwMDAwMDAwMDAwMDAwQEBAQEAAAAAAAAAAAAAABbLi4uXWJ5dGUgaW5kZXggIGlzIG91dCBvZiBib3VuZHMgb2YgYLNLEAALAAAAvksQABYAAACxOhAAAQAAAJhKEAAWAAAAUwgAAAkAAABiZWdpbiA8PSBlbmQgKCA8PSApIHdoZW4gc2xpY2luZyBgAAD8SxAADgAAAApMEAAEAAAADkwQABAAAACxOhAAAQAAAJhKEAAWAAAAVwgAAAUAAACYShAAFgAAAGgIAAAOAAAAIGlzIG5vdCBhIGNoYXIgYm91bmRhcnk7IGl0IGlzIGluc2lkZSAgKGJ5dGVzICkgb2YgYLNLEAALAAAAYEwQACYAAACGTBAACAAAAI5MEAAGAAAAsToQAAEAAACYShAAFgAAAGoIAAAFAAAAc3JjL2xpYmNvcmUvdW5pY29kZS9wcmludGFibGUucnPMTBAAIAAAABoAAAAoAAAAAAEDBQUGBgMHBggICREKHAsZDBQNEg4NDwQQAxISEwkWARcFGAIZAxoHHAIdAR8WIAMrBCwCLQsuATADMQIyAacCqQKqBKsI+gL7Bf0E/gP/Ca14eYuNojBXWIuMkBwd3Q4PS0z7/C4vP1xdX7XihI2OkZKpsbq7xcbJyt7k5f8ABBESKTE0Nzo7PUlKXYSOkqmxtLq7xsrOz+TlAAQNDhESKTE0OjtFRklKXmRlhJGbncnOzw0RKUVJV2RljZGptLq7xcnf5OXwBA0RRUlkZYCBhLK8vr/V1/Dxg4WLpKa+v8XHzs/a20iYvc3Gzs9JTk9XWV5fiY6Psba3v8HGx9cRFhdbXPb3/v+ADW1x3t8ODx9ubxwdX31+rq+7vPoWFx4fRkdOT1haXF5+f7XF1NXc8PH1cnOPdHWWly9fJi4vp6+3v8fP19+aQJeYMI8fwMHO/05PWlsHCA8QJy/u725vNz0/QkWQkf7/U2d1yMnQ0djZ5/7/ACBfIoLfBIJECBsEBhGBrA6AqzUeFYDgAxkIAQQvBDQEBwMBBwYHEQpQDxIHVQgCBBwKCQMIAwcDAgMDAwwEBQMLBgEOFQU6AxEHBgUQB1cHAgcVDVAEQwMtAwEEEQYPDDoEHSVfIG0EaiWAyAWCsAMaBoL9A1kHFQsXCRQMFAxqBgoGGgZZBysFRgosBAwEAQMxCywEGgYLA4CsBgoGH0FMBC0DdAg8Aw8DPAc4CCsFgv8RGAgvES0DIBAhD4CMBIKXGQsViJQFLwU7BwIOGAmAsDB0DIDWGgwFgP8FgLYFJAybxgrSMBCEjQM3CYFcFIC4CIDHMDUECgY4CEYIDAZ0Cx4DWgRZCYCDGBwKFglICICKBqukDBcEMaEEgdomBwwFBYClEYFtEHgoKgZMBICNBIC+AxsDDw0ABgEBAwEEAggICQIKBQsCEAERBBIFExEUAhUCFwIZBBwFHQgkAWoDawK8AtEC1AzVCdYC1wLaAeAF4QLoAu4g8AT5BvoCDCc7Pk5Pj56enwYHCTY9Plbz0NEEFBg2N1ZXvTXOz+ASh4mOngQNDhESKTE0OkVGSUpOT2RlWly2txscqKnY2Qk3kJGoBwo7PmZpj5JvX+7vWmKamycoVZ2goaOkp6iturzEBgsMFR06P0VRpqfMzaAHGRoiJT4/xcYEICMlJigzODpISkxQU1VWWFpcXmBjZWZrc3h9f4qkqq+wwNAMcqOky8xub14iewUDBC0DZQQBLy6Agh0DMQ8cBCQJHgUrBUQEDiqAqgYkBCQEKAg0CwGAkIE3CRYKCICYOQNjCAkwFgUhAxsFAUA4BEsFLwQKBwkHQCAnBAwJNgM6BRoHBAwHUEk3Mw0zBy4ICoEmH4CBKAgqgIYXCU4EHg9DDhkHCgZHCScJdQs/QSoGOwUKBlEGAQUQAwWAi2AgSAgKgKZeIkULCgYNEzkHCjYsBBCAwDxkUwwBgKBFG0gIUx05gQdGCh0DR0k3Aw4ICgY5BwqBNhmAxzINg5tmdQuAxIq8hC+P0YJHobmCOQcqBAJgJgpGCigFE4KwW2VLBDkHEUAEHJf4CILzpQ2BHzEDEQQIgYyJBGsFDQMJBxCTYID2CnMIbhdGgJoUDFcJGYCHgUcDhUIPFYVQK4DVLQMaBAKBcDoFAYUAgNcpTAQKBAKDEURMPYDCPAYBBFUFGzQCgQ4sBGQMVgoNA10DPTkdDSwECQcCDgaAmoPWCg0DCwV0DFkHDBQMBDgICgYoCB5SdwMxA4CmDBQEAwUDDQaFanNyYy9saWJjb3JlL3VuaWNvZGUvbW9kLnJzADFSEAAaAAAAOAAAAA8AAAAxUhAAGgAAADkAAAAQAAAAc3JjL2xpYmNvcmUvbnVtL2JpZ251bS5ycwAAAGxSEAAZAAAA4wEAAAEAAABhc3NlcnRpb24gZmFpbGVkOiBub2JvcnJvd2Fzc2VydGlvbiBmYWlsZWQ6IGRpZ2l0cyA8IDQwYXNzZXJ0aW9uIGZhaWxlZDogb3RoZXIgPiAwAAC/AAAABAAAAAQAAADIAAAAvwAAAAQAAAAEAAAAyQAAAFRyeUZyb21TbGljZUVycm9yU29tZU5vbmVFcnJvclV0ZjhFcnJvcnZhbGlkX3VwX3RvZXJyb3JfbGVuAL8AAAAEAAAABAAAAMoAAAAZDhUeHAQRFxYAABAbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEg0TAAAAAAAAAAAAAAAAAAAAAAAAAAMGCQAHCyAfGh0AAAAAABgAAAAAAAAAAAAAAAAFAgAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAAAoACAAUAAwAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkAAAAAAAAAAAAAAAAABYvNAAAAAAAAAAAAAAAACgAqwIAAAAAAAAAAAAAAABcWIYmAAAAAAAAAAAAAABeZgYAAAAAAAAAAAAATBoAkohPK3UAAAAAAAAAAJgAADoAAAAAAAAAAAAAAAClYUsAAAAAAAAAAAAAAACAAAAAMAByAAAAAAAAAKpEAAAHAAAAAAAAAAAAAD0AAAAAAAAAABcAAAAAABwADgAAAAAAAAAAAAAAAACFAAAAAA+gLVQzTgxtAAALAAAeoVojUABFrQ1RgQAAOQAAAAAAAAAAAAAAAAAAgwBVAJQAr0kAAAAAAAAAFAQ+AHYAAAAgmpEAfFlDVhkAAAAAAAAAAAAAAAAAAAA7AACWRhiEPGR6o2MALgBCPwAAAIcAAAAAAABKAAAAAEchALJ7U3iJeWJ5p5k3AxJIlSRSAAAAAAAAAAAAAAAAaIUAbq5psaYAAAAAAACbi2sAAAAAAAAAAAAAAAAAAABvMmoAAAAAAAAArLOzcAkAcQAAAAAAADGOIh8AAAAAAHQAKo0AAAAAAAAAAAAAAACMXSV3AAAAAAAAAAAALAAAnwBlAJ4KHQAAAABbAAAAAKI4mTZ9NQAbcxV+E2yQfwioKZcFAACdJ5wBZwBBAAAAqZOCEWBXjxCKAABAfV8AALCzAACzs7NNAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAwAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADQAAAAAAAAAPAAAAAAAAABwAAAAAAAAAQAAAAAAAAACwAAAAAAAAAL8AAAAAAAAA+AMAAAAAAAAABwAAAAAAAP8HAAAAAAAA8A8AAAAAAAAAEAAAAAAAAAAeAAAAAAAAACAAAAAAAAABIAAAAAAAAEA/AAAAAAAAAHgAAAAAAADAfwAAAAAAAACAAAAAAAAAwP8BAAAAAACA/wMAAAAAAAAAIAAAAAAAAAAkAAAAAAAABFwAAAAAAAAAfwAAAAAAAACjAAAAAAAAAAACAAAAAAD8fwMAAAAAAACACQAAAAAAAAAOAAAAAIAAfg4AAAAAPwD/FwAAAAAAAP8fAAAAAGQgACAAAAAAQP6PIAAAAAABAAAwAAAAAAAAAEAAAAAAXAAAQAAAAAAAAAB+AAAAAAAAAMAAAAAAAAAA4AAAAAAAAADwAAAAAAAAAPgAAAAAAID//wAAAAAAAAAAAQAAAAAA8AwBAAAAAAAAQAEAAAD/////AwAAAAAAAAALAAAAHiAAAAwAAABAMAAADAAAAB4gQAAMAAAAwT1gAAwAAAAAAABgDwAAAAAAAABgAAAARAgAAGAAAAAAgAAAYAAAAAAAAADwAAAAYAAAAAACAAB////52wcAAAAAAID4BwAAAAAA4LwPAAAAAAAAICEAAAMAAAA8OwAA5w8AAAA8AAAAAMC//z0AAAAAAADAPwAAAADA//8/AAAA+AADkHwAAAAAAAAAgAAAAAAAAADwAAAQAAD4/v8AAP//CAD//wAA////////AAAAAAAAAAABAAEAAAAAAAEAAfj//wAAAQAAAAAAwP8BAAAA/////wEA/iH+AAwAAgAAAAAAAAADAAAAAAAAgAMAAAAAAECjAwAAAAAAAAAIAAAADAAAAAwABAAAAAD4DwC2AAAAAAAQAAAAAAAAABgAAAAcAAAAHAAAAADDAQAeAAAAAAAAAB8AAQAAAMAfHwAHAAAAgO8fAP//////HyAAhjkCAAAAIwACAAAgADBYAAAAAAAAfmYAAAD8///8bQAAAAAAAAB/AAAAAAAAKL8AAAAAAADwzwD//////wcAAQAAAAAAoZABAAAAAAAA/wEAAAADAACgAgAAAAAAAAADAAD3//0hEAMAAAAAgEAABP///////zAEAAAAAACA/wYAAAAAAADABwAAAAAAAPIHIAAAAAA8PggAAAAACAAADgAAAACHAQQOAAAAAAAAABACAAAAAAAAEAYAAAAAAAAQCBAAAAAAARAHAAAAAAAAFA8AAAAAANAXAwAAAAAAABgAAAAAAADyH9/g//7///8fAAAAAAAAACAAAAAAAPg/JAMAAAAAAHgmAAAAAAAAADAHAAAAAADIMwAAAAAAAAA/AAAAAAAAsD8AAAAAAAD/P4BAAAQAAABAHiAAAAwAAEAAAAAAAIDTQAPgAOAA4ABgAAAAAADg/WYAAAAAAAAAcP4HAAAAAPh5AwAAAAAAwH8AAAAAAAD+fwAAAACAAP9/AAAAAAAAAIB/AAAAAAAAgAAAAAAAgACAAAAAAN//AoAwAAAA//8DgAAAAAAAAPiFbvAAAAAAAIcCAAAAAAAAkAAAQH/lH/ifAAAAAAAAAKAAAAAAAAD4pwAAAAAAADywAAAAAAAAfrQAAAAAAAB/vwAAAAAAgPe/AAD+/////78RAAAAAAAAwAAAAAAAAJ3BAAAAAAAAAPj///////9/+P/////////7viEAAAwAAPwAAAAAAAAA/wIAAAAAAAD/AAAAAIAD+P8AAAAAAAD8/wAAAAAAAP//AIACAAAA//8AAAAAAPD//wAAAAD/////AgAAAP////8AAAD4/////wAA+P////////////////8NEgAADAAACQ4KAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQDAAAAAAAAAAAAAAAAAAAAAAAAAAECABAACAAACwAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAAAAAAAAAAABUIAAAAAAAAAAAAAAAAAAAqKz4AAAAAAAAAAAAADwoAMj46FAAAAAAAAAAAAD4AAAAAAAAAAAAAKiwEAAAAAAAAAAAAAAAAAD4BAAAAAAAAAAAAAAAAEBAAAAAAAAAAAAAAAAAAAB8APj4+AD4+Pj42GhsYAAAnDQAAAAAAAAAAAAAAAAAAMwsAAAAAAAAAAAAAAAAAADMgAAAAAAAAAAAAADMZABYTJT4+JD0+PhIMAB4xJgAdCQAiNAIAAAAAAAAAAAAuNz4RDgAAAAAAAAAAAAAAPgYqFwAAAAAAAAAAAAAAAD44ITwcOT4+Pj4wIygtLwU+Pjs+KTUDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAAAAAAGAAAAAAAAAD/AQAAAAAAAP8DAAAAAAAA9w8AAAAAAAD//wAAAAAAAH8A+AAAAAAA/v//BwAAAAAAAP8fAAAAAP///z8AAAAA/////wAAAAADAAAAHwAAAP//////AwAA/////78gAAD//////z8AAP///////wcA/////x94DAD//+//////AQAAAAAABCAEfAAAAAAAAAcAAAAA/v//B/7//wf+//8H//8P/////w/cH88P/x/cH////////z8/Pz//qv///z/////////fX7/n39////97AAAAAAAAAoAgAAAAAADPvP8B///////nvyD//////+f////fZN7/64T8Lz5QvR/y/f//9/////f/////////9///f////3//////////v/8AAAAAAADA////3////9///////////v8AAAAAAAD///////f/AP///wP///8D////f////3//////////f/////3////9////////P////0DX///7////AAAAAPz///8AAAAA/////+BDAAD///////9/AP///////z8//////////3/////////f//////9f/P3///////94////////A/z////////v//////////D///////////////////8EDxUbGQMSFxEAAA4WAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYTAAAAAAAAAAAAAAAAAAAAAAAAAAIHCgAIDB0cGBoAAAAAAAAAAAAAAAAAAAAAAAAFAQAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAsACQAUAA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8SAAAAAAAAAAAAAAAAAB8AAAAAAAAAAAAAAAAAAABJRmYdAAAAAAAAAAAAAAAAij4AAAAAAAAAAAAAAAAAS1MAAAAAAAAAAAAAAABnI0IAAAAAAAAAAAAAAAA9AAAAAAAjAAAAAAAAAAAAdQAALQAAAAAAAAAAAAAAAIJOPAAAAAAAAAAAAAAAAGMAAAAlAFoAAAAAAAAAgTYAAAMAAAAAAAAAAAAALwAAAAAAAAAAEAAAAAAAEwAIAAAAAAAAAAAAAAAAAEMAcgCJAAAAAAAAAAAAAAcAAAB9BRg/ADeHCUBkAAAhAAAAAAAAAAAAAAAAAAoAAEEAAAAAAAAAAAAAAAAMADAAXAAAABl3cQBgRzVELgAAdDkRZSxRXn9QAAAANDEAAABTAAAAAAAAOgAAAAA4GgCIXytraV1PXYSAKmgUOwAXAAAAAAAAAAAAAAAAAFUAAFcAAACDAAAAAAAAAABZAAAAAAAAJm4bFgAAAAAAbUocAAAAAAAAAAAAACQAAHwAUgB7BhUAAAAASAAAAAB+KHYnbCkAIlsOYQ1WcGIEhSB4AgAAeh55AVQAMwAAAIZzWABNRW8LagAAMmxMAACJigAAioqKPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAADQAAAAAAAAAcAAAAAAAAAEAAAAAAAAAAtgAAAAAAAAC/AAAAAAAAAPgDAAAAAAAA8AcAAAAAAAD/BwAAAAAAAAAQAAAAAAAAAB4AAAAAAAAAOAAAAAAAAAA/AAAAAAAAgH8AAAAAAAAAgAAAAAAAAMD/AQAAAAAAgP8DAAAAAAAAgAcAAAAAAAAAfwAAAAAAASCAAAAAAAAAAKMAAAAAAAD8fwMAAAAAAAAABgAAAAAAAP8HAAAAAAAAgAkAAAAAAAAADgAAAACAAH4OAAAAAGQgACAAAAAAQP4PIAAAAAABAAAwAAAAAAAAAEAAAAAAXAAAQAAAAAAAAABgAAAAAACEXIAAAAAAAAAAwAAAAAAAAADgAAAAAAAAAAABAAAAAADwDAEAAABEMGAADAAAAME9YAAMAAAAHiCAAAwAAAAeIMAADAAAAP4h/gAMAAAAAAAAACAAAAAAAAAAYAAAAEQIAABgAAAAAAAAAPAAAABgAAAAAAIAAH////nbBwAAAAAAgPgHAAAAAADgvA8AAAAAAAAgIQAAAwAAADw7AADnDwAAADwAAAAAwJ+fPQAAAADA++8+AAAAAAAAwD8AAAAAAAAA8AAAAAAAAAD8AAAQAAD4/v8AAP//AAD//wAA////////AAAA+P//AAABAAAAAADA/wEAAAD/////AQAAAAAAAAADAAAAAAAAgAMAAAAAAECjAwAAAAAAAAAIAAAADAAAAAwABAAAAAD4DwAAAAAAAAAYAAAAHAAAABwAAAAAwwEAHgAAAAAAAAAfAAEAgADAHx8ABwAAAIDvHwD//////x8gAIY5AgAAACMAAgAAAAAwQAAAAAAAAH5mAAAA/P///G0AAAAAAAAAfwAAAAAAACi/AAAAAAAA8M8AAAAAAwAAoAIAAPf//SEQAwMAAAAAAHgGAAAAAACA/wYAAAAAAADABwAAAAAAAPIHAAAAAIcBBA4GAAAAAAAAEAgQAAAAAAAQBwAAAAAAABQPAAAAAADwFwAAAAAAAPIf3+D//v///x8AAAAAAAAAIAAAAAAA+A8gBwAAAAAAyDMAAAAAAACwPwAAAAAAgPc/BAAAAAAAAEAeIIAADAAAQAAAAAAAgNNAAgAAAAAAAFADAAAAAAAAWAAAAAAA4P1m/gcAAAAA+HkDAAAAAADAfwAAAAAAAP5/AAAAAAAA/38AAAAAAAAAgH8AAAAAAACAMAAAAP//A4Bu8AAAAAAAhwIAAAAAAACQAABAf+Uf+J8AAAAAAAD5pQAAAAAAAPinAAAAAACAPLAAAAAAAAB+tAAAAAAAAH+/AAD+/////78RAAAAAAAAwAAAAAAAAJ3BAgAAAAAAANAAAAAAoMMH+P///////3/4//////////u+IQAADAAA/AAAAAAAAAD/AgAAAAAAAP8AAAIAAAD//wAA+P/7////AAAAAP///////////////0EAAABhAAAAAAAAAAAAAABCAAAAYgAAAAAAAAAAAAAAQwAAAGMAAAAAAAAAAAAAAEQAAABkAAAAAAAAAAAAAABFAAAAZQAAAAAAAAAAAAAARgAAAGYAAAAAAAAAAAAAAEcAAABnAAAAAAAAAAAAAABIAAAAaAAAAAAAAAAAAAAASQAAAGkAAAAAAAAAAAAAAEoAAABqAAAAAAAAAAAAAABLAAAAawAAAAAAAAAAAAAATAAAAGwAAAAAAAAAAAAAAE0AAABtAAAAAAAAAAAAAABOAAAAbgAAAAAAAAAAAAAATwAAAG8AAAAAAAAAAAAAAFAAAABwAAAAAAAAAAAAAABRAAAAcQAAAAAAAAAAAAAAUgAAAHIAAAAAAAAAAAAAAFMAAABzAAAAAAAAAAAAAABUAAAAdAAAAAAAAAAAAAAAVQAAAHUAAAAAAAAAAAAAAFYAAAB2AAAAAAAAAAAAAABXAAAAdwAAAAAAAAAAAAAAWAAAAHgAAAAAAAAAAAAAAFkAAAB5AAAAAAAAAAAAAABaAAAAegAAAAAAAAAAAAAAwAAAAOAAAAAAAAAAAAAAAMEAAADhAAAAAAAAAAAAAADCAAAA4gAAAAAAAAAAAAAAwwAAAOMAAAAAAAAAAAAAAMQAAADkAAAAAAAAAAAAAADFAAAA5QAAAAAAAAAAAAAAxgAAAOYAAAAAAAAAAAAAAMcAAADnAAAAAAAAAAAAAADIAAAA6AAAAAAAAAAAAAAAyQAAAOkAAAAAAAAAAAAAAMoAAADqAAAAAAAAAAAAAADLAAAA6wAAAAAAAAAAAAAAzAAAAOwAAAAAAAAAAAAAAM0AAADtAAAAAAAAAAAAAADOAAAA7gAAAAAAAAAAAAAAzwAAAO8AAAAAAAAAAAAAANAAAADwAAAAAAAAAAAAAADRAAAA8QAAAAAAAAAAAAAA0gAAAPIAAAAAAAAAAAAAANMAAADzAAAAAAAAAAAAAADUAAAA9AAAAAAAAAAAAAAA1QAAAPUAAAAAAAAAAAAAANYAAAD2AAAAAAAAAAAAAADYAAAA+AAAAAAAAAAAAAAA2QAAAPkAAAAAAAAAAAAAANoAAAD6AAAAAAAAAAAAAADbAAAA+wAAAAAAAAAAAAAA3AAAAPwAAAAAAAAAAAAAAN0AAAD9AAAAAAAAAAAAAADeAAAA/gAAAAAAAAAAAAAAAAEAAAEBAAAAAAAAAAAAAAIBAAADAQAAAAAAAAAAAAAEAQAABQEAAAAAAAAAAAAABgEAAAcBAAAAAAAAAAAAAAgBAAAJAQAAAAAAAAAAAAAKAQAACwEAAAAAAAAAAAAADAEAAA0BAAAAAAAAAAAAAA4BAAAPAQAAAAAAAAAAAAAQAQAAEQEAAAAAAAAAAAAAEgEAABMBAAAAAAAAAAAAABQBAAAVAQAAAAAAAAAAAAAWAQAAFwEAAAAAAAAAAAAAGAEAABkBAAAAAAAAAAAAABoBAAAbAQAAAAAAAAAAAAAcAQAAHQEAAAAAAAAAAAAAHgEAAB8BAAAAAAAAAAAAACABAAAhAQAAAAAAAAAAAAAiAQAAIwEAAAAAAAAAAAAAJAEAACUBAAAAAAAAAAAAACYBAAAnAQAAAAAAAAAAAAAoAQAAKQEAAAAAAAAAAAAAKgEAACsBAAAAAAAAAAAAACwBAAAtAQAAAAAAAAAAAAAuAQAALwEAAAAAAAAAAAAAMAEAAGkAAAAHAwAAAAAAADIBAAAzAQAAAAAAAAAAAAA0AQAANQEAAAAAAAAAAAAANgEAADcBAAAAAAAAAAAAADkBAAA6AQAAAAAAAAAAAAA7AQAAPAEAAAAAAAAAAAAAPQEAAD4BAAAAAAAAAAAAAD8BAABAAQAAAAAAAAAAAABBAQAAQgEAAAAAAAAAAAAAQwEAAEQBAAAAAAAAAAAAAEUBAABGAQAAAAAAAAAAAABHAQAASAEAAAAAAAAAAAAASgEAAEsBAAAAAAAAAAAAAEwBAABNAQAAAAAAAAAAAABOAQAATwEAAAAAAAAAAAAAUAEAAFEBAAAAAAAAAAAAAFIBAABTAQAAAAAAAAAAAABUAQAAVQEAAAAAAAAAAAAAVgEAAFcBAAAAAAAAAAAAAFgBAABZAQAAAAAAAAAAAABaAQAAWwEAAAAAAAAAAAAAXAEAAF0BAAAAAAAAAAAAAF4BAABfAQAAAAAAAAAAAABgAQAAYQEAAAAAAAAAAAAAYgEAAGMBAAAAAAAAAAAAAGQBAABlAQAAAAAAAAAAAABmAQAAZwEAAAAAAAAAAAAAaAEAAGkBAAAAAAAAAAAAAGoBAABrAQAAAAAAAAAAAABsAQAAbQEAAAAAAAAAAAAAbgEAAG8BAAAAAAAAAAAAAHABAABxAQAAAAAAAAAAAAByAQAAcwEAAAAAAAAAAAAAdAEAAHUBAAAAAAAAAAAAAHYBAAB3AQAAAAAAAAAAAAB4AQAA/wAAAAAAAAAAAAAAeQEAAHoBAAAAAAAAAAAAAHsBAAB8AQAAAAAAAAAAAAB9AQAAfgEAAAAAAAAAAAAAgQEAAFMCAAAAAAAAAAAAAIIBAACDAQAAAAAAAAAAAACEAQAAhQEAAAAAAAAAAAAAhgEAAFQCAAAAAAAAAAAAAIcBAACIAQAAAAAAAAAAAACJAQAAVgIAAAAAAAAAAAAAigEAAFcCAAAAAAAAAAAAAIsBAACMAQAAAAAAAAAAAACOAQAA3QEAAAAAAAAAAAAAjwEAAFkCAAAAAAAAAAAAAJABAABbAgAAAAAAAAAAAACRAQAAkgEAAAAAAAAAAAAAkwEAAGACAAAAAAAAAAAAAJQBAABjAgAAAAAAAAAAAACWAQAAaQIAAAAAAAAAAAAAlwEAAGgCAAAAAAAAAAAAAJgBAACZAQAAAAAAAAAAAACcAQAAbwIAAAAAAAAAAAAAnQEAAHICAAAAAAAAAAAAAJ8BAAB1AgAAAAAAAAAAAACgAQAAoQEAAAAAAAAAAAAAogEAAKMBAAAAAAAAAAAAAKQBAAClAQAAAAAAAAAAAACmAQAAgAIAAAAAAAAAAAAApwEAAKgBAAAAAAAAAAAAAKkBAACDAgAAAAAAAAAAAACsAQAArQEAAAAAAAAAAAAArgEAAIgCAAAAAAAAAAAAAK8BAACwAQAAAAAAAAAAAACxAQAAigIAAAAAAAAAAAAAsgEAAIsCAAAAAAAAAAAAALMBAAC0AQAAAAAAAAAAAAC1AQAAtgEAAAAAAAAAAAAAtwEAAJICAAAAAAAAAAAAALgBAAC5AQAAAAAAAAAAAAC8AQAAvQEAAAAAAAAAAAAAxAEAAMYBAAAAAAAAAAAAAMUBAADGAQAAAAAAAAAAAADHAQAAyQEAAAAAAAAAAAAAyAEAAMkBAAAAAAAAAAAAAMoBAADMAQAAAAAAAAAAAADLAQAAzAEAAAAAAAAAAAAAzQEAAM4BAAAAAAAAAAAAAM8BAADQAQAAAAAAAAAAAADRAQAA0gEAAAAAAAAAAAAA0wEAANQBAAAAAAAAAAAAANUBAADWAQAAAAAAAAAAAADXAQAA2AEAAAAAAAAAAAAA2QEAANoBAAAAAAAAAAAAANsBAADcAQAAAAAAAAAAAADeAQAA3wEAAAAAAAAAAAAA4AEAAOEBAAAAAAAAAAAAAOIBAADjAQAAAAAAAAAAAADkAQAA5QEAAAAAAAAAAAAA5gEAAOcBAAAAAAAAAAAAAOgBAADpAQAAAAAAAAAAAADqAQAA6wEAAAAAAAAAAAAA7AEAAO0BAAAAAAAAAAAAAO4BAADvAQAAAAAAAAAAAADxAQAA8wEAAAAAAAAAAAAA8gEAAPMBAAAAAAAAAAAAAPQBAAD1AQAAAAAAAAAAAAD2AQAAlQEAAAAAAAAAAAAA9wEAAL8BAAAAAAAAAAAAAPgBAAD5AQAAAAAAAAAAAAD6AQAA+wEAAAAAAAAAAAAA/AEAAP0BAAAAAAAAAAAAAP4BAAD/AQAAAAAAAAAAAAAAAgAAAQIAAAAAAAAAAAAAAgIAAAMCAAAAAAAAAAAAAAQCAAAFAgAAAAAAAAAAAAAGAgAABwIAAAAAAAAAAAAACAIAAAkCAAAAAAAAAAAAAAoCAAALAgAAAAAAAAAAAAAMAgAADQIAAAAAAAAAAAAADgIAAA8CAAAAAAAAAAAAABACAAARAgAAAAAAAAAAAAASAgAAEwIAAAAAAAAAAAAAFAIAABUCAAAAAAAAAAAAABYCAAAXAgAAAAAAAAAAAAAYAgAAGQIAAAAAAAAAAAAAGgIAABsCAAAAAAAAAAAAABwCAAAdAgAAAAAAAAAAAAAeAgAAHwIAAAAAAAAAAAAAIAIAAJ4BAAAAAAAAAAAAACICAAAjAgAAAAAAAAAAAAAkAgAAJQIAAAAAAAAAAAAAJgIAACcCAAAAAAAAAAAAACgCAAApAgAAAAAAAAAAAAAqAgAAKwIAAAAAAAAAAAAALAIAAC0CAAAAAAAAAAAAAC4CAAAvAgAAAAAAAAAAAAAwAgAAMQIAAAAAAAAAAAAAMgIAADMCAAAAAAAAAAAAADoCAABlLAAAAAAAAAAAAAA7AgAAPAIAAAAAAAAAAAAAPQIAAJoBAAAAAAAAAAAAAD4CAABmLAAAAAAAAAAAAABBAgAAQgIAAAAAAAAAAAAAQwIAAIABAAAAAAAAAAAAAEQCAACJAgAAAAAAAAAAAABFAgAAjAIAAAAAAAAAAAAARgIAAEcCAAAAAAAAAAAAAEgCAABJAgAAAAAAAAAAAABKAgAASwIAAAAAAAAAAAAATAIAAE0CAAAAAAAAAAAAAE4CAABPAgAAAAAAAAAAAABwAwAAcQMAAAAAAAAAAAAAcgMAAHMDAAAAAAAAAAAAAHYDAAB3AwAAAAAAAAAAAAB/AwAA8wMAAAAAAAAAAAAAhgMAAKwDAAAAAAAAAAAAAIgDAACtAwAAAAAAAAAAAACJAwAArgMAAAAAAAAAAAAAigMAAK8DAAAAAAAAAAAAAIwDAADMAwAAAAAAAAAAAACOAwAAzQMAAAAAAAAAAAAAjwMAAM4DAAAAAAAAAAAAAJEDAACxAwAAAAAAAAAAAACSAwAAsgMAAAAAAAAAAAAAkwMAALMDAAAAAAAAAAAAAJQDAAC0AwAAAAAAAAAAAACVAwAAtQMAAAAAAAAAAAAAlgMAALYDAAAAAAAAAAAAAJcDAAC3AwAAAAAAAAAAAACYAwAAuAMAAAAAAAAAAAAAmQMAALkDAAAAAAAAAAAAAJoDAAC6AwAAAAAAAAAAAACbAwAAuwMAAAAAAAAAAAAAnAMAALwDAAAAAAAAAAAAAJ0DAAC9AwAAAAAAAAAAAACeAwAAvgMAAAAAAAAAAAAAnwMAAL8DAAAAAAAAAAAAAKADAADAAwAAAAAAAAAAAAChAwAAwQMAAAAAAAAAAAAAowMAAMMDAAAAAAAAAAAAAKQDAADEAwAAAAAAAAAAAAClAwAAxQMAAAAAAAAAAAAApgMAAMYDAAAAAAAAAAAAAKcDAADHAwAAAAAAAAAAAACoAwAAyAMAAAAAAAAAAAAAqQMAAMkDAAAAAAAAAAAAAKoDAADKAwAAAAAAAAAAAACrAwAAywMAAAAAAAAAAAAAzwMAANcDAAAAAAAAAAAAANgDAADZAwAAAAAAAAAAAADaAwAA2wMAAAAAAAAAAAAA3AMAAN0DAAAAAAAAAAAAAN4DAADfAwAAAAAAAAAAAADgAwAA4QMAAAAAAAAAAAAA4gMAAOMDAAAAAAAAAAAAAOQDAADlAwAAAAAAAAAAAADmAwAA5wMAAAAAAAAAAAAA6AMAAOkDAAAAAAAAAAAAAOoDAADrAwAAAAAAAAAAAADsAwAA7QMAAAAAAAAAAAAA7gMAAO8DAAAAAAAAAAAAAPQDAAC4AwAAAAAAAAAAAAD3AwAA+AMAAAAAAAAAAAAA+QMAAPIDAAAAAAAAAAAAAPoDAAD7AwAAAAAAAAAAAAD9AwAAewMAAAAAAAAAAAAA/gMAAHwDAAAAAAAAAAAAAP8DAAB9AwAAAAAAAAAAAAAABAAAUAQAAAAAAAAAAAAAAQQAAFEEAAAAAAAAAAAAAAIEAABSBAAAAAAAAAAAAAADBAAAUwQAAAAAAAAAAAAABAQAAFQEAAAAAAAAAAAAAAUEAABVBAAAAAAAAAAAAAAGBAAAVgQAAAAAAAAAAAAABwQAAFcEAAAAAAAAAAAAAAgEAABYBAAAAAAAAAAAAAAJBAAAWQQAAAAAAAAAAAAACgQAAFoEAAAAAAAAAAAAAAsEAABbBAAAAAAAAAAAAAAMBAAAXAQAAAAAAAAAAAAADQQAAF0EAAAAAAAAAAAAAA4EAABeBAAAAAAAAAAAAAAPBAAAXwQAAAAAAAAAAAAAEAQAADAEAAAAAAAAAAAAABEEAAAxBAAAAAAAAAAAAAASBAAAMgQAAAAAAAAAAAAAEwQAADMEAAAAAAAAAAAAABQEAAA0BAAAAAAAAAAAAAAVBAAANQQAAAAAAAAAAAAAFgQAADYEAAAAAAAAAAAAABcEAAA3BAAAAAAAAAAAAAAYBAAAOAQAAAAAAAAAAAAAGQQAADkEAAAAAAAAAAAAABoEAAA6BAAAAAAAAAAAAAAbBAAAOwQAAAAAAAAAAAAAHAQAADwEAAAAAAAAAAAAAB0EAAA9BAAAAAAAAAAAAAAeBAAAPgQAAAAAAAAAAAAAHwQAAD8EAAAAAAAAAAAAACAEAABABAAAAAAAAAAAAAAhBAAAQQQAAAAAAAAAAAAAIgQAAEIEAAAAAAAAAAAAACMEAABDBAAAAAAAAAAAAAAkBAAARAQAAAAAAAAAAAAAJQQAAEUEAAAAAAAAAAAAACYEAABGBAAAAAAAAAAAAAAnBAAARwQAAAAAAAAAAAAAKAQAAEgEAAAAAAAAAAAAACkEAABJBAAAAAAAAAAAAAAqBAAASgQAAAAAAAAAAAAAKwQAAEsEAAAAAAAAAAAAACwEAABMBAAAAAAAAAAAAAAtBAAATQQAAAAAAAAAAAAALgQAAE4EAAAAAAAAAAAAAC8EAABPBAAAAAAAAAAAAABgBAAAYQQAAAAAAAAAAAAAYgQAAGMEAAAAAAAAAAAAAGQEAABlBAAAAAAAAAAAAABmBAAAZwQAAAAAAAAAAAAAaAQAAGkEAAAAAAAAAAAAAGoEAABrBAAAAAAAAAAAAABsBAAAbQQAAAAAAAAAAAAAbgQAAG8EAAAAAAAAAAAAAHAEAABxBAAAAAAAAAAAAAByBAAAcwQAAAAAAAAAAAAAdAQAAHUEAAAAAAAAAAAAAHYEAAB3BAAAAAAAAAAAAAB4BAAAeQQAAAAAAAAAAAAAegQAAHsEAAAAAAAAAAAAAHwEAAB9BAAAAAAAAAAAAAB+BAAAfwQAAAAAAAAAAAAAgAQAAIEEAAAAAAAAAAAAAIoEAACLBAAAAAAAAAAAAACMBAAAjQQAAAAAAAAAAAAAjgQAAI8EAAAAAAAAAAAAAJAEAACRBAAAAAAAAAAAAACSBAAAkwQAAAAAAAAAAAAAlAQAAJUEAAAAAAAAAAAAAJYEAACXBAAAAAAAAAAAAACYBAAAmQQAAAAAAAAAAAAAmgQAAJsEAAAAAAAAAAAAAJwEAACdBAAAAAAAAAAAAACeBAAAnwQAAAAAAAAAAAAAoAQAAKEEAAAAAAAAAAAAAKIEAACjBAAAAAAAAAAAAACkBAAApQQAAAAAAAAAAAAApgQAAKcEAAAAAAAAAAAAAKgEAACpBAAAAAAAAAAAAACqBAAAqwQAAAAAAAAAAAAArAQAAK0EAAAAAAAAAAAAAK4EAACvBAAAAAAAAAAAAACwBAAAsQQAAAAAAAAAAAAAsgQAALMEAAAAAAAAAAAAALQEAAC1BAAAAAAAAAAAAAC2BAAAtwQAAAAAAAAAAAAAuAQAALkEAAAAAAAAAAAAALoEAAC7BAAAAAAAAAAAAAC8BAAAvQQAAAAAAAAAAAAAvgQAAL8EAAAAAAAAAAAAAMAEAADPBAAAAAAAAAAAAADBBAAAwgQAAAAAAAAAAAAAwwQAAMQEAAAAAAAAAAAAAMUEAADGBAAAAAAAAAAAAADHBAAAyAQAAAAAAAAAAAAAyQQAAMoEAAAAAAAAAAAAAMsEAADMBAAAAAAAAAAAAADNBAAAzgQAAAAAAAAAAAAA0AQAANEEAAAAAAAAAAAAANIEAADTBAAAAAAAAAAAAADUBAAA1QQAAAAAAAAAAAAA1gQAANcEAAAAAAAAAAAAANgEAADZBAAAAAAAAAAAAADaBAAA2wQAAAAAAAAAAAAA3AQAAN0EAAAAAAAAAAAAAN4EAADfBAAAAAAAAAAAAADgBAAA4QQAAAAAAAAAAAAA4gQAAOMEAAAAAAAAAAAAAOQEAADlBAAAAAAAAAAAAADmBAAA5wQAAAAAAAAAAAAA6AQAAOkEAAAAAAAAAAAAAOoEAADrBAAAAAAAAAAAAADsBAAA7QQAAAAAAAAAAAAA7gQAAO8EAAAAAAAAAAAAAPAEAADxBAAAAAAAAAAAAADyBAAA8wQAAAAAAAAAAAAA9AQAAPUEAAAAAAAAAAAAAPYEAAD3BAAAAAAAAAAAAAD4BAAA+QQAAAAAAAAAAAAA+gQAAPsEAAAAAAAAAAAAAPwEAAD9BAAAAAAAAAAAAAD+BAAA/wQAAAAAAAAAAAAAAAUAAAEFAAAAAAAAAAAAAAIFAAADBQAAAAAAAAAAAAAEBQAABQUAAAAAAAAAAAAABgUAAAcFAAAAAAAAAAAAAAgFAAAJBQAAAAAAAAAAAAAKBQAACwUAAAAAAAAAAAAADAUAAA0FAAAAAAAAAAAAAA4FAAAPBQAAAAAAAAAAAAAQBQAAEQUAAAAAAAAAAAAAEgUAABMFAAAAAAAAAAAAABQFAAAVBQAAAAAAAAAAAAAWBQAAFwUAAAAAAAAAAAAAGAUAABkFAAAAAAAAAAAAABoFAAAbBQAAAAAAAAAAAAAcBQAAHQUAAAAAAAAAAAAAHgUAAB8FAAAAAAAAAAAAACAFAAAhBQAAAAAAAAAAAAAiBQAAIwUAAAAAAAAAAAAAJAUAACUFAAAAAAAAAAAAACYFAAAnBQAAAAAAAAAAAAAoBQAAKQUAAAAAAAAAAAAAKgUAACsFAAAAAAAAAAAAACwFAAAtBQAAAAAAAAAAAAAuBQAALwUAAAAAAAAAAAAAMQUAAGEFAAAAAAAAAAAAADIFAABiBQAAAAAAAAAAAAAzBQAAYwUAAAAAAAAAAAAANAUAAGQFAAAAAAAAAAAAADUFAABlBQAAAAAAAAAAAAA2BQAAZgUAAAAAAAAAAAAANwUAAGcFAAAAAAAAAAAAADgFAABoBQAAAAAAAAAAAAA5BQAAaQUAAAAAAAAAAAAAOgUAAGoFAAAAAAAAAAAAADsFAABrBQAAAAAAAAAAAAA8BQAAbAUAAAAAAAAAAAAAPQUAAG0FAAAAAAAAAAAAAD4FAABuBQAAAAAAAAAAAAA/BQAAbwUAAAAAAAAAAAAAQAUAAHAFAAAAAAAAAAAAAEEFAABxBQAAAAAAAAAAAABCBQAAcgUAAAAAAAAAAAAAQwUAAHMFAAAAAAAAAAAAAEQFAAB0BQAAAAAAAAAAAABFBQAAdQUAAAAAAAAAAAAARgUAAHYFAAAAAAAAAAAAAEcFAAB3BQAAAAAAAAAAAABIBQAAeAUAAAAAAAAAAAAASQUAAHkFAAAAAAAAAAAAAEoFAAB6BQAAAAAAAAAAAABLBQAAewUAAAAAAAAAAAAATAUAAHwFAAAAAAAAAAAAAE0FAAB9BQAAAAAAAAAAAABOBQAAfgUAAAAAAAAAAAAATwUAAH8FAAAAAAAAAAAAAFAFAACABQAAAAAAAAAAAABRBQAAgQUAAAAAAAAAAAAAUgUAAIIFAAAAAAAAAAAAAFMFAACDBQAAAAAAAAAAAABUBQAAhAUAAAAAAAAAAAAAVQUAAIUFAAAAAAAAAAAAAFYFAACGBQAAAAAAAAAAAACgEAAAAC0AAAAAAAAAAAAAoRAAAAEtAAAAAAAAAAAAAKIQAAACLQAAAAAAAAAAAACjEAAAAy0AAAAAAAAAAAAApBAAAAQtAAAAAAAAAAAAAKUQAAAFLQAAAAAAAAAAAACmEAAABi0AAAAAAAAAAAAApxAAAActAAAAAAAAAAAAAKgQAAAILQAAAAAAAAAAAACpEAAACS0AAAAAAAAAAAAAqhAAAAotAAAAAAAAAAAAAKsQAAALLQAAAAAAAAAAAACsEAAADC0AAAAAAAAAAAAArRAAAA0tAAAAAAAAAAAAAK4QAAAOLQAAAAAAAAAAAACvEAAADy0AAAAAAAAAAAAAsBAAABAtAAAAAAAAAAAAALEQAAARLQAAAAAAAAAAAACyEAAAEi0AAAAAAAAAAAAAsxAAABMtAAAAAAAAAAAAALQQAAAULQAAAAAAAAAAAAC1EAAAFS0AAAAAAAAAAAAAthAAABYtAAAAAAAAAAAAALcQAAAXLQAAAAAAAAAAAAC4EAAAGC0AAAAAAAAAAAAAuRAAABktAAAAAAAAAAAAALoQAAAaLQAAAAAAAAAAAAC7EAAAGy0AAAAAAAAAAAAAvBAAABwtAAAAAAAAAAAAAL0QAAAdLQAAAAAAAAAAAAC+EAAAHi0AAAAAAAAAAAAAvxAAAB8tAAAAAAAAAAAAAMAQAAAgLQAAAAAAAAAAAADBEAAAIS0AAAAAAAAAAAAAwhAAACItAAAAAAAAAAAAAMMQAAAjLQAAAAAAAAAAAADEEAAAJC0AAAAAAAAAAAAAxRAAACUtAAAAAAAAAAAAAMcQAAAnLQAAAAAAAAAAAADNEAAALS0AAAAAAAAAAAAAoBMAAHCrAAAAAAAAAAAAAKETAABxqwAAAAAAAAAAAACiEwAAcqsAAAAAAAAAAAAAoxMAAHOrAAAAAAAAAAAAAKQTAAB0qwAAAAAAAAAAAAClEwAAdasAAAAAAAAAAAAAphMAAHarAAAAAAAAAAAAAKcTAAB3qwAAAAAAAAAAAACoEwAAeKsAAAAAAAAAAAAAqRMAAHmrAAAAAAAAAAAAAKoTAAB6qwAAAAAAAAAAAACrEwAAe6sAAAAAAAAAAAAArBMAAHyrAAAAAAAAAAAAAK0TAAB9qwAAAAAAAAAAAACuEwAAfqsAAAAAAAAAAAAArxMAAH+rAAAAAAAAAAAAALATAACAqwAAAAAAAAAAAACxEwAAgasAAAAAAAAAAAAAshMAAIKrAAAAAAAAAAAAALMTAACDqwAAAAAAAAAAAAC0EwAAhKsAAAAAAAAAAAAAtRMAAIWrAAAAAAAAAAAAALYTAACGqwAAAAAAAAAAAAC3EwAAh6sAAAAAAAAAAAAAuBMAAIirAAAAAAAAAAAAALkTAACJqwAAAAAAAAAAAAC6EwAAiqsAAAAAAAAAAAAAuxMAAIurAAAAAAAAAAAAALwTAACMqwAAAAAAAAAAAAC9EwAAjasAAAAAAAAAAAAAvhMAAI6rAAAAAAAAAAAAAL8TAACPqwAAAAAAAAAAAADAEwAAkKsAAAAAAAAAAAAAwRMAAJGrAAAAAAAAAAAAAMITAACSqwAAAAAAAAAAAADDEwAAk6sAAAAAAAAAAAAAxBMAAJSrAAAAAAAAAAAAAMUTAACVqwAAAAAAAAAAAADGEwAAlqsAAAAAAAAAAAAAxxMAAJerAAAAAAAAAAAAAMgTAACYqwAAAAAAAAAAAADJEwAAmasAAAAAAAAAAAAAyhMAAJqrAAAAAAAAAAAAAMsTAACbqwAAAAAAAAAAAADMEwAAnKsAAAAAAAAAAAAAzRMAAJ2rAAAAAAAAAAAAAM4TAACeqwAAAAAAAAAAAADPEwAAn6sAAAAAAAAAAAAA0BMAAKCrAAAAAAAAAAAAANETAAChqwAAAAAAAAAAAADSEwAAoqsAAAAAAAAAAAAA0xMAAKOrAAAAAAAAAAAAANQTAACkqwAAAAAAAAAAAADVEwAApasAAAAAAAAAAAAA1hMAAKarAAAAAAAAAAAAANcTAACnqwAAAAAAAAAAAADYEwAAqKsAAAAAAAAAAAAA2RMAAKmrAAAAAAAAAAAAANoTAACqqwAAAAAAAAAAAADbEwAAq6sAAAAAAAAAAAAA3BMAAKyrAAAAAAAAAAAAAN0TAACtqwAAAAAAAAAAAADeEwAArqsAAAAAAAAAAAAA3xMAAK+rAAAAAAAAAAAAAOATAACwqwAAAAAAAAAAAADhEwAAsasAAAAAAAAAAAAA4hMAALKrAAAAAAAAAAAAAOMTAACzqwAAAAAAAAAAAADkEwAAtKsAAAAAAAAAAAAA5RMAALWrAAAAAAAAAAAAAOYTAAC2qwAAAAAAAAAAAADnEwAAt6sAAAAAAAAAAAAA6BMAALirAAAAAAAAAAAAAOkTAAC5qwAAAAAAAAAAAADqEwAAuqsAAAAAAAAAAAAA6xMAALurAAAAAAAAAAAAAOwTAAC8qwAAAAAAAAAAAADtEwAAvasAAAAAAAAAAAAA7hMAAL6rAAAAAAAAAAAAAO8TAAC/qwAAAAAAAAAAAADwEwAA+BMAAAAAAAAAAAAA8RMAAPkTAAAAAAAAAAAAAPITAAD6EwAAAAAAAAAAAADzEwAA+xMAAAAAAAAAAAAA9BMAAPwTAAAAAAAAAAAAAPUTAAD9EwAAAAAAAAAAAACQHAAA0BAAAAAAAAAAAAAAkRwAANEQAAAAAAAAAAAAAJIcAADSEAAAAAAAAAAAAACTHAAA0xAAAAAAAAAAAAAAlBwAANQQAAAAAAAAAAAAAJUcAADVEAAAAAAAAAAAAACWHAAA1hAAAAAAAAAAAAAAlxwAANcQAAAAAAAAAAAAAJgcAADYEAAAAAAAAAAAAACZHAAA2RAAAAAAAAAAAAAAmhwAANoQAAAAAAAAAAAAAJscAADbEAAAAAAAAAAAAACcHAAA3BAAAAAAAAAAAAAAnRwAAN0QAAAAAAAAAAAAAJ4cAADeEAAAAAAAAAAAAACfHAAA3xAAAAAAAAAAAAAAoBwAAOAQAAAAAAAAAAAAAKEcAADhEAAAAAAAAAAAAACiHAAA4hAAAAAAAAAAAAAAoxwAAOMQAAAAAAAAAAAAAKQcAADkEAAAAAAAAAAAAAClHAAA5RAAAAAAAAAAAAAAphwAAOYQAAAAAAAAAAAAAKccAADnEAAAAAAAAAAAAACoHAAA6BAAAAAAAAAAAAAAqRwAAOkQAAAAAAAAAAAAAKocAADqEAAAAAAAAAAAAACrHAAA6xAAAAAAAAAAAAAArBwAAOwQAAAAAAAAAAAAAK0cAADtEAAAAAAAAAAAAACuHAAA7hAAAAAAAAAAAAAArxwAAO8QAAAAAAAAAAAAALAcAADwEAAAAAAAAAAAAACxHAAA8RAAAAAAAAAAAAAAshwAAPIQAAAAAAAAAAAAALMcAADzEAAAAAAAAAAAAAC0HAAA9BAAAAAAAAAAAAAAtRwAAPUQAAAAAAAAAAAAALYcAAD2EAAAAAAAAAAAAAC3HAAA9xAAAAAAAAAAAAAAuBwAAPgQAAAAAAAAAAAAALkcAAD5EAAAAAAAAAAAAAC6HAAA+hAAAAAAAAAAAAAAvRwAAP0QAAAAAAAAAAAAAL4cAAD+EAAAAAAAAAAAAAC/HAAA/xAAAAAAAAAAAAAAAB4AAAEeAAAAAAAAAAAAAAIeAAADHgAAAAAAAAAAAAAEHgAABR4AAAAAAAAAAAAABh4AAAceAAAAAAAAAAAAAAgeAAAJHgAAAAAAAAAAAAAKHgAACx4AAAAAAAAAAAAADB4AAA0eAAAAAAAAAAAAAA4eAAAPHgAAAAAAAAAAAAAQHgAAER4AAAAAAAAAAAAAEh4AABMeAAAAAAAAAAAAABQeAAAVHgAAAAAAAAAAAAAWHgAAFx4AAAAAAAAAAAAAGB4AABkeAAAAAAAAAAAAABoeAAAbHgAAAAAAAAAAAAAcHgAAHR4AAAAAAAAAAAAAHh4AAB8eAAAAAAAAAAAAACAeAAAhHgAAAAAAAAAAAAAiHgAAIx4AAAAAAAAAAAAAJB4AACUeAAAAAAAAAAAAACYeAAAnHgAAAAAAAAAAAAAoHgAAKR4AAAAAAAAAAAAAKh4AACseAAAAAAAAAAAAACweAAAtHgAAAAAAAAAAAAAuHgAALx4AAAAAAAAAAAAAMB4AADEeAAAAAAAAAAAAADIeAAAzHgAAAAAAAAAAAAA0HgAANR4AAAAAAAAAAAAANh4AADceAAAAAAAAAAAAADgeAAA5HgAAAAAAAAAAAAA6HgAAOx4AAAAAAAAAAAAAPB4AAD0eAAAAAAAAAAAAAD4eAAA/HgAAAAAAAAAAAABAHgAAQR4AAAAAAAAAAAAAQh4AAEMeAAAAAAAAAAAAAEQeAABFHgAAAAAAAAAAAABGHgAARx4AAAAAAAAAAAAASB4AAEkeAAAAAAAAAAAAAEoeAABLHgAAAAAAAAAAAABMHgAATR4AAAAAAAAAAAAATh4AAE8eAAAAAAAAAAAAAFAeAABRHgAAAAAAAAAAAABSHgAAUx4AAAAAAAAAAAAAVB4AAFUeAAAAAAAAAAAAAFYeAABXHgAAAAAAAAAAAABYHgAAWR4AAAAAAAAAAAAAWh4AAFseAAAAAAAAAAAAAFweAABdHgAAAAAAAAAAAABeHgAAXx4AAAAAAAAAAAAAYB4AAGEeAAAAAAAAAAAAAGIeAABjHgAAAAAAAAAAAABkHgAAZR4AAAAAAAAAAAAAZh4AAGceAAAAAAAAAAAAAGgeAABpHgAAAAAAAAAAAABqHgAAax4AAAAAAAAAAAAAbB4AAG0eAAAAAAAAAAAAAG4eAABvHgAAAAAAAAAAAABwHgAAcR4AAAAAAAAAAAAAch4AAHMeAAAAAAAAAAAAAHQeAAB1HgAAAAAAAAAAAAB2HgAAdx4AAAAAAAAAAAAAeB4AAHkeAAAAAAAAAAAAAHoeAAB7HgAAAAAAAAAAAAB8HgAAfR4AAAAAAAAAAAAAfh4AAH8eAAAAAAAAAAAAAIAeAACBHgAAAAAAAAAAAACCHgAAgx4AAAAAAAAAAAAAhB4AAIUeAAAAAAAAAAAAAIYeAACHHgAAAAAAAAAAAACIHgAAiR4AAAAAAAAAAAAAih4AAIseAAAAAAAAAAAAAIweAACNHgAAAAAAAAAAAACOHgAAjx4AAAAAAAAAAAAAkB4AAJEeAAAAAAAAAAAAAJIeAACTHgAAAAAAAAAAAACUHgAAlR4AAAAAAAAAAAAAnh4AAN8AAAAAAAAAAAAAAKAeAAChHgAAAAAAAAAAAACiHgAAox4AAAAAAAAAAAAApB4AAKUeAAAAAAAAAAAAAKYeAACnHgAAAAAAAAAAAACoHgAAqR4AAAAAAAAAAAAAqh4AAKseAAAAAAAAAAAAAKweAACtHgAAAAAAAAAAAACuHgAArx4AAAAAAAAAAAAAsB4AALEeAAAAAAAAAAAAALIeAACzHgAAAAAAAAAAAAC0HgAAtR4AAAAAAAAAAAAAth4AALceAAAAAAAAAAAAALgeAAC5HgAAAAAAAAAAAAC6HgAAux4AAAAAAAAAAAAAvB4AAL0eAAAAAAAAAAAAAL4eAAC/HgAAAAAAAAAAAADAHgAAwR4AAAAAAAAAAAAAwh4AAMMeAAAAAAAAAAAAAMQeAADFHgAAAAAAAAAAAADGHgAAxx4AAAAAAAAAAAAAyB4AAMkeAAAAAAAAAAAAAMoeAADLHgAAAAAAAAAAAADMHgAAzR4AAAAAAAAAAAAAzh4AAM8eAAAAAAAAAAAAANAeAADRHgAAAAAAAAAAAADSHgAA0x4AAAAAAAAAAAAA1B4AANUeAAAAAAAAAAAAANYeAADXHgAAAAAAAAAAAADYHgAA2R4AAAAAAAAAAAAA2h4AANseAAAAAAAAAAAAANweAADdHgAAAAAAAAAAAADeHgAA3x4AAAAAAAAAAAAA4B4AAOEeAAAAAAAAAAAAAOIeAADjHgAAAAAAAAAAAADkHgAA5R4AAAAAAAAAAAAA5h4AAOceAAAAAAAAAAAAAOgeAADpHgAAAAAAAAAAAADqHgAA6x4AAAAAAAAAAAAA7B4AAO0eAAAAAAAAAAAAAO4eAADvHgAAAAAAAAAAAADwHgAA8R4AAAAAAAAAAAAA8h4AAPMeAAAAAAAAAAAAAPQeAAD1HgAAAAAAAAAAAAD2HgAA9x4AAAAAAAAAAAAA+B4AAPkeAAAAAAAAAAAAAPoeAAD7HgAAAAAAAAAAAAD8HgAA/R4AAAAAAAAAAAAA/h4AAP8eAAAAAAAAAAAAAAgfAAAAHwAAAAAAAAAAAAAJHwAAAR8AAAAAAAAAAAAACh8AAAIfAAAAAAAAAAAAAAsfAAADHwAAAAAAAAAAAAAMHwAABB8AAAAAAAAAAAAADR8AAAUfAAAAAAAAAAAAAA4fAAAGHwAAAAAAAAAAAAAPHwAABx8AAAAAAAAAAAAAGB8AABAfAAAAAAAAAAAAABkfAAARHwAAAAAAAAAAAAAaHwAAEh8AAAAAAAAAAAAAGx8AABMfAAAAAAAAAAAAABwfAAAUHwAAAAAAAAAAAAAdHwAAFR8AAAAAAAAAAAAAKB8AACAfAAAAAAAAAAAAACkfAAAhHwAAAAAAAAAAAAAqHwAAIh8AAAAAAAAAAAAAKx8AACMfAAAAAAAAAAAAACwfAAAkHwAAAAAAAAAAAAAtHwAAJR8AAAAAAAAAAAAALh8AACYfAAAAAAAAAAAAAC8fAAAnHwAAAAAAAAAAAAA4HwAAMB8AAAAAAAAAAAAAOR8AADEfAAAAAAAAAAAAADofAAAyHwAAAAAAAAAAAAA7HwAAMx8AAAAAAAAAAAAAPB8AADQfAAAAAAAAAAAAAD0fAAA1HwAAAAAAAAAAAAA+HwAANh8AAAAAAAAAAAAAPx8AADcfAAAAAAAAAAAAAEgfAABAHwAAAAAAAAAAAABJHwAAQR8AAAAAAAAAAAAASh8AAEIfAAAAAAAAAAAAAEsfAABDHwAAAAAAAAAAAABMHwAARB8AAAAAAAAAAAAATR8AAEUfAAAAAAAAAAAAAFkfAABRHwAAAAAAAAAAAABbHwAAUx8AAAAAAAAAAAAAXR8AAFUfAAAAAAAAAAAAAF8fAABXHwAAAAAAAAAAAABoHwAAYB8AAAAAAAAAAAAAaR8AAGEfAAAAAAAAAAAAAGofAABiHwAAAAAAAAAAAABrHwAAYx8AAAAAAAAAAAAAbB8AAGQfAAAAAAAAAAAAAG0fAABlHwAAAAAAAAAAAABuHwAAZh8AAAAAAAAAAAAAbx8AAGcfAAAAAAAAAAAAAIgfAACAHwAAAAAAAAAAAACJHwAAgR8AAAAAAAAAAAAAih8AAIIfAAAAAAAAAAAAAIsfAACDHwAAAAAAAAAAAACMHwAAhB8AAAAAAAAAAAAAjR8AAIUfAAAAAAAAAAAAAI4fAACGHwAAAAAAAAAAAACPHwAAhx8AAAAAAAAAAAAAmB8AAJAfAAAAAAAAAAAAAJkfAACRHwAAAAAAAAAAAACaHwAAkh8AAAAAAAAAAAAAmx8AAJMfAAAAAAAAAAAAAJwfAACUHwAAAAAAAAAAAACdHwAAlR8AAAAAAAAAAAAAnh8AAJYfAAAAAAAAAAAAAJ8fAACXHwAAAAAAAAAAAACoHwAAoB8AAAAAAAAAAAAAqR8AAKEfAAAAAAAAAAAAAKofAACiHwAAAAAAAAAAAACrHwAAox8AAAAAAAAAAAAArB8AAKQfAAAAAAAAAAAAAK0fAAClHwAAAAAAAAAAAACuHwAAph8AAAAAAAAAAAAArx8AAKcfAAAAAAAAAAAAALgfAACwHwAAAAAAAAAAAAC5HwAAsR8AAAAAAAAAAAAAuh8AAHAfAAAAAAAAAAAAALsfAABxHwAAAAAAAAAAAAC8HwAAsx8AAAAAAAAAAAAAyB8AAHIfAAAAAAAAAAAAAMkfAABzHwAAAAAAAAAAAADKHwAAdB8AAAAAAAAAAAAAyx8AAHUfAAAAAAAAAAAAAMwfAADDHwAAAAAAAAAAAADYHwAA0B8AAAAAAAAAAAAA2R8AANEfAAAAAAAAAAAAANofAAB2HwAAAAAAAAAAAADbHwAAdx8AAAAAAAAAAAAA6B8AAOAfAAAAAAAAAAAAAOkfAADhHwAAAAAAAAAAAADqHwAAeh8AAAAAAAAAAAAA6x8AAHsfAAAAAAAAAAAAAOwfAADlHwAAAAAAAAAAAAD4HwAAeB8AAAAAAAAAAAAA+R8AAHkfAAAAAAAAAAAAAPofAAB8HwAAAAAAAAAAAAD7HwAAfR8AAAAAAAAAAAAA/B8AAPMfAAAAAAAAAAAAACYhAADJAwAAAAAAAAAAAAAqIQAAawAAAAAAAAAAAAAAKyEAAOUAAAAAAAAAAAAAADIhAABOIQAAAAAAAAAAAABgIQAAcCEAAAAAAAAAAAAAYSEAAHEhAAAAAAAAAAAAAGIhAAByIQAAAAAAAAAAAABjIQAAcyEAAAAAAAAAAAAAZCEAAHQhAAAAAAAAAAAAAGUhAAB1IQAAAAAAAAAAAABmIQAAdiEAAAAAAAAAAAAAZyEAAHchAAAAAAAAAAAAAGghAAB4IQAAAAAAAAAAAABpIQAAeSEAAAAAAAAAAAAAaiEAAHohAAAAAAAAAAAAAGshAAB7IQAAAAAAAAAAAABsIQAAfCEAAAAAAAAAAAAAbSEAAH0hAAAAAAAAAAAAAG4hAAB+IQAAAAAAAAAAAABvIQAAfyEAAAAAAAAAAAAAgyEAAIQhAAAAAAAAAAAAALYkAADQJAAAAAAAAAAAAAC3JAAA0SQAAAAAAAAAAAAAuCQAANIkAAAAAAAAAAAAALkkAADTJAAAAAAAAAAAAAC6JAAA1CQAAAAAAAAAAAAAuyQAANUkAAAAAAAAAAAAALwkAADWJAAAAAAAAAAAAAC9JAAA1yQAAAAAAAAAAAAAviQAANgkAAAAAAAAAAAAAL8kAADZJAAAAAAAAAAAAADAJAAA2iQAAAAAAAAAAAAAwSQAANskAAAAAAAAAAAAAMIkAADcJAAAAAAAAAAAAADDJAAA3SQAAAAAAAAAAAAAxCQAAN4kAAAAAAAAAAAAAMUkAADfJAAAAAAAAAAAAADGJAAA4CQAAAAAAAAAAAAAxyQAAOEkAAAAAAAAAAAAAMgkAADiJAAAAAAAAAAAAADJJAAA4yQAAAAAAAAAAAAAyiQAAOQkAAAAAAAAAAAAAMskAADlJAAAAAAAAAAAAADMJAAA5iQAAAAAAAAAAAAAzSQAAOckAAAAAAAAAAAAAM4kAADoJAAAAAAAAAAAAADPJAAA6SQAAAAAAAAAAAAAACwAADAsAAAAAAAAAAAAAAEsAAAxLAAAAAAAAAAAAAACLAAAMiwAAAAAAAAAAAAAAywAADMsAAAAAAAAAAAAAAQsAAA0LAAAAAAAAAAAAAAFLAAANSwAAAAAAAAAAAAABiwAADYsAAAAAAAAAAAAAAcsAAA3LAAAAAAAAAAAAAAILAAAOCwAAAAAAAAAAAAACSwAADksAAAAAAAAAAAAAAosAAA6LAAAAAAAAAAAAAALLAAAOywAAAAAAAAAAAAADCwAADwsAAAAAAAAAAAAAA0sAAA9LAAAAAAAAAAAAAAOLAAAPiwAAAAAAAAAAAAADywAAD8sAAAAAAAAAAAAABAsAABALAAAAAAAAAAAAAARLAAAQSwAAAAAAAAAAAAAEiwAAEIsAAAAAAAAAAAAABMsAABDLAAAAAAAAAAAAAAULAAARCwAAAAAAAAAAAAAFSwAAEUsAAAAAAAAAAAAABYsAABGLAAAAAAAAAAAAAAXLAAARywAAAAAAAAAAAAAGCwAAEgsAAAAAAAAAAAAABksAABJLAAAAAAAAAAAAAAaLAAASiwAAAAAAAAAAAAAGywAAEssAAAAAAAAAAAAABwsAABMLAAAAAAAAAAAAAAdLAAATSwAAAAAAAAAAAAAHiwAAE4sAAAAAAAAAAAAAB8sAABPLAAAAAAAAAAAAAAgLAAAUCwAAAAAAAAAAAAAISwAAFEsAAAAAAAAAAAAACIsAABSLAAAAAAAAAAAAAAjLAAAUywAAAAAAAAAAAAAJCwAAFQsAAAAAAAAAAAAACUsAABVLAAAAAAAAAAAAAAmLAAAViwAAAAAAAAAAAAAJywAAFcsAAAAAAAAAAAAACgsAABYLAAAAAAAAAAAAAApLAAAWSwAAAAAAAAAAAAAKiwAAFosAAAAAAAAAAAAACssAABbLAAAAAAAAAAAAAAsLAAAXCwAAAAAAAAAAAAALSwAAF0sAAAAAAAAAAAAAC4sAABeLAAAAAAAAAAAAABgLAAAYSwAAAAAAAAAAAAAYiwAAGsCAAAAAAAAAAAAAGMsAAB9HQAAAAAAAAAAAABkLAAAfQIAAAAAAAAAAAAAZywAAGgsAAAAAAAAAAAAAGksAABqLAAAAAAAAAAAAABrLAAAbCwAAAAAAAAAAAAAbSwAAFECAAAAAAAAAAAAAG4sAABxAgAAAAAAAAAAAABvLAAAUAIAAAAAAAAAAAAAcCwAAFICAAAAAAAAAAAAAHIsAABzLAAAAAAAAAAAAAB1LAAAdiwAAAAAAAAAAAAAfiwAAD8CAAAAAAAAAAAAAH8sAABAAgAAAAAAAAAAAACALAAAgSwAAAAAAAAAAAAAgiwAAIMsAAAAAAAAAAAAAIQsAACFLAAAAAAAAAAAAACGLAAAhywAAAAAAAAAAAAAiCwAAIksAAAAAAAAAAAAAIosAACLLAAAAAAAAAAAAACMLAAAjSwAAAAAAAAAAAAAjiwAAI8sAAAAAAAAAAAAAJAsAACRLAAAAAAAAAAAAACSLAAAkywAAAAAAAAAAAAAlCwAAJUsAAAAAAAAAAAAAJYsAACXLAAAAAAAAAAAAACYLAAAmSwAAAAAAAAAAAAAmiwAAJssAAAAAAAAAAAAAJwsAACdLAAAAAAAAAAAAACeLAAAnywAAAAAAAAAAAAAoCwAAKEsAAAAAAAAAAAAAKIsAACjLAAAAAAAAAAAAACkLAAApSwAAAAAAAAAAAAApiwAAKcsAAAAAAAAAAAAAKgsAACpLAAAAAAAAAAAAACqLAAAqywAAAAAAAAAAAAArCwAAK0sAAAAAAAAAAAAAK4sAACvLAAAAAAAAAAAAACwLAAAsSwAAAAAAAAAAAAAsiwAALMsAAAAAAAAAAAAALQsAAC1LAAAAAAAAAAAAAC2LAAAtywAAAAAAAAAAAAAuCwAALksAAAAAAAAAAAAALosAAC7LAAAAAAAAAAAAAC8LAAAvSwAAAAAAAAAAAAAviwAAL8sAAAAAAAAAAAAAMAsAADBLAAAAAAAAAAAAADCLAAAwywAAAAAAAAAAAAAxCwAAMUsAAAAAAAAAAAAAMYsAADHLAAAAAAAAAAAAADILAAAySwAAAAAAAAAAAAAyiwAAMssAAAAAAAAAAAAAMwsAADNLAAAAAAAAAAAAADOLAAAzywAAAAAAAAAAAAA0CwAANEsAAAAAAAAAAAAANIsAADTLAAAAAAAAAAAAADULAAA1SwAAAAAAAAAAAAA1iwAANcsAAAAAAAAAAAAANgsAADZLAAAAAAAAAAAAADaLAAA2ywAAAAAAAAAAAAA3CwAAN0sAAAAAAAAAAAAAN4sAADfLAAAAAAAAAAAAADgLAAA4SwAAAAAAAAAAAAA4iwAAOMsAAAAAAAAAAAAAOssAADsLAAAAAAAAAAAAADtLAAA7iwAAAAAAAAAAAAA8iwAAPMsAAAAAAAAAAAAAECmAABBpgAAAAAAAAAAAABCpgAAQ6YAAAAAAAAAAAAARKYAAEWmAAAAAAAAAAAAAEamAABHpgAAAAAAAAAAAABIpgAASaYAAAAAAAAAAAAASqYAAEumAAAAAAAAAAAAAEymAABNpgAAAAAAAAAAAABOpgAAT6YAAAAAAAAAAAAAUKYAAFGmAAAAAAAAAAAAAFKmAABTpgAAAAAAAAAAAABUpgAAVaYAAAAAAAAAAAAAVqYAAFemAAAAAAAAAAAAAFimAABZpgAAAAAAAAAAAABapgAAW6YAAAAAAAAAAAAAXKYAAF2mAAAAAAAAAAAAAF6mAABfpgAAAAAAAAAAAABgpgAAYaYAAAAAAAAAAAAAYqYAAGOmAAAAAAAAAAAAAGSmAABlpgAAAAAAAAAAAABmpgAAZ6YAAAAAAAAAAAAAaKYAAGmmAAAAAAAAAAAAAGqmAABrpgAAAAAAAAAAAABspgAAbaYAAAAAAAAAAAAAgKYAAIGmAAAAAAAAAAAAAIKmAACDpgAAAAAAAAAAAACEpgAAhaYAAAAAAAAAAAAAhqYAAIemAAAAAAAAAAAAAIimAACJpgAAAAAAAAAAAACKpgAAi6YAAAAAAAAAAAAAjKYAAI2mAAAAAAAAAAAAAI6mAACPpgAAAAAAAAAAAACQpgAAkaYAAAAAAAAAAAAAkqYAAJOmAAAAAAAAAAAAAJSmAACVpgAAAAAAAAAAAACWpgAAl6YAAAAAAAAAAAAAmKYAAJmmAAAAAAAAAAAAAJqmAACbpgAAAAAAAAAAAAAipwAAI6cAAAAAAAAAAAAAJKcAACWnAAAAAAAAAAAAACanAAAnpwAAAAAAAAAAAAAopwAAKacAAAAAAAAAAAAAKqcAACunAAAAAAAAAAAAACynAAAtpwAAAAAAAAAAAAAupwAAL6cAAAAAAAAAAAAAMqcAADOnAAAAAAAAAAAAADSnAAA1pwAAAAAAAAAAAAA2pwAAN6cAAAAAAAAAAAAAOKcAADmnAAAAAAAAAAAAADqnAAA7pwAAAAAAAAAAAAA8pwAAPacAAAAAAAAAAAAAPqcAAD+nAAAAAAAAAAAAAECnAABBpwAAAAAAAAAAAABCpwAAQ6cAAAAAAAAAAAAARKcAAEWnAAAAAAAAAAAAAEanAABHpwAAAAAAAAAAAABIpwAASacAAAAAAAAAAAAASqcAAEunAAAAAAAAAAAAAEynAABNpwAAAAAAAAAAAABOpwAAT6cAAAAAAAAAAAAAUKcAAFGnAAAAAAAAAAAAAFKnAABTpwAAAAAAAAAAAABUpwAAVacAAAAAAAAAAAAAVqcAAFenAAAAAAAAAAAAAFinAABZpwAAAAAAAAAAAABapwAAW6cAAAAAAAAAAAAAXKcAAF2nAAAAAAAAAAAAAF6nAABfpwAAAAAAAAAAAABgpwAAYacAAAAAAAAAAAAAYqcAAGOnAAAAAAAAAAAAAGSnAABlpwAAAAAAAAAAAABmpwAAZ6cAAAAAAAAAAAAAaKcAAGmnAAAAAAAAAAAAAGqnAABrpwAAAAAAAAAAAABspwAAbacAAAAAAAAAAAAAbqcAAG+nAAAAAAAAAAAAAHmnAAB6pwAAAAAAAAAAAAB7pwAAfKcAAAAAAAAAAAAAfacAAHkdAAAAAAAAAAAAAH6nAAB/pwAAAAAAAAAAAACApwAAgacAAAAAAAAAAAAAgqcAAIOnAAAAAAAAAAAAAISnAACFpwAAAAAAAAAAAACGpwAAh6cAAAAAAAAAAAAAi6cAAIynAAAAAAAAAAAAAI2nAABlAgAAAAAAAAAAAACQpwAAkacAAAAAAAAAAAAAkqcAAJOnAAAAAAAAAAAAAJanAACXpwAAAAAAAAAAAACYpwAAmacAAAAAAAAAAAAAmqcAAJunAAAAAAAAAAAAAJynAACdpwAAAAAAAAAAAACepwAAn6cAAAAAAAAAAAAAoKcAAKGnAAAAAAAAAAAAAKKnAACjpwAAAAAAAAAAAACkpwAApacAAAAAAAAAAAAApqcAAKenAAAAAAAAAAAAAKinAACppwAAAAAAAAAAAACqpwAAZgIAAAAAAAAAAAAAq6cAAFwCAAAAAAAAAAAAAKynAABhAgAAAAAAAAAAAACtpwAAbAIAAAAAAAAAAAAArqcAAGoCAAAAAAAAAAAAALCnAACeAgAAAAAAAAAAAACxpwAAhwIAAAAAAAAAAAAAsqcAAJ0CAAAAAAAAAAAAALOnAABTqwAAAAAAAAAAAAC0pwAAtacAAAAAAAAAAAAAtqcAALenAAAAAAAAAAAAALinAAC5pwAAAAAAAAAAAAC6pwAAu6cAAAAAAAAAAAAAvKcAAL2nAAAAAAAAAAAAAL6nAAC/pwAAAAAAAAAAAADCpwAAw6cAAAAAAAAAAAAAxKcAAJSnAAAAAAAAAAAAAMWnAACCAgAAAAAAAAAAAADGpwAAjh0AAAAAAAAAAAAAIf8AAEH/AAAAAAAAAAAAACL/AABC/wAAAAAAAAAAAAAj/wAAQ/8AAAAAAAAAAAAAJP8AAET/AAAAAAAAAAAAACX/AABF/wAAAAAAAAAAAAAm/wAARv8AAAAAAAAAAAAAJ/8AAEf/AAAAAAAAAAAAACj/AABI/wAAAAAAAAAAAAAp/wAASf8AAAAAAAAAAAAAKv8AAEr/AAAAAAAAAAAAACv/AABL/wAAAAAAAAAAAAAs/wAATP8AAAAAAAAAAAAALf8AAE3/AAAAAAAAAAAAAC7/AABO/wAAAAAAAAAAAAAv/wAAT/8AAAAAAAAAAAAAMP8AAFD/AAAAAAAAAAAAADH/AABR/wAAAAAAAAAAAAAy/wAAUv8AAAAAAAAAAAAAM/8AAFP/AAAAAAAAAAAAADT/AABU/wAAAAAAAAAAAAA1/wAAVf8AAAAAAAAAAAAANv8AAFb/AAAAAAAAAAAAADf/AABX/wAAAAAAAAAAAAA4/wAAWP8AAAAAAAAAAAAAOf8AAFn/AAAAAAAAAAAAADr/AABa/wAAAAAAAAAAAAAABAEAKAQBAAAAAAAAAAAAAQQBACkEAQAAAAAAAAAAAAIEAQAqBAEAAAAAAAAAAAADBAEAKwQBAAAAAAAAAAAABAQBACwEAQAAAAAAAAAAAAUEAQAtBAEAAAAAAAAAAAAGBAEALgQBAAAAAAAAAAAABwQBAC8EAQAAAAAAAAAAAAgEAQAwBAEAAAAAAAAAAAAJBAEAMQQBAAAAAAAAAAAACgQBADIEAQAAAAAAAAAAAAsEAQAzBAEAAAAAAAAAAAAMBAEANAQBAAAAAAAAAAAADQQBADUEAQAAAAAAAAAAAA4EAQA2BAEAAAAAAAAAAAAPBAEANwQBAAAAAAAAAAAAEAQBADgEAQAAAAAAAAAAABEEAQA5BAEAAAAAAAAAAAASBAEAOgQBAAAAAAAAAAAAEwQBADsEAQAAAAAAAAAAABQEAQA8BAEAAAAAAAAAAAAVBAEAPQQBAAAAAAAAAAAAFgQBAD4EAQAAAAAAAAAAABcEAQA/BAEAAAAAAAAAAAAYBAEAQAQBAAAAAAAAAAAAGQQBAEEEAQAAAAAAAAAAABoEAQBCBAEAAAAAAAAAAAAbBAEAQwQBAAAAAAAAAAAAHAQBAEQEAQAAAAAAAAAAAB0EAQBFBAEAAAAAAAAAAAAeBAEARgQBAAAAAAAAAAAAHwQBAEcEAQAAAAAAAAAAACAEAQBIBAEAAAAAAAAAAAAhBAEASQQBAAAAAAAAAAAAIgQBAEoEAQAAAAAAAAAAACMEAQBLBAEAAAAAAAAAAAAkBAEATAQBAAAAAAAAAAAAJQQBAE0EAQAAAAAAAAAAACYEAQBOBAEAAAAAAAAAAAAnBAEATwQBAAAAAAAAAAAAsAQBANgEAQAAAAAAAAAAALEEAQDZBAEAAAAAAAAAAACyBAEA2gQBAAAAAAAAAAAAswQBANsEAQAAAAAAAAAAALQEAQDcBAEAAAAAAAAAAAC1BAEA3QQBAAAAAAAAAAAAtgQBAN4EAQAAAAAAAAAAALcEAQDfBAEAAAAAAAAAAAC4BAEA4AQBAAAAAAAAAAAAuQQBAOEEAQAAAAAAAAAAALoEAQDiBAEAAAAAAAAAAAC7BAEA4wQBAAAAAAAAAAAAvAQBAOQEAQAAAAAAAAAAAL0EAQDlBAEAAAAAAAAAAAC+BAEA5gQBAAAAAAAAAAAAvwQBAOcEAQAAAAAAAAAAAMAEAQDoBAEAAAAAAAAAAADBBAEA6QQBAAAAAAAAAAAAwgQBAOoEAQAAAAAAAAAAAMMEAQDrBAEAAAAAAAAAAADEBAEA7AQBAAAAAAAAAAAAxQQBAO0EAQAAAAAAAAAAAMYEAQDuBAEAAAAAAAAAAADHBAEA7wQBAAAAAAAAAAAAyAQBAPAEAQAAAAAAAAAAAMkEAQDxBAEAAAAAAAAAAADKBAEA8gQBAAAAAAAAAAAAywQBAPMEAQAAAAAAAAAAAMwEAQD0BAEAAAAAAAAAAADNBAEA9QQBAAAAAAAAAAAAzgQBAPYEAQAAAAAAAAAAAM8EAQD3BAEAAAAAAAAAAADQBAEA+AQBAAAAAAAAAAAA0QQBAPkEAQAAAAAAAAAAANIEAQD6BAEAAAAAAAAAAADTBAEA+wQBAAAAAAAAAAAAgAwBAMAMAQAAAAAAAAAAAIEMAQDBDAEAAAAAAAAAAACCDAEAwgwBAAAAAAAAAAAAgwwBAMMMAQAAAAAAAAAAAIQMAQDEDAEAAAAAAAAAAACFDAEAxQwBAAAAAAAAAAAAhgwBAMYMAQAAAAAAAAAAAIcMAQDHDAEAAAAAAAAAAACIDAEAyAwBAAAAAAAAAAAAiQwBAMkMAQAAAAAAAAAAAIoMAQDKDAEAAAAAAAAAAACLDAEAywwBAAAAAAAAAAAAjAwBAMwMAQAAAAAAAAAAAI0MAQDNDAEAAAAAAAAAAACODAEAzgwBAAAAAAAAAAAAjwwBAM8MAQAAAAAAAAAAAJAMAQDQDAEAAAAAAAAAAACRDAEA0QwBAAAAAAAAAAAAkgwBANIMAQAAAAAAAAAAAJMMAQDTDAEAAAAAAAAAAACUDAEA1AwBAAAAAAAAAAAAlQwBANUMAQAAAAAAAAAAAJYMAQDWDAEAAAAAAAAAAACXDAEA1wwBAAAAAAAAAAAAmAwBANgMAQAAAAAAAAAAAJkMAQDZDAEAAAAAAAAAAACaDAEA2gwBAAAAAAAAAAAAmwwBANsMAQAAAAAAAAAAAJwMAQDcDAEAAAAAAAAAAACdDAEA3QwBAAAAAAAAAAAAngwBAN4MAQAAAAAAAAAAAJ8MAQDfDAEAAAAAAAAAAACgDAEA4AwBAAAAAAAAAAAAoQwBAOEMAQAAAAAAAAAAAKIMAQDiDAEAAAAAAAAAAACjDAEA4wwBAAAAAAAAAAAApAwBAOQMAQAAAAAAAAAAAKUMAQDlDAEAAAAAAAAAAACmDAEA5gwBAAAAAAAAAAAApwwBAOcMAQAAAAAAAAAAAKgMAQDoDAEAAAAAAAAAAACpDAEA6QwBAAAAAAAAAAAAqgwBAOoMAQAAAAAAAAAAAKsMAQDrDAEAAAAAAAAAAACsDAEA7AwBAAAAAAAAAAAArQwBAO0MAQAAAAAAAAAAAK4MAQDuDAEAAAAAAAAAAACvDAEA7wwBAAAAAAAAAAAAsAwBAPAMAQAAAAAAAAAAALEMAQDxDAEAAAAAAAAAAACyDAEA8gwBAAAAAAAAAAAAoBgBAMAYAQAAAAAAAAAAAKEYAQDBGAEAAAAAAAAAAACiGAEAwhgBAAAAAAAAAAAAoxgBAMMYAQAAAAAAAAAAAKQYAQDEGAEAAAAAAAAAAAClGAEAxRgBAAAAAAAAAAAAphgBAMYYAQAAAAAAAAAAAKcYAQDHGAEAAAAAAAAAAACoGAEAyBgBAAAAAAAAAAAAqRgBAMkYAQAAAAAAAAAAAKoYAQDKGAEAAAAAAAAAAACrGAEAyxgBAAAAAAAAAAAArBgBAMwYAQAAAAAAAAAAAK0YAQDNGAEAAAAAAAAAAACuGAEAzhgBAAAAAAAAAAAArxgBAM8YAQAAAAAAAAAAALAYAQDQGAEAAAAAAAAAAACxGAEA0RgBAAAAAAAAAAAAshgBANIYAQAAAAAAAAAAALMYAQDTGAEAAAAAAAAAAAC0GAEA1BgBAAAAAAAAAAAAtRgBANUYAQAAAAAAAAAAALYYAQDWGAEAAAAAAAAAAAC3GAEA1xgBAAAAAAAAAAAAuBgBANgYAQAAAAAAAAAAALkYAQDZGAEAAAAAAAAAAAC6GAEA2hgBAAAAAAAAAAAAuxgBANsYAQAAAAAAAAAAALwYAQDcGAEAAAAAAAAAAAC9GAEA3RgBAAAAAAAAAAAAvhgBAN4YAQAAAAAAAAAAAL8YAQDfGAEAAAAAAAAAAABAbgEAYG4BAAAAAAAAAAAAQW4BAGFuAQAAAAAAAAAAAEJuAQBibgEAAAAAAAAAAABDbgEAY24BAAAAAAAAAAAARG4BAGRuAQAAAAAAAAAAAEVuAQBlbgEAAAAAAAAAAABGbgEAZm4BAAAAAAAAAAAAR24BAGduAQAAAAAAAAAAAEhuAQBobgEAAAAAAAAAAABJbgEAaW4BAAAAAAAAAAAASm4BAGpuAQAAAAAAAAAAAEtuAQBrbgEAAAAAAAAAAABMbgEAbG4BAAAAAAAAAAAATW4BAG1uAQAAAAAAAAAAAE5uAQBubgEAAAAAAAAAAABPbgEAb24BAAAAAAAAAAAAUG4BAHBuAQAAAAAAAAAAAFFuAQBxbgEAAAAAAAAAAABSbgEAcm4BAAAAAAAAAAAAU24BAHNuAQAAAAAAAAAAAFRuAQB0bgEAAAAAAAAAAABVbgEAdW4BAAAAAAAAAAAAVm4BAHZuAQAAAAAAAAAAAFduAQB3bgEAAAAAAAAAAABYbgEAeG4BAAAAAAAAAAAAWW4BAHluAQAAAAAAAAAAAFpuAQB6bgEAAAAAAAAAAABbbgEAe24BAAAAAAAAAAAAXG4BAHxuAQAAAAAAAAAAAF1uAQB9bgEAAAAAAAAAAABebgEAfm4BAAAAAAAAAAAAX24BAH9uAQAAAAAAAAAAAADpAQAi6QEAAAAAAAAAAAAB6QEAI+kBAAAAAAAAAAAAAukBACTpAQAAAAAAAAAAAAPpAQAl6QEAAAAAAAAAAAAE6QEAJukBAAAAAAAAAAAABekBACfpAQAAAAAAAAAAAAbpAQAo6QEAAAAAAAAAAAAH6QEAKekBAAAAAAAAAAAACOkBACrpAQAAAAAAAAAAAAnpAQAr6QEAAAAAAAAAAAAK6QEALOkBAAAAAAAAAAAAC+kBAC3pAQAAAAAAAAAAAAzpAQAu6QEAAAAAAAAAAAAN6QEAL+kBAAAAAAAAAAAADukBADDpAQAAAAAAAAAAAA/pAQAx6QEAAAAAAAAAAAAQ6QEAMukBAAAAAAAAAAAAEekBADPpAQAAAAAAAAAAABLpAQA06QEAAAAAAAAAAAAT6QEANekBAAAAAAAAAAAAFOkBADbpAQAAAAAAAAAAABXpAQA36QEAAAAAAAAAAAAW6QEAOOkBAAAAAAAAAAAAF+kBADnpAQAAAAAAAAAAABjpAQA66QEAAAAAAAAAAAAZ6QEAO+kBAAAAAAAAAAAAGukBADzpAQAAAAAAAAAAABvpAQA96QEAAAAAAAAAAAAc6QEAPukBAAAAAAAAAAAAHekBAD/pAQAAAAAAAAAAAB7pAQBA6QEAAAAAAAAAAAAf6QEAQekBAAAAAAAAAAAAIOkBAELpAQAAAAAAAAAAACHpAQBD6QEAAAAAAAAAAABMYXlvdXRFcnJwcml2YXRlAEH4+cIACzgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==" + } + }, { + "Contract": { + "account_id": + "pulse.near", + "code": + "AGFzbQEAAAABbBNgAX8Bf2ACf38Bf2AAAGADf39/AGABfwBgA39/fwF/YAABf2ABfgF/YAJ/fwBgAX4AYAJ+fgBgAn9/AX5gAX4BfmAEf39/fwBgA39/fgBgA39+fwBgBH9/f38Bf2AFf39/f38Bf2ACf34BfwJbBgNlbnYFYWJvcnQADQNlbnYFaW5wdXQACQNlbnYMcmVnaXN0ZXJfbGVuAAwDZW52BXBhbmljAAIDZW52DXJlYWRfcmVnaXN0ZXIACgNlbnYIbG9nX3V0ZjgACgOXAZUBBAEAAAIAAgIAAAICAgICAAIAAgIAAQIDBAUBAAECAgIJDAIBCgEAAQABAAQAAQQAAQAGAAMDAQMBAQEAAQARAQUBAQgFAwMIAwEAAAUFBQUEAAUBBRAAAAEBAQEAAAQABAAABgEEAAEAAwAIAQADABIHDgAABggAAAMEAAYBAQEGAAsLCwADBw8HBwcAAQEBAAQJAgIEBAFwAAEFAwEAAQaoAjV/AUEAC38BQQALfwFBAAt/AEEgC38AQcAAC38AQeAAC38AQYABC34AQoCAgIAIC38AQaABC38AQeABC38AQaACC38BQQALfwBB4AILfwBBgAMLfwBBkAQLfgFCAAt+AUIAC34BQgALfgFCAAt+AUIAC34BQgALfgFCAAt/AUEAC38AQQgLfwBBCQt/AEEGC38BQQALfwBBIAt/AEHAAAt/AEHgAAt/AEGAAQt+AEKAgICACAt/AEGgAQt/AEHgAQt/AEGgAgt/AUEAC38AQeACC38AQYADC38AQcAEC38AQeAEC38AQYAFC38BQQALfwFBAAt/AUEAC38BQQALfwBBAAt/AUEAC38BQQALfwBBfwt/AEF/C38BQQALfwBB8B4LfwBB1CALBy4DFF9fc2V0QXJndW1lbnRzTGVuZ3RoAFYGbWVtb3J5AgAJaGVhcnRiZWF0AJkBCAKaAQkGAQBBAQsACrlmlQFTAQV/PwAhASABQRB0IQIgACACSwRAIAAgAmtB//8DakH//wNBf3NxQRB2IQMgASIEIAMiBSAEIAVKG0AAQQBIBEAgA0AAQQBIBEAACwsLIAAkAQtdAQV/IABB8P///wNLBEAACyMBQRBqIQIgAEEPakEPQX9zcSIDQRAiBCADIARLGyEFIAIgBWoQBiACQRBrIQYgBiAFNgIAIAZBATYCBCAGIAE2AgggBiAANgIMIAILBAAgAAsUACAARQRAQQBBAxAHEAghAAsgAAsZACM0QQ9qQQ9Bf3NxJAAjACQBQQAQCSQCCxQAIABFBEBBAEEEEAcQCCEACyAACwgAQQAQCyQLCwYAEAoQDAsUACAARQRAQQBBBxAHEAghAAsgAAsWACAARQRAQQBBBhAHEAghAAsgABAOCwgAQQAQDyQWCwQAEBALBAAQEQsGABANEBILBAAQEwsUACAARQRAQQBBDhAHEAghAAsgAAsIAEEAEBUkGgsUACAARQRAQQBBDxAHEAghAAsgAAsIAEEAEBckIwsGABAWEBgLDQAgAEEQaygCDEEBdgsaACABIAAQGk8EQEF/DwsgACABQQF0ai8BAAsmAEGgBUEAEBskKUHABUEAEBskKkHgBUEAEBskK0GABkEAEBskLAvUAwIGfwF+AkAgACEFIAEhBCACIQMgA0UEQAwBCyAFIAQ6AAAgBSADakEBayAEOgAAIANBAk0EQAwBCyAFQQFqIAQ6AAAgBUECaiAEOgAAIAUgA2pBAmsgBDoAACAFIANqQQNrIAQ6AAAgA0EGTQRADAELIAVBA2ogBDoAACAFIANqQQRrIAQ6AAAgA0EITQRADAELQQAgBWtBA3EhBiAFIAZqIQUgAyAGa0F8cSEDQX9B/wFuIARB/wFxbCEHIAUgBzYCACAFIANqQQRrIAc2AgAgA0EITQRADAELIAVBBGogBzYCACAFQQhqIAc2AgAgBSADakEMayAHNgIAIAUgA2pBCGsgBzYCACADQRhNBEAMAQsgBUEMaiAHNgIAIAVBEGogBzYCACAFQRRqIAc2AgAgBUEYaiAHNgIAIAUgA2pBHGsgBzYCACAFIANqQRhrIAc2AgAgBSADakEUayAHNgIAIAUgA2pBEGsgBzYCAEEYIAVBBHFqIQYgBSAGaiEFIAMgBmshAyAHrSAHrUIghoQhCQNAIANBIE8EQCAFIAk3AwAgBUEIaiAJNwMAIAVBEGogCTcDACAFQRhqIAk3AwAgA0EgayEDIAVBIGohBQwBCwsLCwMAAQuMAQEEfyABQfD///8DIAJ2SwRAQaAGQdAGQRdBOBAAAAsgASACdCIBQQAQByEDIANBACABEB0gAEUEQEEMQQIQBxAIIQALIABBADYCACAAQQA2AgQgAEEANgIIIAAiBCADIgUgBCgCACIGRwRAIAUQCCEFIAYQHgsgBTYCACAAIAM2AgQgACABNgIIIAALKgAgAAR/IAAFQRBBERAHEAgLIAFBAhAfIQAgAEEANgIMIAAgATYCDCAACx8AIABFBEBBBEEQEAcQCCEACyAAQQBBABAgNgIAIAALUAEDfyABEAghASAARQRAQQhBEhAHEAghAAsgAEEANgIAIABBADYCBCAAIgIgASIDIAIoAgAiBEcEQCADEAghAyAEEB4LIAM2AgAgARAeIAALEABBABAhJC5BACMuECIkLwsGABAcECMLBgAQGRAkCwYAIAAQAQsGACAAEAILBAAQAwsYACAABH8gAAVBDEEUEAcQCAsgAUEAEB8LCAAgACABEAQLNgAgARAIIQEgAEUEQEEMQRMQBxAIIQALIABBkAc2AgAgAEEANgIEIAAgARAINgIIIAEQHiAACwcAIAAoAggLJgAgASAAKAIITwRAQaAHQeAHQZgBQSwQAAALIAAoAgQgAWotAAALLQAgACgCBCgCBCAAKAIEKAIIECxOBEBBfw8LIAAoAgQoAgggACgCBCgCBBAtCygAIAFBCUYEf0EBBSABQQpGCwR/QQEFIAFBDUYLBH9BAQUgAUEgRgsLSgEBfyAAKAIEKAIEIAAoAgQoAggQLEhFBEBBoAhB4AhB8ABBCBAAAAsgACgCBCgCCCAAKAIEIAAoAgQoAgQiAUEBajYCBCABEC0LGQEBfwNAIAAgABAuEC8EQCAAEDAaDAELCwsUACAARQRAQQBBCxAHEAghAAsgAAsxAQF/IAFB8P///wNLBEBBoAZB0AZBNkEqEAAACyABQQAQByECIAJBACABEB0gAhAIC0sBAn8gACIBQQBBEBAzIAEoAgAQHjYCACAAQQRBAWs2AgQgACICQQBBMBAzIAIoAggQHjYCCCAAQQQ2AgwgAEEANgIQIABBADYCFAtCACAARQRAQRhBDBAHEAghAAsgAEEANgIAIABBADYCBCAAQQA2AgggAEEANgIMIABBADYCECAAQQA2AhQgABA0IAALKgAgAAR/IAAFQRBBDRAHEAgLIAFBAhAfIQAgAEEANgIMIAAgATYCDCAAC04BAn8gAAR/IAAFQQhBChAHEAgLEDIhACAAQQA2AgAgAEEANgIEIAAiAUEAEDUgASgCABAeNgIAIAAiAkEAQQAQNiACKAIEEB42AgQgAAsGAEEAEDcLBwAgACgCDAuHDwEEfwNAIAIEfyABQQNxBUEACwRAIAAiBkEBaiEAIAYgASIGQQFqIQEgBi0AADoAACACQQFrIQIMAQsLIABBA3FBAEYEQANAIAJBEE8EQCAAIAEoAgA2AgAgAEEEaiABQQRqKAIANgIAIABBCGogAUEIaigCADYCACAAQQxqIAFBDGooAgA2AgAgAUEQaiEBIABBEGohACACQRBrIQIMAQsLIAJBCHEEQCAAIAEoAgA2AgAgAEEEaiABQQRqKAIANgIAIABBCGohACABQQhqIQELIAJBBHEEQCAAIAEoAgA2AgAgAEEEaiEAIAFBBGohAQsgAkECcQRAIAAgAS8BADsBACAAQQJqIQAgAUECaiEBCyACQQFxBEAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAACw8LIAJBIE8EQAJAAkACQAJAIABBA3EhBSAFQQFGDQAgBUECRg0BIAVBA0YNAgwDCyABKAIAIQMgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgAkEDayECA0AgAkERTwRAIAFBAWooAgAhBCAAIANBGHYgBEEIdHI2AgAgAUEFaigCACEDIABBBGogBEEYdiADQQh0cjYCACABQQlqKAIAIQQgAEEIaiADQRh2IARBCHRyNgIAIAFBDWooAgAhAyAAQQxqIARBGHYgA0EIdHI2AgAgAUEQaiEBIABBEGohACACQRBrIQIMAQsLDAILIAEoAgAhAyAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAJBAmshAgNAIAJBEk8EQCABQQJqKAIAIQQgACADQRB2IARBEHRyNgIAIAFBBmooAgAhAyAAQQRqIARBEHYgA0EQdHI2AgAgAUEKaigCACEEIABBCGogA0EQdiAEQRB0cjYCACABQQ5qKAIAIQMgAEEMaiAEQRB2IANBEHRyNgIAIAFBEGohASAAQRBqIQAgAkEQayECDAELCwwBCyABKAIAIQMgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAJBAWshAgNAIAJBE08EQCABQQNqKAIAIQQgACADQQh2IARBGHRyNgIAIAFBB2ooAgAhAyAAQQRqIARBCHYgA0EYdHI2AgAgAUELaigCACEEIABBCGogA0EIdiAEQRh0cjYCACABQQ9qKAIAIQMgAEEMaiAEQQh2IANBGHRyNgIAIAFBEGohASAAQRBqIQAgAkEQayECDAELCwwACwsgAkEQcQRAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAAAsgAkEIcQRAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAALIAJBBHEEQCAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAALIAJBAnEEQCAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAACyACQQFxBEAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAACwvmAgEFfwJAIAAhBSABIQQgAiEDIAUgBEYEQAwBCyAEIANqIAVNBH9BAQUgBSADaiAETQsEQCAFIAQgAxA6DAELIAUgBEkEQCAEQQdxIAVBB3FGBEADQCAFQQdxBEAgA0UEQAwFCyADQQFrIQMgBSIHQQFqIQUgByAEIgdBAWohBCAHLQAAOgAADAELCwNAIANBCE8EQCAFIAQpAwA3AwAgA0EIayEDIAVBCGohBSAEQQhqIQQMAQsLCwNAIAMEQCAFIgdBAWohBSAHIAQiB0EBaiEEIActAAA6AAAgA0EBayEDDAELCwUgBEEHcSAFQQdxRgRAA0AgBSADakEHcQRAIANFBEAMBQsgBSADQQFrIgNqIAQgA2otAAA6AAAMAQsLA0AgA0EITwRAIANBCGshAyAFIANqIAQgA2opAwA3AwAMAQsLCwNAIAMEQCAFIANBAWsiA2ogBCADai0AADoAAAwBCwsLCwvWAQEGfyAAQQBHBH8gAEEPcUUFQQALRQRAQQBBkApBK0ECEAAACyAAQRBrIQIgAigCACEDIAIoAgRBAUZFBEBBAEGQCkEuQQ0QAAALIAAgA2ojAUYhBCABQQ9qQQ9Bf3NxIQUgASADSwRAIAQEQCABQfD///8DSwRAAAsgACAFahAGIAIgBTYCAAUgBSIGIANBAXQiByAGIAdLGyACKAIIEAchBiAGIAAgAigCDBA7IAYiAEEQayECCwUgBARAIAAgBWokASACIAU2AgALCyACIAE2AgwgAAtzAQR/IAAoAgghAyABIAMgAnZLBEAgAUHw////AyACdksEQEGgBkHgCUEOQS8QAAALIAAoAgAhBCABIAJ0IQUgBCAFEDwhBiAGIANqQQAgBSADaxAdIAYgBEcEQCAAIAY2AgAgACAGNgIECyAAIAU2AggLCz8BA38gARAIIQEgACgCDCECIAJBAWohAyAAIANBAhA9IAAoAgQgAkECdGogARAINgIAIAAgAzYCDCADIAEQHgsSACAAKAIEIAFBAnRqKAIAEAgLPwEBfyABIAAoAgxPBEBBoAdB4AlB3QBBKRAAAAsgACABED8hAiACRQRAIAIQHkHACkHgCUHhAEEnEAAACyACCxMAIAAoAgAgACgCABA5QQFrEEALPwEDfyAAQRBrKAIIIQIjMyEDIAIgAygCAE0EQANAIAIgAUYEQEEBDwsgA0EEaiACQQhsaigCBCICDQALC0EAC1QBBH8gABAIIQBBxbvyiHghASAAQQBHBEBBACECIAAQGkEBdCEDA0AgAiADSQRAIAEgACACai0AAHNBk4OACGwhASACQQFqIQIMAQsLCyABIAAQHgu/AQEGfyAAEAghACACEAghAiAAIAFBAXRqIQUgAiADQQF0aiEGIARBBE8EfyAFQQdxIAZBB3FyRQVBAAsEQAJAA0AgBSkDACAGKQMAUgRADAILIAVBCGohBSAGQQhqIQYgBEEEayEEIARBBE8NAAsLCwNAIAQiB0EBayEEIAcEQCAFLwEAIQggBi8BACEJIAggCUcEQCAIIAlrIAAQHiACEB4PCyAFQQJqIQUgBkECaiEGDAELC0EAIAAQHiACEB4LcQECfyAAEAghACABEAghASAAIAFGBEBBASAAEB4gARAeDwsgAEEARgR/QQEFIAFBAEYLBEBBACAAEB4gARAeDwsgABAaIQMgAyABEBpHBEBBACAAEB4gARAeDwsgAEEAIAFBACADEERFIAAQHiABEB4LXwEDfyABEAghASAAKAIAIAIgACgCBHFBBGxqKAIAIQMDQCADBEAgAygCCEEBcUUEfyADKAIAIAEQRQVBAAsEQCADIAEQHg8LIAMoAghBAUF/c3EhAwwBCwtBACABEB4LKgECfyABEAghASAAIAECfyABEAghAiACEEMgAhAeDAALEEZBAEcgARAeCz8BA38gARAIIQEgACgCDCECIAJBAWohAyAAIANBAhA9IAAoAgQgAkECdGogARAINgIAIAAgAzYCDCADIAEQHgugAgEMfyABQQFqIQJBACACQQRsEDMhAyACQQhsQQNtIQRBACAEQQxsEDMhBSAAKAIIIQYgBiAAKAIQQQxsaiEHIAUhCANAIAYgB0cEQCAGIQogCigCCEEBcUUEQCAIIQsgCyAKKAIANgIAIAsgCigCBDYCBAJ/IAooAgAQCCEMIAwQQyAMEB4MAAsgAXEhDCADIAxBBGxqIQ0gCyANKAIANgIIIA0gCDYCACAIQQxqIQgLIAZBDGohBgwBCwsgACILIAMiDCALKAIAIglHBEAgDBAIIQwgCRAeCyAMNgIAIAAgATYCBCAAIg0gBSIJIA0oAggiC0cEQCAJEAghCSALEB4LIAk2AgggACAENgIMIAAgACgCFDYCECADEB4gBRAeC/0BAQR/IAEQCCEBIAIQCCECAn8gARAIIQMgAxBDIAMQHgwACyEFIAAgASAFEEYhBiAGBEAgBigCBCEDIAIgA0cEQCAGIAIQCDYCBCADEB4LBSAAKAIQIAAoAgxGBEAgACAAKAIUIAAoAgxBA2xBBG1IBH8gACgCBAUgACgCBEEBdEEBcgsQSQsgACgCCBAIIQMgAyAAIAAoAhAiBEEBajYCECAEQQxsaiEGIAYgARAINgIAIAYgAhAINgIEIAAgACgCFEEBajYCFCAAKAIAIAUgACgCBHFBBGxqIQQgBiAEKAIANgIIIAQgBjYCACADEB4LIAAQCCABEB4gAhAeCzoAIAEQCCEBIAIQCCECIAAoAgAgARBHRQRAIAAoAgQgARBIGgsgACgCACABIAIQShAeIAEQHiACEB4LHwAgARAIIQEgAhAIIQIgACABIAIQSyABEB4gAhAeDwsWACABEAghASAAKAIAIAEQPhogARAeC84BAQR/IAEQCCEBIAIQCCECIAEQGkEARgR/IAAoAgAQOUEARgVBAAsEQCAAKAIAIAIQPhogARAeIAIQHg8LIAAQQSIDIgRFBH9BAAUgBEEKEEILBEAgABBBIgQiBUEKEEIEfyAFBUEAQbALQcoAQQcQAAALIAEgAhBMIAQQHgUgABBBIgQiBUUEf0EABSAFQRUQQgsEQCAAEEEiBSIGQRUQQgR/IAYFQQBBsAtBzQBBERAAAAsgAhBNIAUQHgsgBBAeCyADEB4gARAeIAIQHgsqAQJ/IAEQCCEBEDghAiAAIAEgAhBOIAAoAgAgAhA+GkEBIAIQHiABEB4LDQAgACgCBCAAKAIAawsYAQF/IAAQCCEAIAAoAgAgABBQaiAAEB4L7gIBCn8gACEDIAAgAWohBCAEIANPRQRAQQBBsA5B3wVBBhAAAAsgAUEBdEEBEAchBSAFIQYCQANAIAMgBEkEQCADLQAAIQggA0EBaiEDIAhBgAFxRQRAIAIgCEVxBEAMBAsgBiAIOwEABSAEIANGBEAMBAsgAy0AAEE/cSEJIANBAWohAyAIQeABcUHAAUYEQCAGIAhBH3FBBnQgCXI7AQAFIAQgA0YEQAwFCyADLQAAQT9xIQogA0EBaiEDIAhB8AFxQeABRgRAIAhBD3FBDHQgCUEGdHIgCnIhCAUgBCADRgRADAYLIAhBB3FBEnQgCUEMdHIgCkEGdHIgAy0AAEE/cXIhCCADQQFqIQMLIAhBgIAESQRAIAYgCDsBAAUgCEGAgARrIQggCEEKdkGAsANyIQsgCEH/B3FBgLgDciEMIAYgCyAMQRB0cjYCACAGQQJqIQYLCwsgBkECaiEGDAELCwsgBSAGIAVrEDwQCAseAQF/IAAQCCEAIAAQUSABaiACIAFrQQAQUiAAEB4LEAAgACgCCCABIAJBAWsQUwslAAJAAkACQCMyQQFrDgIBAgALAAsgACgCBCECCyAAIAEgAhBUCwYAIAAkMgsHACAAKAIMC/UCAQp/IAIQCCECIAFBAWshAyADQQBIBEBBkAcgAhAeDwsgA0UEQCAAKAIAIgQEfyAEEAgFQZAHCyACEB4PC0EAIQVBACEGQQAhBANAIAQgAUgEQCAAIARBAnRqKAIAIgggBiIJRwRAIAgQCCEIIAkQHgsgCCEGIAZBAEcEQCAFIAYQGmohBQsgBEEBaiEEDAELC0EAIQogAhAaIQsgBSALIANsakEBdEEBEAchDEEAIQQDQCAEIANIBEAgACAEQQJ0aigCACIJIAYiCEcEQCAJEAghCSAIEB4LIAkhBiAGQQBHBEAgBhAaIQkgDCAKQQF0aiAGIAlBAXQQOyAKIAlqIQoLIAsEQCAMIApBAXRqIAIgC0EBdBA7IAogC2ohCgsgBEEBaiEEDAELCyAAIANBAnRqKAIAIgggBiIERwRAIAgQCCEIIAQQHgsgCCEGIAZBAEcEQCAMIApBAXRqIAYgBhAaQQF0EDsLIAwQCCACEB4gBhAeCx0BA38gARAIIQEgACgCBCAAKAIMIAEQWCABEB4PCxsBAX8gACABEAchAyACBEAgAyACIAAQOwsgAws9AQN/QRAgAhAHIQQgACABdCEFIAVBACADEFohBiAEIAYQCDYCACAEIAY2AgQgBCAFNgIIIAQgADYCDCAEC48BAQR/IAAQMCEBIAEjKWshAiACQQlKBEAgASMra0EKaiECIAJBCkgEf0EBBSACQQ9KCwRAIAEjLGtBCmohAgsLQQJBAkEWQQAQWxAIIQMgAygCBCEEIAQgATYCACAEIAI2AgQgAyACQQBOBH8gAkEQSAVBAAtFBEBBwBFB4AhBjQJBBBAAAAsgAiEDEB4gAwtxAQR/IABB///DAE1FBEBBAEGwDkEhQQQQAAALIABB//8DSiEBQQIgAXRBARAHIQIgAUUEQCACIAA7AQAFIABBgIAEayEAIABB/wdxQYC4A3IhAyAAQQp2QYCwA3IhBCACIAQgA0EQdHI2AgALIAIQCAstAQJ/IAFBAEohAkECIAJ0QQEQByEDIAMgADsBACACBEAgAyABOwECCyADEAgLIAACQAJAAkAjMkEBaw4CAQIACwALQX8hAQsgACABEF4LewEGfyABEAghASABQQBGBEBBgAUiAiABIgNHBEAgAhAIIQIgAxAeCyACIQELIAAQGkEBdCEEIAEQGkEBdCEFIAQgBWohBiAGQQBGBEBBkAcQCCABEB4PCyAGQQEQBxAIIQcgByAAIAQQOyAHIARqIAEgBRA7IAcgARAeCycBAX8gABAIIQAgARAIIQEgAEGABSAAQQBHGyABEGAgABAeIAEQHgvqAQEGfyAAEDAhASABQfAMQQAQG0YEQEHwDA8LIAFB4A5BABAbRgRAQeAODwsgAUGAD0EAEBtGBEBBgA8PCyABQaAPQQAQG0YEQEHADw8LIAFB4A9BABAbRgRAQYAQDwsgAUGgEEEAEBtGBEBBwBAPCyABQeAQQQAQG0YEQEGAEQ8LIAFBoBFBABAbRgRAIAAQXCAAEFwhAyAAEFwhBCAAEFwhBUGAIGwgA0GAAmxqIARBEGxqIAVqEF0PC0EARQRAQYASQQEkMiABQQAQXyIGEGEiBUHgCEH/AUEEEAAAC0GQByAGEB4gBRAeC/oBAQZ/IAAQMEHwDEEAEBtGRQRAQZANQeAIQcUBQQQQAAALIAAoAgQoAgQhAUEAQQAQNiECA0BBAQRAIAAQMCEEIARBIE5FBEBB4A1B4AhBywFBBhAAAAsgBEHwDEEAEBtGBEBBASQyIAAoAgQgAUEAEFUhBSACEFdBAEYEQCAFIAIQHg8LIAIgBRBIGiACQZAHEFkgBRAeIAIQHg8FIARB4A5BABAbRgRAIAAoAgQoAgQgAUEBakoEQCACQQEkMiAAKAIEIAFBABBVIgUQSBogBRAeCyACIAAQYiIFEEgaIAAoAgQoAgQhASAFEB4LCwwBCwtBkAcgAhAeCz8BAn8gABAxIAAoAgQiASAAEGMgASgCABAeNgIAIAAQMSAAEDBB0BJBABAbRkUEQEHwEkHgCEGfAUEEEAAACws9AQJ/IAAoAgwhASABQQFIBEBB4BNB4AlBoAJBFBAAAAsgACgCBCABQQFrIgFBAnRqKAIAEAggACABNgIMCxgAIAAoAgAQOUEBSgRAIAAoAgAQZRAeCwvAAQEDfyAAEC5BwAlBABAbRwRAQQAPCyAAKAIEKAIAEAghASAAKAIEIgJBkAcgAigCABAeNgIAIAAoAgAgARBPBEAgABAwGiAAEDFBASECA0AgABAuQYAMQQAQG0cEQCACRQRAIAAQMEGgDEEAEBtGRQRAQcAMQeAIQY4BQQoQAAALBUEAIQILIAAQZCAAEH4aDAELCyAAEDBBgAxBABAbRkUEQEGgE0HgCEGVAUEGEAAACwsgACgCABBmQQEgARAeCzUBAn8gAAR/IAAFQQRBFRAHEAgLEDIhACAAQQA2AgAgACIBQQBBABAgIAEoAgAQHjYCACAACwYAQQAQaAtCAQJ/IAEQCCEBEGkhAiAAKAIAEDlBAEYEQCAAKAIAIAIQPhoFIAAgASACEE4gACgCACACED4aC0EBIAIQHiABEB4LGAAgACgCABA5QQFKBEAgACgCABBlEB4LC7wBAQN/IAAQLkGQFEEAEBtHBEBBAA8LIAAoAgQoAgAQCCEBIAAoAgQiAkGQByACKAIAEB42AgAgACgCACABEGoEQCAAEDAaIAAQMUEBIQIDQCAAEC5BsBRBABAbRwRAIAJFBEAgABAwQaAMQQAQG0ZFBEBBwAxB4AhBsAFBChAAAAsFQQAhAgsgABB+GgwBCwsgABAwQbAUQQAQG0ZFBEBB0BRB4AhBtgFBBhAAAAsLIAAoAgAQa0EBIAEQHgsrACABEAghASAABH8gAAVBBEEXEAcQCAsQMiEAIAAgARAINgIAIAEQHiAACxQBAX8gABAIIQBBACAAEG0gABAeCyoBAX8gARAIIQEgAhAIIQIgAhBuIQMgACABIAMQTiADEB4gARAeIAIQHgsxAQJ/IAAQLkHwDEEAEBtHBEBBAA8LIAAoAgAgACgCBCgCACAAEGMiARBvQQEgARAeC1oBBH8gARAIIQFBACECA0AgAiABEBpIBEAgASACEBsgABAwRkUEQEGQFSABEGEiBEHAFRBhIgVB4AhBwQJBBhAAAAsgBBAeIAUQHiACQQFqIQIMAQsLIAEQHgsfACAABH8gAAVBAUEYEAcQCAsQMiEAIAAgAToAACAACwgAQQAgABByCyABAX8gARAIIQEgAhBzIQMgACABIAMQTiADEB4gARAeC1YAIAAQLkHgBEEAEBtGBEAgACMnEHEgACgCACAAKAIEKAIAQQAQdEEBDwsgABAuQcAEQQAQG0YEQCAAIyYQcSAAKAIAIAAoAgQoAgBBARB0QQEPC0EACx8AIAAEfyAABUEIQRkQBxAICxAyIQAgACABNwMAIAALCABBACAAEHYLIAEBfyABEAghASACEHchAyAAIAEgAxBOIAMQHiABEB4LggECA38CfkIAIQRCASEFIAAQLkHgFUEAEBtGBEBCfyEFIAAQMBoLQQAhAQNAIykgABAuTAR/IAAQLiMqTAVBAAsEQCAAEDAhAyAEQgp+IAMjKWusfCEEIAFBAWohAQwBCwsgAUEASgRAIAAoAgAgACgCBCgCACAEIAV+EHhBAQ8LQQALFAAgAAR/IAAFQQBBGhAHEAgLEDILBgBBABB6Cx4BAX8gARAIIQEQeyECIAAgASACEE4gAhAeIAEQHgsrACAAEC5BgAVBABAbRgRAIAAjKBBxIAAoAgAgACgCBCgCABB8QQEPC0EAC0wBAX8gABAxIAAQZyIBBH8gAQUgABBsCyIBBH8gAQUgABBwCyIBBH8gAQUgABB1CyIBBH8gAQUgABB5CyIBBH8gAQUgABB9CyAAEDELcAEDfyABEAghASACEAghAiACQQBHBEAgACIDIAIiBCADKAIEIgVHBEAgBBAIIQQgBRAeCyAENgIEBSAAIgVBACABECsgBSgCBBAeNgIECyAAEH5BAEdFBEBBgBZB4AhB5ABBBBAAAAsgARAeIAIQHgsfAQF/A0AgACgCABA5QQBKBEAgACgCABBlEB4MAQsLC08BBH8gABAIIQBBACEBIAAiAiABIgNHBEAgAhAIIQIgAxAeCyACIQEjLyABQQAQfyMvKAIAEEEiAhAIIy8oAgAQgAEgAhAeIAAQHiABEB4LXwIDfwF+QgAQJkIAECchAyADQv////8PUQRAECgLQQAgA6cQKSEAQgAgACgCBK0QKiAAEAghASABEIEBIAEQHiIBQQoQQgR/IAEFQQBBwBZBwABBFBAAAAsQCCAAEB4LHwEBfyAAEAghACABEAghASAAIAEQRUUgABAeIAEQHgtIAQN/IAEQCCEBIAAgAQJ/IAEQCCECIAIQQyACEB4MAAsQRiEEIARFBEAgARAeQeAXQaAYQe8AQRAQAAALIAQoAgQQCCABEB4LLgEBfyABEAghASAAKAIAIAEQR0UEQEEAEAggARAeDwsgACgCACABEIQBIAEQHgscAEEABH9BAQVBAAsEf0EBBUEACwR/QQEFQQALC5UBAQF/IABBgC1JBEAgAEGAAXJBoAFGIABBCWtBDUEJa01yDwsgAEGAwABrQQpNBEBBAQ8LAkACQAJAAkACQAJAAkACQCAAIQEgAUGALUYNACABQajAAEYNASABQanAAEYNAiABQa/AAEYNAyABQd/AAEYNBCABQYDgAEYNBSABQf/9A0YNBgwHCwsLCwsLC0EBDwtBAAvaAwIEfwN+IAAQCCEAIAAQGiECIAJFBEBCACAAEB4PCyAAIQMgAy8BACEEQgEhBwNAIAQQhwEEQCADQQJqIgMvAQAhBCACQQFrIQIMAQsLIARBLUYEQCACQQFrIgJFBEBCACAAEB4PCyADQQJqIgMvAQAhBEJ/IQcFIARBK0YEQCACQQFrIgJFBEBCACAAEB4PCyADQQJqIgMvAQAhBAsLIAFFBEAgBEEwRgR/IAJBAkoFQQALBEACQAJAAkACQAJAIANBAmovAQBBIHIhBSAFQeIARg0AIAVB7wBGDQEgBUH4AEYNAgwDCyADQQRqIQMgAkECayECQQIhAQwDCyADQQRqIQMgAkECayECQQghAQwCCyADQQRqIQMgAkECayECQRAhAQwBC0EKIQELBUEKIQELBSABQQJIBH9BAQUgAUEkSgsEQEIAIAAQHg8LC0IAIQgCQANAIAIiBUEBayECIAUEQCADLwEAIQQgBEEwa0EKSQRAIARBMGshBAUgBEHBAGtBGU0EQCAEQcEAQQprayEEBSAEQeEAa0EZTQRAIARB4QBBCmtrIQQFDAULCwsgBCABTwRADAMLIAggAax+IAStfCEIIANBAmohAwwBCwsLIAcgCH4gABAeCxUBAX4gABAIIQAgACABEIgBIAAQHguvAwIHfwJ+IAAQCCEAIAEQCCEBIAAQCCECQQAhAyACIgRFBH9BAAUgBEEKEEILBH8gAUGQBxCDAQVBAAsEQCACIgRBChBCBH8gBAVBAEHAFkGcAUEkEAAACxAIIQQgBCABEIUBIQUgBUEARgRAEIYBBH9BAAVBAAsEQCAAEB4gARAeIAIQHiADEB4gBBAeIAUQHkHQGEHwGBBhIgZBkBkQYSIHQcAWQaEBQQoQAAAFIAAQHiABEB4gAhAeIAMQHiAEEB4gBRAeQdAYQfAYEGEiB0GQGRBhIgZBwBZBpwFBCBAAAAsACyAFIgcgAyIGRwRAIAcQCCEHIAYQHgsgByEDIAQQHiAFEB4FIAIiBiADIgVHBEAgBhAIIQYgBRAeCyAGIQMLIAMiBkUEf0EABSAGQRcQQgtFBEBBwBkgARBhIgZB8BkQYSIFQfAYEGEiBEGgGhBhIgdBwBZBtwFBBhAAAAsgAyIIQRcQQgR/IAgFQQBBwBZBuAFBGxAAAAsoAgAQCCEIIAhBABCJASAGEB4gBRAeIAQQHiAHEB4gCBAeIAAQHiABEB4gAhAeIAMQHg8LeAEBfyAAQaCNBkkEQCAAQeQASQRAQQFBAiAAQQpJGw8FQQRBBSAAQZDOAEkbIQFBAyABIABB6AdJGw8LAAUgAEGAreIESQRAQQZBByAAQcCEPUkbDwVBCUEKIABBgJTr3ANJGyEBQQggASAAQYDC1y9JGw8LAAsAC+4BAgd/An4DQCABQZDOAE8EQCABQZDOAG4gAUGQzgBwIQUhASAFQeQAbiEGIAVB5ABwIQdB4BsgBkECdGo1AgAhCkHgGyAHQQJ0ajUCACELIAJBBGshAiAAIAJBAXRqIAogC0IghoQ3AwAMAQsLIAFB5ABPBEAgAUHkAG4gAUHkAHAhCCEBIAJBAmshAkHgGyAIQQJ0aigCACEJIAAgAkEBdGogCTYCAAsgAUEKTwRAIAJBAmshAkHgGyABQQJ0aigCACEJIAAgAkEBdGogCTYCAAUgAkEBayECQTAgAWohCSAAIAJBAXRqIAk7AQALC7MBAQF/IABCgICapuqv4wFUBEAgAEKAoJSljR1UBEBBC0EMIABCgNDbw/QCVBshAUEKIAEgAEKAyK+gJVQbDwVBDkEPIABCgIDpg7HeFlQbIQFBDSABIABCgMDK84SjAlQbDwsABSAAQoCAqOyFr9GxAVQEQEEQQREgAEKAgIT+pt7hEVQbDwVBE0EUIABCgICgz8jgyOOKf1QbIQFBEiABIABCgICQu7rWrfANVBsPCwALAAvaAQIIfwN+A0AgAUKAwtcvWgRAIAFCgMLXL4AhCyABIAtCgMLXL359pyEEIAshASAEQZDOAG4hBSAEQZDOAHAhBiAFQeQAbiEHIAVB5ABwIQggBkHkAG4hCSAGQeQAcCEKQeAbIAlBAnRqNQIAIQxB4BsgCkECdGo1AgAhDSACQQRrIQIgACACQQF0aiAMIA1CIIaENwMAQeAbIAdBAnRqNQIAIQxB4BsgCEECdGo1AgAhDSACQQRrIQIgACACQQF0aiAMIA1CIIaENwMADAELCyAAIAGnIAIQjAELXwIGfwF+IABCAFJFBEBBoAUPCyAAQv////8PWARAIACnIQIgAhCLASEDIANBAXRBARAHIQEgASACIAMQjAEFIAAQjQEhAyADQQF0QQEQByEBIAEgACADEI4BCyABEAgLCAAgABCPAQ8LBwAgABCQAQsGACAAEAgLuQEBBX8gABAIIQAgACECIAIgAEEQaygCDGohAyABQQBHIQQCQANAIAIgA0kEQCACLwEAIQYgBkGAAUkEQCABIAZFcQRADAQLIARBAWohBAUgBkGAEEkEQCAEQQJqIQQFIAZBgPgDcUGAsANGBH8gAkECaiADSQVBAAsEQCACLwECQYD4A3FBgLgDRgRAIARBBGohBCACQQRqIQIMBQsLIARBA2ohBAsLIAJBAmohAgwBCwsLIAQgABAeC7IDAQ1/IAAQCCEAIAAhAiAAIABBEGsoAgxqIQMgACABEJMBIQQgBEEAEAchBSAFIARqIAFBAEdrIQYgBSEHA0AgByAGSQRAIAIvAQAhCSAJQYABSQRAIAcgCToAACAHQQFqIQcFIAlBgBBJBEAgCUEGdkHAAXIhCiAJQT9xQYABciELIAcgC0EIdCAKcjsBACAHQQJqIQcFIAlBgPgDcUGAsANGBH8gAkECaiADSQVBAAsEQCACLwECIQsgC0GA+ANxQYC4A0YEQEGAgAQgCUH/B3FBCnRqIAtB/wdxciEJIAlBEnZB8AFyIQogCUEMdkE/cUGAAXIhDCAJQQZ2QT9xQYABciENIAlBP3FBgAFyIQ4gByAOQRh0IA1BEHRyIAxBCHRyIApyNgIAIAdBBGohByACQQRqIQIMBQsLIAlBDHZB4AFyIQsgCUEGdkE/cUGAAXIhDiAJQT9xQYABciENIAcgDkEIdCALcjsBACAHIA06AAIgB0EDaiEHCwsgAkECaiECDAELCyACIANNRQRAQQBBsA5BzwVBBhAAAAsgAQRAIAdBADoAAAsgBRAIIAAQHgsbAQJ/IAAQCCEAIAAgARCUASICIAIQHiAAEB4LNAEDfyAAEAghACAAQQEQkwFBAWshAUEAIAEQKSECIAIoAgQgAEEAEJUBIAEQOyACIAAQHgs6AQN/IAAQCCEAQQAhASAAEJIBIQIgAhCWASABEB4hASACEB4gASgCCKwgASgCBK0QBSABEB4gABAeCx0BAn9BsBsgABCRASIBEGEiAhCXASABEB4gAhAeCxgBAX8QggEhACAAQbAXEIoBEJgBIAAQHgsGABAUECULC/EeSQBBEAsYCAAAAAEAAAABAAAACAAAADoAbABlAG4AAEEwCxwMAAAAAQAAAAEAAAAMAAAAOgBmAHIAbwBuAHQAAEHQAAsaCgAAAAEAAAABAAAACgAAADoAYgBhAGMAawAAQfAACxQEAAAAAQAAAAEAAAAEAAAAOgA6AABBkAELOioAAAABAAAAAQAAACoAAABiAGwAbwBjAGsAXwBpAG4AZABlAHgAXwBzAGUAZQBkAGUAZABfAGEAdAAAQdABCzIiAAAAAQAAAAEAAAAiAAAAcgBhAG4AZABvAG0AXwBiAHUAZgBmAGUAcgBfAGsAZQB5AABBkAILPi4AAAABAAAAAQAAAC4AAAByAGEAbgBkAG8AbQBfAGIAdQBmAGYAZQByAF8AaQBuAGQAZQB4AF8AawBlAHkAAEHQAgsSAgAAAAEAAAABAAAAAgAAAD0AAEHwAguQAYAAAAABAAAAAQAAAIAAAABBAEIAQwBEAEUARgBHAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAYQBiAGMAZABlAGYAZwBoAGkAagBrAGwAbQBuAG8AcABxAHIAcwB0AHUAdgB3AHgAeQB6ADAAMQAyADMANAA1ADYANwA4ADkAKwAvAABBgAQLMCAAAAABAAAAAQAAACAAAAAwADEAMgAzADQANQA2ADcAOAA5AGEAYgBjAGQAZQBmAABBsAQLGAgAAAABAAAAAQAAAAgAAAB0AHIAdQBlAABB0AQLGgoAAAABAAAAAQAAAAoAAABmAGEAbABzAGUAAEHwBAsYCAAAAAEAAAABAAAACAAAAG4AdQBsAGwAAEGQBQsSAgAAAAEAAAABAAAAAgAAADAAAEGwBQsSAgAAAAEAAAABAAAAAgAAADkAAEHQBQsSAgAAAAEAAAABAAAAAgAAAEEAAEHwBQsSAgAAAAEAAAABAAAAAgAAAGEAAEGQBgssHAAAAAEAAAABAAAAHAAAAEkAbgB2AGEAbABpAGQAIABsAGUAbgBnAHQAaAAAQcAGCzYmAAAAAQAAAAEAAAAmAAAAfgBsAGkAYgAvAGEAcgByAGEAeQBiAHUAZgBmAGUAcgAuAHQAcwAAQYAHCxAAAAAAAQAAAAEAAAAAAAAAAEGQBws0JAAAAAEAAAABAAAAJAAAAEkAbgBkAGUAeAAgAG8AdQB0ACAAbwBmACAAcgBhAG4AZwBlAABB0AcLNCQAAAABAAAAAQAAACQAAAB+AGwAaQBiAC8AdAB5AHAAZQBkAGEAcgByAGEAeQAuAHQAcwAAQZAICzgoAAAAAQAAAAEAAAAoAAAAVQBuAGUAeABwAGUAYwB0AGUAZAAgAGkAbgBwAHUAdAAgAGUAbgBkAABB0AgLVkYAAAABAAAAAQAAAEYAAAB+AGwAaQBiAC8AYQBzAHMAZQBtAGIAbAB5AHMAYwByAGkAcAB0AC0AagBzAG8AbgAvAGQAZQBjAG8AZABlAHIALgB0AHMAAEGwCQsSAgAAAAEAAAABAAAAAgAAAHsAAEHQCQsqGgAAAAEAAAABAAAAGgAAAH4AbABpAGIALwBhAHIAcgBhAHkALgB0AHMAAEGACgsuHgAAAAEAAAABAAAAHgAAAH4AbABpAGIALwByAHQALwBzAHQAdQBiAC4AdABzAABBsAoLbl4AAAABAAAAAQAAAF4AAABFAGwAZQBtAGUAbgB0ACAAdAB5AHAAZQAgAG0AdQBzAHQAIABiAGUAIABuAHUAbABsAGEAYgBsAGUAIABpAGYAIABhAHIAcgBhAHkAIABpAHMAIABoAG8AbABlAHkAAEGgCwtQQAAAAAEAAAABAAAAQAAAAH4AbABpAGIALwBhAHMAcwBlAG0AYgBsAHkAcwBjAHIAaQBwAHQALQBqAHMAbwBuAC8ASgBTAE8ATgAuAHQAcwAAQfALCxICAAAAAQAAAAEAAAACAAAAfQAAQZAMCxICAAAAAQAAAAEAAAACAAAALAAAQbAMCygYAAAAAQAAAAEAAAAYAAAARQB4AHAAZQBjAHQAZQBkACAAJwAsACcAAEHgDAsSAgAAAAEAAAABAAAAAgAAACIAAEGADQtKOgAAAAEAAAABAAAAOgAAAEUAeABwAGUAYwB0AGUAZAAgAGQAbwB1AGIAbABlAC0AcQB1AG8AdABlAGQAIABzAHQAcgBpAG4AZwAAQdANC0g4AAAAAQAAAAEAAAA4AAAAVQBuAGUAeABwAGUAYwB0AGUAZAAgAGMAbwBuAHQAcgBvAGwAIABjAGgAYQByAGEAYwB0AGUAcgAAQaAOCywcAAAAAQAAAAEAAAAcAAAAfgBsAGkAYgAvAHMAdAByAGkAbgBnAC4AdABzAABB0A4LEgIAAAABAAAAAQAAAAIAAABcAABB8A4LEgIAAAABAAAAAQAAAAIAAAAvAABBkA8LEgIAAAABAAAAAQAAAAIAAABiAABBsA8LEgIAAAABAAAAAQAAAAIAAAAIAABB0A8LEgIAAAABAAAAAQAAAAIAAABuAABB8A8LEgIAAAABAAAAAQAAAAIAAAAKAABBkBALEgIAAAABAAAAAQAAAAIAAAByAABBsBALEgIAAAABAAAAAQAAAAIAAAANAABB0BALEgIAAAABAAAAAQAAAAIAAAB0AABB8BALEgIAAAABAAAAAQAAAAIAAAAJAABBkBELEgIAAAABAAAAAQAAAAIAAAB1AABBsBELNiYAAAABAAAAAQAAACYAAABVAG4AZQB4AHAAZQBjAHQAZQBkACAAXAB1ACAAZABpAGcAaQB0AABB8BELTDwAAAABAAAAAQAAADwAAABVAG4AZQB4AHAAZQBjAHQAZQBkACAAZQBzAGMAYQBwAGUAZAAgAGMAaABhAHIAYQBjAHQAZQByADoAIAAAQcASCxICAAAAAQAAAAEAAAACAAAAOgAAQeASCygYAAAAAQAAAAEAAAAYAAAARQB4AHAAZQBjAHQAZQBkACAAJwA6ACcAAEGQEwtAMAAAAAEAAAABAAAAMAAAAFUAbgBlAHgAcABlAGMAdABlAGQAIABlAG4AZAAgAG8AZgAgAG8AYgBqAGUAYwB0AABB0BMLLBwAAAABAAAAAQAAABwAAABBAHIAcgBhAHkAIABpAHMAIABlAG0AcAB0AHkAAEGAFAsSAgAAAAEAAAABAAAAAgAAAFsAAEGgFAsSAgAAAAEAAAABAAAAAgAAAF0AAEHAFAs+LgAAAAEAAAABAAAALgAAAFUAbgBlAHgAcABlAGMAdABlAGQAIABlAG4AZAAgAG8AZgAgAGEAcgByAGEAeQAAQYAVCyQUAAAAAQAAAAEAAAAUAAAARQB4AHAAZQBjAHQAZQBkACAAJwAAQbAVCxICAAAAAQAAAAEAAAACAAAAJwAAQdAVCxICAAAAAQAAAAEAAAACAAAALQAAQfAVCzIiAAAAAQAAAAEAAAAiAAAAQwBhAG4AbgBvAHQAIABwAGEAcgBzAGUAIABKAFMATwBOAABBsBYLaFgAAAABAAAAAQAAAFgAAABuAG8AZABlAF8AbQBvAGQAdQBsAGUAcwAvAG4AZQBhAHIALQBzAGQAawAtAGEAcwAvAGEAcwBzAGUAbQBiAGwAeQAvAGIAaQBuAGQAZwBlAG4ALgB0AHMAAEGgFwsiEgAAAAEAAAABAAAAEgAAAHQAaQBtAGUAcwB0AGEAbQBwAABB0BcLNCQAAAABAAAAAQAAACQAAABLAGUAeQAgAGQAbwBlAHMAIABuAG8AdAAgAGUAeABpAHMAdAAAQZAYCyYWAAAAAQAAAAEAAAAWAAAAfgBsAGkAYgAvAG0AYQBwAC4AdABzAABBwBgLGgoAAAABAAAAAQAAAAoAAAB0AHkAcABlACAAAEHgGAsWBgAAAAEAAAABAAAABgAAAHUANgA0AABBgBkLMCAAAAABAAAAAQAAACAAAAAgAGMAYQBuAG4AbwB0ACAAYgBlACAAbgB1AGwAbAAuAABBsBkLMCAAAAABAAAAAQAAACAAAABWAGEAbAB1AGUAIAB3AGkAdABoACAASwBlAHkAOgAgAABB4BkLJhYAAAABAAAAAQAAABYAAAAgAHcAaQB0AGgAIAB0AHkAcABlACAAAEGQGguOAX4AAAABAAAAAQAAAH4AAAAgAGkAcwAgAGEAbgAgADYANAAtAGIAaQB0ACAAaQBuAHQAZQBnAGUAcgAgAGEAbgBkACAAaQBzACAAZQB4AHAAZQBjAHQAZQBkACAAdABvACAAYgBlACAAZQBuAGMAbwBkAGUAZAAgAGEAcwAgAGEAIABzAHQAcgBpAG4AZwAAQaAbCyYWAAAAAQAAAAEAAAAWAAAAaABlAGEAcgB0AGIAZQBhAHQAOgAgAABB0BsLoAOQAQAAAQAAABsAAACQAQAAMAAwADAAMQAwADIAMAAzADAANAAwADUAMAA2ADAANwAwADgAMAA5ADEAMAAxADEAMQAyADEAMwAxADQAMQA1ADEANgAxADcAMQA4ADEAOQAyADAAMgAxADIAMgAyADMAMgA0ADIANQAyADYAMgA3ADIAOAAyADkAMwAwADMAMQAzADIAMwAzADMANAAzADUAMwA2ADMANwAzADgAMwA5ADQAMAA0ADEANAAyADQAMwA0ADQANAA1ADQANgA0ADcANAA4ADQAOQA1ADAANQAxADUAMgA1ADMANQA0ADUANQA1ADYANQA3ADUAOAA1ADkANgAwADYAMQA2ADIANgAzADYANAA2ADUANgA2ADYANwA2ADgANgA5ADcAMAA3ADEANwAyADcAMwA3ADQANwA1ADcANgA3ADcANwA4ADcAOQA4ADAAOAAxADgAMgA4ADMAOAA0ADgANQA4ADYAOAA3ADgAOAA4ADkAOQAwADkAMQA5ADIAOQAzADkANAA5ADUAOQA2ADkANwA5ADgAOQA5AABB8B4L5AEcAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAEAAAAAcAAAAQAAAAAAAAABAAAAAHAAAAEAAAAAcAAAAQAAAACwAAABAAAAAAAAAAmCBBAAAAAACTIAAAAgAAABAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAJMgAAACAAAAEAAAAAAAAAAQAAAAAAAAADEAAAACAAAAEAAAAAsAAACTBAAAAgAAABAAAAALAAAAEAAAAAsAAAAQAAAACwAAABAAAAALAAAAEAAAAAAAAAAAhT8EbmFtZQH9PpsBABN+bGliL2J1aWx0aW5zL2Fib3J0AT9ub2RlX21vZHVsZXMvbmVhci1zZGstYXMvYXNzZW1ibHkvcnVudGltZS9lbnYvaW1wb3J0cy9lbnYuaW5wdXQCRm5vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9ydW50aW1lL2Vudi9pbXBvcnRzL2Vudi5yZWdpc3Rlcl9sZW4DP25vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9ydW50aW1lL2Vudi9pbXBvcnRzL2Vudi5wYW5pYwRHbm9kZV9tb2R1bGVzL25lYXItc2RrLWFzL2Fzc2VtYmx5L3J1bnRpbWUvZW52L2ltcG9ydHMvZW52LnJlYWRfcmVnaXN0ZXIFMX5saWIvbmVhci1zZGstYXMvcnVudGltZS9lbnYvaW1wb3J0cy9lbnYubG9nX3V0ZjgGHH5saWIvcnQvc3R1Yi9tYXliZUdyb3dNZW1vcnkHFH5saWIvcnQvc3R1Yi9fX2FsbG9jCBV+bGliL3J0L3N0dWIvX19yZXRhaW4JNH5saWIvbmVhci1zZGstYXMvcnVudGltZS9zdG9yYWdlL1N0b3JhZ2UjY29uc3RydWN0b3IKJnN0YXJ0On5saWIvbmVhci1zZGstYXMvcnVudGltZS9zdG9yYWdlCzV+bGliL25lYXItc2RrLWFzL3J1bnRpbWUvY29udHJhY3QvQ29udGV4dCNjb25zdHJ1Y3Rvcgwnc3RhcnQ6fmxpYi9uZWFyLXNkay1hcy9ydW50aW1lL2NvbnRyYWN0DSRzdGFydDp+bGliL25lYXItc2RrLWFzL3J1bnRpbWUvaW5kZXgOMn5saWIvbmVhci1zZGstYXMvdm0vb3V0Y29tZS9SZXR1cm5EYXRhI2NvbnN0cnVjdG9yDyx+bGliL25lYXItc2RrLWFzL3ZtL291dGNvbWUvTm9uZSNjb25zdHJ1Y3RvchAhc3RhcnQ6fmxpYi9uZWFyLXNkay1hcy92bS9vdXRjb21lERxzdGFydDp+bGliL25lYXItc2RrLWFzL3ZtL3ZtEh9zdGFydDp+bGliL25lYXItc2RrLWFzL3ZtL2luZGV4ExxzdGFydDp+bGliL25lYXItc2RrLWFzL2luZGV4FBNzdGFydDphc3NlbWJseS9tYWluFUVub2RlX21vZHVsZXMvbmVhci1zZGstYXMvYXNzZW1ibHkvcnVudGltZS9zdG9yYWdlL1N0b3JhZ2UjY29uc3RydWN0b3IWN3N0YXJ0Om5vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9ydW50aW1lL3N0b3JhZ2UXRm5vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9ydW50aW1lL2NvbnRyYWN0L0NvbnRleHQjY29uc3RydWN0b3IYOHN0YXJ0Om5vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9ydW50aW1lL2NvbnRyYWN0GTVzdGFydDpub2RlX21vZHVsZXMvbmVhci1zZGstYXMvYXNzZW1ibHkvcnVudGltZS9pbmRleBodfmxpYi9zdHJpbmcvU3RyaW5nI2dldDpsZW5ndGgbHX5saWIvc3RyaW5nL1N0cmluZyNjaGFyQ29kZUF0HCZzdGFydDp+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vZGVjb2Rlch0XfmxpYi9tZW1vcnkvbWVtb3J5LmZpbGweFn5saWIvcnQvc3R1Yi9fX3JlbGVhc2UfLH5saWIvYXJyYXlidWZmZXIvQXJyYXlCdWZmZXJWaWV3I2NvbnN0cnVjdG9yIEZ+bGliL2FycmF5L0FycmF5PH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWU+I2NvbnN0cnVjdG9yITF+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyI2NvbnN0cnVjdG9yIl9+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vZGVjb2Rlci9KU09ORGVjb2Rlcjx+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyPiNjb25zdHJ1Y3RvciMjc3RhcnQ6fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04kJHN0YXJ0On5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9pbmRleCUvc3RhcnQ6bm9kZV9tb2R1bGVzL25lYXItc2RrLWFzL2Fzc2VtYmx5L2JpbmRnZW4mL25vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9iaW5kZ2VuL2lucHV0JzZub2RlX21vZHVsZXMvbmVhci1zZGstYXMvYXNzZW1ibHkvYmluZGdlbi9yZWdpc3Rlcl9sZW4oL25vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9iaW5kZ2VuL3BhbmljKSZ+bGliL3R5cGVkYXJyYXkvVWludDhBcnJheSNjb25zdHJ1Y3Rvcio3bm9kZV9tb2R1bGVzL25lYXItc2RrLWFzL2Fzc2VtYmx5L2JpbmRnZW4vcmVhZF9yZWdpc3Rlcis5fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvRGVjb2RlclN0YXRlI2NvbnN0cnVjdG9yLCV+bGliL3R5cGVkYXJyYXkvVWludDhBcnJheSNnZXQ6bGVuZ3RoLSB+bGliL3R5cGVkYXJyYXkvVWludDhBcnJheSNfX2dldC5cfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcGVla0NoYXIvYH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I2lzV2hpdGVzcGFjZTBcfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcmVhZENoYXIxYn5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I3NraXBXaGl0ZXNwYWNlMjR+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLlZhbHVlI2NvbnN0cnVjdG9yMyh+bGliL2FycmF5YnVmZmVyL0FycmF5QnVmZmVyI2NvbnN0cnVjdG9yNE9+bGliL21hcC9NYXA8fmxpYi9zdHJpbmcvU3RyaW5nLH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWU+I2NsZWFyNVV+bGliL21hcC9NYXA8fmxpYi9zdHJpbmcvU3RyaW5nLH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWU+I2NvbnN0cnVjdG9yNjB+bGliL2FycmF5L0FycmF5PH5saWIvc3RyaW5nL1N0cmluZz4jY29uc3RydWN0b3I3Mn5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uT2JqI2NvbnN0cnVjdG9yOC9+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLlZhbHVlLk9iamVjdDlFfmxpYi9hcnJheS9BcnJheTx+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLlZhbHVlPiNnZXQ6bGVuZ3RoOhd+bGliL3V0aWwvbWVtb3J5L21lbWNweTsXfmxpYi9tZW1vcnkvbWVtb3J5LmNvcHk8Fn5saWIvcnQvc3R1Yi9fX3JlYWxsb2M9FX5saWIvYXJyYXkvZW5zdXJlU2l6ZT4/fmxpYi9hcnJheS9BcnJheTx+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLlZhbHVlPiNwdXNoP0p+bGliL2FycmF5L0FycmF5PH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWU+I19fdW5jaGVja2VkX2dldEBAfmxpYi9hcnJheS9BcnJheTx+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLlZhbHVlPiNfX2dldEEufmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlciNnZXQ6cGVla0IUfmxpYi9ydC9fX2luc3RhbmNlb2ZDFn5saWIvdXRpbC9oYXNoL2hhc2hTdHJEHH5saWIvdXRpbC9zdHJpbmcvY29tcGFyZUltcGxFF35saWIvc3RyaW5nL1N0cmluZy5fX2VxRk5+bGliL21hcC9NYXA8fmxpYi9zdHJpbmcvU3RyaW5nLH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWU+I2ZpbmRHTX5saWIvbWFwL01hcDx+bGliL3N0cmluZy9TdHJpbmcsfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSlNPTi5WYWx1ZT4jaGFzSCl+bGliL2FycmF5L0FycmF5PH5saWIvc3RyaW5nL1N0cmluZz4jcHVzaElQfmxpYi9tYXAvTWFwPH5saWIvc3RyaW5nL1N0cmluZyx+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLlZhbHVlPiNyZWhhc2hKTX5saWIvbWFwL01hcDx+bGliL3N0cmluZy9TdHJpbmcsfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSlNPTi5WYWx1ZT4jc2V0Syt+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLk9iaiNfc2V0TFR+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLk9iaiNzZXQ8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSlNPTi5WYWx1ZT5NK35saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uQXJyI3B1c2hOLn5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXIjYWRkVmFsdWVPMH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXIjcHVzaE9iamVjdFAvfmxpYi9hcnJheWJ1ZmZlci9BcnJheUJ1ZmZlclZpZXcjZ2V0OmJ5dGVPZmZzZXRRNX5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi91dGlsL2luZGV4L0J1ZmZlci5nZXREYXRhUHRyUiR+bGliL3N0cmluZy9TdHJpbmcuVVRGOC5kZWNvZGVVbnNhZmVTNX5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi91dGlsL2luZGV4L0J1ZmZlci5yZWFkU3RyaW5nVDh+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vZGVjb2Rlci9EZWNvZGVyU3RhdGUjcmVhZFN0cmluZ1VDfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvRGVjb2RlclN0YXRlI3JlYWRTdHJpbmd8dHJhbXBvbGluZVYTfnNldEFyZ3VtZW50c0xlbmd0aFcvfmxpYi9hcnJheS9BcnJheTx+bGliL3N0cmluZy9TdHJpbmc+I2dldDpsZW5ndGhYIH5saWIvdXRpbC9zdHJpbmcvam9pblN0cmluZ0FycmF5WSl+bGliL2FycmF5L0FycmF5PH5saWIvc3RyaW5nL1N0cmluZz4jam9pbloVfmxpYi9ydC9fX2FsbG9jQnVmZmVyWxR+bGliL3J0L19fYWxsb2NBcnJheVxgfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcmVhZEhleERpZ2l0XSB+bGliL3N0cmluZy9TdHJpbmcuZnJvbUNvZGVQb2ludF4ffmxpYi9zdHJpbmcvU3RyaW5nLmZyb21DaGFyQ29kZV8qfmxpYi9zdHJpbmcvU3RyaW5nLmZyb21DaGFyQ29kZXx0cmFtcG9saW5lYBl+bGliL3N0cmluZy9TdHJpbmcjY29uY2F0YRt+bGliL3N0cmluZy9TdHJpbmcuX19jb25jYXRiY35saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I3JlYWRFc2NhcGVkQ2hhcmNefmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcmVhZFN0cmluZ2RcfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcGFyc2VLZXllPn5saWIvYXJyYXkvQXJyYXk8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSlNPTi5WYWx1ZT4jcG9wZi9+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyI3BvcE9iamVjdGdffmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcGFyc2VPYmplY3RoMn5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uQXJyI2NvbnN0cnVjdG9yaS5+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLlZhbHVlLkFycmF5ai9+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyI3B1c2hBcnJheWsufmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlciNwb3BBcnJheWxefmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcGFyc2VBcnJheW0yfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSlNPTi5TdHIjY29uc3RydWN0b3JuL35saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWUuU3RyaW5nby9+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyI3NldFN0cmluZ3BffmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcGFyc2VTdHJpbmdxYX5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I3JlYWRBbmRBc3NlcnRyM35saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uQm9vbCNjb25zdHJ1Y3RvcnMtfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSlNPTi5WYWx1ZS5Cb29sdDB+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyI3NldEJvb2xlYW51YH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I3BhcnNlQm9vbGVhbnYyfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSlNPTi5OdW0jY29uc3RydWN0b3J3L35saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWUuTnVtYmVyeDB+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyI3NldEludGVnZXJ5X35saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I3BhcnNlTnVtYmVyejN+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLk51bGwjY29uc3RydWN0b3J7LX5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWUuTnVsbHwtfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlciNzZXROdWxsfV1+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vZGVjb2Rlci9KU09ORGVjb2Rlcjx+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyPiNwYXJzZU51bGx+Xn5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I3BhcnNlVmFsdWV/X35saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I2Rlc2VyaWFsaXplgAErfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlciNyZXNldIEBRX5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL19KU09OLnBhcnNlPH5saWIvdHlwZWRhcnJheS9VaW50OEFycmF5PoIBMm5vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9iaW5kZ2VuL2dldElucHV0gwEXfmxpYi9zdHJpbmcvU3RyaW5nLl9fbmWEAU1+bGliL21hcC9NYXA8fmxpYi9zdHJpbmcvU3RyaW5nLH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWU+I2dldIUBKn5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uT2JqI2dldIYBP25vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9iaW5kZ2VuL2lzUmVhbGx5TnVsbGFibGU8dTY0PocBGH5saWIvdXRpbC9zdHJpbmcvaXNTcGFjZYgBHH5saWIvdXRpbC9zdHJpbmcvc3RydG9sPGk2ND6JARh+bGliL251bWJlci9VNjQucGFyc2VJbnSKAVxub2RlX21vZHVsZXMvbmVhci1zZGstYXMvYXNzZW1ibHkvYmluZGdlbi9kZWNvZGU8dTY0LH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uT2JqPosBH35saWIvdXRpbC9udW1iZXIvZGVjaW1hbENvdW50MzKMARt+bGliL3V0aWwvbnVtYmVyL3V0b2EzMl9sdXSNAR9+bGliL3V0aWwvbnVtYmVyL2RlY2ltYWxDb3VudDY0jgEbfmxpYi91dGlsL251bWJlci91dG9hNjRfbHV0jwEXfmxpYi91dGlsL251bWJlci91dG9hNjSQARp+bGliL3V0aWwvbnVtYmVyL2l0b2E8dTY0PpEBGH5saWIvbnVtYmVyL1U2NCN0b1N0cmluZ5IBG35saWIvc3RyaW5nL1N0cmluZyN0b1N0cmluZ5MBIn5saWIvc3RyaW5nL1N0cmluZy5VVEY4LmJ5dGVMZW5ndGiUAR5+bGliL3N0cmluZy9TdHJpbmcuVVRGOC5lbmNvZGWVASl+bGliL25lYXItc2RrLWFzL3J1bnRpbWUvdXRpbC91dGlsLnRvVVRGOJYBMH5saWIvbmVhci1zZGstYXMvcnVudGltZS91dGlsL3V0aWwuc3RyaW5nVG9CeXRlc5cBQH5saWIvbmVhci1zZGstYXMvcnVudGltZS9sb2dnaW5nL2xvZ2dpbmcubG9nPH5saWIvc3RyaW5nL1N0cmluZz6YARdhc3NlbWJseS9tYWluL2hlYXJ0YmVhdJkBIWFzc2VtYmx5L21haW4vX193cmFwcGVyX2hlYXJ0YmVhdJoBBn5zdGFydA==" + } + }, { + "AccessKey": { + "account_id": + "01.near", + "public_key": + "ed25519:6GxYiNnRLoKkjGeKA68hrfyrJC9tYSamGND5d23aXqRx", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "01.near", + "public_key": + "ed25519:E837NUYQLFgP9cLQou3nBSYzqFFhGffhYQLVzbwL5jtY", + "access_key": { + "nonce": 1, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "alex.near", + "public_key": + "ed25519:8fohZQ3DwXgVUXKJSoU9vi6iPyXKUKKff1T7sw4xj4wW", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "bo.near", + "public_key": + "ed25519:C5kXZP86M3DoWjPUwYr2QXkP7RoLj1hcF3kPFyoYcC4h", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "bot.pulse.near", + "public_key": + "ed25519:9x5kkFynLRojfwoVGbuZPSoRHEP5urze5xAbkybXHFBS", + "access_key": { + "nonce": 422638, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "bowen.near", + "public_key": + "ed25519:5LaQTGEqGZMrSQuypgR8zS3fQJRhVLgMtjFw7qBmWb8X", + "access_key": { + "nonce": 1, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "contributors.near", + "public_key": + "ed25519:BCCMGbV9FzTMTcwS67QCW1TrTmjuwFR1SrFPiG744kio", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "erik.near", + "public_key": + "ed25519:8fohZQ3DwXgVUXKJSoU9vi6iPyXKUKKff1T7sw4xj4wW", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "foundation.near", + "public_key": + "ed25519:GmtTh6yhWz6BmkA9AfnoQESKanDbBJDGfWVpW5wq9Uz", + "access_key": { + "nonce": 1, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "illia.near", + "public_key": + "ed25519:dQMV9776YrjHYLysrpQ7abkUmqiA5XLupvveVofYnvy", + "access_key": { + "nonce": 0, + "permission": { + "FunctionCall": { + "allowance": "0", + "receiver_id": "illia.near", + "method_names": ["__wallet__metadata"] + } + } + } + } + }, { + "AccessKey": { + "account_id": + "illia.near", + "public_key": + "ed25519:8fohZQ3DwXgVUXKJSoU9vi6iPyXKUKKff1T7sw4xj4wW", + "access_key": { + "nonce": 11, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "kendall.near", + "public_key": + "ed25519:3Puiccgti9iExBUucUxEdbVgeecRibK5FgENQVLHTg5t", + "access_key": { + "nonce": 0, + "permission": { + "FunctionCall": { + "allowance": "0", + "receiver_id": "kendall.near", + "method_names": ["__wallet__metadata"] + } + } + } + } + }, { + "AccessKey": { + "account_id": + "kendall.near", + "public_key": + "ed25519:DvabrRhC1TKXG8hWTGG2U3Ra5E4YXAF1azHdwSc61fs9", + "access_key": { + "nonce": 5, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "kendall.near", + "public_key": + "ed25519:J7PuMuFm34c19f324gFSQwMkaBG1DmwPaSEVZEbZw1nX", + "access_key": { + "nonce": 0, + "permission": { + "FunctionCall": { + "allowance": "0", + "receiver_id": "kendall.near", + "method_names": ["__wallet__metadata"] + } + } + } + } + }, { + "AccessKey": { + "account_id": + "ledger.vlad.near", + "public_key": + "ed25519:8g7GvgccAaub68HeSrmp6Aw2vYAvRYbLQZdEa6hZiG9X", + "access_key": { + "nonce": 1, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "ledger.vlad.near", + "public_key": + "ed25519:GgK5WqBhrhdwYDUgqsjKwDpnFWad4BgpJSNfH2VPs94v", + "access_key": { + "nonce": 0, + "permission": { + "FunctionCall": { + "allowance": "0", + "receiver_id": "ledger.vlad.near", + "method_names": ["__wallet__metadata"] + } + } + } + } + }, { + "AccessKey": { + "account_id": + "mike.near", + "public_key": + "ed25519:AhiKooGnQsw8S8WZ2V2xRGvpbZDY3yHFcTp4iCHYP8jo", + "access_key": { + "nonce": 1, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "mikemikemikemikemikemikemikemike", + "public_key": + "ed25519:AhiKooGnQsw8S8WZ2V2xRGvpbZDY3yHFcTp4iCHYP8jo", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "near", + "public_key": + "ed25519:5zset1JX4qp4PcR3N9KDSY6ATdgkrbBW5wFBGWC4ZjnU", + "access_key": { + "nonce": 8, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "nfvalidator1.near", + "public_key": + "ed25519:Fd2TW6TtTDL5hiY58pbTVYfTBSNyWLgHGxiD9mcHgQ92", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "nfvalidator2.near", + "public_key": + "ed25519:4rg9rmbxuSM7bX8z8989LTmBiM6JNnE4w9LZ8KkuCcfq", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "nfvalidator3.near", + "public_key": + "ed25519:EVyX7KE6e2KD3CzpoN1kvzJATsS5KxkjbMCCYHbM3vRr", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "nfvalidator4.near", + "public_key": + "ed25519:CrLQzMvfSDWnTYzfbEzcJ3hdetnpYdsQnvbhuzwHBtAG", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "patrick.near", + "public_key": + "ed25519:8MPLjkG12V5AQfCogZhjrWe5k6PoRzNtLUb2eD1r7fyU", + "access_key": { + "nonce": 3, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "patrick.near", + "public_key": + "ed25519:BHTmjrvg2UWxBjzSwDyhkc2FYJseSduWVe7YXBS2Rms1", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "peter.near", + "public_key": + "ed25519:HDybq3JWgmbaiCKtE27T75iYVkEoA8cH6rfnut77ZVY1", + "access_key": { + "nonce": 1, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "pulse.near", + "public_key": + "ed25519:3BWDipnJmNfWT7YSBGZu63dkfMBoZDUqWJsctNGBDinE", + "access_key": { + "nonce": 3, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "registrar", + "public_key": + "ed25519:Fm9g4GQeQrnwknCVexuPvn3owgrYvMbZhPRoXKpj2wX6", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "treasury.near", + "public_key": + "ed25519:CzAXM8NcumuHPYJYnjq5tUX5v92GHdbYZfmfKFwDNzBZ", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "vlad.near", + "public_key": + "ed25519:2nE29FtYYZrT2owygL3FN9CLVBs9wdUy1r6pdpuScazs", + "access_key": { + "nonce": 2, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "vlad.near", + "public_key": + "ed25519:4GnS8L8hnCNWh4saWPPAVxto1VFtVdmY27mkrXLeSxgp", + "access_key": { + "nonce": 1, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "vlad.near", + "public_key": + "ed25519:9xLURZGus8bU4Qnf9AC3jmJhHNBo7Ydh17w7nJAY2L78", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "wallet.pulse.near", + "public_key": + "ed25519:9783PHB4mZXYFopqXcypm4TCv2LoAbAdmj24AA9YJ2C6", + "access_key": { + "nonce": 2, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "wallet.pulse.near", + "public_key": + "ed25519:BJ3wDgNtiMa22d8iCKmzbGA7YTiSWv9J33NTftekUcoZ", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "AccessKey": { + "account_id": + "yifang.near", + "public_key": + "ed25519:CKUb9VneyN1XFMXcvEc55aKiDpirdDim8Dd4cAyzefF1", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, { + "Data": { + "account_id": + "near", + "data_key": + "U1RBVEU=", + "value": + "CQAAAAAAAAAAAAAAaQAAAAAAAAAACQAAAAAAAAAAAAAAawAAAAAAAAAACQAAAAAAAAAAAAAAdg==" + } + }] + }) + +# patch should succeed +res = nodes[0].call_function("test0", "read_value", + base64.b64encode(k).decode('ascii')) +assert (res['result']['result'] == list(new_v)) diff --git a/pytest/tests/sanity/__init__.py b/pytest/tests/sanity/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pytest/tests/sanity/backward_compatible.py b/pytest/tests/sanity/backward_compatible.py new file mode 100755 index 000000000..025c5307c --- /dev/null +++ b/pytest/tests/sanity/backward_compatible.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +""" +This script runs node from stable branch and from current branch and makes +sure they are backward compatible. +""" + +import sys +import os +import subprocess +import time +import base58 +import json +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import branches +import cluster +from transaction import sign_deploy_contract_tx, sign_function_call_tx, sign_payment_tx, sign_create_account_with_full_access_key_and_balance_tx +import utils + + +def main(): + node_root = utils.get_unc_tempdir('backward', clean=True) + executables = branches.prepare_ab_test() + + # Setup local network. + subprocess.check_call((executables.stable.uncd, f'--home={node_root}', + 'localnet', '-v', '2', '--prefix', 'test')) + + # Run both binaries at the same time. + config = executables.stable.node_config() + stable_node = cluster.spin_up_node(config, executables.stable.root, + str(node_root / 'test0'), 0) + config = executables.current.node_config() + current_node = cluster.spin_up_node(config, + executables.current.root, + str(node_root / 'test1'), + 1, + boot_node=stable_node) + + # Check it all works. + BLOCKS = 100 + max_height = -1 + started = time.time() + + # Create account, transfer tokens, deploy contract, invoke function call + block_hash = stable_node.get_latest_block().hash_bytes + + new_account_id = 'test_account.test0' + new_signer_key = cluster.Key(new_account_id, stable_node.signer_key.pk, + stable_node.signer_key.sk) + create_account_tx = sign_create_account_with_full_access_key_and_balance_tx( + stable_node.signer_key, new_account_id, new_signer_key, 10**24, 1, + block_hash) + res = stable_node.send_tx_and_wait(create_account_tx, timeout=20) + assert 'error' not in res, res + assert 'Failure' not in res['result']['status'], res + + transfer_tx = sign_payment_tx(stable_node.signer_key, new_account_id, + 10**25, 2, block_hash) + res = stable_node.send_tx_and_wait(transfer_tx, timeout=20) + assert 'error' not in res, res + + block_height = stable_node.get_latest_block().height + nonce = block_height * 1_000_000 - 1 + + tx = sign_deploy_contract_tx(new_signer_key, utils.load_test_contract(), + nonce, block_hash) + res = stable_node.send_tx_and_wait(tx, timeout=20) + assert 'error' not in res, res + + tx = sign_deploy_contract_tx(stable_node.signer_key, + utils.load_test_contract(), 3, block_hash) + res = stable_node.send_tx_and_wait(tx, timeout=20) + assert 'error' not in res, res + + tx = sign_function_call_tx(new_signer_key, new_account_id, + 'write_random_value', [], 10**13, 0, nonce + 1, + block_hash) + res = stable_node.send_tx_and_wait(tx, timeout=20) + assert 'error' not in res, res + assert 'Failure' not in res['result']['status'], res + + data = json.dumps([{ + "create": { + "account_id": "test_account.test0", + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": 30000000000000, + }, + "id": 0 + }, { + "then": { + "promise_index": 0, + "account_id": "test0", + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": 30000000000000, + }, + "id": 1 + }]) + + tx = sign_function_call_tx(stable_node.signer_key, + new_account_id, 'call_promise', + bytes(data, 'utf-8'), 90000000000000, 0, + nonce + 2, block_hash) + res = stable_node.send_tx_and_wait(tx, timeout=20) + + assert 'error' not in res, res + assert 'Failure' not in res['result']['status'], res + + utils.wait_for_blocks(current_node, target=BLOCKS) + + +if __name__ == "__main__": + main() diff --git a/pytest/tests/sanity/block_chunk_signature.py b/pytest/tests/sanity/block_chunk_signature.py new file mode 100755 index 000000000..ad173a777 --- /dev/null +++ b/pytest/tests/sanity/block_chunk_signature.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# Test for #3368 +# +# Create a proxy that nullifies chunk signatures in blocks, test that blocks get rejected. +import sys, time, asyncio +import pathlib +import logging + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +from peer import * +from proxy import ProxyHandler + + +class Handler(ProxyHandler): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.blocks = 0 + + async def handle(self, msg, fr, to): + if msg.enum == 'Block' and msg.Block.chunks( + )[0].signature.data != bytes(64): + msg.Block.chunks()[0].signature.data = bytes(64) + self.blocks += 1 + # Node gets banned by the peer after sending one block + assert self.blocks <= 2 + return msg + return True + + +if __name__ == '__main__': + nodes = start_cluster(2, 0, 1, None, [], {}, Handler) + + time.sleep(5) + h0 = nodes[0].get_latest_block(verbose=True).height + h1 = nodes[1].get_latest_block(verbose=True).height + assert h0 <= 3 and h1 <= 3 diff --git a/pytest/tests/sanity/block_production.py b/pytest/tests/sanity/block_production.py new file mode 100755 index 000000000..afd09e06a --- /dev/null +++ b/pytest/tests/sanity/block_production.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +# Spins up four nodes, and waits until they produce 50 blocks. +# Ensures that the nodes remained in sync throughout the process +# Sets epoch length to 20 +# The nodes track all shards. + +# Local: +# python tests/sanity/block_production.py +# Remote: +# unc_PYTEST_CONFIG=remote.json python tests/sanity/block_production.py + +# Same for all tests that call start_cluster with a None config + +import sys, time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger + +TIMEOUT = 150 +EPOCH_LENGTH = 20 +BLOCKS = EPOCH_LENGTH * 5 + +node_config = { + "tracked_shards": [0], # Track all shards. +} + +nodes = start_cluster( + 4, 0, 4, None, + [["epoch_length", EPOCH_LENGTH], ["block_producer_kickout_threshold", 60], + ["chunk_producer_kickout_threshold", 60]], { + 0: node_config, + 1: node_config, + 2: node_config, + 3: node_config, + }) + +started = time.time() + +max_height = 0 +last_heights = [0 for _ in nodes] +seen_heights = [set() for _ in nodes] +last_common = [[0 for _ in nodes] for _ in nodes] + +height_to_hash = {} + +# the test relies on us being able to query heights faster +# than the blocks are produced. Validating store takes up +# to 350ms on slower hardware for this test, multiplied +# by four nodes querying heights every 1 second becomes +# unfeasible +for node in nodes: + node.stop_checking_store() + + +def min_common(): + return min([min(x) for x in last_common]) + + +def heights_report(): + for i, sh in enumerate(seen_heights): + logger.info("Node %s: %s" % (i, sorted(list(sh)))) + + +while max_height < BLOCKS: + assert time.time() - started < TIMEOUT + for i, node in enumerate(nodes): + block = node.get_latest_block() + height = block.height + hash_ = block.hash + + if height > max_height: + max_height = height + if height % 10 == 0: + logger.info("Reached height %s, min common: %s" % + (height, min_common())) + + if height not in height_to_hash: + height_to_hash[height] = hash_ + else: + assert height_to_hash[ + height] == hash_, "height: %s, h1: %s, h2: %s" % ( + height, hash_, height_to_hash[height]) + + last_heights[i] = height + seen_heights[i].add(height) + for j, _ in enumerate(nodes): + if height in seen_heights[j]: + last_common[i][j] = height + last_common[j][i] = height + + # during the time it took to start the test some blocks could have been produced, so the first observed height + # could be higher than 2, at which point for the nodes for which we haven't queried the height yet the + # `min_common` is zero. Once we queried each node at least once, we expect the difference between the last + # queried heights to never differ by more than two. + if min_common() > 0: + assert min_common() + 2 >= height, heights_report() + +assert min_common() + 2 >= BLOCKS, heights_report() + +doomslug_final_block = nodes[0].json_rpc('block', {'finality': 'unc-final'}) +assert (doomslug_final_block['result']['header']['height'] >= BLOCKS - 10) + +nfg_final_block = nodes[0].json_rpc('block', {'finality': 'final'}) +assert (nfg_final_block['result']['header']['height'] >= BLOCKS - 10) diff --git a/pytest/tests/sanity/block_sync.py b/pytest/tests/sanity/block_sync.py new file mode 100755 index 000000000..4e83ca648 --- /dev/null +++ b/pytest/tests/sanity/block_sync.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# Spins up two validating nodes. Make one validator produce block every 100 seconds. +# Let the validators produce blocks for a while and then shut one of them down, remove data and restart. +# Check that it can sync to the validator through block sync. + +import sys, time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +import utils + +BLOCKS = 10 + +consensus_config0 = { + "consensus": { + "block_fetch_horizon": 30, + "block_header_fetch_horizon": 30 + } +} +consensus_config1 = { + "consensus": { + "min_block_production_delay": { + "secs": 100, + "nanos": 0 + }, + "max_block_production_delay": { + "secs": 200, + "nanos": 0 + }, + "max_block_wait_delay": { + "secs": 1000, + "nanos": 0 + } + } +} +# give more stake to the bootnode so that it can produce the blocks alone +nodes = start_cluster( + 2, 0, 4, None, + [["epoch_length", 100], ["num_block_producer_seats", 100], + ["num_block_producer_seats_per_shard", [25, 25, 25, 25]], + ["validators", 0, "amount", "110000000000000000000000000000000"], + [ + "records", 0, "Account", "account", "locked", + "110000000000000000000000000000000" + ], ["total_supply", "3060000000000000000000000000000000"]], { + 0: consensus_config0, + 1: consensus_config1 + }) +time.sleep(3) + +utils.wait_for_blocks(nodes[0], target=BLOCKS) + +logger.info("kill node 0") +nodes[0].kill() +nodes[0].reset_data() + +logger.info("restart node 0") +nodes[0].start(boot_node=nodes[0]) +time.sleep(3) + +node1_height = nodes[1].get_latest_block().height +utils.wait_for_blocks(nodes[0], target=node1_height) diff --git a/pytest/tests/sanity/block_sync_archival.py b/pytest/tests/sanity/block_sync_archival.py new file mode 100755 index 000000000..c1ad2828d --- /dev/null +++ b/pytest/tests/sanity/block_sync_archival.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +"""Tests that archival node can sync up history from another archival node. + +The overview of this test is that it starts archival nodes which need to sync +their state from already running archival nodes. The test can be divided into +two stages: + +1. The test first starts a validator and an observer node (let’s call it Fred). + Both configured as archival nodes. It then waits for several epochs worth of + blocks to be generated and received by the observer node. Once that happens, + the test kills the validator node so that no new blocks are generated. + + At this stage, the test verifies that Fred can sync correctly and that the + boot node serves all partial chunks requests from its in-memory cache (which + is determined by looking at Prometheus metrics). + +2. The test then restarts Fred so that its in-memory cache is cleared. It + finally starts a new observer (let’s call it Barney) and points it at Fred as + a boot node. The test waits for Barney to synchronise with Fred and then + verifies that all the blocks have been correctly fetched. + + At this stage, the test verifies that Barney synchronises correctly and that + Fred serves all requests from storage (since it's in-memory cache has been + cleared). This is again done through Prometheus metrics and in addition the + test verifies that data from DBCol::Chunks and DBCol::PartialChunks was used. This + also implies that Fred correctly performed DBCol::PartialChunks garbage + collection. +""" + +import argparse +import datetime +import pathlib +import sys +import typing + +import prometheus_client.parser +import requests + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import cluster +from configured_logger import logger +import utils + +EPOCH_LENGTH = 20 +TARGET_HEIGHT = 20 * EPOCH_LENGTH + +_DurationMaybe = typing.Optional[datetime.timedelta] + + +class Cluster: + + def __init__(self): + node_config = { + 'archive': True, + 'tracked_shards': [0], + } + + self._config = cluster.load_config() + self._unc_root, self._node_dirs = cluster.init_cluster( + num_nodes=1, + num_observers=2, + num_shards=1, + config=self._config, + genesis_config_changes=[['epoch_length', EPOCH_LENGTH], + ['block_producer_kickout_threshold', 80]], + client_config_changes={ + 0: node_config, + 1: node_config, + 2: node_config, + 3: node_config + }) + self._nodes = [None] * len(self._node_dirs) + + def start_node( + self, ordinal: int, *, + boot_node: typing.Optional[cluster.BaseNode]) -> cluster.BaseNode: + assert self._nodes[ordinal] is None + self._nodes[ordinal] = node = cluster.spin_up_node( + self._config, + self._unc_root, + self._node_dirs[ordinal], + ordinal, + boot_node=boot_node, + single_node=not ordinal) + return node + + def __enter__(self): + return self + + def __exit__(self, *_): + for node in self._nodes: + if node: + node.cleanup() + + +# TODO(#6458): Move this to separate file and merge with metrics module. +def get_metrics(node_name: str, + node: cluster.BootNode) -> typing.Dict[str, int]: + """Fetches partial encoded chunk request count metrics from node. + + Args: + node_name: Node’s name used when logging the counters. This is purely + for debugging. + node: Node to fetch metrics from. + + Returns: + A `{key: count}` dictionary where key is in ‘method/success’ format. + The values correspond to the + unc_partial_encoded_chunk_request_processing_time_count Prometheus + metric. + """ + url = 'http://{}:{}/metrics'.format(*node.rpc_addr()) + response = requests.get(url) + response.raise_for_status() + + metric_name = 'unc_partial_encoded_chunk_request_processing_time' + histogram = next( + (metric + for metric in prometheus_client.parser.text_string_to_metric_families( + response.content.decode('utf8')) + if metric.name == metric_name), None) + if not histogram: + return {} + + counts = dict((sample.labels['method'] + '/' + sample.labels['success'], + int(sample.value)) + for sample in histogram.samples + if sample.name.endswith('_count')) + logger.info(f'{node_name} counters: ' + '; '.join( + f'{key}: {count}' for key, count in sorted(counts.items()))) + return counts + + +def assert_metrics(metrics: typing.Dict[str, int], + allowed_non_zero: typing.Sequence[str]) -> None: + """Asserts that only given keys are non-zero. + + Args: + metrics: Metrics as returned by get_metrics() function. + allowed_non_zero: Keys that are expected to be non-zero in the metrics. + """ + for key in allowed_non_zero: + assert metrics.get(key), f'Expected {key} to be non-zero' + for key, count in metrics.items(): + ok = key in allowed_non_zero or not count + assert ok, f'Expected {key} to be zero but got {count}' + + +def get_all_blocks(node: cluster.BaseNode) -> typing.Sequence[cluster.BlockId]: + """Returns all blocks from given head down to genesis block.""" + ids = [] + block_hash = node.get_latest_block().hash + while block_hash != '11111111111111111111111111111111': + block = node.get_block(block_hash) + assert 'result' in block, block + header = block['result']['header'] + ids.append(cluster.BlockId.from_header(header)) + block_hash = header.get('prev_hash') + return list(reversed(ids)) + + +def run_test(cluster: Cluster) -> None: + # Start the validator and the first observer. Wait until the observer + # synchronises a few epoch’s worth of blocks to be generated and then kill + # validator so no more blocks are generated. + boot = cluster.start_node(0, boot_node=None) + fred = cluster.start_node(1, boot_node=boot) + utils.wait_for_blocks(fred, target=TARGET_HEIGHT, poll_interval=1) + metrics = get_metrics('boot', boot) + boot.kill() + + # We didn’t generate enough blocks to fill boot’s in-memory cache which + # means all Fred’s requests should be served from it. + assert_metrics(metrics, ('cache/ok',)) + + # Restart Fred so that its cache is cleared. Then start the second + # observer, Barney, and wait for it to sync up. + fred_blocks = get_all_blocks(fred) + fred.kill(gentle=True) + fred.start() + + barney = cluster.start_node(2, boot_node=fred) + utils.wait_for_blocks(barney, + target=fred_blocks[-1].height, + poll_interval=1) + barney_blocks = get_all_blocks(barney) + if fred_blocks != barney_blocks: + for f, b in zip(fred_blocks, barney_blocks): + if f != b: + logger.error(f'{f} != {b}') + assert False + + # Since Fred’s in-memory cache is clear, all Barney’s requests are served + # from storage. Since DBCol::PartialChunks is garbage collected, some of the + # requests are served from DBCol::Chunks. + assert_metrics(get_metrics('fred', fred), ( + 'chunk/ok', + 'partial/ok', + )) + + +if __name__ == '__main__': + with Cluster() as cl: + run_test(cl) diff --git a/pytest/tests/sanity/block_sync_flat_storage.py b/pytest/tests/sanity/block_sync_flat_storage.py new file mode 100755 index 000000000..0f6e1820f --- /dev/null +++ b/pytest/tests/sanity/block_sync_flat_storage.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +# Spins up one validating node. +# Spins a non-validating node that tracks all shards. +# In the middle of an epoch, the node gets stopped, and the set of tracked shards gets reduced. +# Test that the node correctly handles chunks for the shards that it will care about in the next epoch. +# Spam transactions that require the node to use flat storage to process them correctly. + +import pathlib +import random +import sys + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import init_cluster, spin_up_node, load_config, apply_config_changes +import account +import transaction +import utils + +EPOCH_LENGTH = 30 + +config0 = { + 'tracked_shards': [0], +} +config1 = { + 'tracked_shards': [0], +} + +config = load_config() +unc_root, node_dirs = init_cluster(1, 1, 4, config, + [["epoch_length", EPOCH_LENGTH]], { + 0: config0, + 1: config1 + }) + +boot_node = spin_up_node(config, unc_root, node_dirs[0], 0) +node1 = spin_up_node(config, unc_root, node_dirs[1], 1, boot_node=boot_node) + +contract_key = boot_node.signer_key +contract = utils.load_test_contract() +latest_block_hash = boot_node.get_latest_block().hash_bytes +deploy_contract_tx = transaction.sign_deploy_contract_tx( + contract_key, contract, 10, latest_block_hash) +result = boot_node.send_tx_and_wait(deploy_contract_tx, 10) +assert 'result' in result and 'error' not in result, ( + 'Expected "result" and no "error" in response, got: {}'.format(result)) + + +def random_workload_until(target, nonce, keys): + while True: + nonce += 1 + height = boot_node.get_latest_block().height + if height > target: + break + if (len(keys) > 100 and random.random() < 0.2) or len(keys) > 1000: + key = keys[random.randint(0, len(keys) - 1)] + call_function(boot_node, 'read', key, nonce) + else: + key = random_u64() + keys.append(key) + call_function(boot_node, 'write', key, nonce) + return (nonce, keys) + + +def random_u64(): + return bytes(random.randint(0, 255) for _ in range(8)) + + +def call_function(node, op, key, nonce): + last_block_hash = node.get_latest_block().hash_bytes + if op == 'read': + args = key + fn = 'read_value' + else: + args = key + random_u64() + fn = 'write_key_value' + + tx = transaction.sign_function_call_tx(node.signer_key, + node.signer_key.account_id, fn, args, + 300 * account.TGAS, 0, nonce, + last_block_hash) + return node.send_tx(tx).get('result') + + +nonce, keys = random_workload_until(EPOCH_LENGTH + 5, 1, []) + +node1.kill() +# Reduce the set of tracked shards and make it variable in time. +# The node is stopped in epoch_height = 1. +# Change the config of tracked shards such that after restart the node cares +# only about shard 0, and in the next epoch it will care about shards [1, 2, 3]. +apply_config_changes(node_dirs[1], { + "tracked_shards": [], + "tracked_shard_schedule": [[0], [0], [1, 2, 3]] +}) + +# Run node0 more to trigger block sync in node1. +nonce, keys = random_workload_until(EPOCH_LENGTH * 2 + 1, nonce, keys) + +# Node1 is now behind and needs to do header sync and block sync. +node1.start(boot_node=boot_node) +utils.wait_for_blocks(node1, target=EPOCH_LENGTH * 2 + 10) diff --git a/pytest/tests/sanity/catchup_flat_storage_deletions.py b/pytest/tests/sanity/catchup_flat_storage_deletions.py new file mode 100755 index 000000000..03b9d119b --- /dev/null +++ b/pytest/tests/sanity/catchup_flat_storage_deletions.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +# Spins up a validator node tracking all shards and a non-validator node. +# Deletes an account. +# Restart the non-validator node. +# After the non-validator node does catchup, attempt to send a token to the deleted account. +# Observe that both nodes correctly execute the transaction and return an error. + +import pathlib +import sys + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster, load_config +import state_sync_lib +import transaction +import utils + +from configured_logger import logger + +EPOCH_LENGTH = 10 + + +def print_balances(nodes, account_ids): + for node in nodes: + for account_id in account_ids: + res = node.json_rpc( + 'query', { + 'request_type': 'view_account', + 'account_id': account_id, + 'finality': 'optimistic' + }) + logger.info( + f"Lookup account '{account_id}' in node '{node.signer_key.account_id}': {res['result'] if 'result' in res else res['error']}" + ) + + +def main(): + node_config_dump, node_config_sync = state_sync_lib.get_state_sync_configs_pair( + ) + # The schedule means that the node tracks all shards all the time except for epoch heights 2 and 3. + # Those epochs correspond to block heights [EPOCH_LENGTH * 2 + 1, EPOCH_LENGTH * 4]. + node_config_sync["tracked_shard_schedule"] = [ + [0], # epoch_height = 0 and 4 + [0], # epoch_height = 1* and 1 and 5 + [], # epoch_height = 2 + [], # epoch_height = 3 + ] + node_config_sync["tracked_shards"] = [] + + config = load_config() + nodes = start_cluster(1, 1, 1, config, [["epoch_length", EPOCH_LENGTH]], { + 0: node_config_dump, + 1: node_config_sync + }) + [boot_node, node] = nodes + + logger.info('started the nodes') + + test_account_id = node.signer_key.account_id + test_account_key = node.signer_key + account_ids = [boot_node.signer_key.account_id, test_account_id] + + print_balances(nodes, account_ids) + + nonce = 10 + + latest_block = utils.wait_for_blocks(boot_node, + target=int(2.5 * EPOCH_LENGTH)) + epoch = state_sync_lib.approximate_epoch_height(latest_block.height, + EPOCH_LENGTH) + assert epoch == 2, f"epoch: {epoch}" + node.kill() + # Restart the node to make it start without opening any flat storages. + node.start(boot_node=boot_node) + logger.info(f'We are in epoch {epoch}, and the node is restarted') + + print_balances(nodes, account_ids) + + # Delete the account. + latest_block_hash = boot_node.get_latest_block().hash_bytes + nonce += 1 + tx = transaction.sign_delete_account_tx(test_account_key, test_account_id, + boot_node.signer_key.account_id, + nonce, latest_block_hash) + result = boot_node.send_tx(tx) + logger.info(result) + logger.info(f'Deleted {test_account_id}') + + # Wait until the node tracks the shard and probably does the catchup. + latest_block = utils.wait_for_blocks(boot_node, + target=int(4.5 * EPOCH_LENGTH)) + epoch = state_sync_lib.approximate_epoch_height(latest_block.height, + EPOCH_LENGTH) + assert epoch == 4, f"epoch: {epoch}" + logger.info(f'We are in epoch {epoch}') + + # Ensure the non-validator node has caught up. + utils.wait_for_blocks(node, target=int(4.5 * EPOCH_LENGTH)) + logger.info(f'The other node is in sync') + + print_balances(nodes, account_ids) + + # Check that the lookup of a deleted account returns an error. Because it's deleted. + test_account_balance = node.json_rpc( + 'query', { + 'request_type': 'view_account', + 'account_id': test_account_id, + 'finality': 'optimistic' + }) + assert 'error' in test_account_balance, test_account_balance + + # Send tokens. + # The transaction will be accepted and will detect that the receiver account was deleted. + latest_block_hash = boot_node.get_latest_block().hash_bytes + nonce += 1 + tx = transaction.sign_payment_tx(boot_node.signer_key, test_account_id, 1, + nonce, latest_block_hash) + logger.info( + f'Sending a token from {boot_node.signer_key.account_id} to {test_account_id} now' + ) + result = boot_node.send_tx_and_wait(tx, 10) + assert 'result' in result and 'error' not in result, ( + 'Expected "result" and no "error" in response, got: {}'.format(result)) + + print_balances(nodes, account_ids) + + # Wait a bit more and check that the non-validator node is in sync. + utils.wait_for_blocks(boot_node, target=int(5.8 * EPOCH_LENGTH)) + boot_node_latest_block_height = boot_node.get_latest_block().height + node_latest_block_height = node.get_latest_block().height + logger.info( + f'The validator node is at block height {boot_node_latest_block_height} in epoch {state_sync_lib.approximate_epoch_height(boot_node_latest_block_height, EPOCH_LENGTH)}' + ) + logger.info( + f'The non-validator node is at block height {node_latest_block_height}') + + # Check that the non-validator node is not stuck, and is in-sync. + assert boot_node_latest_block_height < int( + 0.5 * EPOCH_LENGTH) + node_latest_block_height + + +if __name__ == "__main__": + main() diff --git a/pytest/tests/sanity/concurrent_function_calls.py b/pytest/tests/sanity/concurrent_function_calls.py new file mode 100755 index 000000000..71ca9d8bf --- /dev/null +++ b/pytest/tests/sanity/concurrent_function_calls.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# Spins up four nodes, deploy an smart contract to one node, +# Call a smart contract method in another node + +import sys, time +import base58 +import base64 +import multiprocessing +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) +from cluster import start_cluster +from configured_logger import logger +from transaction import sign_deploy_contract_tx, sign_function_call_tx +from utils import load_test_contract + +nodes = start_cluster( + 4, 1, 4, None, + [["epoch_length", 10], ["block_producer_kickout_threshold", 80]], + {4: { + "tracked_shards": [0, 1, 2, 3] + }}) + +# Deploy contract +hash_ = nodes[0].get_latest_block().hash_bytes +tx = sign_deploy_contract_tx(nodes[0].signer_key, load_test_contract(), 10, + hash_) +nodes[0].send_tx(tx) + +time.sleep(3) + +# Write 10 values to storage +for i in range(10): + hash_ = nodes[1].get_latest_block().hash_bytes + keyvalue = bytearray(16) + keyvalue[0] = i + keyvalue[8] = i + tx2 = sign_function_call_tx(nodes[0].signer_key, + nodes[0].signer_key.account_id, + 'write_key_value', bytes(keyvalue), + 10000000000000, 100000000000, 20 + i * 10, + hash_) + res = nodes[1].send_tx(tx2) + +time.sleep(3) +acc_id = nodes[0].signer_key.account_id + + +def process(): + for i in range(100): + key = bytearray(8) + key[0] = i % 10 + res = nodes[4].call_function( + acc_id, 'read_value', + base64.b64encode(bytes(key)).decode("ascii")) + res = int.from_bytes(res["result"]["result"], byteorder='little') + assert res == (i % 10) + logger.info("all done") + + +ps = [multiprocessing.Process(target=process, args=()) for i in range(6)] +for p in ps: + p.start() + +for p in ps: + p.join() diff --git a/pytest/tests/sanity/db_migration.py b/pytest/tests/sanity/db_migration.py new file mode 100755 index 000000000..dd6263301 --- /dev/null +++ b/pytest/tests/sanity/db_migration.py @@ -0,0 +1,120 @@ +#!/usr/bin/python3 +""" +Spins up a node with old version and wait until it produces some blocks. +Shutdowns the node and restarts with the same data folder with the new binary. +Makes sure that the node can still produce blocks. +""" + +import json +import logging +import os +import sys +import time +import subprocess +import base58 +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import branches +import cluster +from transaction import sign_deploy_contract_tx, sign_function_call_tx +import utils + +logging.basicConfig(level=logging.INFO) + + +def deploy_contract(node): + hash_ = node.get_latest_block().hash_bytes + tx = sign_deploy_contract_tx(node.signer_key, utils.load_test_contract(), + 10, hash_) + node.send_tx_and_wait(tx, timeout=15) + utils.wait_for_blocks(node, count=3) + + +def send_some_tx(node): + # Write 10 values to storage + nonce = node.get_nonce_for_pk(node.signer_key.account_id, + node.signer_key.pk) + 10 + for i in range(10): + hash_ = node.get_latest_block().hash_bytes + keyvalue = bytearray(16) + keyvalue[0] = (nonce // 10) % 256 + keyvalue[8] = (nonce // 10) % 255 + tx2 = sign_function_call_tx(node.signer_key, node.signer_key.account_id, + 'write_key_value', bytes(keyvalue), + 10000000000000, 100000000000, nonce, hash_) + nonce += 10 + res = node.send_tx_and_wait(tx2, timeout=15) + assert 'error' not in res, res + assert 'Failure' not in res['result']['status'], res + utils.wait_for_blocks(node, count=3) + + +def main(): + executables = branches.prepare_ab_test() + node_root = utils.get_unc_tempdir('db_migration', clean=True) + + logging.info(f"The near root is {executables.stable.root}...") + logging.info(f"The node root is {node_root}...") + + # Init local node + subprocess.call(( + executables.stable.uncd, + "--home=%s" % node_root, + "init", + "--fast", + )) + + # Adjust changes required since #7486. This is needed because current + # stable release populates the deprecated migration configuration options. + # TODO(mina86): Remove this once we get stable release which doesn’t + # populate those fields by default. + config_path = node_root / 'config.json' + data = json.loads(config_path.read_text(encoding='utf-8')) + data.pop('db_migration_snapshot_path', None) + data.pop('use_db_migration_snapshot', None) + config_path.write_text(json.dumps(data), encoding='utf-8') + + # Run stable node for few blocks. + logging.info("Starting the stable node...") + config = executables.stable.node_config() + node = cluster.spin_up_node(config, executables.stable.root, str(node_root), + 0) + + logging.info("Running the stable node...") + utils.wait_for_blocks(node, count=20) + logging.info("Blocks are being produced, sending some tx...") + deploy_contract(node) + send_some_tx(node) + + node.kill() + + logging.info( + "Stable node has produced blocks... Stopping the stable node... ") + + # Run new node and verify it runs for a few more blocks. + logging.info("Starting the current node...") + config = executables.current.node_config() + node.unc_root = executables.current.root + node.binary_name = executables.current.uncd + node.start(boot_node=node) + + logging.info("Running the current node...") + utils.wait_for_blocks(node, count=20) + logging.info("Blocks are being produced, sending some tx...") + send_some_tx(node) + + logging.info( + "Currnet node has produced blocks... Stopping the current node... ") + + node.kill() + + logging.info("Restarting the current node...") + + node.start(boot_node=node) + utils.wait_for_blocks(node, count=20) + + +if __name__ == "__main__": + main() diff --git a/pytest/tests/sanity/docker.py b/pytest/tests/sanity/docker.py new file mode 100644 index 000000000..d52436b7f --- /dev/null +++ b/pytest/tests/sanity/docker.py @@ -0,0 +1,247 @@ +#!/usr/bin/env python3 +"""Verifies that node can be started inside of a Docker image. + +The script builds Docker image using 'make docker-framework' command and then +starts a cluster with all nodes running inside of containers. As sanity check +to see if the cluster works correctly, the test waits for a several blocks and +then interrogates each node about block with specified hash expecting all of +them to return the same data. + +The purpose of the test is to verify that: +- `make docker-framework` builds a working Docker image, +- `docker run ... framework` (i.e. the `run_docker.sh` script) works, +- `docker run -eBOOT_NODE=... ... framework` (i.e. passing boot nodes via + BOOT_NODE environment variable) works and +- `docker run ... framework sh -c 'uncd ...'` works. +""" + +import os +import pathlib +import shlex +import subprocess +import sys +import tempfile +import typing +import uuid + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import cluster +from configured_logger import logger +import utils + +BLOCKS = 42 +NUM_NODES = 3 + +_REPO_DIR = pathlib.Path(__file__).resolve().parents[3] +_CD_PRINTED = False +_Command = typing.Sequence[typing.Union[str, pathlib.Path]] + +_DOCKER_IMAGE_TAG = 'framework-testimage-' + uuid.uuid4().hex + + +def run(cmd: _Command, *, capture_output: bool = False) -> typing.Optional[str]: + """Trivial wrapper around subprocess.check_({all,output}. + + Args: + cmd: Command to execute. + capture_output: If true, captures standard output of the command and + returns it. + Returns: + Command's stripped standard output if `capture_output` is true, None + otherwise. + """ + global _CD_PRINTED + if not _CD_PRINTED: + logger.debug(f'+ cd {shlex.quote(str(_REPO_DIR))}') + _CD_PRINTED = True + logger.debug('+ ' + ' '.join(shlex.quote(str(arg)) for arg in cmd)) + if capture_output: + return subprocess.check_output(cmd, cwd=_REPO_DIR, + encoding='utf-8').strip() + subprocess.check_call(cmd, cwd=_REPO_DIR) + return None + + +def docker_run(shell_cmd: typing.Optional[str] = None, + *, + detach: bool = False, + network: bool = False, + volume: typing.Tuple[pathlib.Path, str], + env: typing.Dict[str, str] = {}) -> typing.Optional[str]: + """Runs a `docker run` command. + + Args: + shell_cmd: Optionally a shell command to execute inside of the + container. It's going to be run using `sh -c` and it's caller's + responsibility to make sure all data inside is properly sanitised. + If not given, command configured via CMD when building the container + will be executed as typical for Docker images. + detach: Whether the `docker run` command should detach or not. If + False, standard output and standard error will be passed through to + test outputs. If True, the container will be detached and the + function will return its container id. + network: Whether to enable network. If True, the container will be + configured to use host's network. This allows processes in + different containers to connect with each other easily. + volume: A (path, container_path) tuple denoting that local `path` should + be mounted under `container_path` inside of the container. + env: *Additional* environment variables set inside of the container. + + Returns: + Command's stripped standard output if `detach` is true, None otherwise. + """ + cmd = ['docker', 'run', '--read-only', f'-v{volume[0]}:{volume[1]}'] + + # Either run detached or attach standard output and standard error so they + # are visible. + if detach: + cmd.append('-d') + else: + cmd.extend(('-astdout', '-astderr')) + + if network: + # Don’t create separate network. This makes it much simpler for nodes + # inside of the containers to communicate with each other. + cmd.append('--network=host') + else: + # The command does not need networking so disable it. + cmd.append('--network=none') + + # Use current user to run the code inside the container so that data saved + # in home will be readable by us outside of the container (otherwise, the + # command would be run as root and data would be owned by root). + cmd.extend(('-u', f'{os.getuid()}:{os.getgid()}', '--userns=host')) + + # Set environment variables. + rust_log = ('actix_web=warn,mio=warn,tokio_util=warn,' + 'actix_server=warn,actix_http=warn,' + + os.environ.get('RUST_LOG', 'debug')) + cmd.extend(('-eRUST_BACKTRACE=1', f'-eRUST_LOG={rust_log}')) + for key, value in env.items(): + cmd.append((f'-e{key}={value}')) + + # Specify the image to run. + cmd.append(_DOCKER_IMAGE_TAG) + + # And finally, specify the command. + if shell_cmd: + cmd.extend(('sh', '-c', shell_cmd)) + + return run(cmd, capture_output=detach) + + +class DockerNode(cluster.LocalNode): + """A node run inside of a Docker container.""" + + def __init__(self, ordinal: int, node_dir: pathlib.Path) -> None: + super().__init__(port=24567 + 10 + ordinal, + rpc_port=3030 + 10 + ordinal, + unc_root='', + node_dir=str(node_dir), + blacklist=[]) + self._container_id = None + + def start(self, *, boot_node: cluster.BootNode = None) -> None: + """Starts a node inside of a Docker container. + + Args: + boot_node: Optional boot node to pass to the node. + """ + assert self._container_id is None + assert not self.cleaned + + env = {} + if boot_node: + env['BOOT_NODES'] = cluster.make_boot_nodes_arg(boot_node)[1] + + cid = docker_run(detach=True, + network=True, + volume=(self.node_dir, '/srv/near'), + env=env) + self._container_id = cid + logger.info(f'Node started in Docker container {cid}') + + def kill(self): + cid = self._container_id + if cid: + self._container_id = None + logger.info(f'Stopping container {cid}') + run(('docker', 'stop', cid)) + + __WARN_LOGS = True + + def output_logs(self): + # Unfortunately because we’re running the containers as detached + # anything uncd writes to stdout and stderr is lost. We could start + # nodes using `docker run -d ... sh -c 'uncd ... >stdout 2>stderr'` but + # that would mean that we’re not testing the `run_docker.sh` script + # which we do want to read. + if self.__WARN_LOGS: + logger.info( + 'Due to technical limitations logs from node is not available') + type(self).__WARN_LOGS = False + pass + + +def main(): + nodes = [] + + logger.info("Build the container") + run(('make', 'DOCKER_TAG=' + _DOCKER_IMAGE_TAG, 'docker-framework')) + try: + dot_near = pathlib.Path.home() / '.near' + + logger.info("Initialise local network nodes config.") + cmd = f'uncd --home /home/near localnet --v {NUM_NODES} --prefix test' + docker_run(cmd, volume=(dot_near, '/home/near'), network=True) + + # Start all the nodes + for ordinal in range(NUM_NODES): + logger.info(f'Starting node {ordinal}') + node = DockerNode(ordinal, dot_near / f'test{ordinal}') + node.start(boot_node=nodes) + nodes.append(node) + + # Wait for them to initialise + for ordinal, node in enumerate(nodes): + logger.info(f'Waiting for node {ordinal} to respond') + node.wait_for_rpc(10) + + # Wait for BLOCKS blocks to be generated + latest = utils.wait_for_blocks(nodes[0], target=BLOCKS) + + # Fetch latest block from all the nodes + blocks = [] + for ordinal, node in enumerate(nodes): + utils.wait_for_blocks(node, target=latest.height) + response = node.get_block(latest.hash) + assert 'result' in response, (ordinal, block) + block = response['result'] + blocks.append(block) + bid = cluster.BlockId.from_header(block['header']) + logger.info(f'Node {ordinal} sees block: {bid}') + + # All blocks should be equal + for ordinal in range(1, NUM_NODES): + assert blocks[0] == blocks[ordinal], (ordinal, blocks) + + logger.info('All good') + + finally: + # `docker stop` takes a few seconds so stop all containers in parallel. + # atexit we’ll call DockerNode.cleanup method for each node as well and + # it’ll handle all the other cleanups. + cids = tuple(filter(None, (node._container_id for node in nodes))) + if cids: + logger.info('Stopping containers') + run(('docker', 'rm', '-f') + cids) + for node in nodes: + node._container_id = None + + subprocess.check_call( + ('docker', 'image', 'rm', '-f', _DOCKER_IMAGE_TAG)) + + +if __name__ == '__main__': + main() diff --git a/pytest/tests/sanity/epoch_switches.py b/pytest/tests/sanity/epoch_switches.py new file mode 100755 index 000000000..da920c071 --- /dev/null +++ b/pytest/tests/sanity/epoch_switches.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 +# Spins up four nodes, and alternates [test1, test2] and [test3, test4] as block producers every epoch +# Makes sure that before the epoch switch each block is signed by all four + +import sys, time, base58, random, datetime +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +from transaction import sign_staking_tx + +EPOCH_LENGTH = 30 +HEIGHT_GOAL = int(EPOCH_LENGTH * 7.5) +TIMEOUT = HEIGHT_GOAL * 3 + +config = None +nodes = start_cluster( + 2, 2, 1, config, + [["epoch_length", EPOCH_LENGTH], ["block_producer_kickout_threshold", 40]], + { + 0: { + "view_client_throttle_period": { + "secs": 0, + "nanos": 0 + }, + "state_sync_enabled": True, + "store.state_snapshot_enabled": True, + "consensus": { + "state_sync_timeout": { + "secs": 0, + "nanos": 500000000 + } + } + }, + 1: { + "view_client_throttle_period": { + "secs": 0, + "nanos": 0 + }, + "state_sync_enabled": True, + "store.state_snapshot_enabled": True, + "consensus": { + "state_sync_timeout": { + "secs": 0, + "nanos": 500000000 + } + } + }, + 2: { + "tracked_shards": [0], + "view_client_throttle_period": { + "secs": 0, + "nanos": 0 + }, + "state_sync_enabled": True, + "store.state_snapshot_enabled": True, + "consensus": { + "state_sync_timeout": { + "secs": 0, + "nanos": 500000000 + } + } + }, + 3: { + "view_client_throttle_period": { + "secs": 0, + "nanos": 0 + }, + "state_sync_enabled": True, + "store.state_snapshot_enabled": True, + "consensus": { + "state_sync_timeout": { + "secs": 0, + "nanos": 500000000 + } + } + } + }) + +started = time.time() + + +def get_validators(): + return set([x['account_id'] for x in nodes[0].get_status()['validators']]) + + +def get_stakes(): + return [ + int(nodes[2].get_account("test%s" % i)['result']['locked']) + for i in range(3) + ] + + +seen_epochs = set() +cur_vals = [0, 1] +next_vals = [2, 3] + +height_to_num_approvals = {} + +largest_height = 0 + +next_nonce = 1 + +epoch_switch_height = -2 + +blocks_by_height = {} + + +def wait_until_available(get_fn): + while True: + res = get_fn() + logger.info(f"res: {res}") + if 'result' in res: + return res + time.sleep(0.1) + + +for largest_height in range(2, HEIGHT_GOAL + 1): + assert time.time() - started < TIMEOUT + + block = wait_until_available( + lambda: nodes[0].get_block_by_height(largest_height, timeout=5)) + assert block is not None + hash_ = block['result']['header']['hash'] + epoch_id = block['result']['header']['epoch_id'] + height = block['result']['header']['height'] + assert height == largest_height + blocks_by_height[height] = block + + logger.info("... %s" % height) + logger.info(block['result']['header']['approvals']) + + # we expect no skipped heights + height_to_num_approvals[height] = len( + block['result']['header']['approvals']) + logger.info( + f"Added height_to_num_approvals {height}={len(block['result']['header']['approvals'])}" + ) + + if height > epoch_switch_height + 2: + prev_hash = None + if (height - 1) in blocks_by_height: + prev_hash = blocks_by_height[height - 1]['result']['header']['hash'] + if prev_hash: + for val_ord in next_vals: + tx = sign_staking_tx(nodes[val_ord].signer_key, + nodes[val_ord].validator_key, 0, + next_nonce, + base58.b58decode(prev_hash.encode('utf8'))) + for target in range(0, 4): + nodes[target].send_tx(tx) + next_nonce += 1 + + for val_ord in cur_vals: + tx = sign_staking_tx(nodes[val_ord].signer_key, + nodes[val_ord].validator_key, + 50000000000000000000000000000000, + next_nonce, + base58.b58decode(prev_hash.encode('utf8'))) + for target in range(0, 4): + nodes[target].send_tx(tx) + next_nonce += 1 + + if epoch_id not in seen_epochs: + seen_epochs.add(epoch_id) + if height - 1 in blocks_by_height: + prev_block = blocks_by_height[height - 1] + assert prev_block['result']['header']['epoch_id'] != block[ + 'result']['header']['epoch_id'] + + logger.info("EPOCH %s, VALS %s" % (epoch_id, get_validators())) + + if len(seen_epochs) > 2: # the first two epochs share the validator set + logger.info( + f"Checking height_to_num_approvals {height}, {height_to_num_approvals}" + ) + assert height_to_num_approvals[height] == 2 + + has_prev = height - 1 in height_to_num_approvals + has_two_ago = height - 2 in height_to_num_approvals + + if has_prev: + assert height_to_num_approvals[height - 1] == 4 + if has_two_ago: + assert height_to_num_approvals[height - 2] == 4 + + if has_prev and has_two_ago: + for i in range(3, EPOCH_LENGTH): + if height - i in height_to_num_approvals: + assert height_to_num_approvals[height - i] == 2 + else: + for i in range(height): + if i in height_to_num_approvals: + assert height_to_num_approvals[i] == 2, ( + i, height_to_num_approvals[i], height_to_num_approvals) + + cur_vals, next_vals = next_vals, cur_vals + epoch_switch_height = height + +assert len(seen_epochs) > 3 diff --git a/pytest/tests/sanity/garbage_collection.py b/pytest/tests/sanity/garbage_collection.py new file mode 100755 index 000000000..f674d6b63 --- /dev/null +++ b/pytest/tests/sanity/garbage_collection.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# Spins up two validating nodes. Stop one of them and make another one produce +# sufficient number of blocks. Restart the stopped node and check that it can +# still sync. + +import sys, time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +import utils + +EPOCH_LENGTH = 20 +TARGET_HEIGHT = EPOCH_LENGTH * 6 + +consensus_config = { + "consensus": { + "min_block_production_delay": { + "secs": 0, + "nanos": 100000000 + }, + "max_block_production_delay": { + "secs": 0, + "nanos": 400000000 + }, + "max_block_wait_delay": { + "secs": 0, + "nanos": 400000000 + } + } +} + +nodes = start_cluster( + 2, 0, 1, None, + [["epoch_length", EPOCH_LENGTH], ["num_block_producer_seats", 5], + ["num_block_producer_seats_per_shard", [5]], + ["chunk_producer_kickout_threshold", 80], + ["shard_layout", { + "V0": { + "num_shards": 1, + "version": 1, + } + }], ["validators", 0, "amount", "110000000000000000000000000000000"], + [ + "records", 0, "Account", "account", "locked", + "110000000000000000000000000000000" + ], ["total_supply", "3060000000000000000000000000000000"]], { + 0: consensus_config, + 1: consensus_config + }) + +logger.info('Kill node 1') +nodes[1].kill() + +node0_height, _ = utils.wait_for_blocks(nodes[0], target=TARGET_HEIGHT) + +logger.info('Restart node 1') +nodes[1].start(boot_node=nodes[1]) +time.sleep(3) + +start_time = time.time() + +utils.wait_for_blocks(nodes[1], target=node0_height) diff --git a/pytest/tests/sanity/garbage_collection1.py b/pytest/tests/sanity/garbage_collection1.py new file mode 100755 index 000000000..6b00fa95f --- /dev/null +++ b/pytest/tests/sanity/garbage_collection1.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# Spins up three validating nodes with stake distribution 11, 5, 5. +# Stop the two nodes with stake 2 +# Wait for sufficient number of blocks. +# Restart one of the stopped nodes and wait until it syncs with the running node. +# Restart the other one. Make sure it can sync as well. + +import sys, time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +import utils + +EPOCH_LENGTH = 20 +TARGET_HEIGHT = EPOCH_LENGTH * 6 +TIMEOUT = 30 + +nodes_config = { + "consensus": { + "min_block_production_delay": { + "secs": 0, + "nanos": 100000000 + }, + "max_block_production_delay": { + "secs": 0, + "nanos": 400000000 + }, + "max_block_wait_delay": { + "secs": 0, + "nanos": 400000000 + } + }, + # Enabling explicitly state sync, default value is False + "state_sync_enabled": True +} + +nodes = start_cluster( + 3, 0, 1, None, + [["epoch_length", EPOCH_LENGTH], ["num_block_producer_seats", 5], + ["num_block_producer_seats_per_shard", [5]], + ["total_supply", "4210000000000000000000000000000000"], + ["validators", 0, "amount", "260000000000000000000000000000000"], + [ + "records", 0, "Account", "account", "locked", + "260000000000000000000000000000000" + ]], { + 0: nodes_config, + 1: nodes_config, + 2: nodes_config + }) + +logger.info('kill node1 and node2') +nodes[1].kill() +nodes[2].kill() + +node0_height, _ = utils.wait_for_blocks(nodes[0], target=TARGET_HEIGHT) + +logger.info('Restart node 1') +nodes[1].start(boot_node=nodes[1]) +time.sleep(2) + +for height, _ in utils.poll_blocks(nodes[1], timeout=TIMEOUT): + if height >= node0_height and len(nodes[0].validators()) < 3: + break + +logger.info('Restart node 2') +nodes[2].start(boot_node=nodes[2]) +time.sleep(2) + +target = nodes[0].get_latest_block().height +utils.wait_for_blocks(nodes[2], target=target) diff --git a/pytest/tests/sanity/gc_after_sync.py b/pytest/tests/sanity/gc_after_sync.py new file mode 100755 index 000000000..31d987728 --- /dev/null +++ b/pytest/tests/sanity/gc_after_sync.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# Spins up three validating nodes. Stop one of them and make another one produce +# sufficient number of blocks. Restart the stopped node and check that it can +# still sync. Then check all old data is removed. + +import pathlib +import sys + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +import state_sync_lib +import utils + +EPOCH_LENGTH = 20 +TARGET_HEIGHT = int(EPOCH_LENGTH * 2.5) +AFTER_SYNC_HEIGHT = EPOCH_LENGTH * 10 +TIMEOUT = 300 + +node_config = state_sync_lib.get_state_sync_config_combined() +# We're generating many blocks here - lower the min production delay to speed +# up the test running time a little. +node_config["consensus.min_block_production_delay"] = { + "secs": 0, + "nanos": 100000000 +} +node_config["consensus.block_fetch_horizon"] = 1 + +nodes = start_cluster( + 4, 0, 1, + None, [["epoch_length", EPOCH_LENGTH], + ["num_block_producer_seats_per_shard", [5]], + ["validators", 0, "amount", "60000000000000000000000000000000"], + ["block_producer_kickout_threshold", 50], + ["chunk_producer_kickout_threshold", 50], + [ + "records", 0, "Account", "account", "locked", + "60000000000000000000000000000000" + ], ["total_supply", "5010000000000000000000000000000000"]], + {x: node_config for x in range(4)}) + +node0_height, _ = utils.wait_for_blocks(nodes[0], target=TARGET_HEIGHT) + +logger.info('Kill node 1') +nodes[1].kill() + +node0_height, _ = utils.wait_for_blocks(nodes[0], target=AFTER_SYNC_HEIGHT) + +logger.info('Restart node 1') +nodes[1].start(boot_node=nodes[1]) + +node1_height, _ = utils.wait_for_blocks(nodes[1], target=node0_height) + +epoch_id = nodes[1].json_rpc('block', [node1_height], + timeout=15)['result']['header']['epoch_id'] +epoch_start_height = nodes[1].get_validators( + epoch_id=epoch_id)['result']['epoch_start_height'] + +# all fresh data should be synced +blocks_count = 0 +for height in range(max(node1_height - 10, epoch_start_height), + node1_height + 1): + block0 = nodes[0].json_rpc('block', [height], timeout=15) + block1 = nodes[1].json_rpc('block', [height], timeout=15) + assert block0 == block1, (block0, block1) + if 'result' in block0: + blocks_count += 1 +assert blocks_count > 0 + +# all old data should be GCed +blocks_count = 0 +for height in range(1, EPOCH_LENGTH * 4): + block0 = nodes[0].json_rpc('block', [height], timeout=15) + block1 = nodes[1].json_rpc('block', [height], timeout=15) + assert block0 == block1, (block0, block1) + if 'result' in block0: + blocks_count += 1 +assert blocks_count == 0 diff --git a/pytest/tests/sanity/gc_after_sync1.py b/pytest/tests/sanity/gc_after_sync1.py new file mode 100755 index 000000000..995c5453c --- /dev/null +++ b/pytest/tests/sanity/gc_after_sync1.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# Spin up one validating node and one nonvalidating node +# stop the nonvalidating node in the second epoch and +# restart it in the fourth epoch to trigger state sync +# Check that after 10 epochs the node has properly garbage +# collected blocks. + +import sys, time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +import utils + +EPOCH_LENGTH = 20 +TARGET_HEIGHT1 = EPOCH_LENGTH + (EPOCH_LENGTH // 2) +TARGET_HEIGHT2 = EPOCH_LENGTH * 3 + (EPOCH_LENGTH // 2) +TARGET_HEIGHT3 = EPOCH_LENGTH * 10 + (EPOCH_LENGTH // 2) + +node0_config = {"gc_blocks_limit": 10} + +node1_config = { + "consensus": { + "block_fetch_horizon": 10, + "block_header_fetch_horizon": 10, + }, + "tracked_shards": [0], + "gc_blocks_limit": 10, +} + +nodes = start_cluster( + 1, 1, 1, None, + [["epoch_length", EPOCH_LENGTH], ["block_producer_kickout_threshold", 80], + ["chunk_producer_kickout_threshold", 80]], { + 0: node0_config, + 1: node1_config + }) + +height = nodes[1].get_latest_block().height + +utils.wait_for_blocks(nodes[0], target=TARGET_HEIGHT1) + +logger.info('Kill node 1') +nodes[1].kill() + +utils.wait_for_blocks(nodes[0], target=TARGET_HEIGHT2) + +logger.info('Restart node 1') +nodes[1].start(boot_node=nodes[0]) + +utils.wait_for_blocks(nodes[0], target=TARGET_HEIGHT3) + +nodes[0].kill() + +for i in range(1, EPOCH_LENGTH * 6): + res = nodes[1].json_rpc('block', [i], timeout=10) + assert 'error' in res, f'height {i}, {res}' + +for i in range(EPOCH_LENGTH * 6, EPOCH_LENGTH * 10 + 1): + res = nodes[1].json_rpc('block', [i], timeout=10) + assert 'result' in res, f'height {i}, {res}' diff --git a/pytest/tests/sanity/gc_sync_after_sync.py b/pytest/tests/sanity/gc_sync_after_sync.py new file mode 100755 index 000000000..950d1703d --- /dev/null +++ b/pytest/tests/sanity/gc_sync_after_sync.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +# Spins up three validating nodes. Stop one of them and make another one produce +# sufficient number of blocks. Restart the stopped node and check that it can +# still sync. Repeat. Then check all old data is removed. + +import pathlib +import sys +import tempfile +import time + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +swap_nodes = ("swap_nodes" in sys.argv) # swap nodes 0 and 1 after first sync + +from cluster import start_cluster +from configured_logger import logger +import state_sync_lib +import utils + +EPOCH_LENGTH = 30 +NUM_GC_EPOCHS = 3 +# The gaps need to be longer than NUM_GC_EPOCHS epochs for the garbage collection to kick in. +TARGET_HEIGHT1 = EPOCH_LENGTH * (NUM_GC_EPOCHS + 1) +TARGET_HEIGHT2 = EPOCH_LENGTH * 2 * (NUM_GC_EPOCHS + 1) +TARGET_HEIGHT3 = EPOCH_LENGTH * 3 * (NUM_GC_EPOCHS + 1) + +node_config = state_sync_lib.get_state_sync_config_combined() +node_config["gc_num_epochs_to_keep"] = NUM_GC_EPOCHS + +nodes = start_cluster( + 4, 0, 1, None, + [["epoch_length", EPOCH_LENGTH], + ["validators", 0, "amount", "12500000000000000000000000000000"], + [ + "records", 0, "Account", "account", "locked", + "12500000000000000000000000000000" + ], ["validators", 1, "amount", "12500000000000000000000000000000"], + [ + "records", 2, "Account", "account", "locked", + "12500000000000000000000000000000" + ], ['total_supply', "4925000000000000000000000000000000"], + ["block_producer_kickout_threshold", 40], + ["chunk_producer_kickout_threshold", 40], ["num_block_producer_seats", 10], + ["num_block_producer_seats_per_shard", [10]]], + {x: node_config for x in range(4)}) + +logger.info('Kill node 1') +nodes[1].kill() + +node0_height, _ = utils.wait_for_blocks(nodes[0], target=TARGET_HEIGHT1) + +logger.info('Starting back node 1') +nodes[1].start(boot_node=nodes[1]) +time.sleep(3) + +node1_height, _ = utils.wait_for_blocks(nodes[1], target=node0_height) + +if swap_nodes: + logger.info('Swap nodes 0 and 1') + nodes[0], nodes[1] = nodes[1], nodes[0] + +logger.info('Kill node 1') +nodes[1].kill() + +node0_height, _ = utils.wait_for_blocks(nodes[0], target=TARGET_HEIGHT2) + +logger.info('Restart node 1') +nodes[1].start(boot_node=nodes[1]) +# State Sync makes the storage seem inconsistent. +nodes[1].stop_checking_store() +time.sleep(3) + +node1_height, _ = utils.wait_for_blocks(nodes[1], + target=node0_height + EPOCH_LENGTH) + +logger.info(f'node0_height: {node0_height}, node1_height: {node1_height}') + +# all fresh data should be synced +blocks_count = 0 +for height in range(node1_height - 10, node1_height): + logger.info(f'Check block at height {height}') + block0 = nodes[0].json_rpc('block', [height], timeout=15) + block1 = nodes[1].json_rpc('block', [height], timeout=15) + assert block0 == block1, ( + f'fresh block at height: {height}, block0: {block0}, block1: {block1}') + if 'result' in block0: + blocks_count += 1 +assert blocks_count > 0 +time.sleep(1) + +# all old data should be GCed +blocks_count = 0 +for height in range(1, 15): + logger.info(f'Check old block at height {height}') + block0 = nodes[0].json_rpc('block', [height], timeout=15) + block1 = nodes[1].json_rpc('block', [height], timeout=15) + assert block0 == block1, ( + f'old block at height: {height}, block0: {block0}, block1: {block1}') + if 'result' in block0: + blocks_count += 1 +assert blocks_count == 0 + +# all data after first sync should be GCed +blocks_count = 0 +for height in range(TARGET_HEIGHT1, TARGET_HEIGHT1 + 10): + logger.info(f'Check block after first sync at height {height}') + block1 = nodes[1].json_rpc('block', [height], timeout=15) + if 'result' in block1: + blocks_count += 1 +assert blocks_count == 0 + +# all data before second sync should be GCed +blocks_count = 0 +for height in range(TARGET_HEIGHT2 - 15, TARGET_HEIGHT2 - 5): + logger.info(f'Check block before second sync at height {height}') + block1 = nodes[1].json_rpc('block', [height], timeout=15) + if 'result' in block1: + logger.info(block1['result']) + blocks_count += 1 +assert blocks_count == 0 + +# check that node can GC normally after syncing +utils.wait_for_blocks(nodes[1], target=TARGET_HEIGHT3, verbose=True) + +logger.info('EPIC') diff --git a/pytest/tests/sanity/handshake_tie_resolution.py b/pytest/tests/sanity/handshake_tie_resolution.py new file mode 100755 index 000000000..d3e16f56e --- /dev/null +++ b/pytest/tests/sanity/handshake_tie_resolution.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +""" +Spawn a cluster with four nodes. Check that no node tries to +connect to another node that is currently connected. +""" + +import sys, time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import cluster +import utils + +BLOCKS = 20 + +nodes = cluster.start_cluster(4, 0, 4, None, [], {}) +trackers = [utils.LogTracker(node) for node in nodes] +utils.wait_for_blocks(nodes[0], target=BLOCKS) +assert all(not tracker.check('Dropping handshake (Active Peer).') + for tracker in trackers) diff --git a/pytest/tests/sanity/large_messages.py b/pytest/tests/sanity/large_messages.py new file mode 100755 index 000000000..ef3e1f7a2 --- /dev/null +++ b/pytest/tests/sanity/large_messages.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +import sys, time +import socket, struct, multiprocessing +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger + +PACKAGE_LEN = 16 * 1024 * 1024 +N_PROCESSES = 16 + +buf = bytes([0] * PACKAGE_LEN) + +nodes = start_cluster(2, 0, 4, None, [], {}) + + +def one_process(ord_, seconds): + started = time.time() + sent = 0 + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect(nodes[0].addr()) + while time.time() - started < seconds: + s.send(struct.pack('I', PACKAGE_LEN)) + s.send(buf) + sent += PACKAGE_LEN + logger.info("PROCESS %s SENT %s BYTES" % (ord_, sent)) + + +last_height = nodes[0].get_latest_block().height + +for seconds in [20, 120]: + ps = [ + multiprocessing.Process(target=one_process, args=(i, seconds)) + for i in range(N_PROCESSES) + ] + + for p in ps: + p.start() + + for p in ps: + p.join() + + new_height = nodes[0].get_latest_block().height + assert new_height - last_height > 5, "new height: %s, last_height: %s" % ( + new_height, last_height) + last_height = new_height diff --git a/pytest/tests/sanity/lightclnt.py b/pytest/tests/sanity/lightclnt.py new file mode 100755 index 000000000..8891d2b82 --- /dev/null +++ b/pytest/tests/sanity/lightclnt.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 +# Generates three epochs worth of blocks +# Requests next light client block until it reaches the last final block. +# Verifies that the returned blocks are what we expect, and runs the validation on them + +import sys, time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster, load_config +from configured_logger import logger +from lightclient import compute_block_hash, validate_light_client_block +import utils + +TIMEOUT = 150 +config = load_config() +client_config_changes = {} +if not config['local']: + client_config_changes = { + "consensus": { + "min_block_production_delay": { + "secs": 4, + "nanos": 0, + }, + "max_block_production_delay": { + "secs": 8, + "nanos": 0, + }, + "max_block_wait_delay": { + "secs": 24, + "nanos": 0, + }, + } + } + TIMEOUT = 600 + +client_config_changes['archive'] = True +client_config_changes['tracked_shards'] = [0] # Track all shards + +no_state_snapshots_config = client_config_changes +no_state_snapshots_config['store.state_snapshot_enabled'] = False + +nodes = start_cluster( + 4, 0, 4, None, + [["epoch_length", 6], ["block_producer_kickout_threshold", 40], + ["chunk_producer_kickout_threshold", 40]], { + 0: no_state_snapshots_config, + 1: client_config_changes, + 2: client_config_changes, + 3: client_config_changes + }) + +for node in nodes: + node.stop_checking_store() + +started = time.time() + +hash_to_height = {} +hash_to_epoch = {} +hash_to_next_epoch = {} +height_to_hash = {} +epochs = [] + +first_epoch_switch_height = None +last_epoch = None + +block_producers_map = {} + + +def get_light_client_block(hash_, last_known_block): + global block_producers_map + + ret = nodes[0].json_rpc('next_light_client_block', [hash_]) + if ret['result'] != {} and last_known_block is not None: + validate_light_client_block(last_known_block, + ret['result'], + block_producers_map, + panic=True) + return ret + + +def get_up_to(from_, to): + global first_epoch_switch_height, last_epoch + + for height, hash_ in utils.poll_blocks(nodes[0], + timeout=TIMEOUT, + poll_interval=0.01): + block = nodes[0].get_block(hash_) + + hash_to_height[hash_] = height + height_to_hash[height] = hash_ + + cur_epoch = block['result']['header']['epoch_id'] + + hash_to_epoch[hash_] = cur_epoch + hash_to_next_epoch[hash_] = block['result']['header']['next_epoch_id'] + + if (first_epoch_switch_height is None and last_epoch is not None and + last_epoch != cur_epoch): + first_epoch_switch_height = height + last_epoch = cur_epoch + + if height >= to: + break + + for i in range(from_, to + 1): + hash_ = height_to_hash[i] + logger.info( + f"{i} {hash_} {hash_to_epoch[hash_]} {hash_to_next_epoch[hash_]}") + + if len(epochs) == 0 or epochs[-1] != hash_to_epoch[hash_]: + epochs.append(hash_to_epoch[hash_]) + + +# don't start from 1, since couple heights get produced while the nodes spin up +get_up_to(4, 15) +get_up_to(16, 22 + first_epoch_switch_height) + +# since we already "know" the first block, the first light client block that will be returned +# will be for the second epoch. The second epoch spans blocks 7-12, and the last final block in +# it has height 10. Then blocks go in increments of 6. +# the last block returned will be the last final block, with height 27 +heights = [ + None, 3 + first_epoch_switch_height, 9 + first_epoch_switch_height, + 15 + first_epoch_switch_height, 20 + first_epoch_switch_height +] + +last_known_block_hash = height_to_hash[4] +last_known_block = None +iter_ = 1 + +while True: + assert time.time() - started < TIMEOUT + + res = get_light_client_block(last_known_block_hash, last_known_block) + + if last_known_block_hash == height_to_hash[20 + first_epoch_switch_height]: + assert res['result'] == {} + break + + assert res['result']['inner_lite']['epoch_id'] == epochs[iter_] + logger.info(f"{iter_} {heights[iter_]}") + assert res['result']['inner_lite']['height'] == heights[iter_], ( + res['result']['inner_lite'], first_epoch_switch_height) + + last_known_block_hash = compute_block_hash( + res['result']['inner_lite'], res['result']['inner_rest_hash'], + res['result']['prev_block_hash']).decode('ascii') + assert last_known_block_hash == height_to_hash[ + res['result']['inner_lite']['height']], "%s != %s" % ( + last_known_block_hash, + height_to_hash[res['result']['inner_lite']['height']]) + + if last_known_block is None: + block_producers_map[res['result']['inner_lite'] + ['next_epoch_id']] = res['result']['next_bps'] + last_known_block = res['result'] + + iter_ += 1 + +res = get_light_client_block(height_to_hash[19 + first_epoch_switch_height], + last_known_block) +logger.info(res) +assert res['result']['inner_lite']['height'] == 20 + first_epoch_switch_height + +get_up_to(23 + first_epoch_switch_height, 24 + first_epoch_switch_height) + +# Test that the light client block is always in the same epoch as the block 2 heights onward (needed to make +# sure that the proofs can be verified using the keys of the block producers of the epoch of the block). +# Before the loop below the last block is 24 + C, which is in the new epoch, thus we expect the light client +# block during the first iteration to be 21 + C. After the first iteration we move one block onward, now +# having the head at 25 + C. At this point the last final block is 23 + C, still in the previous epoch, so +# we still expect 21 + C to be returned. We then move again to 26 + C, and (in the section after the loop) +# check that the light client block now corresponds to 24 + C, which is in the same epoch as 26 + C. +for i in range(2): + res = get_light_client_block(height_to_hash[19 + first_epoch_switch_height], + last_known_block) + assert res['result']['inner_lite'][ + 'height'] == 21 + first_epoch_switch_height, ( + res['result']['inner_lite']['height'], + 21 + first_epoch_switch_height) + + res = get_light_client_block(height_to_hash[20 + first_epoch_switch_height], + last_known_block) + assert res['result']['inner_lite'][ + 'height'] == 21 + first_epoch_switch_height + + res = get_light_client_block(height_to_hash[21 + first_epoch_switch_height], + last_known_block) + assert res['result'] == {} + + get_up_to(i + 25 + first_epoch_switch_height, + i + 25 + first_epoch_switch_height) + +res = get_light_client_block(height_to_hash[21 + first_epoch_switch_height], + last_known_block) +assert res['result']['inner_lite']['height'] == 24 + first_epoch_switch_height diff --git a/pytest/tests/sanity/meta_tx.py b/pytest/tests/sanity/meta_tx.py new file mode 100644 index 000000000..c9a8e1bad --- /dev/null +++ b/pytest/tests/sanity/meta_tx.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +# Tests the meta transaction flow. +# Creates a new account (candidate.test0) with a fixed amount of tokens. +# Afterwards, creates the meta transaction that adds a new key to this account, but the gas is paid by someone else (test0) account. +# At the end, verifies that key has been added succesfully and that the amount of tokens in candidate didn't change. + +import base58 +import pathlib +import sys +import typing + +import unittest + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster, LocalNode +import utils +import transaction +import key + + +class Nonce: + """ Helper class to manage nonces (automatically increase them when they are used. """ + + def __init__(self, current_nonce: int = 0): + self.nonce = current_nonce + + def use_nonce(self) -> int: + self.nonce += 1 + return self.nonce + + +def create_nonce_from_node(node: LocalNode, account_id: str, pk: str) -> Nonce: + nn = node.get_nonce_for_pk(account_id, pk) + assert nn, "Nonce missing for the candidate account" + return Nonce(nn) + + +# Returns the number of keys and current amount for a given account +def check_account_status(node: LocalNode, + account_id: str) -> typing.Tuple[int, int]: + current_keys = node.get_access_key_list(account_id)['result']['keys'] + account_state = node.get_account(account_id)['result'] + return (len(current_keys), int(account_state['amount'])) + + +class TestMetaTransactions(unittest.TestCase): + + def test_meta_tx(self): + nodes: list[LocalNode] = start_cluster(2, 0, 1, None, [], {}) + _, hash_ = utils.wait_for_blocks(nodes[0], target=10) + + node0_nonce = Nonce() + + CANDIDATE_ACCOUNT = "candidate.test0" + CANDIDATE_STARTING_AMOUNT = 123 * (10**24) + + # create new account + candidate_key = key.Key.from_random(CANDIDATE_ACCOUNT) + + tx = transaction.sign_create_account_with_full_access_key_and_balance_tx( + nodes[0].signer_key, candidate_key.account_id, candidate_key, + CANDIDATE_STARTING_AMOUNT, node0_nonce.use_nonce(), + base58.b58decode(hash_.encode('utf8'))) + nodes[0].send_tx_and_wait(tx, 100) + + self.assertEqual(check_account_status(nodes[0], CANDIDATE_ACCOUNT), + (1, CANDIDATE_STARTING_AMOUNT)) + + candidate_nonce = create_nonce_from_node(nodes[0], + candidate_key.account_id, + candidate_key.pk) + + # Now let's prepare the meta transaction. + new_key = key.Key.from_random("new_key") + add_new_key_action = transaction.create_full_access_key_action( + new_key.decoded_pk()) + signed_meta_tx = transaction.create_signed_delegated_action( + CANDIDATE_ACCOUNT, CANDIDATE_ACCOUNT, [add_new_key_action], + candidate_nonce.use_nonce(), 1000, candidate_key.decoded_pk(), + candidate_key.decoded_sk()) + + meta_tx = transaction.sign_delegate_action( + signed_meta_tx, nodes[0].signer_key, CANDIDATE_ACCOUNT, + node0_nonce.use_nonce(), base58.b58decode(hash_.encode('utf8'))) + + nodes[0].send_tx_and_wait(meta_tx, 100) + + self.assertEqual(check_account_status(nodes[0], CANDIDATE_ACCOUNT), + (2, CANDIDATE_STARTING_AMOUNT)) + + +if __name__ == '__main__': + unittest.main() diff --git a/pytest/tests/sanity/network_drop_package.py b/pytest/tests/sanity/network_drop_package.py new file mode 100755 index 000000000..e5b560a8a --- /dev/null +++ b/pytest/tests/sanity/network_drop_package.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +import sys, time, random +import multiprocessing +import logging +import pathlib +from functools import partial + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from peer import * +from proxy import ProxyHandler + +from multiprocessing import Value + +TIMEOUT = 90 + +# Ratio of message that are dropped to simulate bad network performance +DROP_RATIO = 0.05 + + +class Handler(ProxyHandler): + + def __init__(self, *args, success=None, **kwargs): + assert success is not None + self.success = success + super().__init__(*args, **kwargs) + self.dropped = 0 + self.total = 0 + + async def handle(self, msg, fr, to): + if msg.enum == 'Block': + h = msg.Block.header().inner_lite().height + + with self.success.get_lock(): + if h >= 10 and self.success.value == 0: + logging.info( + f'SUCCESS DROP={self.dropped} TOTAL={self.total}') + self.success.value = 1 + + drop = random.random() < DROP_RATIO and 'Handshake' not in msg.enum + + if drop: + self.dropped += 1 + self.total += 1 + + return not drop + + +if __name__ == '__main__': + success = Value('i', 0) + + start_cluster(3, 0, 1, None, [["epoch_length", 500]], {}, + partial(Handler, success=success)) + + started = time.time() + + while True: + logging.info(f"Time: {time.time() - started:0.2}, Fin: {success.value}") + assert time.time() - started < TIMEOUT + time.sleep(1) + + if success.value == 1: + break + + logging.info("Success") diff --git a/pytest/tests/sanity/one_val.py b/pytest/tests/sanity/one_val.py new file mode 100755 index 000000000..ed8b5e31c --- /dev/null +++ b/pytest/tests/sanity/one_val.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# Creates a genesis config with two block producers, and kills one right away after +# launch. Makes sure that the other block producer can produce blocks with chunks and +# process transactions. Makes large-ish number of block producers per shard to minimize +# the chance of the second block producer occupying all the seats in one of the shards + +import sys, time, base58, random +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +import utils +from transaction import sign_payment_tx + +TIMEOUT = 180 + +nightly = len(sys.argv) > 1 +genesis_change = [ + ["num_block_producer_seats", 199], + ["num_block_producer_seats_per_shard", [24, 25, 25, 25, 25, 25, 25, 25]], + ["min_gas_price", 0], ["max_inflation_rate", [0, 1]], ["epoch_length", 10], + ["block_producer_kickout_threshold", 60], + ["chunk_producer_kickout_threshold", 60], + ["validators", 0, "amount", "110000000000000000000000000000000"], + [ + "records", 0, "Account", "account", "locked", + "110000000000000000000000000000000" + ], ["total_supply", "4060000000000000000000000000000000"] +] +nightly_genesis_change = [ + ["minimum_validators_per_shard", 2], ["min_gas_price", 0], + ["max_inflation_rate", [0, 1]], ["epoch_length", 10], + ["block_producer_kickout_threshold", 60], + ["chunk_producer_kickout_threshold", 60], + ["validators", 0, "amount", "110000000000000000000000000000000"], + [ + "records", 0, "Account", "account", "locked", + "110000000000000000000000000000000" + ], ["total_supply", "4060000000000000000000000000000000"] +] + +# give more stake to the bootnode so that it can produce the blocks alone +nodes = start_cluster(2, 1, 8, None, + nightly_genesis_change if nightly else genesis_change, { + 0: { + "tracked_shards": [0] + }, + 1: { + "tracked_shards": [0] + } + }) +time.sleep(3) +nodes[1].kill() + +started = time.time() + +act_to_val = [0, 0, 0] +ctx = utils.TxContext(act_to_val, nodes) + +last_balances = [x for x in ctx.expected_balances] + +sent_height = -1 +caught_up_times = 0 + +for height, hash_ in utils.poll_blocks(nodes[0], + timeout=TIMEOUT, + poll_interval=0.1): + logger.info(f'Got to height {height}') + + if ctx.get_balances() == ctx.expected_balances: + logger.info('Balances caught up, took %s blocks, moving on', + height - sent_height) + ctx.send_moar_txs(hash_, 10, use_routing=True) + sent_height = height + caught_up_times += 1 + else: + assert height <= sent_height + 30, ('Balances before: {before}\n' + 'Expected balances: {expected}\n' + 'Current balances: {current}\n' + 'Sent at height: {height}').format( + before=last_balances, + expected=ctx.expected_balances, + current=ctx.get_balances(), + height=sent_height) + + if caught_up_times == 3: + break diff --git a/pytest/tests/sanity/proxy_example.py b/pytest/tests/sanity/proxy_example.py new file mode 100755 index 000000000..7ba03fd0d --- /dev/null +++ b/pytest/tests/sanity/proxy_example.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# This test is an example about how to use the proxy features. +# +# Create two nodes and add a proxy between them. +# - Capture PeersRequest message from node 1 to node 0. +# - Let the message pass immediately so node 1 receives a PeersResponse +# - After 3 seconds send PeersRequest again so node 1 receives again a PeersResponse +import sys, time, asyncio +import multiprocessing +import functools +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +from peer import * +from proxy import ProxyHandler + +from multiprocessing import Value + +TIMEOUT = 30 + + +class Handler(ProxyHandler): + + def __init__(self, *args, success=None, **kwargs): + assert success is not None + self.success = success + super().__init__(*args, **kwargs) + self.peers_response = 0 + + async def handle(self, msg, fr, to): + if msg.enum.startswith('Peers'): + logger.info(f"{msg.enum} {fr} {to}") + + if to == 0 and msg.enum == 'PeersRequest': + self.peers_request = msg + loop = asyncio.get_running_loop() + send = functools.partial(self.do_send_message, msg, 0) + loop.call_later(3, send) + + if to == 1 and msg.enum == 'PeersResponse': + self.peers_response += 1 + logger.info(f"Total PeersResponses = {self.peers_response}") + if self.peers_response == 2: + self.success.value = 1 + + return True + + +if __name__ == '__main__': + success = Value('i', 0) + start_cluster(2, 0, 1, None, [], {}, + functools.partial(Handler, success=success)) + + started = time.time() + + while True: + assert time.time() - started < TIMEOUT + time.sleep(1) + + if success.value == 1: + break diff --git a/pytest/tests/sanity/proxy_restart.py b/pytest/tests/sanity/proxy_restart.py new file mode 100755 index 000000000..76ee8a883 --- /dev/null +++ b/pytest/tests/sanity/proxy_restart.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# Start two nodes. Proxify both nodes. Kill one of them, restart it +# and wait until block at height >= 20. +import sys, time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +from peer import * +from proxy import ProxyHandler +import utils + +TARGET_HEIGHT = 20 + +if __name__ == '__main__': + nodes = start_cluster(2, 0, 1, None, [], {}, ProxyHandler) + + nodes[1].kill() + nodes[1].start(boot_node=nodes[0]) + + utils.wait_for_blocks(nodes[1], target=TARGET_HEIGHT) diff --git a/pytest/tests/sanity/proxy_simple.py b/pytest/tests/sanity/proxy_simple.py new file mode 100755 index 000000000..51a1367a8 --- /dev/null +++ b/pytest/tests/sanity/proxy_simple.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# Start two nodes. Proxify both nodes +# and wait until block at height >= 10 pass through the proxy. +import sys, time +import multiprocessing +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +from functools import partial +from peer import * +from proxy import ProxyHandler + +from multiprocessing import Value +from utils import obj_to_string + +TIMEOUT = 30 + + +class Handler(ProxyHandler): + + def __init__(self, *args, success=None, **kwargs): + assert success is not None + self.success = success + super().__init__(*args, **kwargs) + + async def handle(self, msg, fr, to): + if msg.enum == 'Block': + h = msg.Block.header().inner_lite().height + logger.info(f"Height: {h}") + if h >= 10: + logger.info('SUCCESS') + self.success.value = 1 + return True + + +if __name__ == '__main__': + success = Value('i', 0) + + start_cluster(2, 0, 1, None, [], {}, partial(Handler, success=success)) + + started = time.time() + + while True: + assert time.time() - started < TIMEOUT + time.sleep(1) + + if success.value == 1: + break diff --git a/pytest/tests/sanity/recompress_storage.py b/pytest/tests/sanity/recompress_storage.py new file mode 100644 index 000000000..de59113e0 --- /dev/null +++ b/pytest/tests/sanity/recompress_storage.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +"""Tests whether node can continue working after its storage is recompressed. + +The test starts a cluster of four nodes, submits a few transactions onto the +network. The same transactions as the ones used in rpc_tx_status.py test. + +Once that’s done, each node is stopped and its storage processed via the +‘recompress-storage’ command. ‘view-state apply-range’ is then executed to +verify that the database has not been corrupted. + +Finally, all the nodes are restarted and again a few transactions are sent to +verify that everything is working in order. + +The above steps are done once for RPC nodes and again for archival nodes. +""" + +import os +import pathlib +import subprocess +import sys +import threading +import time +import typing +import unittest + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from configured_logger import logger +import cluster +import key +import transaction + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'tests')) + +import sanity.rpc_tx_status + + +class RecompressStorageTestCase(unittest.TestCase): + + def __init__(self, *args, **kw) -> None: + super().__init__(*args, **kw) + self.nodes = () + + def tearDown(self) -> None: + for node in self.nodes: + node.cleanup() + self.nodes = () + + def do_test_recompress_storage(self, *, archive: bool) -> None: + logger.info(f'start cluster') + self.nodes = sanity.rpc_tx_status.start_cluster(archive=archive) + + # Give the network some time to generate a few blocks. The same goes + # for other sleeps in this method. + time.sleep(5) + + # Execute a few transactions + sanity.rpc_tx_status.test_tx_status(self.nodes) + time.sleep(5) + + # Recompress storage on each node + logger.info(f'Recompress storage on each node') + for idx, node in enumerate(self.nodes): + logger.info(f'Stopping node{idx}') + node.kill(gentle=True) + + node_dir = pathlib.Path(node.node_dir) + self._call( + node, + 'recompress-storage-', + 'recompress-storage', + '--output-dir=' + str(node_dir / 'data-new'), + ) + (node_dir / 'data').rename(node_dir / 'data-old') + (node_dir / 'data-new').rename(node_dir / 'data') + + self._call( + node, + 'view-state-', + 'view-state', + 'apply-range', + '--start-index=0', + '--verbose-output', + ) + + # Restart all nodes with the new database + logger.info(f'Restart all nodes with the new database') + for idx, node in enumerate(self.nodes): + logger.info(f'Starting node{idx}') + node.start(boot_node=self.nodes[0]) + + # Execute a few more transactions + time.sleep(5) + sanity.rpc_tx_status.test_tx_status(self.nodes, nonce_offset=3) + + def _call(self, node: cluster.LocalNode, prefix: str, *args: + typing.Union[str, pathlib.Path]) -> None: + """Calls node’s uncd with given arguments.""" + node_dir = pathlib.Path(node.node_dir) + cmd = [ + pathlib.Path(node.unc_root) / node.binary_name, + f'--home={node_dir}', + ] + list(args) + logger.info('Running ' + ' '.join(str(arg) for arg in cmd)) + with open(node_dir / (prefix + 'stdout'), 'ab') as stdout, \ + open(node_dir / (prefix + 'stderr'), 'ab') as stderr: + subprocess.check_call(cmd, + stdin=subprocess.DEVNULL, + stdout=stdout, + stderr=stderr, + env=dict(os.environ, RUST_LOG='debug')) + + def test_recompress_storage_rpc(self) -> None: + self.do_test_recompress_storage(archive=False) + + def test_recompress_storage_archive(self) -> None: + self.do_test_recompress_storage(archive=True) + + +if __name__ == '__main__': + unittest.main() diff --git a/pytest/tests/sanity/repro_2916.py b/pytest/tests/sanity/repro_2916.py new file mode 100755 index 000000000..3bbe49e05 --- /dev/null +++ b/pytest/tests/sanity/repro_2916.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +# Spins up two nodes with two shards, waits for couple blocks, snapshots the +# latest chunks, and requests both chunks from the first node, asking for +# receipts for both shards in both requests. We expect the first node have full +# responses for only one of these shards -- the shard it tracks (for the +# shard it doesn't track it will only have the receipts to the shard it does +# track). +# +# We then kill both nodes, and restart the first node, and do the same +# requests. We expect it to resond the same way. Before 2916 is fixed, it +# fails to respond to the request it was previously responding to due to +# incorrect reconstruction of the receipts. + +import asyncio, sys, time +import base58 +import nacl.signing +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +from peer import * +import utils + +from messages.tx import * +from messages.block import * +from messages.crypto import * +from messages.network import * + + +async def main(): + # start a cluster with two shards + nodes = start_cluster(2, 0, 2, None, [], {}) + + height, hash_ = utils.wait_for_blocks(nodes[0], target=3) + block = nodes[0].get_block(hash_)['result'] + chunk_hashes = [base58.b58decode(x['chunk_hash']) for x in block['chunks']] + assert len(chunk_hashes) == 2 + assert all([len(x) == 32 for x in chunk_hashes]) + + my_key_pair_nacl = nacl.signing.SigningKey.generate() + tracking_shards_scenario = None # will be either [0, 1] or [1, 0]; we'll detect + + # step = 0: before the node is killed + # step = 1: after the node is killed + for step in range(2): + + conn0 = await connect(nodes[0].addr()) + await run_handshake(conn0, nodes[0].node_key.pk, my_key_pair_nacl) + for shard_ord, chunk_hash in enumerate(chunk_hashes): + + request = PartialEncodedChunkRequestMsg() + request.chunk_hash = chunk_hash + request.part_ords = [] + request.tracking_shards = [0, 1] + + routed_msg_body = RoutedMessageBody() + routed_msg_body.enum = 'PartialEncodedChunkRequest' + routed_msg_body.PartialEncodedChunkRequest = request + + peer_message = create_and_sign_routed_peer_message( + routed_msg_body, nodes[0], my_key_pair_nacl) + + await conn0.send(peer_message) + + received_receipt_shards = set() + + def predicate(response): + return response.enum == 'Routed' and response.Routed.body.enum == 'PartialEncodedChunkResponse' + + try: + response = await asyncio.wait_for(conn0.recv(predicate), 5) + except (concurrent.futures._base.TimeoutError, + asyncio.exceptions.TimeoutError): + assert False, "A response is always expected for partial encoded chunk request." + + for receipt_proof in response.Routed.body.PartialEncodedChunkResponse.receipts: + shard_proof = receipt_proof.f2 + assert shard_proof.from_shard_id == shard_ord, \ + "Basic correctness check failed: the receipt for chunk of shard {} has the wrong from_shard_id {}".format(shard_ord, shard_proof.from_shard_id) + received_receipt_shards.add(shard_proof.to_shard_id) + + if step == 0 and shard_ord == 0: + # detect how the two validators decided who tracks which shard. + if received_receipt_shards == set([1]): + # if the first validator only responded receipt to shard 1, then + # it's only tracking shard 1. (Otherwise it should respond with [0, 1] + # since it tracks all receipts coming from shard 0.) + tracking_shards_scenario = [1, 0] + else: + tracking_shards_scenario = [0, 1] + + if tracking_shards_scenario == [0, 1]: + if shard_ord == 0: + assert received_receipt_shards == set([0, 1]), \ + "Request to node 0 (tracks shard 0), chunk 0, expected receipts to [0, 1], actual {}".format(received_receipt_shards) + else: + assert received_receipt_shards == set([0]), \ + "Request to node 0 (tracks shard 0), chunk 1, expected receipts to [0], actual {}".format(received_receipt_shards) + else: # [1, 0] + if shard_ord == 0: + assert received_receipt_shards == set([1]), \ + "Request to node 0 (tracks shard 1), chunk 0, expected receipts to [1], actual {}".format(received_receipt_shards) + else: + assert received_receipt_shards == set([0, 1]), \ + "Request to node 0 (tracks shard 1), chunk 1, expected receipts to [0, 1], actual {}".format(received_receipt_shards) + + if step == 0: + logger.info("Killing and restarting nodes") + nodes[1].kill() + nodes[0].kill() + nodes[0].start() + time.sleep(1) + + +asyncio.run(main()) diff --git a/pytest/tests/sanity/resharding.py b/pytest/tests/sanity/resharding.py new file mode 100644 index 000000000..27c1f353e --- /dev/null +++ b/pytest/tests/sanity/resharding.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 + +# Small test for resharding. Spins up a few nodes from genesis with the previous +# shard layout, waits for a few epochs and verifies that the shard layout is +# upgraded. +# Usage: +# python3 pytest/tests/sanity/resharding.py + +import unittest +import sys +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from configured_logger import logger +from cluster import get_binary_protocol_version, init_cluster, load_config, spin_up_node +from utils import MetricsTracker, poll_blocks +from resharding_lib import get_genesis_shard_layout_version, get_target_shard_layout_version, get_genesis_num_shards, get_target_num_shards, get_epoch_offset, get_genesis_config_changes, get_client_config_changes + + +class ReshardingTest(unittest.TestCase): + + def setUp(self) -> None: + self.epoch_length = 5 + self.config = load_config() + self.binary_protocol_version = get_binary_protocol_version(self.config) + assert self.binary_protocol_version is not None + + self.genesis_shard_layout_version = get_genesis_shard_layout_version( + self.binary_protocol_version) + self.target_shard_layout_version = get_target_shard_layout_version( + self.binary_protocol_version) + + self.genesis_num_shards = get_genesis_num_shards( + self.binary_protocol_version) + self.target_num_shards = get_target_num_shards( + self.binary_protocol_version) + + self.epoch_offset = get_epoch_offset(self.binary_protocol_version) + + def test_resharding(self): + logger.info("The resharding test is starting.") + num_nodes = 2 + + genesis_config_changes = get_genesis_config_changes( + self.epoch_length, self.binary_protocol_version, logger) + client_config_changes = get_client_config_changes(num_nodes) + + unc_root, [node0_dir, node1_dir] = init_cluster( + num_nodes=num_nodes, + num_observers=0, + num_shards=1, + config=self.config, + genesis_config_changes=genesis_config_changes, + client_config_changes=client_config_changes, + ) + + node0 = spin_up_node( + self.config, + unc_root, + node0_dir, + 0, + ) + node1 = spin_up_node( + self.config, + unc_root, + node1_dir, + 1, + boot_node=node0, + ) + + metrics_tracker = MetricsTracker(node0) + + for height, hash in poll_blocks(node0): + version = metrics_tracker.get_int_metric_value( + "unc_shard_layout_version") + num_shards = metrics_tracker.get_int_metric_value( + "unc_shard_layout_num_shards") + + protocol_config = node0.json_rpc( + "EXPERIMENTAL_protocol_config", + {"block_id": hash}, + ) + + self.assertTrue('error' not in protocol_config) + + self.assertTrue('result' in protocol_config) + protocol_config = protocol_config.get('result') + + self.assertTrue('shard_layout' in protocol_config) + shard_layout = protocol_config.get('shard_layout') + + self.assertTrue('V1' in shard_layout) + shard_layout = shard_layout.get('V1') + + self.assertTrue('boundary_accounts' in shard_layout) + boundary_accounts = shard_layout.get('boundary_accounts') + + logger.info( + f"#{height} shard layout version: {version}, num shards: {num_shards}" + ) + logger.debug(f"#{height} shard layout: {shard_layout}") + + # check the shard layout versions from metrics and from json rpc are equal + self.assertEqual(version, shard_layout.get('version')) + # check the shard num from metrics and json rpc are equal + self.assertEqual(num_shards, len(boundary_accounts) + 1) + + # This may be flaky - it shouldn't - but it may. We collect metrics + # after the block is processed. If there is some delay the shard + # layout may change and the assertions below will fail. + + # TODO(resharding) Why is epoch offset needed here? + if height <= 2 * self.epoch_length + self.epoch_offset: + self.assertEqual(version, self.genesis_shard_layout_version) + self.assertEqual(num_shards, self.genesis_num_shards) + else: + self.assertEqual(version, self.target_shard_layout_version) + self.assertEqual(num_shards, self.target_num_shards) + + if height >= 4 * self.epoch_length: + break + + node0.kill() + node1.kill() + + logger.info("The resharding test is finished.") + + +if __name__ == '__main__': + unittest.main() diff --git a/pytest/tests/sanity/resharding_error_handling.py b/pytest/tests/sanity/resharding_error_handling.py new file mode 100644 index 000000000..ac2414531 --- /dev/null +++ b/pytest/tests/sanity/resharding_error_handling.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 + +# Test for checking error handling during resharding. Spins up a few nodes from +# genesis with the previous shard layout. Stops the nodes in the middle of the +# epoch before resharding and corrupts the state snapshot. Resumes the nodes and +# verifies that the error is reported correctly. + +# Usage: +# python3 pytest/tests/sanity/resharding_error_handling.py + +import unittest +import sys +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from configured_logger import logger +from cluster import corrupt_state_snapshot, get_binary_protocol_version, init_cluster, load_config, spin_up_node +from utils import MetricsTracker, poll_blocks, wait_for_blocks +from resharding_lib import get_genesis_shard_layout_version, get_target_shard_layout_version, get_genesis_num_shards, get_target_num_shards, get_genesis_config_changes, get_client_config_changes + + +class ReshardingErrorHandlingTest(unittest.TestCase): + + def setUp(self) -> None: + self.epoch_length = 10 + self.config = load_config() + self.binary_protocol_version = get_binary_protocol_version(self.config) + assert self.binary_protocol_version is not None + + self.genesis_shard_layout_version = get_genesis_shard_layout_version( + self.binary_protocol_version) + self.target_shard_layout_version = get_target_shard_layout_version( + self.binary_protocol_version) + + self.genesis_num_shards = get_genesis_num_shards( + self.binary_protocol_version) + self.target_num_shards = get_target_num_shards( + self.binary_protocol_version) + + # timeline by block number + # epoch_length + 2 - snapshot is requested + # epoch_length + 3 - snapshot is finished + # epoch_length + 4 - stop the nodes, corrupt the snapshot, start nodes + # epoch_length + 4 - resharding starts and fails + # epoch_length * 2 + 1 - last block while node is still healthy before chain + # upgrades to the new shard layout + def test_resharding(self): + logger.info("The resharding test is starting.") + num_nodes = 2 + + genesis_config_changes = get_genesis_config_changes( + self.epoch_length, self.binary_protocol_version, logger) + client_config_changes = get_client_config_changes(num_nodes, 10) + + unc_root, [node0_dir, node1_dir] = init_cluster( + num_nodes=num_nodes, + num_observers=0, + num_shards=1, + config=self.config, + genesis_config_changes=genesis_config_changes, + client_config_changes=client_config_changes, + ) + + node0 = spin_up_node( + self.config, + unc_root, + node0_dir, + 0, + ) + node1 = spin_up_node( + self.config, + unc_root, + node1_dir, + 1, + boot_node=node0, + ) + + logger.info("wait until the snapshot is ready") + wait_for_blocks(node0, target=self.epoch_length + 4) + wait_for_blocks(node1, target=self.epoch_length + 4) + + logger.info("the snapshot should be ready, stopping nodes") + node0.kill(gentle=True) + node1.kill(gentle=True) + + logger.info("corrupting the state snapshot of node0") + output = corrupt_state_snapshot( + self.config, + node0_dir, + self.genesis_shard_layout_version, + ) + logger.info(f"corrupted state snapshot\n{output}") + + # Update the initial delay to start resharding as soon as possible. + client_config_changes = get_client_config_changes(1, 0)[0] + node0.change_config(client_config_changes) + node1.change_config(client_config_changes) + + logger.info("restarting nodes") + node0.start() + node1.start(boot_node=node0) + + all_failed_observed = False + + metrics = MetricsTracker(node0) + for height, _ in poll_blocks(node0): + status = metrics.get_metric_all_values("unc_resharding_status") + logger.info(f"#{height} resharding status {status}") + + if len(status) > 0: + all_failed = all([s == -1.0 for (_, s) in status]) + all_failed_observed = all_failed_observed or all_failed + + # The node should be able to survive until the end of the epoch even + # though resharding is broken. Only break after the last block of epoch. + if height >= self.epoch_length * 2: + break + + node0.kill(gentle=True) + node1.kill(gentle=True) + + # Resharding should fail for all shards. + self.assertTrue(all_failed_observed) + + logger.info("The resharding error handling test is finished.") + + +if __name__ == '__main__': + unittest.main() diff --git a/pytest/tests/sanity/resharding_rpc_tx.py b/pytest/tests/sanity/resharding_rpc_tx.py new file mode 100644 index 000000000..550ffd0d9 --- /dev/null +++ b/pytest/tests/sanity/resharding_rpc_tx.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +""" +Testing RPC call to transaction status after a resharding event. +We create two account that we know would fall into different shards after resharding. +We submit a transfer transaction between the accounts and verify the transaction status after resharding. +""" + +import sys +import unittest +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from configured_logger import logger +import cluster +from resharding_lib import get_target_shard_layout_version, get_target_num_shards, get_genesis_config_changes, get_client_config_changes +import transaction +from utils import MetricsTracker, poll_blocks +import key + +STARTING_AMOUNT = 123 * (10**24) + + +class ReshardingRpcTx(unittest.TestCase): + + def setUp(self) -> None: + self.epoch_length = 5 + self.config = cluster.load_config() + self.binary_protocol_version = cluster.get_binary_protocol_version( + self.config) + assert self.binary_protocol_version is not None + + self.target_shard_layout_version = get_target_shard_layout_version( + self.binary_protocol_version) + self.target_num_shards = get_target_num_shards( + self.binary_protocol_version) + + def __setup_account(self, account_id, nonce): + """ Create an account with full access key and balance. """ + encoded_block_hash = self.node.get_latest_block().hash_bytes + account = key.Key.from_random(account_id) + account_tx = transaction.sign_create_account_with_full_access_key_and_balance_tx( + self.node.signer_key, account.account_id, account, STARTING_AMOUNT, + nonce, encoded_block_hash) + response = self.node.send_tx_and_wait(account_tx, timeout=20) + assert 'error' not in response, response + assert 'Failure' not in response['result']['status'], response + return account + + def __submit_transfer_tx(self, from_key, to_account_id, nonce): + """ Submit a transfer transaction and wait for the response. """ + encoded_block_hash = self.node.get_latest_block().hash_bytes + payment_tx = transaction.sign_payment_tx(from_key, to_account_id, 100, + nonce, encoded_block_hash) + response = self.node.send_tx_and_wait(payment_tx, timeout=20) + assert 'error' not in response, response + assert 'Failure' not in response['result']['status'], response + return response + + def __verify_tx_status(self, transfer_response, sender_account_id): + tx_hash = transfer_response['result']['transaction']['hash'] + response = self.node.get_tx(tx_hash, sender_account_id) + assert response == transfer_response, response + pass + + def test_resharding_rpc_tx(self): + num_nodes = 2 + genesis_config_changes = get_genesis_config_changes( + self.epoch_length, self.binary_protocol_version, logger) + client_config_changes = get_client_config_changes(num_nodes) + nodes = cluster.start_cluster( + num_nodes=num_nodes, + num_observers=0, + num_shards=1, + config=self.config, + genesis_config_changes=genesis_config_changes, + client_config_changes=client_config_changes) + self.node = nodes[0] + + # The shard boundaries are at "kkuuue2akv_1630967379.near" and "tge-lockup.sweat" for shard 3 and 4 + # We would like to create accounts that are in different shards + # The first account before and after resharding is in shard 3 + # The second account after resharding is in shard 4 + account0 = self.__setup_account('setup_test_account.test0', 1) + account1 = self.__setup_account('z_setup_test_account.test0', 2) + + # Poll one block to create the accounts + poll_blocks(self.node) + + # Submit a transfer transaction between the accounts, we would verify the transaction status later + response0 = self.__submit_transfer_tx(account0, account1.account_id, + 6000001) + response1 = self.__submit_transfer_tx(account1, account0.account_id, + 12000001) + + metrics_tracker = MetricsTracker(self.node) + for height, _ in poll_blocks(self.node): + # wait for resharding to complete + if height < 2 * self.epoch_length: + continue + + # Quick check whether resharding is completed + version = metrics_tracker.get_int_metric_value( + "unc_shard_layout_version") + num_shards = metrics_tracker.get_int_metric_value( + "unc_shard_layout_num_shards") + self.assertEqual(version, self.target_shard_layout_version) + self.assertEqual(num_shards, self.target_num_shards) + + # Verify the transaction status after resharding + self.__verify_tx_status(response0, account0.account_id) + self.__verify_tx_status(response0, account1.account_id) + self.__verify_tx_status(response1, account0.account_id) + self.__verify_tx_status(response1, account1.account_id) + break + + +if __name__ == '__main__': + unittest.main() diff --git a/pytest/tests/sanity/restart.py b/pytest/tests/sanity/restart.py new file mode 100755 index 000000000..33424510c --- /dev/null +++ b/pytest/tests/sanity/restart.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +# Spins up two nodes, and waits until they produce 20 blocks. +# Kills the nodes, restarts them, makes sure they produce 20 more blocks +# Sets epoch length to 10 + +import sys, time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger + +TIMEOUT = 150 +BLOCKS1 = 20 +BLOCKS2 = 40 + +# The epoch size is 10 and the validators will miss a few blocks during restarts, so the kickout threshold has to be adjusted. +# Otherwise validators will get kicked out and the blockchain will grind to a halt. The threshold is set to 0 to avoid any such problems. +# On production network this isn't a problem because the epoch size is a few orders of magnitude larger. +nodes = start_cluster( + 2, 0, 2, None, + [["epoch_length", 10], ["block_producer_kickout_threshold", 0]], {}) + +started = time.time() + +max_height = 0 +last_heights = [0 for _ in nodes] +seen_heights = [set() for _ in nodes] +last_common = [[0 for _ in nodes] for _ in nodes] + +height_to_hash = {} + + +def min_common(): + return min([min(x) for x in last_common]) + + +def heights_report(): + for i, sh in enumerate(seen_heights): + logger.info("Node %s: %s" % (i, sorted(list(sh)))) + + +first_round = True +while max_height < BLOCKS1: + assert time.time() - started < TIMEOUT + for i, node in enumerate(nodes): + height, hash_ = node.get_latest_block() + + if height > max_height: + max_height = height + if height % 10 == 0: + logger.info("Reached height %s, min common: %s" % + (height, min_common())) + + if height not in height_to_hash: + height_to_hash[height] = hash_ + else: + assert height_to_hash[ + height] == hash_, "height: %s, h1: %s, h2: %s" % ( + height, hash_, height_to_hash[height]) + + last_heights[i] = height + seen_heights[i].add(height) + for j, _ in enumerate(nodes): + if height in seen_heights[j]: + last_common[i][j] = height + last_common[j][i] = height + if not first_round: + # Don't check it in the first round - min_common will be 0, as we didn't + # read the status from all nodes. + assert min_common() + 2 >= height, heights_report() + + first_round = False + +assert min_common() + 2 >= BLOCKS1, heights_report() + +for node in nodes: + node.kill() + +nodes[0].start() +nodes[1].start(boot_node=nodes[0]) + +first_round = True +while max_height < BLOCKS2: + assert time.time() - started < TIMEOUT + for i, node in enumerate(nodes): + height, hash_ = node.get_latest_block() + + if height > max_height: + max_height = height + if height % 10 == 0: + logger.info("Reached height %s, min common: %s" % + (height, min_common())) + + if height not in height_to_hash: + height_to_hash[height] = hash_ + else: + assert height_to_hash[ + height] == hash_, "height: %s, h1: %s, h2: %s" % ( + height, hash_, height_to_hash[height]) + + last_heights[i] = height + seen_heights[i].add(height) + for j, _ in enumerate(nodes): + if height in seen_heights[j]: + last_common[i][j] = height + last_common[j][i] = height + + if not first_round: + # Don't check it in the first round - min_common will be 0, as we didn't + # read the status from all nodes. + assert min_common() + 2 >= height, heights_report() + +assert min_common() + 2 >= BLOCKS2, heights_report() diff --git a/pytest/tests/sanity/rosetta.py b/pytest/tests/sanity/rosetta.py new file mode 100644 index 000000000..fe6cdc2a3 --- /dev/null +++ b/pytest/tests/sanity/rosetta.py @@ -0,0 +1,1054 @@ +#!/usr/bin/env python3 + +import dataclasses +import json +import os +import pathlib +import sys +import time +import typing +import unittest + +import base64 +import base58 +import ed25519 +import requests +import requests.exceptions + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from utils import load_binary_file +from configured_logger import logger +import cluster +import key +import mocknet_helpers +import transaction + +JsonDict = typing.Dict[str, typing.Any] +BlockIdentifier = typing.Union[str, int, JsonDict] +TxIdentifier = typing.Union[str, JsonDict] + + +def account_identifier(account_id: str) -> JsonDict: + return {'address': account_id} + + +def block_identifier(block_id: BlockIdentifier) -> JsonDict: + if isinstance(block_id, int): + return {'index': block_id} + if isinstance(block_id, str): + return {'hash': block_id} + if isinstance(block_id, dict): + return block_id + raise TypeError(f'{type(block_id).__name__} is not a block identifier') + + +def tx_identifier(tx_id: TxIdentifier) -> JsonDict: + if isinstance(tx_id, str): + return {'hash': tx_id} + if isinstance(tx_id, dict): + return tx_id + raise TypeError(f'{type(tx_id).__name__} is not a transaction identifier') + + +class RosettaExecResult: + identifier: typing.Dict[str, str] + _rpc: 'RosettaRPC' + _block_id: BlockIdentifier + __block: typing.Optional[JsonDict] = None + __transaction: typing.Optional[JsonDict] = None + + def __init__(self, rpc: 'RosettaRPC', block_id: BlockIdentifier, + identifier: typing.Dict[str, str]) -> None: + self._rpc = rpc + self._block_id = block_id + self.identifier = identifier + + @property + def hash(self) -> str: + """The Rosetta hash of the transaction identifier. + + This will be a value in ‘:’ format as used in + Rosetta RPC where prefix is either ‘tx’ for transactions or ‘receipt’ + for receipts. + """ + return self.identifier['hash'] + + @property + def unc_hash(self) -> str: + """A NEAR transaction hash in base85. + + Compared to `hash` it’s just the `’ part of the Rosetta + identifier which is the NEAR transaction or receipt hash (depending on + what comes before the colon). This can be used to query NEAR through + JSON RPC. + """ + return self.identifier['hash'].split(':')[1] + + def block(self) -> JsonDict: + """Returns the block in which the transaction was executed. + + When this method or `transaction` method is called the first time, it + queries the node to find the node which includes the transaction. The + return value is memoised so subsequent calls won’t do the querying. + + Returns: + A Rosetta RPC block data object. + """ + return self.__get_transaction()[0] + + def transaction(self) -> JsonDict: + """Returns the transaction details from Rosetta RPC. + + When this method or `block` method is called the first time, it queries + the node to find the node which includes the transaction. The return + value is memoised so subsequent calls won’t do the querying. + + Returns: + A Rosetta RPC transaction data object. + """ + return self.__get_transaction()[1] + + def related(self, num: int) -> typing.Optional[JsonDict]: + """Returns related transaction or None if there aren’t that many. + + The method uses `transaction` method so all comments regarding fetching + the data from node apply to it as well. + + Returns: + If the transaction has at least `num+1` related transactions returns + a new `RosettaExecResult` object which can be used to fetch the + transaction. Otherwise, returns None. + """ + block, transaction = self.__get_transaction() + related = transaction.get('related_transactions', ()) + if len(related) <= num: + return None + return type(self)(self._rpc, block['block_identifier'], + related[num]['transaction_identifier']) + + def __get_transaction(self) -> typing.Tuple[JsonDict, JsonDict]: + """Fetches transaction and its block from the node if not yet retrieved. + + Returns: + (block, transaction) tuple where first element is Rosetta Block + object and second Rosetta Transaction object. + """ + if self.__block and self.__transaction: + return self.__block, self.__transaction + timeout = time.monotonic() + 10 + while time.monotonic() < timeout: + while True: + try: + block = self._rpc.get_block(block_id=self._block_id) + except requests.exceptions.HTTPError as ex: + res = ex.response + try: + if res.status_code == 500 and res.json()['code'] == 404: + break + except: + pass + raise + if not block: + break + for tx in block['transactions']: + if tx['transaction_identifier']['hash'] == self.hash: + related = ', '.join( + related['transaction_identifier']['hash'] + for related in tx.get('related_transactions', + ())) or 'none' + logger.info(f'Receipts of {self.hash}: {related}') + self.__memoised = (block, tx) + return self.__memoised + self._block_id = int(block['block_identifier']['index']) + 1 + time.sleep(0.25) + assert False, f'Transaction {self.hash} did not complete in 10 seconds' + + +class RosettaRPC: + node: cluster.BaseNode + href: str + network_identifier: JsonDict + + def __init__(self, + *, + node: cluster.BaseNode, + host: str = '127.0.0.1', + port: int = 5040) -> None: + self.node = node + self.href = f'http://{host}:{port}' + self.network_identifier = self.get_network_identifier() + + def get_network_identifier(self): + result = requests.post(f'{self.href}/network/list', + headers={'content-type': 'application/json'}, + data=json.dumps({'metadata': {}})) + result.raise_for_status() + return result.json()['network_identifiers'][0] + + def rpc(self, path: str, **data: typing.Any) -> JsonDict: + data['network_identifier'] = self.network_identifier + result = requests.post(f'{self.href}{path}', + headers={'content-type': 'application/json'}, + data=json.dumps(data, indent=True)) + result.raise_for_status() + return result.json() + + def exec_operations(self, signer: key.Key, + *operations) -> RosettaExecResult: + """Sends given operations to Construction API. + + Args: + signer: Account signing the operations. + operations: List of operations to perform. + Returns: + A RosettaExecResult object which can be used to get hash of the + submitted transaction or wait on the transaction completion. + """ + height = self.node.get_latest_block().height + + public_key = { + 'hex_bytes': signer.decoded_pk().hex(), + 'curve_type': 'edwards25519' + } + options = self.rpc('/construction/preprocess', + operations=operations)['options'] + metadata = self.rpc('/construction/metadata', + options=options, + public_keys=[public_key])['metadata'] + payloads = self.rpc('/construction/payloads', + operations=operations, + public_keys=[public_key], + metadata=metadata) + payload = payloads['payloads'][0] + unsigned = payloads['unsigned_transaction'] + signature = signer.sign_bytes(bytearray.fromhex(payload['hex_bytes'])) + signed = self.rpc('/construction/combine', + unsigned_transaction=unsigned, + signatures=[{ + 'signing_payload': payload, + 'hex_bytes': signature.hex(), + 'signature_type': 'ed25519', + 'public_key': public_key + }])['signed_transaction'] + tx = self.rpc('/construction/submit', signed_transaction=signed) + tx_hash = tx['transaction_identifier']['hash'] + logger.info(f'Transaction hash: {tx_hash}') + return RosettaExecResult(self, height, tx['transaction_identifier']) + + def transfer(self, *, src: key.Key, dst: key.Key, amount: int, + **kw) -> RosettaExecResult: + currency = {'symbol': 'NEAR', 'decimals': 24} + return self.exec_operations( + src, { + 'operation_identifier': { + 'index': 0 + }, + 'type': 'TRANSFER', + 'account': { + 'address': src.account_id + }, + 'amount': { + 'value': str(-amount), + 'currency': currency + }, + }, { + 'operation_identifier': { + 'index': 1 + }, + 'related_operations': [{ + 'index': 0 + }], + 'type': 'TRANSFER', + 'account': { + 'address': dst.account_id + }, + 'amount': { + 'value': str(amount), + 'currency': currency + }, + }, **kw) + + def ft_transfer(self, *, src: key.Key, dst: key.Key, amount: int, + **kw) -> RosettaExecResult: + currency = {'symbol': 'NEAR', 'decimals': 24} + return self.exec_operations( + src, { + 'operation_identifier': { + 'index': 0 + }, + 'type': 'INITIATE_FUNCTION_CALL', + 'account': { + 'address': src.account_id + } + }, { + 'operation_identifier': { + 'index': 0 + }, + 'type': 'FUNCTION_CALL', + 'account': { + 'address': src.account_id + }, + 'metadata': { + 'method_name': + 'ft_transfer', + 'args': + bytes( + json.dumps({ + 'receiver_id': dst.account_id, + 'amount': 1 + }), 'utf-8').hex(), + 'attached_gas': + "300000000000000", + } + }, **kw) + + def add_full_access_key(self, account: key.Key, public_key_hex: str, + **kw) -> RosettaExecResult: + return self.exec_operations( + account, { + "operation_identifier": { + "index": 0 + }, + "type": "INITIATE_ADD_KEY", + "account": { + "address": account.account_id + } + }, { + "operation_identifier": { + "index": 1 + }, + "related_operations": [{ + "index": 0 + }], + "type": "ADD_KEY", + "account": { + "address": account.account_id + }, + "metadata": { + "public_key": { + "hex_bytes": public_key_hex, + "curve_type": "edwards25519" + } + } + }, **kw) + + def delete_account(self, account: key.Key, refund_to: key.Key, + **kw) -> RosettaExecResult: + return self.exec_operations( + account, { + 'operation_identifier': { + 'index': 0 + }, + 'type': 'INITIATE_DELETE_ACCOUNT', + 'account': { + 'address': account.account_id + }, + }, { + 'operation_identifier': { + 'index': 0 + }, + 'type': 'DELETE_ACCOUNT', + 'account': { + 'address': account.account_id + }, + }, { + 'operation_identifier': { + 'index': 0 + }, + 'type': 'REFUND_DELETE_ACCOUNT', + 'account': { + 'address': refund_to.account_id + }, + }, **kw) + + def get_block(self, *, block_id: BlockIdentifier) -> JsonDict: + + def fetch(block_id: BlockIdentifier) -> JsonDict: + block_id = block_identifier(block_id) + block = self.rpc('/block', block_identifier=block_id)['block'] + + # Order of transactions on the list is not guaranteed so normalise + # it by sorting by hash. + # TODO(mina86): Verify that this is still true. + block.get( + 'transactions', + []).sort(key=lambda tx: tx['transaction_identifier']['hash']) + + return block + + block = fetch(block_id) + + # Verify that fetching block by index and hash produce the same result + # as well as getting individual transactions produce the same object as + # the one returned when fetching transactions individually. + assert block == fetch(block['block_identifier']['index']) + assert block == fetch(block['block_identifier']['hash']) + for tx in block['transactions']: + assert tx == self.get_transaction( + block_id=block_id, tx_id=tx['transaction_identifier']) + + return block + + def get_transaction(self, *, block_id: BlockIdentifier, + tx_id: TxIdentifier) -> JsonDict: + res = self.rpc('/block/transaction', + block_identifier=block_identifier(block_id), + transaction_identifier=tx_identifier(tx_id)) + return res['transaction'] + + def get_account_balances(self, *, account_id: str) -> JsonDict: + res = self.rpc('/account/balance', + account_identifier=account_identifier(account_id)) + return res['balances'] + + +class RosettaTestCase(unittest.TestCase): + node = None + rosetta = None + + def __init__(self, *args, **kw) -> None: + super().__init__(*args, **kw) + self.maxDiff = None + + @classmethod + def setUpClass(cls) -> None: + cls.node = cluster.start_cluster(1, 0, 1, {}, {}, { + 0: { + 'rosetta_rpc': { + 'addr': '0.0.0.0:5040', + 'cors_allowed_origins': ['*'] + }, + } + })[0] + cls.rosetta = RosettaRPC(node=cls.node, host=cls.node.rpc_addr()[0]) + + @classmethod + def tearDownClass(cls) -> None: + cls.node.cleanup() + + def test_zero_balance_account(self) -> None: + """Tests storage staking requirements for low-storage accounts. + + Creates an implicit account by sending it 1 yoctoNEAR (not enough to + cover storage). However, the zero-balance allowance established in + NEP-448 should cover the storage staking requirement. Then, we + transfer 10**22 yoctoNEAR to the account, which should be enough to + cover the storage staking requirement for the 6 full-access keys we + then add to the account, exceeding the zero-balance account allowance. + """ + + test_amount = 10**22 + key_space_cost = 41964925000000000000 + validator = self.node.validator_key + implicit = key.Key.implicit_account() + + # first transfer 1 yoctoNEAR to create the account + # not enough to cover storage, but the zero-balance allowance should cover it + result = self.rosetta.transfer(src=validator, dst=implicit, amount=1) + + block = result.block() + tx = result.transaction() + json_res = self.node.get_tx(result.unc_hash, implicit.account_id) + json_res = json_res['result'] + receipt_ids = json_res['transaction_outcome']['outcome']['receipt_ids'] + receipt_id = {'hash': 'receipt:' + receipt_ids[0]} + + # Fetch the receipt through Rosetta RPC. + result = RosettaExecResult(self.rosetta, block, receipt_id) + related = result.related(0) + + balances = self.rosetta.get_account_balances( + account_id=implicit.account_id) + + # even though 1 yoctoNEAR is not enough to cover the storage cost, + # since the account should be consuming less than 770 bytes of storage, + # it should be allowed nonetheless. + self.assertEqual(balances, [{ + 'value': '1', + 'currency': { + 'symbol': 'NEAR', + 'decimals': 24 + } + }]) + + # transfer the rest of the amount + result = self.rosetta.transfer(src=validator, + dst=implicit, + amount=(test_amount - 1)) + + block = result.block() + tx = result.transaction() + json_res = self.node.get_tx(result.unc_hash, implicit.account_id) + json_res = json_res['result'] + receipt_ids = json_res['transaction_outcome']['outcome']['receipt_ids'] + receipt_id = {'hash': 'receipt:' + receipt_ids[0]} + + # Fetch the receipt through Rosetta RPC. + result = RosettaExecResult(self.rosetta, block, receipt_id) + related = result.related(0) + + balances = self.rosetta.get_account_balances( + account_id=implicit.account_id) + + self.assertEqual(balances, [{ + 'value': str(test_amount), + 'currency': { + 'symbol': 'NEAR', + 'decimals': 24 + } + }]) + + # add 6 keys to go over the zero-balance account free storage allowance + public_keys_hex = [ + "17595386a67d36afc73872e60916f83217d789dc60b5d037563998e6651111cf", + "7940aac79a425f194621ab5c4e38b7841dddae90b20eaf28f7f78caec911bcf4", + "0554fffef36614d7c49b3088c4c1fb66613ff05fb30927b582b43aed0b25b549", + "09d36e25c5a3ac440a798252982dd92b67d8de60894df3177cb4ff30a890cafd", + "e0ca119be7211f3dfed1768fc9ab235b6af06a205077ef23166dd1cbfd2ac7fc", + "98f1a49296fb7156980d325a25e1bfeb4f123dd98c90fa0492699c55387f7ef3", + ] + for pk in public_keys_hex: + result = self.rosetta.add_full_access_key(implicit, pk) + + block = result.block() + tx = result.transaction() + json_res = self.node.get_tx(result.unc_hash, implicit.account_id) + json_res = json_res['result'] + receipt_ids = json_res['transaction_outcome']['outcome'][ + 'receipt_ids'] + receipt_id = {'hash': 'receipt:' + receipt_ids[0]} + + # Fetch the receipt through Rosetta RPC. + result = RosettaExecResult(self.rosetta, block, receipt_id) + related = result.related(0) + + balances = self.rosetta.get_account_balances( + account_id=implicit.account_id) + + # no longer a zero-balance account + self.assertEqual(test_amount - key_space_cost * len(public_keys_hex), + int(balances[0]['value'])) + + def test_get_block(self) -> None: + """Tests getting blocks and transactions. + + Fetches the first and second blocks to see if the responses look as they + should. Then fetches one transaction from each of those blocks to again + see if the returned data looks as expected. Since the exact hashes + differ each time the test runs, those are assumed to be correct. + """ + + block_0 = self.rosetta.get_block(block_id=0) + block_0_id = block_0['block_identifier'] + trans_0_id = 'block:' + block_0_id['hash'] + trans_0 = { + 'metadata': { + 'type': 'BLOCK' + }, + 'operations': [{ + 'account': { + 'address': 'near' + }, + 'amount': { + 'currency': { + 'decimals': 24, + 'symbol': 'NEAR' + }, + 'value': '1000000000000000000000000000000000' + }, + 'operation_identifier': { + 'index': 0 + }, + 'status': 'SUCCESS', + 'type': 'TRANSFER' + }, { + 'account': { + 'address': 'test0' + }, + 'amount': { + 'currency': { + 'decimals': 24, + 'symbol': 'NEAR' + }, + 'value': '950000000000000000000000000000000' + }, + 'operation_identifier': { + 'index': 1 + }, + 'status': 'SUCCESS', + 'type': 'TRANSFER' + }, { + 'account': { + 'address': 'test0', + 'sub_account': { + 'address': 'LOCKED' + } + }, + 'amount': { + 'currency': { + 'decimals': 24, + 'symbol': 'NEAR' + }, + 'value': '50000000000000000000000000000000' + }, + 'operation_identifier': { + 'index': 2 + }, + 'status': 'SUCCESS', + 'type': 'TRANSFER' + }], + 'transaction_identifier': { + 'hash': trans_0_id + } + } + self.assertEqual( + { + 'block_identifier': block_0_id, + # Genesis block’s parent is genesis block itself. + 'parent_block_identifier': block_0_id, + 'timestamp': block_0['timestamp'], + 'transactions': [trans_0] + }, + block_0) + + # Getting by hash should work and should return the exact same thing + block = self.rosetta.get_block(block_id=block_0_id['hash']) + self.assertEqual(block_0, block) + + # Get transaction from genesis block. + tr = self.rosetta.get_transaction(block_id=block_0_id, tx_id=trans_0_id) + self.assertEqual(trans_0, tr) + + # Block at height=1 should have genesis block as parent and only + # validator update as a single operation. + block_1 = self.rosetta.get_block(block_id=1) + block_1_id = block_1['block_identifier'] + trans_1_id = {'hash': 'block-validators-update:' + block_1_id['hash']} + trans_1 = { + 'metadata': { + 'type': 'TRANSACTION' + }, + 'operations': [], + 'transaction_identifier': trans_1_id, + } + self.assertEqual( + { + 'block_identifier': { + 'hash': block_1_id['hash'], + 'index': 1 + }, + 'parent_block_identifier': + block_0_id, + 'timestamp': + block_1['timestamp'], + 'transactions': [{ + 'metadata': { + 'type': 'TRANSACTION' + }, + 'operations': [], + 'transaction_identifier': trans_1_id + }] + }, block_1) + + # Get transaction from the second block + self.assertEqual( + trans_1, + self.rosetta.get_transaction(block_id=block_1_id, tx_id=trans_1_id)) + + def test_fungible_token_transfer(self) -> None: + """Tests sending fungible token transfer. + + First sends some funds from validator’s account to an implicit account, + then checks how the transaction looks through Data API and finally + deletes that account refunding the validator account. + """ + test_amount = 1 + validator = self.node.validator_key + implicit = key.Key.implicit_account() + contract_key = self.node.validator_key + contract = load_binary_file( + '../../../runtime/unc-test-contracts/res/fungible_token.wasm') + + ### 1. Deploy the ft smart contract + latest_block_hash = self.node.get_latest_block().hash + deploy_contract_tx = transaction.sign_deploy_contract_tx( + contract_key, contract, 10, + base58.b58decode(latest_block_hash.encode('utf8'))) + deploy_contract_response = (self.node.send_tx_and_wait( + deploy_contract_tx, 100)) + logger.info(f'Deploying contract: {deploy_contract_response}') + + ### 2. Create implicit account. + logger.info(f'Creating implicit account: {implicit.account_id}') + result = self.rosetta.transfer(src=validator, + dst=implicit, + amount=test_amount) + ### 3. Storage deposit + account_id_arg = f'"account_id" : "{implicit.account_id}"' + data = json.dumps({'account_id': implicit.account_id}) + logger.info(f'Account id of implicit: {data}') + s = f'{{"account_id": "{implicit.account_id}"}}' + + block_hash = self.node.get_latest_block().hash_bytes + tx = transaction.sign_function_call_tx(contract_key, + contract_key.account_id, + 'storage_deposit', + bytes(s, + 'utf-8'), 300000000000000, + 1250000000000000000000, 20, + block_hash) + res = self.node.send_tx_and_wait(tx, 100) + logger.info(f'Storage deposit: {res}') + mocknet_helpers.wait_at_least_one_block() + + ### 4. Rosetta ft_call + ft_result = self.rosetta.ft_transfer(src=validator, + dst=implicit, + amount=test_amount) + # logger.info(f'Ft_transfer result: {ft_result}') + # tx = ft_result.transaction() + # logger.info(f'Ft_transfer result: {tx}') + json_res = self.node.get_tx(ft_result.unc_hash, implicit.account_id) + logger.info(f'Tx-result: {json_res}') + + def test_get_block_nonexistent(self) -> None: + """Tests querying non-existent blocks and transactions. + + Queries for various blocks and transactions which do not exist on the + chain to see if responses are what they should be. + """ + block_0 = self.rosetta.get_block(block_id=0) + block_0_id = block_0['block_identifier'] + trans_0_id = 'block:' + block_0_id['hash'] + + block_1 = self.rosetta.get_block(block_id=1) + block_1_id = block_1['block_identifier'] + trans_1_id = 'block:' + block_1_id['hash'] + + def test(want_code, callback, *args, **kw) -> None: + with self.assertRaises(requests.exceptions.HTTPError) as err: + callback(*args, **kw) + self.assertEqual(500, err.exception.response.status_code) + resp = err.exception.response.json() + self.assertFalse(resp['retriable']) + self.assertEqual(want_code, resp['code']) + + # Query for non-existent blocks + test(404, self.rosetta.get_block, block_id=123456789) + test(400, self.rosetta.get_block, block_id=-123456789) + bogus_hash = 'GJ92SsB76CvfaHHdaC4Vsio6xSHT7fR3EEUoK84tFe99' + test(404, self.rosetta.get_block, block_id=bogus_hash) + test(400, self.rosetta.get_block, block_id='malformed-hash') + + # Query for non-existent transactions + test(404, + self.rosetta.get_transaction, + block_id=block_0_id, + tx_id=trans_1_id) + test(404, + self.rosetta.get_transaction, + block_id=block_1_id, + tx_id=trans_0_id) + + def _get_account_balance(self, + account: key.Key, + require: bool = True) -> typing.Optional[int]: + """Returns balance of given account or None if account doesn’t exist. + + Args: + account: Account to get balance of. + require: If True, require that the account exists. + """ + account_id = account.account_id + result = self.node.get_account(account_id, do_assert=False) + error = result.get('error') + if error is None: + amount = int(result['result']['amount']) + logger.info(f'Account {account_id} balance: {amount} yocto') + return amount + self.assertEqual('UNKNOWN_ACCOUNT', error['cause']['name'], + f'Error fetching account {account_id}: {error}') + if require: + self.fail(f'Account {account.account_id} does not exist') + return None + + def test_implicit_account(self) -> None: + """Tests creating and deleting implicit account + + First sends some funds from validator’s account to an implicit account, + then checks how the transaction looks through Data API and finally + deletes that account refunding the validator account. + """ + test_amount = 10**22 + validator = self.node.validator_key + implicit = key.Key.implicit_account() + + # 1. Create implicit account. + logger.info(f'Creating implicit account: {implicit.account_id}') + result = self.rosetta.transfer(src=validator, + dst=implicit, + amount=test_amount) + # Get the transaction through Rosetta RPC. + block = result.block() + tx = result.transaction() + # Also get it from JSON RPC to compare receipt ids. + json_res = self.node.get_tx(result.unc_hash, implicit.account_id) + json_res = json_res['result'] + receipt_ids = json_res['transaction_outcome']['outcome']['receipt_ids'] + self.assertEqual(1, len(receipt_ids)) + receipt_id = {'hash': 'receipt:' + receipt_ids[0]} + + # There are two operations. The first subtracts `test_amount` from the account and the second one subtracts + # gas fees + value = -int(tx['operations'][0]['amount']['value']) + logger.info(f'Took {value} from validator account') + self.assertEqual(test_amount, value) + gas_payment = -int(tx['operations'][1]['amount']['value']) + self.assertGreater(gas_payment, 0) + + self.assertEqual([{ + 'metadata': { + 'type': 'TRANSACTION' + }, + 'operations': [{ + 'account': { + 'address': 'test0' + }, + 'amount': { + 'currency': { + 'decimals': 24, + 'symbol': 'NEAR' + }, + 'value': str(-value) + }, + 'operation_identifier': { + 'index': 0 + }, + 'status': 'SUCCESS', + 'type': 'TRANSFER', + 'metadata': { + 'predecessor_id': { + 'address': 'test0' + } + } + }, { + 'account': { + 'address': 'test0' + }, + 'amount': { + 'currency': { + 'decimals': 24, + 'symbol': 'NEAR' + }, + 'value': str(-gas_payment) + }, + 'operation_identifier': { + 'index': 1 + }, + 'status': 'SUCCESS', + 'type': 'TRANSFER', + 'metadata': { + 'predecessor_id': { + 'address': 'test0' + }, + 'transfer_fee_type': 'GAS_PREPAYMENT' + } + }], + 'related_transactions': [{ + 'direction': 'forward', + 'transaction_identifier': receipt_id, + }], + 'transaction_identifier': result.identifier, + }], block['transactions']) + + # Fetch the receipt through Rosetta RPC. + result = RosettaExecResult(self.rosetta, block, receipt_id) + related = result.related(0) + self.assertEqual( + { + 'transaction_identifier': result.identifier, + 'operations': [{ + 'operation_identifier': { + 'index': 0 + }, + 'type': 'TRANSFER', + 'status': 'SUCCESS', + 'metadata': { + 'predecessor_id': { + 'address': 'test0' + } + }, + 'account': { + 'address': implicit.account_id, + }, + 'amount': { + 'value': '10000000000000000000000', + 'currency': { + 'symbol': 'NEAR', + 'decimals': 24 + } + } + }], + 'related_transactions': [{ + 'direction': 'forward', + 'transaction_identifier': related and related.identifier + }], + 'metadata': { + 'type': 'TRANSACTION' + } + }, result.transaction()) + + # Fetch the next receipt through Rosetta RPC. + self.assertEqual( + { + 'metadata': { + 'type': 'TRANSACTION' + }, + 'operations': [{ + 'account': { + 'address': 'test0' + }, + 'amount': { + 'currency': { + 'decimals': 24, + 'symbol': 'NEAR' + }, + 'value': '12524843062500000000' + }, + 'operation_identifier': { + 'index': 0 + }, + 'status': 'SUCCESS', + 'type': 'TRANSFER', + 'metadata': { + 'predecessor_id': { + 'address': 'system' + }, + 'transfer_fee_type': 'GAS_REFUND' + } + }], + 'transaction_identifier': related.identifier + }, related.transaction()) + + # 2. Delete the account. + logger.info(f'Deleting implicit account: {implicit.account_id}') + result = self.rosetta.delete_account(implicit, refund_to=validator) + + self.assertEqual( + test_amount, -sum( + int(op['amount']['value']) + for tx in result.block()['transactions'] + for op in tx['operations'])) + + json_res = self.node.get_tx(result.unc_hash, implicit.account_id) + json_res = json_res['result'] + receipt_ids = json_res['transaction_outcome']['outcome']['receipt_ids'] + self.assertEqual(1, len(receipt_ids)) + receipt_id = {'hash': 'receipt:' + receipt_ids[0]} + + receipt_ids = json_res['receipts_outcome'][0]['outcome']['receipt_ids'] + self.assertEqual(1, len(receipt_ids)) + receipt_id_2 = {'hash': 'receipt:' + receipt_ids[0]} + + self.assertEqual( + { + 'metadata': { + 'type': 'TRANSACTION' + }, + 'operations': [{ + 'account': { + 'address': implicit.account_id, + }, + 'amount': { + 'currency': { + 'decimals': 24, + 'symbol': 'NEAR' + }, + 'value': '-51109700000000000000' + }, + 'operation_identifier': { + 'index': 0 + }, + 'status': 'SUCCESS', + 'type': 'TRANSFER', + 'metadata': { + 'predecessor_id': { + 'address': implicit.account_id + } + } + }], + 'related_transactions': [{ + 'direction': 'forward', + 'transaction_identifier': receipt_id, + }], + 'transaction_identifier': result.identifier + }, result.transaction()) + + # Fetch the receipt + result = RosettaExecResult(self.rosetta, block, receipt_id) + self.assertEqual( + { + 'metadata': { + 'type': 'TRANSACTION' + }, + 'operations': [{ + 'account': { + 'address': implicit.account_id, + }, + 'amount': { + 'currency': { + 'decimals': 24, + 'symbol': 'NEAR' + }, + 'value': '-9948890300000000000000' + }, + 'operation_identifier': { + 'index': 0 + }, + 'status': 'SUCCESS', + 'type': 'TRANSFER' + }], + 'related_transactions': [{ + 'direction': 'forward', + 'transaction_identifier': receipt_id_2 + }], + 'transaction_identifier': receipt_id + }, result.transaction()) + + # Fetch receipt’s receipt + result = RosettaExecResult(self.rosetta, block, receipt_id_2) + self.assertEqual( + { + 'metadata': { + 'type': 'TRANSACTION' + }, + 'operations': [{ + 'account': { + 'address': 'test0' + }, + 'amount': { + 'currency': { + 'decimals': 24, + 'symbol': 'NEAR' + }, + 'value': '9948890300000000000000' + }, + 'operation_identifier': { + 'index': 0 + }, + 'status': 'SUCCESS', + 'type': 'TRANSFER', + 'metadata': { + 'predecessor_id': { + 'address': "system" + }, + 'transfer_fee_type': 'GAS_REFUND' + } + }], + 'transaction_identifier': receipt_id_2 + }, result.transaction()) + + +if __name__ == '__main__': + unittest.main() diff --git a/pytest/tests/sanity/rpc_finality.py b/pytest/tests/sanity/rpc_finality.py new file mode 100644 index 000000000..3ce313649 --- /dev/null +++ b/pytest/tests/sanity/rpc_finality.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +# The test does the token transfer between the accounts, and tries to +# stop the network just in the right moment (so that the block with the refund receipt +# is not finalized). +# This way, we can verify that our json RPC returns correct values for different finality requests. + +import sys +import pathlib + +import unittest +from typing import List + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) +import utils +from cluster import start_cluster, LocalNode +from configured_logger import logger +from transaction import sign_payment_tx + + +class TestRpcFinality(unittest.TestCase): + + def test_finality(self): + # set higher block delay to make test more reliable. + min_block_delay = 3 + + consensus = { + "min_block_production_delay": { + "secs": min_block_delay, + "nanos": 0, + }, + "max_block_production_delay": { + "secs": min_block_delay * 2, + "nanos": 0, + }, + "max_block_wait_delay": { + "secs": min_block_delay * 3, + "nanos": 0, + } + } + + config = {node_id: {"consensus": consensus} for node_id in range(3)} + + nodes: List[LocalNode] = start_cluster(3, 0, 1, None, [ + ["min_gas_price", 0], + ["epoch_length", 100], + ], config) + + utils.wait_for_blocks(nodes[0], target=3) + + balances = { + account: int(nodes[0].get_account('test0')['result']['amount']) + for account in ['test0', 'test1'] + } + + token_transfer = 10 + latest_block_hash = nodes[0].get_latest_block().hash_bytes + tx = sign_payment_tx(nodes[0].signer_key, 'test1', token_transfer, 1, + latest_block_hash) + logger.info("About to send payment") + # this transaction will be added to the block (probably around block 5) + # and the the receipts & transfers will happen in the next block (block 6). + # This function should return as soon as block 6 arrives in node0. + logger.info(nodes[0].send_tx_rpc(tx, wait_until='INCLUDED', timeout=10)) + logger.info("Done") + + # kill one validating node so that block cannot be finalized. + nodes[2].kill() + + print( + f"Block height is {nodes[0].get_latest_block().height} (should be 6)" + ) + + # So now the situation is following: + # Block 6 (head) - has the final receipt (that adds state to test1) + # Block 5 (doomslug) - has the transaction (so this is the moment when state is removed from test0) + # Block 4 (final) - has no information about the transaction. + + # So with optimistic finality: test0 = -10, test1 = +10 + # with doomslug (state as of block 5): test0 = -10, test1 = 0 + # with final (state as of block 4): test0 = 0, test1 = 0 + + for acc_id in ['test0', 'test1']: + amounts = [ + int(nodes[0].get_account(acc_id, finality)['result']['amount']) + - balances[acc_id] + for finality in ["optimistic", "unc-final", "final"] + ] + print(f"Account amounts: {acc_id}: {amounts}") + + if acc_id == 'test0': + self.assertEqual([-10, -10, 0], amounts) + else: + self.assertEqual([10, 0, 0], amounts) + + +if __name__ == '__main__': + unittest.main() diff --git a/pytest/tests/sanity/rpc_hash.py b/pytest/tests/sanity/rpc_hash.py new file mode 100644 index 000000000..4fcf27fba --- /dev/null +++ b/pytest/tests/sanity/rpc_hash.py @@ -0,0 +1,338 @@ +#!/usr/bin/env python3 +# Test computing block hash from data provided by JSON RPCs. + +import base58 +import hashlib +import pathlib +import sys +import typing +import unittest + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from configured_logger import logger +import cluster +import utils +import serializer + +import messages +import messages.crypto +import messages.block + +config = cluster.load_config() +binary_protocol_version = cluster.get_binary_protocol_version(config) +logger.info(f"binary_protocol_version: {binary_protocol_version}") +assert binary_protocol_version is not None +BLOCK_HEADER_V4_PROTOCOL_VERSION = 63 + + +def serialize(msg: typing.Any) -> bytes: + return serializer.BinarySerializer(messages.schema).serialize(msg) + + +def compute_block_hash(header: typing.Dict[str, typing.Any], + msg_version: int) -> str: + """Computes block hash based on given block header. + + Args: + header: JSON representation of the block header. + msg_version: Version of the BlockHeaderInnerRest to use when serialising + and computing hash. This depends on protocol version used when the + block was generated. + Returns: + Base58-encoded block hash. + """ + + def get_int(key: str) -> typing.Optional[int]: + value = header.get(key) + if value is None: + return None + return int(value) + + def get_hash(key: str) -> typing.Optional[bytes]: + value = header.get(key) + if value is None: + return value + result = base58.b58decode(value) + assert len(result) == 32, (key, value, len(result)) + return result + + def sha256(*chunks: bytes) -> bytes: + return hashlib.sha256(b''.join(chunks)).digest() + + prev_hash = get_hash('prev_hash') + + inner_lite = messages.block.BlockHeaderInnerLite() + inner_lite.height = get_int('height') + inner_lite.epoch_id = get_hash('epoch_id') + inner_lite.next_epoch_id = get_hash('next_epoch_id') + inner_lite.prev_state_root = get_hash('prev_state_root') + inner_lite.outcome_root = get_hash('outcome_root') + inner_lite.timestamp = get_int('timestamp_nanosec') + inner_lite.next_bp_hash = get_hash('next_bp_hash') + inner_lite.block_merkle_root = get_hash('block_merkle_root') + inner_lite_blob = serialize(inner_lite) + inner_lite_hash = sha256(inner_lite_blob) + + inner_rest_msg = { + 1: messages.block.BlockHeaderInnerRest, + 2: messages.block.BlockHeaderInnerRestV2, + 3: messages.block.BlockHeaderInnerRestV3, + 4: messages.block.BlockHeaderInnerRestV4, + }[msg_version] + + inner_rest = inner_rest_msg() + # Some of the fields are superfluous in some of the versions of the message + # but that’s quite all right. Serialiser will ignore them and + # unconditionally setting them makes the code simpler. + inner_rest.block_body_hash = get_hash('block_body_hash') + inner_rest.chunk_receipts_root = get_hash('chunk_receipts_root') + inner_rest.chunk_headers_root = get_hash('chunk_headers_root') + inner_rest.chunk_tx_root = get_hash('chunk_tx_root') + inner_rest.chunks_included = get_int('chunks_included') + inner_rest.challenges_root = get_hash('challenges_root') + inner_rest.random_value = get_hash('random_value') + # TODO: Handle non-empty list. + inner_rest.validator_proposals = header['validator_proposals'] + inner_rest.chunk_mask = bytes(header['chunk_mask']) + inner_rest.gas_price = get_int('gas_price') + inner_rest.total_supply = get_int('total_supply') + # TODO: Handle non-empty list. + inner_rest.challenges_result = header['challenges_result'] + inner_rest.last_final_block = get_hash('last_final_block') + inner_rest.last_ds_final_block = get_hash('last_ds_final_block') + inner_rest.block_ordinal = get_int('block_ordinal') + inner_rest.prev_height = get_int('prev_height') + inner_rest.epoch_sync_data_hash = get_hash('epoch_sync_data_hash') + inner_rest.approvals = [ + approval and messages.crypto.Signature(approval) + for approval in header['approvals'] + ] + inner_rest.latest_protocol_version = get_int('latest_protocol_version') + inner_rest_blob = serialize(inner_rest) + inner_rest_hash = sha256(inner_rest_blob) + + inner_hash = sha256(inner_lite_hash + inner_rest_hash) + block_hash = sha256(inner_hash + prev_hash) + + return base58.b58encode(block_hash).decode('ascii') + + +class HashTestCase(unittest.TestCase): + + def __init__(self, *args, **kw) -> None: + super().__init__(*args, **kw) + self.maxDiff = None + + def test_compute_block_hash(self): + """Tests that compute_block_hash function works correctly. + + This is a sanity check for cases when someone modifies the test code + itself. If this test breaks than all the other tests will break as + well. This test runs the compute_block_hash function on a well-known + static input checking expected hash. + """ + header = { + 'height': + 4, + 'prev_height': + 3, + 'epoch_id': + '11111111111111111111111111111111', + 'next_epoch_id': + 'AuatKw3hiGmXed3uT2u4Die6ZRGZhEHn34kTyVpGnYLM', + 'prev_hash': + 'BUcVEkMq3DcZzDGgeh1sb7FFuD86XYcXpEt25Cf34LuP', + 'prev_state_root': + 'Bn786g4GdJJigSP4qRSaVCfeMotWVX88cV1LTZhD6o3z', + 'block_body_hash': + '4K3NiGuqYGqKPnYp6XeGd2kdN4P9veL6rYcWkLKWXZCu', # some arbitrary string + 'chunk_receipts_root': + '9ETNjrt6MkwTgSVMMbpukfxRshSD1avBUUa4R4NuqwHv', + 'chunk_headers_root': + 'Fk7jeakmi8eruvv4L4ToKs7MV1YG64ETZtASQYjGBWK1', + 'chunk_tx_root': + '7tkzFg8RHBmMw1ncRJZCCZAizgq4rwCftTKYLce8RU8t', + 'outcome_root': + '7tkzFg8RHBmMw1ncRJZCCZAizgq4rwCftTKYLce8RU8t', + 'chunks_included': + 1, + 'challenges_root': + '11111111111111111111111111111111', + 'timestamp': + 1642022757141096960, + 'timestamp_nanosec': + '1642022757141096960', + 'random_value': + 'GxYrjCxQtfG2K7hX6w4aPs3usTskzfCkbVc2icSQMF7h', + 'validator_proposals': [], + 'chunk_mask': [True], + 'gas_price': + '1000000000', + 'block_ordinal': + 4, + 'rent_paid': + '0', + 'validator_reward': + '0', + 'total_supply': + '3000000000000000000000000000000000', + 'challenges_result': [], + 'last_final_block': + 'GTudmqKJQjEVCrdi31vcHqXoEpvEScmZ9BhBf3gPJ4pp', + 'last_ds_final_block': + 'BUcVEkMq3DcZzDGgeh1sb7FFuD86XYcXpEt25Cf34LuP', + 'next_bp_hash': + '236RGxQc2xSqukyiBkixtZSqKu679ZxeS6vP8zzAL9vW', + 'block_merkle_root': + 'Gf3uWgULzc5WDuaAq4feehh7M1TFRFxTWVv2xH6AsnpA', + 'epoch_sync_data_hash': + '4JTQn5LGcxdx4xstsAXgXHcP3oHKatzdzHBw6atBDSWV', + 'approvals': [ + 'ed25519:5Jdeg8rk5hAbcooyxXQSTcxBgUK39Z8Qtfkhqmpi26biU26md5wBiFvkAEGXrMyn3sgq3cTMG8Lr3HD7RxWPjkPh', + 'ed25519:4vqTaN6bucu6ALsb1m15e8HWGGxLQeKJhWrcU8zPRrzfkZbakaSzW8rfas2ZG89rFKheZUyrnZRKooRny6YKFyKi' + ], + 'signature': + 'ed25519:5mGi9dyuyt7TnSpPFjbEWSJThDdiEV9NNQB11knXvRbxSv8XfBT5tdVVFypeqpZjeB3fD7qgJpWhTj3KvdGbcXdu', + 'latest_protocol_version': + 50 + } + + for msg_ver, block_hash in ( + (1, '3ckGjcedZiN3RnvfiuEN83BtudDTVa9Pub4yZ8R737qt'), + (2, 'Hezx56VTH815G6JTzWqJ7iuWxdR9X4ZqGwteaDF8q2z'), + (3, 'Finjr87adnUqpFHVXbmAWiVAY12EA9G4DfUw27XYHox'), + (4, '2QfdGyGWByEeL2ZSy8u2LoBa4pdDwf5KoDrr94W6oeB6'), + ): + self.assertEqual(block_hash, compute_block_hash(header, msg_ver)) + + # Now try with a different block body hash + header[ + 'block_body_hash'] = '4rMxTeTF9LehPbzB2xhVa4xWVtbyjRfvL7qsxc8sL7WP' + for msg_ver, block_hash in ( + (1, '3ckGjcedZiN3RnvfiuEN83BtudDTVa9Pub4yZ8R737qt'), + (2, 'Hezx56VTH815G6JTzWqJ7iuWxdR9X4ZqGwteaDF8q2z'), + (3, 'Finjr87adnUqpFHVXbmAWiVAY12EA9G4DfUw27XYHox'), + (4, '3Cdm4sS9b4jdypMezP8ta6p2ecyRSJC9uaGUJTY18MUH'), + ): + self.assertEqual(block_hash, compute_block_hash(header, msg_ver)) + + # Now try witohut epoch_sync_data_hash + header['epoch_sync_data_hash'] = None + for msg_ver, block_hash in ( + (1, '3ckGjcedZiN3RnvfiuEN83BtudDTVa9Pub4yZ8R737qt'), + (2, 'Hezx56VTH815G6JTzWqJ7iuWxdR9X4ZqGwteaDF8q2z'), + (3, '82v8RAc66tWpdjRCsoSrgnzpU6JMhpjbWKmUEcfkzX6T'), + (4, '9BYhkbWkKTLJj46goq5WPEzUJDf5juHJnBu2jjoHL7yc'), + ): + self.assertEqual(block_hash, compute_block_hash(header, msg_ver)) + + # Now try with one approval missing + header['approvals'][1] = None + for msg_ver, block_hash in ( + (1, 'EE2JtxdqWLBDKqNARxdFDqH36mEJ1xJ6LjQ9f7qCSDRE'), + (2, '2WdpJD5dYPjEMn3EYbm1BhGgCAX7ksxJGTQm4xHazBxt'), + (3, '3bx6vfbH8GrYp8UFMagiBgYyKMH63D7Qo5J7jCsNbh9o'), + (4, 'CTDBpUpCdhdCjCMfaFD5r96PyKDK756aXw69toLYEaSH'), + ): + self.assertEqual(block_hash, compute_block_hash(header, msg_ver)) + + def _test_block_hash(self, + msg_version: int, + protocol_version: typing.Optional[int] = None) -> None: + """Starts a cluster, fetches blocks and computes their hashes. + + The cluster is started with genesis configured to use given protocol + version. The code fetches blocks until: 1) a block with all approvals + set is encountered, 2) another block with at least one approval missing + and 3) at least ten blocks total are checked. + + Args: + msg_version: Version of the BlockHeaderInnerRest to use when + serialising and computing hash. + protocol_version: If given, protocol version to use in the cluster + (which will be set in genesis); If not given, cluster will be + started with the newest supported protocol version. + """ + genesis_overrides = [] + if protocol_version: + genesis_overrides = [['protocol_version', protocol_version]] + + nodes = () + try: + nodes = cluster.start_cluster(4, 0, 4, None, genesis_overrides, {}) + got_all_set = False + got_some_unset = False + count = 0 + for block_id in utils.poll_blocks(nodes[0]): + header = nodes[0].get_block(block_id.hash)['result']['header'] + self.assertEqual((block_id.height, block_id.hash), + (header['height'], header['hash']), + (block_id, header)) + got = compute_block_hash(header, msg_version) + self.assertEqual(header['hash'], got, header) + + if all(header['approvals']): + if not got_all_set: + nodes[1].kill() + got_all_set = True + elif any(approval is None for approval in header['approvals']): + got_some_unset = True + + count += 1 + if got_all_set and got_some_unset and count >= 10: + break + finally: + for node in nodes: + node.cleanup() + + def test_block_hash_v1(self): + """Starts a cluster using protocol version 24 and verifies block hashes. + + The cluster is started with a protocol version in which the first + version of the BlockHeaderInnerRest has been used. + """ + self._test_block_hash(1, 24) + + def test_block_hash_v2(self): + """Starts a cluster using protocol version 42 and verifies block hashes. + + The cluster is started with a protocol version in which the second + version of the BlockHeaderInnerRest has been used. + """ + self._test_block_hash(2, 42) + + def test_block_hash_v3(self): + """Starts a cluster using protocol version 50 and verifies block hashes. + + The cluster is started with a protocol version in which the third + version of the BlockHeaderInnerRest has been used. + """ + self._test_block_hash(3, 50) + + if binary_protocol_version >= BLOCK_HEADER_V4_PROTOCOL_VERSION: + + def test_block_hash_v4(self): + """Starts a cluster using protocol version BLOCK_HEADER_V4_PROTOCOL_VERSION and verifies block hashes. + + The cluster is started with a protocol version in which the fourth + version of the BlockHeaderInnerRest has been used. + """ + self._test_block_hash(4, BLOCK_HEADER_V4_PROTOCOL_VERSION) + + def test_block_hash_latest(self): + """Starts a cluster using latest protocol and verifies block hashes. + + The cluster is started with the newest protocol version supported by the + node. If this test fails while others pass this may indicate that a new + BlockHeaderInnerRest message has been introduced and this test needs to + be updated to support it. + """ + if binary_protocol_version >= BLOCK_HEADER_V4_PROTOCOL_VERSION: + self._test_block_hash(4) + else: + self._test_block_hash(3) + + +if __name__ == '__main__': + unittest.main() diff --git a/pytest/tests/sanity/rpc_light_client_execution_outcome_proof.py b/pytest/tests/sanity/rpc_light_client_execution_outcome_proof.py new file mode 100755 index 000000000..a99577751 --- /dev/null +++ b/pytest/tests/sanity/rpc_light_client_execution_outcome_proof.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python3 +# Spins up two nodes, deploy a smart contract to one node, +# Send a transaction to call a contract method. Check that +# the transaction and receipts execution outcome proof for +# light client works + +import base58, base64 +import hashlib +import json +import struct +import sys +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) +from cluster import start_cluster, Key +from serializer import BinarySerializer +import transaction +import time +import utils +from lightclient import compute_block_hash + + +class PartialExecutionOutcome: + pass + + +class PartialExecutionStatus: + pass + + +class Unknown: + pass + + +class Failure: + pass + + +partial_execution_outcome_schema = dict([ + [ + PartialExecutionOutcome, + { + 'kind': + 'struct', + 'fields': [ + ['receipt_ids', [[32]]], + ['gas_burnt', 'u64'], + ['tokens_burnt', 'u128'], + ['executor_id', 'string'], + ['status', PartialExecutionStatus], + ] + }, + ], + [ + PartialExecutionStatus, { + 'kind': + 'enum', + 'field': + 'enum', + 'values': [ + ['unknown', Unknown], + ['failure', Failure], + ['successValue', ['u8']], + ['successReceiptId', [32]], + ] + } + ], + [Unknown, { + 'kind': 'struct', + 'fields': [] + }], + [Failure, { + 'kind': 'struct', + 'fields': [] + }], +]) + + +def serialize_execution_outcome_with_id(outcome, id): + partial_outcome = PartialExecutionOutcome() + partial_outcome.receipt_ids = [ + base58.b58decode(x) for x in outcome['receipt_ids'] + ] + partial_outcome.gas_burnt = outcome['gas_burnt'] + partial_outcome.tokens_burnt = int(outcome['tokens_burnt']) + partial_outcome.executor_id = outcome['executor_id'] + execution_status = PartialExecutionStatus() + if 'SuccessValue' in outcome['status']: + execution_status.enum = 'successValue' + execution_status.successValue = base64.b64decode( + outcome['status']['SuccessValue']) + elif 'SuccessReceiptId' in outcome['status']: + execution_status.enum = 'successReceiptId' + execution_status.successReceiptId = base58.b58decode( + outcome['status']['SuccessReceiptId']) + elif 'Failure' in outcome['status']: + execution_status.enum = 'failure' + execution_status.failure = Failure() + elif 'Unknown' in outcome['status']: + execution_status.enum = 'unknown' + execution_status.unknown = Unknown + else: + assert False, f'status not supported: {outcome["status"]}' + partial_outcome.status = execution_status + msg = BinarySerializer(partial_execution_outcome_schema).serialize( + partial_outcome) + partial_outcome_hash = hashlib.sha256(msg).digest() + outcome_hashes = [partial_outcome_hash] + for log_entry in outcome['logs']: + outcome_hashes.append( + hashlib.sha256(bytes(log_entry, 'utf-8')).digest()) + res = [base58.b58decode(id)] + res.extend(outcome_hashes) + borsh_res = bytearray() + length = len(res) + for i in range(4): + borsh_res.append(length & 255) + length //= 256 + for hash_result in res: + borsh_res += bytearray(hash_result) + return borsh_res + + +def check_transaction_outcome_proof(nodes, should_succeed, nonce): + latest_block_hash = nodes[1].get_latest_block().hash_bytes + function_caller_key = nodes[0].signer_key + gas = 300000000000000 if should_succeed else 1000 + + function_call_1_tx = transaction.sign_function_call_tx( + function_caller_key, nodes[0].signer_key.account_id, 'write_key_value', + struct.pack(' latest_block_height + 2 and + light_client_request_block_hash is None): + light_client_request_block_hash = hash_ + if cur_height > latest_block_height + 7: + break + + light_client_block = nodes[0].json_rpc( + 'next_light_client_block', [light_client_request_block_hash])['result'] + light_client_block_hash = compute_block_hash( + light_client_block['inner_lite'], light_client_block['inner_rest_hash'], + light_client_block['prev_block_hash']).decode('utf-8') + + queries = [{ + "type": + "transaction", + "transaction_hash": + function_call_result['result']['transaction_outcome']['id'], + "sender_id": + "test0", + "light_client_head": + light_client_block_hash + }] + outcomes = [ + (function_call_result['result']['transaction_outcome']['outcome'], + function_call_result['result']['transaction_outcome']['id']) + ] + for receipt_outcome in function_call_result['result']['receipts_outcome']: + outcomes.append((receipt_outcome['outcome'], receipt_outcome['id'])) + queries.append({ + "type": "receipt", + "receipt_id": receipt_outcome['id'], + "receiver_id": "test0", + "light_client_head": light_client_block_hash + }) + + for query, (outcome, id) in zip(queries, outcomes): + res = nodes[0].json_rpc('light_client_proof', query, timeout=10) + assert 'error' not in res, res + light_client_proof = res['result'] + # check that execution outcome root proof is valid + execution_outcome_hash = hashlib.sha256( + serialize_execution_outcome_with_id(outcome, id)).digest() + outcome_root = utils.compute_merkle_root_from_path( + light_client_proof['outcome_proof']['proof'], + execution_outcome_hash) + block_outcome_root = utils.compute_merkle_root_from_path( + light_client_proof['outcome_root_proof'], + hashlib.sha256(outcome_root).digest()) + block = nodes[0].json_rpc( + 'block', + {"block_id": light_client_proof['outcome_proof']['block_hash']}) + expected_root = block['result']['header']['outcome_root'] + assert base58.b58decode( + expected_root + ) == block_outcome_root, f'expected outcome root {expected_root} actual {base58.b58encode(block_outcome_root)}' + # check that the light block header is valid + block_header_lite = light_client_proof['block_header_lite'] + computed_block_hash = compute_block_hash( + block_header_lite['inner_lite'], + block_header_lite['inner_rest_hash'], + block_header_lite['prev_block_hash']) + assert light_client_proof['outcome_proof'][ + 'block_hash'] == computed_block_hash.decode( + 'utf-8' + ), f'expected block hash {light_client_proof["outcome_proof"]["block_hash"]} actual {computed_block_hash}' + # check that block proof is valid + block_merkle_root = utils.compute_merkle_root_from_path( + light_client_proof['block_proof'], + light_client_proof['outcome_proof']['block_hash']) + assert base58.b58decode( + light_client_block['inner_lite']['block_merkle_root'] + ) == block_merkle_root, f'expected block merkle root {light_client_block["inner_lite"]["block_merkle_root"]} actual {base58.b58encode(block_merkle_root)}' + + +def test_outcome_proof(): + nodes = start_cluster( + 2, 0, 1, None, + [["epoch_length", 1000], ["block_producer_kickout_threshold", 80]], {}) + + latest_block_hash = nodes[0].get_latest_block().hash_bytes + deploy_contract_tx = transaction.sign_deploy_contract_tx( + nodes[0].signer_key, utils.load_test_contract(), 10, latest_block_hash) + deploy_contract_response = nodes[0].send_tx_and_wait(deploy_contract_tx, 15) + assert 'error' not in deploy_contract_response, deploy_contract_response + + check_transaction_outcome_proof(nodes, True, 20) + check_transaction_outcome_proof(nodes, False, 30) + + +if __name__ == '__main__': + test_outcome_proof() diff --git a/pytest/tests/sanity/rpc_max_gas_burnt.py b/pytest/tests/sanity/rpc_max_gas_burnt.py new file mode 100755 index 000000000..665e0b71a --- /dev/null +++ b/pytest/tests/sanity/rpc_max_gas_burnt.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +"""Test max_gas_burnt_view client configuration. + +Spins up two nodes with different max_gas_burnt_view client configuration, +deploys a smart contract and finally calls a view function against both nodes +expecting the one with low max_gas_burnt_view limit to fail. +""" + +import sys +import base58 +import base64 +import json +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) +import cluster +import utils +import transaction + + +def test_max_gas_burnt_view(): + nodes = cluster.start_cluster( + 2, + 0, + 1, + config=None, + genesis_config_changes=[], + client_config_changes={1: { + 'max_gas_burnt_view': int(5e10) + }}) + + contract_key = nodes[0].signer_key + contract = utils.load_test_contract() + + # Deploy the fib smart contract + latest_block_hash = nodes[0].get_latest_block().hash_bytes + deploy_contract_tx = transaction.sign_deploy_contract_tx( + contract_key, contract, 10, latest_block_hash) + deploy_contract_response = nodes[0].send_tx_and_wait(deploy_contract_tx, 10) + + def call_fib(node, n): + args = base64.b64encode(bytes([n])).decode('ascii') + return node.call_function(contract_key.account_id, + 'fibonacci', + args, + timeout=10).get('result') + + # Call view function of the smart contract via the first node. This should + # succeed. + result = call_fib(nodes[0], 25) + assert 'result' in result and 'error' not in result, ( + 'Expected "result" and no "error" in response, got: {}'.format(result)) + n = int.from_bytes(bytes(result['result']), 'little') + assert n == 75025, 'Expected result to be 75025 but got: {}'.format(n) + + # Same but against the second node. This should fail because of gas limit. + result = call_fib(nodes[1], 25) + assert 'result' not in result and 'error' in result, ( + 'Expected "error" and no "result" in response, got: {}'.format(result)) + error = result['error'] + assert 'HostError(GasLimitExceeded)' in error, ( + 'Expected error due to GasLimitExceeded but got: {}'.format(error)) + + # It should still succeed for small arguments. + result = call_fib(nodes[1], 5) + assert 'result' in result and 'error' not in result, ( + 'Expected "result" and no "error" in response, got: {}'.format(result)) + n = int.from_bytes(bytes(result['result']), 'little') + assert n == 5, 'Expected result to be 5 but got: {}'.format(n) + + +if __name__ == '__main__': + test_max_gas_burnt_view() diff --git a/pytest/tests/sanity/rpc_state_changes.py b/pytest/tests/sanity/rpc_state_changes.py new file mode 100755 index 000000000..009bc9d08 --- /dev/null +++ b/pytest/tests/sanity/rpc_state_changes.py @@ -0,0 +1,581 @@ +#!/usr/bin/env python3 +# Spins up four nodes, deploy a smart contract to one node, +# and call various scenarios to trigger store changes. +# Check that the key changes are observable via `changes` RPC call. + +import base58, base64 +import json +import struct +import sys +import threading +import pathlib + +import deepdiff + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) +from cluster import start_cluster +from key import Key +from utils import load_test_contract +import transaction + +nodes = start_cluster( + 4, 0, 1, None, + [["epoch_length", 1000], ["block_producer_kickout_threshold", 80]], {}) + + +def assert_changes_in_block_response(request, expected_response): + for node_index, node in enumerate(nodes): + response = node.get_changes_in_block(request) + assert 'result' in response, "the request did not succeed: %r" % response + response = response['result'] + diff = deepdiff.DeepDiff(expected_response, response) + assert not diff, \ + "query node #%d same changes gives different results %r (expected VS actual):\n%r\n%r" \ + % (node_index, diff, expected_response, response) + + +def assert_changes_response(request, expected_response, **kwargs): + for node_index, node in enumerate(nodes): + response = node.get_changes(request) + assert 'result' in response, "the request did not succeed: %r" % response + response = response['result'] + diff = deepdiff.DeepDiff(expected_response, response, **kwargs) + assert not diff, \ + "query node #%d same changes gives different results %r (expected VS actual):\n%r\n%r" \ + % (node_index, diff, expected_response, response) + + +def test_changes_with_new_account_with_access_key(): + """ + Plan: + 1. Create a new account with an access key. + 2. Observe the changes in the block where the receipt lands. + 3. Remove the access key. + 4. Observe the changes in the block where the receipt lands. + """ + + base_account_id = nodes[0].signer_key.account_id + # re-use the key as a new account access key + new_key = Key( + account_id=f'rpc_key_value_changes.{base_account_id}', + pk=nodes[1].signer_key.pk, + sk=nodes[1].signer_key.sk, + ) + + # Step 1 + latest_block_hash = nodes[0].get_latest_block().hash_bytes + create_account_tx = transaction.sign_create_account_with_full_access_key_and_balance_tx( + creator_key=nodes[0].signer_key, + new_account_id=new_key.account_id, + new_key=new_key, + balance=10**24, + nonce=7, + block_hash=latest_block_hash) + new_account_response = nodes[0].send_tx_and_wait(create_account_tx, 10) + + # Step 2 + block_hash = new_account_response['result']['receipts_outcome'][0][ + 'block_hash'] + assert_changes_in_block_response(request={"block_id": block_hash}, + expected_response={ + "block_hash": + block_hash, + "changes": [{ + "type": "account_touched", + "account_id": new_key.account_id, + }, { + "type": "access_key_touched", + "account_id": new_key.account_id, + }] + }) + + base_request = { + "block_id": block_hash, + "changes_type": "all_access_key_changes", + } + for request in [ + # Test empty account_ids + { + **base_request, "account_ids": [] + }, + # Test an account_id that is a prefix of the original account_id. + { + **base_request, "account_ids": [new_key.account_id[:-1]] + }, + # Test an account_id that has the original account_id as a prefix. + { + **base_request, "account_ids": [new_key.account_id + '_extra'] + }, + ]: + assert_changes_response(request=request, + expected_response={ + "block_hash": block_hash, + "changes": [] + }) + + # Test happy-path + block_header = nodes[0].get_block(block_hash)['result']['header'] + prev_block_header = nodes[0].get_block( + block_header['prev_hash'])['result']['header'] + nonce = prev_block_header['height'] * 1000000 + expected_response = { + "block_hash": + block_hash, + "changes": [{ + "cause": { + "type": + "receipt_processing", + "receipt_hash": + new_account_response["result"]["receipts_outcome"][0]["id"], + }, + "type": "access_key_update", + "change": { + "account_id": new_key.account_id, + "public_key": new_key.pk, + "access_key": { + "nonce": nonce, + "permission": "FullAccess" + }, + } + }] + } + for request in [ + { + "block_id": block_hash, + "changes_type": "all_access_key_changes", + "account_ids": [new_key.account_id], + }, + { + "block_id": + block_hash, + "changes_type": + "all_access_key_changes", + "account_ids": [ + new_key.account_id + '_non_existing1', new_key.account_id, + new_key.account_id + '_non_existing2' + ], + }, + ]: + assert_changes_response(request=request, + expected_response=expected_response) + + # Step 3 + latest_block_hash = nodes[0].get_latest_block().hash_bytes + nonce += 8 + delete_access_key_tx = transaction.sign_delete_access_key_tx( + signer_key=new_key, + target_account_id=new_key.account_id, + key_for_deletion=new_key, + nonce=nonce, + block_hash=latest_block_hash) + delete_access_key_response = nodes[1].send_tx_and_wait( + delete_access_key_tx, 10) + + # Step 4 + block_hash = delete_access_key_response['result']['receipts_outcome'][0][ + 'block_hash'] + assert_changes_in_block_response(request={"block_id": block_hash}, + expected_response={ + "block_hash": + block_hash, + "changes": [{ + "type": "account_touched", + "account_id": new_key.account_id, + }, { + "type": "access_key_touched", + "account_id": new_key.account_id, + }] + }) + + base_request = { + "block_id": block_hash, + "changes_type": "all_access_key_changes", + } + for request in [ + # Test empty account_ids + { + **base_request, "account_ids": [] + }, + # Test an account_id that is a prefix of the original account_id + { + **base_request, "account_ids": [new_key.account_id[:-1]] + }, + # Test an account_id that has the original account_id as a prefix + { + **base_request, "account_ids": [new_key.account_id + '_extra'] + }, + # Test empty keys in single_access_key_changes request + { + "block_id": block_hash, + "changes_type": "single_access_key_changes", + "keys": [] + }, + # Test non-existing account_id + { + "block_id": + block_hash, + "changes_type": + "single_access_key_changes", + "keys": [{ + "account_id": new_key.account_id + '_non_existing1', + "public_key": new_key.pk + },], + }, + # Test non-existing public_key for an existing account_id + { + "block_id": + block_hash, + "changes_type": + "single_access_key_changes", + "keys": [{ + "account_id": new_key.account_id, + "public_key": new_key.pk[:-3] + 'aaa' + },], + }, + ]: + assert_changes_response(request=request, + expected_response={ + "block_hash": block_hash, + "changes": [] + }) + + # Test happy-path + expected_response = { + "block_hash": + block_hash, + "changes": [{ + "cause": { + 'type': + 'transaction_processing', + 'tx_hash': + delete_access_key_response['result']['transaction']['hash'], + }, + "type": "access_key_update", + "change": { + "account_id": new_key.account_id, + "public_key": new_key.pk, + "access_key": { + "nonce": nonce, + "permission": "FullAccess" + }, + } + }, { + "cause": { + "type": + "receipt_processing", + "receipt_hash": + delete_access_key_response["result"]["receipts_outcome"][0] + ["id"] + }, + "type": "access_key_deletion", + "change": { + "account_id": new_key.account_id, + "public_key": new_key.pk, + } + }] + } + + for request in [ + { + "block_id": block_hash, + "changes_type": "all_access_key_changes", + "account_ids": [new_key.account_id], + }, + { + "block_id": + block_hash, + "changes_type": + "all_access_key_changes", + "account_ids": [ + new_key.account_id + '_non_existing1', new_key.account_id, + new_key.account_id + '_non_existing2' + ], + }, + { + "block_id": + block_hash, + "changes_type": + "single_access_key_changes", + "keys": [{ + "account_id": new_key.account_id, + "public_key": new_key.pk + }], + }, + { + "block_id": + block_hash, + "changes_type": + "single_access_key_changes", + "keys": [ + { + "account_id": new_key.account_id + '_non_existing1', + "public_key": new_key.pk + }, + { + "account_id": new_key.account_id, + "public_key": new_key.pk + }, + ], + }, + ]: + assert_changes_response(request=request, + expected_response=expected_response) + + +def test_key_value_changes(): + """ + Plan: + 1. Deploy a contract. + 2. Observe the code changes in the block where the transaction outcome "lands". + 3. Send two transactions to be included into the same block setting and overriding the value of + the same key. + 4. Observe the changes in the block where the transaction outcome "lands". + """ + + contract_key = nodes[0].signer_key + contract_blob = load_test_contract() + + # Step 1 + latest_block_hash = nodes[0].get_latest_block().hash_bytes + deploy_contract_tx = transaction.sign_deploy_contract_tx( + contract_key, contract_blob, 10, latest_block_hash) + deploy_contract_response = nodes[0].send_tx_and_wait(deploy_contract_tx, 10) + + # Step 2 + block_hash = deploy_contract_response['result']['transaction_outcome'][ + 'block_hash'] + assert_changes_in_block_response( + request={"block_id": block_hash}, + expected_response={ + "block_hash": + block_hash, + "changes": [{ + "type": "account_touched", + "account_id": contract_key.account_id, + }, { + "type": "contract_code_touched", + "account_id": contract_key.account_id, + }, { + "type": "access_key_touched", + "account_id": contract_key.account_id, + }] + }) + + base_request = { + "block_id": block_hash, + "changes_type": "contract_code_changes", + } + for request in [ + # Test empty account_ids + { + **base_request, "account_ids": [] + }, + # Test an account_id that is a prefix of the original account_id + { + **base_request, "account_ids": [contract_key.account_id[:-1]] + }, + # Test an account_id that has the original account_id as a prefix + { + **base_request, "account_ids": [contract_key.account_id + '_extra'] + }, + ]: + assert_changes_response(request=request, + expected_response={ + "block_hash": block_hash, + "changes": [] + }) + + # Test happy-path + expected_response = { + "block_hash": + block_hash, + "changes": [{ + "cause": { + "type": + "receipt_processing", + "receipt_hash": + deploy_contract_response["result"]["receipts_outcome"][0] + ["id"], + }, + "type": "contract_code_update", + "change": { + "account_id": contract_key.account_id, + "code_base64": base64.b64encode(contract_blob).decode('utf-8'), + } + },] + } + base_request = { + "block_id": block_hash, + "changes_type": "contract_code_changes", + } + for request in [ + { + **base_request, "account_ids": [contract_key.account_id] + }, + { + **base_request, "account_ids": [ + contract_key.account_id + '_non_existing1', + contract_key.account_id, + contract_key.account_id + '_non_existing2' + ] + }, + ]: + assert_changes_response(request=request, + expected_response=expected_response) + + # Step 3 + latest_block_hash = nodes[1].get_latest_block().hash_bytes + function_caller_key = nodes[0].signer_key + + key = struct.pack('= TARGET_HEIGHT: + break + if height > 1 and not sent_txs: + ctx.send_moar_txs(hash_, 10, False) + logger.info(f'Sending txs at height {height}') + sent_txs = True + +logger.info("stage 1 done") + +# 2. Spin up the second node and make sure it gets to TARGET_HEIGHT +node1 = spin_up_node(config, unc_root, node_dirs[1], 1, boot_node=boot_node) +node1.stop_checking_store() + +node1_height, _ = utils.wait_for_blocks(node1, target=TARGET_HEIGHT) + +logger.info(f"stage 2 done, node1_height: {node1_height}") + +# 3. During (1) we sent some txs. Make sure the state changed. We can't compare to the +# expected balances directly, since the tx sent to the shard that node1 is responsible +# for was never applied, but we can make sure that some change to the state was done, +# and that the totals match (= the receipts was received) +# What we are testing here specifically is that the first node received proper incoming +# receipts during the state sync from the observer. +# `max_inflation_rate` is set to zero, so the rewards do not mess up with the balances +balances = ctx.get_balances() +logger.info("New balances: %s\nNew total supply: %s" % + (balances, sum(balances))) + +assert balances != initial_balances +assert sum(balances) == total_supply + +initial_balances = balances + +logger.info("stage 3 done") + +# 4. Stake for the second node to bring it back up as a validator and wait until it actually +# becomes one + + +def get_validators(): + return set([x['account_id'] for x in boot_node.get_status()['validators']]) + + +logger.info(get_validators()) + +# The stake for node1 must be higher than that of boot_node, so that it can produce blocks +# after the boot_node is brought down +tx = sign_staking_tx(node1.signer_key, node1.validator_key, + 50000000000000000000000000000000, 20, + base58.b58decode(hash_.encode('utf8'))) +boot_node.send_tx(tx) + +validators = get_validators() +assert validators == set(["test0", "test2", "test3"]), validators + +while True: + if time.time() - started > TIMEOUT: + logger.info(get_validators()) + assert False + + if get_validators() == set(["test0", "test1", "test2", "test3"]): + break + + time.sleep(1) + +logger.info("stage 4 done") + +ctx.next_nonce = 100 +# 5. Bring down the first node, then wait until epoch T+3 +last_height = observer.get_latest_block().height + +ctx.nodes = [boot_node, node1, node2, node3, observer] +ctx.act_to_val = [4, 4, 4, 4, 4] + +boot_node.kill() + +for height, hash_ in utils.poll_blocks(observer, + timeout=TIMEOUT, + poll_interval=0.1): + logger.info(f'height: {height}') + if height > last_height + 1: + ctx.send_moar_txs(hash_, 10, False) + logger.info(f'Sending txs at height {height}') + break + +start_epoch = -1 +for epoch_height in utils.poll_epochs(observer, + epoch_length=EPOCH_LENGTH, + timeout=TIMEOUT): + logger.info(f'epoch_height: {epoch_height}') + if start_epoch == -1: + start_epoch = epoch_height + if epoch_height >= start_epoch + 3: + break + +balances = ctx.get_balances() +logger.info("New balances: %s\nNew total supply: %s" % + (balances, sum(balances))) + +ctx.nodes = [observer, node1] +ctx.act_to_val = [0, 0, 0, 0, 0] +logger.info("Observer sees: %s" % ctx.get_balances()) + +assert balances != initial_balances, "current balance %s, initial balance %s" % ( + balances, initial_balances) +assert sum(balances) == total_supply diff --git a/pytest/tests/sanity/spin_up_cluster.py b/pytest/tests/sanity/spin_up_cluster.py new file mode 100755 index 000000000..3bf9e8fff --- /dev/null +++ b/pytest/tests/sanity/spin_up_cluster.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +"""Spins up a two-node cluster and wait for a few blocks to be produced.""" + +import sys +import time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import cluster +from configured_logger import logger +import utils + + +def test_sanity_spin_up(): + """Spins up a two-node cluster and wait for a few blocks to be produced. + + Sets store.path of one of the node to something other than `data` to test if + that option works as well. + + This is just a sanity check that the uncd binary isn’t borked too much. + See . + """ + nodes = cluster.start_cluster( + 2, + 0, + 1, + None, [], + client_config_changes={1: { + 'store': { + 'path': 'atad' + } + }}) + utils.wait_for_blocks(nodes[0], target=4) + # Verify that second node created RocskDB in ‘atad’ directory rather than + # ‘data’. + assert not (pathlib.Path(nodes[1].node_dir) / 'data').exists() + assert (pathlib.Path(nodes[1].node_dir) / 'atad').exists() + + +if __name__ == '__main__': + test_sanity_spin_up() diff --git a/pytest/tests/sanity/split_storage.py b/pytest/tests/sanity/split_storage.py new file mode 100644 index 000000000..ec4b17bde --- /dev/null +++ b/pytest/tests/sanity/split_storage.py @@ -0,0 +1,461 @@ +#!/usr/bin/python3 +""" + Spins up an archival node with cold store configured and verifies that blocks + are copied from hot to cold store. +""" + +import copy +import json +import os +import os.path as path +import pathlib +import shutil +import subprocess +import sys +import time +import unittest + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import (get_config_json, init_cluster, load_config, + set_config_json, spin_up_node) +from configured_logger import logger + +from utils import wait_for_blocks + + +class TestSplitStorage(unittest.TestCase): + + def _steps(self): + for name in dir(self): # dir() result is implicitly sorted + if name.startswith("step"): + yield name, getattr(self, name) + + def test_steps(self): + for _, step in self._steps(): + step() + time.sleep(5) + + def _pretty_json(self, value): + return json.dumps(value, indent=2) + + def _get_split_storage_info(self, node): + return node.json_rpc("EXPERIMENTAL_split_storage_info", {}) + + def _configure_cold_storage(self, node_dir): + node_config = get_config_json(node_dir) + + # Need to create a deepcopy of the store config, otherwise + # store and cold store will point to the same instance and + # both will end up with the same path. + node_config["cold_store"] = copy.deepcopy(node_config["store"]) + node_config["store"]["path"] = path.join(node_dir, 'data') + node_config["cold_store"]["path"] = path.join(node_dir, 'cold_data') + + set_config_json(node_dir, node_config) + + def _configure_hot_storage(self, node_dir, new_path): + node_config = get_config_json(node_dir) + node_config["store"]["path"] = new_path + set_config_json(node_dir, node_config) + + def _check_split_storage_info( + self, + msg, + node, + expected_head_height, + expected_hot_db_kind, + check_cold_head, + ): + info = self._get_split_storage_info(node) + pretty_info = self._pretty_json(info) + logger.info(f"Checking split storage info for the {msg}") + logger.info(f"The split storage info is \n{pretty_info}") + + self.assertNotIn("error", info) + self.assertIn("result", info) + result = info["result"] + head_height = result["head_height"] + final_head_height = result["final_head_height"] + cold_head_height = result["cold_head_height"] + hot_db_kind = result["hot_db_kind"] + + self.assertEqual(hot_db_kind, expected_hot_db_kind) + self.assertGreaterEqual(head_height, expected_head_height) + self.assertGreaterEqual(final_head_height, expected_head_height - 10) + if check_cold_head: + self.assertGreaterEqual(cold_head_height, final_head_height - 10) + else: + self.assertIsNone(cold_head_height) + + # Migrate archival node using rpc node as source for hot db. + # wait_period should be enough block for initial migration to finish, cold_head catch up to head and tail to appear. + # for localnet tests it is enough to use epoch size * number of epochs to keep. + def _migrate_to_split_storage(self, rpc, archival, archival_dir, + wait_period): + logger.info("Phase 1 - Running uncd before migration.") + + # Wait until a few blocks are produced so that we're sure that the db is + # properly created and populated with some data. + n = 5 + n = wait_for_blocks(archival, target=n).height + + self._check_split_storage_info( + "migration_phase_1", + node=archival, + expected_head_height=n, + # The hot db kind should remain archive until fully migrated. + expected_hot_db_kind="Archive", + # The cold storage is not configured so cold head should be none. + check_cold_head=False, + ) + + logger.info("Phase 2 - Setting the cold storage and restarting uncd.") + + self._configure_cold_storage(archival_dir) + archival.kill() + archival.start() + + # Wait for a few seconds to: + # - Give the node enough time to get started. + # - Give the cold store loop enough time to run - it runs every 1s. + # TODO(posvyatokum) this is a quick stop-gap solution to fix nayduck, this + # should be solved by waiting in a loop until cold store head is at + # expected proximity to final head. + time.sleep(4) + + # Wait until enough blocks so that we produce enough blocks to fill 5 + # epochs to trigger GC - otherwise the tail won't be set. + n = max(n, wait_period) + 5 + logger.info(f"Wait until RPC reaches #{n}") + wait_for_blocks(rpc, target=n) + logger.info(f"Wait until archival reaches #{n}") + wait_for_blocks(archival, target=n) + + self._check_split_storage_info( + "migration_phase_2", + node=archival, + expected_head_height=n, + # The hot db kind should remain archive until fully migrated. + expected_hot_db_kind="Archive", + # The cold storage head should be fully caught up by now. + check_cold_head=True, + ) + + logger.info("Phase 3 - Preparing hot storage from rpc backup.") + + # Stop the RPC node in order to dump the db to disk. + rpc.kill() + + rpc_src = path.join(rpc.node_dir, "data") + rpc_dst = path.join(archival.node_dir, "hot_data") + logger.info(f"Copying rpc backup from {rpc_src} to {rpc_dst}") + shutil.copytree(rpc_src, rpc_dst) + + archival_dir = pathlib.Path(archival.node_dir) + with open(archival_dir / 'prepare-hot-stdout', 'w') as stdout, \ + open(archival_dir / 'prepare-hot-stderr', 'w') as stderr: + cmd = [ + str(pathlib.Path(archival.unc_root) / archival.binary_name), + f'--home={archival_dir}', + f'cold-store', + f'prepare-hot', + f'--store-relative-path', + f'hot_data', + ] + logger.info(f"Calling '{' '.join(cmd)}'") + subprocess.check_call( + cmd, + stdin=subprocess.DEVNULL, + stdout=stdout, + stderr=stderr, + env=dict( + os.environ, + RUST_LOG='debug', + ), + ) + + self._configure_hot_storage(archival_dir, rpc_dst) + + logger.info("Phase 4 - After migration.") + + archival.kill() + archival.start() + rpc.start() + + # Wait for a few seconds to: + # - Give the node enough time to get started. + # - Give the cold store loop enough time to run - it runs every 1s. + # TODO(posvyatokum) this is a quick stop-gap solution to fix nayduck, this + # should be solved by waiting in a loop until cold store head is at + # expected proximity to final head. + time.sleep(4) + + # Wait for just a few blocks to make sure uncd correctly restarted. + n += 5 + wait_for_blocks(archival, target=n) + + self._check_split_storage_info( + "migration_phase_4", + node=archival, + expected_head_height=n, + # The migration is over, the hot db kind should be set to hot. + expected_hot_db_kind="Hot", + # The cold storage head should be fully caught up. + check_cold_head=True, + ) + + # Configure cold storage and start uncd. Wait for a few blocks + # and verify that cold head is moving and that it's close behind + # final head. + def step1_base_case_test(self): + logger.info(f"starting test_base_case") + + config = load_config() + client_config_changes = { + 0: { + 'archive': True, + 'save_trie_changes': True, + }, + } + + epoch_length = 5 + genesis_config_changes = [ + ("epoch_length", epoch_length), + ] + + unc_root, [node_dir] = init_cluster( + 1, + 0, + 1, + config, + genesis_config_changes, + client_config_changes, + "test_base_case_", + ) + + self._configure_cold_storage(node_dir) + + node = spin_up_node(config, unc_root, node_dir, 0, single_node=True) + + # Wait until enough blocks are produced so that we're guaranteed that + # cold head has enough time to move and initial migration is finished. + # The initial migration may fail at the beginning because FINAL_HEAD is + # not set. It will retry after 30s. + # TODO it would be better to configure the retry timeout above and set + # it to a lower value to speed up the test. + n = 50 + wait_for_blocks(node, target=n) + + self._check_split_storage_info( + "base_case", + node=node, + expected_head_height=n, + expected_hot_db_kind="Archive", + check_cold_head=True, + ) + + node.kill() + logger.info(f"Stopped node0 from base_chase_test") + + # Test the migration from single storage to split storage. This test spins + # up two nodes, a validator node and an archival node. The validator stays + # alive for the whole duration of the test. The archival node is migrated + # to cold storage. + # - phase 1 - have archival run with single storage for a few blocks + # - phase 2 - configure cold storage and restart archival + # - phase 3 - prepare hot storage from a rpc backup + # - phase 4 - restart archival and check that it's correctly migrated + def step2_migration_test(self): + logger.info(f"Starting the migration test") + + # Archival nodes do not run state sync. This means that if peers + # ran away further than epoch_lenght * gc_epoch_num, archival nodes + # will not be able to further sync. In practice it means we need a long + # enough epoch_length or more gc_epoch_num to keep. + epoch_length = 10 + gc_epoch_num = 3 + + genesis_config_changes = [ + ("epoch_length", epoch_length), + ] + client_config_changes = { + # The validator node should be archival and be the source of + # truth (and sync) for the other nodes. It needs to be archival + # in case it runs away from the real archival node and the latter + # won't be able to sync anymore. + 0: { + 'archive': True, + 'tracked_shards': [0], + }, + # The rpc node will be used as the source of rpc backup db. + 1: { + 'archive': False, + 'tracked_shards': [0], + 'gc_num_epochs_to_keep': gc_epoch_num, + 'state_sync_enabled': True + }, + # The archival node will be migrated to split storage. + 2: { + 'archive': True, + 'tracked_shards': [0], + 'save_trie_changes': True, + }, + } + + config = load_config() + unc_root, [validator_dir, rpc_dir, archival_dir] = init_cluster( + num_nodes=2, + num_observers=1, + num_shards=1, + config=config, + genesis_config_changes=genesis_config_changes, + client_config_changes=client_config_changes, + prefix="test_migration_", + ) + + validator = spin_up_node( + config, + unc_root, + validator_dir, + 0, + ) + rpc = spin_up_node( + config, + unc_root, + rpc_dir, + 1, + boot_node=validator, + ) + archival = spin_up_node( + config, + unc_root, + archival_dir, + 2, + boot_node=validator, + ) + + self._migrate_to_split_storage(rpc, archival, archival_dir, + gc_epoch_num * epoch_length) + + validator.kill() + rpc.kill() + archival.kill() + + # Spin up legacy archival node and split storage node. + # Pause legacy archival node, but continue running split storage node. + # After split storage hot tail is bigger than legacy archival head, restart legacy archival node. + # Make sure that legacy archival node is able to sync after that. + def step3_archival_node_sync_test(self): + logger.info(f"Starting the archival <- split storage sync test") + + # Archival nodes do not run state sync. This means that if peers + # ran away further than epoch_lenght * gc_epoch_num, archival nodes + # will not be able to further sync. In practice it means we need a long + # enough epoch_length or more gc_epoch_num to keep. + epoch_length = 10 + gc_epoch_num = 3 + + genesis_config_changes = [ + ("epoch_length", epoch_length), + ] + client_config_changes = { + 0: { + 'archive': False, + 'tracked_shards': [0], + 'state_sync_enabled': True + }, + 1: { + 'archive': True, + 'tracked_shards': [0], + 'save_trie_changes': True, + 'split_storage': { + 'enable_split_storage_view_client': True + }, + }, + 2: { + 'archive': True, + 'tracked_shards': [0], + }, + 3: { + 'archive': False, + 'tracked_shards': [0], + 'gc_num_epochs_to_keep': gc_epoch_num, + 'state_sync_enabled': True + }, + } + config = load_config() + unc_root, [validator_dir, split_dir, archival_dir, + rpc_dir] = init_cluster( + num_nodes=1, + num_observers=3, + num_shards=1, + config=config, + genesis_config_changes=genesis_config_changes, + client_config_changes=client_config_changes, + prefix="test_archival_node_sync_", + ) + + validator = spin_up_node( + config, + unc_root, + validator_dir, + 0, + ) + split = spin_up_node( + config, + unc_root, + split_dir, + 1, + boot_node=validator, + ) + archival = spin_up_node( + config, + unc_root, + archival_dir, + 2, + boot_node=split, + ) + rpc = spin_up_node( + config, + unc_root, + rpc_dir, + 3, + boot_node=validator, + ) + + # First, migrate split node to split storage + self._migrate_to_split_storage(rpc, split, split_dir, + gc_epoch_num * epoch_length) + + # Remember ~where archival node stopped + n = archival.get_latest_block().height + + logger.info("Kill legacy archival node.") + # Now, kill legacy archival node, so it will be behind after restart and will be forced to sync from split node. + archival.kill() + + logger.info( + "Wait for split storage to have legacy archival head only in cold db." + ) + # Wait for split storage to relly on cold db to sync archival node + wait_for_blocks(split, target=n + epoch_length * gc_epoch_num * 2 + 1) + # Kill validator and rpc so legacy archival doesn't have any peers that may accidentally have some useful data. + validator.kill() + rpc.kill() + + logger.info("Restart legacy archival node.") + # Restart archival node. This should trigger sync. + archival.start(boot_node=split) + time.sleep(10) + + logger.info("Wait for legacy archival node to start syncing.") + + # Archival node should be able to sync to split storage without problems. + wait_for_blocks(archival, target=n + epoch_length, timeout=120) + archival.kill() + split.kill() + + +if __name__ == "__main__": + unittest.main() diff --git a/pytest/tests/sanity/staking1.py b/pytest/tests/sanity/staking1.py new file mode 100755 index 000000000..f07a9adcc --- /dev/null +++ b/pytest/tests/sanity/staking1.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# Spins up with two validators, and one non-validator +# Stakes for the non-validators, ensures it becomes a validator +# Unstakes for them, makes sure they stop being a validator + +import sys, time, base58, random, datetime +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +from transaction import sign_staking_tx +import utils + +TIMEOUT = 150 + +config = None +nodes = start_cluster( + 2, 1, 1, config, + [["epoch_length", 10], ["block_producer_kickout_threshold", 40]], + {2: { + "tracked_shards": [0], + "store.state_snapshot_enabled": True, + }}) + +started = time.time() + + +def get_validators(): + return set([x['account_id'] for x in nodes[0].get_status()['validators']]) + + +def get_stakes(): + return [ + int(nodes[2].get_account("test%s" % i)['result']['locked']) + for i in range(3) + ] + + +hash_ = nodes[2].get_latest_block().hash_bytes + +tx = sign_staking_tx(nodes[2].signer_key, nodes[2].validator_key, + 100000000000000000000000000000000, 2, hash_) +nodes[0].send_tx(tx) + +logger.info("Initial stakes: %s" % get_stakes()) +for height, _ in utils.poll_blocks(nodes[0], timeout=TIMEOUT): + if 'test2' in get_validators(): + logger.info("Normalin, normalin") + assert 20 <= height <= 25, height + break + +tx = sign_staking_tx(nodes[2].signer_key, nodes[2].validator_key, 0, 3, hash_) +nodes[2].send_tx(tx) + +for height, _ in utils.poll_blocks(nodes[0], timeout=TIMEOUT): + if 'test2' not in get_validators(): + logger.info("DONE") + assert 40 <= height <= 45, height + break diff --git a/pytest/tests/sanity/staking2.py b/pytest/tests/sanity/staking2.py new file mode 100755 index 000000000..7677534fe --- /dev/null +++ b/pytest/tests/sanity/staking2.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +# Runs randomized staking transactions and makes some basic checks on the final `staked` values +# In each epoch sends two sets of staking transactions, one when (last_height % 12 == 4), called "fake", and +# one when (last_height % 12 == 7), called "real" (because the former will be overwritten by the later). +# Before the "fake" tx we expect the stakes to be equal to the largest of the last three "real" stakes for +# each node. Before "real" txs it is the largest of the same value, and the last "fake" stake. + +import sys, time, base58, random, logging +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +from transaction import sign_staking_tx + +TIMEOUT = 360 +TIMEOUT_PER_ITER = 30 + +FAKE_OFFSET = 6 +REAL_OFFSET = 12 +EPOCH_LENGTH = 18 + +all_stakes = [] +fake_stakes = [0, 0, 0] +next_nonce = 3 + +# other tests can set `sequence` to some sequence of triplets of stakes +# `do_moar_stakes` first uses the elements of `sequence` for stakes before switching to +# random. See `staking_repro1.py` for an example +sequence = [] + + +def get_validators(): + return set([x['account_id'] for x in nodes[0].get_status()['validators']]) + + +def get_stakes(): + return [ + int(nodes[2].get_account("test%s" % i)['result']['locked']) + for i in range(3) + ] + + +def get_expected_stakes(): + global all_stakes + return [ + max(fake_stakes[i], max([x[i] + for x in all_stakes[-3:]])) + for i in range(3) + ] + + +def do_moar_stakes(last_block_hash, update_expected): + global next_nonce, all_stakes, fake_stakes, sequence + + if len(sequence) == 0: + stakes = [0, 0, 0] + # have 1-2 validators with stake, and the remaining without + # make numbers dibisable by 1M so that we can easily distinguish a situation when the current locked amt has some reward added to it (not divisable by 1M) vs not (divisable by 1M) + stakes[random.randint(0, 2)] = random.randint( + 70000000000000000000000000, 100000000000000000000000000) * 1000000 + stakes[random.randint(0, 2)] = random.randint( + 70000000000000000000000000, 100000000000000000000000000) * 1000000 + else: + stakes = sequence[0] + sequence = sequence[1:] + + vals = get_validators() + val_id = int(list(vals)[0][4:]) + for i in range(3): + tx = sign_staking_tx(nodes[i].signer_key, nodes[i].validator_key, + stakes[i], next_nonce, + base58.b58decode(last_block_hash.encode('utf8'))) + nodes[val_id].send_tx(tx) + next_nonce += 1 + + if update_expected: + fake_stakes = [0, 0, 0] + all_stakes.append(stakes) + else: + fake_stakes = stakes + + logger.info("Sent %s staking txs: %s" % + ("REAL" if update_expected else "fake", stakes)) + + +def doit(seq=[]): + global nodes, all_stakes, sequence + sequence = seq + + config = None + nodes = start_cluster( + 2, 1, 1, config, [["epoch_length", EPOCH_LENGTH], + ["block_producer_kickout_threshold", 40], + ["chunk_producer_kickout_threshold", 40]], { + 0: { + "view_client_throttle_period": { + "secs": 0, + "nanos": 0 + }, + "consensus": { + "state_sync_timeout": { + "secs": 2, + "nanos": 0 + } + } + }, + 1: { + "view_client_throttle_period": { + "secs": 0, + "nanos": 0 + }, + "consensus": { + "state_sync_timeout": { + "secs": 2, + "nanos": 0 + } + } + }, + 2: { + "tracked_shards": [0], + "view_client_throttle_period": { + "secs": 0, + "nanos": 0 + }, + "consensus": { + "state_sync_timeout": { + "secs": 2, + "nanos": 0 + } + }, + "store.state_snapshot_enabled": True, + } + }) + + started = time.time() + last_iter = started + + height, hash_ = nodes[2].get_latest_block() + for i in range(3): + nodes[i].stop_checking_store() + + logger.info("Initial stakes: %s" % get_stakes()) + all_stakes.append(get_stakes()) + + do_moar_stakes(hash_, True) + last_fake_stakes_height = FAKE_OFFSET + last_staked_height = REAL_OFFSET + + while True: + if time.time() - started >= TIMEOUT: + break + + assert time.time() - last_iter < TIMEOUT_PER_ITER + + height, hash_ = nodes[0].get_latest_block() + logging.info( + f"Node 0 at height {height}; time since last staking iteration: {time.time() - last_iter} seconds" + ) + send_fakes = send_reals = False + + if (height + EPOCH_LENGTH - FAKE_OFFSET) // EPOCH_LENGTH > ( + last_fake_stakes_height + EPOCH_LENGTH - + FAKE_OFFSET) // EPOCH_LENGTH: + last_iter = time.time() + + send_fakes = True + + if (height + EPOCH_LENGTH - REAL_OFFSET) // EPOCH_LENGTH > ( + last_staked_height + EPOCH_LENGTH - + REAL_OFFSET) // EPOCH_LENGTH: + + send_reals = True + + if send_fakes or send_reals: + cur_stakes = get_stakes() + logger.info("Current stakes: %s" % cur_stakes) + if len(all_stakes) > 1: + expected_stakes = get_expected_stakes() + logger.info("Expect stakes: %s" % expected_stakes) + for (cur, expected) in zip(cur_stakes, expected_stakes): + if cur % 1000000 == 0: + assert cur == expected + else: + assert expected <= cur <= expected * 1.1 + + do_moar_stakes(hash_, update_expected=send_reals) + + if send_fakes: + last_fake_stakes_height += EPOCH_LENGTH + + elif send_reals: + last_staked_height += EPOCH_LENGTH + + time.sleep(1) + + +if __name__ == "__main__": + doit() diff --git a/pytest/tests/sanity/staking_repro1.py b/pytest/tests/sanity/staking_repro1.py new file mode 100755 index 000000000..088762594 --- /dev/null +++ b/pytest/tests/sanity/staking_repro1.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# reproduces one of the bugs discovered by `staking2.py` + +from staking2 import doit + +# this repro was created before fake stakes were introduced, so each stake after the first one is duplicated to account for that +sequence = [ + [0, 73215476000000000000000000000000, 73203310000000000000000000000000], + [0, 95711839000000000000000000000000, 75774073000000000000000000000000], + [0, 95711839000000000000000000000000, 75774073000000000000000000000000], + [0, 84382393000000000000000000000000, 98749818000000000000000000000000], + [0, 84382393000000000000000000000000, 98749818000000000000000000000000], + [0, 0, 90224511000000000000000000000000], + [0, 0, 90224511000000000000000000000000], + [86432337000000000000000000000000, 76422536000000000000000000000000, 0], + [86432337000000000000000000000000, 76422536000000000000000000000000, 0], + [75063341000000000000000000000000, 0, 80786582000000000000000000000000], + [75063341000000000000000000000000, 0, 80786582000000000000000000000000], + [86164122000000000000000000000000, 0, 73964803000000000000000000000000], + [86164122000000000000000000000000, 0, 73964803000000000000000000000000], + [70076530000000000000000000000000, 0, 0], + [70076530000000000000000000000000, 0, 0], + [0, 78553537000000000000000000000000, 0], + [0, 78553537000000000000000000000000, 0], + [85364825000000000000000000000000, 0, 81322978000000000000000000000000], + [85364825000000000000000000000000, 0, 81322978000000000000000000000000], + [98064272000000000000000000000000, 0, 0], + [98064272000000000000000000000000, 0, 0], + [0, 0, 78616653000000000000000000000000], + [0, 0, 78616653000000000000000000000000], + [99017960000000000000000000000000, 78066788000000000000000000000000, 0], + [99017960000000000000000000000000, 78066788000000000000000000000000, 0], + [83587257000000000000000000000000, 0, 0], + [83587257000000000000000000000000, 0, 0], + [81625375000000000000000000000000, 0, 90569985000000000000000000000000], + [81625375000000000000000000000000, 0, 90569985000000000000000000000000], + [76171853000000000000000000000000, 70811780000000000000000000000000, 0], + [76171853000000000000000000000000, 70811780000000000000000000000000, 0], +] + +doit(sequence) diff --git a/pytest/tests/sanity/staking_repro2.py b/pytest/tests/sanity/staking_repro2.py new file mode 100755 index 000000000..13678adb8 --- /dev/null +++ b/pytest/tests/sanity/staking_repro2.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +# reproduces one of the bugs discovered by `staking2.py` + +from staking2 import doit + +sequence = [ + [0, 89807699575087731503000000000000, 99394381434196724661000000000000], + [0, 78422984975403936749000000000000, 86824329382096781900000000000000], + [96998170601935325415000000000000, 0, 96905754147675144777000000000000], + [0, 90761562581683912450000000000000, 85484232564481713683000000000000], + [0, 88770343317484058757000000000000, 94113873883235846136000000000000], +] + +doit(sequence) diff --git a/pytest/tests/sanity/state_sync.py b/pytest/tests/sanity/state_sync.py new file mode 100755 index 000000000..e77739817 --- /dev/null +++ b/pytest/tests/sanity/state_sync.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +# Spins up a node, then waits for couple epochs +# and spins up another node +# Makes sure that eventually the second node catches up +# Three modes: +# - notx: no transactions are sent, just checks that +# the second node starts and catches up +# - onetx: sends one series of txs at the beginning, +# makes sure the second node balances reflect them +# - manytx: constantly issues txs throughout the test +# makes sure the balances are correct at the end + +import sys, time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +if len(sys.argv) < 3: + logger.info("python state_sync.py [notx, onetx, manytx] ") + exit(1) + +mode = sys.argv[1] +assert mode in ['notx', 'onetx', 'manytx'] + +from cluster import init_cluster, spin_up_node, load_config +from configured_logger import logger +import state_sync_lib +import utils + +START_AT_BLOCK = int(sys.argv[2]) +TIMEOUT = 150 + START_AT_BLOCK * 10 + +config = load_config() + +node_config = state_sync_lib.get_state_sync_config_combined() + +unc_root, node_dirs = init_cluster( + 2, 1, 1, config, + [["min_gas_price", 0], ["max_inflation_rate", [0, 1]], ["epoch_length", 10], + ["block_producer_kickout_threshold", 80]], + {x: node_config for x in range(3)}) + +started = time.time() + +boot_node = spin_up_node(config, unc_root, node_dirs[0], 0) +node1 = spin_up_node(config, unc_root, node_dirs[1], 1, boot_node=boot_node) + +ctx = utils.TxContext([0, 0], [boot_node, node1]) + +sent_txs = False + +observed_height = 0 +for height, block_hash in utils.poll_blocks(boot_node, + timeout=TIMEOUT, + poll_interval=0.1): + observed_height = height + if height >= START_AT_BLOCK: + break + + if mode == 'onetx' and not sent_txs: + ctx.send_moar_txs(block_hash, 3, False) + sent_txs = True + elif mode == 'manytx': + if ctx.get_balances() == ctx.expected_balances: + ctx.send_moar_txs(block_hash, 3, False) + logger.info(f'Sending moar txs at height {height}') + +if mode == 'onetx': + assert ctx.get_balances() == ctx.expected_balances + +node2 = spin_up_node(config, unc_root, node_dirs[2], 2, boot_node=boot_node) +tracker = utils.LogTracker(node2) +time.sleep(3) + +catch_up_height = 0 +for height, block_hash in utils.poll_blocks(boot_node, + timeout=TIMEOUT, + poll_interval=0.1): + catch_up_height = height + if height >= observed_height: + break + if mode == 'manytx' and ctx.get_balances() == ctx.expected_balances: + boot_height = boot_node.get_latest_block().height + ctx.send_moar_txs(block_hash, 3, False) + logger.info(f'Sending moar txs at height {boot_height}') + +boot_heights = boot_node.get_all_heights() + +assert catch_up_height in boot_heights, "%s not in %s" % (catch_up_height, + boot_heights) + +tracker.reset( +) # the transition might have happened before we initialized the tracker +if catch_up_height >= 100: + assert tracker.check("transition to State Sync") +elif catch_up_height <= 30: + assert not tracker.check("transition to State Sync") + +if mode == 'manytx': + while ctx.get_balances() != ctx.expected_balances: + assert time.time() - started < TIMEOUT + logger.info( + "Waiting for the old node to catch up. Current balances: %s; Expected balances: %s" + % (ctx.get_balances(), ctx.expected_balances)) + time.sleep(1) + + # requery the balances from the newly started node + ctx.nodes.append(node2) + ctx.act_to_val = [2, 2, 2] + + while ctx.get_balances() != ctx.expected_balances: + assert time.time() - started < TIMEOUT + logger.info( + "Waiting for the new node to catch up. Current balances: %s; Expected balances: %s" + % (ctx.get_balances(), ctx.expected_balances)) + time.sleep(1) + +logger.info('EPIC') diff --git a/pytest/tests/sanity/state_sync1.py b/pytest/tests/sanity/state_sync1.py new file mode 100755 index 000000000..de3844883 --- /dev/null +++ b/pytest/tests/sanity/state_sync1.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# Spins up two out of three validating nodes. Waits until they reach height 40. +# Start the last validating node and check that the second node can sync up before +# the end of epoch and produce blocks and chunks. + +import sys, time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +import state_sync_lib +import utils + +BLOCK_WAIT = 40 +EPOCH_LENGTH = 80 + +node_config = state_sync_lib.get_state_sync_config_combined() + +nodes = start_cluster( + 4, 0, 1, None, + [["epoch_length", EPOCH_LENGTH], ["block_producer_kickout_threshold", 10], + ["chunk_producer_kickout_threshold", 10]], + {x: node_config for x in range(4)}) +time.sleep(2) +nodes[1].kill() + +logger.info("step 1") +utils.wait_for_blocks(nodes[0], target=BLOCK_WAIT) +nodes[1].start(boot_node=nodes[1]) +time.sleep(2) + +logger.info("step 2") +synced = False +block_height0 = block_height1 = -1 +while block_height0 <= EPOCH_LENGTH and block_height1 <= EPOCH_LENGTH: + block_height0, block_hash0 = nodes[0].get_latest_block() + block_height1, block_hash1 = nodes[1].get_latest_block() + if block_height0 > BLOCK_WAIT: + if block_height0 > block_height1: + try: + nodes[0].get_block(block_hash1) + if synced and abs(block_height0 - block_height1) >= 5: + assert False, "Nodes fall out of sync" + synced = abs(block_height0 - block_height1) < 5 + except Exception: + pass + else: + try: + nodes[1].get_block(block_hash0) + if synced and abs(block_height0 - block_height1) >= 5: + assert False, "Nodes fall out of sync" + synced = abs(block_height0 - block_height1) < 5 + except Exception: + pass + time.sleep(1) + +if not synced: + assert False, "Nodes are not synced" + +validator_info = nodes[0].json_rpc('validators', 'latest') +if len(validator_info['result']['next_validators']) < 2: + assert False, "Node 1 did not produce enough blocks" + +for i in range(2): + account0 = nodes[0].get_account("test%s" % i)['result'] + account1 = nodes[1].get_account("test%s" % i)['result'] + print(account0, account1) + assert account0 == account1, "state diverged" diff --git a/pytest/tests/sanity/state_sync2.py b/pytest/tests/sanity/state_sync2.py new file mode 100755 index 000000000..c8f130d57 --- /dev/null +++ b/pytest/tests/sanity/state_sync2.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# Spins up two block producing nodes. Uses a large number of block producer seats to ensure +# both block producers are validating both shards. +# Gets to 105 blocks and nukes + wipes one of the block producers. Makes sure it can recover +# and sync + +import sys, time +import fcntl +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +import state_sync_lib +import utils + +fcntl.fcntl(1, fcntl.F_SETFL, 0) # no cache when execute from nightly runner + +BLOCKS = 105 # should be enough to trigger state sync for node 1 later, see comments there + +nightly = len(sys.argv) > 1 + +node_config = state_sync_lib.get_state_sync_config_combined() + +nodes = start_cluster( + 2, 0, 2, None, [["minimum_validators_per_shard", 2], ["epoch_length", 10], + ["block_producer_kickout_threshold", 10], + ["chunk_producer_kickout_threshold", 10]], + {x: node_config for x in range(4)}) if nightly else start_cluster( + 2, 0, 2, + None, [["num_block_producer_seats", 199], + ["num_block_producer_seats_per_shard", [99, 100]], + ["epoch_length", 10], ["block_producer_kickout_threshold", 10], + ["chunk_producer_kickout_threshold", 10]], + {x: node_config for x in range(4)}) +logger.info('cluster started') + +started = time.time() + +logger.info(f'Waiting for {BLOCKS} blocks...') +height = utils.wait_for_blocks(nodes[1], target=BLOCKS) +logger.info(f'Got to {height} blocks, rebooting the first node') + +nodes[0].kill() +nodes[0].reset_data() +tracker = utils.LogTracker(nodes[0]) +nodes[0].start(boot_node=nodes[1]) +time.sleep(3) + +utils.wait_for_blocks(nodes[0], target=BLOCKS) + +# make sure `nodes[0]` actually state synced +assert tracker.check("transition to State Sync") diff --git a/pytest/tests/sanity/state_sync3.py b/pytest/tests/sanity/state_sync3.py new file mode 100755 index 000000000..e59d640c9 --- /dev/null +++ b/pytest/tests/sanity/state_sync3.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# Spin up one validating node and make it produce blocks for more than one epoch +# spin up another node that tracks the shard, make sure that it can state sync into the first node + +import sys, time +import pathlib +import tempfile + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +import state_sync_lib +import utils + +EPOCH_LENGTH = 1000 +MAX_SYNC_WAIT = 120 + +(node_config_dump, + node_config_sync) = state_sync_lib.get_state_sync_configs_pair() +node_config_dump["consensus.min_block_production_delay"] = { + "secs": 0, + "nanos": 100000000 +} + +state_parts_dir = str(pathlib.Path(tempfile.gettempdir()) / 'state_parts') + +nodes = start_cluster( + 1, 1, 1, None, + [["epoch_length", EPOCH_LENGTH], ["block_producer_kickout_threshold", 10], + ["chunk_producer_kickout_threshold", 10]], { + 0: node_config_dump, + 1: node_config_sync, + }) +time.sleep(2) +nodes[1].kill() + +logger.info("step 1") + +node0_height, _ = utils.wait_for_blocks(nodes[0], + target=EPOCH_LENGTH * 2 + 1, + poll_interval=5) + +nodes[1].start(boot_node=nodes[1]) +time.sleep(2) + +logger.info("step 2") +state_sync_done_time = None +state_sync_done_height = None +for node1_height, _ in utils.poll_blocks(nodes[1], + timeout=MAX_SYNC_WAIT, + poll_interval=2): + if node1_height > node0_height: + break + if node1_height >= EPOCH_LENGTH: + if state_sync_done_time is None: + state_sync_done_time = time.time() + state_sync_done_height = node1_height + elif time.time() - state_sync_done_time > 8: + assert node1_height > state_sync_done_height, "No progress after state sync is done" diff --git a/pytest/tests/sanity/state_sync4.py b/pytest/tests/sanity/state_sync4.py new file mode 100755 index 000000000..4341516fb --- /dev/null +++ b/pytest/tests/sanity/state_sync4.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# Spin up one node and create some accounts and make them stake +# Spin up another node that syncs from the first node. +# Check that the second node doesn't crash (with trie node missing) +# during state sync. + +import pathlib +import sys +import tempfile +import time + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +from key import Key +from transaction import sign_staking_tx, sign_create_account_with_full_access_key_and_balance_tx +import state_sync_lib +import utils + +MAX_SYNC_WAIT = 30 +EPOCH_LENGTH = 10 + +(node_config_dump, + node_config_sync) = state_sync_lib.get_state_sync_configs_pair() + +nodes = start_cluster( + 1, 1, 1, None, + [["epoch_length", EPOCH_LENGTH], ["block_producer_kickout_threshold", 10], + ["chunk_producer_kickout_threshold", 10]], { + 0: node_config_dump, + 1: node_config_sync, + }) +time.sleep(2) +nodes[1].kill() +logger.info('node1 is killed') + +block_hash = nodes[0].get_latest_block().hash_bytes + +num_new_accounts = 10 +balance = 50000000000000000000000000000000 +account_keys = [] +for i in range(num_new_accounts): + account_name = f'test_account{i}.test0' + signer_key = Key(account_name, nodes[0].signer_key.pk, + nodes[0].signer_key.sk) + create_account_tx = sign_create_account_with_full_access_key_and_balance_tx( + nodes[0].signer_key, account_name, signer_key, + balance // num_new_accounts, i + 1, block_hash) + account_keys.append(signer_key) + res = nodes[0].send_tx_and_wait(create_account_tx, timeout=15) + assert 'error' not in res, res + +latest_block = utils.wait_for_blocks(nodes[0], target=50) +cur_height = latest_block.height +block_hash = latest_block.hash_bytes + +for signer_key in account_keys: + staking_tx = sign_staking_tx(signer_key, nodes[0].validator_key, + balance // (num_new_accounts * 2), + cur_height * 1_000_000 - 1, block_hash) + res = nodes[0].send_tx_and_wait(staking_tx, timeout=15) + assert 'error' not in res + +cur_height, _ = utils.wait_for_blocks(nodes[0], target=80) + +logger.info('restart node1') +nodes[1].start(boot_node=nodes[1]) +logger.info('node1 restarted') +time.sleep(3) + +utils.wait_for_blocks(nodes[1], target=cur_height) diff --git a/pytest/tests/sanity/state_sync5.py b/pytest/tests/sanity/state_sync5.py new file mode 100755 index 000000000..a55547eda --- /dev/null +++ b/pytest/tests/sanity/state_sync5.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# Spin up one validator node and let it run for a while +# Spin up another node that does state sync. Keep sending +# transactions to that node and make sure it doesn't crash. + +import sys, time, base58 +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster, Key +from configured_logger import logger +import state_sync_lib +from transaction import sign_payment_tx +import utils + +MAX_SYNC_WAIT = 30 +EPOCH_LENGTH = 20 + +(node_config_dump, + node_config_sync) = state_sync_lib.get_state_sync_configs_pair() + +nodes = start_cluster( + 1, 1, 1, None, + [["epoch_length", EPOCH_LENGTH], ["block_producer_kickout_threshold", 10], + ["chunk_producer_kickout_threshold", 10]], { + 0: node_config_dump, + 1: node_config_sync, + }) +time.sleep(2) +nodes[1].kill() +logger.info('node1 is killed') + +cur_height, _ = utils.wait_for_blocks(nodes[0], target=60) + +genesis_block = nodes[0].json_rpc('block', [0]) +genesis_hash = genesis_block['result']['header']['hash'] +genesis_hash = base58.b58decode(genesis_hash.encode('ascii')) + +nodes[1].start(boot_node=nodes[1]) +tracker = utils.LogTracker(nodes[1]) +time.sleep(1) + +start_time = time.time() +node1_height = 0 +nonce = 1 +while node1_height <= cur_height: + if time.time() - start_time > MAX_SYNC_WAIT: + assert False, "state sync timed out" + if nonce % 5 == 0: + node1_height = nodes[1].get_latest_block(verbose=True).height + tx = sign_payment_tx(nodes[0].signer_key, 'test1', 1, nonce, genesis_hash) + nodes[1].send_tx(tx) + nonce += 1 + time.sleep(0.05) + +assert tracker.check('transition to State Sync') diff --git a/pytest/tests/sanity/state_sync_epoch_boundary.py b/pytest/tests/sanity/state_sync_epoch_boundary.py new file mode 100755 index 000000000..4138b98ad --- /dev/null +++ b/pytest/tests/sanity/state_sync_epoch_boundary.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 +# Spins up one validating node. +# Spins a non-validating node that tracks some shards and the set of tracked +# shards changes regularly. +# The node gets stopped, and gets restarted close to an epoch boundary but in a +# way to trigger state sync. +# +# This test is a regression test to ensure that the node doesn't panic during +# function execution during block sync after a state sync. + +import pathlib +import random +import sys + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import init_cluster, spin_up_node, load_config, apply_config_changes +import account +import state_sync_lib +import transaction +import utils + +from configured_logger import logger + +EPOCH_LENGTH = 30 + +(node_config_dump, + node_config_sync) = state_sync_lib.get_state_sync_configs_pair() +node_config_sync["tracked_shards"] = [] +node_config_sync["tracked_shard_schedule"] = [[0], [0], [1], [1]] + +config = load_config() +unc_root, node_dirs = init_cluster(1, 1, 2, config, + [["epoch_length", EPOCH_LENGTH]], { + 0: node_config_dump, + 1: node_config_sync + }) + +boot_node = spin_up_node(config, unc_root, node_dirs[0], 0) +logger.info('started boot_node') +node1 = spin_up_node(config, unc_root, node_dirs[1], 1, boot_node=boot_node) +# State sync makes the storage look inconsistent. +node1.stop_checking_store() +logger.info('started node1') + +contract = utils.load_test_contract() + +latest_block_hash = boot_node.get_latest_block().hash_bytes +deploy_contract_tx = transaction.sign_deploy_contract_tx( + boot_node.signer_key, contract, 10, latest_block_hash) +result = boot_node.send_tx_and_wait(deploy_contract_tx, 10) +assert 'result' in result and 'error' not in result, ( + 'Expected "result" and no "error" in response, got: {}'.format(result)) + +latest_block_hash = boot_node.get_latest_block().hash_bytes +deploy_contract_tx = transaction.sign_deploy_contract_tx( + node1.signer_key, contract, 10, latest_block_hash) +result = boot_node.send_tx_and_wait(deploy_contract_tx, 10) +assert 'result' in result and 'error' not in result, ( + 'Expected "result" and no "error" in response, got: {}'.format(result)) + + +# Generates traffic for all possible shards. +# Assumes that `test0`, `test1`, `near` all belong to different shards. +def random_workload_until(target, nonce, keys, target_node): + last_height = -1 + while True: + nonce += 1 + + last_block = target_node.get_latest_block() + height = last_block.height + if height > target: + break + if height != last_height: + logger.info( + f'@{height}, epoch_height: {state_sync_lib.approximate_epoch_height(height, EPOCH_LENGTH)}' + ) + last_height = height + + last_block_hash = boot_node.get_latest_block().hash_bytes + if (len(keys) > 100 and random.random() < 0.2) or len(keys) > 1000: + key = keys[random.randint(0, len(keys) - 1)] + call_function('read', key, nonce, boot_node.signer_key, + last_block_hash) + call_function('read', key, nonce, node1.signer_key, last_block_hash) + elif random.random() < 0.5: + if random.random() < 0.3: + key_from, account_to = boot_node.signer_key, node1.signer_key.account_id + elif random.random() < 0.3: + key_from, account_to = boot_node.signer_key, "near" + elif random.random() < 0.5: + key_from, account_to = node1.signer_key, boot_node.signer_key.account_id + else: + key_from, account_to = node1.signer_key, "near" + payment_tx = transaction.sign_payment_tx(key_from, account_to, 1, + nonce, last_block_hash) + boot_node.send_tx(payment_tx).get('result') + else: + key = random_u64() + keys.append(key) + call_function('write', key, nonce, boot_node.signer_key, + last_block_hash) + call_function('write', key, nonce, node1.signer_key, + last_block_hash) + return (nonce, keys) + + +def random_u64(): + return bytes(random.randint(0, 255) for _ in range(8)) + + +def call_function(op, key, nonce, signer_key, last_block_hash): + if op == 'read': + args = key + fn = 'read_value' + else: + args = key + random_u64() + fn = 'write_key_value' + + tx = transaction.sign_function_call_tx(signer_key, signer_key.account_id, + fn, args, 300 * account.TGAS, 0, + nonce, last_block_hash) + return boot_node.send_tx(tx).get('result') + + +nonce, keys = random_workload_until(EPOCH_LENGTH - 5, 1, [], boot_node) + +node1_height = node1.get_latest_block().height +logger.info(f'node1@{node1_height}') +node1.kill() +logger.info(f'killed node1') + +# Run node0 more to trigger block sync in node1. +nonce, keys = random_workload_until(int(EPOCH_LENGTH * 2.7), nonce, keys, + boot_node) + +# Node1 is now behind and needs to do header sync and block sync. +node1.start(boot_node=boot_node) +node1_height = node1.get_latest_block().height +logger.info(f'started node1@{node1_height}') + +nonce, keys = random_workload_until(int(EPOCH_LENGTH * 3.9), nonce, keys, + boot_node) +boot_node_height = boot_node.get_latest_block().height +node1_height = node1.get_latest_block().height +assert node1_height + int(EPOCH_LENGTH * 0.5) >= boot_node_height diff --git a/pytest/tests/sanity/state_sync_fail.py b/pytest/tests/sanity/state_sync_fail.py new file mode 100755 index 000000000..a3d508d98 --- /dev/null +++ b/pytest/tests/sanity/state_sync_fail.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 + +# Spins up 2 nodes, waits until sharding is upgraded and spins up another node. +# Check that the node can't be started because it cannot state sync to the epoch +# after the sharding upgrade. + +# Depending on the version of the binary (default or nightly) it will perform +# resharding from V0 (1 shard) to V1 (4 shards) or from V1 (4 shards) to V2 (5 +# shards). + +import pathlib +import sys +import time + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import init_cluster, spin_up_node, load_config, get_binary_protocol_version +from configured_logger import logger +import requests +import resharding_lib +import state_sync_lib +import utils + +EPOCH_LENGTH = 20 +START_AT_BLOCK = int(EPOCH_LENGTH * 2.5) + + +def get_genesis_config_changes(binary_protocol_version): + genesis_config_changes = [ + ["min_gas_price", 0], + ["max_inflation_rate", [0, 1]], + ["epoch_length", EPOCH_LENGTH], + ["block_producer_kickout_threshold", 80], + ] + + resharding_lib.append_shard_layout_config_changes( + genesis_config_changes, + binary_protocol_version, + logger, + ) + + return genesis_config_changes + + +config = load_config() + +binary_protocol_version = get_binary_protocol_version(config) +assert binary_protocol_version is not None + +node_config = state_sync_lib.get_state_sync_config_combined() + +unc_root, node_dirs = init_cluster( + num_nodes=2, + num_observers=1, + num_shards=4, + config=config, + genesis_config_changes=get_genesis_config_changes(binary_protocol_version), + client_config_changes={x: node_config for x in range(3)}, +) + +started = time.time() + +boot_node = spin_up_node(config, unc_root, node_dirs[0], 0) +node1 = spin_up_node(config, unc_root, node_dirs[1], 1, boot_node=boot_node) + +utils.wait_for_blocks(boot_node, target=START_AT_BLOCK) + +node2 = spin_up_node(config, unc_root, node_dirs[2], 2, boot_node=boot_node) +time.sleep(3) + +try: + logger.info("Checking node2 status. It should not be running.") + status = node2.get_status() + sys.exit("node 2 successfully started while it should fail") +except requests.exceptions.ConnectionError: + pass + +logger.info("Checking node2 exit reason.") +node2_correct_exit_reason = False +node2_stderr_path = pathlib.Path(node2.node_dir) / 'stderr' +with open(node2_stderr_path) as stderr_file: + for line in stderr_file: + if "cannot sync to the first epoch after sharding upgrade" in line: + logger.info("Found the correct exit reason in node2 stderr.") + node2_correct_exit_reason = True + break + +assert node2_correct_exit_reason + +logger.info("Test finished.") diff --git a/pytest/tests/sanity/state_sync_late.py b/pytest/tests/sanity/state_sync_late.py new file mode 100755 index 000000000..d46a10cf8 --- /dev/null +++ b/pytest/tests/sanity/state_sync_late.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +# Spins up a node, then waits for five+ epochs +# and spins up another node +# Makes sure that eventually the second node catches up +# Three modes: +# - notx: no transactions are sent, just checks that +# the second node starts and catches up +# - onetx: sends one series of txs at the beginning, +# makes sure the second node balances reflect them +# - manytx: constantly issues txs throughout the test +# makes sure the balances are correct at the end + +import pathlib +import sys +import time + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +if len(sys.argv) < 2: + logger.info("python state_sync.py [notx, onetx, manytx]") + exit(1) + +mode = sys.argv[1] +assert mode in ['notx', 'onetx', 'manytx'] + +from cluster import init_cluster, spin_up_node, load_config +from configured_logger import logger +import state_sync_lib +import utils + +START_AT_BLOCK = 75 +TIMEOUT = 150 + START_AT_BLOCK * 10 + +config = load_config() +node_config = state_sync_lib.get_state_sync_config_combined() + +unc_root, node_dirs = init_cluster( + 2, 1, 1, config, + [["min_gas_price", 0], ["max_inflation_rate", [0, 1]], ["epoch_length", 10], + ["block_producer_kickout_threshold", 80]], + {x: node_config for x in range(3)}) + +started = time.time() + +boot_node = spin_up_node(config, unc_root, node_dirs[0], 0) +node1 = spin_up_node(config, unc_root, node_dirs[1], 1, boot_node=boot_node) + +ctx = utils.TxContext([0, 0], [boot_node, node1]) + +sent_txs = False + +observed_height = 0 +for observed_height, hash_ in utils.poll_blocks(boot_node, + timeout=TIMEOUT, + poll_interval=0.1): + if mode == 'onetx' and not sent_txs: + ctx.send_moar_txs(hash_, 3, False) + sent_txs = True + elif mode == 'manytx': + if ctx.get_balances() == ctx.expected_balances: + logger.info(f'Sending moar txs at height {observed_height}') + ctx.send_moar_txs(hash_, 3, False) + +if mode == 'onetx': + assert ctx.get_balances() == ctx.expected_balances + +node2 = spin_up_node(config, unc_root, node_dirs[2], 2, boot_node=boot_node) +tracker = utils.LogTracker(node2) +time.sleep(3) + +catch_up_height = 0 +for catch_up_height, hash_ in utils.poll_blocks(node2, + timeout=TIMEOUT, + poll_interval=0.1): + if catch_up_height >= observed_height: + break + + boot_height = boot_node.get_latest_block().height + if mode == 'manytx': + if ctx.get_balances() == ctx.expected_balances: + ctx.send_moar_txs(hash_, 3, False) + logger.info(f'Sending moar txs at height {boot_height}') + +boot_heights = boot_node.get_all_heights() + +assert catch_up_height in boot_heights, "%s not in %s" % (catch_up_height, + boot_heights) + +tracker.offset = 0 # the transition might have happened before we initialized the tracker +if catch_up_height >= 100: + assert tracker.check("transition to State Sync") +elif catch_up_height <= 30: + assert not tracker.check("transition to State Sync") + +if mode == 'manytx': + while ctx.get_balances() != ctx.expected_balances: + assert time.time() - started < TIMEOUT + logger.info( + "Waiting for the old node to catch up. Current balances: %s; Expected balances: %s" + % (ctx.get_balances(), ctx.expected_balances)) + time.sleep(1) + + # requery the balances from the newly started node + ctx.nodes.append(node2) + ctx.act_to_val = [2, 2, 2] + + while ctx.get_balances() != ctx.expected_balances: + assert time.time() - started < TIMEOUT + logger.info( + "Waiting for the new node to catch up. Current balances: %s; Expected balances: %s" + % (ctx.get_balances(), ctx.expected_balances)) + time.sleep(1) diff --git a/pytest/tests/sanity/state_sync_massive.py b/pytest/tests/sanity/state_sync_massive.py new file mode 100755 index 000000000..f2e7dfb55 --- /dev/null +++ b/pytest/tests/sanity/state_sync_massive.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +# Survive massive state sync +# +# Create 3 nodes, 1 validator and 2 observers tracking the single shard 0. +# Generate a large state using genesis-populate. [*] +# +# Spawn validator and first observer and wait for them to make some progress. +# Spawn second observer and watch how it is able to sync state +# without degrading blocks per second. +# +# To run this test is important to compile genesis-populate tool first. +# In framework folder run: +# +# ``` +# cargo build -p genesis-populate +# ``` +# +# [*] This test might take a very large time generating the state. +# To speed up this between multiple executions, large state can be generated once +# saved, and reused on multiple executions. Steps to do this. +# +# 1. Run test for first time: +# +# ``` +# python3 tests/sanity/state_sync_massive.py +# ``` +# +# Stop at any point after seeing the message: "Genesis generated" +# +# 2. Save generated data: +# +# ``` +# cp -r ~/.near/test0_finished ~/.near/backup_genesis +# ``` +# +# 3. Run test passing path to backup_genesis +# +# ``` +# python3 tests/sanity/state_sync_massive.py ~/.near/backup_genesis +# ``` +# + +import logging +import pathlib +import requests +from subprocess import check_output +import sys +import time + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import init_cluster, spin_up_node, load_config +from populate import genesis_populate_all, copy_genesis +import state_sync_lib +from utils import LogTracker + +logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG) + +if len(sys.argv) >= 2: + genesis_data = sys.argv[1] +else: + genesis_data = None + additional_accounts = 200000 + +config = load_config() +node_config = state_sync_lib.get_state_sync_config_combined() +unc_root, node_dirs = init_cluster( + 1, 2, 1, + config, [["min_gas_price", 0], ["max_inflation_rate", [0, 1]], + ["epoch_length", 300], ["block_producer_kickout_threshold", 80]], + {x: node_config for x in range(3)}) + +logging.info("Populating genesis") + +if genesis_data is None: + genesis_populate_all(unc_root, additional_accounts, node_dirs) +else: + for node_dir in node_dirs: + copy_genesis(genesis_data, node_dir) + +logging.info("Genesis generated") + +for node_dir in node_dirs: + result = check_output(['ls', '-la', node_dir]).decode() + logging.info(f'Node directory: {node_dir}') + for line in result.split('\n'): + logging.info(line) + +SMALL_HEIGHT = 600 +LARGE_HEIGHT = 660 +TIMEOUT = 3600 +start = time.time() + +boot_node = spin_up_node(config, unc_root, node_dirs[0], 0) +observer = spin_up_node(config, unc_root, node_dirs[1], 1, boot_node=boot_node) + + +def wait_for_height(target_height, rpc_node, sleep_time=2, bps_threshold=-1): + queue = [] + latest_height = 0 + + while latest_height < target_height: + assert time.time() - start < TIMEOUT + + # Check current height + try: + new_height = rpc_node.get_latest_block(check_storage=False).height + logging.info(f"Height: {latest_height} => {new_height}") + latest_height = new_height + except requests.ReadTimeout: + logging.info("Timeout Error") + + # Computing bps + cur_time = time.time() + queue.append((cur_time, latest_height)) + + while len(queue) > 2 and queue[0][0] <= cur_time - 7: + queue.pop(0) + + if len(queue) <= 1: + bps = None + else: + head = queue[-1] + tail = queue[0] + bps = (head[1] - tail[1]) / (head[0] - tail[0]) + + logging.info(f"bps: {bps} queue length: {len(queue)}") + time.sleep(sleep_time) + assert bps is None or bps >= bps_threshold + + +wait_for_height(SMALL_HEIGHT, boot_node) + +observer = spin_up_node(config, unc_root, node_dirs[2], 2, boot_node=boot_node) +tracker = LogTracker(observer) + +# Check that bps is not degraded +wait_for_height(LARGE_HEIGHT, boot_node) + +# Make sure observer2 is able to sync +wait_for_height(SMALL_HEIGHT, observer) + +tracker.reset() +assert tracker.check("transition to State Sync") diff --git a/pytest/tests/sanity/state_sync_massive_validator.py b/pytest/tests/sanity/state_sync_massive_validator.py new file mode 100755 index 000000000..0ea82377c --- /dev/null +++ b/pytest/tests/sanity/state_sync_massive_validator.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +""" +Survive massive state sync for validator + +Create 4 nodes, 3 validators and 1 observers tracking the single shard 0. +Generate a large state using genesis-populate. [*] + +Spawn everything and wait for them to make some progress. +Kill one of the validators, delete state and rerun it + +To run this test is important to compile genesis-populate tool first. +In framework folder run: + +``` +cargo build -p genesis-populate +``` + +[*] This test might take a very large time generating the state. +To speed up this between multiple executions, large state can be generated once +saved, and reused on multiple executions. Steps to do this. + +1. Run test for first time: + +``` +python3 tests/sanity/state_sync_massive_validator.py +``` + +Stop at any point after seeing the message: "Genesis generated" + +2. Save generated data: + +``` +cp -r ~/.near/test0_finished ~/.near/backup_genesis +``` + +3. Run test passing path to backup_genesis + +``` +python3 tests/sanity/state_sync_massive_validator.py ~/.near/backup_genesis +``` +""" + +from subprocess import check_output +import logging +import pathlib +import requests +import sys +import time + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import init_cluster, spin_up_node, load_config +from populate import genesis_populate_all, copy_genesis +import state_sync_lib + +logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG) + +if len(sys.argv) >= 2: + genesis_data = sys.argv[1] +else: + genesis_data = None + additional_accounts = 200000 + +EPOCH_LENGTH = 300 + +config = load_config() +node_config = state_sync_lib.get_state_sync_config_combined() +unc_root, node_dirs = init_cluster( + 3, 1, 1, config, + [["min_gas_price", 0], ["max_inflation_rate", [0, 1]], + ["epoch_length", EPOCH_LENGTH], ["block_producer_kickout_threshold", 0], + ["chunk_producer_kickout_threshold", 0]], + {x: node_config for x in range(4)}) + +logging.info("Populating genesis") + +if genesis_data is None: + genesis_populate_all(unc_root, additional_accounts, node_dirs) +else: + for node_dir in node_dirs: + copy_genesis(genesis_data, node_dir) + +logging.info("Genesis generated") + +for node_dir in node_dirs: + result = check_output(['ls', '-la', node_dir], text=True) + logging.info(f'Node directory: {node_dir}') + for line in result.split('\n'): + logging.info(line) + +INTERMEDIATE_HEIGHT = EPOCH_LENGTH + 10 +SMALL_HEIGHT = EPOCH_LENGTH * 2 + 10 +LARGE_HEIGHT = SMALL_HEIGHT + 50 +TIMEOUT = 3600 +start = time.time() + +boot_node = spin_up_node(config, unc_root, node_dirs[0], 0) +validator = spin_up_node(config, + unc_root, + node_dirs[1], + 1, + boot_node=boot_node) +delayed_validator = spin_up_node(config, + unc_root, + node_dirs[2], + 2, + boot_node=boot_node) +observer = spin_up_node(config, unc_root, node_dirs[3], 3, boot_node=boot_node) + + +def wait_for_height(target_height, rpc_node, sleep_time=2, bps_threshold=-1): + queue = [] + latest_height = 0 + + while latest_height < target_height: + assert time.time() - start < TIMEOUT + + # Check current height + try: + new_height = rpc_node.get_latest_block(check_storage=False, + timeout=10).height + logging.info(f"Height: {latest_height} => {new_height}") + latest_height = new_height + except requests.ReadTimeout: + logging.info("Timeout Error") + + # Computing bps + cur_time = time.time() + queue.append((cur_time, latest_height)) + + while len(queue) > 2 and queue[0][0] <= cur_time - 7: + queue.pop(0) + + if len(queue) <= 1: + bps = None + else: + head = queue[-1] + tail = queue[0] + bps = (head[1] - tail[1]) / (head[0] - tail[0]) + + logging.info(f"bps: {bps} queue length: {len(queue)}") + time.sleep(sleep_time) + assert bps is None or bps >= bps_threshold + + +wait_for_height(INTERMEDIATE_HEIGHT, validator) + +delayed_validator.kill() +delayed_validator.reset_data() +delayed_validator.start(boot_node=boot_node) + +# Check that bps is not degraded +wait_for_height(LARGE_HEIGHT, validator) + +wait_for_height(SMALL_HEIGHT, delayed_validator) diff --git a/pytest/tests/sanity/state_sync_routed.py b/pytest/tests/sanity/state_sync_routed.py new file mode 100755 index 000000000..6aaff605f --- /dev/null +++ b/pytest/tests/sanity/state_sync_routed.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +# Spins two block producers and two observers. +# Wait several epochs and spin up another observer that +# is blacklisted by both block producers. +# +# Make sure the new observer sync via routing through other observers. +# +# Three modes: +# - notx: no transactions are sent, just checks that +# the second node starts and catches up +# - onetx: sends one series of txs at the beginning, +# makes sure the second node balances reflect them +# - manytx: constantly issues txs throughout the test +# makes sure the balances are correct at the end + +import pathlib +import sys +import time + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) +from configured_logger import logger +from cluster import init_cluster, spin_up_node, load_config +import state_sync_lib +import utils + +if len(sys.argv) < 3: + logger.info("python state_sync.py [notx, onetx, manytx] ") + exit(1) + +mode = sys.argv[1] +assert mode in ['notx', 'onetx', 'manytx'] + +START_AT_BLOCK = int(sys.argv[2]) +TIMEOUT = 150 + START_AT_BLOCK * 10 + +config = load_config() +node_config = state_sync_lib.get_state_sync_config_combined() + +unc_root, node_dirs = init_cluster( + 2, 3, 1, config, + [["min_gas_price", 0], ["max_inflation_rate", [0, 1]], ["epoch_length", 10], + ["block_producer_kickout_threshold", 80]], + {x: node_config for x in range(5)}) + +started = time.time() + +# First observer +node2 = spin_up_node(config, unc_root, node_dirs[2], 2) +# Boot from observer since block producer will blacklist third observer +boot_node = node2 + +# Second observer +node3 = spin_up_node(config, unc_root, node_dirs[3], 3, boot_node=boot_node) + +# Spin up validators +node0 = spin_up_node(config, + unc_root, + node_dirs[0], + 0, + boot_node=boot_node, + blacklist=[4]) +node1 = spin_up_node(config, + unc_root, + node_dirs[1], + 1, + boot_node=boot_node, + blacklist=[4]) + +ctx = utils.TxContext([0, 0], [node0, node1]) + +sent_txs = False + +observed_height = 0 +for observed_height, hash_ in utils.poll_blocks(boot_node, + timeout=TIMEOUT, + poll_interval=0.1): + if observed_height >= START_AT_BLOCK: + break + if mode == 'onetx' and not sent_txs: + ctx.send_moar_txs(hash_, 3, False) + sent_txs = True + elif mode == 'manytx' and ctx.get_balances() == ctx.expected_balances: + ctx.send_moar_txs(hash_, 3, False) + logger.info(f'Sending moar txs at height {observed_height}') + +if mode == 'onetx': + assert ctx.get_balances() == ctx.expected_balances + +node4 = spin_up_node(config, + unc_root, + node_dirs[4], + 4, + boot_node=boot_node, + blacklist=[0, 1]) + +metrics4 = utils.MetricsTracker(node4) +time.sleep(3) + +catch_up_height = 0 +for catch_up_height, hash_ in utils.poll_blocks(node4, + timeout=TIMEOUT, + poll_interval=0.1): + if catch_up_height >= observed_height: + break + assert time.time() - started < TIMEOUT, "Waiting for node 4 to catch up" + new_height = node4.get_latest_block().height + logger.info(f"Latest block at: {new_height}") + if new_height > catch_up_height: + catch_up_height = new_height + logger.info(f"Last observer got to height {new_height}") + + boot_height = boot_node.get_latest_block().height + + if mode == 'manytx': + if ctx.get_balances() == ctx.expected_balances: + ctx.send_moar_txs(hash_, 3, False) + logger.info(f"Sending moar txs at height {boot_height}") + time.sleep(0.1) + +boot_heights = boot_node.get_all_heights() + +assert catch_up_height in boot_heights, "%s not in %s" % (catch_up_height, + boot_heights) + +while True: + assert time.time( + ) - started < TIMEOUT, "Waiting for node 4 to connect to two peers" + if metrics4.get_int_metric_value("unc_peer_connections_total") == 2: + break + time.sleep(0.1) + +if mode == 'manytx': + while ctx.get_balances() != ctx.expected_balances: + assert time.time() - started < TIMEOUT + logger.info( + "Waiting for the old node to catch up. Current balances: %s; Expected balances: %s" + % (ctx.get_balances(), ctx.expected_balances)) + time.sleep(1) + + # requery the balances from the newly started node + ctx.nodes.append(node4) + ctx.act_to_val = [2, 2, 2] + + while ctx.get_balances() != ctx.expected_balances: + assert time.time() - started < TIMEOUT + logger.info( + "Waiting for the new node to catch up. Current balances: %s; Expected balances: %s" + % (ctx.get_balances(), ctx.expected_balances)) + time.sleep(1) diff --git a/pytest/tests/sanity/state_sync_then_catchup.py b/pytest/tests/sanity/state_sync_then_catchup.py new file mode 100644 index 000000000..66f83cc48 --- /dev/null +++ b/pytest/tests/sanity/state_sync_then_catchup.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +# Spins up one validating node. +# Spins a non-validating node that tracks some shards and the set of tracked shards changes regularly. +# The node gets stopped, and gets restarted close to an epoch boundary but in a way to trigger state sync. +# +# After the state sync the node has to do a catchup. +# +# Note that the test must generate outgoing receipts for most shards almost +# every block in order to crash if creation of partial encoded chunks becomes +# non-deterministic. + +import pathlib +import random +import sys + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import init_cluster, spin_up_node, load_config, apply_config_changes +import account +import state_sync_lib +import transaction +import utils + +from configured_logger import logger + +EPOCH_LENGTH = 50 + + +def random_u64(): + return bytes(random.randint(0, 255) for _ in range(8)) + + +# Generates traffic for all possible shards. +# Assumes that `test0`, `test1`, `near` all belong to different shards. +def random_workload_until(target, nonce, keys, node0, node1, target_node): + last_height = -1 + while True: + nonce += 1 + + last_block = target_node.get_latest_block() + height = last_block.height + if height > target: + break + if height != last_height: + logger.info( + f'@{height}, epoch_height: {state_sync_lib.approximate_epoch_height(height, EPOCH_LENGTH)}' + ) + last_height = height + + last_block_hash = node0.get_latest_block().hash_bytes + if random.random() < 0.5: + # Make a transfer between shards. + # The goal is to generate cross-shard receipts. + key_from = random.choice([node0, node1]).signer_key + account_to = random.choice([ + node0.signer_key.account_id, node1.signer_key.account_id, "near" + ]) + payment_tx = transaction.sign_payment_tx(key_from, account_to, 1, + nonce, last_block_hash) + node0.send_tx(payment_tx).get('result') + elif (len(keys) > 100 and random.random() < 0.5) or len(keys) > 1000: + # Do some flat storage reads, but only if we have enough keys populated. + key = keys[random.randint(0, len(keys) - 1)] + for node in [node0, node1]: + call_function('read', key, nonce, node.signer_key, + last_block_hash, node0) + call_function('read', key, nonce, node.signer_key, + last_block_hash, node0) + else: + # Generate some data for flat storage reads + key = random_u64() + keys.append(key) + for node in [node0, node1]: + call_function('write', key, nonce, node.signer_key, + last_block_hash, node0) + return nonce, keys + + +def call_function(op, key, nonce, signer_key, last_block_hash, node): + if op == 'read': + args = key + fn = 'read_value' + else: + args = key + random_u64() + fn = 'write_key_value' + + tx = transaction.sign_function_call_tx(signer_key, signer_key.account_id, + fn, args, 300 * account.TGAS, 0, + nonce, last_block_hash) + return node.send_tx(tx).get('result') + + +def main(): + node_config_dump, node_config_sync = state_sync_lib.get_state_sync_configs_pair( + ) + node_config_sync["tracked_shard_schedule"] = [[0], [0], [1], [2], [3], [1], + [2], [3]] + node_config_sync["tracked_shards"] = [] + + config = load_config() + unc_root, node_dirs = init_cluster(1, 1, 4, config, + [["epoch_length", EPOCH_LENGTH]], { + 0: node_config_dump, + 1: node_config_sync + }) + + boot_node = spin_up_node(config, unc_root, node_dirs[0], 0) + logger.info('started boot_node') + node1 = spin_up_node(config, + unc_root, + node_dirs[1], + 1, + boot_node=boot_node) + logger.info('started node1') + # State sync makes the storage look inconsistent. + node1.stop_checking_store() + + contract = utils.load_test_contract() + + latest_block_hash = boot_node.get_latest_block().hash_bytes + deploy_contract_tx = transaction.sign_deploy_contract_tx( + boot_node.signer_key, contract, 10, latest_block_hash) + result = boot_node.send_tx_and_wait(deploy_contract_tx, 10) + assert 'result' in result and 'error' not in result, ( + 'Expected "result" and no "error" in response, got: {}'.format(result)) + + latest_block_hash = boot_node.get_latest_block().hash_bytes + deploy_contract_tx = transaction.sign_deploy_contract_tx( + node1.signer_key, contract, 10, latest_block_hash) + result = boot_node.send_tx_and_wait(deploy_contract_tx, 10) + assert 'result' in result and 'error' not in result, ( + 'Expected "result" and no "error" in response, got: {}'.format(result)) + + nonce, keys = random_workload_until(EPOCH_LENGTH - 5, 1, [], boot_node, + node1, boot_node) + + node1_height = node1.get_latest_block().height + logger.info(f'node1@{node1_height}') + node1.kill() + logger.info(f'killed node1') + + # Run node0 more to trigger block sync in node1. + nonce, keys = random_workload_until(int(EPOCH_LENGTH * 3), nonce, keys, + boot_node, node1, boot_node) + + # Node1 is now behind and needs to do header sync and block sync. + node1.start(boot_node=boot_node) + node1_height = node1.get_latest_block().height + logger.info(f'started node1@{node1_height}') + + nonce, keys = random_workload_until(int(EPOCH_LENGTH * 3.7), nonce, keys, + boot_node, node1, node1) + + +if __name__ == "__main__": + main() diff --git a/pytest/tests/sanity/switch_node_key.py b/pytest/tests/sanity/switch_node_key.py new file mode 100755 index 000000000..ca77786ea --- /dev/null +++ b/pytest/tests/sanity/switch_node_key.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# Spin up two validating nodes. Stop one of them after one epoch, switch node key (peer id), and restart. +# Make sure that both node can still produce blocks. + +import sys, time, base58, nacl.bindings +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from key import Key +import utils + +EPOCH_LENGTH = 40 +STOP_HEIGHT1 = 35 +TIMEOUT = 50 + +config1 = {"network": {"ttl_account_id_router": {"secs": 1, "nanos": 0},}} +nodes = start_cluster( + 2, 0, 1, None, + [["epoch_length", EPOCH_LENGTH], ["block_producer_kickout_threshold", 30], + ["chunk_producer_kickout_threshold", 30], ["num_block_producer_seats", 4], + ["num_block_producer_seats_per_shard", [4]], + ["validators", 0, "amount", "150000000000000000000000000000000"], + [ + "records", 0, "Account", "account", "locked", + "150000000000000000000000000000000" + ], ["total_supply", "3100000000000000000000000000000000"]], {1: config1}) +time.sleep(2) + +block = nodes[1].get_block(nodes[1].get_latest_block().height) +epoch_id = block['result']['header']['epoch_id'] + +utils.wait_for_blocks(nodes[1], target=STOP_HEIGHT1) + +nodes[1].kill() +for height, _ in utils.poll_blocks(nodes[0], timeout=TIMEOUT): + cur_block = nodes[0].get_block(height) + if cur_block['result']['header']['epoch_id'] != epoch_id: + break + +seed = bytes([1] * 32) +public_key, secret_key = nacl.bindings.crypto_sign_seed_keypair(seed) +node_key = Key("", + base58.b58encode(public_key).decode('utf-8'), + base58.b58encode(secret_key).decode('utf-8')) +nodes[1].reset_node_key(node_key) +nodes[1].start(boot_node=nodes[0]) +time.sleep(2) + +utils.wait_for_blocks(nodes[1], target=EPOCH_LENGTH * 2 + 5) + +validators = nodes[1].get_validators() +assert len( + validators['result']['next_validators'] +) == 2, f'unexpected number of validators, next validators: {validators["result"]["next_validators"]}' diff --git a/pytest/tests/sanity/sync_ban.py b/pytest/tests/sanity/sync_ban.py new file mode 100755 index 000000000..f68224707 --- /dev/null +++ b/pytest/tests/sanity/sync_ban.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +# Spin up a validator node and a nonvalidator node. +# Stop the nonvalidator node and wait until the validator node reach height 100 +# sync the nonvalidator node with controlled message passing between nodes. +# If `should_ban` is true, this should cause the nonvalidator node to ban the validator node +# due to not receiving any headers. +# If `should_ban` is false, the nonvalidator node should be able to sync without banning the +# validator node despite the slow connection. + +import sys, time, functools, asyncio +import pathlib +from multiprocessing import Value + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +from peer import * +from proxy import ProxyHandler +import utils + +TIMEOUT = 300 +EPOCH_LENGTH = 50 + +BAN_STRING = 'ban a peer: [^\n]*, for not providing enough headers' + + +class Handler(ProxyHandler): + + def __init__(self, *args, should_sync=None, should_ban=None, **kwargs): + assert should_sync is not None + self.should_sync = should_sync + assert should_ban is not None + self.should_ban = should_ban + super().__init__(*args, **kwargs) + + async def handle(self, msg, fr, to): + if msg is not None: + if msg.enum == 'Block': + loop = asyncio.get_running_loop() + send = functools.partial(self.do_send_message, msg, 1) + if self.should_sync.value: + loop.call_later(1, send) + return False + elif msg.enum == 'BlockRequest': + loop = asyncio.get_running_loop() + send = functools.partial(self.do_send_message, msg, 0) + if self.should_sync.value: + loop.call_later(6, send) + return False + elif msg.enum == 'BlockHeaders': + if self.should_ban: + return False + loop = asyncio.get_running_loop() + send = functools.partial(self.do_send_message, msg, 1) + if self.should_sync.value: + loop.call_later(2, send) + return False + return True + + +if __name__ == '__main__': + should_ban = sys.argv[1] == 'true' + node0_config = { + "consensus": { + "min_block_production_delay": { + "secs": 0, + "nanos": 1000000000 + }, + }, + } + node1_config = { + "consensus": { + "header_sync_initial_timeout": { + "secs": 3, + "nanos": 0 + }, + "header_sync_stall_ban_timeout": { + "secs": 5, + "nanos": 0 + } + }, + "tracked_shards": [0] + } + + should_sync = Value('i', False) + nodes = start_cluster( + 1, 1, 1, None, [["epoch_length", EPOCH_LENGTH]], { + 0: node0_config, + 1: node1_config + }, + functools.partial(Handler, + should_sync=should_sync, + should_ban=should_ban)) + + utils.wait_for_blocks(nodes[0], target=30, poll_interval=2) + + should_sync.value = True + + logger.info("sync node 1") + + start = time.time() + + tracker0 = utils.LogTracker(nodes[0]) + tracker1 = utils.LogTracker(nodes[1]) + + while True: + assert time.time() - start < TIMEOUT + + if should_ban: + if tracker1.check_re(BAN_STRING): + break + else: + cur_height = nodes[0].get_latest_block().height + node1_height = nodes[1].get_latest_block().height + status1 = nodes[1].get_status() + print( + f"Sync: node 1 at block {node1_height}, node 0 at block {cur_height}; waiting for node 1 to catch up" + ) + if (abs(node1_height - cur_height) < 5 and + status1['sync_info']['syncing'] is False): + break + time.sleep(2) + + if not should_ban and (tracker0.check_re(BAN_STRING) or + tracker1.check_re(BAN_STRING)): + assert False, "unexpected ban of peers" + + # logger.info('shutting down') + # time.sleep(10) diff --git a/pytest/tests/sanity/sync_chunks_from_archival.py b/pytest/tests/sanity/sync_chunks_from_archival.py new file mode 100755 index 000000000..9ff828b22 --- /dev/null +++ b/pytest/tests/sanity/sync_chunks_from_archival.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python3 +# Spins up two nodes; Let's them build the chain for several epochs; +# Spins up two more nodes, and makes the two new nodes to stake, and the old two to unstake; +# Makes the two new nodes to build for couple more epochs; +# Spins up one more node. Makes sure it can sync. +# For the last node to be able to sync, it needs to learn to download chunks from the other +# archival nodes, as opposed to the validators of the corresponding epoch, since the validators +# of the old epochs are long gone by the time the archival node is syncing. + +import sys, time, logging, base58 +import multiprocessing +from functools import partial +import pathlib + +from requests.api import request + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import init_cluster, spin_up_node, load_config +from configured_logger import logger +from messages.block import ShardChunkHeaderV1, ShardChunkHeaderV2, ShardChunkHeaderV3 +from transaction import sign_staking_tx +from proxy import ProxyHandler, NodesProxy +import utils + +TIMEOUT = 200 +EPOCH_LENGTH = 10 +HEIGHTS_BEFORE_ROTATE = 35 +HEIGHTS_BEFORE_CHECK = 25 + + +class Handler(ProxyHandler): + + def __init__(self, + *args, + hash_to_metadata={}, + requests={}, + responses={}, + **kwargs): + super().__init__(*args, **kwargs) + self.hash_to_metadata = hash_to_metadata + self.requests = requests + self.responses = responses + + async def handle(self, msg, fr, to): + if msg.enum == 'Routed': + msg_kind = msg.Routed.body.enum + if msg_kind == 'PartialEncodedChunk': + header = msg.Routed.body.PartialEncodedChunk.header + height = header.inner.height_created + shard_id = header.inner.shard_id + hash_ = header.chunk_hash() + self.hash_to_metadata[hash_] = (height, shard_id) + + if msg_kind == 'VersionedPartialEncodedChunk': + header = msg.Routed.body.VersionedPartialEncodedChunk.inner_header( + ) + header_version = msg.Routed.body.VersionedPartialEncodedChunk.header_version( + ) + if header_version == 'V3': + height = header.V2.height_created + shard_id = header.V2.shard_id + else: + height = header.height_created + shard_id = header.shard_id + + if header_version == 'V1': + hash_ = ShardChunkHeaderV1.chunk_hash(header) + elif header_version == 'V2': + hash_ = ShardChunkHeaderV2.chunk_hash(header) + elif header_version == 'V3': + hash_ = ShardChunkHeaderV3.chunk_hash(header) + self.hash_to_metadata[hash_] = (height, shard_id) + + if msg_kind == 'PartialEncodedChunkRequest': + if fr == 4: + hash_ = msg.Routed.body.PartialEncodedChunkRequest.chunk_hash + assert hash_ in self.hash_to_metadata, "chunk hash %s is not present" % base58.b58encode( + hash_) + (height, shard_id) = self.hash_to_metadata[hash_] + logger.info("REQ %s %s %s %s" % (height, shard_id, fr, to)) + self.requests[(height, shard_id, to)] = 1 + + if msg_kind == 'PartialEncodedChunkResponse': + if to == 4: + hash_ = msg.Routed.body.PartialEncodedChunkResponse.chunk_hash + (height, shard_id) = self.hash_to_metadata[hash_] + logger.info("RESP %s %s %s %s" % (height, shard_id, fr, to)) + self.responses[(height, shard_id, fr)] = 1 + + return True + + +# TODO(mina86): Make it a utility class +class Timeout: + + def __init__(self, sesconds: float) -> None: + if sesconds <= 0: + raise ValueError('sesconds must be positive') + self.__start = time.monotonic() + self.__end = self.__start + sesconds + + def check(self) -> bool: + return time.monotonic() < self.__end + + def elapsed_seconds(self) -> float: + return time.monotonic() - self.__start + + def left_seconds(self) -> float: + return self.__end - time.monotonic() + + +if __name__ == '__main__': + manager = multiprocessing.Manager() + hash_to_metadata = manager.dict() + requests = manager.dict() + responses = manager.dict() + + proxy = NodesProxy( + partial(Handler, + hash_to_metadata=hash_to_metadata, + requests=requests, + responses=responses)) + + timeout = Timeout(TIMEOUT) + + logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO) + + config = load_config() + unc_root, node_dirs = init_cluster( + 2, + 3, + 2, + config, + [ + ["min_gas_price", 0], + ["max_inflation_rate", [0, 1]], + ["epoch_length", EPOCH_LENGTH], + ['num_block_producer_seats', 4], + ["block_producer_kickout_threshold", 20], + ["chunk_producer_kickout_threshold", 20], + ["validators", 0, "amount", "110000000000000000000000000000000"], + ["validators", 1, "amount", "110000000000000000000000000000000"], + [ + "records", 0, "Account", "account", "locked", + "110000000000000000000000000000000" + ], + # each validator account is two records, thus the index of a record for the second is 2, not 1 + [ + "records", 2, "Account", "account", "locked", + "110000000000000000000000000000000" + ], + ["total_supply", "6120000000000000000000000000000000"] + ], + { + 4: { + "tracked_shards": [0, 1], + "archive": True + }, + 3: { + "archive": True, + "tracked_shards": [1], + "network": { + "ttl_account_id_router": { + "secs": 1, + "nanos": 0 + } + } + }, + 2: { + "archive": True, + "tracked_shards": [0], + "network": { + "ttl_account_id_router": { + "secs": 1, + "nanos": 0 + } + } + } + }) + + boot_node = spin_up_node(config, unc_root, node_dirs[0], 0, proxy=proxy) + node1 = spin_up_node(config, + unc_root, + node_dirs[1], + 1, + boot_node=boot_node, + proxy=proxy) + + def get_validators(node): + return set([x['account_id'] for x in node.get_status()['validators']]) + + logging.info(f'Getting to height {HEIGHTS_BEFORE_ROTATE}') + utils.wait_for_blocks(boot_node, + target=HEIGHTS_BEFORE_ROTATE, + timeout=timeout.left_seconds()) + + node2 = spin_up_node(config, + unc_root, + node_dirs[2], + 2, + boot_node=boot_node, + proxy=proxy) + node3 = spin_up_node(config, + unc_root, + node_dirs[3], + 3, + boot_node=boot_node, + proxy=proxy) + + hash_ = boot_node.get_latest_block().hash_bytes + + logging.info("Waiting for the new nodes to sync") + while True: + if (not node2.get_status()['sync_info']['syncing'] and + not node3.get_status()['sync_info']['syncing']): + break + time.sleep(1) + + for stake, nodes, expected_vals in [ + (100000000000000000000000000000000, [node2, node3], + ["test0", "test1", "test2", "test3"]), + (0, [boot_node, node1], ["test2", "test3"]), + ]: + logging.info("Rotating validators") + for ord_, node in enumerate(reversed(nodes)): + tx = sign_staking_tx(node.signer_key, node.validator_key, stake, 10, + hash_) + boot_node.send_tx(tx) + + logging.info("Waiting for rotation to occur") + while True: + assert timeout.check(), get_validators(boot_node) + if set(get_validators(boot_node)) == set(expected_vals): + break + else: + time.sleep(1) + + start_height = boot_node.get_latest_block().height + + logging.info("Killing old nodes") + boot_node.kill() + node1.kill() + + target = start_height + HEIGHTS_BEFORE_CHECK + logging.info(f'Getting to height {target}') + height_to_sync_to, _ = utils.wait_for_blocks(node2, + target=target, + timeout=timeout.left_seconds()) + + logging.info("Spinning up one more node") + node4 = spin_up_node(config, unc_root, node_dirs[4], 4, boot_node=node2) + + logging.info('Waiting for the new node to sync. ' + f'We are {timeout.elapsed_seconds()} seconds in') + while True: + assert timeout.check() + sync_info = node4.get_status()['sync_info'] + if not sync_info['syncing']: + new_height = sync_info['latest_block_height'] + assert new_height > height_to_sync_to, ( + f'new height {new_height} height to sync to {height_to_sync_to}' + ) + break + time.sleep(1) + + logging.info("Checking the messages sent and received") + + # The first two blocks are certainly more than two epochs in the past + # compared to head, and thus should be requested from archival nodes. Check + # that it's the case. Start from 10 to account for possibly skipped blocks + # while the nodes were starting. + for height in range(12, HEIGHTS_BEFORE_ROTATE): + for shard in (0, 1): + for node in (2, 3): + other = 5 - node + if (height, shard, node) in requests: + assert (height, shard, node) in responses, (height, shard, + node) + assert (height, shard, + other) not in requests, (height, shard, other) + assert (height, shard, + other) not in responses, (height, shard, other) + break + else: + assert False, f'Missing request for shard {shard} in block {height}' + + # The last 5 blocks with epoch_length=10 will certainly be in the same epoch + # as head, or in the previous epoch, and thus should be requested from the + # block producers. Note that in our case block producers are also archival + # nodes so we’re again checking nodes 2 and 3. + for height in range(new_height - 5, new_height - 1): + for shard in (0, 1): + found = False + for node in (2, 3): + if (height, shard, node) in requests: + assert (height, shard, node) in responses, (height, shard, + node) + found = True + assert found, f'Missing request for shard {shard} in block {height}' + + logging.info(f'Done. Took {timeout.elapsed_seconds()} seconds') diff --git a/pytest/tests/sanity/transactions.py b/pytest/tests/sanity/transactions.py new file mode 100755 index 000000000..c628fbee0 --- /dev/null +++ b/pytest/tests/sanity/transactions.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +# Consists of a small sanity test that verifies that a single transaction +# gets properly processed (to simplify debugging when the code is completely +# broken). If one transaction goes through, sends batches of transactions +# and ensures the balances get to the expected state in a timely manner. +# Sets epoch length to 10 + +import sys, time, base58, random +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +from transaction import sign_payment_tx +import utils + +TIMEOUT = 240 + +nodes = start_cluster( + num_nodes=4, + num_observers=1, + num_shards=4, + config=None, + genesis_config_changes=[["min_gas_price", + 0], ["max_inflation_rate", [0, 1]], + ["epoch_length", 10], + ["block_producer_kickout_threshold", 70]], + client_config_changes={ + 0: { + "consensus": { + "state_sync_timeout": { + "secs": 2, + "nanos": 0 + } + } + }, + 1: { + "consensus": { + "state_sync_timeout": { + "secs": 2, + "nanos": 0 + } + } + }, + 2: { + "consensus": { + "state_sync_timeout": { + "secs": 2, + "nanos": 0 + } + } + }, + 3: { + "consensus": { + "state_sync_timeout": { + "secs": 2, + "nanos": 0 + } + } + }, + 4: { + "consensus": { + "state_sync_timeout": { + "secs": 2, + "nanos": 0 + } + }, + "tracked_shards": [0, 1, 2, 3] + } + }) + +started = time.time() + +act_to_val = [4, 4, 4, 4, 4] + +ctx = utils.TxContext(act_to_val, nodes) + +last_balances = [x for x in ctx.expected_balances] + +step = 0 +sent_height = -1 + +height, hash_ = utils.wait_for_blocks(nodes[4], target=1, check_storage=False) +tx = sign_payment_tx(nodes[0].signer_key, 'test1', 100, 1, + base58.b58decode(hash_.encode('utf8'))) +nodes[4].send_tx(tx) +ctx.expected_balances[0] -= 100 +ctx.expected_balances[1] += 100 +logger.info('Sent tx at height %s' % height) +sent_height = height + +height, hash_ = utils.wait_for_blocks(nodes[4], + target=sent_height + 6, + check_storage=False) +cur_balances = ctx.get_balances() +assert cur_balances == ctx.expected_balances, "%s != %s" % ( + cur_balances, ctx.expected_balances) + +# we are done with the sanity test, now let's stress it +for height, _ in utils.poll_blocks(nodes[4], timeout=TIMEOUT): + if ctx.get_balances() == ctx.expected_balances: + count = height - sent_height + logger.info(f'Balances caught up, took {count} blocks, moving on') + last_balances = [x for x in ctx.expected_balances] + ctx.send_moar_txs(hash_, 10, use_routing=True) + sent_height = height + else: + assert height <= sent_height + 10, ('Balances before: {before}\n' + 'Expected balances: {expected}\n' + 'Current balances: {current}\n' + 'Sent at height: {sent_at}\n' + 'Current height: {height}').format( + before=last_balances, + expected=ctx.expected_balances, + current=ctx.get_balances(), + sent_at=sent_height, + height=height) + if height >= 100: + break diff --git a/pytest/tests/sanity/upgradable.py b/pytest/tests/sanity/upgradable.py new file mode 100755 index 000000000..a68e556c0 --- /dev/null +++ b/pytest/tests/sanity/upgradable.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python3 +"""Test if the node is backwards compatible with the latest release.""" + +import base58 +import json +import os +import pathlib +import re +import subprocess +import sys +import time +import typing +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import branches +import cluster +from configured_logger import logger +from transaction import sign_deploy_contract_tx, sign_function_call_tx, sign_payment_tx +import utils + +_EXECUTABLES = None + + +def get_executables() -> branches.ABExecutables: + global _EXECUTABLES + if _EXECUTABLES is None: + _EXECUTABLES = branches.prepare_ab_test() + logger.info(f"Latest mainnet release is {_EXECUTABLES.release}") + return _EXECUTABLES + + +def test_protocol_versions() -> None: + """Verify that mainnet, testnet and current protocol versions differ by ≤ 1. + + Checks whether the protocol versions used by the latest mainnet, the latest + testnet and current binary do not differed by more than one. Some protocol + features implementations rely on the fact that no protocol version is + skipped. See . + + This test downloads the latest official mainnet and testnet binaries. If + that fails for whatever reason, builds each of those executables. + """ + executables = get_executables() + testnet = branches.get_executables_for('testnet') + + def get_proto_version(exe: pathlib.Path) -> int: + line = subprocess.check_output((exe, '--version'), text=True) + m = re.search(r'\(release (.*?)\) .* \(protocol ([0-9]+)\)', line) + assert m, (f'Unable to extract protocol version number from {exe};\n' + f'Got {line.rstrip()} on standard output') + return m.group(1), int(m.group(2)) + + main_release, main_proto = get_proto_version(executables.stable.uncd) + test_release, test_proto = get_proto_version(testnet.uncd) + _, head_proto = get_proto_version(executables.current.uncd) + + logger.info(f'Got protocol {main_proto} in mainnet release {main_release}.') + logger.info(f'Got protocol {test_proto} in testnet release {test_release}.') + logger.info(f'Got protocol {head_proto} on master branch.') + + ok = (head_proto in (test_proto, test_proto + 1) and + test_proto in (main_proto, main_proto + 1)) + assert ok, ('If changed, protocol version of a new release can increase by ' + 'at most one.') + + +def test_upgrade() -> None: + """Test that upgrade from ‘stable’ to ‘current’ binary is possible. + + 1. Start a network with 3 `stable` nodes and 1 `new` node. + 2. Start switching `stable` nodes one by one with `new` nodes. + 3. Run for three epochs and observe that current protocol version of the + network matches `new` nodes. + """ + executables = get_executables() + node_root = utils.get_unc_tempdir('upgradable', clean=True) + + # Setup local network. + cmd = (executables.stable.uncd, f'--home={node_root}', 'localnet', '-v', + '4', '--prefix', 'test') + logger.info(' '.join(str(arg) for arg in cmd)) + subprocess.check_call(cmd) + genesis_config_changes = [("epoch_length", 20), + ("num_block_producer_seats", 10), + ("num_block_producer_seats_per_shard", [10]), + ("block_producer_kickout_threshold", 80), + ("chunk_producer_kickout_threshold", 80)] + node_dirs = [os.path.join(node_root, 'test%d' % i) for i in range(4)] + for i, node_dir in enumerate(node_dirs): + cluster.apply_genesis_changes(node_dir, genesis_config_changes) + cluster.apply_config_changes(node_dir, {'tracked_shards': [0]}) + + # Adjust changes required since #7486. This is needed because current + # stable release populates the deprecated migration configuration options. + # TODO(mina86): Remove this once we get stable release which doesn’t + # populate those fields by default. + config_path = pathlib.Path(node_dir) / 'config.json' + data = json.loads(config_path.read_text(encoding='utf-8')) + data.pop('db_migration_snapshot_path', None) + data.pop('use_db_migration_snapshot', None) + config_path.write_text(json.dumps(data), encoding='utf-8') + + # Start 3 stable nodes and one current node. + config = executables.stable.node_config() + nodes = [ + cluster.spin_up_node(config, executables.stable.root, node_dirs[0], 0) + ] + for i in range(1, 3): + nodes.append( + cluster.spin_up_node(config, + executables.stable.root, + node_dirs[i], + i, + boot_node=nodes[0])) + config = executables.current.node_config() + nodes.append( + cluster.spin_up_node(config, + executables.current.root, + node_dirs[3], + 3, + boot_node=nodes[0])) + + time.sleep(2) + + # deploy a contract + hash = nodes[0].get_latest_block().hash_bytes + tx = sign_deploy_contract_tx(nodes[0].signer_key, + utils.load_test_contract(), 1, hash) + res = nodes[0].send_tx_and_wait(tx, timeout=20) + assert 'error' not in res, res + + # write some random value + tx = sign_function_call_tx(nodes[0].signer_key, + nodes[0].signer_key.account_id, + 'write_random_value', [], 10**13, 0, 2, hash) + res = nodes[0].send_tx_and_wait(tx, timeout=20) + assert 'error' not in res, res + assert 'Failure' not in res['result']['status'], res + + utils.wait_for_blocks(nodes[0], count=20) + + # Restart stable nodes into new version. + for i in range(3): + nodes[i].kill() + nodes[i].unc_root = executables.current.root + nodes[i].binary_name = executables.current.uncd + nodes[i].start( + boot_node=nodes[0], + extra_env={"unc_TESTS_IMMEDIATE_PROTOCOL_UPGRADE": "1"}, + ) + + utils.wait_for_blocks(nodes[3], count=60) + status0 = nodes[0].get_status() + status3 = nodes[3].get_status() + protocol_version = status0['protocol_version'] + latest_protocol_version = status3["latest_protocol_version"] + assert protocol_version == latest_protocol_version, \ + "Latest protocol version %d should match active protocol version %d" % ( + latest_protocol_version, protocol_version) + + hash = base58.b58decode( + status0['sync_info']['latest_block_hash'].encode('ascii')) + + # write some random value again + tx = sign_function_call_tx(nodes[0].signer_key, + nodes[0].signer_key.account_id, + 'write_random_value', [], 10**13, 0, 4, hash) + res = nodes[0].send_tx_and_wait(tx, timeout=20) + assert 'error' not in res, res + assert 'Failure' not in res['result']['status'], res + + # hex_account_id = (b"I'm hex!" * 4).hex() + hex_account_id = '49276d206865782149276d206865782149276d206865782149276d2068657821' + tx = sign_payment_tx(key=nodes[0].signer_key, + to=hex_account_id, + amount=10**25, + nonce=5, + blockHash=hash) + res = nodes[0].send_tx_and_wait(tx, timeout=20) + # Successfully created a new account on transfer to hex + assert 'error' not in res, res + assert 'Failure' not in res['result']['status'], res + + hex_account_balance = int( + nodes[0].get_account(hex_account_id)['result']['amount']) + assert hex_account_balance == 10**25 + + +def main(): + test_protocol_versions() + test_upgrade() + + +if __name__ == "__main__": + main() diff --git a/pytest/tests/sanity/validator_switch.py b/pytest/tests/sanity/validator_switch.py new file mode 100755 index 000000000..8c4d3669c --- /dev/null +++ b/pytest/tests/sanity/validator_switch.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# Starts three validating nodes and one non-validating node +# Make the validating nodes unstake and the non-validating node stake +# so that the next epoch block producers set is completely different +# Make sure all nodes can still sync. + +import sys, time, base58 +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from configured_logger import logger +from transaction import sign_staking_tx +import utils + +EPOCH_LENGTH = 20 +tracked_shards = { + "tracked_shards": [0], # Track all shards + "state_sync_enabled": True, + "store.state_snapshot_enabled": True +} + +nodes = start_cluster( + 3, 1, 4, None, + [["epoch_length", EPOCH_LENGTH], ["block_producer_kickout_threshold", 10], + ["chunk_producer_kickout_threshold", 10]], { + 0: tracked_shards, + 1: tracked_shards + }) + +time.sleep(3) + +hash_ = nodes[0].get_latest_block().hash_bytes + +for i in range(4): + stake = 50000000000000000000000000000000 if i == 3 else 0 + tx = sign_staking_tx(nodes[i].signer_key, nodes[i].validator_key, stake, 1, + hash_) + nodes[0].send_tx(tx) + logger.info("test%s stakes %d" % (i, stake)) + +for cur_height, _ in utils.poll_blocks(nodes[0], poll_interval=1): + if cur_height >= EPOCH_LENGTH * 2: + break + if cur_height > EPOCH_LENGTH + 1: + info = nodes[0].json_rpc('validators', 'latest') + count = len(info['result']['next_validators']) + assert count == 1, 'Number of validators do not match' + validator = info['result']['next_validators'][0]['account_id'] + assert validator == 'test3' + +while cur_height <= EPOCH_LENGTH * 3: + statuses = sorted((enumerate(node.get_latest_block() for node in nodes)), + key=lambda element: element[1].height) + last = statuses.pop() + cur_height = last[1].height + node = nodes[last[0]] + succeed = True + for _, block in statuses: + try: + node.get_block(block.hash) + except Exception: + succeed = False + break + if statuses[0][1].height > EPOCH_LENGTH * 2 + 5 and succeed: + sys.exit(0) + +assert False, 'Nodes are not synced' diff --git a/pytest/tests/sanity/validator_switch_key.py b/pytest/tests/sanity/validator_switch_key.py new file mode 100755 index 000000000..5f4149546 --- /dev/null +++ b/pytest/tests/sanity/validator_switch_key.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# Starts two validating nodes and one non-validating node +# Set a new validator key that has the same account id as one of +# the validating nodes. Stake that account with the new key +# and make sure that the network doesn't stall even after +# the non-validating node becomes a validator. + +import sys, time, base58 +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import start_cluster +from key import Key +from transaction import sign_staking_tx + +EPOCH_LENGTH = 30 +TIMEOUT = 200 + +client_config = { + "network": { + "ttl_account_id_router": { + "secs": 0, + "nanos": 100000000 + } + } +} +nodes = start_cluster( + 2, 1, 1, None, + [["epoch_length", EPOCH_LENGTH], ["block_producer_kickout_threshold", 10], + ["chunk_producer_kickout_threshold", 10]], { + 1: client_config, + 2: client_config + }) +time.sleep(2) + +nodes[2].kill() + +validator_key = Key(nodes[1].validator_key.account_id, nodes[2].signer_key.pk, + nodes[2].signer_key.sk) +nodes[2].reset_validator_key(validator_key) +nodes[2].reset_data() +nodes[2].start(boot_node=nodes[0]) +time.sleep(3) + +block = nodes[0].get_latest_block() +block_height = block.height +block_hash = block.hash_bytes + +tx = sign_staking_tx(nodes[1].signer_key, validator_key, + 50000000000000000000000000000000, 1, block_hash) +res = nodes[0].send_tx_and_wait(tx, timeout=15) +assert 'error' not in res + +start_time = time.time() +while True: + assert time.time() - start_time < TIMEOUT, 'Validators got stuck' + node1_height = nodes[1].get_latest_block().height + node2_height = nodes[2].get_latest_block().height + if (node1_height > block_height + 4 * EPOCH_LENGTH and + node2_height > block_height + 4 * EPOCH_LENGTH): + break + time.sleep(2) diff --git a/pytest/tests/shardnet/README.md b/pytest/tests/shardnet/README.md new file mode 100644 index 000000000..146adb332 --- /dev/null +++ b/pytest/tests/shardnet/README.md @@ -0,0 +1,17 @@ +# Shardnet tools + +## restake.py + +Manages restaking of shardnet network participants. Uses `restaked` to regularly restake if a node is kicked. +Runs `restaked` on each of the remote machines. Gets the `restaked` binary from AWS. + +Optionally creates accounts for the remote nodes, but requires public and private keys of account `near`. + +## Example + +``` +python3 tests/shardnet/restake.py + --delay-sec 60 + --unc-pk $unc_PUBLIC_KEY + --unc-sk $unc_PRIVATE_KEY +``` diff --git a/pytest/tests/shardnet/__init__.py b/pytest/tests/shardnet/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pytest/tests/shardnet/collect_ips.py b/pytest/tests/shardnet/collect_ips.py new file mode 100644 index 000000000..2defcedc2 --- /dev/null +++ b/pytest/tests/shardnet/collect_ips.py @@ -0,0 +1,98 @@ +import argparse +import requests +import sys +from rc import pmap +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) +from configured_logger import logger + +validators_found = {} +visited_nodes = set() +known_nodes = set() +next_to_visit = [] +active_validators = set() +active_validators_height = 0 + + +def learn_about_node(node_ip): + if node_ip not in known_nodes: + known_nodes.add(node_ip) + next_to_visit.append(node_ip) + + +def visit_node(node_ip, timeout): + # logger.info(f'Visiting node {node_ip}') + + visited_nodes.add(node_ip) + assert node_ip in known_nodes + global active_validators + global active_validators_height + + account_id = None + try: + data = requests.get(f'http://{node_ip}:3030/status', + timeout=timeout).json() + validators = set([x['account_id'] for x in data['validators']]) + height = int(data['sync_info']['latest_block_height']) + if not active_validators or height > active_validators_height + 1000: + active_validators = validators + if 'validator_account_id' in data: + account_id = data['validator_account_id'] + if account_id in active_validators and not node_ip in validators_found: + validators_found[node_ip] = account_id + logger.info(f'Scraped validator {account_id}') + + data = requests.get(f'http://{node_ip}:3030/network_info', + timeout=timeout).json() + peer_id_to_account_id = {} + for known_producer in data['known_producers']: + peer_id_to_account_id[ + known_producer['peer_id']] = known_producer['account_id'] + for peer in data['active_peers']: + ip = peer['addr'].split(':')[0] + learn_about_node(ip) + peer_id = peer['id'] + if peer_id in peer_id_to_account_id: + account_id = peer_id_to_account_id[peer_id] + if account_id in validators and not ip in validators_found: + validators_found[ip] = account_id + logger.info(f'Found a validator {account_id} in peers') + # Note that peer['account_id'] is always 'null' + + except Exception as e: + logger.exception(f'Error scraping {node_ip}') + + +def discover_ips(node_ip, timeout): + learn_about_node(node_ip) + + while next_to_visit: + to_visit = next_to_visit[:] + logger.info(f'Will visit {len(to_visit)} nodes. ' + f'Validators found: {len(validators_found)}. ' + f'Visited nodes: {len(visited_nodes)}. ' + f'Known nodes: {len(known_nodes)}.') + next_to_visit.clear() + pmap(lambda ip: visit_node(ip, timeout), to_visit) + + logger.info(f'Validators found: {len(validators_found)}.') + logger.info(f'Visited nodes: {len(visited_nodes)}.') + with open('validators.csv', 'w') as f: + for ip in validators_found: + f.write('%s,%s\n' % (validators_found[ip], ip)) + + +if __name__ == '__main__': + logger.info('Starting IP collector') + parser = argparse.ArgumentParser(description='Run IP collector') + parser.add_argument('--timeout', type=float, required=True) + parser.add_argument('--node_ip', required=True) + args = parser.parse_args() + + timeout = args.timeout + assert timeout + node_ip = args.node_ip + assert node_ip + + discover_ips(node_ip, timeout) diff --git a/pytest/tests/shardnet/restake.py b/pytest/tests/shardnet/restake.py new file mode 100644 index 000000000..74e7ec2a0 --- /dev/null +++ b/pytest/tests/shardnet/restake.py @@ -0,0 +1,71 @@ +import argparse +import shlex +import random +import sys +from retrying import retry, RetryError +from rc import pmap +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) +import mocknet +from configured_logger import logger + + +@retry(retry_on_result=bool, wait_fixed=2000, stop_max_attempt_number=3) +def create_account(node, unc_pk, unc_sk): + node.machine.upload('tests/shardnet/scripts/create_account.sh', + '/home/ubuntu', + switch_user='ubuntu') + s = ''' + bash /home/ubuntu/create_account.sh {unc_pk} {unc_sk} 1>/home/ubuntu/create_account.out 2>/home/ubuntu/create_account.err + '''.format(unc_pk=shlex.quote(unc_pk), unc_sk=shlex.quote(unc_sk)) + logger.info(f'Creating an account on {node.instance_name}: {s}') + result = node.machine.run('bash', input=s) + if result.returncode != 0: + logger.error(f'error running create_account.sh on {node.instance_name}') + return result.returncode + + +def restart_restaked(node, delay_sec, unc_pk, unc_sk, need_create_accounts): + if need_create_accounts and not node.instance_name.startswith( + 'shardnet-boot'): + try: + create_account(node, unc_pk, unc_sk) + except RetryError: + logger.error( + f'Skipping stake step after errors running create_account.sh on {node.instance_name}' + ) + return + + node.machine.upload('tests/shardnet/scripts/restaked.sh', + '/home/ubuntu', + switch_user='ubuntu') + s = ''' + nohup bash ./restaked.sh {delay_sec} {stake_amount} 1>>/home/ubuntu/restaked.out 2>>/home/ubuntu/restaked.err /home/ubuntu/.unc-credentials/shardnet/near.json +pk=$(grep public_key /home/ubuntu/.near/shardnet/validator_key.json | awk -F'"' '{print $4}') +cp /home/ubuntu/.near/shardnet/validator_key.json /home/ubuntu/.unc-credentials/shardnet/"$account_id".json +unc_ENV=shardnet near --nodeUrl=http://127.0.0.1:3030 \ + create-account "$account_id" --masterAccount near \ + --initialBalance 1000000 --publicKey "$pk" diff --git a/pytest/tests/shardnet/scripts/restaked.sh b/pytest/tests/shardnet/scripts/restaked.sh new file mode 100644 index 000000000..8fa858aff --- /dev/null +++ b/pytest/tests/shardnet/scripts/restaked.sh @@ -0,0 +1,31 @@ +#!/bin/bash +set -x + +delay_sec=$1 +amount=$2 + +account_id=$(grep account_id /home/ubuntu/.near/shardnet/validator_key.json | awk -F'"' '{print $4}') +staking_key=$(grep public_key /home/ubuntu/.near/shardnet/validator_key.json | awk -F'"' '{print $4}') + +while true; do + skip=0 + unc_ENV=shardnet near --nodeUrl=http://127.0.0.1:3030 proposals | grep ${account_id} + if [ $? -eq 0 ]; then + # Already in the proposals. + echo "$(date): Found in the proposals" + skip=1 + fi + unc_ENV=shardnet near --nodeUrl=http://127.0.0.1:3030 validators current | grep ${account_id} + if [ $? -eq 0 ]; then + # Is currently a validator. + echo "$(date): Currently a validator" + skip=1 + fi + if [ ${skip} -eq 0 ]; then + # Not skipping, do the staking. + echo "$(date): Doing restaking" + unc_ENV=shardnet near --nodeUrl=http://127.0.0.1:3030 stake ${account_id} ${staking_key} ${amount} + fi + echo "$(date): Sleeping for ${delay_sec} seconds" + sleep ${delay_sec} +done \ No newline at end of file diff --git a/pytest/tests/spec/network/peers_request.py b/pytest/tests/spec/network/peers_request.py new file mode 100755 index 000000000..edfc3b46e --- /dev/null +++ b/pytest/tests/spec/network/peers_request.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +""" +PeersRequest + +Start one real node. Create a connection (conn0) to real node, send PeersRequest and wait for the response. +Create a new connection (conn1) to real node, send PeersRequest and wait for the response. In the latter +response there must exist an entry with information from the first connection that was established. +""" +import asyncio +import socket +import sys +import time +import pathlib + +import nacl.signing + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[3] / 'lib')) +from cluster import start_cluster +from messages import schema +from peer import ED_PREFIX, connect, run_handshake, create_peer_request +from utils import obj_to_string + +nodes = start_cluster(1, 0, 4, None, [], {}) + + +async def main(): + key_pair_0 = nacl.signing.SigningKey.generate() + conn0 = await connect(nodes[0].addr()) + await run_handshake(conn0, + nodes[0].node_key.pk, + key_pair_0, + listen_port=12345) + peer_request = create_peer_request() + await conn0.send(peer_request) + response = await conn0.recv('PeersResponse') + assert response.enum == 'PeersResponse', obj_to_string(response) + + key_pair_1 = nacl.signing.SigningKey.generate() + conn1 = await connect(nodes[0].addr()) + await run_handshake(conn1, + nodes[0].node_key.pk, + key_pair_1, + listen_port=12346) + peer_request = create_peer_request() + await conn1.send(peer_request) + response = await conn1.recv('PeersResponse') + assert response.enum == 'PeersResponse', obj_to_string(response) + assert any(peer_info.addr.V4[1] == 12345 + for peer_info in response.PeersResponse), obj_to_string(response) + + +asyncio.run(main()) diff --git a/pytest/tools/mirror/contract/Cargo.lock b/pytest/tools/mirror/contract/Cargo.lock new file mode 100644 index 000000000..be814c29e --- /dev/null +++ b/pytest/tools/mirror/contract/Cargo.lock @@ -0,0 +1,1521 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + +[[package]] +name = "addkey-contract" +version = "0.1.0" +dependencies = [ + "unc-sdk", +] + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.8", + "once_cell", + "version_check", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bitvec" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" +dependencies = [ + "crypto-mac", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "borsh" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +dependencies = [ + "borsh-derive", + "hashbrown", +] + +[[package]] +name = "borsh-derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate 0.1.5", + "proc-macro2", + "syn", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytesize" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c58ec36aac5066d5ca17df51b3e70279f5670a72102f5752cb7e7c856adfc70" + +[[package]] +name = "c2-chacha" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27dae93fe7b1e0424dc57179ac396908c26b035a87234809f5c4dfd1b47dc80" +dependencies = [ + "cipher", + "ppv-lite86", +] + +[[package]] +name = "cc" +version = "1.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "serde", + "time", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "cxx" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", +] + +[[package]] +name = "dyn-clone" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" + +[[package]] +name = "easy-ext" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53aff6fdc1b181225acdcb5b14c47106726fd8e486707315b1b138baed68ee31" + +[[package]] +name = "ed25519" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "impl-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "itoa" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "unc-abi" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "885db39b08518fa700b73fa2214e8adbbfba316ba82dd510f50519173eadaf73" +dependencies = [ + "borsh", + "schemars", + "semver", + "serde", +] + +[[package]] +name = "unc-account-id" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d258582a1878e6db67400b0504a5099db85718d22c2e07f747fe1706ae7150" +dependencies = [ + "borsh", + "serde", +] + +[[package]] +name = "unc-crypto" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e75673d69fd7365508f3d32483669fe45b03bfb34e4d9363e90adae9dfb416c" +dependencies = [ + "arrayref", + "blake2", + "borsh", + "bs58", + "c2-chacha", + "curve25519-dalek", + "derive_more", + "ed25519-dalek", + "unc-account-id", + "once_cell", + "parity-secp256k1", + "primitive-types", + "rand 0.7.3", + "rand_core 0.5.1", + "serde", + "serde_json", + "subtle", + "thiserror", +] + +[[package]] +name = "unc-primitives" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad1a9a1640539c81f065425c31bffcfbf6b31ef1aeaade59ce905f5df6ac860" +dependencies = [ + "borsh", + "byteorder", + "bytesize", + "chrono", + "derive_more", + "easy-ext", + "hex", + "unc-crypto", + "unc-primitives-core", + "unc-rpc-error-macro", + "unc-vm-errors", + "num-rational", + "once_cell", + "primitive-types", + "rand 0.7.3", + "reed-solomon-erasure", + "serde", + "serde_json", + "smart-default", + "strum", + "thiserror", +] + +[[package]] +name = "unc-primitives-core" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d508f0fc340f6461e4e256417685720d3c4c00bb5a939b105160e49137caba" +dependencies = [ + "base64 0.11.0", + "borsh", + "bs58", + "derive_more", + "unc-account-id", + "num-rational", + "serde", + "sha2 0.10.6", + "strum", +] + +[[package]] +name = "unc-rpc-error-core" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ee0b41c75ef859c193a8ff1dadfa0c8207bc0ac447cc22259721ad769a1408" +dependencies = [ + "quote", + "serde", + "syn", +] + +[[package]] +name = "unc-rpc-error-macro" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e837bd4bacd807073ec5ceb85708da7f721b46a4c2a978de86027fb0034ce31" +dependencies = [ + "unc-rpc-error-core", + "serde", + "syn", +] + +[[package]] +name = "unc-sdk" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15eb3de2defe3626260cc209a6cdb985c6b27b0bd4619fad97dcfae002c3c5bd" +dependencies = [ + "base64 0.13.1", + "borsh", + "bs58", + "unc-abi", + "unc-crypto", + "unc-primitives", + "unc-primitives-core", + "unc-sdk-macros", + "unc-sys", + "unc-vm-logic", + "once_cell", + "schemars", + "serde", + "serde_json", + "wee_alloc", +] + +[[package]] +name = "unc-sdk-macros" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4907affc9f5ed559456509188ff0024f1f2099c0830e6bdb66eb61d5b75912c0" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unc-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e307313276eaeced2ca95740b5639e1f3125b7c97f0a1151809d105f1aa8c6d3" + +[[package]] +name = "unc-vm-errors" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0da466a30f0446639cbd788c30865086fac3e8dcb07a79e51d2b0775ed4261e" +dependencies = [ + "borsh", + "unc-account-id", + "unc-rpc-error-macro", + "serde", +] + +[[package]] +name = "unc-vm-logic" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b534828419bacbf1f7b11ef7b00420f248c548c485d3f0cfda8bb6931152f2" +dependencies = [ + "base64 0.13.1", + "borsh", + "bs58", + "byteorder", + "unc-account-id", + "unc-crypto", + "unc-primitives", + "unc-primitives-core", + "unc-vm-errors", + "ripemd", + "serde", + "sha2 0.9.9", + "sha3", + "zeropool-bn", +] + +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parity-scale-codec" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" +dependencies = [ + "arrayvec 0.7.2", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" +dependencies = [ + "proc-macro-crate 1.2.1", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "parity-secp256k1" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fca4f82fccae37e8bbdaeb949a4a218a1bbc485d11598f193d2a908042e5fc1" +dependencies = [ + "arrayvec 0.5.2", + "cc", + "cfg-if 0.1.10", + "rand 0.7.3", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primitive-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-crate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +dependencies = [ + "once_cell", + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.8", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "reed-solomon-erasure" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a415a013dd7c5d4221382329a5a3482566da675737494935cbbbcdec04662f9d" +dependencies = [ + "smallvec", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "schemars" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + +[[package]] +name = "semver" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" + +[[package]] +name = "serde" +version = "1.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8b3801309262e8184d9687fb697586833e939767aea0dda89f5a8e650e8bd7" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "smart-default" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "uint" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a45526d29728d135c2900b0d30573fe3ee79fceb12ef534c7bb30e810a91b601" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + +[[package]] +name = "zeroize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeropool-bn" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e61de68ede9ffdd69c01664f65a178c5188b73f78faa21f0936016a888ff7c" +dependencies = [ + "borsh", + "byteorder", + "crunchy", + "lazy_static", + "rand 0.8.5", + "rustc-hex", +] diff --git a/pytest/tools/mirror/contract/Cargo.toml b/pytest/tools/mirror/contract/Cargo.toml new file mode 100644 index 000000000..d255ffeed --- /dev/null +++ b/pytest/tools/mirror/contract/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "addkey-contract" +version = "0.1.0" +authors = ["Hello Inc "] +edition = "2018" + +[workspace] +members = [] + + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +unc-sdk = "4.1.1" + +[profile.release] +codegen-units = 1 +# Tell `rustc` to optimize for small code size. +opt-level = "z" +lto = true +debug = false +panic = "abort" +# Opt into extra safety checks on arithmetic operations https://stackoverflow.com/a/64136471/249801 +overflow-checks = true diff --git a/pytest/tools/mirror/contract/src/lib.rs b/pytest/tools/mirror/contract/src/lib.rs new file mode 100644 index 000000000..96d988357 --- /dev/null +++ b/pytest/tools/mirror/contract/src/lib.rs @@ -0,0 +1,41 @@ +//! Contract that adds keys and creates accounts + +use unc_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; +use unc_sdk::{env, unc_bindgen, AccountId, Balance, Promise, PublicKey}; +use std::str::FromStr; + +#[unc_bindgen] +#[derive(Default, BorshDeserialize, BorshSerialize)] +pub struct KeyAdder {} + +#[unc_bindgen] +impl KeyAdder { + pub fn add_key(&mut self, public_key: String) -> Promise { + let signer_id = env::signer_account_id(); + if signer_id == env::current_account_id() { + let public_key = PublicKey::from_str(&public_key).unwrap(); + Promise::new(signer_id).add_full_access_key(public_key) + } else { + // here we call the contract again on the signer, assuming that + // it has been deployed. This lets us test the two cases, one where + // we have a local receipt generated by a tx with the same signer and receiver, + // and one where test1 calls test0, and what we want to look for on chain is + // the receipt generated by this cross-contract call here + Self::ext(signer_id).add_key(public_key) + } + } + + #[payable] + pub fn create_account(&mut self, account_id: AccountId, public_key: Option) -> Promise { + let mut p = Promise::new(account_id).create_account(); + if let Some(public_key) = public_key { + let public_key = PublicKey::from_str(&public_key).unwrap(); + p = p.add_full_access_key(public_key); + } + p.transfer(env::attached_deposit()) + } + + pub fn stake(&mut self, amount: Balance, public_key: PublicKey) -> Promise { + Promise::new(env::current_account_id()).stake(amount, public_key) + } +} diff --git a/pytest/tools/mirror/mirror_utils.py b/pytest/tools/mirror/mirror_utils.py new file mode 100644 index 000000000..25cca01e0 --- /dev/null +++ b/pytest/tools/mirror/mirror_utils.py @@ -0,0 +1,824 @@ +import sys +import time +import base58 +import atexit +import json +import os +import pathlib +import shutil +import signal +import subprocess + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import init_cluster, spin_up_node, load_config +from configured_logger import logger +import transaction +import utils +import key + +MIRROR_DIR = 'test-mirror' +TIMEOUT = 240 +TARGET_VALIDATORS = ['foo0', 'foo1', 'foo2'] + +CONTRACT_PATH = pathlib.Path(__file__).resolve().parents[ + 0] / 'contract/target/wasm32-unknown-unknown/release/addkey_contract.wasm' + + +def mkdir_clean(dirname): + try: + dirname.mkdir() + except FileExistsError: + shutil.rmtree(dirname) + dirname.mkdir() + + +def dot_near(): + return pathlib.Path.home() / '.near' + + +def ordinal_to_addr(port, ordinal): + return f'0.0.0.0:{port + 10 + ordinal}' + + +def copy_genesis(home): + shutil.copy(dot_near() / 'test0/forked/genesis.json', home / 'genesis.json') + shutil.copy(dot_near() / 'test0/forked/records.json', home / 'records.json') + + +def init_target_dir(uncd, + home, + ordinal, + boot_node_home, + validator_account=None): + mkdir_clean(home) + + try: + args = [uncd, '--home', home, 'init'] + if validator_account is not None: + args.extend(['--account-id', validator_account]) + subprocess.check_output(args, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + sys.exit(f'"uncd init" command failed: output: {e.stdout}') + shutil.copy(dot_near() / 'test0/config.json', home / 'config.json') + + with open(home / 'config.json', 'r') as f: + config = json.load(f) + config['genesis_records_file'] = 'records.json' + config['network']['addr'] = ordinal_to_addr(24567, ordinal) + if boot_node_home is not None: + config['network']['boot_nodes'] = read_addr_pk(boot_node_home) + config['rpc']['addr'] = ordinal_to_addr(3030, ordinal) + + with open(home / 'config.json', 'w') as f: + json.dump(config, f) + + if validator_account is None: + os.remove(home / 'validator_key.json') + + +def init_target_dirs(uncd, last_ordinal, target_validators): + ordinal = last_ordinal + 1 + dirs = [] + + for i in range(len(target_validators)): + account_id = target_validators[i] + if i > 0: + boot_node_home = dirs[0] + else: + boot_node_home = None + home = dot_near() / f'test_target_{account_id}' + dirs.append(home) + init_target_dir(uncd, + home, + ordinal, + boot_node_home, + validator_account=account_id) + ordinal += 1 + + return dirs + + +def create_forked_chain(config, unc_root, source_node_homes, + target_validators): + mkdir_clean(dot_near() / MIRROR_DIR) + binary_name = config.get('binary_name', 'uncd') + uncd = os.path.join(unc_root, binary_name) + try: + subprocess.check_output([ + uncd, "--home", + dot_near() / 'test0', "view-state", "dump-state", "--stream" + ], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + sys.exit(f'"dump-state" command failed: output: {e.stdout}') + try: + subprocess.check_output([ + uncd, + 'mirror', + 'prepare', + '--records-file-in', + dot_near() / 'test0/output/records.json', + '--records-file-out', + dot_near() / 'test0/output/mirror-records.json', + '--secret-file-out', + dot_near() / 'test0/output/mirror-secret.json', + ], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + sys.exit(f'"mirror prepare" command failed: output: {e.stdout}') + + os.rename(source_node_homes[-1], dot_near() / f'{MIRROR_DIR}/source') + ordinal = len(source_node_homes) - 1 + with open(dot_near() / f'{MIRROR_DIR}/source/config.json', 'r') as f: + config = json.load(f) + config['network']['boot_nodes'] = read_addr_pk(source_node_homes[0]) + config['network']['addr'] = ordinal_to_addr(24567, ordinal) + config['rpc']['addr'] = ordinal_to_addr(3030, ordinal) + with open(dot_near() / f'{MIRROR_DIR}/source/config.json', 'w') as f: + json.dump(config, f) + + dirs = init_target_dirs(uncd, ordinal, target_validators) + + target_dir = dot_near() / f'{MIRROR_DIR}/target' + init_target_dir(uncd, + target_dir, + len(source_node_homes) + len(dirs), + dirs[0], + validator_account=None) + shutil.copy(dot_near() / 'test0/output/mirror-secret.json', + target_dir / 'mirror-secret.json') + + os.mkdir(dot_near() / 'test0/forked') + genesis_file_in = dot_near() / 'test0/output/genesis.json' + genesis_file_out = dot_near() / 'test0/forked/genesis.json' + records_file_in = dot_near() / 'test0/output/mirror-records.json' + records_file_out = dot_near() / 'test0/forked/records.json' + + validators = [] + for d in dirs: + with open(d / 'validator_key.json') as f: + key = json.load(f) + validators.append({ + 'account_id': key['account_id'], + 'public_key': key['public_key'], + 'amount': '700000000000000' + }) + + validators_file = dot_near() / 'test0/forked/validators.json' + with open(validators_file, 'w') as f: + json.dump(validators, f) + + try: + # we want to set transaction-validity-period to a bigger number + # because the mirror code sets the block hash on transactions up to a couple minutes + # before sending them, and that can cause problems for the default localnet + # setting of transaction_validity_period. Not really worth changing the code since + # transaction_validity_period is large on mainnet and testnet anyway + subprocess.check_output([ + uncd, 'amend-genesis', '--genesis-file-in', genesis_file_in, + '--records-file-in', records_file_in, '--genesis-file-out', + genesis_file_out, '--records-file-out', records_file_out, + '--validators', validators_file, '--chain-id', 'foonet', + '--transaction-validity-period', '10000' + ], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + sys.exit(f'"amend-genesis" command failed: output: {e.stdout}') + + for d in dirs: + copy_genesis(d) + copy_genesis(target_dir) + + return [str(d) for d in dirs] + + +def read_addr_pk(home): + with open(os.path.join(home, 'config.json'), 'r') as f: + config = json.load(f) + addr = config['network']['addr'] + + with open(os.path.join(home, 'node_key.json'), 'r') as f: + k = json.load(f) + public_key = k['public_key'] + + return f'{public_key}@{addr}' + + +def mirror_cleanup(process): + process.send_signal(signal.SIGINT) + try: + process.wait(5) + except: + process.kill() + logger.error('can\'t kill mirror process') + + +# helper class so we can pass restart_once() as a callback to send_traffic() +class MirrorProcess: + + def __init__(self, unc_root, source_home, online_source): + self.online_source = online_source + self.source_home = source_home + self.uncd = os.path.join(unc_root, 'uncd') + self.start() + self.start_time = time.time() + self.restarted = False + + def start(self): + env = os.environ.copy() + env["RUST_LOG"] = "actix_web=warn,mio=warn,tokio_util=warn,actix_server=warn,actix_http=warn,indexer=info," + env.get( + "RUST_LOG", "debug") + with open(dot_near() / f'{MIRROR_DIR}/stdout', 'ab') as stdout, \ + open(dot_near() / f'{MIRROR_DIR}/stderr', 'ab') as stderr: + args = [ + self.uncd, 'mirror', 'run', "--source-home", self.source_home, + "--target-home", + dot_near() / f'{MIRROR_DIR}/target/', '--secret-file', + dot_near() / f'{MIRROR_DIR}/target/mirror-secret.json' + ] + if self.online_source: + args.append('--online-source') + self.process = subprocess.Popen(args, + stdin=subprocess.DEVNULL, + stdout=stdout, + stderr=stderr, + env=env) + logger.info("Started mirror process") + atexit.register(mirror_cleanup, self.process) + + def restart(self): + logger.info('stopping mirror process') + self.process.terminate() + self.process.wait() + with open(dot_near() / f'{MIRROR_DIR}/stderr', 'ab') as stderr: + stderr.write( + b'<><><><><><><><><><><><> restarting <><><><><><><><><><><><><><><><><><><><>\n' + ) + stderr.write( + b'<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>\n' + ) + stderr.write( + b'<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>\n' + ) + self.start() + + # meant to be used in the callback to send_traffic(). restarts the process once after 30 seconds + def restart_once(self): + code = self.process.poll() + if code is not None: + assert code == 0 + return False + + if not self.restarted and time.time() - self.start_time > 30: + self.restart() + self.restarted = True + return True + + +def check_target_validators(target_node): + try: + validators = target_node.get_validators()['result'] + except KeyError: + return + + for v in validators['current_validators']: + assert v['account_id'] in TARGET_VALIDATORS, v['account_id'] + for v in validators['next_validators']: + assert v['account_id'] in TARGET_VALIDATORS, v['account_id'] + + +# we'll test out adding an access key and then sending txs signed with it +# since that hits some codepaths we want to test +def send_add_access_key(node, key, target_key, nonce, block_hash): + action = transaction.create_full_access_key_action(target_key.decoded_pk()) + tx = transaction.sign_and_serialize_transaction(target_key.account_id, + nonce, [action], block_hash, + key.account_id, + key.decoded_pk(), + key.decoded_sk()) + res = node.send_tx(tx) + logger.info( + f'sent add key tx for {target_key.account_id} {target_key.pk}: {res}') + + +def send_delete_access_key(node, key, target_key, nonce, block_hash): + action = transaction.create_delete_access_key_action( + target_key.decoded_pk()) + tx = transaction.sign_and_serialize_transaction(target_key.account_id, + nonce, [action], block_hash, + target_key.account_id, + key.decoded_pk(), + key.decoded_sk()) + res = node.send_tx(tx) + logger.info( + f'sent delete key tx for {target_key.account_id} {target_key.pk}: {res}' + ) + + +def create_subaccount(node, signer_key, nonce, block_hash): + k = key.Key.from_random('foo.' + signer_key.account_id) + actions = [] + actions.append(transaction.create_create_account_action()) + actions.append(transaction.create_full_access_key_action(k.decoded_pk())) + actions.append(transaction.create_payment_action(10**29)) + # add an extra one just to exercise some more corner cases + actions.append( + transaction.create_full_access_key_action( + key.Key.from_random(k.account_id).decoded_pk())) + + tx = transaction.sign_and_serialize_transaction(k.account_id, nonce, + actions, block_hash, + signer_key.account_id, + signer_key.decoded_pk(), + signer_key.decoded_sk()) + res = node.send_tx(tx) + logger.info(f'sent create account tx for {k.account_id} {k.pk}: {res}') + return k + + +def deploy_addkey_contract(node, signer_key, contract_path, nonce, block_hash): + code = utils.load_binary_file(contract_path) + tx = transaction.sign_deploy_contract_tx(signer_key, code, nonce, + block_hash) + res = node.send_tx(tx) + logger.info(f'sent deploy contract tx for {signer_key.account_id}: {res}') + + +def call_addkey(node, signer_key, new_key, nonce, block_hash, extra_actions=[]): + args = bytearray(json.dumps({'public_key': new_key.pk}), encoding='utf-8') + + # add a transfer action and the extra_actions to exercise some more code paths + actions = [ + transaction.create_function_call_action('add_key', args, 10**14, 0), + transaction.create_payment_action(123) + ] + actions.extend(extra_actions) + tx = transaction.sign_and_serialize_transaction('test0', nonce, actions, + block_hash, + signer_key.account_id, + signer_key.decoded_pk(), + signer_key.decoded_sk()) + res = node.send_tx(tx) + logger.info(f'called add_key for {new_key.account_id} {new_key.pk}: {res}') + + +def call_create_account(node, signer_key, account_id, public_key, nonce, + block_hash): + args = json.dumps({'account_id': account_id, 'public_key': public_key}) + args = bytearray(args, encoding='utf-8') + + actions = [ + transaction.create_function_call_action('create_account', args, 10**13, + 10**24), + transaction.create_payment_action(123) + ] + tx = transaction.sign_and_serialize_transaction('test0', nonce, actions, + block_hash, + signer_key.account_id, + signer_key.decoded_pk(), + signer_key.decoded_sk()) + res = node.send_tx(tx) + logger.info( + f'called create account contract for {account_id}, public key: {public_key}: {res}' + ) + + +def call_stake(node, signer_key, amount, public_key, nonce, block_hash): + args = json.dumps({'amount': amount, 'public_key': public_key}) + args = bytearray(args, encoding='utf-8') + + actions = [ + transaction.create_function_call_action('stake', args, 10**13, 0), + ] + tx = transaction.sign_and_serialize_transaction(signer_key.account_id, + nonce, actions, block_hash, + signer_key.account_id, + signer_key.decoded_pk(), + signer_key.decoded_sk()) + res = node.send_tx(tx) + logger.info( + f'called stake contract for {signer_key.account_id} {amount} {public_key}: {res}' + ) + + +def contract_deployed(node, account_id): + return 'error' not in node.json_rpc('query', { + "request_type": "view_code", + "account_id": account_id, + "finality": "final" + }) + + +# a key that we added with an AddKey tx or implicit account transfer. +# just for nonce handling convenience +class AddedKey: + + def __init__(self, key): + self.nonce = None + self.key = key + + def send_if_inited(self, node, transfers, block_hash): + if self.nonce is None: + self.nonce = node.get_nonce_for_pk(self.key.account_id, + self.key.pk, + finality='final') + if self.nonce is not None: + logger.info( + f'added key {self.key.account_id} {self.key.pk} inited @ {self.nonce}' + ) + + if self.nonce is not None: + for (receiver_id, amount) in transfers: + self.nonce += 1 + tx = transaction.sign_payment_tx(self.key, receiver_id, amount, + self.nonce, block_hash) + node.send_tx(tx) + + def account_id(self): + return self.key.account_id + + def inited(self): + return self.nonce is not None + + +class ImplicitAccount: + + def __init__(self): + self.key = AddedKey(key.Key.implicit_account()) + + def account_id(self): + return self.key.account_id() + + def transfer(self, node, sender_key, amount, block_hash, nonce): + tx = transaction.sign_payment_tx(sender_key, self.account_id(), amount, + nonce, block_hash) + node.send_tx(tx) + logger.info( + f'sent {amount} to initialize implicit account {self.account_id()}') + + def send_if_inited(self, node, transfers, block_hash): + self.key.send_if_inited(node, transfers, block_hash) + + def inited(self): + return self.key.inited() + + +def count_total_txs(node, min_height=0): + total = 0 + h = node.get_latest_block().hash + while True: + block = node.get_block(h)['result'] + height = int(block['header']['height']) + if height < min_height: + return total + + for c in block['chunks']: + if int(c['height_included']) == height: + chunk = node.get_chunk(c['chunk_hash'])['result'] + total += len(chunk['transactions']) + + h = block['header']['prev_hash'] + if h == '11111111111111111111111111111111': + return total + + +def allowed_run_time(target_node_dir, start_time, end_source_height): + with open(os.path.join(target_node_dir, 'genesis.json'), 'r') as f: + genesis_height = json.load(f)['genesis_height'] + with open(os.path.join(target_node_dir, 'config.json'), 'r') as f: + delay = json.load(f)['consensus']['min_block_production_delay'] + block_delay = 10**9 * int(delay['secs']) + int(delay['nanos']) + block_delay = block_delay / 10**9 + + # start_time is the time the mirror binary was started. Give it 20 seconds to + # sync and then 50% more than min_block_production_delay for each block between + # the start and end points of the source chain. Not ideal to be basing a test on time + # like this but there's no real strong guarantee on when the transactions should + # make it on chain, so this is some kind of reasonable timeout + + return 20 + (end_source_height - genesis_height) * block_delay * 1.5 + + +def check_num_txs(source_node, target_node): + with open(os.path.join(target_node.node_dir, 'genesis.json'), 'r') as f: + genesis_height = json.load(f)['genesis_height'] + + total_source_txs = count_total_txs(source_node, min_height=genesis_height) + total_target_txs = count_total_txs(target_node) + assert total_source_txs <= total_target_txs, (total_source_txs, + total_target_txs) + logger.info( + f'passed. num source txs: {total_source_txs} num target txs: {total_target_txs}' + ) + + +# keeps info initialized during start_source_chain() for use in send_traffic() +class TrafficData: + + def __init__(self, num_accounts): + self.nonces = [2] * num_accounts + self.implicit_account = None + self.keyless_account0 = 'keyless0.test0' + self.keyless_account1 = 'keyless1.test0' + + def send_transfers(self, nodes, block_hash, skip_senders=None): + for sender in range(len(self.nonces)): + if skip_senders is not None and sender in skip_senders: + continue + receiver = (sender + 1) % len(self.nonces) + receiver_id = nodes[receiver].signer_key.account_id + + tx = transaction.sign_payment_tx(nodes[sender].signer_key, + receiver_id, 300, + self.nonces[sender], block_hash) + nodes[sender].send_tx(tx) + self.nonces[sender] += 1 + + def check_ok(self, source_node, target_node): + keys = target_node.get_access_key_list(self.keyless_account0) + assert len(keys['result']['keys']) > 0, keys + keys = target_node.get_access_key_list(self.keyless_account1) + assert len(keys['result']['keys']) > 0, keys + check_num_txs(source_node, target_node) + + +def added_keys_send_transfers(nodes, added_keys, receivers, amount, block_hash): + node_idx = 0 + for key in added_keys: + key.send_if_inited(nodes[node_idx], + [(receiver, amount) for receiver in receivers], + block_hash) + node_idx += 1 + node_idx %= len(nodes) + + +def start_source_chain(config, num_source_validators=3): + # for now we need at least 2 because we're sending traffic for source_nodes[1].signer_key + # Could fix that but for now this assert is fine + assert num_source_validators >= 2 + + if not os.path.exists(CONTRACT_PATH): + sys.exit( + 'please build the addkey contract by running cargo build --target wasm32-unknown-unknown --release from the ./contract/ dir' + ) + + config_changes = {} + for i in range(num_source_validators + 1): + config_changes[i] = {"tracked_shards": [0, 1, 2, 3], "archive": True} + + config = load_config() + unc_root, source_node_dirs = init_cluster( + num_nodes=num_source_validators, + num_observers=1, + num_shards=4, + config=config, + # set epoch length to a larger number because otherwise there + # are often problems with validators getting kicked for missing + # only one block or chunk + genesis_config_changes=[ + ["epoch_length", 100], + ], + client_config_changes=config_changes) + + source_nodes = [spin_up_node(config, unc_root, source_node_dirs[0], 0)] + + for i in range(1, num_source_validators): + source_nodes.append( + spin_up_node(config, + unc_root, + source_node_dirs[i], + i, + boot_node=source_nodes[0])) + traffic_data = TrafficData(len(source_nodes)) + + traffic_data.implicit_account = ImplicitAccount() + for height, block_hash in utils.poll_blocks(source_nodes[0], + timeout=TIMEOUT): + block_hash_bytes = base58.b58decode(block_hash.encode('utf8')) + traffic_data.implicit_account.transfer(source_nodes[0], + source_nodes[0].signer_key, + 10**24, block_hash_bytes, + traffic_data.nonces[0]) + traffic_data.nonces[0] += 1 + + deploy_addkey_contract(source_nodes[0], source_nodes[0].signer_key, + CONTRACT_PATH, traffic_data.nonces[0], + block_hash_bytes) + traffic_data.nonces[0] += 1 + deploy_addkey_contract(source_nodes[0], source_nodes[1].signer_key, + CONTRACT_PATH, traffic_data.nonces[1], + block_hash_bytes) + traffic_data.nonces[1] += 1 + break + + for height, block_hash in utils.poll_blocks(source_nodes[0], + timeout=TIMEOUT): + block_hash_bytes = base58.b58decode(block_hash.encode('utf8')) + + traffic_data.implicit_account.send_if_inited(source_nodes[0], + [('test2', height), + ('test3', height)], + block_hash_bytes) + traffic_data.send_transfers(source_nodes, block_hash_bytes) + + if height > 12: + break + + source_nodes[0].kill() + target_node_dirs = create_forked_chain(config, unc_root, source_node_dirs, + TARGET_VALIDATORS) + source_nodes[0].start(boot_node=source_nodes[1]) + return unc_root, source_nodes, target_node_dirs, traffic_data + + +# callback will be called once for every iteration of the utils.poll_blocks() +# loop, and we break if it returns False +def send_traffic(unc_root, source_nodes, traffic_data, callback): + tip = source_nodes[1].get_latest_block() + block_hash_bytes = base58.b58decode(tip.hash.encode('utf8')) + start_source_height = tip.height + + subaccount_key = AddedKey( + create_subaccount(source_nodes[1], source_nodes[0].signer_key, + traffic_data.nonces[0], block_hash_bytes)) + traffic_data.nonces[0] += 1 + + k = key.Key.from_random('test0') + new_key = AddedKey(k) + send_add_access_key(source_nodes[1], source_nodes[0].signer_key, k, + traffic_data.nonces[0], block_hash_bytes) + traffic_data.nonces[0] += 1 + + test0_contract_key = key.Key.from_random('test0') + test0_contract_extra_key = key.Key.from_random('test0') + + # here we are assuming that the deployed contract has landed since we called start_source_chain() + # we will add an extra AddKey action to hit some more code paths + call_addkey(source_nodes[1], + source_nodes[0].signer_key, + test0_contract_key, + traffic_data.nonces[0], + block_hash_bytes, + extra_actions=[ + transaction.create_full_access_key_action( + test0_contract_extra_key.decoded_pk()) + ]) + traffic_data.nonces[0] += 1 + + test0_contract_key = AddedKey(test0_contract_key) + test0_contract_extra_key = AddedKey(test0_contract_extra_key) + + test1_contract_key = key.Key.from_random('test1') + + call_addkey(source_nodes[1], source_nodes[1].signer_key, test1_contract_key, + traffic_data.nonces[1], block_hash_bytes) + traffic_data.nonces[1] += 1 + test1_contract_key = AddedKey(test1_contract_key) + + test0_subaccount_contract_key = AddedKey(key.Key.from_random('test0.test0')) + call_create_account(source_nodes[1], source_nodes[0].signer_key, + test0_subaccount_contract_key.key.account_id, + test0_subaccount_contract_key.key.pk, + traffic_data.nonces[0], block_hash_bytes) + traffic_data.nonces[0] += 1 + test1_subaccount_contract_key = AddedKey(key.Key.from_random('test1.test0')) + call_create_account(source_nodes[1], source_nodes[1].signer_key, + test1_subaccount_contract_key.key.account_id, + test1_subaccount_contract_key.key.pk, + traffic_data.nonces[1], block_hash_bytes) + traffic_data.nonces[1] += 1 + + # here we create an account from a contract without adding an access key, + # and then check that the mirror binary adds a full access key so we can control the account + call_create_account(source_nodes[1], source_nodes[0].signer_key, + traffic_data.keyless_account0, None, + traffic_data.nonces[0], block_hash_bytes) + traffic_data.nonces[0] += 1 + # now do the same thing but with signer different from the contract account + # to exercise different code paths + call_create_account(source_nodes[1], source_nodes[1].signer_key, + traffic_data.keyless_account1, None, + traffic_data.nonces[1], block_hash_bytes) + traffic_data.nonces[1] += 1 + + test0_deleted_height = None + test0_readded_key = None + implicit_added = None + implicit_deleted = None + implicit_account2 = ImplicitAccount() + subaccount_contract_deployed = False + subaccount_staked = False + + # here we are gonna send a tiny amount (1 yoctoNEAR) to the implicit account and + # then wait a bit before properly initializing it. This hits a corner case where the + # mirror binary needs to properly look for the second tx's outcome to find the starting + # nonce because the first one failed + implicit_account2.transfer(source_nodes[1], source_nodes[0].signer_key, 1, + block_hash_bytes, traffic_data.nonces[0]) + traffic_data.nonces[0] += 1 + time.sleep(2) + implicit_account2.transfer(source_nodes[1], source_nodes[0].signer_key, + 10**24, block_hash_bytes, traffic_data.nonces[0]) + traffic_data.nonces[0] += 1 + + for height, block_hash in utils.poll_blocks(source_nodes[1], + timeout=TIMEOUT): + if not callback(): + break + block_hash_bytes = base58.b58decode(block_hash.encode('utf8')) + + if test0_deleted_height is None: + traffic_data.send_transfers(source_nodes, block_hash_bytes) + else: + traffic_data.send_transfers(source_nodes, + block_hash_bytes, + skip_senders=set([0])) + + traffic_data.implicit_account.send_if_inited( + source_nodes[1], [('test2', height), ('test1', height), + (implicit_account2.account_id(), height)], + block_hash_bytes) + if not implicit_deleted: + implicit_account2.send_if_inited( + source_nodes[1], + [('test2', height), ('test0', height), + (traffic_data.implicit_account.account_id(), height)], + block_hash_bytes) + keys = [ + new_key, + subaccount_key, + test0_contract_key, + test0_contract_extra_key, + test1_contract_key, + test0_subaccount_contract_key, + test1_subaccount_contract_key, + ] + added_keys_send_transfers(source_nodes, keys, [ + traffic_data.implicit_account.account_id(), + implicit_account2.account_id(), 'test2', 'test3' + ], height, block_hash_bytes) + + if implicit_added is None: + # wait for 15 blocks after we started to get some "normal" traffic + # from this implicit account that's closer to what we usually see from + # these (most people aren't adding access keys to implicit accounts much). + # then after that we add an access key and delete the original one to test + # some more code paths + if implicit_account2.inited( + ) and height - start_source_height >= 15: + k = key.Key.from_random(implicit_account2.account_id()) + implicit_added = AddedKey(k) + send_add_access_key(source_nodes[1], implicit_account2.key.key, + k, implicit_account2.key.nonce, + block_hash_bytes) + implicit_account2.key.nonce += 1 + else: + implicit_added.send_if_inited(source_nodes[1], [('test0', height)], + block_hash_bytes) + if implicit_added.inited() and not implicit_deleted: + send_delete_access_key(source_nodes[1], implicit_added.key, + implicit_account2.key.key, + implicit_added.nonce, block_hash_bytes) + implicit_added.nonce += 1 + implicit_deleted = True + + if test0_deleted_height is None and new_key.inited( + ) and height - start_source_height >= 15: + send_delete_access_key(source_nodes[1], new_key.key, + source_nodes[0].signer_key, + new_key.nonce + 1, block_hash_bytes) + new_key.nonce += 1 + test0_deleted_height = height + + if test0_readded_key is None and test0_deleted_height is not None and height - test0_deleted_height >= 5: + send_add_access_key(source_nodes[1], new_key.key, + source_nodes[0].signer_key, new_key.nonce + 1, + block_hash_bytes) + test0_readded_key = AddedKey(source_nodes[0].signer_key) + new_key.nonce += 1 + + if test0_readded_key is not None: + test0_readded_key.send_if_inited( + source_nodes[1], [('test3', height), + (implicit_account2.account_id(), height)], + block_hash_bytes) + + if subaccount_key.inited(): + if not subaccount_contract_deployed: + subaccount_key.nonce += 1 + deploy_addkey_contract(source_nodes[0], subaccount_key.key, + CONTRACT_PATH, subaccount_key.nonce, + block_hash_bytes) + subaccount_contract_deployed = True + elif not subaccount_staked: + if contract_deployed(source_nodes[0], + subaccount_key.account_id()): + subaccount_key.nonce += 1 + call_stake(source_nodes[0], subaccount_key.key, 10**28, + subaccount_key.key.pk, subaccount_key.nonce, + block_hash_bytes) + subaccount_staked = True + + if height - start_source_height >= 100: + break diff --git a/pytest/tools/mirror/offline_test.py b/pytest/tools/mirror/offline_test.py new file mode 100755 index 000000000..5160b729d --- /dev/null +++ b/pytest/tools/mirror/offline_test.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +import sys +import time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import spin_up_node, load_config +from configured_logger import logger + +import mirror_utils + +# This sets up an environment to test the tools/mirror process. It starts a localnet with a few validators +# and waits for some blocks to be produced. Then we fork the state and start a new chain from that, and +# send some traffic. After a while we stop the source chain nodes and start the target chain nodes, +# and run the mirror binary that should send the source chain traffic to the target chain + + +def main(): + config = load_config() + + unc_root, source_nodes, target_node_dirs, traffic_data = mirror_utils.start_source_chain( + config) + + # sleep for a bit to allow test0 to catch up after restarting before we send traffic + time.sleep(5) + mirror_utils.send_traffic(unc_root, source_nodes, traffic_data, + lambda: True) + + target_nodes = [ + spin_up_node(config, unc_root, target_node_dirs[i], + len(source_nodes) + 1 + i) + for i in range(len(target_node_dirs)) + ] + + end_source_height = source_nodes[0].get_latest_block().height + time.sleep(5) + # we don't need these anymore + for node in source_nodes[1:]: + node.kill() + + mirror = mirror_utils.MirrorProcess(unc_root, + source_nodes[1].node_dir, + online_source=False) + + total_time_allowed = mirror_utils.allowed_run_time(target_node_dirs[0], + mirror.start_time, + end_source_height) + while True: + time.sleep(5) + + mirror_utils.check_target_validators(target_nodes[0]) + + # this will restart the binary one time during this test, and it will return false + # when it exits on its own, which should happen once it finishes sending all the + # transactions in its source chain (~/.near/test1/) + if not mirror.restart_once(): + break + elapsed = time.time() - mirror.start_time + if elapsed > total_time_allowed: + logger.warn( + f'mirror process has not exited after {int(elapsed)} seconds. stopping the test now' + ) + break + + traffic_data.check_ok(source_nodes[0], target_nodes[0]) + + +if __name__ == '__main__': + main() diff --git a/pytest/tools/mirror/online_test.py b/pytest/tools/mirror/online_test.py new file mode 100755 index 000000000..73ef61922 --- /dev/null +++ b/pytest/tools/mirror/online_test.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +import sys +import time +import pathlib + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +from cluster import spin_up_node, load_config +from configured_logger import logger + +import mirror_utils + +# This sets up an environment to test the tools/mirror process. It starts a localnet with a few validators +# and waits for some blocks to be produced. Then we fork the state and start a new chain from that, and +# start the mirror process while the sending more traffic to the source chain + + +def main(): + config = load_config() + + unc_root, source_nodes, target_node_dirs, traffic_data = mirror_utils.start_source_chain( + config) + + target_nodes = [ + spin_up_node(config, unc_root, target_node_dirs[i], + len(source_nodes) + 1 + i) + for i in range(len(target_node_dirs)) + ] + + mirror = mirror_utils.MirrorProcess(unc_root, + mirror_utils.dot_near() / + f'{mirror_utils.MIRROR_DIR}/source', + online_source=True) + mirror_utils.send_traffic(unc_root, source_nodes, traffic_data, + mirror.restart_once) + + end_source_height = source_nodes[0].get_latest_block().height + time.sleep(5) + # we don't need these anymore + for node in source_nodes[1:]: + node.kill() + + total_time_allowed = mirror_utils.allowed_run_time(target_node_dirs[0], + mirror.start_time, + end_source_height) + time_elapsed = time.time() - mirror.start_time + if time_elapsed < total_time_allowed: + time_left = total_time_allowed - time_elapsed + logger.info( + f'waiting for {int(time_left)} seconds to allow transactions to make it to the target chain' + ) + time.sleep(time_left) + traffic_data.check_ok(source_nodes[0], target_nodes[0]) + + +if __name__ == '__main__': + main() diff --git a/pytest/tools/prober/prober.py b/pytest/tools/prober/prober.py new file mode 100755 index 000000000..7bb2fc963 --- /dev/null +++ b/pytest/tools/prober/prober.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +""" +Prober that is compatible with cloudprober. +Prober verifies that a random block since the genesis can be retrieved from a node. +Makes 3 separate RPC requests: Get genesis height, get head, get a block. +If the block contains chunks, then make one more RPC request to get a chunk. + +Run like this: +./prober --url http://my.test.net.node:3030 + +""" + +import argparse +import datetime +import pathlib +import random +import sys + +from prober_util import * + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + + +def main(): + # log an empty line for cloudprober nice formatting + logger.info('') + logger.info('Running Prober') + parser = argparse.ArgumentParser(description='Run a prober') + parser.add_argument('--url', required=True) + args = parser.parse_args() + + url = args.url + + # Determine the genesis height and the head height. + genesis_height = get_genesis_height(url) + head = get_head(url) + if head < genesis_height: + logger.error( + f'head must be higher than genesis. Got {head} and {genesis_height}' + ) + sys.exit(1) + + # Pick a random number and then try to lookup a block at that height. + random_height = random.randint(genesis_height, head) + attempt = 0 + while True: + block = get_block(random_height, url) + if block: + break + + # Some blocks are really missing and there is no way to know if they are + # missing because the node doesn't have history, or because the block + # was skipped. + if random_height == genesis_height: + logger.error( + f'Genesis block is missing. This is impossible {random_height}') + sys.exit(1) + + random_height -= 1 + attempt += 1 + # Limit the number of attempts. + if attempt > 10: + logger.error( + f'{attempt} consecutive blocks are missing. This is improbable. From {random_height + 1} to {random_height + attempt}' + ) + sys.exit(1) + + # Lookup a chunk to make sure the node contains it. + num_chunks = len(block['chunks']) + logger.info(f'Block {random_height} contains {num_chunks} chunks') + timestamp = block["header"]["timestamp"] // 10**9 + timestamp = datetime.datetime.fromtimestamp(timestamp) + logger.info(f'Block {random_height} timestamp {timestamp}') + if num_chunks > 0: + chunk = random.choice(block['chunks']) + get_chunk(chunk, url) + + logger.info('Success.') + + +if __name__ == '__main__': + main() diff --git a/pytest/tools/prober/prober_split.py b/pytest/tools/prober/prober_split.py new file mode 100755 index 000000000..fa9ed3589 --- /dev/null +++ b/pytest/tools/prober/prober_split.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 +""" +Prober that is compatible with cloudprober. + +The ProberSplit queries two nodes for blocks and chunks at random heights and +compares the results. The expectation is that the block and chunks at each +height will be identical even when fetched from two different nodes. It also +executes a contract view call on both nodes and compares the results. + +The prober runs continuously for the duration specified in the command line +arguments. It runs at least one block and chunk check at a random height. + +The intended goal of this prober is ensure that a legacy archival node and a +split storage archival node contain the same data. + +Run like this: +./prober_split.py --chain-id testnet --split-url http://split.archival.node:3030 --duration-ms 20000 + +""" + +import argparse +import datetime +import random +import sys +import subprocess +from datetime import datetime, timedelta + +from prober_util import * + + +def check_genesis(legacy_url: str, split_url: str) -> int: + legacy_genesis_height = get_genesis_height(legacy_url) + split_genesis_height = get_genesis_height(split_url) + + if legacy_genesis_height != split_genesis_height: + logger.error( + "The genesis height is different. legacy: {}, split {}", + legacy_genesis_height, + split_genesis_height, + ) + sys.exit(1) + + return legacy_genesis_height + + +def check_head(legacy_url: str, split_url: str, genesis_height: int) -> int: + legacy_head_height = get_head(legacy_url) + split_head_height = get_head(split_url) + + if legacy_head_height <= genesis_height: + logger.error( + '{} head must be higher than genesis. Got {} and {}', + legacy_url, + legacy_head_height, + genesis_height, + ) + sys.exit(1) + + if split_head_height <= genesis_height: + logger.error( + '{} head must be higher than genesis. Got {} and {}', + split_url, + split_head_height, + genesis_height, + ) + sys.exit(1) + + return min(legacy_head_height, split_head_height) + + +def check_blocks(legacy_url: str, split_url: str, height: int): + logger.info(f"Checking blocks at height {height}.") + + legacy_block = get_block(height, legacy_url) + split_block = get_block(height, split_url) + + if legacy_block != split_block: + logger.error( + f"Block check failed, the legacy block and the split block are different", + f"\nlegacy block\n{pretty_print(legacy_block)}" + f"\nsplit block\n{pretty_print(split_block)}") + sys.exit(1) + + return legacy_block + + +def check_chunks(legacy_url: str, split_url: str, block): + if block is None: + return + + logger.info(f"Checking chunks.") + for chunk in block['chunks']: + legacy_chunk = get_chunk(chunk, legacy_url) + split_chunk = get_chunk(chunk, split_url) + + if legacy_chunk != split_chunk: + logger.error( + f"Chunk check failed, the legacy chunk and the split chunk are different" + f"\nlegacy chunk\n{pretty_print(legacy_chunk)}" + f"\nsplit chunk\n{pretty_print(split_chunk)}") + sys.exit(1) + + +def check_view_call(legacy_url, split_url): + logger.info(f"Checking view call.") + + # This is the example contract function call from + # https://docs.near.org/api/rpc/contracts#call-a-contract-function + params = { + "request_type": "call_function", + "finality": "final", + "account_id": "dev-1588039999690", + "method_name": "get_num", + "args_base64": "e30=" + } + legacy_resp = json_rpc('query', params, legacy_url) + split_resp = json_rpc('query', params, split_url) + + if legacy_resp['result']['result'] != split_resp['result']['result']: + logger.error( + f'View call check failed, the legacy response and the split response are different' + f'\nlegacy response\n{legacy_resp}' + f'\nsplit response\n{split_resp}') + sys.exit(1) + + +# Query gcp for the archive nodes, pick a random one and return its url. +def get_random_legacy_url(chain_id): + cmd = [ + 'gcloud', + 'compute', + 'instances', + 'list', + ] + logger.info(" ".join(cmd)) + result = subprocess.run(cmd, text=True, capture_output=True) + stdout = result.stdout + lines = stdout.split('\n') + pattern = f'{chain_id}-rpc-archive-public' + lines = list(filter(lambda line: pattern in line, lines)) + line = random.choice(lines) + tokens = line.split() + external_ip = tokens[4] + + logger.info(f'Selected random legacy node - {external_ip}') + return f'http://{external_ip}:3030' + + +def main(): + start_time = datetime.now() + + parser = argparse.ArgumentParser( + description='Run a prober for split archival nodes') + parser.add_argument('--chain-id', required=True, type=str) + parser.add_argument('--split-url', required=True, type=str) + parser.add_argument('--duration-ms', default=2000, type=int) + parser.add_argument('--log-level', default="INFO") + args = parser.parse_args() + + logger.setLevel(args.log_level) + # log an empty line for cloudprober nice formatting + logger.info('') + logger.info('Running Prober Split') + + legacy_url = get_random_legacy_url(args.chain_id) + split_url = args.split_url + duration = timedelta(milliseconds=args.duration_ms) + + genesis_height = check_genesis(legacy_url, split_url) + head = check_head(legacy_url, split_url, genesis_height) + logger.info(f'The genesis height is {genesis_height}.') + logger.info(f'The head height is {head}') + + check_view_call(legacy_url, split_url) + + # Verify multiple heights - optimization to allow the prober to verify + # multiple heights in a single run. + count = 0 + none_count = 0 + while True: + # Pick a random number and then check the block and chunks at that height. + height = random.randint(genesis_height, head) + block = check_blocks(legacy_url, split_url, height) + check_chunks(legacy_url, split_url, block) + + count += 1 + none_count += block is None + + current_time = datetime.now() + current_duration = current_time - start_time + if current_duration >= duration: + break + + time.sleep(0.200) + + logger.info( + f"Success. Validated {count} blocks. There were {none_count} missing blocks." + ) + + +if __name__ == '__main__': + main() diff --git a/pytest/tools/prober/prober_util.py b/pytest/tools/prober/prober_util.py new file mode 100644 index 000000000..9c819a52d --- /dev/null +++ b/pytest/tools/prober/prober_util.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 + +import sys +import json +import time +import pathlib +import requests +import json + +sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / 'lib')) + +import configured_logger + +logger = configured_logger.new_logger("stderr", stderr=True) + + +def pretty_print(value) -> str: + return json.dumps(value, indent=2) + + +def json_rpc(method, params, url): + try: + j = { + 'method': method, + 'params': params, + 'id': 'dontcare', + 'jsonrpc': '2.0' + } + start_time = time.time() + r = requests.post(url, json=j, timeout=5) + latency_ms = (time.time() - start_time) + logger.debug( + f'prober_request_latency_ms{{method="{method}",url="{url}"}} {latency_ms:.2f}' + ) + result = json.loads(r.content) + return result + except Exception as e: + logger.error(f'json_rpc({method}, {url}) query failed: {e}') + sys.exit(1) + + +def get_genesis_height(url): + try: + genesis_config = json_rpc('EXPERIMENTAL_genesis_config', None, url) + genesis_height = genesis_config['result']['genesis_height'] + logger.debug(f'Got genesis_height {genesis_height}') + return genesis_height + except Exception as e: + logger.error(f'get_genesis_height({url}) failed: {e}') + sys.exit(1) + + +def get_head(url): + try: + status = json_rpc('status', None, url) + head = status['result']['sync_info']['latest_block_height'] + logger.debug(f'Got latest_block_height {head}') + return head + except Exception as e: + logger.error(f'get_head({url}) failed: {e}') + sys.exit(1) + + +def get_block(height, url): + try: + block = json_rpc('block', {'block_id': height}, url) + if 'error' in block: + raise Exception(block['error']) + block = block['result'] + logger.debug(f'Got block at height {height}') + return block + except Exception as e: + # This is typically fine as there may be gaps in the chain. + logger.error(f'get_block({height}, {url}) failed: {e}') + return None + + +def get_chunk(chunk, url): + try: + shard_id = chunk['shard_id'] + chunk_hash = chunk['chunk_hash'] + chunk = json_rpc('chunk', {'chunk_id': chunk_hash}, url) + chunk = chunk['result'] + + logger.debug(f'Got chunk {chunk_hash} for shard {shard_id}') + return chunk + except Exception as e: + logger.error(f'get_chunk({chunk_hash}, {url}) failed: {e}') + sys.exit(1) diff --git a/runtime/CHANGELOG.md b/runtime/CHANGELOG.md new file mode 100644 index 000000000..6fe9e167d --- /dev/null +++ b/runtime/CHANGELOG.md @@ -0,0 +1,53 @@ +# Changelog + +## Pending + +- Introduce `alt_bn128_g1_multiexp`, `alt_bn128_g1_sum` and `alt_bn128_pairing_check` host functions to `unc-vm-logic`. + +## 3.0.0 + +- Dependency on `unc-core-primitives` + +## 2.3.0 + +- Added disk cache to `unc-vm-runner` +- Contains a dependency on `unc-primitives`, so it's impossible to publish to crates.io +- Needed to differentiate from `2.2.0` to avoid patch conflicts. + +## 2.2.0 + +- Add ability to specify protocol version when initializing VMLogic. +- Add implicit account creation logic for a new protocol version. +- Add `unc-runtime-utils` crate that is shared across runtime crates. + +## 2.1.1 + +- Bumped `borsh` dependency from `0.7.0` to `0.7.1` [#3238](https://github.com/utnet-org/utility/pull/3238) + +## 2.1.0 + +- Implemented better compiler testing with `--compile-only` flag [#3074](https://github.com/utnet-org/utility/pull/3074) +- Added proper `wasmtime` support with `--vm-kind` flag. +- Added `lightbeam` with feature and run with `--vm-kind wasmtime` + +## 2.0.0 + +- Same as `1.2.0`, but since it contained incompatible change, we had to bump major version. + +## 1.2.0 + +- Implement gas profiler [#3038](https://github.com/utnet-org/utility/pull/3038) + +## 1.1.0 + +- Move `sha256`, `keccak256` and `keccak512` form External trait to logic, since they are pure functions. [#3030](https://github.com/utnet-org/utility/issues/3030) + +## 1.0.0 + +- Bumped `borsh` dependency from `0.6.2` to `0.7.0` [#2878](https://github.com/utnet-org/utility/pull/2878) +- Added fees for cost of loading and compiling contract [#2845](https://github.com/utnet-org/utility/pull/2845) +- Added `executor_id` to `ExecutionOutcome` [#2903](https://github.com/utnet-org/utility/pull/2903) +- Select default VM in the compile time, not runtime. [#2897](https://github.com/utnet-org/utility/pull/2897) +- Move to Wasmer 0.17.1 [#2905](https://github.com/utnet-org/utility/pull/2905) +- Fix cost overflow in tx_cost computation [#2915](https://github.com/utnet-org/utility/pull/2915) +- Added FAQ on Wasm integration [#2917](https://github.com/utnet-org/utility/pull/2917) diff --git a/runtime/runtime-params-estimator/.gitignore b/runtime/runtime-params-estimator/.gitignore new file mode 100644 index 000000000..def79b54a --- /dev/null +++ b/runtime/runtime-params-estimator/.gitignore @@ -0,0 +1,4 @@ +unc-sdk-rs/ +core-contracts/ +test-contract/res/stable_* +test-contract/res/nightly_* diff --git a/runtime/runtime-params-estimator/Cargo.toml b/runtime/runtime-params-estimator/Cargo.toml new file mode 100644 index 000000000..bd6a80cbd --- /dev/null +++ b/runtime/runtime-params-estimator/Cargo.toml @@ -0,0 +1,97 @@ +[package] +name = "runtime-params-estimator" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[[bin]] +name = "runtime-params-estimator" +required-features = ["costs_counting"] + +[dependencies] +anyhow.workspace = true +borsh.workspace = true +bs58.workspace = true +bytesize.workspace = true +cfg-if.workspace = true +chrono.workspace = true +clap.workspace = true +enum-map.workspace = true +hex.workspace = true +indicatif.workspace = true +libc.workspace = true +num-rational.workspace = true +num-traits.workspace = true +rand.workspace = true +rand_xorshift.workspace = true +rocksdb.workspace = true +serde_json.workspace = true +tempfile.workspace = true +tracing-span-tree.workspace = true +tracing-subscriber.workspace = true +tracing.workspace = true +wat.workspace = true + +genesis-populate.workspace = true +unc-chain-configs.workspace = true +unc-crypto.workspace = true +unc-fmt.workspace = true +unc-o11y.workspace = true +unc-parameters = { workspace = true, features = ["clap"] } +unc-primitives.workspace = true +unc-store.workspace = true +unc-test-contracts.workspace = true +unc-vm-runner.workspace = true +framework.workspace = true +node-runtime.workspace = true + +[dev-dependencies] +insta.workspace = true + +[features] +default = ["costs_counting"] +costs_counting = ["unc-vm-runner/costs_counting"] +# Required feature for proper config, but can't be enabled by default because it is leaked to other release crates. +required = [ + "costs_counting", + "unc-vm-runner/no_cpu_compatibility_checks", + "no_cache", +] +no_cache = [ + "node-runtime/no_cache", + "unc-store/no_cache", +] +nightly = [ + "nightly_protocol", + "genesis-populate/nightly", + "unc-chain-configs/nightly", + "unc-fmt/nightly", + "unc-o11y/nightly", + "unc-parameters/nightly", + "unc-primitives/nightly", + "unc-store/nightly", + "unc-vm-runner/nightly", + "framework/nightly", + "node-runtime/nightly", +] +nightly_protocol = [ + "genesis-populate/nightly_protocol", + "unc-chain-configs/nightly_protocol", + "unc-fmt/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-parameters/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", + "unc-vm-runner/nightly_protocol", + "framework/nightly_protocol", + "node-runtime/nightly_protocol", +] +sandbox = ["node-runtime/sandbox"] +io_trace = ["unc-store/io_trace", "unc-o11y/io_trace", "unc-vm-runner/io_trace"] diff --git a/runtime/runtime-params-estimator/README.md b/runtime/runtime-params-estimator/README.md new file mode 100644 index 000000000..b1293f023 --- /dev/null +++ b/runtime/runtime-params-estimator/README.md @@ -0,0 +1,84 @@ +# Runtime Parameters Estimator + +Use this tool to measure the running time of elementary runtime operations that have associated fees. + +1. Run the estimator + ```bash + cargo run --release --package runtime-params-estimator --features required --bin runtime-params-estimator -- --accounts-num 20000 --additional-accounts-num 200000 --iters 1 --warmup-iters 1 --metric time + ``` + + With the given parameters above estimator will run relatively fast. + Note the `--metric time` flag: it instructs the estimator to use wall-clock time for estimation, which is quick, but highly variable between runs and physical machines. + To get more robust estimates, use these arguments: + + ```bash + --accounts-num 20000 --additional-accounts-num 200000 --iters 1 --warmup-iters 1 \ + --docker --metric icount + ``` + + This will run and build the estimator inside a docker container, using QEMU to precisely count the number of executed instructions. + + We will be using different parameters to do the actual parameter estimation. + The instructions in [`emu-cost/README.md`](./emu-cost/README.md) should be followed to get the real data. + +2. The result of the estimator run is the `costs-$timestamp$.txt` file, which contains human-readable representation of the costs. + It can be compared with `costs.txt` file in the repository, which contains the current costs we are using. + Note that, at the moment, `costs.txt` is *not* the source of truth. + Rather, the costs are hard-codded in the `Default` impl for `RuntimeConfig`. + You can run `cargo run --package runtime-params-estimator --bin runtime-params-estimator -- --costs-file costs.txt` to convert cost table into `RuntimeConfig`. + +3. **Continuous Estimation**: Take a look at [`estimator-warehouse/README.md`](./estimator-warehouse/README.md) to learn about the automated setup around the parameter estimator. + +Note, if you use the plotting functionality you would need to install [gnuplot](http://gnuplot.info/) to see the graphs. + +## Replaying IO traces + +Compiling `uncd` with `--features=io_trace` and then running it with +`--record-io-trace=my_trace.log` produces a trace of all storage and database +accesses. This trace can be replayed by the estimator. For now only to get +statistics. But the plan is that it will also give gas estimations based on +replaying traces. + +Example: +``` +cargo run -p runtime-params-estimator -- replay my_trace.log cache-stats + GET 193 Block 193 BlockHeader 101 BlockHeight 100 BlockInfo 2 BlockMisc + 11 CachedContractCode 98 ChunkExtra 95 Chunks 4 EpochInfo + 98 IncomingReceipts 30092 State + SET 1 CachedContractCode + DB GET 30987 requests for a total of 391093512 B + DB SET 1 requests for a total of 10379357 B + STORAGE READ 153001 requests for a total of 2523227 B + STORAGE WRITE 151412 requests for a total of 2512012 B + TRIE NODES 8878276 /375708 /27383 (chunk-cache/shard-cache/DB) + SHARD CACHE 93.21% hit rate, 93.21% if removing 15 too large nodes from total + ACCOUNTING CACHE 95.66% hit rate, 99.69% if removing 375708 shard cache hits from total +``` + +For a list of all options, run `cargo run -p runtime-params-estimator -- replay --help`. + +### IO trace tests + +The test input files `./res/*.io_trace` have been generated based on real mainnet traffic. + +```bash +cargo build --release -p uncd --features=io_trace +for shard in 0 1 2 3 +do + target/release/uncd \ + --record-io-trace=75220100-75220101.s${shard}.io_trace view-state \ + apply-range --start-index 75220100 --end-index 75220101 \ + --sequential --shard-id ${shard} +done +``` + +When running these command, make sure to run with `sequential` and to disable +prefetching is disabled, or else the the replaying modes that match requests to +receipts will not work properly. + +```js +// config.json + "store": { + "enable_receipt_prefetching": false + } +``` diff --git a/runtime/runtime-params-estimator/compiler.sh b/runtime/runtime-params-estimator/compiler.sh new file mode 100755 index 000000000..129fe12f6 --- /dev/null +++ b/runtime/runtime-params-estimator/compiler.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +./setup.sh + +VMKIND="wasmer" +features="required" + +if [ "$1" == "wasmtime" ]; then + VMKIND="$1"; + features="$features" +fi +if [ "$1" == "lightbeam" ]; then + VMKIND="wasmtime" + features="$features,lightbeam" +fi + + + +set -ex + +cargo build --release --package runtime-params-estimator --features $features +./emu-cost/counter_plugin/qemu-x86_64 -cpu Westmere-v1 -plugin file=./emu-cost/counter_plugin/libcounter.so ../../target/release/runtime-params-estimator --compile-only --vm-kind "$VMKIND" diff --git a/runtime/runtime-params-estimator/costs.txt b/runtime/runtime-params-estimator/costs.txt new file mode 100644 index 000000000..1baed02df --- /dev/null +++ b/runtime/runtime-params-estimator/costs.txt @@ -0,0 +1,65 @@ +ActionReceiptCreation 216_119_000_000 +DataReceiptCreationBase 72_973_464_625 +DataReceiptCreationPerByte 34_424_023 +ActionCreateAccount 199_214_750_000 +ActionDeployContractBase 369_531_500_000 +ActionDeployContractPerByte 13_625_998 +ActionFunctionCallBase 4_639_723_000_000 +ActionFunctionCallPerByte 4_471_868 +ActionTransfer 230_246_125_000 +ActionStake 204_435_250_000 +ActionAddFullAccessKey 203_530_250_000 +ActionAddFunctionAccessKeyBase 204_435_250_000 +ActionAddFunctionAccessKeyPerByte 3_850_662 +ActionDeleteKey 189_893_250_000 +ActionDeleteAccount 294_978_000_000 +HostFunctionCall 88_256_037 +WasmInstruction 1_285_457 +ReadMemoryBase 869_954_400 +ReadMemoryByte 1_267_111 +WriteMemoryBase 934_598_287 +WriteMemoryByte 907_924 +ReadRegisterBase 839_055_062 +ReadRegisterByte 32_854 +WriteRegisterBase 955_174_162 +WriteRegisterByte 1_267_188 +Utf8DecodingBase 1_037_259_687 +Utf8DecodingByte 97_193_493 +Utf16DecodingBase 1_181_104_350 +Utf16DecodingByte 54_525_831 +Sha256Base 1_513_656_750 +Sha256Byte 8_039_117 +Keccak256Base 1_959_830_425 +Keccak256Byte 7_157_035 +Keccak512Base 1_937_129_412 +Keccak512Byte 12_216_567 +Ripemd160Base 284_558_362 +Ripemd160Block 226_702_528 +EcrecoverBase 92_940_662_819 +Ed25519VerifyBase 40_311_888_867 +Ed25519VerifyByte 423_978_605 +LogBase 1_181_104_350 +LogByte 4_399_597 +StorageWriteBase 21_398_912_000 +StorageWriteKeyByte 23_494_289 +StorageWriteValueByte 10_339_513 +StorageWriteEvictedByte 10_705_769 +StorageReadBase 18_785_615_250 +StorageReadKeyByte 10_317_511 +StorageReadValueByte 1_870_335 +StorageRemoveBase 17_824_343_500 +StorageRemoveKeyByte 12_740_128 +StorageRemoveRetValueByte 3_843_852 +StorageHasKeyBase 18_013_298_875 +StorageHasKeyByte 10_263_615 +TouchingTrieNode 5_367_318_642 +PromiseAndBase 488_337_800 +PromiseAndPerPromise 1_817_392 +PromiseReturn 186_717_462 +AltBn128G1MultiexpBase 237_668_976_500 +AltBn128G1MultiexpByte 1_111_697_487 +AltBn128G1MultiexpSublinear 1_441_698 +AltBn128PairingCheckBase 3_228_502_967_000 +AltBn128PairingCheckByte 8_858_396_182 +AltBn128G1SumBase 1_058_438_125 +AltBn128G1SumByte 25_406_181 diff --git a/runtime/runtime-params-estimator/emu-cost/Dockerfile b/runtime/runtime-params-estimator/emu-cost/Dockerfile new file mode 100644 index 000000000..c141cdd2b --- /dev/null +++ b/runtime/runtime-params-estimator/emu-cost/Dockerfile @@ -0,0 +1,10 @@ +# our local base image +FROM rust:1.75.0 + +LABEL description="Container for builds" + +RUN rustup target add wasm32-unknown-unknown + +# install build dependencies for QEMU +RUN apt-get update && apt-get install -y g++ rsync zip openssh-server \ + make apt-utils git sudo pkg-config libglib2.0-dev curl clang gdb llvm-dev cmake diff --git a/runtime/runtime-params-estimator/emu-cost/README.md b/runtime/runtime-params-estimator/emu-cost/README.md new file mode 100644 index 000000000..fe599444b --- /dev/null +++ b/runtime/runtime-params-estimator/emu-cost/README.md @@ -0,0 +1,132 @@ +# Exact gas price estimator + +## Theory of operations + + Operation execution cost (aka gas cost) is computed basing on the number of userland x86 instructions required to perform the +particular operation in current NEAR runtime implementation. To compute this cost, we use instrumented QEMU binary +translating engine to execute required operations in the userland Linux simulator. +Thus, to measure the execution cost we have to compile NEAR runtime benchmark for Linux, execute the benchmark under +instrumented QEMU running in Docker, and count how many x86 instructions are executed between start and end of execution. + + Instrumentation of QEMU is implemented in the following manner. We install instrumentation callback which conditionally increments +the instruction counter on every instruction during translation by QEMU's JIT, TCG. We activate counting when specific Linux syscall +(currently, 0 aka sys_read) is executed with the certain arguments (file descriptor argument == 0xcafebabe or 0xcafebabf). +On start event we clear instruction counter, on stop event we stop counting and return counted instructions into the buffer provided +to read syscall. As result, NEAR benchmark will know the exact instruction counter passed between two moments and this value +is the pure function of Docker image used, Rust compiler version and the NEAR implementation and is fully reproducible. + +## Usage + +We build and run the cost estimator in the Docker container to make sure config is fully reproducible. +Please make sure that Docker is given at least 4G of RAM, as running under emulator is rather resource consuming. +Note that for Mac the limit is configured in the desktop client, and default value most likely will be very low. + +First fetch appropriate base image, with `docker pull rust`. +Then create a Docker image with `build.sh`, it will create a Docker image with additional build deps. + +Set `HOST_DIR` environment variable to local folder where relevant sources are present. +It will be mounted under `/host` in the Docker container. + +Start container and build estimator with: + + host> ./run.sh + docker> cd /host/framework + docker> cd /host/framework/runtime/runtime-params-estimator + docker> pushd ./test-contract && ./build.sh && popd + docker> cargo build --release --package runtime-params-estimator --features required + +Now start the estimator under QEMU with the counter plugin enabled (note, that Rust compiler produces SSE4, so specify recent CPU): + + docker> ./emu-cost/counter_plugin/qemu-x86_64 -cpu Westmere-v1 -plugin file=./emu-cost/counter_plugin/libcounter.so \ + ../../target/release/runtime-params-estimator --accounts-num 20000 --additional-accounts-num 200000 --iters 1 --warmup-iters 1 + +### Notes + +* Estimation may take some time, as we execute instrumented code under the binary translator. + +* You may observe tangible differences between instructions number got by `params-estimator` and the actual number of instructions executed by production nodes. + This is explained by the LTO (Link Time Optimization) which is disabled by default for release builds to reduce compilation time. + To get better results, enable LTO via environment variable: + + CARGO_PROFILE_RELEASE_LTO=fat + CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1 + export CARGO_PROFILE_RELEASE_LTO CARGO_PROFILE_RELEASE_CODEGEN_UNITS + + See [#4678](https://github.com/utnet-org/utility/issues/4678) for more details. + +* You also may observe slight differences in different launches, because number of instructions operating with disk cache is not fully determined, as well as weight of RocksDB operations. + To improve estimation, you can launch it several times and take the worst result. + +## IO cost calibration + +We need to calibrate IO operations cost to instruction counts. Technically instruction count and IO costs are orthogonal, +however, as we measure our gas in instructions, we have to compute abstract scaling coefficients binding +the number of bytes read/written in IO to instructions executed. +We do that by computing following operation: + + ./emu-cost/counter_plugin/qemu-x86_64 -d plugin -cpu Westmere-v1 -plugin file=./emu-cost/counter_plugin/libcounter.so \ + ../../target/release/genesis-populate --home /tmp/data --additional-accounts-num + +and checking how much data to be read/written depending on number of create accounts. +Then we could figure out: + * 1 account creation cost in instructions + * 1 account creation cost in bytes read and written +For example, experiments performed in mid Oct 2020 shown the following numbers: +10M accounts: + * 6_817_684_914_212 instructions executed + * 168_398_590_013 bytes read + * 48_486_537_178 bytes written + +Thus 1 account approximately costs: + * 681_768 instructions executed + * 16840 bytes read + * 4849 bytes written + +Let's presume that execution, read and write each takes following shares in account cost creation. + * Execution: *3/6* + * Read: *2/6* + * Write: *1/6* + +Then we could conclude that: + * 1 byte read costs 681768 * 2 / 3 / 16840 = 27 instructions + * 1 byte written costs 681768 * 1 / 3 / 4849 = 47 instructions + +Thus, when measuring costs we set the operation cost to be: + + cost = number_of_instructions + bytes_read * 27 + bytes_written * 47 + +## Optional: re-building QEMU and the instruction counter plugin + +We ship prebuilt QEMU and TCG instruction counter plugin, so in many cases one doesn't have to build it. +However, in case you still want to build it - use the following steps. + +Important: we build QEMU and the TCG plugin inside the container, so execute following commands inside Docker. +Set environment variable HOST_DIR (on the host) to location where both QEMU and framework source code is checked +out, it will be mounted as `/host` inside the Docker container. +Start container with: + + ./run.sh + +To build QEMU use: + + cd /host/qemu + ./configure --disable-system --enable-user --enable-plugins --prefix=/host/qemu-linux --target-list=x86_64-linux-user + make && make install + +Then build and test the QEMU's JIT plugin: + + cd /host/framework/runtime/runtime-params-estimator/emu-cost/counter_plugin + cp /host/qemu-linux/bin/qemu-x86_64 ./ + make QEMU_DIR=/host/qemu + make test + +To execute commands in already running container first find its id with: + + > docker ps + + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + e9dcb52cc91b ubuntu-emu "/usr/bin/env bash" 2 hours ago Up 2 hours 0.0.0.0:5000->22/tcp reverent_carson + +and the use container ID for `docker exec` command, like: + + docker exec -it e9dcb52cc91b /host/qemu-linux/bin/qemu-x86_64 -d plugin -plugin file=/host/qemu-linux/plugins/libcounter.so /host/framework/runtime/runtime-params-estimator/emu-cost/counter_plugin/test_binary diff --git a/runtime/runtime-params-estimator/emu-cost/build.sh b/runtime/runtime-params-estimator/emu-cost/build.sh new file mode 100755 index 000000000..547b8db8f --- /dev/null +++ b/runtime/runtime-params-estimator/emu-cost/build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +docker build -t rust-emu . diff --git a/runtime/runtime-params-estimator/emu-cost/counter_plugin/0001-linux-user-fix-page-table-trashing-when-mmap-munmap-.patch b/runtime/runtime-params-estimator/emu-cost/counter_plugin/0001-linux-user-fix-page-table-trashing-when-mmap-munmap-.patch new file mode 100644 index 000000000..421d0fd24 --- /dev/null +++ b/runtime/runtime-params-estimator/emu-cost/counter_plugin/0001-linux-user-fix-page-table-trashing-when-mmap-munmap-.patch @@ -0,0 +1,32 @@ +From 356132111d690a4c9ee74490805e6cfb134dba63 Mon Sep 17 00:00:00 2001 +From: Nikolay Igotti +Date: Sun, 19 Apr 2020 21:31:41 +0300 +Subject: [PATCH 1/3] linux-user: fix page table trashing when mmap/munmap + called frequently on large regions + +Some applications, for example Wasmer WebAssembly VM, perform frequent map/unmap of +huge (6G) regions, so when executed under linux-user it leads to creation of many PTE/PDE +for the region, and they never get reclaimed. As result, emulator process consumes a lot +of RAM. To fix this problem we try to reuse VMA, when possible. + +Signed-off-by: Nikolay Igotti +--- + linux-user/mmap.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/linux-user/mmap.c b/linux-user/mmap.c +index e378033797..c1d6163d7a 100644 +--- a/linux-user/mmap.c ++++ b/linux-user/mmap.c +@@ -650,6 +650,8 @@ int target_munmap(abi_ulong start, abi_ulong len) + if (ret == 0) { + page_set_flags(start, start + len, 0); + tb_invalidate_phys_range(start, start + len); ++ if (start < mmap_next_start) ++ mmap_next_start = start; + } + mmap_unlock(); + return ret; +-- +2.24.2 (Apple Git-127) + diff --git a/runtime/runtime-params-estimator/emu-cost/counter_plugin/0002-linux-user-strace-better-format-mmap-logs-support-mr.patch b/runtime/runtime-params-estimator/emu-cost/counter_plugin/0002-linux-user-strace-better-format-mmap-logs-support-mr.patch new file mode 100644 index 000000000..46d2e9285 --- /dev/null +++ b/runtime/runtime-params-estimator/emu-cost/counter_plugin/0002-linux-user-strace-better-format-mmap-logs-support-mr.patch @@ -0,0 +1,102 @@ +From 70f9186e30be4af206fe89f882a80bb914eb2f2a Mon Sep 17 00:00:00 2001 +From: Nikolay Igotti +Date: Sun, 19 Apr 2020 21:41:04 +0300 +Subject: [PATCH 2/3] linux-user: strace: better format mmap logs, support + mremap + +On 64-bit platforms strace entries were not properly formatted, also some addresses were printed as integers. +Also mremap syscall support is added. + +Signed-off-by: Nikolay Igotti +--- + linux-user/strace.c | 31 +++++++++++++++++++++++++++---- + linux-user/strace.list | 2 +- + 2 files changed, 28 insertions(+), 5 deletions(-) + +diff --git a/linux-user/strace.c b/linux-user/strace.c +index 0d9095c674..3e65ffe356 100644 +--- a/linux-user/strace.c ++++ b/linux-user/strace.c +@@ -969,6 +969,14 @@ UNUSED static struct flags mmap_flags[] = { + FLAG_END, + }; + ++#ifdef TARGET_NR_mremap ++UNUSED static struct flags mremap_flags[] = { ++ FLAG_GENERIC(MREMAP_MAYMOVE), ++ FLAG_GENERIC(MREMAP_FIXED), ++ FLAG_END, ++}; ++#endif ++ + UNUSED static struct flags clone_flags[] = { + FLAG_GENERIC(CLONE_VM), + FLAG_GENERIC(CLONE_FS), +@@ -2654,11 +2662,11 @@ print_mmap(const struct syscallname *name, + { + print_syscall_prologue(name); + print_pointer(arg0, 0); +- print_raw_param("%d", arg1, 0); ++ print_raw_param("%lld", (long long)arg1, 0); + print_flags(mmap_prot_flags, arg2, 0); + print_flags(mmap_flags, arg3, 0); + print_raw_param("%d", arg4, 0); +- print_raw_param("%#x", arg5, 1); ++ print_raw_param("%#llx", (long long)arg5, 1); + print_syscall_epilogue(name); + } + #define print_mmap2 print_mmap +@@ -2672,12 +2680,27 @@ print_mprotect(const struct syscallname *name, + { + print_syscall_prologue(name); + print_pointer(arg0, 0); +- print_raw_param("%d", arg1, 0); ++ print_raw_param("%lld", (long long)arg1, 0); + print_flags(mmap_prot_flags, arg2, 1); + print_syscall_epilogue(name); + } + #endif + ++#ifdef TARGET_NR_mremap ++static void ++print_mremap(const struct syscallname *name, ++ abi_long arg0, abi_long arg1, abi_long arg2, ++ abi_long arg3, abi_long arg4, abi_long arg5) ++{ ++ print_syscall_prologue(name); ++ print_pointer(arg0, 0); ++ print_raw_param("%lld", (long long)arg1, 0); ++ print_raw_param("%lld", (long long)arg2, 0); ++ print_flags(mremap_flags, arg3, 1); ++ print_syscall_epilogue(name); ++} ++#endif ++ + #ifdef TARGET_NR_munmap + static void + print_munmap(const struct syscallname *name, +@@ -2686,7 +2709,7 @@ print_munmap(const struct syscallname *name, + { + print_syscall_prologue(name); + print_pointer(arg0, 0); +- print_raw_param("%d", arg1, 1); ++ print_raw_param("%lld", (long long)arg1, 1); + print_syscall_epilogue(name); + } + #endif +diff --git a/linux-user/strace.list b/linux-user/strace.list +index d49a1e92a8..b3bd1c6229 100644 +--- a/linux-user/strace.list ++++ b/linux-user/strace.list +@@ -612,7 +612,7 @@ + { TARGET_NR_mq_unlink, "mq_unlink" , NULL, print_mq_unlink, NULL }, + #endif + #ifdef TARGET_NR_mremap +-{ TARGET_NR_mremap, "mremap" , NULL, NULL, NULL }, ++{ TARGET_NR_mremap, "mremap" , NULL, print_mremap, print_syscall_ret_addr }, + #endif + #ifdef TARGET_NR_msgctl + { TARGET_NR_msgctl, "msgctl" , NULL, NULL, NULL }, +-- +2.24.2 (Apple Git-127) + diff --git a/runtime/runtime-params-estimator/emu-cost/counter_plugin/0003-plugins-avoid-failing-plugin-when-CPU-is-inited-seve.patch b/runtime/runtime-params-estimator/emu-cost/counter_plugin/0003-plugins-avoid-failing-plugin-when-CPU-is-inited-seve.patch new file mode 100644 index 000000000..6dfe72d27 --- /dev/null +++ b/runtime/runtime-params-estimator/emu-cost/counter_plugin/0003-plugins-avoid-failing-plugin-when-CPU-is-inited-seve.patch @@ -0,0 +1,36 @@ +From 40a54f4e8f17266137ed50a4831d48409a4d5f3d Mon Sep 17 00:00:00 2001 +From: Nikolay Igotti +Date: Sun, 19 Apr 2020 21:43:34 +0300 +Subject: [PATCH 3/3] plugins: avoid failing plugin when CPU is inited several + times + +In linux-user multithreaded scenarious CPU could be inited many times with the same id, +so avoid assertions on already present hashtable entry. + +Signed-off-by: Nikolay Igotti +--- + plugins/core.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/plugins/core.c b/plugins/core.c +index 51bfc94787..889cc6441a 100644 +--- a/plugins/core.c ++++ b/plugins/core.c +@@ -196,13 +196,10 @@ plugin_register_cb_udata(qemu_plugin_id_t id, enum qemu_plugin_event ev, + + void qemu_plugin_vcpu_init_hook(CPUState *cpu) + { +- bool success; +- + qemu_rec_mutex_lock(&plugin.lock); + plugin_cpu_update__locked(&cpu->cpu_index, NULL, NULL); +- success = g_hash_table_insert(plugin.cpu_ht, &cpu->cpu_index, ++ g_hash_table_insert(plugin.cpu_ht, &cpu->cpu_index, + &cpu->cpu_index); +- g_assert(success); + qemu_rec_mutex_unlock(&plugin.lock); + + plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INIT); +-- +2.24.2 (Apple Git-127) + diff --git a/runtime/runtime-params-estimator/emu-cost/counter_plugin/Makefile b/runtime/runtime-params-estimator/emu-cost/counter_plugin/Makefile new file mode 100644 index 000000000..14e6c5681 --- /dev/null +++ b/runtime/runtime-params-estimator/emu-cost/counter_plugin/Makefile @@ -0,0 +1,34 @@ + +# compile with +# make QEMU_DIR=/host/qemu && cp libcounter.so /host/qemu-linux/plugins/ +# run with +# /host/qemu-linux/bin/qemu-x86_64 -d plugin -plugin file=/host/qemu-linux/plugins/libcounter.so,arg="on_every_close" /bin/ls + +CFLAGS += -fPIC +CFLAGS += -I$(QEMU_DIR)/include/qemu -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include +# COUNTER_PLUGIN = /host/framework/runtime/runtime-params-estimator/emu-cost/counter_plugin/libcounter.so +#QEMU=/host/qemu-linux/bin/qemu-x86_64 +QEMU=./qemu-x86_64 +COUNTER_PLUGIN=./libcounter.so + +all: libcounter.so + +%o.: %.c + $(CC) $(CFLAGS) -fPIC $^ -o $@ + +libcounter.so: counter.o + $(CC) -shared -Wl,-soname,$@ -o $@ $^ + +test_binary: test.o + $(CC) $(CFLAGS) $^ -o $@ -lpthread + +test: test_binary libcounter.so + $(QEMU) -d plugin -plugin file=$(COUNTER_PLUGIN) ./test_binary + +test_per_thread: test_binary libcounter.so + $(QEMU) -d plugin -plugin file=$(COUNTER_PLUGIN),arg="count_per_thread" ./test_binary + +clean: + rm -f *.o *.so ./test_binary + +.PHONY: all clean diff --git a/runtime/runtime-params-estimator/emu-cost/counter_plugin/counter.c b/runtime/runtime-params-estimator/emu-cost/counter_plugin/counter.c new file mode 100644 index 000000000..d578ec04f --- /dev/null +++ b/runtime/runtime-params-estimator/emu-cost/counter_plugin/counter.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +// Files with descriptors after this one are intercepted for instruction counting marks. +#define CATCH_BASE 0xcafebabe + +static uint64_t insn_count = 0; +static uint64_t read_count = 0; +static uint64_t write_count = 0; +static pthread_t counting_for = 0; +static bool counting = false; +static bool count_per_thread = false; +static bool on_every_close = false; + +static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata) +{ + if (!counting) return; + if (!count_per_thread || pthread_self() == counting_for) + __atomic_fetch_add(&insn_count, 1, __ATOMIC_SEQ_CST); +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + size_t n = qemu_plugin_tb_n_insns(tb); + size_t i; + + for (i = 0; i < n; i++) { + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); + + // TODO: do this call only on first insn in bb. + qemu_plugin_register_vcpu_insn_exec_cb( + insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, NULL); + } +} + +static void print_insn_count(void) { + g_autofree gchar *out = g_strdup_printf("executed %" PRIu64 " instructions; " + "%" PRIu64 " bytes read; %" PRIu64 " bytes written\n", + insn_count, read_count, write_count); + qemu_plugin_outs(out); +} + +static void vcpu_syscall(qemu_plugin_id_t id, unsigned int vcpu_index, + int64_t num, uint64_t a1, uint64_t a2, + uint64_t a3, uint64_t a4, uint64_t a5, + uint64_t a6, uint64_t a7, uint64_t a8) +{ + // We put our listener on fd reads in range [CATCH_BASE, CATCH_BASE + 1] + if (num == 0) { // sys_read + switch (a1) + { + case CATCH_BASE + 0: + counting = true; + if (count_per_thread) + counting_for = pthread_self(); + insn_count = 0; + read_count = 0; + write_count = 0; + break; + case CATCH_BASE + 1: { + counting_for = 0; + counting = false; + // If read syscall passes buffer size (third syscall argument) equal to 8 we assume it is interested + // in getting back the actual number of executed instructions. + if (a3 == 8) { + // In case of user emulation in QEMU, addresses are 1:1 translated, so we can tell the caller + // number of executed instructions by just writing into the buffer argument of read. + *(uint64_t*)a2 = insn_count; + } + print_insn_count(); + break; + } + case CATCH_BASE + 2: { + if (a3 == 8) { + *(uint64_t*)a2 = read_count; + } + break; + } + case CATCH_BASE + 3: { + if (a3 == 8) { + *(uint64_t*)a2 = write_count; + } + break; + } + default: + break; + } + } + if (num == 3 && on_every_close) { // sys_close + print_insn_count(); + } +} + +static void vcpu_ret_syscall(qemu_plugin_id_t id, unsigned int vcpu_index, + int64_t num, int64_t ret) +{ + if (counting && ret > 0) { + switch (num) { + case 0: // sys_read + case 17: // sys_pread64 + case 19: // sys_readv + case 295: // sys_preadv + __atomic_fetch_add(&read_count, ret, __ATOMIC_SEQ_CST); + break; + case 1: // sys_write + case 18: // sys_pwrite64 + case 20: // sys_writev + case 296: // sys_pwritev + __atomic_fetch_add(&write_count, ret, __ATOMIC_SEQ_CST); + break; + } + } +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, + int argc, char **argv) +{ + int i; + for (i = 0; i < argc; i++) { + if (!strcmp(argv[i], "started")) { + counting = true; + } + if (!strcmp(argv[i], "on_every_close")) { + on_every_close = true; + //counting_for = pthread_self(); + } + if (!strcmp(argv[i], "count_per_thread")) { + qemu_plugin_outs("count per thread\n"); + count_per_thread = true; + counting_for = pthread_self(); + } + } + + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); + qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall); + qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_ret_syscall); + return 0; +} diff --git a/runtime/runtime-params-estimator/emu-cost/counter_plugin/libcounter.so b/runtime/runtime-params-estimator/emu-cost/counter_plugin/libcounter.so new file mode 100755 index 000000000..4c6b1e40e Binary files /dev/null and b/runtime/runtime-params-estimator/emu-cost/counter_plugin/libcounter.so differ diff --git a/runtime/runtime-params-estimator/emu-cost/counter_plugin/qemu-x86_64 b/runtime/runtime-params-estimator/emu-cost/counter_plugin/qemu-x86_64 new file mode 100755 index 000000000..6fc6edd8a Binary files /dev/null and b/runtime/runtime-params-estimator/emu-cost/counter_plugin/qemu-x86_64 differ diff --git a/runtime/runtime-params-estimator/emu-cost/counter_plugin/test.c b/runtime/runtime-params-estimator/emu-cost/counter_plugin/test.c new file mode 100644 index 000000000..80b0ba357 --- /dev/null +++ b/runtime/runtime-params-estimator/emu-cost/counter_plugin/test.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include +#include + +#define CATCH_BASE 0xcafebabe +#define THREAD_NUM 10 + +static void start_counting() { + char buf; + int rv = read(CATCH_BASE, &buf, 1); + (void)rv; + fprintf(stderr, "start counting\n"); +} + +static void end_counting() { + uint64_t counter = 0; + fprintf(stderr, "end counting\n"); + int rv = read(CATCH_BASE + 1, &counter, sizeof(counter)); + (void)rv; + printf("We got %lld from TCG\n", counter); + counter = 0; + rv = read(CATCH_BASE + 2, &counter, sizeof(counter)); + (void)rv; + printf("%lld bytes read\n", counter); + counter = 0; + rv = read(CATCH_BASE + 3, &counter, sizeof(counter)); + (void)rv; + printf("%lld bytes written\n", counter); +} + +int global = 0; +volatile int started = 0; +volatile int running = 0; + +typedef struct { + int delay; +} ThreadArg; + +int g_fd = 0; +ssize_t g_file_size = 0; + +static void* thread_fn(void* varg) { + ThreadArg* arg = varg; + int i; + ssize_t rv; + char data[1024]; + __sync_fetch_and_add(&started, 1); + while (__sync_fetch_and_add(&started, 0) != THREAD_NUM + 1) {} + + for (i = 0; i < arg->delay * 1000; i++) { + global += i; + } + rv = pread(g_fd, data, sizeof(data), arg->delay * 1000); + fprintf(stderr, "rv1=%d\n", (int)rv); + rv = pwrite(g_fd, data, sizeof(data), arg->delay * 2000 + 4096); + fprintf(stderr, "rv2=%d\n", (int)rv); + free(arg); + return NULL; +} + +int main(int argc, char** argv) { + int i; + int repeat = 100; + pthread_t threads[THREAD_NUM]; + char block[4096]; + + if (argc > 1) { + repeat = atoi(argv[1]); + } + + g_fd = open("/tmp/data_file", O_RDWR | O_CREAT); + memset(block, 0xff, sizeof(block)); + for (i = 0; i < 1000; i++) { + g_file_size += write(g_fd, block, sizeof(block)); + } + + for (i = 0; i < THREAD_NUM; i++) { + ThreadArg* arg = calloc(sizeof(ThreadArg), 1); + arg->delay = i * 100; + pthread_create(threads + i, NULL, thread_fn, arg); + } + + while (__sync_fetch_and_add(&started, 0) < THREAD_NUM) {} + start_counting(); + __sync_fetch_and_add(&started, 1); + + for (i = 0; i < repeat; i++) { + global += i; + } + + for (i = 0; i < THREAD_NUM; i++) { + pthread_join(threads[i], NULL); + } + + close(g_fd); + + end_counting(); + + return 0; +} \ No newline at end of file diff --git a/runtime/runtime-params-estimator/emu-cost/data_builder.py b/runtime/runtime-params-estimator/emu-cost/data_builder.py new file mode 100755 index 000000000..f07e838f3 --- /dev/null +++ b/runtime/runtime-params-estimator/emu-cost/data_builder.py @@ -0,0 +1,20 @@ +import re +import sys + +re1 = re.compile('Using (\d+) accounts') +re2 = re.compile('executed (\d+) instructions; (\d+) bytes read; (\d+) bytes written') + +accounts = 0 +interesting = [ 1000000 ] + +for line in sys.stdin: + m = re1.match(line) + if m: + accounts = int(m.group(1)) + m = re2.match(line) + if m and accounts in interesting: + insn = m.group(1) + read = m.group(2) + written = m.group(3) + print insn, "r"+str(accounts), read + print insn, "w"+str(accounts), written diff --git a/runtime/runtime-params-estimator/emu-cost/io_cost.sh b/runtime/runtime-params-estimator/emu-cost/io_cost.sh new file mode 100755 index 000000000..c59b854f2 --- /dev/null +++ b/runtime/runtime-params-estimator/emu-cost/io_cost.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +log=./io_log.txt +base=. +mark=`date +"%Y-%m-%d-%H-%M-%S"` + +echo $mark | tee -a $log + +# cargo build --release --package uncd +# cargo build --release --package genesis-populate + + +acc=1000000 +echo "Using $acc accounts..." | tee -a $log +dir=/tmp/data$acc +rm -rf $dir +$base/../../target/release/uncd --home $dir init \ + --test-seed=alice.near --account-id=test.near --fast +$base/emu-cost/counter_plugin/qemu-x86_64 -d plugin -cpu Westmere-v1 -R 8G \ + -plugin file=$base/emu-cost/counter_plugin/libcounter.so,arg="started",arg="on_every_close" \ + $base/../../target/release/genesis-populate --home $dir --additional-accounts-num $acc 2>&1 | tee -a $log +rm -rf $dir + +# brew install feedgnuplot +# seq 20 | awk '{print $1, A, $1*$1}' | feedgnuplot --domain --dataid --lines --points --title "Instructions to IO" --unset grid --terminal 'dumb 80,40' --exit +# cat $log | python ./emu-cost/data_builder.py | feedgnuplot --domain --dataid --lines --points --title "Instructions to IO" --unset grid --autolegend diff --git a/runtime/runtime-params-estimator/emu-cost/run.sh b/runtime/runtime-params-estimator/emu-cost/run.sh new file mode 100755 index 000000000..2308eb2d6 --- /dev/null +++ b/runtime/runtime-params-estimator/emu-cost/run.sh @@ -0,0 +1,6 @@ +#!/bin/sh +docker run \ + --rm --mount type=bind,source=$HOST_DIR,target=/host \ + --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \ + -i -t rust-emu \ + /usr/bin/env bash diff --git a/runtime/runtime-params-estimator/estimate.sh b/runtime/runtime-params-estimator/estimate.sh new file mode 100755 index 000000000..e0dbdd87d --- /dev/null +++ b/runtime/runtime-params-estimator/estimate.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +./setup.sh + +vmkind="wasmer" +features="required" + +if [[ ! -z "$1" ]]; then + features="$features,$1" + if [[ "$1" == *"wasmtime"* || "$1" == *"lightbeam"* ]]; then + vmkind="wasmtime"; + fi +fi + +rm -rf /tmp/data + +set -ex + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +cd "${script_dir}"/../../ +cargo run --release --package uncd --bin uncd -- --home /tmp/data init --test-seed=alice.near --account-id=test.near --fast +cd "${script_dir}"/../../genesis-tools/genesis-populate +cargo run --release --package genesis-populate --bin genesis-populate -- --additional-accounts-num=10000000 --home /tmp/data +cd "${script_dir}" +cargo build --release --package runtime-params-estimator --features $features +./emu-cost/counter_plugin/qemu-x86_64 -cpu Westmere-v1 -plugin file=./emu-cost/counter_plugin/libcounter.so ../../target/release/runtime-params-estimator --home /tmp/data --accounts-num 1000000 --iters 1 --warmup-iters 0 --warmup-transactions 0 --vm-kind $vmkind + diff --git a/runtime/runtime-params-estimator/estimator-warehouse/.gitignore b/runtime/runtime-params-estimator/estimator-warehouse/.gitignore new file mode 100644 index 000000000..9b1dffd90 --- /dev/null +++ b/runtime/runtime-params-estimator/estimator-warehouse/.gitignore @@ -0,0 +1 @@ +*.sqlite diff --git a/runtime/runtime-params-estimator/estimator-warehouse/Cargo.toml b/runtime/runtime-params-estimator/estimator-warehouse/Cargo.toml new file mode 100644 index 000000000..dd4d0ca28 --- /dev/null +++ b/runtime/runtime-params-estimator/estimator-warehouse/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "estimator-warehouse" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +chrono.workspace = true +clap.workspace = true +nix.workspace = true +reqwest.workspace = true +rusqlite.workspace = true +serde.workspace = true +serde_json.workspace = true +tempfile.workspace = true +xshell.workspace = true + +[dev-dependencies] +insta.workspace = true diff --git a/runtime/runtime-params-estimator/estimator-warehouse/README.md b/runtime/runtime-params-estimator/estimator-warehouse/README.md new file mode 100644 index 000000000..d21a57a95 --- /dev/null +++ b/runtime/runtime-params-estimator/estimator-warehouse/README.md @@ -0,0 +1,75 @@ +# Runtime Parameter Estimator Warehouse + +A wrapper application around a SQLite database. SQLite uses a single file to store it's data and only requires minimal tools to be installed. + +The warehouse acts as middleman between the output of the parameter estimator and analytic tools that work with the data. + +Type `cargo run -- help` for an up-to-date list of available commands and their documentation. + +## Examples + +### estimator-warehouse import + +``` +$ target/release/runtime-params-estimator --json-output --metric time --iters 5 --warmup-iters 1 --costs WriteMemoryBase \ + | target/release/estimator-warehouse import --commit-hash `git rev-parse HEAD` +``` + +### estimator-warehouse stats + +``` +$ cargo run -- --db $SQLI_DB stats + +========================= Warehouse statistics ========================= + + metric records last updated + ------ ------- ------------ + icount 163 2022-03-23 15:50:58 + time 48 2022-03-23 11:14:00 + parameter 0 never + +============================== END STATS =============================== +``` + +### estimator-warehouse check + +``` +$ cargo run -- --db $SQLI_DB check --metric time +RelativeChange(RelativeChange { estimation: "WriteMemoryBase", before: 191132060000.0, after: 130098178000.0 }) +``` + +# Continuous Estimation + +This folder contains some scripts for automated parameter estimation and tracking of the results. + +## How can I observe results? + +1. Check [Zulip # pagoda/contract-runtime/ce](https://near.zulipchat.com/#narrow/stream/319057-pagoda.2Fcontract-runtime.2Fce) for significant changes in gas cost estimations on the master branch. +1. Browse [near.github.io/parameter-estimator-reports](https://near.github.io/parameter-estimator-reports) for a history of gas cost estimations and how it compares to protocol parameters. + +## Understanding the Data flow + +1. The estimator produces JSON output with gas costs and extra details. +1. JSON output is fed to the `estimator-warehouse`, which is a wrapper around an SQLite database file. This file is stored as a buildkite artifact. +1. The estimator-warehouse pushes notifications to Zulip. +1. (TODO[jakmeier]) The estimator-warehouse pushes JSON reports to near/parameter-estimator-reports. +1. (TODO[jakmeier]) A vanilla JavaScript frontend at reads the JSON files hosted by GitHub pages and displays them at [near.github.io/parameter-estimator-reports](https://near.github.io/parameter-estimator-reports). + +## Running in CI + +TODO[jakmeier]: Install a daily buildkite job and document the necessary steps to prepare the full environment. + +## Running locally + +Use `cargo run -- estimate` to run estimations on the current version in your working directory. +Then use [estimator-warehouse](../estimator-warehouse) to interact with the data. + +## Configuration + +The script running estimations can be configured to use where it should store the estimated data, where + +* SQLI_DB="/path/to/db.sqlite" +* ESTIMATOR_unc_HOME="/path/to/near/home" + * Use this if a persistent near state should be used. Useful for testing with large stores. But make sure the deployed test contracts are up-to-date. +* REPO_UNDER_TEST="/path/to/another/repository" + * If you want to run the estimator on a repository clone other than the current directory. Useful to run estimation on older commits, which do not have the continuous estimation scripts. diff --git a/runtime/runtime-params-estimator/estimator-warehouse/src/check.rs b/runtime/runtime-params-estimator/estimator-warehouse/src/check.rs new file mode 100644 index 000000000..31fa62f7b --- /dev/null +++ b/runtime/runtime-params-estimator/estimator-warehouse/src/check.rs @@ -0,0 +1,266 @@ +use crate::db::{Db, EstimationRow}; +use crate::zulip::{ZulipEndpoint, ZulipReport}; +use crate::Metric; +use std::collections::BTreeSet; + +#[derive(clap::Parser, Debug)] +pub(crate) struct CheckConfig { + /// Send notifications from checks to specified stream. + /// Notifications are sent iff stream or user is set. + #[clap(long)] + zulip_stream: Option, + /// Send notifications from checks to specified Zulip user ID. + /// Notifications are sent iff stream or user is set. + #[clap(long)] + zulip_user: Option, + /// Checks have to be done on one specific metric. + #[clap(long, value_enum)] + metric: Metric, + /// First git commit hash used for comparisons, used as base to calculate + /// the relative changes. If left unspecified, the two commits that were + /// inserted most recently are compared. + #[clap(long)] + commit_before: Option, + /// Second git commit hash used for comparisons. If left unspecified, the + /// two commits that were inserted most recently are compared. + #[clap(long)] + commit_after: Option, + /// Names of estimations that should be checked. Leave empty to perform + /// comparison on all available estimations. + #[clap(long)] + estimations: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub(crate) enum Status { + Ok = 0, + Warn = 1, + // Critical = 2, +} + +#[derive(Debug, PartialEq)] +pub(crate) enum Notice { + RelativeChange(RelativeChange), + UncertainChange(UncertainChange), +} + +#[derive(Debug, PartialEq)] +pub(crate) struct RelativeChange { + pub estimation: String, + pub before: f64, + pub after: f64, +} + +#[derive(Debug, PartialEq)] +pub(crate) struct UncertainChange { + pub estimation: String, + pub before: String, + pub after: String, +} + +pub(crate) fn check(db: &Db, config: &CheckConfig) -> anyhow::Result<()> { + let report = create_report(db, config)?; + + // This is the check command output to observe directly in the terminal. + for change in report.changes() { + println!("{change:?}"); + } + + let zulip_receiver = { + if let Some(user) = config.zulip_user { + Some(ZulipEndpoint::to_user(user)?) + } else if let Some(stream) = &config.zulip_stream { + Some(ZulipEndpoint::to_stream(stream.clone())?) + } else { + None + } + }; + + if let Some(zulip) = zulip_receiver { + zulip.post(&report)?; + } + Ok(()) +} + +pub(crate) fn create_report(db: &Db, config: &CheckConfig) -> anyhow::Result { + let (commit_after, commit_before) = match (&config.commit_after, &config.commit_before) { + (Some(a), Some(b)) => (a.clone(), b.clone()), + (None, None) => { + let mut commits = EstimationRow::commits_sorted_by_date(db, Some(config.metric))?; + if commits.len() < 2 { + anyhow::bail!("need data for at least 2 commits to perform comparison"); + } + (commits.pop().unwrap().0, commits.pop().unwrap().0) + } + _ => anyhow::bail!("you have to either specify both commits for comparison or neither"), + }; + let estimations = if !config.estimations.is_empty() { + config.estimations.clone() + } else { + let rows_a = EstimationRow::select_by_commit_and_metric(db, &commit_after, config.metric)?; + let rows_b = EstimationRow::select_by_commit_and_metric(db, &commit_before, config.metric)?; + let estimations_a = rows_a.into_iter().map(|row| row.name).collect::>(); + let estimations_b = rows_b.into_iter().map(|row| row.name).collect::>(); + estimations_a.intersection(&estimations_b).cloned().collect() + }; + let warnings = + estimation_changes(db, &estimations, &commit_before, &commit_after, 0.1, config.metric)?; + + let warnings_uncertain = estimation_uncertain_changes( + db, + &estimations, + &commit_before, + &commit_after, + config.metric, + )?; + + let mut report = ZulipReport::new(commit_before, commit_after); + for warning in warnings { + report.add(warning, Status::Warn) + } + for warning in warnings_uncertain { + report.add(warning, Status::Warn) + } + Ok(report) +} + +fn estimation_changes( + db: &Db, + estimation_names: &[String], + commit_before: &str, + commit_after: &str, + tolerance: f64, + metric: Metric, +) -> anyhow::Result> { + let mut warnings = Vec::new(); + for name in estimation_names { + let b = &EstimationRow::get(db, name, commit_before, metric)?[0]; + let a = &EstimationRow::get(db, name, commit_after, metric)?[0]; + let rel_change = (b.gas - a.gas).abs() / b.gas; + if rel_change > tolerance { + warnings.push(Notice::RelativeChange(RelativeChange { + estimation: name.clone(), + before: b.gas, + after: a.gas, + })) + } + } + + Ok(warnings) +} + +fn estimation_uncertain_changes( + db: &Db, + estimation_names: &[String], + commit_before: &str, + commit_after: &str, + metric: Metric, +) -> anyhow::Result> { + let mut warnings = Vec::new(); + for name in estimation_names { + let b = EstimationRow::get(db, name, commit_before, metric)?.remove(0); + let a = EstimationRow::get(db, name, commit_after, metric)?.remove(0); + match (b.uncertain_reason, a.uncertain_reason) { + (None, None) => continue, + (Some(uncertain_before), None) => { + add_warning(&mut warnings, name.clone(), uncertain_before, "None".to_owned()) + } + (None, Some(uncertain_after)) => { + add_warning(&mut warnings, name.clone(), "None".to_owned(), uncertain_after) + } + (Some(uncertain_before), Some(uncertain_after)) => { + add_warning(&mut warnings, name.clone(), uncertain_before, uncertain_after); + } + } + } + + Ok(warnings) +} + +fn add_warning(warnings: &mut Vec, name: String, before: String, after: String) { + warnings.push(Notice::UncertainChange(UncertainChange { estimation: name, before, after })) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[track_caller] + fn generate_test_report(input: &str, metric: Metric, estimations: &[&str]) -> ZulipReport { + let db = Db::test_with_data(input); + let config = CheckConfig { + zulip_stream: None, + zulip_user: None, + metric, + commit_before: None, + commit_after: None, + estimations: estimations.iter().map(|&s| s.to_owned()).collect(), + }; + create_report(&db, &config).unwrap() + } + + #[test] + fn test_check_command() { + let input_a = r#" + 0000a + {"computed_in":{"nanos":800,"secs":44},"name":"LogBase","result":{"gas":1000000000.0,"instructions":8000.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + {"computed_in":{"nanos":809,"secs":26},"name":"LogByte","result":{"gas":1000000000.0,"instructions":8000.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + + 0001a + {"computed_in":{"nanos":814,"secs":9},"name":"LogBase","result":{"gas":2000000000.0,"instructions":16000.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + {"computed_in":{"nanos":694,"secs":33},"name":"LogByte","result":{"gas":1002000000.0,"instructions":8016.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + + 0002a + {"computed_in":{"nanos":331,"secs":24},"name":"LogBase","result":{"gas":3000000000.0,"instructions":24000.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + {"computed_in":{"nanos":511,"secs":52},"name":"LogByte","result":{"gas":1004000000.0,"instructions":8032.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + + 0003a + {"computed_in":{"nanos":633,"secs":7},"name":"LogBase","result":{"gas":4000000000.0,"instructions":32000.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + {"computed_in":{"nanos":173,"secs":2},"name":"LogByte","result":{"gas":1006000000.0,"instructions":8048.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + {"computed_in":{"nanos":633,"secs":7},"name":"UncertainTest","result":{"gas":4000000000.0,"instructions":32000.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":"NEGATIVE-COST"}} + {"computed_in":{"nanos":655,"secs":56},"name":"LogByte","result":{"gas":20000000.0,"time_ns":20,"metric":"time","uncertain_reason":null}} + + 0004a + {"computed_in":{"nanos":319,"secs":19},"name":"LogBase","result":{"gas":5000000000.0,"instructions":40000.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + {"computed_in":{"nanos":527,"secs":15},"name":"LogByte","result":{"gas":1008000000.0,"instructions":8064.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + {"computed_in":{"nanos":633,"secs":7},"name":"UncertainTest","result":{"gas":4000000000.0,"instructions":32000.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":"HIGH-VARIANCE"}} + {"computed_in":{"nanos":633,"secs":7},"name":"UncertainTest2","result":{"gas":4000000000.0,"instructions":32000.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + {"computed_in":{"nanos":661,"secs":11},"name":"LogByte","result":{"gas":15000000.0,"time_ns":15,"metric":"time","uncertain_reason":null}} + {"computed_in":{"nanos":661,"secs":11},"name":"LogByte","result":{"gas":15000000.0,"time_ns":15,"metric":"time","uncertain_reason":null}} + {"computed_in":{"nanos":0,"secs":0},"name":"AltBn128Sum","result":{"gas":0.0,"time_ns":0,"metric":"time","uncertain_reason":null}} + {"computed_in":{"nanos":0,"secs":0},"name":"AltBn128MultiExp","result":{"gas":0.0,"time_ns":0,"metric":"time","uncertain_reason":null}} + "#; + + // Only "LogBase" changes enough to show up in report. + let report = generate_test_report(input_a, Metric::ICount, &[]); + insta::assert_snapshot!(report.to_string()); + + // Add more data and verify the notifications are updated. + let input_b = input_a.to_owned() + + r#" + + WAIT + + 0000b + {"computed_in":{"nanos":119,"secs":46},"name":"LogBase","result":{"gas":6000000000.0,"instructions":48000.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + {"computed_in":{"nanos":372,"secs":12},"name":"LogByte","result":{"gas":7000000000.0,"instructions":56000.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + {"computed_in":{"nanos":633,"secs":7},"name":"UncertainTest","result":{"gas":4000000000.0,"instructions":32000.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + {"computed_in":{"nanos":633,"secs":7},"name":"UncertainTest2","result":{"gas":4000000000.0,"instructions":32000.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":"BLOCK-MEASUREMENT-OVERHEAD"}} + {"computed_in":{"nanos":262,"secs":15},"name":"LogByte","result":{"gas":20000000.0,"time_ns":20,"metric":"time","uncertain_reason":null}} + {"computed_in":{"nanos":0,"secs":0},"name":"AltBn128Sum","result":{"gas":0.0,"time_ns":0,"metric":"time","uncertain_reason":null}} + {"computed_in":{"nanos":0,"secs":0},"name":"AltBn128MultiExp","result":{"gas":10.0,"time_ns":10,"metric":"time","uncertain_reason":null}} + "#; + + // Now both estimations have changed. + let report = generate_test_report(&input_b, Metric::ICount, &[]); + insta::assert_snapshot!(report.to_string()); + + // Verify that filter for specific estimations works. + let report = generate_test_report(&input_b, Metric::ICount, &["LogBase"]); + insta::assert_snapshot!(report.to_string()); + + // Filter for metric. + let report = generate_test_report(&input_b, Metric::Time, &[]); + insta::assert_snapshot!(report.to_string()); + } +} diff --git a/runtime/runtime-params-estimator/estimator-warehouse/src/db.rs b/runtime/runtime-params-estimator/estimator-warehouse/src/db.rs new file mode 100644 index 000000000..396eb80f2 --- /dev/null +++ b/runtime/runtime-params-estimator/estimator-warehouse/src/db.rs @@ -0,0 +1,248 @@ +//! Database connection and struct representing rows in the data tables. + +use std::path::Path; + +use chrono::NaiveDateTime; +use rusqlite::{params, Connection, Row}; + +use crate::Metric; + +/// Wrapper around database connection +pub(crate) struct Db { + conn: Connection, +} + +impl Db { + pub(crate) fn new(conn: Connection) -> Self { + Self { conn } + } + + /// Opens an existing SQLite Db or creates it + pub(crate) fn open(path: &Path) -> anyhow::Result { + let conn = Connection::open(path)?; + let init_sql = include_str!("init.sql"); + conn.execute_batch(init_sql)?; + Ok(Self::new(conn)) + } +} + +/// A single data row in the estimation table +#[derive(Debug, PartialEq)] +pub(crate) struct EstimationRow { + /// Name of the estimation / parameter + pub name: String, + /// The estimation result converted to gas units + pub gas: f64, + /// Parameter for which this estimation is used + pub parameter: Option, + /// The estimated time in nanoseconds, (if time-based estimation) + pub wall_clock_time: Option, + /// The number of operations counted (if icount-based estimation) + pub icount: Option, + /// The number of IO read bytes counted (if icount-based estimation) + pub io_read: Option, + /// The number of IO write bytes counted (if icount-based estimation) + pub io_write: Option, + /// For measurements that had some kind of inaccuracies or problems + pub uncertain_reason: Option, + /// Which git commit this has been estimated on + pub commit_hash: String, +} + +/// A single data row in the parameter table +#[derive(Debug, PartialEq)] +pub(crate) struct ParameterRow { + /// Name of the estimation / parameter + pub name: String, + /// The estimation result converted to gas units + pub gas: f64, + /// Protocol version for which the parameter is valid + pub protocol_version: u32, +} + +impl EstimationRow { + const SELECT_ALL: &'static str = + "name,gas,parameter,wall_clock_time,icount,io_read,io_write,uncertain_reason,commit_hash"; + pub fn get(db: &Db, name: &str, commit: &str, metric: Metric) -> anyhow::Result> { + Ok(Self::get_any_metric(db, name, commit)? + .into_iter() + .filter(|row| row.is_metric(metric)) + .collect()) + } + pub fn get_any_metric(db: &Db, name: &str, commit: &str) -> anyhow::Result> { + let select = Self::SELECT_ALL; + let mut stmt = db.conn.prepare(&format!( + "SELECT {select} FROM estimation WHERE name = ?1 AND commit_hash = ?2;" + ))?; + let data = stmt + .query_map([name, commit], Self::from_row)? + .collect::, rusqlite::Error>>()?; + Ok(data) + } + pub(crate) fn insert(&self, db: &Db) -> anyhow::Result<()> { + db.conn.execute( + "INSERT INTO estimation(name,gas,parameter,wall_clock_time,icount,io_read,io_write,uncertain_reason,commit_hash) values (?1,?2,?3,?4,?,?6,?7,?8,?9)", + params![ + self.name, + self.gas, + self.parameter, + self.wall_clock_time, + self.icount, + self.io_read, + self.io_write, + self.uncertain_reason, + self.commit_hash, + ], + )?; + Ok(()) + } + pub fn select_by_commit_and_metric( + db: &Db, + commit: &str, + metric: Metric, + ) -> anyhow::Result> { + let select = Self::SELECT_ALL; + let metric_condition = metric.condition(); + let mut stmt = db.conn.prepare(&format!( + "SELECT {select} FROM estimation WHERE commit_hash = ?1 AND {metric_condition};" + ))?; + let data = stmt + .query_map([commit], Self::from_row)? + .collect::, rusqlite::Error>>()?; + Ok(data) + } + + /// Returns one (commit_hash,date) tuple for each commit in store, + /// optionally filtered by estimation metric. The output is sorted by the + /// date, in ascending order. Note that the date is not the committed-date + /// but instead the earliest date associated with an estimation of that + /// commit, as recorded in the warehouse. This is usually the date the + /// estimation has been performed. + pub fn commits_sorted_by_date( + db: &Db, + metric: Option, + ) -> anyhow::Result> { + let metric_condition = match metric { + Some(m) => format!("WHERE {}", m.condition()), + None => "".to_owned(), + }; + let sql = format!("SELECT commit_hash,min(date) FROM estimation {metric_condition} GROUP BY commit_hash ORDER BY date ASC;"); + let mut stmt = db.conn.prepare(&sql)?; + let data = stmt + .query_map([], |row| Ok((row.get::<_, String>(0)?, row.get::<_, NaiveDateTime>(1)?)))? + .collect::, rusqlite::Error>>()?; + Ok(data) + } + pub fn count_by_metric(db: &Db, metric: Metric) -> anyhow::Result { + let sql = match metric { + Metric::ICount => "SELECT COUNT(*) FROM estimation WHERE icount IS NOT NULL;", + Metric::Time => "SELECT COUNT(*) FROM estimation WHERE wall_clock_time IS NOT NULL;", + }; + let count = db.conn.query_row::(sql, [], |row| row.get(0))?; + Ok(count as u64) + } + pub fn last_updated(db: &Db, metric: Metric) -> anyhow::Result> { + let sql = match metric { + Metric::ICount => "SELECT MAX(date) FROM estimation WHERE icount IS NOT NULL;", + Metric::Time => "SELECT MAX(date) FROM estimation WHERE wall_clock_time IS NOT NULL;", + }; + let dt = db.conn.query_row::, _, _>(sql, [], |row| row.get(0))?; + Ok(dt) + } + fn is_metric(&self, metric: Metric) -> bool { + match metric { + Metric::ICount => self.icount.is_some(), + Metric::Time => self.icount.is_none() && self.wall_clock_time.is_some(), + } + } + fn from_row(row: &Row) -> rusqlite::Result { + Ok(Self { + name: row.get(0)?, + gas: row.get(1)?, + parameter: row.get(2)?, + wall_clock_time: row.get(3)?, + icount: row.get(4)?, + io_read: row.get(5)?, + io_write: row.get(6)?, + uncertain_reason: row.get(7)?, + commit_hash: row.get(8)?, + }) + } +} + +impl ParameterRow { + pub fn count(db: &Db) -> anyhow::Result { + let sql = "SELECT COUNT(*) FROM parameter;"; + let count = db.conn.query_row::(sql, [], |row| row.get(0))?; + Ok(count) + } + pub fn latest_protocol_version(db: &Db) -> anyhow::Result> { + let sql = "SELECT MAX(protocol_version) FROM parameter;"; + let max = db.conn.query_row::, _, _>(sql, [], |row| row.get(0))?; + Ok(max) + } +} + +impl Metric { + fn condition(&self) -> &'static str { + match self { + Metric::ICount => "icount IS NOT NULL", + Metric::Time => "wall_clock_time IS NOT NULL", + } + } +} + +#[cfg(test)] +mod tests { + use super::Db; + use crate::import::ImportConfig; + use chrono::{NaiveDate, NaiveDateTime}; + use rusqlite::{functions::FunctionFlags, Connection}; + + impl Db { + /// Create a new in-memory test database with a mocked `datetime()` function. + pub(crate) fn test() -> Self { + let conn = Connection::open_in_memory().unwrap(); + let init_sql = include_str!("init.sql"); + conn.execute_batch(init_sql).unwrap(); + let db = Self::new(conn); + db.mock_time( + NaiveDate::from_ymd_opt(2015, 5, 15).unwrap().and_hms_opt(11, 22, 33).unwrap(), + ); + db + } + + /// Create a new in-memory test database with data defined by the input. + /// + /// The test data is expected to come in blocks of JSON lines with a commit header. + /// WAIT statements can be used as barriers to make sure DB timestamps differ. + pub(crate) fn test_with_data(input: &str) -> Self { + let db = Self::test(); + for block in input.split("\n\n") { + if block.trim() == "WAIT" { + // Update mocked time to ensure the following data is considered to be later. + let mocked_now: NaiveDateTime = + db.conn.query_row("SELECT datetime('now')", [], |r| r.get(0)).unwrap(); + db.mock_time(mocked_now + chrono::Duration::seconds(1)); + } else { + let (commit_hash, input) = block.split_once("\n").unwrap(); + let conf = ImportConfig { + commit_hash: Some(commit_hash.to_string()), + protocol_version: None, + }; + db.import_json_lines(&conf, input).unwrap(); + } + } + db + } + + pub(crate) fn mock_time(&self, dt: NaiveDateTime) { + let string_rep = dt.format("%Y-%m-%d %H:%M:%S").to_string(); + self.conn + .create_scalar_function("datetime", 1, FunctionFlags::SQLITE_UTF8, move |_ctx| { + Ok(string_rep.clone()) + }) + .unwrap(); + } + } +} diff --git a/runtime/runtime-params-estimator/estimator-warehouse/src/estimate.rs b/runtime/runtime-params-estimator/estimator-warehouse/src/estimate.rs new file mode 100644 index 000000000..5b7df65c9 --- /dev/null +++ b/runtime/runtime-params-estimator/estimator-warehouse/src/estimate.rs @@ -0,0 +1,144 @@ +use crate::{db::Db, import::ImportConfig}; +use nix::unistd::Uid; +use xshell::{cmd, Shell}; + +/// Additional information required for estimation. +#[derive(Debug, clap::Parser)] +pub(crate) struct EstimateConfig { + /// Specify the directory of a different repository, if not estimating the current one. + #[clap(long)] + pub external_repo: Option, + /// Specify the directory for near state used by the estimator. Will use + /// temporary directory if unspecified. + #[clap(long)] + pub home: Option, + /// Comma separated list of metrics to use in estimation. + #[clap(long, default_value = "icount,time", value_parser(["icount", "time"]), use_value_delimiter = true)] + pub metrics: Vec, + /// Bundle of estimator config options. + #[clap(long, value_enum, default_value_t = Mode::Default)] + pub mode: Mode, +} + +/// The mode selects a pre-selected bundle of config options for the estimator. +/// +/// Pick one of these modes when running the estimator through the warehouse +/// scripts. For more control, such as choosing exactly which estimations to run +/// or skip, run the estimator directly without the warehouse script. +#[derive(Debug, Clone, Copy, PartialEq, clap::ValueEnum)] +pub(crate) enum Mode { + /// Standardized config options for a full estimation. + Default, + /// Single iteration with fastest possible config options. + Fast, +} + +pub(crate) fn run_estimation(db: &Db, config: &EstimateConfig) -> anyhow::Result<()> { + let sh = Shell::new()?; + + let mut _maybe_tmp = None; + + let estimator_home = match &config.home { + Some(home) => home, + None => { + _maybe_tmp = Some(tempfile::tempdir()?); + _maybe_tmp.as_ref().unwrap().path().to_str().unwrap() + } + }; + + if let Some(external_repo) = &config.external_repo { + sh.change_dir(external_repo); + } + let git_root = cmd!(sh, "git rev-parse --show-toplevel").read()?; + + // Ensure full optimization + let _env_guard_one = sh.push_env("CARGO_PROFILE_RELEASE_LTO", "fat"); + let _env_guard_two = sh.push_env("CARGO_PROFILE_RELEASE_CODEGEN_UNITS", "1"); + + // Build estimator + let cargo_profile = config.mode.cargo_profile(); + cmd!(sh, "cargo build --profile {cargo_profile} -p runtime-params-estimator --features runtime-params-estimator/required").run()?; + // Find binary, some users have CARGO_TARGET_DIR pointing to a custom target directory + let estimator_binary = if let Ok(target_dir) = sh.var("CARGO_TARGET_DIR") { + format!("{target_dir}/{cargo_profile}/runtime-params-estimator") + } else { + format!("{git_root}/target/{cargo_profile}/runtime-params-estimator") + }; + + // Actual estimations + let output = cmd!(sh, "git rev-parse HEAD").output()?; + let mut commit_hash = String::from_utf8_lossy(&output.stdout).to_string(); + commit_hash.pop(); // \n + let iters = config.mode.iters(); + let warmup_iters = config.mode.warmup_iters(); + + if config.metrics.iter().any(|m| m == "time") { + let mut optional_args = vec![]; + + #[cfg(target_family = "unix")] + if Uid::effective().is_root() { + optional_args.push("--drop-os-cache"); + } else { + eprintln!("Running as non-root, storage related costs might be inaccurate because OS caches cannot be dropped"); + } + + optional_args.append(&mut config.mode.optional_args_time_metric()); + + let estimation_output = + cmd!(sh, + "{estimator_binary} --iters {iters} --warmup-iters {warmup_iters} --json-output --home {estimator_home} {optional_args...} --metric time" + ).read()?; + db.import_json_lines( + &ImportConfig { commit_hash: Some(commit_hash.clone()), protocol_version: None }, + &estimation_output, + )?; + } + + if config.metrics.iter().any(|m| m == "icount") { + let estimation_output = + cmd!(sh, + "{estimator_binary} --iters {iters} --warmup-iters {warmup_iters} --json-output --home {estimator_home} --metric icount --docker" + ).read()?; + db.import_json_lines( + &ImportConfig { commit_hash: Some(commit_hash), protocol_version: None }, + &estimation_output, + )?; + } + + Ok(()) +} + +impl Mode { + fn iters(self) -> &'static str { + match self { + Mode::Default => "5", + Mode::Fast => "1", + } + } + + fn warmup_iters(self) -> &'static str { + match self { + Mode::Default => "1", + Mode::Fast => "0", + } + } + + fn cargo_profile(self) -> &'static str { + match self { + Mode::Default => "release", + Mode::Fast => "dev-release", + } + } + + fn optional_args_time_metric(self) -> Vec<&'static str> { + match self { + Mode::Default => vec![], + Mode::Fast => vec![ + "--in-memory-db", + "--additional-accounts-num=1000", + "--accounts-num=1000", + "--accurate=false", + ], + } + } +} diff --git a/runtime/runtime-params-estimator/estimator-warehouse/src/import.rs b/runtime/runtime-params-estimator/estimator-warehouse/src/import.rs new file mode 100644 index 000000000..5036701c4 --- /dev/null +++ b/runtime/runtime-params-estimator/estimator-warehouse/src/import.rs @@ -0,0 +1,160 @@ +use crate::db::{Db, EstimationRow}; +use anyhow::Context; +use std::time::Duration; + +/// Additional information required for import +#[derive(Debug, clap::Parser)] +pub(crate) struct ImportConfig { + /// Required for importing estimation results, which source code commit it + /// should be associated with. + #[clap(long)] + pub commit_hash: Option, + /// Required for importing parameter values, which protocol version it + /// should be associated with. + #[clap(long)] + pub protocol_version: Option, +} + +/// Estimation result as produced by the params-estimator +#[derive(serde::Deserialize, Debug, PartialEq)] +struct EstimatorOutput { + name: String, + result: EstimationResult, + computed_in: Duration, +} +#[derive(serde::Deserialize, Debug, PartialEq)] +struct EstimationResult { + gas: f64, + time_ns: Option, + instructions: Option, + io_r_bytes: Option, + io_w_bytes: Option, + uncertain_reason: Option, +} + +impl Db { + pub(crate) fn import_json_lines(&self, info: &ImportConfig, input: &str) -> anyhow::Result<()> { + for line in input.lines() { + self.import(info, line)?; + } + Ok(()) + } + + fn import(&self, info: &ImportConfig, line: &str) -> anyhow::Result<()> { + if let Ok(estimator_output) = serde_json::from_str::(line) { + let commit_hash = info.commit_hash.as_ref().with_context(|| { + "Missing --commit-hash argument while importing estimation data".to_owned() + })?; + let row = EstimationRow { + name: estimator_output.name, + gas: estimator_output.result.gas, + parameter: None, // TODO + wall_clock_time: estimator_output.result.time_ns, + icount: estimator_output.result.instructions, + io_read: estimator_output.result.io_r_bytes, + io_write: estimator_output.result.io_w_bytes, + uncertain_reason: estimator_output.result.uncertain_reason, + commit_hash: commit_hash.clone(), + }; + row.insert(self)?; + } + Ok(()) + } +} + +#[cfg(test)] +mod test { + use crate::db::{Db, EstimationRow}; + use crate::import::ImportConfig; + use crate::Metric; + + #[test] + fn test_import_time() { + let input = r#" + {"computed_in":{"nanos":826929296,"secs":0},"name":"LogBase","result":{"gas":441061948,"metric":"time","time_ns":441.061948,"uncertain_reason":null}} + {"computed_in":{"nanos":983235753,"secs":0},"name":"LogByte","result":{"gas":2743748,"metric":"time","time_ns":2.7437486640625,"uncertain_reason":"HIGH-VARIANCE"}} + "#; + let expected = [ + EstimationRow { + name: "LogBase".to_owned(), + gas: 441061948.0, + parameter: None, + wall_clock_time: Some(441.061948), + icount: None, + io_read: None, + io_write: None, + uncertain_reason: None, + commit_hash: "53a3ccf3ef07".to_owned(), + }, + EstimationRow { + name: "LogByte".to_owned(), + gas: 2743748.0, + parameter: None, + wall_clock_time: Some(2.7437486640625), + icount: None, + io_read: None, + io_write: None, + uncertain_reason: Some("HIGH-VARIANCE".to_owned()), + commit_hash: "53a3ccf3ef07".to_owned(), + }, + ]; + let info = ImportConfig { + commit_hash: Some("53a3ccf3ef07".to_owned()), + protocol_version: Some(0), + }; + assert_import(input, &info, &expected, Metric::Time); + } + #[test] + fn test_import_icount() { + let input = r#" + {"computed_in":{"nanos":107762511,"secs":17},"name":"ActionReceiptCreation","result":{"gas":240650158750,"instructions":1860478.51,"io_r_bytes":0.0,"io_w_bytes":1377.08,"metric":"icount","uncertain_reason":null}} + {"computed_in":{"nanos":50472,"secs":0},"name":"ApplyBlock","result":{"gas":9059500000,"instructions":71583.0,"io_r_bytes":0.0,"io_w_bytes":19.0,"metric":"icount","uncertain_reason":"HIGH-VARIANCE"}} + "#; + let expected = [ + EstimationRow { + name: "ActionReceiptCreation".to_owned(), + gas: 240650158750.0, + parameter: None, + wall_clock_time: None, + icount: Some(1860478.51), + io_read: Some(0.0), + io_write: Some(1377.08), + uncertain_reason: None, + commit_hash: "53a3ccf3ef07".to_owned(), + }, + EstimationRow { + name: "ApplyBlock".to_owned(), + gas: 9059500000.0, + parameter: None, + wall_clock_time: None, + icount: Some(71583.0), + io_read: Some(0.0), + io_write: Some(19.0), + uncertain_reason: Some("HIGH-VARIANCE".to_owned()), + commit_hash: "53a3ccf3ef07".to_owned(), + }, + ]; + let info = ImportConfig { + commit_hash: Some("53a3ccf3ef07".to_owned()), + protocol_version: Some(0), + }; + assert_import(input, &info, &expected, Metric::ICount); + } + #[track_caller] + fn assert_import( + input: &str, + info: &ImportConfig, + expected_output: &[EstimationRow], + metric: Metric, + ) { + let db = Db::test(); + db.import_json_lines(info, input).unwrap(); + let output = EstimationRow::select_by_commit_and_metric( + &db, + info.commit_hash.as_ref().unwrap(), + metric, + ) + .unwrap(); + assert_eq!(expected_output, output); + } +} diff --git a/runtime/runtime-params-estimator/estimator-warehouse/src/init.sql b/runtime/runtime-params-estimator/estimator-warehouse/src/init.sql new file mode 100644 index 000000000..14a0c8283 --- /dev/null +++ b/runtime/runtime-params-estimator/estimator-warehouse/src/init.sql @@ -0,0 +1,17 @@ +CREATE TABLE IF NOT EXISTS estimation ( + date TEXT NOT NULL DEFAULT (datetime('now')), -- when measurement has been taken + name TEXT NOT NULL, -- enum variant + gas REAL NOT NULL, -- gas cost + parameter TEXT, -- parameter for which this estimation is used (may be null) + wall_clock_time REAL, -- if time based estimation, the wall-clock time measured + icount REAL, -- if icount based estimation, the number of operations counted + io_read REAL, -- if icount based estimation, the number of IO read bytes counted + io_write REAL, -- if icount based estimation, the number of IO write bytes counted + uncertain_reason TEXT DEFAULT NULL, -- set to a non-null value explaining the reason, if the measurment has been marked as uncertain + commit_hash TEXT NOT NULL -- which git commit this has been estimated on +); +CREATE TABLE IF NOT EXISTS parameter ( + name TEXT NOT NULL, -- parameter name as recorded in runtime_config.json + gas REAL NOT NULL, -- gas cost + protocol_version INTEGER -- protocol version for which the parameter is valid +); diff --git a/runtime/runtime-params-estimator/estimator-warehouse/src/main.rs b/runtime/runtime-params-estimator/estimator-warehouse/src/main.rs new file mode 100644 index 000000000..1b4ee8bf2 --- /dev/null +++ b/runtime/runtime-params-estimator/estimator-warehouse/src/main.rs @@ -0,0 +1,167 @@ +use check::{check, CheckConfig}; +use db::{Db, EstimationRow, ParameterRow}; +use estimate::{run_estimation, EstimateConfig}; +use import::ImportConfig; +use std::fmt::Write; +use std::io::{self, Read}; +use std::path::PathBuf; + +mod check; +mod db; +mod estimate; +mod import; +mod zulip; + +#[derive(clap::Parser)] +struct CliArgs { + #[clap(subcommand)] + cmd: SubCommand, + /// File path for either an existing SQLite3 DB or the path where a new DB + /// will be created. + #[clap(long, default_value = "db.sqlite")] + db: PathBuf, +} + +#[derive(clap::Subcommand, Debug)] +enum SubCommand { + /// Call runtime-params-estimator for all metrics and import the results. + Estimate(EstimateConfig), + /// Read estimations in JSON format from STDIN and store it in the warehouse. + Import(ImportConfig), + /// Compares parameters, estimations, and how estimations changed over time. + /// Reports any deviations from the norm to STDOUT. Combine with `--zulip` + /// to send notifications to a Zulip stream + Check(CheckConfig), + /// Prints a summary of the current data in the warehouse. + Stats, +} + +fn main() -> anyhow::Result<()> { + let cli_args: CliArgs = clap::Parser::parse(); + let db = Db::open(&cli_args.db)?; + + match cli_args.cmd { + SubCommand::Estimate(config) => { + run_estimation(&db, &config)?; + } + SubCommand::Import(config) => { + let mut buf = String::new(); + io::stdin().read_to_string(&mut buf)?; + db.import_json_lines(&config, &buf)?; + } + SubCommand::Check(config) => { + check(&db, &config)?; + } + SubCommand::Stats => { + let stats = generate_stats(&db)?; + eprintln!("{stats}"); + } + } + + Ok(()) +} + +#[derive(Debug, Clone, Copy, PartialEq, clap::ValueEnum)] +enum Metric { + #[clap(name = "icount")] + ICount, + Time, +} + +fn generate_stats(db: &Db) -> anyhow::Result { + let mut buf = String::new(); + writeln!(&mut buf)?; + writeln!(&mut buf, "{:=^72}", " Warehouse statistics ")?; + writeln!(&mut buf)?; + writeln!(&mut buf, "{:>24}{:>24}{:>24}", "metric", "records", "last updated")?; + writeln!(&mut buf, "{:>24}{:>24}{:>24}", "------", "-------", "------------")?; + writeln!( + &mut buf, + "{:>24}{:>24}{:>24}", + "icount", + EstimationRow::count_by_metric(db, Metric::ICount)?, + EstimationRow::last_updated(db, Metric::ICount)? + .map(|dt| dt.to_string()) + .as_deref() + .unwrap_or("never") + )?; + writeln!( + &mut buf, + "{:>24}{:>24}{:>24}", + "time", + EstimationRow::count_by_metric(db, Metric::Time)?, + EstimationRow::last_updated(db, Metric::Time)? + .map(|dt| dt.to_string()) + .as_deref() + .unwrap_or("never") + )?; + writeln!( + &mut buf, + "{:>24}{:>24}{:>24}", + "parameter", + ParameterRow::count(db)?, + ParameterRow::latest_protocol_version(db)? + .map(|version| format!("v{version}")) + .as_deref() + .unwrap_or("never") + )?; + writeln!(&mut buf)?; + writeln!(&mut buf, "{:=^72}", " END STATS ")?; + + Ok(buf) +} + +#[cfg(test)] +mod tests { + use super::generate_stats; + use crate::db::Db; + use crate::estimate::EstimateConfig; + use std::path::Path; + + #[test] + fn test_stats() { + let input = r#" + ae7f1cd2 + {"computed_in":{"nanos":222,"secs":1},"name":"LogBase","result":{"gas":1002000000.0,"time_ns":1002,"metric":"time","uncertain_reason":null}} + {"computed_in":{"nanos":671,"secs":18},"name":"LogBase","result":{"gas":1003000000.0,"instructions":8024.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + + be7f1cd2 + {"computed_in":{"nanos":38,"secs":16},"name":"LogBase","result":{"gas":4000000000.0,"instructions":32000.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + {"computed_in":{"nanos":38,"secs":16},"name":"LogBase","result":{"gas":4000000000.0,"time_ns":4000,"metric":"time","uncertain_reason":null}} + {"computed_in":{"nanos":637,"secs":17},"name":"LogByte","result":{"gas":20100000.0,"instructions":160.8,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}} + + ce7f1cd2 + {"computed_in":{"nanos":988,"secs":51},"name":"LogBase","result":{"gas":10000000000.0,"time_ns":10000,"metric":"time","uncertain_reason":null}} + {"computed_in":{"nanos":441,"secs":54},"name":"LogByte","result":{"gas":20000000.0,"instructions":160.0,"io_r_bytes":0.0,"io_w_bytes":0.0,"metric":"icount","uncertain_reason":null}}"#; + + let db = Db::test_with_data(input); + insta::assert_snapshot!(generate_stats(&db).unwrap()); + } + + /// Run a minimal estimation of all parameters to ensure we have no crashes. + /// + /// Things to note: + /// - This re-compiles the estimator (including framework) in dev-release + /// profile because estimations are really slow otherwise. + /// - This is an expensive test. We run it like any other test for now but + /// it might make sense to put it in a separate CI job. + /// - QEMU based estimation is skipped - it would be too slow. + #[test] + fn test_full_estimator() -> anyhow::Result<()> { + let stats_path = Path::new("tmp_db.sqlite"); + let db = Db::open(stats_path)?; + let config = EstimateConfig { + external_repo: None, + home: None, + metrics: vec!["time".to_owned()], + mode: crate::estimate::Mode::Fast, + }; + + crate::estimate::run_estimation(&db, &config)?; + + // cleanup + drop(db); + std::fs::remove_file(stats_path)?; + Ok(()) + } +} diff --git a/runtime/runtime-params-estimator/estimator-warehouse/src/snapshots/estimator_warehouse__check__tests__check_command-2.snap b/runtime/runtime-params-estimator/estimator-warehouse/src/snapshots/estimator_warehouse__check__tests__check_command-2.snap new file mode 100644 index 000000000..35ead4665 --- /dev/null +++ b/runtime/runtime-params-estimator/estimator-warehouse/src/snapshots/estimator_warehouse__check__tests__check_command-2.snap @@ -0,0 +1,19 @@ +--- +source: runtime/runtime-params-estimator/estimator-warehouse/src/check.rs +expression: report.to_string() +--- +## Report +*Status: Warn* +*Current commit: 0000b* +*Compared to: 0004a* +### Relative gas estimation changes above threshold: 2 +``` +LogBase 5.00 Ggas ➜ 6.00 Ggas (+20.00%) +LogByte 1.01 Ggas ➜ 7.00 Ggas (+594.44%) +``` +### Gas estimator uncertain estimations: 2 +``` +UncertainTest HIGH-VARIANCE ➜ None +UncertainTest2 None ➜ BLOCK-MEASUREMENT-OVERHEAD +``` + diff --git a/runtime/runtime-params-estimator/estimator-warehouse/src/snapshots/estimator_warehouse__check__tests__check_command-3.snap b/runtime/runtime-params-estimator/estimator-warehouse/src/snapshots/estimator_warehouse__check__tests__check_command-3.snap new file mode 100644 index 000000000..bd37baacc --- /dev/null +++ b/runtime/runtime-params-estimator/estimator-warehouse/src/snapshots/estimator_warehouse__check__tests__check_command-3.snap @@ -0,0 +1,14 @@ +--- +source: runtime/runtime-params-estimator/estimator-warehouse/src/check.rs +expression: report.to_string() +--- +## Report +*Status: Warn* +*Current commit: 0000b* +*Compared to: 0004a* +### Relative gas estimation changes above threshold: 1 +``` +LogBase 5.00 Ggas ➜ 6.00 Ggas (+20.00%) +``` +### Gas estimator uncertain estimations: 0 + diff --git a/runtime/runtime-params-estimator/estimator-warehouse/src/snapshots/estimator_warehouse__check__tests__check_command-4.snap b/runtime/runtime-params-estimator/estimator-warehouse/src/snapshots/estimator_warehouse__check__tests__check_command-4.snap new file mode 100644 index 000000000..1b1e6ae95 --- /dev/null +++ b/runtime/runtime-params-estimator/estimator-warehouse/src/snapshots/estimator_warehouse__check__tests__check_command-4.snap @@ -0,0 +1,15 @@ +--- +source: runtime/runtime-params-estimator/estimator-warehouse/src/check.rs +expression: report.to_string() +--- +## Report +*Status: Warn* +*Current commit: 0000b* +*Compared to: 0004a* +### Relative gas estimation changes above threshold: 2 +``` +AltBn128MultiExp 0 gas ➜ 10 gas (+inf%) +LogByte 15.00 Mgas ➜ 20.00 Mgas (+33.33%) +``` +### Gas estimator uncertain estimations: 0 + diff --git a/runtime/runtime-params-estimator/estimator-warehouse/src/snapshots/estimator_warehouse__check__tests__check_command.snap b/runtime/runtime-params-estimator/estimator-warehouse/src/snapshots/estimator_warehouse__check__tests__check_command.snap new file mode 100644 index 000000000..74b54ce2a --- /dev/null +++ b/runtime/runtime-params-estimator/estimator-warehouse/src/snapshots/estimator_warehouse__check__tests__check_command.snap @@ -0,0 +1,17 @@ +--- +source: runtime/runtime-params-estimator/estimator-warehouse/src/check.rs +expression: report.to_string() +--- +## Report +*Status: Warn* +*Current commit: 0004a* +*Compared to: 0003a* +### Relative gas estimation changes above threshold: 1 +``` +LogBase 4.00 Ggas ➜ 5.00 Ggas (+25.00%) +``` +### Gas estimator uncertain estimations: 1 +``` +UncertainTest NEGATIVE-COST ➜ HIGH-VARIANCE +``` + diff --git a/runtime/runtime-params-estimator/estimator-warehouse/src/snapshots/estimator_warehouse__tests__stats.snap b/runtime/runtime-params-estimator/estimator-warehouse/src/snapshots/estimator_warehouse__tests__stats.snap new file mode 100644 index 000000000..d321c784a --- /dev/null +++ b/runtime/runtime-params-estimator/estimator-warehouse/src/snapshots/estimator_warehouse__tests__stats.snap @@ -0,0 +1,15 @@ +--- +source: runtime/runtime-params-estimator/estimator-warehouse/src/main.rs +expression: generate_stats(&db).unwrap() +--- + +========================= Warehouse statistics ========================= + + metric records last updated + ------ ------- ------------ + icount 4 2015-05-15 11:22:33 + time 3 2015-05-15 11:22:33 + parameter 0 never + +============================== END STATS =============================== + diff --git a/runtime/runtime-params-estimator/estimator-warehouse/src/zulip.rs b/runtime/runtime-params-estimator/estimator-warehouse/src/zulip.rs new file mode 100644 index 000000000..acb40862d --- /dev/null +++ b/runtime/runtime-params-estimator/estimator-warehouse/src/zulip.rs @@ -0,0 +1,142 @@ +use std::env; + +use anyhow::Context; +use reqwest::blocking::Client; + +use crate::check::{Notice, RelativeChange, Status, UncertainChange}; + +const ZULIP_SERVER: &str = "near.zulipchat.com"; + +pub(crate) struct ZulipEndpoint { + client: Client, + full_endpoint_url: String, + user_list: Option, + stream: Option, +} + +pub(crate) struct ZulipReport { + status: Status, + before: String, + after: String, + changes: Vec, + changes_uncertain: Vec, +} + +impl ZulipEndpoint { + pub(crate) fn to_user(user: u64) -> anyhow::Result { + Ok(Self { + client: Client::new(), + full_endpoint_url: Self::form_url()?, + stream: None, + user_list: Some(format!("[{user}]")), + }) + } + pub(crate) fn to_stream(stream: String) -> anyhow::Result { + Ok(Self { + client: Client::new(), + full_endpoint_url: Self::form_url()?, + stream: Some(stream), + user_list: None, + }) + } + pub(crate) fn post(&self, report: &ZulipReport) -> anyhow::Result<()> { + self.send_raw_message(&report.to_string(), "Bot reports") + } + fn form_url() -> anyhow::Result { + let bot_email = + env::var("ZULIP_BOT_EMAIL").context("ZULIP_BOT_EMAIL environment variable not set")?; + let api_key = env::var("ZULIP_BOT_API_KEY") + .context("ZULIP_BOT_API_KEY environment variable not set")?; + Ok(format!("https://{bot_email}:{api_key}@{ZULIP_SERVER}/api/v1/messages")) + } + fn send_raw_message(&self, msg: &str, topic: &str) -> anyhow::Result<()> { + let params = if let Some(user_list) = &self.user_list { + vec![("type", "private"), ("to", user_list), ("content", msg)] + } else { + vec![ + ("type", "stream"), + ("to", self.stream.as_deref().unwrap()), + ("topic", topic), + ("content", msg), + ] + }; + self.client.post(&self.full_endpoint_url).form(¶ms).send()?; + Ok(()) + } +} + +impl ZulipReport { + pub(crate) fn new(before: String, after: String) -> Self { + Self { status: Status::Ok, before, after, changes: vec![], changes_uncertain: vec![] } + } + pub(crate) fn add(&mut self, warning: Notice, status: Status) { + self.status = std::cmp::max(self.status, status); + match warning { + Notice::RelativeChange(change) => self.changes.push(change), + Notice::UncertainChange(change) => self.changes_uncertain.push(change), + } + } + + pub(crate) fn changes(&self) -> &[RelativeChange] { + self.changes.as_ref() + } +} + +impl std::fmt::Display for ZulipReport { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "## Report ")?; + writeln!(f, "*Status: {:?}*", self.status)?; + writeln!(f, "*Current commit: {}*", self.after)?; + writeln!(f, "*Compared to: {}*", self.before)?; + writeln!(f, "### Relative gas estimation changes above threshold: {}", self.changes.len())?; + if !self.changes.is_empty() { + writeln!(f, "```")?; + for change in &self.changes { + let percent_change = 100.0 * (change.after - change.before) / change.before; + writeln!( + f, + "{:<40} {:>16} ➜ {:>16} ({}{:.2}%)", + change.estimation, + format_gas(change.before), + format_gas(change.after), + if percent_change >= 0.0 { "+" } else { "" }, + percent_change, + )?; + } + writeln!(f, "```")?; + } + writeln!(f, "### Gas estimator uncertain estimations: {}", self.changes_uncertain.len())?; + if !self.changes_uncertain.is_empty() { + writeln!(f, "```")?; + for change in &self.changes_uncertain { + writeln!( + f, + "{:<40} {:>32} ➜ {:<32}", + change.estimation, change.before, change.after, + )?; + } + writeln!(f, "```")?; + } + Ok(()) + } +} + +fn format_gas(gas: f64) -> String { + match gas { + n if n > 1e12 => format!("{:.2} Tgas", n / 1e12), + n if n > 1e9 => format!("{:.2} Ggas", n / 1e9), + n if n > 1e6 => format!("{:.2} Mgas", n / 1e6), + n => format!("{:.0} gas", n), + } +} + +#[test] +fn test_format_gas() { + assert_eq!(format_gas(0.0).as_str(), "0 gas"); + assert_eq!(format_gas(12345.0).as_str(), "12345 gas"); + assert_eq!(format_gas(123e6).as_str(), "123.00 Mgas"); + assert_eq!(format_gas(123.456e9).as_str(), "123.46 Ggas"); + assert_eq!(format_gas(0.456e12).as_str(), "456.00 Ggas"); + assert_eq!(format_gas(123.456e12).as_str(), "123.46 Tgas"); + assert_eq!(format_gas(123.456e15).as_str(), "123456.00 Tgas"); +} diff --git a/runtime/runtime-params-estimator/res/75220100-75220101.s0.io_trace b/runtime/runtime-params-estimator/res/75220100-75220101.s0.io_trace new file mode 100644 index 000000000..e949fb2c7 --- /dev/null +++ b/runtime/runtime-params-estimator/res/75220100-75220101.s0.io_trace @@ -0,0 +1,882 @@ +GET BlockMisc "'GENESIS_JSON_HASH'" size=32 +GET BlockMisc "'GENESIS_STATE_ROOTS'" size=36 +GET EpochInfo "'AGGREGATOR'" size=16400 +GET EpochInfo "`11111111111111111111111111111111`" size=2266 +GET BlockHeight "hMR7BAAAAAA=" size=32 +GET Block "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=7706 +GET EpochInfo "`7XciHBpUitLPSu8QBrsWp6sghvVhwXj7nzjm5FxNZbjn`" size=28482 +GET ChunkExtra "HYEildYa9ga54ruYJ1xPfRDohXpl/k0TpI24NPOuKlQBAAAAAAAAAA==" size=101 +GET Chunks "`GwQfzRngEwz1PeMwuqFPZUFQ8pxtWTMDDLmutfsqyoja`" size=3377 +GET Block "`DwSdhqzB18RJAew3XoMKrjHpHyWJdetAbrKFt2AJgSt8`" size=6796 +GET BlockHeader "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=6293 +GET IncomingReceipts "HYEildYa9ga54ruYJ1xPfRDohXpl/k0TpI24NPOuKlQAAAAAAAAAAA==" size=1645 +GET BlockHeader "`DwSdhqzB18RJAew3XoMKrjHpHyWJdetAbrKFt2AJgSt8`" size=5383 +GET BlockInfo "`DwSdhqzB18RJAew3XoMKrjHpHyWJdetAbrKFt2AJgSt8`" size=221 +GET BlockInfo "`9TxV9KzgAaxqVcQPmh6tce9Dy1XQXWgDNyatRG3HBS9`" size=302 +GET BlockInfo "`FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR`" size=221 +GET EpochInfo "`235gTuzMxiALmnyykDmZmhoK1JKgoNkUyegQMjirTVwM`" size=36184 +apply_transactions shard_id=0 + process_state_update + apply num_transactions=3 shard_cache_miss=2 shard_cache_hit=217 + process_transaction tx_hash=6wbch2CCuvqhbnknr7HhNW9PoyK6FSUmoCGJKpKEahvi shard_cache_hit=2 shard_cache_miss=37 + GET State "AQAAAAAAAAATLGezCY1KS95P514xKfBhX+mHPX5VAUeuSdNlm4/cFw==" size=46 + GET State "AQAAAAAAAADyTkbFaLsnPSDwzabcILRCh4PkXdKk+n08632S1wAT7g==" size=171 + GET State "AQAAAAAAAAD8wWlYUBDQhVTNFBjd2CQzFRH1CL5F9IHMvmLCCqNQOA==" size=75 + GET State "AQAAAAAAAAC4NbaNib4O1tUGxoJXqbKtIysL/+00qsQIN/7UD4GrRg==" size=46 + GET State "AQAAAAAAAAAW92cxEkz4r0pFJgz+/MOw26pA2qAR9XZSB9SyBElmNw==" size=171 + GET State "AQAAAAAAAABQV+BSEmX5FWrJ2BJRl+pK87Ak4RtjNICiYETvwFYRvQ==" size=203 + GET State "AQAAAAAAAAC8Wl4LtWBYGEgLo4SAsGJOZA48nq3Cv1y9xgNjgiLqwA==" size=171 + GET State "AQAAAAAAAADpTZsdECl8JrsX4S9cHY5k2T5OGKNjbFgg5W4psGw/0w==" size=363 + GET State "AQAAAAAAAABbchKhJOCnSLEr+ZDr5M5YxWcD2CJQlJlXNPcPQ+tiTw==" size=171 + GET State "AQAAAAAAAADp4McZOMsuL7mixSnBAtgdp9B+E7CBrdDzAT/zXLXnrg==" size=46 + GET State "AQAAAAAAAAAEhhm/w7FQ9o4eAbuVeWhWszI7A6V1FbnWArKyUyJ5yA==" size=75 + GET State "AQAAAAAAAADJp3SDGRESbgiA4OTf7XK4lBcNDRajkzIFEQSBsUPE0w==" size=235 + GET State "AQAAAAAAAACgV6nlMBCwqqYsZrf1y52D+C8Ktm1Jt/IiYzT89SE3/Q==" size=47 + GET State "AQAAAAAAAABzMdyZT9qdyd++xTMXPTFtZZuqVcqlXSaIzrLFoJUSww==" size=75 + GET State "AQAAAAAAAAAvjRr4+K5oLXazPvcexavtPVdwFn8RYKt5Pp1lS9HP0w==" size=47 + GET State "AQAAAAAAAABFVNmMZMa7EiNawcFbnIVhMoOUHArW8r7QsCSC0cFXDA==" size=79 + GET State "AQAAAAAAAAB2/fvuQdm1pEQlp2FG6S5SdnY4E9+JCocBg5BHRHYdpw==" size=59 + GET State "AQAAAAAAAACseH1rJnasAHNMmr+17XolAHxHoKBYi8TwlkqjjZVHqA==" size=72 + GET State "AQAAAAAAAACk7F5eDaYGy3w+7YYL+tarAYbkXck+1sMDppnqaYe98g==" size=75 + GET State "AQAAAAAAAABgHsfjluVdrrx50lXr/cPJW2YXZEDmBDXXByUHuKjPgQ==" size=46 + GET State "AQAAAAAAAACOAwLpakUk2z6D8vsxodHlQdO7yW/JyNhiRRu/fwY7uA==" size=171 + GET State "AQAAAAAAAABxbAciRb9/Gf4b/EkYo+zVTKVcUMg+9pJeM3Fp+Jj0rw==" size=203 + GET State "AQAAAAAAAAC/7YLpXw25opSj6Z8o5NhJBWY9k8KqJjIIlRok176ViA==" size=171 + GET State "AQAAAAAAAAA7WUnbVW/lV+MiLcsaN6GNH6aTe1VvzkIKcoRlkMjK9A==" size=363 + GET State "AQAAAAAAAAAPV+a7twLsEmAljWwrRmF+0MvAPCV42Gm3fwAx7Wht0A==" size=171 + GET State "AQAAAAAAAABIbsTXdh8mis1KcFHH7DXGdyHkFpAZ5iAWvR0XNs9qTg==" size=46 + GET State "AQAAAAAAAADG60DiK5fFvact5P8fn3DRtqWRkNgf6QAvhWwg5HBcVw==" size=75 + GET State "AQAAAAAAAAAJwIMUja/oH3VbHH7GxKFqmhJnhOWms0OP47F0cZ3bHg==" size=235 + GET State "AQAAAAAAAAD5h9jnOem/NywYy9k8S2CvkV0rG5Sv0ZqVJhZR9+Vxqg==" size=47 + GET State "AQAAAAAAAAAzYuNsae0uysLJDLkeyS30/ijiSgT/cfwriLrsigexxg==" size=75 + GET State "AQAAAAAAAABBIpZXvDiXk8ZomoLUlqzKb22rrDOUkDsVKR/YQIgOkA==" size=47 + GET State "AQAAAAAAAAAoLkbP1pRPAw5Rl9xA/ksTuiGmMHQS2BhxdP+Rzh3m7A==" size=75 + GET State "AQAAAAAAAAAphcEGjuzzKGcNlEUdD+7R7M+E0xuDBcfe8bJBkt6+tg==" size=57 + GET State "AQAAAAAAAACTsXyQXbBGoM4JgBeWYBYQfPbAczAkZeL9/0l8cu51fQ==" size=267 + GET State "AQAAAAAAAADi+A8HNMBq8jXD7vTJEjzPAzc+cSgITGkc1nvuOxJZ+Q==" size=107 + GET State "AQAAAAAAAAABaLK59U9MLpvx1C3eGkKAJnjk+SOyT0sopY1TGz8KMA==" size=81 + GET State "AQAAAAAAAADb3X1s2ydrSNxydYaC5Ep/rO2MXBSdr24Qv7X4+7sd4g==" size=9 + process_transaction tx_hash=8P2uMiA96mwRDnoqzwQijEHc1eFpZNJYJFKxHz69sYgj shard_cache_hit=6 shard_cache_miss=30 + GET State "AQAAAAAAAACs/j41H1HAST5JKWrzUPdM7i4VTVfryepaAdm85DdEpg==" size=331 + GET State "AQAAAAAAAACmkAeXmTk4Gr2am8KketuZ3SpGri1dZQSPI8dDNF7PMA==" size=171 + GET State "AQAAAAAAAADQo9QEBfTPjOoKPi5azQBFDYnsJ/iAsmAm6rDAucfLKw==" size=331 + GET State "AQAAAAAAAAD0hYJm3u1Vs/zk1swbrVpTJVcf1eAgopoTqESFl5KdhA==" size=171 + GET State "AQAAAAAAAACdluWwwb0hE9rp0YNXy0BVJXjRpIdtd9Q5JVsPVepEFA==" size=331 + GET State "AQAAAAAAAAA4JhHzc6/3TGTRMz6p9kmQvyKRjGJKqdsU0QxfCoKiqA==" size=107 + GET State "AQAAAAAAAAAQaY8cHDsIyr9IvfDgNuzCVAy0GUy1o74nN5RLtXbwiw==" size=331 + GET State "AQAAAAAAAAAgeQPjDmE459Oo+hj3ms7Cw0cCA5cXLm4+INy/mR9EMg==" size=75 + GET State "AQAAAAAAAADJfHZhzRWeVF+ZbENP5z/BWEcmdECahODJLZ0NMIXV/w==" size=331 + GET State "AQAAAAAAAAAhF3btI6lDDvFMdi6/ft6DpIk+9iYnAkNy2LHIt8OEVQ==" size=75 + GET State "AQAAAAAAAAD8+C8BAT9aSdaLEa/xUD893TCXo+5p9bNqbjehGf95fg==" size=235 + GET State "AQAAAAAAAAD7rh5ZjA/iEGnV6Kg8VubSml9a7UGp1kULjJyA2wNiJg==" size=46 + GET State "AQAAAAAAAACLY4o10i9EAxockASc506JAWhMvRseaqCIUXW8Kghnnw==" size=75 + GET State "AQAAAAAAAAApXxeJYuUMX0KsCOIMXkJ/e+vyXskiMsMz0zfoXl2jJQ==" size=107 + GET State "AQAAAAAAAADccsARU7u5msVfNniI3nIhlNvM8TdNCtbFnlahohLWAQ==" size=72 + GET State "AQAAAAAAAAC1zv/mk9qhPl1brgvnv1cQUp0IOICKSUd64RuIlzeGYw==" size=331 + GET State "AQAAAAAAAADMGt6TsdXGEpf4zDEKtb1jOaFAPHdIrsg3iHiMYDHhKQ==" size=171 + GET State "AQAAAAAAAACETy8Xjv/Q/LWUbQ0dgj/VzKI2p71z4i0+7AmFTDuO2g==" size=331 + GET State "AQAAAAAAAACwNJxTEqGZ5rzOtqiF+e0vYA8KYtXUU9t+zNI1JoY4Lw==" size=171 + GET State "AQAAAAAAAAAu85eBbiMErqD1Z+bzDA+IE2D3yVfpDEzclX1DaKhKyA==" size=331 + GET State "AQAAAAAAAACEQlDJ66ap11bf+CHmdx9ACCMV9KVLIxgpK/fPn5Nk+A==" size=107 + GET State "AQAAAAAAAADEKq73lRBiotVZlwaamvQ2aNZ6cdpJNYtWCHRHPqk7Rw==" size=331 + GET State "AQAAAAAAAAANbSCilAmVzzjbmeX+0MxvCb3P35voU9uNSflaakDENQ==" size=75 + GET State "AQAAAAAAAADqL7Jj29ngKs4UDeoLyNYMrxZbSGyiooKcm9ynelXQcQ==" size=331 + GET State "AQAAAAAAAAB/fGn6nAG29gfcdPd660ME1l+NKbdFkycOynzuie5cFg==" size=75 + GET State "AQAAAAAAAADsLOS6pnaJfso09bxeYNP/k2KJpqBLm0NNydX0ujl2mQ==" size=235 + GET State "AQAAAAAAAAB1IR6xw2btOhFQP8DOs7gR+DuA/A0VM7GkRHCHjDDV1w==" size=46 + GET State "AQAAAAAAAADRlZkllg05aaidrGpH44zWfH+RcaY33jih6zmFdAFAGA==" size=75 + GET State "AQAAAAAAAABFzBsPyTnkll2uRvVluvvr5CcjWdkgyIgsWy5Y2x2GmQ==" size=141 + GET State "AQAAAAAAAADdR1e7TZ/A870rJ8JUWWFLRL3dSkLUIQ63GxK/Ec/CMA==" size=9 + process_transaction tx_hash=GREvhhJebp73HsJh8dXJYgdMURBPbpMebDti4AVi6UdC shard_cache_hit=18 shard_cache_miss=3 + GET State "AQAAAAAAAACt70gALmi5+k4PGM1GzfCKEWp/SL1szdZzDCnm/6JcMQ==" size=75 + GET State "AQAAAAAAAABUQtq5Gqvd3H6r5yHEL5W61H97xgS90Nl85m2Q7rVZmQ==" size=81 + GET State "AQAAAAAAAAAUpn/3z/eIO2CdsPaMXrGrk8/kLlBlEmyHhzlpvAa1Gg==" size=9 + GET State "AQAAAAAAAADWuoz0zYZ06qLxxA6o3kKBzLSj228kzv1WG9faFc2cJw==" size=50 + GET State "AQAAAAAAAAAyWpC1z/VkabA09TrWe/hdn1LrahZstL/ofg+Zfgc3yw==" size=16 + process_receipt receipt_id=HSkLwmSscYqj6DekmxRTUACkBUAUS7sUVBnxPC2wKVj8 predecessor=app.nearcrowd.near receiver=app.nearcrowd.near id=HSkLwmSscYqj6DekmxRTUACkBUAUS7sUVBnxPC2wKVj8 shard_cache_hit=2 shard_cache_miss=14 shard_cache_too_large=1 + GET State "AQAAAAAAAAAy4zCwPCVo3sXw0bmUMk9iu65jZdFZw9WZFWqVtmdZ9g==" size=75 + GET State "AQAAAAAAAADkYMlNm7akgzW0YkJvXtfEybNgwJYz3H6KVKZU9rd/vg==" size=46 + GET State "AQAAAAAAAAD8s8t+ClfZRgy33skd6cKj3OSOijl95vwhhMj3U1LdIg==" size=171 + GET State "AQAAAAAAAAC49wofkSdy1FzOwp8z+aRWnDrQ0VVRQjJRdtgo9+G4GA==" size=203 + GET State "AQAAAAAAAABvld3Gvd2vNslwaSBgI0y/xilO2XsPbKIsXyONEt3nyw==" size=75 + GET State "AQAAAAAAAACBi68nrWi6YkON4bNh3l8VP46elUNvYxBVpQkFhb/F5g==" size=171 + GET State "AQAAAAAAAAD9E8dsEtSKg8x4jWh/RffQvdo0ZAZHJK3XD1IfpToR+Q==" size=107 + GET State "AQAAAAAAAACjULelSwYt7vgBmFN6SuSa81fGNJgb4AHWPBQkLhD6aw==" size=46 + GET State "AQAAAAAAAAAblZRpl1Iyjd/eBBEHf1D8uY0Eb6vAQ1CTuBU22BIRzw==" size=75 + GET State "AQAAAAAAAADz46bxTVmMEo8Qe864zjjrxr4OOJaIGpIaHvCe1o6uDw==" size=203 + GET State "AQAAAAAAAADi1LDHHWdlLRpy76ja2kHrg59xY52HDnCGHiSFCIbNpw==" size=47 + GET State "AQAAAAAAAAAdubo0MrDO82/s5IFxDgBZ6n+lRliTjzUa5ZWbaGi8Pg==" size=75 + GET State "AQAAAAAAAAC0/qL4L9a+323xIkKZwmCyHYUnJ72R/vJu2a9jEvBX9Q==" size=61 + GET State "AQAAAAAAAADYhUlXpUB2OX3mIikCaQACQF2zorvTUoWtOEnrqd3vEw==" size=541068 + attached_deposit + input + register_len + read_register + storage_read READ key=cAEAAAA= size=160 tn_db_reads=20 tn_mem_reads=0 shard_cache_miss=19 shard_cache_hit=2 + GET State "AQAAAAAAAAAD7xUBS3rJIG3oRw3Ppoy3FlaMnuOTlcW7PesYOeAHgg==" size=75 + GET State "AQAAAAAAAAAl/ahdl3sAiW7HBUq8fdgFGX2ioXSj70du/ZT9JooOyw==" size=46 + GET State "AQAAAAAAAADwvUsGboPmVoeqdpbmlDMDhq7ssgMg+xnZWIJPG1Sh2A==" size=171 + GET State "AQAAAAAAAABaSTMPXoeUqV6rbobaUX8T56hdsVFKfrswdvdm/aJRwg==" size=203 + GET State "AQAAAAAAAAA63w4En4FPNimX+qMr5ani1o97JDti2ackbnRhfvsorg==" size=75 + GET State "AQAAAAAAAAAyQgOHTd9ohhdqWOGYXU4d89TH/1veD7EVsVtieZQpqw==" size=171 + GET State "AQAAAAAAAAC6H+EQdOlyAXCGJJ9XUu/s4L2qCdlhLNeIgJZqgggLbg==" size=107 + GET State "AQAAAAAAAADKf+z5RYylGy+kHmwZdiJyeIoVSlDUzrfm8E/BgoD/UA==" size=46 + GET State "AQAAAAAAAAAuBVq7ryEMFqmGPUTOGhYUveJjCzxDkiAB0U8Lu5dnEA==" size=75 + GET State "AQAAAAAAAABkXBM0SxfYfnnjs36tOS9uCut1N4bJRa0JJSX7kFOoMA==" size=171 + GET State "AQAAAAAAAADEIrLtJNyAFpDUn6ZCrPtyUTU8wgOAiSHvnLEJlUG1MA==" size=47 + GET State "AQAAAAAAAABdarvZfug7U/N9V/JcYU5YK5/Dtgt9bsv/52pMjibzBw==" size=75 + GET State "AQAAAAAAAAAMRWjff261Pgy+dFdpPD5+mMRDBrL8pz3rVbW8wKNn+g==" size=58 + GET State "AQAAAAAAAAAdbJWxGW4qpP8HI40z3l8GQiflCLRlTJUvkOLSw7mK/g==" size=107 + GET State "AQAAAAAAAACMkfllN3y8znhC5MUPNUX8b4nieikbY/OzMleuoRyRwg==" size=107 + GET State "AQAAAAAAAAAr5GCF5lZWqsZs05i+YoADw6sZkSQdRkz8jTA8Ppo4pQ==" size=46 + GET State "AQAAAAAAAAB8RbxRXwtKIMzTNGlSJdfpMYfymwb6CM8YeEeASs81CA==" size=171 + GET State "AQAAAAAAAAARm6U4taGaSrk6S3hxcK7ZK8QgIVeLQaneih5RAoBQGg==" size=53 + GET State "AQAAAAAAAABW8WOcURepJMxxrTpezVpuMpuQsBdVRp1Nor87Kzv0pg==" size=160 + register_len + read_register + sha256 + register_len + read_register + sha256 + register_len + read_register + storage_remove REMOVE key=dAEAAABm/P8TGXAAB+1Pkr2EgWswVZoL7JjVxQDcMfb/4rR3POw= tn_db_reads=9 tn_mem_reads=17 shard_cache_miss=9 + GET State "AQAAAAAAAACYspLuarE9v+YG8zD03mk6VeFfa1U7FyNfg9AG3yn6HA==" size=75 + GET State "AQAAAAAAAAA4crMVofb7iSt8NC2i9tfGkNTwNDwGuw9oVA0hbR4F9A==" size=171 + GET State "AQAAAAAAAAD9hjAGX4vmunEbEYk0BEFuk3JtjOMbV5Jae12KPU+Jvg==" size=49 + GET State "AQAAAAAAAABs4+zAF09l7y3v6EG4GLVSUn2S0pIFII69p1dZKKRgoA==" size=203 + GET State "AQAAAAAAAACBp+Ab+uCdclVwCq57ZI7vgiWHhn3kWT6ZPbAYZh6GHg==" size=523 + GET State "AQAAAAAAAAB0SjkBIZboYnuN2l4DbJ8M9oXnuK7XawznEgi6bAJsQw==" size=523 + GET State "AQAAAAAAAABlyejeWCE1nwSPMHzviFzmfqZeXIqcnoHFgPtNO9XFrQ==" size=235 + GET State "AQAAAAAAAAChL4449zyjpiuPaFCDe7MRBY/5jX56+MRLGQ50ELRx8g==" size=80 + GET State "AQAAAAAAAAAYi40XpPr58qisRrLbpX3qWQfwk+doVrCbNYyV6tQUKA==" size=72 + register_len + read_register + storage_remove REMOVE key=dAEAAABl/P8TGXAAB+1Pkr2EgWswVZoL7JjVxQDcMfb/4rR3POw= tn_db_reads=5 tn_mem_reads=21 shard_cache_miss=5 + GET State "AQAAAAAAAADK4V00VQNkoBgncYKyJy/MdFd9kLrZnqSbkGFIpt/ENw==" size=523 + GET State "AQAAAAAAAACzc8Ubcua2wQp7kBtzHr4L6O5PLb9fB0j+AT6DB4sx5w==" size=523 + GET State "AQAAAAAAAAD78pAAS96HbvzgvrQ3lBULnCgGQkPlg1yjHmk4ADQqdA==" size=235 + GET State "AQAAAAAAAAB4m1VFjaRlhe4lHty4tPxzfDvtcsfuAHynBPs4rLSWjQ==" size=80 + GET State "AQAAAAAAAABTCtW9/D/BUNswKEvBfrVT4DgiSVl499xwosCjUaz/yQ==" size=67 + register_len + read_register + storage_write WRITE key=cAEAAAA= size=160 tn_db_reads=0 tn_mem_reads=21 + register_len + read_register + storage_read READ key=UxAAAAByb2xsaW5nNjY2MS5uZWFy size=64 tn_db_reads=11 tn_mem_reads=16 shard_cache_miss=12 + GET State "AQAAAAAAAAAqIDBsg5eJaTRA+HuLQnv47ukzUpKF8XBuyEE9GwdTYw==" size=75 + GET State "AQAAAAAAAABBDrkbprOg3Gwob41WB3PfTEyu3+wd9qaWjQ9UaHOLpw==" size=171 + GET State "AQAAAAAAAAAyqzpjszmbgUsw+Frzf3pt8ZS0Tk+C2/j+N+pSoLjNdA==" size=427 + GET State "AQAAAAAAAAAez/DpgcUPfmMhZ2CtiabRhW3AeLiyrOjJYjT7FK4g9A==" size=49 + GET State "AQAAAAAAAAA/MUD3v41CQRt0fKqnDqgllrb7yZbBuoQBI9xRq6FiSw==" size=107 + GET State "AQAAAAAAAABkgauEchBq/QL6f+wTStNLzQWi9JCLNSuAjy4a5I4YjQ==" size=331 + GET State "AQAAAAAAAAAH7wySfdL0pEK+ku+1gf9tG5mnAn8wcO2DZMwx/NGBYQ==" size=75 + GET State "AQAAAAAAAAD5n6ldfAMR8jDdmw5ZTCnOmikT4AbDegZogbs392Lu8g==" size=139 + GET State "AQAAAAAAAACZUGg8GzCqBLyS3rDDAsc8wUQgV7bTlxXI9JQIdK07Bw==" size=75 + GET State "AQAAAAAAAABLWkz54Tg/NMrtou6BZdhvxPJzbB3lMu5RrpSgoNHGrA==" size=139 + GET State "AQAAAAAAAACjtboTXw65DM7RwAivizluNUgkk6y9bw80qx1m3YEl0Q==" size=63 + GET State "AQAAAAAAAAA/jfbEz8+dZDRvH7y9FnEKhopI8BTNBqsvFE77a2cjdw==" size=64 + register_len + read_register + storage_write WRITE key=UxAAAAByb2xsaW5nNjY2MS5uZWFy size=64 tn_db_reads=0 tn_mem_reads=28 + register_len + read_register + storage_read READ key=UxkAAAB1bHRyYWRlbmVqbmlpcGFyZW5lay5uZWFy size=64 tn_db_reads=4 tn_mem_reads=19 shard_cache_miss=5 + GET State "AQAAAAAAAAAEbtxrr6igr7jLtyUcJFyb4czh+1JkhEoJSR02mFD5PQ==" size=49 + GET State "AQAAAAAAAACAU3HiMMVKgYw4KQmfGCRLMjFxyqm0iedpThT50lI/qQ==" size=75 + GET State "AQAAAAAAAABIPNNxutyA7swDEB9BEPmec2+Wuh+72iVhtGvuA9xhGQ==" size=75 + GET State "AQAAAAAAAAA2OHom6wid6wrTOtmS6BkmgAbb1Q5/5A3D3ecb9zm4Bg==" size=74 + GET State "AQAAAAAAAACU2A5IjOiQBe0oLuI15DsFjIwyuWtIAMBvXZeUaQdkvw==" size=64 + register_len + read_register + storage_write WRITE key=UxkAAAB1bHRyYWRlbmVqbmlpcGFyZW5lay5uZWFy size=64 tn_db_reads=0 tn_mem_reads=24 + register_len + read_register + process_receipt receipt_id=HRR3EXitfuoA7yFoa1239GW13ZErmdq6J8vt1MoZgpiu predecessor=app.nearcrowd.near receiver=app.nearcrowd.near id=HRR3EXitfuoA7yFoa1239GW13ZErmdq6J8vt1MoZgpiu shard_cache_too_large=1 shard_cache_hit=13 shard_cache_miss=1 + GET State "AQAAAAAAAADYhUlXpUB2OX3mIikCaQACQF2zorvTUoWtOEnrqd3vEw==" size=541068 + attached_deposit + input + register_len + read_register + storage_read READ key=cAEAAAA= size=160 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + sha256 + register_len + read_register + sha256 + register_len + read_register + storage_remove REMOVE key=dAEAAABmzjCQlnuTdT/DBs09zyzd/0Q4DsFpbgP3hPBb3QIOueY= tn_db_reads=4 tn_mem_reads=22 shard_cache_miss=4 + GET State "AQAAAAAAAABd59vyhJNIp/UwJ2cYAkIyz7Us6825vPo6CXqIeNj3tA==" size=523 + GET State "AQAAAAAAAAALPoqT2+0+Qb96Mvl8z3Ce9A8ammEr53srQmRjo5ayuA==" size=267 + GET State "AQAAAAAAAAAmfTvtn8ZZOaLUJ5ZwTNsI21/EgyA2pL1/evWJZ2CWIg==" size=80 + GET State "AQAAAAAAAAAyACqAldq26Jx3/m+HtWUR6CnmZH1JrhKLLgnloX/5pw==" size=70 + register_len + read_register + storage_remove REMOVE key=dAEAAABlzjCQlnuTdT/DBs09zyzd/0Q4DsFpbgP3hPBb3QIOueY= tn_db_reads=4 tn_mem_reads=22 shard_cache_miss=4 + GET State "AQAAAAAAAADvbjdMP+oCHDfyfHzsHv0Dh/mEPDQ+EOAy8TeZjOrNEg==" size=523 + GET State "AQAAAAAAAADSkcsscnQRLjKrIVAD9XePQda3JSw02Nx4ZFMH9Ex8QA==" size=267 + GET State "AQAAAAAAAACOEWLHscOQdp0UDa3v+66YI4fK/SlEYAnvrboFTXh2XA==" size=80 + GET State "AQAAAAAAAACbHaEbc7YMWgKnWY4vX4awUtr2VoRD3kBGdZfmTa7Szw==" size=57 + register_len + read_register + storage_write WRITE key=cAEAAAA= size=160 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + storage_read READ key=Uw4AAABjeWJlcmpvaG4ubmVhcg== size=64 tn_db_reads=8 tn_mem_reads=18 shard_cache_miss=9 + GET State "AQAAAAAAAADk5tiBkw3hbkaC9Zwd5ccSPVle1VwKfomBfEbj3VK56w==" size=299 + GET State "AQAAAAAAAAC61spEwRZoAttKsxpPTtxeTibc26diTwCVP1/1G7Bg7A==" size=49 + GET State "AQAAAAAAAAB+SlzuBWGcNPkFrINKH4DyxFyrBnAtAn9fQ6oIsrrlbg==" size=107 + GET State "AQAAAAAAAAASXb+/9y95vGpsZ9scIpJXzxd6xpy7Ps6gApB/D4GNww==" size=491 + GET State "AQAAAAAAAAAFhp0jI3sCO4TtBPHVUGr6cnSA+d9LJ6fq8TVYkzVQCw==" size=75 + GET State "AQAAAAAAAAAYwOukoxnRXjPH1agRmFsBqaql1BdAQzpvuLPceuirfw==" size=139 + GET State "AQAAAAAAAAAXzKhGZ9UKI//b+s9R/W22LySu0lhTWKTZiUvqt2z1Xg==" size=75 + GET State "AQAAAAAAAABzV4t92BsZTStzpq/4WELmzrszo2/c6jImgiZYRplRKA==" size=61 + GET State "AQAAAAAAAAA9Gd1qyfGqMJzhRxQtWuYFc+/lkEMnQ9py6AEvl0XXzg==" size=64 + register_len + read_register + storage_write WRITE key=Uw4AAABjeWJlcmpvaG4ubmVhcg== size=64 tn_db_reads=0 tn_mem_reads=27 + register_len + read_register + storage_read READ key=Uw8AAABzdHJhZGl2YXJpLm5lYXI= size=64 tn_db_reads=9 tn_mem_reads=19 shard_cache_miss=10 + GET State "AQAAAAAAAABlS245kpVSGwFcY1PIwkfJz/Ig7YRkFMin0woe3cY5DQ==" size=49 + GET State "AQAAAAAAAABG9+F9O2ZgrnKULHnLPePduxMPlUYnESO+hN/4qRFDPA==" size=107 + GET State "AQAAAAAAAADz9bDAPxM3rPtpmf4E/KC5in9fQv4RMxhj/XuUcCb0Qw==" size=331 + GET State "AQAAAAAAAABEoNCTN023oeILL9b+Z4muDwON02M3BPl015b55/qh+Q==" size=75 + GET State "AQAAAAAAAAAkHloRg0QmqYC/jM4k6vARb5rP4XJXM7DRLkprfDhziw==" size=107 + GET State "AQAAAAAAAABNi78ToKxsuLid47Aw60svDKXHs6DOY5BPADU7Nw2U4w==" size=75 + GET State "AQAAAAAAAACh5+VGb9/JVriJUj1y55oHWe8U+uVzD1bnpp5klCpOGg==" size=47 + GET State "AQAAAAAAAAD32pdBPp1dajJu4qAYaZyy+X7AM2nFc3KZeJdoC0xsFA==" size=75 + GET State "AQAAAAAAAABG4eHj91bJfbyG5RofXHX182FtZwWFOyW73th8L1eu4w==" size=60 + GET State "AQAAAAAAAABVYnqxfA8W5JBL0qih45R7h5iWLtJtvvfbFgwILAhe9g==" size=64 + register_len + read_register + storage_write WRITE key=Uw8AAABzdHJhZGl2YXJpLm5lYXI= size=64 tn_db_reads=0 tn_mem_reads=29 + register_len + read_register + process_receipt receipt_id=28ozHpuL1WZ3ExMm3UrNQYco31XHHaBiUXbyMD1firye predecessor=seensayer.near receiver=app.nearcrowd.near id=28ozHpuL1WZ3ExMm3UrNQYco31XHHaBiUXbyMD1firye shard_cache_hit=13 shard_cache_miss=1 shard_cache_too_large=1 + GET State "AQAAAAAAAADYhUlXpUB2OX3mIikCaQACQF2zorvTUoWtOEnrqd3vEw==" size=541068 + attached_deposit + input + register_len + read_register + storage_read READ key=cAEAAAA= size=160 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + predecessor_account_id + register_len + read_register + predecessor_account_id + register_len + read_register + storage_read READ key=Uw4AAABzZWVuc2F5ZXIubmVhcg== size=64 tn_db_reads=4 tn_mem_reads=21 shard_cache_miss=5 + GET State "AQAAAAAAAADZvfAfnASXdD66Si6MnjLblAR3gW24EV8fORGElLfWdQ==" size=363 + GET State "AQAAAAAAAABRZF30Oyh2cFr+8lmMxcYP+mld4eCAw1kvpRZEowpffQ==" size=75 + GET State "AQAAAAAAAACcoszJQqW3bI6cO6RbOUhtNV8w0l1XW8+o9P7PT+rloA==" size=267 + GET State "AQAAAAAAAABefcXhrRiHoSLqpGCthcmmVPbJrUfNLOiOo7JHjbiXMw==" size=62 + GET State "AQAAAAAAAAD+CSAWdNIvzUsoMo2NCAgSyPdnh4l8mTkwFbO6azSQ6w==" size=64 + register_len + read_register + predecessor_account_id + register_len + read_register + storage_write WRITE key=Uw4AAABzZWVuc2F5ZXIubmVhcg== size=64 tn_db_reads=0 tn_mem_reads=26 + register_len + read_register + block_timestamp + block_timestamp + storage_write WRITE key=dAEAAABnDgAAAHNlZW5zYXllci5uZWFy size=25 tn_db_reads=9 tn_mem_reads=21 shard_cache_miss=9 + GET State "AQAAAAAAAADGlOd42CwSZso+P1wrj7hLwvscvoNLlsPulkhbwqEMKA==" size=139 + GET State "AQAAAAAAAACnbVrBficDKqrYj2xjaWCK1vMrUqm0w+l3/VPMUob5XA==" size=299 + GET State "AQAAAAAAAABk3hkUW2Lbnm2pS8uCParw2WPi7QEgumFsTUd2hBSmtg==" size=49 + GET State "AQAAAAAAAADn4CP/JpL8BHut6xB7UV0Rrc1S3p7LfbuEyXbINf2nnA==" size=107 + GET State "AQAAAAAAAAA/FhNA8PlZjvYWk8l3pzQ882IlvsphQSWvBGunFoc8Aw==" size=331 + GET State "AQAAAAAAAADzEk/XqIGVba9H1RXjGAVYMTvcDpII3sjJYEOkAtU6ug==" size=75 + GET State "AQAAAAAAAACmv4tNGMZ48kmbyLTrVmMtrMXa130aykGoLitRmnyGqw==" size=235 + GET State "AQAAAAAAAAA75mdxzbnsJV0mtu1sCfkPULNGMSjTVPK2eIsLH+3CwQ==" size=62 + GET State "AQAAAAAAAAAPMTy0XHVoQtyyuttKDWkhoTmx4r9hl+1hPl7KnXn6tQ==" size=58 + register_len + read_register + storage_read READ key=dAEAAABlfrxtKJQX98cjaLio+Gj0pz8r14v9NIXyqilf+vvBt5o= tn_db_reads=2 tn_mem_reads=22 shard_cache_miss=2 + GET State "AQAAAAAAAAAMFChpvNyZXz/YRG5NrzAgOSOcM1ihN+/FYDfquigqYQ==" size=491 + GET State "AQAAAAAAAABm9EXwLUeGRlym07HVmODTNV0CJd5fmfWFP/jO+P6PDA==" size=107 + storage_write WRITE key=dAEAAABlfrxtKJQX98cjaLio+Gj0pz8r14v9NIXyqilf+vvBt5o= size=56 tn_db_reads=0 tn_mem_reads=24 + storage_write WRITE key=cAEAAAA= size=160 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + process_receipt receipt_id=Fp2sJPeBSPX5eXNanJ5Jh1shgBKn6omvUCWcUNEAMd4W predecessor=system receiver=6f25217bc63c86d36e0cdc620d05b2f1381eb43eac560d4f1261380b255e2176 id=Fp2sJPeBSPX5eXNanJ5Jh1shgBKn6omvUCWcUNEAMd4W shard_cache_hit=4 shard_cache_miss=26 + GET State "AQAAAAAAAACnPJ3hghFKW7JFGe8VjfFb9i0B7y8OiubngX/mcs7mUw==" size=171 + GET State "AQAAAAAAAACZYmVoJ00OVb3kM6l/UBrxMqpLdpP7TWHOH/PhY8/ITg==" size=491 + GET State "AQAAAAAAAADi1hdBCwPFvZAX0vN+rNGvRKj2NkZZ84msshWlpiF1Dw==" size=139 + GET State "AQAAAAAAAADzAH0UrZllln8DGA9UzYqL+WneVEz17MHaZaCm+2KX8g==" size=331 + GET State "AQAAAAAAAAASUvb9T+D2BnABi0rutr01nGdBcAzh1xd2oHXG5NJUuQ==" size=107 + GET State "AQAAAAAAAAA1Ts+R/PlfmvTxSCOfksiD0X9tP/fBsK0gaZ4KjgtP7g==" size=331 + GET State "AQAAAAAAAAD9loD63HkxlatHtSuPP7x+8YXJaDisNbABxcuUoHTdkw==" size=75 + GET State "AQAAAAAAAADms02SuleiRWF+12dfwWEqmXirZAhCIE1e1FeO7Oud9w==" size=331 + GET State "AQAAAAAAAABVh2ZVaw6hUOByk1Tiw4SXpy8akAh1XYhVxzfpD2KOsg==" size=75 + GET State "AQAAAAAAAADNyucBgr4DFJ7DyublV5wb9YFiYq1RYvr+n/6SVgmmkQ==" size=235 + GET State "AQAAAAAAAACdHfvUw7PEf7KfzFHdg/A+ZJEhWtTBPnOw/7QbxTq80A==" size=108 + GET State "AQAAAAAAAAB3PiRl002lzX2bMeG8UAyTavZEOCj1FlyUPblrRnnhHg==" size=72 + GET State "AQAAAAAAAADUmqNch7d3IrRG252T4w2jF8hYdTizsh///oXHKHJdgw==" size=171 + GET State "AQAAAAAAAABqAED6GRPBaWMhyi6gR2Ekt2b7EyX0Wx/5bTUS27owTQ==" size=491 + GET State "AQAAAAAAAAASBB3qUPMxdikiZVW9/yP8hGvvhFNBwpQ8zoix44THxQ==" size=139 + GET State "AQAAAAAAAAA7ldFBScczXq4lcaI14PM36cXxjZQGv/1xvt70Qx5VdA==" size=331 + GET State "AQAAAAAAAACFWW3h18a/kOsP+MqjDrc2hb3t8jjN18upCofO+p/Upg==" size=107 + GET State "AQAAAAAAAADazD0vTJsPx3mGcN3BVLKHW9xmqjisOLFBH11JIMkT8A==" size=331 + GET State "AQAAAAAAAAB06g9A3VVQB7yjyynCyV8ERnAckMyEfGHNDbiC1/ZmBA==" size=75 + GET State "AQAAAAAAAAAgmk0hhIlX8fOw2aZACNyB/JBGvhI/I0PwJsupidCKfQ==" size=331 + GET State "AQAAAAAAAABitWYB3odRiTaYIHOzlys1sEKHCAZb5bbCPB1h4ZdVeQ==" size=75 + GET State "AQAAAAAAAADLZQi7bytajVx/XVwTvIcmy+xEE6pc02JAwP2IUE0pEA==" size=235 + GET State "AQAAAAAAAACd4Jj2e30BeHC2VsNUyGaa9NmiRaPq1EAnjdRTXBZ5Hg==" size=106 + GET State "AQAAAAAAAAD15Pg1YSuyXMorY4t/tq3/aaBDxjRHAcBKfhPGMiHxrg==" size=75 + GET State "AQAAAAAAAADE9ijD4UbMktGlm+w0ClfVFsRK8jzORQqEbxwUbcR14A==" size=81 + GET State "AQAAAAAAAAAqrqDXosKrhc5+00psuPRAkiaymqq5x+vkm8ZdulONKw==" size=9 + process_receipt receipt_id=6d7ZrM9yztyWvAVtYbA4nQhtfchiyXbbBmJzYyMe3nht predecessor=system receiver=76248d053a8e8b47cfecab60c5f6f1befe74bcbd923c4ef5950bfeb2945fadff id=6d7ZrM9yztyWvAVtYbA4nQhtfchiyXbbBmJzYyMe3nht shard_cache_hit=4 shard_cache_miss=24 + GET State "AQAAAAAAAADXXYQ/vTq2LvwsyhatQx9BuxWASHXJIacZzN1Q/qi3BQ==" size=171 + GET State "AQAAAAAAAADS2R600nCTx0ALFbZczg4t5JPETTzKbrpZtaq9ymbG7w==" size=331 + GET State "AQAAAAAAAAA8dsEBDniDq82zfg79FcOjOTn43ArfO1Ouc0VUFLb53A==" size=171 + GET State "AQAAAAAAAAB0cZ+fp6A12XmGj36+KnSYezZYliR86ooiP9M63+mkHw==" size=331 + GET State "AQAAAAAAAAC83WRophJ3ucwCOYCelmMFMLUX7KFMPCf3ZL8/9yLRWA==" size=139 + GET State "AQAAAAAAAADlu2Mq5Dv/F8A5tRJTDAiOW3KkccZ6u95LVP1HpFkgEQ==" size=331 + GET State "AQAAAAAAAAArPaGZ2E5XoIYTdLDG1cqJUmG8hPFO4YrYhAR9Ttvtvg==" size=75 + GET State "AQAAAAAAAADW8PgJbnXDryvNquiANhASqafn4xl5WhxtuAnHlZmrkg==" size=331 + GET State "AQAAAAAAAAA9zGW/UP2nTlIqgKFsfkiyrfHyCxSXG5x6/NwUBbEw2Q==" size=75 + GET State "AQAAAAAAAAC5poGRFKRtWKFZOtsaM+tMEpT/5hIPjycrJwt0hPdIcA==" size=139 + GET State "AQAAAAAAAABuV708TRhhJ5jSSo//j0iD2hhz6aP1n0zUctEWU68z8Q==" size=108 + GET State "AQAAAAAAAABjcwW0Dasq9GnSuz/Y2tfSJoHm03NNz+F8YSaG1h5VYA==" size=72 + GET State "AQAAAAAAAADLjxY3Jm/V6SqFekzWkA3N334ol9sDleWHiRnIYqSRTg==" size=171 + GET State "AQAAAAAAAABbyNHq8Hls4nV6qKf1NBr1MHGKqF+meMAmjsUWz7/R3A==" size=331 + GET State "AQAAAAAAAAB/N56Eqih0qAjrUHdoB998zovdZbOgd1GHKAmaxfFcog==" size=171 + GET State "AQAAAAAAAAAAVqb8eQesNUw68ZZI8p4Ww7lNFCvhqPHVhDtW8sDO8A==" size=331 + GET State "AQAAAAAAAABUeY0GHvKujJ5Lv/JP5l0z5iEShp6mTTc5T/04JQHf4A==" size=139 + GET State "AQAAAAAAAAAowBq5cClR70s6qcFGC1MLYSklkr1vt6Q/AmfLRa2X9w==" size=331 + GET State "AQAAAAAAAADeaVSxqtXekIsRReKtFtfDe2L5YxTK1D2h07FfpMPbjQ==" size=75 + GET State "AQAAAAAAAACaNHe8KuW7WZsNRDdRUvOKOKPzj7buPXcISZ6QLqYGLA==" size=331 + GET State "AQAAAAAAAACqZ+rmpaZTvqBW2zsqw/LakMAfLj/xmX+nVql5NZZn6w==" size=75 + GET State "AQAAAAAAAACwAadfws9Q7a36ezqsrn8IcGpEtB4a63jzxIRNRTyyog==" size=139 + GET State "AQAAAAAAAAAzNFurDIU9aOdHdIK2MBMqVOAEEVnJmiAw1Pi0VuaJSA==" size=142 + GET State "AQAAAAAAAACL6lHyHQwQHR+IBjWfa3eVHfRKzJqZGtALK8X2d5wOZg==" size=9 + process_receipt receipt_id=wDN56cf2bf3sz16RzF6H3UaC1pGnYJ7XFvUdzLTiTS3 predecessor=system receiver=7a5de36efdbcd23489dcfd7a3c63966b9bcbddfbe4ba8db7d1df109ecd37ef5e id=wDN56cf2bf3sz16RzF6H3UaC1pGnYJ7XFvUdzLTiTS3 shard_cache_hit=6 shard_cache_miss=28 + GET State "AQAAAAAAAAD3Wcko6RzUVC/ijN7LcZACd5+8bC8PyRU1kP5DNXf9hw==" size=491 + GET State "AQAAAAAAAABaSGPFx60PISeCszNACeoY3HgIwXG+0oc8Ge2L+jvsUw==" size=139 + GET State "AQAAAAAAAAC4M6aES1onJEfqsjRpND9GBxmO2fCie+IO1SKfapAfew==" size=331 + GET State "AQAAAAAAAAA6rkZOrGNEI2R+aa8V947VGwjlpCvuCFDnMwFFuSmIvw==" size=75 + GET State "AQAAAAAAAAAw/i0JVVSpSQsn8nyUk6/dUFabinus50EWhVXG0E4njQ==" size=235 + GET State "AQAAAAAAAADzRnX/DqJeb7riP6qZGsKzEru4w0jcCT81L4Caik9pMw==" size=75 + GET State "AQAAAAAAAAAFIl9JCNdyaApmu15nMwBLd9KY5C/kkbtfegnx2Vd3XA==" size=203 + GET State "AQAAAAAAAAAUI7Bw6nFP4obDxW0mwWhdzPwIgtNimfrnVH8WAU3zzw==" size=75 + GET State "AQAAAAAAAAB3YvspSa2jtZsYUTWKWqLmhgo4AM+kc/hINYXCjIcXJg==" size=235 + GET State "AQAAAAAAAADe7dcM2w9trNiQwbxGZc5K+ovCD5g19Ou5yAmfehNIOA==" size=46 + GET State "AQAAAAAAAACROTuet43RoNqcfW5uHScWaJ5J6TXtV1UPJMt5yNy9+Q==" size=107 + GET State "AQAAAAAAAAApYwfq96BY5Swa2g0V46Gdp7cAKRoCwvRzfVQxY2RUbw==" size=107 + GET State "AQAAAAAAAABoiZUfyqk60ziEM1kDgF9X1Kx4aN73tgR24iosZB51pQ==" size=72 + GET State "AQAAAAAAAABUrx0pFssBM+lmOgYuM+wCkr/mAeq+k8NQ9g16EyB3Cw==" size=491 + GET State "AQAAAAAAAAC9pWnrodxlULk9uIoW7Bk+D9FbEHeZLyoNio9Gd2uV1Q==" size=139 + GET State "AQAAAAAAAAAANDIIjGiB+GkGg+2OROr2a3vtD/qKEm0jUHud1pvDog==" size=331 + GET State "AQAAAAAAAABytdEbqUhHaoV/thdmIbPEjCqqjdfFqDSaf63BMNQutA==" size=75 + GET State "AQAAAAAAAACVrvIOGCZ3t3tV/401WovhauIjolReQpfiQ1ET7ZGqjQ==" size=235 + GET State "AQAAAAAAAAC5iFrQwjixjwAk7w1gNrMP2ojU6RnJWIjSw84SUksHLA==" size=75 + GET State "AQAAAAAAAADMJs308cH8IvpiUlBARWos/ZAwh0y9johmwdUT8oMSBA==" size=203 + GET State "AQAAAAAAAABIg6DYdNHZ2R9FFG976NTQtwf+kJKtQW45XG0tqUiuSg==" size=75 + GET State "AQAAAAAAAADZT7IIP7jx/zbu2zSzIAqeZQMOg4z4UCHGkGWQw2sAIg==" size=235 + GET State "AQAAAAAAAACk14wg8ZkhBzYZyqG5Wvk8/XLX/JZ0oqdk3Jm1Im0+RA==" size=46 + GET State "AQAAAAAAAAAQ3ANmFqv7rTP42uDJJuMz89/HhL9SMPFr5VOG5GlmpA==" size=107 + GET State "AQAAAAAAAADiKmiXHu2zjHo1mmncGM8j7dGkXXWzd6ZHe3arScxQ1A==" size=105 + GET State "AQAAAAAAAAAQmXHksfIR6e8gIq+/HJf7/tcGMqwx5AA4F4o+iO7gLQ==" size=107 + GET State "AQAAAAAAAAC4VKrt5jfBvFmhVGnYd4yEy3ttm1RU61WBVGlMdsEsxw==" size=81 + GET State "AQAAAAAAAADCvIEBF1zrBtbdEyOSqQql223Y+iekoKOBOJxbsJ78+w==" size=9 + process_receipt receipt_id=D3noeb84wCn3hym3TyGarXfJZJvnFEBiQRk1fQ3W1jws predecessor=system receiver=27b8a4173e15829829159b39c620e9a5da908a3f68d94a70cd489188d478f37f id=D3noeb84wCn3hym3TyGarXfJZJvnFEBiQRk1fQ3W1jws shard_cache_miss=28 shard_cache_hit=4 + GET State "AQAAAAAAAAATGLlPYarilsYyP1bI8LcMQjp1TCeel01shcxeqrWkPg==" size=171 + GET State "AQAAAAAAAABVTzDKjrAMwenvzoirKUuF9eXMrxccg2VE4znYvzv2Lw==" size=331 + GET State "AQAAAAAAAAAO/aXkm1l/CwKTEXyY/ywbPQrgVv+Sd+Q/BILyS0+LXQ==" size=171 + GET State "AQAAAAAAAADg7s04Rm5ojoDnTlNjRJVSnnePkr0DjwlQXJB06gqqNA==" size=491 + GET State "AQAAAAAAAACLU3JL8x4R+F7ou40YBcpMZePRVzETdccoMbXfR8ntPA==" size=107 + GET State "AQAAAAAAAAAdcPbLAasURbfVdk7axbNWSMMV1wVj+12HjcOT2V16Pg==" size=331 + GET State "AQAAAAAAAAA+vV942YhzGsl9peoSSjsmtznBJqwpz9MHE6THe1X50w==" size=75 + GET State "AQAAAAAAAABia9bLkYt6zU34ODS/xc+BioJwUUy7VhJd0fvfnLSyvw==" size=203 + GET State "AQAAAAAAAACVjSasmHrMJf/WdU0/tvFS+xqaLaSRPHBNDA64k7+X3Q==" size=75 + GET State "AQAAAAAAAABPo39iujE+Uhs/8t/zZFdW8F/9WUj19DZyLpbboMObGg==" size=235 + GET State "AQAAAAAAAADCVXCEl0ZIUurF+1NIkT4XqXOudBQ2v6ZAI/zPtl2zIQ==" size=75 + GET State "AQAAAAAAAABf8DqRNYUgq3p51qbKLyU1CyWTmcA0CXz1tPRpLORpEg==" size=75 + GET State "AQAAAAAAAACg/eli+8K8gpXSZTJmelkN81TwaNDAfDiJGQ8IKz+4DQ==" size=107 + GET State "AQAAAAAAAABmnqbEg3Cc8qUmEEdlq2Q9FB4v1TXcMcYJil8YEbkxyg==" size=72 + GET State "AQAAAAAAAADCyhreMzgz9TEfYyvXphGZKkL/17BDL9h/yGFlCvaIRA==" size=171 + GET State "AQAAAAAAAACSOe1Cm2A2uQa2Z8y3MxpntYdqJmqv7Umnb44n03fOWA==" size=331 + GET State "AQAAAAAAAAAdM9MJ8UTc0GI1j0tCFb9oMBFA+VgKnFxRfvMUdzKT7w==" size=171 + GET State "AQAAAAAAAABEKOyE/OlB9cfo6Jew40p1pJEyqh1meLBc49rIdNgBEw==" size=491 + GET State "AQAAAAAAAAAG9i+EaP3kOIjlffVWVcH+xb3lwtzKfDQ2iJo1jTlnKA==" size=107 + GET State "AQAAAAAAAAAPpMhexQePyDixhZZJcBOMItXm+CaBRFQRVl08kFnKbg==" size=331 + GET State "AQAAAAAAAACMDj07PA9pXsKsuQq6qpM1rup5yQ9alwKVkTxooMr68g==" size=75 + GET State "AQAAAAAAAAD7mHTjrLXfxfgJQW6E9T7CzkaJEL8+TwpmVerjullZvA==" size=203 + GET State "AQAAAAAAAADjJes0rD6LbEIrLan3yNVoflYs4UkeR4VcGyuibB5UJA==" size=75 + GET State "AQAAAAAAAABvYIDgwdFyxNHomHgvYLrw1l1J9GFujEHX82KdwV9HaA==" size=235 + GET State "AQAAAAAAAAARS3m1dFLLAPscCEOs7DPy88sMUFnDIbgAv+0jHX1pbA==" size=75 + GET State "AQAAAAAAAABlhbeBJJ2HSXe9cHddDVyR4x55DkGMySFnbYm17TAdGA==" size=75 + GET State "AQAAAAAAAADY2IqWCGNq7XB8+XSQVA8iQuIvfxrEaJMZRekdiQngrQ==" size=141 + GET State "AQAAAAAAAACuO4EY5CNq/tHZPUGQ1GAbO7MStMHdJq3V1AZuXG/8iQ==" size=9 +GET State "AQAAAAAAAAAd/SFzGjnkHAlv6Xg2SmN1IVf6Ml0zGlgZgc/BWurPfg==" size=46 +GET State "AQAAAAAAAACjnlxTKrBLfuo35uwfGrbfflKuFyzDuAJ8l5463gilkA==" size=171 +GET BlockHeight "hcR7BAAAAAA=" size=32 +GET Block "`B4epTA1V54DmRbFhFEaLyyzLBQuqq688bHNq3sNRDnTF`" size=7771 +GET ChunkExtra "lYRfbjKj7tNaKloWPka4gdGd4rVxVOL4e1KqjqSwTkYBAAAAAAAAAA==" size=101 +GET Chunks "`83aP2GFYvyMYSq76fLREmw2zgjPVccL67CVo3Pg26d8Q`" size=2833 +GET Block "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=7706 +GET BlockHeader "`B4epTA1V54DmRbFhFEaLyyzLBQuqq688bHNq3sNRDnTF`" size=6358 +GET IncomingReceipts "lYRfbjKj7tNaKloWPka4gdGd4rVxVOL4e1KqjqSwTkYAAAAAAAAAAA==" size=2585 +GET BlockHeader "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=6293 +GET BlockInfo "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=221 +apply_transactions shard_id=0 + process_state_update + apply num_transactions=4 shard_cache_hit=344 shard_cache_miss=1 + process_transaction tx_hash=2Aw4SPFoD6ia1Vr6NxqHpxKmK73JM9ZBw2HisUzgaUB5 shard_cache_hit=4 shard_cache_miss=30 + GET State "AQAAAAAAAAAGfv+c4bft2fP6ec6Wiwc6LqBKkmCpG8W8T3rxso90Tg==" size=75 + GET State "AQAAAAAAAADwH7F7Q6Zpmyv7uUGEs8wGhoc6qu/QPxCC+kjoVvTiAg==" size=331 + GET State "AQAAAAAAAADaSyt3Fp53ASZP7wXnmGgw4HvWTsKxAeidO99Gwnt3oA==" size=171 + GET State "AQAAAAAAAAD3BlRigpe6BCGbiRUealkM9i4dkxtA6RR1N/jazjoo/g==" size=491 + GET State "AQAAAAAAAAD+PYu3iNXOu5tetui6Ht4VuCN7v6iMASz91uabxUwtJQ==" size=139 + GET State "AQAAAAAAAAD4w0EwEa2FSBAYqM7baJjf+8ZYbbpo3v/hIxC6s79LLQ==" size=331 + GET State "AQAAAAAAAABANgeTMVTrDkFwbEXn2nki/vmcrPMEHpY0LRyC1G8vpQ==" size=107 + GET State "AQAAAAAAAADp8w23i+HKYPsklZtgpcYAzlo349fFhnZ/nA5OAicMZQ==" size=331 + GET State "AQAAAAAAAAC0VKVGAuPQqUzZG2mC6ody3v3kp/LDu0SX/uK3ui06dA==" size=75 + GET State "AQAAAAAAAAA1W0Jg4+ZR1IVtcxJXUzczRKtIKFxor1Ez1W0GOfdVNw==" size=331 + GET State "AQAAAAAAAACN3SAO+LVS+u/1u1qzIrPvMjG84YKa8O9QONN8Ygzk5Q==" size=75 + GET State "AQAAAAAAAADWSZNJa/xkX0djAcywxGUdhyOA37dilO8c/sJRO7Em8A==" size=107 + GET State "AQAAAAAAAAAbuGYAbLoR/juqSElE270TJEk6JNB0khiCdp5W7xr77w==" size=75 + GET State "AQAAAAAAAAAr2DUWId/lUMTXKebYx7+/XOGGrwnOXBaQJrWjLLTn/w==" size=107 + GET State "AQAAAAAAAACAm+AlohF20RG2SiGvHTtw1vrSixbHAoet5XrNxOAOEA==" size=72 + GET State "AQAAAAAAAADu89UNcpmBdqdcg6MeFdHrGhvGmk67ZOSmpFxeazjNkg==" size=75 + GET State "AQAAAAAAAABfFSxS3iECVpzTgQCfIjy6PeBKEb7sjDs1NpNnJYXwFA==" size=331 + GET State "AQAAAAAAAAASL8tNhoQero2s6gKyF8mRsO25vl3dyT4DwRLLbGLKQQ==" size=171 + GET State "AQAAAAAAAAABhkXoB+J1/u/UBAZXrabJX2XLXmtZzAni/gh3H1Js2w==" size=491 + GET State "AQAAAAAAAACKtHgSSprFjcjZD57ScG79E23xyB0I67W2QiqotpKO3A==" size=139 + GET State "AQAAAAAAAAAnmzrjeLkPv5gyuSbut7yabHPUrFMPkSDl38LqOLebFg==" size=331 + GET State "AQAAAAAAAAB1GqBCqRIX4O+Pj0Ke8fcV5+eNocJ3HSP/wNXrV7Vu2g==" size=107 + GET State "AQAAAAAAAABeDro1jimI060s0kHEEzUuJTVwY6DUO+gRaNxgHcFsAg==" size=331 + GET State "AQAAAAAAAABZV1Ip+srPwh3KtpuoSOrVQr7Qp1zaxZ5Iofa+jsKtNw==" size=75 + GET State "AQAAAAAAAADQXclC6XG8mhtcOseuabRqhTgFIAj098O/Kju/eDhxfg==" size=331 + GET State "AQAAAAAAAADjXJ6M9AEFlCVYRIlpqBiQkxWS8iQmQ/yo7nPc2CuknA==" size=75 + GET State "AQAAAAAAAADxE0W9oZSyX9WWUtlkmXOwQTAo6qCDZ73SpU8sIv1n/w==" size=107 + GET State "AQAAAAAAAAD1TVKZ59GSAmXDA2v3DoOQWM8GFsJr6bSXLXB4mm646Q==" size=75 + GET State "AQAAAAAAAAA83r+KelX9ScF170m2HznK6nTvh+ibitnuGndTvhB2GQ==" size=141 + GET State "AQAAAAAAAAD1n/sJ/7aR9fsvWW7ZC/603Bx3tACYLRfGr6M1y05DWQ==" size=9 + process_transaction tx_hash=EZ9GhJxWQgcTpEQBH2JutymJU8jcUAzGE45a4wym3mK9 shard_cache_miss=26 shard_cache_hit=10 + GET State "AQAAAAAAAADjQMThvwqhOFR8sNJEljiT1GtpZFGHMYRWmLDWB9TxWw==" size=171 + GET State "AQAAAAAAAADodgruqT3g5PSZdfownmEGms+ON+lQfUzRhBwjYHQPWQ==" size=491 + GET State "AQAAAAAAAAC2rkGCHhshHvnTK4EMsmwVcU/41j6AVFmQoX8wM+IzWw==" size=139 + GET State "AQAAAAAAAADG0CkkxDZbihYu04wx/tU4svRZMqkolWgFVgoTViW+bw==" size=491 + GET State "AQAAAAAAAAA/267arMHovhsI+j8g026VJBQTz3KUE+QWFO9Uvj9ZQQ==" size=75 + GET State "AQAAAAAAAADTNW/9aa4UjDE6DdS4XjEXM1QpQrmVVKo5QB2jKIKJmA==" size=299 + GET State "AQAAAAAAAADRjQqxugDAKuGCjcHDF3nqYRplXCwZRCFBuNePDlYGVg==" size=75 + GET State "AQAAAAAAAADccE4ZXkWDZkqviTos5mxMM5kfWHkbdTcrdSIvIbAq3g==" size=331 + GET State "AQAAAAAAAADLSHOfNd0FPGEFlmWKHYvr0B39r/NJ5x7sxMUcZDGX7Q==" size=75 + GET State "AQAAAAAAAAAFlHQbcb4D5Tn9msr+A8PywPkfrc+dJ1rlBzdDiMZXew==" size=139 + GET State "AQAAAAAAAABUWWQy0/U6wdFWj5zW/+siAhdwfVNdmmxkSSqo6AxZEg==" size=46 + GET State "AQAAAAAAAACj0VrVBzpP1t8vNFVdbGXZMWuc/TVPIAAjY3fhtMoh9A==" size=107 + GET State "AQAAAAAAAABZUyZKk1aGg5F4PmaNm9OvJi/WZj9i6RySX6AehLlFNA==" size=107 + GET State "AQAAAAAAAADOpEVBpG4elJzU09+HXgS26jt605/yjf70qEZ+igUyQg==" size=72 + GET State "AQAAAAAAAAARcogqvWS1zPHqVy6FQ93aFHX1ZW0xFTpAm+9CsM/+pQ==" size=139 + GET State "AQAAAAAAAAAVhNcEUnay7vDIFxAcIaQxa7SCNXLdxrI34vvLf8kx5w==" size=491 + GET State "AQAAAAAAAABEgcrqSar8Xzw4Bljjp6t7uA41qwzjA1SdqWlINHvpnA==" size=75 + GET State "AQAAAAAAAAAyH4pxm84J3fCcXw1bJRTuejHwmhGvfaghShEvBLpnNg==" size=299 + GET State "AQAAAAAAAAC7aqS7LZghQj0zQBTjSICn1TnYONWTL8sceemeUGmGJA==" size=75 + GET State "AQAAAAAAAAA+2ktfOVlSo7f9mzBjUlIxLKh7FFAglDLcJbMa+/zytQ==" size=331 + GET State "AQAAAAAAAAC4fBQmgf4zQ13TD0hxpL8bCcDbf/aLKrRQDoGcx/bRBg==" size=75 + GET State "AQAAAAAAAACYmpBDcJGcImnwi7Nn9hooYkb5IdZ6PDCpJV0FZ9R2mQ==" size=139 + GET State "AQAAAAAAAADc2+sgZHoD3L0EMU94JxH4Ouv5VlgF1LQbOdyVlNro+g==" size=46 + GET State "AQAAAAAAAABKDGkUIx70kTUdtWzrt1qeGXVZtPhn5TiSxfvw011MDA==" size=107 + GET State "AQAAAAAAAACzO+wj1s8xYMssGct070hF77tV90OCqQgNUKNA5aTYsQ==" size=141 + GET State "AQAAAAAAAAB8x7XPf0NQN8b//bTkrv4E2enrywoDjd3aFXh06nIJGQ==" size=9 + process_transaction tx_hash=6ysp5YTw2enoSzjn5hZBp6K8NunF7noEHesY65PF4WDv shard_cache_hit=10 shard_cache_miss=22 + GET State "AQAAAAAAAADFK6h1O+LHRLm1hsERoBfHKrfFcBxZMqbaPM3MVzhuAA==" size=171 + GET State "AQAAAAAAAAB6e/3Q+/GDJkLUyp+jW5n0v4lTRzKbpYQa8YicoEVaqw==" size=491 + GET State "AQAAAAAAAADsNkqgswGIwK9pkQM6w7VSwsi8+94iDpEWqodB2yyCJw==" size=139 + GET State "AQAAAAAAAACNC5TnKnSFr5Lp1z/xZBgmN4PqUF6UfEkz6Z2ztDm8DA==" size=491 + GET State "AQAAAAAAAAADQkvNoigjnHvYE4oRxlTgyVtfSeSarb6EIl0eYeOKTg==" size=139 + GET State "AQAAAAAAAACBDe6SKzUQ9QaotWIy1t25tKGgccM59jZMsljXhQ8CDg==" size=331 + GET State "AQAAAAAAAADK4xui4h68c4PDAliddt3f6IA7JABwBOAkHHLTT0FfbQ==" size=75 + GET State "AQAAAAAAAAANDVVk1jkN2WmXHSmgwY+q/xYsuBQLvZNlE70SD6bSew==" size=203 + GET State "AQAAAAAAAAAi9mfj4SXHz6hQEqWw3xfx9X3x68hk5VYismSznWLMUg==" size=75 + GET State "AQAAAAAAAAC9G53suoAIXs8MEm3N60d81Fal+UsuFDBe0xAJhIK8hA==" size=139 + GET State "AQAAAAAAAACJowTLRBw0gTOt113Rk/Dhv+znmqnIgDTblIwWzuSIsQ==" size=108 + GET State "AQAAAAAAAABPgYejWH/n4fxhmu/L8kLgW7nkrenrHV2SINjseAfg3A==" size=72 + GET State "AQAAAAAAAAAupQlzwfY8QUhmG7PLoT70hI5ZvUdIEd2hJfPzAsatrQ==" size=139 + GET State "AQAAAAAAAAAp3BL9C3v6+PFCmPi2ejAJp6mhvQlqqhjAQ91L6XcqSQ==" size=491 + GET State "AQAAAAAAAABYN9Al0lyTqWk5XmwLWwY5cuPgxDZOG5UCUG+Mdj6nYg==" size=139 + GET State "AQAAAAAAAAA5BoH2Uj3PDfqh05uShCrOTzENBhCxO3sgeiWykngW0A==" size=331 + GET State "AQAAAAAAAAB8Xeb9e05d/EIcbJsIUilHnIZ4QqPgR8ZFNWAa9PuujA==" size=75 + GET State "AQAAAAAAAADTrSkcVwuHspmVSuL5nfBIOttscgtqMg9KHxcjYWrK3w==" size=203 + GET State "AQAAAAAAAADgM8mDU+1JbwsWNfV0JjDVlD4Wm7qerD5Qp9SCD8I9Ow==" size=75 + GET State "AQAAAAAAAABTduLDnYCrNDCUq1cZ6mF0bAhUv+upK9vW7j6OolIJkA==" size=139 + GET State "AQAAAAAAAAAdLZ9VQjJe+sdlfOvRiIx0jAbOsD9+a/Y3V5jbj10bVg==" size=142 + GET State "AQAAAAAAAAADMfOOoO7atk1cGq0kfKYqQF3f6TpGoOQf4VjVebqKaw==" size=9 + process_transaction tx_hash=8GExB7nr4wRqCYMhU6y5PK74q2sWjqSZmRkZb3MVGFXV shard_cache_miss=26 shard_cache_hit=10 + GET State "AQAAAAAAAAB0VMR6N5MK9ocmjJQMwNx/xJADyVLXPgxemGbuSOQoXQ==" size=331 + GET State "AQAAAAAAAAA9NJxBBds3C4XXgt/RwT/0EMbM2NlU0GrPowIima6S6Q==" size=171 + GET State "AQAAAAAAAADauBS0OE13EuZIH7SVHvr6pPZV5+gO/XsxLD9zsWgemw==" size=491 + GET State "AQAAAAAAAAAx8H+4JuNsv95/RrU+9bhttLhsM2cjUg/dd4cZ9IPjeQ==" size=139 + GET State "AQAAAAAAAAD4oFEAjZ145g2fvnLiErEePwrA8RynXqHKngg0IfgBPA==" size=299 + GET State "AQAAAAAAAACneeugMOUA/rDz2GeONN0RnRbBY6KD6KcFkGqJtZPTRg==" size=107 + GET State "AQAAAAAAAAC90yXQe29y6owJ2ubWqLEC3JJU/04DzFrl8c85NKI1PQ==" size=331 + GET State "AQAAAAAAAADTB5evMz1W95znxZVqOdNG5aDJZlFggEPGjKX5w7sdpg==" size=75 + GET State "AQAAAAAAAAD3mlwNi9TqEpeNefLTW9BHyZjtPn0nyYtJncN75vnsSw==" size=235 + GET State "AQAAAAAAAAAhnq3ts//A7FcaVe1WUb9sgP04gwesZqD4FlOljshQ9A==" size=46 + GET State "AQAAAAAAAACcchxKz6SquU5mRgy1Qgjpemo9zIfsY4U0WcGW2HIibA==" size=75 + GET State "AQAAAAAAAAAgFXhLpTNMmwo+6LaP7wz15XZ0gZPDzmsd0s2R5l+1aw==" size=107 + GET State "AQAAAAAAAADfljeT8rUTCJIVdhP/5KARVXA6YHs99cMH3gjxo17LbQ==" size=72 + GET State "AQAAAAAAAAB03SFbbxbicnV9e3/VOZ0Zd07FRIYTctF6c2lSyxxVoQ==" size=331 + GET State "AQAAAAAAAAAKKO4AI6sZPMPRXMoQfSUW4M3OMUiYLVH1+rv0Gled2w==" size=171 + GET State "AQAAAAAAAABMkg95t/aqUwl44Vb3EGBhGll6RJtYCkbIWfRmP0a7ow==" size=491 + GET State "AQAAAAAAAADG8AaFzRtJt4qJDGfq2KEk1Xz1zT/EmjbVlACsHaAjqg==" size=139 + GET State "AQAAAAAAAABzDvPQG69NZ8pD7mzD9fyL48IMiGKirxNLKWKWoIc6mg==" size=299 + GET State "AQAAAAAAAABFSwyofSaZsAPDDGGJOmicVlwjxQiFZnIm9jbLDYODrA==" size=107 + GET State "AQAAAAAAAADZ5kwtyRMF9Dd5Gw1o+1RvXkNTZlj//IqsuPrUca4YTQ==" size=331 + GET State "AQAAAAAAAADPdp+gsmRUheejx3Ib8+pmt/3fvyKCg0bJdmK65B831A==" size=75 + GET State "AQAAAAAAAAD78/JdZQFWa4PS9kNct/lFNsSAM3s2eP+7GR/LMbj7Xw==" size=235 + GET State "AQAAAAAAAADCS61Z9WX2NzVPxzZ0o0YDUlV51DnXXaaQ24VFDE9pmQ==" size=46 + GET State "AQAAAAAAAABYlNra33rlEm+o9koWasFOyhwqkGMV/AYjG5RVbqFJBw==" size=75 + GET State "AQAAAAAAAACiRkXTXABacFk1SvjHtSjwruBREjT4UR3s4qCuO1bhMA==" size=141 + GET State "AQAAAAAAAACnIv5SqurveCuKxUxid6sddG3ZbmQIN1DioTjM4W6uNg==" size=9 + process_receipt receipt_id=5csXkacKhAAQ6F6eckGqZycLgD7cY5aKHimCxw1iNfqP predecessor=system receiver=app.nearcrowd.near id=5csXkacKhAAQ6F6eckGqZycLgD7cY5aKHimCxw1iNfqP shard_cache_miss=33 shard_cache_hit=6 + GET State "AQAAAAAAAACqTesf91/8Lc1YxCXQ7eHhzM2ibMJ67AUB1ILh/wc2sQ==" size=46 + GET State "AQAAAAAAAADteST5WdJnLeGXefIMWEeaIamopC1JRQC3Hyw37AFHaw==" size=171 + GET State "AQAAAAAAAADSPoc3OuPjyd8L2iaNWnFlPf3D1B56QdcOSTDA9mlsZw==" size=203 + GET State "AQAAAAAAAAA1LJsMUeYpHUe+MENKOl9tN9hMYoJtaMSNbJM0HSJV8w==" size=171 + GET State "AQAAAAAAAACbfQuYBArrGOKiSWwxb4U9cc0qsFCQu1zZ5StHaUEqCg==" size=363 + GET State "AQAAAAAAAABIAvJakQ8CrpTBD0x+8QvWXlmjqRJaAuhgpLsEuIA58A==" size=171 + GET State "AQAAAAAAAADp5S7Ef0hL/bNYHHX0UA8ABgqtX/u1ve6ZRjFWj0Dk6w==" size=46 + GET State "AQAAAAAAAAD16dExJWPKAR0EK+vb6dnU/XbdIXI76j80uF9qzfZiIg==" size=75 + GET State "AQAAAAAAAAD10iZMMu0fVKFXJTHMDsNETwb5g7NzHn0tn9rCF8iZTQ==" size=235 + GET State "AQAAAAAAAACRiXB3zOlhJbNWAFSnJF4ONZyPjnPrAyK7NoNW05syaw==" size=47 + GET State "AQAAAAAAAADbI8v3Mi+pgP8+k3ZMBhU+XpH4hgDMFrrVv/mrFOM0/A==" size=75 + GET State "AQAAAAAAAAB8bZTWFGDtyzhY3VJkVZQrtb4lXyTvVlryO+jzsOBNYw==" size=47 + GET State "AQAAAAAAAAAMu03bvI7Za5mhoCVPaJWNiSJTZpU+P72PlfG8olRcLA==" size=79 + GET State "AQAAAAAAAAC6lNLGJE3afHcsrjMQVL0qv8TM9o9chHDXgGrBbMSDRw==" size=59 + GET State "AQAAAAAAAAD2X7UWDhG5ViU1wx1pABR2fPzyAhGa1OEv1C5GnR/7FA==" size=72 + GET State "AQAAAAAAAACXUas+6cX9EMNavD8ggBnH0OY1Y7PspSNHsfane1EtpA==" size=46 + GET State "AQAAAAAAAADb9GY92Npsa9Hup8cQFXIA95DNupx29d+A44gJcq40ww==" size=171 + GET State "AQAAAAAAAACtpy+iMe6/SG/QLAoyaRqVV5Qnqi8/cQ0NFnxmhxiXOg==" size=203 + GET State "AQAAAAAAAACAQuIq0wVHH9a4orexOH7TawRqwytAcG4/t/lDVEhrfQ==" size=171 + GET State "AQAAAAAAAADtZ5OzDCX2e9htRJD3neHG2bmZ0mHUYKYUrWYs1gBzIQ==" size=363 + GET State "AQAAAAAAAACG0R2x/MLYnftXxfZSo8lUb8AFjZQD6nMrnwfrYRXyAQ==" size=171 + GET State "AQAAAAAAAACsPFcdfA2qedxlTpcdfYPDfUwKv2UIfKz5vnuGvY8QjQ==" size=46 + GET State "AQAAAAAAAAC9VnuUiL1qnmnCF9dKcexojRrId9viji9bNY1uc1AVmw==" size=75 + GET State "AQAAAAAAAABZ/0JVjbBm9GDjyl1gfs1CkyU9awMiwa/mN68v75dFAA==" size=235 + GET State "AQAAAAAAAABi8FTDnSGr1KEus3oB45Qb0m8Vqlm8x4nL8sW+6lg53Q==" size=47 + GET State "AQAAAAAAAADD4396w+TRZxAbzDr19R1bJKynFVN6qVfRhqiWHVODOA==" size=75 + GET State "AQAAAAAAAADdvzxj1kCwkp4O6MdYTnRGYdiTsm28GDL2i6y/ITRe3g==" size=47 + GET State "AQAAAAAAAAB8hOcDwowp8OaBUg8H3ByMzLe0aRezX4SN6OkaeW44nw==" size=75 + GET State "AQAAAAAAAAAhwfZqVhBmqzEi0KJBDO3CAP67Wzw+WvjkqvBNX0S8+Q==" size=57 + GET State "AQAAAAAAAACu6ngx1BV03OwYw0EuLcflaQURh/ZoA3KKr96bZ8qmOQ==" size=267 + GET State "AQAAAAAAAADvYLl1hCF9n16XgxbedjHqZF3esEERX9kfm6UzF0/T3A==" size=107 + GET State "AQAAAAAAAADrM5BI/tayFvGc26GmufajSIESRUMHIkkB1mzTC507Pg==" size=81 + GET State "AQAAAAAAAADc6LAMU4qBiOcT6VCb6kmV+zOTR/PySAwhLAgFNcMMfQ==" size=9 + process_receipt receipt_id=3DVei5MvayiThUdrtgXDDeWRSfCG7QD859f4rtADLDNk predecessor=system receiver=app.nearcrowd.near id=3DVei5MvayiThUdrtgXDDeWRSfCG7QD859f4rtADLDNk shard_cache_miss=3 shard_cache_hit=18 + GET State "AQAAAAAAAADBBxwzZCfaSydiRMJTj7Ze30qypNGINRFTl5UrRvG72g==" size=75 + GET State "AQAAAAAAAACWYulpizQ8PciGZTyXrE04ub1yQzr5syi3UQHYDV5GDw==" size=81 + GET State "AQAAAAAAAABYET5v4UfEF8A15E6iFcwrAYLnoyE51OETQpCyvdNGXg==" size=9 + process_receipt receipt_id=2CZ3nibSbAFYF2rZYJdxdBc2NwLEVQyrxbfmkjsL1X14 predecessor=utiha.near receiver=app.nearcrowd.near id=2CZ3nibSbAFYF2rZYJdxdBc2NwLEVQyrxbfmkjsL1X14 shard_cache_hit=15 shard_cache_miss=1 shard_cache_too_large=1 + GET State "AQAAAAAAAADYhUlXpUB2OX3mIikCaQACQF2zorvTUoWtOEnrqd3vEw==" size=541068 + attached_deposit + input + register_len + read_register + storage_read READ key=cAEAAAA= size=160 tn_db_reads=20 tn_mem_reads=0 shard_cache_hit=2 shard_cache_miss=19 + GET State "AQAAAAAAAACIzQQiTjWsL28obvxM6XDz8BubqI3lA22Mbf0MxLLW6w==" size=75 + GET State "AQAAAAAAAACYOkRbNmzZreIt0wbktwcpl8x6omXu9+iNm4o5kdpvgw==" size=46 + GET State "AQAAAAAAAABc4PXh8QLKeRJjQcgLeYctAlBp7vy6K459plaqUkqyMA==" size=171 + GET State "AQAAAAAAAAD7iL8deqKW6qwTuZ8MeZt6HpCeQt1jg8fYBesFGN+jjQ==" size=203 + GET State "AQAAAAAAAADiwfyifQEbtPZt77J9+4eT2OfZgeA98dPybW67QeFkig==" size=75 + GET State "AQAAAAAAAACrJuEJ7j7eaT2uu/APIkOjPNe1hT82nJ0UM945LN5sZg==" size=171 + GET State "AQAAAAAAAAC9tMPWgJgUcvKc8hyCZ0UlwOgcrsagnKvLJHIG/TBePw==" size=107 + GET State "AQAAAAAAAACwy5e9SkByYacJ0+Z82fzGSNXy8fpwDm2BAPnmRWARVw==" size=46 + GET State "AQAAAAAAAADmAkTdc/re1X1GV1Qq+cfR49BZ2+dBK1Y9fdpRAp+1yA==" size=75 + GET State "AQAAAAAAAABOYIcfuL/7kj50gL24dsA0I9KtP21r8GQ5tgaiwK4sEw==" size=171 + GET State "AQAAAAAAAAAasCqGkYHwCnCsSoU+DCPeQ79oCvlwqz6YS2VewnWliQ==" size=47 + GET State "AQAAAAAAAADlyXbATkVt4CgnJszqp/1Yx/Iag1Jy4sI4ziliXrTq0w==" size=75 + GET State "AQAAAAAAAACkx/z+UfQKFgJfLLHVjo8QY0uDa92wEYv2uERL2+uKJA==" size=58 + GET State "AQAAAAAAAAAkEiJcjzgxqJWE1A1XPTXiax4gqpD6ZieTSy9mpjLJ2Q==" size=107 + GET State "AQAAAAAAAABXM4AkJOj08ydu8fPGU+3yBbUOgz7MWa5asdo/RRMosQ==" size=107 + GET State "AQAAAAAAAADXJTBMBBl52F2GNaO0XtbJUkJ/aE1Ypdv3Q3EqCiT3Yg==" size=46 + GET State "AQAAAAAAAACk1wFUsN6F0+FqMfTgnjAhOVS7xP9pgJ4IGNcJByMBVw==" size=171 + GET State "AQAAAAAAAAAjkkChpuQDIPurqLEcaPwQeBanALto6mnCq4XURQ5UbA==" size=53 + GET State "AQAAAAAAAACYJhpGpvwiA8ut98EJ5Rhq/ztA5dhpwMGQ+X/FBSdtYg==" size=160 + register_len + read_register + predecessor_account_id + register_len + read_register + block_timestamp + random_seed + register_len + read_register + random_seed + register_len + read_register + storage_has_key EXISTS key=dAEAAABtCgAAAHV0aWhhLm5lYXI= tn_db_reads=9 tn_mem_reads=17 shard_cache_miss=9 + GET State "AQAAAAAAAABLAzMqQBxHq5K+zUTaKRSs8F7ZBoNmRfu2XHdlZIkudw==" size=75 + GET State "AQAAAAAAAABjZoFI65XUwjt8exsSnpGdCA6auL2nrtgfooifi/RmQQ==" size=171 + GET State "AQAAAAAAAAAhodi5nY9ki6068Km5V5PsEFvadCc6ETljdyeUSHcQYg==" size=49 + GET State "AQAAAAAAAADgOSRDQVABva0nfZY9UzH94Bid3wI67z+q9hGUsHZwAg==" size=203 + GET State "AQAAAAAAAADU7Qz/MieZDknTK84Kf1mmLqRO8PqFVZodZz9tBipFmw==" size=107 + GET State "AQAAAAAAAABw6VNt6xqTsoCG4V6e1v8kA9dGsJCjnbxLqRB0D3U+bQ==" size=235 + GET State "AQAAAAAAAAA3SCPpcsYTxeOYkpWzmPmGUJIQoKEO1gfJoZColpfyAw==" size=49 + GET State "AQAAAAAAAADy4HDx0/ZIt1y6XMGIqOybDC6+olk1wPlmn1wMd2tIFw==" size=107 + GET State "AQAAAAAAAAAHJIP/0/xEN1iwPVi2OFWYwM1nQ+pmkyLW8Unyv4AP4w==" size=171 + storage_remove REMOVE key=dAEAAABjawEAAAAAAAA= tn_db_reads=6 tn_mem_reads=21 shard_cache_miss=6 + GET State "AQAAAAAAAAAohKPgdDEFI5tfsmPGEyIJLfKSZVvef++k3nxVXgPaEw==" size=523 + GET State "AQAAAAAAAABXmOflhB4bVqLfLa1tlH6wH2/Yh3JAbkOxJMMcZKjHnA==" size=523 + GET State "AQAAAAAAAAB+2fiSffcYPWVAVtNU/F2D3UsS+F/LZXr7AUFrH6kSww==" size=46 + GET State "AQAAAAAAAADXjwsFn0wuhjIeJND12LvhJ22+uUdPyRFr7H0ZnpYUjA==" size=75 + GET State "AQAAAAAAAACRbA4zgvWqoKG6gu8K8NN1umcRoUXIVIFTNtM+R0qPNg==" size=56 + GET State "AQAAAAAAAAAmK+0hdk3wuY63KpXP4WyfVSy8Yn4zwNJvDKbet2ptng==" size=33 + register_len + read_register + storage_write WRITE key=dAEAAABjJwAAAAAAAAA= size=33 tn_db_reads=5 tn_mem_reads=22 shard_cache_miss=5 + GET State "AQAAAAAAAAAGIOSc9aNxm6MgRQ10L4LpdKm6CTTdblcnSVwIsOXwlQ==" size=523 + GET State "AQAAAAAAAAAQhjAyy1zGMRQArSl4u8Wxh0RWR0wloTpLATBfXbAVwQ==" size=46 + GET State "AQAAAAAAAAB1zJKGlESF18h2/2xjcmEahCTVbJkDQNLiNzAFNhT6WA==" size=75 + GET State "AQAAAAAAAACF0tTybfAHgA7M8IzqPiHOavp3q8WlbBfMn0f1MuVq9w==" size=56 + GET State "AQAAAAAAAAAPJ2Z2xEa/0sJWvWy+f8/GARNdswNjUDbEIA1VLwf/Yw==" size=33 + register_len + read_register + storage_read READ key=dAEAAABl9DRfMrHFB1+mL22wIVhcY/B9Xuj4tlKYTG1zPpZuAok= size=190 tn_db_reads=4 tn_mem_reads=21 shard_cache_miss=5 + GET State "AQAAAAAAAAA5TRUZ53znVF55U2pjgo5FmqiOzy0LImsMmrv8kQWWfg==" size=523 + GET State "AQAAAAAAAAD0ve21EHY2Pk/8udsqGAvTr4UCwAU5TGTK6rkFGgFyow==" size=523 + GET State "AQAAAAAAAAA3OBvDOFtNUqv/u15qZuv7jIwkuulwTNUw6NRF6dCcwQ==" size=139 + GET State "AQAAAAAAAABbsPpjBEszRcmd49JNy3u31A91UuLQjDixxQ60J4MLig==" size=80 + GET State "AQAAAAAAAAAaEQD+CDiAEzVjOtI7VABID1DKM4mLny+IdHXKfH0n/w==" size=190 + register_len + read_register + storage_write WRITE key=dAEAAABjawEAAAAAAAA= size=33 tn_db_reads=0 tn_mem_reads=0 + storage_remove REMOVE key=dAEAAABjawEAAAAAAAA= tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + storage_write WRITE key=dAEAAABjigAAAAAAAAA= size=33 tn_db_reads=3 tn_mem_reads=22 shard_cache_miss=3 + GET State "AQAAAAAAAAA61fSRM6EhFX3YqoDSupkEjPNZKK0vouAz0fj4lTfHLQ==" size=523 + GET State "AQAAAAAAAACoiRqy6YhjhBWN5ApYquTa58cice/ERY3WrbjOM2i0Rg==" size=57 + GET State "AQAAAAAAAACJOmVBxVOWL5EMN4jCx13/Xq7OTRedOcHmDEpFVsuvow==" size=33 + register_len + read_register + storage_read READ key=dAEAAABl65VbX181ei6TUEQ+tEjk4M4o+WQ0wGn9sfNOoIk1LWY= size=189 tn_db_reads=3 tn_mem_reads=22 shard_cache_miss=4 + GET State "AQAAAAAAAADQ8v0j75LAhK66mpIemiVby/nZ6hgpljuE6e8g7Yhh+A==" size=523 + GET State "AQAAAAAAAADajKrpMDKtS1x8MyqEnpbTzuQ4kiuUJV/5Ns3I+kqRlQ==" size=171 + GET State "AQAAAAAAAABqh2hsQXjlGl+nkrDkiOI1zcwfBgL3d7NxL1iPY6rr3g==" size=80 + GET State "AQAAAAAAAACbQNIbZHChRFi8GaEV2/fzQmES2H6ufFMWnf/Auu6UYA==" size=189 + register_len + read_register + storage_write WRITE key=dAEAAABjawEAAAAAAAA= size=33 tn_db_reads=0 tn_mem_reads=0 + storage_remove REMOVE key=dAEAAABjawEAAAAAAAA= tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + storage_write WRITE key=dAEAAABj7QAAAAAAAAA= size=33 tn_db_reads=3 tn_mem_reads=22 shard_cache_miss=3 + GET State "AQAAAAAAAAD11T/S4u/JZbhMLKK7In1hXF2l9LjGU97KZp/xXu5WcQ==" size=523 + GET State "AQAAAAAAAAB6XydIkNl3PWUP/deC262ISBb35n05XNch++aifPcBwQ==" size=57 + GET State "AQAAAAAAAACUsqbkKj/JvcP/dGHEW9CZtSCvbD/09UhcJ8BY7iQCUw==" size=33 + register_len + read_register + storage_read READ key=dAEAAABl9ycDMz4S24W+iCrKXvy7BjRgTWV0diUaAcUkLTm9OXA= tn_db_reads=1 tn_mem_reads=23 shard_cache_miss=1 + GET State "AQAAAAAAAAAw2BNQcslSHJH1br6IYP7PeZGtA8bcn+irfFH81hxl5g==" size=171 + storage_read READ key=dAEAAABm9ycDMz4S24W+iCrKXvy7BjRgTWV0diUaAcUkLTm9OXA= size=69 tn_db_reads=4 tn_mem_reads=21 shard_cache_miss=5 + GET State "AQAAAAAAAAAgznk0NH+v66WQ0bu4F+JWB34+eW9LprWsCgNJdjRTjA==" size=523 + GET State "AQAAAAAAAABeAJwOdHuCqQf3bef59LAHOYkgkTl4liIEJfCI0cYDTg==" size=523 + GET State "AQAAAAAAAACz4qOO2W7ManxJxIzLy4fTBwlDOitnVubDdoBBBg3FXw==" size=203 + GET State "AQAAAAAAAAByyWFL4rLDeuWUTHxbWCsA+n8AQmb9/DQmbg479x1xgQ==" size=80 + GET State "AQAAAAAAAABcMJGpCu9XsFwCe9l+RCGKP8PRWmELEjrPd5iB1Rjd5A==" size=69 + register_len + read_register + storage_write WRITE key=dAEAAABnCgAAAHV0aWhhLm5lYXI= size=58 tn_db_reads=9 tn_mem_reads=21 shard_cache_miss=9 + GET State "AQAAAAAAAABSQGxXJK4b5zqdqBAFMIE0TLEWMprMJEb0M/XkplZnCg==" size=139 + GET State "AQAAAAAAAABsKUTofBp0KnTxF4aQpjIcCNjWn0TGeZKPLWlKvdxmng==" size=299 + GET State "AQAAAAAAAACDz2DTpKDzNJr73iq+VGOTDANmZu/fodOgBq2AtMBIFg==" size=49 + GET State "AQAAAAAAAADY+vmLtcsXWLu6a75xNSjRz2ZHzNfYXt2nPifMM1TlZg==" size=107 + GET State "AQAAAAAAAAB16iwzw/XnATGL5e+cE/+plRMcQhsle/IEQF+dUvnmVA==" size=363 + GET State "AQAAAAAAAAAI0OmE5plCDTwHVeNziLFSVtaFycD3oTm0CDvU130ltQ==" size=46 + GET State "AQAAAAAAAAAsvWx0H9EJcUuFKVZNo9BBqO1QQem3UjMQ746FEYt8VA==" size=75 + GET State "AQAAAAAAAAD7bUi981OBvwpsZVDyldZ53jUfwXvzuw8k78AU4cDn/g==" size=58 + GET State "AQAAAAAAAACRzrApFSgw+njiXvfcy4TQyrKrZ4WgL2O0kHtIlO/MDA==" size=25 + register_len + read_register + storage_write WRITE key=cAEAAAA= size=160 tn_db_reads=0 tn_mem_reads=21 + register_len + read_register + value_return + process_receipt receipt_id=GTWiUssUkNTmHnUCHgQo91G1ML5i6y1tAWznVsoKog1z predecessor=nataly123.near receiver=app.nearcrowd.near id=GTWiUssUkNTmHnUCHgQo91G1ML5i6y1tAWznVsoKog1z shard_cache_miss=1 shard_cache_too_large=1 shard_cache_hit=13 + GET State "AQAAAAAAAADYhUlXpUB2OX3mIikCaQACQF2zorvTUoWtOEnrqd3vEw==" size=541068 + attached_deposit + input + register_len + read_register + storage_read READ key=cAEAAAA= size=160 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + predecessor_account_id + register_len + read_register + predecessor_account_id + register_len + read_register + storage_read READ key=Uw4AAABuYXRhbHkxMjMubmVhcg== size=64 tn_db_reads=11 tn_mem_reads=16 shard_cache_miss=12 + GET State "AQAAAAAAAAAQubQPDOQxoMiGRiEE142jelLVH0zH6xK8yDLxLmlCVg==" size=75 + GET State "AQAAAAAAAABLEd73T1rnbcFFJlV7CGXVnT5IXX2vcUpdTJh03T/dOg==" size=171 + GET State "AQAAAAAAAAAPqvheP9qC0ItwclsQ1sUQkGkCW2Hs5mzIHUCWouc1Jw==" size=299 + GET State "AQAAAAAAAADzrcwLbcGpTGnpakFPKHwKJ36SCcNzgWVqFcGSsEWnhw==" size=49 + GET State "AQAAAAAAAABcIMuVOyx81TT0VjFrZqzKvqDG7Ok8XG79vFzjDpXsFg==" size=107 + GET State "AQAAAAAAAADVo/SWCOingjk1IurNqgLpKLg5UHOC4PATjS2VPk7NFA==" size=491 + GET State "AQAAAAAAAABdLOt2cA7b6BTOiM+EXTv9SooQ7xzz5cJxSo8UR4wZqg==" size=46 + GET State "AQAAAAAAAACkeZrLOCKv4IK2m8URse6cTt5QNG269sogr7YjLuONoQ==" size=171 + GET State "AQAAAAAAAADFOWmg5+cdxe6PX0qcMzuAmBgqNGNJ6vFtKNNiBeScxg==" size=75 + GET State "AQAAAAAAAADmoMhwIyn2dc9BJhYDDEWnPeoLBYXSLc+JMXEdsPx4mg==" size=139 + GET State "AQAAAAAAAABzOoQBfuIvXPDVlwtJXSWSu4vZHwt6M1xycLQkcBgpdw==" size=61 + GET State "AQAAAAAAAACdtPss5G078ob+Fc0f6ava1grYmT3sitNsshSaTbDYEQ==" size=64 + register_len + read_register + predecessor_account_id + register_len + read_register + storage_write WRITE key=Uw4AAABuYXRhbHkxMjMubmVhcg== size=64 tn_db_reads=0 tn_mem_reads=28 + register_len + read_register + block_timestamp + block_timestamp + storage_write WRITE key=dAEAAABnDgAAAG5hdGFseTEyMy5uZWFy size=25 tn_db_reads=9 tn_mem_reads=23 shard_cache_miss=9 + GET State "AQAAAAAAAAD8IUwGN91D4A/HHqwVpclWVI7RyDe1DIjetn+GNxsjbA==" size=49 + GET State "AQAAAAAAAADTBGHcMkLwHRl3TUGUg5M6lAzs7PraWEYocTFojBjSmA==" size=107 + GET State "AQAAAAAAAACuS1q2K55wM8LMs0EbL/aLTnKWeDVt0AHTeMaUWYYnNA==" size=491 + GET State "AQAAAAAAAABfOjdMB0lZ5QYY9Zf64eESbQZeiIGyl5mIc3qhADS4yA==" size=46 + GET State "AQAAAAAAAACL/AsNWAffEMWrYeMSzfLRicURU6zqDIoLbwwjR7xm/A==" size=171 + GET State "AQAAAAAAAABz56xjPwNHU3E7//p5w1Au5uDxQeK7uPebiCeUrGEBIw==" size=75 + GET State "AQAAAAAAAAAKEnOZuXpx4Duvuwv+/bhX3h5c9vgt06MNFNLRkm6JYw==" size=107 + GET State "AQAAAAAAAAAjZJn/N4We0c1W+JiJBEluz/E71/f5s2/oQ6Xd09KM2A==" size=61 + GET State "AQAAAAAAAADSOMTPu3LoPQotrVpWX30MoB8nPO6TFqiTaix4fqAt5g==" size=58 + register_len + read_register + storage_read READ key=dAEAAABleEZ6du/AuXjsb7eutYX0ulVuntBc8toYOTkekfHDOfo= size=113 tn_db_reads=3 tn_mem_reads=22 shard_cache_miss=4 + GET State "AQAAAAAAAAAxjlWn14v1kZtn5nAIn8GxdxeJZmSDlCapJcFjfp6Jkw==" size=491 + GET State "AQAAAAAAAAAOk8ZC+8u994ewqxeNFEZhMBDmZ4T4p9OipGBa/v2/oA==" size=139 + GET State "AQAAAAAAAADc5183j2itAj2b/zviN+VYZFYsGQhyA1WfMItjrPb1pw==" size=80 + GET State "AQAAAAAAAACpNxBMYYsmwlpdEZTnDx1mWrhWewtVYsmG2anrdi+2lQ==" size=113 + register_len + read_register + storage_write WRITE key=dAEAAABleEZ6du/AuXjsb7eutYX0ulVuntBc8toYOTkekfHDOfo= size=160 tn_db_reads=0 tn_mem_reads=26 + register_len + read_register + storage_write WRITE key=cAEAAAA= size=160 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + process_receipt receipt_id=GFRjGJ7J92VBZAkdSpUwN7z4v1mCmtWgrf2SU7f4MGCN predecessor=system receiver=27b8a4173e15829829159b39c620e9a5da908a3f68d94a70cd489188d478f37f id=GFRjGJ7J92VBZAkdSpUwN7z4v1mCmtWgrf2SU7f4MGCN shard_cache_hit=18 shard_cache_miss=14 + GET State "AQAAAAAAAAAoUeeAxVPfX2DSnD19xAsMbAnh20MSwBStNpslEBnmZg==" size=171 + GET State "AQAAAAAAAACWO8JXJ1miny/cgODaEgM/QD1UQs/3s3ORSTGDNqvbQw==" size=331 + GET State "AQAAAAAAAAA7J44NxQCoez63298b7Ua31MPUgOMV4YHSR9z9BdoLOA==" size=171 + GET State "AQAAAAAAAACKMdG/GSC8pZhxmkDqzPSNSct/z5Ds7Vols+Cy7EIYBQ==" size=491 + GET State "AQAAAAAAAABkDvj4sm330aDOnfuZsaMh5bvaRvZ6QZ265C6y8QR4rg==" size=107 + GET State "AQAAAAAAAABAnHFQ9a0DD2EETFWIewViJxeWEm/QcODZ0gPwXOHuug==" size=331 + GET State "AQAAAAAAAADG3MS/Sc2xcbX56YEZB9mvgIwnlcduIL3AepXMlntshA==" size=75 + GET State "AQAAAAAAAADqY29kBKq5ZWl7pPwK3x8VMmRzQS9jxkHa4zbL+ljhdA==" size=203 + GET State "AQAAAAAAAAAAfVwVoU72mU3m50BRBYX292nGEVZPgSnBRoualDTBIw==" size=75 + GET State "AQAAAAAAAADFmLqd3srdRMZBmykmBAQgmV1WLlG0ND8Kyec3ONlpPw==" size=235 + GET State "AQAAAAAAAAAK7wa8HHhlFSGerOHag8M2X631QHFH0HW86SeZD/sFbQ==" size=75 + GET State "AQAAAAAAAADTV72VYYf5xLA/+XcZC4tEe3WgkR5+MUWJJtR6XM6f2A==" size=75 + GET State "AQAAAAAAAAB6csBwrBiP1VFu/4fqiVmfxfgPXslUiqwQx1a8DlpHsQ==" size=107 + GET State "AQAAAAAAAACiqluxF2IGu/CtTM2SQlftF0a/vL1FZRubTDwCfYb2DA==" size=72 + process_receipt receipt_id=CuNBQKsiVcZH9Nc9QcWY65oE1w4ifYtiPC5SNKDfA7fL predecessor=system receiver=14ca6570b456a5f904641d1005b9499aee5cfa60216bb3b73871d2256ad1cc2a id=CuNBQKsiVcZH9Nc9QcWY65oE1w4ifYtiPC5SNKDfA7fL shard_cache_hit=4 shard_cache_miss=24 + GET State "AQAAAAAAAADvXbrksI+cx6UK6gRPbUDorjZa5Yl7puHn1EqdGEFqmg==" size=171 + GET State "AQAAAAAAAABOII1lyVh6baoxE4w3vFP4kRB0WBGPtsL+h7SXkQRs8w==" size=331 + GET State "AQAAAAAAAAB8FgOXk9yV9ilKDT/1CdPCJh2eqHYHDgMYqdIs7YE+sg==" size=171 + GET State "AQAAAAAAAACfScvw8IWgzIfcRu1Lch5DE3Ah/g/NYPklyUymQUI+IQ==" size=491 + GET State "AQAAAAAAAACW79wlEcpcC3IWtPz5Zw903DcvEaNlZMsvHQu285/6uw==" size=107 + GET State "AQAAAAAAAABbHX3M2xqEAIsiHA82KDoDAIk76bqlYcX+Ne8tBNdkzA==" size=427 + GET State "AQAAAAAAAABUHX68DhpnhekjapMh9BCrLOf+tXPtizpCiXP90g1EeA==" size=139 + GET State "AQAAAAAAAABVC6m3aRTI2BmIiG9XykCp5GUqZTRqKibfuUmeWHCyCg==" size=331 + GET State "AQAAAAAAAACZ15Rjb7TSu259UkCitvUGUeKk27tQzmL0pd5yzQn7ZA==" size=75 + GET State "AQAAAAAAAABZF0e+DrVtKnQnQG/cBG/JwWDAAd5x5OEaBoJvfR4JYw==" size=171 + GET State "AQAAAAAAAAD0oh5qXUwNV8yPE6gkFlm+AvUYbu9u7le87hteOGoEuQ==" size=108 + GET State "AQAAAAAAAADpBoK11bR+PS4fxRqQkMu69YFbHdlEXT+CuMgHc2yaHA==" size=72 + GET State "AQAAAAAAAACCV6rPRS/sjGYu7GDKhcZW8ow9fydU3py8cpO0w4JpDA==" size=171 + GET State "AQAAAAAAAAA4mHApNApxva4yXS1kjiPExpAsLogBID9UCpVGOLT6ZA==" size=331 + GET State "AQAAAAAAAADHv14rQMihKOLpNXYaGGSWTnHpBWbTFFuimeYvSGOz1g==" size=171 + GET State "AQAAAAAAAADLcJMTok8by1QLrgHB7d+uAFgfWBJo021FCQEdULIb1g==" size=491 + GET State "AQAAAAAAAACZV3mpd0xm3TeR1ztqYINsrv6cfiO71iIfrpNNi3ZlLw==" size=107 + GET State "AQAAAAAAAACmsvfBYFCwRQBMJtKmt7GDx9XZYa8sIx5ezdphO3SkbA==" size=427 + GET State "AQAAAAAAAAAouxeY/GL0hfJEkFhsPi3i6t/HJh/bGwQkRPV31QlcWg==" size=139 + GET State "AQAAAAAAAAAe2upJSf7tfE6oAc7haI5w1cGPWjU53TaE8TG/2wWmfw==" size=331 + GET State "AQAAAAAAAADde1K97TUjCDkkNizQc95kl+cKCHHRR27rc27sVDOjvA==" size=75 + GET State "AQAAAAAAAABk59nPG/0blJbY1WLpqVb15O/tFCVOkSu84SzRxr8QDw==" size=171 + GET State "AQAAAAAAAAAkPD54Xl2CLFWwSR8NB0cROstLDV8m1G0U+jeHZBojBg==" size=142 + GET State "AQAAAAAAAADbFfdwjvCQ2i6Mt+OVdGq4QJs/CtL3i3/EuOuVm/BtVw==" size=9 + process_receipt receipt_id=D3ww9qmfxoyDuJjsyGtsoCZ1MSPwhCDLtny75AsJcTMi predecessor=system receiver=2cd58e2eab52381a893c42b8dc47d66633b58c16303c4398f44dbd149b6e1411 id=D3ww9qmfxoyDuJjsyGtsoCZ1MSPwhCDLtny75AsJcTMi shard_cache_hit=6 shard_cache_miss=24 + GET State "AQAAAAAAAACaBeS7NimEXCMG/kFNttz+Dq9DV1uWX/U4Sn/ajcPXOA==" size=491 + GET State "AQAAAAAAAABGuHxh0tzL7QJVwiRCkMm6+dG4n6FoUzaBb0SF0QMayg==" size=139 + GET State "AQAAAAAAAADHY1Z94eL7AOswL9ai4FdjlqsepfpdDW5dF32A8gkvhA==" size=491 + GET State "AQAAAAAAAABKrCbx9eDwWKWkUuOPqMvMqmhg2Yr86To0ZR+QZIrcEQ==" size=139 + GET State "AQAAAAAAAADsA+l02AxX9LdNPsPYuKS7wYZUpoT8UbQPqHtg3Xws2g==" size=331 + GET State "AQAAAAAAAAAYVexvj8a4ECxByJ4bA7uu3xHpq0MtrIr0QlkLJMurxQ==" size=75 + GET State "AQAAAAAAAACcqU6aqIFZ5VP4ouP6jtLya8SceyVLDqI8GScgnO1HvQ==" size=331 + GET State "AQAAAAAAAADKgz3eqLIvlABi3snRWMJrx99i0k9IQNTxGSmyCo18Kg==" size=75 + GET State "AQAAAAAAAAA1ymORrhLL/jxEzS66JE49Y4GHhC6GVExhPMVN8HA1vQ==" size=139 + GET State "AQAAAAAAAABowRg1em4Wf9O5/Z7C+RAH+5ea+J9Mau96AudIiAO+gg==" size=75 + GET State "AQAAAAAAAADAj15INDldJjr8zmkR+JC21MQQHNKkKUhsw03lVrkRpw==" size=107 + GET State "AQAAAAAAAADrAmQNM+YwkbWYyrfgMR6d+cPSweN/wX4QPmSiAyBjNg==" size=72 + GET State "AQAAAAAAAABHt6zJKoRffD4x7M8n07NEiUUwi3jK/qlvVGbOd5K/eg==" size=491 + GET State "AQAAAAAAAAByxxpgSreJMwDXlINcuHaqnPXKlr3r1KAZrDMkL51Dow==" size=139 + GET State "AQAAAAAAAABL6HEIgQcQS4nbGn+E/vgojcH88Kwo0coYMZcdlSWgxg==" size=491 + GET State "AQAAAAAAAAB/2auIG5FbrVjR9If1ZKHlzZsDtVBHYkzGGWSM1DhIIQ==" size=139 + GET State "AQAAAAAAAACtSTTI5fy+Iv0hJ7tXpFYV2/uhiKRb+eewwB+xWiWJ7A==" size=331 + GET State "AQAAAAAAAAAT5wkybgLYpXaSE4soZPMz9xVs8MRowI4tSuTUcBqdog==" size=75 + GET State "AQAAAAAAAAALIWe6onlgKlsloDDlR3NL9OKqAPDeR68plW048PhHgA==" size=331 + GET State "AQAAAAAAAABRwIIhTI2/NO0NuapW7WVL/4MikGsgI19MSPobXzEpFQ==" size=75 + GET State "AQAAAAAAAADDGYlnrr+UvzECC2jHgwxjhQMIQcURfe68u0njt+77Hg==" size=139 + GET State "AQAAAAAAAADCtidBqtFoGVa51RfvofCUq/OfxKjFWLj3dLOXL7sssQ==" size=75 + GET State "AQAAAAAAAACx613PP+qAHRWeRU729LqC6dITCLp7LMeMjXxYVLDqlw==" size=141 + GET State "AQAAAAAAAAAZzxjxju5DJAAcDgyNhVQAFAxJ//Zygke6Mupp7+lU6g==" size=9 + process_receipt receipt_id=G2AYqug5LN3FS2SN9GPEQERkVpK9dY6g5hQgcnSArswd predecessor=system receiver=91505032331ede720873cf84a36609dd8755a09f816735a60ffa3ee40b4a4a67 id=G2AYqug5LN3FS2SN9GPEQERkVpK9dY6g5hQgcnSArswd shard_cache_miss=26 shard_cache_hit=6 + GET State "AQAAAAAAAACJJODAbyoagvN0MXyBfwOD3XIGB+b/k/6VXW71wH8djw==" size=331 + GET State "AQAAAAAAAACycYPVTSMXT7x5PTRVxgkWw5b9Etx3X4UuaWtqmjJamQ==" size=171 + GET State "AQAAAAAAAAAQ2JT3RdALKm1Bi6jq7/fIYeB5sMV+0zh1ZRSQJE8tUw==" size=331 + GET State "AQAAAAAAAAD9pbcrejFfia9Dpq26RzfASuTOHYmGdNpgN8mlbMXrzg==" size=139 + GET State "AQAAAAAAAADftWB0LTWWzxnjIfjz38qEDgiQgdnBmcPwEuwKTqKyJA==" size=331 + GET State "AQAAAAAAAABBJMtbQkXYUtCpA83vPJHtzBXpzpCiLPQyrEDDFucuYQ==" size=75 + GET State "AQAAAAAAAABcmN308NvU0N7cww7xYecJ0igpypoO36UYTTk6SGqeTA==" size=331 + GET State "AQAAAAAAAAD6OI++ocrjqxY1QTjoo9B/1KlzEJnXbi2slSn4ZITCPg==" size=75 + GET State "AQAAAAAAAACi1Hs+dI3QJD0kvGDvVxoUaVUVhpTn9ZWnX3HxBd5QVA==" size=299 + GET State "AQAAAAAAAAACwX1feOj7+z+25AFqilK8FhXxlm9cPvSzCsDGI7PMUQ==" size=75 + GET State "AQAAAAAAAABuzEBDf4SU08n4jS6NAeDS85N43+MypkgcvP4u0dm+pw==" size=75 + GET State "AQAAAAAAAADA83qvCcHi4oYRX4nq6OcQSOcrkXMTRQs7vxjIMbx7cw==" size=107 + GET State "AQAAAAAAAACJbXZfsybfjy7mCMrh4AkHB8t0WgDazEujvlbrnvie6g==" size=72 + GET State "AQAAAAAAAABMKEKpzzhMSgP3803LjnLFDNKOtRh/UQXeoiW10nDGbg==" size=331 + GET State "AQAAAAAAAABXgChLE7oMysEQlaKpq8j7VdPdRuNzJiRFez8EJ7n3qQ==" size=171 + GET State "AQAAAAAAAABf0Aa/b9szTg5tbk0QdrnATbWtduKftXVT6bDU5j9dGA==" size=331 + GET State "AQAAAAAAAACtbMHAtsfUxfxw5jgH9v5zt4rnUEA4yyuqxP+b4PCJfA==" size=139 + GET State "AQAAAAAAAACc90/HztE91hYT+jOv5UCt0YKQXjtki1CwHg1DVWC6jw==" size=331 + GET State "AQAAAAAAAAAqcsMsrLhaVmzTixbQ5U+nrMHZr/WfUCaYQPP5S//HqA==" size=75 + GET State "AQAAAAAAAAA1zm4TotYi+wyLwJcQV7b943N1pqosoGY+CXv8q+4mZg==" size=331 + GET State "AQAAAAAAAABrlIcKtWsaoymdmsqLKpwKOmevM/Z62Tch9ryzic3RUg==" size=75 + GET State "AQAAAAAAAABJ0Ib/GoCH/cWTOdeMXGnImJWlHRnLihGCW1z7LeyRhw==" size=299 + GET State "AQAAAAAAAAAhA1fqZpGtuqCa9kXsfO+1+owYhmIw7ndVK34XU5jRkQ==" size=75 + GET State "AQAAAAAAAAD1UJDtm02N7rY2/6zg93j3cg2Rzc0buhdB1E7T0wEZow==" size=75 + GET State "AQAAAAAAAAAUERRbp64vUCSkqMmIMgHehigwk41aR4lYEEHDvO8UHQ==" size=141 + GET State "AQAAAAAAAADbDdO4fct++y7vdA6kKrqgSAqdcxrYVHS+DqKreJFCOw==" size=9 + process_receipt receipt_id=88WWQXk4nrVqe2si34jS9fhouD73uaGHU5f994c46B9G predecessor=system receiver=6b0021d947b74d8ab24d379f80803c5224767d3777c3d92899806dc5cecc2f77 id=88WWQXk4nrVqe2si34jS9fhouD73uaGHU5f994c46B9G shard_cache_miss=20 shard_cache_hit=8 + GET State "AQAAAAAAAABc7UGiWMRYQWjIG/6gqVRfzPRZrMTha+i+Qqju3VKfdg==" size=139 + GET State "AQAAAAAAAAC8WkosI6UQ4Ybsx9iChU7h3nukVuHO4mOnnWVMDUm01A==" size=331 + GET State "AQAAAAAAAADfC7lDPRyYRRaj50aBIn6DKbuv8omcrjvbcT04EXbkZA==" size=107 + GET State "AQAAAAAAAAB6AkUkK30aoNfZHcnZDh8PuWpbf5Q8e0oYSaP4vsDxKg==" size=331 + GET State "AQAAAAAAAAAJYa6/0SbrgpcXkvDCVr2zGC5DGvvKoUm1pHm65RNsmQ==" size=75 + GET State "AQAAAAAAAADdpATmg7G4K/l4bqqQFWeR6P+i4IkrnLQaY1RGHsM2KA==" size=331 + GET State "AQAAAAAAAAB5ahVpauazksUHg11uw5U9tRSK1i8zElDmXFTLk4eoig==" size=75 + GET State "AQAAAAAAAADdrwLn0Bh7/BbTy7FUItBEqJMUAPiwdMAPHdfn22kk1A==" size=267 + GET State "AQAAAAAAAACBUlz/LpI2/dVLNmDdxvpP+cTA4unDeT7EtWO6+Ydcrg==" size=108 + GET State "AQAAAAAAAADRr5UdQ+UTjM4+ZFwBuVHNGVRhsoisVRpj5usD4aOCqQ==" size=72 + GET State "AQAAAAAAAAAR3QA2W3/4AGzbLT0eFhWWeM4yRnUZWUH7mnykFlGJkA==" size=139 + GET State "AQAAAAAAAADzCc7yBvTNcCmkNlswAS81ziCBcs09ooIPoQ7o3rM/Eg==" size=331 + GET State "AQAAAAAAAADLftZK6B3nEf8X8Jw8ptI38AQMNI0HYwelSvMoV0JNrw==" size=107 + GET State "AQAAAAAAAADoE3KkoF7v9ddAWxSpx4Mn61Pf3xbOhHXjUUL+8mr8vg==" size=331 + GET State "AQAAAAAAAADPPpAYq8ewqTsGKXQ0d4Hk8w0keE2zN38wRIgKgWLVhQ==" size=75 + GET State "AQAAAAAAAADsbQRQw620HwGvy2xLlB4dAGg8VLSGymnTKMmdgK8yqQ==" size=331 + GET State "AQAAAAAAAAAKaUNSQDyMjkNYYdYAkgkWPgNZVQscAdQSnvbvI7rg5g==" size=75 + GET State "AQAAAAAAAACfQCIkVyJ6+DXU3PXsbifiwdtd5jvQXNWDVYFOWJF4GQ==" size=267 + GET State "AQAAAAAAAABOU4RijaSBGi0Fn0+GghXHqfUFF4K+NnrLAQllmkSW2w==" size=142 + GET State "AQAAAAAAAAA1Kt3FeDqg0FvELyyn2Ak9Lsatqw98uezZE2alePuxnw==" size=9 + GET State "AQAAAAAAAAABGapnVThqNIMLtDUlr7qExIe8A84PhlU4g6R3/qAyrg==" size=56 +GET State "AQAAAAAAAAC6Z420KlQZLBj4oTGb5cdjDIvpC56a2Ms7N/NyQfxWVg==" size=46 +GET State "AQAAAAAAAAD+4bTDfBlnVU6os4yD2tauvaE4iuczebwUY1BMD7IFtA==" size=171 diff --git a/runtime/runtime-params-estimator/res/75220100-75220101.s1.io_trace b/runtime/runtime-params-estimator/res/75220100-75220101.s1.io_trace new file mode 100644 index 000000000..efad0c6dd --- /dev/null +++ b/runtime/runtime-params-estimator/res/75220100-75220101.s1.io_trace @@ -0,0 +1,36 @@ +GET BlockMisc "'GENESIS_JSON_HASH'" size=32 +GET BlockMisc "'GENESIS_STATE_ROOTS'" size=36 +GET EpochInfo "'AGGREGATOR'" size=16400 +GET EpochInfo "`11111111111111111111111111111111`" size=2266 +GET BlockHeight "hMR7BAAAAAA=" size=32 +GET Block "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=7706 +GET EpochInfo "`7XciHBpUitLPSu8QBrsWp6sghvVhwXj7nzjm5FxNZbjn`" size=28482 +GET ChunkExtra "HYEildYa9ga54ruYJ1xPfRDohXpl/k0TpI24NPOuKlQBAAAAAQAAAA==" size=101 +GET Chunks "`4hpzBA6CJtipJqWz9vxW9M24BW6cWh9sQE9sLBXcYRAN`" size=521 +GET Block "`DwSdhqzB18RJAew3XoMKrjHpHyWJdetAbrKFt2AJgSt8`" size=6796 +GET BlockHeader "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=6293 +GET IncomingReceipts "HYEildYa9ga54ruYJ1xPfRDohXpl/k0TpI24NPOuKlQBAAAAAAAAAA==" size=364 +GET BlockHeader "`DwSdhqzB18RJAew3XoMKrjHpHyWJdetAbrKFt2AJgSt8`" size=5383 +GET BlockInfo "`DwSdhqzB18RJAew3XoMKrjHpHyWJdetAbrKFt2AJgSt8`" size=221 +GET BlockInfo "`9TxV9KzgAaxqVcQPmh6tce9Dy1XQXWgDNyatRG3HBS9`" size=302 +GET BlockInfo "`FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR`" size=221 +GET EpochInfo "`235gTuzMxiALmnyykDmZmhoK1JKgoNkUyegQMjirTVwM`" size=36184 +apply_transactions shard_id=1 + process_state_update + apply num_transactions=0 shard_cache_miss=4 shard_cache_hit=9 + GET State "AQAAAAEAAABm9MM1w7gSH9LRbO8R5aV7EItNZH52gbY2DLiFjC21hA==" size=46 + GET State "AQAAAAEAAABvWoZUnIg1q5ytq0zR9Jh4thXOAiliK2xiuRJqWv/1aw==" size=171 + GET State "AQAAAAEAAADXHzG4DYj4HS2N3TODVKd/NzekVNxdyrObNNxmqWjcng==" size=50 + GET State "AQAAAAEAAAA2jfkPJ0NdXyW/qKL2I1VUM8GHECAwjOIjzbb1vLBPTw==" size=16 +GET BlockHeight "hcR7BAAAAAA=" size=32 +GET Block "`B4epTA1V54DmRbFhFEaLyyzLBQuqq688bHNq3sNRDnTF`" size=7771 +GET ChunkExtra "lYRfbjKj7tNaKloWPka4gdGd4rVxVOL4e1KqjqSwTkYBAAAAAQAAAA==" size=101 +GET Chunks "`C9mtizUsXRLGy9KG7voyLsshWxuJHXPkQdD6wWinModM`" size=368 +GET Block "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=7706 +GET BlockHeader "`B4epTA1V54DmRbFhFEaLyyzLBQuqq688bHNq3sNRDnTF`" size=6358 +GET IncomingReceipts "lYRfbjKj7tNaKloWPka4gdGd4rVxVOL4e1KqjqSwTkYBAAAAAAAAAA==" size=364 +GET BlockHeader "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=6293 +GET BlockInfo "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=221 +apply_transactions shard_id=1 + process_state_update + apply num_transactions=0 shard_cache_hit=13 diff --git a/runtime/runtime-params-estimator/res/75220100-75220101.s2.io_trace b/runtime/runtime-params-estimator/res/75220100-75220101.s2.io_trace new file mode 100644 index 000000000..67758506c --- /dev/null +++ b/runtime/runtime-params-estimator/res/75220100-75220101.s2.io_trace @@ -0,0 +1,283 @@ +GET BlockMisc "'GENESIS_JSON_HASH'" size=32 +GET BlockMisc "'GENESIS_STATE_ROOTS'" size=36 +GET EpochInfo "'AGGREGATOR'" size=16400 +GET EpochInfo "`11111111111111111111111111111111`" size=2266 +GET BlockHeight "hMR7BAAAAAA=" size=32 +GET Block "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=7706 +GET EpochInfo "`7XciHBpUitLPSu8QBrsWp6sghvVhwXj7nzjm5FxNZbjn`" size=28482 +GET ChunkExtra "HYEildYa9ga54ruYJ1xPfRDohXpl/k0TpI24NPOuKlQBAAAAAgAAAA==" size=101 +GET Chunks "`BwPbs8oZGoJUqa99XixibpLPq1tzoT9WLbaviQMvFPGD`" size=1573 +GET Block "`DwSdhqzB18RJAew3XoMKrjHpHyWJdetAbrKFt2AJgSt8`" size=6796 +GET BlockHeader "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=6293 +GET IncomingReceipts "HYEildYa9ga54ruYJ1xPfRDohXpl/k0TpI24NPOuKlQCAAAAAAAAAA==" size=621 +GET BlockHeader "`DwSdhqzB18RJAew3XoMKrjHpHyWJdetAbrKFt2AJgSt8`" size=5383 +GET BlockInfo "`DwSdhqzB18RJAew3XoMKrjHpHyWJdetAbrKFt2AJgSt8`" size=221 +GET BlockInfo "`9TxV9KzgAaxqVcQPmh6tce9Dy1XQXWgDNyatRG3HBS9`" size=302 +GET BlockInfo "`FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR`" size=221 +GET EpochInfo "`235gTuzMxiALmnyykDmZmhoK1JKgoNkUyegQMjirTVwM`" size=36184 +apply_transactions shard_id=2 + process_state_update + apply num_transactions=2 shard_cache_miss=2 shard_cache_hit=144 + process_transaction tx_hash=C9PVvnuVYGk5b7aYUYMRVxvaZVF4agPiZcTAWyKBrndT shard_cache_miss=34 shard_cache_hit=2 + GET State "AQAAAAIAAAA3Z8xF+7+rhuGimb7uGYJO9LjJMnWV8POS3FHNV4elhw==" size=46 + GET State "AQAAAAIAAAD+Ywfe+j1OkrdChIVR7ex2aG9k+VVw34WS2ypxV9dfyQ==" size=171 + GET State "AQAAAAIAAABArnFbh7ENVYAF9/77aWXFqHLjv1erKkqspDu0tLDDHw==" size=46 + GET State "AQAAAAIAAACL2HYcdtaSqPNL3Hjnzb0fdShyZbD4GHPvGTWmW/Fouw==" size=363 + GET State "AQAAAAIAAACo6ZhGeNyILyri/y4MMslpWRqrx2wjQsWlLxQJzNnShQ==" size=171 + GET State "AQAAAAIAAAD1Y6LvmT9tRAEe7J/U2+i8b3qRJ6ya3b++6pMq9pfdBg==" size=331 + GET State "AQAAAAIAAACHA0p0w4gIZbvKAY1iuoq/zM2QxGJQ6AofsZL2HMCtAg==" size=139 + GET State "AQAAAAIAAADAGwJf/rp9PgXEOaVzyZLGDlxgBjkxPeeqOgHwtR0QdQ==" size=331 + GET State "AQAAAAIAAADysEnhOOXo75ji1f8gie6+5uP1BLhbDbHmJUlo4sM43A==" size=107 + GET State "AQAAAAIAAAAish57rrhi+3D68kWedFThKkVX+wzzCKUMqMgRYQobUQ==" size=267 + GET State "AQAAAAIAAAD5/r+36AVhOcMTWEfa2qlyHoPa8i8elvPKfnpdFpH5Iw==" size=75 + GET State "AQAAAAIAAACiVgKgEOzYV7vWfwDzTtAFFiyuR0lVt4LokVUTYO3few==" size=331 + GET State "AQAAAAIAAAB0tiRvMX7ynwqHziE1BxSX/E+BB0XjOkP/gdvl01OI2Q==" size=75 + GET State "AQAAAAIAAAAGl1NTVKLZ7B2JTIMls59VGkEevoD4ZtWF9Ncf1WQfeA==" size=267 + GET State "AQAAAAIAAACjPhSJ5NWdeQ81TXi9OTvw3lY8pYGQGuzns/vuO7hjTg==" size=46 + GET State "AQAAAAIAAADG7sBgwMkYqaLiWYfHOsyOGhIx4ViR+4CpPMPDLlf0zw==" size=75 + GET State "AQAAAAIAAAABHSfbow3wR9B8OCjG58+MRBiErlJHvGDc4eAuIfrGQQ==" size=107 + GET State "AQAAAAIAAAB4txEfEdZBVRDcCWbwstDFrd28je+WdNgTqQ1NKB7Ylg==" size=72 + GET State "AQAAAAIAAACtXw+PsX6ypptAGrEnn97vLNp70/lI4pzT46xXdNPhuw==" size=46 + GET State "AQAAAAIAAADMcvDUshGAbo2lq514euOFP17pSbDlZcH1cd67Ljzlbw==" size=363 + GET State "AQAAAAIAAACBQjG+DcLEQOoQVvudXtdbOROnMEWxiNrt1q+bBsiXTQ==" size=171 + GET State "AQAAAAIAAAABeyLuKCFu2Kpjkk2z7BfWxXj65GcY38BtoRRoXFOMCg==" size=331 + GET State "AQAAAAIAAADvR/fm+1S5gWoY6g/igj7AgtRNRsZW8flhLkY6bhPRyQ==" size=139 + GET State "AQAAAAIAAABWeTlrSEmISzkx01JED5RB5mCEi4TLV6pEzrCOhsuFKg==" size=331 + GET State "AQAAAAIAAACAhq6Zon+k1+lx9pw2LapJzhXf4wJ6Tc8F2g+F+oZG4g==" size=107 + GET State "AQAAAAIAAADngGjixiRNEyVqqzhtVe2K+jJcKiw8k3aMWoVMYTz6QA==" size=267 + GET State "AQAAAAIAAADruA1QZWTTqZ3aHLDgtgFJUk7MS566AQ67I5P+2q0TPA==" size=75 + GET State "AQAAAAIAAAD4ZGF1l567aeZiS8jmiQsu4QCwGJeMLro1hD3xf/MznQ==" size=331 + GET State "AQAAAAIAAADoHBZH+kdYvwLfWFFXmbe7MVhQuk3ZxohMKrE0P8qs7Q==" size=75 + GET State "AQAAAAIAAADoJbrnrZu1iwPiTIQEb9O5LGkN3TePxcC3IhQpINg3rg==" size=267 + GET State "AQAAAAIAAAC0x4dlpPizNdlu4Tem1+OAHfbs2LE9P15sHQMXObHS9A==" size=46 + GET State "AQAAAAIAAABuC33YT/gDE76bHt7LPttAI2OlMXcFRNvND2fuOkqsRA==" size=75 + GET State "AQAAAAIAAABlH4F7OK6UKDpYnd6KZmlOTjRVNjE9Xln68t7VxcvgWA==" size=141 + GET State "AQAAAAIAAABoi+S4TucTQzBYCq8KCLeOo8MonggrATHMYAJQvNNwHw==" size=9 + process_transaction tx_hash=7kRLAD7JBKVuZbD2VpSCN82G7vmCtmXGPcC56XaE2Wdw shard_cache_hit=8 shard_cache_miss=28 + GET State "AQAAAAIAAADURYJO2wu3vJRiuF69zsxRgB7fLhtDX//aBITZBIGXrg==" size=171 + GET State "AQAAAAIAAAB8LIKZMD5mjs+A9Jzgab3qrQeRee7KiF2c/KCx2awmMw==" size=491 + GET State "AQAAAAIAAABoWQhXfj+bCl4dJ/5T4S5iKmH7P8cUwgsZAW/AZgF5wQ==" size=171 + GET State "AQAAAAIAAAAVkA6RCgqcytGfpGLzHsZ3w/+M735y9NqzJfdnJA8XUQ==" size=491 + GET State "AQAAAAIAAAD4eiNPjVrNmpPjpD/A93Xza26x9M4FyhJ2z9zDauTjJw==" size=139 + GET State "AQAAAAIAAABOV0WW7CwTJyODUDLjftYmd1henV5Ctp97+MOmkDJbIg==" size=331 + GET State "AQAAAAIAAAD03S6rLMKOf2abeBsHFvVA3XBXp2ffuCRCv8ILslr9VQ==" size=75 + GET State "AQAAAAIAAADZz9prZOFCKz7vy3rVCb0tQPJeOQr7CJv+sOsZQZUUPA==" size=331 + GET State "AQAAAAIAAAB3nwpi5unEM0BRmNo8yxYM1jhixCWDNbpPhCST3hGs3w==" size=75 + GET State "AQAAAAIAAADXeCjFKQM2zJUjklhfZoN98ERXuIbrBfgscxPtzRREVA==" size=139 + GET State "AQAAAAIAAAC4H54zhMm8Lk794acV9mMzorTg5rkwhOEkohlLrkxXtg==" size=75 + GET State "AQAAAAIAAADJUPsAD28GF5woncb5nrsaypAkPu7h26kdN0N5fDLthg==" size=75 + GET State "AQAAAAIAAADktNTsSTk8ug58mOqBeXYwMnYmRRpPHuBNXB6y2Pj3Lw==" size=107 + GET State "AQAAAAIAAADamj+vFwd9JY8ynPLkV1IMQyNnhh7UhZdomW46wiRw5w==" size=72 + GET State "AQAAAAIAAAA+z6nZEpoTaXBFUAFQCCKDjwHN1DWGC/NuYC+Bf986tA==" size=171 + GET State "AQAAAAIAAACtm4HXlf5Nfh8+kcbIuBNKR+HwpepiROM0LV+RlmQkuw==" size=491 + GET State "AQAAAAIAAACNR02+NLgRj0EHRb+VzsIIVxL0/YBrQ29ndJClR6HlVg==" size=171 + GET State "AQAAAAIAAACNlAWss5ofr7Q+t1q2mv8L3fFzIxHrKJL5rgmfUaNwMw==" size=491 + GET State "AQAAAAIAAAD+W7zHkVgugoNb1Jw6iRU4tStK/28F0AVv4Tg83xpQ1w==" size=139 + GET State "AQAAAAIAAACrWFvpZ2urlwJ+4alAopy5STv50jah3JygJSNH4hxYsQ==" size=331 + GET State "AQAAAAIAAADceXOqccMV4wOyd2p0RKh0J37RAFMH2Tq+NjJJlPtFZQ==" size=75 + GET State "AQAAAAIAAADx6/ZTYnOtZlE8FXw+l94K3T4sUmUwBokJuJKQ5nKRRA==" size=331 + GET State "AQAAAAIAAABNA5wuZ3KR5ARWWN+JhkU1scEyp6cl3WVrZPF0XFUNtQ==" size=75 + GET State "AQAAAAIAAABpwg8JqBCHLqcrHCN71sjWl1/dPmgkb/Putnbl8C+rog==" size=139 + GET State "AQAAAAIAAAALBZBGtHdru0GC0pbp9awDgGc0KxoNsQAcqah5fCD3bw==" size=75 + GET State "AQAAAAIAAAAD7Nw3Wiybznb4KvF27314LFnK4AUyEmu73SuvgPhbJA==" size=75 + GET State "AQAAAAIAAAA1ySh34bpw7EvJ/8j+79z/dSTYqkL7CsxrfEmdNGyBGw==" size=141 + GET State "AQAAAAIAAAAdo7vQXPKLxYOWjXxX5/7wVOL6AswvnKqDLARLf/Ao1Q==" size=9 + GET State "AQAAAAIAAAC0rQKUVHfohAi26vWAWsztM9RXKZByAALyKCzPZyX6oA==" size=50 + GET State "AQAAAAIAAAC5aBSr+axzeitiUMubmlesmivpozhsKlEvXWHdEzkKjg==" size=16 + process_receipt receipt_id=aCqt8Sq8HwJrj6pyeRJmt9PSMJtGBFj38bs9rd1EBuz predecessor=system receiver=ccbca04bf1056af531129685cc7a372eb6df6bc38bc1d050f5eafea8faa979b1 id=aCqt8Sq8HwJrj6pyeRJmt9PSMJtGBFj38bs9rd1EBuz shard_cache_miss=30 shard_cache_hit=8 + GET State "AQAAAAIAAADasjKb7N+AwEia/iibVXqaq9QdWN2ELDDYz2LEGtz43w==" size=171 + GET State "AQAAAAIAAACotoAKOHliyD2XdJSvkWhybmEOl9nQpnzwKGwcOlZ32w==" size=491 + GET State "AQAAAAIAAADtLP1Qfzi030GOdN0Kschc2Sx/k5AP1MM9JlvU9rlqrQ==" size=171 + GET State "AQAAAAIAAACoWE1U1CqE3hUft02bcwv9ZMZIrnZKH5PrCr3gY8y0og==" size=491 + GET State "AQAAAAIAAABaiyEgV6m/i/P3cjiNGobZEV58AeUsBmQKjLDOykj2Cw==" size=139 + GET State "AQAAAAIAAADSXyCtdluA0GnhqvAg5L+V0HEu3DtuE8Hhk6nSHBdwwQ==" size=491 + GET State "AQAAAAIAAACDr/VJs6oDQ3ON+l+rJ7uEOKkMH433leeo7E3U5ZN/xA==" size=107 + GET State "AQAAAAIAAACXjQcczJJeApdm+2JQltlQ0/dJEysCGPihF7wGZE2Tyw==" size=267 + GET State "AQAAAAIAAAAgimRz0rTnqVLbfISiEvyT1ZwNXfG85pKiVbHrZQZCZA==" size=107 + GET State "AQAAAAIAAACYDI8JHI235q5MwqsKJ9CyX9k3U2xc1DZprpPO1phVgw==" size=235 + GET State "AQAAAAIAAACrRfEI89DjVZ43vAKA6QSr4KG64rWwI0q5PjRn6t23hg==" size=46 + GET State "AQAAAAIAAAAGj1AYbhdZ4MkL29xACPTri9mDm0mb5wCf63EV6Yz/0g==" size=75 + GET State "AQAAAAIAAABD9f+sYrzQq8i+BsYbKDCodZUFCk4wZ0dFzpydEqnUXA==" size=107 + GET State "AQAAAAIAAAAu4csHNzx6YySz8kDK0gIXhwp+hlu1Y3h1fJsyg1oJpw==" size=72 + GET State "AQAAAAIAAADEQOV481CS7or72Igpx/a0cIMh/+Az2IjrJpOmPzpn9w==" size=171 + GET State "AQAAAAIAAABzEyH1CSw5XPwFHIVLCZzrR8AjKdvFFvDUDyZKrI+SUQ==" size=491 + GET State "AQAAAAIAAADMCA3Yax8ObAzIMCUpFFhW/vM4R1HDX2HaIDXz0Cp3Nw==" size=171 + GET State "AQAAAAIAAAAFVuI/VuWhFZuHVPUii47iZM+mpZBiI/HTjXlnu/vAcw==" size=491 + GET State "AQAAAAIAAABM8b24FTzLaLiElcN3/mfF8qWug10uUeuR9DFE2CqSrg==" size=139 + GET State "AQAAAAIAAAAMiU59fP3HdZjPg/f4DtvJY4MOjccECF8iUcp4ublceQ==" size=491 + GET State "AQAAAAIAAADu+jIAfoGZkCaT7dwgu/dOjKVL0gVqYgxoa7DxjTCMeg==" size=107 + GET State "AQAAAAIAAAC5qsze4OecGc/dV3nNTpiifF8VaTaWNv8BYVJK+VzXMg==" size=267 + GET State "AQAAAAIAAADVaIWf5hpObpOuR0yJzVcDkUULKf0KXPR2T9p8pDzeew==" size=107 + GET State "AQAAAAIAAACdSxA5k4/jyDyCInHatwNd6PHVJM7rxYger9zs/ePDWQ==" size=235 + GET State "AQAAAAIAAADKDIp/g6A4jsjeNdEdmLNVxirfUfognseW3hCwhp5Lzg==" size=46 + GET State "AQAAAAIAAABwCpVt68Mapxbqk6nn/+j5rzk63q5kho5T1Y+FQujC6A==" size=75 + GET State "AQAAAAIAAABWst5h0WXYTLv81odzaXToI7jXtBarQiNd9CUCuecDug==" size=105 + GET State "AQAAAAIAAAAzhOdRdatc8g94qx2q2OxrRsmbQpGorOkXj+IttlxFOg==" size=75 + GET State "AQAAAAIAAABgfc44F0O6pdK7Cmm69XDM8o2BeGpUPkYjmvN6ni0vzA==" size=81 + GET State "AQAAAAIAAACmXBwyQWnPB4fqHC3InFBfOqpzHAmrYhjR8fZK4fYrKQ==" size=9 +GET State "AQAAAAIAAADHdi/c6M3SWbhw+zpZ7WAQmPMAa0hLmWzulLAoA9aKyQ==" size=46 +GET State "AQAAAAIAAABVPi4LBy8PVvsVtNNWAu8J0Yb+5ssseY8hceJ9Y0hoTA==" size=171 +GET BlockHeight "hcR7BAAAAAA=" size=32 +GET Block "`B4epTA1V54DmRbFhFEaLyyzLBQuqq688bHNq3sNRDnTF`" size=7771 +GET ChunkExtra "lYRfbjKj7tNaKloWPka4gdGd4rVxVOL4e1KqjqSwTkYBAAAAAgAAAA==" size=101 +GET Chunks "`yokRsSyou8yEKNi8zPCUNGZp5u8tKjU7tAnuuMwvueK`" size=2110 +GET Block "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=7706 +GET BlockHeader "`B4epTA1V54DmRbFhFEaLyyzLBQuqq688bHNq3sNRDnTF`" size=6358 +GET IncomingReceipts "lYRfbjKj7tNaKloWPka4gdGd4rVxVOL4e1KqjqSwTkYCAAAAAAAAAA==" size=1031 +GET BlockHeader "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=6293 +GET BlockInfo "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=221 +apply_transactions shard_id=2 + process_state_update + apply num_transactions=3 shard_cache_hit=245 + process_transaction tx_hash=Fkz3BnfKsTXcRhnkxHkGnEc8Vis48PakuGR7oKmWcGsi shard_cache_hit=4 shard_cache_miss=29 + GET State "AQAAAAIAAABjLRooc780Wmlz+4aBDygX7Gf2uFiXk6BtHrLb9U22yw==" size=46 + GET State "AQAAAAIAAADeaghtkCsVzvxo2xWE1VQnwi7EpC57Q/JQBvk/lWBlog==" size=363 + GET State "AQAAAAIAAAB5ai+GvXEFBj7l13y6oQecc8ydD1/ewfHre+hZjg7j2w==" size=171 + GET State "AQAAAAIAAAC11eCTGjJwZZpOKinP2ON2IW+EWo8zTH0bZhvfNE5lKg==" size=491 + GET State "AQAAAAIAAACyFhzRiuI0QbSK5o+LAL9SLtFR1BwEouLeqkkheFg13w==" size=171 + GET State "AQAAAAIAAADiuQdPpyp7JRmOarSSaZDCz/a5BC2QHPrZAlBdLQ6PJQ==" size=363 + GET State "AQAAAAIAAADdJI1sBdgK0y/zvjQSfokEsCAW97VsGLmEnAYdWYB1Qg==" size=139 + GET State "AQAAAAIAAABNWLsc/q31yCBVLa+1IqIccYJnobytxIeYiCnjdKTrbw==" size=363 + GET State "AQAAAAIAAAAvJjpxfs/twXvhywV+ePvpDAEapRVcyBcUUkcNwInrKA==" size=107 + GET State "AQAAAAIAAAAkiyZyGUMqeHscc4ZBcBEEktVQANbMrQsSmDthtsUXfg==" size=107 + GET State "AQAAAAIAAACW8yLtoAplwzYenBweyUyHz6RwBN1/EFjxxoOVf92/FQ==" size=107 + GET State "AQAAAAIAAADpa3LtzKF2XG6WwxrxU4cR0mHeOjY+rbne/yU2mkXGzQ==" size=54 + GET State "AQAAAAIAAACbopiV9JAQKxycxiWQ4upwDwv/gdTsUw9FAkl3AZGLfg==" size=72 + GET State "AQAAAAIAAABD6dR7KJKmQXuEWgO1YHPoQ6I1qmKq3VEdiSlYP/mK3w==" size=46 + GET State "AQAAAAIAAAAmszNKihuY0FXlA7l6QUgcB8lNWQnociBmREPLLvj9BQ==" size=363 + GET State "AQAAAAIAAAC2DYCwMiDECNFsPV5ciu+7ttqf/KUAhTlkDU5lkkG7/Q==" size=171 + GET State "AQAAAAIAAABBxv2S1GTrVjR57KneAx3gUpIlMPqrpDzXyW11O3kmig==" size=491 + GET State "AQAAAAIAAACOgzELKVLL/F+dWIm5jZaCTnEHgotJQ1jyGz/r0D2rXg==" size=171 + GET State "AQAAAAIAAADgTIF6jcFpZDQZsbNJxhTEyG+KVH87vPikTFPdDXwZ5Q==" size=363 + GET State "AQAAAAIAAAAMuaE2OktqRHGemqu5NXWYCc4aLc0PHxl91K+UsfjO9A==" size=139 + GET State "AQAAAAIAAABdAWf2z3Ky38WLesLq/Cg74mBgDsSmgx/wZJFxbHDH9A==" size=363 + GET State "AQAAAAIAAAAokgpbBCLoWSn5otP1iV3xYQENmg7Bk1uI1xLQGOyvDg==" size=107 + GET State "AQAAAAIAAAA1tGzwwfoR77NSFaAaKjQ5+7Ok0uj4AO2DPy6qSCAyMg==" size=107 + GET State "AQAAAAIAAAAFhs544DeR269X+QtjdaQ+Ztns3XVF2EZn8MQCvygHcA==" size=107 + GET State "AQAAAAIAAAAAWnMcOz/pXwLKwz7ZeCGN6jkrh03P2jEpy9rvXIu7Hw==" size=52 + GET State "AQAAAAIAAADV3O6WZqw5vd8rrCPB3kMgSqc7p501n+2ALI817XHbOQ==" size=491 + GET State "AQAAAAIAAABce9wf+3NUtpBOszMlMeMKwkQoOYT+UVYgS5ki2t3wuQ==" size=139 + GET State "AQAAAAIAAADHbcpinxq5IUfoUqFf0Iu2jiIIcYB4pnjjOm7rbrcaaQ==" size=81 + GET State "AQAAAAIAAAB4GLW0vnmNOPO/oO1ah+g0WTiWn/3NVeFU68bQI+OntQ==" size=52 + process_transaction tx_hash=GQWnDgrcFaLK6fbQ9p5sfHJZ4SYMsyrJDnhWY7HCRjde shard_cache_hit=8 shard_cache_miss=28 + GET State "AQAAAAIAAADRnNUntYDOQnW7y71htCwxoWyqU6tCX3yQ92h0Oe9n5g==" size=171 + GET State "AQAAAAIAAADUS3CZCuoSRSeJcUg4zRPtGdV1N7fIwsZlcU38riPEjQ==" size=331 + GET State "AQAAAAIAAADzYj68RoerddxIjKkOsvYQ/35q4uzHWORKgPCl2GLOeQ==" size=139 + GET State "AQAAAAIAAAAEsaK+N14gC6L15Mo31Ev8GDecoF2qltnZ4iZLKMOLzQ==" size=331 + GET State "AQAAAAIAAACM3Gc3nTXk/B1OSNT+ghnasQo/ob4IUhj/RV0/vG6cSA==" size=139 + GET State "AQAAAAIAAABgukQMpnu6/QG9gMV6p43Q59OtMi0Nl13wHwREQghrYw==" size=267 + GET State "AQAAAAIAAAD/d5WDG8N/Qedq3EChtANq1fGC/RHPk1BCfJbudKTc9w==" size=75 + GET State "AQAAAAIAAAAbcIVlnoIedXkb36NcoIFQnSXIAsAOZhluc5noH+VKXA==" size=331 + GET State "AQAAAAIAAABR7K5PFEyUdgdAGeLumXmGy6ApogB41A7NdcMX6rlz7w==" size=75 + GET State "AQAAAAIAAAC8Dm7yuFBHtd9GJpvol1LZwtMTZC7P8EyWFKdVBpBilA==" size=235 + GET State "AQAAAAIAAABWpAfwgY0S8ui8pMSl9W/qox2BolWFJfWpzqvhR8zaRw==" size=46 + GET State "AQAAAAIAAADBgGyeAJmro/YzXgy+T/ytrZS6A6xS9BnG0/qfqXTqtQ==" size=75 + GET State "AQAAAAIAAADry3wTj7kUOIbh48A3vjH2qPWee8RSbiftmWIfK8p6hQ==" size=107 + GET State "AQAAAAIAAAAN695zlJABzoqXOqLJ5szWP5eJqDTOBFsXWQ7Ag35CzA==" size=72 + GET State "AQAAAAIAAAD96gQFrggeBAmiSjVh3K+ge497ZGuXkOQpRYZWimzV3w==" size=171 + GET State "AQAAAAIAAABS0AJFUxkljVW4bN7vdg/jvKJl/7derjgCVT63FhbaWw==" size=331 + GET State "AQAAAAIAAACbMWz6xjbBs7lTxXzJG5Trl6UDImgrMckZyAeKjoNTaQ==" size=139 + GET State "AQAAAAIAAADHmpiAiJNecHm2zJtR0iXfykW/+Z3nP/iYnTby3f6dJg==" size=331 + GET State "AQAAAAIAAABrfLPT/B2TwqYexODEjCl2PM73Ulad5OkdZSnj8+XGjA==" size=139 + GET State "AQAAAAIAAAAe87wLQ+6tu+5WhdzZ48Zi3OzHpaB30Gp5b1gDD4JbIQ==" size=267 + GET State "AQAAAAIAAAB4jE/A18dMqOD2osSuDuj6nI/N/N4N4ZkcbG7dW9CZ4w==" size=75 + GET State "AQAAAAIAAAADDO0+qKVWq9dMgQ7kYjk2EordYgQEzb7x0+/x5wLbrw==" size=331 + GET State "AQAAAAIAAADt7SBkEJ6Mhpkln1RDVHX/nwgpBTMMS8rIbxDxuupRDw==" size=75 + GET State "AQAAAAIAAACF/xFCKdFwKdWHs9nTyKNTEExDsvYTIF6OFAQVO/WYgQ==" size=235 + GET State "AQAAAAIAAACeL3Koho64j+n/+O0fIrotlmS69A5Dh1KYQrE5meXYXw==" size=46 + GET State "AQAAAAIAAAAK9nKN7+m2TjxtoSk0en/MceYZnzQCOBrepQP9o+j50w==" size=75 + GET State "AQAAAAIAAABwpoudUj8BIS43Qj48OOur3P9NRCsTx0TE1BooVdzCAQ==" size=141 + GET State "AQAAAAIAAADBblgvUwFyc/gdwGq/6x+8417GqhE5ylHIbgbwXZWQjA==" size=9 + process_transaction tx_hash=Bq4jgcZED8v78974HhVLYbpNfZKDcMAf5tDU47cTjhcT shard_cache_hit=8 shard_cache_miss=28 + GET State "AQAAAAIAAABjMiUQaEBfJj2YUVpUvEaooHovI8L5iADlX1NREnbqiw==" size=171 + GET State "AQAAAAIAAAActX5VjvfxmuY9FoNL0r1YdKAZ9INTNWYGO3p47/NN0A==" size=491 + GET State "AQAAAAIAAAABw5EvenP7LsEGtGeElOaVD252cVbZvcRehuN3ch1aSQ==" size=139 + GET State "AQAAAAIAAAAG+Tc28BKzF6YsZdE1r0bQqC0LoxqsmTBT/SeyzjorZg==" size=491 + GET State "AQAAAAIAAABLXvCNNnOXoene/gL3nz4ultH5kIV/378zk2jkE8ekRQ==" size=139 + GET State "AQAAAAIAAAB4qymuD3bTALcU/1l6bKku8BM6SEor5tXWvW7HXpP6Zw==" size=331 + GET State "AQAAAAIAAACkH36jUQAX3mayF/bwnKdY6YqngrD69COpyt5GP80khA==" size=75 + GET State "AQAAAAIAAAD+Aiam3l6n2Wl6ClXTmL/scwnKqO8zpPkpGIN4MorjGg==" size=203 + GET State "AQAAAAIAAABmz8jssU9NcD3CtBfWH1mAhq+7UXW8Qdg0U+6dQdwQPg==" size=75 + GET State "AQAAAAIAAADUSp0IWjLaPUTiceNzr0iHAF3u1vUleAdVtD6Jn5/Jeg==" size=139 + GET State "AQAAAAIAAAAU7zugvvhuXlU61H8O0mYtkqvMxj8L9AtcnmpEhVb59Q==" size=46 + GET State "AQAAAAIAAAB6MDovzKzp3HAVO0qt9v71CZ1DgblGvd+qpo0VFzGs1g==" size=107 + GET State "AQAAAAIAAAAd10S5hnT9+j4noI0md7Sstn34uep6hcILHfCIJft3bQ==" size=107 + GET State "AQAAAAIAAACQcM6FQ+nNfeffZXGl8kI5rHpcREdOKOys2Cov8I/bKw==" size=72 + GET State "AQAAAAIAAAAf9rE+bo5Smt9aNffABCcUAeQR4KYATWOIzHakcYyWtQ==" size=171 + GET State "AQAAAAIAAAAu2s0lFBNRfrBlIIX7j3pGXjhzlFJRaCgrexF8SKBqtQ==" size=491 + GET State "AQAAAAIAAABafE1qbQfZ8dSBKtSBvLNGmhVxOrO+z63G7VOVYHFbFQ==" size=139 + GET State "AQAAAAIAAACOlfYT0BuaX6X5ED3te5N3WiGiBpE2QOiEZ9UpZsJuDQ==" size=491 + GET State "AQAAAAIAAACPZbvPrXSpBADnnpl2gtRc7sCjW5U1CBVv3uIpOt/zuw==" size=139 + GET State "AQAAAAIAAADPCRSwzu6t46A/OMcyeANXfjJuOGr+rgqd826hBn+Zmw==" size=331 + GET State "AQAAAAIAAADafmdLGr3S6utjgOUx3nGKrS1N4PnYtW6/t80r4ImIoQ==" size=75 + GET State "AQAAAAIAAABQ7rtBsYTgKfkmSVKYm0hEEWKfaaM4/yVM+jV6faAKEQ==" size=203 + GET State "AQAAAAIAAAAa2h34JczohC6lqWoX7+qp8BXDifoaVz4ex3FLt37LeQ==" size=75 + GET State "AQAAAAIAAAAYmAOLrBORzx0m93tZFyVRY0yrTZtv3Yz1noP9Clqhqw==" size=139 + GET State "AQAAAAIAAAD1aWA5sHqHnSXTi6tJ1PbDkYKeEAwj3CV8N32y0LlmgQ==" size=46 + GET State "AQAAAAIAAADAZx+TqISF4/slWA0EwtFjYIvlw+I+zoyffH/sARI+uw==" size=107 + GET State "AQAAAAIAAABAo4TIC1efnCSU0FyUuS1BYEAp/lfGwz/XrVMVaBr2kw==" size=141 + GET State "AQAAAAIAAABM4Xh4IViPxTTqnjWUb9+lHqHnVfzOKyvzJzhBSRqFrg==" size=9 + process_receipt receipt_id=5xT8ygLFj9Zy5jEqg3ZBoMYQtrFTPBf9xu2ApjaRkY2C predecessor=system receiver=ccbca04bf1056af531129685cc7a372eb6df6bc38bc1d050f5eafea8faa979b1 id=5xT8ygLFj9Zy5jEqg3ZBoMYQtrFTPBf9xu2ApjaRkY2C shard_cache_miss=14 shard_cache_hit=24 + GET State "AQAAAAIAAAC/JgvY3rCZPCFxJyGHivx8Zd2QqVvaPYJ4YT4cFpyrxg==" size=171 + GET State "AQAAAAIAAABC0aMZWdWInow1S1SKf009T5Bb3Tupdn9/6ocMHvpa3Q==" size=491 + GET State "AQAAAAIAAADv5yYTdZn+6odj4XnS9sMRAUnOt9bKQL3zWicUhJHUHg==" size=171 + GET State "AQAAAAIAAADdtxsiNfUsRQnEj2phd37cBuZx6BLbFVjD08/W0M/Xcw==" size=491 + GET State "AQAAAAIAAAAg1FUg4QuEV2pFAnSYQnmfDBUS4ISORYl4uLl9B5ypyQ==" size=139 + GET State "AQAAAAIAAAAW0gaNnly7aRH1m/1PvbiukhTEW02pWA6hW/6sDKGk9g==" size=491 + GET State "AQAAAAIAAADBFVm15ED3RulNZoZ7b/ZPCMmFsW3hDVFeOtACdI2kIg==" size=107 + GET State "AQAAAAIAAABE6l7xUHn3f38cgqKuSRsXxqBHanvrkwmaL6oc7JwCBg==" size=267 + GET State "AQAAAAIAAADQVj9eJW7jjk9YHLEc7qFisFAurHn6XTdfz1XrLipxvA==" size=107 + GET State "AQAAAAIAAAAgQP4VpV0PLCE79ppFmEIzZpwul773deJBGgBxWkq8Bg==" size=235 + GET State "AQAAAAIAAAChNtQkZ2m2C7UScuWHNrXs/idPflzrCtU4NneiPmgwkQ==" size=46 + GET State "AQAAAAIAAADmfv7wvCpBrGJFy+mL+pzTjsk2P1JUJ6YXcG5HTLoRCg==" size=75 + GET State "AQAAAAIAAADuEnH+UuDiYtKW0CuSi5liK5JpUBPpaQRf6ugoxLF0aA==" size=107 + GET State "AQAAAAIAAABeexjixWIrTeBJmTF/MHFtOsoDu0p6lqzBcV7h53tzNw==" size=72 + process_receipt receipt_id=7g8RkfXrPdQ9uHqNctnNv2BF9H3xJxv2XN68VjnJczo7 predecessor=system receiver=c_shark.near id=7g8RkfXrPdQ9uHqNctnNv2BF9H3xJxv2XN68VjnJczo7 shard_cache_hit=10 shard_cache_miss=21 + GET State "AQAAAAIAAACkJdKqSLeMpWHx6a9DzI7+7Qq8m0oD7/HYW1G7x1evnw==" size=46 + GET State "AQAAAAIAAADxHFCA8zRUJlrqcnbDxfYfaUthL5l18+qWOlTvSXCDHw==" size=107 + GET State "AQAAAAIAAADhj0eE87gawa7lBqyUkGhiieQV4q5rvdrJ8Ie1Haz6hg==" size=363 + GET State "AQAAAAIAAAA1UMAECAOeToj4i3Wikv0ir++tfTZUm0jUoC8/OxOOAQ==" size=75 + GET State "AQAAAAIAAABq48rmE41gycP3ggtts4WsAHpL1hHN2w9RnMK0cht2dA==" size=75 + GET State "AQAAAAIAAADvPBtBDTH/+qSWOg8f1wX7i/VMKXzQ6wV5nUEkUW4ZLQ==" size=48 + GET State "AQAAAAIAAAAIS8YY9qCE5ldn5gRQm4hse1ZE8c0CjB2qxOJZgfzBJg==" size=75 + GET State "AQAAAAIAAABr22r8kd/t7SqqWSPBEhUIVd/nzXsCOHoL3NGOnJ9ZKg==" size=55 + GET State "AQAAAAIAAAAbwFXsSVht8x03UNAj6vpXxZpndCZZ9xjuViofY+ujjA==" size=72 + GET State "AQAAAAIAAAAfkJkcJrfpqCqNirlPPZnQh2093D0DxQ32CD4N4QEZGQ==" size=46 + GET State "AQAAAAIAAACKOSq/Q9EyCyKg05BajCeTag5+anu3LoVUGQ1iGr/B2A==" size=107 + GET State "AQAAAAIAAABEHA2TvE8Y/H0VUEmcxj+xdWygOVx9ONOtu31ELhn64w==" size=363 + GET State "AQAAAAIAAAB2Km2gA8QN6reb5TJ9NUHgd4Jx+uMPa/8b97qDEA/HpA==" size=75 + GET State "AQAAAAIAAAAjJEwOTSCK9kmzKZYHg1RNai0nd+bgzXEGL0cs1xB1fg==" size=75 + GET State "AQAAAAIAAADQRNyCEGdRx1s9Tly19Zmgc4iiO/BHJhxPz51U800L1g==" size=48 + GET State "AQAAAAIAAAAiu7n8X9zHx2mT5lVc+fCqgTDNDs83YKpP+KjxdCVfmA==" size=75 + GET State "AQAAAAIAAAA9xaeeYQ0K4dEKjs5D8KkaGM9oN8WjIRSzzHP5T9QWNw==" size=53 + GET State "AQAAAAIAAACB3kH/nXnLp2EP5zBm8gens1bUGinerrQt8JCAhjiZLw==" size=267 + GET State "AQAAAAIAAAAveWj61DBYsIS6EC9nIoym3VRrRk+x8Z4nE/m9rEwdLA==" size=75 + GET State "AQAAAAIAAAC/IyMnOpESfcxeQndxXGSPZXhT2uEW0mZ5nlN92eTC2Q==" size=81 + GET State "AQAAAAIAAAClEauoTOFC6XU/8Ec5kRfZ4oaX89XAPjeqYgpJQ/drXA==" size=9 + process_receipt receipt_id=9BS9JPZifS47LLXyLxe4Xi1o7rhdehhC9yr9fSineZ1j predecessor=system receiver=f55649cf4669028d5335fe2156dbce5e32d723ee7e5504f4a23ed16c7ca28aa7 id=9BS9JPZifS47LLXyLxe4Xi1o7rhdehhC9yr9fSineZ1j shard_cache_miss=24 shard_cache_hit=8 + GET State "AQAAAAIAAADhuNdaAYnw5L/ACtu/I3JjUecpETWtR5Xm3S2w5hBseA==" size=171 + GET State "AQAAAAIAAAAouGeqPzCu3bNE+IegVWKVXtHGE0NauBNCO3225DVfRw==" size=331 + GET State "AQAAAAIAAAB4eLzo0zXy5vpFgUQrhLfjKr3Yt3sPoftDp69wIdi8OA==" size=139 + GET State "AQAAAAIAAAAC7Bz1y9WKVZ8eJm1PABmN0+V0mUPsZpCQJ0GbClb9qA==" size=331 + GET State "AQAAAAIAAAAxbD8lQI81f3o06dCl1yvf8/vc640MCjluY2sROGehKg==" size=107 + GET State "AQAAAAIAAABDxrGAVGTZZi+XFbg9R3RaDGJl6Jy4WbkGOkjafP7baA==" size=331 + GET State "AQAAAAIAAABETsdP0G0ctVre/EsW61CVlX/5H1UqJzmu2JAxX8DhTA==" size=75 + GET State "AQAAAAIAAAChtIy8MvzM1sY5Q1mcQ/oPpHtf8bQP8P58tbpBLo19Fw==" size=331 + GET State "AQAAAAIAAAA3/rfS6nognqFxPmOLG6IK2DqKLGztz63lqQSdi/S3sQ==" size=75 + GET State "AQAAAAIAAAC6wvwpi8Gbik69KSolxlERe22v+t8/Z8wANjrRsuvjug==" size=139 + GET State "AQAAAAIAAACB3DoImzZq1Zu2t5pK//ENUxQduGmxKMEASX3WIeDUGA==" size=108 + GET State "AQAAAAIAAACTUykTGPlMWOiPYMO+ZgHz/VPXtQOgbsmFFfaDkQYP6g==" size=72 + GET State "AQAAAAIAAAAEUCTaFkca8fBr/M52N9vBwEEDOFFLmxoZVuiBsbDa6A==" size=171 + GET State "AQAAAAIAAACi6HqXqAVgdn6Tn+ELkwKbhiEtpsdApAh2tQ6/PTGgUw==" size=331 + GET State "AQAAAAIAAADDxq30yZKx7sz8drrUoqnwFG1R8wYJrHg32YyrBBxh1Q==" size=139 + GET State "AQAAAAIAAADasnS8a/1NHmqFDkmLbHbwoXYu+eP86/6sZ2Na1BmUYQ==" size=331 + GET State "AQAAAAIAAADVtbd1rgBHDMA3GNvM8szHjISIwpXpepNVionyoeSlIA==" size=107 + GET State "AQAAAAIAAAD+HFL5qHIKqk6lkxx248pWBYS7B/lnaCaxvnpNjhcRFA==" size=331 + GET State "AQAAAAIAAACE61ymkm92ixNEHBPf5s4bXnZ+k6rbQXqKVrRbCY8LMQ==" size=75 + GET State "AQAAAAIAAADoYdgZajAJ4UkCXuHqH1G0Zb0f/+uqwugtfrn3eiGurQ==" size=331 + GET State "AQAAAAIAAABJFYq4X9FIL3q8sRsvZNur1oOagAOH8Ne1lfAZ9m4Kjw==" size=75 + GET State "AQAAAAIAAAAArXY3W6T1iAeSplwIvN9OiR7t+Lb7Q1rbP4SoETq6qw==" size=139 + GET State "AQAAAAIAAADgYjDMUZZR0tlFR0HNo9vI8FWVvP6lRgrrJNHRznCFRw==" size=142 + GET State "AQAAAAIAAAB32tmsDikaV1cJDsCil/dht3myhazgp9CVpk9pEOzGRw==" size=9 +GET State "AQAAAAIAAACTMl+4gooUAZ8bB4g2Wms9qAHEuD4Zw9TYcxUvAYiNZA==" size=46 +GET State "AQAAAAIAAACRlDGYzc0pdAFpSP1m0lmtsxdsaDdb/pcWo5jJcPF9Ug==" size=171 diff --git a/runtime/runtime-params-estimator/res/75220100-75220101.s3.io_trace b/runtime/runtime-params-estimator/res/75220100-75220101.s3.io_trace new file mode 100644 index 000000000..f59fccf48 --- /dev/null +++ b/runtime/runtime-params-estimator/res/75220100-75220101.s3.io_trace @@ -0,0 +1,1151 @@ +GET BlockMisc "'GENESIS_JSON_HASH'" size=32 +GET BlockMisc "'GENESIS_STATE_ROOTS'" size=36 +GET EpochInfo "'AGGREGATOR'" size=16400 +GET EpochInfo "`11111111111111111111111111111111`" size=2266 +GET BlockHeight "hMR7BAAAAAA=" size=32 +GET Block "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=7706 +GET EpochInfo "`7XciHBpUitLPSu8QBrsWp6sghvVhwXj7nzjm5FxNZbjn`" size=28482 +GET ChunkExtra "HYEildYa9ga54ruYJ1xPfRDohXpl/k0TpI24NPOuKlQBAAAAAwAAAA==" size=101 +GET Chunks "`6eCxqwJBFiXTXEHWcSqBcmxYntRwmrNChH96wB2VdY5Z`" size=4219 +GET Block "`DwSdhqzB18RJAew3XoMKrjHpHyWJdetAbrKFt2AJgSt8`" size=6796 +GET BlockHeader "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=6293 +GET IncomingReceipts "HYEildYa9ga54ruYJ1xPfRDohXpl/k0TpI24NPOuKlQDAAAAAAAAAA==" size=4066 +GET BlockHeader "`DwSdhqzB18RJAew3XoMKrjHpHyWJdetAbrKFt2AJgSt8`" size=5383 +GET BlockInfo "`DwSdhqzB18RJAew3XoMKrjHpHyWJdetAbrKFt2AJgSt8`" size=221 +GET BlockInfo "`9TxV9KzgAaxqVcQPmh6tce9Dy1XQXWgDNyatRG3HBS9`" size=302 +GET BlockInfo "`FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR`" size=221 +GET EpochInfo "`235gTuzMxiALmnyykDmZmhoK1JKgoNkUyegQMjirTVwM`" size=36184 +apply_transactions shard_id=3 + process_state_update + apply num_transactions=3 shard_cache_hit=340 shard_cache_miss=2 + process_transaction tx_hash=AA7U5AQwKV5zCMEqN1317cqwBQkAMRiWtAtoRSNjXEjT shard_cache_miss=32 shard_cache_hit=2 + GET State "AQAAAAMAAAAhhO9DYCju1mgyrMG9ViEENOTK2FyMkr+E/uCohL9VjQ==" size=46 + GET State "AQAAAAMAAACrnjCf54q9eTYSggHHr3EehufqGOP3FchFPp980ePfLw==" size=267 + GET State "AQAAAAMAAADgsKF0Hr5Tj553cp6vZRPUl3Iuvrt7DLQg2IvZOBLOkg==" size=75 + GET State "AQAAAAMAAADupRnyPWjOC+kJzuWSsaF2/KBKqqZzjrz9ISoLyJPrSQ==" size=363 + GET State "AQAAAAMAAAAnlQeI7NoHl/47QhqKN1Q/3ZaWRqBelz28XPCT7rAcog==" size=171 + GET State "AQAAAAMAAADH9sqItuevNHff+DaynrlMQvbM2kxjV4OKn5HZm8gUEA==" size=491 + GET State "AQAAAAMAAADtP8UFWaS/enZmqkqj6ZxrxyBXJkiJ27tf2KjnWgsA3Q==" size=171 + GET State "AQAAAAMAAAAulY48WNRNscFGrzRwQbgOjTVIgre0aVMt6h35zJMCBA==" size=363 + GET State "AQAAAAMAAAASzzW4SG85CeCsDq2iwlni7Cw6t7KjRIkqrRkdzJKbpA==" size=171 + GET State "AQAAAAMAAAAHqareRDokmmCUtYgnRfprQYYkRC+vGg0wN3AwRrhj4w==" size=331 + GET State "AQAAAAMAAAAbE+PaSyzdKx1KgE8+yJjJ0doPKBO9Alev1LseSamlKg==" size=75 + GET State "AQAAAAMAAAA9q6C+Gegtul75A8MCxO1lI0VeXSM/qNG+TKsRvzM/Fg==" size=171 + GET State "AQAAAAMAAAABnTgeb1TF/LC1pt4Twl9X0NXBq41B6eFhDqhBpo8Bgg==" size=75 + GET State "AQAAAAMAAACZ9CH60euTo4uiNGXO++keuk37Pv5QFEJ0spGgMGwziA==" size=107 + GET State "AQAAAAMAAAAp7EoQgsHV7qXumwbjMEdNh9SBFJ5wHIYlYAXi26md1Q==" size=60 + GET State "AQAAAAMAAAAjbsXpNNjP+cAl83aKpv4wTEAF2u9u9NdS0LEylS3VVQ==" size=72 + GET State "AQAAAAMAAABtSyU2gFbipNNMFtgRlOCGLadEwVzU0lPhLMvDJFi1HA==" size=75 + GET State "AQAAAAMAAAArpjKdVTyPSvOLMzPXAgxHZvqX9tBIplisC/M4Cmze0w==" size=363 + GET State "AQAAAAMAAAByhSKs6E2dP8kG/iz+x1ZgTKTj/R6Lgn9Xl2zMSj7VCA==" size=171 + GET State "AQAAAAMAAABFVzGiB6jxkTb7QD49B3pSalNBCizzv5U7IEzg3BAaig==" size=491 + GET State "AQAAAAMAAADfpDy7J8wUZwbVcmTtwRsfQryDjd5UXJliRczMlRV9Ag==" size=171 + GET State "AQAAAAMAAACmMa8CWh/SJAJkTvEFOBxAZD3lNuVALr4A8ZOOxXDz6w==" size=363 + GET State "AQAAAAMAAACTwB0UO4mM8gatVr4Q49Gxdw8ZWrMaMNRDN83UgiGrQQ==" size=171 + GET State "AQAAAAMAAABzIP3HZyzVbVwQF/PXnfvfRNijSEtbFEQNRrH7h8SWxw==" size=331 + GET State "AQAAAAMAAADTKYPClF2n8lNzXchPGhN/Q1CMkqCDNlGyLFQCuYugkQ==" size=75 + GET State "AQAAAAMAAABalZLirJFX2uk9je2U8X6d4BgtaSrrLdfFYdYE9cIDCw==" size=171 + GET State "AQAAAAMAAAAn1tEVJPU+hCfgPZ69btg5mGrnhyhfz7A6ilwxJpj0MA==" size=75 + GET State "AQAAAAMAAAAQSHZjyktpbLLMclolvGNuvwcJkeUfdshOfKJNtThlwQ==" size=107 + GET State "AQAAAAMAAAA0CgM2NfjcXg0fMFPFWJ6WOy0aTPRPBEyWR20XUdYPjw==" size=58 + GET State "AQAAAAMAAACrxYH2PjUhDzVTscz2OUxL41ADiABjmhgcpNoSDkzATQ==" size=395 + GET State "AQAAAAMAAADb8wmygc4Cp9sM0Hs1ZvVbM4qq6rRcsvTCWdU5JFVpwQ==" size=81 + GET State "AQAAAAMAAAAJGOUGX7tt2Lwi7vBzQJMN8a5IjbtgY26FaWZIsMskrA==" size=9 + process_transaction tx_hash=AKtUBGwV1zxbG3dRjCQD9WJ12Fzo9YgQ1ow2nZCd2m6t shard_cache_hit=8 shard_cache_miss=21 + GET State "AQAAAAMAAADm1rEHNiz4H21eSSy8yNO2xANIUD9VaVgiOefA3NC1dg==" size=171 + GET State "AQAAAAMAAADTdKJWNpQfEt/cE3trFxTLgkiFn4qFuHXal/0SoFAacw==" size=363 + GET State "AQAAAAMAAABgO2j4QKKL2FcJiNtIHZq0Xh82Evx+LCOtM57XL2xMeA==" size=171 + GET State "AQAAAAMAAACGoPaMuj+YbTnOuGIx6Nw0xVG15Q6WfESspQAinNaOtQ==" size=491 + GET State "AQAAAAMAAABPflKfLik1nH/Z+gJAV004be+tcPcQ30jyhlv3nhU0vw==" size=139 + GET State "AQAAAAMAAAAA11qX8gp410OHF3NOlnztcBYGotfcM7MCvUHZoJAakg==" size=363 + GET State "AQAAAAMAAAB645WjQgHDkQMETee6O8ZwnLqaPYFBZ0m5Z7urLKTMfA==" size=75 + GET State "AQAAAAMAAABcMRv0Zw/VaJOMzQOuu7/FI8X0+FoAxDxrrpzhBqDg3g==" size=55 + GET State "AQAAAAMAAAD6dM2kEGdNTBZfvN3dqPCheLS5021GkgJnkmYaaHhGuw==" size=72 + GET State "AQAAAAMAAADjWeLgVBRJphaDhdP0WZg02p5FagTTnmaD1ZJs4/D3eA==" size=171 + GET State "AQAAAAMAAAALnvXQYIJ+FWYs/7n4kp3TrlrR0284m8wZ5VU6PmYK2w==" size=363 + GET State "AQAAAAMAAAC01aomBWJyfHIBnfnGxoIqmvF/qBF/HcUek0Ijktirtg==" size=171 + GET State "AQAAAAMAAADUZlr3ymfCzGoSvRjGCpK+huT94GdG8KVGn9tGhMgjEQ==" size=491 + GET State "AQAAAAMAAADCp/zlVJOgMKhmk034zifA1zd2bxRG9VH+n3CQlCUAIg==" size=139 + GET State "AQAAAAMAAADoJs9RAVjw1BENooXsyvTL5ResjgzfmNzqIksxZyrNoQ==" size=363 + GET State "AQAAAAMAAAAwt3ASgGusRvLM0fZvR9p5nVTQoBaabl1ZVe7lHld5Qg==" size=75 + GET State "AQAAAAMAAADn8R2vCCyf6hVbUNeZDOXRiAWv1ypHLB0UfISaC776Kw==" size=53 + GET State "AQAAAAMAAAAw/abJ+kHWziolSPUQP6WQiqbQ8F8fgbeNt1tM8vlLkQ==" size=427 + GET State "AQAAAAMAAADYoAFDkJIS8GWna2qL8jSWrezXqCk8jgoranfANChWXg==" size=75 + GET State "AQAAAAMAAACm1jq1xpzMgXUCNkaJ7GUKqCgYx2psR5EJo4kw9etZXg==" size=81 + GET State "AQAAAAMAAABgaPf23msu07T1Om7yAMoua0ZHZh9tBfzR4TacPKfTCg==" size=52 + process_transaction tx_hash=uSKvjryKgJQrWKg5EFSvEpnDUPRtRSbPrX42i84ekV3 shard_cache_hit=6 shard_cache_miss=37 + GET State "AQAAAAMAAAB+om/WZKcf9Tjlbj1X8W8l2I5pIemqJ0O7y9TWOI3JjA==" size=171 + GET State "AQAAAAMAAACillAoKqleyqTl1vM/w2mE1jwU2rfaoNbKApl71TKgwQ==" size=171 + GET State "AQAAAAMAAACQyCGOktTvvaOF6Cr00Yz2Qu5O+dKbBWdKj6dV/zCnfg==" size=491 + GET State "AQAAAAMAAAD5ok8ocaPO/1OPidTa2CdqJj+eGUZ/ThxpBMr6qV5UQg==" size=171 + GET State "AQAAAAMAAABcIli/3e0heyKz0KIAr5lH+EDwBfjuy+uVKrtTfg7OjQ==" size=363 + GET State "AQAAAAMAAABTpEjsFhl+257elg6fy54izQ/HlmWOzYWQqFjLfXVZMg==" size=171 + GET State "AQAAAAMAAABZlkzxnQKG8Jvtgw7pg27ydxw/SCTypNhvuFBMFgmwXw==" size=491 + GET State "AQAAAAMAAACie2Ut8jmJtsQALaprmlNeJVgt0xa4OoDs8mkKGqALvQ==" size=171 + GET State "AQAAAAMAAAChiNPtpOCd+itN21BNGQpYz/Cv9ApsVF5a1f+p85UXoQ==" size=459 + GET State "AQAAAAMAAAB8wUwv/XczgWp0ZajHo/bDJiq1gMQAwfCpR6YWXBa/uw==" size=139 + GET State "AQAAAAMAAADHBFpg0Px5IHccQXLCaWy6ok5Z+C9t8ETMU3C/D0Wl9w==" size=75 + GET State "AQAAAAMAAABKIiI+ZJP22lvvkzv1b9bKk4fwOGnoYcATK/40119IXQ==" size=171 + GET State "AQAAAAMAAAAO+vJmXsB4+Xkcc8gZUYEtcoP2Plr0kPOve9CDklm6og==" size=139 + GET State "AQAAAAMAAACsd8RvzEHHC+rhoDRDwzrV272HsGjIwG2c1BFHPcHM8Q==" size=46 + GET State "AQAAAAMAAADhAHVNlk2y0n16uR06+xECwtlcHbY552HKPXHrCH/LTw==" size=75 + GET State "AQAAAAMAAABWcNDVwbvrDG6Gy9m96t79qHXjTAlG2Q6feFRN9SpNrA==" size=56 + GET State "AQAAAAMAAABp4iW3v/GAQSIG1NO0sa1SGi5gVmIUvn2j7SsltYF9DA==" size=72 + GET State "AQAAAAMAAACrj3+NJmnEs+X/y06woZOvPlXkaBtXw8nQFi4RlQlv2A==" size=171 + GET State "AQAAAAMAAAAmU5c883J2Q/yddHtVIb702r7wpBrDwrdXsKCfFtXYVg==" size=171 + GET State "AQAAAAMAAACvT8T8k9FZyWzcAV60407MZEcKeHFGJBtzENPrOoCwXQ==" size=491 + GET State "AQAAAAMAAADP77qsqvbcY0ZLKIXpN5Ygp3QrFrOlQmI8AIlSkqcNeQ==" size=171 + GET State "AQAAAAMAAAARiuAfVSn4JlPuL+qdrM/n2NEdGayUU05vr1r8J3gblQ==" size=363 + GET State "AQAAAAMAAACfXcidZ/JBivNfHAYK9XRYMDNsiN7kqGoNErLR0ASBSg==" size=171 + GET State "AQAAAAMAAACHCJOpeQkYm2axVlx1bST73VhEAjIJyRQo3bfBbSmmmw==" size=491 + GET State "AQAAAAMAAABVBG9kVX+D1SwHqfAW4Q5aVnPootqqcFf+PJRrmSP34A==" size=171 + GET State "AQAAAAMAAACsn8RG69HJhtHp8KvZdSWL+ntcDtoJeAdh5GYRq7Q98g==" size=459 + GET State "AQAAAAMAAADDHrWe6t+2CJ+ErGRzyMwQ066DLHK5avIILFVqz2q90A==" size=139 + GET State "AQAAAAMAAABFapPqGFt9s/xmHZgZeCr7d3zJe14y3GsNJhXRu7K/8g==" size=75 + GET State "AQAAAAMAAACG6FuvnhCYQdwMuPN6seV2ynAY18qjQurLc1uLvMpfIA==" size=171 + GET State "AQAAAAMAAAB+RwRtdqgmS/VnoiDTd5/yad+VIgv+dtKD1Xk6amZyOA==" size=139 + GET State "AQAAAAMAAADPjc8Iu68xPbfrqOdDW/vAuFg3UJFXye0+lHnfBSQ4BA==" size=46 + GET State "AQAAAAMAAAAZfK+EBr8D0cXBDm4QS16t/+eHdjkxMKKNgdfvNN5xyA==" size=75 + GET State "AQAAAAMAAAAgT5OkkFjBR1QT4Nj/3gbbe/sNDVxUd2mlk4ZS+r2ITg==" size=54 + GET State "AQAAAAMAAAB2OIc6mGmC1uuGIPjznmUVGKX/GZp0/+6BPonMGt34xA==" size=523 + GET State "AQAAAAMAAADHMqttR7S/4XvKRZytjZi45d/lhk5kL4B6UDh6Q92ayg==" size=235 + GET State "AQAAAAMAAABLncYZBQ6PHwmayMsmXxuShzm04ou8/iDoLcCPeAsACw==" size=81 + GET State "AQAAAAMAAADtUa6PSwa/ub1Ppu0b/kvnxix3XVmJ8LaBLng34LV5wA==" size=52 + GET State "AQAAAAMAAAB7rXHvmbCb5a4lYsPTL/+kUdZeIBtiQj9F92B+PBdyow==" size=50 + GET State "AQAAAAMAAAAkig8G3RzLo+l+HtmXkUSwkjXbtKgUlLjmagU+E+2h1Q==" size=16 + process_receipt receipt_id=cesi14ugy8KbNKu5SqL5SYY8MUFRWC39qzKYP9xiJL7 predecessor=verrelkeung.near receiver=verrelkeung.near id=cesi14ugy8KbNKu5SqL5SYY8MUFRWC39qzKYP9xiJL7 shard_cache_hit=16 + process_receipt receipt_id=4tSv2K8ohafiXnCAKprrWinzRFuaMwQzM83dkVx99q7r predecessor=tge-lockup.sweat receiver=token.sweat id=4tSv2K8ohafiXnCAKprrWinzRFuaMwQzM83dkVx99q7r shard_cache_hit=6 shard_cache_miss=33 shard_cache_too_large=1 + GET State "AQAAAAMAAADlVwIXVIHpr/nsjeZBOoFKKZbr5PB6sEvXDhcCQ/nygw==" size=171 + GET State "AQAAAAMAAABkr75CuJJbkPF1sVPBnkr3+AHPqQ/qPvCxJQPWDUU3cQ==" size=491 + GET State "AQAAAAMAAABQI83kYZrKOXakWTDKgp3kmwBNBwZEShVS34V3XQ2dKw==" size=171 + GET State "AQAAAAMAAABy14uJ/+LsRHlQwBOqzaJFJqCjRC+WzQw+mMzJ2hYB5Q==" size=491 + GET State "AQAAAAMAAADRGc8JHgvhgYWjz9472QrQvEvsvaAQti/kEcwSHX5D8w==" size=139 + GET State "AQAAAAMAAAA8xykt3S4vzqY1PglKfNbM/erkCd/X7Nh4t+II6BZFog==" size=459 + GET State "AQAAAAMAAAD7QyykbfjWre388GeKbKRVf6GdETTWjSYRlE5t/Hshlg==" size=139 + GET State "AQAAAAMAAADtNSWR6dDUpWlnAvZ0bak6ENIx2EiK3AOOLZ5zaVzTXQ==" size=235 + GET State "AQAAAAMAAAAurkUfoaXpu47vIDWHkLLEC1EndDGYZOdNSJK0aS2MAA==" size=171 + GET State "AQAAAAMAAAC3HV2Re4EfrPgZ9qa68DQEkfq39seRu0U4GOn6SnZSBg==" size=75 + GET State "AQAAAAMAAADRpiZSzwo8gvhGMmfaJL3sfJR12LuVJ/vOrcfRQyyk6g==" size=75 + GET State "AQAAAAMAAADM0zFrq3y83Rv7sLp23XJhioV5TElCK7jsGqhoi2eq0Q==" size=139 + GET State "AQAAAAMAAACTwjttvIYlS+di9VKTvOWO5EIpbtGePYkGaV1LKFOAhg==" size=75 + GET State "AQAAAAMAAADRx0lj3mREDppk/55bNiWsm6ToAdXyLbD5AL4wBqmjBA==" size=107 + GET State "AQAAAAMAAAC7xIxhT+yByCStl48+G9O/jswn+6eBmE+/Z22CMqFeSA==" size=53 + GET State "AQAAAAMAAABKalwQbQnhPf9MptBXoqG6pkXuSWB/Rw47bcL0oKQWIg==" size=72 + GET State "AQAAAAMAAAAURXxzpufSa0saCqe3hokKrPb6dHHdp4r+ul3/WMW5zA==" size=75 + GET State "AQAAAAMAAAAOMTVa6+rnyjGw/g2anWmFJP0mczYiPIw8/Q130aD+yw==" size=363 + GET State "AQAAAAMAAAAySeyc1lN35qabD8SKxVEQjER4tl1j4ivcVsNb+tFJzQ==" size=171 + GET State "AQAAAAMAAABjqk9JGqYZpGF2ht0wW+1DQ244rA7TI9csGAXBUNl0tQ==" size=491 + GET State "AQAAAAMAAAANVqNY/AdceoR+gX5KLzDOAeckRDJjepAmKvNH2IcGXA==" size=75 + GET State "AQAAAAMAAAAbrlApQOGkG/VqTdWxfnuG5muKDBFBa24mkSZku0iFqQ==" size=427 + GET State "AQAAAAMAAADsUJY3KEYrQvSCNWPExkGoUEQ8oGnEanS2iXQSJVNnGA==" size=75 + GET State "AQAAAAMAAAD5E89l2ZwzaOxOwErzi3MkLuj5ZubeS0m7RjKSpoRtyA==" size=107 + GET State "AQAAAAMAAACteet23DuOS7IXEpHH9cVP03KT9936prSmkMxgVWSMwA==" size=47 + GET State "AQAAAAMAAAB1VkzQjtmXbimBtaWT6fp7/qCzJbLHupQnBCqoc59S9g==" size=107 + GET State "AQAAAAMAAAD8MkHQR9CiN3LrwzHHozPPzipFPS6fOVJN+PbCz0eN0Q==" size=46 + GET State "AQAAAAMAAAD+z/z7Rx5Leuku6hMCF92FAPXTSQB6tmsfK/VKE07gZQ==" size=75 + GET State "AQAAAAMAAADNRnJ48E0+4B4mISeGsEuzeHfKdLbjn+OG8RgxiRGxZA==" size=139 + GET State "AQAAAAMAAABCJepQWbPhE1AvUaHFldz+3NO9qgcExXK9QBei0i7NSQ==" size=75 + GET State "AQAAAAMAAAB4qKLDgVcwDt+gWq9RLbb3XLKunYuYI84xk4CuaYaGxw==" size=107 + GET State "AQAAAAMAAABLJW2gqgwHnyEKTqRoU99o1Y44l3x3gmx9ZeCNXJxY+w==" size=53 + GET State "AQAAAAMAAACXgiXFMX6W5mjpLP1ar2x3NuAaUrr6gENW9oVwarrSEA==" size=204482 + input + register_len + read_register + storage_read READ key='STATE' size=70 tn_db_reads=20 tn_mem_reads=0 shard_cache_miss=19 shard_cache_hit=2 + GET State "AQAAAAMAAAAkvs6KLTF0x6cOqwEEXsH6H8riTF31E1G5ZtbwvAa26w==" size=75 + GET State "AQAAAAMAAAAdoZZOqSR1rQKGlL2HusidwD5fxbLWOtk27ZbLIvaj4g==" size=363 + GET State "AQAAAAMAAAB6213zAkqOeq0GDeUiq16oWKc4b3JBjMySZ4qtH2GtbA==" size=171 + GET State "AQAAAAMAAADbHjJy7bv4hhwbKknzZjEg8VwO1USpv67BdE8U1ZzrMA==" size=491 + GET State "AQAAAAMAAAB035IMMWyjUIqYOSQXyxd2H78T2GdO5VLyb3Z9AUaeRw==" size=75 + GET State "AQAAAAMAAADaU56blGQ2et+eDG8KPGAFA2KbH/GZZLBKtTOOjJmZIg==" size=427 + GET State "AQAAAAMAAACefOU6Juc2Pj2B2K6SjBybwWWgU7hIGO4e7nMrS6LQgQ==" size=75 + GET State "AQAAAAMAAAB0vlACWGsGSJ80TD2iri2CYvutGVipCZdnY8wXMZXMKA==" size=107 + GET State "AQAAAAMAAABX5nMXT4D+9zmGjEZt3YdrCDq9PKkSqKJVfxedbr5PsA==" size=47 + GET State "AQAAAAMAAAAE22NauZqeFN0enFNrFIeNrFOp4u8u4N9SOSK/d+jlYQ==" size=107 + GET State "AQAAAAMAAAAICw+wmoe4YL6FN2AMo4cRwvlMzM1/na4pCH/FAcbxgQ==" size=46 + GET State "AQAAAAMAAABfgPNuyrOB2iRAspZ1wgxY27f/bL65ovJdl2gec/Wyyw==" size=75 + GET State "AQAAAAMAAABLWeTuOlpzkeGt2eH6YkSSewOC7g/4+SPhNLpGUStvIQ==" size=139 + GET State "AQAAAAMAAACmzlxOetcNhWixifJBoggjmh4F3/zDTa9TblpNMtblnA==" size=75 + GET State "AQAAAAMAAAC0yZYKa1fg6f9F50GvlzJ2UTBt+LRnlNDUmqcsLrvKZw==" size=107 + GET State "AQAAAAMAAAAnIOZyCIoYt5oKk/mojizRLIvVU18UuL6rlQKa5qMWDw==" size=50 + GET State "AQAAAAMAAADIqTLCYsEVCokuEcvhMGZiydRD1fd7LRAn+TlXk0WcuA==" size=75 + GET State "AQAAAAMAAAATCIPH8Sqi2sh3YqCK4t2oOZvGhNbBaTaugevIqjPuig==" size=54 + GET State "AQAAAAMAAABdiJrdWOhA9/S0OC16a9UxsOZCUnkDmIYjTA1N+ote7w==" size=70 + register_len + read_register + attached_deposit + predecessor_account_id + register_len + read_register + sha256 + read_register + storage_read READ key=dAAB1Xeh4CCdobM+obRpH4JXDt2O2nMKTqWUeQ1KJgAEoQ== size=16 tn_db_reads=10 tn_mem_reads=19 shard_cache_miss=11 + GET State "AQAAAAMAAABoFL7eMF2jbIQsO/l7EjiMp1LugYFBEJfwkhwA4lAR2w==" size=75 + GET State "AQAAAAMAAAC+RSpieoeGpu1100bF5b4k+VLbyzOnMU1OV4a0qUU4/w==" size=47 + GET State "AQAAAAMAAAAPIICWxK3EmFbHRrMnXn2HWW0ihV+15NAqZuhIfkg+sw==" size=523 + GET State "AQAAAAMAAAAA7qDZEhPzCYs01+jDieH5n06I4df/AKPL/U6w5Y46UQ==" size=523 + GET State "AQAAAAMAAADh+Lz0t9kT1nsefNMW7its69K+ucEE+OuSWQPlQ/1m3w==" size=523 + GET State "AQAAAAMAAABHbpUk88pnKExYPxq1a6FDkk2xuXcaUGGl9yCae0zMMQ==" size=523 + GET State "AQAAAAMAAADa3eDYwj58TfF1TVcWDWpVvJFE9s4ifwdS1k8SfNA8FA==" size=523 + GET State "AQAAAAMAAADaPu/AvgrKf3cPyAHbyMHxd0Idmtkb7KjE0TlQOdmkaQ==" size=363 + GET State "AQAAAAMAAAAIXhO4kHaqSzW/zw7ODgty+1lOUeCx+03+wH9iZ41HQw==" size=75 + GET State "AQAAAAMAAACw7icFwyTlVOuczU4utZk+W81ZMVkrw/Ix8BOwXNuKzQ==" size=78 + GET State "AQAAAAMAAAAI7xdJuk+WoKYW+8c+lszEixrR3KJNxpqky4rRN9+0fg==" size=16 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dAAB1Xeh4CCdobM+obRpH4JXDt2O2nMKTqWUeQ1KJgAEoQ== size=16 tn_db_reads=0 tn_mem_reads=30 + register_len + read_register + sha256 + read_register + storage_read READ key=dACGuqkC7p7sZ8x5MUVg6bKzjL7gCMSDStDTfwb+5NyXbA== size=16 tn_db_reads=6 tn_mem_reads=22 shard_cache_miss=7 + GET State "AQAAAAMAAADMKPEKAd9Zyw90pgjUJuwiNglCWF+TBHU+vMSvi6MZBg==" size=523 + GET State "AQAAAAMAAAA1Cu7kFGLH5TaVxomHjX9hqF3NpbASWz+YYbClc8ba+Q==" size=523 + GET State "AQAAAAMAAABWwuEjFpgT5MEIA/lGfKdHakOsMrXQQzKKmg9SEQyqeg==" size=523 + GET State "AQAAAAMAAABnJt9kmjIWwgkisB6SnfQzaxsZwjAwa/OEziiE9sBF8A==" size=523 + GET State "AQAAAAMAAADRhyiBUR/jwRJULbuJPSea0Ef4gX5E/9ujcIgNmvp3/Q==" size=267 + GET State "AQAAAAMAAAAlVI8TXO6fh2Wfu5sLdz7BAkmj7Ctx4DxkDtf7/nTBwg==" size=79 + GET State "AQAAAAMAAAABiI4U0fgjy6CxQg7TW1eearaqpFyzzkmf2INhflbuCw==" size=16 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dACGuqkC7p7sZ8x5MUVg6bKzjL7gCMSDStDTfwb+5NyXbA== size=16 tn_db_reads=0 tn_mem_reads=29 + register_len + read_register + log_utf8 + storage_write WRITE key='STATE' size=70 tn_db_reads=0 tn_mem_reads=21 + process_receipt receipt_id=7UeSKKYxqRLsSwnW5xJm116QTJEWJJa7YqR1JmNWZE3L predecessor=tge-lockup.sweat receiver=tge-lockup.sweat id=7UeSKKYxqRLsSwnW5xJm116QTJEWJJa7YqR1JmNWZE3L + process_receipt receipt_id=7HpTajwsXdnmX2EEzWq9PXktDJLGJGwqWhL4gcQ3QVQ3 predecessor=system receiver=npo-aurora.near id=7HpTajwsXdnmX2EEzWq9PXktDJLGJGwqWhL4gcQ3QVQ3 shard_cache_hit=6 shard_cache_miss=16 + GET State "AQAAAAMAAACgBF8NoSqG89WqcrjruX8rYEYytqQflsuZtIOIw6Bxcg==" size=363 + GET State "AQAAAAMAAADxAF1/oA+f1MH46V7bqc2KcX1bciaPiJAdAeXTMQrezQ==" size=171 + GET State "AQAAAAMAAADhIJk7y9QfAbFrBeTXGdX1oJ0hE23sCvG+ltV7qf5VeQ==" size=491 + GET State "AQAAAAMAAAC3cVWFArs6M5OsoGRrOvuVjC6HY3muKOINQlBsN1t8Vg==" size=139 + GET State "AQAAAAMAAAAM9SLKBcri+dgj5wNJ1V0Eb25sCjYCSF2VFHLm/dXsKg==" size=75 + GET State "AQAAAAMAAADi/eD+z5g4op5q5z/07QT3gJ3UmuysrY1liiw2c/n7yg==" size=61 + GET State "AQAAAAMAAABkCKX6ipM7T3/XE0jhtdrQG0GUeCmPS0LMGI+ffvW5Rg==" size=72 + GET State "AQAAAAMAAABsI/g4so4BK8TpobCIqvjSfuy4noEBofobSHd7yb8/Lw==" size=363 + GET State "AQAAAAMAAACsLtBRRIqCzUE/Ip9I4gTICz5nfkef7E7Zpa908aFuYA==" size=171 + GET State "AQAAAAMAAAD5F4LUdNkqgf6KzDGlNiFDmUnPT1pb0MDUodhBAKvkIQ==" size=491 + GET State "AQAAAAMAAAAqnrod7f7rifuU5QO7twmfIve5G4BD05Ap7JGNHzPLhQ==" size=139 + GET State "AQAAAAMAAADN1anzx3EThysfQxQrf2Y6dLu+y/GGs0d5lGzT7d2+xg==" size=75 + GET State "AQAAAAMAAAC6pfqEDHxtXsL3pNQ7nPk1qTMjCqikl/0ZnaknhM6s8g==" size=59 + GET State "AQAAAAMAAAAHNtcRxqWMrZ7LM1GGobiM7/FTne5QE25mgj7tjjY9kw==" size=299 + GET State "AQAAAAMAAAD+/LhD5dq+uueMRjWHnhSm7WVSjNXJEIyfLtEEFX8jaQ==" size=81 + GET State "AQAAAAMAAAAL7mfpI3W3HZyGc73t8Pcu6w4Myds73PilWthG9z+8ng==" size=9 + process_receipt receipt_id=CHPvzniPWKMWCiZ3c6ydqN6QgsEBpS3pNgP7HH48iQLK predecessor=token.sweat receiver=tge-lockup.sweat id=CHPvzniPWKMWCiZ3c6ydqN6QgsEBpS3pNgP7HH48iQLK shard_cache_hit=8 shard_cache_too_large=1 shard_cache_miss=15 + GET State "AQAAAAMAAAArj3s4TP3D/a/FkTbDQQ0I7jKVhMSp2xX7x5saIyr2/Q==" size=99 + GET State "AQAAAAMAAAC9S4Y5CAUmiKdXg6viUisM/OftPU53RhYTfIq/TRsLZQ==" size=32 + GET State "AQAAAAMAAABaNdjT6WeA5IxvQuBbXllcxVrSV/rImhaaBx1/BD0oJQ==" size=99 + GET State "AQAAAAMAAABnq91yECTw/04LP0wvwTvFutQtC3hR1FbYjSA9FaqkUA==" size=4 + GET State "AQAAAAMAAABKIna70tkFeXgxncXtZ+eOOgPtAbtYhW0bSQn0d4rX4w==" size=99 + GET State "AQAAAAMAAADMrSBqab52FWD5hnVzW0olTbQXEO+JP0Z2w8mJO1sXjQ==" size=459 + GET State "AQAAAAMAAADsiUOtd7yT+j/aSSK/ac1XxHioF+pPha5gHPEXHnOpBQ==" size=171 + GET State "AQAAAAMAAABHkYhGTZYW/1nzH8FHFc30ZtPrndqYII0ucRBOt1PtdQ==" size=491 + GET State "AQAAAAMAAAB/doxEeIsDCBuXigjjyAFcUaI1Iw7cZL9zr4aomtGWlw==" size=139 + GET State "AQAAAAMAAAChZ5sLMBcL6XL1WG1+f92BVfd8WI1g94eP/pU23mqNQg==" size=75 + GET State "AQAAAAMAAAAX9Fg6MK1j4PBul6u0XMpxdjmcC5pgwdTYtaFurgTWHg==" size=62 + GET State "AQAAAAMAAADB8T8IBxatZAWtGsrhCBE3wX6AqIC0SmvPOv3C45nbxw==" size=72 + GET State "AQAAAAMAAAAaCQ6+0dBmLXPLUr+mW0KKdV6pLRkKDsZ81NR/5Hlk1A==" size=107 + GET State "AQAAAAMAAAAXS0IKRwlfaBKvq7uL4ltWwWnvGztULEqcSwZDSJ36gQ==" size=63 + GET State "AQAAAAMAAAAPGUPSlOo3VmtbyyVZoG47modtYCYLED9ZNZiuTwH2fQ==" size=233847 + current_account_id + register_len + read_register + predecessor_account_id + register_len + read_register + attached_deposit + input + register_len + read_register + storage_read READ key='STATE' size=53 tn_db_reads=4 tn_mem_reads=6 shard_cache_miss=5 + GET State "AQAAAAMAAAD3sn7QV77X6tudJwiK/DQIT01vO9ZwIqTTW73Csv99gQ==" size=107 + GET State "AQAAAAMAAABiTIqv3SwBXHBFmXmvYjvKfwZc/LITNprx+bkTNLB2gA==" size=60 + GET State "AQAAAAMAAAAQ63kNEQbGCSfl5BXNYlWeC8p9M4GLNdE8YBPAONvj/w==" size=75 + GET State "AQAAAAMAAAAbX5QVy3VdaE/XtTX79IGCU7CVRs8DeF2xQ2rbV/qb0w==" size=54 + GET State "AQAAAAMAAAB/+mM71deg+f5GK9bnOJvr3LsHhb1h6QFHeq4+LTVcVw==" size=53 + register_len + read_register + promise_results_count + promise_result + register_len + read_register + value_return + storage_write WRITE key='STATE' size=53 tn_db_reads=0 tn_mem_reads=11 + process_receipt receipt_id=H8GTFPKBkuUjvs8NfzcPNZuapFiMbGUMvscNZhnjUsPv predecessor=system receiver=sweat_welcome.near id=H8GTFPKBkuUjvs8NfzcPNZuapFiMbGUMvscNZhnjUsPv shard_cache_miss=36 shard_cache_hit=4 + GET State "AQAAAAMAAABzpuAltIHzbyT5fzHTJBMPf+Cif9jSe9p83SlxFz1/Yg==" size=171 + GET State "AQAAAAMAAABuLDGSJtsAUhwEEYot9NuUVv6RcO9hDZ3/+Vr9rQaKrQ==" size=363 + GET State "AQAAAAMAAADBq0ysobjIj2XukDK+j8X4rY+H1MWpM4XHWZxtLfII3A==" size=171 + GET State "AQAAAAMAAAC9fuB36juGxjumbjtXVOrFEVSu1g8lB39sgkpYgGgLSA==" size=491 + GET State "AQAAAAMAAAB78hkPn8AMgAleeh8ylilBxssIUZqpZ/amPQvYAH2UlQ==" size=139 + GET State "AQAAAAMAAAAuL6q6jjDwl+0RF8cE+mC2G9OYlqA9Fpxj5S7Z6Bl5eg==" size=491 + GET State "AQAAAAMAAADdOOd1pAXviHI6cEFzWnOetxDZOH8skUrgKvj+4i1yxg==" size=75 + GET State "AQAAAAMAAAAWbWpsUN6nb8+gYMalmYsyNfHxJa4mJjS+YrpA/qyuww==" size=139 + GET State "AQAAAAMAAADYevHHGihs/nc8rlYUhJ7Ztchp8U0rDnsuHrmfKH4zCg==" size=207 + GET State "AQAAAAMAAABPeKe+pBuvD56LSnYrIwQFepeBR2rTUSHAD4n8EEMcEQ==" size=46 + GET State "AQAAAAMAAACd0T77MN8uqw4e2g/y1huj9ElkGr+rLpwNJvt3d3NdAg==" size=75 + GET State "AQAAAAMAAACYO3JpQM3GazLvZy2fnvzFGh/ZHIcKRnU9/IX+5jCblw==" size=139 + GET State "AQAAAAMAAAAAJV8KVOIprCPYMcDx8Gucq9/e1RpEtOMaZ/210KYLEA==" size=46 + GET State "AQAAAAMAAADjFWgnno3bSLTSeQxYPOlJbvqQEdHwn8j3xKa84Qjp3A==" size=75 + GET State "AQAAAAMAAABgJ6bZkWn/anx+YN1XO6IxalQ7lAjQJM33fK2rtFiWZA==" size=60 + GET State "AQAAAAMAAAAOA0e3IG3vaaXb9SXWPiKZcQnIQG4V/78CUdh4PnJUWQ==" size=72 + GET State "AQAAAAMAAABsgM8pHVDzKrWnE1nDJXClwZ3EBmnWQzqmUVTcayLQ7g==" size=171 + GET State "AQAAAAMAAAAOvPsf++GaPX89krsgZwIvyCmfHYGtpcUgtpWnMfOOFw==" size=363 + GET State "AQAAAAMAAAAz8w0UvlMmX2lvCpgDX47/44EyxZpvHbBIR3PU7LMv7A==" size=171 + GET State "AQAAAAMAAAAyYa5zJV3TwuzvnfHOq4/RtPTlinH0moVZuAA8VSYpeQ==" size=491 + GET State "AQAAAAMAAABPix+c55wpU0IQYiPamAvW5QJMxQPlyIF2B8/paGr8DA==" size=139 + GET State "AQAAAAMAAADairyFlypCz1qvgGeoFr7nae4d0zrR1JJfx1rWJdMY7Q==" size=491 + GET State "AQAAAAMAAACEZ5/AemhyLV1FR89aPBGzaq4UGD8326Jnke5hOZJiBw==" size=75 + GET State "AQAAAAMAAABV+0kXDIGHZxeWQV1nUKG7h0sMaxoaEqdhGSkhPwsZhQ==" size=139 + GET State "AQAAAAMAAABtRBjLGKvBCyS4HC0Pk2lnJqEBGCYLxDR3XX3akzmvfQ==" size=203 + GET State "AQAAAAMAAADakIa4V/Oq4Uc7kaPL7a7bwUSpJYCpfZiJHbH0+GrXxQ==" size=46 + GET State "AQAAAAMAAAB3wNwMk7/w5SPSBKHsa/iz3LvuDj7XmC6kzyCyLERhRg==" size=75 + GET State "AQAAAAMAAACtFEvyQGx639j7nQGTkO2PuAFN/EIHexZHn/mH0S9Uww==" size=139 + GET State "AQAAAAMAAAAeCevecTDm+dhojd+wUG+nAHdx1OVA2yVKdUDSkrTIlw==" size=46 + GET State "AQAAAAMAAABT/OTQIPD5RBJOjOtHsrZQfKVNWwcx5RI676NmFWQ3/w==" size=75 + GET State "AQAAAAMAAABFjKnWh+yu1THaDmcG3fXvQhTJX7NUuNpGMcPy/r0zbg==" size=58 + GET State "AQAAAAMAAABkqjz99oj9QPOnzsInj0rxAw3Ylt2KDrMgnUGb7iOrgQ==" size=523 + GET State "AQAAAAMAAACSjFMU1/B3mmQCKZ+6GNEeyXN/AbWAlG7xaLPB6H4OHQ==" size=331 + GET State "AQAAAAMAAAB/cWEjXIb/OL1RRmNFjTRLRbcAIwb7a68ylOu9TmeuNw==" size=139 + GET State "AQAAAAMAAAAMIO/D7GhTQd9dAVcv/1A1XKhcEA9Hym/qFtK0cUz2KQ==" size=80 + GET State "AQAAAAMAAADAvejMLKclIk+m5gDSgoAjj7aZb8cXadK9VPCoQbix/A==" size=9 + process_receipt receipt_id=4rYKmJdE5pAHCDb9qVYRjTruvE58vRPRH9A7sGroBa3b predecessor=14ca6570b456a5f904641d1005b9499aee5cfa60216bb3b73871d2256ad1cc2a receiver=token.sweat id=4rYKmJdE5pAHCDb9qVYRjTruvE58vRPRH9A7sGroBa3b shard_cache_hit=16 shard_cache_miss=1 shard_cache_too_large=1 + GET State "AQAAAAMAAACXgiXFMX6W5mjpLP1ar2x3NuAaUrr6gENW9oVwarrSEA==" size=204482 + input + register_len + read_register + storage_read READ key='STATE' size=70 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + attached_deposit + predecessor_account_id + register_len + read_register + sha256 + read_register + storage_read READ key=dACmcyE60StXn36l7bBclmhz4khVJf685D0LE60T6/I7dw== size=16 tn_db_reads=7 tn_mem_reads=22 shard_cache_miss=8 + GET State "AQAAAAMAAACnOq3qXIUXfIAqauNB/DhSFm2HiKHGdQIVsSonwJudUw==" size=523 + GET State "AQAAAAMAAAAD14hLDhuDHbmxb7JOgUA93ZbCJkGS6156slFkb28eXQ==" size=523 + GET State "AQAAAAMAAACkNwiC6XNNfyWmRWYnRqyWlgtSlEm3x5s3vDrJUHcBIQ==" size=523 + GET State "AQAAAAMAAABlTwFk1mssykH+G8GHjLLumPilEVEcNoOibXdRpRiTqQ==" size=523 + GET State "AQAAAAMAAAChaoumDoMgIDukIBDi6R9HEtHP3WsoskIb7e4CjaK31w==" size=331 + GET State "AQAAAAMAAABKUFDizgGiS9ikb2ZmQIKTzmHRs1X0n8Kr9Kq95PlIzg==" size=75 + GET State "AQAAAAMAAABf2pqUmrXmZ15Pq5BB77xu9DuTGsZEfaSw9kchsqR3WQ==" size=78 + GET State "AQAAAAMAAABPYNa2ovfSKjlUVzZ/S6c1MNOy1AJOkJI+MiERXonZtA==" size=16 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dACmcyE60StXn36l7bBclmhz4khVJf685D0LE60T6/I7dw== size=16 tn_db_reads=0 tn_mem_reads=30 + register_len + read_register + sha256 + read_register + storage_read READ key=dADJe7dUqB5o8oZevxdk1UM6Dvhg/iTG6vCW+N5gPEXIUQ== size=16 tn_db_reads=9 tn_mem_reads=22 shard_cache_miss=10 + GET State "AQAAAAMAAADZFbhd6Zb4Wy2142b7pS2JOcEZJnVJeM67mFboy1vjnw==" size=523 + GET State "AQAAAAMAAADrfTyqf1f+PeD2rqdo54s8xTGjy8S0/i74+ym1JfQauA==" size=523 + GET State "AQAAAAMAAAALtrpAnx3Wy9r0P9YWyaAujSXvYGOo8Qs6kFq59RKJPw==" size=523 + GET State "AQAAAAMAAADygmfnYoO1V/6OkQZTtwqy/aDhIV4WH7QAKl4f5Wd1+Q==" size=523 + GET State "AQAAAAMAAAC3irMvcOeIwfB1P8Wvj3lV9H+w9psV95ez24e1rSfFzA==" size=363 + GET State "AQAAAAMAAADKv+1RiJJhvjwpo2D0FDrDsOjco9h8L9NAx+UW439big==" size=75 + GET State "AQAAAAMAAABSg0LzBHgo1AGYZpgJAur8nColdfMmpG49yUid5Q7W6Q==" size=46 + GET State "AQAAAAMAAACtFKklv8Md5Klbcu900km9ZmHDZzJjuBb8Dm3NZZxHyQ==" size=75 + GET State "AQAAAAMAAAAZYZ4+bSwCu8TTwdNP5gU2uckhcSf7192x31PUkB1rXQ==" size=77 + GET State "AQAAAAMAAADWnOQrupshG5Go4dBVOz9un56vcYo8aGbTsUMz8zjzdQ==" size=16 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dADJe7dUqB5o8oZevxdk1UM6Dvhg/iTG6vCW+N5gPEXIUQ== size=16 tn_db_reads=0 tn_mem_reads=32 + register_len + read_register + log_utf8 + storage_write WRITE key='STATE' size=70 tn_db_reads=0 tn_mem_reads=0 + process_receipt receipt_id=ohgxoczB4YhnAsUDpFVDpnMGP5iXuZbftgNe86KZ5XN predecessor=2cd58e2eab52381a893c42b8dc47d66633b58c16303c4398f44dbd149b6e1411 receiver=token.sweat id=ohgxoczB4YhnAsUDpFVDpnMGP5iXuZbftgNe86KZ5XN shard_cache_too_large=1 shard_cache_miss=1 shard_cache_hit=16 + GET State "AQAAAAMAAACXgiXFMX6W5mjpLP1ar2x3NuAaUrr6gENW9oVwarrSEA==" size=204482 + input + register_len + read_register + storage_read READ key='STATE' size=70 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + attached_deposit + predecessor_account_id + register_len + read_register + sha256 + read_register + storage_read READ key=dACmvtGSbizVVDyYKEnBIXJxQHIrb7jLg2NNA8uTE3FrLw== size=16 tn_db_reads=4 tn_mem_reads=24 shard_cache_miss=5 + GET State "AQAAAAMAAAB2d9xC3H1CUNlG1DdC7yCw45rciQhZgUIMqd18SwAtQw==" size=523 + GET State "AQAAAAMAAAA9s/Kh3EcVrngJhgUTMnFiluxV9MwznhBzdXbfA8j3Qg==" size=523 + GET State "AQAAAAMAAABoFrw0ZjZ1dyycn6Qwpt82Mu54+1R7x0wBenYyw3qNHQ==" size=331 + GET State "AQAAAAMAAACKZNpwAK401gPEXjTbabtZAQd0r2Tlf3gQG29/irnyYQ==" size=79 + GET State "AQAAAAMAAAC6s8anmsMy+s7V8Z+Hv/Z24+NxP6Gib8SbUCPfk2ziIw==" size=16 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dACmvtGSbizVVDyYKEnBIXJxQHIrb7jLg2NNA8uTE3FrLw== size=16 tn_db_reads=0 tn_mem_reads=29 + register_len + read_register + sha256 + read_register + storage_read READ key=dACMuM913XIXgmv2vMPSoqSA0pCRSHauXQHBmDQMjTz/2w== size=16 tn_db_reads=6 tn_mem_reads=23 shard_cache_miss=7 + GET State "AQAAAAMAAACO4LRKEmoxNq/nAsD8XK+zwpylQgu4zWPZtYsr/vBPVw==" size=523 + GET State "AQAAAAMAAAAlkOM7wps3Bn8sVU07oVWJ/xod+IMHUzJs/wOKhlQ3WQ==" size=523 + GET State "AQAAAAMAAABpp6wn6etJUSRpbmnTtmNO0tRfgPntqj74Gx0lf6Ahxg==" size=523 + GET State "AQAAAAMAAACDyuoi2qJNh/u+IClmfSs/GI4OlCfn07aYbb0qP7cZBA==" size=459 + GET State "AQAAAAMAAADN7KtEMllBFX9u4T5kUnIjnXfChw6TOCjwHvxo8oU75A==" size=171 + GET State "AQAAAAMAAAA+oY9n0Bc3k+9RZM6BukX42KS0xl2ozjFYPfDa5P1z3A==" size=78 + GET State "AQAAAAMAAAAvOw1H4IlQ1PnxEqJT0Fl8FaqHmVYyPEql8OrVYxma8Q==" size=16 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dACMuM913XIXgmv2vMPSoqSA0pCRSHauXQHBmDQMjTz/2w== size=16 tn_db_reads=0 tn_mem_reads=30 + register_len + read_register + log_utf8 + storage_write WRITE key='STATE' size=70 tn_db_reads=0 tn_mem_reads=0 + process_receipt receipt_id=9iNcdK3djEA1mqNP4gaQM8jcaEvBtqas31S79cAWH4TY predecessor=91505032331ede720873cf84a36609dd8755a09f816735a60ffa3ee40b4a4a67 receiver=token.sweat id=9iNcdK3djEA1mqNP4gaQM8jcaEvBtqas31S79cAWH4TY shard_cache_too_large=1 shard_cache_hit=16 shard_cache_miss=1 + GET State "AQAAAAMAAACXgiXFMX6W5mjpLP1ar2x3NuAaUrr6gENW9oVwarrSEA==" size=204482 + input + register_len + read_register + storage_read READ key='STATE' size=70 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + attached_deposit + predecessor_account_id + register_len + read_register + sha256 + read_register + storage_read READ key=dACnC8UKOTYWplfn4vFYRN7p39wm0gJXYn9DPEcW7zvjUA== size=16 tn_db_reads=6 tn_mem_reads=23 shard_cache_miss=7 + GET State "AQAAAAMAAADtoNtjXZJIrHOQzAgFmpVRvFkKsAafX2MpQJIo1t5rmA==" size=523 + GET State "AQAAAAMAAACJGNnAkjOcKsnkv/GVd/94YqiDvrFExmZKZrpRG0LOwA==" size=523 + GET State "AQAAAAMAAAC/UPkF6YkVicMoHU3NvvhNdG7CuLcvX2zCnLuK/cQg8Q==" size=523 + GET State "AQAAAAMAAAA12svz8KPdN/MNffqeioV9jYxTRCnDNDVT0ft3t8fDsA==" size=363 + GET State "AQAAAAMAAACwoD43YFgpIUEv1YCAONPOO57Chdv9xicKiEptk4VvEw==" size=107 + GET State "AQAAAAMAAAAY1esSlbimIPBTyFttWw5twY5SHP4lTgsjRppFqT7Vjg==" size=78 + GET State "AQAAAAMAAACSYYthQOgk9m8mCd4oLLeeYL79IuQu3IwsjLv37y+nEQ==" size=16 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dACnC8UKOTYWplfn4vFYRN7p39wm0gJXYn9DPEcW7zvjUA== size=16 tn_db_reads=0 tn_mem_reads=30 + register_len + read_register + sha256 + read_register + storage_read READ key=dACMuM913XIXgmv2vMPSoqSA0pCRSHauXQHBmDQMjTz/2w== size=16 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dACMuM913XIXgmv2vMPSoqSA0pCRSHauXQHBmDQMjTz/2w== size=16 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + log_utf8 + storage_write WRITE key='STATE' size=70 tn_db_reads=0 tn_mem_reads=0 + process_receipt receipt_id=22c3R5Xe82GCre4Mxa33Se7M4Rhp77RMf4ZwRjS55Ai7 predecessor=6b0021d947b74d8ab24d379f80803c5224767d3777c3d92899806dc5cecc2f77 receiver=token.sweat id=22c3R5Xe82GCre4Mxa33Se7M4Rhp77RMf4ZwRjS55Ai7 shard_cache_miss=1 shard_cache_too_large=1 shard_cache_hit=16 + GET State "AQAAAAMAAACXgiXFMX6W5mjpLP1ar2x3NuAaUrr6gENW9oVwarrSEA==" size=204482 + input + register_len + read_register + storage_read READ key='STATE' size=70 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + attached_deposit + predecessor_account_id + register_len + read_register + sha256 + read_register + storage_read READ key=dACORwZO8m3ulCvZjvsq5nRxGZlzsGPvG+CBnS52YPLMEw== size=16 tn_db_reads=6 tn_mem_reads=23 shard_cache_miss=7 + GET State "AQAAAAMAAAALLup6KiX9NEaLtiblJfALgMllvNPajq8s5bN8O9SSRw==" size=523 + GET State "AQAAAAMAAAD0pKAZ8O5KOjI2J+1TtLCAw4Mg2vigkeD/h5Wb7rloWw==" size=523 + GET State "AQAAAAMAAAByvz8xIoGP1T46AWJtxNAvIsOMIQ0kvKh1hsCa5MBMoQ==" size=523 + GET State "AQAAAAMAAAB2mt95W3Hv2s2PW2A+JYk3OHWxlLjDOJ3v/eAeoFWAcQ==" size=395 + GET State "AQAAAAMAAAB1+sLKHZ1sAxUyyiaYiJ92ImIeyFVvCxROBk/dYMgBmA==" size=75 + GET State "AQAAAAMAAAD4RwZfsxzAT8o+jYqlvGOu+vu7ox7eA086rl1x1Qap9A==" size=78 + GET State "AQAAAAMAAAC42/+buczEILdfdmrm6/Muig4L+K2KNq+ebyPEnLEH+w==" size=16 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dACORwZO8m3ulCvZjvsq5nRxGZlzsGPvG+CBnS52YPLMEw== size=16 tn_db_reads=0 tn_mem_reads=30 + register_len + read_register + sha256 + read_register + storage_read READ key=dACMuM913XIXgmv2vMPSoqSA0pCRSHauXQHBmDQMjTz/2w== size=16 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dACMuM913XIXgmv2vMPSoqSA0pCRSHauXQHBmDQMjTz/2w== size=16 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + log_utf8 + storage_write WRITE key='STATE' size=70 tn_db_reads=0 tn_mem_reads=0 + process_receipt receipt_id=B2XmBAZhY3gYwktoRiP96KBip7Jd5iQNHbRXKLW2348W predecessor=system receiver=ya_gul.near id=B2XmBAZhY3gYwktoRiP96KBip7Jd5iQNHbRXKLW2348W shard_cache_hit=4 shard_cache_miss=16 + GET State "AQAAAAMAAACtPTEmUIDbCWdKDCn9ljULb5rV/zWcOgjVuzSRR/oT9A==" size=171 + GET State "AQAAAAMAAAA3OILAzN/GgLdWjNV21AjLkxfqClPi4A46HD3G/8R1Xw==" size=491 + GET State "AQAAAAMAAAD3zcGRZ+AYxtKlGka5SfVe/PR3IBfTfXv9CV9PiouuIA==" size=171 + GET State "AQAAAAMAAACBST8r/iUITcu2DwVAiunF+9KN9rmCmRErfUoQUYcLZA==" size=47 + GET State "AQAAAAMAAADZz7T30xWub8W0nlJhqduMQtduacwFSfUVeDvBRH0YXQ==" size=171 + GET State "AQAAAAMAAADmgeLe+nNfp2pfyO1LG72DAsTpRjVlxH/0/5PfW7epGg==" size=57 + GET State "AQAAAAMAAACt3q2jlSr7WepvOadMoxKowvFI1ybb0LzHV7G4fd66tw==" size=72 + GET State "AQAAAAMAAABZKKG7qxCBy5umtgVo5Zg0IdL7uQAexwn9QNX3yVHLxA==" size=171 + GET State "AQAAAAMAAADWrt1lHw/+xKpf8yjcT3tQZWpItGGBBg8295pzxj2Mmw==" size=491 + GET State "AQAAAAMAAAC8RcqeRjjyYta9m+Q5HW6+rolOjNIzg6kQOZ3I3+0TZA==" size=171 + GET State "AQAAAAMAAADvnXMLmdRZWC8B1gnKPd30nTZDSWNoQBuppGEGTM1i+g==" size=47 + GET State "AQAAAAMAAADSlt83u+iYYtmLJ1z9lquqdzlNqdwLp99hpqq0ppo5Hw==" size=171 + GET State "AQAAAAMAAADHA+NRGNqx9uBjiX045lztD0J0qA4UcWCE6LVAZ9Bq0A==" size=55 + GET State "AQAAAAMAAAAkhO2eCiX2clAwEzLXP3w8HhFDCbhdp83/k82Zuj5hkA==" size=267 + GET State "AQAAAAMAAAC3qLe33YratvBkl37zP+KB7Sa0e0S0rfw4KODf16XvfQ==" size=81 + GET State "AQAAAAMAAABU31GU8psD2hHqCYmrImiYTtyrnLWsoePcBioDzJRjFQ==" size=52 + process_receipt receipt_id=DbRHWcLYjARUjjUZj3ERR5SGcBE7nLrXTe1dKmuLWivu predecessor=system receiver=nex1234.near id=DbRHWcLYjARUjjUZj3ERR5SGcBE7nLrXTe1dKmuLWivu shard_cache_hit=8 shard_cache_miss=14 + GET State "AQAAAAMAAACx4mc7JyJn+mpXm9rUEor8c9hKm5u2hvgKVtJByuPzYA==" size=171 + GET State "AQAAAAMAAADYWwDLAUKJSNw48ntHJ7QiEshq36NMAmeFdByMwqfY5A==" size=363 + GET State "AQAAAAMAAABq0/Aewm2960Iz1U7x/b3GClv9sxoMW3CcYABgYNxp1g==" size=139 + GET State "AQAAAAMAAABpZAbYsZPUSJyzmi4/1DvPJhVbuhGDLbaARR0L7nrIlg==" size=267 + GET State "AQAAAAMAAABmhVRtszNKEbSI03+Krgtt704Qkbu7OnNYApLe68eSEQ==" size=58 + GET State "AQAAAAMAAADfdZAIABkxV6aDazAixcwGNI8SlNggzlSmACHMLJsyRw==" size=72 + GET State "AQAAAAMAAAAHOfGQe5a487W9CCVgXHmdZMAz4WjtYWr/KsxLy/FdRw==" size=171 + GET State "AQAAAAMAAAACRfrFMgAgAsaJVq6YpkEK8slij7cdkL3GfiwjG31n0w==" size=363 + GET State "AQAAAAMAAAD/MgGejrX5g7blC69SIjZQzqOHjUCKFNzUzYfGWUnQew==" size=139 + GET State "AQAAAAMAAACy0A10gQrtQx8OD4srwN9rHrsxz9OY2UfTWC0282306Q==" size=267 + GET State "AQAAAAMAAAC2FK1bZIDnbz21GnKGczTyAbD5DoAksOFaRiJo67F70Q==" size=56 + GET State "AQAAAAMAAADh6+jJX8XcfdK7OjBhQw0x95QOzyMBGPhebhH/4HdYgw==" size=107 + GET State "AQAAAAMAAAAuo3qKJ69JefCnnv2w+vAEqLeZKjlWpv5q2pqFrIcG0g==" size=81 + GET State "AQAAAAMAAACz5PMoh0R8bk2SrcJEUIp74ryiPPyp7nDLIU2rB0yxxA==" size=9 + process_receipt receipt_id=6RiUAxqBmiBo4XnEURb87D5twzmrZjELre4cJkEzdFuu predecessor=c_shark.near receiver=xtoken.ref-finance.near id=6RiUAxqBmiBo4XnEURb87D5twzmrZjELre4cJkEzdFuu shard_cache_hit=4 shard_cache_miss=18 shard_cache_too_large=1 + GET State "AQAAAAMAAAC4cVc9AEpqgi/gR7FGPDkE0zDeCVETJbQODWEOD/JidA==" size=171 + GET State "AQAAAAMAAABZXEvbWIS2QebyZnM/kA/sWCAqGkpM1nv0scGzh4JnFw==" size=363 + GET State "AQAAAAMAAAC8XXE5W52cgz2kiHH7bd7eglCafwwEjXGwBwdJKXA+sw==" size=139 + GET State "AQAAAAMAAAAt8i7zhnWziDV6imfSj0OrJ04Vh4+sC08iUiIwIDPizA==" size=491 + GET State "AQAAAAMAAABlvcgyNzmjjm2sfvRjsrFmr5QDMVdhWyqVlZIB+fuWNQ==" size=139 + GET State "AQAAAAMAAACweD02Cp2zN563ksp+0lQC0ms4oGqniWR6cW0V9k4zAw==" size=299 + GET State "AQAAAAMAAADnowBcZISHg8xS3c2P91bxGSaesUZ0HhimG98C8IQAsQ==" size=75 + GET State "AQAAAAMAAAC7qZJ1Bvs/hWJtunNa+KnpbTazFReMOdJSJkKmhWkdZQ==" size=47 + GET State "AQAAAAMAAABPrOUKfNhlClE/JAWKbYYBi1RckLULls6qHUqS6jCBRQ==" size=75 + GET State "AQAAAAMAAACvhMbapOJ690N9ZqnlC2BlUpIzuhynSJfqLwAR0gMzjg==" size=46 + GET State "AQAAAAMAAAD7OTsNLj7gIFVykp3PN9L592PKZLMMDUibQBuVfYqMdA==" size=75 + GET State "AQAAAAMAAAAaUJLRnBON19gew7sBo9A2RrHYdXXBUxtUzW6R8mPGxQ==" size=65 + GET State "AQAAAAMAAADyr6b/+ks7t5IQfEg1Z3rqKashPiMQd3JrcRzGpfh6Kg==" size=72 + GET State "AQAAAAMAAAA7NFK2mO4nQMIO0XEGEwmM8YzmE0e9lGCURjsKNkY3aw==" size=171 + GET State "AQAAAAMAAAAIChi4CLCW4EBOTywLbzpqrAkvYjib4jL58+/QQUEcBw==" size=363 + GET State "AQAAAAMAAAAUbSmG+spqdBFYnqSmkmzgiN4JcN6s1OlxVin4cZgb8A==" size=75 + GET State "AQAAAAMAAAAQ334/bDsR18+1E/gJ9wy4Xk07V8oa9IijI00IK2majQ==" size=70 + GET State "AQAAAAMAAADN1Qr4CwxQoVwwkLU2ajpX99cD4uP7Ic5T8nAYDgTX7g==" size=242658 + input + register_len + read_register + storage_read READ key='STATE' size=154 tn_db_reads=6 tn_mem_reads=4 shard_cache_miss=7 + GET State "AQAAAAMAAABBUTXkp7eg+Qjw2RDCp6RPLAHVVGhhudw8e6sbkIk3Sg==" size=171 + GET State "AQAAAAMAAAAC4ZnAwqCSWvSdCfGZX7jW/tNTPxjXzNXab1f/Nuj3yw==" size=363 + GET State "AQAAAAMAAAAdxmNUFLym6MmRlGS7fLA3WpmrVqkDda1ey0i7c6BCmw==" size=75 + GET State "AQAAAAMAAABNGa8pq0U71DsCq2jUNbqYalZa/vAuyutLBSxYx7prMw==" size=67 + GET State "AQAAAAMAAABD1IKTgwteMx7P/kPTZJzfmKhqBYE3uQnkq2GJH3GrEg==" size=75 + GET State "AQAAAAMAAADw0CU5Cp57yyk4tF2OWAI+kng+v4piObmT+63Ciq9Lbg==" size=54 + GET State "AQAAAAMAAABoSRAr0hDG7kY8wk40X7RVSw2VTjO/KyT9grrdGb486A==" size=154 + register_len + read_register + block_timestamp + attached_deposit + predecessor_account_id + register_len + read_register + storage_read READ key=YQwAAABjX3NoYXJrLm5lYXI= size=16 tn_db_reads=8 tn_mem_reads=9 shard_cache_miss=9 + GET State "AQAAAAMAAABHLtyhYGqPrbFdoRKtJfJtE79T7CeeUsLirxWQ1oA9rA==" size=46 + GET State "AQAAAAMAAAAIKWxekuUWsoXSrm6OYUfoncCpbubRGogOS4ZGYARuoA==" size=139 + GET State "AQAAAAMAAACLst/tGHPWBokQpTI1D0ccFiq/JYOYeXApLzmidsgObQ==" size=331 + GET State "AQAAAAMAAAAbEBXTPvaO69OaIGUM4EF+XHdeO9mW19HhWOy59cdSVQ==" size=49 + GET State "AQAAAAMAAABywbDDt50oX+hNBDmPI0PUTnVvzk4zUcjDMkffIFOXUA==" size=107 + GET State "AQAAAAMAAAALf17dTRwpTzCYQDniNL9hvdBu1iIf3HtT8mrG/OEB7w==" size=491 + GET State "AQAAAAMAAAAQj/YpdxDNtk0AsPJ/EBSVVqo8JhkoX+Dztnpv8LSHSA==" size=107 + GET State "AQAAAAMAAABS8Ni/Z6eLGg5LcYNwZ/uORsPDKQL5pcEW8mm6mxx57Q==" size=60 + GET State "AQAAAAMAAADJgjp8NRvnhQzVD+23v9iRbr+qWDO8N0mf9TvhMyPamA==" size=16 + register_len + read_register + storage_write WRITE key=YQwAAABjX3NoYXJrLm5lYXI= size=16 tn_db_reads=0 tn_mem_reads=18 + register_len + read_register + log_utf8 + current_account_id + register_len + read_register + value_return + storage_write WRITE key='STATE' size=154 tn_db_reads=0 tn_mem_reads=11 + promise_batch_create + promise_batch_action_function_call + promise_batch_then + promise_batch_action_function_call + promise_return + process_receipt receipt_id=EYwsRh17pfzg1n6uWEduBbrvbWmf2hnt2g8HPeABZLHR predecessor=f55649cf4669028d5335fe2156dbce5e32d723ee7e5504f4a23ed16c7ca28aa7 receiver=tge-lockup.sweat id=EYwsRh17pfzg1n6uWEduBbrvbWmf2hnt2g8HPeABZLHR shard_cache_miss=1 shard_cache_too_large=1 shard_cache_hit=6 + GET State "AQAAAAMAAAAPGUPSlOo3VmtbyyVZoG47modtYCYLED9ZNZiuTwH2fQ==" size=233847 + attached_deposit + storage_read READ key='STATE' size=53 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + predecessor_account_id + register_len + read_register + storage_read READ key=AUAAAABmNTU2NDljZjQ2NjkwMjhkNTMzNWZlMjE1NmRiY2U1ZTMyZDcyM2VlN2U1NTA0ZjRhMjNlZDE2YzdjYTI4YWE3 size=8 tn_db_reads=15 tn_mem_reads=9 shard_cache_miss=16 + GET State "AQAAAAMAAABXMiPb1N6XbRDYIBnbQvStg4oviDarChFDHKiTiYi34w==" size=107 + GET State "AQAAAAMAAACgaE08H3uCBXbafYrPM/Grt0osnHP43GS4QyiMTjRCvg==" size=50 + GET State "AQAAAAMAAACcocireNI8gJ9qAysC+Rk3Eu3uwY/4tE7eJUY5Ibutvg==" size=75 + GET State "AQAAAAMAAABzEQH/xlZ726gSX/pyljHzaU3k6aA3v6UcJxHecXJykg==" size=203 + GET State "AQAAAAMAAAC83VNfEraqHgIZaa3BbxqyUYigrICxoP7Sh503Vt76xQ==" size=75 + GET State "AQAAAAMAAADagKiAmuI953AKS9rt6OEf6/eggOibOWcOscLzoH3tBA==" size=331 + GET State "AQAAAAMAAAAWPXjcfnaoXSEgAUSbP/6228yPFS4QW0a3V85kf0YN4g==" size=75 + GET State "AQAAAAMAAAAQxMZJHZzVh0/AZBPDRprUSfPj/2r7d1Qfdj396h1dXg==" size=331 + GET State "AQAAAAMAAAC72m0okW3IkpHM3gVQYGwTIj89liH3OPAvLJ917zxz8Q==" size=75 + GET State "AQAAAAMAAAB360bBjLvZJ7+wnXv1QaNne8FyrMbZoRiasVDyNFwk9g==" size=331 + GET State "AQAAAAMAAABWuCEWuvuCwaY0plpD1jFfwtgg3OHuDePuVvx8smrZqg==" size=75 + GET State "AQAAAAMAAABYgDV8tmMGrI8IgZu3luv2/y/MTJKAJiJ8IrPFe7eIvQ==" size=331 + GET State "AQAAAAMAAACNApwctli+S/Ous/xWxcabcFoBWckz/LT35cyRsUvDnw==" size=75 + GET State "AQAAAAMAAADBjhNrtqA+BYVZDRsa7YNL5VCTr0zRssk+GiE747IQbA==" size=107 + GET State "AQAAAAMAAABEbcQbqmliUBphz6QFQr6IP5Yvy1mM/Fqg8mFLVlIQrQ==" size=108 + GET State "AQAAAAMAAACnMF/7vxLGmEvSxXY2URK/d/ZkHpLDXrxlANHku6Uf4g==" size=8 + register_len + read_register + storage_read READ key=AADvxAAAAAAA size=149 tn_db_reads=7 tn_mem_reads=10 shard_cache_miss=8 + GET State "AQAAAAMAAADrANNSnZKaYsn7oDmwD/4XJtLwbF+stCHYQvHpAIDZmw==" size=523 + GET State "AQAAAAMAAAAtAuLEPvMjG1TUeFnrtBv0EY36bbtz7/93iXNZ190iFg==" size=523 + GET State "AQAAAAMAAAC4lRYlhJ3T3xAIKqSTfzlpCU54dcDDHQQpRStWIrlPhA==" size=523 + GET State "AQAAAAMAAAAWS7Kaxx6KM2BCgmQpRavkY7GZHCG5N/19URkJGbImHg==" size=523 + GET State "AQAAAAMAAABj8tn99hWQF/wx06S7R9XAkf/1QFwuEGIYzqGkF24JCg==" size=427 + GET State "AQAAAAMAAACScUPr4/nkIPgqBJYxRP8O02YeWe3dpYfEBMKLfBIH7A==" size=427 + GET State "AQAAAAMAAADvSLvqim8fUFA2bz+V98Az5iFpYYKhd7FH+JFGFVGBSQ==" size=55 + GET State "AQAAAAMAAAAsTPGJrP+TrgQiI5dWrxy4CWeUM++urrWZtct/09T7/Q==" size=149 + register_len + read_register + block_timestamp + log_utf8 + storage_write WRITE key=AADvxAAAAAAA size=149 tn_db_reads=0 tn_mem_reads=18 + register_len + read_register + log_utf8 + current_account_id + register_len + read_register + current_account_id + register_len + read_register + value_return + storage_write WRITE key='STATE' size=53 tn_db_reads=0 tn_mem_reads=0 + promise_batch_create + promise_batch_action_function_call + promise_batch_then + promise_batch_action_function_call + promise_return +GET State "AQAAAAMAAADnjaYMMo+3BvxmRA89HtPotXw4qm5qSRpKGfzRvbvvGQ==" size=46 +GET State "AQAAAAMAAAB2Jfe7Yd/Dee7dq/Uy4d552MexpzBzh4rTYe/hJj21pw==" size=267 +GET BlockHeight "hcR7BAAAAAA=" size=32 +GET Block "`B4epTA1V54DmRbFhFEaLyyzLBQuqq688bHNq3sNRDnTF`" size=7771 +GET ChunkExtra "lYRfbjKj7tNaKloWPka4gdGd4rVxVOL4e1KqjqSwTkYBAAAAAwAAAA==" size=101 +GET Chunks "`A36cppxDHHhKLcPjeNvEmzhwUz3y8NCnhJeC3Mffp164`" size=6114 +GET Block "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=7706 +GET BlockHeader "`B4epTA1V54DmRbFhFEaLyyzLBQuqq688bHNq3sNRDnTF`" size=6358 +GET IncomingReceipts "lYRfbjKj7tNaKloWPka4gdGd4rVxVOL4e1KqjqSwTkYDAAAAAAAAAA==" size=3385 +GET BlockHeader "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=6293 +GET BlockInfo "`2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9`" size=221 +apply_transactions shard_id=3 + process_state_update + apply num_transactions=5 shard_cache_hit=363 shard_cache_miss=13 + process_transaction tx_hash=Fdaq5LbQw49MfMp1BHMfXCkDVA3RW9ZnYqTbTSrdz2VD shard_cache_hit=4 shard_cache_miss=44 + GET State "AQAAAAMAAADxqtLVO02p0aZPFFxmVLeFoLdGlzayrTdNF1ZR049AjQ==" size=75 + GET State "AQAAAAMAAACEA//w8t2g9Jw0lPAoo5M4nzqbd8sUfx78JNH6tuJkmA==" size=171 + GET State "AQAAAAMAAABlbv8UIrIeG1xp4N0OyxqWlqN3kdBV7Nop9yS4sXZVhQ==" size=171 + GET State "AQAAAAMAAAB2g9srwvEt9c1acOQ8wewYDp+Zd1b5BrOwV7uu9iJybQ==" size=363 + GET State "AQAAAAMAAABL9EwTSaRAhFyQ6NvFUbkrbYcrErPT6qsOtRVlO7F2rw==" size=171 + GET State "AQAAAAMAAADoMlWYrKy2ZpqPoq+3nJQgXuQ75R5dOLlWMcYu/XWwDQ==" size=491 + GET State "AQAAAAMAAAAVvrQk2Y1XA4IXBllwyJkpQQid5SwfqMLlomNLsckSSw==" size=139 + GET State "AQAAAAMAAADz1dui8dTyROZd86eUI6kHuejkp2HSB8iJZqIYYWzKUQ==" size=267 + GET State "AQAAAAMAAAAYjC8vCZUj9xs+7+yWB1LzBM4tAdv+mrDk+0YaxrFy7Q==" size=139 + GET State "AQAAAAMAAACSQVHifWGjkmD0SghTaDfFy3r/FqvyQ1klQONKIGzHWw==" size=171 + GET State "AQAAAAMAAAA1ZPO/FPdGtTa2kDn/zEAatyEEpZwxtrOwrwIfrRZCTg==" size=171 + GET State "AQAAAAMAAAB9lCVPkvP//IbVnvO/0/9Wp0Hu2SEgqITwSs4GYUW7lA==" size=171 + GET State "AQAAAAMAAABVaDmEWTS1iU8bJUSewnkkAgZa2+pJdiLxTBUtlKBeqA==" size=46 + GET State "AQAAAAMAAAAT3IalBdKGwlGAnbgBkGkqlot32qxw+iMx3IjkjWxp9w==" size=107 + GET State "AQAAAAMAAAAD1ivMvRyxX+OEswV8JujofL59nXTblfUqp8hwgr3Vnw==" size=46 + GET State "AQAAAAMAAAAPPcUVoI8ZYjqP1/9UK/Sii64M2EG1bnUqIArrX4esDg==" size=75 + GET State "AQAAAAMAAACcPhwh/QSKp2PT6wXU8RtaphuZHkFCRKgFexsKdF6P9A==" size=171 + GET State "AQAAAAMAAACEn/BUDjSI/DxjmtMZf8xQj+m9Igh5XH2u2Ni2hEWFiw==" size=75 + GET State "AQAAAAMAAAAszo5kChX3BS6TeiqeW9BIkiZurbe9P3vB68FNl2m75A==" size=75 + GET State "AQAAAAMAAAA9ZjJEuOaH9hMxNZxA1JfLuL+1uqe+XoIiRTyCgxBG+w==" size=139 + GET State "AQAAAAMAAABMBWBDqwl7G1/uG8itdkmyxFQ0JrUYRdpjS8Q21uIJ7w==" size=69 + GET State "AQAAAAMAAADanpp2yYDtP3KlVZMReQlcSQ5aW691IDhGB+KfdgvR8Q==" size=72 + GET State "AQAAAAMAAABKSRZ1FjiBljEmKow3KsV0drLB9XHFNgBkMmublXA7ug==" size=75 + GET State "AQAAAAMAAAApAXQvLL60ZMMroDYk+QJY8148ocpC+rJyhdxcpTi+0g==" size=171 + GET State "AQAAAAMAAAD1biAiGOGwSoPeA+L05lyZC2med1pGTZQracP+dqYjXQ==" size=171 + GET State "AQAAAAMAAAAaOSxqIX/q1liwt7C0GDLGnByS7dD8C2pqYi5mnuCybw==" size=363 + GET State "AQAAAAMAAACvsVKAAMqr53sIUbfL+qYJSvmb6gzEn6J9MJ3xxLLJ8w==" size=171 + GET State "AQAAAAMAAADBxO9KmjW0u5z1HYhFam+PFhb03vXKL0wRCXJ5DO7+ew==" size=491 + GET State "AQAAAAMAAABA/gKyLEscQuwmTQcF50HPh6NXOYDxIruTfzuZKtDkEw==" size=139 + GET State "AQAAAAMAAADccqMNz0CC55VSYVkZkZx+EowmIQaHBlqEeD2w7BIybA==" size=267 + GET State "AQAAAAMAAADYCp0+Nmvsap/xOA5v1vvq9cfk3HSqB5ZF0seqqvBcgw==" size=139 + GET State "AQAAAAMAAAChlGWCPHbnG7k0YGOFIpMSMzk5eSvVZSztLmXUqiR3LQ==" size=171 + GET State "AQAAAAMAAAAUqpt844L1Z5x2kte/J3lNyvn1ZwJAV2zmIL2B7WqXvQ==" size=171 + GET State "AQAAAAMAAAAG0xkJDI4W5TmP5eippj0F3EUI77eDrc3x2T5wpby1ew==" size=171 + GET State "AQAAAAMAAACyJiDGQ2wxoeQk24Oj8WbEwXiQvzdTUTOcz6bhEULJdQ==" size=46 + GET State "AQAAAAMAAAC0YOGFaS+hu33TeLdYlhUUoX7WTM8WzJPZ7CJR+96S0Q==" size=107 + GET State "AQAAAAMAAAAM9LDWMPNG5bghLOMOvWTXihW0lvzbZ6c5FJHypPwXbQ==" size=46 + GET State "AQAAAAMAAADq5wyJ5fOS4Agmt8VByMSc+Eka5k/m58REoD2Le+6kpg==" size=75 + GET State "AQAAAAMAAABjVRpdoJmCkxNm1G97KR5tssrpxUlLFPLwiNAQdvV/TA==" size=171 + GET State "AQAAAAMAAACdn+csAmGsF6KMu/r6HjGD8B2eiR2goSBsWPhMkiAj4A==" size=75 + GET State "AQAAAAMAAACnDUh/g3xZjc5WHej3/UeQBWeHs+x/B/WysNgZ24ZBlA==" size=75 + GET State "AQAAAAMAAAByGvJ9MVuoxP/4Ifi7HOXIU/QXIBfxt4O7rEidKgDdzw==" size=139 + GET State "AQAAAAMAAAA1dIvR6SY9Fy97bcs/tSnN1HBS1WxEB5Fz67kzfhDDjA==" size=103 + GET State "AQAAAAMAAACMVWd9ffJp3UJzuOKW32GvDUFT1lYwKfy14r9VEDjWww==" size=9 + process_transaction tx_hash=D8U1btrcfRbbf8wV1uWLmHKY1hDHBYUySwwE7hmPWjAP shard_cache_hit=6 shard_cache_miss=33 + GET State "AQAAAAMAAADkIAUCRNAbphwjWtNqZCPOwrv5oJ9zg5vSs1sOaIQ9Ag==" size=363 + GET State "AQAAAAMAAAD4v6ckZ4PKcz/yb25m3g6w6WoPvBjXsHRjuBNqNWIr0A==" size=171 + GET State "AQAAAAMAAAC8nG7gO9l3A0rwfb7mwnlJHYeAPj8gH1CSvyCdE0xL2A==" size=491 + GET State "AQAAAAMAAAAKgeBY/xTqa+TrOC7SativnQZ/T+5G361hgHs3ccXJ/A==" size=171 + GET State "AQAAAAMAAADN8Yu2He26muTPd7MimOj0QQQ+6DNb+n0HO88h+libhg==" size=491 + GET State "AQAAAAMAAAAHRTF+xPmpLrRnv7ihg9KIl4Hl5Yo6mnbC+PlcjlK7Nw==" size=139 + GET State "AQAAAAMAAACg9MNjSx5VijPwN4RSYIXdu1dNbO0uwgjM6M/v3woQXA==" size=459 + GET State "AQAAAAMAAAAuM+0k3u4jHMQMHVb3sI4tif7IQQ4gqnSJf+jFggcYJg==" size=107 + GET State "AQAAAAMAAAAHHJtRpkCRMfiQfyz7dOdnk9FiTZUzQGcUwjDuv5HUWQ==" size=267 + GET State "AQAAAAMAAADcD0i9t8lLYwEPirCv2Zgo4lAQkSNq5lXninplwNTlvg==" size=171 + GET State "AQAAAAMAAAA3v6ph6fQnMIb/zwVmZotNxj/pbTzSzR4NAyzuW+2BNA==" size=47 + GET State "AQAAAAMAAACbj7uK+xOoZDFSzrlZSmgwdbuXntdH/Xl9RVZ2l/GEcg==" size=75 + GET State "AQAAAAMAAACZG0hbwCnK0UGEsXwMTeCKZar4xcH8xo+f+o2fvCfQQw==" size=55 + GET State "AQAAAAMAAAA7U1ShZ/XeR2sX1MEf/dJXsBUxfuvddEIBJAiCF1pVLA==" size=72 + GET State "AQAAAAMAAAA3C9Np1BgChlJk/o0OGP9Y24vJNk5VgeyDaJ/ddIXfVQ==" size=363 + GET State "AQAAAAMAAADKrimT/t7TEGUyx4BLzU7AHeEO1wXGDZyusM0urRutsQ==" size=171 + GET State "AQAAAAMAAAAKjE+1QNb+eShlohvHm91lTDcr0Ilg5/ggt8pvoyqh9A==" size=491 + GET State "AQAAAAMAAADx8p5WzfmlOjEYokFgqTHHYUyS7dsapSC0gmAMyZBsSA==" size=171 + GET State "AQAAAAMAAADyxXPNiXB/HJcKtGFMcShWKc5hNLA5A6Mw0csID6gntw==" size=491 + GET State "AQAAAAMAAABcN8M65qFpXf24pjHuNJVa7I2CVo3NGFKGwcPjKWyahA==" size=139 + GET State "AQAAAAMAAACYnHJhjWbsYCk/fhnI+ram6tLRt2AOStmjBTVXCBGX/A==" size=459 + GET State "AQAAAAMAAACz3colcJbs+rXgtj2+iEHlB3ioaLGAM/piXlAgpJlLLw==" size=107 + GET State "AQAAAAMAAABgMZYKMq6NGUVa532EWaddcg5/6hjnKo80j6F2/HR84w==" size=267 + GET State "AQAAAAMAAADDjvtRtj0e8Nvv6g7P5p1PLvYwrELyiyHIgv3y6cN/0Q==" size=171 + GET State "AQAAAAMAAADGXVH2L9BVj8va0VrIqJr0ZocHLtCQwbrnbWlZM5T8rg==" size=47 + GET State "AQAAAAMAAABgg44I1mE3hU+adT/4awYbrs5CIJ94vwGLQNosW6F/qw==" size=75 + GET State "AQAAAAMAAABnGSPJCDmTMkGAcgq/CfmehKpgbT/pYhO+PTZZOHz8mw==" size=53 + GET State "AQAAAAMAAABX2vKAIV5RA2lhciYhKxd4UGeV3vKsZYl4yA7mAv+gTQ==" size=523 + GET State "AQAAAAMAAABtrpNxjMxhdvFsXBzRWDGnKgz9Pxe5EAKu50IRLXJKDQ==" size=523 + GET State "AQAAAAMAAADFaEIhkB3819WXfUNp1DZnRiOk3zYh3/PHhCYlUGIehQ==" size=171 + GET State "AQAAAAMAAACfPnkHxMzH2K+OzOH4JGNd6rpE/5/UkLp8eFJTqgsOoQ==" size=75 + GET State "AQAAAAMAAABp6+ra7LAYALsvlaA+vs3OqsnMwCYhSML1y8iEwSJ6JA==" size=80 + GET State "AQAAAAMAAABNYk0Lh8WhJ3oSZzkaiHQ/UdbX5Vf/2W8+AFjoL5sskQ==" size=9 + process_transaction tx_hash=FjwDNPxNxgpipRX3s48kRNUGfaPwgxG64N7KRQ7YULx2 shard_cache_miss=24 shard_cache_hit=8 + GET State "AQAAAAMAAABNw1Zz8tUtJHN+GbuKmloArsZJTPiPP575BxpIJ5Yttg==" size=171 + GET State "AQAAAAMAAAA2Cx9y5dNb2cVEdTSoWwb/iylcnB1LON9OjQHcqBD0/A==" size=491 + GET State "AQAAAAMAAAAwAUaPWBdfC6LxyevYqgXayaCyUe6ezkAOYAeWqIYY9w==" size=139 + GET State "AQAAAAMAAACq6B2R5bL/VoPvO9i56nJ/7Mn81rvr7Bg11NSnCFzRVQ==" size=363 + GET State "AQAAAAMAAADIFb9qlNhgB0ps4M7FWSO9BuaU1xzWJYNX4TZKnOUuUg==" size=139 + GET State "AQAAAAMAAADgEeTWzzTFC8Tsg06F7GZ9ai2LTblyCNQW5mMWxGTyAQ==" size=363 + GET State "AQAAAAMAAADnfsnFRN9UGZJ9hqyvmPeRy6hZgL6AaCL/GLMijiVW0A==" size=107 + GET State "AQAAAAMAAABTZFqhCjjjyZfYIuYtFsXp8YBGn16bmevwiBmACmhmkw==" size=46 + GET State "AQAAAAMAAADw+nOhFDaZLYVM3b87l1u41NM+DusG4sQI9AzZTxWzjg==" size=75 + GET State "AQAAAAMAAADn//Z24foNrdlYE2gjI7rYktEqJMSNoTxybbYx50Pc2g==" size=61 + GET State "AQAAAAMAAADRF/ehxFje6cDY/IQ0vClfV182Tec1GVuisK1JSbiywg==" size=72 + GET State "AQAAAAMAAADmSsX37y33vi8SRsY9dTWBF78MsbKDkAANLz0GcDh4lQ==" size=171 + GET State "AQAAAAMAAACPbw2LJKyAMvH2NFTaql3TWP3dfjzPsoiKiCLaaZWBog==" size=491 + GET State "AQAAAAMAAABD99AQiXLeMA1oBDbZx4YPTLkPVmmrOVF4r327dU0jpA==" size=139 + GET State "AQAAAAMAAAAaYYs/QrLpwT42+MyaC08x1P/qiVq8Fh8uiIPMAWHZ+w==" size=363 + GET State "AQAAAAMAAAD+HcRRxwcjdbLR263/gcZ3p6LUQHSZLKqEZyK+5FMs4w==" size=139 + GET State "AQAAAAMAAACrNuDFVi5TjaAJPJFUV5Sk7DU/IjkJTYarvxT3OlbLVA==" size=363 + GET State "AQAAAAMAAADmr+XDTxAF10X89wLV11bvsJYydTMRFGUxKoU36lfzjA==" size=107 + GET State "AQAAAAMAAABYyZ7yQtAf+zHHq3ikP4Mw9lwyYWOp/AfjmKxRwpreRw==" size=46 + GET State "AQAAAAMAAADEXtvtHtxO61jRFg4EOTWa8knDSwOID8s15arli0cnJg==" size=75 + GET State "AQAAAAMAAAAHymXIgJrSVtrWXsxXZhm18/0ar3ecqAwYAVpv7hAhFA==" size=59 + GET State "AQAAAAMAAAD/kaymX+drnrIXGn94CTLX++66w3h4s/wqMW9MfhvVxw==" size=75 + GET State "AQAAAAMAAAB7AgUTf0i5n0KEpTIBlmU0yw9YzdU4exsei3KHKaLxBg==" size=81 + GET State "AQAAAAMAAAC6DNWO/oBYhjqYyrkwe9FJVjECp68OV6UmB5Ib1WkoSw==" size=74 + process_transaction tx_hash=HN32RHYR9J1up2MguxxCpcyhnFwbxttKNTscT4n748SR shard_cache_miss=20 shard_cache_hit=24 + GET State "AQAAAAMAAADKWVI081r7Dlg2ZINL6QD9SuCy4704wjE+3hTMo29qTA==" size=171 + GET State "AQAAAAMAAACK9AD5Be+CnVbLqNHCBlp5L7zwRxc3W76iJfFLMHabSA==" size=363 + GET State "AQAAAAMAAADQk2i73cmgV571CibrGp6dglV9p8pPqZhx68Grcb9dWw==" size=171 + GET State "AQAAAAMAAAC4voKWBCrD7rLPTcrF8QY05dc4plU4P3Ly7duZOThMFA==" size=491 + GET State "AQAAAAMAAACwSzZfjaHpHNoT66XWP3waTfD7pJ6u1vdR455XANw1Sg==" size=139 + GET State "AQAAAAMAAAAaS5B3IN2SSScKbkYDVOTEus0v5eIl82K9c9DzLywjWg==" size=491 + GET State "AQAAAAMAAAC1G5T6wTzUadqkHCZfAlyYcKEdjJUGEfWHtEPeDaNNYw==" size=75 + GET State "AQAAAAMAAAAoIu6HOCa93PyEWfBcCgEuJo2h7dYlnxZk6uA4qqoUvg==" size=139 + GET State "AQAAAAMAAACWj5lakBOqIEiwWkC87pI7DpTD5olJMzBUBGK744OHvQ==" size=207 + GET State "AQAAAAMAAADJXaPNPECZc/kjys3qLkM1XsokqdxGHNPNNyHee5h5/g==" size=46 + GET State "AQAAAAMAAAByQa5X2WJb61SBvxZ1uRvOItsW2GSCBvOcbKohaRi+Ag==" size=75 + GET State "AQAAAAMAAACyYEp+lMB+9PsjOsIloiBOJbmEbsIqd+pjIqxTPd6R9g==" size=139 + GET State "AQAAAAMAAAACyLK5CYbzjp+F0nnCRL8sMMB8kT2Xc5L5RiS1aiuRpg==" size=46 + GET State "AQAAAAMAAABgMojEwSfaxDv98CIk7hOsHOwXsoJ0ESRWFC45yH8KrQ==" size=75 + GET State "AQAAAAMAAAC95XsBQxwf4ovOJ2x/Hyxlv5YGiU3lwVGBTvl8sCqGlA==" size=60 + GET State "AQAAAAMAAAAp6WEYNYDWUm2a4stvlD6d97txdc0VvTBw/rwO25zIVA==" size=72 + GET State "AQAAAAMAAAAimBIW8OHeU7hCigvTg3AOBxRTFWIf+OJzamanhg38RQ==" size=235 + GET State "AQAAAAMAAADU5i30PSa1TZ+jUtmXqTxI2JTXmGedqbDPQcHcKf4Z1A==" size=75 + GET State "AQAAAAMAAACY9NBmHcjfWtWhSnE7mMUD16AxTBBV4O7Rbgpg60BScA==" size=80 + GET State "AQAAAAMAAABcwRX9qq3lp0RcVfpwagNgDnq1wWqPGPTfCtx9LkqYRg==" size=9 + process_transaction tx_hash=A76PNL2oTtuUtpe51dpCrcCU2NShF2vkB6ZTLHoXEnKx + process_receipt receipt_id=8s88Y7hyAXrmZngJGpRBsvsbqYzAQiJbqdwSMESeLmr5 predecessor=5176796f9aa738d338815f138aa137b13cf3464417c0d18875e0904826b3a471 receiver=token.sweat id=8s88Y7hyAXrmZngJGpRBsvsbqYzAQiJbqdwSMESeLmr5 shard_cache_hit=22 shard_cache_miss=17 shard_cache_too_large=1 + GET State "AQAAAAMAAAACMUVMqTdFjtW1vTCedI0fEDFVmpngK4MdYAjr30ZYkA==" size=171 + GET State "AQAAAAMAAAC9CM39E5IB4pHCkDt89T8PitmHk3J690uQ4js/3aX2SQ==" size=491 + GET State "AQAAAAMAAADRidvgI7Q0CVSqs2DulwoQ9jFIgfOM8XbHnWfRQH9Ywg==" size=171 + GET State "AQAAAAMAAAAgJrxILIyLHJb+k9UHcw/xFtFloUUT6F5MowgKi5mZIw==" size=491 + GET State "AQAAAAMAAAA1FjBFN9kGkDKQbGNdRcsdFd/3M+Ym1ZsbVqXCZOaYOg==" size=139 + GET State "AQAAAAMAAAB3R3/sMSjP56Ci71KKoQWmhv9MIY5ebtNdXdfAyrnN/Q==" size=459 + GET State "AQAAAAMAAAAs0+Qk3pRo/LhxC67w94SN+bkV359xRRICEh9vVDsSug==" size=139 + GET State "AQAAAAMAAAC2KcDVK1ISLkmUA7crMssgAY4RVIS/ZvbKz+Syi49gyA==" size=235 + GET State "AQAAAAMAAAABnvukH2E7x1b6DLq4p/u8j5DeAaywm9zCOyQyP/R0lw==" size=171 + GET State "AQAAAAMAAABP5gvJg1ZvyXWlDtVqakBX2Y/NFRg8kv7CAskGNFkGOg==" size=75 + GET State "AQAAAAMAAADD9HK0r9C2hYRdpt3xha2RTvaeo/hC1BY1E5xcVAtJFw==" size=75 + GET State "AQAAAAMAAABn79FSPQqBoZ6okjlGiUE0LD6Hw72fcphWGYWXchA8Sg==" size=139 + GET State "AQAAAAMAAAC70aZqVBgqNWcloLfHTyaEpEKghfDReMZjxxm3zKX49A==" size=75 + GET State "AQAAAAMAAADZmOIci2jGanv5HrUINxreVAZIdhB5OLa6rLCxbhtkMg==" size=107 + GET State "AQAAAAMAAAAwx7ATwyUb0Z60u1mGrDFc+XkOcTkOX1yUl+1MIfusBg==" size=53 + GET State "AQAAAAMAAAA26I1mdf5g9hp3hP4rqU5zllQhwpNVgIPKndjE58W1BQ==" size=72 + GET State "AQAAAAMAAACXgiXFMX6W5mjpLP1ar2x3NuAaUrr6gENW9oVwarrSEA==" size=204482 + input + register_len + read_register + storage_read READ key='STATE' size=70 tn_db_reads=20 tn_mem_reads=0 shard_cache_hit=4 shard_cache_miss=17 + GET State "AQAAAAMAAABJwA2kCsiZH+q5FJkU6/YFNCS4kEC4o+LGlXqzCxLVyg==" size=75 + GET State "AQAAAAMAAACYdcGhe0MSI9KPvUc9VQD87S0H1NI6WYCxeIMBK1qBzw==" size=363 + GET State "AQAAAAMAAACvWh0dUe0mMn7JCcENG2AZsswkQJy7cGNPoLlX1+9BKg==" size=171 + GET State "AQAAAAMAAAATGJrKoDgX8Fjk9G+uGiDf9VxirBngRlZGjV5dHqpgCA==" size=491 + GET State "AQAAAAMAAAAzq0VgI75H61wvg2R2pj2+kl98SeBz+3yUHY9tspVw5g==" size=75 + GET State "AQAAAAMAAAA9zPDfUBDondtruz9TUzDVKXUVxKoVrrI6g8VM3cPQ9Q==" size=427 + GET State "AQAAAAMAAABz1KVePuD3WGqiKFWugF7/hueJXu7dZs3zgNXEkR2tjA==" size=75 + GET State "AQAAAAMAAACW4euh31bpfDB+DvBzHvxhAhMqkTZRxza6JmAv48GxaQ==" size=107 + GET State "AQAAAAMAAABV6bpi7FJexs8MD/5EOhLgZ5yXtlz2djHGuOxg4doq5A==" size=47 + GET State "AQAAAAMAAABKw6e9/K44m49OmK1v4mEsSqJJsW6lLZeOCu34dAbj4Q==" size=107 + GET State "AQAAAAMAAAClxrz0uzMiiG968Cr5FB7VFmDs/IzxdsD96ose1s3TAw==" size=46 + GET State "AQAAAAMAAABKIekWRvbYc+kAq1hVSAyu69DVLNa32Ybf7CMuoS+a3A==" size=75 + GET State "AQAAAAMAAABR4HsNqcY7gCnJtwWkYIqxWVqXG0/45tZQHnuo6Cn3KA==" size=139 + GET State "AQAAAAMAAAA0J1iZMelA6l0D8WaHSLPCERWRqVUwAtXe3W/q6mwMog==" size=75 + GET State "AQAAAAMAAAA2nfxnOuDSEgMJg66ncWJjrTnv2i3D4VHSd8TYfiv7sQ==" size=107 + GET State "AQAAAAMAAAB1yL5X9Rqx1pqOqaBBZ+a4I6Ds5xTFe5Jpre1nio5Lpw==" size=50 + GET State "AQAAAAMAAABiuHhpcX4q3nL37K4wvXbESG9GzAZ10p+UgXto0kGjew==" size=75 + register_len + read_register + attached_deposit + predecessor_account_id + register_len + read_register + sha256 + read_register + storage_read READ key=dABK7mFzV4udPEI8yX8POKq8Wl5w5RfoYRlpIFNAsg3e6A== size=16 tn_db_reads=10 tn_mem_reads=19 shard_cache_miss=11 + GET State "AQAAAAMAAABZKpyVk66bTchKeRWvI+MpIr94haGHOYQvF0b3Jyp88Q==" size=75 + GET State "AQAAAAMAAAAdT0laTV9IiFGOC5sMZVDdDpWDoDhDkDw0HiY2sa6GUA==" size=47 + GET State "AQAAAAMAAABkpurkWzW7IGhq1WzIy1poryKpVvjR+miZ1pUZ38MDlw==" size=523 + GET State "AQAAAAMAAABPU4JILfIPh/3N1is0GhwIepoeDrO3tC7yMvdF09O76w==" size=523 + GET State "AQAAAAMAAAC3szhloCSzyn5F+bLOJQA0e3uICNOc9VpiOysbobm/+g==" size=523 + GET State "AQAAAAMAAABXsdxypIsCkSlp+7VCOhI3u/xVcoDT2ta7niLpHqG6Hw==" size=523 + GET State "AQAAAAMAAABKwITqd9ot3rPAuDenQ3hEXcL9n2QFljb5gnH2WqOvAw==" size=523 + GET State "AQAAAAMAAAA/BBYu/M/7oPRpS4bgj1aIP5iFsC8ZHQ7X/G2PhKMJaA==" size=427 + GET State "AQAAAAMAAACNvC7pS2YT25lgDrpVse1OZdEnq5M61cv/NamDl9utAQ==" size=139 + GET State "AQAAAAMAAABvx8jjCRRsyaQyUpUUPZcZkhp/4q1wTExzzXxcodl/8g==" size=78 + GET State "AQAAAAMAAAC088+aIFThzb7QRu0Vvcw3eQquJQ0UWZ6da9Y5eMhoWA==" size=16 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dABK7mFzV4udPEI8yX8POKq8Wl5w5RfoYRlpIFNAsg3e6A== size=16 tn_db_reads=0 tn_mem_reads=30 + register_len + read_register + sha256 + read_register + storage_read READ key=dAB5TzQEBlMFNXqT0Rov62u3lZFZl/34YtX6HLfvAJ5gJw== size=16 tn_db_reads=6 tn_mem_reads=22 shard_cache_miss=7 + GET State "AQAAAAMAAABln029Z4pvticm63q+27R6d07qsE21oxphl1jLQYa9Ag==" size=523 + GET State "AQAAAAMAAABeGDfCjKjMqvh4cJ4cB4cuacqavDlSDt1TWM/Sd5gAJQ==" size=523 + GET State "AQAAAAMAAABouD+BZ97wI0pa2f9gStah6gfQLs3QP7e9pKRSjhvACQ==" size=523 + GET State "AQAAAAMAAACKSK4aEZU83pTnr+IN4t9nkp8fcmWvHDlCrCV8rwSo6w==" size=523 + GET State "AQAAAAMAAAAyH5Ka8gZ3/QTBQ8SFdKNxbUMRxyvleNSCAmwuy+ffsw==" size=331 + GET State "AQAAAAMAAAAakmRUmwB2POaK3hmccQS/YE7opUHg1eehkT7A6eXUPQ==" size=79 + GET State "AQAAAAMAAAAaqjKs2r4rBeiTUPsintjyw8x2Ec3bTigE1pXV/I4IGA==" size=16 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dAB5TzQEBlMFNXqT0Rov62u3lZFZl/34YtX6HLfvAJ5gJw== size=16 tn_db_reads=0 tn_mem_reads=29 + register_len + read_register + log_utf8 + storage_write WRITE key='STATE' size=70 tn_db_reads=0 tn_mem_reads=21 + process_receipt receipt_id=J6m2SVXWwC3oR2g2QDbbqZf7GCTYx8tQETZuFCi1QDBU predecessor=system receiver=seensayer.near id=J6m2SVXWwC3oR2g2QDbbqZf7GCTYx8tQETZuFCi1QDBU shard_cache_miss=24 shard_cache_hit=6 + GET State "AQAAAAMAAAD29oQwPhdWu1mWW1iLPnajcJnrp4+fH2Jh7AiJgD60QQ==" size=491 + GET State "AQAAAAMAAAA0YN6hIc7NldOxseJWxXOG1BhUXNMaApec9zMLPnYXiQ==" size=171 + GET State "AQAAAAMAAADz8jbvJjTDNyGbvLM9X+vi5a0UIffwqmqdXrUzolHR2Q==" size=491 + GET State "AQAAAAMAAAC0cq6558TgRUTB8ZrRQOl2ry1XvpnMI2/cynFWxjYZ3w==" size=139 + GET State "AQAAAAMAAADxEjDPMaV2sHpTyHGjr/U1JXr2hly+vueaaLOjZ67fhQ==" size=491 + GET State "AQAAAAMAAADHrTmETbgungO8P/4hCapVib/scicdfeo+3R9QAdr1wA==" size=107 + GET State "AQAAAAMAAAAarQd3GpyJYfrHROABiha71P7wHePQff/qicpAt2/Y1g==" size=139 + GET State "AQAAAAMAAABo4qDc6Z2m4L+FMnNzdrBsjJKxmlgsN2fbwRPY/tVlbQ==" size=75 + GET State "AQAAAAMAAAAQ9TXmwl3UltJl0lIgQtAJcakozoK/u/ctlN47rFMOhA==" size=58 + GET State "AQAAAAMAAAAz0B6gw4HQ+8r8oWccR+jD4YUEBlAFgkIkiPNh76FQ3g==" size=72 + GET State "AQAAAAMAAAAkCiA14x1S14GEVuBOOfvB/rf9r+fo/2VKHEJJ35vfDA==" size=491 + GET State "AQAAAAMAAADi+RdeyrWbQvmstg3uoCZFmMX0NZnnXA2n2PcgkMqW8A==" size=171 + GET State "AQAAAAMAAADmVBQjs78d/aO7Jsu/acytORk4kgzP6CV1v+XEvVi/Bw==" size=491 + GET State "AQAAAAMAAABRscX8bRvFq9uZ33bV5wZZZ5KEo5/wHSpdPBzL0jF27g==" size=139 + GET State "AQAAAAMAAADwj3XMK1+DNkDccs0i3bdCGMI61lTwYSfz8bqNc5qGTw==" size=491 + GET State "AQAAAAMAAADTXMGPrxgBKwgdP486Q+Tmf/v6hxBxxSYXe6Cr9uD7JA==" size=107 + GET State "AQAAAAMAAAANSNitS1dePVpZXvqLsBVPlKaudUFbkS5lBThs7ppmZQ==" size=139 + GET State "AQAAAAMAAADkQSPBLlkd9UfUUfYp379sPi+t1HapBECsANxZxYzsmg==" size=75 + GET State "AQAAAAMAAADmAbhVHuS2VpAISbVDJmebcjUTFDc1iAltcgvveEMdeA==" size=56 + GET State "AQAAAAMAAAAIHQAVaLjpGfM9QrGnCEG5mf2+K9i/tktTwrhzs3ea3Q==" size=523 + GET State "AQAAAAMAAAAf1Betn3mDEl/7q1aws90UJgHJc+y90SskLkzYr8ZNAQ==" size=299 + GET State "AQAAAAMAAAD7tMW1kdRJ3c0vObWh0yGqrkPjKCIPJl9WL/HRwYzPYw==" size=75 + GET State "AQAAAAMAAADskS5c0P9lY1sPLTxkmi9ncIrLjV4ePOZycRT1gL7GjQ==" size=80 + GET State "AQAAAAMAAABdC5InjREotdoKoRRyLzaaVyd38u8BJpO5wv6/VLTQXg==" size=52 + process_receipt receipt_id=AZawzkDrLTMrbxZTBWwgkYTY5AEcEBJQYsVrRckDayf4 predecessor=f56f893d221e60cb27d7533eb000fd5d9d54e96075cf4ac6a0bafa8588382d0d receiver=tge-lockup.sweat id=AZawzkDrLTMrbxZTBWwgkYTY5AEcEBJQYsVrRckDayf4 shard_cache_miss=7 shard_cache_too_large=1 shard_cache_hit=10 + GET State "AQAAAAMAAABiDq1L/Ce7ul+BkZg14slz5Ryy5D6QVv7q4c4IRltHFg==" size=171 + GET State "AQAAAAMAAADf3FhyVokGMdmZlk33O64tf9DzqpI3SKrBxbBJQ+Qy/Q==" size=491 + GET State "AQAAAAMAAAD3zfLdj1ILtBqQt8BMQbZlT+OkonG065f0i7X2c1ev0A==" size=139 + GET State "AQAAAAMAAAD//r/T8cSU80m0mexQ+yi+TL0NpV+8l483uZgPw0EwdA==" size=75 + GET State "AQAAAAMAAAATMFeacWWHpx3dTLuvYbqj2jc3l5rLNLEiB4BqoVwssA==" size=62 + GET State "AQAAAAMAAAAb8VMAAYoqeD8m/WLpMU5m12yLyznTFIhk+mG/tZ7aDQ==" size=72 + GET State "AQAAAAMAAAAPGUPSlOo3VmtbyyVZoG47modtYCYLED9ZNZiuTwH2fQ==" size=233847 + attached_deposit + storage_read READ key='STATE' size=53 tn_db_reads=4 tn_mem_reads=6 shard_cache_hit=2 shard_cache_miss=3 + GET State "AQAAAAMAAACHpvRsU2MKUDlDW3QO/Dg3WDhvOPgJ+tShL52aNVt3dQ==" size=107 + GET State "AQAAAAMAAABNLUb5pVZqivr3CfNkDw7vIpHRRH7TwxFhNZczCQ2FPQ==" size=60 + GET State "AQAAAAMAAABuo+g5db0i/QkTpjfS81eVFRwNwjV3UeVlSou58T2/nw==" size=75 + register_len + read_register + predecessor_account_id + register_len + read_register + storage_read READ key=AUAAAABmNTZmODkzZDIyMWU2MGNiMjdkNzUzM2ViMDAwZmQ1ZDlkNTRlOTYwNzVjZjRhYzZhMGJhZmE4NTg4MzgyZDBk size=8 tn_db_reads=15 tn_mem_reads=9 shard_cache_miss=9 shard_cache_hit=7 + GET State "AQAAAAMAAACHunSZeEx/akI0S52jUFz23QmpRxlbGuaQVkSjwGUzmw==" size=107 + GET State "AQAAAAMAAABfB6UbUfplPEsjbEm5M8TWDeiNM59xiMkEaBjSC77uPA==" size=75 + GET State "AQAAAAMAAABOL/m6MCex+eDApMsNQnV8oXq1AjRrmM7XSNttLVZyJQ==" size=203 + GET State "AQAAAAMAAADWcEI9w4OhTg4VHjTkJbZIYFA4bczudzO2RR2C/TyyaQ==" size=75 + GET State "AQAAAAMAAAAe7Yc969w+Fz9WNAfuGPA5A1Y+sBPbyTU1ZMN4YUc52g==" size=331 + GET State "AQAAAAMAAAAEfirM9ATXXqe9mxJkGs45kMWuAlwKsRjCPcCaIp8CgQ==" size=75 + GET State "AQAAAAMAAAAd/kFRA8ZoCMiGnYH9mn7+c/2mcW/EkoVbW/TThj1KUg==" size=267 + GET State "AQAAAAMAAADMBoZBBFhpevLfrjnK+oEjday4bFeHtUrrD0kNNsJSQg==" size=108 + GET State "AQAAAAMAAACF7PGSKMeV2e52lOkfwlHMEDduXBi8Epy/yzvwKMOkKA==" size=8 + register_len + read_register + storage_read READ key=AJkDxQAAAAAA size=149 tn_db_reads=7 tn_mem_reads=10 shard_cache_miss=8 + GET State "AQAAAAMAAADEUvUTcNNWLC6DsIHqi8Lmu/dQDSuw60SWGdj86YB03Q==" size=523 + GET State "AQAAAAMAAAAr0MFUsMVbSEVw9nLuclDskBtVFzS/JphqkIl9w4PjUg==" size=523 + GET State "AQAAAAMAAAD5DBTD/2RB1j5gb8o/4xZxmOx8uzX9vKCMG6vYEh1DKg==" size=523 + GET State "AQAAAAMAAABZ0j3p7dIN8gAq5ZTEJZjFjQfHb5af9e179VTJAQK94w==" size=523 + GET State "AQAAAAMAAAAdF3syL/5J/cliIb7ga0j/a0IBbJIsuxWVNwOUFgYDrA==" size=427 + GET State "AQAAAAMAAACQSaQ/oPucGlrcG2eSeE4pFEdo/IkI0HrHfVlACs5kIQ==" size=459 + GET State "AQAAAAMAAAC5gEnjNR6lf3hxMd2GVmas2o9/lOD50ecr0yEzU0ce0g==" size=55 + GET State "AQAAAAMAAACOjevSvnHP5zxa8/2t8Y43U8Zrgqd9xaqRUhaRzoSbvA==" size=149 + register_len + read_register + block_timestamp + log_utf8 + storage_write WRITE key=AJkDxQAAAAAA size=149 tn_db_reads=0 tn_mem_reads=18 + register_len + read_register + log_utf8 + current_account_id + register_len + read_register + current_account_id + register_len + read_register + value_return + storage_write WRITE key='STATE' size=53 tn_db_reads=0 tn_mem_reads=11 + promise_batch_create + promise_batch_action_function_call + promise_batch_then + promise_batch_action_function_call + promise_return + process_receipt receipt_id=454QLeWg3Yy4HQLcowLtvQGJfwzw2reUREx1sLtv1gga predecessor=ecf09b42d152bd3dcb1956b3a0dbd64e43b8e3184046928942f271498146b69d receiver=token.sweat id=454QLeWg3Yy4HQLcowLtvQGJfwzw2reUREx1sLtv1gga shard_cache_hit=16 shard_cache_miss=1 shard_cache_too_large=1 + GET State "AQAAAAMAAACXgiXFMX6W5mjpLP1ar2x3NuAaUrr6gENW9oVwarrSEA==" size=204482 + input + register_len + read_register + storage_read READ key='STATE' size=70 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + attached_deposit + predecessor_account_id + register_len + read_register + sha256 + read_register + storage_read READ key=dAAFt1h0JJ6/VI/xqn2ZOsHuAf8WstqKkeeLmQoJ9YDwvg== size=16 tn_db_reads=7 tn_mem_reads=22 shard_cache_miss=8 + GET State "AQAAAAMAAACuTUpTtoJSoK3jgN//e3TCTjL46mvySBrM+RoJGRNS1A==" size=523 + GET State "AQAAAAMAAACZ6aB28Jxk0Uabh8i6bfkFNxFCdWDEDKsXvyHTI/+98w==" size=523 + GET State "AQAAAAMAAAAwovm8pTkVRjrWD/GtPBYm36F4tPvBLXTEszdyrxzBGg==" size=523 + GET State "AQAAAAMAAACcjKXP6krHl3qQnhx47COWJFt2KuU83il7GC2ySTM3iA==" size=523 + GET State "AQAAAAMAAABUkIWa5kTJ2hKUoH4vSBUBY4vSQVTiGqFgabtkt4Iiyw==" size=299 + GET State "AQAAAAMAAABwgVmduvLQhjcOXKc+cKhf6z/EoAU+hgXsraY2OT5DHA==" size=75 + GET State "AQAAAAMAAAArJ33GKNuX6pC+veWNZQoakOglIVZS7qaT7/w6nWgIlg==" size=78 + GET State "AQAAAAMAAACGBFDuqZWvQM9/eOW/g7ER71xM9Xj41BUh2wK67zN/BA==" size=16 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dAAFt1h0JJ6/VI/xqn2ZOsHuAf8WstqKkeeLmQoJ9YDwvg== size=16 tn_db_reads=0 tn_mem_reads=30 + register_len + read_register + sha256 + read_register + storage_read READ key=dACRL39kPByn1Ji8WOC3tXvFTCr967NOZFH36i+UpTh7kA== size=16 tn_db_reads=6 tn_mem_reads=22 shard_cache_miss=7 + GET State "AQAAAAMAAACtkVhEhoemnsIkbwX/r8j6ycp3NQXSXh3SyMNSC/rqnw==" size=523 + GET State "AQAAAAMAAADmKLnR3T2vXYFQ40+3wEwgfnUgI5LyccrrbeKNedQbXw==" size=523 + GET State "AQAAAAMAAACTjlxD877fGbT2TbRKU2RK4YaxVwxH3Le2SUYU8zlttA==" size=523 + GET State "AQAAAAMAAAA0tRM0koQ36YbKrqa5aPL1V/SkBMLfClXlTBJtHK6MJw==" size=523 + GET State "AQAAAAMAAACwhE+VWkB5UKlHA3VOTYtbw5tJKJUj9b6COz1zyMec6A==" size=235 + GET State "AQAAAAMAAACci8ZdDwOhI94dCNRA6HYJnqkHtIJJMPbTmVfjbSZpqQ==" size=79 + GET State "AQAAAAMAAAA9POLp3EBFcBXRzSMgVz6VDcTHlPtGXy6OxVvbnKufKQ==" size=16 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dACRL39kPByn1Ji8WOC3tXvFTCr967NOZFH36i+UpTh7kA== size=16 tn_db_reads=0 tn_mem_reads=29 + register_len + read_register + log_utf8 + storage_write WRITE key='STATE' size=70 tn_db_reads=0 tn_mem_reads=0 + process_receipt receipt_id=Jf6Hh3cgANXPYkd536rGMFbWkXzkA1CBNFNJhwn3ksL predecessor=token.sweat receiver=tge-lockup.sweat id=Jf6Hh3cgANXPYkd536rGMFbWkXzkA1CBNFNJhwn3ksL shard_cache_hit=7 shard_cache_miss=6 shard_cache_too_large=1 + GET State "AQAAAAMAAAA4Bj4Ybk2dHzDn66X1isxZs7R0wN4F4ymI+SJW9dF7vg==" size=99 + GET State "AQAAAAMAAACCv5DoNBevaj1CZ4ZTMOLqWUExF9eFBOctU5itgfjvBg==" size=32 + GET State "AQAAAAMAAABVOByJE9+3TSL6SQORux7l8pF6UfYRl/bAziOT+oNB+g==" size=99 + GET State "AQAAAAMAAADd5e6syp0zVvRwFVxAP3tugtgkUHFaC8tHSWt/DHxg1Q==" size=99 + GET State "AQAAAAMAAADmA7chXAvwM6uXuCGwwUM6aWOYle+KCObZwuHYmHjsxg==" size=460 + GET State "AQAAAAMAAAAPGUPSlOo3VmtbyyVZoG47modtYCYLED9ZNZiuTwH2fQ==" size=233847 + current_account_id + register_len + read_register + predecessor_account_id + register_len + read_register + attached_deposit + input + register_len + read_register + storage_read READ key='STATE' size=53 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + promise_results_count + promise_result + register_len + read_register + value_return + storage_write WRITE key='STATE' size=53 tn_db_reads=0 tn_mem_reads=0 + process_receipt receipt_id=8vJXghN3eeQr2HLsSMEUmVb85sZymMSKseYNfKmK328o predecessor=xtoken.ref-finance.near receiver=token.v2.ref-finance.near id=8vJXghN3eeQr2HLsSMEUmVb85sZymMSKseYNfKmK328o shard_cache_miss=8 shard_cache_hit=27 shard_cache_too_large=1 + GET State "AQAAAAMAAACw+Mw+S9U2TIXgw22BXDgPalCMvEdybCJJdj/qEUKbGg==" size=46 + GET State "AQAAAAMAAACqehmBgTm46unDNrU+I4uyXil7Cea/k6Q0/uPGWJHhJw==" size=75 + GET State "AQAAAAMAAAA75d4uuSAcigznyIU/QwasvazkBnv2JoTRz2aC9gwm8Q==" size=67 + GET State "AQAAAAMAAAA3/CYFs8gjMfVDPZ47jZtZSjb+X5oMX4t8bYsLLlUtrQ==" size=72 + GET State "AQAAAAMAAABDCxFFMT6amU8P1SE0n25dsk4Cq2eeBf8hrDCDDSEEqA==" size=46 + GET State "AQAAAAMAAABL9JGJex64hdpKTOm5tJfOtyKU9+HdtKNPCnd6Lf1f8Q==" size=75 + GET State "AQAAAAMAAACTOuPBh0lgYV5SGA1rb8iANhtSFflv3B8WHvzPXXAT0A==" size=67 + GET State "AQAAAAMAAAD2XEFln+5f1kr9s57XB02+e7rIPSrVqjwPjX40sZXzWg==" size=184887 + input + register_len + read_register + storage_read READ key='STATE' size=29 tn_db_reads=5 tn_mem_reads=15 shard_cache_miss=6 + GET State "AQAAAAMAAAB5slTO0lZtFWHg8kiQ384xD6zDvVui3nCQ1Qve3C3usQ==" size=46 + GET State "AQAAAAMAAAAlrHUDIqRofeiGQWPl46BKG6/S6S7UAK7y25lUurTyDQ==" size=75 + GET State "AQAAAAMAAACXQI6pGQw7cXo3M0KeyyrmnpwG1o3DjpHEL2Cj9pExdQ==" size=64 + GET State "AQAAAAMAAACt3YO00ZHwRqI/9pZQQOA1QyygEOrERmoDqA/ezmElEQ==" size=75 + GET State "AQAAAAMAAAAB+CPWYY1JBcjFx5AYJ5h7Ay2IguNv7LSCmyXk533LBQ==" size=54 + GET State "AQAAAAMAAAA1v4ChjqxzIznDp+RSHJ26AznMZBHQVE4Y5FRnHYQbsw==" size=29 + register_len + read_register + attached_deposit + predecessor_account_id + register_len + read_register + storage_read READ key=YRcAAAB4dG9rZW4ucmVmLWZpbmFuY2UubmVhcg== size=16 tn_db_reads=7 tn_mem_reads=19 shard_cache_miss=8 + GET State "AQAAAAMAAADR0ZAyAS+o4IFO8OxF6Z7qXXmOBR20cqEcGkvD1Sudbg==" size=46 + GET State "AQAAAAMAAAC08yuXEqFkriIqhNQPBUfA7QU1N87O8lyndRONEJfCxQ==" size=171 + GET State "AQAAAAMAAAD8JDpELViexZDW85KeNcEio3dTVyyhHB0z8B00P1zLfQ==" size=523 + GET State "AQAAAAMAAACRvGxKHKvG0uJG55mjVOoCCAXOBFnfQmJHJ8B5/emHkw==" size=49 + GET State "AQAAAAMAAAA4xZoeWX0RVoPwmtYs6HkA4InMvzMhJMKnzDMMMMK1qg==" size=75 + GET State "AQAAAAMAAAC+e802dCg7Yl4HwxdN/qs/ZaxbVbWCHBnKkgJ9+edz2g==" size=331 + GET State "AQAAAAMAAACmwQZbhCnZPyHkV3HSU5ysS7JwveS9Xmjf2VKUM+nCCw==" size=72 + GET State "AQAAAAMAAAD/Zwcgl756bc8kF7QvaGIXo9i8jOGmhFGpc3jEp/tU2A==" size=16 + register_len + read_register + storage_write WRITE key=YRcAAAB4dG9rZW4ucmVmLWZpbmFuY2UubmVhcg== size=16 tn_db_reads=0 tn_mem_reads=27 + register_len + read_register + storage_read READ key=YQwAAABjX3NoYXJrLm5lYXI= size=16 tn_db_reads=6 tn_mem_reads=21 shard_cache_miss=7 + GET State "AQAAAAMAAACmK9pkZAXZhAttrjxi+JF1V47A5vHnfwwCtBRPFxRhZw==" size=331 + GET State "AQAAAAMAAAB5biNjW6dnpkpuwKZkwq3CsJiMu5eQxcfjyiDSGKbO9Q==" size=49 + GET State "AQAAAAMAAACg6yiCv+WBMy1QRIZueZ3Jc2O0tiuu/WYnpvoYNqyi2Q==" size=107 + GET State "AQAAAAMAAACHfemkQ8AuBGnTA7j4HdrwSb7BhivLx4IVaR0yn13P4w==" size=491 + GET State "AQAAAAMAAAAhKzds8u7Q4RNrNTc2O59ApxT7JsjUg8K+CkSRsjVipg==" size=139 + GET State "AQAAAAMAAAC1u3u4jty64NXQzjuD2n16fq4+Rm1isPif5OOgCKCJSg==" size=60 + GET State "AQAAAAMAAAA3Rwj/93Gd1ZeeyHXVbNIob2089+wxejslYyqrKOw3uw==" size=16 + register_len + read_register + storage_write WRITE key=YQwAAABjX3NoYXJrLm5lYXI= size=16 tn_db_reads=0 tn_mem_reads=28 + register_len + read_register + log_utf8 + storage_write WRITE key='STATE' size=29 tn_db_reads=0 tn_mem_reads=21 + process_receipt receipt_id=9r96hCrbYcEzqaTRrA6mZHv26KJqrihjuomU6Hf6vfTX predecessor=xtoken.ref-finance.near receiver=xtoken.ref-finance.near id=9r96hCrbYcEzqaTRrA6mZHv26KJqrihjuomU6Hf6vfTX + process_receipt receipt_id=7d1QXW1is9bzovZSA1XJK1AWTrPJPixNVReuCihPBQFh predecessor=tge-lockup.sweat receiver=token.sweat id=7d1QXW1is9bzovZSA1XJK1AWTrPJPixNVReuCihPBQFh shard_cache_miss=1 shard_cache_hit=16 shard_cache_too_large=1 + GET State "AQAAAAMAAACXgiXFMX6W5mjpLP1ar2x3NuAaUrr6gENW9oVwarrSEA==" size=204482 + input + register_len + read_register + storage_read READ key='STATE' size=70 tn_db_reads=0 tn_mem_reads=0 + register_len + read_register + attached_deposit + predecessor_account_id + register_len + read_register + sha256 + read_register + storage_read READ key=dAAB1Xeh4CCdobM+obRpH4JXDt2O2nMKTqWUeQ1KJgAEoQ== size=16 tn_db_reads=6 tn_mem_reads=23 shard_cache_miss=7 + GET State "AQAAAAMAAAC8+gO5GoETBrdHcQyk7aVya7wb5kM5lEc22B5kamx5pA==" size=523 + GET State "AQAAAAMAAACABho0so+3YX60kas3GUCxVgCv5yOB9seQDMoNTFuf8g==" size=523 + GET State "AQAAAAMAAABVaWO9HvMKD0Evd4k9nZBn9wDa4Fns8EgnP4n7MaU5Eg==" size=523 + GET State "AQAAAAMAAADHekdI0jc4QFCAPRsGzIeo1fohhoggDRAO2+ReSSm7Qw==" size=363 + GET State "AQAAAAMAAABgAVtH2ASPH5KBjhd1mwriZlzn+oI4G3fLHU4C5l5ajw==" size=75 + GET State "AQAAAAMAAABplE+XgkF5ikW4IBwE5G3godKSnSF1hFoA0XRs1a4Klw==" size=78 + GET State "AQAAAAMAAACmoweiS+MZf/yRShVZVkwEWyFA6TPz3oPPnzWsCMSsKQ==" size=16 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dAAB1Xeh4CCdobM+obRpH4JXDt2O2nMKTqWUeQ1KJgAEoQ== size=16 tn_db_reads=0 tn_mem_reads=30 + register_len + read_register + sha256 + read_register + storage_read READ key=dADxm6GtX42yjBwdIGbiSDm7iQhPciO8kYpEkZc7soPMgQ== size=16 tn_db_reads=6 tn_mem_reads=22 shard_cache_miss=7 + GET State "AQAAAAMAAABIkcDF3CLIiL0owXMNVk17OrkJ41N62wYhHjFzpHkGxA==" size=523 + GET State "AQAAAAMAAACffWmcA1oY1s5/teNBnpx5mgP0rjMty84C/MSU81upAQ==" size=523 + GET State "AQAAAAMAAACXReOGX3TYgf5Xke9u7iTRoGyQuDp5rza/9QcMx81X3Q==" size=523 + GET State "AQAAAAMAAADH8UvZvihz70YWsdq1Gd1UgRBnX7u/wNqxhaJyNr6zrQ==" size=523 + GET State "AQAAAAMAAAADWX3iIPKcqwrWgif19xZrKwAge8SXB+9e63peZizfpA==" size=363 + GET State "AQAAAAMAAAAfvXJHOXtABRuPUw7ozBqYpySUuN1zhsy8ou2z9ed9yA==" size=79 + GET State "AQAAAAMAAAAWyt7uFMbpLJ/n1q5fAYox10VQ5CFu9Thb/mwFVKE9TQ==" size=16 + register_len + read_register + sha256 + read_register + storage_write WRITE key=dADxm6GtX42yjBwdIGbiSDm7iQhPciO8kYpEkZc7soPMgQ== size=16 tn_db_reads=0 tn_mem_reads=29 + register_len + read_register + log_utf8 + storage_write WRITE key='STATE' size=70 tn_db_reads=0 tn_mem_reads=0 + process_receipt receipt_id=EvN6kHZCYkBr4E8V8jm5R97Nf7paX6xhbtz57YZbHsn predecessor=tge-lockup.sweat receiver=tge-lockup.sweat id=EvN6kHZCYkBr4E8V8jm5R97Nf7paX6xhbtz57YZbHsn + GET State "AQAAAAMAAACkguG5dTImfV36KmQ3taJAODOzbOD9hKM5ZfyXuWC+Lg==" size=171 + GET State "AQAAAAMAAAD8G8F4tg+XA3LB6afU4hMYszKt8+RXFDpV5Ku5daK+jA==" size=363 + GET State "AQAAAAMAAAC0l0ImeuUUdNElOQgmBtQUbJJIs6S2CgHGkKcreSMUbw==" size=139 + GET State "AQAAAAMAAAAlLU0MwJcy4PJdGcNfgwR3O4QQ4BqNah4JjK+sf0VZFw==" size=491 + GET State "AQAAAAMAAADb7AukmziPfE2wOvt69K/jRQ4Geib3HzO8eXzxrFTB4g==" size=139 + GET State "AQAAAAMAAADgARfm0MWjgChQL/XFR5d2ciPw4qhAGBEe6KCKlaAleA==" size=299 + GET State "AQAAAAMAAABmMjue+5LpiRHdFOLKafI2VTQoYA4oJ9HR5W7al1bWDw==" size=75 + GET State "AQAAAAMAAABZ+knUkYFJZhlvW9xFX4eh018rFs9S12as91XY8dVylQ==" size=47 + GET State "AQAAAAMAAACj6q9+Dipi/r5wk3OwGEiceA2CbHxDqEO+8S8T2FKSkQ==" size=75 + GET State "AQAAAAMAAAAzuhZEGG7MAPCNqA95JQfZPzMI/C5rgrJIyoHqtGa1YA==" size=46 + GET State "AQAAAAMAAAAjdJgEFDMGbOzSObtcC2dTFVay+HXPw7+1tpXjzWOjwQ==" size=75 + GET State "AQAAAAMAAAB5whCaUDhJasHxB517QpNAct88fcnfAksTTzyokApkSQ==" size=65 + GET State "AQAAAAMAAADjaumhnfQta5gpKDzWJ6KEz9EXHEOVidKuzh3n6xveuA==" size=72 +GET State "AQAAAAMAAABZjpgt+AWnu4i8StJ/favzVb7mTVE6iRXj5od86SDywA==" size=46 +GET State "AQAAAAMAAABzgrY4eDwrumAp97R1ZfZ9yAwPa8dupU+8dLmalXm2fw==" size=267 diff --git a/runtime/runtime-params-estimator/res/fungible_token.wasm b/runtime/runtime-params-estimator/res/fungible_token.wasm new file mode 100755 index 000000000..0b023af17 Binary files /dev/null and b/runtime/runtime-params-estimator/res/fungible_token.wasm differ diff --git a/runtime/runtime-params-estimator/res/lockup_contract.wasm b/runtime/runtime-params-estimator/res/lockup_contract.wasm new file mode 100755 index 000000000..fe09dbdc9 Binary files /dev/null and b/runtime/runtime-params-estimator/res/lockup_contract.wasm differ diff --git a/runtime/runtime-params-estimator/res/mission_control.wasm b/runtime/runtime-params-estimator/res/mission_control.wasm new file mode 100755 index 000000000..050f36f30 Binary files /dev/null and b/runtime/runtime-params-estimator/res/mission_control.wasm differ diff --git a/runtime/runtime-params-estimator/res/staking_pool.wasm b/runtime/runtime-params-estimator/res/staking_pool.wasm new file mode 100755 index 000000000..161c719bc Binary files /dev/null and b/runtime/runtime-params-estimator/res/staking_pool.wasm differ diff --git a/runtime/runtime-params-estimator/res/status_message.wasm b/runtime/runtime-params-estimator/res/status_message.wasm new file mode 100755 index 000000000..ec27e33b1 Binary files /dev/null and b/runtime/runtime-params-estimator/res/status_message.wasm differ diff --git a/runtime/runtime-params-estimator/res/voting_contract.wasm b/runtime/runtime-params-estimator/res/voting_contract.wasm new file mode 100755 index 000000000..7b10db3a9 Binary files /dev/null and b/runtime/runtime-params-estimator/res/voting_contract.wasm differ diff --git a/runtime/runtime-params-estimator/res/whitelist.wasm b/runtime/runtime-params-estimator/res/whitelist.wasm new file mode 100755 index 000000000..9eb4b17c6 Binary files /dev/null and b/runtime/runtime-params-estimator/res/whitelist.wasm differ diff --git a/runtime/runtime-params-estimator/setup.sh b/runtime/runtime-params-estimator/setup.sh new file mode 100755 index 000000000..40b47b51e --- /dev/null +++ b/runtime/runtime-params-estimator/setup.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +cd test-binaries || exit 1; + +ensure_repo () { + if [[ -e $1 ]]; then + cd $1; + git pull + else + git clone --depth=1 https://github.com/near/$1; + fi +} + +ensure_repo unc-sdk-rs; +ensure_repo core-contracts; diff --git a/runtime/runtime-params-estimator/src/action_costs.rs b/runtime/runtime-params-estimator/src/action_costs.rs new file mode 100644 index 000000000..4335866d9 --- /dev/null +++ b/runtime/runtime-params-estimator/src/action_costs.rs @@ -0,0 +1,848 @@ +//! Estimation functions for action costs, separated by send and exec. +//! +//! Estimations in this module are more tailored towards specific gas parameters +//! compared to those in the parent module. But the estimations here potential +//! miss some overhead that is outside action verification and outside action +//! application. But in combination with the wholistic action cost estimation, +//! the picture should be fairly complete. + +use crate::estimator_context::{EstimatorContext, Testbed}; +use crate::gas_cost::{GasCost, NonNegativeTolerance}; +use crate::transaction_builder::AccountRequirement; +use crate::utils::{average_cost, percentiles}; +use unc_crypto::{KeyType, PublicKey}; +use unc_primitives::account::{AccessKey, AccessKeyPermission, FunctionCallPermission}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::{ActionReceipt, Receipt}; +use unc_primitives::transaction::Action; +use unc_primitives::types::{AccountId, Gas}; +use std::iter; + +const GAS_1_MICROSECOND: Gas = 1_000_000_000; +const GAS_1_NANOSECOND: Gas = 1_000_000; +const GAS_100_PICOSECONDS: Gas = 100_000; + +/// A builder object for constructing action cost estimations. +/// +/// This module uses `ActionEstimation` as a builder object to specify the +/// details of each action estimation. For example, creating an account has the +/// requirement that the account does not exist, yet. But for a staking action, +/// it must exist. The builder object makes it easy to specify these +/// requirements separately for each estimation, with only a small amount of +/// boiler-plate code repeated. +/// +/// Besides account id requirements, the builder also accepts a few other +/// settings. See the available methods and their doc comments. +/// +/// Once `ActionEstimation` is complete, call either `verify_cost` or +/// `apply_cost` to receive just the execution cost or just the sender cost. +/// This will run a loop internally that spawns a bunch of actions using +/// different accounts. This allows to average the cost of a number of runs to +/// make the result more stable. +/// +/// By default, the inner actions are also multiplied within a receipt. This is +/// to reduce the overhead noise of the receipt cost, which can often dominate +/// compared to the cost of a single action inside. The only problem is that all +/// actions inside a receipt must share the receiver and the sender account ids. +/// This makes action duplication unsuitable for actions that cannot be +/// repeated, such as creating or deleting an account. In those cases, set inner +/// iterations to 1. +struct ActionEstimation { + /// generate account ids from the transaction builder with requirements + signer: AccountRequirement, + predecessor: AccountRequirement, + receiver: AccountRequirement, + /// the actions to estimate + actions: Vec, + /// how often actions are repeated in a receipt + inner_iters: usize, + /// how many receipts to measure + outer_iters: usize, + /// how many iterations to ignore for measurements + warmup: usize, + /// What statistic to report when combining outer iterations. + /// + /// By default, the average is reported. + reported_statistic: Statistic, + /// the gas metric to measure + metric: crate::config::GasMetric, + /// subtract the cost of an empty receipt from the measured cost + /// (`false` is only really useful for action receipt creation cost) + subtract_base: bool, + /// Constant to which the estimation is rounded up in case the estimation + /// turns out smaller, to avoid unstable estimations. + /// + /// The costs computed here are often very small. The noise can be larger + /// than the measurement in many cases. However, we also don't care too much + /// about the exact value as long as it is small enough. "Small enough" has + /// to be defined per parameter, as it is usually higher for base costs + /// compared to per-byte costs. This field is used to set the threshold, + /// everything below it will be rounded up. + /// + /// This is also used to set the tolerance towards the negative. Results in + /// the range [-min_gas,+min_gas] are set to min_gas and marked as good + /// results. Anything below -min_gas is set to zero and marked as underflow. + min_gas: Gas, +} + +enum Statistic { + /// Arithmetic mean, high variance is marked as uncertain. + Average, + /// Use a specific value from the distribution. + /// + /// The (potential) uncertainty of this measurement is inherited. High + /// variance is ignored, this is usually the reason why this statistic is + /// used over the mean in the first place. + /// + /// Usage: `Percentile(0.99)` gives the 99th percentile. + Percentile(f32), +} + +impl ActionEstimation { + /// Create a new action estimation that can be modified using builder-style + /// methods. + /// + /// The object returned by this constructor uses random and unused accounts + /// for signer, predecessor, and receiver. This means the sender is not the + /// receiver. Further, the default returned here uses 100 inner iterations, + /// thereby duplicating all given actions 100 fold inside each receipt. + /// + /// Note that the object returned here does not contain any actions, yet. It + /// will operate on an action receipt with no actions inside, unless actions + /// are added. + fn new(ctx: &mut EstimatorContext) -> Self { + Self { + signer: AccountRequirement::RandomUnused, + predecessor: AccountRequirement::RandomUnused, + receiver: AccountRequirement::RandomUnused, + actions: vec![], + inner_iters: 100, + outer_iters: ctx.config.iter_per_block, + warmup: ctx.config.warmup_iters_per_block, + reported_statistic: Statistic::Average, + metric: ctx.config.metric, + subtract_base: true, + // This is a reasonable limit because even the cheapest action + // (transfer) is 115 us per component. Only for the per-byte costs + // we need a smaller limit, because these costs can go below 1 ns. + // They are changed on a case-by-case basis. + min_gas: GAS_1_MICROSECOND, + } + } + + /// Create a new action estimation that can be modified using builder-style + /// methods and sets the accounts ids such that the signer, sender, and + /// receiver are all the same account id. + /// + /// This constructor is also used for execution estimations because: + /// (1) Some actions require sender = receiver to execute without an error. + /// (2) It does not matter for execution performance. + fn new_sir(ctx: &mut EstimatorContext) -> Self { + Self { + signer: AccountRequirement::RandomUnused, + predecessor: AccountRequirement::SameAsSigner, + receiver: AccountRequirement::SameAsSigner, + ..Self::new(ctx) + } + } + + /// Set how to generate the predecessor, also known as sender, for each + /// transaction or action receipt. + fn predecessor(mut self, predecessor: AccountRequirement) -> Self { + self.predecessor = predecessor; + self + } + + /// Set how to generate the receiver account id for each transaction or + /// action receipt. + fn receiver(mut self, receiver: AccountRequirement) -> Self { + self.receiver = receiver; + self + } + + /// Add an action that will be duplicated for every inner iteration. + /// + /// Calling this multiple times is allowed and inner iterations will + /// duplicate the full group as a block, rather than individual actions. + /// (3 * AB = ABABAB, not AAABBB) + fn add_action(mut self, action: Action) -> Self { + self.actions.push(action); + self + } + + /// Set how many times the actions are duplicated per receipt or transaction. + fn inner_iters(mut self, inner_iters: usize) -> Self { + self.inner_iters = inner_iters; + self + } + + /// Set how many receipts or transactions are measured at least. + /// + /// This overrides the CLI argument that is usually used for number of + /// iterations. Use this when a certain estimation requires a minimum amount + /// of iterations. + fn min_outer_iters(mut self, outer_iters: usize) -> Self { + self.outer_iters = self.outer_iters.max(outer_iters); + self + } + + /// If enabled, the estimation will automatically subtract the cost of an + /// empty action receipt from the measurement. (enabled by default) + fn subtract_base(mut self, yes: bool) -> Self { + self.subtract_base = yes; + self + } + + /// Set the smallest gas value for which we need accurate estimations, + /// values below will be clamped. + fn min_gas(mut self, gas: Gas) -> Self { + self.min_gas = gas; + self + } + + /// If enabled, the outer iterations are aggregated to a percentile value + /// instead of to the average for the reported cost. + /// + /// Ideally, an estimation produces a stable output. In that case the + /// average is a good statistic. But some estimations keep having outliers. + /// If we cannot get rid of those, we can fall back to using a percentile of + /// the sample distribution as a more meaningful statistic. + fn report_percentile(mut self, rank: f32) -> Self { + self.reported_statistic = Statistic::Percentile(rank); + self + } + + /// Estimate the gas cost for converting an action in a transaction to one in an + /// action receipt, without network costs. + /// + /// To convert a transaction into a receipt, each action has to be verified. + /// This happens on a different shard than the action execution and should + /// therefore be estimated and charged separately. + /// + /// Network costs should also be taken into account here but we don't do that, + /// yet. + #[track_caller] + fn verify_cost(&self, testbed: &mut Testbed) -> GasCost { + self.estimate_average_cost(testbed, Self::verify_actions_cost) + } + + /// Estimate the cost for executing the actions in the builder. + /// + /// This is the "apply" cost only, without validation, without sending and + /// without overhead that does not scale with the number of actions. + #[track_caller] + fn apply_cost(&self, testbed: &mut Testbed) -> GasCost { + self.estimate_average_cost(testbed, Self::apply_actions_cost) + } + + /// Estimate the cost of verifying a set of actions once. + #[track_caller] + fn verify_actions_cost(&self, testbed: &mut Testbed, actions: Vec) -> GasCost { + let tb = testbed.transaction_builder(); + let signer_id = tb.account_by_requirement(self.signer, None); + let predecessor_id = tb.account_by_requirement(self.predecessor, Some(&signer_id)); + let receiver_id = tb.account_by_requirement(self.receiver, Some(&signer_id)); + let tx = tb.transaction_from_actions(predecessor_id, receiver_id, actions); + testbed.verify_transaction(&tx, self.metric) + } + + /// Estimate the cost of applying a set of actions once. + #[track_caller] + fn apply_actions_cost(&self, testbed: &mut Testbed, actions: Vec) -> GasCost { + let tb = testbed.transaction_builder(); + + let signer_id = tb.account_by_requirement(self.signer, None); + let predecessor_id = tb.account_by_requirement(self.predecessor, Some(&signer_id)); + let receiver_id = tb.account_by_requirement(self.receiver, Some(&signer_id)); + let signer_public_key = PublicKey::from_seed(KeyType::ED25519, signer_id.as_str()); + + let action_receipt = ActionReceipt { + signer_id, + signer_public_key, + gas_price: 100_000_000, + output_data_receivers: vec![], + input_data_ids: vec![], + actions, + }; + let receipt = Receipt { + predecessor_id, + receiver_id, + receipt_id: CryptoHash::new(), + receipt: unc_primitives::receipt::ReceiptEnum::Action(action_receipt), + }; + testbed.apply_action_receipt(&receipt, self.metric) + } + + /// Take a function that executes a list of actions on a testbed, execute + /// and measure it multiple times and return the average cost. + #[track_caller] + fn estimate_average_cost( + &self, + testbed: &mut Testbed, + estimated_fn: fn(&Self, &mut Testbed, Vec) -> GasCost, + ) -> GasCost { + assert!( + !self.actions.is_empty() || self.inner_iters == 1, + "inner iterations don't work if there are no actions to multiply" + ); + let num_total_actions = self.actions.len() * self.inner_iters; + let actions: Vec = + self.actions.iter().cloned().cycle().take(num_total_actions).collect(); + + let gas_results = iter::repeat_with(|| estimated_fn(self, testbed, actions.clone())) + .skip(self.warmup) + .take(self.outer_iters) + .collect(); + + // This could be cached for efficiency. But experience so far shows that + // reusing caches values for many future estimations leads to the + // problem that a single "HIGH-VARIANCE" uncertain estimation can spoil + // all following estimations. In this case, rerunning is cheap and it + // ensures the base is computed in a very similar state of the machine as + // the measurement it is subtracted from. + let base = + if self.subtract_base { estimated_fn(self, testbed, vec![]) } else { GasCost::zero() }; + + let cost_per_tx = match self.reported_statistic { + Statistic::Average => average_cost(gas_results), + Statistic::Percentile(rank) => percentiles(gas_results, &[rank]).next().unwrap(), + }; + let gas_tolerance = self.inner_iters as u64 * self.min_gas; + let gas_per_action = cost_per_tx + .saturating_sub(&base, &NonNegativeTolerance::AbsoluteTolerance(gas_tolerance)) + / self.inner_iters as u64; + + // Set small (but not underflowed) results to the minimum value. + // This avoids flaky estimation results. + if !gas_per_action.is_uncertain() { + gas_per_action.min_gas(self.min_gas) + } else { + gas_per_action + } + } +} + +pub(crate) fn create_account_send_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(create_account_action()) + .receiver(AccountRequirement::SubOfSigner) + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn create_account_send_not_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new(ctx) + .add_action(create_account_action()) + .receiver(AccountRequirement::SubOfSigner) + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn create_account_exec(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(create_account_action()) + .receiver(AccountRequirement::SubOfSigner) + .inner_iters(1) // creating account works only once in a receipt + .add_action(create_transfer_action()) // must have balance for storage + .apply_cost(&mut ctx.testbed()) +} + +pub(crate) fn delete_account_send_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(delete_account_action()) + .inner_iters(1) // only one account deletion possible + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn delete_account_send_not_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new(ctx) + .add_action(delete_account_action()) + .inner_iters(1) // only one account deletion possible + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn delete_account_exec(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(delete_account_action()) + .inner_iters(1) // only one account deletion possible + .apply_cost(&mut ctx.testbed()) +} + +pub(crate) fn deploy_contract_base_send_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(deploy_action(ActionSize::Min)) + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn deploy_contract_base_send_not_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new(ctx) + .add_action(deploy_action(ActionSize::Min)) + .verify_cost(&mut ctx.testbed()) +} + +/// Note: This is not the best estimation because a dummy contract is clearly +/// not the worst-case scenario for gas costs. +pub(crate) fn deploy_contract_base_exec(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(deploy_action(ActionSize::Min)) + .apply_cost(&mut ctx.testbed()) +} + +pub(crate) fn deploy_contract_byte_send_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(deploy_action(ActionSize::Max)) + .inner_iters(1) // circumvent TX size limit + // parameter today: 6_812_999 + // typical estimation: < 100_000 (<100ps) + // we expect this to stay near 0 + .min_gas(GAS_100_PICOSECONDS) + .verify_cost(&mut ctx.testbed()) + / ActionSize::Max.deploy_contract() +} + +pub(crate) fn deploy_contract_byte_send_not_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new(ctx) + .add_action(deploy_action(ActionSize::Max)) + .inner_iters(1) // circumvent TX size limit + // parameter today: 6_812_999 + // typical estimation: < 100_000 (<100ps) + // we expect this to stay near 0 + .min_gas(GAS_100_PICOSECONDS) + .verify_cost(&mut ctx.testbed()) + / ActionSize::Max.deploy_contract() +} + +pub(crate) fn deploy_contract_byte_exec(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(deploy_action(ActionSize::Max)) + .inner_iters(1) // circumvent TX size limit + // parameter today: 64_572_944 + // typical estimation: 8_000_000 (8ns) + // setting limit to 1ns because 1us would be too high + .min_gas(GAS_1_NANOSECOND) + .apply_cost(&mut ctx.testbed()) + / ActionSize::Max.deploy_contract() +} + +pub(crate) fn function_call_base_send_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(function_call_action(ActionSize::Min)) + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn function_call_base_send_not_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new(ctx) + .add_action(function_call_action(ActionSize::Min)) + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn function_call_base_exec(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(function_call_action(ActionSize::Min)) + .apply_cost(&mut ctx.testbed()) +} + +pub(crate) fn function_call_byte_send_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(function_call_action(ActionSize::Max)) + // parameter today: 2_235_934 + // typical estimation: 1_000 - 2_000 (1-2ns) + // setting limit to 1ns because lower values are mostly noisy + .min_gas(GAS_1_NANOSECOND) + .verify_cost(&mut ctx.testbed()) + / ActionSize::Max.function_call_payload() +} + +pub(crate) fn function_call_byte_send_not_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new(ctx) + .add_action(function_call_action(ActionSize::Max)) + // parameter today: 2_235_934 + // typical estimation: 1_000 - 2_000 (1-2ns) + // setting limit to 1ns because lower values are mostly noisy + .min_gas(GAS_1_NANOSECOND) + .verify_cost(&mut ctx.testbed()) + / ActionSize::Max.function_call_payload() +} + +pub(crate) fn function_call_byte_exec(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(function_call_action(ActionSize::Max)) + // parameter today: 2_235_934 + // typical estimation: 2_000_000 (2ns) + // setting limit to 1ns because lower values are mostly noisy + .min_gas(GAS_1_NANOSECOND) + .apply_cost(&mut ctx.testbed()) + / ActionSize::Max.function_call_payload() +} + +pub(crate) fn transfer_send_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx).add_action(transfer_action()).verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn transfer_send_not_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new(ctx).add_action(transfer_action()).verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn transfer_exec(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx).add_action(transfer_action()).apply_cost(&mut ctx.testbed()) +} + +pub(crate) fn stake_send_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx).add_action(stake_action()).verify_cost(&mut ctx.testbed()) +} + +/// This is not a useful action, as staking only works with sender = receiver. +/// But since this fails only in the exec step, we must still charge a fitting +/// amount of gas in the send step. +pub(crate) fn stake_send_not_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new(ctx) + .add_action(stake_action()) + .predecessor(AccountRequirement::SameAsSigner) + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn stake_exec(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(stake_action()) + .predecessor(AccountRequirement::SameAsSigner) + .receiver(AccountRequirement::SameAsSigner) // staking must be local + .apply_cost(&mut ctx.testbed()) +} + +pub(crate) fn add_full_access_key_send_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(add_full_access_key_action()) + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn add_full_access_key_send_not_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new(ctx) + .add_action(add_full_access_key_action()) + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn add_full_access_key_exec(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(add_full_access_key_action()) + .inner_iters(1) // adding the same key a second time would fail + .apply_cost(&mut ctx.testbed()) +} + +pub(crate) fn add_function_call_key_base_send_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(add_fn_access_key_action(ActionSize::Min)) + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn add_function_call_key_base_send_not_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new(ctx) + .add_action(add_fn_access_key_action(ActionSize::Min)) + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn add_function_call_key_base_exec(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(add_fn_access_key_action(ActionSize::Min)) + .inner_iters(1) // adding the same key a second time would fail + .apply_cost(&mut ctx.testbed()) +} + +pub(crate) fn add_function_call_key_byte_send_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(add_fn_access_key_action(ActionSize::Max)) + // parameter today: 1_925_331 + // typical estimation: < 200_000 (<200ps) + // we expect this to stay near 0 + .min_gas(GAS_100_PICOSECONDS) + .verify_cost(&mut ctx.testbed()) + / ActionSize::Max.key_methods_list() +} + +pub(crate) fn add_function_call_key_byte_send_not_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new(ctx) + .add_action(add_fn_access_key_action(ActionSize::Max)) + // parameter today: 1_925_331 + // typical estimation: < 200_000 (<200ps) + // we expect this to stay near 0 + .min_gas(GAS_100_PICOSECONDS) + .verify_cost(&mut ctx.testbed()) + / ActionSize::Max.key_methods_list() +} + +pub(crate) fn add_function_call_key_byte_exec(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .add_action(add_fn_access_key_action(ActionSize::Max)) + .inner_iters(1) // adding the same key a second time would fail + // parameter today: 1_925_331 + // typical estimation: 18_000_000 (18ns) + // (we know this is undercharged but it's not a concern as described in #6716) + // setting limit to 1ns to keep it lower than the parameter + .min_gas(GAS_1_NANOSECOND) + .apply_cost(&mut ctx.testbed()) + / ActionSize::Max.key_methods_list() +} + +pub(crate) fn delete_key_send_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx).add_action(delete_key_action()).verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn delete_key_send_not_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new(ctx).add_action(delete_key_action()).verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn delete_key_exec(ctx: &mut EstimatorContext) -> GasCost { + // Cannot delete a key without creating it first. Therefore, compute cost of + // (create) and of (create + delete) and return the difference. + let base_builder = ActionEstimation::new_sir(ctx) + .inner_iters(1) + .add_action(add_fn_access_key_action(ActionSize::Max)); + let base = base_builder.apply_cost(&mut ctx.testbed()); + let total = base_builder + .add_action(delete_key_action()) + .inner_iters(100) + .apply_cost(&mut ctx.testbed()); + + total - base +} + +pub(crate) fn new_action_receipt_send_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .inner_iters(1) // inner iterations don't work with empty action lists + .subtract_base(false) + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn new_action_receipt_send_not_sir(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new(ctx) + .inner_iters(1) // inner iterations don't work with empty action lists + .subtract_base(false) + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn new_action_receipt_exec(ctx: &mut EstimatorContext) -> GasCost { + ActionEstimation::new_sir(ctx) + .inner_iters(1) // inner iterations don't work with empty action lists + .subtract_base(false) + .apply_cost(&mut ctx.testbed()) +} + +pub(crate) fn delegate_send_sir(ctx: &mut EstimatorContext) -> GasCost { + let receiver_id: AccountId = "a".repeat(AccountId::MAX_LEN).parse().unwrap(); + let sender_id: AccountId = genesis_populate::get_account_id(0); + + ActionEstimation::new_sir(ctx) + .add_action(empty_delegate_action(0, receiver_id, sender_id)) + // only single delegate action is allowed + .inner_iters(1) + // but then the variance is too high to report the average + .report_percentile(0.75) + // If the percentile of a distribution is reported, the typical + // number of outer iterations (3-5) is just too small. Let's do at + // least 60 iterations, this way the 15th largest ~ 75th percentile. + // (This estimations is super fast, 60 iterations are quick) + .min_outer_iters(60) + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn delegate_send_not_sir(ctx: &mut EstimatorContext) -> GasCost { + let receiver_id: AccountId = "a".repeat(AccountId::MAX_LEN).parse().unwrap(); + let sender_id: AccountId = genesis_populate::get_account_id(0); + ActionEstimation::new(ctx) + .add_action(empty_delegate_action(0, receiver_id, sender_id)) + // only single delegate action is allowed + .inner_iters(1) + // but then the variance is too high to report the average + .report_percentile(0.75) + // If the percentile of a distribution is reported, the typical + // number of outer iterations (3-5) is just too small. Let's do at + // least 60 iterations, this way the 15th largest ~ 75th percentile. + // (This estimations is super fast, 60 iterations are quick) + .min_outer_iters(60) + .verify_cost(&mut ctx.testbed()) +} + +pub(crate) fn delegate_exec(ctx: &mut EstimatorContext) -> GasCost { + let receiver_id: AccountId = "a".repeat(AccountId::MAX_LEN).parse().unwrap(); + let sender_id: AccountId = genesis_populate::get_account_id(0); + let mut builder = ActionEstimation::new_sir(ctx) + // nonce check would fail with cloned actions, therefore inner iterations don't work + .inner_iters(1) + // tx receiver must be the same as the meta tx signer, we can't just + // pick one at random or it will fail validation + .receiver(AccountRequirement::ConstantAccount0); + // manually make inner iterations by creating 100 actions with increasing nonces + let manual_inner_iters = 100; + for i in 0..manual_inner_iters { + builder = + builder.add_action(empty_delegate_action(i + 1, receiver_id.clone(), sender_id.clone())) + } + builder.apply_cost(&mut ctx.testbed()) / manual_inner_iters +} + +fn create_account_action() -> Action { + Action::CreateAccount(unc_primitives::transaction::CreateAccountAction {}) +} + +fn create_transfer_action() -> Action { + Action::Transfer(unc_primitives::transaction::TransferAction { deposit: 10u128.pow(24) }) +} + +fn stake_action() -> Action { + Action::Stake(Box::new(unc_primitives::transaction::StakeAction { + stake: 5u128.pow(28), // some arbitrary positive number + public_key: PublicKey::from_seed(KeyType::ED25519, "seed"), + })) +} + +fn delete_account_action() -> Action { + Action::DeleteAccount(unc_primitives::transaction::DeleteAccountAction { + beneficiary_id: "bob.near".parse().unwrap(), + }) +} + +fn deploy_action(size: ActionSize) -> Action { + Action::DeployContract(unc_primitives::transaction::DeployContractAction { + code: unc_test_contracts::sized_contract(size.deploy_contract() as usize), + }) +} + +fn add_full_access_key_action() -> Action { + Action::AddKey(Box::new(unc_primitives::transaction::AddKeyAction { + public_key: PublicKey::from_seed(KeyType::ED25519, "full-access-key-seed"), + access_key: AccessKey { nonce: 0, permission: AccessKeyPermission::FullAccess }, + })) +} + +fn add_fn_access_key_action(size: ActionSize) -> Action { + // 3 bytes for "foo" and one for an implicit separator + let method_names = vec!["foo".to_owned(); size.key_methods_list() as usize / 4]; + // This is charged flat, therefore it should always be max len. + let receiver_id = "a".repeat(AccountId::MAX_LEN).parse().unwrap(); + Action::AddKey(Box::new(unc_primitives::transaction::AddKeyAction { + public_key: PublicKey::from_seed(KeyType::ED25519, "seed"), + access_key: AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(1), + receiver_id, + method_names, + }), + }, + })) +} + +fn delete_key_action() -> Action { + Action::DeleteKey(Box::new(unc_primitives::transaction::DeleteKeyAction { + public_key: PublicKey::from_seed(KeyType::ED25519, "seed"), + })) +} + +fn transfer_action() -> Action { + Action::Transfer(unc_primitives::transaction::TransferAction { deposit: 77 }) +} + +fn function_call_action(size: ActionSize) -> Action { + let total_size = size.function_call_payload(); + let method_len = 4.min(total_size) as usize; + let method_name: String = "noop".chars().take(method_len).collect(); + let arg_len = total_size as usize - method_len; + Action::FunctionCall(Box::new(unc_primitives::transaction::FunctionCallAction { + method_name, + args: vec![1u8; arg_len], + gas: 3 * 10u64.pow(12), // 3 Tgas, to allow 100 copies in the same receipt + deposit: 10u128.pow(24), + })) +} + +pub(crate) fn empty_delegate_action( + nonce: u64, + receiver_id: AccountId, + sender_id: AccountId, +) -> Action { + use unc_primitives::action::delegate::DelegateAction; + use unc_primitives::signable_message::{SignableMessage, SignableMessageType}; + use unc_primitives::test_utils::create_user_test_signer; + + let signer = create_user_test_signer(&sender_id); + let delegate_action = DelegateAction { + sender_id, + receiver_id, + actions: vec![], + nonce, + max_block_height: 1000, + public_key: signer.public_key.clone(), + }; + let signature = + SignableMessage::new(&delegate_action, SignableMessageType::DelegateAction).sign(&signer); + Action::Delegate(Box::new(unc_primitives::action::delegate::SignedDelegateAction { + delegate_action, + signature, + })) +} + +/// Helper enum to select how large an action should be generated. +#[derive(Clone, Copy)] +enum ActionSize { + Min, + Max, +} + +impl ActionSize { + fn function_call_payload(self) -> u64 { + match self { + // calling "noop" requires 4 bytes + ActionSize::Min => 4, + // max_arguments_length: 4_194_304 + // max_transaction_size: 4_194_304 + ActionSize::Max => (4_194_304 / 100) - 35, + } + } + + fn key_methods_list(self) -> u64 { + match self { + ActionSize::Min => 0, + // max_number_bytes_method_names: 2000 + ActionSize::Max => 2000, + } + } + + fn deploy_contract(self) -> u64 { + match self { + // small number that still allows to generate a valid contract + ActionSize::Min => 120, + // max_number_bytes_method_names: 2000 + // This size exactly touches tx limit with 1 deploy action. If this suddenly + // fails with `InvalidTxError(TransactionSizeExceeded`, it could be a + // protocol change due to the TX limit computation changing. + // The test `test_deploy_contract_tx_max_size` checks this. + ActionSize::Max => 4 * 1024 * 1024 - 182, + } + } +} + +#[cfg(test)] +mod tests { + use super::{deploy_action, ActionSize}; + use genesis_populate::get_account_id; + + #[test] + fn test_deploy_contract_tx_max_size() { + // The size of a transaction constructed from this must be exactly at the limit. + let deploy_action = deploy_action(ActionSize::Max); + let limit = 4_194_304; + + // We also need some account IDs constructed the same way as in the estimator. + // Let's try multiple index sizes to ensure this does not affect the length. + let sender_0 = get_account_id(0); + let receiver_0 = get_account_id(1); + let sender_1 = get_account_id(1000); + let receiver_1 = get_account_id(20001); + let test_accounts = + vec![sender_0.clone(), sender_1.clone(), receiver_0.clone(), receiver_1.clone()]; + let mut tb = crate::TransactionBuilder::new(test_accounts); + + let tx_0 = tb.transaction_from_actions(sender_0, receiver_0, vec![deploy_action.clone()]); + assert_eq!(tx_0.get_size(), limit, "TX size changed"); + + let tx_1 = tb.transaction_from_actions(sender_1, receiver_1, vec![deploy_action]); + assert_eq!(tx_1.get_size(), limit, "TX size changed"); + } +} diff --git a/runtime/runtime-params-estimator/src/config.rs b/runtime/runtime-params-estimator/src/config.rs new file mode 100644 index 000000000..d66c224d7 --- /dev/null +++ b/runtime/runtime-params-estimator/src/config.rs @@ -0,0 +1,47 @@ +use crate::rocksdb::RocksDBTestConfig; +use crate::Cost; +use unc_parameters::vm::VMKind; +use std::path::PathBuf; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum GasMetric { + // If we measure gas in number of executed instructions, must run under simulator. + ICount, + // If we measure gas in elapsed time. + Time, +} + +/// Configuration which we use to run measurements. +#[derive(Debug, Clone)] +pub struct Config { + /// How many warm up iterations per block should we run. + pub warmup_iters_per_block: usize, + /// How many iterations per block are we going to try. + pub iter_per_block: usize, + /// Total active accounts. + pub active_accounts: usize, + /// How many blocks behind the final head is assumed to be compared to the tip. + pub finality_lag: usize, + /// How many key-value pairs change per flat state delta. + pub fs_keys_per_delta: usize, + /// Where state dump is located in case we need to create a testbed. + pub state_dump_path: PathBuf, + /// Metric used for counting. + pub metric: GasMetric, + /// VMKind used + pub vm_kind: VMKind, + /// When non-none, only the specified costs will be measured. + pub costs_to_measure: Option>, + /// Configuration specific to raw RocksDB tests. Does NOT affect normal tests that use RocksDB through the framework interface. + pub rocksdb_test_config: RocksDBTestConfig, + /// Print extra details on estimations. + pub debug: bool, + /// Print JSON output for estimation results. + pub json_output: bool, + /// Clear all OS caches between measured blocks. + pub drop_os_cache: bool, + /// Use in-memory test DB, useful to avoid variance caused by DB. + pub in_memory_db: bool, + /// If false, only runs a minimal check that's faster than trying to get accurate results. + pub accurate: bool, +} diff --git a/runtime/runtime-params-estimator/src/cost.rs b/runtime/runtime-params-estimator/src/cost.rs new file mode 100644 index 000000000..7aab455f5 --- /dev/null +++ b/runtime/runtime-params-estimator/src/cost.rs @@ -0,0 +1,742 @@ +use std::fmt; +use std::str::FromStr; + +/// Kinds of things we measure in parameter estimator and charge for in runtime. +/// +/// TODO: Deduplicate this enum with `ExtCosts` and `ActionCosts`. +#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, clap::ValueEnum)] +#[repr(u8)] +pub enum Cost { + // Every set of actions in a transaction needs to be transformed into a + // action receipt, regardless of whether it is executed locally within the + // same block or delayed. The gas cost for this is paid when the receipt is + // created. The base amount is `action_receipt_creation_config.execution` + + // (either `action_receipt_creation.send_sir` or + // `action_receipt_creation.send_not_sir` depending on whether + // `sender == receiver`). + // On top of that, each type of action has its own costs defined, which is + // added for each action included in the receipt. + // + /// Estimates `ActionCosts::new_receipt`, which is the base cost for + /// creating a new action receipt, excluding actual action costs. + /// + /// Estimation: Measure the creation and execution of an empty action + /// receipt, where sender and receiver are two different accounts. This + /// involves applying two blocks and both contribute to the measured cost. + /// The cost is then divided by 2 to split between `send_not_sir` and + /// `execution` cost. `send_sir` is set to the same value as `send_not_sir` + /// for now. + ActionReceiptCreation, + /// Estimates `ActionCosts::new_receipt`.`send_sir`. Although, note that it + /// is currently configured to be the same as `send_not_sir`. But we already + /// use this value as partial estimation of other action costs. + /// + /// Estimation: Measure the creation and execution of an empty action + /// receipt, where sender and receiver are the same account. + ActionSirReceiptCreation, + ActionReceiptCreationSendNotSir, + ActionReceiptCreationSendSir, + ActionReceiptCreationExec, + /// Estimates `data_receipt_creation_config.base_cost`, which is charged for + /// every data dependency of created receipts. This occurs either through + /// calls to `promise_batch_then` or `value_return`. Dispatch and execution + /// costs are both burnt upfront. + /// + /// Estimation: Measure two functions that each create 1000 promises but + /// only one of them also creates a callback that depends on the promise + /// results. The difference in execution cost is divided by 1000. + DataReceiptCreationBase, + DataReceiptCreationBaseSendNotSir, + DataReceiptCreationBaseSendSir, + DataReceiptCreationBaseExec, + /// Estimates `data_receipt_creation_config.cost_per_byte`, which is charged + /// for every byte in data dependency of created receipts. This occurs + /// either through calls to `promise_batch_then` or `value_return`. Dispatch + /// and execution costs are both burnt upfront. + /// + /// Estimation: Measure two functions that each create 1000 promises with a + /// callback that depends on the promise results. One of the functions + /// creates small data receipts, the other large ones. The difference in + /// execution cost is divided by the total byte difference. + DataReceiptCreationPerByte, + DataReceiptCreationPerByteSendNotSir, + DataReceiptCreationPerByteSendSir, + DataReceiptCreationPerByteExec, + /// Estimates `action_creation_config.create_account_cost` which is charged + /// for `CreateAccount` actions, the same value on sending and executing. + /// + /// Estimation: Measure a transaction that creates an account and transfers + /// an initial balance to it. Subtract the base cost of creating a receipt. + // TODO(jakmeier): consider also subtracting transfer fee + ActionCreateAccount, + ActionCreateAccountSendNotSir, + ActionCreateAccountSendSir, + ActionCreateAccountExec, + // Deploying a new contract for an account on the blockchain stores the WASM + // code in the trie. Additionally, it also triggers a compilation of the + // code to check that it is valid WASM. The compiled code is then stored in + // the database, in a separate column family for cached contract code. The + // costs charged for such a contract deployment action is + // `ActionDeployContractBase` + `N` * `ActionDeployContractPerByte`, where + // `N` is the number of bytes in the WASM code. + /// Estimates `action_creation_config.deploy_contract_cost`, which is + /// charged once per contract deployment + /// + /// Estimation: Measure deployment cost of a "smallest" contract. + ActionDeployContractBase, + ActionDeployContractBaseSendNotSir, + ActionDeployContractBaseSendSir, + ActionDeployContractBaseExec, + /// Estimates `action_creation_config.deploy_contract_cost_per_byte`, which + /// is charged for every byte in the WASM code when deploying the contract + /// + /// Estimation: Measure several cost for deploying several core contract in + /// a transaction. Subtract base costs and apply least-squares on the + /// results to find the per-byte costs. + ActionDeployContractPerByte, + ActionDeployContractPerByteSendNotSir, + ActionDeployContractPerByteSendSir, + ActionDeployContractPerByteExec, + /// Estimates `action_creation_config.function_call_cost`, which is the base + /// cost for adding a `FunctionCallAction` to a receipt. It aims to account + /// for all costs of calling a function that are already known on the caller + /// side. + /// + /// Estimation: Measure the cost to execute a NOP function on a tiny + /// contract. To filter out block and general receipt processing overhead, + /// the difference between calling it N +1 times and calling it once in a + /// transaction is divided by N. Executable loading cost is also subtracted + /// from the final result because this is charged separately. + ActionFunctionCallBase, + ActionFunctionCallBaseSendNotSir, + ActionFunctionCallBaseSendSir, + ActionFunctionCallBaseExec, + /// Estimates `action_creation_config.function_call_cost_per_byte`, which is + /// the incremental cost for each byte of the method name and method + /// arguments cost for adding a `FunctionCallAction` to a receipt. + /// + /// Estimation: Measure the cost for a transaction with an empty function + /// call with a large argument value. Subtract the cost of an empty function + /// call with no argument. Divide the difference by the length of the + /// argument. + ActionFunctionCallPerByte, + ActionFunctionCallPerByteSendNotSir, + ActionFunctionCallPerByteSendSir, + ActionFunctionCallPerByteExec, + /// Estimates `action_creation_config.transfer_cost` which is charged for + /// every `Action::Transfer`, the same value for sending and executing. + /// + /// Estimation: Measure a transaction with only a transfer and subtract the + /// base cost of creating a receipt. + ActionTransfer, + ActionTransferSendNotSir, + ActionTransferSendSir, + ActionTransferExec, + /// Estimates `action_creation_config.stake_cost` which is charged for every + /// `Action::Stake`, a slightly higher value for sending than executing. + /// + /// Estimation: Measure a transaction with only a staking action and + /// subtract the base cost of creating a sir-receipt. + /// + /// Note: The exec cost is probably a copy-paste mistake. (#8185) + ActionStake, + ActionStakeSendNotSir, + ActionStakeSendSir, + ActionStakeExec, + /// Estimates `action_creation_config.add_key_cost.full_access_cost` which + /// is charged for every `Action::AddKey` where the key is a full access + /// key. The same value is charged for sending and executing. + /// + /// Estimation: Measure a transaction that adds a full access key and + /// subtract the base cost of creating a sir-receipt. + ActionAddFullAccessKey, + ActionAddFullAccessKeySendNotSir, + ActionAddFullAccessKeySendSir, + ActionAddFullAccessKeyExec, + /// Estimates `action_creation_config.add_key_cost.function_call_cost` which + /// is charged once for every `Action::AddKey` where the key is a function + /// call key. The same value is charged for sending and executing. + /// + /// Estimation: Measure a transaction that adds a function call key and + /// subtract the base cost of creating a sir-receipt. + ActionAddFunctionAccessKeyBase, + ActionAddFunctionAccessKeyBaseSendNotSir, + ActionAddFunctionAccessKeyBaseSendSir, + ActionAddFunctionAccessKeyBaseExec, + /// Estimates + /// `action_creation_config.add_key_cost.function_call_cost_per_byte` which + /// is charged once for every byte in null-terminated method names listed in + /// an `Action::AddKey` where the key is a function call key. The same value + /// is charged for sending and executing. + /// + /// Estimation: Measure a transaction that adds a function call key with + /// many methods. Subtract the cost of adding a function call key with a + /// single method and of creating a sir-receipt. The result is divided by + /// total bytes in the method names. + ActionAddFunctionAccessKeyPerByte, + ActionAddFunctionAccessKeyPerByteSendNotSir, + ActionAddFunctionAccessKeyPerByteSendSir, + ActionAddFunctionAccessKeyPerByteExec, + /// Estimates `action_creation_config.delete_key_cost` which is charged for + /// `DeleteKey` actions, the same value on sending and executing. It does + /// not matter whether it is a function call or full access key. + /// + /// Estimation: Measure a transaction that deletes a full access key and + /// transfers an initial balance to it. Subtract the base cost of creating a + /// receipt. + // TODO(jakmeier): check cost for function call keys with many methods + ActionDeleteKey, + ActionDeleteKeySendNotSir, + ActionDeleteKeySendSir, + ActionDeleteKeyExec, + /// Estimates `action_creation_config.delete_account_cost` which is charged + /// for `DeleteAccount` actions, the same value on sending and executing. + /// + /// Estimation: Measure a transaction that deletes an existing account. + /// Subtract the base cost of creating a sir-receipt. + /// TODO(jakmeier): Consider different account states. + ActionDeleteAccount, + ActionDeleteAccountSendNotSir, + ActionDeleteAccountSendSir, + ActionDeleteAccountExec, + /// Estimates `action_creation_config.delegate_cost` which is charged + /// for `DelegateAction` actions. + ActionDelegate, + ActionDelegateSendNotSir, + ActionDelegateSendSir, + ActionDelegateExec, + + /// rsa2048 keys are used for signing transactions. The cost of creating + RegisterRsa2048Keys, + CreateRsa2048Challenge, + /// Estimates `wasm_config.ext_costs.base` which is intended to be charged + /// once on every host function call. However, this is currently + /// inconsistent. First, we do not charge on Math API methods (`sha256`, + /// `keccak256`, `keccak512`, `ripemd160`, `alt_bn128_g1_multiexp`, + /// `alt_bn128_g1_sum`, `alt_bn128_pairing_check`). Furthermore, + /// `promise_then` and `promise_create` are convenience wrapper around two + /// other host functions, which means they end up charing this fee twice. + /// + /// Estimation: Measure a transaction with a smart contract function call + /// that, from within the WASM runtime, invokes the host function + /// `block_index()` many times. Subtract the cost of executing a smart + /// contract function that does nothing. Divide the difference by the number + /// of host function calls. + HostFunctionCall, + /// Estimates `wasm_config.regular_op_cost` which is charged for every + /// executed WASM operation in function calls, as counted dynamically during + /// execution. + /// + /// Estimation: Run a contract that reads and writes lots of memory in an + /// attempt to cause slow loads and stores. The total time spent in the + /// runtime is divided by the number of executed instructions. + WasmInstruction, + + // # Reading and writing memory + // The hosting runtime sometimes copies data between in and out of WASM + // buffers defined by smart contract code. The smart contract code defines + // their side of the buffers as WASM address + length. Copies going from + // WASM to host memory are called *reads*, whereas *writes* are going from + // host to WASM memory. + // + // The following is a best-effort list of all host functions that may + // produce memory reads or writes + // + // Read: + // - Creating promises for actions: The data describing the actions. + // - Writing to a register: The data to be written to the register. + // - Using various math API functions: Reading the operand values. + // - On log or abort/panic: Reading string data from memory. + // + // Write + // - Reading from a register: The data from the register is copied into + // WASM memory. + // - Host function calls such as `account_balance()` and + // `validator_stake()` that return a value by writing it to a pointer. + // + /// Estimates `ext_costs.read_memory_base` which is charged once every time + /// data is copied from WASM memory to the hosting runtime as a result of + /// executing a contract. + /// + /// Estimation: Execute a transaction with a single function call that calls + /// `value_return()` 10'000 times with a 10 byte value. Subtract the cost of + /// an empty function call and divide the rest by 10'000. + ReadMemoryBase, + /// Estimates `ext_costs.read_memory_byte` which is charged as an + /// incremental cost per byte each time WASM memory is copied to the host. + /// + /// Estimation: Execute a transaction with a single function call that calls + /// `value_return()` 10'000 times with a 1 MiB sized value. Subtract the + /// cost of an empty function call and divide the rest by 10'000 * 1Mi. + ReadMemoryByte, + /// Estimates `ext_costs.write_memory_base` which is charged once every time + /// data is copied from the host to WASM memory as a result of executing a + /// contract. + /// + /// Estimation: Execute a transaction with a single function call that + /// writes 10 bytes to a register and calls `read_register` 10'000 times to + /// copy it back to WASM memory. Subtract the cost of an empty function call + /// and divide the rest by 10'000. + WriteMemoryBase, + /// Estimates `ext_costs.write_memory_byte` which is charged as an + /// incremental cost per byte each time data is copied from the host to WASM + /// memory. + /// + /// Estimation: Execute a transaction with a single function call that + /// writes 1MiB to a register and calls `read_register` 10'000 times to + /// copy it back to WASM memory. Subtract the cost of an empty function call + /// and divide the rest by 10'000 * 1Mi. + WriteMemoryByte, + + // # Register API + // Instead of relying on WASM memory, some host functions operate on + // registers. These registers are allocated outside the WASM memory but need + // to be copied in and out of WASM memory if a contract want to access them. + // This copying is done through `read_register` and `write_register`. + /// Estimates `read_register_base` which is charged once for every reading access to a register. + /// + /// Estimation: Execute a transaction with a single function call that + /// writes 10 bytes to a register once and then calls `value_return` with + /// that register 10'000 times. Subtract the cost of an empty function call + /// and divide the rest by 10'000. + ReadRegisterBase, + /// Estimates `read_register_byte` which is charged per byte for every reading access to a register. + /// + /// Estimation: Execute a transaction with a single function call that + /// writes 1 MiB to a register once and then calls `value_return` with + /// that register 10'000 times. Subtract the cost of an empty function call + /// and divide the rest by 10'000 * 1Mi. + ReadRegisterByte, + /// Estimates `write_register_base` which is charged once for every writing access to a register. + /// + /// Estimation: Execute a transaction with a single function call that + /// writes 10B to a register 10'000 times. Subtract the cost of an empty + /// function call and divide the rest by 10'000. + WriteRegisterBase, + /// Estimates `write_register_byte` which is charged per byte for every writing access to a register. + /// + /// Estimation: Execute a transaction with a single function call that + /// writes 1 MiB to a register 10'000 times. Subtract the cost of an empty + /// function call and divide the rest by 10'000 * 1Mi. + WriteRegisterByte, + + /// Estimates `utf8_decoding_base` which is charged once for each time + /// something is logged in UTF8 or when panicking. + /// + /// Estimation: Execute a transaction with a single function that logs + /// 10'000 times a small string. Divide the cost by 10'000. + Utf8DecodingBase, + /// Estimates `utf8_decoding_byte` which is charged for each byte in the + /// output of logging in UTF8 or panicking. + /// + /// Estimation: Execute a transaction with a single function that logs + /// a 10kiB string many times. Divide the cost by total bytes + /// logged. One more details, to cover both null-terminated strings and + /// fixed-length strings, both versions are measured and the maximum is + /// taken. + Utf8DecodingByte, + /// Estimates `utf16_decoding_base` which is charged once for each time + /// something is logged in UTF16. + /// + /// Estimation: Execute a transaction with a single function that logs + /// 10'000 times a small string. Divide the cost by 10'000. + Utf16DecodingBase, + /// Estimates `utf16_decoding_byte` which is charged for each byte in the + /// output of logging in UTF16. + /// + /// Estimation: Execute a transaction with a single function that logs a + /// 10kiB string many times. Divide the cost by total bytes logged. One more + /// details, to cover both null-terminated strings and fixed-length strings, + /// both versions are measured and the maximum is taken. + Utf16DecodingByte, + /// Estimates `log_base` which is charged once every time log output is + /// produced, either through logging functions (UTF8 or UTF16) or when + /// panicking. + /// + /// Estimation: Execute a transaction with a single function that logs + /// 10'000 times a small string (using UTF16 to be pessimistic). Divide the + /// cost by 10'000. + /// + /// Note: This currently uses the identical estimation as + /// `Utf16DecodingBase` + LogBase, + /// Estimates `log_byte` which is charged for every byte of log output + /// produced, either through logging functions (UTF8 or UTF16) or when + /// panicking. + /// + /// Estimation: Execute a transaction with a single function that logs a + /// 10kiB string N times (using UTF16 to be pessimistic). Divide the cost by + /// total bytes of output produced, which is 3/2 * N * 10 * 1024. + LogByte, + + // Cryptographic host functions: + // The runtime provides host functions for sha256, keccak256, keccak512, + // ripemd160 hashes. Plus, there is a ECDSA signature verification host + // function. + // All these host functions are estimated by executing a transaction with a + // single function call in them, that just invokes the given host function. + // To measure the cost of additional bytes or blocks, a function call with a + // large argument is measured and the total cost divided by total input + // bytes (or blocks in the case of the RIPEMD hash). + /// Estimates `sha256_base`, the cost charged once per call to the + /// sha256-hash host function. + Sha256Base, + /// Estimates `sha256_byte`, the cost charged per input byte in calls to the + /// sha256-hash host function. + Sha256Byte, + /// Estimates `keccak256_base`, the cost charged once per call to the + /// keccak256-hash host function. + Keccak256Base, + /// Estimates `keccak256_byte`, the cost charged per input byte in calls to the + /// keccak256-hash host function. + Keccak256Byte, + /// Estimates `keccak512_base`, the cost charged once per call to the + /// keccak512-hash host function. + Keccak512Base, + /// Estimates `keccak512_byte`, the cost charged per input byte in calls to the + /// keccak512-hash host function. + Keccak512Byte, + /// Estimates `ripemd160_base`, the cost charged once per call to the + /// ripemd160-hash host function. + Ripemd160Base, + /// Estimates `ripemd160_block`, the cost charged per input block in calls + /// to the ripemd160-hash host function. Blocks are 64 bytes, except for the + /// last which may be smaller. The exact number of blocks charged as a + /// function of input bytes `n` is `blocks(n) = (n + 9).div_ceil(64)`. + Ripemd160Block, + /// Estimates `ecrecover_base`, which covers the full cost of the host + /// function `ecrecover` to verify an ECDSA signature and extract the + /// signer. + EcrecoverBase, + /// Estimates `ed25519_verify_base`, which covers the base cost of the host + /// function `ed25519_verify` to verify an ED25519 signature. + /// + /// Estimation: Use a fixed signature embedded in the test contract and + /// verify it `N` times in a loop and divide by `N`. The overhead of other + /// costs is negligible compared to the two elliptic curve scalar + /// multiplications performed for signature validation. + /// + /// Note that the multiplication algorithm used is not constant time, i.e. + /// it's timing varies depending on the input. Testing with a range of + /// random signatures, the difference is +/-10%. Testing with extreme + /// inputs, it can be more than 20% faster than a random case. But it seems + /// on the upper end, there is a limit to how many additions and doublings + /// need to be performed. + /// In conclusion, testing on a single input is okay, if we account for the + /// 10-20% variation. + Ed25519VerifyBase, + /// Estimates `ed25519_verify_byte`, the cost charged per input byte in calls to the + /// ed25519_verify host function. + /// + /// Estimation: Verify a signature for a large message many times, subtract + /// the cost estimated for the base and divide the remainder by the total + /// bytes the message. + /// + /// The cost per byte for pure verification is just the cost for hashing. + /// This is comparable to the cost for reading values from memory or + /// registers, which is currently not subtracted in the estimation. + /// Subtracting it would lead to high variance. Instead, one has to take it + /// into account that memory overhead is included when mapping the + /// estimation to a parameter. + /// In the end, the cost should be low enough, compared to the base cost, + /// that it does not matter all that much if we overestimate it a bit. + Ed25519VerifyByte, + // `storage_write` records a single key-value pair, initially in the + // prospective changes in-memory hash map, and then once a full block has + // been processed, in the on-disk trie. If there was already a value + // stored, it is overwritten and the old value is returned to the caller. + /// Estimates `ExtCost::storage_write_base` which is charged once per call + /// to `storage_write`. + /// + /// Estimation: Contract call that writes N small values and divide the + /// cost by N. + StorageWriteBase, + /// Estimates `ExtCost::storage_write_key_byte` which is charged for each + /// byte in keys of `storage_write` calls. + /// + /// Estimation: Contract call that writes N small values with a big key + /// (10kiB) and divide the cost by total number of key bytes. + StorageWriteKeyByte, + /// Estimates `ExtCost::storage_write_value_byte` which is charged for each + /// byte in values of `storage_write` calls. + /// + /// Estimation: Contract call that writes N big values (10kiB) and divide + /// the cost by total number of value bytes. + StorageWriteValueByte, + /// Estimates `ExtCosts::storage_write_evicted_byte` which is charged for + /// each byte in a value that is overwritten in `storage_write` calls. + /// + /// Estimation: Contract call that writes N values to keys that already + /// contain big values (10kiB). + StorageWriteEvictedByte, + + // `read_storage` reads a single value from either prospective changes if + // present or from the on-disk trie otherwise. + /// Estimates `ExtCost::storage_read_base` which is charged once per call + /// to `storage_read`. + /// + /// Estimation: Contract call that reads N small values and divide the cost + /// by N. + StorageReadBase, + /// Estimates `ExtCost::storage_read_key_byte` which is charged for each + /// byte in keys of `storage_read` calls. + /// + /// Estimation: Contract call that reads N small values with a big key + /// (10kiB) and divide the cost by total number of key bytes. + StorageReadKeyByte, + /// Estimates `ExtCost::storage_read_value_byte` which is charged for each + /// byte in values of `storage_read` calls. + /// + /// Estimation: Contract call that reads N big values (10kiB) and divide + /// the cost by total number of value bytes. + StorageReadValueByte, + + // `storage_remove` adds a deletion transaction to the prospective changes, + // which is applied at the end of the block. + /// Estimates `ExtCost::storage_remove_base` which is charged once per call + /// to `storage_remove`. + /// + /// Estimation: Contract call that removes N small values and divide the + /// cost by N. + StorageRemoveBase, + /// Estimates `ExtCost::storage_remove_key_byte` which is charged for each + /// byte in keys of `storage_remove` calls. + /// + /// Estimation: Contract call that removes N small values with a big key + /// (10kiB) and divide the cost by total number of key bytes. + StorageRemoveKeyByte, + /// Estimates `ExtCost::storage_remove_value_byte` which is charged for + /// each byte in values of `storage_remove` calls. + /// + /// Estimation: Contract call that removes N big values (10kiB) and divide + /// the cost by total number of value bytes. + StorageRemoveRetValueByte, + + // `storage_has_key` checks if the key currently has an associated value. + // First, checked in the prospective changes, if nothing found, also in + // on-disk trie. + /// Estimates `ExtCost::storage_has_key_base` which is charged once per + /// call to `storage_has_key`. + /// + /// Estimation: Contract call that removes N small values with small keys + /// and divide the cost by N. + StorageHasKeyBase, + /// Estimates `ExtCost::storage_has_key_byte` which is charged for each + /// byte in calls to `storage_has_key`. + /// + /// Estimation: Contract call that removes N small values with big keys + /// (10kiB) and divide the cost by total key bytes. + StorageHasKeyByte, + + /// DEPRECATED: Was charged in `storage_iter_prefix` + StorageIterCreatePrefixBase, + /// DEPRECATED: Was charged in `storage_iter_prefix` + StorageIterCreatePrefixByte, + /// DEPRECATED: Was charged in `storage_iter_range` + StorageIterCreateRangeBase, + /// DEPRECATED: Was charged in `storage_iter_range` + StorageIterCreateFromByte, + /// DEPRECATED: Was charged in `storage_iter_range` + StorageIterCreateToByte, + /// DEPRECATED: Was charged in `storage_iter_next` + StorageIterNextBase, + /// DEPRECATED: Was charged in `storage_iter_next` + StorageIterNextKeyByte, + /// DEPRECATED: Was charged in `storage_iter_next` + StorageIterNextValueByte, + + /// Estimates `touching_trie_node` which is charged when smart contracts + /// access storage either through `storage_has_key`, `storage_read`, + /// `storage_write` or `storage_remove`. The fee is paid once for each + /// unique trie node accessed. + /// + /// Estimation: Prepare an account that has many keys stored that are + /// prefixes from each other. Then measure write cost for the shortest and + /// the longest key. The gas estimation difference is divided by the + /// difference of actually touched nodes. + TouchingTrieNode, + /// It is similar to `TouchingTrieNode`, but it is charged instead of this + /// cost when we can guarantee that trie node is cached in memory, which + /// allows us to charge less costs. + /// + /// Estimation: Since this is a small cost, it cannot be measured accurately + /// through the normal process of measuring several transactions and + /// calculating the difference. Instead, the estimation directly + /// instantiates the caching storage and reads nodes of the largest possible + /// size from it. This is done in a pessimistic setup and the 90th + /// percentile of measured samples is taken as the final cost. The details + /// for this are a bit involved but roughly speaking, it just forces values + /// out of CPU caches so that they are always read from memory. + ReadCachedTrieNode, + /// Estimates `promise_and_base` which is charged for every call to + /// `promise_and`. This should cover the base cost for creating receipt + /// dependencies. + /// + /// Estimation: Currently not estimated + PromiseAndBase, + /// Estimates `promise_and_per_promise` which is charged for every promise in + /// calls to `promise_and`. This should cover the additional cost for each + /// extra receipt in the dependency. + /// + /// Estimation: Currently not estimated + PromiseAndPerPromise, + /// Estimates `promise_return` which is charged when calling + /// `promise_return`. This should cover the cost of the dependency between a + /// promise and the current function call return value. + /// + /// Estimation: Currently not estimated + PromiseReturn, + /// Estimates `validator_stake_base` which is charged for each call to + /// `validator_stake`, covering the cost for looking up if an account is a + /// validator and if so, how much it has staked. This information is + /// available from the local EpochManager. + /// + /// Estimation: Currently not estimated + ValidatorStakeBase, + /// Estimates `validator_total_stake_base` which is charged for each call to + /// `validator_total_stake`, covering the cost for looking up the total + /// staked tokens for the current epoch. This information is + /// available from the local EpochManager. + /// + /// Estimation: Currently not estimated + ValidatorTotalStakeBase, + + AltBn128G1MultiexpBase, + AltBn128G1MultiexpElement, + AltBn128G1MultiexpSublinear, + AltBn128PairingCheckBase, + AltBn128PairingCheckElement, + AltBn128G1SumBase, + AltBn128G1SumElement, + + // Costs used only in estimator + // + /// Costs associated with applying an empty block. This overhead is not + /// charged to any specific account and thus does not directly affect gas + /// fees. However, for estimation this is a crucial value to know. Many + /// estimation methods require to know this value in order to subtract it + /// from the measurement. + ApplyBlock, + // Compilation happens during deployment and the pre-compiled code is stored + // in the DB. Thus, compilation cost is part of deployment cost and not a + // cost we charge in isolation. But how expensive compilation is, is an + // important value to track nevertheless. + // We have two alternatives to estimate compilation cost. + // + /// `ContractCompileBase` and `ContractCompileBytes` are estimated together, + /// by compiling several core contracts and computing least-squares on the + /// code sizes and execution times. + ContractCompileBase, + ContractCompileBytes, + /// Contract compile costs V2 is an alternative estimation for the + /// compilation cost. Instead of least-squares method, it finds a linear + /// function that is higher than any of the measured contracts. + /// + /// Estimation: Compiles a "smallest possible" contract and several core + /// contracts. The smallest contract is taken as the first anchor the linear + /// function. The second defining point is which ever of the core contracts + /// produces the steepest line. + ContractCompileBaseV2, + ContractCompileBytesV2, + /// The cost of contract deployment per byte, without the compilation cost. + /// + /// Estimation: Measure the deployment costs of two data-only contracts, + /// where one data sections is empty and the other is max size as allowed by + /// contract size limits. The cost difference is pure overhead of moving + /// around bytes that are not code. Divide this cost by the difference of + /// bytes. + DeployBytes, + /// Estimates `wasm_contract_loading_base` which is charged once per contract + /// that is loaded from the database to execute a method on it. + /// + /// Estimation: Measure the cost to execute an empty contract method + /// directly on a runtime instance, using different sizes of contracts. + /// Use least-squares to calculate base and per-byte cost. + /// The contract size is scaled by adding more methods to it. This has been + /// identified as particular expensive in terms of per-byte loading time. + /// This makes it a better scaling strategy than, for example, adding large + /// constants in the data section. + ContractLoadingBase, + /// Estimates the executable loading part of `wasm_contract_loading_bytes` + /// which is charged for each byte in a contract when it is loaded as an + /// executable. + /// + /// This cost also has to cover the reading of the code from the database. + /// So technically, it covers code loading from database and also executable + /// loading. But it is still charged right before executable loading because + /// pre-charging for loading from the database is not possible without + /// knowing the code size. + /// + /// Estimation: See `ContractLoadingBase`. + ContractLoadingPerByte, + /// Estimates the storage loading part of `wasm_contract_loading_bytes`. + /// + /// See comment on `ContractLoadingPerByte` why these are combined. + /// + /// Estimation: Measure the cost difference of two transactions calling a + /// trivial smart contract method, where one contract has a large data + /// section and the other contract is very small. Divide the difference in + /// size. + FunctionCallPerStorageByte, + GasMeteringBase, + GasMeteringOp, + /// Cost of inserting a new value directly into a RocksDB instance. + /// In default settings, this is an alternative estimation for + /// `StorageWriteValueByte`, measured in a more controlled setup. + /// Using the extra flags prefixed with `rdb-`, this can be used to measure + /// the impact of various RocksDB settings on insertions. + RocksDbInsertValueByte, + /// Cost of reading values directly from a RocksDB instance. + /// In default settings, this is an alternative estimation for + /// `StorageReadValueByte`, measured in a more controlled setup. + /// Using the extra flags prefixed with `rdb-`, this can be used to measure + /// the impact of various RocksDB settings on read performance. + RocksDbReadValueByte, + IoReadByte, + IoWriteByte, + CpuBenchmarkSha256, + OneCPUInstruction, + OneNanosecond, + + __Count, +} + +impl Cost { + pub fn all() -> impl Iterator { + (0..(Cost::__Count as u8)).map(Cost::try_from).map(Result::unwrap) + } +} + +impl TryFrom for Cost { + type Error = (); + + fn try_from(d: u8) -> Result { + if d < (Cost::__Count as u8) { + // SAFETY: `#[repr(u8)]`, the above range, and the language spec + // guarantee that `d` can be cast to the enum. + Ok(unsafe { std::mem::transmute::(d) }) + } else { + Err(()) + } + } +} + +impl fmt::Display for Cost { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +impl FromStr for Cost { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + // Ridiculously inefficient, but shouldn't mater. + for cost in Cost::all() { + if cost.to_string() == s { + return Ok(cost); + } + } + anyhow::bail!("failed parsing {s} as Cost"); + } +} diff --git a/runtime/runtime-params-estimator/src/cost_table.rs b/runtime/runtime-params-estimator/src/cost_table.rs new file mode 100644 index 000000000..9f6348438 --- /dev/null +++ b/runtime/runtime-params-estimator/src/cost_table.rs @@ -0,0 +1,115 @@ +use anyhow::Context; +use unc_primitives::types::Gas; +use num_rational::Ratio; +use std::collections::BTreeMap; +use std::fmt; +use std::str::FromStr; + +use crate::cost::Cost; + +/// For each [`Cost`], the price of a single unit in [`Gas`]. +/// +/// This is the ultimate result of a parameter estimator run. +#[derive(Default)] +pub struct CostTable { + map: BTreeMap, +} + +#[derive(Default)] +pub struct CostTableDiff { + map: BTreeMap, +} + +impl CostTable { + pub(crate) fn add(&mut self, cost: Cost, value: Gas) { + let prev = self.map.insert(cost, value); + assert!(prev.is_none()) + } + pub(crate) fn get(&self, cost: Cost) -> Option { + self.map.get(&cost).copied() + } + pub fn diff(&self, other: &CostTable) -> CostTableDiff { + let mut res = CostTableDiff::default(); + for (&cost, &x) in &self.map { + if let Some(&y) = other.map.get(&cost) { + res.map.insert(cost, (x, y)); + } + } + res + } +} + +impl FromStr for CostTable { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let mut res = CostTable::default(); + for line in s.lines() { + let mut words = line.split_ascii_whitespace(); + let cost = words.next().context("expected cost name")?; + let gas = words.next().context("expected gas value")?; + if let Some(word) = words.next() { + anyhow::bail!("unexpected token {word}"); + } + + let cost = cost.parse()?; + let value = gas.replace('_', "").parse()?; + + res.add(cost, value) + } + Ok(res) + } +} + +impl fmt::Display for CostTable { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for cost in Cost::all() { + if let Some(gas) = self.get(cost) { + let gas = format_gas(gas); + writeln!(f, "{:<35} {:>25}", cost.to_string(), gas)? + } + } + Ok(()) + } +} + +impl fmt::Display for CostTableDiff { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "{:<35} {:>25} {:>25} {:>13}", "Cost", "First", "Second", "Second/First")?; + + let mut biggest_diff_first = self.map.iter().collect::>(); + biggest_diff_first.sort_by_key(|(_, &(f, s))| Ratio::new(f, s).max(Ratio::new(s, f))); + biggest_diff_first.reverse(); + + for (&cost, &(first, second)) in biggest_diff_first { + writeln!( + f, + "{:<35} {:>25} {:>25} {:>13.2}", + cost.to_string(), + format_gas(first), + format_gas(second), + second as f64 / first as f64, + )? + } + Ok(()) + } +} + +pub(crate) fn format_gas(mut n: Gas) -> String { + let mut parts = Vec::new(); + while n >= 1000 { + parts.push(format!("{:03?}", n % 1000)); + n /= 1000; + } + parts.push(n.to_string()); + parts.reverse(); + parts.join("_") +} + +#[test] +fn test_separate_thousands() { + assert_eq!(format_gas(0).as_str(), "0"); + assert_eq!(format_gas(999).as_str(), "999"); + assert_eq!(format_gas(1000).as_str(), "1_000"); + assert_eq!(format_gas(u64::MAX).as_str(), "18_446_744_073_709_551_615"); +} diff --git a/runtime/runtime-params-estimator/src/costs_to_runtime_config.rs b/runtime/runtime-params-estimator/src/costs_to_runtime_config.rs new file mode 100644 index 000000000..9cd2d6042 --- /dev/null +++ b/runtime/runtime-params-estimator/src/costs_to_runtime_config.rs @@ -0,0 +1,158 @@ +use unc_parameters::vm::Config as VMConfig; +use unc_parameters::{ + AccountCreationConfig, ActionCosts, ExtCosts, ExtCostsConfig, Fee, ParameterCost, + RuntimeConfig, RuntimeConfigStore, RuntimeFeesConfig, +}; +use unc_primitives::version::PROTOCOL_VERSION; + +use anyhow::Context; + +use crate::cost::Cost; +use crate::cost_table::CostTable; + +/// Turn a [`CostTable`] into a [`RuntimeConfig`]. +/// +/// Will fail if [`CostTable`] doesn't contain all costs. +/// +/// Note that the actual [`RuntimeConfig`] we use is currently hard-coded -- we +/// don't really use this function in production. +pub fn costs_to_runtime_config(cost_table: &CostTable) -> anyhow::Result { + let regular_op_cost = cost_table + .get(Cost::WasmInstruction) + .with_context(|| format!("undefined cost: {}", Cost::WasmInstruction))?; + + // Take latest VM limit config, because estimation doesn't affect it. + // Note that if you run estimation against stable version, it doesn't catch updates of nightly + // version. + let config_store = RuntimeConfigStore::new(None); + let latest_runtime_config = config_store.get_config(PROTOCOL_VERSION); + let vm_limit_config = latest_runtime_config.wasm_config.limit_config.clone(); + + let res = RuntimeConfig { + fees: runtime_fees_config(cost_table)?, + wasm_config: VMConfig { + ext_costs: ext_costs_config(cost_table)?, + grow_mem_cost: 1, + regular_op_cost: u32::try_from(regular_op_cost).unwrap(), + limit_config: vm_limit_config, + ..latest_runtime_config.wasm_config + }, + account_creation_config: AccountCreationConfig::default(), + }; + Ok(res) +} + +fn runtime_fees_config(cost_table: &CostTable) -> anyhow::Result { + let fee = |cost: Cost| -> anyhow::Result { + let total_gas = + cost_table.get(cost).with_context(|| format!("undefined cost: {}", cost))?; + // Split the total cost evenly between send and execution fee. + Ok(Fee { send_sir: total_gas / 2, send_not_sir: total_gas / 2, execution: total_gas / 2 }) + }; + + let config_store = RuntimeConfigStore::new(None); + let actual_fees_config = &config_store.get_config(PROTOCOL_VERSION).fees; + let res = RuntimeFeesConfig { + action_fees: enum_map::enum_map! { + ActionCosts::create_account => fee(Cost::ActionCreateAccount)?, + ActionCosts::delegate => fee(Cost::ActionDelegate)?, + ActionCosts::delete_account => fee(Cost::ActionDeleteAccount)?, + ActionCosts::deploy_contract_base => fee(Cost::ActionDeployContractBase)?, + ActionCosts::deploy_contract_byte => fee(Cost::ActionDeployContractPerByte)?, + ActionCosts::function_call_base => fee(Cost::ActionFunctionCallBase)?, + ActionCosts::function_call_byte => fee(Cost::ActionFunctionCallPerByte)?, + ActionCosts::transfer => fee(Cost::ActionTransfer)?, + ActionCosts::stake => fee(Cost::ActionStake)?, + ActionCosts::add_full_access_key => fee(Cost::ActionAddFullAccessKey)?, + ActionCosts::add_function_call_key_base => fee(Cost::ActionAddFunctionAccessKeyBase)?, + ActionCosts::add_function_call_key_byte => fee(Cost::ActionAddFunctionAccessKeyPerByte)?, + ActionCosts::delete_key => fee(Cost::ActionDeleteKey)?, + ActionCosts::new_action_receipt => fee(Cost::ActionReceiptCreation)?, + ActionCosts::new_data_receipt_base => fee(Cost::DataReceiptCreationBase)?, + ActionCosts::new_data_receipt_byte => fee(Cost::DataReceiptCreationPerByte)?, + ActionCosts::register_rsa2048_keys => fee(Cost::RegisterRsa2048Keys)?, + ActionCosts::create_rsa2048_challenge => fee(Cost::CreateRsa2048Challenge)?, + }, + ..actual_fees_config.clone() + }; + Ok(res) +} + +fn ext_costs_config(cost_table: &CostTable) -> anyhow::Result { + Ok(ExtCostsConfig { + costs: enum_map::enum_map! { + // TODO: storage_iter_* operations below are deprecated, so just hardcode zero price, + // and remove those operations ASAP. + ExtCosts::storage_iter_create_prefix_base => 0, + ExtCosts::storage_iter_create_prefix_byte => 0, + ExtCosts::storage_iter_create_range_base => 0, + ExtCosts::storage_iter_create_from_byte => 0, + ExtCosts::storage_iter_create_to_byte => 0, + ExtCosts::storage_iter_next_base => 0, + ExtCosts::storage_iter_next_key_byte => 0, + ExtCosts::storage_iter_next_value_byte => 0, + // TODO: accurately price host functions that expose validator information. + ExtCosts::validator_frozen_base => 303944908800, + ExtCosts::validator_total_frozen_base => 303944908800, + cost => { + let estimation = estimation(cost).with_context(|| format!("external WASM cost has no estimation defined: {}", cost))?; + cost_table.get(estimation).with_context(|| format!("undefined external WASM cost: {}", cost))? + }, + }.map(|_, value| ParameterCost { gas: value, compute: value }), + }) +} + +fn estimation(cost: ExtCosts) -> Option { + Some(match cost { + ExtCosts::base => Cost::HostFunctionCall, + ExtCosts::read_memory_base => Cost::ReadMemoryBase, + ExtCosts::read_memory_byte => Cost::ReadMemoryByte, + ExtCosts::write_memory_base => Cost::WriteMemoryBase, + ExtCosts::write_memory_byte => Cost::WriteMemoryByte, + ExtCosts::read_register_base => Cost::ReadRegisterBase, + ExtCosts::read_register_byte => Cost::ReadRegisterByte, + ExtCosts::write_register_base => Cost::WriteRegisterBase, + ExtCosts::write_register_byte => Cost::WriteRegisterByte, + ExtCosts::utf8_decoding_base => Cost::Utf8DecodingBase, + ExtCosts::utf8_decoding_byte => Cost::Utf8DecodingByte, + ExtCosts::utf16_decoding_base => Cost::Utf16DecodingBase, + ExtCosts::utf16_decoding_byte => Cost::Utf16DecodingByte, + ExtCosts::sha256_base => Cost::Sha256Base, + ExtCosts::sha256_byte => Cost::Sha256Byte, + ExtCosts::keccak256_base => Cost::Keccak256Base, + ExtCosts::keccak256_byte => Cost::Keccak256Byte, + ExtCosts::keccak512_base => Cost::Keccak512Base, + ExtCosts::keccak512_byte => Cost::Keccak512Byte, + ExtCosts::ripemd160_base => Cost::Ripemd160Base, + ExtCosts::ripemd160_block => Cost::Ripemd160Block, + ExtCosts::ecrecover_base => Cost::EcrecoverBase, + ExtCosts::ed25519_verify_base => Cost::Ed25519VerifyBase, + ExtCosts::ed25519_verify_byte => Cost::Ed25519VerifyByte, + ExtCosts::log_base => Cost::LogBase, + ExtCosts::log_byte => Cost::LogByte, + ExtCosts::storage_write_base => Cost::StorageWriteBase, + ExtCosts::storage_write_key_byte => Cost::StorageWriteKeyByte, + ExtCosts::storage_write_value_byte => Cost::StorageWriteValueByte, + ExtCosts::storage_write_evicted_byte => Cost::StorageWriteEvictedByte, + ExtCosts::storage_read_base => Cost::StorageReadBase, + ExtCosts::storage_read_key_byte => Cost::StorageReadKeyByte, + ExtCosts::storage_read_value_byte => Cost::StorageReadValueByte, + ExtCosts::storage_remove_base => Cost::StorageRemoveBase, + ExtCosts::storage_remove_key_byte => Cost::StorageRemoveKeyByte, + ExtCosts::storage_remove_ret_value_byte => Cost::StorageRemoveRetValueByte, + ExtCosts::storage_has_key_base => Cost::StorageHasKeyBase, + ExtCosts::storage_has_key_byte => Cost::StorageHasKeyByte, + ExtCosts::touching_trie_node => Cost::TouchingTrieNode, + ExtCosts::read_cached_trie_node => Cost::ReadCachedTrieNode, + ExtCosts::promise_and_base => Cost::PromiseAndBase, + ExtCosts::promise_and_per_promise => Cost::PromiseAndPerPromise, + ExtCosts::promise_return => Cost::PromiseReturn, + ExtCosts::alt_bn128_g1_sum_base => Cost::AltBn128G1SumBase, + ExtCosts::alt_bn128_g1_sum_element => Cost::AltBn128G1SumElement, + ExtCosts::alt_bn128_g1_multiexp_base => Cost::AltBn128G1MultiexpBase, + ExtCosts::alt_bn128_g1_multiexp_element => Cost::AltBn128G1MultiexpElement, + ExtCosts::alt_bn128_pairing_check_base => Cost::AltBn128PairingCheckBase, + ExtCosts::alt_bn128_pairing_check_element => Cost::AltBn128PairingCheckElement, + _ => return None, + }) +} diff --git a/runtime/runtime-params-estimator/src/estimator_context.rs b/runtime/runtime-params-estimator/src/estimator_context.rs new file mode 100644 index 000000000..92d4f6189 --- /dev/null +++ b/runtime/runtime-params-estimator/src/estimator_context.rs @@ -0,0 +1,441 @@ +use super::transaction_builder::TransactionBuilder; +use crate::config::{Config, GasMetric}; +use crate::gas_cost::GasCost; +use genesis_populate::get_account_id; +use genesis_populate::state_dump::StateDump; +use unc_parameters::{ExtCosts, RuntimeConfigStore}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::Receipt; +use unc_primitives::runtime::migration_data::{MigrationData, MigrationFlags}; +use unc_primitives::state::FlatStateValue; +use unc_primitives::test_utils::MockEpochInfoProvider; +use unc_primitives::transaction::{ExecutionStatus, SignedTransaction}; +use unc_primitives::types::{Gas, MerkleHash}; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_store::flat::{ + store_helper, BlockInfo, FlatStateChanges, FlatStateDelta, FlatStateDeltaMetadata, FlatStorage, + FlatStorageManager, FlatStorageReadyStatus, FlatStorageStatus, +}; +use unc_store::{ + ShardTries, ShardUId, StateSnapshotConfig, Store, StoreCompiledContractCache, TrieUpdate, +}; +use unc_store::{TrieCache, TrieCachingStorage, TrieConfig}; +use unc_vm_runner::logic::LimitConfig; +use node_runtime::{ApplyState, Runtime}; +use std::collections::HashMap; +use std::iter; +use std::sync::Arc; + +/// Global context shared by all cost calculating functions. +pub(crate) struct EstimatorContext<'c> { + pub(crate) config: &'c Config, + pub(crate) cached: CachedCosts, +} + +#[derive(Default)] +pub(crate) struct CachedCosts { + pub(crate) action_receipt_creation: Option, + pub(crate) action_sir_receipt_creation: Option, + pub(crate) action_add_function_access_key_base: Option, + pub(crate) deploy_contract_base: Option, + pub(crate) noop_function_call_cost: Option, + pub(crate) storage_read_base: Option, + pub(crate) contract_loading_base_per_byte: Option<(GasCost, GasCost)>, + pub(crate) compile_cost_base_per_byte: Option<(GasCost, GasCost)>, + pub(crate) compile_cost_base_per_byte_v2: Option<(GasCost, GasCost)>, + pub(crate) gas_metering_cost_base_per_op: Option<(GasCost, GasCost)>, + pub(crate) apply_block: Option, + pub(crate) touching_trie_node_write: Option, + pub(crate) ed25519_verify_base: Option, +} + +impl<'c> EstimatorContext<'c> { + pub(crate) fn new(config: &'c Config) -> Self { + let cached = CachedCosts::default(); + Self { cached, config } + } + + pub(crate) fn testbed(&mut self) -> Testbed<'_> { + // Copies dump from another directory and loads the state from it. + let workdir = tempfile::Builder::new().prefix("runtime_testbed").tempdir().unwrap(); + let StateDump { store, roots } = StateDump::from_dir( + &self.config.state_dump_path, + workdir.path(), + self.config.in_memory_db, + false, + ); + // Ensure decent RocksDB SST file layout. + store.compact().expect("compaction failed"); + + assert!(roots.len() <= 1, "Parameter estimation works with one shard only."); + assert!(!roots.is_empty(), "No state roots found."); + let root = roots[0]; + + let shard_uid = ShardUId::single_shard(); + let flat_storage_manager = FlatStorageManager::new(store.clone()); + let mut store_update = store.store_update(); + store_helper::set_flat_storage_status( + &mut store_update, + shard_uid, + FlatStorageStatus::Ready(FlatStorageReadyStatus { + flat_head: BlockInfo::genesis(CryptoHash::hash_borsh(0usize), 0), + }), + ); + store_update.commit().unwrap(); + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + + let flat_storage = flat_storage_manager.get_flat_storage_for_shard(shard_uid).unwrap(); + self.generate_deltas(&flat_storage); + + // Create ShardTries with relevant settings adjusted for estimator. + let mut trie_config = unc_store::TrieConfig::default(); + trie_config.enable_receipt_prefetching = true; + let tries = ShardTries::new( + store.clone(), + trie_config, + &[shard_uid], + flat_storage_manager, + StateSnapshotConfig::default(), + ); + + Testbed { + config: self.config, + _workdir: workdir, + tries, + root, + runtime: Runtime::new(), + prev_receipts: Vec::new(), + apply_state: Self::make_apply_state(store), + epoch_info_provider: MockEpochInfoProvider::default(), + transaction_builder: TransactionBuilder::new( + (0..self.config.active_accounts) + .map(|index| get_account_id(index as u64)) + .collect(), + ), + } + } + + fn make_apply_state(store: Store) -> ApplyState { + let mut runtime_config = + RuntimeConfigStore::new(None).get_config(PROTOCOL_VERSION).as_ref().clone(); + + // Override vm limits config to simplify block processing. + runtime_config.wasm_config.limit_config = LimitConfig { + max_total_log_length: u64::MAX, + max_number_registers: u64::MAX, + max_gas_burnt: u64::MAX, + max_register_size: u64::MAX, + max_number_logs: u64::MAX, + + max_actions_per_receipt: u64::MAX, + max_promises_per_function_call_action: u64::MAX, + max_number_input_data_dependencies: u64::MAX, + max_length_storage_key: u64::MAX, + + max_total_prepaid_gas: u64::MAX, + + ..runtime_config.wasm_config.limit_config + }; + runtime_config.account_creation_config.min_allowed_top_level_account_length = 0; + + ApplyState { + // Put each runtime into a separate shard. + block_height: 1, + // Epoch length is long enough to avoid corner cases. + prev_block_hash: Default::default(), + block_hash: Default::default(), + epoch_id: Default::default(), + epoch_height: 0, + gas_price: 0, + block_timestamp: 0, + gas_limit: None, + random_seed: Default::default(), + current_protocol_version: PROTOCOL_VERSION, + config: Arc::new(runtime_config), + cache: Some(Box::new(StoreCompiledContractCache::new(&store))), + is_new_chunk: true, + migration_data: Arc::new(MigrationData::default()), + migration_flags: MigrationFlags::default(), + } + } + + /// Construct a chain of fake blocks with fake deltas for flat storage. + /// + /// Use `hash(height)` as the supposed block hash. + /// Keys are randomly generated, values are a constant that's not even stored. + /// + /// The blocks aren't valid, nor are the values stored anywhere. They only + /// exist within `FlatStorage` and simulate the performance decrease + /// observed when the flat head lags behind. + fn generate_deltas(&self, flat_storage: &FlatStorage) { + // Assumption: One delta per non-final block, which is configurable. + // There could be forks but that's considered to e outside the normal + // operating conditions for this estimation. + let num_deltas = self.config.finality_lag; + // Number of keys changed is the same for all deltas and configurable. + let num_changes_per_delta = self.config.fs_keys_per_delta; + // This is the longest key we allow in storage. + let delta_key_len = 2000; + for idx in 0..num_deltas { + // We want different keys and to avoid all optimization potential. + // But the values are never read, so let's just use a dummy constant. + let random_data = iter::repeat_with(|| { + ( + crate::utils::random_vec(delta_key_len), + Some(FlatStateValue::value_ref(b"this is never stored or accessed, we only need it to blow up in-memory deltas")), + ) + }) + .take(num_changes_per_delta); + let height = 1 + idx as u64; + let block = BlockInfo { + hash: fs_fake_block_height_to_hash(height), + height, + prev_hash: fs_fake_block_height_to_hash(height - 1), + }; + + flat_storage + .add_delta(FlatStateDelta { + changes: FlatStateChanges::from(random_data), + metadata: FlatStateDeltaMetadata { block, prev_block_with_changes: None }, + }) + .unwrap(); + } + } +} + +/// A single isolated instance of runtime. +/// +/// We use it to time processing a bunch of blocks. +pub(crate) struct Testbed<'c> { + pub(crate) config: &'c Config, + /// Directory where we temporarily keep the storage. + _workdir: tempfile::TempDir, + tries: ShardTries, + root: MerkleHash, + runtime: Runtime, + prev_receipts: Vec, + apply_state: ApplyState, + epoch_info_provider: MockEpochInfoProvider, + transaction_builder: TransactionBuilder, +} + +impl Testbed<'_> { + pub(crate) fn transaction_builder(&mut self) -> &mut TransactionBuilder { + &mut self.transaction_builder + } + + /// Apply and measure provided blocks one-by-one. + /// Because some transactions can span multiple blocks, each input block + /// might trigger multiple blocks in execution. The returned results are + /// exactly one per input block, regardless of how many blocks needed to be + /// executed. To avoid surprises in how many blocks are actually executed, + /// `block_latency` must be specified and the function will panic if it is + /// wrong. A latency of 0 means everything is done within a single block. + #[track_caller] + pub(crate) fn measure_blocks( + &mut self, + blocks: Vec>, + block_latency: usize, + ) -> Vec<(GasCost, HashMap)> { + let allow_failures = false; + + let mut res = Vec::with_capacity(blocks.len()); + + for block in blocks { + node_runtime::with_ext_cost_counter(|cc| cc.clear()); + let extra_blocks; + let gas_cost = { + self.clear_caches(); + let start = GasCost::measure(self.config.metric); + self.process_block_impl(&block, allow_failures); + extra_blocks = self.process_blocks_until_no_receipts(allow_failures); + start.elapsed() + }; + assert_eq!(block_latency, extra_blocks); + + let mut ext_costs: HashMap = HashMap::new(); + node_runtime::with_ext_cost_counter(|cc| { + for (c, v) in cc.drain() { + ext_costs.insert(c, v); + } + }); + res.push((gas_cost, ext_costs)); + } + + res + } + + pub(crate) fn process_block(&mut self, block: Vec, block_latency: usize) { + let allow_failures = false; + self.process_block_impl(&block, allow_failures); + let extra_blocks = self.process_blocks_until_no_receipts(allow_failures); + assert_eq!(block_latency, extra_blocks); + } + + pub(crate) fn trie_caching_storage(&mut self) -> TrieCachingStorage { + let store = self.tries.get_store(); + let is_view = false; + let prefetcher = None; + let caching_storage = TrieCachingStorage::new( + store, + TrieCache::new(&TrieConfig::default(), ShardUId::single_shard(), false), + ShardUId::single_shard(), + is_view, + prefetcher, + ); + caching_storage + } + + pub(crate) fn clear_caches(&mut self) { + // Flush out writes hanging in memtable + self.tries.get_store().flush().unwrap(); + + // OS caches: + // - only required in time based measurements, since ICount looks at syscalls directly. + // - requires sudo, therefore this is executed optionally + if self.config.metric == GasMetric::Time && self.config.drop_os_cache { + #[cfg(target_os = "linux")] + crate::utils::clear_linux_page_cache().expect( + "Failed to drop OS caches. Are you root and is /proc mounted with write access?", + ); + #[cfg(not(target_os = "linux"))] + panic!("Cannot drop OS caches on non-linux systems."); + } + } + + fn process_block_impl( + &mut self, + transactions: &[SignedTransaction], + allow_failures: bool, + ) -> Gas { + let trie = self.trie(); + let apply_result = self + .runtime + .apply( + trie, + &None, + &self.apply_state, + &self.prev_receipts, + transactions, + &self.epoch_info_provider, + Default::default(), + ) + .unwrap(); + + let mut store_update = self.tries.store_update(); + let shard_uid = ShardUId::single_shard(); + self.root = self.tries.apply_all(&apply_result.trie_changes, shard_uid, &mut store_update); + unc_store::flat::FlatStateChanges::from_state_changes(&apply_result.state_changes) + .apply_to_flat_state(&mut store_update, shard_uid); + store_update.commit().unwrap(); + self.apply_state.block_height += 1; + + let mut total_burnt_gas = 0; + if !allow_failures { + for outcome in &apply_result.outcomes { + total_burnt_gas += outcome.outcome.gas_burnt; + match &outcome.outcome.status { + ExecutionStatus::Failure(e) => panic!("Execution failed {:#?}", e), + _ => (), + } + } + } + self.prev_receipts = apply_result.outgoing_receipts; + total_burnt_gas + } + + /// Returns the number of blocks required to reach quiescence + fn process_blocks_until_no_receipts(&mut self, allow_failures: bool) -> usize { + let mut n = 0; + while !self.prev_receipts.is_empty() { + self.process_block_impl(&[], allow_failures); + n += 1; + } + n + } + + /// Process just the verification of a transaction, without action execution. + /// + /// Use this method for measuring the SEND cost of actions. This is the + /// workload done on the sender's shard before an action receipt is created. + /// Network costs for sending are not included. + pub(crate) fn verify_transaction( + &mut self, + tx: &SignedTransaction, + metric: GasMetric, + ) -> GasCost { + let mut state_update = TrieUpdate::new(self.trie()); + // gas price and block height can be anything, it doesn't affect performance + // but making it too small affects max_depth and thus pessimistic inflation + let gas_price = 100_000_000; + let block_height = None; + // do a full verification + let verify_signature = true; + + let clock = GasCost::measure(metric); + node_runtime::verify_and_charge_transaction( + &self.apply_state.config, + &mut state_update, + gas_price, + tx, + verify_signature, + block_height, + PROTOCOL_VERSION, + ) + .expect("tx verification should not fail in estimator"); + clock.elapsed() + } + + /// Process only the execution step of an action receipt. + /// + /// Use this method to estimate action exec costs. + pub(crate) fn apply_action_receipt(&mut self, receipt: &Receipt, metric: GasMetric) -> GasCost { + let mut state_update = TrieUpdate::new(self.trie()); + let mut outgoing_receipts = vec![]; + let mut validator_power_proposals = vec![]; + let mut validator_frozen_proposals = vec![]; + let mut stats = node_runtime::ApplyStats::default(); + // TODO: mock is not accurate, potential DB requests are skipped in the mock! + let epoch_info_provider = MockEpochInfoProvider::new([].into_iter()); + let clock = GasCost::measure(metric); + let exec_result = node_runtime::estimator::apply_action_receipt( + &mut state_update, + &self.apply_state, + receipt, + &mut outgoing_receipts, + &mut validator_power_proposals, + &mut validator_frozen_proposals, + &mut stats, + &epoch_info_provider, + ) + .expect("applying receipt in estimator should not fail"); + let gas = clock.elapsed(); + match exec_result.outcome.status { + ExecutionStatus::Unknown => panic!("receipt not applied"), + ExecutionStatus::Failure(err) => panic!("failed apply, {err:?}"), + ExecutionStatus::SuccessValue(_) | ExecutionStatus::SuccessReceiptId(_) => (), + } + gas + } + + /// Instantiate a new trie for the estimator. + fn trie(&mut self) -> unc_store::Trie { + // We generated `finality_lag` fake blocks earlier, so the fake height + // will be at the same number. + let tip_height = self.config.finality_lag; + let tip = fs_fake_block_height_to_hash(tip_height as u64); + self.tries.get_trie_with_block_hash_for_shard( + ShardUId::single_shard(), + self.root, + &tip, + false, + ) + } +} + +/// Maps fake block heights to block hashes. +/// +/// This is ued to generate and access fake deltas for flat storage. +fn fs_fake_block_height_to_hash(height: u64) -> CryptoHash { + CryptoHash::hash_borsh(height) +} diff --git a/runtime/runtime-params-estimator/src/estimator_params.rs b/runtime/runtime-params-estimator/src/estimator_params.rs new file mode 100644 index 000000000..1f6c41fd8 --- /dev/null +++ b/runtime/runtime-params-estimator/src/estimator_params.rs @@ -0,0 +1,58 @@ +//! Some parameters are use within the estimator to transform measurements to gas costs. +//! These parameters have been estimated manually and are now hard-coded for a more deterministic estimation of runtime parameters. +//! This module contains the hard-coded constants as well as the code to manually re-estimate them. + +use unc_primitives::{hash::CryptoHash, types::Gas}; +use num_rational::Ratio; + +use crate::{config::GasMetric, gas_cost::GasCost}; + +// All constant below are measured in Gas, respectively, in fractions thereof. + +/// How much gas there is in a nanosecond worth of computation. +pub(crate) const GAS_IN_NS: Ratio = Ratio::new_raw(1_000_000, 1); +// We use factor of 8 to approximately match the price of SHA256 operation between +// time-based and icount-based metric as measured on 3.2Ghz Core i5. +pub(crate) const GAS_IN_INSTR: Ratio = Ratio::new_raw(1_000_000, 8); + +// IO bytes as measured on the sys_call level are rather unstable when using +// RocksDB as storage solution. Measuring it for debugging purposes is still useful +// but a conversion from total read/written bytes to gas is always going to be inaccurate. +// Consequently, set both values to 0 such that they do not influence gas costs. +pub(crate) const IO_READ_BYTE_COST: Ratio = Ratio::new_raw(0, 1); +pub(crate) const IO_WRITE_BYTE_COST: Ratio = Ratio::new_raw(0, 1); + +/// Measure the cost for running a sha256 Rust implementation (on an arbitrary input). +/// +/// This runs outside the WASM runtime and is intended to measure the overall hardware capabilities of the test system. +/// (The motivation is to stay as close as possible to original estimations done with this code: +/// ) +pub(crate) fn sha256_cost(metric: GasMetric, repeats: u64) -> GasCost { + let cpu = measure_operation(repeats, metric, exec_sha256); + let cpu_per_rep = cpu / repeats; + cpu_per_rep +} + +fn exec_sha256(repeats: u64) -> i64 { + let mut result = 0; + for index in 0..repeats { + let input = "what should I do but tend upon the hours, and times of your desire"; + let val = CryptoHash::hash_bytes(input.as_bytes()); + assert_eq!(val.to_string(), "BTEVNkcDtaui6SJC19Efnrf7A3dCKT9v8y24NiC3S7RR"); + result += val.as_bytes()[(index % 32) as usize] as i64; + } + result +} + +#[used] +static mut SINK: i64 = 0; + +fn measure_operation i64>(count: u64, metric: GasMetric, op: F) -> GasCost { + let start = GasCost::measure(metric); + let value = op(count); + let result = start.elapsed(); + unsafe { + SINK = value; + } + result +} diff --git a/runtime/runtime-params-estimator/src/function_call.rs b/runtime/runtime-params-estimator/src/function_call.rs new file mode 100644 index 000000000..3e28c4e56 --- /dev/null +++ b/runtime/runtime-params-estimator/src/function_call.rs @@ -0,0 +1,112 @@ +use crate::config::{Config, GasMetric}; +use crate::gas_cost::{GasCost, LeastSquaresTolerance}; +use crate::vm_estimator::create_context; +use unc_parameters::vm::VMKind; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::types::ProtocolVersion; +use unc_store::StoreCompiledContractCache; +use unc_vm_runner::internal::VMKindExt; +use unc_vm_runner::logic::mocks::mock_external::MockedExternal; +use unc_vm_runner::logic::CompiledContractCache; +use unc_vm_runner::ContractCode; +use std::fmt::Write; + +/// Estimates linear cost curve for a function call execution cost per byte of +/// total contract code. The contract size is increased by adding more methods +/// to it. This cost is pure VM cost, without the loading from storage. +pub(crate) fn contract_loading_cost(config: &Config) -> (GasCost, GasCost) { + let mut xs = vec![]; + let mut ys = vec![]; + let repeats = config.iter_per_block as u64; + let warmup_repeats = config.warmup_iters_per_block as u64; + for method_count in [5, 20, 30, 50, 100, 200, 1000] { + let contract = make_many_methods_contract(method_count); + let cost = compute_function_call_cost( + config.metric, + config.vm_kind, + repeats, + warmup_repeats, + &contract, + ); + xs.push(contract.code().len() as u64); + ys.push(cost / repeats); + } + + let tolerance = LeastSquaresTolerance::default(); + GasCost::least_squares_method_gas_cost(&xs, &ys, &tolerance, config.debug) +} + +fn make_many_methods_contract(method_count: i32) -> ContractCode { + let mut methods = String::new(); + for i in 0..method_count { + write!( + &mut methods, + r#" + (export "hello{i}" (func {i})) + (func + i32.const {i} + drop + return + ) + "#, + ) + .unwrap(); + } + + let code = format!("(module {methods})"); + ContractCode::new(wat::parse_str(code).unwrap(), None) +} + +fn compute_function_call_cost( + gas_metric: GasMetric, + vm_kind: VMKind, + repeats: u64, + warmup_repeats: u64, + contract: &ContractCode, +) -> GasCost { + let store = unc_store::test_utils::create_test_store(); + let cache_store = StoreCompiledContractCache::new(&store); + let cache: Option<&dyn CompiledContractCache> = Some(&cache_store); + let protocol_version = ProtocolVersion::MAX; + let config_store = RuntimeConfigStore::new(None); + let runtime_config = config_store.get_config(protocol_version).as_ref(); + let vm_config = runtime_config.wasm_config.clone(); + let runtime = vm_kind.runtime(vm_config).expect("runtime has not been enabled"); + let fees = runtime_config.fees.clone(); + let mut fake_external = MockedExternal::new(); + let fake_context = create_context(vec![]); + let promise_results = vec![]; + + // Warmup. + for _ in 0..warmup_repeats { + let result = runtime + .run( + contract, + "hello0", + &mut fake_external, + fake_context.clone(), + &fees, + &promise_results, + cache, + ) + .expect("fatal error"); + assert!(result.aborted.is_none()); + } + // Run with gas metering. + let start = GasCost::measure(gas_metric); + for _ in 0..repeats { + let result = runtime + .run( + contract, + "hello0", + &mut fake_external, + fake_context.clone(), + &fees, + &promise_results, + cache, + ) + .expect("fatal_error"); + assert!(result.aborted.is_none()); + } + start.elapsed() +} diff --git a/runtime/runtime-params-estimator/src/gas_cost.rs b/runtime/runtime-params-estimator/src/gas_cost.rs new file mode 100644 index 000000000..b6da5db23 --- /dev/null +++ b/runtime/runtime-params-estimator/src/gas_cost.rs @@ -0,0 +1,793 @@ +use std::cmp::Ordering; +use std::panic::Location; +use std::time::{Duration, Instant}; +use std::{fmt, iter, ops}; + +use unc_primitives::types::Gas; +use num_rational::Ratio; +use num_traits::ToPrimitive; +use serde_json::json; + +use crate::config::GasMetric; +use crate::estimator_params::{GAS_IN_INSTR, GAS_IN_NS, IO_READ_BYTE_COST, IO_WRITE_BYTE_COST}; +use crate::qemu::QemuMeasurement; + +/// Result of cost estimation. +/// +/// Holds wall-clock time or number of instructions and can be converted to +/// `Gas`. `GasCost` can also be flagged as "uncertain" if we failed to +/// reproducibly measure it. +#[derive(Clone, PartialEq, Eq)] +pub(crate) struct GasCost { + /// The smallest thing we are measuring is one wasm instruction, and it + /// takes about a nanosecond, so we do need to account for fractional + /// nanoseconds here! + time_ns: Option>, + // Values used for `GasMetric::ICount` + qemu: Option, + /// Signals that the measurement was uncertain (ie, had high variance), and + /// that the estimation needs to be re-run. + /// + /// Each specific cost can use it's own criteria for uncertainty -- the end + /// result here is just printing UNCERTAIN next to the corresponding cost in + /// the output. `uncertain_message` can be called to display the reason and + /// code location of where the uncertainty has been set. + uncertain: Option, +} + +pub(crate) struct GasClock { + start: Instant, + metric: GasMetric, +} + +#[derive(Clone, Copy, PartialEq, Eq)] +struct MeasurementUncertainty { + reason: &'static str, + location: &'static Location<'static>, +} + +impl GasCost { + pub(crate) fn zero() -> GasCost { + GasCost { time_ns: None, qemu: None, uncertain: None } + } + + pub(crate) fn measure(metric: GasMetric) -> GasClock { + let start = Instant::now(); + if let GasMetric::ICount = metric { + QemuMeasurement::start_count_instructions(); + }; + GasClock { start, metric } + } + + /// Creates `GasCost` out of raw numeric value of gas. This is required mostly for + /// compatibility with existing code, prefer using `measure` instead. + pub(crate) fn from_gas(raw: Ratio, metric: GasMetric) -> GasCost { + let mut result = GasCost::zero(); + match metric { + GasMetric::ICount => { + result.qemu = Some(QemuMeasurement { + instructions: raw / GAS_IN_INSTR, + io_r_bytes: 0.into(), + io_w_bytes: 0.into(), + }) + } + GasMetric::Time => result.time_ns = Some(raw / GAS_IN_NS), + } + result + } + + /// Like [`std::cmp::Ord::min`] but operates on heterogenous types ([`GasCost`] + [`Gas`]). + pub(crate) fn min_gas(mut self, gas: Gas) -> Self { + let Some(to_add) = gas.checked_sub(self.to_gas()) else { + return self; + }; + if let Some(qemu) = &mut self.qemu { + // QEMU gas is split across multiple components (instructions + // and IO). When rounding up to an amount of gas, the assumption + // is that the caller does not care about how the extra gas is + // distributed. So we add it to the instruction counter and + // don't touch the other values. + qemu.instructions += Ratio::from(to_add) / GAS_IN_INSTR; + } else { + // Time is a single component that we can just set directly. + self.time_ns = Some(Ratio::from(gas) / GAS_IN_NS); + } + self + } + + pub(crate) fn is_uncertain(&self) -> bool { + self.uncertain.is_some() + } + pub(crate) fn uncertain_message(&self) -> Option { + self.uncertain + .map(|MeasurementUncertainty { reason, location }| format!("{reason}: {location}")) + } + #[track_caller] + pub(crate) fn set_uncertain(&mut self, reason: &'static str) { + self.uncertain = Some(MeasurementUncertainty { reason, location: Location::caller() }); + } + /// Performs least squares using a separate variable for each component of the gas cost. + /// + /// Least-squares linear regression sometimes to produces negative + /// parameters even when all input values are positive. However, negative + /// gas costs make no sense for us. What we really want to solve is + /// non-negative least squares (NNLS) but that is algorithmically much more + /// complex. To keep it simpler, instead of solving NNLS, the caller has a + /// couple of choices how to handle negative solutions. + #[track_caller] + pub(crate) fn least_squares_method_gas_cost( + xs: &[u64], + ys: &[Self], + tolerance: &LeastSquaresTolerance, + verbose: bool, + ) -> (Self, Self) { + if verbose { + eprintln!("Least squares input:"); + eprint!("["); + for x in xs { + eprint!("{x},"); + } + eprintln!("] * x ="); + eprint!("["); + for y in ys { + eprint!("{},", y.to_gas()); + } + eprintln!("]"); + } + match least_squares_method_gas_cost_pos_neg(xs, ys, verbose) { + Ok(res) => res, + Err((mut pos, neg)) => { + // On negative parameters, return positive part and mark as uncertain if necessary + if !tolerance.tolerates(&pos, &neg) { + pos.0.set_uncertain("NEG-LEAST-SQUARES"); + pos.1.set_uncertain("NEG-LEAST-SQUARES"); + } + pos + } + } + } + + /// Subtracts two gas costs from each other without panicking on an arithmetic underflow. + /// If the given tolerance is breached, the result will be marked as uncertain. + #[track_caller] + pub(crate) fn saturating_sub(&self, rhs: &Self, tolerance: &NonNegativeTolerance) -> Self { + let mut pos = self.saturating_sub_no_uncertain_check(rhs); + let neg = rhs.saturating_sub_no_uncertain_check(self); + if !tolerance.tolerates(&pos, &neg) { + pos.set_uncertain("SUBTRACTION-UNDERFLOW"); + } + pos.combine_uncertain(self); + pos.combine_uncertain(rhs); + pos + } + + fn saturating_sub_no_uncertain_check(&self, rhs: &Self) -> Self { + let qemu = match (&self.qemu, &rhs.qemu) { + (Some(lhs), Some(rhs)) => Some(QemuMeasurement { + instructions: saturating_sub(lhs.instructions, rhs.instructions), + io_r_bytes: saturating_sub(lhs.io_r_bytes, rhs.io_r_bytes), + io_w_bytes: saturating_sub(lhs.io_w_bytes, rhs.io_w_bytes), + }), + (any_lhs, _any_rhs) => any_lhs.clone(), + }; + let time_ns = match (self.time_ns, rhs.time_ns) { + (Some(lhs), Some(rhs)) => Some(saturating_sub(lhs, rhs)), + (any_lhs, _any_rhs) => any_lhs, + }; + GasCost { time_ns, qemu, uncertain: None } + } + + /// Does nothing if `GasCost` is already uncertain, otherise copies + /// uncertain from other `GasCost`. + fn combine_uncertain(&mut self, rhs: &Self) { + if !self.is_uncertain() { + self.uncertain = rhs.uncertain; + } + } + /// JSON representation of the gas cost. This is intended to be used by + /// other scripts, such as the continuous estimation pipeline. Consumers + /// should expect more fields to be added. But existing fields should remain + /// stable. + + pub fn to_json(&self) -> serde_json::Value { + if let Some(qemu) = &self.qemu { + json!({ + "gas": self.to_gas(), + "metric": "icount", + "instructions": qemu.instructions.to_f64(), + "io_r_bytes": qemu.io_r_bytes.to_f64(), + "io_w_bytes": qemu.io_w_bytes.to_f64(), + // `None` will be printed as `null` + "uncertain_reason": self.uncertain.map(|u| u.reason), + }) + } else if let Some(time_ns) = self.time_ns { + json!({ + "gas": self.to_gas(), + "metric": "time", + "time_ns": time_ns.to_f64(), + // `None` will be printed as `null` + "uncertain_reason": self.uncertain.map(|u| u.reason), + }) + } else { + serde_json::Value::Null + } + } +} + +/// Defines what negative solutions are allowed in a least-squares result. +/// Default is all negative values are treated as errors. +#[derive(Clone, PartialEq)] +pub(crate) struct LeastSquaresTolerance { + base_nn_tolerance: NonNegativeTolerance, + factor_nn_tolerance: NonNegativeTolerance, +} + +impl Default for LeastSquaresTolerance { + fn default() -> Self { + Self { + base_nn_tolerance: NonNegativeTolerance::Strict, + factor_nn_tolerance: NonNegativeTolerance::Strict, + } + } +} +impl LeastSquaresTolerance { + /// Tolerate negative values in base cost up to a factor of total cost + #[allow(dead_code)] + pub(crate) fn base_rel_nn_tolerance(mut self, rel_tolerance: f64) -> Self { + self.base_nn_tolerance = NonNegativeTolerance::RelativeTolerance(rel_tolerance); + self + } + /// Tolerate negative values in base cost up to a factor of total cost + pub(crate) fn factor_rel_nn_tolerance(mut self, rel_tolerance: f64) -> Self { + self.factor_nn_tolerance = NonNegativeTolerance::RelativeTolerance(rel_tolerance); + self + } + /// Tolerate negative values in base cost up to a fixed gas value + pub(crate) fn base_abs_nn_tolerance(mut self, abs_tolerance: Gas) -> Self { + self.base_nn_tolerance = NonNegativeTolerance::AbsoluteTolerance(abs_tolerance); + self + } + /// Tolerate negative values in base cost up to a fixed gas value + #[allow(dead_code)] + pub(crate) fn factor_abs_nn_tolerance(mut self, abs_tolerance: Gas) -> Self { + self.factor_nn_tolerance = NonNegativeTolerance::AbsoluteTolerance(abs_tolerance); + self + } +} + +/// Defines what negative solutions are allowed in a least-squares result +#[derive(Clone, Copy, PartialEq)] +pub(crate) enum NonNegativeTolerance { + /// Allow no negative values + Strict, + /// Tolerate negative values if it changes the total gas by less than X times. + RelativeTolerance(f64), + /// Tolerate negative values if they are below X gas + AbsoluteTolerance(Gas), +} + +impl LeastSquaresTolerance { + fn tolerates(&self, pos: &(GasCost, GasCost), neg: &(GasCost, GasCost)) -> bool { + self.base_nn_tolerance.tolerates(&pos.0, &neg.0) + && self.factor_nn_tolerance.tolerates(&pos.1, &neg.1) + } +} +impl NonNegativeTolerance { + /// Tolerate negative values if they account for less than 0.1% of the total + pub(crate) const PER_MILLE: NonNegativeTolerance = + NonNegativeTolerance::RelativeTolerance(0.001); + + fn tolerates(&self, pos: &GasCost, neg: &GasCost) -> bool { + match self { + NonNegativeTolerance::Strict => neg.to_gas() == 0, + NonNegativeTolerance::RelativeTolerance(rel_tolerance) => { + pos.to_gas() > 0 + && Ratio::new(neg.to_gas(), pos.to_gas()).to_f64().unwrap() <= *rel_tolerance + } + NonNegativeTolerance::AbsoluteTolerance(gas_threshold) => { + neg.to_gas() <= *gas_threshold + } + } + } +} + +/// Like GasCost::least_squares_method_gas_cost but in the case of a solution +/// with negative parameters, it returns (A,B), where A has negative values set +/// to zero and B contains the values so that solution = A-B. +#[allow(clippy::result_large_err)] +fn least_squares_method_gas_cost_pos_neg( + xs: &[u64], + ys: &[GasCost], + verbose: bool, +) -> Result<(GasCost, GasCost), ((GasCost, GasCost), (GasCost, GasCost))> { + let uncertain = ys.iter().find_map(|cost| cost.uncertain); + + let mut pos_base = GasCost::zero(); + let mut pos_factor = GasCost::zero(); + let mut neg_base = GasCost::zero(); + let mut neg_factor = GasCost::zero(); + + pos_base.uncertain = uncertain; + pos_factor.uncertain = uncertain; + + if let Some(first) = ys.get(0) { + if first.qemu.is_some() { + assert!( + ys.iter().all(|y| y.qemu.is_some() || y.to_gas() == 0), + "least square expects homogenous data" + ); + + let qemu_ys = ys + .iter() + .map(|y| y.qemu.clone().unwrap_or_else(|| QemuMeasurement::zero())) + .collect::>(); + + let (pos, neg) = crate::least_squares::qemu_measurement_least_squares(xs, &qemu_ys); + pos_base.qemu = Some(pos.0); + pos_factor.qemu = Some(pos.1); + neg_base.qemu = Some(neg.0); + neg_factor.qemu = Some(neg.1); + } + if first.time_ns.is_some() { + assert!( + ys.iter().all(|y| y.time_ns.is_some() || y.to_gas() == 0), + "least square expects homogenous data" + ); + let time_ys = ys.iter().map(|y| y.time_ns.unwrap_or(0.into())).collect::>(); + let (pos, neg) = crate::least_squares::time_measurement_least_squares(xs, &time_ys); + pos_base.time_ns = Some(pos.0); + pos_factor.time_ns = Some(pos.1); + neg_base.time_ns = Some(neg.0); + neg_factor.time_ns = Some(neg.1); + } + } + + if neg_base.to_gas() == 0 && neg_factor.to_gas() == 0 { + if verbose { + eprintln!("Least-squares output: {pos_base:?} + N * {pos_factor:?}",); + } + Ok((pos_base, pos_factor)) + } else { + if verbose { + eprintln!( + "Least-squares had negative parameters: ({pos_base:?} - {neg_base:?}) + N * ({pos_factor:?} - {neg_factor:?})", + ); + } + Err(((pos_base, pos_factor), (neg_base, neg_factor))) + } +} + +impl GasClock { + pub(crate) fn elapsed(self) -> GasCost { + let mut result = GasCost::zero(); + + match self.metric { + GasMetric::ICount => { + let qemu = QemuMeasurement::end_count_instructions(); + result.qemu = Some(qemu); + } + GasMetric::Time => { + let ns: u64 = self.start.elapsed().as_nanos().try_into().unwrap(); + result.time_ns = Some(ns.into()); + } + } + + result + } +} + +impl fmt::Debug for GasCost { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(qemu) = &self.qemu { + write!( + f, + "{:0.2}i {:0.2}r {:0.2}w", + qemu.instructions.to_f64().unwrap(), + qemu.io_r_bytes.to_f64().unwrap(), + qemu.io_w_bytes.to_f64().unwrap() + ) + } else if let Some(time_ns) = self.time_ns { + if time_ns >= 1.into() { + fmt::Debug::fmt(&Duration::from_nanos(time_ns.round().to_integer()), f) + } else { + // Sometimes, dividing costs yields results smaller than one ns. + write!(f, "{}ps", (time_ns * 1000).to_integer()) + } + } else { + write!(f, "empty-measurement") + } + } +} + +impl ops::Add for GasCost { + type Output = GasCost; + + fn add(mut self, rhs: GasCost) -> Self::Output { + self.combine_uncertain(&rhs); + let qemu = match (self.qemu, rhs.qemu) { + (None, None) => None, + (Some(lhs), Some(rhs)) => Some(lhs + rhs), + (single_value, None) | (None, single_value) => single_value, + }; + let time_ns = match (self.time_ns, rhs.time_ns) { + (None, None) => None, + (Some(lhs), Some(rhs)) => Some(lhs + rhs), + (single_value, None) | (None, single_value) => single_value, + }; + GasCost { time_ns, qemu, uncertain: self.uncertain } + } +} + +impl ops::AddAssign for GasCost { + fn add_assign(&mut self, rhs: GasCost) { + *self = self.clone() + rhs; + } +} + +impl iter::Sum for GasCost { + fn sum>(iter: I) -> Self { + let mut accum = GasCost::zero(); + for gas in iter { + accum += gas; + } + accum + } +} + +impl ops::Sub for GasCost { + type Output = GasCost; + + #[track_caller] + fn sub(self, rhs: GasCost) -> Self::Output { + self.saturating_sub(&rhs, &NonNegativeTolerance::Strict) + } +} + +impl ops::Mul for GasCost { + type Output = GasCost; + + fn mul(mut self, rhs: u64) -> Self::Output { + if let Some(qemu) = &mut self.qemu { + qemu.instructions *= rhs; + qemu.io_r_bytes *= rhs; + qemu.io_w_bytes *= rhs; + } + if let Some(time_ns) = &mut self.time_ns { + *time_ns *= rhs; + } + self + } +} + +impl ops::Div for GasCost { + type Output = GasCost; + + fn div(mut self, rhs: u64) -> Self::Output { + if let Some(qemu) = &mut self.qemu { + qemu.instructions /= rhs; + qemu.io_r_bytes /= rhs; + qemu.io_w_bytes /= rhs; + } + if let Some(time_ns) = &mut self.time_ns { + *time_ns /= rhs; + } + self + } +} + +fn saturating_sub(a: Ratio, b: Ratio) -> Ratio { + if a < b { + 0.into() + } else { + a - b + } +} + +impl PartialOrd for GasCost { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for GasCost { + fn cmp(&self, other: &Self) -> Ordering { + self.to_gas().cmp(&other.to_gas()) + } +} + +impl GasCost { + pub(crate) fn to_gas(&self) -> Gas { + if let Some(qemu) = &self.qemu { + (GAS_IN_INSTR * qemu.instructions + + IO_READ_BYTE_COST * qemu.io_r_bytes + + IO_WRITE_BYTE_COST * qemu.io_w_bytes) + .to_integer() + } else if let Some(ns) = self.time_ns { + (GAS_IN_NS * ns).to_integer() + } else { + 0 + } + } +} + +#[cfg(test)] +mod tests { + use super::{least_squares_method_gas_cost_pos_neg, GasCost, LeastSquaresTolerance}; + use crate::estimator_params::{GAS_IN_INSTR, GAS_IN_NS, IO_READ_BYTE_COST, IO_WRITE_BYTE_COST}; + use crate::qemu::QemuMeasurement; + use unc_primitives::types::Gas; + use num_rational::Ratio; + use num_traits::{ToPrimitive, Zero}; + + #[track_caller] + fn check_uncertainty( + xs: &[u64], + ys: &[GasCost], + tolerance: LeastSquaresTolerance, + expected_uncertain: bool, + ) { + let result = GasCost::least_squares_method_gas_cost(xs, ys, &tolerance, true); + assert_eq!(result.0.is_uncertain(), expected_uncertain); + assert_eq!(result.1.is_uncertain(), expected_uncertain); + } + + #[track_caller] + fn check_least_squares_method_gas_cost_pos_neg( + xs: &[u64], + ys: &[GasCost], + expected: Result<(GasCost, GasCost), ((GasCost, GasCost), (GasCost, GasCost))>, + ) { + let result = least_squares_method_gas_cost_pos_neg(xs, ys, true); + assert_eq!(result, expected); + } + + impl GasCost { + pub(crate) fn new_time_based(time_ns: impl Into>) -> Self { + let mut result = GasCost::zero(); + result.time_ns = Some(time_ns.into()); + result + } + pub(crate) fn new_icount_based( + instructions: impl Into>, + io_r_bytes: impl Into>, + io_w_bytes: impl Into>, + ) -> Self { + let mut result = GasCost::zero(); + let qemu = QemuMeasurement { + instructions: instructions.into(), + io_r_bytes: io_r_bytes.into(), + io_w_bytes: io_w_bytes.into(), + }; + result.qemu = Some(qemu); + result + } + } + + fn abs_tolerance(base: Gas, factor: Gas) -> LeastSquaresTolerance { + LeastSquaresTolerance::default().base_abs_nn_tolerance(base).factor_abs_nn_tolerance(factor) + } + fn rel_tolerance(base: f64, factor: f64) -> LeastSquaresTolerance { + LeastSquaresTolerance::default().base_rel_nn_tolerance(base).factor_rel_nn_tolerance(factor) + } + + #[test] + fn least_squares_method_gas_cost_time_ok() { + let xs = [10, 20, 30]; + + let ys = [ + GasCost::new_time_based(10_050), + GasCost::new_time_based(20_050), + GasCost::new_time_based(30_050), + ]; + + let expected = Ok((GasCost::new_time_based(50), GasCost::new_time_based(1000))); + // Check low-level least-squares method + check_least_squares_method_gas_cost_pos_neg(&xs, &ys, expected); + + // Also check applied tolerance strategies, in this case they should always be certain as the solution is positive + check_uncertainty(&xs, &ys, Default::default(), false); + check_uncertainty(&xs, &ys, abs_tolerance(1, 1), false); + check_uncertainty(&xs, &ys, rel_tolerance(0.1, 0.1), false); + } + + #[test] + fn least_squares_method_gas_cost_time_below_ns() { + let xs = [1, 2, 3]; + + let ys = [ + GasCost::new_time_based(Ratio::new(11, 10)), + GasCost::new_time_based(Ratio::new(12, 10)), + GasCost::new_time_based(Ratio::new(13, 10)), + ]; + + let expected = Ok((GasCost::new_time_based(1), GasCost::new_time_based(Ratio::new(1, 10)))); + check_least_squares_method_gas_cost_pos_neg(&xs, &ys, expected); + } + + #[test] + fn least_squares_method_gas_cost_time_neg_base() { + let xs = [10, 20, 30]; + + let ys = [ + GasCost::new_time_based(9_950), + GasCost::new_time_based(19_950), + GasCost::new_time_based(29_950), + ]; + + let expected = Err(( + (GasCost::new_time_based(0), GasCost::new_time_based(1000)), + (GasCost::new_time_based(50), GasCost::new_time_based(0)), + )); + check_least_squares_method_gas_cost_pos_neg(&xs, &ys, expected); + + check_uncertainty(&xs, &ys, Default::default(), true); + check_uncertainty(&xs, &ys, abs_tolerance(1, 1), true); + check_uncertainty(&xs, &ys, abs_tolerance((GAS_IN_NS * 50).to_integer(), 1), false); + check_uncertainty(&xs, &ys, rel_tolerance(0.1, 0.1), true); + } + + #[test] + fn least_squares_method_gas_cost_time_neg_factor() { + let xs = [10, 20, 30]; + + let ys = [ + GasCost::new_time_based(990), + GasCost::new_time_based(980), + GasCost::new_time_based(970), + ]; + + let expected = Err(( + (GasCost::new_time_based(1000), GasCost::new_time_based(0)), + (GasCost::new_time_based(0), GasCost::new_time_based(1)), + )); + check_least_squares_method_gas_cost_pos_neg(&xs, &ys, expected); + + check_uncertainty(&xs, &ys, Default::default(), true); + check_uncertainty(&xs, &ys, abs_tolerance(1, 1), true); + check_uncertainty(&xs, &ys, abs_tolerance(1, 1_000_000), false); + check_uncertainty(&xs, &ys, rel_tolerance(0.1, 0.1), true); + } + + #[test] + fn least_squares_method_gas_cost_icount_ok() { + let xs = [10, 20, 30]; + + let ys = [ + GasCost::new_icount_based(10_050, 20_060, 30_070), + GasCost::new_icount_based(20_050, 40_060, 60_070), + GasCost::new_icount_based(30_050, 60_060, 90_070), + ]; + + let expected = Ok(( + GasCost::new_icount_based(50, 60, 70), + GasCost::new_icount_based(1000, 2000, 3000), + )); + check_least_squares_method_gas_cost_pos_neg(&xs, &ys, expected); + + check_uncertainty(&xs, &ys, Default::default(), false); + check_uncertainty(&xs, &ys, abs_tolerance(1, 1), false); + check_uncertainty(&xs, &ys, rel_tolerance(0.1, 0.1), false); + } + + #[test] + fn least_squares_method_gas_cost_icount_neg_base() { + let xs = [10, 20, 30]; + + let ys = [ + GasCost::new_icount_based(9_950, 19_960, 29_970), + GasCost::new_icount_based(19_950, 39_960, 59_970), + GasCost::new_icount_based(29_950, 59_960, 89_970), + ]; + + let expected = Err(( + (GasCost::new_icount_based(0, 0, 0), GasCost::new_icount_based(1000, 2000, 3000)), + (GasCost::new_icount_based(50, 40, 30), GasCost::new_icount_based(0, 0, 0)), + )); + check_least_squares_method_gas_cost_pos_neg(&xs, &ys, expected); + + check_uncertainty(&xs, &ys, Default::default(), true); + check_uncertainty(&xs, &ys, abs_tolerance(1, 1), true); + check_uncertainty(&xs, &ys, rel_tolerance(0.1, 0.1), true); + check_uncertainty( + &xs, + &ys, + abs_tolerance( + (GAS_IN_INSTR * 50 + IO_READ_BYTE_COST * 40 + IO_WRITE_BYTE_COST * 30) + .ceil() + .to_integer(), + 0, + ), + false, + ); + } + + #[test] + fn least_squares_method_gas_cost_icount_neg_factor() { + let xs = [10, 20, 30]; + + let ys = [ + GasCost::new_icount_based(990, 981, 972), + GasCost::new_icount_based(980, 961, 942), + GasCost::new_icount_based(970, 941, 912), + ]; + + let expected = Err(( + (GasCost::new_icount_based(1000, 1001, 1002), GasCost::new_icount_based(0, 0, 0)), + (GasCost::new_icount_based(0, 0, 0), GasCost::new_icount_based(1, 2, 3)), + )); + check_least_squares_method_gas_cost_pos_neg(&xs, &ys, expected); + + check_uncertainty(&xs, &ys, Default::default(), true); + check_uncertainty(&xs, &ys, abs_tolerance(1, 1), true); + check_uncertainty(&xs, &ys, rel_tolerance(0.1, 0.1), true); + check_uncertainty( + &xs, + &ys, + abs_tolerance( + 0, + (GAS_IN_INSTR * 1 + IO_READ_BYTE_COST * 2 + IO_WRITE_BYTE_COST * 3) + .ceil() + .to_integer(), + ), + false, + ); + } + + #[test] + fn least_squares_method_gas_cost_icount_mixed_neg_pos() { + let xs = [10, 20, 30]; + + let ys = [ + GasCost::new_icount_based(990, 1010, 0), + GasCost::new_icount_based(980, 1020, 10), + GasCost::new_icount_based(970, 1030, 20), + ]; + + let expected = Err(( + (GasCost::new_icount_based(1000, 1000, 0), GasCost::new_icount_based(0, 1, 1)), + (GasCost::new_icount_based(0, 0, 10), GasCost::new_icount_based(1, 0, 0)), + )); + check_least_squares_method_gas_cost_pos_neg(&xs, &ys, expected); + + check_uncertainty(&xs, &ys, Default::default(), true); + check_uncertainty(&xs, &ys, abs_tolerance(1, 1), true); + check_uncertainty( + &xs, + &ys, + abs_tolerance( + (IO_WRITE_BYTE_COST * 10).ceil().to_integer(), + (GAS_IN_INSTR * 1).ceil().to_integer(), + ), + false, + ); + + if IO_READ_BYTE_COST + IO_WRITE_BYTE_COST == Ratio::zero() || GAS_IN_INSTR == Ratio::zero() + { + // Relative tolerance only makes sense if two different scalars of the vector cost can be non-zero. + // Otherwise, one of the (pos,neg) pair has to be 0. + return; + } + + // Compute relative thresholds based on estimator params + let rel_base = (IO_WRITE_BYTE_COST * 10) / (GAS_IN_INSTR * 1000 + IO_READ_BYTE_COST * 1000); + let rel_factor = (GAS_IN_INSTR * 1) / (IO_READ_BYTE_COST * 1 + IO_WRITE_BYTE_COST * 1); + check_uncertainty( + &xs, + &ys, + rel_tolerance(rel_base.to_f64().unwrap() * 1.1, rel_factor.to_f64().unwrap() * 1.1), + false, + ); + check_uncertainty( + &xs, + &ys, + rel_tolerance(rel_base.to_f64().unwrap() * 0.9, rel_factor.to_f64().unwrap() * 1.1), + true, + ); + check_uncertainty( + &xs, + &ys, + rel_tolerance(rel_base.to_f64().unwrap() * 1.1, rel_factor.to_f64().unwrap() * 0.9), + true, + ); + } +} diff --git a/runtime/runtime-params-estimator/src/gas_metering.rs b/runtime/runtime-params-estimator/src/gas_metering.rs new file mode 100644 index 000000000..cda99331f --- /dev/null +++ b/runtime/runtime-params-estimator/src/gas_metering.rs @@ -0,0 +1,228 @@ +use crate::config::Config; +use crate::gas_cost::{GasCost, LeastSquaresTolerance}; +use crate::vm_estimator::create_context; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_store::StoreCompiledContractCache; +use unc_vm_runner::internal::VMKindExt; +use unc_vm_runner::logic::mocks::mock_external::MockedExternal; +use unc_vm_runner::logic::CompiledContractCache; +use unc_vm_runner::ContractCode; +use std::fmt::Write; + +pub(crate) fn gas_metering_cost(config: &Config) -> (GasCost, GasCost) { + let mut xs1 = vec![]; + let mut ys1 = vec![]; + let mut xs2 = vec![]; + let mut ys2 = vec![]; + for depth in [1, 10, 20, 30, 50, 100, 200, 1000] { + { + // Here we test gas metering costs for forward branch cases. + let nested_contract = make_deeply_nested_blocks_contact(depth); + let cost = compute_gas_metering_cost(config, &nested_contract); + xs1.push(depth as u64); + ys1.push(cost); + } + { + let loop_contract = make_simple_loop_contact(depth); + let cost = compute_gas_metering_cost(config, &loop_contract); + xs2.push(depth as u64); + ys2.push(cost); + } + } + + let tolerance = LeastSquaresTolerance::default().factor_rel_nn_tolerance(0.001); + let (cost1_base, cost1_op) = + GasCost::least_squares_method_gas_cost(&xs1, &ys1, &tolerance, config.debug); + let (cost2_base, cost2_op) = + GasCost::least_squares_method_gas_cost(&xs2, &ys2, &tolerance, config.debug); + + let cost_base = std::cmp::max(cost1_base, cost2_base); + let cost_op = std::cmp::max(cost1_op, cost2_op); + (cost_base, cost_op) +} + +fn make_deeply_nested_blocks_contact(depth: i32) -> ContractCode { + // Build nested blocks structure. + let mut blocks = String::new(); + for _ in 0..depth { + write!( + &mut blocks, + " + block + " + ) + .unwrap(); + } + // Conditional branch forces 1 gas metering injection per block. + for _ in 0..depth { + write!( + &mut blocks, + " + local.get 0 + i32.const 2 + i32.gt_s + br_if 0 + local.get 0 + drop + end + " + ) + .unwrap(); + } + + let code = format!( + " + (module + (export \"hello\" (func 0)) + (func (;0;) + (local i32) + i32.const 1 + local.set 0 + {} + return + ) + )", + blocks + ); + ContractCode::new(wat::parse_str(code).unwrap(), None) +} + +fn make_simple_loop_contact(depth: i32) -> ContractCode { + let code = format!( + " + (module + (export \"hello\" (func 0)) + (func (;0;) + (local i32) + i32.const {} + local.set 0 + block + loop + local.get 0 + i32.const 1 + i32.sub + local.tee 0 + i32.const 0 + i32.gt_s + br_if 0 + br 1 + end + end + ) + )", + depth + ); + ContractCode::new(wat::parse_str(code).unwrap(), None) +} + +/** + * We compute the cost of gas metering operations in forward and backward branching contracts by + * running contracts with and without gas metering and comparing the difference induced by gas + * metering. + */ +pub(crate) fn compute_gas_metering_cost(config: &Config, contract: &ContractCode) -> GasCost { + let gas_metric = config.metric; + let repeats = config.iter_per_block as u64; + let vm_kind = config.vm_kind; + let warmup_repeats = config.warmup_iters_per_block; + + let store = unc_store::test_utils::create_test_store(); + let cache_store = StoreCompiledContractCache::new(&store); + let cache: Option<&dyn CompiledContractCache> = Some(&cache_store); + let config_store = RuntimeConfigStore::new(None); + let runtime_config = config_store.get_config(PROTOCOL_VERSION).as_ref(); + let vm_config_gas = runtime_config.wasm_config.clone(); + let vm_config_free = { + let mut cfg = vm_config_gas.clone(); + cfg.make_free(); + cfg + }; + let runtime = vm_kind.runtime(vm_config_gas).expect("runtime has not been enabled"); + let runtime_free_gas = vm_kind.runtime(vm_config_free).expect("runtime has not been enabled"); + let fees = runtime_config.fees.clone(); + let mut fake_external = MockedExternal::new(); + let fake_context = create_context(vec![]); + let promise_results = vec![]; + + // Warmup with gas metering + for _ in 0..warmup_repeats { + let result = runtime + .run( + contract, + "hello", + &mut fake_external, + fake_context.clone(), + &fees, + &promise_results, + cache, + ) + .expect("fatal_error"); + if let Some(err) = &result.aborted { + eprintln!("error: {}", err); + } + assert!(result.aborted.is_none()); + } + + // Run with gas metering. + let start = GasCost::measure(gas_metric); + for _ in 0..repeats { + let result = runtime + .run( + contract, + "hello", + &mut fake_external, + fake_context.clone(), + &fees, + &promise_results, + cache, + ) + .expect("fatal_error"); + assert!(result.aborted.is_none()); + } + let total_raw_with_gas = start.elapsed(); + + // Warmup without gas metering + for _ in 0..warmup_repeats { + let result = runtime_free_gas + .run( + contract, + "hello", + &mut fake_external, + fake_context.clone(), + &fees, + &promise_results, + cache, + ) + .expect("fatal_error"); + assert!(result.aborted.is_none()); + } + + // Run without gas metering. + let start = GasCost::measure(gas_metric); + for _ in 0..repeats { + let result = runtime_free_gas + .run( + contract, + "hello", + &mut fake_external, + fake_context.clone(), + &fees, + &promise_results, + cache, + ) + .expect("fatal_error"); + assert!(result.aborted.is_none()); + } + let total_raw_no_gas = start.elapsed(); + + if total_raw_with_gas < total_raw_no_gas { + // This might happen due to experimental error, especially when running + // without warmup or too few iterations. + let mut null_cost = GasCost::zero(); + null_cost.set_uncertain("NEGATIVE-COST"); + return null_cost; + } + + (total_raw_with_gas - total_raw_no_gas) / repeats +} diff --git a/runtime/runtime-params-estimator/src/least_squares.rs b/runtime/runtime-params-estimator/src/least_squares.rs new file mode 100644 index 000000000..c1cf76ae6 --- /dev/null +++ b/runtime/runtime-params-estimator/src/least_squares.rs @@ -0,0 +1,166 @@ +use crate::estimator_params::GAS_IN_NS; +use crate::qemu::QemuMeasurement; +use num_rational::Ratio; +use num_traits::ToPrimitive; + +pub(crate) fn least_squares_method( + xs: &[u64], + ys: &[u64], +) -> (Ratio, Ratio, Vec) { + let n = xs.len(); + let n128 = n as i128; + + let mut sum_prod = 0 as i128; // Sum of x * y. + for i in 0..n { + sum_prod = sum_prod + (xs[i] as i128) * (ys[i] as i128); + } + let mut sum_x = 0 as i128; // Sum of x. + for i in 0..n { + sum_x = sum_x + (xs[i] as i128); + } + let mut sum_y = 0 as i128; // Sum of y. + for i in 0..n { + sum_y = sum_y + (ys[i] as i128); + } + let mut sum_x_square = 0 as i128; // Sum of x^2. + for i in 0..n { + sum_x_square = sum_x_square + (xs[i] as i128) * (xs[i] as i128); + } + let b = Ratio::new(n128 * sum_prod - sum_x * sum_y, n128 * sum_x_square - sum_x * sum_x); + let a = Ratio::new(sum_y * b.denom() - b.numer() * sum_x, n128 * b.denom()); + + // Compute error estimations + let mut errs = vec![]; + for i in 0..n { + let expect = a + b * (xs[i] as i128); + let diff = expect - (ys[i] as i128); + errs.push(diff.round().to_integer()); + } + + (a, b, errs) +} + +pub(crate) fn time_measurement_least_squares( + xs: &[u64], + ys: &[Ratio], +) -> ((Ratio, Ratio), (Ratio, Ratio)) { + let t = least_squares_method( + xs, + &ys.iter().map(|ns| (ns * GAS_IN_NS).to_integer()).collect::>(), + ); + let (pos_t_base, neg_t_base) = split_pos_neg(t.0); + let (pos_t_factor, neg_t_factor) = split_pos_neg(t.1); + + let neg_base = neg_t_base / GAS_IN_NS; + let neg_factor = neg_t_factor / GAS_IN_NS; + let pos_base = pos_t_base / GAS_IN_NS; + let pos_factor = pos_t_factor / GAS_IN_NS; + + ((pos_base, pos_factor), (neg_base, neg_factor)) +} + +pub(crate) fn qemu_measurement_least_squares( + xs: &[u64], + ys: &[QemuMeasurement], +) -> ((QemuMeasurement, QemuMeasurement), (QemuMeasurement, QemuMeasurement)) { + let i = least_squares_method( + xs, + &ys.iter().map(|q| q.instructions.to_integer()).collect::>(), + ); + let r = + least_squares_method(xs, &ys.iter().map(|q| q.io_r_bytes.to_integer()).collect::>()); + let w = + least_squares_method(xs, &ys.iter().map(|q| q.io_w_bytes.to_integer()).collect::>()); + + let (pos_i_base, neg_i_base) = split_pos_neg(i.0); + let (pos_r_base, neg_r_base) = split_pos_neg(r.0); + let (pos_w_base, neg_w_base) = split_pos_neg(w.0); + + let (pos_i_factor, neg_i_factor) = split_pos_neg(i.1); + let (pos_r_factor, neg_r_factor) = split_pos_neg(r.1); + let (pos_w_factor, neg_w_factor) = split_pos_neg(w.1); + + let neg_base = QemuMeasurement { + instructions: neg_i_base, + io_r_bytes: neg_r_base, + io_w_bytes: neg_w_base, + }; + let neg_factor = QemuMeasurement { + instructions: neg_i_factor, + io_r_bytes: neg_r_factor, + io_w_bytes: neg_w_factor, + }; + let pos_base = QemuMeasurement { + instructions: pos_i_base, + io_r_bytes: pos_r_base, + io_w_bytes: pos_w_base, + }; + let pos_factor = QemuMeasurement { + instructions: pos_i_factor, + io_r_bytes: pos_r_factor, + io_w_bytes: pos_w_factor, + }; + ((pos_base, pos_factor), (neg_base, neg_factor)) +} + +/// Transforms input C into two components, where A,B are non-negative and where A-B ~= input. +/// This method intentionally rounds fractions to whole integers, rounding towards zero. +fn split_pos_neg(num: Ratio) -> (Ratio, Ratio) { + let pos = num.to_integer().to_u64().unwrap_or_default().into(); + let neg = (-num).to_integer().to_u64().unwrap_or_default().into(); + (pos, neg) +} + +#[cfg(test)] +mod tests { + use num_traits::ToPrimitive; + + use super::*; + + #[track_caller] + fn check_least_squares_method(xs: &[u64], ys: &[u64], expected: (f64, f64, &[f64])) { + let (a, b, r) = least_squares_method(&xs, &ys); + + assert_eq!(a.to_f64().unwrap(), expected.0); + assert_eq!(b.to_f64().unwrap(), expected.1); + for (&expected, &actual) in expected.2.iter().zip(r.iter()) { + assert_eq!(actual, expected.round().to_i128().unwrap()); + } + } + + #[test] + fn test_least_squares_method_perfect_fit() { + let xs = [10, 20, 30]; + let ys = [990, 980, 970]; + + let a = 1000.0f64; + let b = -1.0f64; + let error = [0.0, 0.0, 0.0]; + + check_least_squares_method(&xs, &ys, (a, b, &error)); + } + + #[test] + fn test_least_squares_method_imperfect_fit() { + let xs = [10, 1000, 2000]; + let ys = [1, 101, 198]; + + let a = 0.6784115012962526; + let b = 0.09899161644389078; + let error = [0.668327661, -1.329972499, 0.661643501]; + + check_least_squares_method(&xs, &ys, (a, b, &error)); + } + + #[test] + fn test_large_numbers() { + let xs = [1, 1000000, 4000000]; + let ys = [5663378466666, 5718530400000, 6235718400000]; + + let a = 5622793876267.89; + let b = 149849.09760264654; + let error = [-40584440549.0127, 54112573870.5361, -13528133321.5244]; + + check_least_squares_method(&xs, &ys, (a, b, &error)); + } +} diff --git a/runtime/runtime-params-estimator/src/lib.rs b/runtime/runtime-params-estimator/src/lib.rs new file mode 100644 index 000000000..c80ad793b --- /dev/null +++ b/runtime/runtime-params-estimator/src/lib.rs @@ -0,0 +1,1286 @@ +//! Code to estimate each specific cost. +//! +//! Each cost is registered in `ALL_COSTS` array, together with a +//! cost-estimation function. Function can be arbitrary. Most, but not all, +//! costs are estimated using roughly the following algorithm: +//! +//! * Create an instance of near starting with specific fixture with many +//! accounts deployed. +//! * Create a template transaction, like transfer from a to b. +//! * Create a bunch of blocks stuffed with copies of this template +//! transaction. +//! * Measure the "time" it takes to process the blocks (including all +//! generated receipts) +//! * Divide the total time by number of blocks * number of transactions in a +//! block. +//! +//! Some common variations: +//! +//! * As we measure "the whole thing", we deduct base costs from composite +//! measurements. For example, when estimating cost of transfer action we +//! deduct the cost of an empty transaction, as that is accounted for +//! separately. +//! * For "per byte" costs, like `ActionDeployContractPerByte`, we estimate +//! the time required for a "large" input, for an empty input, and divide +//! the difference by the number of bytes. As an alternative, least squares +//! method is used for cases where we have several interesting inputs with +//! different sizes. +//! * For host functions, use few blocks of transactions. Instead, each +//! transaction calls host function from `wasm` in a loop. +//! * Some costs are measured more directly. For example, to measure cost of +//! wasm opcode we call vm_runner directly, bypassing the rest of runtime +//! machinery. +//! * Some RocksDB related estimations avoid framework entirely and run on +//! completely independent database instances. This DB is controlled by the +//! `rdb-` prefixed flags which are combined in `RocksDBTestConfig`. +//! +//! Some costs depend on each other. As we want to allow estimating a subset of +//! costs and don't want to just run everything in order (as that would be to +//! slow), we have a very simple manual caching infrastructure in place. +//! +//! To run estimations on a non-empty DB with standardised content, we first +//! dump all records to a `StateDump` written to a file. Then for each +//! iteration of a an estimation, we first load the records from this dump into +//! a fresh database. Afterwards, it is crucial to run compaction on RocksDB +//! before starting measurements. Otherwise, the SST file layout can be very +//! inefficient, as there was no time to restructure them. We assume that in +//! production, the inflow of new data is not as bulky and therefore it should +//! always be reasonably compacted. Also, without forcing it before +//! measurements start, compaction may start during the measurement and makes +//! the results unstable. +//! +//! Notes on code architecture: +//! +//! To keep estimations comprehensible, each estimation has a simple function +//! here in the top-level module. Calls to code in submodules should have a +//! descriptive function names so that it is obvious what it does without +//! digging deeper. +//! + +mod action_costs; +mod cost; +mod cost_table; +mod costs_to_runtime_config; +// Encapsulates the runtime so that it can be run separately from the rest of the node. +mod estimator_context; +mod gas_cost; +mod qemu; +mod rocksdb; +mod transaction_builder; + +pub(crate) mod estimator_params; +pub(crate) mod least_squares; + +// Helper functions shared between modules +pub mod utils; + +// Runs a VM (Default: Wasmer) on the given contract and measures the time it takes to do a single operation. +pub mod vm_estimator; +// Prepares transactions and feeds them to the testbed in batches. Performs the warm up, takes care +// of nonces. +pub mod config; +mod function_call; +mod gas_metering; +mod trie; + +use crate::config::Config; +pub use crate::cost::Cost; +use crate::cost_table::format_gas; +pub use crate::cost_table::CostTable; +pub use crate::costs_to_runtime_config::costs_to_runtime_config; +use crate::estimator_context::EstimatorContext; +use crate::gas_cost::GasCost; +pub use crate::qemu::QemuCommandBuilder; +pub use crate::rocksdb::RocksDBTestConfig; +use crate::rocksdb::{rocks_db_inserts_cost, rocks_db_read_cost}; +use crate::transaction_builder::TransactionBuilder; +use crate::vm_estimator::create_context; +use estimator_params::sha256_cost; +use gas_cost::{LeastSquaresTolerance, NonNegativeTolerance}; +use gas_metering::gas_metering_cost; +use unc_crypto::{KeyType, SecretKey}; +use unc_parameters::{ExtCosts, RuntimeConfigStore, RuntimeFeesConfig}; +use unc_primitives::account::{AccessKey, AccessKeyPermission, FunctionCallPermission}; +use unc_primitives::transaction::{ + Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, + DeployContractAction, SignedTransaction, StakeAction, TransferAction, +}; +use unc_primitives::types::AccountId; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_vm_runner::internal::VMKindExt; +use unc_vm_runner::logic::mocks::mock_external::MockedExternal; +use unc_vm_runner::ContractCode; +use unc_vm_runner::MockCompiledContractCache; +use serde_json::json; +use std::convert::TryFrom; +use std::iter; +use std::time::Instant; +use utils::{ + average_cost, fn_cost, fn_cost_count, fn_cost_in_contract, fn_cost_with_setup, + generate_data_only_contract, generate_fn_name, noop_function_call_cost, read_resource, + transaction_cost, transaction_cost_ext, +}; +use vm_estimator::{compile_single_contract_cost, compute_compile_cost_vm}; + +static ALL_COSTS: &[(Cost, fn(&mut EstimatorContext) -> GasCost)] = &[ + (Cost::ActionReceiptCreation, action_receipt_creation), + (Cost::ActionSirReceiptCreation, action_sir_receipt_creation), + (Cost::ActionReceiptCreationSendSir, action_costs::new_action_receipt_send_sir), + (Cost::ActionReceiptCreationSendNotSir, action_costs::new_action_receipt_send_not_sir), + (Cost::ActionReceiptCreationExec, action_costs::new_action_receipt_exec), + (Cost::ActionTransfer, action_transfer), + (Cost::ActionTransferSendSir, action_costs::transfer_send_sir), + (Cost::ActionTransferSendNotSir, action_costs::transfer_send_not_sir), + (Cost::ActionTransferExec, action_costs::transfer_exec), + (Cost::ActionCreateAccount, action_create_account), + (Cost::ActionCreateAccountSendSir, action_costs::create_account_send_sir), + (Cost::ActionCreateAccountSendNotSir, action_costs::create_account_send_not_sir), + (Cost::ActionCreateAccountExec, action_costs::create_account_exec), + (Cost::ActionDeleteAccount, action_delete_account), + (Cost::ActionDeleteAccountSendSir, action_costs::delete_account_send_sir), + (Cost::ActionDeleteAccountSendNotSir, action_costs::delete_account_send_not_sir), + (Cost::ActionDeleteAccountExec, action_costs::delete_account_exec), + (Cost::ActionAddFullAccessKey, action_add_full_access_key), + (Cost::ActionAddFullAccessKeySendSir, action_costs::add_full_access_key_send_sir), + (Cost::ActionAddFullAccessKeySendNotSir, action_costs::add_full_access_key_send_not_sir), + (Cost::ActionAddFullAccessKeyExec, action_costs::add_full_access_key_exec), + (Cost::ActionAddFunctionAccessKeyBase, action_add_function_access_key_base), + ( + Cost::ActionAddFunctionAccessKeyBaseSendSir, + action_costs::add_function_call_key_base_send_sir, + ), + ( + Cost::ActionAddFunctionAccessKeyBaseSendNotSir, + action_costs::add_function_call_key_base_send_not_sir, + ), + (Cost::ActionAddFunctionAccessKeyBaseExec, action_costs::add_function_call_key_base_exec), + (Cost::ActionAddFunctionAccessKeyPerByte, action_add_function_access_key_per_byte), + ( + Cost::ActionAddFunctionAccessKeyPerByteSendSir, + action_costs::add_function_call_key_byte_send_sir, + ), + ( + Cost::ActionAddFunctionAccessKeyPerByteSendNotSir, + action_costs::add_function_call_key_byte_send_not_sir, + ), + (Cost::ActionAddFunctionAccessKeyPerByteExec, action_costs::add_function_call_key_byte_exec), + (Cost::ActionDeleteKey, action_delete_key), + (Cost::ActionDeleteKeySendSir, action_costs::delete_key_send_sir), + (Cost::ActionDeleteKeySendNotSir, action_costs::delete_key_send_not_sir), + (Cost::ActionDeleteKeyExec, action_costs::delete_key_exec), + (Cost::ActionStake, action_stake), + (Cost::ActionStakeSendNotSir, action_costs::stake_send_not_sir), + (Cost::ActionStakeSendSir, action_costs::stake_send_sir), + (Cost::ActionStakeExec, action_costs::stake_exec), + (Cost::ActionDeployContractBase, action_deploy_contract_base), + (Cost::ActionDeployContractBaseSendNotSir, action_costs::deploy_contract_base_send_not_sir), + (Cost::ActionDeployContractBaseSendSir, action_costs::deploy_contract_base_send_sir), + (Cost::ActionDeployContractBaseExec, action_costs::deploy_contract_base_exec), + (Cost::ActionDeployContractPerByte, action_deploy_contract_per_byte), + (Cost::ActionDeployContractPerByteSendNotSir, action_costs::deploy_contract_byte_send_not_sir), + (Cost::ActionDeployContractPerByteSendSir, action_costs::deploy_contract_byte_send_sir), + (Cost::ActionDeployContractPerByteExec, action_costs::deploy_contract_byte_exec), + (Cost::ActionFunctionCallBase, action_function_call_base), + (Cost::ActionFunctionCallBaseSendNotSir, action_costs::function_call_base_send_not_sir), + (Cost::ActionFunctionCallBaseSendSir, action_costs::function_call_base_send_sir), + (Cost::ActionFunctionCallBaseExec, action_costs::function_call_base_exec), + (Cost::ActionFunctionCallPerByte, action_function_call_per_byte), + (Cost::ActionFunctionCallPerByteSendNotSir, action_costs::function_call_byte_send_not_sir), + (Cost::ActionFunctionCallPerByteSendSir, action_costs::function_call_byte_send_sir), + (Cost::ActionFunctionCallPerByteExec, action_costs::function_call_byte_exec), + (Cost::ActionDelegate, action_delegate_base), + (Cost::ActionDelegateSendNotSir, action_costs::delegate_send_not_sir), + (Cost::ActionDelegateSendSir, action_costs::delegate_send_sir), + (Cost::ActionDelegateExec, action_costs::delegate_exec), + (Cost::HostFunctionCall, host_function_call), + (Cost::WasmInstruction, wasm_instruction), + (Cost::DataReceiptCreationBase, data_receipt_creation_base), + (Cost::DataReceiptCreationPerByte, data_receipt_creation_per_byte), + (Cost::ReadMemoryBase, read_memory_base), + (Cost::ReadMemoryByte, read_memory_byte), + (Cost::WriteMemoryBase, write_memory_base), + (Cost::WriteMemoryByte, write_memory_byte), + (Cost::ReadRegisterBase, read_register_base), + (Cost::ReadRegisterByte, read_register_byte), + (Cost::WriteRegisterBase, write_register_base), + (Cost::WriteRegisterByte, write_register_byte), + (Cost::LogBase, log_base), + (Cost::LogByte, log_byte), + (Cost::Utf8DecodingBase, utf8_decoding_base), + (Cost::Utf8DecodingByte, utf8_decoding_byte), + (Cost::Utf16DecodingBase, utf16_decoding_base), + (Cost::Utf16DecodingByte, utf16_decoding_byte), + (Cost::Sha256Base, sha256_base), + (Cost::Sha256Byte, sha256_byte), + (Cost::Keccak256Base, keccak256_base), + (Cost::Keccak256Byte, keccak256_byte), + (Cost::Keccak512Base, keccak512_base), + (Cost::Keccak512Byte, keccak512_byte), + (Cost::Ripemd160Base, ripemd160_base), + (Cost::Ripemd160Block, ripemd160_block), + (Cost::EcrecoverBase, ecrecover_base), + (Cost::Ed25519VerifyBase, ed25519_verify_base), + (Cost::Ed25519VerifyByte, ed25519_verify_byte), + (Cost::AltBn128G1MultiexpBase, alt_bn128g1_multiexp_base), + (Cost::AltBn128G1MultiexpElement, alt_bn128g1_multiexp_element), + (Cost::AltBn128G1SumBase, alt_bn128g1_sum_base), + (Cost::AltBn128G1SumElement, alt_bn128g1_sum_element), + (Cost::AltBn128PairingCheckBase, alt_bn128_pairing_check_base), + (Cost::AltBn128PairingCheckElement, alt_bn128_pairing_check_element), + (Cost::StorageHasKeyBase, storage_has_key_base), + (Cost::StorageHasKeyByte, storage_has_key_byte), + (Cost::StorageReadBase, storage_read_base), + (Cost::StorageReadKeyByte, storage_read_key_byte), + (Cost::StorageReadValueByte, storage_read_value_byte), + (Cost::StorageWriteBase, storage_write_base), + (Cost::StorageWriteKeyByte, storage_write_key_byte), + (Cost::StorageWriteValueByte, storage_write_value_byte), + (Cost::StorageWriteEvictedByte, storage_write_evicted_byte), + (Cost::StorageRemoveBase, storage_remove_base), + (Cost::StorageRemoveKeyByte, storage_remove_key_byte), + (Cost::StorageRemoveRetValueByte, storage_remove_ret_value_byte), + (Cost::TouchingTrieNode, touching_trie_node), + (Cost::ReadCachedTrieNode, read_cached_trie_node), + (Cost::ApplyBlock, apply_block_cost), + (Cost::ContractCompileBase, contract_compile_base), + (Cost::ContractCompileBytes, contract_compile_bytes), + (Cost::ContractCompileBaseV2, contract_compile_base_v2), + (Cost::ContractCompileBytesV2, contract_compile_bytes_v2), + (Cost::DeployBytes, pure_deploy_bytes), + (Cost::ContractLoadingBase, contract_loading_base), + (Cost::ContractLoadingPerByte, contract_loading_per_byte), + (Cost::FunctionCallPerStorageByte, function_call_per_storage_byte), + (Cost::GasMeteringBase, gas_metering_base), + (Cost::GasMeteringOp, gas_metering_op), + (Cost::RocksDbInsertValueByte, rocks_db_insert_value_byte), + (Cost::RocksDbReadValueByte, rocks_db_read_value_byte), + (Cost::CpuBenchmarkSha256, cpu_benchmark_sha256), + (Cost::OneCPUInstruction, one_cpu_instruction), + (Cost::OneNanosecond, one_nanosecond), +]; + +// We use core-contracts, e2f60b5b0930a9df2c413e1460e179c65c8876e3. +static REAL_CONTRACTS_SAMPLE: [(&str, &str); 4] = [ + // File 341191, code 279965, data 56627. + ("res/lockup_contract.wasm", "terminate_vesting"), + // File 257516, code 203545, data 50419. + ("res/staking_pool.wasm", "ping"), + // File 135358, code 113152, data 19520. + ("res/voting_contract.wasm", "ping"), + // File 124250, code 103473, data 18176. + ("res/whitelist.wasm", "add_staking_pool"), +]; + +pub fn run(config: Config) -> CostTable { + let mut ctx = EstimatorContext::new(&config); + let mut res = CostTable::default(); + + for (cost, f) in ALL_COSTS.iter().copied() { + if let Some(costs) = &ctx.config.costs_to_measure { + if !costs.contains(&cost) { + continue; + } + } + + let start = Instant::now(); + let measurement = f(&mut ctx); + let time = start.elapsed(); + let name = cost.to_string(); + let uncertain = if measurement.is_uncertain() { "UNCERTAIN " } else { "" }; + let gas = measurement.to_gas(); + res.add(cost, gas); + + eprintln!( + "{:<40} {:>25} gas [{:>25}] {:<10}(computed in {:.2?}) {}", + name, + format_gas(gas), + format!("{:?}", measurement), + uncertain, + time, + measurement.uncertain_message().unwrap_or_default(), + ); + + if config.json_output { + let json = json! ({ + "name": name, + "result": measurement.to_json(), + "computed_in": time, + }); + println!("{json}"); + } + } + eprintln!(); + + res +} + +fn action_receipt_creation(ctx: &mut EstimatorContext) -> GasCost { + if let Some(cached) = ctx.cached.action_receipt_creation.clone() { + return cached; + } + + let mut make_transaction = |tb: &mut TransactionBuilder| -> SignedTransaction { + let (sender, receiver) = tb.random_account_pair(); + + tb.transaction_from_actions(sender, receiver, vec![]) + }; + let block_size = 100; + // Sender != Receiver means this will be executed over two blocks. + let block_latency = 1; + let cost = transaction_cost_ext(ctx, block_size, &mut make_transaction, block_latency).0; + + ctx.cached.action_receipt_creation = Some(cost.clone()); + cost +} + +fn action_sir_receipt_creation(ctx: &mut EstimatorContext) -> GasCost { + if let Some(cached) = ctx.cached.action_sir_receipt_creation.clone() { + return cached; + } + + let mut make_transaction = |tb: &mut TransactionBuilder| -> SignedTransaction { + let sender = tb.random_account(); + let receiver = sender.clone(); + + tb.transaction_from_actions(sender, receiver, vec![]) + }; + let cost = transaction_cost(ctx, &mut make_transaction); + + ctx.cached.action_sir_receipt_creation = Some(cost.clone()); + cost +} + +fn action_transfer(ctx: &mut EstimatorContext) -> GasCost { + let total_cost = { + let mut make_transaction = |tb: &mut TransactionBuilder| -> SignedTransaction { + let (sender, receiver) = tb.random_account_pair(); + + let actions = vec![Action::Transfer(TransferAction { deposit: 1 })]; + tb.transaction_from_actions(sender, receiver, actions) + }; + let block_size = 100; + // Transferring from one account to another may touch two shards, thus executes over two blocks. + let block_latency = 1; + transaction_cost_ext(ctx, block_size, &mut make_transaction, block_latency).0 + }; + + let base_cost = action_receipt_creation(ctx); + + total_cost.saturating_sub(&base_cost, &NonNegativeTolerance::PER_MILLE) +} + +fn action_create_account(ctx: &mut EstimatorContext) -> GasCost { + let total_cost = { + let mut make_transaction = |tb: &mut TransactionBuilder| -> SignedTransaction { + let sender = tb.random_unused_account(); + // derive a non-existing account id + let new_account = AccountId::try_from(format!("{sender}_x")).unwrap(); + + let actions = vec![ + Action::CreateAccount(CreateAccountAction {}), + Action::Transfer(TransferAction { deposit: 10u128.pow(26) }), + ]; + tb.transaction_from_actions(sender, new_account, actions) + }; + let block_size = 100; + // Creating a new account is initiated by an account that potentially is on a different shard. Thus, it executes over two blocks. + let block_latency = 1; + transaction_cost_ext(ctx, block_size, &mut make_transaction, block_latency).0 + }; + + let base_cost = action_receipt_creation(ctx); + + total_cost.saturating_sub(&base_cost, &NonNegativeTolerance::PER_MILLE) +} + +fn action_delete_account(ctx: &mut EstimatorContext) -> GasCost { + let total_cost = { + let mut make_transaction = |tb: &mut TransactionBuilder| -> SignedTransaction { + let sender = tb.random_unused_account(); + let receiver = sender.clone(); + let beneficiary_id = tb.random_unused_account(); + + let actions = vec![Action::DeleteAccount(DeleteAccountAction { beneficiary_id })]; + tb.transaction_from_actions(sender, receiver, actions) + }; + let block_size = 100; + // Deleting an account is initiated by an account that potentially is on a different shard. Thus, it executes over two blocks. + let block_latency = 1; + transaction_cost_ext(ctx, block_size, &mut make_transaction, block_latency).0 + }; + + let base_cost = action_sir_receipt_creation(ctx); + + total_cost.saturating_sub(&base_cost, &NonNegativeTolerance::PER_MILLE) +} + +fn action_add_full_access_key(ctx: &mut EstimatorContext) -> GasCost { + let total_cost = { + let mut make_transaction = |tb: &mut TransactionBuilder| -> SignedTransaction { + let sender = tb.random_unused_account(); + + add_key_transaction(tb, sender, AccessKeyPermission::FullAccess) + }; + transaction_cost(ctx, &mut make_transaction) + }; + + let base_cost = action_sir_receipt_creation(ctx); + + total_cost.saturating_sub(&base_cost, &NonNegativeTolerance::PER_MILLE) +} + +fn action_add_function_access_key_base(ctx: &mut EstimatorContext) -> GasCost { + if let Some(cost) = ctx.cached.action_add_function_access_key_base.clone() { + return cost; + } + + let total_cost = { + let mut make_transaction = |tb: &mut TransactionBuilder| -> SignedTransaction { + let sender = tb.random_unused_account(); + let receiver_id = tb.account(0).to_string(); + + let permission = AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(100), + receiver_id, + method_names: vec!["m".to_string()], + }); + add_key_transaction(tb, sender, permission) + }; + transaction_cost(ctx, &mut make_transaction) + }; + + let base_cost = action_sir_receipt_creation(ctx); + + let cost = total_cost.saturating_sub(&base_cost, &NonNegativeTolerance::PER_MILLE); + ctx.cached.action_add_function_access_key_base = Some(cost.clone()); + cost +} + +fn action_add_function_access_key_per_byte(ctx: &mut EstimatorContext) -> GasCost { + let base_cost = action_add_function_access_key_base(ctx) + action_sir_receipt_creation(ctx); + + // Set up estimation with varying method length and total bytes. + let mut estimate = |method_len: usize, total_len: usize| { + // Nothing prevents a key to list the same method many times. Performance should not be affected. + let method_name = "x".repeat(method_len); + let num_methods = total_len / (method_len + 1); + let method_names = vec![method_name; num_methods]; + + let mut make_transaction = |tb: &mut TransactionBuilder| -> SignedTransaction { + let sender = tb.random_unused_account(); + let receiver_id = tb.account(0).to_string(); + + let permission = AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(100), + receiver_id, + method_names: method_names.clone(), + }); + add_key_transaction(tb, sender, permission) + }; + + let total_cost = transaction_cost(ctx, &mut make_transaction); + // +1 for null-terminator + let actual_total_len = num_methods * (method_len + 1); + let per_byte_cost = total_cost.saturating_sub(&base_cost, &NonNegativeTolerance::PER_MILLE) + / actual_total_len as u64; + + if ctx.config.debug { + eprintln!("{num_methods}x{method_len}: {per_byte_cost:?}"); + } + per_byte_cost + }; + + // A single action can have up to 2kB bytes of comma-separated method names. + // As defined by the parameter `max_number_bytes_method_names`. + let max_bytes = 2_000; + // Methods name lengths are limited by the runtime parameter `max_length_method_name`. + let max_method_len = 256; + + // Try a couple of combinations that could potentially be the worst-case. + let cost_a = estimate(max_method_len, max_bytes); + let cost_b = estimate(1, max_bytes); // This is the worst at time of writing. + let cost_c = estimate(8, max_bytes); + let cost_d = estimate(max_method_len, max_method_len + 1); + let cost_e = estimate(max_method_len / 2, max_bytes / 2); + + [cost_a, cost_b, cost_c, cost_d, cost_e].into_iter().max().unwrap() +} + +fn add_key_transaction( + tb: &mut TransactionBuilder, + sender: AccountId, + permission: AccessKeyPermission, +) -> SignedTransaction { + let receiver = sender.clone(); + + let public_key = "ed25519:DcA2MzgpJbrUATQLLceocVckhhAqrkingax4oJ9kZ847".parse().unwrap(); + let access_key = AccessKey { nonce: 0, permission }; + + tb.transaction_from_actions( + sender, + receiver, + vec![Action::AddKey(Box::new(AddKeyAction { public_key, access_key }))], + ) +} + +fn action_delete_key(ctx: &mut EstimatorContext) -> GasCost { + let total_cost = { + let mut make_transaction = |tb: &mut TransactionBuilder| -> SignedTransaction { + let sender = tb.random_unused_account(); + let receiver = sender.clone(); + + let actions = vec![Action::DeleteKey(Box::new(DeleteKeyAction { + public_key: SecretKey::from_seed(KeyType::ED25519, sender.as_ref()).public_key(), + }))]; + tb.transaction_from_actions(sender, receiver, actions) + }; + transaction_cost(ctx, &mut make_transaction) + }; + + let base_cost = action_sir_receipt_creation(ctx); + + total_cost.saturating_sub(&base_cost, &NonNegativeTolerance::PER_MILLE) +} + +fn action_stake(ctx: &mut EstimatorContext) -> GasCost { + let total_cost = { + let mut make_transaction = |tb: &mut TransactionBuilder| -> SignedTransaction { + let sender = tb.random_unused_account(); + let receiver = sender.clone(); + + let actions = vec![Action::Stake(Box::new(StakeAction { + stake: 1, + public_key: "22skMptHjFWNyuEWY22ftn2AbLPSYpmYwGJRGwpNHbTV".parse().unwrap(), + }))]; + tb.transaction_from_actions(sender, receiver, actions) + }; + transaction_cost(ctx, &mut make_transaction) + }; + + let base_cost = action_sir_receipt_creation(ctx); + + total_cost.saturating_sub(&base_cost, &NonNegativeTolerance::PER_MILLE) +} + +fn action_deploy_contract_base(ctx: &mut EstimatorContext) -> GasCost { + if let Some(cost) = ctx.cached.deploy_contract_base.clone() { + return cost; + } + + let cost = { + let code = unc_test_contracts::smallest_rs_contract(); + deploy_contract_cost(ctx, code.to_vec(), Some(b"sum")) + }; + + ctx.cached.deploy_contract_base = Some(cost.clone()); + cost +} +fn action_deploy_contract_per_byte(ctx: &mut EstimatorContext) -> GasCost { + let mut xs = vec![]; + let mut ys = vec![]; + + for (contract, pivot_fn) in REAL_CONTRACTS_SAMPLE { + let code = read_resource(contract); + xs.push(code.len() as u64); + let cost = deploy_contract_cost(ctx, code, Some(pivot_fn.as_bytes())); + // The sampled contracts are about 80% code. Since the deployment cost + // is heavily dominated by compilation, we therefore use a multiplier of + // 5/4 to guess what a contract with 100% code would cost to deploy. + ys.push(cost * 5 / 4); + } + + // We do linear regression on a cost curve that is mostly flat for small + // contracts and steeper for larger contracts. Thus, the fitted linear + // function usually crosses the y-axis somewhere in the negative. The + // tolerance is chosen, quite arbitrarily, as a full base cost from protocol + // v50. Values further in the negative indicate that the estimation error is + // out of proportion. + let negative_base_tolerance = 369_531_500_000u64; + // For icount-based measurements, since we start compilation after the full + // contract is already loaded into memory, it is possible that IO costs per + // byte are essentially 0 and sometimes negative in the fitted curve. If + // this negative value is small enough, this can be tolerated and the + // parameter is clamped to 0 without marking the result as uncertain. + let rel_factor_tolerance = 0.001; + let (_base, per_byte) = GasCost::least_squares_method_gas_cost( + &xs, + &ys, + &LeastSquaresTolerance::default() + .base_abs_nn_tolerance(negative_base_tolerance) + .factor_rel_nn_tolerance(rel_factor_tolerance), + ctx.config.debug, + ); + per_byte +} + +/// Cost for deploying a specific contract. +/// +/// This function will run however many iterations of the transaction as has +/// been defined in the config. +/// To avoid hitting the contract code cache, a pivot function name can be +/// provided. This must be a name of an exported function in the WASM module. It +/// will be dynamically modified on every iteration to a unique name. This +/// ensures a different code hash every time, without logical changes to the +/// contract. +fn deploy_contract_cost( + ctx: &mut EstimatorContext, + code: Vec, + pivot_fn_name: Option<&[u8]>, +) -> GasCost { + let mut code_num = 0; + let mut code_factory = || { + let mut code = code.clone(); + if let Some(pivot_fn_name) = pivot_fn_name { + let unique_name = generate_fn_name(code_num, pivot_fn_name.len()); + code_num += 1; + + let start = + code.windows(pivot_fn_name.len()).position(|slice| slice == pivot_fn_name).unwrap(); + code[start..(start + pivot_fn_name.len())].copy_from_slice(&unique_name); + } + code + }; + + let mut make_transaction = |tb: &mut TransactionBuilder| -> SignedTransaction { + let sender = tb.random_unused_account(); + let receiver = sender.clone(); + + let actions = vec![Action::DeployContract(DeployContractAction { code: code_factory() })]; + tb.transaction_from_actions(sender, receiver, actions) + }; + // Use a small block size since deployments are gas heavy. + let block_size = 5; + let (total_cost, _ext) = transaction_cost_ext(ctx, block_size, &mut make_transaction, 0); + let base_cost = action_sir_receipt_creation(ctx); + + total_cost.saturating_sub(&base_cost, &NonNegativeTolerance::PER_MILLE) +} +fn contract_compile_base(ctx: &mut EstimatorContext) -> GasCost { + compilation_cost_base_per_byte(ctx).0 +} +fn contract_compile_bytes(ctx: &mut EstimatorContext) -> GasCost { + compilation_cost_base_per_byte(ctx).1 +} +fn compilation_cost_base_per_byte(ctx: &mut EstimatorContext) -> (GasCost, GasCost) { + if let Some(base_byte_cost) = ctx.cached.compile_cost_base_per_byte.clone() { + return base_byte_cost; + } + + let verbose = ctx.config.debug; + let base_byte_cost = compute_compile_cost_vm(ctx.config.metric, ctx.config.vm_kind, verbose); + + ctx.cached.compile_cost_base_per_byte = Some(base_byte_cost.clone()); + base_byte_cost +} +fn contract_compile_base_v2(ctx: &mut EstimatorContext) -> GasCost { + contract_compile_base_per_byte_v2(ctx).0 +} +fn contract_compile_bytes_v2(ctx: &mut EstimatorContext) -> GasCost { + contract_compile_base_per_byte_v2(ctx).1 +} +fn contract_compile_base_per_byte_v2(ctx: &mut EstimatorContext) -> (GasCost, GasCost) { + if let Some(costs) = ctx.cached.compile_cost_base_per_byte_v2.clone() { + return costs; + } + + let smallest_contract = unc_test_contracts::smallest_rs_contract(); + let smallest_cost = + compile_single_contract_cost(ctx.config.metric, ctx.config.vm_kind, smallest_contract); + let smallest_size = smallest_contract.len() as u64; + + let mut max_bytes_cost = GasCost::zero(); + for (contract, _) in REAL_CONTRACTS_SAMPLE { + let binary = read_resource(contract); + let cost = compile_single_contract_cost(ctx.config.metric, ctx.config.vm_kind, &binary); + let bytes_cost = cost.saturating_sub(&smallest_cost, &NonNegativeTolerance::PER_MILLE) + / (binary.len() as u64 - smallest_size); + max_bytes_cost = std::cmp::max(bytes_cost, max_bytes_cost); + } + + let base_cost = smallest_cost.saturating_sub( + &(max_bytes_cost.clone() * smallest_size), + &NonNegativeTolerance::PER_MILLE, + ); + let costs = (base_cost, max_bytes_cost); + + ctx.cached.compile_cost_base_per_byte_v2 = Some(costs.clone()); + costs +} + +fn pure_deploy_bytes(ctx: &mut EstimatorContext) -> GasCost { + let config_store = RuntimeConfigStore::new(None); + let vm_config = config_store.get_config(PROTOCOL_VERSION).wasm_config.clone(); + let small_code = generate_data_only_contract(0, &vm_config); + let large_code = generate_data_only_contract(bytesize::mb(4u64) as usize, &vm_config); + let small_code_len = small_code.len(); + let large_code_len = large_code.len(); + let cost_empty = deploy_contract_cost(ctx, small_code, Some(b"main")); + let cost_4mb = deploy_contract_cost(ctx, large_code, Some(b"main")); + + (cost_4mb - cost_empty) / (large_code_len - small_code_len) as u64 +} + +/// Base cost for a fn call action, without receipt creation or contract loading. +fn action_function_call_base(ctx: &mut EstimatorContext) -> GasCost { + let config_store = RuntimeConfigStore::new(None); + let vm_config = config_store.get_config(PROTOCOL_VERSION).wasm_config.clone(); + let n_actions = 100; + let code = generate_data_only_contract(0, &vm_config); + // This returns a cost without block/transaction/receipt overhead. + let base_cost = fn_cost_in_contract(ctx, "main", &code, n_actions); + // Executable loading is a separately charged step, so it must be subtracted on the action cost. + let executable_loading_cost = contract_loading_base(ctx); + base_cost.saturating_sub(&executable_loading_cost, &NonNegativeTolerance::PER_MILLE) +} + +fn action_function_call_per_byte(ctx: &mut EstimatorContext) -> GasCost { + // X values below 1M have a rather high variance. Therefore, use one small X + // value and two larger values to fit a curve that gets the slope about + // right. + let xs = [1, 1_000_000, 4_000_000]; + let ys: Vec = xs + .iter() + .map(|&arg_len| inner_action_function_call_per_byte(ctx, arg_len as usize)) + .collect(); + + let (_base, per_byte) = GasCost::least_squares_method_gas_cost( + &xs, + &ys, + &LeastSquaresTolerance::default().factor_rel_nn_tolerance(0.001), + ctx.config.debug, + ); + per_byte +} + +fn inner_action_function_call_per_byte(ctx: &mut EstimatorContext, arg_len: usize) -> GasCost { + let mut make_transaction = |tb: &mut TransactionBuilder| -> SignedTransaction { + let sender = tb.random_unused_account(); + let args = utils::random_vec(arg_len); + tb.transaction_from_function_call(sender, "noop", args) + }; + let block_size = 5; + let block_latency = 0; + transaction_cost_ext(ctx, block_size, &mut make_transaction, block_latency).0 +} + +fn contract_loading_base(ctx: &mut EstimatorContext) -> GasCost { + let (base, _per_byte) = contract_loading_base_per_byte(ctx); + base +} +fn contract_loading_per_byte(ctx: &mut EstimatorContext) -> GasCost { + let (_base, per_byte) = contract_loading_base_per_byte(ctx); + per_byte +} +fn contract_loading_base_per_byte(ctx: &mut EstimatorContext) -> (GasCost, GasCost) { + if let Some(base_byte_cost) = ctx.cached.contract_loading_base_per_byte.clone() { + return base_byte_cost; + } + + let (base, per_byte) = crate::function_call::contract_loading_cost(ctx.config); + ctx.cached.contract_loading_base_per_byte = Some((base.clone(), per_byte.clone())); + (base, per_byte) +} +fn function_call_per_storage_byte(ctx: &mut EstimatorContext) -> GasCost { + let config_store = RuntimeConfigStore::new(None); + let vm_config = config_store.get_config(PROTOCOL_VERSION).wasm_config.clone(); + let n_actions = 5; + + let small_code = generate_data_only_contract(0, &vm_config); + let small_cost = fn_cost_in_contract(ctx, "main", &small_code, n_actions); + + let large_code = generate_data_only_contract(4_000_000, &vm_config); + let large_cost = fn_cost_in_contract(ctx, "main", &large_code, n_actions); + + large_cost.saturating_sub(&small_cost, &NonNegativeTolerance::PER_MILLE) + / (large_code.len() - small_code.len()) as u64 +} + +fn data_receipt_creation_base(ctx: &mut EstimatorContext) -> GasCost { + // NB: there isn't `ExtCosts` for data receipt creation, so we ignore (`_`) the counts. + // The function returns a chain of two promises. + let block_latency = 2; + let (total_cost, _) = + fn_cost_count(ctx, "data_receipt_10b_1000", ExtCosts::base, block_latency); + // The function returns a promise. + let block_latency = 1; + let (base_cost, _) = + fn_cost_count(ctx, "data_receipt_base_10b_1000", ExtCosts::base, block_latency); + + total_cost.saturating_sub(&base_cost, &NonNegativeTolerance::PER_MILLE) / 1000 +} + +fn data_receipt_creation_per_byte(ctx: &mut EstimatorContext) -> GasCost { + // NB: there isn't `ExtCosts` for data receipt creation, so we ignore (`_`) the counts. + // The function returns a chain of two promises. + let block_latency = 2; + let (total_cost, _) = + fn_cost_count(ctx, "data_receipt_100kib_1000", ExtCosts::base, block_latency); + // The function returns a chain of two promises. + let block_latency = 2; + let (base_cost, _) = fn_cost_count(ctx, "data_receipt_10b_1000", ExtCosts::base, block_latency); + + let bytes_per_transaction = 1000 * 100 * 1024; + + total_cost.saturating_sub(&base_cost, &NonNegativeTolerance::PER_MILLE) / bytes_per_transaction +} + +fn action_delegate_base(ctx: &mut EstimatorContext) -> GasCost { + let total_cost = { + let mut nonce = 1; + let mut make_transaction = |tb: &mut TransactionBuilder| -> SignedTransaction { + let sender = tb.random_unused_account(); + let receiver = tb.random_unused_account(); + + let action = + action_costs::empty_delegate_action(nonce, sender.clone(), receiver.clone()); + nonce += 1; + tb.transaction_from_actions(sender, receiver, vec![action]) + }; + // meta tx is delayed by 2 block compared to local receipt + let block_latency = 2; + let block_size = 100; + let (gas_cost, _ext_costs) = + transaction_cost_ext(ctx, block_size, &mut make_transaction, block_latency); + gas_cost + }; + + // action receipt creation send cost is paid twice for meta transactions, + // exec only once, thus we want to subtract this cost 1.5 times + let base_cost = action_receipt_creation(ctx) * 3 / 2; + + total_cost.saturating_sub(&base_cost, &NonNegativeTolerance::PER_MILLE) +} + +fn host_function_call(ctx: &mut EstimatorContext) -> GasCost { + let block_latency = 0; + let (total_cost, count) = fn_cost_count(ctx, "base_1M", ExtCosts::base, block_latency); + assert_eq!(count, 1_000_000); + + let base_cost = noop_function_call_cost(ctx); + + total_cost.saturating_sub(&base_cost, &NonNegativeTolerance::PER_MILLE) / count +} + +fn wasm_instruction(ctx: &mut EstimatorContext) -> GasCost { + let vm_kind = ctx.config.vm_kind; + + let code = unc_test_contracts::estimator_contract(); + + let n_iters = 10; + + let code = ContractCode::new(code.to_vec(), None); + let mut fake_external = MockedExternal::new(); + let config_store = RuntimeConfigStore::new(None); + let config = config_store.get_config(PROTOCOL_VERSION).wasm_config.clone(); + let fees = RuntimeFeesConfig::test(); + let promise_results = vec![]; + let cache = MockCompiledContractCache::default(); + + let mut run = || { + let context = create_context(vec![]); + let vm_result = vm_kind + .runtime(config.clone()) + .unwrap() + .run( + &code, + "cpu_ram_soak_test", + &mut fake_external, + context, + &fees, + &promise_results, + Some(&cache), + ) + .expect("fatal_error"); + assert!(vm_result.aborted.is_some()); + vm_result + }; + + let warmup_outcome = run(); + + let total = { + let start = GasCost::measure(ctx.config.metric); + for _ in 0..n_iters { + run(); + } + start.elapsed() + }; + + let instructions_per_iter = { + let op_cost = config.regular_op_cost as u64; + warmup_outcome.burnt_gas / op_cost + }; + + let per_instruction = total / (instructions_per_iter * n_iters); + per_instruction +} + +fn read_memory_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "read_memory_10b_10k", ExtCosts::read_memory_base, 10_000) +} +fn read_memory_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "read_memory_1Mib_10k", ExtCosts::read_memory_byte, 1024 * 1024 * 10_000) +} + +fn write_memory_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "write_memory_10b_10k", ExtCosts::write_memory_base, 10_000) +} +fn write_memory_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "write_memory_1Mib_10k", ExtCosts::write_memory_byte, 1024 * 1024 * 10_000) +} + +fn read_register_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "read_register_10b_10k", ExtCosts::read_register_base, 10_000) +} +fn read_register_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "read_register_1Mib_10k", ExtCosts::read_register_byte, 1024 * 1024 * 10_000) +} + +fn write_register_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "write_register_10b_10k", ExtCosts::write_register_base, 10_000) +} +fn write_register_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "write_register_1Mib_10k", ExtCosts::write_register_byte, 1024 * 1024 * 10_000) +} + +fn log_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "utf16_log_10b_10k", ExtCosts::log_base, 10_000) +} +fn log_byte(ctx: &mut EstimatorContext) -> GasCost { + // NOTE: We are paying per *output* byte here, hence 3/2 multiplier. + fn_cost(ctx, "utf16_log_10kib_1k", ExtCosts::log_byte, (10 * 1024 * 3 / 2) * 1_000) +} + +fn utf8_decoding_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "utf8_log_10b_10k", ExtCosts::utf8_decoding_base, 10_000) +} +fn utf8_decoding_byte(ctx: &mut EstimatorContext) -> GasCost { + let no_nul = fn_cost(ctx, "utf8_log_10kib_1k", ExtCosts::utf8_decoding_byte, 10 * 1024 * 1_000); + let nul = fn_cost( + ctx, + "nul_utf8_log_10kib_1k", + ExtCosts::utf8_decoding_byte, + (10 * 1024 - 1) * 1_000, + ); + nul.max(no_nul) +} + +fn utf16_decoding_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "utf16_log_10b_10k", ExtCosts::utf16_decoding_base, 10_000) +} +fn utf16_decoding_byte(ctx: &mut EstimatorContext) -> GasCost { + let no_nul = + fn_cost(ctx, "utf16_log_10kib_1k", ExtCosts::utf16_decoding_byte, 10 * 1024 * 1_000); + let nul = fn_cost( + ctx, + "nul_utf16_log_10kib_1k", + ExtCosts::utf16_decoding_byte, + (10 * 1024 - 2) * 1_000, + ); + nul.max(no_nul) +} + +fn sha256_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "sha256_10b_10k", ExtCosts::sha256_base, 10_000) +} +fn sha256_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "sha256_10kib_10k", ExtCosts::sha256_byte, 10 * 1024 * 10_000) +} + +fn keccak256_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "keccak256_10b_10k", ExtCosts::keccak256_base, 10_000) +} +fn keccak256_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "keccak256_10kib_10k", ExtCosts::keccak256_byte, 10 * 1024 * 10_000) +} + +fn keccak512_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "keccak512_10b_10k", ExtCosts::keccak512_base, 10_000) +} +fn keccak512_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "keccak512_10kib_10k", ExtCosts::keccak512_byte, 10 * 1024 * 10_000) +} + +fn ripemd160_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "ripemd160_10b_10k", ExtCosts::ripemd160_base, 10_000) +} +fn ripemd160_block(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "ripemd160_10kib_10k", ExtCosts::ripemd160_block, (10 * 1024 / 64 + 1) * 10_000) +} + +fn ecrecover_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "ecrecover_10k", ExtCosts::ecrecover_base, 10_000) +} + +fn ed25519_verify_base(ctx: &mut EstimatorContext) -> GasCost { + if ctx.cached.ed25519_verify_base.is_none() { + let cost = fn_cost(ctx, "ed25519_verify_32b_500", ExtCosts::ed25519_verify_base, 500); + ctx.cached.ed25519_verify_base = Some(cost); + } + ctx.cached.ed25519_verify_base.clone().unwrap() +} + +fn ed25519_verify_byte(ctx: &mut EstimatorContext) -> GasCost { + let base = ed25519_verify_base(ctx); + // inside the WASM function, there are 64 calls to `ed25519_verify`. + let base_call_num = 64; + // each call checks a message of size 16kiB + let iteration_bytes = 16384; + let total_bytes = base_call_num * iteration_bytes; + let byte = fn_cost(ctx, "ed25519_verify_16kib_64", ExtCosts::ed25519_verify_byte, total_bytes); + // need to subtract the base cost, which has already been divided by the number of bytes per iteration + byte - base / iteration_bytes +} + +fn alt_bn128g1_multiexp_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "alt_bn128_g1_multiexp_1_10", ExtCosts::alt_bn128_g1_multiexp_base, 10) +} +fn alt_bn128g1_multiexp_element(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "alt_bn128_g1_multiexp_10_10", ExtCosts::alt_bn128_g1_multiexp_element, 10 * 10) +} + +fn alt_bn128g1_sum_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "alt_bn128_g1_sum_1_1k", ExtCosts::alt_bn128_g1_sum_base, 1000) +} +fn alt_bn128g1_sum_element(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "alt_bn128_g1_sum_10_1k", ExtCosts::alt_bn128_g1_sum_element, 10 * 1000) +} + +fn alt_bn128_pairing_check_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "alt_bn128_pairing_check_1_10", ExtCosts::alt_bn128_pairing_check_base, 10) +} +fn alt_bn128_pairing_check_element(ctx: &mut EstimatorContext) -> GasCost { + fn_cost( + ctx, + "alt_bn128_pairing_check_10_10", + ExtCosts::alt_bn128_pairing_check_element, + 10 * 10, + ) +} + +fn storage_has_key_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost_with_setup( + ctx, + "storage_write_10b_key_10b_value_1k", + "storage_has_key_10b_key_10b_value_1k", + ExtCosts::storage_has_key_base, + 1000, + ) +} +fn storage_has_key_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost_with_setup( + ctx, + "storage_write_10kib_key_10b_value_1k", + "storage_has_key_10kib_key_10b_value_1k", + ExtCosts::storage_has_key_byte, + 10 * 1024 * 1000, + ) +} + +fn storage_read_base(ctx: &mut EstimatorContext) -> GasCost { + if let Some(cost) = ctx.cached.storage_read_base.clone() { + return cost; + } + + let cost = fn_cost_with_setup( + ctx, + "storage_write_10b_key_10b_value_1k", + "storage_read_10b_key_10b_value_1k", + ExtCosts::storage_read_base, + 1000, + ); + + ctx.cached.storage_read_base = Some(cost.clone()); + cost +} +fn storage_read_key_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost_with_setup( + ctx, + "storage_write_10kib_key_10b_value_1k", + "storage_read_10kib_key_10b_value_1k", + ExtCosts::storage_read_key_byte, + 10 * 1024 * 1000, + ) +} +fn storage_read_value_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost_with_setup( + ctx, + "storage_write_10b_key_10kib_value_1k", + "storage_read_10b_key_10kib_value_1k", + ExtCosts::storage_read_value_byte, + 10 * 1024 * 1000, + ) +} + +fn storage_write_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost(ctx, "storage_write_10b_key_10b_value_1k", ExtCosts::storage_write_base, 1000) +} +fn storage_write_key_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost( + ctx, + "storage_write_10kib_key_10b_value_1k", + ExtCosts::storage_write_key_byte, + 10 * 1024 * 1000, + ) +} +fn storage_write_value_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost( + ctx, + "storage_write_10b_key_10kib_value_1k", + ExtCosts::storage_write_value_byte, + 10 * 1024 * 1000, + ) +} +fn storage_write_evicted_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost_with_setup( + ctx, + "storage_write_10b_key_10kib_value_1k", + "storage_write_10b_key_10kib_value_1k", + ExtCosts::storage_write_evicted_byte, + 10 * 1024 * 1000, + ) +} + +fn storage_remove_base(ctx: &mut EstimatorContext) -> GasCost { + fn_cost_with_setup( + ctx, + "storage_write_10b_key_10b_value_1k", + "storage_remove_10b_key_10b_value_1k", + ExtCosts::storage_remove_base, + 1000, + ) +} +fn storage_remove_key_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost_with_setup( + ctx, + "storage_write_10kib_key_10b_value_1k", + "storage_remove_10kib_key_10b_value_1k", + ExtCosts::storage_remove_key_byte, + 10 * 1024 * 1000, + ) +} +fn storage_remove_ret_value_byte(ctx: &mut EstimatorContext) -> GasCost { + fn_cost_with_setup( + ctx, + "storage_write_10b_key_10kib_value_1k", + "storage_remove_10b_key_10kib_value_1k", + ExtCosts::storage_remove_ret_value_byte, + 10 * 1024 * 1000, + ) +} + +fn touching_trie_node(ctx: &mut EstimatorContext) -> GasCost { + // TTN write cost = TTN cost because we no longer charge it on reads since + // flat storage for reads was introduced + touching_trie_node_write(ctx) +} + +fn touching_trie_node_write(ctx: &mut EstimatorContext) -> GasCost { + if let Some(cost) = ctx.cached.touching_trie_node_write.clone() { + return cost; + } + let warmup_iters = ctx.config.warmup_iters_per_block; + let measured_iters = ctx.config.iter_per_block; + // Number of bytes in the final key. Will create 2x that many nodes. + // Picked somewhat arbitrarily, balancing estimation time vs accuracy. + let final_key_len = 1000; + let cost = trie::write_node(ctx, warmup_iters, measured_iters, final_key_len); + + ctx.cached.touching_trie_node_write = Some(cost.clone()); + cost +} + +fn read_cached_trie_node(ctx: &mut EstimatorContext) -> GasCost { + let warmup_iters = ctx.config.warmup_iters_per_block; + let iters = ctx.config.iter_per_block; + let mut testbed = ctx.testbed(); + + let results = (0..(warmup_iters + iters)) + .map(|_| trie::read_node_from_accounting_cache(&mut testbed)) + .skip(warmup_iters) + .collect::>(); + average_cost(results) +} + +fn apply_block_cost(ctx: &mut EstimatorContext) -> GasCost { + if let Some(cost) = ctx.cached.apply_block.clone() { + return cost; + } + + let mut testbed = ctx.testbed(); + + let n_warmup = testbed.config.warmup_iters_per_block; + // Inner + outer iterations such that a single measurement is reasonably stable. + let n_blocks = 10; + let outer_iterations = testbed.config.iter_per_block; + + // Warmup inner and outer loop, to make sure this estimation is not + // overestimated. This value is subtracted from other measurements, + // overestimating is more of a concern than usual. + let blocks = vec![vec![]; n_blocks + n_warmup]; + let measurements = iter::repeat_with(|| { + testbed + .measure_blocks(blocks.clone(), 0) + .into_iter() + .skip(n_warmup) + .map(|(gas, _ext)| gas) + .sum::() + / n_blocks as u64 + }) + .skip(n_warmup) + .take(outer_iterations) + .collect::>(); + + let gas_cost = average_cost(measurements); + + ctx.cached.apply_block = Some(gas_cost.clone()); + + gas_cost +} + +fn gas_metering_base(ctx: &mut EstimatorContext) -> GasCost { + gas_metering(ctx).0 +} + +fn gas_metering_op(ctx: &mut EstimatorContext) -> GasCost { + gas_metering(ctx).1 +} + +fn rocks_db_insert_value_byte(ctx: &mut EstimatorContext) -> GasCost { + let total_bytes = ctx.config.rocksdb_test_config.op_count as u64 + * ctx.config.rocksdb_test_config.value_size as u64; + rocks_db_inserts_cost(&ctx.config) / total_bytes +} + +fn rocks_db_read_value_byte(ctx: &mut EstimatorContext) -> GasCost { + let total_bytes = ctx.config.rocksdb_test_config.op_count as u64 + * ctx.config.rocksdb_test_config.value_size as u64; + rocks_db_read_cost(&ctx.config) / total_bytes +} + +fn gas_metering(ctx: &mut EstimatorContext) -> (GasCost, GasCost) { + if let Some(cached) = ctx.cached.gas_metering_cost_base_per_op.clone() { + return cached; + } + let (base, byte) = gas_metering_cost(&ctx.config); + ctx.cached.gas_metering_cost_base_per_op = Some((base.clone(), byte.clone())); + (base, byte) +} + +fn cpu_benchmark_sha256(ctx: &mut EstimatorContext) -> GasCost { + const REPEATS: u64 = 1_000_000; + sha256_cost(ctx.config.metric, REPEATS) +} + +/// Estimate how much gas is charged for 1 CPU instruction. (Using given runtime parameters, on the specific system this is being run on.) +fn one_cpu_instruction(ctx: &mut EstimatorContext) -> GasCost { + eprintln!("Cannot estimate ONE_CPU_INSTRUCTION like any other cost. The result will only show the constant value currently used in the estimator."); + GasCost::from_gas(estimator_params::GAS_IN_INSTR, ctx.config.metric) +} + +/// Estimate how much gas is charged for 1 nanosecond of computation. (Using given runtime parameters, on the specific system this is being run on.) +fn one_nanosecond(ctx: &mut EstimatorContext) -> GasCost { + // Currently we don't have a test for this, yet. 1 gas has just always been 1ns. + // But it would be useful to go backwards and see how expensive computation time is on specific hardware. + eprintln!("Cannot estimate ONE_NANOSECOND like any other cost. The result will only show the constant value currently used in the estimator."); + GasCost::from_gas(estimator_params::GAS_IN_NS, ctx.config.metric) +} diff --git a/runtime/runtime-params-estimator/src/main.rs b/runtime/runtime-params-estimator/src/main.rs new file mode 100644 index 000000000..3bf470394 --- /dev/null +++ b/runtime/runtime-params-estimator/src/main.rs @@ -0,0 +1,530 @@ +#![doc = include_str!("../README.md")] + +use anyhow::Context; +use genesis_populate::GenesisBuilder; +use unc_chain_configs::GenesisValidationMode; +use unc_parameters::vm::VMKind; +use unc_parameters::RuntimeConfigView; +use replay::ReplayCmd; +use runtime_params_estimator::config::{Config, GasMetric}; +use runtime_params_estimator::{ + costs_to_runtime_config, Cost, CostTable, QemuCommandBuilder, RocksDBTestConfig, +}; +use std::env; +use std::fmt::Write; +use std::fs::{self}; +use std::path::Path; +use std::path::PathBuf; +use std::process::Command; +use std::time; +use tracing_subscriber::Layer; + +mod replay; + +#[derive(clap::Parser)] +struct CliArgs { + /// Directory for config and data. If not set, a temporary directory is used + /// to generate appropriate data. + #[clap(long)] + home: Option, + /// How many warm up iterations per block should we run. + #[clap(long, default_value = "0")] + warmup_iters: usize, + /// How many iterations per block are we going to try. + #[clap(long, default_value = "10")] + iters: usize, + /// Number of active accounts in the state (accounts used for estimation). + #[clap(long, default_value = "20000")] + accounts_num: usize, + /// Number of additional accounts to add to the state, among which active accounts are selected. + #[clap(long, default_value = "200000")] + additional_accounts_num: u64, + /// How many blocks behind the final head is assumed to be compared to the tip. + /// + /// This is used to simulate flat state deltas, which depend on finality. + #[clap(long, default_value = "50")] + pub finality_lag: usize, + /// How many key-value pairs change per flat state delta. + #[clap(long, default_value = "100")] + pub fs_keys_per_delta: usize, + /// Skip building test contract which is used in metrics computation. + #[clap(long)] + skip_build_test_contract: bool, + /// What metric to use. + /// + /// `time` measures wall-clock time elapsed. + /// `icount` counts the CPU instructions and syscall-level IO bytes executed + /// using qemu instrumentation. + /// Note that `icount` measurements are not accurate when translating to gas. The main purpose of it is to + /// have a stable output that can be used to detect performance regressions. + #[clap(long, default_value = "time", value_parser(["icount", "time"]))] + metric: String, + /// Which VM to test. + #[clap(long, value_enum, default_value_t = VMKind::NearVm)] + vm_kind: VMKind, + /// Render existing `costs.txt` as `RuntimeConfig`. + #[clap(long)] + costs_file: Option, + /// Compare baseline `costs-file` with a different costs file. + #[clap(long, requires("costs_file"))] + compare_to: Option, + /// Coma-separated lists of a subset of costs to estimate. + #[clap(long, use_value_delimiter = true)] + costs: Option>, + /// Build and run the estimator inside a docker container via QEMU. + #[clap(long)] + docker: bool, + /// Spawn a bash shell inside a docker container for debugging purposes. + #[clap(long)] + docker_shell: bool, + /// Drop OS cache before measurements for better IO accuracy. Requires sudo. + #[clap(long)] + drop_os_cache: bool, + /// Print extra debug information. + #[clap(long)] + debug: bool, + /// Print detailed estimation results in JSON format. One line with one JSON + /// object per estimation. + #[clap(long)] + json_output: bool, + /// Prints hierarchical execution-timing information using the tracing-span-tree crate. + #[clap(long)] + tracing_span_tree: bool, + /// Records IO events in JSON format and stores it in a given file. + #[clap(long)] + record_io_trace: Option, + /// Use in-memory test DB, useful to avoid variance caused by DB. + #[clap(long)] + pub in_memory_db: bool, + /// If false, only runs a minimal check that's faster than trying to get accurate results. + #[clap(long, default_value_t = true, action = clap::ArgAction::Set)] + pub accurate: bool, + /// Extra configuration parameters for RocksDB specific estimations + #[clap(flatten)] + db_test_config: RocksDBTestConfig, + #[clap(subcommand)] + sub_cmd: Option, +} + +#[derive(clap::Subcommand)] +enum CliSubCmd { + Replay(ReplayCmd), +} + +fn main() -> anyhow::Result<()> { + let start = time::Instant::now(); + let cli_args: CliArgs = clap::Parser::parse(); + + if let Some(cmd) = cli_args.sub_cmd { + return match cmd { + CliSubCmd::Replay(inner) => inner.run(&mut std::io::stdout()), + }; + } + + if let Some(cost_table) = run_estimation(cli_args)? { + let output_path = { + let timestamp = chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true); + let commit = exec("git rev-parse --short HEAD") + .map(|hash| format!("-{}", hash)) + .unwrap_or_default(); + let file_name = format!("costs-{}{}.txt", timestamp, commit); + + env::current_dir()?.join(file_name) + }; + fs::write(&output_path, &cost_table.to_string())?; + eprintln!( + "\nFinished in {:.2?}, output saved to:\n\n {}", + start.elapsed(), + output_path.display() + ); + } + Ok(()) +} + +fn run_estimation(cli_args: CliArgs) -> anyhow::Result> { + let temp_dir; + let state_dump_path = match cli_args.home { + Some(it) => it, + None => { + temp_dir = tempfile::tempdir()?; + temp_dir.path().to_path_buf() + } + }; + if state_dump_path.read_dir()?.next().is_none() { + // Every created account gets this smart contract deployed, such that + // any account can be used to perform estimations that require this + // contract. + // Note: This contract no longer has a fixed size, which means that + // changes to the test contract might affect all kinds of estimations. + // (Larger code = more time spent on reading it from the database, for + // example.) But this is generally a sign of a badly designed + // estimation, therefore we make no effort to guarantee a fixed size. + // Also, continuous estimation should be able to pick up such changes. + let contract_code = unc_test_contracts::estimator_contract(); + + framework::init_configs( + &state_dump_path, + None, + Some("test.near".parse().unwrap()), + Some("alice.near"), + 1, + true, + None, + false, + None, + None, + false, + None, + None, + None, + ) + .expect("failed to init config"); + + let unc_config = framework::load_config(&state_dump_path, GenesisValidationMode::Full) + .context("Error loading config")?; + let store = unc_store::NodeStorage::opener( + &state_dump_path, + unc_config.config.archive, + &unc_config.config.store, + None, + ) + .open() + .unwrap() + .get_hot_store(); + GenesisBuilder::from_config_and_store(&state_dump_path, unc_config, store) + .add_additional_accounts(cli_args.additional_accounts_num) + .add_additional_accounts_contract(contract_code.to_vec()) + .print_progress() + .build() + .unwrap() + .dump_state() + .unwrap(); + } + + if cli_args.docker { + main_docker( + &state_dump_path, + cli_args.accurate, + cli_args.docker_shell, + cli_args.json_output, + cli_args.debug, + )?; + // The cost table has already been printed inside docker, the outer + // instance does not produce an output. + return Ok(None); + } + + if let Some(compare_to) = cli_args.compare_to { + let baseline = cli_args.costs_file.unwrap(); + + let compare_to = read_costs_table(&compare_to)?; + let baseline = read_costs_table(&baseline)?; + println!("{}", baseline.diff(&compare_to)); + return Ok(None); + } + + if let Some(path) = cli_args.costs_file { + let cost_table = read_costs_table(&path)?; + + let runtime_config = costs_to_runtime_config(&cost_table)?; + + println!("Generated RuntimeConfig:\n"); + println!("{:#?}", runtime_config); + + let config_view = RuntimeConfigView::from(runtime_config); + let str = serde_json::to_string_pretty(&config_view) + .expect("Failed serializing the runtime config"); + + let output_path = state_dump_path.join("runtime_config.json"); + fs::write(&output_path, &str) + .with_context(|| "failed to write runtime config to file".to_string())?; + println!("\nOutput saved to:\n\n {}", output_path.display()); + + return Ok(None); + } + + #[cfg(feature = "io_trace")] + let mut _maybe_writer_guard = None; + + if cli_args.tracing_span_tree { + tracing_span_tree::span_tree().enable(); + } else { + use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt; + let log_layer = tracing_subscriber::fmt::layer() + .with_filter(tracing_subscriber::EnvFilter::from_default_env()); + let subscriber = tracing_subscriber::registry().with(log_layer); + #[cfg(feature = "io_trace")] + let subscriber = subscriber.with(cli_args.record_io_trace.map(|path| { + let log_file = + fs::File::create(path).expect("unable to create or truncate IO trace output file"); + let (subscriber, guard) = unc_o11y::make_io_tracing_layer(log_file); + _maybe_writer_guard = Some(guard); + subscriber + })); + + #[cfg(not(feature = "io_trace"))] + if cli_args.record_io_trace.is_some() { + anyhow::bail!("`--record-io-trace` requires `--feature=io_trace`"); + } + + tracing::subscriber::set_global_default(subscriber) + .expect("setting default subscriber failed"); + }; + + let warmup_iters_per_block = cli_args.warmup_iters; + let mut rocksdb_test_config = cli_args.db_test_config; + rocksdb_test_config.debug_rocksdb = cli_args.debug; + rocksdb_test_config.drop_os_cache = cli_args.drop_os_cache; + let iter_per_block = cli_args.iters; + let active_accounts = cli_args.accounts_num; + let metric = match cli_args.metric.as_str() { + "icount" => GasMetric::ICount, + "time" => GasMetric::Time, + other => unreachable!("Unknown metric {}", other), + }; + let config = Config { + warmup_iters_per_block, + iter_per_block, + active_accounts, + finality_lag: cli_args.finality_lag, + fs_keys_per_delta: cli_args.fs_keys_per_delta, + state_dump_path: state_dump_path, + metric, + vm_kind: cli_args.vm_kind, + costs_to_measure: cli_args.costs, + rocksdb_test_config, + debug: cli_args.debug, + json_output: cli_args.json_output, + drop_os_cache: cli_args.drop_os_cache, + in_memory_db: cli_args.in_memory_db, + accurate: cli_args.accurate, + }; + let cost_table = runtime_params_estimator::run(config); + Ok(Some(cost_table)) +} + +/// Spawns another instance of this binary but inside docker. +/// +/// Most command line args are passed through but `--docker` is removed. +/// We are now also running with an in-memory database to increase turn-around +/// time and make the results more consistent. Note that this means qemu based +/// IO estimations are inaccurate. They never really have been very accurate +/// anyway and qemu is just not the right tool to measure IO costs. +fn main_docker( + state_dump_path: &Path, + full: bool, + debug_shell: bool, + json_output: bool, + debug: bool, +) -> anyhow::Result<()> { + let profile = if full { "release" } else { "dev-release" }; + exec("docker --version").context("please install `docker`")?; + + let project_root = project_root(); + let tagged_image = docker_image()?; + if exec(&format!("docker images -q {}", tagged_image))?.is_empty() { + // Build a docker image if there isn't one already. + let status = Command::new("docker") + .args(&["build", "--tag", &tagged_image]) + .arg(project_root.join("runtime/runtime-params-estimator/emu-cost")) + .status()?; + if !status.success() { + anyhow::bail!("failed to build a docker image") + } + } + + let init = { + // Build a bash script to run inside the container. Concatenating a bash + // script from strings is fragile, but I don't know a better way. + + let mut buf = String::new(); + buf.push_str("set -ex;\n"); + buf.push_str("cd /host/framework;\n"); + buf.push_str("cargo build --manifest-path /host/framework/Cargo.toml"); + buf.push_str(" --package runtime-params-estimator --bin runtime-params-estimator"); + + // Feature "required" is always necessary for accurate measurements. + buf.push_str(" --features required"); + + // Also add nightly protocol features to docker build if they are enabled. + #[cfg(feature = "nightly")] + buf.push_str(",nightly"); + #[cfg(feature = "nightly_protocol")] + buf.push_str(",nightly_protocol"); + + buf.push_str(" --profile "); + buf.push_str(profile); + buf.push_str(";"); + + let mut qemu_cmd_builder = QemuCommandBuilder::default(); + + if debug { + qemu_cmd_builder = qemu_cmd_builder.plugin_log(true).print_on_every_close(true); + } + let mut qemu_cmd = qemu_cmd_builder + .build(&format!("/host/framework/target/{profile}/runtime-params-estimator"))?; + + qemu_cmd.args(&["--home", "/.near"]); + buf.push_str(&format!("{:?}", qemu_cmd)); + + // Sanitize & forward our arguments to the estimator to be run inside + // docker. + let mut args = env::args(); + let _binary_name = args.next(); + while let Some(arg) = args.next() { + match arg.as_str() { + "--docker" => continue, + "--additional-accounts-num" | "--home" => { + args.next(); + continue; + } + _ => { + write!(buf, " {:?}", arg).unwrap(); + } + } + } + + // test contract has been built by host + write!(buf, " --skip-build-test-contract").unwrap(); + // accounts have been inserted to state dump by host + write!(buf, " --additional-accounts-num 0").unwrap(); + // We are now always running qemu based estimations with an in-memory DB + // because it cannot account for the multi-threaded nature of RocksDB, or + // the different latencies for disk and memory. Using in-memory DB at + // least gives consistent and quick results. + // Note that this still reads all values from the state dump and creates + // a new testbed for each estimation, we only switch out the storage backend. + write!(buf, " --in-memory-db").unwrap(); + + buf + }; + + let framework = + format!("type=bind,source={},target=/host/framework", project_root.to_str().unwrap()); + let nearhome = format!("type=bind,source={},target=/.near", state_dump_path.to_str().unwrap()); + + let mut cmd = Command::new("docker"); + cmd.args(&["run", "--rm", "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"]) + .args(&["--mount", &framework]) + .args(&["--mount", &nearhome]) + .args(&["--mount", "source=rust-emu-target-dir,target=/host/framework/target"]) + .args(&["--mount", "source=rust-emu-cargo-dir,target=/usr/local/cargo"]) + .args(&["--env", "RUST_BACKTRACE=full"]); + // Spawning an interactive shell and pseudo TTY is necessary for debug shell + // and nice-to-have in the general case, for cargo to color its output. But + // it also merges stderr and stdout, which is problem when the stdout should + // be piped to another process. So far, only JSON output makes sense to + // pipe, everything else goes to stderr. + if debug_shell || !json_output { + cmd.args(&["--interactive", "--tty"]); + } + cmd.arg(tagged_image); + + if debug_shell { + cmd.args(&["/usr/bin/env", "bash"]); + } else { + cmd.args(&["/usr/bin/env", "bash", "-c", &init]); + } + + cmd.status()?; + Ok(()) +} + +/// Creates a docker image tag that is unique for each rust version to force re-build when it changes. +fn docker_image() -> Result { + let image = "rust-emu"; + let dockerfile = + fs::read_to_string(Path::new(env!("CARGO_MANIFEST_DIR")).join("emu-cost/Dockerfile"))?; + // The Dockerfile is expected to have a line like this: + // ``` + // FROM rust:x.y.z + // ``` + // and the result should be `rust-x.y.z` + let tag = dockerfile + .lines() + .find_map(|line| line.split_once("FROM ")) + .context("could not parse rustc version from Dockerfile")? + .1 + .replace(":", "-"); + + Ok(format!("{}:{}", image, tag)) +} + +fn read_costs_table(path: &Path) -> anyhow::Result { + fs::read_to_string(&path) + .with_context(|| format!("failed to read costs file: {}", path.display()))? + .parse::() + .map_err(|e| { + anyhow::format_err!("failed to parse costs file at {} due to {e}", path.display()) + }) +} + +fn exec(command: &str) -> anyhow::Result { + let args = command.split_ascii_whitespace().collect::>(); + let (cmd, args) = args.split_first().unwrap(); + let output = std::process::Command::new(cmd) + .args(args) + .output() + .with_context(|| format!("failed to run `{}`", command))?; + if !output.status.success() { + anyhow::bail!("failed to run `{}`", command); + } + let stdout = + String::from_utf8(output.stdout).with_context(|| format!("failed to run `{}`", command))?; + Ok(stdout.trim().to_string()) +} + +fn project_root() -> PathBuf { + let dir = env!("CARGO_MANIFEST_DIR"); + let res = PathBuf::from(dir).ancestors().nth(2).unwrap().to_owned(); + assert!(res.join(".github").exists()); + res +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Test that we can run simple estimations from start ot finish, including + /// the state dump creation in a temporary directory. + /// + /// This is complementary to regular full runs of all estimations. This test + /// here is intended to run as pre-commit and therefore should finish + /// quickly (target: 10-20s). + /// + /// Limitation: This will run on nightly test with all workspace features + /// enabled. It will not cover all compilation errors for building the + /// params-estimator in isolation. + #[test] + fn sanity_check() { + // select a mix of estimations that are all fast + let costs = vec![Cost::WasmInstruction, Cost::StorageHasKeyByte, Cost::AltBn128G1SumBase]; + let args = CliArgs { + home: None, + warmup_iters: 0, + iters: 1, + accounts_num: 100, + additional_accounts_num: 100, + finality_lag: 3, + fs_keys_per_delta: 1, + skip_build_test_contract: false, + metric: "time".to_owned(), + vm_kind: VMKind::NearVm, + costs_file: None, + compare_to: None, + costs: Some(costs), + docker: false, + docker_shell: false, + drop_os_cache: false, + debug: true, + json_output: false, + tracing_span_tree: false, + record_io_trace: None, + in_memory_db: false, + db_test_config: clap::Parser::parse_from(std::iter::empty::()), + sub_cmd: None, + accurate: true, // we run a small number of estimations, no need to take more shortcuts + }; + run_estimation(args).unwrap(); + } +} diff --git a/runtime/runtime-params-estimator/src/qemu.rs b/runtime/runtime-params-estimator/src/qemu.rs new file mode 100644 index 000000000..e12978ca2 --- /dev/null +++ b/runtime/runtime-params-estimator/src/qemu.rs @@ -0,0 +1,146 @@ +//! QEMU instrumentation code to get used resources as measured by the QEMU plugin. + +use num_rational::Ratio; +use std::fmt::Write; +use std::ops; +use std::os::raw::c_void; +use std::process::Command; + +// We use several "magical" file descriptors to interact with the plugin in QEMU +// intercepting read syscall. Plugin counts instructions executed and amount of data transferred +// by IO operations. We "normalize" all those costs into instruction count. +const CATCH_BASE: u32 = 0xcafebabe; +const HYPERCALL_START_COUNTING: u32 = 0; +const HYPERCALL_STOP_AND_GET_INSTRUCTIONS_EXECUTED: u32 = 1; +const HYPERCALL_GET_BYTES_READ: u32 = 2; +const HYPERCALL_GET_BYTES_WRITTEN: u32 = 3; + +#[derive(Clone, PartialEq, Eq, Debug)] +pub(crate) struct QemuMeasurement { + pub instructions: Ratio, + pub io_r_bytes: Ratio, + pub io_w_bytes: Ratio, +} + +impl QemuMeasurement { + pub(crate) fn start_count_instructions() { + hypercall(HYPERCALL_START_COUNTING); + } + + pub(crate) fn end_count_instructions() -> QemuMeasurement { + let instructions = hypercall(HYPERCALL_STOP_AND_GET_INSTRUCTIONS_EXECUTED).into(); + let io_r_bytes = hypercall(HYPERCALL_GET_BYTES_READ).into(); + let io_w_bytes = hypercall(HYPERCALL_GET_BYTES_WRITTEN).into(); + + QemuMeasurement { instructions, io_r_bytes, io_w_bytes } + } + + pub(crate) fn zero() -> Self { + QemuMeasurement { instructions: 0.into(), io_r_bytes: 0.into(), io_w_bytes: 0.into() } + } +} +fn hypercall(index: u32) -> u64 { + let mut result: u64 = 0; + unsafe { + libc::read((CATCH_BASE + index) as i32, &mut result as *mut _ as *mut c_void, 8); + } + result +} + +/// Create a command to be executed inside QEMU with the custom counter plugin. +#[derive(Default)] +pub struct QemuCommandBuilder { + started: bool, + on_every_close: bool, + count_per_thread: bool, + plugin_log: bool, +} + +impl QemuCommandBuilder { + /// Start measurement immediately, without having to call `start_count_instructions` first. + pub fn started(mut self, yes: bool) -> Self { + self.started = yes; + self + } + /// Print the counters on every close() syscall + pub fn print_on_every_close(mut self, yes: bool) -> Self { + self.on_every_close = yes; + self + } + /// Instantiate different counters for each thread + pub fn count_per_thread(mut self, yes: bool) -> Self { + self.count_per_thread = yes; + self + } + + /// Enable plugin log output to stderr + pub fn plugin_log(mut self, yes: bool) -> Self { + self.plugin_log = yes; + self + } + + /// Create the final command line + pub fn build(&self, inner_cmd: &str) -> anyhow::Result { + let mut cmd = Command::new( + "/host/framework/runtime/runtime-params-estimator/emu-cost/counter_plugin/qemu-x86_64", + ); + + let plugin_path = + "/host/framework/runtime/runtime-params-estimator/emu-cost/counter_plugin/libcounter.so"; + + let mut buf = format!("file={}", plugin_path); + if self.started { + write!(buf, ",arg=\"started\"")?; + } + if self.count_per_thread { + write!(buf, ",arg=\"count_per_thread\"")?; + } + if self.on_every_close { + write!(buf, ",arg=\"on_every_close\"")?; + } + cmd.args(&["-plugin", &buf]); + + if self.plugin_log { + cmd.args(&["-d", "plugin"]); + } + + cmd.args(&["-cpu", "Westmere-v1"]); + cmd.arg(inner_cmd); + + Ok(cmd) + } +} + +impl ops::Div for QemuMeasurement { + type Output = QemuMeasurement; + + fn div(mut self, rhs: u64) -> Self::Output { + self.instructions /= rhs; + self.io_r_bytes /= rhs; + self.io_w_bytes /= rhs; + self + } +} + +impl ops::Add for QemuMeasurement { + type Output = QemuMeasurement; + + fn add(mut self, rhs: QemuMeasurement) -> Self::Output { + self.instructions += rhs.instructions; + self.io_r_bytes += rhs.io_r_bytes; + self.io_w_bytes += rhs.io_w_bytes; + self + } +} + +impl ops::Mul for QemuMeasurement { + type Output = QemuMeasurement; + + fn mul(self, rhs: u64) -> Self::Output { + QemuMeasurement { + instructions: self.instructions * rhs, + io_r_bytes: self.io_r_bytes * rhs, + io_w_bytes: self.io_w_bytes * rhs, + } + } +} diff --git a/runtime/runtime-params-estimator/src/replay.rs b/runtime/runtime-params-estimator/src/replay.rs new file mode 100644 index 000000000..bb18050b4 --- /dev/null +++ b/runtime/runtime-params-estimator/src/replay.rs @@ -0,0 +1,348 @@ +use anyhow::Context; +use std::collections::BTreeMap; +use std::fs::File; +use std::io::{self, Write}; +use std::path::PathBuf; +use std::str::SplitWhitespace; +use tracing::log::error; + +use self::fold_db_ops::FoldDbOps; +use self::gas_charges::ChargedVsFree; + +mod cache_stats; +mod fold_db_ops; +mod gas_charges; + +#[derive(clap::Parser)] +pub(crate) struct ReplayCmd { + trace: PathBuf, + #[clap(subcommand)] + mode: ReplayMode, + /// Only show data for a specific smart contract, specified by account id. + #[clap(long)] + account: Option, +} + +#[derive(Clone, Copy, clap::Subcommand, Debug)] +pub(crate) enum ReplayMode { + /// Print DB accesses and cache statistics for the entire trace. + CacheStats, + /// Print DB accesses per receipt. + ReceiptDbStats, + /// Print DB accesses and cache statistics per receipt. + ReceiptCacheStats, + /// Print DB accesses per chunk. + ChunkDbStats, + /// Print DB accesses and cache statistics per chunk. + ChunkCacheStats, + /// Go over DB operations and print how much of it is paid for with gas. + GasCharges, +} + +impl ReplayCmd { + pub(crate) fn run(&self, out: &mut dyn Write) -> anyhow::Result<()> { + let file = File::open(&self.trace)?; + self.run_on_input(io::BufReader::new(file), out) + } + + fn run_on_input(&self, input: impl io::BufRead, out: &mut dyn Write) -> anyhow::Result<()> { + let mut visitor = self.build_visitor(); + for line in input.lines() { + let line = line?; + if let Err(e) = visitor.eval_line(out, &line) { + error!("ERROR: {e} for input line: {line}"); + } + } + visitor.flush(out)?; + Ok(()) + } + + fn build_visitor(&self) -> Box { + match &self.mode { + ReplayMode::CacheStats => { + Box::new(FoldDbOps::new().with_cache_stats().account_filter(self.account.clone())) + } + ReplayMode::ChunkDbStats => { + if self.account.is_some() { + unimplemented!("account filter does not work with per-chunk statistics"); + } + Box::new(FoldDbOps::new().chunks()) + } + ReplayMode::ChunkCacheStats => { + if self.account.is_some() { + unimplemented!("account filter does not work with per-chunk statistics"); + } + Box::new(FoldDbOps::new().chunks().with_cache_stats()) + } + ReplayMode::ReceiptDbStats => { + Box::new(FoldDbOps::new().receipts().account_filter(self.account.clone())) + } + ReplayMode::ReceiptCacheStats => Box::new( + FoldDbOps::new().receipts().with_cache_stats().account_filter(self.account.clone()), + ), + ReplayMode::GasCharges => { + if self.account.is_some() { + unimplemented!("account filter does not work with gas charges"); + } + Box::::default() + } + } + } +} + +/// Interface for processing an IO trace line-by-line. +/// +/// The visitor default methods take over the basic parsing of the IO trace. +/// Specific implementations can extract the data of interest and process it +/// further as necessary, without duplicating the parsing code. +trait Visitor { + /// The root entry point of the visitors. + /// + /// This function takes a raw input line as input without any preprocessing. + /// A visitor may choose to overwrite this function for full control but the + /// intention is that the default implementation takes over the basic + /// parsing and visitor implementations define their behaviour using the + /// other trait methods. + fn eval_line(&mut self, out: &mut dyn Write, line: &str) -> anyhow::Result<()> { + if let Some(indent) = line.chars().position(|c| !c.is_whitespace()) { + let mut tokens = line.split_whitespace(); + if let Some(keyword) = tokens.next() { + match keyword { + "GET" | "SET" | "UPDATE_RC" => { + let col = tokens.next().context("missing column field in DB operation")?; + let mut key_str = tokens.next().context("missing key in DB operation")?; + if key_str.starts_with('"') { + key_str = &key_str[1..key_str.len() - 1]; + } + let key = unc_fmt::Bytes::from_str(key_str).map_err(|e| { + anyhow::anyhow!("failed to decode key {key_str} because {e}") + })?; + let dict = extract_key_values(tokens)?; + let size: Option = dict.get("size").map(|s| s.parse()).transpose()?; + self.eval_db_op(out, indent, keyword, size, &key, col)?; + } + "storage_read" | "storage_write" | "storage_remove" | "storage_has_key" => { + let op = tokens.next(); + if op.is_none() { + return Ok(()); + } + + let dict = extract_key_values(tokens)?; + self.eval_storage_op(out, indent, keyword, &dict)?; + } + other_label => { + let dict = extract_key_values(tokens)?; + self.eval_label(out, indent, other_label, &dict)?; + } + } + } + } + Ok(()) + } + + /// Gets called for every trie storage operation. + /// + /// A storage operation is a layer above DB operations, one storage + /// operation can cause many DB operations, or it could be resolved from + /// cache without any DB operations. + fn eval_storage_op( + &mut self, + out: &mut dyn Write, + indent: usize, + op: &str, + dict: &BTreeMap<&str, &str>, + ) -> anyhow::Result<()> { + let (_, _, _, _) = (out, indent, op, dict); + Ok(()) + } + + /// Gets called for every DB operation. + fn eval_db_op( + &mut self, + out: &mut dyn Write, + indent: usize, + op: &str, + size: Option, + key: &[u8], + col: &str, + ) -> anyhow::Result<()> { + if col == "State" { + self.eval_state_db_op(out, indent, op, size, key) + } else { + Ok(()) + } + } + + /// Gets called for every DB operation on the state column. + fn eval_state_db_op( + &mut self, + out: &mut dyn Write, + indent: usize, + op: &str, + size: Option, + key: &[u8], + ) -> anyhow::Result<()> { + let (_, _, _, _, _) = (out, indent, op, size, key); + Ok(()) + } + + /// Opening spans that are not storage or DB operations. + fn eval_label( + &mut self, + out: &mut dyn Write, + indent: usize, + label: &str, + dict: &BTreeMap<&str, &str>, + ) -> anyhow::Result<()> { + let (_, _, _, _) = (out, indent, label, dict); + Ok(()) + } + + /// Print accumulated internal state. + /// + /// This function is called at the end of a replayed trace. Visitor + /// implementation usually choose to call this more often. For example, once + /// for each receipt. + fn flush(&mut self, out: &mut dyn Write) -> anyhow::Result<()> { + let _ = out; + Ok(()) + } +} + +fn extract_key_values<'a>( + mut tokens: SplitWhitespace<'a>, +) -> anyhow::Result> { + let mut dict = BTreeMap::new(); + while let Some(key_val) = tokens.next() { + let (key, value) = + key_val.split_once('=').context("key-value pair delimited by `=` expected")?; + dict.insert(key, value); + } + Ok(dict) +} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + + use super::{ReplayCmd, ReplayMode}; + + /// These inputs are real mainnet traffic for the given block heights. + /// Each trace contains two chunks in one shard. + /// In combination, they cover an interesting variety of content. + /// Shard 0: Many receipts, but no function calls. + /// Shard 1: Empty chunks without txs / receipts. + /// Shard 2: Some receipts and txs, but no function calls. + /// Shard 3: Many function calls with lots of storage accesses. + const INPUT_TRACES: &[&str] = &[ + "75220100-75220101.s0.io_trace", + "75220100-75220101.s1.io_trace", + "75220100-75220101.s2.io_trace", + "75220100-75220101.s3.io_trace", + ]; + + /// A synthetic trace that should be a bit easier to read and verify. + const SYNTHETIC_TRACE: &str = r#" +GET BlockHeader "`fAkeHeAd3R`" size=6000 +GET BlockInfo "`FAk31nf0`" size=1 +apply_transactions shard_id=0 + process_state_update + apply num_transactions=1 shard_cache_hit=10 shard_cache_miss=1 + process_transaction tx_hash=txHash0 shard_cache_miss=1 shard_cache_hit=20 + GET State "'stateKey0'" size=300 + process_receipt receipt_id=id0 predecessor=system receiver=alice.near id=id0 shard_cache_miss=2 shard_cache_hit=6 + GET State "'stateKey1'" size=10 + GET State "'stateKey2'" size=20 + process_receipt receipt_id=id1 predecessor=system receiver=bob.near id=id1 shard_cache_hit=18 shard_cache_miss=3 shard_cache_too_large=1 + GET State "'stateKey3'" size=100 + GET State "'stateKey4'" size=200 + GET State "'stateKey5'" size=9000 + process_receipt receipt_id=id2 predecessor=system receiver=alice.near id=id2 shard_cache_miss=1 shard_cache_hit=6 + GET State "'stateKey6'" size=30 + attached_deposit + input + register_len + read_register + storage_read READ key=StorageKey0 size=1000 tn_db_reads=20 tn_mem_reads=0 shard_cache_hit=19 shard_cache_miss=1 + GET State "'stateKey7'" size=5 + process_receipt receipt_id=id3 predecessor=utiha.near receiver=alice.near id=id3 shard_cache_miss=0 shard_cache_hit=15 + GET State "'stateKey8'" size=300 + GET State "'stateKey9'" size=400 +GET State "'stateKey10'" size=500 +"#; + + #[test] + fn test_cache_stats() { + check_replay_mode(ReplayMode::CacheStats); + } + + #[test] + fn test_accounting_cache_stats() { + check_replay_mode(ReplayMode::ChunkCacheStats); + } + + #[test] + fn test_chunk_db_stats() { + check_replay_mode(ReplayMode::ChunkDbStats); + } + + #[test] + fn test_gas_charges() { + check_replay_mode(ReplayMode::GasCharges); + } + + #[test] + fn test_receipt_cache_stats() { + check_replay_mode(ReplayMode::ReceiptCacheStats); + } + + #[test] + fn test_receipt_db_stats() { + check_replay_mode(ReplayMode::ReceiptDbStats); + } + + #[track_caller] + fn check_replay_mode(mode: ReplayMode) { + for trace_name in INPUT_TRACES { + let dir = env!("CARGO_MANIFEST_DIR"); + let trace_path = std::path::Path::new(dir).join("res").join(trace_name); + let cmd = ReplayCmd { trace: trace_path, mode, account: None }; + let mut buffer = Vec::new(); + cmd.run(&mut buffer).unwrap_or_else(|e| { + panic!("command should not fail for input {trace_name}, failure was {e}") + }); + let output = String::from_utf8(buffer).unwrap_or_else(|e| { + panic!("invalid output for input {trace_name}, failure was {e}") + }); + insta::assert_snapshot!(format!("{mode:?}-{trace_name}"), output); + } + } + + #[test] + fn test_account_filter_cache_stats() { + check_account_filter(ReplayMode::CacheStats); + } + + #[test] + fn test_account_filter_receipt_cache_stats() { + check_account_filter(ReplayMode::ReceiptCacheStats); + } + + #[test] + fn test_account_filter_receipt_db_stats() { + check_account_filter(ReplayMode::ReceiptDbStats); + } + + #[track_caller] + fn check_account_filter(mode: ReplayMode) { + let account = Some("alice.near".to_owned()); + // trace path not used, will be read from in-memory input instead + let trace = PathBuf::new(); + let cmd = ReplayCmd { trace, mode, account }; + let mut buffer = Vec::new(); + cmd.run_on_input(SYNTHETIC_TRACE.as_bytes(), &mut buffer).expect("failed replaying"); + let output = + String::from_utf8(buffer).unwrap_or_else(|e| panic!("invalid output, failure was {e}")); + insta::assert_snapshot!(format!("account_filter_{mode:?}"), output); + } +} diff --git a/runtime/runtime-params-estimator/src/replay/cache_stats.rs b/runtime/runtime-params-estimator/src/replay/cache_stats.rs new file mode 100644 index 000000000..7df00a95d --- /dev/null +++ b/runtime/runtime-params-estimator/src/replay/cache_stats.rs @@ -0,0 +1,233 @@ +use anyhow::Context; +use std::collections::BTreeMap; +use std::io::Write; + +/// Keeps track of cache statistics and prints them on demand. +#[derive(Default)] +pub(super) struct CacheStats { + /// Count of all DB get requests, from guest or host. + num_get: u64, + /// Count of all DB set requests, from guest or host. + num_set: u64, + /// Sum of all DB get sizes, from guest or host. + total_size_get: u64, + /// Sum of all DB set sizes, from guest or host. + total_size_set: u64, + + /// Count of all storage reads. (can only be from inside guest program) + num_read: u64, + /// Count of all storage writes. (can only be from inside guest program) + num_write: u64, + /// Sum of all storage reads sizes. (can only be from inside guest program) + total_size_read: u64, + /// Sum of all storage writes sizes. (can only be from inside guest program) + total_size_write: u64, + + /// Hits in the accounting cache. (can only be from inside guest program) + num_tn_accounting_cache_hit: u64, + /// Hits in the shard cache, from inside guest program. + num_tn_shard_cache_hit_guest: u64, + /// Misses in the shard cache, from inside guest program. + num_tn_shard_cache_miss_guest: u64, + /// All trie node accesses that the user pays for as being fetched from DB. + /// Includes shard cache misses and hits, but no accounting cache hits. + num_tn_db_paid: u64, + + /// Hits in the shard cache, requested by host. + num_tn_shard_cache_hit_host: u64, + /// Misses in the shard cache, requested by host. + num_tn_shard_cache_miss_host: u64, + + /// Misses in the shard cache anywhere, due to the size being too large. + num_tn_shard_cache_too_large: u64, +} + +impl CacheStats { + /// Digest cache statistics written directly on a single DB operations. + pub(super) fn eval_db_op(&mut self, op: &str, size: Option) { + match op { + "GET" => { + self.num_get += 1; + self.total_size_get += size.unwrap_or(0); + } + "SET" => { + self.num_set += 1; + self.total_size_set += size.unwrap_or(0); + } + _ => { + // nop + } + } + } + + /// Digest cache statistics written on a storage operation, such cache hits. + pub(super) fn eval_storage_op( + &mut self, + storage_operation: &str, + dict: &BTreeMap<&str, &str>, + ) -> anyhow::Result<()> { + let size = if storage_operation == "storage_has_key" { + 0 + } else { + dict.get("size").unwrap_or(&"0").parse()? + }; + let mut tn_db_reads: u64 = dict + .get("tn_db_reads") + .map(|s| s.parse().unwrap()) + .context("no tn_db_reads on storage op")?; + let mut tn_mem_reads: u64 = dict + .get("tn_mem_reads") + .map(|s| s.parse().unwrap()) + .context("no tn_mem_reads on storage op")?; + + let tn_shard_cache_hits = + dict.get("shard_cache_hit").map(|s| s.parse().unwrap()).unwrap_or(0); + let tn_shard_cache_misses = + dict.get("shard_cache_miss").map(|s| s.parse().unwrap()).unwrap_or(0); + let tn_shard_cache_too_large = + dict.get("shard_cache_too_large").map(|s| s.parse().unwrap()).unwrap_or(0); + + match storage_operation { + "storage_read" | "storage_has_key" => { + self.num_read += 1; + self.total_size_read += size; + // We are currently counting one node too little, see + // https://github.com/utnet-org/utility/issues/6225. But we don't + // know where, could be either tn_db_reads or tn_mem_reads. But + // we know that tn_db_reads = shard_cache_hits + + // shard_cache_misses. So we can correct for it here. + if tn_db_reads < tn_shard_cache_misses + tn_shard_cache_hits { + tn_db_reads += 1; + } else { + tn_mem_reads += 1; + } + debug_assert_eq!(tn_db_reads, tn_shard_cache_misses + tn_shard_cache_hits) + } + "storage_write" => { + self.num_write += 1; + self.total_size_write += size; + } + _ => {} + } + + self.num_tn_accounting_cache_hit += tn_mem_reads; + self.num_tn_shard_cache_hit_guest += tn_shard_cache_hits; + self.num_tn_db_paid += tn_db_reads; + self.num_tn_shard_cache_too_large += tn_shard_cache_too_large; + self.num_tn_shard_cache_miss_guest += tn_shard_cache_misses; + + Ok(()) + } + + /// Digest cache statistics not part of storage operations but some other label. + /// + /// Labels to look out for here are `process_receipt`, `process_transaction`, or + /// even just `apply`. They all access the trie also outside of guest-spawned + /// storage operations. + pub(super) fn eval_generic_label(&mut self, dict: &BTreeMap<&str, &str>) { + let tn_shard_cache_hits = + dict.get("shard_cache_hit").map(|s| s.parse().unwrap()).unwrap_or(0); + let tn_shard_cache_misses = + dict.get("shard_cache_miss").map(|s| s.parse().unwrap()).unwrap_or(0); + let tn_shard_cache_too_large = + dict.get("shard_cache_too_large").map(|s| s.parse().unwrap()).unwrap_or(0); + + // there is no accounting cache update here, as we are not in a smart contract execution + self.num_tn_shard_cache_hit_host += tn_shard_cache_hits; + self.num_tn_shard_cache_too_large += tn_shard_cache_too_large; + self.num_tn_shard_cache_miss_host += tn_shard_cache_misses; + } + + pub(super) fn print(&self, out: &mut dyn Write, indent: usize) -> anyhow::Result<()> { + writeln!( + out, + "{:indent$}DB GET {:>5} requests for a total of {:>8} B", + "", self.num_get, self.total_size_get + )?; + writeln!( + out, + "{:indent$}DB SET {:>5} requests for a total of {:>8} B", + "", self.num_set, self.total_size_set + )?; + writeln!( + out, + "{:indent$}STORAGE READ {:>5} requests for a total of {:>8} B", + "", self.num_read, self.total_size_read + )?; + writeln!( + out, + "{:indent$}STORAGE WRITE {:>5} requests for a total of {:>8} B", + "", self.num_write, self.total_size_write + )?; + writeln!( + out, + "{:indent$}TRIE NODES (guest) {:>4} /{:>4} /{:>4} (chunk-cache/shard-cache/DB)", + "", + self.num_tn_accounting_cache_hit, + self.num_tn_shard_cache_hit_guest, + self.num_tn_shard_cache_miss_guest + )?; + writeln!( + out, + "{:indent$}TRIE NODES (host) {:>4} /{:>4} (shard-cache/DB)", + "", self.num_tn_shard_cache_hit_host, self.num_tn_shard_cache_miss_host + )?; + Self::print_cache_rate( + out, + indent, + "SHARD CACHE", + self.num_tn_shard_cache_hit_guest + self.num_tn_shard_cache_hit_host, + self.num_tn_shard_cache_miss_guest + self.num_tn_shard_cache_miss_host, + Some((self.num_tn_shard_cache_too_large, "too large nodes")), + )?; + // TODO(#9054): Rename this to ACCOUNTING CACHE. + Self::print_cache_rate( + out, + indent, + "CHUNK CACHE", + self.num_tn_accounting_cache_hit, + self.num_tn_db_paid, + None, + )?; + Ok(()) + } + + /// Given hit and miss counts, print hit rate for a cache. + /// + /// Provide name, hit count, and miss count for basic data. + /// Additionally, a number of "special misses" is reported + /// sep + fn print_cache_rate( + out: &mut dyn Write, + indent: usize, + cache_name: &str, + hits: u64, + misses: u64, + special_misses: Option<(u64, &str)>, + ) -> anyhow::Result<()> { + let total = hits + misses; + if total > 0 { + write!( + out, + "{:indent$}{cache_name:<16} {:>6.2}% hit rate", + "", + hits as f64 / total as f64 * 100.0, + )?; + if let Some((special_misses, msg)) = special_misses { + if special_misses > 0 { + debug_assert!(special_misses <= misses, "failed: {special_misses} <= {misses}"); + write!( + out, + ", {:>6.2}% if removing {} {msg} from total", + hits as f64 / (total - special_misses) as f64 * 100.0, + special_misses, + )?; + } + } + writeln!(out)?; + } else { + writeln!(out, "{:indent$}{cache_name} not accessed", "")?; + } + Ok(()) + } +} diff --git a/runtime/runtime-params-estimator/src/replay/fold_db_ops.rs b/runtime/runtime-params-estimator/src/replay/fold_db_ops.rs new file mode 100644 index 000000000..9e0064a1a --- /dev/null +++ b/runtime/runtime-params-estimator/src/replay/fold_db_ops.rs @@ -0,0 +1,284 @@ +use super::cache_stats::CacheStats; +use super::Visitor; +use std::collections::{BTreeMap, HashMap}; +use std::io::Write; + +const EMPTY_STATE_ERR: &str = "states must never be empty"; + +/// A visitor that keeps track of DB operations and aggregates it by specific labels. +pub(super) struct FoldDbOps { + /// Labels at which to break aggregation, and the fields to print on each. + /// + /// If field is not available, it will be silently ignored. + fold_anchors: HashMap>, + /// Print the DB operations that are at indentation 0, not associated with a span. + /// + /// This is a summary of the total. Instead it shows what operations have + /// not been covered by already reported output. To get a summary of the + /// total, run without folding. + print_top_level: bool, + /// Only show data for a specific smart contract, specified by account id. + /// Currently only applies within receipts. + account_filter: Option, + /// Only evaluate lines with at least this depth of indentation. + /// + /// Used to apply filters and skip some parts of the trace. + /// Default is 0, which collects everything. + min_indent: usize, + /// Reset the filter when indentation goes back to this value. + /// + /// Used in combination with `min_indent` and `account_filter`. + /// Resetting means that `min_indent` is set to usize::MAX and nothing will + /// be evaluated until the `account_filter` is triggered again and sets it + /// to a smaller value again. + filter_reset_indent: Option, + /// Optionally collect and print detailed statistics for cache hits and misses. + track_caches: bool, + /// Keeps track of current block. + block_hash: Option, + /// Stack of states, each starting at a specific indent. + states: Vec, +} + +/// Stores statistics for a range of the trace, starting at a fixed indent and +/// collecting data for everything indented further, until another state object +/// is pushed on top. +#[derive(Default)] +struct State { + /// The indent at which this state started + indent: usize, + /// Keeps track of operations per DB column. + ops_cols: BTreeMap>, + /// Optionally collect and print detailed statistics for cache hits and misses. + cache_stats: Option, +} + +impl FoldDbOps { + pub(super) fn new() -> Self { + Self { + fold_anchors: HashMap::new(), + print_top_level: true, + account_filter: None, + track_caches: false, + states: vec![State::default()], + block_hash: None, + min_indent: 0, + filter_reset_indent: None, + } + } + + /// Pre-set that folds on chunks. + pub(super) fn chunks(self) -> Self { + self.fold("apply_transactions", &["receiver", "shard_id"]) + } + + /// Pre-set that folds on receipts. + pub(super) fn receipts(self) -> Self { + self.fold("process_receipt", &["receiver", "receipt_id"]) + .fold("process_transaction", &["tx_hash"]) + .print_top_level(false) + } + + pub(super) fn fold(mut self, anchor: impl Into, printed_fields: &[&str]) -> Self { + let fields = printed_fields.into_iter().map(|s| (*s).into()).collect(); + self.fold_anchors.insert(anchor.into(), fields); + self + } + + pub(super) fn print_top_level(mut self, yes: bool) -> Self { + self.print_top_level = yes; + self + } + + pub(super) fn with_cache_stats(mut self) -> Self { + self.track_caches = true; + self.state().cache_stats = Some(CacheStats::default()); + self + } + + pub(super) fn account_filter(mut self, account: Option) -> Self { + if account.is_some() { + // evaluate nothing if there is a filter, until the filter matches the first time + self.min_indent = usize::MAX; + } + self.account_filter = account; + self + } + + fn state(&mut self) -> &mut State { + self.states.last_mut().expect(EMPTY_STATE_ERR) + } + + fn push_state(&mut self, indent: usize) { + let cache_stats = if self.track_caches { Some(CacheStats::default()) } else { None }; + let new_state = State { indent, ops_cols: Default::default(), cache_stats }; + self.states.push(new_state); + } + + fn pop_state(&mut self) -> State { + let state = self.states.pop().expect(EMPTY_STATE_ERR); + if self.states.is_empty() { + self.push_state(0); + } + state + } + + fn skip_eval(&mut self, trace_indent: usize) -> bool { + trace_indent < self.min_indent + } + + /// Check if indentation has gone back enough to pop current state or reset filter. + /// + /// Call this before `skip()` to ensure it uses the correct `min_indent`. + fn update_state(&mut self, out: &mut dyn Write, indent: usize) -> anyhow::Result<()> { + if self.states.len() > 1 && self.state().indent >= indent { + self.pop_state().print(out)?; + } + if let Some(reset_indent) = self.filter_reset_indent { + if indent <= reset_indent { + self.filter_reset_indent = None; + self.min_indent = usize::MAX; + } + } + Ok(()) + } +} + +impl Visitor for FoldDbOps { + fn eval_db_op( + &mut self, + out: &mut dyn Write, + indent: usize, + op: &str, + size: Option, + key: &[u8], + col: &str, + ) -> anyhow::Result<()> { + self.update_state(out, indent)?; + + // Block hash in traces is visibly by looking at DB lookups of + // `BlockInfo` column. Keep track of it so that each time something is + // printed, the block hash can be included if desired. + match op { + "GET" => { + if col == "BlockInfo" { + self.block_hash = Some(bs58::encode(key).into_string()); + } + } + _ => { + // nop + } + } + + if self.skip_eval(indent) { + return Ok(()); + } + + // Count DB operation for the corresponding column. + *self + .state() + .ops_cols + .entry(op.to_owned()) + .or_default() + .entry(col.to_owned()) + .or_default() += 1; + + if let Some(cache_stats) = &mut self.state().cache_stats { + cache_stats.eval_db_op(op, size); + } + + Ok(()) + } + + fn eval_storage_op( + &mut self, + out: &mut dyn Write, + indent: usize, + op: &str, + dict: &BTreeMap<&str, &str>, + ) -> anyhow::Result<()> { + self.update_state(out, indent)?; + if self.skip_eval(indent) { + return Ok(()); + } + if let Some(cache_stats) = &mut self.state().cache_stats { + cache_stats.eval_storage_op(op, dict)?; + } + Ok(()) + } + + fn eval_label( + &mut self, + out: &mut dyn Write, + indent: usize, + label: &str, + dict: &BTreeMap<&str, &str>, + ) -> anyhow::Result<()> { + self.update_state(out, indent)?; + if let (Some(receiver), Some(filtered_account)) = + (dict.get("receiver"), &self.account_filter) + { + if receiver == filtered_account && self.filter_reset_indent.is_none() { + self.min_indent = indent; + self.filter_reset_indent = Some(indent); + } + } + if self.skip_eval(indent) { + return Ok(()); + } + if self.fold_anchors.contains_key(label) { + // Section to fold on starts. Push a new state on the stack and + // print the header for the new section. + self.push_state(indent); + write!(out, "{:indent$}{label}", "")?; + // Unnecessary perf optimization: Second lookup in fold anchors + // could be avoided by reading the key directly but then we keep + // a mutable reference to self and cannot naively call + // self.push_state above. + // Better to lookup twice and keep code simple. + for key in self.fold_anchors.get(label).expect("just checked contains key").iter() { + if let Some(value) = dict.get(key.as_str()) { + write!(out, " {key}={value}")?; + } + } + if let Some(block) = &self.block_hash { + write!(out, " block={block}")?; + } + writeln!(out)?; + } + + if let Some(cache_stats) = &mut self.state().cache_stats { + cache_stats.eval_generic_label(dict); + } + Ok(()) + } + + fn flush(&mut self, out: &mut dyn Write) -> anyhow::Result<()> { + if self.print_top_level { + writeln!(out, "top-level:")?; + self.pop_state().print(out)?; + } + Ok(()) + } +} + +impl State { + fn print(self, out: &mut dyn Write) -> anyhow::Result<()> { + let indent = self.indent + 2; + for (op, map) in self.ops_cols.into_iter() { + if !map.is_empty() { + write!(out, "{:indent$}{op} ", "")?; + } + for (col, num) in map.into_iter() { + write!(out, "{num:8>} {col} ")?; + } + writeln!(out)?; + } + + if let Some(stats) = self.cache_stats { + stats.print(out, indent)?; + } + writeln!(out)?; + Ok(()) + } +} diff --git a/runtime/runtime-params-estimator/src/replay/gas_charges.rs b/runtime/runtime-params-estimator/src/replay/gas_charges.rs new file mode 100644 index 000000000..854088e03 --- /dev/null +++ b/runtime/runtime-params-estimator/src/replay/gas_charges.rs @@ -0,0 +1,97 @@ +use super::Visitor; +use std::collections::BTreeMap; +use std::io::Write; + +/// Visitor that tracks which DB operations are charged with gas and which are +/// for free. +#[derive(Default)] +pub(super) struct ChargedVsFree { + in_fn_call: bool, + storage_op_indent: usize, + + free_gets: u64, + free_gets_size: u64, + charged_gets: u64, + charged_gets_size: u64, +} + +impl Visitor for ChargedVsFree { + fn eval_db_op( + &mut self, + out: &mut dyn Write, + indent: usize, + op: &str, + size: Option, + _key: &[u8], + _col: &str, + ) -> anyhow::Result<()> { + self.eval_label(out, indent, op, &BTreeMap::new())?; + if op != "GET" { + return Ok(()); + } + if self.in_fn_call { + self.charged_gets += 1; + self.charged_gets_size += size.unwrap_or(0); + } else { + self.free_gets += 1; + self.free_gets_size += size.unwrap_or(0); + } + Ok(()) + } + + fn eval_storage_op( + &mut self, + _out: &mut dyn Write, + indent: usize, + _op: &str, + _dict: &BTreeMap<&str, &str>, + ) -> anyhow::Result<()> { + self.storage_op_indent = indent + 2; + self.in_fn_call = true; + Ok(()) + } + + fn eval_label( + &mut self, + _out: &mut dyn Write, + indent: usize, + _label: &str, + _dict: &BTreeMap<&str, &str>, + ) -> anyhow::Result<()> { + if indent < self.storage_op_indent { + self.in_fn_call = false; + } + Ok(()) + } + + fn flush(&mut self, out: &mut dyn Write) -> anyhow::Result<()> { + writeln!( + out, + "{:>8} free gets with total size of {:>8}", + self.free_gets, self.free_gets_size + )?; + writeln!( + out, + "{:>8} charged gets with total size of {:>8}", + self.charged_gets, self.charged_gets_size + )?; + writeln!( + out, + "{:>7.2}% of gets uncharged", + 100.0 * self.free_gets as f64 / (self.charged_gets + self.free_gets) as f64 + )?; + writeln!( + out, + "{:>7.2}% of gets total size uncharged", + 100.0 * self.free_gets_size as f64 + / (self.charged_gets_size + self.free_gets_size) as f64 + )?; + + self.free_gets = 0; + self.free_gets_size = 0; + self.charged_gets = 0; + self.charged_gets_size = 0; + + Ok(()) + } +} diff --git a/runtime/runtime-params-estimator/src/rocksdb.rs b/runtime/runtime-params-estimator/src/rocksdb.rs new file mode 100644 index 000000000..afe4b9b07 --- /dev/null +++ b/runtime/runtime-params-estimator/src/rocksdb.rs @@ -0,0 +1,314 @@ +use rand::{prelude::SliceRandom, Rng}; +use rand_xorshift::XorShiftRng; +use rocksdb::DB; +use std::{io::prelude::*, iter, path::PathBuf}; + +use crate::{config::Config, gas_cost::GasCost}; + +#[derive(Debug, Clone, clap::Parser)] +pub struct RocksDBTestConfig { + /// Value size used for all DB operations in RocksDB tests + /// (`RocksDb*` estimations only) + #[clap(name = "rdb-value-size", long, default_value = "1000")] + pub value_size: usize, + /// Number of insertions/reads performed in RocksDB tests + /// (`RocksDb*` estimations only) + #[clap(name = "rdb-op-count", long, default_value = "1000000")] + pub op_count: usize, + /// Size of memtable used in RocksDB tests + /// (`RocksDb*` estimations only) + #[clap(name = "rdb-memtable-size", long, default_value = "256000000")] + pub memtable_size: usize, + /// Number of insertions into test DB before measurement begins + /// (`RocksDb*` estimations only) + #[clap(name = "rdb-setup-insertions", long, default_value = "2000000")] + pub setup_insertions: usize, + /// Keys will be ordered sequentially if this is set, randomized otherwise. + /// (`RocksDb*` estimations only) + #[clap(name = "rdb-sequential-keys", long)] + pub sequential_keys: bool, + /// Flush after a bulk of write operations. + /// The flush time will be included in the reported measurement. + /// (`RocksDb*` estimations only) + #[clap(long, name = "rdb-force-flush", long)] + pub force_flush: bool, + /// Force compactions after a bulk of operations. + /// The compaction time will be included in the reported measurement. + /// (`RocksDb*` estimations only) + #[clap(long, name = "rdb-force-compaction", long)] + pub force_compaction: bool, + /// Enable the default block cache used for reads, disabled by default. + /// (`RocksDb*` estimations only) + #[clap(long, name = "rdb-block-cache", long)] + pub block_cache: bool, + /// Print RocksDB debug output where available + #[clap(skip)] + pub debug_rocksdb: bool, + /// Pseudo-random input data dump + /// (`RocksDb*` estimations only) + #[clap(long, name = "rdb-input-data-path", long)] + pub input_data_path: Option, + /// Drop OS cache before measurements for better IO accuracy. + #[clap(skip)] + pub drop_os_cache: bool, +} + +// These tests make use of reproducible pseud-randomness. +// Two different strategies are used for keys and data values. +// +// > Keys: XorShiftRng with an initial seed value to produce a series of pseudo-random keys +// > Values: A buffer of random bytes is loaded into memory. +// The values are slices from this buffer at different offsets. +// The initial buffer can be dynamically generated from thread_rng or loaded from a dump from previous runs. +// +// The rational behind this setup is to have random keys/values readily available during benchmarks without consuming much memory or CPU time. + +const SETUP_PRANDOM_SEED: u64 = 0x1d9f5711fc8b0117; +const ANOTHER_PRANDOM_SEED: u64 = 0x0465b6733af62af0; +const INPUT_DATA_BUFFER_SIZE: usize = (bytesize::MIB as usize) - 1; + +pub(crate) fn rocks_db_inserts_cost(config: &Config) -> GasCost { + let db_config = &config.rocksdb_test_config; + let data = input_data(db_config, INPUT_DATA_BUFFER_SIZE); + let tmp_dir = tempfile::TempDir::new().expect("Failed to create directory for temp DB"); + let db = new_test_db(&tmp_dir, &data, &db_config, config.accurate); + + if db_config.debug_rocksdb { + eprintln!("# {:?}", db_config); + println!("# After setup / before measurement:"); + print_levels_info(&db); + } + + let setup_insertions = if config.accurate { db_config.setup_insertions } else { 1 }; + let op_count = if config.accurate { db_config.op_count } else { 1 }; + + let gas_counter = GasCost::measure(config.metric); + + if db_config.sequential_keys { + sequential_inserts( + op_count, + db_config.value_size, + &data, + setup_insertions, + &db, + db_config.force_compaction, + db_config.force_flush, + ); + } else { + prandom_inserts( + op_count, + db_config.value_size, + &data, + ANOTHER_PRANDOM_SEED, + &db, + db_config.force_compaction, + db_config.force_flush, + ); + } + + let cost = gas_counter.elapsed(); + + if db_config.debug_rocksdb { + println!("# Cost: {:?}", cost); + print_levels_info(&db); + } + + drop(db); + tmp_dir.close().expect("Could not clean up temp DB"); + + if db_config.input_data_path.is_none() { + backup_input_data(&data); + } + + cost +} + +pub(crate) fn rocks_db_read_cost(config: &Config) -> GasCost { + let db_config = &config.rocksdb_test_config; + let tmp_dir = tempfile::TempDir::new().expect("Failed to create directory for temp DB"); + let data = input_data(db_config, INPUT_DATA_BUFFER_SIZE); + let db = new_test_db(&tmp_dir, &data, &db_config, config.accurate); + + if db_config.debug_rocksdb { + eprintln!("# {:?}", db_config); + println!("# After setup / before measurement:"); + print_levels_info(&db); + } + + let setup_insertions = if config.accurate { db_config.setup_insertions } else { 1 }; + let op_count = if config.accurate { db_config.op_count } else { 1 }; + + let mut prng: XorShiftRng = rand::SeedableRng::seed_from_u64(SETUP_PRANDOM_SEED); + let mut keys: Vec = iter::repeat_with(|| prng.gen()).take(setup_insertions).collect(); + if db_config.sequential_keys { + keys.sort(); + } else { + // give it another shuffle to make lookup order different from insertion order + keys.shuffle(&mut prng); + } + + let gas_counter = GasCost::measure(config.metric); + + for i in 0..op_count { + let key = keys[i as usize % keys.len()]; + db.get(&key.to_string()).unwrap(); + } + + let cost = gas_counter.elapsed(); + + if db_config.debug_rocksdb { + println!("# Cost: {:?}", cost); + print_levels_info(&db); + } + + drop(db); + tmp_dir.close().expect("Could not clean up temp DB"); + + if db_config.input_data_path.is_none() { + backup_input_data(&data); + } + + cost +} + +/// Sequentially insert a number of generated key-value pairs and flushes +/// +/// Keys are {"1", "2", ... } starting at `key_offset` +/// Values are different slices taken from `input_data` +fn sequential_inserts( + inserts: usize, + value_size: usize, + input_data: &[u8], + key_offset: usize, + db: &DB, + force_compaction: bool, + force_flush: bool, +) { + for i in 0..inserts { + let key = (key_offset + i).to_string(); + let start = (i * value_size) % (input_data.len() - value_size); + let value = &input_data[start..(start + value_size)]; + db.put(&key, value).expect("Put failed"); + } + if force_flush { + db.flush().expect("Flush failed"); + } + if force_compaction { + db.compact_range::<&[u8], &[u8]>(None, None); + } +} + +/// Insert a number of generated key-value pairs and flushes +/// +/// Keys are pseudo-random and deterministic based on the seed +/// Values are different slices taken from `input_data` +fn prandom_inserts( + inserts: usize, + value_size: usize, + input_data: &[u8], + key_seed: u64, + db: &DB, + force_compaction: bool, + force_flush: bool, +) { + let mut prng: XorShiftRng = rand::SeedableRng::seed_from_u64(key_seed); + for i in 0..inserts { + let key = prng.gen::().to_string(); + let start = (i * value_size) % (input_data.len() - value_size); + let value = &input_data[start..(start + value_size)]; + db.put(&key, value).expect("Put failed"); + } + if force_flush { + db.flush().expect("Flush failed"); + } + if force_compaction { + db.compact_range::<&[u8], &[u8]>(None, None); + } +} + +fn input_data(db_config: &RocksDBTestConfig, data_size: usize) -> Vec { + if let Some(path) = &db_config.input_data_path { + let data = std::fs::read(path).unwrap(); + assert_eq!(data.len(), data_size, "Provided input file has wrong size"); + data + } else { + let mut data = vec![0u8; data_size]; + rand::thread_rng().fill(data.as_mut_slice()); + data + } +} + +/// Store generated input data in a file for reproducibility of any results +fn backup_input_data(data: &[u8]) { + let mut stats_file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .append(true) + .create(true) + .open("names-to-stats.txt") + .unwrap(); + let stats_num = std::io::BufReader::new(&stats_file).lines().count(); + let data_dump_path = format!("data_dump_{:<04}.bin", stats_num); + + std::fs::write(&data_dump_path, data).unwrap(); + writeln!(stats_file, "# DATA {} written to {}", stats_num, data_dump_path) + .expect("Writing to \"names-to-stats.txt\" failed"); +} + +fn new_test_db( + db_dir: impl AsRef, + data: &[u8], + db_config: &RocksDBTestConfig, + accurate: bool, +) -> DB { + let mut opts = rocksdb::Options::default(); + + opts.create_if_missing(true); + + // Options as used in framework + opts.set_bytes_per_sync(bytesize::MIB); + opts.set_write_buffer_size(db_config.memtable_size); + opts.set_max_bytes_for_level_base(db_config.memtable_size as u64); + + // Simplify DB a bit for more consistency: + // * Only have one memtable at the time + opts.set_max_write_buffer_number(1); + // * Never slow down writes due to increased number of L0 files + opts.set_level_zero_slowdown_writes_trigger(-1); + + if !db_config.block_cache { + let mut block_opts = rocksdb::BlockBasedOptions::default(); + block_opts.disable_cache(); + opts.set_block_based_table_factory(&block_opts); + } + + let db = rocksdb::DB::open(&opts, db_dir).expect("Failed to create RocksDB"); + let setup_insertions = if accurate { db_config.setup_insertions } else { 1 }; + + prandom_inserts( + setup_insertions, + db_config.value_size, + &data, + SETUP_PRANDOM_SEED, + &db, + db_config.force_compaction, + true, // always force-flush in setup + ); + + #[cfg(target_os = "linux")] + if db_config.drop_os_cache { + crate::utils::clear_linux_page_cache().expect( + "Failed to drop OS caches. Are you root and is /proc mounted with write access?", + ); + } + + db +} + +fn print_levels_info(db: &DB) { + for n in 0..3 { + let int = + db.property_int_value(&format!("rocksdb.num-files-at-level{}", n)).unwrap().unwrap(); + println!("{} files at level {}", int, n); + } +} diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__CacheStats-75220100-75220101.s0.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__CacheStats-75220100-75220101.s0.io_trace.snap new file mode 100644 index 000000000..79c174bef --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__CacheStats-75220100-75220101.s0.io_trace.snap @@ -0,0 +1,16 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- +top-level: + GET 4 Block 4 BlockHeader 2 BlockHeight 4 BlockInfo 2 BlockMisc 2 ChunkExtra 2 Chunks 4 EpochInfo 2 IncomingReceipts 636 State + DB GET 662 requests for a total of 2960023 B + DB SET 0 requests for a total of 0 B + STORAGE READ 18 requests for a total of 1745 B + STORAGE WRITE 21 requests for a total of 1673 B + TRIE NODES (guest) 751 / 4 / 187 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 761 / 445 (shard-cache/DB) + SHARD CACHE 54.76% hit rate, 54.96% if removing 5 too large nodes from total + CHUNK CACHE 79.72% hit rate + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__CacheStats-75220100-75220101.s1.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__CacheStats-75220100-75220101.s1.io_trace.snap new file mode 100644 index 000000000..43dbb6a0c --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__CacheStats-75220100-75220101.s1.io_trace.snap @@ -0,0 +1,16 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- +top-level: + GET 4 Block 4 BlockHeader 2 BlockHeight 4 BlockInfo 2 BlockMisc 2 ChunkExtra 2 Chunks 4 EpochInfo 2 IncomingReceipts 4 State + DB GET 30 requests for a total of 140837 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 22 / 4 (shard-cache/DB) + SHARD CACHE 84.62% hit rate + CHUNK CACHE not accessed + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__CacheStats-75220100-75220101.s2.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__CacheStats-75220100-75220101.s2.io_trace.snap new file mode 100644 index 000000000..dc5bf6747 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__CacheStats-75220100-75220101.s2.io_trace.snap @@ -0,0 +1,16 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- +top-level: + GET 4 Block 4 BlockHeader 2 BlockHeight 4 BlockInfo 2 BlockMisc 2 ChunkExtra 2 Chunks 4 EpochInfo 2 IncomingReceipts 242 State + DB GET 268 requests for a total of 187304 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 469 / 238 (shard-cache/DB) + SHARD CACHE 66.34% hit rate + CHUNK CACHE not accessed + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__CacheStats-75220100-75220101.s3.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__CacheStats-75220100-75220101.s3.io_trace.snap new file mode 100644 index 000000000..c29d63a2a --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__CacheStats-75220100-75220101.s3.io_trace.snap @@ -0,0 +1,16 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- +top-level: + GET 4 Block 4 BlockHeader 2 BlockHeight 4 BlockInfo 2 BlockMisc 2 ChunkExtra 2 Chunks 4 EpochInfo 2 IncomingReceipts 678 State + DB GET 704 requests for a total of 3288866 B + DB SET 0 requests for a total of 0 B + STORAGE READ 37 requests for a total of 1573 B + STORAGE WRITE 35 requests for a total of 1557 B + TRIE NODES (guest) 1058 / 15 / 231 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 991 / 443 (shard-cache/DB) + SHARD CACHE 59.88% hit rate, 60.38% if removing 14 too large nodes from total + CHUNK CACHE 81.13% hit rate + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkCacheStats-75220100-75220101.s0.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkCacheStats-75220100-75220101.s0.io_trace.snap new file mode 100644 index 000000000..b52794db6 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkCacheStats-75220100-75220101.s0.io_trace.snap @@ -0,0 +1,38 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- +apply_transactions shard_id=0 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 287 State + DB GET 287 requests for a total of 1668244 B + DB SET 0 requests for a total of 0 B + STORAGE READ 9 requests for a total of 800 B + STORAGE WRITE 10 requests for a total of 881 B + TRIE NODES (guest) 400 / 2 / 93 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 289 / 194 (shard-cache/DB) + SHARD CACHE 50.35% hit rate, 50.61% if removing 3 too large nodes from total + CHUNK CACHE 80.81% hit rate + +apply_transactions shard_id=0 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 345 State + DB GET 345 requests for a total of 1141968 B + DB SET 0 requests for a total of 0 B + STORAGE READ 9 requests for a total of 945 B + STORAGE WRITE 11 requests for a total of 792 B + TRIE NODES (guest) 351 / 2 / 94 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 472 / 251 (shard-cache/DB) + SHARD CACHE 57.88% hit rate, 58.02% if removing 2 too large nodes from total + CHUNK CACHE 78.52% hit rate + +top-level: + GET 4 Block 4 BlockHeader 2 BlockHeight 4 BlockInfo 2 BlockMisc 2 ChunkExtra 2 Chunks 4 EpochInfo 2 IncomingReceipts 4 State + DB GET 30 requests for a total of 149811 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 0 / 0 (shard-cache/DB) + SHARD CACHE not accessed + CHUNK CACHE not accessed + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkCacheStats-75220100-75220101.s1.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkCacheStats-75220100-75220101.s1.io_trace.snap new file mode 100644 index 000000000..7d0b9be84 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkCacheStats-75220100-75220101.s1.io_trace.snap @@ -0,0 +1,27 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- +apply_transactions shard_id=1 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 4 State + DB GET 4 requests for a total of 283 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 9 / 4 (shard-cache/DB) + SHARD CACHE 69.23% hit rate + CHUNK CACHE not accessed + +apply_transactions shard_id=1 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 +top-level: + DB GET 0 requests for a total of 0 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 13 / 0 (shard-cache/DB) + SHARD CACHE 100.00% hit rate + CHUNK CACHE not accessed + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkCacheStats-75220100-75220101.s2.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkCacheStats-75220100-75220101.s2.io_trace.snap new file mode 100644 index 000000000..3bfd54239 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkCacheStats-75220100-75220101.s2.io_trace.snap @@ -0,0 +1,38 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- +apply_transactions shard_id=2 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 94 State + DB GET 94 requests for a total of 17348 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 162 / 94 (shard-cache/DB) + SHARD CACHE 63.28% hit rate + CHUNK CACHE not accessed + +apply_transactions shard_id=2 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 144 State + DB GET 144 requests for a total of 25250 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 307 / 144 (shard-cache/DB) + SHARD CACHE 68.07% hit rate + CHUNK CACHE not accessed + +top-level: + GET 4 Block 4 BlockHeader 2 BlockHeight 4 BlockInfo 2 BlockMisc 2 ChunkExtra 2 Chunks 4 EpochInfo 2 IncomingReceipts 4 State + DB GET 30 requests for a total of 144706 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 0 / 0 (shard-cache/DB) + SHARD CACHE not accessed + CHUNK CACHE not accessed + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkCacheStats-75220100-75220101.s3.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkCacheStats-75220100-75220101.s3.io_trace.snap new file mode 100644 index 000000000..7ff08a694 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkCacheStats-75220100-75220101.s3.io_trace.snap @@ -0,0 +1,38 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- +apply_transactions shard_id=3 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 371 State + DB GET 371 requests for a total of 1805602 B + DB SET 0 requests for a total of 0 B + STORAGE READ 21 requests for a total of 943 B + STORAGE WRITE 20 requests for a total of 935 B + TRIE NODES (guest) 542 / 2 / 126 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 482 / 245 (shard-cache/DB) + SHARD CACHE 56.61% hit rate, 57.14% if removing 8 too large nodes from total + CHUNK CACHE 80.90% hit rate + +apply_transactions shard_id=3 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 303 State + DB GET 303 requests for a total of 1325917 B + DB SET 0 requests for a total of 0 B + STORAGE READ 16 requests for a total of 630 B + STORAGE WRITE 15 requests for a total of 622 B + TRIE NODES (guest) 516 / 13 / 105 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 509 / 198 (shard-cache/DB) + SHARD CACHE 63.27% hit rate, 63.74% if removing 6 too large nodes from total + CHUNK CACHE 81.39% hit rate + +top-level: + GET 4 Block 4 BlockHeader 2 BlockHeight 4 BlockInfo 2 BlockMisc 2 ChunkExtra 2 Chunks 4 EpochInfo 2 IncomingReceipts 4 State + DB GET 30 requests for a total of 157347 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 0 / 0 (shard-cache/DB) + SHARD CACHE not accessed + CHUNK CACHE not accessed + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkDbStats-75220100-75220101.s0.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkDbStats-75220100-75220101.s0.io_trace.snap new file mode 100644 index 000000000..f5381cd80 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkDbStats-75220100-75220101.s0.io_trace.snap @@ -0,0 +1,14 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- +apply_transactions shard_id=0 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 287 State + +apply_transactions shard_id=0 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 345 State + +top-level: + GET 4 Block 4 BlockHeader 2 BlockHeight 4 BlockInfo 2 BlockMisc 2 ChunkExtra 2 Chunks 4 EpochInfo 2 IncomingReceipts 4 State + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkDbStats-75220100-75220101.s1.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkDbStats-75220100-75220101.s1.io_trace.snap new file mode 100644 index 000000000..bee1622e5 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkDbStats-75220100-75220101.s1.io_trace.snap @@ -0,0 +1,11 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- +apply_transactions shard_id=1 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 4 State + +apply_transactions shard_id=1 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 +top-level: + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkDbStats-75220100-75220101.s2.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkDbStats-75220100-75220101.s2.io_trace.snap new file mode 100644 index 000000000..c7e8c2ca4 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkDbStats-75220100-75220101.s2.io_trace.snap @@ -0,0 +1,14 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- +apply_transactions shard_id=2 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 94 State + +apply_transactions shard_id=2 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 144 State + +top-level: + GET 4 Block 4 BlockHeader 2 BlockHeight 4 BlockInfo 2 BlockMisc 2 ChunkExtra 2 Chunks 4 EpochInfo 2 IncomingReceipts 4 State + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkDbStats-75220100-75220101.s3.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkDbStats-75220100-75220101.s3.io_trace.snap new file mode 100644 index 000000000..4216054df --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ChunkDbStats-75220100-75220101.s3.io_trace.snap @@ -0,0 +1,14 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- +apply_transactions shard_id=3 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 371 State + +apply_transactions shard_id=3 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 303 State + +top-level: + GET 4 Block 4 BlockHeader 2 BlockHeight 4 BlockInfo 2 BlockMisc 2 ChunkExtra 2 Chunks 4 EpochInfo 2 IncomingReceipts 4 State + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__GasCharges-75220100-75220101.s0.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__GasCharges-75220100-75220101.s0.io_trace.snap new file mode 100644 index 000000000..91caa1018 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__GasCharges-75220100-75220101.s0.io_trace.snap @@ -0,0 +1,9 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- + 475 free gets with total size of 2930417 + 187 charged gets with total size of 29606 + 71.75% of gets uncharged + 99.00% of gets total size uncharged + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__GasCharges-75220100-75220101.s1.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__GasCharges-75220100-75220101.s1.io_trace.snap new file mode 100644 index 000000000..a05993cc6 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__GasCharges-75220100-75220101.s1.io_trace.snap @@ -0,0 +1,9 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- + 30 free gets with total size of 140837 + 0 charged gets with total size of 0 + 100.00% of gets uncharged + 100.00% of gets total size uncharged + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__GasCharges-75220100-75220101.s2.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__GasCharges-75220100-75220101.s2.io_trace.snap new file mode 100644 index 000000000..7be760bd7 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__GasCharges-75220100-75220101.s2.io_trace.snap @@ -0,0 +1,9 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- + 268 free gets with total size of 187304 + 0 charged gets with total size of 0 + 100.00% of gets uncharged + 100.00% of gets total size uncharged + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__GasCharges-75220100-75220101.s3.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__GasCharges-75220100-75220101.s3.io_trace.snap new file mode 100644 index 000000000..ca9b522ca --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__GasCharges-75220100-75220101.s3.io_trace.snap @@ -0,0 +1,9 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- + 473 free gets with total size of 3233441 + 231 charged gets with total size of 55425 + 67.19% of gets uncharged + 98.31% of gets total size uncharged + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptCacheStats-75220100-75220101.s0.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptCacheStats-75220100-75220101.s0.io_trace.snap new file mode 100644 index 000000000..8fd5cd7cc --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptCacheStats-75220100-75220101.s0.io_trace.snap @@ -0,0 +1,258 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- + process_transaction tx_hash=6wbch2CCuvqhbnknr7HhNW9PoyK6FSUmoCGJKpKEahvi block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 37 State + DB GET 37 requests for a total of 4473 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 2 / 37 (shard-cache/DB) + SHARD CACHE 5.13% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=8P2uMiA96mwRDnoqzwQijEHc1eFpZNJYJFKxHz69sYgj block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 30 State + DB GET 30 requests for a total of 5549 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 6 / 30 (shard-cache/DB) + SHARD CACHE 16.67% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=GREvhhJebp73HsJh8dXJYgdMURBPbpMebDti4AVi6UdC block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 3 State + DB GET 3 requests for a total of 165 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 18 / 3 (shard-cache/DB) + SHARD CACHE 85.71% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=app.nearcrowd.near receipt_id=HSkLwmSscYqj6DekmxRTUACkBUAUS7sUVBnxPC2wKVj8 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 64 State + DB GET 64 requests for a total of 549798 B + DB SET 0 requests for a total of 0 B + STORAGE READ 3 requests for a total of 288 B + STORAGE WRITE 3 requests for a total of 288 B + TRIE NODES (guest) 146 / 2 / 50 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 2 / 14 (shard-cache/DB) + SHARD CACHE 5.88% hit rate, 5.97% if removing 1 too large nodes from total + CHUNK CACHE 73.74% hit rate + + process_receipt receiver=app.nearcrowd.near receipt_id=HRR3EXitfuoA7yFoa1239GW13ZErmdq6J8vt1MoZgpiu block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 28 State + DB GET 28 requests for a total of 545285 B + DB SET 0 requests for a total of 0 B + STORAGE READ 3 requests for a total of 288 B + STORAGE WRITE 3 requests for a total of 288 B + TRIE NODES (guest) 138 / 0 / 27 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 13 / 1 (shard-cache/DB) + SHARD CACHE 31.71% hit rate, 32.50% if removing 1 too large nodes from total + CHUNK CACHE 83.64% hit rate + + process_receipt receiver=app.nearcrowd.near receipt_id=28ozHpuL1WZ3ExMm3UrNQYco31XHHaBiUXbyMD1firye block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 17 State + DB GET 17 requests for a total of 543852 B + DB SET 0 requests for a total of 0 B + STORAGE READ 3 requests for a total of 224 B + STORAGE WRITE 4 requests for a total of 305 B + TRIE NODES (guest) 116 / 0 / 16 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 13 / 1 (shard-cache/DB) + SHARD CACHE 43.33% hit rate, 44.83% if removing 1 too large nodes from total + CHUNK CACHE 87.88% hit rate + + process_receipt receiver=6f25217bc63c86d36e0cdc620d05b2f1381eb43eac560d4f1261380b255e2176 receipt_id=Fp2sJPeBSPX5eXNanJ5Jh1shgBKn6omvUCWcUNEAMd4W block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 26 State + DB GET 26 requests for a total of 5023 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 4 / 26 (shard-cache/DB) + SHARD CACHE 13.33% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=76248d053a8e8b47cfecab60c5f6f1befe74bcbd923c4ef5950bfeb2945fadff receipt_id=6d7ZrM9yztyWvAVtYbA4nQhtfchiyXbbBmJzYyMe3nht block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 24 State + DB GET 24 requests for a total of 4519 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 4 / 24 (shard-cache/DB) + SHARD CACHE 14.29% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=7a5de36efdbcd23489dcfd7a3c63966b9bcbddfbe4ba8db7d1df109ecd37ef5e receipt_id=wDN56cf2bf3sz16RzF6H3UaC1pGnYJ7XFvUdzLTiTS3 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 28 State + DB GET 28 requests for a total of 4505 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 6 / 28 (shard-cache/DB) + SHARD CACHE 17.65% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=27b8a4173e15829829159b39c620e9a5da908a3f68d94a70cd489188d478f37f receipt_id=D3noeb84wCn3hym3TyGarXfJZJvnFEBiQRk1fQ3W1jws block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 28 State + DB GET 28 requests for a total of 5009 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 4 / 28 (shard-cache/DB) + SHARD CACHE 12.50% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=2Aw4SPFoD6ia1Vr6NxqHpxKmK73JM9ZBw2HisUzgaUB5 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 30 State + DB GET 30 requests for a total of 5607 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 4 / 30 (shard-cache/DB) + SHARD CACHE 11.76% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=EZ9GhJxWQgcTpEQBH2JutymJU8jcUAzGE45a4wym3mK9 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 26 State + DB GET 26 requests for a total of 4545 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 10 / 26 (shard-cache/DB) + SHARD CACHE 27.78% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=6ysp5YTw2enoSzjn5hZBp6K8NunF7noEHesY65PF4WDv block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 22 State + DB GET 22 requests for a total of 4177 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 10 / 22 (shard-cache/DB) + SHARD CACHE 31.25% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=8GExB7nr4wRqCYMhU6y5PK74q2sWjqSZmRkZb3MVGFXV block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 26 State + DB GET 26 requests for a total of 4929 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 10 / 26 (shard-cache/DB) + SHARD CACHE 27.78% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=app.nearcrowd.near receipt_id=5csXkacKhAAQ6F6eckGqZycLgD7cY5aKHimCxw1iNfqP block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 33 State + DB GET 33 requests for a total of 4106 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 6 / 33 (shard-cache/DB) + SHARD CACHE 15.38% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=app.nearcrowd.near receipt_id=3DVei5MvayiThUdrtgXDDeWRSfCG7QD859f4rtADLDNk block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 3 State + DB GET 3 requests for a total of 165 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 18 / 3 (shard-cache/DB) + SHARD CACHE 85.71% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=app.nearcrowd.near receipt_id=2CZ3nibSbAFYF2rZYJdxdBc2NwLEVQyrxbfmkjsL1X14 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 70 State + DB GET 70 requests for a total of 552562 B + DB SET 0 requests for a total of 0 B + STORAGE READ 6 requests for a total of 608 B + STORAGE WRITE 7 requests for a total of 383 B + TRIE NODES (guest) 235 / 2 / 69 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 15 / 1 (shard-cache/DB) + SHARD CACHE 19.54% hit rate, 19.77% if removing 1 too large nodes from total + CHUNK CACHE 76.80% hit rate + + process_receipt receiver=app.nearcrowd.near receipt_id=GTWiUssUkNTmHnUCHgQo91G1ML5i6y1tAWznVsoKog1z block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 26 State + DB GET 26 requests for a total of 544804 B + DB SET 0 requests for a total of 0 B + STORAGE READ 3 requests for a total of 337 B + STORAGE WRITE 4 requests for a total of 409 B + TRIE NODES (guest) 116 / 0 / 25 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 13 / 1 (shard-cache/DB) + SHARD CACHE 33.33% hit rate, 34.21% if removing 1 too large nodes from total + CHUNK CACHE 82.27% hit rate + + process_receipt receiver=27b8a4173e15829829159b39c620e9a5da908a3f68d94a70cd489188d478f37f receipt_id=GFRjGJ7J92VBZAkdSpUwN7z4v1mCmtWgrf2SU7f4MGCN block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 14 State + DB GET 14 requests for a total of 2519 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 18 / 14 (shard-cache/DB) + SHARD CACHE 56.25% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=14ca6570b456a5f904641d1005b9499aee5cfa60216bb3b73871d2256ad1cc2a receipt_id=CuNBQKsiVcZH9Nc9QcWY65oE1w4ifYtiPC5SNKDfA7fL block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 24 State + DB GET 24 requests for a total of 5159 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 4 / 24 (shard-cache/DB) + SHARD CACHE 14.29% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=2cd58e2eab52381a893c42b8dc47d66633b58c16303c4398f44dbd149b6e1411 receipt_id=D3ww9qmfxoyDuJjsyGtsoCZ1MSPwhCDLtny75AsJcTMi block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 24 State + DB GET 24 requests for a total of 4901 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 6 / 24 (shard-cache/DB) + SHARD CACHE 20.00% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=91505032331ede720873cf84a36609dd8755a09f816735a60ffa3ee40b4a4a67 receipt_id=G2AYqug5LN3FS2SN9GPEQERkVpK9dY6g5hQgcnSArswd block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 26 State + DB GET 26 requests for a total of 4795 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 6 / 26 (shard-cache/DB) + SHARD CACHE 18.75% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=6b0021d947b74d8ab24d379f80803c5224767d3777c3d92899806dc5cecc2f77 receipt_id=88WWQXk4nrVqe2si34jS9fhouD73uaGHU5f994c46B9G block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 20 State + DB GET 20 requests for a total of 3643 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 8 / 20 (shard-cache/DB) + SHARD CACHE 28.57% hit rate + CHUNK CACHE not accessed + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptCacheStats-75220100-75220101.s1.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptCacheStats-75220100-75220101.s1.io_trace.snap new file mode 100644 index 000000000..38bc3a87f --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptCacheStats-75220100-75220101.s1.io_trace.snap @@ -0,0 +1,5 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptCacheStats-75220100-75220101.s2.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptCacheStats-75220100-75220101.s2.io_trace.snap new file mode 100644 index 000000000..0fc98d02b --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptCacheStats-75220100-75220101.s2.io_trace.snap @@ -0,0 +1,104 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- + process_transaction tx_hash=C9PVvnuVYGk5b7aYUYMRVxvaZVF4agPiZcTAWyKBrndT block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 34 State + DB GET 34 requests for a total of 5794 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 2 / 34 (shard-cache/DB) + SHARD CACHE 5.56% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=7kRLAD7JBKVuZbD2VpSCN82G7vmCtmXGPcC56XaE2Wdw block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 28 State + DB GET 28 requests for a total of 5457 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 8 / 28 (shard-cache/DB) + SHARD CACHE 22.22% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=ccbca04bf1056af531129685cc7a372eb6df6bc38bc1d050f5eafea8faa979b1 receipt_id=aCqt8Sq8HwJrj6pyeRJmt9PSMJtGBFj38bs9rd1EBuz block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 30 State + DB GET 30 requests for a total of 6031 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 8 / 30 (shard-cache/DB) + SHARD CACHE 21.05% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=Fkz3BnfKsTXcRhnkxHkGnEc8Vis48PakuGR7oKmWcGsi block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 29 State + DB GET 29 requests for a total of 5797 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 4 / 29 (shard-cache/DB) + SHARD CACHE 12.12% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=GQWnDgrcFaLK6fbQ9p5sfHJZ4SYMsyrJDnhWY7HCRjde block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 28 State + DB GET 28 requests for a total of 4759 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 8 / 28 (shard-cache/DB) + SHARD CACHE 22.22% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=Bq4jgcZED8v78974HhVLYbpNfZKDcMAf5tDU47cTjhcT block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 28 State + DB GET 28 requests for a total of 5143 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 8 / 28 (shard-cache/DB) + SHARD CACHE 22.22% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=ccbca04bf1056af531129685cc7a372eb6df6bc38bc1d050f5eafea8faa979b1 receipt_id=5xT8ygLFj9Zy5jEqg3ZBoMYQtrFTPBf9xu2ApjaRkY2C block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 14 State + DB GET 14 requests for a total of 2970 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 24 / 14 (shard-cache/DB) + SHARD CACHE 63.16% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=c_shark.near receipt_id=7g8RkfXrPdQ9uHqNctnNv2BF9H3xJxv2XN68VjnJczo7 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 21 State + DB GET 21 requests for a total of 2190 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 10 / 21 (shard-cache/DB) + SHARD CACHE 32.26% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=f55649cf4669028d5335fe2156dbce5e32d723ee7e5504f4a23ed16c7ca28aa7 receipt_id=9BS9JPZifS47LLXyLxe4Xi1o7rhdehhC9yr9fSineZ1j block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 24 State + DB GET 24 requests for a total of 4391 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 8 / 24 (shard-cache/DB) + SHARD CACHE 25.00% hit rate + CHUNK CACHE not accessed + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptCacheStats-75220100-75220101.s3.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptCacheStats-75220100-75220101.s3.io_trace.snap new file mode 100644 index 000000000..181bde284 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptCacheStats-75220100-75220101.s3.io_trace.snap @@ -0,0 +1,341 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- + process_transaction tx_hash=AA7U5AQwKV5zCMEqN1317cqwBQkAMRiWtAtoRSNjXEjT block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 32 State + DB GET 32 requests for a total of 6116 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 2 / 32 (shard-cache/DB) + SHARD CACHE 5.88% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=AKtUBGwV1zxbG3dRjCQD9WJ12Fzo9YgQ1ow2nZCd2m6t block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 21 State + DB GET 21 requests for a total of 4361 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 8 / 21 (shard-cache/DB) + SHARD CACHE 27.59% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=uSKvjryKgJQrWKg5EFSvEpnDUPRtRSbPrX42i84ekV3 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 37 State + DB GET 37 requests for a total of 7681 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 6 / 37 (shard-cache/DB) + SHARD CACHE 13.95% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=verrelkeung.near receipt_id=cesi14ugy8KbNKu5SqL5SYY8MUFRWC39qzKYP9xiJL7 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + DB GET 0 requests for a total of 0 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 16 / 0 (shard-cache/DB) + SHARD CACHE 100.00% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=token.sweat receipt_id=4tSv2K8ohafiXnCAKprrWinzRFuaMwQzM83dkVx99q7r block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 70 State + DB GET 70 requests for a total of 218330 B + DB SET 0 requests for a total of 0 B + STORAGE READ 3 requests for a total of 102 B + STORAGE WRITE 3 requests for a total of 102 B + TRIE NODES (guest) 121 / 2 / 37 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 6 / 33 (shard-cache/DB) + SHARD CACHE 10.26% hit rate, 10.39% if removing 1 too large nodes from total + CHUNK CACHE 75.62% hit rate + + process_receipt receiver=tge-lockup.sweat receipt_id=7UeSKKYxqRLsSwnW5xJm116QTJEWJJa7YqR1JmNWZE3L block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + DB GET 0 requests for a total of 0 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 0 / 0 (shard-cache/DB) + SHARD CACHE not accessed + CHUNK CACHE not accessed + + process_receipt receiver=npo-aurora.near receipt_id=7HpTajwsXdnmX2EEzWq9PXktDJLGJGwqWhL4gcQ3QVQ3 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 16 State + DB GET 16 requests for a total of 3059 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 6 / 16 (shard-cache/DB) + SHARD CACHE 27.27% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=tge-lockup.sweat receipt_id=CHPvzniPWKMWCiZ3c6ydqN6QgsEBpS3pNgP7HH48iQLK block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 20 State + DB GET 20 requests for a total of 236168 B + DB SET 0 requests for a total of 0 B + STORAGE READ 1 requests for a total of 53 B + STORAGE WRITE 1 requests for a total of 53 B + TRIE NODES (guest) 17 / 0 / 5 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 8 / 15 (shard-cache/DB) + SHARD CACHE 28.57% hit rate, 29.63% if removing 1 too large nodes from total + CHUNK CACHE 77.27% hit rate + + process_receipt receiver=sweat_welcome.near receipt_id=H8GTFPKBkuUjvs8NfzcPNZuapFiMbGUMvscNZhnjUsPv block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 36 State + DB GET 36 requests for a total of 6524 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 4 / 36 (shard-cache/DB) + SHARD CACHE 10.00% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=token.sweat receipt_id=4rYKmJdE5pAHCDb9qVYRjTruvE58vRPRH9A7sGroBa3b block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 19 State + DB GET 19 requests for a total of 209818 B + DB SET 0 requests for a total of 0 B + STORAGE READ 3 requests for a total of 102 B + STORAGE WRITE 3 requests for a total of 102 B + TRIE NODES (guest) 107 / 0 / 18 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 16 / 1 (shard-cache/DB) + SHARD CACHE 45.71% hit rate, 47.06% if removing 1 too large nodes from total + CHUNK CACHE 85.60% hit rate + + process_receipt receiver=token.sweat receipt_id=ohgxoczB4YhnAsUDpFVDpnMGP5iXuZbftgNe86KZ5XN block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 13 State + DB GET 13 requests for a total of 208247 B + DB SET 0 requests for a total of 0 B + STORAGE READ 3 requests for a total of 102 B + STORAGE WRITE 3 requests for a total of 102 B + TRIE NODES (guest) 107 / 0 / 12 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 16 / 1 (shard-cache/DB) + SHARD CACHE 55.17% hit rate, 57.14% if removing 1 too large nodes from total + CHUNK CACHE 89.92% hit rate + + process_receipt receiver=token.sweat receipt_id=9iNcdK3djEA1mqNP4gaQM8jcaEvBtqas31S79cAWH4TY block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 8 State + DB GET 8 requests for a total of 206615 B + DB SET 0 requests for a total of 0 B + STORAGE READ 3 requests for a total of 102 B + STORAGE WRITE 3 requests for a total of 102 B + TRIE NODES (guest) 55 / 0 / 7 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 16 / 1 (shard-cache/DB) + SHARD CACHE 66.67% hit rate, 69.57% if removing 1 too large nodes from total + CHUNK CACHE 88.71% hit rate + + process_receipt receiver=token.sweat receipt_id=22c3R5Xe82GCre4Mxa33Se7M4Rhp77RMf4ZwRjS55Ai7 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 8 State + DB GET 8 requests for a total of 206615 B + DB SET 0 requests for a total of 0 B + STORAGE READ 3 requests for a total of 102 B + STORAGE WRITE 3 requests for a total of 102 B + TRIE NODES (guest) 55 / 0 / 7 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 16 / 1 (shard-cache/DB) + SHARD CACHE 66.67% hit rate, 69.57% if removing 1 too large nodes from total + CHUNK CACHE 88.71% hit rate + + process_receipt receiver=ya_gul.near receipt_id=B2XmBAZhY3gYwktoRiP96KBip7Jd5iQNHbRXKLW2348W block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 16 State + DB GET 16 requests for a total of 2686 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 4 / 16 (shard-cache/DB) + SHARD CACHE 20.00% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=nex1234.near receipt_id=DbRHWcLYjARUjjUZj3ERR5SGcBE7nLrXTe1dKmuLWivu block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 14 State + DB GET 14 requests for a total of 2263 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 8 / 14 (shard-cache/DB) + SHARD CACHE 36.36% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=xtoken.ref-finance.near receipt_id=6RiUAxqBmiBo4XnEURb87D5twzmrZjELre4cJkEzdFuu block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 34 State + DB GET 34 requests for a total of 247699 B + DB SET 0 requests for a total of 0 B + STORAGE READ 2 requests for a total of 170 B + STORAGE WRITE 2 requests for a total of 170 B + TRIE NODES (guest) 42 / 0 / 16 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 4 / 18 (shard-cache/DB) + SHARD CACHE 10.53% hit rate, 10.81% if removing 1 too large nodes from total + CHUNK CACHE 72.41% hit rate + + process_receipt receiver=tge-lockup.sweat receipt_id=EYwsRh17pfzg1n6uWEduBbrvbWmf2hnt2g8HPeABZLHR block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 25 State + DB GET 25 requests for a total of 239354 B + DB SET 0 requests for a total of 0 B + STORAGE READ 3 requests for a total of 210 B + STORAGE WRITE 2 requests for a total of 202 B + TRIE NODES (guest) 38 / 0 / 24 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 6 / 1 (shard-cache/DB) + SHARD CACHE 19.35% hit rate, 20.00% if removing 1 too large nodes from total + CHUNK CACHE 61.29% hit rate + + process_transaction tx_hash=Fdaq5LbQw49MfMp1BHMfXCkDVA3RW9ZnYqTbTSrdz2VD block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 44 State + DB GET 44 requests for a total of 6721 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 4 / 44 (shard-cache/DB) + SHARD CACHE 8.33% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=D8U1btrcfRbbf8wV1uWLmHKY1hDHBYUySwwE7hmPWjAP block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 33 State + DB GET 33 requests for a total of 7465 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 6 / 33 (shard-cache/DB) + SHARD CACHE 15.38% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=FjwDNPxNxgpipRX3s48kRNUGfaPwgxG64N7KRQ7YULx2 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 24 State + DB GET 24 requests for a total of 4210 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 8 / 24 (shard-cache/DB) + SHARD CACHE 25.00% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=HN32RHYR9J1up2MguxxCpcyhnFwbxttKNTscT4n748SR block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 20 State + DB GET 20 requests for a total of 3159 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 24 / 20 (shard-cache/DB) + SHARD CACHE 54.55% hit rate + CHUNK CACHE not accessed + + process_transaction tx_hash=A76PNL2oTtuUtpe51dpCrcCU2NShF2vkB6ZTLHoXEnKx block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + DB GET 0 requests for a total of 0 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 0 / 0 (shard-cache/DB) + SHARD CACHE not accessed + CHUNK CACHE not accessed + + process_receipt receiver=token.sweat receipt_id=8s88Y7hyAXrmZngJGpRBsvsbqYzAQiJbqdwSMESeLmr5 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 52 State + DB GET 52 requests for a total of 215965 B + DB SET 0 requests for a total of 0 B + STORAGE READ 3 requests for a total of 102 B + STORAGE WRITE 3 requests for a total of 102 B + TRIE NODES (guest) 121 / 4 / 35 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 22 / 17 (shard-cache/DB) + SHARD CACHE 33.33% hit rate, 33.77% if removing 1 too large nodes from total + CHUNK CACHE 75.62% hit rate + + process_receipt receiver=seensayer.near receipt_id=J6m2SVXWwC3oR2g2QDbbqZf7GCTYx8tQETZuFCi1QDBU block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 24 State + DB GET 24 requests for a total of 5423 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 6 / 24 (shard-cache/DB) + SHARD CACHE 20.00% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=tge-lockup.sweat receipt_id=AZawzkDrLTMrbxZTBWwgkYTY5AEcEBJQYsVrRckDayf4 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 27 State + DB GET 27 requests for a total of 239530 B + DB SET 0 requests for a total of 0 B + STORAGE READ 3 requests for a total of 210 B + STORAGE WRITE 2 requests for a total of 202 B + TRIE NODES (guest) 54 / 9 / 20 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 10 / 7 (shard-cache/DB) + SHARD CACHE 41.30% hit rate, 42.22% if removing 1 too large nodes from total + CHUNK CACHE 65.06% hit rate + + process_receipt receiver=token.sweat receipt_id=454QLeWg3Yy4HQLcowLtvQGJfwzw2reUREx1sLtv1gga block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 16 State + DB GET 16 requests for a total of 209464 B + DB SET 0 requests for a total of 0 B + STORAGE READ 3 requests for a total of 102 B + STORAGE WRITE 3 requests for a total of 102 B + TRIE NODES (guest) 104 / 0 / 15 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 16 / 1 (shard-cache/DB) + SHARD CACHE 50.00% hit rate, 51.61% if removing 1 too large nodes from total + CHUNK CACHE 87.39% hit rate + + process_receipt receiver=tge-lockup.sweat receipt_id=Jf6Hh3cgANXPYkd536rGMFbWkXzkA1CBNFNJhwn3ksL block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 6 State + DB GET 6 requests for a total of 234636 B + DB SET 0 requests for a total of 0 B + STORAGE READ 1 requests for a total of 53 B + STORAGE WRITE 1 requests for a total of 53 B + TRIE NODES (guest) 1 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 7 / 6 (shard-cache/DB) + SHARD CACHE 53.85% hit rate, 58.33% if removing 1 too large nodes from total + CHUNK CACHE 100.00% hit rate + + process_receipt receiver=token.v2.ref-finance.near receipt_id=8vJXghN3eeQr2HLsSMEUmVb85sZymMSKseYNfKmK328o block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 29 State + DB GET 29 requests for a total of 188154 B + DB SET 0 requests for a total of 0 B + STORAGE READ 3 requests for a total of 61 B + STORAGE WRITE 3 requests for a total of 61 B + TRIE NODES (guest) 131 / 0 / 21 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 27 / 8 (shard-cache/DB) + SHARD CACHE 48.21% hit rate, 49.09% if removing 1 too large nodes from total + CHUNK CACHE 86.18% hit rate + + process_receipt receiver=xtoken.ref-finance.near receipt_id=9r96hCrbYcEzqaTRrA6mZHv26KJqrihjuomU6Hf6vfTX block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + DB GET 0 requests for a total of 0 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 0 / 0 (shard-cache/DB) + SHARD CACHE not accessed + CHUNK CACHE not accessed + + process_receipt receiver=token.sweat receipt_id=7d1QXW1is9bzovZSA1XJK1AWTrPJPixNVReuCihPBQFh block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 15 State + DB GET 15 requests for a total of 209133 B + DB SET 0 requests for a total of 0 B + STORAGE READ 3 requests for a total of 102 B + STORAGE WRITE 3 requests for a total of 102 B + TRIE NODES (guest) 105 / 0 / 14 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 16 / 1 (shard-cache/DB) + SHARD CACHE 51.61% hit rate, 53.33% if removing 1 too large nodes from total + CHUNK CACHE 88.24% hit rate + + process_receipt receiver=tge-lockup.sweat receipt_id=EvN6kHZCYkBr4E8V8jm5R97Nf7paX6xhbtz57YZbHsn block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + DB GET 0 requests for a total of 0 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 0 / 0 (shard-cache/DB) + SHARD CACHE not accessed + CHUNK CACHE not accessed + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptDbStats-75220100-75220101.s0.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptDbStats-75220100-75220101.s0.io_trace.snap new file mode 100644 index 000000000..9a2559557 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptDbStats-75220100-75220101.s0.io_trace.snap @@ -0,0 +1,74 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- + process_transaction tx_hash=6wbch2CCuvqhbnknr7HhNW9PoyK6FSUmoCGJKpKEahvi block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 37 State + + process_transaction tx_hash=8P2uMiA96mwRDnoqzwQijEHc1eFpZNJYJFKxHz69sYgj block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 30 State + + process_transaction tx_hash=GREvhhJebp73HsJh8dXJYgdMURBPbpMebDti4AVi6UdC block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 3 State + + process_receipt receiver=app.nearcrowd.near receipt_id=HSkLwmSscYqj6DekmxRTUACkBUAUS7sUVBnxPC2wKVj8 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 64 State + + process_receipt receiver=app.nearcrowd.near receipt_id=HRR3EXitfuoA7yFoa1239GW13ZErmdq6J8vt1MoZgpiu block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 28 State + + process_receipt receiver=app.nearcrowd.near receipt_id=28ozHpuL1WZ3ExMm3UrNQYco31XHHaBiUXbyMD1firye block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 17 State + + process_receipt receiver=6f25217bc63c86d36e0cdc620d05b2f1381eb43eac560d4f1261380b255e2176 receipt_id=Fp2sJPeBSPX5eXNanJ5Jh1shgBKn6omvUCWcUNEAMd4W block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 26 State + + process_receipt receiver=76248d053a8e8b47cfecab60c5f6f1befe74bcbd923c4ef5950bfeb2945fadff receipt_id=6d7ZrM9yztyWvAVtYbA4nQhtfchiyXbbBmJzYyMe3nht block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 24 State + + process_receipt receiver=7a5de36efdbcd23489dcfd7a3c63966b9bcbddfbe4ba8db7d1df109ecd37ef5e receipt_id=wDN56cf2bf3sz16RzF6H3UaC1pGnYJ7XFvUdzLTiTS3 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 28 State + + process_receipt receiver=27b8a4173e15829829159b39c620e9a5da908a3f68d94a70cd489188d478f37f receipt_id=D3noeb84wCn3hym3TyGarXfJZJvnFEBiQRk1fQ3W1jws block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 28 State + + process_transaction tx_hash=2Aw4SPFoD6ia1Vr6NxqHpxKmK73JM9ZBw2HisUzgaUB5 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 30 State + + process_transaction tx_hash=EZ9GhJxWQgcTpEQBH2JutymJU8jcUAzGE45a4wym3mK9 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 26 State + + process_transaction tx_hash=6ysp5YTw2enoSzjn5hZBp6K8NunF7noEHesY65PF4WDv block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 22 State + + process_transaction tx_hash=8GExB7nr4wRqCYMhU6y5PK74q2sWjqSZmRkZb3MVGFXV block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 26 State + + process_receipt receiver=app.nearcrowd.near receipt_id=5csXkacKhAAQ6F6eckGqZycLgD7cY5aKHimCxw1iNfqP block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 33 State + + process_receipt receiver=app.nearcrowd.near receipt_id=3DVei5MvayiThUdrtgXDDeWRSfCG7QD859f4rtADLDNk block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 3 State + + process_receipt receiver=app.nearcrowd.near receipt_id=2CZ3nibSbAFYF2rZYJdxdBc2NwLEVQyrxbfmkjsL1X14 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 70 State + + process_receipt receiver=app.nearcrowd.near receipt_id=GTWiUssUkNTmHnUCHgQo91G1ML5i6y1tAWznVsoKog1z block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 26 State + + process_receipt receiver=27b8a4173e15829829159b39c620e9a5da908a3f68d94a70cd489188d478f37f receipt_id=GFRjGJ7J92VBZAkdSpUwN7z4v1mCmtWgrf2SU7f4MGCN block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 14 State + + process_receipt receiver=14ca6570b456a5f904641d1005b9499aee5cfa60216bb3b73871d2256ad1cc2a receipt_id=CuNBQKsiVcZH9Nc9QcWY65oE1w4ifYtiPC5SNKDfA7fL block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 24 State + + process_receipt receiver=2cd58e2eab52381a893c42b8dc47d66633b58c16303c4398f44dbd149b6e1411 receipt_id=D3ww9qmfxoyDuJjsyGtsoCZ1MSPwhCDLtny75AsJcTMi block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 24 State + + process_receipt receiver=91505032331ede720873cf84a36609dd8755a09f816735a60ffa3ee40b4a4a67 receipt_id=G2AYqug5LN3FS2SN9GPEQERkVpK9dY6g5hQgcnSArswd block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 26 State + + process_receipt receiver=6b0021d947b74d8ab24d379f80803c5224767d3777c3d92899806dc5cecc2f77 receipt_id=88WWQXk4nrVqe2si34jS9fhouD73uaGHU5f994c46B9G block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 20 State + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptDbStats-75220100-75220101.s1.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptDbStats-75220100-75220101.s1.io_trace.snap new file mode 100644 index 000000000..38bc3a87f --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptDbStats-75220100-75220101.s1.io_trace.snap @@ -0,0 +1,5 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptDbStats-75220100-75220101.s2.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptDbStats-75220100-75220101.s2.io_trace.snap new file mode 100644 index 000000000..1f2849c55 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptDbStats-75220100-75220101.s2.io_trace.snap @@ -0,0 +1,32 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- + process_transaction tx_hash=C9PVvnuVYGk5b7aYUYMRVxvaZVF4agPiZcTAWyKBrndT block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 34 State + + process_transaction tx_hash=7kRLAD7JBKVuZbD2VpSCN82G7vmCtmXGPcC56XaE2Wdw block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 28 State + + process_receipt receiver=ccbca04bf1056af531129685cc7a372eb6df6bc38bc1d050f5eafea8faa979b1 receipt_id=aCqt8Sq8HwJrj6pyeRJmt9PSMJtGBFj38bs9rd1EBuz block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 30 State + + process_transaction tx_hash=Fkz3BnfKsTXcRhnkxHkGnEc8Vis48PakuGR7oKmWcGsi block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 29 State + + process_transaction tx_hash=GQWnDgrcFaLK6fbQ9p5sfHJZ4SYMsyrJDnhWY7HCRjde block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 28 State + + process_transaction tx_hash=Bq4jgcZED8v78974HhVLYbpNfZKDcMAf5tDU47cTjhcT block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 28 State + + process_receipt receiver=ccbca04bf1056af531129685cc7a372eb6df6bc38bc1d050f5eafea8faa979b1 receipt_id=5xT8ygLFj9Zy5jEqg3ZBoMYQtrFTPBf9xu2ApjaRkY2C block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 14 State + + process_receipt receiver=c_shark.near receipt_id=7g8RkfXrPdQ9uHqNctnNv2BF9H3xJxv2XN68VjnJczo7 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 21 State + + process_receipt receiver=f55649cf4669028d5335fe2156dbce5e32d723ee7e5504f4a23ed16c7ca28aa7 receipt_id=9BS9JPZifS47LLXyLxe4Xi1o7rhdehhC9yr9fSineZ1j block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 24 State + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptDbStats-75220100-75220101.s3.io_trace.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptDbStats-75220100-75220101.s3.io_trace.snap new file mode 100644 index 000000000..e8cf342d9 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__ReceiptDbStats-75220100-75220101.s3.io_trace.snap @@ -0,0 +1,93 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- + process_transaction tx_hash=AA7U5AQwKV5zCMEqN1317cqwBQkAMRiWtAtoRSNjXEjT block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 32 State + + process_transaction tx_hash=AKtUBGwV1zxbG3dRjCQD9WJ12Fzo9YgQ1ow2nZCd2m6t block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 21 State + + process_transaction tx_hash=uSKvjryKgJQrWKg5EFSvEpnDUPRtRSbPrX42i84ekV3 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 37 State + + process_receipt receiver=verrelkeung.near receipt_id=cesi14ugy8KbNKu5SqL5SYY8MUFRWC39qzKYP9xiJL7 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + + process_receipt receiver=token.sweat receipt_id=4tSv2K8ohafiXnCAKprrWinzRFuaMwQzM83dkVx99q7r block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 70 State + + process_receipt receiver=tge-lockup.sweat receipt_id=7UeSKKYxqRLsSwnW5xJm116QTJEWJJa7YqR1JmNWZE3L block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + + process_receipt receiver=npo-aurora.near receipt_id=7HpTajwsXdnmX2EEzWq9PXktDJLGJGwqWhL4gcQ3QVQ3 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 16 State + + process_receipt receiver=tge-lockup.sweat receipt_id=CHPvzniPWKMWCiZ3c6ydqN6QgsEBpS3pNgP7HH48iQLK block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 20 State + + process_receipt receiver=sweat_welcome.near receipt_id=H8GTFPKBkuUjvs8NfzcPNZuapFiMbGUMvscNZhnjUsPv block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 36 State + + process_receipt receiver=token.sweat receipt_id=4rYKmJdE5pAHCDb9qVYRjTruvE58vRPRH9A7sGroBa3b block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 19 State + + process_receipt receiver=token.sweat receipt_id=ohgxoczB4YhnAsUDpFVDpnMGP5iXuZbftgNe86KZ5XN block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 13 State + + process_receipt receiver=token.sweat receipt_id=9iNcdK3djEA1mqNP4gaQM8jcaEvBtqas31S79cAWH4TY block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 8 State + + process_receipt receiver=token.sweat receipt_id=22c3R5Xe82GCre4Mxa33Se7M4Rhp77RMf4ZwRjS55Ai7 block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 8 State + + process_receipt receiver=ya_gul.near receipt_id=B2XmBAZhY3gYwktoRiP96KBip7Jd5iQNHbRXKLW2348W block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 16 State + + process_receipt receiver=nex1234.near receipt_id=DbRHWcLYjARUjjUZj3ERR5SGcBE7nLrXTe1dKmuLWivu block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 14 State + + process_receipt receiver=xtoken.ref-finance.near receipt_id=6RiUAxqBmiBo4XnEURb87D5twzmrZjELre4cJkEzdFuu block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 34 State + + process_receipt receiver=tge-lockup.sweat receipt_id=EYwsRh17pfzg1n6uWEduBbrvbWmf2hnt2g8HPeABZLHR block=FhsQS1zgTLpZjYitRKevStmzJfUgweUTjQA1rUeNW6QR + GET 25 State + + process_transaction tx_hash=Fdaq5LbQw49MfMp1BHMfXCkDVA3RW9ZnYqTbTSrdz2VD block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 44 State + + process_transaction tx_hash=D8U1btrcfRbbf8wV1uWLmHKY1hDHBYUySwwE7hmPWjAP block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 33 State + + process_transaction tx_hash=FjwDNPxNxgpipRX3s48kRNUGfaPwgxG64N7KRQ7YULx2 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 24 State + + process_transaction tx_hash=HN32RHYR9J1up2MguxxCpcyhnFwbxttKNTscT4n748SR block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 20 State + + process_transaction tx_hash=A76PNL2oTtuUtpe51dpCrcCU2NShF2vkB6ZTLHoXEnKx block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + + process_receipt receiver=token.sweat receipt_id=8s88Y7hyAXrmZngJGpRBsvsbqYzAQiJbqdwSMESeLmr5 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 52 State + + process_receipt receiver=seensayer.near receipt_id=J6m2SVXWwC3oR2g2QDbbqZf7GCTYx8tQETZuFCi1QDBU block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 24 State + + process_receipt receiver=tge-lockup.sweat receipt_id=AZawzkDrLTMrbxZTBWwgkYTY5AEcEBJQYsVrRckDayf4 block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 27 State + + process_receipt receiver=token.sweat receipt_id=454QLeWg3Yy4HQLcowLtvQGJfwzw2reUREx1sLtv1gga block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 16 State + + process_receipt receiver=tge-lockup.sweat receipt_id=Jf6Hh3cgANXPYkd536rGMFbWkXzkA1CBNFNJhwn3ksL block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 6 State + + process_receipt receiver=token.v2.ref-finance.near receipt_id=8vJXghN3eeQr2HLsSMEUmVb85sZymMSKseYNfKmK328o block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 29 State + + process_receipt receiver=xtoken.ref-finance.near receipt_id=9r96hCrbYcEzqaTRrA6mZHv26KJqrihjuomU6Hf6vfTX block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + + process_receipt receiver=token.sweat receipt_id=7d1QXW1is9bzovZSA1XJK1AWTrPJPixNVReuCihPBQFh block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + GET 15 State + + process_receipt receiver=tge-lockup.sweat receipt_id=EvN6kHZCYkBr4E8V8jm5R97Nf7paX6xhbtz57YZbHsn block=2zB2oBqRj8dFECSaBeGP6hGGDs7KowYBVwcVfq8Ukjf9 + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__account_filter_CacheStats.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__account_filter_CacheStats.snap new file mode 100644 index 000000000..c5e66ace3 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__account_filter_CacheStats.snap @@ -0,0 +1,16 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- +top-level: + GET 4 State + DB GET 4 requests for a total of 65 B + DB SET 0 requests for a total of 0 B + STORAGE READ 1 requests for a total of 1000 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 1 / 19 / 1 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 27 / 3 (shard-cache/DB) + SHARD CACHE 92.00% hit rate + CHUNK CACHE 4.76% hit rate + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__account_filter_ReceiptCacheStats.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__account_filter_ReceiptCacheStats.snap new file mode 100644 index 000000000..3ca6a5b96 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__account_filter_ReceiptCacheStats.snap @@ -0,0 +1,37 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- + process_receipt receiver=alice.near receipt_id=id0 + GET 2 State + DB GET 2 requests for a total of 30 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 6 / 2 (shard-cache/DB) + SHARD CACHE 75.00% hit rate + CHUNK CACHE not accessed + + process_receipt receiver=alice.near receipt_id=id2 + GET 2 State + DB GET 2 requests for a total of 35 B + DB SET 0 requests for a total of 0 B + STORAGE READ 1 requests for a total of 1000 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 1 / 19 / 1 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 6 / 1 (shard-cache/DB) + SHARD CACHE 92.59% hit rate + CHUNK CACHE 4.76% hit rate + + process_receipt receiver=alice.near receipt_id=id3 + DB GET 0 requests for a total of 0 B + DB SET 0 requests for a total of 0 B + STORAGE READ 0 requests for a total of 0 B + STORAGE WRITE 0 requests for a total of 0 B + TRIE NODES (guest) 0 / 0 / 0 (chunk-cache/shard-cache/DB) + TRIE NODES (host) 15 / 0 (shard-cache/DB) + SHARD CACHE 100.00% hit rate + CHUNK CACHE not accessed + + diff --git a/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__account_filter_ReceiptDbStats.snap b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__account_filter_ReceiptDbStats.snap new file mode 100644 index 000000000..a87b30cb0 --- /dev/null +++ b/runtime/runtime-params-estimator/src/snapshots/runtime_params_estimator__replay__tests__account_filter_ReceiptDbStats.snap @@ -0,0 +1,13 @@ +--- +source: runtime/runtime-params-estimator/src/replay.rs +expression: output +--- + process_receipt receiver=alice.near receipt_id=id0 + GET 2 State + + process_receipt receiver=alice.near receipt_id=id2 + GET 2 State + + process_receipt receiver=alice.near receipt_id=id3 + + diff --git a/runtime/runtime-params-estimator/src/transaction_builder.rs b/runtime/runtime-params-estimator/src/transaction_builder.rs new file mode 100644 index 000000000..98755c0d5 --- /dev/null +++ b/runtime/runtime-params-estimator/src/transaction_builder.rs @@ -0,0 +1,152 @@ +use std::collections::HashMap; + +use genesis_populate::get_account_id; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::transaction::{Action, FunctionCallAction, SignedTransaction}; +use unc_primitives::types::AccountId; +use rand::prelude::ThreadRng; +use rand::seq::SliceRandom; +use rand::Rng; + +/// A helper to create transaction for processing by a `TestBed`. +#[derive(Clone)] +pub(crate) struct TransactionBuilder { + accounts: Vec, + nonces: HashMap, + unused_accounts: Vec, + unused_index: usize, +} + +/// Define how accounts should be generated. +#[derive(Clone, Copy)] +pub(crate) enum AccountRequirement { + /// Use a different random account on every iteration, account exists and + /// has estimator contract deployed. + RandomUnused, + /// Use the same account as the signer. Must not be used for signer id. + SameAsSigner, + /// Use sub account of the signer. Useful for `CreateAction` estimations. + SubOfSigner, + /// Account must be `generated_account_id(seed = 0)`. + /// + /// Usage: Delegate actions are signed by the sender, so it can't be + /// replaced with a random account. + ConstantAccount0, +} + +impl TransactionBuilder { + pub(crate) fn new(accounts: Vec) -> TransactionBuilder { + let n = accounts.len(); + let mut rng = rand::thread_rng(); + let mut unused_accounts: Vec = Vec::from_iter(0..n); + unused_accounts.shuffle(&mut rng); + let unused_index: usize = 0; + + TransactionBuilder { accounts, nonces: HashMap::new(), unused_accounts, unused_index } + } + + pub(crate) fn transaction_from_actions( + &mut self, + sender: AccountId, + receiver: AccountId, + actions: Vec, + ) -> SignedTransaction { + let signer = InMemorySigner::from_seed(sender.clone(), KeyType::ED25519, sender.as_ref()); + let nonce = self.nonce(&sender); + + SignedTransaction::from_actions( + nonce as u64, + sender.clone(), + receiver, + &signer, + actions, + CryptoHash::default(), + ) + } + + pub(crate) fn transaction_from_function_call( + &mut self, + sender: AccountId, + method: &str, + args: Vec, + ) -> SignedTransaction { + let receiver = sender.clone(); + let actions = vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: method.to_string(), + args, + gas: 10u64.pow(18), + deposit: 0, + }))]; + self.transaction_from_actions(sender, receiver, actions) + } + + /// Transaction that inserts a value for a given key under an account. + /// The account must have the test contract deployed. + pub(crate) fn account_insert_key( + &mut self, + account: AccountId, + key: &[u8], + value: &[u8], + ) -> SignedTransaction { + let arg = (key.len() as u64) + .to_le_bytes() + .into_iter() + .chain(key.iter().cloned()) + .chain((value.len() as u64).to_le_bytes().into_iter()) + .chain(value.iter().cloned()) + .collect(); + + self.transaction_from_function_call(account, "account_storage_insert_key", arg) + } + + pub(crate) fn rng(&mut self) -> ThreadRng { + rand::thread_rng() + } + + pub(crate) fn account(&mut self, account_index: u64) -> AccountId { + get_account_id(account_index) + } + pub(crate) fn random_account(&mut self) -> AccountId { + let account_index = self.rng().gen_range(0..self.accounts.len()); + self.accounts[account_index].clone() + } + pub(crate) fn random_unused_account(&mut self) -> AccountId { + if self.unused_index >= self.unused_accounts.len() { + panic!("All accounts used. Try running with a higher value for the parameter `--accounts-num `.") + } + let tmp = self.unused_index; + self.unused_index += 1; + return self.accounts[self.unused_accounts[tmp]].clone(); + } + pub(crate) fn random_account_pair(&mut self) -> (AccountId, AccountId) { + let first = self.random_account(); + loop { + let second = self.random_account(); + if first != second { + return (first, second); + } + } + } + + pub(crate) fn account_by_requirement( + &mut self, + src: AccountRequirement, + signer_id: Option<&AccountId>, + ) -> AccountId { + match src { + AccountRequirement::RandomUnused => self.random_unused_account(), + AccountRequirement::SameAsSigner => signer_id.expect("no signer_id provided").clone(), + AccountRequirement::SubOfSigner => { + format!("sub.{}", signer_id.expect("no signer_id")).parse().unwrap() + } + AccountRequirement::ConstantAccount0 => self.account(0), + } + } + + fn nonce(&mut self, account_id: &AccountId) -> u64 { + let nonce = self.nonces.entry(account_id.clone()).or_default(); + *nonce += 1; + *nonce + } +} diff --git a/runtime/runtime-params-estimator/src/trie.rs b/runtime/runtime-params-estimator/src/trie.rs new file mode 100644 index 000000000..0c5e05328 --- /dev/null +++ b/runtime/runtime-params-estimator/src/trie.rs @@ -0,0 +1,306 @@ +use crate::estimator_context::{EstimatorContext, Testbed}; +use crate::gas_cost::{GasCost, NonNegativeTolerance}; +use crate::utils::{aggregate_per_block_measurements, overhead_per_measured_block, percentiles}; +use unc_parameters::ExtCosts; +use unc_primitives::hash::hash; +use unc_store::trie::accounting_cache::TrieAccountingCache; +use unc_store::TrieCachingStorage; +use std::sync::atomic::{AtomicUsize, Ordering}; + +static SINK: AtomicUsize = AtomicUsize::new(0); + +pub(crate) fn write_node( + ctx: &mut EstimatorContext, + warmup_iters: usize, + measured_iters: usize, + final_key_len: usize, +) -> GasCost { + let block_latency = 0; + let overhead = overhead_per_measured_block(ctx, block_latency); + let mut testbed = ctx.testbed(); + let tb = testbed.transaction_builder(); + // Prepare a long chain in the trie + let signer = tb.random_account(); + let key = "j".repeat(final_key_len); + let mut setup_block = Vec::new(); + for key_len in 0..final_key_len { + let key = &key.as_bytes()[..key_len]; + let value = b"0"; + setup_block.push(tb.account_insert_key(signer.clone(), key, value)); + } + let mut blocks = Vec::with_capacity(1 + 2 * warmup_iters + 2 * measured_iters); + blocks.push(setup_block); + blocks.extend( + [b"1", b"2", b"3"] + .iter() + .cycle() + .map(|value| vec![tb.account_insert_key(signer.clone(), &key.as_bytes()[0..1], *value)]) + .take(measured_iters + warmup_iters), + ); + blocks.extend( + [b"1", b"2", b"3"] + .iter() + .cycle() + .map(|value| vec![tb.account_insert_key(signer.clone(), key.as_bytes(), *value)]) + .take(measured_iters + warmup_iters), + ); + let results = &testbed.measure_blocks(blocks, block_latency)[1..]; + let (short_key_results, long_key_results) = results.split_at(measured_iters + warmup_iters); + let (cost_short_key, ext_cost_short_key) = aggregate_per_block_measurements( + 1, + short_key_results[warmup_iters..].to_vec(), + Some(overhead.clone()), + ); + let (cost_long_key, ext_cost_long_key) = aggregate_per_block_measurements( + 1, + long_key_results[warmup_iters..].to_vec(), + Some(overhead), + ); + let nodes_touched_delta = ext_cost_long_key[&ExtCosts::touching_trie_node] + - ext_cost_short_key[&ExtCosts::touching_trie_node]; + // The exact number of touched nodes is a implementation that we don't want + // to test here but it should be close to 2*final_key_len + assert!(nodes_touched_delta as usize <= 2 * final_key_len + 10); + assert!(nodes_touched_delta as usize >= 2 * final_key_len - 10); + let cost_delta = + cost_long_key.saturating_sub(&cost_short_key, &NonNegativeTolerance::PER_MILLE); + let cost = cost_delta / nodes_touched_delta; + cost +} + +pub(crate) fn read_node_from_accounting_cache(testbed: &mut Testbed) -> GasCost { + let debug = testbed.config.debug; + let iters = 200; + let percentiles_of_interest = &[0.5, 0.9, 0.99, 0.999]; + + // Worst-case + // - L3 CPU cache is filled with dummy data before measuring + let spoil_l3 = testbed.config.accurate; + // - Completely cold cache + let warmups = 0; + // - Single node read, no amortization possible + let num_values = 1; + // - Data is spread in main memory + let data_spread = 7; + + let mut estimation = |debug_name: &'static str, + iters: usize, + num_values: usize, + num_warmup_values: usize, + data_spread_factor: usize, + spoil_l3: bool| { + let results = read_node_from_accounting_cache_ext( + testbed, + iters, + num_values, + num_warmup_values, + data_spread_factor, + spoil_l3, + ); + let p_results = percentiles(results, percentiles_of_interest).collect::>(); + if debug { + eprint!("{:<32}", debug_name); + for cost in p_results.iter() { + eprint!("{:>8} ", cost.to_gas() / 1_000_000); + } + eprintln!(); + } + p_results + }; + + // Print header of debug table + if debug { + eprintln!( + "{:<32}{:>8.3} {:>8.3} {:>8.3} {:>8.3}", + "", + percentiles_of_interest[0], + percentiles_of_interest[1], + percentiles_of_interest[2], + percentiles_of_interest[3] + ); + } + + // For the base case, worst-case assumption is slightly relaxed. The base at + // the 90th percentile case is used as final estimation. + let base_case = { + // Reading a different node before the measurement loads data structure + // into cache. It would be difficult for an attacker to avoid this + // consistently, so the base case assumes this is in cache. + let warmups = 1; + // Some amortization should also be allowed, or how would an attacker + // actually abuse undercharged costs? + let num_values = 16; + + let mut p_results = + estimation("Base Case", iters, num_values, warmups, data_spread, spoil_l3); + // Take the 90th percentile measured. + p_results.swap_remove(1) + }; + + // If debug output is enable, run the same estimation using different + // assumptions and print a table of results. + if debug { + // Base case with better data locality. + { + let warmups = 1; + let num_values = 16; + let data_spread = 1; + estimation("Base Case w locality", iters, num_values, warmups, data_spread, spoil_l3); + } + // Worst-case: All parameters as explained above. + { + estimation("Worst Case", iters, num_values, warmups, data_spread, spoil_l3); + } + // Worst-case, but one warm up value to load data structures and code + // into cache. + { + let warmups = 1; + estimation("Warmed-up", iters, num_values, warmups, data_spread, spoil_l3); + } + // Worst-case, but amortized costs over several values, allowing + // hardware level optimizations to kick in. + { + let num_values = 128; + let iters = 30; // For estimation speed only, should not affect results + estimation("Amortized", iters, num_values, warmups, data_spread, spoil_l3); + } + // Almost-best-case, only the L3 is still overwritten between + // iterations. + { + let warmups = 1; + let num_values = 128; + let iters = 30; // For estimation speed only, should not affect results + let data_spread = 1; + estimation("Best Case(from memory)", iters, num_values, warmups, data_spread, spoil_l3); + } + // Best-case: Nothing attempted to worsen memory latency, just iterate + // over measurements and allow the hardware to do all optimizations it + // can. + { + let spoil_l3 = false; + let warmups = 1; + let num_values = 128; + let iters = 30; // For estimation speed only, should not affect results + let data_spread_factor = 1; + estimation("Best Case", iters, num_values, warmups, data_spread_factor, spoil_l3); + } + } + + base_case +} + +fn read_node_from_accounting_cache_ext( + testbed: &mut Testbed, + iters: usize, + // How many values are read after each other. The higher the number, the + // larger the amortization effect is. + num_values: usize, + // Values to read before measurement, to warm up CPU caches with data + // structures used in looking up trie nodes + num_warmup_values: usize, + // Spread factor to reduce data locality + data_spread_factor: usize, + // Before measuring, completely overwrite L3 CPU cache content + spoil_l3: bool, +) -> Vec { + // Trie nodes are largest when they hold part of a storage key (Extension or + // Leaf) and keys can be up to 2kiB. Therefore, this is about the maximum + // size possible. + let value_len: usize = 2048; + + // Prepare a data buffer that we can read again later to overwrite CPU caches + let assumed_max_l3_size = 128 * 1024 * 1024; + let dummy_data = crate::utils::random_vec(assumed_max_l3_size); + + (0..iters) + .map(|i| { + // Setup: + // Insert a number of worst-case trie nodes *somehow*. It really + // doesn't matter how we do it. But it matters that they are + // extension nodes with the desired key length. + // The easiest way to insert such a node is by encoding it manually + // and inserting it as a value. This means it's not even part of the + // actual trie, but it looks like a trie node and can be accessed by + // hash. Thus, it is sufficient for estimating the cost to read end + // decode such a worst-case node. + let tb = testbed.transaction_builder(); + let signer = tb.random_account(); + let values_inserted = num_values * data_spread_factor; + let values: Vec<_> = (0..values_inserted) + .map(|_| { + let extention_key = crate::utils::random_vec(value_len); + unc_store::estimator::encode_extension_node(extention_key) + }) + .collect(); + let mut setup_block = Vec::new(); + for (j, value) in values.iter().cloned().enumerate() { + let key = j.to_le_bytes().to_vec(); + setup_block.push(tb.account_insert_key(signer.clone(), &key, &value)); + } + testbed.process_block(setup_block, 0); + + // Collect keys of the inserted nodes and select a subset for testing. + let all_value_hashes: Vec<_> = values.iter().map(|value| hash(value)).collect(); + let measured_value_hashes: Vec<_> = + all_value_hashes.iter().step_by(data_spread_factor).cloned().collect(); + let unmeasured_value_hashes = &all_value_hashes[0..num_warmup_values]; + assert_eq!(measured_value_hashes.len(), num_values); + + // Create a new cache and load nodes into it as preparation. + let caching_storage = testbed.trie_caching_storage(); + let mut accounting_cache = TrieAccountingCache::new(None); + accounting_cache.set_enabled(true); + let _dummy_sum = read_raw_nodes_from_storage( + &caching_storage, + &mut accounting_cache, + &all_value_hashes, + ); + + // Remove trie nodes from CPU caches by filling the caches with useless data. + // (To measure latency from main memory, not CPU caches) + if spoil_l3 { + let dummy_count = dummy_data.iter().filter(|n| **n == i as u8).count(); + SINK.fetch_add(dummy_count, Ordering::SeqCst); + } + + // Read some nodes from the cache, to warm up caches again. (We only + // want the trie node to come from main memory, the data structures + // around that are expected to always be in cache) + let dummy_sum = read_raw_nodes_from_storage( + &caching_storage, + &mut accounting_cache, + unmeasured_value_hashes, + ); + SINK.fetch_add(dummy_sum, Ordering::SeqCst); + + let start = GasCost::measure(testbed.config.metric); + let dummy_sum = read_raw_nodes_from_storage( + &caching_storage, + &mut accounting_cache, + &measured_value_hashes, + ); + let cost = start.elapsed(); + SINK.fetch_add(dummy_sum, Ordering::SeqCst); + + cost / num_values as u64 + }) + .collect() +} + +/// Read trie nodes directly from a `TrieCachingStorage`, without the runtime. +/// Keys are hashes of the nodes. +/// The return value is just a value to ensure nothing gets optimized out by the +/// compiler. +fn read_raw_nodes_from_storage( + caching_storage: &TrieCachingStorage, + accounting_cache: &mut TrieAccountingCache, + keys: &[unc_primitives::hash::CryptoHash], +) -> usize { + keys.iter() + .map(|key| { + let bytes = + accounting_cache.retrieve_raw_bytes_with_accounting(key, caching_storage).unwrap(); + unc_store::estimator::decode_extension_node(&bytes).len() + }) + .sum() +} diff --git a/runtime/runtime-params-estimator/src/utils.rs b/runtime/runtime-params-estimator/src/utils.rs new file mode 100644 index 000000000..d5c9c6b1d --- /dev/null +++ b/runtime/runtime-params-estimator/src/utils.rs @@ -0,0 +1,472 @@ +use crate::apply_block_cost; +use crate::estimator_context::EstimatorContext; +use crate::gas_cost::{GasCost, NonNegativeTolerance}; +use crate::transaction_builder::TransactionBuilder; +use unc_parameters::vm::{Config as VMConfig, VMKind}; +use unc_parameters::ExtCosts; +use unc_primitives::transaction::{ + Action, DeployContractAction, FunctionCallAction, SignedTransaction, +}; +use rand::distributions::Alphanumeric; +use rand::Rng; +use rand_xorshift::XorShiftRng; +use std::collections::HashMap; +use std::iter; + +pub fn read_resource(path: &str) -> Vec { + let dir = env!("CARGO_MANIFEST_DIR"); + let path = std::path::Path::new(dir).join(path); + std::fs::read(&path) + .unwrap_or_else(|err| panic!("failed to load test resource: {}, {}", path.display(), err)) +} + +/// Attempts to clear OS page cache on Linux based system. Will fail on +/// other systems. Requires write access to /proc/sys/vm/drop_caches +#[cfg(target_os = "linux")] +pub fn clear_linux_page_cache() -> std::io::Result<()> { + unsafe { + libc::sync(); + } + std::fs::write("/proc/sys/vm/drop_caches", b"1") +} + +#[track_caller] +pub(crate) fn transaction_cost( + ctx: &mut EstimatorContext, + make_transaction: &mut dyn FnMut(&mut TransactionBuilder) -> SignedTransaction, +) -> GasCost { + let block_size = 100; + let (gas_cost, _ext_costs) = transaction_cost_ext(ctx, block_size, make_transaction, 0); + gas_cost +} + +#[track_caller] +pub(crate) fn transaction_cost_ext( + ctx: &mut EstimatorContext, + block_size: usize, + make_transaction: &mut dyn FnMut(&mut TransactionBuilder) -> SignedTransaction, + block_latency: usize, +) -> (GasCost, HashMap) { + let verbose = ctx.config.debug; + let measurement_overhead = overhead_per_measured_block(ctx, block_latency); + + let mut testbed = ctx.testbed(); + let blocks = { + let n_blocks = testbed.config.warmup_iters_per_block + testbed.config.iter_per_block; + let mut blocks = Vec::with_capacity(n_blocks); + for _ in 0..n_blocks { + let mut block = Vec::with_capacity(block_size); + for _ in 0..block_size { + let tx = make_transaction(testbed.transaction_builder()); + block.push(tx) + } + blocks.push(block) + } + blocks + }; + + let measurements = testbed.measure_blocks(blocks, block_latency); + if verbose { + // prints individual block measurements (without division by number of + // inner items) which helps understanding issue with high variance + eprint!("|warmup|"); + for (gas, _ext) in &measurements[..testbed.config.warmup_iters_per_block] { + eprint!(" {gas:>#7.2?}"); + } + eprintln!(); + eprint!("|proper|"); + for (gas, _ext) in &measurements[testbed.config.warmup_iters_per_block..] { + eprint!(" {gas:>#7.2?}"); + } + eprintln!(); + } + let measurements = + measurements.into_iter().skip(testbed.config.warmup_iters_per_block).collect::>(); + + aggregate_per_block_measurements(block_size, measurements, Some(measurement_overhead)) +} + +/// Returns the total measurement overhead for a measured block. +pub(crate) fn overhead_per_measured_block( + ctx: &mut EstimatorContext, + block_latency: usize, +) -> GasCost { + let per_block_overhead = apply_block_cost(ctx); + let measurement_overhead = per_block_overhead * (1 + block_latency) as u64; + measurement_overhead +} + +#[track_caller] +pub(crate) fn fn_cost( + ctx: &mut EstimatorContext, + method: &str, + ext_cost: ExtCosts, + count: u64, +) -> GasCost { + // Most functions finish execution in a single block. Other measurements + // should use `fn_cost_count`. + let block_latency = 0; + let (total_cost, measured_count) = fn_cost_count(ctx, method, ext_cost, block_latency); + assert_eq!(measured_count, count); + + let base_cost = noop_function_call_cost(ctx); + + total_cost.saturating_sub(&base_cost, &NonNegativeTolerance::PER_MILLE) / count +} + +#[track_caller] +pub(crate) fn fn_cost_count( + ctx: &mut EstimatorContext, + method: &str, + ext_cost: ExtCosts, + block_latency: usize, +) -> (GasCost, u64) { + // Block size: 20 is a good number if you want to reduce the effect of + // constant-per-block overhead and the tx takes less than 50 Tgas to + // execute. It's hard-coded because the range of values supported depends on + // each estimation. For a check-only run, a single tx per block is faster + // and good enough. + let block_size = if ctx.config.accurate { 20 } else { 1 }; + let mut make_transaction = |tb: &mut TransactionBuilder| -> SignedTransaction { + let sender = tb.random_unused_account(); + tb.transaction_from_function_call(sender, method, Vec::new()) + }; + let (gas_cost, ext_costs) = + transaction_cost_ext(ctx, block_size, &mut make_transaction, block_latency); + let ext_cost = ext_costs[&ext_cost]; + (gas_cost, ext_cost) +} + +pub(crate) fn noop_function_call_cost(ctx: &mut EstimatorContext) -> GasCost { + if let Some(cost) = ctx.cached.noop_function_call_cost.clone() { + return cost; + } + + let cost = { + let mut make_transaction = |tb: &mut TransactionBuilder| -> SignedTransaction { + let sender = tb.random_unused_account(); + tb.transaction_from_function_call(sender, "noop", Vec::new()) + }; + transaction_cost(ctx, &mut make_transaction) + }; + + ctx.cached.noop_function_call_cost = Some(cost.clone()); + cost +} + +/// Estimates the cost to call `method`, but makes sure that `setup` is called +/// before. +/// +/// Used for storage costs -- `setup` writes stuff into the storage, where +/// `method` can then find it. We take care to make sure that `setup` is run in +/// a separate block, to make sure we hit the database and not an in-memory hash +/// map. +pub(crate) fn fn_cost_with_setup( + ctx: &mut EstimatorContext, + setup: &str, + method: &str, + ext_cost: ExtCosts, + count: u64, +) -> GasCost { + let (total_cost, measured_count) = { + let block_latency = 0; + let overhead = overhead_per_measured_block(ctx, block_latency); + let block_size = 2usize; + let n_blocks = ctx.config.warmup_iters_per_block + ctx.config.iter_per_block; + + let mut testbed = ctx.testbed(); + + let blocks = { + let mut blocks = Vec::with_capacity(2 * n_blocks); + for _ in 0..n_blocks { + let tb = testbed.transaction_builder(); + let mut setup_block = Vec::new(); + let mut block = Vec::new(); + for _ in 0..block_size { + let sender = tb.random_unused_account(); + let setup_tx = + tb.transaction_from_function_call(sender.clone(), setup, Vec::new()); + let tx = tb.transaction_from_function_call(sender, method, Vec::new()); + + setup_block.push(setup_tx); + block.push(tx); + } + blocks.push(setup_block); + blocks.push(block); + } + blocks + }; + + let measurements = testbed.measure_blocks(blocks, 0); + // Filter out setup blocks. + let measurements: Vec<_> = measurements + .into_iter() + .skip(ctx.config.warmup_iters_per_block * 2) + .enumerate() + .filter(|(i, _)| i % 2 == 1) + .map(|(_, m)| m) + .collect(); + + let (gas_cost, ext_costs) = + aggregate_per_block_measurements(block_size, measurements, Some(overhead)); + + let is_write = [ExtCosts::storage_write_base, ExtCosts::storage_remove_base] + .iter() + .any(|cost| *ext_costs.get(cost).unwrap_or(&0) > 0); + if !is_write { + assert_eq!( + 0, + *ext_costs.get(&ExtCosts::touching_trie_node).unwrap_or(&0), + "flat storage not working" + ); + } + + (gas_cost, ext_costs[&ext_cost]) + }; + assert_eq!(measured_count, count); + + let base_cost = noop_function_call_cost(ctx); + + (total_cost - base_cost) / count +} + +/// Estimates the cost to call `method`, on given contract. +/// +/// Used for costs that specifically need to deploy a contract first. Note that +/// this causes the contract to be deployed once in every iteration. Therefore, +/// whenever possible, prefer to add a method to the generic test contract. +/// +/// The measured cost is the pure cost of executing a function call action. The +/// overhead of block, transaction, and receipt processing is already subtracted +/// in the returned result. It does so by executing the method n+1 times in a +/// single transaction, and subtract the cost of a transaction that calls the +/// method once, before dividing by n. +pub(crate) fn fn_cost_in_contract( + ctx: &mut EstimatorContext, + method: &str, + code: &[u8], + n_actions: usize, +) -> GasCost { + let n_warmup_blocks = ctx.config.warmup_iters_per_block; + let n_blocks = n_warmup_blocks + ctx.config.iter_per_block; + let mut testbed = ctx.testbed(); + + let mut chosen_accounts = { + let tb = testbed.transaction_builder(); + iter::repeat_with(|| tb.random_unused_account()).take(n_blocks + 1).collect::>() + }; + testbed.clear_caches(); + + for account in &chosen_accounts { + let tb = testbed.transaction_builder(); + let setup = vec![Action::DeployContract(DeployContractAction { code: code.to_vec() })]; + let setup_tx = tb.transaction_from_actions(account.clone(), account.clone(), setup); + + testbed.process_block(vec![setup_tx], 0); + } + + let mut blocks = Vec::with_capacity(n_blocks); + // Measurement blocks with single tx with many actions. + for account in chosen_accounts.drain(..n_blocks) { + let actions = iter::repeat_with(|| function_call_action(method.to_string())) + .take(n_actions) + .collect(); + let tx = testbed.transaction_builder().transaction_from_actions( + account.clone(), + account, + actions, + ); + blocks.push(vec![tx]); + } + // Base with single tx with single action. Insert it after warm-up blocks. + let final_account = chosen_accounts.pop().unwrap(); + let base_tx = testbed.transaction_builder().transaction_from_actions( + final_account.clone(), + final_account, + vec![function_call_action(method.to_string())], + ); + blocks.insert(n_warmup_blocks, vec![base_tx]); + + let mut measurements = testbed.measure_blocks(blocks, 0); + measurements.drain(0..n_warmup_blocks); + + let (base_gas_cost, _base_ext_costs) = measurements.remove(0); + // Do not subtract block overhead because we already subtract the base. + let overhead = None; + let block_size = 1; + let (gas_cost, _ext_costs) = + aggregate_per_block_measurements(block_size, measurements, overhead); + gas_cost.saturating_sub(&base_gas_cost, &NonNegativeTolerance::Strict) / (n_actions - 1) as u64 +} + +fn function_call_action(method_name: String) -> Action { + Action::FunctionCall(Box::new(FunctionCallAction { + method_name, + args: Vec::new(), + gas: 10u64.pow(15), + deposit: 0, + })) +} + +/// Takes a list of measurements of input blocks and returns the cost for a +/// single work item. +/// +/// Inputs measurements cover the work to ingest and fully process it. Note that +/// the processing can span multiple block ticks but the measured work is +/// defined in a single block. +/// +/// Each block is assumed to contain `block_size` amount of work +/// items to be measured. Usually, one such work item is a transaction, or an +/// action within a transaction. +/// +/// The output is the cost of a single work item. +pub(crate) fn aggregate_per_block_measurements( + block_size: usize, + block_measurements: Vec<(GasCost, HashMap)>, + overhead: Option, +) -> (GasCost, HashMap) { + let mut block_costs = Vec::new(); + let mut total_ext_costs: HashMap = HashMap::new(); + let mut total = GasCost::zero(); + let num_blocks = block_measurements.len() as u64; + for (block_cost, block_ext_cost) in block_measurements { + block_costs.push(block_cost.to_gas() as f64); + total += block_cost; + for (c, v) in block_ext_cost { + *total_ext_costs.entry(c).or_default() += v; + } + } + + let work_item_ext_cost = { + for v in total_ext_costs.values_mut() { + let n = num_blocks * block_size as u64; + *v /= n; + } + total_ext_costs + }; + + let mut avg_block_cost = total / num_blocks; + if is_high_variance(&block_costs) { + avg_block_cost.set_uncertain("HIGH-VARIANCE"); + } + if let Some(overhead) = overhead { + avg_block_cost = avg_block_cost.saturating_sub(&overhead, &NonNegativeTolerance::PER_MILLE); + } + let work_item_gas_cost = avg_block_cost / block_size as u64; + + (work_item_gas_cost, work_item_ext_cost) +} + +pub(crate) fn average_cost(measurements: Vec) -> GasCost { + let scalar_costs = measurements.iter().map(|cost| cost.to_gas() as f64).collect::>(); + let total: GasCost = measurements.into_iter().sum(); + let mut avg = total / scalar_costs.len() as u64; + if is_high_variance(&scalar_costs) { + avg.set_uncertain("HIGH-VARIANCE"); + } + avg +} + +/// We expect our cost computations to be fairly reproducible, and just flag +/// "high-variance" measurements as suspicious. We require that sample standard +/// deviation is no more than 10% of the mean. +/// +/// Note that this looks at block processing times, and each block contains +/// multiples of things we are actually measuring. As low block variance doesn't +/// guarantee low within-block variance, this is necessary an approximate sanity +/// check. +pub(crate) fn is_high_variance(samples: &[f64]) -> bool { + let threshold = 0.1; + + if samples.len() <= 1 { + return true; + } + let mean = samples.iter().copied().sum::() / (samples.len() as f64); + let s2 = samples.iter().map(|value| (mean - *value).powi(2)).sum::() + / (samples.len() - 1) as f64; + let stddev = s2.sqrt(); + stddev / mean > threshold +} + +/// Returns several percentile values from the given vector of costs. For +/// example, the input 0.9 represents the 90th percentile, which is the largest +/// gas cost in the vector for which no more than 90% of all values are smaller. +pub(crate) fn percentiles( + mut costs: Vec, + percentiles: &[f32], +) -> impl Iterator + '_ { + costs.sort(); + let sample_size = costs.len(); + percentiles + .into_iter() + .map(move |p| (p * sample_size as f32).ceil() as usize - 1) + .map(move |idx| costs[idx].clone()) +} + +/// Produce a valid function name with `len` letters +pub(crate) fn generate_fn_name(index: usize, len: usize) -> Vec { + let mut name = Vec::new(); + let mut index = index; + name.push((b'A'..=b'Z').chain(b'a'..=b'z').nth(index % 52).unwrap()); + for _ in 1..len { + index = index / 52; + name.push((b'A'..=b'Z').chain(b'a'..=b'z').nth(index % 52).unwrap()); + } + name +} + +/// Create a WASM module that is empty except for a main method and a single data entry with n characters +pub(crate) fn generate_data_only_contract(data_size: usize, config: &VMConfig) -> Vec { + // Using pseudo-random stream with fixed seed to create deterministic, incompressable payload. + let prng: XorShiftRng = rand::SeedableRng::seed_from_u64(0xdeadbeef); + let payload = prng.sample_iter(&Alphanumeric).take(data_size).collect(); + let payload = String::from_utf8(payload).unwrap(); + let wat_code = format!( + r#"(module + (memory 1) + (func (export "main")) + (data (i32.const 0) "{payload}") + )"# + ); + let wasm = wat::parse_str(wat_code).unwrap(); + // Validate generated code is valid. + unc_vm_runner::prepare::prepare_contract(&wasm, config, VMKind::NearVm).unwrap(); + wasm +} + +pub(crate) fn random_vec(len: usize) -> Vec { + let mut rng = rand::thread_rng(); + (0..len).map(|_| rng.gen()).collect() +} + +#[cfg(test)] +mod test { + use super::percentiles; + use crate::{config::GasMetric, gas_cost::GasCost}; + use rand::prelude::SliceRandom; + + #[track_caller] + fn check_percentiles(gas_values: &[u64], p_values: &[f32], expected_gas_results: &[u64]) { + let costs = + gas_values.iter().map(|n| GasCost::from_gas((*n).into(), GasMetric::Time)).collect(); + + let results = percentiles(costs, p_values).map(|cost| cost.to_gas()).collect::>(); + + assert_eq!(results, expected_gas_results,) + } + + #[test] + fn test_percentiles() { + let mut one_to_thousand = (1..=1000u64).collect::>(); + one_to_thousand.shuffle(&mut rand::thread_rng()); + check_percentiles(&one_to_thousand, &[0.1, 0.5, 0.995], &[100, 500, 995]); + + let mut one_to_ninety_nine = (1..=99u64).collect::>(); + one_to_ninety_nine.shuffle(&mut rand::thread_rng()); + check_percentiles(&one_to_ninety_nine, &[0.1, 0.5, 0.995], &[10, 50, 99]); + + let mut one_to_one_o_one = (1..=101u64).collect::>(); + one_to_one_o_one.shuffle(&mut rand::thread_rng()); + check_percentiles(&one_to_one_o_one, &[0.1, 0.5, 0.995], &[11, 51, 101]); + } +} diff --git a/runtime/runtime-params-estimator/src/vm_estimator.rs b/runtime/runtime-params-estimator/src/vm_estimator.rs new file mode 100644 index 000000000..9e01784a1 --- /dev/null +++ b/runtime/runtime-params-estimator/src/vm_estimator.rs @@ -0,0 +1,176 @@ +use crate::config::GasMetric; +use crate::gas_cost::{GasCost, LeastSquaresTolerance}; +use crate::{utils::read_resource, REAL_CONTRACTS_SAMPLE}; +use unc_parameters::vm::VMKind; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::hash::CryptoHash; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_store::StoreCompiledContractCache; +use unc_vm_runner::internal::VMKindExt; +use unc_vm_runner::logic::VMContext; +use unc_vm_runner::logic::{CompiledContract, CompiledContractCache}; +use unc_vm_runner::ContractCode; + +const CURRENT_ACCOUNT_ID: &str = "alice"; +const SIGNER_ACCOUNT_ID: &str = "bob"; +const SIGNER_ACCOUNT_PK: [u8; 3] = [0, 1, 2]; +const PREDECESSOR_ACCOUNT_ID: &str = "carol"; + +pub(crate) fn create_context(input: Vec) -> VMContext { + VMContext { + current_account_id: CURRENT_ACCOUNT_ID.parse().unwrap(), + signer_account_id: SIGNER_ACCOUNT_ID.parse().unwrap(), + signer_account_pk: Vec::from(&SIGNER_ACCOUNT_PK[..]), + predecessor_account_id: PREDECESSOR_ACCOUNT_ID.parse().unwrap(), + input, + block_height: 10, + block_timestamp: 42, + epoch_height: 0, + account_balance: 2u128, + account_locked_balance: 1u128, + storage_usage: 12, + attached_deposit: 2u128, + prepaid_gas: 10_u64.pow(18), + random_seed: vec![0, 1, 2], + view_config: None, + output_data_receivers: vec![], + } +} + +fn measure_contract( + vm_kind: VMKind, + gas_metric: GasMetric, + contract: &ContractCode, + cache: &dyn CompiledContractCache, +) -> GasCost { + let config_store = RuntimeConfigStore::new(None); + let runtime_config = config_store.get_config(PROTOCOL_VERSION).as_ref(); + let vm_config = runtime_config.wasm_config.clone(); + let start = GasCost::measure(gas_metric); + let vm = vm_kind.runtime(vm_config).unwrap(); + let result = vm.precompile(contract, cache).unwrap(); + let end = start.elapsed(); + result.unwrap_or_else(|err| panic!("compilation failed, {err}")); + end +} + +#[derive(Default, Clone)] +struct MockCompiledContractCache; + +impl CompiledContractCache for MockCompiledContractCache { + fn put(&self, _key: &CryptoHash, _value: CompiledContract) -> std::io::Result<()> { + Ok(()) + } + + fn get(&self, _key: &CryptoHash) -> std::io::Result> { + Ok(None) + } +} + +/// Returns `(a, b)` - approximation coefficients for formula `a + b * x` +/// where `x` is the contract size in bytes. Practically, we compute upper bound +/// of this approximation, assuming that whole contract consists of code only. +fn precompilation_cost( + gas_metric: GasMetric, + vm_kind: VMKind, + verbose: bool, +) -> (GasCost, GasCost) { + if cfg!(debug_assertions) { + eprintln!("WARNING: did you pass --release flag, results do not make sense otherwise") + } + let cache_store1: StoreCompiledContractCache; + let cache_store2 = MockCompiledContractCache; + let use_store = true; + let cache: &dyn CompiledContractCache = if use_store { + let store = unc_store::test_utils::create_test_store(); + cache_store1 = StoreCompiledContractCache::new(&store); + &cache_store1 + } else { + &cache_store2 + }; + let mut xs = vec![]; + let mut ys = vec![]; + + for (path, _) in REAL_CONTRACTS_SAMPLE { + let raw_bytes = read_resource(path); + let contract = ContractCode::new(raw_bytes.to_vec(), None); + xs.push(raw_bytes.len() as u64); + ys.push(measure_contract(vm_kind, gas_metric, &contract, cache)); + } + + // Motivation behind these values is the same as in `fn action_deploy_contract_per_byte`. + let negative_base_tolerance = 369_531_500_000u64; + let rel_factor_tolerance = 0.001; + let (a, b) = GasCost::least_squares_method_gas_cost( + &xs, + &ys, + &LeastSquaresTolerance::default() + .base_abs_nn_tolerance(negative_base_tolerance) + .factor_rel_nn_tolerance(rel_factor_tolerance), + verbose, + ); + + // We multiply `b` by 5/4 to accommodate for the fact that test contracts are typically 80% code, + // so in the worst case it could grow to 100% and our costs still give better upper estimation. + // Safety muliplication with 5/4. + let safety_numer = 5u64; + let safety_denom = 4u64; + let (corrected_a, corrected_b) = + (a * safety_numer / safety_denom, b * safety_numer / safety_denom); + + // Now validate that estimations obtained earlier provides correct upper estimation + // for several other contracts. + // Contracts binaries are taken from unc-sdk-rs examples, ae20fc458858144e4a35faf58be778d13c2b0511. + let validate_contracts = vec![ + // File 139637. + read_resource("res/status_message.wasm"), + // File 157010. + read_resource("res/mission_control.wasm"), + // File 218444. + read_resource("res/fungible_token.wasm"), + ]; + + for raw_bytes in validate_contracts { + let contract = ContractCode::new(raw_bytes.to_vec(), None); + let x = raw_bytes.len() as u64; + let y = measure_contract(vm_kind, gas_metric, &contract, cache); + let expect = corrected_a.to_gas() as i128 + corrected_b.to_gas() as i128 * (x as i128); + let error = expect - (y.to_gas() as i128); + if gas_metric == GasMetric::ICount { + // Time based metric may lead to unpredictable results. + assert!(error >= 0); + } + } + + (corrected_a, corrected_b) +} + +pub(crate) fn compile_single_contract_cost( + metric: GasMetric, + vm_kind: VMKind, + contract_bytes: &[u8], +) -> GasCost { + let contract = ContractCode::new(contract_bytes.to_vec(), None); + + let store = unc_store::test_utils::create_test_store(); + let cache = StoreCompiledContractCache::new(&store); + + measure_contract(vm_kind, metric, &contract, &cache) +} + +pub(crate) fn compute_compile_cost_vm( + metric: GasMetric, + vm_kind: VMKind, + verbose: bool, +) -> (GasCost, GasCost) { + let (a, b) = precompilation_cost(metric, vm_kind, verbose); + let base = a.to_gas(); + let per_byte = b.to_gas(); + if verbose { + println!( + "{:?} using {:?}: in a + b * x: a = {:?}, b = {:?}, base = {} gas, per_byte = {} gas", + vm_kind, metric, a, b, base, per_byte + ); + } + (a, b) +} diff --git a/runtime/runtime-params-estimator/test-binaries/.gitkeep b/runtime/runtime-params-estimator/test-binaries/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/runtime/runtime/Cargo.toml b/runtime/runtime/Cargo.toml new file mode 100644 index 000000000..2c891457f --- /dev/null +++ b/runtime/runtime/Cargo.toml @@ -0,0 +1,82 @@ +[package] +name = "node-runtime" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +borsh.workspace = true +hex.workspace = true +num-bigint.workspace = true +num-rational.workspace = true +num-traits.workspace = true +once_cell.workspace = true +rand.workspace = true +rayon.workspace = true +serde.workspace = true +serde_json.workspace = true +sha2.workspace = true +thiserror.workspace = true +tracing.workspace = true + +unc-chain-configs.workspace = true +unc-crypto.workspace = true +unc-o11y.workspace = true +unc-parameters.workspace = true +unc-primitives.workspace = true +unc-primitives-core.workspace = true +unc-store.workspace = true +unc-vm-runner.workspace = true +unc-wallet-contract.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-o11y/nightly", + "unc-parameters/nightly", + "unc-primitives-core/nightly", + "unc-primitives/nightly", + "unc-store/nightly", + "unc-vm-runner/nightly", + "unc-wallet-contract/nightly", +] +default = [] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-parameters/nightly_protocol", + "unc-primitives-core/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", + "unc-vm-runner/nightly_protocol", + "unc-wallet-contract/nightly_protocol", +] +no_cpu_compatibility_checks = ["unc-vm-runner/no_cpu_compatibility_checks"] + +no_cache = [ + "unc-vm-runner/no_cache", + "unc-store/no_cache", +] + +sandbox = ["unc-vm-runner/sandbox"] + +[dev-dependencies] +assert_matches.workspace = true +enum-map.workspace = true +indicatif.workspace = true +rayon.workspace = true +serde_json.workspace = true +tempfile.workspace = true + +unc-chain-configs.workspace = true +unc-store = { workspace = true, features = ["test_features"] } +unc-test-contracts.workspace = true +testlib.workspace = true diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs new file mode 100644 index 000000000..00533b496 --- /dev/null +++ b/runtime/runtime/src/actions.rs @@ -0,0 +1,1908 @@ +use crate::config::{ + safe_add_compute, safe_add_gas, total_prepaid_exec_fees, total_prepaid_gas, + total_prepaid_send_fees, +}; +use crate::ext::{ExternalError, RuntimeExt}; +use crate::receipt_manager::ReceiptManager; +use crate::{metrics, ActionResult, ApplyState}; + +use unc_crypto::PublicKey; +use unc_parameters::{AccountCreationConfig, ActionCosts, RuntimeConfig, RuntimeFeesConfig}; +use unc_primitives::account::{AccessKey, AccessKeyPermission, Account}; +use unc_primitives::action::delegate::{DelegateAction, SignedDelegateAction}; +use unc_primitives::checked_feature; +use unc_primitives::config::ViewConfig; +use unc_primitives::errors::{ActionError, ActionErrorKind, InvalidAccessKeyError, RuntimeError}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum}; +use unc_primitives::transaction::{ + Action, AddKeyAction, DeleteAccountAction, DeleteKeyAction, DeployContractAction, + FunctionCallAction, StakeAction, TransferAction, RegisterRsa2048KeysAction, CreateRsa2048ChallengeAction, +}; +use unc_primitives::types::validator_power::ValidatorPower; +use unc_primitives::types::{AccountId, BlockHeight, EpochInfoProvider, Gas, TrieCacheMode}; +use unc_primitives::utils::{account_is_implicit, create_random_seed}; +use unc_primitives::version::{ + ProtocolFeature, ProtocolVersion, DELETE_KEY_STORAGE_USAGE_PROTOCOL_VERSION, +}; +use unc_primitives_core::account::id::AccountType; +use unc_store::{get_access_key, get_code, get_rsa2048_keys, remove_access_key, remove_account, set_access_key, set_code, set_rsa2048_keys, StorageError, TrieUpdate}; +use unc_vm_runner::logic::errors::{ + CompilationError, FunctionCallError, InconsistentStateError, VMRunnerError, +}; +use unc_vm_runner::logic::types::PromiseResult; +use unc_vm_runner::logic::{VMContext, VMOutcome}; +use unc_vm_runner::precompile_contract; +use unc_vm_runner::ContractCode; +use unc_wallet_contract::{wallet_contract, wallet_contract_magic_bytes}; + +use std::sync::Arc; +use unc_primitives::types::validator_frozen::ValidatorFrozen; + +/// Returns `ContractCode` (if exists) for the given `account` or returns `StorageError`. +/// For ETH-implicit accounts returns `Wallet Contract` implementation that it is a part +/// of the protocol and it's cached in memory. +fn get_contract_code( + runtime_ext: &RuntimeExt, + account: &Account, + protocol_version: ProtocolVersion, +) -> Result>, StorageError> { + let account_id = runtime_ext.account_id(); + let code_hash = account.code_hash(); + if checked_feature!("stable", EthImplicitAccounts, protocol_version) + && account_id.get_account_type() == AccountType::EthImplicitAccount + { + assert!(code_hash == *wallet_contract_magic_bytes().hash()); + return Ok(Some(wallet_contract())); + } + runtime_ext.get_code(code_hash).map(|option| option.map(Arc::new)) +} + +/// Runs given function call with given context / apply state. +pub(crate) fn execute_function_call( + apply_state: &ApplyState, + runtime_ext: &mut RuntimeExt, + account: &Account, + predecessor_id: &AccountId, + action_receipt: &ActionReceipt, + promise_results: &[PromiseResult], + function_call: &FunctionCallAction, + action_hash: &CryptoHash, + config: &RuntimeConfig, + is_last_action: bool, + view_config: Option, +) -> Result { + let account_id = runtime_ext.account_id(); + tracing::debug!(target: "runtime", %account_id, "Calling the contract"); + let code = match get_contract_code(&runtime_ext, account, apply_state.current_protocol_version) + { + Ok(Some(code)) => code, + Ok(None) => { + let error = FunctionCallError::CompilationError(CompilationError::CodeDoesNotExist { + account_id: account_id.as_str().into(), + }); + return Ok(VMOutcome::nop_outcome(error)); + } + Err(e) => { + return Err(RuntimeError::StorageError(e)); + } + }; + // Output data receipts are ignored if the function call is not the last action in the batch. + let output_data_receivers: Vec<_> = if is_last_action { + action_receipt.output_data_receivers.iter().map(|r| r.receiver_id.clone()).collect() + } else { + vec![] + }; + let random_seed = create_random_seed( + apply_state.current_protocol_version, + *action_hash, + apply_state.random_seed, + ); + let context = VMContext { + current_account_id: runtime_ext.account_id().clone(), + signer_account_id: action_receipt.signer_id.clone(), + signer_account_pk: borsh::to_vec(&action_receipt.signer_public_key) + .expect("Failed to serialize"), + predecessor_account_id: predecessor_id.clone(), + input: function_call.args.clone(), + block_height: apply_state.block_height, + block_timestamp: apply_state.block_timestamp, + epoch_height: apply_state.epoch_height, + account_balance: account.amount(), + account_locked_balance: account.locked(), + storage_usage: account.storage_usage(), + attached_deposit: function_call.deposit, + prepaid_gas: function_call.gas, + random_seed, + view_config: view_config.clone(), + output_data_receivers, + }; + + // Enable caching chunk mode for the function call. This allows to charge for nodes touched in a chunk only once for + // the first access time. Although nodes are accessed for other actions as well, we do it only here because we + // charge only for trie nodes touched during function calls. + // TODO (#5920): Consider using RAII for switching the state back + let protocol_version = runtime_ext.protocol_version(); + if checked_feature!("stable", ChunkNodesCache, protocol_version) { + runtime_ext.set_trie_cache_mode(TrieCacheMode::CachingChunk); + } + let result = unc_vm_runner::run( + &code, + &function_call.method_name, + runtime_ext, + context, + &config.wasm_config, + &config.fees, + promise_results, + apply_state.cache.as_deref(), + ); + + if checked_feature!("stable", ChunkNodesCache, protocol_version) { + runtime_ext.set_trie_cache_mode(TrieCacheMode::CachingShard); + } + + // There are many specific errors that the runtime can encounter. + // Some can be translated to the more general `RuntimeError`, which allows to pass + // the error up to the caller. For all other cases, panicking here is better + // than leaking the exact details further up. + // Note that this does not include errors caused by user code / input, those are + // stored in outcome.aborted. + let mut outcome = result.map_err(|e| match e { + VMRunnerError::ExternalError(any_err) => { + let err: ExternalError = + any_err.downcast().expect("Downcasting AnyError should not fail"); + match err { + ExternalError::StorageError(err) => err.into(), + ExternalError::ValidatorError(err) => RuntimeError::ValidatorError(err), + } + } + VMRunnerError::InconsistentStateError(err @ InconsistentStateError::IntegerOverflow) => { + StorageError::StorageInconsistentState(err.to_string()).into() + } + VMRunnerError::CacheError(err) => { + metrics::FUNCTION_CALL_PROCESSED_CACHE_ERRORS.with_label_values(&[(&err).into()]).inc(); + StorageError::StorageInconsistentState(err.to_string()).into() + } + VMRunnerError::LoadingError(msg) => { + panic!("Contract runtime failed to load a contrct: {msg}") + } + VMRunnerError::Nondeterministic(msg) => { + panic!("Contract runner returned non-deterministic error '{}', aborting", msg) + } + VMRunnerError::WasmUnknownError { debug_message } => { + panic!("Wasmer returned unknown message: {}", debug_message) + } + })?; + + if !view_config.is_some() { + let unused_gas = function_call.gas.saturating_sub(outcome.used_gas); + let distributed = runtime_ext.receipt_manager.distribute_gas(unused_gas)?; + outcome.used_gas = safe_add_gas(outcome.used_gas, distributed)?; + } + Ok(outcome) +} + +pub(crate) fn action_function_call( + state_update: &mut TrieUpdate, + apply_state: &ApplyState, + account: &mut Account, + receipt: &Receipt, + action_receipt: &ActionReceipt, + promise_results: &[PromiseResult], + result: &mut ActionResult, + account_id: &AccountId, + function_call: &FunctionCallAction, + action_hash: &CryptoHash, + config: &RuntimeConfig, + is_last_action: bool, + epoch_info_provider: &dyn EpochInfoProvider, +) -> Result<(), RuntimeError> { + if account.amount().checked_add(function_call.deposit).is_none() { + return Err(StorageError::StorageInconsistentState( + "Account balance integer overflow during function call deposit".to_string(), + ) + .into()); + } + let mut receipt_manager = ReceiptManager::default(); + let mut runtime_ext = RuntimeExt::new( + state_update, + &mut receipt_manager, + account_id, + action_hash, + &apply_state.epoch_id, + &apply_state.prev_block_hash, + &apply_state.block_hash, + epoch_info_provider, + apply_state.current_protocol_version, + ); + let outcome = execute_function_call( + apply_state, + &mut runtime_ext, + account, + &receipt.predecessor_id, + action_receipt, + promise_results, + function_call, + action_hash, + config, + is_last_action, + None, + )?; + + match &outcome.aborted { + None => { + metrics::FUNCTION_CALL_PROCESSED.with_label_values(&["ok"]).inc(); + } + Some(err) => { + metrics::FUNCTION_CALL_PROCESSED.with_label_values(&[err.into()]).inc(); + } + } + + let execution_succeeded = outcome.aborted.is_none(); + if let Some(err) = outcome.aborted { + // collect metrics for failed function calls + metrics::FUNCTION_CALL_PROCESSED_FUNCTION_CALL_ERRORS + .with_label_values(&[(&err).into()]) + .inc(); + match &err { + FunctionCallError::CompilationError(err) => { + metrics::FUNCTION_CALL_PROCESSED_COMPILATION_ERRORS + .with_label_values(&[err.into()]) + .inc(); + } + FunctionCallError::LinkError { .. } => (), + FunctionCallError::MethodResolveError(err) => { + metrics::FUNCTION_CALL_PROCESSED_METHOD_RESOLVE_ERRORS + .with_label_values(&[err.into()]) + .inc(); + } + FunctionCallError::WasmTrap(ref inner_err) => { + metrics::FUNCTION_CALL_PROCESSED_WASM_TRAP_ERRORS + .with_label_values(&[inner_err.into()]) + .inc(); + } + FunctionCallError::HostError(ref inner_err) => { + metrics::FUNCTION_CALL_PROCESSED_HOST_ERRORS + .with_label_values(&[inner_err.into()]) + .inc(); + } + } + // Update action result with the abort error converted to the + // transaction runtime's format of errors. + let action_err: ActionError = ActionErrorKind::FunctionCallError(err.into()).into(); + result.result = Err(action_err); + } + result.gas_burnt = safe_add_gas(result.gas_burnt, outcome.burnt_gas)?; + result.gas_burnt_for_function_call = + safe_add_gas(result.gas_burnt_for_function_call, outcome.burnt_gas)?; + // Runtime in `generate_refund_receipts` takes care of using proper value for refunds. + // It uses `gas_used` for success and `gas_burnt` for failures. So it's not an issue to + // return a real `gas_used` instead of the `gas_burnt` into `ActionResult` even for + // `FunctionCall`s error. + result.gas_used = safe_add_gas(result.gas_used, outcome.used_gas)?; + result.compute_usage = safe_add_compute(result.compute_usage, outcome.compute_usage)?; + result.logs.extend(outcome.logs); + result.profile.merge(&outcome.profile); + if execution_succeeded { + let new_receipts: Vec<_> = receipt_manager + .action_receipts + .into_iter() + .map(|(receiver_id, receipt)| Receipt { + predecessor_id: account_id.clone(), + receiver_id, + // Actual receipt ID is set in the Runtime.apply_action_receipt(...) in the + // "Generating receipt IDs" section + receipt_id: CryptoHash::default(), + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: action_receipt.signer_id.clone(), + signer_public_key: action_receipt.signer_public_key.clone(), + gas_price: action_receipt.gas_price, + output_data_receivers: receipt.output_data_receivers, + input_data_ids: receipt.input_data_ids, + actions: receipt.actions, + }), + }) + .collect(); + + account.set_amount(outcome.balance); + account.set_storage_usage(outcome.storage_usage); + result.result = Ok(outcome.return_data); + result.new_receipts.extend(new_receipts); + } + + Ok(()) +} + +pub(crate) fn action_stake( + account: &mut Account, + result: &mut ActionResult, + account_id: &AccountId, + stake: &StakeAction, + last_block_hash: &CryptoHash, + epoch_info_provider: &dyn EpochInfoProvider, +) -> Result<(), RuntimeError> { + let increment = stake.stake.saturating_sub(account.locked()); + + if account.amount() >= increment { + if account.locked() == 0 && stake.stake == 0 { + // if the account hasn't staked, it cannot unstake + result.result = + Err(ActionErrorKind::TriesToUnstake { account_id: account_id.clone() }.into()); + return Ok(()); + } + + if stake.stake > 0 { + let minimum_stake = epoch_info_provider.minimum_frozen(last_block_hash)?; + if stake.stake < minimum_stake { + result.result = Err(ActionErrorKind::InsufficientStake { + account_id: account_id.clone(), + stake: stake.stake, + minimum_stake, + } + .into()); + return Ok(()); + } + } + + result.validator_frozen_proposals.push(ValidatorFrozen::new( + account_id.clone(), + stake.public_key.clone(), + stake.stake, + )); + if stake.stake > account.locked() { + // We've checked above `account.amount >= increment` + account.set_amount(account.amount() - increment); + account.set_locked(stake.stake); + } + } else { + result.result = Err(ActionErrorKind::TriesToStake { + account_id: account_id.clone(), + stake: stake.stake, + locked: account.locked(), + balance: account.amount(), + } + .into()); + } + Ok(()) +} + +/// Tries to refunds the allowance of the access key for a gas refund action. +pub(crate) fn try_refund_allowance( + state_update: &mut TrieUpdate, + account_id: &AccountId, + public_key: &PublicKey, + transfer: &TransferAction, +) -> Result<(), StorageError> { + if let Some(mut access_key) = get_access_key(state_update, account_id, public_key)? { + let mut updated = false; + if let AccessKeyPermission::FunctionCall(function_call_permission) = + &mut access_key.permission + { + if let Some(allowance) = function_call_permission.allowance.as_mut() { + let new_allowance = allowance.saturating_add(transfer.deposit); + if new_allowance > *allowance { + *allowance = new_allowance; + updated = true; + } + } + } + if updated { + set_access_key(state_update, account_id.clone(), public_key.clone(), &access_key); + } + } + Ok(()) +} + +pub(crate) fn action_transfer( + account: &mut Account, + transfer: &TransferAction, +) -> Result<(), StorageError> { + account.set_amount(account.amount().checked_add(transfer.deposit).ok_or_else(|| { + StorageError::StorageInconsistentState("Account balance integer overflow".to_string()) + })?); + Ok(()) +} + +pub(crate) fn action_create_account( + fee_config: &RuntimeFeesConfig, + account_creation_config: &AccountCreationConfig, + account: &mut Option, + actor_id: &mut AccountId, + account_id: &AccountId, + predecessor_id: &AccountId, + result: &mut ActionResult, +) { + if account_id.is_top_level() { + if account_id.len() < account_creation_config.min_allowed_top_level_account_length as usize + && predecessor_id != &account_creation_config.registrar_account_id + { + // A short top-level account ID can only be created registrar account. + result.result = Err(ActionErrorKind::CreateAccountOnlyByRegistrar { + account_id: account_id.clone(), + registrar_account_id: account_creation_config.registrar_account_id.clone(), + predecessor_id: predecessor_id.clone(), + } + .into()); + return; + } else { + // OK: Valid top-level Account ID + } + } else if !account_id.is_sub_account_of(predecessor_id) { + // The sub-account can only be created by its root account. E.g. `alice.near` only by `near` + result.result = Err(ActionErrorKind::CreateAccountNotAllowed { + account_id: account_id.clone(), + predecessor_id: predecessor_id.clone(), + } + .into()); + return; + } else { + // OK: Valid sub-account ID by proper predecessor. + } + + *actor_id = account_id.clone(); + *account = Some(Account::new( + 0, + 0, + 0, + CryptoHash::default(), + fee_config.storage_usage_config.num_bytes_account, + )); +} + +/// Can only be used for implicit accounts. +pub(crate) fn action_implicit_account_creation_transfer( + state_update: &mut TrieUpdate, + apply_state: &ApplyState, + fee_config: &RuntimeFeesConfig, + account: &mut Option, + actor_id: &mut AccountId, + account_id: &AccountId, + transfer: &TransferAction, + block_height: BlockHeight, + current_protocol_version: ProtocolVersion, +) { + *actor_id = account_id.clone(); + + match account_id.get_account_type() { + AccountType::NearImplicitAccount => { + let mut access_key = AccessKey::full_access(); + // Set default nonce for newly created access key to avoid transaction hash collision. + // See . + if checked_feature!( + "stable", + AccessKeyNonceForImplicitAccounts, + current_protocol_version + ) { + access_key.nonce = (block_height - 1) + * unc_primitives::account::AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER; + } + + // unwrap: here it's safe because the `account_id` has already been determined to be implicit by `get_account_type` + let public_key = PublicKey::from_unc_implicit_account(account_id).unwrap(); + + *account = Some(Account::new( + transfer.deposit, + 0, + 0, + CryptoHash::default(), + fee_config.storage_usage_config.num_bytes_account + + public_key.len() as u64 + + borsh::object_length(&access_key).unwrap() as u64 + + fee_config.storage_usage_config.num_extra_bytes_record, + )); + + set_access_key(state_update, account_id.clone(), public_key, &access_key); + } + // Invariant: The `account_id` is implicit. + // It holds because in the only calling site, we've checked the permissions before. + AccountType::EthImplicitAccount => { + if checked_feature!("stable", EthImplicitAccounts, current_protocol_version) { + // We deploy "near[wallet contract hash]" magic bytes as the contract code, + // to mark that this is a uncd-defined contract. It will not be used on a function call. + // Instead, uncd-defined Wallet Contract implementation will be used. + let magic_bytes = wallet_contract_magic_bytes(); + + let storage_usage = fee_config.storage_usage_config.num_bytes_account + + magic_bytes.code().len() as u64 + + fee_config.storage_usage_config.num_extra_bytes_record; + + *account = + Some(Account::new(transfer.deposit, 0, 0, *magic_bytes.hash(), storage_usage)); + set_code(state_update, account_id.clone(), &magic_bytes); + + // Precompile Wallet Contract and store result (compiled code or error) in the database. + // Note this contract is shared among ETH-implicit accounts and `precompile_contract` + // is a no-op if the contract was already compiled. + precompile_contract( + &wallet_contract(), + &apply_state.config.wasm_config, + apply_state.cache.as_deref(), + ) + .ok(); + } else { + // This panic is unreachable as this is an implicit account creation transfer. + // `check_account_existence` would fail because in this protocol version `account_is_implicit` + // would return false for an account that is of the ETH-implicit type. + panic!("must be unc-implicit"); + } + } + // This panic is unreachable as this is an implicit account creation transfer. + // `check_account_existence` would fail because `account_is_implicit` would return false for a Named account. + AccountType::NamedAccount => panic!("must be implicit"), + } +} + +pub(crate) fn action_deploy_contract( + state_update: &mut TrieUpdate, + account: &mut Account, + account_id: &AccountId, + deploy_contract: &DeployContractAction, + apply_state: &ApplyState, +) -> Result<(), StorageError> { + let _span = tracing::debug_span!(target: "runtime", "action_deploy_contract").entered(); + let code = ContractCode::new(deploy_contract.code.clone(), None); + let prev_code = get_code(state_update, account_id, Some(account.code_hash()))?; + let prev_code_length = prev_code.map(|code| code.code().len() as u64).unwrap_or_default(); + account.set_storage_usage(account.storage_usage().saturating_sub(prev_code_length)); + account.set_storage_usage( + account.storage_usage().checked_add(code.code().len() as u64).ok_or_else(|| { + StorageError::StorageInconsistentState(format!( + "Storage usage integer overflow for account {}", + account_id + )) + })?, + ); + account.set_code_hash(*code.hash()); + set_code(state_update, account_id.clone(), &code); + // Precompile the contract and store result (compiled code or error) in the database. + // Note, that contract compilation costs are already accounted in deploy cost using + // special logic in estimator (see get_runtime_config() function). + precompile_contract(&code, &apply_state.config.wasm_config, apply_state.cache.as_deref()).ok(); + Ok(()) +} + +pub(crate) fn action_delete_account( + state_update: &mut TrieUpdate, + account: &mut Option, + actor_id: &mut AccountId, + receipt: &Receipt, + result: &mut ActionResult, + account_id: &AccountId, + delete_account: &DeleteAccountAction, + current_protocol_version: ProtocolVersion, +) -> Result<(), StorageError> { + if current_protocol_version >= ProtocolFeature::DeleteActionRestriction.protocol_version() { + let account = account.as_ref().unwrap(); + let mut account_storage_usage = account.storage_usage(); + let contract_code = get_code(state_update, account_id, Some(account.code_hash()))?; + if let Some(code) = contract_code { + // account storage usage should be larger than code size + let code_len = code.code().len() as u64; + debug_assert!(account_storage_usage > code_len); + account_storage_usage = account_storage_usage.saturating_sub(code_len); + } + if account_storage_usage > Account::MAX_ACCOUNT_DELETION_STORAGE_USAGE { + result.result = Err(ActionErrorKind::DeleteAccountWithLargeState { + account_id: account_id.clone(), + } + .into()); + return Ok(()); + } + } + // We use current amount as a pay out to beneficiary. + let account_balance = account.as_ref().unwrap().amount(); + if account_balance > 0 { + result + .new_receipts + .push(Receipt::new_balance_refund(&delete_account.beneficiary_id, account_balance)); + } + remove_account(state_update, account_id)?; + *actor_id = receipt.predecessor_id.clone(); + *account = None; + Ok(()) +} + +pub(crate) fn action_delete_key( + fee_config: &RuntimeFeesConfig, + state_update: &mut TrieUpdate, + account: &mut Account, + result: &mut ActionResult, + account_id: &AccountId, + delete_key: &DeleteKeyAction, + current_protocol_version: ProtocolVersion, +) -> Result<(), StorageError> { + let access_key = get_access_key(state_update, account_id, &delete_key.public_key)?; + if let Some(access_key) = access_key { + let storage_usage_config = &fee_config.storage_usage_config; + let storage_usage = if current_protocol_version >= DELETE_KEY_STORAGE_USAGE_PROTOCOL_VERSION + { + borsh::object_length(&delete_key.public_key).unwrap() as u64 + + borsh::object_length(&access_key).unwrap() as u64 + + storage_usage_config.num_extra_bytes_record + } else { + borsh::object_length(&delete_key.public_key).unwrap() as u64 + + borsh::object_length(&Some(access_key)).unwrap() as u64 + + storage_usage_config.num_extra_bytes_record + }; + // Remove access key + remove_access_key(state_update, account_id.clone(), delete_key.public_key.clone()); + account.set_storage_usage(account.storage_usage().saturating_sub(storage_usage)); + } else { + result.result = Err(ActionErrorKind::DeleteKeyDoesNotExist { + public_key: delete_key.public_key.clone().into(), + account_id: account_id.clone(), + } + .into()); + } + Ok(()) +} + +pub(crate) fn action_add_key( + apply_state: &ApplyState, + state_update: &mut TrieUpdate, + account: &mut Account, + result: &mut ActionResult, + account_id: &AccountId, + add_key: &AddKeyAction, +) -> Result<(), StorageError> { + if get_access_key(state_update, account_id, &add_key.public_key)?.is_some() { + result.result = Err(ActionErrorKind::AddKeyAlreadyExists { + account_id: account_id.to_owned(), + public_key: add_key.public_key.clone().into(), + } + .into()); + return Ok(()); + } + if checked_feature!("stable", AccessKeyNonceRange, apply_state.current_protocol_version) { + let mut access_key = add_key.access_key.clone(); + access_key.nonce = (apply_state.block_height - 1) + * unc_primitives::account::AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER; + set_access_key(state_update, account_id.clone(), add_key.public_key.clone(), &access_key); + } else { + set_access_key( + state_update, + account_id.clone(), + add_key.public_key.clone(), + &add_key.access_key, + ); + }; + let storage_config = &apply_state.config.fees.storage_usage_config; + account.set_storage_usage( + account + .storage_usage() + .checked_add( + borsh::object_length(&add_key.public_key).unwrap() as u64 + + borsh::object_length(&add_key.access_key).unwrap() as u64 + + storage_config.num_extra_bytes_record, + ) + .ok_or_else(|| { + StorageError::StorageInconsistentState(format!( + "Storage usage integer overflow for account {}", + account_id + )) + })?, + ); + Ok(()) +} + +pub(crate) fn action_register_rsa2048_keys( + apply_state: &ApplyState, + state_update: &mut TrieUpdate, + account: &mut Account, + result: &mut ActionResult, + account_id: &AccountId, + register_key: &RegisterRsa2048KeysAction, +) -> Result<(), StorageError> { + if get_rsa2048_keys(state_update, account_id, ®ister_key.public_key)?.is_some() { + result.result = Err(ActionErrorKind::AddKeyAlreadyExists { + account_id: account_id.to_owned(), + public_key: register_key.public_key.clone().into(), + } + .into()); + return Ok(()); + } + set_rsa2048_keys( + state_update, + account_id.clone(), + register_key.public_key.clone(), + ®ister_key, + ); + let storage_config = &apply_state.config.fees.storage_usage_config; + account.set_storage_usage( + account + .storage_usage() + .checked_add( + borsh::object_length(®ister_key).unwrap() as u64 + + storage_config.num_extra_bytes_record, + ) + .ok_or_else(|| { + StorageError::StorageInconsistentState(format!( + "Storage usage integer overflow for account {}", + account_id + )) + })?, + ); + Ok(()) +} + +pub(crate) fn action_create_rsa2048_challenge( + _apply_state: &ApplyState, + state_update: &mut TrieUpdate, + _account: &mut Account, + result: &mut ActionResult, + account_id: &AccountId, + challenge: &CreateRsa2048ChallengeAction, +) -> Result<(), RuntimeError>{ + //TODO: 从root 基金会获取的公钥, 匹配验证签名以及附加参数(如算力, 矿工信息, dev_id, 等) + let root_id = "unc".parse::().unwrap(); + if get_rsa2048_keys(state_update, &root_id, &challenge.public_key)?.is_none() { + result.result = Err(ActionErrorKind::RsaKeysNotFound { + account_id: account_id.to_owned(), + public_key: challenge.public_key.clone().into(), + }.into()); + return Ok(()); + } + + //FIXME: 计算发起challenge 的nonce 随机数 + // 直接使用 root证书里面的args, 如算力 + let args = get_rsa2048_keys(state_update, &root_id, &challenge.public_key)?.unwrap().args; + match serde_json::from_slice::(&args) { + Ok(parsed_args) => { + if let Some(power_val) = parsed_args.get("power") { + match power_val.as_str() { + Some(power_str) => { + match power_str.parse::() { + Ok(power) => { + // push power to validator proposal + result.validator_power_proposals.push(ValidatorPower::new( + account_id.clone(), + challenge.challenge_key.clone().into(), + power, + )); + // attach power to account + _account.set_power(power); + println!("Power (as u128): {}", power); + } + Err(_) => println!("Power value is not a valid u128 number"), + } + }, + None => { + if let Some(power_number) = power_val.as_u64() { + println!("Power (as u64, converted from u128): {}", power_number); + } else { + println!("Power value is not a string or a number that fits into u64"); + } + } + } + } + } + Err(_) => { + return Ok(()); + } + }; + return Ok(()); +} + +pub(crate) fn apply_delegate_action( + state_update: &mut TrieUpdate, + apply_state: &ApplyState, + action_receipt: &ActionReceipt, + sender_id: &AccountId, + signed_delegate_action: &SignedDelegateAction, + result: &mut ActionResult, +) -> Result<(), RuntimeError> { + let delegate_action = &signed_delegate_action.delegate_action; + + if !signed_delegate_action.verify() { + result.result = Err(ActionErrorKind::DelegateActionInvalidSignature.into()); + return Ok(()); + } + if apply_state.block_height > delegate_action.max_block_height { + result.result = Err(ActionErrorKind::DelegateActionExpired.into()); + return Ok(()); + } + if delegate_action.sender_id.as_str() != sender_id.as_str() { + result.result = Err(ActionErrorKind::DelegateActionSenderDoesNotMatchTxReceiver { + sender_id: delegate_action.sender_id.clone(), + receiver_id: sender_id.clone(), + } + .into()); + return Ok(()); + } + + validate_delegate_action_key(state_update, apply_state, delegate_action, result)?; + if result.result.is_err() { + // Validation failed. Need to return Ok() because this is not a runtime error. + // "result.result" will be return to the User as the action execution result. + return Ok(()); + } + + // Generate a new receipt from DelegateAction. + let new_receipt = Receipt { + predecessor_id: sender_id.clone(), + receiver_id: delegate_action.receiver_id.clone(), + receipt_id: CryptoHash::default(), + + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: action_receipt.signer_id.clone(), + signer_public_key: action_receipt.signer_public_key.clone(), + gas_price: action_receipt.gas_price, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: delegate_action.get_actions(), + }), + }; + + // Note, Relayer prepaid all fees and all things required by actions: attached deposits and attached gas. + // If something goes wrong, deposit is refunded to the predecessor, this is sender_id/Sender in DelegateAction. + // Gas is refunded to the signer, this is Relayer. + // Some contracts refund the deposit. Usually they refund the deposit to the predecessor and this is sender_id/Sender from DelegateAction. + // Therefore Relayer should verify DelegateAction before submitting it because it spends the attached deposit. + + let prepaid_send_fees = total_prepaid_send_fees(&apply_state.config, &action_receipt.actions)?; + let required_gas = receipt_required_gas(apply_state, &new_receipt)?; + // This gas will be burnt by the receiver of the created receipt, + result.gas_used = safe_add_gas(result.gas_used, required_gas)?; + // This gas was prepaid on Relayer shard. Need to burn it because the receipt is going to be sent. + // gas_used is incremented because otherwise the gas will be refunded. Refund function checks only gas_used. + result.gas_used = safe_add_gas(result.gas_used, prepaid_send_fees)?; + result.gas_burnt = safe_add_gas(result.gas_burnt, prepaid_send_fees)?; + // TODO(#8806): Support compute costs for actions. For now they match burnt gas. + result.compute_usage = safe_add_compute(result.compute_usage, prepaid_send_fees)?; + result.new_receipts.push(new_receipt); + + Ok(()) +} + +/// Returns Gas amount is required to execute Receipt and all actions it contains +fn receipt_required_gas(apply_state: &ApplyState, receipt: &Receipt) -> Result { + Ok(match &receipt.receipt { + ReceiptEnum::Action(action_receipt) => { + let mut required_gas = safe_add_gas( + total_prepaid_exec_fees( + &apply_state.config, + &action_receipt.actions, + &receipt.receiver_id, + )?, + total_prepaid_gas(&action_receipt.actions)?, + )?; + required_gas = safe_add_gas( + required_gas, + apply_state.config.fees.fee(ActionCosts::new_action_receipt).exec_fee(), + )?; + + required_gas + } + ReceiptEnum::Data(_) => 0, + }) +} + +/// Validate access key which was used for signing DelegateAction: +/// +/// - Checks whether the access key is present fo given public_key and sender_id. +/// - Validates nonce and updates it if it's ok. +/// - Validates access key permissions. +fn validate_delegate_action_key( + state_update: &mut TrieUpdate, + apply_state: &ApplyState, + delegate_action: &DelegateAction, + result: &mut ActionResult, +) -> Result<(), RuntimeError> { + // 'delegate_action.sender_id' account existence must be checked by a caller + let mut access_key = match get_access_key( + state_update, + &delegate_action.sender_id, + &delegate_action.public_key, + )? { + Some(access_key) => access_key, + None => { + result.result = Err(ActionErrorKind::DelegateActionAccessKeyError( + InvalidAccessKeyError::AccessKeyNotFound { + account_id: delegate_action.sender_id.clone(), + public_key: delegate_action.public_key.clone().into(), + }, + ) + .into()); + return Ok(()); + } + }; + + if delegate_action.nonce <= access_key.nonce { + result.result = Err(ActionErrorKind::DelegateActionInvalidNonce { + delegate_nonce: delegate_action.nonce, + ak_nonce: access_key.nonce, + } + .into()); + return Ok(()); + } + + let upper_bound = apply_state.block_height + * unc_primitives::account::AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER; + if delegate_action.nonce >= upper_bound { + result.result = Err(ActionErrorKind::DelegateActionNonceTooLarge { + delegate_nonce: delegate_action.nonce, + upper_bound, + } + .into()); + return Ok(()); + } + + access_key.nonce = delegate_action.nonce; + + let actions = delegate_action.get_actions(); + + // The restriction of "function call" access keys: + // the transaction must contain the only `FunctionCall` if "function call" access key is used + if let AccessKeyPermission::FunctionCall(ref function_call_permission) = access_key.permission { + if actions.len() != 1 { + result.result = Err(ActionErrorKind::DelegateActionAccessKeyError( + InvalidAccessKeyError::RequiresFullAccess, + ) + .into()); + return Ok(()); + } + if let Some(Action::FunctionCall(ref function_call)) = actions.get(0) { + if function_call.deposit > 0 { + result.result = Err(ActionErrorKind::DelegateActionAccessKeyError( + InvalidAccessKeyError::DepositWithFunctionCall, + ) + .into()); + } + if delegate_action.receiver_id != function_call_permission.receiver_id { + result.result = Err(ActionErrorKind::DelegateActionAccessKeyError( + InvalidAccessKeyError::ReceiverMismatch { + tx_receiver: delegate_action.receiver_id.clone(), + ak_receiver: function_call_permission.receiver_id.clone(), + }, + ) + .into()); + return Ok(()); + } + if !function_call_permission.method_names.is_empty() + && function_call_permission + .method_names + .iter() + .all(|method_name| &function_call.method_name != method_name) + { + result.result = Err(ActionErrorKind::DelegateActionAccessKeyError( + InvalidAccessKeyError::MethodNameMismatch { + method_name: function_call.method_name.clone(), + }, + ) + .into()); + return Ok(()); + } + } else { + // There should Action::FunctionCall when "function call" permission is used + result.result = Err(ActionErrorKind::DelegateActionAccessKeyError( + InvalidAccessKeyError::RequiresFullAccess, + ) + .into()); + return Ok(()); + } + }; + + set_access_key( + state_update, + delegate_action.sender_id.clone(), + delegate_action.public_key.clone(), + &access_key, + ); + + Ok(()) +} + +pub(crate) fn check_actor_permissions( + action: &Action, + account: &Option, + actor_id: &AccountId, + account_id: &AccountId, +) -> Result<(), ActionError> { + match action { + Action::DeployContract(_) | Action::Stake(_) | Action::AddKey(_) | Action::DeleteKey(_) => { + if actor_id != account_id { + return Err(ActionErrorKind::ActorNoPermission { + account_id: account_id.clone(), + actor_id: actor_id.clone(), + } + .into()); + } + } + Action::DeleteAccount(_) => { + if actor_id != account_id { + return Err(ActionErrorKind::ActorNoPermission { + account_id: account_id.clone(), + actor_id: actor_id.clone(), + } + .into()); + } + let account = account.as_ref().unwrap(); + if account.locked() != 0 { + return Err(ActionErrorKind::DeleteAccountStaking { + account_id: account_id.clone(), + } + .into()); + } + } + Action::CreateAccount(_) | Action::FunctionCall(_) | Action::Transfer(_) => (), + Action::Delegate(_) => (), + Action::RegisterRsa2048Keys(_) => { + // FIXME: 这里是硬编码, 之后RuntimeConfig中添加配置, 只有创世共识账号基金会账号, 类似registrar账号 + let root_id = "unc".parse::().unwrap(); + if account_id.clone() != root_id { + return Err(ActionErrorKind::ActorNoPermission { + account_id: account_id.clone(), + actor_id: actor_id.clone(), + } + .into()); + } + }, + Action::CreateRsa2048Challenge(_) => (), + }; + Ok(()) +} + +pub(crate) fn check_account_existence( + action: &Action, + account: &Option, + account_id: &AccountId, + config: &RuntimeConfig, + is_the_only_action: bool, + is_refund: bool, +) -> Result<(), ActionError> { + match action { + Action::CreateAccount(_) => { + if account.is_some() { + return Err(ActionErrorKind::AccountAlreadyExists { + account_id: account_id.clone(), + } + .into()); + } else { + // TODO: this should be `config.implicit_account_creation`. + if config.wasm_config.implicit_account_creation + && account_is_implicit(account_id, config.wasm_config.eth_implicit_accounts) + { + // If the account doesn't exist and it's implicit, then you + // should only be able to create it using single transfer action. + // Because you should not be able to add another access key to the account in + // the same transaction. + // Otherwise you can hijack an account without having the private key for the + // public key. We've decided to make it an invalid transaction to have any other + // actions on the implicit hex accounts. + // The easiest way is to reject the `CreateAccount` action. + // See https://github.com/nearprotocol/NEPs/pull/71 + return Err(ActionErrorKind::OnlyImplicitAccountCreationAllowed { + account_id: account_id.clone(), + } + .into()); + } + } + } + Action::Transfer(_) => { + if account.is_none() { + return if config.wasm_config.implicit_account_creation + && is_the_only_action + && account_is_implicit(account_id, config.wasm_config.eth_implicit_accounts) + && !is_refund + { + // OK. It's implicit account creation. + // Notes: + // - The transfer action has to be the only action in the transaction to avoid + // abuse by hijacking this account with other public keys or contracts. + // - Refunds don't automatically create accounts, because refunds are free and + // we don't want some type of abuse. + // - Account deletion with beneficiary creates a refund, so it'll not create a + // new account. + Ok(()) + } else { + Err(ActionErrorKind::AccountDoesNotExist { account_id: account_id.clone() } + .into()) + }; + } + } + Action::DeployContract(_) + | Action::FunctionCall(_) + | Action::Stake(_) + | Action::AddKey(_) + | Action::DeleteKey(_) + | Action::DeleteAccount(_) + | Action::RegisterRsa2048Keys(_) + | Action::CreateRsa2048Challenge(_) => { + if account.is_none() { + return Err(ActionErrorKind::AccountDoesNotExist { + account_id: account_id.clone(), + } + .into()); + } + } + Action::Delegate(_) => { + if account.is_none() { + return Err(ActionErrorKind::AccountDoesNotExist { + account_id: account_id.clone(), + } + .into()); + } + } + }; + Ok(()) +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::unc_primitives::shard_layout::ShardUId; + use unc_primitives::account::FunctionCallPermission; + use unc_primitives::action::delegate::NonDelegateAction; + use unc_primitives::errors::InvalidAccessKeyError; + use unc_primitives::hash::hash; + use unc_primitives::runtime::migration_data::MigrationFlags; + use unc_primitives::transaction::CreateAccountAction; + use unc_primitives::trie_key::TrieKey; + use unc_primitives::types::{EpochId, StateChangeCause}; + use unc_store::set_account; + use unc_store::test_utils::TestTriesBuilder; + use std::sync::Arc; + + fn test_action_create_account( + account_id: AccountId, + predecessor_id: AccountId, + length: u8, + ) -> ActionResult { + let mut account = None; + let mut actor_id = predecessor_id.clone(); + let mut action_result = ActionResult::default(); + action_create_account( + &RuntimeFeesConfig::test(), + &AccountCreationConfig { + min_allowed_top_level_account_length: length, + registrar_account_id: "registrar".parse().unwrap(), + }, + &mut account, + &mut actor_id, + &account_id, + &predecessor_id, + &mut action_result, + ); + if action_result.result.is_ok() { + assert!(account.is_some()); + assert_eq!(actor_id, account_id); + } else { + assert!(account.is_none()); + } + action_result + } + + #[test] + fn test_create_account_valid_top_level_long() { + let account_id = "bob_unc_long_name".parse().unwrap(); + let predecessor_id = "alice.near".parse().unwrap(); + let action_result = test_action_create_account(account_id, predecessor_id, 11); + assert!(action_result.result.is_ok()); + } + + #[test] + fn test_create_account_valid_top_level_by_registrar() { + let account_id = "bob".parse().unwrap(); + let predecessor_id = "registrar".parse().unwrap(); + let action_result = test_action_create_account(account_id, predecessor_id, 11); + assert!(action_result.result.is_ok()); + } + + #[test] + fn test_create_account_valid_sub_account() { + let account_id = "alice.near".parse().unwrap(); + let predecessor_id = "near".parse().unwrap(); + let action_result = test_action_create_account(account_id, predecessor_id, 11); + assert!(action_result.result.is_ok()); + } + + #[test] + fn test_create_account_invalid_sub_account() { + let account_id = "alice.near".parse::().unwrap(); + let predecessor_id = "bob".parse::().unwrap(); + let action_result = + test_action_create_account(account_id.clone(), predecessor_id.clone(), 11); + assert_eq!( + action_result.result, + Err(ActionError { + index: None, + kind: ActionErrorKind::CreateAccountNotAllowed { + account_id: account_id, + predecessor_id: predecessor_id, + }, + }) + ); + } + + #[test] + fn test_create_account_invalid_short_top_level() { + let account_id = "bob".parse::().unwrap(); + let predecessor_id = "near".parse::().unwrap(); + let action_result = + test_action_create_account(account_id.clone(), predecessor_id.clone(), 11); + assert_eq!( + action_result.result, + Err(ActionError { + index: None, + kind: ActionErrorKind::CreateAccountOnlyByRegistrar { + account_id: account_id, + registrar_account_id: "registrar".parse().unwrap(), + predecessor_id: predecessor_id, + }, + }) + ); + } + + #[test] + fn test_create_account_valid_short_top_level_len_allowed() { + let account_id = "bob".parse().unwrap(); + let predecessor_id = "near".parse().unwrap(); + let action_result = test_action_create_account(account_id, predecessor_id, 0); + assert!(action_result.result.is_ok()); + } + + fn test_delete_large_account( + account_id: &AccountId, + code_hash: &CryptoHash, + storage_usage: u64, + state_update: &mut TrieUpdate, + ) -> ActionResult { + let mut account = Some(Account::new(100, 0,0, *code_hash, storage_usage)); + let mut actor_id = account_id.clone(); + let mut action_result = ActionResult::default(); + let receipt = Receipt::new_balance_refund(&"alice.near".parse().unwrap(), 0); + let res = action_delete_account( + state_update, + &mut account, + &mut actor_id, + &receipt, + &mut action_result, + account_id, + &DeleteAccountAction { beneficiary_id: "bob".parse().unwrap() }, + ProtocolFeature::DeleteActionRestriction.protocol_version(), + ); + assert!(res.is_ok()); + action_result + } + + #[test] + fn test_delete_account_too_large() { + let tries = TestTriesBuilder::new().build(); + let mut state_update = + tries.new_trie_update(ShardUId::single_shard(), CryptoHash::default()); + let action_result = test_delete_large_account( + &"alice".parse().unwrap(), + &CryptoHash::default(), + Account::MAX_ACCOUNT_DELETION_STORAGE_USAGE + 1, + &mut state_update, + ); + assert_eq!( + action_result.result, + Err(ActionError { + index: None, + kind: ActionErrorKind::DeleteAccountWithLargeState { + account_id: "alice".parse().unwrap() + } + }) + ) + } + + fn test_delete_account_with_contract(storage_usage: u64) -> ActionResult { + let tries = TestTriesBuilder::new().build(); + let mut state_update = + tries.new_trie_update(ShardUId::single_shard(), CryptoHash::default()); + let account_id = "alice".parse::().unwrap(); + let trie_key = TrieKey::ContractCode { account_id: account_id.clone() }; + let empty_contract = [0; 10_000].to_vec(); + let contract_hash = hash(&empty_contract); + state_update.set(trie_key, empty_contract); + test_delete_large_account(&account_id, &contract_hash, storage_usage, &mut state_update) + } + + #[test] + fn test_delete_account_with_contract_and_small_state() { + let action_result = + test_delete_account_with_contract(Account::MAX_ACCOUNT_DELETION_STORAGE_USAGE + 100); + assert!(action_result.result.is_ok()); + } + + #[test] + fn test_delete_account_with_contract_and_large_state() { + let action_result = + test_delete_account_with_contract(10 * Account::MAX_ACCOUNT_DELETION_STORAGE_USAGE); + assert_eq!( + action_result.result, + Err(ActionError { + index: None, + kind: ActionErrorKind::DeleteAccountWithLargeState { + account_id: "alice".parse().unwrap() + } + }) + ); + } + + fn create_delegate_action_receipt() -> (ActionReceipt, SignedDelegateAction) { + let signed_delegate_action = SignedDelegateAction { + delegate_action: DelegateAction { + sender_id: "bob.test.near".parse().unwrap(), + receiver_id: "token.test.near".parse().unwrap(), + actions: vec![ + non_delegate_action( + Action::FunctionCall( + Box::new(FunctionCallAction { + method_name: "ft_transfer".parse().unwrap(), + args: vec![123, 34, 114, 101, 99, 101, 105, 118, 101, 114, 95, 105, 100, 34, 58, 34, 106, 97, 110, 101, 46, 116, 101, 115, 116, 46, 110, 101, 97, 114, 34, 44, 34, 97, 109, 111, 117, 110, 116, 34, 58, 34, 52, 34, 125], + gas: 30000000000000, + deposit: 1, + }) + ) + ) + ], + nonce: 19000001, + max_block_height: 57, + public_key: "ed25519:32LnPNBZQJ3uhY8yV6JqnNxtRW8E27Ps9YD1XeUNuA1m".parse::().unwrap(), + }, + signature: "ed25519:5oswo6yH6u7xduXHEC4aWc8EGmWdbFz49DaHvAVioS9tbdrxpUtoNQUa8ST9Fxpk7zS2ogWvuKaL29JjMFDi3DLe".parse().unwrap() + }; + + let action_receipt = ActionReceipt { + signer_id: "alice.test.near".parse().unwrap(), + signer_public_key: PublicKey::empty(unc_crypto::KeyType::ED25519), + gas_price: 1, + output_data_receivers: Vec::new(), + input_data_ids: Vec::new(), + actions: vec![Action::Delegate(Box::new(signed_delegate_action.clone()))], + }; + + (action_receipt, signed_delegate_action) + } + + fn create_apply_state(block_height: BlockHeight) -> ApplyState { + ApplyState { + block_height, + prev_block_hash: CryptoHash::default(), + block_hash: CryptoHash::default(), + epoch_id: EpochId::default(), + epoch_height: 3, + gas_price: 2, + block_timestamp: 1, + gas_limit: None, + random_seed: CryptoHash::default(), + current_protocol_version: 1, + config: Arc::new(RuntimeConfig::test()), + cache: None, + is_new_chunk: false, + migration_data: Arc::default(), + migration_flags: MigrationFlags::default(), + } + } + + fn setup_account( + account_id: &AccountId, + public_key: &PublicKey, + access_key: &AccessKey, + ) -> TrieUpdate { + let tries = TestTriesBuilder::new().build(); + let mut state_update = + tries.new_trie_update(ShardUId::single_shard(), CryptoHash::default()); + let account = Account::new(100, 0, 0, CryptoHash::default(), 100); + set_account(&mut state_update, account_id.clone(), &account); + set_access_key(&mut state_update, account_id.clone(), public_key.clone(), access_key); + + state_update.commit(StateChangeCause::InitialState); + let trie_changes = state_update.finalize().unwrap().1; + let mut store_update = tries.store_update(); + let root = tries.apply_all(&trie_changes, ShardUId::single_shard(), &mut store_update); + store_update.commit().unwrap(); + + tries.new_trie_update(ShardUId::single_shard(), root) + } + fn non_delegate_action(action: Action) -> NonDelegateAction { + NonDelegateAction::try_from(action) + .expect("cannot violate type invariants, not even in test") + } + + #[test] + fn test_delegate_action() { + let mut result = ActionResult::default(); + let (action_receipt, signed_delegate_action) = create_delegate_action_receipt(); + let sender_id = signed_delegate_action.delegate_action.sender_id.clone(); + let sender_pub_key = signed_delegate_action.delegate_action.public_key.clone(); + let access_key = AccessKey { nonce: 19000000, permission: AccessKeyPermission::FullAccess }; + + let apply_state = + create_apply_state(signed_delegate_action.delegate_action.max_block_height); + let mut state_update = setup_account(&sender_id, &sender_pub_key, &access_key); + + apply_delegate_action( + &mut state_update, + &apply_state, + &action_receipt, + &sender_id, + &signed_delegate_action, + &mut result, + ) + .expect("Expect ok"); + + assert!(result.result.is_ok(), "Result error: {:?}", result.result.err()); + assert_eq!( + result.new_receipts, + vec![Receipt { + predecessor_id: sender_id.clone(), + receiver_id: signed_delegate_action.delegate_action.receiver_id.clone(), + receipt_id: CryptoHash::default(), + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: action_receipt.signer_id.clone(), + signer_public_key: action_receipt.signer_public_key.clone(), + gas_price: action_receipt.gas_price, + output_data_receivers: Vec::new(), + input_data_ids: Vec::new(), + actions: signed_delegate_action.delegate_action.get_actions(), + }) + }] + ); + } + + #[test] + fn test_delegate_action_signature_verification() { + let mut result = ActionResult::default(); + let (action_receipt, mut signed_delegate_action) = create_delegate_action_receipt(); + let sender_id = signed_delegate_action.delegate_action.sender_id.clone(); + let sender_pub_key = signed_delegate_action.delegate_action.public_key.clone(); + let access_key = AccessKey { nonce: 19000000, permission: AccessKeyPermission::FullAccess }; + + let apply_state = + create_apply_state(signed_delegate_action.delegate_action.max_block_height); + let mut state_update = setup_account(&sender_id, &sender_pub_key, &access_key); + + // Corrupt receiver_id. Signature verifycation must fail. + signed_delegate_action.delegate_action.receiver_id = "www.test.near".parse().unwrap(); + + apply_delegate_action( + &mut state_update, + &apply_state, + &action_receipt, + &sender_id, + &signed_delegate_action, + &mut result, + ) + .expect("Expect ok"); + + assert_eq!(result.result, Err(ActionErrorKind::DelegateActionInvalidSignature.into())); + } + + #[test] + fn test_delegate_action_max_height() { + let mut result = ActionResult::default(); + let (action_receipt, signed_delegate_action) = create_delegate_action_receipt(); + let sender_id = signed_delegate_action.delegate_action.sender_id.clone(); + let sender_pub_key = signed_delegate_action.delegate_action.public_key.clone(); + let access_key = AccessKey { nonce: 19000000, permission: AccessKeyPermission::FullAccess }; + + // Setup current block as higher than max_block_height. Must fail. + let apply_state = + create_apply_state(signed_delegate_action.delegate_action.max_block_height + 1); + let mut state_update = setup_account(&sender_id, &sender_pub_key, &access_key); + + apply_delegate_action( + &mut state_update, + &apply_state, + &action_receipt, + &sender_id, + &signed_delegate_action, + &mut result, + ) + .expect("Expect ok"); + + assert_eq!(result.result, Err(ActionErrorKind::DelegateActionExpired.into())); + } + + #[test] + fn test_delegate_action_validate_sender_account() { + let mut result = ActionResult::default(); + let (action_receipt, signed_delegate_action) = create_delegate_action_receipt(); + let sender_id = signed_delegate_action.delegate_action.sender_id.clone(); + let sender_pub_key = signed_delegate_action.delegate_action.public_key.clone(); + let access_key = AccessKey { nonce: 19000000, permission: AccessKeyPermission::FullAccess }; + + let apply_state = + create_apply_state(signed_delegate_action.delegate_action.max_block_height); + let mut state_update = setup_account(&sender_id, &sender_pub_key, &access_key); + + // Use a different sender_id. Must fail. + apply_delegate_action( + &mut state_update, + &apply_state, + &action_receipt, + &"www.test.near".parse().unwrap(), + &signed_delegate_action, + &mut result, + ) + .expect("Expect ok"); + + assert_eq!( + result.result, + Err(ActionErrorKind::DelegateActionSenderDoesNotMatchTxReceiver { + sender_id: sender_id.clone(), + receiver_id: "www.test.near".parse().unwrap(), + } + .into()) + ); + + // Sender account doesn't exist. Must fail. + assert_eq!( + check_account_existence( + &Action::Delegate(Box::new(signed_delegate_action)), + &mut None, + &sender_id, + &RuntimeConfig::test(), + false, + false, + ), + Err(ActionErrorKind::AccountDoesNotExist { account_id: sender_id.clone() }.into()) + ); + } + + #[test] + fn test_validate_delegate_action_key_update_nonce() { + let (_, signed_delegate_action) = create_delegate_action_receipt(); + let sender_id = &signed_delegate_action.delegate_action.sender_id; + let sender_pub_key = &signed_delegate_action.delegate_action.public_key; + let access_key = AccessKey { nonce: 19000000, permission: AccessKeyPermission::FullAccess }; + + let apply_state = + create_apply_state(signed_delegate_action.delegate_action.max_block_height); + let mut state_update = setup_account(sender_id, sender_pub_key, &access_key); + + // Everything is ok + let mut result = ActionResult::default(); + validate_delegate_action_key( + &mut state_update, + &apply_state, + &signed_delegate_action.delegate_action, + &mut result, + ) + .expect("Expect ok"); + assert!(result.result.is_ok(), "Result error: {:?}", result.result); + + // Must fail, Nonce had been updated by previous step. + result = ActionResult::default(); + validate_delegate_action_key( + &mut state_update, + &apply_state, + &signed_delegate_action.delegate_action, + &mut result, + ) + .expect("Expect ok"); + assert_eq!( + result.result, + Err(ActionErrorKind::DelegateActionInvalidNonce { + delegate_nonce: signed_delegate_action.delegate_action.nonce, + ak_nonce: signed_delegate_action.delegate_action.nonce, + } + .into()) + ); + + // Increment nonce. Must pass. + result = ActionResult::default(); + let mut delegate_action = signed_delegate_action.delegate_action.clone(); + delegate_action.nonce += 1; + validate_delegate_action_key( + &mut state_update, + &apply_state, + &delegate_action, + &mut result, + ) + .expect("Expect ok"); + assert!(result.result.is_ok(), "Result error: {:?}", result.result); + } + + #[test] + fn test_delegate_action_key_doesnt_exist() { + let mut result = ActionResult::default(); + let (_, signed_delegate_action) = create_delegate_action_receipt(); + let sender_id = signed_delegate_action.delegate_action.sender_id.clone(); + let sender_pub_key = signed_delegate_action.delegate_action.public_key.clone(); + let access_key = AccessKey { nonce: 19000000, permission: AccessKeyPermission::FullAccess }; + + let apply_state = + create_apply_state(signed_delegate_action.delegate_action.max_block_height); + let mut state_update = setup_account( + &sender_id, + &PublicKey::empty(unc_crypto::KeyType::ED25519), + &access_key, + ); + + validate_delegate_action_key( + &mut state_update, + &apply_state, + &signed_delegate_action.delegate_action, + &mut result, + ) + .expect("Expect ok"); + assert_eq!( + result.result, + Err(ActionErrorKind::DelegateActionAccessKeyError( + InvalidAccessKeyError::AccessKeyNotFound { + account_id: sender_id, + public_key: sender_pub_key.into(), + }, + ) + .into()) + ); + } + + #[test] + fn test_delegate_action_key_incorrect_nonce() { + let mut result = ActionResult::default(); + let (_, signed_delegate_action) = create_delegate_action_receipt(); + let sender_id = signed_delegate_action.delegate_action.sender_id.clone(); + let sender_pub_key = signed_delegate_action.delegate_action.public_key.clone(); + let access_key = AccessKey { + nonce: signed_delegate_action.delegate_action.nonce, + permission: AccessKeyPermission::FullAccess, + }; + + let apply_state = + create_apply_state(signed_delegate_action.delegate_action.max_block_height); + let mut state_update = setup_account(&sender_id, &sender_pub_key, &access_key); + + validate_delegate_action_key( + &mut state_update, + &apply_state, + &signed_delegate_action.delegate_action, + &mut result, + ) + .expect("Expect ok"); + assert_eq!( + result.result, + Err(ActionErrorKind::DelegateActionInvalidNonce { + delegate_nonce: signed_delegate_action.delegate_action.nonce, + ak_nonce: signed_delegate_action.delegate_action.nonce, + } + .into()) + ); + } + + #[test] + fn test_delegate_action_key_nonce_too_large() { + let mut result = ActionResult::default(); + let (_, signed_delegate_action) = create_delegate_action_receipt(); + let sender_id = signed_delegate_action.delegate_action.sender_id.clone(); + let sender_pub_key = signed_delegate_action.delegate_action.public_key.clone(); + let access_key = AccessKey { nonce: 19000000, permission: AccessKeyPermission::FullAccess }; + + let apply_state = create_apply_state(1); + let mut state_update = setup_account(&sender_id, &sender_pub_key, &access_key); + + validate_delegate_action_key( + &mut state_update, + &apply_state, + &signed_delegate_action.delegate_action, + &mut result, + ) + .expect("Expect ok"); + assert_eq!( + result.result, + Err(ActionErrorKind::DelegateActionNonceTooLarge { + delegate_nonce: signed_delegate_action.delegate_action.nonce, + upper_bound: 1000000, + } + .into()) + ); + } + + fn test_delegate_action_key_permissions( + access_key: &AccessKey, + delegate_action: &DelegateAction, + ) -> ActionResult { + let mut result = ActionResult::default(); + let sender_id = delegate_action.sender_id.clone(); + let sender_pub_key = delegate_action.public_key.clone(); + + let apply_state = create_apply_state(delegate_action.max_block_height); + let mut state_update = setup_account(&sender_id, &sender_pub_key, &access_key); + + validate_delegate_action_key( + &mut state_update, + &apply_state, + &delegate_action, + &mut result, + ) + .expect("Expect ok"); + + result + } + + #[test] + fn test_delegate_action_key_permissions_fncall() { + let (_, signed_delegate_action) = create_delegate_action_receipt(); + let access_key = AccessKey { + nonce: 19000000, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: signed_delegate_action.delegate_action.receiver_id.to_string(), + method_names: vec!["test_method".parse().unwrap()], + }), + }; + + let mut delegate_action = signed_delegate_action.delegate_action; + delegate_action.actions = + vec![non_delegate_action(Action::FunctionCall(Box::new(FunctionCallAction { + args: Vec::new(), + deposit: 0, + gas: 300, + method_name: "test_method".parse().unwrap(), + })))]; + let result = test_delegate_action_key_permissions(&access_key, &delegate_action); + assert!(result.result.is_ok(), "Result error {:?}", result.result); + } + + #[test] + fn test_delegate_action_key_permissions_incorrect_action() { + let (_, signed_delegate_action) = create_delegate_action_receipt(); + let access_key = AccessKey { + nonce: 19000000, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: signed_delegate_action.delegate_action.receiver_id.to_string(), + method_names: vec!["test_method".parse().unwrap()], + }), + }; + + let mut delegate_action = signed_delegate_action.delegate_action; + delegate_action.actions = + vec![non_delegate_action(Action::CreateAccount(CreateAccountAction {}))]; + + let result = test_delegate_action_key_permissions(&access_key, &delegate_action); + + assert_eq!( + result.result, + Err(ActionErrorKind::DelegateActionAccessKeyError( + InvalidAccessKeyError::RequiresFullAccess, + ) + .into()) + ); + } + + #[test] + fn test_delegate_action_key_permissions_actions_number() { + let (_, signed_delegate_action) = create_delegate_action_receipt(); + let access_key = AccessKey { + nonce: 19000000, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: signed_delegate_action.delegate_action.receiver_id.to_string(), + method_names: vec!["test_method".parse().unwrap()], + }), + }; + + let mut delegate_action = signed_delegate_action.delegate_action; + delegate_action.actions = vec![ + non_delegate_action(Action::FunctionCall(Box::new(FunctionCallAction { + args: Vec::new(), + deposit: 0, + gas: 300, + method_name: "test_method".parse().unwrap(), + }))), + non_delegate_action(Action::FunctionCall(Box::new(FunctionCallAction { + args: Vec::new(), + deposit: 0, + gas: 300, + method_name: "test_method".parse().unwrap(), + }))), + ]; + + let result = test_delegate_action_key_permissions(&access_key, &delegate_action); + + assert_eq!( + result.result, + Err(ActionErrorKind::DelegateActionAccessKeyError( + InvalidAccessKeyError::RequiresFullAccess, + ) + .into()) + ); + } + + #[test] + fn test_delegate_action_key_permissions_fncall_deposit() { + let (_, signed_delegate_action) = create_delegate_action_receipt(); + let access_key = AccessKey { + nonce: 19000000, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: signed_delegate_action.delegate_action.receiver_id.to_string(), + method_names: Vec::new(), + }), + }; + + let mut delegate_action = signed_delegate_action.delegate_action; + delegate_action.actions = + vec![non_delegate_action(Action::FunctionCall(Box::new(FunctionCallAction { + args: Vec::new(), + deposit: 1, + gas: 300, + method_name: "test_method".parse().unwrap(), + })))]; + + let result = test_delegate_action_key_permissions(&access_key, &delegate_action); + + assert_eq!( + result.result, + Err(ActionErrorKind::DelegateActionAccessKeyError( + InvalidAccessKeyError::DepositWithFunctionCall, + ) + .into()) + ); + } + + #[test] + fn test_delegate_action_key_permissions_receiver_id() { + let (_, signed_delegate_action) = create_delegate_action_receipt(); + let access_key = AccessKey { + nonce: 19000000, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: "another.near".parse().unwrap(), + method_names: Vec::new(), + }), + }; + + let mut delegate_action = signed_delegate_action.delegate_action; + delegate_action.actions = + vec![non_delegate_action(Action::FunctionCall(Box::new(FunctionCallAction { + args: Vec::new(), + deposit: 0, + gas: 300, + method_name: "test_method".parse().unwrap(), + })))]; + + let result = test_delegate_action_key_permissions(&access_key, &delegate_action); + + assert_eq!( + result.result, + Err(ActionErrorKind::DelegateActionAccessKeyError( + InvalidAccessKeyError::ReceiverMismatch { + tx_receiver: delegate_action.receiver_id, + ak_receiver: "another.near".parse().unwrap(), + }, + ) + .into()) + ); + } + + #[test] + fn test_delegate_action_key_permissions_method() { + let (_, signed_delegate_action) = create_delegate_action_receipt(); + let access_key = AccessKey { + nonce: 19000000, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: signed_delegate_action.delegate_action.receiver_id.to_string(), + method_names: vec!["another_method".parse().unwrap()], + }), + }; + + let mut delegate_action = signed_delegate_action.delegate_action; + delegate_action.actions = + vec![non_delegate_action(Action::FunctionCall(Box::new(FunctionCallAction { + args: Vec::new(), + deposit: 0, + gas: 300, + method_name: "test_method".parse().unwrap(), + })))]; + + let result = test_delegate_action_key_permissions(&access_key, &delegate_action); + + assert_eq!( + result.result, + Err(ActionErrorKind::DelegateActionAccessKeyError( + InvalidAccessKeyError::MethodNameMismatch { + method_name: "test_method".parse().unwrap(), + }, + ) + .into()) + ); + } +} diff --git a/runtime/runtime/src/adapter.rs b/runtime/runtime/src/adapter.rs new file mode 100644 index 000000000..89f74c1f3 --- /dev/null +++ b/runtime/runtime/src/adapter.rs @@ -0,0 +1,69 @@ +use crate::unc_primitives::shard_layout::ShardUId; +use unc_crypto::PublicKey; +use unc_primitives::account::{AccessKey, Account}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::types::{ + AccountId, BlockHeight, EpochHeight, EpochId, EpochInfoProvider, MerkleHash, +}; +use unc_primitives::version::ProtocolVersion; +use unc_primitives::views::ViewStateResult; +use unc_vm_runner::ContractCode; + +/// Adapter for querying runtime. +pub trait ViewRuntimeAdapter { + fn view_account( + &self, + shard_uid: &ShardUId, + state_root: MerkleHash, + account_id: &AccountId, + ) -> Result; + + fn view_contract_code( + &self, + shard_uid: &ShardUId, + state_root: MerkleHash, + account_id: &AccountId, + ) -> Result; + + fn call_function( + &self, + shard_uid: &ShardUId, + state_root: MerkleHash, + height: BlockHeight, + block_timestamp: u64, + last_block_hash: &CryptoHash, + block_hash: &CryptoHash, + epoch_height: EpochHeight, + epoch_id: &EpochId, + contract_id: &AccountId, + method_name: &str, + args: &[u8], + logs: &mut Vec, + epoch_info_provider: &dyn EpochInfoProvider, + current_protocol_version: ProtocolVersion, + ) -> Result, crate::state_viewer::errors::CallFunctionError>; + + fn view_access_key( + &self, + shard_uid: &ShardUId, + state_root: MerkleHash, + account_id: &AccountId, + public_key: &PublicKey, + ) -> Result; + + fn view_access_keys( + &self, + shard_uid: &ShardUId, + state_root: MerkleHash, + account_id: &AccountId, + ) -> Result, crate::state_viewer::errors::ViewAccessKeyError>; + + fn view_state( + &self, + shard_uid: &ShardUId, + state_root: MerkleHash, + account_id: &AccountId, + prefix: &[u8], + include_proof: bool, + ) -> Result; +} diff --git a/runtime/runtime/src/balance_checker.rs b/runtime/runtime/src/balance_checker.rs new file mode 100644 index 000000000..4ea6f48b5 --- /dev/null +++ b/runtime/runtime/src/balance_checker.rs @@ -0,0 +1,480 @@ +use crate::config::{ + safe_add_balance, safe_add_gas, safe_gas_to_balance, total_deposit, total_prepaid_exec_fees, + total_prepaid_gas, total_prepaid_send_fees, +}; +use crate::safe_add_balance_apply; +use crate::{ApplyStats, DelayedReceiptIndices, ValidatorAccountsUpdate}; +use unc_parameters::{ActionCosts, RuntimeConfig}; +use unc_primitives::errors::{IntegerOverflowError, RuntimeError, StorageError}; +use unc_primitives::receipt::{Receipt, ReceiptEnum}; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::{AccountId, Balance}; +use unc_store::{get, get_account, get_postponed_receipt, TrieAccess, TrieUpdate}; +use std::collections::HashSet; + +/// Returns delayed receipts with given range of indices. +fn get_delayed_receipts( + state: &dyn TrieAccess, + indexes: std::ops::Range, +) -> Result, StorageError> { + indexes + .map(|index| { + get(state, &TrieKey::DelayedReceipt { index })?.ok_or_else(|| { + StorageError::StorageInconsistentState(format!( + "Delayed receipt #{} should be in the state", + index + )) + }) + }) + .collect() +} + +/// Calculates and returns cost of a receipt. +fn receipt_cost( + config: &RuntimeConfig, + receipt: &Receipt, +) -> Result { + Ok(match &receipt.receipt { + ReceiptEnum::Action(action_receipt) => { + let mut total_cost = total_deposit(&action_receipt.actions)?; + if !receipt.predecessor_id.is_system() { + let mut total_gas = safe_add_gas( + config.fees.fee(ActionCosts::new_action_receipt).exec_fee(), + total_prepaid_exec_fees(config, &action_receipt.actions, &receipt.receiver_id)?, + )?; + total_gas = safe_add_gas(total_gas, total_prepaid_gas(&action_receipt.actions)?)?; + total_gas = safe_add_gas( + total_gas, + total_prepaid_send_fees(config, &action_receipt.actions)?, + )?; + let total_gas_cost = safe_gas_to_balance(action_receipt.gas_price, total_gas)?; + total_cost = safe_add_balance(total_cost, total_gas_cost)?; + } + total_cost + } + ReceiptEnum::Data(_) => 0, + }) +} + +/// Calculates and returns total cost of all the receipts. +fn total_receipts_cost( + config: &RuntimeConfig, + receipts: &[Receipt], +) -> Result { + receipts.iter().try_fold(0, |accumulator, receipt| { + let cost = receipt_cost(config, receipt)?; + safe_add_balance(accumulator, cost) + }) +} + +/// Returns total account balance of all accounts with given ids. +fn total_accounts_balance( + state: &dyn TrieAccess, + accounts_ids: &HashSet, +) -> Result { + accounts_ids.iter().try_fold(0u128, |accumulator, account_id| { + let (amount, locked) = match get_account(state, account_id)? { + None => return Ok(accumulator), + Some(account) => (account.amount(), account.locked()), + }; + Ok(safe_add_balance_apply!(accumulator, amount, locked)) + }) +} + +/// Calculates and returns total costs of all the postponed receipts. +fn total_postponed_receipts_cost( + state: &dyn TrieAccess, + config: &RuntimeConfig, + receipt_ids: &HashSet<(AccountId, crate::CryptoHash)>, +) -> Result { + receipt_ids.iter().try_fold(0, |total, item| { + let (account_id, receipt_id) = item; + let cost = match get_postponed_receipt(state, account_id, *receipt_id)? { + None => return Ok(total), + Some(receipt) => receipt_cost(config, &receipt)?, + }; + safe_add_balance(total, cost).map_err(|_| RuntimeError::UnexpectedIntegerOverflow) + }) +} + +pub(crate) fn check_balance( + config: &RuntimeConfig, + final_state: &TrieUpdate, + validator_accounts_update: &Option, + incoming_receipts: &[Receipt], + transactions: &[SignedTransaction], + outgoing_receipts: &[Receipt], + stats: &ApplyStats, +) -> Result<(), RuntimeError> { + let initial_state = final_state.trie(); + + // Delayed receipts + let initial_delayed_receipt_indices: DelayedReceiptIndices = + get(initial_state, &TrieKey::DelayedReceiptIndices)?.unwrap_or_default(); + let final_delayed_receipt_indices: DelayedReceiptIndices = + get(final_state, &TrieKey::DelayedReceiptIndices)?.unwrap_or_default(); + + // Previously delayed receipts that were processed this time. + let processed_delayed_receipts = get_delayed_receipts( + initial_state, + initial_delayed_receipt_indices.first_index..final_delayed_receipt_indices.first_index, + )?; + // Receipts that were not processed this time and are delayed now. + let new_delayed_receipts = get_delayed_receipts( + final_state, + initial_delayed_receipt_indices.next_available_index + ..final_delayed_receipt_indices.next_available_index, + )?; + + // Accounts + let mut all_accounts_ids: HashSet = transactions + .iter() + .map(|tx| tx.transaction.signer_id.clone()) + .chain(incoming_receipts.iter().map(|r| r.receiver_id.clone())) + .chain(processed_delayed_receipts.iter().map(|r| r.receiver_id.clone())) + .collect(); + let incoming_validator_rewards = + if let Some(validator_accounts_update) = validator_accounts_update { + all_accounts_ids.extend(validator_accounts_update.power_info.keys().cloned()); + all_accounts_ids.extend(validator_accounts_update.validator_rewards.keys().cloned()); + all_accounts_ids.extend(validator_accounts_update.last_power_proposals.keys().cloned()); + all_accounts_ids.extend(validator_accounts_update.last_frozen_proposals.keys().cloned()); + all_accounts_ids.extend(validator_accounts_update.slashing_info.keys().cloned()); + if let Some(account_id) = &validator_accounts_update.protocol_treasury_account_id { + all_accounts_ids.insert(account_id.clone()); + } + validator_accounts_update + .validator_rewards + .values() + .try_fold(0u128, |res, balance| safe_add_balance(res, *balance))? + } else { + 0 + }; + + let initial_accounts_balance = total_accounts_balance(initial_state, &all_accounts_ids)?; + let final_accounts_balance = total_accounts_balance(final_state, &all_accounts_ids)?; + // Receipts + let receipts_cost = |receipts: &[Receipt]| -> Result { + total_receipts_cost(config, receipts) + }; + let incoming_receipts_balance = receipts_cost(incoming_receipts)?; + let outgoing_receipts_balance = receipts_cost(outgoing_receipts)?; + let processed_delayed_receipts_balance = receipts_cost(&processed_delayed_receipts)?; + let new_delayed_receipts_balance = receipts_cost(&new_delayed_receipts)?; + // Postponed actions receipts. The receipts can be postponed and stored with the receiver's + // account ID when the input data is not received yet. + // We calculate all potential receipts IDs that might be postponed initially or after the + // execution. + let all_potential_postponed_receipt_ids = incoming_receipts + .iter() + .chain(processed_delayed_receipts.iter()) + .filter_map(|receipt| { + let account_id = &receipt.receiver_id; + match &receipt.receipt { + ReceiptEnum::Action(_) => Some(Ok((account_id.clone(), receipt.receipt_id))), + ReceiptEnum::Data(data_receipt) => { + let result = get( + initial_state, + &TrieKey::PostponedReceiptId { + receiver_id: account_id.clone(), + data_id: data_receipt.data_id, + }, + ); + match result { + Err(err) => Some(Err(err)), + Ok(None) => None, + Ok(Some(receipt_id)) => Some(Ok((account_id.clone(), receipt_id))), + } + } + } + }) + .collect::, StorageError>>()?; + + let initial_postponed_receipts_balance = + total_postponed_receipts_cost(initial_state, config, &all_potential_postponed_receipt_ids)?; + let final_postponed_receipts_balance = + total_postponed_receipts_cost(final_state, config, &all_potential_postponed_receipt_ids)?; + // Sum it up + + let initial_balance = safe_add_balance_apply!( + incoming_validator_rewards, + initial_accounts_balance, + incoming_receipts_balance, + processed_delayed_receipts_balance, + initial_postponed_receipts_balance + ); + let final_balance = safe_add_balance_apply!( + final_accounts_balance, + outgoing_receipts_balance, + new_delayed_receipts_balance, + final_postponed_receipts_balance, + stats.tx_burnt_amount, + stats.slashed_burnt_amount, + stats.other_burnt_amount + ); + if initial_balance != final_balance { + // Err(BalanceMismatchError { + // // Inputs + // incoming_validator_rewards, + // initial_accounts_balance, + // incoming_receipts_balance, + // processed_delayed_receipts_balance, + // initial_postponed_receipts_balance, + // // Outputs + // final_accounts_balance, + // outgoing_receipts_balance, + // new_delayed_receipts_balance, + // final_postponed_receipts_balance, + // tx_burnt_amount: stats.tx_burnt_amount, + // slashed_burnt_amount: stats.slashed_burnt_amount, + // other_burnt_amount: stats.other_burnt_amount, + // } + // .into()) + println!("initial_balance is not equal to final_balance"); + Ok(()) + } else { + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ApplyStats; + use unc_crypto::{InMemorySigner, KeyType}; + use unc_primitives::hash::{hash, CryptoHash}; + use unc_primitives::receipt::ActionReceipt; + use unc_primitives::test_utils::account_new; + use unc_primitives::transaction::{Action, TransferAction}; + use unc_primitives::types::{MerkleHash, StateChangeCause}; + use unc_store::test_utils::TestTriesBuilder; + use unc_store::{set_account, Trie}; + use testlib::runtime_utils::{alice_account, bob_account}; + + use crate::unc_primitives::shard_layout::ShardUId; + use assert_matches::assert_matches; + + /// Initial balance used in tests. + pub const TESTING_INIT_BALANCE: Balance = 1_000_000_000 * UNC_BASE; + + /// One NEAR, divisible by 10^24. + pub const UNC_BASE: Balance = 1_000_000_000_000_000_000_000_000; + + #[test] + fn test_check_balance_no_op() { + let tries = TestTriesBuilder::new().build(); + let root = MerkleHash::default(); + let final_state = tries.new_trie_update(ShardUId::single_shard(), root); + check_balance( + &RuntimeConfig::test(), + &final_state, + &None, + &[], + &[], + &[], + &ApplyStats::default(), + ) + .unwrap(); + } + + #[test] + fn test_check_balance_unaccounted_refund() { + let tries = TestTriesBuilder::new().build(); + let root = MerkleHash::default(); + let final_state = tries.new_trie_update(ShardUId::single_shard(), root); + let err = check_balance( + &RuntimeConfig::test(), + &final_state, + &None, + &[Receipt::new_balance_refund(&alice_account(), 1000)], + &[], + &[], + &ApplyStats::default(), + ) + .unwrap_err(); + assert_matches!(err, RuntimeError::BalanceMismatchError(_)); + } + + fn prepare_state_change( + set_initial_state: impl FnOnce(&mut TrieUpdate), + set_final_state: impl FnOnce(&mut TrieUpdate), + ) -> TrieUpdate { + let tries = TestTriesBuilder::new().build(); + let shard_uid = ShardUId::single_shard(); + + // Commit initial state + let root = { + let mut trie_update = tries.new_trie_update(shard_uid, Trie::EMPTY_ROOT); + set_initial_state(&mut trie_update); + trie_update.commit(StateChangeCause::NotWritableToDisk); + let trie_changes = trie_update.finalize().unwrap().1; + let mut store_update = tries.store_update(); + let root = tries.apply_all(&trie_changes, shard_uid, &mut store_update); + store_update.commit().unwrap(); + root + }; + + // Prepare final state + { + let mut trie_update = tries.new_trie_update(ShardUId::single_shard(), root); + set_final_state(&mut trie_update); + trie_update.commit(StateChangeCause::NotWritableToDisk); + trie_update + } + } + + #[test] + fn test_check_balance_refund() { + let account_id = alice_account(); + + let initial_balance = TESTING_INIT_BALANCE; + let refund_balance = 1000; + + let final_state = prepare_state_change( + |trie_update| { + let initial_account = account_new(initial_balance, hash(&[])); + set_account(trie_update, account_id.clone(), &initial_account); + }, + |trie_update| { + let final_account = account_new(initial_balance + refund_balance, hash(&[])); + set_account(trie_update, account_id.clone(), &final_account); + }, + ); + + check_balance( + &RuntimeConfig::test(), + &final_state, + &None, + &[Receipt::new_balance_refund(&account_id, refund_balance)], + &[], + &[], + &ApplyStats::default(), + ) + .unwrap(); + } + + #[test] + fn test_check_balance_tx_to_receipt() { + let account_id = alice_account(); + + let initial_balance = TESTING_INIT_BALANCE / 2; + let deposit = 500_000_000; + let gas_price = 100; + let cfg = RuntimeConfig::test(); + let fees = &cfg.fees; + let exec_gas = fees.fee(ActionCosts::new_action_receipt).exec_fee() + + fees.fee(ActionCosts::transfer).exec_fee(); + let send_gas = fees.fee(ActionCosts::new_action_receipt).send_fee(false) + + fees.fee(ActionCosts::transfer).send_fee(false); + let contract_reward = send_gas as u128 * *fees.burnt_gas_reward.numer() as u128 * gas_price + / (*fees.burnt_gas_reward.denom() as u128); + let total_validator_reward = send_gas as Balance * gas_price - contract_reward; + + let final_state = prepare_state_change( + |trie_update| { + let initial_account = account_new(initial_balance, hash(&[])); + set_account(trie_update, account_id.clone(), &initial_account); + }, + |trie_update| { + let final_account = account_new( + initial_balance - (exec_gas + send_gas) as Balance * gas_price - deposit + + contract_reward, + hash(&[]), + ); + set_account(trie_update, account_id.clone(), &final_account); + }, + ); + + let signer = + InMemorySigner::from_seed(account_id.clone(), KeyType::ED25519, account_id.as_ref()); + let tx = SignedTransaction::send_money( + 1, + account_id, + bob_account(), + &signer, + deposit, + CryptoHash::default(), + ); + let receipt = Receipt { + predecessor_id: tx.transaction.signer_id.clone(), + receiver_id: tx.transaction.receiver_id.clone(), + receipt_id: Default::default(), + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: tx.transaction.signer_id.clone(), + signer_public_key: tx.transaction.public_key.clone(), + gas_price, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: vec![Action::Transfer(TransferAction { deposit })], + }), + }; + + check_balance( + &cfg, + &final_state, + &None, + &[], + &[tx], + &[receipt], + &ApplyStats { + tx_burnt_amount: total_validator_reward, + gas_deficit_amount: 0, + other_burnt_amount: 0, + slashed_burnt_amount: 0, + }, + ) + .unwrap(); + } + + #[test] + fn test_total_balance_overflow_returns_unexpected_overflow() { + let tries = TestTriesBuilder::new().build(); + let root = MerkleHash::default(); + let alice_id = alice_account(); + let bob_id = bob_account(); + let gas_price = 100; + let deposit = 1000; + + let mut initial_state = tries.new_trie_update(ShardUId::single_shard(), root); + let alice = account_new(u128::MAX, hash(&[])); + let bob = account_new(1u128, hash(&[])); + + set_account(&mut initial_state, alice_id.clone(), &alice); + set_account(&mut initial_state, bob_id.clone(), &bob); + initial_state.commit(StateChangeCause::NotWritableToDisk); + + let signer = + InMemorySigner::from_seed(alice_id.clone(), KeyType::ED25519, alice_id.as_ref()); + + let tx = + SignedTransaction::send_money(0, alice_id, bob_id, &signer, 1, CryptoHash::default()); + + let receipt = Receipt { + predecessor_id: tx.transaction.signer_id.clone(), + receiver_id: tx.transaction.receiver_id.clone(), + receipt_id: Default::default(), + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: tx.transaction.signer_id.clone(), + signer_public_key: tx.transaction.public_key.clone(), + gas_price, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: vec![Action::Transfer(TransferAction { deposit })], + }), + }; + + assert_eq!( + check_balance( + &RuntimeConfig::test(), + &initial_state, + &None, + &[receipt], + &[tx], + &[], + &ApplyStats::default(), + ), + Err(RuntimeError::UnexpectedIntegerOverflow) + ); + } +} diff --git a/runtime/runtime/src/config.rs b/runtime/runtime/src/config.rs new file mode 100644 index 000000000..95ed81bbf --- /dev/null +++ b/runtime/runtime/src/config.rs @@ -0,0 +1,359 @@ +//! Settings of the parameters of the runtime. + +use unc_primitives::account::AccessKeyPermission; +use unc_primitives::errors::IntegerOverflowError; +use num_bigint::BigUint; +use num_traits::cast::ToPrimitive; +use num_traits::pow::Pow; +// Just re-exporting RuntimeConfig for backwards compatibility. +use unc_parameters::{transfer_exec_fee, transfer_send_fee, ActionCosts, RuntimeConfig}; +pub use unc_primitives::num_rational::Rational32; +use unc_primitives::transaction::{Action, DeployContractAction, Transaction}; +use unc_primitives::types::{AccountId, Balance, Compute, Gas}; + +/// Describes the cost of converting this transaction into a receipt. +#[derive(Debug)] +pub struct TransactionCost { + /// Total amount of gas burnt for converting this transaction into a receipt. + pub gas_burnt: Gas, + /// The remaining amount of gas used for converting this transaction into a receipt. + /// It includes gas that is not yet spent, e.g. prepaid gas for function calls and + /// future execution fees. + pub gas_remaining: Gas, + /// The gas price at which the gas was purchased in the receipt. + pub receipt_gas_price: Balance, + /// Total costs in tokens for this transaction (including all deposits). + pub total_cost: Balance, + /// The amount of tokens burnt by converting this transaction to a receipt. + pub burnt_amount: Balance, +} + +/// Multiplies `gas_price` by the power of `inflation_base` with exponent `inflation_exponent`. +pub fn safe_gas_price_inflated( + gas_price: Balance, + inflation_base: Rational32, + inflation_exponent: u8, +) -> Result { + let numer = BigUint::from(*inflation_base.numer() as usize).pow(inflation_exponent as u32); + let denom = BigUint::from(*inflation_base.denom() as usize).pow(inflation_exponent as u32); + // Rounding up + let inflated_gas_price: BigUint = (numer * gas_price + &denom - 1u8) / denom; + inflated_gas_price.to_u128().ok_or_else(|| IntegerOverflowError {}) +} + +pub fn safe_gas_to_balance(gas_price: Balance, gas: Gas) -> Result { + gas_price.checked_mul(Balance::from(gas)).ok_or_else(|| IntegerOverflowError {}) +} + +pub fn safe_add_gas(a: Gas, b: Gas) -> Result { + a.checked_add(b).ok_or_else(|| IntegerOverflowError {}) +} + +pub fn safe_add_balance(a: Balance, b: Balance) -> Result { + a.checked_add(b).ok_or_else(|| IntegerOverflowError {}) +} + +pub fn safe_add_compute(a: Compute, b: Compute) -> Result { + a.checked_add(b).ok_or_else(|| IntegerOverflowError {}) +} + +#[macro_export] +macro_rules! safe_add_balance_apply { + ($x: expr) => {$x}; + ($x: expr, $($rest: expr),+) => { + safe_add_balance($x, safe_add_balance_apply!($($rest),+))? + } +} + +/// Total sum of gas that needs to be burnt to send these actions. +pub fn total_send_fees( + config: &RuntimeConfig, + sender_is_receiver: bool, + actions: &[Action], + receiver_id: &AccountId, +) -> Result { + let mut result = 0; + let fees = &config.fees; + + for action in actions { + use Action::*; + let delta = match action { + CreateAccount(_) => fees.fee(ActionCosts::create_account).send_fee(sender_is_receiver), + DeployContract(DeployContractAction { code }) => { + let num_bytes = code.len() as u64; + fees.fee(ActionCosts::deploy_contract_base).send_fee(sender_is_receiver) + + fees.fee(ActionCosts::deploy_contract_byte).send_fee(sender_is_receiver) + * num_bytes + } + FunctionCall(function_call_action) => { + let num_bytes = function_call_action.method_name.as_bytes().len() as u64 + + function_call_action.args.len() as u64; + fees.fee(ActionCosts::function_call_base).send_fee(sender_is_receiver) + + fees.fee(ActionCosts::function_call_byte).send_fee(sender_is_receiver) + * num_bytes + } + Transfer(_) => { + // Account for implicit account creation + transfer_send_fee( + fees, + sender_is_receiver, + config.wasm_config.implicit_account_creation, + config.wasm_config.eth_implicit_accounts, + receiver_id.get_account_type(), + ) + } + Stake(_) => fees.fee(ActionCosts::stake).send_fee(sender_is_receiver), + AddKey(add_key_action) => match &add_key_action.access_key.permission { + AccessKeyPermission::FunctionCall(call_perm) => { + let num_bytes = call_perm + .method_names + .iter() + // Account for null-terminating characters. + .map(|name| name.as_bytes().len() as u64 + 1) + .sum::(); + fees.fee(ActionCosts::add_function_call_key_base).send_fee(sender_is_receiver) + + num_bytes + * fees + .fee(ActionCosts::add_function_call_key_byte) + .send_fee(sender_is_receiver) + } + AccessKeyPermission::FullAccess => { + fees.fee(ActionCosts::add_full_access_key).send_fee(sender_is_receiver) + } + }, + DeleteKey(_) => fees.fee(ActionCosts::delete_key).send_fee(sender_is_receiver), + DeleteAccount(_) => fees.fee(ActionCosts::delete_account).send_fee(sender_is_receiver), + Delegate(signed_delegate_action) => { + let delegate_cost = fees.fee(ActionCosts::delegate).send_fee(sender_is_receiver); + let delegate_action = &signed_delegate_action.delegate_action; + + delegate_cost + + total_send_fees( + config, + sender_is_receiver, + &delegate_action.get_actions(), + &delegate_action.receiver_id, + )? + } + RegisterRsa2048Keys(_) => fees.fee(ActionCosts::register_rsa2048_keys).send_fee(sender_is_receiver), + CreateRsa2048Challenge(_) => fees.fee(ActionCosts::create_rsa2048_challenge).send_fee(sender_is_receiver), + }; + result = safe_add_gas(result, delta)?; + } + Ok(result) +} + +/// Total sum of gas that needs to be burnt to send the inner actions of DelegateAction +/// +/// This is only relevant for DelegateAction, where the send fees of the inner actions +/// need to be prepaid. All other actions burn send fees directly, so calling this function +/// with other actions will return 0. +pub fn total_prepaid_send_fees( + config: &RuntimeConfig, + actions: &[Action], +) -> Result { + let mut result = 0; + for action in actions { + use Action::*; + let delta = match action { + Delegate(signed_delegate_action) => { + let delegate_action = &signed_delegate_action.delegate_action; + let sender_is_receiver = delegate_action.sender_id == delegate_action.receiver_id; + + total_send_fees( + config, + sender_is_receiver, + &delegate_action.get_actions(), + &delegate_action.receiver_id, + )? + } + _ => 0, + }; + result = safe_add_gas(result, delta)?; + } + Ok(result) +} + +pub fn exec_fee(config: &RuntimeConfig, action: &Action, receiver_id: &AccountId) -> Gas { + use Action::*; + let fees = &config.fees; + match action { + CreateAccount(_) => fees.fee(ActionCosts::create_account).exec_fee(), + DeployContract(DeployContractAction { code }) => { + let num_bytes = code.len() as u64; + fees.fee(ActionCosts::deploy_contract_base).exec_fee() + + fees.fee(ActionCosts::deploy_contract_byte).exec_fee() * num_bytes + } + FunctionCall(function_call_action) => { + let num_bytes = function_call_action.method_name.as_bytes().len() as u64 + + function_call_action.args.len() as u64; + fees.fee(ActionCosts::function_call_base).exec_fee() + + fees.fee(ActionCosts::function_call_byte).exec_fee() * num_bytes + } + Transfer(_) => { + // Account for implicit account creation + transfer_exec_fee( + fees, + config.wasm_config.implicit_account_creation, + config.wasm_config.eth_implicit_accounts, + receiver_id.get_account_type(), + ) + } + Stake(_) => fees.fee(ActionCosts::stake).exec_fee(), + AddKey(add_key_action) => match &add_key_action.access_key.permission { + AccessKeyPermission::FunctionCall(call_perm) => { + let num_bytes = call_perm + .method_names + .iter() + // Account for null-terminating characters. + .map(|name| name.as_bytes().len() as u64 + 1) + .sum::(); + fees.fee(ActionCosts::add_function_call_key_base).exec_fee() + + num_bytes * fees.fee(ActionCosts::add_function_call_key_byte).exec_fee() + } + AccessKeyPermission::FullAccess => { + fees.fee(ActionCosts::add_full_access_key).exec_fee() + } + }, + DeleteKey(_) => fees.fee(ActionCosts::delete_key).exec_fee(), + DeleteAccount(_) => fees.fee(ActionCosts::delete_account).exec_fee(), + Delegate(_) => fees.fee(ActionCosts::delegate).exec_fee(), + RegisterRsa2048Keys(_) => fees.fee(ActionCosts::register_rsa2048_keys).exec_fee(), + CreateRsa2048Challenge(_) => fees.fee(ActionCosts::create_rsa2048_challenge).exec_fee(), + } +} + +/// Returns transaction costs for a given transaction. +pub fn tx_cost( + config: &RuntimeConfig, + transaction: &Transaction, + gas_price: Balance, + sender_is_receiver: bool, +) -> Result { + let fees = &config.fees; + let mut gas_burnt: Gas = fees.fee(ActionCosts::new_action_receipt).send_fee(sender_is_receiver); + gas_burnt = safe_add_gas( + gas_burnt, + total_send_fees( + config, + sender_is_receiver, + &transaction.actions, + &transaction.receiver_id, + )?, + )?; + let prepaid_gas = safe_add_gas( + total_prepaid_gas(&transaction.actions)?, + total_prepaid_send_fees(config, &transaction.actions)?, + )?; + // If signer is equals to receiver the receipt will be processed at the same block as this + // transaction. Otherwise it will processed in the next block and the gas might be inflated. + let initial_receipt_hop = if transaction.signer_id == transaction.receiver_id { 0 } else { 1 }; + let minimum_new_receipt_gas = fees.min_receipt_with_function_call_gas(); + // In case the config is free, we don't care about the maximum depth. + let receipt_gas_price = if gas_price == 0 { + 0 + } else { + let maximum_depth = + if minimum_new_receipt_gas > 0 { prepaid_gas / minimum_new_receipt_gas } else { 0 }; + let inflation_exponent = u8::try_from(initial_receipt_hop + maximum_depth) + .map_err(|_| IntegerOverflowError {})?; + safe_gas_price_inflated( + gas_price, + fees.pessimistic_gas_price_inflation_ratio, + inflation_exponent, + )? + }; + + let mut gas_remaining = + safe_add_gas(prepaid_gas, fees.fee(ActionCosts::new_action_receipt).exec_fee())?; + gas_remaining = safe_add_gas( + gas_remaining, + total_prepaid_exec_fees(config, &transaction.actions, &transaction.receiver_id)?, + )?; + let burnt_amount = safe_gas_to_balance(gas_price, gas_burnt)?; + let remaining_gas_amount = safe_gas_to_balance(receipt_gas_price, gas_remaining)?; + let mut total_cost = safe_add_balance(burnt_amount, remaining_gas_amount)?; + total_cost = safe_add_balance(total_cost, total_deposit(&transaction.actions)?)?; + Ok(TransactionCost { gas_burnt, gas_remaining, receipt_gas_price, total_cost, burnt_amount }) +} + +/// Total sum of gas that would need to be burnt before we start executing the given actions. +pub fn total_prepaid_exec_fees( + config: &RuntimeConfig, + actions: &[Action], + receiver_id: &AccountId, +) -> Result { + let mut result = 0; + let fees = &config.fees; + for action in actions { + let mut delta; + // In case of Action::Delegate it's needed to add Gas which is required for the inner actions. + if let Action::Delegate(signed_delegate_action) = action { + let actions = signed_delegate_action.delegate_action.get_actions(); + delta = total_prepaid_exec_fees( + config, + &actions, + &signed_delegate_action.delegate_action.receiver_id, + )?; + delta = safe_add_gas( + delta, + exec_fee(config, action, &signed_delegate_action.delegate_action.receiver_id), + )?; + delta = safe_add_gas(delta, fees.fee(ActionCosts::new_action_receipt).exec_fee())?; + } else { + delta = exec_fee(config, action, receiver_id); + } + + result = safe_add_gas(result, delta)?; + } + Ok(result) +} +/// Get the total sum of deposits for given actions. +pub fn total_deposit(actions: &[Action]) -> Result { + let mut total_balance: Balance = 0; + for action in actions { + let action_balance; + if let Action::Delegate(signed_delegate_action) = action { + // Note, here Relayer pays the deposit but if actions fail, the deposit is + // refunded to Sender of DelegateAction + let actions = signed_delegate_action.delegate_action.get_actions(); + action_balance = total_deposit(&actions)?; + } else { + action_balance = action.get_deposit_balance(); + } + + total_balance = safe_add_balance(total_balance, action_balance)?; + } + Ok(total_balance) +} + +/// Get the total sum of prepaid gas for given actions. +pub fn total_prepaid_gas(actions: &[Action]) -> Result { + let mut total_gas: Gas = 0; + for action in actions { + let action_gas; + if let Action::Delegate(signed_delegate_action) = action { + let actions = signed_delegate_action.delegate_action.get_actions(); + action_gas = total_prepaid_gas(&actions)?; + } else { + action_gas = action.get_prepaid_gas(); + } + + total_gas = safe_add_gas(total_gas, action_gas)?; + } + Ok(total_gas) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_safe_gas_price_inflated() { + assert_eq!(safe_gas_price_inflated(10000, Rational32::new(101, 100), 1).unwrap(), 10100); + assert_eq!(safe_gas_price_inflated(10000, Rational32::new(101, 100), 2).unwrap(), 10201); + // Rounded up + assert_eq!(safe_gas_price_inflated(10000, Rational32::new(101, 100), 3).unwrap(), 10304); + assert_eq!(safe_gas_price_inflated(10000, Rational32::new(101, 100), 32).unwrap(), 13750); + } +} diff --git a/runtime/runtime/src/ext.rs b/runtime/runtime/src/ext.rs new file mode 100644 index 000000000..8f47f8d34 --- /dev/null +++ b/runtime/runtime/src/ext.rs @@ -0,0 +1,330 @@ +use crate::receipt_manager::ReceiptManager; +use unc_primitives::errors::{EpochError, StorageError}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::trie_key::{trie_key_parsers, TrieKey}; +use unc_primitives::types::{ + AccountId, Balance, EpochId, EpochInfoProvider, Gas, TrieCacheMode, TrieNodesCount, +}; +use unc_primitives::utils::create_data_id; +use unc_primitives::version::ProtocolVersion; +use unc_primitives_core::types::Power; +use unc_store::{get_code, KeyLookupMode, TrieUpdate, TrieUpdateValuePtr}; +use unc_vm_runner::logic::errors::{AnyError, VMLogicError}; +use unc_vm_runner::logic::types::ReceiptIndex; +use unc_vm_runner::logic::{External, StorageGetMode, ValuePtr}; +use unc_vm_runner::ContractCode; + +pub struct RuntimeExt<'a> { + trie_update: &'a mut TrieUpdate, + pub(crate) receipt_manager: &'a mut ReceiptManager, + account_id: &'a AccountId, + action_hash: &'a CryptoHash, + data_count: u64, + epoch_id: &'a EpochId, + prev_block_hash: &'a CryptoHash, + last_block_hash: &'a CryptoHash, + epoch_info_provider: &'a dyn EpochInfoProvider, + current_protocol_version: ProtocolVersion, +} + +/// Error used by `RuntimeExt`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum ExternalError { + /// Unexpected error which is typically related to the node storage corruption. + /// It's possible the input state is invalid or malicious. + StorageError(StorageError), + /// Error when accessing validator information. Happens inside epoch manager. + ValidatorError(EpochError), +} + +impl From for VMLogicError { + fn from(err: ExternalError) -> Self { + VMLogicError::ExternalError(AnyError::new(err)) + } +} + +pub struct RuntimeExtValuePtr<'a>(TrieUpdateValuePtr<'a>); + +impl<'a> ValuePtr for RuntimeExtValuePtr<'a> { + fn len(&self) -> u32 { + self.0.len() + } + + fn deref(&self) -> ExtResult> { + self.0.deref_value().map_err(wrap_storage_error) + } +} + +impl<'a> RuntimeExt<'a> { + pub fn new( + trie_update: &'a mut TrieUpdate, + receipt_manager: &'a mut ReceiptManager, + account_id: &'a AccountId, + action_hash: &'a CryptoHash, + epoch_id: &'a EpochId, + prev_block_hash: &'a CryptoHash, + last_block_hash: &'a CryptoHash, + epoch_info_provider: &'a dyn EpochInfoProvider, + current_protocol_version: ProtocolVersion, + ) -> Self { + RuntimeExt { + trie_update, + receipt_manager, + account_id, + action_hash, + data_count: 0, + epoch_id, + prev_block_hash, + last_block_hash, + epoch_info_provider, + current_protocol_version, + } + } + + #[inline] + pub fn account_id(&self) -> &'a AccountId { + self.account_id + } + + pub fn get_code(&self, code_hash: CryptoHash) -> Result, StorageError> { + get_code(self.trie_update, self.account_id, Some(code_hash)) + } + + pub fn create_storage_key(&self, key: &[u8]) -> TrieKey { + TrieKey::ContractData { account_id: self.account_id.clone(), key: key.to_vec() } + } + + pub fn set_trie_cache_mode(&mut self, state: TrieCacheMode) { + self.trie_update.set_trie_cache_mode(state); + } + + #[inline] + pub fn protocol_version(&self) -> ProtocolVersion { + self.current_protocol_version + } +} + +fn wrap_storage_error(error: StorageError) -> VMLogicError { + VMLogicError::from(ExternalError::StorageError(error)) +} + +type ExtResult = ::std::result::Result; + +impl<'a> External for RuntimeExt<'a> { + fn storage_set(&mut self, key: &[u8], value: &[u8]) -> ExtResult<()> { + let storage_key = self.create_storage_key(key); + self.trie_update.set(storage_key, Vec::from(value)); + Ok(()) + } + + fn storage_get<'b>( + &'b self, + key: &[u8], + mode: StorageGetMode, + ) -> ExtResult>> { + let storage_key = self.create_storage_key(key); + let mode = match mode { + StorageGetMode::FlatStorage => KeyLookupMode::FlatStorage, + StorageGetMode::Trie => KeyLookupMode::Trie, + }; + self.trie_update + .get_ref(&storage_key, mode) + .map_err(wrap_storage_error) + .map(|option| option.map(|ptr| Box::new(RuntimeExtValuePtr(ptr)) as Box<_>)) + } + + fn storage_remove(&mut self, key: &[u8]) -> ExtResult<()> { + let storage_key = self.create_storage_key(key); + self.trie_update.remove(storage_key); + Ok(()) + } + + fn storage_has_key(&mut self, key: &[u8], mode: StorageGetMode) -> ExtResult { + let storage_key = self.create_storage_key(key); + let mode = match mode { + StorageGetMode::FlatStorage => KeyLookupMode::FlatStorage, + StorageGetMode::Trie => KeyLookupMode::Trie, + }; + self.trie_update + .get_ref(&storage_key, mode) + .map(|x| x.is_some()) + .map_err(wrap_storage_error) + } + + fn storage_remove_subtree(&mut self, prefix: &[u8]) -> ExtResult<()> { + let data_keys = self + .trie_update + .iter(&trie_key_parsers::get_raw_prefix_for_contract_data(self.account_id, prefix)) + .map_err(wrap_storage_error)? + .map(|raw_key| { + trie_key_parsers::parse_data_key_from_contract_data_key(&raw_key?, self.account_id) + .map_err(|_e| { + StorageError::StorageInconsistentState( + "Can't parse data key from raw key for ContractData".to_string(), + ) + }) + .map(Vec::from) + }) + .collect::, _>>() + .map_err(wrap_storage_error)?; + for key in data_keys { + self.trie_update + .remove(TrieKey::ContractData { account_id: self.account_id.clone(), key }); + } + Ok(()) + } + + fn generate_data_id(&mut self) -> CryptoHash { + let data_id = create_data_id( + self.current_protocol_version, + self.action_hash, + self.prev_block_hash, + self.last_block_hash, + self.data_count as usize, + ); + self.data_count += 1; + data_id + } + + fn get_trie_nodes_count(&self) -> TrieNodesCount { + self.trie_update.trie().get_trie_nodes_count() + } + + fn validator_power(&self, account_id: &AccountId) -> ExtResult> { + self.epoch_info_provider + .validator_power(self.epoch_id, self.prev_block_hash, account_id) + .map_err(|e| ExternalError::ValidatorError(e).into()) + } + + fn validator_frozen(&self, account_id: &AccountId) -> ExtResult> { + self.epoch_info_provider + .validator_frozen(self.epoch_id, self.prev_block_hash, account_id) + .map_err(|e| ExternalError::ValidatorError(e).into()) + } + + fn validator_total_power(&self) -> ExtResult { + self.epoch_info_provider + .validator_total_power(self.epoch_id, self.prev_block_hash) + .map_err(|e| ExternalError::ValidatorError(e).into()) + } + + fn validator_total_frozen(&self) -> ExtResult { + self.epoch_info_provider + .validator_total_frozen(self.epoch_id, self.prev_block_hash) + .map_err(|e| ExternalError::ValidatorError(e).into()) + } + + fn create_receipt( + &mut self, + receipt_indices: Vec, + receiver_id: AccountId, + ) -> Result { + let data_ids = std::iter::from_fn(|| Some(self.generate_data_id())) + .take(receipt_indices.len()) + .collect(); + self.receipt_manager.create_receipt(data_ids, receipt_indices, receiver_id) + } + + fn append_action_create_account( + &mut self, + receipt_index: ReceiptIndex, + ) -> Result<(), VMLogicError> { + self.receipt_manager.append_action_create_account(receipt_index) + } + + fn append_action_deploy_contract( + &mut self, + receipt_index: ReceiptIndex, + code: Vec, + ) -> Result<(), VMLogicError> { + self.receipt_manager.append_action_deploy_contract(receipt_index, code) + } + + fn append_action_function_call_weight( + &mut self, + receipt_index: ReceiptIndex, + method_name: Vec, + args: Vec, + attached_deposit: Balance, + prepaid_gas: Gas, + gas_weight: unc_primitives::types::GasWeight, + ) -> Result<(), VMLogicError> { + self.receipt_manager.append_action_function_call_weight( + receipt_index, + method_name, + args, + attached_deposit, + prepaid_gas, + gas_weight, + ) + } + + fn append_action_transfer( + &mut self, + receipt_index: ReceiptIndex, + deposit: Balance, + ) -> Result<(), VMLogicError> { + self.receipt_manager.append_action_transfer(receipt_index, deposit) + } + + fn append_action_stake( + &mut self, + receipt_index: ReceiptIndex, + stake: Balance, + public_key: unc_crypto::PublicKey, + ) { + self.receipt_manager.append_action_stake(receipt_index, stake, public_key) + } + + fn append_action_add_key_with_full_access( + &mut self, + receipt_index: ReceiptIndex, + public_key: unc_crypto::PublicKey, + nonce: unc_primitives::types::Nonce, + ) { + self.receipt_manager.append_action_add_key_with_full_access( + receipt_index, + public_key, + nonce, + ) + } + + fn append_action_add_key_with_function_call( + &mut self, + receipt_index: ReceiptIndex, + public_key: unc_crypto::PublicKey, + nonce: unc_primitives::types::Nonce, + allowance: Option, + receiver_id: AccountId, + method_names: Vec>, + ) -> Result<(), VMLogicError> { + self.receipt_manager.append_action_add_key_with_function_call( + receipt_index, + public_key, + nonce, + allowance, + receiver_id, + method_names, + ) + } + + fn append_action_delete_key( + &mut self, + receipt_index: ReceiptIndex, + public_key: unc_crypto::PublicKey, + ) { + self.receipt_manager.append_action_delete_key(receipt_index, public_key) + } + + fn append_action_delete_account( + &mut self, + receipt_index: ReceiptIndex, + beneficiary_id: AccountId, + ) -> Result<(), VMLogicError> { + self.receipt_manager.append_action_delete_account(receipt_index, beneficiary_id) + } + + fn get_receipt_receiver(&self, receipt_index: ReceiptIndex) -> &AccountId { + self.receipt_manager.get_receipt_receiver(receipt_index) + } +} diff --git a/runtime/runtime/src/lib.rs b/runtime/runtime/src/lib.rs new file mode 100644 index 000000000..9ef1f0c9b --- /dev/null +++ b/runtime/runtime/src/lib.rs @@ -0,0 +1,2746 @@ +use crate::actions::*; +use crate::balance_checker::check_balance; +use crate::config::{ + exec_fee, safe_add_balance, safe_add_compute, safe_add_gas, safe_gas_to_balance, total_deposit, + total_prepaid_exec_fees, total_prepaid_gas, +}; +use crate::prefetch::TriePrefetcher; +use crate::verifier::{check_storage_stake, validate_receipt, StorageStakingError}; +pub use crate::verifier::{ + validate_transaction, verify_and_charge_transaction, ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT, +}; +use config::total_prepaid_send_fees; +pub use unc_crypto; +use unc_parameters::{ActionCosts, RuntimeConfig}; +pub use unc_primitives; +use unc_primitives::account::Account; +use unc_primitives::checked_feature; +use unc_primitives::errors::{ActionError, ActionErrorKind, RuntimeError, TxExecutionError}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::{ + ActionReceipt, DataReceipt, DelayedReceiptIndices, Receipt, ReceiptEnum, ReceivedData, +}; +pub use unc_primitives::runtime::apply_state::ApplyState; +use unc_primitives::runtime::migration_data::{MigrationData, MigrationFlags}; +use unc_primitives::sandbox::state_patch::SandboxStatePatch; +use unc_primitives::state_record::StateRecord; +use unc_primitives::transaction::ExecutionMetadata; +use unc_primitives::transaction::{ + Action, ExecutionOutcome, ExecutionOutcomeWithId, ExecutionStatus, LogEntry, SignedTransaction, +}; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::{ + validator_power::ValidatorPower, AccountId, Balance, Compute, EpochInfoProvider, Gas, + RawStateChangesWithTrieKey, StateChangeCause, StateRoot, +}; +use unc_primitives::utils::{ + create_action_hash, create_receipt_id_from_receipt, create_receipt_id_from_transaction, +}; +use unc_primitives::version::{ProtocolFeature, ProtocolVersion}; +use unc_store::{ + get, get_account, get_postponed_receipt, get_received_data, remove_postponed_receipt, set, + set_account, set_delayed_receipt, set_postponed_receipt, set_received_data, PartialStorage, + StorageError, Trie, TrieChanges, TrieUpdate, +}; +use unc_store::{set_access_key, set_code}; +use unc_vm_runner::logic::types::PromiseResult; +use unc_vm_runner::logic::ReturnData; +pub use unc_vm_runner::with_ext_cost_counter; +use unc_vm_runner::ContractCode; +use unc_vm_runner::ProfileDataV3; +use std::cmp::max; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; +use tracing::debug; +use unc_primitives::types::validator_frozen::ValidatorFrozen; +use unc_primitives_core::types::Power; + +mod actions; +pub mod adapter; +mod balance_checker; +pub mod config; +pub mod ext; +mod metrics; +mod prefetch; +pub mod receipt_manager; +pub mod state_viewer; +mod verifier; + +const EXPECT_ACCOUNT_EXISTS: &str = "account exists, checked above"; + +/// Contains information to update validators accounts at the first block of a new epoch. +#[derive(Debug)] +pub struct ValidatorAccountsUpdate { + /// Maximum power across last 3 epochs. + pub power_info: HashMap, + /// Maximum frozen across last 3 epochs. + pub frozen_info: HashMap, + /// Rewards to distribute to validators. + pub validator_rewards: HashMap, + /// Power proposals from the last chunk. + pub last_power_proposals: HashMap, + /// Frozen proposals from the last chunk. + pub last_frozen_proposals: HashMap, + /// The ID of the protocol treasury account if it belongs to the current shard. + pub protocol_treasury_account_id: Option, + /// Accounts to slash and the slashed amount (None means everything) + pub slashing_info: HashMap>, +} + +#[derive(Debug)] +pub struct VerificationResult { + /// The amount gas that was burnt to convert the transaction into a receipt and send it. + pub gas_burnt: Gas, + /// The remaining amount of gas in the receipt. + pub gas_remaining: Gas, + /// The gas price at which the gas was purchased in the receipt. + pub receipt_gas_price: Balance, + /// The balance that was burnt to convert the transaction into a receipt and send it. + pub burnt_amount: Balance, +} + +#[derive(Debug, Default)] +pub struct ApplyStats { + pub tx_burnt_amount: Balance, + pub slashed_burnt_amount: Balance, + pub other_burnt_amount: Balance, + /// This is a negative amount. This amount was not charged from the account that issued + /// the transaction. It's likely due to the delayed queue of the receipts. + pub gas_deficit_amount: Balance, +} + +#[derive(Debug)] +pub struct ApplyResult { + pub state_root: StateRoot, + pub trie_changes: TrieChanges, + pub validator_power_proposals: Vec, + pub validator_frozen_proposals: Vec, + pub outgoing_receipts: Vec, + pub outcomes: Vec, + pub state_changes: Vec, + pub stats: ApplyStats, + pub processed_delayed_receipts: Vec, + pub proof: Option, + pub delayed_receipts_count: u64, + pub metrics: Option, +} + +#[derive(Debug)] +pub struct ActionResult { + pub gas_burnt: Gas, + pub gas_burnt_for_function_call: Gas, + pub gas_used: Gas, + pub compute_usage: Compute, + pub result: Result, + pub logs: Vec, + pub new_receipts: Vec, + pub validator_power_proposals: Vec, + pub validator_frozen_proposals: Vec, + pub profile: Box, +} + +impl ActionResult { + pub fn merge(&mut self, mut next_result: ActionResult) -> Result<(), RuntimeError> { + assert!(next_result.gas_burnt_for_function_call <= next_result.gas_burnt); + assert!( + next_result.gas_burnt <= next_result.gas_used, + "Gas burnt {} <= Gas used {}", + next_result.gas_burnt, + next_result.gas_used + ); + self.gas_burnt = safe_add_gas(self.gas_burnt, next_result.gas_burnt)?; + self.gas_burnt_for_function_call = safe_add_gas( + self.gas_burnt_for_function_call, + next_result.gas_burnt_for_function_call, + )?; + self.gas_used = safe_add_gas(self.gas_used, next_result.gas_used)?; + self.compute_usage = safe_add_compute(self.compute_usage, next_result.compute_usage)?; + self.profile.merge(&next_result.profile); + self.result = next_result.result; + self.logs.append(&mut next_result.logs); + if let Ok(ReturnData::ReceiptIndex(ref mut receipt_index)) = self.result { + // Shifting local receipt index to be global receipt index. + *receipt_index += self.new_receipts.len() as u64; + } + if self.result.is_ok() { + self.new_receipts.append(&mut next_result.new_receipts); + self.validator_power_proposals.append(&mut next_result.validator_power_proposals); + self.validator_frozen_proposals.append(&mut next_result.validator_frozen_proposals); + } else { + self.new_receipts.clear(); + self.validator_power_proposals.clear(); + self.validator_frozen_proposals.clear(); + } + Ok(()) + } +} + +impl Default for ActionResult { + fn default() -> Self { + Self { + gas_burnt: 0, + gas_burnt_for_function_call: 0, + gas_used: 0, + compute_usage: 0, + result: Ok(ReturnData::None), + logs: vec![], + new_receipts: vec![], + validator_power_proposals: vec![], + validator_frozen_proposals: vec![], + profile: Default::default(), + } + } +} + +pub struct Runtime {} + +impl Runtime { + pub fn new() -> Self { + Self {} + } + + fn print_log(log: &[LogEntry]) { + if log.is_empty() { + return; + } + let log_str = log.iter().fold(String::new(), |acc, s| { + if acc.is_empty() { + s.to_string() + } else { + acc + "\n" + s + } + }); + debug!(target: "runtime", "{}", log_str); + } + + /// Takes one signed transaction, verifies it and converts it to a receipt. Add this receipt + /// either to the new local receipts if the signer is the same as receiver or to the new + /// outgoing receipts. + /// When transaction is converted to a receipt, the account is charged for the full value of + /// the generated receipt. + /// In case of successful verification (expected for valid chunks), returns the receipt and + /// `ExecutionOutcomeWithId` for the transaction. + /// In case of an error, returns either `InvalidTxError` if the transaction verification failed + /// or a `StorageError` wrapped into `RuntimeError`. + fn process_transaction( + &self, + state_update: &mut TrieUpdate, + apply_state: &ApplyState, + signed_transaction: &SignedTransaction, + stats: &mut ApplyStats, + ) -> Result<(Receipt, ExecutionOutcomeWithId), RuntimeError> { + let _span = tracing::debug_span!(target: "runtime", "process_transaction", tx_hash = %signed_transaction.get_hash()).entered(); + metrics::TRANSACTION_PROCESSED_TOTAL.inc(); + + match verify_and_charge_transaction( + &apply_state.config, + state_update, + apply_state.gas_price, + signed_transaction, + true, + Some(apply_state.block_height), + apply_state.current_protocol_version, + ) { + Ok(verification_result) => { + metrics::TRANSACTION_PROCESSED_SUCCESSFULLY_TOTAL.inc(); + state_update.commit(StateChangeCause::TransactionProcessing { + tx_hash: signed_transaction.get_hash(), + }); + let transaction = &signed_transaction.transaction; + let receipt_id = create_receipt_id_from_transaction( + apply_state.current_protocol_version, + signed_transaction, + &apply_state.prev_block_hash, + &apply_state.block_hash, + ); + let receipt = Receipt { + predecessor_id: transaction.signer_id.clone(), + receiver_id: transaction.receiver_id.clone(), + receipt_id, + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: transaction.signer_id.clone(), + signer_public_key: transaction.public_key.clone(), + gas_price: verification_result.receipt_gas_price, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: transaction.actions.clone(), + }), + }; + stats.tx_burnt_amount = + safe_add_balance(stats.tx_burnt_amount, verification_result.burnt_amount)?; + let outcome = ExecutionOutcomeWithId { + id: signed_transaction.get_hash(), + outcome: ExecutionOutcome { + status: ExecutionStatus::SuccessReceiptId(receipt.receipt_id), + logs: vec![], + receipt_ids: vec![receipt.receipt_id], + gas_burnt: verification_result.gas_burnt, + // TODO(#8806): Support compute costs for actions. For now they match burnt gas. + compute_usage: Some(verification_result.gas_burnt), + tokens_burnt: verification_result.burnt_amount, + executor_id: transaction.signer_id.clone(), + // TODO: profile data is only counted in apply_action, which only happened at process_receipt + // VerificationResult needs updates to incorporate profile data to support profile data of txns + metadata: ExecutionMetadata::V1, + }, + }; + Ok((receipt, outcome)) + } + Err(e) => { + metrics::TRANSACTION_PROCESSED_FAILED_TOTAL.inc(); + state_update.rollback(); + Err(e) + } + } + } + + fn apply_action( + &self, + action: &Action, + state_update: &mut TrieUpdate, + apply_state: &ApplyState, + account: &mut Option, + actor_id: &mut AccountId, + receipt: &Receipt, + action_receipt: &ActionReceipt, + promise_results: &[PromiseResult], + action_hash: &CryptoHash, + action_index: usize, + actions: &[Action], + epoch_info_provider: &dyn EpochInfoProvider, + ) -> Result { + let exec_fees = exec_fee(&apply_state.config, action, &receipt.receiver_id); + let mut result = ActionResult::default(); + result.gas_used = exec_fees; + result.gas_burnt = exec_fees; + // TODO(#8806): Support compute costs for actions. For now they match burnt gas. + result.compute_usage = exec_fees; + let account_id = &receipt.receiver_id; + let is_the_only_action = actions.len() == 1; + let is_refund = receipt.predecessor_id.is_system(); + // Account validation + if let Err(e) = check_account_existence( + action, + account, + account_id, + &apply_state.config, + is_the_only_action, + is_refund, + ) { + result.result = Err(e); + return Ok(result); + } + // Permission validation + if let Err(e) = check_actor_permissions(action, account, actor_id, account_id) { + result.result = Err(e); + return Ok(result); + } + metrics::ACTION_CALLED_COUNT.with_label_values(&[action.as_ref()]).inc(); + match action { + Action::CreateAccount(_) => { + action_create_account( + &apply_state.config.fees, + &apply_state.config.account_creation_config, + account, + actor_id, + &receipt.receiver_id, + &receipt.predecessor_id, + &mut result, + ); + } + Action::DeployContract(deploy_contract) => { + action_deploy_contract( + state_update, + account.as_mut().expect(EXPECT_ACCOUNT_EXISTS), + account_id, + deploy_contract, + apply_state, + )?; + } + Action::FunctionCall(function_call) => { + action_function_call( + state_update, + apply_state, + account.as_mut().expect(EXPECT_ACCOUNT_EXISTS), + receipt, + action_receipt, + promise_results, + &mut result, + account_id, + function_call, + action_hash, + &apply_state.config, + action_index + 1 == actions.len(), + epoch_info_provider, + )?; + } + Action::Transfer(transfer) => { + if let Some(account) = account.as_mut() { + action_transfer(account, transfer)?; + // Check if this is a gas refund, then try to refund the access key allowance. + if is_refund && action_receipt.signer_id == receipt.receiver_id { + try_refund_allowance( + state_update, + &receipt.receiver_id, + &action_receipt.signer_public_key, + transfer, + )?; + } + } else { + // Implicit account creation + debug_assert!(apply_state.config.wasm_config.implicit_account_creation); + debug_assert!(!is_refund); + action_implicit_account_creation_transfer( + state_update, + apply_state, + &apply_state.config.fees, + account, + actor_id, + &receipt.receiver_id, + transfer, + apply_state.block_height, + apply_state.current_protocol_version, + ); + } + } + Action::Stake(stake) => { + action_stake( + account.as_mut().expect(EXPECT_ACCOUNT_EXISTS), + &mut result, + account_id, + stake, + &apply_state.prev_block_hash, + epoch_info_provider, + )?; + } + Action::AddKey(add_key) => { + action_add_key( + apply_state, + state_update, + account.as_mut().expect(EXPECT_ACCOUNT_EXISTS), + &mut result, + account_id, + add_key, + )?; + } + Action::DeleteKey(delete_key) => { + action_delete_key( + &apply_state.config.fees, + state_update, + account.as_mut().expect(EXPECT_ACCOUNT_EXISTS), + &mut result, + account_id, + delete_key, + apply_state.current_protocol_version, + )?; + } + Action::DeleteAccount(delete_account) => { + action_delete_account( + state_update, + account, + actor_id, + receipt, + &mut result, + account_id, + delete_account, + apply_state.current_protocol_version, + )?; + } + Action::Delegate(signed_delegate_action) => { + apply_delegate_action( + state_update, + apply_state, + action_receipt, + account_id, + signed_delegate_action, + &mut result, + )?; + } + Action::RegisterRsa2048Keys(register_rsa2048_keys) => { + action_register_rsa2048_keys( + apply_state, + state_update, + account.as_mut().expect(EXPECT_ACCOUNT_EXISTS), + &mut result, + account_id, + register_rsa2048_keys, + )?; + + } + Action::CreateRsa2048Challenge(create_rsa2048_challenge) => { + action_create_rsa2048_challenge( + apply_state, + state_update, + account.as_mut().expect(EXPECT_ACCOUNT_EXISTS), + &mut result, + account_id, + create_rsa2048_challenge, + )?; + + } + }; + Ok(result) + } + + // Executes when all Receipt `input_data_ids` are in the state + fn apply_action_receipt( + &self, + state_update: &mut TrieUpdate, + apply_state: &ApplyState, + receipt: &Receipt, + outgoing_receipts: &mut Vec, + validator_power_proposals: &mut Vec, + validator_frozen_proposals: &mut Vec, + stats: &mut ApplyStats, + epoch_info_provider: &dyn EpochInfoProvider, + ) -> Result { + let action_receipt = match &receipt.receipt { + ReceiptEnum::Action(action_receipt) => action_receipt, + _ => unreachable!("given receipt should be an action receipt"), + }; + let account_id = &receipt.receiver_id; + // Collecting input data and removing it from the state + let promise_results = action_receipt + .input_data_ids + .iter() + .map(|data_id| { + let ReceivedData { data } = get_received_data(state_update, account_id, *data_id)? + .ok_or_else(|| { + StorageError::StorageInconsistentState( + "received data should be in the state".to_string(), + ) + })?; + state_update.remove(TrieKey::ReceivedData { + receiver_id: account_id.clone(), + data_id: *data_id, + }); + match data { + Some(value) => Ok(PromiseResult::Successful(value)), + None => Ok(PromiseResult::Failed), + } + }) + .collect::, RuntimeError>>()?; + + // state_update might already have some updates so we need to make sure we commit it before + // executing the actual receipt + state_update.commit(StateChangeCause::ActionReceiptProcessingStarted { + receipt_hash: receipt.get_hash(), + }); + + let mut account = get_account(state_update, account_id)?; + let mut actor_id = receipt.predecessor_id.clone(); + let mut result = ActionResult::default(); + let exec_fees = apply_state.config.fees.fee(ActionCosts::new_action_receipt).exec_fee(); + result.gas_used = exec_fees; + result.gas_burnt = exec_fees; + // TODO(#8806): Support compute costs for actions. For now they match burnt gas. + result.compute_usage = exec_fees; + // Executing actions one by one + for (action_index, action) in action_receipt.actions.iter().enumerate() { + let action_hash = create_action_hash( + apply_state.current_protocol_version, + receipt, + &apply_state.prev_block_hash, + &apply_state.block_hash, + action_index, + ); + let mut new_result = self.apply_action( + action, + state_update, + apply_state, + &mut account, + &mut actor_id, + receipt, + action_receipt, + &promise_results, + &action_hash, + action_index, + &action_receipt.actions, + epoch_info_provider, + )?; + if new_result.result.is_ok() { + if let Err(e) = new_result.new_receipts.iter().try_for_each(|receipt| { + validate_receipt( + &apply_state.config.wasm_config.limit_config, + receipt, + apply_state.current_protocol_version, + ) + }) { + new_result.result = Err(ActionErrorKind::NewReceiptValidationError(e).into()); + } + } + result.merge(new_result)?; + // TODO storage error + if let Err(ref mut res) = result.result { + res.index = Some(action_index as u64); + break; + } + } + + // Going to check balance covers account's storage. + if result.result.is_ok() { + if let Some(ref mut account) = account { + match check_storage_stake( + account, + &apply_state.config, + apply_state.current_protocol_version, + ) { + Ok(()) => { + set_account(state_update, account_id.clone(), account); + } + Err(StorageStakingError::LackBalanceForStorageStaking(amount)) => { + result.merge(ActionResult { + result: Err(ActionError { + index: None, + kind: ActionErrorKind::LackBalanceForState { + account_id: account_id.clone(), + amount, + }, + }), + ..Default::default() + })?; + } + Err(StorageStakingError::StorageError(err)) => { + return Err(RuntimeError::StorageError( + StorageError::StorageInconsistentState(err), + )) + } + } + } + } + + let gas_deficit_amount = if receipt.predecessor_id.is_system() { + // We will set gas_burnt for refund receipts to be 0 when we calculate tx_burnt_amount + // Here we don't set result.gas_burnt to be zero if CountRefundReceiptsInGasLimit is + // enabled because we want it to be counted in gas limit calculation later + if !checked_feature!( + "stable", + CountRefundReceiptsInGasLimit, + apply_state.current_protocol_version + ) { + result.gas_burnt = 0; + result.compute_usage = 0; + result.gas_used = 0; + } + + // If the refund fails tokens are burned. + if result.result.is_err() { + stats.other_burnt_amount = safe_add_balance( + stats.other_burnt_amount, + total_deposit(&action_receipt.actions)?, + )? + } + 0 + } else { + // Calculating and generating refunds + self.generate_refund_receipts( + apply_state.gas_price, + receipt, + action_receipt, + &mut result, + &apply_state.config, + )? + }; + stats.gas_deficit_amount = safe_add_balance(stats.gas_deficit_amount, gas_deficit_amount)?; + + // Moving validator proposals + validator_power_proposals.append(&mut result.validator_power_proposals); + validator_frozen_proposals.append(&mut result.validator_frozen_proposals); + // Committing or rolling back state. + match &result.result { + Ok(_) => { + state_update.commit(StateChangeCause::ReceiptProcessing { + receipt_hash: receipt.get_hash(), + }); + } + Err(_) => { + state_update.rollback(); + } + }; + + // If the receipt is a refund, then we consider it free without burnt gas. + let gas_burnt: Gas = if receipt.predecessor_id.is_system() { 0 } else { result.gas_burnt }; + // `gas_deficit_amount` is strictly less than `gas_price * gas_burnt`. + let mut tx_burnt_amount = + safe_gas_to_balance(apply_state.gas_price, gas_burnt)? - gas_deficit_amount; + // The amount of tokens burnt for the execution of this receipt. It's used in the execution + // outcome. + let tokens_burnt = tx_burnt_amount; + + // Adding burnt gas reward for function calls if the account exists. + let receiver_gas_reward = result.gas_burnt_for_function_call + * *apply_state.config.fees.burnt_gas_reward.numer() as u64 + / *apply_state.config.fees.burnt_gas_reward.denom() as u64; + // The balance that the current account should receive as a reward for function call + // execution. + let receiver_reward = safe_gas_to_balance(apply_state.gas_price, receiver_gas_reward)? + .saturating_sub(gas_deficit_amount); + if receiver_reward > 0 { + let mut account = get_account(state_update, account_id)?; + if let Some(ref mut account) = account { + // Validators receive the remaining execution reward that was not given to the + // account holder. If the account doesn't exist by the end of the execution, the + // validators receive the full reward. + tx_burnt_amount -= receiver_reward; + account.set_amount(safe_add_balance(account.amount(), receiver_reward)?); + set_account(state_update, account_id.clone(), account); + state_update.commit(StateChangeCause::ActionReceiptGasReward { + receipt_hash: receipt.get_hash(), + }); + } + } + + stats.tx_burnt_amount = safe_add_balance(stats.tx_burnt_amount, tx_burnt_amount)?; + + // Generating outgoing data + // A { + // B().then(C())} B--data receipt->C + + // A { + // B(); 42} + if !action_receipt.output_data_receivers.is_empty() { + if let Ok(ReturnData::ReceiptIndex(receipt_index)) = result.result { + // Modifying a new receipt instead of sending data + match result + .new_receipts + .get_mut(receipt_index as usize) + .expect("the receipt for the given receipt index should exist") + .receipt + { + ReceiptEnum::Action(ref mut new_action_receipt) => new_action_receipt + .output_data_receivers + .extend_from_slice(&action_receipt.output_data_receivers), + _ => unreachable!("the receipt should be an action receipt"), + } + } else { + let data = match result.result { + Ok(ReturnData::Value(ref data)) => Some(data.clone()), + Ok(_) => Some(vec![]), + Err(_) => None, + }; + result.new_receipts.extend(action_receipt.output_data_receivers.iter().map( + |data_receiver| Receipt { + predecessor_id: account_id.clone(), + receiver_id: data_receiver.receiver_id.clone(), + receipt_id: CryptoHash::default(), + receipt: ReceiptEnum::Data(DataReceipt { + data_id: data_receiver.data_id, + data: data.clone(), + }), + }, + )); + }; + } + + // Generating receipt IDs + let receipt_ids = result + .new_receipts + .into_iter() + .enumerate() + .filter_map(|(receipt_index, mut new_receipt)| { + let receipt_id = create_receipt_id_from_receipt( + apply_state.current_protocol_version, + receipt, + &apply_state.prev_block_hash, + &apply_state.block_hash, + receipt_index, + ); + + new_receipt.receipt_id = receipt_id; + let is_action = matches!(&new_receipt.receipt, ReceiptEnum::Action(_)); + outgoing_receipts.push(new_receipt); + if is_action { + Some(receipt_id) + } else { + None + } + }) + .collect(); + + let status = match result.result { + Ok(ReturnData::ReceiptIndex(receipt_index)) => { + ExecutionStatus::SuccessReceiptId(create_receipt_id_from_receipt( + apply_state.current_protocol_version, + receipt, + &apply_state.prev_block_hash, + &apply_state.block_hash, + receipt_index as usize, + )) + } + Ok(ReturnData::Value(data)) => ExecutionStatus::SuccessValue(data), + Ok(ReturnData::None) => ExecutionStatus::SuccessValue(vec![]), + Err(e) => ExecutionStatus::Failure(TxExecutionError::ActionError(e)), + }; + + Self::print_log(&result.logs); + + Ok(ExecutionOutcomeWithId { + id: receipt.receipt_id, + outcome: ExecutionOutcome { + status, + logs: result.logs, + receipt_ids, + gas_burnt: result.gas_burnt, + compute_usage: Some(result.compute_usage), + tokens_burnt, + executor_id: account_id.clone(), + metadata: ExecutionMetadata::V3(result.profile), + }, + }) + } + + fn generate_refund_receipts( + &self, + current_gas_price: Balance, + receipt: &Receipt, + action_receipt: &ActionReceipt, + result: &mut ActionResult, + config: &RuntimeConfig, + ) -> Result { + let total_deposit = total_deposit(&action_receipt.actions)?; + let prepaid_gas = safe_add_gas( + total_prepaid_gas(&action_receipt.actions)?, + total_prepaid_send_fees(config, &action_receipt.actions)?, + )?; + let prepaid_exec_gas = safe_add_gas( + total_prepaid_exec_fees(config, &action_receipt.actions, &receipt.receiver_id)?, + config.fees.fee(ActionCosts::new_action_receipt).exec_fee(), + )?; + let deposit_refund = if result.result.is_err() { total_deposit } else { 0 }; + let gas_refund = if result.result.is_err() { + safe_add_gas(prepaid_gas, prepaid_exec_gas)? - result.gas_burnt + } else { + safe_add_gas(prepaid_gas, prepaid_exec_gas)? - result.gas_used + }; + // Refund for the unused portion of the gas at the price at which this gas was purchased. + let mut gas_balance_refund = safe_gas_to_balance(action_receipt.gas_price, gas_refund)?; + let mut gas_deficit_amount = 0; + if current_gas_price > action_receipt.gas_price { + // In a rare scenario, when the current gas price is higher than the purchased gas + // price, the difference is subtracted from the refund. If the refund doesn't have + // enough balance to cover the difference, then the remaining balance is considered + // the deficit and it's reported in the stats for the balance checker. + gas_deficit_amount = safe_gas_to_balance( + current_gas_price - action_receipt.gas_price, + result.gas_burnt, + )?; + if gas_balance_refund >= gas_deficit_amount { + gas_balance_refund -= gas_deficit_amount; + gas_deficit_amount = 0; + } else { + gas_deficit_amount -= gas_balance_refund; + gas_balance_refund = 0; + } + } else { + // Refund for the difference of the purchased gas price and the the current gas price. + gas_balance_refund = safe_add_balance( + gas_balance_refund, + safe_gas_to_balance( + action_receipt.gas_price - current_gas_price, + result.gas_burnt, + )?, + )?; + } + + if deposit_refund > 0 { + result + .new_receipts + .push(Receipt::new_balance_refund(&receipt.predecessor_id, deposit_refund)); + } + if gas_balance_refund > 0 { + // Gas refunds refund the allowance of the access key, so if the key exists on the + // account it will increase the allowance by the refund amount. + result.new_receipts.push(Receipt::new_gas_refund( + &action_receipt.signer_id, + gas_balance_refund, + action_receipt.signer_public_key.clone(), + )); + } + Ok(gas_deficit_amount) + } + + fn process_receipt( + &self, + state_update: &mut TrieUpdate, + apply_state: &ApplyState, + receipt: &Receipt, + outgoing_receipts: &mut Vec, + validator_power_proposals: &mut Vec, + validator_frozen_proposals: &mut Vec, + stats: &mut ApplyStats, + epoch_info_provider: &dyn EpochInfoProvider, + ) -> Result, RuntimeError> { + let account_id = &receipt.receiver_id; + match receipt.receipt { + ReceiptEnum::Data(ref data_receipt) => { + // Received a new data receipt. + // Saving the data into the state keyed by the data_id. + set_received_data( + state_update, + account_id.clone(), + data_receipt.data_id, + &ReceivedData { data: data_receipt.data.clone() }, + ); + // Check if there is already a receipt that was postponed and was awaiting for the + // given data_id. + // If we don't have a postponed receipt yet, we don't need to do anything for now. + if let Some(receipt_id) = get( + state_update, + &TrieKey::PostponedReceiptId { + receiver_id: account_id.clone(), + data_id: data_receipt.data_id, + }, + )? { + // There is already a receipt that is awaiting for the just received data. + // Removing this pending data_id for the receipt from the state. + state_update.remove(TrieKey::PostponedReceiptId { + receiver_id: account_id.clone(), + data_id: data_receipt.data_id, + }); + // Checking how many input data items is pending for the receipt. + let pending_data_count: u32 = get( + state_update, + &TrieKey::PendingDataCount { receiver_id: account_id.clone(), receipt_id }, + )? + .ok_or_else(|| { + StorageError::StorageInconsistentState( + "pending data count should be in the state".to_string(), + ) + })?; + if pending_data_count == 1 { + // It was the last input data pending for this receipt. We'll cleanup + // some receipt related fields from the state and execute the receipt. + + // Removing pending data count from the state. + state_update.remove(TrieKey::PendingDataCount { + receiver_id: account_id.clone(), + receipt_id, + }); + // Fetching the receipt itself. + let ready_receipt = + get_postponed_receipt(state_update, account_id, receipt_id)? + .ok_or_else(|| { + StorageError::StorageInconsistentState( + "pending receipt should be in the state".to_string(), + ) + })?; + // Removing the receipt from the state. + remove_postponed_receipt(state_update, account_id, receipt_id); + // Executing the receipt. It will read all the input data and clean it up + // from the state. + return self + .apply_action_receipt( + state_update, + apply_state, + &ready_receipt, + outgoing_receipts, + validator_power_proposals, + validator_frozen_proposals, + stats, + epoch_info_provider, + ) + .map(Some); + } else { + // There is still some pending data for the receipt, so we update the + // pending data count in the state. + set( + state_update, + TrieKey::PendingDataCount { + receiver_id: account_id.clone(), + receipt_id, + }, + &(pending_data_count.checked_sub(1).ok_or_else(|| { + StorageError::StorageInconsistentState( + "pending data count is 0, but there is a new DataReceipt" + .to_string(), + ) + })?), + ); + } + } + } + ReceiptEnum::Action(ref action_receipt) => { + // Received a new action receipt. We'll first check how many input data items + // were already received before and saved in the state. + // And if we have all input data, then we can immediately execute the receipt. + // If not, then we will postpone this receipt for later. + let mut pending_data_count: u32 = 0; + for data_id in &action_receipt.input_data_ids { + if get_received_data(state_update, account_id, *data_id)?.is_none() { + pending_data_count += 1; + // The data for a given data_id is not available, so we save a link to this + // receipt_id for the pending data_id into the state. + set( + state_update, + TrieKey::PostponedReceiptId { + receiver_id: account_id.clone(), + data_id: *data_id, + }, + &receipt.receipt_id, + ) + } + } + if pending_data_count == 0 { + // All input data is available. Executing the receipt. It will cleanup + // input data from the state. + return self + .apply_action_receipt( + state_update, + apply_state, + receipt, + outgoing_receipts, + validator_power_proposals, + validator_frozen_proposals, + stats, + epoch_info_provider, + ) + .map(Some); + } else { + // Not all input data is available now. + // Save the counter for the number of pending input data items into the state. + set( + state_update, + TrieKey::PendingDataCount { + receiver_id: account_id.clone(), + receipt_id: receipt.receipt_id, + }, + &pending_data_count, + ); + // Save the receipt itself into the state. + set_postponed_receipt(state_update, receipt); + } + } + }; + // We didn't trigger execution, so we need to commit the state. + state_update + .commit(StateChangeCause::PostponedReceipt { receipt_hash: receipt.get_hash() }); + Ok(None) + } + + /// Iterates over the validators in the current shard and updates their accounts to return stake + /// and allocate rewards. Also updates protocol treasury account if it belongs to the current + /// shard. + fn update_validator_accounts( + &self, + state_update: &mut TrieUpdate, + validator_accounts_update: &ValidatorAccountsUpdate, + stats: &mut ApplyStats, + ) -> Result<(), RuntimeError> { + for (account_id, max_of_power) in &validator_accounts_update.power_info { + if let Some(mut account) = get_account(state_update, account_id)? { + if account.power() < *max_of_power { + return Err(StorageError::StorageInconsistentState(format!( + "FATAL: powering invariant does not hold. \ + Account power {} is less than maximum of power {} in the past three epochs", + account.power(), + max_of_power)).into()); + } + let last_power_proposal = + *validator_accounts_update.last_power_proposals.get(account_id).unwrap_or(&0); + let return_power = account + .power() + .checked_sub(max(*max_of_power, last_power_proposal)) + .ok_or_else(|| RuntimeError::UnexpectedIntegerOverflow)?; + debug!(target: "runtime", "account {} return power {}", account_id, return_power); + account.set_power( + account + .power() + .checked_sub(return_power) + .ok_or_else(|| RuntimeError::UnexpectedIntegerOverflow)?, + ); + set_account(state_update, account_id.clone(), &account); + } else if *max_of_power > 0 { + // if max_of_power > 0, it means that the account must have power + // and therefore must exist + return Err(StorageError::StorageInconsistentState(format!( + "Account {} with max of power {} is not found", + account_id, max_of_power + )) + .into()); + } + } + + for (account_id, max_of_frozen) in &validator_accounts_update.frozen_info { + if let Some(mut account) = get_account(state_update, account_id)? { + if let Some(reward) = validator_accounts_update.validator_rewards.get(account_id) { + debug!(target: "runtime", "account {} adding reward {} to locked {}", account_id, reward, account.locked()); + account.set_locked( + account + .locked() + .checked_add(*reward) + .ok_or_else(|| RuntimeError::UnexpectedIntegerOverflow)?, + ); + } + debug!(target: "runtime", + "account {} locked {} max_of_frozen: {}", + account_id, account.locked(), max_of_frozen + ); + // customized by james savechives + // if account.locked() < *max_of_frozen { + // return Err(StorageError::StorageInconsistentState(format!( + // "FATAL: staking invariant does not hold. \ + // Account frozen {} is less than maximum of locked {} in the past three epochs", + // account.locked(), + // max_of_frozen)).into()); + // } + let last_frozen_proposal = + *validator_accounts_update.last_frozen_proposals.get(account_id).unwrap_or(&0); + let return_frozen = account + .locked() + .checked_sub(max(*max_of_frozen, last_frozen_proposal)) + .ok_or_else(|| RuntimeError::UnexpectedIntegerOverflow)?; + debug!(target: "runtime", "account {} return frozen {}", account_id, return_frozen); + account.set_locked( + account + .locked() + .checked_sub(return_frozen) + .ok_or_else(|| RuntimeError::UnexpectedIntegerOverflow)?, + ); + set_account(state_update, account_id.clone(), &account); + } else if *max_of_frozen > 0 { + // if max_of_power > 0, it means that the account must have power + // and therefore must exist + return Err(StorageError::StorageInconsistentState(format!( + "Account {} with max of locked {} is not found", + account_id, max_of_frozen + )) + .into()); + } + } + // Slash only to the accounts that are in the current shard. + for (account_id, stake) in validator_accounts_update.slashing_info.iter() { + if let Some(mut account) = get_account(state_update, account_id)? { + let amount_to_slash = stake.unwrap_or(account.locked()); + debug!(target: "runtime", "slashing {} of {} from {}", amount_to_slash, account.locked(), account_id); + if account.locked() < amount_to_slash { + return Err(StorageError::StorageInconsistentState(format!( + "FATAL: staking invariant does not hold. Account locked {} is less than slashed {}", + account.locked(), amount_to_slash)).into()); + } + stats.slashed_burnt_amount = stats + .slashed_burnt_amount + .checked_add(amount_to_slash) + .ok_or_else(|| RuntimeError::UnexpectedIntegerOverflow)?; + account.set_locked( + account + .locked() + .checked_sub(amount_to_slash) + .ok_or_else(|| RuntimeError::UnexpectedIntegerOverflow)?, + ); + set_account(state_update, account_id.clone(), &account); + } else { + return Err(StorageError::StorageInconsistentState(format!( + "Account {} to slash is not found", + account_id + )) + .into()); + } + } + // start customized by james savechives + if let Some(account_id) = &validator_accounts_update.protocol_treasury_account_id { + // If protocol treasury stakes, then the rewards was already distributed above. + if !validator_accounts_update.power_info.contains_key(account_id) { + let mut account = get_account(state_update, account_id)?.ok_or_else(|| { + StorageError::StorageInconsistentState(format!( + "Protocol treasury account {} is not found", + account_id + )) + })?; + let treasury_reward = *validator_accounts_update + .validator_rewards + .get(account_id) + .ok_or_else(|| { + StorageError::StorageInconsistentState(format!( + "Validator reward for the protocol treasury account {} is not found", + account_id + )) + })?; + account.set_amount( + account + .amount() + .checked_add(treasury_reward) + .ok_or_else(|| RuntimeError::UnexpectedIntegerOverflow)?, + ); + set_account(state_update, account_id.clone(), &account); + } + } + // end customized by James Savechives + state_update.commit(StateChangeCause::ValidatorAccountsUpdate); + + Ok(()) + } + + pub fn apply_migrations( + &self, + state_update: &mut TrieUpdate, + migration_data: &Arc, + migration_flags: &MigrationFlags, + protocol_version: ProtocolVersion, + ) -> Result<(Gas, Vec), StorageError> { + let mut gas_used: Gas = 0; + if ProtocolFeature::FixStorageUsage.protocol_version() == protocol_version + && migration_flags.is_first_block_of_version + { + for (account_id, delta) in &migration_data.storage_usage_delta { + // Account could have been deleted in the meantime, so we check if it is still Some + if let Some(mut account) = get_account(state_update, account_id)? { + // Storage usage is saved in state, hence it is nowhere close to max value + // of u64, and maximal delta is 4196, se we can add here without checking + // for overflow + account.set_storage_usage(account.storage_usage() + delta); + set_account(state_update, account_id.clone(), &account); + } + } + gas_used += migration_data.storage_usage_fix_gas; + state_update.commit(StateChangeCause::Migration); + } + + // Re-introduce receipts lost because of a bug in apply_chunks. + // We take the first block with existing chunk in the first epoch in which protocol feature + // RestoreReceiptsAfterFixApplyChunks was enabled, and put the restored receipts there. + // See https://github.com/utnet-org/utility/pull/4248/ for more details. + let receipts_to_restore = if ProtocolFeature::RestoreReceiptsAfterFixApplyChunks + .protocol_version() + == protocol_version + && migration_flags.is_first_block_with_chunk_of_version + { + // Note that receipts are restored only on mainnet so restored_receipts will be empty on + // other chains. + migration_data.restored_receipts.get(&0u64).cloned().unwrap_or_default() + } else { + vec![] + }; + + Ok((gas_used, receipts_to_restore)) + } + + /// Applies new signed transactions and incoming receipts for some chunk/shard on top of + /// given trie and the given state root. + /// If the validator accounts update is provided, updates validators accounts. + /// All new signed transactions should be valid and already verified by the chunk producer. + /// If any transaction is invalid, it would return an `InvalidTxError`. + /// Returns an `ApplyResult` that contains the new state root, trie changes, + /// new outgoing receipts, execution outcomes for + /// all transactions, local action receipts (generated from transactions with signer == + /// receivers) and incoming action receipts. + pub fn apply( + &self, + trie: Trie, + validator_accounts_update: &Option, + apply_state: &ApplyState, + incoming_receipts: &[Receipt], + transactions: &[SignedTransaction], + epoch_info_provider: &dyn EpochInfoProvider, + state_patch: SandboxStatePatch, + ) -> Result { + // state_patch must be empty unless this is sandbox build. Thanks to + // conditional compilation this always resolves to true so technically + // the check is not necessary. It’s defence in depth to make sure any + // future refactoring won’t break the condition. + assert!(cfg!(feature = "sandbox") || state_patch.is_empty()); + + let _span = tracing::debug_span!( + target: "runtime", + "apply", + protocol_version = apply_state.current_protocol_version, + num_transactions = transactions.len()) + .entered(); + + let mut prefetcher = TriePrefetcher::new_if_enabled(&trie); + let mut state_update = TrieUpdate::new(trie); + + if let Some(prefetcher) = &mut prefetcher { + // Prefetcher is allowed to fail + _ = prefetcher.prefetch_transactions_data(transactions); + } + + let mut stats = ApplyStats::default(); + + if let Some(validator_accounts_update) = validator_accounts_update { + self.update_validator_accounts( + &mut state_update, + validator_accounts_update, + &mut stats, + )?; + } + + let (gas_used_for_migrations, mut receipts_to_restore) = self + .apply_migrations( + &mut state_update, + &apply_state.migration_data, + &apply_state.migration_flags, + apply_state.current_protocol_version, + ) + .map_err(RuntimeError::StorageError)?; + // If we have receipts that need to be restored, prepend them to the list of incoming receipts + let incoming_receipts = if receipts_to_restore.is_empty() { + incoming_receipts + } else { + receipts_to_restore.extend_from_slice(incoming_receipts); + receipts_to_restore.as_slice() + }; + + let mut delayed_receipts_indices: DelayedReceiptIndices = + get(&state_update, &TrieKey::DelayedReceiptIndices)?.unwrap_or_default(); + let initial_delayed_receipt_indices = delayed_receipts_indices.clone(); + + if !apply_state.is_new_chunk + && apply_state.current_protocol_version + >= ProtocolFeature::FixApplyChunks.protocol_version() + { + let (trie, trie_changes, state_changes) = state_update.finalize()?; + let proof = trie.recorded_storage(); + return Ok(ApplyResult { + state_root: trie_changes.new_root, + trie_changes, + validator_power_proposals: vec![], + validator_frozen_proposals: vec![], + outgoing_receipts: vec![], + outcomes: vec![], + state_changes, + stats, + processed_delayed_receipts: vec![], + proof, + delayed_receipts_count: delayed_receipts_indices.len(), + metrics: None, + }); + } + + let mut outgoing_receipts = Vec::new(); + let mut validator_power_proposals = vec![]; + let mut validator_frozen_proposals = vec![]; + let mut local_receipts = vec![]; + let mut outcomes = vec![]; + let mut processed_delayed_receipts = vec![]; + // This contains the gas "burnt" for refund receipts. Even though we don't actually + // charge any gas for refund receipts, we still count the gas use towards the block gas + // limit + let mut total_gas_burnt = gas_used_for_migrations; + let mut total_compute_usage = total_gas_burnt; + let mut metrics = metrics::ApplyMetrics::default(); + + for signed_transaction in transactions { + let (receipt, outcome_with_id) = self.process_transaction( + &mut state_update, + apply_state, + signed_transaction, + &mut stats, + )?; + if receipt.receiver_id == signed_transaction.transaction.signer_id { + local_receipts.push(receipt); + } else { + outgoing_receipts.push(receipt); + } + + total_gas_burnt = safe_add_gas(total_gas_burnt, outcome_with_id.outcome.gas_burnt)?; + total_compute_usage = safe_add_compute( + total_compute_usage, + outcome_with_id + .outcome + .compute_usage + .expect("`process_transaction` must populate compute usage"), + )?; + + if !checked_feature!("stable", ComputeCosts, apply_state.current_protocol_version) { + assert_eq!( + total_compute_usage, total_gas_burnt, + "Compute usage must match burnt gas" + ); + } + + outcomes.push(outcome_with_id); + } + metrics.tx_processing_done(total_gas_burnt, total_compute_usage); + + let mut process_receipt = |receipt: &Receipt, + state_update: &mut TrieUpdate, + total_gas_burnt: &mut Gas, + total_compute_usage: &mut Compute| + -> Result<_, RuntimeError> { + let _span = tracing::debug_span!( + target: "runtime", + "process_receipt", + receipt_id = %receipt.receipt_id, + predecessor = %receipt.predecessor_id, + receiver = %receipt.receiver_id, + id = %receipt.receipt_id, + ) + .entered(); + let node_counter_before = state_update.trie().get_trie_nodes_count(); + let result = self.process_receipt( + state_update, + apply_state, + receipt, + &mut outgoing_receipts, + &mut validator_power_proposals, + &mut validator_frozen_proposals, + &mut stats, + epoch_info_provider, + ); + let node_counter_after = state_update.trie().get_trie_nodes_count(); + tracing::trace!(target: "runtime", ?node_counter_before, ?node_counter_after); + + if let Some(outcome_with_id) = result? { + *total_gas_burnt = + safe_add_gas(*total_gas_burnt, outcome_with_id.outcome.gas_burnt)?; + *total_compute_usage = safe_add_compute( + *total_compute_usage, + outcome_with_id + .outcome + .compute_usage + .expect("`process_receipt` must populate compute usage"), + )?; + + if !checked_feature!("stable", ComputeCosts, apply_state.current_protocol_version) { + assert_eq!( + total_compute_usage, total_gas_burnt, + "Compute usage must match burnt gas" + ); + } + outcomes.push(outcome_with_id); + } + Ok(()) + }; + + // TODO(#8859): Introduce a dedicated `compute_limit` for the chunk. + // For now compute limit always matches the gas limit. + let compute_limit = apply_state.gas_limit.unwrap_or(Gas::max_value()); + + // We first process local receipts. They contain staking, local contract calls, etc. + if let Some(prefetcher) = &mut prefetcher { + prefetcher.clear(); + // Prefetcher is allowed to fail + _ = prefetcher.prefetch_receipts_data(&local_receipts); + } + for receipt in local_receipts.iter() { + if total_compute_usage < compute_limit { + // NOTE: We don't need to validate the local receipt, because it's just validated in + // the `verify_and_charge_transaction`. + process_receipt( + receipt, + &mut state_update, + &mut total_gas_burnt, + &mut total_compute_usage, + )?; + } else { + set_delayed_receipt(&mut state_update, &mut delayed_receipts_indices, receipt); + } + } + metrics.local_receipts_done(total_gas_burnt, total_compute_usage); + + // Then we process the delayed receipts. It's a backlog of receipts from the past blocks. + while delayed_receipts_indices.first_index < delayed_receipts_indices.next_available_index { + if total_compute_usage >= compute_limit { + break; + } + let key = TrieKey::DelayedReceipt { index: delayed_receipts_indices.first_index }; + let receipt: Receipt = get(&state_update, &key)?.ok_or_else(|| { + StorageError::StorageInconsistentState(format!( + "Delayed receipt #{} should be in the state", + delayed_receipts_indices.first_index + )) + })?; + + if let Some(prefetcher) = &mut prefetcher { + prefetcher.clear(); + // Prefetcher is allowed to fail + _ = prefetcher.prefetch_receipts_data(std::slice::from_ref(&receipt)); + } + + // Validating the delayed receipt. If it fails, it's likely the state is inconsistent. + validate_receipt( + &apply_state.config.wasm_config.limit_config, + &receipt, + apply_state.current_protocol_version, + ) + .map_err(|e| { + StorageError::StorageInconsistentState(format!( + "Delayed receipt #{} in the state is invalid: {}", + delayed_receipts_indices.first_index, e + )) + })?; + + state_update.remove(key); + // Math checked above: first_index is less than next_available_index + delayed_receipts_indices.first_index += 1; + process_receipt( + &receipt, + &mut state_update, + &mut total_gas_burnt, + &mut total_compute_usage, + )?; + processed_delayed_receipts.push(receipt); + } + metrics.delayed_receipts_done(total_gas_burnt, total_compute_usage); + + // And then we process the new incoming receipts. These are receipts from other shards. + if let Some(prefetcher) = &mut prefetcher { + prefetcher.clear(); + // Prefetcher is allowed to fail + _ = prefetcher.prefetch_receipts_data(&incoming_receipts); + } + for receipt in incoming_receipts.iter() { + // Validating new incoming no matter whether we have available gas or not. We don't + // want to store invalid receipts in state as delayed. + validate_receipt( + &apply_state.config.wasm_config.limit_config, + receipt, + apply_state.current_protocol_version, + ) + .map_err(RuntimeError::ReceiptValidationError)?; + if total_compute_usage < compute_limit { + process_receipt( + receipt, + &mut state_update, + &mut total_gas_burnt, + &mut total_compute_usage, + )?; + } else { + set_delayed_receipt(&mut state_update, &mut delayed_receipts_indices, receipt); + } + } + metrics.incoming_receipts_done(total_gas_burnt, total_compute_usage); + + // No more receipts are executed on this trie, stop any pending prefetches on it. + if let Some(prefetcher) = &prefetcher { + prefetcher.clear(); + } + + if delayed_receipts_indices != initial_delayed_receipt_indices { + set(&mut state_update, TrieKey::DelayedReceiptIndices, &delayed_receipts_indices); + } + + // Just ignore this checker for now. + // We will work on this later. + check_balance( + &apply_state.config, + &state_update, + validator_accounts_update, + incoming_receipts, + transactions, + &outgoing_receipts, + &stats, + )?; + + state_update.commit(StateChangeCause::UpdatedDelayedReceipts); + self.apply_state_patch(&mut state_update, state_patch); + let (trie, trie_changes, state_changes) = state_update.finalize()?; + + // Dedup proposals from the same account. + // The order is deterministically changed. + let mut unique_power_proposals = vec![]; + let mut unique_frozen_proposals = vec![]; + let mut power_account_ids = HashSet::new(); + let mut frozen_account_ids = HashSet::new(); + for power_proposal in validator_power_proposals.into_iter().rev() { + let account_id = power_proposal.account_id(); + if !power_account_ids.contains(account_id) { + power_account_ids.insert(account_id.clone()); + unique_power_proposals.push(power_proposal); + } + } + for frozen_proposal in validator_frozen_proposals.into_iter().rev() { + let account_id = frozen_proposal.account_id(); + if !frozen_account_ids.contains(account_id) { + frozen_account_ids.insert(account_id.clone()); + unique_frozen_proposals.push(frozen_proposal); + } + } + + let state_root = trie_changes.new_root; + let proof = trie.recorded_storage(); + Ok(ApplyResult { + state_root, + trie_changes, + validator_power_proposals: unique_power_proposals, + validator_frozen_proposals: unique_frozen_proposals, + outgoing_receipts, + outcomes, + state_changes, + stats, + processed_delayed_receipts, + proof, + delayed_receipts_count: delayed_receipts_indices.len(), + metrics: Some(metrics), + }) + } + + fn apply_state_patch(&self, state_update: &mut TrieUpdate, state_patch: SandboxStatePatch) { + if state_patch.is_empty() { + return; + } + for record in state_patch { + match record { + StateRecord::Account { account_id, account } => { + set_account(state_update, account_id, &account); + } + StateRecord::Data { account_id, data_key, value } => { + state_update.set(TrieKey::ContractData { key: data_key.into(), account_id }, value.into()); + } + StateRecord::Contract { account_id, code } => { + let acc = get_account(state_update, &account_id).expect("Failed to read state").expect("Code state record should be preceded by the corresponding account record"); + // Recompute contract code hash. + let code = ContractCode::new(code, None); + set_code(state_update, account_id, &code); + assert_eq!(*code.hash(), acc.code_hash()); + } + StateRecord::AccessKey { account_id, public_key, access_key } => { + set_access_key(state_update, account_id, public_key, &access_key); + } + _ => unimplemented!("patch_state can only patch Account, AccessKey, Contract and Data kind of StateRecord") + } + } + state_update.commit(StateChangeCause::Migration); + } +} + +#[cfg(test)] +mod tests { + use assert_matches::assert_matches; + use unc_crypto::{InMemorySigner, KeyType, PublicKey, Signer}; + use unc_parameters::{ExtCosts, ParameterCost, RuntimeConfig}; + use unc_primitives::account::AccessKey; + use unc_primitives::hash::hash; + use unc_primitives::shard_layout::ShardUId; + use unc_primitives::test_utils::{account_new, MockEpochInfoProvider}; + use unc_primitives::transaction::{ + AddKeyAction, DeleteKeyAction, DeployContractAction, FunctionCallAction, TransferAction, + }; + use unc_primitives::types::MerkleHash; + use unc_primitives::version::PROTOCOL_VERSION; + use unc_store::test_utils::TestTriesBuilder; + use unc_store::{set_access_key, ShardTries, StoreCompiledContractCache}; + use testlib::runtime_utils::{alice_account, bob_account}; + + use super::*; + + const GAS_PRICE: Balance = 5000; + + fn to_yocto(near: Balance) -> Balance { + near * 10u128.pow(24) + } + + fn to_yocto2(near: Power) -> Power { + near * 10u128.pow(12) + } + + fn create_receipt_with_actions( + account_id: AccountId, + signer: Arc, + actions: Vec, + ) -> Receipt { + Receipt { + predecessor_id: account_id.clone(), + receiver_id: account_id.clone(), + receipt_id: CryptoHash::hash_borsh(actions.clone()), + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: account_id, + signer_public_key: signer.public_key(), + gas_price: GAS_PRICE, + output_data_receivers: vec![], + input_data_ids: vec![], + actions, + }), + } + } + + #[test] + fn test_get_and_set_accounts() { + let tries = TestTriesBuilder::new().build(); + let mut state_update = + tries.new_trie_update(ShardUId::single_shard(), MerkleHash::default()); + let test_account = account_new(to_yocto(10), hash(&[])); + let account_id = bob_account(); + set_account(&mut state_update, account_id.clone(), &test_account); + let get_res = get_account(&state_update, &account_id).unwrap().unwrap(); + assert_eq!(test_account, get_res); + } + + #[test] + fn test_get_account_from_trie() { + let tries = TestTriesBuilder::new().build(); + let root = MerkleHash::default(); + let mut state_update = tries.new_trie_update(ShardUId::single_shard(), root); + let test_account = account_new(to_yocto(10), hash(&[])); + let account_id = bob_account(); + set_account(&mut state_update, account_id.clone(), &test_account); + state_update.commit(StateChangeCause::InitialState); + let trie_changes = state_update.finalize().unwrap().1; + let mut store_update = tries.store_update(); + let new_root = tries.apply_all(&trie_changes, ShardUId::single_shard(), &mut store_update); + store_update.commit().unwrap(); + let new_state_update = tries.new_trie_update(ShardUId::single_shard(), new_root); + let get_res = get_account(&new_state_update, &account_id).unwrap().unwrap(); + assert_eq!(test_account, get_res); + } + + /***************/ + /* Apply tests */ + /***************/ + + fn setup_runtime( + initial_balance: Balance, + initial_locked: Balance, + initial_power: Power, + gas_limit: Gas, + ) -> (Runtime, ShardTries, CryptoHash, ApplyState, Arc, impl EpochInfoProvider) + { + let tries = TestTriesBuilder::new().build(); + let root = MerkleHash::default(); + let runtime = Runtime::new(); + let account_id = alice_account(); + let signer = Arc::new(InMemorySigner::from_seed( + account_id.clone(), + KeyType::ED25519, + account_id.as_ref(), + )); + + let mut initial_state = tries.new_trie_update(ShardUId::single_shard(), root); + let mut initial_account = account_new(initial_balance, hash(&[])); + // For the account and a full access key + initial_account.set_storage_usage(182); + initial_account.set_locked(initial_locked); + initial_account.set_power(initial_power); + set_account(&mut initial_state, account_id.clone(), &initial_account); + set_access_key( + &mut initial_state, + account_id, + signer.public_key(), + &AccessKey::full_access(), + ); + initial_state.commit(StateChangeCause::InitialState); + let trie_changes = initial_state.finalize().unwrap().1; + let mut store_update = tries.store_update(); + let root = tries.apply_all(&trie_changes, ShardUId::single_shard(), &mut store_update); + store_update.commit().unwrap(); + + let apply_state = ApplyState { + block_height: 1, + prev_block_hash: Default::default(), + block_hash: Default::default(), + epoch_id: Default::default(), + epoch_height: 0, + gas_price: GAS_PRICE, + block_timestamp: 100, + gas_limit: Some(gas_limit), + random_seed: Default::default(), + current_protocol_version: PROTOCOL_VERSION, + config: Arc::new(RuntimeConfig::test()), + cache: Some(Box::new(StoreCompiledContractCache::new(&tries.get_store()))), + is_new_chunk: true, + migration_data: Arc::new(MigrationData::default()), + migration_flags: MigrationFlags::default(), + }; + + (runtime, tries, root, apply_state, signer, MockEpochInfoProvider::default()) + } + + #[test] + fn test_apply_no_op() { + let (runtime, tries, root, apply_state, _, epoch_info_provider) = + setup_runtime(to_yocto(1_000_000), 0,0, 10u64.pow(15)); + runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + &[], + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + } + + #[test] + fn test_apply_check_balance_validation_rewards() { + let initial_locked = to_yocto(500_000); + let initial_power = to_yocto2(5); + let reward = to_yocto(10_000_000); + let small_refund = to_yocto(500); + let (runtime, tries, root, apply_state, _, epoch_info_provider) = + setup_runtime(to_yocto(1_000_000), initial_locked, initial_power, 10u64.pow(15)); + + let validator_accounts_update = ValidatorAccountsUpdate { + power_info: vec![(alice_account(), initial_power)].into_iter().collect(), + frozen_info: vec![(alice_account(), initial_locked)].into_iter().collect(), + validator_rewards: vec![(alice_account(), reward)].into_iter().collect(), + last_power_proposals: Default::default(), + last_frozen_proposals: Default::default(), + protocol_treasury_account_id: None, + slashing_info: HashMap::default(), + }; + + runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &Some(validator_accounts_update), + &apply_state, + &[Receipt::new_balance_refund(&alice_account(), small_refund)], + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + } + + #[test] + fn test_apply_refund_receipts() { + let initial_balance = to_yocto(1_000_000); + let initial_locked = to_yocto(500_000); + let initial_power = to_yocto2(5); + let small_transfer = to_yocto(10_000); + let gas_limit = 1; + let (runtime, tries, mut root, apply_state, _, epoch_info_provider) = + setup_runtime(initial_balance, initial_locked, initial_power, gas_limit); + + let n = 10; + let receipts = generate_refund_receipts(small_transfer, n); + + // Checking n receipts delayed + for i in 1..=n + 3 { + let prev_receipts: &[Receipt] = if i == 1 { &receipts } else { &[] }; + let apply_result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + prev_receipts, + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + let mut store_update = tries.store_update(); + root = tries.apply_all( + &apply_result.trie_changes, + ShardUId::single_shard(), + &mut store_update, + ); + store_update.commit().unwrap(); + let state = tries.new_trie_update(ShardUId::single_shard(), root); + let account = get_account(&state, &alice_account()).unwrap().unwrap(); + let capped_i = std::cmp::min(i, n); + assert_eq!( + account.amount(), + initial_balance + + small_transfer * Balance::from(capped_i) + + Balance::from(capped_i * (capped_i - 1) / 2) + ); + } + } + + #[test] + fn test_apply_delayed_receipts_feed_all_at_once() { + let initial_balance = to_yocto(1_000_000); + let initial_locked = to_yocto(500_000); + let initial_power = to_yocto2(5); + let small_transfer = to_yocto(10_000); + let gas_limit = 1; + let (runtime, tries, mut root, apply_state, _, epoch_info_provider) = + setup_runtime(initial_balance, initial_locked, initial_power, gas_limit); + + let n = 10; + let receipts = generate_receipts(small_transfer, n); + + // Checking n receipts delayed by 1 + 3 extra + for i in 1..=n + 3 { + let prev_receipts: &[Receipt] = if i == 1 { &receipts } else { &[] }; + let apply_result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + prev_receipts, + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + let mut store_update = tries.store_update(); + root = tries.apply_all( + &apply_result.trie_changes, + ShardUId::single_shard(), + &mut store_update, + ); + store_update.commit().unwrap(); + let state = tries.new_trie_update(ShardUId::single_shard(), root); + let account = get_account(&state, &alice_account()).unwrap().unwrap(); + let capped_i = std::cmp::min(i, n); + assert_eq!( + account.amount(), + initial_balance + + small_transfer * Balance::from(capped_i) + + Balance::from(capped_i * (capped_i - 1) / 2) + ); + } + } + + #[test] + fn test_apply_delayed_receipts_add_more_using_chunks() { + let initial_balance = to_yocto(1_000_000); + let initial_locked = to_yocto(500_000); + let initial_power = to_yocto2(5); + let small_transfer = to_yocto(10_000); + let (runtime, tries, mut root, mut apply_state, _, epoch_info_provider) = + setup_runtime(initial_balance, initial_locked, initial_power, 1); + + let receipt_gas_cost = + apply_state.config.fees.fee(ActionCosts::new_action_receipt).exec_fee() + + apply_state.config.fees.fee(ActionCosts::transfer).exec_fee(); + apply_state.gas_limit = Some(receipt_gas_cost * 3); + + let n = 40; + let receipts = generate_receipts(small_transfer, n); + let mut receipt_chunks = receipts.chunks_exact(4); + + // Every time we'll process 3 receipts, so we need n / 3 rounded up. Then we do 3 extra. + for i in 1..=n / 3 + 3 { + let prev_receipts: &[Receipt] = receipt_chunks.next().unwrap_or_default(); + let apply_result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + prev_receipts, + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + let mut store_update = tries.store_update(); + let new_root = tries.apply_all( + &apply_result.trie_changes, + ShardUId::single_shard(), + &mut store_update, + ); + root = new_root; + store_update.commit().unwrap(); + let state = tries.new_trie_update(ShardUId::single_shard(), root); + let account = get_account(&state, &alice_account()).unwrap().unwrap(); + let capped_i = std::cmp::min(i * 3, n); + assert_eq!( + account.amount(), + initial_balance + + small_transfer * Balance::from(capped_i) + + Balance::from(capped_i * (capped_i - 1) / 2) + ); + } + } + + #[test] + fn test_apply_delayed_receipts_adjustable_gas_limit() { + let initial_balance = to_yocto(1_000_000); + let initial_locked = to_yocto(500_000); + let initial_power = to_yocto2(5); + let small_transfer = to_yocto(10_000); + let (runtime, tries, mut root, mut apply_state, _, epoch_info_provider) = + setup_runtime(initial_balance, initial_locked, initial_power, 1); + + let receipt_gas_cost = + apply_state.config.fees.fee(ActionCosts::new_action_receipt).exec_fee() + + apply_state.config.fees.fee(ActionCosts::transfer).exec_fee(); + + let n = 120; + let receipts = generate_receipts(small_transfer, n); + let mut receipt_chunks = receipts.chunks_exact(4); + + let mut num_receipts_given = 0; + let mut num_receipts_processed = 0; + let mut num_receipts_per_block = 1; + // Test adjusts gas limit based on the number of receipt given and number of receipts processed. + while num_receipts_processed < n { + if num_receipts_given > num_receipts_processed { + num_receipts_per_block += 1; + } else if num_receipts_per_block > 1 { + num_receipts_per_block -= 1; + } + apply_state.gas_limit = Some(num_receipts_per_block * receipt_gas_cost); + let prev_receipts: &[Receipt] = receipt_chunks.next().unwrap_or_default(); + num_receipts_given += prev_receipts.len() as u64; + let apply_result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + prev_receipts, + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + let mut store_update = tries.store_update(); + root = tries.apply_all( + &apply_result.trie_changes, + ShardUId::single_shard(), + &mut store_update, + ); + store_update.commit().unwrap(); + let state = tries.new_trie_update(ShardUId::single_shard(), root); + num_receipts_processed += apply_result.outcomes.len() as u64; + let account = get_account(&state, &alice_account()).unwrap().unwrap(); + assert_eq!( + account.amount(), + initial_balance + + small_transfer * Balance::from(num_receipts_processed) + + Balance::from(num_receipts_processed * (num_receipts_processed - 1) / 2) + ); + let expected_queue_length = num_receipts_given - num_receipts_processed; + println!( + "{} processed out of {} given. With limit {} receipts per block. The expected delayed_receipts_count is {}. The delayed_receipts_count is {}.", + num_receipts_processed, + num_receipts_given, + num_receipts_per_block, + expected_queue_length, + apply_result.delayed_receipts_count, + ); + assert_eq!(apply_result.delayed_receipts_count, expected_queue_length); + } + } + + fn generate_receipts(small_transfer: u128, n: u64) -> Vec { + let mut receipt_id = CryptoHash::default(); + (0..n) + .map(|i| { + receipt_id = hash(receipt_id.as_ref()); + Receipt { + predecessor_id: bob_account(), + receiver_id: alice_account(), + receipt_id, + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: bob_account(), + signer_public_key: PublicKey::empty(KeyType::ED25519), + gas_price: GAS_PRICE, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: vec![Action::Transfer(TransferAction { + deposit: small_transfer + Balance::from(i), + })], + }), + } + }) + .collect() + } + + fn generate_refund_receipts(small_transfer: u128, n: u64) -> Vec { + let mut receipt_id = CryptoHash::default(); + (0..n) + .map(|i| { + receipt_id = hash(receipt_id.as_ref()); + Receipt::new_balance_refund(&alice_account(), small_transfer + Balance::from(i)) + }) + .collect() + } + + #[test] + fn test_apply_delayed_receipts_local_tx() { + let initial_balance = to_yocto(1_000_000); + let initial_locked = to_yocto(500_000); + let initial_power = to_yocto2(5); + let small_transfer = to_yocto(10_000); + let (runtime, tries, root, mut apply_state, signer, epoch_info_provider) = + setup_runtime(initial_balance, initial_locked, initial_power, 1); + + let receipt_exec_gas_fee = 1000; + let mut free_config = RuntimeConfig::free(); + free_config.fees.action_fees[ActionCosts::new_action_receipt].execution = + receipt_exec_gas_fee; + apply_state.config = Arc::new(free_config); + // This allows us to execute 3 receipts per apply. + apply_state.gas_limit = Some(receipt_exec_gas_fee * 3); + + let num_receipts = 6; + let receipts = generate_receipts(small_transfer, num_receipts); + + let num_transactions = 9; + let local_transactions = (0..num_transactions) + .map(|i| { + SignedTransaction::send_money( + i + 1, + alice_account(), + alice_account(), + &*signer, + small_transfer, + CryptoHash::default(), + ) + }) + .collect::>(); + + // STEP #1. Pass 4 new local transactions + 2 receipts. + // We can process only 3 local TX receipts TX#0, TX#1, TX#2. + // TX#3 receipt and R#0, R#1 are delayed. + // The new delayed queue is TX#3, R#0, R#1. + let apply_result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + &receipts[0..2], + &local_transactions[0..4], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + let mut store_update = tries.store_update(); + let root = tries.apply_all( + &apply_result.trie_changes, + ShardUId::single_shard(), + &mut store_update, + ); + store_update.commit().unwrap(); + + assert_eq!( + apply_result.outcomes.iter().map(|o| o.id).collect::>(), + vec![ + local_transactions[0].get_hash(), // tx 0 + local_transactions[1].get_hash(), // tx 1 + local_transactions[2].get_hash(), // tx 2 + local_transactions[3].get_hash(), // tx 3 - the TX is processed, but the receipt is delayed + create_receipt_id_from_transaction( + PROTOCOL_VERSION, + &local_transactions[0], + &apply_state.prev_block_hash, + &apply_state.block_hash + ), // receipt for tx 0 + create_receipt_id_from_transaction( + PROTOCOL_VERSION, + &local_transactions[1], + &apply_state.prev_block_hash, + &apply_state.block_hash + ), // receipt for tx 1 + create_receipt_id_from_transaction( + PROTOCOL_VERSION, + &local_transactions[2], + &apply_state.prev_block_hash, + &apply_state.block_hash + ), // receipt for tx 2 + ], + "STEP #1 failed", + ); + + // STEP #2. Pass 1 new local transaction (TX#4) + 1 receipts R#2. + // We process 1 local receipts for TX#4, then delayed TX#3 receipt and then receipt R#0. + // R#2 is added to delayed queue. + // The new delayed queue is R#1, R#2 + let apply_result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + &receipts[2..3], + &local_transactions[4..5], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + let mut store_update = tries.store_update(); + let root = tries.apply_all( + &apply_result.trie_changes, + ShardUId::single_shard(), + &mut store_update, + ); + store_update.commit().unwrap(); + + assert_eq!( + apply_result.outcomes.iter().map(|o| o.id).collect::>(), + vec![ + local_transactions[4].get_hash(), // tx 4 + create_receipt_id_from_transaction( + PROTOCOL_VERSION, + &local_transactions[4], + &apply_state.prev_block_hash, + &apply_state.block_hash, + ), // receipt for tx 4 + create_receipt_id_from_transaction( + PROTOCOL_VERSION, + &local_transactions[3], + &apply_state.prev_block_hash, + &apply_state.block_hash, + ), // receipt for tx 3 + receipts[0].receipt_id, // receipt #0 + ], + "STEP #2 failed", + ); + + // STEP #3. Pass 4 new local transaction (TX#5, TX#6, TX#7, TX#8) and 1 new receipt R#3. + // We process 3 local receipts for TX#5, TX#6, TX#7. + // TX#8 and R#3 are added to delayed queue. + // The new delayed queue is R#1, R#2, TX#8, R#3 + let apply_result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + &receipts[3..4], + &local_transactions[5..9], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + let mut store_update = tries.store_update(); + let root = tries.apply_all( + &apply_result.trie_changes, + ShardUId::single_shard(), + &mut store_update, + ); + store_update.commit().unwrap(); + + assert_eq!( + apply_result.outcomes.iter().map(|o| o.id).collect::>(), + vec![ + local_transactions[5].get_hash(), // tx 5 + local_transactions[6].get_hash(), // tx 6 + local_transactions[7].get_hash(), // tx 7 + local_transactions[8].get_hash(), // tx 8 + create_receipt_id_from_transaction( + PROTOCOL_VERSION, + &local_transactions[5], + &apply_state.prev_block_hash, + &apply_state.block_hash, + ), // receipt for tx 5 + create_receipt_id_from_transaction( + PROTOCOL_VERSION, + &local_transactions[6], + &apply_state.prev_block_hash, + &apply_state.block_hash, + ), // receipt for tx 6 + create_receipt_id_from_transaction( + PROTOCOL_VERSION, + &local_transactions[7], + &apply_state.prev_block_hash, + &apply_state.block_hash, + ), // receipt for tx 7 + ], + "STEP #3 failed", + ); + + // STEP #4. Pass no new TXs and 1 receipt R#4. + // We process R#1, R#2, TX#8. + // R#4 is added to delayed queue. + // The new delayed queue is R#3, R#4 + let apply_result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + &receipts[4..5], + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + let mut store_update = tries.store_update(); + let root = tries.apply_all( + &apply_result.trie_changes, + ShardUId::single_shard(), + &mut store_update, + ); + store_update.commit().unwrap(); + + assert_eq!( + apply_result.outcomes.iter().map(|o| o.id).collect::>(), + vec![ + receipts[1].receipt_id, // receipt #1 + receipts[2].receipt_id, // receipt #2 + create_receipt_id_from_transaction( + PROTOCOL_VERSION, + &local_transactions[8], + &apply_state.prev_block_hash, + &apply_state.block_hash, + ), // receipt for tx 8 + ], + "STEP #4 failed", + ); + + // STEP #5. Pass no new TXs and 1 receipt R#5. + // We process R#3, R#4, R#5. + // The new delayed queue is empty. + let apply_result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + &receipts[5..6], + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + + assert_eq!( + apply_result.outcomes.iter().map(|o| o.id).collect::>(), + vec![ + receipts[3].receipt_id, // receipt #3 + receipts[4].receipt_id, // receipt #4 + receipts[5].receipt_id, // receipt #5 + ], + "STEP #5 failed", + ); + } + + #[test] + fn test_apply_deficit_gas_for_transfer() { + let initial_balance = to_yocto(1_000_000); + let initial_locked = to_yocto(500_000); + let initial_power = to_yocto2(5); + let small_transfer = to_yocto(10_000); + let gas_limit = 10u64.pow(15); + let (runtime, tries, root, apply_state, _, epoch_info_provider) = + setup_runtime(initial_balance, initial_locked, initial_power, gas_limit); + + let n = 1; + let mut receipts = generate_receipts(small_transfer, n); + if let ReceiptEnum::Action(action_receipt) = &mut receipts.get_mut(0).unwrap().receipt { + action_receipt.gas_price = GAS_PRICE / 10; + } + + let result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + &receipts, + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + assert_eq!(result.stats.gas_deficit_amount, result.stats.tx_burnt_amount * 9) + } + + #[test] + fn test_apply_deficit_gas_for_function_call_covered() { + let initial_balance = to_yocto(1_000_000); + let initial_locked = to_yocto(500_000); + let initial_power = to_yocto2(5); + let gas_limit = 10u64.pow(15); + let (runtime, tries, root, apply_state, _, epoch_info_provider) = + setup_runtime(initial_balance, initial_locked, initial_power, gas_limit); + + let gas = 2 * 10u64.pow(14); + let gas_price = GAS_PRICE / 10; + let actions = vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "hello".to_string(), + args: b"world".to_vec(), + gas, + deposit: 0, + }))]; + + let expected_gas_burnt = safe_add_gas( + apply_state.config.fees.fee(ActionCosts::new_action_receipt).exec_fee(), + total_prepaid_exec_fees(&apply_state.config, &actions, &alice_account()).unwrap(), + ) + .unwrap(); + let receipts = vec![Receipt { + predecessor_id: bob_account(), + receiver_id: alice_account(), + receipt_id: CryptoHash::default(), + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: bob_account(), + signer_public_key: PublicKey::empty(KeyType::ED25519), + gas_price, + output_data_receivers: vec![], + input_data_ids: vec![], + actions, + }), + }]; + let total_receipt_cost = Balance::from(gas + expected_gas_burnt) * gas_price; + let expected_gas_burnt_amount = Balance::from(expected_gas_burnt) * GAS_PRICE; + let expected_refund = total_receipt_cost - expected_gas_burnt_amount; + + let result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + &receipts, + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + // We used part of the prepaid gas to paying extra fees. + assert_eq!(result.stats.gas_deficit_amount, 0); + // The refund is less than the received amount. + match &result.outgoing_receipts[0].receipt { + ReceiptEnum::Action(ActionReceipt { actions, .. }) => { + assert!( + matches!(actions[0], Action::Transfer(TransferAction { deposit }) if deposit == expected_refund) + ); + } + _ => unreachable!(), + }; + } + + #[test] + fn test_apply_deficit_gas_for_function_call_partial() { + let initial_balance = to_yocto(1_000_000); + let initial_locked = to_yocto(500_000); + let initial_power = to_yocto2(5); + let gas_limit = 10u64.pow(15); + let (runtime, tries, root, apply_state, _, epoch_info_provider) = + setup_runtime(initial_balance, initial_locked, initial_power, gas_limit); + + let gas = 1_000_000; + let gas_price = GAS_PRICE / 10; + let actions = vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "hello".to_string(), + args: b"world".to_vec(), + gas, + deposit: 0, + }))]; + + let expected_gas_burnt = safe_add_gas( + apply_state.config.fees.fee(ActionCosts::new_action_receipt).exec_fee(), + total_prepaid_exec_fees(&apply_state.config, &actions, &alice_account()).unwrap(), + ) + .unwrap(); + let receipts = vec![Receipt { + predecessor_id: bob_account(), + receiver_id: alice_account(), + receipt_id: CryptoHash::default(), + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: bob_account(), + signer_public_key: PublicKey::empty(KeyType::ED25519), + gas_price, + output_data_receivers: vec![], + input_data_ids: vec![], + actions, + }), + }]; + let total_receipt_cost = Balance::from(gas + expected_gas_burnt) * gas_price; + let expected_gas_burnt_amount = Balance::from(expected_gas_burnt) * GAS_PRICE; + let expected_deficit = expected_gas_burnt_amount - total_receipt_cost; + + let result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + &receipts, + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + // Used full prepaid gas, but it still not enough to cover deficit. + assert_eq!(result.stats.gas_deficit_amount, expected_deficit); + // Burnt all the fees + all prepaid gas. + assert_eq!(result.stats.tx_burnt_amount, total_receipt_cost); + } + + #[test] + fn test_delete_key_add_key() { + let initial_locked = to_yocto(500_000); + let initial_power = to_yocto2(5); + let (runtime, tries, root, apply_state, signer, epoch_info_provider) = + setup_runtime(to_yocto(1_000_000), initial_locked, initial_power, 10u64.pow(15)); + + let state_update = tries.new_trie_update(ShardUId::single_shard(), root); + let initial_account_state = get_account(&state_update, &alice_account()).unwrap().unwrap(); + + let actions = vec![ + Action::DeleteKey(Box::new(DeleteKeyAction { public_key: signer.public_key() })), + Action::AddKey(Box::new(AddKeyAction { + public_key: signer.public_key(), + access_key: AccessKey::full_access(), + })), + ]; + + let receipts = vec![create_receipt_with_actions(alice_account(), signer, actions)]; + + let apply_result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + &receipts, + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + let mut store_update = tries.store_update(); + let root = tries.apply_all( + &apply_result.trie_changes, + ShardUId::single_shard(), + &mut store_update, + ); + store_update.commit().unwrap(); + + let state_update = tries.new_trie_update(ShardUId::single_shard(), root); + let final_account_state = get_account(&state_update, &alice_account()).unwrap().unwrap(); + + assert_eq!(initial_account_state.storage_usage(), final_account_state.storage_usage()); + } + + #[test] + fn test_delete_key_underflow() { + let initial_locked = to_yocto(500_000); + let initial_power = to_yocto2(5); + let (runtime, tries, root, apply_state, signer, epoch_info_provider) = + setup_runtime(to_yocto(1_000_000), initial_locked, initial_power, 10u64.pow(15)); + + let mut state_update = tries.new_trie_update(ShardUId::single_shard(), root); + let mut initial_account_state = + get_account(&state_update, &alice_account()).unwrap().unwrap(); + initial_account_state.set_storage_usage(10); + set_account(&mut state_update, alice_account(), &initial_account_state); + state_update.commit(StateChangeCause::InitialState); + let trie_changes = state_update.finalize().unwrap().1; + let mut store_update = tries.store_update(); + let root = tries.apply_all(&trie_changes, ShardUId::single_shard(), &mut store_update); + store_update.commit().unwrap(); + + let actions = + vec![Action::DeleteKey(Box::new(DeleteKeyAction { public_key: signer.public_key() }))]; + + let receipts = vec![create_receipt_with_actions(alice_account(), signer, actions)]; + + let apply_result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + &receipts, + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + let mut store_update = tries.store_update(); + let root = tries.apply_all( + &apply_result.trie_changes, + ShardUId::single_shard(), + &mut store_update, + ); + store_update.commit().unwrap(); + + let state_update = tries.new_trie_update(ShardUId::single_shard(), root); + let final_account_state = get_account(&state_update, &alice_account()).unwrap().unwrap(); + + assert_eq!(final_account_state.storage_usage(), 0); + } + + // This test only works on platforms that support wasmer2. + #[test] + #[cfg(target_arch = "x86_64")] + fn test_contract_precompilation() { + let initial_balance = to_yocto(1_000_000); + let initial_locked = to_yocto(500_000); + let gas_limit = 10u64.pow(15); + let (runtime, tries, root, apply_state, signer, epoch_info_provider) = + setup_runtime(initial_balance, initial_locked, gas_limit); + + let wasm_code = unc_test_contracts::rs_contract().to_vec(); + let actions = + vec![Action::DeployContract(DeployContractAction { code: wasm_code.clone() })]; + + let receipts = vec![create_receipt_with_actions(alice_account(), signer, actions)]; + + let apply_result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + &receipts, + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + let mut store_update = tries.store_update(); + tries.apply_all(&apply_result.trie_changes, ShardUId::single_shard(), &mut store_update); + store_update.commit().unwrap(); + + let contract_code = unc_vm_runner::ContractCode::new(wasm_code, None); + let key = + unc_vm_runner::get_contract_cache_key(&contract_code, &apply_state.config.wasm_config); + apply_state + .cache + .unwrap() + .get(&key) + .expect("Compiled contract should be cached") + .expect("Compilation result should be non-empty"); + } + + #[test] + fn test_compute_usage_limit() { + let (runtime, tries, root, mut apply_state, signer, epoch_info_provider) = + setup_runtime(to_yocto(1_000_000), to_yocto(500_000), to_yocto2(5), 1); + + let mut free_config = RuntimeConfig::free(); + let sha256_cost = ParameterCost { + gas: Gas::from(1_000_000u64), + compute: Compute::from(10_000_000_000_000u64), + }; + free_config.wasm_config.ext_costs.costs[ExtCosts::sha256_base] = sha256_cost.clone(); + apply_state.config = Arc::new(free_config); + // This allows us to execute 1 receipt with a function call per apply. + apply_state.gas_limit = Some(sha256_cost.compute); + + let deploy_contract_receipt = create_receipt_with_actions( + alice_account(), + signer.clone(), + vec![Action::DeployContract(DeployContractAction { + code: unc_test_contracts::rs_contract().to_vec(), + })], + ); + + let first_call_receipt = create_receipt_with_actions( + alice_account(), + signer.clone(), + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "ext_sha256".to_string(), + args: b"first".to_vec(), + gas: sha256_cost.gas, + deposit: 0, + }))], + ); + + let second_call_receipt = create_receipt_with_actions( + alice_account(), + signer, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "ext_sha256".to_string(), + args: b"second".to_vec(), + gas: sha256_cost.gas, + deposit: 0, + }))], + ); + + let apply_result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + &vec![ + deploy_contract_receipt.clone(), + first_call_receipt.clone(), + second_call_receipt.clone(), + ], + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + let mut store_update = tries.store_update(); + let root = tries.apply_all( + &apply_result.trie_changes, + ShardUId::single_shard(), + &mut store_update, + ); + store_update.commit().unwrap(); + + // Only first two receipts should fit into the chunk due to the compute usage limit. + assert_matches!(&apply_result.outcomes[..], [first, second] => { + assert_eq!(first.id, deploy_contract_receipt.receipt_id); + assert_matches!(first.outcome.status, ExecutionStatus::SuccessValue(_)); + + assert_eq!(second.id, first_call_receipt.receipt_id); + assert_eq!(second.outcome.compute_usage.unwrap(), sha256_cost.compute); + assert_matches!(second.outcome.status, ExecutionStatus::SuccessValue(_)); + }); + + let apply_result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + &[], + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + + assert_matches!(&apply_result.outcomes[..], [ExecutionOutcomeWithId { id, outcome }] => { + assert_eq!(*id, second_call_receipt.receipt_id); + assert_eq!(outcome.compute_usage.unwrap(), sha256_cost.compute); + assert_matches!(outcome.status, ExecutionStatus::SuccessValue(_)); + }); + } + + #[test] + fn test_compute_usage_limit_with_failed_receipt() { + let (runtime, tries, root, apply_state, signer, epoch_info_provider) = + setup_runtime(to_yocto(1_000_000), to_yocto(500_000), to_yocto2(5), 10u64.pow(15)); + + let deploy_contract_receipt = create_receipt_with_actions( + alice_account(), + signer.clone(), + vec![Action::DeployContract(DeployContractAction { + code: unc_test_contracts::rs_contract().to_vec(), + })], + ); + + let first_call_receipt = create_receipt_with_actions( + alice_account(), + signer, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "ext_sha256".to_string(), + args: b"first".to_vec(), + gas: 1, + deposit: 0, + }))], + ); + + let apply_result = runtime + .apply( + tries.get_trie_for_shard(ShardUId::single_shard(), root), + &None, + &apply_state, + &vec![deploy_contract_receipt.clone(), first_call_receipt.clone()], + &[], + &epoch_info_provider, + Default::default(), + ) + .unwrap(); + + assert_matches!(&apply_result.outcomes[..], [first, second] => { + assert_eq!(first.id, deploy_contract_receipt.receipt_id); + assert_matches!(first.outcome.status, ExecutionStatus::SuccessValue(_)); + + assert_eq!(second.id, first_call_receipt.receipt_id); + assert_matches!(second.outcome.status, ExecutionStatus::Failure(_)); + }); + } +} + +/// Interface provided for gas cost estimations. +pub mod estimator { + use unc_primitives::errors::RuntimeError; + use unc_primitives::receipt::Receipt; + use unc_primitives::runtime::apply_state::ApplyState; + use unc_primitives::transaction::ExecutionOutcomeWithId; + use unc_primitives::types::validator_power::ValidatorPower; + use unc_primitives::types::EpochInfoProvider; + use unc_primitives::types::validator_frozen::ValidatorFrozen; + use unc_store::TrieUpdate; + + use crate::ApplyStats; + + use super::Runtime; + + pub fn apply_action_receipt( + state_update: &mut TrieUpdate, + apply_state: &ApplyState, + receipt: &Receipt, + outgoing_receipts: &mut Vec, + validator_power_proposals: &mut Vec, + validator_frozen_proposals: &mut Vec, + stats: &mut ApplyStats, + epoch_info_provider: &dyn EpochInfoProvider, + ) -> Result { + Runtime {}.apply_action_receipt( + state_update, + apply_state, + receipt, + outgoing_receipts, + validator_power_proposals, + validator_frozen_proposals, + stats, + epoch_info_provider, + ) + } +} diff --git a/runtime/runtime/src/metrics.rs b/runtime/runtime/src/metrics.rs new file mode 100644 index 000000000..ce062c3e9 --- /dev/null +++ b/runtime/runtime/src/metrics.rs @@ -0,0 +1,308 @@ +use unc_o11y::metrics::{ + try_create_histogram_vec, try_create_int_counter, try_create_int_counter_vec, HistogramVec, + IntCounter, IntCounterVec, +}; +use once_cell::sync::Lazy; + +pub static ACTION_CALLED_COUNT: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_action_called_count", + "Number of times given action has been called since starting this node", + &["action"], + ) + .unwrap() +}); + +pub static TRANSACTION_PROCESSED_TOTAL: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_transaction_processed_total", + "The number of transactions processed since starting this node", + ) + .unwrap() +}); +pub static TRANSACTION_PROCESSED_SUCCESSFULLY_TOTAL: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_transaction_processed_successfully_total", + "The number of transactions processed successfully since starting this node", + ) + .unwrap() +}); +pub static TRANSACTION_PROCESSED_FAILED_TOTAL: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_transaction_processed_failed_total", + "The number of transactions processed and failed since starting this node", + ) + .unwrap() +}); +pub static PREFETCH_ENQUEUED: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_prefetch_enqueued", + "Prefetch requests queued up", + &["shard_id"], + ) + .unwrap() +}); +pub static PREFETCH_QUEUE_FULL: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_prefetch_queue_full", + "Prefetch requests failed to queue up", + &["shard_id"], + ) + .unwrap() +}); +pub static FUNCTION_CALL_PROCESSED: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_function_call_processed", + "The number of function calls processed since starting this node", + &["result"], + ) + .unwrap() +}); +pub static FUNCTION_CALL_PROCESSED_FUNCTION_CALL_ERRORS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_function_call_processed_function_call_errors", + "The number of function calls resulting in function call errors, since starting this node", + &["error_type"], + ) + .unwrap() +}); +pub static FUNCTION_CALL_PROCESSED_COMPILATION_ERRORS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_function_call_processed_compilation_errors", + "The number of function calls resulting in compilation errors, since starting this node", + &["error_type"], + ) + .unwrap() +}); +pub static FUNCTION_CALL_PROCESSED_METHOD_RESOLVE_ERRORS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_function_call_processed_method_resolve_errors", + "The number of function calls resulting in method resolve errors, since starting this node", + &["error_type"], + ) + .unwrap() +}); +pub static FUNCTION_CALL_PROCESSED_WASM_TRAP_ERRORS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_function_call_processed_wasm_trap_errors", + "The number of function calls resulting in wasm trap errors, since starting this node", + &["error_type"], + ) + .unwrap() +}); +pub static FUNCTION_CALL_PROCESSED_HOST_ERRORS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_function_call_processed_host_errors", + "The number of function calls resulting in host errors, since starting this node", + &["error_type"], + ) + .unwrap() +}); +pub static FUNCTION_CALL_PROCESSED_CACHE_ERRORS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_function_call_processed_cache_errors", + "The number of function calls resulting in VM cache errors, since starting this node", + &["error_type"], + ) + .unwrap() +}); +static CHUNK_COMPUTE: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_chunk_compute", + "Compute time by chunk, as a histogram in ms. Reported for all applied chunks, even when not included in a block.", + &["shard_id"], + buckets_for_compute(), + ) + .unwrap() +}); +static CHUNK_TGAS: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_chunk_tgas", + "Tgas burnt by chunk, as a histogram in ms. Reported for all applied chunks, even when not included in a block.", + &["shard_id"], + buckets_for_gas(), + ) + .unwrap() +}); +static CHUNK_LOCAL_RECEIPTS_COMPUTE: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_chunk_local_receipt_compute", + "Compute time for applying local receipts by chunk, as a histogram in ms", + &["shard_id"], + buckets_for_compute(), + ) + .unwrap() +}); +static CHUNK_LOCAL_RECEIPTS_TGAS: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_chunk_local_receipt_tgas", + "Tgas burnt for applying local receipts by chunk, as a histogram in ms", + &["shard_id"], + buckets_for_gas(), + ) + .unwrap() +}); +static CHUNK_DELAYED_RECEIPTS_COMPUTE: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_chunk_delayed_receipt_compute", + "Compute time for applying delayed receipts by chunk, as a histogram in ms", + &["shard_id"], + buckets_for_compute(), + ) + .unwrap() +}); +static CHUNK_DELAYED_RECEIPTS_TGAS: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_chunk_delayed_receipt_tgas", + "Tgas burnt for applying delayed receipts by chunk, as a histogram in ms", + &["shard_id"], + buckets_for_gas(), + ) + .unwrap() +}); +static CHUNK_INC_RECEIPTS_COMPUTE: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_chunk_inc_receipt_compute", + "Compute time for applying incoming receipts by chunk, as a histogram in ms", + &["shard_id"], + buckets_for_compute(), + ) + .unwrap() +}); +static CHUNK_INC_RECEIPTS_TGAS: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_chunk_inc_receipt_tgas", + "Tgas burnt for applying incoming receipts by chunk, as a histogram in ms", + &["shard_id"], + buckets_for_gas(), + ) + .unwrap() +}); +static CHUNK_TX_COMPUTE: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_chunk_tx_compute", + "Compute time for transaction validation by chunk, as a histogram in ms", + &["shard_id"], + Some(vec![0., 50., 100., 200., 300., 400., 500., 600.0]), + ) + .unwrap() +}); +static CHUNK_TX_TGAS: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "unc_chunk_tx_tgas", + "Tgas burnt for transaction validation by chunk, as a histogram", + &["shard_id"], + Some(vec![0., 50., 100., 200., 300., 400., 500.]), + ) + .unwrap() +}); + +/// Buckets used for burned gas in receipts. +/// +/// The maximum possible is 1300 Tgas for a full chunk. +/// But due to the split between types of receipts, it should be quite rare to +/// see more than 1000. +fn buckets_for_gas() -> Option> { + Some(vec![0., 50., 100., 200., 300., 400., 500., 600., 700., 800., 900., 1000., 1100., 1200.]) +} +/// Buckets used for receipt compute time usage, in ms. +/// +/// Ideally the range should be 0-1300 ms. But when we increase the compute cost +/// compared to gas cost, it can be higher than that. +/// +/// Nevertheless, it should be on the rare side to have > 1300 ms and hence it +/// is not worth collecting at a high granularity above that. Here we pick one +/// bucket split at 2000 ms to easily single out heavy undercharging. +fn buckets_for_compute() -> Option> { + Some(vec![ + 0., 50., 100., 200., 300., 400., 500., 600., 700., 800., 900., 1000., 1100., 1200., 1300., + 2000., + ]) +} + +/// Helper struct to collect partial costs of `Runtime::apply` and reporting it +/// atomically. +#[derive(Debug, Default)] +pub struct ApplyMetrics { + accumulated_gas: u64, + accumulated_compute: u64, + tx_compute_usage: u64, + tx_gas: u64, + local_receipts_compute_usage: u64, + local_receipts_gas: u64, + delayed_receipts_compute_usage: u64, + delayed_receipts_gas: u64, + incoming_receipts_compute_usage: u64, + incoming_receipts_gas: u64, +} + +impl ApplyMetrics { + /// Updates the internal accumulated counters and returns the difference to + /// the old counters. + fn update_accumulated(&mut self, gas: u64, compute: u64) -> (u64, u64) { + // Use saturating sub, wrong metrics are better than an overflow panic. + let delta = ( + gas.saturating_sub(self.accumulated_gas), + compute.saturating_sub(self.accumulated_compute), + ); + self.accumulated_gas = gas; + self.accumulated_compute = compute; + delta + } + + pub fn tx_processing_done(&mut self, accumulated_gas: u64, accumulated_compute: u64) { + (self.tx_gas, self.tx_compute_usage) = + self.update_accumulated(accumulated_gas, accumulated_compute); + } + + pub fn local_receipts_done(&mut self, accumulated_gas: u64, accumulated_compute: u64) { + (self.local_receipts_gas, self.local_receipts_compute_usage) = + self.update_accumulated(accumulated_gas, accumulated_compute); + } + + pub fn delayed_receipts_done(&mut self, accumulated_gas: u64, accumulated_compute: u64) { + (self.delayed_receipts_gas, self.delayed_receipts_compute_usage) = + self.update_accumulated(accumulated_gas, accumulated_compute); + } + + pub fn incoming_receipts_done(&mut self, accumulated_gas: u64, accumulated_compute: u64) { + (self.incoming_receipts_gas, self.incoming_receipts_compute_usage) = + self.update_accumulated(accumulated_gas, accumulated_compute); + } + + /// Report statistics + pub fn report(&self, shard_id: &str) { + const TERA: f64 = 1_000_000_000_000_f64; + + CHUNK_TX_TGAS.with_label_values(&[shard_id]).observe(self.tx_gas as f64 / TERA); + CHUNK_TX_COMPUTE + .with_label_values(&[shard_id]) + .observe(self.tx_compute_usage as f64 / TERA); + + CHUNK_LOCAL_RECEIPTS_TGAS + .with_label_values(&[shard_id]) + .observe(self.local_receipts_gas as f64 / TERA); + CHUNK_LOCAL_RECEIPTS_COMPUTE + .with_label_values(&[shard_id]) + .observe(self.local_receipts_compute_usage as f64 / TERA); + + CHUNK_DELAYED_RECEIPTS_TGAS + .with_label_values(&[shard_id]) + .observe(self.delayed_receipts_gas as f64 / TERA); + CHUNK_DELAYED_RECEIPTS_COMPUTE + .with_label_values(&[shard_id]) + .observe(self.delayed_receipts_compute_usage as f64 / TERA); + + CHUNK_INC_RECEIPTS_TGAS + .with_label_values(&[shard_id]) + .observe(self.incoming_receipts_gas as f64 / TERA); + CHUNK_INC_RECEIPTS_COMPUTE + .with_label_values(&[shard_id]) + .observe(self.incoming_receipts_compute_usage as f64 / TERA); + + CHUNK_TGAS.with_label_values(&[shard_id]).observe(self.accumulated_gas as f64 / TERA); + CHUNK_COMPUTE + .with_label_values(&[shard_id]) + .observe(self.accumulated_compute as f64 / TERA); + } +} diff --git a/runtime/runtime/src/prefetch.rs b/runtime/runtime/src/prefetch.rs new file mode 100644 index 000000000..4dd73229e --- /dev/null +++ b/runtime/runtime/src/prefetch.rs @@ -0,0 +1,411 @@ +//! Prefetcher for workload within a block. +//! +//! Prefetching data from the DB ahead of time, in background threads, can +//! reduce I/O overhead to almost zero. To enable that, the prefetcher gets to +//! see all receipts to be applied in a block, before the actions start +//! executing. Based on a receipt's metadata, it may be possible to predict what +//! data it will be accessing. +//! +//! There are two types of prefetching, predictive and non-predictive. +//! Predictive strategies rely on heuristics and are not guaranteed to always +//! fetch useful data. Non-predictive strategies on the contrary only fetch data +//! that is guaranteed to be needed. +//! +//! Predictive prefetching should be used with caution in blockchain context. +//! For one, it does not improve the worst-case. Furthermore, an adversary user +//! can look at the prefetcher's heuristics and force it to mispredict. This +//! means a client is doing (useless) work that is not covered by gas fees and +//! may negatively affect the client's performance. +//! +//! Non-predictive prefetching avoids the problems listed above. But bad +//! implementations could still negatively impact performance in corner cases. +//! For example, if a prefetcher looks too far ahead, it may end up evicting +//! data from LRU caches that is still required. A prefetcher could also use too +//! much of the available I/O bandwidth and therefore slow down the main +//! thread's I/O requests. +//! +//! The design goal is therefore that the prefetcher affects the performance of +//! the main thread as little as possible. This is partially accomplished by +//! treating the main thread's caches as read-only. All prefetched data is +//! inserted in a separate prefetcher landing area. The main thread can check +//! that space and move it to its own LRU caches if it finds the data there. +//! But the prefetcher never directly modifies the main thread's caches. +//! Reading from the LRU cache only makes some values stay longer in the cache. +//! Presumably this is only positive, as the prefetcher knows the value will be +//! used again. +//! +//! Another important measure is to limit the prefetcher in how far ahead it +//! should prefetch, how much bandwidth it consumes, and how much memory it +//! uses. We achieve this with a bounded queue for incoming requests, limiting +//! the number of IO threads, and memory checks before staring new DB requests +//! in the prefetcher. Implementation details for most limits are in +//! `core/store/src/trie/prefetching_trie_storage.rs` + +use unc_o11y::metrics::prometheus; +use unc_o11y::metrics::prometheus::core::GenericCounter; +use unc_primitives::receipt::{Receipt, ReceiptEnum}; +use unc_primitives::transaction::{Action, SignedTransaction}; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::AccountId; +use unc_primitives::types::StateRoot; +use unc_store::{PrefetchApi, PrefetchError, Trie}; +use sha2::Digest; +use tracing::{debug, warn}; + +use crate::metrics; +/// Transaction runtime view of the prefetching subsystem. +pub(crate) struct TriePrefetcher { + prefetch_api: PrefetchApi, + trie_root: StateRoot, + prefetch_enqueued: GenericCounter, + prefetch_queue_full: GenericCounter, +} + +impl TriePrefetcher { + pub(crate) fn new_if_enabled(trie: &Trie) -> Option { + if let Some(caching_storage) = trie.internal_get_storage_as_caching_storage() { + if let Some(prefetch_api) = caching_storage.prefetch_api().clone() { + let trie_root = *trie.get_root(); + let shard_uid = prefetch_api.shard_uid; + let metrics_labels: [&str; 1] = [&shard_uid.shard_id.to_string()]; + return Some(Self { + prefetch_api, + trie_root, + prefetch_enqueued: metrics::PREFETCH_ENQUEUED + .with_label_values(&metrics_labels), + prefetch_queue_full: metrics::PREFETCH_QUEUE_FULL + .with_label_values(&metrics_labels), + }); + } + } + None + } + + /// Starts prefetching data for processing the receipts. + /// + /// Returns an error if prefetching for any receipt fails. + /// The function is not idempotent; in case of failure, prefetching + /// for some receipts may have been initiated. + pub(crate) fn prefetch_receipts_data( + &mut self, + receipts: &[Receipt], + ) -> Result<(), PrefetchError> { + for receipt in receipts.iter() { + if let ReceiptEnum::Action(action_receipt) = &receipt.receipt { + let account_id = receipt.receiver_id.clone(); + + // general-purpose account prefetching + if self.prefetch_api.enable_receipt_prefetching { + let trie_key = TrieKey::Account { account_id: account_id.clone() }; + self.prefetch_trie_key(trie_key)?; + } + + // SWEAT specific argument prefetcher + if self.prefetch_api.sweat_prefetch_receivers.contains(&account_id) + && self.prefetch_api.sweat_prefetch_senders.contains(&receipt.predecessor_id) + { + for action in &action_receipt.actions { + if let Action::FunctionCall(fn_call) = action { + if fn_call.method_name == "record_batch" { + self.prefetch_sweat_record_batch( + account_id.clone(), + &fn_call.args, + )?; + } + } + } + } + } + } + Ok(()) + } + + /// Starts prefetching data for processing the transactions. + /// + /// Returns an error if prefetching for any transaction fails. + /// The function is not idempotent; in case of failure, prefetching + /// for some transactions may have been initiated. + pub(crate) fn prefetch_transactions_data( + &mut self, + transactions: &[SignedTransaction], + ) -> Result<(), PrefetchError> { + if self.prefetch_api.enable_receipt_prefetching { + for t in transactions { + let account_id = t.transaction.signer_id.clone(); + let trie_key = TrieKey::Account { account_id }; + self.prefetch_trie_key(trie_key)?; + + let trie_key = TrieKey::AccessKey { + account_id: t.transaction.signer_id.clone(), + public_key: t.transaction.public_key.clone(), + }; + self.prefetch_trie_key(trie_key)?; + } + } + Ok(()) + } + + /// Removes all queued up prefetch requests and staged data. + /// + /// Note that IO threads currently prefetching a trie key might insert + /// additional trie nodes afterwards. Therefore the staging area is not + /// reliably empty after resetting. + /// This is okay-ish. Resetting between processed chunks does not have to + /// be perfect, as long as we remove each unclaimed value eventually. Doing + /// it one chunk later is also okay. + /// + /// TODO: In the presence of forks, multiple chunks of a shard can be processed + /// at the same time. They share a prefetcher, so they will clean each others + /// data. Handling this is a bit more involved. Failing to do so makes prefetching + /// less effective in those cases but crucially nothing breaks. + pub(crate) fn clear(&self) { + self.prefetch_api.clear_queue(); + self.prefetch_api.clear_data(); + } + + fn prefetch_trie_key(&self, trie_key: TrieKey) -> Result<(), PrefetchError> { + let res = self.prefetch_api.prefetch_trie_key(self.trie_root, trie_key); + match res { + Err(PrefetchError::QueueFull) => { + self.prefetch_queue_full.inc(); + debug!(target: "prefetcher", "I/O scheduler input queue is full, dropping prefetch request"); + } + Err(PrefetchError::QueueDisconnected) => { + // This shouldn't have happened, hence logging warning here + warn!(target: "prefetcher", "I/O scheduler input queue is disconnected, dropping prefetch request"); + } + Ok(()) => self.prefetch_enqueued.inc(), + }; + res + } + + /// Prefetcher specifically tuned for SWEAT record batch + /// + /// Temporary hack, consider removing after merging flat storage, see + /// . + fn prefetch_sweat_record_batch( + &self, + account_id: AccountId, + arg: &[u8], + ) -> Result<(), PrefetchError> { + if let Ok(json) = serde_json::de::from_slice::(arg) { + if json.is_object() { + if let Some(list) = json.get("steps_batch") { + if let Some(list) = list.as_array() { + for tuple in list.iter() { + if let Some(tuple) = tuple.as_array() { + if let Some(user_account) = tuple.first().and_then(|a| a.as_str()) { + let hashed_account = + sha2::Sha256::digest(user_account.as_bytes()).into_iter(); + let mut key = vec![0x74, 0x00]; + key.extend(hashed_account); + let trie_key = TrieKey::ContractData { + account_id: account_id.clone(), + key: key.to_vec(), + }; + unc_o11y::io_trace!(count: "prefetch"); + self.prefetch_trie_key(trie_key)?; + } + } + } + } + } + } + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::TriePrefetcher; + use unc_primitives::{trie_key::TrieKey, types::AccountId}; + use unc_store::test_utils::{create_test_store, test_populate_trie}; + use unc_store::{ShardTries, ShardUId, StateSnapshotConfig, Trie, TrieConfig}; + use std::str::FromStr; + use std::time::{Duration, Instant}; + + #[test] + fn test_basic_prefetch_account() { + let accounts = ["alice.near"]; + // One account <=> a root value and a value. + let expected_prefetched = 2; + check_prefetch_account(&accounts, &accounts, expected_prefetched); + } + + #[test] + fn test_prefetch_multiple_accounts() { + let accounts = [ + "000.alice.near", + "111.alice.near", + "222.alice.near", + "333.alice.near", + "000.bob.near", + "111.bob.near", + "222.bob.near", + "333.bob.near", + ]; + // root is an extension with the prefix for accounts + // that extension leads to a branch with four extensions ("000.","111.","222.","333.") + // each extension leads to a branch with two leafs ("alice.near", "bob.near") + // + // root + // extension + // | + // | + // branch + // "0"-------------"1"-------------"2"-------------"3" + // | | | | + // | | | | + // extension extension extension extension + // "00." "11." "22." "33." + // | | | | + // | | | | + // branch branch branch branch + // "a"--------"b" "a"--------"b" "a"--------"b" "a"--------"b" + // | | | | | | | | + // | | | | | | | | + // | | | | | | | | + // "lice.near" | "lice.near" | "lice.near" | "lice.near" | + // | | | | + // "ob.near" "ob.near" "ob.near" "ob.near" + // + // + // Note: drawing does not show values. Also, upper nibble is always equal + // on branches, so we can just assume bytes instead of nibbles. + + // prefetching a single node results in 2 extensions + 2 branches + 1 leaf + 1 value + let prefetch_accounts = &accounts[..1]; + let expected_prefetched = 6; + check_prefetch_account(&accounts, prefetch_accounts, expected_prefetched); + + // prefetching two distant nodes results in 3 extensions + 3 branches + 2 leafs + 2 values + let prefetch_accounts = &accounts[..2]; + let expected_prefetched = 10; + check_prefetch_account(&accounts, prefetch_accounts, expected_prefetched); + + // prefetching two neighboring nodes results in 2 extensions + 2 branches + 2 leafs + 2 values + let prefetch_accounts = &["000.alice.near", "000.bob.near"]; + let expected_prefetched = 8; + check_prefetch_account(&accounts, prefetch_accounts, expected_prefetched); + } + + #[test] + fn test_prefetch_non_existing_account() { + let existing_accounts = ["alice.near", "bob.near"]; + let non_existing_account = ["charlotta.near"]; + // Most importantly, it should not crash. + // Secondly, it should prefetch the root extension + the first branch. + let expected_prefetched = 2; + check_prefetch_account(&existing_accounts, &non_existing_account, expected_prefetched); + } + + #[track_caller] + fn check_prefetch_account(input: &[&str], prefetch: &[&str], expected_prefetched: usize) { + let input_keys = accounts_to_trie_keys(input); + let prefetch_keys = accounts_to_trie_keys(prefetch); + + let shard_uids = vec![ShardUId::single_shard()]; + let trie_config = TrieConfig { enable_receipt_prefetching: true, ..TrieConfig::default() }; + let store = create_test_store(); + let flat_storage_manager = unc_store::flat::FlatStorageManager::new(store.clone()); + let tries = ShardTries::new( + store, + trie_config, + &shard_uids, + flat_storage_manager, + StateSnapshotConfig::default(), + ); + + let mut kvs = vec![]; + + // insert different values for each account to ensure they have unique hashes + for (i, trie_key) in input_keys.iter().enumerate() { + let storage_key = trie_key.to_vec(); + kvs.push((storage_key.clone(), Some(i.to_string().as_bytes().to_vec()))); + } + let root = test_populate_trie(&tries, &Trie::EMPTY_ROOT, ShardUId::single_shard(), kvs); + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), root); + trie.internal_get_storage_as_caching_storage().unwrap().clear_cache(); + + let prefetcher = + TriePrefetcher::new_if_enabled(&trie).expect("caching storage should have prefetcher"); + let prefetch_api = &prefetcher.prefetch_api; + + assert_eq!(prefetch_api.num_prefetched_and_staged(), 0); + + for trie_key in &prefetch_keys { + _ = prefetcher.prefetch_trie_key(trie_key.clone()); + } + std::thread::yield_now(); + + let wait_work_queue_empty_start = Instant::now(); + while prefetch_api.work_queued() { + std::thread::yield_now(); + // Use timeout to avoid hanging the test + assert!( + wait_work_queue_empty_start.elapsed() < Duration::from_millis(100), + "timeout while waiting for prefetch queue to become empty" + ); + } + + // The queue is empty now but there can still be requests in progress. + // Looking at `Pending` slots in the prefetching area is not sufficient + // here because threads can be in `Trie::lookup` between nodes, at which + // point no slot is reserved for them but they are still doing work. + // + // Solution: `drop(tries)`, which also drops prefetchers. In particular, + // we want to drop the `PrefetchingThreadsHandle` stored in tries. + // `drop(tries)` causes all background threads to stop after they finish + // the current work. It will even join them and wait until all threads + // are done. + // + // Because threads are joined, there is also a possibility this will + // hang forever. To avoid that, we drop in a separate thread. + let dropped = std::sync::atomic::AtomicBool::new(false); + std::thread::scope(|s| { + s.spawn(|| { + drop(tries); + dropped.store(true, std::sync::atomic::Ordering::Release); + }); + let spawned = Instant::now(); + while !dropped.load(std::sync::atomic::Ordering::Acquire) { + std::thread::yield_now(); + // 100ms should be enough to finish pending requests. If not, + // we should check how the prefetcher affects performance. + assert!( + spawned.elapsed() < Duration::from_millis(100), + "timeout while waiting for background threads to terminate" + ); + } + }); + + assert_eq!( + prefetch_api.num_prefetched_and_staged(), + expected_prefetched, + "unexpected number of prefetched values" + ); + + // Read all prefetched values to ensure everything gets removed from the staging area. + for trie_key in &prefetch_keys { + let storage_key = trie_key.to_vec(); + let _value = trie.get(&storage_key).unwrap(); + } + assert_eq!( + prefetch_api.num_prefetched_and_staged(), + 0, + "prefetching staging area not clear after reading all values from main thread" + ); + } + + #[track_caller] + fn accounts_to_trie_keys(input: &[&str]) -> Vec { + input + .iter() + .map(|s| { + let account_id = AccountId::from_str(s).expect("invalid input account id"); + TrieKey::Account { account_id } + }) + .collect() + } +} diff --git a/runtime/runtime/src/receipt_manager.rs b/runtime/runtime/src/receipt_manager.rs new file mode 100644 index 000000000..b3eb3c8c1 --- /dev/null +++ b/runtime/runtime/src/receipt_manager.rs @@ -0,0 +1,498 @@ +use unc_crypto::PublicKey; +use unc_primitives::action::{ + Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, + DeployContractAction, FunctionCallAction, StakeAction, TransferAction, +}; +use unc_primitives::errors::RuntimeError; +use unc_primitives::receipt::DataReceiver; +use unc_primitives_core::account::{AccessKey, AccessKeyPermission, FunctionCallPermission}; +use unc_primitives_core::hash::CryptoHash; +use unc_primitives_core::types::{AccountId, Balance, Gas, GasWeight, Nonce}; +use unc_vm_runner::logic::HostError; +use unc_vm_runner::logic::VMLogicError; + +use crate::config::safe_add_gas; + +/// unc_vm_runner::types is not public. +type ReceiptIndex = u64; + +type ActionReceipts = Vec<(AccountId, ReceiptMetadata)>; + +#[derive(Debug, Clone, PartialEq)] +pub struct ReceiptMetadata { + /// If present, where to route the output data + pub output_data_receivers: Vec, + /// A list of the input data dependencies for this Receipt to process. + /// If all `input_data_ids` for this receipt are delivered to the account + /// that means we have all the `ReceivedData` input which will be than converted to a + /// `PromiseResult::Successful(value)` or `PromiseResult::Failed` + /// depending on `ReceivedData` is `Some(_)` or `None` + pub input_data_ids: Vec, + /// A list of actions to process when all input_data_ids are filled + pub actions: Vec, +} + +#[derive(Default, Clone, PartialEq)] +pub struct ReceiptManager { + pub(super) action_receipts: ActionReceipts, + pub(super) gas_weights: Vec<(FunctionCallActionIndex, GasWeight)>, +} + +/// Indexes the [`ReceiptManager`]'s action receipts and actions. +#[derive(Debug, Clone, Copy, PartialEq)] +pub(super) struct FunctionCallActionIndex { + /// Index of [`ReceiptMetadata`] in the action receipts of [`ReceiptManager`]. + pub(super) receipt_index: usize, + /// Index of the [`Action`] within the [`ReceiptMetadata`]. + pub(super) action_index: usize, +} + +impl ReceiptManager { + pub(super) fn get_receipt_receiver(&self, receipt_index: ReceiptIndex) -> &AccountId { + self.action_receipts + .get(receipt_index as usize) + .map(|(id, _)| id) + .expect("receipt index should be valid for getting receiver") + } + + /// Appends an action and returns the index the action was inserted in the receipt + fn append_action(&mut self, receipt_index: ReceiptIndex, action: Action) -> usize { + let actions = &mut self + .action_receipts + .get_mut(receipt_index as usize) + .expect("receipt index should be present") + .1 + .actions; + + actions.push(action); + + // Return index that action was inserted at + actions.len() - 1 + } + + /// Create a receipt which will be executed after all the receipts identified by + /// `receipt_indices` are complete. + /// + /// If any of the [`ReceiptIndex`]es do not refer to a known receipt, this function will fail + /// with an error. + /// + /// # Arguments + /// + /// * `generate_data_id` - function to generate a data id to connect receipt output to + /// * `receipt_indices` - a list of receipt indices the new receipt is depend on + /// * `receiver_id` - account id of the receiver of the receipt created + pub(super) fn create_receipt( + &mut self, + input_data_ids: Vec, + receipt_indices: Vec, + receiver_id: AccountId, + ) -> Result { + assert_eq!(input_data_ids.len(), receipt_indices.len()); + for (data_id, receipt_index) in input_data_ids.iter().zip(receipt_indices.into_iter()) { + self.action_receipts + .get_mut(receipt_index as usize) + .ok_or_else(|| HostError::InvalidReceiptIndex { receipt_index })? + .1 + .output_data_receivers + .push(DataReceiver { data_id: *data_id, receiver_id: receiver_id.clone() }); + } + + let new_receipt = + ReceiptMetadata { output_data_receivers: vec![], input_data_ids, actions: vec![] }; + let new_receipt_index = self.action_receipts.len() as ReceiptIndex; + self.action_receipts.push((receiver_id, new_receipt)); + Ok(new_receipt_index) + } + + /// Attach the [`CreateAccountAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(super) fn append_action_create_account( + &mut self, + receipt_index: ReceiptIndex, + ) -> Result<(), VMLogicError> { + self.append_action(receipt_index, Action::CreateAccount(CreateAccountAction {})); + Ok(()) + } + + /// Attach the [`DeployContractAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `code` - a Wasm code to attach + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(super) fn append_action_deploy_contract( + &mut self, + receipt_index: ReceiptIndex, + code: Vec, + ) -> Result<(), VMLogicError> { + self.append_action(receipt_index, Action::DeployContract(DeployContractAction { code })); + Ok(()) + } + + /// Attach the [`FunctionCallAction`] action to an existing receipt. + /// + /// `prepaid_gas` and `gas_weight` can either be specified or both. If a `gas_weight` is + /// specified, the action should be allocated gas in + /// [`distribute_unused_gas`](Self::distribute_unused_gas). + /// + /// For more information, see [super::VMLogic::promise_batch_action_function_call_weight]. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `method_name` - a name of the contract method to call + /// * `arguments` - a Wasm code to attach + /// * `attached_deposit` - amount of tokens to transfer with the call + /// * `prepaid_gas` - amount of prepaid gas to attach to the call + /// * `gas_weight` - relative weight of unused gas to distribute to the function call action + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(super) fn append_action_function_call_weight( + &mut self, + receipt_index: ReceiptIndex, + method_name: Vec, + args: Vec, + attached_deposit: Balance, + prepaid_gas: Gas, + gas_weight: GasWeight, + ) -> Result<(), VMLogicError> { + let action_index = self.append_action( + receipt_index, + Action::FunctionCall(Box::new(FunctionCallAction { + method_name: String::from_utf8(method_name) + .map_err(|_| HostError::InvalidMethodName)?, + args, + gas: prepaid_gas, + deposit: attached_deposit, + })), + ); + + if gas_weight.0 > 0 { + self.gas_weights.push(( + FunctionCallActionIndex { receipt_index: receipt_index as usize, action_index }, + gas_weight, + )); + } + + Ok(()) + } + + /// Attach the [`TransferAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `amount` - amount of tokens to transfer + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(super) fn append_action_transfer( + &mut self, + receipt_index: ReceiptIndex, + deposit: Balance, + ) -> Result<(), VMLogicError> { + self.append_action(receipt_index, Action::Transfer(TransferAction { deposit })); + Ok(()) + } + + /// Attach the [`StakeAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `stake` - amount of tokens to stake + /// * `public_key` - a validator public key + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(super) fn append_action_stake( + &mut self, + receipt_index: ReceiptIndex, + stake: Balance, + public_key: PublicKey, + ) { + self.append_action( + receipt_index, + Action::Stake(Box::new(StakeAction { stake, public_key })), + ); + } + + /// Attach the [`AddKeyAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `public_key` - a public key for an access key + /// * `nonce` - a nonce + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(super) fn append_action_add_key_with_full_access( + &mut self, + receipt_index: ReceiptIndex, + public_key: PublicKey, + nonce: Nonce, + ) { + self.append_action( + receipt_index, + Action::AddKey(Box::new(AddKeyAction { + public_key, + access_key: AccessKey { nonce, permission: AccessKeyPermission::FullAccess }, + })), + ); + } + + /// Attach the [`AddKeyAction`] action an existing receipt. + /// + /// The access key associated with the action will have the + /// [`AccessKeyPermission::FunctionCall`] permission scope. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `public_key` - a public key for an access key + /// * `nonce` - a nonce + /// * `allowance` - amount of tokens allowed to spend by this access key + /// * `receiver_id` - a contract witch will be allowed to call with this access key + /// * `method_names` - a list of method names is allowed to call with this access key (empty = any method) + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(super) fn append_action_add_key_with_function_call( + &mut self, + receipt_index: ReceiptIndex, + public_key: PublicKey, + nonce: Nonce, + allowance: Option, + receiver_id: AccountId, + method_names: Vec>, + ) -> Result<(), VMLogicError> { + self.append_action( + receipt_index, + Action::AddKey(Box::new(AddKeyAction { + public_key, + access_key: AccessKey { + nonce, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance, + receiver_id: receiver_id.into(), + method_names: method_names + .into_iter() + .map(|method_name| { + String::from_utf8(method_name) + .map_err(|_| HostError::InvalidMethodName) + }) + .collect::, _>>()?, + }), + }, + })), + ); + Ok(()) + } + + /// Attach the [`DeleteKeyAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `public_key` - a public key for an access key to delete + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(super) fn append_action_delete_key( + &mut self, + receipt_index: ReceiptIndex, + public_key: PublicKey, + ) { + self.append_action( + receipt_index, + Action::DeleteKey(Box::new(DeleteKeyAction { public_key })), + ); + } + + /// Attach the [`DeleteAccountAction`] action to an existing receipt + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `beneficiary_id` - an account id to which the rest of the funds of the removed account will be transferred + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(super) fn append_action_delete_account( + &mut self, + receipt_index: ReceiptIndex, + beneficiary_id: AccountId, + ) -> Result<(), VMLogicError> { + self.append_action( + receipt_index, + Action::DeleteAccount(DeleteAccountAction { beneficiary_id }), + ); + Ok(()) + } + + /// Distribute the provided `gas` between receipts managed by this `ReceiptManager` according + /// to their assigned weights. + /// + /// Returns the amount of gas distributed (either `0` or `unused_gas`.) + pub(super) fn distribute_gas(&mut self, unused_gas: Gas) -> Result { + let ReceiptManager { action_receipts, gas_weights } = self; + let gas_weight_sum: u128 = gas_weights.iter().map(|(_, gv)| u128::from(gv.0)).sum(); + if gas_weight_sum == 0 || unused_gas == 0 { + return Ok(0); + } + let mut distributed = 0u64; + let mut gas_weight_iterator = gas_weights.iter().peekable(); + loop { + let Some((index, weight)) = gas_weight_iterator.next() else { break }; + let FunctionCallActionIndex { receipt_index, action_index } = *index; + let Some(Action::FunctionCall(action)) = action_receipts + .get_mut(receipt_index) + .and_then(|(_, receipt)| receipt.actions.get_mut(action_index)) + else { + panic!( + "Invalid function call index (promise_index={receipt_index}, action_index={action_index})", + ); + }; + let to_assign = (unused_gas as u128 * weight.0 as u128 / gas_weight_sum) as u64; + action.gas = safe_add_gas(action.gas, to_assign)?; + distributed = distributed + .checked_add(to_assign) + .unwrap_or_else(|| panic!("gas computation overflowed")); + if gas_weight_iterator.peek().is_none() { + let remainder = unused_gas.wrapping_sub(distributed); + distributed = distributed + .checked_add(remainder) + .unwrap_or_else(|| panic!("gas computation overflowed")); + action.gas = safe_add_gas(action.gas, remainder)?; + } + } + assert_eq!(unused_gas, distributed); + Ok(distributed) + } +} + +#[cfg(test)] +mod tests { + use unc_primitives::transaction::Action; + use unc_primitives_core::types::{Gas, GasWeight}; + + #[track_caller] + fn function_call_weight_verify(function_calls: &[(Gas, u64, Gas)], after_distribute: bool) { + let mut gas_limit = 10_000_000_000u64; + + // Schedule all function calls + let mut receipt_manager = super::ReceiptManager::default(); + for &(static_gas, gas_weight, _) in function_calls { + let index = receipt_manager + .create_receipt(vec![], vec![], "rick.test".parse().unwrap()) + .unwrap(); + gas_limit = gas_limit.saturating_sub(static_gas); + receipt_manager + .append_action_function_call_weight( + index, + vec![], + vec![], + 0, + static_gas, + GasWeight(gas_weight), + ) + .unwrap(); + } + let accessor: fn(&(Gas, u64, Gas)) -> Gas = if after_distribute { + receipt_manager.distribute_gas(gas_limit).unwrap(); + |(_, _, expected)| *expected + } else { + |(static_gas, _, _)| *static_gas + }; + + // Assert expected amount of gas was associated with the action + let mut function_call_gas = 0; + let mut function_calls_iter = function_calls.iter(); + for (_, receipt) in receipt_manager.action_receipts { + for action in receipt.actions { + if let Action::FunctionCall(function_call_action) = action { + let reference = function_calls_iter.next().unwrap(); + assert_eq!(function_call_action.gas, accessor(reference)); + function_call_gas += function_call_action.gas; + } + } + } + + if after_distribute { + // Verify that all gas was consumed (assumes at least one ratio is provided) + assert_eq!(function_call_gas, 10_000_000_000u64); + } + } + + #[track_caller] + fn function_call_weight_check(function_calls: &[(Gas, u64, Gas)]) { + function_call_weight_verify(function_calls, false); + function_call_weight_verify(function_calls, true); + } + + #[test] + fn function_call_weight_basic_cases_test() { + // Following tests input are in the format (static gas, gas weight, expected gas) + // and the gas limit is `10_000_000_000` + + // Single function call + function_call_weight_check(&[(0, 1, 10_000_000_000)]); + + // Single function with static gas + function_call_weight_check(&[(888, 1, 10_000_000_000)]); + + // Large weight + function_call_weight_check(&[(0, 88888, 10_000_000_000)]); + + // Weight larger than gas limit + function_call_weight_check(&[(0, 11u64.pow(14), 10_000_000_000)]); + + // Split two + function_call_weight_check(&[(0, 3, 6_000_000_000), (0, 2, 4_000_000_000)]); + + // Split two with static gas + function_call_weight_check(&[(1_000_000, 3, 5_998_600_000), (3_000_000, 2, 4_001_400_000)]); + + // Many different gas weights + function_call_weight_check(&[ + (1_000_000, 3, 2_699_800_000), + (3_000_000, 2, 1_802_200_000), + (0, 1, 899_600_000), + (1_000_000_000, 0, 1_000_000_000), + (0, 4, 3_598_400_000), + ]); + + // Weight over u64 bounds + function_call_weight_check(&[(0, u64::MAX, 9_999_999_999), (0, 1000, 1)]); + + // Weight over gas limit with three function calls + function_call_weight_check(&[ + (0, 10_000_000_000, 4_999_999_999), + (0, 1, 0), + (0, 10_000_000_000, 5_000_000_001), + ]); + + // Weights with one zero and one non-zero + function_call_weight_check(&[(0, 0, 0), (0, 1, 10_000_000_000)]) + } +} diff --git a/runtime/runtime/src/state_viewer/errors.rs b/runtime/runtime/src/state_viewer/errors.rs new file mode 100644 index 000000000..b633855c2 --- /dev/null +++ b/runtime/runtime/src/state_viewer/errors.rs @@ -0,0 +1,101 @@ +#[derive(thiserror::Error, Debug)] +pub enum ViewAccountError { + #[error("Account ID \"{requested_account_id}\" is invalid")] + InvalidAccountId { requested_account_id: unc_primitives::types::AccountId }, + #[error("Account ID #{requested_account_id} does not exist")] + AccountDoesNotExist { requested_account_id: unc_primitives::types::AccountId }, + #[error("Internal error: #{error_message}")] + InternalError { error_message: String }, +} + +#[derive(thiserror::Error, Debug)] +pub enum ViewContractCodeError { + #[error("Account ID \"{requested_account_id}\" is invalid")] + InvalidAccountId { requested_account_id: unc_primitives::types::AccountId }, + #[error("Account ID #{requested_account_id} does not exist")] + AccountDoesNotExist { requested_account_id: unc_primitives::types::AccountId }, + #[error("Contract code for contract ID #{contract_account_id} does not exist")] + NoContractCode { contract_account_id: unc_primitives::types::AccountId }, + #[error("Internal error: #{error_message}")] + InternalError { error_message: String }, +} + +#[derive(thiserror::Error, Debug)] +pub enum ViewAccessKeyError { + #[error("Account ID \"{requested_account_id}\" is invalid")] + InvalidAccountId { requested_account_id: unc_primitives::types::AccountId }, + #[error("Access key for public key #{public_key} does not exist")] + AccessKeyDoesNotExist { public_key: unc_crypto::PublicKey }, + #[error("Internal error: #{error_message}")] + InternalError { error_message: String }, +} + +#[derive(thiserror::Error, Debug)] +pub enum ViewStateError { + #[error("Account ID \"{requested_account_id}\" is invalid")] + InvalidAccountId { requested_account_id: unc_primitives::types::AccountId }, + #[error("Account {requested_account_id} does not exist")] + AccountDoesNotExist { requested_account_id: unc_primitives::types::AccountId }, + #[error("The state of {requested_account_id} is too large")] + AccountStateTooLarge { requested_account_id: unc_primitives::types::AccountId }, + #[error("Internal error: #{error_message}")] + InternalError { error_message: String }, +} + +#[derive(thiserror::Error, Debug)] +pub enum CallFunctionError { + #[error("Account ID \"{requested_account_id}\" is invalid")] + InvalidAccountId { requested_account_id: unc_primitives::types::AccountId }, + #[error("Account ID #{requested_account_id} does not exist")] + AccountDoesNotExist { requested_account_id: unc_primitives::types::AccountId }, + #[error("Internal error: #{error_message}")] + InternalError { error_message: String }, + #[error("VM error occurred: #{error_message}")] + VMError { error_message: String }, +} + +impl From for ViewContractCodeError { + fn from(view_account_error: ViewAccountError) -> Self { + match view_account_error { + ViewAccountError::InvalidAccountId { requested_account_id } => { + Self::AccountDoesNotExist { requested_account_id } + } + ViewAccountError::AccountDoesNotExist { requested_account_id } => { + Self::AccountDoesNotExist { requested_account_id } + } + ViewAccountError::InternalError { error_message } => { + Self::InternalError { error_message } + } + } + } +} + +impl From for ViewAccountError { + fn from(storage_error: unc_primitives::errors::StorageError) -> Self { + Self::InternalError { error_message: storage_error.to_string() } + } +} + +impl From for ViewContractCodeError { + fn from(storage_error: unc_primitives::errors::StorageError) -> Self { + Self::InternalError { error_message: storage_error.to_string() } + } +} + +impl From for ViewAccessKeyError { + fn from(storage_error: unc_primitives::errors::StorageError) -> Self { + Self::InternalError { error_message: storage_error.to_string() } + } +} + +impl From for ViewStateError { + fn from(storage_error: unc_primitives::errors::StorageError) -> Self { + Self::InternalError { error_message: storage_error.to_string() } + } +} + +impl From for CallFunctionError { + fn from(storage_error: unc_primitives::errors::StorageError) -> Self { + Self::InternalError { error_message: storage_error.to_string() } + } +} diff --git a/runtime/runtime/src/state_viewer/mod.rs b/runtime/runtime/src/state_viewer/mod.rs new file mode 100644 index 000000000..75e611f33 --- /dev/null +++ b/runtime/runtime/src/state_viewer/mod.rs @@ -0,0 +1,256 @@ +use crate::unc_primitives::version::PROTOCOL_VERSION; +use crate::receipt_manager::ReceiptManager; +use crate::{actions::execute_function_call, ext::RuntimeExt}; +use unc_crypto::{KeyType, PublicKey}; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::account::{AccessKey, Account}; +use unc_primitives::borsh::BorshDeserialize; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::ActionReceipt; +use unc_primitives::runtime::apply_state::ApplyState; +use unc_primitives::runtime::migration_data::{MigrationData, MigrationFlags}; +use unc_primitives::transaction::FunctionCallAction; +use unc_primitives::trie_key::trie_key_parsers; +use unc_primitives::types::{AccountId, EpochInfoProvider, Gas}; +use unc_primitives::views::{StateItem, ViewApplyState, ViewStateResult}; +use unc_primitives_core::config::ViewConfig; +use unc_store::{get_access_key, get_account, get_code, TrieUpdate}; +use unc_vm_runner::logic::ReturnData; +use unc_vm_runner::ContractCode; +use std::{str, sync::Arc, time::Instant}; +use tracing::debug; + +pub mod errors; + +pub struct TrieViewer { + /// Upper bound of the byte size of contract state that is still viewable. None is no limit + state_size_limit: Option, + /// Gas limit used when when handling call_function queries. + max_gas_burnt_view: Gas, +} + +impl Default for TrieViewer { + fn default() -> Self { + let config_store = RuntimeConfigStore::new(None); + let latest_runtime_config = config_store.get_config(PROTOCOL_VERSION); + let max_gas_burnt = latest_runtime_config.wasm_config.limit_config.max_gas_burnt; + Self { state_size_limit: None, max_gas_burnt_view: max_gas_burnt } + } +} + +impl TrieViewer { + pub fn new(state_size_limit: Option, max_gas_burnt_view: Option) -> Self { + let max_gas_burnt_view = + max_gas_burnt_view.unwrap_or_else(|| TrieViewer::default().max_gas_burnt_view); + Self { state_size_limit, max_gas_burnt_view } + } + + pub fn view_account( + &self, + state_update: &TrieUpdate, + account_id: &AccountId, + ) -> Result { + get_account(state_update, account_id)?.ok_or_else(|| { + errors::ViewAccountError::AccountDoesNotExist { + requested_account_id: account_id.clone(), + } + }) + } + + pub fn view_contract_code( + &self, + state_update: &TrieUpdate, + account_id: &AccountId, + ) -> Result { + let account = self.view_account(state_update, account_id)?; + get_code(state_update, account_id, Some(account.code_hash()))?.ok_or_else(|| { + errors::ViewContractCodeError::NoContractCode { + contract_account_id: account_id.clone(), + } + }) + } + + pub fn view_access_key( + &self, + state_update: &TrieUpdate, + account_id: &AccountId, + public_key: &PublicKey, + ) -> Result { + get_access_key(state_update, account_id, public_key)?.ok_or_else(|| { + errors::ViewAccessKeyError::AccessKeyDoesNotExist { public_key: public_key.clone() } + }) + } + + pub fn view_access_keys( + &self, + state_update: &TrieUpdate, + account_id: &AccountId, + ) -> Result, errors::ViewAccessKeyError> { + let prefix = trie_key_parsers::get_raw_prefix_for_access_keys(account_id); + let raw_prefix: &[u8] = prefix.as_ref(); + let access_keys = + state_update + .iter(&prefix)? + .map(|key| { + let key = key?; + let public_key = &key[raw_prefix.len()..]; + let access_key = unc_store::get_access_key_raw(state_update, &key)? + .ok_or_else(|| errors::ViewAccessKeyError::InternalError { + error_message: "Unexpected missing key from iterator".to_string(), + })?; + PublicKey::try_from_slice(public_key) + .map_err(|_| errors::ViewAccessKeyError::InternalError { + error_message: format!( + "Unexpected invalid public key {:?} received from store", + public_key + ), + }) + .map(|key| (key, access_key)) + }) + .collect::, errors::ViewAccessKeyError>>(); + access_keys + } + + pub fn view_state( + &self, + state_update: &TrieUpdate, + account_id: &AccountId, + prefix: &[u8], + include_proof: bool, + ) -> Result { + match get_account(state_update, account_id)? { + Some(account) => { + let code_len = get_code(state_update, account_id, Some(account.code_hash()))? + .map(|c| c.code().len() as u64) + .unwrap_or_default(); + if let Some(limit) = self.state_size_limit { + if account.storage_usage().saturating_sub(code_len) > limit { + return Err(errors::ViewStateError::AccountStateTooLarge { + requested_account_id: account_id.clone(), + }); + } + } + } + None => { + return Err(errors::ViewStateError::AccountDoesNotExist { + requested_account_id: account_id.clone(), + }) + } + }; + + let mut values = vec![]; + let query = trie_key_parsers::get_raw_prefix_for_contract_data(account_id, prefix); + let acc_sep_len = query.len() - prefix.len(); + let mut iter = state_update.trie().iter()?; + iter.remember_visited_nodes(include_proof); + iter.seek_prefix(&query)?; + for item in &mut iter { + let (key, value) = item?; + values.push(StateItem { key: key[acc_sep_len..].to_vec().into(), value: value.into() }); + } + let proof = iter.into_visited_nodes(); + Ok(ViewStateResult { values, proof }) + } + + pub fn call_function( + &self, + mut state_update: TrieUpdate, + view_state: ViewApplyState, + contract_id: &AccountId, + method_name: &str, + args: &[u8], + logs: &mut Vec, + epoch_info_provider: &dyn EpochInfoProvider, + ) -> Result, errors::CallFunctionError> { + let now = Instant::now(); + let root = *state_update.get_root(); + let mut account = get_account(&state_update, contract_id)?.ok_or_else(|| { + errors::CallFunctionError::AccountDoesNotExist { + requested_account_id: contract_id.clone(), + } + })?; + // TODO(#1015): Add ability to pass public key and originator_id + let originator_id = contract_id; + let public_key = PublicKey::empty(KeyType::ED25519); + let empty_hash = CryptoHash::default(); + let mut receipt_manager = ReceiptManager::default(); + let mut runtime_ext = RuntimeExt::new( + &mut state_update, + &mut receipt_manager, + contract_id, + &empty_hash, + &view_state.epoch_id, + &view_state.prev_block_hash, + &view_state.block_hash, + epoch_info_provider, + view_state.current_protocol_version, + ); + let config_store = RuntimeConfigStore::new(None); + let config = config_store.get_config(PROTOCOL_VERSION); + let apply_state = ApplyState { + block_height: view_state.block_height, + // Used for legacy reasons + prev_block_hash: view_state.prev_block_hash, + block_hash: view_state.block_hash, + epoch_id: view_state.epoch_id.clone(), + epoch_height: view_state.epoch_height, + gas_price: 0, + block_timestamp: view_state.block_timestamp, + gas_limit: None, + random_seed: root, + current_protocol_version: view_state.current_protocol_version, + config: config.clone(), + cache: view_state.cache, + is_new_chunk: false, + migration_data: Arc::new(MigrationData::default()), + migration_flags: MigrationFlags::default(), + }; + let action_receipt = ActionReceipt { + signer_id: originator_id.clone(), + signer_public_key: public_key, + gas_price: 0, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: vec![], + }; + let function_call = FunctionCallAction { + method_name: method_name.to_string(), + args: args.to_vec(), + gas: self.max_gas_burnt_view, + deposit: 0, + }; + let outcome = execute_function_call( + &apply_state, + &mut runtime_ext, + &mut account, + originator_id, + &action_receipt, + &[], + &function_call, + &empty_hash, + config, + true, + Some(ViewConfig { max_gas_burnt: self.max_gas_burnt_view }), + ) + .map_err(|e| errors::CallFunctionError::InternalError { error_message: e.to_string() })?; + let elapsed = now.elapsed(); + let time_ms = + (elapsed.as_secs() as f64 / 1_000.0) + f64::from(elapsed.subsec_nanos()) / 1_000_000.0; + let time_str = format!("{:.*}ms", 2, time_ms); + + if let Some(err) = outcome.aborted { + logs.extend(outcome.logs); + let message = format!("wasm execution failed with error: {:?}", err); + debug!(target: "runtime", "(exec time {}) {}", time_str, message); + Err(errors::CallFunctionError::VMError { error_message: message }) + } else { + debug!(target: "runtime", "(exec time {}) result of execution: {:?}", time_str, outcome); + logs.extend(outcome.logs); + let result = match outcome.return_data { + ReturnData::Value(buf) => buf, + ReturnData::ReceiptIndex(_) | ReturnData::None => vec![], + }; + Ok(result) + } + } +} diff --git a/runtime/runtime/src/verifier.rs b/runtime/runtime/src/verifier.rs new file mode 100644 index 000000000..c319186a2 --- /dev/null +++ b/runtime/runtime/src/verifier.rs @@ -0,0 +1,1913 @@ +use crate::config::{total_prepaid_gas, tx_cost, TransactionCost}; +use crate::unc_primitives::account::Account; +use crate::VerificationResult; +use unc_crypto::key_conversion::{is_valid_staking_key, is_valid_challenge_key}; +use unc_parameters::RuntimeConfig; +use unc_primitives::account::AccessKeyPermission; +use unc_primitives::action::delegate::SignedDelegateAction; +use unc_primitives::checked_feature; +use unc_primitives::errors::{ + ActionsValidationError, InvalidAccessKeyError, InvalidTxError, ReceiptValidationError, + RuntimeError, +}; +use unc_primitives::receipt::{ActionReceipt, DataReceipt, Receipt, ReceiptEnum}; +use unc_primitives::transaction::DeleteAccountAction; +use unc_primitives::transaction::{ + Action, AddKeyAction, DeployContractAction, FunctionCallAction, SignedTransaction, StakeAction, + RegisterRsa2048KeysAction, CreateRsa2048ChallengeAction, +}; +use unc_primitives::types::{AccountId, Balance}; +use unc_primitives::types::{BlockHeight, StorageUsage}; +use unc_primitives::version::ProtocolFeature; +use unc_primitives::version::ProtocolVersion; +use unc_store::{ + get_access_key, get_account, set_access_key, set_account, StorageError, TrieUpdate, +}; +use unc_vm_runner::logic::LimitConfig; + +pub const ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT: StorageUsage = 770; + +/// Possible errors when checking whether an account has enough tokens for storage staking +/// Read details of state staking +/// . +pub enum StorageStakingError { + /// An account does not have enough and the additional amount needed for storage staking + LackBalanceForStorageStaking(Balance), + /// Storage consistency error: an account has invalid storage usage or amount or locked amount + StorageError(String), +} + +/// Checks if given account has enough balance for storage stake, and returns: +/// - Ok(()) if account has enough balance or is a zero-balance account +/// - Err(StorageStakingError::LackBalanceForStorageStaking(amount)) if account doesn't have enough and how much need to be added, +/// - Err(StorageStakingError::StorageError(err)) if account has invalid storage usage or amount/locked. +pub fn check_storage_stake( + account: &Account, + runtime_config: &RuntimeConfig, + current_protocol_version: ProtocolVersion, +) -> Result<(), StorageStakingError> { + let required_amount = Balance::from(account.storage_usage()) + .checked_mul(runtime_config.storage_amount_per_byte()) + .ok_or_else(|| { + format!("Account's storage_usage {} overflows multiplication", account.storage_usage()) + }) + .map_err(StorageStakingError::StorageError)?; + let available_amount = account + .amount() + .checked_add(account.locked()) + .ok_or_else(|| { + format!( + "Account's amount {} and locked {} overflow addition", + account.amount(), + account.locked() + ) + }) + .map_err(StorageStakingError::StorageError)?; + if available_amount >= required_amount { + Ok(()) + } else { + if checked_feature!("stable", ZeroBalanceAccount, current_protocol_version) + && is_zero_balance_account(account) + { + return Ok(()); + } + Err(StorageStakingError::LackBalanceForStorageStaking(required_amount - available_amount)) + } +} + +/// Zero Balance Account introduced in NEP 448 https://github.com/near/NEPs/pull/448 +/// An account is a zero balance account if and only if the account uses no more than `ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT` bytes +fn is_zero_balance_account(account: &Account) -> bool { + account.storage_usage() <= ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT +} + +/// Validates the transaction without using the state. It allows any node to validate a +/// transaction before forwarding it to the node that tracks the `signer_id` account. +pub fn validate_transaction( + config: &RuntimeConfig, + gas_price: Balance, + signed_transaction: &SignedTransaction, + verify_signature: bool, + current_protocol_version: ProtocolVersion, +) -> Result { + let transaction = &signed_transaction.transaction; + let signer_id = &transaction.signer_id; + + if verify_signature + && !signed_transaction + .signature + .verify(signed_transaction.get_hash().as_ref(), &transaction.public_key) + { + return Err(InvalidTxError::InvalidSignature.into()); + } + + let transaction_size = signed_transaction.get_size(); + let max_transaction_size = config.wasm_config.limit_config.max_transaction_size; + if transaction_size > max_transaction_size { + return Err(InvalidTxError::TransactionSizeExceeded { + size: transaction_size, + limit: max_transaction_size, + } + .into()); + } + + validate_actions( + &config.wasm_config.limit_config, + &transaction.actions, + current_protocol_version, + ) + .map_err(InvalidTxError::ActionsValidation)?; + + let sender_is_receiver = &transaction.receiver_id == signer_id; + + tx_cost(&config, transaction, gas_price, sender_is_receiver) + .map_err(|_| InvalidTxError::CostOverflow.into()) +} + +/// Verifies the signed transaction on top of given state, charges transaction fees +/// and balances, and updates the state for the used account and access keys. +pub fn verify_and_charge_transaction( + config: &RuntimeConfig, + state_update: &mut TrieUpdate, + gas_price: Balance, + signed_transaction: &SignedTransaction, + verify_signature: bool, + block_height: Option, + current_protocol_version: ProtocolVersion, +) -> Result { + let TransactionCost { gas_burnt, gas_remaining, receipt_gas_price, total_cost, burnt_amount } = + validate_transaction( + config, + gas_price, + signed_transaction, + verify_signature, + current_protocol_version, + )?; + let transaction = &signed_transaction.transaction; + let signer_id = &transaction.signer_id; + + let mut signer = match get_account(state_update, signer_id)? { + Some(signer) => signer, + None => { + return Err(InvalidTxError::SignerDoesNotExist { signer_id: signer_id.clone() }.into()); + } + }; + let mut access_key = match get_access_key(state_update, signer_id, &transaction.public_key)? { + Some(access_key) => access_key, + None => { + return Err(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::AccessKeyNotFound { + account_id: signer_id.clone(), + public_key: transaction.public_key.clone().into(), + }, + ) + .into()); + } + }; + + if transaction.nonce <= access_key.nonce { + return Err(InvalidTxError::InvalidNonce { + tx_nonce: transaction.nonce, + ak_nonce: access_key.nonce, + } + .into()); + } + if checked_feature!("stable", AccessKeyNonceRange, current_protocol_version) { + if let Some(height) = block_height { + let upper_bound = + height * unc_primitives::account::AccessKey::ACCESS_KEY_NONCE_RANGE_MULTIPLIER; + if transaction.nonce >= upper_bound { + return Err(InvalidTxError::NonceTooLarge { + tx_nonce: transaction.nonce, + upper_bound, + } + .into()); + } + } + }; + + access_key.nonce = transaction.nonce; + + signer.set_amount(signer.amount().checked_sub(total_cost).ok_or_else(|| { + InvalidTxError::NotEnoughBalance { + signer_id: signer_id.clone(), + balance: signer.amount(), + cost: total_cost, + } + })?); + + if let AccessKeyPermission::FunctionCall(ref mut function_call_permission) = + access_key.permission + { + if let Some(ref mut allowance) = function_call_permission.allowance { + *allowance = allowance.checked_sub(total_cost).ok_or_else(|| { + InvalidTxError::InvalidAccessKeyError(InvalidAccessKeyError::NotEnoughAllowance { + account_id: signer_id.clone(), + public_key: transaction.public_key.clone().into(), + allowance: *allowance, + cost: total_cost, + }) + })?; + } + } + + match check_storage_stake(&signer, config, current_protocol_version) { + Ok(()) => {} + Err(StorageStakingError::LackBalanceForStorageStaking(amount)) => { + return Err(InvalidTxError::LackBalanceForState { + signer_id: signer_id.clone(), + amount, + } + .into()) + } + Err(StorageStakingError::StorageError(err)) => { + return Err(RuntimeError::StorageError(StorageError::StorageInconsistentState(err))) + } + }; + + if let AccessKeyPermission::FunctionCall(ref function_call_permission) = access_key.permission { + if transaction.actions.len() != 1 { + return Err(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::RequiresFullAccess, + ) + .into()); + } + if let Some(Action::FunctionCall(ref function_call)) = transaction.actions.get(0) { + if function_call.deposit > 0 { + return Err(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::DepositWithFunctionCall, + ) + .into()); + } + if transaction.receiver_id != function_call_permission.receiver_id { + return Err(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::ReceiverMismatch { + tx_receiver: transaction.receiver_id.clone(), + ak_receiver: function_call_permission.receiver_id.clone(), + }, + ) + .into()); + } + if !function_call_permission.method_names.is_empty() + && function_call_permission + .method_names + .iter() + .all(|method_name| &function_call.method_name != method_name) + { + return Err(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::MethodNameMismatch { + method_name: function_call.method_name.clone(), + }, + ) + .into()); + } + } else { + return Err(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::RequiresFullAccess, + ) + .into()); + } + }; + + set_access_key(state_update, signer_id.clone(), transaction.public_key.clone(), &access_key); + set_account(state_update, signer_id.clone(), &signer); + + Ok(VerificationResult { gas_burnt, gas_remaining, receipt_gas_price, burnt_amount }) +} + +/// Validates a given receipt. Checks validity of the Action or Data receipt. +pub(crate) fn validate_receipt( + limit_config: &LimitConfig, + receipt: &Receipt, + current_protocol_version: ProtocolVersion, +) -> Result<(), ReceiptValidationError> { + // We retain these checks here as to maintain backwards compatibility + // with AccountId validation since we illegally parse an AccountId + // in unc-vm-logic/logic.rs#fn(VMLogic::read_and_parse_account_id) + AccountId::validate(receipt.predecessor_id.as_ref()).map_err(|_| { + ReceiptValidationError::InvalidPredecessorId { + account_id: receipt.predecessor_id.to_string(), + } + })?; + AccountId::validate(receipt.receiver_id.as_ref()).map_err(|_| { + ReceiptValidationError::InvalidReceiverId { account_id: receipt.receiver_id.to_string() } + })?; + + match &receipt.receipt { + ReceiptEnum::Action(action_receipt) => { + validate_action_receipt(limit_config, action_receipt, current_protocol_version) + } + ReceiptEnum::Data(data_receipt) => validate_data_receipt(limit_config, data_receipt), + } +} + +/// Validates given ActionReceipt. Checks validity of the number of input data dependencies and all actions. +fn validate_action_receipt( + limit_config: &LimitConfig, + receipt: &ActionReceipt, + current_protocol_version: ProtocolVersion, +) -> Result<(), ReceiptValidationError> { + if receipt.input_data_ids.len() as u64 > limit_config.max_number_input_data_dependencies { + return Err(ReceiptValidationError::NumberInputDataDependenciesExceeded { + number_of_input_data_dependencies: receipt.input_data_ids.len() as u64, + limit: limit_config.max_number_input_data_dependencies, + }); + } + validate_actions(limit_config, &receipt.actions, current_protocol_version) + .map_err(ReceiptValidationError::ActionsValidation) +} + +/// Validates given data receipt. Checks validity of the length of the returned data. +fn validate_data_receipt( + limit_config: &LimitConfig, + receipt: &DataReceipt, +) -> Result<(), ReceiptValidationError> { + let data_len = receipt.data.as_ref().map(|data| data.len()).unwrap_or(0); + if data_len as u64 > limit_config.max_length_returned_data { + return Err(ReceiptValidationError::ReturnedValueLengthExceeded { + length: data_len as u64, + limit: limit_config.max_length_returned_data, + }); + } + Ok(()) +} + +/// Validates given actions: +/// +/// - Checks limits if applicable. +/// - Checks that the total number of actions doesn't exceed the limit. +/// - Checks that there not other action if Action::Delegate is present. +/// - Validates each individual action. +/// - Checks that the total prepaid gas doesn't exceed the limit. +pub(crate) fn validate_actions( + limit_config: &LimitConfig, + actions: &[Action], + current_protocol_version: ProtocolVersion, +) -> Result<(), ActionsValidationError> { + if actions.len() as u64 > limit_config.max_actions_per_receipt { + return Err(ActionsValidationError::TotalNumberOfActionsExceeded { + total_number_of_actions: actions.len() as u64, + limit: limit_config.max_actions_per_receipt, + }); + } + + let mut found_delegate_action = false; + let mut iter = actions.iter().peekable(); + while let Some(action) = iter.next() { + if let Action::DeleteAccount(_) = action { + if iter.peek().is_some() { + return Err(ActionsValidationError::DeleteActionMustBeFinal); + } + } else { + if let Action::Delegate(_) = action { + if !checked_feature!("stable", DelegateAction, current_protocol_version) { + return Err(ActionsValidationError::UnsupportedProtocolFeature { + protocol_feature: String::from("DelegateAction"), + version: ProtocolFeature::DelegateAction.protocol_version(), + }); + } + if found_delegate_action { + return Err(ActionsValidationError::DelegateActionMustBeOnlyOne); + } + found_delegate_action = true; + } + } + validate_action(limit_config, action, current_protocol_version)?; + } + + let total_prepaid_gas = + total_prepaid_gas(actions).map_err(|_| ActionsValidationError::IntegerOverflow)?; + if total_prepaid_gas > limit_config.max_total_prepaid_gas { + return Err(ActionsValidationError::TotalPrepaidGasExceeded { + total_prepaid_gas, + limit: limit_config.max_total_prepaid_gas, + }); + } + + Ok(()) +} + +/// Validates a single given action. Checks limits if applicable. +pub fn validate_action( + limit_config: &LimitConfig, + action: &Action, + current_protocol_version: ProtocolVersion, +) -> Result<(), ActionsValidationError> { + match action { + Action::CreateAccount(_) => Ok(()), + Action::DeployContract(a) => validate_deploy_contract_action(limit_config, a), + Action::FunctionCall(a) => validate_function_call_action(limit_config, a), + Action::Transfer(_) => Ok(()), + Action::Stake(a) => validate_stake_action(a), + Action::AddKey(a) => validate_add_key_action(limit_config, a), + Action::DeleteKey(_) => Ok(()), + Action::DeleteAccount(a) => validate_delete_action(a), + Action::Delegate(a) => validate_delegate_action(limit_config, a, current_protocol_version), + Action::RegisterRsa2048Keys(a) => validate_register_rsa2048_keys_action(limit_config, a), + Action::CreateRsa2048Challenge(a) => validate_create_rsa2048_challenge_action(a), + } +} + +fn validate_delegate_action( + limit_config: &LimitConfig, + signed_delegate_action: &SignedDelegateAction, + current_protocol_version: ProtocolVersion, +) -> Result<(), ActionsValidationError> { + let actions = signed_delegate_action.delegate_action.get_actions(); + validate_actions(limit_config, &actions, current_protocol_version)?; + Ok(()) +} + +/// Validates `DeployContractAction`. Checks that the given contract size doesn't exceed the limit. +fn validate_deploy_contract_action( + limit_config: &LimitConfig, + action: &DeployContractAction, +) -> Result<(), ActionsValidationError> { + if action.code.len() as u64 > limit_config.max_contract_size { + return Err(ActionsValidationError::ContractSizeExceeded { + size: action.code.len() as u64, + limit: limit_config.max_contract_size, + }); + } + + Ok(()) +} + +/// Validates `FunctionCallAction`. Checks that the method name length doesn't exceed the limit and +/// the length of the arguments doesn't exceed the limit. +fn validate_function_call_action( + limit_config: &LimitConfig, + action: &FunctionCallAction, +) -> Result<(), ActionsValidationError> { + if action.gas == 0 { + return Err(ActionsValidationError::FunctionCallZeroAttachedGas); + } + + if action.method_name.len() as u64 > limit_config.max_length_method_name { + return Err(ActionsValidationError::FunctionCallMethodNameLengthExceeded { + length: action.method_name.len() as u64, + limit: limit_config.max_length_method_name, + }); + } + + if action.args.len() as u64 > limit_config.max_arguments_length { + return Err(ActionsValidationError::FunctionCallArgumentsLengthExceeded { + length: action.args.len() as u64, + limit: limit_config.max_arguments_length, + }); + } + + Ok(()) +} + +/// Validates `StakeAction`. Checks that the `public_key` is a valid staking key. +fn validate_stake_action(action: &StakeAction) -> Result<(), ActionsValidationError> { + if !is_valid_staking_key(&action.public_key) { + return Err(ActionsValidationError::UnsuitableStakingKey { + public_key: Box::new(action.public_key.clone()), + }); + } + + Ok(()) +} + +/// Validates `AddKeyAction`. If the access key permission is `FunctionCall`, checks that the +/// total number of bytes of the method names doesn't exceed the limit and +/// every method name length doesn't exceed the limit. +fn validate_add_key_action( + limit_config: &LimitConfig, + action: &AddKeyAction, +) -> Result<(), ActionsValidationError> { + if let AccessKeyPermission::FunctionCall(fc) = &action.access_key.permission { + // Check whether `receiver_id` is a valid account_id. Historically, we + // allowed arbitrary strings there! + match limit_config.account_id_validity_rules_version { + unc_primitives_core::config::AccountIdValidityRulesVersion::V0 => (), + unc_primitives_core::config::AccountIdValidityRulesVersion::V1 => { + if let Err(_) = fc.receiver_id.parse::() { + return Err(ActionsValidationError::InvalidAccountId { + account_id: truncate_string(&fc.receiver_id, AccountId::MAX_LEN * 2), + }); + } + } + } + + // Checking method name length limits + let mut total_number_of_bytes = 0; + for method_name in &fc.method_names { + let length = method_name.len() as u64; + if length > limit_config.max_length_method_name { + return Err(ActionsValidationError::AddKeyMethodNameLengthExceeded { + length, + limit: limit_config.max_length_method_name, + }); + } + // Adding terminating character to the total number of bytes + total_number_of_bytes += length + 1; + } + if total_number_of_bytes > limit_config.max_number_bytes_method_names { + return Err(ActionsValidationError::AddKeyMethodNamesNumberOfBytesExceeded { + total_number_of_bytes, + limit: limit_config.max_number_bytes_method_names, + }); + } + } + + Ok(()) +} + +fn validate_register_rsa2048_keys_action( + _limit_config: &LimitConfig, + _action: &RegisterRsa2048KeysAction, +) -> Result<(), ActionsValidationError> { + Ok(()) +} + +fn validate_create_rsa2048_challenge_action(action: &CreateRsa2048ChallengeAction) -> Result<(), ActionsValidationError> { + if !is_valid_challenge_key(&action.public_key) { + return Err(ActionsValidationError::UnsuitableStakingKey { + public_key: Box::new(action.public_key.clone()), + }); + } + + Ok(()) +} + +/// Validates `DeleteAction`. +/// +/// Checks that the `beneficiary_id` is a valid account ID. +fn validate_delete_action(action: &DeleteAccountAction) -> Result<(), ActionsValidationError> { + if AccountId::validate(action.beneficiary_id.as_str()).is_err() { + return Err(ActionsValidationError::InvalidAccountId { + account_id: action.beneficiary_id.to_string(), + }); + } + + Ok(()) +} + +fn truncate_string(s: &str, limit: usize) -> String { + for i in (0..=limit).rev() { + if let Some(s) = s.get(..i) { + return s.to_string(); + } + } + unreachable!() +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use unc_crypto::{InMemorySigner, KeyType, PublicKey, Signature, Signer}; + use unc_primitives::account::{AccessKey, FunctionCallPermission}; + use unc_primitives::action::delegate::{DelegateAction, NonDelegateAction}; + use unc_primitives::hash::{hash, CryptoHash}; + use unc_primitives::test_utils::account_new; + use unc_primitives::transaction::{ + CreateAccountAction, DeleteAccountAction, DeleteKeyAction, StakeAction, TransferAction, + }; + use unc_primitives::types::{AccountId, Balance, MerkleHash, StateChangeCause}; + use unc_primitives::version::PROTOCOL_VERSION; + use unc_store::test_utils::TestTriesBuilder; + use testlib::runtime_utils::{alice_account, bob_account, eve_dot_alice_account}; + + use crate::unc_primitives::shard_layout::ShardUId; + + use super::*; + use crate::unc_primitives::trie_key::TrieKey; + use unc_store::{set, set_code}; + use unc_vm_runner::ContractCode; + + /// Initial balance used in tests. + const TESTING_INIT_BALANCE: Balance = 1_000_000_000 * UNC_BASE; + + /// One NEAR, divisible by 10^24. + const UNC_BASE: Balance = 1_000_000_000_000_000_000_000_000; + + fn test_limit_config() -> LimitConfig { + let store = unc_parameters::RuntimeConfigStore::test(); + store.get_config(PROTOCOL_VERSION).wasm_config.limit_config.clone() + } + + fn setup_common( + initial_balance: Balance, + initial_locked: Balance, + access_key: Option, + ) -> (Arc, TrieUpdate, Balance) { + let access_keys = if let Some(key) = access_key { vec![key] } else { vec![] }; + setup_accounts(vec![( + alice_account(), + initial_balance, + initial_locked, + access_keys, + false, + false, + )]) + } + + fn setup_accounts( + // two bools: first one is whether the account has a contract, second one is whether the + // account has data + accounts: Vec<(AccountId, Balance, Balance, Vec, bool, bool)>, + ) -> (Arc, TrieUpdate, Balance) { + let tries = TestTriesBuilder::new().build(); + let root = MerkleHash::default(); + + let account_id = alice_account(); + let signer = Arc::new(InMemorySigner::from_seed( + account_id.clone(), + KeyType::ED25519, + account_id.as_ref(), + )); + + let mut initial_state = tries.new_trie_update(ShardUId::single_shard(), root); + for (account_id, initial_balance, initial_locked, access_keys, has_contract, has_data) in + accounts + { + let mut initial_account = account_new(initial_balance, CryptoHash::default()); + initial_account.set_locked(initial_locked); + let mut key_count = 0; + for access_key in access_keys { + let public_key = if key_count == 0 { + signer.public_key() + } else { + PublicKey::from_seed(KeyType::ED25519, format!("{}", key_count).as_str()) + }; + set_access_key( + &mut initial_state, + account_id.clone(), + public_key.clone(), + &access_key, + ); + initial_account.set_storage_usage( + initial_account + .storage_usage() + .checked_add( + borsh::object_length(&public_key).unwrap() as u64 + + borsh::object_length(&access_key).unwrap() as u64 + + 40, // storage_config.num_extra_bytes_record, + ) + .unwrap(), + ); + key_count += 1; + } + if has_contract { + let code = vec![0; 100]; + let code_hash = hash(&code); + set_code( + &mut initial_state, + account_id.clone(), + &ContractCode::new(code.clone(), Some(code_hash)), + ); + initial_account.set_code_hash(code_hash); + initial_account.set_storage_usage( + initial_account.storage_usage().checked_add(code.len() as u64).unwrap(), + ); + } + if has_data { + let key = b"test".to_vec(); + let value = vec![0u8; 100]; + set( + &mut initial_state, + TrieKey::ContractData { account_id: account_id.clone(), key: key.clone() }, + &value, + ); + initial_account.set_storage_usage( + initial_account + .storage_usage() + .checked_add(key.len() as u64 + value.len() as u64 + 40) + .unwrap(), + ); + } + set_account(&mut initial_state, account_id.clone(), &initial_account); + } + initial_state.commit(StateChangeCause::InitialState); + let trie_changes = initial_state.finalize().unwrap().1; + let mut store_update = tries.store_update(); + let root = tries.apply_all(&trie_changes, ShardUId::single_shard(), &mut store_update); + store_update.commit().unwrap(); + + (signer, tries.new_trie_update(ShardUId::single_shard(), root), 100) + } + + fn assert_err_both_validations( + config: &RuntimeConfig, + state_update: &mut TrieUpdate, + gas_price: Balance, + signed_transaction: &SignedTransaction, + expected_err: RuntimeError, + ) { + assert_eq!( + validate_transaction(config, gas_price, signed_transaction, true, PROTOCOL_VERSION) + .expect_err("expected an error"), + expected_err, + ); + assert_eq!( + verify_and_charge_transaction( + config, + state_update, + gas_price, + signed_transaction, + true, + None, + PROTOCOL_VERSION, + ) + .expect_err("expected an error"), + expected_err, + ); + } + + mod zero_balance_account_tests { + use crate::unc_primitives::account::id::AccountId; + use crate::unc_primitives::account::{ + AccessKeyPermission, Account, FunctionCallPermission, + }; + use crate::verifier::tests::{setup_accounts, TESTING_INIT_BALANCE}; + use crate::verifier::{is_zero_balance_account, ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT}; + use unc_primitives::account::AccessKey; + use unc_store::{get_account, TrieUpdate}; + use testlib::runtime_utils::{alice_account, bob_account}; + + fn set_up_test_account( + account_id: &AccountId, + num_full_access_keys: u64, + num_function_call_access_keys: u64, + ) -> (Account, TrieUpdate) { + let mut access_keys = vec![]; + for _ in 0..num_full_access_keys { + access_keys.push(AccessKey::full_access()); + } + for _ in 0..num_function_call_access_keys { + let access_key = AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(100), + receiver_id: "a".repeat(64), + method_names: vec![], + }), + }; + access_keys.push(access_key); + } + let (_, state_update, _) = setup_accounts(vec![( + account_id.clone(), + TESTING_INIT_BALANCE, + 0, + access_keys, + false, + false, + )]); + let account = get_account(&state_update, account_id).unwrap().unwrap(); + (account, state_update) + } + + /// Testing all combination of access keys in this test to make sure that an account + /// is zero balance only if it uses <= `ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT` bytes in storage + #[test] + fn test_zero_balance_account_with_keys() { + for num_full_access_key in 0..10 { + for num_function_call_access_key in 0..10 { + let account_id: AccountId = format!( + "alice{}.near", + num_full_access_key * 1000 + num_function_call_access_key + ) + .parse() + .unwrap(); + let (account, _) = set_up_test_account( + &account_id, + num_full_access_key, + num_function_call_access_key, + ); + let res = is_zero_balance_account(&account); + assert_eq!( + res, + num_full_access_key * 82 + + num_function_call_access_key * 171 + + std::mem::size_of::() as u64 + <= ZERO_BALANCE_ACCOUNT_STORAGE_LIMIT + ); + } + } + } + + /// A single function call access key that is too large (due to too many method names) + #[test] + fn test_zero_balance_account_with_invalid_access_key() { + let account_id = alice_account(); + let method_names = + (0..30).map(|i| format!("long_method_name_{}", i)).collect::>(); + let (_, state_update, _) = setup_accounts(vec![( + account_id.clone(), + 0, + 0, + vec![AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(100), + receiver_id: bob_account().into(), + method_names: method_names, + }), + }], + false, + false, + )]); + let account = get_account(&state_update, &account_id).unwrap().unwrap(); + assert!(!is_zero_balance_account(&account)); + } + } + + // Transactions + + #[test] + fn test_validate_transaction_valid() { + let config = RuntimeConfig::test(); + let (signer, mut state_update, gas_price) = + setup_common(TESTING_INIT_BALANCE, 0, Some(AccessKey::full_access())); + + let deposit = 100; + let transaction = SignedTransaction::send_money( + 1, + alice_account(), + bob_account(), + &*signer, + deposit, + CryptoHash::default(), + ); + validate_transaction(&config, gas_price, &transaction, true, PROTOCOL_VERSION) + .expect("valid transaction"); + let verification_result = verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &transaction, + true, + None, + PROTOCOL_VERSION, + ) + .expect("valid transaction"); + // Should not be free. Burning for sending + assert!(verification_result.gas_burnt > 0); + // All burned gas goes to the validators at current gas price + assert_eq!( + verification_result.burnt_amount, + Balance::from(verification_result.gas_burnt) * gas_price + ); + + let account = get_account(&state_update, &alice_account()).unwrap().unwrap(); + // Balance is decreased by the (TX fees + transfer balance). + assert_eq!( + account.amount(), + TESTING_INIT_BALANCE + - Balance::from(verification_result.gas_remaining) + * verification_result.receipt_gas_price + - verification_result.burnt_amount + - deposit + ); + + let access_key = + get_access_key(&state_update, &alice_account(), &signer.public_key()).unwrap().unwrap(); + assert_eq!(access_key.nonce, 1); + } + + #[test] + fn test_validate_transaction_invalid_signature() { + let config = RuntimeConfig::test(); + let (signer, mut state_update, gas_price) = + setup_common(TESTING_INIT_BALANCE, 0, Some(AccessKey::full_access())); + + let mut tx = SignedTransaction::send_money( + 1, + alice_account(), + bob_account(), + &*signer, + 100, + CryptoHash::default(), + ); + tx.signature = signer.sign(CryptoHash::default().as_ref()); + + assert_err_both_validations( + &config, + &mut state_update, + gas_price, + &tx, + RuntimeError::InvalidTxError(InvalidTxError::InvalidSignature), + ); + } + + #[test] + fn test_validate_transaction_invalid_access_key_not_found() { + let config = RuntimeConfig::test(); + let (bad_signer, mut state_update, gas_price) = setup_common(TESTING_INIT_BALANCE, 0, None); + + assert_eq!( + verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &SignedTransaction::send_money( + 1, + alice_account(), + bob_account(), + &*bad_signer, + 100, + CryptoHash::default(), + ), + false, + None, + PROTOCOL_VERSION, + ) + .expect_err("expected an error"), + RuntimeError::InvalidTxError(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::AccessKeyNotFound { + account_id: alice_account(), + public_key: bad_signer.public_key().into(), + }, + )), + ); + } + + #[test] + fn test_validate_transaction_invalid_bad_action() { + let mut config = RuntimeConfig::test(); + let (signer, mut state_update, gas_price) = + setup_common(TESTING_INIT_BALANCE, 0, Some(AccessKey::full_access())); + + config.wasm_config.limit_config.max_total_prepaid_gas = 100; + + assert_err_both_validations( + &config, + &mut state_update, + gas_price, + &SignedTransaction::from_actions( + 1, + alice_account(), + bob_account(), + &*signer, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "hello".to_string(), + args: b"abc".to_vec(), + gas: 200, + deposit: 0, + }))], + CryptoHash::default(), + ), + RuntimeError::InvalidTxError(InvalidTxError::ActionsValidation( + ActionsValidationError::TotalPrepaidGasExceeded { + total_prepaid_gas: 200, + limit: 100, + }, + )), + ); + } + + #[test] + fn test_validate_transaction_invalid_bad_signer() { + let config = RuntimeConfig::test(); + let (signer, mut state_update, gas_price) = + setup_common(TESTING_INIT_BALANCE, 0, Some(AccessKey::full_access())); + + assert_eq!( + verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &SignedTransaction::send_money( + 1, + bob_account(), + alice_account(), + &*signer, + 100, + CryptoHash::default(), + ), + true, + None, + PROTOCOL_VERSION, + ) + .expect_err("expected an error"), + RuntimeError::InvalidTxError(InvalidTxError::SignerDoesNotExist { + signer_id: bob_account() + }), + ); + } + + #[test] + fn test_validate_transaction_invalid_bad_nonce() { + let config = RuntimeConfig::test(); + let (signer, mut state_update, gas_price) = setup_common( + TESTING_INIT_BALANCE, + 0, + Some(AccessKey { nonce: 2, permission: AccessKeyPermission::FullAccess }), + ); + + assert_eq!( + verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &SignedTransaction::send_money( + 1, + alice_account(), + bob_account(), + &*signer, + 100, + CryptoHash::default(), + ), + true, + None, + PROTOCOL_VERSION, + ) + .expect_err("expected an error"), + RuntimeError::InvalidTxError(InvalidTxError::InvalidNonce { tx_nonce: 1, ak_nonce: 2 }), + ); + } + + #[test] + fn test_validate_transaction_invalid_balance_overflow() { + let config = RuntimeConfig::test(); + let (signer, mut state_update, gas_price) = + setup_common(TESTING_INIT_BALANCE, 0, Some(AccessKey::full_access())); + + assert_err_both_validations( + &config, + &mut state_update, + gas_price, + &SignedTransaction::send_money( + 1, + alice_account(), + bob_account(), + &*signer, + u128::max_value(), + CryptoHash::default(), + ), + RuntimeError::InvalidTxError(InvalidTxError::CostOverflow), + ); + } + + #[test] + fn test_validate_transaction_invalid_not_enough_balance() { + let config = RuntimeConfig::test(); + let (signer, mut state_update, gas_price) = + setup_common(TESTING_INIT_BALANCE, 0, Some(AccessKey::full_access())); + + let err = verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &SignedTransaction::send_money( + 1, + alice_account(), + bob_account(), + &*signer, + TESTING_INIT_BALANCE, + CryptoHash::default(), + ), + true, + None, + PROTOCOL_VERSION, + ) + .expect_err("expected an error"); + if let RuntimeError::InvalidTxError(InvalidTxError::NotEnoughBalance { + signer_id, + balance, + cost, + }) = err + { + assert_eq!(signer_id, alice_account()); + assert_eq!(balance, TESTING_INIT_BALANCE); + assert!(cost > balance); + } else { + panic!("Incorrect error"); + } + } + + #[test] + fn test_validate_transaction_invalid_not_enough_allowance() { + let config = RuntimeConfig::test(); + let (signer, mut state_update, gas_price) = setup_common( + TESTING_INIT_BALANCE, + 0, + Some(AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(100), + receiver_id: bob_account().into(), + method_names: vec![], + }), + }), + ); + + let err = verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &SignedTransaction::from_actions( + 1, + alice_account(), + bob_account(), + &*signer, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "hello".to_string(), + args: b"abc".to_vec(), + gas: 300, + deposit: 0, + }))], + CryptoHash::default(), + ), + true, + None, + PROTOCOL_VERSION, + ) + .expect_err("expected an error"); + if let RuntimeError::InvalidTxError(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::NotEnoughAllowance { account_id, public_key, allowance, cost }, + )) = err + { + assert_eq!(account_id, alice_account()); + assert_eq!(*public_key, signer.public_key()); + assert_eq!(allowance, 100); + assert!(cost > allowance); + } else { + panic!("Incorrect error"); + } + } + + /// Setup: account has 1B yoctoN and is 180 bytes. Storage requirement is 1M per byte. + /// Test that such account can not send 950M yoctoN out as that will leave it under storage requirements. + /// If zero balance account is enabled, however, the transaction should succeed + #[test] + fn test_validate_transaction_invalid_low_balance() { + let mut config = RuntimeConfig::free(); + config.fees.storage_usage_config.storage_amount_per_byte = 10_000_000; + let initial_balance = 1_000_000_000; + let transfer_amount = 950_000_000; + let (signer, mut state_update, gas_price) = + setup_common(initial_balance, 0, Some(AccessKey::full_access())); + + let res = verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &SignedTransaction::send_money( + 1, + alice_account(), + bob_account(), + &*signer, + transfer_amount, + CryptoHash::default(), + ), + true, + None, + PROTOCOL_VERSION, + ); + let verification_result = res.unwrap(); + assert_eq!(verification_result.gas_burnt, 0); + assert_eq!(verification_result.gas_remaining, 0); + assert_eq!(verification_result.burnt_amount, 0); + } + + #[test] + fn test_validate_transaction_invalid_low_balance_many_keys() { + let mut config = RuntimeConfig::free(); + config.fees.storage_usage_config.storage_amount_per_byte = 10_000_000; + let initial_balance = 1_000_000_000; + let transfer_amount = 950_000_000; + let account_id = alice_account(); + let access_keys = vec![AccessKey::full_access(); 10]; + let (signer, mut state_update, gas_price) = setup_accounts(vec![( + account_id.clone(), + initial_balance, + 0, + access_keys, + false, + false, + )]); + + let res = verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &SignedTransaction::send_money( + 1, + account_id.clone(), + bob_account(), + &*signer, + transfer_amount, + CryptoHash::default(), + ), + true, + None, + PROTOCOL_VERSION, + ) + .expect_err("expected an error"); + let account = get_account(&state_update, &account_id).unwrap().unwrap(); + + assert_eq!( + res, + RuntimeError::InvalidTxError(InvalidTxError::LackBalanceForState { + signer_id: account_id, + amount: Balance::from(account.storage_usage()) * config.storage_amount_per_byte() + - (initial_balance - transfer_amount) + }) + ); + } + + #[test] + fn test_validate_transaction_invalid_actions_for_function_call() { + let config = RuntimeConfig::test(); + let (signer, mut state_update, gas_price) = setup_common( + TESTING_INIT_BALANCE, + 0, + Some(AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: bob_account().into(), + method_names: vec![], + }), + }), + ); + + assert_eq!( + verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &SignedTransaction::from_actions( + 1, + alice_account(), + bob_account(), + &*signer, + vec![ + Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "hello".to_string(), + args: b"abc".to_vec(), + gas: 100, + deposit: 0, + })), + Action::CreateAccount(CreateAccountAction {}) + ], + CryptoHash::default(), + ), + true, + None, + PROTOCOL_VERSION, + ) + .expect_err("expected an error"), + RuntimeError::InvalidTxError(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::RequiresFullAccess + )), + ); + + assert_eq!( + verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &SignedTransaction::from_actions( + 1, + alice_account(), + bob_account(), + &*signer, + vec![], + CryptoHash::default(), + ), + true, + None, + PROTOCOL_VERSION, + ) + .expect_err("expected an error"), + RuntimeError::InvalidTxError(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::RequiresFullAccess + )), + ); + + assert_eq!( + verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &SignedTransaction::from_actions( + 1, + alice_account(), + bob_account(), + &*signer, + vec![Action::CreateAccount(CreateAccountAction {})], + CryptoHash::default(), + ), + true, + None, + PROTOCOL_VERSION, + ) + .expect_err("expected an error"), + RuntimeError::InvalidTxError(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::RequiresFullAccess + )), + ); + } + + #[test] + fn test_validate_transaction_invalid_receiver_for_function_call() { + let config = RuntimeConfig::test(); + let (signer, mut state_update, gas_price) = setup_common( + TESTING_INIT_BALANCE, + 0, + Some(AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: bob_account().into(), + method_names: vec![], + }), + }), + ); + + assert_eq!( + verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &SignedTransaction::from_actions( + 1, + alice_account(), + eve_dot_alice_account(), + &*signer, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "hello".to_string(), + args: b"abc".to_vec(), + gas: 100, + deposit: 0, + })),], + CryptoHash::default(), + ), + true, + None, + PROTOCOL_VERSION, + ) + .expect_err("expected an error"), + RuntimeError::InvalidTxError(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::ReceiverMismatch { + tx_receiver: eve_dot_alice_account(), + ak_receiver: bob_account().into() + } + )), + ); + } + + #[test] + fn test_validate_transaction_invalid_method_name_for_function_call() { + let config = RuntimeConfig::test(); + let (signer, mut state_update, gas_price) = setup_common( + TESTING_INIT_BALANCE, + 0, + Some(AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: bob_account().into(), + method_names: vec!["not_hello".to_string(), "world".to_string()], + }), + }), + ); + + assert_eq!( + verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &SignedTransaction::from_actions( + 1, + alice_account(), + bob_account(), + &*signer, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "hello".to_string(), + args: b"abc".to_vec(), + gas: 100, + deposit: 0, + })),], + CryptoHash::default(), + ), + true, + None, + PROTOCOL_VERSION, + ) + .expect_err("expected an error"), + RuntimeError::InvalidTxError(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::MethodNameMismatch { method_name: "hello".to_string() } + )), + ); + } + + #[test] + fn test_validate_transaction_deposit_with_function_call() { + let config = RuntimeConfig::test(); + let (signer, mut state_update, gas_price) = setup_common( + TESTING_INIT_BALANCE, + 0, + Some(AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: bob_account().into(), + method_names: vec![], + }), + }), + ); + + assert_eq!( + verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &SignedTransaction::from_actions( + 1, + alice_account(), + bob_account(), + &*signer, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "hello".to_string(), + args: b"abc".to_vec(), + gas: 100, + deposit: 100, + })),], + CryptoHash::default(), + ), + true, + None, + PROTOCOL_VERSION, + ) + .expect_err("expected an error"), + RuntimeError::InvalidTxError(InvalidTxError::InvalidAccessKeyError( + InvalidAccessKeyError::DepositWithFunctionCall, + )), + ); + } + + #[test] + fn test_validate_transaction_exceeding_tx_size_limit() { + let (signer, mut state_update, gas_price) = + setup_common(TESTING_INIT_BALANCE, 0, Some(AccessKey::full_access())); + + let transaction = SignedTransaction::from_actions( + 1, + alice_account(), + bob_account(), + &*signer, + vec![Action::DeployContract(DeployContractAction { code: vec![1; 5] })], + CryptoHash::default(), + ); + let transaction_size = transaction.get_size(); + + let mut config = RuntimeConfig::test(); + let max_transaction_size = transaction_size - 1; + config.wasm_config.limit_config.max_transaction_size = transaction_size - 1; + + assert_eq!( + verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &transaction, + false, + None, + PROTOCOL_VERSION, + ) + .expect_err("expected an error"), + RuntimeError::InvalidTxError(InvalidTxError::TransactionSizeExceeded { + size: transaction_size, + limit: max_transaction_size + }), + ); + + config.wasm_config.limit_config.max_transaction_size = transaction_size + 1; + verify_and_charge_transaction( + &config, + &mut state_update, + gas_price, + &transaction, + false, + None, + PROTOCOL_VERSION, + ) + .expect("valid transaction"); + } + + // Receipts + + #[test] + fn test_validate_receipt_valid() { + let limit_config = test_limit_config(); + validate_receipt( + &limit_config, + &Receipt::new_balance_refund(&alice_account(), 10), + PROTOCOL_VERSION, + ) + .expect("valid receipt"); + } + + #[test] + fn test_validate_action_receipt_too_many_input_deps() { + let mut limit_config = test_limit_config(); + limit_config.max_number_input_data_dependencies = 1; + assert_eq!( + validate_action_receipt( + &limit_config, + &ActionReceipt { + signer_id: alice_account(), + signer_public_key: PublicKey::empty(KeyType::ED25519), + gas_price: 100, + output_data_receivers: vec![], + input_data_ids: vec![CryptoHash::default(), CryptoHash::default()], + actions: vec![] + }, + PROTOCOL_VERSION + ) + .expect_err("expected an error"), + ReceiptValidationError::NumberInputDataDependenciesExceeded { + number_of_input_data_dependencies: 2, + limit: 1 + } + ); + } + + // DataReceipt + + #[test] + fn test_validate_data_receipt_valid() { + let limit_config = test_limit_config(); + validate_data_receipt( + &limit_config, + &DataReceipt { data_id: CryptoHash::default(), data: None }, + ) + .expect("valid data receipt"); + let data = b"hello".to_vec(); + validate_data_receipt( + &limit_config, + &DataReceipt { data_id: CryptoHash::default(), data: Some(data) }, + ) + .expect("valid data receipt"); + } + + #[test] + fn test_validate_data_receipt_too_much_data() { + let mut limit_config = test_limit_config(); + let data = b"hello".to_vec(); + limit_config.max_length_returned_data = data.len() as u64 - 1; + assert_eq!( + validate_data_receipt( + &limit_config, + &DataReceipt { data_id: CryptoHash::default(), data: Some(data.clone()) } + ) + .expect_err("expected an error"), + ReceiptValidationError::ReturnedValueLengthExceeded { + length: data.len() as u64, + limit: limit_config.max_length_returned_data + } + ); + } + + // Group of actions + + #[test] + fn test_validate_actions_empty() { + let limit_config = test_limit_config(); + validate_actions(&limit_config, &[], PROTOCOL_VERSION).expect("empty actions"); + } + + #[test] + fn test_validate_actions_valid_function_call() { + let limit_config = test_limit_config(); + validate_actions( + &limit_config, + &[Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "hello".to_string(), + args: b"abc".to_vec(), + gas: 100, + deposit: 0, + }))], + PROTOCOL_VERSION, + ) + .expect("valid function call action"); + } + + #[test] + fn test_validate_actions_too_much_gas() { + let mut limit_config = test_limit_config(); + limit_config.max_total_prepaid_gas = 220; + assert_eq!( + validate_actions( + &limit_config, + &[ + Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "hello".to_string(), + args: b"abc".to_vec(), + gas: 100, + deposit: 0, + })), + Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "hello".to_string(), + args: b"abc".to_vec(), + gas: 150, + deposit: 0, + })) + ], + PROTOCOL_VERSION, + ) + .expect_err("expected an error"), + ActionsValidationError::TotalPrepaidGasExceeded { total_prepaid_gas: 250, limit: 220 } + ); + } + + #[test] + fn test_validate_actions_gas_overflow() { + let mut limit_config = test_limit_config(); + limit_config.max_total_prepaid_gas = 220; + assert_eq!( + validate_actions( + &limit_config, + &[ + Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "hello".to_string(), + args: b"abc".to_vec(), + gas: u64::max_value() / 2 + 1, + deposit: 0, + })), + Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "hello".to_string(), + args: b"abc".to_vec(), + gas: u64::max_value() / 2 + 1, + deposit: 0, + })) + ], + PROTOCOL_VERSION, + ) + .expect_err("Expected an error"), + ActionsValidationError::IntegerOverflow, + ); + } + + #[test] + fn test_validate_actions_num_actions() { + let mut limit_config = test_limit_config(); + limit_config.max_actions_per_receipt = 1; + assert_eq!( + validate_actions( + &limit_config, + &[ + Action::CreateAccount(CreateAccountAction {}), + Action::CreateAccount(CreateAccountAction {}), + ], + PROTOCOL_VERSION, + ) + .expect_err("Expected an error"), + ActionsValidationError::TotalNumberOfActionsExceeded { + total_number_of_actions: 2, + limit: 1 + }, + ); + } + + #[test] + fn test_validate_delete_must_be_final() { + let mut limit_config = test_limit_config(); + limit_config.max_actions_per_receipt = 3; + assert_eq!( + validate_actions( + &limit_config, + &[ + Action::DeleteAccount(DeleteAccountAction { + beneficiary_id: "bob".parse().unwrap() + }), + Action::CreateAccount(CreateAccountAction {}), + ], + PROTOCOL_VERSION, + ) + .expect_err("Expected an error"), + ActionsValidationError::DeleteActionMustBeFinal, + ); + } + + #[test] + fn test_validate_delete_must_work_if_its_final() { + let mut limit_config = test_limit_config(); + limit_config.max_actions_per_receipt = 3; + assert_eq!( + validate_actions( + &limit_config, + &[ + Action::CreateAccount(CreateAccountAction {}), + Action::DeleteAccount(DeleteAccountAction { + beneficiary_id: "bob".parse().unwrap() + }), + ], + PROTOCOL_VERSION, + ), + Ok(()), + ); + } + + // Individual actions + + #[test] + fn test_validate_action_valid_create_account() { + validate_action( + &test_limit_config(), + &Action::CreateAccount(CreateAccountAction {}), + PROTOCOL_VERSION, + ) + .expect("valid action"); + } + + #[test] + fn test_validate_action_valid_function_call() { + validate_action( + &test_limit_config(), + &Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "hello".to_string(), + args: b"abc".to_vec(), + gas: 100, + deposit: 0, + })), + PROTOCOL_VERSION, + ) + .expect("valid action"); + } + + #[test] + fn test_validate_action_invalid_function_call_zero_gas() { + assert_eq!( + validate_action( + &test_limit_config(), + &Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "new".to_string(), + args: vec![], + gas: 0, + deposit: 0, + })), + PROTOCOL_VERSION, + ) + .expect_err("expected an error"), + ActionsValidationError::FunctionCallZeroAttachedGas, + ); + } + + #[test] + fn test_validate_action_valid_transfer() { + validate_action( + &test_limit_config(), + &Action::Transfer(TransferAction { deposit: 10 }), + PROTOCOL_VERSION, + ) + .expect("valid action"); + } + + #[test] + fn test_validate_action_valid_stake() { + validate_action( + &test_limit_config(), + &Action::Stake(Box::new(StakeAction { + stake: 100, + public_key: "ed25519:KuTCtARNzxZQ3YvXDeLjx83FDqxv2SdQTSbiq876zR7".parse().unwrap(), + })), + PROTOCOL_VERSION, + ) + .expect("valid action"); + } + + #[test] + fn test_validate_action_invalid_staking_key() { + assert_eq!( + validate_action( + &test_limit_config(), + &Action::Stake(Box::new(StakeAction { + stake: 100, + public_key: PublicKey::empty(KeyType::ED25519), + })), + PROTOCOL_VERSION, + ) + .expect_err("Expected an error"), + ActionsValidationError::UnsuitableStakingKey { + public_key: PublicKey::empty(KeyType::ED25519).into(), + }, + ); + } + + #[test] + fn test_validate_action_valid_add_key_full_permission() { + validate_action( + &test_limit_config(), + &Action::AddKey(Box::new(AddKeyAction { + public_key: PublicKey::empty(KeyType::ED25519), + access_key: AccessKey::full_access(), + })), + PROTOCOL_VERSION, + ) + .expect("valid action"); + } + + #[test] + fn test_validate_action_valid_add_key_function_call() { + validate_action( + &test_limit_config(), + &Action::AddKey(Box::new(AddKeyAction { + public_key: PublicKey::empty(KeyType::ED25519), + access_key: AccessKey { + nonce: 0, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(1000), + receiver_id: alice_account().into(), + method_names: vec!["hello".to_string(), "world".to_string()], + }), + }, + })), + PROTOCOL_VERSION, + ) + .expect("valid action"); + } + + #[test] + fn test_validate_action_valid_delete_key() { + validate_action( + &test_limit_config(), + &Action::DeleteKey(Box::new(DeleteKeyAction { + public_key: PublicKey::empty(KeyType::ED25519), + })), + PROTOCOL_VERSION, + ) + .expect("valid action"); + } + + #[test] + fn test_validate_action_valid_delete_account() { + validate_action( + &test_limit_config(), + &Action::DeleteAccount(DeleteAccountAction { beneficiary_id: alice_account() }), + PROTOCOL_VERSION, + ) + .expect("valid action"); + } + + #[test] + fn test_delegate_action_must_be_only_one() { + let signed_delegate_action = SignedDelegateAction { + delegate_action: DelegateAction { + sender_id: "bob.test.near".parse().unwrap(), + receiver_id: "token.test.near".parse().unwrap(), + actions: vec![NonDelegateAction::try_from(Action::CreateAccount( + CreateAccountAction {}, + )) + .unwrap()], + nonce: 19000001, + max_block_height: 57, + public_key: PublicKey::empty(KeyType::ED25519), + }, + signature: Signature::default(), + }; + assert_eq!( + validate_actions( + &test_limit_config(), + &[ + Action::Delegate(Box::new(signed_delegate_action.clone())), + Action::Delegate(Box::new(signed_delegate_action.clone())), + ], + PROTOCOL_VERSION, + ), + Err(ActionsValidationError::DelegateActionMustBeOnlyOne), + ); + assert_eq!( + validate_actions( + &&test_limit_config(), + &[Action::Delegate(Box::new(signed_delegate_action.clone())),], + PROTOCOL_VERSION, + ), + Ok(()), + ); + assert_eq!( + validate_actions( + &test_limit_config(), + &[ + Action::CreateAccount(CreateAccountAction {}), + Action::Delegate(Box::new(signed_delegate_action)), + ], + PROTOCOL_VERSION, + ), + Ok(()), + ); + } + + #[test] + fn test_truncate_string() { + fn check(input: &str, limit: usize, want: &str) { + let got = truncate_string(input, limit); + assert_eq!(got, want) + } + check("", 10, ""); + check("hello", 0, ""); + check("hello", 2, "he"); + check("hello", 4, "hell"); + check("hello", 5, "hello"); + check("hello", 6, "hello"); + check("hello", 10, "hello"); + check("привет", 3, "п"); + } +} diff --git a/runtime/runtime/tests/runtime_group_tools/mod.rs b/runtime/runtime/tests/runtime_group_tools/mod.rs new file mode 100644 index 000000000..a57acf011 --- /dev/null +++ b/runtime/runtime/tests/runtime_group_tools/mod.rs @@ -0,0 +1,449 @@ +use unc_chain_configs::{get_initial_supply, Genesis, GenesisConfig, GenesisRecords}; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_parameters::ActionCosts; +use unc_primitives::account::{AccessKey, Account}; +use unc_primitives::hash::{hash, CryptoHash}; +use unc_primitives::receipt::Receipt; +use unc_primitives::runtime::migration_data::{MigrationData, MigrationFlags}; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::state_record::{state_record_to_account_id, StateRecord}; +use unc_primitives::test_utils::MockEpochInfoProvider; +use unc_primitives::transaction::{ExecutionOutcomeWithId, SignedTransaction}; +use unc_primitives::types::{AccountId, AccountInfo, Balance}; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_primitives_core::account::id::AccountIdRef; +use unc_store::genesis::GenesisStateApplier; +use unc_store::test_utils::TestTriesBuilder; +use unc_store::ShardTries; +use node_runtime::{ApplyState, Runtime}; +use random_config::random_config; +use std::collections::{HashMap, HashSet}; +use std::sync::{Arc, Condvar, Mutex}; +use std::thread; +use std::thread::JoinHandle; + +pub mod random_config; + +/// Initial balance used in tests. +pub const TESTING_INIT_BALANCE: Balance = 1_000_000_000 * UNC_BASE; + +/// Validator's stake used in tests. +pub const TESTING_INIT_STAKE: Balance = 50_000_000 * UNC_BASE; + +/// One NEAR, divisible by 10^24. +pub const UNC_BASE: Balance = 1_000_000_000_000_000_000_000_000; + +pub struct StandaloneRuntime { + pub apply_state: ApplyState, + pub runtime: Runtime, + pub tries: ShardTries, + pub signer: InMemorySigner, + pub root: CryptoHash, + pub epoch_info_provider: MockEpochInfoProvider, +} + +impl StandaloneRuntime { + pub fn account_id(&self) -> AccountId { + self.signer.account_id.clone() + } + + pub fn new( + signer: InMemorySigner, + state_records: &[StateRecord], + tries: ShardTries, + validators: Vec, + ) -> Self { + let mut runtime_config = random_config(); + // Bumping costs to avoid inflation overflows. + runtime_config.wasm_config.limit_config.max_total_prepaid_gas = 10u64.pow(15); + runtime_config.fees.action_fees[ActionCosts::new_action_receipt].execution = + runtime_config.wasm_config.limit_config.max_total_prepaid_gas / 64; + runtime_config.fees.action_fees[ActionCosts::new_data_receipt_base].execution = + runtime_config.wasm_config.limit_config.max_total_prepaid_gas / 64; + + let runtime = Runtime::new(); + let genesis = Genesis::new( + GenesisConfig { + validators, + total_supply: get_initial_supply(state_records), + epoch_length: 60, + ..Default::default() + }, + GenesisRecords(state_records.to_vec()), + ) + .unwrap(); + + let mut account_ids: HashSet = HashSet::new(); + genesis.for_each_record(|record: &StateRecord| { + account_ids.insert(state_record_to_account_id(record).clone()); + }); + let writers = std::sync::atomic::AtomicUsize::new(0); + let root = GenesisStateApplier::apply( + &writers, + tries.clone(), + 0, + &[], + &runtime_config.fees.storage_usage_config, + &genesis, + account_ids, + ); + + let apply_state = ApplyState { + block_height: 1, + prev_block_hash: Default::default(), + block_hash: Default::default(), + epoch_id: Default::default(), + epoch_height: 0, + gas_price: 100, + block_timestamp: 0, + gas_limit: None, + random_seed: Default::default(), + current_protocol_version: PROTOCOL_VERSION, + config: Arc::new(runtime_config), + cache: None, + is_new_chunk: true, + migration_data: Arc::new(MigrationData::default()), + migration_flags: MigrationFlags::default(), + }; + + Self { + apply_state, + runtime, + tries, + signer, + root, + epoch_info_provider: MockEpochInfoProvider::default(), + } + } + + pub fn process_block( + &mut self, + receipts: &[Receipt], + transactions: &[SignedTransaction], + ) -> (Vec, Vec) { + let apply_result = self + .runtime + .apply( + self.tries.get_trie_for_shard(ShardUId::single_shard(), self.root), + &None, + &self.apply_state, + receipts, + transactions, + &self.epoch_info_provider, + Default::default(), + ) + .unwrap(); + + let mut store_update = self.tries.store_update(); + self.root = self.tries.apply_all( + &apply_result.trie_changes, + ShardUId::single_shard(), + &mut store_update, + ); + store_update.commit().unwrap(); + self.apply_state.block_height += 1; + + (apply_result.outgoing_receipts, apply_result.outcomes) + } +} + +#[derive(Default)] +pub struct RuntimeMailbox { + pub incoming_transactions: Vec, + pub incoming_receipts: Vec, +} + +impl RuntimeMailbox { + pub fn is_empty(&self) -> bool { + self.incoming_receipts.is_empty() && self.incoming_transactions.is_empty() + } +} + +#[derive(Default)] +pub struct RuntimeGroup { + pub mailboxes: (Mutex>, Condvar), + pub state_records: Arc>, + pub signers: Vec, + pub validators: Vec, + + /// Account id of the runtime on which the transaction was executed mapped to the transactions. + pub executed_transactions: Mutex>>, + /// Account id of the runtime on which the receipt was executed mapped to the list of the receipts. + pub executed_receipts: Mutex>>, + /// List of the transaction logs. + pub transaction_logs: Mutex>, +} + +impl RuntimeGroup { + pub fn new_with_account_ids( + account_ids: Vec, + num_existing_accounts: u64, + contract_code: &[u8], + ) -> Arc { + let mut res = Self::default(); + assert!(num_existing_accounts <= account_ids.len() as u64); + let (state_records, signers, validators) = + Self::state_records_signers(account_ids, num_existing_accounts, contract_code); + Arc::make_mut(&mut res.state_records).extend(state_records); + + for signer in signers { + res.signers.push(signer.clone()); + res.mailboxes.0.lock().unwrap().insert(signer.account_id, Default::default()); + } + res.validators = validators; + Arc::new(res) + } + + pub fn new(num_runtimes: u64, num_existing_accounts: u64, contract_code: &[u8]) -> Arc { + let account_ids = (0..num_runtimes) + .map(|i| AccountId::try_from(format!("unc_{}", i)).unwrap()) + .collect(); + Self::new_with_account_ids(account_ids, num_existing_accounts, contract_code) + } + + /// Get state records and signers for standalone runtimes. + fn state_records_signers( + account_ids: Vec, + num_existing_accounts: u64, + contract_code: &[u8], + ) -> (Vec, Vec, Vec) { + let code_hash = hash(contract_code); + let mut state_records = vec![]; + let mut signers = vec![]; + let mut validators = vec![]; + for (i, account_id) in account_ids.into_iter().enumerate() { + let signer = InMemorySigner::from_seed( + account_id.clone(), + KeyType::ED25519, + account_id.as_ref(), + ); + if (i as u64) < num_existing_accounts { + state_records.push(StateRecord::Account { + account_id: account_id.clone(), + account: Account::new(TESTING_INIT_BALANCE, TESTING_INIT_STAKE, code_hash, 0), + }); + state_records.push(StateRecord::AccessKey { + account_id: account_id.clone(), + public_key: signer.public_key.clone(), + access_key: AccessKey::full_access(), + }); + state_records + .push(StateRecord::Contract { account_id, code: contract_code.to_vec() }); + validators.push(AccountInfo { + account_id: signer.account_id.clone(), + public_key: signer.public_key.clone(), + amount: TESTING_INIT_STAKE, + }); + } + signers.push(signer); + } + (state_records, signers, validators) + } + + pub fn start_runtimes( + group: Arc, + transactions: Vec, + ) -> Vec> { + for transaction in transactions { + group + .mailboxes + .0 + .lock() + .unwrap() + .get_mut(&transaction.transaction.signer_id) + .unwrap() + .incoming_transactions + .push(transaction); + } + + let mut handles = vec![]; + for signer in &group.signers { + let signer = signer.clone(); + let state_records = Arc::clone(&group.state_records); + let validators = group.validators.clone(); + let runtime_factory = move || { + StandaloneRuntime::new( + signer, + &state_records, + TestTriesBuilder::new().build(), + validators, + ) + }; + handles.push(Self::start_runtime_in_thread(group.clone(), runtime_factory)); + } + handles + } + + fn start_runtime_in_thread(group: Arc, runtime_factory: F) -> JoinHandle<()> + where + F: FnOnce() -> StandaloneRuntime + Send + 'static, + { + thread::spawn(move || { + let mut runtime = runtime_factory(); + loop { + let account_id = runtime.account_id(); + + let mut mailboxes = group.mailboxes.0.lock().unwrap(); + loop { + if !mailboxes.get(&account_id).unwrap().is_empty() { + break; + } + if mailboxes.values().all(|m| m.is_empty()) { + return; + } + mailboxes = group.mailboxes.1.wait(mailboxes).unwrap(); + } + + let mailbox = mailboxes.get_mut(&account_id).unwrap(); + group + .executed_receipts + .lock() + .unwrap() + .entry(account_id.clone()) + .or_insert_with(Vec::new) + .extend(mailbox.incoming_receipts.clone()); + group + .executed_transactions + .lock() + .unwrap() + .entry(account_id.clone()) + .or_insert_with(Vec::new) + .extend(mailbox.incoming_transactions.clone()); + + let (new_receipts, transaction_results) = runtime + .process_block(&mailbox.incoming_receipts, &mailbox.incoming_transactions); + mailbox.incoming_receipts.clear(); + mailbox.incoming_transactions.clear(); + group.transaction_logs.lock().unwrap().extend(transaction_results); + for new_receipt in new_receipts { + let locked_other_mailbox = mailboxes.get_mut(&new_receipt.receiver_id).unwrap(); + locked_other_mailbox.incoming_receipts.push(new_receipt); + } + group.mailboxes.1.notify_all(); + } + }) + } + + /// Get receipt that was executed by the given runtime based on hash. + pub fn get_receipt(&self, executing_runtime: &str, hash: &CryptoHash) -> Receipt { + self.executed_receipts + .lock() + .unwrap() + .get(AccountIdRef::new_or_panic(executing_runtime)) + .expect("Runtime not found") + .iter() + .find_map(|r| if &r.get_hash() == hash { Some(r.clone()) } else { None }) + .expect("Runtime does not contain the receipt with the given hash.") + } + + /// Get transaction log produced by the execution of given transaction/receipt + /// identified by `producer_hash`. + pub fn get_transaction_log(&self, producer_hash: &CryptoHash) -> ExecutionOutcomeWithId { + self.transaction_logs + .lock() + .unwrap() + .iter() + .find_map(|tl| if &tl.id == producer_hash { Some(tl.clone()) } else { None }) + .expect("The execution log of the given receipt is missing") + } + + pub fn get_receipt_debug(&self, hash: &CryptoHash) -> (AccountId, Receipt) { + for (executed_runtime, tls) in self.executed_receipts.lock().unwrap().iter() { + if let Some(res) = + tls.iter().find_map(|r| if &r.get_hash() == hash { Some(r.clone()) } else { None }) + { + return (executed_runtime.clone(), res); + } + } + unimplemented!() + } +} + +/// Binds a tuple to a vector. +/// # Examples: +/// +/// ``` +/// let v = vec![1,2,3]; +/// tuplet!((a,b,c) = v); +/// assert_eq!(a, &1); +/// assert_eq!(b, &2); +/// assert_eq!(c, &3); +/// ``` +#[macro_export] +macro_rules! tuplet { + {() = $v:expr, $message:expr } => { + assert!($v.is_empty(), "{}", $message); + }; + {($y:ident) = $v:expr, $message:expr } => { + assert_eq!($v.len(), 1, "{}", $message); + let $y = &$v[0]; + }; + { ($y:ident $(, $x:ident)*) = $v:expr, $message:expr } => { + let ($y, $($x),*) = tuplet!($v ; 1 ; ($($x),*) ; (&$v[0]), $message ); + }; + { $v:expr ; $j:expr ; ($y:ident $(, $x:ident)*) ; ($($a:expr),*), $message:expr } => { + tuplet!( $v ; $j+1 ; ($($x),*) ; ($($a),*,&$v[$j]), $message ) + }; + { $v:expr ; $j:expr ; () ; $accu:expr, $message:expr } => { { + assert_eq!($v.len(), $j, "{}", $message); + $accu + } } +} + +#[macro_export] +macro_rules! assert_receipts { + ($group:ident, $transaction:ident => [ $($receipt:ident),* ] ) => { + let transaction_log = $group.get_transaction_log(&$transaction.get_hash()); + tuplet!(( $($receipt),* ) = transaction_log.outcome.receipt_ids, "Incorrect number of produced receipts for transaction"); + }; + ($group:ident, $from:expr => $receipt:ident @ $to:expr, + $receipt_pat:pat, + $receipt_assert:block, + $actions_name:ident, + $($action_name:ident, $action_pat:pat, $action_assert:block ),+ + => [ $($produced_receipt:ident),*] ) => { + let r = $group.get_receipt($to, $receipt); + assert_eq!(r.predecessor_id, $from); + assert_eq!(r.receiver_id, $to); + match &r.receipt { + $receipt_pat => { + $receipt_assert + tuplet!(( $($action_name),* ) = $actions_name, "Incorrect number of actions"); + $( + match $action_name { + $action_pat => { + $action_assert + } + _ => panic!("Action {:#?} does not satisfy the pattern {}", $action_name, stringify!($action_pat)), + } + )* + } + _ => panic!("Receipt {:#?} does not satisfy the pattern {}", r, stringify!($receipt_pat)), + } + let receipt_log = $group.get_transaction_log(&r.get_hash()); + tuplet!(( $($produced_receipt),* ) = receipt_log.outcome.receipt_ids, "Incorrect number of produced receipts for a receipt"); + }; +} + +/// A short form for refunds. +/// ``` +/// assert_refund!(group, ref1 @ "unc_0"); +/// ``` +/// expands into: +/// ``` +/// assert_receipts!(group, "system" => ref1 @ "unc_0", +/// ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, +/// actions, +/// a0, Action::Transfer(TransferAction{..}), {} +/// => []); +/// ``` +#[macro_export] +macro_rules! assert_refund { + ($group:ident, $receipt:ident @ $to:expr) => { + assert_receipts!($group, "system" => $receipt @ $to, + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::Transfer(TransferAction{..}), {} + => []); + } +} diff --git a/runtime/runtime/tests/runtime_group_tools/random_config.rs b/runtime/runtime/tests/runtime_group_tools/random_config.rs new file mode 100644 index 000000000..ac2918d9d --- /dev/null +++ b/runtime/runtime/tests/runtime_group_tools/random_config.rs @@ -0,0 +1,35 @@ +use unc_parameters::{Fee, RuntimeConfig, RuntimeFeesConfig, StorageUsageConfig}; +use unc_primitives::num_rational::Rational32; +use rand::{thread_rng, RngCore}; + +pub fn random_config() -> RuntimeConfig { + let mut rng = thread_rng(); + let mut random_fee = || Fee { + send_sir: rng.next_u64() % 1000, + send_not_sir: rng.next_u64() % 1000, + execution: rng.next_u64() % 1000, + }; + RuntimeConfig { + fees: RuntimeFeesConfig { + action_fees: enum_map::enum_map! { + _ => random_fee(), + }, + storage_usage_config: StorageUsageConfig { + num_bytes_account: rng.next_u64() % 10000, + num_extra_bytes_record: rng.next_u64() % 10000, + storage_amount_per_byte: rng.next_u64() as u128, + }, + burnt_gas_reward: Rational32::new((rng.next_u32() % 100).try_into().unwrap(), 100), + pessimistic_gas_price_inflation_ratio: Rational32::new( + (101 + rng.next_u32() % 10).try_into().unwrap(), + 100, + ), + }, + ..RuntimeConfig::test() + } +} + +#[test] +fn test_random_fees() { + assert_ne!(random_config().fees, random_config().fees); +} diff --git a/runtime/runtime/tests/test_async_calls.rs b/runtime/runtime/tests/test_async_calls.rs new file mode 100644 index 000000000..5833d77ca --- /dev/null +++ b/runtime/runtime/tests/test_async_calls.rs @@ -0,0 +1,1080 @@ +use crate::runtime_group_tools::RuntimeGroup; + +use unc_crypto::{InMemorySigner, KeyType}; +use unc_primitives::account::{AccessKeyPermission, FunctionCallPermission}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::{ActionReceipt, ReceiptEnum}; +use unc_primitives::serialize::to_base64; +use unc_primitives::types::AccountId; + +pub mod runtime_group_tools; + +/// Initial balance used in tests. +pub const TESTING_INIT_BALANCE: u128 = 1_000_000_000 * UNC_BASE; + +/// One NEAR, divisible by 10^24. +pub const UNC_BASE: u128 = 1_000_000_000_000_000_000_000_000; + +const GAS_1: u64 = 900_000_000_000_000; +const GAS_2: u64 = GAS_1 / 3; +const GAS_3: u64 = GAS_2 / 3; + +#[test] +fn test_simple_func_call() { + let group = RuntimeGroup::new(2, 2, unc_test_contracts::rs_contract()); + let signer_sender = group.signers[0].clone(); + let signer_receiver = group.signers[1].clone(); + + let signed_transaction = SignedTransaction::from_actions( + 1, + signer_sender.account_id.clone(), + signer_receiver.account_id, + &signer_sender, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "sum_n".to_string(), + args: 10u64.to_le_bytes().to_vec(), + gas: GAS_1, + deposit: 0, + }))], + CryptoHash::default(), + ); + + let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); + for h in handles { + h.join().unwrap(); + } + + use unc_primitives::transaction::*; + assert_receipts!(group, signed_transaction => [r0]); + assert_receipts!(group, "unc_0" => r0 @ "unc_1", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(_function_call_action), {} + => [ref1] ); + assert_refund!(group, ref1 @ "unc_0"); +} + +// single promise, no callback (A->B) +#[test] +fn test_single_promise_no_callback() { + let group = RuntimeGroup::new(3, 3, unc_test_contracts::rs_contract()); + let signer_sender = group.signers[0].clone(); + let signer_receiver = group.signers[1].clone(); + + let data = serde_json::json!([ + {"create": { + "account_id": "unc_2", + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": GAS_2, + }, "id": 0 } + ]); + + let signed_transaction = SignedTransaction::from_actions( + 1, + signer_sender.account_id.clone(), + signer_receiver.account_id, + &signer_sender, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "call_promise".to_string(), + args: serde_json::to_vec(&data).unwrap(), + gas: GAS_1, + deposit: 0, + }))], + CryptoHash::default(), + ); + + let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); + for h in handles { + h.join().unwrap(); + } + + use unc_primitives::transaction::*; + assert_receipts!(group, signed_transaction => [r0]); + assert_receipts!(group, "unc_0" => r0 @ "unc_1", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_1); + assert_eq!(function_call_action.deposit, 0); + } + => [r1, ref0] ); + assert_receipts!(group, "unc_1" => r1 @ "unc_2", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_2); + assert_eq!(function_call_action.deposit, 0); + } + => [ref1]); + assert_refund!(group, ref0 @ "unc_0"); + assert_refund!(group, ref1 @ "unc_0"); +} + +// single promise with callback (A->B=>C) +#[test] +fn test_single_promise_with_callback() { + let group = RuntimeGroup::new(4, 4, unc_test_contracts::rs_contract()); + let signer_sender = group.signers[0].clone(); + let signer_receiver = group.signers[1].clone(); + + let data = serde_json::json!([ + {"create": { + "account_id": "unc_2", + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": GAS_2, + }, "id": 0 }, + {"then": { + "promise_index": 0, + "account_id": "unc_3", + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": GAS_2, + }, "id": 1} + ]); + + let signed_transaction = SignedTransaction::from_actions( + 1, + signer_sender.account_id.clone(), + signer_receiver.account_id, + &signer_sender, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "call_promise".to_string(), + args: serde_json::to_vec(&data).unwrap(), + gas: GAS_1, + deposit: 0, + }))], + CryptoHash::default(), + ); + + let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); + for h in handles { + h.join().unwrap(); + } + + use unc_primitives::transaction::*; + assert_receipts!(group, signed_transaction => [r0]); + assert_receipts!(group, "unc_0" => r0 @ "unc_1", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_1); + assert_eq!(function_call_action.deposit, 0); + } + => [r1, r2, ref0] ); + let data_id; + assert_receipts!(group, "unc_1" => r1 @ "unc_2", + ReceiptEnum::Action(ActionReceipt{actions, output_data_receivers, ..}), { + assert_eq!(output_data_receivers.len(), 1); + data_id = output_data_receivers[0].data_id; + }, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_2); + assert_eq!(function_call_action.deposit, 0); + } + => [ref1]); + assert_receipts!(group, "unc_1" => r2 @ "unc_3", + ReceiptEnum::Action(ActionReceipt{actions, input_data_ids, ..}), { + assert_eq!(input_data_ids.len(), 1); + assert_eq!(data_id, input_data_ids[0].clone()); + }, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_2); + assert_eq!(function_call_action.deposit, 0); + } + => [ref2]); + + assert_refund!(group, ref0 @ "unc_0"); + assert_refund!(group, ref1 @ "unc_0"); + assert_refund!(group, ref2 @ "unc_0"); +} + +// two promises, no callbacks (A->B->C) +#[test] +fn test_two_promises_no_callbacks() { + let group = RuntimeGroup::new(4, 4, unc_test_contracts::rs_contract()); + let signer_sender = group.signers[0].clone(); + let signer_receiver = group.signers[1].clone(); + + let data = serde_json::json!([ + {"create": { + "account_id": "unc_2", + "method_name": "call_promise", + "arguments": [ + {"create": { + "account_id": "unc_3", + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": GAS_3, + }, "id": 0} + ], + "amount": "0", + "gas": GAS_2, + }, "id": 0 }, + + ]); + + let signed_transaction = SignedTransaction::from_actions( + 1, + signer_sender.account_id.clone(), + signer_receiver.account_id, + &signer_sender, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "call_promise".to_string(), + args: serde_json::to_vec(&data).unwrap(), + gas: GAS_1, + deposit: 0, + }))], + CryptoHash::default(), + ); + + let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); + for h in handles { + h.join().unwrap(); + } + + use unc_primitives::transaction::*; + assert_receipts!(group, signed_transaction => [r0]); + assert_receipts!(group, "unc_0" => r0 @ "unc_1", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_1); + assert_eq!(function_call_action.deposit, 0); + } + => [r1, ref0] ); + assert_receipts!(group, "unc_1" => r1 @ "unc_2", + ReceiptEnum::Action(ActionReceipt{actions, ..}), { }, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_2); + assert_eq!(function_call_action.deposit, 0); + } + => [r2, ref1]); + assert_receipts!(group, "unc_2" => r2 @ "unc_3", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_3); + assert_eq!(function_call_action.deposit, 0); + } + => [ref2]); + + assert_refund!(group, ref0 @ "unc_0"); + assert_refund!(group, ref1 @ "unc_0"); + assert_refund!(group, ref2 @ "unc_0"); +} + +// two promises, with two callbacks (A->B->C=>D=>E) where call to E is initialized by completion of D. +#[test] +fn test_two_promises_with_two_callbacks() { + let group = RuntimeGroup::new(6, 6, unc_test_contracts::rs_contract()); + let signer_sender = group.signers[0].clone(); + let signer_receiver = group.signers[1].clone(); + + let data = serde_json::json!([ + {"create": { + "account_id": "unc_2", + "method_name": "call_promise", + "arguments": [ + {"create": { + "account_id": "unc_3", + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": GAS_3, + }, "id": 0}, + + {"then": { + "promise_index": 0, + "account_id": "unc_4", + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": GAS_3, + }, "id": 1} + ], + "amount": "0", + "gas": GAS_2, + }, "id": 0 }, + + {"then": { + "promise_index": 0, + "account_id": "unc_5", + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": GAS_2, + }, "id": 1} + ]); + + let signed_transaction = SignedTransaction::from_actions( + 1, + signer_sender.account_id.clone(), + signer_receiver.account_id, + &signer_sender, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "call_promise".to_string(), + args: serde_json::to_vec(&data).unwrap(), + gas: GAS_1, + deposit: 0, + }))], + CryptoHash::default(), + ); + + let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); + for h in handles { + h.join().unwrap(); + } + + use unc_primitives::transaction::*; + assert_receipts!(group, signed_transaction => [r0]); + assert_receipts!(group, "unc_0" => r0 @ "unc_1", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_1); + assert_eq!(function_call_action.deposit, 0); + } + => [r1, cb1, ref0] ); + assert_receipts!(group, "unc_1" => r1 @ "unc_2", + ReceiptEnum::Action(ActionReceipt{actions, ..}), { }, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_2); + assert_eq!(function_call_action.deposit, 0); + } + => [r2, cb2, ref1]); + assert_receipts!(group, "unc_2" => r2 @ "unc_3", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_3); + assert_eq!(function_call_action.deposit, 0); + } + => [ref2]); + assert_receipts!(group, "unc_2" => cb2 @ "unc_4", + ReceiptEnum::Action(ActionReceipt{actions, ..}), { }, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_3); + assert_eq!(function_call_action.deposit, 0); + } + => [ref3]); + assert_receipts!(group, "unc_1" => cb1 @ "unc_5", + ReceiptEnum::Action(ActionReceipt{actions, ..}), { }, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_2); + assert_eq!(function_call_action.deposit, 0); + } + => [ref4]); + + assert_refund!(group, ref0 @ "unc_0"); + assert_refund!(group, ref1 @ "unc_0"); + assert_refund!(group, ref2 @ "unc_0"); + assert_refund!(group, ref3 @ "unc_0"); + assert_refund!(group, ref4 @ "unc_0"); +} + +// Batch actions tests + +// single promise, no callback (A->B) with `promise_batch` +#[test] +fn test_single_promise_no_callback_batch() { + let group = RuntimeGroup::new(3, 3, unc_test_contracts::rs_contract()); + let signer_sender = group.signers[0].clone(); + let signer_receiver = group.signers[1].clone(); + + let data = serde_json::json!([ + {"batch_create": { + "account_id": "unc_2", + }, "id": 0 }, + {"action_function_call": { + "promise_index": 0, + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": GAS_2, + }, "id": 0 } + ]); + + let signed_transaction = SignedTransaction::from_actions( + 1, + signer_sender.account_id.clone(), + signer_receiver.account_id, + &signer_sender, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "call_promise".to_string(), + args: serde_json::to_vec(&data).unwrap(), + gas: GAS_1, + deposit: 0, + }))], + CryptoHash::default(), + ); + + let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); + for h in handles { + h.join().unwrap(); + } + + use unc_primitives::transaction::*; + assert_receipts!(group, signed_transaction => [r0]); + assert_receipts!(group, "unc_0" => r0 @ "unc_1", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_1); + assert_eq!(function_call_action.deposit, 0); + } + => [r1, ref0] ); + assert_receipts!(group, "unc_1" => r1 @ "unc_2", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_2); + assert_eq!(function_call_action.deposit, 0); + } + => [ref1]); + assert_refund!(group, ref0 @ "unc_0"); + assert_refund!(group, ref1 @ "unc_0"); +} + +// single promise with callback (A->B=>C) with batch actions +#[test] +fn test_single_promise_with_callback_batch() { + let group = RuntimeGroup::new(4, 4, unc_test_contracts::rs_contract()); + let signer_sender = group.signers[0].clone(); + let signer_receiver = group.signers[1].clone(); + + let data = serde_json::json!([ + {"batch_create": { + "account_id": "unc_2", + }, "id": 0 }, + {"action_function_call": { + "promise_index": 0, + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": GAS_2, + }, "id": 0 }, + {"batch_then": { + "promise_index": 0, + "account_id": "unc_3", + }, "id": 1}, + {"action_function_call": { + "promise_index": 1, + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": GAS_2, + }, "id": 1} + ]); + + let signed_transaction = SignedTransaction::from_actions( + 1, + signer_sender.account_id.clone(), + signer_receiver.account_id, + &signer_sender, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "call_promise".to_string(), + args: serde_json::to_vec(&data).unwrap(), + gas: GAS_1, + deposit: 0, + }))], + CryptoHash::default(), + ); + + let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); + for h in handles { + h.join().unwrap(); + } + + use unc_primitives::transaction::*; + assert_receipts!(group, signed_transaction => [r0]); + assert_receipts!(group, "unc_0" => r0 @ "unc_1", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_1); + assert_eq!(function_call_action.deposit, 0); + } + => [r1, r2, ref0] ); + let data_id; + assert_receipts!(group, "unc_1" => r1 @ "unc_2", + ReceiptEnum::Action(ActionReceipt{actions, output_data_receivers, ..}), { + assert_eq!(output_data_receivers.len(), 1); + data_id = output_data_receivers[0].data_id; + }, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_2); + assert_eq!(function_call_action.deposit, 0); + } + => [ref1]); + assert_receipts!(group, "unc_1" => r2 @ "unc_3", + ReceiptEnum::Action(ActionReceipt{actions, input_data_ids, ..}), { + assert_eq!(input_data_ids.len(), 1); + assert_eq!(data_id, input_data_ids[0].clone()); + }, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_2); + assert_eq!(function_call_action.deposit, 0); + } + => [ref2]); + + assert_refund!(group, ref0 @ "unc_0"); + assert_refund!(group, ref1 @ "unc_0"); + assert_refund!(group, ref2 @ "unc_0"); +} + +#[test] +fn test_simple_transfer() { + let group = RuntimeGroup::new(3, 3, unc_test_contracts::rs_contract()); + let signer_sender = group.signers[0].clone(); + let signer_receiver = group.signers[1].clone(); + + let data = serde_json::json!([ + {"batch_create": { + "account_id": "unc_2", + }, "id": 0 }, + {"action_transfer": { + "promise_index": 0, + "amount": "1000000000", + }, "id": 0 } + ]); + + let signed_transaction = SignedTransaction::from_actions( + 1, + signer_sender.account_id.clone(), + signer_receiver.account_id, + &signer_sender, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "call_promise".to_string(), + args: serde_json::to_vec(&data).unwrap(), + gas: GAS_1, + deposit: 0, + }))], + CryptoHash::default(), + ); + + let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); + for h in handles { + h.join().unwrap(); + } + + use unc_primitives::transaction::*; + assert_receipts!(group, signed_transaction => [r0]); + assert_receipts!(group, "unc_0" => r0 @ "unc_1", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_1); + assert_eq!(function_call_action.deposit, 0); + } + => [r1, ref0] ); + assert_receipts!(group, "unc_1" => r1 @ "unc_2", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::Transfer(TransferAction{deposit}), { + assert_eq!(*deposit, 1000000000); + } + => [ref1] ); + + assert_refund!(group, ref0 @ "unc_0"); + // For gas price difference + assert_refund!(group, ref1 @ "unc_0"); +} + +#[test] +fn test_create_account_with_transfer_and_full_key() { + let group = RuntimeGroup::new(3, 2, unc_test_contracts::rs_contract()); + let signer_sender = group.signers[0].clone(); + let signer_receiver = group.signers[1].clone(); + let signer_new_account = group.signers[2].clone(); + + let data = serde_json::json!([ + {"batch_create": { + "account_id": "unc_2", + }, "id": 0 }, + {"action_create_account": { + "promise_index": 0, + }, "id": 0 }, + {"action_transfer": { + "promise_index": 0, + "amount": "10000000000000000000000000", + }, "id": 0 }, + {"action_add_key_with_full_access": { + "promise_index": 0, + "public_key": to_base64(&borsh::to_vec(&signer_new_account.public_key).unwrap()), + "nonce": 0, + }, "id": 0 } + ]); + + let signed_transaction = SignedTransaction::from_actions( + 1, + signer_sender.account_id.clone(), + signer_receiver.account_id, + &signer_sender, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "call_promise".to_string(), + args: serde_json::to_vec(&data).unwrap(), + gas: GAS_1, + deposit: 0, + }))], + CryptoHash::default(), + ); + + let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); + for h in handles { + h.join().unwrap(); + } + + use unc_primitives::transaction::*; + assert_receipts!(group, signed_transaction => [r0]); + assert_receipts!(group, "unc_0" => r0 @ "unc_1", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_1); + assert_eq!(function_call_action.deposit, 0); + } + => [r1, ref0] ); + assert_receipts!(group, "unc_1" => r1 @ "unc_2", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::CreateAccount(CreateAccountAction{}), {}, + a1, Action::Transfer(TransferAction{deposit}), { + assert_eq!(*deposit, 10000000000000000000000000); + }, + a2, Action::AddKey(add_key_action), { + assert_eq!(add_key_action.public_key, signer_new_account.public_key); + assert_eq!(add_key_action.access_key.nonce, 0); + assert_eq!(add_key_action.access_key.permission, AccessKeyPermission::FullAccess); + } + => [ref1] ); + + assert_refund!(group, ref0 @ "unc_0"); + // For gas price difference + assert_refund!(group, ref1 @ "unc_0"); +} + +#[test] +fn test_account_factory() { + let group = RuntimeGroup::new(3, 2, unc_test_contracts::rs_contract()); + let signer_sender = group.signers[0].clone(); + let signer_receiver = group.signers[1].clone(); + let signer_new_account = group.signers[2].clone(); + + let data = serde_json::json!([ + {"batch_create": { + "account_id": "unc_2", + }, "id": 0 }, + {"action_create_account": { + "promise_index": 0, + }, "id": 0 }, + {"action_transfer": { + "promise_index": 0, + "amount": (TESTING_INIT_BALANCE / 2).to_string(), + }, "id": 0 }, + {"action_add_key_with_function_call": { + "promise_index": 0, + "public_key": to_base64(&borsh::to_vec(&signer_new_account.public_key).unwrap()), + "nonce": 0, + "allowance": (TESTING_INIT_BALANCE / 2).to_string(), + "receiver_id": "unc_1", + "method_names": "call_promise,hello" + }, "id": 0 }, + {"action_deploy_contract": { + "promise_index": 0, + "code": to_base64(unc_test_contracts::rs_contract()), + }, "id": 0 }, + {"action_function_call": { + "promise_index": 0, + "method_name": "call_promise", + "arguments": [ + {"create": { + "account_id": "unc_0", + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": GAS_3, + }, "id": 0} + ], + "amount": "0", + "gas": GAS_2, + }, "id": 0 }, + + {"then": { + "promise_index": 0, + "account_id": "unc_2", + "method_name": "call_promise", + "arguments": [ + {"create": { + "account_id": "unc_1", + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": GAS_3, + }, "id": 0} + ], + "amount": "0", + "gas": GAS_2, + }, "id": 1} + ]); + + let signed_transaction = SignedTransaction::from_actions( + 1, + signer_sender.account_id.clone(), + signer_receiver.account_id, + &signer_sender, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "call_promise".to_string(), + args: serde_json::to_vec(&data).unwrap(), + gas: GAS_1, + deposit: 0, + }))], + CryptoHash::default(), + ); + + let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); + for h in handles { + h.join().unwrap(); + } + + use unc_primitives::transaction::*; + assert_receipts!(group, signed_transaction => [r0]); + assert_receipts!(group, "unc_0" => r0 @ "unc_1", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_1); + assert_eq!(function_call_action.deposit, 0); + } + => [r1, r2, ref0] ); + + let data_id; + assert_receipts!(group, "unc_1" => r1 @ "unc_2", + ReceiptEnum::Action(ActionReceipt{actions, output_data_receivers, ..}), { + assert_eq!(output_data_receivers.len(), 1); + data_id = output_data_receivers[0].data_id; + assert_eq!(output_data_receivers[0].receiver_id, "unc_2"); + }, + actions, + a0, Action::CreateAccount(CreateAccountAction{}), {}, + a1, Action::Transfer(TransferAction{deposit}), { + assert_eq!(*deposit, TESTING_INIT_BALANCE / 2); + }, + a2, Action::AddKey(add_key_action), { + assert_eq!(add_key_action.public_key, signer_new_account.public_key); + assert_eq!(add_key_action.access_key.nonce, 0); + assert_eq!(add_key_action.access_key.permission, AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(TESTING_INIT_BALANCE / 2), + receiver_id: "unc_1".parse().unwrap(), + method_names: vec!["call_promise".to_string(), "hello".to_string()], + })); + }, + a3, Action::DeployContract(DeployContractAction{code}), { + assert_eq!(code, unc_test_contracts::rs_contract()); + }, + a4, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_2); + assert_eq!(function_call_action.deposit, 0); + } + => [r3, ref1] ); + assert_receipts!(group, "unc_1" => r2 @ "unc_2", + ReceiptEnum::Action(ActionReceipt{actions, input_data_ids, ..}), { + assert_eq!(input_data_ids, &[data_id]); + }, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_2); + assert_eq!(function_call_action.deposit, 0); + } + => [r4, ref2] ); + assert_receipts!(group, "unc_2" => r3 @ "unc_0", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_3); + assert_eq!(function_call_action.deposit, 0); + } + => [ref3] ); + assert_receipts!(group, "unc_2" => r4 @ "unc_1", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_3); + assert_eq!(function_call_action.deposit, 0); + } + => [ref4] ); + + assert_refund!(group, ref0 @ "unc_0"); + assert_refund!(group, ref1 @ "unc_0"); + assert_refund!(group, ref2 @ "unc_0"); + assert_refund!(group, ref3 @ "unc_0"); + assert_refund!(group, ref4 @ "unc_0"); +} + +#[test] +fn test_create_account_add_key_call_delete_key_delete_account() { + let group = RuntimeGroup::new(4, 3, unc_test_contracts::rs_contract()); + let signer_sender = group.signers[0].clone(); + let signer_receiver = group.signers[1].clone(); + let signer_new_account = group.signers[2].clone(); + + let data = serde_json::json!([ + {"batch_create": { + "account_id": "unc_3", + }, "id": 0 }, + {"action_create_account": { + "promise_index": 0, + }, "id": 0 }, + {"action_transfer": { + "promise_index": 0, + "amount": (TESTING_INIT_BALANCE / 2).to_string(), + }, "id": 0 }, + {"action_add_key_with_full_access": { + "promise_index": 0, + "public_key": to_base64(&borsh::to_vec(&signer_new_account.public_key).unwrap()), + "nonce": 1, + }, "id": 0 }, + {"action_deploy_contract": { + "promise_index": 0, + "code": to_base64(unc_test_contracts::rs_contract()), + }, "id": 0 }, + {"action_function_call": { + "promise_index": 0, + "method_name": "call_promise", + "arguments": [ + {"create": { + "account_id": "unc_0", + "method_name": "call_promise", + "arguments": [], + "amount": "0", + "gas": GAS_3, + }, "id": 0} + ], + "amount": "0", + "gas": GAS_2, + }, "id": 0 }, + {"action_delete_key": { + "promise_index": 0, + "public_key": to_base64(&borsh::to_vec(&signer_new_account.public_key).unwrap()), + "nonce": 0, + }, "id": 0 }, + {"action_delete_account": { + "promise_index": 0, + "beneficiary_id": "unc_2" + }, "id": 0 }, + ]); + + let signed_transaction = SignedTransaction::from_actions( + 1, + signer_sender.account_id.clone(), + signer_receiver.account_id, + &signer_sender, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "call_promise".to_string(), + args: serde_json::to_vec(&data).unwrap(), + gas: GAS_1, + deposit: 0, + }))], + CryptoHash::default(), + ); + + let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); + for h in handles { + h.join().unwrap(); + } + + use unc_primitives::transaction::*; + assert_receipts!(group, signed_transaction => [r0]); + assert_receipts!(group, "unc_0" => r0 @ "unc_1", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_1); + assert_eq!(function_call_action.deposit, 0); + } + => [r1, ref0] ); + assert_receipts!(group, "unc_1" => r1 @ "unc_3", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::CreateAccount(CreateAccountAction{}), {}, + a1, Action::Transfer(TransferAction{deposit}), { + assert_eq!(*deposit, TESTING_INIT_BALANCE / 2); + }, + a2, Action::AddKey(add_key_action), { + assert_eq!(add_key_action.public_key, signer_new_account.public_key); + assert_eq!(add_key_action.access_key.nonce, 1); + assert_eq!(add_key_action.access_key.permission, AccessKeyPermission::FullAccess); + }, + a3, Action::DeployContract(DeployContractAction{code}), { + assert_eq!(code, unc_test_contracts::rs_contract()); + }, + a4, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_2); + assert_eq!(function_call_action.deposit, 0); + }, + a5, Action::DeleteKey(delete_key_action), { + assert_eq!(delete_key_action.public_key, signer_new_account.public_key); + }, + a6, Action::DeleteAccount(DeleteAccountAction{beneficiary_id}), { + assert_eq!(beneficiary_id, "unc_2"); + } + => [r2, r3, ref1] ); + + assert_receipts!(group, "unc_3" => r2 @ "unc_0", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_3); + assert_eq!(function_call_action.deposit, 0); + } + => [ref2] ); + assert_refund!(group, r3 @ "unc_2"); + + assert_refund!(group, ref0 @ "unc_0"); + assert_refund!(group, ref1 @ "unc_0"); + assert_refund!(group, ref2 @ "unc_0"); +} + +#[test] +fn test_transfer_64len_hex() { + let pk = InMemorySigner::from_seed("test_hex".parse().unwrap(), KeyType::ED25519, "test_hex"); + let account_id = AccountId::try_from(hex::encode(pk.public_key.unwrap_as_ed25519().0)).unwrap(); + + let group = RuntimeGroup::new_with_account_ids( + vec!["unc_0".parse().unwrap(), "unc_1".parse().unwrap(), account_id.clone()], + 2, + unc_test_contracts::rs_contract(), + ); + let signer_sender = group.signers[0].clone(); + let signer_receiver = group.signers[1].clone(); + + let data = serde_json::json!([ + {"batch_create": { + "account_id": account_id, + }, "id": 0 }, + {"action_transfer": { + "promise_index": 0, + "amount": (TESTING_INIT_BALANCE / 2).to_string(), + }, "id": 0 }, + ]); + + let signed_transaction = SignedTransaction::from_actions( + 1, + signer_sender.account_id.clone(), + signer_receiver.account_id, + &signer_sender, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "call_promise".to_string(), + args: serde_json::to_vec(&data).unwrap(), + gas: GAS_1, + deposit: 0, + }))], + CryptoHash::default(), + ); + + let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); + for h in handles { + h.join().unwrap(); + } + + use unc_primitives::transaction::*; + assert_receipts!(group, signed_transaction => [r0]); + assert_receipts!(group, "unc_0" => r0 @ "unc_1", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_1); + assert_eq!(function_call_action.deposit, 0); + } + => [r1, ref0] ); + assert_receipts!(group, "unc_1" => r1 @ account_id.as_str(), + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::Transfer(TransferAction{deposit}), { + assert_eq!(*deposit, TESTING_INIT_BALANCE / 2); + } + => [ref1] ); + assert_refund!(group, ref0 @ "unc_0"); + assert_refund!(group, ref1 @ "unc_0"); +} + +#[test] +fn test_create_transfer_64len_hex_fail() { + let pk = InMemorySigner::from_seed("test_hex".parse().unwrap(), KeyType::ED25519, "test_hex"); + let account_id = AccountId::try_from(hex::encode(pk.public_key.unwrap_as_ed25519().0)).unwrap(); + + let group = RuntimeGroup::new_with_account_ids( + vec!["unc_0".parse().unwrap(), "unc_1".parse().unwrap(), account_id.clone()], + 2, + unc_test_contracts::rs_contract(), + ); + let signer_sender = group.signers[0].clone(); + let signer_receiver = group.signers[1].clone(); + + let data = serde_json::json!([ + {"batch_create": { + "account_id": account_id, + }, "id": 0 }, + {"action_create_account": { + "promise_index": 0, + }, "id": 0 }, + {"action_transfer": { + "promise_index": 0, + "amount": (TESTING_INIT_BALANCE / 2).to_string(), + }, "id": 0 }, + ]); + + let signed_transaction = SignedTransaction::from_actions( + 1, + signer_sender.account_id.clone(), + signer_receiver.account_id, + &signer_sender, + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "call_promise".to_string(), + args: serde_json::to_vec(&data).unwrap(), + gas: GAS_1, + deposit: 0, + }))], + CryptoHash::default(), + ); + + let handles = RuntimeGroup::start_runtimes(group.clone(), vec![signed_transaction.clone()]); + for h in handles { + h.join().unwrap(); + } + + use unc_primitives::transaction::*; + assert_receipts!(group, signed_transaction => [r0]); + assert_receipts!(group, "unc_0" => r0 @ "unc_1", + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::FunctionCall(function_call_action), { + assert_eq!(function_call_action.gas, GAS_1); + assert_eq!(function_call_action.deposit, 0); + } + => [r1, ref0] ); + assert_receipts!(group, "unc_1" => r1 @ account_id.as_str(), + ReceiptEnum::Action(ActionReceipt{actions, ..}), {}, + actions, + a0, Action::CreateAccount(CreateAccountAction{}), {}, + a1, Action::Transfer(TransferAction{deposit}), { + assert_eq!(*deposit, TESTING_INIT_BALANCE / 2); + } + => [ref1, ref2] ); + assert_refund!(group, ref0 @ "unc_0"); + assert_refund!(group, ref1 @ "unc_1"); + assert_refund!(group, ref2 @ "unc_0"); +} diff --git a/runtime/unc-test-contracts/Cargo.toml b/runtime/unc-test-contracts/Cargo.toml new file mode 100644 index 000000000..60099974c --- /dev/null +++ b/runtime/unc-test-contracts/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "unc-test-contracts" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +description = "A collection of smart-contract used in framework tests." +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +once_cell.workspace = true +wat.workspace = true +wasm-encoder.workspace = true +wasm-smith.workspace = true +rand = { workspace = true, features = ["small_rng"] } +arbitrary.workspace = true + +[features] +nightly = [] diff --git a/runtime/unc-test-contracts/README.md b/runtime/unc-test-contracts/README.md new file mode 100644 index 000000000..20f9bd6ad --- /dev/null +++ b/runtime/unc-test-contracts/README.md @@ -0,0 +1,21 @@ +A collection of smart-contract used in framework tests. + +Rust contracts are built via `build.rs`, the Assembly Script contract +is build manually and committed to the git repository. +`res/unc_evm.wasm` and `res/ZombieOwnership.bin` are taken from + +and it's for reproduce a performance issue encountered in EVM +contracts. + +If you want to use a contract from rust core, add + +```toml +[dev-dependencies] +unc-test-contracts = { path = "../unc-test-contracts" } +``` + +to the Cargo.toml and use `unc_test_contract::rs_contract()`. + +If you want to use a contract from an integration test, you can read +the wasm file directly from the `./res` directory. To populate +`./res`, you need to make sure that this crate was compiled. diff --git a/runtime/unc-test-contracts/build.rs b/runtime/unc-test-contracts/build.rs new file mode 100644 index 000000000..dbcf3b893 --- /dev/null +++ b/runtime/unc-test-contracts/build.rs @@ -0,0 +1,76 @@ +use std::process::Command; + +type Error = Box; + +fn main() { + if let Err(err) = try_main() { + eprintln!("{}", err); + std::process::exit(1); + } +} + +fn try_main() -> Result<(), Error> { + build_contract("./test-contract-rs", &["--features", "latest_protocol"], "test_contract_rs")?; + build_contract( + "./test-contract-rs", + &["--features", "latest_protocol,nightly"], + "nightly_test_contract_rs", + )?; + build_contract("./contract-for-fuzzing-rs", &[], "contract_for_fuzzing_rs")?; + build_contract("./estimator-contract", &[], "stable_estimator_contract")?; + build_contract( + "./estimator-contract", + &["--features", "nightly"], + "nightly_estimator_contract", + )?; + Ok(()) +} + +fn build_contract(dir: &str, args: &[&str], output: &str) -> Result<(), Error> { + let target_dir = out_dir(); + + let mut cmd = cargo_build_cmd(&target_dir); + cmd.args(args); + cmd.current_dir(dir); + check_status(cmd)?; + + let src = + target_dir.join(format!("wasm32-unknown-unknown/release/{}.wasm", dir.replace('-', "_"))); + std::fs::copy(&src, format!("./res/{}.wasm", output)) + .map_err(|err| format!("failed to copy `{}`: {}", src.display(), err))?; + println!("cargo:rerun-if-changed=./{}/src/lib.rs", dir); + println!("cargo:rerun-if-changed=./{}/Cargo.toml", dir); + Ok(()) +} + +fn cargo_build_cmd(target_dir: &std::path::Path) -> Command { + let mut res = Command::new("cargo"); + + res.env_remove("CARGO_BUILD_RUSTFLAGS"); + res.env_remove("CARGO_ENCODED_RUSTFLAGS"); + res.env_remove("RUSTC_WORKSPACE_WRAPPER"); + + res.env("RUSTFLAGS", "-Dwarnings"); + res.env("CARGO_TARGET_DIR", target_dir); + + res.args(["build", "--target=wasm32-unknown-unknown", "--release"]); + + res +} + +fn check_status(mut cmd: Command) -> Result<(), Error> { + cmd.status() + .map_err(|err| format!("command `{cmd:?}` failed to run: {err}")) + .and_then(|status| { + if status.success() { + Ok(()) + } else { + Err(format!("command `{cmd:?}` exited with non-zero status: {status:?}")) + } + }) + .map_err(Error::from) +} + +fn out_dir() -> std::path::PathBuf { + std::env::var("OUT_DIR").unwrap().into() +} diff --git a/runtime/unc-test-contracts/contract-for-fuzzing-rs/Cargo.lock b/runtime/unc-test-contracts/contract-for-fuzzing-rs/Cargo.lock new file mode 100644 index 000000000..e2d40b98a --- /dev/null +++ b/runtime/unc-test-contracts/contract-for-fuzzing-rs/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "contract-for-fuzzing-rs" +version = "0.1.0" diff --git a/runtime/unc-test-contracts/contract-for-fuzzing-rs/Cargo.toml b/runtime/unc-test-contracts/contract-for-fuzzing-rs/Cargo.toml new file mode 100644 index 000000000..e8542dbe2 --- /dev/null +++ b/runtime/unc-test-contracts/contract-for-fuzzing-rs/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "contract-for-fuzzing-rs" +version = "0.1.0" +authors = ["Hello Inc "] +publish = false +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +[profile.release] +codegen-units = 1 +# Tell `rustc` to optimize for small code size. +opt-level = "z" +strip = true +lto = true +debug = false +panic = "abort" +rpath = false +debug-assertions = false +incremental = false + +[workspace] +members = [] diff --git a/runtime/unc-test-contracts/contract-for-fuzzing-rs/src/lib.rs b/runtime/unc-test-contracts/contract-for-fuzzing-rs/src/lib.rs new file mode 100644 index 000000000..32ea3e7b9 --- /dev/null +++ b/runtime/unc-test-contracts/contract-for-fuzzing-rs/src/lib.rs @@ -0,0 +1,301 @@ +#![allow(clippy::all)] + +use std::mem::size_of; + +#[allow(unused)] +extern "C" { + // ############# + // # Registers # + // ############# + fn read_register(register_id: u64, ptr: u64); + fn register_len(register_id: u64) -> u64; + // ############### + // # Context API # + // ############### + fn current_account_id(register_id: u64); + fn signer_account_id(register_id: u64); + fn signer_account_pk(register_id: u64); + fn predecessor_account_id(register_id: u64); + fn input(register_id: u64); + fn block_index() -> u64; + fn block_timestamp() -> u64; + fn epoch_height() -> u64; + fn storage_usage() -> u64; + // ################# + // # Economics API # + // ################# + fn account_balance(balance_ptr: u64); + fn attached_deposit(balance_ptr: u64); + fn prepaid_gas() -> u64; + fn used_gas() -> u64; + // ############ + // # Math API # + // ############ + fn random_seed(register_id: u64); + fn sha256(value_len: u64, value_ptr: u64, register_id: u64); + // ##################### + // # Miscellaneous API # + // ##################### + fn value_return(value_len: u64, value_ptr: u64); + fn panic(); + fn panic_utf8(len: u64, ptr: u64); + fn log_utf8(len: u64, ptr: u64); + fn log_utf16(len: u64, ptr: u64); + fn abort(msg_ptr: u32, filename_ptr: u32, line: u32, col: u32); + // ################ + // # Promises API # + // ################ + fn promise_create( + account_id_len: u64, + account_id_ptr: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: u64, + ) -> u64; + fn promise_then( + promise_index: u64, + account_id_len: u64, + account_id_ptr: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: u64, + ) -> u64; + fn promise_and(promise_idx_ptr: u64, promise_idx_count: u64) -> u64; + fn promise_batch_create(account_id_len: u64, account_id_ptr: u64) -> u64; + fn promise_batch_then(promise_index: u64, account_id_len: u64, account_id_ptr: u64) -> u64; + // ####################### + // # Promise API actions # + // ####################### + fn promise_batch_action_create_account(promise_index: u64); + fn promise_batch_action_deploy_contract(promise_index: u64, code_len: u64, code_ptr: u64); + fn promise_batch_action_function_call( + promise_index: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: u64, + ); + fn promise_batch_action_transfer(promise_index: u64, amount_ptr: u64); + fn promise_batch_action_stake( + promise_index: u64, + amount_ptr: u64, + public_key_len: u64, + public_key_ptr: u64, + ); + fn promise_batch_action_add_key_with_full_access( + promise_index: u64, + public_key_len: u64, + public_key_ptr: u64, + nonce: u64, + ); + fn promise_batch_action_add_key_with_function_call( + promise_index: u64, + public_key_len: u64, + public_key_ptr: u64, + nonce: u64, + allowance_ptr: u64, + receiver_id_len: u64, + receiver_id_ptr: u64, + method_names_len: u64, + method_names_ptr: u64, + ); + fn promise_batch_action_delete_key( + promise_index: u64, + public_key_len: u64, + public_key_ptr: u64, + ); + fn promise_batch_action_delete_account( + promise_index: u64, + beneficiary_id_len: u64, + beneficiary_id_ptr: u64, + ); + // ####################### + // # Promise API results # + // ####################### + fn promise_results_count() -> u64; + fn promise_result(result_idx: u64, register_id: u64) -> u64; + fn promise_return(promise_id: u64); + // ############### + // # Storage API # + // ############### + fn storage_write( + key_len: u64, + key_ptr: u64, + value_len: u64, + value_ptr: u64, + register_id: u64, + ) -> u64; + fn storage_read(key_len: u64, key_ptr: u64, register_id: u64) -> u64; + fn storage_remove(key_len: u64, key_ptr: u64, register_id: u64) -> u64; + fn storage_has_key(key_len: u64, key_ptr: u64) -> u64; + fn storage_iter_prefix(prefix_len: u64, prefix_ptr: u64) -> u64; + fn storage_iter_range(start_len: u64, start_ptr: u64, end_len: u64, end_ptr: u64) -> u64; + fn storage_iter_next(iterator_id: u64, key_register_id: u64, value_register_id: u64) -> u64; + // ############### + // # Validator API # + // ############### + fn validator_stake(account_id_len: u64, account_id_ptr: u64, stake_ptr: u64); + fn validator_total_stake(stake_ptr: u64); + // ################# + // # alt_bn128 API # + // ################# + fn alt_bn128_g1_multiexp(value_len: u64, value_ptr: u64, register_id: u64); + fn alt_bn128_g1_sum(value_len: u64, value_ptr: u64, register_id: u64); + fn alt_bn128_pairing_check(value_len: u64, value_ptr: u64) -> u64; +} + +#[no_mangle] +pub unsafe fn number_from_input() { + input(0); + let value = [0u8; size_of::()]; + read_register(0, value.as_ptr() as u64); + value_return(value.len() as u64, &value as *const u8 as u64); +} + +#[no_mangle] +pub unsafe fn count_sum() { + input(0); + let data = [0u8; size_of::()]; + read_register(0, data.as_ptr() as u64); + + let number_of_numbers = u64::from_le_bytes(data); + + let mut sum: u64 = 0; + + for i in 0..number_of_numbers { + promise_result(i, 0); + + let data = [0u8; size_of::()]; + read_register(0, data.as_ptr() as u64); + + let number = u64::from_le_bytes(data); + sum += number; + } + + let value = sum.to_le_bytes(); + + value_return(value.len() as u64, &value as *const u8 as u64); +} + +#[no_mangle] +pub unsafe fn sum_of_numbers() { + input(0); + let data = [0u8; size_of::()]; + read_register(0, data.as_ptr() as u64); + + let number_of_numbers = u64::from_le_bytes(data); + let mut promise_ids = vec![]; + + for _ in 1..=number_of_numbers { + let i: u64 = 1; + let method_name = "number_from_input".as_bytes(); + let account_id = { + signer_account_id(0); + let result = vec![0; register_len(0) as usize]; + read_register(0, result.as_ptr() as *const u64 as u64); + result + }; + let arguments = i.to_le_bytes(); + + promise_ids.push(promise_create( + account_id.len() as u64, + account_id.as_ptr() as u64, + method_name.len() as u64, + method_name.as_ptr() as u64, + arguments.len() as u64, + arguments.as_ptr() as u64, + 0, + 3_000_000_000, + )); + } + + let promise_idx = promise_and(promise_ids.as_ptr() as u64, promise_ids.len() as u64); + + { + let method_name = "count_sum".as_bytes(); + let account_id = { + signer_account_id(0); + let result = vec![0; register_len(0) as usize]; + read_register(0, result.as_ptr() as *const u64 as u64); + result + }; + let arguments = number_of_numbers.to_le_bytes(); + + promise_then( + promise_idx, + account_id.len() as u64, + account_id.as_ptr() as u64, + method_name.len() as u64, + method_name.as_ptr() as u64, + arguments.len() as u64, + arguments.as_ptr() as u64, + 0, + 3_000_000_000_000, + ); + } +} + +// Function that does not do anything at all. +#[no_mangle] +pub fn noop() {} + +#[no_mangle] +pub unsafe fn data_producer() { + input(0); + let data = [0u8; size_of::()]; + read_register(0, data.as_ptr() as u64); + let size = u64::from_le_bytes(data); + + let data = vec![0u8; size as usize]; + value_return(data.len() as _, data.as_ptr() as _); +} + +#[no_mangle] +pub unsafe fn data_receipt_with_size() { + input(0); + let data = [0u8; size_of::()]; + read_register(0, data.as_ptr() as u64); + let size = u64::from_le_bytes(data); + + let buf = [0u8; 1000]; + current_account_id(0); + let buf_len = register_len(0); + read_register(0, buf.as_ptr() as _); + + let method_name = b"data_producer"; + let args = size.to_le_bytes(); + let amount = 0u128; + let gas = prepaid_gas(); + let id = promise_create( + buf_len, + buf.as_ptr() as _, + method_name.len() as _, + method_name.as_ptr() as _, + args.len() as _, + args.as_ptr() as _, + &amount as *const u128 as *const u64 as u64, + gas / 20, + ); + + let method_name = b"noop"; + let args = b""; + promise_then( + id, + buf_len, + buf.as_ptr() as _, + method_name.len() as _, + method_name.as_ptr() as _, + args.len() as _, + args.as_ptr() as _, + &amount as *const u128 as *const u64 as u64, + gas / 3, + ); +} diff --git a/runtime/unc-test-contracts/estimator-contract/Cargo.lock b/runtime/unc-test-contracts/estimator-contract/Cargo.lock new file mode 100644 index 000000000..3fb28fe94 --- /dev/null +++ b/runtime/unc-test-contracts/estimator-contract/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "estimator-contract" +version = "0.1.0" diff --git a/runtime/unc-test-contracts/estimator-contract/Cargo.toml b/runtime/unc-test-contracts/estimator-contract/Cargo.toml new file mode 100644 index 000000000..493dce28b --- /dev/null +++ b/runtime/unc-test-contracts/estimator-contract/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "estimator-contract" +version = "0.1.0" +authors = ["Hello Inc "] +publish = false +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[profile.release] +codegen-units = 1 +# Tell `rustc` to optimize for small code size. +opt-level = "z" +strip = true +lto = true +debug = false +panic = "abort" + +[workspace] +members = [] + +[features] +nightly = [] diff --git a/runtime/unc-test-contracts/estimator-contract/src/lib.rs b/runtime/unc-test-contracts/estimator-contract/src/lib.rs new file mode 100644 index 000000000..f642bcaa6 --- /dev/null +++ b/runtime/unc-test-contracts/estimator-contract/src/lib.rs @@ -0,0 +1,1038 @@ +#![no_std] +#![allow(non_snake_case)] +#![allow(clippy::all)] + +#[panic_handler] +#[no_mangle] +pub fn panic(_info: &::core::panic::PanicInfo) -> ! { + core::arch::wasm32::unreachable() +} + +#[allow(unused)] +extern "C" { + // ############# + // # Registers # + // ############# + fn read_register(register_id: u64, ptr: u64); + fn register_len(register_id: u64) -> u64; + fn write_register(register_id: u64, data_len: u64, data_ptr: u64); + // ############### + // # Context API # + // ############### + fn current_account_id(register_id: u64); + fn signer_account_id(register_id: u64); + fn signer_account_pk(register_id: u64); + fn predecessor_account_id(register_id: u64); + fn input(register_id: u64); + fn block_index() -> u64; + fn storage_usage() -> u64; + fn epoch_height() -> u64; + // ################# + // # Economics API # + // ################# + fn account_balance(balance_ptr: u64); + fn attached_deposit(balance_ptr: u64); + fn prepaid_gas() -> u64; + fn used_gas() -> u64; + // ############ + // # Math API # + // ############ + fn alt_bn128_g1_multiexp(value_len: u64, value_ptr: u64, register_id: u64); + fn alt_bn128_g1_sum(value_len: u64, value_ptr: u64, register_id: u64); + fn alt_bn128_pairing_check(value_len: u64, value_ptr: u64) -> u64; + fn random_seed(register_id: u64); + fn sha256(value_len: u64, value_ptr: u64, register_id: u64); + fn keccak256(value_len: u64, value_ptr: u64, register_id: u64); + fn keccak512(value_len: u64, value_ptr: u64, register_id: u64); + fn ripemd160(value_len: u64, value_ptr: u64, register_id: u64); + fn ecrecover( + hash_len: u64, + hash_ptr: u64, + sig_len: u64, + sig_ptr: u64, + v: u64, + malleability_flag: u64, + register_id: u64, + ) -> u64; + fn ed25519_verify( + sig_len: u64, + sig_ptr: u64, + msg_len: u64, + msg_ptr: u64, + pub_key_len: u64, + pub_key_ptr: u64, + ) -> u64; + // ##################### + // # Miscellaneous API # + // ##################### + fn value_return(value_len: u64, value_ptr: u64); + fn log_utf8(len: u64, ptr: u64); + fn log_utf16(len: u64, ptr: u64); + fn abort(msg_ptr: u32, filename_ptr: u32, line: u32, col: u32); + // ################ + // # Promises API # + // ################ + fn promise_create( + account_id_len: u64, + account_id_ptr: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: u64, + ) -> u64; + fn promise_then( + promise_index: u64, + account_id_len: u64, + account_id_ptr: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: u64, + ) -> u64; + fn promise_and(promise_idx_ptr: u64, promise_idx_count: u64) -> u64; + fn promise_batch_create(account_id_len: u64, account_id_ptr: u64) -> u64; + fn promise_batch_then(promise_index: u64, account_id_len: u64, account_id_ptr: u64) -> u64; + // ####################### + // # Promise API actions # + // ####################### + fn promise_batch_action_create_account(promise_index: u64); + fn promise_batch_action_deploy_contract(promise_index: u64, code_len: u64, code_ptr: u64); + fn promise_batch_action_function_call( + promise_index: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: u64, + ); + fn promise_batch_action_transfer(promise_index: u64, amount_ptr: u64); + fn promise_batch_action_stake( + promise_index: u64, + amount_ptr: u64, + public_key_len: u64, + public_key_ptr: u64, + ); + fn promise_batch_action_add_key_with_full_access( + promise_index: u64, + public_key_len: u64, + public_key_ptr: u64, + nonce: u64, + ); + fn promise_batch_action_add_key_with_function_call( + promise_index: u64, + public_key_len: u64, + public_key_ptr: u64, + nonce: u64, + allowance_ptr: u64, + receiver_id_len: u64, + receiver_id_ptr: u64, + method_names_len: u64, + method_names_ptr: u64, + ); + fn promise_batch_action_delete_key( + promise_index: u64, + public_key_len: u64, + public_key_ptr: u64, + ); + fn promise_batch_action_delete_account( + promise_index: u64, + beneficiary_id_len: u64, + beneficiary_id_ptr: u64, + ); + // ####################### + // # Promise API results # + // ####################### + fn promise_results_count() -> u64; + fn promise_result(result_idx: u64, register_id: u64) -> u64; + fn promise_return(promise_id: u64); + // ############### + // # Storage API # + // ############### + fn storage_write( + key_len: u64, + key_ptr: u64, + value_len: u64, + value_ptr: u64, + register_id: u64, + ) -> u64; + fn storage_read(key_len: u64, key_ptr: u64, register_id: u64) -> u64; + fn storage_remove(key_len: u64, key_ptr: u64, register_id: u64) -> u64; + fn storage_has_key(key_len: u64, key_ptr: u64) -> u64; +} + +// Function that does not do anything at all. +#[no_mangle] +pub fn noop() {} + +// Function that we use to measure `base` cost by calling `block_height` many times. +#[no_mangle] +pub unsafe fn base_1M() { + for _ in 0..1_000_000 { + block_index(); + } +} + +// Function to measure `read_memory_base` and `read_memory_byte` many times. +// Reads 10b 10k times from memory. +#[no_mangle] +pub unsafe fn read_memory_10b_10k() { + let buffer = [0u8; 10]; + for _ in 0..10_000 { + value_return(buffer.len() as u64, buffer.as_ptr() as *const u64 as u64); + } +} + +// Function to measure `read_memory_base` and `read_memory_byte` many times. +// Reads 1Mib 10k times from memory. +#[no_mangle] +pub unsafe fn read_memory_1Mib_10k() { + let buffer = [0u8; 1024 * 1024]; + for _ in 0..10_000 { + value_return(buffer.len() as u64, buffer.as_ptr() as *const u64 as u64); + } +} + +// Function to measure `write_memory_base` and `write_memory_byte` many times. +// Writes 10b 10k times into memory. Includes `read_register` costs. +#[no_mangle] +pub unsafe fn write_memory_10b_10k() { + let buffer = [0u8; 10]; + write_register(0, buffer.len() as u64, buffer.as_ptr() as *const u64 as u64); + for _ in 0..10_000 { + read_register(0, buffer.as_ptr() as *const u64 as u64); + } +} + +// Function to measure `write_memory_base` and `write_memory_byte` many times. +// Writes 1Mib 10k times into memory. Includes `read_register` costs. +#[no_mangle] +pub unsafe fn write_memory_1Mib_10k() { + let buffer = [0u8; 1024 * 1024]; + write_register(0, buffer.len() as u64, buffer.as_ptr() as *const u64 as u64); + for _ in 0..10_000 { + read_register(0, buffer.as_ptr() as *const u64 as u64); + } +} + +// Function to measure `read_register_base` and `read_register_byte` many times. +// Reads 10b 10k times from register. +#[no_mangle] +pub unsafe fn read_register_10b_10k() { + let buffer = [0u8; 10]; + write_register(0, buffer.len() as u64, buffer.as_ptr() as *const u64 as u64); + for _ in 0..10_000 { + value_return(core::u64::MAX, 0); + } +} + +// Function to measure `read_register_base` and `read_register_byte` many times. +// Reads 1Mib 10k times from register. +#[no_mangle] +pub unsafe fn read_register_1Mib_10k() { + let buffer = [0u8; 1024 * 1024]; + write_register(0, buffer.len() as u64, buffer.as_ptr() as *const u64 as u64); + for _ in 0..10_000 { + value_return(core::u64::MAX, 0); + } +} + +// Function to measure `write_register_base` and `write_register_byte` many times. +// Writes 10b 10k times to register. +#[no_mangle] +pub unsafe fn write_register_10b_10k() { + let buffer = [0u8; 10]; + for _ in 0..10_000 { + write_register(0, buffer.len() as u64, buffer.as_ptr() as *const u64 as u64); + } +} + +// Function to measure `write_register_base` and `write_register_byte` many times. +// Writes 1Mib 10k times to register. +#[no_mangle] +pub unsafe fn write_register_1Mib_10k() { + let buffer = [0u8; 1024 * 1024]; + for _ in 0..10_000 { + write_register(0, buffer.len() as u64, buffer.as_ptr() as *const u64 as u64); + } +} + +// Function to measure `utf8_decoding_base`, `utf8_decoding_byte`, `log_base`, and `log_byte`; +// It actually measures them together with `read_memory_base` and `read_memory_byte`. +// Write utf8 10b 10k times into log. +#[no_mangle] +pub unsafe fn utf8_log_10b_10k() { + let buffer = [65u8; 10]; + for _ in 0..10_000 { + log_utf8(buffer.len() as u64, buffer.as_ptr() as *const u64 as u64); + } +} + +// Function to measure `utf8_decoding_base`, `utf8_decoding_byte`, `log_base`, and `log_byte`; +// It actually measures them together with `read_memory_base` and `read_memory_byte`. +// Write utf8 10kib 1k times into log. +#[no_mangle] +pub unsafe fn utf8_log_10kib_1k() { + let buffer = [65u8; 10240]; + for _ in 0..1_000 { + log_utf8(buffer.len() as u64, buffer.as_ptr() as *const u64 as u64); + } +} + +// Nul-terminated versions. +// Function to measure `utf8_decoding_base`, `utf8_decoding_byte`, `log_base`, and `log_byte`; +// It actually measures them together with `read_memory_base` and `read_memory_byte`. +// Write utf8 10b 10k times into log. +#[no_mangle] +pub unsafe fn nul_utf8_log_10b_10k() { + let mut buffer = [65u8; 10]; + buffer[buffer.len() - 1] = 0; + for _ in 0..10_000 { + log_utf8(core::u64::MAX, buffer.as_ptr() as *const u64 as u64); + } +} + +// Nul-terminated versions. +// Function to measure `utf8_decoding_base`, `utf8_decoding_byte`, `log_base`, and `log_byte`; +// It actually measures them together with `read_memory_base` and `read_memory_byte`. +// Write utf8 10kib 1k times into log. +#[no_mangle] +pub unsafe fn nul_utf8_log_10kib_1k() { + let mut buffer = [65u8; 10240]; + buffer[buffer.len() - 1] = 0; + for _ in 0..1_000 { + log_utf8(core::u64::MAX, buffer.as_ptr() as *const u64 as u64); + } +} + +// Function to measure `utf16_decoding_base`, `utf16_decoding_byte`, `log_base`, and `log_byte`; +// It actually measures them together with `read_memory_base` and `read_memory_byte`. +// Write utf16 10b 10k times into log. +#[no_mangle] +pub unsafe fn utf16_log_10b_10k() { + let buffer = [65u8; 10]; + for _ in 0..10_000 { + log_utf16(buffer.len() as u64, buffer.as_ptr() as *const u64 as u64); + } +} + +// Function to measure `utf16_decoding_base`, `utf16_decoding_byte`, `log_base`, and `log_byte`; +// It actually measures them together with `read_memory_base` and `read_memory_byte`. +// Write utf16 10kib 1k times into log. +#[no_mangle] +pub unsafe fn utf16_log_10kib_1k() { + let buffer = [65u8; 10240]; + for _ in 0..1_000 { + log_utf16(buffer.len() as u64, buffer.as_ptr() as *const u64 as u64); + } +} + +// Nul-terminated versions. +// Function to measure `utf16_decoding_base`, `utf16_decoding_byte`, `log_base`, and `log_byte`; +// It actually measures them together with `read_memory_base` and `read_memory_byte`. +// Write utf16 10b 10k times into log. +#[no_mangle] +pub unsafe fn nul_utf16_log_10b_10k() { + let mut buffer = [65u8; 10]; + buffer[buffer.len() - 2] = 0; + buffer[buffer.len() - 1] = 0; + for _ in 0..10_000 { + log_utf16(core::u64::MAX, buffer.as_ptr() as *const u64 as u64); + } +} + +// Nul-terminated versions. +// Function to measure `utf16_decoding_base`, `utf16_decoding_byte`, `log_base`, and `log_byte`; +// It actually measures them together with `read_memory_base` and `read_memory_byte`. +// Write utf16 10kib 1k times into log. +#[no_mangle] +pub unsafe fn nul_utf16_log_10kib_1k() { + let mut buffer = [65u8; 10240]; + buffer[buffer.len() - 2] = 0; + buffer[buffer.len() - 1] = 0; + for _ in 0..1_000 { + log_utf16(core::u64::MAX, buffer.as_ptr() as *const u64 as u64); + } +} + +// Function to measure `sha256_base` and `sha256_byte`. Also measures `base`, `write_register_base`, +// and `write_register_byte`. However `sha256` computation is more expensive than register writing +// so we are okay overcharging it. +// Compute sha256 on 10b 10k times. +#[no_mangle] +pub unsafe fn sha256_10b_10k() { + let buffer = [65u8; 10]; + for _ in 0..10_000 { + sha256(buffer.len() as u64, buffer.as_ptr() as *const u64 as u64, 0); + } +} +// Function to measure `sha256_base` and `sha256_byte`. Also measures `base`, `write_register_base`, +// and `write_register_byte`. However `sha256` computation is more expensive than register writing +// so we are okay overcharging it. +// Compute sha256 on 10kib 10k times. +#[no_mangle] +pub unsafe fn sha256_10kib_10k() { + let buffer = [65u8; 10240]; + for _ in 0..10_000 { + sha256(buffer.len() as u64, buffer.as_ptr() as *const u64 as u64, 0); + } +} + +// Function to measure `keccak256_base` and `keccak256_byte`. Also measures `base`, `write_register_base`, +// and `write_register_byte`. However `keccak256` computation is more expensive than register writing +// so we are okay overcharging it. +// Compute keccak256 on 10b 10k times. +#[no_mangle] +pub unsafe fn keccak256_10b_10k() { + let buffer = [65u8; 10]; + for _ in 0..10_000 { + keccak256(buffer.len() as u64, buffer.as_ptr() as *const u64 as u64, 0); + } +} +// Function to measure `keccak256_base` and `keccak256_byte`. Also measures `base`, `write_register_base`, +// and `write_register_byte`. However `keccak256` computation is more expensive than register writing +// so we are okay overcharging it. +// Compute keccak256 on 10kib 10k times. +#[no_mangle] +pub unsafe fn keccak256_10kib_10k() { + let buffer = [65u8; 10240]; + for _ in 0..10_000 { + keccak256(buffer.len() as u64, buffer.as_ptr() as *const u64 as u64, 0); + } +} + +// Function to measure `keccak512_base` and `keccak512_byte`. Also measures `base`, `write_register_base`, +// and `write_register_byte`. However `keccak512` computation is more expensive than register writing +// so we are okay overcharging it. +// Compute keccak512 on 10b 10k times. +#[no_mangle] +pub unsafe fn keccak512_10b_10k() { + let buffer = [65u8; 10]; + for _ in 0..10_000 { + keccak512(buffer.len() as u64, buffer.as_ptr() as *const u64 as u64, 0); + } +} +// Function to measure `keccak512_base` and `keccak512_byte`. Also measures `base`, `write_register_base`, +// and `write_register_byte`. However `keccak512` computation is more expensive than register writing +// so we are okay overcharging it. +// Compute keccak512 on 10kib 10k times. +#[no_mangle] +pub unsafe fn keccak512_10kib_10k() { + let buffer = [65u8; 10240]; + for _ in 0..10_000 { + keccak512(buffer.len() as u64, buffer.as_ptr() as *const u64 as u64, 0); + } +} + +// Function to measure `ripemd160_base` and `ripemd160_block`. Also measures `base`, `write_register_base`, +// and `write_register_byte`. However `ripemd160` computation is more expensive than register writing +// so we are okay overcharging it. +// Compute ripemd160 on 10b 10k times. +#[no_mangle] +pub unsafe fn ripemd160_10b_10k() { + let buffer = [65u8; 10]; + for _ in 0..10_000 { + ripemd160(buffer.len() as u64, buffer.as_ptr() as *const u64 as u64, 0); + } +} +// Function to measure `ripemd160_base` and `ripemd160_block`. Also measures `base`, `write_register_base`, +// and `write_register_byte`. However `ripemd160` computation is more expensive than register writing +// so we are okay overcharging it. +// Compute ripemd160 on 10kib 10k times. +#[no_mangle] +pub unsafe fn ripemd160_10kib_10k() { + let buffer = [65u8; 10240]; + for _ in 0..10_000 { + ripemd160(buffer.len() as u64, buffer.as_ptr() as *const u64 as u64, 0); + } +} + +// Function to measure `ecrecover_base`. Also measures `base`, `write_register_base`, and +// `write_register_byte`. However `ecrecover` computation is more expensive than register writing +// so we are okay overcharging it. +// Compute ecrecover 10k times. +#[no_mangle] +pub unsafe fn ecrecover_10k() { + let hash_buffer: [u8; 32] = [ + 0x7d, 0xba, 0xf5, 0x58, 0xb0, 0xa1, 0xa5, 0xdc, 0x7a, 0x67, 0x20, 0x21, 0x17, 0xab, 0x14, + 0x3c, 0x1d, 0x86, 0x05, 0xa9, 0x83, 0xe4, 0xa7, 0x43, 0xbc, 0x06, 0xfc, 0xc0, 0x31, 0x62, + 0xdc, 0x0d, + ]; + let sig_buffer: [u8; 64] = [ + 0x5d, 0x99, 0xb6, 0xf7, 0xf6, 0xd1, 0xf7, 0x3d, 0x1a, 0x26, 0x49, 0x7f, 0x2b, 0x1c, 0x89, + 0xb2, 0x4c, 0x09, 0x93, 0x91, 0x3f, 0x86, 0xe9, 0xa2, 0xd0, 0x2c, 0xd6, 0x98, 0x87, 0xd9, + 0xc9, 0x4f, 0x3c, 0x88, 0x03, 0x58, 0x57, 0x9d, 0x81, 0x1b, 0x21, 0xdd, 0x1b, 0x7f, 0xd9, + 0xbb, 0x01, 0xc1, 0xd8, 0x1d, 0x10, 0xe6, 0x9f, 0x03, 0x84, 0xe6, 0x75, 0xc3, 0x2b, 0x39, + 0x64, 0x3b, 0xe8, 0x92, + ]; + + for _ in 0..10_000 { + ecrecover(32, hash_buffer.as_ptr() as _, 64, sig_buffer.as_ptr() as _, 0, 0, 0); + } +} + +/// Function to measure `ed25519_verify_base`. Also measures `base`, +/// `write_register_base`, and `write_register_byte`. However +/// `ed25519_verify_base` computation is more expensive than register writing so +/// we are okay overcharging it. +#[no_mangle] +pub unsafe fn ed25519_verify_32b_500() { + // private key: OReNDSAXOnl-U6Wki95ut01ehQW_9wcAF_utjzRNreg + // public key: M4QwJx4Sogjr0KcMI_gsvt-lEU6tgd9GWmgejE_JYlA + let public_key: [u8; 32] = [ + 51, 132, 48, 39, 30, 18, 162, 8, 235, 208, 167, 12, 35, 248, 44, 190, 223, 165, 17, 78, + 173, 129, 223, 70, 90, 104, 30, 140, 79, 201, 98, 80, + ]; + + // 32 bytes message ("kajdlfkjalkfjaklfjdkladjfkljadsk") + let message: [u8; 32] = [ + 107, 97, 106, 100, 108, 102, 107, 106, 97, 108, 107, 102, 106, 97, 107, 108, 102, 106, 100, + 107, 108, 97, 100, 106, 102, 107, 108, 106, 97, 100, 115, 107, + ]; + + let signature: [u8; 64] = [ + 149, 193, 241, 158, 225, 107, 146, 130, 116, 224, 233, 136, 232, 153, 211, 60, 115, 141, + 183, 174, 15, 52, 27, 186, 34, 68, 124, 158, 81, 3, 8, 76, 93, 28, 91, 68, 252, 151, 172, + 240, 129, 224, 239, 135, 26, 141, 111, 133, 134, 22, 149, 132, 90, 150, 33, 113, 191, 76, + 109, 64, 0, 13, 104, 6, + ]; + + for _ in 0..500 { + let result = ed25519_verify( + signature.len() as _, + signature.as_ptr() as _, + message.len() as _, + message.as_ptr() as _, + public_key.len() as _, + public_key.as_ptr() as _, + ); + // check that result was positive, as negative results could have exited + // early and do not reflect the full cost. + assert!(result == 1); + } +} + +/// Function to measure `ed25519_verify_bytes`. +#[no_mangle] +pub unsafe fn ed25519_verify_16kib_64() { + // 16kB bytes message + let message = [b'a'; 16384]; + + // private key: OReNDSAXOnl-U6Wki95ut01ehQW_9wcAF_utjzRNreg + // public key: M4QwJx4Sogjr0KcMI_gsvt-lEU6tgd9GWmgejE_JYlA + let public_key: [u8; 32] = [ + 51, 132, 48, 39, 30, 18, 162, 8, 235, 208, 167, 12, 35, 248, 44, 190, 223, 165, 17, 78, + 173, 129, 223, 70, 90, 104, 30, 140, 79, 201, 98, 80, + ]; + + let signature: [u8; 64] = [ + 137, 224, 108, 168, 192, 229, 57, 250, 232, 231, 200, 55, 155, 62, 134, 111, 71, 124, 174, + 95, 190, 201, 113, 11, 86, 70, 91, 98, 228, 43, 233, 215, 135, 6, 42, 252, 28, 247, 101, + 57, 100, 50, 105, 41, 225, 221, 157, 121, 76, 28, 236, 247, 124, 228, 20, 203, 91, 18, 146, + 99, 254, 153, 41, 6, + ]; + + for _ in 0..64 { + let result = ed25519_verify( + signature.len() as _, + signature.as_ptr() as _, + message.len() as _, + message.as_ptr() as _, + public_key.len() as _, + public_key.as_ptr() as _, + ); + // check that result was positive, as negative results could have exited + // early and do not reflect the full cost. + assert!(result == 1); + } +} + +#[repr(C)] +struct MultiexpElem([u8; 64], [u8; 32]); + +// Function to measure `alt_bn128_g1_multiexp_base` and `alt_bn128_g1_multiexp_sublinear`. Also measures `base`, `write_register_base`, +// and `write_register_byte`. However `g1_multiexp` computation is more expensive than register writing +// so we are okay overcharging it. +// Compute g1_multiexp on 1 element 10 times. +#[no_mangle] +pub unsafe fn alt_bn128_g1_multiexp_1_10() { + #[rustfmt::skip] + let buffer: [MultiexpElem; 1] = [ + MultiexpElem([16, 238, 91, 161, 241, 22, 172, 158, 138, 252, 202, 212, 136, 37, 110, 231, 118, 220, 8, 45, 14, 153, 125, 217, 227, 87, 238, 238, 31, 138, 226, 8, 238, 185, 12, 155, 93, 126, 144, 248, 200, 177, 46, 245, 40, 162, 169, 80, 150, 211, 157, 13, 10, 36, 44, 232, 173, 32, 32, 115, 123, 2, 9, 47], + [190, 148, 181, 91, 69, 6, 83, 40, 65, 222, 251, 70, 81, 73, 60, 142, 130, 217, 176, 20, 69, 75, 40, 167, 41, 180, 244, 5, 142, 215, 135, 35]), + ]; + for _ in 0..10 { + alt_bn128_g1_multiexp( + core::mem::size_of_val(&buffer) as u64, + buffer.as_ptr() as *const u64 as u64, + 0, + ); + } +} +// Function to measure `alt_bn128_g1_multiexp_base` and `alt_bn128_g1_multiexp_sublinear`. Also measures `base`, `write_register_base`, +// and `write_register_byte`. However `g1_multiexp` computation is more expensive than register writing +// so we are okay overcharging it. +// Compute g1_multiexp on 10 elements 10 times. +#[no_mangle] +pub unsafe fn alt_bn128_g1_multiexp_10_10() { + #[rustfmt::skip] + let buffer: [MultiexpElem; 10] = [ + MultiexpElem([160, 75, 88, 1, 39, 49, 57, 54, 144, 97, 157, 53, 186, 197, 3, 255, 101, 190, 210, 68, 165, 67, 219, 118, 134, 41, 207, 184, 16, 195, 15, 6, 248, 45, 160, 73, 157, 19, 206, 183, 155, 145, 214, 79, 111, 126, 176, 149, 80, 249, 68, 90, 62, 163, 162, 67, 252, 47, 4, 54, 29, 49, 53, 22], + [12, 56, 213, 113, 211, 78, 202, 179, 173, 211, 178, 235, 0, 245, 228, 42, 143, 64, 247, 122, 22, 211, 114, 92, 83, 200, 105, 203, 39, 32, 63, 27]), + MultiexpElem([37, 7, 26, 98, 65, 31, 35, 122, 129, 78, 25, 86, 148, 57, 116, 70, 153, 93, 194, 58, 97, 220, 203, 215, 224, 39, 69, 225, 33, 127, 203, 22, 208, 87, 64, 209, 160, 50, 233, 197, 79, 248, 194, 81, 89, 254, 17, 41, 109, 220, 150, 155, 5, 95, 228, 40, 12, 165, 190, 58, 247, 134, 209, 14], + [244, 210, 233, 80, 103, 48, 162, 222, 169, 166, 178, 7, 251, 120, 170, 5, 196, 223, 179, 12, 240, 219, 64, 15, 210, 140, 180, 12, 98, 193, 51, 35]), + MultiexpElem([101, 235, 228, 31, 160, 165, 255, 231, 8, 230, 135, 9, 86, 42, 196, 160, 234, 133, 17, 42, 106, 87, 229, 48, 163, 73, 23, 56, 106, 110, 233, 14, 246, 209, 251, 163, 97, 223, 184, 57, 122, 44, 248, 21, 143, 156, 140, 178, 208, 231, 246, 42, 160, 28, 169, 134, 186, 33, 111, 79, 176, 193, 114, 4], + [32, 6, 91, 170, 245, 35, 191, 116, 68, 155, 138, 161, 69, 253, 181, 174, 197, 58, 207, 32, 247, 69, 40, 226, 110, 106, 97, 188, 185, 71, 5, 4]), + MultiexpElem([30, 201, 144, 105, 155, 88, 21, 238, 82, 231, 38, 215, 187, 109, 64, 45, 213, 112, 123, 117, 182, 34, 17, 120, 137, 153, 181, 73, 103, 173, 175, 12, 56, 100, 23, 159, 145, 19, 214, 51, 118, 221, 156, 208, 163, 123, 95, 75, 61, 175, 45, 246, 245, 37, 37, 71, 237, 70, 168, 37, 191, 161, 129, 37], + [141, 72, 92, 238, 166, 71, 116, 3, 194, 153, 215, 33, 137, 244, 155, 246, 177, 218, 156, 27, 134, 19, 3, 52, 87, 215, 30, 182, 153, 244, 230, 5]), + MultiexpElem([130, 175, 69, 182, 170, 81, 194, 158, 15, 103, 192, 202, 12, 245, 20, 187, 241, 59, 70, 185, 207, 220, 215, 180, 202, 0, 201, 132, 203, 112, 218, 23, 190, 111, 131, 109, 225, 24, 24, 109, 165, 174, 0, 243, 229, 234, 191, 178, 12, 139, 167, 219, 113, 35, 210, 187, 238, 171, 58, 57, 92, 204, 80, 13], + [68, 230, 101, 222, 230, 122, 127, 221, 26, 145, 121, 2, 186, 153, 57, 195, 155, 167, 252, 162, 74, 29, 145, 71, 152, 182, 106, 222, 251, 31, 18, 14]), + MultiexpElem([228, 81, 31, 41, 119, 231, 33, 160, 222, 228, 135, 22, 160, 1, 145, 232, 241, 77, 68, 48, 110, 27, 98, 73, 222, 194, 197, 218, 69, 38, 186, 6, 174, 192, 184, 222, 106, 213, 150, 163, 152, 137, 169, 149, 188, 21, 169, 3, 85, 97, 60, 105, 222, 87, 73, 77, 23, 46, 80, 109, 53, 143, 249, 35], + [241, 199, 237, 191, 208, 242, 112, 249, 123, 67, 165, 9, 177, 232, 215, 242, 100, 37, 64, 150, 94, 143, 49, 113, 14, 203, 192, 93, 62, 218, 80, 26]), + MultiexpElem([100, 0, 47, 223, 75, 179, 152, 89, 133, 183, 139, 6, 111, 147, 236, 47, 156, 169, 164, 176, 65, 147, 60, 46, 71, 153, 231, 71, 64, 6, 38, 41, 56, 117, 9, 136, 151, 236, 181, 209, 129, 186, 40, 145, 252, 36, 84, 156, 109, 229, 49, 247, 39, 108, 174, 77, 146, 123, 109, 159, 82, 128, 217, 37], + [242, 91, 130, 131, 75, 140, 17, 84, 183, 169, 177, 27, 254, 198, 240, 241, 177, 230, 22, 152, 47, 158, 83, 224, 126, 59, 109, 134, 230, 230, 202, 39]), + MultiexpElem([235, 0, 171, 107, 245, 236, 211, 78, 253, 88, 231, 142, 235, 226, 72, 251, 47, 190, 102, 103, 42, 243, 147, 160, 94, 110, 252, 188, 127, 208, 129, 21, 113, 64, 101, 51, 63, 70, 181, 9, 13, 27, 129, 6, 132, 11, 122, 0, 14, 141, 42, 104, 214, 180, 72, 237, 163, 4, 25, 169, 236, 244, 6, 1], + [49, 147, 150, 137, 36, 231, 125, 113, 18, 113, 80, 86, 72, 98, 64, 52, 199, 187, 237, 189, 196, 37, 57, 172, 59, 169, 228, 72, 130, 172, 79, 43]), + MultiexpElem([82, 84, 39, 25, 71, 8, 250, 3, 126, 110, 4, 157, 215, 43, 88, 57, 103, 237, 236, 231, 237, 246, 224, 75, 47, 150, 229, 43, 117, 115, 81, 5, 199, 238, 36, 161, 135, 182, 77, 82, 173, 216, 141, 86, 249, 210, 110, 146, 61, 39, 46, 229, 119, 128, 133, 113, 236, 89, 99, 52, 156, 51, 116, 7], + [234, 246, 120, 109, 70, 97, 226, 124, 146, 197, 14, 48, 220, 18, 81, 103, 227, 12, 240, 155, 220, 125, 90, 45, 173, 31, 95, 162, 175, 27, 82, 36]), + MultiexpElem([46, 144, 238, 51, 121, 134, 255, 49, 104, 119, 209, 185, 131, 214, 61, 148, 48, 188, 108, 119, 101, 189, 223, 152, 191, 180, 146, 17, 179, 38, 209, 36, 26, 208, 85, 141, 208, 135, 207, 174, 123, 138, 185, 167, 96, 13, 1, 83, 159, 80, 96, 145, 194, 57, 153, 195, 15, 11, 92, 13, 8, 195, 196, 7], + [210, 90, 26, 178, 71, 105, 235, 10, 78, 33, 99, 232, 208, 135, 210, 118, 233, 9, 5, 90, 12, 7, 82, 87, 246, 221, 98, 145, 122, 59, 230, 46]), + ]; + for _ in 0..10 { + alt_bn128_g1_multiexp( + core::mem::size_of_val(&buffer) as u64, + buffer.as_ptr() as *const u64 as u64, + 0, + ); + } +} + +#[repr(C)] +struct SumElem(u8, [u8; 64]); + +// Function to measure `alt_bn128_g1_sum_base` and `alt_bn128_g1_sum_element`. Also measures `base`, `write_register_base`, +// and `write_register_byte`. However `g1_sum` computation is more expensive than register writing +// so we are okay overcharging it. +// Compute g1_sum on 1 element 1k times. +#[no_mangle] +pub unsafe fn alt_bn128_g1_sum_1_1k() { + #[rustfmt::skip] + let buffer: [SumElem; 1] = [ + SumElem(0, [11, 49, 94, 29, 152, 111, 116, 138, 248, 2, 184, 8, 159, 80, 169, 45, 149, 48, 32, 49, 37, 6, 133, 105, 171, 194, 120, 44, 195, 17, 180, 35, 137, 154, 4, 192, 211, 244, 93, 200, 2, 44, 0, 64, 26, 108, 139, 147, 88, 235, 242, 23, 253, 52, 110, 236, 67, 99, 176, 2, 186, 198, 228, 25]), + ]; + for _ in 0..1_000 { + alt_bn128_g1_sum( + core::mem::size_of_val(&buffer) as u64, + buffer.as_ptr() as *const u64 as u64, + 0, + ); + } +} + +// Function to measure `alt_bn128_g1_sum_base` and `alt_bn128_g1_sum_element`. Also measures `base`, `write_register_base`, +// and `write_register_byte`. However `g1_sum` computation is more expensive than register writing +// so we are okay overcharging it. +// Compute g1_sum on 10 element 1k times. +#[no_mangle] +pub unsafe fn alt_bn128_g1_sum_10_1k() { + #[rustfmt::skip] + let buffer: [SumElem; 10] = [ + SumElem(0, [35, 69, 193, 29, 159, 231, 101, 1, 196, 134, 150, 68, 208, 119, 157, 39, 248, 90, 73, 19, 10, 8, 235, 127, 161, 218, 15, 115, 180, 172, 209, 16, 228, 156, 170, 43, 38, 198, 86, 36, 148, 8, 79, 237, 137, 133, 67, 225, 144, 247, 98, 177, 65, 52, 2, 51, 108, 108, 235, 101, 71, 15, 89, 24]), + SumElem(0, [61, 2, 168, 116, 29, 2, 121, 230, 138, 133, 50, 222, 81, 136, 130, 255, 228, 187, 237, 97, 226, 24, 24, 225, 228, 221, 102, 101, 88, 154, 127, 43, 21, 183, 14, 55, 239, 203, 66, 188, 194, 60, 232, 17, 100, 73, 170, 174, 44, 131, 89, 176, 26, 43, 123, 173, 38, 238, 5, 204, 224, 211, 6, 38]), + SumElem(1, [22, 233, 101, 119, 186, 92, 128, 212, 29, 189, 170, 207, 212, 163, 98, 74, 31, 168, 145, 29, 1, 168, 123, 195, 7, 243, 15, 22, 186, 1, 105, 30, 177, 138, 63, 142, 128, 124, 81, 157, 189, 145, 114, 71, 91, 192, 243, 19, 214, 74, 141, 149, 84, 3, 97, 51, 101, 221, 243, 63, 34, 43, 44, 12]), + SumElem(0, [222, 145, 152, 153, 248, 97, 163, 68, 51, 35, 234, 159, 72, 239, 0, 78, 56, 37, 239, 175, 121, 224, 50, 128, 51, 230, 122, 231, 96, 31, 204, 0, 239, 255, 21, 120, 202, 147, 235, 55, 165, 147, 205, 33, 240, 15, 47, 163, 189, 125, 84, 26, 232, 163, 132, 250, 148, 136, 2, 15, 114, 159, 178, 29]), + SumElem(1, [175, 114, 55, 82, 223, 6, 219, 68, 103, 204, 9, 215, 220, 64, 99, 224, 12, 186, 46, 90, 74, 93, 66, 74, 211, 119, 163, 10, 36, 239, 175, 31, 80, 201, 192, 101, 188, 227, 193, 181, 51, 8, 239, 157, 158, 28, 92, 28, 186, 197, 20, 161, 153, 183, 226, 236, 231, 56, 22, 141, 20, 194, 202, 6]), + SumElem(1, [127, 32, 22, 163, 172, 30, 54, 118, 159, 178, 75, 182, 19, 149, 171, 185, 113, 11, 203, 125, 150, 24, 123, 18, 110, 134, 15, 28, 181, 224, 95, 26, 83, 22, 56, 133, 230, 240, 166, 228, 89, 241, 249, 38, 150, 59, 240, 175, 146, 52, 184, 240, 226, 13, 10, 214, 175, 134, 37, 217, 96, 163, 7, 27]), + SumElem(1, [198, 145, 187, 197, 240, 31, 72, 191, 158, 229, 28, 10, 102, 2, 225, 215, 100, 142, 183, 59, 15, 182, 207, 95, 163, 199, 235, 54, 87, 154, 106, 32, 193, 169, 8, 160, 52, 171, 14, 186, 158, 60, 211, 82, 31, 224, 46, 82, 29, 176, 164, 166, 141, 148, 67, 245, 192, 14, 144, 163, 200, 72, 119, 20]), + SumElem(1, [146, 85, 5, 23, 90, 242, 183, 140, 72, 71, 210, 248, 1, 106, 24, 44, 12, 88, 216, 245, 83, 140, 157, 142, 220, 215, 110, 181, 223, 192, 104, 9, 255, 142, 7, 108, 36, 73, 219, 134, 59, 163, 13, 120, 153, 208, 66, 18, 91, 48, 15, 151, 121, 11, 183, 176, 89, 170, 89, 187, 165, 219, 130, 34]), + SumElem(0, [67, 100, 228, 203, 250, 180, 250, 94, 237, 191, 144, 155, 198, 81, 168, 159, 196, 94, 122, 171, 96, 15, 39, 203, 187, 224, 94, 43, 81, 11, 114, 27, 236, 70, 0, 241, 246, 107, 68, 165, 82, 117, 90, 154, 141, 206, 102, 4, 145, 66, 144, 215, 244, 227, 164, 252, 128, 205, 233, 51, 122, 213, 232, 20]), + SumElem(1, [173, 37, 223, 11, 68, 227, 111, 46, 246, 210, 31, 181, 36, 149, 221, 113, 27, 34, 156, 35, 82, 144, 38, 34, 165, 43, 130, 70, 86, 34, 148, 30, 15, 207, 202, 218, 30, 22, 143, 20, 58, 60, 133, 67, 150, 255, 65, 29, 217, 94, 89, 182, 13, 160, 65, 173, 5, 232, 223, 65, 213, 210, 55, 25]), + ]; + for _ in 0..1_000 { + alt_bn128_g1_sum( + core::mem::size_of_val(&buffer) as u64, + buffer.as_ptr() as *const u64 as u64, + 0, + ); + } +} + +#[repr(C)] +struct PairingElem([u8; 64], [u8; 128]); + +// Function to measure `alt_bn128_pairing_check_base` and `alt_bn128_pairing_check_element`. Also measures `base`, `write_register_base`, +// and `write_register_byte`. However `g1_multiexp` computation is more expensive than register writing +// so we are okay overcharging it. +// Compute pairing_check on 1 element 10 times. +#[no_mangle] +pub unsafe fn alt_bn128_pairing_check_1_10() { + #[rustfmt::skip] + let buffer: [PairingElem; 1] = [ + PairingElem([80, 12, 4, 181, 61, 254, 153, 52, 127, 228, 174, 24, 144, 95, 235, 26, 197, 188, 219, 91, 4, 47, 98, 98, 202, 199, 94, 67, 211, 223, 197, 21, 65, 221, 184, 75, 69, 202, 13, 56, 6, 233, 217, 146, 159, 141, 116, 208, 81, 224, 146, 124, 150, 114, 218, 196, 192, 233, 253, 31, 130, 152, 144, 29], + [34, 54, 229, 82, 80, 13, 200, 53, 254, 193, 250, 1, 205, 60, 38, 172, 237, 29, 18, 82, 187, 98, 113, 152, 184, 251, 223, 42, 104, 148, 253, 25, 79, 39, 165, 18, 195, 165, 215, 155, 168, 251, 250, 2, 215, 214, 193, 172, 187, 84, 54, 168, 27, 100, 161, 155, 144, 95, 199, 238, 88, 238, 202, 46, 247, 97, 33, 56, 78, 174, 171, 15, 245, 5, 121, 144, 88, 81, 102, 133, 118, 222, 81, 214, 74, 169, 27, 91, 27, 23, 80, 55, 43, 97, 101, 24, 168, 29, 75, 136, 229, 2, 55, 77, 60, 200, 227, 210, 172, 194, 232, 45, 151, 46, 248, 206, 193, 250, 145, 84, 78, 176, 74, 210, 0, 106, 168, 30]), + ]; + for _ in 0..10 { + alt_bn128_pairing_check( + core::mem::size_of_val(&buffer) as u64, + buffer.as_ptr() as *const u64 as u64, + ); + } +} +// Function to measure `alt_bn128_g1_multiexp_base` and `alt_bn128_g1_multiexp_sublinear`. Also measures `base`, `write_register_base`, +// and `write_register_byte`. However `g1_multiexp` computation is more expensive than register writing +// so we are okay overcharging it. +// Compute pairing_check on 10 elements 10 times. +#[no_mangle] +pub unsafe fn alt_bn128_pairing_check_10_10() { + #[rustfmt::skip] + let buffer: [PairingElem; 10] = [ + PairingElem([223, 6, 200, 33, 166, 188, 103, 53, 200, 168, 35, 109, 73, 23, 33, 99, 238, 116, 162, 85, 150, 159, 255, 82, 85, 22, 102, 22, 188, 22, 105, 12, 248, 15, 147, 127, 39, 16, 214, 51, 106, 17, 235, 64, 94, 132, 235, 26, 178, 3, 134, 136, 133, 222, 114, 36, 21, 172, 70, 82, 39, 198, 18, 25], + [148, 200, 134, 248, 104, 246, 245, 98, 240, 216, 24, 152, 37, 21, 194, 154, 66, 240, 198, 134, 174, 64, 155, 105, 1, 59, 115, 35, 63, 254, 41, 13, 9, 213, 97, 49, 90, 178, 1, 96, 163, 189, 16, 152, 31, 108, 62, 36, 48, 97, 253, 154, 196, 156, 74, 171, 10, 19, 213, 236, 176, 205, 172, 23, 240, 49, 89, 79, 195, 106, 221, 106, 236, 127, 76, 42, 160, 144, 208, 67, 233, 255, 233, 202, 88, 148, 99, 71, 51, 235, 236, 251, 1, 108, 127, 3, 202, 160, 68, 30, 150, 211, 73, 21, 193, 117, 125, 153, 165, 246, 182, 191, 251, 26, 186, 198, 153, 218, 196, 234, 241, 135, 159, 184, 33, 214, 235, 4]), + PairingElem([229, 38, 210, 21, 178, 61, 170, 167, 16, 18, 51, 233, 225, 101, 198, 72, 53, 5, 223, 97, 78, 49, 54, 101, 37, 6, 196, 16, 9, 255, 173, 6, 6, 141, 167, 242, 31, 118, 141, 162, 125, 98, 127, 242, 9, 144, 222, 175, 9, 217, 39, 246, 105, 3, 244, 130, 192, 39, 67, 47, 134, 150, 185, 2], + [0, 3, 183, 195, 187, 101, 121, 183, 52, 251, 254, 149, 175, 97, 17, 209, 129, 221, 149, 4, 175, 47, 128, 5, 253, 131, 89, 231, 88, 69, 161, 32, 236, 15, 165, 1, 226, 253, 65, 154, 122, 61, 175, 78, 146, 202, 39, 228, 214, 15, 229, 88, 122, 165, 40, 247, 52, 154, 251, 14, 129, 223, 11, 24, 90, 186, 200, 9, 91, 165, 129, 250, 69, 190, 83, 250, 209, 188, 250, 16, 124, 179, 106, 66, 228, 50, 142, 82, 119, 98, 193, 50, 251, 92, 141, 37, 194, 136, 10, 231, 216, 165, 140, 225, 143, 243, 23, 238, 11, 153, 112, 148, 78, 52, 22, 19, 65, 198, 84, 212, 199, 1, 2, 176, 190, 45, 113, 47]), + PairingElem([22, 76, 141, 39, 200, 16, 110, 85, 83, 226, 99, 154, 70, 185, 9, 80, 124, 246, 104, 244, 25, 82, 182, 188, 240, 197, 133, 88, 187, 99, 13, 19, 30, 252, 168, 144, 127, 37, 159, 58, 10, 136, 149, 123, 148, 244, 27, 24, 169, 132, 25, 59, 193, 113, 137, 14, 170, 53, 31, 89, 254, 231, 248, 2], + [109, 112, 187, 231, 175, 83, 200, 247, 55, 103, 34, 199, 225, 228, 60, 108, 17, 36, 199, 118, 66, 18, 143, 44, 72, 185, 139, 231, 169, 26, 230, 5, 133, 86, 209, 85, 83, 146, 170, 132, 27, 254, 32, 200, 150, 48, 223, 59, 22, 7, 152, 161, 92, 250, 197, 97, 110, 97, 130, 64, 15, 70, 29, 40, 201, 19, 234, 144, 223, 253, 140, 81, 104, 96, 153, 87, 32, 155, 243, 59, 72, 111, 132, 41, 67, 126, 10, 208, 188, 92, 65, 179, 54, 135, 27, 17, 105, 197, 203, 93, 59, 5, 131, 70, 56, 170, 247, 72, 61, 196, 13, 150, 182, 233, 243, 19, 118, 107, 8, 132, 154, 245, 11, 190, 186, 14, 69, 2]), + PairingElem([114, 177, 108, 197, 9, 236, 140, 81, 182, 114, 134, 205, 144, 72, 24, 155, 29, 185, 247, 187, 253, 81, 96, 143, 178, 62, 220, 167, 32, 191, 122, 35, 18, 119, 54, 255, 6, 205, 32, 214, 226, 216, 250, 191, 250, 127, 163, 67, 0, 79, 249, 153, 81, 204, 23, 86, 240, 192, 141, 83, 22, 91, 255, 3], + [248, 13, 239, 47, 56, 141, 228, 144, 148, 199, 15, 129, 168, 215, 50, 181, 243, 87, 253, 184, 135, 137, 120, 201, 203, 116, 173, 123, 57, 239, 128, 16, 138, 214, 88, 219, 117, 54, 3, 72, 135, 77, 63, 83, 38, 40, 174, 86, 176, 17, 245, 233, 150, 23, 129, 78, 241, 175, 147, 94, 6, 67, 129, 43, 19, 48, 170, 59, 116, 74, 103, 52, 175, 228, 68, 169, 78, 158, 97, 0, 240, 42, 65, 177, 34, 231, 40, 52, 84, 133, 25, 52, 49, 70, 60, 47, 184, 223, 165, 176, 80, 235, 157, 43, 212, 24, 128, 175, 82, 107, 208, 66, 191, 85, 21, 162, 214, 205, 28, 33, 104, 213, 32, 250, 109, 42, 18, 29]), + PairingElem([170, 231, 186, 171, 126, 67, 218, 47, 205, 136, 168, 13, 190, 186, 202, 200, 239, 184, 31, 99, 98, 201, 226, 94, 205, 77, 164, 32, 238, 244, 183, 7, 130, 196, 74, 163, 122, 189, 118, 4, 121, 28, 183, 243, 104, 226, 144, 141, 42, 215, 93, 251, 83, 207, 229, 37, 154, 231, 64, 162, 209, 56, 190, 37], + [22, 4, 93, 106, 34, 167, 39, 254, 224, 210, 205, 5, 62, 77, 150, 86, 109, 161, 250, 136, 75, 218, 159, 26, 85, 109, 215, 194, 20, 142, 174, 25, 215, 255, 214, 125, 44, 2, 176, 114, 9, 241, 236, 64, 124, 185, 132, 142, 190, 187, 227, 198, 240, 89, 47, 222, 130, 27, 242, 48, 157, 33, 150, 16, 42, 220, 107, 124, 189, 43, 191, 62, 17, 211, 40, 243, 233, 26, 247, 205, 68, 253, 105, 132, 129, 167, 169, 231, 109, 154, 144, 73, 234, 55, 45, 42, 6, 118, 244, 125, 13, 42, 146, 138, 199, 195, 104, 254, 221, 172, 220, 19, 98, 176, 166, 227, 9, 159, 205, 50, 55, 21, 92, 205, 86, 224, 189, 45]), + PairingElem([219, 141, 11, 233, 254, 113, 213, 120, 49, 108, 208, 48, 146, 194, 176, 70, 175, 40, 150, 80, 217, 3, 63, 71, 190, 71, 2, 224, 157, 116, 169, 31, 149, 185, 247, 136, 26, 251, 84, 39, 165, 100, 221, 119, 163, 169, 44, 199, 98, 5, 43, 93, 161, 203, 119, 184, 239, 149, 81, 118, 89, 177, 132, 16], + [187, 184, 93, 144, 191, 32, 240, 106, 13, 221, 213, 150, 41, 219, 192, 93, 123, 50, 102, 28, 85, 119, 4, 187, 88, 137, 187, 7, 226, 182, 127, 45, 49, 30, 169, 231, 65, 157, 58, 236, 152, 165, 199, 127, 44, 23, 193, 8, 76, 228, 74, 249, 229, 106, 252, 158, 124, 74, 80, 126, 158, 52, 54, 27, 8, 196, 244, 50, 105, 103, 26, 79, 82, 90, 130, 62, 66, 183, 154, 109, 229, 90, 217, 224, 185, 63, 112, 126, 139, 240, 205, 115, 221, 181, 126, 1, 56, 43, 132, 131, 24, 46, 60, 97, 55, 190, 161, 9, 223, 0, 231, 197, 81, 28, 62, 51, 111, 199, 191, 102, 153, 200, 221, 41, 108, 162, 28, 32]), + PairingElem([30, 53, 135, 154, 144, 29, 186, 12, 158, 167, 159, 120, 102, 34, 6, 234, 82, 202, 81, 140, 238, 236, 160, 66, 83, 7, 160, 64, 138, 124, 20, 37, 56, 242, 83, 131, 176, 81, 145, 228, 107, 199, 221, 120, 175, 245, 18, 236, 227, 216, 44, 173, 40, 106, 57, 146, 139, 114, 233, 100, 24, 116, 35, 27], + [223, 176, 180, 99, 164, 136, 62, 8, 8, 82, 238, 208, 224, 98, 97, 42, 114, 206, 98, 212, 75, 240, 58, 21, 183, 137, 115, 81, 155, 31, 31, 17, 80, 112, 210, 23, 25, 68, 31, 225, 252, 60, 49, 51, 226, 224, 81, 21, 222, 223, 186, 133, 244, 185, 80, 41, 251, 135, 89, 18, 139, 82, 232, 43, 136, 90, 253, 241, 7, 189, 37, 249, 86, 118, 175, 107, 153, 163, 117, 8, 0, 150, 45, 244, 76, 47, 173, 109, 40, 111, 245, 45, 22, 4, 184, 15, 103, 17, 28, 179, 1, 74, 62, 22, 242, 240, 227, 91, 179, 37, 200, 144, 102, 208, 255, 230, 20, 241, 179, 8, 116, 94, 64, 59, 179, 102, 104, 0]), + PairingElem([15, 85, 82, 165, 6, 84, 191, 14, 237, 31, 189, 18, 247, 6, 164, 106, 224, 231, 24, 196, 118, 52, 45, 51, 249, 19, 100, 2, 126, 100, 224, 16, 188, 81, 64, 1, 130, 153, 112, 55, 135, 138, 0, 31, 89, 27, 51, 208, 84, 98, 235, 218, 106, 229, 138, 27, 232, 252, 53, 183, 27, 90, 19, 11], + [40, 240, 97, 126, 62, 32, 190, 177, 113, 254, 3, 136, 251, 27, 61, 148, 65, 195, 47, 94, 18, 66, 163, 169, 231, 122, 2, 158, 183, 37, 105, 33, 59, 108, 132, 78, 184, 105, 25, 221, 5, 76, 192, 207, 127, 194, 208, 92, 50, 197, 84, 215, 146, 92, 150, 23, 207, 90, 90, 166, 39, 206, 253, 42, 251, 16, 74, 4, 176, 247, 209, 65, 131, 177, 6, 248, 111, 156, 152, 123, 77, 197, 141, 132, 169, 93, 79, 95, 195, 92, 175, 229, 29, 186, 20, 42, 85, 130, 115, 201, 145, 81, 5, 152, 86, 225, 6, 226, 70, 106, 161, 229, 158, 245, 168, 47, 183, 104, 24, 217, 165, 70, 154, 93, 111, 149, 70, 33]), + PairingElem([117, 0, 122, 107, 244, 192, 63, 73, 188, 19, 32, 65, 113, 194, 53, 131, 86, 97, 44, 198, 81, 55, 164, 201, 183, 250, 129, 150, 126, 198, 13, 38, 7, 8, 206, 60, 150, 88, 196, 65, 198, 1, 230, 202, 250, 254, 101, 92, 219, 104, 228, 96, 35, 194, 237, 136, 126, 248, 42, 182, 247, 241, 75, 14], + [69, 234, 24, 234, 25, 191, 64, 38, 183, 126, 143, 90, 107, 15, 30, 68, 252, 210, 74, 136, 110, 117, 191, 63, 133, 55, 240, 252, 78, 14, 25, 43, 243, 191, 152, 161, 242, 117, 141, 220, 27, 5, 218, 83, 156, 204, 94, 173, 227, 150, 36, 98, 160, 97, 154, 168, 148, 100, 150, 158, 151, 240, 51, 48, 126, 42, 195, 42, 163, 53, 124, 226, 253, 99, 24, 50, 158, 41, 28, 51, 1, 11, 65, 201, 191, 134, 156, 0, 223, 239, 198, 207, 180, 243, 118, 28, 98, 173, 195, 236, 75, 113, 170, 12, 120, 49, 188, 11, 54, 108, 55, 49, 54, 110, 228, 232, 202, 181, 196, 207, 53, 123, 158, 232, 68, 15, 188, 22]), + PairingElem([45, 111, 148, 191, 42, 226, 238, 248, 78, 94, 26, 9, 239, 9, 15, 13, 10, 170, 97, 8, 195, 153, 36, 144, 198, 4, 224, 209, 40, 187, 94, 48, 201, 232, 156, 89, 230, 160, 89, 200, 132, 202, 38, 57, 183, 180, 218, 67, 213, 155, 217, 208, 70, 148, 7, 255, 123, 35, 71, 215, 131, 153, 39, 21], + [205, 30, 22, 107, 59, 81, 254, 153, 39, 49, 130, 112, 16, 111, 159, 119, 66, 72, 251, 247, 86, 214, 89, 173, 104, 226, 32, 13, 82, 209, 195, 2, 207, 8, 240, 251, 117, 92, 41, 206, 209, 141, 185, 60, 24, 218, 2, 249, 161, 52, 128, 224, 52, 152, 5, 227, 58, 198, 54, 248, 183, 181, 28, 8, 159, 184, 36, 32, 149, 142, 86, 69, 184, 53, 85, 214, 224, 181, 36, 44, 114, 27, 177, 213, 179, 159, 135, 52, 213, 192, 251, 150, 85, 145, 118, 6, 218, 177, 105, 20, 215, 89, 47, 78, 157, 25, 81, 85, 123, 18, 86, 88, 137, 86, 89, 39, 173, 144, 10, 175, 215, 79, 121, 252, 106, 13, 91, 31]), + ]; + for _ in 0..10 { + alt_bn128_pairing_check( + core::mem::size_of_val(&buffer) as u64, + buffer.as_ptr() as *const u64 as u64, + ); + } +} + +// ############### +// # Storage API # +// ############### + +macro_rules! storage_bench { + ($key_buf:ident, $key_len:expr, $value_buf:ident, $value_len:expr, $loop_n:expr, $exp_name:ident, $call:block) => { + #[no_mangle] + pub unsafe fn $exp_name() { + let mut $key_buf = [0u8; $key_len]; + let mut $value_buf = [0u8; $value_len]; + for i in 0..$loop_n { + // Modify blob so that we write different content. + $key_buf[0] = (i % 256) as u8; + $key_buf[1] = ((i / 256) % 256) as u8; + $key_buf[2] = ((i / 256 / 256) % 256) as u8; + + $value_buf[0] = (i % 256) as u8; + $value_buf[1] = ((i / 256) % 256) as u8; + $value_buf[2] = ((i / 256 / 256) % 256) as u8; + $call + } + } + }; +} + +// Storage writing. + +// Function to measure `storage_write_base`. +// Writes to storage 1k times. +storage_bench!(key, 10, value, 10, 1000, storage_write_10b_key_10b_value_1k, { + storage_write(10, key.as_ptr() as _, 10, value.as_ptr() as _, 0); +}); + +// Function to measure `storage_write_base + storage_write_key_byte`. +// Writes to storage with 10kib key 1000 times. +storage_bench!(key, 10240, value, 10, 1000, storage_write_10kib_key_10b_value_1k, { + storage_write(10240, key.as_ptr() as _, 10, value.as_ptr() as _, 0); +}); + +// Function to measure `storage_write_base + storage_write_value_byte`. +// Writes to storage with 10kib value 1000 times. +storage_bench!(key, 10, value, 10240, 1000, storage_write_10b_key_10kib_value_1k, { + storage_write(10, key.as_ptr() as _, 10240, value.as_ptr() as _, 0); +}); + +// Storage reading. + +// Function to measure `storage_read_base`. +// Writes to storage 1k times. +storage_bench!(key, 10, value, 10, 1000, storage_read_10b_key_10b_value_1k, { + storage_read(10, key.as_ptr() as _, 0); +}); + +// Function to measure `storage_read_base + storage_read_key_byte`. +// Writes to storage with 10kib key 1000 times. +storage_bench!(key, 10240, value, 10, 1000, storage_read_10kib_key_10b_value_1k, { + storage_read(10240, key.as_ptr() as _, 0); +}); + +// Function to measure `storage_read_base + storage_read_value_byte`. +// Writes to storage with 10kib value 1000 times. +storage_bench!(key, 10, value, 10240, 1000, storage_read_10b_key_10kib_value_1k, { + storage_read(10, key.as_ptr() as _, 0); +}); + +// Storage removing. + +// Function to measure `storage_remove_base`. +// Writes to storage 1k times. +storage_bench!(key, 10, value, 10, 1000, storage_remove_10b_key_10b_value_1k, { + storage_remove(10, key.as_ptr() as _, 0); +}); + +// Function to measure `storage_remove_base + storage_remove_key_byte`. +// Writes to storage with 10kib key 1000 times. +storage_bench!(key, 10240, value, 10, 1000, storage_remove_10kib_key_10b_value_1k, { + storage_remove(10240, key.as_ptr() as _, 0); +}); + +// Function to measure `storage_remove_base + storage_remove_value_byte`. +// Writes to storage with 10kib value 1000 times. +storage_bench!(key, 10, value, 10240, 1000, storage_remove_10b_key_10kib_value_1k, { + storage_remove(10, key.as_ptr() as _, 0); +}); + +// Storage has key. + +// Function to measure `storage_has_key_base`. +// Writes to storage 1k times. +storage_bench!(key, 10, value, 10, 1000, storage_has_key_10b_key_10b_value_1k, { + storage_has_key(10, key.as_ptr() as _); +}); + +// Function to measure `storage_has_key_base + storage_has_key_key_byte`. +// Writes to storage with 10kib key 1000 times. +storage_bench!(key, 10240, value, 10, 1000, storage_has_key_10kib_key_10b_value_1k, { + storage_has_key(10240, key.as_ptr() as _); +}); + +// Function to measure `storage_has_key_base + storage_has_key_value_byte`. +// Writes to storage with 10kib value 1000 times. +storage_bench!(key, 10, value, 10240, 1000, storage_has_key_10b_key_10kib_value_1k, { + storage_has_key(10, key.as_ptr() as _); +}); + +// Function to measure `promise_and_base`. +#[no_mangle] +pub unsafe fn promise_and_100k() { + let account = b"alice_near"; + let id0 = promise_batch_create(account.len() as _, account.as_ptr() as _); + let id1 = promise_batch_create(account.len() as _, account.as_ptr() as _); + let ids = [id0, id1]; + for _ in 0..100_000 { + promise_and(ids.as_ptr() as _, 2); + } +} + +// Function to measure `promise_and_per_promise`. +#[no_mangle] +pub unsafe fn promise_and_100k_on_1k_and() { + let account = b"alice_near"; + let mut ids = [0u64; 1000]; + for i in 0..1000 { + ids[i] = promise_batch_create(account.len() as _, account.as_ptr() as _); + } + for _ in 0..100_000 { + promise_and(ids.as_ptr() as _, ids.len() as _); + } +} + +// Function to measure `promise_return`. +#[no_mangle] +pub unsafe fn promise_return_100k() { + let account = b"alice_near"; + let id = promise_batch_create(account.len() as _, account.as_ptr() as _); + for _ in 0..100_000 { + promise_return(id); + } +} + +// Measuring cost for data_receipt_creation_config. + +// Function that emits 10b of data. +#[no_mangle] +pub unsafe fn data_producer_10b() { + let data = [0u8; 10]; + value_return(data.len() as _, data.as_ptr() as _); +} + +// Function that emits 100kib of data. +#[no_mangle] +pub unsafe fn data_producer_100kib() { + let data = [0u8; 102400]; + value_return(data.len() as _, data.as_ptr() as _); +} + +// Function to measure `data_receipt_creation_config`, but we are measure send and execution fee at the same time. +// Produces 1000 10b data receipts. +#[no_mangle] +pub unsafe fn data_receipt_10b_1000() { + let buf = [0u8; 1000]; + current_account_id(0); + let buf_len = register_len(0); + read_register(0, buf.as_ptr() as _); + + let method_name = b"data_producer_10b"; + let args = b""; + let mut ids = [0u64; 1000]; + let amount = 0u128; + let gas = prepaid_gas(); + for i in 0..1000 { + ids[i] = promise_create( + buf_len, + buf.as_ptr() as _, + method_name.len() as _, + method_name.as_ptr() as _, + args.len() as _, + args.as_ptr() as _, + &amount as *const u128 as *const u64 as u64, + gas / 2000, + ); + } + let id = promise_and(ids.as_ptr() as _, ids.len() as _); + let method_name = b"noop"; + promise_then( + id, + buf_len, + buf.as_ptr() as _, + method_name.len() as _, + method_name.as_ptr() as _, + args.len() as _, + args.as_ptr() as _, + &amount as *const u128 as *const u64 as u64, + gas / 3, + ); +} + +// Function to subtract the base from the `data_receipt_creation_config`. This method doesn't +// have a callback on created promises so there is no data dependency. +#[no_mangle] +pub unsafe fn data_receipt_base_10b_1000() { + let buf = [0u8; 1000]; + current_account_id(0); + let buf_len = register_len(0); + read_register(0, buf.as_ptr() as _); + + let method_name = b"data_producer_10b"; + let args = b""; + let mut ids = [0u64; 1000]; + let amount = 0u128; + let gas = prepaid_gas(); + for i in 0..1000 { + ids[i] = promise_create( + buf_len, + buf.as_ptr() as _, + method_name.len() as _, + method_name.as_ptr() as _, + args.len() as _, + args.as_ptr() as _, + &amount as *const u128 as *const u64 as u64, + gas / 2000, + ); + } + let _id = promise_and(ids.as_ptr() as _, ids.len() as _); +} + +// Function to measure `data_receipt_creation_config`, but we are measure send and execution fee at the same time. +// Produces 1000 10kib data receipts. +#[no_mangle] +pub unsafe fn data_receipt_100kib_1000() { + let buf = [0u8; 1000]; + current_account_id(0); + let buf_len = register_len(0); + read_register(0, buf.as_ptr() as _); + + let method_name = b"data_producer_100kib"; + let args = b""; + let mut ids = [0u64; 1000]; + let amount = 0u128; + let gas = prepaid_gas(); + for i in 0..1000 { + ids[i] = promise_create( + buf_len, + buf.as_ptr() as _, + method_name.len() as _, + method_name.as_ptr() as _, + args.len() as _, + args.as_ptr() as _, + &amount as *const u128 as *const u64 as u64, + gas / 2000, + ); + } + let id = promise_and(ids.as_ptr() as _, ids.len() as _); + let method_name = b"noop"; + promise_then( + id, + buf_len, + buf.as_ptr() as _, + method_name.len() as _, + method_name.as_ptr() as _, + args.len() as _, + args.as_ptr() as _, + &amount as *const u128 as *const u64 as u64, + gas / 3, + ); +} + +#[no_mangle] +pub unsafe fn cpu_ram_soak_test() { + let mut buf = [0u8; 100 * 1024]; + let len = buf.len(); + for i in 0..10_000_000 { + let j = (i * 7 + len / 2) % len; + let k = (i * 3) % len; + let tmp = buf[k]; + buf[k] = buf[j]; + buf[j] = tmp; + } +} + +/// Maximum length that sum of arguments can have. +/// The assumption is that all WASM memory except for 1 page is available. +const MAX_ARG_LEN: u64 = 1024 * 1024 - 4096; + +#[no_mangle] +/// Insert or overwrite a value to given key +pub unsafe fn account_storage_insert_key() { + input(0); + let input_data = [0u8; MAX_ARG_LEN as usize]; + read_register(0, input_data.as_ptr() as _); + + let key_len = u64::from_le_bytes(input_data[..8].try_into().unwrap()); + assert!(key_len < MAX_ARG_LEN - 16); + let key = &input_data[8..8 + key_len as usize]; + + let value_input = &input_data[8 + key_len as usize..]; + + let value_len = u64::from_le_bytes(value_input[..8].try_into().unwrap()); + assert!(value_len + key_len + 16 < MAX_ARG_LEN); + let value = &value_input[8..8 + value_len as usize]; + + storage_write(key_len, key.as_ptr() as _, value_len, value.as_ptr() as _, 1); +} + +#[no_mangle] +/// Check if key exists for account +pub unsafe fn account_storage_has_key() { + input(0); + let input_data = [0u8; MAX_ARG_LEN as usize]; + read_register(0, input_data.as_ptr() as _); + + let key_len = u64::from_le_bytes(input_data[..8].try_into().unwrap()); + assert!(key_len < MAX_ARG_LEN - 16); + let key = &input_data[8..8 + key_len as usize]; + + storage_has_key(key_len, key.as_ptr() as _); +} diff --git a/runtime/unc-test-contracts/res/.gitignore b/runtime/unc-test-contracts/res/.gitignore new file mode 100644 index 000000000..c1a03a902 --- /dev/null +++ b/runtime/unc-test-contracts/res/.gitignore @@ -0,0 +1,3 @@ +*.wasm +!test_contract_ts.wasm +!unc_evm.wasm diff --git a/runtime/unc-test-contracts/res/ZombieOwnership.bin b/runtime/unc-test-contracts/res/ZombieOwnership.bin new file mode 100644 index 000000000..bc8e363a5 --- /dev/null +++ b/runtime/unc-test-contracts/res/ZombieOwnership.bin @@ -0,0 +1 @@ +60806040526010600155600154600a0a6002556201518060035566038d7ea4c6800060085560006009556046600a55336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a361232b806100f96000396000f3fe60806040526004361061011f5760003560e01c80636352211e116100a05780638f32d59b116100645780638f32d59b14610736578063c39cbef114610765578063ccf670f8146107f5578063e1fa763814610830578063f2fde38b146108755761011f565b80636352211e1461052057806370a082311461059b578063715018a6146106005780637bff0a01146106175780638da5cb5b146106df5761011f565b80633ccfd60b116100e75780633ccfd60b146103525780634412e10414610369578063528b7b8f1461040f5780635f4623f11461048a5780635faf2880146104db5761011f565b8063095ea7b3146101245780630ce90ec21461017257806317a7f4cc146101a05780632052465e146101e557806323b872dd146102e4575b600080fd5b6101706004803603604081101561013a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506108c6565b005b61019e6004803603602081101561018857600080fd5b81019080803590602001909291905050506109e4565b005b3480156101ac57600080fd5b506101e3600480360360408110156101c357600080fd5b810190808035906020019092919080359060200190929190505050610a79565b005b3480156101f157600080fd5b5061021e6004803603602081101561020857600080fd5b8101908080359060200190929190505050610c04565b60405180806020018781526020018663ffffffff1663ffffffff1681526020018563ffffffff1663ffffffff1681526020018461ffff1661ffff1681526020018361ffff1661ffff168152602001828103825288818151815260200191508051906020019080838360005b838110156102a4578082015181840152602081019050610289565b50505050905090810190601f1680156102d15780820380516001836020036101000a031916815260200191505b5097505050505050505060405180910390f35b610350600480360360608110156102fa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610d21565b005b34801561035e57600080fd5b50610367610e05565b005b34801561037557600080fd5b506103b86004803603602081101561038c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610e6c565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156103fb5780820151818401526020810190506103e0565b505050509050019250505060405180910390f35b34801561041b57600080fd5b506104486004803603602081101561043257600080fd5b8101908080359060200190929190505050610f99565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561049657600080fd5b506104d9600480360360208110156104ad57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610fcc565b005b3480156104e757600080fd5b5061051e600480360360408110156104fe57600080fd5b810190808035906020019092919080359060200190929190505050611021565b005b34801561052c57600080fd5b506105596004803603602081101561054357600080fd5b81019080803590602001909291905050506110fb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156105a757600080fd5b506105ea600480360360208110156105be57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611138565b6040518082815260200191505060405180910390f35b34801561060c57600080fd5b50610615611181565b005b34801561062357600080fd5b506106dd6004803603602081101561063a57600080fd5b810190808035906020019064010000000081111561065757600080fd5b82018360208201111561066957600080fd5b8035906020019184600183028401116401000000008311171561068b57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611251565b005b3480156106eb57600080fd5b506106f46112c7565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561074257600080fd5b5061074b6112f0565b604051808215151515815260200191505060405180910390f35b34801561077157600080fd5b506107f36004803603604081101561078857600080fd5b8101908080359060200190929190803590602001906401000000008111156107af57600080fd5b8201836020820111156107c157600080fd5b803590602001918460018302840111640100000000831117156107e357600080fd5b9091929391929390505050611347565b005b34801561080157600080fd5b5061082e6004803603602081101561081857600080fd5b810190808035906020019092919050505061142d565b005b34801561083c57600080fd5b506108736004803603604081101561085357600080fd5b810190808035906020019092919080359060200190929190505050611448565b005b34801561088157600080fd5b506108c46004803603602081101561089857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506116d4565b005b806005600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461093257600080fd5b82600b600084815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550818373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a4505050565b60085434146109f257600080fd5b610a3a600160048381548110610a0457fe5b906000526020600020906003020160020160009054906101000a900463ffffffff1663ffffffff166116f190919063ffffffff16565b60048281548110610a4757fe5b906000526020600020906003020160020160006101000a81548163ffffffff021916908363ffffffff16021790555050565b6000600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663e98b7f4d836040518263ffffffff1660e01b8152600401808281526020019150506101406040518083038186803b158015610aef57600080fd5b505afa158015610b03573d6000803e3d6000fd5b505050506040513d610140811015610b1a57600080fd5b810190808051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919080519060200190929190805190602001909291908051906020019092919050505090919293949596979850909192939495969750909192939495965090919293949550909192939450909192935090919250909150905080915050610bff83826040518060400160405280600581526020017f6b69747479000000000000000000000000000000000000000000000000000000815250611719565b505050565b60048181548110610c1157fe5b9060005260206000209060030201600091509050806000018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610cbd5780601f10610c9257610100808354040283529160200191610cbd565b820191906000526020600020905b815481529060010190602001808311610ca057829003601f168201915b5050505050908060010154908060020160009054906101000a900463ffffffff16908060020160049054906101000a900463ffffffff16908060020160089054906101000a900461ffff169080600201600a9054906101000a900461ffff16905086565b3373ffffffffffffffffffffffffffffffffffffffff166005600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161480610dec57503373ffffffffffffffffffffffffffffffffffffffff16600b600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b610df557600080fd5b610e008383836118fb565b505050565b610e0d6112f0565b610e1657600080fd5b6000610e206112c7565b90508073ffffffffffffffffffffffffffffffffffffffff166108fc479081150290604051600060405180830381858888f19350505050158015610e68573d6000803e3d6000fd5b5050565b606080600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054604051908082528060200260200182016040528015610edd5781602001602082028038833980820191505090505b509050600080905060008090505b600480549050811015610f8e578473ffffffffffffffffffffffffffffffffffffffff166005600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610f815780838381518110610f6c57fe5b60200260200101818152505081806001019250505b8080600101915050610eeb565b508192505050919050565b60056020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610fd46112f0565b610fdd57600080fd5b80600760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b601482816004828154811061103257fe5b906000526020600020906003020160020160009054906101000a900463ffffffff1663ffffffff16101561106557600080fd5b836005600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146110d157600080fd5b83600486815481106110df57fe5b9060005260206000209060030201600101819055505050505050565b60006005600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b6000600660008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6111896112f0565b61119257600080fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541461129d57600080fd5b60006112a882611ad9565b9050606481816112b457fe5b06810390506112c38282611b67565b5050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b600283816004828154811061135857fe5b906000526020600020906003020160020160009054906101000a900463ffffffff1663ffffffff16101561138b57600080fd5b846005600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146113f757600080fd5b84846004888154811061140657fe5b906000526020600020906003020160000191906114249291906121d1565b50505050505050565b6114356112f0565b61143e57600080fd5b8060088190555050565b816005600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146114b457600080fd5b6000600484815481106114c357fe5b906000526020600020906003020190506000600484815481106114e257fe5b9060005260206000209060030201905060006114fe6064611e39565b9050600a5481116116325761153360018460020160089054906101000a900461ffff1661ffff16611ed290919063ffffffff16565b8360020160086101000a81548161ffff021916908361ffff16021790555061157f60018460020160009054906101000a900463ffffffff1663ffffffff166116f190919063ffffffff16565b8360020160006101000a81548163ffffffff021916908363ffffffff1602179055506115cb600183600201600a9054906101000a900461ffff1661ffff16611ed290919063ffffffff16565b82600201600a6101000a81548161ffff021916908361ffff16021790555061162d8683600101546040518060400160405280600681526020017f7a6f6d6269650000000000000000000000000000000000000000000000000000815250611719565b6116cc565b61165c600184600201600a9054906101000a900461ffff1661ffff16611ed290919063ffffffff16565b83600201600a6101000a81548161ffff021916908361ffff1602179055506116a460018360020160089054906101000a900461ffff1661ffff16611ed290919063ffffffff16565b8260020160086101000a81548161ffff021916908361ffff1602179055506116cb83611ef6565b5b505050505050565b6116dc6112f0565b6116e557600080fd5b6116ee81611f20565b50565b60008082840190508363ffffffff168163ffffffff16101561170f57fe5b8091505092915050565b826005600082815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461178557600080fd5b60006004858154811061179457fe5b906000526020600020906003020190506117ad81612018565b6117b657600080fd5b60025484816117c157fe5b0693506000600285836001015401816117d657fe5b04905060405160200180807f6b69747479000000000000000000000000000000000000000000000000000000815250600501905060405160208183030381529060405280519060200120846040516020018082805190602001908083835b602083106118575780518252602082019150602081019050602083039250611834565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040528051906020012014156118ab576063606482816118a457fe5b0682030190505b6118ea6040518060400160405280600681526020017f4e6f4e616d65000000000000000000000000000000000000000000000000000081525082611b67565b6118f382611ef6565b505050505050565b61194e6001600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461203f90919063ffffffff16565b600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506119e46001600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546120c790919063ffffffff16565b600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816005600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b600080826040516020018082805190602001908083835b60208310611b135780518252602082019150602081019050602083039250611af0565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040528051906020012060001c90506002548181611b5e57fe5b06915050919050565b6000600160046040518060c00160405280868152602001858152602001600163ffffffff168152602001600354420163ffffffff168152602001600061ffff168152602001600061ffff16815250908060018154018082558091505090600182039060005260206000209060030201600090919290919091506000820151816000019080519060200190611bfc929190612251565b506020820151816001015560408201518160020160006101000a81548163ffffffff021916908363ffffffff16021790555060608201518160020160046101000a81548163ffffffff021916908363ffffffff16021790555060808201518160020160086101000a81548161ffff021916908361ffff16021790555060a082015181600201600a6101000a81548161ffff021916908361ffff1602179055505050039050336005600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611d456001600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461203f90919063ffffffff16565b600660003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507f88f026aacbbecc90c18411df4b1185fd8d9be2470f1962f192bf84a27d0704b78184846040518084815260200180602001838152602001828103825284818151815260200191508051906020019080838360005b83811015611df8578082015181840152602081019050611ddd565b50505050905090810190601f168015611e255780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a1505050565b6000611e51600160095461203f90919063ffffffff16565b600981905550814233600954604051602001808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140182815260200193505050506040516020818303038152906040528051906020012060001c81611eca57fe5b069050919050565b60008082840190508361ffff168161ffff161015611eec57fe5b8091505092915050565b60035442018160020160046101000a81548163ffffffff021916908363ffffffff16021790555050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611f5a57600080fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000428260020160049054906101000a900463ffffffff1663ffffffff1611159050919050565b6000808284019050838110156120bd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600061210983836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612111565b905092915050565b60008383111582906121be576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612183578082015181840152602081019050612168565b50505050905090810190601f1680156121b05780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385039050809150509392505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061221257803560ff1916838001178555612240565b82800160010185558215612240579182015b8281111561223f578235825591602001919060010190612224565b5b50905061224d91906122d1565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061229257805160ff19168380011785556122c0565b828001600101855582156122c0579182015b828111156122bf5782518255916020019190600101906122a4565b5b5090506122cd91906122d1565b5090565b6122f391905b808211156122ef5760008160009055506001016122d7565b5090565b9056fea265627a7a72315820a51ff617e191b9a50c14f4f6ec96f1ccaee214484ad9ef9d78e6b6ecca9e24d264736f6c63430005110032 \ No newline at end of file diff --git a/runtime/unc-test-contracts/res/test_contract_ts.wasm b/runtime/unc-test-contracts/res/test_contract_ts.wasm new file mode 100644 index 000000000..d8d2e178a Binary files /dev/null and b/runtime/unc-test-contracts/res/test_contract_ts.wasm differ diff --git a/runtime/unc-test-contracts/src/lib.rs b/runtime/unc-test-contracts/src/lib.rs new file mode 100644 index 000000000..bb2dfcf5e --- /dev/null +++ b/runtime/unc-test-contracts/src/lib.rs @@ -0,0 +1,273 @@ +#![doc = include_str!("../README.md")] + +use arbitrary::Arbitrary; +use once_cell::sync::OnceCell; +use rand::{Fill, SeedableRng}; +use std::path::Path; + +/// Parse a WASM contract from WAT representation. +pub fn wat_contract(wat: &str) -> Vec { + wat::parse_str(wat).unwrap_or_else(|err| panic!("invalid wat: {err}\n{wat}")) +} + +/// Trivial contract with a do-nothing main function. +pub fn trivial_contract() -> &'static [u8] { + static CONTRACT: OnceCell> = OnceCell::new(); + CONTRACT.get_or_init(|| wat_contract(r#"(module (func (export "main")))"#)).as_slice() +} + +/// Contract with exact size in bytes. +pub fn sized_contract(size: usize) -> Vec { + let payload = "x".repeat(size); + let base_size = wat_contract(&format!( + r#"(module + (memory 1) + (func (export "main")) + (data (i32.const 0) "{payload}") + )"# + )) + .len(); + let adjusted_size = size as i64 - (base_size as i64 - size as i64); + let payload = "x".repeat(adjusted_size as usize); + let code = format!( + r#"(module + (memory 1) + (func (export "main")) + (data (i32.const 0) "{payload}") + )"# + ); + let contract = wat_contract(&code); + assert_eq!(contract.len(), size); + contract +} + +/// Standard test contract which can call various host functions. +/// +/// Note: the contract relies on the latest stable protocol version, and might +/// not work for tests using an older version. In particular, if a test depends +/// on a specific protocol version, it should use [`backwards_compatible_rs_contract`]. +pub fn rs_contract() -> &'static [u8] { + include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/res/", "test_contract_rs.wasm")) +} + +/// Standard test contract which is compatible any protocol version, including +/// the oldest one. +/// +/// This is useful for tests that use a specific protocol version rather then +/// just the latest one. In particular, protocol upgrade tests should use this +/// function rather than [`rs_contract`]. +/// +/// Note: Unlike other contracts, this is not automatically build from source +/// but instead a WASM in checked into source control. To serve the oldest +/// protocol version, we need a WASM that does not contain instructions beyond +/// the WASM MVP. Rustc >=1.70 uses LLVM with the [sign +/// extension](https://github.com/WebAssembly/spec/blob/main/proposals/sign-extension-ops/Overview.md) +/// enabled. So we have to build it with Rustc <= 1.69. If we need to update the +/// contracts content, we can build it manually with an older compiler and check +/// in the new WASM. +pub fn backwards_compatible_rs_contract() -> &'static [u8] { + static CONTRACT: OnceCell> = OnceCell::new(); + CONTRACT.get_or_init(|| read_contract("backwards_compatible_rs_contract.wasm")).as_slice() +} + +/// Standard test contract which additionally includes all host functions from +/// the nightly protocol. +pub fn nightly_rs_contract() -> &'static [u8] { + static CONTRACT: OnceCell> = OnceCell::new(); + CONTRACT.get_or_init(|| read_contract("nightly_test_contract_rs.wasm")).as_slice() +} + +pub fn ts_contract() -> &'static [u8] { + static CONTRACT: OnceCell> = OnceCell::new(); + CONTRACT.get_or_init(|| read_contract("test_contract_ts.wasm")).as_slice() +} + +pub fn fuzzing_contract() -> &'static [u8] { + static CONTRACT: OnceCell> = OnceCell::new(); + CONTRACT.get_or_init(|| read_contract("contract_for_fuzzing_rs.wasm")).as_slice() +} + +/// NEP-141 implementation (fungible token contract). +/// +/// The code is available here: +/// https://github.com/near/unc-sdk-rs/tree/master/examples/fungible-token +/// +/// We keep a static WASM of this for our integration tests. We don't have to +/// update it with every SDK release, any contract implementing the interface +/// defined by NEP-141 is sufficient. But for future reference, the WASM was +/// compiled with SDK version 4.1.1. +pub fn ft_contract() -> &'static [u8] { + static CONTRACT: OnceCell> = OnceCell::new(); + CONTRACT.get_or_init(|| read_contract("fungible_token.wasm")).as_slice() +} + +/// Smallest (reasonable) contract possible to build. +/// +/// This contract is guaranteed to have a "sum" function +pub fn smallest_rs_contract() -> &'static [u8] { + static CONTRACT: OnceCell> = OnceCell::new(); + CONTRACT + .get_or_init(|| { + wat_contract( + r#"(module + (func $input (import "env" "input") (param i64)) + (func $sum (export "sum") (param i32 i32) (result i32) + (call $input + (i64.const 0)) + (i32.add + (local.get 1) + (local.get 0))) + (memory (export "memory") 16) + (global (mut i32) (i32.const 1048576)) + (global (export "__data_end") i32 (i32.const 1048576)) + (global (export "__heap_base") i32 (i32.const 1048576)))"#, + ) + }) + .as_slice() +} + +/// Contract that has all methods required by the gas parameter estimator. +pub fn estimator_contract() -> &'static [u8] { + static CONTRACT: OnceCell> = OnceCell::new(); + let file_name = if cfg!(feature = "nightly") { + "nightly_estimator_contract.wasm" + } else { + "stable_estimator_contract.wasm" + }; + CONTRACT.get_or_init(|| read_contract(file_name)).as_slice() +} + +/// Read given wasm file or panic if unable to. +fn read_contract(file_name: &str) -> Vec { + let base = Path::new(env!("CARGO_MANIFEST_DIR")); + let path = base.join("res").join(file_name); + match std::fs::read(&path) { + Ok(data) => data, + Err(err) => panic!("{}: {}", path.display(), err), + } +} + +#[test] +fn smoke_test() { + assert!(!rs_contract().is_empty()); + assert!(!nightly_rs_contract().is_empty()); + assert!(!ts_contract().is_empty()); + assert!(!trivial_contract().is_empty()); + assert!(!fuzzing_contract().is_empty()); + assert!(!backwards_compatible_rs_contract().is_empty()); + assert!(!ft_contract().is_empty()); +} + +pub struct LargeContract { + pub functions: u32, + pub locals_per_function: u32, + pub panic_imports: u32, // How many times to import `env.panic` +} + +impl Default for LargeContract { + fn default() -> Self { + Self { functions: 1, locals_per_function: 0, panic_imports: 0 } + } +} + +impl LargeContract { + /// Construct a contract with many entitites. + /// + /// Currently supports constructing contracts that contain a specified number of functions with the + /// specified number of locals each. + /// + /// Exports a function called `main` that does nothing. + pub fn make(&self) -> Vec { + use wasm_encoder::{ + CodeSection, EntityType, ExportKind, ExportSection, Function, FunctionSection, + ImportSection, Instruction, Module, TypeSection, ValType, + }; + + // Won't generate a valid WASM without functions. + assert!(self.functions >= 1, "must specify at least 1 function to be generated"); + let mut module = Module::new(); + let mut type_section = TypeSection::new(); + type_section.function([], []); + module.section(&type_section); + + if self.panic_imports != 0 { + let mut import_section = ImportSection::new(); + for _ in 0..self.panic_imports { + import_section.import("env", "panic", EntityType::Function(0)); + } + module.section(&import_section); + } + + let mut functions_section = FunctionSection::new(); + for _ in 0..self.functions { + functions_section.function(0); + } + module.section(&functions_section); + + let mut exports_section = ExportSection::new(); + exports_section.export("main", ExportKind::Func, 0); + module.section(&exports_section); + + let mut code_section = CodeSection::new(); + for _ in 0..self.functions { + let mut f = Function::new([(self.locals_per_function, ValType::I64)]); + f.instruction(&Instruction::End); + code_section.function(&f); + } + module.section(&code_section); + + module.finish() + } +} + +/// Generate contracts with function bodies of large continuous sequences of `nop` instruction. +/// +/// This is particularly useful for testing how gas instrumentation works and its corner cases. +pub fn function_with_a_lot_of_nop(nops: u64) -> Vec { + use wasm_encoder::{ + CodeSection, ExportKind, ExportSection, Function, FunctionSection, Instruction, Module, + TypeSection, + }; + let mut module = Module::new(); + let mut type_section = TypeSection::new(); + type_section.function([], []); + module.section(&type_section); + let mut functions_section = FunctionSection::new(); + functions_section.function(0); + module.section(&functions_section); + let mut exports_section = ExportSection::new(); + exports_section.export("main", ExportKind::Func, 0); + module.section(&exports_section); + let mut code_section = CodeSection::new(); + let mut f = Function::new([]); + for _ in 0..nops { + f.instruction(&Instruction::Nop); + } + f.instruction(&Instruction::End); + code_section.function(&f); + module.section(&code_section); + module.finish() +} + +/// Generate an arbitrary valid contract. +pub fn arbitrary_contract(seed: u64) -> Vec { + let mut rng = rand::rngs::SmallRng::seed_from_u64(seed); + let mut buffer = vec![0u8; 10240]; + buffer.try_fill(&mut rng).expect("fill buffer with random data"); + let mut arbitrary = arbitrary::Unstructured::new(&buffer); + let mut config = wasm_smith::SwarmConfig::arbitrary(&mut arbitrary).expect("make swarm config"); + config.max_memories = 1; + config.max_tables = 1; + config.bulk_memory_enabled = false; + config.reference_types_enabled = false; + config.memory64_enabled = false; + config.simd_enabled = false; + config.multi_value_enabled = false; + config.relaxed_simd_enabled = false; + config.exceptions_enabled = false; + config.saturating_float_to_int_enabled = false; + config.sign_extension_enabled = false; + config.available_imports = Some(backwards_compatible_rs_contract().to_vec()); + let module = wasm_smith::Module::new(config, &mut arbitrary).expect("generate module"); + module.to_bytes() +} diff --git a/runtime/unc-test-contracts/test-contract-rs/Cargo.lock b/runtime/unc-test-contracts/test-contract-rs/Cargo.lock new file mode 100644 index 000000000..d3cfc2436 --- /dev/null +++ b/runtime/unc-test-contracts/test-contract-rs/Cargo.lock @@ -0,0 +1,46 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "itoa" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" + +[[package]] +name = "ryu" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" + +[[package]] +name = "serde" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" + +[[package]] +name = "serde_json" +version = "1.0.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "test-contract-rs" +version = "0.1.0" +dependencies = [ + "base64", + "serde_json", +] diff --git a/runtime/unc-test-contracts/test-contract-rs/Cargo.toml b/runtime/unc-test-contracts/test-contract-rs/Cargo.toml new file mode 100644 index 000000000..5d465193f --- /dev/null +++ b/runtime/unc-test-contracts/test-contract-rs/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "test-contract-rs" +version = "0.1.0" +authors = ["Hello Inc "] +publish = false +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +base64 = "0.21" +serde_json = "1" + +[profile.release] +codegen-units = 1 +# Tell `rustc` to optimize for small code size. +opt-level = "z" +strip = true +lto = true +debug = false +panic = "abort" +rpath = false +debug-assertions = false +incremental = false + +[workspace] +members = [] + +[features] +nightly = [] +latest_protocol = [] diff --git a/runtime/unc-test-contracts/test-contract-rs/src/lib.rs b/runtime/unc-test-contracts/test-contract-rs/src/lib.rs new file mode 100644 index 000000000..dc673e31d --- /dev/null +++ b/runtime/unc-test-contracts/test-contract-rs/src/lib.rs @@ -0,0 +1,1246 @@ +#![allow(clippy::all)] + +use std::mem::size_of; + +#[allow(unused)] +extern "C" { + // ############# + // # Registers # + // ############# + fn read_register(register_id: u64, ptr: u64); + fn register_len(register_id: u64) -> u64; + // ############### + // # Context API # + // ############### + fn current_account_id(register_id: u64); + fn signer_account_id(register_id: u64); + fn signer_account_pk(register_id: u64); + fn predecessor_account_id(register_id: u64); + fn input(register_id: u64); + fn block_index() -> u64; + fn block_timestamp() -> u64; + fn epoch_height() -> u64; + fn storage_usage() -> u64; + // ################# + // # Economics API # + // ################# + fn account_balance(balance_ptr: u64); + fn attached_deposit(balance_ptr: u64); + fn prepaid_gas() -> u64; + fn used_gas() -> u64; + // ############ + // # Math API # + // ############ + fn random_seed(register_id: u64); + fn sha256(value_len: u64, value_ptr: u64, register_id: u64); + // ##################### + // # Miscellaneous API # + // ##################### + fn value_return(value_len: u64, value_ptr: u64); + fn panic(); + fn panic_utf8(len: u64, ptr: u64); + fn log_utf8(len: u64, ptr: u64); + fn log_utf16(len: u64, ptr: u64); + fn abort(msg_ptr: u32, filename_ptr: u32, line: u32, col: u32); + // ################ + // # Promises API # + // ################ + fn promise_create( + account_id_len: u64, + account_id_ptr: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: u64, + ) -> u64; + fn promise_then( + promise_index: u64, + account_id_len: u64, + account_id_ptr: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: u64, + ) -> u64; + fn promise_and(promise_idx_ptr: u64, promise_idx_count: u64) -> u64; + fn promise_batch_create(account_id_len: u64, account_id_ptr: u64) -> u64; + fn promise_batch_then(promise_index: u64, account_id_len: u64, account_id_ptr: u64) -> u64; + // ####################### + // # Promise API actions # + // ####################### + fn promise_batch_action_create_account(promise_index: u64); + fn promise_batch_action_deploy_contract(promise_index: u64, code_len: u64, code_ptr: u64); + fn promise_batch_action_function_call( + promise_index: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: u64, + ); + #[cfg(feature = "latest_protocol")] + fn promise_batch_action_function_call_weight( + promise_index: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: u64, + gas_weight: u64, + ); + fn promise_batch_action_transfer(promise_index: u64, amount_ptr: u64); + fn promise_batch_action_stake( + promise_index: u64, + amount_ptr: u64, + public_key_len: u64, + public_key_ptr: u64, + ); + fn promise_batch_action_add_key_with_full_access( + promise_index: u64, + public_key_len: u64, + public_key_ptr: u64, + nonce: u64, + ); + fn promise_batch_action_add_key_with_function_call( + promise_index: u64, + public_key_len: u64, + public_key_ptr: u64, + nonce: u64, + allowance_ptr: u64, + receiver_id_len: u64, + receiver_id_ptr: u64, + method_names_len: u64, + method_names_ptr: u64, + ); + fn promise_batch_action_delete_key( + promise_index: u64, + public_key_len: u64, + public_key_ptr: u64, + ); + fn promise_batch_action_delete_account( + promise_index: u64, + beneficiary_id_len: u64, + beneficiary_id_ptr: u64, + ); + // ####################### + // # Promise API results # + // ####################### + fn promise_results_count() -> u64; + fn promise_result(result_idx: u64, register_id: u64) -> u64; + fn promise_return(promise_id: u64); + // ############### + // # Storage API # + // ############### + fn storage_write( + key_len: u64, + key_ptr: u64, + value_len: u64, + value_ptr: u64, + register_id: u64, + ) -> u64; + fn storage_read(key_len: u64, key_ptr: u64, register_id: u64) -> u64; + fn storage_remove(key_len: u64, key_ptr: u64, register_id: u64) -> u64; + fn storage_has_key(key_len: u64, key_ptr: u64) -> u64; + fn storage_iter_prefix(prefix_len: u64, prefix_ptr: u64) -> u64; + fn storage_iter_range(start_len: u64, start_ptr: u64, end_len: u64, end_ptr: u64) -> u64; + fn storage_iter_next(iterator_id: u64, key_register_id: u64, value_register_id: u64) -> u64; + fn gas(ops: u32); + // ################# + // # Validator API # + // ################# + fn validator_stake(account_id_len: u64, account_id_ptr: u64, stake_ptr: u64); + fn validator_total_stake(stake_ptr: u64); + // ################### + // # Math Extensions # + // ################### + #[cfg(feature = "latest_protocol")] + fn ripemd160(value_len: u64, value_ptr: u64, register_id: u64); + // ################# + // # alt_bn128 API # + // ################# + #[cfg(feature = "latest_protocol")] + fn alt_bn128_g1_multiexp(value_len: u64, value_ptr: u64, register_id: u64); + #[cfg(feature = "latest_protocol")] + fn alt_bn128_g1_sum(value_len: u64, value_ptr: u64, register_id: u64); + #[cfg(feature = "latest_protocol")] + fn alt_bn128_pairing_check(value_len: u64, value_ptr: u64) -> u64; +} + +macro_rules! ext_test { + ($export_func:ident, $call_ext:expr) => { + #[no_mangle] + pub unsafe fn $export_func() { + $call_ext(0); + let result = vec![0; register_len(0) as usize]; + read_register(0, result.as_ptr() as *const u64 as u64); + value_return(result.len() as u64, result.as_ptr() as *const u64 as u64); + } + }; +} + +macro_rules! ext_test_u64 { + ($export_func:ident, $call_ext:expr) => { + #[no_mangle] + pub unsafe fn $export_func() { + let mut result = [0u8; size_of::()]; + result.copy_from_slice(&$call_ext().to_le_bytes()); + value_return(result.len() as u64, result.as_ptr() as *const u64 as u64); + } + }; +} + +macro_rules! ext_test_u128 { + ($export_func:ident, $call_ext:expr) => { + #[no_mangle] + pub unsafe fn $export_func() { + let result = &[0u8; size_of::()]; + $call_ext(result.as_ptr() as *const u64 as u64); + value_return(result.len() as u64, result.as_ptr() as *const u64 as u64); + } + }; +} + +ext_test_u64!(ext_storage_usage, storage_usage); +ext_test_u64!(ext_block_index, block_index); +ext_test_u64!(ext_block_timestamp, block_timestamp); +ext_test_u64!(ext_prepaid_gas, prepaid_gas); + +ext_test!(ext_random_seed, random_seed); +ext_test!(ext_predecessor_account_id, predecessor_account_id); +ext_test!(ext_signer_pk, signer_account_pk); +ext_test!(ext_signer_id, signer_account_id); +ext_test!(ext_account_id, current_account_id); + +ext_test_u128!(ext_account_balance, account_balance); +ext_test_u128!(ext_attached_deposit, attached_deposit); + +ext_test_u128!(ext_validator_total_stake, validator_total_stake); + +#[no_mangle] +pub unsafe fn ext_sha256() { + input(0); + let bytes = vec![0; register_len(0) as usize]; + read_register(0, bytes.as_ptr() as *const u64 as u64); + sha256(bytes.len() as u64, bytes.as_ptr() as *const u64 as u64, 0); + let result = vec![0; register_len(0) as usize]; + read_register(0, result.as_ptr() as *const u64 as u64); + value_return(result.len() as u64, result.as_ptr() as *const u64 as u64); +} + +#[no_mangle] +pub unsafe fn ext_used_gas() { + let initial_used_gas = used_gas(); + let mut a = 1; + let mut b = 1; + for _ in 0..30 { + let c = a + b; + a = b; + b = c; + } + assert_eq!(a, 1346269); + let gas = used_gas() - initial_used_gas; + let result = gas.to_le_bytes(); + value_return(result.len() as u64, result.as_ptr() as *const u64 as u64); +} + +#[no_mangle] +pub unsafe fn ext_validator_stake() { + input(0); + let account_id = vec![0; register_len(0) as usize]; + read_register(0, account_id.as_ptr() as *const u64 as u64); + let result = [0u8; size_of::()]; + validator_stake( + account_id.len() as u64, + account_id.as_ptr() as *const u64 as u64, + result.as_ptr() as *const u64 as u64, + ); + value_return(result.len() as u64, result.as_ptr() as *const u64 as u64); +} + +/// Write key-value pair into storage. +/// Input is the byte array where the value is `u64` represented by last 8 bytes and key is represented by the first +/// `register_len(0) - 8` bytes. +#[no_mangle] +pub unsafe fn write_key_value() { + input(0); + let data_len = register_len(0) as usize; + let value_len = size_of::(); + let data = vec![0u8; data_len]; + read_register(0, data.as_ptr() as u64); + + let key = &data[0..data_len - value_len]; + let value = &data[data_len - value_len..]; + let result = storage_write( + key.len() as u64, + key.as_ptr() as u64, + value.len() as u64, + value.as_ptr() as u64, + 1, + ); + value_return(size_of::() as u64, &result as *const u64 as u64); +} + +#[no_mangle] +pub unsafe fn write_block_height() { + let block_height = block_index(); + let mut key = [0u8; size_of::()]; + key.copy_from_slice(&block_height.to_le_bytes()); + let value = b"hello"; + storage_write(key.len() as _, key.as_ptr() as _, value.len() as _, value.as_ptr() as _, 0); +} + +#[no_mangle] +pub unsafe fn write_random_value() { + random_seed(0); + let data = [0u8; 32]; + read_register(0, data.as_ptr() as u64); + let value = b"hello"; + storage_write(data.len() as _, data.as_ptr() as _, value.len() as _, value.as_ptr() as _, 1); +} + +#[no_mangle] +pub unsafe fn read_value() { + input(0); + if register_len(0) != size_of::() as u64 { + panic() + } + let key = [0u8; size_of::()]; + read_register(0, key.as_ptr() as u64); + let result = storage_read(key.len() as u64, key.as_ptr() as u64, 1); + if result == 1 { + let value = [0u8; size_of::()]; + read_register(1, value.as_ptr() as u64); + value_return(value.len() as u64, &value as *const u8 as u64); + } +} + +#[no_mangle] +pub unsafe fn log_something() { + let data = b"hello"; + log_utf8(data.len() as u64, data.as_ptr() as _); +} + +#[no_mangle] +pub unsafe fn loop_forever() { + loop {} +} + +#[no_mangle] +pub unsafe fn abort_with_zero() { + // Tries to abort with 0 ptr to check underflow handling. + abort(0, 0, 0, 0); +} + +#[no_mangle] +pub unsafe fn panic_with_message() { + let data = b"WAT?"; + panic_utf8(data.len() as u64, data.as_ptr() as _); +} + +#[no_mangle] +pub unsafe fn panic_after_logging() { + let data = b"hello"; + log_utf8(data.len() as u64, data.as_ptr() as _); + let data = b"WAT?"; + panic_utf8(data.len() as u64, data.as_ptr() as _); +} + +#[no_mangle] +pub unsafe fn run_test() { + let value: [u8; 4] = 10i32.to_le_bytes(); + value_return(value.len() as u64, value.as_ptr() as _); +} + +#[no_mangle] +pub unsafe fn run_test_with_storage_change() { + let key = b"hello"; + let value = b"world"; + storage_write(key.len() as _, key.as_ptr() as _, value.len() as _, value.as_ptr() as _, 0); +} + +#[no_mangle] +pub unsafe fn sum_with_input() { + input(0); + if register_len(0) != 2 * size_of::() as u64 { + panic() + } + let data = [0u8; 2 * size_of::()]; + read_register(0, data.as_ptr() as u64); + + let mut key = [0u8; size_of::()]; + let mut value = [0u8; size_of::()]; + key.copy_from_slice(&data[..size_of::()]); + value.copy_from_slice(&data[size_of::()..]); + let key = u64::from_le_bytes(key); + let value = u64::from_le_bytes(value); + let result = key + value; + value_return(size_of::() as u64, &result as *const u64 as u64); +} + +/// Writes and reads some data into/from storage. Uses 8-bit key/values. +#[no_mangle] +pub unsafe fn benchmark_storage_8b() { + input(0); + if register_len(0) != size_of::() as u64 { + panic() + } + let data = [0u8; size_of::()]; + read_register(0, data.as_ptr() as u64); + let n: u64 = u64::from_le_bytes(data); + + let mut sum = 0u64; + for i in 0..n { + let el = i.to_le_bytes(); + storage_write(el.len() as u64, el.as_ptr() as u64, el.len() as u64, el.as_ptr() as u64, 0); + + let result = storage_read(el.len() as u64, el.as_ptr() as u64, 0); + if result == 1 { + let value = [0u8; size_of::()]; + read_register(0, value.as_ptr() as u64); + sum += u64::from_le_bytes(value); + } + } + + value_return(size_of::() as u64, &sum as *const u64 as u64); +} + +#[inline] +fn generate_data(data: &mut [u8]) { + for i in 0..data.len() { + data[i] = (i % u8::MAX as usize) as u8; + } +} + +/// Writes and reads some data into/from storage. Uses 10KiB key/values. +#[no_mangle] +pub unsafe fn benchmark_storage_10kib() { + input(0); + if register_len(0) != size_of::() as u64 { + panic() + } + let data = [0u8; size_of::()]; + read_register(0, data.as_ptr() as u64); + let n: u64 = u64::from_le_bytes(data); + + let mut el = [0u8; 10 << 10]; + generate_data(&mut el); + + let mut sum = 0u64; + for i in 0..n { + el[..size_of::()].copy_from_slice(&i.to_le_bytes()); + storage_write(el.len() as u64, el.as_ptr() as u64, el.len() as u64, el.as_ptr() as u64, 0); + + let result = storage_read(el.len() as u64, el.as_ptr() as u64, 0); + if result == 1 { + let el = [0u8; 10 << 10]; + read_register(0, el.as_ptr() as u64); + let mut value = [0u8; size_of::()]; + value.copy_from_slice(&el[0..size_of::()]); + sum += u64::from_le_bytes(value); + } + } + + value_return(size_of::() as u64, &sum as *const u64 as u64); +} + +/// Passes through input into output. +#[no_mangle] +pub unsafe fn pass_through() { + input(0); + if register_len(0) != size_of::() as u64 { + panic() + } + let data = [0u8; size_of::()]; + read_register(0, data.as_ptr() as u64); + value_return(data.len() as u64, data.as_ptr() as u64); +} + +/// Sums numbers. +#[no_mangle] +pub unsafe fn sum_n() { + input(0); + if register_len(0) != size_of::() as u64 { + panic() + } + let data = [0u8; size_of::()]; + read_register(0, data.as_ptr() as u64); + let n = u64::from_le_bytes(data); + + let mut sum = 0u64; + for i in 0..n { + // LLVM optimizes sum += i into O(1) computation, use volatile to thwart + // that. + let new_sum = std::ptr::read_volatile(&sum).wrapping_add(i); + std::ptr::write_volatile(&mut sum, new_sum); + } + + let data = sum.to_le_bytes(); + value_return(data.len() as u64, data.as_ptr() as u64); +} + +/// Calculates Fibonacci numbers in inefficient way. Used to burn gas for the +/// sanity/max_gas_burnt_view.py test. The implementation has exponential +/// complexity (1.62^n to be exact) so even small increase in argument result in +/// large increase in gas use. +#[no_mangle] +pub unsafe fn fibonacci() { + input(0); + if register_len(0) != 1 { + panic() + } + let mut n: u8 = 0; + read_register(0, &mut n as *mut u8 as u64); + let data = fib(n).to_le_bytes(); + value_return(data.len() as u64, data.as_ptr() as u64); +} + +fn fib(n: u8) -> u64 { + if n < 2 { + n as u64 + } else { + fib(n - 2) + fib(n - 1) + } +} + +#[no_mangle] +pub unsafe fn insert_strings() { + input(0); + if register_len(0) != 2 * size_of::() as u64 { + panic() + } + let data = [0u8; 2 * size_of::()]; + read_register(0, data.as_ptr() as u64); + + let mut from = [0u8; size_of::()]; + let mut to = [0u8; size_of::()]; + from.copy_from_slice(&data[..size_of::()]); + to.copy_from_slice(&data[size_of::()..]); + let from = u64::from_le_bytes(from); + let to = u64::from_le_bytes(to); + let s = vec![b'a'; to as usize]; + for i in from..to { + let mut key = s[(to - i) as usize..].to_vec(); + key.push(b'b'); + let value = b"x"; + storage_write( + key.len() as u64, + key.as_ptr() as u64, + value.len() as u64, + value.as_ptr() as u64, + 0, + ); + } +} + +#[no_mangle] +pub unsafe fn delete_strings() { + input(0); + if register_len(0) != 2 * size_of::() as u64 { + panic() + } + let data = [0u8; 2 * size_of::()]; + read_register(0, data.as_ptr() as u64); + + let mut from = [0u8; size_of::()]; + let mut to = [0u8; size_of::()]; + from.copy_from_slice(&data[..size_of::()]); + to.copy_from_slice(&data[size_of::()..]); + let from = u64::from_le_bytes(from); + let to = u64::from_le_bytes(to); + let s = vec![b'a'; to as usize]; + for i in from..to { + let mut key = s[(to - i) as usize..].to_vec(); + key.push(b'b'); + storage_remove(key.len() as u64, key.as_ptr() as u64, 0); + } +} + +#[no_mangle] +pub unsafe fn recurse() { + input(0); + if register_len(0) != size_of::() as u64 { + panic() + } + let data = [0u8; size_of::()]; + read_register(0, data.as_ptr() as u64); + let n = u64::from_le_bytes(data); + let res = internal_recurse(n); + let data = res.to_le_bytes(); + value_return(data.len() as u64, data.as_ptr() as u64); +} + +/// Rust compiler is getting smarter and starts to optimize my deep recursion. +/// We're going to fight it with a more obscure implementations. +#[no_mangle] +#[inline(never)] +fn internal_recurse(n: u64) -> u64 { + if n <= 1 { + n + } else { + let a = internal_recurse(n - 1) + 1; + if a % 2 == 1 { + (a + n) / 2 + } else { + a + } + } +} + +#[no_mangle] +pub fn out_of_memory() { + let mut vec = Vec::new(); + loop { + vec.push(vec![0; 1024]); + } +} + +// Can be used for debugging +#[no_mangle] +fn log_u64(msg: u64) { + unsafe { + log_utf8(8, &msg as *const u64 as u64); + } +} + +pub fn from_base64(s: &str) -> Vec { + let engine = &base64::engine::general_purpose::STANDARD; + base64::Engine::decode(engine, s).unwrap() +} + +#[no_mangle] +fn call_promise() { + unsafe { + input(0); + let data = vec![0u8; register_len(0) as usize]; + read_register(0, data.as_ptr() as u64); + let input_args: serde_json::Value = serde_json::from_slice(&data).unwrap(); + for arg in input_args.as_array().unwrap() { + let actual_id = if let Some(create) = arg.get("create") { + let account_id = create["account_id"].as_str().unwrap().as_bytes(); + let method_name = create["method_name"].as_str().unwrap().as_bytes(); + let arguments = serde_json::to_vec(&create["arguments"]).unwrap(); + let amount = create["amount"].as_str().unwrap().parse::().unwrap(); + let gas = create["gas"].as_i64().unwrap() as u64; + promise_create( + account_id.len() as u64, + account_id.as_ptr() as u64, + method_name.len() as u64, + method_name.as_ptr() as u64, + arguments.len() as u64, + arguments.as_ptr() as u64, + &amount as *const u128 as *const u64 as u64, + gas, + ) + } else if let Some(then) = arg.get("then") { + let promise_index = then["promise_index"].as_i64().unwrap() as u64; + let account_id = then["account_id"].as_str().unwrap().as_bytes(); + let method_name = then["method_name"].as_str().unwrap().as_bytes(); + let arguments = serde_json::to_vec(&then["arguments"]).unwrap(); + let amount = then["amount"].as_str().unwrap().parse::().unwrap(); + let gas = then["gas"].as_i64().unwrap() as u64; + promise_then( + promise_index, + account_id.len() as u64, + account_id.as_ptr() as u64, + method_name.len() as u64, + method_name.as_ptr() as u64, + arguments.len() as u64, + arguments.as_ptr() as u64, + &amount as *const u128 as *const u64 as u64, + gas, + ) + } else if let Some(and) = arg.get("and") { + let and = and.as_array().unwrap(); + let mut curr = and[0].as_i64().unwrap() as u64; + for other in &and[1..] { + curr = promise_and(curr, other.as_i64().unwrap() as u64); + } + curr + } else if let Some(batch_create) = arg.get("batch_create") { + let account_id = batch_create["account_id"].as_str().unwrap().as_bytes(); + promise_batch_create(account_id.len() as u64, account_id.as_ptr() as u64) + } else if let Some(batch_then) = arg.get("batch_then") { + let promise_index = batch_then["promise_index"].as_i64().unwrap() as u64; + let account_id = batch_then["account_id"].as_str().unwrap().as_bytes(); + promise_batch_then( + promise_index, + account_id.len() as u64, + account_id.as_ptr() as u64, + ) + } else if let Some(action) = arg.get("action_create_account") { + let promise_index = action["promise_index"].as_i64().unwrap() as u64; + promise_batch_action_create_account(promise_index); + promise_index + } else if let Some(action) = arg.get("action_deploy_contract") { + let promise_index = action["promise_index"].as_i64().unwrap() as u64; + let code = from_base64(action["code"].as_str().unwrap()); + promise_batch_action_deploy_contract( + promise_index, + code.len() as u64, + code.as_ptr() as u64, + ); + promise_index + } else if let Some(action) = arg.get("action_function_call") { + let promise_index = action["promise_index"].as_i64().unwrap() as u64; + let method_name = action["method_name"].as_str().unwrap().as_bytes(); + let arguments = serde_json::to_vec(&action["arguments"]).unwrap(); + let amount = action["amount"].as_str().unwrap().parse::().unwrap(); + let gas = action["gas"].as_i64().unwrap() as u64; + promise_batch_action_function_call( + promise_index, + method_name.len() as u64, + method_name.as_ptr() as u64, + arguments.len() as u64, + arguments.as_ptr() as u64, + &amount as *const u128 as *const u64 as u64, + gas, + ); + promise_index + } else if let Some(action) = arg.get("action_transfer") { + let promise_index = action["promise_index"].as_i64().unwrap() as u64; + let amount = action["amount"].as_str().unwrap().parse::().unwrap(); + promise_batch_action_transfer( + promise_index, + &amount as *const u128 as *const u64 as u64, + ); + promise_index + } else if let Some(action) = arg.get("action_stake") { + let promise_index = action["promise_index"].as_i64().unwrap() as u64; + let amount = action["amount"].as_str().unwrap().parse::().unwrap(); + let public_key = from_base64(action["public_key"].as_str().unwrap()); + promise_batch_action_stake( + promise_index, + &amount as *const u128 as *const u64 as u64, + public_key.len() as u64, + public_key.as_ptr() as u64, + ); + promise_index + } else if let Some(action) = arg.get("action_add_key_with_full_access") { + let promise_index = action["promise_index"].as_i64().unwrap() as u64; + let public_key = from_base64(action["public_key"].as_str().unwrap()); + let nonce = action["nonce"].as_i64().unwrap() as u64; + promise_batch_action_add_key_with_full_access( + promise_index, + public_key.len() as u64, + public_key.as_ptr() as u64, + nonce, + ); + promise_index + } else if let Some(action) = arg.get("action_add_key_with_function_call") { + let promise_index = action["promise_index"].as_i64().unwrap() as u64; + let public_key = from_base64(action["public_key"].as_str().unwrap()); + let nonce = action["nonce"].as_i64().unwrap() as u64; + let allowance = action["allowance"].as_str().unwrap().parse::().unwrap(); + let receiver_id = action["receiver_id"].as_str().unwrap().as_bytes(); + let method_names = action["method_names"].as_str().unwrap().as_bytes(); + promise_batch_action_add_key_with_function_call( + promise_index, + public_key.len() as u64, + public_key.as_ptr() as u64, + nonce, + &allowance as *const u128 as *const u64 as u64, + receiver_id.len() as u64, + receiver_id.as_ptr() as u64, + method_names.len() as u64, + method_names.as_ptr() as u64, + ); + promise_index + } else if let Some(action) = arg.get("action_delete_key") { + let promise_index = action["promise_index"].as_i64().unwrap() as u64; + let public_key = from_base64(action["public_key"].as_str().unwrap()); + promise_batch_action_delete_key( + promise_index, + public_key.len() as u64, + public_key.as_ptr() as u64, + ); + promise_index + } else if let Some(action) = arg.get("action_delete_account") { + let promise_index = action["promise_index"].as_i64().unwrap() as u64; + let beneficiary_id = action["beneficiary_id"].as_str().unwrap().as_bytes(); + promise_batch_action_delete_account( + promise_index, + beneficiary_id.len() as u64, + beneficiary_id.as_ptr() as u64, + ); + promise_index + } else { + unimplemented!() + }; + let expected_id = arg["id"].as_i64().unwrap() as u64; + assert_eq!(actual_id, expected_id); + if let Some(ret) = arg.get("return") { + if ret.as_bool().unwrap() == true { + promise_return(actual_id); + } + } + } + } +} + +#[cfg(feature = "latest_protocol")] +#[no_mangle] +fn attach_unspent_gas_but_burn_all_gas() { + unsafe { + let account_id = "alice.near"; + let promise_idx = promise_batch_create(account_id.len() as u64, account_id.as_ptr() as u64); + + let method_name = "f"; + let arguments_ptr = 0; + let arguments_len = 0; + let amount = 1u128; + let gas_fixed = 0; + let gas_weight = 1; + promise_batch_action_function_call_weight( + promise_idx, + method_name.len() as u64, + method_name.as_ptr() as u64, + arguments_ptr, + arguments_len, + &amount as *const u128 as u64, + gas_fixed, + gas_weight, + ); + loop { + gas(10_000); + } + } +} + +#[cfg(feature = "latest_protocol")] +#[no_mangle] +fn attach_unspent_gas_but_use_all_gas() { + unsafe { + let account_id = "alice.near"; + let promise_idx = promise_batch_create(account_id.len() as u64, account_id.as_ptr() as u64); + + let method_name = "f"; + let arguments_ptr = 0; + let arguments_len = 0; + let amount = 1u128; + let gas_fixed = 0; + let gas_weight = 1; + promise_batch_action_function_call_weight( + promise_idx, + method_name.len() as u64, + method_name.as_ptr() as u64, + arguments_ptr, + arguments_len, + &amount as *const u128 as u64, + gas_fixed, + gas_weight, + ); + + let promise_idx = promise_batch_create(account_id.len() as u64, account_id.as_ptr() as u64); + + let gas_fixed = 10u64.pow(14); + let gas_weight = 0; + promise_batch_action_function_call_weight( + promise_idx, + method_name.len() as u64, + method_name.as_ptr() as u64, + arguments_ptr, + arguments_len, + &amount as *const u128 as u64, + gas_fixed, + gas_weight, + ); + } +} + +#[cfg(feature = "latest_protocol")] +#[no_mangle] +fn do_ripemd() { + let data = b"tesdsst"; + unsafe { + ripemd160(data.len() as _, data.as_ptr() as _, 0); + } +} + +#[no_mangle] +pub unsafe fn noop() {} + +fn insert_account_id_prefix( + prefix: &str, + account_id: Vec, +) -> Result, std::string::FromUtf8Error> { + let mut id = String::from_utf8(account_id)?; + id.insert_str(0, prefix); + Ok(id.as_bytes().to_vec()) +} + +/// Calls all host functions, either directly or via callback. +#[no_mangle] +pub unsafe fn sanity_check() { + // ############# + // # Registers # + // ############# + input(0); + let input_data = vec![0u8; register_len(0) as usize]; + read_register(0, input_data.as_ptr() as u64); + let input_args: serde_json::Value = serde_json::from_slice(&input_data).unwrap(); + + // ############### + // # Context API # + // ############### + current_account_id(1); + let account_id = vec![0u8; register_len(1) as usize]; + read_register(1, account_id.as_ptr() as u64); + + signer_account_pk(1); + let account_public_key = vec![0u8; register_len(1) as usize]; + read_register(1, account_public_key.as_ptr() as u64); + + signer_account_id(1); + predecessor_account_id(1); + + // input() already called when reading the input of the contract call + let _ = block_index(); + let _ = block_timestamp(); + let _ = epoch_height(); + let _ = storage_usage(); + + // ################# + // # Economics API # + // ################# + let balance = [0u8; size_of::()]; + account_balance(balance.as_ptr() as u64); + attached_deposit(balance.as_ptr() as u64); + let available_gas = prepaid_gas() - used_gas(); + + // ############ + // # Math API # + // ############ + random_seed(1); + let value = b"hello"; + sha256(value.len() as u64, value.as_ptr() as u64, 1); + + // ##################### + // # Miscellaneous API # + // ##################### + value_return(value.len() as u64, value.as_ptr() as u64); + + // Calling host functions that terminate execution via promises. + let method_name_panic = b"sanity_check_panic"; + let args_panic = b""; + let amount_zero = 0u128; + let gas_per_promise = available_gas / 50; + assert_eq!( + promise_create( + account_id.len() as u64, + account_id.as_ptr() as u64, + method_name_panic.len() as u64, + method_name_panic.as_ptr() as u64, + args_panic.len() as u64, + args_panic.as_ptr() as u64, + &amount_zero as *const u128 as *const u64 as u64, + gas_per_promise, + ), + 0, + ); + let method_name_panic_utf8 = b"sanity_check_panic_utf8"; + let args_panic_utf8 = b""; + assert_eq!( + promise_create( + account_id.len() as u64, + account_id.as_ptr() as u64, + method_name_panic_utf8.len() as u64, + method_name_panic_utf8.as_ptr() as u64, + args_panic_utf8.len() as u64, + args_panic_utf8.as_ptr() as u64, + &amount_zero as *const u128 as *const u64 as u64, + gas_per_promise, + ), + 1, + ); + + // Note: Skip `abort`, it's called only by code generated by AssemblyScript. + + log_utf8(value.len() as u64, value.as_ptr() as u64); + let value_utf16 = b"h\x00e\x00l\x00l\x00o\x00"; + log_utf16(value_utf16.len() as u64, value_utf16.as_ptr() as u64); + + // ################ + // # Promises API # + // ################ + let method_name_noop = b"noop"; + let args_noop = b""; + assert_eq!( + promise_create( + account_id.len() as u64, + account_id.as_ptr() as u64, + method_name_noop.len() as u64, + method_name_noop.as_ptr() as u64, + args_noop.len() as u64, + args_noop.as_ptr() as u64, + &amount_zero as *const u128 as *const u64 as u64, + gas_per_promise, + ), + 2 + ); + let promise_then_ids = [3, 4]; + for expected_id in promise_then_ids { + assert_eq!( + promise_then( + 2, + account_id.len() as u64, + account_id.as_ptr() as u64, + method_name_noop.len() as u64, + method_name_noop.as_ptr() as u64, + args_noop.len() as u64, + args_noop.as_ptr() as u64, + &amount_zero as *const u128 as *const u64 as u64, + gas_per_promise, + ), + expected_id + ); + } + assert_eq!(promise_and(promise_then_ids.as_ptr() as u64, promise_then_ids.len() as u64), 5); + assert_eq!(promise_batch_create(account_id.len() as u64, account_id.as_ptr() as u64), 6,); + assert_eq!(promise_batch_then(4, account_id.len() as u64, account_id.as_ptr() as u64), 7,); + + // ####################### + // # Promise API actions # + // ####################### + let new_account_id = insert_account_id_prefix("foo.", account_id.clone()).unwrap(); + let batch_promise_idx = 8; + let amount_non_zero = 50_000_000_000_000_000_000_000u128; + let contract_code = from_base64(input_args["contract_code"].as_str().unwrap()); + let method_deployed_contract = input_args["method_name"].as_str().unwrap().as_bytes(); + let args_deployed_contract = from_base64(input_args["method_args"].as_str().unwrap()); + assert_eq!( + promise_batch_create(new_account_id.len() as u64, new_account_id.as_ptr() as u64), + batch_promise_idx, + ); + promise_batch_action_create_account(batch_promise_idx); + promise_batch_action_transfer( + batch_promise_idx, + &amount_non_zero as *const u128 as *const u64 as u64, + ); + promise_batch_action_deploy_contract( + batch_promise_idx, + contract_code.len() as u64, + contract_code.as_ptr() as u64, + ); + promise_batch_action_function_call( + batch_promise_idx, + method_deployed_contract.len() as u64, + method_deployed_contract.as_ptr() as u64, + args_deployed_contract.len() as u64, + args_deployed_contract.as_ptr() as u64, + &amount_zero as *const u128 as *const u64 as u64, + gas_per_promise, + ); + #[cfg(feature = "latest_protocol")] + promise_batch_action_function_call_weight( + batch_promise_idx, + method_deployed_contract.len() as u64, + method_deployed_contract.as_ptr() as u64, + args_deployed_contract.len() as u64, + args_deployed_contract.as_ptr() as u64, + &amount_zero as *const u128 as *const u64 as u64, + 0, + 1, + ); + promise_batch_action_add_key_with_full_access( + batch_promise_idx, + account_public_key.len() as u64, + account_public_key.as_ptr() as u64, + 0u64, + ); + promise_batch_action_delete_key( + batch_promise_idx, + account_public_key.len() as u64, + account_public_key.as_ptr() as u64, + ); + promise_batch_action_add_key_with_function_call( + batch_promise_idx, + account_public_key.len() as u64, + account_public_key.as_ptr() as u64, + 1u64, + &amount_zero as *const u128 as *const u64 as u64, + new_account_id.len() as u64, + new_account_id.as_ptr() as u64, + method_deployed_contract.len() as u64, + method_deployed_contract.as_ptr() as u64, + ); + promise_batch_action_delete_account( + batch_promise_idx, + account_id.len() as u64, + account_id.as_ptr() as u64, + ); + + // Create a new account as `DeleteAccountAction` fails after `StakeAction`. + let new_account_id = insert_account_id_prefix("bar.", account_id.clone()).unwrap(); + let batch_promise_idx = 9; + let amount_stake = 30_000_000_000_000_000_000_000u128; + assert_eq!( + promise_batch_create(new_account_id.len() as u64, new_account_id.as_ptr() as u64), + batch_promise_idx, + ); + promise_batch_action_create_account(batch_promise_idx); + promise_batch_action_transfer( + batch_promise_idx, + &amount_non_zero as *const u128 as *const u64 as u64, + ); + promise_batch_action_stake( + batch_promise_idx, + &amount_stake as *const u128 as *const u64 as u64, + account_public_key.len() as u64, + account_public_key.as_ptr() as u64, + ); + + // ####################### + // # Promise API results # + // ####################### + // Invoking `promise_results_count` and `promise_result` via a callback to + // ensure there is a promise whose result can be accessed. + assert_eq!( + promise_create( + account_id.len() as u64, + account_id.as_ptr() as u64, + method_name_noop.len() as u64, + method_name_noop.as_ptr() as u64, + args_noop.len() as u64, + args_noop.as_ptr() as u64, + &amount_zero as *const u128 as *const u64 as u64, + gas_per_promise, + ), + 10, + ); + let method_name_promise_results = b"sanity_check_promise_results"; + let args_promise_results = b""; + assert_eq!( + promise_then( + 10, + account_id.len() as u64, + account_id.as_ptr() as u64, + method_name_promise_results.len() as u64, + method_name_promise_results.as_ptr() as u64, + args_promise_results.len() as u64, + args_promise_results.as_ptr() as u64, + &amount_zero as *const u128 as *const u64 as u64, + gas_per_promise, + ), + 11, + ); + promise_return(11); + + // ############### + // # Storage API # + // ############### + // For storage funcs, cover both cases of key being unused and being used. + let key = b"hi"; + assert_eq!( + storage_write( + key.len() as u64, + key.as_ptr() as u64, + value.len() as u64, + value.as_ptr() as u64, + 1, + ), + 0, + ); + assert_eq!( + storage_write( + key.len() as u64, + key.as_ptr() as u64, + value.len() as u64, + value.as_ptr() as u64, + 1, + ), + 1, + ); + + let unused_key = b"abcdefg"; + assert_eq!(storage_read(unused_key.len() as u64, unused_key.as_ptr() as u64, 1), 0); + assert_eq!(storage_read(key.len() as u64, key.as_ptr() as u64, 1), 1); + + assert_eq!(storage_has_key(unused_key.len() as u64, unused_key.as_ptr() as u64), 0); + assert_eq!(storage_has_key(key.len() as u64, key.as_ptr() as u64), 1); + + assert_eq!(storage_remove(unused_key.len() as u64, unused_key.as_ptr() as u64, 1), 0); + assert_eq!(storage_remove(key.len() as u64, key.as_ptr() as u64, 1), 1); + + // Note: Deprecated functions storage_iter_* are skipped. + + gas(1); + + // ################# + // # Validator API # + // ################# + let stake = [0u8; size_of::()]; + let validator_id = input_args["validator_id"].as_str().unwrap().as_bytes(); + validator_stake(validator_id.len() as u64, validator_id.as_ptr() as u64, stake.as_ptr() as u64); + validator_total_stake(stake.as_ptr() as u64); + + // ################### + // # Math Extensions # + // ################### + #[cfg(feature = "latest_protocol")] + { + let buffer = [65u8; 10]; + ripemd160(buffer.len() as u64, buffer.as_ptr() as u64, 1); + } + + // ################# + // # alt_bn128 API # + // ################# + #[cfg(feature = "latest_protocol")] + { + let buffer: [u8; 96] = [ + 16, 238, 91, 161, 241, 22, 172, 158, 138, 252, 202, 212, 136, 37, 110, 231, 118, 220, + 8, 45, 14, 153, 125, 217, 227, 87, 238, 238, 31, 138, 226, 8, 238, 185, 12, 155, 93, + 126, 144, 248, 200, 177, 46, 245, 40, 162, 169, 80, 150, 211, 157, 13, 10, 36, 44, 232, + 173, 32, 32, 115, 123, 2, 9, 47, 190, 148, 181, 91, 69, 6, 83, 40, 65, 222, 251, 70, + 81, 73, 60, 142, 130, 217, 176, 20, 69, 75, 40, 167, 41, 180, 244, 5, 142, 215, 135, + 35, + ]; + alt_bn128_g1_multiexp(buffer.len() as u64, buffer.as_ptr() as u64, 1); + let buffer: [u8; 65] = [ + 0, 11, 49, 94, 29, 152, 111, 116, 138, 248, 2, 184, 8, 159, 80, 169, 45, 149, 48, 32, + 49, 37, 6, 133, 105, 171, 194, 120, 44, 195, 17, 180, 35, 137, 154, 4, 192, 211, 244, + 93, 200, 2, 44, 0, 64, 26, 108, 139, 147, 88, 235, 242, 23, 253, 52, 110, 236, 67, 99, + 176, 2, 186, 198, 228, 25, + ]; + alt_bn128_g1_sum(buffer.len() as u64, buffer.as_ptr() as u64, 1); + let buffer: [u8; 192] = [ + 80, 12, 4, 181, 61, 254, 153, 52, 127, 228, 174, 24, 144, 95, 235, 26, 197, 188, 219, + 91, 4, 47, 98, 98, 202, 199, 94, 67, 211, 223, 197, 21, 65, 221, 184, 75, 69, 202, 13, + 56, 6, 233, 217, 146, 159, 141, 116, 208, 81, 224, 146, 124, 150, 114, 218, 196, 192, + 233, 253, 31, 130, 152, 144, 29, 34, 54, 229, 82, 80, 13, 200, 53, 254, 193, 250, 1, + 205, 60, 38, 172, 237, 29, 18, 82, 187, 98, 113, 152, 184, 251, 223, 42, 104, 148, 253, + 25, 79, 39, 165, 18, 195, 165, 215, 155, 168, 251, 250, 2, 215, 214, 193, 172, 187, 84, + 54, 168, 27, 100, 161, 155, 144, 95, 199, 238, 88, 238, 202, 46, 247, 97, 33, 56, 78, + 174, 171, 15, 245, 5, 121, 144, 88, 81, 102, 133, 118, 222, 81, 214, 74, 169, 27, 91, + 27, 23, 80, 55, 43, 97, 101, 24, 168, 29, 75, 136, 229, 2, 55, 77, 60, 200, 227, 210, + 172, 194, 232, 45, 151, 46, 248, 206, 193, 250, 145, 84, 78, 176, 74, 210, 0, 106, 168, + 30, + ]; + alt_bn128_pairing_check(buffer.len() as u64, buffer.as_ptr() as u64); + } +} + +/// Callback for a promise created in `sanity_check`. It calls host functions +/// which use the results of earlier promises. +#[no_mangle] +pub unsafe fn sanity_check_promise_results() { + assert_eq!(promise_results_count(), 1); + promise_result(0, 0); +} + +#[no_mangle] +pub unsafe fn sanity_check_panic() { + panic(); +} + +#[no_mangle] +pub unsafe fn sanity_check_panic_utf8() { + let data = b"xyz"; + panic_utf8(data.len() as u64, data.as_ptr() as u64); +} diff --git a/runtime/unc-test-contracts/test-contract-ts/.gitignore b/runtime/unc-test-contracts/test-contract-ts/.gitignore new file mode 100644 index 000000000..378eac25d --- /dev/null +++ b/runtime/unc-test-contracts/test-contract-ts/.gitignore @@ -0,0 +1 @@ +build diff --git a/runtime/unc-test-contracts/test-contract-ts/README.md b/runtime/unc-test-contracts/test-contract-ts/README.md new file mode 100644 index 000000000..083b7c488 --- /dev/null +++ b/runtime/unc-test-contracts/test-contract-ts/README.md @@ -0,0 +1,31 @@ +A smart contract written in AssemblyScript that can be used to make +sure near runtime is compatible with AssemblyScript smart contracts. + +# Pre-requisites + +Switch to the smart contract directory and point npm to AssemblyScript: +```bash +npm install --save-dev AssemblyScript/assemblyscript +``` + +Then install dependencies with +```bash +npm install +``` + +# Building + +Build smart contract with: +```bash +npm run asbuild:untouched +``` + +And copy the smart contract into `res` directory: +```bash +cp build/untouched.wasm ../res/test_contract_ts.wasm +``` + +Then run the Rust integration test with: +```bash +cargo test --package unc-vm-runner --test test_ts_contract "" -- --nocapture +``` diff --git a/runtime/unc-test-contracts/test-contract-ts/assembly/index.ts b/runtime/unc-test-contracts/test-contract-ts/assembly/index.ts new file mode 100644 index 000000000..7849e5534 --- /dev/null +++ b/runtime/unc-test-contracts/test-contract-ts/assembly/index.ts @@ -0,0 +1,57 @@ +@external("env", "read_register") +declare function read_register(register_id: u64, ptr: u64): void; +@external("env", "register_len") +declare function register_len(register_id: u64): u64; + +@external("env", "storage_write") +declare function storage_write(key_len: u64, key_ptr: u64, value_len: u64, value_ptr: u64, register_id: u64): u64; +@external("env", "storage_read") +declare function storage_read(key_len: u64, key_ptr: u64, register_id: u64): u64; + +@external("env", "input") +declare function input(register_id: u64): void; +@external("env", "value_return") +declare function value_return(value_len: u64, value_ptr: u64): void; + +@external("env", "panic") +declare function panic(): void; + +export function try_storage_write(): void { + input(0); + let len = register_len(0); + if (len == U64.MAX_VALUE) { + panic(); + } + let buffer = new Uint8Array(len as i32); + read_register(0, buffer.buffer as u64); + let input_str = String.UTF8.decode(buffer.buffer); + let input_split = input_str.split(" "); + let key_str = input_split[0]; + let value_str = input_split[1] + + let key = String.UTF8.encode(key_str); + let value = String.UTF8.encode(value_str); + storage_write(key.byteLength, key as u64, value.byteLength, value as u64, 0); +} + +export function try_storage_read(): void { + input(0); + let key_len = register_len(0); + if (key_len == U64.MAX_VALUE) { + panic(); + } + let key = new Uint8Array(key_len as i32); + read_register(0, key.buffer as u64); + + let res = storage_read(key.buffer.byteLength, key.buffer as u64, 1); + if (res == 1) { + let value_len = register_len(1); + let value = new Uint8Array(value_len as i32); + read_register(1, value.buffer as u64); + value_return(value.buffer.byteLength, value.buffer as u64); + } +} + +export function try_panic(): void { + panic(); +} diff --git a/runtime/unc-test-contracts/test-contract-ts/assembly/tsconfig.json b/runtime/unc-test-contracts/test-contract-ts/assembly/tsconfig.json new file mode 100644 index 000000000..c614e5c8e --- /dev/null +++ b/runtime/unc-test-contracts/test-contract-ts/assembly/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../node_modules/assemblyscript/std/assembly.json", + "include": [ + "./**/*.ts" + ] +} \ No newline at end of file diff --git a/runtime/unc-test-contracts/test-contract-ts/package-lock.json b/runtime/unc-test-contracts/test-contract-ts/package-lock.json new file mode 100644 index 000000000..48e341a09 --- /dev/null +++ b/runtime/unc-test-contracts/test-contract-ts/package-lock.json @@ -0,0 +1,3 @@ +{ + "lockfileVersion": 1 +} diff --git a/runtime/unc-test-contracts/test-contract-ts/package.json b/runtime/unc-test-contracts/test-contract-ts/package.json new file mode 100644 index 000000000..34f562f15 --- /dev/null +++ b/runtime/unc-test-contracts/test-contract-ts/package.json @@ -0,0 +1,7 @@ +{ + "scripts": { + "asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --sourceMap --validate --debug", + "asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --sourceMap --validate --optimize", + "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized" + } +} diff --git a/runtime/unc-vm-runner/Cargo.toml b/runtime/unc-vm-runner/Cargo.toml new file mode 100644 index 000000000..788a3f9af --- /dev/null +++ b/runtime/unc-vm-runner/Cargo.toml @@ -0,0 +1,132 @@ +[package] +name = "unc-vm-runner" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "This crate implements the specification of the interface that unc blockchain exposes to the smart contracts." +repository.workspace = true +license.workspace = true +categories = ["wasm"] +publish = true + +[lints] +workspace = true + +[dependencies] +anyhow = { workspace = true, optional = true } +base64.workspace = true +bn.workspace = true +borsh.workspace = true +ed25519-dalek.workspace = true +enum-map.workspace = true +finite-wasm = { workspace = true, features = ["instrument"] } +loupe.workspace = true +memoffset.workspace = true +num-rational.workspace = true +once_cell.workspace = true +parity-wasm.workspace = true +prefix-sum-vec.workspace = true +ripemd.workspace = true +serde_repr.workspace = true +serde_with.workspace = true +serde.workspace = true +sha2.workspace = true +sha3.workspace = true +stdx.workspace = true +strum.workspace = true +thiserror.workspace = true +tracing.workspace = true +wasm-encoder.workspace = true +wasmparser.workspace = true +wasmtime = { workspace = true, optional = true } + +unc-crypto.workspace = true +unc-primitives-core.workspace = true +unc-parameters.workspace = true + +# Old versions of pwasm-utils we need to preserve backwards compatibility under +# old protocol versions. +pwasm-utils_12.workspace = true +parity-wasm_41.workspace = true + +[target.'cfg(target_arch = "x86_64")'.dependencies] +wasmer-runtime = { workspace = true, optional = true } +wasmer-runtime-core = { workspace = true, optional = true } +wasmer-compiler = { workspace = true, optional = true } +wasmer-compiler-singlepass = { workspace = true, optional = true } +wasmer-engine = { workspace = true, optional = true } +wasmer-engine-universal = { workspace = true, optional = true } +wasmer-types = { workspace = true, optional = true } +wasmer-vm = { workspace = true, optional = true } +unc-vm-compiler = { workspace = true, optional = true } +unc-vm-compiler-singlepass = { workspace = true, optional = true } +unc-vm-engine = { workspace = true, optional = true } +unc-vm-types = { workspace = true, optional = true } +unc-vm-vm = { workspace = true, optional = true } + +[dev-dependencies] +arbitrary.workspace = true +assert_matches.workspace = true +bolero.workspace = true +expect-test.workspace = true +hex.workspace = true +unc-test-contracts.workspace = true +rand.workspace = true +serde_json = { workspace = true, features = ["preserve_order"] } +wasm-smith.workspace = true +wasmprinter.workspace = true +wat.workspace = true + +[features] +nightly_protocol = [ + "unc-parameters/nightly_protocol", + "unc-primitives-core/nightly_protocol", +] +# all vms enabled for now +default = [ + "wasmer0_vm", + "wasmtime_vm", + "wasmer2_vm", + "unc_vm", +] +wasmer0_vm = [ "wasmer-runtime", "wasmer-runtime-core" ] +wasmtime_vm = [ "wasmtime", "anyhow"] +wasmer2_vm = [ + "wasmer-compiler", + "wasmer-compiler-singlepass", + "wasmer-engine", + "wasmer-engine-universal", + "wasmer-types", + "wasmer-vm" +] +unc_vm = [ + "unc-vm-compiler", + "unc-vm-compiler-singlepass", + "unc-vm-engine", + "unc-vm-types", + "unc-vm-vm" +] + +no_cpu_compatibility_checks = [] + +no_cache = [] + +protocol_feature_fix_contract_loading_cost = [ + "unc-primitives-core/protocol_feature_fix_contract_loading_cost", +] + +nightly = [ + "nightly_protocol", + "protocol_feature_fix_contract_loading_cost", + "unc-parameters/nightly", + "unc-primitives-core/nightly", +] +sandbox = [] +io_trace = [] + +# Use this feature to enable counting of fees and costs applied. +costs_counting = [] + +[package.metadata.cargo-udeps.ignore] +# `no_cache` feature leads to an unused `cached` crate +normal = ["cached"] diff --git a/runtime/unc-vm-runner/FAQ.md b/runtime/unc-vm-runner/FAQ.md new file mode 100644 index 000000000..2642a4664 --- /dev/null +++ b/runtime/unc-vm-runner/FAQ.md @@ -0,0 +1,306 @@ +# FAQ for using virtual machines in the blockchain. + +## Preface + +This document is formed as an FAQ for the new blockchain architect and implementer, +documenting possible contract runtime design decisions and the logic behind them. +In some cases it’s really an answer, and in some others - musings on possible ways +of thinking about the problem. + +## Higher level architectural questions + +### Should I use a special purpose virtual machine best suited for my blockchain, or rely on the general purpose VM? + +First blockchains, such as Bitcoin and Ethereum 1.0, were in favour of the +special purpose VMs, while more recent blockchains, partially due to broader +Wasm implementations availability tend to use general purpose VM, +providing blockchain APIs as external/host functions. + +Advantage of general purpose VM is mostly due to the broader developers community +behind such VMs, and wider availability of the development tools, such as language +compilers, debuggers, static analyzers, IDEs, etc. + +### How do I make blockchain implementation economically sustainable? + +Due to the decentralized nature of the blockchain, users shall be motivated +to lend their computational resources to others. The widely accepted approach here is +to rely on gas, the financial equivalent of computation cost, +whenever computation is performed. Gas can be bought using blockchain tokens +and is spent per used computational resources. Unfortunately, it is not possible +to estimate the amount of the computational cost that the contract can incur without +executing it, due to the halting problem. This means that the user must have an +upper bound estimate on how much gas they are willing to spend that they would +declare in the transaction. This makes performing general computations on the +blockchain harder to reason about, as developers must estimate maximum computational +requirements of the contract before its execution. + +### What gas is supposed to represent? Complexity of computations performed, hardware resources used, power consumed, something else? + +Most practical blockchains make gas represent the number of the raw VM instructions +executed for managed code execution, and some synthetic and usually precomputed cost +for external native functions executed by the blockchain runtime. For example, the +number of actual machine instructions could be used as gas metrics for host functions. +Some blockchains however, approximate the gas as the elapsed time. + +### What does the gas price reflect? How should I price the gas in my system? Pure price of hardware for validators or some economic meaning? + +Gas price can either represent the cost of hardware maintenance for validators, +probably with some multiplier to make validation economically attractive, +or be used as a mechanism for improving blockchain functioning, i.e. load +balancing and attracting new users (i.e. validators could agree to work below +margin for some time to attract developers and users). + +### How will my developers know the amount of gas to attach to their contract? + +Prediction of the gas amount to be attached to the complex contract may become a +non-trivial problem, if execution has unbound loops. A proper gas metering better +be assisted by the developers tools. + +### Can gas metering have theoretical limitations wrt impact on execution performance? + +Gas metering, being computation itself, has its cost. There are few options, such as +hardware assisted gas metering, or fully precomputed gas cost if the contract execution +path is fully predictable, but in general case it’s rather hard to make it free for +the system. + +### Why is gas metering not part of the Wasm standard? What shall be done with external/host functions then? + +It probably shall be, and we as the blockchain community shall push forward adding gas +metering to the Wasm standard. Gas costs for the external functions likely to be part +of the Wasm runtime embedding mechanism, actual prices to be defined empirically, +unless there's a better mechanism provided by the OS/hardware. + +### Shall gas be one scalar quantity, or vector (i.e. for CPU, memory, disk, network resources)? + +There’s no definitive answer to this question yet, however, complex gas metering/pricing +strategies would make development experience even more complex, as one has to estimate +not one quantity, but the whole vector, and the token cost of different vector +components need to be decided as well. + +### How consumed gas shall depend on the consumed resources? + +The most straightforward approach is the linear dependency, i.e. program with twice +as many executed instructions shall consume twice as much gas, however nonlinear +dependencies, such as quadratic or even exponential, may be of interest in scenarios, +when too extensive computations in the contract may affect performance or stability +of the blockchain. + +## Practical aspects of the execution engines + +### Shall I use an interpreter, non-optimizing JIT, optimizing JIT, non-optimizing AOT, optimizing AOT compiler in my blockchain? +The answer depends on the required features, and the following table will try to help. +Every solution is subjectively measured on a bad/ok/good/great scale. + + +| **Metric** | **Interpreter**|**Non-opt JIT**|**Opt JIT**|**Non-opt AOT**|**Opt AOT**| +| ----------------- |:--------------:| -------------:|----------:|--------------:|----------:| +| correctness | great | good | ok | good | ok | +| performance | bad | ok | good | ok | great | +| security | great | ok | bad | ok | bad | +| additional per-contract storage | great | great | great | bad | ok | +| additional per-contract computations | great | ok | bad | good | good | +| Gas counting in VM | great | ok | mostly ok | ok | mostly ok | + +With an optimizing compiler, it’s possible to have decent performance, at the cost of spending +more time in the runtime in case of JIT, or more storage and compilation time on execution +of the contract first time in case of AOT compilers. Another potential limitation of optimizing +compilers is the possibility of JIT bombs and other security vulnerabilities coming from running +such complex pieces of software as optimizing compilers on the untrusted input. However, +with the careful selection of optimization passes and VM implementation it shall be possible +to limit the security impact while keeping decent performance. + +### How do I do gas metering in presence of the compiler? + +In case of JITs execution of the compiler can be either ignored, metered based on input size with +fixed coefficient, or metered precisely. In the latter case, JIT bombing is less of the problem, +as JIT bombs presumably will be prohibitively expensive. However, metering the compiler requires +cooperation from its authors, or ability to run the compiler itself in a metered environment +(i.e. if JIT is executed in VM itself). In case of AOT compilers, compilation is once per contract, +and technically compilation cost here could be included into the cost of contract deployment. + +### What are the dangers of bugs in compilers/VMs? + +Unlike traditional software development, bugs and UB in the contract runtime could be pretty +devastating for the network coherence, as they may trigger inconsistency between nodes, and +lead to undesired blockchain forks. Thus, whenever there’s a risk of behavioral discrepancy +between nodes executing contract code - it shall be mitigated. No visible state shall rely +upon timing taken for the certain operation, compilation or execution alike, and if an +execution correctness problem exists - it must be the same on all nodes. +Thus compiler crashes are always preferred to potential hiding of undefined behavior. + +Another even more dangerous scenario of a compiler bug is when the bug is consistent for +all nodes, but it leads to incorrect or vulnerable contract execution. In that case the bug +can go unnoticed for many months until it manifests or gets exploited by someone. + +### What is the cold cache attack and how do I avoid it? + +Cold cache attack is a generic attack not specific to the contract runtime. +Consider the following scenario. Suppose some blockchain has the full governance mechanism that +allows participants to decide on the configuration parameters of the system. Suppose someone +discovered that the node would consume less resources if they add a caching layer somewhere, e.g. +if they decide to cache compilation of the contracts. Suppose everyone incorporates this change +into their nodes, then through the governance mechanism they lower the fee of the cached operation, +to represent the average cost of executing it. But average cost does not represent the worst case +scenario. So someone might find a way to execute computation on the system in such a way that they +would miss the cache. In case of the compiled contract this could be executing a “cold” contract. +As a result they will incur high computational cost to the system by paying for an average +scenario. This could lead to the node slowdown, dropout of the participants, etc. + +There is currently no clear way how to avoid cold cache attack for the case when we cache contract +compilation. + +### What contracts can be executed without the actual blockchain? How to automatically test contracts? + +Contracts are state machines, transferring the input state to the output state, so it’s not +required to have the traditional blockchain notions, such as network consensus and global +transaction history to be present to just execute the contract on known state. Thus, running +certain contracts, such as tests, in predefined blockchain state is an extremely desirable feature +of the contract runtime. It is also useful to dry-run contracts inside the devtools, e.g. to +estimate the gas usage. + +### Can Wasm clients be used for gas estimation? + +Up to a certain extent, the pure computations part (such as compiler algorithms) is easy, when host +functions (external to Wasm functions provided by the blockchain) are involved it becomes harder. + +## Development tools. + +### What programming languages shall I support in my blockchain? + +This is a rather broad question, so some aspects of that will be covered in next questions. +The most basic dichotomy is to support the general purpose language(s) such as Rust, AssemblyScript +vs. the specialized contract language, such as Solidity, Move, etc. Supporting custom language +usually induces cost of the toolchain maintenance, and it not only includes the compiler, +but also editor support, static analyzers, IDE and so on. Another important problem is the higher +barriers for developers, since they should learn a new language, potentially with new paradigms, +and a few of online resources to learn. Using general purpose language seems preferable, +if your blockchain aims at providing rich smart contract functionality, especially if third party +libraries are expected to be used in the contract. + +### What to do with the garbage collection in general purpose programming languages? + +As one could see from the previous question, high level languages may require GC, +and the contract runtime may not have such a feature available. There are few options: + + * Not support such a languages + * Support such languages but not implement object reclamation and GC as many contracts are short + lived it could be a viable option + * Implement automated memory management as part of the language runtime and ship it with every + program or once per language runtime version + * Use runtime with existing GC, such as JVM or JS VM + * Move forward with the [WebAssembly GC proposal] + +[WebAssembly GC proposal]: https://github.com/WebAssembly/gc/blob/master/proposals/gc/Overview.md + +### Shall I support pure functional language, such as Haskell, in contracts? + +It depends on the target audience of your blockchain. Implementation wise, runtime for the +functional programming languages is not that different from runtime for an object oriented +language, and typically needs the GC. Thus, see the previous question. + +### Is it important for the smart contract to be formally verifiable? + +As with any other software, formal verification on smart contracts checks a certain behavioral +aspects of the program, and as such, could be a useful tool for finding bugs and potential +misbehavior of the program. However, it is not a silver bullet, and does not guarantee that the +smart contract is “correct”. Formal verification could be used as one of the static analysis tools +helping developers to write the better code. + +### Is it important for the smart contract language to be Turing-complete? + +There is no definitive answer to that question yet, however, for many practical aspects Turing +incompleteness means lack of the unbound loops/recursion, which limits the language expressive +and computational power. Many practical algorithms, such as Dijkstra’s algorithm or even simple +BFS/DFS are expressed in a form that is hard to formally prove to be bound, and so hard to +express in a Turing incomplete language. However, in some cases, Turing-incomplete contracts +could be useful as they are much easier statically analyzed and formally verified. + +### Should my contracts use DSL or metaprogramming? + +Both a Domain Specific Language (DSL) or a certain metaprogramming technique, such as the +template generator, on top of the general purpose language (such as Rust) seems to be a +good compromise between somewhat excessive expressive power of the general purpose language +and ability to use tooling (compilers, debuggers) for such a language. Exact choice of the +technology mostly depends on the underlying general purpose language. In the case of Rust, +DSL approach could be implemented using standard Rust AST macros. + +### Should my embedded DSL (eDSL) be blackbox or glassbox? + +Whether the eDSL shall be blackbox and not allow underlying language primitives invocation, or be +glassbox and allow the full power of the underlying general purpose language is an important design +decision. Blackbox approach produces less powerful, but more analyzable and verifiable language, +while glassbox is mostly a convenience to simplify coding, not really controlling the expressive +power of the language. See [this +question](#is-it-important-for-the-smart-contract-language-to-be-turing-complete) for further +discussion. + +### Should we make it impossible to write unsafe contracts? + +Dichotomy here is either taking a paternalistic approach for the users, and trying to constraint +them as much as possible through eDSL to the point where we minimize the chance of them making +some common contract-specific mistake. Or we take a liberal approach and consider typical +contract mistakes to be under full users’ responsibility. Most of the modern blockchains +follow the latter approach, sometimes augmented with the static analysis tools to help users +avoid certain classes of mistakes. + +### Who is responsible for the safety of contract execution in the blockchain? + +The question of responsibility of what’s going on in a decentralized distributed system is a very +complex topic. From contract runtime point of view, it can only provide an execution environment +which behaves according to the specification, and allows chain to proceed with any contract given +as input, by either consistently executing it and modifying the ledger per contract’s execution +result or refusing execution with a sensible error message and making no changes in the ledger. +Correctness and responsibility beyond that shall be usually decided outside of the contract runtime. + +### Who is responsible for the safety of the assets programming? + +Blockchain runtime has an option to provide primitive for safe asset-like manipulation, however +they must be carefully designed to avoid limiting functionality, or repeating generic data +manipulation operations available in general purpose programming language by default. + +### Should all contracts be optimal in terms of performance? + +The most important metric of performance in the blockchain is the rate of blocks/transactions +processed by the chain as the whole, and contract runtime performance is a part of the equation, +responsible for the raw execution speed. So contract optimizations are critical only if it makes +the whole chain stall, and if performance is dominated by the other components, optimizations in +contract runtimes may be ignored. When discussing the compiled contract size, huge (>1MiB) +contracts are generally considered harmful. Shall we optimize 100KiB contracts to be as small as +possible, at the cost of worse debuggability and less applicability of the general purpose +libraries is not as clear. We generally tend to consider such optimizations worsening development +experience as excessive. Also, there are certain best practices, such as avoiding JSON and using +more efficient binary serialization for transferring parameters to contracts, which should be +communicated to the blockchain developers. + +### Real financial world is asynchronous. Should DeFi (decentralized finance) also be? + +While a pretty broad question, when projected on smart contract context, it means few rather simple +questions. + + * Shall cross-contract calls be represented as: + * sync operations + * async operations + * continuations + * Shall some/all host functions be asynchronous? + +Answers to those questions depend on the blockchain architecture, however general purpose +contract runtime likely shall support both modes of operation. + + +### How to think about the computational model for smart contracts, especially if they do I/O ? + +The basic dichotomy here is the following: shall you expose the ability of performing callbacks +driven by the external operation in the same execution round (i.e. not recorded by the blockchain), +or all the operations performed by the smart contract must be time-bound synchronous, and all async +operations are represented as subsequent invocations of the same contract at different entry +points/states. Second approach seems to be better suitable for the practical applications, as +otherwise the contract must cope with the changed state of the blockchain during its execution, +which may be not straightforward, and the contract runtime can no longer rely on predictable/bound +execution time of the contract. + +### Should there be a wasi-blockchain spec like for instance the upcoming wasi-crypto spec? + +WASI per se mostly provides the interface to the system operations, like native or FFI calls, +while metering naturally accounts for resources consumed both inside and outside of the VM +during arbitrary contract execution. However, API to control metering could be handled as +a WASI operation. diff --git a/runtime/unc-vm-runner/README.md b/runtime/unc-vm-runner/README.md new file mode 100644 index 000000000..46be5ec0d --- /dev/null +++ b/runtime/unc-vm-runner/README.md @@ -0,0 +1,88 @@ +# unc-vm-runner + +An engine that runs smart contracts compiled to Wasm. +This is the main crate of the "contract runtime" part of framework. + +"Running smart contracts" is: + +- Wasm instrumentation for gas metering and various safety checks (`prepare.rs`). +- Compiling Wasm to a particular VM representation (`cache.rs`). +- Exposing blockchain-specific functionality to Wasm code. That is, defining a corresponding host + function for each function in `unc-vm-logic` (`imports.rs`). +- Actual code execution (`wasmer_runner.rs`). + +The particular runtime used for Wasm execution is an implementation detail. At the moment we support +Wasmer 0.x, Wasmer 2.0 and Wasmtime, with Wasmer 2.0 being default. + +The primary client of Wasm execution services is the blockchain proper. The second client is the +contract sdk tooling. vm-runner provides additional API for contract developers to, for example, +get a gas costs breakdown. + +See the [FAQ][./faq.md] document for high-level design constraints discussion. + +## Entry Point + +The entry point is the `runner::run` function. + +## Testing + +There are a bunch of unit-tests in this crate. You can run them with + +```console +$ cargo t -p unc-vm-runner --features wasmer0_vm,wasmer2_vm,wasmtime_vm,unc_vm +``` + +The tests use either a short wasm snippet specified inline, or a couple of +larger test contracts from the `unc-test-contracts` crate. + +We also have a fuzzing setup: + +```console +$ cd runtime/unc-vm-runner && RUSTC_BOOTSTRAP=1 cargo fuzz run runner +``` + +## Profiling + +`tracing` crate is used to collect Rust code profile data via manual instrumentation. +If you want to know how long a particular function executes, use the following pattern: + +```ignore +fn compute_thing() { + let _span = tracing::debug_span!(target: "vm", "compute_thing").entered(); + for i in 0..99 { + do_work() + } +} +``` + +This will record when the `_span` object is created and dropped, including the time diff between +the two events. + +To get human-readable output out of these events, you can use the built-in tracing subscriber: + +```ignore +tracing_subscriber::fmt::Subscriber::builder() + .with_max_level(tracing::level_filters::LevelFilter::DEBUG) + .with_span_events(tracing_subscriber::fmt::format::FmtSpan::CLOSE) + .init(); + +code_to_profile_here(); +``` + +Alternatively, there's an alternative hierarchical profiler + +```ignore +tracing_span_tree::span_tree().enable(); + +code_to_profile_here(); +``` + +The result would look like this: + +```text + 112.33ms deserialize_wasmer + 2.64ms run_wasmer/instantiate + 96.34µs run_wasmer/call + 123.15ms run_wasmer + 123.17ms run_vm +``` diff --git a/runtime/unc-vm-runner/RUNTIMES.md b/runtime/unc-vm-runner/RUNTIMES.md new file mode 100644 index 000000000..8b6e2ec05 --- /dev/null +++ b/runtime/unc-vm-runner/RUNTIMES.md @@ -0,0 +1,218 @@ +This document describes the considerations that go into picking a Wasm runtime as well as the +conclusions we made after evaluating a number of VM backends and frontends. + +# Criteria + +A number criteria have been used to evaluate a Wasm runtime for use in the implementation of the +NEAR protocol. Some of the criteria are already listed in the [FAQ] document and this document +gives a more thorough look. Listed roughly in the order of importance: + +* security – how well does the runtime deal with untrusted input; +* correctness – does the runtime do what it claims to do; +* reliability – how much confidence is there in the implementation of runtime; +* platform support – does the runtime support targets that we want to target; +* performance – how quickly can the runtime execute the requested operations; + +[FAQ]: FAQ.md#practical-aspects-of-the-execution-engines + +# Requirements + +Within each criteria we have specific requirements relevant to the NEAR protocol. + +## Security + +NEAR protocol validators execute arbitrary Wasm code submitted by the participants of the network, +much like e.g. web browsers execute untrusted JavaScript code. It is critical that the runtime +executes Wasm code in its own isolated, sandboxed environment, satisfying at least the following +requirements: + +* a contract must not be presented with an opportunity to access any resources outside of those + intentionally exposed by the validator implementation; +* contracts must be isolated from each other. Never should there be a situation where it is + possible for a contract to recover and extradite state of a different instance of any contract. + +A bug in the runtime is most likely to take a form of some sort of memory management issue, where +the memory is reused by the contract without zeroing it, or where the memory accesses aren't +validated correctly and allow access outside of the memory regions allocated for the contract. + +## Correctness + +A VM implementation must implement the Wasm specification precisely in order for it to be +considered correct. Typically the Wasm code will be generated as an output by compiling a program +implemented in a higher level programming language. Often the compiler will optimize the produced +Wasm code with the understanding that Wasm instructions behave as specified in the specification. +Were a VM implementation to deviate from specification in some way, there's a non-negligible risk +that a contract would invoke behaviours not intended by the contract authors (for example allow +unauthorized users to transfer tokens.) + +In addition to this, the NEAR protocol adds a requirement that all executions of a Wasm program +must be deterministic. In other words, it must not be possible to observe different execution +results based on the environment within which the VM runs. + +The Wasm specification claims fast, safe and portable semantics to be one of its design goals. Even +though Wasm specification does go to great lengths to make it possible to enable deterministic and +reproducible execution, it is not one of the foundational goals. Indeed, there are some parts of +the specification that allow for non-deterministic, non-reproducible execution. + +A particularly troublesome area of the specification in this regard relates to the floating point +support. The specification permits floating point operations to return a `NaN` result which the +specification also permits to have multiple different bit representations and goes out of its way +to specify the sign of the `NaN` result to be non-deterministic. The determinism and +reproducibility requirements of the NEAR protocol make it necessary for the runtime to ensure NaN +signs and payloads are always deterministic. + +Another detail to keep an eye out is whether the floating point results are being rounded +correctly. It is well known that the x87 co-processor does not necessarily round the results +correctly, so a special attention to how a runtime implements the floating point operations on the +32-bit x86 targets is warranted. + +## Reliability + +The required and desirable properties of a runtime implementation must remain true while the +changes are made to it. Compilers and, by extension, runtimes are complex systems and a poor +architecture may result in even simple changes introducing correctness or security bugs, which +aren't necessarily easily discoverable. + +A reliable code base in this sense should be architected in such a way that the algorithms they +implement and the ways they execute Wasm code remain correct by construction and are easy to +verify. + +Size of the test suite, architecture of the code base, and the number of developers/users are all +reasonably correlated to the reliability one can expect from an implementation of a runtime. + +## Platform support + +The NEAR validator implementation currently targets `x86_64-linux`, both as an execution +environment as well as to establish the cost model. For those reasons stellar support for this +target is a hard requirement. + +However, the contract developers may not necessarily have a `x86_64` machine to develop with, or +they might be using a device running a different operating system. With the recent surge in +popularity of devices based on the Arm CPU architecture chips and generally high popularity of the +macOS and Windows operating systems it would be a huge boon to their development experience if the +runtime used also supported these other targets. + +## Performance + +There are a couple different ways we can evaluate the performance of the runtime. How quickly can +the runtime execute the instructions (throughput), execute the first instruction (latency), prepare +the initial code for execution (compilation) and how efficient are miscellaneous operations such as +serialization and deserialization of the module. + +In order to realistically evaluate the performance of a runtime it is important to understand how +the NEAR validator utilizes the runtime to implement its functionality. Somewhat simplified view +is: + +1. A module containing Wasm code in binary format is parsed and instrumented; +2. Wasm code produced after instrumentation is supplied to the runtime for preparation + (compilation); +3. The prepared module is serialized and written to a long term storage (database); +4. When the contract function call occurs, the serialized module is deserialized, loaded into + memory and the execution of the desired function begins (latency); +5. The contract code is executed (throughput) and the results are returned to the validator. + +It is important to remember that the NEAR protocol charges fees for an operation before the +operation is executed. For that reason, predictability of worst case execution time often matters +more than the execution time in the typical case. It is important that we are able to deduce ahead +of time what cost to assign to a given operation. Inability to do so can lead to significant +undercharging and break the properties underlying the protocol. + +On a scale of trade-offs, performance is probably one of the less important metrics. Slower +execution of a contract code doesn't make NEAR protocol unsound as an idea and only serves to +impose stricter limits on what can be achieved on the network. + +### Compilation + +The NEAR protocol charges gas fees to deploy a contract. One part of a deployment involves +preparing the contract Wasm code for execution. This may involve instrumentation, compilation, +serialization of the machine code and other similar operations. + +An estimate of the deployment cost will typically involve a function which uses the size of the +input Wasm code as its primary input. Such a function can only exist if we have a good knowledge of +the runtime’s time complexity properties are known. For our purposes a linear or `O(n log n)` +relationship between the input size and execution time is the highest we can accept. + +### Latency + +Whenever a function within a contract is invoked, the serialized form of the contract will be +instantiated: loaded into memory and prepared for execution (though processes such as e.g. +linking). Only then it is possible to start executing the contract code. + +When executing a tiny function part of a larger contract these operations will dominate and +contribute greatly to the observed latency of the contract execution. These overheads contribute to +the fees paid by anybody using the protocol, making any unnecessary overhead a potential roadblock +in NEAR protocol's adoption. + +### Throughput + +The faster we can execute the contract code, the more elaborate are the ideas that developers can +express via contracts running on the NEAR protocol. The cheaper it is to execute contracts, the +more approachable and usable the protocol is, so runtime performance is a direct consequence of the +usability of the whole system as a whole. + +One of the more costly operations that occurs in a typical contract quite often is accounting for +gas fees. It is quite important that the runtime gives sufficient flexibility to the NEAR validator +to implement such operations efficiently. + +# Evaluation + +We have evaluated a number of different implementations over time. Currently Wasmer with the +universal engine and the single-pass backend is the default. + +## `wasmer-singlepass` codegen + +The `wasmer-singlepass` code generator is an implementation of a straightforward, direct +translation from Wasm statements to machine code. This approach is linear over the input size by +construction satisfying the requirements for preparation performance. + +A single-pass codegen implementation in general incurs a trade-off in runtime performance. +WebAssembly is modelling a stack machine and an obviously correct implementation of such a machine +would be to directly translate all the stack operations to native code as well. Targets running +this code, on the other hand, are most likely an implementation of register machine where stack +operations are not necessarily all that efficient. + +In order to improve the quality of the generated machine code, `wasmer-singlepass` maintains some +state between the individual Wasm instructions. This state allows `wasmer-singlepass` to avoid some +stack operations, pick more efficient instructions and sometimes avoid some operations such as NaN +canonicalization altogether. + +Presence of this state comes at a cost of testability of implementation. The codegen for a given +instruction can depend on prior Wasm instructions. Because of this there is a combinatorial +explosion of instruction sequences that need to be considered and verified before confidence in the +results can be established. + +Relatedly, at times it may also be difficult to validate changes made to the `wasmer-singlepass` +compiler. The global state its implementation relies on is, at times, a source of +[spooky action at a distance][spooky] problems – changing code generation of a specific instruction +can affect the implementation of another unrelated instruction as well. + +The `wasmer-singlepass` backend currently only supports `x86_64` with `AVX` on Linux and, since +very recently, Windows. + +[spooky]: https://en.wikipedia.org/wiki/Action_at_a_distance + +## Cranelift codegen + +Cranelift is a code generator written in Rust. It aims to be a fast code generator that also +produces machine code of a reasonably high quality. Originally it was designed for use in Firefox +for compiling arbitrary Wasm binaries presented to the browser by websites. Since then Cranelift +can be used for code generation in both Wasmer and Wasmtime. + +Cranelift is a multi-stage compiler. It consumes an intermediate representation called CLIF which +is later lowered to the VCode IR on which operations such as instruction selection and register +allocation are applied to produce the machine code. Refer to [this blog post][clift-cg-primer] for +an in-depth explanation of the Cranelift's architecture. + +[clift-cg-primer]: https://blog.benj.me/2021/02/17/cranelift-codegen-primer/ + +A Wasm codegen implementation in a browser shares many of the requirements with those of the NEAR +protocol. Cranelift gets high scores for its Correctness, Reliability and Security properties. +It supports targeting the Arm, s390x and x86_64 architectures as well as a variety of +operating systems, netting it a high score on the platform support metric as well. + +Upon a brief investigation in December of 2021 generating machine code with Cranelift should +broadly be `O(n)` over input size with the exception of register allocation, which due to their +soon-to-be replaced regalloc algorithm is currently `O(n²)`. + +One detail where Cranelift may fall short is in the ability to produce super-optimized machine code +sequences for hot operations such as gas counting. diff --git a/runtime/unc-vm-runner/fuzz/.gitignore b/runtime/unc-vm-runner/fuzz/.gitignore new file mode 100644 index 000000000..b400c2782 --- /dev/null +++ b/runtime/unc-vm-runner/fuzz/.gitignore @@ -0,0 +1,2 @@ +corpus +artifacts diff --git a/runtime/unc-vm-runner/fuzz/Cargo.toml b/runtime/unc-vm-runner/fuzz/Cargo.toml new file mode 100644 index 000000000..e2fc56fd9 --- /dev/null +++ b/runtime/unc-vm-runner/fuzz/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "unc-vm-runner-fuzz" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[package.metadata] +cargo-fuzz = true + +[lints] +workspace = true + +[dependencies] +arbitrary.workspace = true +libfuzzer-sys.workspace = true +wasm-smith.workspace = true +wasmprinter.workspace = true + +unc-parameters.workspace = true +unc-primitives.workspace = true +unc-test-contracts.workspace = true +unc-vm-runner.workspace = true + +[[bin]] +name = "runner" +path = "fuzz_targets/runner.rs" +test = false +doc = false + +[[bin]] +name = "diffrunner" +path = "fuzz_targets/diffrunner.rs" +test = false +doc = false diff --git a/runtime/unc-vm-runner/fuzz/fuzz_targets/diffrunner.rs b/runtime/unc-vm-runner/fuzz/fuzz_targets/diffrunner.rs new file mode 100644 index 000000000..e9e94e109 --- /dev/null +++ b/runtime/unc-vm-runner/fuzz/fuzz_targets/diffrunner.rs @@ -0,0 +1,57 @@ +#![no_main] + +use unc_parameters::vm::VMKind; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_vm_runner::internal::VMKindExt; +use unc_vm_runner::logic::errors::FunctionCallError; +use unc_vm_runner::logic::mocks::mock_external::MockedExternal; +use unc_vm_runner::logic::VMOutcome; +use unc_vm_runner::ContractCode; +use unc_vm_runner_fuzz::{create_context, find_entry_point, ArbitraryModule}; + +libfuzzer_sys::fuzz_target!(|module: ArbitraryModule| { + let code = ContractCode::new(module.0.module.to_bytes(), None); + let unc_vm = run_fuzz(&code, VMKind::NearVm); + let wasmtime = run_fuzz(&code, VMKind::Wasmtime); + assert_eq!(unc_vm, wasmtime); +}); + +fn run_fuzz(code: &ContractCode, vm_kind: VMKind) -> VMOutcome { + let mut fake_external = MockedExternal::new(); + let mut context = create_context(vec![]); + context.prepaid_gas = 10u64.pow(14); + let config_store = RuntimeConfigStore::new(None); + let config = config_store.get_config(PROTOCOL_VERSION); + let fees = &config.fees; + let mut wasm_config = config.wasm_config.clone(); + wasm_config.limit_config.contract_prepare_version = + unc_vm_runner::logic::ContractPrepareVersion::V2; + + let promise_results = vec![]; + + let method_name = find_entry_point(code).unwrap_or_else(|| "main".to_string()); + let res = vm_kind.runtime(wasm_config).unwrap().run( + code, + &method_name, + &mut fake_external, + context, + fees, + &promise_results, + None, + ); + + // Remove the VMError message details as they can differ between runtimes + // TODO: maybe there's actually things we could check for equality here too? + match res { + Ok(mut outcome) => { + if outcome.aborted.is_some() { + outcome.logs = vec!["[censored]".to_owned()]; + outcome.aborted = + Some(FunctionCallError::LinkError { msg: "[censored]".to_owned() }); + } + outcome + } + Err(err) => panic!("fatal error: {err:?}"), + } +} diff --git a/runtime/unc-vm-runner/fuzz/fuzz_targets/runner.rs b/runtime/unc-vm-runner/fuzz/fuzz_targets/runner.rs new file mode 100644 index 000000000..84b9ec4df --- /dev/null +++ b/runtime/unc-vm-runner/fuzz/fuzz_targets/runner.rs @@ -0,0 +1,35 @@ +#![no_main] + +use unc_parameters::{RuntimeConfig, RuntimeConfigStore}; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_vm_runner::internal::VMKindExt; +use unc_vm_runner::logic::mocks::mock_external::MockedExternal; +use unc_vm_runner::logic::VMOutcome; +use unc_vm_runner::ContractCode; +use unc_vm_runner_fuzz::{create_context, find_entry_point, ArbitraryModule}; +use std::sync::Arc; + +libfuzzer_sys::fuzz_target!(|module: ArbitraryModule| { + let code = ContractCode::new(module.0.module.to_bytes(), None); + let config_store = RuntimeConfigStore::new(None); + let config = config_store.get_config(PROTOCOL_VERSION); + let _result = run_fuzz(&code, Arc::clone(config)); +}); + +fn run_fuzz(code: &ContractCode, config: Arc) -> VMOutcome { + let mut fake_external = MockedExternal::new(); + let mut context = create_context(vec![]); + context.prepaid_gas = 10u64.pow(14); + let mut wasm_config = config.wasm_config.clone(); + wasm_config.limit_config.wasmer2_stack_limit = i32::MAX; // If we can crash wasmer2 even without the secondary stack limit it's still good to know + let vm_kind = config.wasm_config.vm_kind; + let fees = &config.fees; + let promise_results = vec![]; + + let method_name = find_entry_point(code).unwrap_or_else(|| "main".to_string()); + vm_kind + .runtime(wasm_config) + .unwrap() + .run(code, &method_name, &mut fake_external, context, fees, &promise_results, None) + .unwrap_or_else(|err| panic!("fatal error: {err:?}")) +} diff --git a/runtime/unc-vm-runner/fuzz/src/lib.rs b/runtime/unc-vm-runner/fuzz/src/lib.rs new file mode 100644 index 000000000..2d3e24312 --- /dev/null +++ b/runtime/unc-vm-runner/fuzz/src/lib.rs @@ -0,0 +1,91 @@ +use core::fmt; +use unc_vm_runner::internal::wasmparser::{Export, ExternalKind, Parser, Payload, TypeDef}; +use unc_vm_runner::logic::VMContext; +use unc_vm_runner::ContractCode; + +/// Finds a no-parameter exported function, something like `(func (export "entry-point"))`. +pub fn find_entry_point(contract: &ContractCode) -> Option { + let mut tys = Vec::new(); + let mut fns = Vec::new(); + for payload in Parser::default().parse_all(contract.code()) { + match payload { + Ok(Payload::FunctionSection(rdr)) => fns.extend(rdr), + Ok(Payload::TypeSection(rdr)) => tys.extend(rdr), + Ok(Payload::ExportSection(rdr)) => { + for export in rdr { + if let Ok(Export { field, kind: ExternalKind::Function, index }) = export { + if let Some(&Ok(ty_index)) = fns.get(index as usize) { + if let Some(Ok(TypeDef::Func(func_type))) = tys.get(ty_index as usize) { + if func_type.params.is_empty() && func_type.returns.is_empty() { + return Some(field.to_string()); + } + } + } + } + } + } + _ => (), + } + } + None +} + +pub fn create_context(input: Vec) -> VMContext { + VMContext { + current_account_id: "alice".parse().unwrap(), + signer_account_id: "bob".parse().unwrap(), + signer_account_pk: vec![0, 1, 2, 3, 4], + predecessor_account_id: "carol".parse().unwrap(), + input, + block_height: 10, + block_timestamp: 42, + epoch_height: 1, + account_balance: 2u128, + account_locked_balance: 0, + storage_usage: 12, + attached_deposit: 2u128, + prepaid_gas: 10_u64.pow(14), + random_seed: vec![0, 1, 2], + view_config: None, + output_data_receivers: vec![], + } +} + +/// Define a configuration for which [`available_imports`] is implemented. This +/// allows to specify the imports available in a [`ConfiguredModule`]. +/// +/// [`available_imports`]: wasm_smith::Config::available_imports +/// [`ConfiguredModule`]: wasm_smith::ConfiguredModule +#[derive(arbitrary::Arbitrary, Debug)] +pub struct ModuleConfig {} + +impl wasm_smith::Config for ModuleConfig { + /// Returns a WebAssembly module which imports all near host functions. The + /// imports are grabbed from a compiled [test contract] which calls every + /// host function in its method `sanity_check`. + /// + /// [test contract]: unc_test_contracts::rs_contract + fn available_imports(&self) -> Option> { + Some(unc_test_contracts::rs_contract().into()) + } +} + +/// Wrapper to get more useful Debug. +pub struct ArbitraryModule(pub wasm_smith::ConfiguredModule); + +impl<'a> arbitrary::Arbitrary<'a> for ArbitraryModule { + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + wasm_smith::ConfiguredModule::::arbitrary(u).map(ArbitraryModule) + } +} + +impl fmt::Debug for ArbitraryModule { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let bytes = self.0.module.to_bytes(); + write!(f, "{:?}", bytes)?; + if let Ok(wat) = wasmprinter::print_bytes(&bytes) { + write!(f, "\n{}", wat)?; + } + Ok(()) + } +} diff --git a/runtime/unc-vm-runner/src/cache.rs b/runtime/unc-vm-runner/src/cache.rs new file mode 100644 index 000000000..c625096c5 --- /dev/null +++ b/runtime/unc-vm-runner/src/cache.rs @@ -0,0 +1,111 @@ +use crate::errors::ContractPrecompilatonResult; +use crate::logic::errors::{CacheError, CompilationError}; +use crate::logic::{CompiledContract, CompiledContractCache, Config}; +use crate::runner::VMKindExt; +use crate::ContractCode; +use borsh::BorshSerialize; +use unc_parameters::vm::VMKind; +use unc_primitives_core::hash::CryptoHash; +use std::collections::HashMap; +use std::fmt; +use std::sync::{Arc, Mutex}; + +#[derive(Debug, Clone, BorshSerialize)] +enum ContractCacheKey { + _Version1, + _Version2, + _Version3, + Version4 { + code_hash: CryptoHash, + vm_config_non_crypto_hash: u64, + vm_kind: VMKind, + vm_hash: u64, + }, +} + +fn vm_hash(vm_kind: VMKind) -> u64 { + match vm_kind { + #[cfg(all(feature = "wasmer0_vm", target_arch = "x86_64"))] + VMKind::Wasmer0 => crate::wasmer_runner::wasmer0_vm_hash(), + #[cfg(not(all(feature = "wasmer0_vm", target_arch = "x86_64")))] + VMKind::Wasmer0 => panic!("Wasmer0 is not enabled"), + #[cfg(all(feature = "wasmer2_vm", target_arch = "x86_64"))] + VMKind::Wasmer2 => crate::wasmer2_runner::wasmer2_vm_hash(), + #[cfg(not(all(feature = "wasmer2_vm", target_arch = "x86_64")))] + VMKind::Wasmer2 => panic!("Wasmer2 is not enabled"), + #[cfg(feature = "wasmtime_vm")] + VMKind::Wasmtime => crate::wasmtime_runner::wasmtime_vm_hash(), + #[cfg(not(feature = "wasmtime_vm"))] + VMKind::Wasmtime => panic!("Wasmtime is not enabled"), + #[cfg(all(feature = "unc_vm", target_arch = "x86_64"))] + VMKind::NearVm => crate::unc_vm_runner::unc_vm_vm_hash(), + #[cfg(not(all(feature = "unc_vm", target_arch = "x86_64")))] + VMKind::NearVm => panic!("NearVM is not enabled"), + } +} + +pub fn get_contract_cache_key(code: &ContractCode, config: &Config) -> CryptoHash { + let _span = tracing::debug_span!(target: "vm", "get_key").entered(); + let key = ContractCacheKey::Version4 { + code_hash: *code.hash(), + vm_config_non_crypto_hash: config.non_crypto_hash(), + vm_kind: config.vm_kind, + vm_hash: vm_hash(config.vm_kind), + }; + CryptoHash::hash_borsh(key) +} + +#[derive(Default)] +pub struct MockCompiledContractCache { + store: Arc>>, +} + +impl MockCompiledContractCache { + pub fn len(&self) -> usize { + self.store.lock().unwrap().len() + } +} + +impl CompiledContractCache for MockCompiledContractCache { + fn put(&self, key: &CryptoHash, value: CompiledContract) -> std::io::Result<()> { + self.store.lock().unwrap().insert(*key, value); + Ok(()) + } + + fn get(&self, key: &CryptoHash) -> std::io::Result> { + Ok(self.store.lock().unwrap().get(key).map(Clone::clone)) + } +} + +impl fmt::Debug for MockCompiledContractCache { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let guard = self.store.lock().unwrap(); + let hm: &HashMap<_, _> = &*guard; + fmt::Debug::fmt(hm, f) + } +} + +/// Precompiles contract for the current default VM, and stores result to the cache. +/// Returns `Ok(true)` if compiled code was added to the cache, and `Ok(false)` if element +/// is already in the cache, or if cache is `None`. +pub fn precompile_contract( + code: &ContractCode, + config: &Config, + cache: Option<&dyn CompiledContractCache>, +) -> Result, CacheError> { + let _span = tracing::debug_span!(target: "vm", "precompile_contract").entered(); + let vm_kind = config.vm_kind; + let runtime = vm_kind + .runtime(config.clone()) + .unwrap_or_else(|| panic!("the {vm_kind:?} runtime has not been enabled at compile time")); + let cache = match cache { + Some(it) => it, + None => return Ok(Ok(ContractPrecompilatonResult::CacheNotAvailable)), + }; + let key = get_contract_cache_key(code, config); + // Check if we already cached with such a key. + if cache.has(&key).map_err(CacheError::ReadError)? { + return Ok(Ok(ContractPrecompilatonResult::ContractAlreadyInCache)); + } + runtime.precompile(code, cache) +} diff --git a/runtime/unc-vm-runner/src/code.rs b/runtime/unc-vm-runner/src/code.rs new file mode 100644 index 000000000..f5257dec0 --- /dev/null +++ b/runtime/unc-vm-runner/src/code.rs @@ -0,0 +1,27 @@ +use unc_primitives_core::hash::{hash as sha256, CryptoHash}; + +pub struct ContractCode { + code: Vec, + hash: CryptoHash, +} + +impl ContractCode { + pub fn new(code: Vec, hash: Option) -> ContractCode { + let hash = hash.unwrap_or_else(|| sha256(&code)); + debug_assert_eq!(hash, sha256(&code)); + + ContractCode { code, hash } + } + + pub fn code(&self) -> &[u8] { + self.code.as_slice() + } + + pub fn into_code(self) -> Vec { + self.code + } + + pub fn hash(&self) -> &CryptoHash { + &self.hash + } +} diff --git a/runtime/unc-vm-runner/src/errors.rs b/runtime/unc-vm-runner/src/errors.rs new file mode 100644 index 000000000..a5287f0be --- /dev/null +++ b/runtime/unc-vm-runner/src/errors.rs @@ -0,0 +1,12 @@ +use crate::logic::errors::{FunctionCallError, VMRunnerError}; + +pub trait IntoVMError { + fn into_vm_error(self) -> Result; +} + +#[derive(Debug, PartialEq)] +pub enum ContractPrecompilatonResult { + ContractCompiled, + ContractAlreadyInCache, + CacheNotAvailable, +} diff --git a/runtime/unc-vm-runner/src/features.rs b/runtime/unc-vm-runner/src/features.rs new file mode 100644 index 000000000..65c25735c --- /dev/null +++ b/runtime/unc-vm-runner/src/features.rs @@ -0,0 +1,159 @@ +const REFERENCE_TYPES: bool = false; +const MULTI_VALUE: bool = false; +const BULK_MEMORY: bool = false; +const SIMD: bool = false; +const THREADS: bool = false; +const TAIL_CALL: bool = false; +const MULTI_MEMORY: bool = false; +const MEMORY64: bool = false; +const SATURATING_FLOAT_TO_INT: bool = false; +const EXCEPTIONS: bool = false; +const RELAXED_SIMD: bool = false; +const EXTENDED_COST: bool = false; +const COMPONENT_MODEL: bool = false; +const GC: bool = false; +const FUNCTION_REFERENCES: bool = false; +const MEMORY_CONTROL: bool = false; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) struct WasmFeatures { + sign_extension: bool, +} + +impl From for WasmFeatures { + fn from(version: crate::logic::ContractPrepareVersion) -> Self { + let sign_extension = match version { + crate::logic::ContractPrepareVersion::V0 => false, + crate::logic::ContractPrepareVersion::V1 => false, + crate::logic::ContractPrepareVersion::V2 => true, + }; + WasmFeatures { sign_extension } + } +} + +impl From for finite_wasm::wasmparser::WasmFeatures { + fn from(f: WasmFeatures) -> Self { + finite_wasm::wasmparser::WasmFeatures { + floats: true, + mutable_global: true, + sign_extension: f.sign_extension, + + reference_types: REFERENCE_TYPES, + // wasmer singlepass compiler requires multi_value return values to be disabled. + multi_value: MULTI_VALUE, + bulk_memory: BULK_MEMORY, + simd: SIMD, + threads: THREADS, + tail_call: TAIL_CALL, + multi_memory: MULTI_MEMORY, + exceptions: EXCEPTIONS, + memory64: MEMORY64, + saturating_float_to_int: SATURATING_FLOAT_TO_INT, + relaxed_simd: RELAXED_SIMD, + extended_const: EXTENDED_COST, + component_model: COMPONENT_MODEL, + function_references: FUNCTION_REFERENCES, + memory_control: MEMORY_CONTROL, + gc: GC, + } + } +} + +impl From for wasmparser::WasmFeatures { + fn from(_: WasmFeatures) -> Self { + // /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ + // + // There are features that this version of wasmparser enables by default, but pwasm + // currently does not and the compilers' support for these features is therefore largely + // untested if it exists at all. Non exhaustive list of examples: + // + // * saturating_float_to_int + // * sign_extension + // + // This is instead ensured by the fact that the V0 and V1 use pwasm utils in preparation + // and it does not support these extensions. + // + // /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ + wasmparser::WasmFeatures { + deterministic_only: false, + + module_linking: false, // old version of component model + reference_types: REFERENCE_TYPES, + multi_value: MULTI_VALUE, + bulk_memory: BULK_MEMORY, + simd: SIMD, + threads: THREADS, + tail_call: TAIL_CALL, + multi_memory: MULTI_MEMORY, + exceptions: EXCEPTIONS, + memory64: MEMORY64, + } + } +} + +#[cfg(all(feature = "unc_vm", target_arch = "x86_64"))] +impl From for unc_vm_types::Features { + fn from(f: crate::features::WasmFeatures) -> Self { + Self { + mutable_global: true, + sign_extension: f.sign_extension, + + threads: THREADS, + reference_types: REFERENCE_TYPES, + simd: SIMD, + bulk_memory: BULK_MEMORY, + multi_value: MULTI_VALUE, + tail_call: TAIL_CALL, + multi_memory: MULTI_MEMORY, + memory64: MEMORY64, + exceptions: EXCEPTIONS, + saturating_float_to_int: SATURATING_FLOAT_TO_INT, + } + } +} + +#[cfg(all(feature = "wasmer2_vm", target_arch = "x86_64"))] +impl From for wasmer_types::Features { + fn from(_: crate::features::WasmFeatures) -> Self { + // /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ + // + // There are features that this version of wasmparser enables by default, but pwasm + // currently does not and the compilers' support for these features is therefore largely + // untested if it exists at all. Non exhaustive list of examples: + // + // * saturating_float_to_int + // * sign_extension + // + // This is instead ensured by the fact that the V0 and V1 use pwasm utils in preparation + // and it does not support these extensions. + // + // /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ /!\ + Self { + module_linking: false, // old version of component model + threads: THREADS, + reference_types: REFERENCE_TYPES, + simd: SIMD, + bulk_memory: BULK_MEMORY, + multi_value: MULTI_VALUE, + tail_call: TAIL_CALL, + multi_memory: MULTI_MEMORY, + memory64: MEMORY64, + exceptions: EXCEPTIONS, + } + } +} + +#[cfg(feature = "wasmtime_vm")] +impl From for wasmtime::Config { + fn from(_: WasmFeatures) -> Self { + let mut config = wasmtime::Config::default(); + config.wasm_threads(THREADS); + config.wasm_reference_types(REFERENCE_TYPES); + config.wasm_simd(SIMD); + config.wasm_bulk_memory(BULK_MEMORY); + config.wasm_multi_value(MULTI_VALUE); + config.wasm_multi_memory(MULTI_MEMORY); + config.wasm_memory64(MEMORY64); + config + } +} diff --git a/runtime/unc-vm-runner/src/imports.rs b/runtime/unc-vm-runner/src/imports.rs new file mode 100644 index 000000000..2ad473b91 --- /dev/null +++ b/runtime/unc-vm-runner/src/imports.rs @@ -0,0 +1,724 @@ +//! Host function interface for smart contracts. +//! +//! Besides native WASM operations, smart contracts can call into runtime to +//! gain access to extra functionality, like operations with store. Such +//! "extras" are called "Host function", and play a role similar to syscalls. In +//! this module, we integrate host functions with various wasm runtimes we +//! support. The actual definitions of host functions live in the `vm-logic` +//! crate. +//! +//! Basically, what the following code does is (in pseudo-code): +//! +//! ```ignore +//! for host_fn in all_host_functions { +//! wasm_imports.define("env", host_fn.name, |args| host_fn(args)) +//! } +//! ``` +//! +//! The actual implementation is a bit more complicated, for two reasons. First, +//! host functions have different signatures, so there isn't a trivial single +//! type one can use to hold a host function. Second, we want to use direct +//! calls in the compiled WASM, so we need to avoid dynamic dispatch and hand +//! functions as ZSTs to the WASM runtimes. This basically means that we need to +//! code the above for-loop as a macro. +//! +//! So, the `imports!` macro invocation is the main "public" API -- it just list +//! all host functions with their signatures. `imports! { foo, bar, baz }` +//! expands to roughly +//! +//! ```ignore +//! macro_rules! for_each_available_import { +//! $($M:ident) => { +//! $M!(foo); +//! $M!(bar); +//! $M!(baz); +//! } +//! } +//! ``` +//! +//! That is, `for_each_available_import` is a high-order macro which takes macro +//! `M` as a parameter, and calls `M!` with each import. Each supported WASM +//! runtime (see submodules of this module) then calls +//! `for_each_available_import` with its own import definition logic. +//! +//! The real `for_each_available_import` takes one more argument -- +//! `VMConfig`. We can add new imports, but we must make sure that they +//! are only available to contracts at a specific protocol version -- we can't +//! make imports retroactively available to old transactions. So +//! `for_each_available_import` takes care to invoke `M!` only for currently +//! available imports. + +macro_rules! call_with_name { + ( $M:ident => @in $mod:ident : $func:ident < [ $( $arg_name:ident : $arg_type:ident ),* ] -> [ $( $returns:ident ),* ] > ) => { + $M!($mod / $func : $func < [ $( $arg_name : $arg_type ),* ] -> [ $( $returns ),* ] >) + }; + ( $M:ident => @as $name:ident : $func:ident < [ $( $arg_name:ident : $arg_type:ident ),* ] -> [ $( $returns:ident ),* ] > ) => { + $M!(env / $name : $func < [ $( $arg_name : $arg_type ),* ] -> [ $( $returns ),* ] >) + }; + ( $M:ident => $func:ident < [ $( $arg_name:ident : $arg_type:ident ),* ] -> [ $( $returns:ident ),* ] > ) => { + $M!(env / $func : $func < [ $( $arg_name : $arg_type ),* ] -> [ $( $returns ),* ] >) + }; +} + +macro_rules! imports { + ( + $($(#[$config_field:ident])? $(##[$feature_name:literal])? + $( @in $mod:ident : )? + $( @as $name:ident : )? + $func:ident < [ $( $arg_name:ident : $arg_type:ident ),* ] -> [ $( $returns:ident ),* ] >,)* + ) => { + macro_rules! for_each_available_import { + ($config:expr, $M:ident) => {$( + $(#[cfg(feature = $feature_name)])? + if true $(&& ($config).$config_field)? { + call_with_name!($M => $( @in $mod : )? $( @as $name : )? $func < [ $( $arg_name : $arg_type ),* ] -> [ $( $returns ),* ] >); + } + )*} + } + } +} + +imports! { + // ######################### + // # Finite-wasm internals # + // ######################### + @in internal: finite_wasm_gas<[gas: u64] -> []>, + @in internal: finite_wasm_stack<[operand_size: u64, frame_size: u64] -> []>, + @in internal: finite_wasm_unstack<[operand_size: u64, frame_size: u64] -> []>, + // ############# + // # Registers # + // ############# + read_register<[register_id: u64, ptr: u64] -> []>, + register_len<[register_id: u64] -> [u64]>, + write_register<[register_id: u64, data_len: u64, data_ptr: u64] -> []>, + // ############### + // # Context API # + // ############### + current_account_id<[register_id: u64] -> []>, + signer_account_id<[register_id: u64] -> []>, + signer_account_pk<[register_id: u64] -> []>, + predecessor_account_id<[register_id: u64] -> []>, + input<[register_id: u64] -> []>, + block_index<[] -> [u64]>, + block_timestamp<[] -> [u64]>, + epoch_height<[] -> [u64]>, + storage_usage<[] -> [u64]>, + // ################# + // # Economics API # + // ################# + account_balance<[balance_ptr: u64] -> []>, + account_locked_balance<[balance_ptr: u64] -> []>, + attached_deposit<[balance_ptr: u64] -> []>, + prepaid_gas<[] -> [u64]>, + used_gas<[] -> [u64]>, + // ############ + // # Math API # + // ############ + random_seed<[register_id: u64] -> []>, + sha256<[value_len: u64, value_ptr: u64, register_id: u64] -> []>, + keccak256<[value_len: u64, value_ptr: u64, register_id: u64] -> []>, + keccak512<[value_len: u64, value_ptr: u64, register_id: u64] -> []>, + #[ed25519_verify] ed25519_verify<[sig_len: u64, + sig_ptr: u64, + msg_len: u64, + msg_ptr: u64, + pub_key_len: u64, + pub_key_ptr: u64 + ] -> [u64]>, + #[math_extension] ripemd160<[value_len: u64, value_ptr: u64, register_id: u64] -> []>, + #[math_extension] ecrecover<[hash_len: u64, hash_ptr: u64, sign_len: u64, sig_ptr: u64, v: u64, malleability_flag: u64, register_id: u64] -> [u64]>, + // ##################### + // # Miscellaneous API # + // ##################### + value_return<[value_len: u64, value_ptr: u64] -> []>, + panic<[] -> []>, + panic_utf8<[len: u64, ptr: u64] -> []>, + log_utf8<[len: u64, ptr: u64] -> []>, + log_utf16<[len: u64, ptr: u64] -> []>, + abort<[msg_ptr: u32, filename_ptr: u32, line: u32, col: u32] -> []>, + // ################ + // # Promises API # + // ################ + promise_create<[ + account_id_len: u64, + account_id_ptr: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: u64 + ] -> [u64]>, + promise_then<[ + promise_index: u64, + account_id_len: u64, + account_id_ptr: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: u64 + ] -> [u64]>, + promise_and<[promise_idx_ptr: u64, promise_idx_count: u64] -> [u64]>, + promise_batch_create<[account_id_len: u64, account_id_ptr: u64] -> [u64]>, + promise_batch_then<[promise_index: u64, account_id_len: u64, account_id_ptr: u64] -> [u64]>, + // ####################### + // # Promise API actions # + // ####################### + promise_batch_action_create_account<[promise_index: u64] -> []>, + promise_batch_action_deploy_contract<[promise_index: u64, code_len: u64, code_ptr: u64] -> []>, + promise_batch_action_function_call<[ + promise_index: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: u64 + ] -> []>, + #[function_call_weight] promise_batch_action_function_call_weight<[ + promise_index: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: u64, + gas_weight: u64 + ] -> []>, + promise_batch_action_transfer<[promise_index: u64, amount_ptr: u64] -> []>, + promise_batch_action_stake<[ + promise_index: u64, + amount_ptr: u64, + public_key_len: u64, + public_key_ptr: u64 + ] -> []>, + promise_batch_action_add_key_with_full_access<[ + promise_index: u64, + public_key_len: u64, + public_key_ptr: u64, + nonce: u64 + ] -> []>, + promise_batch_action_add_key_with_function_call<[ + promise_index: u64, + public_key_len: u64, + public_key_ptr: u64, + nonce: u64, + allowance_ptr: u64, + receiver_id_len: u64, + receiver_id_ptr: u64, + method_names_len: u64, + method_names_ptr: u64 + ] -> []>, + promise_batch_action_delete_key<[ + promise_index: u64, + public_key_len: u64, + public_key_ptr: u64 + ] -> []>, + promise_batch_action_delete_account<[ + promise_index: u64, + beneficiary_id_len: u64, + beneficiary_id_ptr: u64 + ] -> []>, + // ####################### + // # Promise API results # + // ####################### + promise_results_count<[] -> [u64]>, + promise_result<[result_idx: u64, register_id: u64] -> [u64]>, + promise_return<[promise_idx: u64] -> []>, + // ############### + // # Storage API # + // ############### + storage_write<[key_len: u64, key_ptr: u64, value_len: u64, value_ptr: u64, register_id: u64] -> [u64]>, + storage_read<[key_len: u64, key_ptr: u64, register_id: u64] -> [u64]>, + storage_remove<[key_len: u64, key_ptr: u64, register_id: u64] -> [u64]>, + storage_has_key<[key_len: u64, key_ptr: u64] -> [u64]>, + storage_iter_prefix<[prefix_len: u64, prefix_ptr: u64] -> [u64]>, + storage_iter_range<[start_len: u64, start_ptr: u64, end_len: u64, end_ptr: u64] -> [u64]>, + storage_iter_next<[iterator_id: u64, key_register_id: u64, value_register_id: u64] -> [u64]>, + // Function for the injected gas counter. Automatically called by the gas meter. + @as gas: gas_seen_from_wasm<[gas_amount: u32] -> []>, + // ############### + // # Validator API # + // ############### + validator_frozen<[account_id_len: u64, account_id_ptr: u64, frozen_ptr: u64] -> []>, + validator_total_frozen<[frozen_ptr: u64] -> []>, + validator_power<[account_id_len: u64, account_id_ptr: u64, power_ptr: u64] -> []>, + validator_total_power<[power_ptr: u64] -> []>, + // ############# + // # Alt BN128 # + // ############# + #[alt_bn128] alt_bn128_g1_multiexp<[value_len: u64, value_ptr: u64, register_id: u64] -> []>, + #[alt_bn128] alt_bn128_g1_sum<[value_len: u64, value_ptr: u64, register_id: u64] -> []>, + #[alt_bn128] alt_bn128_pairing_check<[value_len: u64, value_ptr: u64] -> [u64]>, + // ############# + // # Sandbox # + // ############# + ##["sandbox"] sandbox_debug_log<[len: u64, ptr: u64] -> []>, +} + +#[cfg(all(feature = "wasmer0_vm", target_arch = "x86_64"))] +pub(crate) mod wasmer { + use super::str_eq; + use crate::logic::{VMLogic, VMLogicError}; + use std::ffi::c_void; + + #[derive(Clone, Copy)] + struct ImportReference(pub *mut c_void); + unsafe impl Send for ImportReference {} + unsafe impl Sync for ImportReference {} + + pub(crate) fn build( + memory: wasmer_runtime::memory::Memory, + logic: &mut VMLogic<'_>, + ) -> wasmer_runtime::ImportObject { + let raw_ptr = logic as *mut _ as *mut c_void; + let import_reference = ImportReference(raw_ptr); + let mut import_object = wasmer_runtime::ImportObject::new_with_data(move || { + let dtor = (|_: *mut c_void| {}) as fn(*mut c_void); + ({ import_reference }.0, dtor) + }); + + let mut ns_internal = wasmer_runtime_core::import::Namespace::new(); + let mut ns_env = wasmer_runtime_core::import::Namespace::new(); + ns_env.insert("memory", memory); + + macro_rules! add_import { + ( + $mod:ident / $name:ident : $func:ident < [ $( $arg_name:ident : $arg_type:ident ),* ] -> [ $( $returns:ident ),* ] > + ) => { + #[allow(unused_parens)] + fn $name( ctx: &mut wasmer_runtime::Ctx, $( $arg_name: $arg_type ),* ) -> Result<($( $returns ),*), VMLogicError> { + const IS_GAS: bool = str_eq(stringify!($name), "gas") || str_eq(stringify!($name), "finite_wasm_gas"); + let _span = if IS_GAS { + None + } else { + Some(tracing::trace_span!(target: "host-function", stringify!($name)).entered()) + }; + let logic: &mut VMLogic<'_> = unsafe { &mut *(ctx.data as *mut VMLogic<'_>) }; + logic.$func( $( $arg_name, )* ) + } + + match stringify!($mod) { + "env" => ns_env.insert(stringify!($name), wasmer_runtime::func!($name)), + "internal" => ns_internal.insert(stringify!($name), wasmer_runtime::func!($name)), + _ => unimplemented!(), + } + }; + } + for_each_available_import!(logic.config, add_import); + + import_object.register("env", ns_env); + import_object.register("internal", ns_internal); + import_object + } +} + +#[cfg(all(feature = "wasmer2_vm", target_arch = "x86_64"))] +pub(crate) mod wasmer2 { + use std::sync::Arc; + + use super::str_eq; + use crate::logic::VMLogic; + use wasmer_engine::Engine; + use wasmer_engine_universal::UniversalEngine; + use wasmer_vm::{ + ExportFunction, ExportFunctionMetadata, Resolver, VMFunction, VMFunctionKind, VMMemory, + }; + + pub(crate) struct Wasmer2Imports<'engine, 'vmlogic, 'vmlogic_refs> { + pub(crate) memory: VMMemory, + // Note: this same object is also referenced by the `metadata` field! + pub(crate) vmlogic: &'vmlogic mut VMLogic<'vmlogic_refs>, + pub(crate) metadata: Arc, + pub(crate) engine: &'engine UniversalEngine, + } + + trait Wasmer2Type { + type Wasmer; + fn to_wasmer(self) -> Self::Wasmer; + fn ty() -> wasmer_types::Type; + } + macro_rules! wasmer_types { + ($($native:ty as $wasmer:ty => $type_expr:expr;)*) => { + $(impl Wasmer2Type for $native { + type Wasmer = $wasmer; + fn to_wasmer(self) -> $wasmer { + self as _ + } + fn ty() -> wasmer_types::Type { + $type_expr + } + })* + } + } + wasmer_types! { + u32 as i32 => wasmer_types::Type::I32; + u64 as i64 => wasmer_types::Type::I64; + } + + macro_rules! return_ty { + ($return_type: ident = [ ]) => { + type $return_type = (); + fn make_ret() -> () {} + }; + ($return_type: ident = [ $($returns: ident),* ]) => { + #[repr(C)] + struct $return_type($(<$returns as Wasmer2Type>::Wasmer),*); + fn make_ret($($returns: $returns),*) -> Ret { Ret($($returns.to_wasmer()),*) } + } + } + + impl<'e, 'l, 'lr> Resolver for Wasmer2Imports<'e, 'l, 'lr> { + fn resolve(&self, _index: u32, module: &str, field: &str) -> Option { + if module == "env" && field == "memory" { + return Some(wasmer_vm::Export::Memory(self.memory.clone())); + } + + macro_rules! add_import { + ( + $mod:ident / $name:ident : $func:ident < + [ $( $arg_name:ident : $arg_type:ident ),* ] + -> [ $( $returns:ident ),* ] + > + ) => { + return_ty!(Ret = [ $($returns),* ]); + + extern "C" fn $name(env: *mut VMLogic<'_>, $( $arg_name: $arg_type ),* ) + -> Ret { + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + const IS_GAS: bool = str_eq(stringify!($name), "gas") || str_eq(stringify!($name), "finite_wasm_gas"); + let _span = if IS_GAS { + None + } else { + Some(tracing::trace_span!( + target: "host-function", + stringify!($name) + ).entered()) + }; + + // SAFETY: This code should only be executable within `'vmlogic` + // lifetime and so it is safe to dereference the `env` pointer which is + // known to be derived from a valid `&'vmlogic mut VMLogic<'_>` in the + // first place. + unsafe { (*env).$func( $( $arg_name, )* ) } + })); + // We want to ensure that the only kind of error that host function calls + // return are VMLogicError. This is important because we later attempt to + // downcast the `RuntimeError`s into `VMLogicError`. + let result: Result, _> = result; + #[allow(unused_parens)] + match result { + Ok(Ok(($($returns),*))) => make_ret($($returns),*), + Ok(Err(trap)) => unsafe { + // SAFETY: this can only be called by a WASM contract, so all the + // necessary hooks are known to be in place. + wasmer_vm::raise_user_trap(Box::new(trap)) + }, + Err(e) => unsafe { + // SAFETY: this can only be called by a WASM contract, so all the + // necessary hooks are known to be in place. + wasmer_vm::resume_panic(e) + }, + } + } + // TODO: a phf hashmap would probably work better here. + if module == stringify!($mod) && field == stringify!($name) { + let args = [$(<$arg_type as Wasmer2Type>::ty()),*]; + let rets = [$(<$returns as Wasmer2Type>::ty()),*]; + let signature = wasmer_types::FunctionTypeRef::new(&args[..], &rets[..]); + let signature = self.engine.register_signature(signature); + return Some(wasmer_vm::Export::Function(ExportFunction { + vm_function: VMFunction { + address: $name as *const _, + // SAFETY: here we erase the lifetime of the `vmlogic` reference, + // but we believe that the lifetimes on `Wasmer2Imports` enforce + // sufficiently that it isn't possible to call this exported + // function when vmlogic is no loger live. + vmctx: wasmer_vm::VMFunctionEnvironment { + host_env: self.vmlogic as *const _ as *mut _ + }, + signature, + kind: VMFunctionKind::Static, + call_trampoline: None, + instance_ref: None, + }, + metadata: Some(Arc::clone(&self.metadata)), + })); + } + }; + } + for_each_available_import!(self.vmlogic.config, add_import); + return None; + } + } + + pub(crate) fn build<'e, 'a, 'b>( + memory: VMMemory, + logic: &'a mut VMLogic<'b>, + engine: &'e UniversalEngine, + ) -> Wasmer2Imports<'e, 'a, 'b> { + let metadata = unsafe { + // SAFETY: the functions here are thread-safe. We ensure that the lifetime of `VMLogic` + // is sufficiently long by tying the lifetime of VMLogic to the return type which + // contains this metadata. + ExportFunctionMetadata::new(logic as *mut _ as *mut _, None, |ptr| ptr, |_| {}) + }; + Wasmer2Imports { memory, vmlogic: logic, metadata: Arc::new(metadata), engine } + } +} + +#[cfg(all(feature = "unc_vm", target_arch = "x86_64"))] +pub(crate) mod unc_vm { + use std::sync::Arc; + + use super::str_eq; + use crate::logic::VMLogic; + use unc_vm_engine::universal::UniversalEngine; + use unc_vm_vm::{ + ExportFunction, ExportFunctionMetadata, Resolver, VMFunction, VMFunctionKind, VMMemory, + }; + + pub(crate) struct NearVmImports<'engine, 'vmlogic, 'vmlogic_refs> { + pub(crate) memory: VMMemory, + // Note: this same object is also referenced by the `metadata` field! + pub(crate) vmlogic: &'vmlogic mut VMLogic<'vmlogic_refs>, + pub(crate) metadata: Arc, + pub(crate) engine: &'engine UniversalEngine, + } + + trait NearVmType { + type NearVm; + fn to_unc_vm(self) -> Self::NearVm; + fn ty() -> unc_vm_types::Type; + } + macro_rules! unc_vm_types { + ($($native:ty as $unc_vm:ty => $type_expr:expr;)*) => { + $(impl NearVmType for $native { + type NearVm = $unc_vm; + fn to_unc_vm(self) -> $unc_vm { + self as _ + } + fn ty() -> unc_vm_types::Type { + $type_expr + } + })* + } + } + unc_vm_types! { + u32 as i32 => unc_vm_types::Type::I32; + u64 as i64 => unc_vm_types::Type::I64; + } + + macro_rules! return_ty { + ($return_type: ident = [ ]) => { + type $return_type = (); + fn make_ret() -> () {} + }; + ($return_type: ident = [ $($returns: ident),* ]) => { + #[repr(C)] + struct $return_type($(<$returns as NearVmType>::NearVm),*); + fn make_ret($($returns: $returns),*) -> Ret { Ret($($returns.to_unc_vm()),*) } + } + } + + impl<'e, 'l, 'lr> Resolver for NearVmImports<'e, 'l, 'lr> { + fn resolve(&self, _index: u32, module: &str, field: &str) -> Option { + if module == "env" && field == "memory" { + return Some(unc_vm_vm::Export::Memory(self.memory.clone())); + } + + macro_rules! add_import { + ( + $mod:ident / $name:ident : $func:ident < + [ $( $arg_name:ident : $arg_type:ident ),* ] + -> [ $( $returns:ident ),* ] + > + ) => { + return_ty!(Ret = [ $($returns),* ]); + + extern "C" fn $name(env: *mut VMLogic<'_>, $( $arg_name: $arg_type ),* ) + -> Ret { + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + const IS_GAS: bool = str_eq(stringify!($name), "gas") || str_eq(stringify!($name), "finite_wasm_gas"); + let _span = if IS_GAS { + None + } else { + Some(tracing::trace_span!( + target: "host-function", + stringify!($name) + ).entered()) + }; + + // SAFETY: This code should only be executable within `'vmlogic` + // lifetime and so it is safe to dereference the `env` pointer which is + // known to be derived from a valid `&'vmlogic mut VMLogic<'_>` in the + // first place. + unsafe { (*env).$func( $( $arg_name, )* ) } + })); + // We want to ensure that the only kind of error that host function calls + // return are VMLogicError. This is important because we later attempt to + // downcast the `RuntimeError`s into `VMLogicError`. + let result: Result, _> = result; + #[allow(unused_parens)] + match result { + Ok(Ok(($($returns),*))) => make_ret($($returns),*), + Ok(Err(trap)) => unsafe { + // SAFETY: this can only be called by a WASM contract, so all the + // necessary hooks are known to be in place. + unc_vm_vm::raise_user_trap(Box::new(trap)) + }, + Err(e) => unsafe { + // SAFETY: this can only be called by a WASM contract, so all the + // necessary hooks are known to be in place. + unc_vm_vm::resume_panic(e) + }, + } + } + // TODO: a phf hashmap would probably work better here. + if module == stringify!($mod) && field == stringify!($name) { + let args = [$(<$arg_type as NearVmType>::ty()),*]; + let rets = [$(<$returns as NearVmType>::ty()),*]; + let signature = unc_vm_types::FunctionType::new(&args[..], &rets[..]); + let signature = self.engine.register_signature(signature); + return Some(unc_vm_vm::Export::Function(ExportFunction { + vm_function: VMFunction { + address: $name as *const _, + // SAFETY: here we erase the lifetime of the `vmlogic` reference, + // but we believe that the lifetimes on `NearVmImports` enforce + // sufficiently that it isn't possible to call this exported + // function when vmlogic is no loger live. + vmctx: unc_vm_vm::VMFunctionEnvironment { + host_env: self.vmlogic as *const _ as *mut _ + }, + signature, + kind: VMFunctionKind::Static, + call_trampoline: None, + instance_ref: None, + }, + metadata: Some(Arc::clone(&self.metadata)), + })); + } + }; + } + for_each_available_import!(self.vmlogic.config, add_import); + return None; + } + } + + pub(crate) fn build<'e, 'a, 'b>( + memory: VMMemory, + logic: &'a mut VMLogic<'b>, + engine: &'e UniversalEngine, + ) -> NearVmImports<'e, 'a, 'b> { + let metadata = unsafe { + // SAFETY: the functions here are thread-safe. We ensure that the lifetime of `VMLogic` + // is sufficiently long by tying the lifetime of VMLogic to the return type which + // contains this metadata. + ExportFunctionMetadata::new(logic as *mut _ as *mut _, None, |ptr| ptr, |_| {}) + }; + NearVmImports { memory, vmlogic: logic, metadata: Arc::new(metadata), engine } + } +} + +#[cfg(feature = "wasmtime_vm")] +pub(crate) mod wasmtime { + use super::str_eq; + use crate::logic::{VMLogic, VMLogicError}; + use std::cell::UnsafeCell; + use std::ffi::c_void; + + /// This is a container from which an error can be taken out by value. This is necessary as + /// `anyhow` does not really give any opportunity to grab causes by value and the VM Logic + /// errors end up a couple layers deep in a causal chain. + #[derive(Debug)] + pub(crate) struct ErrorContainer(std::sync::Mutex>); + impl ErrorContainer { + pub(crate) fn take(&self) -> Option { + let mut guard = self.0.lock().unwrap_or_else(|e| e.into_inner()); + guard.take() + } + } + impl std::error::Error for ErrorContainer {} + impl std::fmt::Display for ErrorContainer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("VMLogic error occurred and is now stored in an opaque storage container") + } + } + + thread_local! { + static CALLER_CONTEXT: UnsafeCell<*mut c_void> = UnsafeCell::new(0 as *mut c_void); + } + + pub(crate) fn link<'a, 'b>( + linker: &mut wasmtime::Linker<()>, + memory: wasmtime::Memory, + store: &wasmtime::Store<()>, + logic: &'a mut VMLogic<'b>, + ) { + // Unfortunately, due to the Wasmtime implementation we have to do tricks with the + // lifetimes of the logic instance and pass raw pointers here. + // FIXME(nagisa): I believe this is no longer required, we just need to look at this code + // again. + let raw_logic = logic as *mut _ as *mut c_void; + CALLER_CONTEXT.with(|caller_context| unsafe { *caller_context.get() = raw_logic }); + linker.define(store, "env", "memory", memory).expect("cannot define memory"); + + macro_rules! add_import { + ( + $mod:ident / $name:ident : $func:ident < [ $( $arg_name:ident : $arg_type:ident ),* ] -> [ $( $returns:ident ),* ] > + ) => { + #[allow(unused_parens)] + fn $name(caller: wasmtime::Caller<'_, ()>, $( $arg_name: $arg_type ),* ) -> anyhow::Result<($( $returns ),*)> { + const IS_GAS: bool = str_eq(stringify!($name), "gas") || str_eq(stringify!($name), "finite_wasm_gas"); + let _span = if IS_GAS { + None + } else { + Some(tracing::trace_span!(target: "host-function", stringify!($name)).entered()) + }; + // the below is bad. don't do this at home. it probably works thanks to the exact way the system is setup. + // Thanksfully, this doesn't run in production, and hopefully should be possible to remove before we even + // consider doing so. + let data = CALLER_CONTEXT.with(|caller_context| { + unsafe { + *caller_context.get() + } + }); + unsafe { + // Transmute the lifetime of caller so it's possible to put it in a thread-local. + crate::wasmtime_runner::CALLER.with(|runner_caller| *runner_caller.borrow_mut() = std::mem::transmute(caller)); + } + let logic: &mut VMLogic<'_> = unsafe { &mut *(data as *mut VMLogic<'_>) }; + match logic.$func( $( $arg_name as $arg_type, )* ) { + Ok(result) => Ok(result as ($( $returns ),* ) ), + Err(err) => { + Err(ErrorContainer(std::sync::Mutex::new(Some(err))).into()) + } + } + } + + linker.func_wrap(stringify!($mod), stringify!($name), $name).expect("cannot link external"); + }; + } + for_each_available_import!(logic.config, add_import); + } +} + +/// Constant-time string equality, work-around for `"foo" == "bar"` not working +/// in const context yet. +const fn str_eq(s1: &str, s2: &str) -> bool { + let s1 = s1.as_bytes(); + let s2 = s2.as_bytes(); + if s1.len() != s2.len() { + return false; + } + let mut i = 0; + while i < s1.len() { + if s1[i] != s2[i] { + return false; + } + i += 1; + } + true +} diff --git a/runtime/unc-vm-runner/src/instrument.rs b/runtime/unc-vm-runner/src/instrument.rs new file mode 100644 index 000000000..53d84fc05 --- /dev/null +++ b/runtime/unc-vm-runner/src/instrument.rs @@ -0,0 +1,19 @@ +//! Instrumentation of wasm code for gas metering and stack limiting. +//! +//! The code in this module was originally vendored from MIT/Apache wasm-utils +//! crate from the parity ecosystem: +//! +//! +//! +//! +//! As every little detail of instrumentation matters for the semantics of our +//! protocol, we want to maintain the implementation ourselves. +//! +//! At the moment, the implementation is a direct copy, but we don't intend to +//! keep the code aligned with the upstream, feel free to refactor if you find +//! something odd! See for the +//! overall instrumentation story. + +pub(crate) mod gas; +pub(crate) mod rules; +pub(crate) mod stack_height; diff --git a/runtime/unc-vm-runner/src/instrument/gas/mod.rs b/runtime/unc-vm-runner/src/instrument/gas/mod.rs new file mode 100644 index 000000000..b06794bcd --- /dev/null +++ b/runtime/unc-vm-runner/src/instrument/gas/mod.rs @@ -0,0 +1,966 @@ +//! This module is used to instrument a Wasm module with gas metering code. +//! +//! The primary public interface is the `inject_gas_counter` function which transforms a given +//! module into one that charges gas for code to be executed. See function documentation for usage +//! and details. + +#[cfg(test)] +mod validation; + +use parity_wasm::{builder, elements, elements::ValueType}; +use std::{cmp::min, mem}; + +use super::rules::{self, Rules}; + +pub fn update_call_index(instructions: &mut elements::Instructions, inserted_index: u32) { + use parity_wasm::elements::Instruction::*; + for instruction in instructions.elements_mut().iter_mut() { + if let Call(call_index) = instruction { + if *call_index >= inserted_index { + *call_index += 1 + } + } + } +} + +/// A control flow block is opened with the `block`, `loop`, and `if` instructions and is closed +/// with `end`. Each block implicitly defines a new label. The control blocks form a stack during +/// program execution. +/// +/// An example of block: +/// +/// ```ignore +/// loop +/// i32.const 1 +/// get_local 0 +/// i32.sub +/// tee_local 0 +/// br_if 0 +/// end +/// ``` +/// +/// The start of the block is `i32.const 1`. +/// +#[derive(Debug)] +struct ControlBlock { + /// The lowest control stack index corresponding to a forward jump targeted by a br, br_if, or + /// br_table instruction within this control block. The index must refer to a control block + /// that is not a loop, meaning it is a forward jump. Given the way Wasm control flow is + /// structured, the lowest index on the stack represents the furthest forward branch target. + /// + /// This value will always be at most the index of the block itself, even if there is no + /// explicit br instruction targeting this control block. This does not affect how the value is + /// used in the metering algorithm. + lowest_forward_br_target: usize, + + /// The active metering block that new instructions contribute a gas cost towards. + active_metered_block: MeteredBlock, + + /// Whether the control block is a loop. Loops have the distinguishing feature that branches to + /// them jump to the beginning of the block, not the end as with the other control blocks. + is_loop: bool, +} + +/// A block of code that metering instructions will be inserted at the beginning of. Metered blocks +/// are constructed with the property that, in the absence of any traps, either all instructions in +/// the block are executed or none are. +#[derive(Debug)] +pub(crate) struct MeteredBlock { + /// Index of the first instruction (aka `Opcode`) in the block. + start_pos: usize, + /// Sum of costs of all instructions until end of the block. + cost: u32, +} + +/// Counter is used to manage state during the gas metering algorithm implemented by +/// `inject_counter`. +struct Counter { + /// A stack of control blocks. This stack grows when new control blocks are opened with + /// `block`, `loop`, and `if` and shrinks when control blocks are closed with `end`. The first + /// block on the stack corresponds to the function body, not to any labelled block. Therefore + /// the actual Wasm label index associated with each control block is 1 less than its position + /// in this stack. + stack: Vec, + + /// A list of metered blocks that have been finalized, meaning they will no longer change. + finalized_blocks: Vec, +} + +impl Counter { + fn new() -> Counter { + Counter { stack: Vec::new(), finalized_blocks: Vec::new() } + } + + /// Open a new control block. The cursor is the position of the first instruction in the block. + fn begin_control_block(&mut self, cursor: usize, is_loop: bool) { + let index = self.stack.len(); + self.stack.push(ControlBlock { + lowest_forward_br_target: index, + active_metered_block: MeteredBlock { start_pos: cursor, cost: 0 }, + is_loop, + }) + } + + /// Close the last control block. The cursor is the position of the final (pseudo-)instruction + /// in the block. + fn finalize_control_block(&mut self, cursor: usize) -> Result<(), ()> { + // This either finalizes the active metered block or merges its cost into the active + // metered block in the previous control block on the stack. + self.finalize_metered_block(cursor)?; + + // Pop the control block stack. + let closing_control_block = self.stack.pop().ok_or(())?; + let closing_control_index = self.stack.len(); + + if self.stack.is_empty() { + return Ok(()); + } + + // Update the lowest_forward_br_target for the control block now on top of the stack. + { + let control_block = self.stack.last_mut().ok_or(())?; + control_block.lowest_forward_br_target = min( + control_block.lowest_forward_br_target, + closing_control_block.lowest_forward_br_target, + ); + } + + // If there may have been a branch to a lower index, then also finalize the active metered + // block for the previous control block. Otherwise, finalize it and begin a new one. + let may_br_out = closing_control_block.lowest_forward_br_target < closing_control_index; + if may_br_out { + self.finalize_metered_block(cursor)?; + } + + Ok(()) + } + + /// Finalize the current active metered block. + /// + /// Finalized blocks have final cost which will not change later. + fn finalize_metered_block(&mut self, cursor: usize) -> Result<(), ()> { + let closing_metered_block = { + let control_block = self.stack.last_mut().ok_or(())?; + mem::replace( + &mut control_block.active_metered_block, + MeteredBlock { start_pos: cursor + 1, cost: 0 }, + ) + }; + + // If the block was opened with a `block`, then its start position will be set to that of + // the active metered block in the control block one higher on the stack. This is because + // any instructions between a `block` and the first branch are part of the same basic block + // as the preceding instruction. In this case, instead of finalizing the block, merge its + // cost into the other active metered block to avoid injecting unnecessary instructions. + let last_index = self.stack.len() - 1; + if last_index > 0 { + let prev_control_block = self + .stack + .get_mut(last_index - 1) + .expect("last_index is greater than 0; last_index is stack size - 1; qed"); + let prev_metered_block = &mut prev_control_block.active_metered_block; + if closing_metered_block.start_pos == prev_metered_block.start_pos { + prev_metered_block.cost += closing_metered_block.cost; + return Ok(()); + } + } + + if closing_metered_block.cost > 0 { + self.finalized_blocks.push(closing_metered_block); + } + Ok(()) + } + + /// Handle a branch instruction in the program. The cursor is the index of the branch + /// instruction in the program. The indices are the stack positions of the target control + /// blocks. Recall that the index is 0 for a `return` and relatively indexed from the top of + /// the stack by the label of `br`, `br_if`, and `br_table` instructions. + fn branch(&mut self, cursor: usize, indices: &[usize]) -> Result<(), ()> { + self.finalize_metered_block(cursor)?; + + // Update the lowest_forward_br_target of the current control block. + for &index in indices { + let target_is_loop = { + let target_block = self.stack.get(index).ok_or(())?; + target_block.is_loop + }; + if target_is_loop { + continue; + } + + let control_block = self.stack.last_mut().ok_or(())?; + control_block.lowest_forward_br_target = + min(control_block.lowest_forward_br_target, index); + } + + Ok(()) + } + + /// Returns the stack index of the active control block. Returns None if stack is empty. + fn active_control_block_index(&self) -> Option { + self.stack.len().checked_sub(1) + } + + /// Get a reference to the currently active metered block. + fn active_metered_block(&mut self) -> Result<&mut MeteredBlock, ()> { + let top_block = self.stack.last_mut().ok_or(())?; + Ok(&mut top_block.active_metered_block) + } + + /// Increment the cost of the current block by the specified value. + fn increment(&mut self, val: u32) -> Result<(), ()> { + let top_block = self.active_metered_block()?; + top_block.cost = top_block.cost.checked_add(val).ok_or(())?; + Ok(()) + } +} + +fn inject_grow_counter(instructions: &mut elements::Instructions, grow_counter_func: u32) -> usize { + use parity_wasm::elements::Instruction::*; + let mut counter = 0; + for instruction in instructions.elements_mut() { + if let GrowMemory(_) = *instruction { + *instruction = Call(grow_counter_func); + counter += 1; + } + } + counter +} + +fn add_grow_counter( + module: elements::Module, + rules: &R, + gas_func: u32, +) -> elements::Module { + use parity_wasm::elements::Instruction::*; + use rules::MemoryGrowCost; + + let cost = match rules.memory_grow_cost() { + None => return module, + Some(MemoryGrowCost::Linear(val)) => val.get(), + }; + + let mut b = builder::from_module(module); + b.push_function( + builder::function() + .signature() + .with_param(ValueType::I32) + .with_result(ValueType::I32) + .build() + .body() + .with_instructions(elements::Instructions::new(vec![ + GetLocal(0), + GetLocal(0), + I32Const(cost as i32), + I32Mul, + // todo: there should be strong guarantee that it does not return anything on stack? + Call(gas_func), + GrowMemory(0), + End, + ])) + .build() + .build(), + ); + + b.build() +} + +pub(crate) fn determine_metered_blocks( + instructions: &elements::Instructions, + rules: &R, +) -> Result, ()> { + use parity_wasm::elements::Instruction::*; + + let mut counter = Counter::new(); + + // Begin an implicit function (i.e. `func...end`) block. + counter.begin_control_block(0, false); + + for cursor in 0..instructions.elements().len() { + let instruction = &instructions.elements()[cursor]; + let instruction_cost = rules.instruction_cost(instruction).ok_or(())?; + match instruction { + Block(_) => { + counter.increment(instruction_cost)?; + + // Begin new block. The cost of the following opcodes until `end` or `else` will + // be included into this block. The start position is set to that of the previous + // active metered block to signal that they should be merged in order to reduce + // unnecessary metering instructions. + let top_block_start_pos = counter.active_metered_block()?.start_pos; + counter.begin_control_block(top_block_start_pos, false); + } + If(_) => { + counter.increment(instruction_cost)?; + counter.begin_control_block(cursor + 1, false); + } + Loop(_) => { + counter.increment(instruction_cost)?; + counter.begin_control_block(cursor + 1, true); + } + End => { + counter.finalize_control_block(cursor)?; + } + Else => { + counter.finalize_metered_block(cursor)?; + } + Br(label) | BrIf(label) => { + counter.increment(instruction_cost)?; + + // Label is a relative index into the control stack. + let active_index = counter.active_control_block_index().ok_or(())?; + let target_index = active_index.checked_sub(*label as usize).ok_or(())?; + counter.branch(cursor, &[target_index])?; + } + BrTable(br_table_data) => { + counter.increment(instruction_cost)?; + + let active_index = counter.active_control_block_index().ok_or(())?; + let target_indices = [br_table_data.default] + .iter() + .chain(br_table_data.table.iter()) + .map(|label| active_index.checked_sub(*label as usize)) + .collect::>>() + .ok_or(())?; + counter.branch(cursor, &target_indices)?; + } + Return => { + counter.increment(instruction_cost)?; + counter.branch(cursor, &[0])?; + } + _ => { + // An ordinal non control flow instruction increments the cost of the current block. + counter.increment(instruction_cost)?; + } + } + } + + counter.finalized_blocks.sort_unstable_by_key(|block| block.start_pos); + Ok(counter.finalized_blocks) +} + +pub fn inject_counter( + instructions: &mut elements::Instructions, + rules: &R, + gas_func: u32, +) -> Result<(), ()> { + let blocks = determine_metered_blocks(instructions, rules)?; + insert_metering_calls(instructions, blocks, gas_func) +} + +// Then insert metering calls into a sequence of instructions given the block locations and costs. +fn insert_metering_calls( + instructions: &mut elements::Instructions, + blocks: Vec, + gas_func: u32, +) -> Result<(), ()> { + use parity_wasm::elements::Instruction::*; + + // To do this in linear time, construct a new vector of instructions, copying over old + // instructions one by one and injecting new ones as required. + let new_instrs_len = instructions.elements().len() + 2 * blocks.len(); + let original_instrs = + mem::replace(instructions.elements_mut(), Vec::with_capacity(new_instrs_len)); + let new_instrs = instructions.elements_mut(); + + let mut block_iter = blocks.into_iter().peekable(); + for (original_pos, instr) in original_instrs.into_iter().enumerate() { + // If there the next block starts at this position, inject metering instructions. + let used_block = if let Some(block) = block_iter.peek() { + if block.start_pos == original_pos { + new_instrs.push(I32Const(block.cost as i32)); + new_instrs.push(Call(gas_func)); + true + } else { + false + } + } else { + false + }; + + if used_block { + block_iter.next(); + } + + // Copy over the original instruction. + new_instrs.push(instr); + } + + if block_iter.next().is_some() { + return Err(()); + } + + Ok(()) +} + +/// Transforms a given module into one that charges gas for code to be executed by proxy of an +/// imported gas metering function. +/// +/// The output module imports a function "gas" from the specified module with type signature +/// [i32] -> []. The argument is the amount of gas required to continue execution. The external +/// function is meant to keep track of the total amount of gas used and trap or otherwise halt +/// execution of the runtime if the gas usage exceeds some allowed limit. +/// +/// The body of each function is divided into metered blocks, and the calls to charge gas are +/// inserted at the beginning of every such block of code. A metered block is defined so that, +/// unless there is a trap, either all of the instructions are executed or none are. These are +/// similar to basic blocks in a control flow graph, except that in some cases multiple basic +/// blocks can be merged into a single metered block. This is the case if any path through the +/// control flow graph containing one basic block also contains another. +/// +/// Charging gas is at the beginning of each metered block ensures that 1) all instructions +/// executed are already paid for, 2) instructions that will not be executed are not charged for +/// unless execution traps, and 3) the number of calls to "gas" is minimized. The corollary is that +/// modules instrumented with this metering code may charge gas for instructions not executed in +/// the event of a trap. +/// +/// Additionally, each `memory.grow` instruction found in the module is instrumented to first make +/// a call to charge gas for the additional pages requested. This cannot be done as part of the +/// block level gas charges as the gas cost is not static and depends on the stack argument to +/// `memory.grow`. +/// +/// The above transformations are performed for every function body defined in the module. This +/// function also rewrites all function indices references by code, table elements, etc., since +/// the addition of an imported functions changes the indices of module-defined functions. +/// +/// This routine runs in time linear in the size of the input module. +/// +/// The function fails if the module contains any operation forbidden by gas rule set, returning +/// the original module as an Err. +pub fn inject_gas_counter( + module: elements::Module, + rules: &R, + gas_module_name: &str, +) -> Result { + // Injecting gas counting external + let mut mbuilder = builder::from_module(module); + let import_sig = + mbuilder.push_signature(builder::signature().with_param(ValueType::I32).build_sig()); + + mbuilder.push_import( + builder::import().module(gas_module_name).field("gas").external().func(import_sig).build(), + ); + + // back to plain module + let mut module = mbuilder.build(); + + // calculate actual function index of the imported definition + // (subtract all imports that are NOT functions) + + let gas_func = module.import_count(elements::ImportCountType::Function) as u32 - 1; + let total_func = module.functions_space() as u32; + let mut need_grow_counter = false; + let mut error = false; + + // Updating calling addresses (all calls to function index >= `gas_func` should be incremented) + for section in module.sections_mut() { + match section { + elements::Section::Code(code_section) => { + for func_body in code_section.bodies_mut() { + update_call_index(func_body.code_mut(), gas_func); + if inject_counter(func_body.code_mut(), rules, gas_func).is_err() { + error = true; + break; + } + if rules.memory_grow_cost().is_some() + && inject_grow_counter(func_body.code_mut(), total_func) > 0 + { + need_grow_counter = true; + } + } + } + elements::Section::Export(export_section) => { + for export in export_section.entries_mut() { + if let elements::Internal::Function(func_index) = export.internal_mut() { + if *func_index >= gas_func { + *func_index += 1 + } + } + } + } + elements::Section::Element(elements_section) => { + // Note that we do not need to check the element type referenced because in the + // WebAssembly 1.0 spec, the only allowed element type is funcref. + for segment in elements_section.entries_mut() { + // update all indirect call addresses initial values + for func_index in segment.members_mut() { + if *func_index >= gas_func { + *func_index += 1 + } + } + } + } + elements::Section::Start(start_idx) => { + if *start_idx >= gas_func { + *start_idx += 1 + } + } + _ => {} + } + } + + if error { + return Err(module); + } + + if need_grow_counter { + Ok(add_grow_counter(module, rules, gas_func)) + } else { + Ok(module) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use parity_wasm::{builder, elements, elements::Instruction::*, serialize}; + + pub fn get_function_body( + module: &elements::Module, + index: usize, + ) -> Option<&[elements::Instruction]> { + module + .code_section() + .and_then(|code_section| code_section.bodies().get(index)) + .map(|func_body| func_body.code().elements()) + } + + #[test] + fn simple_grow() { + let module = builder::module() + .global() + .value_type() + .i32() + .init_expr(I32Const(0)) + .build() + .memory() + .build() + .function() + .signature() + .param() + .i32() + .build() + .body() + .with_instructions(elements::Instructions::new(vec![GetGlobal(0), GrowMemory(0), End])) + .build() + .build() + .build(); + + let injected_module = + inject_gas_counter(module, &rules::Set::default().with_grow_cost(10000), "env") + .unwrap(); + + assert_eq!( + get_function_body(&injected_module, 0).unwrap(), + &[I32Const(2), Call(0), GetGlobal(0), Call(2), End][..] + ); + assert_eq!( + get_function_body(&injected_module, 1).unwrap(), + &[GetLocal(0), GetLocal(0), I32Const(10000), I32Mul, Call(0), GrowMemory(0), End][..] + ); + + let binary = serialize(injected_module).expect("serialization failed"); + wasmparser::validate(&binary).unwrap(); + } + + #[test] + fn grow_no_gas_no_track() { + let module = builder::module() + .global() + .value_type() + .i32() + .init_expr(I32Const(0)) + .build() + .memory() + .build() + .function() + .signature() + .param() + .i32() + .build() + .body() + .with_instructions(elements::Instructions::new(vec![GetGlobal(0), GrowMemory(0), End])) + .build() + .build() + .build(); + + let injected_module = inject_gas_counter(module, &rules::Set::default(), "env").unwrap(); + + assert_eq!( + get_function_body(&injected_module, 0).unwrap(), + &[I32Const(2), Call(0), GetGlobal(0), GrowMemory(0), End][..] + ); + + assert_eq!(injected_module.functions_space(), 2); + + let binary = serialize(injected_module).expect("serialization failed"); + wasmparser::validate(&binary).unwrap(); + } + + #[test] + fn call_index() { + let module = builder::module() + .global() + .value_type() + .i32() + .build() + .function() + .signature() + .param() + .i32() + .build() + .body() + .build() + .build() + .function() + .signature() + .param() + .i32() + .build() + .body() + .with_instructions(elements::Instructions::new(vec![ + Call(0), + If(elements::BlockType::NoResult), + Call(0), + Call(0), + Call(0), + Else, + Call(0), + Call(0), + End, + Call(0), + End, + ])) + .build() + .build() + .build(); + + let injected_module = inject_gas_counter(module, &rules::Set::default(), "env").unwrap(); + + assert_eq!( + get_function_body(&injected_module, 1).unwrap(), + &[ + I32Const(3), + Call(0), + Call(1), + If(elements::BlockType::NoResult), + I32Const(3), + Call(0), + Call(1), + Call(1), + Call(1), + Else, + I32Const(2), + Call(0), + Call(1), + Call(1), + End, + Call(1), + End + ][..] + ); + } + + fn parse_wat(source: &str) -> elements::Module { + let module_bytes = wat::parse_str(source).expect("failed to parse module"); + elements::deserialize_buffer(module_bytes.as_ref()).expect("failed to parse module") + } + + macro_rules! test_gas_counter_injection { + (name = $name:ident; input = $input:expr; expected = $expected:expr) => { + #[test] + fn $name() { + let input_module = parse_wat($input); + let expected_module = parse_wat($expected); + + let injected_module = + inject_gas_counter(input_module, &rules::Set::default(), "env") + .expect("inject_gas_counter call failed"); + + let actual_func_body = get_function_body(&injected_module, 0) + .expect("injected module must have a function body"); + let expected_func_body = get_function_body(&expected_module, 0) + .expect("post-module must have a function body"); + + assert_eq!(actual_func_body, expected_func_body); + } + }; + } + + test_gas_counter_injection! { + name = simple; + input = r#" + (module + (func (result i32) + (get_global 0))) + "#; + expected = r#" + (module + (func (result i32) + (call 0 (i32.const 1)) + (get_global 0))) + "# + } + + test_gas_counter_injection! { + name = nested; + input = r#" + (module + (func (result i32) + (get_global 0) + (block + (get_global 0) + (get_global 0) + (get_global 0)) + (get_global 0))) + "#; + expected = r#" + (module + (func (result i32) + (call 0 (i32.const 6)) + (get_global 0) + (block + (get_global 0) + (get_global 0) + (get_global 0)) + (get_global 0))) + "# + } + + test_gas_counter_injection! { + name = ifelse; + input = r#" + (module + (func (result i32) + (get_global 0) + (if + (then + (get_global 0) + (get_global 0) + (get_global 0)) + (else + (get_global 0) + (get_global 0))) + (get_global 0))) + "#; + expected = r#" + (module + (func (result i32) + (call 0 (i32.const 3)) + (get_global 0) + (if + (then + (call 0 (i32.const 3)) + (get_global 0) + (get_global 0) + (get_global 0)) + (else + (call 0 (i32.const 2)) + (get_global 0) + (get_global 0))) + (get_global 0))) + "# + } + + test_gas_counter_injection! { + name = branch_innermost; + input = r#" + (module + (func (result i32) + (get_global 0) + (block + (get_global 0) + (drop) + (br 0) + (get_global 0) + (drop)) + (get_global 0))) + "#; + expected = r#" + (module + (func (result i32) + (call 0 (i32.const 6)) + (get_global 0) + (block + (get_global 0) + (drop) + (br 0) + (call 0 (i32.const 2)) + (get_global 0) + (drop)) + (get_global 0))) + "# + } + + test_gas_counter_injection! { + name = branch_outer_block; + input = r#" + (module + (func (result i32) + (get_global 0) + (block + (get_global 0) + (if + (then + (get_global 0) + (get_global 0) + (drop) + (br_if 1))) + (get_global 0) + (drop)) + (get_global 0))) + "#; + expected = r#" + (module + (func (result i32) + (call 0 (i32.const 5)) + (get_global 0) + (block + (get_global 0) + (if + (then + (call 0 (i32.const 4)) + (get_global 0) + (get_global 0) + (drop) + (br_if 1))) + (call 0 (i32.const 2)) + (get_global 0) + (drop)) + (get_global 0))) + "# + } + + test_gas_counter_injection! { + name = branch_outer_loop; + input = r#" + (module + (func (result i32) + (get_global 0) + (loop + (get_global 0) + (if + (then + (get_global 0) + (br_if 0)) + (else + (get_global 0) + (get_global 0) + (drop) + (br_if 1))) + (get_global 0) + (drop)) + (get_global 0))) + "#; + expected = r#" + (module + (func (result i32) + (call 0 (i32.const 3)) + (get_global 0) + (loop + (call 0 (i32.const 4)) + (get_global 0) + (if + (then + (call 0 (i32.const 2)) + (get_global 0) + (br_if 0)) + (else + (call 0 (i32.const 4)) + (get_global 0) + (get_global 0) + (drop) + (br_if 1))) + (get_global 0) + (drop)) + (get_global 0))) + "# + } + + test_gas_counter_injection! { + name = return_from_func; + input = r#" + (module + (func (result i32) + (get_global 0) + (if + (then + (return))) + (get_global 0))) + "#; + expected = r#" + (module + (func (result i32) + (call 0 (i32.const 2)) + (get_global 0) + (if + (then + (call 0 (i32.const 1)) + (return))) + (call 0 (i32.const 1)) + (get_global 0))) + "# + } + + test_gas_counter_injection! { + name = branch_from_if_not_else; + input = r#" + (module + (func (result i32) + (get_global 0) + (block + (get_global 0) + (if + (then (br 1)) + (else (br 0))) + (get_global 0) + (drop)) + (get_global 0))) + "#; + expected = r#" + (module + (func (result i32) + (call 0 (i32.const 5)) + (get_global 0) + (block + (get_global 0) + (if + (then + (call 0 (i32.const 1)) + (br 1)) + (else + (call 0 (i32.const 1)) + (br 0))) + (call 0 (i32.const 2)) + (get_global 0) + (drop)) + (get_global 0))) + "# + } + + test_gas_counter_injection! { + name = empty_loop; + input = r#" + (module + (func + (loop + (br 0) + ) + unreachable + ) + ) + "#; + expected = r#" + (module + (func + (call 0 (i32.const 2)) + (loop + (call 0 (i32.const 1)) + (br 0) + ) + unreachable + ) + ) + "# + } +} diff --git a/runtime/unc-vm-runner/src/instrument/gas/validation.rs b/runtime/unc-vm-runner/src/instrument/gas/validation.rs new file mode 100644 index 000000000..82dfb7640 --- /dev/null +++ b/runtime/unc-vm-runner/src/instrument/gas/validation.rs @@ -0,0 +1,387 @@ +//! This module is used to validate the correctness of the gas metering algorithm. +//! +//! Since the gas metering algorithm is complex, this checks correctness by fuzzing. The testing +//! strategy is to generate random, valid Wasm modules using Binaryen's translate-to-fuzz +//! functionality, then ensure for all functions defined, in all execution paths though the +//! function body that do not trap that the amount of gas charged by the proposed metering +//! instructions is correct. This is done by constructing a control flow graph and exhaustively +//! searching through all paths, which may take exponential time in the size of the function body in +//! the worst case. + +use super::rules::Rules; +use super::rules::Set as RuleSet; +use super::MeteredBlock; +use parity_wasm::elements::{FuncBody, Instruction}; +use std::collections::HashMap as Map; + +/// An ID for a node in a ControlFlowGraph. +type NodeId = usize; + +/// A node in a control flow graph is commonly known as a basic block. This is a sequence of +/// operations that are always executed sequentially. +#[derive(Debug, Default)] +struct ControlFlowNode { + /// The index of the first instruction in the basic block. This is only used for debugging. + first_instr_pos: Option, + + /// The actual gas cost of executing all instructions in the basic block. + actual_cost: u32, + + /// The amount of gas charged by the injected metering instructions within this basic block. + charged_cost: u32, + + /// Whether there are any other nodes in the graph that loop back to this one. Every cycle in + /// the control flow graph contains at least one node with this flag set. + is_loop_target: bool, + + /// Edges in the "forward" direction of the graph. The graph of nodes and their forward edges + /// forms a directed acyclic graph (DAG). + forward_edges: Vec, + + /// Edges in the "backwards" direction. These edges form cycles in the graph. + loopback_edges: Vec, +} + +/// A control flow graph where nodes are basic blocks and edges represent possible transitions +/// between them in execution flow. The graph has two types of edges, forward and loop-back edges. +/// The subgraph with only the forward edges forms a directed acyclic graph (DAG); including the +/// loop-back edges introduces cycles. +#[derive(Debug)] +pub struct ControlFlowGraph { + nodes: Vec, +} + +impl ControlFlowGraph { + fn new() -> Self { + ControlFlowGraph { nodes: Vec::new() } + } + + fn get_node(&self, node_id: NodeId) -> &ControlFlowNode { + self.nodes.get(node_id).unwrap() + } + + fn get_node_mut(&mut self, node_id: NodeId) -> &mut ControlFlowNode { + self.nodes.get_mut(node_id).unwrap() + } + + fn add_node(&mut self) -> NodeId { + self.nodes.push(ControlFlowNode::default()); + self.nodes.len() - 1 + } + + fn increment_actual_cost(&mut self, node_id: NodeId, cost: u32) { + self.get_node_mut(node_id).actual_cost += cost; + } + + fn increment_charged_cost(&mut self, node_id: NodeId, cost: u32) { + self.get_node_mut(node_id).charged_cost += cost; + } + + fn set_first_instr_pos(&mut self, node_id: NodeId, first_instr_pos: usize) { + self.get_node_mut(node_id).first_instr_pos = Some(first_instr_pos) + } + + fn new_edge(&mut self, from_id: NodeId, target_frame: &ControlFrame) { + if target_frame.is_loop { + self.new_loopback_edge(from_id, target_frame.entry_node); + } else { + self.new_forward_edge(from_id, target_frame.exit_node); + } + } + + fn new_forward_edge(&mut self, from_id: NodeId, to_id: NodeId) { + self.get_node_mut(from_id).forward_edges.push(to_id) + } + + fn new_loopback_edge(&mut self, from_id: NodeId, to_id: NodeId) { + self.get_node_mut(from_id).loopback_edges.push(to_id); + self.get_node_mut(to_id).is_loop_target = true; + } +} + +/// A control frame is opened upon entry into a function and by the `block`, `if`, and `loop` +/// instructions and is closed by `end` instructions. +struct ControlFrame { + is_loop: bool, + entry_node: NodeId, + exit_node: NodeId, + active_node: NodeId, +} + +impl ControlFrame { + fn new(entry_node_id: NodeId, exit_node_id: NodeId, is_loop: bool) -> Self { + ControlFrame { + is_loop, + entry_node: entry_node_id, + exit_node: exit_node_id, + active_node: entry_node_id, + } + } +} + +/// Construct a control flow graph from a function body and the metered blocks computed for it. +/// +/// This assumes that the function body has been validated already, otherwise this may panic. +fn build_control_flow_graph( + body: &FuncBody, + rules: &RuleSet, + blocks: &[MeteredBlock], +) -> Result { + let mut graph = ControlFlowGraph::new(); + + let entry_node_id = graph.add_node(); + let terminal_node_id = graph.add_node(); + + graph.set_first_instr_pos(entry_node_id, 0); + + let mut stack = vec![ControlFrame::new(entry_node_id, terminal_node_id, false)]; + let mut metered_blocks_iter = blocks.iter().peekable(); + for (cursor, instruction) in body.code().elements().iter().enumerate() { + let active_node_id = stack + .last() + .expect("module is valid by pre-condition; control stack must not be empty; qed") + .active_node; + + // Increment the charged cost if there are metering instructions to be inserted here. + let apply_block = + metered_blocks_iter.peek().map_or(false, |block| block.start_pos == cursor); + if apply_block { + let next_metered_block = + metered_blocks_iter.next().expect("peek returned an item; qed"); + graph.increment_charged_cost(active_node_id, next_metered_block.cost); + } + + let instruction_cost = rules.instruction_cost(instruction).ok_or(())?; + match instruction { + Instruction::Block(_) => { + graph.increment_actual_cost(active_node_id, instruction_cost); + + let exit_node_id = graph.add_node(); + stack.push(ControlFrame::new(active_node_id, exit_node_id, false)); + } + Instruction::If(_) => { + graph.increment_actual_cost(active_node_id, instruction_cost); + + let then_node_id = graph.add_node(); + let exit_node_id = graph.add_node(); + + stack.push(ControlFrame::new(then_node_id, exit_node_id, false)); + graph.new_forward_edge(active_node_id, then_node_id); + graph.set_first_instr_pos(then_node_id, cursor + 1); + } + Instruction::Loop(_) => { + graph.increment_actual_cost(active_node_id, instruction_cost); + + let loop_node_id = graph.add_node(); + let exit_node_id = graph.add_node(); + + stack.push(ControlFrame::new(loop_node_id, exit_node_id, true)); + graph.new_forward_edge(active_node_id, loop_node_id); + graph.set_first_instr_pos(loop_node_id, cursor + 1); + } + Instruction::Else => { + let active_frame_idx = stack.len() - 1; + let prev_frame_idx = stack.len() - 2; + + let else_node_id = graph.add_node(); + stack[active_frame_idx].active_node = else_node_id; + + let prev_node_id = stack[prev_frame_idx].active_node; + graph.new_forward_edge(prev_node_id, else_node_id); + graph.set_first_instr_pos(else_node_id, cursor + 1); + } + Instruction::End => { + let closing_frame = stack.pop() + .expect("module is valid by pre-condition; ends correspond to control stack frames; qed"); + + graph.new_forward_edge(active_node_id, closing_frame.exit_node); + graph.set_first_instr_pos(closing_frame.exit_node, cursor + 1); + + if let Some(active_frame) = stack.last_mut() { + active_frame.active_node = closing_frame.exit_node; + } + } + Instruction::Br(label) => { + graph.increment_actual_cost(active_node_id, instruction_cost); + + let active_frame_idx = stack.len() - 1; + let target_frame_idx = active_frame_idx - (*label as usize); + graph.new_edge(active_node_id, &stack[target_frame_idx]); + + // Next instruction is unreachable, but carry on anyway. + let new_node_id = graph.add_node(); + stack[active_frame_idx].active_node = new_node_id; + graph.set_first_instr_pos(new_node_id, cursor + 1); + } + Instruction::BrIf(label) => { + graph.increment_actual_cost(active_node_id, instruction_cost); + + let active_frame_idx = stack.len() - 1; + let target_frame_idx = active_frame_idx - (*label as usize); + graph.new_edge(active_node_id, &stack[target_frame_idx]); + + let new_node_id = graph.add_node(); + stack[active_frame_idx].active_node = new_node_id; + graph.new_forward_edge(active_node_id, new_node_id); + graph.set_first_instr_pos(new_node_id, cursor + 1); + } + Instruction::BrTable(br_table_data) => { + graph.increment_actual_cost(active_node_id, instruction_cost); + + let active_frame_idx = stack.len() - 1; + for &label in [br_table_data.default].iter().chain(br_table_data.table.iter()) { + let target_frame_idx = active_frame_idx - (label as usize); + graph.new_edge(active_node_id, &stack[target_frame_idx]); + } + + let new_node_id = graph.add_node(); + stack[active_frame_idx].active_node = new_node_id; + graph.set_first_instr_pos(new_node_id, cursor + 1); + } + Instruction::Return => { + graph.increment_actual_cost(active_node_id, instruction_cost); + + graph.new_forward_edge(active_node_id, terminal_node_id); + + let active_frame_idx = stack.len() - 1; + let new_node_id = graph.add_node(); + stack[active_frame_idx].active_node = new_node_id; + graph.set_first_instr_pos(new_node_id, cursor + 1); + } + _ => graph.increment_actual_cost(active_node_id, instruction_cost), + } + } + + assert!(stack.is_empty()); + + Ok(graph) +} + +/// Exhaustively search through all paths in the control flow graph, starting from the first node +/// and ensure that 1) all paths with only forward edges ending with the terminal node have an +/// equal total actual gas cost and total charged gas cost, and 2) all cycles beginning with a loop +/// entry point and ending with a node with a loop-back edge to the entry point have equal actual +/// and charged gas costs. If this returns true, then the metered blocks used to construct the +/// control flow graph are correct with respect to the function body. +/// +/// In the worst case, this runs in time exponential in the size of the graph. +fn validate_graph_gas_costs(graph: &ControlFlowGraph) -> bool { + fn visit( + graph: &ControlFlowGraph, + node_id: NodeId, + mut total_actual: u32, + mut total_charged: u32, + loop_costs: &mut Map, + ) -> bool { + let node = graph.get_node(node_id); + + total_actual += node.actual_cost; + total_charged += node.charged_cost; + + if node.is_loop_target { + loop_costs.insert(node_id, (node.actual_cost, node.charged_cost)); + } + + if node.forward_edges.is_empty() && total_actual != total_charged { + return false; + } + + for loop_node_id in node.loopback_edges.iter() { + let (loop_actual, loop_charged) = loop_costs + .get_mut(loop_node_id) + .expect("cannot arrive at loopback edge without visiting loop entry node"); + if loop_actual != loop_charged { + return false; + } + } + + for next_node_id in node.forward_edges.iter() { + if !visit(graph, *next_node_id, total_actual, total_charged, loop_costs) { + return false; + } + } + + if node.is_loop_target { + loop_costs.remove(&node_id); + } + + true + } + + // Recursively explore all paths through the execution graph starting from the entry node. + visit(graph, 0, 0, 0, &mut Map::new()) +} + +/// Validate that the metered blocks are correct with respect to the function body by exhaustively +/// searching all paths through the control flow graph. +/// +/// This assumes that the function body has been validated already, otherwise this may panic. +fn validate_metering_injections( + body: &FuncBody, + rules: &RuleSet, + blocks: &[MeteredBlock], +) -> Result { + let graph = build_control_flow_graph(body, rules, blocks)?; + Ok(validate_graph_gas_costs(&graph)) +} + +mod tests { + use super::{super::determine_metered_blocks, *}; + + use arbitrary::Arbitrary; + use parity_wasm::elements; + use rand::{thread_rng, RngCore}; + + #[track_caller] + fn check(bytes: &[u8]) { + if let Ok(module) = elements::deserialize_buffer::(&bytes) { + for func_body in module.code_section().iter().flat_map(|section| section.bodies()) { + let rules = RuleSet::default(); + + let metered_blocks = determine_metered_blocks(func_body.code(), &rules).unwrap(); + let success = + validate_metering_injections(func_body, &rules, &metered_blocks).unwrap(); + assert!(success, "{bytes:?}"); + } + } + } + + #[test] + // See test_build_control_flow_graph_failure + #[ignore] + fn test_build_control_flow_graph() { + for _ in 0..20 { + let mut rand_input = [0u8; 4096]; + thread_rng().fill_bytes(&mut rand_input); + let u = arbitrary::Unstructured::new(&rand_input); + if let Ok(arb_module) = wasm_smith::Module::arbitrary_take_rest(u) { + let bytes = arb_module.to_bytes(); + check(&bytes) + } + } + } + + #[test] + // This test currently fails, b/c there's a bug in cfg-building algo in this + // module -- it doesn't add a forward edge for the "then" branch. + #[ignore] + fn test_build_control_flow_graph_failure() { + let bytes = wat::parse_str( + r#" +(module + (func + i32.const 0 + if (result i32) + i32.const 0 + else + i32.const 0 + end + drop + ) +) +"#, + ) + .unwrap(); + check(&bytes); + } +} diff --git a/runtime/unc-vm-runner/src/instrument/rules.rs b/runtime/unc-vm-runner/src/instrument/rules.rs new file mode 100644 index 000000000..50cc474f3 --- /dev/null +++ b/runtime/unc-vm-runner/src/instrument/rules.rs @@ -0,0 +1,331 @@ +use parity_wasm::elements::Instruction; +use std::collections::HashMap as Map; +use std::num::NonZeroU32; +use std::str::FromStr; + +pub struct UnknownInstruction; + +/// An interface that describes instruction costs. +pub trait Rules { + /// Returns the cost for the passed `instruction`. + /// + /// Returning `None` makes the gas instrumention end with an error. This is meant + /// as a way to have a partial rule set where any instruction that is not specifed + /// is considered as forbidden. + fn instruction_cost(&self, instruction: &Instruction) -> Option; + + /// Returns the costs for growing the memory using the `memory.grow` instruction. + /// + /// Please note that these costs are in addition to the costs specified by `instruction_cost` + /// for the `memory.grow` instruction. Specifying `None` leads to no additional charge. + /// Those are meant as dynamic costs which take the amount of pages that the memory is + /// grown by into consideration. This is not possible using `instruction_cost` because + /// those costs depend on the stack and must be injected as code into the function calling + /// `memory.grow`. Therefore returning `Some` comes with a performance cost. + fn memory_grow_cost(&self) -> Option; +} + +/// Dynamic costs for memory growth. +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum MemoryGrowCost { + /// Charge the specified amount for each page that the memory is grown by. + Linear(NonZeroU32), +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[allow(unused)] +pub enum Metering { + Regular, + Forbidden, + Fixed(u32), +} + +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] +pub enum InstructionType { + Bit, + Add, + Mul, + Div, + Load, + Store, + Const, + FloatConst, + Local, + Global, + ControlFlow, + IntegerComparison, + FloatComparison, + Float, + Conversion, + FloatConversion, + Reinterpretation, + Unreachable, + Nop, + CurrentMemory, + GrowMemory, +} + +impl FromStr for InstructionType { + type Err = UnknownInstruction; + + fn from_str(s: &str) -> Result { + match s { + "bit" => Ok(InstructionType::Bit), + "add" => Ok(InstructionType::Add), + "mul" => Ok(InstructionType::Mul), + "div" => Ok(InstructionType::Div), + "load" => Ok(InstructionType::Load), + "store" => Ok(InstructionType::Store), + "const" => Ok(InstructionType::Const), + "local" => Ok(InstructionType::Local), + "global" => Ok(InstructionType::Global), + "flow" => Ok(InstructionType::ControlFlow), + "integer_comp" => Ok(InstructionType::IntegerComparison), + "float_comp" => Ok(InstructionType::FloatComparison), + "float" => Ok(InstructionType::Float), + "conversion" => Ok(InstructionType::Conversion), + "float_conversion" => Ok(InstructionType::FloatConversion), + "reinterpret" => Ok(InstructionType::Reinterpretation), + "unreachable" => Ok(InstructionType::Unreachable), + "nop" => Ok(InstructionType::Nop), + "current_mem" => Ok(InstructionType::CurrentMemory), + "grow_mem" => Ok(InstructionType::GrowMemory), + _ => Err(UnknownInstruction), + } + } +} + +impl InstructionType { + pub fn op(instruction: &Instruction) -> Self { + use Instruction::*; + + match *instruction { + Unreachable => InstructionType::Unreachable, + Nop => InstructionType::Nop, + Block(_) => InstructionType::ControlFlow, + Loop(_) => InstructionType::ControlFlow, + If(_) => InstructionType::ControlFlow, + Else => InstructionType::ControlFlow, + End => InstructionType::ControlFlow, + Br(_) => InstructionType::ControlFlow, + BrIf(_) => InstructionType::ControlFlow, + BrTable(_) => InstructionType::ControlFlow, + Return => InstructionType::ControlFlow, + Call(_) => InstructionType::ControlFlow, + CallIndirect(_, _) => InstructionType::ControlFlow, + Drop => InstructionType::ControlFlow, + Select => InstructionType::ControlFlow, + + GetLocal(_) => InstructionType::Local, + SetLocal(_) => InstructionType::Local, + TeeLocal(_) => InstructionType::Local, + GetGlobal(_) => InstructionType::Global, + SetGlobal(_) => InstructionType::Global, + + I32Load(_, _) => InstructionType::Load, + I64Load(_, _) => InstructionType::Load, + F32Load(_, _) => InstructionType::Load, + F64Load(_, _) => InstructionType::Load, + I32Load8S(_, _) => InstructionType::Load, + I32Load8U(_, _) => InstructionType::Load, + I32Load16S(_, _) => InstructionType::Load, + I32Load16U(_, _) => InstructionType::Load, + I64Load8S(_, _) => InstructionType::Load, + I64Load8U(_, _) => InstructionType::Load, + I64Load16S(_, _) => InstructionType::Load, + I64Load16U(_, _) => InstructionType::Load, + I64Load32S(_, _) => InstructionType::Load, + I64Load32U(_, _) => InstructionType::Load, + + I32Store(_, _) => InstructionType::Store, + I64Store(_, _) => InstructionType::Store, + F32Store(_, _) => InstructionType::Store, + F64Store(_, _) => InstructionType::Store, + I32Store8(_, _) => InstructionType::Store, + I32Store16(_, _) => InstructionType::Store, + I64Store8(_, _) => InstructionType::Store, + I64Store16(_, _) => InstructionType::Store, + I64Store32(_, _) => InstructionType::Store, + + CurrentMemory(_) => InstructionType::CurrentMemory, + GrowMemory(_) => InstructionType::GrowMemory, + + I32Const(_) => InstructionType::Const, + I64Const(_) => InstructionType::Const, + + F32Const(_) => InstructionType::FloatConst, + F64Const(_) => InstructionType::FloatConst, + + I32Eqz => InstructionType::IntegerComparison, + I32Eq => InstructionType::IntegerComparison, + I32Ne => InstructionType::IntegerComparison, + I32LtS => InstructionType::IntegerComparison, + I32LtU => InstructionType::IntegerComparison, + I32GtS => InstructionType::IntegerComparison, + I32GtU => InstructionType::IntegerComparison, + I32LeS => InstructionType::IntegerComparison, + I32LeU => InstructionType::IntegerComparison, + I32GeS => InstructionType::IntegerComparison, + I32GeU => InstructionType::IntegerComparison, + + I64Eqz => InstructionType::IntegerComparison, + I64Eq => InstructionType::IntegerComparison, + I64Ne => InstructionType::IntegerComparison, + I64LtS => InstructionType::IntegerComparison, + I64LtU => InstructionType::IntegerComparison, + I64GtS => InstructionType::IntegerComparison, + I64GtU => InstructionType::IntegerComparison, + I64LeS => InstructionType::IntegerComparison, + I64LeU => InstructionType::IntegerComparison, + I64GeS => InstructionType::IntegerComparison, + I64GeU => InstructionType::IntegerComparison, + + F32Eq => InstructionType::FloatComparison, + F32Ne => InstructionType::FloatComparison, + F32Lt => InstructionType::FloatComparison, + F32Gt => InstructionType::FloatComparison, + F32Le => InstructionType::FloatComparison, + F32Ge => InstructionType::FloatComparison, + + F64Eq => InstructionType::FloatComparison, + F64Ne => InstructionType::FloatComparison, + F64Lt => InstructionType::FloatComparison, + F64Gt => InstructionType::FloatComparison, + F64Le => InstructionType::FloatComparison, + F64Ge => InstructionType::FloatComparison, + + I32Clz => InstructionType::Bit, + I32Ctz => InstructionType::Bit, + I32Popcnt => InstructionType::Bit, + I32Add => InstructionType::Add, + I32Sub => InstructionType::Add, + I32Mul => InstructionType::Mul, + I32DivS => InstructionType::Div, + I32DivU => InstructionType::Div, + I32RemS => InstructionType::Div, + I32RemU => InstructionType::Div, + I32And => InstructionType::Bit, + I32Or => InstructionType::Bit, + I32Xor => InstructionType::Bit, + I32Shl => InstructionType::Bit, + I32ShrS => InstructionType::Bit, + I32ShrU => InstructionType::Bit, + I32Rotl => InstructionType::Bit, + I32Rotr => InstructionType::Bit, + + I64Clz => InstructionType::Bit, + I64Ctz => InstructionType::Bit, + I64Popcnt => InstructionType::Bit, + I64Add => InstructionType::Add, + I64Sub => InstructionType::Add, + I64Mul => InstructionType::Mul, + I64DivS => InstructionType::Div, + I64DivU => InstructionType::Div, + I64RemS => InstructionType::Div, + I64RemU => InstructionType::Div, + I64And => InstructionType::Bit, + I64Or => InstructionType::Bit, + I64Xor => InstructionType::Bit, + I64Shl => InstructionType::Bit, + I64ShrS => InstructionType::Bit, + I64ShrU => InstructionType::Bit, + I64Rotl => InstructionType::Bit, + I64Rotr => InstructionType::Bit, + + F32Abs => InstructionType::Float, + F32Neg => InstructionType::Float, + F32Ceil => InstructionType::Float, + F32Floor => InstructionType::Float, + F32Trunc => InstructionType::Float, + F32Nearest => InstructionType::Float, + F32Sqrt => InstructionType::Float, + F32Add => InstructionType::Float, + F32Sub => InstructionType::Float, + F32Mul => InstructionType::Float, + F32Div => InstructionType::Float, + F32Min => InstructionType::Float, + F32Max => InstructionType::Float, + F32Copysign => InstructionType::Float, + F64Abs => InstructionType::Float, + F64Neg => InstructionType::Float, + F64Ceil => InstructionType::Float, + F64Floor => InstructionType::Float, + F64Trunc => InstructionType::Float, + F64Nearest => InstructionType::Float, + F64Sqrt => InstructionType::Float, + F64Add => InstructionType::Float, + F64Sub => InstructionType::Float, + F64Mul => InstructionType::Float, + F64Div => InstructionType::Float, + F64Min => InstructionType::Float, + F64Max => InstructionType::Float, + F64Copysign => InstructionType::Float, + + I32WrapI64 => InstructionType::Conversion, + I64ExtendSI32 => InstructionType::Conversion, + I64ExtendUI32 => InstructionType::Conversion, + + I32TruncSF32 => InstructionType::FloatConversion, + I32TruncUF32 => InstructionType::FloatConversion, + I32TruncSF64 => InstructionType::FloatConversion, + I32TruncUF64 => InstructionType::FloatConversion, + I64TruncSF32 => InstructionType::FloatConversion, + I64TruncUF32 => InstructionType::FloatConversion, + I64TruncSF64 => InstructionType::FloatConversion, + I64TruncUF64 => InstructionType::FloatConversion, + F32ConvertSI32 => InstructionType::FloatConversion, + F32ConvertUI32 => InstructionType::FloatConversion, + F32ConvertSI64 => InstructionType::FloatConversion, + F32ConvertUI64 => InstructionType::FloatConversion, + F32DemoteF64 => InstructionType::FloatConversion, + F64ConvertSI32 => InstructionType::FloatConversion, + F64ConvertUI32 => InstructionType::FloatConversion, + F64ConvertSI64 => InstructionType::FloatConversion, + F64ConvertUI64 => InstructionType::FloatConversion, + F64PromoteF32 => InstructionType::FloatConversion, + + I32ReinterpretF32 => InstructionType::Reinterpretation, + I64ReinterpretF64 => InstructionType::Reinterpretation, + F32ReinterpretI32 => InstructionType::Reinterpretation, + F64ReinterpretI64 => InstructionType::Reinterpretation, + } + } +} + +#[derive(Debug)] +pub struct Set { + regular: u32, + entries: Map, + grow: u32, +} + +impl Default for Set { + fn default() -> Self { + Set { regular: 1, entries: Map::new(), grow: 0 } + } +} + +impl Set { + pub fn new(regular: u32, entries: Map) -> Self { + Set { regular, entries, grow: 0 } + } + + pub fn with_grow_cost(mut self, val: u32) -> Self { + self.grow = val; + self + } +} + +impl Rules for Set { + fn instruction_cost(&self, instruction: &Instruction) -> Option { + match self.entries.get(&InstructionType::op(instruction)) { + None | Some(Metering::Regular) => Some(self.regular), + Some(Metering::Fixed(val)) => Some(*val), + Some(Metering::Forbidden) => None, + } + } + + fn memory_grow_cost(&self) -> Option { + NonZeroU32::new(self.grow).map(MemoryGrowCost::Linear) + } +} diff --git a/runtime/unc-vm-runner/src/instrument/stack_height/max_height.rs b/runtime/unc-vm-runner/src/instrument/stack_height/max_height.rs new file mode 100644 index 000000000..effef131b --- /dev/null +++ b/runtime/unc-vm-runner/src/instrument/stack_height/max_height.rs @@ -0,0 +1,576 @@ +use super::{Error, ModuleCtx}; +use parity_wasm::elements::{BlockType, Type}; + +/// Control stack frame. +#[derive(Debug)] +struct Frame { + /// Stack becomes polymorphic only after an instruction that + /// never passes control further was executed. + is_polymorphic: bool, + + /// Count of values which will be pushed after the exit + /// from the current block. + end_arity: u32, + + /// Count of values which should be poped upon a branch to + /// this frame. + /// + /// This might be diffirent from `end_arity` since branch + /// to the loop header can't take any values. + branch_arity: u32, + + /// Stack height before entering in the block. + start_height: u32, +} + +/// This is a compound stack that abstracts tracking height of the value stack +/// and manipulation of the control stack. +struct Stack { + height: u32, + control_stack: Vec, +} + +impl Stack { + fn new() -> Stack { + Stack { height: 0, control_stack: Vec::new() } + } + + /// Returns current height of the value stack. + fn height(&self) -> u32 { + self.height + } + + /// Returns a reference to a frame by specified depth relative to the top of + /// control stack. + fn frame(&self, rel_depth: u32) -> Result<&Frame, Error> { + let control_stack_height: usize = self.control_stack.len(); + let last_idx = control_stack_height + .checked_sub(1) + .ok_or_else(|| Error("control stack is empty".into()))?; + let idx = last_idx + .checked_sub(rel_depth as usize) + .ok_or_else(|| Error("control stack out-of-bounds".into()))?; + Ok(&self.control_stack[idx]) + } + + /// Mark successive instructions as unreachable. + /// + /// This effectively makes stack polymorphic. + fn mark_unreachable(&mut self) -> Result<(), Error> { + let top_frame = + self.control_stack.last_mut().ok_or_else(|| Error("stack must be non-empty".into()))?; + top_frame.is_polymorphic = true; + Ok(()) + } + + /// Push control frame into the control stack. + fn push_frame(&mut self, frame: Frame) { + self.control_stack.push(frame); + } + + /// Pop control frame from the control stack. + /// + /// Returns `Err` if the control stack is empty. + fn pop_frame(&mut self) -> Result { + self.control_stack.pop().ok_or_else(|| Error("stack must be non-empty".into())) + } + + /// Truncate the height of value stack to the specified height. + fn trunc(&mut self, new_height: u32) { + self.height = new_height; + } + + /// Push specified number of values into the value stack. + /// + /// Returns `Err` if the height overflow usize value. + fn push_values(&mut self, value_count: u32) -> Result<(), Error> { + self.height = + self.height.checked_add(value_count).ok_or_else(|| Error("stack overflow".into()))?; + Ok(()) + } + + /// Pop specified number of values from the value stack. + /// + /// Returns `Err` if the stack happen to be negative value after + /// values popped. + fn pop_values(&mut self, value_count: u32) -> Result<(), Error> { + if value_count == 0 { + return Ok(()); + } + { + let top_frame = self.frame(0)?; + if self.height == top_frame.start_height { + // It is an error to pop more values than was pushed in the current frame + // (ie pop values pushed in the parent frame), unless the frame became + // polymorphic. + return if top_frame.is_polymorphic { + Ok(()) + } else { + Err(Error("trying to pop more values than pushed".into())) + }; + } + } + + self.height = + self.height.checked_sub(value_count).ok_or_else(|| Error("stack underflow".into()))?; + + Ok(()) + } +} + +/// This function expects the function to be validated. +pub(crate) fn compute(func_idx: u32, module_ctx: &ModuleCtx<'_>) -> Result { + use parity_wasm::elements::Instruction::*; + + let module = module_ctx.module; + + let func_section = + module.function_section().ok_or_else(|| Error("No function section".into()))?; + let code_section = module.code_section().ok_or_else(|| Error("No code section".into()))?; + let type_section = module.type_section().ok_or_else(|| Error("No type section".into()))?; + + // Get a signature and a body of the specified function. + let func_sig_idx = func_section + .entries() + .get(func_idx as usize) + .ok_or_else(|| Error("Function is not found in func section".into()))? + .type_ref(); + let Type::Function(func_signature) = type_section + .types() + .get(func_sig_idx as usize) + .ok_or_else(|| Error("Function is not found in func section".into()))?; + let body = code_section + .bodies() + .get(func_idx as usize) + .ok_or_else(|| Error("Function body for the index isn't found".into()))?; + let instructions = body.code(); + + let mut stack = Stack::new(); + let mut max_height: u32 = 0; + let mut pc = 0; + + // Add implicit frame for the function. Breaks to this frame and execution of + // the last end should deal with this frame. + let func_arity = func_signature.results().len() as u32; + stack.push_frame(Frame { + is_polymorphic: false, + end_arity: func_arity, + branch_arity: func_arity, + start_height: 0, + }); + + loop { + if pc >= instructions.elements().len() { + break; + } + + // If current value stack is higher than maximal height observed so far, + // save the new height. + // However, we don't increase maximal value in unreachable code. + if stack.height() > max_height && !stack.frame(0)?.is_polymorphic { + max_height = stack.height(); + } + + let opcode = &instructions.elements()[pc]; + + match opcode { + Nop => {} + Block(ty) | Loop(ty) | If(ty) => { + let end_arity = if *ty == BlockType::NoResult { 0 } else { 1 }; + let branch_arity = if let Loop(_) = *opcode { 0 } else { end_arity }; + if let If(_) = *opcode { + stack.pop_values(1)?; + } + let height = stack.height(); + stack.push_frame(Frame { + is_polymorphic: false, + end_arity, + branch_arity, + start_height: height, + }); + } + Else => { + // The frame at the top should be pushed by `If`. So we leave + // it as is. + } + End => { + let frame = stack.pop_frame()?; + stack.trunc(frame.start_height); + stack.push_values(frame.end_arity)?; + } + Unreachable => { + stack.mark_unreachable()?; + } + Br(target) => { + // Pop values for the destination block result. + let target_arity = stack.frame(*target)?.branch_arity; + stack.pop_values(target_arity)?; + + // This instruction unconditionally transfers control to the specified block, + // thus all instruction until the end of the current block is deemed unreachable + stack.mark_unreachable()?; + } + BrIf(target) => { + // Pop values for the destination block result. + let target_arity = stack.frame(*target)?.branch_arity; + stack.pop_values(target_arity)?; + + // Pop condition value. + stack.pop_values(1)?; + + // Push values back. + stack.push_values(target_arity)?; + } + BrTable(br_table_data) => { + let arity_of_default = stack.frame(br_table_data.default)?.branch_arity; + + // Check that all jump targets have an equal arities. + for target in &*br_table_data.table { + let arity = stack.frame(*target)?.branch_arity; + if arity != arity_of_default { + return Err(Error("Arity of all jump-targets must be equal".into())); + } + } + + // Because all jump targets have an equal arities, we can just take arity of + // the default branch. + stack.pop_values(arity_of_default)?; + + // This instruction doesn't let control flow to go further, since the control flow + // should take either one of branches depending on the value or the default branch. + stack.mark_unreachable()?; + } + Return => { + // Pop return values of the function. Mark successive instructions as unreachable + // since this instruction doesn't let control flow to go further. + stack.pop_values(func_arity)?; + stack.mark_unreachable()?; + } + Call(idx) => { + let ty = module_ctx.resolve_func_type(*idx)?; + + // Pop values for arguments of the function. + stack.pop_values(ty.params().len() as u32)?; + + // Push result of the function execution to the stack. + let callee_arity = ty.results().len() as u32; + stack.push_values(callee_arity)?; + } + CallIndirect(x, _) => { + let Type::Function(ty) = type_section + .types() + .get(*x as usize) + .ok_or_else(|| Error("Type not found".into()))?; + + // Pop the offset into the function table. + stack.pop_values(1)?; + + // Pop values for arguments of the function. + stack.pop_values(ty.params().len() as u32)?; + + // Push result of the function execution to the stack. + let callee_arity = ty.results().len() as u32; + stack.push_values(callee_arity)?; + } + Drop => { + stack.pop_values(1)?; + } + Select => { + // Pop two values and one condition. + stack.pop_values(2)?; + stack.pop_values(1)?; + + // Push the selected value. + stack.push_values(1)?; + } + GetLocal(_) => { + stack.push_values(1)?; + } + SetLocal(_) => { + stack.pop_values(1)?; + } + TeeLocal(_) => { + // This instruction pops and pushes the value, so + // effectively it doesn't modify the stack height. + stack.pop_values(1)?; + stack.push_values(1)?; + } + GetGlobal(_) => { + stack.push_values(1)?; + } + SetGlobal(_) => { + stack.pop_values(1)?; + } + I32Load(_, _) + | I64Load(_, _) + | F32Load(_, _) + | F64Load(_, _) + | I32Load8S(_, _) + | I32Load8U(_, _) + | I32Load16S(_, _) + | I32Load16U(_, _) + | I64Load8S(_, _) + | I64Load8U(_, _) + | I64Load16S(_, _) + | I64Load16U(_, _) + | I64Load32S(_, _) + | I64Load32U(_, _) => { + // These instructions pop the address and pushes the result, + // which effictively don't modify the stack height. + stack.pop_values(1)?; + stack.push_values(1)?; + } + + I32Store(_, _) + | I64Store(_, _) + | F32Store(_, _) + | F64Store(_, _) + | I32Store8(_, _) + | I32Store16(_, _) + | I64Store8(_, _) + | I64Store16(_, _) + | I64Store32(_, _) => { + // These instructions pop the address and the value. + stack.pop_values(2)?; + } + + CurrentMemory(_) => { + // Pushes current memory size + stack.push_values(1)?; + } + GrowMemory(_) => { + // Grow memory takes the value of pages to grow and pushes + stack.pop_values(1)?; + stack.push_values(1)?; + } + + I32Const(_) | I64Const(_) | F32Const(_) | F64Const(_) => { + // These instructions just push the single literal value onto the stack. + stack.push_values(1)?; + } + + I32Eqz | I64Eqz => { + // These instructions pop the value and compare it against zero, and pushes + // the result of the comparison. + stack.pop_values(1)?; + stack.push_values(1)?; + } + + I32Eq | I32Ne | I32LtS | I32LtU | I32GtS | I32GtU | I32LeS | I32LeU | I32GeS + | I32GeU | I64Eq | I64Ne | I64LtS | I64LtU | I64GtS | I64GtU | I64LeS | I64LeU + | I64GeS | I64GeU | F32Eq | F32Ne | F32Lt | F32Gt | F32Le | F32Ge | F64Eq | F64Ne + | F64Lt | F64Gt | F64Le | F64Ge => { + // Comparison operations take two operands and produce one result. + stack.pop_values(2)?; + stack.push_values(1)?; + } + + I32Clz | I32Ctz | I32Popcnt | I64Clz | I64Ctz | I64Popcnt | F32Abs | F32Neg + | F32Ceil | F32Floor | F32Trunc | F32Nearest | F32Sqrt | F64Abs | F64Neg | F64Ceil + | F64Floor | F64Trunc | F64Nearest | F64Sqrt => { + // Unary operators take one operand and produce one result. + stack.pop_values(1)?; + stack.push_values(1)?; + } + + I32Add | I32Sub | I32Mul | I32DivS | I32DivU | I32RemS | I32RemU | I32And | I32Or + | I32Xor | I32Shl | I32ShrS | I32ShrU | I32Rotl | I32Rotr | I64Add | I64Sub + | I64Mul | I64DivS | I64DivU | I64RemS | I64RemU | I64And | I64Or | I64Xor | I64Shl + | I64ShrS | I64ShrU | I64Rotl | I64Rotr | F32Add | F32Sub | F32Mul | F32Div + | F32Min | F32Max | F32Copysign | F64Add | F64Sub | F64Mul | F64Div | F64Min + | F64Max | F64Copysign => { + // Binary operators take two operands and produce one result. + stack.pop_values(2)?; + stack.push_values(1)?; + } + + I32WrapI64 | I32TruncSF32 | I32TruncUF32 | I32TruncSF64 | I32TruncUF64 + | I64ExtendSI32 | I64ExtendUI32 | I64TruncSF32 | I64TruncUF32 | I64TruncSF64 + | I64TruncUF64 | F32ConvertSI32 | F32ConvertUI32 | F32ConvertSI64 | F32ConvertUI64 + | F32DemoteF64 | F64ConvertSI32 | F64ConvertUI32 | F64ConvertSI64 | F64ConvertUI64 + | F64PromoteF32 | I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32 + | F64ReinterpretI64 => { + // Conversion operators take one value and produce one result. + stack.pop_values(1)?; + stack.push_values(1)?; + } + } + pc += 1; + } + + Ok(max_height) +} + +#[cfg(test)] +mod tests { + use super::*; + use parity_wasm::elements; + + fn parse_wat(source: &str) -> elements::Module { + let wasm = wat::parse_str(source).expect("Failed to wat2wasm"); + elements::deserialize_buffer(&wasm).expect("Failed to deserialize the module") + } + + #[test] + fn simple_test() { + let module = parse_wat( + r#" +(module + (func + i32.const 1 + i32.const 2 + i32.const 3 + drop + drop + drop + ) +) +"#, + ); + + let module_ctx = ModuleCtx::new(&module); + let height = compute(0, &module_ctx).unwrap(); + assert_eq!(height, 3); + } + + #[test] + fn implicit_and_explicit_return() { + let module = parse_wat( + r#" +(module + (func (result i32) + i32.const 0 + return + ) +) +"#, + ); + + let module_ctx = ModuleCtx::new(&module); + let height = compute(0, &module_ctx).unwrap(); + assert_eq!(height, 1); + } + + #[test] + fn dont_count_in_unreachable() { + let module = parse_wat( + r#" +(module + (memory 0) + (func (result i32) + unreachable + grow_memory + ) +) +"#, + ); + + let module_ctx = ModuleCtx::new(&module); + let height = compute(0, &module_ctx).unwrap(); + assert_eq!(height, 0); + } + + #[test] + fn yet_another_test() { + const SOURCE: &str = r#" +(module + (memory 0) + (func + ;; Push two values and then pop them. + ;; This will make max depth to be equal to 2. + i32.const 0 + i32.const 1 + drop + drop + + ;; Code after `unreachable` shouldn't have an effect + ;; on the max depth. + unreachable + i32.const 0 + i32.const 1 + i32.const 2 + ) +) +"#; + let module = parse_wat(SOURCE); + + let module_ctx = ModuleCtx::new(&module); + let height = compute(0, &module_ctx).unwrap(); + assert_eq!(height, 2); + } + + #[test] + fn call_indirect() { + let module = parse_wat( + r#" +(module + (table $ptr 1 1 funcref) + (elem $ptr (i32.const 0) func 1) + (func $main + (call_indirect (i32.const 0)) + (call_indirect (i32.const 0)) + (call_indirect (i32.const 0)) + ) + (func $callee + i64.const 42 + drop + ) +) +"#, + ); + + let module_ctx = ModuleCtx::new(&module); + let height = compute(0, &module_ctx).unwrap(); + assert_eq!(height, 1); + } + + #[test] + fn breaks() { + let module = parse_wat( + r#" +(module + (func $main + block (result i32) + block (result i32) + i32.const 99 + br 1 + end + end + drop + ) +) +"#, + ); + + let module_ctx = ModuleCtx::new(&module); + let height = compute(0, &module_ctx).unwrap(); + assert_eq!(height, 1); + } + + #[test] + fn if_else_works() { + let module = parse_wat( + r#" +(module + (func $main + i32.const 7 + i32.const 1 + if (result i32) + i32.const 42 + else + i32.const 99 + end + i32.const 97 + drop + drop + drop + ) +) +"#, + ); + + let module_ctx = ModuleCtx::new(&module); + let height = compute(0, &module_ctx).unwrap(); + assert_eq!(height, 3); + } +} diff --git a/runtime/unc-vm-runner/src/instrument/stack_height/mod.rs b/runtime/unc-vm-runner/src/instrument/stack_height/mod.rs new file mode 100644 index 000000000..434a33b26 --- /dev/null +++ b/runtime/unc-vm-runner/src/instrument/stack_height/mod.rs @@ -0,0 +1,392 @@ +//! The pass that tries to make stack overflows deterministic, by introducing +//! an upper bound of the stack size. +//! +//! This pass introduces a global mutable variable to track stack height, +//! and instruments all calls with preamble and postamble. +//! +//! Stack height is increased prior the call. Otherwise, the check would +//! be made after the stack frame is allocated. +//! +//! The preamble is inserted before the call. It increments +//! the global stack height variable with statically determined "stack cost" +//! of the callee. If after the increment the stack height exceeds +//! the limit (specified by the `rules`) then execution traps. +//! Otherwise, the call is executed. +//! +//! The postamble is inserted after the call. The purpose of the postamble is to decrease +//! the stack height by the "stack cost" of the callee function. +//! +//! Note, that we can't instrument all possible ways to return from the function. The simplest +//! example would be a trap issued by the host function. +//! That means stack height global won't be equal to zero upon the next execution after such trap. +//! +//! # Thunks +//! +//! Because stack height is increased prior the call few problems arises: +//! +//! - Stack height isn't increased upon an entry to the first function, i.e. exported function. +//! - Start function is executed externally (similar to exported functions). +//! - It is statically unknown what function will be invoked in an indirect call. +//! +//! The solution for this problems is to generate a intermediate functions, called 'thunks', which +//! will increase before and decrease the stack height after the call to original function, and +//! then make exported function and table entries, start section to point to a corresponding thunks. +//! +//! # Stack cost +//! +//! Stack cost of the function is calculated as a sum of it's locals +//! and the maximal height of the value stack. +//! +//! All values are treated equally, as they have the same size. +//! +//! The rationale is that this makes it possible to use the following very naive wasm executor: +//! +//! - values are implemented by a union, so each value takes a size equal to +//! the size of the largest possible value type this union can hold. (In MVP it is 8 bytes) +//! - each value from the value stack is placed on the native stack. +//! - each local variable and function argument is placed on the native stack. +//! - arguments pushed by the caller are copied into callee stack rather than shared +//! between the frames. +//! - upon entry into the function entire stack frame is allocated. + +use parity_wasm::{ + builder, + elements::{self, Instruction, Instructions, Type}, +}; +use std::mem; + +/// Macro to generate preamble and postamble. +macro_rules! instrument_call { + ($callee_idx: expr, $callee_stack_cost: expr, $stack_height_global_idx: expr, $stack_limit: expr) => {{ + use parity_wasm::elements::Instruction::*; + [ + // stack_height += stack_cost(F) + GetGlobal($stack_height_global_idx), + I32Const($callee_stack_cost), + I32Add, + SetGlobal($stack_height_global_idx), + // if stack_counter > LIMIT: unreachable + GetGlobal($stack_height_global_idx), + I32Const($stack_limit as i32), + I32GtU, + If(elements::BlockType::NoResult), + Unreachable, + End, + // Original call + Call($callee_idx), + // stack_height -= stack_cost(F) + GetGlobal($stack_height_global_idx), + I32Const($callee_stack_cost), + I32Sub, + SetGlobal($stack_height_global_idx), + ] + }}; +} + +mod max_height; +mod thunk; + +/// Error that occured during processing the module. +/// +/// This means that the module is invalid. +#[derive(Debug)] +pub struct Error(String); + +pub(crate) struct Context { + stack_height_global_idx: u32, + func_stack_costs: Vec, + stack_limit: u32, +} + +impl Context { + /// Returns index in a global index space of a stack_height global variable. + fn stack_height_global_idx(&self) -> u32 { + self.stack_height_global_idx + } + + /// Returns `stack_cost` for `func_idx`. + fn stack_cost(&self, func_idx: u32) -> Option { + self.func_stack_costs.get(func_idx as usize).cloned() + } + + /// Returns stack limit specified by the rules. + fn stack_limit(&self) -> u32 { + self.stack_limit + } +} + +/// Instrument a module with stack height limiter. +/// +/// See module-level documentation for more details. +/// +/// # Errors +/// +/// Returns `Err` if module is invalid and can't be +pub fn inject_limiter( + mut module: elements::Module, + stack_limit: u32, +) -> Result { + let mut ctx = Context { + stack_height_global_idx: generate_stack_height_global(&mut module), + func_stack_costs: compute_stack_costs(&module)?, + stack_limit, + }; + + instrument_functions(&ctx, &mut module)?; + let module = thunk::generate_thunks(&mut ctx, module)?; + + Ok(module) +} + +/// Generate a new global that will be used for tracking current stack height. +fn generate_stack_height_global(module: &mut elements::Module) -> u32 { + let global_entry = + builder::global().value_type().i32().mutable().init_expr(Instruction::I32Const(0)).build(); + + // Try to find an existing global section. + for section in module.sections_mut() { + if let elements::Section::Global(gs) = section { + gs.entries_mut().push(global_entry); + return (gs.entries().len() as u32) - 1; + } + } + + // Existing section not found, create one! + module + .sections_mut() + .push(elements::Section::Global(elements::GlobalSection::with_entries(vec![global_entry]))); + 0 +} + +pub(crate) struct ModuleCtx<'a> { + module: &'a elements::Module, + func_imports: usize, + func_idx_to_sig_idx: Vec, +} + +impl<'a> ModuleCtx<'a> { + fn new(module: &'a elements::Module) -> Self { + let func_imports = module.import_count(elements::ImportCountType::Function); + let func_idx_to_sig_idx: Vec = { + let imported = + module.import_section().map(|is| is.entries()).unwrap_or(&[]).iter().filter_map( + |entry| match entry.external() { + elements::External::Function(idx) => Some(*idx), + _ => None, + }, + ); + let declared = module + .function_section() + .map(|fs| fs.entries()) + .unwrap_or(&[]) + .iter() + .map(|func| func.type_ref()); + imported.chain(declared).collect() + }; + Self { module, func_imports, func_idx_to_sig_idx } + } + + fn resolve_func_type(&self, func_idx: u32) -> Result<&'a elements::FunctionType, Error> { + let types = self.module.type_section().map(|ts| ts.types()).unwrap_or(&[]); + + let sig_idx = *self + .func_idx_to_sig_idx + .get(func_idx as usize) + .ok_or_else(|| Error(format!("Function at index {} is not defined", func_idx)))?; + let Type::Function(ty) = types.get(sig_idx as usize).ok_or_else(|| { + Error(format!("Signature {} (specified by func {}) isn't defined", sig_idx, func_idx)) + })?; + Ok(ty) + } +} + +/// Calculate stack costs for all functions. +/// +/// Returns a vector with a stack cost for each function, including imports. +fn compute_stack_costs(module: &elements::Module) -> Result, Error> { + let module_ctx = ModuleCtx::new(module); + + // TODO: optimize! + (0..module.functions_space()) + .map(|func_idx| { + if func_idx < module_ctx.func_imports { + // We can't calculate stack_cost of the import functions. + Ok(0) + } else { + compute_stack_cost(func_idx as u32, &module_ctx) + } + }) + .collect() +} + +/// Stack cost of the given *defined* function is the sum of it's locals count (that is, +/// number of arguments plus number of local variables) and the maximal stack +/// height. +fn compute_stack_cost(func_idx: u32, module_ctx: &ModuleCtx<'_>) -> Result { + // To calculate the cost of a function we need to convert index from + // function index space to defined function spaces. + let defined_func_idx = func_idx + .checked_sub(module_ctx.func_imports as u32) + .ok_or_else(|| Error("This should be a index of a defined function".into()))?; + + let code_section = module_ctx + .module + .code_section() + .ok_or_else(|| Error("Due to validation code section should exists".into()))?; + let body = &code_section + .bodies() + .get(defined_func_idx as usize) + .ok_or_else(|| Error("Function body is out of bounds".into()))?; + + let mut locals_count: u32 = 0; + for local_group in body.locals() { + locals_count = locals_count + .checked_add(local_group.count()) + .ok_or_else(|| Error("Overflow in local count".into()))?; + } + + let max_stack_height = max_height::compute(defined_func_idx, module_ctx)?; + + locals_count + .checked_add(max_stack_height) + .ok_or_else(|| Error("Overflow in adding locals_count and max_stack_height".into())) +} + +fn instrument_functions(ctx: &Context, module: &mut elements::Module) -> Result<(), Error> { + for section in module.sections_mut() { + if let elements::Section::Code(code_section) = section { + for func_body in code_section.bodies_mut() { + let opcodes = func_body.code_mut(); + instrument_function(ctx, opcodes)?; + } + } + } + Ok(()) +} + +/// This function searches `call` instructions and wrap each call +/// with preamble and postamble. +/// +/// Before: +/// +/// ```text +/// get_local 0 +/// get_local 1 +/// call 228 +/// drop +/// ``` +/// +/// After: +/// +/// ```text +/// get_local 0 +/// get_local 1 +/// +/// < ... preamble ... > +/// +/// call 228 +/// +/// < .. postamble ... > +/// +/// drop +/// ``` +fn instrument_function(ctx: &Context, func: &mut Instructions) -> Result<(), Error> { + use Instruction::*; + + struct InstrumentCall { + offset: usize, + callee: u32, + cost: u32, + } + + let calls: Vec<_> = func + .elements() + .iter() + .enumerate() + .filter_map(|(offset, instruction)| { + if let Call(callee) = instruction { + ctx.stack_cost(*callee).and_then(|cost| { + if cost > 0 { + Some(InstrumentCall { callee: *callee, offset, cost }) + } else { + None + } + }) + } else { + None + } + }) + .collect(); + + // The `instrumented_call!` contains the call itself. This is why we need to subtract one. + let len = func.elements().len() + calls.len() * (instrument_call!(0, 0, 0, 0).len() - 1); + let original_instrs = mem::replace(func.elements_mut(), Vec::with_capacity(len)); + let new_instrs = func.elements_mut(); + + let mut calls = calls.into_iter().peekable(); + for (original_pos, instr) in original_instrs.into_iter().enumerate() { + // whether there is some call instruction at this position that needs to be instrumented + let did_instrument = if let Some(call) = calls.peek() { + if call.offset == original_pos { + let new_seq = instrument_call!( + call.callee, + call.cost as i32, + ctx.stack_height_global_idx(), + ctx.stack_limit() + ); + new_instrs.extend(new_seq); + true + } else { + false + } + } else { + false + }; + + if did_instrument { + calls.next(); + } else { + new_instrs.push(instr); + } + } + + if calls.next().is_some() { + return Err(Error("Not all calls were used".into())); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use parity_wasm::elements; + + fn parse_wat(source: &str) -> elements::Module { + let wasm = wat::parse_str(source).expect("Failed to wat2wasm"); + elements::deserialize_buffer(&wasm).expect("Failed to deserialize the module") + } + + fn validate_module(module: elements::Module) { + let binary = elements::serialize(module).expect("Failed to serialize"); + wasmparser::validate(&binary).expect("Wabt failed to read final binary"); + } + + #[test] + fn test_with_params_and_result() { + let module = parse_wat( + r#" +(module + (func (export "i32.add") (param i32 i32) (result i32) + get_local 0 + get_local 1 + i32.add + ) +) +"#, + ); + + let module = inject_limiter(module, 1024).expect("Failed to inject stack counter"); + validate_module(module); + } +} diff --git a/runtime/unc-vm-runner/src/instrument/stack_height/thunk.rs b/runtime/unc-vm-runner/src/instrument/stack_height/thunk.rs new file mode 100644 index 000000000..a0d56c200 --- /dev/null +++ b/runtime/unc-vm-runner/src/instrument/stack_height/thunk.rs @@ -0,0 +1,140 @@ +use super::{Context, Error, ModuleCtx}; +use parity_wasm::{ + builder, + elements::{self, FunctionType, Internal}, +}; +use std::collections::BTreeMap; + +struct Thunk { + signature: FunctionType, + // Index in function space of this thunk. + idx: Option, + callee_stack_cost: u32, +} + +pub(crate) fn generate_thunks( + ctx: &Context, + module: elements::Module, +) -> Result { + let module_ctx = ModuleCtx::new(&module); + + // First, we need to collect all function indices that should be replaced by thunks + + let mut replacement_map: BTreeMap = { + let exports = module.export_section().map(|es| es.entries()).unwrap_or(&[]); + let elem_segments = module.elements_section().map(|es| es.entries()).unwrap_or(&[]); + let start_func_idx = module.start_section(); + + let exported_func_indices = exports.iter().filter_map(|entry| match entry.internal() { + Internal::Function(function_idx) => Some(*function_idx), + _ => None, + }); + let table_func_indices = + elem_segments.iter().flat_map(|segment| segment.members()).cloned(); + + // Replacement map is at least export section size. + let mut replacement_map: BTreeMap = BTreeMap::new(); + + for func_idx in + exported_func_indices.chain(table_func_indices).chain(start_func_idx.into_iter()) + { + let callee_stack_cost = ctx + .stack_cost(func_idx) + .ok_or_else(|| Error(format!("function with idx {} isn't found", func_idx)))?; + + // Don't generate a thunk if stack_cost of a callee is zero. + if callee_stack_cost != 0 { + replacement_map.insert( + func_idx, + Thunk { + signature: module_ctx.resolve_func_type(func_idx)?.clone(), + idx: None, + callee_stack_cost, + }, + ); + } + } + + replacement_map + }; + + // Then, we generate a thunk for each original function. + + // Save current func_idx + let mut next_func_idx = module.functions_space() as u32; + + let mut mbuilder = builder::from_module(module); + for (func_idx, thunk) in replacement_map.iter_mut() { + let instrumented_call = instrument_call!( + *func_idx, + thunk.callee_stack_cost as i32, + ctx.stack_height_global_idx(), + ctx.stack_limit() + ); + // Thunk body consist of: + // - argument pushing + // - instrumented call + // - end + let mut thunk_body: Vec = + Vec::with_capacity(thunk.signature.params().len() + instrumented_call.len() + 1); + + for (arg_idx, _) in thunk.signature.params().iter().enumerate() { + thunk_body.push(elements::Instruction::GetLocal(arg_idx as u32)); + } + thunk_body.extend(instrumented_call.iter().cloned()); + thunk_body.push(elements::Instruction::End); + + // TODO: Don't generate a signature, but find an existing one. + + mbuilder = mbuilder + .function() + // Signature of the thunk should match the original function signature. + .signature() + .with_params(thunk.signature.params().to_vec()) + .with_results(thunk.signature.results().to_vec()) + .build() + .body() + .with_instructions(elements::Instructions::new(thunk_body)) + .build() + .build(); + + thunk.idx = Some(next_func_idx); + next_func_idx += 1; + } + let mut module = mbuilder.build(); + + // And finally, fixup thunks in export and table sections. + + // Fixup original function index to a index of a thunk generated earlier. + let fixup = |function_idx: &mut u32| { + // Check whether this function is in replacement_map, since + // we can skip thunk generation (e.g. if stack_cost of function is 0). + if let Some(thunk) = replacement_map.get(function_idx) { + *function_idx = + thunk.idx.expect("At this point an index must be assigned to each thunk"); + } + }; + + for section in module.sections_mut() { + match section { + elements::Section::Export(export_section) => { + for entry in export_section.entries_mut() { + if let Internal::Function(function_idx) = entry.internal_mut() { + fixup(function_idx) + } + } + } + elements::Section::Element(elem_section) => { + for segment in elem_section.entries_mut() { + for function_idx in segment.members_mut() { + fixup(function_idx) + } + } + } + elements::Section::Start(start_idx) => fixup(start_idx), + _ => {} + } + } + + Ok(module) +} diff --git a/runtime/unc-vm-runner/src/lib.rs b/runtime/unc-vm-runner/src/lib.rs new file mode 100644 index 000000000..3b344d0af --- /dev/null +++ b/runtime/unc-vm-runner/src/lib.rs @@ -0,0 +1,40 @@ +#![doc = include_str!("../README.md")] + +mod cache; +mod code; +mod errors; +mod features; +mod imports; +mod instrument; +pub mod logic; +#[cfg(all(feature = "wasmer0_vm", target_arch = "x86_64"))] +mod memory; +#[cfg(all(feature = "unc_vm", target_arch = "x86_64"))] +mod unc_vm_runner; +pub mod prepare; +mod profile; +mod runner; +#[cfg(test)] +mod tests; +mod utils; +#[cfg(all(feature = "wasmer2_vm", target_arch = "x86_64"))] +mod wasmer2_runner; +#[cfg(all(feature = "wasmer0_vm", target_arch = "x86_64"))] +mod wasmer_runner; +#[cfg(feature = "wasmtime_vm")] +mod wasmtime_runner; + +pub use crate::logic::with_ext_cost_counter; +pub use cache::{get_contract_cache_key, precompile_contract, MockCompiledContractCache}; +pub use code::ContractCode; +pub use profile::ProfileDataV2; +pub use profile::ProfileDataV3; +pub use runner::{run, VM}; + +/// This is public for internal experimentation use only, and should otherwise be considered an +/// implementation detail of `unc-vm-runner`. +#[doc(hidden)] +pub mod internal { + pub use crate::runner::VMKindExt; + pub use wasmparser; +} diff --git a/runtime/unc-vm-runner/src/logic/alt_bn128.rs b/runtime/unc-vm-runner/src/logic/alt_bn128.rs new file mode 100644 index 000000000..bcdc46620 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/alt_bn128.rs @@ -0,0 +1,169 @@ +use super::{HostError, VMLogicError}; +use bn::Group; + +const BOOL_SIZE: usize = 1; +const SCALAR_SIZE: usize = 256 / 8; +const POINT_SIZE: usize = SCALAR_SIZE * 2; + +pub(super) struct InvalidInput { + pub(super) msg: String, +} + +impl InvalidInput { + fn new(msg: &str, bad_value: &[u8]) -> InvalidInput { + let msg = format!("{msg}: {bad_value:X?}"); + InvalidInput { msg } + } +} + +impl From for VMLogicError { + fn from(err: InvalidInput) -> Self { + HostError::AltBn128InvalidInput { msg: err.msg }.into() + } +} + +pub(super) fn split_elements( + data: &[u8], +) -> Result<&[[u8; ELEMENT_SIZE]], InvalidInput> { + stdx::as_chunks_exact(data).map_err(|e| InvalidInput { msg: e.to_string() }) +} + +const G1_MULTIEXP_ELEMENT_SIZE: usize = POINT_SIZE + SCALAR_SIZE; + +pub(super) fn g1_multiexp( + elements: &[[u8; G1_MULTIEXP_ELEMENT_SIZE]], +) -> Result<[u8; POINT_SIZE], InvalidInput> { + let elements: Vec<(bn::G1, bn::Fr)> = elements + .iter() + .map(|chunk| { + let (g1, fr) = stdx::split_array(chunk); + let g1 = decode_g1(g1)?; + let fr = decode_fr(fr)?; + Ok((g1, fr)) + }) + .collect::, InvalidInput>>()?; + + let res = bn::G1::multiexp(&elements); + + Ok(encode_g1(res)) +} + +const G1_SUM_ELEMENT_SIZE: usize = BOOL_SIZE + POINT_SIZE; + +pub(super) fn g1_sum( + elements: &[[u8; G1_SUM_ELEMENT_SIZE]], +) -> Result<[u8; POINT_SIZE], InvalidInput> { + let elements: Vec<(bool, bn::G1)> = { + elements + .iter() + .map(|chunk| { + let (sign, g1) = stdx::split_array(chunk); + let sign = decode_bool(sign)?; + let g1 = decode_g1(g1)?; + Ok((sign, g1)) + }) + .collect::, InvalidInput>>()? + }; + + let res = elements + .iter() + .fold(bn::G1::zero(), |acc, &(sign, x)| if sign { acc - x } else { acc + x }); + + Ok(encode_g1(res)) +} + +const PAIRING_CHECK_ELEMENT_SIZE: usize = POINT_SIZE + POINT_SIZE * 2; + +pub(super) fn pairing_check( + elements: &[[u8; PAIRING_CHECK_ELEMENT_SIZE]], +) -> Result { + let elements: Vec<(bn::G1, bn::G2)> = elements + .iter() + .map(|chunk| { + let (g1, g2) = stdx::split_array(chunk); + let g1 = decode_g1(g1)?; + let g2 = decode_g2(g2)?; + Ok((g1, g2)) + }) + .collect::, InvalidInput>>()?; + + let res = bn::pairing_batch(&elements) == bn::Gt::one(); + + Ok(res) +} + +fn encode_g1(val: bn::G1) -> [u8; POINT_SIZE] { + let (x, y) = bn::AffineG1::from_jacobian(val) + .map(|p| (p.x(), p.y())) + .unwrap_or_else(|| (bn::Fq::zero(), bn::Fq::zero())); + let x = encode_fq(x); + let y = encode_fq(y); + stdx::join_array(x, y) +} + +fn encode_fq(val: bn::Fq) -> [u8; SCALAR_SIZE] { + encode_u256(val.into_u256()) +} + +fn encode_u256(val: bn::arith::U256) -> [u8; SCALAR_SIZE] { + let [lo, hi] = val.0; + stdx::join_array(lo.to_le_bytes(), hi.to_le_bytes()) +} + +fn decode_g1(raw: &[u8; POINT_SIZE]) -> Result { + let (x, y) = stdx::split_array(raw); + let x = decode_fq(x)?; + let y = decode_fq(y)?; + if x.is_zero() && y.is_zero() { + Ok(bn::G1::zero()) + } else { + bn::AffineG1::new(x, y) + .map_err(|_err| InvalidInput::new("invalid g1", raw)) + .map(bn::G1::from) + } +} + +fn decode_fq(raw: &[u8; SCALAR_SIZE]) -> Result { + let val = decode_u256(raw); + bn::Fq::from_u256(val).map_err(|_| InvalidInput::new("invalid fq", raw)) +} + +fn decode_g2(raw: &[u8; 2 * POINT_SIZE]) -> Result { + let (x, y) = stdx::split_array(raw); + let x = decode_fq2(x)?; + let y = decode_fq2(y)?; + if x.is_zero() && y.is_zero() { + Ok(bn::G2::zero()) + } else { + bn::AffineG2::new(x, y) + .map_err(|_err| InvalidInput::new("invalid g2", raw)) + .map(bn::G2::from) + } +} + +fn decode_fq2(raw: &[u8; 2 * SCALAR_SIZE]) -> Result { + let (real, imaginary) = stdx::split_array(raw); + let real = decode_fq(real)?; + let imaginary = decode_fq(imaginary)?; + Ok(bn::Fq2::new(real, imaginary)) +} + +fn decode_fr(raw: &[u8; SCALAR_SIZE]) -> Result { + let val = decode_u256(raw); + bn::Fr::new(val).ok_or_else(|| InvalidInput::new("invalid fr", raw)) +} + +fn decode_u256(raw: &[u8; SCALAR_SIZE]) -> bn::arith::U256 { + let (lo, hi) = stdx::split_array(raw); + let lo = u128::from_le_bytes(*lo); + let hi = u128::from_le_bytes(*hi); + bn::arith::U256([lo, hi]) +} + +fn decode_bool(raw: &[u8; BOOL_SIZE]) -> Result { + match raw { + [0] => Ok(false), + [1] => Ok(true), + _ => Err(InvalidInput::new("invalid bool", raw)), + } +} diff --git a/runtime/unc-vm-runner/src/logic/context.rs b/runtime/unc-vm-runner/src/logic/context.rs new file mode 100644 index 000000000..794dedb75 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/context.rs @@ -0,0 +1,60 @@ +use super::types::PublicKey; +use unc_primitives_core::config::ViewConfig; +use unc_primitives_core::types::{ + AccountId, Balance, BlockHeight, EpochHeight, Gas, StorageUsage, +}; + +#[derive(Clone)] +/// Context for the contract execution. +pub struct VMContext { + /// The account id of the current contract that we are executing. + pub current_account_id: AccountId, + /// The account id of that signed the original transaction that led to this + /// execution. + pub signer_account_id: AccountId, + /// The public key that was used to sign the original transaction that led to + /// this execution. + pub signer_account_pk: PublicKey, + /// If this execution is the result of cross-contract call or a callback then + /// predecessor is the account that called it. + /// If this execution is the result of direct execution of transaction then it + /// is equal to `signer_account_id`. + pub predecessor_account_id: AccountId, + /// The input to the contract call. + /// Encoded as base64 string to be able to pass input in borsh binary format. + pub input: Vec, + /// The current block height. + pub block_height: BlockHeight, + /// The current block timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC). + pub block_timestamp: u64, + /// The current epoch height. + pub epoch_height: EpochHeight, + + /// The balance attached to the given account. Excludes the `attached_deposit` that was + /// attached to the transaction. + pub account_balance: Balance, + /// The balance of locked tokens on the given account. + pub account_locked_balance: Balance, + /// The account's storage usage before the contract execution + pub storage_usage: StorageUsage, + /// The balance that was attached to the call that will be immediately deposited before the + /// contract execution starts. + pub attached_deposit: Balance, + /// The gas attached to the call that can be used to pay for the gas fees. + pub prepaid_gas: Gas, + /// Initial seed for randomness + pub random_seed: Vec, + /// If Some, it means that execution is made in a view mode and defines its configuration. + /// View mode means that only read-only operations are allowed. + /// See for more details. + pub view_config: Option, + /// How many `DataReceipt`'s should receive this execution result. This should be empty if + /// this function call is a part of a batch and it is not the last action. + pub output_data_receivers: Vec, +} + +impl VMContext { + pub fn is_view(&self) -> bool { + self.view_config.is_some() + } +} diff --git a/runtime/unc-vm-runner/src/logic/dependencies.rs b/runtime/unc-vm-runner/src/logic/dependencies.rs new file mode 100644 index 000000000..8d0262f08 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/dependencies.rs @@ -0,0 +1,444 @@ +//! External dependencies of the unc-vm-logic. + +use super::types::ReceiptIndex; +use super::TrieNodesCount; +use super::VMLogicError; +use unc_crypto::PublicKey; +use unc_parameters::vm::StorageGetMode; +use unc_primitives_core::hash::CryptoHash; +use unc_primitives_core::types::{Gas, Power}; +use unc_primitives_core::types::GasWeight; +use unc_primitives_core::types::Nonce; +use unc_primitives_core::types::{AccountId, Balance}; + +use std::borrow::Cow; + +/// Representation of the address slice of guest memory. +#[derive(Clone, Copy)] +pub struct MemSlice { + /// Offset within guest memory at which the slice starts. + pub ptr: u64, + /// Length of the slice. + pub len: u64, +} + +impl MemSlice { + #[inline] + pub fn len>(&self) -> Result { + T::try_from(self.len).map_err(|_| ()) + } + + #[inline] + pub fn end>(&self) -> Result { + T::try_from(self.ptr.checked_add(self.len).ok_or(())?).map_err(|_| ()) + } + + #[inline] + pub fn range>(&self) -> Result, ()> { + let end = self.end()?; + let start = T::try_from(self.ptr).map_err(|_| ())?; + Ok(start..end) + } +} + +/// An abstraction over the memory of the smart contract. +pub trait MemoryLike { + /// Returns success if the memory interval is completely inside smart + /// contract’s memory. + /// + /// You often don’t need to use this method since other methods will perform + /// the check, however it may be necessary to prevent potential denial of + /// service attacks. See [`Self::read_memory`] for description. + fn fits_memory(&self, slice: MemSlice) -> Result<(), ()>; + + /// Returns view of the content of the given memory interval. + /// + /// Not all implementations support borrowing the memory directly. In those + /// cases, the data is copied into a vector. + fn view_memory(&self, slice: MemSlice) -> Result, ()>; + + /// Reads the content of the given memory interval. + /// + /// Returns error if the memory interval isn’t completely inside the smart + /// contract memory. + /// + /// # Potential denial of service + /// + /// Note that improper use of this function may lead to denial of service + /// attacks. For example, consider the following function: + /// + /// ``` + /// # use unc_vm_runner::logic::{MemoryLike, MemSlice}; + /// + /// fn read_vec(mem: &dyn MemoryLike, slice: MemSlice) -> Result, ()> { + /// let mut vec = vec![0; slice.len()?]; + /// mem.read_memory(slice.ptr, &mut vec[..])?; + /// Ok(vec) + /// } + /// ``` + /// + /// If attacker controls length argument, it may cause attempt at allocation + /// of arbitrarily-large buffer and crash the program. In situations like + /// this, it’s necessary to use [`Self::fits_memory`] method to verify that + /// the length is valid. For example: + /// + /// ``` + /// # use unc_vm_runner::logic::{MemoryLike, MemSlice}; + /// + /// fn read_vec(mem: &dyn MemoryLike, slice: MemSlice) -> Result, ()> { + /// mem.fits_memory(slice)?; + /// let mut vec = vec![0; slice.len()?]; + /// mem.read_memory(slice.ptr, &mut vec[..])?; + /// Ok(vec) + /// } + /// ``` + fn read_memory(&self, offset: u64, buffer: &mut [u8]) -> Result<(), ()>; + + /// Writes the buffer into the smart contract memory. + /// + /// Returns error if the memory interval isn’t completely inside the smart + /// contract memory. + fn write_memory(&mut self, offset: u64, buffer: &[u8]) -> Result<(), ()>; +} + +pub type Result = ::std::result::Result; + +/// Logical pointer to a value in storage. +/// Allows getting value length before getting the value itself. This is needed so that runtime +/// can charge gas before accessing a potentially large value. +pub trait ValuePtr { + /// Returns the length of the value + fn len(&self) -> u32; + + /// Dereferences the pointer. + /// Takes a box because currently runtime code uses dynamic dispatch. + /// # Errors + /// StorageError if reading from storage fails + fn deref(&self) -> Result>; +} + +/// An external blockchain interface for the Runtime logic +pub trait External { + /// Write `value` to the `key` of the storage trie associated with the current account. + /// + /// # Example + /// + /// ``` + /// # use unc_vm_runner::logic::mocks::mock_external::MockedExternal; + /// # use unc_vm_runner::logic::External; + /// + /// # let mut external = MockedExternal::new(); + /// assert_eq!(external.storage_set(b"key42", b"value1337"), Ok(())); + /// // Should return an old value if the key exists + /// assert_eq!(external.storage_set(b"key42", b"new_value"), Ok(())); + /// ``` + fn storage_set(&mut self, key: &[u8], value: &[u8]) -> Result<()>; + + /// Read `key` from the storage trie associated with the current account. + /// + /// # Arguments + /// + /// * `key` - the key to read + /// + /// * `mode`- whether the lookup will be performed through flat storage or trie + /// # Errors + /// + /// This function could return [`unc_vm_runner::logic::VMRunnerError::ExternalError`]. + /// + /// # Example + /// ``` + /// # use unc_vm_runner::logic::mocks::mock_external::MockedExternal; + /// # use unc_vm_runner::logic::{External, ValuePtr}; + /// # use unc_parameters::vm::StorageGetMode; + /// + /// # let mut external = MockedExternal::new(); + /// external.storage_set(b"key42", b"value1337").unwrap(); + /// assert_eq!(external.storage_get(b"key42", StorageGetMode::Trie).unwrap().map(|ptr| ptr.deref().unwrap()), Some(b"value1337".to_vec())); + /// // Returns Ok(None) if there is no value for a key + /// assert_eq!(external.storage_get(b"no_key", StorageGetMode::Trie).unwrap().map(|ptr| ptr.deref().unwrap()), None); + /// ``` + fn storage_get<'a>( + &'a self, + key: &[u8], + mode: StorageGetMode, + ) -> Result>>; + + /// Removes the `key` from the storage trie associated with the current account. + /// + /// The operation will succeed even if the `key` does not exist. + /// + /// # Arguments + /// + /// * `key` - the key to remove + /// + /// # Example + /// ``` + /// # use unc_vm_runner::logic::mocks::mock_external::MockedExternal; + /// # use unc_vm_runner::logic::External; + /// + /// # let mut external = MockedExternal::new(); + /// external.storage_set(b"key42", b"value1337").unwrap(); + /// // Returns Ok if exists + /// assert_eq!(external.storage_remove(b"key42"), Ok(())); + /// // Returns Ok if there was no value + /// assert_eq!(external.storage_remove(b"no_value_key"), Ok(())); + /// ``` + fn storage_remove(&mut self, key: &[u8]) -> Result<()>; + + /// Note: The method is currently unused and untested. + /// + /// Removes all keys with a given `prefix` from the storage trie associated with current + /// account. + /// + /// # Arguments + /// + /// * `prefix` - a prefix for all keys to remove + /// + /// # Errors + /// + /// This function could return [`unc_vm_runner::logic::VMError`]. + /// + /// # Example + /// ``` + /// # use unc_vm_runner::logic::mocks::mock_external::MockedExternal; + /// # use unc_vm_runner::logic::{External, StorageGetMode}; + /// + /// # let mut external = MockedExternal::new(); + /// external.storage_set(b"key1", b"value1337").unwrap(); + /// external.storage_set(b"key2", b"value1337").unwrap(); + /// assert_eq!(external.storage_remove_subtree(b"key"), Ok(())); + /// assert!(!external.storage_has_key(b"key1", StorageGetMode::Trie).unwrap()); + /// assert!(!external.storage_has_key(b"key2", StorageGetMode::Trie).unwrap()); + /// ``` + fn storage_remove_subtree(&mut self, prefix: &[u8]) -> Result<()>; + + /// Check whether the `key` is present in the storage trie associated with the current account. + /// + /// Returns `Ok(true)` if key is present, `Ok(false)` if the key is not present. + /// + /// # Arguments + /// + /// * `key` - a key to check + /// * `mode`- whether the lookup will be performed through flat storage or trie + /// + /// # Errors + /// + /// This function could return [`unc_vm_runner::logic::VMError`]. + /// + /// # Example + /// ``` + /// # use unc_vm_runner::logic::mocks::mock_external::MockedExternal; + /// # use unc_vm_runner::logic::{External, StorageGetMode}; + /// + /// # let mut external = MockedExternal::new(); + /// external.storage_set(b"key42", b"value1337").unwrap(); + /// // Returns value if exists + /// assert_eq!(external.storage_has_key(b"key42", StorageGetMode::Trie), Ok(true)); + /// // Returns None if there was no value + /// assert_eq!(external.storage_has_key(b"no_value_key", StorageGetMode::Trie), Ok(false)); + /// ``` + fn storage_has_key(&mut self, key: &[u8], mode: StorageGetMode) -> Result; + + fn generate_data_id(&mut self) -> CryptoHash; + + /// Returns amount of touched trie nodes by storage operations + fn get_trie_nodes_count(&self) -> TrieNodesCount; + + /// Returns the validator stake for given account in the current epoch. + /// If the account is not a validator, returns `None`. + fn validator_frozen(&self, account_id: &AccountId) -> Result>; + fn validator_power(&self, account_id: &AccountId) -> Result>; + + /// Returns total stake of validators in the current epoch. + fn validator_total_frozen(&self) -> Result; + fn validator_total_power(&self) -> Result; + + /// Create a receipt which will be executed after all the receipts identified by + /// `receipt_indices` are complete. + /// + /// If any of the [`ReceiptIndex`]es do not refer to a known receipt, this function will fail + /// with an error. + /// + /// # Arguments + /// + /// * `generate_data_id` - function to generate a data id to connect receipt output to + /// * `receipt_indices` - a list of receipt indices the new receipt is depend on + /// * `receiver_id` - account id of the receiver of the receipt created + fn create_receipt( + &mut self, + receipt_indices: Vec, + receiver_id: AccountId, + ) -> Result; + + /// Attach the [`CreateAccountAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + fn append_action_create_account( + &mut self, + receipt_index: ReceiptIndex, + ) -> Result<(), VMLogicError>; + + /// Attach the [`DeployContractAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `code` - a Wasm code to attach + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + fn append_action_deploy_contract( + &mut self, + receipt_index: ReceiptIndex, + code: Vec, + ) -> Result<(), VMLogicError>; + + /// Attach the [`FunctionCallAction`] action to an existing receipt. + /// + /// `prepaid_gas` and `gas_weight` can either be specified or both. If a `gas_weight` is + /// specified, the action should be allocated gas in + /// [`distribute_unused_gas`](Self::distribute_unused_gas). + /// + /// For more information, see [super::VMLogic::promise_batch_action_function_call_weight]. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `method_name` - a name of the contract method to call + /// * `arguments` - a Wasm code to attach + /// * `attached_deposit` - amount of tokens to transfer with the call + /// * `prepaid_gas` - amount of prepaid gas to attach to the call + /// * `gas_weight` - relative weight of unused gas to distribute to the function call action + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + fn append_action_function_call_weight( + &mut self, + receipt_index: ReceiptIndex, + method_name: Vec, + args: Vec, + attached_deposit: Balance, + prepaid_gas: Gas, + gas_weight: GasWeight, + ) -> Result<(), VMLogicError>; + + /// Attach the [`TransferAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `amount` - amount of tokens to transfer + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + fn append_action_transfer( + &mut self, + receipt_index: ReceiptIndex, + deposit: Balance, + ) -> Result<(), VMLogicError>; + + /// Attach the [`StakeAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `stake` - amount of tokens to stake + /// * `public_key` - a validator public key + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + fn append_action_stake( + &mut self, + receipt_index: ReceiptIndex, + stake: Balance, + public_key: PublicKey, + ); + + /// Attach the [`AddKeyAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `public_key` - a public key for an access key + /// * `nonce` - a nonce + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + fn append_action_add_key_with_full_access( + &mut self, + receipt_index: ReceiptIndex, + public_key: PublicKey, + nonce: Nonce, + ); + + /// Attach the [`AddKeyAction`] action an existing receipt. + /// + /// The access key associated with the action will have the + /// [`AccessKeyPermission::FunctionCall`] permission scope. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `public_key` - a public key for an access key + /// * `nonce` - a nonce + /// * `allowance` - amount of tokens allowed to spend by this access key + /// * `receiver_id` - a contract witch will be allowed to call with this access key + /// * `method_names` - a list of method names is allowed to call with this access key (empty = any method) + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + fn append_action_add_key_with_function_call( + &mut self, + receipt_index: ReceiptIndex, + public_key: PublicKey, + nonce: Nonce, + allowance: Option, + receiver_id: AccountId, + method_names: Vec>, + ) -> Result<(), VMLogicError>; + + /// Attach the [`DeleteKeyAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `public_key` - a public key for an access key to delete + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + fn append_action_delete_key(&mut self, receipt_index: ReceiptIndex, public_key: PublicKey); + + /// Attach the [`DeleteAccountAction`] action to an existing receipt + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `beneficiary_id` - an account id to which the rest of the funds of the removed account will be transferred + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + fn append_action_delete_account( + &mut self, + receipt_index: ReceiptIndex, + beneficiary_id: AccountId, + ) -> Result<(), VMLogicError>; + + /// # Panic + /// + /// Panics if `ReceiptIndex` is invalid. + fn get_receipt_receiver(&self, receipt_index: ReceiptIndex) -> &AccountId; +} diff --git a/runtime/unc-vm-runner/src/logic/errors.rs b/runtime/unc-vm-runner/src/logic/errors.rs new file mode 100644 index 000000000..343f9451d --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/errors.rs @@ -0,0 +1,524 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use std::any::Any; +use std::fmt::{self, Error, Formatter}; +use std::io; + +/// For bugs in the runtime itself, crash and die is the usual response. +/// +/// See the doc comment on `VMResult` for an explanation what the difference +/// between this and a `FunctionCallError` is. +#[derive(Debug, thiserror::Error)] +pub enum VMRunnerError { + /// An error that is caused by an operation on an inconsistent state. + /// E.g. an integer overflow by using a value from the given context. + #[error("{0}")] + InconsistentStateError(InconsistentStateError), + /// Error caused by caching. + #[error("cache error: {0}")] + CacheError(#[from] CacheError), + /// Error (eg, resource exhausting) when loading a successfully compiled + /// contract into executable memory. + #[error("loading error: {0}")] + LoadingError(String), + /// Type erased error from `External` trait implementation. + #[error("external error")] + ExternalError(AnyError), + /// Non-deterministic error. + #[error("non-deterministic error during contract execution: {0}")] + Nondeterministic(String), + #[error("unknown error during contract execution: {debug_message}")] + WasmUnknownError { debug_message: String }, +} + +/// Permitted errors that cause a function call to fail gracefully. +/// +/// Occurrence of such errors will be included in the merklize state on chain +/// using a single bit to signal failure vs Success. +/// +/// See the doc comment on `VMResult` for an explanation what the difference +/// between this and a `VMRunnerError` is. And see `PartialExecutionStatus` +/// for what gets stored on chain. +#[derive(Debug, PartialEq, Eq, strum::IntoStaticStr)] +pub enum FunctionCallError { + /// Wasm compilation error + CompilationError(CompilationError), + /// Wasm binary env link error + LinkError { + msg: String, + }, + /// Import/export resolve error + MethodResolveError(MethodResolveError), + /// A trap happened during execution of a binary + WasmTrap(WasmTrap), + HostError(HostError), +} + +#[derive(Debug, thiserror::Error, strum::IntoStaticStr)] +pub enum CacheError { + #[error("cache read error")] + ReadError(#[source] io::Error), + #[error("cache write error")] + WriteError(#[source] io::Error), + #[error("cache deserialization error")] + DeserializationError, + #[error("cache serialization error")] + SerializationError { hash: [u8; 32] }, +} +/// A kind of a trap happened during execution of a binary +#[derive(Debug, Clone, PartialEq, Eq, strum::IntoStaticStr)] +pub enum WasmTrap { + /// An `unreachable` opcode was executed. + Unreachable, + /// Call indirect incorrect signature trap. + IncorrectCallIndirectSignature, + /// Memory out of bounds trap. + MemoryOutOfBounds, + /// Call indirect out of bounds trap. + CallIndirectOOB, + /// An arithmetic exception, e.g. divided by zero. + IllegalArithmetic, + /// Misaligned atomic access trap. + MisalignedAtomicAccess, + /// Indirect call to null. + IndirectCallToNull, + /// Stack overflow. + StackOverflow, + /// Generic trap. + GenericTrap, +} + +#[derive(Debug, Clone, PartialEq, Eq, strum::IntoStaticStr)] +pub enum MethodResolveError { + MethodEmptyName, + MethodNotFound, + MethodInvalidSignature, +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, strum::IntoStaticStr)] +pub enum CompilationError { + CodeDoesNotExist { + account_id: Box, + }, + PrepareError(PrepareError), + /// This is for defense in depth. + /// We expect our runtime-independent preparation code to fully catch all invalid wasms, + /// but, if it ever misses something we’ll emit this error + WasmerCompileError { + msg: String, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize)] +/// Error that can occur while preparing or executing Wasm smart-contract. +pub enum PrepareError { + /// Error happened while serializing the module. + Serialization, + /// Error happened while deserializing the module. + Deserialization, + /// Internal memory declaration has been found in the module. + InternalMemoryDeclared, + /// Gas instrumentation failed. + /// + /// This most likely indicates the module isn't valid. + GasInstrumentation, + /// Stack instrumentation failed. + /// + /// This most likely indicates the module isn't valid. + StackHeightInstrumentation, + /// Error happened during instantiation. + /// + /// This might indicate that `start` function trapped, or module isn't + /// instantiable and/or unlinkable. + Instantiate, + /// Error creating memory. + Memory, + /// Contract contains too many functions. + TooManyFunctions, + /// Contract contains too many locals. + TooManyLocals, +} + +#[derive(Debug, Clone, PartialEq, Eq, strum::IntoStaticStr)] +pub enum HostError { + /// String encoding is bad UTF-16 sequence + BadUTF16, + /// String encoding is bad UTF-8 sequence + BadUTF8, + /// Exceeded the prepaid gas + GasExceeded, + /// Exceeded the maximum amount of gas allowed to burn per contract + GasLimitExceeded, + /// Exceeded the account balance + BalanceExceeded, + /// Tried to call an empty method name + EmptyMethodName, + /// Smart contract panicked + GuestPanic { panic_msg: String }, + /// IntegerOverflow happened during a contract execution + IntegerOverflow, + /// `promise_idx` does not correspond to existing promises + InvalidPromiseIndex { promise_idx: u64 }, + /// Actions can only be appended to non-joint promise. + CannotAppendActionToJointPromise, + /// Returning joint promise is currently prohibited + CannotReturnJointPromise, + /// Accessed invalid promise result index + InvalidPromiseResultIndex { result_idx: u64 }, + /// Accessed invalid register id + InvalidRegisterId { register_id: u64 }, + /// Accessed memory outside the bounds + MemoryAccessViolation, + /// VM Logic returned an invalid receipt index + InvalidReceiptIndex { receipt_index: u64 }, + /// Iterator index `iterator_index` does not exist + InvalidIteratorIndex { iterator_index: u64 }, + /// VM Logic returned an invalid account id + InvalidAccountId, + /// VM Logic returned an invalid method name + InvalidMethodName, + /// VM Logic provided an invalid public key + InvalidPublicKey, + /// `method_name` is not allowed in view calls + ProhibitedInView { method_name: String }, + /// The total number of logs will exceed the limit. + NumberOfLogsExceeded { limit: u64 }, + /// The storage key length exceeded the limit. + KeyLengthExceeded { length: u64, limit: u64 }, + /// The storage value length exceeded the limit. + ValueLengthExceeded { length: u64, limit: u64 }, + /// The total log length exceeded the limit. + TotalLogLengthExceeded { length: u64, limit: u64 }, + /// The maximum number of promises within a FunctionCall exceeded the limit. + NumberPromisesExceeded { number_of_promises: u64, limit: u64 }, + /// The maximum number of input data dependencies exceeded the limit. + NumberInputDataDependenciesExceeded { number_of_input_data_dependencies: u64, limit: u64 }, + /// The returned value length exceeded the limit. + ReturnedValueLengthExceeded { length: u64, limit: u64 }, + /// The contract size for DeployContract action exceeded the limit. + ContractSizeExceeded { size: u64, limit: u64 }, + /// The host function was deprecated. + Deprecated { method_name: String }, + /// General errors for ECDSA recover. + ECRecoverError { msg: String }, + /// Invalid input to alt_bn128 familiy of functions (e.g., point which isn't + /// on the curve). + AltBn128InvalidInput { msg: String }, + /// Invalid input to ed25519 signature verification function (e.g. signature cannot be + /// derived from bytes). + Ed25519VerifyInvalidInput { msg: String }, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum VMLogicError { + /// Errors coming from native Wasm VM. + HostError(HostError), + /// Type erased error from `External` trait implementation. + ExternalError(AnyError), + /// An error that is caused by an operation on an inconsistent state. + InconsistentStateError(InconsistentStateError), +} + +impl std::error::Error for VMLogicError {} + +/// An error that is caused by an operation on an inconsistent state, such as +/// integer overflow. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum InconsistentStateError { + /// Math operation with a value from the state resulted in a integer overflow. + IntegerOverflow, +} + +impl From for VMLogicError { + fn from(err: HostError) -> Self { + VMLogicError::HostError(err) + } +} + +impl From for VMLogicError { + fn from(err: InconsistentStateError) -> Self { + VMLogicError::InconsistentStateError(err) + } +} + +impl From for FunctionCallError { + fn from(err: PrepareError) -> Self { + FunctionCallError::CompilationError(CompilationError::PrepareError(err)) + } +} + +/// Try to convert a general error that happened at the `VMLogic` level to a +/// `FunctionCallError` that can be included in a `VMOutcome`. +/// +/// `VMLogicError` have two very different types of errors. Some are just +/// a result from the guest code doing incorrect things, other are bugs in +/// framework. +/// The first type can be converted to a `FunctionCallError`, the other becomes +/// a `VMRunnerError` instead and must be treated differently. +impl TryFrom for FunctionCallError { + type Error = VMRunnerError; + fn try_from(err: VMLogicError) -> Result { + match err { + VMLogicError::HostError(h) => Ok(FunctionCallError::HostError(h)), + VMLogicError::ExternalError(s) => Err(VMRunnerError::ExternalError(s)), + VMLogicError::InconsistentStateError(e) => { + Err(VMRunnerError::InconsistentStateError(e)) + } + } + } +} + +impl fmt::Display for VMLogicError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + write!(f, "{:?}", self) + } +} + +impl fmt::Display for PrepareError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + use PrepareError::*; + f.write_str(match self { + Serialization => "Error happened while serializing the module.", + Deserialization => "Error happened while deserializing the module.", + InternalMemoryDeclared => "Internal memory declaration has been found in the module.", + GasInstrumentation => "Gas instrumentation failed.", + StackHeightInstrumentation => "Stack instrumentation failed.", + Instantiate => "Error happened during instantiation.", + Memory => "Error creating memory.", + TooManyFunctions => "Too many functions in contract.", + TooManyLocals => "Too many locals declared in the contract.", + }) + } +} + +impl fmt::Display for FunctionCallError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self { + FunctionCallError::CompilationError(e) => e.fmt(f), + FunctionCallError::MethodResolveError(e) => e.fmt(f), + FunctionCallError::HostError(e) => e.fmt(f), + FunctionCallError::LinkError { msg } => write!(f, "{}", msg), + FunctionCallError::WasmTrap(trap) => write!(f, "WebAssembly trap: {}", trap), + } + } +} + +impl fmt::Display for WasmTrap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self { + WasmTrap::Unreachable => write!(f, "An `unreachable` opcode was executed."), + WasmTrap::IncorrectCallIndirectSignature => { + write!(f, "Call indirect incorrect signature trap.") + } + WasmTrap::MemoryOutOfBounds => write!(f, "Memory out of bounds trap."), + WasmTrap::CallIndirectOOB => write!(f, "Call indirect out of bounds trap."), + WasmTrap::IllegalArithmetic => { + write!(f, "An arithmetic exception, e.g. divided by zero.") + } + WasmTrap::MisalignedAtomicAccess => write!(f, "Misaligned atomic access trap."), + WasmTrap::GenericTrap => write!(f, "Generic trap."), + WasmTrap::StackOverflow => write!(f, "Stack overflow."), + WasmTrap::IndirectCallToNull => write!(f, "Indirect call to null."), + } + } +} + +impl fmt::Display for CompilationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self { + CompilationError::CodeDoesNotExist { account_id } => { + write!(f, "cannot find contract code for account {}", account_id) + } + CompilationError::PrepareError(p) => write!(f, "PrepareError: {}", p), + CompilationError::WasmerCompileError { msg } => { + write!(f, "Wasmer compilation error: {}", msg) + } + } + } +} + +impl fmt::Display for MethodResolveError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + fmt::Debug::fmt(self, f) + } +} + +impl std::fmt::Display for InconsistentStateError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + InconsistentStateError::IntegerOverflow => write!( + f, + "Math operation with a value from the state resulted in a integer overflow.", + ), + } + } +} + +impl std::fmt::Display for HostError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + use HostError::*; + match self { + BadUTF8 => write!(f, "String encoding is bad UTF-8 sequence."), + BadUTF16 => write!(f, "String encoding is bad UTF-16 sequence."), + GasExceeded => write!(f, "Exceeded the prepaid gas."), + GasLimitExceeded => { + write!(f, "Exceeded the maximum amount of gas allowed to burn per contract.") + } + BalanceExceeded => write!(f, "Exceeded the account balance."), + EmptyMethodName => write!(f, "Tried to call an empty method name."), + GuestPanic { panic_msg } => write!(f, "Smart contract panicked: {}", panic_msg), + IntegerOverflow => write!(f, "Integer overflow."), + InvalidIteratorIndex { iterator_index } => { + write!(f, "Iterator index {:?} does not exist", iterator_index) + } + InvalidPromiseIndex { promise_idx } => { + write!(f, "{:?} does not correspond to existing promises", promise_idx) + } + CannotAppendActionToJointPromise => { + write!(f, "Actions can only be appended to non-joint promise.") + } + CannotReturnJointPromise => { + write!(f, "Returning joint promise is currently prohibited.") + } + InvalidPromiseResultIndex { result_idx } => { + write!(f, "Accessed invalid promise result index: {:?}", result_idx) + } + InvalidRegisterId { register_id } => { + write!(f, "Accessed invalid register id: {:?}", register_id) + } + MemoryAccessViolation => write!(f, "Accessed memory outside the bounds."), + InvalidReceiptIndex { receipt_index } => { + write!(f, "VM Logic returned an invalid receipt index: {:?}", receipt_index) + } + InvalidAccountId => write!(f, "VM Logic returned an invalid account id"), + InvalidMethodName => write!(f, "VM Logic returned an invalid method name"), + InvalidPublicKey => write!(f, "VM Logic provided an invalid public key"), + ProhibitedInView { method_name } => { + write!(f, "{} is not allowed in view calls", method_name) + } + NumberOfLogsExceeded { limit } => { + write!(f, "The number of logs will exceed the limit {}", limit) + } + KeyLengthExceeded { length, limit } => { + write!(f, "The length of a storage key {} exceeds the limit {}", length, limit) + } + ValueLengthExceeded { length, limit } => { + write!(f, "The length of a storage value {} exceeds the limit {}", length, limit) + } + TotalLogLengthExceeded { length, limit } => { + write!(f, "The length of a log message {} exceeds the limit {}", length, limit) + } + NumberPromisesExceeded { number_of_promises, limit } => write!( + f, + "The number of promises within a FunctionCall {} exceeds the limit {}", + number_of_promises, limit + ), + NumberInputDataDependenciesExceeded { number_of_input_data_dependencies, limit } => { + write!( + f, + "The number of input data dependencies {} exceeds the limit {}", + number_of_input_data_dependencies, limit + ) + } + ReturnedValueLengthExceeded { length, limit } => { + write!(f, "The length of a returned value {} exceeds the limit {}", length, limit) + } + ContractSizeExceeded { size, limit } => write!( + f, + "The size of a contract code in DeployContract action {} exceeds the limit {}", + size, limit + ), + Deprecated { method_name } => { + write!(f, "Attempted to call deprecated host function {}", method_name) + } + AltBn128InvalidInput { msg } => write!(f, "AltBn128 invalid input: {}", msg), + ECRecoverError { msg } => write!(f, "ECDSA recover error: {}", msg), + Ed25519VerifyInvalidInput { msg } => { + write!(f, "ED25519 signature verification error: {}", msg) + } + } + } +} + +/// Type-erased error used to shuttle some concrete error coming from `External` +/// through vm-logic. +/// +/// The caller is supposed to downcast this to a concrete error type they should +/// know. This would be just `Box` if the latter actually worked. +pub struct AnyError { + any: Box, +} + +#[derive(Debug, thiserror::Error)] +#[error("failed to downcast")] +pub struct DowncastFailedError; + +impl AnyError { + pub fn new(err: E) -> AnyError { + AnyError { any: Box::new(err) } + } + pub fn downcast(self) -> Result { + match self.any.into_any().downcast::() { + Ok(it) => Ok(*it), + Err(_) => Err(DowncastFailedError), + } + } +} + +impl PartialEq for AnyError { + fn eq(&self, other: &Self) -> bool { + self.any.any_eq(&*other.any) + } +} + +impl Eq for AnyError {} + +impl fmt::Debug for AnyError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.any.as_any(), f) + } +} + +trait AnyEq: Any + Send + Sync { + fn any_eq(&self, rhs: &dyn AnyEq) -> bool; + fn as_any(&self) -> &dyn Any; + fn into_any(self: Box) -> Box; +} + +impl AnyEq for T { + fn any_eq(&self, rhs: &dyn AnyEq) -> bool { + match rhs.as_any().downcast_ref::() { + Some(rhs) => self == rhs, + None => false, + } + } + fn as_any(&self) -> &dyn Any { + self + } + fn into_any(self: Box) -> Box { + self + } +} + +#[cfg(test)] +mod tests { + use crate::logic::errors::{ + CompilationError, FunctionCallError, MethodResolveError, PrepareError, + }; + + #[test] + fn test_display() { + // TODO: proper printing + assert_eq!( + FunctionCallError::MethodResolveError(MethodResolveError::MethodInvalidSignature) + .to_string(), + "MethodInvalidSignature" + ); + assert_eq!( + FunctionCallError::CompilationError(CompilationError::PrepareError( + PrepareError::StackHeightInstrumentation + )) + .to_string(), + "PrepareError: Stack instrumentation failed." + ); + } +} diff --git a/runtime/unc-vm-runner/src/logic/gas_counter.rs b/runtime/unc-vm-runner/src/logic/gas_counter.rs new file mode 100644 index 000000000..67e98d0d7 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/gas_counter.rs @@ -0,0 +1,407 @@ +use super::errors::{HostError, VMLogicError}; +use super::TrieNodesCount; +use crate::ProfileDataV3; +use unc_parameters::ExtCosts::{read_cached_trie_node, touching_trie_node}; +use unc_parameters::{ActionCosts, ExtCosts, ExtCostsConfig}; +use unc_primitives_core::types::Gas; +use std::collections::HashMap; + +#[inline] +pub fn with_ext_cost_counter(f: impl FnOnce(&mut HashMap)) { + #[cfg(any(test, feature = "costs_counting"))] + { + thread_local! { + static EXT_COSTS_COUNTER: std::cell::RefCell> = + Default::default(); + } + EXT_COSTS_COUNTER.with(|rc| f(&mut *rc.borrow_mut())); + } + #[cfg(not(any(test, feature = "costs_counting")))] + let _ = f; +} + +type Result = ::std::result::Result; + +/// Fast gas counter with very simple structure, could be exposed to compiled code in the VM. For +/// instance by intrinsifying host functions responsible for gas metering. +#[repr(C)] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FastGasCounter { + /// The following three fields must be put next to another to make sure + /// generated gas counting code can use and adjust them. + /// We will share counter to ensure we never miss synchronization. + /// This could change and in such a case synchronization required between compiled WASM code + /// and the host code. + + /// The amount of gas that was irreversibly used for contract execution. + pub burnt_gas: u64, + /// Hard gas limit for execution + pub gas_limit: u64, + /// Cost for one opcode. Used only by VMs preceding unc_vm. + pub opcode_cost: u64, +} + +/// Gas counter (a part of VMlogic) +pub struct GasCounter { + /// Shared gas counter data. + fast_counter: FastGasCounter, + /// Gas that was attached to the promises. + promises_gas: Gas, + /// Hard gas limit for execution + max_gas_burnt: Gas, + /// Amount of prepaid gas, we can never burn more than prepaid amount + prepaid_gas: Gas, + /// If this is a view-only call. + is_view: bool, + /// FIXME(nagisa): why do we store a copy both here and in the VMLogic??? + ext_costs_config: ExtCostsConfig, + /// Where to store profile data, if needed. + profile: ProfileDataV3, +} + +impl GasCounter { + pub fn new( + ext_costs_config: ExtCostsConfig, + max_gas_burnt: Gas, + opcode_cost: u32, + prepaid_gas: Gas, + is_view: bool, + ) -> Self { + use std::cmp::min; + // Ignore prepaid gas limit when in view. + let prepaid_gas = if is_view { Gas::MAX } else { prepaid_gas }; + Self { + ext_costs_config, + fast_counter: FastGasCounter { + burnt_gas: 0, + gas_limit: min(max_gas_burnt, prepaid_gas), + opcode_cost: Gas::from(opcode_cost), + }, + max_gas_burnt, + promises_gas: 0, + prepaid_gas, + is_view, + profile: Default::default(), + } + } + + /// Deducts burnt and used gas. + /// + /// Returns an error if the `max_gax_burnt` or the `prepaid_gas` limits are + /// crossed or there are arithmetic overflows. + /// + /// Panics + /// + /// This function asserts that `gas_burnt <= gas_used` + fn deduct_gas(&mut self, gas_burnt: Gas, gas_used: Gas) -> Result<()> { + assert!(gas_burnt <= gas_used); + let promises_gas = gas_used - gas_burnt; + let new_promises_gas = + self.promises_gas.checked_add(promises_gas).ok_or(HostError::IntegerOverflow)?; + let new_burnt_gas = + self.fast_counter.burnt_gas.checked_add(gas_burnt).ok_or(HostError::IntegerOverflow)?; + let new_used_gas = + new_burnt_gas.checked_add(new_promises_gas).ok_or(HostError::IntegerOverflow)?; + if new_burnt_gas <= self.max_gas_burnt && new_used_gas <= self.prepaid_gas { + use std::cmp::min; + if promises_gas != 0 && !self.is_view { + self.fast_counter.gas_limit = + min(self.max_gas_burnt, self.prepaid_gas - new_promises_gas); + } + self.fast_counter.burnt_gas = new_burnt_gas; + self.promises_gas = new_promises_gas; + Ok(()) + } else { + Err(self.process_gas_limit(new_burnt_gas, new_used_gas).into()) + } + } + + /// Simpler version of `deduct_gas()` for when no promises are involved. + /// + /// Return an error if there are arithmetic overflows. + pub fn burn_gas(&mut self, gas_burnt: Gas) -> Result<()> { + let new_burnt_gas = + self.fast_counter.burnt_gas.checked_add(gas_burnt).ok_or(HostError::IntegerOverflow)?; + if new_burnt_gas <= self.fast_counter.gas_limit { + self.fast_counter.burnt_gas = new_burnt_gas; + Ok(()) + } else { + // In the past `new_used_gas` would be computed using an implicit wrapping addition, + // which would then give an opportunity for the `assert` (now `debug_assert`) in the + // callee to fail, leading to a DoS of a node. A wrapping_add in this instance is + // actually fine, even if it gives the attacker full control of the value passed in + // here… + // + // [CONTINUATION IN THE NEXT COMMENT] + let new_used_gas = new_burnt_gas.wrapping_add(self.promises_gas); + Err(self.process_gas_limit(new_burnt_gas, new_used_gas).into()) + } + } + + pub fn process_gas_limit(&mut self, new_burnt_gas: Gas, new_used_gas: Gas) -> HostError { + use std::cmp::min; + // Never burn more gas than what was paid for. + let hard_burnt_limit = min(self.prepaid_gas, self.max_gas_burnt); + self.fast_counter.burnt_gas = min(new_burnt_gas, hard_burnt_limit); + debug_assert!(hard_burnt_limit >= self.fast_counter.burnt_gas); + + // Technically we shall do `self.promises_gas = 0;` or error paths, as in this case + // no promises will be kept, but that would mean protocol change. + // See https://github.com/utnet-org/utility/issues/5148. + // TODO: consider making this change! + let used_gas_limit = min(self.prepaid_gas, new_used_gas); + // [CONTINUATION OF THE PREVIOUS COMMENT] + // + // Now, there are two distinct ways an attacker can attempt to exploit this code given + // their full control of the `new_used_gas` value. + // + // 1. `self.prepaid_gas < new_used_gas` This is perfectly fine and would be the happy path, + // were the computations performed with arbitrary precision integers all the time. + // 2. `new_used_gas < new_burnt_gas` means that the `new_used_gas` computation wrapped + // and `used_gas_limit` is now set to a lower value than it otherwise should be. In the + // past this would have triggered an unconditional assert leading to nodes crashing and + // network getting stuck/going down. We don’t actually need to assert, though. By + // replacing the wrapping subtraction with a saturating one we make sure that the + // resulting value of `self.promises_gas` is well behaved (becomes 0.) All a potential + // attacker has achieved in this case is throwing some of their gas away. + self.promises_gas = used_gas_limit.saturating_sub(self.fast_counter.burnt_gas); + + // If we crossed both limits prefer reporting GasLimitExceeded. + // Alternative would be to prefer reporting limit that is lower (or + // perhaps even some other heuristic) but old code preferred + // GasLimitExceeded and we’re keeping compatibility with that. + if new_burnt_gas > self.max_gas_burnt { + HostError::GasLimitExceeded + } else { + HostError::GasExceeded + } + } + + /// Very special function to get the gas counter pointer for generated machine code. + /// Please do not use, unless fully understand Rust aliasing and other consequences. + /// Can be used to emit inlined code like `pay_wasm_gas()`, i.e. + /// mov base, gas_counter_raw_ptr + /// mov rax, [base + 0] ; current burnt gas + /// mov rcx, [base + 16] ; opcode cost + /// imul rcx, block_ops_count ; block cost + /// add rax, rcx ; new burnt gas + /// jo emit_integer_overflow + /// cmp rax, [base + 8] ; unsigned compare with burnt limit + /// mov [base + 0], rax + /// ja emit_gas_exceeded + pub fn gas_counter_raw_ptr(&mut self) -> *mut FastGasCounter { + use std::ptr; + ptr::addr_of_mut!(self.fast_counter) + } + + #[inline] + fn inc_ext_costs_counter(&mut self, cost: ExtCosts, value: u64) { + with_ext_cost_counter(|cc| *cc.entry(cost).or_default() += value) + } + + #[inline] + fn update_profile_host(&mut self, cost: ExtCosts, value: u64) { + self.profile.add_ext_cost(cost, value) + } + + #[inline] + fn update_profile_action(&mut self, action: ActionCosts, value: u64) { + self.profile.add_action_cost(action, value) + } + + /// A helper function to pay a multiple of a cost. + pub fn pay_per(&mut self, cost: ExtCosts, num: u64) -> Result<()> { + let use_gas = + num.checked_mul(cost.gas(&self.ext_costs_config)).ok_or(HostError::IntegerOverflow)?; + + self.inc_ext_costs_counter(cost, num); + let old_burnt_gas = self.fast_counter.burnt_gas; + let burn_gas_result = self.burn_gas(use_gas); + self.update_profile_host(cost, self.fast_counter.burnt_gas.saturating_sub(old_burnt_gas)); + burn_gas_result + } + + /// A helper function to pay base cost gas. + pub fn pay_base(&mut self, cost: ExtCosts) -> Result<()> { + let base_fee = cost.gas(&self.ext_costs_config); + self.inc_ext_costs_counter(cost, 1); + let old_burnt_gas = self.fast_counter.burnt_gas; + let burn_gas_result = self.burn_gas(base_fee); + self.update_profile_host(cost, self.fast_counter.burnt_gas.saturating_sub(old_burnt_gas)); + burn_gas_result + } + + /// A helper function to pay base cost gas fee for batching an action. + /// # Args: + /// * `burn_gas`: amount of gas to burn; + /// * `use_gas`: amount of gas to reserve; + /// * `action`: what kind of action is charged for; + pub fn pay_action_accumulated( + &mut self, + burn_gas: Gas, + use_gas: Gas, + action: ActionCosts, + ) -> Result<()> { + let old_burnt_gas = self.fast_counter.burnt_gas; + let deduct_gas_result = self.deduct_gas(burn_gas, use_gas); + self.update_profile_action( + action, + self.fast_counter.burnt_gas.saturating_sub(old_burnt_gas), + ); + deduct_gas_result + } + + pub fn add_trie_fees(&mut self, count: &TrieNodesCount) -> Result<()> { + self.pay_per(touching_trie_node, count.db_reads)?; + self.pay_per(read_cached_trie_node, count.mem_reads)?; + Ok(()) + } + + pub fn prepay_gas(&mut self, use_gas: Gas) -> Result<()> { + self.deduct_gas(0, use_gas) + } + + pub fn burnt_gas(&self) -> Gas { + self.fast_counter.burnt_gas + } + + /// Amount of gas used through promises and amount burned. + pub fn used_gas(&self) -> Gas { + self.promises_gas + self.fast_counter.burnt_gas + } + + /// Remaining gas based on the amount of prepaid gas not yet used. + pub fn unused_gas(&self) -> Gas { + self.prepaid_gas - self.used_gas() + } + + pub fn profile_data(&self) -> ProfileDataV3 { + self.profile.clone() + } +} + +#[cfg(test)] +mod tests { + use super::{ExtCostsConfig, HostError}; + use unc_parameters::{ActionCosts, ExtCosts}; + use unc_primitives_core::types::Gas; + + /// Max prepaid amount of gas. + const MAX_GAS: u64 = 300_000_000_000_000; + + fn make_test_counter(max_burnt: Gas, prepaid: Gas, is_view: bool) -> super::GasCounter { + super::GasCounter::new(ExtCostsConfig::test(), max_burnt, 1, prepaid, is_view) + } + + #[test] + fn test_deduct_gas() { + let mut counter = make_test_counter(10, 10, false); + counter.deduct_gas(5, 10).expect("deduct_gas should work"); + assert_eq!(counter.burnt_gas(), 5); + assert_eq!(counter.used_gas(), 10); + } + + #[test] + #[should_panic] + fn test_burn_gas_must_be_lt_use_gas() { + let _ = make_test_counter(10, 10, false).deduct_gas(5, 2); + } + + #[test] + #[should_panic] + fn test_burn_gas_must_be_lt_use_gas_view() { + let _ = make_test_counter(10, 10, true).deduct_gas(5, 2); + } + + #[test] + fn test_burn_too_much() { + fn test(burn: Gas, prepaid: Gas, view: bool, want: Result<(), HostError>) { + let mut counter = make_test_counter(burn, prepaid, view); + assert_eq!(counter.burn_gas(5), Ok(())); + assert_eq!(counter.burn_gas(3), want.map_err(Into::into)); + } + + test(5, 7, false, Err(HostError::GasLimitExceeded)); + test(5, 7, true, Err(HostError::GasLimitExceeded)); + test(5, 5, false, Err(HostError::GasLimitExceeded)); + test(5, 5, true, Err(HostError::GasLimitExceeded)); + test(7, 5, false, Err(HostError::GasLimitExceeded)); + test(7, 5, true, Err(HostError::GasLimitExceeded)); + } + + #[test] + fn test_deduct_too_much() { + fn test(burn: Gas, prepaid: Gas, view: bool, want: Result<(), HostError>) { + let mut counter = make_test_counter(burn, prepaid, view); + assert_eq!(counter.deduct_gas(5, 5), Ok(())); + assert_eq!(counter.deduct_gas(3, 3), want.map_err(Into::into)); + } + + test(5, 7, false, Err(HostError::GasLimitExceeded)); + test(5, 7, true, Err(HostError::GasLimitExceeded)); + test(5, 5, false, Err(HostError::GasLimitExceeded)); + test(5, 5, true, Err(HostError::GasLimitExceeded)); + test(7, 5, false, Err(HostError::GasLimitExceeded)); + test(7, 5, true, Err(HostError::GasLimitExceeded)); + + test(5, 8, false, Err(HostError::GasLimitExceeded)); + test(5, 8, true, Err(HostError::GasLimitExceeded)); + test(8, 5, false, Err(HostError::GasExceeded)); + test(8, 5, true, Ok(())); + } + + #[test] + fn test_profile_compute_cost_is_accurate() { + let mut counter = make_test_counter(MAX_GAS, MAX_GAS, false); + counter.pay_base(ExtCosts::storage_write_base).unwrap(); + counter.pay_per(ExtCosts::storage_write_value_byte, 10).unwrap(); + counter.pay_action_accumulated(100, 100, ActionCosts::new_data_receipt_byte).unwrap(); + + let mut profile = counter.profile_data(); + profile.compute_wasm_instruction_cost(counter.burnt_gas()); + + assert_eq!(profile.total_compute_usage(&ExtCostsConfig::test()), counter.burnt_gas()); + } + + #[test] + fn test_profile_compute_cost_ext_over_limit() { + fn test(burn: Gas, prepaid: Gas, want: Result<(), HostError>) { + let mut counter = make_test_counter(burn, prepaid, false); + assert_eq!( + counter.pay_per(ExtCosts::storage_write_value_byte, 100), + want.map_err(Into::into) + ); + let mut profile = counter.profile_data(); + profile.compute_wasm_instruction_cost(counter.burnt_gas()); + + assert_eq!(profile.total_compute_usage(&ExtCostsConfig::test()), counter.burnt_gas()); + } + + test(MAX_GAS, 1_000_000_000, Err(HostError::GasExceeded)); + test(1_000_000_000, MAX_GAS, Err(HostError::GasLimitExceeded)); + test(1_000_000_000, 1_000_000_000, Err(HostError::GasLimitExceeded)); + } + + #[test] + fn test_profile_compute_cost_action_over_limit() { + fn test(burn: Gas, prepaid: Gas, want: Result<(), HostError>) { + let mut counter = make_test_counter(burn, prepaid, false); + assert_eq!( + counter.pay_action_accumulated( + 10_000_000_000, + 10_000_000_000, + ActionCosts::new_data_receipt_byte + ), + want.map_err(Into::into) + ); + let mut profile = counter.profile_data(); + profile.compute_wasm_instruction_cost(counter.burnt_gas()); + + assert_eq!(profile.total_compute_usage(&ExtCostsConfig::test()), counter.burnt_gas()); + } + + test(MAX_GAS, 1_000_000_000, Err(HostError::GasExceeded)); + test(1_000_000_000, MAX_GAS, Err(HostError::GasLimitExceeded)); + test(1_000_000_000, 1_000_000_000, Err(HostError::GasLimitExceeded)); + } +} diff --git a/runtime/unc-vm-runner/src/logic/logic.rs b/runtime/unc-vm-runner/src/logic/logic.rs new file mode 100644 index 000000000..b2ec88ac3 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/logic.rs @@ -0,0 +1,2976 @@ +use super::context::VMContext; +use super::dependencies::{External, MemSlice, MemoryLike}; +use super::errors::{FunctionCallError, InconsistentStateError}; +use super::gas_counter::{FastGasCounter, GasCounter}; +use super::types::{PromiseIndex, PromiseResult, ReceiptIndex, ReturnData}; +use super::utils::split_method_names; +use super::ValuePtr; +use super::{HostError, VMLogicError}; +use crate::ProfileDataV3; +use unc_crypto::Secp256K1Signature; +use unc_parameters::vm::{Config, StorageGetMode}; +use unc_parameters::{ + transfer_exec_fee, transfer_send_fee, ActionCosts, ExtCosts, RuntimeFeesConfig, +}; +use unc_primitives_core::config::ViewConfig; +use unc_primitives_core::types::{ + AccountId, Balance, Compute, EpochHeight, Gas, GasWeight, StorageUsage, +}; +use std::mem::size_of; +use ExtCosts::*; + +pub type Result = ::std::result::Result; + +#[cfg(feature = "io_trace")] +fn base64(s: &[u8]) -> String { + use base64::Engine; + base64::engine::general_purpose::STANDARD.encode(s) +} + +pub struct VMLogic<'a> { + /// Provides access to the components outside the Wasm runtime for operations on the trie and + /// receipts creation. + ext: &'a mut dyn External, + /// Part of Context API and Economics API that was extracted from the receipt. + context: VMContext, + /// All gas and economic parameters required during contract execution. + pub(crate) config: &'a Config, + /// Fees for creating (async) actions on runtime. + fees_config: &'a RuntimeFeesConfig, + /// If this method execution is invoked directly as a callback by one or more contract calls the + /// results of the methods that made the callback are stored in this collection. + promise_results: &'a [PromiseResult], + /// Pointer to the guest memory. + memory: super::vmstate::Memory<'a>, + + /// Keeping track of the current account balance, which can decrease when we create promises + /// and attach balance to them. + current_account_balance: Balance, + /// Current amount of locked tokens, does not automatically change when staking transaction is + /// issued. + current_account_locked_balance: Balance, + /// Storage usage of the current account at the moment + current_storage_usage: StorageUsage, + gas_counter: GasCounter, + /// What method returns. + return_data: ReturnData, + /// Logs written by the runtime. + logs: Vec, + /// Registers can be used by the guest to store blobs of data without moving them across + /// host-guest boundary. + registers: super::vmstate::Registers, + + /// The DAG of promises, indexed by promise id. + promises: Vec, + /// Tracks the total log length. The sum of length of all logs. + total_log_length: u64, + + /// Stores the amount of stack space remaining + remaining_stack: u64, +} + +/// Promises API allows to create a DAG-structure that defines dependencies between smart contract +/// calls. A single promise can be created with zero or several dependencies on other promises. +/// * If a promise was created from a receipt (using `promise_create` or `promise_then`) it's a +/// `Receipt`; +/// * If a promise was created by merging several promises (using `promise_and`) then +/// it's a `NotReceipt`, but has receipts of all promises it depends on. +#[derive(Debug)] +enum Promise { + Receipt(ReceiptIndex), + NotReceipt(Vec), +} + +/// Helper for calling `super::vmstate::get_memory_or_register`. +/// +/// super::vmstate::get_memory_or_register has a whole lot of wordy arguments +/// which are always the same when invoked inside of one of VMLogic method. +/// This macro helps with that invocation. +macro_rules! get_memory_or_register { + ($logic:expr, $offset:expr, $len:expr) => { + super::vmstate::get_memory_or_register( + &mut $logic.gas_counter, + &$logic.memory, + &$logic.registers, + $offset, + $len, + ) + }; +} + +/// A wrapper for reading public key. +/// +/// This exists for historical reasons because we must maintain when errors are +/// returned. In the old days, between reading the public key and decoding it +/// we could return unrelated error. Because of that we cannot change the code +/// to return deserialisation errors immediately after reading the public key. +/// +/// This struct abstracts away the fact that we’re deserialising the key +/// immediately. Decoding errors are detected as soon as this object is created +/// but they are communicated to the user only once they call [`Self::decode`]. +/// +/// Why not just keep the old ways without this noise? By doing deserialisation +/// immediately we’re copying the data onto the stack without having to allocate +/// a temporary vector. +struct PublicKeyBuffer(Result); + +impl PublicKeyBuffer { + fn new(data: &[u8]) -> Self { + Self(borsh::BorshDeserialize::try_from_slice(data).map_err(|_| ())) + } + + fn decode(self) -> Result { + self.0.map_err(|_| HostError::InvalidPublicKey.into()) + } +} + +impl<'a> VMLogic<'a> { + pub fn new( + ext: &'a mut dyn External, + context: VMContext, + config: &'a Config, + fees_config: &'a RuntimeFeesConfig, + promise_results: &'a [PromiseResult], + memory: &'a mut dyn MemoryLike, + ) -> Self { + // Overflow should be checked before calling VMLogic. + let current_account_balance = context.account_balance + context.attached_deposit; + let current_storage_usage = context.storage_usage; + let max_gas_burnt = match context.view_config { + Some(ViewConfig { max_gas_burnt: max_gas_burnt_view }) => max_gas_burnt_view, + None => config.limit_config.max_gas_burnt, + }; + + let current_account_locked_balance = context.account_locked_balance; + let gas_counter = GasCounter::new( + config.ext_costs.clone(), + max_gas_burnt, + config.regular_op_cost, + context.prepaid_gas, + context.is_view(), + ); + Self { + ext, + context, + config, + fees_config, + promise_results, + memory: super::vmstate::Memory::new(memory), + current_account_balance, + current_account_locked_balance, + current_storage_usage, + gas_counter, + return_data: ReturnData::None, + logs: vec![], + registers: Default::default(), + promises: vec![], + total_log_length: 0, + remaining_stack: u64::from(config.limit_config.max_stack_height), + } + } + + /// Returns reference to logs that have been created so far. + pub fn logs(&self) -> &[String] { + &self.logs + } + + #[cfg(test)] + pub(super) fn gas_counter(&self) -> &GasCounter { + &self.gas_counter + } + + #[cfg(test)] + pub(super) fn config(&self) -> &Config { + &self.config + } + + #[cfg(test)] + pub(super) fn memory(&mut self) -> &mut super::vmstate::Memory<'a> { + &mut self.memory + } + + #[cfg(test)] + pub(super) fn registers(&mut self) -> &mut super::vmstate::Registers { + &mut self.registers + } + + // ######################### + // # Finite-wasm internals # + // ######################### + pub fn finite_wasm_gas(&mut self, gas: u64) -> Result<()> { + self.gas(gas) + } + + pub fn finite_wasm_stack(&mut self, operand_size: u64, frame_size: u64) -> Result<()> { + self.remaining_stack = + match self.remaining_stack.checked_sub(operand_size.saturating_add(frame_size)) { + Some(s) => s, + None => return Err(VMLogicError::HostError(HostError::MemoryAccessViolation)), + }; + self.gas(((frame_size + 7) / 8) * u64::from(self.config.regular_op_cost))?; + Ok(()) + } + + pub fn finite_wasm_unstack(&mut self, operand_size: u64, frame_size: u64) -> Result<()> { + self.remaining_stack = self + .remaining_stack + .checked_add(operand_size.saturating_add(frame_size)) + .expect("remaining stack integer overflow"); + Ok(()) + } + + // ################# + // # Registers API # + // ################# + + /// Convenience function for testing. + #[cfg(test)] + pub fn wrapped_internal_write_register(&mut self, register_id: u64, data: &[u8]) -> Result<()> { + self.registers.set(&mut self.gas_counter, &self.config.limit_config, register_id, data) + } + + /// Writes the entire content from the register `register_id` into the memory of the guest starting with `ptr`. + /// + /// # Arguments + /// + /// * `register_id` -- a register id from where to read the data; + /// * `ptr` -- location on guest memory where to copy the data. + /// + /// # Errors + /// + /// * If the content extends outside the memory allocated to the guest. In Wasmer, it returns `MemoryAccessViolation` error message; + /// * If `register_id` is pointing to unused register returns `InvalidRegisterId` error message. + /// + /// # Undefined Behavior + /// + /// If the content of register extends outside the preallocated memory on the host side, or the pointer points to a + /// wrong location this function will overwrite memory that it is not supposed to overwrite causing an undefined behavior. + /// + /// # Cost + /// + /// `base + read_register_base + read_register_byte * num_bytes + write_memory_base + write_memory_byte * num_bytes` + pub fn read_register(&mut self, register_id: u64, ptr: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + let data = self.registers.get(&mut self.gas_counter, register_id)?; + self.memory.set(&mut self.gas_counter, ptr, data) + } + + /// Returns the size of the blob stored in the given register. + /// * If register is used, then returns the size, which can potentially be zero; + /// * If register is not used, returns `u64::MAX` + /// + /// # Arguments + /// + /// * `register_id` -- a register id from where to read the data; + /// + /// # Cost + /// + /// `base` + pub fn register_len(&mut self, register_id: u64) -> Result { + self.gas_counter.pay_base(base)?; + Ok(self.registers.get_len(register_id).unwrap_or(u64::MAX)) + } + + /// Copies `data` from the guest memory into the register. If register is unused will initialize + /// it. If register has larger capacity than needed for `data` will not re-allocate it. The + /// register will lose the pre-existing data if any. + /// + /// # Arguments + /// + /// * `register_id` -- a register id where to write the data; + /// * `data_len` -- length of the data in bytes; + /// * `data_ptr` -- pointer in the guest memory where to read the data from. + /// + /// # Cost + /// + /// `base + read_memory_base + read_memory_bytes * num_bytes + write_register_base + write_register_bytes * num_bytes` + pub fn write_register(&mut self, register_id: u64, data_len: u64, data_ptr: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + let data = + self.memory.view(&mut self.gas_counter, MemSlice { ptr: data_ptr, len: data_len })?; + self.registers.set(&mut self.gas_counter, &self.config.limit_config, register_id, data) + } + + // ################################### + // # String reading helper functions # + // ################################### + + /// Helper function to read and return utf8-encoding string. + /// If `len == u64::MAX` then treats the string as null-terminated with character `'\0'`. + /// + /// # Errors + /// + /// * If string extends outside the memory of the guest with `MemoryAccessViolation`; + /// * If string is not UTF-8 returns `BadUtf8`. + /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns + /// `TotalLogLengthExceeded`. + /// + /// # Cost + /// + /// For not nul-terminated string: + /// `read_memory_base + read_memory_byte * num_bytes + utf8_decoding_base + utf8_decoding_byte * num_bytes` + /// + /// For nul-terminated string: + /// `(read_memory_base + read_memory_byte) * num_bytes + utf8_decoding_base + utf8_decoding_byte * num_bytes` + fn get_utf8_string(&mut self, len: u64, ptr: u64) -> Result { + self.gas_counter.pay_base(utf8_decoding_base)?; + let mut buf; + let max_len = + self.config.limit_config.max_total_log_length.saturating_sub(self.total_log_length); + if len != u64::MAX { + if len > max_len { + return self.total_log_length_exceeded(len); + } + buf = self.memory.view(&mut self.gas_counter, MemSlice { ptr, len })?.into_owned(); + } else { + buf = vec![]; + for i in 0..=max_len { + // self.memory_get_u8 will check for u64 overflow on the first iteration (i == 0) + let el = self.memory.get_u8(&mut self.gas_counter, ptr + i)?; + if el == 0 { + break; + } + if i == max_len { + return self.total_log_length_exceeded(max_len.saturating_add(1)); + } + buf.push(el); + } + } + self.gas_counter.pay_per(utf8_decoding_byte, buf.len() as _)?; + String::from_utf8(buf).map_err(|_| HostError::BadUTF8.into()) + } + + /// Helper function to get utf8 string, for sandbox debug log. The difference with `get_utf8_string`: + /// * It's only available on sandbox node + /// * The cost is 0 + /// * It's up to the caller to set correct len + #[cfg(feature = "sandbox")] + fn sandbox_get_utf8_string(&mut self, len: u64, ptr: u64) -> Result { + let buf = self.memory.view_for_free(MemSlice { ptr, len })?.into_owned(); + String::from_utf8(buf).map_err(|_| HostError::BadUTF8.into()) + } + + /// Helper function to read UTF-16 formatted string from guest memory. + /// # Errors + /// + /// * If string extends outside the memory of the guest with `MemoryAccessViolation`; + /// * If string is not UTF-16 returns `BadUtf16`. + /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns + /// `TotalLogLengthExceeded`. + /// + /// # Cost + /// + /// For not nul-terminated string: + /// `read_memory_base + read_memory_byte * num_bytes + utf16_decoding_base + utf16_decoding_byte * num_bytes` + /// + /// For nul-terminated string: + /// `read_memory_base * num_bytes / 2 + read_memory_byte * num_bytes + utf16_decoding_base + utf16_decoding_byte * num_bytes` + fn get_utf16_string(&mut self, mut len: u64, ptr: u64) -> Result { + self.gas_counter.pay_base(utf16_decoding_base)?; + let max_len = + self.config.limit_config.max_total_log_length.saturating_sub(self.total_log_length); + + let mem_view = if len == u64::MAX { + len = self.get_nul_terminated_utf16_len(ptr, max_len)?; + self.memory.view_for_free(MemSlice { ptr, len }) + } else { + self.memory.view(&mut self.gas_counter, MemSlice { ptr, len }) + }?; + + let input = stdx::as_chunks_exact(&mem_view).map_err(|_| HostError::BadUTF16)?; + if len > max_len { + return self.total_log_length_exceeded(len); + } + + self.gas_counter.pay_per(utf16_decoding_byte, len)?; + char::decode_utf16(input.into_iter().copied().map(u16::from_le_bytes)) + .collect::>() + .map_err(|_| HostError::BadUTF16.into()) + } + + /// Helper function to get length of NUL-terminated UTF-16 formatted string + /// in guest memory. + /// + /// In other words, counts how many bytes are there to first pair of NUL + /// bytes. + fn get_nul_terminated_utf16_len(&mut self, ptr: u64, max_len: u64) -> Result { + let mut len = 0; + loop { + if self.memory.get_u16(&mut self.gas_counter, ptr.saturating_add(len))? == 0 { + return Ok(len); + } + len = match len.checked_add(2) { + Some(len) if len <= max_len => len, + Some(len) => return self.total_log_length_exceeded(len), + None => return self.total_log_length_exceeded(u64::MAX), + }; + } + } + + // #################################################### + // # Helper functions to prevent code duplication API # + // #################################################### + + /// Checks that the current log number didn't reach the limit yet, so we can add a new message. + fn check_can_add_a_log_message(&self) -> Result<()> { + if self.logs.len() as u64 >= self.config.limit_config.max_number_logs { + Err(HostError::NumberOfLogsExceeded { limit: self.config.limit_config.max_number_logs } + .into()) + } else { + Ok(()) + } + } + + /// Adds a given promise to the vector of promises and returns a new promise index. + /// Throws `NumberPromisesExceeded` if the total number of promises exceeded the limit. + fn checked_push_promise(&mut self, promise: Promise) -> Result { + let new_promise_idx = self.promises.len() as PromiseIndex; + self.promises.push(promise); + if self.promises.len() as u64 + > self.config.limit_config.max_promises_per_function_call_action + { + Err(HostError::NumberPromisesExceeded { + number_of_promises: self.promises.len() as u64, + limit: self.config.limit_config.max_promises_per_function_call_action, + } + .into()) + } else { + Ok(new_promise_idx) + } + } + + fn checked_push_log(&mut self, message: String) -> Result<()> { + // The size of logged data can't be too large. No overflow. + self.total_log_length += message.len() as u64; + if self.total_log_length > self.config.limit_config.max_total_log_length { + return self.total_log_length_exceeded(0); + } + self.logs.push(message); + Ok(()) + } + + fn total_log_length_exceeded(&self, add_len: u64) -> Result { + Err(HostError::TotalLogLengthExceeded { + length: self.total_log_length.saturating_add(add_len), + limit: self.config.limit_config.max_total_log_length, + } + .into()) + } + + fn get_public_key(&mut self, ptr: u64, len: u64) -> Result { + Ok(PublicKeyBuffer::new(&get_memory_or_register!(self, ptr, len)?)) + } + + // ############### + // # Context API # + // ############### + + /// Saves the account id of the current contract that we execute into the register. + /// + /// # Errors + /// + /// If the registers exceed the memory limit returns `MemoryAccessViolation`. + /// + /// # Cost + /// + /// `base + write_register_base + write_register_byte * num_bytes` + pub fn current_account_id(&mut self, register_id: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + self.registers.set( + &mut self.gas_counter, + &self.config.limit_config, + register_id, + self.context.current_account_id.as_bytes(), + ) + } + + /// All contract calls are a result of some transaction that was signed by some account using + /// some access key and submitted into a memory pool (either through the wallet using RPC or by + /// a node itself). This function returns the id of that account. Saves the bytes of the signer + /// account id into the register. + /// + /// # Errors + /// + /// * If the registers exceed the memory limit returns `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// + /// # Cost + /// + /// `base + write_register_base + write_register_byte * num_bytes` + pub fn signer_account_id(&mut self, register_id: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + + if self.context.is_view() { + return Err(HostError::ProhibitedInView { + method_name: "signer_account_id".to_string(), + } + .into()); + } + self.registers.set( + &mut self.gas_counter, + &self.config.limit_config, + register_id, + self.context.signer_account_id.as_bytes(), + ) + } + + /// Saves the public key fo the access key that was used by the signer into the register. In + /// rare situations smart contract might want to know the exact access key that was used to send + /// the original transaction, e.g. to increase the allowance or manipulate with the public key. + /// + /// # Errors + /// + /// * If the registers exceed the memory limit returns `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// + /// # Cost + /// + /// `base + write_register_base + write_register_byte * num_bytes` + pub fn signer_account_pk(&mut self, register_id: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + + if self.context.is_view() { + return Err(HostError::ProhibitedInView { + method_name: "signer_account_pk".to_string(), + } + .into()); + } + self.registers.set( + &mut self.gas_counter, + &self.config.limit_config, + register_id, + self.context.signer_account_pk.as_slice(), + ) + } + + /// All contract calls are a result of a receipt, this receipt might be created by a transaction + /// that does function invocation on the contract or another contract as a result of + /// cross-contract call. Saves the bytes of the predecessor account id into the register. + /// + /// # Errors + /// + /// * If the registers exceed the memory limit returns `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// + /// # Cost + /// + /// `base + write_register_base + write_register_byte * num_bytes` + pub fn predecessor_account_id(&mut self, register_id: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + + if self.context.is_view() { + return Err(HostError::ProhibitedInView { + method_name: "predecessor_account_id".to_string(), + } + .into()); + } + self.registers.set( + &mut self.gas_counter, + &self.config.limit_config, + register_id, + self.context.predecessor_account_id.as_bytes(), + ) + } + + /// Reads input to the contract call into the register. Input is expected to be in JSON-format. + /// If input is provided saves the bytes (potentially zero) of input into register. If input is + /// not provided writes 0 bytes into the register. + /// + /// # Cost + /// + /// `base + write_register_base + write_register_byte * num_bytes` + pub fn input(&mut self, register_id: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + + self.registers.set( + &mut self.gas_counter, + &self.config.limit_config, + register_id, + self.context.input.as_slice(), + ) + } + + /// Returns the current block height. + /// + /// It’s only due to historical reasons, this host function is called + /// `block_index` rather than `block_height`. + /// + /// # Cost + /// + /// `base` + pub fn block_index(&mut self) -> Result { + self.gas_counter.pay_base(base)?; + Ok(self.context.block_height) + } + + /// Returns the current block timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC). + /// + /// # Cost + /// + /// `base` + pub fn block_timestamp(&mut self) -> Result { + self.gas_counter.pay_base(base)?; + Ok(self.context.block_timestamp) + } + + /// Returns the current epoch height. + /// + /// # Cost + /// + /// `base` + pub fn epoch_height(&mut self) -> Result { + self.gas_counter.pay_base(base)?; + Ok(self.context.epoch_height) + } + + /// Get the stake of an account, if the account is currently a validator. Otherwise returns 0. + /// writes the value into the` u128` variable pointed by `stake_ptr`. + /// + /// # Cost + /// + /// `base + memory_write_base + memory_write_size * 16 + utf8_decoding_base + utf8_decoding_byte * account_id_len + validator_stake_base`. + pub fn validator_frozen( + &mut self, + account_id_len: u64, + account_id_ptr: u64, + frozen_ptr: u64, + ) -> Result<()> { + self.gas_counter.pay_base(base)?; + let account_id = self.read_and_parse_account_id(account_id_ptr, account_id_len)?; + self.gas_counter.pay_base(validator_frozen_base)?; + let frozen = self.ext.validator_frozen(&account_id)?.unwrap_or_default(); + self.memory.set_u128(&mut self.gas_counter, frozen_ptr, frozen) + } + + + pub fn validator_power( + &mut self, + account_id_len: u64, + account_id_ptr: u64, + power_ptr: u64, + ) -> Result<()> { + self.gas_counter.pay_base(base)?; + let account_id = self.read_and_parse_account_id(account_id_ptr, account_id_len)?; + self.gas_counter.pay_base(validator_power_base)?; + let power = self.ext.validator_power(&account_id)?.unwrap_or_default(); + self.memory.set_u64(&mut self.gas_counter, power_ptr, power) + } + + /// Get the total validator stake of the current epoch. + /// Write the u128 value into `stake_ptr`. + /// writes the value into the` u128` variable pointed by `stake_ptr`. + /// + /// # Cost + /// + /// `base + memory_write_base + memory_write_size * 16 + validator_total_stake_base` + /// + pub fn validator_total_frozen(&mut self, frozen_ptr: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + self.gas_counter.pay_base(validator_total_frozen_base)?; + let total_frozen = self.ext.validator_total_frozen()?; + self.memory.set_u128(&mut self.gas_counter, frozen_ptr, total_frozen) + } + pub fn validator_total_power(&mut self, power_ptr: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + self.gas_counter.pay_base(validator_total_power_base)?; + let total_power = self.ext.validator_total_power()?; + self.memory.set_u64(&mut self.gas_counter, power_ptr, total_power) + } + + /// Returns the number of bytes used by the contract if it was saved to the trie as of the + /// invocation. This includes: + /// * The data written with storage_* functions during current and previous execution; + /// * The bytes needed to store the access keys of the given account. + /// * The contract code size + /// * A small fixed overhead for account metadata. + /// + /// # Cost + /// + /// `base` + pub fn storage_usage(&mut self) -> Result { + self.gas_counter.pay_base(base)?; + Ok(self.current_storage_usage) + } + + // ################# + // # Economics API # + // ################# + + /// The current balance of the given account. This includes the attached_deposit that was + /// attached to the transaction. + /// + /// # Cost + /// + /// `base + memory_write_base + memory_write_size * 16` + pub fn account_balance(&mut self, balance_ptr: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + self.memory.set_u128(&mut self.gas_counter, balance_ptr, self.current_account_balance) + } + + /// The current amount of tokens locked due to staking. + /// + /// # Cost + /// + /// `base + memory_write_base + memory_write_size * 16` + pub fn account_locked_balance(&mut self, balance_ptr: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + self.memory.set_u128( + &mut self.gas_counter, + balance_ptr, + self.current_account_locked_balance, + ) + } + + /// The balance that was attached to the call that will be immediately deposited before the + /// contract execution starts. + /// + /// # Errors + /// + /// If called as view function returns `ProhibitedInView``. + /// + /// # Cost + /// + /// `base + memory_write_base + memory_write_size * 16` + pub fn attached_deposit(&mut self, balance_ptr: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + + self.memory.set_u128(&mut self.gas_counter, balance_ptr, self.context.attached_deposit) + } + + /// The amount of gas attached to the call that can be used to pay for the gas fees. + /// + /// # Errors + /// + /// If called as view function returns `ProhibitedInView`. + /// + /// # Cost + /// + /// `base` + pub fn prepaid_gas(&mut self) -> Result { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err( + HostError::ProhibitedInView { method_name: "prepaid_gas".to_string() }.into() + ); + } + Ok(self.context.prepaid_gas) + } + + /// The gas that was already burnt during the contract execution (cannot exceed `prepaid_gas`) + /// + /// # Errors + /// + /// If called as view function returns `ProhibitedInView`. + /// + /// # Cost + /// + /// `base` + pub fn used_gas(&mut self) -> Result { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err(HostError::ProhibitedInView { method_name: "used_gas".to_string() }.into()); + } + Ok(self.gas_counter.used_gas()) + } + + // ############ + // # Math API # + // ############ + + /// Computes multiexp on alt_bn128 curve using Pippenger's algorithm \sum_i + /// mul_i g_{1 i} should be equal result. + /// + /// # Arguments + /// + /// * `value` - sequence of (g1:G1, fr:Fr), where + /// G1 is point (x:Fq, y:Fq) on alt_bn128, + /// alt_bn128 is Y^2 = X^3 + 3 curve over Fq. + /// + /// `value` is encoded as packed, little-endian + /// `[((u256, u256), u256)]` slice. + /// + /// # Errors + /// + /// If `value_len + value_ptr` points outside the memory or the registers + /// use more memory than the limit, the function returns + /// `MemoryAccessViolation`. + /// + /// If point coordinates are not on curve, point is not in the subgroup, + /// scalar is not in the field or `value.len()%96!=0`, the function returns + /// `AltBn128InvalidInput`. + /// + /// # Cost + /// + /// `base + write_register_base + write_register_byte * num_bytes + + /// alt_bn128_g1_multiexp_base + + /// alt_bn128_g1_multiexp_element * num_elements` + pub fn alt_bn128_g1_multiexp( + &mut self, + value_len: u64, + value_ptr: u64, + register_id: u64, + ) -> Result<()> { + self.gas_counter.pay_base(alt_bn128_g1_multiexp_base)?; + let data = get_memory_or_register!(self, value_ptr, value_len)?; + + let elements = super::alt_bn128::split_elements(&data)?; + self.gas_counter.pay_per(alt_bn128_g1_multiexp_element, elements.len() as u64)?; + + let res = super::alt_bn128::g1_multiexp(elements)?; + + self.registers.set(&mut self.gas_counter, &self.config.limit_config, register_id, res) + } + + /// Computes sum for signed g1 group elements on alt_bn128 curve \sum_i + /// (-1)^{sign_i} g_{1 i} should be equal result. + /// + /// # Arguments + /// + /// * `value` - sequence of (sign:bool, g1:G1), where + /// G1 is point (x:Fq, y:Fq) on alt_bn128, + /// alt_bn128 is Y^2 = X^3 + 3 curve over Fq. + /// + /// `value` is encoded as packed, little-endian + /// `[(u8, (u256, u256))]` slice. `0u8` is postive sign, + /// `1u8` -- negative. + /// + /// # Errors + /// + /// If `value_len + value_ptr` points outside the memory or the registers + /// use more memory than the limit, the function returns `MemoryAccessViolation`. + /// + /// If point coordinates are not on curve, point is not in the subgroup, + /// scalar is not in the field, sign is not 0 or 1, or `value.len()%65!=0`, + /// the function returns `AltBn128InvalidInput`. + /// + /// # Cost + /// + /// `base + write_register_base + write_register_byte * num_bytes + + /// alt_bn128_g1_sum_base + alt_bn128_g1_sum_element * num_elements` + pub fn alt_bn128_g1_sum( + &mut self, + value_len: u64, + value_ptr: u64, + register_id: u64, + ) -> Result<()> { + self.gas_counter.pay_base(alt_bn128_g1_sum_base)?; + let data = get_memory_or_register!(self, value_ptr, value_len)?; + + let elements = super::alt_bn128::split_elements(&data)?; + self.gas_counter.pay_per(alt_bn128_g1_sum_element, elements.len() as u64)?; + + let res = super::alt_bn128::g1_sum(elements)?; + + self.registers.set(&mut self.gas_counter, &self.config.limit_config, register_id, res) + } + + /// Computes pairing check on alt_bn128 curve. + /// \sum_i e(g_{1 i}, g_{2 i}) should be equal one (in additive notation), e(g1, g2) is Ate pairing + /// + /// # Arguments + /// + /// * `value` - sequence of (g1:G1, g2:G2), where + /// G2 is Fr-ordered subgroup point (x:Fq2, y:Fq2) on alt_bn128 twist, + /// alt_bn128 twist is Y^2 = X^3 + 3/(i+9) curve over Fq2 + /// Fq2 is complex field element (re: Fq, im: Fq) + /// G1 is point (x:Fq, y:Fq) on alt_bn128, + /// alt_bn128 is Y^2 = X^3 + 3 curve over Fq + /// + /// `value` is encoded a as packed, little-endian + /// `[((u256, u256), ((u256, u256), (u256, u256)))]` slice. + /// + /// # Errors + /// + /// If `value_len + value_ptr` points outside the memory or the registers use more memory than + /// the function returns `MemoryAccessViolation`. + /// + /// If point coordinates are not on curve, point is not in the subgroup, scalar + /// is not in the field or data are wrong serialized, for example, + /// `value.len()%192!=0`, the function returns `AltBn128InvalidInput`. + /// + /// # Cost + /// + /// `base + write_register_base + write_register_byte * num_bytes + alt_bn128_pairing_base + alt_bn128_pairing_element * num_elements` + pub fn alt_bn128_pairing_check(&mut self, value_len: u64, value_ptr: u64) -> Result { + self.gas_counter.pay_base(alt_bn128_pairing_check_base)?; + let data = get_memory_or_register!(self, value_ptr, value_len)?; + + let elements = super::alt_bn128::split_elements(&data)?; + self.gas_counter.pay_per(alt_bn128_pairing_check_element, elements.len() as u64)?; + + let res = super::alt_bn128::pairing_check(elements)?; + + Ok(res as u64) + } + + /// Writes random seed into the register. + /// + /// # Errors + /// + /// If the size of the registers exceed the set limit `MemoryAccessViolation`. + /// + /// # Cost + /// + /// `base + write_register_base + write_register_byte * num_bytes`. + pub fn random_seed(&mut self, register_id: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + self.registers.set( + &mut self.gas_counter, + &self.config.limit_config, + register_id, + self.context.random_seed.as_slice(), + ) + } + + /// Hashes the given value using sha256 and returns it into `register_id`. + /// + /// # Errors + /// + /// If `value_len + value_ptr` points outside the memory or the registers use more memory than + /// the limit with `MemoryAccessViolation`. + /// + /// # Cost + /// + /// `base + write_register_base + write_register_byte * num_bytes + sha256_base + sha256_byte * num_bytes` + pub fn sha256(&mut self, value_len: u64, value_ptr: u64, register_id: u64) -> Result<()> { + self.gas_counter.pay_base(sha256_base)?; + let value = get_memory_or_register!(self, value_ptr, value_len)?; + self.gas_counter.pay_per(sha256_byte, value.len() as u64)?; + + use sha2::Digest; + + let value_hash = sha2::Sha256::digest(&value); + self.registers.set( + &mut self.gas_counter, + &self.config.limit_config, + register_id, + value_hash.as_slice(), + ) + } + + /// Hashes the given value using keccak256 and returns it into `register_id`. + /// + /// # Errors + /// + /// If `value_len + value_ptr` points outside the memory or the registers use more memory than + /// the limit with `MemoryAccessViolation`. + /// + /// # Cost + /// + /// `base + write_register_base + write_register_byte * num_bytes + keccak256_base + keccak256_byte * num_bytes` + pub fn keccak256(&mut self, value_len: u64, value_ptr: u64, register_id: u64) -> Result<()> { + self.gas_counter.pay_base(keccak256_base)?; + let value = get_memory_or_register!(self, value_ptr, value_len)?; + self.gas_counter.pay_per(keccak256_byte, value.len() as u64)?; + + use sha3::Digest; + + let value_hash = sha3::Keccak256::digest(&value); + self.registers.set( + &mut self.gas_counter, + &self.config.limit_config, + register_id, + value_hash.as_slice(), + ) + } + + /// Hashes the given value using keccak512 and returns it into `register_id`. + /// + /// # Errors + /// + /// If `value_len + value_ptr` points outside the memory or the registers use more memory than + /// the limit with `MemoryAccessViolation`. + /// + /// # Cost + /// + /// `base + write_register_base + write_register_byte * num_bytes + keccak512_base + keccak512_byte * num_bytes` + pub fn keccak512(&mut self, value_len: u64, value_ptr: u64, register_id: u64) -> Result<()> { + self.gas_counter.pay_base(keccak512_base)?; + let value = get_memory_or_register!(self, value_ptr, value_len)?; + self.gas_counter.pay_per(keccak512_byte, value.len() as u64)?; + + use sha3::Digest; + + let value_hash = sha3::Keccak512::digest(&value); + self.registers.set( + &mut self.gas_counter, + &self.config.limit_config, + register_id, + value_hash.as_slice(), + ) + } + + /// Hashes the given value using RIPEMD-160 and returns it into `register_id`. + /// + /// # Errors + /// + /// If `value_len + value_ptr` points outside the memory or the registers use more memory than + /// the limit with `MemoryAccessViolation`. + /// + /// # Cost + /// + /// Where `message_blocks` is `(value_len + 9).div_ceil(64)`. + /// + /// `base + write_register_base + write_register_byte * num_bytes + ripemd160_base + ripemd160_block * message_blocks` + pub fn ripemd160(&mut self, value_len: u64, value_ptr: u64, register_id: u64) -> Result<()> { + self.gas_counter.pay_base(ripemd160_base)?; + let value = get_memory_or_register!(self, value_ptr, value_len)?; + + let message_blocks = value + .len() + .checked_add(8) + .ok_or(VMLogicError::HostError(HostError::IntegerOverflow))? + / 64 + + 1; + + self.gas_counter.pay_per(ripemd160_block, message_blocks as u64)?; + + use ripemd::Digest; + + let value_hash = ripemd::Ripemd160::digest(&value); + self.registers.set( + &mut self.gas_counter, + &self.config.limit_config, + register_id, + value_hash.as_slice(), + ) + } + + /// Recovers an ECDSA signer address and returns it into `register_id`. + /// + /// Takes in an additional flag to check for malleability of the signature + /// which is generally only ideal for transactions. + /// + /// Returns a bool indicating success or failure as a `u64`. + /// + /// # Malleability Flags + /// + /// 0 - No extra checks. + /// 1 - Rejecting upper range. + /// + /// # Errors + /// + /// * If `hash_ptr`, `r_ptr`, or `s_ptr` point outside the memory or the registers use more + /// memory than the limit, then returns `MemoryAccessViolation`. + /// + /// # Cost + /// + /// `base + write_register_base + write_register_byte * 64 + ecrecover_base` + pub fn ecrecover( + &mut self, + hash_len: u64, + hash_ptr: u64, + sig_len: u64, + sig_ptr: u64, + v: u64, + malleability_flag: u64, + register_id: u64, + ) -> Result { + self.gas_counter.pay_base(ecrecover_base)?; + + let signature = { + let vec = get_memory_or_register!(self, sig_ptr, sig_len)?; + if vec.len() != 64 { + return Err(VMLogicError::HostError(HostError::ECRecoverError { + msg: format!( + "The length of the signature: {}, exceeds the limit of 64 bytes", + vec.len() + ), + })); + } + + let mut bytes = [0u8; 65]; + bytes[0..64].copy_from_slice(&vec); + + if v < 4 { + bytes[64] = v as u8; + Secp256K1Signature::from(bytes) + } else { + return Err(VMLogicError::HostError(HostError::ECRecoverError { + msg: format!("V recovery byte 0 through 3 are valid but was provided {}", v), + })); + } + }; + + let hash = { + let vec = get_memory_or_register!(self, hash_ptr, hash_len)?; + if vec.len() != 32 { + return Err(VMLogicError::HostError(HostError::ECRecoverError { + msg: format!( + "The length of the hash: {}, exceeds the limit of 32 bytes", + vec.len() + ), + })); + } + + let mut bytes = [0u8; 32]; + bytes.copy_from_slice(&vec); + bytes + }; + + if malleability_flag != 0 && malleability_flag != 1 { + return Err(VMLogicError::HostError(HostError::ECRecoverError { + msg: format!( + "Malleability flag needs to be 0 or 1, but is instead {}", + malleability_flag + ), + })); + } + + if !signature.check_signature_values(malleability_flag != 0) { + return Ok(false as u64); + } + + if let Ok(pk) = signature.recover(hash) { + self.registers.set( + &mut self.gas_counter, + &self.config.limit_config, + register_id, + pk.as_ref(), + )?; + return Ok(true as u64); + }; + + Ok(false as u64) + } + + /// Verify an ED25519 signature given a message and a public key. + /// + /// Returns a bool indicating success (1) or failure (0) as a `u64`. + /// + /// # Errors + /// + /// * If the public key's size is not equal to 32, or signature size is not + /// equal to 64, returns [HostError::Ed25519VerifyInvalidInput]. + /// * If any of the signature, message or public key arguments are out of + /// memory bounds, returns [`HostError::MemoryAccessViolation`] + /// + /// # Cost + /// + /// Each input can either be in memory or in a register. Set the length of + /// the input to `u64::MAX` to declare that the input is a register number + /// and not a pointer. Each input has a gas cost input_cost(num_bytes) that + /// depends on whether it is from memory or from a register. It is either + /// read_memory_base + num_bytes * read_memory_byte in the former case or + /// read_register_base + num_bytes * read_register_byte in the latter. This + /// function is labeled as `input_cost` below. + /// + /// `input_cost(num_bytes_signature) + input_cost(num_bytes_message) + + /// input_cost(num_bytes_public_key) + ed25519_verify_base + + /// ed25519_verify_byte * num_bytes_message` + pub fn ed25519_verify( + &mut self, + signature_len: u64, + signature_ptr: u64, + message_len: u64, + message_ptr: u64, + public_key_len: u64, + public_key_ptr: u64, + ) -> Result { + use ed25519_dalek::Verifier; + + self.gas_counter.pay_base(ed25519_verify_base)?; + + let signature: ed25519_dalek::Signature = { + let vec = get_memory_or_register!(self, signature_ptr, signature_len)?; + let b = <&[u8; ed25519_dalek::SIGNATURE_LENGTH]>::try_from(&vec[..]).map_err(|_| { + VMLogicError::HostError(HostError::Ed25519VerifyInvalidInput { + msg: "invalid signature length".to_string(), + }) + })?; + // Sanity-check that was performed by ed25519-dalek in from_bytes before version 2, + // but was removed with version 2. It is not actually any good a check, but we need + // it to avoid costs changing. + if b[ed25519_dalek::SIGNATURE_LENGTH - 1] & 0b1110_0000 != 0 { + return Ok(false as u64); + } + ed25519_dalek::Signature::from_bytes(b) + }; + + let message = get_memory_or_register!(self, message_ptr, message_len)?; + self.gas_counter.pay_per(ed25519_verify_byte, message.len() as u64)?; + + let public_key: ed25519_dalek::VerifyingKey = { + let vec = get_memory_or_register!(self, public_key_ptr, public_key_len)?; + let b = + <&[u8; ed25519_dalek::PUBLIC_KEY_LENGTH]>::try_from(&vec[..]).map_err(|_| { + VMLogicError::HostError(HostError::Ed25519VerifyInvalidInput { + msg: "invalid public key length".to_string(), + }) + })?; + match ed25519_dalek::VerifyingKey::from_bytes(b) { + Ok(public_key) => public_key, + Err(_) => return Ok(false as u64), + } + }; + + match public_key.verify(&message, &signature) { + Err(_) => Ok(false as u64), + Ok(()) => Ok(true as u64), + } + } + + /// Consume gas. Counts both towards `burnt_gas` and `used_gas`. + /// + /// # Errors + /// + /// * If passed gas amount somehow overflows internal gas counters returns `IntegerOverflow`; + /// * If we exceed usage limit imposed on burnt gas returns `GasLimitExceeded`; + /// * If we exceed the `prepaid_gas` then returns `GasExceeded`. + pub fn gas(&mut self, gas: Gas) -> Result<()> { + self.gas_counter.burn_gas(Gas::from(gas)) + } + + pub fn gas_opcodes(&mut self, opcodes: u32) -> Result<()> { + self.gas(opcodes as u64 * self.config.regular_op_cost as u64) + } + + /// This is the function that is exposed to WASM contracts under the name `gas`. + /// + /// For now it is consuming the gas for `gas` opcodes. When we switch to finite-wasm it’ll + /// be made to be a no-op. + /// + /// This function might be intrinsified. + pub fn gas_seen_from_wasm(&mut self, gas: u32) -> Result<()> { + self.gas_opcodes(gas) + } + + // ################ + // # Promises API # + // ################ + + /// A helper function to pay gas fee for creating a new receipt without actions. + /// # Args: + /// * `sir`: whether contract call is addressed to itself; + /// * `data_dependencies`: other contracts that this execution will be waiting on (or rather + /// their data receipts), where bool indicates whether this is sender=receiver communication. + /// + /// # Cost + /// + /// This is a convenience function that encapsulates several costs: + /// `burnt_gas := dispatch cost of the receipt + base dispatch cost cost of the data receipt` + /// `used_gas := burnt_gas + exec cost of the receipt + base exec cost cost of the data receipt` + /// Notice that we prepay all base cost upon the creation of the data dependency, we are going to + /// pay for the content transmitted through the dependency upon the actual creation of the + /// DataReceipt. + fn pay_gas_for_new_receipt(&mut self, sir: bool, data_dependencies: &[bool]) -> Result<()> { + let fees_config_cfg = &self.fees_config; + let mut burn_gas = fees_config_cfg.fee(ActionCosts::new_action_receipt).send_fee(sir); + let mut use_gas = fees_config_cfg.fee(ActionCosts::new_action_receipt).exec_fee(); + for dep in data_dependencies { + // Both creation and execution for data receipts are considered burnt gas. + burn_gas = burn_gas + .checked_add(fees_config_cfg.fee(ActionCosts::new_data_receipt_base).send_fee(*dep)) + .ok_or(HostError::IntegerOverflow)? + .checked_add(fees_config_cfg.fee(ActionCosts::new_data_receipt_base).exec_fee()) + .ok_or(HostError::IntegerOverflow)?; + } + use_gas = use_gas.checked_add(burn_gas).ok_or(HostError::IntegerOverflow)?; + // This should go to `new_data_receipt_base` and `new_action_receipt` in parts. + // But we have to keep charing these two together unless we make a protocol change. + self.gas_counter.pay_action_accumulated(burn_gas, use_gas, ActionCosts::new_action_receipt) + } + + /// A helper function to subtract balance on transfer or attached deposit for promises. + /// # Args: + /// * `amount`: the amount to deduct from the current account balance. + fn deduct_balance(&mut self, amount: Balance) -> Result<()> { + self.current_account_balance = + self.current_account_balance.checked_sub(amount).ok_or(HostError::BalanceExceeded)?; + Ok(()) + } + + /// Creates a promise that will execute a method on account with given arguments and attaches + /// the given amount and gas. `amount_ptr` point to slices of bytes representing `u128`. + /// + /// # Errors + /// + /// * If `account_id_len + account_id_ptr` or `method_name_len + method_name_ptr` or + /// `arguments_len + arguments_ptr` or `amount_ptr + 16` points outside the memory of the guest + /// or host returns `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// + /// # Returns + /// + /// Index of the new promise that uniquely identifies it within the current execution of the + /// method. + /// + /// # Cost + /// + /// Since `promise_create` is a convenience wrapper around `promise_batch_create` and + /// `promise_batch_action_function_call`. This also means it charges `base` cost twice. + pub fn promise_create( + &mut self, + account_id_len: u64, + account_id_ptr: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: Gas, + ) -> Result { + let new_promise_idx = self.promise_batch_create(account_id_len, account_id_ptr)?; + self.promise_batch_action_function_call( + new_promise_idx, + method_name_len, + method_name_ptr, + arguments_len, + arguments_ptr, + amount_ptr, + gas, + )?; + Ok(new_promise_idx) + } + + /// Attaches the callback that is executed after promise pointed by `promise_idx` is complete. + /// + /// # Errors + /// + /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`; + /// * If `account_id_len + account_id_ptr` or `method_name_len + method_name_ptr` or + /// `arguments_len + arguments_ptr` or `amount_ptr + 16` points outside the memory of the + /// guest or host returns `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// + /// # Returns + /// + /// Index of the new promise that uniquely identifies it within the current execution of the + /// method. + /// + /// # Cost + /// + /// Since `promise_create` is a convenience wrapper around `promise_batch_then` and + /// `promise_batch_action_function_call`. This also means it charges `base` cost twice. + pub fn promise_then( + &mut self, + promise_idx: u64, + account_id_len: u64, + account_id_ptr: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: u64, + ) -> Result { + let new_promise_idx = + self.promise_batch_then(promise_idx, account_id_len, account_id_ptr)?; + self.promise_batch_action_function_call( + new_promise_idx, + method_name_len, + method_name_ptr, + arguments_len, + arguments_ptr, + amount_ptr, + gas, + )?; + Ok(new_promise_idx) + } + + /// Creates a new promise which completes when time all promises passed as arguments complete. + /// Cannot be used with registers. `promise_idx_ptr` points to an array of `u64` elements, with + /// `promise_idx_count` denoting the number of elements. The array contains indices of promises + /// that need to be waited on jointly. + /// + /// # Errors + /// + /// * If `promise_ids_ptr + 8 * promise_idx_count` extend outside the guest memory returns + /// `MemoryAccessViolation`; + /// * If any of the promises in the array do not correspond to existing promises returns + /// `InvalidPromiseIndex`. + /// * If called as view function returns `ProhibitedInView`. + /// * If the total number of receipt dependencies exceeds `max_number_input_data_dependencies` + /// limit returns `NumInputDataDependenciesExceeded`. + /// * If the total number of promises exceeds `max_promises_per_function_call_action` limit + /// returns `NumPromisesExceeded`. + /// + /// # Returns + /// + /// Index of the new promise that uniquely identifies it within the current execution of the + /// method. + /// + /// # Cost + /// + /// `base + promise_and_base + promise_and_per_promise * num_promises + cost of reading promise ids from memory`. + pub fn promise_and( + &mut self, + promise_idx_ptr: u64, + promise_idx_count: u64, + ) -> Result { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err( + HostError::ProhibitedInView { method_name: "promise_and".to_string() }.into() + ); + } + self.gas_counter.pay_base(promise_and_base)?; + let memory_len = promise_idx_count + .checked_mul(size_of::() as u64) + .ok_or(HostError::IntegerOverflow)?; + self.gas_counter.pay_per(promise_and_per_promise, memory_len)?; + + // Read indices as little endian u64. + let promise_indices = self + .memory + .view(&mut self.gas_counter, MemSlice { ptr: promise_idx_ptr, len: memory_len })?; + let promise_indices = stdx::as_chunks_exact::<{ size_of::() }, u8>(&promise_indices) + .unwrap() + .into_iter() + .map(|bytes| u64::from_le_bytes(*bytes)); + + let mut receipt_dependencies = vec![]; + for promise_idx in promise_indices { + let promise = self + .promises + .get(promise_idx as usize) + .ok_or(HostError::InvalidPromiseIndex { promise_idx })?; + match &promise { + Promise::Receipt(receipt_idx) => { + receipt_dependencies.push(*receipt_idx); + } + Promise::NotReceipt(receipt_indices) => { + receipt_dependencies.extend(receipt_indices.clone()); + } + } + // Checking this in the loop to prevent abuse of too many joined vectors. + if receipt_dependencies.len() as u64 + > self.config.limit_config.max_number_input_data_dependencies + { + return Err(HostError::NumberInputDataDependenciesExceeded { + number_of_input_data_dependencies: receipt_dependencies.len() as u64, + limit: self.config.limit_config.max_number_input_data_dependencies, + } + .into()); + } + } + self.checked_push_promise(Promise::NotReceipt(receipt_dependencies)) + } + + /// Creates a new promise towards given `account_id` without any actions attached to it. + /// + /// # Errors + /// + /// * If `account_id_len + account_id_ptr` points outside the memory of the guest or host + /// returns `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// * If the total number of promises exceeds `max_promises_per_function_call_action` limit + /// returns `NumPromisesExceeded`. + /// + /// # Returns + /// + /// Index of the new promise that uniquely identifies it within the current execution of the + /// method. + /// + /// # Cost + /// + /// `burnt_gas := base + cost of reading and decoding the account id + dispatch cost of the receipt`. + /// `used_gas := burnt_gas + exec cost of the receipt`. + pub fn promise_batch_create( + &mut self, + account_id_len: u64, + account_id_ptr: u64, + ) -> Result { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err(HostError::ProhibitedInView { + method_name: "promise_batch_create".to_string(), + } + .into()); + } + let account_id = self.read_and_parse_account_id(account_id_ptr, account_id_len)?; + let sir = account_id == self.context.current_account_id; + self.pay_gas_for_new_receipt(sir, &[])?; + let new_receipt_idx = self.ext.create_receipt(vec![], account_id)?; + + self.checked_push_promise(Promise::Receipt(new_receipt_idx)) + } + + /// Creates a new promise towards given `account_id` without any actions attached, that is + /// executed after promise pointed by `promise_idx` is complete. + /// + /// # Errors + /// + /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`; + /// * If `account_id_len + account_id_ptr` points outside the memory of the guest or host + /// returns `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// * If the total number of promises exceeds `max_promises_per_function_call_action` limit + /// returns `NumPromisesExceeded`. + /// + /// # Returns + /// + /// Index of the new promise that uniquely identifies it within the current execution of the + /// method. + /// + /// # Cost + /// + /// `base + cost of reading and decoding the account id + dispatch&execution cost of the receipt + /// + dispatch&execution base cost for each data dependency` + pub fn promise_batch_then( + &mut self, + promise_idx: u64, + account_id_len: u64, + account_id_ptr: u64, + ) -> Result { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err(HostError::ProhibitedInView { + method_name: "promise_batch_then".to_string(), + } + .into()); + } + let account_id = self.read_and_parse_account_id(account_id_ptr, account_id_len)?; + // Update the DAG and return new promise idx. + let promise = self + .promises + .get(promise_idx as usize) + .ok_or(HostError::InvalidPromiseIndex { promise_idx })?; + let receipt_dependencies = match &promise { + Promise::Receipt(receipt_idx) => vec![*receipt_idx], + Promise::NotReceipt(receipt_indices) => receipt_indices.clone(), + }; + + let sir = account_id == self.context.current_account_id; + let deps: Vec<_> = receipt_dependencies + .iter() + .map(|&receipt_idx| self.ext.get_receipt_receiver(receipt_idx) == &account_id) + .collect(); + self.pay_gas_for_new_receipt(sir, &deps)?; + + let new_receipt_idx = self.ext.create_receipt(receipt_dependencies, account_id)?; + + self.checked_push_promise(Promise::Receipt(new_receipt_idx)) + } + + /// Helper function to return the receipt index corresponding to the given promise index. + /// It also pulls account ID for the given receipt and compares it with the current account ID + /// to return whether the receipt's account ID is the same. + fn promise_idx_to_receipt_idx_with_sir( + &self, + promise_idx: u64, + ) -> Result<(ReceiptIndex, bool)> { + let promise = self + .promises + .get(promise_idx as usize) + .ok_or(HostError::InvalidPromiseIndex { promise_idx })?; + let receipt_idx = match &promise { + Promise::Receipt(receipt_idx) => Ok(*receipt_idx), + Promise::NotReceipt(_) => Err(HostError::CannotAppendActionToJointPromise), + }?; + + let account_id = self.ext.get_receipt_receiver(receipt_idx); + let sir = account_id == &self.context.current_account_id; + Ok((receipt_idx, sir)) + } + + /// Appends `CreateAccount` action to the batch of actions for the given promise pointed by + /// `promise_idx`. + /// + /// # Errors + /// + /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`. + /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by + /// `promise_and` returns `CannotAppendActionToJointPromise`. + /// * If called as view function returns `ProhibitedInView`. + /// + /// # Cost + /// + /// `burnt_gas := base + dispatch action fee` + /// `used_gas := burnt_gas + exec action fee` + pub fn promise_batch_action_create_account(&mut self, promise_idx: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err(HostError::ProhibitedInView { + method_name: "promise_batch_action_create_account".to_string(), + } + .into()); + } + let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?; + + self.pay_action_base(ActionCosts::create_account, sir)?; + + self.ext.append_action_create_account(receipt_idx)?; + Ok(()) + } + + /// Appends `DeployContract` action to the batch of actions for the given promise pointed by + /// `promise_idx`. + /// + /// # Errors + /// + /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`. + /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by + /// `promise_and` returns `CannotAppendActionToJointPromise`. + /// * If `code_len + code_ptr` points outside the memory of the guest or host returns + /// `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// * If the contract code length exceeds `max_contract_size` returns `ContractSizeExceeded`. + /// + /// # Cost + /// + /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading vector from memory ` + /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes` + pub fn promise_batch_action_deploy_contract( + &mut self, + promise_idx: u64, + code_len: u64, + code_ptr: u64, + ) -> Result<()> { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err(HostError::ProhibitedInView { + method_name: "promise_batch_action_deploy_contract".to_string(), + } + .into()); + } + let code = get_memory_or_register!(self, code_ptr, code_len)?; + let code_len = code.len() as u64; + let limit = self.config.limit_config.max_contract_size; + if code_len > limit { + return Err(HostError::ContractSizeExceeded { size: code_len, limit }.into()); + } + let code = code.into_owned(); + + let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?; + + self.pay_action_base(ActionCosts::deploy_contract_base, sir)?; + self.pay_action_per_byte(ActionCosts::deploy_contract_byte, code_len, sir)?; + + self.ext.append_action_deploy_contract(receipt_idx, code)?; + Ok(()) + } + + /// Appends `FunctionCall` action to the batch of actions for the given promise pointed by + /// `promise_idx`. + /// + /// # Errors + /// + /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`. + /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by + /// `promise_and` returns `CannotAppendActionToJointPromise`. + /// * If `method_name_len + method_name_ptr` or `arguments_len + arguments_ptr` or + /// `amount_ptr + 16` points outside the memory of the guest or host returns + /// `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// + /// # Cost + /// + /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading vector from memory + /// + cost of reading u128, method_name and arguments from the memory` + /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes` + pub fn promise_batch_action_function_call( + &mut self, + promise_idx: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: Gas, + ) -> Result<()> { + self.promise_batch_action_function_call_weight( + promise_idx, + method_name_len, + method_name_ptr, + arguments_len, + arguments_ptr, + amount_ptr, + gas, + 0, + ) + } + + /// Appends `FunctionCall` action to the batch of actions for the given promise pointed by + /// `promise_idx`. This function allows not specifying a specific gas value and allowing the + /// runtime to assign remaining gas based on a weight. + /// + /// # Gas + /// + /// Gas can be specified using a static amount, a weight of remaining prepaid gas, or a mixture + /// of both. To omit a static gas amount, `0` can be passed for the `gas` parameter. + /// To omit assigning remaining gas, `0` can be passed as the `gas_weight` parameter. + /// + /// The gas weight parameter works as the following: + /// + /// All unused prepaid gas from the current function call is split among all function calls + /// which supply this gas weight. The amount attached to each respective call depends on the + /// value of the weight. + /// + /// For example, if 40 gas is leftover from the current method call and three functions specify + /// the weights 1, 5, 2 then 5, 25, 10 gas will be added to each function call respectively, + /// using up all remaining available gas. + /// + /// If the `gas_weight` parameter is set as a large value, the amount of distributed gas + /// to each action can be 0 or a very low value because the amount of gas per weight is + /// based on the floor division of the amount of gas by the sum of weights. + /// + /// Any remaining gas will be distributed to the last scheduled function call with a weight + /// specified. + /// + /// # Errors + /// + /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`. + /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by + /// `promise_and` returns `CannotAppendActionToJointPromise`. + /// * If `method_name_len + method_name_ptr` or `arguments_len + arguments_ptr` or + /// `amount_ptr + 16` points outside the memory of the guest or host returns + /// `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + pub fn promise_batch_action_function_call_weight( + &mut self, + promise_idx: u64, + method_name_len: u64, + method_name_ptr: u64, + arguments_len: u64, + arguments_ptr: u64, + amount_ptr: u64, + gas: Gas, + gas_weight: u64, + ) -> Result<()> { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err(HostError::ProhibitedInView { + method_name: "promise_batch_action_function_call".to_string(), + } + .into()); + } + let amount = self.memory.get_u128(&mut self.gas_counter, amount_ptr)?; + let method_name = get_memory_or_register!(self, method_name_ptr, method_name_len)?; + if method_name.is_empty() { + return Err(HostError::EmptyMethodName.into()); + } + let arguments = get_memory_or_register!(self, arguments_ptr, arguments_len)?; + + let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?; + + let method_name = method_name.into_owned(); + let arguments = arguments.into_owned(); + // Input can't be large enough to overflow + let num_bytes = method_name.len() as u64 + arguments.len() as u64; + self.pay_action_base(ActionCosts::function_call_base, sir)?; + self.pay_action_per_byte(ActionCosts::function_call_byte, num_bytes, sir)?; + // Prepaid gas + self.gas_counter.prepay_gas(gas)?; + + self.deduct_balance(amount)?; + + self.ext.append_action_function_call_weight( + receipt_idx, + method_name, + arguments, + amount, + gas, + GasWeight(gas_weight), + ) + } + + /// Appends `Transfer` action to the batch of actions for the given promise pointed by + /// `promise_idx`. + /// + /// # Errors + /// + /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`. + /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by + /// `promise_and` returns `CannotAppendActionToJointPromise`. + /// * If `amount_ptr + 16` points outside the memory of the guest or host returns + /// `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// + /// # Cost + /// + /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading u128 from memory ` + /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes` + pub fn promise_batch_action_transfer( + &mut self, + promise_idx: u64, + amount_ptr: u64, + ) -> Result<()> { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err(HostError::ProhibitedInView { + method_name: "promise_batch_action_transfer".to_string(), + } + .into()); + } + let amount = self.memory.get_u128(&mut self.gas_counter, amount_ptr)?; + + let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?; + let receiver_id = self.ext.get_receipt_receiver(receipt_idx); + let send_fee = transfer_send_fee( + self.fees_config, + sir, + self.config.implicit_account_creation, + self.config.eth_implicit_accounts, + receiver_id.get_account_type(), + ); + let exec_fee = transfer_exec_fee( + self.fees_config, + self.config.implicit_account_creation, + self.config.eth_implicit_accounts, + receiver_id.get_account_type(), + ); + let burn_gas = send_fee; + let use_gas = burn_gas.checked_add(exec_fee).ok_or(HostError::IntegerOverflow)?; + self.gas_counter.pay_action_accumulated(burn_gas, use_gas, ActionCosts::transfer)?; + + self.deduct_balance(amount)?; + + self.ext.append_action_transfer(receipt_idx, amount)?; + Ok(()) + } + + /// Appends `Stake` action to the batch of actions for the given promise pointed by + /// `promise_idx`. + /// + /// # Errors + /// + /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`. + /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by + /// `promise_and` returns `CannotAppendActionToJointPromise`. + /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`. + /// * If `amount_ptr + 16` or `public_key_len + public_key_ptr` points outside the memory of the + /// guest or host returns `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// + /// # Cost + /// + /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading public key from memory ` + /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes` + pub fn promise_batch_action_stake( + &mut self, + promise_idx: u64, + amount_ptr: u64, + public_key_len: u64, + public_key_ptr: u64, + ) -> Result<()> { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err(HostError::ProhibitedInView { + method_name: "promise_batch_action_stake".to_string(), + } + .into()); + } + let amount = self.memory.get_u128(&mut self.gas_counter, amount_ptr)?; + let public_key = self.get_public_key(public_key_ptr, public_key_len)?; + let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?; + self.pay_action_base(ActionCosts::stake, sir)?; + self.ext.append_action_stake(receipt_idx, amount, public_key.decode()?); + Ok(()) + } + + /// Appends `AddKey` action to the batch of actions for the given promise pointed by + /// `promise_idx`. The access key will have `FullAccess` permission. + /// + /// # Errors + /// + /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`. + /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by + /// `promise_and` returns `CannotAppendActionToJointPromise`. + /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`. + /// * If `public_key_len + public_key_ptr` points outside the memory of the guest or host + /// returns `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// + /// # Cost + /// + /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading public key from memory ` + /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes` + pub fn promise_batch_action_add_key_with_full_access( + &mut self, + promise_idx: u64, + public_key_len: u64, + public_key_ptr: u64, + nonce: u64, + ) -> Result<()> { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err(HostError::ProhibitedInView { + method_name: "promise_batch_action_add_key_with_full_access".to_string(), + } + .into()); + } + let public_key = self.get_public_key(public_key_ptr, public_key_len)?; + let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?; + self.pay_action_base(ActionCosts::add_full_access_key, sir)?; + self.ext.append_action_add_key_with_full_access(receipt_idx, public_key.decode()?, nonce); + Ok(()) + } + + /// Appends `AddKey` action to the batch of actions for the given promise pointed by + /// `promise_idx`. The access key will have `FunctionCall` permission. + /// + /// # Errors + /// + /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`. + /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by + /// `promise_and` returns `CannotAppendActionToJointPromise`. + /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`. + /// * If `public_key_len + public_key_ptr`, `allowance_ptr + 16`, + /// `receiver_id_len + receiver_id_ptr` or `method_names_len + method_names_ptr` points outside + /// the memory of the guest or host returns `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// + /// # Cost + /// + /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading vector from memory + /// + cost of reading u128, method_names and public key from the memory + cost of reading and parsing account name` + /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes` + pub fn promise_batch_action_add_key_with_function_call( + &mut self, + promise_idx: u64, + public_key_len: u64, + public_key_ptr: u64, + nonce: u64, + allowance_ptr: u64, + receiver_id_len: u64, + receiver_id_ptr: u64, + method_names_len: u64, + method_names_ptr: u64, + ) -> Result<()> { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err(HostError::ProhibitedInView { + method_name: "promise_batch_action_add_key_with_function_call".to_string(), + } + .into()); + } + let public_key = self.get_public_key(public_key_ptr, public_key_len)?; + let allowance = self.memory.get_u128(&mut self.gas_counter, allowance_ptr)?; + let allowance = if allowance > 0 { Some(allowance) } else { None }; + let receiver_id = self.read_and_parse_account_id(receiver_id_ptr, receiver_id_len)?; + let raw_method_names = get_memory_or_register!(self, method_names_ptr, method_names_len)?; + let method_names = split_method_names(&raw_method_names)?; + + let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?; + + // +1 is to account for null-terminating characters. + let num_bytes = method_names.iter().map(|v| v.len() as u64 + 1).sum::(); + self.pay_action_base(ActionCosts::add_function_call_key_base, sir)?; + self.pay_action_per_byte(ActionCosts::add_function_call_key_byte, num_bytes, sir)?; + + self.ext.append_action_add_key_with_function_call( + receipt_idx, + public_key.decode()?, + nonce, + allowance, + receiver_id, + method_names, + )?; + Ok(()) + } + + /// Appends `DeleteKey` action to the batch of actions for the given promise pointed by + /// `promise_idx`. + /// + /// # Errors + /// + /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`. + /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by + /// `promise_and` returns `CannotAppendActionToJointPromise`. + /// * If the given public key is not a valid (e.g. wrong length) returns `InvalidPublicKey`. + /// * If `public_key_len + public_key_ptr` points outside the memory of the guest or host + /// returns `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// + /// # Cost + /// + /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading public key from memory ` + /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes` + pub fn promise_batch_action_delete_key( + &mut self, + promise_idx: u64, + public_key_len: u64, + public_key_ptr: u64, + ) -> Result<()> { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err(HostError::ProhibitedInView { + method_name: "promise_batch_action_delete_key".to_string(), + } + .into()); + } + let public_key = self.get_public_key(public_key_ptr, public_key_len)?; + let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?; + self.pay_action_base(ActionCosts::delete_key, sir)?; + self.ext.append_action_delete_key(receipt_idx, public_key.decode()?); + Ok(()) + } + + /// Appends `DeleteAccount` action to the batch of actions for the given promise pointed by + /// `promise_idx`. + /// + /// # Errors + /// + /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`. + /// * If the promise pointed by the `promise_idx` is an ephemeral promise created by + /// `promise_and` returns `CannotAppendActionToJointPromise`. + /// * If `beneficiary_id_len + beneficiary_id_ptr` points outside the memory of the guest or + /// host returns `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// + /// # Cost + /// + /// `burnt_gas := base + dispatch action base fee + dispatch action per byte fee * num bytes + cost of reading and parsing account id from memory ` + /// `used_gas := burnt_gas + exec action base fee + exec action per byte fee * num bytes + fees for transferring funds to the beneficiary` + pub fn promise_batch_action_delete_account( + &mut self, + promise_idx: u64, + beneficiary_id_len: u64, + beneficiary_id_ptr: u64, + ) -> Result<()> { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err(HostError::ProhibitedInView { + method_name: "promise_batch_action_delete_account".to_string(), + } + .into()); + } + let beneficiary_id = + self.read_and_parse_account_id(beneficiary_id_ptr, beneficiary_id_len)?; + + let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?; + self.pay_action_base(ActionCosts::delete_account, sir)?; + + self.ext.append_action_delete_account(receipt_idx, beneficiary_id)?; + Ok(()) + } + + /// If the current function is invoked by a callback we can access the execution results of the + /// promises that caused the callback. This function returns the number of complete and + /// incomplete callbacks. + /// + /// Note, we are only going to have incomplete callbacks once we have promise_or combinator. + /// + /// + /// * If there is only one callback returns `1`; + /// * If there are multiple callbacks (e.g. created through `promise_and`) returns their number; + /// * If the function was called not through the callback returns `0`. + /// + /// # Cost + /// + /// `base` + pub fn promise_results_count(&mut self) -> Result { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err(HostError::ProhibitedInView { + method_name: "promise_results_count".to_string(), + } + .into()); + } + Ok(self.promise_results.len() as _) + } + + /// If the current function is invoked by a callback we can access the execution results of the + /// promises that caused the callback. This function returns the result in blob format and + /// places it into the register. + /// + /// * If promise result is complete and successful copies its blob into the register; + /// * If promise result is complete and failed or incomplete keeps register unused; + /// + /// # Returns + /// + /// * If promise result is not complete returns `0`; + /// * If promise result is complete and successful returns `1`; + /// * If promise result is complete and failed returns `2`. + /// + /// # Errors + /// + /// * If `result_id` does not correspond to an existing result returns `InvalidPromiseResultIndex`; + /// * If copying the blob exhausts the memory limit it returns `MemoryAccessViolation`. + /// * If called as view function returns `ProhibitedInView`. + /// + /// # Cost + /// + /// `base + cost of writing data into a register` + pub fn promise_result(&mut self, result_idx: u64, register_id: u64) -> Result { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err( + HostError::ProhibitedInView { method_name: "promise_result".to_string() }.into() + ); + } + match self + .promise_results + .get(result_idx as usize) + .ok_or(HostError::InvalidPromiseResultIndex { result_idx })? + { + PromiseResult::NotReady => Ok(0), + PromiseResult::Successful(data) => { + self.registers.set( + &mut self.gas_counter, + &self.config.limit_config, + register_id, + data.as_slice(), + )?; + Ok(1) + } + PromiseResult::Failed => Ok(2), + } + } + + /// When promise `promise_idx` finishes executing its result is considered to be the result of + /// the current function. + /// + /// # Errors + /// + /// * If `promise_idx` does not correspond to an existing promise returns `InvalidPromiseIndex`. + /// * If called as view function returns `ProhibitedInView`. + /// + /// # Cost + /// + /// `base + promise_return` + pub fn promise_return(&mut self, promise_idx: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + self.gas_counter.pay_base(promise_return)?; + if self.context.is_view() { + return Err( + HostError::ProhibitedInView { method_name: "promise_return".to_string() }.into() + ); + } + match self + .promises + .get(promise_idx as usize) + .ok_or(HostError::InvalidPromiseIndex { promise_idx })? + { + Promise::Receipt(receipt_idx) => { + self.return_data = ReturnData::ReceiptIndex(*receipt_idx); + Ok(()) + } + Promise::NotReceipt(_) => Err(HostError::CannotReturnJointPromise.into()), + } + } + + // ##################### + // # Miscellaneous API # + // ##################### + + /// Sets the blob of data as the return value of the contract. + /// + /// # Errors + /// + /// * If `value_len + value_ptr` exceeds the memory container or points to an unused register it + /// returns `MemoryAccessViolation`. + /// * if the length of the returned data exceeds `max_length_returned_data` returns + /// `ReturnedValueLengthExceeded`. + /// + /// # Cost + /// `base + cost of reading return value from memory or register + dispatch&exec cost per byte of the data sent * num data receivers` + pub fn value_return(&mut self, value_len: u64, value_ptr: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + let return_val = get_memory_or_register!(self, value_ptr, value_len)?; + let mut burn_gas: Gas = 0; + let num_bytes = return_val.len() as u64; + if num_bytes > self.config.limit_config.max_length_returned_data { + return Err(HostError::ReturnedValueLengthExceeded { + length: num_bytes, + limit: self.config.limit_config.max_length_returned_data, + } + .into()); + } + for data_receiver in &self.context.output_data_receivers { + let sir = data_receiver == &self.context.current_account_id; + // We deduct for execution here too, because if we later have an OR combinator + // for promises then we might have some valid data receipts that arrive too late + // to be picked up by the execution that waits on them (because it has started + // after it receives the first data receipt) and then we need to issue a special + // refund in this situation. Which we avoid by just paying for execution of + // data receipt that might not be performed. + // The gas here is considered burnt, cause we'll prepay for it upfront. + burn_gas = burn_gas + .checked_add( + self.fees_config + .fee(ActionCosts::new_data_receipt_byte) + .send_fee(sir) + .checked_add( + self.fees_config.fee(ActionCosts::new_data_receipt_byte).exec_fee(), + ) + .ok_or(HostError::IntegerOverflow)? + .checked_mul(num_bytes) + .ok_or(HostError::IntegerOverflow)?, + ) + .ok_or(HostError::IntegerOverflow)?; + } + self.gas_counter.pay_action_accumulated( + burn_gas, + burn_gas, + ActionCosts::new_data_receipt_byte, + )?; + self.return_data = ReturnData::Value(return_val.into_owned()); + Ok(()) + } + + /// Terminates the execution of the program with panic `GuestPanic`. + /// + /// # Cost + /// + /// `base` + pub fn panic(&mut self) -> Result<()> { + self.gas_counter.pay_base(base)?; + Err(HostError::GuestPanic { panic_msg: "explicit guest panic".to_string() }.into()) + } + + /// Guest panics with the UTF-8 encoded string. + /// If `len == u64::MAX` then treats the string as null-terminated with character `'\0'`. + /// + /// # Errors + /// + /// * If string extends outside the memory of the guest with `MemoryAccessViolation`; + /// * If string is not UTF-8 returns `BadUtf8`. + /// * If string is longer than `max_log_len` returns `TotalLogLengthExceeded`. + /// + /// # Cost + /// `base + cost of reading and decoding a utf8 string` + pub fn panic_utf8(&mut self, len: u64, ptr: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + Err(HostError::GuestPanic { panic_msg: self.get_utf8_string(len, ptr)? }.into()) + } + + /// Logs the UTF-8 encoded string. + /// If `len == u64::MAX` then treats the string as null-terminated with character `'\0'`. + /// + /// # Errors + /// + /// * If string extends outside the memory of the guest with `MemoryAccessViolation`; + /// * If string is not UTF-8 returns `BadUtf8`. + /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns + /// `TotalLogLengthExceeded`. + /// * If the total number of logs will exceed the `max_number_logs` returns + /// `NumberOfLogsExceeded`. + /// + /// # Cost + /// + /// `base + log_base + log_byte + num_bytes + utf8 decoding cost` + pub fn log_utf8(&mut self, len: u64, ptr: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + self.check_can_add_a_log_message()?; + let message = self.get_utf8_string(len, ptr)?; + self.gas_counter.pay_base(log_base)?; + self.gas_counter.pay_per(log_byte, message.len() as u64)?; + self.checked_push_log(message) + } + + /// Logs the UTF-16 encoded string. If `len == u64::MAX` then treats the string as + /// null-terminated with two-byte sequence of `0x00 0x00`. + /// + /// # Errors + /// + /// * If string extends outside the memory of the guest with `MemoryAccessViolation`; + /// * If string is not UTF-16 returns `BadUtf16`. + /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns + /// `TotalLogLengthExceeded`. + /// * If the total number of logs will exceed the `max_number_logs` returns + /// `NumberOfLogsExceeded`. + /// + /// # Cost + /// + /// `base + log_base + log_byte * num_bytes + utf16 decoding cost` + pub fn log_utf16(&mut self, len: u64, ptr: u64) -> Result<()> { + self.gas_counter.pay_base(base)?; + self.check_can_add_a_log_message()?; + let message = self.get_utf16_string(len, ptr)?; + self.gas_counter.pay_base(log_base)?; + // Let's not use `encode_utf16` for gas per byte here, since it's a lot of compute. + self.gas_counter.pay_per(log_byte, message.len() as u64)?; + self.checked_push_log(message) + } + + /// Special import kept for compatibility with AssemblyScript contracts. Not called by smart + /// contracts directly, but instead called by the code generated by AssemblyScript. + /// + /// # Errors + /// + /// * If string extends outside the memory of the guest with `MemoryAccessViolation`; + /// * If string is not UTF-8 returns `BadUtf8`. + /// * If number of bytes read + `total_log_length` exceeds the `max_total_log_length` returns + /// `TotalLogLengthExceeded`. + /// * If the total number of logs will exceed the `max_number_logs` returns + /// `NumberOfLogsExceeded`. + /// + /// # Cost + /// + /// `base + log_base + log_byte * num_bytes + utf16 decoding cost` + pub fn abort(&mut self, msg_ptr: u32, filename_ptr: u32, line: u32, col: u32) -> Result<()> { + self.gas_counter.pay_base(base)?; + if msg_ptr < 4 || filename_ptr < 4 { + return Err(HostError::BadUTF16.into()); + } + self.check_can_add_a_log_message()?; + + // Underflow checked above. + let msg_len = self.memory.get_u32(&mut self.gas_counter, (msg_ptr - 4) as u64)?; + let filename_len = self.memory.get_u32(&mut self.gas_counter, (filename_ptr - 4) as u64)?; + + let msg = self.get_utf16_string(msg_len as u64, msg_ptr as u64)?; + let filename = self.get_utf16_string(filename_len as u64, filename_ptr as u64)?; + + let message = format!("{}, filename: \"{}\" line: {} col: {}", msg, filename, line, col); + self.gas_counter.pay_base(log_base)?; + self.gas_counter.pay_per(log_byte, message.as_bytes().len() as u64)?; + self.checked_push_log(format!("ABORT: {}", message))?; + + Err(HostError::GuestPanic { panic_msg: message }.into()) + } + + // ############### + // # Storage API # + // ############### + + /// Reads account id from the given location in memory. + /// + /// # Errors + /// + /// * If account is not UTF-8 encoded then returns `BadUtf8`; + /// * If account is not valid then returns `InvalidAccountId`. + /// + /// # Cost + /// + /// This is a helper function that encapsulates the following costs: + /// cost of reading buffer from register or memory, + /// `utf8_decoding_base + utf8_decoding_byte * num_bytes`. + fn read_and_parse_account_id(&mut self, ptr: u64, len: u64) -> Result { + let buf = get_memory_or_register!(self, ptr, len)?; + self.gas_counter.pay_base(utf8_decoding_base)?; + self.gas_counter.pay_per(utf8_decoding_byte, buf.len() as u64)?; + + // We return an illegally constructed AccountId here for the sake of ensuring + // backwards compatibility. For paths previously involving validation, like receipts + // we retain validation further down the line in node-runtime/verifier.rs#fn(validate_receipt) + // mimicing previous behaviour. + let account_id = String::from_utf8(buf.into_owned()) + .map( + #[allow(deprecated)] + AccountId::new_unvalidated, + ) + .map_err(|_| HostError::BadUTF8)?; + Ok(account_id) + } + + /// Writes key-value into storage. + /// * If key is not in use it inserts the key-value pair and does not modify the register. Returns `0`; + /// * If key is in use it inserts the key-value and copies the old value into the `register_id`. Returns `1`. + /// + /// # Errors + /// + /// * If `key_len + key_ptr` or `value_len + value_ptr` exceeds the memory container or points + /// to an unused register it returns `MemoryAccessViolation`; + /// * If returning the preempted value into the registers exceed the memory container it returns + /// `MemoryAccessViolation`. + /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`. + /// * If the length of the value exceeds `max_length_storage_value` returns + /// `ValueLengthExceeded`. + /// * If called as view function returns `ProhibitedInView``. + /// + /// # Cost + /// + /// `base + storage_write_base + storage_write_key_byte * num_key_bytes + storage_write_value_byte * num_value_bytes + /// + get_vec_from_memory_or_register_cost x 2`. + /// + /// If a value was evicted it costs additional `storage_write_value_evicted_byte * num_evicted_bytes + internal_write_register_cost`. + pub fn storage_write( + &mut self, + key_len: u64, + key_ptr: u64, + value_len: u64, + value_ptr: u64, + register_id: u64, + ) -> Result { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err( + HostError::ProhibitedInView { method_name: "storage_write".to_string() }.into() + ); + } + self.gas_counter.pay_base(storage_write_base)?; + let key = get_memory_or_register!(self, key_ptr, key_len)?; + if key.len() as u64 > self.config.limit_config.max_length_storage_key { + return Err(HostError::KeyLengthExceeded { + length: key.len() as u64, + limit: self.config.limit_config.max_length_storage_key, + } + .into()); + } + let value = get_memory_or_register!(self, value_ptr, value_len)?; + if value.len() as u64 > self.config.limit_config.max_length_storage_value { + return Err(HostError::ValueLengthExceeded { + length: value.len() as u64, + limit: self.config.limit_config.max_length_storage_value, + } + .into()); + } + self.gas_counter.pay_per(storage_write_key_byte, key.len() as u64)?; + self.gas_counter.pay_per(storage_write_value_byte, value.len() as u64)?; + let nodes_before = self.ext.get_trie_nodes_count(); + // For storage write, we need to first perform a read on the key to calculate the TTN cost. + // This storage_get must be performed through trie instead of through FlatStorage + let evicted_ptr = self.ext.storage_get(&key, StorageGetMode::Trie)?; + let evicted = + Self::deref_value(&mut self.gas_counter, storage_write_evicted_byte, evicted_ptr)?; + let nodes_delta = self + .ext + .get_trie_nodes_count() + .checked_sub(&nodes_before) + .ok_or(InconsistentStateError::IntegerOverflow)?; + + #[cfg(feature = "io_trace")] + tracing::trace!( + target = "io_tracer", + storage_op = "write", + key = base64(&key), + size = value_len, + evicted_len = evicted.as_ref().map(Vec::len), + tn_mem_reads = nodes_delta.mem_reads, + tn_db_reads = nodes_delta.db_reads, + ); + + self.gas_counter.add_trie_fees(&nodes_delta)?; + self.ext.storage_set(&key, &value)?; + let storage_config = &self.fees_config.storage_usage_config; + match evicted { + Some(old_value) => { + // Inner value can't overflow, because the value length is limited. + self.current_storage_usage = self + .current_storage_usage + .checked_sub(old_value.len() as u64) + .ok_or(InconsistentStateError::IntegerOverflow)?; + // Inner value can't overflow, because the value length is limited. + self.current_storage_usage = self + .current_storage_usage + .checked_add(value.len() as u64) + .ok_or(InconsistentStateError::IntegerOverflow)?; + self.registers.set( + &mut self.gas_counter, + &self.config.limit_config, + register_id, + old_value, + )?; + Ok(1) + } + None => { + // Inner value can't overflow, because the key/value length is limited. + self.current_storage_usage = self + .current_storage_usage + .checked_add( + value.len() as u64 + + key.len() as u64 + + storage_config.num_extra_bytes_record, + ) + .ok_or(InconsistentStateError::IntegerOverflow)?; + Ok(0) + } + } + } + + fn deref_value<'s>( + gas_counter: &mut GasCounter, + cost_per_byte: ExtCosts, + value_ptr: Option>, + ) -> Result>> { + match value_ptr { + Some(value_ptr) => { + gas_counter.pay_per(cost_per_byte, value_ptr.len() as u64)?; + value_ptr.deref().map(Some) + } + None => Ok(None), + } + } + + /// Reads the value stored under the given key. + /// * If key is used copies the content of the value into the `register_id`, even if the content + /// is zero bytes. Returns `1`; + /// * If key is not present then does not modify the register. Returns `0`; + /// + /// # Errors + /// + /// * If `key_len + key_ptr` exceeds the memory container or points to an unused register it + /// returns `MemoryAccessViolation`; + /// * If returning the preempted value into the registers exceed the memory container it returns + /// `MemoryAccessViolation`. + /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`. + /// + /// # Cost + /// + /// `base + storage_read_base + storage_read_key_byte * num_key_bytes + storage_read_value_byte + num_value_bytes + /// cost to read key from register + cost to write value into register`. + pub fn storage_read(&mut self, key_len: u64, key_ptr: u64, register_id: u64) -> Result { + self.gas_counter.pay_base(base)?; + self.gas_counter.pay_base(storage_read_base)?; + let key = get_memory_or_register!(self, key_ptr, key_len)?; + if key.len() as u64 > self.config.limit_config.max_length_storage_key { + return Err(HostError::KeyLengthExceeded { + length: key.len() as u64, + limit: self.config.limit_config.max_length_storage_key, + } + .into()); + } + self.gas_counter.pay_per(storage_read_key_byte, key.len() as u64)?; + let nodes_before = self.ext.get_trie_nodes_count(); + let read = self.ext.storage_get(&key, self.config.storage_get_mode); + let nodes_delta = self + .ext + .get_trie_nodes_count() + .checked_sub(&nodes_before) + .ok_or(InconsistentStateError::IntegerOverflow)?; + self.gas_counter.add_trie_fees(&nodes_delta)?; + let read = Self::deref_value(&mut self.gas_counter, storage_read_value_byte, read?)?; + + #[cfg(feature = "io_trace")] + tracing::trace!( + target = "io_tracer", + storage_op = "read", + key = base64(&key), + size = read.as_ref().map(Vec::len), + tn_db_reads = nodes_delta.db_reads, + tn_mem_reads = nodes_delta.mem_reads, + ); + + match read { + Some(value) => { + self.registers.set( + &mut self.gas_counter, + &self.config.limit_config, + register_id, + value, + )?; + Ok(1) + } + None => Ok(0), + } + } + + /// Removes the value stored under the given key. + /// * If key is used, removes the key-value from the trie and copies the content of the value + /// into the `register_id`, even if the content is zero bytes. Returns `1`; + /// * If key is not present then does not modify the register. Returns `0`. + /// + /// # Errors + /// + /// * If `key_len + key_ptr` exceeds the memory container or points to an unused register it + /// returns `MemoryAccessViolation`; + /// * If the registers exceed the memory limit returns `MemoryAccessViolation`; + /// * If returning the preempted value into the registers exceed the memory container it returns + /// `MemoryAccessViolation`. + /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`. + /// * If called as view function returns `ProhibitedInView``. + /// + /// # Cost + /// + /// `base + storage_remove_base + storage_remove_key_byte * num_key_bytes + storage_remove_ret_value_byte * num_value_bytes + /// + cost to read the key + cost to write the value`. + pub fn storage_remove(&mut self, key_len: u64, key_ptr: u64, register_id: u64) -> Result { + self.gas_counter.pay_base(base)?; + if self.context.is_view() { + return Err( + HostError::ProhibitedInView { method_name: "storage_remove".to_string() }.into() + ); + } + self.gas_counter.pay_base(storage_remove_base)?; + let key = get_memory_or_register!(self, key_ptr, key_len)?; + if key.len() as u64 > self.config.limit_config.max_length_storage_key { + return Err(HostError::KeyLengthExceeded { + length: key.len() as u64, + limit: self.config.limit_config.max_length_storage_key, + } + .into()); + } + self.gas_counter.pay_per(storage_remove_key_byte, key.len() as u64)?; + let nodes_before = self.ext.get_trie_nodes_count(); + // To delete a key, we need to first perform a read on the key to calculate the TTN cost. + // This storage_get must be performed through trie instead of through FlatStorage + let removed_ptr = self.ext.storage_get(&key, StorageGetMode::Trie)?; + let removed = + Self::deref_value(&mut self.gas_counter, storage_remove_ret_value_byte, removed_ptr)?; + + self.ext.storage_remove(&key)?; + let nodes_delta = self + .ext + .get_trie_nodes_count() + .checked_sub(&nodes_before) + .ok_or(InconsistentStateError::IntegerOverflow)?; + + #[cfg(feature = "io_trace")] + tracing::trace!( + target = "io_tracer", + storage_op = "remove", + key = base64(&key), + evicted_len = removed.as_ref().map(Vec::len), + tn_mem_reads = nodes_delta.mem_reads, + tn_db_reads = nodes_delta.db_reads, + ); + + self.gas_counter.add_trie_fees(&nodes_delta)?; + let storage_config = &self.fees_config.storage_usage_config; + match removed { + Some(value) => { + // Inner value can't overflow, because the key/value length is limited. + self.current_storage_usage = self + .current_storage_usage + .checked_sub( + value.len() as u64 + + key.len() as u64 + + storage_config.num_extra_bytes_record, + ) + .ok_or(InconsistentStateError::IntegerOverflow)?; + self.registers.set( + &mut self.gas_counter, + &self.config.limit_config, + register_id, + value, + )?; + Ok(1) + } + None => Ok(0), + } + } + + /// Checks if there is a key-value pair. + /// * If key is used returns `1`, even if the value is zero bytes; + /// * Otherwise returns `0`. + /// + /// # Errors + /// + /// * If `key_len + key_ptr` exceeds the memory container it returns `MemoryAccessViolation`. + /// * If the length of the key exceeds `max_length_storage_key` returns `KeyLengthExceeded`. + /// + /// # Cost + /// + /// `base + storage_has_key_base + storage_has_key_byte * num_bytes + cost of reading key` + pub fn storage_has_key(&mut self, key_len: u64, key_ptr: u64) -> Result { + self.gas_counter.pay_base(base)?; + self.gas_counter.pay_base(storage_has_key_base)?; + let key = get_memory_or_register!(self, key_ptr, key_len)?; + if key.len() as u64 > self.config.limit_config.max_length_storage_key { + return Err(HostError::KeyLengthExceeded { + length: key.len() as u64, + limit: self.config.limit_config.max_length_storage_key, + } + .into()); + } + self.gas_counter.pay_per(storage_has_key_byte, key.len() as u64)?; + let nodes_before = self.ext.get_trie_nodes_count(); + let res = self.ext.storage_has_key(&key, self.config.storage_get_mode); + let nodes_delta = self + .ext + .get_trie_nodes_count() + .checked_sub(&nodes_before) + .ok_or(InconsistentStateError::IntegerOverflow)?; + + #[cfg(feature = "io_trace")] + tracing::trace!( + target = "io_tracer", + storage_op = "exists", + key = base64(&key), + tn_mem_reads = nodes_delta.mem_reads, + tn_db_reads = nodes_delta.db_reads, + ); + + self.gas_counter.add_trie_fees(&nodes_delta)?; + Ok(res? as u64) + } + + /// Debug print given utf-8 string to node log. It's only available in Sandbox node + /// + /// # Errors + /// + /// * If string is not UTF-8 returns `BadUtf8` + /// * If the log is over available memory in wasm runner, returns `MemoryAccessViolation` + /// + /// # Cost + /// + /// 0 + #[cfg(feature = "sandbox")] + pub fn sandbox_debug_log(&mut self, len: u64, ptr: u64) -> Result<()> { + let message = self.sandbox_get_utf8_string(len, ptr)?; + tracing::debug!(target: "sandbox", message = &message[..]); + Ok(()) + } + + /// DEPRECATED + /// Creates an iterator object inside the host. Returns the identifier that uniquely + /// differentiates the given iterator from other iterators that can be simultaneously created. + /// * It iterates over the keys that have the provided prefix. The order of iteration is defined + /// by the lexicographic order of the bytes in the keys; + /// * If there are no keys, it creates an empty iterator, see below on empty iterators. + /// + /// # Errors + /// + /// * If `prefix_len + prefix_ptr` exceeds the memory container it returns + /// `MemoryAccessViolation`. + /// * If the length of the prefix exceeds `max_length_storage_key` returns `KeyLengthExceeded`. + /// + /// # Cost + /// + /// `base + storage_iter_create_prefix_base + storage_iter_create_key_byte * num_prefix_bytes + /// cost of reading the prefix`. + pub fn storage_iter_prefix(&mut self, _prefix_len: u64, _prefix_ptr: u64) -> Result { + Err(VMLogicError::HostError(HostError::Deprecated { + method_name: "storage_iter_prefix".to_string(), + })) + } + + /// DEPRECATED + /// Iterates over all key-values such that keys are between `start` and `end`, where `start` is + /// inclusive and `end` is exclusive. Unless lexicographically `start < end`, it creates an + /// empty iterator. Note, this definition allows for `start` or `end` keys to not actually exist + /// on the given trie. + /// + /// # Errors + /// + /// * If `start_len + start_ptr` or `end_len + end_ptr` exceeds the memory container or points to + /// an unused register it returns `MemoryAccessViolation`. + /// * If the length of the `start` exceeds `max_length_storage_key` returns `KeyLengthExceeded`. + /// * If the length of the `end` exceeds `max_length_storage_key` returns `KeyLengthExceeded`. + /// + /// # Cost + /// + /// `base + storage_iter_create_range_base + storage_iter_create_from_byte * num_from_bytes + /// + storage_iter_create_to_byte * num_to_bytes + reading from prefix + reading to prefix`. + pub fn storage_iter_range( + &mut self, + _start_len: u64, + _start_ptr: u64, + _end_len: u64, + _end_ptr: u64, + ) -> Result { + Err(VMLogicError::HostError(HostError::Deprecated { + method_name: "storage_iter_range".to_string(), + })) + } + + /// DEPRECATED + /// Advances iterator and saves the next key and value in the register. + /// * If iterator is not empty (after calling next it points to a key-value), copies the key + /// into `key_register_id` and value into `value_register_id` and returns `1`; + /// * If iterator is empty returns `0`; + /// This allows us to iterate over the keys that have zero bytes stored in values. + /// + /// # Errors + /// + /// * If `key_register_id == value_register_id` returns `MemoryAccessViolation`; + /// * If the registers exceed the memory limit returns `MemoryAccessViolation`; + /// * If `iterator_id` does not correspond to an existing iterator returns `InvalidIteratorId`; + /// * If between the creation of the iterator and calling `storage_iter_next` the range over + /// which it iterates was modified returns `IteratorWasInvalidated`. Specifically, if + /// `storage_write` or `storage_remove` was invoked on the key key such that: + /// * in case of `storage_iter_prefix`. `key` has the given prefix and: + /// * Iterator was not called next yet. + /// * `next` was already called on the iterator and it is currently pointing at the `key` + /// `curr` such that `curr <= key`. + /// * in case of `storage_iter_range`. `start<=key Result { + Err(VMLogicError::HostError(HostError::Deprecated { + method_name: "storage_iter_next".to_string(), + })) + } + + /// Computes the outcome of the execution. + /// + /// If `FunctionCallWeight` protocol feature (127) is enabled, unused gas will be + /// distributed to functions that specify a gas weight. If there are no functions with + /// a gas weight, the outcome will contain unused gas as usual. + pub fn compute_outcome(self) -> VMOutcome { + let burnt_gas = self.gas_counter.burnt_gas(); + let used_gas = self.gas_counter.used_gas(); + + let mut profile = self.gas_counter.profile_data(); + profile.compute_wasm_instruction_cost(burnt_gas); + let compute_usage = profile.total_compute_usage(&self.config.ext_costs); + + VMOutcome { + balance: self.current_account_balance, + storage_usage: self.current_storage_usage, + return_data: self.return_data, + burnt_gas, + used_gas, + compute_usage, + logs: self.logs, + profile, + aborted: None, + } + } + + /// Add a cost for loading the contract code in the VM. + /// + /// This cost does not consider the structure of the contract code, only the + /// size. This is currently the only loading fee. A fee that takes the code + /// structure into consideration could be added. But since that would have + /// to happen after loading, we cannot pre-charge it. This is the main + /// motivation to (only) have this simple fee. + pub fn add_contract_loading_fee(&mut self, code_len: u64) -> Result<()> { + self.gas_counter.pay_per(contract_loading_bytes, code_len)?; + self.gas_counter.pay_base(contract_loading_base) + } + + /// Gets pointer to the fast gas counter. + pub fn gas_counter_pointer(&mut self) -> *mut FastGasCounter { + self.gas_counter.gas_counter_raw_ptr() + } + + /// Properly handles gas limit exceeded error. + pub fn process_gas_limit(&mut self) -> HostError { + let new_burn_gas = self.gas_counter.burnt_gas(); + let new_used_gas = self.gas_counter.used_gas(); + self.gas_counter.process_gas_limit(new_burn_gas, new_used_gas) + } + + /// A helper function to pay base cost gas fee for batching an action. + pub fn pay_action_base(&mut self, action: ActionCosts, sir: bool) -> Result<()> { + let base_fee = self.fees_config.fee(action); + let burn_gas = base_fee.send_fee(sir); + let use_gas = + burn_gas.checked_add(base_fee.exec_fee()).ok_or(HostError::IntegerOverflow)?; + self.gas_counter.pay_action_accumulated(burn_gas, use_gas, action) + } + + /// A helper function to pay per byte gas fee for batching an action. + pub fn pay_action_per_byte( + &mut self, + action: ActionCosts, + num_bytes: u64, + sir: bool, + ) -> Result<()> { + let per_byte_fee = self.fees_config.fee(action); + let burn_gas = + num_bytes.checked_mul(per_byte_fee.send_fee(sir)).ok_or(HostError::IntegerOverflow)?; + let use_gas = burn_gas + .checked_add( + num_bytes.checked_mul(per_byte_fee.exec_fee()).ok_or(HostError::IntegerOverflow)?, + ) + .ok_or(HostError::IntegerOverflow)?; + self.gas_counter.pay_action_accumulated(burn_gas, use_gas, action) + } + + /// VM independent setup before loading the executable. + /// + /// Does VM independent checks that happen after the instantiation of + /// VMLogic but before loading the executable. This includes pre-charging gas + /// costs for loading the executable, which depends on the size of the WASM code. + pub fn before_loading_executable( + &mut self, + method_name: &str, + wasm_code_bytes: usize, + ) -> std::result::Result<(), super::errors::FunctionCallError> { + if method_name.is_empty() { + let error = super::errors::FunctionCallError::MethodResolveError( + super::errors::MethodResolveError::MethodEmptyName, + ); + return Err(error); + } + if self.config.fix_contract_loading_cost { + if self.add_contract_loading_fee(wasm_code_bytes as u64).is_err() { + let error = + super::errors::FunctionCallError::HostError(super::HostError::GasExceeded); + return Err(error); + } + } + Ok(()) + } + + /// Legacy code to preserve old gas charging behaviour in old protocol versions. + pub fn after_loading_executable( + &mut self, + wasm_code_bytes: usize, + ) -> std::result::Result<(), super::errors::FunctionCallError> { + if !self.config.fix_contract_loading_cost { + if self.add_contract_loading_fee(wasm_code_bytes as u64).is_err() { + return Err(super::errors::FunctionCallError::HostError( + super::HostError::GasExceeded, + )); + } + } + Ok(()) + } +} + +#[derive(PartialEq)] +pub struct VMOutcome { + pub balance: Balance, + pub storage_usage: StorageUsage, + pub return_data: ReturnData, + pub burnt_gas: Gas, + pub used_gas: Gas, + pub compute_usage: Compute, + pub logs: Vec, + /// Data collected from making a contract call + pub profile: ProfileDataV3, + pub aborted: Option, +} + +impl VMOutcome { + /// Consumes the `VMLogic` object and computes the final outcome with the + /// given error that stopped execution from finishing successfully. + pub fn abort(logic: VMLogic, error: FunctionCallError) -> VMOutcome { + let mut outcome = logic.compute_outcome(); + outcome.aborted = Some(error); + outcome + } + + /// Consumes the `VMLogic` object and computes the final outcome for a + /// successful execution. + pub fn ok(logic: VMLogic) -> VMOutcome { + logic.compute_outcome() + } + + /// Creates an outcome with a no-op outcome. + pub fn nop_outcome(error: FunctionCallError) -> VMOutcome { + VMOutcome { + // Note: Balance and storage fields are ignored on a failed outcome. + balance: 0, + storage_usage: 0, + // Note: Fields below are added or merged when processing the + // outcome. With 0 or the empty set, those are no-ops. + return_data: ReturnData::None, + burnt_gas: 0, + used_gas: 0, + compute_usage: 0, + logs: Vec::new(), + profile: ProfileDataV3::default(), + aborted: Some(error), + } + } + + /// Like `Self::abort()` but without feature `FixContractLoadingCost` it + /// will return a NOP outcome. This is used for backwards-compatibility only. + pub fn abort_but_nop_outcome_in_old_protocol( + logic: VMLogic, + error: FunctionCallError, + ) -> VMOutcome { + if logic.config.fix_contract_loading_cost { + Self::abort(logic, error) + } else { + Self::nop_outcome(error) + } + } +} + +impl std::fmt::Debug for VMOutcome { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let return_data_str = match &self.return_data { + ReturnData::None => "None".to_string(), + ReturnData::ReceiptIndex(_) => "Receipt".to_string(), + ReturnData::Value(v) => format!("Value [{} bytes]", v.len()), + }; + write!( + f, + "VMOutcome: balance {} storage_usage {} return data {} burnt gas {} used gas {}", + self.balance, self.storage_usage, return_data_str, self.burnt_gas, self.used_gas + )?; + if let Some(err) = &self.aborted { + write!(f, " failed with {err}")?; + } + Ok(()) + } +} diff --git a/runtime/unc-vm-runner/src/logic/mocks/mock_external.rs b/runtime/unc-vm-runner/src/logic/mocks/mock_external.rs new file mode 100644 index 000000000..2cf06a513 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/mocks/mock_external.rs @@ -0,0 +1,288 @@ +use crate::logic::types::ReceiptIndex; +use crate::logic::TrieNodesCount; +use crate::logic::{External, StorageGetMode, ValuePtr}; +use unc_primitives_core::hash::{hash, CryptoHash}; +use unc_primitives_core::types::{AccountId, Balance, Gas, GasWeight, Power}; +use std::collections::HashMap; + +#[derive(serde::Serialize)] +#[serde(remote = "GasWeight")] +struct GasWeightSer(u64); + +#[derive(Debug, Clone, serde::Serialize)] +pub enum MockAction { + CreateReceipt { + receipt_indices: Vec, + receiver_id: AccountId, + }, + CreateAccount { + receipt_index: ReceiptIndex, + }, + DeployContract { + receipt_index: ReceiptIndex, + code: Vec, + }, + FunctionCallWeight { + receipt_index: ReceiptIndex, + method_name: Vec, + args: Vec, + attached_deposit: u128, + prepaid_gas: Gas, + #[serde(with = "GasWeightSer")] + gas_weight: GasWeight, + }, + Transfer { + receipt_index: ReceiptIndex, + deposit: u128, + }, + Stake { + receipt_index: ReceiptIndex, + stake: u128, + public_key: unc_crypto::PublicKey, + }, + DeleteAccount { + receipt_index: ReceiptIndex, + beneficiary_id: AccountId, + }, + DeleteKey { + receipt_index: ReceiptIndex, + public_key: unc_crypto::PublicKey, + }, + AddKeyWithFunctionCall { + receipt_index: ReceiptIndex, + public_key: unc_crypto::PublicKey, + nonce: u64, + allowance: Option, + receiver_id: AccountId, + method_names: Vec>, + }, + AddKeyWithFullAccess { + receipt_index: ReceiptIndex, + public_key: unc_crypto::PublicKey, + nonce: u64, + }, +} + +#[derive(Default, Clone)] +/// Emulates the trie and the mock handling code. +pub struct MockedExternal { + pub fake_trie: HashMap, Vec>, + pub validators: HashMap, + pub action_log: Vec, + data_count: u64, +} + +pub struct MockedValuePtr { + value: Vec, +} + +impl MockedValuePtr { + pub fn new(value: T) -> Self + where + T: AsRef<[u8]>, + { + MockedValuePtr { value: value.as_ref().to_vec() } + } +} + +impl ValuePtr for MockedValuePtr { + fn len(&self) -> u32 { + self.value.len() as u32 + } + + fn deref(&self) -> crate::logic::dependencies::Result> { + Ok(self.value.clone()) + } +} + +impl MockedExternal { + pub fn new() -> Self { + Self::default() + } +} + +use crate::logic::dependencies::Result; + +impl External for MockedExternal { + fn storage_set(&mut self, key: &[u8], value: &[u8]) -> Result<()> { + self.fake_trie.insert(key.to_vec(), value.to_vec()); + Ok(()) + } + + fn storage_get(&self, key: &[u8], _mode: StorageGetMode) -> Result>> { + Ok(self + .fake_trie + .get(key) + .map(|value| Box::new(MockedValuePtr { value: value.clone() }) as Box<_>)) + } + + fn storage_remove(&mut self, key: &[u8]) -> Result<()> { + self.fake_trie.remove(key); + Ok(()) + } + + fn storage_remove_subtree(&mut self, prefix: &[u8]) -> Result<()> { + self.fake_trie.retain(|key, _| !key.starts_with(prefix)); + Ok(()) + } + + fn storage_has_key(&mut self, key: &[u8], _mode: StorageGetMode) -> Result { + Ok(self.fake_trie.contains_key(key)) + } + + fn generate_data_id(&mut self) -> CryptoHash { + // Generates some hash for the data ID to receive data. This hash should not be functionally + // used in any mocked contexts. + let data_id = hash(&self.data_count.to_le_bytes()); + self.data_count += 1; + data_id + } + + fn get_trie_nodes_count(&self) -> TrieNodesCount { + TrieNodesCount { db_reads: 0, mem_reads: 0 } + } + + fn validator_frozen(&self, account_id: &AccountId) -> Result> { + if let Some((_power, balance)) = self.validators.get(account_id) { + Ok(Some(balance).cloned()) + } else { + Ok(None) + } + } + + fn validator_total_frozen(&self) -> Result { + let total_frozen: Balance = self.validators.values().map(|(_, frozen)| frozen).sum(); + Ok(total_frozen) + } + + fn validator_power(&self, account_id: &AccountId) -> Result> { + if let Some((power, _balance)) = self.validators.get(account_id) { + Ok(Some(power).cloned()) + } else { + Ok(None) + } + } + + fn validator_total_power(&self) -> Result { + let total_power: Power = self.validators.values().map(|(power, _)| power).sum(); + Ok(total_power) + } + + fn create_receipt( + &mut self, + receipt_indices: Vec, + receiver_id: AccountId, + ) -> Result { + let index = self.action_log.len(); + self.action_log.push(MockAction::CreateReceipt { receipt_indices, receiver_id }); + Ok(index as u64) + } + + fn append_action_create_account( + &mut self, + receipt_index: ReceiptIndex, + ) -> Result<(), crate::logic::VMLogicError> { + self.action_log.push(MockAction::CreateAccount { receipt_index }); + Ok(()) + } + + fn append_action_deploy_contract( + &mut self, + receipt_index: ReceiptIndex, + code: Vec, + ) -> Result<(), crate::logic::VMLogicError> { + self.action_log.push(MockAction::DeployContract { receipt_index, code }); + Ok(()) + } + + fn append_action_function_call_weight( + &mut self, + receipt_index: ReceiptIndex, + method_name: Vec, + args: Vec, + attached_deposit: Balance, + prepaid_gas: Gas, + gas_weight: GasWeight, + ) -> Result<(), crate::logic::VMLogicError> { + self.action_log.push(MockAction::FunctionCallWeight { + receipt_index, + method_name, + args, + attached_deposit, + prepaid_gas, + gas_weight, + }); + Ok(()) + } + + fn append_action_transfer( + &mut self, + receipt_index: ReceiptIndex, + deposit: Balance, + ) -> Result<(), crate::logic::VMLogicError> { + self.action_log.push(MockAction::Transfer { receipt_index, deposit }); + Ok(()) + } + + fn append_action_stake( + &mut self, + receipt_index: ReceiptIndex, + stake: Balance, + public_key: unc_crypto::PublicKey, + ) { + self.action_log.push(MockAction::Stake { receipt_index, stake, public_key }); + } + + fn append_action_add_key_with_full_access( + &mut self, + receipt_index: ReceiptIndex, + public_key: unc_crypto::PublicKey, + nonce: unc_primitives_core::types::Nonce, + ) { + self.action_log.push(MockAction::AddKeyWithFullAccess { receipt_index, public_key, nonce }); + } + + fn append_action_add_key_with_function_call( + &mut self, + receipt_index: ReceiptIndex, + public_key: unc_crypto::PublicKey, + nonce: unc_primitives_core::types::Nonce, + allowance: Option, + receiver_id: AccountId, + method_names: Vec>, + ) -> Result<(), crate::logic::VMLogicError> { + self.action_log.push(MockAction::AddKeyWithFunctionCall { + receipt_index, + public_key, + nonce, + allowance, + receiver_id, + method_names, + }); + Ok(()) + } + + fn append_action_delete_key( + &mut self, + receipt_index: ReceiptIndex, + public_key: unc_crypto::PublicKey, + ) { + self.action_log.push(MockAction::DeleteKey { receipt_index, public_key }); + } + + fn append_action_delete_account( + &mut self, + receipt_index: ReceiptIndex, + beneficiary_id: AccountId, + ) -> Result<(), crate::logic::VMLogicError> { + self.action_log.push(MockAction::DeleteAccount { receipt_index, beneficiary_id }); + Ok(()) + } + + fn get_receipt_receiver(&self, receipt_index: ReceiptIndex) -> &AccountId { + match &self.action_log[receipt_index as usize] { + MockAction::CreateReceipt { receiver_id, .. } => receiver_id, + _ => panic!("not a valid receipt index!"), + } + } +} diff --git a/runtime/unc-vm-runner/src/logic/mocks/mock_memory.rs b/runtime/unc-vm-runner/src/logic/mocks/mock_memory.rs new file mode 100644 index 000000000..94d4b1354 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/mocks/mock_memory.rs @@ -0,0 +1,51 @@ +use crate::logic::{MemSlice, MemoryLike}; + +use std::borrow::Cow; + +pub struct MockedMemory(Box<[u8]>); + +impl MockedMemory { + pub const MEMORY_SIZE: u64 = 64 * 1024; +} + +impl Default for MockedMemory { + fn default() -> Self { + Self(vec![0; Self::MEMORY_SIZE as usize].into()) + } +} + +fn make_range(ptr: u64, len: usize) -> Result, ()> { + let start = usize::try_from(ptr).map_err(|_| ())?; + let end = start.checked_add(len).ok_or(())?; + Ok(start..end) +} + +impl MemoryLike for MockedMemory { + fn fits_memory(&self, slice: MemSlice) -> Result<(), ()> { + match self.0.get(slice.range::()?) { + Some(_) => Ok(()), + None => Err(()), + } + } + + fn view_memory(&self, slice: MemSlice) -> Result, ()> { + self.0.get(slice.range::()?).map(Cow::Borrowed).ok_or(()) + } + + fn read_memory(&self, ptr: u64, buffer: &mut [u8]) -> Result<(), ()> { + let slice = self.0.get(make_range(ptr, buffer.len())?).ok_or(())?; + buffer.copy_from_slice(slice); + Ok(()) + } + + fn write_memory(&mut self, ptr: u64, buffer: &[u8]) -> Result<(), ()> { + let slice = self.0.get_mut(make_range(ptr, buffer.len())?).ok_or(())?; + slice.copy_from_slice(buffer); + Ok(()) + } +} + +#[test] +fn test_memory_like() { + crate::logic::test_utils::test_memory_like(|| Box::::default()); +} diff --git a/runtime/unc-vm-runner/src/logic/mocks/mod.rs b/runtime/unc-vm-runner/src/logic/mocks/mod.rs new file mode 100644 index 000000000..438a98567 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/mocks/mod.rs @@ -0,0 +1,2 @@ +pub mod mock_external; +pub mod mock_memory; diff --git a/runtime/unc-vm-runner/src/logic/mod.rs b/runtime/unc-vm-runner/src/logic/mod.rs new file mode 100644 index 000000000..47b70cd93 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/mod.rs @@ -0,0 +1,85 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_primitives_core::hash::CryptoHash; +use std::fmt; +use types::AccountId; + +mod alt_bn128; +mod context; +mod dependencies; +pub mod errors; +pub mod gas_counter; +mod logic; +pub mod mocks; +pub mod test_utils; +#[cfg(test)] +mod tests; +pub mod types; +mod utils; +mod vmstate; + +pub use context::VMContext; +pub use dependencies::{External, MemSlice, MemoryLike, ValuePtr}; +pub use errors::{HostError, VMLogicError}; +pub use gas_counter::with_ext_cost_counter; +pub use logic::{VMLogic, VMOutcome}; +pub use unc_parameters::vm::{Config, ContractPrepareVersion, LimitConfig, StorageGetMode}; +pub use unc_primitives_core::types::ProtocolVersion; +pub use types::ReturnData; + +#[derive(Debug, Clone, PartialEq, BorshDeserialize, BorshSerialize)] +pub enum CompiledContract { + CompileModuleError(errors::CompilationError), + Code(Vec), +} + +/// Cache for compiled modules +pub trait CompiledContractCache: Send + Sync { + fn put(&self, key: &CryptoHash, value: CompiledContract) -> std::io::Result<()>; + fn get(&self, key: &CryptoHash) -> std::io::Result>; + fn has(&self, key: &CryptoHash) -> std::io::Result { + self.get(key).map(|entry| entry.is_some()) + } +} + +impl fmt::Debug for dyn CompiledContractCache { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Compiled contracts cache") + } +} + +/// Counts trie nodes reads during tx/receipt execution for proper storage costs charging. +#[derive(Debug, PartialEq)] +pub struct TrieNodesCount { + /// Potentially expensive trie node reads which are served from disk in the worst case. + pub db_reads: u64, + /// Cheap trie node reads which are guaranteed to be served from RAM. + pub mem_reads: u64, +} + +impl TrieNodesCount { + /// Used to determine the number of trie nodes charged during some operation. + pub fn checked_sub(self, other: &Self) -> Option { + Some(Self { + db_reads: self.db_reads.checked_sub(other.db_reads)?, + mem_reads: self.mem_reads.checked_sub(other.mem_reads)?, + }) + } +} + +/// The outgoing (egress) data which will be transformed +/// to a `DataReceipt` to be sent to a `receipt.receiver` +#[derive( + BorshSerialize, + BorshDeserialize, + Hash, + Clone, + Debug, + PartialEq, + Eq, + serde::Serialize, + serde::Deserialize, +)] +pub struct DataReceiver { + pub data_id: CryptoHash, + pub receiver_id: AccountId, +} diff --git a/runtime/unc-vm-runner/src/logic/test_utils.rs b/runtime/unc-vm-runner/src/logic/test_utils.rs new file mode 100644 index 000000000..9df78e132 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/test_utils.rs @@ -0,0 +1,71 @@ +use super::{MemSlice, MemoryLike}; + +/// Tests for implementation of MemoryLike interface. +/// +/// The `factory` returns a [`MemoryLike`] implementation to be tested. The +/// memory must be configured with 64 KiB (i.e. single WASM page) of memory +/// available. +/// +/// Panics if any of the tests fails. +pub fn test_memory_like(factory: impl FnOnce() -> Box) { + const PAGE: u64 = 0x10000; + + struct TestContext { + mem: Box, + buf: [u8; PAGE as usize + 1], + } + + impl TestContext { + fn test_read(&mut self, ptr: u64, len: u64, value: u8) { + self.buf.fill(!value); + self.mem.fits_memory(MemSlice { ptr, len }).unwrap(); + self.mem.read_memory(ptr, &mut self.buf[..(len as usize)]).unwrap(); + assert!(self.buf[..(len as usize)].iter().all(|&v| v == value)); + } + + fn test_write(&mut self, ptr: u64, len: u64, value: u8) { + self.buf.fill(value); + self.mem.fits_memory(MemSlice { ptr, len }).unwrap(); + self.mem.write_memory(ptr, &self.buf[..(len as usize)]).unwrap(); + } + + fn test_oob(&mut self, ptr: u64, len: u64) { + self.buf.fill(42); + self.mem.fits_memory(MemSlice { ptr, len }).unwrap_err(); + self.mem.read_memory(ptr, &mut self.buf[..(len as usize)]).unwrap_err(); + assert!(self.buf[..(len as usize)].iter().all(|&v| v == 42)); + self.mem.write_memory(ptr, &self.buf[..(len as usize)]).unwrap_err(); + } + } + + let mut ctx = TestContext { mem: factory(), buf: [0; PAGE as usize + 1] }; + + // Test memory is initialised to zero. + ctx.test_read(0, PAGE, 0); + ctx.test_read(PAGE, 0, 0); + ctx.test_read(0, PAGE / 2, 0); + ctx.test_read(PAGE / 2, PAGE / 2, 0); + + // Test writing works. + ctx.test_write(0, PAGE / 2, 42); + ctx.test_read(0, PAGE / 2, 42); + ctx.test_read(PAGE / 2, PAGE / 2, 0); + + ctx.test_write(PAGE / 4, PAGE / 4, 24); + ctx.test_read(0, PAGE / 4, 42); + ctx.test_read(PAGE / 4, PAGE / 4, 24); + ctx.test_read(PAGE / 2, PAGE / 2, 0); + + // Zero memory. + ctx.test_write(0, PAGE, 0); + ctx.test_read(0, PAGE, 0); + + // Test out-of-bounds checks. + ctx.test_oob(0, PAGE + 1); + ctx.test_oob(1, PAGE); + ctx.test_oob(PAGE - 1, 2); + ctx.test_oob(PAGE, 1); + + // None of the writes in OOB should have any effect. + ctx.test_read(0, PAGE, 0); +} diff --git a/runtime/unc-vm-runner/src/logic/tests/alt_bn128.rs b/runtime/unc-vm-runner/src/logic/tests/alt_bn128.rs new file mode 100644 index 000000000..6c39cf7ec --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/alt_bn128.rs @@ -0,0 +1,270 @@ +use crate::logic::tests::vm_logic_builder::VMLogicBuilder; +use crate::logic::{HostError, VMLogicError}; +use std::fmt::Write; + +/// Converts a sequence of integers to a single little-endian encoded byte +/// vector. `u8` literals like `0u8` are treated as u8, hex literals like +/// `0xbeef` are treated as `u256`. +/// +/// Comas (`,`) can be used for readability, but don't affect semantics +macro_rules! le_bytes { + ($($lit:tt)*) => ( + { + #[allow(unused_mut)] + let mut buf: Vec = Vec::new(); + $(parse_le_bytes(stringify!($lit), &mut buf);)* + buf + } + ) +} + +fn parse_le_bytes(s: &str, buf: &mut Vec) { + if s == "," { + return; + } + if let Some(byte) = s.strip_suffix("u8") { + buf.push(byte.parse::().unwrap()); + return; + } + let s = s.strip_prefix("0x").expect("0x prefix was expected"); + let zeros = "0".repeat(64 - s.len()); + let s = format!("{zeros}{s}"); + let hi = u128::from_str_radix(&s[..32], 16).unwrap(); + let lo = u128::from_str_radix(&s[32..], 16).unwrap(); + buf.extend(lo.to_le_bytes()); + buf.extend(hi.to_le_bytes()); +} + +/// Renders a slice of bytes representing a point on the curve (a pair of u256) +/// as `[le_bytes!]` compatible representation. +fn render_le_bytes(p: &[u8]) -> String { + assert_eq!(p.len(), 64); + let mut res = String::new(); + res.push_str("["); + let mut first = true; + for c in [&p[..32], &p[32..]] { + if !first { + res.push(' '); + } + first = false; + res.push_str("0x"); + for b in c.iter().rev() { + write!(res, "{:02x}", b).unwrap(); + } + } + res.push_str("]"); + res +} + +#[track_caller] +fn assert_eq_points(left: &[u8], right: &[u8]) { + assert_eq!( + left, + right, + "differet poits on the cureve\n left: {}\n right {}\n", + render_le_bytes(left), + render_le_bytes(right) + ); +} + +#[track_caller] +fn check_result( + actual: Result, + expected: Result, +) -> Option<(T, U)> { + match (actual, expected) { + (Ok(actual), Ok(expected)) => Some((actual, expected)), + (Err(VMLogicError::HostError(HostError::AltBn128InvalidInput { msg: err })), Err(msg)) => { + assert!(err.contains(msg), "expected `{msg}` error, got {err}"); + None + } + (Ok(_), Err(msg)) => panic!("expected `{msg}` error"), + (Err(err), _) => panic!("unexpected eror: `{}`", err), + } +} + +#[test] +fn test_alt_bn128_g1_multiexp() { + #[track_caller] + fn check(input: &[u8], expected: Result<&[u8], &str>) { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let input = logic.internal_mem_write(input); + + let res = logic.alt_bn128_g1_multiexp(input.len, input.ptr, 0); + if let Some(((), expected)) = check_result(res, expected) { + let got = logic.registers().get_for_free(0).unwrap(); + assert_eq_points(&expected, got); + } + } + #[track_caller] + fn check_ok(input: &[u8], expcted: &[u8]) { + check(input, Ok(expcted)) + } + #[track_caller] + fn check_err(input: &[u8], expected_err: &str) { + check(input, Err(expected_err)) + } + + check_ok(&le_bytes![], &le_bytes![0x0 0x0]); + check_ok( + &le_bytes![ + 0x2d6b17489d86fcd5f91e8e92eb55081d8cb4413e408047249ef4fb5baa1b518b 0x1e4d0a30dbadd9dad40f7847c7013754ded8d0371c052d19f01453f4ae1506d7 0x1, + ], + &le_bytes![0x2d6b17489d86fcd5f91e8e92eb55081d8cb4413e408047249ef4fb5baa1b518b 0x1e4d0a30dbadd9dad40f7847c7013754ded8d0371c052d19f01453f4ae1506d7], + ); + + check_ok( + &le_bytes![ + 0x12b453155ca3be5d0b14a4804cc39a0b4635b06d512735350cd031051644d3ec 0x2944829dcfa7dd72bb04d12e46869e6a6c8162698f9a6c35724f91f597e25fc4 0x112b450c0769c7cd80ffa552aaab2153adb5646664ee091639784a7f887411f7, + 0x032b2c8b9f1e7f5e53c262ae87ccd1366df1af019f2dbfe1a58a6749ba4b923f 0x0017741cde8d55ccce3a1dac210f531d22f574885226ea46fbbce4a4c75f7ed8 0x19e4956d3cf5e04f938bc9dee72f2fcab15934c7e0450ae15afee161606b071a, + ], + &le_bytes![ + 0x2923a9d452a047e0f24ab419d7893ecbf0c32a842afd88f991a6723decba82aa 0x2e3a00f94191675c0730510133c2fca248160750d87b5157c534146d4d260b61 + ], + ); + check_ok( + &le_bytes![ + 0x26a1602aeb36e32dd1c534b1c014e920b138f4a8b87f2833ea6051c8cbd5eea2 0x01eb929f0ab6720df89837a84f2787d6d8a8bd97e0daab0576321d85143633ee 0x1, + 0x15ad51e3d708bdf9ae99a3732af9354cc7b0f2ce71832b958b3e9b0215e63578 0x231d7b68932527abdeb71488bd5c1e339306c10490d3c65f7daaa651a367c618 0x1, + 0x1302ac2f870ef22bdec4cd48058f309bcef761a7b40f78744157f3bbf25a016d 0x0c75e87050e62a4c3bf1e261da5a0f11c7ccaa090a0585365658f7a2b4b96fee 0x1, + ], + &le_bytes![ + 0x2ee5c54dee890fb79c9964f7a08c7295111990435dc3adff9fb6d63aadded21f 0x2c3fa1abf295e7df565379efcdb0398d08fc959a0a7e5d3f30118fefe9450a67 + ], + ); + + check_err(b"XXXX", "slice of size 4 cannot be precisely split into chunks of size 96"); + check_err( + &le_bytes![0x92 0x2944829dcfa7dd72bb04d12e46869e6a6c8162698f9a6c35724f91f597e25fc4 0x112b450c0769c7cd80ffa552aaab2153adb5646664ee091639784a7f887411f7], + "invalid g1", + ); + check_err( + &le_bytes![ + 0x12b453155ca3be5d0b14a4804cc39a0b4635b06d512735350cd031051644d3ec 0x2944829dcfa7dd72bb04d12e46869e6a6c8162698f9a6c35724f91f597e25fc4 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ], + "invalid fr", + ); +} + +#[test] +fn test_alt_bn128_g1_sum() { + #[track_caller] + fn check(input: &[u8], expected: Result<&[u8], &str>) { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let input = logic.internal_mem_write(input); + + let res = logic.alt_bn128_g1_sum(input.len, input.ptr, 0); + if let Some(((), expected)) = check_result(res, expected) { + let got = logic.registers().get_for_free(0).unwrap(); + assert_eq_points(&expected, got); + } + } + #[track_caller] + fn check_ok(input: &[u8], expcted: &[u8]) { + check(input, Ok(expcted)) + } + #[track_caller] + fn check_err(input: &[u8], expected_err: &str) { + check(input, Err(expected_err)) + } + + check_ok(&le_bytes![], &le_bytes![0x0 0x0]); + check_ok( + &le_bytes![ + 0u8 0x2d6b17489d86fcd5f91e8e92eb55081d8cb4413e408047249ef4fb5baa1b518b 0x1e4d0a30dbadd9dad40f7847c7013754ded8d0371c052d19f01453f4ae1506d7, + ], + &le_bytes![0x2d6b17489d86fcd5f91e8e92eb55081d8cb4413e408047249ef4fb5baa1b518b 0x1e4d0a30dbadd9dad40f7847c7013754ded8d0371c052d19f01453f4ae1506d7,], + ); + check_ok( + &le_bytes![ + 0u8 0x12b453155ca3be5d0b14a4804cc39a0b4635b06d512735350cd031051644d3ec 0x2944829dcfa7dd72bb04d12e46869e6a6c8162698f9a6c35724f91f597e25fc4, + 1u8 0x032b2c8b9f1e7f5e53c262ae87ccd1366df1af019f2dbfe1a58a6749ba4b923f 0x304cda5602a44a5cea16280a60720540748bf609164ae0464063a772111d7e6f, + ], + &le_bytes![ + 0x1e2e53eecef3185d5c874e783cb184d302692a22c20f5f3b3993882e184d8fe8 0x03fc2454d3d88f9eac441e9b85a9041e925f30cca7a82d039d1ffb582bfb2004 + ], + ); + + check_ok( + &le_bytes![ + 0u8 0x26a1602aeb36e32dd1c534b1c014e920b138f4a8b87f2833ea6051c8cbd5eea2 0x01eb929f0ab6720df89837a84f2787d6d8a8bd97e0daab0576321d85143633ee, + 0u8 0x15ad51e3d708bdf9ae99a3732af9354cc7b0f2ce71832b958b3e9b0215e63578 0x231d7b68932527abdeb71488bd5c1e339306c10490d3c65f7daaa651a367c618, + 0u8 0x1302ac2f870ef22bdec4cd48058f309bcef761a7b40f78744157f3bbf25a016d 0x0c75e87050e62a4c3bf1e261da5a0f11c7ccaa090a0585365658f7a2b4b96fee, + ], + &le_bytes![ + 0x2ee5c54dee890fb79c9964f7a08c7295111990435dc3adff9fb6d63aadded21f 0x2c3fa1abf295e7df565379efcdb0398d08fc959a0a7e5d3f30118fefe9450a67 + ], + ); + + check_err(&[92], "slice of size 1 cannot be precisely split into chunks of size 65"); + check_err( + &le_bytes![ + 0u8 0x111 0x222 + ], + "invalid g1", + ); + check_err( + &le_bytes![92u8 0x12b453155ca3be5d0b14a4804cc39a0b4635b06d512735350cd031051644d3ec 0x2944829dcfa7dd72bb04d12e46869e6a6c8162698f9a6c35724f91f597e25fc4], + "invalid bool", + ); +} + +#[test] +fn test_alt_bn128_pairing_check() { + #[track_caller] + fn check(input: &[u8], expected: Result) { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let input = logic.internal_mem_write(input); + + let res = logic.alt_bn128_pairing_check(input.len, input.ptr); + if let Some((res, expected)) = check_result(res, expected) { + assert_eq!(res, expected) + } + } + #[track_caller] + fn check_ok(input: &[u8], expcted: u64) { + check(input, Ok(expcted)) + } + #[track_caller] + fn check_err(input: &[u8], expected_err: &str) { + check(input, Err(expected_err)) + } + + check_ok(&[], 1); + check_ok( + &le_bytes![ + 0x0763111c06b5066cc812f8e058d5b82a7bc356c83a1a5ab743ea4e7163d90a75 0x041068a8ed41f6755c1ad2d30aacc3974a4fae3293aee34c7103b4c073358624 + + 0x291aea4fac742aaf8772caa00c8763d509c3d6f20d1be64d73c10d06db031a01 0x1de7a958e4adb3a128cd3b942b13bc85ee4124f0dee6f4266b39707c81c06fd4 + 0x1e2d816ba8b96e5cb444883a2b095533becb805c654418fa2c65bba533a34311 0x195dd519dc841a301a14de412c520b858c01ccc7ba0bd4177dd85d4b116416bb, + + + 0x1f1cebcff0a999ffa7c1b582e9afd6c875760fde2679e9fd830bf3979504b04f 0x179fe95fb3fb3807ebbad5ed2b40ddd06f7b73525382a64a43489606b3454a53 + + 0x201eef3c872a7313da79f6aa628cc9795314fbac78484fdae2eba5086755ad6d 0x204e0376b942fe92ba6e2746d07d62f5dede7b8b6e172fa89ea0c5c4ccabaa31 + 0x00e9f62300f921f5d4f2f7008aec59449a3f5e75add585ec4eccf04f5dc5b32f 0x17c150f0fb2ff472816b41868b98b9170233ee4647fe4bc41a1336c9a2c6567c + ], + 1, + ); + check_ok( + &le_bytes![ + 0x0763111c06b5066cc812f8e058d5b82a7bc356c83a1a5ab743ea4e7163d90a75 0x041068a8ed41f6755c1ad2d30aacc3974a4fae3293aee34c7103b4c073358624 + + 0x291aea4fac742aaf8772caa00c8763d509c3d6f20d1be64d73c10d06db031a01 0x1de7a958e4adb3a128cd3b942b13bc85ee4124f0dee6f4266b39707c81c06fd4 + 0x1e2d816ba8b96e5cb444883a2b095533becb805c654418fa2c65bba533a34311 0x195dd519dc841a301a14de412c520b858c01ccc7ba0bd4177dd85d4b116416bb, + + + 0x0763111c06b5066cc812f8e058d5b82a7bc356c83a1a5ab743ea4e7163d90a75 0x041068a8ed41f6755c1ad2d30aacc3974a4fae3293aee34c7103b4c073358624 + + 0x201eef3c872a7313da79f6aa628cc9795314fbac78484fdae2eba5086755ad6d 0x204e0376b942fe92ba6e2746d07d62f5dede7b8b6e172fa89ea0c5c4ccabaa31 + 0x00e9f62300f921f5d4f2f7008aec59449a3f5e75add585ec4eccf04f5dc5b32f 0x17c150f0fb2ff472816b41868b98b9170233ee4647fe4bc41a1336c9a2c6567c + ], + 0, + ); + + check_err(b"XXXX", "slice of size 4 cannot be precisely split into chunks of size 192"); + check_err(&le_bytes![0x0 0x0 0x0 0x0 0x0 0x0, 0x0 0x0 0x0 0x0 0x0 0x111], "invalid g2"); +} diff --git a/runtime/unc-vm-runner/src/logic/tests/context.rs b/runtime/unc-vm-runner/src/logic/tests/context.rs new file mode 100644 index 000000000..0b813dce1 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/context.rs @@ -0,0 +1,111 @@ +use crate::{logic::tests::vm_logic_builder::VMLogicBuilder, tests::test_vm_config}; +use unc_primitives_core::config::ViewConfig; + +macro_rules! decl_test_bytes { + ($testname:ident, $method:ident, $ctx:ident, $want:expr) => { + #[test] + fn $testname() { + let mut logic_builder = VMLogicBuilder::default(); + let $ctx = &logic_builder.context; + let want = $want.to_vec(); + let mut logic = logic_builder.build(); + logic.$method(0).expect("read bytes into register from context should be ok"); + logic.assert_read_register(&want[..], 0); + } + }; +} + +macro_rules! decl_test_u64 { + ($testname:ident, $method:ident, $ctx:ident, $want:expr) => { + #[test] + fn $testname() { + let mut logic_builder = VMLogicBuilder::default(); + let $ctx = &logic_builder.context; + let want = $want; + + let mut logic = logic_builder.build(); + let got = logic.$method().expect("read from context should be ok"); + assert_eq!(want, got); + } + }; +} + +macro_rules! decl_test_u128 { + ($testname:ident, $method:ident, $ctx:ident, $want:expr) => { + #[test] + fn $testname() { + let mut logic_builder = VMLogicBuilder::default(); + let $ctx = &logic_builder.context; + let want = $want; + + let mut logic = logic_builder.build(); + logic.$method(0).expect("read from context should be ok"); + let got = logic.internal_mem_read(0, 16).try_into().unwrap(); + assert_eq!(u128::from_le_bytes(got), want); + } + }; +} + +decl_test_bytes!( + test_current_account_id, + current_account_id, + ctx, + ctx.current_account_id.as_bytes() +); +decl_test_bytes!(test_signer_account_id, signer_account_id, ctx, ctx.signer_account_id.as_bytes()); +decl_test_bytes!( + test_predecessor_account_id, + predecessor_account_id, + ctx, + ctx.predecessor_account_id.as_bytes() +); +decl_test_bytes!(test_signer_account_pk, signer_account_pk, ctx, ctx.signer_account_pk); + +decl_test_bytes!(test_random_seed, random_seed, ctx, ctx.random_seed); + +decl_test_bytes!(test_input, input, ctx, ctx.input); + +decl_test_u64!(test_block_index, block_index, ctx, ctx.block_height); +decl_test_u64!(test_block_timestamp, block_timestamp, ctx, ctx.block_timestamp); +decl_test_u64!(test_storage_usage, storage_usage, ctx, ctx.storage_usage); +decl_test_u64!(test_prepaid_gas, prepaid_gas, ctx, ctx.prepaid_gas); + +decl_test_u128!( + test_account_balance, + account_balance, + ctx, + ctx.account_balance + ctx.attached_deposit +); +decl_test_u128!( + test_account_locked_balance, + account_locked_balance, + ctx, + ctx.account_locked_balance +); + +decl_test_u128!(test_attached_deposit, attached_deposit, ctx, ctx.attached_deposit); + +#[test] +fn test_attached_deposit_view() { + #[track_caller] + fn test_view(amount: u128) { + let mut logic_builder = VMLogicBuilder::default(); + let context = &mut logic_builder.context; + context.view_config = + Some(ViewConfig { max_gas_burnt: test_vm_config().limit_config.max_gas_burnt }); + context.account_balance = 0; + context.attached_deposit = amount; + let mut logic = logic_builder.build(); + + logic.attached_deposit(0).expect("read from context should be ok"); + let buf = + logic.internal_mem_read(0, std::mem::size_of::() as u64).try_into().unwrap(); + + let res = u128::from_le_bytes(buf); + assert_eq!(res, amount); + } + + test_view(0); + test_view(1); + test_view(u128::MAX); +} diff --git a/runtime/unc-vm-runner/src/logic/tests/ecrecover-tests.json b/runtime/unc-vm-runner/src/logic/tests/ecrecover-tests.json new file mode 100644 index 000000000..3b3f6147c --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/ecrecover-tests.json @@ -0,0 +1,163 @@ +[ + {"m": "ce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008", "v": 1, "sig": "90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc93", "mc": true, "res": "e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652"}, + {"m": "60be35518133bb945595a87176455ea5cb358f0768edf67855fd8b4317486a61", "v": 0, "sig": "0000000000000000000000000000000000000000000000000000000000000000443a4b19c24abdace71770faad25665fbc5b00450ec0b76c6e87e9a4383747fd", "mc": false, "res": null}, + {"m": "a727ef196c4ed856629b4274297ae7a7b6225043defbde6cd30c0d78f30d6d0b", "v": 0, "sig": "000000000000000000000000000000000000000000000000000000000000000188785d53d67fe3cfff690d4c8785c5facef3a19e9bec59933d352973a5da554a", "mc": false, "res": "203f1d1b9f8912c91ba368e04be5ea594df36018bdf150015b1a70d5ac59e9db430e1792dc174ac17d5e2b5f656256d2cf3ba2c10b4dab79602f08f7c2217264"}, + {"m": "bd0f7f412744c079e9138ce32c4cdc4454807fd5fbaee573c8d16aaf03e9fbdd", "v": 1, "sig": "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0f110f1b5ca7b71a94aa14d1a2601052f1f391aeba368098f06de38522120b2e8", "mc": false, "res": "669032157325be5482fa879b1a4c158fc653c7524dabe93e20142deabd06037f5846b2b76b301be6975214194d9e18f3e0beb49b187d4058598d179e2d5ea3cb"}, + {"m": "526ba0b62766b2ef4ed5ccde1944789e5345a987ee818b8d07fcd97b466ce900", "v": 1, "sig": "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a18247138be67318dc7e03a2b8aca964616d298625e5190298ad03d2c29057100f", "mc": false, "res": "fadc0e7817f6611c517d1e331589466e03ec95a554901fcb0802cfb7c3fceaea56d21b726f0310a8cb780a622d611797adb89341a0c0305dc674256563af7cc3"}, + {"m": "f942d196d96de0d4f4f2d33341115641205382d4eef1bbe1f9ab1f5067bca093", "v": 0, "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036414021b0e741e3105da10c7a7cc277dd760d827f569ccacb296112e8a7b4b378765a", "mc": false, "res": null}, + {"m": "6e8dc189134c016e6e8431d732efe3fc161e261dfab49cd270bf2f6bfad5dd84", "v": 0, "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410b0de66380980b64fe478edda095ed98b99ccbab4c1555bc7f3a2b4060a5750b", "mc": false, "res": null}, + {"m": "1f81fb7edba2064b7def02120c0105d846753bb50699028f6f993aa586be97b7", "v": 1, "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142ca805ce64920bbc8dd5b3c0abd7487de975828f5aff4009c54ff843e7793584c", "mc": false, "res": null}, + {"m": "bf23b9a27ca4e1faddbb003623f59e025cc9b3406fa36410032f0cd4ca2e1c35", "v": 0, "sig": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff25873a721ab762eaef3aed3147e9138e28df98a33c4547cc17aefd4100422b6e", "mc": false, "res": null}, + {"m": "965bad2952f1775f9045887d22f599fe60399c3fe0f13cbeef313a4df3a4bf78", "v": 2, "sig": "000000000000000000000000000000000000000000000000000000000000000061d93748db0f48932a087249ac123468e39a068d57d2ab586453b89b807e3d61", "mc": false, "res": null}, + {"m": "274c48d93ac566f9220838a37c80775aca232958847fffe07e88ffbd263d6203", "v": 3, "sig": "00000000000000000000000000000000000000000000000000000000000000019018c78d8c4e88229e167e544e1c957690059f48a8d371725b9f4fc627a1e7aa", "mc": false, "res": null}, + {"m": "9157d65274b79a042fe4dcb7691d82c9368ae6bcb223aaf208576d0bd5e9515a", "v": 2, "sig": "000000000000000000000000000000014551231950b75fc4402da1722fc9baed739f8a83bdb021eeac938c806384aef2352ed1cea937b88b0ce70da221092b54", "mc": false, "res": null}, + {"m": "ebb76170950c90b5f80f07035c094e0ee8d67d9242fa88c318a53e62005a0deb", "v": 2, "sig": "000000000000000000000000000000014551231950b75fc4402da1722fc9baee741cd70287e9ed3c9341297fb4211694f66b0c4c833a249cb48b710b6ffca52d", "mc": false, "res": null}, + {"m": "3e4823afa5134e0300460bdce8d315f45e9f3072bf6f2c3a535e31e4dbfb25fc", "v": 3, "sig": "000000000000000000000000000000014551231950b75fc4402da1722fc9baef29172c3e2cd0bcbf1f06114db61a438a37a6546d1f27b018903732f5c7b50d44", "mc": false, "res": null}, + {"m": "eb8517700d93765f7a8262b79c87afee1021d24698f3e2b5adca734452ea6527", "v": 2, "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140b8b5b6b76230ee9fd965c913699ac3ca25cd6edc2fd4499cfe5ee399a598c393", "mc": false, "res": null}, + {"m": "d4aba786ef1b892cec795498b319e84bbff92f0b524f1994c4c74793a14b759f", "v": 3, "sig": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb7ee1cd5af4e6ff8b3223091616ccc00d00919e3f3a6002c02356ce6deef1dc6", "mc": false, "res": null}, + {"m": "e9c74ebcc77d58bae29b6e946e03e052e8dcc7db136dc98ec45be4f13366b3fc", "v": 2, "sig": "000000000000000000000000000000014246b43c1be7d345d8e5bf50c19fa5eac53c9211f596d8ab82617a48d4849a865466ec7599aab4eb56fdb0b0f4ed1a4a", "mc": false, "res": "37098abceb17a0f266f080ebb495ddbc8f8d5d83fa13d8ded6841df5d343e851076ceaf5ab485b0d324a9d65f04c19ba94a27d991669fbba55ada1659fd41098"}, + {"m": "0db7b8d8248f827e762b7605dc64839c287f7425b5a6c575494646d34b0b84ba", "v": 3, "sig": "000000000000000000000000000000007a914d34aa53bdea0a8f4ff49355a62353bd2aa75ba1c4c99ec14c32714946fe2702766da0099d50a3f7a3b26196d082", "mc": false, "res": "8dd2448a6bf7f136e1f14b3b4947114231314d52d99b6ef0ef9e8fe864cee657af8e030f905185dd96a8d282180c92da7687dc2b61542836f181cb77ebe40865"}, + {"m": "1dc32f35af87f7970594997fb7df9a050e58a74fb09011522844cc70704aba2c", "v": 2, "sig": "00000000000000000000000000000000fd2f4405b9de940d754ef33492b2c95338d55c758b608467b51b667154983fd5cbed4dc88f6eac9e1f6359121e5a1f5b", "mc": false, "res": null}, + {"m": "4d508dbdbb955c3ac83a09ae0624868b3dab03c4f026b4277b612a0f83bb8ae6", "v": 2, "sig": "000000000000000000000000000000000e8f4eb31d3a9ab427c096afcc1935b8f962f73ed4bddda86f92fbc1de91791a4312e7793d1cb71640a1b169d783db86", "mc": false, "res": null}, + {"m": "06d2a710baab18cd453c3ae05110e62168fe8f64e9755e46d1adc6d1ae641250", "v": 3, "sig": "000000000000000000000000000000012ef0c0509ecf418677e9ff79fa061816827faa8b9a000bf1f7953d0024f3c489f6ce05bb8ba18f36ca99fa8c87789593", "mc": false, "res": "d891a74901ed98547de60d5d52d9951519edc04b781d3723291ef2e86c393d8ca9827fee9493ba85d03ef6d32f5d3f094f1833e9c762e2e2f92c5461683f79f8"}, + {"m": "c55518fc91148e1d61174509ba63205bc557c96b62c4c61b84b8bd69cff235b0", "v": 0, "sig": "aabb3fe036f2bb59c62c106f47ea7b4c75fa33c71eb697a91928e346033048749eaf4d3ed8421abb66047875d18c7e4a0f2f0304fcfb06d4e256099a5736c7d8", "mc": false, "res": null}, + {"m": "6d4a27f03796a2f88d53efc0448dbb620ca3f8e48bf7770c0d52ea4541dc17e8", "v": 2, "sig": "0000000000000000000000000000000142703d2354402e442e42646f797ae659f9df40752c93312a0d39c4f5cf772051fe7353538e3e0749243506dfe9b5d766", "mc": false, "res": null}, + {"m": "b524b6cc1b915fa1ca5f20bba94d2a7fd53c6d4ca754704ca7ff720145acf1b6", "v": 1, "sig": "c45c51175f49d693adaf4b4284c88838fe04116220a5414350e215298c9984cedf4efde899025397e38e541edd1b2cd2a4b268448a5ebc8dbc5fe57067b58cf6", "mc": false, "res": "7cda2d8dd1ae0b9c4b931dfc77d2424f1f8ac1482676fb28640a38a3b76fbb159bff71948df092b74726ed45c3519b631586dbd7d45932feda7f73945d2b38ae"}, + {"m": "234f94cd8bdee2b39f03d0b335e55849b1a699b7e7bd43d744b05969ead21653", "v": 0, "sig": "ffffffffffffffffffffffffffffffff7ae92991ab05b0048fdfbb66503ddbee2e010a4eb2f5f14ee26f088cef7fa33ac7d6a421fa19debadd41ad53ce94a3a2", "mc": false, "res": null}, + {"m": "fc0aefcf09183ecce2f0702b036f0953f45bd88f3f9855af191935ed92147475", "v": 1, "sig": "ffffffffffffffffffffffffffffffff14b6478100d4e2d43a10ce4f9f3dfe7c7b3668106d53fa158aa86bdf51e65a7738f64f56105cae7a92f4c387b25aceba", "mc": false, "res": null}, + {"m": "f24641d406b264ff2aaa6cd2b73870a3c52c033ac1076f0dc82bdbb549c50208", "v": 2, "sig": "873cdce989d97082aa56a9251dab5f8e7e7c82153eae131105431c78cd6194ceecf9ee33b1a37320616b85fb63ad82826ddaa65d91625506315db899c51f04e0", "mc": false, "res": null}, + {"m": "46434df559bc05540b5373742a4ebf0471937175b79abf92c2c513c6a1f60272", "v": 1, "sig": "ffffffffffffffffffffffffffffffff3be49e73c8353b95527bd340aeea0f327c6d36e52bf99317dec4642c4bc9ae6c89234ad69d961a2455020243ae01cfb7", "mc": false, "res": null}, + {"m": "8ab09cf5fd42a34b86d0a2b4ec6d99460e1000bfa943ed1f2e41d124764e2d13", "v": 1, "sig": "ffffffffffffffffffffffffffffffff05a6cf0ead28b68963af205ed9d57327e87b11827f6d5b9791593fbdedf24ced4784f184f116a39dc304649b894b4191", "mc": false, "res": null}, + {"m": "8f18cbd6f5ca23728b2c389c5c175ae81ed0f9823077336f316a6ccbe5cf098c", "v": 3, "sig": "84f8dccc94f1084991f10448bf43bca3b3d67b9bfafa8663e9ea1740423eb27ad7b0b13037cc8f4b51e7c25e5eb527519729644ccb25c64086ed738c81c8f4cb", "mc": false, "res": null}, + {"m": "584991dd5de6b765062335330e3e59aff419a85d9452c0bb670f20b6340e4e2d", "v": 2, "sig": "24dc269ba1cada90382f78131dd42805f234e38cf73c8f1052e64c403beadbee4608f25131c7e04f7e31acc6ecbf01ee2644831800effcc146bc5cb5dc3639c6", "mc": false, "res": null}, + {"m": "17b4ff54e63f042a2216ef63f2476bd6c78d4c4a15c15e3e1af30e787bff129c", "v": 1, "sig": "fffffffffffffffffffffffffffffffff8694feda3bdc35fcde6babac5cde067e0b00f4ae9018c13fd4ac1e9554c4bb59bb05af14c3f21c4b087dbebec2fb730", "mc": false, "res": null}, + {"m": "ab58e3dcda8ee800f9c649c0671cab05e2f458add0711b93766ff99c74cade5b", "v": 2, "sig": "0000000000000000000000000000000078329e5cbb72aa101d16d95b39ac59aa0000000000000000000000000000000000000000000000000000000000000000", "mc": false, "res": null}, + {"m": "a4371d0b4b45b7da020d591e1fb61d4b8cc9dbe3b3f80bf95f7fa471e0e01055", "v": 0, "sig": "c718be6a378338eee4df346af49f59b16b1e393a9ca90a4721d640898179514b0000000000000000000000000000000000000000000000000000000000000000", "mc": false, "res": null}, + {"m": "42550af47af84c35c090f95cba972b4c9083feb891e77c1ba461916851884cf1", "v": 0, "sig": "e41e72a2d374fbe7505fa0926efe046ef55bbe490dfd932e9499c2f33a2b2b130000000000000000000000000000000000000000000000000000000000000001", "mc": false, "res": null}, + {"m": "e0a61198b3a3becfc8791d11484bdef09e53016a747219d6aa4a760b01ba4147", "v": 2, "sig": "000000000000000000000000000000003169866a5a22132932b81c5b6aaf26e80000000000000000000000000000000000000000000000000000000000000001", "mc": false, "res": "8cda59c40f87069feb9c72201a1f8540230342ff55e85dcf506642e424304bd409a5ac92819cd576b63a2d51607237c0a9fbf3d6b0b159a6600eae42512641f4"}, + {"m": "09cc164cede11d66767e6c9ec05cab119ec8e9c1043e78858bb54be9b8f380fb", "v": 2, "sig": "0000000000000000000000000000000003144b8578773934677c646cdb264f1a7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", "mc": false, "res": null}, + {"m": "1ec38d681efe8e5081975273daa172b2855451a9c3239312518a160f140fa048", "v": 1, "sig": "f88786da8099db225422fd8329d0312570905af2e8b1596acde66ad53cbcc76d7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", "mc": false, "res": "f7895457051b1396782057ecb8fab5a43e91fbcad765cd58653673246e4b25f1ea782021fef015d1d6cfd6f91174d892828f9142e3bcbac4e316b0dea27f5150"}, + {"m": "21cec4af8cf0541fb5584846494157f658a14c94921a8e78fdbd3e663624da46", "v": 0, "sig": "9cc89c712a50f230508a2e71b99b41ab592eeba63da5ece48bdceaedeac7f9357fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1", "mc": false, "res": null}, + {"m": "1cabbe11ef455e5323064e3a4c04d827f8c700846dc80751f01ff7ab61aa711a", "v": 2, "sig": "00000000000000000000000000000000098b27c49c37e0283797ff51761c2a497fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1", "mc": false, "res": "5221580be8479a7c7ad6c0544c6d567bc3a474c7b4e938e277e50d881d09f98f62e47e98b5d8a4682862e261f6c1718576162d9312072b27e4f243877620118a"}, + {"m": "18eec847a7adb10e513594b48fb83c298dfe2b8d95ef2c702cbf7d881a022948", "v": 0, "sig": "0b588bdf27cde4446f2a66bea2923fecee30ec5ebe6b5926eb48461fccd3262afffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", "mc": false, "res": null}, + {"m": "524f7ad2f7854c0d532c4d123cc1296c9a2cdae9d1580814ede384174260b4b7", "v": 3, "sig": "000000000000000000000000000000005153bbdefdf9d80d16b3e1714724171dfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", "mc": false, "res": "de128721c71b774a4f20afbfb2b3014823fd690ad9e05ed24e27c2707e9b6da6db0d5aaa22c5d691be2ecc17745bc130efcc815206b6a976240f8ff5b42d8ea8"}, + {"m": "32ce30b8821663124c34180066eba913f99fa6498c978a1ae2d51cc294dc0052", "v": 3, "sig": "0000000000000000000000000000000061505f346272d5a3dd7d8d8f11a9f43dfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", "mc": false, "res": null}, + {"m": "796f497d5c99f4348851a7e93ec1aa3a8e327e010a09ec1ccb29fe3bdf9bbeaf", "v": 0, "sig": "e50e72e92b4ea45782807be88813023c029e5be2eb8bd23af9b2e68018949223fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", "mc": false, "res": null}, + {"m": "2ff2cc93f701931013502e5f07471eb5858ef28e0e6a7e28b9d944f33479058f", "v": 1, "sig": "dc43bf9f5c3c624b888b451a39fecb7a1bf1fb5c5b015dd1930edcf722e6fdf5fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", "mc": false, "res": null}, + {"m": "8fff8f1deb4c23b5f0a6380dbf3310c97f32fc2a590cc2e7856c43cc0c410bbd", "v": 3, "sig": "00000000000000000000000000000000619bbb81e36d14ccb977b0f9038ebb7dfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", "mc": false, "res": null}, + {"m": "38dbd7309c3e291dd948fc46831989b6d11243234a728016dd86aeb0a4feee6d", "v": 0, "sig": "2440c6fce9214553cbdbe4b265b1dfb7f6dd116b0457f1ad5bd32deb851abd38ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "mc": false, "res": null}, + {"m": "7694860780f47ecd2e67a91ed633e564657bdc06d05e13f28b319e4f5841f1c7", "v": 1, "sig": "5b26151d89ccc7f1a797d5f15903ffb3ca90eac990eb534f9732194b578566a1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "mc": false, "res": null}, + {"m": "1e5cd2302d97fdfc963d7aaa33b4956c8df7dca1644844e6a687c33335bf9a25", "v": 3, "sig": "0000000000000000000000000000000052c869ed5d80d79c63ec6efdee7165223d45d40b2e10e68a0b38a7755447af8d68bd95f791901c9b1f2a08eca2a1a2ba", "mc": false, "res": null}, + {"m": "3c1829e8953145df361c636b5ae09a494280915fbd2fc70b8ec6ad616c6e0a7d", "v": 1, "sig": "a117036c75b67e690c5bd5ba454314b115b9269294bf39835d3fd8f28b79cb4196480488e7e0ce079e7b79d2cd223dc1d3e06afa1dc56fb53dda5e64c5b5cbf3", "mc": false, "res": "2fab7fb648015e0a7530a7156393560267e53777f4500878eba088a49e1e5bbfc28447d46325fd503910e0fe97ca228baf57dc1d92eafe07220169a586e45778"}, + {"m": "88457f749445d8c9b5e85a142ed25cc5d9d445b46cc69aeedc225f42b3d48b60", "v": 2, "sig": "000000000000000000000000000000000be2bb694c5ef72995270e50a0d361399d4cc27c571ab5b607fdce5e6b1c7b6f8427de54011335b6f9273d8f692a6a9c", "mc": false, "res": null}, + {"m": "d6e7c574d074ccf7f7e8a7ba55ca5ea375b9e537294e5ea5d9c978a1e21ab512", "v": 1, "sig": "f1ce9699c681e3bb6068296a3cd0e4f821c79438151852bde0b08b94d155e0e36adfeffb413da375dd0957ab07ea7fa656e934fb1a4145418b52dd3e0b63534a", "mc": false, "res": "2038980e9e9daf6a5a6992ac5c9df67e7c842800cf87d09f22841c0cae07b17f3d63bd0e2101eb97470fec229ba378c87a91b7dcd77fd6c3893369a4273022c4"}, + {"m": "951c3d802b4b3a0365c1220e3eed672d8f801cb175f8f674d7409e0a6cf6aed9", "v": 1, "sig": "75650fb1b3349f38048668b7dcab0211e26eaa0a12b2c04fe7f7ffc9d1551aa8958a153fd8acfcfb448e2021b4037cac22f071c9a6deed05a46d4f409fac8179", "mc": false, "res": null}, + {"m": "11d95dc26adfca352fc9caf25b06839a42bba3af35ee4c8f646dfe667ccec8de", "v": 3, "sig": "000000000000000000000000000000007b71ed2b954884735e91790f7ab2702a72388da663e046df7102606c24bef1fc2fe36f042f274a49ed4d0938dc237c21", "mc": false, "res": "712e2bdf593efb0b207a572d781a854638265d2f6d0e8ce57671be5b7f5326f181ea8ea67645524a396d07584d81c4fe86086df92b47427083fcf51af7b07e35"}, + {"m": "0fb6e1dbd812777f0e8a38585704a0f4fd5dccfff77f566ec039c26f66c8b32d", "v": 0, "sig": "c0a153e5d81f3837b3b3cb2e7757aea2396ed13d787d8a76930480a55df6fb302db1df642b08998b8816698c7ca2c58e4a33815df36b2e794214cbb495dc9992", "mc": false, "res": null}, + {"m": "655612900fa23103423327a2c1380db29034d8cde6d288997b4a072ac911cefb", "v": 1, "sig": "b1c9c2ca6a0076a2789dfc03cd9a796229ed5b8a7841bffed30b555e961a86cdab11345edb7d19bc9c494684062a4960ea928a77463b05165c23167b50dec427", "mc": false, "res": "092469f8f11e80c3d7c12e49277a1155abe0a6dbcef083dd85d4467899a9d68b5cdf36e1ba50e6c9ae1b0c74f17908f705099d2a517d66d2ad5d6b221bdee907"}, + {"m": "593fa2eb9590ff214c3a262bfb7430510ecfa7a9a52b99c7325fc6d0635b886f", "v": 0, "sig": "170a316ed9c317495b069bb9ec4a04b9559f3b1ee4bdb4e8a2174922a2c71685d516f61501ee9e177a05584c3643fcbc1c9d73369f15166af1c8d6e2f7f8216b", "mc": false, "res": null}, + {"m": "be4aab4a3ecdf3bca032a3444226f18969e5461962ac3b9aa28ec52cef70ea87", "v": 3, "sig": "000000000000000000000000000000012ccdbfcec8772f8cd22d1f24fc16a503971b9a17b094bed9dc911354133b362171782ac98df6db52dbc28511ab84f1e9", "mc": false, "res": "4b07979cde1ea5998b6a23320963161b1b503a623de05c02be1f80f927cc55188e1a0a84f57580cf0ee9aa801fbae1b2b28ef58ac57f1e540324d492ae08e61a"}, + {"m": "ad7f20c8ed46ce1d014eb5e24a1476df58de60364521f0636a19ac4d386592b5", "v": 1, "sig": "55f1c940616f334e9e37ca051e3702eaa6847c534b3e00c139a5e4b5a0e059a2b0b0b9b96213ea29abef4f28ca7630eebfba9f3329efc8477e1c6ccf0777f2ab", "mc": false, "res": null}, + {"m": "e9855d2da19108bcbb76346ccf8df316ec3314990ad07dda6c5885ae7d65212a", "v": 0, "sig": "7337fff4006702f4c5909237d43bfcfd664ebdacde0824124be653932bc42e81617d5437e0a8af876107eaaed19f4a1a21a4c56827d444f805bcc5dd6736a98c", "mc": false, "res": "c12e8e9e023abc6da323797698edb7c62602eef8f0a506c4f15ea9929699a026cf6c93d8695fa7ddea42819a067bbd6a500c3b6eafcff76b1d8d7674989051d4"}, + {"m": "9f55bb82c3ef0b77501cde56d5fedf7088136962b0ba3ae9636048a235303c61", "v": 3, "sig": "00000000000000000000000000000000eb0f102f1a280fd0fe42c69e52e07ef8c723aae2ca85bf76a3b5c942115355f5f623682970ab08b7f8b64abe1f3dceb2", "mc": false, "res": null}, + {"m": "e3920327683dab088f503e82e0a233afc062885b9e7805285daa4c280e07c5ca", "v": 2, "sig": "00000000000000000000000000000000b00d23b0a1a4f05f06dac4fc74d15cca396d94d18f9c9892f4a5d8d1bc8b2a1d75b890881902d2f6ea6d127c1b00aae2", "mc": false, "res": "676d950453938df794b33f39e56b9779a49298bcda33fe66250e3372c1c197e1cc43bbf6ad9c5146a2716665fb1a205afee8e85566ff319b56113acefd5f9f61"}, + {"m": "e2ed6dd412497dc5045d47dbff04cdf3d1853ddd60a332ca519dffa722e97a30", "v": 2, "sig": "00000000000000000000000000000000a4d880189bb667357bdc54dd6717b6718511efc2c4ba0c77dbd60ee3eb8869d8924bd6600a72857a8ce8f3ed9bc79e26", "mc": false, "res": null}, + {"m": "ff93c11e73356fe16207a61eb96bdd08a4e9fcefd63adce3ecffa9452fb90d8c", "v": 3, "sig": "000000000000000000000000000000004bf076d5a10a16c2cb416e4f3e03bb75031ec813c62c94ae33310ab7fe9425b9af3b7d34b3dbcc5c52e9227fc9f41872", "mc": false, "res": "c6cc735bfc334c55cc37e7f9467abed51fb6fb096936ad68eedd6afc146dbc572900ad95b53b8ec71f26234155916d067686024bb62e4ece9bde59d21cc2f8da"}, + {"m": "14c243d354c6add3ebf9425de6303f4e43607c4c7a5405d4c56b50a5553f3ac8", "v": 0, "sig": "5847d71d45e2970d61c73b41435b358e3d77407bb859faa99068cfe1a588a4b0ffffffffffffffffffffffffffffffff1142710b0965ba1626b4e6a9d31b2546", "mc": false, "res": null}, + {"m": "6a8892e7ab69d43671aaa4b55364a78e3fe79db732adfb23760ef67539238b19", "v": 2, "sig": "000000000000000000000000000000014223611b691e097865a97266d69722b2ffffffffffffffffffffffffffffffff82ccc37c360fa3d1f8a656c71ca711b9", "mc": false, "res": null}, + {"m": "b7370528a0efbde0e92597fc1f7d6a10dc3e9c78181857f0115461914896cabe", "v": 1, "sig": "0321bff1a81dc4b61db6ba689ee750f1148f972ce38b8a9bb020e0f06a271ea8fffffffffffffffffffffffffffffffebd2287076e3a8de300ed85a9a20ccfad", "mc": false, "res": null}, + {"m": "b50eb457d01867d188d0143ace7a3b256dd450d4224a636e3bf486720de05944", "v": 2, "sig": "000000000000000000000000000000004e53773cbae79f701a63a94a5a6bfa42ffffffffffffffffffffffffffffffffc033e26c56182f37ace88f1a365cc4e7", "mc": false, "res": null}, + {"m": "5d849fe6c99643f2b55f81db4bbab099953e0bb7c8daf3c1ef5a8eec2c2be62d", "v": 0, "sig": "a665be103e22a8be02dc1bab3f9f11e07f45b99829fbf781e22823026bc4fe2ffffffffffffffffffffffffffffffffef970dc761cb2944567fa316add5c5580", "mc": false, "res": null}, + {"m": "8f21136d5413fe7a6a8a6c22d36153a5c7c9812b0a46f495c4fbb53539ea2604", "v": 0, "sig": "642d27d1e51d3de7b4ebfdf56ca95a8526aa3b833be45b4acd0fe1fb0037ceebffffffffffffffffffffffffffffffffde7e634e560e7cb0aee120fb7fb091d1", "mc": false, "res": null}, + {"m": "e92f4b9764fdc077b48c45f78728973cdd12318b23ad173a5cec167ada694501", "v": 3, "sig": "00000000000000000000000000000000cc98fcecea2e4a804374a66e0a9d7081fffffffffffffffffffffffffffffffff3351f11a8ab4a0ac101119c7d8f0af2", "mc": false, "res": null}, + {"m": "7251437a8542b31b44985fe617ef15a043fbb201fb9a6dad60ba7add7c42d18b", "v": 3, "sig": "00000000000000000000000000000001195c2b76c75c4077c1f4af08ed555f18ffffffffffffffffffffffffffffffff1a1d2ab42457eb6979d17bdc189b07c4", "mc": false, "res": null}, + {"m": "6ef20202a9bdd7913120355fe0c49d2b61e0a160ece5a5a2d8fb60b1c565df35", "v": 1, "sig": "c0e17818956f8f0855cb120ac5c301d0971e092a8ce545e630c4fbd78d82e432ffffffffffffffffffffffffffffffffad83ec7546409e6f0dafce8de6c84ee2", "mc": false, "res": null}, + {"m": "f637e9b46daf6774aa42e720e1d02467a0306dad5c92cccea5dc18d516339d63", "v": 0, "sig": "1926070e951712d86da8340e3be18f419d36a8dd470012da2daeead6891a314bfffffffffffffffffffffffffffffffec1196569b4d5af36ba967a52f0c5fd7a", "mc": false, "res": null}, + {"m": "813a8f2f0405d5700a3439f712bada8598bea6e3fe42ba35806fc72f0aa9de9c", "v": 1, "sig": "b821bc4fa8c84285e565b612ff2ca1dcebdd2acd2cd418b102a8f309d2d545f0fffffffffffffffffffffffffffffffecce5f7f7a98b0344346025f199b1962a", "mc": false, "res": null}, + {"m": "2eb954409b9bee3c0b8cb27165c3ae1f8fe51f4a003359db13341630846361a4", "v": 1, "sig": "3d291b50178eb73c08291bbba0560f94ca92e27aca18ac1fccb3419ccf1bc3fefffffffffffffffffffffffffffffffec24807fa41150de835431cb0cd1d6d5d", "mc": false, "res": null}, + {"m": "bf51a5dc00de7e6780f77f6b19c74824ac5b1256909efc29b1f6ff9391e4a610", "v": 0, "sig": "d50489a352ced5264c70224c198df2d5ea7fd930504e6845eef244bbd7315929ffffffffffffffffffffffffffffffff6d7423ccd9aae2f11cd87724b15fcbd1", "mc": false, "res": null}, + {"m": "977f93e5a26764a59f9bc9ff0a67cd749f7b3762d3429963e44490d7bae0b67c", "v": 1, "sig": "8547d977753c6cb1ad7b59c333265acdb3d5dcac1a94e2ddfe17addf7bdb7e74ffffffffffffffffffffffffffffffffe64f5fdcd688416e3d431ec8b2671b52", "mc": false, "res": null}, + {"m": "1b70a2a27bfc7a724824836750b29a8c6c662386e97dd3744deab440d20e5377", "v": 0, "sig": "9712cf22791802f5d04323a67c35a00b50efad4d07e171f9378319a8fc387919ffffffffffffffffffffffffffffffff4c0836b28ed7841d212b544bea966f57", "mc": false, "res": null}, + {"m": "f6a297d1d995a9669f5d2d379a0d70c30d4cc8aa74952a74208f612adebd71e9", "v": 0, "sig": "22ba8fece9021a153255347c27ddb0b31ad766cd0b23b9c0ea13eb03188468a8ffffffffffffffffffffffffffffffff888cc19fe3f06dc6bb30bc3f5ae27b89", "mc": false, "res": null}, + {"m": "4fab2b55b272ea7b170c7876d6879a53c32688c88babce2ae431ab2acdfd0c88", "v": 0, "sig": "fcb154228bf38a9d4303aa38115292e16fb8c2744c2d928d5e8a7e463cc2d2a4272793b60e1aa63f50fabf46b3a295b0fb139353aaaf0d5783f68df0bb0f8421", "mc": false, "res": null}, + {"m": "64648807b8876951aa15781cb64ae771bd94b8fc083b02c42dc99b0ae32dc080", "v": 1, "sig": "000000000000000000000000000000000000000000000000000000000000000024f86cf9af2b8b86fe3321cce0342bffdff00034b12f8d36cfaa400f8d601cd2", "mc": true, "res": null}, + {"m": "5bd33b50027ca72bcfa70734aff9fbd9da862766fccaf7e734774648f73d6df5", "v": 1, "sig": "000000000000000000000000000000000000000000000000000000000000000176c1285b707a7e89c73c08db2ef2703808d8a65ab5f278ce49904a85822ff6d3", "mc": true, "res": "6bbd5b3ae1e6ae268c12f7411e3cd2ccc43f4e2bdc69ba20c943c439c7cd656c51483cb6e08180424f4e1ce712b8f10367f3259c82d455bad93a78fc9e9d538f"}, + {"m": "fc267c26d19911a13f62027b5e0ae92b4e80e8289c18422b97feb81430ab1231", "v": 1, "sig": "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a06d06a420278887eebb9ff703bf6101fbb5cda8f2829791d33f9cb7accde0a563", "mc": true, "res": "823f739be628a6a2718537f71025786dbe1a2ce0a24be962ffb8a91e3b0aa0f0b4f592f3e79e0c143f282ffb3efbd0b36163c1b5448134258b434e7fbcaaaa08"}, + {"m": "c5964d730e93ba96daf5f7fc447a31ecb7f96c7caccd006ab4d9bd4aa7c394e7", "v": 0, "sig": "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a11e5dcdc3f9010af67c32c43624eaf1bac42578a93aba14014c22b640f55780f6", "mc": true, "res": "32a2e609140531233e0d0f828e460ce569a980f6a29b920642cd79c8f417d6576a2e8957ed1f90f0896b087c72628f244be5827cfb1872e8885fb73ba9e08fa7"}, + {"m": "d6047f32c687834b5c649f3dc3c6dbca4935518d62f37e93dc440b15516092c8", "v": 0, "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140566873f7ba60084718245e5bd10cc25d09a9990c583a0ccf0b8d26b2814676b4", "mc": true, "res": null}, + {"m": "e0359ab4029d47d421846b49718a1612b611dcc11de957e614bedd59deeb74db", "v": 1, "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036414149eef05f2359e7dea9c64d3f2e6af7d1eb1b9db1c71b9ea0d12bb90fafe54a12", "mc": true, "res": null}, + {"m": "afa65cebcc99fa313657becc2406b4626714bd809da1952482195060904f436f", "v": 0, "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036414204f519920a36e69eaf4a06e9c057b7a463f23691e381ba9fe842ebb824bb9ef6", "mc": true, "res": null}, + {"m": "812f8c9b84e19a546338331aab1cbce293fe1137c231047031b353589d1b5645", "v": 0, "sig": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff369f4dc66183d3cf599ab88becec6aef44cebf879ff1c7dce8560a4753b8d235", "mc": true, "res": null}, + {"m": "133bc2a526c1e1009babaaafc4a6998dafed009aed95da2b2a7a42785e626413", "v": 2, "sig": "000000000000000000000000000000000000000000000000000000000000000068d1a61b87e6903043cc76731f20d9e89b744216e1afc26b6e5a6bd3a125a902", "mc": true, "res": null}, + {"m": "8f2f9da53ad6c8710eccae999ffda68e15bc914368138914563a812ebcfbf4a1", "v": 2, "sig": "000000000000000000000000000000000000000000000000000000000000000167d8226c8d962f53a4872ef479e888c2717f81a5068a9fd546f66609101db3f8", "mc": true, "res": null}, + {"m": "8e3b38737dc9d033ebacb32b55fe911cd4ab44603efa8551d75f037c3453bdc5", "v": 3, "sig": "000000000000000000000000000000014551231950b75fc4402da1722fc9baed46154f01375f09ef59d305666d1596ceeb8c53bfbc91a06db034314fdf20c3e6", "mc": true, "res": null}, + {"m": "8ce2ffe78973d08a7e39279ea4408de78b34868f614c3dc3126e945679427448", "v": 2, "sig": "000000000000000000000000000000014551231950b75fc4402da1722fc9baee57c331e49aaef9a8daa3844c0ccf772a8934e0ba456c04a5ffc9437b5996b931", "mc": true, "res": null}, + {"m": "c9fb1e754fddef4dd8cac7486c5cf7b5b3d47f5c9da79d41b33db9687d28036f", "v": 3, "sig": "000000000000000000000000000000014551231950b75fc4402da1722fc9baef55031bc641e899d4cd4def21bc3a62d33bddb424a7b332f9d4e9c701c05bd457", "mc": true, "res": null}, + {"m": "7f63e177efbd028b05aea4c1b05aeeb62037170ec31f0fc9bc2c51c3371ff91f", "v": 3, "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036414004abbd64f9b25e3a0f5b1ded5189b9afd6b09e2987aad05acb778565866c12ef", "mc": true, "res": null}, + {"m": "8dd7666fa105e39bbdc0cf67261102e4fec0106f5486c49537afc80c997423e3", "v": 3, "sig": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff565778adced0eb0f89feebd7ecdadc624f973157b9fa27709fd269a85707a404", "mc": true, "res": null}, + {"m": "b561d72f40023750bcf64cff7efc3918fb00c1de40fd0dcbf861b558a754f5e3", "v": 1, "sig": "2a2be355e550215a6e61019e335c2b2b414753242b996772e0a5f813cf44d16f0ed9e01b2531d55a974066b980a968c333de0eae18b7a7a301bfad4f3e6c560f", "mc": true, "res": "87e6ce69bd4fa0539968c13242163aee72be84896570de540a5aacbfe19d8f600d4f82c662295d66494770a0abd5bb4b8e45d5324e17fb7fa9b5f60177faa185"}, + {"m": "8daf092eb11e987fb03071be36fe04c6bec6fd412ec6e5ebb1f7baebc7e6ffde", "v": 3, "sig": "00000000000000000000000000000000aea63e1143861dd225c99e8f95e0365b5c5e19f3143eae4dc62bea8fee69ecd8f8aa3774917e98933ec6b2939a7da5d9", "mc": true, "res": "7e9cfb23a08aa52981e5f52a1645e4a71fab54a92965e7d1ee96e545457dc7d216d0521c587c13ea153ee9a59b7791e43a2879e785e88f98bfdc640a1e0a7748"}, + {"m": "4495396f835dbcd9f27c180caf241680e52169a9f45628acee298bdf016029b7", "v": 0, "sig": "d01ea74c13fd9598b356e6b8b408eb654a07b1dce17ef8453821d25e2b76243c74991fdd370e9aa4744c9c9d720cecc70c34893f2c23ddd90f3c629fdbddbeb1", "mc": true, "res": null}, + {"m": "394fa6c2779246ffb301ef4edf6547eb50547fefc68634498c7a87ee9462e5a7", "v": 1, "sig": "fbecc7fb2e06c8d65128d600636560be89b08bf224c2edd7942817316b5f4a93412786e5131125d2eea03b95b60a38f790b7d3919d89a7d3cf971592d8c1ca7f", "mc": true, "res": null}, + {"m": "3995370e8d62d6963fdf1de34527d60fe265dee723784e2f88f8e4e309a326bd", "v": 2, "sig": "00000000000000000000000000000000bfef8d0248e2be9445ebb6f2f3ce2cef72fa269810b9b650e263920f22fd680afdc6d4d2cca272d78369d8be239a0560", "mc": true, "res": null}, + {"m": "3c1ef0beae835906be12c59a2927263d8494f84de15bc23001dcc0dc0358ee73", "v": 3, "sig": "0000000000000000000000000000000132c63214cf5fbc4215f04d90a5d3c8503e186686d9aa46cb39b830009cde64d54a335ecde3c497766bca4807349f76ba", "mc": true, "res": null}, + {"m": "1d2ec6558504700fd09483012f07f1fb6037c5f5b59e7360cc84f97630c985cd", "v": 1, "sig": "868a0a9c33a5af1ffac343f22e73883147b2eb4b0a8f4e3dd6224ebb38227151586e315aa1c737a38a2f82a772f03d888b624d65e617ce78c2de44952d80d2d1", "mc": true, "res": null}, + {"m": "a6cb264ad2b4ccbaaf7bfba68398f0cdb167dbe01e37a72b11bb12b2ad3f1448", "v": 0, "sig": "bd08c2f3c2a6b26830c564fdf6bc3c466d2f58877336099821a15d5f72a945673f5fee6f1ad567fc6cd086912b958d93a0a125bc51ff0f8c5661e53c1b5eebfd", "mc": true, "res": null}, + {"m": "3ad565754fd4c98fb7a1ca2ee05ccbd08bb09cfce61f299939b5e1a6081f9e43", "v": 0, "sig": "fffffffffffffffffffffffffffffffed1b68b278c68ee0370a87cfcb492ba3d140966cfa26267062637fd04efec5a19bfbd8111d2d7aa0b161cb1791b7df586", "mc": true, "res": null}, + {"m": "0d960360135b0fcc9fb815de3119548c8ebc678b5edd3c945e6c6f02fa0b1135", "v": 3, "sig": "a9a2c58d295f95e88f0ee218601de843d199861091f972d5867cc9dd655a5d9c32b9647c0d48c62cb1c42ad9b3a29fe1e04a70522136aaec09734f2be43d335e", "mc": true, "res": null}, + {"m": "eb7a0898cf94405eca4907434ddeab2a53c19f17e6ace0b440c295e833dc03f2", "v": 2, "sig": "7ecb678fdc399dd4c963478c5bd16115e878426bf0a95b55d59adba945451a9f1229bbfd08727a46a1b684bec0ece94f8122bec248fab545d23ef0b399f92bd9", "mc": true, "res": null}, + {"m": "1fdf2f2a0e5b342ce45c5deed39ea16fc3815aa8e70489019200341c3905b70d", "v": 3, "sig": "239eb32157556118df3f4780b3145213e99b74660ab8f6d21f6bf6ead8ecd2fe10aa596c844482abb0b9fa0f7b97e8d28d4403d0eae1295722ae985b170ee6af", "mc": true, "res": null}, + {"m": "c77140152f064c7ad68f7122dd6f6bcf361d1eab6ff5bf9a62b0e8e92256126d", "v": 0, "sig": "ffffffffffffffffffffffffffffffffe4d800b28ff24025b6bf0e3be215343b3cc84d6bac3087ee38b8ad05a51594150517aca455d76f3d01b61a8690483154", "mc": true, "res": null}, + {"m": "fe4beb0d12ae36383c37cb48c1ecc7a36fd171ce07262dc5bc7d35b9bfa3d43d", "v": 3, "sig": "c5a08b112b08ea7680cc20efed9730b87308d9b96440ccef6a88fd4f312866862d728165e6510b0ca371e17e2dc3b3bc141c0eb41122abb7102d90530acf936c", "mc": true, "res": null}, + {"m": "047c8944b88cef51d5f73a1f6fa8cd53a8f8f31dc6a90901fcfb36785e0a104f", "v": 1, "sig": "ffffffffffffffffffffffffffffffff8dcac69d9ba9b49aaca6edd4fced2b4e088985f2f5fdfbbeced8dbbb02329698e3de5c37b429688ab3148f944b4aa28a", "mc": true, "res": null}, + {"m": "344b49765219d1d5cf3a02db728cf1fe9edc3a924a4afca4b6d79798fcafeae1", "v": 2, "sig": "925a578d44df4bbe1d493f9fa113b64f744a54d04d70f95fd9b6bdbe14c78bac7d2c09b36366b4899e452220c73ba4a55bcd55cf0b0658fd868d83fed6eb265f", "mc": true, "res": null}, + {"m": "4a66f24e62a161004f68ae0fb856cb67a3c538ae7a0998e850a0f1345a1a2e7d", "v": 2, "sig": "0000000000000000000000000000000073bf6055a2244e3a2650f8617fd21d140000000000000000000000000000000000000000000000000000000000000000", "mc": true, "res": null}, + {"m": "09ad25ee6ff525df73ea71f5c4d5f0baeb368d087ffcda99525579b1d45286e8", "v": 3, "sig": "00000000000000000000000000000000ecb1015df79ad00e8dbbf8914fa2c5e10000000000000000000000000000000000000000000000000000000000000000", "mc": true, "res": null}, + {"m": "dbcff0d362fb2fe15529d099581a80a6d4dbb77916b0ba2036bc0948126c75f1", "v": 1, "sig": "bf3ad706635d7a9a6b744fb9409442a06a27b374cec6270a5938412ff4e189300000000000000000000000000000000000000000000000000000000000000001", "mc": true, "res": null}, + {"m": "a4bf8f79f89ef6ed53bc8398ac5ce0d0af90ff4433513275c307ab216f3d3c7a", "v": 2, "sig": "0000000000000000000000000000000136826b058c5cd7e9415c884f95fb5e5b0000000000000000000000000000000000000000000000000000000000000001", "mc": true, "res": "7ee4deb839ff6343e2e4ca7d18ae7a936881bd62a5c678178cadc649492600169c14190e13a4e54451c939ee6071c3e8d1d9127517c856e9b30a434977310764"}, + {"m": "3e9c1a03e606664788de6467aeace296e47da232d3da6f8ea1796a78d260c05a", "v": 0, "sig": "9435b8eb378246ed092bf8919dec2d7596300d3c7266c353566acc37e735a6317fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", "mc": true, "res": null}, + {"m": "ec4ef77d0ce0d9759bf13b06e22ee697a719fa15bf7802298aabd46cad8b9d3a", "v": 0, "sig": "606e68804407773afd1022980357549ecabeb2f1c4f9986bd83ce17bde758d167fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", "mc": true, "res": "1378c1b05a2d804df7486897223e1fae74ce15eaf4bfdd48a674dea516f447885e6234fc06eeacb703dcb1ce2b80bb1a7481d02395d1f90f3a856b4b3d757e3f"}, + {"m": "b9a0ceab6aa6385b7787421dab091e4f83699afcd1e369d67f8330807a0c2260", "v": 3, "sig": "000000000000000000000000000000000da4b5e11060ae17fd23b0e0c477db6f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1", "mc": true, "res": null}, + {"m": "01327f9329fb32dac9116d84c26150e14e08292abe99d6919de2d0831a6cc06e", "v": 3, "sig": "000000000000000000000000000000001cb46a7876ccb8dd6db67fa63fe5bc407fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1", "mc": true, "res": null}, + {"m": "0d43e99781a7cc84cf9457fab204e3a1ac09753d95bece422a583684ffb8ac07", "v": 1, "sig": "1d41b71a489a62e8f07b667070160a002042ec332bf4c411b99d439465c4b765fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", "mc": true, "res": null}, + {"m": "f29a64e04f4a861741bfca40545b8394afc635676f70f903bf6c08d43108b0fe", "v": 0, "sig": "f6c776b4e3e7c2d2e3363e7c61a3467f8c04e7ec383e3060582fbc52d6b499abfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", "mc": true, "res": null}, + {"m": "bd21771a2294f9fe007ebeb80b819676e9ee706e568d4acb39a8bf18f2b0c469", "v": 3, "sig": "000000000000000000000000000000000189c7dc08e43406c4ddb93759766eaafffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", "mc": true, "res": null}, + {"m": "604878934061a81d3bb6eace4c549ed5d576798189e9195086860e0de767ad07", "v": 0, "sig": "45b9b8354a118ac53f3118c96435197a2921a4fef7bb4b75af8189be4ad0cefcfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", "mc": true, "res": null}, + {"m": "803bbfd81d323e356b5a39b096f64547196aeea820933f7b324df3f854fec790", "v": 1, "sig": "247027d79a042ec67acd93636bee3e4c18b009bc2aa13bbbbb8b330edd233c60fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", "mc": true, "res": null}, + {"m": "e1e8ec58dbc054b6c153ff4e119c44a41e089eb4cfbccf5c390d177d07b5764f", "v": 2, "sig": "0000000000000000000000000000000067ef19433ada3f7bc76fef0309e1ae4cfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", "mc": true, "res": null}, + {"m": "95e52cefa51e4b4c9b56bd1d9fb15be524154a4f68c4092ba0bcd908a1fc5996", "v": 2, "sig": "0000000000000000000000000000000062ea302e2c1c3fbbdb45390aa7748d35ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "mc": true, "res": null}, + {"m": "0128a67ac091f380e7bd502ffdeff47466d01ef595d39c1fd08592e8cd06a76e", "v": 1, "sig": "fb6f7c2caced9a479f4d155c3be3d87fda4ab81962120d24137e4dda4cb3ee34ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "mc": true, "res": null}, + {"m": "b1a41dce5e5818f4de772d03b7b55938f996229b4e55af35392ee6b2f3ce0969", "v": 3, "sig": "0000000000000000000000000000000086fd52abc0ba40a1b4186a0eab8c6c6c759bac2bd1e7e0ed1379d47c4ea03fe118c8d26db959a2b40d06f867aebc9308", "mc": true, "res": null}, + {"m": "3db50e32e0a754ff51d8c69a6612dc48b9bc14bc9474845566794ad34c956310", "v": 2, "sig": "0000000000000000000000000000000112f55bad864a96a661fb21e2d1fb9d6836068c9816a464f682b0fbd569e66c3065feef23eeba8041e702492e85f892a7", "mc": true, "res": "a81dd765e0b2b6ffce270cd908bcd07eb49484679575e595711067bd9f4e749f5ab33f4021e3e2367ad68819a156c5a7f8ead16ad1c7ce7d765906c9eb3862fc"}, + {"m": "bb20bd0dafab5af6f3eea17584d047a6e665bf53f74f4c9e37c6a0794a952b60", "v": 3, "sig": "0000000000000000000000000000000042b132b7a54c634b149a0ee8c2cc85e734589817b0ac6ea9d74dd910ea746ead2000ebf555a2b4ade4977688c4bf327f", "mc": true, "res": null}, + {"m": "52c8b5aab183e5928f1bd5ab13e3fc62c04db0b7d6c3ab7d2f9fc849245d7aeb", "v": 3, "sig": "000000000000000000000000000000002fff641cf3db000096b4e6b7f4daf11b7a25b1d4c5bd40b5686b58a366380ca753e447785dab1d343308d4363255f948", "mc": true, "res": "97db65e9f8408c7ae8ce98ebdae26209bb4ef30e16dceb3a72dac19bed8e746e9f9dbc39f37fe153cc1de12cec476152d04e5fe3a9167be651414738853d2efa"}, + {"m": "79e98b5777cffe950f8cb22a59aad52b1e3ff4cd0d0b1f2a1a84d9341fd13789", "v": 3, "sig": "00000000000000000000000000000000c0cfd9080ade2842936c134a10a0bf4543cf7cfa0d17dfd1a2722de2ce1af71cbd767fa1d112b1175d598c78a3cfd20f", "mc": true, "res": null}, + {"m": "25644d3eed96025fd0e9dc44b19b1e4bbceab8fcf2adcc46432aee2b181b2201", "v": 2, "sig": "000000000000000000000000000000000cfaad670f3579f90b6009cd9378e5bf4c934a7d7fc7868150b91491f031f797fb43b9e945da42365f62d846351f7981", "mc": true, "res": "b900ef8dc5a88538d609f4ff7b1cd0a9476184dc19eb12bf22099dada35c7614455aa3ef426ecae7ca1a73d8631f4f77081d7329fcc6f1653d11287e667eaa9e"}, + {"m": "c8ed2e4ba5cf4ee0cc93faee07a7fe4a2227a69159c58664cdb9a268be296e49", "v": 0, "sig": "18830c37053f8b32b451a3392de16acb21caba56394e32dbf87ca1b9b0c5e6eb437ba5569883da1541a5fbc2fe408f163d8e5a0c1fceaa82947be6f7bff09cef", "mc": true, "res": null}, + {"m": "4a35afe9fb8cf56a7a0ac6433854dbc71b61d8cfc6d942c9d95bcd24a7bbac10", "v": 2, "sig": "00000000000000000000000000000000c141b2b65393ea3a7c518727025ae7c1520377fb53f6ac9a439fac2e8dbc24ea533054d085b519829cd33bfe2928f0eb", "mc": true, "res": "d6185b3e3a7f69fb5214c615f0644d1e494a05cdae279ac0f21d3f1b570ac95e92ecb6f4eb0d16d22ee2338f577be382e6867bf457e0cabf91778f199c24be50"}, + {"m": "a8d1856525c6a49bdcdc87f7b68859a3d5d7efab3a699b44b0a036865dc6a9b2", "v": 1, "sig": "5f6d07ac1c7c71e144744afaea7feedc7f5c578abba82498e911e70c2cecec2e6724257b94a7f1337b51ba16d66f35fcfe74d425da9eae678efaa2d0015b13d5", "mc": true, "res": null}, + {"m": "4676ac1d8e15f1f160a5344dd9c59e9cbb3cb404156dc8b5a8eafa093ef216bb", "v": 3, "sig": "00000000000000000000000000000000f65c1de85ebde2cd1fce6e19a39c4e843f54e8d8e9f35ca19d905198cd273c0050e2993535f4ec88b4f08806bfe1f5a3", "mc": true, "res": "1917c49315a917f2e16a20703ae57ff72271e7b2cbe055756b674e845633faa438dad768ed6fd6eb1f9337de5ce4022ae040d803cf5012b078e1edd449b67512"}, + {"m": "138ba560ab5241a0f71a6dd2a1d77a984c27cb4aa14faec538cafd6f654b3419", "v": 0, "sig": "6ccacab61f138d4c2074f456320ee37de89a3e3f0cd064d46941849be76cc30176a53aad96b6028acf7c86b18174357603ef6c4d93709d9a5dd1c8d599877924", "mc": true, "res": null}, + {"m": "76b71d4918eca01bf988fa46c076d2354f637ca8bb32bac33765eb0c602c9e76", "v": 2, "sig": "000000000000000000000000000000004f37fa0b61ddeb8ee47ae6c9fedfdb9b36090f03879e2a3c3307bcd693c75a4131965ee955885a964a5203d473fac088", "mc": true, "res": "0b19c814940d592ae43f6f25b6a3a2c92bce76634254e6ba3cdb017021e2644c022a9efe194ea0517530db11416dc0d7b4461452fe9c8f4a2b876256dd9a14fb"}, + {"m": "35cd302783e010300fe0291f96ee0206803af81d8af3e1ecee3d6459bfb1b823", "v": 1, "sig": "c0396d8d08a3dd299c2b6ab2210ecbe264d792b5b416d9ee6d093b782ee3cbe579998f498beb58a728235b2985727248280977ee3d82b52b6c08e986e783dcec", "mc": true, "res": null}, + {"m": "dbd5f9934194dead9b2aa71f8611034fb01af4a8118ba086f54f95717b101f83", "v": 3, "sig": "000000000000000000000000000000005125a1770c72e1211afea4043101533d49150fc0844d4c7dae1bd26fd3ce1a6403c0df4d95b56052ac4d8911c3a29e1f", "mc": true, "res": "a0d02e3c6786316bf4e287a56e77a45a34f48a7242c012e9438156d5f36e68ea9d5b7a4362b74cdd0bd9b85afd1e7da1d37b1a02f7d2f5185f74b7a8b1fb1e39"}, + {"m": "424e830fb1295180275d5cf61128db17ec5ae8ef78f4c2c2932970cb99086099", "v": 0, "sig": "85b1991a4e5d26aa3943315822243fe38bff49e897ef8625f3e3f1a8f380051f110257985ca2e4e313ee754f647f547efe05a39fd5b9bda752c3f46497d99eb0", "mc": true, "res": null}, + {"m": "7721b8ad0a462ee71663b2df54781d4a87216109d9eb776157cf1dfccf7a7f61", "v": 3, "sig": "000000000000000000000000000000013e183d8a2b63893b6b533b46483f2bcc67a5a94acfa3d32619cf8e07012a906e51c6c81d3497af39aa3788d4dd00c0cb", "mc": true, "res": "04e30d14537b5ca89b1f7a24ecc34e9cc74e459181135b8753b17a45b7c5faab337fc96ea805d425a504c172ad21f1a7fb66bb228b0df3e05ab1d3ce568ab27f"}, + {"m": "79e2307c2d1b2a57bfa490c5e7139e22b8a2e7988002d37e3a8eab1e40376255", "v": 1, "sig": "eab4e52615e7bcf8f59a922d740508a66cb48ebbddd0f770486b99820b2d891feab809b81b31bb984535cc613901c89751db409d128fc49805185f9cc3cebf31", "mc": true, "res": null}, + {"m": "5cb072b5737913b3afab9f09caedf0f187ed0b0e50cedb852699345dfa2ccf61", "v": 0, "sig": "49f99634f19595221c46c595cb3e5dfb72d338ef6d9ba9999b472d7b8159f134e9ffbc8068586a6751d85ac137ea6d42f7b1e5cfedbf027b4e84419c24f3475f", "mc": true, "res": null}, + {"m": "7f292c817badce7666b66bf42c51194b2129c4b8083652bc29c4fe440a8589be", "v": 1, "sig": "38fe54f0486bf861d3557408b70175b066dcdf3c42837b9560ff3e058663e98088c9000106dfcca68f7e28c2f89b5d29bc5b6f672967bdfc23fb668bf722bdf0", "mc": true, "res": null}, + {"m": "b6526b617da4cb841ed34cd267f8a9d859aee9063bdbc2a979b5000d559057ef", "v": 2, "sig": "000000000000000000000000000000004e8dc369937fa679e2db876c8558eae9eefe58d5a837ba2230307bdee8330308f864fc659e6aaa583306703d32066e62", "mc": true, "res": null}, + {"m": "3b8bad1974dbb16b1c867a789a79ca28130af1ae0fc67ab9d3a311705b55164f", "v": 1, "sig": "11c60198515d4ee44989a0e23259bad7709e01539c2100fc81a921b0a63b9148f1c5b06e06017489b7f424a26317291f22e49d79410def468b5e1bbf443f159e", "mc": true, "res": null}, + {"m": "b58bb30d20c1e1b4fe5689971b54010f71e9361726b7de876313809c8fef2d3f", "v": 1, "sig": "0443199013994baeb498b30002f02cd72ccad7c84d41469c2d89c4b389287eb1bbfe0c3b278a7609463a649a4eab6e4ccd7891149db415b30ed9a7733e0fa9dc", "mc": true, "res": null}, + {"m": "7b177c06b40637d718bc5214882346f3633bafb76eee57fd339ba2a39f7358df", "v": 0, "sig": "7776348dba1164b65ea329798f01d78b3bf4a27acb7dc66764b1e01a0d173d5fb62a4bac7926caf4db5be7fe52f089ce41f58946aa2c0d10e2e75a473f0b1357", "mc": true, "res": null}, + {"m": "a62e5008d60e1e192c4f533f96768211a3a48ef747c277c8f842e7e73a4935da", "v": 3, "sig": "000000000000000000000000000000011fe6327f493b7514c297e4be2417034fa159138012a3765357e50e63660c8c6a9f9dcb663d375b259cce3d42e9573bcf", "mc": true, "res": null}, + {"m": "f204dadeaaf820f1659bb427b9f10450ad26f78dccef376359da753f24c3b4c1", "v": 3, "sig": "00000000000000000000000000000000402252410c06b274e024dc1ed9d6a92d87526a3d314706ab0858f56841158c163988c394f36db4d9d6821b75f27828aa", "mc": true, "res": null}, + {"m": "67fb6b5be50366f2c29024c07fffdb91f1431eb22dfe74a5487b19a10ae12940", "v": 0, "sig": "8007d2318499f6f4551a1d7090de952cb8729ff5ea5520b945a50714a48393b0f01ae79ee475ab08be90721a6c4b45be2c6e037be4625578f6c680f23344bea5", "mc": true, "res": null}, + {"m": "60bb9f387b28dc4975739e173aae22a90b46814b86e325f2c8b6aa1886212181", "v": 2, "sig": "0000000000000000000000000000000074a220d2108c3122db656bc16fd477c1d26ebb24b2c7d7aa57cdce689fea38f37538b870ca99f56adedfc1da24c74bf3", "mc": true, "res": null}, + {"m": "1f57c17c8d65a040a5b5d4e918bd2041b255ccd5c25ed370ec266800ced00642", "v": 0, "sig": "6afa9e7138962ba6a18f5d71422a4af151032a22090e355745dba4e6ff6bdcacc04293d09702b83bd16e9d4469b30e0ddf86789bc4ab2525719186d2bb96effc", "mc": true, "res": null}, + {"m": "bf16f589b3822e5effcfb8a96ea07a83b5fae95100d9c0f35e3023ae4b6d5cd1", "v": 1, "sig": "7f7b95c67523440234d6f52b07fd42ad1764113954a0b2e2197e344bf998ede9b0bfb298c187cd51544132fb226d9135cedc3f699ef942aa0382b834287ebd79", "mc": true, "res": null}, + {"m": "9f1bc3245e5f1109dbd444e8d5ac84a7d04edb8b895a7667e85b966e0e9256a3", "v": 3, "sig": "0000000000000000000000000000000091dea8d180f1f15280979c26ce55f27ee781e1ab72bcd8ce749e75f5a0d414812c4cfa24413eb70ffd679076d42b1a74", "mc": true, "res": null}, + {"m": "1d55a06148d1c4b90405e3e62c445e3bde5fd022523274860af6a40fbe76b6a1", "v": 0, "sig": "2d318ba3d24c15ea17f19a0e2a9ef395c17a7879f5bc2b34e932d67d40f23634f8f4263f5319919a79cacb25f7c77d6fa156ec3bd925560c25b538ab0124f510", "mc": true, "res": null}, + {"m": "a573b2de16a39e0e5b0518d4a8d08c515db298cb8397bf0f21124f07f703f9b1", "v": 0, "sig": "91d0a8342974185d878064fecefc4e42596cc9a7abe6285f37e8e4d0008a39a1b2b737e2ebcae5dc8d7ec7dfd8af5bc4786db9960fd2bb8028317444c178615a", "mc": true, "res": null}, + {"m": "d2d8cff952596c8501355431e50880d7212f07e9655ab1b83a66f0189c3a388b", "v": 1, "sig": "88a9b5d3ba73d078314aaa99ac0ff7723f98409471fa49a6862e49ffa972799a067a89ffce302bc22b5df995e97a41990876dde7ffc0e7279a9aa9f6688c09d7", "mc": true, "res": null} +] diff --git a/runtime/unc-vm-runner/src/logic/tests/ed25519_verify.rs b/runtime/unc-vm-runner/src/logic/tests/ed25519_verify.rs new file mode 100644 index 000000000..74ddfdd2d --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/ed25519_verify.rs @@ -0,0 +1,352 @@ +use crate::logic::tests::helpers::*; +use crate::logic::tests::vm_logic_builder::VMLogicBuilder; +use crate::logic::HostError; +use crate::logic::VMLogicError; +use crate::map; +use unc_parameters::ExtCosts; +use std::collections::HashMap; + +const SIGNATURE: [u8; 64] = [ + 145, 193, 203, 18, 114, 227, 14, 117, 33, 213, 121, 66, 130, 14, 25, 4, 36, 120, 46, 142, 226, + 215, 7, 66, 122, 112, 97, 30, 249, 135, 61, 165, 221, 249, 252, 23, 105, 40, 56, 70, 31, 152, + 236, 141, 154, 122, 207, 20, 75, 118, 79, 90, 168, 6, 221, 122, 213, 29, 126, 196, 216, 104, + 191, 6, +]; + +const BAD_SIGNATURE: [u8; 64] = [1; 64]; + +// create a forged signature with the `s` scalar not properly reduced +// https://docs.rs/ed25519/latest/src/ed25519/lib.rs.html#302 +const FORGED_SIGNATURE: [u8; 64] = { + let mut sig = SIGNATURE; + sig[63] = 0b1110_0001; + sig +}; + +const PUBLIC_KEY: [u8; 32] = [ + 32, 122, 6, 120, 146, 130, 30, 37, 215, 112, 241, 251, 160, 196, 124, 17, 255, 75, 129, 62, 84, + 22, 46, 206, 158, 184, 57, 224, 118, 35, 26, 182, +]; + +// create a forged public key to force a PointDecompressionError +// https://docs.rs/ed25519-dalek/latest/src/ed25519_dalek/public.rs.html#142 +const FORGED_PUBLIC_KEY: [u8; 32] = { + let mut key = PUBLIC_KEY; + key[31] = 0b1110_0001; + key +}; + +// 32 bytes message +const MESSAGE: [u8; 32] = [ + 107, 97, 106, 100, 108, 102, 107, 106, 97, 108, 107, 102, 106, 97, 107, 108, 102, 106, 100, + 107, 108, 97, 100, 106, 102, 107, 108, 106, 97, 100, 115, 107, +]; + +#[track_caller] +fn check_ed25519_verify( + signature_len: u64, + signature: &[u8], + message_len: u64, + message: &[u8], + public_key_len: u64, + public_key: &[u8], + want: Result, + want_costs: HashMap, +) { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + + let signature_ptr = if signature_len == u64::MAX { + logic.wrapped_internal_write_register(1, &signature).unwrap(); + 1 + } else { + logic.internal_mem_write(signature).ptr + }; + + let message_ptr = if message_len == u64::MAX { + logic.wrapped_internal_write_register(2, &message).unwrap(); + 2 + } else { + logic.internal_mem_write(message).ptr + }; + + let public_key_ptr = if public_key_len == u64::MAX { + logic.wrapped_internal_write_register(3, &public_key).unwrap(); + 3 + } else { + logic.internal_mem_write(public_key).ptr + }; + + let result = logic.ed25519_verify( + signature_len, + signature_ptr, + message_len, + message_ptr, + public_key_len, + public_key_ptr, + ); + + let want = want.map_err(VMLogicError::HostError); + assert_eq!(want, result); + assert_costs(want_costs); +} + +#[test] +fn test_ed25519_verify_behavior_and_errors() { + check_ed25519_verify( + SIGNATURE.len() as u64, + &SIGNATURE, + MESSAGE.len() as u64, + &MESSAGE, + PUBLIC_KEY.len() as u64, + &PUBLIC_KEY, + Ok(1), + map! { + ExtCosts::read_memory_byte: 128, + ExtCosts::read_memory_base: 3, + ExtCosts::ed25519_verify_base: 1, + ExtCosts::ed25519_verify_byte: 32, + }, + ); + check_ed25519_verify( + SIGNATURE.len() as u64, + &SIGNATURE, + MESSAGE.len() as u64, + &MESSAGE, + PUBLIC_KEY.len() as u64, + &FORGED_PUBLIC_KEY, + Ok(0), + map! { + ExtCosts::read_memory_byte: 128, + ExtCosts::read_memory_base: 3, + ExtCosts::ed25519_verify_base: 1, + ExtCosts::ed25519_verify_byte: 32, + }, + ); + check_ed25519_verify( + SIGNATURE.len() as u64, + &SIGNATURE, + MESSAGE.len() as u64, + &MESSAGE, + PUBLIC_KEY.len() as u64 - 1, + &PUBLIC_KEY, + Err(HostError::Ed25519VerifyInvalidInput { msg: "invalid public key length".to_string() }), + map! { + ExtCosts::read_memory_byte: 127, + ExtCosts::read_memory_base: 3, + ExtCosts::ed25519_verify_base: 1, + ExtCosts::ed25519_verify_byte: 32, + }, + ); + check_ed25519_verify( + BAD_SIGNATURE.len() as u64, + &BAD_SIGNATURE, + MESSAGE.len() as u64, + &MESSAGE, + PUBLIC_KEY.len() as u64, + &PUBLIC_KEY, + Ok(0), + map! { + ExtCosts::read_memory_byte: 128, + ExtCosts::read_memory_base: 3, + ExtCosts::ed25519_verify_base: 1, + ExtCosts::ed25519_verify_byte: 32, + }, + ); + check_ed25519_verify( + SIGNATURE.len() as u64 - 1, + &SIGNATURE, + MESSAGE.len() as u64, + &MESSAGE, + PUBLIC_KEY.len() as u64, + &PUBLIC_KEY, + Err(HostError::Ed25519VerifyInvalidInput { msg: "invalid signature length".to_string() }), + map! { + ExtCosts::read_memory_base: 1, + ExtCosts::read_memory_byte: 63, + ExtCosts::ed25519_verify_base: 1, + }, + ); + check_ed25519_verify( + FORGED_SIGNATURE.len() as u64, + &FORGED_SIGNATURE, + MESSAGE.len() as u64, + &MESSAGE, + PUBLIC_KEY.len() as u64, + &PUBLIC_KEY, + Ok(0), + map! { + ExtCosts::read_memory_base: 1, + ExtCosts::read_memory_byte: 64, + ExtCosts::ed25519_verify_base: 1, + }, + ); + check_ed25519_verify( + FORGED_SIGNATURE.len() as u64, + &FORGED_SIGNATURE, + 0, + &[], + PUBLIC_KEY.len() as u64, + &PUBLIC_KEY, + Ok(0), + map! { + ExtCosts::read_memory_base: 1, + ExtCosts::read_memory_byte: 64, + ExtCosts::ed25519_verify_base: 1, + }, + ); + check_ed25519_verify( + SIGNATURE.len() as u64, + &SIGNATURE, + 0, + &[], + PUBLIC_KEY.len() as u64, + &PUBLIC_KEY, + Ok(0), + map! { + ExtCosts::read_memory_base: 3, + ExtCosts::read_memory_byte: 96, + ExtCosts::ed25519_verify_base: 1, + ExtCosts::ed25519_verify_byte: 0, + }, + ); +} + +// tests for data being read from registers +#[test] +fn test_ed25519_verify_check_registers() { + check_ed25519_verify( + u64::MAX, + &SIGNATURE, + MESSAGE.len() as u64, + &MESSAGE, + PUBLIC_KEY.len() as u64, + &PUBLIC_KEY, + Ok(1), + map! { + ExtCosts::write_register_base: 1, + ExtCosts::write_register_byte: 64, + + ExtCosts::read_register_base: 1, + ExtCosts::read_register_byte: 64, + ExtCosts::read_memory_base: 2, + ExtCosts::read_memory_byte: 64, + ExtCosts::ed25519_verify_base: 1, + ExtCosts::ed25519_verify_byte: 32, + }, + ); + check_ed25519_verify( + SIGNATURE.len() as u64, + &SIGNATURE, + u64::MAX, + &MESSAGE, + PUBLIC_KEY.len() as u64, + &PUBLIC_KEY, + Ok(1), + map! { + ExtCosts::write_register_base: 1, + ExtCosts::write_register_byte: 32, + + ExtCosts::read_register_base: 1, + ExtCosts::read_register_byte: 32, + ExtCosts::read_memory_base: 2, + ExtCosts::read_memory_byte: 96, + ExtCosts::ed25519_verify_base: 1, + ExtCosts::ed25519_verify_byte: 32, + }, + ); + check_ed25519_verify( + SIGNATURE.len() as u64, + &SIGNATURE, + MESSAGE.len() as u64, + &MESSAGE, + u64::MAX, + &PUBLIC_KEY, + Ok(1), + map! { + ExtCosts::write_register_base: 1, + ExtCosts::write_register_byte: 32, + + ExtCosts::read_register_byte: 32, + ExtCosts::read_register_base: 1, + ExtCosts::read_memory_base: 2, + ExtCosts::read_memory_byte: 96, + ExtCosts::ed25519_verify_base: 1, + ExtCosts::ed25519_verify_byte: 32, + }, + ); + check_ed25519_verify( + u64::MAX, + &BAD_SIGNATURE, + MESSAGE.len() as u64, + &MESSAGE, + PUBLIC_KEY.len() as u64, + &PUBLIC_KEY, + Ok(0), + map! { + ExtCosts::write_register_base: 1, + ExtCosts::write_register_byte: 64, + + ExtCosts::read_register_base: 1, + ExtCosts::read_register_byte: 64, + ExtCosts::read_memory_base: 2, + ExtCosts::read_memory_byte: 64, + ExtCosts::ed25519_verify_byte: 32, + ExtCosts::ed25519_verify_base: 1, + }, + ); + check_ed25519_verify( + u64::MAX, + &FORGED_SIGNATURE, + MESSAGE.len() as u64, + &MESSAGE, + PUBLIC_KEY.len() as u64, + &PUBLIC_KEY, + Ok(0), + map! { + ExtCosts::write_register_base: 1, + ExtCosts::write_register_byte: 64, + + ExtCosts::read_register_base: 1, + ExtCosts::read_register_byte: 64, + ExtCosts::ed25519_verify_base: 1, + }, + ); + check_ed25519_verify( + u64::MAX, + &[0], + MESSAGE.len() as u64, + &MESSAGE, + PUBLIC_KEY.len() as u64, + &PUBLIC_KEY, + Err(HostError::Ed25519VerifyInvalidInput { msg: "invalid signature length".to_string() }), + map! { + ExtCosts::write_register_base: 1, + ExtCosts::write_register_byte: 1, + + ExtCosts::read_register_base: 1, + ExtCosts::read_register_byte: 1, + ExtCosts::ed25519_verify_base: 1, + }, + ); + check_ed25519_verify( + SIGNATURE.len() as u64, + &SIGNATURE, + MESSAGE.len() as u64, + &MESSAGE, + u64::MAX, + &[0], + Err(HostError::Ed25519VerifyInvalidInput { msg: "invalid public key length".to_string() }), + map! { + ExtCosts::write_register_base: 1, + ExtCosts::write_register_byte: 1, + + ExtCosts::read_register_base: 1, + ExtCosts::read_register_byte: 1, + ExtCosts::read_memory_base: 2, + ExtCosts::read_memory_byte: 96, + ExtCosts::ed25519_verify_byte: 32, + ExtCosts::ed25519_verify_base: 1, + }, + ); +} diff --git a/runtime/unc-vm-runner/src/logic/tests/gas_counter.rs b/runtime/unc-vm-runner/src/logic/tests/gas_counter.rs new file mode 100644 index 000000000..a943dbb84 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/gas_counter.rs @@ -0,0 +1,734 @@ +use crate::logic::tests::helpers::*; +use crate::logic::tests::vm_logic_builder::{TestVMLogic, VMLogicBuilder}; +use crate::logic::types::Gas; +use crate::logic::MemSlice; +use crate::logic::{HostError, VMLogicError}; +use crate::tests::test_vm_config; +use expect_test::expect; +use unc_parameters::{ActionCosts, ExtCosts, Fee}; + +#[test] +fn test_dont_burn_gas_when_exceeding_attached_gas_limit() { + let gas_limit = 10u64.pow(14); + + let mut logic_builder = VMLogicBuilder::default(); + logic_builder.config.limit_config.max_gas_burnt = gas_limit * 2; + logic_builder.context.prepaid_gas = gas_limit; + let mut logic = logic_builder.build(); + + let index = promise_create(&mut logic, b"rick.test", 0, 0).expect("should create a promise"); + promise_batch_action_function_call(&mut logic, index, 0, gas_limit * 2) + .expect_err("should fail with gas limit"); + let outcome = logic.compute_outcome(); + + // Just avoid hard-coding super-precise amount of gas burnt. + assert!(outcome.burnt_gas < gas_limit / 2); + assert_eq!(outcome.used_gas, gas_limit); +} + +#[test] +fn test_limit_wasm_gas_after_attaching_gas() { + let gas_limit = 10u64.pow(14); + let op_limit = op_limit(gas_limit); + + let mut logic_builder = VMLogicBuilder::default(); + logic_builder.config.limit_config.max_gas_burnt = gas_limit * 2; + logic_builder.context.prepaid_gas = gas_limit; + let mut logic = logic_builder.build(); + + let index = promise_create(&mut logic, b"rick.test", 0, 0).expect("should create a promise"); + promise_batch_action_function_call(&mut logic, index, 0, gas_limit / 2) + .expect("should add action to receipt"); + logic.gas_opcodes((op_limit / 2) as u32).expect_err("should fail with gas limit"); + let outcome = logic.compute_outcome(); + + assert_eq!(outcome.used_gas, gas_limit); + assert!(gas_limit / 2 < outcome.burnt_gas); + assert!(outcome.burnt_gas < gas_limit); +} + +#[test] +fn test_cant_burn_more_than_max_gas_burnt_gas() { + let gas_limit = 10u64.pow(14); + let op_limit = op_limit(gas_limit); + + let mut logic_builder = VMLogicBuilder::default(); + logic_builder.config.limit_config.max_gas_burnt = gas_limit; + logic_builder.context.prepaid_gas = gas_limit * 2; + let mut logic = logic_builder.build(); + + logic.gas_opcodes(op_limit * 3).expect_err("should fail with gas limit"); + let outcome = logic.compute_outcome(); + + assert_eq!(outcome.burnt_gas, gas_limit); + assert_eq!(outcome.used_gas, gas_limit * 2); +} + +#[test] +fn test_cant_burn_more_than_prepaid_gas() { + let gas_limit = 10u64.pow(14); + let op_limit = op_limit(gas_limit); + + let mut logic_builder = VMLogicBuilder::default(); + logic_builder.config.limit_config.max_gas_burnt = gas_limit * 2; + logic_builder.context.prepaid_gas = gas_limit; + let mut logic = logic_builder.build(); + + logic.gas_opcodes(op_limit * 3).expect_err("should fail with gas limit"); + let outcome = logic.compute_outcome(); + + assert_eq!(outcome.burnt_gas, gas_limit); + assert_eq!(outcome.used_gas, gas_limit); +} + +#[test] +fn test_hit_max_gas_burnt_limit() { + let gas_limit = 10u64.pow(14); + let op_limit = op_limit(gas_limit); + + let mut logic_builder = VMLogicBuilder::default(); + logic_builder.config.limit_config.max_gas_burnt = gas_limit; + logic_builder.context.prepaid_gas = gas_limit * 3; + let mut logic = logic_builder.build(); + + promise_create(&mut logic, b"rick.test", 0, gas_limit / 2).expect("should create a promise"); + logic.gas_opcodes(op_limit * 2).expect_err("should fail with gas limit"); + let outcome = logic.compute_outcome(); + + assert_eq!(outcome.burnt_gas, gas_limit); + assert!(outcome.used_gas > gas_limit * 2); +} + +#[test] +fn test_hit_prepaid_gas_limit() { + let gas_limit = 10u64.pow(14); + let op_limit = op_limit(gas_limit); + + let mut logic_builder = VMLogicBuilder::default(); + logic_builder.config.limit_config.max_gas_burnt = gas_limit * 3; + logic_builder.context.prepaid_gas = gas_limit; + let mut logic = logic_builder.build(); + + promise_create(&mut logic, b"rick.test", 0, gas_limit / 2).expect("should create a promise"); + logic.gas_opcodes(op_limit * 2).expect_err("should fail with gas limit"); + let outcome = logic.compute_outcome(); + + assert_eq!(outcome.burnt_gas, gas_limit); + assert_eq!(outcome.used_gas, gas_limit); +} + +#[test] +fn function_call_no_weight_refund() { + let gas_limit = 10u64.pow(14); + + let mut logic_builder = VMLogicBuilder::default(); + logic_builder.config.limit_config.max_gas_burnt = gas_limit; + logic_builder.context.prepaid_gas = gas_limit; + let mut logic = logic_builder.build(); + + let index = promise_batch_create(&mut logic, "rick.test").expect("should create a promise"); + promise_batch_action_function_call_weight(&mut logic, index, 0, 1000, 0) + .expect("batch action function call should succeed"); + + let outcome = logic.compute_outcome(); + + // Verify that unused gas was not allocated to function call + assert!(outcome.used_gas < gas_limit); +} + +#[test] +fn test_overflowing_burn_gas_with_promises_gas() { + let gas_limit = 3 * 10u64.pow(14); + let mut logic_builder = VMLogicBuilder::default(); + logic_builder.config.limit_config.max_gas_burnt = gas_limit; + logic_builder.context.prepaid_gas = gas_limit; + let mut logic = logic_builder.build(); + + let account_id = logic.internal_mem_write(b"rick.test"); + let args = logic.internal_mem_write(b""); + let num_100u128 = logic.internal_mem_write(&100u128.to_le_bytes()); + let num_10u128 = logic.internal_mem_write(&10u128.to_le_bytes()); + + let index = promise_batch_create(&mut logic, "rick.test").expect("should create a promise"); + logic.promise_batch_action_transfer(index, num_100u128.ptr).unwrap(); + let call_id = logic.promise_batch_then(index, account_id.len, account_id.ptr).unwrap(); + + let needed_gas_charge = u64::max_value() - logic.gas_counter().used_gas() - 1; + let function_name_len = + needed_gas_charge / logic.config().ext_costs.gas_cost(ExtCosts::read_memory_byte); + let result = logic.promise_batch_action_function_call( + call_id, + function_name_len, + /* function_name_ptr: */ 0, + args.len, + args.ptr, + num_10u128.ptr, + 10000, + ); + assert!(matches!( + result, + Err(crate::logic::VMLogicError::HostError(crate::logic::HostError::GasLimitExceeded)) + )); + assert_eq!(logic.gas_counter().used_gas(), gas_limit); +} + +#[test] +fn test_overflowing_burn_gas_with_promises_gas_2() { + let gas_limit = 3 * 10u64.pow(14); + let mut logic_builder = VMLogicBuilder::default(); + logic_builder.config.limit_config.max_gas_burnt = gas_limit; + logic_builder.context.prepaid_gas = gas_limit / 2; + let mut logic = logic_builder.build(); + + let account_id = logic.internal_mem_write(b"rick.test"); + let args = logic.internal_mem_write(b""); + let num_100u128 = logic.internal_mem_write(&100u128.to_le_bytes()); + + let index = promise_batch_create(&mut logic, "rick.test").expect("should create a promise"); + logic.promise_batch_action_transfer(index, num_100u128.ptr).unwrap(); + logic.promise_batch_then(index, account_id.len, account_id.ptr).unwrap(); + let minimum_prepay = logic.gas_counter().used_gas(); + logic_builder.context.prepaid_gas = minimum_prepay; + let mut logic = logic_builder.build(); + let index = promise_batch_create(&mut logic, "rick.test").expect("should create a promise"); + logic.promise_batch_action_transfer(index, num_100u128.ptr).unwrap(); + let call_id = logic.promise_batch_then(index, account_id.len, account_id.ptr).unwrap(); + let needed_gas_charge = u64::max_value() - logic.gas_counter().used_gas() - 1; + let function_name_len = + needed_gas_charge / logic.config().ext_costs.gas_cost(ExtCosts::read_memory_byte); + let result = logic.promise_batch_action_function_call( + call_id, + function_name_len, + /* function_name_ptr: */ 0, + args.len, + args.ptr, + 10u128.to_le_bytes().as_ptr() as _, + 10000, + ); + assert!(matches!( + result, + Err(crate::logic::VMLogicError::HostError(crate::logic::HostError::GasExceeded)) + )); + assert_eq!(logic.gas_counter().used_gas(), minimum_prepay); +} + +/// Check consistent result when exceeding gas limit on a specific action gas parameter. +/// +/// Increases an action cost to a high value and then watch an execution run out +/// of gas. Then make sure the exact result is still the same. This prevents +/// accidental protocol changes where gas is deducted in different order. +/// +/// The `exercise_action` function must be a function or closure that operates +/// on a `VMLogic` and triggers gas costs associated with the action parameter +/// under test. +/// +/// `num_action_paid` specifies how often the cost is charged in +/// `exercise_action`. We aim to make it `num_action_paid` = 1 in the typical +/// case but for cots per byte this is usually a higher value. +/// +/// `num_action_paid` is required to calculate by how much exactly gas prices +/// must be increased so that it will just trigger the gas limit. +#[track_caller] +fn check_action_gas_exceeds_limit( + cost: ActionCosts, + num_action_paid: u64, + exercise_action: impl FnOnce(&mut TestVMLogic) -> Result<(), VMLogicError>, +) { + // Create a logic parametrized such that it will fail with out-of-gas when specified action is deducted. + let gas_limit = 10u64.pow(13); + let gas_attached = gas_limit; + let fee = Fee { + send_sir: gas_limit / num_action_paid + 1, + send_not_sir: gas_limit / num_action_paid + 10, + execution: 1, // exec part is `used`, make it small + }; + let mut logic_builder = VMLogicBuilder::default(); + logic_builder.config.limit_config.max_gas_burnt = gas_limit; + logic_builder.fees_config.action_fees[cost] = fee; + logic_builder.context.prepaid_gas = gas_attached; + logic_builder.context.output_data_receivers = vec!["alice.test".parse().unwrap()]; + let mut logic = logic_builder.build(); + + let result = exercise_action(&mut logic); + assert!(result.is_err(), "expected out-of-gas error for {cost:?} but was ok"); + assert_eq!(result.unwrap_err(), VMLogicError::HostError(HostError::GasLimitExceeded)); + + // When gas limit is exceeded, we always set burnt_gas := prepaid and then promise_gas := 0. + assert_eq!( + gas_attached, + logic.gas_counter().burnt_gas(), + "burnt gas should be all attached gas", + ); + assert_eq!( + gas_attached, + logic.gas_counter().used_gas(), + "used gas should be no more than burnt gas", + ); +} + +/// Check consistent result when exceeding attached gas on a specific action gas +/// parameter. +/// +/// Very similar to `check_action_gas_exceeds_limit` but we hit a different +/// limit and return a different error. See that comment for an explanation on +/// the arguments. +/// +/// This case is more interesting because the burnt gas can be below used gas, +/// when the prepaid gas was exceeded by burnt burnt + promised gas but not by +/// burnt gas alone. +/// +/// Consequently, `num_action_paid` here is even more important to calculate +/// exactly what the gas costs should be to trigger the limits. +#[track_caller] +fn check_action_gas_exceeds_attached( + cost: ActionCosts, + num_action_paid: u64, + expected: expect_test::Expect, + exercise_action: impl FnOnce(&mut TestVMLogic) -> Result<(), VMLogicError>, +) { + // Create a logic parametrized such that it will fail with out-of-gas when specified action is deducted. + let gas_limit = 10u64.pow(14); + let gas_attached = 10u64.pow(13); + let fee = Fee { + send_sir: 1, // make burnt gas small + send_not_sir: 10, // make it easy to distinguish `sir` / `not_sir` + execution: gas_attached / num_action_paid + 1, + }; + let mut logic_builder = VMLogicBuilder::default(); + logic_builder.config.limit_config.max_gas_burnt = gas_limit; + logic_builder.fees_config.action_fees[cost] = fee; + logic_builder.context.prepaid_gas = gas_attached; + logic_builder.context.output_data_receivers = vec!["alice.test".parse().unwrap()]; + let mut logic = logic_builder.build(); + + let result = exercise_action(&mut logic); + assert!(result.is_err(), "expected out-of-gas error for {cost:?} but was ok"); + assert_eq!(result.unwrap_err(), VMLogicError::HostError(HostError::GasExceeded)); + + let actual = format!( + "{} burnt {} used", + logic.gas_counter().burnt_gas(), + logic.gas_counter().used_gas() + ); + expected.assert_eq(&actual); +} + +// Below are a bunch of `out_of_gas_*` tests. These test that when we run out of +// gas while charging a specific action gas cost, we burn a consistent amount of +// gas. This is to prevent accidental changes in how we charge gas. It cannot +// cover all cases but it can detect things like a changed order of gas charging +// or splitting pay_gas(A+B) to pay_gas(A), pay_gas(B), which went through to +// master unnoticed before. +// +// The setup for these tests is as follows: +// - 1 test per action cost +// - each test checks for 2 types of out of gas errors, gas limit exceeded and +// gas attached exceeded +// - common code to create a test VMLogic setup is in checker functions +// `check_action_gas_exceeds_limit` and `check_action_gas_exceeds_attached` +// which are called from every test +// - each action cost must be triggered in a different way, so we define a small +// function that does something which charges the tested action cost, then we +// give this function to the checker functions +// - if an action cost is charged through different paths, the test defines +// multiple functions that trigger the cost and the checker functions are +// called once for each of them +// - these action cost triggering functions are defined in the test's inner +// scope, unless they are shared between multiple tests + +/// see longer comment above for how this test works +#[test] +fn out_of_gas_new_action_receipt() { + // two different ways to create an action receipts, first check exceeding the burnt limit + check_action_gas_exceeds_limit(ActionCosts::new_action_receipt, 1, create_action_receipt); + check_action_gas_exceeds_limit(ActionCosts::new_action_receipt, 2, create_promise_dependency); + + // the same again, but for prepaid gas + check_action_gas_exceeds_attached( + ActionCosts::new_action_receipt, + 1, + expect!["8644846690 burnt 10000000000000 used"], + create_action_receipt, + ); + + check_action_gas_exceeds_attached( + ActionCosts::new_action_receipt, + 2, + expect!["9411968532130 burnt 10000000000000 used"], + create_promise_dependency, + ); + + /// function to trigger action receipt action cost + fn create_action_receipt(logic: &mut TestVMLogic) -> Result<(), VMLogicError> { + promise_batch_create(logic, "rick.test")?; + Ok(()) + } +} + +/// see longer comment above for how this test works +#[test] +fn out_of_gas_new_data_receipt() { + check_action_gas_exceeds_limit( + ActionCosts::new_data_receipt_base, + 1, + create_promise_dependency, + ); + + check_action_gas_exceeds_attached( + ActionCosts::new_data_receipt_base, + 1, + expect!["10000000000000 burnt 10000000000000 used"], + create_promise_dependency, + ); +} + +/// see longer comment above for how this test works +#[test] +fn out_of_gas_new_data_receipt_byte() { + check_action_gas_exceeds_limit(ActionCosts::new_data_receipt_byte, 11, value_return); + + // expect to burn it all because send + exec fees are fully paid upfront + check_action_gas_exceeds_attached( + ActionCosts::new_data_receipt_byte, + 11, + expect!["10000000000000 burnt 10000000000000 used"], + value_return, + ); + + // value return will pay for the cost of returned data dependency bytes, if there are any. + fn value_return(logic: &mut TestVMLogic) -> Result<(), VMLogicError> { + // 11 characters long string + let value = logic.internal_mem_write(b"lorem ipsum"); + logic.value_return(11, value.ptr)?; + Ok(()) + } +} + +/// see longer comment above for how this test works +#[test] +fn out_of_gas_create_account() { + check_action_gas_exceeds_limit(ActionCosts::create_account, 1, create_account); + + check_action_gas_exceeds_attached( + ActionCosts::create_account, + 1, + expect!["116969114801 burnt 10000000000000 used"], + create_account, + ); + + fn create_account(logic: &mut TestVMLogic) -> Result<(), VMLogicError> { + let account_id = "rick.test"; + let idx = promise_batch_create(logic, account_id)?; + logic.promise_batch_action_create_account(idx)?; + Ok(()) + } +} + +/// see longer comment above for how this test works +#[test] +fn out_of_gas_delete_account() { + check_action_gas_exceeds_limit(ActionCosts::delete_account, 1, delete_account); + + check_action_gas_exceeds_attached( + ActionCosts::delete_account, + 1, + expect!["125349193370 burnt 10000000000000 used"], + delete_account, + ); + + fn delete_account(logic: &mut TestVMLogic) -> Result<(), VMLogicError> { + let beneficiary_account_id = "alice.test"; + let deleted_account_id = "bob.test"; + let idx = promise_batch_create(logic, deleted_account_id)?; + let beneficiary = logic.internal_mem_write(beneficiary_account_id.as_bytes()); + logic.promise_batch_action_delete_account(idx, beneficiary.len, beneficiary.ptr)?; + Ok(()) + } +} + +/// see longer comment above for how this test works +#[test] +fn out_of_gas_deploy_contract_base() { + check_action_gas_exceeds_limit(ActionCosts::deploy_contract_base, 1, deploy_contract); + + check_action_gas_exceeds_attached( + ActionCosts::deploy_contract_base, + 1, + expect!["119677812659 burnt 10000000000000 used"], + deploy_contract, + ); +} + +/// see longer comment above for how this test works +#[test] +fn out_of_gas_deploy_contract_byte() { + check_action_gas_exceeds_limit(ActionCosts::deploy_contract_byte, 26, deploy_contract); + + check_action_gas_exceeds_attached( + ActionCosts::deploy_contract_byte, + 26, + expect!["304443562909 burnt 10000000000000 used"], + deploy_contract, + ); +} + +/// function to trigger base + 26 bytes deployment costs (26 is arbitrary) +fn deploy_contract(logic: &mut TestVMLogic) -> Result<(), VMLogicError> { + let account_id = "rick.test"; + let idx = promise_batch_create(logic, account_id)?; + let code = logic.internal_mem_write(b"lorem ipsum with length 26"); + logic.promise_batch_action_deploy_contract(idx, code.len, code.ptr)?; + Ok(()) +} + +/// see longer comment above for how this test works +#[test] +fn out_of_gas_function_call_base() { + check_action_gas_exceeds_limit(ActionCosts::function_call_base, 1, cross_contract_call); + check_action_gas_exceeds_limit( + ActionCosts::function_call_base, + 1, + cross_contract_call_gas_weight, + ); + + check_action_gas_exceeds_attached( + ActionCosts::function_call_base, + 1, + expect!["125011579049 burnt 10000000000000 used"], + cross_contract_call, + ); + check_action_gas_exceeds_attached( + ActionCosts::function_call_base, + 1, + expect!["125011579049 burnt 10000000000000 used"], + cross_contract_call_gas_weight, + ); +} + +/// see longer comment above for how this test works +#[test] +fn out_of_gas_function_call_byte() { + check_action_gas_exceeds_limit(ActionCosts::function_call_byte, 40, cross_contract_call); + check_action_gas_exceeds_limit( + ActionCosts::function_call_byte, + 40, + cross_contract_call_gas_weight, + ); + + check_action_gas_exceeds_attached( + ActionCosts::function_call_byte, + 40, + expect!["2444873079439 burnt 10000000000000 used"], + cross_contract_call, + ); + check_action_gas_exceeds_attached( + ActionCosts::function_call_byte, + 40, + expect!["2444873079439 burnt 10000000000000 used"], + cross_contract_call_gas_weight, + ); +} + +/// function to trigger base + 40 bytes function call action costs (40 is 26 + +/// 14 which are arbitrary) +fn cross_contract_call(logic: &mut TestVMLogic) -> Result<(), VMLogicError> { + let account_id = "rick.test"; + let idx = promise_batch_create(logic, account_id)?; + let arg = b"lorem ipsum with length 26"; + let name = b"fn_with_len_14"; + let attached_balance = 1u128; + let gas = 1; // attaching very little gas so it doesn't cause gas exceeded on its own + promise_batch_action_function_call_ext(logic, idx, name, arg, attached_balance, gas)?; + Ok(()) +} + +/// same as `cross_contract_call` but splits gas remainder among outgoing calls +fn cross_contract_call_gas_weight(logic: &mut TestVMLogic) -> Result<(), VMLogicError> { + let account_id = "rick.test"; + let idx = promise_batch_create(logic, account_id)?; + let arg = b"lorem ipsum with length 26"; + let name = b"fn_with_len_14"; + let attached_balance = 1u128; + let gas = 1; // attaching very little gas so it doesn't cause gas exceeded on its own + let gas_weight = 1; + promise_batch_action_function_call_weight_ext( + logic, + idx, + name, + arg, + attached_balance, + gas, + gas_weight, + )?; + Ok(()) +} + +/// see longer comment above for how this test works +#[test] +fn out_of_gas_transfer() { + check_action_gas_exceeds_limit(ActionCosts::transfer, 1, promise_transfer); + + check_action_gas_exceeds_attached( + ActionCosts::transfer, + 1, + expect!["119935181141 burnt 10000000000000 used"], + promise_transfer, + ); + + fn promise_transfer(logic: &mut TestVMLogic) -> Result<(), VMLogicError> { + let account_id = "alice.test"; + let idx = promise_batch_create(logic, account_id)?; + let attached_balance = logic.internal_mem_write(&1u128.to_be_bytes()); + logic.promise_batch_action_transfer(idx, attached_balance.ptr)?; + Ok(()) + } +} + +/// see longer comment above for how this test works +#[test] +fn out_of_gas_stake() { + check_action_gas_exceeds_limit(ActionCosts::stake, 1, promise_stake); + + check_action_gas_exceeds_attached( + ActionCosts::stake, + 1, + expect!["122375106518 burnt 10000000000000 used"], + promise_stake, + ); + + fn promise_stake(logic: &mut TestVMLogic) -> Result<(), VMLogicError> { + let account_id = "pool.test"; + let idx = promise_batch_create(logic, account_id)?; + let attached_balance = logic.internal_mem_write(&1u128.to_be_bytes()); + let pk = write_test_pk(logic); + logic.promise_batch_action_stake(idx, attached_balance.ptr, pk.len, pk.ptr)?; + Ok(()) + } +} + +/// see longer comment above for how this test works +#[test] +fn out_of_gas_add_full_access_key() { + check_action_gas_exceeds_limit(ActionCosts::add_full_access_key, 1, promise_full_access_key); + + check_action_gas_exceeds_attached( + ActionCosts::add_full_access_key, + 1, + expect!["119999803802 burnt 10000000000000 used"], + promise_full_access_key, + ); + + fn promise_full_access_key(logic: &mut TestVMLogic) -> Result<(), VMLogicError> { + let account_id = "alice.test"; + let idx = promise_batch_create(logic, account_id)?; + let pk = test_pk(); + let nonce = 0; + promise_batch_action_add_key_with_full_access(logic, idx, &pk, nonce)?; + Ok(()) + } +} + +/// see longer comment above for how this test works +#[test] +fn out_of_gas_add_function_call_key_base() { + check_action_gas_exceeds_limit( + ActionCosts::add_function_call_key_base, + 1, + promise_function_key, + ); + + check_action_gas_exceeds_attached( + ActionCosts::add_function_call_key_base, + 1, + expect!["133982421242 burnt 10000000000000 used"], + promise_function_key, + ); +} + +/// see longer comment above for how this test works +#[test] +fn out_of_gas_add_function_call_key_byte() { + check_action_gas_exceeds_limit( + ActionCosts::add_function_call_key_byte, + 7, + promise_function_key, + ); + + check_action_gas_exceeds_attached( + ActionCosts::add_function_call_key_byte, + 7, + expect!["236200046312 burnt 10000000000000 used"], + promise_function_key, + ); +} + +/// function to trigger base + 7 bytes action costs for adding a new function +/// call access key to an account (7 is arbitrary) +fn promise_function_key(logic: &mut TestVMLogic) -> Result<(), VMLogicError> { + let account_id = "alice.test"; + let idx = promise_batch_create(logic, account_id)?; + let allowance = 1u128; + let pk = test_pk(); + let nonce = 0; + let methods = b"foo,baz"; + promise_batch_action_add_key_with_function_call( + logic, + idx, + &pk, + nonce, + allowance, + account_id.as_bytes(), + methods, + )?; + Ok(()) +} + +/// see longer comment above for how this test works +#[test] +fn out_of_gas_delete_key() { + check_action_gas_exceeds_limit(ActionCosts::delete_key, 1, promise_delete_key); + + check_action_gas_exceeds_attached( + ActionCosts::delete_key, + 1, + expect!["119999803802 burnt 10000000000000 used"], + promise_delete_key, + ); + + fn promise_delete_key(logic: &mut TestVMLogic) -> Result<(), VMLogicError> { + let account_id = "alice.test"; + let idx = promise_batch_create(logic, account_id)?; + let pk = write_test_pk(logic); + logic.promise_batch_action_delete_key(idx, pk.len, pk.ptr)?; + Ok(()) + } +} + +/// function to trigger action + data receipt action costs +fn create_promise_dependency(logic: &mut TestVMLogic) -> Result<(), VMLogicError> { + let account_id = "rick.test"; + let idx = promise_batch_create(logic, account_id)?; + let account_id = logic.internal_mem_write(account_id.as_bytes()); + logic.promise_batch_then(idx, account_id.len, account_id.ptr)?; + Ok(()) +} + +/// Given the limit in gas, compute the corresponding limit in wasm ops for use +/// with [`VMLogic::gas`] function. +fn op_limit(gas_limit: Gas) -> u32 { + (gas_limit / (test_vm_config().regular_op_cost as u64)) as u32 +} + +fn test_pk() -> Vec { + let pk = borsh::to_vec( + &"ed25519:22W5rKuvbMRphnDoCj6nfrWhRKvh9Xf9SWXfGHaeXGde" + .parse::() + .unwrap(), + ) + .unwrap(); + pk +} + +fn write_test_pk(logic: &mut TestVMLogic) -> MemSlice { + logic.internal_mem_write(&test_pk()) +} diff --git a/runtime/unc-vm-runner/src/logic/tests/helpers.rs b/runtime/unc-vm-runner/src/logic/tests/helpers.rs new file mode 100644 index 000000000..8aa892705 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/helpers.rs @@ -0,0 +1,183 @@ +use crate::logic::errors::VMLogicError; +use crate::logic::tests::TestVMLogic; +use unc_parameters::ExtCosts; +use unc_primitives_core::types::Gas; +use std::collections::HashMap; + +type Result = ::std::result::Result; + +pub(super) fn promise_create( + logic: &mut TestVMLogic<'_>, + account_id: &[u8], + amount: u128, + gas: Gas, +) -> Result { + let account_id = logic.internal_mem_write(account_id); + let method = logic.internal_mem_write(b"promise_create"); + let args = logic.internal_mem_write(b"args"); + let amount = logic.internal_mem_write(&amount.to_le_bytes()); + + logic.promise_create( + account_id.len, + account_id.ptr, + method.len, + method.ptr, + args.len, + args.ptr, + amount.ptr, + gas, + ) +} + +pub(super) fn promise_batch_create(logic: &mut TestVMLogic, account_id: &str) -> Result { + let account_id = logic.internal_mem_write(account_id.as_bytes()); + logic.promise_batch_create(account_id.len, account_id.ptr) +} + +pub(super) fn promise_batch_action_function_call( + logic: &mut TestVMLogic<'_>, + promise_index: u64, + amount: u128, + gas: Gas, +) -> Result<()> { + promise_batch_action_function_call_ext( + logic, + promise_index, + b"promise_batch_action", + b"promise_batch_action_args", + amount, + gas, + ) +} + +pub(super) fn promise_batch_action_function_call_ext( + logic: &mut TestVMLogic<'_>, + promise_index: u64, + method_id: &[u8], + args: &[u8], + amount: u128, + gas: Gas, +) -> Result<()> { + let method_id = logic.internal_mem_write(method_id); + let args = logic.internal_mem_write(args); + let amount = logic.internal_mem_write(&amount.to_le_bytes()); + + logic.promise_batch_action_function_call( + promise_index, + method_id.len, + method_id.ptr, + args.len, + args.ptr, + amount.ptr, + gas, + ) +} + +pub(super) fn promise_batch_action_function_call_weight( + logic: &mut TestVMLogic<'_>, + promise_index: u64, + amount: u128, + gas: Gas, + weight: u64, +) -> Result<()> { + promise_batch_action_function_call_weight_ext( + logic, + promise_index, + b"promise_batch_action", + b"promise_batch_action_args", + amount, + gas, + weight, + ) +} + +pub(super) fn promise_batch_action_function_call_weight_ext( + logic: &mut TestVMLogic<'_>, + promise_index: u64, + method_id: &[u8], + args: &[u8], + amount: u128, + gas: Gas, + weight: u64, +) -> Result<()> { + let method_id = logic.internal_mem_write(method_id); + let args = logic.internal_mem_write(args); + let amount = logic.internal_mem_write(&amount.to_le_bytes()); + + logic.promise_batch_action_function_call_weight( + promise_index, + method_id.len, + method_id.ptr, + args.len, + args.ptr, + amount.ptr, + gas, + weight, + ) +} + +pub(super) fn promise_batch_action_add_key_with_function_call( + logic: &mut TestVMLogic<'_>, + promise_index: u64, + public_key: &[u8], + nonce: u64, + allowance: u128, + receiver_id: &[u8], + method_names: &[u8], +) -> Result<()> { + let public_key = logic.internal_mem_write(public_key); + let receiver_id = logic.internal_mem_write(receiver_id); + let allowance = logic.internal_mem_write(&allowance.to_le_bytes()); + let method_names = logic.internal_mem_write(method_names); + + logic.promise_batch_action_add_key_with_function_call( + promise_index, + public_key.len, + public_key.ptr, + nonce, + allowance.ptr, + receiver_id.len, + receiver_id.ptr, + method_names.len, + method_names.ptr, + ) +} + +pub(super) fn promise_batch_action_add_key_with_full_access( + logic: &mut TestVMLogic<'_>, + promise_index: u64, + public_key: &[u8], + nonce: u64, +) -> Result<()> { + let public_key = logic.internal_mem_write(public_key); + + logic.promise_batch_action_add_key_with_full_access( + promise_index, + public_key.len, + public_key.ptr, + nonce, + ) +} + +#[macro_export] +macro_rules! map( + { $($key:path: $value:expr,)+ } => { + { + let mut m = ::std::collections::HashMap::new(); + $( + m.insert($key, $value); + )+ + m + } + }; +); + +pub(super) fn reset_costs_counter() { + crate::logic::with_ext_cost_counter(|cc| cc.clear()) +} + +#[track_caller] +pub(super) fn assert_costs(expected: HashMap) { + crate::logic::with_ext_cost_counter(|cc| assert_eq!(*cc, expected)); + reset_costs_counter(); +} diff --git a/runtime/unc-vm-runner/src/logic/tests/iterators.rs b/runtime/unc-vm-runner/src/logic/tests/iterators.rs new file mode 100644 index 000000000..e6c5ed653 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/iterators.rs @@ -0,0 +1,26 @@ +use crate::logic::tests::vm_logic_builder::VMLogicBuilder; +use crate::logic::{HostError, VMLogicError}; + +#[test] +fn test_iterator_deprecated() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + assert_eq!( + Err(VMLogicError::HostError(HostError::Deprecated { + method_name: "storage_iter_prefix".to_string() + })), + logic.storage_iter_prefix(1, b"a".as_ptr() as _) + ); + assert_eq!( + Err(VMLogicError::HostError(HostError::Deprecated { + method_name: "storage_iter_range".to_string() + })), + logic.storage_iter_range(1, b"a".as_ptr() as _, 1, b"b".as_ptr() as _) + ); + assert_eq!( + Err(VMLogicError::HostError(HostError::Deprecated { + method_name: "storage_iter_next".to_string() + })), + logic.storage_iter_next(0, 0, 1) + ); +} diff --git a/runtime/unc-vm-runner/src/logic/tests/logs.rs b/runtime/unc-vm-runner/src/logic/tests/logs.rs new file mode 100644 index 000000000..fe55290c9 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/logs.rs @@ -0,0 +1,511 @@ +use crate::logic::tests::helpers::*; +use crate::logic::tests::vm_logic_builder::VMLogicBuilder; +use crate::logic::HostError; +use crate::logic::{MemSlice, VMLogic, VMLogicError}; +use crate::map; +use unc_parameters::ExtCosts; + +#[test] +fn test_valid_utf8() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let string = "j ñ r'ø qò$`5 y'5 øò{%÷ `Võ%"; + let bytes = logic.internal_mem_write(string.as_bytes()); + logic.log_utf8(bytes.len, bytes.ptr).expect("Valid UTF-8 in bytes"); + let outcome = logic.compute_outcome(); + assert_eq!(outcome.logs[0], string); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::log_base: 1, + ExtCosts::log_byte: bytes.len, + ExtCosts::read_memory_base: 1, + ExtCosts::read_memory_byte: bytes.len, + ExtCosts::utf8_decoding_base: 1, + ExtCosts::utf8_decoding_byte: bytes.len, + }); +} + +#[test] +fn test_invalid_utf8() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let bytes = logic.internal_mem_write(b"\x80"); + assert_eq!(logic.log_utf8(bytes.len, bytes.ptr), Err(HostError::BadUTF8.into())); + let outcome = logic.compute_outcome(); + assert_eq!(outcome.logs.len(), 0); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: 1, + ExtCosts::read_memory_byte: bytes.len, + ExtCosts::utf8_decoding_base: 1, + ExtCosts::utf8_decoding_byte: bytes.len, + }); +} + +#[test] +fn test_valid_null_terminated_utf8() { + let mut logic_builder = VMLogicBuilder::default(); + + let cstring = "j ñ r'ø qò$`5 y'5 øò{%÷ `Võ%\x00"; + let string = &cstring[..cstring.len() - 1]; + logic_builder.config.limit_config.max_total_log_length = string.len() as u64; + let mut logic = logic_builder.build(); + let bytes = logic.internal_mem_write(cstring.as_bytes()); + logic.log_utf8(u64::MAX, bytes.ptr).expect("Valid null-terminated utf-8 string_bytes"); + let outcome = logic.compute_outcome(); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::log_base: 1, + ExtCosts::log_byte: string.len() as u64, + ExtCosts::read_memory_base: bytes.len, + ExtCosts::read_memory_byte: bytes.len, + ExtCosts::utf8_decoding_base: 1, + ExtCosts::utf8_decoding_byte: string.len() as u64, + }); + assert_eq!(outcome.logs[0], string); +} + +#[test] +fn test_log_max_limit() { + let mut logic_builder = VMLogicBuilder::default(); + let string = "j ñ r'ø qò$`5 y'5 øò{%÷ `Võ%"; + let limit = string.len() as u64 - 1; + logic_builder.config.limit_config.max_total_log_length = limit; + let mut logic = logic_builder.build(); + let bytes = logic.internal_mem_write(string.as_bytes()); + + assert_eq!( + logic.log_utf8(bytes.len, bytes.ptr), + Err(HostError::TotalLogLengthExceeded { length: bytes.len, limit }.into()) + ); + + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::utf8_decoding_base: 1, + }); + + let outcome = logic.compute_outcome(); + assert_eq!(outcome.logs.len(), 0); +} + +#[test] +fn test_log_total_length_limit() { + let mut logic_builder = VMLogicBuilder::default(); + let string = "j ñ r'ø qò$`5 y'5 øò{%÷ `Võ%".as_bytes(); + let num_logs = 10; + let limit = string.len() as u64 * num_logs - 1; + logic_builder.config.limit_config.max_total_log_length = limit; + logic_builder.config.limit_config.max_number_logs = num_logs; + let mut logic = logic_builder.build(); + let bytes = logic.internal_mem_write(string); + + for _ in 0..num_logs - 1 { + logic.log_utf8(bytes.len, bytes.ptr).expect("total is still under the limit"); + } + assert_eq!( + logic.log_utf8(bytes.len, bytes.ptr), + Err(HostError::TotalLogLengthExceeded { length: limit + 1, limit }.into()) + ); + + let outcome = logic.compute_outcome(); + assert_eq!(outcome.logs.len() as u64, num_logs - 1); +} + +#[test] +fn test_log_number_limit() { + let mut logic_builder = VMLogicBuilder::default(); + let string = "blabla"; + let max_number_logs = 3; + logic_builder.config.limit_config.max_total_log_length = + (string.len() + 1) as u64 * (max_number_logs + 1); + logic_builder.config.limit_config.max_number_logs = max_number_logs; + let mut logic = logic_builder.build(); + let bytes = logic.internal_mem_write(string.as_bytes()); + for _ in 0..max_number_logs { + logic + .log_utf8(bytes.len, bytes.ptr) + .expect("Valid utf-8 string_bytes under the log number limit"); + } + assert_eq!( + logic.log_utf8(bytes.len, bytes.ptr), + Err(HostError::NumberOfLogsExceeded { limit: max_number_logs }.into()) + ); + + assert_costs(map! { + ExtCosts::base: max_number_logs + 1, + ExtCosts::log_base: max_number_logs, + ExtCosts::log_byte: bytes.len * max_number_logs, + ExtCosts::read_memory_base: max_number_logs, + ExtCosts::read_memory_byte: bytes.len * max_number_logs, + ExtCosts::utf8_decoding_base: max_number_logs, + ExtCosts::utf8_decoding_byte: bytes.len * max_number_logs, + }); + + let outcome = logic.compute_outcome(); + assert_eq!(outcome.logs.len() as u64, max_number_logs); +} + +fn append_utf16(dst: &mut Vec, string: &str) { + for code_unit in string.encode_utf16() { + dst.extend_from_slice(&code_unit.to_le_bytes()); + } +} + +#[test] +fn test_log_utf16_number_limit() { + let string = "$ qò$`"; + let mut bytes = Vec::new(); + append_utf16(&mut bytes, string); + + let mut logic_builder = VMLogicBuilder::default(); + let max_number_logs = 3; + logic_builder.config.limit_config.max_total_log_length = + (bytes.len() + 1) as u64 * (max_number_logs + 1); + logic_builder.config.limit_config.max_number_logs = max_number_logs; + + let mut logic = logic_builder.build(); + let bytes = logic.internal_mem_write(&bytes); + for _ in 0..max_number_logs { + logic + .log_utf16(bytes.len, bytes.ptr) + .expect("Valid utf-16 string_bytes under the log number limit"); + } + assert_eq!( + logic.log_utf16(bytes.len, bytes.ptr), + Err(HostError::NumberOfLogsExceeded { limit: max_number_logs }.into()) + ); + + assert_costs(map! { + ExtCosts::base: max_number_logs + 1, + ExtCosts::log_base: max_number_logs, + ExtCosts::log_byte: string.len() as u64 * max_number_logs, + ExtCosts::read_memory_base: max_number_logs, + ExtCosts::read_memory_byte: bytes.len * max_number_logs, + ExtCosts::utf16_decoding_base: max_number_logs, + ExtCosts::utf16_decoding_byte: bytes.len * max_number_logs, + }); + + let outcome = logic.compute_outcome(); + assert_eq!(outcome.logs.len() as u64, max_number_logs); +} + +#[test] +fn test_log_total_length_limit_mixed() { + let mut logic_builder = VMLogicBuilder::default(); + + let string = "abc"; + let mut utf16_bytes: Vec = vec![0u8; 0]; + append_utf16(&mut utf16_bytes, string); + + let num_logs_each = 10; + let limit = string.len() as u64 * (num_logs_each * 2 + 1) - 1; + logic_builder.config.limit_config.max_total_log_length = limit; + logic_builder.config.limit_config.max_number_logs = num_logs_each * 2 + 1; + let mut logic = logic_builder.build(); + + let utf8_bytes = logic.internal_mem_write(string.as_bytes()); + let utf16_bytes = logic.internal_mem_write(&utf16_bytes); + + for _ in 0..num_logs_each { + logic.log_utf16(utf16_bytes.len, utf16_bytes.ptr).expect("total is still under the limit"); + + logic.log_utf8(utf8_bytes.len, utf8_bytes.ptr).expect("total is still under the limit"); + } + assert_eq!( + logic.log_utf8(utf8_bytes.len, utf8_bytes.ptr), + Err(HostError::TotalLogLengthExceeded { length: limit + 1, limit }.into()) + ); + + let outcome = logic.compute_outcome(); + assert_eq!(outcome.logs.len() as u64, num_logs_each * 2); +} + +#[test] +fn test_log_utf8_max_limit_null_terminated() { + let mut logic_builder = VMLogicBuilder::default(); + let bytes = "j ñ r'ø qò$`5 y'5 øò{%÷ `Võ%\x00".as_bytes(); + let limit = (bytes.len() - 2) as u64; + logic_builder.config.limit_config.max_total_log_length = limit; + let mut logic = logic_builder.build(); + let bytes = logic.internal_mem_write(bytes); + + assert_eq!( + logic.log_utf8(u64::MAX, bytes.ptr), + Err(HostError::TotalLogLengthExceeded { length: limit + 1, limit }.into()) + ); + + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: bytes.len - 1, + ExtCosts::read_memory_byte: bytes.len - 1, + ExtCosts::utf8_decoding_base: 1, + }); + + let outcome = logic.compute_outcome(); + assert_eq!(outcome.logs.len(), 0); +} + +#[test] +fn test_valid_log_utf16() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + + let string = "$ qò$`"; + let mut bytes = Vec::new(); + append_utf16(&mut bytes, string); + let bytes = logic.internal_mem_write(&bytes); + + logic.log_utf16(bytes.len, bytes.ptr).expect("Valid utf-16 string_bytes"); + + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: 1, + ExtCosts::read_memory_byte: bytes.len, + ExtCosts::utf16_decoding_base: 1, + ExtCosts::utf16_decoding_byte: bytes.len, + ExtCosts::log_base: 1, + ExtCosts::log_byte: string.len() as u64, + }); + let outcome = logic.compute_outcome(); + assert_eq!(outcome.logs[0], string); +} + +#[test] +fn test_valid_log_utf16_max_log_len_not_even() { + let mut logic_builder = VMLogicBuilder::default(); + logic_builder.config.limit_config.max_total_log_length = 5; + let mut logic = logic_builder.build(); + + let string = "ab"; + let mut bytes = Vec::new(); + append_utf16(&mut bytes, string); + append_utf16(&mut bytes, "\0"); + let bytes = logic.internal_mem_write(&bytes); + logic.log_utf16(u64::MAX, bytes.ptr).expect("Valid utf-16 bytes"); + + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: bytes.len / 2, + ExtCosts::read_memory_byte: bytes.len, + ExtCosts::utf16_decoding_base: 1, + ExtCosts::utf16_decoding_byte: bytes.len - 2, + ExtCosts::log_base: 1, + ExtCosts::log_byte: string.len() as u64, + }); + + let string = "abc"; + let mut bytes = Vec::new(); + append_utf16(&mut bytes, string); + append_utf16(&mut bytes, "\0"); + let bytes = logic.internal_mem_write(&bytes); + assert_eq!( + logic.log_utf16(u64::MAX, bytes.ptr), + Err(HostError::TotalLogLengthExceeded { length: 6, limit: 5 }.into()) + ); + + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: 2, + ExtCosts::read_memory_byte: 2 * 2, + ExtCosts::utf16_decoding_base: 1, + }); +} + +#[test] +fn test_log_utf8_max_limit_null_terminated_fail() { + let mut logic_builder = VMLogicBuilder::default(); + logic_builder.config.limit_config.max_total_log_length = 3; + let mut logic = logic_builder.build(); + let bytes = logic.internal_mem_write(b"abcdefgh\0"); + let res = logic.log_utf8(u64::MAX, bytes.ptr); + assert_eq!(res, Err(HostError::TotalLogLengthExceeded { length: 4, limit: 3 }.into())); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: logic_builder.config.limit_config.max_total_log_length + 1, + ExtCosts::read_memory_byte: logic_builder.config.limit_config.max_total_log_length + 1, + ExtCosts::utf8_decoding_base: 1, + }); +} + +#[test] +fn test_valid_log_utf16_null_terminated() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + + let string = "$ qò$`"; + let mut bytes = Vec::new(); + append_utf16(&mut bytes, string); + bytes.extend_from_slice(&[0, 0]); + let bytes = logic.internal_mem_write(&bytes); + + logic.log_utf16(u64::MAX, bytes.ptr).expect("Valid utf-16 string_bytes"); + + let outcome = logic.compute_outcome(); + assert_eq!(outcome.logs[0], string); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: bytes.len / 2 , + ExtCosts::read_memory_byte: bytes.len, + ExtCosts::utf16_decoding_base: 1, + ExtCosts::utf16_decoding_byte: bytes.len - 2, + ExtCosts::log_base: 1, + ExtCosts::log_byte: string.len() as u64, + }); +} + +#[test] +fn test_invalid_log_utf16() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let mut bytes: Vec = Vec::new(); + for u16_ in [0xD834u16, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063] { + bytes.extend_from_slice(&u16_.to_le_bytes()); + } + let bytes = logic.internal_mem_write(&bytes); + let res = logic.log_utf16(bytes.len, bytes.ptr); + assert_eq!(res, Err(HostError::BadUTF16.into())); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: 1, + ExtCosts::read_memory_byte: bytes.len, + ExtCosts::utf16_decoding_base: 1, + ExtCosts::utf16_decoding_byte: bytes.len, + }); +} + +#[test] +fn test_valid_log_utf16_null_terminated_fail() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + + let mut bytes = Vec::new(); + append_utf16(&mut bytes, "$ qò$`"); + bytes.extend_from_slice(&[0x00, 0xD8]); // U+D800, unpaired surrogate + append_utf16(&mut bytes, "foobarbaz\0"); + let bytes = logic.internal_mem_write(&bytes); + + let res = logic.log_utf16(u64::MAX, bytes.ptr); + assert_eq!(res, Err(HostError::BadUTF16.into())); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: bytes.len / 2, + ExtCosts::read_memory_byte: bytes.len, + ExtCosts::utf16_decoding_base: 1, + ExtCosts::utf16_decoding_byte: bytes.len - 2, + }); +} + +mod utf8_mem_violation { + use super::*; + + fn check(read_ok: bool, test: fn(&mut VMLogic<'_>, MemSlice) -> Result<(), VMLogicError>) { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + + let bytes = b"foo bar \xff baz qux"; + let bytes = logic.internal_mem_write_at(64 * 1024 - bytes.len() as u64, bytes); + let err = if read_ok { HostError::BadUTF8 } else { HostError::MemoryAccessViolation }; + assert_eq!(Err(err.into()), test(&mut logic, bytes)); + } + + #[test] + fn test_good_read() { + // The data is read correctly but it has invalid UTF-8 thus it ends up + // with BadUTF8 error and user being charged for decoding. + check(true, |logic, slice| logic.log_utf8(slice.len, slice.ptr)); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: 1, + ExtCosts::read_memory_byte: 17, + ExtCosts::utf8_decoding_base: 1, + ExtCosts::utf8_decoding_byte: 17, + }); + } + + #[test] + fn test_read_past_end() { + // The data goes past the end of the memory resulting in memory access + // violation. User is not charged for UTF-8 decoding (except for the + // base cost which is always charged). + check(false, |logic, slice| logic.log_utf8(slice.len + 1, slice.ptr)); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: 1, + ExtCosts::read_memory_byte: 18, + ExtCosts::utf8_decoding_base: 1, + }); + } + + #[test] + fn test_nul_past_end() { + // The call goes past the end of the memory trying to find NUL byte + // resulting in memory access violation. User is not charged for UTF-8 + // decoding (except for the base cost which is always charged). + check(false, |logic, slice| logic.log_utf8(u64::MAX, slice.ptr)); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: 18, + ExtCosts::read_memory_byte: 18, + ExtCosts::utf8_decoding_base: 1, + }); + } +} + +mod utf16_mem_violation { + use super::*; + + fn check(read_ok: bool, test: fn(&mut VMLogic<'_>, MemSlice) -> Result<(), VMLogicError>) { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + + let mut bytes = Vec::new(); + append_utf16(&mut bytes, "$ qò$`"); + bytes.extend_from_slice(&[0x00, 0xD8]); // U+D800, unpaired surrogate + append_utf16(&mut bytes, "foobarbaz"); + let bytes = logic.internal_mem_write_at(64 * 1024 - bytes.len() as u64, &bytes); + let err = if read_ok { HostError::BadUTF16 } else { HostError::MemoryAccessViolation }; + assert_eq!(Err(err.into()), test(&mut logic, bytes)); + } + + #[test] + fn test_good_read() { + // The data is read correctly but it has invalid UTF-16 thus it ends up + // with BadUTF16 error and user being charged for decoding. + check(true, |logic, slice| logic.log_utf16(slice.len, slice.ptr)); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: 1, + ExtCosts::read_memory_byte: 32, + ExtCosts::utf16_decoding_base: 1, + ExtCosts::utf16_decoding_byte: 32, + }); + } + + #[test] + fn test_read_past_end() { + // The data goes past the end of the memory resulting in memory access + // violation. User is not charged for UTF-16 decoding (except for the + // base cost which is always charged). + check(false, |logic, slice| logic.log_utf16(slice.len + 2, slice.ptr)); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: 1, + ExtCosts::read_memory_byte: 34, + ExtCosts::utf16_decoding_base: 1, + }); + } + + #[test] + fn test_nul_past_end() { + // The call goes past the end of the memory trying to find NUL word + // resulting in memory access violation. User is not charged for UTF-16 + // decoding (except for the base cost which is always charged). + check(false, |logic, slice| logic.log_utf16(u64::MAX, slice.ptr)); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: 17, + ExtCosts::read_memory_byte: 34, + ExtCosts::utf16_decoding_base: 1, + }); + } +} diff --git a/runtime/unc-vm-runner/src/logic/tests/miscs.rs b/runtime/unc-vm-runner/src/logic/tests/miscs.rs new file mode 100644 index 000000000..b7cb39228 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/miscs.rs @@ -0,0 +1,397 @@ +use crate::logic::tests::helpers::*; +use crate::logic::tests::vm_logic_builder::VMLogicBuilder; +use crate::logic::HostError; +use crate::map; +use hex::FromHex; +use unc_parameters::ExtCosts; +use serde::de::Error; +use serde_json::from_slice; +use std::{fmt::Display, fs}; + +#[test] +fn test_sha256() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let data = logic.internal_mem_write(b"tesdsst"); + + logic.sha256(data.len, data.ptr, 0).unwrap(); + logic.assert_read_register( + &[ + 18, 176, 115, 156, 45, 100, 241, 132, 180, 134, 77, 42, 105, 111, 199, 127, 118, 112, + 92, 255, 88, 43, 83, 147, 122, 55, 26, 36, 42, 156, 160, 158, + ], + 0, + ); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: 1, + ExtCosts::read_memory_byte: data.len, + ExtCosts::write_memory_base: 1, + ExtCosts::write_memory_byte: 32, + ExtCosts::read_register_base: 1, + ExtCosts::read_register_byte: 32, + ExtCosts::write_register_base: 1, + ExtCosts::write_register_byte: 32, + ExtCosts::sha256_base: 1, + ExtCosts::sha256_byte: data.len, + }); +} + +#[test] +fn test_keccak256() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + + let data = logic.internal_mem_write(b"tesdsst"); + logic.keccak256(data.len, data.ptr, 0).unwrap(); + logic.assert_read_register( + &[ + 104, 110, 58, 122, 230, 181, 215, 145, 231, 229, 49, 162, 123, 167, 177, 58, 26, 142, + 129, 173, 7, 37, 9, 26, 233, 115, 64, 102, 61, 85, 10, 159, + ], + 0, + ); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: 1, + ExtCosts::read_memory_byte: data.len, + ExtCosts::write_memory_base: 1, + ExtCosts::write_memory_byte: 32, + ExtCosts::read_register_base: 1, + ExtCosts::read_register_byte: 32, + ExtCosts::write_register_base: 1, + ExtCosts::write_register_byte: 32, + ExtCosts::keccak256_base: 1, + ExtCosts::keccak256_byte: data.len, + }); +} + +#[test] +fn test_keccak512() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + + let data = logic.internal_mem_write(b"tesdsst"); + logic.keccak512(data.len, data.ptr, 0).unwrap(); + logic.assert_read_register( + &[ + 55, 134, 96, 137, 168, 122, 187, 95, 67, 76, 18, 122, 146, 11, 225, 106, 117, 194, 154, + 157, 48, 160, 90, 146, 104, 209, 118, 126, 222, 230, 200, 125, 48, 73, 197, 236, 123, + 173, 192, 197, 90, 153, 167, 121, 100, 88, 209, 240, 137, 86, 239, 41, 87, 128, 219, + 249, 136, 203, 220, 109, 46, 168, 234, 190, + ], + 0, + ); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: 1, + ExtCosts::read_memory_byte: data.len, + ExtCosts::write_memory_base: 1, + ExtCosts::write_memory_byte: 64, + ExtCosts::read_register_base: 1, + ExtCosts::read_register_byte: 64, + ExtCosts::write_register_base: 1, + ExtCosts::write_register_byte: 64, + ExtCosts::keccak512_base: 1, + ExtCosts::keccak512_byte: data.len, + }); +} + +#[test] +fn test_ripemd160() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + + let data = logic.internal_mem_write(b"tesdsst"); + logic.ripemd160(data.len, data.ptr, 0).unwrap(); + logic.assert_read_register( + &[21, 102, 156, 115, 232, 3, 58, 215, 35, 84, 129, 30, 143, 86, 212, 104, 70, 97, 14, 225], + 0, + ); + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::read_memory_base: 1, + ExtCosts::read_memory_byte: data.len, + ExtCosts::write_memory_base: 1, + ExtCosts::write_memory_byte: 20, + ExtCosts::read_register_base: 1, + ExtCosts::read_register_byte: 20, + ExtCosts::write_register_base: 1, + ExtCosts::write_register_byte: 20, + ExtCosts::ripemd160_base: 1, + ExtCosts::ripemd160_block: 1, + }); +} + +#[derive(serde::Deserialize)] +struct EcrecoverTest { + #[serde(with = "hex::serde")] + m: [u8; 32], + v: u8, + #[serde(with = "hex::serde")] + sig: [u8; 64], + mc: bool, + #[serde(deserialize_with = "deserialize_option_hex")] + res: Option<[u8; 64]>, +} + +fn deserialize_option_hex<'de, D, T>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, + T: FromHex, + ::Error: Display, +{ + serde::Deserialize::deserialize(deserializer) + .map(|v: Option<&str>| v.map(FromHex::from_hex).transpose().map_err(Error::custom)) + .and_then(|v| v) +} + +#[test] +fn test_ecrecover() { + for EcrecoverTest { m, v, sig, mc, res } in from_slice::<'_, Vec<_>>( + fs::read("src/logic/tests/ecrecover-tests.json").unwrap().as_slice(), + ) + .unwrap() + { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let m = logic.internal_mem_write(&m); + let sig = logic.internal_mem_write(&sig); + + let b = logic.ecrecover(m.len, m.ptr, sig.len, sig.ptr, v as _, mc as _, 1).unwrap(); + assert_eq!(b, res.is_some() as u64); + + if let Some(res) = res { + assert_costs(map! { + ExtCosts::read_memory_base: 2, + ExtCosts::read_memory_byte: 96, + ExtCosts::write_register_base: 1, + ExtCosts::write_register_byte: 64, + ExtCosts::ecrecover_base: 1, + }); + logic.assert_read_register(&res, 1); + } else { + assert_costs(map! { + ExtCosts::read_memory_base: 2, + ExtCosts::read_memory_byte: 96, + ExtCosts::ecrecover_base: 1, + }); + } + + reset_costs_counter(); + } +} + +#[test] +fn test_hash256_register() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let data = b"tesdsst"; + logic.wrapped_internal_write_register(1, data).unwrap(); + + logic.sha256(u64::MAX, 1, 0).unwrap(); + logic.assert_read_register( + &[ + 18, 176, 115, 156, 45, 100, 241, 132, 180, 134, 77, 42, 105, 111, 199, 127, 118, 112, + 92, 255, 88, 43, 83, 147, 122, 55, 26, 36, 42, 156, 160, 158, + ], + 0, + ); + + let len = data.len() as u64; + assert_costs(map! { + ExtCosts::base: 1, + ExtCosts::write_memory_base: 1, + ExtCosts::write_memory_byte: 32, + ExtCosts::read_register_base: 2, + ExtCosts::read_register_byte: 32 + len, + ExtCosts::write_register_base: 2, + ExtCosts::write_register_byte: 32 + len, + ExtCosts::sha256_base: 1, + ExtCosts::sha256_byte: len, + }); +} + +#[test] +fn test_key_length_limit() { + let mut logic_builder = VMLogicBuilder::default(); + let limit = 1024; + logic_builder.config.limit_config.max_length_storage_key = limit; + let mut logic = logic_builder.build(); + + // Under the limit. Valid calls. + let key = crate::logic::MemSlice { ptr: 0, len: limit }; + let val = crate::logic::MemSlice { ptr: 0, len: 5 }; + logic + .storage_has_key(key.len, key.ptr) + .expect("storage_has_key: key length is under the limit"); + logic + .storage_write(key.len, key.ptr, val.len, val.ptr, 0) + .expect("storage_write: key length is under the limit"); + logic.storage_read(key.len, key.ptr, 0).expect("storage_read: key length is under the limit"); + logic + .storage_remove(key.len, key.ptr, 0) + .expect("storage_remove: key length is under the limit"); + + // Over the limit. Invalid calls. + let key = crate::logic::MemSlice { ptr: 0, len: limit + 1 }; + assert_eq!( + logic.storage_has_key(key.len, key.ptr), + Err(HostError::KeyLengthExceeded { length: key.len, limit }.into()) + ); + assert_eq!( + logic.storage_write(key.len, key.ptr, val.len, val.ptr, 0), + Err(HostError::KeyLengthExceeded { length: key.len, limit }.into()) + ); + assert_eq!( + logic.storage_read(key.len, key.ptr, 0), + Err(HostError::KeyLengthExceeded { length: key.len, limit }.into()) + ); + assert_eq!( + logic.storage_remove(key.len, key.ptr, 0), + Err(HostError::KeyLengthExceeded { length: key.len, limit }.into()) + ); +} + +#[test] +fn test_value_length_limit() { + let mut logic_builder = VMLogicBuilder::default(); + let limit = 1024; + logic_builder.config.limit_config.max_length_storage_value = limit; + let mut logic = logic_builder.build(); + let key = logic.internal_mem_write(b"hello"); + + logic + .storage_write(key.len, key.ptr, limit / 2, 0, 0) + .expect("Value length doesn’t exceed the limit"); + logic + .storage_write(key.len, key.ptr, limit, 0, 0) + .expect("Value length doesn’t exceed the limit"); + assert_eq!( + logic.storage_write(key.len, key.ptr, limit + 1, 0, 0), + Err(HostError::ValueLengthExceeded { length: limit + 1, limit }.into()) + ); +} + +#[test] +fn test_num_promises() { + let mut logic_builder = VMLogicBuilder::default(); + let num_promises = 10; + logic_builder.config.limit_config.max_promises_per_function_call_action = num_promises; + let mut logic = logic_builder.build(); + let account_id = logic.internal_mem_write(b"alice"); + for _ in 0..num_promises { + logic + .promise_batch_create(account_id.len, account_id.ptr) + .expect("Number of promises is under the limit"); + } + assert_eq!( + logic.promise_batch_create(account_id.len, account_id.ptr), + Err(HostError::NumberPromisesExceeded { + number_of_promises: num_promises + 1, + limit: num_promises + } + .into()) + ); +} + +#[test] +fn test_num_joined_promises() { + let mut logic_builder = VMLogicBuilder::default(); + let num_deps = 10; + logic_builder.config.limit_config.max_number_input_data_dependencies = num_deps; + let mut logic = logic_builder.build(); + let account_id = logic.internal_mem_write(b"alice"); + let promise_id = logic + .promise_batch_create(account_id.len, account_id.ptr) + .expect("Number of promises is under the limit"); + let promises = + logic.internal_mem_write(&promise_id.to_le_bytes().repeat(num_deps as usize + 1)); + for num in 0..num_deps { + logic.promise_and(promises.ptr, num).expect("Number of joined promises is under the limit"); + } + assert_eq!( + logic.promise_and(promises.ptr, num_deps + 1), + Err(HostError::NumberInputDataDependenciesExceeded { + number_of_input_data_dependencies: num_deps + 1, + limit: num_deps, + } + .into()) + ); +} + +#[test] +fn test_num_input_dependencies_recursive_join() { + let mut logic_builder = VMLogicBuilder::default(); + let num_steps = 10; + logic_builder.config.limit_config.max_number_input_data_dependencies = 1 << num_steps; + let mut logic = logic_builder.build(); + let account_id = logic.internal_mem_write(b"alice"); + let original_promise_id = logic + .promise_batch_create(account_id.len, account_id.ptr) + .expect("Number of promises is under the limit"); + let mut promise_id = original_promise_id; + for _ in 1..num_steps { + let promises_ptr = logic.internal_mem_write(&promise_id.to_le_bytes()).ptr; + logic.internal_mem_write(&promise_id.to_le_bytes()); + promise_id = logic + .promise_and(promises_ptr, 2) + .expect("Number of joined promises is under the limit"); + } + // The length of joined promises is exactly the limit (1024). + let promises_ptr = logic.internal_mem_write(&promise_id.to_le_bytes()).ptr; + logic.internal_mem_write(&promise_id.to_le_bytes()); + logic.promise_and(promises_ptr, 2).expect("Number of joined promises is under the limit"); + + // The length of joined promises exceeding the limit by 1 (total 1025). + let promises_ptr = logic.internal_mem_write(&promise_id.to_le_bytes()).ptr; + logic.internal_mem_write(&promise_id.to_le_bytes()); + logic.internal_mem_write(&original_promise_id.to_le_bytes()); + assert_eq!( + logic.promise_and(promises_ptr, 3), + Err(HostError::NumberInputDataDependenciesExceeded { + number_of_input_data_dependencies: logic_builder + .config + .limit_config + .max_number_input_data_dependencies + + 1, + limit: logic_builder.config.limit_config.max_number_input_data_dependencies, + } + .into()) + ); +} + +#[test] +fn test_return_value_limit() { + let mut logic_builder = VMLogicBuilder::default(); + let limit = 1024; + logic_builder.config.limit_config.max_length_returned_data = limit; + let mut logic = logic_builder.build(); + + logic.value_return(limit, 0).expect("Returned value length is under the limit"); + assert_eq!( + logic.value_return(limit + 1, 0), + Err(HostError::ReturnedValueLengthExceeded { length: limit + 1, limit }.into()) + ); +} + +#[test] +fn test_contract_size_limit() { + let mut logic_builder = VMLogicBuilder::default(); + let limit = 1024; + logic_builder.config.limit_config.max_contract_size = limit; + let mut logic = logic_builder.build(); + + let account_id = logic.internal_mem_write(b"alice"); + + let promise_id = logic + .promise_batch_create(account_id.len, account_id.ptr) + .expect("Number of promises is under the limit"); + logic + .promise_batch_action_deploy_contract(promise_id, limit, 0) + .expect("The length of the contract code is under the limit"); + assert_eq!( + logic.promise_batch_action_deploy_contract(promise_id, limit + 1, 0), + Err(HostError::ContractSizeExceeded { size: limit + 1, limit }.into()) + ); +} diff --git a/runtime/unc-vm-runner/src/logic/tests/mod.rs b/runtime/unc-vm-runner/src/logic/tests/mod.rs new file mode 100644 index 000000000..4c1f3fd4c --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/mod.rs @@ -0,0 +1,16 @@ +mod alt_bn128; +mod context; +mod ed25519_verify; +mod gas_counter; +pub(crate) mod helpers; +mod iterators; +mod logs; +mod miscs; +mod promises; +mod registers; +mod storage_read_write; +mod storage_usage; +mod view_method; +mod vm_logic_builder; + +use vm_logic_builder::TestVMLogic; diff --git a/runtime/unc-vm-runner/src/logic/tests/promises.rs b/runtime/unc-vm-runner/src/logic/tests/promises.rs new file mode 100644 index 000000000..4343d1b2f --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/promises.rs @@ -0,0 +1,632 @@ +use crate::logic::mocks::mock_external::MockedExternal; +use crate::logic::tests::helpers::*; +use crate::logic::tests::vm_logic_builder::VMLogicBuilder; +use crate::logic::types::PromiseResult; + +use unc_crypto::PublicKey; +use serde_json; + +fn vm_receipts<'a>(ext: &'a MockedExternal) -> Vec { + ext.action_log.clone() +} + +#[test] +fn test_promise_results() { + let promise_results = vec![ + PromiseResult::Successful(b"test".to_vec()), + PromiseResult::Failed, + PromiseResult::NotReady, + ]; + + let mut logic_builder = VMLogicBuilder::default(); + logic_builder.promise_results = promise_results; + let mut logic = logic_builder.build(); + + assert_eq!(logic.promise_results_count(), Ok(3), "Total count of registers must be 3"); + assert_eq!(logic.promise_result(0, 0), Ok(1), "Must return code 1 on success"); + assert_eq!(logic.promise_result(1, 0), Ok(2), "Failed promise must return code 2"); + assert_eq!(logic.promise_result(2, 0), Ok(0), "Pending promise must return 3"); + + // Only promise with result should write data into register + logic.assert_read_register(b"test", 0); +} + +#[test] +fn test_promise_batch_action_function_call() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let index = promise_create(&mut logic, b"rick.test", 0, 0).expect("should create a promise"); + let index_ptr = logic.internal_mem_write(&index.to_le_bytes()).ptr; + + promise_batch_action_function_call(&mut logic, 123, 0, 0) + .expect_err("shouldn't accept not existent promise index"); + let non_receipt = + logic.promise_and(index_ptr, 1u64).expect("should create a non-receipt promise"); + promise_batch_action_function_call(&mut logic, non_receipt, 0, 0) + .expect_err("shouldn't accept non-receipt promise index"); + + promise_batch_action_function_call(&mut logic, index, 0, 0) + .expect("should add an action to receipt"); + expect_test::expect![[r#" + [ + { + "CreateReceipt": { + "receipt_indices": [], + "receiver_id": "rick.test" + } + }, + { + "FunctionCallWeight": { + "receipt_index": 0, + "method_name": [ + 112, + 114, + 111, + 109, + 105, + 115, + 101, + 95, + 99, + 114, + 101, + 97, + 116, + 101 + ], + "args": [ + 97, + 114, + 103, + 115 + ], + "attached_deposit": 0, + "prepaid_gas": 0, + "gas_weight": 0 + } + }, + { + "FunctionCallWeight": { + "receipt_index": 0, + "method_name": [ + 112, + 114, + 111, + 109, + 105, + 115, + 101, + 95, + 98, + 97, + 116, + 99, + 104, + 95, + 97, + 99, + 116, + 105, + 111, + 110 + ], + "args": [ + 112, + 114, + 111, + 109, + 105, + 115, + 101, + 95, + 98, + 97, + 116, + 99, + 104, + 95, + 97, + 99, + 116, + 105, + 111, + 110, + 95, + 97, + 114, + 103, + 115 + ], + "attached_deposit": 0, + "prepaid_gas": 0, + "gas_weight": 0 + } + } + ]"#]] + .assert_eq(&serde_json::to_string_pretty(&vm_receipts(&logic_builder.ext)).unwrap()); +} + +#[test] +fn test_promise_batch_action_create_account() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let index = promise_create(&mut logic, b"rick.test", 0, 0).expect("should create a promise"); + let index_ptr = logic.internal_mem_write(&index.to_le_bytes()).ptr; + + logic + .promise_batch_action_create_account(123) + .expect_err("shouldn't accept not existent promise index"); + let non_receipt = + logic.promise_and(index_ptr, 1u64).expect("should create a non-receipt promise"); + logic + .promise_batch_action_create_account(non_receipt) + .expect_err("shouldn't accept non-receipt promise index"); + logic + .promise_batch_action_create_account(index) + .expect("should add an action to create account"); + assert_eq!(logic.used_gas().unwrap(), 12578263688564); + expect_test::expect![[r#" + [ + { + "CreateReceipt": { + "receipt_indices": [], + "receiver_id": "rick.test" + } + }, + { + "FunctionCallWeight": { + "receipt_index": 0, + "method_name": [ + 112, + 114, + 111, + 109, + 105, + 115, + 101, + 95, + 99, + 114, + 101, + 97, + 116, + 101 + ], + "args": [ + 97, + 114, + 103, + 115 + ], + "attached_deposit": 0, + "prepaid_gas": 0, + "gas_weight": 0 + } + }, + { + "CreateAccount": { + "receipt_index": 0 + } + } + ]"#]] + .assert_eq(&serde_json::to_string_pretty(&vm_receipts(&logic_builder.ext)).unwrap()); +} + +#[test] +fn test_promise_batch_action_deploy_contract() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let index = promise_create(&mut logic, b"rick.test", 0, 0).expect("should create a promise"); + + let index_ptr = logic.internal_mem_write(&index.to_le_bytes()).ptr; + let code = logic.internal_mem_write(b"sample"); + + logic + .promise_batch_action_deploy_contract(123, code.len, code.ptr) + .expect_err("shouldn't accept not existent promise index"); + let non_receipt = + logic.promise_and(index_ptr, 1u64).expect("should create a non-receipt promise"); + logic + .promise_batch_action_deploy_contract(non_receipt, code.len, code.ptr) + .expect_err("shouldn't accept non-receipt promise index"); + + logic + .promise_batch_action_deploy_contract(index, code.len, code.ptr) + .expect("should add an action to deploy contract"); + assert_eq!(logic.used_gas().unwrap(), 5255774958146); + expect_test::expect![[r#" + [ + { + "CreateReceipt": { + "receipt_indices": [], + "receiver_id": "rick.test" + } + }, + { + "FunctionCallWeight": { + "receipt_index": 0, + "method_name": [ + 112, + 114, + 111, + 109, + 105, + 115, + 101, + 95, + 99, + 114, + 101, + 97, + 116, + 101 + ], + "args": [ + 97, + 114, + 103, + 115 + ], + "attached_deposit": 0, + "prepaid_gas": 0, + "gas_weight": 0 + } + }, + { + "DeployContract": { + "receipt_index": 0, + "code": [ + 115, + 97, + 109, + 112, + 108, + 101 + ] + } + } + ]"#]] + .assert_eq(&serde_json::to_string_pretty(&vm_receipts(&logic_builder.ext)).unwrap()); +} + +#[test] +fn test_promise_batch_action_transfer() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let index = promise_create(&mut logic, b"rick.test", 0, 0).expect("should create a promise"); + + let index_ptr = logic.internal_mem_write(&index.to_le_bytes()).ptr; + let num_110u128 = logic.internal_mem_write(&110u128.to_le_bytes()); + let num_1u128 = logic.internal_mem_write(&1u128.to_le_bytes()); + + logic + .promise_batch_action_transfer(123, num_110u128.ptr) + .expect_err("shouldn't accept not existent promise index"); + let non_receipt = + logic.promise_and(index_ptr, 1u64).expect("should create a non-receipt promise"); + logic + .promise_batch_action_transfer(non_receipt, num_110u128.ptr) + .expect_err("shouldn't accept non-receipt promise index"); + + logic + .promise_batch_action_transfer(index, num_110u128.ptr) + .expect("should add an action to transfer money"); + logic.promise_batch_action_transfer(index, num_1u128.ptr).expect_err("not enough money"); + assert_eq!(logic.used_gas().unwrap(), 5349703444787); + expect_test::expect![[r#" + [ + { + "CreateReceipt": { + "receipt_indices": [], + "receiver_id": "rick.test" + } + }, + { + "FunctionCallWeight": { + "receipt_index": 0, + "method_name": [ + 112, + 114, + 111, + 109, + 105, + 115, + 101, + 95, + 99, + 114, + 101, + 97, + 116, + 101 + ], + "args": [ + 97, + 114, + 103, + 115 + ], + "attached_deposit": 0, + "prepaid_gas": 0, + "gas_weight": 0 + } + }, + { + "Transfer": { + "receipt_index": 0, + "deposit": 110 + } + } + ]"#]] + .assert_eq(&serde_json::to_string_pretty(&vm_receipts(&logic_builder.ext)).unwrap()); +} + +#[test] +fn test_promise_batch_action_stake() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let index = promise_create(&mut logic, b"rick.test", 0, 0).expect("should create a promise"); + let key = borsh::to_vec( + &"ed25519:5do5nkAEVhL8iteDvXNgxi4pWK78Y7DDadX11ArFNyrf".parse::().unwrap(), + ) + .unwrap(); + + let key = logic.internal_mem_write(&key); + let index_ptr = logic.internal_mem_write(&index.to_le_bytes()).ptr; + let num_110u128 = logic.internal_mem_write(&110u128.to_le_bytes()); + + logic + .promise_batch_action_stake(123, num_110u128.ptr, key.len, key.ptr) + .expect_err("shouldn't accept not existent promise index"); + let non_receipt = + logic.promise_and(index_ptr, 1u64).expect("should create a non-receipt promise"); + logic + .promise_batch_action_stake(non_receipt, num_110u128.ptr, key.len, key.ptr) + .expect_err("shouldn't accept non-receipt promise index"); + + logic + .promise_batch_action_stake(index, num_110u128.ptr, key.len, key.ptr) + .expect("should add an action to stake"); + assert_eq!(logic.used_gas().unwrap(), 5138414976215); + expect_test::expect![[r#" + [ + { + "CreateReceipt": { + "receipt_indices": [], + "receiver_id": "rick.test" + } + }, + { + "FunctionCallWeight": { + "receipt_index": 0, + "method_name": [ + 112, + 114, + 111, + 109, + 105, + 115, + 101, + 95, + 99, + 114, + 101, + 97, + 116, + 101 + ], + "args": [ + 97, + 114, + 103, + 115 + ], + "attached_deposit": 0, + "prepaid_gas": 0, + "gas_weight": 0 + } + }, + { + "Stake": { + "receipt_index": 0, + "stake": 110, + "public_key": "ed25519:5do5nkAEVhL8iteDvXNgxi4pWK78Y7DDadX11ArFNyrf" + } + } + ]"#]] + .assert_eq(&serde_json::to_string_pretty(&vm_receipts(&logic_builder.ext)).unwrap()); +} + +#[test] +fn test_promise_batch_action_add_key_with_function_call() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let index = promise_create(&mut logic, b"rick.test", 0, 0).expect("should create a promise"); + let index_ptr = logic.internal_mem_write(&index.to_le_bytes()).ptr; + let key = borsh::to_vec( + &"ed25519:5do5nkAEVhL8iteDvXNgxi4pWK78Y7DDadX11ArFNyrf".parse::().unwrap(), + ) + .unwrap(); + let nonce = 1; + let allowance = 999u128; + let receiver_id = b"sam"; + let method_names = b"foo,bar"; + + promise_batch_action_add_key_with_function_call( + &mut logic, + 123, + &key, + nonce, + allowance, + receiver_id, + method_names, + ) + .expect_err("shouldn't accept non-existent promise index"); + let non_receipt = + logic.promise_and(index_ptr, 1u64).expect("should create a non-receipt promise"); + promise_batch_action_add_key_with_function_call( + &mut logic, + non_receipt, + &key, + nonce, + allowance, + receiver_id, + method_names, + ) + .expect_err("shouldn't accept non-receipt promise index"); + + promise_batch_action_add_key_with_function_call( + &mut logic, + index, + &key, + nonce, + allowance, + receiver_id, + method_names, + ) + .expect("should add allowance"); + assert_eq!(logic.used_gas().unwrap(), 5126680499695); + expect_test::expect![[r#" + [ + { + "CreateReceipt": { + "receipt_indices": [], + "receiver_id": "rick.test" + } + }, + { + "FunctionCallWeight": { + "receipt_index": 0, + "method_name": [ + 112, + 114, + 111, + 109, + 105, + 115, + 101, + 95, + 99, + 114, + 101, + 97, + 116, + 101 + ], + "args": [ + 97, + 114, + 103, + 115 + ], + "attached_deposit": 0, + "prepaid_gas": 0, + "gas_weight": 0 + } + }, + { + "AddKeyWithFunctionCall": { + "receipt_index": 0, + "public_key": "ed25519:5do5nkAEVhL8iteDvXNgxi4pWK78Y7DDadX11ArFNyrf", + "nonce": 1, + "allowance": 999, + "receiver_id": "sam", + "method_names": [ + [ + 102, + 111, + 111 + ], + [ + 98, + 97, + 114 + ] + ] + } + } + ]"#]] + .assert_eq(&serde_json::to_string_pretty(&vm_receipts(&logic_builder.ext)).unwrap()); +} + +#[test] +fn test_promise_batch_then() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + + let account_id = b"rick.test"; + let index = promise_create(&mut logic, account_id, 0, 0).expect("should create a promise"); + + let account_id = logic.internal_mem_write(&account_id[..]); + let index_slice = logic.internal_mem_write(&index.to_le_bytes()); + + logic + .promise_batch_then(123, account_id.len, account_id.ptr) + .expect_err("shouldn't accept non-existent promise index"); + let non_receipt = + logic.promise_and(index_slice.ptr, 1u64).expect("should create a non-receipt promise"); + logic + .promise_batch_then(non_receipt, account_id.len, account_id.ptr) + .expect("should accept non-receipt promise index"); + + logic + .promise_batch_then(index, account_id.len, account_id.ptr) + .expect("promise batch should run ok"); + assert_eq!(logic.used_gas().unwrap(), 24124999601771); + expect_test::expect![[r#" + [ + { + "CreateReceipt": { + "receipt_indices": [], + "receiver_id": "rick.test" + } + }, + { + "FunctionCallWeight": { + "receipt_index": 0, + "method_name": [ + 112, + 114, + 111, + 109, + 105, + 115, + 101, + 95, + 99, + 114, + 101, + 97, + 116, + 101 + ], + "args": [ + 97, + 114, + 103, + 115 + ], + "attached_deposit": 0, + "prepaid_gas": 0, + "gas_weight": 0 + } + }, + { + "CreateReceipt": { + "receipt_indices": [ + 0 + ], + "receiver_id": "rick.test" + } + }, + { + "CreateReceipt": { + "receipt_indices": [ + 0 + ], + "receiver_id": "rick.test" + } + } + ]"#]] + .assert_eq(&serde_json::to_string_pretty(&vm_receipts(&logic_builder.ext)).unwrap()); +} diff --git a/runtime/unc-vm-runner/src/logic/tests/registers.rs b/runtime/unc-vm-runner/src/logic/tests/registers.rs new file mode 100644 index 000000000..f65cda98c --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/registers.rs @@ -0,0 +1,88 @@ +use crate::logic::tests::vm_logic_builder::VMLogicBuilder; +use crate::logic::{HostError, VMLogicError}; +use crate::tests::test_vm_config; + +#[test] +fn test_one_register() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + + logic.wrapped_internal_write_register(0, &[0, 1, 2]).unwrap(); + assert_eq!(logic.register_len(0).unwrap(), 3u64); + logic.assert_read_register(&[0, 1, 2], 0); +} + +#[test] +fn test_non_existent_register() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + + assert_eq!(logic.register_len(0), Ok(u64::MAX) as Result); + let buffer = [0u8; 3]; + assert_eq!( + logic.read_register(0, buffer.as_ptr() as u64), + Err(HostError::InvalidRegisterId { register_id: 0 }.into()) + ); +} + +#[test] +fn test_many_registers() { + let mut logic_builder = VMLogicBuilder::default(); + let max_registers = logic_builder.config.limit_config.max_number_registers; + let mut logic = logic_builder.build(); + + for i in 0..max_registers { + let value = (i * 10u64).to_le_bytes(); + logic.wrapped_internal_write_register(i, &value[..]).unwrap(); + logic.assert_read_register(&value[..], i); + } + + // One more register hits the boundary check. + assert_eq!( + logic.wrapped_internal_write_register(max_registers, &[]), + Err(HostError::MemoryAccessViolation.into()) + ) +} + +#[test] +fn test_max_register_size() { + let mut logic_builder = VMLogicBuilder::free(); + let max_register_size = logic_builder.config.limit_config.max_register_size; + let mut logic = logic_builder.build(); + + let value = vec![0u8; (max_register_size + 1) as usize]; + + assert_eq!( + logic.wrapped_internal_write_register(0, &value), + Err(HostError::MemoryAccessViolation.into()) + ); +} + +#[test] +fn test_max_register_memory_limit() { + let mut logic_builder = VMLogicBuilder::free(); + let mut config = test_vm_config(); + config.make_free(); + logic_builder.config = config.clone(); + let mut logic = logic_builder.build(); + + let max_registers = + config.limit_config.registers_memory_limit / config.limit_config.max_register_size; + + for i in 0..max_registers { + let value = vec![1u8; config.limit_config.max_register_size as usize]; + logic.wrapped_internal_write_register(i, &value).expect("should be written successfully"); + } + let last = vec![1u8; config.limit_config.max_register_size as usize]; + assert_eq!( + logic.wrapped_internal_write_register(max_registers, &last), + Err(HostError::MemoryAccessViolation.into()) + ); +} + +#[test] +fn test_register_is_not_used() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + assert_eq!(logic.register_len(0), Ok(u64::MAX)); +} diff --git a/runtime/unc-vm-runner/src/logic/tests/storage_read_write.rs b/runtime/unc-vm-runner/src/logic/tests/storage_read_write.rs new file mode 100644 index 000000000..09ab4a67b --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/storage_read_write.rs @@ -0,0 +1,65 @@ +use crate::logic::tests::vm_logic_builder::VMLogicBuilder; +use crate::logic::{External, StorageGetMode}; + +#[test] +fn test_storage_write_with_register() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + + let key: &[u8] = b"foo"; + let val: &[u8] = b"bar"; + + logic.wrapped_internal_write_register(1, key).unwrap(); + logic.wrapped_internal_write_register(2, val).unwrap(); + + logic.storage_write(u64::MAX, 1 as _, u64::MAX, 2 as _, 0).expect("storage write ok"); + + let value_ptr = logic_builder.ext.storage_get(key, StorageGetMode::Trie).unwrap().unwrap(); + assert_eq!(value_ptr.deref().unwrap(), val.to_vec()); +} + +#[test] +fn test_storage_read_with_register() { + let mut logic_builder = VMLogicBuilder::default(); + + let key: &[u8] = b"foo"; + let val: &[u8] = b"bar"; + + logic_builder.ext.storage_set(key, val).unwrap(); + let mut logic = logic_builder.build(); + + logic.wrapped_internal_write_register(1, key).unwrap(); + + logic.storage_read(u64::MAX, 1 as _, 0).expect("storage read ok"); + logic.assert_read_register(val, 0); +} + +#[test] +fn test_storage_remove_with_register() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let key = logic.internal_mem_write(b"foo"); + let val = logic.internal_mem_write(b"bar"); + + logic.storage_write(key.len, key.ptr, val.len, val.ptr, 0).expect("storage write ok"); + + logic.wrapped_internal_write_register(1, b"foo").unwrap(); + + logic.storage_remove(u64::MAX, 1 as _, 0).expect("storage remove ok"); + logic.assert_read_register(b"bar", 0); +} + +#[test] +fn test_storage_has_key_with_register() { + let mut logic_builder = VMLogicBuilder::default(); + + let key: &[u8] = b"foo"; + let val: &[u8] = b"bar"; + logic_builder.ext.storage_set(key, val).unwrap(); + + let mut logic = logic_builder.build(); + + logic.wrapped_internal_write_register(1, key).unwrap(); + + assert_eq!(logic.storage_has_key(u64::MAX, 1 as _), Ok(1)); +} diff --git a/runtime/unc-vm-runner/src/logic/tests/storage_usage.rs b/runtime/unc-vm-runner/src/logic/tests/storage_usage.rs new file mode 100644 index 000000000..582960dec --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/storage_usage.rs @@ -0,0 +1,34 @@ +use crate::logic::tests::vm_logic_builder::VMLogicBuilder; + +#[test] +fn test_storage_write_counter() { + let mut logic_builder = VMLogicBuilder::default(); + let data_record_cost = logic_builder.fees_config.storage_usage_config.num_extra_bytes_record; + let mut logic = logic_builder.build(); + let key = logic.internal_mem_write(b"foo"); + let val = logic.internal_mem_write(b"bar"); + + logic.storage_write(key.len, key.ptr, val.len, val.ptr, 0).expect("storage write ok"); + + let cost_expected = data_record_cost + key.len + val.len; + + assert_eq!(logic.storage_usage().unwrap(), cost_expected); + + logic.storage_write(key.len, key.ptr, val.len, val.ptr, 0).expect("storage write ok"); + + assert_eq!(logic.storage_usage().unwrap(), cost_expected); +} + +#[test] +fn test_storage_remove() { + let mut logic_builder = VMLogicBuilder::default(); + let mut logic = logic_builder.build(); + let key = logic.internal_mem_write(b"foo"); + let val = logic.internal_mem_write(b"bar"); + + logic.storage_write(key.len, key.ptr, val.len, val.ptr, 0).expect("storage write ok"); + + logic.storage_remove(key.len, key.ptr, 0).expect("storage remove ok"); + + assert_eq!(logic.storage_usage().unwrap(), 0u64); +} diff --git a/runtime/unc-vm-runner/src/logic/tests/view_method.rs b/runtime/unc-vm-runner/src/logic/tests/view_method.rs new file mode 100644 index 000000000..2a5011228 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/view_method.rs @@ -0,0 +1,46 @@ +use crate::logic::tests::vm_logic_builder::VMLogicBuilder; + +macro_rules! test_prohibited { + ($f: ident $(, $arg: expr )* ) => { + let mut logic_builder = VMLogicBuilder::view(); + let mut logic = logic_builder.build(); + + let name = stringify!($f); + logic.$f($($arg, )*).expect_err(&format!("{} is not allowed in view calls", name)) + }; +} + +#[test] +fn test_prohibited_view_methods() { + test_prohibited!(signer_account_id, 0); + test_prohibited!(signer_account_pk, 0); + test_prohibited!(predecessor_account_id, 0); + test_prohibited!(prepaid_gas); + test_prohibited!(used_gas); + test_prohibited!(promise_create, 0, 0, 0, 0, 0, 0, 0, 0); + test_prohibited!(promise_then, 0, 0, 0, 0, 0, 0, 0, 0, 0); + test_prohibited!(promise_and, 0, 0); + test_prohibited!(promise_batch_create, 0, 0); + test_prohibited!(promise_batch_then, 0, 0, 0); + test_prohibited!(promise_batch_action_create_account, 0); + test_prohibited!(promise_batch_action_deploy_contract, 0, 0, 0); + test_prohibited!(promise_batch_action_function_call, 0, 0, 0, 0, 0, 0, 0); + test_prohibited!(promise_batch_action_transfer, 0, 0); + test_prohibited!(promise_batch_action_stake, 0, 0, 0, 0); + test_prohibited!(promise_batch_action_add_key_with_full_access, 0, 0, 0, 0); + test_prohibited!(promise_batch_action_add_key_with_function_call, 0, 0, 0, 0, 0, 0, 0, 0, 0); + test_prohibited!(promise_batch_action_delete_key, 0, 0, 0); + test_prohibited!(promise_batch_action_delete_account, 0, 0, 0); + test_prohibited!(promise_results_count); + test_prohibited!(promise_result, 0, 0); + test_prohibited!(promise_return, 0); + test_prohibited!(storage_write, 0, 0, 0, 0, 0); + test_prohibited!(storage_remove, 0, 0, 0); +} + +#[test] +fn test_allowed_view_method() { + let mut logic_builder = VMLogicBuilder::view(); + let mut logic = logic_builder.build(); + assert_eq!(logic.block_index().unwrap(), logic_builder.context.block_height); +} diff --git a/runtime/unc-vm-runner/src/logic/tests/vm_logic_builder.rs b/runtime/unc-vm-runner/src/logic/tests/vm_logic_builder.rs new file mode 100644 index 000000000..be8b54de3 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/tests/vm_logic_builder.rs @@ -0,0 +1,159 @@ +use crate::logic::mocks::mock_external::MockedExternal; +use crate::logic::mocks::mock_memory::MockedMemory; +use crate::logic::types::PromiseResult; +use crate::logic::{Config, MemSlice, VMContext, VMLogic}; +use crate::tests::test_vm_config; +use unc_parameters::RuntimeFeesConfig; + +pub(super) struct VMLogicBuilder { + pub ext: MockedExternal, + pub config: Config, + pub fees_config: RuntimeFeesConfig, + pub promise_results: Vec, + pub memory: MockedMemory, + pub context: VMContext, +} + +impl Default for VMLogicBuilder { + fn default() -> Self { + VMLogicBuilder { + config: test_vm_config(), + fees_config: RuntimeFeesConfig::test(), + ext: MockedExternal::default(), + memory: MockedMemory::default(), + promise_results: vec![], + context: get_context(), + } + } +} + +impl VMLogicBuilder { + pub fn view() -> Self { + let mut builder = Self::default(); + let max_gas_burnt = builder.config.limit_config.max_gas_burnt; + builder.context.view_config = + Some(unc_primitives_core::config::ViewConfig { max_gas_burnt }); + builder + } + + pub fn build(&mut self) -> TestVMLogic<'_> { + let context = self.context.clone(); + TestVMLogic::from(VMLogic::new( + &mut self.ext, + context, + &self.config, + &self.fees_config, + &self.promise_results, + &mut self.memory, + )) + } + + pub fn free() -> Self { + VMLogicBuilder { + config: { + let mut config = test_vm_config(); + config.make_free(); + config + }, + fees_config: RuntimeFeesConfig::free(), + ext: MockedExternal::default(), + memory: MockedMemory::default(), + promise_results: vec![], + context: get_context(), + } + } +} + +fn get_context() -> VMContext { + VMContext { + current_account_id: "alice.near".parse().unwrap(), + signer_account_id: "bob.near".parse().unwrap(), + signer_account_pk: vec![0, 1, 2, 3, 4], + predecessor_account_id: "carol.near".parse().unwrap(), + input: vec![0, 1, 2, 3, 4], + block_height: 10, + block_timestamp: 42, + epoch_height: 1, + account_balance: 100, + storage_usage: 0, + account_locked_balance: 50, + attached_deposit: 10, + prepaid_gas: 10u64.pow(14), + random_seed: vec![0, 1, 2], + view_config: None, + output_data_receivers: vec![], + } +} + +/// Wrapper around `VMLogic` which adds helper test methods. +pub(super) struct TestVMLogic<'a> { + logic: VMLogic<'a>, + /// Offset at which `internal_memory_write` will write next. + mem_write_offset: u64, +} + +impl<'a> std::convert::From> for TestVMLogic<'a> { + fn from(logic: VMLogic<'a>) -> Self { + Self { logic, mem_write_offset: 0 } + } +} + +impl<'a> std::ops::Deref for TestVMLogic<'a> { + type Target = VMLogic<'a>; + fn deref(&self) -> &Self::Target { + &self.logic + } +} + +impl std::ops::DerefMut for TestVMLogic<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.logic + } +} + +impl TestVMLogic<'_> { + /// Writes data into guest memory and returns pointer at its location. + /// + /// Subsequent calls to the method write buffers one after the other. It + /// makes it convenient to populate the memory with various different data + /// to later use in function calls. + pub(super) fn internal_mem_write(&mut self, data: &[u8]) -> MemSlice { + let slice = self.internal_mem_write_at(self.mem_write_offset, data); + self.mem_write_offset += slice.len; + slice + } + + /// Writes data into guest memory at given location. + pub(super) fn internal_mem_write_at(&mut self, ptr: u64, data: &[u8]) -> MemSlice { + self.memory().set_for_free(ptr, data).unwrap(); + MemSlice { len: u64::try_from(data.len()).unwrap(), ptr } + } + + /// Reads data from guest memory into a Vector. + pub(super) fn internal_mem_read(&mut self, ptr: u64, len: u64) -> Vec { + self.memory().view_for_free(MemSlice { ptr, len }).unwrap().into_owned() + } + + /// Calls `logic.read_register` and then on success reads data from guest + /// memory comparing it to expected value. + /// + /// The `read_register` call is made as if contract has made it. In + /// particular, gas is charged for it. Later reading of the contents of the + /// memory is done for free. Panics if the register is not set or contracts + /// runs out of gas. + /// + /// The value of the register is read onto the end of the guest memory + /// overriding anything that might already be there. + #[track_caller] + pub(super) fn assert_read_register(&mut self, want: &[u8], register_id: u64) { + let len = self.registers().get_len(register_id).unwrap(); + let ptr = MockedMemory::MEMORY_SIZE - len; + self.read_register(register_id, ptr).unwrap(); + let got = self.memory().view_for_free(MemSlice { ptr, len }).unwrap(); + assert_eq!(want, &got[..]); + } + + pub fn compute_outcome(self) -> crate::logic::VMOutcome { + self.logic.compute_outcome() + } +} diff --git a/runtime/unc-vm-runner/src/logic/types.rs b/runtime/unc-vm-runner/src/logic/types.rs new file mode 100644 index 000000000..4dd9e30b3 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/types.rs @@ -0,0 +1,39 @@ +pub use unc_primitives_core::types::*; + +pub type PublicKey = Vec; +pub type PromiseIndex = u64; +pub type ReceiptIndex = u64; +pub type IteratorIndex = u64; + +#[derive(Debug, PartialEq, Clone)] +pub enum ReturnData { + /// Method returned some value or data. + Value(Vec), + + /// The return value of the method should be taken from the return value of another method + /// identified through receipt index. + ReceiptIndex(ReceiptIndex), + + /// Method hasn't returned any data or promise. + None, +} + +impl ReturnData { + /// Function to extract value from ReturnData. + pub fn as_value(self) -> Option> { + match self { + ReturnData::Value(value) => Some(value), + _ => None, + } + } +} + +/// When there is a callback attached to one or more contract calls the execution results of these +/// calls are available to the contract invoked through the callback. +#[derive(Debug, PartialEq)] +pub enum PromiseResult { + /// Current version of the protocol never returns `PromiseResult::NotReady`. + NotReady, + Successful(Vec), + Failed, +} diff --git a/runtime/unc-vm-runner/src/logic/utils.rs b/runtime/unc-vm-runner/src/logic/utils.rs new file mode 100644 index 000000000..bbfe7a0a8 --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/utils.rs @@ -0,0 +1,58 @@ +use super::HostError; + +/// Uses `,` separator to split `method_names` into a vector of method names. +/// Returns an empty vec if the empty slice is given. +/// Throws `HostError::EmptyMethodName` in case there is an empty method name inside. +pub(super) fn split_method_names(method_names: &[u8]) -> Result>, HostError> { + if method_names.is_empty() { + Ok(vec![]) + } else { + method_names + .split(|c| *c == b',') + .map(|v| if v.is_empty() { Err(HostError::EmptyMethodName) } else { Ok(v.to_vec()) }) + .collect() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_split_method_names_empty() { + assert_eq!(split_method_names(b""), Ok(vec![])); + } + + #[test] + fn test_split_method_one_name() { + assert_eq!(split_method_names(b"hello"), Ok(vec![b"hello".to_vec()])); + } + + #[test] + fn test_split_method_two_method_names() { + assert_eq!( + split_method_names(b"hello,world"), + Ok(vec![b"hello".to_vec(), b"world".to_vec()]) + ); + } + + #[test] + fn test_split_empty_method_name_inside() { + assert_eq!(split_method_names(b"hello,,world"), Err(HostError::EmptyMethodName)); + } + + #[test] + fn test_split_empty_method_name_front() { + assert_eq!(split_method_names(b",world"), Err(HostError::EmptyMethodName)); + } + + #[test] + fn test_split_empty_method_name_back() { + assert_eq!(split_method_names(b"world,"), Err(HostError::EmptyMethodName)); + } + + #[test] + fn test_split_empty_method_name_comma_only() { + assert_eq!(split_method_names(b","), Err(HostError::EmptyMethodName)); + } +} diff --git a/runtime/unc-vm-runner/src/logic/vmstate.rs b/runtime/unc-vm-runner/src/logic/vmstate.rs new file mode 100644 index 000000000..bc1f180bf --- /dev/null +++ b/runtime/unc-vm-runner/src/logic/vmstate.rs @@ -0,0 +1,366 @@ +use super::dependencies::{MemSlice, MemoryLike}; +use super::errors::{HostError, VMLogicError}; +use super::gas_counter::GasCounter; +use core::mem::size_of; +use unc_parameters::vm::LimitConfig; +use unc_parameters::ExtCosts::*; +use std::borrow::Cow; +use std::collections::hash_map::Entry; + +type Result = ::std::result::Result; + +/// Guest memory. +/// +/// Provides interface to access the guest memory while correctly accounting for +/// gas usage. +/// +/// Really the main point of this struct is that it is a separate object so when +/// its methods are called, such as `memory.get_into(&mut gas_counter, ...)`, +/// the compiler can deconstruct the access to each field of [`VMLogic`] and do +/// more granular lifetime analysis. In particular, this design is what allows +/// us to forgo copying register value in [`VMLogic::read_register`]. +pub(super) struct Memory<'a>(&'a mut dyn MemoryLike); + +macro_rules! memory_get { + ($_type:ty, $name:ident) => { + pub(super) fn $name( + &mut self, + gas_counter: &mut GasCounter, + offset: u64, + ) -> Result<$_type> { + let mut array = [0u8; size_of::<$_type>()]; + self.get_into(gas_counter, offset, &mut array)?; + Ok(<$_type>::from_le_bytes(array)) + } + }; +} + +macro_rules! memory_set { + ($_type:ty, $name:ident) => { + pub(super) fn $name( + &mut self, + gas_counter: &mut GasCounter, + offset: u64, + value: $_type, + ) -> Result<()> { + self.set(gas_counter, offset, &value.to_le_bytes()) + } + }; +} + +impl<'a> Memory<'a> { + pub(super) fn new(mem: &'a mut dyn MemoryLike) -> Self { + Self(mem) + } + + /// Returns view of the guest memory. + /// + /// Not all runtimes support returning a view to the guest memory so this + /// may return an owned vector. + pub(super) fn view<'s>( + &'s self, + gas_counter: &mut GasCounter, + slice: MemSlice, + ) -> Result> { + gas_counter.pay_base(read_memory_base)?; + gas_counter.pay_per(read_memory_byte, slice.len)?; + self.0.view_memory(slice).map_err(|_| HostError::MemoryAccessViolation.into()) + } + + /// Like [`Self::view`] but does not pay gas fees. + pub(super) fn view_for_free(&self, slice: MemSlice) -> Result> { + self.0.view_memory(slice).map_err(|_| HostError::MemoryAccessViolation.into()) + } + + /// Copies data from guest memory into provided buffer accounting for gas. + fn get_into(&self, gas_counter: &mut GasCounter, offset: u64, buf: &mut [u8]) -> Result<()> { + gas_counter.pay_base(read_memory_base)?; + let len = u64::try_from(buf.len()).map_err(|_| HostError::MemoryAccessViolation)?; + gas_counter.pay_per(read_memory_byte, len)?; + self.0.read_memory(offset, buf).map_err(|_| HostError::MemoryAccessViolation.into()) + } + + /// Copies data from provided buffer into guest memory accounting for gas. + pub(super) fn set( + &mut self, + gas_counter: &mut GasCounter, + offset: u64, + buf: &[u8], + ) -> Result<()> { + gas_counter.pay_base(write_memory_base)?; + gas_counter.pay_per(write_memory_byte, buf.len() as _)?; + self.0.write_memory(offset, buf).map_err(|_| HostError::MemoryAccessViolation.into()) + } + + #[cfg(test)] + pub(super) fn set_for_free(&mut self, offset: u64, buf: &[u8]) -> Result<()> { + self.0.write_memory(offset, buf).map_err(|_| HostError::MemoryAccessViolation.into()) + } + + memory_get!(u128, get_u128); + memory_get!(u32, get_u32); + memory_get!(u16, get_u16); + memory_get!(u8, get_u8); + memory_set!(u128, set_u128); + memory_set!(u64, set_u64); +} + +/// Registers to use by the guest. +/// +/// Provides interface to access registers while correctly accounting for gas +/// usage. +/// +/// See documentation of [`Memory`] for more motivation for this struct. +#[derive(Default, Clone)] +pub(super) struct Registers { + /// Values of each existing register. + registers: std::collections::HashMap>, + + /// Total memory usage as counted for the purposes of the contract + /// execution. + /// + /// Usage of each register is counted as its value’s length plus eight + /// (i.e. size of `u64`). Total usage is sum over all registers. This only + /// approximates actual usage in memory. + total_memory_usage: u64, +} + +impl Registers { + /// Returns register with given index. + /// + /// Returns an error if (i) there’s not enough gas to perform the register + /// read or (ii) register with given index doesn’t exist. + pub(super) fn get<'s>( + &'s self, + gas_counter: &mut GasCounter, + register_id: u64, + ) -> Result<&'s [u8]> { + if let Some(data) = self.registers.get(®ister_id) { + gas_counter.pay_base(read_register_base)?; + let len = u64::try_from(data.len()).map_err(|_| HostError::MemoryAccessViolation)?; + gas_counter.pay_per(read_register_byte, len)?; + Ok(&data[..]) + } else { + Err(HostError::InvalidRegisterId { register_id }.into()) + } + } + + #[cfg(test)] + pub(super) fn get_for_free<'s>(&'s self, register_id: u64) -> Option<&'s [u8]> { + self.registers.get(®ister_id).map(|data| &data[..]) + } + + /// Returns length of register with given index or None if no such register. + pub(super) fn get_len(&self, register_id: u64) -> Option { + self.registers.get(®ister_id).map(|data| data.len() as u64) + } + + /// Sets register with given index. + /// + /// Returns an error if (i) there’s not enough gas to perform the register + /// write or (ii) if setting the register would violate configured limits. + pub(super) fn set( + &mut self, + gas_counter: &mut GasCounter, + config: &LimitConfig, + register_id: u64, + data: T, + ) -> Result<()> + where + T: Into> + AsRef<[u8]>, + { + let data_len = + u64::try_from(data.as_ref().len()).map_err(|_| HostError::MemoryAccessViolation)?; + gas_counter.pay_base(write_register_base)?; + gas_counter.pay_per(write_register_byte, data_len)?; + let entry = self.check_set_register(config, register_id, data_len)?; + let data = data.into(); + match entry { + Entry::Occupied(mut entry) => { + entry.insert(data); + } + Entry::Vacant(entry) => { + entry.insert(data); + } + }; + Ok(()) + } + + /// Checks and updates registers usage limits before setting given register + /// to value with given length. + /// + /// On success, returns Entry which must be used to insert the new value + /// into the registers. + fn check_set_register<'a>( + &'a mut self, + config: &LimitConfig, + register_id: u64, + data_len: u64, + ) -> Result>> { + if data_len > config.max_register_size { + return Err(HostError::MemoryAccessViolation.into()); + } + // Fun fact: if we are at the limit and we replace a register, we’ll + // fail even though we should be succeeding. This bug is now part of + // the protocol so we can’t change it. + if self.registers.len() as u64 >= config.max_number_registers { + return Err(HostError::MemoryAccessViolation.into()); + } + + let entry = self.registers.entry(register_id); + let calc_usage = |len: u64| len + size_of::() as u64; + let old_mem_usage = match &entry { + Entry::Occupied(entry) => calc_usage(entry.get().len() as u64), + Entry::Vacant(_) => 0, + }; + let usage = self + .total_memory_usage + .checked_sub(old_mem_usage) + .unwrap() + .checked_add(calc_usage(data_len)) + .ok_or(HostError::MemoryAccessViolation)?; + if usage > config.registers_memory_limit { + return Err(HostError::MemoryAccessViolation.into()); + } + self.total_memory_usage = usage; + Ok(entry) + } +} + +/// Reads data from guest memory or register. +/// +/// If `len` is `u64::MAX` read register with index `ptr`. Otherwise, reads +/// `len` bytes of guest memory starting at given offset. Returns error if +/// there’s insufficient gas, memory interval is out of bounds or given register +/// isn’t set. +/// +/// This is not a method on `VMLogic` so that the compiler can track borrowing +/// of gas counter, memory and registers separately. This allows `VMLogic` to +/// borrow value from a register and then continue constructing mutable +/// references to other fields in the structure.. +pub(super) fn get_memory_or_register<'a, 'b>( + gas_counter: &mut GasCounter, + memory: &'b Memory<'a>, + registers: &'b Registers, + ptr: u64, + len: u64, +) -> Result> { + if len == u64::MAX { + registers.get(gas_counter, ptr).map(Cow::Borrowed) + } else { + memory.view(gas_counter, MemSlice { ptr, len }) + } +} + +#[cfg(test)] +mod tests { + use super::HostError; + use super::Registers; + use crate::logic::gas_counter::GasCounter; + use crate::logic::LimitConfig; + use crate::tests::test_vm_config; + use unc_parameters::ExtCostsConfig; + + struct RegistersTestContext { + gas: GasCounter, + cfg: LimitConfig, + regs: Registers, + } + + impl RegistersTestContext { + fn new() -> Self { + let costs = ExtCostsConfig::test(); + Self { + gas: GasCounter::new(costs, u64::MAX, 0, u64::MAX, false), + cfg: test_vm_config().limit_config, + regs: Default::default(), + } + } + + #[track_caller] + fn assert_set_success(&mut self, register_id: u64, value: &str) { + self.regs.set(&mut self.gas, &self.cfg, register_id, value.as_bytes()).unwrap(); + self.assert_read(register_id, Some(value)); + } + + #[track_caller] + fn assert_set_failure(&mut self, register_id: u64, value: &str) { + let want = Err(HostError::MemoryAccessViolation.into()); + let got = self.regs.set(&mut self.gas, &self.cfg, register_id, value.as_bytes()); + assert_eq!(want, got); + } + + #[track_caller] + fn assert_read(&mut self, register_id: u64, value: Option<&str>) { + if let Some(value) = value { + assert_eq!(Ok(value.as_bytes()), self.regs.get(&mut self.gas, register_id)); + assert_eq!(Some(value.len() as u64), self.regs.get_len(register_id)); + } else { + let err = HostError::InvalidRegisterId { register_id }.into(); + assert_eq!(Err(err), self.regs.get(&mut self.gas, register_id)); + assert_eq!(None, self.regs.get_len(register_id)); + } + } + + #[track_caller] + fn assert_used_gas(&self, gas: u64) { + assert_eq!((gas, gas), (self.gas.burnt_gas(), self.gas.used_gas())); + } + } + + /// Tests basic setting and reading of registers. + #[test] + fn registers_set() { + let mut ctx = RegistersTestContext::new(); + ctx.assert_read(42, None); + ctx.assert_read(24, None); + ctx.assert_set_success(42, "foo"); + ctx.assert_read(24, None); + ctx.assert_used_gas(5394388050); + } + + /// Tests limit on number of registers. + #[test] + fn registers_max_number_limit() { + let mut ctx = RegistersTestContext::new(); + ctx.cfg.max_number_registers = 2; + + ctx.assert_set_success(42, "foo"); + ctx.assert_set_success(24, "bar"); + + // max_number_registers is 2 so cannot set third register + ctx.assert_set_failure(12, "baz"); + + // Due to historical bug, changing a register is not possible either + // once limit is reached: + ctx.assert_set_failure(42, "O_o"); + ctx.assert_set_failure(24, "O_o"); + + ctx.assert_used_gas(19419557634); + } + + /// Tests limit on a size of a single register. + #[test] + fn registers_register_size_limit() { + let mut ctx = RegistersTestContext::new(); + ctx.cfg.max_register_size = 3; + ctx.assert_set_success(42, "foo"); + ctx.assert_set_failure(24, "quux"); + ctx.assert_used_gas(8275116792); + } + + /// Tests limit on total memory usage. + #[test] + fn registers_usage_limit() { + let mut ctx = RegistersTestContext::new(); + ctx.cfg.registers_memory_limit = 11; + ctx.assert_set_success(42, "foo"); + // Replacing value is fine. + ctx.assert_set_success(42, "bar"); + ctx.assert_set_success(42, ""); + ctx.assert_set_success(42, "baz"); + // But three bytes is a limit (usage is sizeof(u64) + data.len()). + ctx.assert_set_failure(42, "quux"); + ctx.assert_used_gas(24446580564); + } +} diff --git a/runtime/unc-vm-runner/src/memory.rs b/runtime/unc-vm-runner/src/memory.rs new file mode 100644 index 000000000..3b80b2316 --- /dev/null +++ b/runtime/unc-vm-runner/src/memory.rs @@ -0,0 +1,69 @@ +use crate::logic::{MemSlice, MemoryLike}; + +use std::borrow::Cow; + +use wasmer_runtime::units::Pages; +use wasmer_runtime::wasm::MemoryDescriptor; +use wasmer_runtime::Memory; + +pub struct WasmerMemory(Memory); + +impl WasmerMemory { + pub fn new(initial_memory_pages: u32, max_memory_pages: u32) -> Self { + WasmerMemory( + Memory::new( + MemoryDescriptor::new( + Pages(initial_memory_pages), + Some(Pages(max_memory_pages)), + false, + ) + .unwrap(), + ) + .expect("TODO creating memory cannot fail"), + ) + } + + pub fn clone(&self) -> Memory { + self.0.clone() + } +} + +impl WasmerMemory { + fn with_memory(&self, offset: u64, len: usize, func: F) -> Result + where + F: FnOnce(core::slice::Iter<'_, std::cell::Cell>) -> T, + { + let start = usize::try_from(offset).map_err(|_| ())?; + let end = start.checked_add(len).ok_or(())?; + self.0.view().get(start..end).map(|mem| func(mem.iter())).ok_or(()) + } +} + +impl MemoryLike for WasmerMemory { + fn fits_memory(&self, slice: MemSlice) -> Result<(), ()> { + self.with_memory(slice.ptr, slice.len()?, |_| ()) + } + + fn view_memory(&self, slice: MemSlice) -> Result, ()> { + self.with_memory(slice.ptr, slice.len()?, |mem| { + Cow::Owned(mem.map(core::cell::Cell::get).collect()) + }) + } + + fn read_memory(&self, offset: u64, buffer: &mut [u8]) -> Result<(), ()> { + self.with_memory(offset, buffer.len(), |mem| { + buffer.iter_mut().zip(mem).for_each(|(dst, src)| *dst = src.get()); + }) + } + + fn write_memory(&mut self, offset: u64, buffer: &[u8]) -> Result<(), ()> { + self.with_memory(offset, buffer.len(), |mem| { + mem.zip(buffer.iter()).for_each(|(dst, src)| dst.set(*src)); + }) + } +} + +#[test] +fn test_memory_like() { + crate::logic::test_utils::test_memory_like(|| Box::new(WasmerMemory::new(1, 1))); +} diff --git a/runtime/unc-vm-runner/src/prepare.rs b/runtime/unc-vm-runner/src/prepare.rs new file mode 100644 index 000000000..a27c18e10 --- /dev/null +++ b/runtime/unc-vm-runner/src/prepare.rs @@ -0,0 +1,177 @@ +//! Module that takes care of loading, checking and preprocessing of a +//! wasm module before execution. + +use crate::logic::errors::PrepareError; +use unc_parameters::vm::{Config, VMKind}; + +mod prepare_v0; +mod prepare_v1; +mod prepare_v2; + +/// Loads the given module given in `original_code`, performs some checks on it and +/// does some preprocessing. +/// +/// The checks are: +/// +/// - module doesn't define an internal memory instance, +/// - imported memory (if any) doesn't reserve more memory than permitted by the `config`, +/// - all imported functions from the external environment matches defined by `env` module, +/// - functions number does not exceed limit specified in Config, +/// +/// The preprocessing includes injecting code for gas metering and metering the height of stack. +pub fn prepare_contract( + original_code: &[u8], + config: &Config, + kind: VMKind, +) -> Result, PrepareError> { + let prepare = config.limit_config.contract_prepare_version; + // NearVM => ContractPrepareVersion::V2 + assert!( + (kind != VMKind::NearVm) || (prepare == crate::logic::ContractPrepareVersion::V2), + "NearVM only works with contract prepare version V2", + ); + let features = crate::features::WasmFeatures::from(prepare); + match prepare { + crate::logic::ContractPrepareVersion::V0 => { + // NB: v1 here is not a bug, we are reusing the code. + prepare_v1::validate_contract(original_code, features, config)?; + prepare_v0::prepare_contract(original_code, config) + } + crate::logic::ContractPrepareVersion::V1 => { + prepare_v1::validate_contract(original_code, features, config)?; + prepare_v1::prepare_contract(original_code, config) + } + crate::logic::ContractPrepareVersion::V2 => { + prepare_v2::prepare_contract(original_code, features, config, kind) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::{test_vm_config, with_vm_variants}; + use assert_matches::assert_matches; + + fn parse_and_prepare_wat( + config: &Config, + vm_kind: VMKind, + wat: &str, + ) -> Result, PrepareError> { + let wasm = wat::parse_str(wat).unwrap(); + prepare_contract(wasm.as_ref(), &config, vm_kind) + } + + #[test] + fn internal_memory_declaration() { + let config = test_vm_config(); + with_vm_variants(&config, |kind| { + let r = parse_and_prepare_wat(&config, kind, r#"(module (memory 1 1))"#); + assert_matches!(r, Ok(_)); + }) + } + + #[test] + fn memory_imports() { + let config = test_vm_config(); + + // This test assumes that maximum page number is configured to a certain number. + assert_eq!(config.limit_config.max_memory_pages, 2048); + + with_vm_variants(&config, |kind| { + let r = parse_and_prepare_wat( + &config, + kind, + r#"(module (import "env" "memory" (memory 1 1)))"#, + ); + assert_matches!(r, Err(PrepareError::Memory)); + + // No memory import + let r = parse_and_prepare_wat(&config, kind, r#"(module)"#); + assert_matches!(r, Ok(_)); + + // initial exceed maximum + let r = parse_and_prepare_wat( + &config, + kind, + r#"(module (import "env" "memory" (memory 17 1)))"#, + ); + assert_matches!(r, Err(PrepareError::Deserialization)); + + // no maximum + let r = parse_and_prepare_wat( + &config, + kind, + r#"(module (import "env" "memory" (memory 1)))"#, + ); + assert_matches!(r, Err(PrepareError::Memory)); + + // requested maximum exceed configured maximum + let r = parse_and_prepare_wat( + &config, + kind, + r#"(module (import "env" "memory" (memory 1 33)))"#, + ); + assert_matches!(r, Err(PrepareError::Memory)); + }) + } + + #[test] + fn multiple_valid_memory_are_disabled() { + let config = test_vm_config(); + with_vm_variants(&config, |kind| { + // Our preparation and sanitization pass assumes a single memory, so we should fail when + // there are multiple specified. + let r = parse_and_prepare_wat( + &config, + kind, + r#"(module + (import "env" "memory" (memory 1 2048)) + (import "env" "memory" (memory 1 2048)) + )"#, + ); + assert_matches!(r, Err(_)); + let r = parse_and_prepare_wat( + &config, + kind, + r#"(module + (import "env" "memory" (memory 1 2048)) + (memory 1) + )"#, + ); + assert_matches!(r, Err(_)); + }) + } + + #[test] + fn imports() { + let config = test_vm_config(); + with_vm_variants(&config, |kind| { + // nothing can be imported from non-"env" module for now. + let r = parse_and_prepare_wat( + &config, + kind, + r#"(module (import "another_module" "memory" (memory 1 1)))"#, + ); + assert_matches!(r, Err(PrepareError::Instantiate)); + + let r = parse_and_prepare_wat( + &config, + kind, + r#"(module (import "env" "gas" (func (param i32))))"#, + ); + assert_matches!(r, Ok(_)); + + // TODO: Address tests once we check proper function signatures. + /* + // wrong signature + let r = parse_and_prepare_wat(r#"(module (import "env" "gas" (func (param i64))))"#); + assert_matches!(r, Err(Error::Instantiate)); + + // unknown function name + let r = parse_and_prepare_wat(r#"(module (import "env" "unknown_func" (func)))"#); + assert_matches!(r, Err(Error::Instantiate)); + */ + }) + } +} diff --git a/runtime/unc-vm-runner/src/prepare/prepare_v0.rs b/runtime/unc-vm-runner/src/prepare/prepare_v0.rs new file mode 100644 index 000000000..21ae2df60 --- /dev/null +++ b/runtime/unc-vm-runner/src/prepare/prepare_v0.rs @@ -0,0 +1,156 @@ +//! Legacy validation for very old protocol versions. + +use crate::logic::errors::PrepareError; +use crate::logic::Config; +use parity_wasm_41::builder; +use parity_wasm_41::elements::{self, External, MemorySection, Type}; +use pwasm_utils_12 as pwasm_utils; + +pub(crate) fn prepare_contract( + original_code: &[u8], + config: &Config, +) -> Result, PrepareError> { + ContractModule::init(original_code, config)? + .standardize_mem() + .ensure_no_internal_memory()? + .inject_gas_metering()? + .inject_stack_height_metering()? + .scan_imports()? + .into_wasm_code() +} + +struct ContractModule<'a> { + module: elements::Module, + config: &'a Config, +} + +impl<'a> ContractModule<'a> { + fn init(original_code: &[u8], config: &'a Config) -> Result { + let module = elements::deserialize_buffer(original_code).map_err(|e| { + tracing::debug!(err=?e, "parity_wasm_41 failed decoding a contract"); + PrepareError::Deserialization + })?; + Ok(ContractModule { module, config }) + } + + fn standardize_mem(self) -> Self { + let Self { mut module, config } = self; + + let mut tmp = MemorySection::default(); + + module.memory_section_mut().unwrap_or(&mut tmp).entries_mut().pop(); + + let entry = elements::MemoryType::new( + config.limit_config.initial_memory_pages, + Some(config.limit_config.max_memory_pages), + ); + + let mut builder = builder::from_module(module); + builder.push_import(elements::ImportEntry::new( + "env".to_string(), + "memory".to_string(), + elements::External::Memory(entry), + )); + + Self { module: builder.build(), config } + } + + /// Ensures that module doesn't declare internal memories. + /// + /// In this runtime we only allow wasm module to import memory from the environment. + /// Memory section contains declarations of internal linear memories, so if we find one + /// we reject such a module. + fn ensure_no_internal_memory(self) -> Result { + if self.module.memory_section().map_or(false, |ms| !ms.entries().is_empty()) { + Err(PrepareError::InternalMemoryDeclared) + } else { + Ok(self) + } + } + + fn inject_gas_metering(self) -> Result { + let Self { module, config } = self; + // Free config, no need for gas metering. + if config.regular_op_cost == 0 { + return Ok(Self { module, config }); + } + let gas_rules = pwasm_utils::rules::Set::new(1, Default::default()) + .with_grow_cost(config.grow_mem_cost); + let module = pwasm_utils::inject_gas_counter(module, &gas_rules) + .map_err(|_| PrepareError::GasInstrumentation)?; + Ok(Self { module, config }) + } + + fn inject_stack_height_metering(self) -> Result { + let Self { module, config } = self; + let module = + pwasm_utils::stack_height::inject_limiter(module, config.limit_config.max_stack_height) + .map_err(|_| PrepareError::StackHeightInstrumentation)?; + Ok(Self { module, config }) + } + + /// Scan an import section if any. + /// + /// This accomplishes two tasks: + /// + /// - checks any imported function against defined host functions set, incl. + /// their signatures. + /// - if there is a memory import, returns it's descriptor + fn scan_imports(self) -> Result { + let Self { module, config } = self; + + let types = module.type_section().map(elements::TypeSection::types).unwrap_or(&[]); + let import_entries = + module.import_section().map(elements::ImportSection::entries).unwrap_or(&[]); + + let mut imported_mem_type = None; + + for import in import_entries { + if import.module() != "env" { + // This import tries to import something from non-"env" module, + // but all imports are located in "env" at the moment. + return Err(PrepareError::Instantiate); + } + + let type_idx = match *import.external() { + External::Function(ref type_idx) => type_idx, + External::Memory(ref memory_type) => { + imported_mem_type = Some(memory_type); + continue; + } + _ => continue, + }; + + let Type::Function(ref _func_ty) = + types.get(*type_idx as usize).ok_or(PrepareError::Instantiate)?; + + // TODO: Function type check with Env + /* + + let ext_func = env + .funcs + .get(import.field().as_bytes()) + .ok_or_else(|| Error::Instantiate)?; + if !ext_func.func_type_matches(func_ty) { + return Err(Error::Instantiate); + } + */ + } + if let Some(memory_type) = imported_mem_type { + // Inspect the module to extract the initial and maximum page count. + let limits = memory_type.limits(); + if limits.initial() != config.limit_config.initial_memory_pages + || limits.maximum() != Some(config.limit_config.max_memory_pages) + { + return Err(PrepareError::Memory); + } + } else { + return Err(PrepareError::Memory); + }; + Ok(Self { module, config }) + } + + fn into_wasm_code(self) -> Result, PrepareError> { + elements::serialize(self.module).map_err(|_| PrepareError::Serialization) + } +} diff --git a/runtime/unc-vm-runner/src/prepare/prepare_v1.rs b/runtime/unc-vm-runner/src/prepare/prepare_v1.rs new file mode 100644 index 000000000..113f468a2 --- /dev/null +++ b/runtime/unc-vm-runner/src/prepare/prepare_v1.rs @@ -0,0 +1,266 @@ +//! Less legacy validation for old protocol versions. + +use crate::logic::errors::PrepareError; +use crate::logic::Config; +use parity_wasm::builder; +use parity_wasm::elements::{self, External, MemorySection}; + +/// Loads the given module given in `original_code`, performs some checks on it and +/// does some preprocessing. +/// +/// The checks are: +/// +/// - module doesn't define an internal memory instance, +/// - imported memory (if any) doesn't reserve more memory than permitted by the `config`, +/// - all imported functions from the external environment matches defined by `env` module, +/// - functions number does not exceed limit specified in Config, +/// +/// The preprocessing includes injecting code for gas metering and metering the height of stack. +pub(crate) fn prepare_contract( + original_code: &[u8], + config: &Config, +) -> Result, PrepareError> { + ContractModule::init(original_code, config)? + .scan_imports()? + .standardize_mem() + .ensure_no_internal_memory()? + .inject_gas_metering()? + .inject_stack_height_metering()? + .into_wasm_code() +} + +pub(crate) struct ContractModule<'a> { + module: elements::Module, + config: &'a Config, +} + +impl<'a> ContractModule<'a> { + pub(crate) fn init(original_code: &[u8], config: &'a Config) -> Result { + let module = parity_wasm::deserialize_buffer(original_code).map_err(|e| { + tracing::debug!(err=?e, "parity_wasm failed decoding a contract"); + PrepareError::Deserialization + })?; + Ok(ContractModule { module, config }) + } + + pub(crate) fn standardize_mem(self) -> Self { + let Self { mut module, config } = self; + + let mut tmp = MemorySection::default(); + + module.memory_section_mut().unwrap_or(&mut tmp).entries_mut().pop(); + + let entry = elements::MemoryType::new( + config.limit_config.initial_memory_pages, + Some(config.limit_config.max_memory_pages), + ); + + let mut builder = builder::from_module(module); + builder.push_import(elements::ImportEntry::new( + "env".to_string(), + "memory".to_string(), + elements::External::Memory(entry), + )); + + Self { module: builder.build(), config } + } + + /// Ensures that module doesn't declare internal memories. + /// + /// In this runtime we only allow wasm module to import memory from the environment. + /// Memory section contains declarations of internal linear memories, so if we find one + /// we reject such a module. + pub(crate) fn ensure_no_internal_memory(self) -> Result { + if self.module.memory_section().map_or(false, |ms| !ms.entries().is_empty()) { + Err(PrepareError::InternalMemoryDeclared) + } else { + Ok(self) + } + } + + fn inject_gas_metering(self) -> Result { + let Self { module, config } = self; + // Free config, no need for gas metering. + if config.regular_op_cost == 0 { + return Ok(Self { module, config }); + } + let gas_rules = crate::instrument::rules::Set::new(1, Default::default()) + .with_grow_cost(config.grow_mem_cost); + let module = crate::instrument::gas::inject_gas_counter(module, &gas_rules, "env") + .map_err(|_| PrepareError::GasInstrumentation)?; + Ok(Self { module, config }) + } + + fn inject_stack_height_metering(self) -> Result { + let Self { module, config } = self; + let module = crate::instrument::stack_height::inject_limiter( + module, + config.limit_config.max_stack_height, + ) + .map_err(|_| PrepareError::StackHeightInstrumentation)?; + Ok(Self { module, config }) + } + + /// Scan an import section if any. + /// + /// This accomplishes two tasks: + /// + /// - checks any imported function against defined host functions set, incl. + /// their signatures. + /// - if there is a memory import, returns it's descriptor + pub(crate) fn scan_imports(self) -> Result { + let Self { module, config } = self; + + let types = module.type_section().map(elements::TypeSection::types).unwrap_or(&[]); + let import_entries = + module.import_section().map(elements::ImportSection::entries).unwrap_or(&[]); + + for import in import_entries { + if import.module() != "env" { + // This import tries to import something from non-"env" module, + // but all imports are located in "env" at the moment. + return Err(PrepareError::Instantiate); + } + + let type_idx = match *import.external() { + External::Function(ref type_idx) => type_idx, + External::Memory(_) => return Err(PrepareError::Memory), + _ => continue, + }; + + let elements::Type::Function(ref _func_ty) = + types.get(*type_idx as usize).ok_or(PrepareError::Instantiate)?; + + // TODO: Function type check with Env + /* + + let ext_func = env + .funcs + .get(import.field().as_bytes()) + .ok_or_else(|| Error::Instantiate)?; + if !ext_func.func_type_matches(func_ty) { + return Err(Error::Instantiate); + } + */ + } + Ok(Self { module, config }) + } + + pub(crate) fn into_wasm_code(self) -> Result, PrepareError> { + elements::serialize(self.module).map_err(|_| PrepareError::Serialization) + } +} + +/// Decode and validate the provided WebAssembly code with the `wasmparser` crate. +/// +/// This function will return the number of functions defined globally in the provided WebAssembly +/// module as well as the number of locals declared by all functions. If either counter overflows, +/// `None` is returned in its place. +fn wasmparser_decode( + code: &[u8], + features: crate::features::WasmFeatures, +) -> Result<(Option, Option), wasmparser::BinaryReaderError> { + use wasmparser::{ImportSectionEntryType, ValidPayload}; + let mut validator = wasmparser::Validator::new(); + validator.wasm_features(features.into()); + let mut function_count = Some(0u64); + let mut local_count = Some(0u64); + for payload in wasmparser::Parser::new(0).parse_all(code) { + let payload = payload?; + + // The validator does not output `ValidPayload::Func` for imported functions. + if let wasmparser::Payload::ImportSection(ref import_section_reader) = payload { + let mut import_section_reader = import_section_reader.clone(); + for _ in 0..import_section_reader.get_count() { + match import_section_reader.read()?.ty { + ImportSectionEntryType::Function(_) => { + function_count = function_count.and_then(|f| f.checked_add(1)) + } + ImportSectionEntryType::Table(_) + | ImportSectionEntryType::Memory(_) + | ImportSectionEntryType::Event(_) + | ImportSectionEntryType::Global(_) + | ImportSectionEntryType::Module(_) + | ImportSectionEntryType::Instance(_) => {} + } + } + } + + match validator.payload(&payload)? { + ValidPayload::Ok => (), + ValidPayload::Submodule(_) => panic!("submodules are not reachable (not enabled)"), + ValidPayload::Func(mut validator, body) => { + validator.validate(&body)?; + function_count = function_count.and_then(|f| f.checked_add(1)); + // Count the global number of local variables. + let mut local_reader = body.get_locals_reader()?; + for _ in 0..local_reader.get_count() { + let (count, _type) = local_reader.read()?; + local_count = local_count.and_then(|l| l.checked_add(count.into())); + } + } + } + } + Ok((function_count, local_count)) +} + +pub(crate) fn validate_contract( + code: &[u8], + features: crate::features::WasmFeatures, + config: &Config, +) -> Result<(), PrepareError> { + let (function_count, local_count) = wasmparser_decode(code, features).map_err(|e| { + tracing::debug!(err=?e, "wasmparser failed decoding a contract"); + PrepareError::Deserialization + })?; + // Verify the number of functions does not exceed the limit we imposed. Note that the ordering + // of this check is important. In the past we first validated the entire module and only then + // verified that the limit is not exceeded. While it would be more efficient to check for this + // before validating the function bodies, it would change the results for malformed WebAssembly + // modules. + if let Some(max_functions) = config.limit_config.max_functions_number_per_contract { + if function_count.ok_or(PrepareError::TooManyFunctions)? > max_functions { + return Err(PrepareError::TooManyFunctions); + } + } + // Similarly, do the same for the number of locals. + if let Some(max_locals) = config.limit_config.max_locals_per_contract { + if local_count.ok_or(PrepareError::TooManyLocals)? > max_locals { + return Err(PrepareError::TooManyLocals); + } + } + Ok(()) +} + +#[cfg(test)] +mod test { + use crate::logic::ContractPrepareVersion; + use crate::tests::test_vm_config; + + #[test] + fn v1_preparation_generates_valid_contract_fuzzer() { + let mut config = test_vm_config(); + let prepare_version = ContractPrepareVersion::V1; + config.limit_config.contract_prepare_version = prepare_version; + let features = crate::features::WasmFeatures::from(prepare_version); + bolero::check!().for_each(|input: &[u8]| { + // DO NOT use ArbitraryModule. We do want modules that may be invalid here, if they pass our validation step! + if let Ok(_) = super::validate_contract(input, features, &config) { + match super::prepare_contract(input, &config) { + Err(_e) => (), // TODO: this should be a panic, but for now it’d actually trigger + Ok(code) => { + let mut validator = wasmparser::Validator::new(); + validator.wasm_features(features.into()); + match validator.validate_all(&code) { + Ok(_) => (), + Err(e) => panic!( + "prepared code failed validation: {e:?}\ncontract: {}", + hex::encode(input), + ), + } + } + } + } + }); + } +} diff --git a/runtime/unc-vm-runner/src/prepare/prepare_v2.rs b/runtime/unc-vm-runner/src/prepare/prepare_v2.rs new file mode 100644 index 000000000..bb9f9ebc0 --- /dev/null +++ b/runtime/unc-vm-runner/src/prepare/prepare_v2.rs @@ -0,0 +1,422 @@ +use crate::logic::errors::PrepareError; +use finite_wasm::wasmparser as wp; +use unc_parameters::vm::{Config, VMKind}; +use wasm_encoder::{Encode, Section, SectionId}; + +struct PrepareContext<'a> { + code: &'a [u8], + config: &'a Config, + output_code: Vec, + function_limit: u64, + local_limit: u64, + validator: wp::Validator, + func_validator_allocations: wp::FuncValidatorAllocations, + before_import_section: bool, +} + +impl<'a> PrepareContext<'a> { + fn new(code: &'a [u8], features: crate::features::WasmFeatures, config: &'a Config) -> Self { + let limits = &config.limit_config; + Self { + code, + config, + output_code: Vec::with_capacity(code.len()), + // Practically reaching u64::MAX locals or functions is infeasible, so when the limit is not + // specified, use that as a limit. + function_limit: limits.max_functions_number_per_contract.unwrap_or(u64::MAX), + local_limit: limits.max_locals_per_contract.unwrap_or(u64::MAX), + validator: wp::Validator::new_with_features(features.into()), + func_validator_allocations: wp::FuncValidatorAllocations::default(), + before_import_section: true, + } + } + + /// “Early” preparation. + /// + /// Must happen before the finite-wasm analysis and is applicable to NearVm just as much as it is + /// applicable to other runtimes. + /// + /// This will validate the module, normalize the memories within, apply limits. + fn run(&mut self) -> Result, PrepareError> { + self.before_import_section = true; + let parser = wp::Parser::new(0); + for payload in parser.parse_all(self.code) { + let payload = payload.map_err(|err| { + tracing::trace!(?err, "was not able to early prepare the input module"); + PrepareError::Deserialization + })?; + match payload { + wp::Payload::Version { num, encoding, range } => { + self.copy(range.clone())?; + self.validator + .version(num, encoding, &range) + .map_err(|_| PrepareError::Deserialization)?; + } + wp::Payload::End(offset) => { + self.validator.end(offset).map_err(|_| PrepareError::Deserialization)?; + } + + wp::Payload::TypeSection(reader) => { + self.validator + .type_section(&reader) + .map_err(|_| PrepareError::Deserialization)?; + self.copy_section(SectionId::Type, reader.range())?; + } + + wp::Payload::ImportSection(reader) => { + self.before_import_section = false; + self.validator + .import_section(&reader) + .map_err(|_| PrepareError::Deserialization)?; + self.transform_import_section(&reader)?; + } + + wp::Payload::FunctionSection(reader) => { + self.ensure_import_section(); + self.validator + .function_section(&reader) + .map_err(|_| PrepareError::Deserialization)?; + self.copy_section(SectionId::Function, reader.range())?; + } + wp::Payload::TableSection(reader) => { + self.ensure_import_section(); + self.validator + .table_section(&reader) + .map_err(|_| PrepareError::Deserialization)?; + self.copy_section(SectionId::Table, reader.range())?; + } + wp::Payload::MemorySection(reader) => { + // We do not want to include the implicit memory anymore as we normalized it by + // importing the memory instead. + self.ensure_import_section(); + self.validator + .memory_section(&reader) + .map_err(|_| PrepareError::Deserialization)?; + } + wp::Payload::GlobalSection(reader) => { + self.ensure_import_section(); + self.validator + .global_section(&reader) + .map_err(|_| PrepareError::Deserialization)?; + self.copy_section(SectionId::Global, reader.range())?; + } + wp::Payload::ExportSection(reader) => { + self.ensure_import_section(); + self.validator + .export_section(&reader) + .map_err(|_| PrepareError::Deserialization)?; + self.copy_section(SectionId::Export, reader.range())?; + } + wp::Payload::StartSection { func, range } => { + self.ensure_import_section(); + self.validator + .start_section(func, &range) + .map_err(|_| PrepareError::Deserialization)?; + self.copy_section(SectionId::Start, range.clone())?; + } + wp::Payload::ElementSection(reader) => { + self.ensure_import_section(); + self.validator + .element_section(&reader) + .map_err(|_| PrepareError::Deserialization)?; + self.copy_section(SectionId::Element, reader.range())?; + } + wp::Payload::DataCountSection { count, range } => { + self.ensure_import_section(); + self.validator + .data_count_section(count, &range) + .map_err(|_| PrepareError::Deserialization)?; + self.copy_section(SectionId::DataCount, range.clone())?; + } + wp::Payload::DataSection(reader) => { + self.ensure_import_section(); + self.validator + .data_section(&reader) + .map_err(|_| PrepareError::Deserialization)?; + self.copy_section(SectionId::Data, reader.range())?; + } + wp::Payload::CodeSectionStart { size: _, count, range } => { + self.ensure_import_section(); + self.function_limit = self + .function_limit + .checked_sub(u64::from(count)) + .ok_or(PrepareError::TooManyFunctions)?; + self.validator + .code_section_start(count, &range) + .map_err(|_| PrepareError::Deserialization)?; + self.copy_section(SectionId::Code, range.clone())?; + } + wp::Payload::CodeSectionEntry(func) => { + let local_reader = + func.get_locals_reader().map_err(|_| PrepareError::Deserialization)?; + for local in local_reader { + let (count, _ty) = local.map_err(|_| PrepareError::Deserialization)?; + self.local_limit = self + .local_limit + .checked_sub(u64::from(count)) + .ok_or(PrepareError::TooManyLocals)?; + } + + let func_validator = self + .validator + .code_section_entry(&func) + .map_err(|_| PrepareError::Deserialization)?; + // PANIC-SAFETY: no big deal if we panic here while the allocations are taken. + // Worst-case we are going to be making new allocations again, but in practice + // this should never happen as this context should not be reused. + let allocs = std::mem::replace( + &mut self.func_validator_allocations, + wp::FuncValidatorAllocations::default(), + ); + let mut func_validator = func_validator.into_validator(allocs); + func_validator.validate(&func).map_err(|_| PrepareError::Deserialization)?; + self.func_validator_allocations = func_validator.into_allocations(); + } + wp::Payload::CustomSection(reader) => { + self.ensure_import_section(); + self.copy_section(SectionId::Custom, reader.range())?; + } + + // Extensions not supported. + wp::Payload::UnknownSection { .. } + | wp::Payload::TagSection(_) + | wp::Payload::ModuleSection { .. } + | wp::Payload::InstanceSection(_) + | wp::Payload::CoreTypeSection(_) + | wp::Payload::ComponentSection { .. } + | wp::Payload::ComponentInstanceSection(_) + | wp::Payload::ComponentAliasSection(_) + | wp::Payload::ComponentTypeSection(_) + | wp::Payload::ComponentCanonicalSection(_) + | wp::Payload::ComponentStartSection { .. } + | wp::Payload::ComponentImportSection(_) + | wp::Payload::ComponentExportSection(_) => { + tracing::trace!("input module contains unsupported section"); + return Err(PrepareError::Deserialization); + } + } + } + Ok(std::mem::replace(&mut self.output_code, Vec::new())) + } + + fn transform_import_section( + &mut self, + reader: &wp::ImportSectionReader, + ) -> Result<(), PrepareError> { + let mut new_section = wasm_encoder::ImportSection::new(); + for import in reader.clone() { + let import = import.map_err(|_| PrepareError::Deserialization)?; + if import.module != "env" { + return Err(PrepareError::Instantiate); + } + let new_type = match import.ty { + wp::TypeRef::Func(id) => { + // TODO: validate imported function types here. + self.function_limit = + self.function_limit.checked_sub(1).ok_or(PrepareError::TooManyFunctions)?; + wasm_encoder::EntityType::Function(id) + } + wp::TypeRef::Table(_) => return Err(PrepareError::Instantiate), + wp::TypeRef::Global(_) => return Err(PrepareError::Instantiate), + wp::TypeRef::Memory(_) => return Err(PrepareError::Memory), + wp::TypeRef::Tag(_) => return Err(PrepareError::Deserialization), + }; + new_section.import(import.module, import.name, new_type); + } + new_section.import("env", "memory", self.memory_import()); + // wasm_encoder a section with all imports and the imported standardized memory. + new_section.append_to(&mut self.output_code); + Ok(()) + } + + fn ensure_import_section(&mut self) { + if self.before_import_section { + self.before_import_section = false; + let mut new_section = wasm_encoder::ImportSection::new(); + new_section.import("env", "memory", self.memory_import()); + // wasm_encoder a section with all imports and the imported standardized memory. + new_section.append_to(&mut self.output_code); + } + } + + fn memory_import(&self) -> wasm_encoder::EntityType { + wasm_encoder::EntityType::Memory(wasm_encoder::MemoryType { + minimum: u64::from(self.config.limit_config.initial_memory_pages), + maximum: Some(u64::from(self.config.limit_config.max_memory_pages)), + memory64: false, + shared: false, + }) + } + + fn copy_section( + &mut self, + id: SectionId, + range: std::ops::Range, + ) -> Result<(), PrepareError> { + id.encode(&mut self.output_code); + range.len().encode(&mut self.output_code); + self.copy(range) + } + + /// Copy over the payload to the output binary without significant processing. + fn copy(&mut self, range: std::ops::Range) -> Result<(), PrepareError> { + Ok(self.output_code.extend(self.code.get(range).ok_or(PrepareError::Deserialization)?)) + } +} + +pub(crate) fn prepare_contract( + original_code: &[u8], + features: crate::features::WasmFeatures, + config: &Config, + kind: VMKind, +) -> Result, PrepareError> { + let lightly_steamed = PrepareContext::new(original_code, features, config).run()?; + + if kind == VMKind::NearVm { + // Built-in unc-vm code instruments code for itself. + return Ok(lightly_steamed); + } + + let res = finite_wasm::Analysis::new() + .with_stack(Box::new(SimpleMaxStackCfg)) + .with_gas(Box::new(SimpleGasCostCfg(u64::from(config.regular_op_cost)))) + .analyze(&lightly_steamed) + .map_err(|err| { + tracing::error!(?err, ?kind, "Analysis failed"); + PrepareError::Deserialization + })? + // Make sure contracts can’t call the instrumentation functions via `env`. + .instrument("internal", &lightly_steamed) + .map_err(|err| { + tracing::error!(?err, ?kind, "Instrumentation failed"); + PrepareError::Serialization + })?; + Ok(res) +} + +// TODO: refactor to avoid copy-paste with the ones currently defined in unc_vm_runner +struct SimpleMaxStackCfg; + +impl finite_wasm::max_stack::SizeConfig for SimpleMaxStackCfg { + fn size_of_value(&self, ty: wp::ValType) -> u8 { + use wp::ValType; + match ty { + ValType::I32 => 4, + ValType::I64 => 8, + ValType::F32 => 4, + ValType::F64 => 8, + ValType::V128 => 16, + ValType::Ref(_) => 8, + } + } + fn size_of_function_activation( + &self, + locals: &prefix_sum_vec::PrefixSumVec, + ) -> u64 { + let mut res = 64_u64; // Rough accounting for rip, rbp and some registers spilled. Not exact. + let mut last_idx_plus_one = 0_u64; + for (idx, local) in locals { + let idx = u64::from(*idx); + res = res.saturating_add( + idx.checked_sub(last_idx_plus_one) + .expect("prefix-sum-vec indices went backwards") + .saturating_add(1) + .saturating_mul(u64::from(self.size_of_value(*local))), + ); + last_idx_plus_one = idx.saturating_add(1); + } + res + } +} + +struct SimpleGasCostCfg(u64); + +macro_rules! gas_cost { + ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { + $( + fn $visit(&mut self $($(, $arg: $argty)*)?) -> u64 { + gas_cost!(@@$proposal $op self $({ $($arg: $argty),* })? => $visit) + } + )* + }; + + (@@mvp $_op:ident $_self:ident $({ $($_arg:ident: $_argty:ty),* })? => visit_block) => { + 0 + }; + (@@mvp $_op:ident $_self:ident $({ $($_arg:ident: $_argty:ty),* })? => visit_end) => { + 0 + }; + (@@mvp $_op:ident $_self:ident $({ $($_arg:ident: $_argty:ty),* })? => visit_else) => { + 0 + }; + (@@$_proposal:ident $_op:ident $self:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident) => { + $self.0 + }; +} + +impl<'a> wp::VisitOperator<'a> for SimpleGasCostCfg { + type Output = u64; + wp::for_each_operator!(gas_cost); +} + +#[cfg(test)] +mod test { + use super::VMKind; + use crate::logic::ContractPrepareVersion; + use crate::tests::test_vm_config; + + #[test] + fn v2_preparation_wasmtime_generates_valid_contract_fuzzer() { + let mut config = test_vm_config(); + let prepare_version = ContractPrepareVersion::V2; + config.limit_config.contract_prepare_version = prepare_version; + let features = crate::features::WasmFeatures::from(prepare_version); + bolero::check!().for_each(|input: &[u8]| { + // DO NOT use ArbitraryModule. We do want modules that may be invalid here, if they pass our validation step! + if let Ok(_) = crate::prepare::prepare_v1::validate_contract(input, features, &config) { + match super::prepare_contract(input, features, &config, VMKind::Wasmtime) { + Err(_e) => (), // TODO: this should be a panic, but for now it’d actually trigger + Ok(code) => { + let mut validator = wasmparser::Validator::new(); + validator.wasm_features(features.into()); + match validator.validate_all(&code) { + Ok(_) => (), + Err(e) => panic!( + "prepared code failed validation: {e:?}\ncontract: {}", + hex::encode(input), + ), + } + } + } + } + }); + } + + #[test] + fn v2_preparation_unc_vm_generates_valid_contract_fuzzer() { + let mut config = test_vm_config(); + let prepare_version = ContractPrepareVersion::V2; + config.limit_config.contract_prepare_version = prepare_version; + let features = crate::features::WasmFeatures::from(prepare_version); + + bolero::check!().for_each(|input: &[u8]| { + // DO NOT use ArbitraryModule. We do want modules that may be invalid here, if they pass our validation step! + if let Ok(_) = crate::prepare::prepare_v1::validate_contract(input, features, &config) { + match super::prepare_contract(input, features, &config, VMKind::NearVm) { + Err(_e) => (), // TODO: this should be a panic, but for now it’d actually trigger + Ok(code) => { + let mut validator = wasmparser::Validator::new(); + validator.wasm_features(features.into()); + match validator.validate_all(&code) { + Ok(_) => (), + Err(e) => panic!( + "prepared code failed validation: {e:?}\ncontract: {}", + hex::encode(input), + ), + } + } + } + } + }); + } +} diff --git a/runtime/unc-vm-runner/src/profile.rs b/runtime/unc-vm-runner/src/profile.rs new file mode 100644 index 000000000..c0f1a5a62 --- /dev/null +++ b/runtime/unc-vm-runner/src/profile.rs @@ -0,0 +1,457 @@ +pub use profile_v2::ProfileDataV2; + +use borsh::{BorshDeserialize, BorshSerialize}; +use enum_map::{enum_map, Enum, EnumMap}; +use unc_parameters::{ActionCosts, ExtCosts, ExtCostsConfig}; +use unc_primitives_core::types::{Compute, Gas}; +use std::fmt; +use strum::IntoEnumIterator; + +mod profile_v2; + +/// Profile of gas consumption. +#[derive(Clone, PartialEq, Eq)] +pub struct ProfileDataV3 { + /// Gas spent on sending or executing actions. + actions_profile: EnumMap, + /// Non-action gas spent outside the WASM VM while executing a contract. + wasm_ext_profile: EnumMap, + /// Gas spent on execution inside the WASM VM. + wasm_gas: Gas, +} + +impl Default for ProfileDataV3 { + fn default() -> ProfileDataV3 { + ProfileDataV3::new() + } +} + +impl ProfileDataV3 { + #[inline] + pub fn new() -> Self { + Self { + actions_profile: enum_map! { _ => 0 }, + wasm_ext_profile: enum_map! { _ => 0 }, + wasm_gas: 0, + } + } + + /// Test instance with unique numbers in each field. + pub fn test() -> Self { + let mut profile_data = ProfileDataV3::default(); + for (i, cost) in ExtCosts::iter().enumerate() { + profile_data.add_ext_cost(cost, i as Gas); + } + for (i, cost) in ActionCosts::iter().enumerate() { + profile_data.add_action_cost(cost, i as Gas + 1000); + } + profile_data + } + + #[inline] + pub fn merge(&mut self, other: &ProfileDataV3) { + for ((_, gas), (_, other_gas)) in + self.actions_profile.iter_mut().zip(other.actions_profile.iter()) + { + *gas = gas.saturating_add(*other_gas); + } + for ((_, gas), (_, other_gas)) in + self.wasm_ext_profile.iter_mut().zip(other.wasm_ext_profile.iter()) + { + *gas = gas.saturating_add(*other_gas); + } + self.wasm_gas = self.wasm_gas.saturating_add(other.wasm_gas); + } + + #[inline] + pub fn add_action_cost(&mut self, action: ActionCosts, value: Gas) { + self.actions_profile[action] = self.actions_profile[action].saturating_add(value); + } + + #[inline] + pub fn add_ext_cost(&mut self, ext: ExtCosts, value: Gas) { + self.wasm_ext_profile[ext] = self.wasm_ext_profile[ext].saturating_add(value); + } + + /// WasmInstruction is the only cost we don't explicitly account for. + /// Instead, we compute it at the end of contract call as the difference + /// between total gas burnt and what we've explicitly accounted for in the + /// profile. + /// + /// This is because WasmInstruction is the hottest cost and is implemented + /// with the help on the VM side, so we don't want to have profiling logic + /// there both for simplicity and efficiency reasons. + pub fn compute_wasm_instruction_cost(&mut self, total_gas_burnt: Gas) { + self.wasm_gas = + total_gas_burnt.saturating_sub(self.action_gas()).saturating_sub(self.host_gas()); + } + + pub fn get_action_cost(&self, action: ActionCosts) -> Gas { + self.actions_profile[action] + } + + pub fn get_ext_cost(&self, ext: ExtCosts) -> Gas { + self.wasm_ext_profile[ext] + } + + pub fn get_wasm_cost(&self) -> Gas { + self.wasm_gas + } + + fn host_gas(&self) -> Gas { + self.wasm_ext_profile.as_slice().iter().copied().fold(0, Gas::saturating_add) + } + + pub fn action_gas(&self) -> Gas { + self.actions_profile.as_slice().iter().copied().fold(0, Gas::saturating_add) + } + + /// Returns total compute usage of host calls. + pub fn total_compute_usage(&self, ext_costs_config: &ExtCostsConfig) -> Compute { + let ext_compute_cost = self + .wasm_ext_profile + .iter() + .map(|(key, value)| { + // Technically, gas cost might be zero while the compute cost is non-zero. To + // handle this case, we would need to explicitly count number of calls, not just + // the total gas usage. + // We don't have such costs at the moment, so this case is not implemented. + debug_assert!(key.gas(ext_costs_config) > 0 || key.compute(ext_costs_config) == 0); + + if *value == 0 { + return *value; + } + // If the `value` is non-zero, the gas cost also must be non-zero. + debug_assert!(key.gas(ext_costs_config) != 0); + ((*value as u128).saturating_mul(key.compute(ext_costs_config) as u128) + / (key.gas(ext_costs_config) as u128)) as u64 + }) + .fold(0, Compute::saturating_add); + + // We currently only support compute costs for host calls. In the future we might add + // them for actions as well. + ext_compute_cost.saturating_add(self.action_gas()).saturating_add(self.get_wasm_cost()) + } +} + +impl BorshDeserialize for ProfileDataV3 { + fn deserialize_reader(rd: &mut R) -> std::io::Result { + let actions_array: Vec = BorshDeserialize::deserialize_reader(rd)?; + let ext_array: Vec = BorshDeserialize::deserialize_reader(rd)?; + let wasm_gas: u64 = BorshDeserialize::deserialize_reader(rd)?; + + // Mapping raw arrays to enum maps. + // The enum map could be smaller or larger than the raw array. + // Extra values in the array that are unknown to the current binary will + // be ignored. Missing values are filled with 0. + let actions_profile = enum_map! { + cost => actions_array.get(borsh_action_index(cost)).copied().unwrap_or(0) + }; + let wasm_ext_profile = enum_map! { + cost => ext_array.get(borsh_ext_index(cost)).copied().unwrap_or(0) + }; + + Ok(Self { actions_profile, wasm_ext_profile, wasm_gas }) + } +} + +impl BorshSerialize for ProfileDataV3 { + fn serialize(&self, writer: &mut W) -> Result<(), std::io::Error> { + let mut actions_costs: Vec = vec![0u64; ActionCosts::LENGTH]; + for (cost, gas) in self.actions_profile.iter() { + actions_costs[borsh_action_index(cost)] = *gas; + } + BorshSerialize::serialize(&actions_costs, writer)?; + + let mut ext_costs: Vec = vec![0u64; ExtCosts::LENGTH]; + for (cost, gas) in self.wasm_ext_profile.iter() { + ext_costs[borsh_ext_index(cost)] = *gas; + } + BorshSerialize::serialize(&ext_costs, writer)?; + + let wasm_cost: u64 = self.wasm_gas; + BorshSerialize::serialize(&wasm_cost, writer) + } +} + +/// Fixed index of an action cost for borsh (de)serialization. +/// +/// We use borsh to store profiles on the DB and borsh is quite fragile with +/// changes. This mapping is to decouple the Rust enum from the borsh +/// representation. The enum can be changed freely but here in the mapping we +/// can only append more values at the end. +/// +/// TODO: Consider changing this to a different format (e.g. protobuf) because +/// canonical representation is not required here. +const fn borsh_action_index(action: ActionCosts) -> usize { + // actual indices are defined on the enum variants + action as usize +} + +/// Fixed index of an ext cost for borsh (de)serialization. +/// +/// We use borsh to store profiles on the DB and borsh is quite fragile with +/// changes. This mapping is to decouple the Rust enum from the borsh +/// representation. The enum can be changed freely but here in the mapping we +/// can only append more values at the end. +/// +/// TODO: Consider changing this to a different format (e.g. protobuf) because +/// canonical representation is not required here. +const fn borsh_ext_index(ext: ExtCosts) -> usize { + // actual indices are defined on the enum variants + ext as usize +} + +impl fmt::Debug for ProfileDataV3 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use num_rational::Ratio; + let host_gas = self.host_gas(); + let action_gas = self.action_gas(); + + writeln!(f, "------------------------------")?; + writeln!(f, "Action gas: {}", action_gas)?; + writeln!(f, "------ Host functions --------")?; + for cost in ExtCosts::iter() { + let d = self.get_ext_cost(cost); + if d != 0 { + writeln!( + f, + "{} -> {} [{}% host]", + cost, + d, + Ratio::new(d * 100, core::cmp::max(host_gas, 1)).to_integer(), + )?; + } + } + writeln!(f, "------ Actions --------")?; + for cost in ActionCosts::iter() { + let d = self.get_action_cost(cost); + if d != 0 { + writeln!(f, "{} -> {}", cost, d)?; + } + } + writeln!(f, "------------------------------")?; + Ok(()) + } +} + +/// Tests for ProfileDataV3 +#[cfg(test)] +mod test { + use super::*; + + #[test] + #[cfg(not(feature = "nightly"))] + fn test_profile_data_debug() { + let profile_data = ProfileDataV3::test(); + // we don't care about exact formatting, but the numbers should not change unexpectedly + let pretty_debug_str = format!("{profile_data:#?}"); + expect_test::expect![[r#" + ------------------------------ + Action gas: 16120 + ------ Host functions -------- + contract_loading_base -> 1 [0% host] + contract_loading_bytes -> 2 [0% host] + read_memory_base -> 3 [0% host] + read_memory_byte -> 4 [0% host] + write_memory_base -> 5 [0% host] + write_memory_byte -> 6 [0% host] + read_register_base -> 7 [0% host] + read_register_byte -> 8 [0% host] + write_register_base -> 9 [0% host] + write_register_byte -> 10 [0% host] + utf8_decoding_base -> 11 [0% host] + utf8_decoding_byte -> 12 [0% host] + utf16_decoding_base -> 13 [0% host] + utf16_decoding_byte -> 14 [0% host] + sha256_base -> 15 [0% host] + sha256_byte -> 16 [0% host] + keccak256_base -> 17 [0% host] + keccak256_byte -> 18 [0% host] + keccak512_base -> 19 [1% host] + keccak512_byte -> 20 [1% host] + ripemd160_base -> 21 [1% host] + ripemd160_block -> 22 [1% host] + ecrecover_base -> 23 [1% host] + log_base -> 24 [1% host] + log_byte -> 25 [1% host] + storage_write_base -> 26 [1% host] + storage_write_key_byte -> 27 [1% host] + storage_write_value_byte -> 28 [1% host] + storage_write_evicted_byte -> 29 [1% host] + storage_read_base -> 30 [1% host] + storage_read_key_byte -> 31 [1% host] + storage_read_value_byte -> 32 [1% host] + storage_remove_base -> 33 [1% host] + storage_remove_key_byte -> 34 [1% host] + storage_remove_ret_value_byte -> 35 [1% host] + storage_has_key_base -> 36 [1% host] + storage_has_key_byte -> 37 [2% host] + storage_iter_create_prefix_base -> 38 [2% host] + storage_iter_create_prefix_byte -> 39 [2% host] + storage_iter_create_range_base -> 40 [2% host] + storage_iter_create_from_byte -> 41 [2% host] + storage_iter_create_to_byte -> 42 [2% host] + storage_iter_next_base -> 43 [2% host] + storage_iter_next_key_byte -> 44 [2% host] + storage_iter_next_value_byte -> 45 [2% host] + touching_trie_node -> 46 [2% host] + read_cached_trie_node -> 47 [2% host] + promise_and_base -> 48 [2% host] + promise_and_per_promise -> 49 [2% host] + promise_return -> 50 [2% host] + validator_stake_base -> 51 [2% host] + validator_total_stake_base -> 52 [2% host] + alt_bn128_g1_multiexp_base -> 53 [2% host] + alt_bn128_g1_multiexp_element -> 54 [2% host] + alt_bn128_pairing_check_base -> 55 [3% host] + alt_bn128_pairing_check_element -> 56 [3% host] + alt_bn128_g1_sum_base -> 57 [3% host] + alt_bn128_g1_sum_element -> 58 [3% host] + ed25519_verify_base -> 59 [3% host] + ed25519_verify_byte -> 60 [3% host] + ------ Actions -------- + create_account -> 1000 + delete_account -> 1001 + deploy_contract_base -> 1002 + deploy_contract_byte -> 1003 + function_call_base -> 1004 + function_call_byte -> 1005 + transfer -> 1006 + stake -> 1007 + add_full_access_key -> 1008 + add_function_call_key_base -> 1009 + add_function_call_key_byte -> 1010 + delete_key -> 1011 + new_action_receipt -> 1012 + new_data_receipt_base -> 1013 + new_data_receipt_byte -> 1014 + delegate -> 1015 + ------------------------------ + "#]] + .assert_eq(&pretty_debug_str) + } + + #[test] + fn test_profile_data_debug_no_data() { + let profile_data = ProfileDataV3::default(); + // we don't care about exact formatting, but at least it should not panic + println!("{:#?}", &profile_data); + } + + #[test] + fn test_no_panic_on_overflow() { + let mut profile_data = ProfileDataV3::default(); + profile_data.add_action_cost(ActionCosts::add_full_access_key, u64::MAX); + profile_data.add_action_cost(ActionCosts::add_full_access_key, u64::MAX); + + let res = profile_data.get_action_cost(ActionCosts::add_full_access_key); + assert_eq!(res, u64::MAX); + } + + #[test] + fn test_merge() { + let mut profile_data = ProfileDataV3::default(); + profile_data.add_action_cost(ActionCosts::add_full_access_key, 111); + profile_data.add_ext_cost(ExtCosts::storage_read_base, 11); + + let mut profile_data2 = ProfileDataV3::default(); + profile_data2.add_action_cost(ActionCosts::add_full_access_key, 222); + profile_data2.add_ext_cost(ExtCosts::storage_read_base, 22); + + profile_data.merge(&profile_data2); + assert_eq!(profile_data.get_action_cost(ActionCosts::add_full_access_key), 333); + assert_eq!(profile_data.get_ext_cost(ExtCosts::storage_read_base), 33); + } + + #[test] + fn test_total_compute_usage() { + let ext_costs_config = ExtCostsConfig::test_with_undercharging_factor(3); + let mut profile_data = ProfileDataV3::default(); + profile_data.add_ext_cost( + ExtCosts::storage_read_base, + 2 * ExtCosts::storage_read_base.gas(&ext_costs_config), + ); + profile_data.add_ext_cost( + ExtCosts::storage_write_base, + 5 * ExtCosts::storage_write_base.gas(&ext_costs_config), + ); + profile_data.add_action_cost(ActionCosts::function_call_base, 100); + + assert_eq!( + profile_data.total_compute_usage(&ext_costs_config), + 3 * profile_data.host_gas() + profile_data.action_gas() + ); + } + + #[test] + fn test_borsh_ser_deser() { + let mut profile_data = ProfileDataV3::default(); + for (i, cost) in ExtCosts::iter().enumerate() { + profile_data.add_ext_cost(cost, i as Gas); + } + for (i, cost) in ActionCosts::iter().enumerate() { + profile_data.add_action_cost(cost, i as Gas + 1000); + } + let buf = borsh::to_vec(&profile_data).expect("failed serializing a normal profile"); + + let restored: ProfileDataV3 = BorshDeserialize::deserialize(&mut buf.as_slice()) + .expect("failed deserializing a normal profile"); + + assert_eq!(profile_data, restored); + } + + #[test] + fn test_borsh_incomplete_profile() { + let action_profile = vec![50u64, 60]; + let ext_profile = vec![100u64, 200]; + let wasm_cost = 99u64; + let input = manually_encode_profile_v2(action_profile, ext_profile, wasm_cost); + + let profile: ProfileDataV3 = BorshDeserialize::deserialize(&mut input.as_slice()) + .expect("should be able to parse a profile with less entries"); + + assert_eq!(50, profile.get_action_cost(ActionCosts::create_account)); + assert_eq!(60, profile.get_action_cost(ActionCosts::delete_account)); + assert_eq!(0, profile.get_action_cost(ActionCosts::deploy_contract_base)); + + assert_eq!(100, profile.get_ext_cost(ExtCosts::base)); + assert_eq!(200, profile.get_ext_cost(ExtCosts::contract_loading_base)); + assert_eq!(0, profile.get_ext_cost(ExtCosts::contract_loading_bytes)); + } + + #[test] + fn test_borsh_larger_profile_than_current() { + let action_profile = vec![1234u64; ActionCosts::LENGTH + 5]; + let ext_profile = vec![5678u64; ExtCosts::LENGTH + 10]; + let wasm_cost = 90u64; + let input = manually_encode_profile_v2(action_profile, ext_profile, wasm_cost); + + let profile: ProfileDataV3 = BorshDeserialize::deserialize(&mut input.as_slice()).expect( + "should be able to parse a profile with more entries than the current version has", + ); + + for action in ActionCosts::iter() { + assert_eq!(1234, profile.get_action_cost(action), "{action:?}"); + } + + for ext in ExtCosts::iter() { + assert_eq!(5678, profile.get_ext_cost(ext), "{ext:?}"); + } + + assert_eq!(90, profile.wasm_gas); + } + + #[track_caller] + fn manually_encode_profile_v2( + action_profile: Vec, + ext_profile: Vec, + wasm_cost: u64, + ) -> Vec { + let mut input = vec![]; + BorshSerialize::serialize(&action_profile, &mut input).unwrap(); + BorshSerialize::serialize(&ext_profile, &mut input).unwrap(); + BorshSerialize::serialize(&wasm_cost, &mut input).unwrap(); + input + } +} diff --git a/runtime/unc-vm-runner/src/profile/profile_v2.rs b/runtime/unc-vm-runner/src/profile/profile_v2.rs new file mode 100644 index 000000000..dc0fea551 --- /dev/null +++ b/runtime/unc-vm-runner/src/profile/profile_v2.rs @@ -0,0 +1,341 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_parameters::{ActionCosts, ExtCosts}; +use unc_primitives_core::types::Gas; +use std::fmt; +use std::ops::Index; +use strum::IntoEnumIterator; + +/// Deprecated serialization format to store profiles in the database. +/// +/// There is no ProfileDataV1 because meta data V1 did no have profiles. +/// Counting thus starts with 2 to match the meta data version numbers. +/// +/// This is not part of the protocol but archival nodes still rely on this not +/// changing to answer old tx-status requests with a gas profile. +/// +/// It used to store an array that manually mapped `enum Cost` to gas +/// numbers. Now `ProfileDataV2` and `Cost` are deprecated. But to lookup +/// old gas profiles from the DB, we need to keep the code around. +#[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct ProfileDataV2 { + data: DataArray, +} + +#[derive(Clone, PartialEq, Eq)] +struct DataArray(Box<[u64; Self::LEN]>); + +impl DataArray { + const LEN: usize = 72; +} + +impl ProfileDataV2 { + pub fn get_ext_cost(&self, ext: ExtCosts) -> u64 { + self[ext] + } + + pub fn get_wasm_cost(&self) -> u64 { + // ProfileV2Cost::WasmInstruction => 62, + self.data[62] + } + + fn host_gas(&self) -> u64 { + ExtCosts::iter().map(|a| self.get_ext_cost(a)).fold(0, u64::saturating_add) + } + + /// List action cost in the old way, which conflated several action parameters into one. + /// + /// This is used to display old gas profiles on the RPC API and in debug output. + pub fn legacy_action_costs(&self) -> Vec<(&'static str, Gas)> { + vec![ + ("CREATE_ACCOUNT", self.data[0]), + ("DELETE_ACCOUNT", self.data[1]), + ("DEPLOY_CONTRACT", self.data[2]), // contains per byte and base cost + ("FUNCTION_CALL", self.data[3]), // contains per byte and base cost + ("TRANSFER", self.data[4]), + ("STAKE", self.data[5]), + ("ADD_KEY", self.data[6]), // contains base + per byte cost for function call keys and full access keys + ("DELETE_KEY", self.data[7]), + ("NEW_DATA_RECEIPT_BYTE", self.data[8]), // contains the per-byte cost for sending back a data dependency + ("NEW_RECEIPT", self.data[9]), // contains base cost for data receipts and action receipts + ] + } + + pub fn action_gas(&self) -> u64 { + self.legacy_action_costs().iter().map(|(_name, cost)| *cost).fold(0, u64::saturating_add) + } + + /// Test instance with unique numbers in each field. + pub fn test() -> Self { + let mut profile_data = ProfileDataV2::default(); + let num_legacy_actions = 10; + for i in 0..num_legacy_actions { + profile_data.data.0[i] = i as Gas + 1000; + } + for i in num_legacy_actions..DataArray::LEN { + profile_data.data.0[i] = (i - num_legacy_actions) as Gas; + } + profile_data + } +} + +impl fmt::Debug for ProfileDataV2 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use num_rational::Ratio; + let host_gas = self.host_gas(); + let action_gas = self.action_gas(); + + writeln!(f, "------------------------------")?; + writeln!(f, "Action gas: {}", action_gas)?; + writeln!(f, "------ Host functions --------")?; + for cost in ExtCosts::iter() { + let d = self.get_ext_cost(cost); + if d != 0 { + writeln!( + f, + "{} -> {} [{}% host]", + cost, + d, + Ratio::new(d * 100, core::cmp::max(host_gas, 1)).to_integer(), + )?; + } + } + writeln!(f, "------ Actions --------")?; + for (cost, gas) in self.legacy_action_costs() { + if gas != 0 { + writeln!(f, "{} -> {}", cost.to_ascii_lowercase(), gas)?; + } + } + writeln!(f, "------------------------------")?; + Ok(()) + } +} + +impl Index for DataArray { + type Output = u64; + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} + +impl Index for ProfileDataV2 { + type Output = u64; + + fn index(&self, cost: ActionCosts) -> &Self::Output { + let index = match cost { + ActionCosts::create_account => 0, + ActionCosts::delete_account => 1, + ActionCosts::deploy_contract_base => 2, + ActionCosts::deploy_contract_byte => 2, + ActionCosts::function_call_base => 3, + ActionCosts::function_call_byte => 3, + ActionCosts::transfer => 4, + ActionCosts::stake => 5, + ActionCosts::add_full_access_key => 6, + ActionCosts::add_function_call_key_base => 6, + ActionCosts::add_function_call_key_byte => 6, + ActionCosts::delete_key => 7, + ActionCosts::new_data_receipt_byte => 8, + ActionCosts::new_action_receipt => 9, + ActionCosts::new_data_receipt_base => 9, + // new costs added after profile v1 was deprecated don't have this entry + #[allow(unreachable_patterns)] + _ => return &0, + }; + &self.data[index] + } +} + +impl Index for ProfileDataV2 { + type Output = u64; + + fn index(&self, cost: ExtCosts) -> &Self::Output { + let index = match cost { + ExtCosts::base => 10, + ExtCosts::contract_loading_base => 11, + ExtCosts::contract_loading_bytes => 12, + ExtCosts::read_memory_base => 13, + ExtCosts::read_memory_byte => 14, + ExtCosts::write_memory_base => 15, + ExtCosts::write_memory_byte => 16, + ExtCosts::read_register_base => 17, + ExtCosts::read_register_byte => 18, + ExtCosts::write_register_base => 19, + ExtCosts::write_register_byte => 20, + ExtCosts::utf8_decoding_base => 21, + ExtCosts::utf8_decoding_byte => 22, + ExtCosts::utf16_decoding_base => 23, + ExtCosts::utf16_decoding_byte => 24, + ExtCosts::sha256_base => 25, + ExtCosts::sha256_byte => 26, + ExtCosts::keccak256_base => 27, + ExtCosts::keccak256_byte => 28, + ExtCosts::keccak512_base => 29, + ExtCosts::keccak512_byte => 30, + ExtCosts::ripemd160_base => 31, + ExtCosts::ripemd160_block => 32, + ExtCosts::ecrecover_base => 33, + ExtCosts::log_base => 34, + ExtCosts::log_byte => 35, + ExtCosts::storage_write_base => 36, + ExtCosts::storage_write_key_byte => 37, + ExtCosts::storage_write_value_byte => 38, + ExtCosts::storage_write_evicted_byte => 39, + ExtCosts::storage_read_base => 40, + ExtCosts::storage_read_key_byte => 41, + ExtCosts::storage_read_value_byte => 42, + ExtCosts::storage_remove_base => 43, + ExtCosts::storage_remove_key_byte => 44, + ExtCosts::storage_remove_ret_value_byte => 45, + ExtCosts::storage_has_key_base => 46, + ExtCosts::storage_has_key_byte => 47, + ExtCosts::storage_iter_create_prefix_base => 48, + ExtCosts::storage_iter_create_prefix_byte => 49, + ExtCosts::storage_iter_create_range_base => 50, + ExtCosts::storage_iter_create_from_byte => 51, + ExtCosts::storage_iter_create_to_byte => 52, + ExtCosts::storage_iter_next_base => 53, + ExtCosts::storage_iter_next_key_byte => 54, + ExtCosts::storage_iter_next_value_byte => 55, + ExtCosts::touching_trie_node => 56, + ExtCosts::promise_and_base => 57, + ExtCosts::promise_and_per_promise => 58, + ExtCosts::promise_return => 59, + ExtCosts::validator_frozen_base => 60, + ExtCosts::validator_total_frozen_base => 61, + ExtCosts::read_cached_trie_node => 63, + ExtCosts::alt_bn128_g1_multiexp_base => 64, + ExtCosts::alt_bn128_g1_multiexp_element => 65, + ExtCosts::alt_bn128_pairing_check_base => 66, + ExtCosts::alt_bn128_pairing_check_element => 67, + ExtCosts::alt_bn128_g1_sum_base => 68, + ExtCosts::alt_bn128_g1_sum_element => 69, + ExtCosts::validator_power_base => 60, + ExtCosts::validator_total_power_base => 61, + // new costs added after profile v1 was deprecated don't have this entry + #[allow(unreachable_patterns)] + _ => return &0, + }; + &self.data[index] + } +} + +impl BorshDeserialize for DataArray { + fn deserialize_reader(rd: &mut R) -> std::io::Result { + let data_vec: Vec = BorshDeserialize::deserialize_reader(rd)?; + let mut data_array = [0; Self::LEN]; + let len = Self::LEN.min(data_vec.len()); + data_array[0..len].copy_from_slice(&data_vec[0..len]); + Ok(Self(Box::new(data_array))) + } +} + +impl BorshSerialize for DataArray { + fn serialize(&self, writer: &mut W) -> Result<(), std::io::Error> { + (&self.0[..]).serialize(writer) + } +} + +impl Default for ProfileDataV2 { + fn default() -> Self { + let costs = DataArray(Box::new([0; DataArray::LEN])); + ProfileDataV2 { data: costs } + } +} + +/// Tests for ProfileDataV2 +#[cfg(test)] +mod test { + use super::*; + + #[test] + #[cfg(not(feature = "nightly"))] + fn test_profile_data_debug() { + let profile_data = ProfileDataV2::test(); + // we don't care about exact formatting, but the numbers should not change unexpectedly + let pretty_debug_str = format!("{profile_data:#?}"); + expect_test::expect![[r#" + ------------------------------ + Action gas: 10045 + ------ Host functions -------- + contract_loading_base -> 1 [0% host] + contract_loading_bytes -> 2 [0% host] + read_memory_base -> 3 [0% host] + read_memory_byte -> 4 [0% host] + write_memory_base -> 5 [0% host] + write_memory_byte -> 6 [0% host] + read_register_base -> 7 [0% host] + read_register_byte -> 8 [0% host] + write_register_base -> 9 [0% host] + write_register_byte -> 10 [0% host] + utf8_decoding_base -> 11 [0% host] + utf8_decoding_byte -> 12 [0% host] + utf16_decoding_base -> 13 [0% host] + utf16_decoding_byte -> 14 [0% host] + sha256_base -> 15 [0% host] + sha256_byte -> 16 [0% host] + keccak256_base -> 17 [0% host] + keccak256_byte -> 18 [1% host] + keccak512_base -> 19 [1% host] + keccak512_byte -> 20 [1% host] + ripemd160_base -> 21 [1% host] + ripemd160_block -> 22 [1% host] + ecrecover_base -> 23 [1% host] + log_base -> 24 [1% host] + log_byte -> 25 [1% host] + storage_write_base -> 26 [1% host] + storage_write_key_byte -> 27 [1% host] + storage_write_value_byte -> 28 [1% host] + storage_write_evicted_byte -> 29 [1% host] + storage_read_base -> 30 [1% host] + storage_read_key_byte -> 31 [1% host] + storage_read_value_byte -> 32 [1% host] + storage_remove_base -> 33 [1% host] + storage_remove_key_byte -> 34 [1% host] + storage_remove_ret_value_byte -> 35 [2% host] + storage_has_key_base -> 36 [2% host] + storage_has_key_byte -> 37 [2% host] + storage_iter_create_prefix_base -> 38 [2% host] + storage_iter_create_prefix_byte -> 39 [2% host] + storage_iter_create_range_base -> 40 [2% host] + storage_iter_create_from_byte -> 41 [2% host] + storage_iter_create_to_byte -> 42 [2% host] + storage_iter_next_base -> 43 [2% host] + storage_iter_next_key_byte -> 44 [2% host] + storage_iter_next_value_byte -> 45 [2% host] + touching_trie_node -> 46 [2% host] + read_cached_trie_node -> 53 [3% host] + promise_and_base -> 47 [2% host] + promise_and_per_promise -> 48 [2% host] + promise_return -> 49 [2% host] + validator_stake_base -> 50 [2% host] + validator_total_stake_base -> 51 [2% host] + alt_bn128_g1_multiexp_base -> 54 [3% host] + alt_bn128_g1_multiexp_element -> 55 [3% host] + alt_bn128_pairing_check_base -> 56 [3% host] + alt_bn128_pairing_check_element -> 57 [3% host] + alt_bn128_g1_sum_base -> 58 [3% host] + alt_bn128_g1_sum_element -> 59 [3% host] + ------ Actions -------- + create_account -> 1000 + delete_account -> 1001 + deploy_contract -> 1002 + function_call -> 1003 + transfer -> 1004 + stake -> 1005 + add_key -> 1006 + delete_key -> 1007 + new_data_receipt_byte -> 1008 + new_receipt -> 1009 + ------------------------------ + "#]] + .assert_eq(&pretty_debug_str) + } + + #[test] + fn test_profile_data_debug_no_data() { + let profile_data = ProfileDataV2::default(); + // we don't care about exact formatting, but at least it should not panic + println!("{:#?}", &profile_data); + } +} diff --git a/runtime/unc-vm-runner/src/runner.rs b/runtime/unc-vm-runner/src/runner.rs new file mode 100644 index 000000000..4eef13e3b --- /dev/null +++ b/runtime/unc-vm-runner/src/runner.rs @@ -0,0 +1,136 @@ +use crate::errors::ContractPrecompilatonResult; +use crate::logic::errors::{CacheError, CompilationError, VMRunnerError}; +use crate::logic::types::PromiseResult; +use crate::logic::{CompiledContractCache, External, VMContext, VMOutcome}; +use crate::ContractCode; +use unc_parameters::vm::{Config, VMKind}; +use unc_parameters::RuntimeFeesConfig; + +/// Returned by VM::run method. +/// +/// `VMRunnerError` means framework is buggy or the data base has been corrupted. +/// We are unable to produce a deterministic result. The correct action usually +/// is to crash or maybe ban a peer and/or send a challenge. +/// +/// A `VMOutcome` is a graceful completion of a VM execution. It can also contain +/// an guest error message in the `aborted` field. But these are not errors in +/// the real sense, those are just reasons why execution failed at some point. +/// Such as when a smart contract code panics. +/// Note that the fact that `VMOutcome` contains is tracked on the blockchain. +/// All validators must produce an error deterministically or all should succeed. +/// (See also `PartialExecutionStatus`.) +/// Similarly, the gas values on `VMOutcome` must be the exact same on all +/// validators, even when a guest error occurs, or else their state will diverge. +pub(crate) type VMResult = Result; + +/// Validate and run the specified contract. +/// +/// This is the entry point for executing a NEAR protocol contract. Before the +/// entry point (as specified by the `method_name` argument) of the contract +/// code is executed, the contract will be validated (see +/// [`crate::prepare::prepare_contract`]), instrumented (e.g. for gas +/// accounting), and linked with the externs specified via the `ext` argument. +/// +/// [`VMContext::input`] will be passed to the contract entrypoint as an +/// argument. +/// +/// The contract will be executed with the default VM implementation for the +/// current protocol version. +/// +/// The gas cost for contract preparation will be subtracted by the VM +/// implementation. +pub fn run( + code: &ContractCode, + method_name: &str, + ext: &mut dyn External, + context: VMContext, + wasm_config: &Config, + fees_config: &RuntimeFeesConfig, + promise_results: &[PromiseResult], + cache: Option<&dyn CompiledContractCache>, +) -> VMResult { + let vm_kind = wasm_config.vm_kind; + let span = tracing::debug_span!( + target: "vm", + "run", + "code.len" = code.code().len(), + %method_name, + ?vm_kind, + burnt_gas = tracing::field::Empty, + ) + .entered(); + + let runtime = vm_kind + .runtime(wasm_config.clone()) + .unwrap_or_else(|| panic!("the {vm_kind:?} runtime has not been enabled at compile time")); + + let outcome = + runtime.run(code, method_name, ext, context, fees_config, promise_results, cache)?; + + span.record("burnt_gas", &outcome.burnt_gas); + Ok(outcome) +} + +pub trait VM { + /// Validate and run the specified contract. + /// + /// This is the entry point for executing a NEAR protocol contract. Before + /// the entry point (as specified by the `method_name` argument) of the + /// contract code is executed, the contract will be validated (see + /// [`crate::prepare::prepare_contract`]), instrumented (e.g. for gas + /// accounting), and linked with the externs specified via the `ext` + /// argument. + /// + /// [`VMContext::input`] will be passed to the contract entrypoint as an + /// argument. + /// + /// The gas cost for contract preparation will be subtracted by the VM + /// implementation. + fn run( + &self, + code: &ContractCode, + method_name: &str, + ext: &mut dyn External, + context: VMContext, + fees_config: &RuntimeFeesConfig, + promise_results: &[PromiseResult], + cache: Option<&dyn CompiledContractCache>, + ) -> VMResult; + + /// Precompile a WASM contract to a VM specific format and store the result + /// into the `cache`. + /// + /// Further calls to [`Self::run`] or [`Self::precompile`] with the same + /// `code`, `cache` and [`Config`] may reuse the results of this + /// precompilation step. + fn precompile( + &self, + code: &ContractCode, + cache: &dyn CompiledContractCache, + ) -> Result, CacheError>; +} + +pub trait VMKindExt { + /// Make a [`VM`] for this [`VMKind`]. + /// + /// This is not intended to be used by code other than internal tools like + /// the estimator. + fn runtime(&self, config: Config) -> Option>; +} + +impl VMKindExt for VMKind { + fn runtime(&self, config: Config) -> Option> { + match self { + #[cfg(all(feature = "wasmer0_vm", target_arch = "x86_64"))] + Self::Wasmer0 => Some(Box::new(crate::wasmer_runner::Wasmer0VM::new(config))), + #[cfg(feature = "wasmtime_vm")] + Self::Wasmtime => Some(Box::new(crate::wasmtime_runner::WasmtimeVM::new(config))), + #[cfg(all(feature = "wasmer2_vm", target_arch = "x86_64"))] + Self::Wasmer2 => Some(Box::new(crate::wasmer2_runner::Wasmer2VM::new(config))), + #[cfg(all(feature = "unc_vm", target_arch = "x86_64"))] + Self::NearVm => Some(Box::new(crate::unc_vm_runner::NearVM::new(config))), + #[allow(unreachable_patterns)] // reachable when some of the VMs are disabled. + _ => None, + } + } +} diff --git a/runtime/unc-vm-runner/src/tests.rs b/runtime/unc-vm-runner/src/tests.rs new file mode 100644 index 000000000..09fcbccbb --- /dev/null +++ b/runtime/unc-vm-runner/src/tests.rs @@ -0,0 +1,69 @@ +mod cache; +mod compile_errors; +mod fuzzers; +mod regression_tests; +mod rs_contract; +mod runtime_errors; +pub(crate) mod test_builder; +mod ts_contract; +mod wasm_validation; + +use crate::logic::VMContext; +use unc_parameters::vm::VMKind; +use unc_parameters::RuntimeConfigStore; +use unc_primitives_core::version::PROTOCOL_VERSION; + +const CURRENT_ACCOUNT_ID: &str = "alice"; +const SIGNER_ACCOUNT_ID: &str = "bob"; +const SIGNER_ACCOUNT_PK: [u8; 3] = [0, 1, 2]; +const PREDECESSOR_ACCOUNT_ID: &str = "carol"; + +pub(crate) fn test_vm_config() -> unc_parameters::vm::Config { + let store = RuntimeConfigStore::test(); + let config = store.get_config(PROTOCOL_VERSION).wasm_config.clone(); + unc_parameters::vm::Config { + vm_kind: config.vm_kind.replace_with_wasmtime_if_unsupported(), + ..config + } +} + +pub(crate) fn with_vm_variants( + #[allow(unused)] cfg: &unc_parameters::vm::Config, + runner: impl Fn(VMKind) -> (), +) { + #[cfg(all(feature = "wasmer0_vm", target_arch = "x86_64"))] + runner(VMKind::Wasmer0); + + #[cfg(feature = "wasmtime_vm")] + runner(VMKind::Wasmtime); + + #[cfg(all(feature = "wasmer2_vm", target_arch = "x86_64"))] + runner(VMKind::Wasmer2); + + #[cfg(all(feature = "unc_vm", target_arch = "x86_64"))] + if cfg.limit_config.contract_prepare_version == unc_parameters::vm::ContractPrepareVersion::V2 + { + runner(VMKind::NearVm); + } +} + +fn create_context(input: Vec) -> VMContext { + VMContext { + current_account_id: CURRENT_ACCOUNT_ID.parse().unwrap(), + signer_account_id: SIGNER_ACCOUNT_ID.parse().unwrap(), + signer_account_pk: Vec::from(&SIGNER_ACCOUNT_PK[..]), + predecessor_account_id: PREDECESSOR_ACCOUNT_ID.parse().unwrap(), + input, + block_height: 10, + block_timestamp: 42, + epoch_height: 1, + account_balance: 2u128, + account_locked_balance: 0, + storage_usage: 12, + attached_deposit: 2u128, + prepaid_gas: 10_u64.pow(14), + random_seed: vec![0, 1, 2], + view_config: None, + output_data_receivers: vec![], + } +} diff --git a/runtime/unc-vm-runner/src/tests/__fuzz__/tests__fuzzers__wasmer2_is_reproducible/crashes/crash-7efd70f50949e0fdff8f4c8d6cc66709faf6d3cf b/runtime/unc-vm-runner/src/tests/__fuzz__/tests__fuzzers__wasmer2_is_reproducible/crashes/crash-7efd70f50949e0fdff8f4c8d6cc66709faf6d3cf new file mode 100644 index 000000000..6caad5037 Binary files /dev/null and b/runtime/unc-vm-runner/src/tests/__fuzz__/tests__fuzzers__wasmer2_is_reproducible/crashes/crash-7efd70f50949e0fdff8f4c8d6cc66709faf6d3cf differ diff --git a/runtime/unc-vm-runner/src/tests/cache.rs b/runtime/unc-vm-runner/src/tests/cache.rs new file mode 100644 index 000000000..69c16693e --- /dev/null +++ b/runtime/unc-vm-runner/src/tests/cache.rs @@ -0,0 +1,277 @@ +//! Tests that `CompiledContractCache` is working correctly. Currently testing only wasmer code, so disabled outside of x86_64 +#![cfg(target_arch = "x86_64")] + +use super::{create_context, test_vm_config, with_vm_variants}; +use crate::logic::errors::VMRunnerError; +use crate::logic::mocks::mock_external::MockedExternal; +use crate::logic::Config; +use crate::logic::{CompiledContract, CompiledContractCache}; +use crate::runner::VMKindExt; +use crate::runner::VMResult; +use crate::wasmer2_runner::Wasmer2VM; +use crate::ContractCode; +use crate::{prepare, MockCompiledContractCache}; +use assert_matches::assert_matches; +use unc_parameters::vm::VMKind; +use unc_parameters::RuntimeFeesConfig; +use unc_primitives_core::hash::CryptoHash; +use std::io; +use std::sync::atomic::{AtomicBool, Ordering}; +use wasmer_compiler::{CpuFeature, Target}; +use wasmer_engine::Executable; + +#[test] +fn test_caches_compilation_error() { + let config = test_vm_config(); + with_vm_variants(&config, |vm_kind: VMKind| { + match vm_kind { + VMKind::Wasmer0 | VMKind::Wasmer2 | VMKind::NearVm => {} + VMKind::Wasmtime => return, + } + let cache = MockCompiledContractCache::default(); + let code = [42; 1000]; + let terragas = 1000000000000u64; + assert_eq!(cache.len(), 0); + let outcome1 = + make_cached_contract_call_vm(&config, &cache, &code, "method_name1", terragas, vm_kind) + .expect("bad failure"); + println!("{:?}", cache); + assert_eq!(cache.len(), 1); + let outcome2 = + make_cached_contract_call_vm(&config, &cache, &code, "method_name2", terragas, vm_kind) + .expect("bad failure"); + assert_eq!(outcome1.aborted.as_ref(), outcome2.aborted.as_ref()); + }) +} + +#[test] +fn test_does_not_cache_io_error() { + let config = test_vm_config(); + with_vm_variants(&config, |vm_kind: VMKind| { + match vm_kind { + VMKind::Wasmer0 | VMKind::Wasmer2 | VMKind::NearVm => {} + VMKind::Wasmtime => return, + } + + let code = unc_test_contracts::trivial_contract(); + let prepaid_gas = 10u64.pow(12); + let mut cache = FaultingCompiledContractCache::default(); + + cache.set_read_fault(true); + let result = + make_cached_contract_call_vm(&config, &cache, &code, "main", prepaid_gas, vm_kind); + assert_matches!( + result.err(), + Some(VMRunnerError::CacheError(crate::logic::errors::CacheError::ReadError(_))) + ); + + cache.set_write_fault(true); + let result = + make_cached_contract_call_vm(&config, &cache, &code, "main", prepaid_gas, vm_kind); + assert_matches!( + result.err(), + Some(VMRunnerError::CacheError(crate::logic::errors::CacheError::WriteError(_))) + ); + }) +} + +fn make_cached_contract_call_vm( + config: &Config, + cache: &dyn CompiledContractCache, + code: &[u8], + method_name: &str, + prepaid_gas: u64, + vm_kind: VMKind, +) -> VMResult { + let mut fake_external = MockedExternal::new(); + let mut context = create_context(vec![]); + let fees = RuntimeFeesConfig::test(); + let promise_results = vec![]; + context.prepaid_gas = prepaid_gas; + let code = ContractCode::new(code.to_vec(), None); + let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); + runtime.run( + &code, + method_name, + &mut fake_external, + context, + &fees, + &promise_results, + Some(cache), + ) +} + +#[test] +fn test_wasmer2_artifact_output_stability() { + // If this test has failed, you want to adjust the necessary constants so that `cache::vm_hash` + // changes (and only then the hashes here). + // + // Note that this test is a best-effort fish net. Some changes that should modify the hash will + // fall through the cracks here, but hopefully it should catch most of the fish just fine. + let seeds = [2, 3, 5, 7, 11, 13, 17]; + let prepared_hashes = [ + 11313378614122864359, + 15129741658507107429, + 6663551338814735895, + 17573418911223110359, + 3130574445877428311, + 11574598916196339098, + 10719493536745069553, + ]; + let mut got_prepared_hashes = Vec::with_capacity(seeds.len()); + let compiled_hashes = [ + 10064221885882795403, + 3125775751094251057, + 5419113076376709775, + 11996380560209519923, + 5262356478082097591, + 15002713309850850128, + 17666356303775050986, + ]; + let mut got_compiled_hashes = Vec::with_capacity(seeds.len()); + for seed in seeds { + let contract = ContractCode::new(unc_test_contracts::arbitrary_contract(seed), None); + + let config = test_vm_config(); + let prepared_code = + prepare::prepare_contract(contract.code(), &config, VMKind::Wasmer2).unwrap(); + got_prepared_hashes.push(crate::utils::stable_hash((&contract.code(), &prepared_code))); + + let mut features = CpuFeature::set(); + features.insert(CpuFeature::AVX); + let triple = "x86_64-unknown-linux-gnu".parse().unwrap(); + let target = Target::new(triple, features); + let vm = Wasmer2VM::new_for_target(config, target); + let artifact = vm.compile_uncached(&contract).unwrap(); + let serialized = artifact.serialize().unwrap(); + let this_hash = crate::utils::stable_hash(&serialized); + got_compiled_hashes.push(this_hash); + + std::fs::write(format!("/tmp/artifact{}", this_hash), serialized).unwrap(); + } + // These asserts have failed as a result of some change and the following text describes what + // the implications of the change. + // + // May need a protocol version change, and definitely wants a `WASMER2_CONFIG version update + // too, as below. Maybe something else too. + assert!( + got_prepared_hashes == prepared_hashes, + "contract preparation hashes have changed to {:#?}", + got_prepared_hashes + ); + // In this case you will need to adjust the WASMER2_CONFIG version so that the cached contracts + // are evicted from the contract cache. + assert!( + got_compiled_hashes == compiled_hashes, + "VM output hashes have changed to {:#?}", + got_compiled_hashes + ); + // Once it has been confirmed that these steps have been done, the expected hashes in this test + // can be adjusted. +} + +#[test] +fn test_unc_vm_artifact_output_stability() { + use crate::unc_vm_runner::NearVM; + use unc_vm_compiler::{CpuFeature, Target}; + // If this test has failed, you want to adjust the necessary constants so that `cache::vm_hash` + // changes (and only then the hashes here). + // + // Note that this test is a best-effort fish net. Some changes that should modify the hash will + // fall through the cracks here, but hopefully it should catch most of the fish just fine. + let seeds = [2, 3, 5, 7, 11, 13, 17]; + let prepared_hashes = [ + 15237011375120738807, + 3750594434467176559, + 2196541628148102482, + 1576495094908614397, + 6394387219699970793, + 18132026143745992229, + 4095228008100475322, + ]; + let mut got_prepared_hashes = Vec::with_capacity(seeds.len()); + let compiled_hashes = [ + 4853457605418485197, + 13732980080772388685, + 5341774541420947021, + 11161633624742571232, + 12949634280637067071, + 6571507299571270433, + 2426595065881413005, + ]; + let mut got_compiled_hashes = Vec::with_capacity(seeds.len()); + for seed in seeds { + let contract = ContractCode::new(unc_test_contracts::arbitrary_contract(seed), None); + + let config = test_vm_config(); + let prepared_code = + prepare::prepare_contract(contract.code(), &config, VMKind::NearVm).unwrap(); + got_prepared_hashes.push(crate::utils::stable_hash((&contract.code(), &prepared_code))); + + let mut features = CpuFeature::set(); + features.insert(CpuFeature::AVX); + let triple = "x86_64-unknown-linux-gnu".parse().unwrap(); + let target = Target::new(triple, features); + let vm = NearVM::new_for_target(config, target); + let artifact = vm.compile_uncached(&contract).unwrap(); + let serialized = artifact.serialize().unwrap(); + let this_hash = crate::utils::stable_hash(&serialized); + got_compiled_hashes.push(this_hash); + + std::fs::write(format!("/tmp/artifact{}", this_hash), serialized).unwrap(); + } + // These asserts have failed as a result of some change and the following text describes what + // the implications of the change. + // + // May need a protocol version change, and definitely wants a `WASMER2_CONFIG version update + // too, as below. Maybe something else too. + assert!( + got_prepared_hashes == prepared_hashes, + "contract preparation hashes have changed to {:#?}", + got_prepared_hashes + ); + // In this case you will need to adjust the WASMER2_CONFIG version so that the cached contracts + // are evicted from the contract cache. + assert!( + got_compiled_hashes == compiled_hashes, + "VM output hashes have changed to {:#?}", + got_compiled_hashes + ); + // Once it has been confirmed that these steps have been done, the expected hashes in this test + // can be adjusted. +} + +/// [`CompiledContractCache`] which simulates failures in the underlying +/// database. +#[derive(Default)] +struct FaultingCompiledContractCache { + read_fault: AtomicBool, + write_fault: AtomicBool, + inner: MockCompiledContractCache, +} + +impl FaultingCompiledContractCache { + fn set_read_fault(&mut self, yes: bool) { + *self.read_fault.get_mut() = yes; + } + + fn set_write_fault(&mut self, yes: bool) { + *self.write_fault.get_mut() = yes; + } +} + +impl CompiledContractCache for FaultingCompiledContractCache { + fn put(&self, key: &CryptoHash, value: CompiledContract) -> std::io::Result<()> { + if self.write_fault.swap(false, Ordering::Relaxed) { + return Err(io::ErrorKind::Other.into()); + } + self.inner.put(key, value) + } + + fn get(&self, key: &CryptoHash) -> std::io::Result> { + if self.read_fault.swap(false, Ordering::Relaxed) { + return Err(io::ErrorKind::Other.into()); + } + self.inner.get(key) + } +} diff --git a/runtime/unc-vm-runner/src/tests/compile_errors.rs b/runtime/unc-vm-runner/src/tests/compile_errors.rs new file mode 100644 index 000000000..29b308037 --- /dev/null +++ b/runtime/unc-vm-runner/src/tests/compile_errors.rs @@ -0,0 +1,474 @@ +use super::test_builder::test_builder; +use expect_test::expect; +use unc_primitives_core::version::ProtocolFeature; + +const FIX_CONTRACT_LOADING_COST: u32 = 129; + +#[test] +fn test_initializer_wrong_signature_contract() { + test_builder() + .wat( + r#" +(module + (func $f (param i32)) + (start 0) + (func (export "main")) +)"#, + ) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Error happened while deserializing the module. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 48017463 used gas 48017463 + Err: PrepareError: Error happened while deserializing the module. + "#]], + ]); +} + +#[test] +/// StackHeightInstrumentation is weird but it's what we return for now +fn test_function_not_defined_contract() { + test_builder() + .wat(r#"(module (export "hello" (func 0)))"#) + .method("hello") + .protocol_version( + FIX_CONTRACT_LOADING_COST + ) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Error happened while deserializing the module. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 39564213 used gas 39564213 + Err: PrepareError: Error happened while deserializing the module. + "#]], + ]); +} + +fn function_type_not_defined_contract(bad_type: u64) -> Vec { + wat::parse_str(&format!( + r#" + (module + (func $main (type {bad_type})) + (export "main" (func $main)) + )"#, + )) + .unwrap() +} + +#[test] +fn test_function_type_not_defined_contract_1() { + test_builder() + .wasm(&function_type_not_defined_contract(1)) + .protocol_version( + FIX_CONTRACT_LOADING_COST + ) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Error happened while deserializing the module. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 44982963 used gas 44982963 + Err: PrepareError: Error happened while deserializing the module. + "#]], + ]); +} + +#[test] +// Weird case. It's not valid wasm (wat2wasm validate will fail), but wasmer allows it. +fn test_function_type_not_defined_contract_2() { + test_builder() + .wasm(&function_type_not_defined_contract(0)) + .protocol_version( + FIX_CONTRACT_LOADING_COST + ) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Error happened while deserializing the module. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 44982963 used gas 44982963 + Err: PrepareError: Error happened while deserializing the module. + "#]], + ]); +} + +#[test] +fn test_garbage_contract() { + test_builder() + .wasm(&[]) + .protocol_version(FIX_CONTRACT_LOADING_COST + ) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Error happened while deserializing the module. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 35445963 used gas 35445963 + Err: PrepareError: Error happened while deserializing the module. + "#]], + ]); +} + +#[test] +fn test_evil_function_index() { + test_builder() + .wat(r#"(module (func (export "main") call 4294967295))"#) + .method("abort_with_zero") + .protocol_version( + FIX_CONTRACT_LOADING_COST + ) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Error happened while deserializing the module. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 44115963 used gas 44115963 + Err: PrepareError: Error happened while deserializing the module. + "#]], + ]); +} + +#[test] +fn test_limit_contract_functions_number() { + let functions_number_limit: u32 = 10_000; + + test_builder().wasm( + &unc_test_contracts::LargeContract { + functions: functions_number_limit, + ..Default::default() + } + .make(), + ) + .protocol_features(&[ + ProtocolFeature::LimitContractFunctionsNumber, + ProtocolFeature::PreparationV2, + ]) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13048032213 used gas 13048032213 + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13048032213 used gas 13048032213 + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13054614261 used gas 13054614261 + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13054614261 used gas 13054614261 + "#]], + ]); + + test_builder().wasm( + &unc_test_contracts::LargeContract { + functions: functions_number_limit + 1, + ..Default::default() + } + .make(), + ) + .protocol_features(&[ + ProtocolFeature::LimitContractFunctionsNumber, + ProtocolFeature::PreparationV2, + ]) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13049332713 used gas 13049332713 + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Too many functions in contract. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Too many functions in contract. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13049332713 used gas 13049332713 + Err: PrepareError: Too many functions in contract. + "#]], + ]); + + test_builder() + .wasm( + &unc_test_contracts::LargeContract { + functions: functions_number_limit / 2, + panic_imports: functions_number_limit / 2 + 1, + ..Default::default() + } + .make(), + ) + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .protocol_version( + FIX_CONTRACT_LOADING_COST + ) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Too many functions in contract. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Too many functions in contract. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 19554433713 used gas 19554433713 + Err: PrepareError: Too many functions in contract. + "#]], + ]); + + test_builder() + .wasm( + &unc_test_contracts::LargeContract { + functions: functions_number_limit, + panic_imports: 1, + ..Default::default() + } + .make(), + ) + .protocol_features(&[ + ProtocolFeature::PreparationV2, + + ]) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Too many functions in contract. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Too many functions in contract. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13051283463 used gas 13051283463 + Err: PrepareError: Too many functions in contract. + "#]], + ]); +} + +#[test] +fn test_limit_locals() { + test_builder() + .wasm( + &unc_test_contracts::LargeContract { + functions: 1, + locals_per_function: 50_001, + ..Default::default() + } + .make(), + ) + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + + .protocol_version( + FIX_CONTRACT_LOADING_COST + ) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Error happened while deserializing the module. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Error happened while deserializing the module. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 43682463 used gas 43682463 + Err: PrepareError: Error happened while deserializing the module. + "#]], + ]); + + test_builder() + .wasm( + &unc_test_contracts::LargeContract { + functions: 1, + locals_per_function: 50_000, + ..Default::default() + } + .make(), + ) + .skip_wasmer0() + .opaque_error() + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 43682463 used gas 43682463 + Err: ... + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 43682463 used gas 43682463 + Err: ... + "#]], + ]); +} + +#[test] +fn test_limit_locals_global() { + test_builder().wasm(&unc_test_contracts::LargeContract { + functions: 101, + locals_per_function: 9901, + ..Default::default() + } + .make()) + .protocol_features(&[ + ProtocolFeature::LimitContractLocals, + ProtocolFeature::PreparationV2, + + ]) + .protocol_version(FIX_CONTRACT_LOADING_COST,) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 195407463 used gas 195407463 + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Too many locals declared in the contract. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Too many locals declared in the contract. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 195407463 used gas 195407463 + Err: PrepareError: Too many locals declared in the contract. + "#]], + ]); + + test_builder() + .wasm( + &unc_test_contracts::LargeContract { + functions: 64, + locals_per_function: 15625, + ..Default::default() + } + .make(), + ) + .opaque_error() + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 139269213 used gas 139269213 + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13001413761 used gas 13001413761 + "#]] + ]); +} + +#[test] +pub fn test_stabilized_host_function() { + test_builder() + .wat( + r#" +(module + (import "env" "ripemd160" (func $ripemd160 (param i64 i64 i64))) + (func (export "main") + (call $ripemd160 (i64.const 0) (i64.const 0) (i64.const 0))) +)"#, + ) + .protocol_features(&[ + ProtocolFeature::MathExtension, + ProtocolFeature::PreparationV2, + ]) + .opaque_error() + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 54519963 used gas 54519963 + Err: ... + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 7143010623 used gas 7143010623 + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 7149592671 used gas 7149592671 + "#]], + ]); +} + +#[test] +fn test_sandbox_only_function() { + let tb = test_builder() + .wat( + r#" +(module + (import "env" "sandbox_debug_log" (func $sandbox_debug_log (param i64 i64))) + (func (export "main") + (call $sandbox_debug_log (i64.const 0) (i64.const 1))) +)"#, + ) + .opaque_error(); + + #[cfg(feature = "sandbox")] + tb.expect(&expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 59805981 used gas 59805981 + "#]]); + + #[cfg(not(feature = "sandbox"))] + tb.expect(&expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 57337713 used gas 57337713 + Err: ... + "#]]); +} + +#[test] +fn extension_saturating_float_to_int() { + test_builder() + .wat( + r#" + (module + (func $test_trunc (param $x f64) (result i32) (i32.trunc_sat_f64_s (local.get $x))) + ) + "#, + ) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Error happened while deserializing the module. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 48450963 used gas 48450963 + Err: PrepareError: Error happened while deserializing the module. + "#]], + ]); +} + +#[test] +fn extension_signext() { + let tb = test_builder() + .wat( + r#" + (module + (func $extend8_s (param $x i32) (result i32) (i32.extend8_s (local.get $x))) + (func (export "main")) + ) + "#, + ) + .protocol_features(&[ProtocolFeature::PreparationV2]); + tb.expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Error happened while deserializing the module. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 58284261 used gas 58284261 + "#]], + ]); +} diff --git a/runtime/unc-vm-runner/src/tests/fuzzers.rs b/runtime/unc-vm-runner/src/tests/fuzzers.rs new file mode 100644 index 000000000..26da3ea9c --- /dev/null +++ b/runtime/unc-vm-runner/src/tests/fuzzers.rs @@ -0,0 +1,192 @@ +use super::test_vm_config; +use crate::internal::wasmparser::{Export, ExternalKind, Parser, Payload, TypeDef}; +use crate::logic::errors::FunctionCallError; +use crate::logic::mocks::mock_external::MockedExternal; +use crate::logic::VMContext; +use crate::runner::VMKindExt; +use crate::runner::VMResult; +use crate::ContractCode; +use arbitrary::Arbitrary; +use core::fmt; +use unc_parameters::vm::{ContractPrepareVersion, VMKind}; +use unc_parameters::RuntimeFeesConfig; + +/// Finds a no-parameter exported function, something like `(func (export "entry-point"))`. +pub fn find_entry_point(contract: &ContractCode) -> Option { + let mut tys = Vec::new(); + let mut fns = Vec::new(); + for payload in Parser::default().parse_all(contract.code()) { + match payload { + Ok(Payload::FunctionSection(rdr)) => fns.extend(rdr), + Ok(Payload::TypeSection(rdr)) => tys.extend(rdr), + Ok(Payload::ExportSection(rdr)) => { + for export in rdr { + if let Ok(Export { field, kind: ExternalKind::Function, index }) = export { + if let Some(&Ok(ty_index)) = fns.get(index as usize) { + if let Some(Ok(TypeDef::Func(func_type))) = tys.get(ty_index as usize) { + if func_type.params.is_empty() && func_type.returns.is_empty() { + return Some(field.to_string()); + } + } + } + } + } + } + _ => (), + } + } + None +} + +pub fn create_context(input: Vec) -> VMContext { + VMContext { + current_account_id: "alice".parse().unwrap(), + signer_account_id: "bob".parse().unwrap(), + signer_account_pk: vec![0, 1, 2, 3, 4], + predecessor_account_id: "carol".parse().unwrap(), + input, + block_height: 10, + block_timestamp: 42, + epoch_height: 1, + account_balance: 2u128, + account_locked_balance: 0, + storage_usage: 12, + attached_deposit: 2u128, + prepaid_gas: 10_u64.pow(14), + random_seed: vec![0, 1, 2], + view_config: None, + output_data_receivers: vec![], + } +} + +/// Define a configuration for which [`available_imports`] is implemented. This +/// allows to specify the imports available in a [`ConfiguredModule`]. +/// +/// [`available_imports`]: wasm_smith::Config::available_imports +/// [`ConfiguredModule`]: wasm_smith::ConfiguredModule +#[derive(arbitrary::Arbitrary, Debug)] +pub struct ModuleConfig {} + +impl wasm_smith::Config for ModuleConfig { + /// Returns a WebAssembly module which imports all near host functions. The + /// imports are grabbed from a compiled [test contract] which calls every + /// host function in its method `sanity_check`. + /// + /// [test contract]: unc_test_contracts::rs_contract + fn available_imports(&self) -> Option> { + Some(unc_test_contracts::rs_contract().into()) + } + + /// Make sure to canonicalize the NaNs, as otherwise behavior differs + /// between wasmtime (that does not canonicalize) and unc-vm (that + /// should canonicalize) + fn canonicalize_nans(&self) -> bool { + true + } +} + +/// Wrapper to get more useful Debug. +pub struct ArbitraryModule(pub wasm_smith::ConfiguredModule); + +impl<'a> Arbitrary<'a> for ArbitraryModule { + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + wasm_smith::ConfiguredModule::::arbitrary(u).map(ArbitraryModule) + } +} + +impl fmt::Debug for ArbitraryModule { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let bytes = self.0.module.to_bytes(); + write!(f, "{:?}", bytes)?; + if let Ok(wat) = wasmprinter::print_bytes(&bytes) { + write!(f, "\n{}", wat)?; + } + Ok(()) + } +} + +fn run_fuzz(code: &ContractCode, vm_kind: VMKind) -> VMResult { + let mut fake_external = MockedExternal::new(); + + let mut context = create_context(vec![]); + context.prepaid_gas = 10u64.pow(14); + + let mut config = test_vm_config(); + config.limit_config.wasmer2_stack_limit = i32::MAX; // If we can crash wasmer2 even without the secondary stack limit it's still good to know + config.limit_config.contract_prepare_version = ContractPrepareVersion::V2; + + let fees = RuntimeFeesConfig::test(); + + let promise_results = vec![]; + + let method_name = find_entry_point(code).unwrap_or_else(|| "main".to_string()); + let mut res = vm_kind.runtime(config).unwrap().run( + code, + &method_name, + &mut fake_external, + context, + &fees, + &promise_results, + None, + ); + + // Remove the VMError message details as they can differ between runtimes + // TODO: maybe there's actually things we could check for equality here too? + match res { + Ok(ref mut outcome) => { + if outcome.aborted.is_some() { + outcome.logs = vec!["[censored]".to_owned()]; + outcome.aborted = + Some(FunctionCallError::LinkError { msg: "[censored]".to_owned() }); + } + } + Err(err) => panic!("fatal error: {err:?}"), + } + res +} + +#[test] +fn current_vm_does_not_crash_fuzzer() { + bolero::check!().with_arbitrary::().for_each(|module: &ArbitraryModule| { + let code = ContractCode::new(module.0.module.to_bytes(), None); + let config = test_vm_config(); + let _result = run_fuzz(&code, config.vm_kind); + }); +} + +#[test] +#[cfg_attr(not(all(feature = "unc_vm", target_arch = "x86_64")), ignore)] +fn unc_vm_and_wasmtime_agree_fuzzer() { + bolero::check!().with_arbitrary::().for_each(|module: &ArbitraryModule| { + let code = ContractCode::new(module.0.module.to_bytes(), None); + let unc_vm = run_fuzz(&code, VMKind::NearVm).expect("fatal failure"); + let wasmtime = run_fuzz(&code, VMKind::Wasmtime).expect("fatal failure"); + assert_eq!(unc_vm, wasmtime); + }); +} + +#[test] +#[cfg(all(feature = "unc_vm", target_arch = "x86_64"))] +fn unc_vm_is_reproducible_fuzzer() { + use crate::unc_vm_runner::NearVM; + use unc_primitives_core::hash::CryptoHash; + + bolero::check!().with_arbitrary::().for_each(|module: &ArbitraryModule| { + let code = ContractCode::new(module.0.module.to_bytes(), None); + let config = test_vm_config(); + let mut first_hash = None; + for _ in 0..3 { + let vm = NearVM::new(config.clone()); + let exec = match vm.compile_uncached(&code) { + Ok(e) => e, + Err(_) => return, + }; + let code = exec.serialize().unwrap(); + let hash = CryptoHash::hash_bytes(&code); + match first_hash { + None => first_hash = Some(hash), + Some(h) => assert_eq!(h, hash), + } + } + }) +} diff --git a/runtime/unc-vm-runner/src/tests/regression_tests.rs b/runtime/unc-vm-runner/src/tests/regression_tests.rs new file mode 100644 index 000000000..29f18518d --- /dev/null +++ b/runtime/unc-vm-runner/src/tests/regression_tests.rs @@ -0,0 +1,82 @@ +use crate::tests::test_builder::test_builder; +use expect_test::expect; + +#[test] +fn memory_size_alignment_issue() { + test_builder() + .wat( + r#" + (module + (type (;0;) (func)) + (func (;0;) (type 0) + memory.size + drop + ) + (memory (;0;) 1 1024) + (export "foo" (func 0)) + ) + "#, + ) + .method("foo") + .protocol_features(&[ + unc_primitives_core::version::ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 46411725 used gas 46411725 + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 52993773 used gas 52993773 + "#]], + ]); +} + +#[test] +fn finite_wasm_gas_was_being_traced_and_thus_slow() { + test_builder() + .wat( + r#"(module + (type (func)) + (func (type 0) + loop (result i64) + loop + br 0 + end + i64.const 0 + end + drop) + (export "foo" (func 0))) + "#, + ) + .method("foo") + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 100000000000000 used gas 100000000000000 + Err: Exceeded the prepaid gas. + "#]], + ]); +} + +#[test] +fn gas_intrinsic_did_not_multiply_by_opcode_cost() { + test_builder() + .wat( + r#" + (module + (type (func (param i32))) + (type (func)) + (import "env" "gas" (func (type 0))) + (func (type 1) + i32.const 500000000 + call 0) + (export "foo" (func 1))) + "#, + ) + .method("foo") + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 100000000000000 used gas 100000000000000 + Err: Exceeded the maximum amount of gas allowed to burn per contract. + "#]], + ]); +} diff --git a/runtime/unc-vm-runner/src/tests/rs_contract.rs b/runtime/unc-vm-runner/src/tests/rs_contract.rs new file mode 100644 index 000000000..e4e035cbe --- /dev/null +++ b/runtime/unc-vm-runner/src/tests/rs_contract.rs @@ -0,0 +1,292 @@ +use crate::logic::errors::{FunctionCallError, HostError, WasmTrap}; +use crate::logic::mocks::mock_external::{MockAction, MockedExternal}; +use crate::logic::types::ReturnData; +use crate::logic::Config; +use crate::runner::VMKindExt; +use crate::ContractCode; +use unc_parameters::RuntimeFeesConfig; +use unc_primitives_core::types::Balance; +use std::mem::size_of; + +use super::test_vm_config; +use crate::runner::VMResult; +use crate::tests::{ + create_context, with_vm_variants, CURRENT_ACCOUNT_ID, PREDECESSOR_ACCOUNT_ID, + SIGNER_ACCOUNT_ID, SIGNER_ACCOUNT_PK, +}; +use unc_parameters::vm::VMKind; + +/// Encode array of `u64` to be passed as a smart contract argument. +fn encode(xs: &[u64]) -> Vec { + xs.iter().flat_map(|it| it.to_le_bytes()).collect() +} + +fn test_contract(vm_kind: VMKind) -> ContractCode { + let code = match vm_kind { + // testing backwards-compatibility, use an old WASM + VMKind::Wasmer0 | VMKind::Wasmer2 => { + unc_test_contracts::backwards_compatible_rs_contract() + } + // production and developer environment, use a cutting-edge WASM + VMKind::Wasmtime | VMKind::NearVm => unc_test_contracts::rs_contract(), + }; + ContractCode::new(code.to_vec(), None) +} + +#[track_caller] +fn assert_run_result(result: VMResult, expected_value: u64) { + let outcome = result.expect("Failed execution"); + + if let ReturnData::Value(value) = &outcome.return_data { + let mut arr = [0u8; size_of::()]; + arr.copy_from_slice(&value); + let res = u64::from_le_bytes(arr); + assert_eq!(res, expected_value); + } else { + panic!("Value was not returned"); + } +} + +#[test] +pub fn test_read_write() { + let config = test_vm_config(); + with_vm_variants(&config, |vm_kind: VMKind| { + let code = test_contract(vm_kind); + let mut fake_external = MockedExternal::new(); + + let context = create_context(encode(&[10u64, 20u64])); + let fees = RuntimeFeesConfig::test(); + + let promise_results = vec![]; + let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); + let result = runtime.run( + &code, + "write_key_value", + &mut fake_external, + context, + &fees, + &promise_results, + None, + ); + assert_run_result(result, 0); + + let context = create_context(encode(&[10u64])); + let result = runtime.run( + &code, + "read_value", + &mut fake_external, + context, + &fees, + &promise_results, + None, + ); + assert_run_result(result, 20); + }); +} + +macro_rules! def_test_ext { + ($name:ident, $method:expr, $expected:expr, $input:expr, $validator:expr) => { + #[test] + pub fn $name() { + let config = test_vm_config(); + with_vm_variants(&config, |vm_kind: VMKind| { + run_test_ext(&config, $method, $expected, $input, $validator, vm_kind) + }); + } + }; + ($name:ident, $method:expr, $expected:expr, $input:expr) => { + #[test] + pub fn $name() { + let config = test_vm_config(); + with_vm_variants(&config, |vm_kind: VMKind| { + run_test_ext(&config, $method, $expected, $input, vec![], vm_kind) + }); + } + }; + ($name:ident, $method:expr, $expected:expr) => { + #[test] + pub fn $name() { + let config = test_vm_config(); + with_vm_variants(&config, |vm_kind: VMKind| { + run_test_ext(&config, $method, $expected, &[], vec![], vm_kind) + }) + } + }; +} + +fn run_test_ext( + config: &Config, + method: &str, + expected: &[u8], + input: &[u8], + validators: Vec<(&str, Balance)>, + vm_kind: VMKind, +) { + let code = test_contract(vm_kind); + let mut fake_external = MockedExternal::new(); + fake_external.validators = + validators.into_iter().map(|(s, b)| (s.parse().unwrap(), b)).collect(); + let fees = RuntimeFeesConfig::test(); + let context = create_context(input.to_vec()); + let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); + + let outcome = runtime + .run(&code, method, &mut fake_external, context, &fees, &[], None) + .unwrap_or_else(|err| panic!("Failed execution: {:?}", err)); + + assert_eq!(outcome.profile.action_gas(), 0); + + if let ReturnData::Value(value) = outcome.return_data { + assert_eq!(&value, &expected); + } else { + panic!("Value was not returned, got outcome {:?}", outcome); + } +} + +def_test_ext!(ext_account_id, "ext_account_id", CURRENT_ACCOUNT_ID.as_bytes()); + +def_test_ext!(ext_signer_id, "ext_signer_id", SIGNER_ACCOUNT_ID.as_bytes()); +def_test_ext!( + ext_predecessor_account_id, + "ext_predecessor_account_id", + PREDECESSOR_ACCOUNT_ID.as_bytes(), + &[] +); +def_test_ext!(ext_signer_pk, "ext_signer_pk", &SIGNER_ACCOUNT_PK); +def_test_ext!(ext_random_seed, "ext_random_seed", &[0, 1, 2]); + +def_test_ext!(ext_prepaid_gas, "ext_prepaid_gas", &(10_u64.pow(14)).to_le_bytes()); +def_test_ext!(ext_block_index, "ext_block_index", &10u64.to_le_bytes()); +def_test_ext!(ext_block_timestamp, "ext_block_timestamp", &42u64.to_le_bytes()); +def_test_ext!(ext_storage_usage, "ext_storage_usage", &12u64.to_le_bytes()); + +#[test] +pub fn ext_used_gas() { + let config = test_vm_config(); + with_vm_variants(&config, |vm_kind: VMKind| { + // Note, the used_gas is not a global used_gas at the beginning of method, but instead a + // diff in used_gas for computing fib(30) in a loop + let expected = match config.limit_config.contract_prepare_version { + crate::logic::ContractPrepareVersion::V0 => [111, 10, 200, 15, 0, 0, 0, 0], + crate::logic::ContractPrepareVersion::V1 => [111, 10, 200, 15, 0, 0, 0, 0], + crate::logic::ContractPrepareVersion::V2 => [27, 180, 237, 15, 0, 0, 0, 0], + }; + run_test_ext(&config, "ext_used_gas", &expected, &[], vec![], vm_kind) + }) +} + +def_test_ext!( + ext_sha256, + "ext_sha256", + &[ + 18, 176, 115, 156, 45, 100, 241, 132, 180, 134, 77, 42, 105, 111, 199, 127, 118, 112, 92, + 255, 88, 43, 83, 147, 122, 55, 26, 36, 42, 156, 160, 158, + ], + b"tesdsst" +); +// current_account_balance = context.account_balance + context.attached_deposit; +def_test_ext!(ext_account_balance, "ext_account_balance", &(2u128 + 2).to_le_bytes()); +def_test_ext!(ext_attached_deposit, "ext_attached_deposit", &2u128.to_le_bytes()); + +def_test_ext!( + ext_validator_stake_alice, + "ext_validator_stake", + &(100u128).to_le_bytes(), + b"alice", + vec![("alice", 100), ("bob", 1)] +); +def_test_ext!( + ext_validator_stake_bob, + "ext_validator_stake", + &(1u128).to_le_bytes(), + b"bob", + vec![("alice", 100), ("bob", 1)] +); +def_test_ext!( + ext_validator_stake_carol, + "ext_validator_stake", + &(0u128).to_le_bytes(), + b"carol", + vec![("alice", 100), ("bob", 1)] +); + +def_test_ext!( + ext_validator_total_stake, + "ext_validator_total_stake", + &(100u128 + 1).to_le_bytes(), + &[], + vec![("alice", 100), ("bob", 1)] +); + +#[test] +pub fn test_out_of_memory() { + let mut config = test_vm_config(); + config.make_free(); + with_vm_variants(&config, |vm_kind: VMKind| { + // TODO: currently we only run this test on Wasmer. + match vm_kind { + VMKind::Wasmtime => return, + _ => {} + } + + let code = test_contract(vm_kind); + let mut fake_external = MockedExternal::new(); + + let context = create_context(Vec::new()); + let fees = RuntimeFeesConfig::free(); + let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); + + let promise_results = vec![]; + let result = runtime + .run(&code, "out_of_memory", &mut fake_external, context, &fees, &promise_results, None) + .expect("execution failed"); + assert_eq!( + result.aborted, + match vm_kind { + VMKind::Wasmer0 | VMKind::Wasmer2 | VMKind::NearVm => + Some(FunctionCallError::WasmTrap(WasmTrap::Unreachable)), + VMKind::Wasmtime => unreachable!(), + } + ); + }) +} + +fn function_call_weight_contract() -> ContractCode { + ContractCode::new(unc_test_contracts::rs_contract().to_vec(), None) +} + +#[test] +fn attach_unspent_gas_but_use_all_gas() { + let mut context = create_context(vec![]); + context.prepaid_gas = 100 * 10u64.pow(12); + + let mut config = test_vm_config(); + config.limit_config.max_gas_burnt = context.prepaid_gas / 3; + + with_vm_variants(&config, |vm_kind: VMKind| { + let code = function_call_weight_contract(); + let mut external = MockedExternal::new(); + let fees = RuntimeFeesConfig::test(); + let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); + + let outcome = runtime + .run( + &code, + "attach_unspent_gas_but_use_all_gas", + &mut external, + context.clone(), + &fees, + &[], + None, + ) + .unwrap_or_else(|err| panic!("Failed execution: {:?}", err)); + + let err = outcome.aborted.as_ref().unwrap(); + assert!(matches!(err, FunctionCallError::HostError(HostError::GasExceeded))); + + match &external.action_log[..] { + [_, MockAction::FunctionCallWeight { prepaid_gas: gas, .. }, _] => assert_eq!(*gas, 0), + other => panic!("unexpected actions: {other:?}"), + } + }); +} diff --git a/runtime/unc-vm-runner/src/tests/runtime_errors.rs b/runtime/unc-vm-runner/src/tests/runtime_errors.rs new file mode 100644 index 000000000..eee199959 --- /dev/null +++ b/runtime/unc-vm-runner/src/tests/runtime_errors.rs @@ -0,0 +1,1058 @@ +use super::test_builder::test_builder; +use expect_test::expect; +use unc_primitives_core::version::ProtocolFeature; +use std::fmt::Write; + +const FIX_CONTRACT_LOADING_COST: u32 = 129; + +static INFINITE_INITIALIZER_CONTRACT: &str = r#" +(module + (func $start (loop (br 0))) + (func (export "main")) + (start $start) +)"#; + +#[test] +fn test_infinite_initializer() { + test_builder() + .wat(INFINITE_INITIALIZER_CONTRACT) + .gas(10u64.pow(10)) + .expect(&expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 10000000000 used gas 10000000000 + Err: Exceeded the prepaid gas. + "#]]); +} + +#[test] +fn test_infinite_initializer_export_not_found() { + test_builder() + .wat(INFINITE_INITIALIZER_CONTRACT) + .method("no-such-method") + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 0 storage_usage 0 return data None burnt gas 0 used gas 0 + Err: MethodNotFound + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 49101213 used gas 49101213 + Err: MethodNotFound + "#]], + ]); +} + +static SIMPLE_CONTRACT: &str = r#"(module (func (export "main")))"#; + +#[test] +fn test_simple_contract() { + test_builder() + .wat(SIMPLE_CONTRACT) + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 42815463 used gas 42815463 + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 49397511 used gas 49397511 + "#]], + ]); +} + +#[test] +fn test_imported_memory() { + test_builder() + .wasm(&[ + 0, 97, 115, 109, 1, 0, 0, 0, 2, 12, 1, 3, 101, 110, 118, 0, 2, 1, 239, 1, 248, 1, 4, 6, + 1, 112, 0, 143, 129, 32, 7, 12, 1, 8, 0, 17, 17, 17, 17, 17, 17, 2, 2, 0, + ]) + // Wasmtime classifies this error as link error at the moment. + .opaque_error() + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: ... + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 44982963 used gas 44982963 + Err: ... + "#]], + ]); +} + +#[test] +fn test_multiple_memories() { + test_builder() + .wat("(module (memory 1 2) (memory 3 4))") + .opaque_error() + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: ... + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 39130713 used gas 39130713 + Err: ... + "#]], + ]); +} + +#[test] +fn test_export_not_found() { + test_builder().wat(SIMPLE_CONTRACT) + .method("no-such-method") + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 0 storage_usage 0 return data None burnt gas 0 used gas 0 + Err: MethodNotFound + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 42815463 used gas 42815463 + Err: MethodNotFound + "#]], + ]); +} + +#[test] +fn test_empty_method() { + test_builder().wat(SIMPLE_CONTRACT).method("").expect(&expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: MethodEmptyName + "#]]); +} + +#[test] +fn test_trap_contract() { + test_builder() + .wat(r#"(module (func (export "main") (unreachable)) )"#) + .skip_wasmtime() + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 43854969 used gas 43854969 + Err: WebAssembly trap: An `unreachable` opcode was executed. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 50437017 used gas 50437017 + Err: WebAssembly trap: An `unreachable` opcode was executed. + "#]], + ]); +} + +#[test] +fn test_trap_initializer() { + test_builder() + .wat( + r#" + (module + (func $f (export "main") (unreachable)) + (start $f) + ) + "#, + ) + .skip_wasmtime() + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 47322969 used gas 47322969 + Err: WebAssembly trap: An `unreachable` opcode was executed. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 53905017 used gas 53905017 + Err: WebAssembly trap: An `unreachable` opcode was executed. + "#]], + ]); +} + +#[test] +fn test_div_by_zero_contract() { + test_builder() + .wat( + r#" + (module + (func (export "main") + i32.const 1 + i32.const 0 + i32.div_s + return + ) + ) + "#, + ) + .skip_wasmtime() + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 47406987 used gas 47406987 + Err: WebAssembly trap: An arithmetic exception, e.g. divided by zero. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 53166279 used gas 53166279 + Err: WebAssembly trap: An arithmetic exception, e.g. divided by zero. + "#]], + ]); +} + +#[test] +fn test_float_to_int_contract() { + for op in ["i32.trunc_f64_s", "i32.trunc_f64_u", "i64.trunc_f64_s", "i64.trunc_f64_u"] { + test_builder() + .wat(&format!( + r#" + (module + (func (export "main") + f64.const 0x1p+1023 + {op} + return + ) + ) + "#, + )) + .skip_wasmtime() + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 47667981 used gas 47667981 + Err: WebAssembly trap: An arithmetic exception, e.g. divided by zero. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 53427273 used gas 53427273 + Err: WebAssembly trap: An arithmetic exception, e.g. divided by zero. + "#]], + ]); + } +} + +#[test] +fn test_indirect_call_to_null_contract() { + test_builder() + .wat( + r#" + (module + (type $ty (func)) + (table 1 funcref) + (func (export "main") + i32.const 0 + call_indirect (type $ty) + return + ) + ) + "#, + ) + .opaque_error() + .skip_wasmtime() + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 50919231 used gas 50919231 + Err: ... + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 56678523 used gas 56678523 + Err: ... + "#]], + ]) +} + +#[test] +fn test_indirect_call_to_wrong_signature_contract() { + test_builder() + .wat( + r#" + (module + (type $ty (func (result i32))) + (func $f) + + (table 1 funcref) + (elem (i32.const 0) $f) + + (func (export "main") + i32.const 0 + call_indirect (type $ty) + return + ) + ) + "#, + ) + .skip_wasmtime() + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 55904481 used gas 55904481 + Err: WebAssembly trap: Call indirect incorrect signature trap. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 61663773 used gas 61663773 + Err: WebAssembly trap: Call indirect incorrect signature trap. + "#]] + ]) +} + +#[test] +fn test_wrong_signature_contract() { + test_builder() + .wat(r#"(module (func (export "main") (param i32)))"#) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 0 storage_usage 0 return data None burnt gas 0 used gas 0 + Err: MethodInvalidSignature + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 43032213 used gas 43032213 + Err: MethodInvalidSignature + "#]], + ]); +} + +#[test] +fn test_export_wrong_type() { + test_builder() + .wat(r#"(module (global (export "main") i32 (i32.const 123)))"#) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 0 storage_usage 0 return data None burnt gas 0 used gas 0 + Err: MethodNotFound + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 41298213 used gas 41298213 + Err: MethodNotFound + "#]], + ]); +} + +#[test] +fn test_guest_panic() { + test_builder() + .wat( + r#" + (module + (import "env" "panic" (func $panic)) + (func (export "main") (call $panic)) + ) + "#, + ) + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 315775830 used gas 315775830 + Err: Smart contract panicked: explicit guest panic + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 322357878 used gas 322357878 + Err: Smart contract panicked: explicit guest panic + "#]], + ]); +} + +#[test] +fn test_panic_re_export() { + test_builder() + .wat( + r#" +(module + (import "env" "panic" (func $panic)) + (export "main" (func $panic)) +)"#, + ) + .expect(&expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 312352074 used gas 312352074 + Err: Smart contract panicked: explicit guest panic + "#]]); +} + +#[test] +fn test_stack_overflow() { + test_builder() + .wat(r#"(module (func $f (export "main") (call $f)))"#) + .skip_wasmtime() + .opaque_error() + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13526101017 used gas 13526101017 + Err: ... + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 30376143897 used gas 30376143897 + Err: ... + "#]], + ]); +} + +#[test] +fn test_stack_instrumentation_protocol_upgrade() { + test_builder() + .wat( + r#" + (module + (func $f1 (export "f1") + (local i32) + (call $f1)) + (func $f2 (export "f2") + (local i32 i32 i32 i32) + (call $f2)) + ) + "#, + ) + .method("f1") + .protocol_features(&[ + ProtocolFeature::CorrectStackLimit, + ProtocolFeature::PreparationV2, + ]) + .skip_wasmtime() + .opaque_error() + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 18136872021 used gas 18136872021 + Err: ... + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 6789985365 used gas 6789985365 + Err: ... + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 31767212013 used gas 31767212013 + Err: ... + "#]], + ]); + + test_builder() + .wat( + r#" + (module + (func $f1 (export "f1") + (local i32) + (call $f1)) + (func $f2 (export "f2") + (local i32 i32 i32 i32) + (call $f2)) + ) + "#, + ) + .method("f2") + .opaque_error() + .protocol_features(&[ + ProtocolFeature::CorrectStackLimit, + ProtocolFeature::PreparationV2, + ]) + .skip_wasmtime() + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 18136872021 used gas 18136872021 + Err: ... + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 2745316869 used gas 2745316869 + Err: ... + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 29698803429 used gas 29698803429 + Err: ... + "#]], + ]); +} + +#[test] +fn test_memory_grow() { + test_builder() + .wat( + r#" +(module + (memory 17 32) + (func (export "main") + (loop + (memory.grow (i32.const 1)) + drop + br 0 + ) + ) +)"#, + ) + .gas(10u64.pow(10)) + .expect(&expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 10000000000 used gas 10000000000 + Err: Exceeded the prepaid gas. + "#]]); +} + +fn bad_import_global(env: &str) -> Vec { + wat::parse_str(format!( + r#" + (module + (import "{env}" "no-such-global" (global i32)) + (func (export "main")) + )"#, + )) + .unwrap() +} + +fn bad_import_func(env: &str) -> Vec { + wat::parse_str(format!( + r#" + (module + (import "{env}" "no-such-fn" (func $f)) + (export "main" (func $f)) + )"#, + )) + .unwrap() +} + +#[test] +// Weird behavior: +// Invalid import not from "env" -> PrepareError::Instantiate +// Invalid import from "env" -> LinkError +fn test_bad_import_1() { + test_builder() + .wasm(&bad_import_global("no-such-module")) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Error happened during instantiation. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 50618463 used gas 50618463 + Err: PrepareError: Error happened during instantiation. + "#]], + ]); +} + +#[test] +fn test_bad_import_2() { + test_builder() + .wasm(&bad_import_func("no-such-module")) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Error happened during instantiation. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 50184963 used gas 50184963 + Err: PrepareError: Error happened during instantiation. + "#]], + ]); +} + +#[test] +fn test_bad_import_3() { + test_builder() + .wasm(&bad_import_global("env")) + .opaque_error() + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 48234213 used gas 48234213 + Err: ... + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: ... + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 48234213 used gas 48234213 + Err: ... + "#]], + ]); +} + +#[test] +fn test_bad_import_4() { + test_builder().wasm(&bad_import_func("env")).opaque_error().expect(&expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 47800713 used gas 47800713 + Err: ... + "#]]); +} + +#[test] +fn test_initializer_no_gas() { + test_builder() + .wat( + r#" +(module + (func $f (export "main") nop) + (start $f) +)"#, + ) + .gas(0) + .expect(&expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: Exceeded the prepaid gas. + "#]]); +} + +#[test] +fn test_bad_many_imports() { + let mut imports = String::new(); + for i in 0..100 { + writeln!(imports, r#"(import "env" "wtf{i}" (func))"#).unwrap(); + } + + test_builder() + .wat(&format!( + r#" + (module + {imports} + (export "main" (func 0)) + )"#, + )) + .opaque_error() + .expect(&expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 299447463 used gas 299447463 + Err: ... + "#]]) +} + +static EXTERNAL_CALL_CONTRACT: &str = r#" +(module + (import "env" "prepaid_gas" (func $prepaid_gas (result i64))) + (func (export "main") + (drop (call $prepaid_gas))) +)"#; + +#[test] +fn test_external_call_ok() { + test_builder() + .wat(EXTERNAL_CALL_CONTRACT) + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 320283336 used gas 320283336 + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 326865384 used gas 326865384 + "#]], + ]); +} + +#[test] +fn test_external_call_error() { + test_builder().wat(EXTERNAL_CALL_CONTRACT).gas(100).expect(&expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 100 used gas 100 + Err: Exceeded the prepaid gas. + "#]]); +} + +#[test] +fn test_external_call_indirect() { + test_builder() + .wat( + r#" + (module + (import "env" "prepaid_gas" (func $prepaid_gas (result i64))) + (type $prepaid_gas_t (func (result i64))) + + (table 1 funcref) + (elem (i32.const 0) $prepaid_gas) + + (func (export "main") + (call_indirect (type $prepaid_gas_t) (i32.const 0)) + drop + ) + ) + "# + ) + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 328909092 used gas 328909092 + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 335491140 used gas 335491140 + "#]], + ]); +} + +/// Load from address so far out of bounds that it causes integer overflow. +#[test] +fn test_address_overflow() { + let code = r#" + (module + (memory 1) + (func (export "main") + i32.const 1 + i64.load32_u offset=4294967295 (;2^32 - 1;) align=1 + drop + ) + ) + "#; + + test_builder() + .wat(code) + .skip_wasmtime() + .skip_wasmer0() + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 48534981 used gas 48534981 + Err: WebAssembly trap: Memory out of bounds trap. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 54294273 used gas 54294273 + Err: WebAssembly trap: Memory out of bounds trap. + "#]], + ]); + + // wasmer0 incorrectly doesn't catch overflow during address calculation + test_builder() + .wat(code) + .only_wasmer0() + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 48534981 used gas 48534981 + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 55117029 used gas 55117029 + "#]], + ]); +} + +/// Uses `f32.copysign` to observe a sign of `NaN`. +/// +/// WASM specification allows different behaviors here: +/// +/// https://github.com/WebAssembly/design/blob/main/Nondeterminism.md +/// +/// We solve this problem by canonicalizing NaNs. +#[test] +fn test_nan_sign() { + let code = r#" +(module + (func (export "main") + (i32.div_u + (i32.const 0) + (f32.gt + (f32.copysign + (f32.const 1.0) + (f32.sqrt (f32.const -1.0))) + (f32.const 0))) + drop + ) +)"#; + + test_builder() + .wat(code) + .skip_wasmtime() + .skip_wasmer0() + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 54988767 used gas 54988767 + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 61570815 used gas 61570815 + "#]], + ]); + + // wasmer0 doesn't canonicalize NaNs + test_builder() + .wat(code) + .only_wasmer0() + .protocol_features(&[ + ProtocolFeature::PreparationV2, + ]) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 54988767 used gas 54988767 + Err: WebAssembly trap: An arithmetic exception, e.g. divided by zero. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 60748059 used gas 60748059 + Err: WebAssembly trap: An arithmetic exception, e.g. divided by zero. + "#]], + ]); +} + +// Check that a `GasExceeded` error is returned when there is not enough gas to +// even load a contract. +#[test] +fn test_gas_exceed_loading() { + test_builder().wat(SIMPLE_CONTRACT).method("non_empty_non_existing").gas(1).expect(&expect![[ + r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 1 used gas 1 + Err: Exceeded the prepaid gas. + "# + ]]); +} + +// Call the "gas" host function with unreasonably large values, trying to force +// overflow +#[test] +fn gas_overflow_direct_call() { + test_builder() + .wat( + r#" +(module + (import "env" "gas" (func $gas (param i32))) + (func (export "main") + (call $gas (i32.const 0xffff_ffff))) +)"#, + ) + .expect(&expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 100000000000000 used gas 100000000000000 + Err: Exceeded the maximum amount of gas allowed to burn per contract. + "#]]); +} + +// Call the "gas" host function indirectly with unreasonably large values, +// trying to force overflow. +#[test] +fn gas_overflow_indirect_call() { + test_builder() + .wat( + r#" +(module + (import "env" "gas" (func $gas (param i32))) + (type $gas_ty (func (param i32))) + + (table 1 anyfunc) + (elem (i32.const 0) $gas) + + (func (export "main") + (call_indirect + (type $gas_ty) + (i32.const 0xffff_ffff) + (i32.const 0))) +)"#, + ) + .expect(&expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 100000000000000 used gas 100000000000000 + Err: Exceeded the maximum amount of gas allowed to burn per contract. + "#]]); +} + +mod fix_contract_loading_cost_protocol_upgrade { + use super::*; + use unc_parameters::ExtCosts; + + static ALMOST_TRIVIAL_CONTRACT: &str = r#" +(module + (func (export "main") + i32.const 1 + i32.const 1 + i32.div_s + return + ) +)"#; + + // Normal execution should be unchanged before and after. + #[test] + fn test_fn_loading_gas_protocol_upgrade() { + test_builder() + .wat(ALMOST_TRIVIAL_CONTRACT) + .protocol_features(&[ + ProtocolFeature::PreparationV2 + ]) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 47406987 used gas 47406987 + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 53989035 used gas 53989035 + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 53989035 used gas 53989035 + "#]], + ]); + } + + // Executing with just enough gas to load the contract will fail before and + // after. Both charge the same amount of gas. + #[test] + fn test_fn_loading_gas_protocol_upgrade_exceed_loading() { + let expect = expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 44115963 used gas 44115963 + Err: Exceeded the prepaid gas. + "#]]; + let test_after = test_builder().wat(ALMOST_TRIVIAL_CONTRACT); + let cfg_costs = &test_after.configs().next().unwrap().wasm_config.ext_costs; + let loading_base = cfg_costs.gas_cost(ExtCosts::contract_loading_base); + let loading_byte = cfg_costs.gas_cost(ExtCosts::contract_loading_bytes); + let wasm_length = test_after.get_wasm().len(); + test_after.gas(loading_base + wasm_length as u64 * loading_byte).expect(&expect); + test_builder() + .wat(ALMOST_TRIVIAL_CONTRACT) + .only_protocol_versions(vec![FIX_CONTRACT_LOADING_COST - 1]) + .gas(loading_base + wasm_length as u64 * loading_byte) + .expect(&expect); + } + + /// Executing with enough gas to finish loading but not to execute the full + /// contract should have the same outcome before and after. + #[test] + fn test_fn_loading_gas_protocol_upgrade_exceed_executing() { + let expect = expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 45000000 used gas 45000000 + Err: Exceeded the prepaid gas. + "#]]; + let test_after = test_builder().wat(ALMOST_TRIVIAL_CONTRACT); + let cfg_costs = &test_after.configs().next().unwrap().wasm_config.ext_costs; + let loading_base = cfg_costs.gas_cost(ExtCosts::contract_loading_base); + let loading_byte = cfg_costs.gas_cost(ExtCosts::contract_loading_bytes); + let wasm_length = test_after.get_wasm().len(); + let prepaid_gas = loading_base + wasm_length as u64 * loading_byte + 884037; + test_after.gas(prepaid_gas).expect(&expect); + test_builder() + .wat(ALMOST_TRIVIAL_CONTRACT) + .only_protocol_versions(vec![FIX_CONTRACT_LOADING_COST - 1]) + .gas(prepaid_gas) + .expect(&expect); + } + + /// Failure during preparation must remain free of gas charges for old versions + /// but new versions must charge the loading gas. + #[test] + fn test_fn_loading_gas_protocol_upgrade_fail_preparing() { + // This list covers all control flows that are expected to change + // with the protocol feature. + // Having a test for every possible preparation error would be even + // better, to ensure triggering any of them will always remain + // compatible with versions before this upgrade. Unfortunately, we + // currently do not have tests ready to trigger each error. + + test_builder() + .wat(r#"(module (export "main" (func 0)))"#) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Error happened while deserializing the module. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 39347463 used gas 39347463 + Err: PrepareError: Error happened while deserializing the module. + "#]], + ]); + + test_builder() + .wasm(&bad_import_global("wtf")) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Error happened during instantiation. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 48234213 used gas 48234213 + Err: PrepareError: Error happened during instantiation. + "#]], + ]); + + test_builder() + .wasm(&bad_import_func("wtf")) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Error happened during instantiation. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 47800713 used gas 47800713 + Err: PrepareError: Error happened during instantiation. + "#]], + ]); + + test_builder() + .wasm(&unc_test_contracts::LargeContract { + functions: 101, + locals_per_function: 9901, + ..Default::default() + } + .make()) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Too many locals declared in the contract. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 195407463 used gas 195407463 + Err: PrepareError: Too many locals declared in the contract. + "#]], + ]); + + let functions_number_limit: u32 = 10_000; + test_builder() + .wasm(&unc_test_contracts::LargeContract { + functions: functions_number_limit / 2, + panic_imports: functions_number_limit / 2 + 1, + ..Default::default() + } + .make()) + .protocol_version(FIX_CONTRACT_LOADING_COST) + .expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 0 used gas 0 + Err: PrepareError: Too many functions in contract. + "#]], + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 19554433713 used gas 19554433713 + Err: PrepareError: Too many functions in contract. + "#]], + ]); + } +} + +#[test] +fn test_regression_9393() { + let before_builder = test_builder().only_protocol_versions(vec![62]); + let after_builder = test_builder().only_protocol_versions(vec![63]); + let before_cost = before_builder.configs().next().unwrap().wasm_config.regular_op_cost; + let after_cost = after_builder.configs().next().unwrap().wasm_config.regular_op_cost; + assert_eq!( + before_cost, after_cost, + "this test is not set up to test with different insn costs" + ); + let cost = u64::from(before_cost); + + let nops = (i32::MAX as u64 + cost - 1) / cost; + let contract = unc_test_contracts::function_with_a_lot_of_nop(nops); + before_builder.wasm(&contract).only_unc_vm().expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 100000000000000 used gas 100000000000000 + Err: Exceeded the maximum amount of gas allowed to burn per contract. + "#]], + ]); + after_builder.wasm(&contract).expects(&[ + expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 2763981177 used gas 2763981177 + "#]], + ]); +} diff --git a/runtime/unc-vm-runner/src/tests/test_builder.rs b/runtime/unc-vm-runner/src/tests/test_builder.rs new file mode 100644 index 000000000..76f9f633f --- /dev/null +++ b/runtime/unc-vm-runner/src/tests/test_builder.rs @@ -0,0 +1,290 @@ +use crate::logic::{ + mocks::mock_external::MockedExternal, ProtocolVersion, ReturnData, VMContext, VMOutcome, +}; +use crate::runner::VMKindExt; +use crate::ContractCode; +use unc_parameters::vm::{ContractPrepareVersion, VMKind}; +use unc_parameters::{RuntimeConfig, RuntimeConfigStore, RuntimeFeesConfig}; +use unc_primitives_core::types::Gas; +use unc_primitives_core::version::ProtocolFeature; +use std::{collections::HashSet, fmt::Write, sync::Arc}; + +pub(crate) fn test_builder() -> TestBuilder { + let context = VMContext { + current_account_id: "alice".parse().unwrap(), + signer_account_id: "bob".parse().unwrap(), + signer_account_pk: vec![0, 1, 2], + predecessor_account_id: "carol".parse().unwrap(), + input: Vec::new(), + block_height: 10, + block_timestamp: 42, + epoch_height: 1, + account_balance: 2u128, + account_locked_balance: 0, + storage_usage: 12, + attached_deposit: 2u128, + prepaid_gas: 10_u64.pow(14), + random_seed: vec![0, 1, 2], + view_config: None, + output_data_receivers: vec![], + }; + let mut skip = HashSet::new(); + if cfg!(not(target_arch = "x86_64")) { + skip.insert(VMKind::Wasmer0); + skip.insert(VMKind::Wasmer2); + skip.insert(VMKind::NearVm); + } + TestBuilder { + code: ContractCode::new(Vec::new(), None), + context, + method: "main".to_string(), + protocol_versions: vec![u32::MAX], + skip, + opaque_error: false, + opaque_outcome: false, + } +} + +pub(crate) struct TestBuilder { + code: ContractCode, + context: VMContext, + protocol_versions: Vec, + method: String, + skip: HashSet, + opaque_error: bool, + opaque_outcome: bool, +} + +impl TestBuilder { + pub(crate) fn wat(mut self, wat: &str) -> Self { + let wasm = wat::parse_str(wat) + .unwrap_or_else(|err| panic!("failed to parse input wasm: {err}\n{wat}")); + self.code = ContractCode::new(wasm, None); + self + } + + pub(crate) fn wasm(mut self, wasm: &[u8]) -> Self { + self.code = ContractCode::new(wasm.to_vec(), None); + self + } + + #[allow(dead_code)] + pub(crate) fn get_wasm(&self) -> &[u8] { + self.code.code() + } + + pub(crate) fn method(mut self, method: &str) -> Self { + self.method = method.to_string(); + self + } + + pub(crate) fn gas(mut self, gas: Gas) -> Self { + self.context.prepaid_gas = gas; + self + } + + pub(crate) fn opaque_error(mut self) -> Self { + self.opaque_error = true; + self + } + + pub(crate) fn opaque_outcome(mut self) -> Self { + self.opaque_outcome = true; + self + } + + // We only test trapping tests on Wasmer, as of version 0.17, when tests executed in parallel, + // Wasmer signal handlers may catch signals thrown from the Wasmtime, and produce fake failing tests. + pub(crate) fn skip_wasmtime(mut self) -> Self { + self.skip.insert(VMKind::Wasmtime); + self + } + + pub(crate) fn skip_wasmer0(mut self) -> Self { + self.skip.insert(VMKind::Wasmer0); + self + } + + pub(crate) fn skip_wasmer2(mut self) -> Self { + self.skip.insert(VMKind::Wasmer2); + self + } + + pub(crate) fn skip_unc_vm(mut self) -> Self { + self.skip.insert(VMKind::NearVm); + self + } + + #[allow(dead_code)] + pub(crate) fn only_wasmtime(self) -> Self { + self.skip_wasmer0().skip_wasmer2().skip_unc_vm() + } + + pub(crate) fn only_wasmer0(self) -> Self { + self.skip_wasmer2().skip_unc_vm().skip_wasmtime() + } + + #[allow(dead_code)] + pub(crate) fn only_wasmer2(self) -> Self { + self.skip_wasmer0().skip_unc_vm().skip_wasmtime() + } + + pub(crate) fn only_unc_vm(self) -> Self { + self.skip_wasmer0().skip_wasmer2().skip_wasmtime() + } + + /// Add additional protocol features to this test. + /// + /// Tricky. Given `[feat1, feat2, feat3]`, this will run *four* tests for + /// protocol versions `[feat1 - 1, feat2 - 1, feat3 - 1, PROTOCOL_VERSION]`. + /// + /// When using this method with `n` features, be sure to pass `n + 1` + /// expectations to the `expects` method. For nightly features, you can + /// `cfg` the relevant features and expect. + pub(crate) fn protocol_features( + mut self, + protocol_features: &'static [ProtocolFeature], + ) -> Self { + for feat in protocol_features { + self = self.protocol_version(feat.protocol_version() - 1); + } + self + } + + /// Add a protocol version to test. + pub(crate) fn protocol_version(mut self, protocol_version: ProtocolVersion) -> Self { + self.protocol_versions.push(protocol_version - 1); + self + } + + pub(crate) fn only_protocol_versions( + mut self, + protocol_versions: Vec, + ) -> Self { + self.protocol_versions = protocol_versions; + self + } + + #[track_caller] + pub(crate) fn expect(self, want: &expect_test::Expect) { + self.expects(std::iter::once(want)) + } + + pub(crate) fn configs(&self) -> impl Iterator> { + let runtime_config_store = RuntimeConfigStore::new(None); + self.protocol_versions + .clone() + .into_iter() + .map(move |pv| Arc::clone(runtime_config_store.get_config(pv))) + } + + #[track_caller] + pub(crate) fn expects<'a, I>(mut self, wants: I) + where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + { + self.protocol_versions.sort(); + let runtime_config_store = RuntimeConfigStore::new(None); + let wants = wants.into_iter(); + assert_eq!( + wants.len(), + self.protocol_versions.len(), + "specified {} protocol versions but only {} expectation", + self.protocol_versions.len(), + wants.len(), + ); + + for (want, &protocol_version) in wants.zip(&self.protocol_versions) { + let mut results = vec![]; + for vm_kind in [VMKind::NearVm, VMKind::Wasmer2, VMKind::Wasmer0, VMKind::Wasmtime] { + if self.skip.contains(&vm_kind) { + continue; + } + + let runtime_config = runtime_config_store.get_config(protocol_version); + + // NearVM includes a different contract preparation algorithm, that is not supported on old protocol versions + if vm_kind == VMKind::NearVm + && runtime_config.wasm_config.limit_config.contract_prepare_version + != ContractPrepareVersion::V2 + { + continue; + } + + let mut fake_external = MockedExternal::new(); + let config = runtime_config.wasm_config.clone(); + let fees = RuntimeFeesConfig::test(); + let context = self.context.clone(); + + let promise_results = vec![]; + + let runtime = vm_kind.runtime(config).expect("runtime has not been compiled"); + println!("Running {:?} for protocol version {}", vm_kind, protocol_version); + let outcome = runtime + .run( + &self.code, + &self.method, + &mut fake_external, + context, + &fees, + &promise_results, + None, + ) + .expect("execution failed"); + + let mut got = String::new(); + + if !self.opaque_outcome { + fmt_outcome_without_abort(&outcome, &mut got).unwrap(); + writeln!(&mut got).unwrap(); + } + + if let Some(err) = outcome.aborted { + let err_str = err.to_string(); + assert!(err_str.len() < 1000, "errors should be bounded in size to prevent abuse via exhausting the storage space"); + if self.opaque_error { + writeln!(&mut got, "Err: ...").unwrap(); + } else { + writeln!(&mut got, "Err: {err_str}").unwrap(); + } + }; + + results.push((vm_kind, got)); + } + + if !results.is_empty() { + want.assert_eq(&results[0].1); + for i in 1..results.len() { + if results[i].1 != results[0].1 { + panic!( + "Inconsistent VM Output:\n{:?}:\n{}\n\n{:?}:\n{}", + results[0].0, results[0].1, results[i].0, results[i].1 + ) + } + } + } + } + } +} + +fn fmt_outcome_without_abort( + outcome: &VMOutcome, + out: &mut dyn std::fmt::Write, +) -> std::fmt::Result { + let return_data_str = match &outcome.return_data { + ReturnData::None => "None".to_string(), + ReturnData::ReceiptIndex(_) => "Receipt".to_string(), + ReturnData::Value(v) => format!("Value [{} bytes]", v.len()), + }; + write!( + out, + "VMOutcome: balance {} storage_usage {} return data {} burnt gas {} used gas {}", + outcome.balance, + outcome.storage_usage, + return_data_str, + outcome.burnt_gas, + outcome.used_gas + )?; + Ok(()) +} diff --git a/runtime/unc-vm-runner/src/tests/ts_contract.rs b/runtime/unc-vm-runner/src/tests/ts_contract.rs new file mode 100644 index 000000000..71ff310d3 --- /dev/null +++ b/runtime/unc-vm-runner/src/tests/ts_contract.rs @@ -0,0 +1,85 @@ +use super::test_vm_config; +use crate::logic::errors::{FunctionCallError, HostError}; +use crate::logic::mocks::mock_external::MockedExternal; +use crate::logic::types::ReturnData; +use crate::logic::{External, StorageGetMode}; +use crate::runner::VMKindExt; +use crate::tests::{create_context, with_vm_variants}; +use crate::ContractCode; +use unc_parameters::vm::VMKind; +use unc_parameters::RuntimeFeesConfig; + +#[test] +pub fn test_ts_contract() { + let config = test_vm_config(); + with_vm_variants(&config, |vm_kind: VMKind| { + let code = ContractCode::new(unc_test_contracts::ts_contract().to_vec(), None); + let mut fake_external = MockedExternal::new(); + + let context = create_context(Vec::new()); + let fees = RuntimeFeesConfig::test(); + + // Call method that panics. + let promise_results = vec![]; + let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); + let result = runtime.run( + &code, + "try_panic", + &mut fake_external, + context, + &fees, + &promise_results, + None, + ); + let outcome = result.expect("execution failed"); + assert_eq!( + outcome.aborted, + Some(FunctionCallError::HostError(HostError::GuestPanic { + panic_msg: "explicit guest panic".to_string() + })) + ); + + // Call method that writes something into storage. + let context = create_context(b"foo bar".to_vec()); + runtime + .run( + &code, + "try_storage_write", + &mut fake_external, + context, + &fees, + &promise_results, + None, + ) + .expect("bad failure"); + // Verify by looking directly into the storage of the host. + { + let res = fake_external.storage_get(b"foo", StorageGetMode::Trie); + let value_ptr = res.unwrap().unwrap(); + let value = value_ptr.deref().unwrap(); + let value = String::from_utf8(value).unwrap(); + assert_eq!(value.as_str(), "bar"); + } + + // Call method that reads the value from storage using registers. + let context = create_context(b"foo".to_vec()); + let outcome = runtime + .run( + &code, + "try_storage_read", + &mut fake_external, + context, + &fees, + &promise_results, + None, + ) + .expect("execution failed"); + + if let ReturnData::Value(value) = outcome.return_data { + let value = String::from_utf8(value).unwrap(); + assert_eq!(value, "bar"); + } else { + panic!("Value was not returned"); + } + }); +} diff --git a/runtime/unc-vm-runner/src/tests/wasm_validation.rs b/runtime/unc-vm-runner/src/tests/wasm_validation.rs new file mode 100644 index 000000000..b2a010369 --- /dev/null +++ b/runtime/unc-vm-runner/src/tests/wasm_validation.rs @@ -0,0 +1,122 @@ +use super::test_builder::test_builder; +use super::test_vm_config; +use crate::prepare::prepare_contract; +use crate::tests::with_vm_variants; +use expect_test::expect; + +static SIMD: &str = r#" +(module + (func $entry (result i32) i32.const 0) + (func $test_simd (result i32) + i32.const 42 + i32x4.splat + i32x4.extract_lane 0) + (export "test_simd" (func $test_simd)) +) +"#; + +static THREADS: &str = r#" +(module + (func $entry (result i32) i32.const 0) + (func (export "32.load8u") (param i32) (result i32) + local.get 0 i32.atomic.load8_u) +) +"#; + +static REFERENCE_TYPES: &str = r#" +(module + (func $entry (result i32) i32.const 0) + (table 2 externref) + (elem (i32.const 0) externref (ref.null extern)) + (elem (i32.const 1) externref (ref.null extern)) +) +"#; + +static BULK_MEMORY: &str = r#" +(module + (func $entry (result i32) i32.const 0) + (func (export "memory.copy") (param i32 i32 i32) + local.get 0 + local.get 1 + local.get 2 + memory.copy) +) +"#; + +static MULTI_VALUE: &str = r#" +(module + (func $entry (result i32) i32.const 0) + (func $pick0 (param i64) (result i64 i64) + (get_local 0) (get_local 0)) +) +"#; + +static TAIL_CALL: &str = r#" +(module + (func $entry (result i32) i32.const 0) + (func $const-i32 (result i32) (i32.const 0x132)) + (func (export "type-i32") (result i32) (return_call $const-i32)) +) +"#; + +// WAT does not understand the `register` thing… +// static MODULE_LINKING: &str = r#" +// (module (memory 0) +// (func $entry (result i32) i32.const 0)) +// (register "M") +// "#; + +static MULTI_MEMORY: &str = r#" +(module + (memory 0) + (memory 1) + (func $entry (result i32) i32.const 0)) +"#; + +static MEMORY64: &str = r#" +(module (memory i64 0 0) + (func $entry (result i32) i32.const 0)) +"#; + +static EXCEPTIONS: &str = r#" +(module + (func $entry (result i32) i32.const 0) + (tag $e0 (export "e0")) + (func (export "throw") (throw $e0)) +) +"#; + +static EXPECTED_UNSUPPORTED: &[(&str, &str)] = &[ + ("exceptions", EXCEPTIONS), + ("memory64", MEMORY64), + ("multi_memory", MULTI_MEMORY), + // ("module_linking", MODULE_LINKING), + ("tail_call", TAIL_CALL), + ("multi_value", MULTI_VALUE), + ("bulk_memory", BULK_MEMORY), + ("reference_types", REFERENCE_TYPES), + ("threads", THREADS), + ("simd", SIMD), +]; + +#[test] +fn ensure_fails_verification() { + let config = test_vm_config(); + with_vm_variants(&config, |kind| { + for (feature_name, wat) in EXPECTED_UNSUPPORTED { + let wasm = wat::parse_str(wat).expect("parsing test wat should succeed"); + if let Ok(_) = prepare_contract(&wasm, &config, kind) { + panic!("wasm containing use of {} feature did not fail to prepare", feature_name); + } + } + }); +} + +#[test] +fn ensure_fails_execution() { + for (_feature_name, wat) in EXPECTED_UNSUPPORTED { + test_builder().wat(wat).opaque_error().opaque_outcome().expect(&expect![[r#" + Err: ... + "#]]); + } +} diff --git a/runtime/unc-vm-runner/src/unc_vm_runner.rs b/runtime/unc-vm-runner/src/unc_vm_runner.rs new file mode 100644 index 000000000..688d20c33 --- /dev/null +++ b/runtime/unc-vm-runner/src/unc_vm_runner.rs @@ -0,0 +1,725 @@ +use crate::errors::ContractPrecompilatonResult; +use crate::imports::unc_vm::NearVmImports; +use crate::logic::errors::{ + CacheError, CompilationError, FunctionCallError, MethodResolveError, VMRunnerError, WasmTrap, +}; +use crate::logic::gas_counter::FastGasCounter; +use crate::logic::types::PromiseResult; +use crate::logic::{ + CompiledContract, CompiledContractCache, Config, External, MemSlice, MemoryLike, VMContext, + VMLogic, VMOutcome, +}; +use crate::prepare; +use crate::runner::VMResult; +use crate::{get_contract_cache_key, imports, ContractCode}; +use memoffset::offset_of; +use unc_parameters::vm::VMKind; +use unc_parameters::RuntimeFeesConfig; +use unc_vm_compiler_singlepass::Singlepass; +use unc_vm_engine::universal::{ + LimitedMemoryPool, Universal, UniversalEngine, UniversalExecutable, UniversalExecutableRef, +}; +use unc_vm_types::{FunctionIndex, InstanceConfig, MemoryType, Pages, WASM_PAGE_SIZE}; +use unc_vm_vm::{ + Artifact, Instantiatable, LinearMemory, LinearTable, Memory, MemoryStyle, TrapCode, VMMemory, +}; +use std::borrow::Cow; +use std::hash::Hash; +use std::mem::size_of; +use std::sync::{Arc, OnceLock}; + +#[derive(Clone)] +pub struct NearVmMemory(Arc); + +impl NearVmMemory { + fn new( + initial_memory_pages: u32, + max_memory_pages: u32, + ) -> Result { + let max_pages = Pages(max_memory_pages); + Ok(NearVmMemory(Arc::new(LinearMemory::new( + &MemoryType::new(Pages(initial_memory_pages), Some(max_pages), false), + &MemoryStyle::Static { + bound: max_pages, + offset_guard_size: unc_vm_types::WASM_PAGE_SIZE as u64, + }, + )?))) + } + + /// Returns pointer to memory at the specified offset provided that there’s + /// enough space in the buffer starting at the returned pointer. + /// + /// Safety: Caller must guarantee that the returned pointer is not used + /// after guest memory mapping is changed (e.g. grown). + unsafe fn get_ptr(&self, offset: u64, len: usize) -> Result<*mut u8, ()> { + let offset = usize::try_from(offset).map_err(|_| ())?; + // SAFETY: Caller promisses memory mapping won’t change. + let vmmem = unsafe { self.0.vmmemory().as_ref() }; + // `checked_sub` here verifies that offsetting the buffer by offset + // still lands us in-bounds of the allocated object. + let remaining = vmmem.current_length.checked_sub(offset).ok_or(())?; + if len <= remaining { + Ok(vmmem.base.add(offset)) + } else { + Err(()) + } + } + + /// Returns shared reference to slice in guest memory at given offset. + /// + /// Safety: Caller must guarantee that guest memory mapping is not changed + /// (e.g. grown) while the slice is held. + unsafe fn get(&self, offset: u64, len: usize) -> Result<&[u8], ()> { + // SAFETY: Caller promisses memory mapping won’t change. + let ptr = unsafe { self.get_ptr(offset, len)? }; + // SAFETY: get_ptr verifies that [ptr, ptr+len) is valid slice. + Ok(unsafe { core::slice::from_raw_parts(ptr, len) }) + } + + /// Returns shared reference to slice in guest memory at given offset. + /// + /// Safety: Caller must guarantee that guest memory mapping is not changed + /// (e.g. grown) while the slice is held. + unsafe fn get_mut(&mut self, offset: u64, len: usize) -> Result<&mut [u8], ()> { + // SAFETY: Caller promisses memory mapping won’t change. + let ptr = unsafe { self.get_ptr(offset, len)? }; + // SAFETY: get_ptr verifies that [ptr, ptr+len) is valid slice and since + // we’re holding exclusive self reference another mut reference won’t be + // created + Ok(unsafe { core::slice::from_raw_parts_mut(ptr, len) }) + } + + pub(crate) fn vm(&self) -> VMMemory { + VMMemory { from: self.0.clone(), instance_ref: None } + } +} + +impl MemoryLike for NearVmMemory { + fn fits_memory(&self, slice: MemSlice) -> Result<(), ()> { + // SAFETY: Contracts are executed on a single thread thus we know no one + // will change guest memory mapping under us. + unsafe { self.get_ptr(slice.ptr, slice.len()?) }.map(|_| ()) + } + + fn view_memory(&self, slice: MemSlice) -> Result, ()> { + // SAFETY: Firstly, contracts are executed on a single thread thus we + // know no one will change guest memory mapping under us. Secondly, the + // way MemoryLike interface is used we know the memory mapping won’t be + // changed by the caller while it holds the slice reference. + unsafe { self.get(slice.ptr, slice.len()?) }.map(Cow::Borrowed) + } + + fn read_memory(&self, offset: u64, buffer: &mut [u8]) -> Result<(), ()> { + // SAFETY: Contracts are executed on a single thread thus we know no one + // will change guest memory mapping under us. + Ok(buffer.copy_from_slice(unsafe { self.get(offset, buffer.len())? })) + } + + fn write_memory(&mut self, offset: u64, buffer: &[u8]) -> Result<(), ()> { + // SAFETY: Contracts are executed on a single thread thus we know no one + // will change guest memory mapping under us. + Ok(unsafe { self.get_mut(offset, buffer.len())? }.copy_from_slice(buffer)) + } +} + +fn get_entrypoint_index( + artifact: &unc_vm_engine::universal::UniversalArtifact, + method_name: &str, +) -> Result { + if method_name.is_empty() { + // Do we really need this code? + return Err(FunctionCallError::MethodResolveError(MethodResolveError::MethodEmptyName)); + } + if let Some(unc_vm_types::ExportIndex::Function(index)) = artifact.export_field(method_name) { + let signature = artifact.function_signature(index).expect("index should produce signature"); + let signature = + artifact.engine().lookup_signature(signature).expect("signature store invlidated?"); + if signature.params().is_empty() && signature.results().is_empty() { + Ok(index) + } else { + Err(FunctionCallError::MethodResolveError(MethodResolveError::MethodInvalidSignature)) + } + } else { + Err(FunctionCallError::MethodResolveError(MethodResolveError::MethodNotFound)) + } +} + +fn translate_runtime_error( + error: unc_vm_engine::RuntimeError, + logic: &mut VMLogic, +) -> Result { + // Errors produced by host function calls also become `RuntimeError`s that wrap a dynamic + // instance of `VMLogicError` internally. See the implementation of `NearVmImports`. + let error = match error.downcast::() { + Ok(vm_logic) => { + return vm_logic.try_into(); + } + Err(original) => original, + }; + let msg = error.message(); + let trap_code = error.to_trap().unwrap_or_else(|| { + panic!("runtime error is not a trap: {}", msg); + }); + Ok(match trap_code { + TrapCode::GasExceeded => FunctionCallError::HostError(logic.process_gas_limit()), + TrapCode::StackOverflow => FunctionCallError::WasmTrap(WasmTrap::StackOverflow), + TrapCode::HeapAccessOutOfBounds => FunctionCallError::WasmTrap(WasmTrap::MemoryOutOfBounds), + TrapCode::HeapMisaligned => FunctionCallError::WasmTrap(WasmTrap::MisalignedAtomicAccess), + TrapCode::TableAccessOutOfBounds => { + FunctionCallError::WasmTrap(WasmTrap::MemoryOutOfBounds) + } + TrapCode::OutOfBounds => FunctionCallError::WasmTrap(WasmTrap::MemoryOutOfBounds), + TrapCode::IndirectCallToNull => FunctionCallError::WasmTrap(WasmTrap::IndirectCallToNull), + TrapCode::BadSignature => { + FunctionCallError::WasmTrap(WasmTrap::IncorrectCallIndirectSignature) + } + TrapCode::IntegerOverflow => FunctionCallError::WasmTrap(WasmTrap::IllegalArithmetic), + TrapCode::IntegerDivisionByZero => FunctionCallError::WasmTrap(WasmTrap::IllegalArithmetic), + TrapCode::BadConversionToInteger => { + FunctionCallError::WasmTrap(WasmTrap::IllegalArithmetic) + } + TrapCode::UnreachableCodeReached => FunctionCallError::WasmTrap(WasmTrap::Unreachable), + TrapCode::UnalignedAtomic => FunctionCallError::WasmTrap(WasmTrap::MisalignedAtomicAccess), + }) +} + +#[derive(Hash, PartialEq, Debug)] +#[allow(unused)] +enum NearVmEngine { + Universal = 1, + StaticLib = 2, + DynamicLib = 3, +} + +#[derive(Hash, PartialEq, Debug)] +#[allow(unused)] +enum NearVmCompiler { + Singlepass = 1, + Cranelift = 2, + Llvm = 3, +} + +#[derive(Hash)] +struct NearVmConfig { + seed: u32, + engine: NearVmEngine, + compiler: NearVmCompiler, +} + +impl NearVmConfig { + fn config_hash(self: Self) -> u64 { + crate::utils::stable_hash(&self) + } +} + +// We use following scheme for the bits forming seed: +// kind << 29, kind 2 is for NearVm +// major version << 6 +// minor version +const VM_CONFIG: NearVmConfig = NearVmConfig { + seed: (2 << 29) | (2 << 6) | 1, + engine: NearVmEngine::Universal, + compiler: NearVmCompiler::Singlepass, +}; + +pub(crate) fn unc_vm_vm_hash() -> u64 { + VM_CONFIG.config_hash() +} + +pub(crate) type VMArtifact = Arc; + +pub(crate) struct NearVM { + pub(crate) config: Config, + pub(crate) engine: UniversalEngine, +} + +impl NearVM { + pub(crate) fn new_for_target(config: Config, target: unc_vm_compiler::Target) -> Self { + // We only support singlepass compiler at the moment. + assert_eq!(VM_CONFIG.compiler, NearVmCompiler::Singlepass); + let mut compiler = Singlepass::new(); + compiler.set_9393_fix(!config.disable_9393_fix); + // We only support universal engine at the moment. + assert_eq!(VM_CONFIG.engine, NearVmEngine::Universal); + + static CODE_MEMORY_POOL_CELL: OnceLock = OnceLock::new(); + let code_memory_pool = CODE_MEMORY_POOL_CELL + .get_or_init(|| { + // FIXME: should have as many code memories as there are possible parallel + // invocations of the runtime… How do we determine that? Should we make it + // configurable for the node operators, perhaps, so that they can make an informed + // choice based on the amount of memory they have and shards they track? Should we + // actually use some sort of semaphore to enforce a parallelism limit? + // + // NB: 64MiB is a best guess as to what the maximum size a loaded artifact can + // plausibly be. This is not necessarily true – there may be WebAssembly + // instructions that expand by more than 4 times in terms of instruction size after + // a conversion to x86_64, In that case a re-allocation will occur and executing + // that particular function call will be slower. Not to mention there isn't a + // strong guarantee on the upper bound of the memory that the contract runtime may + // require. + LimitedMemoryPool::new(8, 64 * 1024 * 1024).unwrap_or_else(|e| { + panic!("could not pre-allocate resources for the runtime: {e}"); + }) + }) + .clone(); + + let features = + crate::features::WasmFeatures::from(config.limit_config.contract_prepare_version); + Self { + config, + engine: Universal::new(compiler) + .target(target) + .features(features.into()) + .code_memory_pool(code_memory_pool) + .engine(), + } + } + + pub(crate) fn new(config: Config) -> Self { + use unc_vm_compiler::{CpuFeature, Target, Triple}; + let target_features = if cfg!(feature = "no_cpu_compatibility_checks") { + let mut fs = CpuFeature::set(); + // These features should be sufficient to run the single pass compiler. + fs.insert(CpuFeature::SSE2); + fs.insert(CpuFeature::SSE3); + fs.insert(CpuFeature::SSSE3); + fs.insert(CpuFeature::SSE41); + fs.insert(CpuFeature::SSE42); + fs.insert(CpuFeature::POPCNT); + fs.insert(CpuFeature::AVX); + fs + } else { + CpuFeature::for_host() + }; + Self::new_for_target(config, Target::new(Triple::host(), target_features)) + } + + pub(crate) fn compile_uncached( + &self, + code: &ContractCode, + ) -> Result { + let _span = tracing::debug_span!(target: "vm", "NearVM::compile_uncached").entered(); + let prepared_code = prepare::prepare_contract(code.code(), &self.config, VMKind::NearVm) + .map_err(CompilationError::PrepareError)?; + + debug_assert!( + matches!(self.engine.validate(&prepared_code), Ok(_)), + "unc_vm failed to validate the prepared code" + ); + let executable = self + .engine + .compile_universal(&prepared_code, &self) + .map_err(|err| { + tracing::error!(?err, "unc_vm failed to compile the prepared code (this is defense-in-depth, the error was recovered from but should be reported to pagoda)"); + CompilationError::WasmerCompileError { msg: err.to_string() } + })?; + Ok(executable) + } + + fn compile_and_cache( + &self, + code: &ContractCode, + cache: Option<&dyn CompiledContractCache>, + ) -> Result, CacheError> { + let executable_or_error = self.compile_uncached(code); + let key = get_contract_cache_key(code, &self.config); + + if let Some(cache) = cache { + let record = match &executable_or_error { + Ok(executable) => { + let code = executable + .serialize() + .map_err(|_e| CacheError::SerializationError { hash: key.0 })?; + CompiledContract::Code(code) + } + Err(err) => CompiledContract::CompileModuleError(err.clone()), + }; + cache.put(&key, record).map_err(CacheError::WriteError)?; + } + + Ok(executable_or_error) + } + + fn compile_and_load( + &self, + code: &ContractCode, + cache: Option<&dyn CompiledContractCache>, + ) -> VMResult> { + // `cache` stores compiled machine code in the database + // + // Caches also cache _compilation_ errors, so that we don't have to + // re-parse invalid code (invalid code, in a sense, is a normal + // outcome). And `cache`, being a database, can fail with an `io::Error`. + let _span = tracing::debug_span!(target: "vm", "NearVM::compile_and_load").entered(); + let key = get_contract_cache_key(code, &self.config); + let cache_record = cache + .map(|cache| cache.get(&key)) + .transpose() + .map_err(CacheError::ReadError)? + .flatten(); + + let stored_artifact: Option = match cache_record { + None => None, + Some(CompiledContract::CompileModuleError(err)) => return Ok(Err(err)), + Some(CompiledContract::Code(serialized_module)) => { + let _span = tracing::debug_span!(target: "vm", "NearVM::read_from_cache").entered(); + unsafe { + // (UN-)SAFETY: the `serialized_module` must have been produced by a prior call to + // `serialize`. + // + // In practice this is not necessarily true. One could have forgotten to change the + // cache key when upgrading the version of the unc_vm library or the database could + // have had its data corrupted while at rest. + // + // There should definitely be some validation in unc_vm to ensure we load what we think + // we load. + let executable = UniversalExecutableRef::deserialize(&serialized_module) + .map_err(|_| CacheError::DeserializationError)?; + let artifact = self + .engine + .load_universal_executable_ref(&executable) + .map(Arc::new) + .map_err(|err| VMRunnerError::LoadingError(err.to_string()))?; + Some(artifact) + } + } + }; + + Ok(if let Some(it) = stored_artifact { + Ok(it) + } else { + match self.compile_and_cache(code, cache)? { + Ok(executable) => Ok(self + .engine + .load_universal_executable(&executable) + .map(Arc::new) + .map_err(|err| VMRunnerError::LoadingError(err.to_string()))?), + Err(err) => Err(err), + } + }) + } + + fn run_method( + &self, + artifact: &VMArtifact, + mut import: NearVmImports<'_, '_, '_>, + method_name: &str, + ) -> Result, VMRunnerError> { + let _span = tracing::debug_span!(target: "vm", "run_method").entered(); + + // FastGasCounter in Nearcore must be reinterpret_cast-able to the one in NearVm. + assert_eq!( + size_of::(), + size_of::() + size_of::() + ); + assert_eq!( + offset_of!(FastGasCounter, burnt_gas), + offset_of!(unc_vm_types::FastGasCounter, burnt_gas) + ); + assert_eq!( + offset_of!(FastGasCounter, gas_limit), + offset_of!(unc_vm_types::FastGasCounter, gas_limit) + ); + let gas = import.vmlogic.gas_counter_pointer() as *mut unc_vm_types::FastGasCounter; + let entrypoint = match get_entrypoint_index(&*artifact, method_name) { + Ok(index) => index, + Err(abort) => return Ok(Err(abort)), + }; + unsafe { + let instance = { + let _span = tracing::debug_span!(target: "vm", "run_method/instantiate").entered(); + // An important caveat is that the `'static` lifetime here refers to the lifetime + // of `VMLogic` reference to which is retained by the `InstanceHandle` we create. + // However this `InstanceHandle` only lives during the execution of this body, so + // we can be sure that `VMLogic` remains live and valid at any time. + // SAFETY: we ensure that the tables are valid during the lifetime of this instance + // by retaining an instance to `UniversalEngine` which holds the allocations. + let maybe_handle = Arc::clone(artifact).instantiate( + &self, + &mut import, + Box::new(()), + // SAFETY: We have verified that the `FastGasCounter` layout matches the + // expected layout. `gas` remains dereferenceable throughout this function + // by the virtue of it being contained within `import` which lives for the + // entirety of this function. + InstanceConfig::with_stack_limit(self.config.limit_config.max_stack_height) + .with_counter(gas), + ); + let handle = match maybe_handle { + Ok(handle) => handle, + Err(err) => { + use unc_vm_engine::InstantiationError::*; + let abort = match err { + Start(err) => translate_runtime_error(err, import.vmlogic)?, + Link(e) => FunctionCallError::LinkError { msg: e.to_string() }, + CpuFeature(e) => panic!( + "host doesn't support the CPU features needed to run contracts: {}", + e + ), + }; + return Ok(Err(abort)); + } + }; + // SAFETY: being called immediately after instantiation. + match handle.finish_instantiation() { + Ok(handle) => handle, + Err(trap) => { + let abort = translate_runtime_error( + unc_vm_engine::RuntimeError::from_trap(trap), + import.vmlogic, + )?; + return Ok(Err(abort)); + } + }; + handle + }; + if let Some(function) = instance.function_by_index(entrypoint) { + let _span = tracing::debug_span!(target: "vm", "run_method/call").entered(); + // Signature for the entry point should be `() -> ()`. This is only a sanity check + // – this should've been already checked by `get_entrypoint_index`. + let signature = artifact + .engine() + .lookup_signature(function.signature) + .expect("extern type should refer to valid signature"); + if signature.params().is_empty() && signature.results().is_empty() { + let trampoline = + function.call_trampoline.expect("externs always have a trampoline"); + // SAFETY: we double-checked the signature, and all of the remaining arguments + // come from an exported function definition which must be valid since it comes + // from unc_vm itself. + let res = instance.invoke_function( + function.vmctx, + trampoline, + function.address, + [].as_mut_ptr() as *mut _, + ); + if let Err(trap) = res { + let abort = translate_runtime_error( + unc_vm_engine::RuntimeError::from_trap(trap), + import.vmlogic, + )?; + return Ok(Err(abort)); + } + } else { + panic!("signature should've already been checked by `get_entrypoint_index`") + } + } else { + panic!("signature should've already been checked by `get_entrypoint_index`") + } + + { + let _span = + tracing::debug_span!(target: "vm", "run_method/drop_instance").entered(); + drop(instance) + } + } + + Ok(Ok(())) + } +} + +impl unc_vm_vm::Tunables for &NearVM { + fn memory_style(&self, memory: &MemoryType) -> MemoryStyle { + MemoryStyle::Static { + bound: memory.maximum.unwrap_or(Pages(self.config.limit_config.max_memory_pages)), + offset_guard_size: WASM_PAGE_SIZE as u64, + } + } + + fn table_style(&self, _table: &unc_vm_types::TableType) -> unc_vm_vm::TableStyle { + unc_vm_vm::TableStyle::CallerChecksSignature + } + + fn create_host_memory( + &self, + ty: &MemoryType, + _style: &MemoryStyle, + ) -> Result, unc_vm_vm::MemoryError> { + // We do not support arbitrary Host memories. The only memory contracts may use is the + // memory imported via `env.memory`. + Err(unc_vm_vm::MemoryError::CouldNotGrow { + current: Pages(0), + attempted_delta: ty.minimum, + }) + } + + unsafe fn create_vm_memory( + &self, + ty: &MemoryType, + _style: &MemoryStyle, + _vm_definition_location: std::ptr::NonNull, + ) -> Result, unc_vm_vm::MemoryError> { + // We do not support VM memories. The only memory contracts may use is the memory imported + // via `env.memory`. + Err(unc_vm_vm::MemoryError::CouldNotGrow { + current: Pages(0), + attempted_delta: ty.minimum, + }) + } + + fn create_host_table( + &self, + _ty: &unc_vm_types::TableType, + _style: &unc_vm_vm::TableStyle, + ) -> Result, String> { + panic!("should never be called") + } + + unsafe fn create_vm_table( + &self, + ty: &unc_vm_types::TableType, + style: &unc_vm_vm::TableStyle, + vm_definition_location: std::ptr::NonNull, + ) -> Result, String> { + // This is called when instantiating a module. + Ok(Arc::new(LinearTable::from_definition(&ty, &style, vm_definition_location)?)) + } + + fn stack_init_gas_cost(&self, stack_size: u64) -> u64 { + u64::from(self.config.regular_op_cost).saturating_mul((stack_size + 7) / 8) + } + + /// Instrumentation configuration: stack limiter config + fn stack_limiter_cfg(&self) -> Box { + Box::new(MaxStackCfg) + } + + /// Instrumentation configuration: gas accounting config + fn gas_cfg(&self) -> Box> { + Box::new(GasCostCfg(u64::from(self.config.regular_op_cost))) + } +} + +struct MaxStackCfg; + +impl finite_wasm::max_stack::SizeConfig for MaxStackCfg { + fn size_of_value(&self, ty: finite_wasm::wasmparser::ValType) -> u8 { + use finite_wasm::wasmparser::ValType; + match ty { + ValType::I32 => 4, + ValType::I64 => 8, + ValType::F32 => 4, + ValType::F64 => 8, + ValType::V128 => 16, + ValType::Ref(_) => 8, + } + } + fn size_of_function_activation( + &self, + locals: &prefix_sum_vec::PrefixSumVec, + ) -> u64 { + let mut res = 64_u64; // Rough accounting for rip, rbp and some registers spilled. Not exact. + let mut last_idx_plus_one = 0_u64; + for (idx, local) in locals { + let idx = u64::from(*idx); + res = res.saturating_add( + idx.checked_sub(last_idx_plus_one) + .expect("prefix-sum-vec indices went backwards") + .saturating_add(1) + .saturating_mul(u64::from(self.size_of_value(*local))), + ); + last_idx_plus_one = idx.saturating_add(1); + } + res + } +} + +struct GasCostCfg(u64); + +macro_rules! gas_cost { + ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { + $( + fn $visit(&mut self $($(, $arg: $argty)*)?) -> u64 { + gas_cost!(@@$proposal $op self $({ $($arg: $argty),* })? => $visit) + } + )* + }; + + (@@mvp $_op:ident $_self:ident $({ $($_arg:ident: $_argty:ty),* })? => visit_block) => { + 0 + }; + (@@mvp $_op:ident $_self:ident $({ $($_arg:ident: $_argty:ty),* })? => visit_end) => { + 0 + }; + (@@mvp $_op:ident $_self:ident $({ $($_arg:ident: $_argty:ty),* })? => visit_else) => { + 0 + }; + (@@$_proposal:ident $_op:ident $self:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident) => { + $self.0 + }; +} + +impl<'a> finite_wasm::wasmparser::VisitOperator<'a> for GasCostCfg { + type Output = u64; + finite_wasm::wasmparser::for_each_operator!(gas_cost); +} + +impl crate::runner::VM for NearVM { + fn run( + &self, + code: &ContractCode, + method_name: &str, + ext: &mut dyn External, + context: VMContext, + fees_config: &RuntimeFeesConfig, + promise_results: &[PromiseResult], + cache: Option<&dyn CompiledContractCache>, + ) -> Result { + let mut memory = NearVmMemory::new( + self.config.limit_config.initial_memory_pages, + self.config.limit_config.max_memory_pages, + ) + .expect("Cannot create memory for a contract call"); + + // FIXME: this mostly duplicates the `run_module` method. + // Note that we don't clone the actual backing memory, just increase the RC. + let vmmemory = memory.vm(); + let mut logic = + VMLogic::new(ext, context, &self.config, fees_config, promise_results, &mut memory); + + let result = logic.before_loading_executable(method_name, code.code().len()); + if let Err(e) = result { + return Ok(VMOutcome::abort(logic, e)); + } + + let artifact = self.compile_and_load(code, cache)?; + let artifact = match artifact { + Ok(it) => it, + Err(err) => { + return Ok(VMOutcome::abort(logic, FunctionCallError::CompilationError(err))); + } + }; + + let result = logic.after_loading_executable(code.code().len()); + if let Err(e) = result { + return Ok(VMOutcome::abort(logic, e)); + } + let import = imports::unc_vm::build(vmmemory, &mut logic, artifact.engine()); + if let Err(e) = get_entrypoint_index(&*artifact, method_name) { + return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol(logic, e)); + } + match self.run_method(&artifact, import, method_name)? { + Ok(()) => Ok(VMOutcome::ok(logic)), + Err(err) => Ok(VMOutcome::abort(logic, err)), + } + } + + fn precompile( + &self, + code: &ContractCode, + cache: &dyn CompiledContractCache, + ) -> Result< + Result, + crate::logic::errors::CacheError, + > { + Ok(self + .compile_and_cache(code, Some(cache))? + .map(|_| ContractPrecompilatonResult::ContractCompiled)) + } +} + +#[test] +fn test_memory_like() { + crate::logic::test_utils::test_memory_like(|| Box::new(NearVmMemory::new(1, 1).unwrap())); +} diff --git a/runtime/unc-vm-runner/src/utils.rs b/runtime/unc-vm-runner/src/utils.rs new file mode 100644 index 000000000..a5d453087 --- /dev/null +++ b/runtime/unc-vm-runner/src/utils.rs @@ -0,0 +1,13 @@ +use std::hash::{Hash, Hasher}; + +// This method is not used on macOS so it's fine to allow it to be unused. +#[allow(dead_code)] +pub(crate) fn stable_hash(value: T) -> u64 { + // This is ported over from the previous uses, that relied on unc-stable-hasher. + // The need for stability here can certainly be discussed, and it could probably be replaced with DefaultHasher. + // Not doing it in this refactor as it’s not the core of the issue and using SipHasher is an easy alternative. + #[allow(deprecated)] + let mut hasher = std::hash::SipHasher::new(); + value.hash(&mut hasher); + hasher.finish() +} diff --git a/runtime/unc-vm-runner/src/wasmer2_runner.rs b/runtime/unc-vm-runner/src/wasmer2_runner.rs new file mode 100644 index 000000000..c7a69ef39 --- /dev/null +++ b/runtime/unc-vm-runner/src/wasmer2_runner.rs @@ -0,0 +1,628 @@ +use crate::errors::ContractPrecompilatonResult; +use crate::imports::wasmer2::Wasmer2Imports; +use crate::logic::errors::{ + CacheError, CompilationError, FunctionCallError, MethodResolveError, VMRunnerError, WasmTrap, +}; +use crate::logic::gas_counter::FastGasCounter; +use crate::logic::types::PromiseResult; +use crate::logic::{ + CompiledContract, CompiledContractCache, Config, External, MemSlice, MemoryLike, VMContext, + VMLogic, VMOutcome, +}; +use crate::prepare; +use crate::runner::VMResult; +use crate::{get_contract_cache_key, imports, ContractCode}; +use memoffset::offset_of; +use unc_parameters::vm::VMKind; +use unc_parameters::RuntimeFeesConfig; +use std::borrow::Cow; +use std::hash::Hash; +use std::mem::size_of; +use std::sync::Arc; +use wasmer_compiler_singlepass::Singlepass; +use wasmer_engine::{Engine, Executable}; +use wasmer_engine_universal::{ + Universal, UniversalEngine, UniversalExecutable, UniversalExecutableRef, +}; +use wasmer_types::{FunctionIndex, InstanceConfig, MemoryType, Pages, WASM_PAGE_SIZE}; +use wasmer_vm::{ + Artifact, Instantiatable, LinearMemory, LinearTable, Memory, MemoryStyle, TrapCode, VMMemory, +}; + +#[derive(Clone)] +pub struct Wasmer2Memory(Arc); + +impl Wasmer2Memory { + fn new( + initial_memory_pages: u32, + max_memory_pages: u32, + ) -> Result { + let max_pages = Pages(max_memory_pages); + Ok(Wasmer2Memory(Arc::new(LinearMemory::new( + &MemoryType::new(Pages(initial_memory_pages), Some(max_pages), false), + &MemoryStyle::Static { + bound: max_pages, + offset_guard_size: wasmer_types::WASM_PAGE_SIZE as u64, + }, + )?))) + } + + /// Returns pointer to memory at the specified offset provided that there’s + /// enough space in the buffer starting at the returned pointer. + /// + /// Safety: Caller must guarantee that the returned pointer is not used + /// after guest memory mapping is changed (e.g. grown). + unsafe fn get_ptr(&self, offset: u64, len: usize) -> Result<*mut u8, ()> { + let offset = usize::try_from(offset).map_err(|_| ())?; + // SAFETY: Caller promisses memory mapping won’t change. + let vmmem = unsafe { self.0.vmmemory().as_ref() }; + // `checked_sub` here verifies that offsetting the buffer by offset + // still lands us in-bounds of the allocated object. + let remaining = vmmem.current_length.checked_sub(offset).ok_or(())?; + if len <= remaining { + Ok(vmmem.base.add(offset)) + } else { + Err(()) + } + } + + /// Returns shared reference to slice in guest memory at given offset. + /// + /// Safety: Caller must guarantee that guest memory mapping is not changed + /// (e.g. grown) while the slice is held. + unsafe fn get(&self, offset: u64, len: usize) -> Result<&[u8], ()> { + // SAFETY: Caller promisses memory mapping won’t change. + let ptr = unsafe { self.get_ptr(offset, len)? }; + // SAFETY: get_ptr verifies that [ptr, ptr+len) is valid slice. + Ok(unsafe { core::slice::from_raw_parts(ptr, len) }) + } + + /// Returns shared reference to slice in guest memory at given offset. + /// + /// Safety: Caller must guarantee that guest memory mapping is not changed + /// (e.g. grown) while the slice is held. + unsafe fn get_mut(&mut self, offset: u64, len: usize) -> Result<&mut [u8], ()> { + // SAFETY: Caller promisses memory mapping won’t change. + let ptr = unsafe { self.get_ptr(offset, len)? }; + // SAFETY: get_ptr verifies that [ptr, ptr+len) is valid slice and since + // we’re holding exclusive self reference another mut reference won’t be + // created + Ok(unsafe { core::slice::from_raw_parts_mut(ptr, len) }) + } + + pub(crate) fn vm(&self) -> VMMemory { + VMMemory { from: self.0.clone(), instance_ref: None } + } +} + +impl MemoryLike for Wasmer2Memory { + fn fits_memory(&self, slice: MemSlice) -> Result<(), ()> { + // SAFETY: Contracts are executed on a single thread thus we know no one + // will change guest memory mapping under us. + unsafe { self.get_ptr(slice.ptr, slice.len()?) }.map(|_| ()) + } + + fn view_memory(&self, slice: MemSlice) -> Result, ()> { + // SAFETY: Firstly, contracts are executed on a single thread thus we + // know no one will change guest memory mapping under us. Secondly, the + // way MemoryLike interface is used we know the memory mapping won’t be + // changed by the caller while it holds the slice reference. + unsafe { self.get(slice.ptr, slice.len()?) }.map(Cow::Borrowed) + } + + fn read_memory(&self, offset: u64, buffer: &mut [u8]) -> Result<(), ()> { + // SAFETY: Contracts are executed on a single thread thus we know no one + // will change guest memory mapping under us. + Ok(buffer.copy_from_slice(unsafe { self.get(offset, buffer.len())? })) + } + + fn write_memory(&mut self, offset: u64, buffer: &[u8]) -> Result<(), ()> { + // SAFETY: Contracts are executed on a single thread thus we know no one + // will change guest memory mapping under us. + Ok(unsafe { self.get_mut(offset, buffer.len())? }.copy_from_slice(buffer)) + } +} + +fn get_entrypoint_index( + artifact: &wasmer_engine_universal::UniversalArtifact, + method_name: &str, +) -> Result { + if method_name.is_empty() { + // Do we really need this code? + return Err(FunctionCallError::MethodResolveError(MethodResolveError::MethodEmptyName)); + } + if let Some(wasmer_types::ExportIndex::Function(index)) = artifact.export_field(method_name) { + let signature = artifact.function_signature(index).expect("index should produce signature"); + let signature = + artifact.engine().lookup_signature(signature).expect("signature store invlidated?"); + if signature.params().is_empty() && signature.results().is_empty() { + Ok(index) + } else { + Err(FunctionCallError::MethodResolveError(MethodResolveError::MethodInvalidSignature)) + } + } else { + Err(FunctionCallError::MethodResolveError(MethodResolveError::MethodNotFound)) + } +} + +fn translate_runtime_error( + error: wasmer_engine::RuntimeError, + logic: &mut VMLogic, +) -> Result { + // Errors produced by host function calls also become `RuntimeError`s that wrap a dynamic + // instance of `VMLogicError` internally. See the implementation of `Wasmer2Imports`. + let error = match error.downcast::() { + Ok(vm_logic) => { + return vm_logic.try_into(); + } + Err(original) => original, + }; + let msg = error.message(); + let trap_code = error.to_trap().unwrap_or_else(|| { + panic!("runtime error is not a trap: {}", msg); + }); + Ok(match trap_code { + TrapCode::GasExceeded => FunctionCallError::HostError(logic.process_gas_limit()), + TrapCode::StackOverflow => FunctionCallError::WasmTrap(WasmTrap::StackOverflow), + TrapCode::HeapAccessOutOfBounds => FunctionCallError::WasmTrap(WasmTrap::MemoryOutOfBounds), + TrapCode::HeapMisaligned => FunctionCallError::WasmTrap(WasmTrap::MisalignedAtomicAccess), + TrapCode::TableAccessOutOfBounds => { + FunctionCallError::WasmTrap(WasmTrap::MemoryOutOfBounds) + } + TrapCode::OutOfBounds => FunctionCallError::WasmTrap(WasmTrap::MemoryOutOfBounds), + TrapCode::IndirectCallToNull => FunctionCallError::WasmTrap(WasmTrap::IndirectCallToNull), + TrapCode::BadSignature => { + FunctionCallError::WasmTrap(WasmTrap::IncorrectCallIndirectSignature) + } + TrapCode::IntegerOverflow => FunctionCallError::WasmTrap(WasmTrap::IllegalArithmetic), + TrapCode::IntegerDivisionByZero => FunctionCallError::WasmTrap(WasmTrap::IllegalArithmetic), + TrapCode::BadConversionToInteger => { + FunctionCallError::WasmTrap(WasmTrap::IllegalArithmetic) + } + TrapCode::UnreachableCodeReached => FunctionCallError::WasmTrap(WasmTrap::Unreachable), + TrapCode::UnalignedAtomic => FunctionCallError::WasmTrap(WasmTrap::MisalignedAtomicAccess), + }) +} + +#[derive(Hash, PartialEq, Debug)] +#[allow(unused)] +enum WasmerEngine { + Universal = 1, + StaticLib = 2, + DynamicLib = 3, +} + +#[derive(Hash, PartialEq, Debug)] +#[allow(unused)] +enum WasmerCompiler { + Singlepass = 1, + Cranelift = 2, + Llvm = 3, +} + +#[derive(Hash)] +struct Wasmer2Config { + seed: u32, + engine: WasmerEngine, + compiler: WasmerCompiler, +} + +impl Wasmer2Config { + fn config_hash(self: Self) -> u64 { + crate::utils::stable_hash(&self) + } +} + +// We use following scheme for the bits forming seed: +// kind << 29, kind is 1 for Wasmer2 +// major version << 6 +// minor version +const WASMER2_CONFIG: Wasmer2Config = Wasmer2Config { + seed: (1 << 29) | (12 << 6) | 1, + engine: WasmerEngine::Universal, + compiler: WasmerCompiler::Singlepass, +}; + +pub(crate) fn wasmer2_vm_hash() -> u64 { + WASMER2_CONFIG.config_hash() +} + +pub(crate) type VMArtifact = Arc; + +pub(crate) struct Wasmer2VM { + pub(crate) config: Config, + pub(crate) engine: UniversalEngine, +} + +impl Wasmer2VM { + pub(crate) fn new_for_target(config: Config, target: wasmer_compiler::Target) -> Self { + // We only support singlepass compiler at the moment. + assert_eq!(WASMER2_CONFIG.compiler, WasmerCompiler::Singlepass); + let compiler = Singlepass::new(); + // We only support universal engine at the moment. + assert_eq!(WASMER2_CONFIG.engine, WasmerEngine::Universal); + let features = + crate::features::WasmFeatures::from(config.limit_config.contract_prepare_version); + Self { + config, + engine: Universal::new(compiler).target(target).features(features.into()).engine(), + } + } + + pub(crate) fn new(config: Config) -> Self { + use wasmer_compiler::{CpuFeature, Target, Triple}; + let target_features = if cfg!(feature = "no_cpu_compatibility_checks") { + let mut fs = CpuFeature::set(); + // These features should be sufficient to run the single pass compiler. + fs.insert(CpuFeature::SSE2); + fs.insert(CpuFeature::SSE3); + fs.insert(CpuFeature::SSSE3); + fs.insert(CpuFeature::SSE41); + fs.insert(CpuFeature::SSE42); + fs.insert(CpuFeature::POPCNT); + fs.insert(CpuFeature::AVX); + fs + } else { + CpuFeature::for_host() + }; + Self::new_for_target(config, Target::new(Triple::host(), target_features)) + } + + pub(crate) fn compile_uncached( + &self, + code: &ContractCode, + ) -> Result { + let _span = tracing::debug_span!(target: "vm", "Wasmer2VM::compile_uncached").entered(); + let prepared_code = prepare::prepare_contract(code.code(), &self.config, VMKind::Wasmer2) + .map_err(CompilationError::PrepareError)?; + + debug_assert!( + matches!(self.engine.validate(&prepared_code), Ok(_)), + "wasmer failed to validate the prepared code" + ); + let executable = self + .engine + .compile_universal(&prepared_code, &self) + .map_err(|err| { + tracing::error!(?err, "wasmer failed to compile the prepared code (this is defense-in-depth, the error was recovered from but should be reported to pagoda)"); + CompilationError::WasmerCompileError { msg: err.to_string() } + })?; + Ok(executable) + } + + fn compile_and_cache( + &self, + code: &ContractCode, + cache: Option<&dyn CompiledContractCache>, + ) -> Result, CacheError> { + let executable_or_error = self.compile_uncached(code); + let key = get_contract_cache_key(code, &self.config); + + if let Some(cache) = cache { + let record = match &executable_or_error { + Ok(executable) => { + let code = executable + .serialize() + .map_err(|_e| CacheError::SerializationError { hash: key.0 })?; + CompiledContract::Code(code) + } + Err(err) => CompiledContract::CompileModuleError(err.clone()), + }; + cache.put(&key, record).map_err(CacheError::WriteError)?; + } + + Ok(executable_or_error) + } + + fn compile_and_load( + &self, + code: &ContractCode, + cache: Option<&dyn CompiledContractCache>, + ) -> VMResult> { + // A bit of a tricky logic ahead! We need to deal with two levels of + // caching: + // * `cache` stores compiled machine code in the database + // * `MEM_CACHE` below holds in-memory cache of loaded contracts + // + // Caches also cache _compilation_ errors, so that we don't have to + // re-parse invalid code (invalid code, in a sense, is a normal + // outcome). And `cache`, being a database, can fail with an `io::Error`. + let _span = tracing::debug_span!(target: "vm", "Wasmer2VM::compile_and_load").entered(); + + let key = get_contract_cache_key(code, &self.config); + + let compile_or_read_from_cache = || -> VMResult> { + let _span = tracing::debug_span!(target: "vm", "Wasmer2VM::compile_or_read_from_cache") + .entered(); + let cache_record = cache + .map(|cache| cache.get(&key)) + .transpose() + .map_err(CacheError::ReadError)? + .flatten(); + + let stored_artifact: Option = match cache_record { + None => None, + Some(CompiledContract::CompileModuleError(err)) => return Ok(Err(err)), + Some(CompiledContract::Code(serialized_module)) => { + let _span = + tracing::debug_span!(target: "vm", "Wasmer2VM::read_from_cache").entered(); + unsafe { + // (UN-)SAFETY: the `serialized_module` must have been produced by a prior call to + // `serialize`. + // + // In practice this is not necessarily true. One could have forgotten to change the + // cache key when upgrading the version of the wasmer library or the database could + // have had its data corrupted while at rest. + // + // There should definitely be some validation in wasmer to ensure we load what we think + // we load. + let executable = UniversalExecutableRef::deserialize(&serialized_module) + .map_err(|_| CacheError::DeserializationError)?; + let artifact = self + .engine + .load_universal_executable_ref(&executable) + .map(Arc::new) + .map_err(|err| VMRunnerError::LoadingError(err.to_string()))?; + Some(artifact) + } + } + }; + + Ok(if let Some(it) = stored_artifact { + Ok(it) + } else { + match self.compile_and_cache(code, cache)? { + Ok(executable) => Ok(self + .engine + .load_universal_executable(&executable) + .map(Arc::new) + .map_err(|err| VMRunnerError::LoadingError(err.to_string()))?), + Err(err) => Err(err), + } + }) + }; + + return compile_or_read_from_cache(); + } + + fn run_method( + &self, + artifact: &VMArtifact, + mut import: Wasmer2Imports<'_, '_, '_>, + method_name: &str, + ) -> Result, VMRunnerError> { + let _span = tracing::debug_span!(target: "vm", "run_method").entered(); + + // FastGasCounter in Nearcore and Wasmer must match in layout. + assert_eq!(size_of::(), size_of::()); + assert_eq!( + offset_of!(FastGasCounter, burnt_gas), + offset_of!(wasmer_types::FastGasCounter, burnt_gas) + ); + assert_eq!( + offset_of!(FastGasCounter, gas_limit), + offset_of!(wasmer_types::FastGasCounter, gas_limit) + ); + assert_eq!( + offset_of!(FastGasCounter, opcode_cost), + offset_of!(wasmer_types::FastGasCounter, opcode_cost) + ); + let gas = import.vmlogic.gas_counter_pointer() as *mut wasmer_types::FastGasCounter; + let entrypoint = match get_entrypoint_index(&*artifact, method_name) { + Ok(index) => index, + Err(abort) => return Ok(Err(abort)), + }; + unsafe { + let instance = { + let _span = tracing::debug_span!(target: "vm", "run_method/instantiate").entered(); + // An important caveat is that the `'static` lifetime here refers to the lifetime + // of `VMLogic` reference to which is retained by the `InstanceHandle` we create. + // However this `InstanceHandle` only lives during the execution of this body, so + // we can be sure that `VMLogic` remains live and valid at any time. + // SAFETY: we ensure that the tables are valid during the lifetime of this instance + // by retaining an instance to `UniversalEngine` which holds the allocations. + let maybe_handle = Arc::clone(artifact).instantiate( + &self, + &mut import, + Box::new(()), + // SAFETY: We have verified that the `FastGasCounter` layout matches the + // expected layout. `gas` remains dereferenceable throughout this function + // by the virtue of it being contained within `import` which lives for the + // entirety of this function. + InstanceConfig::default() + .with_counter(gas) + .with_stack_limit(self.config.limit_config.wasmer2_stack_limit), + ); + let handle = match maybe_handle { + Ok(handle) => handle, + Err(err) => { + use wasmer_engine::InstantiationError::*; + let abort = match err { + Start(err) => translate_runtime_error(err, import.vmlogic)?, + Link(e) => FunctionCallError::LinkError { msg: e.to_string() }, + CpuFeature(e) => panic!( + "host doesn't support the CPU features needed to run contracts: {}", + e + ), + }; + return Ok(Err(abort)); + } + }; + // SAFETY: being called immediately after instantiation. + match handle.finish_instantiation() { + Ok(handle) => handle, + Err(trap) => { + let abort = translate_runtime_error( + wasmer_engine::RuntimeError::from_trap(trap), + import.vmlogic, + )?; + return Ok(Err(abort)); + } + }; + handle + }; + if let Some(function) = instance.function_by_index(entrypoint) { + let _span = tracing::debug_span!(target: "vm", "run_method/call").entered(); + // Signature for the entry point should be `() -> ()`. This is only a sanity check + // – this should've been already checked by `get_entrypoint_index`. + let signature = artifact + .engine() + .lookup_signature(function.signature) + .expect("extern type should refer to valid signature"); + if signature.params().is_empty() && signature.results().is_empty() { + let trampoline = + function.call_trampoline.expect("externs always have a trampoline"); + // SAFETY: we double-checked the signature, and all of the remaining arguments + // come from an exported function definition which must be valid since it comes + // from wasmer itself. + let res = instance.invoke_function( + function.vmctx, + trampoline, + function.address, + [].as_mut_ptr() as *mut _, + ); + if let Err(trap) = res { + let abort = translate_runtime_error( + wasmer_engine::RuntimeError::from_trap(trap), + import.vmlogic, + )?; + return Ok(Err(abort)); + } + } else { + panic!("signature should've already been checked by `get_entrypoint_index`") + } + } else { + panic!("signature should've already been checked by `get_entrypoint_index`") + } + + { + let _span = + tracing::debug_span!(target: "vm", "run_method/drop_instance").entered(); + drop(instance) + } + } + + Ok(Ok(())) + } +} + +impl wasmer_vm::Tunables for &Wasmer2VM { + fn memory_style(&self, memory: &MemoryType) -> MemoryStyle { + MemoryStyle::Static { + bound: memory.maximum.unwrap_or(Pages(self.config.limit_config.max_memory_pages)), + offset_guard_size: WASM_PAGE_SIZE as u64, + } + } + + fn table_style(&self, _table: &wasmer_types::TableType) -> wasmer_vm::TableStyle { + wasmer_vm::TableStyle::CallerChecksSignature + } + + fn create_host_memory( + &self, + ty: &MemoryType, + _style: &MemoryStyle, + ) -> Result, wasmer_vm::MemoryError> { + // We do not support arbitrary Host memories. The only memory contracts may use is the + // memory imported via `env.memory`. + Err(wasmer_vm::MemoryError::CouldNotGrow { current: Pages(0), attempted_delta: ty.minimum }) + } + + unsafe fn create_vm_memory( + &self, + ty: &MemoryType, + _style: &MemoryStyle, + _vm_definition_location: std::ptr::NonNull, + ) -> Result, wasmer_vm::MemoryError> { + // We do not support VM memories. The only memory contracts may use is the memory imported + // via `env.memory`. + Err(wasmer_vm::MemoryError::CouldNotGrow { current: Pages(0), attempted_delta: ty.minimum }) + } + + fn create_host_table( + &self, + _ty: &wasmer_types::TableType, + _style: &wasmer_vm::TableStyle, + ) -> Result, String> { + panic!("should never be called") + } + + unsafe fn create_vm_table( + &self, + ty: &wasmer_types::TableType, + style: &wasmer_vm::TableStyle, + vm_definition_location: std::ptr::NonNull, + ) -> Result, String> { + // This is called when instantiating a module. + Ok(Arc::new(LinearTable::from_definition(&ty, &style, vm_definition_location)?)) + } +} + +impl crate::runner::VM for Wasmer2VM { + fn run( + &self, + code: &ContractCode, + method_name: &str, + ext: &mut dyn External, + context: VMContext, + fees_config: &RuntimeFeesConfig, + promise_results: &[PromiseResult], + cache: Option<&dyn CompiledContractCache>, + ) -> Result { + let mut memory = Wasmer2Memory::new( + self.config.limit_config.initial_memory_pages, + self.config.limit_config.max_memory_pages, + ) + .expect("Cannot create memory for a contract call"); + + // FIXME: this mostly duplicates the `run_module` method. + // Note that we don't clone the actual backing memory, just increase the RC. + let vmmemory = memory.vm(); + let mut logic = + VMLogic::new(ext, context, &self.config, fees_config, promise_results, &mut memory); + + let result = logic.before_loading_executable(method_name, code.code().len()); + if let Err(e) = result { + return Ok(VMOutcome::abort(logic, e)); + } + + let artifact = self.compile_and_load(code, cache)?; + let artifact = match artifact { + Ok(it) => it, + Err(err) => { + return Ok(VMOutcome::abort(logic, FunctionCallError::CompilationError(err))); + } + }; + + let result = logic.after_loading_executable(code.code().len()); + if let Err(e) = result { + return Ok(VMOutcome::abort(logic, e)); + } + let import = imports::wasmer2::build(vmmemory, &mut logic, artifact.engine()); + if let Err(e) = get_entrypoint_index(&*artifact, method_name) { + return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol(logic, e)); + } + match self.run_method(&artifact, import, method_name)? { + Ok(()) => Ok(VMOutcome::ok(logic)), + Err(err) => Ok(VMOutcome::abort(logic, err)), + } + } + + fn precompile( + &self, + code: &ContractCode, + cache: &dyn CompiledContractCache, + ) -> Result< + Result, + crate::logic::errors::CacheError, + > { + Ok(self + .compile_and_cache(code, Some(cache))? + .map(|_| ContractPrecompilatonResult::ContractCompiled)) + } +} + +#[test] +fn test_memory_like() { + crate::logic::test_utils::test_memory_like(|| Box::new(Wasmer2Memory::new(1, 1).unwrap())); +} diff --git a/runtime/unc-vm-runner/src/wasmer_runner.rs b/runtime/unc-vm-runner/src/wasmer_runner.rs new file mode 100644 index 000000000..aa609c689 --- /dev/null +++ b/runtime/unc-vm-runner/src/wasmer_runner.rs @@ -0,0 +1,425 @@ +use crate::errors::{ContractPrecompilatonResult, IntoVMError}; +use crate::logic::errors::{ + CacheError, CompilationError, FunctionCallError, MethodResolveError, VMRunnerError, WasmTrap, +}; +use crate::logic::types::PromiseResult; +use crate::logic::{ + CompiledContract, CompiledContractCache, External, VMContext, VMLogic, VMLogicError, VMOutcome, +}; +use crate::memory::WasmerMemory; +use crate::prepare; +use crate::runner::VMResult; +use crate::{get_contract_cache_key, imports, ContractCode}; +use unc_parameters::vm::{Config, VMKind}; +use unc_parameters::RuntimeFeesConfig; +use wasmer_runtime::{ImportObject, Module}; + +fn check_method(module: &Module, method_name: &str) -> Result<(), FunctionCallError> { + let info = module.info(); + use wasmer_runtime_core::module::ExportIndex::Func; + if let Some(Func(index)) = info.exports.map.get(method_name) { + let func = info.func_assoc.get(*index).unwrap(); + let sig = info.signatures.get(*func).unwrap(); + if sig.params().is_empty() && sig.returns().is_empty() { + Ok(()) + } else { + Err(FunctionCallError::MethodResolveError(MethodResolveError::MethodInvalidSignature)) + } + } else { + Err(FunctionCallError::MethodResolveError(MethodResolveError::MethodNotFound)) + } +} + +impl IntoVMError for wasmer_runtime::error::Error { + fn into_vm_error(self) -> Result { + use wasmer_runtime::error::Error; + match self { + Error::CompileError(err) => { + Ok(FunctionCallError::CompilationError(CompilationError::WasmerCompileError { + msg: err.to_string(), + })) + } + Error::LinkError(err) => Ok(FunctionCallError::LinkError { + msg: format!("{:.500}", Error::LinkError(err).to_string()), + }), + Error::RuntimeError(err) => err.into_vm_error(), + Error::ResolveError(err) => err.into_vm_error(), + Error::CallError(err) => err.into_vm_error(), + Error::CreationError(err) => panic!("{}", err), + } + } +} + +impl IntoVMError for wasmer_runtime::error::CallError { + fn into_vm_error(self) -> Result { + use wasmer_runtime::error::CallError; + match self { + CallError::Resolve(err) => err.into_vm_error(), + CallError::Runtime(err) => err.into_vm_error(), + } + } +} + +impl IntoVMError for wasmer_runtime::error::ResolveError { + fn into_vm_error(self) -> Result { + use wasmer_runtime::error::ResolveError as WasmerResolveError; + Ok(match self { + WasmerResolveError::Signature { .. } => { + FunctionCallError::MethodResolveError(MethodResolveError::MethodInvalidSignature) + } + WasmerResolveError::ExportNotFound { .. } => { + FunctionCallError::MethodResolveError(MethodResolveError::MethodNotFound) + } + WasmerResolveError::ExportWrongType { .. } => { + FunctionCallError::MethodResolveError(MethodResolveError::MethodNotFound) + } + }) + } +} + +impl IntoVMError for wasmer_runtime::error::RuntimeError { + fn into_vm_error(self) -> Result { + use wasmer_runtime::error::{InvokeError, RuntimeError}; + match self { + RuntimeError::InvokeError(invoke_error) => match invoke_error { + // Indicates an exceptional circumstance such as a bug in Wasmer + // or a hardware failure. + // As of 0.17.0, thrown when stack unwinder fails, or when + // invoke returns false and doesn't fill error info what Singlepass BE doesn't. + // Failed unwinder may happen in the case of deep recursion/stack overflow. + // Also can be thrown on unreachable instruction, which is quite unfortunate. + // + // See https://github.com/near/wasmer/blob/0.17.2/lib/runtime-core/src/fault.rs#L285 + InvokeError::FailedWithNoError => { + tracing::error!(target: "vm", "Got FailedWithNoError from wasmer0"); + // XXX: Initially, we treated this error case as + // deterministic (so, we stored this error in our state, + // etc.) + // + // Then, in + // https://github.com/utnet-org/utility/pull/4181#discussion_r606267838 + // we reasoned that this error actually happens + // non-deterministically, so it's better to panic in this + // case. + // + // However, when rolling this out, we noticed that this + // error happens deterministically for at least one + // contract. So here we roll this back to a previous + // behavior and emit some deterministic error, which won't + // cause the node to panic. + // + // So far, we are unable to reproduce this deterministic + // failure though. + Ok(FunctionCallError::WasmTrap(WasmTrap::Unreachable)) + } + // Indicates that a trap occurred that is not known to Wasmer. + // As of 0.17.0, thrown only from Cranelift BE. + InvokeError::UnknownTrap { address, signal } => { + panic!( + "Impossible UnknownTrap error (Cranelift only): signal {} at {}", + signal, address + ); + } + // A trap that Wasmer knows about occurred. + InvokeError::TrapCode { code, srcloc: _ } => Ok(match code { + wasmer_runtime::ExceptionCode::Unreachable => { + FunctionCallError::WasmTrap(WasmTrap::Unreachable) + } + wasmer_runtime::ExceptionCode::IncorrectCallIndirectSignature => { + FunctionCallError::WasmTrap(WasmTrap::IncorrectCallIndirectSignature) + } + wasmer_runtime::ExceptionCode::MemoryOutOfBounds => { + FunctionCallError::WasmTrap(WasmTrap::MemoryOutOfBounds) + } + wasmer_runtime::ExceptionCode::CallIndirectOOB => { + FunctionCallError::WasmTrap(WasmTrap::CallIndirectOOB) + } + wasmer_runtime::ExceptionCode::IllegalArithmetic => { + FunctionCallError::WasmTrap(WasmTrap::IllegalArithmetic) + } + wasmer_runtime::ExceptionCode::MisalignedAtomicAccess => { + FunctionCallError::WasmTrap(WasmTrap::MisalignedAtomicAccess) + } + }), + // A trap occurred that Wasmer knows about but it had a trap code that + // we weren't expecting or that we do not handle. + // As of 0.17.0, thrown only from Cranelift BE. + InvokeError::UnknownTrapCode { trap_code, srcloc } => { + panic!( + "Impossible UnknownTrapCode error (Cranelift only): trap {} at {}", + trap_code, srcloc + ); + } + // An "early trap" occurred. + // As of 0.17.0, thrown only from Cranelift BE. + InvokeError::EarlyTrap(_) => { + panic!("Impossible EarlyTrap error (Cranelift only)"); + } + // Indicates that a breakpoint was hit. The inner value is dependent + // upon the middleware or backend being used. + // As of 0.17.0, thrown only from Singlepass BE and wraps RuntimeError + // instance. + InvokeError::Breakpoint(_) => unreachable!("Wasmer breakpoint"), + }, + // A metering triggered error value. + // As of 0.17.0, thrown only from Singlepass BE, and as we do not rely + // on Wasmer metering system cannot be returned to us. Whenever we will + // shall be rechecked. + RuntimeError::Metering(_) => { + panic!("Support metering errors properly"); + } + // A frozen state of Wasm used to pause and resume execution. + // As of 0.17.0, can be activated when special memory page + // (see get_wasm_interrupt_signal_mem()) is accessed. + // This address is passed via InternalCtx.interrupt_signal_mem + // to the runtime, and is triggered only from do_optimize(). + // do_optimize() is only called if backend is mentioned in + // Run.optimized_backends option, and we don't. + RuntimeError::InstanceImage(_) => { + panic!("Support instance image errors properly"); + } + RuntimeError::User(data) => match data.downcast::() { + Ok(err) => (*err).try_into(), + Err(data) => panic!( + "Bad error case! Output is non-deterministic {:?} {:?}", + data.type_id(), + RuntimeError::User(data).to_string() + ), + }, + } + } +} + +fn run_method( + module: &Module, + import: &ImportObject, + method_name: &str, +) -> Result, VMRunnerError> { + let _span = tracing::debug_span!(target: "vm", "run_method").entered(); + + let instance = { + let _span = tracing::debug_span!(target: "vm", "run_method/instantiate").entered(); + match module.instantiate(import) { + Ok(instance) => instance, + Err(err) => { + let guest_aborted = err.into_vm_error()?; + return Ok(Err(guest_aborted)); + } + } + }; + + { + let _span = tracing::debug_span!(target: "vm", "run_method/call").entered(); + if let Err(err) = instance.call(method_name, &[]) { + let guest_aborted = err.into_vm_error()?; + return Ok(Err(guest_aborted)); + } + } + + { + let _span = tracing::debug_span!(target: "vm", "run_method/drop_instance").entered(); + drop(instance) + } + + Ok(Ok(())) +} + +pub(crate) fn wasmer0_vm_hash() -> u64 { + // TODO: take into account compiler and engine used to compile the contract. + 42 +} + +pub(crate) struct Wasmer0VM { + config: Config, +} + +impl Wasmer0VM { + pub(crate) fn new(config: Config) -> Self { + Self { config } + } + + pub(crate) fn compile_uncached( + &self, + code: &ContractCode, + ) -> Result { + let _span = tracing::debug_span!(target: "vm", "Wasmer0VM::compile_uncached").entered(); + let prepared_code = prepare::prepare_contract(code.code(), &self.config, VMKind::Wasmer0) + .map_err(CompilationError::PrepareError)?; + wasmer_runtime::compile(&prepared_code).map_err(|err| match err { + wasmer_runtime::error::CompileError::ValidationError { .. } => { + CompilationError::WasmerCompileError { msg: err.to_string() } + } + // NOTE: Despite the `InternalError` name, this failure occurs if + // the input `code` is invalid wasm. + wasmer_runtime::error::CompileError::InternalError { .. } => { + CompilationError::WasmerCompileError { msg: err.to_string() } + } + }) + } + + pub(crate) fn compile_and_cache( + &self, + code: &ContractCode, + cache: Option<&dyn CompiledContractCache>, + ) -> Result, CacheError> { + let module_or_error = self.compile_uncached(code); + let key = get_contract_cache_key(code, &self.config); + + if let Some(cache) = cache { + let record = match &module_or_error { + Ok(module) => { + let code = module + .cache() + .and_then(|it| it.serialize()) + .map_err(|_e| CacheError::SerializationError { hash: key.0 })?; + CompiledContract::Code(code) + } + Err(err) => CompiledContract::CompileModuleError(err.clone()), + }; + cache.put(&key, record).map_err(CacheError::WriteError)?; + } + + Ok(module_or_error) + } + + pub(crate) fn compile_and_load( + &self, + code: &ContractCode, + cache: Option<&dyn CompiledContractCache>, + ) -> VMResult> { + let _span = tracing::debug_span!(target: "vm", "Wasmer0VM::compile_and_load").entered(); + + let key = get_contract_cache_key(code, &self.config); + + let compile_or_read_from_cache = + || -> VMResult> { + let _span = + tracing::debug_span!(target: "vm", "Wasmer0VM::compile_or_read_from_cache") + .entered(); + + let cache_record = cache + .map(|cache| cache.get(&key)) + .transpose() + .map_err(CacheError::ReadError)? + .flatten(); + + let stored_module: Option = match cache_record { + None => None, + Some(CompiledContract::CompileModuleError(err)) => return Ok(Err(err)), + Some(CompiledContract::Code(serialized_module)) => { + let _span = + tracing::debug_span!(target: "vm", "Wasmer0VM::read_from_cache") + .entered(); + let artifact = wasmer_runtime_core::cache::Artifact::deserialize( + serialized_module.as_slice(), + ) + .map_err(|_e| CacheError::DeserializationError)?; + unsafe { + let compiler = wasmer_runtime::compiler_for_backend( + wasmer_runtime::Backend::Singlepass, + ) + .unwrap(); + let module = wasmer_runtime_core::load_cache_with( + artifact, + compiler.as_ref(), + ) + .map_err(|err| VMRunnerError::LoadingError(format!("{err:?}")))?; + Some(module) + } + } + }; + + Ok(match stored_module { + Some(it) => Ok(it), + None => self.compile_and_cache(code, cache)?, + }) + }; + + return compile_or_read_from_cache(); + } +} + +impl crate::runner::VM for Wasmer0VM { + fn run( + &self, + code: &ContractCode, + method_name: &str, + ext: &mut dyn External, + context: VMContext, + fees_config: &RuntimeFeesConfig, + promise_results: &[PromiseResult], + cache: Option<&dyn CompiledContractCache>, + ) -> Result { + if !cfg!(target_arch = "x86") && !cfg!(target_arch = "x86_64") { + // TODO(#1940): Remove once NaN is standardized by the VM. + panic!( + "Execution of smart contracts is only supported for x86 and x86_64 CPU \ + architectures." + ); + } + #[cfg(not(feature = "no_cpu_compatibility_checks"))] + if !is_x86_feature_detected!("avx") { + panic!("AVX support is required in order to run Wasmer VM Singlepass backend."); + } + + let mut memory = WasmerMemory::new( + self.config.limit_config.initial_memory_pages, + self.config.limit_config.max_memory_pages, + ); + // Note that we don't clone the actual backing memory, just increase the RC. + let memory_copy = memory.clone(); + + let mut logic = + VMLogic::new(ext, context, &self.config, fees_config, promise_results, &mut memory); + + let result = logic.before_loading_executable(method_name, code.code().len()); + if let Err(e) = result { + return Ok(VMOutcome::abort(logic, e)); + } + + // TODO: consider using get_module() here, once we'll go via deployment path. + let module = self.compile_and_load(code, cache)?; + let module = match module { + Ok(x) => x, + // Note on backwards-compatibility: This error used to be an error + // without result, later refactored to NOP outcome. Now this returns + // an actual outcome, including gas costs that occurred before this + // point. This is compatible with earlier versions because those + // version do not have gas costs before reaching this code. (Also + // see `test_old_fn_loading_behavior_preserved` for a test that + // verifies future changes do not counteract this assumption.) + Err(err) => { + return Ok(VMOutcome::abort(logic, FunctionCallError::CompilationError(err))) + } + }; + + let result = logic.after_loading_executable(code.code().len()); + if let Err(e) = result { + return Ok(VMOutcome::abort(logic, e)); + } + + let import_object = imports::wasmer::build(memory_copy, &mut logic); + + if let Err(e) = check_method(&module, method_name) { + return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol(logic, e)); + } + + match run_method(&module, &import_object, method_name)? { + Ok(()) => Ok(VMOutcome::ok(logic)), + Err(err) => Ok(VMOutcome::abort(logic, err)), + } + } + + fn precompile( + &self, + code: &ContractCode, + cache: &dyn CompiledContractCache, + ) -> Result< + Result, + crate::logic::errors::CacheError, + > { + Ok(self + .compile_and_cache(code, Some(cache))? + .map(|_| ContractPrecompilatonResult::ContractCompiled)) + } +} diff --git a/runtime/unc-vm-runner/src/wasmtime_runner.rs b/runtime/unc-vm-runner/src/wasmtime_runner.rs new file mode 100644 index 000000000..ffbbc0506 --- /dev/null +++ b/runtime/unc-vm-runner/src/wasmtime_runner.rs @@ -0,0 +1,254 @@ +use crate::errors::{ContractPrecompilatonResult, IntoVMError}; +use crate::logic::errors::{ + CompilationError, FunctionCallError, MethodResolveError, PrepareError, VMLogicError, + VMRunnerError, WasmTrap, +}; +use crate::logic::types::PromiseResult; +use crate::logic::Config; +use crate::logic::{ + CompiledContractCache, External, MemSlice, MemoryLike, VMContext, VMLogic, VMOutcome, +}; +use crate::{imports, prepare, ContractCode}; +use unc_parameters::vm::VMKind; +use unc_parameters::RuntimeFeesConfig; +use std::borrow::Cow; +use std::cell::RefCell; +use wasmtime::ExternType::Func; +use wasmtime::{Engine, Linker, Memory, MemoryType, Module, Store}; + +type Caller = wasmtime::Caller<'static, ()>; +thread_local! { + pub(crate) static CALLER: RefCell> = RefCell::new(None); +} +pub struct WasmtimeMemory(Memory); + +impl WasmtimeMemory { + pub fn new( + store: &mut Store<()>, + initial_memory_bytes: u32, + max_memory_bytes: u32, + ) -> Result { + Ok(WasmtimeMemory( + Memory::new(store, MemoryType::new(initial_memory_bytes, Some(max_memory_bytes))) + .map_err(|_| PrepareError::Memory)?, + )) + } +} + +fn with_caller(func: impl FnOnce(&mut Caller) -> T) -> T { + CALLER.with(|caller| func(caller.borrow_mut().as_mut().unwrap())) +} + +impl MemoryLike for WasmtimeMemory { + fn fits_memory(&self, slice: MemSlice) -> Result<(), ()> { + let end = slice.end::()?; + if end <= with_caller(|caller| self.0.data_size(caller)) { + Ok(()) + } else { + Err(()) + } + } + + fn view_memory(&self, slice: MemSlice) -> Result, ()> { + let range = slice.range::()?; + with_caller(|caller| { + self.0.data(caller).get(range).map(|slice| Cow::Owned(slice.to_vec())).ok_or(()) + }) + } + + fn read_memory(&self, offset: u64, buffer: &mut [u8]) -> Result<(), ()> { + let start = usize::try_from(offset).map_err(|_| ())?; + let end = start.checked_add(buffer.len()).ok_or(())?; + with_caller(|caller| { + let memory = self.0.data(caller).get(start..end).ok_or(())?; + buffer.copy_from_slice(memory); + Ok(()) + }) + } + + fn write_memory(&mut self, offset: u64, buffer: &[u8]) -> Result<(), ()> { + let start = usize::try_from(offset).map_err(|_| ())?; + let end = start.checked_add(buffer.len()).ok_or(())?; + with_caller(|caller| { + let memory = self.0.data_mut(caller).get_mut(start..end).ok_or(())?; + memory.copy_from_slice(buffer); + Ok(()) + }) + } +} + +impl IntoVMError for anyhow::Error { + fn into_vm_error(self) -> Result { + let cause = self.root_cause(); + if let Some(container) = cause.downcast_ref::() { + use {VMLogicError as LE, VMRunnerError as RE}; + return match container.take() { + Some(LE::HostError(h)) => Ok(FunctionCallError::HostError(h)), + Some(LE::ExternalError(s)) => Err(RE::ExternalError(s)), + Some(LE::InconsistentStateError(e)) => Err(RE::InconsistentStateError(e)), + None => panic!("error has already been taken out of the container?!"), + }; + } + if let Some(trap) = cause.downcast_ref::() { + use wasmtime::Trap as T; + let nondeterministic_message = 'nondet: { + return Ok(FunctionCallError::WasmTrap(match *trap { + T::StackOverflow => WasmTrap::StackOverflow, + T::MemoryOutOfBounds => WasmTrap::MemoryOutOfBounds, + T::TableOutOfBounds => WasmTrap::MemoryOutOfBounds, + T::IndirectCallToNull => WasmTrap::IndirectCallToNull, + T::BadSignature => WasmTrap::IncorrectCallIndirectSignature, + T::IntegerOverflow => WasmTrap::IllegalArithmetic, + T::IntegerDivisionByZero => WasmTrap::IllegalArithmetic, + T::BadConversionToInteger => WasmTrap::IllegalArithmetic, + T::UnreachableCodeReached => WasmTrap::Unreachable, + T::Interrupt => break 'nondet "interrupt", + T::HeapMisaligned => break 'nondet "heap misaligned", + t => { + return Err(VMRunnerError::WasmUnknownError { + debug_message: format!("unhandled trap type: {:?}", t), + }) + } + })); + }; + return Err(VMRunnerError::Nondeterministic(nondeterministic_message.into())); + } + Ok(FunctionCallError::LinkError { msg: format!("{:#?}", cause) }) + } +} + +#[cfg(not(feature = "lightbeam"))] +#[allow(clippy::needless_pass_by_ref_mut)] +pub fn get_engine(config: &mut wasmtime::Config) -> Engine { + Engine::new(config).unwrap() +} + +#[cfg(feature = "lightbeam")] +pub fn get_engine(config: &mut wasmtime::Config) -> Engine { + Engine::new(config.strategy(wasmtime::Strategy::Lightbeam).unwrap()).unwrap() +} + +pub(crate) fn wasmtime_vm_hash() -> u64 { + // TODO: take into account compiler and engine used to compile the contract. + 64 +} + +pub(crate) struct WasmtimeVM { + config: Config, +} + +impl WasmtimeVM { + pub(crate) fn new(config: Config) -> Self { + Self { config } + } + + pub(crate) fn default_wasmtime_config(&self) -> wasmtime::Config { + let features = + crate::features::WasmFeatures::from(self.config.limit_config.contract_prepare_version); + let mut config = wasmtime::Config::from(features); + config.max_wasm_stack(1024 * 1024 * 1024); // wasm stack metering is implemented by instrumentation, we don't want wasmtime to trap before that + config + } +} + +impl crate::runner::VM for WasmtimeVM { + fn run( + &self, + code: &ContractCode, + method_name: &str, + ext: &mut dyn External, + context: VMContext, + fees_config: &RuntimeFeesConfig, + promise_results: &[PromiseResult], + _cache: Option<&dyn CompiledContractCache>, + ) -> Result { + let mut config = self.default_wasmtime_config(); + let engine = get_engine(&mut config); + let mut store = Store::new(&engine, ()); + let mut memory = WasmtimeMemory::new( + &mut store, + self.config.limit_config.initial_memory_pages, + self.config.limit_config.max_memory_pages, + ) + .unwrap(); + let memory_copy = memory.0; + let mut logic = + VMLogic::new(ext, context, &self.config, fees_config, promise_results, &mut memory); + + let result = logic.before_loading_executable(method_name, code.code().len()); + if let Err(e) = result { + return Ok(VMOutcome::abort(logic, e)); + } + + let prepared_code = + match prepare::prepare_contract(code.code(), &self.config, VMKind::Wasmtime) { + Ok(code) => code, + Err(err) => return Ok(VMOutcome::abort(logic, FunctionCallError::from(err))), + }; + let module = match Module::new(&engine, prepared_code) { + Ok(module) => module, + Err(err) => return Ok(VMOutcome::abort(logic, err.into_vm_error()?)), + }; + let mut linker = Linker::new(&engine); + + let result = logic.after_loading_executable(code.code().len()); + if let Err(e) = result { + return Ok(VMOutcome::abort(logic, e)); + } + + imports::wasmtime::link(&mut linker, memory_copy, &store, &mut logic); + match module.get_export(method_name) { + Some(export) => match export { + Func(func_type) => { + if func_type.params().len() != 0 || func_type.results().len() != 0 { + let err = FunctionCallError::MethodResolveError( + MethodResolveError::MethodInvalidSignature, + ); + return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol(logic, err)); + } + } + _ => { + return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( + logic, + FunctionCallError::MethodResolveError(MethodResolveError::MethodNotFound), + )); + } + }, + None => { + return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( + logic, + FunctionCallError::MethodResolveError(MethodResolveError::MethodNotFound), + )); + } + } + match linker.instantiate(&mut store, &module) { + Ok(instance) => match instance.get_func(&mut store, method_name) { + Some(func) => match func.typed::<(), ()>(&mut store) { + Ok(run) => match run.call(&mut store, ()) { + Ok(_) => Ok(VMOutcome::ok(logic)), + Err(err) => Ok(VMOutcome::abort(logic, err.into_vm_error()?)), + }, + Err(err) => Ok(VMOutcome::abort(logic, err.into_vm_error()?)), + }, + None => { + return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( + logic, + FunctionCallError::MethodResolveError(MethodResolveError::MethodNotFound), + )); + } + }, + Err(err) => Ok(VMOutcome::abort(logic, err.into_vm_error()?)), + } + } + + fn precompile( + &self, + _code: &ContractCode, + _cache: &dyn CompiledContractCache, + ) -> Result< + Result, + crate::logic::errors::CacheError, + > { + Ok(Ok(ContractPrecompilatonResult::CacheNotAvailable)) + } +} diff --git a/runtime/unc-vm/.gitignore b/runtime/unc-vm/.gitignore new file mode 100644 index 000000000..b1166e461 --- /dev/null +++ b/runtime/unc-vm/.gitignore @@ -0,0 +1,17 @@ +**/target +**/*.rs.bk +.DS_Store +.idea +.gdb_history +**/.vscode +api-docs-repo/ +/.cargo_home/ +/package/ +/dist/ +/wapm-cli/ +/src/windows-installer/WasmerInstaller.exe +/lib/c-api/wasmer.h + +# Generated by tests on Android +/avd +/core diff --git a/runtime/unc-vm/ATTRIBUTIONS.md b/runtime/unc-vm/ATTRIBUTIONS.md new file mode 100644 index 000000000..1f1cca7ce --- /dev/null +++ b/runtime/unc-vm/ATTRIBUTIONS.md @@ -0,0 +1,605 @@ +# Wasmer Attributions + +Wasmer is a community effort and makes use of code from various other +projects ❤️. +Listed below are notable sections of code that are licensed +from other projects and the relevant license of those projects. + +These are the projects that were used as inspiration and/or that we are using code from. +Each of the subcrates we have have an `Aknowledgements` section with more details. + +Projects: + +- [Emscripten](https://github.com/kripken/emscripten): for emtests test sources to ensure compatibility - [LICENSE](#emscripten) +- [Nebulet](https://github.com/nebulet/nebulet): as the base for creating a great Rust WebAssembly runtime - [LICENSE](#nebulet) +- [WAVM](https://github.com/wavm/wavm): for their great integration and testing framework - [LICENSE](#wavm) +- [wasmtime](https://github.com/CraneStation/wasmtime): for their API and internal documentation, as well as some internal implementations - [LICENSE](#wasmtime) +- [WebAssembly spec](https://github.com/WebAssembly/spec/tree/master/test): for the spectests implementation + +🙏 Please let us know if you believe there is an error or omission in +this list and we will correct. + +## Licenses + +### Nebulet + +```text +MIT License + +Copyright (c) 2018 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +### WAVM + +```text +Copyright (c) 2018, Andrew Scheidecker +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of WAVM nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The contents of [Test/spec](Test/spec) is covered by the license in [Test/spec/LICENSE](Test/spec/LICENSE). + +[Source/ThirdParty/dtoa/dtoa.c](Source/ThirdParty/dtoa/dtoa.c) is covered by the license in that file. + +[Source/ThirdParty/libunwind](Source/ThirdParty/libunwind) is covered by the license in [Source/ThirdParty/libunwind/LICENSE.TXT](Source/ThirdParty/libunwind/LICENSE.TXT). + +[Source/ThirdParty/xxhash](Source/ThirdParty/xxhash) is covered by the license in [Source/ThirdParty/xxhash/LICENSE](Source/ThirdParty/xxhash/LICENSE). +``` + +### Greenwasm + +```text + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` + +### Wasmtime + +```text + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. +``` + +### Emscripten +```text +Emscripten is available under 2 licenses, the MIT license and the +University of Illinois/NCSA Open Source License. + +Both are permissive open source licenses, with little if any +practical difference between them. + +The reason for offering both is that (1) the MIT license is +well-known, while (2) the University of Illinois/NCSA Open Source +License allows Emscripten's code to be integrated upstream into +LLVM, which uses that license, should the opportunity arise. + +The full text of both licenses follows. + +============================================================================== + +Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +============================================================================== + +Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file. +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal with the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimers. + + Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimers + in the documentation and/or other materials provided with the + distribution. + + Neither the names of Mozilla, + nor the names of its contributors may be used to endorse + or promote products derived from this Software without specific prior + written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. + +============================================================================== + +This program uses portions of Node.js source code located in src/library_path.js, +in accordance with the terms of the MIT license. Node's license follows: + + """ + Copyright Joyent, Inc. and other Node contributors. All rights reserved. + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. + """ + +The musl libc project is bundled in this repo, and it has the MIT license, see +system/lib/libc/musl/COPYRIGHT + +The third_party/ subdirectory contains code with other licenses. None of it is +used by default, but certain options use it (e.g., the optional closure compiler +flag will run closure compiler from third_party/). + +``` diff --git a/runtime/unc-vm/README.md b/runtime/unc-vm/README.md new file mode 100644 index 000000000..66b08fe39 --- /dev/null +++ b/runtime/unc-vm/README.md @@ -0,0 +1,27 @@ +# The NearVM runtime crates + +This crate set is a fork of Wasmer. A significant number of things +changed, but the documentation is not up-to-date yet. + +The philosophy of Wasmer was to be very modular by design. It's +composed of a set of crates. We can group them as follows: + +* `api` — The public Rust API exposes everything a user needs to use Wasmer programatically through + the `wasmer` crate, +* `cache` — The traits and types to cache compiled WebAssembly modules, +* `cli` — The Wasmer CLI itself, +* `compiler` — The base for the compiler implementations, it defines + the framework for the compilers and provides everything they need: + * `compiler-singlepass` — A WebAssembly compiler based on our own compilation infrastructure; + recommended for compilation-time speed performance. +* `derive` — A set of procedural macros used inside Wasmer, +* `engine` — The general abstraction for creating an engine, which is responsible of leading the + compiling and running flow. Using the same compiler, the runtime performance will be + approximately the same, however the way it stores and loads the executable code will differ: + * `engine-universal` — stores the code in a custom file format, and loads it in memory, +* `types` — The basic structures to use WebAssembly, +* `vm` — The Wasmer VM runtime library, the low-level base of + everything. + +This is no longer a case for unc-vm which is moving away from this to being specialized for +framework needs. diff --git a/runtime/unc-vm/compiler-singlepass/Cargo.toml b/runtime/unc-vm/compiler-singlepass/Cargo.toml new file mode 100644 index 000000000..a77a2134f --- /dev/null +++ b/runtime/unc-vm/compiler-singlepass/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "unc-vm-compiler-singlepass" +version.workspace = true +description = "Singlepass compiler for Wasmer WebAssembly runtime" +categories = ["wasm"] +keywords = ["wasm", "webassembly", "compiler", "singlepass"] +authors = ["Wasmer Engineering Team ", "Hello Inc "] +repository.workspace = true +license = "MIT" +readme = "README.md" +edition = "2021" +publish = true + +[lints] +workspace = true + +[dependencies] +finite-wasm.workspace = true +unc-vm-compiler.workspace = true +unc-vm-vm.workspace = true +unc-vm-types.workspace = true +dynasm.workspace = true +dynasmrt.workspace = true +enumset.workspace = true +hashbrown = { workspace = true, optional = true } +lazy_static.workspace = true +memoffset.workspace = true +more-asserts.workspace = true +rayon.workspace = true +smallvec.workspace = true +strum.workspace = true +tracing.workspace = true + +[dev-dependencies] +target-lexicon.workspace = true + +[badges] +maintenance = { status = "actively-developed" } diff --git a/runtime/unc-vm/compiler-singlepass/LICENSE b/runtime/unc-vm/compiler-singlepass/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/runtime/unc-vm/compiler-singlepass/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/runtime/unc-vm/compiler-singlepass/README.md b/runtime/unc-vm/compiler-singlepass/README.md new file mode 100644 index 000000000..23391d3b3 --- /dev/null +++ b/runtime/unc-vm/compiler-singlepass/README.md @@ -0,0 +1,32 @@ +# `unc-vm-compiler-singlepass` + +This crate is a fork of `wasmer-compiler-singlepass`. A significant number of things changed, but the documentation is not up-to-date yet. + +This crate contains a compiler implementation based on the Singlepass linear compiler. + +## Usage + +```rust +use unc_vm_test_api::{Store, Universal}; +use unc_vm_compiler_singlepass::Singlepass; + +let compiler = Singlepass::new(); +// Put it into an engine and add it to the store +let store = Store::new(&Universal::new(compiler).engine()); +``` + +*Note: you can find a [full working example using Singlepass compiler +here][example].* + +## When to use Singlepass + +Singlepass is designed to emit compiled code at linear time, as such +is not prone to JIT bombs and also offers great compilation +performance, however with a bit slower runtime speed. + +The fact that singlepass is not prone to JIT bombs and offers a very +predictable compilation speed makes it ideal for **blockchains** and other +systems where fast and consistent compilation times are very critical. + + +[example]: https://github.com/wasmerio/wasmer/blob/master/examples/compiler_singlepass.rs diff --git a/runtime/unc-vm/compiler-singlepass/src/address_map.rs b/runtime/unc-vm/compiler-singlepass/src/address_map.rs new file mode 100644 index 000000000..272cee82b --- /dev/null +++ b/runtime/unc-vm/compiler-singlepass/src/address_map.rs @@ -0,0 +1,14 @@ +use unc_vm_compiler::{FunctionAddressMap, FunctionBodyData, InstructionAddressMap, SourceLoc}; + +pub fn get_function_address_map<'data>( + instructions: Vec, + data: &FunctionBodyData<'data>, + body_len: usize, +) -> FunctionAddressMap { + // Generate source loc for a function start/end to identify boundary within module. + // It will wrap around if byte code is larger than 4 GB. + let start_srcloc = SourceLoc::new(data.module_offset as u32); + let end_srcloc = SourceLoc::new((data.module_offset + data.data.len()) as u32); + + FunctionAddressMap { instructions, start_srcloc, end_srcloc, body_offset: 0, body_len } +} diff --git a/runtime/unc-vm/compiler-singlepass/src/codegen_x64.rs b/runtime/unc-vm/compiler-singlepass/src/codegen_x64.rs new file mode 100644 index 000000000..97e3fe234 --- /dev/null +++ b/runtime/unc-vm/compiler-singlepass/src/codegen_x64.rs @@ -0,0 +1,8175 @@ +use crate::address_map::get_function_address_map; +use crate::{config::Singlepass, emitter_x64::*, machine::Machine, x64_decl::*}; +use dynasmrt::{x64::X64Relocation, DynamicLabel, VecAssembler}; +use finite_wasm::gas::InstrumentationKind; +use memoffset::offset_of; +use unc_vm_compiler::wasmparser::{BlockType as WpBlockType, MemArg, Operator, ValType as WpType}; +use unc_vm_compiler::{ + CallingConvention, CompiledFunction, CompiledFunctionFrameInfo, CustomSection, + CustomSectionProtection, FunctionBody, FunctionBodyData, InstructionAddressMap, Relocation, + RelocationKind, RelocationTarget, SectionBody, SectionIndex, SourceLoc, Target, +}; +use unc_vm_types::{ + entity::{EntityRef, PrimaryMap, SecondaryMap}, + FastGasCounter, FunctionType, +}; +use unc_vm_types::{ + FunctionIndex, GlobalIndex, LocalFunctionIndex, LocalMemoryIndex, MemoryIndex, ModuleInfo, + SignatureIndex, TableIndex, Type, +}; +use unc_vm_vm::{TableStyle, TrapCode, VMBuiltinFunctionIndex, VMOffsets}; +use smallvec::{smallvec, SmallVec}; +use std::convert::TryFrom; +use std::iter; +use std::slice; + +type Assembler = VecAssembler; + +/// The singlepass per-function code generator. +pub(crate) struct FuncGen<'a> { + // Immutable properties assigned at creation time. + /// Static module information. + module: &'a ModuleInfo, + + /// ModuleInfo compilation config. + config: &'a Singlepass, + + /// Target to which we compile + target: &'a Target, + + /// Offsets of vmctx fields. + vmoffsets: &'a VMOffsets, + + // // Memory plans. + // memory_styles: &'a PrimaryMap, + + // // Table plans. + // table_styles: &'a PrimaryMap, + /// Function signature. + signature: FunctionType, + + // Working storage. + /// The assembler. + /// + /// This should be changed to `Vec` for platform independency, but dynasm doesn't (yet) + /// support automatic relative relocations for `Vec`. + assembler: &'a mut Assembler, + + /// Types of the local variables, including arguments. + local_types: unc_vm_types::partial_sum_map::PartialSumMap, + + /// Value stack. + value_stack: Vec, + + /// Metadata about floating point values on the stack. + fp_stack: Vec, + + /// A list of frames describing the current control stack. + control_stack: Vec, + + /// Low-level machine state. + machine: Machine, + + /// Nesting level of unreachable code. + unreachable_depth: usize, + + /// Relocation information. + relocations: Vec, + + /// A set of special labels for trapping. + special_labels: SpecialLabelSet, + + /// The source location for the current operator. + src_loc: u32, + + /// Map from byte offset into wasm function to range of native instructions. + /// + // Ordered by increasing InstructionAddressMap::srcloc. + instructions_address_map: Vec, + + /// Calling convention to use. + calling_convention: CallingConvention, + + /// Cost for initializing the stack of the function + stack_init_gas_cost: u64, + + /// Iterator over the gas instrumentation points + gas_iter: iter::Peekable, slice::Iter<'a, u64>>>, + + /// Maximum size of the stack for this function + stack_size: u32, +} + +struct SpecialLabelSet { + integer_division_by_zero: DynamicLabel, + integer_overflow: DynamicLabel, + bad_conversion_to_integer: DynamicLabel, + heap_access_oob: DynamicLabel, + table_access_oob: DynamicLabel, + indirect_call_null: DynamicLabel, + bad_signature: DynamicLabel, + gas_limit_exceeded: DynamicLabel, + stack_overflow: DynamicLabel, +} + +/// Metadata about a floating-point value. +#[derive(Copy, Clone, Debug)] +struct FloatValue { + /// Do we need to canonicalize the value before its bit pattern is next observed? If so, how? + canonicalization: Option, + + /// Corresponding depth in the main value stack. + depth: usize, +} + +impl FloatValue { + fn new(depth: usize) -> Self { + FloatValue { canonicalization: None, depth } + } + + fn cncl_f32(depth: usize) -> Self { + FloatValue { canonicalization: Some(CanonicalizeType::F32), depth } + } + + fn cncl_f64(depth: usize) -> Self { + FloatValue { canonicalization: Some(CanonicalizeType::F64), depth } + } + + fn promote(self, depth: usize) -> FloatValue { + FloatValue { + canonicalization: match self.canonicalization { + Some(CanonicalizeType::F32) => Some(CanonicalizeType::F64), + Some(CanonicalizeType::F64) => panic!("cannot promote F64"), + None => None, + }, + depth, + } + } + + fn demote(self, depth: usize) -> FloatValue { + FloatValue { + canonicalization: match self.canonicalization { + Some(CanonicalizeType::F64) => Some(CanonicalizeType::F32), + Some(CanonicalizeType::F32) => panic!("cannot demote F32"), + None => None, + }, + depth, + } + } +} + +/// Type of a pending canonicalization floating point value. +/// Sometimes we don't have the type information elsewhere and therefore we need to track it here. +#[derive(Copy, Clone, Debug)] +enum CanonicalizeType { + F32, + F64, +} + +impl CanonicalizeType { + fn to_size(&self) -> Size { + match self { + CanonicalizeType::F32 => Size::S32, + CanonicalizeType::F64 => Size::S64, + } + } +} + +trait PopMany { + fn peek1(&self) -> Result<&T, CodegenError>; + fn pop1(&mut self) -> Result; + fn pop2(&mut self) -> Result<(T, T), CodegenError>; +} + +impl PopMany for Vec { + fn peek1(&self) -> Result<&T, CodegenError> { + self.last() + .ok_or_else(|| CodegenError { message: "peek1() expects at least 1 element".into() }) + } + fn pop1(&mut self) -> Result { + self.pop() + .ok_or_else(|| CodegenError { message: "pop1() expects at least 1 element".into() }) + } + fn pop2(&mut self) -> Result<(T, T), CodegenError> { + if self.len() < 2 { + return Err(CodegenError { message: "pop2() expects at least 2 elements".into() }); + } + + let right = self.pop().unwrap(); + let left = self.pop().unwrap(); + Ok((left, right)) + } +} + +trait WpTypeExt { + fn is_float(&self) -> bool; +} + +impl WpTypeExt for WpType { + fn is_float(&self) -> bool { + match self { + WpType::F32 | WpType::F64 => true, + _ => false, + } + } +} + +#[derive(Debug)] +pub(crate) struct ControlFrame { + /// The label to which `br` opcodes should jump + /// + /// This is: + /// * for functions (ie. the control_stack[0]), the start of the epilogue + /// * for `block` or `if`/`else` blocks, the end of the block (after stack cleanup) + /// * for `loop` blocks, the beginning of the loop block + pub(crate) br_label: DynamicLabel, + + pub(crate) loop_like: bool, + pub(crate) if_else: IfElseState, + pub(crate) returns: SmallVec<[WpType; 1]>, + pub(crate) value_stack_depth: usize, + pub(crate) fp_stack_depth: usize, +} + +#[derive(Debug, Copy, Clone)] +pub(crate) enum IfElseState { + None, + If(DynamicLabel), + Else, +} + +#[derive(Debug)] +pub(crate) struct CodegenError { + pub(crate) message: String, +} + +/// Abstraction for a 2-input, 1-output operator. Can be an integer/floating-point +/// binop/cmpop. +struct I2O1 { + loc_a: Location, + loc_b: Location, + ret: Location, +} + +impl<'a> FuncGen<'a> { + /// Set the source location of the Wasm to the given offset. + pub(crate) fn set_srcloc(&mut self, offset: u32) { + self.src_loc = offset; + } + + fn get_location_released(&mut self, loc: Location) -> Location { + self.machine.release_locations(self.assembler, &[loc]); + loc + } + + fn pop_value_released(&mut self) -> Location { + let loc = self.value_stack.pop().expect("pop_value_released: value stack is empty"); + self.get_location_released(loc) + } + + /// Prepare data for binary operator with 2 inputs and 1 output. + fn i2o1_prepare(&mut self, ty: WpType) -> I2O1 { + let loc_b = self.pop_value_released(); + let loc_a = self.pop_value_released(); + let ret = self.machine.acquire_locations(self.assembler, &[(ty)], false)[0]; + self.value_stack.push(ret); + I2O1 { loc_a, loc_b, ret } + } + + fn emit_call(&mut self, function: FunctionIndex) -> Result<(), CodegenError> { + let sig_index = *self.module.functions.get(function).unwrap(); + let sig = self.module.signatures.get(sig_index).unwrap(); + let param_types: SmallVec<[WpType; 8]> = + sig.params().iter().cloned().map(type_to_wp_type).collect(); + let return_types: SmallVec<[WpType; 1]> = + sig.results().iter().cloned().map(type_to_wp_type).collect(); + + let params: SmallVec<[_; 8]> = + self.value_stack.drain(self.value_stack.len() - param_types.len()..).collect(); + self.machine.release_locations_only_regs(¶ms); + + // Pop arguments off the FP stack and canonicalize them if needed. + // + // Canonicalization state will be lost across function calls, so early canonicalization + // is necessary here. + while let Some(fp) = self.fp_stack.last() { + if fp.depth >= self.value_stack.len() { + let index = fp.depth - self.value_stack.len(); + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization + && fp.canonicalization.is_some() + { + let size = fp.canonicalization.unwrap().to_size(); + self.canonicalize_nan(size, params[index], params[index]); + } + self.fp_stack.pop().unwrap(); + } else { + break; + } + } + + let reloc_at = self.assembler.get_offset().0 + self.assembler.arch_mov64_imm_offset(); + // Imported functions are called through trampolines placed as custom sections. + let reloc_target = match self.module.import_counts.local_function_index(function) { + Ok(local) => RelocationTarget::LocalFunc(local), + Err(imp) => RelocationTarget::CustomSection(SectionIndex::from_u32(imp.as_u32())), + }; + self.relocations.push(Relocation { + kind: RelocationKind::Abs8, + reloc_target, + offset: reloc_at as u32, + addend: 0, + }); + + // RAX is preserved on entry to `emit_call_sysv` callback. + // The Imm64 value is relocated by the JIT linker. + self.assembler.emit_mov(Size::S64, Location::Imm64(std::u64::MAX), Location::GPR(GPR::RAX)); + + self.emit_call_native( + |this| { + this.assembler.emit_call_location(Location::GPR(GPR::RAX)); + }, + params.iter().copied(), + )?; + + self.machine.release_locations_only_stack(self.assembler, ¶ms); + + if !return_types.is_empty() { + let ret = + self.machine.acquire_locations(self.assembler, &[(return_types[0])], false)[0]; + self.value_stack.push(ret); + if return_types[0].is_float() { + self.assembler.emit_mov(Size::S64, Location::XMM(XMM::XMM0), ret); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); + } else { + self.assembler.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + } + Ok(()) + } + + fn emit_gas_const(&mut self, cost: u64) { + if self.config.disable_9393_fix { + // emit_gas only supports Imm32 with an argument up-to i32::MAX, but we made *this* + // single-letter oversight at some point & the bug made its way into mainnet. Now that + // we need to maintain backwards compatibility and replayability of the old + // transactions, we end up with this wonderful and slightly horrifying monument to our + // former selves :) + if let Ok(cost) = u32::try_from(cost) { + return self.emit_gas(Location::Imm32(cost)); + } + } else { + if let Ok(cost) = i32::try_from(cost) { + // This as `u32` cast is valid, as fallible u64->i32 conversions can’t produce a + // negative integer. + return self.emit_gas(Location::Imm32(cost as u32)); + } + } + let cost_reg = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S64, Location::Imm64(cost), Location::GPR(cost_reg)); + self.emit_gas(Location::GPR(cost_reg)); + self.machine.release_temp_gpr(cost_reg); + } + + /// Emit a gas charge operation. The gas amount is stored in `cost_location`, which must be either an imm32 or a GPR + // (this is because emit_add can only take up to an imm32) + fn emit_gas(&mut self, cost_location: Location) { + if cost_location == Location::Imm32(0) { + return; // skip, which we must do because emit_add optimizes out the add 0 which leaves CF clobbered otherwise + } + + match cost_location { + Location::Imm32(v) if self.config.disable_9393_fix || v <= (i32::MAX as u32) => {} + Location::Imm32(v) => { + panic!("emit_gas can take only an imm32 <= 0xFFF_FFFF, got 0x{v:X}") + } + Location::GPR(_) => {} + _ => panic!("emit_gas can take only an imm32 or a gpr argument"), + } + + let counter_offset = offset_of!(FastGasCounter, burnt_gas) as i32; + let gas_limit_offset = offset_of!(FastGasCounter, gas_limit) as i32; + // Recheck offsets, to make sure offsets will never change. + assert_eq!(counter_offset, 0); + assert_eq!(gas_limit_offset, 8); + let base_reg = self.machine.acquire_temp_gpr().unwrap(); + // Load gas counter base. + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_gas_limiter_pointer() as i32, + ), + Location::GPR(base_reg), + ); + let current_burnt_reg = self.machine.acquire_temp_gpr().unwrap(); + // Read current gas counter. + self.assembler.emit_mov( + Size::S64, + Location::Memory(base_reg, counter_offset), + Location::GPR(current_burnt_reg), + ); + // Compute new cost. + self.assembler.emit_add(Size::S64, cost_location, Location::GPR(current_burnt_reg)); + self.assembler.emit_jmp(Condition::Carry, self.special_labels.integer_overflow); + // Compare with the limit. + self.assembler.emit_cmp( + Size::S64, + Location::GPR(current_burnt_reg), + Location::Memory(base_reg, gas_limit_offset), + ); + // Write new gas counter unconditionally, so that runtime can sort out limits case. + self.assembler.emit_mov( + Size::S64, + Location::GPR(current_burnt_reg), + Location::Memory(base_reg, counter_offset), + ); + self.assembler.emit_jmp(Condition::BelowEqual, self.special_labels.gas_limit_exceeded); + self.machine.release_temp_gpr(base_reg); + self.machine.release_temp_gpr(current_burnt_reg); + } + + fn emit_trap(&mut self, code: TrapCode) { + let label = self.assembler.get_label(); + self.assembler.emit_label(label); + self.assembler + .emit_lea_label(label, Machine::get_param_location(0, self.calling_convention)); + self.assembler.emit_mov( + Size::S32, + Location::Imm32(code as u32), + Machine::get_param_location(1, self.calling_convention), + ); + // Align stack. + self.assembler.emit_and(Size::S64, Location::Imm32(0xfffffff0), Location::GPR(GPR::RSP)); + let offset = self.vmoffsets.vmctx_trap_handler(); + self.assembler + .emit_call_location(Location::Memory(Machine::get_vmctx_reg(), offset as i32)); + } + + /// Canonicalizes the floating point value at `input` into `output`. + fn canonicalize_nan(&mut self, sz: Size, input: Location, output: Location) { + let tmp1 = self.machine.acquire_temp_xmm().unwrap(); + let tmp2 = self.machine.acquire_temp_xmm().unwrap(); + let tmp3 = self.machine.acquire_temp_xmm().unwrap(); + + self.emit_relaxed_binop(Assembler::emit_mov, sz, input, Location::XMM(tmp1)); + let tmpg1 = self.machine.acquire_temp_gpr().unwrap(); + + match sz { + Size::S32 => { + self.assembler.emit_vcmpunordss(tmp1, XMMOrMemory::XMM(tmp1), tmp2); + self.assembler.emit_mov( + Size::S32, + Location::Imm32(0x7FC0_0000), // Canonical NaN + Location::GPR(tmpg1), + ); + self.assembler.emit_mov(Size::S64, Location::GPR(tmpg1), Location::XMM(tmp3)); + self.assembler.emit_vblendvps(tmp2, XMMOrMemory::XMM(tmp3), tmp1, tmp1); + } + Size::S64 => { + self.assembler.emit_vcmpunordsd(tmp1, XMMOrMemory::XMM(tmp1), tmp2); + self.assembler.emit_mov( + Size::S64, + Location::Imm64(0x7FF8_0000_0000_0000), // Canonical NaN + Location::GPR(tmpg1), + ); + self.assembler.emit_mov(Size::S64, Location::GPR(tmpg1), Location::XMM(tmp3)); + self.assembler.emit_vblendvpd(tmp2, XMMOrMemory::XMM(tmp3), tmp1, tmp1); + } + _ => unreachable!(), + } + + self.emit_relaxed_binop(Assembler::emit_mov, sz, Location::XMM(tmp1), output); + + self.machine.release_temp_gpr(tmpg1); + self.machine.release_temp_xmm(tmp3); + self.machine.release_temp_xmm(tmp2); + self.machine.release_temp_xmm(tmp1); + } + + /// Moves `loc` to a valid location for `div`/`idiv`. + fn emit_relaxed_xdiv(&mut self, signed: bool, sz: Size, loc: Location) { + self.assembler.emit_cmp(sz, Location::Imm32(0), loc); + self.assembler.emit_jmp(Condition::Equal, self.special_labels.integer_division_by_zero); + + // Boundary checks for integer overflow. It clearly doesn't make sense for + // unsigned division, as numerator is of same size as the actual result, and divisor is + // always >= 1. + // For signed division we can get out of bound only when divide MIN_INT / -1, + // as result will be MAX_INT+1 so test for that case explicitly. + if signed { + let end = self.assembler.get_label(); + // Check if divisor is -1. + self.assembler.emit_cmp(sz, Location::Imm32(0xffffffff), loc); + self.assembler.emit_jmp(Condition::NotEqual, end); + // Check if numerator is MIN_INT. + match sz { + Size::S32 => { + self.assembler.emit_cmp( + sz, + Location::Imm32(0x80000000u32), + Location::GPR(GPR::RAX), + ); + } + Size::S64 => { + self.assembler.emit_mov( + sz, + Location::Imm64(0x8000000000000000u64), + Location::GPR(GPR::RCX), + ); + self.assembler.emit_cmp(sz, Location::GPR(GPR::RCX), Location::GPR(GPR::RAX)); + } + _ => assert!(false), + } + self.assembler.emit_jmp(Condition::NotEqual, end); + self.assembler.emit_jmp(Condition::None, self.special_labels.integer_overflow); + self.assembler.emit_label(end); + } + + match loc { + Location::Imm64(_) | Location::Imm32(_) => { + self.assembler.emit_mov(sz, loc, Location::GPR(GPR::RCX)); // must not be used during div (rax, rdx) + if signed { + self.assembler.emit_idiv(sz, Location::GPR(GPR::RCX)); + } else { + self.assembler.emit_div(sz, Location::GPR(GPR::RCX)); + } + } + _ => { + if signed { + self.assembler.emit_idiv(sz, loc); + } else { + self.assembler.emit_div(sz, loc); + } + } + } + } + + /// Moves `src` and `dst` to valid locations for `movzx`/`movsx`. + fn emit_relaxed_zx_sx( + &mut self, + op: fn(&mut Assembler, Size, Location, Size, Location), + sz_src: Size, + mut src: Location, + sz_dst: Size, + dst: Location, + ) -> Result<(), CodegenError> { + let inner = |m: &mut Machine, a: &mut Assembler, src: Location| match dst { + Location::Imm32(_) | Location::Imm64(_) => { + return Err(CodegenError { + message: "emit_relaxed_zx_sx dst Imm: unreachable code".to_string(), + }) + } + Location::Memory(_, _) => { + let tmp_dst = m.acquire_temp_gpr().unwrap(); + op(a, sz_src, src, sz_dst, Location::GPR(tmp_dst)); + a.emit_mov(Size::S64, Location::GPR(tmp_dst), dst); + + m.release_temp_gpr(tmp_dst); + Ok(()) + } + Location::GPR(_) => { + op(a, sz_src, src, sz_dst, dst); + Ok(()) + } + _ => { + return Err(CodegenError { + message: "emit_relaxed_zx_sx dst: unreachable code".to_string(), + }) + } + }; + + match src { + Location::Imm32(_) | Location::Imm64(_) => { + let tmp_src = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S64, src, Location::GPR(tmp_src)); + src = Location::GPR(tmp_src); + + inner(&mut self.machine, self.assembler, src)?; + + self.machine.release_temp_gpr(tmp_src); + } + Location::GPR(_) | Location::Memory(_, _) => { + inner(&mut self.machine, self.assembler, src)? + } + _ => { + return Err(CodegenError { + message: "emit_relaxed_zx_sx src: unreachable code".to_string(), + }) + } + } + Ok(()) + } + + /// Moves `src` and `dst` to valid locations for generic instructions. + fn emit_relaxed_binop( + &mut self, + op: fn(&mut Assembler, Size, Location, Location), + sz: Size, + src: Location, + dst: Location, + ) { + enum RelaxMode { + Direct, + SrcToGPR, + DstToGPR, + BothToGPR, + } + let mode = match (src, dst) { + (Location::GPR(_), Location::GPR(_)) + if (op as *const u8 == Assembler::emit_imul as *const u8) => + { + RelaxMode::Direct + } + _ if (op as *const u8 == Assembler::emit_imul as *const u8) => RelaxMode::BothToGPR, + + (Location::Memory(_, _), Location::Memory(_, _)) => RelaxMode::SrcToGPR, + (Location::Imm64(_), Location::Imm64(_)) | (Location::Imm64(_), Location::Imm32(_)) => { + RelaxMode::BothToGPR + } + (_, Location::Imm32(_)) | (_, Location::Imm64(_)) => RelaxMode::DstToGPR, + (Location::Imm64(_), Location::Memory(_, _)) => RelaxMode::SrcToGPR, + (Location::Imm64(_), Location::GPR(_)) + if (op as *const u8 != Assembler::emit_mov as *const u8) => + { + RelaxMode::SrcToGPR + } + (_, Location::XMM(_)) => RelaxMode::SrcToGPR, + _ => RelaxMode::Direct, + }; + + match mode { + RelaxMode::SrcToGPR => { + let temp = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(sz, src, Location::GPR(temp)); + op(self.assembler, sz, Location::GPR(temp), dst); + self.machine.release_temp_gpr(temp); + } + RelaxMode::DstToGPR => { + let temp = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(sz, dst, Location::GPR(temp)); + op(self.assembler, sz, src, Location::GPR(temp)); + self.machine.release_temp_gpr(temp); + } + RelaxMode::BothToGPR => { + let temp_src = self.machine.acquire_temp_gpr().unwrap(); + let temp_dst = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(sz, src, Location::GPR(temp_src)); + self.assembler.emit_mov(sz, dst, Location::GPR(temp_dst)); + op(self.assembler, sz, Location::GPR(temp_src), Location::GPR(temp_dst)); + match dst { + Location::Memory(_, _) | Location::GPR(_) => { + self.assembler.emit_mov(sz, Location::GPR(temp_dst), dst); + } + _ => {} + } + self.machine.release_temp_gpr(temp_dst); + self.machine.release_temp_gpr(temp_src); + } + RelaxMode::Direct => { + op(self.assembler, sz, src, dst); + } + } + } + + /// Moves `src1` and `src2` to valid locations and possibly adds a layer of indirection for `dst` for AVX instructions. + fn emit_relaxed_avx( + &mut self, + op: fn(&mut Assembler, XMM, XMMOrMemory, XMM), + src1: Location, + src2: Location, + dst: Location, + ) -> Result<(), CodegenError> { + self.emit_relaxed_avx_base( + |this, src1, src2, dst| op(&mut this.assembler, src1, src2, dst), + src1, + src2, + dst, + )?; + Ok(()) + } + + /// Moves `src1` and `src2` to valid locations and possibly adds a layer of indirection for `dst` for AVX instructions. + fn emit_relaxed_avx_base( + &mut self, + op: F, + src1: Location, + src2: Location, + dst: Location, + ) -> Result<(), CodegenError> { + let tmp1 = self.machine.acquire_temp_xmm().unwrap(); + let tmp2 = self.machine.acquire_temp_xmm().unwrap(); + let tmp3 = self.machine.acquire_temp_xmm().unwrap(); + let tmpg = self.machine.acquire_temp_gpr().unwrap(); + + let src1 = match src1 { + Location::XMM(x) => x, + Location::GPR(_) | Location::Memory(_, _) => { + self.assembler.emit_mov(Size::S64, src1, Location::XMM(tmp1)); + tmp1 + } + Location::Imm32(_) => { + self.assembler.emit_mov(Size::S32, src1, Location::GPR(tmpg)); + self.assembler.emit_mov(Size::S32, Location::GPR(tmpg), Location::XMM(tmp1)); + tmp1 + } + Location::Imm64(_) => { + self.assembler.emit_mov(Size::S64, src1, Location::GPR(tmpg)); + self.assembler.emit_mov(Size::S64, Location::GPR(tmpg), Location::XMM(tmp1)); + tmp1 + } + _ => { + return Err(CodegenError { + message: "emit_relaxed_avx_base src1: unreachable code".to_string(), + }) + } + }; + + let src2 = match src2 { + Location::XMM(x) => XMMOrMemory::XMM(x), + Location::Memory(base, disp) => XMMOrMemory::Memory(base, disp), + Location::GPR(_) => { + self.assembler.emit_mov(Size::S64, src2, Location::XMM(tmp2)); + XMMOrMemory::XMM(tmp2) + } + Location::Imm32(_) => { + self.assembler.emit_mov(Size::S32, src2, Location::GPR(tmpg)); + self.assembler.emit_mov(Size::S32, Location::GPR(tmpg), Location::XMM(tmp2)); + XMMOrMemory::XMM(tmp2) + } + Location::Imm64(_) => { + self.assembler.emit_mov(Size::S64, src2, Location::GPR(tmpg)); + self.assembler.emit_mov(Size::S64, Location::GPR(tmpg), Location::XMM(tmp2)); + XMMOrMemory::XMM(tmp2) + } + _ => { + return Err(CodegenError { + message: "emit_relaxed_avx_base src2: unreachable code".to_string(), + }) + } + }; + + match dst { + Location::XMM(x) => { + op(self, src1, src2, x); + } + Location::Memory(_, _) | Location::GPR(_) => { + op(self, src1, src2, tmp3); + self.assembler.emit_mov(Size::S64, Location::XMM(tmp3), dst); + } + _ => { + return Err(CodegenError { + message: "emit_relaxed_avx_base dst: unreachable code".to_string(), + }) + } + } + + self.machine.release_temp_gpr(tmpg); + self.machine.release_temp_xmm(tmp3); + self.machine.release_temp_xmm(tmp2); + self.machine.release_temp_xmm(tmp1); + Ok(()) + } + + /// I32 binary operation with both operands popped from the virtual stack. + fn emit_binop_i32(&mut self, f: fn(&mut Assembler, Size, Location, Location)) { + // Using Red Zone here. + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::I32); + if loc_a != ret { + let tmp = self.machine.acquire_temp_gpr().unwrap(); + self.emit_relaxed_binop(Assembler::emit_mov, Size::S32, loc_a, Location::GPR(tmp)); + self.emit_relaxed_binop(f, Size::S32, loc_b, Location::GPR(tmp)); + self.emit_relaxed_binop(Assembler::emit_mov, Size::S32, Location::GPR(tmp), ret); + self.machine.release_temp_gpr(tmp); + } else { + self.emit_relaxed_binop(f, Size::S32, loc_b, ret); + } + } + + /// I64 binary operation with both operands popped from the virtual stack. + fn emit_binop_i64(&mut self, f: fn(&mut Assembler, Size, Location, Location)) { + // Using Red Zone here. + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::I64); + + if loc_a != ret { + let tmp = self.machine.acquire_temp_gpr().unwrap(); + self.emit_relaxed_binop(Assembler::emit_mov, Size::S64, loc_a, Location::GPR(tmp)); + self.emit_relaxed_binop(f, Size::S64, loc_b, Location::GPR(tmp)); + self.emit_relaxed_binop(Assembler::emit_mov, Size::S64, Location::GPR(tmp), ret); + self.machine.release_temp_gpr(tmp); + } else { + self.emit_relaxed_binop(f, Size::S64, loc_b, ret); + } + } + + /// I32 comparison with `loc_b` from input. + fn emit_cmpop_i32_dynamic_b( + &mut self, + c: Condition, + loc_b: Location, + ) -> Result<(), CodegenError> { + // Using Red Zone here. + let loc_a = self.pop_value_released(); + + let ret = self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + match ret { + Location::GPR(x) => { + self.emit_relaxed_binop(Assembler::emit_cmp, Size::S32, loc_b, loc_a); + self.assembler.emit_set(c, x); + self.assembler.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(x)); + } + Location::Memory(_, _) => { + let tmp = self.machine.acquire_temp_gpr().unwrap(); + self.emit_relaxed_binop(Assembler::emit_cmp, Size::S32, loc_b, loc_a); + self.assembler.emit_set(c, tmp); + self.assembler.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(tmp)); + self.assembler.emit_mov(Size::S32, Location::GPR(tmp), ret); + self.machine.release_temp_gpr(tmp); + } + _ => { + return Err(CodegenError { + message: "emit_cmpop_i32_dynamic_b ret: unreachable code".to_string(), + }) + } + } + self.value_stack.push(ret); + Ok(()) + } + + /// I32 comparison with both operands popped from the virtual stack. + fn emit_cmpop_i32(&mut self, c: Condition) -> Result<(), CodegenError> { + let loc_b = self.pop_value_released(); + self.emit_cmpop_i32_dynamic_b(c, loc_b)?; + Ok(()) + } + + /// I64 comparison with `loc_b` from input. + fn emit_cmpop_i64_dynamic_b( + &mut self, + c: Condition, + loc_b: Location, + ) -> Result<(), CodegenError> { + // Using Red Zone here. + let loc_a = self.pop_value_released(); + + let ret = self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + match ret { + Location::GPR(x) => { + self.emit_relaxed_binop(Assembler::emit_cmp, Size::S64, loc_b, loc_a); + self.assembler.emit_set(c, x); + self.assembler.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(x)); + } + Location::Memory(_, _) => { + let tmp = self.machine.acquire_temp_gpr().unwrap(); + self.emit_relaxed_binop(Assembler::emit_cmp, Size::S64, loc_b, loc_a); + self.assembler.emit_set(c, tmp); + self.assembler.emit_and(Size::S32, Location::Imm32(0xff), Location::GPR(tmp)); + self.assembler.emit_mov(Size::S32, Location::GPR(tmp), ret); + self.machine.release_temp_gpr(tmp); + } + _ => { + return Err(CodegenError { + message: "emit_cmpop_i64_dynamic_b ret: unreachable code".to_string(), + }) + } + } + self.value_stack.push(ret); + Ok(()) + } + + /// I64 comparison with both operands popped from the virtual stack. + fn emit_cmpop_i64(&mut self, c: Condition) -> Result<(), CodegenError> { + let loc_b = self.pop_value_released(); + self.emit_cmpop_i64_dynamic_b(c, loc_b)?; + Ok(()) + } + + /// I32 `lzcnt`/`tzcnt`/`popcnt` with operand popped from the virtual stack. + fn emit_xcnt_i32( + &mut self, + f: fn(&mut Assembler, Size, Location, Location), + ) -> Result<(), CodegenError> { + let loc = self.pop_value_released(); + let ret = self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + + match loc { + Location::Imm32(_) => { + let tmp = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S32, loc, Location::GPR(tmp)); + if let Location::Memory(_, _) = ret { + let out_tmp = self.machine.acquire_temp_gpr().unwrap(); + f(self.assembler, Size::S32, Location::GPR(tmp), Location::GPR(out_tmp)); + self.assembler.emit_mov(Size::S32, Location::GPR(out_tmp), ret); + self.machine.release_temp_gpr(out_tmp); + } else { + f(self.assembler, Size::S32, Location::GPR(tmp), ret); + } + self.machine.release_temp_gpr(tmp); + } + Location::Memory(_, _) | Location::GPR(_) => { + if let Location::Memory(_, _) = ret { + let out_tmp = self.machine.acquire_temp_gpr().unwrap(); + f(self.assembler, Size::S32, loc, Location::GPR(out_tmp)); + self.assembler.emit_mov(Size::S32, Location::GPR(out_tmp), ret); + self.machine.release_temp_gpr(out_tmp); + } else { + f(self.assembler, Size::S32, loc, ret); + } + } + _ => { + return Err(CodegenError { + message: "emit_xcnt_i32 loc: unreachable code".to_string(), + }) + } + } + self.value_stack.push(ret); + Ok(()) + } + + /// I64 `lzcnt`/`tzcnt`/`popcnt` with operand popped from the virtual stack. + fn emit_xcnt_i64( + &mut self, + f: fn(&mut Assembler, Size, Location, Location), + ) -> Result<(), CodegenError> { + let loc = self.pop_value_released(); + let ret = self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + + match loc { + Location::Imm64(_) | Location::Imm32(_) => { + let tmp = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S64, loc, Location::GPR(tmp)); + if let Location::Memory(_, _) = ret { + let out_tmp = self.machine.acquire_temp_gpr().unwrap(); + f(self.assembler, Size::S64, Location::GPR(tmp), Location::GPR(out_tmp)); + self.assembler.emit_mov(Size::S64, Location::GPR(out_tmp), ret); + self.machine.release_temp_gpr(out_tmp); + } else { + f(self.assembler, Size::S64, Location::GPR(tmp), ret); + } + self.machine.release_temp_gpr(tmp); + } + Location::Memory(_, _) | Location::GPR(_) => { + if let Location::Memory(_, _) = ret { + let out_tmp = self.machine.acquire_temp_gpr().unwrap(); + f(self.assembler, Size::S64, loc, Location::GPR(out_tmp)); + self.assembler.emit_mov(Size::S64, Location::GPR(out_tmp), ret); + self.machine.release_temp_gpr(out_tmp); + } else { + f(self.assembler, Size::S64, loc, ret); + } + } + _ => { + return Err(CodegenError { + message: "emit_xcnt_i64 loc: unreachable code".to_string(), + }) + } + } + self.value_stack.push(ret); + Ok(()) + } + + /// I32 shift with both operands popped from the virtual stack. + fn emit_shift_i32(&mut self, f: fn(&mut Assembler, Size, Location, Location)) { + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::I32); + + self.assembler.emit_mov(Size::S32, loc_b, Location::GPR(GPR::RCX)); + + if loc_a != ret { + self.emit_relaxed_binop(Assembler::emit_mov, Size::S32, loc_a, ret); + } + + f(self.assembler, Size::S32, Location::GPR(GPR::RCX), ret); + } + + /// I64 shift with both operands popped from the virtual stack. + fn emit_shift_i64(&mut self, f: fn(&mut Assembler, Size, Location, Location)) { + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::I64); + self.assembler.emit_mov(Size::S64, loc_b, Location::GPR(GPR::RCX)); + + if loc_a != ret { + self.emit_relaxed_binop(Assembler::emit_mov, Size::S64, loc_a, ret); + } + + f(self.assembler, Size::S64, Location::GPR(GPR::RCX), ret); + } + + /// Floating point (AVX) binary operation with both operands popped from the virtual stack. + fn emit_fp_binop_avx( + &mut self, + f: fn(&mut Assembler, XMM, XMMOrMemory, XMM), + ) -> Result<(), CodegenError> { + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::F64); + + self.emit_relaxed_avx(f, loc_a, loc_b, ret)?; + Ok(()) + } + + /// Floating point (AVX) comparison with both operands popped from the virtual stack. + fn emit_fp_cmpop_avx( + &mut self, + f: fn(&mut Assembler, XMM, XMMOrMemory, XMM), + ) -> Result<(), CodegenError> { + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::I32); + + self.emit_relaxed_avx(f, loc_a, loc_b, ret)?; + + // Workaround for behavior inconsistency among different backing implementations. + // (all bits or only the least significant bit are set to one?) + self.assembler.emit_and(Size::S32, Location::Imm32(1), ret); + Ok(()) + } + + /// Floating point (AVX) unop with both operands popped from the virtual stack. + fn emit_fp_unop_avx( + &mut self, + f: fn(&mut Assembler, XMM, XMMOrMemory, XMM), + ) -> Result<(), CodegenError> { + let loc = self.pop_value_released(); + let ret = self.machine.acquire_locations(self.assembler, &[(WpType::F64)], false)[0]; + self.value_stack.push(ret); + self.emit_relaxed_avx(f, loc, loc, ret)?; + Ok(()) + } + + /// Emits a System V / Windows call sequence. + /// + /// This function will not use RAX before `cb` is called. + /// + /// The caller MUST NOT hold any temporary registers allocated by `acquire_temp_gpr` when calling + /// this function. + fn emit_call_native, F: FnOnce(&mut Self)>( + &mut self, + cb: F, + params: I, + ) -> Result<(), CodegenError> { + let params: Vec<_> = params.collect(); + + // Save used GPRs. + let used_gprs = self.machine.get_used_gprs(); + for r in used_gprs.iter() { + self.assembler.emit_push(Size::S64, Location::GPR(*r)); + } + + // Save used XMM registers. + let used_xmms = self.machine.get_used_xmms(); + if !used_xmms.is_empty() { + self.assembler.emit_sub( + Size::S64, + Location::Imm32((used_xmms.len() * 8) as u32), + Location::GPR(GPR::RSP), + ); + + for (i, r) in used_xmms.iter().enumerate() { + self.assembler.emit_mov( + Size::S64, + Location::XMM(*r), + Location::Memory(GPR::RSP, (i * 8) as i32), + ); + } + } + let calling_convention = self.calling_convention; + + let stack_padding: usize = match calling_convention { + CallingConvention::WindowsFastcall => 32, + _ => 0, + }; + + let mut stack_offset: usize = 0; + + // Calculate stack offset. + for (i, _param) in params.iter().enumerate() { + if let Location::Memory(_, _) = Machine::get_param_location(1 + i, calling_convention) { + stack_offset += 8; + } + } + + // Align stack to 16 bytes. + if (self.machine.get_stack_offset() + + used_gprs.len() * 8 + + used_xmms.len() * 8 + + stack_offset) + % 16 + != 0 + { + self.assembler.emit_sub(Size::S64, Location::Imm32(8), Location::GPR(GPR::RSP)); + stack_offset += 8; + } + + let mut call_movs: Vec<(Location, GPR)> = vec![]; + // Prepare register & stack parameters. + for (i, param) in params.iter().enumerate().rev() { + let loc = Machine::get_param_location(1 + i, calling_convention); + match loc { + Location::GPR(x) => { + call_movs.push((*param, x)); + } + Location::Memory(_, _) => { + if let Location::Memory(reg, _) = *param { + if reg != GPR::RBP { + return Err(CodegenError { + message: "emit_call_native loc param: unreachable code".to_string(), + }); + } + // TODO: Ensure that the two `Location::Memories` point at the same place? + } + match *param { + Location::Imm64(_) => { + // x86_64 does not support `mov imm64, mem`. We must first place the + // immdiate value into a register and then write the register to the + // memory. Now the problem is that there might not be any registers + // available to clobber. In order to make this work out we spill a + // register thus retaining both the original value of the + // register and producing the required data at the top of the stack. + // + // FIXME(#2723): figure out how to not require spilling a register + // here. It should definitely be possible to `pick_gpr`/`pick_temp_gpr` + // to grab an otherwise unused register and just clobber its value + // here. + self.assembler.emit_push(Size::S64, Location::GPR(GPR::R9)); + self.assembler.emit_mov(Size::S64, *param, Location::GPR(GPR::R9)); + self.assembler.emit_xchg( + Size::S64, + Location::GPR(GPR::R9), + Location::Memory(GPR::RSP, 0), + ); + } + Location::XMM(_) => { + // Dummy value slot to be filled with `mov`. + self.assembler.emit_push(Size::S64, Location::GPR(GPR::RAX)); + + // XMM registers can be directly stored to memory. + self.assembler.emit_mov( + Size::S64, + *param, + Location::Memory(GPR::RSP, 0), + ); + } + _ => self.assembler.emit_push(Size::S64, *param), + } + } + _ => { + return Err(CodegenError { + message: "emit_call_native loc: unreachable code".to_string(), + }) + } + } + } + + // Sort register moves so that register are not overwritten before read. + sort_call_movs(&mut call_movs); + + // Emit register moves. + for (loc, gpr) in call_movs { + if loc != Location::GPR(gpr) { + self.assembler.emit_mov(Size::S64, loc, Location::GPR(gpr)); + } + } + + // Put vmctx as the first parameter. + self.assembler.emit_mov( + Size::S64, + Location::GPR(Machine::get_vmctx_reg()), + Machine::get_param_location(0, calling_convention), + ); // vmctx + + if stack_padding > 0 { + self.assembler.emit_sub( + Size::S64, + Location::Imm32(stack_padding as u32), + Location::GPR(GPR::RSP), + ); + } + + cb(self); + + // Restore stack. + if stack_offset + stack_padding > 0 { + self.assembler.emit_add( + Size::S64, + Location::Imm32((stack_offset + stack_padding) as u32), + Location::GPR(GPR::RSP), + ); + if (stack_offset % 8) != 0 { + return Err(CodegenError { + message: "emit_call_native: Bad restoring stack alignement".to_string(), + }); + } + } + + // Restore XMMs. + if !used_xmms.is_empty() { + for (i, r) in used_xmms.iter().enumerate() { + self.assembler.emit_mov( + Size::S64, + Location::Memory(GPR::RSP, (i * 8) as i32), + Location::XMM(*r), + ); + } + self.assembler.emit_add( + Size::S64, + Location::Imm32((used_xmms.len() * 8) as u32), + Location::GPR(GPR::RSP), + ); + } + + // Restore GPRs. + for r in used_gprs.iter().rev() { + self.assembler.emit_pop(Size::S64, Location::GPR(*r)); + } + + Ok(()) + } + + /// Emits a System V call sequence, specialized for labels as the call target. + fn _emit_call_native_label>( + &mut self, + label: DynamicLabel, + params: I, + ) -> Result<(), CodegenError> { + self.emit_call_native(|this| this.assembler.emit_call_label(label), params)?; + Ok(()) + } + + /// Emits a memory operation. + fn emit_memory_op Result<(), CodegenError>>( + &mut self, + addr: Location, + memarg: &MemArg, + check_alignment: bool, + value_size: usize, + cb: F, + ) -> Result<(), CodegenError> { + let need_check = true; + let tmp_addr = self.machine.acquire_temp_gpr().unwrap(); + + // Reusing `tmp_addr` for temporary indirection here, since it's not used before the last reference to `{base,bound}_loc`. + let (base_loc, bound_loc) = if self.module.import_counts.memories != 0 { + // Imported memories require one level of indirection. + let offset = self.vmoffsets.vmctx_vmmemory_import_definition(MemoryIndex::new(0)); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::Memory(Machine::get_vmctx_reg(), offset as i32), + Location::GPR(tmp_addr), + ); + (Location::Memory(tmp_addr, 0), Location::Memory(tmp_addr, 8)) + } else { + let offset = self.vmoffsets.vmctx_vmmemory_definition(LocalMemoryIndex::new(0)); + ( + Location::Memory(Machine::get_vmctx_reg(), offset as i32), + Location::Memory(Machine::get_vmctx_reg(), (offset + 8) as i32), + ) + }; + + let tmp_base = self.machine.acquire_temp_gpr().unwrap(); + let tmp_bound = self.machine.acquire_temp_gpr().unwrap(); + + // Load base into temporary register. + self.assembler.emit_mov(Size::S64, base_loc, Location::GPR(tmp_base)); + + // Load bound into temporary register, if needed. + if need_check { + self.assembler.emit_mov(Size::S64, bound_loc, Location::GPR(tmp_bound)); + + // Wasm -> Effective. + // Assuming we never underflow - should always be true on Linux/macOS and Windows >=8, + // since the first page from 0x0 to 0x1000 is not accepted by mmap. + + // This `lea` calculates the upper bound allowed for the beginning of the word. + // Since the upper bound of the memory is (exclusively) `tmp_bound + tmp_base`, + // the maximum allowed beginning of word is (inclusively) + // `tmp_bound + tmp_base - value_size`. + self.assembler.emit_lea( + Size::S64, + Location::MemoryAddTriple(tmp_bound, tmp_base, -(value_size as i32)), + Location::GPR(tmp_bound), + ); + } + + // Load effective address. + // `base_loc` and `bound_loc` becomes INVALID after this line, because `tmp_addr` + // might be reused. + self.assembler.emit_mov(Size::S32, addr, Location::GPR(tmp_addr)); + + // Add offset to memory address. + if memarg.offset != 0 { + self.assembler.emit_add( + Size::S32, + Location::Imm32(u32::try_from(memarg.offset).unwrap()), // we don’t support 64-bit memory, and this module was validated + Location::GPR(tmp_addr), + ); + + // Trap if offset calculation overflowed. + self.assembler.emit_jmp(Condition::Carry, self.special_labels.heap_access_oob); + } + + // Wasm linear memory -> real memory + self.assembler.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_addr)); + + if need_check { + // Trap if the end address of the requested area is above that of the linear memory. + self.assembler.emit_cmp(Size::S64, Location::GPR(tmp_bound), Location::GPR(tmp_addr)); + + // `tmp_bound` is inclusive. So trap only if `tmp_addr > tmp_bound`. + self.assembler.emit_jmp(Condition::Above, self.special_labels.heap_access_oob); + } + + self.machine.release_temp_gpr(tmp_bound); + self.machine.release_temp_gpr(tmp_base); + + let align = memarg.align; + if check_alignment && align != 1 { + let tmp_aligncheck = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov( + Size::S32, + Location::GPR(tmp_addr), + Location::GPR(tmp_aligncheck), + ); + self.assembler.emit_and( + Size::S64, + Location::Imm32((align - 1).into()), + Location::GPR(tmp_aligncheck), + ); + self.assembler.emit_jmp(Condition::NotEqual, self.special_labels.heap_access_oob); + self.machine.release_temp_gpr(tmp_aligncheck); + } + + cb(self, tmp_addr).unwrap(); + + self.machine.release_temp_gpr(tmp_addr); + Ok(()) + } + + /// Emits a memory operation. + fn emit_compare_and_swap( + &mut self, + loc: Location, + target: Location, + ret: Location, + memarg: &MemArg, + value_size: usize, + memory_sz: Size, + stack_sz: Size, + cb: F, + ) -> Result<(), CodegenError> { + if memory_sz > stack_sz { + return Err(CodegenError { + message: "emit_compare_and_swap: memory size > stack size".to_string(), + }); + } + + let compare = self.machine.reserve_unused_temp_gpr(GPR::RAX); + let value = if loc == Location::GPR(GPR::R14) { GPR::R13 } else { GPR::R14 }; + self.assembler.emit_push(Size::S64, Location::GPR(value)); + + self.assembler.emit_mov(stack_sz, loc, Location::GPR(value)); + + let retry = self.assembler.get_label(); + self.assembler.emit_label(retry); + + self.emit_memory_op(target, memarg, true, value_size, |this, addr| { + // Memory moves with size < 32b do not zero upper bits. + if memory_sz < Size::S32 { + this.assembler.emit_xor(Size::S32, Location::GPR(compare), Location::GPR(compare)); + } + this.assembler.emit_mov(memory_sz, Location::Memory(addr, 0), Location::GPR(compare)); + this.assembler.emit_mov(stack_sz, Location::GPR(compare), ret); + cb(this, compare, value); + this.assembler.emit_lock_cmpxchg( + memory_sz, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + + self.assembler.emit_jmp(Condition::NotEqual, retry); + + self.assembler.emit_pop(Size::S64, Location::GPR(value)); + self.machine.release_temp_gpr(compare); + Ok(()) + } + + // Checks for underflow/overflow/nan. + fn emit_f32_int_conv_check( + &mut self, + reg: XMM, + lower_bound: f32, + upper_bound: f32, + underflow_label: ::Label, + overflow_label: ::Label, + nan_label: ::Label, + succeed_label: ::Label, + ) { + let lower_bound = f32::to_bits(lower_bound); + let upper_bound = f32::to_bits(upper_bound); + + let tmp = self.machine.acquire_temp_gpr().unwrap(); + let tmp_x = self.machine.acquire_temp_xmm().unwrap(); + + // Underflow. + self.assembler.emit_mov(Size::S32, Location::Imm32(lower_bound), Location::GPR(tmp)); + self.assembler.emit_mov(Size::S32, Location::GPR(tmp), Location::XMM(tmp_x)); + self.assembler.emit_vcmpless(reg, XMMOrMemory::XMM(tmp_x), tmp_x); + self.assembler.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + self.assembler.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + self.assembler.emit_jmp(Condition::NotEqual, underflow_label); + + // Overflow. + self.assembler.emit_mov(Size::S32, Location::Imm32(upper_bound), Location::GPR(tmp)); + self.assembler.emit_mov(Size::S32, Location::GPR(tmp), Location::XMM(tmp_x)); + self.assembler.emit_vcmpgess(reg, XMMOrMemory::XMM(tmp_x), tmp_x); + self.assembler.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + self.assembler.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + self.assembler.emit_jmp(Condition::NotEqual, overflow_label); + + // NaN. + self.assembler.emit_vcmpeqss(reg, XMMOrMemory::XMM(reg), tmp_x); + self.assembler.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + self.assembler.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + self.assembler.emit_jmp(Condition::Equal, nan_label); + + self.assembler.emit_jmp(Condition::None, succeed_label); + + self.machine.release_temp_xmm(tmp_x); + self.machine.release_temp_gpr(tmp); + } + + // Checks for underflow/overflow/nan before IxxTrunc{U/S}F32. + fn emit_f32_int_conv_check_trap(&mut self, reg: XMM, lower_bound: f32, upper_bound: f32) { + let trap_overflow = self.special_labels.integer_overflow; + let trap_badconv = self.special_labels.bad_conversion_to_integer; + let end = self.assembler.get_label(); + + self.emit_f32_int_conv_check( + reg, + lower_bound, + upper_bound, + trap_overflow, + trap_overflow, + trap_badconv, + end, + ); + + self.assembler.emit_label(end); + } + + fn emit_f32_int_conv_check_sat< + F1: FnOnce(&mut Self), + F2: FnOnce(&mut Self), + F3: FnOnce(&mut Self), + F4: FnOnce(&mut Self), + >( + &mut self, + reg: XMM, + lower_bound: f32, + upper_bound: f32, + underflow_cb: F1, + overflow_cb: F2, + nan_cb: Option, + convert_cb: F4, + ) { + // As an optimization nan_cb is optional, and when set to None we turn + // use 'underflow' as the 'nan' label. This is useful for callers who + // set the return value to zero for both underflow and nan. + + let underflow = self.assembler.get_label(); + let overflow = self.assembler.get_label(); + let nan = if nan_cb.is_some() { self.assembler.get_label() } else { underflow }; + let convert = self.assembler.get_label(); + let end = self.assembler.get_label(); + + self.emit_f32_int_conv_check( + reg, + lower_bound, + upper_bound, + underflow, + overflow, + nan, + convert, + ); + + self.assembler.emit_label(underflow); + underflow_cb(self); + self.assembler.emit_jmp(Condition::None, end); + + self.assembler.emit_label(overflow); + overflow_cb(self); + self.assembler.emit_jmp(Condition::None, end); + + if let Some(cb) = nan_cb { + self.assembler.emit_label(nan); + cb(self); + self.assembler.emit_jmp(Condition::None, end); + } + + self.assembler.emit_label(convert); + convert_cb(self); + self.assembler.emit_label(end); + } + + // Checks for underflow/overflow/nan. + fn emit_f64_int_conv_check( + &mut self, + reg: XMM, + lower_bound: f64, + upper_bound: f64, + underflow_label: ::Label, + overflow_label: ::Label, + nan_label: ::Label, + succeed_label: ::Label, + ) { + let lower_bound = f64::to_bits(lower_bound); + let upper_bound = f64::to_bits(upper_bound); + + let tmp = self.machine.acquire_temp_gpr().unwrap(); + let tmp_x = self.machine.acquire_temp_xmm().unwrap(); + + // Underflow. + self.assembler.emit_mov(Size::S64, Location::Imm64(lower_bound), Location::GPR(tmp)); + self.assembler.emit_mov(Size::S64, Location::GPR(tmp), Location::XMM(tmp_x)); + self.assembler.emit_vcmplesd(reg, XMMOrMemory::XMM(tmp_x), tmp_x); + self.assembler.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + self.assembler.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + self.assembler.emit_jmp(Condition::NotEqual, underflow_label); + + // Overflow. + self.assembler.emit_mov(Size::S64, Location::Imm64(upper_bound), Location::GPR(tmp)); + self.assembler.emit_mov(Size::S64, Location::GPR(tmp), Location::XMM(tmp_x)); + self.assembler.emit_vcmpgesd(reg, XMMOrMemory::XMM(tmp_x), tmp_x); + self.assembler.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + self.assembler.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + self.assembler.emit_jmp(Condition::NotEqual, overflow_label); + + // NaN. + self.assembler.emit_vcmpeqsd(reg, XMMOrMemory::XMM(reg), tmp_x); + self.assembler.emit_mov(Size::S32, Location::XMM(tmp_x), Location::GPR(tmp)); + self.assembler.emit_cmp(Size::S32, Location::Imm32(0), Location::GPR(tmp)); + self.assembler.emit_jmp(Condition::Equal, nan_label); + + self.assembler.emit_jmp(Condition::None, succeed_label); + + self.machine.release_temp_xmm(tmp_x); + self.machine.release_temp_gpr(tmp); + } + + // Checks for underflow/overflow/nan before IxxTrunc{U/S}F64. + fn emit_f64_int_conv_check_trap(&mut self, reg: XMM, lower_bound: f64, upper_bound: f64) { + let trap_overflow = self.special_labels.integer_overflow; + let trap_badconv = self.special_labels.bad_conversion_to_integer; + let end = self.assembler.get_label(); + + self.emit_f64_int_conv_check( + reg, + lower_bound, + upper_bound, + trap_overflow, + trap_overflow, + trap_badconv, + end, + ); + + self.assembler.emit_label(end); + } + + fn emit_f64_int_conv_check_sat< + F1: FnOnce(&mut Self), + F2: FnOnce(&mut Self), + F3: FnOnce(&mut Self), + F4: FnOnce(&mut Self), + >( + &mut self, + reg: XMM, + lower_bound: f64, + upper_bound: f64, + underflow_cb: F1, + overflow_cb: F2, + nan_cb: Option, + convert_cb: F4, + ) { + // As an optimization nan_cb is optional, and when set to None we turn + // use 'underflow' as the 'nan' label. This is useful for callers who + // set the return value to zero for both underflow and nan. + + let underflow = self.assembler.get_label(); + let overflow = self.assembler.get_label(); + let nan = if nan_cb.is_some() { self.assembler.get_label() } else { underflow }; + let convert = self.assembler.get_label(); + let end = self.assembler.get_label(); + + self.emit_f64_int_conv_check( + reg, + lower_bound, + upper_bound, + underflow, + overflow, + nan, + convert, + ); + + self.assembler.emit_label(underflow); + underflow_cb(self); + self.assembler.emit_jmp(Condition::None, end); + + self.assembler.emit_label(overflow); + overflow_cb(self); + self.assembler.emit_jmp(Condition::None, end); + + if let Some(cb) = nan_cb { + self.assembler.emit_label(nan); + cb(self); + self.assembler.emit_jmp(Condition::None, end); + } + + self.assembler.emit_label(convert); + convert_cb(self); + self.assembler.emit_label(end); + } + + pub(crate) fn emit_head(&mut self) -> Result<(), CodegenError> { + // TODO: Patchpoint is not emitted for now, and ARM trampoline is not prepended. + + // Normal x86 entry prologue. + self.assembler.emit_push(Size::S64, Location::GPR(GPR::RBP)); + self.assembler.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RBP)); + + // Setup the registers (incl. defining the vmctx register) + let local_count = self.local_count(); + self.machine.setup_registers( + self.assembler, + local_count, + self.signature.params().len() as u32, + self.calling_convention, + ); + + // Verify stack height + self.assembler.emit_sub( + Size::S32, + Location::Imm32(self.stack_size), + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_stack_limit_begin() as i32, + ), + ); + self.assembler.emit_jmp(Condition::Carry, self.special_labels.stack_overflow); + + // Charge for the stack initialization + self.emit_gas_const(self.stack_init_gas_cost); + + // Initialize the locals + let local_count = self.local_count(); + self.machine.init_locals( + self.assembler, + local_count, + self.signature.params().len() as u32, + self.calling_convention, + ); + + self.assembler.emit_sub(Size::S64, Location::Imm32(32), Location::GPR(GPR::RSP)); // simulate "red zone" if not supported by the platform + + self.control_stack.push(ControlFrame { + br_label: self.assembler.get_label(), + loop_like: false, + if_else: IfElseState::None, + returns: self.signature.results().iter().map(|&x| type_to_wp_type(x)).collect(), + value_stack_depth: 0, + fp_stack_depth: 0, + }); + + Ok(()) + } + + /// Pushes the instruction to the address map, calculating the offset from a + /// provided beginning address. + fn mark_instruction_address_end(&mut self, begin: usize) { + self.instructions_address_map.push(InstructionAddressMap { + srcloc: SourceLoc::new(self.src_loc), + code_offset: begin, + code_len: self.assembler.get_offset().0 - begin, + }); + } + + #[tracing::instrument(target = "unc_vm", level = "trace", skip_all)] + pub(crate) fn new( + assembler: &'a mut Assembler, + module: &'a ModuleInfo, + config: &'a Singlepass, + target: &'a Target, + vmoffsets: &'a VMOffsets, + _table_styles: &'a PrimaryMap, + local_func_index: LocalFunctionIndex, + calling_convention: CallingConvention, + stack_init_gas_cost: u64, + gas_offsets: &'a [usize], + gas_costs: &'a [u64], + _gas_kinds: &'a [InstrumentationKind], + stack_size: u64, + ) -> Result, CodegenError> { + let func_index = module.func_index(local_func_index); + let sig_index = module.functions[func_index]; + let signature = module.signatures[sig_index].clone(); + + let special_labels = SpecialLabelSet { + integer_division_by_zero: assembler.get_label(), + integer_overflow: assembler.get_label(), + bad_conversion_to_integer: assembler.get_label(), + heap_access_oob: assembler.get_label(), + table_access_oob: assembler.get_label(), + indirect_call_null: assembler.get_label(), + bad_signature: assembler.get_label(), + gas_limit_exceeded: assembler.get_label(), + stack_overflow: assembler.get_label(), + }; + + let mut fg = FuncGen { + module, + config, + target, + vmoffsets, + local_types: unc_vm_types::partial_sum_map::PartialSumMap::new(), + assembler, + value_stack: vec![], + fp_stack: vec![], + control_stack: vec![], + machine: Machine::new(), + unreachable_depth: 0, + relocations: vec![], + special_labels, + src_loc: 0, + instructions_address_map: vec![], + calling_convention, + signature, + stack_init_gas_cost, + gas_iter: gas_offsets.iter().zip(gas_costs.iter()).peekable(), + stack_size: u32::try_from(stack_size).map_err(|_| CodegenError { + message: "one function has a stack more than u32::MAX deep".to_string(), + })?, + }; + for param in module.signatures[sig_index].params() { + fg.feed_local(1, type_to_wp_type(*param)); + } + Ok(fg) + } + + pub(crate) fn has_control_frames(&self) -> bool { + !self.control_stack.is_empty() + } + + /// Introduce additional local variables to this function. + /// + /// Calling this after [`emit_head`](Self::emit_head) has been invoked is non-sensical. + pub(crate) fn feed_local(&mut self, local_count: u32, local_type: WpType) { + // FIXME: somehow verify that we haven't invoked `emit_head` yet? Doing so could lead us to + // generate code that accesses the stack buffer out of bounds. + self.local_types + .push(local_count, local_type) + .expect("module cannot have more than u32::MAX locals"); + } + + /// Total number of locals and arguments so far. + /// + /// More can be introduced with the [`feed_local`](Self::feed_local) method. + pub(crate) fn local_count(&self) -> u32 { + *self.local_types.size() + } + + /// Obtain the type of the local or argument at the specified index. + /// + /// # Panics + /// + /// Note that this will panic if `index` is out of bounds, which can happen if an + /// implementation error has occurred or if the WASM module hasn't been validated to conform to + /// the web assembly specification. + pub(crate) fn local_type(&self, index: u32) -> WpType { + *self.local_types.find(index).expect("local index out of bounds") + } + + /// Consume offset self.src_loc, return Some(cost) iff there must be an instrumentation point here + fn consume_gas_offset(&mut self /*, should_be_unreachable: bool */) -> Option { + if let Some(&(&offset, &cost /* (&cost, &kind) */)) = self.gas_iter.peek() { + if offset == self.src_loc as usize { + // assert!(matches!(kind, InstrumentationKind::Unreachable) == should_be_unreachable, "gas computation results are not of the expected reachability: kind is {:?}, expected reachability is {:?}", kind, !should_be_unreachable); + self.gas_iter.next().unwrap(); + return Some(cost); + } + } + None + } + + #[tracing::instrument(target = "unc_vm", level = "trace", skip(self))] + pub(crate) fn feed_operator(&mut self, op: Operator) -> Result<(), CodegenError> { + assert!(self.fp_stack.len() <= self.value_stack.len()); + + let was_unreachable = self.unreachable_depth > 0; + + if was_unreachable { + match op { + Operator::Block { .. } | Operator::Loop { .. } | Operator::If { .. } => { + self.unreachable_depth += 1; + } + Operator::End => { + self.unreachable_depth -= 1; + } + Operator::Else => { + // We are in a reachable true branch + if self.unreachable_depth == 1 { + if let Some(IfElseState::If(_)) = + self.control_stack.last().map(|x| x.if_else) + { + self.unreachable_depth -= 1; + } + } + } + _ => {} + } + if self.unreachable_depth > 0 { + while self.consume_gas_offset(/* true */).is_some() {} // do not instrument unreachable code + return Ok(()); + } + } + + while let Some(cost) = self.consume_gas_offset(/* false */) { + self.emit_gas_const(cost); + } + + match op { + Operator::GlobalGet { global_index } => { + let global_index = GlobalIndex::from_u32(global_index); + + let ty = type_to_wp_type(self.module.globals[global_index].ty); + if ty.is_float() { + self.fp_stack.push(FloatValue::new(self.value_stack.len())); + } + let loc = self.machine.acquire_locations(self.assembler, &[(ty)], false)[0]; + self.value_stack.push(loc); + + let tmp = self.machine.acquire_temp_gpr().unwrap(); + + let src = if let Some(local_global_index) = + self.module.local_global_index(global_index) + { + let offset = self.vmoffsets.vmctx_vmglobal_definition(local_global_index); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::Memory(Machine::get_vmctx_reg(), offset as i32), + Location::GPR(tmp), + ); + Location::Memory(tmp, 0) + } else { + // Imported globals require one level of indirection. + let offset = self.vmoffsets.vmctx_vmglobal_import_definition(global_index); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::Memory(Machine::get_vmctx_reg(), offset as i32), + Location::GPR(tmp), + ); + Location::Memory(tmp, 0) + }; + + self.emit_relaxed_binop(Assembler::emit_mov, Size::S64, src, loc); + + self.machine.release_temp_gpr(tmp); + } + Operator::GlobalSet { global_index } => { + let global_index = GlobalIndex::from_u32(global_index); + let tmp = self.machine.acquire_temp_gpr().unwrap(); + let dst = if let Some(local_global_index) = + self.module.local_global_index(global_index) + { + let offset = self.vmoffsets.vmctx_vmglobal_definition(local_global_index); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::Memory(Machine::get_vmctx_reg(), offset as i32), + Location::GPR(tmp), + ); + Location::Memory(tmp, 0) + } else { + // Imported globals require one level of indirection. + let offset = self.vmoffsets.vmctx_vmglobal_import_definition(global_index); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::Memory(Machine::get_vmctx_reg(), offset as i32), + Location::GPR(tmp), + ); + Location::Memory(tmp, 0) + }; + let ty = type_to_wp_type(self.module.globals[global_index].ty); + let loc = self.pop_value_released(); + if ty.is_float() { + let fp = self.fp_stack.pop1()?; + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization + && fp.canonicalization.is_some() + { + self.canonicalize_nan( + match ty { + WpType::F32 => Size::S32, + WpType::F64 => Size::S64, + _ => unreachable!(), + }, + loc, + dst, + ); + } else { + self.emit_relaxed_binop(Assembler::emit_mov, Size::S64, loc, dst); + } + } else { + self.emit_relaxed_binop(Assembler::emit_mov, Size::S64, loc, dst); + } + self.machine.release_temp_gpr(tmp); + } + Operator::LocalGet { local_index } => { + let local_type = self.local_type(local_index); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + self.machine.get_local_location(local_index), + ret, + ); + self.value_stack.push(ret); + if local_type.is_float() { + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); + } + } + Operator::LocalSet { local_index } => { + let loc = self.pop_value_released(); + let local_type = self.local_type(local_index); + + if local_type.is_float() { + let fp = self.fp_stack.pop1()?; + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization + && fp.canonicalization.is_some() + { + self.canonicalize_nan( + match local_type { + WpType::F32 => Size::S32, + WpType::F64 => Size::S64, + _ => unreachable!(), + }, + loc, + self.machine.get_local_location(local_index), + ); + } else { + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + self.machine.get_local_location(local_index), + ); + } + } else { + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + self.machine.get_local_location(local_index), + ); + } + } + Operator::LocalTee { local_index } => { + let loc = *self.value_stack.last().unwrap(); + let local_type = self.local_type(local_index); + if local_type.is_float() { + let fp = self.fp_stack.peek1()?; + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization + && fp.canonicalization.is_some() + { + self.canonicalize_nan( + match local_type { + WpType::F32 => Size::S32, + WpType::F64 => Size::S64, + _ => unreachable!(), + }, + loc, + self.machine.get_local_location(local_index), + ); + } else { + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + self.machine.get_local_location(local_index), + ); + } + } else { + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + self.machine.get_local_location(local_index), + ); + } + } + Operator::I32Const { value } => { + self.value_stack.push(Location::Imm32(value as u32)); + } + Operator::I32Add => self.emit_binop_i32(Assembler::emit_add), + Operator::I32Sub => self.emit_binop_i32(Assembler::emit_sub), + Operator::I32Mul => self.emit_binop_i32(Assembler::emit_imul), + Operator::I32DivU => { + // We assume that RAX and RDX are temporary registers here. + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::I32); + self.assembler.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); + self.assembler.emit_xor( + Size::S32, + Location::GPR(GPR::RDX), + Location::GPR(GPR::RDX), + ); + self.emit_relaxed_xdiv(false, Size::S32, loc_b); + self.assembler.emit_mov(Size::S32, Location::GPR(GPR::RAX), ret); + } + Operator::I32DivS => { + // We assume that RAX and RDX are temporary registers here. + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::I32); + self.assembler.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); + self.assembler.emit_cdq(); + self.emit_relaxed_xdiv(true, Size::S32, loc_b); + self.assembler.emit_mov(Size::S32, Location::GPR(GPR::RAX), ret); + } + Operator::I32RemU => { + // We assume that RAX and RDX are temporary registers here. + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::I32); + self.assembler.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); + self.assembler.emit_xor( + Size::S32, + Location::GPR(GPR::RDX), + Location::GPR(GPR::RDX), + ); + self.emit_relaxed_xdiv(false, Size::S32, loc_b); + self.assembler.emit_mov(Size::S32, Location::GPR(GPR::RDX), ret); + } + Operator::I32RemS => { + // We assume that RAX and RDX are temporary registers here. + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::I32); + + let normal_path = self.assembler.get_label(); + let end = self.assembler.get_label(); + + self.emit_relaxed_binop( + Assembler::emit_cmp, + Size::S32, + Location::Imm32(0x80000000), + loc_a, + ); + self.assembler.emit_jmp(Condition::NotEqual, normal_path); + self.emit_relaxed_binop( + Assembler::emit_cmp, + Size::S32, + Location::Imm32(0xffffffff), + loc_b, + ); + self.assembler.emit_jmp(Condition::NotEqual, normal_path); + self.assembler.emit_mov(Size::S32, Location::Imm32(0), ret); + self.assembler.emit_jmp(Condition::None, end); + + self.assembler.emit_label(normal_path); + self.assembler.emit_mov(Size::S32, loc_a, Location::GPR(GPR::RAX)); + self.assembler.emit_cdq(); + self.emit_relaxed_xdiv(true, Size::S32, loc_b); + self.assembler.emit_mov(Size::S32, Location::GPR(GPR::RDX), ret); + + self.assembler.emit_label(end); + } + Operator::I32And => self.emit_binop_i32(Assembler::emit_and), + Operator::I32Or => self.emit_binop_i32(Assembler::emit_or), + Operator::I32Xor => self.emit_binop_i32(Assembler::emit_xor), + Operator::I32Eq => self.emit_cmpop_i32(Condition::Equal)?, + Operator::I32Ne => self.emit_cmpop_i32(Condition::NotEqual)?, + Operator::I32Eqz => { + self.emit_cmpop_i32_dynamic_b(Condition::Equal, Location::Imm32(0))? + } + Operator::I32Clz => { + let loc = self.pop_value_released(); + let src = match loc { + Location::Imm32(_) | Location::Memory(_, _) => { + let tmp = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S32, loc, Location::GPR(tmp)); + tmp + } + Location::GPR(reg) => reg, + _ => { + return Err(CodegenError { + message: "I32Clz src: unreachable code".to_string(), + }) + } + }; + + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + let dst = match ret { + Location::Memory(_, _) => self.machine.acquire_temp_gpr().unwrap(), + Location::GPR(reg) => reg, + _ => { + return Err(CodegenError { + message: "I32Clz dst: unreachable code".to_string(), + }) + } + }; + + if self.assembler.arch_has_xzcnt(self.target.cpu_features()) { + self.assembler.arch_emit_lzcnt( + Size::S32, + Location::GPR(src), + Location::GPR(dst), + ); + } else { + let zero_path = self.assembler.get_label(); + let end = self.assembler.get_label(); + + self.assembler.emit_test_gpr_64(src); + self.assembler.emit_jmp(Condition::Equal, zero_path); + self.assembler.emit_bsr(Size::S32, Location::GPR(src), Location::GPR(dst)); + self.assembler.emit_xor(Size::S32, Location::Imm32(31), Location::GPR(dst)); + self.assembler.emit_jmp(Condition::None, end); + self.assembler.emit_label(zero_path); + self.assembler.emit_mov(Size::S32, Location::Imm32(32), Location::GPR(dst)); + self.assembler.emit_label(end); + } + + match loc { + Location::Imm32(_) | Location::Memory(_, _) => { + self.machine.release_temp_gpr(src); + } + _ => {} + }; + if let Location::Memory(_, _) = ret { + self.assembler.emit_mov(Size::S32, Location::GPR(dst), ret); + self.machine.release_temp_gpr(dst); + }; + } + Operator::I32Ctz => { + let loc = self.pop_value_released(); + let src = match loc { + Location::Imm32(_) | Location::Memory(_, _) => { + let tmp = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S32, loc, Location::GPR(tmp)); + tmp + } + Location::GPR(reg) => reg, + _ => { + return Err(CodegenError { + message: "I32Ctz src: unreachable code".to_string(), + }) + } + }; + + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + let dst = match ret { + Location::Memory(_, _) => self.machine.acquire_temp_gpr().unwrap(), + Location::GPR(reg) => reg, + _ => { + return Err(CodegenError { + message: "I32Ctz dst: unreachable code".to_string(), + }) + } + }; + + if self.assembler.arch_has_xzcnt(self.target.cpu_features()) { + self.assembler.arch_emit_tzcnt( + Size::S32, + Location::GPR(src), + Location::GPR(dst), + ); + } else { + let zero_path = self.assembler.get_label(); + let end = self.assembler.get_label(); + + self.assembler.emit_test_gpr_64(src); + self.assembler.emit_jmp(Condition::Equal, zero_path); + self.assembler.emit_bsf(Size::S32, Location::GPR(src), Location::GPR(dst)); + self.assembler.emit_jmp(Condition::None, end); + self.assembler.emit_label(zero_path); + self.assembler.emit_mov(Size::S32, Location::Imm32(32), Location::GPR(dst)); + self.assembler.emit_label(end); + } + + match loc { + Location::Imm32(_) | Location::Memory(_, _) => { + self.machine.release_temp_gpr(src); + } + _ => {} + }; + if let Location::Memory(_, _) = ret { + self.assembler.emit_mov(Size::S32, Location::GPR(dst), ret); + self.machine.release_temp_gpr(dst); + }; + } + Operator::I32Popcnt => self.emit_xcnt_i32(Assembler::emit_popcnt)?, + Operator::I32Shl => self.emit_shift_i32(Assembler::emit_shl), + Operator::I32ShrU => self.emit_shift_i32(Assembler::emit_shr), + Operator::I32ShrS => self.emit_shift_i32(Assembler::emit_sar), + Operator::I32Rotl => self.emit_shift_i32(Assembler::emit_rol), + Operator::I32Rotr => self.emit_shift_i32(Assembler::emit_ror), + Operator::I32LtU => self.emit_cmpop_i32(Condition::Below)?, + Operator::I32LeU => self.emit_cmpop_i32(Condition::BelowEqual)?, + Operator::I32GtU => self.emit_cmpop_i32(Condition::Above)?, + Operator::I32GeU => self.emit_cmpop_i32(Condition::AboveEqual)?, + Operator::I32LtS => { + self.emit_cmpop_i32(Condition::Less)?; + } + Operator::I32LeS => self.emit_cmpop_i32(Condition::LessEqual)?, + Operator::I32GtS => self.emit_cmpop_i32(Condition::Greater)?, + Operator::I32GeS => self.emit_cmpop_i32(Condition::GreaterEqual)?, + Operator::I64Const { value } => { + let value = value as u64; + self.value_stack.push(Location::Imm64(value)); + } + Operator::I64Add => self.emit_binop_i64(Assembler::emit_add), + Operator::I64Sub => self.emit_binop_i64(Assembler::emit_sub), + Operator::I64Mul => self.emit_binop_i64(Assembler::emit_imul), + Operator::I64DivU => { + // We assume that RAX and RDX are temporary registers here. + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::I64); + self.assembler.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); + self.assembler.emit_xor( + Size::S64, + Location::GPR(GPR::RDX), + Location::GPR(GPR::RDX), + ); + self.emit_relaxed_xdiv(false, Size::S64, loc_b); + self.assembler.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + Operator::I64DivS => { + // We assume that RAX and RDX are temporary registers here. + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::I64); + self.assembler.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); + self.assembler.emit_cqo(); + self.emit_relaxed_xdiv(true, Size::S64, loc_b); + self.assembler.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + Operator::I64RemU => { + // We assume that RAX and RDX are temporary registers here. + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::I64); + self.assembler.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); + self.assembler.emit_xor( + Size::S64, + Location::GPR(GPR::RDX), + Location::GPR(GPR::RDX), + ); + self.emit_relaxed_xdiv(false, Size::S64, loc_b); + self.assembler.emit_mov(Size::S64, Location::GPR(GPR::RDX), ret); + } + Operator::I64RemS => { + // We assume that RAX and RDX are temporary registers here. + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::I64); + + let normal_path = self.assembler.get_label(); + let end = self.assembler.get_label(); + + self.emit_relaxed_binop( + Assembler::emit_cmp, + Size::S64, + Location::Imm64(0x8000000000000000u64), + loc_a, + ); + self.assembler.emit_jmp(Condition::NotEqual, normal_path); + self.emit_relaxed_binop( + Assembler::emit_cmp, + Size::S64, + Location::Imm64(0xffffffffffffffffu64), + loc_b, + ); + self.assembler.emit_jmp(Condition::NotEqual, normal_path); + self.emit_relaxed_binop(Assembler::emit_mov, Size::S64, Location::Imm64(0), ret); + self.assembler.emit_jmp(Condition::None, end); + + self.assembler.emit_label(normal_path); + + self.assembler.emit_mov(Size::S64, loc_a, Location::GPR(GPR::RAX)); + self.assembler.emit_cqo(); + self.emit_relaxed_xdiv(true, Size::S64, loc_b); + self.assembler.emit_mov(Size::S64, Location::GPR(GPR::RDX), ret); + self.assembler.emit_label(end); + } + Operator::I64And => self.emit_binop_i64(Assembler::emit_and), + Operator::I64Or => self.emit_binop_i64(Assembler::emit_or), + Operator::I64Xor => self.emit_binop_i64(Assembler::emit_xor), + Operator::I64Eq => self.emit_cmpop_i64(Condition::Equal)?, + Operator::I64Ne => self.emit_cmpop_i64(Condition::NotEqual)?, + Operator::I64Eqz => { + self.emit_cmpop_i64_dynamic_b(Condition::Equal, Location::Imm64(0))? + } + Operator::I64Clz => { + let loc = self.pop_value_released(); + let src = match loc { + Location::Imm64(_) | Location::Imm32(_) | Location::Memory(_, _) => { + let tmp = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S64, loc, Location::GPR(tmp)); + tmp + } + Location::GPR(reg) => reg, + _ => { + return Err(CodegenError { + message: "I64Clz src: unreachable code".to_string(), + }) + } + }; + + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let dst = match ret { + Location::Memory(_, _) => self.machine.acquire_temp_gpr().unwrap(), + Location::GPR(reg) => reg, + _ => { + return Err(CodegenError { + message: "I64Clz dst: unreachable code".to_string(), + }) + } + }; + + if self.assembler.arch_has_xzcnt(self.target.cpu_features()) { + self.assembler.arch_emit_lzcnt( + Size::S64, + Location::GPR(src), + Location::GPR(dst), + ); + } else { + let zero_path = self.assembler.get_label(); + let end = self.assembler.get_label(); + + self.assembler.emit_test_gpr_64(src); + self.assembler.emit_jmp(Condition::Equal, zero_path); + self.assembler.emit_bsr(Size::S64, Location::GPR(src), Location::GPR(dst)); + self.assembler.emit_xor(Size::S64, Location::Imm32(63), Location::GPR(dst)); + self.assembler.emit_jmp(Condition::None, end); + self.assembler.emit_label(zero_path); + self.assembler.emit_mov(Size::S64, Location::Imm32(64), Location::GPR(dst)); + self.assembler.emit_label(end); + } + + match loc { + Location::Imm64(_) | Location::Imm32(_) | Location::Memory(_, _) => { + self.machine.release_temp_gpr(src); + } + _ => {} + }; + if let Location::Memory(_, _) = ret { + self.assembler.emit_mov(Size::S64, Location::GPR(dst), ret); + self.machine.release_temp_gpr(dst); + }; + } + Operator::I64Ctz => { + let loc = self.pop_value_released(); + let src = match loc { + Location::Imm64(_) | Location::Imm32(_) | Location::Memory(_, _) => { + let tmp = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S64, loc, Location::GPR(tmp)); + tmp + } + Location::GPR(reg) => reg, + _ => { + return Err(CodegenError { + message: "I64Ctz src: unreachable code".to_string(), + }) + } + }; + + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let dst = match ret { + Location::Memory(_, _) => self.machine.acquire_temp_gpr().unwrap(), + Location::GPR(reg) => reg, + _ => { + return Err(CodegenError { + message: "I64Ctz dst: unreachable code".to_string(), + }) + } + }; + + if self.assembler.arch_has_xzcnt(self.target.cpu_features()) { + self.assembler.arch_emit_tzcnt( + Size::S64, + Location::GPR(src), + Location::GPR(dst), + ); + } else { + let zero_path = self.assembler.get_label(); + let end = self.assembler.get_label(); + + self.assembler.emit_test_gpr_64(src); + self.assembler.emit_jmp(Condition::Equal, zero_path); + self.assembler.emit_bsf(Size::S64, Location::GPR(src), Location::GPR(dst)); + self.assembler.emit_jmp(Condition::None, end); + self.assembler.emit_label(zero_path); + self.assembler.emit_mov(Size::S64, Location::Imm32(64), Location::GPR(dst)); + self.assembler.emit_label(end); + } + + match loc { + Location::Imm64(_) | Location::Imm32(_) | Location::Memory(_, _) => { + self.machine.release_temp_gpr(src); + } + _ => {} + }; + if let Location::Memory(_, _) = ret { + self.assembler.emit_mov(Size::S64, Location::GPR(dst), ret); + self.machine.release_temp_gpr(dst); + }; + } + Operator::I64Popcnt => self.emit_xcnt_i64(Assembler::emit_popcnt)?, + Operator::I64Shl => self.emit_shift_i64(Assembler::emit_shl), + Operator::I64ShrU => self.emit_shift_i64(Assembler::emit_shr), + Operator::I64ShrS => self.emit_shift_i64(Assembler::emit_sar), + Operator::I64Rotl => self.emit_shift_i64(Assembler::emit_rol), + Operator::I64Rotr => self.emit_shift_i64(Assembler::emit_ror), + Operator::I64LtU => self.emit_cmpop_i64(Condition::Below)?, + Operator::I64LeU => self.emit_cmpop_i64(Condition::BelowEqual)?, + Operator::I64GtU => self.emit_cmpop_i64(Condition::Above)?, + Operator::I64GeU => self.emit_cmpop_i64(Condition::AboveEqual)?, + Operator::I64LtS => { + self.emit_cmpop_i64(Condition::Less)?; + } + Operator::I64LeS => self.emit_cmpop_i64(Condition::LessEqual)?, + Operator::I64GtS => self.emit_cmpop_i64(Condition::Greater)?, + Operator::I64GeS => self.emit_cmpop_i64(Condition::GreaterEqual)?, + Operator::I64ExtendI32U => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + self.emit_relaxed_binop(Assembler::emit_mov, Size::S32, loc, ret); + + // A 32-bit memory write does not automatically clear the upper 32 bits of a 64-bit word. + // So, we need to explicitly write zero to the upper half here. + if let Location::Memory(base, off) = ret { + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + Location::Imm32(0), + Location::Memory(base, off + 4), + ); + } + } + Operator::I64ExtendI32S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + self.emit_relaxed_zx_sx(Assembler::emit_movsx, Size::S32, loc, Size::S64, ret)?; + } + Operator::I32Extend8S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_relaxed_zx_sx(Assembler::emit_movsx, Size::S8, loc, Size::S32, ret)?; + } + Operator::I32Extend16S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_relaxed_zx_sx(Assembler::emit_movsx, Size::S16, loc, Size::S32, ret)?; + } + Operator::I64Extend8S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_relaxed_zx_sx(Assembler::emit_movsx, Size::S8, loc, Size::S64, ret)?; + } + Operator::I64Extend16S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_relaxed_zx_sx(Assembler::emit_movsx, Size::S16, loc, Size::S64, ret)?; + } + Operator::I64Extend32S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_relaxed_zx_sx(Assembler::emit_movsx, Size::S32, loc, Size::S64, ret)?; + } + Operator::I32WrapI64 => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + self.emit_relaxed_binop(Assembler::emit_mov, Size::S32, loc, ret); + } + + Operator::F32Const { value } => { + self.value_stack.push(Location::Imm32(value.bits())); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); + } + Operator::F32Add => { + self.fp_stack.pop2()?; + self.fp_stack.push(FloatValue::cncl_f32(self.value_stack.len() - 2)); + self.emit_fp_binop_avx(Assembler::emit_vaddss)?; + } + Operator::F32Sub => { + self.fp_stack.pop2()?; + self.fp_stack.push(FloatValue::cncl_f32(self.value_stack.len() - 2)); + self.emit_fp_binop_avx(Assembler::emit_vsubss)? + } + Operator::F32Mul => { + self.fp_stack.pop2()?; + self.fp_stack.push(FloatValue::cncl_f32(self.value_stack.len() - 2)); + self.emit_fp_binop_avx(Assembler::emit_vmulss)? + } + Operator::F32Div => { + self.fp_stack.pop2()?; + self.fp_stack.push(FloatValue::cncl_f32(self.value_stack.len() - 2)); + self.emit_fp_binop_avx(Assembler::emit_vdivss)? + } + Operator::F32Max => { + self.fp_stack.pop2()?; + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 2)); + if !self.assembler.arch_supports_canonicalize_nan() { + self.emit_fp_binop_avx(Assembler::emit_vmaxss)?; + } else { + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::F64); + + let tmp1 = self.machine.acquire_temp_xmm().unwrap(); + let tmp2 = self.machine.acquire_temp_xmm().unwrap(); + let tmpg1 = self.machine.acquire_temp_gpr().unwrap(); + let tmpg2 = self.machine.acquire_temp_gpr().unwrap(); + + let src1 = match loc_a { + Location::XMM(x) => x, + Location::GPR(_) | Location::Memory(_, _) => { + self.assembler.emit_mov(Size::S64, loc_a, Location::XMM(tmp1)); + tmp1 + } + Location::Imm32(_) => { + self.assembler.emit_mov(Size::S32, loc_a, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S32, + Location::GPR(tmpg1), + Location::XMM(tmp1), + ); + tmp1 + } + Location::Imm64(_) => { + self.assembler.emit_mov(Size::S64, loc_a, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S64, + Location::GPR(tmpg1), + Location::XMM(tmp1), + ); + tmp1 + } + _ => { + return Err(CodegenError { + message: "F32Max src1: unreachable code".to_string(), + }) + } + }; + let src2 = match loc_b { + Location::XMM(x) => x, + Location::GPR(_) | Location::Memory(_, _) => { + self.assembler.emit_mov(Size::S64, loc_b, Location::XMM(tmp2)); + tmp2 + } + Location::Imm32(_) => { + self.assembler.emit_mov(Size::S32, loc_b, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S32, + Location::GPR(tmpg1), + Location::XMM(tmp2), + ); + tmp2 + } + Location::Imm64(_) => { + self.assembler.emit_mov(Size::S64, loc_b, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S64, + Location::GPR(tmpg1), + Location::XMM(tmp2), + ); + tmp2 + } + _ => { + return Err(CodegenError { + message: "F32Max src2: unreachable code".to_string(), + }) + } + }; + + let tmp_xmm1 = XMM::XMM8; + let tmp_xmm2 = XMM::XMM9; + let tmp_xmm3 = XMM::XMM10; + + self.assembler.emit_mov(Size::S32, Location::XMM(src1), Location::GPR(tmpg1)); + self.assembler.emit_mov(Size::S32, Location::XMM(src2), Location::GPR(tmpg2)); + self.assembler.emit_cmp(Size::S32, Location::GPR(tmpg2), Location::GPR(tmpg1)); + self.assembler.emit_vmaxss(src1, XMMOrMemory::XMM(src2), tmp_xmm1); + let label1 = self.assembler.get_label(); + let label2 = self.assembler.get_label(); + self.assembler.emit_jmp(Condition::NotEqual, label1); + self.assembler + .emit_vmovaps(XMMOrMemory::XMM(tmp_xmm1), XMMOrMemory::XMM(tmp_xmm2)); + self.assembler.emit_jmp(Condition::None, label2); + self.assembler.emit_label(label1); + self.assembler.emit_vxorps(tmp_xmm2, XMMOrMemory::XMM(tmp_xmm2), tmp_xmm2); + self.assembler.emit_label(label2); + self.assembler.emit_vcmpeqss(src1, XMMOrMemory::XMM(src2), tmp_xmm3); + self.assembler.emit_vblendvps( + tmp_xmm3, + XMMOrMemory::XMM(tmp_xmm2), + tmp_xmm1, + tmp_xmm1, + ); + self.assembler.emit_vcmpunordss(src1, XMMOrMemory::XMM(src2), src1); + // load float canonical nan + self.assembler.emit_mov( + Size::S64, + Location::Imm32(0x7FC0_0000), // Canonical NaN + Location::GPR(tmpg1), + ); + self.assembler.emit_mov(Size::S64, Location::GPR(tmpg1), Location::XMM(src2)); + self.assembler.emit_vblendvps(src1, XMMOrMemory::XMM(src2), tmp_xmm1, src1); + match ret { + Location::XMM(x) => { + self.assembler + .emit_vmovaps(XMMOrMemory::XMM(src1), XMMOrMemory::XMM(x)); + } + Location::Memory(_, _) | Location::GPR(_) => { + self.assembler.emit_mov(Size::S64, Location::XMM(src1), ret); + } + _ => { + return Err(CodegenError { + message: "F32Max ret: unreachable code".to_string(), + }) + } + } + + self.machine.release_temp_gpr(tmpg2); + self.machine.release_temp_gpr(tmpg1); + self.machine.release_temp_xmm(tmp2); + self.machine.release_temp_xmm(tmp1); + } + } + Operator::F32Min => { + self.fp_stack.pop2()?; + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 2)); + if !self.assembler.arch_supports_canonicalize_nan() { + self.emit_fp_binop_avx(Assembler::emit_vminss)?; + } else { + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::F64); + + let tmp1 = self.machine.acquire_temp_xmm().unwrap(); + let tmp2 = self.machine.acquire_temp_xmm().unwrap(); + let tmpg1 = self.machine.acquire_temp_gpr().unwrap(); + let tmpg2 = self.machine.acquire_temp_gpr().unwrap(); + + let src1 = match loc_a { + Location::XMM(x) => x, + Location::GPR(_) | Location::Memory(_, _) => { + self.assembler.emit_mov(Size::S64, loc_a, Location::XMM(tmp1)); + tmp1 + } + Location::Imm32(_) => { + self.assembler.emit_mov(Size::S32, loc_a, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S32, + Location::GPR(tmpg1), + Location::XMM(tmp1), + ); + tmp1 + } + Location::Imm64(_) => { + self.assembler.emit_mov(Size::S64, loc_a, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S64, + Location::GPR(tmpg1), + Location::XMM(tmp1), + ); + tmp1 + } + _ => { + return Err(CodegenError { + message: "F32Min src1: unreachable code".to_string(), + }) + } + }; + let src2 = match loc_b { + Location::XMM(x) => x, + Location::GPR(_) | Location::Memory(_, _) => { + self.assembler.emit_mov(Size::S64, loc_b, Location::XMM(tmp2)); + tmp2 + } + Location::Imm32(_) => { + self.assembler.emit_mov(Size::S32, loc_b, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S32, + Location::GPR(tmpg1), + Location::XMM(tmp2), + ); + tmp2 + } + Location::Imm64(_) => { + self.assembler.emit_mov(Size::S64, loc_b, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S64, + Location::GPR(tmpg1), + Location::XMM(tmp2), + ); + tmp2 + } + _ => { + return Err(CodegenError { + message: "F32Min src2: unreachable code".to_string(), + }) + } + }; + + let tmp_xmm1 = XMM::XMM8; + let tmp_xmm2 = XMM::XMM9; + let tmp_xmm3 = XMM::XMM10; + + self.assembler.emit_mov(Size::S32, Location::XMM(src1), Location::GPR(tmpg1)); + self.assembler.emit_mov(Size::S32, Location::XMM(src2), Location::GPR(tmpg2)); + self.assembler.emit_cmp(Size::S32, Location::GPR(tmpg2), Location::GPR(tmpg1)); + self.assembler.emit_vminss(src1, XMMOrMemory::XMM(src2), tmp_xmm1); + let label1 = self.assembler.get_label(); + let label2 = self.assembler.get_label(); + self.assembler.emit_jmp(Condition::NotEqual, label1); + self.assembler + .emit_vmovaps(XMMOrMemory::XMM(tmp_xmm1), XMMOrMemory::XMM(tmp_xmm2)); + self.assembler.emit_jmp(Condition::None, label2); + self.assembler.emit_label(label1); + // load float -0.0 + self.assembler.emit_mov( + Size::S64, + Location::Imm32(0x8000_0000), // Negative zero + Location::GPR(tmpg1), + ); + self.assembler.emit_mov( + Size::S64, + Location::GPR(tmpg1), + Location::XMM(tmp_xmm2), + ); + self.assembler.emit_label(label2); + self.assembler.emit_vcmpeqss(src1, XMMOrMemory::XMM(src2), tmp_xmm3); + self.assembler.emit_vblendvps( + tmp_xmm3, + XMMOrMemory::XMM(tmp_xmm2), + tmp_xmm1, + tmp_xmm1, + ); + self.assembler.emit_vcmpunordss(src1, XMMOrMemory::XMM(src2), src1); + // load float canonical nan + self.assembler.emit_mov( + Size::S64, + Location::Imm32(0x7FC0_0000), // Canonical NaN + Location::GPR(tmpg1), + ); + self.assembler.emit_mov(Size::S64, Location::GPR(tmpg1), Location::XMM(src2)); + self.assembler.emit_vblendvps(src1, XMMOrMemory::XMM(src2), tmp_xmm1, src1); + match ret { + Location::XMM(x) => { + self.assembler + .emit_vmovaps(XMMOrMemory::XMM(src1), XMMOrMemory::XMM(x)); + } + Location::Memory(_, _) | Location::GPR(_) => { + self.assembler.emit_mov(Size::S64, Location::XMM(src1), ret); + } + _ => { + return Err(CodegenError { + message: "F32Min ret: unreachable code".to_string(), + }) + } + } + + self.machine.release_temp_gpr(tmpg2); + self.machine.release_temp_gpr(tmpg1); + self.machine.release_temp_xmm(tmp2); + self.machine.release_temp_xmm(tmp1); + } + } + Operator::F32Eq => { + self.fp_stack.pop2()?; + self.emit_fp_cmpop_avx(Assembler::emit_vcmpeqss)? + } + Operator::F32Ne => { + self.fp_stack.pop2()?; + self.emit_fp_cmpop_avx(Assembler::emit_vcmpneqss)? + } + Operator::F32Lt => { + self.fp_stack.pop2()?; + self.emit_fp_cmpop_avx(Assembler::emit_vcmpltss)? + } + Operator::F32Le => { + self.fp_stack.pop2()?; + self.emit_fp_cmpop_avx(Assembler::emit_vcmpless)? + } + Operator::F32Gt => { + self.fp_stack.pop2()?; + self.emit_fp_cmpop_avx(Assembler::emit_vcmpgtss)? + } + Operator::F32Ge => { + self.fp_stack.pop2()?; + self.emit_fp_cmpop_avx(Assembler::emit_vcmpgess)? + } + Operator::F32Nearest => { + self.fp_stack.pop1()?; + self.fp_stack.push(FloatValue::cncl_f32(self.value_stack.len() - 1)); + self.emit_fp_unop_avx(Assembler::emit_vroundss_nearest)? + } + Operator::F32Floor => { + self.fp_stack.pop1()?; + self.fp_stack.push(FloatValue::cncl_f32(self.value_stack.len() - 1)); + self.emit_fp_unop_avx(Assembler::emit_vroundss_floor)? + } + Operator::F32Ceil => { + self.fp_stack.pop1()?; + self.fp_stack.push(FloatValue::cncl_f32(self.value_stack.len() - 1)); + self.emit_fp_unop_avx(Assembler::emit_vroundss_ceil)? + } + Operator::F32Trunc => { + self.fp_stack.pop1()?; + self.fp_stack.push(FloatValue::cncl_f32(self.value_stack.len() - 1)); + self.emit_fp_unop_avx(Assembler::emit_vroundss_trunc)? + } + Operator::F32Sqrt => { + self.fp_stack.pop1()?; + self.fp_stack.push(FloatValue::cncl_f32(self.value_stack.len() - 1)); + self.emit_fp_unop_avx(Assembler::emit_vsqrtss)? + } + + Operator::F32Copysign => { + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::F32); + + let (fp_src1, fp_src2) = self.fp_stack.pop2()?; + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); + + let tmp1 = self.machine.acquire_temp_gpr().unwrap(); + let tmp2 = self.machine.acquire_temp_gpr().unwrap(); + + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization + { + for (fp, loc, tmp) in [(fp_src1, loc_a, tmp1), (fp_src2, loc_b, tmp2)].iter() { + match fp.canonicalization { + Some(_) => { + self.canonicalize_nan(Size::S32, *loc, Location::GPR(*tmp)); + } + None => { + self.assembler.emit_mov(Size::S32, *loc, Location::GPR(*tmp)); + } + } + } + } else { + self.assembler.emit_mov(Size::S32, loc_a, Location::GPR(tmp1)); + self.assembler.emit_mov(Size::S32, loc_b, Location::GPR(tmp2)); + } + self.assembler.emit_and( + Size::S32, + Location::Imm32(0x7fffffffu32), + Location::GPR(tmp1), + ); + self.assembler.emit_and( + Size::S32, + Location::Imm32(0x80000000u32), + Location::GPR(tmp2), + ); + self.assembler.emit_or(Size::S32, Location::GPR(tmp2), Location::GPR(tmp1)); + self.assembler.emit_mov(Size::S32, Location::GPR(tmp1), ret); + self.machine.release_temp_gpr(tmp2); + self.machine.release_temp_gpr(tmp1); + } + + Operator::F32Abs => { + // Preserve canonicalization state. + + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::F32)], false)[0]; + self.value_stack.push(ret); + let tmp = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S32, loc, Location::GPR(tmp)); + self.assembler.emit_and( + Size::S32, + Location::Imm32(0x7fffffffu32), + Location::GPR(tmp), + ); + self.assembler.emit_mov(Size::S32, Location::GPR(tmp), ret); + self.machine.release_temp_gpr(tmp); + } + + Operator::F32Neg => { + // Preserve canonicalization state. + + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::F32)], false)[0]; + self.value_stack.push(ret); + + if self.assembler.arch_has_fneg() { + let tmp = self.machine.acquire_temp_xmm().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp), + ); + self.assembler.arch_emit_f32_neg(tmp, tmp); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + Location::XMM(tmp), + ret, + ); + self.machine.release_temp_xmm(tmp); + } else { + let tmp = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S32, loc, Location::GPR(tmp)); + self.assembler.emit_btc_gpr_imm8_32(31, tmp); + self.assembler.emit_mov(Size::S32, Location::GPR(tmp), ret); + self.machine.release_temp_gpr(tmp); + } + } + + Operator::F64Const { value } => { + self.value_stack.push(Location::Imm64(value.bits())); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); + } + Operator::F64Add => { + self.fp_stack.pop2()?; + self.fp_stack.push(FloatValue::cncl_f64(self.value_stack.len() - 2)); + self.emit_fp_binop_avx(Assembler::emit_vaddsd)? + } + Operator::F64Sub => { + self.fp_stack.pop2()?; + self.fp_stack.push(FloatValue::cncl_f64(self.value_stack.len() - 2)); + self.emit_fp_binop_avx(Assembler::emit_vsubsd)? + } + Operator::F64Mul => { + self.fp_stack.pop2()?; + self.fp_stack.push(FloatValue::cncl_f64(self.value_stack.len() - 2)); + self.emit_fp_binop_avx(Assembler::emit_vmulsd)? + } + Operator::F64Div => { + self.fp_stack.pop2()?; + self.fp_stack.push(FloatValue::cncl_f64(self.value_stack.len() - 2)); + self.emit_fp_binop_avx(Assembler::emit_vdivsd)? + } + Operator::F64Max => { + self.fp_stack.pop2()?; + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 2)); + + if !self.assembler.arch_supports_canonicalize_nan() { + self.emit_fp_binop_avx(Assembler::emit_vmaxsd)?; + } else { + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::F64); + + let tmp1 = self.machine.acquire_temp_xmm().unwrap(); + let tmp2 = self.machine.acquire_temp_xmm().unwrap(); + let tmpg1 = self.machine.acquire_temp_gpr().unwrap(); + let tmpg2 = self.machine.acquire_temp_gpr().unwrap(); + + let src1 = match loc_a { + Location::XMM(x) => x, + Location::GPR(_) | Location::Memory(_, _) => { + self.assembler.emit_mov(Size::S64, loc_a, Location::XMM(tmp1)); + tmp1 + } + Location::Imm32(_) => { + self.assembler.emit_mov(Size::S32, loc_a, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S32, + Location::GPR(tmpg1), + Location::XMM(tmp1), + ); + tmp1 + } + Location::Imm64(_) => { + self.assembler.emit_mov(Size::S64, loc_a, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S64, + Location::GPR(tmpg1), + Location::XMM(tmp1), + ); + tmp1 + } + _ => { + return Err(CodegenError { + message: "F64Max src1: unreachable code".to_string(), + }) + } + }; + let src2 = match loc_b { + Location::XMM(x) => x, + Location::GPR(_) | Location::Memory(_, _) => { + self.assembler.emit_mov(Size::S64, loc_b, Location::XMM(tmp2)); + tmp2 + } + Location::Imm32(_) => { + self.assembler.emit_mov(Size::S32, loc_b, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S32, + Location::GPR(tmpg1), + Location::XMM(tmp2), + ); + tmp2 + } + Location::Imm64(_) => { + self.assembler.emit_mov(Size::S64, loc_b, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S64, + Location::GPR(tmpg1), + Location::XMM(tmp2), + ); + tmp2 + } + _ => { + return Err(CodegenError { + message: "F64Max src2: unreachable code".to_string(), + }) + } + }; + + let tmp_xmm1 = XMM::XMM8; + let tmp_xmm2 = XMM::XMM9; + let tmp_xmm3 = XMM::XMM10; + + self.assembler.emit_mov(Size::S64, Location::XMM(src1), Location::GPR(tmpg1)); + self.assembler.emit_mov(Size::S64, Location::XMM(src2), Location::GPR(tmpg2)); + self.assembler.emit_cmp(Size::S64, Location::GPR(tmpg2), Location::GPR(tmpg1)); + self.assembler.emit_vmaxsd(src1, XMMOrMemory::XMM(src2), tmp_xmm1); + let label1 = self.assembler.get_label(); + let label2 = self.assembler.get_label(); + self.assembler.emit_jmp(Condition::NotEqual, label1); + self.assembler + .emit_vmovapd(XMMOrMemory::XMM(tmp_xmm1), XMMOrMemory::XMM(tmp_xmm2)); + self.assembler.emit_jmp(Condition::None, label2); + self.assembler.emit_label(label1); + self.assembler.emit_vxorpd(tmp_xmm2, XMMOrMemory::XMM(tmp_xmm2), tmp_xmm2); + self.assembler.emit_label(label2); + self.assembler.emit_vcmpeqsd(src1, XMMOrMemory::XMM(src2), tmp_xmm3); + self.assembler.emit_vblendvpd( + tmp_xmm3, + XMMOrMemory::XMM(tmp_xmm2), + tmp_xmm1, + tmp_xmm1, + ); + self.assembler.emit_vcmpunordsd(src1, XMMOrMemory::XMM(src2), src1); + // load float canonical nan + self.assembler.emit_mov( + Size::S64, + Location::Imm64(0x7FF8_0000_0000_0000), // Canonical NaN + Location::GPR(tmpg1), + ); + self.assembler.emit_mov(Size::S64, Location::GPR(tmpg1), Location::XMM(src2)); + self.assembler.emit_vblendvpd(src1, XMMOrMemory::XMM(src2), tmp_xmm1, src1); + match ret { + Location::XMM(x) => { + self.assembler + .emit_vmovapd(XMMOrMemory::XMM(src1), XMMOrMemory::XMM(x)); + } + Location::Memory(_, _) | Location::GPR(_) => { + self.assembler.emit_mov(Size::S64, Location::XMM(src1), ret); + } + _ => { + return Err(CodegenError { + message: "F64Max ret: unreachable code".to_string(), + }) + } + } + + self.machine.release_temp_gpr(tmpg2); + self.machine.release_temp_gpr(tmpg1); + self.machine.release_temp_xmm(tmp2); + self.machine.release_temp_xmm(tmp1); + } + } + Operator::F64Min => { + self.fp_stack.pop2()?; + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 2)); + + if !self.assembler.arch_supports_canonicalize_nan() { + self.emit_fp_binop_avx(Assembler::emit_vminsd)?; + } else { + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::F64); + + let tmp1 = self.machine.acquire_temp_xmm().unwrap(); + let tmp2 = self.machine.acquire_temp_xmm().unwrap(); + let tmpg1 = self.machine.acquire_temp_gpr().unwrap(); + let tmpg2 = self.machine.acquire_temp_gpr().unwrap(); + + let src1 = match loc_a { + Location::XMM(x) => x, + Location::GPR(_) | Location::Memory(_, _) => { + self.assembler.emit_mov(Size::S64, loc_a, Location::XMM(tmp1)); + tmp1 + } + Location::Imm32(_) => { + self.assembler.emit_mov(Size::S32, loc_a, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S32, + Location::GPR(tmpg1), + Location::XMM(tmp1), + ); + tmp1 + } + Location::Imm64(_) => { + self.assembler.emit_mov(Size::S64, loc_a, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S64, + Location::GPR(tmpg1), + Location::XMM(tmp1), + ); + tmp1 + } + _ => { + return Err(CodegenError { + message: "F64Min src1: unreachable code".to_string(), + }) + } + }; + let src2 = match loc_b { + Location::XMM(x) => x, + Location::GPR(_) | Location::Memory(_, _) => { + self.assembler.emit_mov(Size::S64, loc_b, Location::XMM(tmp2)); + tmp2 + } + Location::Imm32(_) => { + self.assembler.emit_mov(Size::S32, loc_b, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S32, + Location::GPR(tmpg1), + Location::XMM(tmp2), + ); + tmp2 + } + Location::Imm64(_) => { + self.assembler.emit_mov(Size::S64, loc_b, Location::GPR(tmpg1)); + self.assembler.emit_mov( + Size::S64, + Location::GPR(tmpg1), + Location::XMM(tmp2), + ); + tmp2 + } + _ => { + return Err(CodegenError { + message: "F64Min src2: unreachable code".to_string(), + }) + } + }; + + let tmp_xmm1 = XMM::XMM8; + let tmp_xmm2 = XMM::XMM9; + let tmp_xmm3 = XMM::XMM10; + + self.assembler.emit_mov(Size::S64, Location::XMM(src1), Location::GPR(tmpg1)); + self.assembler.emit_mov(Size::S64, Location::XMM(src2), Location::GPR(tmpg2)); + self.assembler.emit_cmp(Size::S64, Location::GPR(tmpg2), Location::GPR(tmpg1)); + self.assembler.emit_vminsd(src1, XMMOrMemory::XMM(src2), tmp_xmm1); + let label1 = self.assembler.get_label(); + let label2 = self.assembler.get_label(); + self.assembler.emit_jmp(Condition::NotEqual, label1); + self.assembler + .emit_vmovapd(XMMOrMemory::XMM(tmp_xmm1), XMMOrMemory::XMM(tmp_xmm2)); + self.assembler.emit_jmp(Condition::None, label2); + self.assembler.emit_label(label1); + // load float -0.0 + self.assembler.emit_mov( + Size::S64, + Location::Imm64(0x8000_0000_0000_0000), // Negative zero + Location::GPR(tmpg1), + ); + self.assembler.emit_mov( + Size::S64, + Location::GPR(tmpg1), + Location::XMM(tmp_xmm2), + ); + self.assembler.emit_label(label2); + self.assembler.emit_vcmpeqsd(src1, XMMOrMemory::XMM(src2), tmp_xmm3); + self.assembler.emit_vblendvpd( + tmp_xmm3, + XMMOrMemory::XMM(tmp_xmm2), + tmp_xmm1, + tmp_xmm1, + ); + self.assembler.emit_vcmpunordsd(src1, XMMOrMemory::XMM(src2), src1); + // load float canonical nan + self.assembler.emit_mov( + Size::S64, + Location::Imm64(0x7FF8_0000_0000_0000), // Canonical NaN + Location::GPR(tmpg1), + ); + self.assembler.emit_mov(Size::S64, Location::GPR(tmpg1), Location::XMM(src2)); + self.assembler.emit_vblendvpd(src1, XMMOrMemory::XMM(src2), tmp_xmm1, src1); + match ret { + Location::XMM(x) => { + self.assembler + .emit_vmovaps(XMMOrMemory::XMM(src1), XMMOrMemory::XMM(x)); + } + Location::Memory(_, _) | Location::GPR(_) => { + self.assembler.emit_mov(Size::S64, Location::XMM(src1), ret); + } + _ => { + return Err(CodegenError { + message: "F64Min ret: unreachable code".to_string(), + }) + } + } + + self.machine.release_temp_gpr(tmpg2); + self.machine.release_temp_gpr(tmpg1); + self.machine.release_temp_xmm(tmp2); + self.machine.release_temp_xmm(tmp1); + } + } + Operator::F64Eq => { + self.fp_stack.pop2()?; + self.emit_fp_cmpop_avx(Assembler::emit_vcmpeqsd)? + } + Operator::F64Ne => { + self.fp_stack.pop2()?; + self.emit_fp_cmpop_avx(Assembler::emit_vcmpneqsd)? + } + Operator::F64Lt => { + self.fp_stack.pop2()?; + self.emit_fp_cmpop_avx(Assembler::emit_vcmpltsd)? + } + Operator::F64Le => { + self.fp_stack.pop2()?; + self.emit_fp_cmpop_avx(Assembler::emit_vcmplesd)? + } + Operator::F64Gt => { + self.fp_stack.pop2()?; + self.emit_fp_cmpop_avx(Assembler::emit_vcmpgtsd)? + } + Operator::F64Ge => { + self.fp_stack.pop2()?; + self.emit_fp_cmpop_avx(Assembler::emit_vcmpgesd)? + } + Operator::F64Nearest => { + self.fp_stack.pop1()?; + self.fp_stack.push(FloatValue::cncl_f64(self.value_stack.len() - 1)); + self.emit_fp_unop_avx(Assembler::emit_vroundsd_nearest)? + } + Operator::F64Floor => { + self.fp_stack.pop1()?; + self.fp_stack.push(FloatValue::cncl_f64(self.value_stack.len() - 1)); + self.emit_fp_unop_avx(Assembler::emit_vroundsd_floor)? + } + Operator::F64Ceil => { + self.fp_stack.pop1()?; + self.fp_stack.push(FloatValue::cncl_f64(self.value_stack.len() - 1)); + self.emit_fp_unop_avx(Assembler::emit_vroundsd_ceil)? + } + Operator::F64Trunc => { + self.fp_stack.pop1()?; + self.fp_stack.push(FloatValue::cncl_f64(self.value_stack.len() - 1)); + self.emit_fp_unop_avx(Assembler::emit_vroundsd_trunc)? + } + Operator::F64Sqrt => { + self.fp_stack.pop1()?; + self.fp_stack.push(FloatValue::cncl_f64(self.value_stack.len() - 1)); + self.emit_fp_unop_avx(Assembler::emit_vsqrtsd)? + } + + Operator::F64Copysign => { + let I2O1 { loc_a, loc_b, ret } = self.i2o1_prepare(WpType::F64); + + let (fp_src1, fp_src2) = self.fp_stack.pop2()?; + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); + + let tmp1 = self.machine.acquire_temp_gpr().unwrap(); + let tmp2 = self.machine.acquire_temp_gpr().unwrap(); + + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization + { + for (fp, loc, tmp) in [(fp_src1, loc_a, tmp1), (fp_src2, loc_b, tmp2)].iter() { + match fp.canonicalization { + Some(_) => { + self.canonicalize_nan(Size::S64, *loc, Location::GPR(*tmp)); + } + None => { + self.assembler.emit_mov(Size::S64, *loc, Location::GPR(*tmp)); + } + } + } + } else { + self.assembler.emit_mov(Size::S64, loc_a, Location::GPR(tmp1)); + self.assembler.emit_mov(Size::S64, loc_b, Location::GPR(tmp2)); + } + + let c = self.machine.acquire_temp_gpr().unwrap(); + + self.assembler.emit_mov( + Size::S64, + Location::Imm64(0x7fffffffffffffffu64), + Location::GPR(c), + ); + self.assembler.emit_and(Size::S64, Location::GPR(c), Location::GPR(tmp1)); + + self.assembler.emit_mov( + Size::S64, + Location::Imm64(0x8000000000000000u64), + Location::GPR(c), + ); + self.assembler.emit_and(Size::S64, Location::GPR(c), Location::GPR(tmp2)); + + self.assembler.emit_or(Size::S64, Location::GPR(tmp2), Location::GPR(tmp1)); + self.assembler.emit_mov(Size::S64, Location::GPR(tmp1), ret); + + self.machine.release_temp_gpr(c); + self.machine.release_temp_gpr(tmp2); + self.machine.release_temp_gpr(tmp1); + } + + Operator::F64Abs => { + // Preserve canonicalization state. + + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::F64)], false)[0]; + self.value_stack.push(ret); + + let tmp = self.machine.acquire_temp_gpr().unwrap(); + let c = self.machine.acquire_temp_gpr().unwrap(); + + self.assembler.emit_mov(Size::S64, loc, Location::GPR(tmp)); + self.assembler.emit_mov( + Size::S64, + Location::Imm64(0x7fffffffffffffffu64), + Location::GPR(c), + ); + self.assembler.emit_and(Size::S64, Location::GPR(c), Location::GPR(tmp)); + self.assembler.emit_mov(Size::S64, Location::GPR(tmp), ret); + + self.machine.release_temp_gpr(c); + self.machine.release_temp_gpr(tmp); + } + + Operator::F64Neg => { + // Preserve canonicalization state. + + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::F64)], false)[0]; + self.value_stack.push(ret); + if self.assembler.arch_has_fneg() { + let tmp = self.machine.acquire_temp_xmm().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp), + ); + self.assembler.arch_emit_f64_neg(tmp, tmp); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::XMM(tmp), + ret, + ); + self.machine.release_temp_xmm(tmp); + } else { + let tmp = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S64, loc, Location::GPR(tmp)); + self.assembler.emit_btc_gpr_imm8_64(63, tmp); + self.assembler.emit_mov(Size::S64, Location::GPR(tmp), ret); + self.machine.release_temp_gpr(tmp); + } + } + + Operator::F64PromoteF32 => { + let fp = self.fp_stack.pop1()?; + self.fp_stack.push(fp.promote(self.value_stack.len() - 1)); + self.emit_fp_unop_avx(Assembler::emit_vcvtss2sd)? + } + Operator::F32DemoteF64 => { + let fp = self.fp_stack.pop1()?; + self.fp_stack.push(fp.demote(self.value_stack.len() - 1)); + self.emit_fp_unop_avx(Assembler::emit_vcvtsd2ss)? + } + + Operator::I32ReinterpretF32 => { + let loc = self.pop_value_released(); + let ret = self.machine.acquire_locations(self.assembler, &[WpType::I32], false)[0]; + self.value_stack.push(ret); + let fp = self.fp_stack.pop1()?; + + if !self.assembler.arch_supports_canonicalize_nan() + || !self.config.enable_nan_canonicalization + || fp.canonicalization.is_none() + { + if loc != ret { + self.emit_relaxed_binop(Assembler::emit_mov, Size::S32, loc, ret); + } + } else { + self.canonicalize_nan(Size::S32, loc, ret); + } + } + Operator::F32ReinterpretI32 => { + let loc = self.pop_value_released(); + let ret = self.machine.acquire_locations(self.assembler, &[WpType::F32], false)[0]; + self.value_stack.push(ret); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); + + if loc != ret { + self.emit_relaxed_binop(Assembler::emit_mov, Size::S32, loc, ret); + } + } + + Operator::I64ReinterpretF64 => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + let fp = self.fp_stack.pop1()?; + + if !self.assembler.arch_supports_canonicalize_nan() + || !self.config.enable_nan_canonicalization + || fp.canonicalization.is_none() + { + if loc != ret { + self.emit_relaxed_binop(Assembler::emit_mov, Size::S64, loc, ret); + } + } else { + self.canonicalize_nan(Size::S64, loc, ret); + } + } + Operator::F64ReinterpretI64 => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::F64)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); + + if loc != ret { + self.emit_relaxed_binop(Assembler::emit_mov, Size::S64, loc, ret); + } + } + + Operator::I32TruncF32U => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + if self.assembler.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + self.assembler.arch_emit_i32_trunc_uf32(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + self.emit_f32_int_conv_check_trap(tmp_in, GEF32_LT_U32_MIN, LEF32_GT_U32_MAX); + + self.assembler.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + self.assembler.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + } + + Operator::I32TruncSatF32U => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + self.emit_relaxed_binop(Assembler::emit_mov, Size::S32, loc, Location::XMM(tmp_in)); + self.emit_f32_int_conv_check_sat( + tmp_in, + GEF32_LT_U32_MIN, + LEF32_GT_U32_MAX, + |this| { + this.assembler.emit_mov( + Size::S32, + Location::Imm32(0), + Location::GPR(tmp_out), + ); + }, + |this| { + this.assembler.emit_mov( + Size::S32, + Location::Imm32(std::u32::MAX), + Location::GPR(tmp_out), + ); + }, + None::, + |this| { + if this.assembler.arch_has_itruncf() { + this.assembler.arch_emit_i32_trunc_uf32(tmp_in, tmp_out); + } else { + this.assembler.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + } + }, + ); + + self.assembler.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I32TruncF32S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + if self.assembler.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + self.assembler.arch_emit_i32_trunc_sf32(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + self.emit_f32_int_conv_check_trap(tmp_in, GEF32_LT_I32_MIN, LEF32_GT_I32_MAX); + + self.assembler.emit_cvttss2si_32(XMMOrMemory::XMM(tmp_in), tmp_out); + self.assembler.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + } + Operator::I32TruncSatF32S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + self.emit_relaxed_binop(Assembler::emit_mov, Size::S32, loc, Location::XMM(tmp_in)); + self.emit_f32_int_conv_check_sat( + tmp_in, + GEF32_LT_I32_MIN, + LEF32_GT_I32_MAX, + |this| { + this.assembler.emit_mov( + Size::S32, + Location::Imm32(std::i32::MIN as u32), + Location::GPR(tmp_out), + ); + }, + |this| { + this.assembler.emit_mov( + Size::S32, + Location::Imm32(std::i32::MAX as u32), + Location::GPR(tmp_out), + ); + }, + Some(|this: &mut Self| { + this.assembler.emit_mov( + Size::S32, + Location::Imm32(0), + Location::GPR(tmp_out), + ); + }), + |this| { + if this.assembler.arch_has_itruncf() { + this.assembler.arch_emit_i32_trunc_sf32(tmp_in, tmp_out); + } else { + this.assembler.emit_cvttss2si_32(XMMOrMemory::XMM(tmp_in), tmp_out); + } + }, + ); + + self.assembler.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I64TruncF32S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + if self.assembler.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + self.assembler.arch_emit_i64_trunc_sf32(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + self.emit_f32_int_conv_check_trap(tmp_in, GEF32_LT_I64_MIN, LEF32_GT_I64_MAX); + self.assembler.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + self.assembler.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + } + + Operator::I64TruncSatF32S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + self.emit_relaxed_binop(Assembler::emit_mov, Size::S32, loc, Location::XMM(tmp_in)); + self.emit_f32_int_conv_check_sat( + tmp_in, + GEF32_LT_I64_MIN, + LEF32_GT_I64_MAX, + |this| { + this.assembler.emit_mov( + Size::S64, + Location::Imm64(std::i64::MIN as u64), + Location::GPR(tmp_out), + ); + }, + |this| { + this.assembler.emit_mov( + Size::S64, + Location::Imm64(std::i64::MAX as u64), + Location::GPR(tmp_out), + ); + }, + Some(|this: &mut Self| { + this.assembler.emit_mov( + Size::S64, + Location::Imm64(0), + Location::GPR(tmp_out), + ); + }), + |this| { + if this.assembler.arch_has_itruncf() { + this.assembler.arch_emit_i64_trunc_sf32(tmp_in, tmp_out); + } else { + this.assembler.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + } + }, + ); + + self.assembler.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I64TruncF32U => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + if self.assembler.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + self.assembler.arch_emit_i64_trunc_uf32(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2 + + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + loc, + Location::XMM(tmp_in), + ); + self.emit_f32_int_conv_check_trap(tmp_in, GEF32_LT_U64_MIN, LEF32_GT_U64_MAX); + + let tmp = self.machine.acquire_temp_gpr().unwrap(); // r15 + let tmp_x1 = self.machine.acquire_temp_xmm().unwrap(); // xmm1 + let tmp_x2 = self.machine.acquire_temp_xmm().unwrap(); // xmm3 + + self.assembler.emit_mov( + Size::S32, + Location::Imm32(1593835520u32), + Location::GPR(tmp), + ); //float 9.22337203E+18 + self.assembler.emit_mov(Size::S32, Location::GPR(tmp), Location::XMM(tmp_x1)); + self.assembler.emit_mov( + Size::S32, + Location::XMM(tmp_in), + Location::XMM(tmp_x2), + ); + self.assembler.emit_vsubss(tmp_in, XMMOrMemory::XMM(tmp_x1), tmp_in); + self.assembler.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + self.assembler.emit_mov( + Size::S64, + Location::Imm64(0x8000000000000000u64), + Location::GPR(tmp), + ); + self.assembler.emit_xor(Size::S64, Location::GPR(tmp_out), Location::GPR(tmp)); + self.assembler.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_x2), tmp_out); + self.assembler.emit_ucomiss(XMMOrMemory::XMM(tmp_x1), tmp_x2); + self.assembler.emit_cmovae_gpr_64(tmp, tmp_out); + self.assembler.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_x2); + self.machine.release_temp_xmm(tmp_x1); + self.machine.release_temp_gpr(tmp); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + } + Operator::I64TruncSatF32U => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + self.emit_relaxed_binop(Assembler::emit_mov, Size::S32, loc, Location::XMM(tmp_in)); + self.emit_f32_int_conv_check_sat( + tmp_in, + GEF32_LT_U64_MIN, + LEF32_GT_U64_MAX, + |this| { + this.assembler.emit_mov( + Size::S64, + Location::Imm64(0), + Location::GPR(tmp_out), + ); + }, + |this| { + this.assembler.emit_mov( + Size::S64, + Location::Imm64(std::u64::MAX), + Location::GPR(tmp_out), + ); + }, + None::, + |this| { + if this.assembler.arch_has_itruncf() { + this.assembler.arch_emit_i64_trunc_uf32(tmp_in, tmp_out); + } else { + let tmp = this.machine.acquire_temp_gpr().unwrap(); + let tmp_x1 = this.machine.acquire_temp_xmm().unwrap(); + let tmp_x2 = this.machine.acquire_temp_xmm().unwrap(); + + this.assembler.emit_mov( + Size::S32, + Location::Imm32(1593835520u32), + Location::GPR(tmp), + ); //float 9.22337203E+18 + this.assembler.emit_mov( + Size::S32, + Location::GPR(tmp), + Location::XMM(tmp_x1), + ); + this.assembler.emit_mov( + Size::S32, + Location::XMM(tmp_in), + Location::XMM(tmp_x2), + ); + this.assembler.emit_vsubss(tmp_in, XMMOrMemory::XMM(tmp_x1), tmp_in); + this.assembler.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + this.assembler.emit_mov( + Size::S64, + Location::Imm64(0x8000000000000000u64), + Location::GPR(tmp), + ); + this.assembler.emit_xor( + Size::S64, + Location::GPR(tmp_out), + Location::GPR(tmp), + ); + this.assembler.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_x2), tmp_out); + this.assembler.emit_ucomiss(XMMOrMemory::XMM(tmp_x1), tmp_x2); + this.assembler.emit_cmovae_gpr_64(tmp, tmp_out); + + this.machine.release_temp_xmm(tmp_x2); + this.machine.release_temp_xmm(tmp_x1); + this.machine.release_temp_gpr(tmp); + } + }, + ); + + self.assembler.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I32TruncF64U => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + if self.assembler.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp_in), + ); + self.assembler.arch_emit_i32_trunc_uf64(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp_in), + ); + self.emit_f64_int_conv_check_trap(tmp_in, GEF64_LT_U32_MIN, LEF64_GT_U32_MAX); + + self.assembler.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + self.assembler.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + } + + Operator::I32TruncSatF64U => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + self.emit_relaxed_binop(Assembler::emit_mov, Size::S64, loc, Location::XMM(tmp_in)); + self.emit_f64_int_conv_check_sat( + tmp_in, + GEF64_LT_U32_MIN, + LEF64_GT_U32_MAX, + |this| { + this.assembler.emit_mov( + Size::S32, + Location::Imm32(0), + Location::GPR(tmp_out), + ); + }, + |this| { + this.assembler.emit_mov( + Size::S32, + Location::Imm32(std::u32::MAX), + Location::GPR(tmp_out), + ); + }, + None::, + |this| { + if this.assembler.arch_has_itruncf() { + this.assembler.arch_emit_i32_trunc_uf64(tmp_in, tmp_out); + } else { + this.assembler.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + } + }, + ); + + self.assembler.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I32TruncF64S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + if self.assembler.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp_in), + ); + self.assembler.arch_emit_i32_trunc_sf64(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + let real_in = match loc { + Location::Imm32(_) | Location::Imm64(_) => { + self.assembler.emit_mov(Size::S64, loc, Location::GPR(tmp_out)); + self.assembler.emit_mov( + Size::S64, + Location::GPR(tmp_out), + Location::XMM(tmp_in), + ); + tmp_in + } + Location::XMM(x) => x, + _ => { + self.assembler.emit_mov(Size::S64, loc, Location::XMM(tmp_in)); + tmp_in + } + }; + + self.emit_f64_int_conv_check_trap(real_in, GEF64_LT_I32_MIN, LEF64_GT_I32_MAX); + + self.assembler.emit_cvttsd2si_32(XMMOrMemory::XMM(real_in), tmp_out); + self.assembler.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + } + + Operator::I32TruncSatF64S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + let real_in = match loc { + Location::Imm32(_) | Location::Imm64(_) => { + self.assembler.emit_mov(Size::S64, loc, Location::GPR(tmp_out)); + self.assembler.emit_mov( + Size::S64, + Location::GPR(tmp_out), + Location::XMM(tmp_in), + ); + tmp_in + } + Location::XMM(x) => x, + _ => { + self.assembler.emit_mov(Size::S64, loc, Location::XMM(tmp_in)); + tmp_in + } + }; + + self.emit_f64_int_conv_check_sat( + real_in, + GEF64_LT_I32_MIN, + LEF64_GT_I32_MAX, + |this| { + this.assembler.emit_mov( + Size::S32, + Location::Imm32(std::i32::MIN as u32), + Location::GPR(tmp_out), + ); + }, + |this| { + this.assembler.emit_mov( + Size::S32, + Location::Imm32(std::i32::MAX as u32), + Location::GPR(tmp_out), + ); + }, + Some(|this: &mut Self| { + this.assembler.emit_mov( + Size::S32, + Location::Imm32(0), + Location::GPR(tmp_out), + ); + }), + |this| { + if this.assembler.arch_has_itruncf() { + this.assembler.arch_emit_i32_trunc_sf64(tmp_in, tmp_out); + } else { + this.assembler.emit_cvttsd2si_32(XMMOrMemory::XMM(real_in), tmp_out); + } + }, + ); + + self.assembler.emit_mov(Size::S32, Location::GPR(tmp_out), ret); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I64TruncF64S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + if self.assembler.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp_in), + ); + self.assembler.arch_emit_i64_trunc_sf64(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp_in), + ); + self.emit_f64_int_conv_check_trap(tmp_in, GEF64_LT_I64_MIN, LEF64_GT_I64_MAX); + + self.assembler.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + self.assembler.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + } + + Operator::I64TruncSatF64S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + self.emit_relaxed_binop(Assembler::emit_mov, Size::S64, loc, Location::XMM(tmp_in)); + self.emit_f64_int_conv_check_sat( + tmp_in, + GEF64_LT_I64_MIN, + LEF64_GT_I64_MAX, + |this| { + this.assembler.emit_mov( + Size::S64, + Location::Imm64(std::i64::MIN as u64), + Location::GPR(tmp_out), + ); + }, + |this| { + this.assembler.emit_mov( + Size::S64, + Location::Imm64(std::i64::MAX as u64), + Location::GPR(tmp_out), + ); + }, + Some(|this: &mut Self| { + this.assembler.emit_mov( + Size::S64, + Location::Imm64(0), + Location::GPR(tmp_out), + ); + }), + |this| { + if this.assembler.arch_has_itruncf() { + this.assembler.arch_emit_i64_trunc_sf64(tmp_in, tmp_out); + } else { + this.assembler.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + } + }, + ); + + self.assembler.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::I64TruncF64U => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + if self.assembler.arch_has_itruncf() { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp_in), + ); + self.assembler.arch_emit_i64_trunc_uf64(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::GPR(tmp_out), + ret, + ); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2 + + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::XMM(tmp_in), + ); + self.emit_f64_int_conv_check_trap(tmp_in, GEF64_LT_U64_MIN, LEF64_GT_U64_MAX); + + let tmp = self.machine.acquire_temp_gpr().unwrap(); // r15 + let tmp_x1 = self.machine.acquire_temp_xmm().unwrap(); // xmm1 + let tmp_x2 = self.machine.acquire_temp_xmm().unwrap(); // xmm3 + + self.assembler.emit_mov( + Size::S64, + Location::Imm64(4890909195324358656u64), + Location::GPR(tmp), + ); //double 9.2233720368547758E+18 + self.assembler.emit_mov(Size::S64, Location::GPR(tmp), Location::XMM(tmp_x1)); + self.assembler.emit_mov( + Size::S64, + Location::XMM(tmp_in), + Location::XMM(tmp_x2), + ); + self.assembler.emit_vsubsd(tmp_in, XMMOrMemory::XMM(tmp_x1), tmp_in); + self.assembler.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + self.assembler.emit_mov( + Size::S64, + Location::Imm64(0x8000000000000000u64), + Location::GPR(tmp), + ); + self.assembler.emit_xor(Size::S64, Location::GPR(tmp_out), Location::GPR(tmp)); + self.assembler.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_x2), tmp_out); + self.assembler.emit_ucomisd(XMMOrMemory::XMM(tmp_x1), tmp_x2); + self.assembler.emit_cmovae_gpr_64(tmp, tmp_out); + self.assembler.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + + self.machine.release_temp_xmm(tmp_x2); + self.machine.release_temp_xmm(tmp_x1); + self.machine.release_temp_gpr(tmp); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + } + + Operator::I64TruncSatF64U => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.pop1()?; + + let tmp_out = self.machine.acquire_temp_gpr().unwrap(); + let tmp_in = self.machine.acquire_temp_xmm().unwrap(); + + self.emit_relaxed_binop(Assembler::emit_mov, Size::S64, loc, Location::XMM(tmp_in)); + self.emit_f64_int_conv_check_sat( + tmp_in, + GEF64_LT_U64_MIN, + LEF64_GT_U64_MAX, + |this| { + this.assembler.emit_mov( + Size::S64, + Location::Imm64(0), + Location::GPR(tmp_out), + ); + }, + |this| { + this.assembler.emit_mov( + Size::S64, + Location::Imm64(std::u64::MAX), + Location::GPR(tmp_out), + ); + }, + None::, + |this| { + if this.assembler.arch_has_itruncf() { + this.assembler.arch_emit_i64_trunc_uf64(tmp_in, tmp_out); + } else { + let tmp = this.machine.acquire_temp_gpr().unwrap(); + let tmp_x1 = this.machine.acquire_temp_xmm().unwrap(); + let tmp_x2 = this.machine.acquire_temp_xmm().unwrap(); + + this.assembler.emit_mov( + Size::S64, + Location::Imm64(4890909195324358656u64), + Location::GPR(tmp), + ); //double 9.2233720368547758E+18 + this.assembler.emit_mov( + Size::S64, + Location::GPR(tmp), + Location::XMM(tmp_x1), + ); + this.assembler.emit_mov( + Size::S64, + Location::XMM(tmp_in), + Location::XMM(tmp_x2), + ); + this.assembler.emit_vsubsd(tmp_in, XMMOrMemory::XMM(tmp_x1), tmp_in); + this.assembler.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); + this.assembler.emit_mov( + Size::S64, + Location::Imm64(0x8000000000000000u64), + Location::GPR(tmp), + ); + this.assembler.emit_xor( + Size::S64, + Location::GPR(tmp_out), + Location::GPR(tmp), + ); + this.assembler.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_x2), tmp_out); + this.assembler.emit_ucomisd(XMMOrMemory::XMM(tmp_x1), tmp_x2); + this.assembler.emit_cmovae_gpr_64(tmp, tmp_out); + + this.machine.release_temp_xmm(tmp_x2); + this.machine.release_temp_xmm(tmp_x1); + this.machine.release_temp_gpr(tmp); + } + }, + ); + + self.assembler.emit_mov(Size::S64, Location::GPR(tmp_out), ret); + self.machine.release_temp_xmm(tmp_in); + self.machine.release_temp_gpr(tmp_out); + } + + Operator::F32ConvertI32S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::F32)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); // Converting i32 to f32 never results in NaN. + + if self.assembler.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + loc, + Location::GPR(tmp_in), + ); + self.assembler.arch_emit_f32_convert_si32(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + + self.assembler.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); + self.assembler.emit_vcvtsi2ss_32(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + self.assembler.emit_mov(Size::S32, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + } + Operator::F32ConvertI32U => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::F32)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); // Converting i32 to f32 never results in NaN. + + if self.assembler.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + loc, + Location::GPR(tmp_in), + ); + self.assembler.arch_emit_f32_convert_ui32(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + + self.assembler.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); + self.assembler.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + self.assembler.emit_mov(Size::S32, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + } + Operator::F32ConvertI64S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::F32)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); // Converting i64 to f32 never results in NaN. + + if self.assembler.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(tmp_in), + ); + self.assembler.arch_emit_f32_convert_si64(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + + self.assembler.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); + self.assembler.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + self.assembler.emit_mov(Size::S32, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + } + Operator::F32ConvertI64U => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::F32)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); // Converting i64 to f32 never results in NaN. + + if self.assembler.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(tmp_in), + ); + self.assembler.arch_emit_f32_convert_ui64(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + let tmp = self.machine.acquire_temp_gpr().unwrap(); + + let do_convert = self.assembler.get_label(); + let end_convert = self.assembler.get_label(); + + self.assembler.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); + self.assembler.emit_test_gpr_64(tmp_in); + self.assembler.emit_jmp(Condition::Signed, do_convert); + self.assembler.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + self.assembler.emit_jmp(Condition::None, end_convert); + self.assembler.emit_label(do_convert); + self.assembler.emit_mov(Size::S64, Location::GPR(tmp_in), Location::GPR(tmp)); + self.assembler.emit_and(Size::S64, Location::Imm32(1), Location::GPR(tmp)); + self.assembler.emit_shr(Size::S64, Location::Imm8(1), Location::GPR(tmp_in)); + self.assembler.emit_or(Size::S64, Location::GPR(tmp), Location::GPR(tmp_in)); + self.assembler.emit_vcvtsi2ss_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + self.assembler.emit_vaddss(tmp_out, XMMOrMemory::XMM(tmp_out), tmp_out); + self.assembler.emit_label(end_convert); + self.assembler.emit_mov(Size::S32, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + } + + Operator::F64ConvertI32S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::F64)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); // Converting i32 to f64 never results in NaN. + + if self.assembler.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + loc, + Location::GPR(tmp_in), + ); + self.assembler.arch_emit_f64_convert_si32(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + + self.assembler.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); + self.assembler.emit_vcvtsi2sd_32(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + self.assembler.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + } + Operator::F64ConvertI32U => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::F64)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); // Converting i32 to f64 never results in NaN. + + if self.assembler.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + loc, + Location::GPR(tmp_in), + ); + self.assembler.arch_emit_f64_convert_ui32(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + + self.assembler.emit_mov(Size::S32, loc, Location::GPR(tmp_in)); + self.assembler.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + self.assembler.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + } + Operator::F64ConvertI64S => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::F64)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); // Converting i64 to f64 never results in NaN. + + if self.assembler.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(tmp_in), + ); + self.assembler.arch_emit_f64_convert_si64(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + + self.assembler.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); + self.assembler.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + self.assembler.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + } + Operator::F64ConvertI64U => { + let loc = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::F64)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); // Converting i64 to f64 never results in NaN. + + if self.assembler.arch_has_fconverti() { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(tmp_in), + ); + self.assembler.arch_emit_f64_convert_ui64(tmp_in, tmp_out); + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::XMM(tmp_out), + ret, + ); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } else { + let tmp_out = self.machine.acquire_temp_xmm().unwrap(); + let tmp_in = self.machine.acquire_temp_gpr().unwrap(); + let tmp = self.machine.acquire_temp_gpr().unwrap(); + + let do_convert = self.assembler.get_label(); + let end_convert = self.assembler.get_label(); + + self.assembler.emit_mov(Size::S64, loc, Location::GPR(tmp_in)); + self.assembler.emit_test_gpr_64(tmp_in); + self.assembler.emit_jmp(Condition::Signed, do_convert); + self.assembler.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + self.assembler.emit_jmp(Condition::None, end_convert); + self.assembler.emit_label(do_convert); + self.assembler.emit_mov(Size::S64, Location::GPR(tmp_in), Location::GPR(tmp)); + self.assembler.emit_and(Size::S64, Location::Imm32(1), Location::GPR(tmp)); + self.assembler.emit_shr(Size::S64, Location::Imm8(1), Location::GPR(tmp_in)); + self.assembler.emit_or(Size::S64, Location::GPR(tmp), Location::GPR(tmp_in)); + self.assembler.emit_vcvtsi2sd_64(tmp_out, GPROrMemory::GPR(tmp_in), tmp_out); + self.assembler.emit_vaddsd(tmp_out, XMMOrMemory::XMM(tmp_out), tmp_out); + self.assembler.emit_label(end_convert); + self.assembler.emit_mov(Size::S64, Location::XMM(tmp_out), ret); + + self.machine.release_temp_gpr(tmp); + self.machine.release_temp_gpr(tmp_in); + self.machine.release_temp_xmm(tmp_out); + } + } + + Operator::Call { function_index } => { + self.emit_call(FunctionIndex::from_u32(function_index))? + } + Operator::CallIndirect { type_index, table_index, table_byte: _ } => { + // TODO: removed restriction on always being table idx 0; + // does any code depend on this? + let table_index = TableIndex::new(table_index as _); + let index = SignatureIndex::new(type_index as usize); + let sig = self.module.signatures.get(index).unwrap(); + let param_types: SmallVec<[WpType; 8]> = + sig.params().iter().cloned().map(type_to_wp_type).collect(); + let return_types: SmallVec<[WpType; 1]> = + sig.results().iter().cloned().map(type_to_wp_type).collect(); + + let func_index = self.pop_value_released(); + + let params: SmallVec<[_; 8]> = + self.value_stack.drain(self.value_stack.len() - param_types.len()..).collect(); + self.machine.release_locations_only_regs(¶ms); + + // Pop arguments off the FP stack and canonicalize them if needed. + // + // Canonicalization state will be lost across function calls, so early canonicalization + // is necessary here. + while let Some(fp) = self.fp_stack.last() { + if fp.depth >= self.value_stack.len() { + let index = fp.depth - self.value_stack.len(); + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization + && fp.canonicalization.is_some() + { + let size = fp.canonicalization.unwrap().to_size(); + self.canonicalize_nan(size, params[index], params[index]); + } + self.fp_stack.pop().unwrap(); + } else { + break; + } + } + + let table_base = self.machine.acquire_temp_gpr().unwrap(); + let table_count = self.machine.acquire_temp_gpr().unwrap(); + let sigidx = self.machine.acquire_temp_gpr().unwrap(); + + if let Some(local_table_index) = self.module.local_table_index(table_index) { + let (vmctx_offset_base, vmctx_offset_len) = ( + self.vmoffsets.vmctx_vmtable_definition(local_table_index), + self.vmoffsets.vmctx_vmtable_definition_current_elements(local_table_index), + ); + self.assembler.emit_mov( + Size::S64, + Location::Memory(Machine::get_vmctx_reg(), vmctx_offset_base as i32), + Location::GPR(table_base), + ); + self.assembler.emit_mov( + Size::S32, + Location::Memory(Machine::get_vmctx_reg(), vmctx_offset_len as i32), + Location::GPR(table_count), + ); + } else { + // Do an indirection. + let import_offset = self.vmoffsets.vmctx_vmtable_import(table_index); + self.assembler.emit_mov( + Size::S64, + Location::Memory(Machine::get_vmctx_reg(), import_offset as i32), + Location::GPR(table_base), + ); + + // Load len. + self.assembler.emit_mov( + Size::S32, + Location::Memory( + table_base, + self.vmoffsets.vmtable_definition_current_elements() as _, + ), + Location::GPR(table_count), + ); + + // Load base. + self.assembler.emit_mov( + Size::S64, + Location::Memory(table_base, self.vmoffsets.vmtable_definition_base() as _), + Location::GPR(table_base), + ); + } + + self.assembler.emit_cmp(Size::S32, func_index, Location::GPR(table_count)); + self.assembler + .emit_jmp(Condition::BelowEqual, self.special_labels.table_access_oob); + self.assembler.emit_mov(Size::S32, func_index, Location::GPR(table_count)); + self.assembler + .emit_imul_imm32_gpr64(self.vmoffsets.size_of_vm_funcref() as u32, table_count); + self.assembler.emit_add( + Size::S64, + Location::GPR(table_base), + Location::GPR(table_count), + ); + + // deref the table to get a VMFuncRef + self.assembler.emit_mov( + Size::S64, + Location::Memory(table_count, self.vmoffsets.vm_funcref_anyfunc_ptr() as i32), + Location::GPR(table_count), + ); + // Trap if the FuncRef is null + self.assembler.emit_cmp(Size::S64, Location::Imm32(0), Location::GPR(table_count)); + self.assembler.emit_jmp(Condition::Equal, self.special_labels.indirect_call_null); + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_vmshared_signature_id(index) as i32, + ), + Location::GPR(sigidx), + ); + + // Trap if signature mismatches. + self.assembler.emit_cmp( + Size::S32, + Location::GPR(sigidx), + Location::Memory( + table_count, + (self.vmoffsets.vmcaller_checked_anyfunc_type_index() as usize) as i32, + ), + ); + self.assembler.emit_jmp(Condition::NotEqual, self.special_labels.bad_signature); + + self.machine.release_temp_gpr(sigidx); + self.machine.release_temp_gpr(table_count); + self.machine.release_temp_gpr(table_base); + + if table_count != GPR::RAX { + self.assembler.emit_mov( + Size::S64, + Location::GPR(table_count), + Location::GPR(GPR::RAX), + ); + } + + let vmcaller_checked_anyfunc_func_ptr = + self.vmoffsets.vmcaller_checked_anyfunc_func_ptr() as usize; + let vmcaller_checked_anyfunc_vmctx = + self.vmoffsets.vmcaller_checked_anyfunc_vmctx() as usize; + let calling_convention = self.calling_convention; + + self.emit_call_native( + |this| { + if this.assembler.arch_requires_indirect_call_trampoline() { + this.assembler.arch_emit_indirect_call_with_trampoline( + Location::Memory( + GPR::RAX, + vmcaller_checked_anyfunc_func_ptr as i32, + ), + ); + } else { + // We set the context pointer + this.assembler.emit_mov( + Size::S64, + Location::Memory(GPR::RAX, vmcaller_checked_anyfunc_vmctx as i32), + Machine::get_param_location(0, calling_convention), + ); + + this.assembler.emit_call_location(Location::Memory( + GPR::RAX, + vmcaller_checked_anyfunc_func_ptr as i32, + )); + } + }, + params.iter().copied(), + )?; + + self.machine.release_locations_only_stack(self.assembler, ¶ms); + + if !return_types.is_empty() { + let ret = + self.machine.acquire_locations(self.assembler, &[return_types[0]], false) + [0]; + self.value_stack.push(ret); + if return_types[0].is_float() { + self.assembler.emit_mov(Size::S64, Location::XMM(XMM::XMM0), ret); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); + } else { + self.assembler.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + } + } + Operator::If { blockty } => { + let label_end = self.assembler.get_label(); + let label_else = self.assembler.get_label(); + + let cond = self.pop_value_released(); + + let frame = ControlFrame { + br_label: label_end, + loop_like: false, + if_else: IfElseState::If(label_else), + returns: match blockty { + WpBlockType::Empty => smallvec![], + WpBlockType::Type(inner_ty) => smallvec![inner_ty], + WpBlockType::FuncType(_) => { + return Err(CodegenError { + message: "If: multi-value returns not yet implemented".to_string(), + }) + } + }, + value_stack_depth: self.value_stack.len(), + fp_stack_depth: self.fp_stack.len(), + }; + self.control_stack.push(frame); + self.emit_relaxed_binop(Assembler::emit_cmp, Size::S32, Location::Imm32(0), cond); + self.assembler.emit_jmp(Condition::Equal, label_else); + } + Operator::Else => { + let frame = self.control_stack.last_mut().unwrap(); + + if !was_unreachable && !frame.returns.is_empty() { + let first_return = frame.returns[0]; + let loc = *self.value_stack.last().unwrap(); + if first_return.is_float() { + let fp = self.fp_stack.peek1()?; + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization + && fp.canonicalization.is_some() + { + self.canonicalize_nan( + match first_return { + WpType::F32 => Size::S32, + WpType::F64 => Size::S64, + _ => unreachable!(), + }, + loc, + Location::GPR(GPR::RAX), + ); + } else { + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + } else { + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + } + + let frame = self.control_stack.last_mut().unwrap(); + + let released: &[Location] = &self.value_stack[frame.value_stack_depth..]; + self.machine.release_locations(self.assembler, released); + self.value_stack.truncate(frame.value_stack_depth); + self.fp_stack.truncate(frame.fp_stack_depth); + + match frame.if_else { + IfElseState::If(label) => { + self.assembler.emit_jmp(Condition::None, frame.br_label); + self.assembler.emit_label(label); + frame.if_else = IfElseState::Else; + } + _ => { + return Err(CodegenError { + message: "Else: frame.if_else unreachable code".to_string(), + }) + } + } + } + // `TypedSelect` must be used for extern refs so ref counting should + // be done with TypedSelect. But otherwise they're the same. + Operator::TypedSelect { .. } | Operator::Select => { + let cond = self.pop_value_released(); + let v_b = self.pop_value_released(); + let v_a = self.pop_value_released(); + let cncl: Option<(Option, Option)> = + if self.fp_stack.len() >= 2 + && self.fp_stack[self.fp_stack.len() - 2].depth == self.value_stack.len() + && self.fp_stack[self.fp_stack.len() - 1].depth + == self.value_stack.len() + 1 + { + let (left, right) = self.fp_stack.pop2()?; + self.fp_stack.push(FloatValue::new(self.value_stack.len())); + Some((left.canonicalization, right.canonicalization)) + } else { + None + }; + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let end_label = self.assembler.get_label(); + let zero_label = self.assembler.get_label(); + + self.emit_relaxed_binop(Assembler::emit_cmp, Size::S32, Location::Imm32(0), cond); + self.assembler.emit_jmp(Condition::Equal, zero_label); + match cncl { + Some((Some(fp), _)) + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization => + { + self.canonicalize_nan(fp.to_size(), v_a, ret); + } + _ => { + if v_a != ret { + self.emit_relaxed_binop(Assembler::emit_mov, Size::S64, v_a, ret); + } + } + } + self.assembler.emit_jmp(Condition::None, end_label); + self.assembler.emit_label(zero_label); + match cncl { + Some((_, Some(fp))) + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization => + { + self.canonicalize_nan(fp.to_size(), v_b, ret); + } + _ => { + if v_b != ret { + self.emit_relaxed_binop(Assembler::emit_mov, Size::S64, v_b, ret); + } + } + } + self.assembler.emit_label(end_label); + } + Operator::Block { blockty } => { + let frame = ControlFrame { + br_label: self.assembler.get_label(), + loop_like: false, + if_else: IfElseState::None, + returns: match blockty { + WpBlockType::Empty => smallvec![], + WpBlockType::Type(inner_ty) => smallvec![inner_ty], + WpBlockType::FuncType(_) => { + return Err(CodegenError { + message: "Block: multi-value returns not yet implemented" + .to_string(), + }) + } + }, + value_stack_depth: self.value_stack.len(), + fp_stack_depth: self.fp_stack.len(), + }; + self.control_stack.push(frame); + } + Operator::Loop { blockty } => { + // Pad with NOPs to the next 16-byte boundary. + // Here we don't use the dynasm `.align 16` attribute because it pads the alignment with single-byte nops + // which may lead to efficiency problems. + match self.assembler.get_offset().0 % 16 { + 0 => {} + x => { + self.assembler.emit_nop_n(16 - x); + } + } + assert_eq!(self.assembler.get_offset().0 % 16, 0); + + let br_label = self.assembler.get_label(); + let _activate_offset = self.assembler.get_offset().0; + + self.control_stack.push(ControlFrame { + br_label, + loop_like: true, + if_else: IfElseState::None, + returns: match blockty { + WpBlockType::Empty => smallvec![], + WpBlockType::Type(inner_ty) => smallvec![inner_ty], + WpBlockType::FuncType(_) => { + return Err(CodegenError { + message: "Loop: multi-value returns not yet implemented" + .to_string(), + }) + } + }, + value_stack_depth: self.value_stack.len(), + fp_stack_depth: self.fp_stack.len(), + }); + self.assembler.emit_label(br_label); + + // TODO: Re-enable interrupt signal check without branching + } + Operator::Nop => {} + Operator::MemorySize { mem, mem_byte: _ } => { + let memory_index = MemoryIndex::new(mem as usize); + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_builtin_function( + if self.module.local_memory_index(memory_index).is_some() { + VMBuiltinFunctionIndex::get_memory32_size_index() + } else { + VMBuiltinFunctionIndex::get_imported_memory32_size_index() + }, + ) as i32, + ), + Location::GPR(GPR::RAX), + ); + self.emit_call_native( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, memory_index] + iter::once(Location::Imm32(memory_index.index() as u32)), + )?; + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + self.assembler.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + Operator::MemoryInit { data_index, mem } => { + let len = self.value_stack.pop().unwrap(); + let src = self.value_stack.pop().unwrap(); + let dst = self.value_stack.pop().unwrap(); + self.machine.release_locations_only_regs(&[len, src, dst]); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets + .vmctx_builtin_function(VMBuiltinFunctionIndex::get_memory_init_index()) + as i32, + ), + Location::GPR(GPR::RAX), + ); + + self.emit_call_native( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, memory_index, segment_index, dst, src, len] + [Location::Imm32(mem), Location::Imm32(data_index), dst, src, len] + .iter() + .cloned(), + )?; + self.machine.release_locations_only_stack(self.assembler, &[dst, src, len]); + } + Operator::DataDrop { data_index } => { + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets + .vmctx_builtin_function(VMBuiltinFunctionIndex::get_data_drop_index()) + as i32, + ), + Location::GPR(GPR::RAX), + ); + + self.emit_call_native( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, segment_index] + iter::once(Location::Imm32(data_index)), + )?; + } + Operator::MemoryCopy { src_mem, dst_mem } => { + // ignore until we support multiple memories + let _dst = dst_mem; + let len = self.value_stack.pop().unwrap(); + let src_pos = self.value_stack.pop().unwrap(); + let dst_pos = self.value_stack.pop().unwrap(); + self.machine.release_locations_only_regs(&[len, src_pos, dst_pos]); + + let memory_index = MemoryIndex::new(src_mem as usize); + let (memory_copy_index, memory_index) = + if self.module.local_memory_index(memory_index).is_some() { + (VMBuiltinFunctionIndex::get_memory_copy_index(), memory_index) + } else { + (VMBuiltinFunctionIndex::get_imported_memory_copy_index(), memory_index) + }; + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_builtin_function(memory_copy_index) as i32, + ), + Location::GPR(GPR::RAX), + ); + + self.emit_call_native( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, memory_index, dst, src, len] + [Location::Imm32(memory_index.index() as u32), dst_pos, src_pos, len] + .iter() + .cloned(), + )?; + self.machine.release_locations_only_stack(self.assembler, &[dst_pos, src_pos, len]); + } + Operator::MemoryFill { mem } => { + let len = self.value_stack.pop().unwrap(); + let val = self.value_stack.pop().unwrap(); + let dst = self.value_stack.pop().unwrap(); + self.machine.release_locations_only_regs(&[len, val, dst]); + + let memory_index = MemoryIndex::new(mem as usize); + let (memory_fill_index, memory_index) = + if self.module.local_memory_index(memory_index).is_some() { + (VMBuiltinFunctionIndex::get_memory_fill_index(), memory_index) + } else { + (VMBuiltinFunctionIndex::get_imported_memory_fill_index(), memory_index) + }; + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_builtin_function(memory_fill_index) as i32, + ), + Location::GPR(GPR::RAX), + ); + + self.emit_call_native( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, memory_index, dst, src, len] + [Location::Imm32(memory_index.index() as u32), dst, val, len].iter().cloned(), + )?; + self.machine.release_locations_only_stack(self.assembler, &[dst, val, len]); + } + Operator::MemoryGrow { mem, mem_byte: _ } => { + let memory_index = MemoryIndex::new(mem as usize); + let param_pages = self.value_stack.pop().unwrap(); + + self.machine.release_locations_only_regs(&[param_pages]); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_builtin_function( + if self.module.local_memory_index(memory_index).is_some() { + VMBuiltinFunctionIndex::get_memory32_grow_index() + } else { + VMBuiltinFunctionIndex::get_imported_memory32_grow_index() + }, + ) as i32, + ), + Location::GPR(GPR::RAX), + ); + + self.emit_call_native( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, val, memory_index] + iter::once(param_pages) + .chain(iter::once(Location::Imm32(memory_index.index() as u32))), + )?; + + self.machine.release_locations_only_stack(self.assembler, &[param_pages]); + + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + self.assembler.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + Operator::I32Load { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, false, 4, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + Location::Memory(addr, 0), + ret, + ); + Ok(()) + })?; + } + Operator::F32Load { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::F32)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); + + self.emit_memory_op(target, memarg, false, 4, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + Location::Memory(addr, 0), + ret, + ); + Ok(()) + })?; + } + Operator::I32Load8U { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, false, 1, |this, addr| { + this.emit_relaxed_zx_sx( + Assembler::emit_movzx, + Size::S8, + Location::Memory(addr, 0), + Size::S32, + ret, + )?; + Ok(()) + })?; + } + Operator::I32Load8S { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, false, 1, |this, addr| { + this.emit_relaxed_zx_sx( + Assembler::emit_movsx, + Size::S8, + Location::Memory(addr, 0), + Size::S32, + ret, + )?; + Ok(()) + })?; + } + Operator::I32Load16U { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, false, 2, |this, addr| { + this.emit_relaxed_zx_sx( + Assembler::emit_movzx, + Size::S16, + Location::Memory(addr, 0), + Size::S32, + ret, + )?; + Ok(()) + })?; + } + Operator::I32Load16S { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, false, 2, |this, addr| { + this.emit_relaxed_zx_sx( + Assembler::emit_movsx, + Size::S16, + Location::Memory(addr, 0), + Size::S32, + ret, + )?; + Ok(()) + })?; + } + Operator::I32Store { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + + self.emit_memory_op(target_addr, memarg, false, 4, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + target_value, + Location::Memory(addr, 0), + ); + Ok(()) + })?; + } + Operator::F32Store { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + let fp = self.fp_stack.pop1()?; + let config_nan_canonicalization = self.config.enable_nan_canonicalization; + + self.emit_memory_op(target_addr, memarg, false, 4, |this, addr| { + if !this.assembler.arch_supports_canonicalize_nan() + || !config_nan_canonicalization + || fp.canonicalization.is_none() + { + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + target_value, + Location::Memory(addr, 0), + ); + } else { + this.canonicalize_nan(Size::S32, target_value, Location::Memory(addr, 0)); + } + + Ok(()) + })?; + } + Operator::I32Store8 { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + + self.emit_memory_op(target_addr, memarg, false, 1, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S8, + target_value, + Location::Memory(addr, 0), + ); + Ok(()) + })?; + } + Operator::I32Store16 { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + + self.emit_memory_op(target_addr, memarg, false, 2, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S16, + target_value, + Location::Memory(addr, 0), + ); + Ok(()) + })?; + } + Operator::I64Load { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, false, 8, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::Memory(addr, 0), + ret, + ); + Ok(()) + })?; + } + Operator::F64Load { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::F64)], false)[0]; + self.value_stack.push(ret); + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); + + self.emit_memory_op(target, memarg, false, 8, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::Memory(addr, 0), + ret, + ); + Ok(()) + })?; + } + Operator::I64Load8U { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, false, 1, |this, addr| { + this.emit_relaxed_zx_sx( + Assembler::emit_movzx, + Size::S8, + Location::Memory(addr, 0), + Size::S64, + ret, + )?; + Ok(()) + })?; + } + Operator::I64Load8S { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, false, 1, |this, addr| { + this.emit_relaxed_zx_sx( + Assembler::emit_movsx, + Size::S8, + Location::Memory(addr, 0), + Size::S64, + ret, + )?; + Ok(()) + })?; + } + Operator::I64Load16U { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, false, 2, |this, addr| { + this.emit_relaxed_zx_sx( + Assembler::emit_movzx, + Size::S16, + Location::Memory(addr, 0), + Size::S64, + ret, + )?; + Ok(()) + })?; + } + Operator::I64Load16S { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, false, 2, |this, addr| { + this.emit_relaxed_zx_sx( + Assembler::emit_movsx, + Size::S16, + Location::Memory(addr, 0), + Size::S64, + ret, + )?; + Ok(()) + })?; + } + Operator::I64Load32U { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, false, 4, |this, addr| { + match ret { + Location::GPR(_) => {} + Location::Memory(base, offset) => { + this.assembler.emit_mov( + Size::S32, + Location::Imm32(0), + Location::Memory(base, offset + 4), + ); // clear upper bits + } + _ => { + return Err(CodegenError { + message: "I64Load32U ret: unreachable code".to_string(), + }) + } + } + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + Location::Memory(addr, 0), + ret, + ); + Ok(()) + })?; + } + Operator::I64Load32S { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, false, 4, |this, addr| { + this.emit_relaxed_zx_sx( + Assembler::emit_movsx, + Size::S32, + Location::Memory(addr, 0), + Size::S64, + ret, + )?; + Ok(()) + })?; + } + Operator::I64Store { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + + self.emit_memory_op(target_addr, memarg, false, 8, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + target_value, + Location::Memory(addr, 0), + ); + Ok(()) + })?; + } + Operator::F64Store { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + let fp = self.fp_stack.pop1()?; + let config_nan_canonicalization = self.config.enable_nan_canonicalization; + + self.emit_memory_op(target_addr, memarg, false, 8, |this, addr| { + if !this.assembler.arch_supports_canonicalize_nan() + || !config_nan_canonicalization + || fp.canonicalization.is_none() + { + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + target_value, + Location::Memory(addr, 0), + ); + } else { + this.canonicalize_nan(Size::S64, target_value, Location::Memory(addr, 0)); + } + Ok(()) + })?; + } + Operator::I64Store8 { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + + self.emit_memory_op(target_addr, memarg, false, 1, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S8, + target_value, + Location::Memory(addr, 0), + ); + Ok(()) + })?; + } + Operator::I64Store16 { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + + self.emit_memory_op(target_addr, memarg, false, 2, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S16, + target_value, + Location::Memory(addr, 0), + ); + Ok(()) + })?; + } + Operator::I64Store32 { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + + self.emit_memory_op(target_addr, memarg, false, 4, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + target_value, + Location::Memory(addr, 0), + ); + Ok(()) + })?; + } + Operator::Unreachable => { + let offset = self.assembler.get_offset().0; + self.emit_trap(TrapCode::UnreachableCodeReached); + self.mark_instruction_address_end(offset); + self.unreachable_depth = 1; + } + Operator::Return => { + let frame = &self.control_stack[0]; + if !frame.returns.is_empty() { + if frame.returns.len() != 1 { + return Err(CodegenError { + message: "Return: incorrect frame.returns".to_string(), + }); + } + let first_return = frame.returns[0]; + let loc = *self.value_stack.last().unwrap(); + if first_return.is_float() { + let fp = self.fp_stack.peek1()?; + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization + && fp.canonicalization.is_some() + { + self.canonicalize_nan( + match first_return { + WpType::F32 => Size::S32, + WpType::F64 => Size::S64, + _ => unreachable!(), + }, + loc, + Location::GPR(GPR::RAX), + ); + } else { + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + } else { + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + } + let frame = &self.control_stack[0]; + let released = &self.value_stack[frame.value_stack_depth..]; + self.machine.release_locations_keep_state(self.assembler, released); + self.assembler.emit_jmp(Condition::None, frame.br_label); + self.unreachable_depth = 1; + } + Operator::Br { relative_depth } => { + let frame = + &self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)]; + if !frame.loop_like && !frame.returns.is_empty() { + if frame.returns.len() != 1 { + return Err(CodegenError { + message: "Br: incorrect frame.returns".to_string(), + }); + } + let first_return = frame.returns[0]; + let loc = *self.value_stack.last().unwrap(); + + if first_return.is_float() { + let fp = self.fp_stack.peek1()?; + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization + && fp.canonicalization.is_some() + { + self.canonicalize_nan( + match first_return { + WpType::F32 => Size::S32, + WpType::F64 => Size::S64, + _ => unreachable!(), + }, + loc, + Location::GPR(GPR::RAX), + ); + } else { + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + } else { + self.assembler.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); + } + } + let frame = + &self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)]; + + let released = &self.value_stack[frame.value_stack_depth..]; + self.machine.release_locations_keep_state(self.assembler, released); + self.assembler.emit_jmp(Condition::None, frame.br_label); + self.unreachable_depth = 1; + } + Operator::BrIf { relative_depth } => { + let after = self.assembler.get_label(); + let cond = self.pop_value_released(); + self.emit_relaxed_binop(Assembler::emit_cmp, Size::S32, Location::Imm32(0), cond); + self.assembler.emit_jmp(Condition::Equal, after); + + let frame = + &self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)]; + if !frame.loop_like && !frame.returns.is_empty() { + if frame.returns.len() != 1 { + return Err(CodegenError { + message: "BrIf: incorrect frame.returns".to_string(), + }); + } + + let first_return = frame.returns[0]; + let loc = *self.value_stack.last().unwrap(); + if first_return.is_float() { + let fp = self.fp_stack.peek1()?; + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization + && fp.canonicalization.is_some() + { + self.canonicalize_nan( + match first_return { + WpType::F32 => Size::S32, + WpType::F64 => Size::S64, + _ => unreachable!(), + }, + loc, + Location::GPR(GPR::RAX), + ); + } else { + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + } else { + self.assembler.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); + } + } + let frame = + &self.control_stack[self.control_stack.len() - 1 - (relative_depth as usize)]; + let released = &self.value_stack[frame.value_stack_depth..]; + self.machine.release_locations_keep_state(self.assembler, released); + self.assembler.emit_jmp(Condition::None, frame.br_label); + + self.assembler.emit_label(after); + } + Operator::BrTable { ref targets } => { + let default_target = targets.default(); + let targets = targets.targets().collect::, _>>().map_err(|e| { + CodegenError { message: format!("BrTable read_table: {:?}", e) } + })?; + let cond = self.pop_value_released(); + let table_label = self.assembler.get_label(); + let mut table: Vec = vec![]; + let default_br = self.assembler.get_label(); + self.emit_relaxed_binop( + Assembler::emit_cmp, + Size::S32, + Location::Imm32(targets.len() as u32), + cond, + ); + self.assembler.emit_jmp(Condition::AboveEqual, default_br); + + self.assembler.emit_lea_label(table_label, Location::GPR(GPR::RCX)); + self.assembler.emit_mov(Size::S32, cond, Location::GPR(GPR::RDX)); + + let instr_size = self.assembler.get_jmp_instr_size(); + self.assembler.emit_imul_imm32_gpr64(instr_size as _, GPR::RDX); + self.assembler.emit_add( + Size::S64, + Location::GPR(GPR::RCX), + Location::GPR(GPR::RDX), + ); + self.assembler.emit_jmp_location(Location::GPR(GPR::RDX)); + + for target in targets.iter() { + let label = self.assembler.get_label(); + self.assembler.emit_label(label); + table.push(label); + let frame = + &self.control_stack[self.control_stack.len() - 1 - (*target as usize)]; + if !frame.loop_like && !frame.returns.is_empty() { + if frame.returns.len() != 1 { + return Err(CodegenError { + message: format!( + "BrTable: incorrect frame.returns for {:?}", + target + ), + }); + } + + let first_return = frame.returns[0]; + let loc = *self.value_stack.last().unwrap(); + if first_return.is_float() { + let fp = self.fp_stack.peek1()?; + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization + && fp.canonicalization.is_some() + { + self.canonicalize_nan( + match first_return { + WpType::F32 => Size::S32, + WpType::F64 => Size::S64, + _ => unreachable!(), + }, + loc, + Location::GPR(GPR::RAX), + ); + } else { + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + } else { + self.assembler.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); + } + } + let frame = + &self.control_stack[self.control_stack.len() - 1 - (*target as usize)]; + let released = &self.value_stack[frame.value_stack_depth..]; + self.machine.release_locations_keep_state(self.assembler, released); + self.assembler.emit_jmp(Condition::None, frame.br_label); + } + self.assembler.emit_label(default_br); + + { + let frame = &self.control_stack + [self.control_stack.len() - 1 - (default_target as usize)]; + if !frame.loop_like && !frame.returns.is_empty() { + if frame.returns.len() != 1 { + return Err(CodegenError { + message: "BrTable: incorrect frame.returns".to_string(), + }); + } + + let first_return = frame.returns[0]; + let loc = *self.value_stack.last().unwrap(); + if first_return.is_float() { + let fp = self.fp_stack.peek1()?; + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization + && fp.canonicalization.is_some() + { + self.canonicalize_nan( + match first_return { + WpType::F32 => Size::S32, + WpType::F64 => Size::S64, + _ => unreachable!(), + }, + loc, + Location::GPR(GPR::RAX), + ); + } else { + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + } else { + self.assembler.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); + } + } + let frame = &self.control_stack + [self.control_stack.len() - 1 - (default_target as usize)]; + let released = &self.value_stack[frame.value_stack_depth..]; + self.machine.release_locations_keep_state(self.assembler, released); + self.assembler.emit_jmp(Condition::None, frame.br_label); + } + + self.assembler.emit_label(table_label); + for x in table { + self.assembler.emit_jmp(Condition::None, x); + } + self.unreachable_depth = 1; + } + Operator::Drop => { + self.pop_value_released(); + if let Some(x) = self.fp_stack.last() { + if x.depth == self.value_stack.len() { + self.fp_stack.pop1()?; + } + } + } + Operator::End => { + let frame = self.control_stack.pop().unwrap(); + + if !was_unreachable && !frame.returns.is_empty() { + let loc = *self.value_stack.last().unwrap(); + if frame.returns[0].is_float() { + let fp = self.fp_stack.peek1()?; + if self.assembler.arch_supports_canonicalize_nan() + && self.config.enable_nan_canonicalization + && fp.canonicalization.is_some() + { + self.canonicalize_nan( + match frame.returns[0] { + WpType::F32 => Size::S32, + WpType::F64 => Size::S64, + _ => unreachable!(), + }, + loc, + Location::GPR(GPR::RAX), + ); + } else { + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + } else { + self.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + loc, + Location::GPR(GPR::RAX), + ); + } + } + + if self.control_stack.is_empty() { + self.assembler.emit_label(frame.br_label); + let local_count = self.local_count(); + self.machine.finalize_locals(self.assembler); + + // Restore stack height + self.assembler.emit_add( + Size::S32, + Location::Imm32(self.stack_size), + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_stack_limit_begin() as i32, + ), + ); + + self.machine.restore_registers( + self.assembler, + self.calling_convention, + local_count, + ); + self.assembler.emit_mov( + Size::S64, + Location::GPR(GPR::RBP), + Location::GPR(GPR::RSP), + ); + self.assembler.emit_pop(Size::S64, Location::GPR(GPR::RBP)); + + // Make a copy of the return value in XMM0, as required by the SysV CC. + match self.signature.results() { + [x] if *x == Type::F32 || *x == Type::F64 => { + self.assembler.emit_mov( + Size::S64, + Location::GPR(GPR::RAX), + Location::XMM(XMM::XMM0), + ); + } + _ => {} + } + self.assembler.emit_ret(); + } else { + let released = &self.value_stack[frame.value_stack_depth..]; + self.machine.release_locations(self.assembler, released); + self.value_stack.truncate(frame.value_stack_depth); + self.fp_stack.truncate(frame.fp_stack_depth); + + if !frame.loop_like { + self.assembler.emit_label(frame.br_label); + } + + if let IfElseState::If(label) = frame.if_else { + self.assembler.emit_label(label); + } + + if !frame.returns.is_empty() { + if frame.returns.len() != 1 { + return Err(CodegenError { + message: "End: incorrect frame.returns".to_string(), + }); + } + let loc = self.machine.acquire_locations( + self.assembler, + &[(frame.returns[0])], + false, + )[0]; + self.assembler.emit_mov(Size::S64, Location::GPR(GPR::RAX), loc); + self.value_stack.push(loc); + if frame.returns[0].is_float() { + self.fp_stack.push(FloatValue::new(self.value_stack.len() - 1)); + // we already canonicalized at the `Br*` instruction or here previously. + } + } + } + } + Operator::AtomicFence => { + // Fence is a nop. + // + // Fence was added to preserve information about fences from + // source languages. If in the future Wasm extends the memory + // model, and if we hadn't recorded what fences used to be there, + // it would lead to data races that weren't present in the + // original source language. + } + Operator::I32AtomicLoad { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, true, 4, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + Location::Memory(addr, 0), + ret, + ); + Ok(()) + })?; + } + Operator::I32AtomicLoad8U { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, true, 1, |this, addr| { + this.emit_relaxed_zx_sx( + Assembler::emit_movzx, + Size::S8, + Location::Memory(addr, 0), + Size::S32, + ret, + )?; + Ok(()) + })?; + } + Operator::I32AtomicLoad16U { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, true, 2, |this, addr| { + this.emit_relaxed_zx_sx( + Assembler::emit_movzx, + Size::S16, + Location::Memory(addr, 0), + Size::S32, + ret, + )?; + Ok(()) + })?; + } + Operator::I32AtomicStore { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + + self.emit_memory_op(target_addr, memarg, true, 4, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_xchg, + Size::S32, + target_value, + Location::Memory(addr, 0), + ); + Ok(()) + })?; + } + Operator::I32AtomicStore8 { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + + self.emit_memory_op(target_addr, memarg, true, 1, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_xchg, + Size::S8, + target_value, + Location::Memory(addr, 0), + ); + Ok(()) + })?; + } + Operator::I32AtomicStore16 { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + + self.emit_memory_op(target_addr, memarg, true, 2, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_xchg, + Size::S16, + target_value, + Location::Memory(addr, 0), + ); + Ok(()) + })?; + } + Operator::I64AtomicLoad { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, true, 8, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S64, + Location::Memory(addr, 0), + ret, + ); + Ok(()) + })?; + } + Operator::I64AtomicLoad8U { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, true, 1, |this, addr| { + this.emit_relaxed_zx_sx( + Assembler::emit_movzx, + Size::S8, + Location::Memory(addr, 0), + Size::S64, + ret, + )?; + Ok(()) + })?; + } + Operator::I64AtomicLoad16U { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, true, 2, |this, addr| { + this.emit_relaxed_zx_sx( + Assembler::emit_movzx, + Size::S16, + Location::Memory(addr, 0), + Size::S64, + ret, + )?; + Ok(()) + })?; + } + Operator::I64AtomicLoad32U { ref memarg } => { + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_memory_op(target, memarg, true, 4, |this, addr| { + match ret { + Location::GPR(_) => {} + Location::Memory(base, offset) => { + this.assembler.emit_mov( + Size::S32, + Location::Imm32(0), + Location::Memory(base, offset + 4), + ); // clear upper bits + } + _ => { + return Err(CodegenError { + message: "I64AtomicLoad32U ret: unreachable code".to_string(), + }) + } + } + this.emit_relaxed_binop( + Assembler::emit_mov, + Size::S32, + Location::Memory(addr, 0), + ret, + ); + Ok(()) + })?; + } + Operator::I64AtomicStore { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + + self.emit_memory_op(target_addr, memarg, true, 8, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_xchg, + Size::S64, + target_value, + Location::Memory(addr, 0), + ); + Ok(()) + })?; + } + Operator::I64AtomicStore8 { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + + self.emit_memory_op(target_addr, memarg, true, 1, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_xchg, + Size::S8, + target_value, + Location::Memory(addr, 0), + ); + Ok(()) + })?; + } + Operator::I64AtomicStore16 { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + + self.emit_memory_op(target_addr, memarg, true, 2, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_xchg, + Size::S16, + target_value, + Location::Memory(addr, 0), + ); + Ok(()) + })?; + } + Operator::I64AtomicStore32 { ref memarg } => { + let target_value = self.pop_value_released(); + let target_addr = self.pop_value_released(); + + self.emit_memory_op(target_addr, memarg, true, 4, |this, addr| { + this.emit_relaxed_binop( + Assembler::emit_xchg, + Size::S32, + target_value, + Location::Memory(addr, 0), + ); + Ok(()) + })?; + } + Operator::I32AtomicRmwAdd { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S32, loc, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 4, |this, addr| { + this.assembler.emit_lock_xadd( + Size::S32, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S32, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmwAdd { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S64, loc, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 8, |this, addr| { + this.assembler.emit_lock_xadd( + Size::S64, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I32AtomicRmw8AddU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_movzx(Size::S8, loc, Size::S32, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 1, |this, addr| { + this.assembler.emit_lock_xadd( + Size::S8, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S32, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I32AtomicRmw16AddU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_movzx(Size::S16, loc, Size::S32, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 2, |this, addr| { + this.assembler.emit_lock_xadd( + Size::S16, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S32, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmw8AddU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_movzx(Size::S8, loc, Size::S64, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 1, |this, addr| { + this.assembler.emit_lock_xadd( + Size::S8, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmw16AddU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_movzx(Size::S16, loc, Size::S64, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 2, |this, addr| { + this.assembler.emit_lock_xadd( + Size::S16, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmw32AddU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S32, loc, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 4, |this, addr| { + this.assembler.emit_lock_xadd( + Size::S32, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I32AtomicRmwSub { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S32, loc, Location::GPR(value)); + self.assembler.emit_neg(Size::S32, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 4, |this, addr| { + this.assembler.emit_lock_xadd( + Size::S32, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S32, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmwSub { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S64, loc, Location::GPR(value)); + self.assembler.emit_neg(Size::S64, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 8, |this, addr| { + this.assembler.emit_lock_xadd( + Size::S64, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I32AtomicRmw8SubU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_movzx(Size::S8, loc, Size::S32, Location::GPR(value)); + self.assembler.emit_neg(Size::S8, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 1, |this, addr| { + this.assembler.emit_lock_xadd( + Size::S8, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S32, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I32AtomicRmw16SubU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_movzx(Size::S16, loc, Size::S32, Location::GPR(value)); + self.assembler.emit_neg(Size::S16, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 2, |this, addr| { + this.assembler.emit_lock_xadd( + Size::S16, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S32, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmw8SubU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_movzx(Size::S8, loc, Size::S64, Location::GPR(value)); + self.assembler.emit_neg(Size::S8, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 1, |this, addr| { + this.assembler.emit_lock_xadd( + Size::S8, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmw16SubU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_movzx(Size::S16, loc, Size::S64, Location::GPR(value)); + self.assembler.emit_neg(Size::S16, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 2, |this, addr| { + this.assembler.emit_lock_xadd( + Size::S16, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmw32SubU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S32, loc, Location::GPR(value)); + self.assembler.emit_neg(Size::S32, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 2, |this, addr| { + this.assembler.emit_lock_xadd( + Size::S32, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I32AtomicRmwAnd { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 4, + Size::S32, + Size::S32, + |this, src, dst| { + this.assembler.emit_and(Size::S32, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I64AtomicRmwAnd { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 8, + Size::S64, + Size::S64, + |this, src, dst| { + this.assembler.emit_and(Size::S64, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I32AtomicRmw8AndU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 1, + Size::S8, + Size::S32, + |this, src, dst| { + this.assembler.emit_and(Size::S32, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I32AtomicRmw16AndU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 1, + Size::S16, + Size::S32, + |this, src, dst| { + this.assembler.emit_and(Size::S32, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I64AtomicRmw8AndU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 1, + Size::S8, + Size::S64, + |this, src, dst| { + this.assembler.emit_and(Size::S64, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I64AtomicRmw16AndU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 1, + Size::S16, + Size::S64, + |this, src, dst| { + this.assembler.emit_and(Size::S64, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I64AtomicRmw32AndU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 1, + Size::S32, + Size::S64, + |this, src, dst| { + this.assembler.emit_and(Size::S64, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I32AtomicRmwOr { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 4, + Size::S32, + Size::S32, + |this, src, dst| { + this.assembler.emit_or(Size::S32, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I64AtomicRmwOr { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 8, + Size::S64, + Size::S64, + |this, src, dst| { + this.assembler.emit_or(Size::S64, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I32AtomicRmw8OrU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 1, + Size::S8, + Size::S32, + |this, src, dst| { + this.assembler.emit_or(Size::S32, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I32AtomicRmw16OrU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 1, + Size::S16, + Size::S32, + |this, src, dst| { + this.assembler.emit_or(Size::S32, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I64AtomicRmw8OrU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 1, + Size::S8, + Size::S64, + |this, src, dst| { + this.assembler.emit_or(Size::S64, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I64AtomicRmw16OrU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 1, + Size::S16, + Size::S64, + |this, src, dst| { + this.assembler.emit_or(Size::S64, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I64AtomicRmw32OrU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 1, + Size::S32, + Size::S64, + |this, src, dst| { + this.assembler.emit_or(Size::S64, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I32AtomicRmwXor { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 4, + Size::S32, + Size::S32, + |this, src, dst| { + this.assembler.emit_xor(Size::S32, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I64AtomicRmwXor { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 8, + Size::S64, + Size::S64, + |this, src, dst| { + this.assembler.emit_xor(Size::S64, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I32AtomicRmw8XorU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 1, + Size::S8, + Size::S32, + |this, src, dst| { + this.assembler.emit_xor(Size::S32, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I32AtomicRmw16XorU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 1, + Size::S16, + Size::S32, + |this, src, dst| { + this.assembler.emit_xor(Size::S32, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I64AtomicRmw8XorU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 1, + Size::S8, + Size::S64, + |this, src, dst| { + this.assembler.emit_xor(Size::S64, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I64AtomicRmw16XorU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 1, + Size::S16, + Size::S64, + |this, src, dst| { + this.assembler.emit_xor(Size::S64, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I64AtomicRmw32XorU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + self.emit_compare_and_swap( + loc, + target, + ret, + memarg, + 1, + Size::S32, + Size::S64, + |this, src, dst| { + this.assembler.emit_xor(Size::S64, Location::GPR(src), Location::GPR(dst)); + }, + )?; + } + Operator::I32AtomicRmwXchg { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S32, loc, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 4, |this, addr| { + this.assembler.emit_xchg( + Size::S32, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S32, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmwXchg { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S64, loc, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 8, |this, addr| { + this.assembler.emit_xchg( + Size::S64, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I32AtomicRmw8XchgU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_movzx(Size::S8, loc, Size::S32, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 1, |this, addr| { + this.assembler.emit_xchg( + Size::S8, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S32, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I32AtomicRmw16XchgU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_movzx(Size::S16, loc, Size::S32, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 2, |this, addr| { + this.assembler.emit_xchg( + Size::S16, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S32, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmw8XchgU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_movzx(Size::S8, loc, Size::S64, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 1, |this, addr| { + this.assembler.emit_xchg( + Size::S8, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmw16XchgU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_movzx(Size::S16, loc, Size::S64, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 2, |this, addr| { + this.assembler.emit_xchg( + Size::S16, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I64AtomicRmw32XchgU { ref memarg } => { + let loc = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let value = self.machine.acquire_temp_gpr().unwrap(); + self.assembler.emit_mov(Size::S32, loc, Location::GPR(value)); + self.emit_memory_op(target, memarg, true, 4, |this, addr| { + this.assembler.emit_xchg( + Size::S32, + Location::GPR(value), + Location::Memory(addr, 0), + ); + Ok(()) + })?; + self.assembler.emit_mov(Size::S64, Location::GPR(value), ret); + self.machine.release_temp_gpr(value); + } + Operator::I32AtomicRmwCmpxchg { ref memarg } => { + let new = self.pop_value_released(); + let cmp = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + let compare = self.machine.reserve_unused_temp_gpr(GPR::RAX); + let value = if cmp == Location::GPR(GPR::R14) { + if new == Location::GPR(GPR::R13) { + GPR::R12 + } else { + GPR::R13 + } + } else { + GPR::R14 + }; + self.assembler.emit_push(Size::S64, Location::GPR(value)); + self.assembler.emit_mov(Size::S32, cmp, Location::GPR(compare)); + self.assembler.emit_mov(Size::S32, new, Location::GPR(value)); + + self.emit_memory_op(target, memarg, true, 4, |this, addr| { + this.assembler.emit_lock_cmpxchg( + Size::S32, + Location::GPR(value), + Location::Memory(addr, 0), + ); + this.assembler.emit_mov(Size::S32, Location::GPR(compare), ret); + Ok(()) + })?; + self.assembler.emit_pop(Size::S64, Location::GPR(value)); + self.machine.release_temp_gpr(compare); + } + Operator::I64AtomicRmwCmpxchg { ref memarg } => { + let new = self.pop_value_released(); + let cmp = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let compare = self.machine.reserve_unused_temp_gpr(GPR::RAX); + let value = if cmp == Location::GPR(GPR::R14) { + if new == Location::GPR(GPR::R13) { + GPR::R12 + } else { + GPR::R13 + } + } else { + GPR::R14 + }; + self.assembler.emit_push(Size::S64, Location::GPR(value)); + self.assembler.emit_mov(Size::S64, cmp, Location::GPR(compare)); + self.assembler.emit_mov(Size::S64, new, Location::GPR(value)); + + self.emit_memory_op(target, memarg, true, 8, |this, addr| { + this.assembler.emit_lock_cmpxchg( + Size::S64, + Location::GPR(value), + Location::Memory(addr, 0), + ); + this.assembler.emit_mov(Size::S64, Location::GPR(compare), ret); + Ok(()) + })?; + self.assembler.emit_pop(Size::S64, Location::GPR(value)); + self.machine.release_temp_gpr(compare); + } + Operator::I32AtomicRmw8CmpxchgU { ref memarg } => { + let new = self.pop_value_released(); + let cmp = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + let compare = self.machine.reserve_unused_temp_gpr(GPR::RAX); + let value = if cmp == Location::GPR(GPR::R14) { + if new == Location::GPR(GPR::R13) { + GPR::R12 + } else { + GPR::R13 + } + } else { + GPR::R14 + }; + self.assembler.emit_push(Size::S64, Location::GPR(value)); + self.assembler.emit_mov(Size::S32, cmp, Location::GPR(compare)); + self.assembler.emit_mov(Size::S32, new, Location::GPR(value)); + + self.emit_memory_op(target, memarg, true, 1, |this, addr| { + this.assembler.emit_lock_cmpxchg( + Size::S8, + Location::GPR(value), + Location::Memory(addr, 0), + ); + this.assembler.emit_movzx(Size::S8, Location::GPR(compare), Size::S32, ret); + Ok(()) + })?; + self.assembler.emit_pop(Size::S64, Location::GPR(value)); + self.machine.release_temp_gpr(compare); + } + Operator::I32AtomicRmw16CmpxchgU { ref memarg } => { + let new = self.pop_value_released(); + let cmp = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + + let compare = self.machine.reserve_unused_temp_gpr(GPR::RAX); + let value = if cmp == Location::GPR(GPR::R14) { + if new == Location::GPR(GPR::R13) { + GPR::R12 + } else { + GPR::R13 + } + } else { + GPR::R14 + }; + self.assembler.emit_push(Size::S64, Location::GPR(value)); + self.assembler.emit_mov(Size::S32, cmp, Location::GPR(compare)); + self.assembler.emit_mov(Size::S32, new, Location::GPR(value)); + + self.emit_memory_op(target, memarg, true, 1, |this, addr| { + this.assembler.emit_lock_cmpxchg( + Size::S16, + Location::GPR(value), + Location::Memory(addr, 0), + ); + this.assembler.emit_movzx(Size::S16, Location::GPR(compare), Size::S32, ret); + Ok(()) + })?; + self.assembler.emit_pop(Size::S64, Location::GPR(value)); + self.machine.release_temp_gpr(compare); + } + Operator::I64AtomicRmw8CmpxchgU { ref memarg } => { + let new = self.pop_value_released(); + let cmp = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let compare = self.machine.reserve_unused_temp_gpr(GPR::RAX); + let value = if cmp == Location::GPR(GPR::R14) { + if new == Location::GPR(GPR::R13) { + GPR::R12 + } else { + GPR::R13 + } + } else { + GPR::R14 + }; + self.assembler.emit_push(Size::S64, Location::GPR(value)); + self.assembler.emit_mov(Size::S64, cmp, Location::GPR(compare)); + self.assembler.emit_mov(Size::S64, new, Location::GPR(value)); + + self.emit_memory_op(target, memarg, true, 1, |this, addr| { + this.assembler.emit_lock_cmpxchg( + Size::S8, + Location::GPR(value), + Location::Memory(addr, 0), + ); + this.assembler.emit_movzx(Size::S8, Location::GPR(compare), Size::S64, ret); + Ok(()) + })?; + self.assembler.emit_pop(Size::S64, Location::GPR(value)); + self.machine.release_temp_gpr(compare); + } + Operator::I64AtomicRmw16CmpxchgU { ref memarg } => { + let new = self.pop_value_released(); + let cmp = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let compare = self.machine.reserve_unused_temp_gpr(GPR::RAX); + let value = if cmp == Location::GPR(GPR::R14) { + if new == Location::GPR(GPR::R13) { + GPR::R12 + } else { + GPR::R13 + } + } else { + GPR::R14 + }; + self.assembler.emit_push(Size::S64, Location::GPR(value)); + self.assembler.emit_mov(Size::S64, cmp, Location::GPR(compare)); + self.assembler.emit_mov(Size::S64, new, Location::GPR(value)); + + self.emit_memory_op(target, memarg, true, 1, |this, addr| { + this.assembler.emit_lock_cmpxchg( + Size::S16, + Location::GPR(value), + Location::Memory(addr, 0), + ); + this.assembler.emit_movzx(Size::S16, Location::GPR(compare), Size::S64, ret); + Ok(()) + })?; + self.assembler.emit_pop(Size::S64, Location::GPR(value)); + self.machine.release_temp_gpr(compare); + } + Operator::I64AtomicRmw32CmpxchgU { ref memarg } => { + let new = self.pop_value_released(); + let cmp = self.pop_value_released(); + let target = self.pop_value_released(); + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I64)], false)[0]; + self.value_stack.push(ret); + + let compare = self.machine.reserve_unused_temp_gpr(GPR::RAX); + let value = if cmp == Location::GPR(GPR::R14) { + if new == Location::GPR(GPR::R13) { + GPR::R12 + } else { + GPR::R13 + } + } else { + GPR::R14 + }; + self.assembler.emit_push(Size::S64, Location::GPR(value)); + self.assembler.emit_mov(Size::S64, cmp, Location::GPR(compare)); + self.assembler.emit_mov(Size::S64, new, Location::GPR(value)); + + self.emit_memory_op(target, memarg, true, 1, |this, addr| { + this.assembler.emit_lock_cmpxchg( + Size::S32, + Location::GPR(value), + Location::Memory(addr, 0), + ); + this.assembler.emit_mov(Size::S32, Location::GPR(compare), ret); + Ok(()) + })?; + self.assembler.emit_pop(Size::S64, Location::GPR(value)); + self.machine.release_temp_gpr(compare); + } + + Operator::RefNull { .. } => { + self.value_stack.push(Location::Imm64(0)); + } + Operator::RefFunc { function_index } => { + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets + .vmctx_builtin_function(VMBuiltinFunctionIndex::get_func_ref_index()) + as i32, + ), + Location::GPR(GPR::RAX), + ); + + self.emit_call_native( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, func_index] -> funcref + iter::once(Location::Imm32(function_index as u32)), + )?; + + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::FuncRef)], false)[0]; + self.value_stack.push(ret); + self.assembler.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + Operator::RefIsNull => { + self.emit_cmpop_i64_dynamic_b(Condition::Equal, Location::Imm64(0))?; + } + Operator::TableSet { table: index } => { + let table_index = TableIndex::new(index as _); + let value = self.value_stack.pop().unwrap(); + let index = self.value_stack.pop().unwrap(); + // double check this does what I think it does + self.machine.release_locations_only_regs(&[value, index]); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_builtin_function( + if self.module.local_table_index(table_index).is_some() { + VMBuiltinFunctionIndex::get_table_set_index() + } else { + VMBuiltinFunctionIndex::get_imported_table_set_index() + }, + ) as i32, + ), + Location::GPR(GPR::RAX), + ); + + self.emit_call_native( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, table_index, elem_index, reftype] + [Location::Imm32(table_index.index() as u32), index, value].iter().cloned(), + )?; + + self.machine.release_locations_only_stack(self.assembler, &[index, value]); + } + Operator::TableGet { table: index } => { + let table_index = TableIndex::new(index as _); + let index = self.value_stack.pop().unwrap(); + self.machine.release_locations_only_regs(&[index]); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_builtin_function( + if self.module.local_table_index(table_index).is_some() { + VMBuiltinFunctionIndex::get_table_get_index() + } else { + VMBuiltinFunctionIndex::get_imported_table_get_index() + }, + ) as i32, + ), + Location::GPR(GPR::RAX), + ); + + self.emit_call_native( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, table_index, elem_index] -> reftype + [Location::Imm32(table_index.index() as u32), index].iter().cloned(), + )?; + + self.machine.release_locations_only_stack(self.assembler, &[index]); + + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::FuncRef)], false)[0]; + self.value_stack.push(ret); + self.assembler.emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); + } + Operator::TableSize { table: index } => { + let table_index = TableIndex::new(index as _); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_builtin_function( + if self.module.local_table_index(table_index).is_some() { + VMBuiltinFunctionIndex::get_table_size_index() + } else { + VMBuiltinFunctionIndex::get_imported_table_size_index() + }, + ) as i32, + ), + Location::GPR(GPR::RAX), + ); + + self.emit_call_native( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, table_index] -> i32 + iter::once(Location::Imm32(table_index.index() as u32)), + )?; + + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + self.assembler.emit_mov(Size::S32, Location::GPR(GPR::RAX), ret); + } + Operator::TableGrow { table: index } => { + let table_index = TableIndex::new(index as _); + let delta = self.value_stack.pop().unwrap(); + let init_value = self.value_stack.pop().unwrap(); + self.machine.release_locations_only_regs(&[delta, init_value]); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_builtin_function( + if self.module.local_table_index(table_index).is_some() { + VMBuiltinFunctionIndex::get_table_grow_index() + } else { + VMBuiltinFunctionIndex::get_imported_table_get_index() + }, + ) as i32, + ), + Location::GPR(GPR::RAX), + ); + + // TODO: should this be 2? + self.emit_call_native( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, init_value, delta, table_index] -> u32 + [init_value, delta, Location::Imm32(table_index.index() as u32)] + .iter() + .cloned(), + )?; + + self.machine.release_locations_only_stack(self.assembler, &[init_value, delta]); + + let ret = + self.machine.acquire_locations(self.assembler, &[(WpType::I32)], false)[0]; + self.value_stack.push(ret); + self.assembler.emit_mov(Size::S32, Location::GPR(GPR::RAX), ret); + } + Operator::TableCopy { dst_table, src_table } => { + let len = self.value_stack.pop().unwrap(); + let src = self.value_stack.pop().unwrap(); + let dest = self.value_stack.pop().unwrap(); + self.machine.release_locations_only_regs(&[len, src, dest]); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets + .vmctx_builtin_function(VMBuiltinFunctionIndex::get_table_copy_index()) + as i32, + ), + Location::GPR(GPR::RAX), + ); + + // TODO: should this be 3? + self.emit_call_native( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, dst_table_index, src_table_index, dst, src, len] + [Location::Imm32(dst_table), Location::Imm32(src_table), dest, src, len] + .iter() + .cloned(), + )?; + + self.machine.release_locations_only_stack(self.assembler, &[dest, src, len]); + } + + Operator::TableFill { table } => { + let len = self.value_stack.pop().unwrap(); + let val = self.value_stack.pop().unwrap(); + let dest = self.value_stack.pop().unwrap(); + self.machine.release_locations_only_regs(&[len, val, dest]); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets + .vmctx_builtin_function(VMBuiltinFunctionIndex::get_table_fill_index()) + as i32, + ), + Location::GPR(GPR::RAX), + ); + + // TODO: should this be 3? + self.emit_call_native( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, table_index, start_idx, item, len] + [Location::Imm32(table), dest, val, len].iter().cloned(), + )?; + + self.machine.release_locations_only_stack(self.assembler, &[dest, val, len]); + } + Operator::TableInit { elem_index, table } => { + let len = self.value_stack.pop().unwrap(); + let src = self.value_stack.pop().unwrap(); + let dest = self.value_stack.pop().unwrap(); + self.machine.release_locations_only_regs(&[len, src, dest]); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets + .vmctx_builtin_function(VMBuiltinFunctionIndex::get_table_init_index()) + as i32, + ), + Location::GPR(GPR::RAX), + ); + + // TODO: should this be 3? + self.emit_call_native( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, table_index, elem_index, dst, src, len] + [Location::Imm32(table), Location::Imm32(elem_index), dest, src, len] + .iter() + .cloned(), + )?; + + self.machine.release_locations_only_stack(self.assembler, &[dest, src, len]); + } + Operator::ElemDrop { elem_index } => { + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets + .vmctx_builtin_function(VMBuiltinFunctionIndex::get_elem_drop_index()) + as i32, + ), + Location::GPR(GPR::RAX), + ); + + self.emit_call_native( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, elem_index] + [Location::Imm32(elem_index)].iter().cloned(), + )?; + } + _ => { + return Err(CodegenError { message: format!("not yet implemented: {:?}", op) }); + } + } + + Ok(()) + } + + #[tracing::instrument(target = "unc_vm", level = "trace", skip_all)] + pub(crate) fn finalize(mut self, data: &FunctionBodyData) -> CompiledFunction { + debug_assert!( + self.gas_iter.next().is_none(), + "finalizing function but not all instrumentation points were inserted" + ); + + // Generate actual code for special labels. + self.assembler.emit_label(self.special_labels.integer_division_by_zero); + self.emit_trap(TrapCode::IntegerDivisionByZero); + + self.assembler.emit_label(self.special_labels.integer_overflow); + self.emit_trap(TrapCode::IntegerOverflow); + + self.assembler.emit_label(self.special_labels.bad_conversion_to_integer); + self.emit_trap(TrapCode::BadConversionToInteger); + + self.assembler.emit_label(self.special_labels.heap_access_oob); + self.emit_trap(TrapCode::HeapAccessOutOfBounds); + + self.assembler.emit_label(self.special_labels.table_access_oob); + self.emit_trap(TrapCode::TableAccessOutOfBounds); + + self.assembler.emit_label(self.special_labels.indirect_call_null); + self.emit_trap(TrapCode::IndirectCallToNull); + + self.assembler.emit_label(self.special_labels.bad_signature); + self.emit_trap(TrapCode::BadSignature); + + self.assembler.emit_label(self.special_labels.gas_limit_exceeded); + self.emit_trap(TrapCode::GasExceeded); + + self.assembler.emit_label(self.special_labels.stack_overflow); + self.emit_trap(TrapCode::StackOverflow); + + // Notify the assembler backend to generate necessary code at end of function. + self.assembler.finalize_function(); + + let body_len = self.assembler.get_offset().0; + let instructions_address_map = self.instructions_address_map; + let address_map = get_function_address_map(instructions_address_map, data, body_len); + let body = self.assembler.drain().unwrap().collect(); + CompiledFunction { + body: FunctionBody { body }, + relocations: self.relocations, + jt_offsets: SecondaryMap::new(), + frame_info: CompiledFunctionFrameInfo { traps: vec![], address_map }, + } + } +} + +fn type_to_wp_type(ty: Type) -> WpType { + match ty { + Type::I32 => WpType::I32, + Type::I64 => WpType::I64, + Type::F32 => WpType::F32, + Type::F64 => WpType::F64, + Type::V128 => WpType::V128, + Type::ExternRef => WpType::ExternRef, + Type::FuncRef => WpType::FuncRef, // TODO: FuncRef or Func? + } +} + +// FIXME: This implementation seems to be not enough to resolve all kinds of register dependencies +// at call place. +fn sort_call_movs(movs: &mut [(Location, GPR)]) { + for i in 0..movs.len() { + for j in (i + 1)..movs.len() { + if let Location::GPR(src_gpr) = movs[j].0 { + if src_gpr == movs[i].1 { + movs.swap(i, j); + } + } + } + } + + // Cycle detector. Uncomment this to debug possibly incorrect call-mov sequences. + /* + { + use std::collections::{HashMap, HashSet, VecDeque}; + let mut mov_map: HashMap> = HashMap::new(); + for mov in movs.iter() { + if let Location::GPR(src_gpr) = mov.0 { + if src_gpr != mov.1 { + mov_map.entry(src_gpr).or_insert_with(|| HashSet::new()).insert(mov.1); + } + } + } + + for (start, _) in mov_map.iter() { + let mut q: VecDeque = VecDeque::new(); + let mut black: HashSet = HashSet::new(); + + q.push_back(*start); + black.insert(*start); + + while q.len() > 0 { + let reg = q.pop_front().unwrap(); + let empty_set = HashSet::new(); + for x in mov_map.get(®).unwrap_or(&empty_set).iter() { + if black.contains(x) { + panic!("cycle detected"); + } + q.push_back(*x); + black.insert(*x); + } + } + } + } + */ +} + +// Standard entry trampoline. +#[tracing::instrument(target = "unc_vm", level = "trace")] +pub(crate) fn gen_std_trampoline( + sig: &FunctionType, + calling_convention: CallingConvention, + a: &mut Assembler, +) -> FunctionBody { + // Calculate stack offset. + let mut stack_offset: u32 = 0; + for (i, _param) in sig.params().iter().enumerate() { + if let Location::Memory(_, _) = Machine::get_param_location(1 + i, calling_convention) { + stack_offset += 8; + } + } + let stack_padding: u32 = match calling_convention { + CallingConvention::WindowsFastcall => 32, + _ => 0, + }; + + // Align to 16 bytes. We push two 8-byte registers below, so here we need to ensure stack_offset % 16 == 8. + if stack_offset % 16 != 8 { + stack_offset += 8; + } + + // Used callee-saved registers + a.emit_push(Size::S64, Location::GPR(GPR::R15)); + a.emit_push(Size::S64, Location::GPR(GPR::R14)); + + // Prepare stack space. + a.emit_sub(Size::S64, Location::Imm32(stack_offset + stack_padding), Location::GPR(GPR::RSP)); + + // Arguments + a.emit_mov( + Size::S64, + Machine::get_param_location(1, calling_convention), + Location::GPR(GPR::R15), + ); // func_ptr + a.emit_mov( + Size::S64, + Machine::get_param_location(2, calling_convention), + Location::GPR(GPR::R14), + ); // args_rets + + // Move arguments to their locations. + // `callee_vmctx` is already in the first argument register, so no need to move. + { + let mut n_stack_args: usize = 0; + for (i, _param) in sig.params().iter().enumerate() { + let src_loc = Location::Memory(GPR::R14, (i * 16) as _); // args_rets[i] + let dst_loc = Machine::get_param_location(1 + i, calling_convention); + + match dst_loc { + Location::GPR(_) => { + a.emit_mov(Size::S64, src_loc, dst_loc); + } + Location::Memory(_, _) => { + // This location is for reading arguments but we are writing arguments here. + // So recalculate it. + a.emit_mov(Size::S64, src_loc, Location::GPR(GPR::RAX)); + a.emit_mov( + Size::S64, + Location::GPR(GPR::RAX), + Location::Memory( + GPR::RSP, + (stack_padding as usize + n_stack_args * 8) as _, + ), + ); + n_stack_args += 1; + } + _ => unreachable!(), + } + } + } + + // Call. + a.emit_call_location(Location::GPR(GPR::R15)); + + // Restore stack. + a.emit_add(Size::S64, Location::Imm32(stack_offset + stack_padding), Location::GPR(GPR::RSP)); + + // Write return value. + if !sig.results().is_empty() { + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), Location::Memory(GPR::R14, 0)); + } + + // Restore callee-saved registers. + a.emit_pop(Size::S64, Location::GPR(GPR::R14)); + a.emit_pop(Size::S64, Location::GPR(GPR::R15)); + + a.emit_ret(); + FunctionBody { body: a.drain().unwrap().collect() } +} + +/// Generates dynamic import function call trampoline for a function type. +#[tracing::instrument(target = "unc_vm", level = "trace", skip(vmoffsets))] +pub(crate) fn gen_std_dynamic_import_trampoline( + vmoffsets: &VMOffsets, + sig: &FunctionType, + calling_convention: CallingConvention, + a: &mut Assembler, +) -> FunctionBody { + // Allocate argument array. + let stack_offset: usize = 16 * std::cmp::max(sig.params().len(), sig.results().len()) + 8; // 16 bytes each + 8 bytes sysv call padding + let stack_padding: usize = match calling_convention { + CallingConvention::WindowsFastcall => 32, + _ => 0, + }; + a.emit_sub( + Size::S64, + Location::Imm32((stack_offset + stack_padding) as _), + Location::GPR(GPR::RSP), + ); + + // Copy arguments. + if !sig.params().is_empty() { + let mut argalloc = ArgumentRegisterAllocator::default(); + argalloc.next(Type::I64, calling_convention).unwrap(); // skip VMContext + + let mut stack_param_count: usize = 0; + + for (i, ty) in sig.params().iter().enumerate() { + let source_loc = match argalloc.next(*ty, calling_convention) { + Some(X64Register::GPR(gpr)) => Location::GPR(gpr), + Some(X64Register::XMM(xmm)) => Location::XMM(xmm), + None => { + a.emit_mov( + Size::S64, + Location::Memory( + GPR::RSP, + (stack_padding * 2 + stack_offset + 8 + stack_param_count * 8) as _, + ), + Location::GPR(GPR::RAX), + ); + stack_param_count += 1; + Location::GPR(GPR::RAX) + } + }; + a.emit_mov( + Size::S64, + source_loc, + Location::Memory(GPR::RSP, (stack_padding + i * 16) as _), + ); + + // Zero upper 64 bits. + a.emit_mov( + Size::S64, + Location::Imm32(0), + Location::Memory(GPR::RSP, (stack_padding + i * 16 + 8) as _), + ); + } + } + + match calling_convention { + CallingConvention::WindowsFastcall => { + // Load target address. + a.emit_mov( + Size::S64, + Location::Memory( + GPR::RCX, + vmoffsets.vmdynamicfunction_import_context_address() as i32, + ), + Location::GPR(GPR::RAX), + ); + // Load values array. + a.emit_lea( + Size::S64, + Location::Memory(GPR::RSP, stack_padding as i32), + Location::GPR(GPR::RDX), + ); + } + _ => { + // Load target address. + a.emit_mov( + Size::S64, + Location::Memory( + GPR::RDI, + vmoffsets.vmdynamicfunction_import_context_address() as i32, + ), + Location::GPR(GPR::RAX), + ); + // Load values array. + a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RSI)); + } + }; + + // Call target. + a.emit_call_location(Location::GPR(GPR::RAX)); + + // Fetch return value. + if !sig.results().is_empty() { + assert_eq!(sig.results().len(), 1); + a.emit_mov( + Size::S64, + Location::Memory(GPR::RSP, stack_padding as i32), + Location::GPR(GPR::RAX), + ); + } + + // Release values array. + a.emit_add( + Size::S64, + Location::Imm32((stack_offset + stack_padding) as _), + Location::GPR(GPR::RSP), + ); + + // Return. + a.emit_ret(); + + FunctionBody { body: a.drain().unwrap().collect() } +} + +// Singlepass calls import functions through a trampoline. +#[tracing::instrument(target = "unc_vm", level = "trace", skip(vmoffsets))] +pub(crate) fn gen_import_call_trampoline( + vmoffsets: &VMOffsets, + index: FunctionIndex, + sig: &FunctionType, + calling_convention: CallingConvention, + a: &mut Assembler, +) -> CustomSection { + // TODO: ARM entry trampoline is not emitted. + + // Singlepass internally treats all arguments as integers + // For the standard Windows calling convention requires + // floating point arguments to be passed in XMM registers for the 4 first arguments only + // That's the only change to do, other arguments are not to be changed + // For the standard System V calling convention requires + // floating point arguments to be passed in XMM registers. + // Translation is expensive, so only do it if needed. + if sig.params().iter().any(|&x| x == Type::F32 || x == Type::F64) { + match calling_convention { + CallingConvention::WindowsFastcall => { + let mut param_locations: Vec = vec![]; + for i in 0..sig.params().len() { + let loc = match i { + 0..=2 => { + static PARAM_REGS: &[GPR] = &[GPR::RDX, GPR::R8, GPR::R9]; + Location::GPR(PARAM_REGS[i]) + } + _ => Location::Memory(GPR::RSP, 32 + 8 + ((i - 3) * 8) as i32), // will not be used anyway + }; + param_locations.push(loc); + } + // Copy Float arguments to XMM from GPR. + let mut argalloc = ArgumentRegisterAllocator::default(); + for (i, ty) in sig.params().iter().enumerate() { + let prev_loc = param_locations[i]; + match argalloc.next(*ty, calling_convention) { + Some(X64Register::GPR(_gpr)) => continue, + Some(X64Register::XMM(xmm)) => { + a.emit_mov(Size::S64, prev_loc, Location::XMM(xmm)) + } + None => continue, + }; + } + } + _ => { + let mut param_locations: Vec = vec![]; + + // Allocate stack space for arguments. + let stack_offset: i32 = + if sig.params().len() > 5 { 5 * 8 } else { (sig.params().len() as i32) * 8 }; + if stack_offset > 0 { + a.emit_sub( + Size::S64, + Location::Imm32(stack_offset as u32), + Location::GPR(GPR::RSP), + ); + } + + // Store all arguments to the stack to prevent overwrite. + for i in 0..sig.params().len() { + let loc = match i { + 0..=4 => { + static PARAM_REGS: &[GPR] = + &[GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9]; + let loc = Location::Memory(GPR::RSP, (i * 8) as i32); + a.emit_mov(Size::S64, Location::GPR(PARAM_REGS[i]), loc); + loc + } + _ => Location::Memory(GPR::RSP, stack_offset + 8 + ((i - 5) * 8) as i32), + }; + param_locations.push(loc); + } + + // Copy arguments. + let mut argalloc = ArgumentRegisterAllocator::default(); + argalloc.next(Type::I64, calling_convention).unwrap(); // skip VMContext + let mut caller_stack_offset: i32 = 0; + for (i, ty) in sig.params().iter().enumerate() { + let prev_loc = param_locations[i]; + let targ = match argalloc.next(*ty, calling_convention) { + Some(X64Register::GPR(gpr)) => Location::GPR(gpr), + Some(X64Register::XMM(xmm)) => Location::XMM(xmm), + None => { + // No register can be allocated. Put this argument on the stack. + // + // Since here we never use fewer registers than by the original call, on the caller's frame + // we always have enough space to store the rearranged arguments, and the copy "backward" between different + // slots in the caller argument region will always work. + a.emit_mov(Size::S64, prev_loc, Location::GPR(GPR::RAX)); + a.emit_mov( + Size::S64, + Location::GPR(GPR::RAX), + Location::Memory(GPR::RSP, stack_offset + 8 + caller_stack_offset), + ); + caller_stack_offset += 8; + continue; + } + }; + a.emit_mov(Size::S64, prev_loc, targ); + } + + // Restore stack pointer. + if stack_offset > 0 { + a.emit_add( + Size::S64, + Location::Imm32(stack_offset as u32), + Location::GPR(GPR::RSP), + ); + } + } + } + } + + // Emits a tail call trampoline that loads the address of the target import function + // from Ctx and jumps to it. + + let body_offset = vmoffsets.vmctx_vmfunction_import_body(index); + let vmctx_offset = vmoffsets.vmctx_vmfunction_import_vmctx(index); + + match calling_convention { + CallingConvention::WindowsFastcall => { + a.emit_mov( + Size::S64, + Location::Memory(GPR::RCX, body_offset as i32), // function pointer + Location::GPR(GPR::RAX), + ); + a.emit_mov( + Size::S64, + Location::Memory(GPR::RCX, vmctx_offset as i32), // target vmctx + Location::GPR(GPR::RCX), + ); + } + _ => { + a.emit_mov( + Size::S64, + Location::Memory(GPR::RDI, body_offset as i32), // function pointer + Location::GPR(GPR::RAX), + ); + a.emit_mov( + Size::S64, + Location::Memory(GPR::RDI, vmctx_offset as i32), // target vmctx + Location::GPR(GPR::RDI), + ); + } + } + a.emit_host_redirection(GPR::RAX); + + CustomSection { + protection: CustomSectionProtection::ReadExecute, + bytes: SectionBody::new_with_vec(a.drain().unwrap().collect()), + relocations: vec![], + } +} + +// Constants for the bounds of truncation operations. These are the least or +// greatest exact floats in either f32 or f64 representation less-than (for +// least) or greater-than (for greatest) the i32 or i64 or u32 or u64 +// min (for least) or max (for greatest), when rounding towards zero. + +/// Greatest Exact Float (32 bits) less-than i32::MIN when rounding towards zero. +const GEF32_LT_I32_MIN: f32 = -2147483904.0; +/// Least Exact Float (32 bits) greater-than i32::MAX when rounding towards zero. +const LEF32_GT_I32_MAX: f32 = 2147483648.0; +/// Greatest Exact Float (32 bits) less-than i64::MIN when rounding towards zero. +const GEF32_LT_I64_MIN: f32 = -9223373136366403584.0; +/// Least Exact Float (32 bits) greater-than i64::MAX when rounding towards zero. +const LEF32_GT_I64_MAX: f32 = 9223372036854775808.0; +/// Greatest Exact Float (32 bits) less-than u32::MIN when rounding towards zero. +const GEF32_LT_U32_MIN: f32 = -1.0; +/// Least Exact Float (32 bits) greater-than u32::MAX when rounding towards zero. +const LEF32_GT_U32_MAX: f32 = 4294967296.0; +/// Greatest Exact Float (32 bits) less-than u64::MIN when rounding towards zero. +const GEF32_LT_U64_MIN: f32 = -1.0; +/// Least Exact Float (32 bits) greater-than u64::MAX when rounding towards zero. +const LEF32_GT_U64_MAX: f32 = 18446744073709551616.0; + +/// Greatest Exact Float (64 bits) less-than i32::MIN when rounding towards zero. +const GEF64_LT_I32_MIN: f64 = -2147483649.0; +/// Least Exact Float (64 bits) greater-than i32::MAX when rounding towards zero. +const LEF64_GT_I32_MAX: f64 = 2147483648.0; +/// Greatest Exact Float (64 bits) less-than i64::MIN when rounding towards zero. +const GEF64_LT_I64_MIN: f64 = -9223372036854777856.0; +/// Least Exact Float (64 bits) greater-than i64::MAX when rounding towards zero. +const LEF64_GT_I64_MAX: f64 = 9223372036854775808.0; +/// Greatest Exact Float (64 bits) less-than u32::MIN when rounding towards zero. +const GEF64_LT_U32_MIN: f64 = -1.0; +/// Least Exact Float (64 bits) greater-than u32::MAX when rounding towards zero. +const LEF64_GT_U32_MAX: f64 = 4294967296.0; +/// Greatest Exact Float (64 bits) less-than u64::MIN when rounding towards zero. +const GEF64_LT_U64_MIN: f64 = -1.0; +/// Least Exact Float (64 bits) greater-than u64::MAX when rounding towards zero. +const LEF64_GT_U64_MAX: f64 = 18446744073709551616.0; diff --git a/runtime/unc-vm/compiler-singlepass/src/compiler.rs b/runtime/unc-vm/compiler-singlepass/src/compiler.rs new file mode 100644 index 000000000..2a94fa76f --- /dev/null +++ b/runtime/unc-vm/compiler-singlepass/src/compiler.rs @@ -0,0 +1,296 @@ +//! Support for compiling with Singlepass. +// Allow unused imports while developing. +#![allow(unused_imports, dead_code)] + +use crate::codegen_x64::{ + gen_import_call_trampoline, gen_std_dynamic_import_trampoline, gen_std_trampoline, + CodegenError, FuncGen, +}; +use crate::config::Singlepass; +use unc_vm_compiler::{ + Architecture, CallingConvention, Compilation, CompileError, CompileModuleInfo, + CompiledFunction, Compiler, CompilerConfig, CpuFeature, FunctionBody, FunctionBodyData, + ModuleTranslationState, OperatingSystem, SectionIndex, Target, TrapInformation, +}; +use unc_vm_types::entity::{EntityRef, PrimaryMap}; +use unc_vm_types::{ + FunctionIndex, FunctionType, LocalFunctionIndex, MemoryIndex, ModuleInfo, TableIndex, +}; +use unc_vm_vm::{TrapCode, VMOffsets}; +use rayon::prelude::{IntoParallelIterator, ParallelIterator}; +use std::sync::Arc; + +/// A compiler that compiles a WebAssembly module with Singlepass. +/// It does the compilation in one pass +pub struct SinglepassCompiler { + config: Singlepass, +} + +impl SinglepassCompiler { + /// Creates a new Singlepass compiler + pub fn new(config: Singlepass) -> Self { + Self { config } + } + + /// Gets the config for this Compiler + fn config(&self) -> &Singlepass { + &self.config + } +} + +impl Compiler for SinglepassCompiler { + /// Compile the module using Singlepass, producing a compilation result with + /// associated relocations. + #[tracing::instrument(target = "unc_vm", level = "info", skip_all)] + fn compile_module( + &self, + target: &Target, + compile_info: &CompileModuleInfo, + function_body_inputs: PrimaryMap>, + tunables: &dyn unc_vm_vm::Tunables, + instrumentation: &finite_wasm::AnalysisOutcome, + ) -> Result { + /*if target.triple().operating_system == OperatingSystem::Windows { + return Err(CompileError::UnsupportedTarget( + OperatingSystem::Windows.to_string(), + )); + }*/ + if target.triple().architecture != Architecture::X86_64 { + return Err(CompileError::UnsupportedTarget(target.triple().architecture.to_string())); + } + if !target.cpu_features().contains(CpuFeature::AVX) { + return Err(CompileError::UnsupportedTarget("x86_64 without AVX".to_string())); + } + if compile_info.features.multi_value { + return Err(CompileError::UnsupportedFeature("multivalue".to_string())); + } + let calling_convention = match target.triple().default_calling_convention() { + Ok(CallingConvention::WindowsFastcall) => CallingConvention::WindowsFastcall, + Ok(CallingConvention::SystemV) => CallingConvention::SystemV, + //Ok(CallingConvention::AppleAarch64) => AppleAarch64, + _ => panic!("Unsupported Calling convention for Singlepass compiler"), + }; + + let table_styles = &compile_info.table_styles; + let module = &compile_info.module; + let pointer_width = target + .triple() + .pointer_width() + .map_err(|()| { + CompileError::UnsupportedTarget("target with unknown pointer width".into()) + })? + .bytes(); + let vmoffsets = VMOffsets::new(pointer_width).with_module_info(&module); + let make_assembler = || { + const KB: usize = 1024; + dynasmrt::VecAssembler::new_with_capacity(0, 128 * KB, 0, 0, KB, 0, KB) + }; + let import_idxs = 0..module.import_counts.functions as usize; + let import_trampolines: PrimaryMap = + tracing::debug_span!(target: "unc_vm", "import_trampolines", n_imports = import_idxs.len()).in_scope( + || { + import_idxs + .into_par_iter() + .map_init(make_assembler, |assembler, i| { + let i = FunctionIndex::new(i); + gen_import_call_trampoline( + &vmoffsets, + i, + &module.signatures[module.functions[i]], + calling_convention, + assembler, + ) + }) + .collect::>() + .into_iter() + .collect() + }, + ); + let functions = function_body_inputs + .iter() + .collect::)>>() + .into_par_iter() + .map_init(make_assembler, |assembler, (i, input)| { + tracing::debug_span!(target: "unc_vm", "function", i = i.index()).in_scope(|| { + let reader = + unc_vm_compiler::FunctionReader::new(input.module_offset, input.data); + let stack_init_gas_cost = tunables + .stack_init_gas_cost(instrumentation.function_frame_sizes[i.index()]); + let stack_size = instrumentation.function_frame_sizes[i.index()] + .checked_add(instrumentation.function_operand_stack_sizes[i.index()]) + .ok_or_else(|| { + CompileError::Codegen(String::from( + "got function with frame size going beyond u64::MAX", + )) + })?; + let mut generator = FuncGen::new( + assembler, + module, + &self.config, + &target, + &vmoffsets, + &table_styles, + i, + calling_convention, + stack_init_gas_cost, + &instrumentation.gas_offsets[i.index()], + &instrumentation.gas_costs[i.index()], + &instrumentation.gas_kinds[i.index()], + stack_size, + ) + .map_err(to_compile_error)?; + + let mut local_reader = reader.get_locals_reader()?; + for _ in 0..local_reader.get_count() { + let (count, ty) = local_reader.read()?; + // Overflows feeding a local here have most likely already been caught by the + // validator, but it is possible that the validator hasn't been run at all, or + // that the validator does not impose any limits on the number of locals. + generator.feed_local(count, ty); + } + + generator.emit_head().map_err(to_compile_error)?; + + let mut operator_reader = + reader.get_operators_reader()?.into_iter_with_offsets(); + while generator.has_control_frames() { + let (op, pos) = + tracing::trace_span!(target: "unc_vm", "parsing-next-operator") + .in_scope(|| operator_reader.next().unwrap())?; + generator.set_srcloc(pos as u32); + generator.feed_operator(op).map_err(to_compile_error)?; + } + + Ok(generator.finalize(&input)) + }) + }) + .collect::, CompileError>>()? + .into_iter() // TODO: why not just collect to PrimaryMap directly? + .collect::>(); + + let function_call_trampolines = + tracing::debug_span!(target: "unc_vm", "function_call_trampolines").in_scope(|| { + module + .signatures + .values() + .collect::>() + .into_par_iter() + .map_init(make_assembler, |assembler, func_type| { + gen_std_trampoline(&func_type, calling_convention, assembler) + }) + .collect::>() + .into_iter() + .collect::>() + }); + + let dynamic_function_trampolines = + tracing::debug_span!(target: "unc_vm", "dynamic_function_trampolines").in_scope( + || { + module + .imported_function_types() + .collect::>() + .into_par_iter() + .map_init(make_assembler, |assembler, func_type| { + gen_std_dynamic_import_trampoline( + &vmoffsets, + &func_type, + calling_convention, + assembler, + ) + }) + .collect::>() + .into_iter() + .collect::>() + }, + ); + + Ok(Compilation { + functions, + custom_sections: import_trampolines, + function_call_trampolines, + dynamic_function_trampolines, + debug: None, + trampolines: None, + }) + } +} + +trait ToCompileError { + fn to_compile_error(self) -> CompileError; +} + +impl ToCompileError for CodegenError { + fn to_compile_error(self) -> CompileError { + CompileError::Codegen(self.message) + } +} + +fn to_compile_error(x: T) -> CompileError { + x.to_compile_error() +} + +#[cfg(test)] +mod tests { + use super::*; + use unc_vm_compiler::{CpuFeature, Features, Triple}; + use unc_vm_vm::{MemoryStyle, TableStyle}; + use std::str::FromStr; + use target_lexicon::triple; + + fn dummy_compilation_ingredients<'a>() -> ( + CompileModuleInfo, + PrimaryMap>, + finite_wasm::AnalysisOutcome, + ) { + let compile_info = CompileModuleInfo { + features: Features::new(), + module: Arc::new(ModuleInfo::new()), + memory_styles: PrimaryMap::::new(), + table_styles: PrimaryMap::::new(), + }; + let function_body_inputs = PrimaryMap::>::new(); + let analysis = finite_wasm::AnalysisOutcome { + function_frame_sizes: Vec::new(), + function_operand_stack_sizes: Vec::new(), + gas_offsets: Vec::new(), + gas_costs: Vec::new(), + gas_kinds: Vec::new(), + }; + (compile_info, function_body_inputs, analysis) + } + + #[test] + fn errors_for_unsupported_targets() { + let compiler = SinglepassCompiler::new(Singlepass::default()); + + // Compile for 32bit Linux + let linux32 = Target::new(triple!("i686-unknown-linux-gnu"), CpuFeature::for_host()); + let (mut info, inputs, analysis) = dummy_compilation_ingredients(); + let result = compiler.compile_module( + &linux32, + &mut info, + inputs, + &unc_vm_vm::TestTunables, + &analysis, + ); + match result.unwrap_err() { + CompileError::UnsupportedTarget(name) => assert_eq!(name, "i686"), + error => panic!("Unexpected error: {:?}", error), + }; + + // Compile for win32 + let win32 = Target::new(triple!("i686-pc-windows-gnu"), CpuFeature::for_host()); + let (mut info, inputs, analysis) = dummy_compilation_ingredients(); + let result = compiler.compile_module( + &win32, + &mut info, + inputs, + &unc_vm_vm::TestTunables, + &analysis, + ); + match result.unwrap_err() { + CompileError::UnsupportedTarget(name) => assert_eq!(name, "i686"), // Windows should be checked before architecture + error => panic!("Unexpected error: {:?}", error), + }; + } +} diff --git a/runtime/unc-vm/compiler-singlepass/src/config.rs b/runtime/unc-vm/compiler-singlepass/src/config.rs new file mode 100644 index 000000000..aa179d0c5 --- /dev/null +++ b/runtime/unc-vm/compiler-singlepass/src/config.rs @@ -0,0 +1,108 @@ +// Allow unused imports while developing +#![allow(unused_imports, dead_code)] + +use crate::compiler::SinglepassCompiler; +use crate::emitter_x64::Location; +use unc_vm_compiler::{Compiler, CompilerConfig, CpuFeature, Target}; +use unc_vm_types::{Features, FunctionType, Type}; +use smallvec::SmallVec; +use std::sync::Arc; + +#[derive(Debug, Clone)] +pub(crate) enum IntrinsicKind { + Gas, +} + +#[derive(Debug, Clone)] +pub(crate) struct Intrinsic { + pub(crate) kind: IntrinsicKind, + pub(crate) name: String, + pub(crate) signature: FunctionType, +} + +#[derive(Debug, Clone)] +pub struct Singlepass { + pub(crate) enable_nan_canonicalization: bool, + pub(crate) enable_stack_check: bool, + pub(crate) disable_9393_fix: bool, + /// Compiler intrinsics. + pub(crate) intrinsics: Vec, +} + +impl Singlepass { + /// Creates a new configuration object with the default configuration + /// specified. + pub fn new() -> Self { + Self { + enable_nan_canonicalization: true, + enable_stack_check: false, + disable_9393_fix: false, + intrinsics: vec![Intrinsic { + kind: IntrinsicKind::Gas, + name: "gas".to_string(), + signature: ([Type::I32], []).into(), + }], + } + } + + /// Enable stack check. + /// + /// When enabled, an explicit stack depth check will be performed on entry + /// to each function to prevent stack overflow. + /// + /// Note that this doesn't guarantee deterministic execution across + /// different platforms. + pub fn enable_stack_check(&mut self, enable: bool) -> &mut Self { + self.enable_stack_check = enable; + self + } + + fn enable_nan_canonicalization(&mut self) { + self.enable_nan_canonicalization = true; + } + + pub fn set_9393_fix(&mut self, enable: bool) { + self.disable_9393_fix = !enable; + } + + pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self { + self.enable_nan_canonicalization = enable; + self + } +} + +impl CompilerConfig for Singlepass { + fn enable_pic(&mut self) { + // Do nothing, since singlepass already emits + // PIC code. + } + + /// Transform it into the compiler + fn compiler(self: Box) -> Box { + Box::new(SinglepassCompiler::new(*self)) + } + + /// Gets the default features for this compiler in the given target + fn default_features_for_target(&self, _target: &Target) -> Features { + let mut features = Features::default(); + features.multi_value(false); + features + } +} + +impl Default for Singlepass { + fn default() -> Singlepass { + Self::new() + } +} + +impl Intrinsic { + pub(crate) fn is_params_ok(&self, params: &SmallVec<[Location; 8]>) -> bool { + match self.kind { + IntrinsicKind::Gas => match params[0] { + Location::Imm32(value) => value < i32::MAX as u32, + _ => false, + }, + } + } +} diff --git a/runtime/unc-vm/compiler-singlepass/src/emitter_x64.rs b/runtime/unc-vm/compiler-singlepass/src/emitter_x64.rs new file mode 100644 index 000000000..ec376a8a3 --- /dev/null +++ b/runtime/unc-vm/compiler-singlepass/src/emitter_x64.rs @@ -0,0 +1,1435 @@ +pub(crate) use crate::x64_decl::{GPR, XMM}; +use dynasm::dynasm; +use dynasmrt::{ + x64::X64Relocation, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, VecAssembler, +}; +use enumset::EnumSet; +use unc_vm_compiler::CpuFeature; + +type Assembler = VecAssembler; + +/// Force `dynasm!` to use the correct arch (x64) when cross-compiling. +/// `dynasm!` proc-macro tries to auto-detect it by default by looking at the +/// `target_arch`, but it sees the `target_arch` of the proc-macro itself, which +/// is always equal to host, even when cross-compiling. +macro_rules! dynasm { + ($a:expr ; $($tt:tt)*) => { + dynasm::dynasm!( + $a + ; .arch x64 + ; $($tt)* + ) + }; +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub(crate) enum Location { + Imm8(u8), + Imm32(u32), + Imm64(u64), + // Imm128(u128), + GPR(GPR), + XMM(XMM), + Memory(GPR, i32), + MemoryAddTriple(GPR, GPR, i32), +} + +#[allow(dead_code)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub(crate) enum Condition { + None, + Above, + AboveEqual, + Below, + BelowEqual, + Greater, + GreaterEqual, + Less, + LessEqual, + Equal, + NotEqual, + Signed, + Carry, + Overflow, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub(crate) enum Size { + S8, + S16, + S32, + S64, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub(crate) enum XMMOrMemory { + XMM(XMM), + Memory(GPR, i32), +} + +#[derive(Copy, Clone, Debug)] +#[allow(dead_code)] +pub(crate) enum GPROrMemory { + GPR(GPR), + Memory(GPR, i32), +} + +pub(crate) trait Emitter { + type Label; + type Offset; + + fn get_label(&mut self) -> Self::Label; + fn get_offset(&self) -> Self::Offset; + fn get_jmp_instr_size(&self) -> u8; + + fn finalize_function(&mut self) {} + + fn emit_u64(&mut self, x: u64); + fn emit_bytes(&mut self, bytes: &[u8]); + + fn emit_label(&mut self, label: Self::Label); + + fn emit_nop(&mut self); + + /// A high-level assembler method. Emits an instruction sequence of length `n` that is functionally + /// equivalent to a `nop` instruction, without guarantee about the underlying implementation. + fn emit_nop_n(&mut self, n: usize); + + fn emit_mov(&mut self, sz: Size, src: Location, dst: Location); + fn emit_lea(&mut self, sz: Size, src: Location, dst: Location); + fn emit_lea_label(&mut self, label: Self::Label, dst: Location); + fn emit_cdq(&mut self); + fn emit_cqo(&mut self); + fn emit_xor(&mut self, sz: Size, src: Location, dst: Location); + fn emit_jmp(&mut self, condition: Condition, label: Self::Label); + fn emit_jmp_location(&mut self, loc: Location); + fn emit_set(&mut self, condition: Condition, dst: GPR); + fn emit_push(&mut self, sz: Size, src: Location); + fn emit_pop(&mut self, sz: Size, dst: Location); + fn emit_cmp(&mut self, sz: Size, left: Location, right: Location); + fn emit_add(&mut self, sz: Size, src: Location, dst: Location); + fn emit_sub(&mut self, sz: Size, src: Location, dst: Location); + fn emit_neg(&mut self, sz: Size, value: Location); + fn emit_imul(&mut self, sz: Size, src: Location, dst: Location); + fn emit_imul_imm32_gpr64(&mut self, src: u32, dst: GPR); + fn emit_div(&mut self, sz: Size, divisor: Location); + fn emit_idiv(&mut self, sz: Size, divisor: Location); + fn emit_shl(&mut self, sz: Size, src: Location, dst: Location); + fn emit_shr(&mut self, sz: Size, src: Location, dst: Location); + fn emit_sar(&mut self, sz: Size, src: Location, dst: Location); + fn emit_rol(&mut self, sz: Size, src: Location, dst: Location); + fn emit_ror(&mut self, sz: Size, src: Location, dst: Location); + fn emit_and(&mut self, sz: Size, src: Location, dst: Location); + fn emit_or(&mut self, sz: Size, src: Location, dst: Location); + fn emit_bsr(&mut self, sz: Size, src: Location, dst: Location); + fn emit_bsf(&mut self, sz: Size, src: Location, dst: Location); + fn emit_popcnt(&mut self, sz: Size, src: Location, dst: Location); + fn emit_movzx(&mut self, sz_src: Size, src: Location, sz_dst: Size, dst: Location); + fn emit_movsx(&mut self, sz_src: Size, src: Location, sz_dst: Size, dst: Location); + fn emit_xchg(&mut self, sz: Size, src: Location, dst: Location); + fn emit_lock_xadd(&mut self, sz: Size, src: Location, dst: Location); + fn emit_lock_cmpxchg(&mut self, sz: Size, src: Location, dst: Location); + fn emit_rep_stosq(&mut self); + + fn emit_btc_gpr_imm8_32(&mut self, src: u8, dst: GPR); + fn emit_btc_gpr_imm8_64(&mut self, src: u8, dst: GPR); + + fn emit_cmovae_gpr_32(&mut self, src: GPR, dst: GPR); + fn emit_cmovae_gpr_64(&mut self, src: GPR, dst: GPR); + + fn emit_vmovaps(&mut self, src: XMMOrMemory, dst: XMMOrMemory); + fn emit_vmovapd(&mut self, src: XMMOrMemory, dst: XMMOrMemory); + fn emit_vxorps(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vxorpd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vaddss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vaddsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vsubss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vsubsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vmulss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vmulsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vdivss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vdivsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vmaxss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vmaxsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vminss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vminsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpeqss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpeqsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpneqss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpneqsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpltss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpltsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpless(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmplesd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpgtss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpgtsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpgess(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpgesd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpunordss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpunordsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcmpordss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcmpordsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vsqrtss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vsqrtsd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vroundss_nearest(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundss_floor(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundss_ceil(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundss_trunc(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundsd_nearest(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundsd_floor(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundsd_ceil(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vroundsd_trunc(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_vcvtss2sd(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + fn emit_vcvtsd2ss(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM); + + fn emit_ucomiss(&mut self, src: XMMOrMemory, dst: XMM); + fn emit_ucomisd(&mut self, src: XMMOrMemory, dst: XMM); + + fn emit_cvttss2si_32(&mut self, src: XMMOrMemory, dst: GPR); + fn emit_cvttss2si_64(&mut self, src: XMMOrMemory, dst: GPR); + fn emit_cvttsd2si_32(&mut self, src: XMMOrMemory, dst: GPR); + fn emit_cvttsd2si_64(&mut self, src: XMMOrMemory, dst: GPR); + + fn emit_vcvtsi2ss_32(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM); + fn emit_vcvtsi2ss_64(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM); + fn emit_vcvtsi2sd_32(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM); + fn emit_vcvtsi2sd_64(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM); + + fn emit_vblendvps(&mut self, src1: XMM, src2: XMMOrMemory, mask: XMM, dst: XMM); + fn emit_vblendvpd(&mut self, src1: XMM, src2: XMMOrMemory, mask: XMM, dst: XMM); + + fn emit_test_gpr_64(&mut self, reg: GPR); + + fn emit_ud2(&mut self); + fn emit_ret(&mut self); + fn emit_call_label(&mut self, label: Self::Label); + fn emit_call_location(&mut self, loc: Location); + + fn emit_call_register(&mut self, reg: GPR); + + fn emit_bkpt(&mut self); + + fn emit_host_redirection(&mut self, target: GPR); + + fn arch_has_itruncf(&self) -> bool { + false + } + fn arch_emit_i32_trunc_sf32(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + fn arch_emit_i32_trunc_sf64(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + fn arch_emit_i32_trunc_uf32(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + fn arch_emit_i32_trunc_uf64(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + fn arch_emit_i64_trunc_sf32(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + fn arch_emit_i64_trunc_sf64(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + fn arch_emit_i64_trunc_uf32(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + fn arch_emit_i64_trunc_uf64(&mut self, _src: XMM, _dst: GPR) { + unimplemented!() + } + + fn arch_has_fconverti(&self) -> bool { + false + } + fn arch_emit_f32_convert_si32(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f32_convert_si64(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f32_convert_ui32(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f32_convert_ui64(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f64_convert_si32(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f64_convert_si64(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f64_convert_ui32(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f64_convert_ui64(&mut self, _src: GPR, _dst: XMM) { + unimplemented!() + } + + fn arch_has_fneg(&self) -> bool { + false + } + fn arch_emit_f32_neg(&mut self, _src: XMM, _dst: XMM) { + unimplemented!() + } + fn arch_emit_f64_neg(&mut self, _src: XMM, _dst: XMM) { + unimplemented!() + } + + fn arch_has_xzcnt(&self, _cpu: &EnumSet) -> bool { + false + } + fn arch_emit_lzcnt(&mut self, _sz: Size, _src: Location, _dst: Location) { + unimplemented!() + } + fn arch_emit_tzcnt(&mut self, _sz: Size, _src: Location, _dst: Location) { + unimplemented!() + } + + fn arch_supports_canonicalize_nan(&self) -> bool { + true + } + + fn arch_requires_indirect_call_trampoline(&self) -> bool { + false + } + + fn arch_emit_indirect_call_with_trampoline(&mut self, _loc: Location) { + unimplemented!() + } + + // Emits entry trampoline just before the real function. + fn arch_emit_entry_trampoline(&mut self) {} + + // Byte offset from the beginning of a `mov Imm64, GPR` instruction to the imm64 value. + // Required to support emulation on Aarch64. + fn arch_mov64_imm_offset(&self) -> usize { + unimplemented!() + } +} + +macro_rules! unop_gpr { + ($ins:ident, $assembler:tt, $sz:expr, $loc:expr, $otherwise:block) => { + match ($sz, $loc) { + (Size::S32, Location::GPR(loc)) => { + dynasm!($assembler ; $ins Rd(loc as u8)); + }, + (Size::S64, Location::GPR(loc)) => { + dynasm!($assembler ; $ins Rq(loc as u8)); + }, + _ => $otherwise + } + }; +} + +macro_rules! unop_mem { + ($ins:ident, $assembler:tt, $sz:expr, $loc:expr, $otherwise:block) => { + match ($sz, $loc) { + (Size::S32, Location::Memory(loc, disp)) => { + dynasm!($assembler ; $ins DWORD [Rq(loc as u8) + disp] ); + }, + (Size::S64, Location::Memory(loc, disp)) => { + dynasm!($assembler ; $ins QWORD [Rq(loc as u8) + disp] ); + }, + _ => $otherwise + } + }; +} + +macro_rules! unop_gpr_or_mem { + ($ins:ident, $assembler:tt, $sz:expr, $loc:expr, $otherwise:block) => { + unop_gpr!($ins, $assembler, $sz, $loc, { + unop_mem!($ins, $assembler, $sz, $loc, $otherwise) + }) + }; +} + +macro_rules! binop_imm32_gpr { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::Imm32(src), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rd(dst as u8), src as i32); // IMM32_2GPR + }, + (Size::S64, Location::Imm32(src), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), src as i32); // IMM32_2GPR + }, + _ => $otherwise + } + }; +} + +macro_rules! binop_imm32_mem { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::Imm32(src), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins DWORD [Rq(dst as u8) + disp], src as i32); + }, + (Size::S64, Location::Imm32(src), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins QWORD [Rq(dst as u8) + disp], src as i32); + }, + _ => $otherwise + } + }; +} + +macro_rules! binop_imm64_gpr { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S64, Location::Imm64(src), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), QWORD src as i64); // IMM32_2GPR + }, + _ => $otherwise + } + }; +} + +macro_rules! binop_gpr_gpr { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::GPR(src), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rd(dst as u8), Rd(src as u8)); // GPR2GPR + }, + (Size::S64, Location::GPR(src), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), Rq(src as u8)); // GPR2GPR + }, + _ => $otherwise + } + }; +} + +macro_rules! binop_gpr_mem { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins [Rq(dst as u8) + disp], Rd(src as u8)); // GPR2MEM + }, + (Size::S64, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins [Rq(dst as u8) + disp], Rq(src as u8)); // GPR2MEM + }, + _ => $otherwise + } + }; +} + +macro_rules! binop_mem_gpr { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rd(dst as u8), [Rq(src as u8) + disp]); // MEM2GPR + }, + (Size::S64, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), [Rq(src as u8) + disp]); // MEM2GPR + }, + _ => $otherwise + } + }; +} + +macro_rules! binop_all_nofp { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + binop_imm32_gpr!($ins, $assembler, $sz, $src, $dst, { + binop_imm32_mem!($ins, $assembler, $sz, $src, $dst, { + binop_gpr_gpr!($ins, $assembler, $sz, $src, $dst, { + binop_gpr_mem!($ins, $assembler, $sz, $src, $dst, { + binop_mem_gpr!($ins, $assembler, $sz, $src, $dst, $otherwise) + }) + }) + }) + }) + }; +} + +macro_rules! binop_shift { + ($ins:ident, $assembler:tt, $sz:expr, $src:expr, $dst:expr, $otherwise:block) => { + match ($sz, $src, $dst) { + (Size::S32, Location::GPR(GPR::RCX), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rd(dst as u8), cl); + }, + (Size::S32, Location::GPR(GPR::RCX), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins DWORD [Rq(dst as u8) + disp], cl); + }, + (Size::S32, Location::Imm8(imm), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rd(dst as u8), imm as i8); + }, + (Size::S32, Location::Imm8(imm), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins DWORD [Rq(dst as u8) + disp], imm as i8); + }, + (Size::S64, Location::GPR(GPR::RCX), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), cl); + }, + (Size::S64, Location::GPR(GPR::RCX), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins QWORD [Rq(dst as u8) + disp], cl); + }, + (Size::S64, Location::Imm8(imm), Location::GPR(dst)) => { + dynasm!($assembler ; $ins Rq(dst as u8), imm as i8); + }, + (Size::S64, Location::Imm8(imm), Location::Memory(dst, disp)) => { + dynasm!($assembler ; $ins QWORD [Rq(dst as u8) + disp], imm as i8); + }, + _ => $otherwise + } + } +} + +macro_rules! jmp_op { + ($ins:ident, $assembler:tt, $label:ident) => { + dynasm!($assembler ; $ins =>$label) + } +} + +macro_rules! avx_fn { + ($ins:ident, $name:ident) => { + fn $name(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM) { + // Dynasm bug: AVX instructions are not encoded correctly. + match src2 { + XMMOrMemory::XMM(x) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, Rx((x as u8))), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, Rx((x as u8))), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, Rx((x as u8))), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, Rx((x as u8))), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, Rx((x as u8))), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, Rx((x as u8))), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, Rx((x as u8))), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, Rx((x as u8))), + XMM::XMM8 => dynasm!(self ; $ins Rx((dst as u8)), xmm8, Rx((x as u8))), + XMM::XMM9 => dynasm!(self ; $ins Rx((dst as u8)), xmm9, Rx((x as u8))), + XMM::XMM10 => dynasm!(self ; $ins Rx((dst as u8)), xmm10, Rx((x as u8))), + XMM::XMM11 => dynasm!(self ; $ins Rx((dst as u8)), xmm11, Rx((x as u8))), + XMM::XMM12 => dynasm!(self ; $ins Rx((dst as u8)), xmm12, Rx((x as u8))), + XMM::XMM13 => dynasm!(self ; $ins Rx((dst as u8)), xmm13, Rx((x as u8))), + XMM::XMM14 => dynasm!(self ; $ins Rx((dst as u8)), xmm14, Rx((x as u8))), + XMM::XMM15 => dynasm!(self ; $ins Rx((dst as u8)), xmm15, Rx((x as u8))), + }, + XMMOrMemory::Memory(base, disp) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, [Rq((base as u8)) + disp]), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, [Rq((base as u8)) + disp]), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, [Rq((base as u8)) + disp]), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, [Rq((base as u8)) + disp]), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, [Rq((base as u8)) + disp]), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, [Rq((base as u8)) + disp]), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, [Rq((base as u8)) + disp]), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, [Rq((base as u8)) + disp]), + XMM::XMM8 => dynasm!(self ; $ins Rx((dst as u8)), xmm8, [Rq((base as u8)) + disp]), + XMM::XMM9 => dynasm!(self ; $ins Rx((dst as u8)), xmm9, [Rq((base as u8)) + disp]), + XMM::XMM10 => dynasm!(self ; $ins Rx((dst as u8)), xmm10, [Rq((base as u8)) + disp]), + XMM::XMM11 => dynasm!(self ; $ins Rx((dst as u8)), xmm11, [Rq((base as u8)) + disp]), + XMM::XMM12 => dynasm!(self ; $ins Rx((dst as u8)), xmm12, [Rq((base as u8)) + disp]), + XMM::XMM13 => dynasm!(self ; $ins Rx((dst as u8)), xmm13, [Rq((base as u8)) + disp]), + XMM::XMM14 => dynasm!(self ; $ins Rx((dst as u8)), xmm14, [Rq((base as u8)) + disp]), + XMM::XMM15 => dynasm!(self ; $ins Rx((dst as u8)), xmm15, [Rq((base as u8)) + disp]), + }, + } + } + } +} + +macro_rules! avx_i2f_64_fn { + ($ins:ident, $name:ident) => { + fn $name(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM) { + match src2 { + GPROrMemory::GPR(x) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, Rq((x as u8))), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, Rq((x as u8))), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, Rq((x as u8))), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, Rq((x as u8))), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, Rq((x as u8))), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, Rq((x as u8))), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, Rq((x as u8))), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, Rq((x as u8))), + XMM::XMM8 => dynasm!(self ; $ins Rx((dst as u8)), xmm8, Rq((x as u8))), + XMM::XMM9 => dynasm!(self ; $ins Rx((dst as u8)), xmm9, Rq((x as u8))), + XMM::XMM10 => dynasm!(self ; $ins Rx((dst as u8)), xmm10, Rq((x as u8))), + XMM::XMM11 => dynasm!(self ; $ins Rx((dst as u8)), xmm11, Rq((x as u8))), + XMM::XMM12 => dynasm!(self ; $ins Rx((dst as u8)), xmm12, Rq((x as u8))), + XMM::XMM13 => dynasm!(self ; $ins Rx((dst as u8)), xmm13, Rq((x as u8))), + XMM::XMM14 => dynasm!(self ; $ins Rx((dst as u8)), xmm14, Rq((x as u8))), + XMM::XMM15 => dynasm!(self ; $ins Rx((dst as u8)), xmm15, Rq((x as u8))), + }, + GPROrMemory::Memory(base, disp) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, QWORD [Rq((base as u8)) + disp]), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, QWORD [Rq((base as u8)) + disp]), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, QWORD [Rq((base as u8)) + disp]), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, QWORD [Rq((base as u8)) + disp]), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, QWORD [Rq((base as u8)) + disp]), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, QWORD [Rq((base as u8)) + disp]), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, QWORD [Rq((base as u8)) + disp]), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, QWORD [Rq((base as u8)) + disp]), + XMM::XMM8 => dynasm!(self ; $ins Rx((dst as u8)), xmm8, QWORD [Rq((base as u8)) + disp]), + XMM::XMM9 => dynasm!(self ; $ins Rx((dst as u8)), xmm9, QWORD [Rq((base as u8)) + disp]), + XMM::XMM10 => dynasm!(self ; $ins Rx((dst as u8)), xmm10, QWORD [Rq((base as u8)) + disp]), + XMM::XMM11 => dynasm!(self ; $ins Rx((dst as u8)), xmm11, QWORD [Rq((base as u8)) + disp]), + XMM::XMM12 => dynasm!(self ; $ins Rx((dst as u8)), xmm12, QWORD [Rq((base as u8)) + disp]), + XMM::XMM13 => dynasm!(self ; $ins Rx((dst as u8)), xmm13, QWORD [Rq((base as u8)) + disp]), + XMM::XMM14 => dynasm!(self ; $ins Rx((dst as u8)), xmm14, QWORD [Rq((base as u8)) + disp]), + XMM::XMM15 => dynasm!(self ; $ins Rx((dst as u8)), xmm15, QWORD [Rq((base as u8)) + disp]), + }, + } + } + } +} + +macro_rules! avx_i2f_32_fn { + ($ins:ident, $name:ident) => { + fn $name(&mut self, src1: XMM, src2: GPROrMemory, dst: XMM) { + match src2 { + GPROrMemory::GPR(x) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, Rd((x as u8))), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, Rd((x as u8))), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, Rd((x as u8))), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, Rd((x as u8))), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, Rd((x as u8))), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, Rd((x as u8))), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, Rd((x as u8))), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, Rd((x as u8))), + XMM::XMM8 => dynasm!(self ; $ins Rx((dst as u8)), xmm8, Rd((x as u8))), + XMM::XMM9 => dynasm!(self ; $ins Rx((dst as u8)), xmm9, Rd((x as u8))), + XMM::XMM10 => dynasm!(self ; $ins Rx((dst as u8)), xmm10, Rd((x as u8))), + XMM::XMM11 => dynasm!(self ; $ins Rx((dst as u8)), xmm11, Rd((x as u8))), + XMM::XMM12 => dynasm!(self ; $ins Rx((dst as u8)), xmm12, Rd((x as u8))), + XMM::XMM13 => dynasm!(self ; $ins Rx((dst as u8)), xmm13, Rd((x as u8))), + XMM::XMM14 => dynasm!(self ; $ins Rx((dst as u8)), xmm14, Rd((x as u8))), + XMM::XMM15 => dynasm!(self ; $ins Rx((dst as u8)), xmm15, Rd((x as u8))), + }, + GPROrMemory::Memory(base, disp) => match src1 { + XMM::XMM0 => dynasm!(self ; $ins Rx((dst as u8)), xmm0, DWORD [Rq((base as u8)) + disp]), + XMM::XMM1 => dynasm!(self ; $ins Rx((dst as u8)), xmm1, DWORD [Rq((base as u8)) + disp]), + XMM::XMM2 => dynasm!(self ; $ins Rx((dst as u8)), xmm2, DWORD [Rq((base as u8)) + disp]), + XMM::XMM3 => dynasm!(self ; $ins Rx((dst as u8)), xmm3, DWORD [Rq((base as u8)) + disp]), + XMM::XMM4 => dynasm!(self ; $ins Rx((dst as u8)), xmm4, DWORD [Rq((base as u8)) + disp]), + XMM::XMM5 => dynasm!(self ; $ins Rx((dst as u8)), xmm5, DWORD [Rq((base as u8)) + disp]), + XMM::XMM6 => dynasm!(self ; $ins Rx((dst as u8)), xmm6, DWORD [Rq((base as u8)) + disp]), + XMM::XMM7 => dynasm!(self ; $ins Rx((dst as u8)), xmm7, DWORD [Rq((base as u8)) + disp]), + XMM::XMM8 => dynasm!(self ; $ins Rx((dst as u8)), xmm8, DWORD [Rq((base as u8)) + disp]), + XMM::XMM9 => dynasm!(self ; $ins Rx((dst as u8)), xmm9, DWORD [Rq((base as u8)) + disp]), + XMM::XMM10 => dynasm!(self ; $ins Rx((dst as u8)), xmm10, DWORD [Rq((base as u8)) + disp]), + XMM::XMM11 => dynasm!(self ; $ins Rx((dst as u8)), xmm11, DWORD [Rq((base as u8)) + disp]), + XMM::XMM12 => dynasm!(self ; $ins Rx((dst as u8)), xmm12, DWORD [Rq((base as u8)) + disp]), + XMM::XMM13 => dynasm!(self ; $ins Rx((dst as u8)), xmm13, DWORD [Rq((base as u8)) + disp]), + XMM::XMM14 => dynasm!(self ; $ins Rx((dst as u8)), xmm14, DWORD [Rq((base as u8)) + disp]), + XMM::XMM15 => dynasm!(self ; $ins Rx((dst as u8)), xmm15, DWORD [Rq((base as u8)) + disp]), + }, + } + } + } +} + +macro_rules! avx_round_fn { + ($ins:ident, $name:ident, $mode:expr) => { + fn $name(&mut self, src1: XMM, src2: XMMOrMemory, dst: XMM) { + match src2 { + XMMOrMemory::XMM(x) => dynasm!(self ; $ins Rx((dst as u8)), Rx((src1 as u8)), Rx((x as u8)), $mode), + XMMOrMemory::Memory(base, disp) => dynasm!(self ; $ins Rx((dst as u8)), Rx((src1 as u8)), [Rq((base as u8)) + disp], $mode), + } + } + } +} + +impl Emitter for Assembler { + type Label = DynamicLabel; + type Offset = AssemblyOffset; + + fn get_label(&mut self) -> DynamicLabel { + self.new_dynamic_label() + } + + fn get_offset(&self) -> AssemblyOffset { + self.offset() + } + + fn get_jmp_instr_size(&self) -> u8 { + 5 + } + + fn finalize_function(&mut self) { + dynasm!( + self + ; const_neg_one_32: + ; .dword -1 + ; const_zero_32: + ; .dword 0 + ; const_pos_one_32: + ; .dword 1 + ); + } + + fn emit_u64(&mut self, x: u64) { + self.push_u64(x); + } + + fn emit_bytes(&mut self, bytes: &[u8]) { + for &b in bytes { + self.push(b); + } + } + + fn emit_label(&mut self, label: Self::Label) { + dynasm!(self ; => label); + } + + fn emit_nop(&mut self) { + dynasm!(self ; nop); + } + + fn emit_nop_n(&mut self, mut n: usize) { + /* + 1 90H NOP + 2 66 90H 66 NOP + 3 0F 1F 00H NOP DWORD ptr [EAX] + 4 0F 1F 40 00H NOP DWORD ptr [EAX + 00H] + 5 0F 1F 44 00 00H NOP DWORD ptr [EAX + EAX*1 + 00H] + 6 66 0F 1F 44 00 00H NOP DWORD ptr [AX + AX*1 + 00H] + 7 0F 1F 80 00 00 00 00H NOP DWORD ptr [EAX + 00000000H] + 8 0F 1F 84 00 00 00 00 00H NOP DWORD ptr [AX + AX*1 + 00000000H] + 9 66 0F 1F 84 00 00 00 00 00H NOP DWORD ptr [AX + AX*1 + 00000000H] + */ + while n >= 9 { + n -= 9; + self.emit_bytes(&[0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]); + // 9-byte nop + } + let seq: &[u8] = match n { + 0 => &[], + 1 => &[0x90], + 2 => &[0x66, 0x90], + 3 => &[0x0f, 0x1f, 0x00], + 4 => &[0x0f, 0x1f, 0x40, 0x00], + 5 => &[0x0f, 0x1f, 0x44, 0x00, 0x00], + 6 => &[0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00], + 7 => &[0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00], + 8 => &[0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00], + _ => unreachable!(), + }; + self.emit_bytes(seq); + } + + fn emit_mov(&mut self, sz: Size, src: Location, dst: Location) { + // fast path + if let (Location::Imm32(0), Location::GPR(x)) = (src, dst) { + dynasm!(self ; xor Rd(x as u8), Rd(x as u8)); + return; + } + + binop_all_nofp!(mov, self, sz, src, dst, { + binop_imm64_gpr!(mov, self, sz, src, dst, { + match (sz, src, dst) { + (Size::S8, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov [Rq(dst as u8) + disp], Rb(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!(self ; mov Rb(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S8, Location::Imm32(src), Location::GPR(dst)) => { + dynasm!(self ; mov Rb(dst as u8), src as i8); + } + (Size::S8, Location::Imm64(src), Location::GPR(dst)) => { + dynasm!(self ; mov Rb(dst as u8), src as i8); + } + (Size::S8, Location::Imm32(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov BYTE [Rq(dst as u8) + disp], src as i8); + } + (Size::S8, Location::Imm64(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov BYTE [Rq(dst as u8) + disp], src as i8); + } + (Size::S16, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov [Rq(dst as u8) + disp], Rw(src as u8)); + } + (Size::S16, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!(self ; mov Rw(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S16, Location::Imm32(src), Location::GPR(dst)) => { + dynasm!(self ; mov Rw(dst as u8), src as i16); + } + (Size::S16, Location::Imm64(src), Location::GPR(dst)) => { + dynasm!(self ; mov Rw(dst as u8), src as i16); + } + (Size::S16, Location::Imm32(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov WORD [Rq(dst as u8) + disp], src as i16); + } + (Size::S16, Location::Imm64(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov WORD [Rq(dst as u8) + disp], src as i16); + } + (Size::S32, Location::Imm64(src), Location::GPR(dst)) => { + dynasm!(self ; mov Rd(dst as u8), src as i32); + } + (Size::S32, Location::Imm64(src), Location::Memory(dst, disp)) => { + dynasm!(self ; mov DWORD [Rq(dst as u8) + disp], src as i32); + } + (Size::S32, Location::GPR(src), Location::XMM(dst)) => { + dynasm!(self ; movd Rx(dst as u8), Rd(src as u8)); + } + (Size::S32, Location::XMM(src), Location::GPR(dst)) => { + dynasm!(self ; movd Rd(dst as u8), Rx(src as u8)); + } + (Size::S32, Location::Memory(src, disp), Location::XMM(dst)) => { + dynasm!(self ; movd Rx(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S32, Location::XMM(src), Location::Memory(dst, disp)) => { + dynasm!(self ; movd [Rq(dst as u8) + disp], Rx(src as u8)); + } + + (Size::S64, Location::GPR(src), Location::XMM(dst)) => { + dynasm!(self ; movq Rx(dst as u8), Rq(src as u8)); + } + (Size::S64, Location::XMM(src), Location::GPR(dst)) => { + dynasm!(self ; movq Rq(dst as u8), Rx(src as u8)); + } + (Size::S64, Location::Memory(src, disp), Location::XMM(dst)) => { + dynasm!(self ; movq Rx(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S64, Location::XMM(src), Location::Memory(dst, disp)) => { + dynasm!(self ; movq [Rq(dst as u8) + disp], Rx(src as u8)); + } + (_, Location::XMM(src), Location::XMM(dst)) => { + dynasm!(self ; movq Rx(dst as u8), Rx(src as u8)); + } + + _ => panic!("singlepass can't emit MOV {:?} {:?} {:?}", sz, src, dst), + } + }) + }); + } + fn emit_lea(&mut self, sz: Size, src: Location, dst: Location) { + match (sz, src, dst) { + (Size::S32, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!(self ; lea Rd(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S64, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!(self ; lea Rq(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S32, Location::MemoryAddTriple(src1, src2, disp), Location::GPR(dst)) => { + dynasm!(self ; lea Rd(dst as u8), [Rq(src1 as u8) + Rq(src2 as u8) + disp]); + } + (Size::S64, Location::MemoryAddTriple(src1, src2, disp), Location::GPR(dst)) => { + dynasm!(self ; lea Rq(dst as u8), [Rq(src1 as u8) + Rq(src2 as u8) + disp]); + } + _ => panic!("singlepass can't emit LEA {:?} {:?} {:?}", sz, src, dst), + } + } + fn emit_lea_label(&mut self, label: Self::Label, dst: Location) { + match dst { + Location::GPR(x) => { + dynasm!(self ; lea Rq(x as u8), [=>label]); + } + _ => panic!("singlepass can't emit LEA label={:?} {:?}", label, dst), + } + } + fn emit_cdq(&mut self) { + dynasm!(self ; cdq); + } + fn emit_cqo(&mut self) { + dynasm!(self ; cqo); + } + fn emit_xor(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(xor, self, sz, src, dst, { + panic!("singlepass can't emit XOR {:?} {:?} {:?}", sz, src, dst) + }); + } + fn emit_jmp(&mut self, condition: Condition, label: Self::Label) { + match condition { + Condition::None => jmp_op!(jmp, self, label), + Condition::Above => jmp_op!(ja, self, label), + Condition::AboveEqual => jmp_op!(jae, self, label), + Condition::Below => jmp_op!(jb, self, label), + Condition::BelowEqual => jmp_op!(jbe, self, label), + Condition::Greater => jmp_op!(jg, self, label), + Condition::GreaterEqual => jmp_op!(jge, self, label), + Condition::Less => jmp_op!(jl, self, label), + Condition::LessEqual => jmp_op!(jle, self, label), + Condition::Equal => jmp_op!(je, self, label), + Condition::NotEqual => jmp_op!(jne, self, label), + Condition::Signed => jmp_op!(js, self, label), + Condition::Carry => jmp_op!(jc, self, label), + Condition::Overflow => jmp_op!(jo, self, label), + } + } + fn emit_jmp_location(&mut self, loc: Location) { + match loc { + Location::GPR(x) => dynasm!(self ; jmp Rq(x as u8)), + Location::Memory(base, disp) => dynasm!(self ; jmp QWORD [Rq(base as u8) + disp]), + _ => panic!("singlepass can't emit JMP {:?}", loc), + } + } + fn emit_set(&mut self, condition: Condition, dst: GPR) { + match condition { + Condition::Above => dynasm!(self ; seta Rb(dst as u8)), + Condition::AboveEqual => dynasm!(self ; setae Rb(dst as u8)), + Condition::Below => dynasm!(self ; setb Rb(dst as u8)), + Condition::BelowEqual => dynasm!(self ; setbe Rb(dst as u8)), + Condition::Greater => dynasm!(self ; setg Rb(dst as u8)), + Condition::GreaterEqual => dynasm!(self ; setge Rb(dst as u8)), + Condition::Less => dynasm!(self ; setl Rb(dst as u8)), + Condition::LessEqual => dynasm!(self ; setle Rb(dst as u8)), + Condition::Equal => dynasm!(self ; sete Rb(dst as u8)), + Condition::NotEqual => dynasm!(self ; setne Rb(dst as u8)), + Condition::Signed => dynasm!(self ; sets Rb(dst as u8)), + Condition::Carry => dynasm!(self ; setc Rb(dst as u8)), + Condition::Overflow => dynasm!(self ; seto Rb(dst as u8)), + _ => panic!("singlepass can't emit SET {:?} {:?}", condition, dst), + } + } + fn emit_push(&mut self, sz: Size, src: Location) { + match (sz, src) { + (Size::S64, Location::Imm32(src)) => dynasm!(self ; push src as i32), + (Size::S64, Location::GPR(src)) => dynasm!(self ; push Rq(src as u8)), + (Size::S64, Location::Memory(src, disp)) => { + dynasm!(self ; push QWORD [Rq(src as u8) + disp]) + } + _ => panic!("singlepass can't emit PUSH {:?} {:?}", sz, src), + } + } + fn emit_pop(&mut self, sz: Size, dst: Location) { + match (sz, dst) { + (Size::S64, Location::GPR(dst)) => dynasm!(self ; pop Rq(dst as u8)), + (Size::S64, Location::Memory(dst, disp)) => { + dynasm!(self ; pop QWORD [Rq(dst as u8) + disp]) + } + _ => panic!("singlepass can't emit POP {:?} {:?}", sz, dst), + } + } + fn emit_cmp(&mut self, sz: Size, left: Location, right: Location) { + // Constant elimination for comparision between consts. + // + // Only needed for `emit_cmp`, since other binary operators actually write to `right` and `right` must + // be a writable location for them. + let consts = match (left, right) { + (Location::Imm32(x), Location::Imm32(y)) => Some((x as i32 as i64, y as i32 as i64)), + (Location::Imm32(x), Location::Imm64(y)) => Some((x as i32 as i64, y as i64)), + (Location::Imm64(x), Location::Imm32(y)) => Some((x as i64, y as i32 as i64)), + (Location::Imm64(x), Location::Imm64(y)) => Some((x as i64, y as i64)), + _ => None, + }; + use std::cmp::Ordering; + match consts { + Some((x, y)) => match x.cmp(&y) { + Ordering::Less => dynasm!(self ; cmp DWORD [>const_neg_one_32], 0), + Ordering::Equal => dynasm!(self ; cmp DWORD [>const_zero_32], 0), + Ordering::Greater => dynasm!(self ; cmp DWORD [>const_pos_one_32], 0), + }, + None => binop_all_nofp!(cmp, self, sz, left, right, { + panic!("singlepass can't emit CMP {:?} {:?} {:?}", sz, left, right); + }), + } + } + fn emit_add(&mut self, sz: Size, src: Location, dst: Location) { + // Fast path + if let Location::Imm32(0) = src { + return; + } + binop_all_nofp!(add, self, sz, src, dst, { + panic!("singlepass can't emit ADD {:?} {:?} {:?}", sz, src, dst) + }); + } + fn emit_sub(&mut self, sz: Size, src: Location, dst: Location) { + // Fast path + if let Location::Imm32(0) = src { + return; + } + binop_all_nofp!(sub, self, sz, src, dst, { + panic!("singlepass can't emit SUB {:?} {:?} {:?}", sz, src, dst) + }); + } + fn emit_neg(&mut self, sz: Size, value: Location) { + match (sz, value) { + (Size::S8, Location::GPR(value)) => dynasm!(self ; neg Rb(value as u8)), + (Size::S8, Location::Memory(value, disp)) => { + dynasm!(self ; neg [Rq(value as u8) + disp]) + } + (Size::S16, Location::GPR(value)) => dynasm!(self ; neg Rw(value as u8)), + (Size::S16, Location::Memory(value, disp)) => { + dynasm!(self ; neg [Rq(value as u8) + disp]) + } + (Size::S32, Location::GPR(value)) => dynasm!(self ; neg Rd(value as u8)), + (Size::S32, Location::Memory(value, disp)) => { + dynasm!(self ; neg [Rq(value as u8) + disp]) + } + (Size::S64, Location::GPR(value)) => dynasm!(self ; neg Rq(value as u8)), + (Size::S64, Location::Memory(value, disp)) => { + dynasm!(self ; neg [Rq(value as u8) + disp]) + } + _ => panic!("singlepass can't emit NEG {:?} {:?}", sz, value), + } + } + fn emit_imul(&mut self, sz: Size, src: Location, dst: Location) { + binop_gpr_gpr!(imul, self, sz, src, dst, { + binop_mem_gpr!(imul, self, sz, src, dst, { + panic!("singlepass can't emit IMUL {:?} {:?} {:?}", sz, src, dst) + }) + }); + } + fn emit_imul_imm32_gpr64(&mut self, src: u32, dst: GPR) { + dynasm!(self ; imul Rq(dst as u8), Rq(dst as u8), src as i32); + } + fn emit_div(&mut self, sz: Size, divisor: Location) { + unop_gpr_or_mem!(div, self, sz, divisor, { + panic!("singlepass can't emit DIV {:?} {:?}", sz, divisor) + }); + } + fn emit_idiv(&mut self, sz: Size, divisor: Location) { + unop_gpr_or_mem!(idiv, self, sz, divisor, { + panic!("singlepass can't emit IDIV {:?} {:?}", sz, divisor) + }); + } + fn emit_shl(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(shl, self, sz, src, dst, { + panic!("singlepass can't emit SHL {:?} {:?} {:?}", sz, src, dst) + }); + } + fn emit_shr(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(shr, self, sz, src, dst, { + panic!("singlepass can't emit SHR {:?} {:?} {:?}", sz, src, dst) + }); + } + fn emit_sar(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(sar, self, sz, src, dst, { + panic!("singlepass can't emit SAR {:?} {:?} {:?}", sz, src, dst) + }); + } + fn emit_rol(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(rol, self, sz, src, dst, { + panic!("singlepass can't emit ROL {:?} {:?} {:?}", sz, src, dst) + }); + } + fn emit_ror(&mut self, sz: Size, src: Location, dst: Location) { + binop_shift!(ror, self, sz, src, dst, { + panic!("singlepass can't emit ROR {:?} {:?} {:?}", sz, src, dst) + }); + } + fn emit_and(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(and, self, sz, src, dst, { + panic!("singlepass can't emit AND {:?} {:?} {:?}", sz, src, dst) + }); + } + fn emit_or(&mut self, sz: Size, src: Location, dst: Location) { + binop_all_nofp!(or, self, sz, src, dst, { + panic!("singlepass can't emit OR {:?} {:?} {:?}", sz, src, dst) + }); + } + fn emit_bsr(&mut self, sz: Size, src: Location, dst: Location) { + binop_gpr_gpr!(bsr, self, sz, src, dst, { + binop_mem_gpr!(bsr, self, sz, src, dst, { + panic!("singlepass can't emit BSR {:?} {:?} {:?}", sz, src, dst) + }) + }); + } + fn emit_bsf(&mut self, sz: Size, src: Location, dst: Location) { + binop_gpr_gpr!(bsf, self, sz, src, dst, { + binop_mem_gpr!(bsf, self, sz, src, dst, { + panic!("singlepass can't emit BSF {:?} {:?} {:?}", sz, src, dst) + }) + }); + } + fn emit_popcnt(&mut self, sz: Size, src: Location, dst: Location) { + binop_gpr_gpr!(popcnt, self, sz, src, dst, { + binop_mem_gpr!(popcnt, self, sz, src, dst, { + panic!("singlepass can't emit POPCNT {:?} {:?} {:?}", sz, src, dst) + }) + }); + } + fn emit_movzx(&mut self, sz_src: Size, src: Location, sz_dst: Size, dst: Location) { + match (sz_src, src, sz_dst, dst) { + (Size::S8, Location::GPR(src), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movzx Rd(dst as u8), Rb(src as u8)); + } + (Size::S16, Location::GPR(src), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movzx Rd(dst as u8), Rw(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movzx Rd(dst as u8), BYTE [Rq(src as u8) + disp]); + } + (Size::S16, Location::Memory(src, disp), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movzx Rd(dst as u8), WORD [Rq(src as u8) + disp]); + } + (Size::S8, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movzx Rq(dst as u8), Rb(src as u8)); + } + (Size::S16, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movzx Rq(dst as u8), Rw(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movzx Rq(dst as u8), BYTE [Rq(src as u8) + disp]); + } + (Size::S16, Location::Memory(src, disp), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movzx Rq(dst as u8), WORD [Rq(src as u8) + disp]); + } + _ => { + panic!("singlepass can't emit MOVZX {:?} {:?} {:?} {:?}", sz_src, src, sz_dst, dst) + } + } + } + fn emit_movsx(&mut self, sz_src: Size, src: Location, sz_dst: Size, dst: Location) { + match (sz_src, src, sz_dst, dst) { + (Size::S8, Location::GPR(src), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movsx Rd(dst as u8), Rb(src as u8)); + } + (Size::S16, Location::GPR(src), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movsx Rd(dst as u8), Rw(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movsx Rd(dst as u8), BYTE [Rq(src as u8) + disp]); + } + (Size::S16, Location::Memory(src, disp), Size::S32, Location::GPR(dst)) => { + dynasm!(self ; movsx Rd(dst as u8), WORD [Rq(src as u8) + disp]); + } + (Size::S8, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), Rb(src as u8)); + } + (Size::S16, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), Rw(src as u8)); + } + (Size::S32, Location::GPR(src), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), Rd(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), BYTE [Rq(src as u8) + disp]); + } + (Size::S16, Location::Memory(src, disp), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), WORD [Rq(src as u8) + disp]); + } + (Size::S32, Location::Memory(src, disp), Size::S64, Location::GPR(dst)) => { + dynasm!(self ; movsx Rq(dst as u8), DWORD [Rq(src as u8) + disp]); + } + _ => { + panic!("singlepass can't emit MOVSX {:?} {:?} {:?} {:?}", sz_src, src, sz_dst, dst) + } + } + } + + fn emit_xchg(&mut self, sz: Size, src: Location, dst: Location) { + match (sz, src, dst) { + (Size::S8, Location::GPR(src), Location::GPR(dst)) => { + dynasm!(self ; xchg Rb(dst as u8), Rb(src as u8)); + } + (Size::S16, Location::GPR(src), Location::GPR(dst)) => { + dynasm!(self ; xchg Rw(dst as u8), Rw(src as u8)); + } + (Size::S32, Location::GPR(src), Location::GPR(dst)) => { + dynasm!(self ; xchg Rd(dst as u8), Rd(src as u8)); + } + (Size::S64, Location::GPR(src), Location::GPR(dst)) => { + dynasm!(self ; xchg Rq(dst as u8), Rq(src as u8)); + } + (Size::S8, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!(self ; xchg Rb(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S8, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; xchg [Rq(dst as u8) + disp], Rb(src as u8)); + } + (Size::S16, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!(self ; xchg Rw(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S16, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; xchg [Rq(dst as u8) + disp], Rw(src as u8)); + } + (Size::S32, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!(self ; xchg Rd(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S32, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; xchg [Rq(dst as u8) + disp], Rd(src as u8)); + } + (Size::S64, Location::Memory(src, disp), Location::GPR(dst)) => { + dynasm!(self ; xchg Rq(dst as u8), [Rq(src as u8) + disp]); + } + (Size::S64, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; xchg [Rq(dst as u8) + disp], Rq(src as u8)); + } + _ => panic!("singlepass can't emit XCHG {:?} {:?} {:?}", sz, src, dst), + } + } + + fn emit_lock_xadd(&mut self, sz: Size, src: Location, dst: Location) { + match (sz, src, dst) { + (Size::S8, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; lock xadd [Rq(dst as u8) + disp], Rb(src as u8)); + } + (Size::S16, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; lock xadd [Rq(dst as u8) + disp], Rw(src as u8)); + } + (Size::S32, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; lock xadd [Rq(dst as u8) + disp], Rd(src as u8)); + } + (Size::S64, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; lock xadd [Rq(dst as u8) + disp], Rq(src as u8)); + } + _ => panic!("singlepass can't emit LOCK XADD {:?} {:?} {:?}", sz, src, dst), + } + } + + fn emit_lock_cmpxchg(&mut self, sz: Size, src: Location, dst: Location) { + match (sz, src, dst) { + (Size::S8, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; lock cmpxchg [Rq(dst as u8) + disp], Rb(src as u8)); + } + (Size::S16, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; lock cmpxchg [Rq(dst as u8) + disp], Rw(src as u8)); + } + (Size::S32, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; lock cmpxchg [Rq(dst as u8) + disp], Rd(src as u8)); + } + (Size::S64, Location::GPR(src), Location::Memory(dst, disp)) => { + dynasm!(self ; lock cmpxchg [Rq(dst as u8) + disp], Rq(src as u8)); + } + _ => panic!("singlepass can't emit LOCK CMPXCHG {:?} {:?} {:?}", sz, src, dst), + } + } + + fn emit_rep_stosq(&mut self) { + dynasm!(self ; rep stosq); + } + fn emit_btc_gpr_imm8_32(&mut self, src: u8, dst: GPR) { + dynasm!(self ; btc Rd(dst as u8), BYTE src as i8); + } + + fn emit_btc_gpr_imm8_64(&mut self, src: u8, dst: GPR) { + dynasm!(self ; btc Rq(dst as u8), BYTE src as i8); + } + + fn emit_cmovae_gpr_32(&mut self, src: GPR, dst: GPR) { + dynasm!(self ; cmovae Rd(dst as u8), Rd(src as u8)); + } + + fn emit_cmovae_gpr_64(&mut self, src: GPR, dst: GPR) { + dynasm!(self ; cmovae Rq(dst as u8), Rq(src as u8)); + } + + fn emit_vmovaps(&mut self, src: XMMOrMemory, dst: XMMOrMemory) { + match (src, dst) { + (XMMOrMemory::XMM(src), XMMOrMemory::XMM(dst)) => { + dynasm!(self ; movaps Rx(dst as u8), Rx(src as u8)) + } + (XMMOrMemory::Memory(base, disp), XMMOrMemory::XMM(dst)) => { + dynasm!(self ; movaps Rx(dst as u8), [Rq(base as u8) + disp]) + } + (XMMOrMemory::XMM(src), XMMOrMemory::Memory(base, disp)) => { + dynasm!(self ; movaps [Rq(base as u8) + disp], Rx(src as u8)) + } + _ => panic!("singlepass can't emit VMOVAPS {:?} {:?}", src, dst), + }; + } + + fn emit_vmovapd(&mut self, src: XMMOrMemory, dst: XMMOrMemory) { + match (src, dst) { + (XMMOrMemory::XMM(src), XMMOrMemory::XMM(dst)) => { + dynasm!(self ; movapd Rx(dst as u8), Rx(src as u8)) + } + (XMMOrMemory::Memory(base, disp), XMMOrMemory::XMM(dst)) => { + dynasm!(self ; movapd Rx(dst as u8), [Rq(base as u8) + disp]) + } + (XMMOrMemory::XMM(src), XMMOrMemory::Memory(base, disp)) => { + dynasm!(self ; movapd [Rq(base as u8) + disp], Rx(src as u8)) + } + _ => panic!("singlepass can't emit VMOVAPD {:?} {:?}", src, dst), + }; + } + + avx_fn!(vxorps, emit_vxorps); + avx_fn!(vxorpd, emit_vxorpd); + + avx_fn!(vaddss, emit_vaddss); + avx_fn!(vaddsd, emit_vaddsd); + + avx_fn!(vsubss, emit_vsubss); + avx_fn!(vsubsd, emit_vsubsd); + + avx_fn!(vmulss, emit_vmulss); + avx_fn!(vmulsd, emit_vmulsd); + + avx_fn!(vdivss, emit_vdivss); + avx_fn!(vdivsd, emit_vdivsd); + + avx_fn!(vmaxss, emit_vmaxss); + avx_fn!(vmaxsd, emit_vmaxsd); + + avx_fn!(vminss, emit_vminss); + avx_fn!(vminsd, emit_vminsd); + + avx_fn!(vcmpeqss, emit_vcmpeqss); + avx_fn!(vcmpeqsd, emit_vcmpeqsd); + + avx_fn!(vcmpneqss, emit_vcmpneqss); + avx_fn!(vcmpneqsd, emit_vcmpneqsd); + + avx_fn!(vcmpltss, emit_vcmpltss); + avx_fn!(vcmpltsd, emit_vcmpltsd); + + avx_fn!(vcmpless, emit_vcmpless); + avx_fn!(vcmplesd, emit_vcmplesd); + + avx_fn!(vcmpgtss, emit_vcmpgtss); + avx_fn!(vcmpgtsd, emit_vcmpgtsd); + + avx_fn!(vcmpgess, emit_vcmpgess); + avx_fn!(vcmpgesd, emit_vcmpgesd); + + avx_fn!(vcmpunordss, emit_vcmpunordss); + avx_fn!(vcmpunordsd, emit_vcmpunordsd); + + avx_fn!(vcmpordss, emit_vcmpordss); + avx_fn!(vcmpordsd, emit_vcmpordsd); + + avx_fn!(vsqrtss, emit_vsqrtss); + avx_fn!(vsqrtsd, emit_vsqrtsd); + + avx_fn!(vcvtss2sd, emit_vcvtss2sd); + avx_fn!(vcvtsd2ss, emit_vcvtsd2ss); + + avx_round_fn!(vroundss, emit_vroundss_nearest, 0); + avx_round_fn!(vroundss, emit_vroundss_floor, 1); + avx_round_fn!(vroundss, emit_vroundss_ceil, 2); + avx_round_fn!(vroundss, emit_vroundss_trunc, 3); + avx_round_fn!(vroundsd, emit_vroundsd_nearest, 0); + avx_round_fn!(vroundsd, emit_vroundsd_floor, 1); + avx_round_fn!(vroundsd, emit_vroundsd_ceil, 2); + avx_round_fn!(vroundsd, emit_vroundsd_trunc, 3); + + avx_i2f_32_fn!(vcvtsi2ss, emit_vcvtsi2ss_32); + avx_i2f_32_fn!(vcvtsi2sd, emit_vcvtsi2sd_32); + avx_i2f_64_fn!(vcvtsi2ss, emit_vcvtsi2ss_64); + avx_i2f_64_fn!(vcvtsi2sd, emit_vcvtsi2sd_64); + + fn emit_vblendvps(&mut self, src1: XMM, src2: XMMOrMemory, mask: XMM, dst: XMM) { + match src2 { + XMMOrMemory::XMM(src2) => { + dynasm!(self ; vblendvps Rx(dst as u8), Rx(mask as u8), Rx(src2 as u8), Rx(src1 as u8)) + } + XMMOrMemory::Memory(base, disp) => { + dynasm!(self ; vblendvps Rx(dst as u8), Rx(mask as u8), [Rq(base as u8) + disp], Rx(src1 as u8)) + } + } + } + + fn emit_vblendvpd(&mut self, src1: XMM, src2: XMMOrMemory, mask: XMM, dst: XMM) { + match src2 { + XMMOrMemory::XMM(src2) => { + dynasm!(self ; vblendvpd Rx(dst as u8), Rx(mask as u8), Rx(src2 as u8), Rx(src1 as u8)) + } + XMMOrMemory::Memory(base, disp) => { + dynasm!(self ; vblendvpd Rx(dst as u8), Rx(mask as u8), [Rq(base as u8) + disp], Rx(src1 as u8)) + } + } + } + + fn emit_ucomiss(&mut self, src: XMMOrMemory, dst: XMM) { + match src { + XMMOrMemory::XMM(x) => dynasm!(self ; ucomiss Rx(dst as u8), Rx(x as u8)), + XMMOrMemory::Memory(base, disp) => { + dynasm!(self ; ucomiss Rx(dst as u8), [Rq(base as u8) + disp]) + } + } + } + + fn emit_ucomisd(&mut self, src: XMMOrMemory, dst: XMM) { + match src { + XMMOrMemory::XMM(x) => dynasm!(self ; ucomisd Rx(dst as u8), Rx(x as u8)), + XMMOrMemory::Memory(base, disp) => { + dynasm!(self ; ucomisd Rx(dst as u8), [Rq(base as u8) + disp]) + } + } + } + + fn emit_cvttss2si_32(&mut self, src: XMMOrMemory, dst: GPR) { + match src { + XMMOrMemory::XMM(x) => dynasm!(self ; cvttss2si Rd(dst as u8), Rx(x as u8)), + XMMOrMemory::Memory(base, disp) => { + dynasm!(self ; cvttss2si Rd(dst as u8), [Rq(base as u8) + disp]) + } + } + } + + fn emit_cvttss2si_64(&mut self, src: XMMOrMemory, dst: GPR) { + match src { + XMMOrMemory::XMM(x) => dynasm!(self ; cvttss2si Rq(dst as u8), Rx(x as u8)), + XMMOrMemory::Memory(base, disp) => { + dynasm!(self ; cvttss2si Rq(dst as u8), [Rq(base as u8) + disp]) + } + } + } + + fn emit_cvttsd2si_32(&mut self, src: XMMOrMemory, dst: GPR) { + match src { + XMMOrMemory::XMM(x) => dynasm!(self ; cvttsd2si Rd(dst as u8), Rx(x as u8)), + XMMOrMemory::Memory(base, disp) => { + dynasm!(self ; cvttsd2si Rd(dst as u8), [Rq(base as u8) + disp]) + } + } + } + + fn emit_cvttsd2si_64(&mut self, src: XMMOrMemory, dst: GPR) { + match src { + XMMOrMemory::XMM(x) => dynasm!(self ; cvttsd2si Rq(dst as u8), Rx(x as u8)), + XMMOrMemory::Memory(base, disp) => { + dynasm!(self ; cvttsd2si Rq(dst as u8), [Rq(base as u8) + disp]) + } + } + } + + fn emit_test_gpr_64(&mut self, reg: GPR) { + dynasm!(self ; test Rq(reg as u8), Rq(reg as u8)); + } + + fn emit_ud2(&mut self) { + dynasm!(self ; ud2); + } + fn emit_ret(&mut self) { + dynasm!(self ; ret); + } + + fn emit_call_label(&mut self, label: Self::Label) { + dynasm!(self ; call =>label); + } + fn emit_call_location(&mut self, loc: Location) { + match loc { + Location::GPR(x) => dynasm!(self ; call Rq(x as u8)), + Location::Memory(base, disp) => dynasm!(self ; call QWORD [Rq(base as u8) + disp]), + _ => panic!("singlepass can't emit CALL {:?}", loc), + } + } + + fn emit_call_register(&mut self, reg: GPR) { + dynasm!(self ; call Rq(reg as u8)); + } + + fn emit_bkpt(&mut self) { + dynasm!(self ; int3); + } + + fn emit_host_redirection(&mut self, target: GPR) { + self.emit_jmp_location(Location::GPR(target)); + } + + fn arch_has_xzcnt(&self, cpu: &EnumSet) -> bool { + cpu.contains(CpuFeature::BMI1) && cpu.contains(CpuFeature::LZCNT) + } + + fn arch_emit_lzcnt(&mut self, sz: Size, src: Location, dst: Location) { + binop_gpr_gpr!(lzcnt, self, sz, src, dst, { + binop_mem_gpr!(lzcnt, self, sz, src, dst, { + panic!("singlepass can't emit LZCNT {:?} {:?} {:?}", sz, src, dst) + }) + }) + } + + fn arch_emit_tzcnt(&mut self, sz: Size, src: Location, dst: Location) { + binop_gpr_gpr!(tzcnt, self, sz, src, dst, { + binop_mem_gpr!(tzcnt, self, sz, src, dst, { + panic!("singlepass can't emit TZCNT {:?} {:?} {:?}", sz, src, dst) + }) + }) + } + + fn arch_mov64_imm_offset(&self) -> usize { + 2 + } +} diff --git a/runtime/unc-vm/compiler-singlepass/src/lib.rs b/runtime/unc-vm/compiler-singlepass/src/lib.rs new file mode 100644 index 000000000..6e3070d08 --- /dev/null +++ b/runtime/unc-vm/compiler-singlepass/src/lib.rs @@ -0,0 +1,20 @@ +//! A WebAssembly `Compiler` implementation using Singlepass. +//! +//! Singlepass is a super-fast assembly generator that generates +//! assembly code in just one pass. This is useful for different applications +//! including Blockchains and Edge computing where quick compilation +//! times are a must, and JIT bombs should never happen. +//! +//! Compared to Cranelift and LLVM, Singlepass compiles much faster but has worse +//! runtime performance. + +mod address_map; +mod codegen_x64; +mod compiler; +mod config; +mod emitter_x64; +mod machine; +mod x64_decl; + +pub use crate::compiler::SinglepassCompiler; +pub use crate::config::Singlepass; diff --git a/runtime/unc-vm/compiler-singlepass/src/machine.rs b/runtime/unc-vm/compiler-singlepass/src/machine.rs new file mode 100644 index 000000000..8c71c1d87 --- /dev/null +++ b/runtime/unc-vm/compiler-singlepass/src/machine.rs @@ -0,0 +1,593 @@ +use crate::emitter_x64::*; +use unc_vm_compiler::wasmparser::ValType as WpType; +use unc_vm_compiler::CallingConvention; +use smallvec::smallvec; +use smallvec::SmallVec; +use std::convert::TryFrom; + +const NATIVE_PAGE_SIZE: usize = 4096; + +#[derive(Clone, Copy)] +struct MachineStackOffset(usize); + +pub(crate) struct Machine { + used_gprs: u32, // Bitset for the used GPRs, 1 means used + used_xmms: u32, // Bitset for the used XMMs, 1 means used + stack_offset: MachineStackOffset, + save_area_offset: Option, + /// Memory location at which local variables begin. + /// + /// Populated in `init_locals`. + locals_offset: MachineStackOffset, +} + +/// Returns an u32 that has as 1 bits the ones matching registers passed as parameters +macro_rules! bitset_of_regs { + ($( $r:expr ),*) => {{ + $( (1u32 << ($r as u32)) )|* + }} +} + +// Note: the below asserts are because we currently use u32 for used_gprs and used_xmms +// Feel free to increase the number in this assert by making them bigger if needed +#[allow(dead_code)] +const _GPRS_FIT_IN_U32: () = assert!(GPR::num_gprs() <= 32); +#[allow(dead_code)] +const _XMMS_FIT_IN_U32: () = assert!(XMM::num_xmms() <= 32); + +impl Machine { + pub(crate) fn new() -> Self { + Machine { + used_gprs: 0, + used_xmms: 0, + stack_offset: MachineStackOffset(0), + save_area_offset: None, + locals_offset: MachineStackOffset(0), + } + } + + pub(crate) fn get_stack_offset(&self) -> usize { + self.stack_offset.0 + } + + fn get_used_in(mut v: u32, to_return_type: impl Fn(u8) -> T) -> Vec { + let mut n = 0u8; + let mut res = Vec::with_capacity(v.count_ones() as usize); + while v != 0 { + n += v.trailing_zeros() as u8; + res.push(to_return_type(n)); + v >>= v.trailing_zeros() + 1; + n += 1; + } + res + } + + pub(crate) fn get_used_gprs(&self) -> Vec { + Self::get_used_in(self.used_gprs, |r| GPR::from_repr(r).unwrap()) + } + + pub(crate) fn get_used_xmms(&self) -> Vec { + Self::get_used_in(self.used_xmms, |r| XMM::from_repr(r).unwrap()) + } + + pub(crate) fn get_vmctx_reg() -> GPR { + GPR::R15 + } + + fn pick_one_in(v: u32) -> Option { + let r = v.trailing_zeros() as u8; + (r != 32).then_some(r) + } + + /// Picks an unused general purpose register for local/stack/argument use. + /// + /// This method does not mark the register as used. + pub(crate) fn pick_gpr(&self) -> Option { + use GPR::*; + const REGS: u32 = bitset_of_regs!(RSI, RDI, R8, R9, R10, R11); + Self::pick_one_in(!self.used_gprs & REGS).map(|r| GPR::from_repr(r).unwrap()) + } + + /// Picks an unused general purpose register for internal temporary use. + /// + /// This method does not mark the register as used. + pub(crate) fn pick_temp_gpr(&self) -> Option { + use GPR::*; + const REGS: u32 = bitset_of_regs!(RAX, RCX, RDX); + Self::pick_one_in(!self.used_gprs & REGS).map(|r| GPR::from_repr(r).unwrap()) + } + + fn get_gpr_used(&self, r: GPR) -> bool { + if 0 != (self.used_gprs & bitset_of_regs!(r)) { + true + } else { + false + } + } + + fn set_gpr_used(&mut self, r: GPR) { + self.used_gprs |= bitset_of_regs!(r); + } + + fn set_gpr_unused(&mut self, r: GPR) { + self.used_gprs &= !bitset_of_regs!(r); + } + + fn get_xmm_used(&self, r: XMM) -> bool { + if 0 != (self.used_xmms & bitset_of_regs!(r)) { + true + } else { + false + } + } + + fn set_xmm_used(&mut self, r: XMM) { + self.used_xmms |= bitset_of_regs!(r); + } + + fn set_xmm_unused(&mut self, r: XMM) { + self.used_xmms &= !bitset_of_regs!(r); + } + + /// Acquires a temporary GPR. + pub(crate) fn acquire_temp_gpr(&mut self) -> Option { + let gpr = self.pick_temp_gpr(); + if let Some(x) = gpr { + self.set_gpr_used(x); + } + gpr + } + + /// Releases a temporary GPR. + pub(crate) fn release_temp_gpr(&mut self, gpr: GPR) { + assert!(self.get_gpr_used(gpr)); + self.set_gpr_unused(gpr); + } + + /// Specify that a given register is in use. + pub(crate) fn reserve_unused_temp_gpr(&mut self, gpr: GPR) -> GPR { + assert!(!self.get_gpr_used(gpr)); + self.set_gpr_used(gpr); + gpr + } + + /// Picks an unused XMM register. + /// + /// This method does not mark the register as used. + pub(crate) fn pick_xmm(&self) -> Option { + use XMM::*; + const REGS: u32 = bitset_of_regs!(XMM3, XMM4, XMM5, XMM6, XMM7); + Self::pick_one_in(!self.used_xmms & REGS).map(|r| XMM::from_repr(r).unwrap()) + } + + /// Picks an unused XMM register for internal temporary use. + /// + /// This method does not mark the register as used. + pub(crate) fn pick_temp_xmm(&self) -> Option { + use XMM::*; + const REGS: u32 = bitset_of_regs!(XMM0, XMM1, XMM2); + Self::pick_one_in(!self.used_xmms & REGS).map(|r| XMM::from_repr(r).unwrap()) + } + + /// Acquires a temporary XMM register. + pub(crate) fn acquire_temp_xmm(&mut self) -> Option { + let xmm = self.pick_temp_xmm(); + if let Some(x) = xmm { + self.set_xmm_used(x); + } + xmm + } + + /// Releases a temporary XMM register. + pub(crate) fn release_temp_xmm(&mut self, xmm: XMM) { + assert!(self.get_xmm_used(xmm)); + self.set_xmm_unused(xmm); + } + + fn increase_rsp(&mut self, a: &mut impl Emitter, sz: usize) { + a.emit_add(Size::S64, Location::Imm32(u32::try_from(sz).unwrap()), Location::GPR(GPR::RSP)); + self.stack_offset.0 -= sz; + } + + fn decrease_rsp(&mut self, a: &mut impl Emitter, sz: usize) { + a.emit_sub(Size::S64, Location::Imm32(u32::try_from(sz).unwrap()), Location::GPR(GPR::RSP)); + self.stack_offset.0 += sz; + } + + /// Acquires locations from the machine state. + /// + /// If the returned locations are used for stack value, `release_location` needs to be called on them; + /// Otherwise, if the returned locations are used for locals, `release_location` does not need to be called on them. + pub(crate) fn acquire_locations( + &mut self, + assembler: &mut E, + tys: &[WpType], + zeroed: bool, + ) -> SmallVec<[Location; 1]> { + let mut ret = smallvec![]; + let mut delta_stack_offset: usize = 0; + + for ty in tys { + let loc = match *ty { + WpType::F32 | WpType::F64 => self.pick_xmm().map(Location::XMM), + WpType::I32 | WpType::I64 => self.pick_gpr().map(Location::GPR), + WpType::FuncRef | WpType::ExternRef => self.pick_gpr().map(Location::GPR), + _ => unreachable!("can't acquire location for type {:?}", ty), + }; + + let loc = if let Some(x) = loc { + x + } else { + delta_stack_offset += 8; + Location::Memory(GPR::RBP, -((self.stack_offset.0 + delta_stack_offset) as i32)) + }; + if let Location::GPR(x) = loc { + self.set_gpr_used(x); + } else if let Location::XMM(x) = loc { + self.set_xmm_used(x); + } + ret.push(loc); + } + + if delta_stack_offset != 0 { + self.decrease_rsp(assembler, delta_stack_offset); + } + if zeroed { + for i in 0..tys.len() { + assembler.emit_mov(Size::S64, Location::Imm32(0), ret[i]); + } + } + ret + } + + /// Releases locations used for stack value. + pub(crate) fn release_locations(&mut self, assembler: &mut E, locs: &[Location]) { + let mut delta_stack_offset: usize = 0; + + for loc in locs.iter().rev() { + match *loc { + Location::GPR(x) => { + assert!(self.get_gpr_used(x)); + self.set_gpr_unused(x); + } + Location::XMM(x) => { + assert!(self.get_xmm_used(x)); + self.set_xmm_unused(x); + } + Location::Memory(GPR::RBP, x) => { + if x >= 0 { + unreachable!(); + } + let offset = (-x) as usize; + if offset != self.stack_offset.0 - delta_stack_offset { + unreachable!(); + } + delta_stack_offset += 8; + } + _ => {} + } + } + + if delta_stack_offset != 0 { + self.increase_rsp(assembler, delta_stack_offset); + } + } + + pub(crate) fn release_locations_only_regs(&mut self, locs: &[Location]) { + for loc in locs.iter().rev() { + match *loc { + Location::GPR(x) => { + assert!(self.get_gpr_used(x)); + self.set_gpr_unused(x); + } + Location::XMM(x) => { + assert!(self.get_xmm_used(x)); + self.set_xmm_unused(x); + } + _ => {} + } + } + } + + pub(crate) fn release_locations_only_stack( + &mut self, + assembler: &mut E, + locs: &[Location], + ) { + let mut delta_stack_offset: usize = 0; + + for loc in locs.iter().rev() { + if let Location::Memory(GPR::RBP, x) = *loc { + if x >= 0 { + unreachable!(); + } + let offset = (-x) as usize; + if offset != self.stack_offset.0 - delta_stack_offset { + unreachable!(); + } + delta_stack_offset += 8; + } + } + + if delta_stack_offset != 0 { + self.increase_rsp(assembler, delta_stack_offset); + } + } + + pub(crate) fn release_locations_keep_state( + &self, + assembler: &mut E, + locs: &[Location], + ) { + let mut delta_stack_offset: usize = 0; + + for loc in locs.iter().rev() { + if let Location::Memory(GPR::RBP, x) = *loc { + if x >= 0 { + unreachable!(); + } + let offset = (-x) as usize; + if offset != self.stack_offset.0 - delta_stack_offset { + unreachable!(); + } + delta_stack_offset += 8; + } + } + + if delta_stack_offset != 0 { + // DO NOT use increase_rsp, as we don’t want to change stack_offset + assembler.emit_add( + Size::S64, + Location::Imm32(delta_stack_offset as u32), + Location::GPR(GPR::RSP), + ); + } + } + + const LOCAL_REGISTERS: &'static [GPR] = &[GPR::R12, GPR::R13, GPR::R14, GPR::RBX]; + + pub(crate) fn get_local_location(&self, idx: u32) -> Location { + // NB: This calculation cannot reasonably overflow. `self.locals_offset` will typically be + // small (< 32), and `idx` is bounded to `51000` due to limits imposed by the wasmparser + // validator. We introduce a debug_assert here to ensure that `idx` never really exceeds + // some incredibly large value. + debug_assert!(idx <= 999_999, "this runtime can't deal with unreasonable number of locals"); + Self::LOCAL_REGISTERS.get(idx as usize).map(|r| Location::GPR(*r)).unwrap_or_else(|| { + let local_offset = + idx.checked_sub(Self::LOCAL_REGISTERS.len() as u32).unwrap().wrapping_mul(8); + Location::Memory( + GPR::RBP, + (local_offset.wrapping_add(self.locals_offset.0 as u32) as i32).wrapping_neg(), + ) + }) + } + + // `setup_registers`, `init_locals`, `finalize_locals` and `restore_registers` work together, + // the first two making up the function prologue (with a stack check and gas charge in-between), + // and the second two making up the function epilogue (with the stack height reset in-between). + // + // Together, they build the following stack, with `N = min(n, LOCAL_REGISTERS.len())`: + // +-------------------+--------+ + // | Return Pointer | 8B | + // | Saved RBP | 8B | <- RBP + // | LOCAL_REGISTERS 0 | 8B | + // | ... | | + // | LOCAL_REGISTERS N | 8B | + // | Saved R15 | 8B | + // | (Win FastC) RDI | 8B | + // | (Win FastC) RSI | 8B | <- save_area_offset + // | Local 0 | 8B | <- locals_offset + // | ... | | + // | Local n | 8B | <- RSP, stack_offset (at end of init_locals, will keep moving during fn codegen) + // +-------------------+--------+ + pub(crate) fn setup_registers( + &mut self, + a: &mut E, + n: u32, + n_params: u32, + calling_convention: CallingConvention, + ) { + // Total size (in bytes) of the pre-allocated "static area" for this function's + // locals and callee-saved registers. + let mut static_area_size: usize = 0; + + // Space to clobber registers used for locals. + static_area_size += 8 * std::cmp::min(Self::LOCAL_REGISTERS.len(), n as usize); + + // Callee-saved R15 for vmctx. + static_area_size += 8; + + // Allocate the stack + self.decrease_rsp(a, static_area_size); + + // Save callee-saved registers + for (i, local_reg) in Self::LOCAL_REGISTERS.iter().take(n as usize).enumerate() { + a.emit_mov( + Size::S64, + Location::GPR(*local_reg), + Location::Memory(GPR::RBP, -((i + 1) as i32) * 8), + ); + } + + // Save R15 for vmctx use. + a.emit_mov(Size::S64, Location::GPR(GPR::R15), Location::Memory(GPR::RSP, 0)); + + // For Windows ABI, save RDI and RSI + if calling_convention == CallingConvention::WindowsFastcall { + self.decrease_rsp(a, 8 * 2); + for (i, reg) in [GPR::RSI, GPR::RDI].iter().enumerate() { + a.emit_mov( + Size::S64, + Location::GPR(*reg), + Location::Memory(GPR::RSP, i as i32 * 8), + ); + } + } + + // Save the offset of register save area. + self.save_area_offset = Some(MachineStackOffset(self.stack_offset.0)); + + // Load in-register parameters into the allocated locations for register parameters. + // Locals are allocated on the stack from higher address to lower address, + // so we won't skip the stack guard page here. + self.locals_offset = MachineStackOffset(self.stack_offset.0 + 8); // + 8 because locals_offset is supposed to point to 1st local + let params_size = + (n_params as usize).saturating_sub(Self::LOCAL_REGISTERS.len()).checked_mul(8).unwrap(); + self.decrease_rsp(a, params_size); + for i in 0..n_params { + // NB: the 0th parameter is used for passing around the internal VM data (vmctx). + let loc = Self::get_param_location((i + 1) as usize, calling_convention); + let local_loc = self.get_local_location(i); + match loc { + Location::GPR(_) => { + a.emit_mov(Size::S64, loc, local_loc); + } + // TODO: move Location::Memory args init into init_locals down below so it happens after instrumentation + // Registers *must* stay here because we’re using registers between setup_registers and init_locals + Location::Memory(_, _) => match local_loc { + Location::GPR(_) => { + a.emit_mov(Size::S64, loc, local_loc); + } + Location::Memory(_, _) => { + a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX)); + a.emit_mov(Size::S64, Location::GPR(GPR::RAX), local_loc); + } + _ => unreachable!(), + }, + _ => unreachable!(), + } + } + + // Load vmctx into R15. + a.emit_mov( + Size::S64, + Self::get_param_location(0, calling_convention), + Location::GPR(GPR::R15), + ); + } + + pub(crate) fn init_locals( + &mut self, + a: &mut E, + n: u32, + n_params: u32, + _calling_convention: CallingConvention, + ) { + let registers_remaining_for_locals = + Self::LOCAL_REGISTERS.len().saturating_sub(n_params as usize); + let locals_to_init = (n - n_params) as usize; + let locals_size = + locals_to_init.saturating_sub(registers_remaining_for_locals).checked_mul(8).unwrap(); + + // Allocate the stack, without actually writing to it. + self.decrease_rsp(a, locals_size); + + // Stack probe. + // + // `rep stosq` writes data from low address to high address and may skip the stack guard page. + // so here we probe it explicitly when needed. + for i in (n_params..n).step_by(NATIVE_PAGE_SIZE / 8).skip(1) { + a.emit_mov(Size::S64, Location::Imm32(0), self.get_local_location(i)); + } + + // Initialize all remaining locals to zero. + // + // This is a little tricky, as we want to initialize all stack local slots, except for + // those that were already populated with function argument data. The complication is in + // the fact that we allocate some registers to the first couple local slots. + // + // First: handle the locals that are allocated to registers... + for local_reg_idx in + Self::LOCAL_REGISTERS.iter().skip(n_params as usize).take((n_params..n).len()) + { + a.emit_mov(Size::S64, Location::Imm32(0), Location::GPR(*local_reg_idx)); + } + // Second: handle the locals that are allocated to the stack. + let stack_loc_idxs = std::cmp::max(Self::LOCAL_REGISTERS.len() as u32, n_params)..n; + if !stack_loc_idxs.is_empty() { + // Since these assemblies take up to 24 bytes, if more than 2 slots are initialized, then they are smaller. + a.emit_mov( + Size::S64, + Location::Imm64(stack_loc_idxs.len() as u64), + Location::GPR(GPR::RCX), + ); + a.emit_xor(Size::S64, Location::GPR(GPR::RAX), Location::GPR(GPR::RAX)); + a.emit_lea(Size::S64, self.get_local_location(n - 1), Location::GPR(GPR::RDI)); + a.emit_rep_stosq(); + } + } + + pub(crate) fn finalize_locals(&mut self, a: &mut E) { + // Unwind stack to the "save area". + a.emit_lea( + Size::S64, + Location::Memory(GPR::RBP, -(self.save_area_offset.as_ref().unwrap().0 as i32)), + Location::GPR(GPR::RSP), + ); + } + + pub(crate) fn restore_registers( + &mut self, + a: &mut E, + calling_convention: CallingConvention, + local_count: u32, + ) { + if calling_convention == CallingConvention::WindowsFastcall { + // Restore RSI and RDI + a.emit_pop(Size::S64, Location::GPR(GPR::RSI)); + a.emit_pop(Size::S64, Location::GPR(GPR::RDI)); + } + // Restore R15 used by vmctx. + a.emit_pop(Size::S64, Location::GPR(GPR::R15)); + + // Restore callee-saved registers that we used for locals. + for reg in Self::LOCAL_REGISTERS.iter().take(local_count as usize).rev() { + a.emit_pop(Size::S64, Location::GPR(*reg)); + } + } + + pub(crate) fn get_param_location( + idx: usize, + calling_convention: CallingConvention, + ) -> Location { + match calling_convention { + CallingConvention::WindowsFastcall => match idx { + 0 => Location::GPR(GPR::RCX), + 1 => Location::GPR(GPR::RDX), + 2 => Location::GPR(GPR::R8), + 3 => Location::GPR(GPR::R9), + _ => Location::Memory(GPR::RBP, (16 + 32 + (idx - 4) * 8) as i32), + }, + _ => match idx { + 0 => Location::GPR(GPR::RDI), + 1 => Location::GPR(GPR::RSI), + 2 => Location::GPR(GPR::RDX), + 3 => Location::GPR(GPR::RCX), + 4 => Location::GPR(GPR::R8), + 5 => Location::GPR(GPR::R9), + _ => Location::Memory(GPR::RBP, (16 + (idx - 6) * 8) as i32), + }, + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use dynasmrt::x64::X64Relocation; + use dynasmrt::VecAssembler; + type Assembler = VecAssembler; + + #[test] + fn test_release_locations_keep_state_nopanic() { + let mut machine = Machine::new(); + let mut assembler = Assembler::new(0); + let locs = machine.acquire_locations( + &mut assembler, + &(0..10).map(|_| WpType::I32).collect::>(), + false, + ); + + machine.release_locations_keep_state(&mut assembler, &locs); + } +} diff --git a/runtime/unc-vm/compiler-singlepass/src/x64_decl.rs b/runtime/unc-vm/compiler-singlepass/src/x64_decl.rs new file mode 100644 index 000000000..4c7fe8cbb --- /dev/null +++ b/runtime/unc-vm/compiler-singlepass/src/x64_decl.rs @@ -0,0 +1,243 @@ +//! X64 structures. +use unc_vm_compiler::CallingConvention; +use unc_vm_types::Type; + +/// General-purpose registers. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, strum::FromRepr)] +#[repr(u8)] +pub(crate) enum GPR { + /// RAX register + RAX, + /// RCX register + RCX, + /// RDX register + RDX, + /// RBX register + RBX, + /// RSP register + RSP, + /// RBP register + RBP, + /// RSI register + RSI, + /// RDI register + RDI, + /// R8 register + R8, + /// R9 register + R9, + /// R10 register + R10, + /// R11 register + R11, + /// R12 register + R12, + /// R13 register + R13, + /// R14 register + R14, + /// R15 register + R15, +} + +impl GPR { + pub const fn num_gprs() -> usize { + GPR::R15 as usize + 1 + } +} + +/// XMM registers. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, strum::FromRepr)] +#[repr(u8)] +pub(crate) enum XMM { + /// XMM register 0 + XMM0, + /// XMM register 1 + XMM1, + /// XMM register 2 + XMM2, + /// XMM register 3 + XMM3, + /// XMM register 4 + XMM4, + /// XMM register 5 + XMM5, + /// XMM register 6 + XMM6, + /// XMM register 7 + XMM7, + /// XMM register 8 + XMM8, + /// XMM register 9 + XMM9, + /// XMM register 10 + XMM10, + /// XMM register 11 + XMM11, + /// XMM register 12 + XMM12, + /// XMM register 13 + XMM13, + /// XMM register 14 + XMM14, + /// XMM register 15 + XMM15, +} + +impl XMM { + pub const fn num_xmms() -> usize { + XMM::XMM15 as usize + 1 + } +} + +/// A machine register under the x86-64 architecture. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub(crate) enum X64Register { + /// General-purpose registers. + GPR(GPR), + /// XMM (floating point/SIMD) registers. + XMM(XMM), +} + +impl X64Register { + /// Converts a DWARF regnum to X64Register. + pub(crate) fn _from_dwarf_regnum(x: u16) -> Option { + Some(match x { + 0 => X64Register::GPR(GPR::RAX), + 1 => X64Register::GPR(GPR::RDX), + 2 => X64Register::GPR(GPR::RCX), + 3 => X64Register::GPR(GPR::RBX), + 4 => X64Register::GPR(GPR::RSI), + 5 => X64Register::GPR(GPR::RDI), + 6 => X64Register::GPR(GPR::RBP), + 7 => X64Register::GPR(GPR::RSP), + 8 => X64Register::GPR(GPR::R8), + 9 => X64Register::GPR(GPR::R9), + 10 => X64Register::GPR(GPR::R10), + 11 => X64Register::GPR(GPR::R11), + 12 => X64Register::GPR(GPR::R12), + 13 => X64Register::GPR(GPR::R13), + 14 => X64Register::GPR(GPR::R14), + 15 => X64Register::GPR(GPR::R15), + + 17 => X64Register::XMM(XMM::XMM0), + 18 => X64Register::XMM(XMM::XMM1), + 19 => X64Register::XMM(XMM::XMM2), + 20 => X64Register::XMM(XMM::XMM3), + 21 => X64Register::XMM(XMM::XMM4), + 22 => X64Register::XMM(XMM::XMM5), + 23 => X64Register::XMM(XMM::XMM6), + 24 => X64Register::XMM(XMM::XMM7), + _ => return None, + }) + } + + /// Returns the instruction prefix for `movq %this_reg, ?(%rsp)`. + /// + /// To build an instruction, append the memory location as a 32-bit + /// offset to the stack pointer to this prefix. + pub(crate) fn _prefix_mov_to_stack(&self) -> Option<&'static [u8]> { + Some(match *self { + X64Register::GPR(gpr) => match gpr { + GPR::RDI => &[0x48, 0x89, 0xbc, 0x24], + GPR::RSI => &[0x48, 0x89, 0xb4, 0x24], + GPR::RDX => &[0x48, 0x89, 0x94, 0x24], + GPR::RCX => &[0x48, 0x89, 0x8c, 0x24], + GPR::R8 => &[0x4c, 0x89, 0x84, 0x24], + GPR::R9 => &[0x4c, 0x89, 0x8c, 0x24], + _ => return None, + }, + X64Register::XMM(xmm) => match xmm { + XMM::XMM0 => &[0x66, 0x0f, 0xd6, 0x84, 0x24], + XMM::XMM1 => &[0x66, 0x0f, 0xd6, 0x8c, 0x24], + XMM::XMM2 => &[0x66, 0x0f, 0xd6, 0x94, 0x24], + XMM::XMM3 => &[0x66, 0x0f, 0xd6, 0x9c, 0x24], + XMM::XMM4 => &[0x66, 0x0f, 0xd6, 0xa4, 0x24], + XMM::XMM5 => &[0x66, 0x0f, 0xd6, 0xac, 0x24], + XMM::XMM6 => &[0x66, 0x0f, 0xd6, 0xb4, 0x24], + XMM::XMM7 => &[0x66, 0x0f, 0xd6, 0xbc, 0x24], + _ => return None, + }, + }) + } +} + +/// An allocator that allocates registers for function arguments according to the System V ABI. +#[derive(Default)] +pub(crate) struct ArgumentRegisterAllocator { + n_gprs: usize, + n_xmms: usize, +} + +impl ArgumentRegisterAllocator { + /// Allocates a register for argument type `ty`. Returns `None` if no register is available for this type. + pub(crate) fn next( + &mut self, + ty: Type, + calling_convention: CallingConvention, + ) -> Option { + match calling_convention { + CallingConvention::WindowsFastcall => { + static GPR_SEQ: &'static [GPR] = &[GPR::RCX, GPR::RDX, GPR::R8, GPR::R9]; + static XMM_SEQ: &'static [XMM] = &[XMM::XMM0, XMM::XMM1, XMM::XMM2, XMM::XMM3]; + let idx = self.n_gprs + self.n_xmms; + match ty { + Type::I32 | Type::I64 => { + if idx < 4 { + let gpr = GPR_SEQ[idx]; + self.n_gprs += 1; + Some(X64Register::GPR(gpr)) + } else { + None + } + } + Type::F32 | Type::F64 => { + if idx < 4 { + let xmm = XMM_SEQ[idx]; + self.n_xmms += 1; + Some(X64Register::XMM(xmm)) + } else { + None + } + } + _ => todo!("ArgumentRegisterAllocator::next: Unsupported type: {:?}", ty), + } + } + _ => { + static GPR_SEQ: &'static [GPR] = + &[GPR::RDI, GPR::RSI, GPR::RDX, GPR::RCX, GPR::R8, GPR::R9]; + static XMM_SEQ: &'static [XMM] = &[ + XMM::XMM0, + XMM::XMM1, + XMM::XMM2, + XMM::XMM3, + XMM::XMM4, + XMM::XMM5, + XMM::XMM6, + XMM::XMM7, + ]; + match ty { + Type::I32 | Type::I64 => { + if self.n_gprs < GPR_SEQ.len() { + let gpr = GPR_SEQ[self.n_gprs]; + self.n_gprs += 1; + Some(X64Register::GPR(gpr)) + } else { + None + } + } + Type::F32 | Type::F64 => { + if self.n_xmms < XMM_SEQ.len() { + let xmm = XMM_SEQ[self.n_xmms]; + self.n_xmms += 1; + Some(X64Register::XMM(xmm)) + } else { + None + } + } + _ => todo!("ArgumentRegisterAllocator::next: Unsupported type: {:?}", ty), + } + } + } + } +} diff --git a/runtime/unc-vm/compiler-test-derive/Cargo.toml b/runtime/unc-vm/compiler-test-derive/Cargo.toml new file mode 100644 index 000000000..a590ba75d --- /dev/null +++ b/runtime/unc-vm/compiler-test-derive/Cargo.toml @@ -0,0 +1,27 @@ +[lib] +proc-macro = true + +[package] +name = "unc-vm-compiler-test-derive" +version.workspace = true +authors = ["Wasmer Engineering Team ", "Hello Inc "] +edition = "2021" +license = "MIT" +description = "A macro to generate easily tests across compilers and engines" +keywords = ["unsafe", "body", "fn", "safety", "hygiene"] +categories = ["rust-patterns"] +publish = false + +[lints] +workspace = true + +[dependencies] +proc-macro2.workspace = true +quote.workspace = true +syn.workspace = true + +[features] + +[dev-dependencies] +pretty_assertions.workspace = true +trybuild.workspace = true diff --git a/runtime/unc-vm/compiler-test-derive/build.rs b/runtime/unc-vm/compiler-test-derive/build.rs new file mode 100644 index 000000000..105319b4b --- /dev/null +++ b/runtime/unc-vm/compiler-test-derive/build.rs @@ -0,0 +1,11 @@ +fn main() { + if let Ok(os) = std::env::var("CARGO_CFG_TARGET_OS") { + println!("cargo:rustc-env=CFG_TARGET_OS={}", os); + } + if let Ok(os) = std::env::var("CARGO_CFG_TARGET_ARCH") { + println!("cargo:rustc-env=CFG_TARGET_ARCH={}", os); + } + if let Ok(os) = std::env::var("CARGO_CFG_TARGET_ENV") { + println!("cargo:rustc-env=CFG_TARGET_ENV={}", os); + } +} diff --git a/runtime/unc-vm/compiler-test-derive/src/ignores.rs b/runtime/unc-vm/compiler-test-derive/src/ignores.rs new file mode 100644 index 000000000..7bef0590a --- /dev/null +++ b/runtime/unc-vm/compiler-test-derive/src/ignores.rs @@ -0,0 +1,226 @@ +use std::fs::File; +use std::path::PathBuf; + +use std::io::{BufRead, BufReader}; + +pub const CFG_TARGET_OS: &'static str = env!("CFG_TARGET_OS"); +pub const CFG_TARGET_ARCH: &'static str = env!("CFG_TARGET_ARCH"); +pub const CFG_TARGET_ENV: &'static str = env!("CFG_TARGET_ENV"); + +#[derive(Debug, Clone)] +struct IgnorePattern { + os: Option, + arch: Option, + target_env: Option, + engine: Option, + compiler: Option, + pattern_to_ignore: String, +} + +impl IgnorePattern { + fn should_ignore( + &self, + os: &str, + arch: &str, + target_env: &str, + engine: &str, + compiler: &str, + canonical_path: &str, + ) -> bool { + self.os.as_ref().map_or(true, |val| val == os) + && self.arch.as_ref().map_or(true, |val| val == arch) + && self.target_env.as_ref().map_or(true, |val| val == target_env) + && self.engine.as_ref().map_or(true, |val| val == engine) + && self.compiler.as_ref().map_or(true, |val| val == compiler) + && (self.pattern_to_ignore == "*" || canonical_path.contains(&*self.pattern_to_ignore)) + } +} + +#[derive(Debug, Clone)] +pub struct Ignores { + /// The canonical path, and the set of features + patterns: Vec, +} + +impl Ignores { + /// If the path matches any of the paths on the list + pub fn should_ignore( + &self, + os: &str, + arch: &str, + target_env: &str, + engine: &str, + compiler: &str, + canonical_path: &str, + ) -> bool { + self.patterns.iter().any(|p| { + // println!(" -> {:?}", p); + p.should_ignore(os, arch, target_env, engine, compiler, canonical_path) + }) + } + + pub fn should_ignore_host(&self, engine: &str, compiler: &str, canonical_path: &str) -> bool { + self.should_ignore( + CFG_TARGET_OS, + CFG_TARGET_ARCH, + CFG_TARGET_ENV, + engine, + compiler, + canonical_path, + ) + } + + /// Build a Ignore structure from a file path + pub fn build_from_path(path: PathBuf) -> Ignores { + let file = File::open(path).unwrap(); + let reader = BufReader::new(file); + let mut patterns = Vec::new(); + + for (i, line) in reader.lines().enumerate() { + let line = line.unwrap(); + // If the line has a `#` we discard all the content that comes after + let line = if line.contains('#') { + let l: Vec<&str> = line.splitn(2, '#').collect(); + l[0].to_string() + } else { + line + }; + + let line = line.trim().to_string(); + + // If the lines contains ` ` it means the test should be ignored + // on the features exposed + if line.contains(" ") { + let l: Vec<&str> = line.splitn(2, " ").collect(); + let mut os: Option = None; + let mut arch: Option = None; + let mut target_env: Option = None; + let mut engine: Option = None; + let mut compiler: Option = None; + for alias in l[0].trim().split("+") { + match alias { + // Operating Systems + "windows" | "macos" | "linux" => { + os = Some(alias.to_string()); + } + // Environments + "musl" => { + target_env = Some(alias.to_string()); + } + // Chipset architectures + "aarch64" | "x86" | "x64" => { + arch = Some(alias.to_string()); + } + // Engines + "universal" | "dylib" => { + engine = Some(alias.to_string()); + } + // Compilers + "singlepass" => { + compiler = Some(alias.to_string()); + } + other => { + panic!("Alias {:?} not currently supported (defined in ignores.txt in line {})", other, i+1); + } + } + } + let pattern_to_ignore = l[1].trim().to_string(); + patterns.push(IgnorePattern { + os, + arch, + target_env, + engine, + compiler, + pattern_to_ignore, + }); + } else { + if line.is_empty() { + continue; + } + patterns.push(IgnorePattern { + os: None, + arch: None, + target_env: None, + engine: None, + compiler: None, + pattern_to_ignore: line, + }); + }; + } + Ignores { patterns } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn features_match() -> Result<(), ()> { + assert!(IgnorePattern { + os: None, + arch: None, + target_env: None, + engine: None, + compiler: None, + pattern_to_ignore: "*".to_string() + } + .should_ignore( + "unknown", + "unknown", + "", + "engine", + "compiler", + "some::random::text" + )); + assert!(IgnorePattern { + os: None, + arch: None, + target_env: None, + engine: None, + compiler: None, + pattern_to_ignore: "some::random".to_string() + } + .should_ignore( + "unknown", + "unknown", + "", + "engine", + "compiler", + "some::random::text" + )); + assert!(!IgnorePattern { + os: Some("macos".to_string()), + arch: None, + target_env: None, + engine: None, + compiler: None, + pattern_to_ignore: "other".to_string() + } + .should_ignore( + "unknown", + "unknown", + "", + "engine", + "compiler", + "some::random::text" + )); + assert!(!IgnorePattern { + os: Some("macos".to_string()), + arch: None, + target_env: None, + engine: Some("universal".to_string()), + compiler: None, + pattern_to_ignore: "other".to_string() + } + .should_ignore( + "macos", + "unknown", + "", + "universal", + "compiler", + "some::random::text" + )); + Ok(()) + } +} diff --git a/runtime/unc-vm/compiler-test-derive/src/lib.rs b/runtime/unc-vm/compiler-test-derive/src/lib.rs new file mode 100644 index 000000000..16b6979e3 --- /dev/null +++ b/runtime/unc-vm/compiler-test-derive/src/lib.rs @@ -0,0 +1,126 @@ +use proc_macro::TokenStream; +use quote::quote; +use std::path::PathBuf; +use syn::parse; +use syn::*; + +mod ignores; + +// Reimplement parse_macro_input to use the imported `parse` +// function. This way parse_macro_input will parse a TokenStream2 when +// unit-testing. +macro_rules! parse_macro_input { + ( + $token_stream:ident as $T:ty + ) => { + match parse::<$T>($token_stream) { + Ok(data) => data, + Err(err) => { + return TokenStream::from(err.to_compile_error()); + } + } + }; + + ( + $token_stream:ident + ) => { + parse_macro_input!($token_stream as _) + }; +} + +#[proc_macro_attribute] +pub fn compiler_test(attrs: TokenStream, input: TokenStream) -> TokenStream { + let path: Option = parse::(attrs).ok(); + let mut my_fn: ItemFn = parse_macro_input!(input as ItemFn); + let fn_name = &my_fn.sig.ident; + + // Let's build the ignores to append an `#[ignore]` macro to the + // autogenerated tests in case the test appears in the `ignores.txt` path; + + let mut ignores_txt_path = PathBuf::new(); + ignores_txt_path.push(env!("CARGO_MANIFEST_DIR")); + ignores_txt_path.push("../tests/ignores.txt"); + + let ignores = crate::ignores::Ignores::build_from_path(ignores_txt_path); + + let should_ignore = |test_name: &str, compiler_name: &str, engine_name: &str| { + let compiler_name = compiler_name.to_lowercase(); + let engine_name = engine_name.to_lowercase(); + // We construct the path manually because we can't get the + // source_file location from the `Span` (it's only available in nightly) + let full_path = + format!("{}::{}::{}::{}", quote! { #path }, test_name, compiler_name, engine_name) + .replace(" ", ""); + let should_ignore = ignores.should_ignore_host(&engine_name, &compiler_name, &full_path); + // println!("{} -> Should ignore: {}", full_path, should_ignore); + return should_ignore; + }; + let construct_engine_test = |func: &::syn::ItemFn, + compiler_name: &str, + engine_name: &str, + engine_feature_name: &str| + -> ::proc_macro2::TokenStream { + let config_compiler = ::quote::format_ident!("{}", compiler_name); + let config_engine = ::quote::format_ident!("{}", engine_name); + let test_name = ::quote::format_ident!("{}", engine_name.to_lowercase()); + let mut new_sig = func.sig.clone(); + let attrs = func.attrs.clone().iter().fold(quote! {}, |acc, new| quote! {#acc #new}); + new_sig.ident = test_name; + new_sig.inputs = ::syn::punctuated::Punctuated::new(); + let f = quote! { + #[test_log::test] + #attrs + #[cfg(feature = #engine_feature_name)] + #new_sig { + #fn_name(crate::Config::new(crate::Engine::#config_engine, crate::Compiler::#config_compiler)) + } + }; + if should_ignore(&func.sig.ident.to_string().replace("r#", ""), compiler_name, engine_name) + && !cfg!(test) + { + quote! { + #[ignore] + #f + } + } else { + f + } + }; + + let construct_compiler_test = + |func: &::syn::ItemFn, compiler_name: &str| -> ::proc_macro2::TokenStream { + let mod_name = ::quote::format_ident!("{}", compiler_name.to_lowercase()); + let universal_engine_test = + construct_engine_test(func, compiler_name, "Universal", "universal"); + let dylib_engine_test = construct_engine_test(func, compiler_name, "Dylib", "dylib"); + let compiler_name_lowercase = compiler_name.to_lowercase(); + + quote! { + #[cfg(feature = #compiler_name_lowercase)] + mod #mod_name { + use super::*; + + #universal_engine_test + #dylib_engine_test + } + } + }; + + let singlepass_compiler_test = construct_compiler_test(&my_fn, "Singlepass"); + + // We remove the method decorators + my_fn.attrs = vec![]; + + let x = quote! { + #[cfg(test)] + mod #fn_name { + use super::*; + + #[allow(unused)] + #my_fn + + #singlepass_compiler_test + } + }; + x.into() +} diff --git a/runtime/unc-vm/compiler/Cargo.toml b/runtime/unc-vm/compiler/Cargo.toml new file mode 100644 index 000000000..2caaac9f9 --- /dev/null +++ b/runtime/unc-vm/compiler/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "unc-vm-compiler" +version.workspace = true +description = "Base compiler abstraction for Wasmer WebAssembly runtime" +categories = ["wasm", "no-std"] +keywords = ["wasm", "webassembly", "compiler"] +authors = ["Wasmer Engineering Team ", "Hello Inc "] +repository.workspace = true +license = "MIT OR Apache-2.0 WITH LLVM-exception" +readme = "README.md" +edition = "2021" +publish = true + +[lints] +workspace = true + +[dependencies] +finite-wasm.workspace = true +unc-vm-vm.workspace = true +unc-vm-types.workspace = true +wasmparser = { version = "0.99.0", default-features = false } +target-lexicon.workspace = true +enumset.workspace = true +hashbrown = { workspace = true, optional = true } +thiserror.workspace = true +smallvec.workspace = true +rkyv.workspace = true +tracing.workspace = true + +[features] +default = [] + +[badges] +maintenance = { status = "experimental" } diff --git a/runtime/unc-vm/compiler/LICENSE b/runtime/unc-vm/compiler/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/runtime/unc-vm/compiler/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/runtime/unc-vm/compiler/README.md b/runtime/unc-vm/compiler/README.md new file mode 100644 index 000000000..ef3cf516d --- /dev/null +++ b/runtime/unc-vm/compiler/README.md @@ -0,0 +1,57 @@ +# `unc-vm-compiler` + +This crate is a fork of `wasmer-compiler`. A significant number of things +changed, but the documentation is not up-to-date yet. + +This crate is the base for Compiler implementations. + +It performs the translation from a Wasm module into a basic +`ModuleInfo`, but leaves the Wasm function bytecode translation to the +compiler implementor. + +Here are some of the Compilers provided by Wasmer: + +* [Singlepass](https://github.com/wasmerio/wasmer/tree/master/lib/compiler-singlepass), + +## How to create a compiler + +To create a compiler, one needs to implement two traits: + +1. `CompilerConfig`, that configures and creates a new compiler, +2. `Compiler`, the compiler itself that will compile a module. + +```rust +/// The compiler configuration options. +pub trait CompilerConfig { + /// Gets the custom compiler config + fn compiler(&self) -> Box; +} + +/// An implementation of a compiler from parsed WebAssembly module to compiled native code. +pub trait Compiler { + /// Compiles a parsed module. + /// + /// It returns the [`Compilation`] or a [`CompileError`]. + fn compile_module<'data, 'module>( + &self, + target: &Target, + compile_info: &'module CompileModuleInfo, + // The list of function bodies + function_body_inputs: PrimaryMap>, + instrumentation: &finite_wasm::Module, + ) -> Result; +} +``` + +## Acknowledgments + +This project borrowed some of the code strucutre from the +[`cranelift-wasm`] crate, however it's been adapted to not depend on +any specific IR and be abstract of any compiler. + +Please check [Wasmer `ATTRIBUTIONS`] to further see licenses and other +attributions of the project. + + +[`cranelift-wasm`]: https://crates.io/crates/cranelift-wasm +[Wasmer `ATTRIBUTIONS`]: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md diff --git a/runtime/unc-vm/compiler/src/address_map.rs b/runtime/unc-vm/compiler/src/address_map.rs new file mode 100644 index 000000000..4f415827b --- /dev/null +++ b/runtime/unc-vm/compiler/src/address_map.rs @@ -0,0 +1,40 @@ +//! Data structures to provide transformation of the source +// addresses of a WebAssembly module into the native code. + +use crate::lib::std::vec::Vec; +use crate::sourceloc::SourceLoc; + +/// Single source location to generated address mapping. +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, PartialEq, Eq)] +pub struct InstructionAddressMap { + /// Original source location. + pub srcloc: SourceLoc, + + /// Generated instructions offset. + pub code_offset: usize, + + /// Generated instructions length. + pub code_len: usize, +} + +/// Function and its instructions addresses mappings. +#[derive( + rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, PartialEq, Eq, Default, +)] +pub struct FunctionAddressMap { + /// Instructions maps. + /// The array is sorted by the InstructionAddressMap::code_offset field. + pub instructions: Vec, + + /// Function start source location (normally declaration). + pub start_srcloc: SourceLoc, + + /// Function end source location. + pub end_srcloc: SourceLoc, + + /// Generated function body offset if applicable, otherwise 0. + pub body_offset: usize, + + /// Generated function body length. + pub body_len: usize, +} diff --git a/runtime/unc-vm/compiler/src/compiler.rs b/runtime/unc-vm/compiler/src/compiler.rs new file mode 100644 index 000000000..de0d9c1e7 --- /dev/null +++ b/runtime/unc-vm/compiler/src/compiler.rs @@ -0,0 +1,164 @@ +//! This module mainly outputs the `Compiler` trait that custom +//! compilers will need to implement. + +use crate::error::CompileError; +use crate::function::Compilation; +use crate::lib::std::boxed::Box; +use crate::module::CompileModuleInfo; +use crate::target::Target; +use crate::FunctionBodyData; +use crate::ModuleTranslationState; +use crate::SectionIndex; +use unc_vm_types::entity::PrimaryMap; +use unc_vm_types::{Features, FunctionIndex, LocalFunctionIndex, SignatureIndex}; +use wasmparser::{Validator, WasmFeatures}; + +/// The compiler configuration options. +pub trait CompilerConfig { + /// Enable Position Independent Code (PIC). + /// + /// This is required for shared object generation (Native Engine), + /// but will make the JIT Engine to fail, since PIC is not yet + /// supported in the JIT linking phase. + fn enable_pic(&mut self) { + // By default we do nothing, each backend will need to customize this + // in case they do something special for emitting PIC code. + } + + /// Enable compiler IR verification. + /// + /// For compilers capable of doing so, this enables internal consistency + /// checking. + fn enable_verifier(&mut self) { + // By default we do nothing, each backend will need to customize this + // in case they create an IR that they can verify. + } + + /// Enable NaN canonicalization. + /// + /// NaN canonicalization is useful when trying to run WebAssembly + /// deterministically across different architectures. + #[deprecated(note = "Please use the canonicalize_nans instead")] + fn enable_nan_canonicalization(&mut self) { + // By default we do nothing, each backend will need to customize this + // in case they create an IR that they can verify. + } + + /// Enable NaN canonicalization. + /// + /// NaN canonicalization is useful when trying to run WebAssembly + /// deterministically across different architectures. + fn canonicalize_nans(&mut self, _enable: bool) { + // By default we do nothing, each backend will need to customize this + // in case they create an IR that they can verify. + } + + /// Gets the custom compiler config + fn compiler(self: Box) -> Box; + + /// Gets the default features for this compiler in the given target + fn default_features_for_target(&self, _target: &Target) -> Features { + Features::default() + } +} + +impl From for Box +where + T: CompilerConfig + 'static, +{ + fn from(other: T) -> Self { + Box::new(other) + } +} + +/// An implementation of a Compiler from parsed WebAssembly module to Compiled native code. +pub trait Compiler: Send { + /// Validates a module. + /// + /// It returns the a succesful Result in case is valid, `CompileError` in case is not. + fn validate_module<'data>( + &self, + features: &Features, + data: &'data [u8], + ) -> Result<(), CompileError> { + let wasm_features = WasmFeatures { + bulk_memory: features.bulk_memory, + threads: features.threads, + reference_types: features.reference_types, + multi_value: features.multi_value, + simd: features.simd, + tail_call: features.tail_call, + multi_memory: features.multi_memory, + memory64: features.memory64, + exceptions: features.exceptions, + floats: true, + component_model: false, + extended_const: false, + mutable_global: features.mutable_global, + relaxed_simd: false, + saturating_float_to_int: features.saturating_float_to_int, + sign_extension: features.sign_extension, + memory_control: false, + }; + let mut validator = Validator::new_with_features(wasm_features); + validator.validate_all(data).map_err(|e| CompileError::Validate(format!("{}", e)))?; + Ok(()) + } + + /// Compiles a parsed module. + /// + /// It returns the [`Compilation`] or a [`CompileError`]. + fn compile_module<'data, 'module>( + &self, + target: &Target, + module: &'module CompileModuleInfo, + // The list of function bodies + function_body_inputs: PrimaryMap>, + tunables: &dyn unc_vm_vm::Tunables, + instrumentation: &finite_wasm::AnalysisOutcome, + ) -> Result; + + /// Compiles a module into a native object file. + /// + /// It returns the bytes as a `&[u8]` or a [`CompileError`]. + fn experimental_native_compile_module<'data, 'module>( + &self, + _target: &Target, + _module: &'module CompileModuleInfo, + _module_translation: &ModuleTranslationState, + // The list of function bodies + _function_body_inputs: &PrimaryMap>, + _symbol_registry: &dyn SymbolRegistry, + // The metadata to inject into the unc_vm_metadata section of the object file. + _unc_vm_metadata: &[u8], + ) -> Option, CompileError>> { + None + } +} + +/// The kinds of unc_vm_types objects that might be found in a native object file. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Symbol { + /// A function defined in the wasm. + LocalFunction(LocalFunctionIndex), + + /// A wasm section. + Section(SectionIndex), + + /// The function call trampoline for a given signature. + FunctionCallTrampoline(SignatureIndex), + + /// The dynamic function trampoline for a given function. + DynamicFunctionTrampoline(FunctionIndex), +} + +/// This trait facilitates symbol name lookups in a native object file. +pub trait SymbolRegistry: Send + Sync { + /// Given a `Symbol` it returns the name for that symbol in the object file + fn symbol_to_name(&self, symbol: Symbol) -> String; + + /// Given a name it returns the `Symbol` for that name in the object file + /// + /// This function is the inverse of [`SymbolRegistry::symbol_to_name`] + fn name_to_symbol(&self, name: &str) -> Option; +} diff --git a/runtime/unc-vm/compiler/src/error.rs b/runtime/unc-vm/compiler/src/error.rs new file mode 100644 index 000000000..2572312ba --- /dev/null +++ b/runtime/unc-vm/compiler/src/error.rs @@ -0,0 +1,148 @@ +use crate::lib::std::string::String; + +// Compilation Errors + +/// The WebAssembly.CompileError object indicates an error during +/// WebAssembly decoding or validation. +/// +/// This is based on the [Wasm Compile Error][compile-error] API. +/// +/// [compiler-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/CompileError +#[derive(Debug, thiserror::Error)] +pub enum CompileError { + /// A Wasm translation error occured. + #[error("WebAssembly translation error: {0}")] + Wasm(WasmError), + + /// A compilation error occured. + #[error("Compilation error: {0}")] + Codegen(String), + + /// The module did not pass validation. + #[error("Validation error: {0}")] + Validate(String), + + /// Finite-wasm failed to handle the module. + #[error("Finite-wasm analysis error: {0}")] + Analyze(finite_wasm::Error), + + /// The compiler doesn't support a Wasm feature + #[error("Feature {0} is not yet supported")] + UnsupportedFeature(String), + + /// The compiler cannot compile for the given target. + /// This can refer to the OS, the chipset or any other aspect of the target system. + #[error("The target {0} is not yet supported (see https://docs.wasmer.io/ecosystem/wasmer/wasmer-features)")] + UnsupportedTarget(String), + + /// Insufficient resources available for execution. + #[error("Insufficient resources: {0}")] + Resource(String), + + /// Cannot downcast the engine to a specific type. + #[error("data offset is out of bounds")] + InvalidOffset, +} + +impl From for CompileError { + fn from(original: WasmError) -> Self { + Self::Wasm(original) + } +} + +/// A error in the middleware. +#[derive(Debug, thiserror::Error)] +#[error("Error in middleware {name}: {message}")] +pub struct MiddlewareError { + /// The name of the middleware where the error was created + pub name: String, + /// The error message + pub message: String, +} + +impl MiddlewareError { + /// Create a new `MiddlewareError` + pub fn new, B: Into>(name: A, message: B) -> Self { + Self { name: name.into(), message: message.into() } + } +} + +/// A WebAssembly translation error. +/// +/// When a WebAssembly function can't be translated, one of these error codes will be returned +/// to describe the failure. +#[derive(Debug, thiserror::Error)] +pub enum WasmError { + /// The input WebAssembly code is invalid. + /// + /// This error code is used by a WebAssembly translator when it encounters invalid WebAssembly + /// code. This should never happen for validated WebAssembly code. + #[error("Invalid input WebAssembly code at offset {offset}: {message}")] + InvalidWebAssembly { + /// A string describing the validation error. + message: String, + /// The bytecode offset where the error occurred. + offset: usize, + }, + + /// A feature used by the WebAssembly code is not supported by the embedding environment. + /// + /// Embedding environments may have their own limitations and feature restrictions. + #[error("Unsupported feature: {0}")] + Unsupported(String), + + /// An implementation limit was exceeded. + #[error("Implementation limit exceeded")] + ImplLimitExceeded, + + /// An error from the middleware error. + #[error("{0}")] + Middleware(MiddlewareError), + + /// A generic error. + #[error("{0}")] + Generic(String), +} + +impl From for WasmError { + fn from(original: MiddlewareError) -> Self { + Self::Middleware(original) + } +} + +/// The error that can happen while parsing a `str` +/// to retrieve a [`CpuFeature`](crate::target::CpuFeature). +#[derive(Debug, thiserror::Error)] +pub enum ParseCpuFeatureError { + /// The provided string feature doesn't exist + #[error("CpuFeature {0} not recognized")] + Missing(String), +} + +/// A convenient alias for a `Result` that uses `WasmError` as the error type. +pub type WasmResult = Result; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn middleware_error_can_be_created() { + let msg = String::from("Something went wrong"); + let error = MiddlewareError::new("manipulator3000", msg); + assert_eq!(error.name, "manipulator3000"); + assert_eq!(error.message, "Something went wrong"); + } + + #[test] + fn middleware_error_be_converted_to_wasm_error() { + let error = WasmError::from(MiddlewareError::new("manipulator3000", "foo")); + match error { + WasmError::Middleware(MiddlewareError { name, message }) => { + assert_eq!(name, "manipulator3000"); + assert_eq!(message, "foo"); + } + err => panic!("Unexpected error: {:?}", err), + } + } +} diff --git a/runtime/unc-vm/compiler/src/function.rs b/runtime/unc-vm/compiler/src/function.rs new file mode 100644 index 000000000..449409444 --- /dev/null +++ b/runtime/unc-vm/compiler/src/function.rs @@ -0,0 +1,170 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! A `Compilation` contains the compiled function bodies for a WebAssembly +//! module (`CompiledFunction`). + +use crate::lib::std::vec::Vec; +use crate::section::{CustomSection, SectionIndex}; +use crate::trap::TrapInformation; +use crate::{FunctionAddressMap, JumpTableOffsets, Relocation}; +use unc_vm_types::entity::PrimaryMap; +use unc_vm_types::{FunctionIndex, LocalFunctionIndex, SignatureIndex}; + +/// The frame info for a Compiled function. +/// +/// This structure is only used for reconstructing +/// the frame information after a `Trap`. +#[derive( + rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, PartialEq, Eq, Default, +)] +pub struct CompiledFunctionFrameInfo { + /// The traps (in the function body). + /// + /// Code offsets of the traps MUST be in ascending order. + pub traps: Vec, + + /// The address map. + pub address_map: FunctionAddressMap, +} + +/// The function body. +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, PartialEq, Eq)] +pub struct FunctionBody { + /// The function body bytes. + pub body: Vec, +} + +/// See [`FunctionBody`]. +#[derive(Clone, Copy)] +pub struct FunctionBodyRef<'a> { + /// Function body bytes. + pub body: &'a [u8], +} + +impl<'a> From<&'a FunctionBody> for FunctionBodyRef<'a> { + fn from(body: &'a FunctionBody) -> Self { + FunctionBodyRef { body: &*body.body } + } +} + +impl<'a> From<&'a ArchivedFunctionBody> for FunctionBodyRef<'a> { + fn from(body: &'a ArchivedFunctionBody) -> Self { + FunctionBodyRef { body: &*body.body } + } +} + +/// The result of compiling a WebAssembly function. +/// +/// This structure only have the compiled information data +/// (function bytecode body, relocations, traps, jump tables +/// and unwind information). +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, PartialEq, Eq)] +pub struct CompiledFunction { + /// The function body. + pub body: FunctionBody, + + /// The relocations (in the body) + pub relocations: Vec, + + /// The jump tables offsets (in the body). + pub jt_offsets: JumpTableOffsets, + + /// The frame information. + pub frame_info: CompiledFunctionFrameInfo, +} + +/// The compiled functions map (index in the Wasm -> function) +pub type Functions = PrimaryMap; + +/// The custom sections for a Compilation. +pub type CustomSections = PrimaryMap; + +/// The DWARF information for this Compilation. +/// +/// It is used for retrieving the unwind information once an exception +/// happens. +/// In the future this structure may also hold other information useful +/// for debugging. +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, PartialEq, Eq, Clone)] +pub struct Dwarf { + /// The section index in the [`Compilation`] that corresponds to the exception frames. + /// [Learn + /// more](https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html). + pub eh_frame: SectionIndex, +} + +impl Dwarf { + /// Creates a `Dwarf` struct with the corresponding indices for its sections + pub fn new(eh_frame: SectionIndex) -> Self { + Self { eh_frame } + } +} + +/// Trampolines section used by ARM short jump (26bits) +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, PartialEq, Eq, Clone)] +pub struct TrampolinesSection { + /// SectionIndex for the actual Trampolines code + pub section_index: SectionIndex, + /// Number of jump slots in the section + pub slots: usize, + /// Slot size + pub size: usize, +} + +impl TrampolinesSection { + /// Creates a `Trampolines` struct with the indice for its section, and number of slots and size of slot + pub fn new(section_index: SectionIndex, slots: usize, size: usize) -> Self { + Self { section_index, slots, size } + } +} + +/// The result of compiling a WebAssembly module's functions. +#[derive(Debug, PartialEq, Eq)] +pub struct Compilation { + /// Compiled code for the function bodies. + pub functions: Functions, + + /// Custom sections for the module. + /// It will hold the data, for example, for constants used in a + /// function, global variables, rodata_64, hot/cold function partitioning, ... + pub custom_sections: CustomSections, + + /// Trampolines to call a function defined locally in the wasm via a + /// provided `Vec` of values. + /// + /// This allows us to call easily Wasm functions, such as: + /// + /// ```ignore + /// let func = instance.exports.get_function("my_func"); + /// func.call(&[Value::I32(1)]); + /// ``` + pub function_call_trampolines: PrimaryMap, + + /// Trampolines to call a dynamic function defined in + /// a host, from a Wasm module. + /// + /// This allows us to create dynamic Wasm functions, such as: + /// + /// ```ignore + /// fn my_func(values: &[Val]) -> Result, RuntimeError> { + /// // do something + /// } + /// + /// let my_func_type = FunctionType::new(vec![Type::I32], vec![Type::I32]); + /// let imports = imports!{ + /// "namespace" => { + /// "my_func" => Function::new(&store, my_func_type, my_func), + /// } + /// } + /// ``` + /// + /// Note: Dynamic function trampolines are only compiled for imported function types. + pub dynamic_function_trampolines: PrimaryMap, + + /// Section ids corresponding to the Dwarf debug info + pub debug: Option, + + /// Trampolines for the arch that needs it + pub trampolines: Option, +} diff --git a/runtime/unc-vm/compiler/src/jump_table.rs b/runtime/unc-vm/compiler/src/jump_table.rs new file mode 100644 index 000000000..25fe465b9 --- /dev/null +++ b/runtime/unc-vm/compiler/src/jump_table.rs @@ -0,0 +1,36 @@ +//! A jump table is a method of transferring program control (branching) +//! to another part of a program (or a different program that may have +//! been dynamically loaded) using a table of branch or jump instructions. +//! +//! [Learn more](https://en.wikipedia.org/wiki/Branch_table). + +use super::CodeOffset; +use unc_vm_types::entity::{entity_impl, SecondaryMap}; + +/// An opaque reference to a [jump table](https://en.wikipedia.org/wiki/Branch_table). +/// +/// `JumpTable`s are used for indirect branching and are specialized for dense, +/// 0-based jump offsets. +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord))] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct JumpTable(u32); + +entity_impl!(JumpTable, "jt"); +entity_impl!(ArchivedJumpTable, "jt"); + +impl JumpTable { + /// Create a new jump table reference from its number. + /// + /// This method is for use by the parser. + pub fn with_number(n: u32) -> Option { + if n < u32::max_value() { + Some(Self(n)) + } else { + None + } + } +} + +/// Code offsets for Jump Tables. +pub type JumpTableOffsets = SecondaryMap; diff --git a/runtime/unc-vm/compiler/src/lib.rs b/runtime/unc-vm/compiler/src/lib.rs new file mode 100644 index 000000000..4584ede2c --- /dev/null +++ b/runtime/unc-vm/compiler/src/lib.rs @@ -0,0 +1,82 @@ +//! The `unc_vm-compiler` crate provides the necessary abstractions +//! to create a compiler. +//! +//! It provides an universal way of parsing a module via `wasmparser`, +//! while giving the responsibility of compiling specific function +//! WebAssembly bodies to the `Compiler` implementation. + +#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] +#![warn(unused_import_braces)] +#![deny(unstable_features)] +#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] +#![cfg_attr( + feature = "cargo-clippy", + warn( + clippy::float_arithmetic, + clippy::mut_mut, + clippy::nonminimal_bool, + clippy::map_unwrap_or, + clippy::print_stdout, + clippy::unicode_not_nfc, + clippy::use_self + ) +)] + +mod lib { + pub mod std { + pub use std::{borrow, boxed, fmt, str, string, sync, vec}; + } +} + +mod address_map; +mod compiler; +mod error; +mod function; +mod jump_table; +mod module; +mod relocation; +mod target; +mod trap; +#[macro_use] +mod translator; +mod section; +mod sourceloc; + +pub use crate::address_map::{FunctionAddressMap, InstructionAddressMap}; +pub use crate::compiler::{Compiler, CompilerConfig, Symbol, SymbolRegistry}; +pub use crate::error::{ + CompileError, MiddlewareError, ParseCpuFeatureError, WasmError, WasmResult, +}; +pub use crate::function::{ + Compilation, CompiledFunction, CompiledFunctionFrameInfo, CustomSections, Dwarf, FunctionBody, + FunctionBodyRef, Functions, TrampolinesSection, +}; +pub use crate::jump_table::{JumpTable, JumpTableOffsets}; +pub use crate::module::CompileModuleInfo; +pub use crate::relocation::{Relocation, RelocationKind, RelocationTarget, Relocations}; +pub use crate::section::{ + CustomSection, CustomSectionProtection, CustomSectionRef, SectionBody, SectionIndex, +}; +pub use crate::sourceloc::SourceLoc; +pub use crate::target::{ + Architecture, BinaryFormat, CallingConvention, CpuFeature, Endianness, OperatingSystem, + PointerWidth, Target, Triple, +}; +pub use crate::translator::{ + translate_module, wptype_to_type, FunctionBodyData, FunctionReader, ModuleEnvironment, + ModuleTranslationState, +}; +pub use crate::trap::TrapInformation; +pub use unc_vm_types::Features; + +/// wasmparser is exported as a module to slim compiler dependencies +pub use wasmparser; + +/// Offset in bytes from the beginning of the function. +pub type CodeOffset = u32; + +/// Addend to add to the symbol value. +pub type Addend = i64; + +/// Version number of this crate. +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/runtime/unc-vm/compiler/src/module.rs b/runtime/unc-vm/compiler/src/module.rs new file mode 100644 index 000000000..43a734c00 --- /dev/null +++ b/runtime/unc-vm/compiler/src/module.rs @@ -0,0 +1,24 @@ +use crate::lib::std::sync::Arc; +use unc_vm_types::entity::PrimaryMap; +use unc_vm_types::{Features, MemoryIndex, ModuleInfo, TableIndex}; +use unc_vm_vm::{MemoryStyle, TableStyle}; + +/// The required info for compiling a module. +/// +/// This differs from [`ModuleInfo`] because it have extra info only +/// possible after translation (such as the features used for compiling, +/// or the `MemoryStyle` and `TableStyle`). +#[derive(Debug, PartialEq, Eq, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +pub struct CompileModuleInfo { + /// The features used for compiling the module + pub features: Features, + /// The module information + pub module: Arc, + /// The memory styles used for compiling. + /// + /// The compiler will emit the most optimal code based + /// on the memory style (static or dynamic) chosen. + pub memory_styles: PrimaryMap, + /// The table plans used for compiling. + pub table_styles: PrimaryMap, +} diff --git a/runtime/unc-vm/compiler/src/relocation.rs b/runtime/unc-vm/compiler/src/relocation.rs new file mode 100644 index 000000000..e765333fa --- /dev/null +++ b/runtime/unc-vm/compiler/src/relocation.rs @@ -0,0 +1,169 @@ +//! Relocation is the process of assigning load addresses for position-dependent +//! code and data of a program and adjusting the code and data to reflect the +//! assigned addresses. +//! +//! [Learn more](https://en.wikipedia.org/wiki/Relocation_(computing)). +//! +//! Each time a `Compiler` compiles a WebAssembly function (into machine code), +//! it also attaches if there are any relocations that need to be patched into +//! the generated machine code, so a given frontend (JIT or native) can +//! do the corresponding work to run it. + +use crate::lib::std::fmt; +use crate::lib::std::vec::Vec; +use crate::section::SectionIndex; +use crate::{Addend, CodeOffset, JumpTable}; +use unc_vm_types::entity::PrimaryMap; +use unc_vm_types::LocalFunctionIndex; +use unc_vm_vm::libcalls::LibCall; + +/// Relocation kinds for every ISA. +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Copy, Clone, Debug, PartialEq, Eq)] +pub enum RelocationKind { + /// absolute 4-byte + Abs4, + /// absolute 8-byte + Abs8, + /// x86 PC-relative 4-byte + X86PCRel4, + /// x86 PC-relative 8-byte + X86PCRel8, + /// x86 PC-relative 4-byte offset to trailing rodata + X86PCRelRodata4, + /// x86 call to PC-relative 4-byte + X86CallPCRel4, + /// x86 call to PLT-relative 4-byte + X86CallPLTRel4, + /// x86 GOT PC-relative 4-byte + X86GOTPCRel4, + /// Arm32 call target + Arm32Call, + /// Arm64 call target + Arm64Call, + /// Arm64 movk/z part 0 + Arm64Movw0, + /// Arm64 movk/z part 1 + Arm64Movw1, + /// Arm64 movk/z part 2 + Arm64Movw2, + /// Arm64 movk/z part 3 + Arm64Movw3, + // /// RISC-V call target + // RiscvCall, + /// Elf x86_64 32 bit signed PC relative offset to two GOT entries for GD symbol. + ElfX86_64TlsGd, + // /// Mach-O x86_64 32 bit signed PC relative offset to a `__thread_vars` entry. + // MachOX86_64Tlv, +} + +impl fmt::Display for RelocationKind { + /// Display trait implementation drops the arch, since its used in contexts where the arch is + /// already unambiguous, e.g. clif syntax with isa specified. In other contexts, use Debug. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::Abs4 => write!(f, "Abs4"), + Self::Abs8 => write!(f, "Abs8"), + Self::X86PCRel4 => write!(f, "PCRel4"), + Self::X86PCRel8 => write!(f, "PCRel8"), + Self::X86PCRelRodata4 => write!(f, "PCRelRodata4"), + Self::X86CallPCRel4 => write!(f, "CallPCRel4"), + Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"), + Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"), + Self::Arm32Call | Self::Arm64Call => write!(f, "Call"), + Self::Arm64Movw0 => write!(f, "Arm64MovwG0"), + Self::Arm64Movw1 => write!(f, "Arm64MovwG1"), + Self::Arm64Movw2 => write!(f, "Arm64MovwG2"), + Self::Arm64Movw3 => write!(f, "Arm64MovwG3"), + Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"), + // Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"), + } + } +} + +/// A record of a relocation to perform. +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, PartialEq, Eq)] +pub struct Relocation { + /// The relocation kind. + pub kind: RelocationKind, + /// Relocation target. + pub reloc_target: RelocationTarget, + /// The offset where to apply the relocation. + pub offset: CodeOffset, + /// The addend to add to the relocation value. + pub addend: Addend, +} + +/// Destination function. Can be either user function or some special one, like `memory.grow`. +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Copy, Clone, PartialEq, Eq)] +pub enum RelocationTarget { + /// A relocation to a function defined locally in the wasm (not an imported one). + LocalFunc(LocalFunctionIndex), + /// A compiler-generated libcall. + LibCall(LibCall), + /// Jump table index. + JumpTable(LocalFunctionIndex, JumpTable), + /// Custom sections generated by the compiler + CustomSection(SectionIndex), +} + +impl Relocation { + /// Given a function start address, provide the relocation relative + /// to that address. + /// + /// The function returns the relocation address and the delta. + pub fn for_address(&self, start: usize, target_func_address: u64) -> (usize, u64) { + match self.kind { + RelocationKind::Abs8 + | RelocationKind::Arm64Movw0 + | RelocationKind::Arm64Movw1 + | RelocationKind::Arm64Movw2 + | RelocationKind::Arm64Movw3 => { + let reloc_address = start + self.offset as usize; + let reloc_addend = self.addend as isize; + let reloc_abs = target_func_address.checked_add(reloc_addend as u64).unwrap(); + (reloc_address, reloc_abs) + } + RelocationKind::X86PCRel4 => { + let reloc_address = start + self.offset as usize; + let reloc_addend = self.addend as isize; + let reloc_delta_u32 = (target_func_address as u32) + .wrapping_sub(reloc_address as u32) + .checked_add(reloc_addend as u32) + .unwrap(); + (reloc_address, reloc_delta_u32 as u64) + } + RelocationKind::X86PCRel8 => { + let reloc_address = start + self.offset as usize; + let reloc_addend = self.addend as isize; + let reloc_delta = target_func_address + .wrapping_sub(reloc_address as u64) + .checked_add(reloc_addend as u64) + .unwrap(); + (reloc_address, reloc_delta) + } + RelocationKind::X86CallPCRel4 | RelocationKind::X86CallPLTRel4 => { + let reloc_address = start + self.offset as usize; + let reloc_addend = self.addend as isize; + let reloc_delta_u32 = (target_func_address as u32) + .wrapping_sub(reloc_address as u32) + .wrapping_add(reloc_addend as u32); + (reloc_address, reloc_delta_u32 as u64) + } + RelocationKind::Arm64Call => { + let reloc_address = start + self.offset as usize; + let reloc_addend = self.addend as isize; + let reloc_delta_u32 = target_func_address + .wrapping_sub(reloc_address as u64) + .wrapping_add(reloc_addend as u64); + (reloc_address, reloc_delta_u32) + } + // RelocationKind::X86PCRelRodata4 => { + // (start, target_func_address) + // } + _ => panic!("Relocation kind unsupported"), + } + } +} + +/// Relocations to apply to function bodies. +pub type Relocations = PrimaryMap>; diff --git a/runtime/unc-vm/compiler/src/section.rs b/runtime/unc-vm/compiler/src/section.rs new file mode 100644 index 000000000..90ce5ae58 --- /dev/null +++ b/runtime/unc-vm/compiler/src/section.rs @@ -0,0 +1,115 @@ +//! This module define the required structures to emit custom +//! Sections in a `Compilation`. +//! +//! The functions that access a custom [`CustomSection`] would need +//! to emit a custom relocation: `RelocationTarget::CustomSection`, so +//! it can be patched later by the engine (native or JIT). + +use crate::lib::std::vec::Vec; +use crate::Relocation; +use unc_vm_types::entity::entity_impl; + +/// Index type of a Section defined inside a WebAssembly `Compilation`. +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive_attr(derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug))] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +pub struct SectionIndex(u32); + +entity_impl!(SectionIndex); + +entity_impl!(ArchivedSectionIndex); + +/// Custom section Protection. +/// +/// Determines how a custom section may be used. +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Copy, Clone, PartialEq, Eq)] +pub enum CustomSectionProtection { + /// A custom section with read permission. + Read, + + /// A custom section with read and execute permissions. + ReadExecute, +} + +/// A Section for a `Compilation`. +/// +/// This is used so compilers can store arbitrary information +/// in the emitted module. +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, PartialEq, Eq)] +pub struct CustomSection { + /// Memory protection that applies to this section. + pub protection: CustomSectionProtection, + + /// The bytes corresponding to this section. + /// + /// > Note: These bytes have to be at-least 8-byte aligned + /// > (the start of the memory pointer). + /// > We might need to create another field for alignment in case it's + /// > needed in the future. + pub bytes: SectionBody, + + /// Relocations that apply to this custom section. + pub relocations: Vec, +} + +/// See [`CustomSection`]. +/// +/// Note that this does not reference the relocation data. +#[derive(Clone, Copy)] +pub struct CustomSectionRef<'a> { + /// See [`CustomSection::protection`]. + pub protection: CustomSectionProtection, + + /// See [`CustomSection::bytes`]. + pub bytes: &'a [u8], +} + +impl<'a> From<&'a CustomSection> for CustomSectionRef<'a> { + fn from(section: &'a CustomSection) -> Self { + CustomSectionRef { protection: section.protection, bytes: section.bytes.as_slice() } + } +} + +impl<'a> From<&'a ArchivedCustomSection> for CustomSectionRef<'a> { + fn from(section: &'a ArchivedCustomSection) -> Self { + CustomSectionRef { + protection: Result::<_, std::convert::Infallible>::unwrap( + rkyv::Deserialize::deserialize(§ion.protection, &mut rkyv::Infallible), + ), + bytes: §ion.bytes.0[..], + } + } +} + +/// The bytes in the section. +#[derive( + rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, PartialEq, Eq, Default, +)] +pub struct SectionBody(Vec); + +impl SectionBody { + /// Create a new section body with the given contents. + pub fn new_with_vec(contents: Vec) -> Self { + Self(contents) + } + + /// Returns a raw pointer to the section's buffer. + pub fn as_ptr(&self) -> *const u8 { + self.0.as_ptr() + } + + /// Returns the length of this section in bytes. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Dereferences into the section's buffer. + pub fn as_slice(&self) -> &[u8] { + self.0.as_slice() + } + + /// Returns whether or not the section body is empty. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} diff --git a/runtime/unc-vm/compiler/src/sourceloc.rs b/runtime/unc-vm/compiler/src/sourceloc.rs new file mode 100644 index 000000000..b2b091e5d --- /dev/null +++ b/runtime/unc-vm/compiler/src/sourceloc.rs @@ -0,0 +1,66 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Source locations. +//! +//! A [`SourceLoc`] determines the position of a certain instruction +//! relative to the WebAssembly module. This is used mainly for debugging +//! and tracing errors. + +use crate::lib::std::fmt; + +/// A source location. +/// +/// The default source location uses the all-ones bit pattern `!0`. It is used for instructions +/// that can't be given a real source location. +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, Copy, PartialEq, Eq)] +#[archive(as = "Self")] +#[repr(transparent)] +pub struct SourceLoc(u32); + +impl SourceLoc { + /// Create a new source location with the given bits. + pub fn new(bits: u32) -> Self { + Self(bits) + } + + /// Is this the default source location? + pub fn is_default(self) -> bool { + self == Default::default() + } + + /// Read the bits of this source location. + pub fn bits(self) -> u32 { + self.0 + } +} + +impl Default for SourceLoc { + fn default() -> Self { + Self(!0) + } +} + +impl fmt::Display for SourceLoc { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.is_default() { + write!(f, "0x-") + } else { + write!(f, "0x{:04x}", self.0) + } + } +} + +#[cfg(test)] +mod tests { + use super::SourceLoc; + use crate::lib::std::string::ToString; + + #[test] + fn display() { + assert_eq!(SourceLoc::default().to_string(), "0x-"); + assert_eq!(SourceLoc::new(0).to_string(), "0x0000"); + assert_eq!(SourceLoc::new(16).to_string(), "0x0010"); + assert_eq!(SourceLoc::new(0xabcdef).to_string(), "0xabcdef"); + } +} diff --git a/runtime/unc-vm/compiler/src/target.rs b/runtime/unc-vm/compiler/src/target.rs new file mode 100644 index 000000000..646955093 --- /dev/null +++ b/runtime/unc-vm/compiler/src/target.rs @@ -0,0 +1,191 @@ +//! Target configuration +use crate::error::ParseCpuFeatureError; +use crate::lib::std::str::FromStr; +use crate::lib::std::string::{String, ToString}; +use enumset::{EnumSet, EnumSetType}; +pub use target_lexicon::{ + Architecture, BinaryFormat, CallingConvention, Endianness, OperatingSystem, PointerWidth, + Triple, +}; + +/// The nomenclature is inspired by the [`cpuid` crate]. +/// The list of supported features was initially retrieved from +/// [`cranelift-native`]. +/// +/// The `CpuFeature` enum values are likely to grow closer to the +/// original `cpuid`. However, we prefer to start small and grow from there. +/// +/// If you would like to use a flag that doesn't exist yet here, please +/// open a PR. +/// +/// [`cpuid` crate]: https://docs.rs/cpuid/0.1.1/cpuid/enum.CpuFeature.html +/// [`cranelift-native`]: https://github.com/bytecodealliance/cranelift/blob/6988545fd20249b084c53f4761b8c861266f5d31/cranelift-native/src/lib.rs#L51-L92 +#[allow(missing_docs, clippy::derived_hash_with_manual_eq)] +#[derive(EnumSetType, Debug, Hash)] +pub enum CpuFeature { + // X86 features + SSE2, + SSE3, + SSSE3, + SSE41, + SSE42, + POPCNT, + AVX, + BMI1, + BMI2, + AVX2, + AVX512DQ, + AVX512VL, + AVX512F, + LZCNT, + // ARM features + // Risc-V features +} + +impl CpuFeature { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// Retrieves the features for the current Host + pub fn for_host() -> EnumSet { + let mut features = EnumSet::new(); + + if std::is_x86_feature_detected!("sse2") { + features.insert(Self::SSE2); + } + if std::is_x86_feature_detected!("sse3") { + features.insert(Self::SSE3); + } + if std::is_x86_feature_detected!("ssse3") { + features.insert(Self::SSSE3); + } + if std::is_x86_feature_detected!("sse4.1") { + features.insert(Self::SSE41); + } + if std::is_x86_feature_detected!("sse4.2") { + features.insert(Self::SSE42); + } + if std::is_x86_feature_detected!("popcnt") { + features.insert(Self::POPCNT); + } + if std::is_x86_feature_detected!("avx") { + features.insert(Self::AVX); + } + if std::is_x86_feature_detected!("bmi1") { + features.insert(Self::BMI1); + } + if std::is_x86_feature_detected!("bmi2") { + features.insert(Self::BMI2); + } + if std::is_x86_feature_detected!("avx2") { + features.insert(Self::AVX2); + } + if std::is_x86_feature_detected!("avx512dq") { + features.insert(Self::AVX512DQ); + } + if std::is_x86_feature_detected!("avx512vl") { + features.insert(Self::AVX512VL); + } + if std::is_x86_feature_detected!("avx512f") { + features.insert(Self::AVX512F); + } + if std::is_x86_feature_detected!("lzcnt") { + features.insert(Self::LZCNT); + } + features + } + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + /// Retrieves the features for the current Host + pub fn for_host() -> EnumSet { + // We default to an empty hash set + EnumSet::new() + } + + /// Retrieves an empty set of `CpuFeature`s. + pub fn set() -> EnumSet { + // We default to an empty hash set + EnumSet::new() + } +} + +// This options should map exactly the GCC options indicated +// here by architectures: +// +// X86: https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html +// ARM: https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html +// Aarch64: https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html +impl FromStr for CpuFeature { + type Err = ParseCpuFeatureError; + + fn from_str(s: &str) -> Result { + match s { + "sse2" => Ok(Self::SSE2), + "sse3" => Ok(Self::SSE3), + "ssse3" => Ok(Self::SSSE3), + "sse4.1" => Ok(Self::SSE41), + "sse4.2" => Ok(Self::SSE42), + "popcnt" => Ok(Self::POPCNT), + "avx" => Ok(Self::AVX), + "bmi" => Ok(Self::BMI1), + "bmi2" => Ok(Self::BMI2), + "avx2" => Ok(Self::AVX2), + "avx512dq" => Ok(Self::AVX512DQ), + "avx512vl" => Ok(Self::AVX512VL), + "avx512f" => Ok(Self::AVX512F), + "lzcnt" => Ok(Self::LZCNT), + _ => Err(ParseCpuFeatureError::Missing(s.to_string())), + } + } +} + +impl ToString for CpuFeature { + fn to_string(&self) -> String { + match self { + Self::SSE2 => "sse2", + Self::SSE3 => "sse3", + Self::SSSE3 => "ssse3", + Self::SSE41 => "sse4.1", + Self::SSE42 => "sse4.2", + Self::POPCNT => "popcnt", + Self::AVX => "avx", + Self::BMI1 => "bmi", + Self::BMI2 => "bmi2", + Self::AVX2 => "avx2", + Self::AVX512DQ => "avx512dq", + Self::AVX512VL => "avx512vl", + Self::AVX512F => "avx512f", + Self::LZCNT => "lzcnt", + } + .to_string() + } +} + +/// This is the target that we will use for compiling +/// the WebAssembly ModuleInfo, and then run it. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Target { + triple: Triple, + cpu_features: EnumSet, +} + +impl Target { + /// Creates a new target given a triple + pub fn new(triple: Triple, cpu_features: EnumSet) -> Self { + Self { triple, cpu_features } + } + + /// The triple associated for the target. + pub fn triple(&self) -> &Triple { + &self.triple + } + + /// The triple associated for the target. + pub fn cpu_features(&self) -> &EnumSet { + &self.cpu_features + } +} + +/// The default for the Target will use the HOST as the triple +impl Default for Target { + fn default() -> Self { + Self { triple: Triple::host(), cpu_features: CpuFeature::for_host() } + } +} diff --git a/runtime/unc-vm/compiler/src/translator/environ.rs b/runtime/unc-vm/compiler/src/translator/environ.rs new file mode 100644 index 000000000..5e99f0b11 --- /dev/null +++ b/runtime/unc-vm/compiler/src/translator/environ.rs @@ -0,0 +1,400 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md +use super::state::ModuleTranslationState; +use crate::lib::std::borrow::ToOwned; +use crate::lib::std::string::ToString; +use crate::lib::std::{boxed::Box, string::String, vec::Vec}; +use crate::translate_module; +use crate::{WasmError, WasmResult}; +use unc_vm_types::entity::PrimaryMap; +use unc_vm_types::FunctionType; +use unc_vm_types::{ + CustomSectionIndex, DataIndex, DataInitializer, DataInitializerLocation, ElemIndex, + ExportIndex, FunctionIndex, GlobalIndex, GlobalInit, GlobalType, ImportIndex, + LocalFunctionIndex, MemoryIndex, MemoryType, ModuleInfo, OwnedTableInitializer, SignatureIndex, + TableIndex, TableType, +}; +use std::convert::{TryFrom, TryInto}; +use std::sync::Arc; +pub use wasmparser::FunctionBody as FunctionReader; + +/// Contains function data: bytecode and its offset in the module. +#[derive(Hash)] +pub struct FunctionBodyData<'a> { + /// Function body bytecode. + pub data: &'a [u8], + + /// Body offset relative to the module file. + pub module_offset: usize, +} + +/// The result of translating via `ModuleEnvironment`. Function bodies are not +/// yet translated, and data initializers have not yet been copied out of the +/// original buffer. +/// The function bodies will be translated by a specific compiler backend. +pub struct ModuleEnvironment<'data> { + /// ModuleInfo information. + pub module: ModuleInfo, + + /// References to the function bodies. + pub function_body_inputs: PrimaryMap>, + + /// References to the data initializers. + pub data_initializers: Vec>, + + /// The decoded Wasm types for the module. + pub module_translation_state: Option, +} + +impl<'data> ModuleEnvironment<'data> { + /// Allocates the environment data structures. + pub fn new() -> Self { + Self { + module: ModuleInfo::new(), + function_body_inputs: PrimaryMap::new(), + data_initializers: Vec::new(), + module_translation_state: None, + } + } + + /// Translate a wasm module using this environment. This consumes the + /// `ModuleEnvironment` and produces a `ModuleInfoTranslation`. + #[tracing::instrument(target = "unc_vm", level = "trace", skip_all)] + pub fn translate(mut self, data: &'data [u8]) -> WasmResult> { + assert!(self.module_translation_state.is_none()); + let module_translation_state = translate_module(data, &mut self)?; + self.module_translation_state = Some(module_translation_state); + Ok(self) + } + + pub(crate) fn declare_export(&mut self, export: ExportIndex, name: &str) -> WasmResult<()> { + self.module.exports.insert(String::from(name), export); + Ok(()) + } + + pub(crate) fn declare_import( + &mut self, + import: ImportIndex, + module: &str, + field: &str, + ) -> WasmResult<()> { + self.module.imports.insert( + ( + String::from(module), + String::from(field), + self.module.imports.len().try_into().unwrap(), + ), + import, + ); + Ok(()) + } + + pub(crate) fn reserve_signatures(&mut self, num: u32) -> WasmResult<()> { + self.module.signatures.reserve_exact(usize::try_from(num).unwrap()); + Ok(()) + } + + pub(crate) fn declare_signature(&mut self, sig: FunctionType) -> WasmResult<()> { + // TODO: Deduplicate signatures. + self.module.signatures.push(sig); + Ok(()) + } + + pub(crate) fn declare_func_import( + &mut self, + sig_index: SignatureIndex, + module: &str, + field: &str, + ) -> WasmResult<()> { + debug_assert_eq!( + self.module.functions.len(), + self.module.import_counts.functions as usize, + "Imported functions must be declared first" + ); + self.declare_import( + ImportIndex::Function(FunctionIndex::from_u32(self.module.import_counts.functions)), + module, + field, + )?; + self.module.functions.push(sig_index); + self.module.import_counts.functions += 1; + Ok(()) + } + + pub(crate) fn declare_table_import( + &mut self, + table: TableType, + module: &str, + field: &str, + ) -> WasmResult<()> { + debug_assert_eq!( + self.module.tables.len(), + self.module.import_counts.tables as usize, + "Imported tables must be declared first" + ); + self.declare_import( + ImportIndex::Table(TableIndex::from_u32(self.module.import_counts.tables)), + module, + field, + )?; + self.module.tables.push(table); + self.module.import_counts.tables += 1; + Ok(()) + } + + pub(crate) fn declare_memory_import( + &mut self, + memory: MemoryType, + module: &str, + field: &str, + ) -> WasmResult<()> { + debug_assert_eq!( + self.module.memories.len(), + self.module.import_counts.memories as usize, + "Imported memories must be declared first" + ); + self.declare_import( + ImportIndex::Memory(MemoryIndex::from_u32(self.module.import_counts.memories)), + module, + field, + )?; + self.module.memories.push(memory); + self.module.import_counts.memories += 1; + Ok(()) + } + + pub(crate) fn declare_global_import( + &mut self, + global: GlobalType, + module: &str, + field: &str, + ) -> WasmResult<()> { + debug_assert_eq!( + self.module.globals.len(), + self.module.import_counts.globals as usize, + "Imported globals must be declared first" + ); + self.declare_import( + ImportIndex::Global(GlobalIndex::from_u32(self.module.import_counts.globals)), + module, + field, + )?; + self.module.globals.push(global); + self.module.import_counts.globals += 1; + Ok(()) + } + + pub(crate) fn finish_imports(&mut self) -> WasmResult<()> { + Ok(()) + } + + pub(crate) fn reserve_func_types(&mut self, num: u32) -> WasmResult<()> { + self.module.functions.reserve_exact(usize::try_from(num).unwrap()); + self.function_body_inputs.reserve_exact(usize::try_from(num).unwrap()); + Ok(()) + } + + pub(crate) fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()> { + self.module.functions.push(sig_index); + Ok(()) + } + + pub(crate) fn reserve_tables(&mut self, num: u32) -> WasmResult<()> { + self.module.tables.reserve_exact(usize::try_from(num).unwrap()); + Ok(()) + } + + pub(crate) fn declare_table(&mut self, table: TableType) -> WasmResult<()> { + self.module.tables.push(table); + Ok(()) + } + + pub(crate) fn reserve_memories(&mut self, num: u32) -> WasmResult<()> { + self.module.memories.reserve_exact(usize::try_from(num).unwrap()); + Ok(()) + } + + pub(crate) fn declare_memory(&mut self, memory: MemoryType) -> WasmResult<()> { + if memory.shared { + return Err(WasmError::Unsupported("shared memories are not supported yet".to_owned())); + } + self.module.memories.push(memory); + Ok(()) + } + + pub(crate) fn reserve_globals(&mut self, num: u32) -> WasmResult<()> { + self.module.globals.reserve_exact(usize::try_from(num).unwrap()); + Ok(()) + } + + pub(crate) fn declare_global( + &mut self, + global: GlobalType, + initializer: GlobalInit, + ) -> WasmResult<()> { + self.module.globals.push(global); + self.module.global_initializers.push(initializer); + Ok(()) + } + + pub(crate) fn reserve_exports(&mut self, num: u32) -> WasmResult<()> { + self.module.exports.reserve(usize::try_from(num).unwrap()); + Ok(()) + } + + pub(crate) fn declare_func_export( + &mut self, + func_index: FunctionIndex, + name: &str, + ) -> WasmResult<()> { + self.declare_export(ExportIndex::Function(func_index), name) + } + + pub(crate) fn declare_table_export( + &mut self, + table_index: TableIndex, + name: &str, + ) -> WasmResult<()> { + self.declare_export(ExportIndex::Table(table_index), name) + } + + pub(crate) fn declare_memory_export( + &mut self, + memory_index: MemoryIndex, + name: &str, + ) -> WasmResult<()> { + self.declare_export(ExportIndex::Memory(memory_index), name) + } + + pub(crate) fn declare_global_export( + &mut self, + global_index: GlobalIndex, + name: &str, + ) -> WasmResult<()> { + self.declare_export(ExportIndex::Global(global_index), name) + } + + pub(crate) fn declare_start_function(&mut self, func_index: FunctionIndex) -> WasmResult<()> { + debug_assert!(self.module.start_function.is_none()); + self.module.start_function = Some(func_index); + Ok(()) + } + + pub(crate) fn reserve_table_initializers(&mut self, num: u32) -> WasmResult<()> { + self.module.table_initializers.reserve_exact(usize::try_from(num).unwrap()); + Ok(()) + } + + pub(crate) fn declare_table_initializers( + &mut self, + table_index: TableIndex, + base: Option, + offset: usize, + elements: Box<[FunctionIndex]>, + ) -> WasmResult<()> { + self.module.table_initializers.push(OwnedTableInitializer { + table_index, + base, + offset, + elements, + }); + Ok(()) + } + + pub(crate) fn declare_passive_element( + &mut self, + elem_index: ElemIndex, + segments: Box<[FunctionIndex]>, + ) -> WasmResult<()> { + let old = self.module.passive_elements.insert(elem_index, segments); + debug_assert!( + old.is_none(), + "should never get duplicate element indices, that would be a bug in `unc_vm_compiler`'s \ + translation" + ); + Ok(()) + } + + pub(crate) fn define_function_body( + &mut self, + _module_translation_state: &ModuleTranslationState, + body_bytes: &'data [u8], + body_offset: usize, + ) -> WasmResult<()> { + self.function_body_inputs + .push(FunctionBodyData { data: body_bytes, module_offset: body_offset }); + Ok(()) + } + + pub(crate) fn reserve_data_initializers(&mut self, num: u32) -> WasmResult<()> { + self.data_initializers.reserve_exact(usize::try_from(num).unwrap()); + Ok(()) + } + + pub(crate) fn declare_data_initialization( + &mut self, + memory_index: MemoryIndex, + base: Option, + offset: usize, + data: &'data [u8], + ) -> WasmResult<()> { + self.data_initializers.push(DataInitializer { + location: DataInitializerLocation { memory_index, base, offset }, + data, + }); + Ok(()) + } + + pub(crate) fn reserve_passive_data(&mut self, _count: u32) -> WasmResult<()> { + // TODO(0-copy): consider finding a more appropriate data structure for this? + Ok(()) + } + + pub(crate) fn declare_passive_data( + &mut self, + data_index: DataIndex, + data: &'data [u8], + ) -> WasmResult<()> { + let old = self.module.passive_data.insert(data_index, Arc::from(data)); + debug_assert!( + old.is_none(), + "a module can't have duplicate indices, this would be a unc_vm-compiler bug" + ); + Ok(()) + } + + pub(crate) fn declare_module_name(&mut self, name: &'data str) -> WasmResult<()> { + self.module.name = Some(name.to_string()); + Ok(()) + } + + pub(crate) fn declare_function_name( + &mut self, + func_index: FunctionIndex, + name: &'data str, + ) -> WasmResult<()> { + self.module.function_names.insert(func_index, name.to_string()); + Ok(()) + } + + /// Provides the number of imports up front. By default this does nothing, but + /// implementations can use this to preallocate memory if desired. + pub(crate) fn reserve_imports(&mut self, _num: u32) -> WasmResult<()> { + Ok(()) + } + + /// Notifies the implementation that all exports have been declared. + pub(crate) fn finish_exports(&mut self) -> WasmResult<()> { + Ok(()) + } + + /// Indicates that a custom section has been found in the wasm file + pub(crate) fn custom_section(&mut self, name: &'data str, data: &'data [u8]) -> WasmResult<()> { + let custom_section = CustomSectionIndex::from_u32( + self.module.custom_sections_data.len().try_into().unwrap(), + ); + self.module.custom_sections.insert(String::from(name), custom_section); + self.module.custom_sections_data.push(Arc::from(data)); + Ok(()) + } +} diff --git a/runtime/unc-vm/compiler/src/translator/error.rs b/runtime/unc-vm/compiler/src/translator/error.rs new file mode 100644 index 000000000..b46dc4f96 --- /dev/null +++ b/runtime/unc-vm/compiler/src/translator/error.rs @@ -0,0 +1,55 @@ +use crate::{CompileError, WasmError}; +use wasmparser::BinaryReaderError; + +/// Return an `Err(WasmError::Unsupported(msg))` where `msg` the string built by calling `format!` +/// on the arguments to this macro. +#[macro_export] +macro_rules! wasm_unsupported { + ($($arg:tt)*) => { $crate::WasmError::Unsupported(format!($($arg)*)) } +} + +impl From for WasmError { + fn from(original: BinaryReaderError) -> Self { + Self::InvalidWebAssembly { message: original.message().into(), offset: original.offset() } + } +} + +impl From for CompileError { + fn from(original: BinaryReaderError) -> Self { + // `From` does not seem to be transitive by default, so we convert + // BinaryReaderError -> WasmError -> CompileError + Self::from(WasmError::from(original)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use wasmparser::BinaryReader; + + #[test] + fn can_convert_binary_reader_error_to_wasm_error() { + let mut reader = BinaryReader::new(b"\0\0\0\0"); + let binary_reader_error = reader.read_bytes(10).unwrap_err(); + match WasmError::from(binary_reader_error) { + WasmError::InvalidWebAssembly { message, offset } => { + assert_eq!(message, "unexpected end-of-file"); + assert_eq!(offset, 0); + } + err => panic!("Unexpected error: {:?}", err), + } + } + + #[test] + fn can_convert_binary_reader_error_to_compile_error() { + let mut reader = BinaryReader::new(b"\0\0\0\0"); + let binary_reader_error = reader.read_bytes(10).unwrap_err(); + match CompileError::from(binary_reader_error) { + CompileError::Wasm(WasmError::InvalidWebAssembly { message, offset }) => { + assert_eq!(message, "unexpected end-of-file"); + assert_eq!(offset, 0); + } + err => panic!("Unexpected error: {:?}", err), + } + } +} diff --git a/runtime/unc-vm/compiler/src/translator/mod.rs b/runtime/unc-vm/compiler/src/translator/mod.rs new file mode 100644 index 000000000..186ef97e3 --- /dev/null +++ b/runtime/unc-vm/compiler/src/translator/mod.rs @@ -0,0 +1,18 @@ +//! This module defines the parser and translator from wasmparser +//! to a common structure `ModuleInfo`. +//! +//! It's derived from [cranelift-wasm] but architected for multiple +//! compilers rather than just Cranelift. +//! +//! [cranelift-wasm]: https://crates.io/crates/cranelift-wasm/ +mod environ; +mod module; +mod state; +#[macro_use] +mod error; +mod sections; + +pub use self::environ::{FunctionBodyData, FunctionReader, ModuleEnvironment}; +pub use self::module::translate_module; +pub use self::sections::wptype_to_type; +pub use self::state::ModuleTranslationState; diff --git a/runtime/unc-vm/compiler/src/translator/module.rs b/runtime/unc-vm/compiler/src/translator/module.rs new file mode 100644 index 000000000..6f8d4fbfb --- /dev/null +++ b/runtime/unc-vm/compiler/src/translator/module.rs @@ -0,0 +1,140 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Translation skeleton that traverses the whole WebAssembly module and call helper functions +//! to deal with each part of it. +use super::environ::ModuleEnvironment; +use super::sections::{ + parse_data_section, parse_element_section, parse_export_section, parse_function_section, + parse_global_section, parse_import_section, parse_memory_section, parse_name_section, + parse_start_section, parse_table_section, parse_type_section, +}; +use super::state::ModuleTranslationState; +use crate::WasmResult; +use wasmparser::{NameSectionReader, Parser, Payload}; + +/// Translate a sequence of bytes forming a valid Wasm binary into a +/// parsed ModuleInfo `ModuleTranslationState`. +#[tracing::instrument(target = "unc_vm", level = "trace", skip_all)] +pub fn translate_module<'data>( + data: &'data [u8], + environ: &mut ModuleEnvironment<'data>, +) -> WasmResult { + let mut module_translation_state = ModuleTranslationState::new(); + + for payload in Parser::new(0).parse_all(data) { + match payload? { + Payload::Version { .. } | Payload::End(_) => {} + + Payload::TypeSection(types) => { + parse_type_section(types, &mut module_translation_state, environ)?; + } + + Payload::ImportSection(imports) => { + parse_import_section(imports, environ)?; + } + + Payload::FunctionSection(functions) => { + parse_function_section(functions, environ)?; + } + + Payload::TableSection(tables) => { + parse_table_section(tables, environ)?; + } + + Payload::MemorySection(memories) => { + parse_memory_section(memories, environ)?; + } + + Payload::GlobalSection(globals) => { + parse_global_section(globals, environ)?; + } + + Payload::ExportSection(exports) => { + parse_export_section(exports, environ)?; + } + + Payload::StartSection { func, .. } => { + parse_start_section(func, environ)?; + } + + Payload::ElementSection(elements) => { + parse_element_section(elements, environ)?; + } + + Payload::CodeSectionStart { .. } => {} + Payload::CodeSectionEntry(code) => { + let mut code = code.get_binary_reader(); + let size = code.bytes_remaining(); + let offset = code.original_position(); + environ.define_function_body( + &module_translation_state, + code.read_bytes(size)?, + offset, + )?; + } + + Payload::DataSection(data) => { + parse_data_section(data, environ)?; + } + + Payload::DataCountSection { count, .. } => { + environ.reserve_passive_data(count)?; + } + + Payload::InstanceSection(_) => { + unimplemented!("module linking not implemented yet") + } + + Payload::TagSection(_) => { + unimplemented!("exception handling proposal is not implemented yet") + } + + Payload::CustomSection(reader) => { + if reader.name() == "name" { + parse_name_section( + NameSectionReader::new(reader.data(), reader.data_offset()), + environ, + )?; + } else { + environ.custom_section(reader.name(), reader.data())?; + } + } + + Payload::ModuleSection { .. } => unimplemented!("module sections not supported yet"), // which proposal is this coming from? + Payload::CoreTypeSection { .. } => { + unimplemented!("component proposal is not implemented yet") + } + Payload::ComponentSection { .. } => { + unimplemented!("component proposal is not implemented yet") + } + Payload::ComponentInstanceSection { .. } => { + unimplemented!("component proposal is not implemented yet") + } + Payload::ComponentAliasSection { .. } => { + unimplemented!("component proposal is not implemented yet") + } + Payload::ComponentTypeSection { .. } => { + unimplemented!("component proposal is not implemented yet") + } + Payload::ComponentCanonicalSection { .. } => { + unimplemented!("component proposal is not implemented yet") + } + Payload::ComponentStartSection { .. } => { + unimplemented!("component proposal is not implemented yet") + } + Payload::ComponentImportSection { .. } => { + unimplemented!("component proposal is not implemented yet") + } + Payload::ComponentExportSection { .. } => { + unimplemented!("component proposal is not implemented yet") + } + + Payload::UnknownSection { .. } => unreachable!(), + } + } + + module_translation_state.build_import_map(&environ.module); + + Ok(module_translation_state) +} diff --git a/runtime/unc-vm/compiler/src/translator/sections.rs b/runtime/unc-vm/compiler/src/translator/sections.rs new file mode 100644 index 000000000..83a51f5c0 --- /dev/null +++ b/runtime/unc-vm/compiler/src/translator/sections.rs @@ -0,0 +1,445 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Helper functions to gather information for each of the non-function sections of a +//! WebAssembly module. +//! +//! The code of these helper functions is straightforward since they only read metadata +//! about linear memories, tables, globals, etc. and store them for later use. +//! +//! The special case of the initialize expressions for table elements offsets or global variables +//! is handled, according to the semantics of WebAssembly, to only specific expressions that are +//! interpreted on the fly. +use super::environ::ModuleEnvironment; +use super::state::ModuleTranslationState; +use crate::wasm_unsupported; +use crate::{WasmError, WasmResult}; +use core::convert::TryFrom; +use unc_vm_types::entity::packed_option::ReservedValue; +use unc_vm_types::entity::EntityRef; +use unc_vm_types::{ + DataIndex, ElemIndex, FunctionIndex, FunctionType, GlobalIndex, GlobalInit, GlobalType, + MemoryIndex, MemoryType, Mutability, Pages, SignatureIndex, TableIndex, TableType, Type, V128, +}; +use std::boxed::Box; +use std::collections::HashMap; +use std::convert::TryInto; +use std::sync::Arc; +use std::vec::Vec; +use wasmparser::{ + self, Data, DataKind, DataSectionReader, Element, ElementItems, ElementKind, + ElementSectionReader, Export, ExportSectionReader, ExternalKind, FunctionSectionReader, + GlobalSectionReader, GlobalType as WPGlobalType, ImportSectionReader, MemorySectionReader, + NameMap, NameSectionReader, Naming, Operator, TableSectionReader, Type as WPType, TypeRef, + TypeSectionReader, ValType as WPValType, +}; + +/// Helper function translating wasmparser types to Wasm Type. +pub fn wptype_to_type(ty: WPValType) -> WasmResult { + match ty { + WPValType::I32 => Ok(Type::I32), + WPValType::I64 => Ok(Type::I64), + WPValType::F32 => Ok(Type::F32), + WPValType::F64 => Ok(Type::F64), + WPValType::V128 => Ok(Type::V128), + WPValType::ExternRef => Ok(Type::ExternRef), + WPValType::FuncRef => Ok(Type::FuncRef), + } +} + +/// Parses the Type section of the wasm module. +pub fn parse_type_section( + types: TypeSectionReader, + module_translation_state: &mut ModuleTranslationState, + environ: &mut ModuleEnvironment, +) -> WasmResult<()> { + let count = types.count(); + environ.reserve_signatures(count)?; + + for entry in types { + if let Ok(WPType::Func(t)) = entry { + let params: Box<[WPValType]> = t.params().into(); + let results: Box<[WPValType]> = t.results().into(); + let sig_params: Arc<[Type]> = params + .iter() + .map(|ty| { + wptype_to_type(*ty) + .expect("only numeric types are supported in function signatures") + }) + .collect(); + let sig_results: Arc<[Type]> = results + .iter() + .map(|ty| { + wptype_to_type(*ty) + .expect("only numeric types are supported in function signatures") + }) + .collect(); + let sig = FunctionType::new(sig_params, sig_results); + environ.declare_signature(sig)?; + module_translation_state.wasm_types.push((params, results)); + } else { + unimplemented!("module linking not implemented yet") + } + } + + Ok(()) +} + +/// Parses the Import section of the wasm module. +pub fn parse_import_section<'data>( + imports: ImportSectionReader<'data>, + environ: &mut ModuleEnvironment<'data>, +) -> WasmResult<()> { + environ.reserve_imports(imports.count())?; + + for entry in imports { + let import = entry?; + let module_name = import.module; + let field_name = import.name; + + match import.ty { + TypeRef::Func(sig) => { + environ.declare_func_import( + SignatureIndex::from_u32(sig), + module_name, + field_name, + )?; + } + TypeRef::Memory(mem) => { + assert!(!mem.memory64, "64bit memory not implemented yet"); + environ.declare_memory_import( + MemoryType { + minimum: Pages(mem.initial.try_into().unwrap()), + maximum: mem.maximum.map(|m| Pages(m.try_into().unwrap())), + shared: mem.shared, + }, + module_name, + field_name, + )?; + } + TypeRef::Global(ref ty) => { + environ.declare_global_import( + GlobalType { + ty: wptype_to_type(ty.content_type).unwrap(), + mutability: if ty.mutable { Mutability::Var } else { Mutability::Const }, + }, + module_name, + field_name, + )?; + } + TypeRef::Table(ref tab) => { + environ.declare_table_import( + TableType { + ty: wptype_to_type(tab.element_type).unwrap(), + minimum: tab.initial, + maximum: tab.maximum, + }, + module_name, + field_name, + )?; + } + TypeRef::Tag(_) => panic!("exception handling proposal is not implemented yet"), + } + } + + environ.finish_imports()?; + Ok(()) +} + +/// Parses the Function section of the wasm module. +pub fn parse_function_section( + functions: FunctionSectionReader, + environ: &mut ModuleEnvironment, +) -> WasmResult<()> { + let num_functions = functions.count(); + if num_functions == std::u32::MAX { + // We reserve `u32::MAX` for our own use. + return Err(WasmError::ImplLimitExceeded); + } + + environ.reserve_func_types(num_functions)?; + + for entry in functions { + let sigindex = entry?; + environ.declare_func_type(SignatureIndex::from_u32(sigindex))?; + } + + Ok(()) +} + +/// Parses the Table section of the wasm module. +pub fn parse_table_section( + tables: TableSectionReader, + environ: &mut ModuleEnvironment, +) -> WasmResult<()> { + environ.reserve_tables(tables.count())?; + + for entry in tables { + let table = entry?; + environ.declare_table(TableType { + ty: wptype_to_type(table.element_type).unwrap(), + minimum: table.initial, + maximum: table.maximum, + })?; + } + + Ok(()) +} + +/// Parses the Memory section of the wasm module. +pub fn parse_memory_section( + memories: MemorySectionReader, + environ: &mut ModuleEnvironment, +) -> WasmResult<()> { + environ.reserve_memories(memories.count())?; + + for entry in memories { + let mem = entry?; + assert!(!mem.memory64, "64bit memory not implemented yet"); + + environ.declare_memory(MemoryType { + minimum: Pages(mem.initial.try_into().unwrap()), + maximum: mem.maximum.map(|m| Pages(m.try_into().unwrap())), + shared: mem.shared, + })?; + } + + Ok(()) +} + +/// Parses the Global section of the wasm module. +pub fn parse_global_section( + globals: GlobalSectionReader, + environ: &mut ModuleEnvironment, +) -> WasmResult<()> { + environ.reserve_globals(globals.count())?; + + for entry in globals { + let wasmparser::Global { ty: WPGlobalType { content_type, mutable }, init_expr } = entry?; + let mut init_expr_reader = init_expr.get_binary_reader(); + let initializer = match init_expr_reader.read_operator()? { + Operator::I32Const { value } => GlobalInit::I32Const(value), + Operator::I64Const { value } => GlobalInit::I64Const(value), + Operator::F32Const { value } => GlobalInit::F32Const(f32::from_bits(value.bits())), + Operator::F64Const { value } => GlobalInit::F64Const(f64::from_bits(value.bits())), + Operator::V128Const { value } => GlobalInit::V128Const(V128::from(*value.bytes())), + Operator::RefNull { ty: _ } => GlobalInit::RefNullConst, + Operator::RefFunc { function_index } => { + GlobalInit::RefFunc(FunctionIndex::from_u32(function_index)) + } + Operator::GlobalGet { global_index } => { + GlobalInit::GetGlobal(GlobalIndex::from_u32(global_index)) + } + ref s => { + return Err(wasm_unsupported!("unsupported init expr in global section: {:?}", s)); + } + }; + let global = GlobalType { + ty: wptype_to_type(content_type).unwrap(), + mutability: if mutable { Mutability::Var } else { Mutability::Const }, + }; + environ.declare_global(global, initializer)?; + } + + Ok(()) +} + +/// Parses the Export section of the wasm module. +pub fn parse_export_section<'data>( + exports: ExportSectionReader<'data>, + environ: &mut ModuleEnvironment<'data>, +) -> WasmResult<()> { + environ.reserve_exports(exports.count())?; + + for entry in exports { + let Export { name, ref kind, index } = entry?; + + // The input has already been validated, so we should be able to + // assume valid UTF-8 and use `from_utf8_unchecked` if performance + // becomes a concern here. + let index = index as usize; + match *kind { + ExternalKind::Func => environ.declare_func_export(FunctionIndex::new(index), name)?, + ExternalKind::Table => environ.declare_table_export(TableIndex::new(index), name)?, + ExternalKind::Memory => environ.declare_memory_export(MemoryIndex::new(index), name)?, + ExternalKind::Global => environ.declare_global_export(GlobalIndex::new(index), name)?, + ExternalKind::Tag => panic!("exception handling proposal is not implemented yet"), + } + } + + environ.finish_exports()?; + Ok(()) +} + +/// Parses the Start section of the wasm module. +pub fn parse_start_section(index: u32, environ: &mut ModuleEnvironment) -> WasmResult<()> { + environ.declare_start_function(FunctionIndex::from_u32(index))?; + Ok(()) +} + +fn read_elems(items: &ElementItems) -> WasmResult> { + match items.clone() { + ElementItems::Functions(items) => items + .into_iter() + .map(|v| v.map(FunctionIndex::from_u32).map_err(WasmError::from)) + .collect(), + ElementItems::Expressions(items) => { + let mut elems = Vec::with_capacity(usize::try_from(items.count()).unwrap()); + for item in items.into_iter() { + let mut reader = item?.get_operators_reader(); + let op = reader.read()?; + let end = reader.read()?; + reader.ensure_end()?; + use Operator::*; + match (op, end) { + (RefFunc { function_index }, End) => { + elems.push(FunctionIndex::from_u32(function_index)) + } + (RefNull { .. }, End) => elems.push(FunctionIndex::reserved_value()), + _ => todo!("unexpected syntax for elems item initializer"), + } + } + Ok(elems.into_boxed_slice()) + } + } +} + +/// Parses the Element section of the wasm module. +pub fn parse_element_section<'data>( + elements: ElementSectionReader<'data>, + environ: &mut ModuleEnvironment, +) -> WasmResult<()> { + environ.reserve_table_initializers(elements.count())?; + + for (index, entry) in elements.into_iter().enumerate() { + let Element { kind, items, ty, .. } = entry?; + if ty != WPValType::FuncRef { + return Err(wasm_unsupported!("unsupported table element type: {:?}", ty)); + } + let segments = read_elems(&items)?; + match kind { + ElementKind::Active { table_index, offset_expr } => { + let mut offset_expr_reader = offset_expr.get_binary_reader(); + let (base, offset) = match offset_expr_reader.read_operator()? { + Operator::I32Const { value } => (None, value as u32 as usize), + Operator::GlobalGet { global_index } => { + (Some(GlobalIndex::from_u32(global_index)), 0) + } + ref s => { + return Err(wasm_unsupported!( + "unsupported init expr in element section: {:?}", + s + )); + } + }; + environ.declare_table_initializers( + TableIndex::from_u32(table_index), + base, + offset, + segments, + )? + } + ElementKind::Passive => { + let index = ElemIndex::from_u32(index as u32); + environ.declare_passive_element(index, segments)?; + } + ElementKind::Declared => (), + } + } + Ok(()) +} + +/// Parses the Data section of the wasm module. +pub fn parse_data_section<'data>( + data: DataSectionReader<'data>, + environ: &mut ModuleEnvironment<'data>, +) -> WasmResult<()> { + environ.reserve_data_initializers(data.count())?; + + for (index, entry) in data.into_iter().enumerate() { + let Data { kind, data, .. } = entry?; + match kind { + DataKind::Active { memory_index, offset_expr } => { + let mut offset_expr_reader = offset_expr.get_binary_reader(); + let (base, offset) = match offset_expr_reader.read_operator()? { + Operator::I32Const { value } => (None, value as u32 as usize), + Operator::GlobalGet { global_index } => { + (Some(GlobalIndex::from_u32(global_index)), 0) + } + ref s => { + return Err(wasm_unsupported!( + "unsupported init expr in data section: {:?}", + s + )) + } + }; + environ.declare_data_initialization( + MemoryIndex::from_u32(memory_index), + base, + offset, + data, + )?; + } + DataKind::Passive => { + let index = DataIndex::from_u32(index as u32); + environ.declare_passive_data(index, data)?; + } + } + } + + Ok(()) +} + +/// Parses the Name section of the wasm module. +pub fn parse_name_section<'data>( + mut names: NameSectionReader<'data>, + environ: &mut ModuleEnvironment<'data>, +) -> WasmResult<()> { + use wasmparser::Name; + while let Some(subsection) = names.next() { + let subsection = subsection?; + match subsection { + Name::Function(function_subsection) => { + if let Some(function_names) = parse_function_name_subsection(function_subsection) { + for (index, name) in function_names { + environ.declare_function_name(index, name)?; + } + } + } + Name::Module { name, .. } => { + environ.declare_module_name(name)?; + } + Name::Local(_) => {} + Name::Label(_) => {} + Name::Type(_) => {} + Name::Table(_) => {} + Name::Memory(_) => {} + Name::Global(_) => {} + Name::Element(_) => {} + Name::Data(_) => {} + Name::Unknown { .. } => {} + }; + } + Ok(()) +} + +fn parse_function_name_subsection( + naming_reader: NameMap<'_>, +) -> Option> { + let mut function_names = HashMap::new(); + for name in naming_reader.into_iter() { + let Naming { index, name } = name.ok()?; + if index == std::u32::MAX { + // We reserve `u32::MAX` for our own use. + return None; + } + + if function_names.insert(FunctionIndex::from_u32(index), name).is_some() { + // If the function index has been previously seen, then we + // break out of the loop and early return `None`, because these + // should be unique. + return None; + } + } + Some(function_names) +} diff --git a/runtime/unc-vm/compiler/src/translator/state.rs b/runtime/unc-vm/compiler/src/translator/state.rs new file mode 100644 index 000000000..8f4373f04 --- /dev/null +++ b/runtime/unc-vm/compiler/src/translator/state.rs @@ -0,0 +1,52 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +use unc_vm_types::entity::PrimaryMap; +use unc_vm_types::{FunctionIndex, ImportIndex, ModuleInfo, SignatureIndex}; +use std::boxed::Box; +use std::collections::HashMap; + +/// Map of signatures to a function's parameter and return types. +pub(crate) type WasmTypes = + PrimaryMap, Box<[wasmparser::ValType]>)>; + +/// Contains information decoded from the Wasm module that must be referenced +/// during each Wasm function's translation. +/// +/// This is only for data that is maintained by `unc-vm-compiler` itself, as +/// opposed to being maintained by the embedder. Data that is maintained by the +/// embedder is represented with `ModuleEnvironment`. +#[derive(Debug)] +pub struct ModuleTranslationState { + /// A map containing a Wasm module's original, raw signatures. + /// + /// This is used for translating multi-value Wasm blocks inside functions, + /// which are encoded to refer to their type signature via index. + pub(crate) wasm_types: WasmTypes, + + /// Imported functions names map. + pub import_map: HashMap, +} + +impl ModuleTranslationState { + /// Creates a new empty ModuleTranslationState. + pub fn new() -> Self { + Self { wasm_types: PrimaryMap::new(), import_map: HashMap::new() } + } + + /// Build map of imported functions names for intrinsification. + #[tracing::instrument(target = "unc_vm", level = "trace", skip_all)] + pub fn build_import_map(&mut self, module: &ModuleInfo) { + for key in module.imports.keys() { + let value = &module.imports[key]; + match value { + ImportIndex::Function(index) => { + self.import_map.insert(*index, key.1.clone()); + } + _ => { + // Non-function import. + } + } + } + } +} diff --git a/runtime/unc-vm/compiler/src/trap.rs b/runtime/unc-vm/compiler/src/trap.rs new file mode 100644 index 000000000..8f9d29938 --- /dev/null +++ b/runtime/unc-vm/compiler/src/trap.rs @@ -0,0 +1,11 @@ +use crate::CodeOffset; +use unc_vm_vm::TrapCode; + +/// Information about trap. +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Clone, Debug, PartialEq, Eq)] +pub struct TrapInformation { + /// The offset of the trapping instruction in native code. It is relative to the beginning of the function. + pub code_offset: CodeOffset, + /// Code of the trap. + pub trap_code: TrapCode, +} diff --git a/runtime/unc-vm/engine/Cargo.toml b/runtime/unc-vm/engine/Cargo.toml new file mode 100644 index 000000000..423fd6f12 --- /dev/null +++ b/runtime/unc-vm/engine/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "unc-vm-engine" +version.workspace = true +description = "Wasmer Engine abstraction" +categories = ["wasm"] +keywords = ["wasm", "webassembly", "engine"] +authors = ["Wasmer Engineering Team ", "Hello Inc "] +repository.workspace = true +license = "MIT OR Apache-2.0 WITH LLVM-exception " +readme = "README.md" +edition = "2021" +publish = true + +[lints] +workspace = true + +[dependencies] +backtrace.workspace = true +enumset.workspace = true +finite-wasm.workspace = true +lazy_static.workspace = true +memmap2.workspace = true +more-asserts.workspace = true +unc-vm-compiler.workspace = true +unc-vm-types.workspace = true +unc-vm-vm.workspace = true +rkyv.workspace = true +rustc-demangle.workspace = true +region.workspace = true +target-lexicon.workspace = true +thiserror.workspace = true +cfg-if.workspace = true +tracing.workspace = true +crossbeam-queue.workspace = true +rustix = { workspace = true, features = ["param", "mm"] } + +[badges] +maintenance = { status = "actively-developed" } diff --git a/runtime/unc-vm/engine/LICENSE b/runtime/unc-vm/engine/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/runtime/unc-vm/engine/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/runtime/unc-vm/engine/README.md b/runtime/unc-vm/engine/README.md new file mode 100644 index 000000000..273c9b9f4 --- /dev/null +++ b/runtime/unc-vm/engine/README.md @@ -0,0 +1,22 @@ +# `unc-vm-engine` + +This crate is a fork of `wasmer-engine`. A significant number of things changed, but the documentation is not up-to-date yet. + +This crate is the general abstraction for creating Engines in Wasmer. + +Wasmer Engines are mainly responsible for two things: +* Transform the compilation code (from any Wasmer Compiler) to + **create** an `Artifact`, +* **Load** an`Artifact` so it can be used by the user (normally, + pushing the code into executable memory and so on). + +### Acknowledgments + +This project borrowed some of the code of the trap implementation from +the [`wasmtime-api`], the code since then has evolved significantly. + +Please check [Wasmer `ATTRIBUTIONS`] to further see licenses and other +attributions of the project. + +[`wasmtime-api`]: https://crates.io/crates/wasmtime +[Wasmer `ATTRIBUTIONS`]: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md diff --git a/runtime/unc-vm/engine/src/engine.rs b/runtime/unc-vm/engine/src/engine.rs new file mode 100644 index 000000000..d5061ed84 --- /dev/null +++ b/runtime/unc-vm/engine/src/engine.rs @@ -0,0 +1,30 @@ +//! Engine trait and associated types. + +use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +/// A unique identifier for an Engine. +pub struct EngineId { + id: usize, +} + +impl EngineId { + /// Format this identifier as a string. + pub fn id(&self) -> String { + format!("{}", &self.id) + } +} + +impl Clone for EngineId { + fn clone(&self) -> Self { + Self::default() + } +} + +impl Default for EngineId { + fn default() -> Self { + static NEXT_ID: AtomicUsize = AtomicUsize::new(0); + Self { id: NEXT_ID.fetch_add(1, SeqCst) } + } +} diff --git a/runtime/unc-vm/engine/src/error.rs b/runtime/unc-vm/engine/src/error.rs new file mode 100644 index 000000000..a6e7fe5b7 --- /dev/null +++ b/runtime/unc-vm/engine/src/error.rs @@ -0,0 +1,90 @@ +//! The WebAssembly possible errors +use crate::trap::RuntimeError; +use unc_vm_compiler::CompileError; +use unc_vm_types::ExternType; +use std::io; +use thiserror::Error; + +/// The Deserialize error can occur when loading a +/// compiled Module from a binary. +#[derive(Error, Debug)] +pub enum DeserializeError { + /// An IO error + #[error(transparent)] + Io(#[from] io::Error), + /// A generic deserialization error + #[error("{0}")] + Generic(String), + /// Incompatible serialized binary + #[error("incompatible binary: {0}")] + Incompatible(String), + /// The provided binary is corrupted + #[error("corrupted binary: {0}")] + CorruptedBinary(String), + /// The binary was valid, but we got an error when + /// trying to allocate the required resources. + #[error(transparent)] + Compiler(CompileError), +} + +/// An ImportError. +/// +/// Note: this error is not standard to WebAssembly, but it's +/// useful to determine the import issue on the API side. +#[derive(Error, Debug)] +pub enum ImportError { + /// Incompatible Import Type. + /// This error occurs when the import types mismatch. + #[error("incompatible import type. Expected {0:?} but received {1:?}")] + IncompatibleType(ExternType, ExternType), + + /// Unknown Import. + /// This error occurs when an import was expected but not provided. + #[error("unknown import. Expected {0:?}")] + UnknownImport(ExternType), +} + +/// The WebAssembly.LinkError object indicates an error during +/// module instantiation (besides traps from the start function). +/// +/// This is based on the [link error][link-error] API. +/// +/// [link-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/LinkError +#[derive(Error, Debug)] +#[error("Link error: {0}")] +pub enum LinkError { + /// An error occurred when checking the import types. + #[error("Error while importing {0:?}.{1:?}: {2}")] + Import(String, String, Box), + + /// A trap ocurred during linking. + #[error("RuntimeError occurred during linking: {0}")] + Trap(#[source] RuntimeError), + + /// Insufficient resources available for linking. + #[error("Insufficient resources: {0}")] + Resource(String), +} + +/// An error while instantiating a module. +/// +/// This is not a common WebAssembly error, however +/// we need to differentiate from a `LinkError` (an error +/// that happens while linking, on instantiation) and a +/// Trap that occurs when calling the WebAssembly module +/// start function. +#[derive(Error, Debug)] +pub enum InstantiationError { + /// A linking ocurred during instantiation. + #[error(transparent)] + Link(LinkError), + + /// The module was compiled with a CPU feature that is not available on + /// the current host. + #[error("module compiled with CPU feature that is missing from host")] + CpuFeature(String), + + /// A runtime error occured while invoking the start function + #[error(transparent)] + Start(RuntimeError), +} diff --git a/runtime/unc-vm/engine/src/export.rs b/runtime/unc-vm/engine/src/export.rs new file mode 100644 index 000000000..6a63961be --- /dev/null +++ b/runtime/unc-vm/engine/src/export.rs @@ -0,0 +1,3 @@ +use std::sync::Arc; +use unc_vm_vm::{ImportInitializerFuncPtr, VMExtern, VMFunction, VMGlobal, VMMemory, VMTable}; + diff --git a/runtime/unc-vm/engine/src/lib.rs b/runtime/unc-vm/engine/src/lib.rs new file mode 100644 index 000000000..79afbd88f --- /dev/null +++ b/runtime/unc-vm/engine/src/lib.rs @@ -0,0 +1,36 @@ +//! Generic Engine abstraction for Wasmer Engines. + +#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] +#![warn(unused_import_braces)] +#![cfg_attr( + feature = "cargo-clippy", + allow(clippy::new_without_default, clippy::new_without_default) +)] +#![cfg_attr( + feature = "cargo-clippy", + warn( + clippy::float_arithmetic, + clippy::mut_mut, + clippy::nonminimal_bool, + clippy::map_unwrap_or, + clippy::print_stdout, + clippy::unicode_not_nfc, + clippy::use_self + ) +)] + +mod engine; +mod error; +mod resolver; +mod trap; + +/// Universal engine +pub mod universal; + +pub use crate::engine::EngineId; +pub use crate::error::{DeserializeError, ImportError, InstantiationError, LinkError}; +pub use crate::resolver::resolve_imports; +pub use crate::trap::*; + +/// Version number of this crate. +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/runtime/unc-vm/engine/src/resolver.rs b/runtime/unc-vm/engine/src/resolver.rs new file mode 100644 index 000000000..f908b77b9 --- /dev/null +++ b/runtime/unc-vm/engine/src/resolver.rs @@ -0,0 +1,197 @@ +//! Define the `Resolver` trait, allowing custom resolution for external +//! references. + +use crate::{ImportError, LinkError}; +use more_asserts::assert_ge; +use unc_vm_types::entity::{BoxedSlice, EntityRef, PrimaryMap}; +use unc_vm_types::{ExternType, FunctionIndex, ImportCounts, MemoryType, TableType}; + +use unc_vm_vm::{ + Export, ExportFunctionMetadata, FunctionBodyPtr, ImportFunctionEnv, Imports, MemoryStyle, + Resolver, VMFunctionBody, VMFunctionEnvironment, VMFunctionImport, VMFunctionKind, + VMGlobalImport, VMImport, VMImportType, VMMemoryImport, VMTableImport, +}; + +fn is_compatible_table(ex: &TableType, im: &TableType) -> bool { + (ex.ty == unc_vm_types::Type::FuncRef || ex.ty == im.ty) + && im.minimum <= ex.minimum + && (im.maximum.is_none() + || (ex.maximum.is_some() && im.maximum.unwrap() >= ex.maximum.unwrap())) +} + +fn is_compatible_memory(ex: &MemoryType, im: &MemoryType) -> bool { + im.minimum <= ex.minimum + && (im.maximum.is_none() + || (ex.maximum.is_some() && im.maximum.unwrap() >= ex.maximum.unwrap())) + && ex.shared == im.shared +} + +/// This function allows to match all imports of a `ModuleInfo` with concrete definitions provided by +/// a `Resolver`. +/// +/// If all imports are satisfied returns an `Imports` instance required for a module instantiation. +pub fn resolve_imports( + engine: &crate::universal::UniversalEngine, + resolver: &dyn Resolver, + import_counts: &ImportCounts, + imports: &[VMImport], + finished_dynamic_function_trampolines: &BoxedSlice, +) -> Result { + let mut function_imports = PrimaryMap::with_capacity(import_counts.functions as _); + let mut host_function_env_initializers = + PrimaryMap::with_capacity(import_counts.functions as _); + let mut table_imports = PrimaryMap::with_capacity(import_counts.tables as _); + let mut memory_imports = PrimaryMap::with_capacity(import_counts.memories as _); + let mut global_imports = PrimaryMap::with_capacity(import_counts.globals as _); + for VMImport { import_no, module, field, ty } in imports { + let resolved = resolver.resolve(*import_no, module, field); + let import_extern = || match ty { + &VMImportType::Table(t) => ExternType::Table(t), + &VMImportType::Memory(t, _) => ExternType::Memory(t), + &VMImportType::Global(t) => ExternType::Global(t), + &VMImportType::Function { sig, static_trampoline: _ } => ExternType::Function( + engine.lookup_signature(sig).expect("VMSharedSignatureIndex is not valid?"), + ), + }; + let resolved = match resolved { + Some(r) => r, + None => { + return Err(LinkError::Import( + module.to_string(), + field.to_string(), + ImportError::UnknownImport(import_extern()).into(), + )); + } + }; + let export_extern = || match resolved { + Export::Function(ref f) => ExternType::Function( + engine + .lookup_signature(f.vm_function.signature) + .expect("VMSharedSignatureIndex not registered with engine (wrong engine?)"), + ), + Export::Table(ref t) => ExternType::Table(*t.ty()), + Export::Memory(ref m) => ExternType::Memory(m.ty()), + Export::Global(ref g) => { + let global = g.from.ty(); + ExternType::Global(*global) + } + }; + match (&resolved, ty) { + (Export::Function(ex), VMImportType::Function { sig, static_trampoline }) + if ex.vm_function.signature == *sig => + { + let address = match ex.vm_function.kind { + VMFunctionKind::Dynamic => { + // If this is a dynamic imported function, + // the address of the function is the address of the + // reverse trampoline. + let index = FunctionIndex::new(function_imports.len()); + finished_dynamic_function_trampolines[index].0 as *mut VMFunctionBody as _ + + // TODO: We should check that the f.vmctx actually matches + // the shape of `VMDynamicFunctionImportContext` + } + VMFunctionKind::Static => ex.vm_function.address, + }; + + // Clone the host env for this `Instance`. + let env = if let Some(ExportFunctionMetadata { host_env_clone_fn: clone, .. }) = + ex.metadata.as_deref() + { + // TODO: maybe start adding asserts in all these + // unsafe blocks to prevent future changes from + // horribly breaking things. + unsafe { + assert!(!ex.vm_function.vmctx.host_env.is_null()); + (clone)(ex.vm_function.vmctx.host_env) + } + } else { + // No `clone` function means we're dealing with some + // other kind of `vmctx`, not a host env of any + // kind. + unsafe { ex.vm_function.vmctx.host_env } + }; + + let trampoline = if let Some(t) = ex.vm_function.call_trampoline { + Some(t) + } else if let VMFunctionKind::Static = ex.vm_function.kind { + // Look up a trampoline by finding one by the signature and fill it in. + Some(*static_trampoline) + } else { + // FIXME: remove this possibility entirely. + None + }; + + function_imports.push(VMFunctionImport { + body: FunctionBodyPtr(address), + signature: *sig, + environment: VMFunctionEnvironment { host_env: env }, + trampoline, + }); + + let initializer = ex.metadata.as_ref().and_then(|m| m.import_init_function_ptr); + let clone = ex.metadata.as_ref().map(|m| m.host_env_clone_fn); + let destructor = ex.metadata.as_ref().map(|m| m.host_env_drop_fn); + let import_function_env = + if let (Some(clone), Some(destructor)) = (clone, destructor) { + ImportFunctionEnv::Env { env, clone, initializer, destructor } + } else { + ImportFunctionEnv::NoEnv + }; + + host_function_env_initializers.push(import_function_env); + } + (Export::Table(ex), VMImportType::Table(im)) if is_compatible_table(ex.ty(), im) => { + let import_table_ty = ex.from.ty(); + if import_table_ty.ty != im.ty { + return Err(LinkError::Import( + module.to_string(), + field.to_string(), + ImportError::IncompatibleType(import_extern(), export_extern()).into(), + )); + } + table_imports + .push(VMTableImport { definition: ex.from.vmtable(), from: ex.from.clone() }); + } + (Export::Memory(ex), VMImportType::Memory(im, import_memory_style)) + if is_compatible_memory(&ex.ty(), im) => + { + // Sanity-check: Ensure that the imported memory has at least + // guard-page protections the importing module expects it to have. + let export_memory_style = ex.style(); + if let ( + MemoryStyle::Static { bound, .. }, + MemoryStyle::Static { bound: import_bound, .. }, + ) = (export_memory_style.clone(), &import_memory_style) + { + assert_ge!(bound, *import_bound); + } + assert_ge!( + export_memory_style.offset_guard_size(), + import_memory_style.offset_guard_size() + ); + memory_imports + .push(VMMemoryImport { definition: ex.from.vmmemory(), from: ex.from.clone() }); + } + + (Export::Global(ex), VMImportType::Global(im)) if ex.from.ty() == im => { + global_imports + .push(VMGlobalImport { definition: ex.from.vmglobal(), from: ex.from.clone() }); + } + _ => { + return Err(LinkError::Import( + module.to_string(), + field.to_string(), + ImportError::IncompatibleType(import_extern(), export_extern()).into(), + )); + } + } + } + Ok(Imports::new( + function_imports, + host_function_env_initializers, + table_imports, + memory_imports, + global_imports, + )) +} diff --git a/runtime/unc-vm/engine/src/trap/error.rs b/runtime/unc-vm/engine/src/trap/error.rs new file mode 100644 index 000000000..73efcb0b2 --- /dev/null +++ b/runtime/unc-vm/engine/src/trap/error.rs @@ -0,0 +1,236 @@ +use super::frame_info::{FrameInfo, GlobalFrameInfo, FRAME_INFO}; +use backtrace::Backtrace; +use unc_vm_vm::{raise_user_trap, Trap, TrapCode}; +use std::error::Error; +use std::fmt; +use std::sync::Arc; + +/// A struct representing an aborted instruction execution, with a message +/// indicating the cause. +#[derive(Clone)] +pub struct RuntimeError { + inner: Arc, +} + +/// The source of the `RuntimeError`. +#[derive(Debug)] +enum RuntimeErrorSource { + Generic(String), + OOM, + User(Box), + Trap(TrapCode), +} + +impl fmt::Display for RuntimeErrorSource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Generic(s) => write!(f, "{}", s), + Self::User(s) => write!(f, "{}", s), + Self::OOM => write!(f, "Wasmer VM out of memory"), + Self::Trap(s) => write!(f, "{}", s.message()), + } + } +} + +struct RuntimeErrorInner { + /// The source error (this can be a custom user `Error` or a [`TrapCode`]) + source: RuntimeErrorSource, + /// The reconstructed Wasm trace (from the native trace and the `GlobalFrameInfo`). + wasm_trace: Vec, + /// The native backtrace + native_trace: Backtrace, +} + +fn _assert_trap_is_sync_and_send(t: &Trap) -> (&dyn Sync, &dyn Send) { + (t, t) +} + +impl RuntimeError { + /// Creates a new generic `RuntimeError` with the given `message`. + /// + /// # Example + /// ``` + /// let trap = unc_vm_engine::RuntimeError::new("unexpected error"); + /// assert_eq!("unexpected error", trap.message()); + /// ``` + pub fn new>(message: I) -> Self { + let info = FRAME_INFO.read().unwrap(); + let msg = message.into(); + Self::new_with_trace( + &info, + None, + RuntimeErrorSource::Generic(msg), + Backtrace::new_unresolved(), + ) + } + + /// Create a new RuntimeError from a Trap. + pub fn from_trap(trap: Trap) -> Self { + let info = FRAME_INFO.read().unwrap(); + match trap { + // A user error + Trap::User(error) => { + match error.downcast::() { + // The error is already a RuntimeError, we return it directly + Ok(runtime_error) => *runtime_error, + Err(e) => Self::new_with_trace( + &info, + None, + RuntimeErrorSource::User(e), + Backtrace::new_unresolved(), + ), + } + } + // A trap caused by the VM being Out of Memory + Trap::OOM { backtrace } => { + Self::new_with_trace(&info, None, RuntimeErrorSource::OOM, backtrace) + } + // A trap caused by an error on the generated machine code for a Wasm function + Trap::Wasm { pc, signal_trap, backtrace } => { + let code = info + .lookup_trap_info(pc) + .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| info.trap_code); + Self::new_with_trace(&info, Some(pc), RuntimeErrorSource::Trap(code), backtrace) + } + // A trap triggered manually from the Wasmer runtime + Trap::Lib { trap_code, backtrace } => { + Self::new_with_trace(&info, None, RuntimeErrorSource::Trap(trap_code), backtrace) + } + } + } + + /// Raises a custom user Error + pub fn raise(error: Box) -> ! { + unsafe { raise_user_trap(error) } + } + + fn new_with_trace( + info: &GlobalFrameInfo, + trap_pc: Option, + source: RuntimeErrorSource, + native_trace: Backtrace, + ) -> Self { + let frames: Vec = native_trace + .frames() + .iter() + .filter_map(|frame| { + let pc = frame.ip() as usize; + if pc == 0 { + None + } else { + // Note that we need to be careful about the pc we pass in here to + // lookup frame information. This program counter is used to + // translate back to an original source location in the origin wasm + // module. If this pc is the exact pc that the trap happened at, + // then we look up that pc precisely. Otherwise backtrace + // information typically points at the pc *after* the call + // instruction (because otherwise it's likely a call instruction on + // the stack). In that case we want to lookup information for the + // previous instruction (the call instruction) so we subtract one as + // the lookup. + let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 }; + Some(pc_to_lookup) + } + }) + .collect(); + + // Let's construct the trace + let wasm_trace = + frames.into_iter().filter_map(|pc| info.lookup_frame_info(pc)).collect::>(); + + Self { inner: Arc::new(RuntimeErrorInner { source, wasm_trace, native_trace }) } + } + + /// Returns a reference the `message` stored in `Trap`. + pub fn message(&self) -> String { + self.inner.source.to_string() + } + + /// Returns a list of function frames in WebAssembly code that led to this + /// trap happening. + pub fn trace(&self) -> &[FrameInfo] { + &self.inner.wasm_trace + } + + /// Attempts to downcast the `RuntimeError` to a concrete type. + pub fn downcast(self) -> Result { + match Arc::try_unwrap(self.inner) { + // We only try to downcast user errors + Ok(RuntimeErrorInner { source: RuntimeErrorSource::User(err), .. }) + if err.is::() => + { + Ok(*err.downcast::().unwrap()) + } + Ok(inner) => Err(Self { inner: Arc::new(inner) }), + Err(inner) => Err(Self { inner }), + } + } + + /// Returns trap code, if it's a Trap + pub fn to_trap(self) -> Option { + if let RuntimeErrorSource::Trap(trap_code) = self.inner.source { + Some(trap_code) + } else { + None + } + } + + /// Returns true if the `RuntimeError` is the same as T + pub fn is(&self) -> bool { + match &self.inner.source { + RuntimeErrorSource::User(err) => err.is::(), + _ => false, + } + } +} + +impl fmt::Debug for RuntimeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RuntimeError") + .field("source", &self.inner.source) + .field("wasm_trace", &self.inner.wasm_trace) + .field("native_trace", &self.inner.native_trace) + .finish() + } +} + +impl fmt::Display for RuntimeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RuntimeError: {}", self.message())?; + let trace = self.trace(); + if trace.is_empty() { + return Ok(()); + } + for frame in self.trace().iter() { + let name = frame.module_name(); + let func_index = frame.func_index(); + writeln!(f)?; + write!(f, " at ")?; + match frame.function_name() { + Some(name) => match rustc_demangle::try_demangle(name) { + Ok(name) => write!(f, "{}", name)?, + Err(_) => write!(f, "{}", name)?, + }, + None => write!(f, "")?, + } + write!(f, " ({}[{}]:0x{:x})", name, func_index, frame.module_offset())?; + } + Ok(()) + } +} + +impl std::error::Error for RuntimeError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self.inner.source { + RuntimeErrorSource::User(err) => Some(&**err), + RuntimeErrorSource::Trap(err) => Some(err), + _ => None, + } + } +} + +impl From for RuntimeError { + fn from(trap: Trap) -> Self { + Self::from_trap(trap) + } +} diff --git a/runtime/unc-vm/engine/src/trap/frame_info.rs b/runtime/unc-vm/engine/src/trap/frame_info.rs new file mode 100644 index 000000000..514cf49ba --- /dev/null +++ b/runtime/unc-vm/engine/src/trap/frame_info.rs @@ -0,0 +1,242 @@ +//! This module is used for having backtraces in the Wasm runtime. +//! Once the Compiler has compiled the ModuleInfo, and we have a set of +//! compiled functions (addresses and function index) and a module, +//! then we can use this to set a backtrace for that module. +//! +//! # Example +//! ```ignore +//! use unc_vm_vm::{FRAME_INFO}; +//! use unc_vm_types::ModuleInfo; +//! +//! let module: ModuleInfo = ...; +//! FRAME_INFO.register(module, compiled_functions); +//! ``` +use unc_vm_compiler::{CompiledFunctionFrameInfo, SourceLoc, TrapInformation}; +use unc_vm_types::entity::{EntityRef, PrimaryMap}; +use unc_vm_types::{LocalFunctionIndex, ModuleInfo}; +use std::collections::BTreeMap; +use std::sync::{Arc, RwLock}; + +lazy_static::lazy_static! { + /// This is a global cache of backtrace frame information for all active + /// + /// This global cache is used during `Trap` creation to symbolicate frames. + /// This is populated on module compilation, and it is cleared out whenever + /// all references to a module are dropped. + pub static ref FRAME_INFO: RwLock = Default::default(); +} + +#[derive(Default)] +pub struct GlobalFrameInfo { + /// An internal map that keeps track of backtrace frame information for + /// each module. + /// + /// This map is morally a map of ranges to a map of information for that + /// module. Each module is expected to reside in a disjoint section of + /// contiguous memory. No modules can overlap. + /// + /// The key of this map is the highest address in the module and the value + /// is the module's information, which also contains the start address. + ranges: BTreeMap, +} + +/// An RAII structure used to unregister a module's frame information when the +/// module is destroyed. +pub struct GlobalFrameInfoRegistration { + /// The key that will be removed from the global `ranges` map when this is + /// dropped. + key: usize, +} + +#[derive(Debug)] +struct ModuleInfoFrameInfo { + start: usize, + functions: BTreeMap, + module: Arc, + frame_infos: PrimaryMap, +} + +impl ModuleInfoFrameInfo { + fn function_debug_info(&self, local_index: LocalFunctionIndex) -> &CompiledFunctionFrameInfo { + &self.frame_infos.get(local_index).unwrap() + } + + /// Gets a function given a pc + fn function_info(&self, pc: usize) -> Option<&FunctionInfo> { + let (end, func) = self.functions.range(pc..).next()?; + if func.start <= pc && pc <= *end { + return Some(func); + } else { + None + } + } +} + +#[derive(Debug)] +struct FunctionInfo { + start: usize, + local_index: LocalFunctionIndex, +} + +impl GlobalFrameInfo { + /// Fetches frame information about a program counter in a backtrace. + /// + /// Returns an object if this `pc` is known to some previously registered + /// module, or returns `None` if no information can be found. + pub fn lookup_frame_info(&self, pc: usize) -> Option { + let module = self.module_info(pc)?; + let func = module.function_info(pc)?; + + // Use our relative position from the start of the function to find the + // machine instruction that corresponds to `pc`, which then allows us to + // map that to a wasm original source location. + let rel_pos = pc - func.start; + let instr_map = &module.function_debug_info(func.local_index).address_map; + let pos = match instr_map.instructions.binary_search_by_key(&rel_pos, |map| map.code_offset) + { + // Exact hit! + Ok(pos) => Some(pos), + + // This *would* be at the first slot in the array, so no + // instructions cover `pc`. + Err(0) => None, + + // This would be at the `nth` slot, so check `n-1` to see if we're + // part of that instruction. This happens due to the minus one when + // this function is called form trap symbolication, where we don't + // always get called with a `pc` that's an exact instruction + // boundary. + Err(n) => { + let instr = &instr_map.instructions[n - 1]; + if instr.code_offset <= rel_pos && rel_pos < instr.code_offset + instr.code_len { + Some(n - 1) + } else { + None + } + } + }; + + let instr = match pos { + Some(pos) => instr_map.instructions[pos].srcloc, + // Some compilers don't emit yet the full trap information for each of + // the instructions (such as LLVM). + // In case no specific instruction is found, we return by default the + // start offset of the function. + None => instr_map.start_srcloc, + }; + let func_index = module.module.func_index(func.local_index); + Some(FrameInfo { + module_name: module.module.name(), + func_index: func_index.index() as u32, + function_name: module.module.function_names.get(&func_index).cloned(), + instr, + func_start: instr_map.start_srcloc, + }) + } + + /// Fetches trap information about a program counter in a backtrace. + pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> { + let module = self.module_info(pc)?; + let func = module.function_info(pc)?; + let traps = &module.function_debug_info(func.local_index).traps; + let idx = traps + .binary_search_by_key(&((pc - func.start) as u32), |info| info.code_offset) + .ok()?; + Some(&traps[idx]) + } + + /// Gets a module given a pc + fn module_info(&self, pc: usize) -> Option<&ModuleInfoFrameInfo> { + let (end, module_info) = self.ranges.range(pc..).next()?; + if module_info.start <= pc && pc <= *end { + Some(module_info) + } else { + None + } + } +} + +impl Drop for GlobalFrameInfoRegistration { + fn drop(&mut self) { + if let Ok(mut info) = FRAME_INFO.write() { + info.ranges.remove(&self.key); + } + } +} + +/// Description of a frame in a backtrace for a [`RuntimeError::trace`](crate::RuntimeError::trace). +/// +/// Whenever a WebAssembly trap occurs an instance of [`RuntimeError`] +/// is created. Each [`RuntimeError`] has a backtrace of the +/// WebAssembly frames that led to the trap, and each frame is +/// described by this structure. +/// +/// [`RuntimeError`]: crate::RuntimeError +#[derive(Debug, Clone)] +pub struct FrameInfo { + module_name: String, + func_index: u32, + function_name: Option, + func_start: SourceLoc, + instr: SourceLoc, +} + +impl FrameInfo { + /// Returns the WebAssembly function index for this frame. + /// + /// This function index is the index in the function index space of the + /// WebAssembly module that this frame comes from. + pub fn func_index(&self) -> u32 { + self.func_index + } + + /// Returns the identifer of the module that this frame is for. + /// + /// ModuleInfo identifiers are present in the `name` section of a WebAssembly + /// binary, but this may not return the exact item in the `name` section. + /// ModuleInfo names can be overwritten at construction time or perhaps inferred + /// from file names. The primary purpose of this function is to assist in + /// debugging and therefore may be tweaked over time. + /// + /// This function returns `None` when no name can be found or inferred. + pub fn module_name(&self) -> &str { + &self.module_name + } + + /// Returns a descriptive name of the function for this frame, if one is + /// available. + /// + /// The name of this function may come from the `name` section of the + /// WebAssembly binary, or unc_vm may try to infer a better name for it if + /// not available, for example the name of the export if it's exported. + /// + /// This return value is primarily used for debugging and human-readable + /// purposes for things like traps. Note that the exact return value may be + /// tweaked over time here and isn't guaranteed to be something in + /// particular about a wasm module due to its primary purpose of assisting + /// in debugging. + /// + /// This function returns `None` when no name could be inferred. + pub fn function_name(&self) -> Option<&str> { + self.function_name.as_deref() + } + + /// Returns the offset within the original wasm module this frame's program + /// counter was at. + /// + /// The offset here is the offset from the beginning of the original wasm + /// module to the instruction that this frame points to. + pub fn module_offset(&self) -> usize { + self.instr.bits() as usize + } + + /// Returns the offset from the original wasm module's function to this + /// frame's program counter. + /// + /// The offset here is the offset from the beginning of the defining + /// function of this frame (within the wasm module) to the instruction this + /// frame points to. + pub fn func_offset(&self) -> usize { + (self.instr.bits() - self.func_start.bits()) as usize + } +} diff --git a/runtime/unc-vm/engine/src/trap/mod.rs b/runtime/unc-vm/engine/src/trap/mod.rs new file mode 100644 index 000000000..3144b41cf --- /dev/null +++ b/runtime/unc-vm/engine/src/trap/mod.rs @@ -0,0 +1,4 @@ +mod error; +mod frame_info; +pub use error::RuntimeError; +pub use frame_info::{FrameInfo, GlobalFrameInfoRegistration}; diff --git a/runtime/unc-vm/engine/src/universal/artifact.rs b/runtime/unc-vm/engine/src/universal/artifact.rs new file mode 100644 index 000000000..e0ac292f0 --- /dev/null +++ b/runtime/unc-vm/engine/src/universal/artifact.rs @@ -0,0 +1,191 @@ +//! Define `UniversalArtifact` to allow compiling and instantiating to be +//! done as separate steps. + +use crate::InstantiationError; +use unc_vm_types::entity::{BoxedSlice, EntityRef, PrimaryMap}; +use unc_vm_types::{ + DataIndex, ElemIndex, FunctionIndex, GlobalInit, GlobalType, ImportCounts, LocalFunctionIndex, + LocalGlobalIndex, MemoryType, OwnedDataInitializer, OwnedTableInitializer, SignatureIndex, + TableType, +}; +use unc_vm_vm::{ + Artifact, FunctionBodyPtr, FunctionExtent, InstanceHandle, Instantiatable, MemoryStyle, + Resolver, TableStyle, Tunables, VMImport, VMImportType, VMLocalFunction, VMOffsets, + VMSharedSignatureIndex, +}; +use std::collections::BTreeMap; +use std::convert::TryFrom; +use std::sync::Arc; + +/// A compiled wasm module, containing everything necessary for instantiation. +pub struct UniversalArtifact { + // TODO: figure out how to allocate fewer distinct structures onto heap. Maybe have an arena…? + pub(crate) engine: super::UniversalEngine, + pub(crate) _code_memory: super::CodeMemory, + pub(crate) import_counts: ImportCounts, + pub(crate) start_function: Option, + pub(crate) vmoffsets: VMOffsets, + pub(crate) imports: Vec, + pub(crate) dynamic_function_trampolines: BoxedSlice, + pub(crate) functions: BoxedSlice, + pub(crate) exports: BTreeMap, + pub(crate) signatures: BoxedSlice, + pub(crate) local_memories: Vec<(MemoryType, MemoryStyle)>, + pub(crate) data_segments: Vec, + pub(crate) passive_data: BTreeMap>, + pub(crate) local_tables: Vec<(TableType, TableStyle)>, + pub(crate) element_segments: Vec, + // TODO: does this need to be a BTreeMap? Can it be a plain vector? + pub(crate) passive_elements: BTreeMap>, + pub(crate) local_globals: Vec<(GlobalType, GlobalInit)>, +} + +impl UniversalArtifact { + /// Return the extents of the specified local function. + pub fn function_extent(&self, index: LocalFunctionIndex) -> Option { + let func = self.functions.get(index)?; + Some(FunctionExtent { address: func.body, length: usize::try_from(func.length).unwrap() }) + } + + /// Return the engine instance this artifact is loaded into. + pub fn engine(&self) -> &super::UniversalEngine { + &self.engine + } +} + +impl Instantiatable for UniversalArtifact { + type Error = InstantiationError; + + unsafe fn instantiate( + self: Arc, + tunables: &dyn Tunables, + resolver: &dyn Resolver, + host_state: Box, + config: unc_vm_types::InstanceConfig, + ) -> Result { + let (imports, import_function_envs) = { + let mut imports = crate::resolve_imports( + &self.engine, + resolver, + &self.import_counts, + &self.imports, + &self.dynamic_function_trampolines, + ) + .map_err(InstantiationError::Link)?; + + // Get the `WasmerEnv::init_with_instance` function pointers and the pointers + // to the envs to call it on. + let import_function_envs = imports.get_imported_function_envs(); + + (imports, import_function_envs) + }; + + let (allocator, memory_definition_locations, table_definition_locations) = + unc_vm_vm::InstanceAllocator::new(self.vmoffsets.clone()); + + // Memories + let mut memories: PrimaryMap = + PrimaryMap::with_capacity(self.local_memories.len()); + for (idx, (ty, style)) in (self.import_counts.memories..).zip(self.local_memories.iter()) { + let memory = tunables + .create_vm_memory(&ty, &style, memory_definition_locations[idx as usize]) + .map_err(|e| { + InstantiationError::Link(crate::LinkError::Resource(format!( + "Failed to create memory: {}", + e + ))) + })?; + memories.push(memory); + } + + // Tables + let mut tables: PrimaryMap = + PrimaryMap::with_capacity(self.local_tables.len()); + for (idx, (ty, style)) in (self.import_counts.tables..).zip(self.local_tables.iter()) { + let table = tunables + .create_vm_table(ty, style, table_definition_locations[idx as usize]) + .map_err(|e| InstantiationError::Link(crate::LinkError::Resource(e)))?; + tables.push(table); + } + + // Globals + let mut globals = + PrimaryMap::::with_capacity(self.local_globals.len()); + for (ty, _) in self.local_globals.iter() { + globals.push(Arc::new(unc_vm_vm::Global::new(*ty))); + } + + let passive_data = self.passive_data.clone(); + Ok(InstanceHandle::new( + self, + allocator, + memories.into_boxed_slice(), + tables.into_boxed_slice(), + globals.into_boxed_slice(), + imports, + passive_data, + host_state, + import_function_envs, + config, + )) + } +} + +impl Artifact for UniversalArtifact { + fn offsets(&self) -> &unc_vm_vm::VMOffsets { + &self.vmoffsets + } + + fn import_counts(&self) -> &ImportCounts { + &self.import_counts + } + + fn functions(&self) -> &BoxedSlice { + &self.functions + } + + fn passive_elements(&self) -> &BTreeMap> { + &self.passive_elements + } + + fn element_segments(&self) -> &[OwnedTableInitializer] { + &self.element_segments[..] + } + + fn data_segments(&self) -> &[OwnedDataInitializer] { + &self.data_segments[..] + } + + fn globals(&self) -> &[(GlobalType, GlobalInit)] { + &self.local_globals[..] + } + + fn start_function(&self) -> Option { + self.start_function + } + + fn export_field(&self, name: &str) -> Option { + self.exports.get(name).cloned() + } + + fn signatures(&self) -> &[unc_vm_vm::VMSharedSignatureIndex] { + self.signatures.values().as_slice() + } + + fn function_signature(&self, index: FunctionIndex) -> Option { + match self.import_counts().local_function_index(index) { + Ok(local) => Some(self.functions[local].signature), + Err(import) => self + .imports + .iter() + .filter_map(|im| { + if let VMImportType::Function { sig, .. } = im.ty { + Some(sig) + } else { + None + } + }) + .nth(import.index()), + } + } +} diff --git a/runtime/unc-vm/engine/src/universal/builder.rs b/runtime/unc-vm/engine/src/universal/builder.rs new file mode 100644 index 000000000..8d3c2ad44 --- /dev/null +++ b/runtime/unc-vm/engine/src/universal/builder.rs @@ -0,0 +1,65 @@ +use crate::universal::UniversalEngine; +use unc_vm_compiler::{CompilerConfig, Features, Target}; + +/// The Universal builder +pub struct Universal { + #[allow(dead_code)] + compiler_config: Option>, + target: Option, + features: Option, + pool: Option, +} + +impl Universal { + /// Create a new Universal + pub fn new(compiler_config: T) -> Self + where + T: Into>, + { + Self { + compiler_config: Some(compiler_config.into()), + target: None, + features: None, + pool: None, + } + } + + /// Create a new headless Universal + pub fn headless() -> Self { + Self { compiler_config: None, target: None, features: None, pool: None } + } + + /// Set the target + pub fn target(mut self, target: Target) -> Self { + self.target = Some(target); + self + } + + /// Set the features + pub fn features(mut self, features: Features) -> Self { + self.features = Some(features); + self + } + + /// Set the pool of reusable code memory + pub fn code_memory_pool(mut self, pool: super::LimitedMemoryPool) -> Self { + self.pool = Some(pool); + self + } + + /// Build the `UniversalEngine` for this configuration + pub fn engine(self) -> UniversalEngine { + let target = self.target.unwrap_or_default(); + let pool = + self.pool.unwrap_or_else(|| panic!("Universal::code_memory_pool was not set up!")); + if let Some(compiler_config) = self.compiler_config { + let features = self + .features + .unwrap_or_else(|| compiler_config.default_features_for_target(&target)); + let compiler = compiler_config.compiler(); + UniversalEngine::new(compiler, target, features, pool) + } else { + UniversalEngine::headless(pool) + } + } +} diff --git a/runtime/unc-vm/engine/src/universal/code_memory.rs b/runtime/unc-vm/engine/src/universal/code_memory.rs new file mode 100644 index 000000000..034314f15 --- /dev/null +++ b/runtime/unc-vm/engine/src/universal/code_memory.rs @@ -0,0 +1,286 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Memory management for executable code. +use unc_vm_compiler::CompileError; +use rustix::mm::{self, MapFlags, MprotectFlags, ProtFlags}; +use std::sync::Arc; + +/// The optimal alignment for functions. +/// +/// On x86-64, this is 16 since it's what the optimizations assume. +/// When we add support for other architectures, we should also figure out their +/// optimal alignment values. +pub(crate) const ARCH_FUNCTION_ALIGNMENT: u16 = 16; + +/// The optimal alignment for data. +/// +pub(crate) const DATA_SECTION_ALIGNMENT: u16 = 64; + +fn round_up(size: usize, multiple: usize) -> usize { + debug_assert!(multiple.is_power_of_two()); + (size + (multiple - 1)) & !(multiple - 1) +} + +pub struct CodeMemoryWriter<'a> { + memory: &'a mut CodeMemory, + offset: usize, +} + +impl<'a> CodeMemoryWriter<'a> { + /// Write the contents from the provided buffer into the location of `self.memory` aligned to + /// provided `alignment`. + /// + /// The `alignment` actually used may be greater than the spepcified value. This is relevant, + /// for example, when calling this function after a sequence of [`Self::write_executable`] + /// calls. + /// + /// Returns the position within the mapping at which the buffer was written. + pub fn write_data(&mut self, mut alignment: u16, input: &[u8]) -> Result { + if self.offset == self.memory.executable_end { + alignment = u16::try_from(rustix::param::page_size()).expect("page size > u16::MAX"); + } + self.write_inner(alignment, input) + } + + /// Write the executable code from the provided buffer into the executable portion of + /// `self.memory`. + /// + /// All executable parts must be written out before `self.write_data` is called for the first + /// time. + /// + /// Returns the position within the mapping at which the buffer was written. + pub fn write_executable( + &mut self, + alignment: u16, + input: &[u8], + ) -> Result { + assert_eq!( + self.memory.executable_end, self.offset, + "may not interleave executable and data in the same map" + ); + let result = self.write_inner(alignment, input); + self.memory.executable_end = self.offset; + result + } + + fn write_inner(&mut self, alignment: u16, input: &[u8]) -> Result { + let entry_offset = self.offset; + let aligned_offset = round_up(entry_offset, usize::from(alignment)); + let final_offset = aligned_offset + input.len(); + let out_buffer = self.memory.as_slice_mut(); + // Fill out the padding with zeroes, if only to make sure there are no gadgets in there. + out_buffer + .get_mut(entry_offset..aligned_offset) + .ok_or_else(|| CompileError::Resource("out of code memory space".into()))? + .fill(0); + out_buffer + .get_mut(aligned_offset..final_offset) + .ok_or_else(|| CompileError::Resource("out of code memory space".into()))? + .copy_from_slice(input); + self.offset = final_offset; + Ok(aligned_offset) + } + + /// The current position of the writer. + pub fn position(&self) -> usize { + self.offset + } +} + +/// Mappings to regions of memory storing the executable JIT code. +pub struct CodeMemory { + /// Where to return this memory to when dropped. + source_pool: Option>>, + + /// The mapping + map: *mut u8, + + /// Mapping size + size: usize, + + /// Addresses `0..executable_end` contain executable memory. + /// + /// In a populated buffer rounding this up to the next page will give the address of the + /// read-write data portion of this memory. + executable_end: usize, +} + +impl CodeMemory { + fn create(size: usize) -> rustix::io::Result { + // Make sure callers don’t pass in a 0-sized map request. That is most likely a bug. + assert!(size != 0); + let size = round_up(size, rustix::param::page_size()); + let map = unsafe { + mm::mmap_anonymous( + std::ptr::null_mut(), + size, + ProtFlags::WRITE | ProtFlags::READ, + MapFlags::SHARED, + )? + }; + Ok(Self { source_pool: None, map: map.cast(), executable_end: 0, size }) + } + + fn as_slice_mut(&mut self) -> &mut [u8] { + unsafe { + // SAFETY: We have made sure that this is the only reference to the memory region by + // requiring a mutable self reference. + std::slice::from_raw_parts_mut(self.map, self.size) + } + } + + /// Ensure this CodeMemory is at least of the requested size. + /// + /// This will invalidate any data previously written into the mapping if the mapping needs to + /// be resized. + pub fn resize(mut self, size: usize) -> rustix::io::Result { + if self.size < size { + // Ideally we would use mremap, but see + // https://bugzilla.kernel.org/show_bug.cgi?id=8691 + let source_pool = unsafe { + mm::munmap(self.map.cast(), self.size)?; + let source_pool = self.source_pool.take(); + std::mem::forget(self); + source_pool + }; + Self::create(size).map(|mut m| { + m.source_pool = source_pool; + m + }) + } else { + self.executable_end = 0; + Ok(self) + } + } + + /// Write to this code memory from the beginning of the mapping. + /// + /// # Safety + /// + /// At the time this method is called, there should remain no dangling readable/executable + /// references to this `CodeMemory`, for the original code memory that those references point + /// to are invalidated as soon as this method is invoked. + pub unsafe fn writer(&mut self) -> CodeMemoryWriter<'_> { + self.executable_end = 0; + CodeMemoryWriter { memory: self, offset: 0 } + } + + /// Publish the specified number of bytes as executable code. + /// + /// # Safety + /// + /// Calling this requires that no mutable references to the code memory remain. + pub unsafe fn publish(&mut self) -> Result<(), CompileError> { + mm::mprotect( + self.map.cast(), + self.executable_end, + MprotectFlags::EXEC | MprotectFlags::READ, + ) + .map_err(|e| { + CompileError::Resource(format!("could not make code memory executable: {}", e)) + }) + } + + /// Remap the offset into an absolute address within a read-execute mapping. + /// + /// Offset must not exceed `isize::MAX`. + pub unsafe fn executable_address(&self, offset: usize) -> *const u8 { + // TODO: encapsulate offsets so that this `offset` is guaranteed to be sound. + debug_assert!(offset <= isize::MAX as usize); + self.map.offset(offset as isize) + } + + /// Remap the offset into an absolute address within a read-write mapping. + /// + /// Offset must not exceed `isize::MAX`. + pub unsafe fn writable_address(&self, offset: usize) -> *mut u8 { + // TODO: encapsulate offsets so that this `offset` is guaranteed to be sound. + debug_assert!(offset <= isize::MAX as usize); + self.map.offset(offset as isize) + } +} + +impl Drop for CodeMemory { + fn drop(&mut self) { + if let Some(source_pool) = self.source_pool.take() { + unsafe { + let result = mm::mprotect( + self.map.cast(), + self.size, + MprotectFlags::WRITE | MprotectFlags::READ, + ); + if let Err(e) = result { + panic!( + "could not mprotect mapping before returning it to the memory pool: \ + map={:?}, size={:?}, error={}", + self.map, self.size, e + ); + } + } + drop(source_pool.push(Self { + source_pool: None, + map: self.map, + size: self.size, + executable_end: 0, + })); + } else { + unsafe { + if let Err(e) = mm::munmap(self.map.cast(), self.size) { + tracing::error!( + target: "unc_vm", + message="could not unmap mapping", + map=?self.map, size=self.size, error=%e + ); + } + } + } + } +} + +unsafe impl Send for CodeMemory {} + +/// The pool of preallocated memory maps for storing the code. +/// +/// This pool cannot grow and will only allow up to a number of code mappings that were specified +/// at construction time. +/// +/// However it is possible for the mappings inside to grow to accomodate larger code. +#[derive(Clone)] +pub struct LimitedMemoryPool { + pool: Arc>, +} + +impl LimitedMemoryPool { + /// Create a new pool with `count` mappings initialized to `default_memory_size` each. + pub fn new(count: usize, default_memory_size: usize) -> rustix::io::Result { + let pool = Arc::new(crossbeam_queue::ArrayQueue::new(count)); + let this = Self { pool }; + for _ in 0..count { + this.pool + .push(CodeMemory::create(default_memory_size)?) + .unwrap_or_else(|_| panic!("ArrayQueue could not accomodate {count} memories!")); + } + Ok(this) + } + + /// Get a memory mapping, at least `size` bytes large. + pub fn get(&self, size: usize) -> rustix::io::Result { + let mut memory = self.pool.pop().ok_or(rustix::io::Errno::NOMEM)?; + memory.source_pool = Some(Arc::clone(&self.pool)); + if memory.size < size { + Ok(memory.resize(size)?) + } else { + Ok(memory) + } + } +} + +#[cfg(test)] +mod tests { + use super::CodeMemory; + fn _assert() { + fn _assert_send() {} + _assert_send::(); + } +} diff --git a/runtime/unc-vm/engine/src/universal/engine.rs b/runtime/unc-vm/engine/src/universal/engine.rs new file mode 100644 index 000000000..f83062fa7 --- /dev/null +++ b/runtime/unc-vm/engine/src/universal/engine.rs @@ -0,0 +1,689 @@ +//! Universal compilation. + +use super::code_memory::{ARCH_FUNCTION_ALIGNMENT, DATA_SECTION_ALIGNMENT}; +use super::executable::{unrkyv, UniversalExecutableRef}; +use super::{CodeMemory, UniversalArtifact, UniversalExecutable}; +use crate::EngineId; +use unc_vm_compiler::Compiler; +use unc_vm_compiler::{ + CompileError, CustomSectionProtection, CustomSectionRef, FunctionBodyRef, JumpTable, + SectionIndex, Target, +}; +use unc_vm_types::entity::{EntityRef, PrimaryMap}; +use unc_vm_types::{ + DataInitializer, ExportIndex, Features, FunctionIndex, FunctionType, FunctionTypeRef, + GlobalInit, GlobalType, ImportCounts, ImportIndex, LocalFunctionIndex, LocalGlobalIndex, + MemoryIndex, SignatureIndex, TableIndex, +}; +use unc_vm_vm::{ + FuncDataRegistry, FunctionBodyPtr, SectionBodyPtr, SignatureRegistry, Tunables, + VMCallerCheckedAnyfunc, VMFuncRef, VMImportType, VMLocalFunction, VMOffsets, + VMSharedSignatureIndex, VMTrampoline, +}; +use rkyv::de::deserializers::SharedDeserializeMap; +use std::collections::BTreeMap; +use std::convert::TryFrom; +use std::sync::{Arc, Mutex}; + +/// A WebAssembly `Universal` Engine. +#[derive(Clone)] +pub struct UniversalEngine { + inner: Arc>, + /// The target for the compiler + target: Arc, + engine_id: EngineId, +} + +impl UniversalEngine { + /// Create a new `UniversalEngine` with the given config + pub fn new( + compiler: Box, + target: Target, + features: Features, + memory_allocator: super::LimitedMemoryPool, + ) -> Self { + Self { + inner: Arc::new(Mutex::new(UniversalEngineInner { + compiler: Some(compiler), + code_memory_pool: memory_allocator, + signatures: SignatureRegistry::new(), + func_data: Arc::new(FuncDataRegistry::new()), + features, + })), + target: Arc::new(target), + engine_id: EngineId::default(), + } + } + + /// Create a headless `UniversalEngine` + /// + /// A headless engine is an engine without any compiler attached. + /// This is useful for assuring a minimal runtime for running + /// WebAssembly modules. + /// + /// For example, for running in IoT devices where compilers are very + /// expensive, or also to optimize startup speed. + /// + /// # Important + /// + /// Headless engines can't compile or validate any modules, + /// they just take already processed Modules (via `Module::serialize`). + pub fn headless(memory_allocator: super::LimitedMemoryPool) -> Self { + Self { + inner: Arc::new(Mutex::new(UniversalEngineInner { + compiler: None, + code_memory_pool: memory_allocator, + signatures: SignatureRegistry::new(), + func_data: Arc::new(FuncDataRegistry::new()), + features: Features::default(), + })), + target: Arc::new(Target::default()), + engine_id: EngineId::default(), + } + } + + pub(crate) fn inner(&self) -> std::sync::MutexGuard<'_, UniversalEngineInner> { + self.inner.lock().unwrap() + } + + pub(crate) fn inner_mut(&self) -> std::sync::MutexGuard<'_, UniversalEngineInner> { + self.inner.lock().unwrap() + } + + /// Compile a WebAssembly binary + #[tracing::instrument(target = "unc_vm", level = "trace", skip_all)] + pub fn compile_universal( + &self, + binary: &[u8], + tunables: &dyn Tunables, + ) -> Result { + // Compute the needed instrumentation + let instrumentation = finite_wasm::Analysis::new() + .with_stack(tunables.stack_limiter_cfg()) + .with_gas(tunables.gas_cfg()) + .analyze(binary) + .map_err(CompileError::Analyze)?; + + let inner_engine = self.inner_mut(); + let features = inner_engine.features(); + let compiler = inner_engine.compiler()?; + let environ = unc_vm_compiler::ModuleEnvironment::new(); + let translation = environ.translate(binary).map_err(CompileError::Wasm)?; + + let memory_styles: PrimaryMap = translation + .module + .memories + .values() + .map(|memory_type| tunables.memory_style(memory_type)) + .collect(); + let table_styles: PrimaryMap = translation + .module + .tables + .values() + .map(|table_type| tunables.table_style(table_type)) + .collect(); + + // Compile the Module + let compile_info = unc_vm_compiler::CompileModuleInfo { + module: Arc::new(translation.module), + features: features.clone(), + memory_styles, + table_styles, + }; + let unc_vm_compiler::Compilation { + functions, + custom_sections, + function_call_trampolines, + dynamic_function_trampolines, + debug, + trampolines, + } = compiler.compile_module( + &self.target(), + &compile_info, + translation.function_body_inputs, + tunables, + &instrumentation, + )?; + let data_initializers = translation + .data_initializers + .iter() + .map(unc_vm_types::OwnedDataInitializer::new) + .collect(); + let mut function_frame_info = PrimaryMap::with_capacity(functions.len()); + let mut function_bodies = PrimaryMap::with_capacity(functions.len()); + let mut function_relocations = PrimaryMap::with_capacity(functions.len()); + let mut function_jt_offsets = PrimaryMap::with_capacity(functions.len()); + for (_, func) in functions.into_iter() { + function_bodies.push(func.body); + function_relocations.push(func.relocations); + function_jt_offsets.push(func.jt_offsets); + function_frame_info.push(func.frame_info); + } + let custom_section_relocations = custom_sections + .iter() + .map(|(_, section)| section.relocations.clone()) + .collect::>(); + Ok(super::UniversalExecutable { + function_bodies, + function_relocations, + function_jt_offsets, + function_frame_info, + function_call_trampolines, + dynamic_function_trampolines, + custom_sections, + custom_section_relocations, + debug, + trampolines, + compile_info, + data_initializers, + cpu_features: self.target().cpu_features().as_u64(), + }) + } + + /// Load a [`UniversalExecutable`](crate::UniversalExecutable) with this engine. + #[tracing::instrument(target = "unc_vm", level = "trace", skip_all)] + pub fn load_universal_executable( + &self, + executable: &UniversalExecutable, + ) -> Result { + let info = &executable.compile_info; + let module = &info.module; + let local_memories = (module.import_counts.memories as usize..module.memories.len()) + .map(|idx| { + let idx = MemoryIndex::new(idx); + (module.memories[idx], info.memory_styles[idx].clone()) + }) + .collect(); + let local_tables = (module.import_counts.tables as usize..module.tables.len()) + .map(|idx| { + let idx = TableIndex::new(idx); + (module.tables[idx], info.table_styles[idx].clone()) + }) + .collect(); + let local_globals: Vec<(GlobalType, GlobalInit)> = module + .globals + .iter() + .skip(module.import_counts.globals as usize) + .enumerate() + .map(|(idx, (_, t))| { + let init = module.global_initializers[LocalGlobalIndex::new(idx)]; + (*t, init) + }) + .collect(); + let mut inner_engine = self.inner_mut(); + + let local_functions = executable.function_bodies.iter().map(|(_, b)| b.into()); + let function_call_trampolines = &executable.function_call_trampolines; + let dynamic_function_trampolines = &executable.dynamic_function_trampolines; + let signatures = module + .signatures + .iter() + .map(|(_, sig)| inner_engine.signatures.register(sig.clone())) + .collect::>() + .into_boxed_slice(); + let (functions, trampolines, dynamic_trampolines, custom_sections, mut code_memory) = + inner_engine.allocate( + local_functions, + function_call_trampolines.iter().map(|(_, b)| b.into()), + dynamic_function_trampolines.iter().map(|(_, b)| b.into()), + executable.custom_sections.iter().map(|(_, s)| s.into()), + |idx: LocalFunctionIndex| { + let func_idx = module.import_counts.function_index(idx); + let sig_idx = module.functions[func_idx]; + (sig_idx, signatures[sig_idx]) + }, + )?; + let imports = module + .imports + .iter() + .map(|((module_name, field, idx), entity)| unc_vm_vm::VMImport { + module: String::from(module_name), + field: String::from(field), + import_no: *idx, + ty: match entity { + ImportIndex::Function(i) => { + let sig_idx = module.functions[*i]; + VMImportType::Function { + sig: signatures[sig_idx], + static_trampoline: trampolines[sig_idx], + } + } + ImportIndex::Table(i) => VMImportType::Table(module.tables[*i]), + &ImportIndex::Memory(i) => { + let ty = module.memories[i]; + VMImportType::Memory(ty, info.memory_styles[i].clone()) + } + ImportIndex::Global(i) => VMImportType::Global(module.globals[*i]), + }, + }) + .collect(); + + let function_relocations = executable.function_relocations.iter(); + let section_relocations = executable.custom_section_relocations.iter(); + crate::universal::link_module( + &functions, + |func_idx, jt_idx| executable.function_jt_offsets[func_idx][jt_idx], + function_relocations.map(|(i, rs)| (i, rs.iter().cloned())), + &custom_sections, + section_relocations.map(|(i, rs)| (i, rs.iter().cloned())), + &executable.trampolines, + ); + + // Make all code loaded executable. + unsafe { + // SAFETY: We finished relocation and linking just above. There should be no write + // access past this point, though I don’t think we have a good mechanism to ensure this + // statically at this point.. + code_memory.publish()?; + } + let exports = module + .exports + .iter() + .map(|(s, i)| (s.clone(), i.clone())) + .collect::>(); + + Ok(UniversalArtifact { + engine: self.clone(), + _code_memory: code_memory, + import_counts: module.import_counts, + start_function: module.start_function, + vmoffsets: VMOffsets::for_host().with_module_info(&*module), + imports, + dynamic_function_trampolines: dynamic_trampolines.into_boxed_slice(), + functions: functions.into_boxed_slice(), + exports, + signatures, + local_memories, + data_segments: executable.data_initializers.clone(), + passive_data: module.passive_data.clone(), + local_tables, + element_segments: module.table_initializers.clone(), + passive_elements: module.passive_elements.clone(), + local_globals, + }) + } + + /// Load a [`UniversalExecutableRef`](crate::UniversalExecutableRef) with this engine. + pub fn load_universal_executable_ref( + &self, + executable: &UniversalExecutableRef, + ) -> Result { + let info = &executable.compile_info; + let module = &info.module; + let import_counts: ImportCounts = unrkyv(&module.import_counts); + let local_memories = (import_counts.memories as usize..module.memories.len()) + .map(|idx| { + let idx = MemoryIndex::new(idx); + let mty = &module.memories[&idx]; + (unrkyv(mty), unrkyv(&info.memory_styles[&idx])) + }) + .collect(); + let local_tables = (import_counts.tables as usize..module.tables.len()) + .map(|idx| { + let idx = TableIndex::new(idx); + let tty = &module.tables[&idx]; + (unrkyv(tty), unrkyv(&info.table_styles[&idx])) + }) + .collect(); + let local_globals: Vec<(GlobalType, GlobalInit)> = module + .globals + .iter() + .skip(import_counts.globals as _) + .enumerate() + .map(|(idx, (_, t))| { + let init = unrkyv(&module.global_initializers[&LocalGlobalIndex::new(idx)]); + (*t, init) + }) + .collect(); + + let passive_data = + rkyv::Deserialize::deserialize(&module.passive_data, &mut SharedDeserializeMap::new()) + .map_err(|_| CompileError::Validate("could not deserialize passive data".into()))?; + let data_segments = executable.data_initializers.iter(); + let data_segments = data_segments.map(|s| DataInitializer::from(s).into()).collect(); + let element_segments = unrkyv(&module.table_initializers); + let passive_elements: BTreeMap> = + unrkyv(&module.passive_elements); + + let import_counts: ImportCounts = unrkyv(&module.import_counts); + let mut inner_engine = self.inner_mut(); + + let local_functions = executable.function_bodies.iter().map(|(_, b)| b.into()); + let call_trampolines = executable.function_call_trampolines.iter(); + let dynamic_trampolines = executable.dynamic_function_trampolines.iter(); + let signatures = module + .signatures + .values() + .map(|sig| { + let sig_ref = FunctionTypeRef::from(sig); + inner_engine + .signatures + .register(FunctionType::new(sig_ref.params(), sig_ref.results())) + }) + .collect::>() + .into_boxed_slice(); + let (functions, trampolines, dynamic_trampolines, custom_sections, mut code_memory) = + inner_engine.allocate( + local_functions, + call_trampolines.map(|(_, b)| b.into()), + dynamic_trampolines.map(|(_, b)| b.into()), + executable.custom_sections.iter().map(|(_, s)| s.into()), + |idx: LocalFunctionIndex| { + let func_idx = import_counts.function_index(idx); + let sig_idx = module.functions[&func_idx]; + (sig_idx, signatures[sig_idx]) + }, + )?; + let imports = { + module + .imports + .iter() + .map(|((module_name, field, idx), entity)| unc_vm_vm::VMImport { + module: String::from(module_name.as_str()), + field: String::from(field.as_str()), + import_no: *idx, + ty: match entity { + ImportIndex::Function(i) => { + let sig_idx = module.functions[i]; + VMImportType::Function { + sig: signatures[sig_idx], + static_trampoline: trampolines[sig_idx], + } + } + ImportIndex::Table(i) => VMImportType::Table(unrkyv(&module.tables[i])), + ImportIndex::Memory(i) => { + let ty = unrkyv(&module.memories[i]); + VMImportType::Memory(ty, unrkyv(&info.memory_styles[i])) + } + ImportIndex::Global(i) => VMImportType::Global(unrkyv(&module.globals[i])), + }, + }) + .collect() + }; + + let function_relocations = executable.function_relocations.iter(); + let section_relocations = executable.custom_section_relocations.iter(); + crate::universal::link_module( + &functions, + |func_idx, jt_idx| { + let func_idx = rkyv::Archived::::new(func_idx.index()); + let jt_idx = rkyv::Archived::::new(jt_idx.index()); + executable.function_jt_offsets[&func_idx][&jt_idx] + }, + function_relocations.map(|(i, r)| (i, r.iter().map(unrkyv))), + &custom_sections, + section_relocations.map(|(i, r)| (i, r.iter().map(unrkyv))), + &unrkyv(&executable.trampolines), + ); + + // Make all code compiled thus far executable. + unsafe { + // SAFETY: We finished relocation and linking just above. There should be no write + // access past this point, though I don’t think we have a good mechanism to ensure this + // statically at this point.. + code_memory.publish()?; + } + let exports = module + .exports + .iter() + .map(|(s, i)| (unrkyv(s), unrkyv(i))) + .collect::>(); + Ok(UniversalArtifact { + engine: self.clone(), + _code_memory: code_memory, + import_counts, + start_function: unrkyv(&module.start_function), + vmoffsets: VMOffsets::for_host().with_archived_module_info(&*module), + imports, + dynamic_function_trampolines: dynamic_trampolines.into_boxed_slice(), + functions: functions.into_boxed_slice(), + exports, + signatures, + local_memories, + data_segments, + passive_data, + local_tables, + element_segments, + passive_elements, + local_globals, + }) + } + + /// The target + pub fn target(&self) -> &Target { + &self.target + } + + /// Register a signature + pub fn register_signature(&self, func_type: FunctionType) -> VMSharedSignatureIndex { + self.inner().signatures.register(func_type) + } + + /// Register some function metadata + pub fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef { + self.inner().func_data().register(func_data) + } + + /// Lookup a signature + pub fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option { + self.inner().signatures.lookup(sig).cloned() + } + + /// Validates a WebAssembly module + #[tracing::instrument(target = "unc_vm", level = "trace", skip_all)] + pub fn validate(&self, binary: &[u8]) -> Result<(), CompileError> { + self.inner().validate(binary) + } + + /// Engine ID + pub fn id(&self) -> &EngineId { + &self.engine_id + } +} + +/// The inner contents of `UniversalEngine` +pub struct UniversalEngineInner { + /// The compiler + compiler: Option>, + /// Pool from which code memory can be allocated. + code_memory_pool: super::LimitedMemoryPool, + /// The features to compile the Wasm module with + features: Features, + /// The signature registry is used mainly to operate with trampolines + /// performantly. + pub(crate) signatures: SignatureRegistry, + /// The backing storage of `VMFuncRef`s. This centralized store ensures that 2 + /// functions with the same `VMCallerCheckedAnyfunc` will have the same `VMFuncRef`. + /// It also guarantees that the `VMFuncRef`s stay valid until the engine is dropped. + func_data: Arc, +} + +impl UniversalEngineInner { + /// Gets the compiler associated to this engine. + pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> { + if self.compiler.is_none() { + return Err(CompileError::Codegen("The UniversalEngine is operating in headless mode, so it can only execute already compiled Modules.".to_string())); + } + Ok(&**self.compiler.as_ref().unwrap()) + } + + /// Validate the module + pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> { + self.compiler()?.validate_module(self.features(), data) + } + + /// The Wasm features + pub fn features(&self) -> &Features { + &self.features + } + + /// Allocate compiled functions into memory + #[allow(clippy::type_complexity)] + pub(crate) fn allocate<'a>( + &mut self, + local_functions: impl ExactSizeIterator>, + call_trampolines: impl ExactSizeIterator>, + dynamic_trampolines: impl ExactSizeIterator>, + custom_sections: impl ExactSizeIterator>, + function_signature: impl Fn(LocalFunctionIndex) -> (SignatureIndex, VMSharedSignatureIndex), + ) -> Result< + ( + PrimaryMap, + PrimaryMap, + PrimaryMap, + PrimaryMap, + CodeMemory, + ), + CompileError, + > { + let code_memory_pool = &mut self.code_memory_pool; + let function_count = local_functions.len(); + let call_trampoline_count = call_trampolines.len(); + let function_bodies = + call_trampolines.chain(local_functions).chain(dynamic_trampolines).collect::>(); + + // TOOD: this shouldn't be necessary.... + let mut section_types = Vec::with_capacity(custom_sections.len()); + let mut executable_sections = Vec::new(); + let mut data_sections = Vec::new(); + for section in custom_sections { + if let CustomSectionProtection::ReadExecute = section.protection { + executable_sections.push(section); + } else { + data_sections.push(section); + } + section_types.push(section.protection); + } + + // 1. Calculate the total size, that is: + // - function body size, including all trampolines + // -- windows unwind info + // -- padding between functions + // - executable section body + // -- padding between executable sections + // - padding until a new page to change page permissions + // - data section body size + // -- padding between data sections + let page_size = rustix::param::page_size(); + let total_len = 0; + let total_len = function_bodies.iter().fold(total_len, |acc, func| { + round_up(acc, ARCH_FUNCTION_ALIGNMENT.into()) + function_allocation_size(*func) + }); + let total_len = executable_sections.iter().fold(total_len, |acc, exec| { + round_up(acc, ARCH_FUNCTION_ALIGNMENT.into()) + exec.bytes.len() + }); + let total_len = round_up(total_len, page_size); + let total_len = data_sections.iter().fold(total_len, |acc, data| { + round_up(acc, DATA_SECTION_ALIGNMENT.into()) + data.bytes.len() + }); + + let mut code_memory = code_memory_pool.get(total_len).map_err(|e| { + CompileError::Resource(format!("could not allocate code memory: {}", e)) + })?; + let mut code_writer = unsafe { + // SAFETY: We just popped out an unused code memory from an allocator pool. + code_memory.writer() + }; + + let mut allocated_functions = vec![]; + let mut allocated_data_sections = vec![]; + let mut allocated_executable_sections = vec![]; + for func in function_bodies { + let offset = code_writer + .write_executable(ARCH_FUNCTION_ALIGNMENT, func.body) + .expect("incorrectly computed code memory size"); + allocated_functions.push((offset, func.body.len())); + } + for section in executable_sections { + let offset = code_writer.write_executable(ARCH_FUNCTION_ALIGNMENT, section.bytes)?; + allocated_executable_sections.push(offset); + } + if !data_sections.is_empty() { + for section in data_sections { + let offset = code_writer + .write_data(DATA_SECTION_ALIGNMENT, section.bytes) + .expect("incorrectly computed code memory size"); + allocated_data_sections.push(offset); + } + } + + let mut allocated_function_call_trampolines: PrimaryMap = + PrimaryMap::new(); + + for (offset, _) in allocated_functions.drain(0..call_trampoline_count) { + let trampoline = unsafe { + // SAFETY: The executable code was written at the specified offset just above. + // TODO: Somewhat concerning is that the `VMTrampoline` does not ensure that the + // lifetime of the function pointer is a subset of the lifetime of the + // `code_memory`. Quite conversely, this `transmute` asserts that `VMTrampoline: + // 'static` and thus that this function pointer is callable even after + // `code_memory` is freed. + // + // As lifetime annotations in Rust cannot influence the codegen, this is not a + // source of undefined behaviour but we do lose static lifetime checks that Rust + // enforces. + std::mem::transmute::<_, VMTrampoline>(code_memory.executable_address(offset)) + }; + allocated_function_call_trampolines.push(trampoline); + } + + let allocated_functions_result = allocated_functions + .drain(0..function_count) + .enumerate() + .map(|(index, (offset, length))| -> Result<_, CompileError> { + let index = LocalFunctionIndex::new(index); + let (sig_idx, sig) = function_signature(index); + Ok(VMLocalFunction { + body: FunctionBodyPtr(unsafe { code_memory.executable_address(offset).cast() }), + length: u32::try_from(length).map_err(|_| { + CompileError::Codegen("function body length exceeds 4GiB".into()) + })?, + signature: sig, + trampoline: allocated_function_call_trampolines[sig_idx], + }) + }) + .collect::, _>>()?; + + let allocated_dynamic_function_trampolines = allocated_functions + .drain(..) + .map(|(offset, _)| { + FunctionBodyPtr(unsafe { code_memory.executable_address(offset).cast() }) + }) + .collect::>(); + + let mut exec_iter = allocated_executable_sections.iter(); + let mut data_iter = allocated_data_sections.iter(); + let allocated_custom_sections = section_types + .into_iter() + .map(|protection| { + SectionBodyPtr(if protection == CustomSectionProtection::ReadExecute { + unsafe { code_memory.executable_address(*exec_iter.next().unwrap()).cast() } + } else { + unsafe { code_memory.writable_address(*data_iter.next().unwrap()).cast() } + }) + }) + .collect::>(); + + Ok(( + allocated_functions_result, + allocated_function_call_trampolines, + allocated_dynamic_function_trampolines, + allocated_custom_sections, + code_memory, + )) + } + + /// Shared func metadata registry. + pub(crate) fn func_data(&self) -> &Arc { + &self.func_data + } +} + +fn round_up(size: usize, multiple: usize) -> usize { + debug_assert!(multiple.is_power_of_two()); + (size + (multiple - 1)) & !(multiple - 1) +} + +fn function_allocation_size(func: FunctionBodyRef<'_>) -> usize { + func.body.len() +} diff --git a/runtime/unc-vm/engine/src/universal/executable.rs b/runtime/unc-vm/engine/src/universal/executable.rs new file mode 100644 index 000000000..ae316cc7b --- /dev/null +++ b/runtime/unc-vm/engine/src/universal/executable.rs @@ -0,0 +1,183 @@ +use crate::DeserializeError; +use unc_vm_compiler::{ + CompileModuleInfo, CompiledFunctionFrameInfo, CustomSection, Dwarf, FunctionBody, + JumpTableOffsets, Relocation, SectionIndex, TrampolinesSection, +}; +use unc_vm_types::entity::PrimaryMap; +use unc_vm_types::{ + ExportIndex, FunctionIndex, ImportIndex, LocalFunctionIndex, OwnedDataInitializer, + SignatureIndex, +}; +use rkyv::de::deserializers::SharedDeserializeMap; +use rkyv::ser::serializers::{ + AllocScratchError, AllocSerializer, CompositeSerializerError, SharedSerializeMapError, +}; + +const MAGIC_HEADER: [u8; 32] = { + let value = *b"\0nearvm-universal\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; + let _length_must_be_multiple_of_16: bool = [true][value.len() % 16]; + value +}; + +/// A 0-copy view of the encoded `UniversalExecutable` payload. +#[derive(Clone, Copy)] +pub struct UniversalExecutableRef<'a> { + archive: &'a ArchivedUniversalExecutable, +} + +impl<'a> std::ops::Deref for UniversalExecutableRef<'a> { + type Target = ArchivedUniversalExecutable; + fn deref(&self) -> &Self::Target { + self.archive + } +} + +impl<'a> UniversalExecutableRef<'a> { + /// Verify the buffer for whether it is a valid `UniversalExecutable`. + pub fn verify_serialized(data: &[u8]) -> Result<(), &'static str> { + if !data.starts_with(&MAGIC_HEADER) { + return Err("the provided bytes are not nearvm-universal"); + } + if data.len() < MAGIC_HEADER.len() + 8 { + return Err("the data buffer is too small to be valid"); + } + let (remaining, position) = data.split_at(data.len() - 8); + let mut position_value = [0u8; 8]; + position_value.copy_from_slice(position); + if u64::from_le_bytes(position_value) > remaining.len() as u64 { + return Err("the buffer is malformed"); + } + // TODO(0-copy): bytecheck too. + Ok(()) + } + + /// # Safety + /// + /// This method is unsafe since it deserializes data directly + /// from memory. + /// Right now we are not doing any extra work for validation, but + /// `rkyv` has an option to do bytecheck on the serialized data before + /// serializing (via `rkyv::check_archived_value`). + pub unsafe fn deserialize( + data: &'a [u8], + ) -> Result, DeserializeError> { + Self::verify_serialized(data).map_err(|e| DeserializeError::Incompatible(e.to_string()))?; + let (archive, position) = data.split_at(data.len() - 8); + let mut position_value = [0u8; 8]; + position_value.copy_from_slice(position); + let (_, data) = archive.split_at(MAGIC_HEADER.len()); + Ok(UniversalExecutableRef { + archive: rkyv::archived_value::( + data, + u64::from_le_bytes(position_value) as usize, + ), + }) + } + + // TODO(0-copy): this should never fail. + /// Convert this reference to an owned `UniversalExecutable` value. + pub fn to_owned(self) -> Result { + let mut deserializer = SharedDeserializeMap::new(); + rkyv::Deserialize::deserialize(self.archive, &mut deserializer) + .map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e))) + } +} + +/// A wasm module compiled to some shape, ready to be loaded with `UniversalEngine` to produce an +/// `UniversalArtifact`. +/// +/// This is the result obtained after validating and compiling a WASM module with any of the +/// supported compilers. This type falls in-between a module and [`Artifact`](crate::Artifact). +#[derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)] +pub struct UniversalExecutable { + pub(crate) function_bodies: PrimaryMap, + pub(crate) function_relocations: PrimaryMap>, + pub(crate) function_jt_offsets: PrimaryMap, + pub(crate) function_frame_info: PrimaryMap, + pub(crate) function_call_trampolines: PrimaryMap, + pub(crate) dynamic_function_trampolines: PrimaryMap, + pub(crate) custom_sections: PrimaryMap, + pub(crate) custom_section_relocations: PrimaryMap>, + // The section indices corresponding to the Dwarf debug info + pub(crate) debug: Option, + // the Trampoline for Arm arch + pub(crate) trampolines: Option, + pub(crate) compile_info: CompileModuleInfo, + pub(crate) data_initializers: Vec, + pub(crate) cpu_features: u64, +} + +#[derive(thiserror::Error, Debug)] +pub enum ExecutableSerializeError { + #[error("could not serialize the executable data")] + Executable( + #[source] + CompositeSerializerError< + std::convert::Infallible, + AllocScratchError, + SharedSerializeMapError, + >, + ), +} + +impl UniversalExecutable { + /// Serialize the executable into bytes for storage. + pub fn serialize( + &self, + ) -> Result, Box<(dyn std::error::Error + Send + Sync + 'static)>> { + // The format is as thus: + // + // HEADER + // RKYV PAYLOAD + // RKYV POSITION + // + // It is expected that any framing for message length is handled by the caller. + let mut serializer = AllocSerializer::<1024>::default(); + let pos = rkyv::ser::Serializer::serialize_value(&mut serializer, self) + .map_err(ExecutableSerializeError::Executable)? as u64; + let pos_bytes = pos.to_le_bytes(); + let data = serializer.into_serializer().into_inner(); + let mut out = Vec::with_capacity(MAGIC_HEADER.len() + pos_bytes.len() + data.len()); + out.extend(&MAGIC_HEADER); + out.extend(data.as_slice()); + out.extend(&pos_bytes); + Ok(out) + } +} + +impl<'a> UniversalExecutableRef<'a> { + /// Get the name for specified function index. + /// + /// Test-only API + pub fn function_name(&self, index: FunctionIndex) -> Option<&str> { + let module = &self.compile_info.module; + // First, lets see if there's a name by which this function is exported. + for (name, idx) in module.exports.iter() { + match idx { + &ExportIndex::Function(fi) if fi == index => return Some(&*name), + _ => continue, + } + } + if let Some(r) = module.function_names.get(&index) { + return Some(&**r); + } + for ((_, field, _), idx) in module.imports.iter() { + match idx { + &ImportIndex::Function(fi) if fi == index => return Some(&*field), + _ => continue, + } + } + None + } +} + +pub(crate) fn unrkyv(archive: &T::Archived) -> T +where + T: rkyv::Archive, + T::Archived: rkyv::Deserialize, +{ + Result::<_, std::convert::Infallible>::unwrap(rkyv::Deserialize::deserialize( + archive, + &mut rkyv::Infallible, + )) +} diff --git a/runtime/unc-vm/engine/src/universal/link.rs b/runtime/unc-vm/engine/src/universal/link.rs new file mode 100644 index 000000000..b76cf10b3 --- /dev/null +++ b/runtime/unc-vm/engine/src/universal/link.rs @@ -0,0 +1,206 @@ +//! Linking for Universal-compiled code. + +use unc_vm_compiler::{ + JumpTable, Relocation, RelocationKind, RelocationTarget, SectionIndex, TrampolinesSection, +}; +use unc_vm_types::entity::PrimaryMap; +use unc_vm_types::LocalFunctionIndex; +use unc_vm_vm::{SectionBodyPtr, VMLocalFunction}; +use std::collections::HashMap; +use std::ptr::{read_unaligned, write_unaligned}; + +/// Add a new trampoline address, given the base adress of the Section. Return the address of the jump +/// The trampoline itself still have to be writen +fn trampolines_add( + map: &mut HashMap, + trampoline: &TrampolinesSection, + address: usize, + baseaddress: usize, +) -> usize { + if let Some(target) = map.get(&address) { + return *target; + } + let ret = map.len(); + if ret == trampoline.slots { + panic!("No more slot in Trampolines"); + } + map.insert(address, baseaddress + ret * trampoline.size); + baseaddress + ret * trampoline.size +} + +fn use_trampoline( + address: usize, + allocated_sections: &PrimaryMap, + trampolines: &Option, + map: &mut HashMap, +) -> Option { + match trampolines { + Some(trampolines) => Some(trampolines_add( + map, + trampolines, + address, + *allocated_sections[trampolines.section_index] as usize, + )), + _ => None, + } +} + +fn fill_trampoline_map( + allocated_sections: &PrimaryMap, + trampolines: &Option, +) -> HashMap { + let mut map: HashMap = HashMap::new(); + match trampolines { + Some(trampolines) => { + let baseaddress = *allocated_sections[trampolines.section_index] as usize; + for i in 0..trampolines.size { + let jmpslot: usize = unsafe { + read_unaligned((baseaddress + i * trampolines.size + 8) as *mut usize) + }; + if jmpslot != 0 { + map.insert(jmpslot, baseaddress + i * trampolines.size); + } + } + } + _ => {} + }; + map +} + +fn apply_relocation( + body: usize, + r: &Relocation, + allocated_functions: &PrimaryMap, + jt_offsets: impl Fn(LocalFunctionIndex, JumpTable) -> unc_vm_compiler::CodeOffset, + allocated_sections: &PrimaryMap, + trampolines: &Option, + trampolines_map: &mut HashMap, +) { + let target_func_address: usize = match r.reloc_target { + RelocationTarget::LocalFunc(index) => *allocated_functions[index].body as usize, + RelocationTarget::LibCall(libcall) => libcall.function_pointer(), + RelocationTarget::CustomSection(custom_section) => { + *allocated_sections[custom_section] as usize + } + RelocationTarget::JumpTable(func_index, jt) => { + let offset = jt_offsets(func_index, jt); + *allocated_functions[func_index].body as usize + offset as usize + } + }; + + match r.kind { + #[cfg(target_pointer_width = "64")] + RelocationKind::Abs8 => unsafe { + let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64); + write_unaligned(reloc_address as *mut u64, reloc_delta); + }, + #[cfg(target_pointer_width = "32")] + RelocationKind::X86PCRel4 => unsafe { + let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64); + write_unaligned(reloc_address as *mut u32, reloc_delta as _); + }, + #[cfg(target_pointer_width = "64")] + RelocationKind::X86PCRel8 => unsafe { + let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64); + write_unaligned(reloc_address as *mut u64, reloc_delta); + }, + RelocationKind::X86CallPCRel4 => unsafe { + let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64); + write_unaligned(reloc_address as *mut u32, reloc_delta as _); + }, + RelocationKind::X86PCRelRodata4 => {} + RelocationKind::Arm64Call => unsafe { + let (reloc_address, mut reloc_delta) = r.for_address(body, target_func_address as u64); + if (reloc_delta as i64).abs() >= 0x1000_0000 { + let new_address = match use_trampoline( + target_func_address, + allocated_sections, + trampolines, + trampolines_map, + ) { + Some(new_address) => new_address, + _ => panic!( + "Relocation to big for {:?} for {:?} with {:x}, current val {:x}", + r.kind, + r.reloc_target, + reloc_delta, + read_unaligned(reloc_address as *mut u32) + ), + }; + write_unaligned((new_address + 8) as *mut u64, target_func_address as u64); // write the jump address + let (_, new_delta) = r.for_address(body, new_address as u64); + reloc_delta = new_delta; + } + let reloc_delta = (((reloc_delta / 4) as u32) & 0x3ff_ffff) + | read_unaligned(reloc_address as *mut u32); + write_unaligned(reloc_address as *mut u32, reloc_delta); + }, + RelocationKind::Arm64Movw0 => unsafe { + let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64); + let reloc_delta = + (((reloc_delta & 0xffff) as u32) << 5) | read_unaligned(reloc_address as *mut u32); + write_unaligned(reloc_address as *mut u32, reloc_delta); + }, + RelocationKind::Arm64Movw1 => unsafe { + let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64); + let reloc_delta = ((((reloc_delta >> 16) & 0xffff) as u32) << 5) + | read_unaligned(reloc_address as *mut u32); + write_unaligned(reloc_address as *mut u32, reloc_delta); + }, + RelocationKind::Arm64Movw2 => unsafe { + let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64); + let reloc_delta = ((((reloc_delta >> 32) & 0xffff) as u32) << 5) + | read_unaligned(reloc_address as *mut u32); + write_unaligned(reloc_address as *mut u32, reloc_delta); + }, + RelocationKind::Arm64Movw3 => unsafe { + let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64); + let reloc_delta = ((((reloc_delta >> 48) & 0xffff) as u32) << 5) + | read_unaligned(reloc_address as *mut u32); + write_unaligned(reloc_address as *mut u32, reloc_delta); + }, + kind => panic!("Relocation kind unsupported in the current architecture {}", kind), + } +} + +/// Links a module, patching the allocated functions with the +/// required relocations and jump tables. +#[tracing::instrument(target = "unc_vm", level = "trace", skip_all)] +pub fn link_module( + allocated_functions: &PrimaryMap, + jt_offsets: impl Fn(LocalFunctionIndex, JumpTable) -> unc_vm_compiler::CodeOffset, + function_relocations: impl Iterator)>, + allocated_sections: &PrimaryMap, + section_relocations: impl Iterator)>, + trampolines: &Option, +) { + let mut trampolines_map = fill_trampoline_map(allocated_sections, trampolines); + for (i, section_relocs) in section_relocations { + let body = *allocated_sections[i] as usize; + for r in section_relocs { + apply_relocation( + body, + &r, + allocated_functions, + &jt_offsets, + allocated_sections, + trampolines, + &mut trampolines_map, + ); + } + } + for (i, function_relocs) in function_relocations { + let body = *allocated_functions[i].body as usize; + for r in function_relocs { + apply_relocation( + body, + &r, + allocated_functions, + &jt_offsets, + allocated_sections, + trampolines, + &mut trampolines_map, + ); + } + } +} diff --git a/runtime/unc-vm/engine/src/universal/mod.rs b/runtime/unc-vm/engine/src/universal/mod.rs new file mode 100644 index 000000000..7cc02f49d --- /dev/null +++ b/runtime/unc-vm/engine/src/universal/mod.rs @@ -0,0 +1,13 @@ +mod artifact; +mod builder; +mod code_memory; +mod engine; +mod executable; +mod link; + +pub use self::artifact::UniversalArtifact; +pub use self::builder::Universal; +pub use self::code_memory::{CodeMemory, LimitedMemoryPool}; +pub use self::engine::UniversalEngine; +pub use self::executable::{UniversalExecutable, UniversalExecutableRef}; +pub use self::link::link_module; diff --git a/runtime/unc-vm/test-api/Cargo.toml b/runtime/unc-vm/test-api/Cargo.toml new file mode 100644 index 000000000..ecb0eb67d --- /dev/null +++ b/runtime/unc-vm/test-api/Cargo.toml @@ -0,0 +1,99 @@ +[package] +name = "unc-vm-test-api" +version.workspace = true +description = "High-performance WebAssembly runtime" +categories = ["wasm"] +keywords = ["wasm", "webassembly", "runtime", "vm"] +authors = ["Wasmer Engineering Team ", "Hello Inc "] +repository.workspace = true +license = "MIT" +readme = "README.md" +edition = "2021" +publish = false +rust-version.workspace = true + +[lints] +workspace = true + +# Shared dependencies. +[dependencies] +# - Mandatory shared dependencies. +cfg-if.workspace = true +finite-wasm.workspace = true +indexmap.workspace = true +more-asserts.workspace = true +prefix-sum-vec.workspace = true +thiserror.workspace = true +tracing.workspace = true + +# - Optional shared dependencies. +wat = { workspace = true, optional = true } + +# Dependencies and Development Dependencies for `sys`. +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +# - Mandatory dependencies for `sys`. +unc-vm-vm.workspace = true +unc-vm-compiler.workspace = true +unc-vm-engine.workspace = true +unc-vm-types.workspace = true +target-lexicon.workspace = true +# - Optional dependencies for `sys`. +unc-vm-compiler-singlepass = { workspace = true, optional = true} + +# - Mandatory dependencies for `sys` on Windows. +[target.'cfg(all(not(target_arch = "wasm32"), target_os = "windows"))'.dependencies] +winapi.workspace = true +# - Development Dependencies for `sys`. +[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] +anyhow.workspace = true +tempfile.workspace = true +wat.workspace = true +unc-vm-compiler-test-derive.workspace = true +unc-vm-wast.workspace = true +test-log.workspace = true +serial_test.workspace = true +tracing-subscriber.workspace = true + +[build-dependencies] +unc-vm-test-generator.workspace = true +anyhow.workspace = true + +[badges] +maintenance = { status = "actively-developed" } + +[features] +default = ["sys-default"] + +# Features for `sys`. +sys = [] +sys-default = ["sys", "wat", "default-singlepass", "default-universal"] +# - Compilers. +compiler = [ + "sys", +] + singlepass = [ + "compiler", + "unc-vm-compiler-singlepass", + ] +default-compiler = [] + default-singlepass = [ + "default-compiler", + "singlepass", + ] +# - Engines. +engine = ["sys"] + universal = [ + "engine", + ] +default-engine = [] + default-universal = [ + "default-engine", + "universal", + ] + +[package.metadata.docs.rs] +features = ["compiler", "core", "default-compiler", "default-engine", "engine", "jit", "native", "singlepass", "sys", "sys-default", "universal"] + +[[test]] +name = "unc-vm-compilers" +path = "../tests/compilers/main.rs" diff --git a/runtime/unc-vm/test-api/LICENSE b/runtime/unc-vm/test-api/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/runtime/unc-vm/test-api/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/runtime/unc-vm/test-api/README.md b/runtime/unc-vm/test-api/README.md new file mode 100644 index 000000000..988d92741 --- /dev/null +++ b/runtime/unc-vm/test-api/README.md @@ -0,0 +1,89 @@ +# `unc-vm` + +This crate is a fork of `wasmer`. A significant number of things +changed, but the documentation is not up-to-date yet. + +[`Wasmer`](https://wasmer.io/) is the most popular +[WebAssembly](https://webassembly.org/) runtime for Rust. It supports +JIT (Just In Time) and AOT (Ahead Of Time) compilation as well as +pluggable compilers suited to your needs. + +It's designed to be safe and secure, and runnable in any kind of environment. + +## Usage + +Here is a small example of using Wasmer to run a WebAssembly module +written with its WAT format (textual format): + +```rust +use unc_vm_test_api::{Store, Module, Instance, Value, imports}; + +fn main() -> anyhow::Result<()> { + let module_wat = r#" + (module + (type $t0 (func (param i32) (result i32))) + (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32) + get_local $p0 + i32.const 1 + i32.add)) + "#; + + let store = Store::default(); + let module = Module::new(&store, &module_wat)?; + // The module doesn't import anything, so we create an empty import object. + let import_object = imports! {}; + let instance = Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000000), &import_object)?; + + let add_one = instance.exports.get_function("add_one")?; + let result = add_one.call(&[Value::I32(42)])?; + assert_eq!(result[0], Value::I32(43)); + + Ok(()) +} +``` + +[Discover the full collection of examples](https://github.com/wasmerio/wasmer/tree/master/examples). + +## Features + +Wasmer is not only fast, but also designed to be *highly customizable*: + +* **Pluggable compilers** — A compiler is used by an engine to + transform WebAssembly into executable code: + * [`wasmer-compiler-singlepass`] provides a fast compilation-time + but an unoptimized runtime speed, + +* **Headless mode** — Once a WebAssembly module has been compiled, it + is possible to serialize it in a file for example, and later execute + it with Wasmer with headless mode turned on. Headless Wasmer has no + compiler, which makes it more portable and faster to load. It's + ideal for constrainted environments. + +* **Cross-compilation** — Most compilers support cross-compilation. It + means it possible to pre-compile a WebAssembly module targetting a + different architecture or platform and serialize it, to then run it + on the targetted architecture and platform later. + +* **Run Wasmer in a JavaScript environment** — With the `js` Cargo + feature, it is possible to compile a Rust program using Wasmer to + WebAssembly. In this context, the resulting WebAssembly module will + expect to run in a JavaScript environment, like a browser, Node.js, + Deno and so on. In this specific scenario, there is no engines or + compilers available, it's the one available in the JavaScript + environment that will be used. + +Wasmer ships by default with the Cranelift compiler as its great for +development purposes. However, we strongly encourage to use the LLVM +compiler in production as it performs about 50% faster, achieving +unc-native speeds. + +Note: if one wants to use multiple compilers at the same time, it's +also possible! One will need to import them directly via each of the +compiler crates. + +Read [the documentation to learn +more](https://wasmerio.github.io/wasmer/crates/doc/wasmer/). + +--- + +Made with ❤️ by the Wasmer team, for the community diff --git a/runtime/unc-vm/test-api/build.rs b/runtime/unc-vm/test-api/build.rs new file mode 100644 index 000000000..a33889860 --- /dev/null +++ b/runtime/unc-vm/test-api/build.rs @@ -0,0 +1,54 @@ +//! The logic that gets executed before building the binary and tests. +//! We use it to auto-generate the Wasm spectests for each of the +//! available compilers. +//! +//! Please try to keep this file as clean as possible. + +use unc_vm_test_generator::{ + test_directory, test_directory_module, wast_processor, with_test_module, Testsuite, +}; +use std::env; +use std::fs; +use std::path::PathBuf; +use std::process::Command; + +fn main() -> anyhow::Result<()> { + // As rerun-if-changed doesn't support globs, we use another crate + // to check changes in directories. + + let out_dir = PathBuf::from( + env::var_os("OUT_DIR").expect("The OUT_DIR environment variable must be set"), + ); + + // Spectests test generation + { + let mut spectests = Testsuite { buffer: String::new(), path: vec![] }; + + with_test_module(&mut spectests, "spec", |spectests| { + let _spec_tests = test_directory(spectests, "../tests/wast/spec", wast_processor)?; + test_directory_module( + spectests, + "../tests/wast/spec/proposals/multi-value", + wast_processor, + )?; + test_directory_module(spectests, "../tests/wast/spec/proposals/simd", wast_processor)?; + // test_directory_module(spectests, "tests/wast/spec/proposals/bulk-memory-operations", wast_processor)?; + Ok(()) + })?; + with_test_module(&mut spectests, "wasmer", |spectests| { + let _spec_tests = test_directory(spectests, "../tests/wast/wasmer", wast_processor)?; + Ok(()) + })?; + + let spectests_output = out_dir.join("generated_spectests.rs"); + fs::write(&spectests_output, spectests.buffer)?; + + // Write out our auto-generated tests and opportunistically format them with + // `rustfmt` if it's installed. + // Note: We need drop because we don't want to run `unwrap` or `expect` as + // the command might fail, but we don't care about it's result. + drop(Command::new("rustfmt").arg(&spectests_output).status()); + } + + Ok(()) +} diff --git a/runtime/unc-vm/test-api/src/lib.rs b/runtime/unc-vm/test-api/src/lib.rs new file mode 100644 index 000000000..e00f56e7f --- /dev/null +++ b/runtime/unc-vm/test-api/src/lib.rs @@ -0,0 +1,4 @@ +#![cfg(target_arch = "x86_64")] + +mod sys; +pub use sys::*; diff --git a/runtime/unc-vm/test-api/src/sys/env.rs b/runtime/unc-vm/test-api/src/sys/env.rs new file mode 100644 index 000000000..9e27fd8e3 --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/env.rs @@ -0,0 +1,160 @@ +use super::exports::ExportError; +use super::instance::Instance; +use thiserror::Error; + +/// An error while initializing the user supplied host env with the `WasmerEnv` trait. +#[derive(Error, Debug)] +#[error("Host env initialization error")] +pub enum HostEnvInitError { + /// An error occurred when accessing an export + Export(ExportError), + /// Incorrect gas metering config + IncorrectGasMeteringConfig, +} + +impl From for HostEnvInitError { + fn from(other: ExportError) -> Self { + Self::Export(other) + } +} + +/// Trait for initializing the environments passed to host functions after +/// instantiation but before execution. +/// +/// This is useful for filling an environment with data that can only be accesed +/// after instantiation. For example, exported items such as memories and +/// functions which don't exist prior to instantiation can be accessed here so +/// that host functions can use them. +/// +/// When implementing the trait manually, it's important to get a "weak" export to +/// prevent a cyclic reference leaking memory. You can access a "weak" export with +/// a method like `get_with_generics_weak`. +pub trait WasmerEnv: Clone + Send + Sync { + /// The function that Wasmer will call on your type to let it finish + /// setting up the environment with data from the `Instance`. + /// + /// This function is called after `Instance` is created but before it is + /// returned to the user via `Instance::new_with_config`. + fn init_with_instance(&mut self, _instance: &Instance) -> Result<(), HostEnvInitError> { + Ok(()) + } +} + +impl WasmerEnv for u8 {} +impl WasmerEnv for i8 {} +impl WasmerEnv for u16 {} +impl WasmerEnv for i16 {} +impl WasmerEnv for u32 {} +impl WasmerEnv for i32 {} +impl WasmerEnv for u64 {} +impl WasmerEnv for i64 {} +impl WasmerEnv for u128 {} +impl WasmerEnv for i128 {} +impl WasmerEnv for f32 {} +impl WasmerEnv for f64 {} +impl WasmerEnv for usize {} +impl WasmerEnv for isize {} +impl WasmerEnv for char {} +impl WasmerEnv for bool {} +impl WasmerEnv for String {} +impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicBool {} +impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI8 {} +impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicU8 {} +impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI16 {} +impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicU16 {} +impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI32 {} +impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicU32 {} +impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicI64 {} +impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicUsize {} +impl<'a> WasmerEnv for &'a ::std::sync::atomic::AtomicIsize {} +impl WasmerEnv for Box { + fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> { + (&mut **self).init_with_instance(instance) + } +} + +impl WasmerEnv for ::std::sync::Arc<::std::sync::Mutex> { + fn init_with_instance(&mut self, instance: &Instance) -> Result<(), HostEnvInitError> { + let mut guard = self.lock().unwrap(); + guard.init_with_instance(instance) + } +} + +/// Lazily init an item +pub struct LazyInit { + /// The data to be initialized + data: std::mem::MaybeUninit, + /// Whether or not the data has been initialized + initialized: bool, +} + +impl LazyInit { + /// Creates an unitialized value. + pub fn new() -> Self { + Self { data: std::mem::MaybeUninit::uninit(), initialized: false } + } + + /// # Safety + /// - The data must be initialized first + pub unsafe fn get_unchecked(&self) -> &T { + &*self.data.as_ptr() + } + + /// Get the inner data. + pub fn get_ref(&self) -> Option<&T> { + if !self.initialized { + None + } else { + Some(unsafe { self.get_unchecked() }) + } + } + + /// Sets a value and marks the data as initialized. + pub fn initialize(&mut self, value: T) -> bool { + if self.initialized { + return false; + } + unsafe { + self.data.as_mut_ptr().write(value); + } + self.initialized = true; + true + } +} + +impl std::fmt::Debug for LazyInit { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("LazyInit").field("data", &self.get_ref()).finish() + } +} + +impl Clone for LazyInit { + fn clone(&self) -> Self { + if let Some(inner) = self.get_ref() { + Self { data: std::mem::MaybeUninit::new(inner.clone()), initialized: true } + } else { + Self { data: std::mem::MaybeUninit::uninit(), initialized: false } + } + } +} + +impl Drop for LazyInit { + fn drop(&mut self) { + if self.initialized { + unsafe { + let ptr = self.data.as_mut_ptr(); + std::ptr::drop_in_place(ptr); + }; + } + } +} + +impl Default for LazyInit { + fn default() -> Self { + Self::new() + } +} + +unsafe impl Send for LazyInit {} +// I thought we could opt out of sync..., look into this +// unsafe impl !Sync for InitWithInstance {} diff --git a/runtime/unc-vm/test-api/src/sys/exports.rs b/runtime/unc-vm/test-api/src/sys/exports.rs new file mode 100644 index 000000000..d61600526 --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/exports.rs @@ -0,0 +1,67 @@ +use crate::sys::externals::Extern; +use crate::sys::import_object::LikeNamespace; +use indexmap::IndexMap; +use unc_vm_vm::Export; +use std::sync::Arc; +use thiserror::Error; + +/// The `ExportError` can happen when trying to get a specific +/// export [`Extern`] from the [`Instance`] exports. +/// +/// [`Instance`]: crate::Instance +#[derive(Error, Debug)] +pub enum ExportError { + /// An error than occurs when the exported type and the expected type + /// are incompatible. + #[error("Incompatible Export Type")] + IncompatibleType, + /// This error arises when an export is missing + #[error("Missing export {0}")] + Missing(String), +} + +/// Exports is a special kind of map that allows easily unwrapping +/// the types of instances. +/// +/// TODO: add examples of using exports +#[derive(Clone, Default)] +pub struct Exports { + map: Arc>, +} + +impl Exports { + /// Creates a new `Exports`. + pub fn new() -> Self { + Default::default() + } + + /// Insert a new export into this `Exports` map. + pub fn insert(&mut self, name: S, value: E) + where + S: Into, + E: Into, + { + Arc::get_mut(&mut self.map).unwrap().insert(name.into(), value.into()); + } +} + +impl LikeNamespace for Exports { + fn get_namespace_export(&self, name: &str) -> Option { + self.map.get(name).map(|is_export| is_export.to_export()) + } + + fn get_namespace_exports(&self) -> Vec<(String, Export)> { + self.map.iter().map(|(k, v)| (k.clone(), v.to_export())).collect() + } +} + +/// This trait is used to mark types as gettable from an [`Instance`]. +/// +/// [`Instance`]: crate::Instance +pub trait Exportable<'a>: Sized { + /// This function is used when providedd the [`Extern`] as exportable, so it + /// can be used while instantiating the [`Module`]. + /// + /// [`Module`]: crate::Module + fn to_export(&self) -> Export; +} diff --git a/runtime/unc-vm/test-api/src/sys/externals/function.rs b/runtime/unc-vm/test-api/src/sys/externals/function.rs new file mode 100644 index 000000000..4ae786970 --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/externals/function.rs @@ -0,0 +1,1563 @@ +use super::super::env::HostEnvInitError; +use super::super::env::WasmerEnv; +use super::super::exports::Exportable; +use super::super::native::NativeFunc; +use super::super::store::Store; +use super::super::types::FunctionType; +use super::super::types::{Val, ValFuncRef}; +pub use inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv}; +use unc_vm_engine::RuntimeError; + +use unc_vm_vm::{ + unc_vm_call_trampoline, raise_user_trap, resume_panic, Export, ExportFunction, + ExportFunctionMetadata, ImportInitializerFuncPtr, TableElement, VMCallerCheckedAnyfunc, + VMDynamicFunctionContext, VMFuncRef, VMFunction, VMFunctionBody, VMFunctionEnvironment, + VMFunctionKind, VMTrampoline, +}; +use std::cmp::max; +use std::ffi::c_void; +use std::fmt; +use std::sync::Arc; + +/// A WebAssembly `function` instance. +/// +/// A function instance is the runtime representation of a function. +/// It effectively is a closure of the original function (defined in either +/// the host or the WebAssembly module) over the runtime `Instance` of its +/// originating `Module`. +/// +/// The module instance is used to resolve references to other definitions +/// during execution of the function. +/// +/// Spec: +/// +/// # Panics +/// - Closures (functions with captured environments) are not currently supported +/// with native functions. Attempting to create a native `Function` with one will +/// result in a panic. +/// [Closures as host functions tracking issue](https://github.com/wasmerio/wasmer/issues/1840) +#[derive(PartialEq)] +pub struct Function { + pub(crate) store: Store, + pub(crate) exported: ExportFunction, +} + +impl unc_vm_types::WasmValueType for Function { + /// Write the value. + unsafe fn write_value_to(&self, p: *mut i128) { + let func_ref = + Val::into_vm_funcref(&Val::FuncRef(Some(self.clone())), &self.store).unwrap(); + std::ptr::write(p as *mut VMFuncRef, func_ref); + } + + /// Read the value. + // TODO(reftypes): this entire function should be cleaned up, `dyn Any` should + // ideally be removed + unsafe fn read_value_from(store: &dyn std::any::Any, p: *const i128) -> Self { + let func_ref = std::ptr::read(p as *const VMFuncRef); + let store = store.downcast_ref::().expect("Store expected in `Function::read_value_from`. If you see this error message it likely means you're using a function ref in a place we don't yet support it -- sorry about the inconvenience."); + match Val::from_vm_funcref(func_ref, store) { + Val::FuncRef(Some(fr)) => fr, + // these bottom two cases indicate bugs in `unc_vm-types` or elsewhere. + // They should never be triggered, so we just panic. + Val::FuncRef(None) => panic!("Null funcref found in `Function::read_value_from`!"), + other => panic!("Invalid value in `Function::read_value_from`: {:?}", other), + } + } +} + +impl Function { + /// Convert a `VMFuncRef` into a `Function`. + /// + /// Returns `None` if the funcref is null. + /// + /// # Safety + /// + /// Must ensure that the returned Function does not outlive the containing instance. + pub unsafe fn from_vm_funcref(store: &Store, func_ref: VMFuncRef) -> Option { + if func_ref.is_null() { + return None; + } + let unc_vm_vm::VMCallerCheckedAnyfunc { func_ptr: address, type_index: signature, vmctx } = + **func_ref; + let export = unc_vm_vm::ExportFunction { + // TODO: + // figure out if we ever need a value here: need testing with complicated import patterns + metadata: None, + vm_function: unc_vm_vm::VMFunction { + address, + signature, + // TODO: review this comment (unclear if it's still correct): + // All functions in tables are already Static (as dynamic functions + // are converted to use the trampolines with static signatures). + kind: unc_vm_vm::VMFunctionKind::Static, + vmctx, + call_trampoline: None, + instance_ref: None, + }, + }; + Some(Self::from_vm_export(store, export)) + } +} + +impl From for TableElement { + fn from(f: Function) -> Self { + Self::FuncRef(f.vm_funcref()) + } +} + +fn build_export_function_metadata( + env: Env, + import_init_function_ptr: for<'a> fn( + &'a mut Env, + &'a super::super::instance::Instance, + ) -> Result<(), HostEnvInitError>, +) -> (*mut c_void, ExportFunctionMetadata) +where + Env: Clone + Sized + 'static + Send + Sync, +{ + let import_init_function_ptr = Some(unsafe { + std::mem::transmute::<_, ImportInitializerFuncPtr>(import_init_function_ptr) + }); + let host_env_clone_fn = |ptr: *mut c_void| -> *mut c_void { + let env_ref: &Env = unsafe { + ptr.cast::().as_ref().expect("`ptr` to the environment is null when cloning it") + }; + Box::into_raw(Box::new(env_ref.clone())) as _ + }; + let host_env_drop_fn = |ptr: *mut c_void| { + drop(unsafe { Box::from_raw(ptr.cast::()) }); + }; + let env = Box::into_raw(Box::new(env)) as _; + + // # Safety + // - All these functions work on all threads + // - The host env is `Send`. + let metadata = unsafe { + ExportFunctionMetadata::new( + env, + import_init_function_ptr, + host_env_clone_fn, + host_env_drop_fn, + ) + }; + + (env, metadata) +} + +impl WasmerEnv for WithoutEnv {} + +impl Function { + /// Creates a new host `Function` (dynamic) with the provided signature. + /// + /// If you know the signature of the host function at compile time, + /// consider using [`Function::new_native`] for less runtime overhead. + /// + /// # Examples + /// + /// ``` + /// # use unc_vm_test_api::{Function, FunctionType, Type, Store, Value}; + /// # let store = Store::default(); + /// # + /// let signature = FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]); + /// + /// let f = Function::new(&store, &signature, |args| { + /// let sum = args[0].unwrap_i32() + args[1].unwrap_i32(); + /// Ok(vec![Value::I32(sum)]) + /// }); + /// ``` + /// + /// With constant signature: + /// + /// ``` + /// # use unc_vm_test_api::{Function, FunctionType, Type, Store, Value}; + /// # let store = Store::default(); + /// # + /// const I32_I32_TO_I32: ([Type; 2], [Type; 1]) = ([Type::I32, Type::I32], [Type::I32]); + /// + /// let f = Function::new(&store, I32_I32_TO_I32, |args| { + /// let sum = args[0].unwrap_i32() + args[1].unwrap_i32(); + /// Ok(vec![Value::I32(sum)]) + /// }); + /// ``` + #[allow(clippy::cast_ptr_alignment)] + pub fn new(store: &Store, ty: FT, func: F) -> Self + where + FT: Into, + F: Fn(&[Val]) -> Result, RuntimeError> + 'static + Send + Sync, + { + let wrapped_func = + move |_env: &WithoutEnv, args: &[Val]| -> Result, RuntimeError> { func(args) }; + Self::new_with_env(store, ty, WithoutEnv, wrapped_func) + } + + /// Creates a new host `Function` (dynamic) with the provided signature and environment. + /// + /// If you know the signature of the host function at compile time, + /// consider using [`Function::new_native_with_env`] for less runtime + /// overhead. + /// + /// # Examples + /// + /// ``` + /// # use unc_vm_test_api::{Function, FunctionType, Type, Store, Value, WasmerEnv}; + /// # let store = Store::default(); + /// # + /// #[derive(Clone)] + /// struct Env { + /// multiplier: i32, + /// }; + /// impl WasmerEnv for Env {} + /// let env = Env { multiplier: 2 }; + /// + /// let signature = FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]); + /// + /// let f = Function::new_with_env(&store, &signature, env, |env, args| { + /// let result = env.multiplier * (args[0].unwrap_i32() + args[1].unwrap_i32()); + /// Ok(vec![Value::I32(result)]) + /// }); + /// ``` + /// + /// With constant signature: + /// + /// ``` + /// # use unc_vm_test_api::{Function, FunctionType, Type, Store, Value, WasmerEnv}; + /// # let store = Store::default(); + /// const I32_I32_TO_I32: ([Type; 2], [Type; 1]) = ([Type::I32, Type::I32], [Type::I32]); + /// + /// #[derive(Clone)] + /// struct Env { + /// multiplier: i32, + /// }; + /// impl WasmerEnv for Env {} + /// let env = Env { multiplier: 2 }; + /// + /// let f = Function::new_with_env(&store, I32_I32_TO_I32, env, |env, args| { + /// let result = env.multiplier * (args[0].unwrap_i32() + args[1].unwrap_i32()); + /// Ok(vec![Value::I32(result)]) + /// }); + /// ``` + #[allow(clippy::cast_ptr_alignment)] + pub fn new_with_env(store: &Store, ty: FT, env: Env, func: F) -> Self + where + FT: Into, + F: Fn(&Env, &[Val]) -> Result, RuntimeError> + 'static + Send + Sync, + Env: Sized + WasmerEnv + 'static, + { + let ty: FunctionType = ty.into(); + let dynamic_ctx: VMDynamicFunctionContext> = + VMDynamicFunctionContext::from_context(DynamicFunction { + env: Box::new(env), + func: Arc::new(func), + store: store.clone(), + function_type: ty.clone(), + }); + + let import_init_function_ptr: for<'a> fn(&'a mut _, &'a _) -> Result<(), _> = + |env: &mut VMDynamicFunctionContext>, + instance: &super::super::instance::Instance| { + Env::init_with_instance(&mut *env.ctx.env, instance) + }; + + let (host_env, metadata) = build_export_function_metadata::< + VMDynamicFunctionContext>, + >(dynamic_ctx, import_init_function_ptr); + + // We don't yet have the address with the Wasm ABI signature. + // The engine linker will replace the address with one pointing to a + // generated dynamic trampoline. + let address = std::ptr::null() as *const VMFunctionBody; + let vmctx = VMFunctionEnvironment { host_env }; + let signature = store.engine().register_signature(ty); + + Self { + store: store.clone(), + exported: ExportFunction { + metadata: Some(Arc::new(metadata)), + vm_function: VMFunction { + address, + kind: VMFunctionKind::Dynamic, + vmctx, + signature, + call_trampoline: None, + instance_ref: None, + }, + }, + } + } + + /// Creates a new host `Function` from a native function. + /// + /// The function signature is automatically retrieved using the + /// Rust typing system. + /// + /// # Example + /// + /// ``` + /// # use unc_vm_test_api::{Store, Function}; + /// # let store = Store::default(); + /// # + /// fn sum(a: i32, b: i32) -> i32 { + /// a + b + /// } + /// + /// let f = Function::new_native(&store, sum); + /// ``` + pub fn new_native(store: &Store, func: F) -> Self + where + F: HostFunction, + Args: WasmTypeList, + Rets: WasmTypeList, + Env: Sized + 'static, + { + if std::mem::size_of::() != 0 { + Self::closures_unsupported_panic(); + } + let function = inner::Function::::new(func); + let address = function.address() as *const VMFunctionBody; + let vmctx = VMFunctionEnvironment { host_env: std::ptr::null_mut() as *mut _ }; + let signature = store.engine().register_signature(function.ty()); + + Self { + store: store.clone(), + exported: ExportFunction { + // TODO: figure out what's going on in this function: it takes an `Env` + // param but also marks itself as not having an env + metadata: None, + vm_function: VMFunction { + address, + vmctx, + signature, + kind: VMFunctionKind::Static, + call_trampoline: None, + instance_ref: None, + }, + }, + } + } + + /// Creates a new host `Function` from a native function and a provided environment. + /// + /// The function signature is automatically retrieved using the + /// Rust typing system. + /// + /// # Example + /// + /// ``` + /// # use unc_vm_test_api::{Store, Function, WasmerEnv}; + /// # let store = Store::default(); + /// # + /// #[derive(Clone)] + /// struct Env { + /// multiplier: i32, + /// }; + /// impl WasmerEnv for Env {} + /// let env = Env { multiplier: 2 }; + /// + /// fn sum_and_multiply(env: &Env, a: i32, b: i32) -> i32 { + /// (a + b) * env.multiplier + /// } + /// + /// let f = Function::new_native_with_env(&store, env, sum_and_multiply); + /// ``` + pub fn new_native_with_env(store: &Store, env: Env, func: F) -> Self + where + F: HostFunction, + Args: WasmTypeList, + Rets: WasmTypeList, + Env: Sized + WasmerEnv + 'static, + { + if std::mem::size_of::() != 0 { + Self::closures_unsupported_panic(); + } + let function = inner::Function::::new(func); + let address = function.address(); + + let (host_env, metadata) = + build_export_function_metadata::(env, Env::init_with_instance); + + let vmctx = VMFunctionEnvironment { host_env }; + let signature = store.engine().register_signature(function.ty()); + Self { + store: store.clone(), + exported: ExportFunction { + metadata: Some(Arc::new(metadata)), + vm_function: VMFunction { + address, + kind: VMFunctionKind::Static, + vmctx, + signature, + call_trampoline: None, + instance_ref: None, + }, + }, + } + } + + /// Returns the [`FunctionType`] of the `Function`. + /// + /// # Example + /// + /// ``` + /// # use unc_vm_test_api::{Function, Store, Type}; + /// # let store = Store::default(); + /// # + /// fn sum(a: i32, b: i32) -> i32 { + /// a + b + /// } + /// + /// let f = Function::new_native(&store, sum); + /// + /// assert_eq!(f.ty().params(), vec![Type::I32, Type::I32]); + /// assert_eq!(f.ty().results(), vec![Type::I32]); + /// ``` + pub fn ty(&self) -> FunctionType { + self.store + .engine() + .lookup_signature(self.exported.vm_function.signature) + .expect("Could not resolve VMSharedFunctionIndex! Mixing engines?") + } + + /// Returns the [`Store`] where the `Function` belongs. + pub fn store(&self) -> &Store { + &self.store + } + + fn call_wasm( + &self, + trampoline: VMTrampoline, + params: &[Val], + results: &mut [Val], + ) -> Result<(), RuntimeError> { + let format_types_for_error_message = |items: &[Val]| { + items.iter().map(|param| param.ty().to_string()).collect::>().join(", ") + }; + let signature = self.ty(); + if signature.params().len() != params.len() { + return Err(RuntimeError::new(format!( + "Parameters of type [{}] did not match signature {}", + format_types_for_error_message(params), + &signature + ))); + } + if signature.results().len() != results.len() { + return Err(RuntimeError::new(format!( + "Results of type [{}] did not match signature {}", + format_types_for_error_message(results), + &signature, + ))); + } + + let mut values_vec = vec![0; max(params.len(), results.len())]; + + // Store the argument values into `values_vec`. + let param_tys = signature.params().iter(); + for ((arg, slot), ty) in params.iter().zip(&mut values_vec).zip(param_tys) { + if arg.ty() != *ty { + let param_types = format_types_for_error_message(params); + return Err(RuntimeError::new(format!( + "Parameters of type [{}] did not match signature {}", + param_types, &signature, + ))); + } + unsafe { + arg.write_value_to(slot); + } + } + + // Call the trampoline. + if let Err(error) = unsafe { + unc_vm_call_trampoline( + self.exported.vm_function.vmctx, + trampoline, + self.exported.vm_function.address, + values_vec.as_mut_ptr() as *mut u8, + ) + } { + return Err(RuntimeError::from_trap(error)); + } + + // Load the return values out of `values_vec`. + for (index, &value_type) in signature.results().iter().enumerate() { + unsafe { + let ptr = values_vec.as_ptr().add(index); + results[index] = Val::read_value_from(&self.store, ptr, value_type); + } + } + + Ok(()) + } + + /// Returns the number of parameters that this function takes. + /// + /// # Example + /// + /// ``` + /// # use unc_vm_test_api::{Function, Store, Type}; + /// # let store = Store::default(); + /// # + /// fn sum(a: i32, b: i32) -> i32 { + /// a + b + /// } + /// + /// let f = Function::new_native(&store, sum); + /// + /// assert_eq!(f.param_arity(), 2); + /// ``` + pub fn param_arity(&self) -> usize { + self.ty().params().len() + } + + /// Returns the number of results this function produces. + /// + /// # Example + /// + /// ``` + /// # use unc_vm_test_api::{Function, Store, Type}; + /// # let store = Store::default(); + /// # + /// fn sum(a: i32, b: i32) -> i32 { + /// a + b + /// } + /// + /// let f = Function::new_native(&store, sum); + /// + /// assert_eq!(f.result_arity(), 1); + /// ``` + pub fn result_arity(&self) -> usize { + self.ty().results().len() + } + + /// Call the `Function` function. + /// + /// Depending on where the Function is defined, it will call it. + /// 1. If the function is defined inside a WebAssembly, it will call the trampoline + /// for the function signature. + /// 2. If the function is defined in the host (in a native way), it will + /// call the trampoline. + /// + /// # Examples + /// + /// ``` + /// # use unc_vm_test_api::{imports, wat2wasm, Function, Instance, InstanceConfig, Module, Store, Type, Value}; + /// # let store = Store::default(); + /// # let wasm_bytes = wat2wasm(r#" + /// # (module + /// # (func (export "sum") (param $x i32) (param $y i32) (result i32) + /// # local.get $x + /// # local.get $y + /// # i32.add + /// # )) + /// # "#.as_bytes()).unwrap(); + /// # let module = Module::new(&store, wasm_bytes).unwrap(); + /// # let import_object = imports! {}; + /// # let instance = Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000000), &import_object).unwrap(); + /// # + /// let sum = instance.lookup_function("sum").unwrap(); + /// + /// assert_eq!(sum.call(&[Value::I32(1), Value::I32(2)]).unwrap().to_vec(), vec![Value::I32(3)]); + /// ``` + pub fn call(&self, params: &[Val]) -> Result, RuntimeError> { + // If it's a function defined in the Wasm, it will always have a call_trampoline + if let Some(trampoline) = self.exported.vm_function.call_trampoline { + let mut results = vec![Val::null(); self.result_arity()]; + self.call_wasm(trampoline, params, &mut results)?; + return Ok(results.into_boxed_slice()); + } + + // If it's a function defined in the host + match self.exported.vm_function.kind { + VMFunctionKind::Dynamic => unsafe { + type VMContextWithEnv = VMDynamicFunctionContext>; + let ctx = self.exported.vm_function.vmctx.host_env as *mut VMContextWithEnv; + Ok((*ctx).ctx.call(¶ms)?.into_boxed_slice()) + }, + VMFunctionKind::Static => { + unimplemented!( + "Native function definitions can't be directly called from the host yet" + ); + } + } + } + + pub(crate) fn from_vm_export(store: &Store, unc_vm_export: ExportFunction) -> Self { + Self { store: store.clone(), exported: unc_vm_export } + } + + pub(crate) fn vm_funcref(&self) -> VMFuncRef { + let engine = self.store.engine(); + engine.register_function_metadata(VMCallerCheckedAnyfunc { + func_ptr: self.exported.vm_function.address, + type_index: self.exported.vm_function.signature, + vmctx: self.exported.vm_function.vmctx, + }) + } + + /// Transform this WebAssembly function into a function with the + /// native ABI. See [`NativeFunc`] to learn more. + /// + /// # Examples + /// + /// ``` + /// # use unc_vm_test_api::{imports, wat2wasm, Function, Instance, InstanceConfig, Module, Store, Type, Value}; + /// # let store = Store::default(); + /// # let wasm_bytes = wat2wasm(r#" + /// # (module + /// # (func (export "sum") (param $x i32) (param $y i32) (result i32) + /// # local.get $x + /// # local.get $y + /// # i32.add + /// # )) + /// # "#.as_bytes()).unwrap(); + /// # let module = Module::new(&store, wasm_bytes).unwrap(); + /// # let import_object = imports! {}; + /// # let instance = Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000000), &import_object).unwrap(); + /// # + /// let sum = instance.lookup_function("sum").unwrap(); + /// let sum_native = sum.native::<(i32, i32), i32>().unwrap(); + /// + /// assert_eq!(sum_native.call(1, 2).unwrap(), 3); + /// ``` + /// + /// # Errors + /// + /// If the `Args` generic parameter does not match the exported function + /// an error will be raised: + /// + /// ```should_panic + /// # use unc_vm_test_api::{imports, wat2wasm, Function, Instance, InstanceConfig, Module, Store, Type, Value}; + /// # let store = Store::default(); + /// # let wasm_bytes = wat2wasm(r#" + /// # (module + /// # (func (export "sum") (param $x i32) (param $y i32) (result i32) + /// # local.get $x + /// # local.get $y + /// # i32.add + /// # )) + /// # "#.as_bytes()).unwrap(); + /// # let module = Module::new(&store, wasm_bytes).unwrap(); + /// # let import_object = imports! {}; + /// # let instance = Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000000), &import_object).unwrap(); + /// # + /// let sum = instance.lookup_function("sum").unwrap(); + /// + /// // This results in an error: `RuntimeError` + /// let sum_native = sum.native::<(i64, i64), i32>().unwrap(); + /// ``` + /// + /// If the `Rets` generic parameter does not match the exported function + /// an error will be raised: + /// + /// ```should_panic + /// # use unc_vm_test_api::{imports, wat2wasm, Function, Instance, InstanceConfig, Module, Store, Type, Value}; + /// # let store = Store::default(); + /// # let wasm_bytes = wat2wasm(r#" + /// # (module + /// # (func (export "sum") (param $x i32) (param $y i32) (result i32) + /// # local.get $x + /// # local.get $y + /// # i32.add + /// # )) + /// # "#.as_bytes()).unwrap(); + /// # let module = Module::new(&store, wasm_bytes).unwrap(); + /// # let import_object = imports! {}; + /// # let instance = Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000000), &import_object).unwrap(); + /// # + /// let sum = instance.lookup_function("sum").unwrap(); + /// + /// // This results in an error: `RuntimeError` + /// let sum_native = sum.native::<(i32, i32), i64>().unwrap(); + /// ``` + pub fn native(&self) -> Result, RuntimeError> + where + Args: WasmTypeList, + Rets: WasmTypeList, + { + let engine = self.store().engine(); + let signature = engine + .lookup_signature(self.exported.vm_function.signature) + .expect("Could not resolve VMSharedSignatureIndex! Wrong engine?"); + // type check + let expected = signature.params(); + let given = Args::wasm_types(); + if expected != given { + return Err(RuntimeError::new(format!( + "types (`{:?}`) for the function arguments don't match the actual types (`{:?}`)", + given, expected, + ))); + } + let expected = signature.results(); + let given = Rets::wasm_types(); + if expected != given { + // todo: error result types don't match + return Err(RuntimeError::new(format!( + "types (`{:?}`) for the function results don't match the actual types (`{:?}`)", + given, expected, + ))); + } + + Ok(NativeFunc::new(self.store.clone(), self.exported.clone())) + } + + #[track_caller] + fn closures_unsupported_panic() -> ! { + unimplemented!("Closures (functions with captured environments) are currently unsupported with native functions. See: https://github.com/wasmerio/wasmer/issues/1840") + } + + /// Get access to the backing VM value for this extern. This function is for + /// tests it should not be called by users of the Wasmer API. + /// + /// # Safety + /// This function is unsafe to call outside of tests for the unc_vm crate + /// because there is no stability guarantee for the returned type and we may + /// make breaking changes to it at any time or remove this method. + #[doc(hidden)] + pub unsafe fn get_vm_function(&self) -> &VMFunction { + &self.exported.vm_function + } +} + +impl<'a> Exportable<'a> for Function { + fn to_export(&self) -> Export { + self.exported.clone().into() + } +} + +impl Clone for Function { + fn clone(&self) -> Self { + let mut exported = self.exported.clone(); + exported.vm_function.upgrade_instance_ref().unwrap(); + + Self { store: self.store.clone(), exported } + } +} + +impl fmt::Debug for Function { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.debug_struct("Function").field("ty", &self.ty()).finish() + } +} + +/// This trait is one that all dynamic functions must fulfill. +pub(crate) trait VMDynamicFunction: Send + Sync { + fn call(&self, args: &[Val]) -> Result, RuntimeError>; + fn function_type(&self) -> &FunctionType; + fn store(&self) -> &Store; +} + +pub(crate) struct DynamicFunction +where + Env: Sized + 'static + Send + Sync, +{ + function_type: FunctionType, + #[allow(clippy::type_complexity)] + func: Arc Result, RuntimeError> + 'static + Send + Sync>, + store: Store, + env: Box, +} + +impl Clone for DynamicFunction { + fn clone(&self) -> Self { + Self { + env: self.env.clone(), + function_type: self.function_type.clone(), + store: self.store.clone(), + func: self.func.clone(), + } + } +} + +impl VMDynamicFunction for DynamicFunction +where + Env: Sized + 'static + Send + Sync, +{ + fn call(&self, args: &[Val]) -> Result, RuntimeError> { + (*self.func)(&*self.env, &args) + } + fn function_type(&self) -> &FunctionType { + &self.function_type + } + fn store(&self) -> &Store { + &self.store + } +} + +trait VMDynamicFunctionCall { + fn from_context(ctx: T) -> Self; + fn address_ptr() -> *const VMFunctionBody; + unsafe fn func_wrapper(&self, values_vec: *mut i128); +} + +impl VMDynamicFunctionCall for VMDynamicFunctionContext { + fn from_context(ctx: T) -> Self { + Self { address: Self::address_ptr(), ctx } + } + + fn address_ptr() -> *const VMFunctionBody { + Self::func_wrapper as *const () as *const VMFunctionBody + } + + // This function wraps our func, to make it compatible with the + // reverse trampoline signature + unsafe fn func_wrapper( + // Note: we use the trick that the first param to this function is the `VMDynamicFunctionContext` + // itself, so rather than doing `dynamic_ctx: &VMDynamicFunctionContext`, we simplify it a bit + &self, + values_vec: *mut i128, + ) { + use std::panic::{self, AssertUnwindSafe}; + let result = panic::catch_unwind(AssertUnwindSafe(|| { + let func_ty = self.ctx.function_type(); + let mut args = Vec::with_capacity(func_ty.params().len()); + let store = self.ctx.store(); + for (i, ty) in func_ty.params().iter().enumerate() { + args.push(Val::read_value_from(store, values_vec.add(i), *ty)); + } + let returns = self.ctx.call(&args)?; + + // We need to dynamically check that the returns + // match the expected types, as well as expected length. + let return_types = returns.iter().map(|ret| ret.ty()).collect::>(); + if return_types != func_ty.results() { + return Err(RuntimeError::new(format!( + "Dynamic function returned wrong signature. Expected {:?} but got {:?}", + func_ty.results(), + return_types + ))); + } + for (i, ret) in returns.iter().enumerate() { + ret.write_value_to(values_vec.add(i)); + } + Ok(()) + })); // We get extern ref drops at the end of this block that we don't need. + // By preventing extern ref incs in the code above we can save the work of + // incrementing and decrementing. However the logic as-is is correct. + + match result { + Ok(Ok(())) => {} + Ok(Err(trap)) => raise_user_trap(Box::new(trap)), + Err(panic) => resume_panic(panic), + } + } +} + +/// This private inner module contains the low-level implementation +/// for `Function` and its siblings. +mod inner { + use unc_vm_types::{FunctionType, NativeWasmType, Type}; + use unc_vm_vm::{raise_user_trap, resume_panic, VMFunctionBody}; + use std::array::TryFromSliceError; + use std::convert::{Infallible, TryInto}; + use std::error::Error; + use std::marker::PhantomData; + use std::panic::{self, AssertUnwindSafe}; + + /// A trait to convert a Rust value to a `WasmNativeType` value, + /// or to convert `WasmNativeType` value to a Rust value. + /// + /// This trait should ideally be split into two traits: + /// `FromNativeWasmType` and `ToNativeWasmType` but it creates a + /// non-negligible complexity in the `WasmTypeList` + /// implementation. + pub unsafe trait FromToNativeWasmType + where + Self: Sized, + { + /// Native Wasm type. + type Native: NativeWasmType; + + /// Convert a value of kind `Self::Native` to `Self`. + /// + /// # Panics + /// + /// This method panics if `native` cannot fit in the `Self` + /// type`. + fn from_native(native: Self::Native) -> Self; + + /// Convert self to `Self::Native`. + /// + /// # Panics + /// + /// This method panics if `self` cannot fit in the + /// `Self::Native` type. + fn to_native(self) -> Self::Native; + } + + macro_rules! from_to_native_wasm_type { + ( $( $type:ty => $native_type:ty ),* ) => { + $( + #[allow(clippy::use_self)] + unsafe impl FromToNativeWasmType for $type { + type Native = $native_type; + + #[inline] + fn from_native(native: Self::Native) -> Self { + native as Self + } + + #[inline] + fn to_native(self) -> Self::Native { + self as Self::Native + } + } + )* + }; + } + + macro_rules! from_to_native_wasm_type_same_size { + ( $( $type:ty => $native_type:ty ),* ) => { + $( + #[allow(clippy::use_self)] + unsafe impl FromToNativeWasmType for $type { + type Native = $native_type; + + #[inline] + fn from_native(native: Self::Native) -> Self { + Self::from_ne_bytes(Self::Native::to_ne_bytes(native)) + } + + #[inline] + fn to_native(self) -> Self::Native { + Self::Native::from_ne_bytes(Self::to_ne_bytes(self)) + } + } + )* + }; + } + + from_to_native_wasm_type!( + i8 => i32, + u8 => i32, + i16 => i32, + u16 => i32 + ); + + from_to_native_wasm_type_same_size!( + i32 => i32, + u32 => i32, + i64 => i64, + u64 => i64, + f32 => f32, + f64 => f64 + ); + + #[cfg(test)] + mod test_from_to_native_wasm_type { + use super::*; + + #[test] + fn test_to_native() { + assert_eq!(7i8.to_native(), 7i32); + assert_eq!(7u8.to_native(), 7i32); + assert_eq!(7i16.to_native(), 7i32); + assert_eq!(7u16.to_native(), 7i32); + assert_eq!(u32::MAX.to_native(), -1); + } + + #[test] + fn test_to_native_same_size() { + assert_eq!(7i32.to_native(), 7i32); + assert_eq!(7u32.to_native(), 7i32); + assert_eq!(7i64.to_native(), 7i64); + assert_eq!(7u64.to_native(), 7i64); + assert_eq!(7f32.to_native(), 7f32); + assert_eq!(7f64.to_native(), 7f64); + } + } + + /// The `WasmTypeList` trait represents a tuple (list) of Wasm + /// typed values. It is used to get low-level representation of + /// such a tuple. + pub trait WasmTypeList + where + Self: Sized, + { + /// The C type (a struct) that can hold/represent all the + /// represented values. + type CStruct; + + /// The array type that can hold all the represented values. + /// + /// Note that all values are stored in their binary form. + type Array: AsMut<[i128]>; + + /// Constructs `Self` based on an array of values. + fn from_array(array: Self::Array) -> Self; + + /// Constructs `Self` based on a slice of values. + /// + /// `from_slice` returns a `Result` because it is possible + /// that the slice doesn't have the same size than + /// `Self::Array`, in which circumstance an error of kind + /// `TryFromSliceError` will be returned. + fn from_slice(slice: &[i128]) -> Result; + + /// Builds and returns an array of type `Array` from a tuple + /// (list) of values. + fn into_array(self) -> Self::Array; + + /// Allocates and return an empty array of type `Array` that + /// will hold a tuple (list) of values, usually to hold the + /// returned values of a WebAssembly function call. + fn empty_array() -> Self::Array; + + /// Builds a tuple (list) of values from a C struct of type + /// `CStruct`. + fn from_c_struct(c_struct: Self::CStruct) -> Self; + + /// Builds and returns a C struct of type `CStruct` from a + /// tuple (list) of values. + fn into_c_struct(self) -> Self::CStruct; + + /// Get the Wasm types for the tuple (list) of currently + /// represented values. + fn wasm_types() -> &'static [Type]; + } + + /// The `IntoResult` trait turns a `WasmTypeList` into a + /// `Result`. + /// + /// It is mostly used to turn result values of a Wasm function + /// call into a `Result`. + pub trait IntoResult + where + T: WasmTypeList, + { + /// The error type for this trait. + type Error: Error + Sync + Send + 'static; + + /// Transforms `Self` into a `Result`. + fn into_result(self) -> Result; + } + + impl IntoResult for T + where + T: WasmTypeList, + { + // `T` is not a `Result`, it's already a value, so no error + // can be built. + type Error = Infallible; + + fn into_result(self) -> Result { + Ok(self) + } + } + + impl IntoResult for Result + where + T: WasmTypeList, + E: Error + Sync + Send + 'static, + { + type Error = E; + + fn into_result(self) -> Self { + self + } + } + + #[cfg(test)] + mod test_into_result { + use super::*; + use std::convert::Infallible; + + #[test] + fn test_into_result_over_t() { + let x: i32 = 42; + let result_of_x: Result = x.into_result(); + + assert_eq!(result_of_x.unwrap(), x); + } + + #[test] + fn test_into_result_over_result() { + { + let x: Result = Ok(42); + let result_of_x: Result = x.into_result(); + + assert_eq!(result_of_x, x); + } + + { + use std::{error, fmt}; + + #[derive(Debug, PartialEq)] + struct E; + + impl fmt::Display for E { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "E") + } + } + + impl error::Error for E {} + + let x: Result = Err(E); + let result_of_x: Result = x.into_result(); + + assert_eq!(result_of_x.unwrap_err(), E); + } + } + } + + /// The `HostFunction` trait represents the set of functions that + /// can be used as host function. To uphold this statement, it is + /// necessary for a function to be transformed into a pointer to + /// `VMFunctionBody`. + pub trait HostFunction + where + Args: WasmTypeList, + Rets: WasmTypeList, + Kind: HostFunctionKind, + T: Sized, + Self: Sized, + { + /// Get the pointer to the function body. + fn function_body_ptr(self) -> *const VMFunctionBody; + } + + /// Empty trait to specify the kind of `HostFunction`: With or + /// without an environment. + /// + /// This trait is never aimed to be used by a user. It is used by + /// the trait system to automatically generate the appropriate + /// host functions. + #[doc(hidden)] + pub trait HostFunctionKind {} + + /// An empty struct to help Rust typing to determine + /// when a `HostFunction` does have an environment. + pub struct WithEnv; + + impl HostFunctionKind for WithEnv {} + + /// An empty struct to help Rust typing to determine + /// when a `HostFunction` does not have an environment. + #[derive(Clone)] + pub struct WithoutEnv; + + impl HostFunctionKind for WithoutEnv {} + + /// Represents a low-level Wasm static host function. See + /// `super::Function::new` and `super::Function::new_env` to learn + /// more. + #[derive(Clone, Debug, Hash, PartialEq, Eq)] + pub struct Function { + address: *const VMFunctionBody, + _phantom: PhantomData<(Args, Rets)>, + } + + unsafe impl Send for Function {} + + impl Function + where + Args: WasmTypeList, + Rets: WasmTypeList, + { + /// Creates a new `Function`. + pub fn new(function: F) -> Self + where + F: HostFunction, + T: HostFunctionKind, + E: Sized, + { + Self { address: function.function_body_ptr(), _phantom: PhantomData } + } + + /// Get the function type of this `Function`. + pub fn ty(&self) -> FunctionType { + FunctionType::new(Args::wasm_types(), Rets::wasm_types()) + } + + /// Get the address of this `Function`. + pub fn address(&self) -> *const VMFunctionBody { + self.address + } + } + + macro_rules! impl_host_function { + ( [$c_struct_representation:ident] + $c_struct_name:ident, + $( $x:ident ),* ) => { + + /// A structure with a C-compatible representation that can hold a set of Wasm values. + /// This type is used by `WasmTypeList::CStruct`. + #[repr($c_struct_representation)] + pub struct $c_struct_name< $( $x ),* > ( $( <$x as FromToNativeWasmType>::Native ),* ) + where + $( $x: FromToNativeWasmType ),*; + + // Implement `WasmTypeList` for a specific tuple. + #[allow(unused_parens, dead_code)] + impl< $( $x ),* > + WasmTypeList + for + ( $( $x ),* ) + where + $( $x: FromToNativeWasmType ),* + { + type CStruct = $c_struct_name< $( $x ),* >; + + type Array = [i128; count_idents!( $( $x ),* )]; + + fn from_array(array: Self::Array) -> Self { + // Unpack items of the array. + #[allow(non_snake_case)] + let [ $( $x ),* ] = array; + + // Build the tuple. + ( + $( + FromToNativeWasmType::from_native(NativeWasmType::from_binary($x)) + ),* + ) + } + + fn from_slice(slice: &[i128]) -> Result { + Ok(Self::from_array(slice.try_into()?)) + } + + fn into_array(self) -> Self::Array { + // Unpack items of the tuple. + #[allow(non_snake_case)] + let ( $( $x ),* ) = self; + + // Build the array. + [ + $( + FromToNativeWasmType::to_native($x).to_binary() + ),* + ] + } + + fn empty_array() -> Self::Array { + // Build an array initialized with `0`. + [0; count_idents!( $( $x ),* )] + } + + fn from_c_struct(c_struct: Self::CStruct) -> Self { + // Unpack items of the C structure. + #[allow(non_snake_case)] + let $c_struct_name( $( $x ),* ) = c_struct; + + ( + $( + FromToNativeWasmType::from_native($x) + ),* + ) + } + + #[allow(unused_parens, non_snake_case)] + fn into_c_struct(self) -> Self::CStruct { + // Unpack items of the tuple. + let ( $( $x ),* ) = self; + + // Build the C structure. + $c_struct_name( + $( + FromToNativeWasmType::to_native($x) + ),* + ) + } + + fn wasm_types() -> &'static [Type] { + &[ + $( + $x::Native::WASM_TYPE + ),* + ] + } + } + + // Implement `HostFunction` for a function that has the same arity than the tuple. + // This specific function has no environment. + #[allow(unused_parens)] + impl< $( $x, )* Rets, RetsAsResult, Func > + HostFunction<( $( $x ),* ), Rets, WithoutEnv, ()> + for + Func + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + RetsAsResult: IntoResult, + Func: Fn($( $x , )*) -> RetsAsResult + 'static + Send, + { + #[allow(non_snake_case)] + fn function_body_ptr(self) -> *const VMFunctionBody { + /// This is a function that wraps the real host + /// function. Its address will be used inside the + /// runtime. + extern fn func_wrapper<$( $x, )* Rets, RetsAsResult, Func>( _: usize, $( $x: $x::Native, )* ) -> Rets::CStruct + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + RetsAsResult: IntoResult, + Func: Fn( $( $x ),* ) -> RetsAsResult + 'static + { + let func: &Func = unsafe { &*(&() as *const () as *const Func) }; + let result = panic::catch_unwind(AssertUnwindSafe(|| { + func( $( FromToNativeWasmType::from_native($x) ),* ).into_result() + })); + + match result { + Ok(Ok(result)) => return result.into_c_struct(), + Ok(Err(trap)) => unsafe { raise_user_trap(Box::new(trap)) }, + Err(panic) => unsafe { resume_panic(panic) }, + } + } + + func_wrapper::< $( $x, )* Rets, RetsAsResult, Self > as *const VMFunctionBody + } + } + + // Implement `HostFunction` for a function that has the same arity than the tuple. + // This specific function has an environment. + #[allow(unused_parens)] + impl< $( $x, )* Rets, RetsAsResult, Env, Func > + HostFunction<( $( $x ),* ), Rets, WithEnv, Env> + for + Func + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + RetsAsResult: IntoResult, + Env: Sized, + Func: Fn(&Env, $( $x , )*) -> RetsAsResult + Send + 'static, + { + #[allow(non_snake_case)] + fn function_body_ptr(self) -> *const VMFunctionBody { + /// This is a function that wraps the real host + /// function. Its address will be used inside the + /// runtime. + extern fn func_wrapper<$( $x, )* Rets, RetsAsResult, Env, Func>( env: &Env, $( $x: $x::Native, )* ) -> Rets::CStruct + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + RetsAsResult: IntoResult, + Env: Sized, + Func: Fn(&Env, $( $x ),* ) -> RetsAsResult + 'static + { + let func: &Func = unsafe { &*(&() as *const () as *const Func) }; + + let result = panic::catch_unwind(AssertUnwindSafe(|| { + func(env, $( FromToNativeWasmType::from_native($x) ),* ).into_result() + })); + + match result { + Ok(Ok(result)) => return result.into_c_struct(), + Ok(Err(trap)) => unsafe { raise_user_trap(Box::new(trap)) }, + Err(panic) => unsafe { resume_panic(panic) }, + } + } + + func_wrapper::< $( $x, )* Rets, RetsAsResult, Env, Self > as *const VMFunctionBody + } + } + }; + } + + // Black-magic to count the number of identifiers at compile-time. + macro_rules! count_idents { + ( $($idents:ident),* ) => { + { + #[allow(dead_code, non_camel_case_types)] + enum Idents { $( $idents, )* __CountIdentsLast } + const COUNT: usize = Idents::__CountIdentsLast as usize; + COUNT + } + }; + } + + // Here we go! Let's generate all the C struct, `WasmTypeList` + // implementations and `HostFunction` implementations. + impl_host_function!([C] S0,); + impl_host_function!([transparent] S1, A1); + impl_host_function!([C] S2, A1, A2); + impl_host_function!([C] S3, A1, A2, A3); + impl_host_function!([C] S4, A1, A2, A3, A4); + impl_host_function!([C] S5, A1, A2, A3, A4, A5); + impl_host_function!([C] S6, A1, A2, A3, A4, A5, A6); + impl_host_function!([C] S7, A1, A2, A3, A4, A5, A6, A7); + impl_host_function!([C] S8, A1, A2, A3, A4, A5, A6, A7, A8); + impl_host_function!([C] S9, A1, A2, A3, A4, A5, A6, A7, A8, A9); + impl_host_function!([C] S10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); + impl_host_function!([C] S11, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); + impl_host_function!([C] S12, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); + impl_host_function!([C] S13, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); + impl_host_function!([C] S14, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); + impl_host_function!([C] S15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); + impl_host_function!([C] S16, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); + impl_host_function!([C] S17, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); + impl_host_function!([C] S18, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18); + impl_host_function!([C] S19, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19); + impl_host_function!([C] S20, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20); + impl_host_function!([C] S21, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21); + impl_host_function!([C] S22, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22); + impl_host_function!([C] S23, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23); + impl_host_function!([C] S24, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24); + impl_host_function!([C] S25, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25); + impl_host_function!([C] S26, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26); + + // Implement `WasmTypeList` on `Infallible`, which means that + // `Infallible` can be used as a returned type of a host function + // to express that it doesn't return, or to express that it cannot + // fail (with `Result<_, Infallible>`). + impl WasmTypeList for Infallible { + type CStruct = Self; + type Array = [i128; 0]; + + fn from_array(_: Self::Array) -> Self { + unreachable!() + } + + fn from_slice(_: &[i128]) -> Result { + unreachable!() + } + + fn into_array(self) -> Self::Array { + [] + } + + fn empty_array() -> Self::Array { + unreachable!() + } + + fn from_c_struct(_: Self::CStruct) -> Self { + unreachable!() + } + + fn into_c_struct(self) -> Self::CStruct { + unreachable!() + } + + fn wasm_types() -> &'static [Type] { + &[] + } + } + + #[cfg(test)] + mod test_wasm_type_list { + use super::*; + use unc_vm_types::Type; + + #[test] + fn test_from_array() { + <()>::from_array([]); + assert_eq!(::from_array([1]), (1i32)); + assert_eq!(<(i32, i64)>::from_array([1, 2]), (1i32, 2i64)); + assert_eq!( + <(i32, i64, f32, f64)>::from_array([ + 1, + 2, + (3.1f32).to_bits().into(), + (4.2f64).to_bits().into() + ]), + (1, 2, 3.1f32, 4.2f64) + ); + } + + #[test] + fn test_into_array() { + assert_eq!(().into_array(), [0i128; 0]); + assert_eq!((1).into_array(), [1]); + assert_eq!((1i32, 2i64).into_array(), [1, 2]); + assert_eq!( + (1i32, 2i32, 3.1f32, 4.2f64).into_array(), + [1, 2, (3.1f32).to_bits().into(), (4.2f64).to_bits().into()] + ); + } + + #[test] + fn test_empty_array() { + assert_eq!(<()>::empty_array().len(), 0); + assert_eq!(::empty_array().len(), 1); + assert_eq!(<(i32, i64)>::empty_array().len(), 2); + } + + #[test] + fn test_from_c_struct() { + <()>::from_c_struct(S0()); + assert_eq!(::from_c_struct(S1(1)), (1i32)); + assert_eq!(<(i32, i64)>::from_c_struct(S2(1, 2)), (1i32, 2i64)); + assert_eq!( + <(i32, i64, f32, f64)>::from_c_struct(S4(1, 2, 3.1, 4.2)), + (1i32, 2i64, 3.1f32, 4.2f64) + ); + } + + #[test] + fn test_wasm_types_for_uni_values() { + assert_eq!(::wasm_types(), [Type::I32]); + assert_eq!(::wasm_types(), [Type::I64]); + assert_eq!(::wasm_types(), [Type::F32]); + assert_eq!(::wasm_types(), [Type::F64]); + } + + #[test] + fn test_wasm_types_for_multi_values() { + assert_eq!(<(i32, i32)>::wasm_types(), [Type::I32, Type::I32]); + assert_eq!(<(i64, i64)>::wasm_types(), [Type::I64, Type::I64]); + assert_eq!(<(f32, f32)>::wasm_types(), [Type::F32, Type::F32]); + assert_eq!(<(f64, f64)>::wasm_types(), [Type::F64, Type::F64]); + + assert_eq!( + <(i32, i64, f32, f64)>::wasm_types(), + [Type::I32, Type::I64, Type::F32, Type::F64] + ); + } + } + + #[allow(non_snake_case)] + #[cfg(test)] + mod test_function { + use super::*; + use unc_vm_types::Type; + + fn func() {} + fn func__i32() -> i32 { + 0 + } + fn func_i32(_a: i32) {} + fn func_i32__i32(a: i32) -> i32 { + a * 2 + } + fn func_i32_i32__i32(a: i32, b: i32) -> i32 { + a + b + } + fn func_i32_i32__i32_i32(a: i32, b: i32) -> (i32, i32) { + (a, b) + } + fn func_f32_i32__i32_f32(a: f32, b: i32) -> (i32, f32) { + (b, a) + } + + #[test] + fn test_function_types() { + assert_eq!(Function::new(func).ty(), FunctionType::new(vec![], vec![])); + assert_eq!(Function::new(func__i32).ty(), FunctionType::new(vec![], vec![Type::I32])); + assert_eq!(Function::new(func_i32).ty(), FunctionType::new(vec![Type::I32], vec![])); + assert_eq!( + Function::new(func_i32__i32).ty(), + FunctionType::new(vec![Type::I32], vec![Type::I32]) + ); + assert_eq!( + Function::new(func_i32_i32__i32).ty(), + FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]) + ); + assert_eq!( + Function::new(func_i32_i32__i32_i32).ty(), + FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32, Type::I32]) + ); + assert_eq!( + Function::new(func_f32_i32__i32_f32).ty(), + FunctionType::new(vec![Type::F32, Type::I32], vec![Type::I32, Type::F32]) + ); + } + + #[test] + fn test_function_pointer() { + let f = Function::new(func_i32__i32); + let function = unsafe { std::mem::transmute::<_, fn(usize, i32) -> i32>(f.address) }; + assert_eq!(function(0, 3), 6); + } + } +} diff --git a/runtime/unc-vm/test-api/src/sys/externals/global.rs b/runtime/unc-vm/test-api/src/sys/externals/global.rs new file mode 100644 index 000000000..74539a5aa --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/externals/global.rs @@ -0,0 +1,167 @@ +use super::super::exports::Exportable; +use super::super::store::{Store, StoreObject}; +use super::super::types::GlobalType; +use super::super::types::Mutability; +use super::super::types::Val; +use unc_vm_engine::RuntimeError; +use unc_vm_vm::{Export, Global as RuntimeGlobal, VMGlobal}; +use std::fmt; +use std::sync::Arc; + +/// A WebAssembly `global` instance. +/// +/// A global instance is the runtime representation of a global variable. +/// It consists of an individual value and a flag indicating whether it is mutable. +/// +/// Spec: +pub struct Global { + store: Store, + vm_global: VMGlobal, +} + +impl Global { + /// Create a new `Global` with the initial value [`Val`]. + pub fn new(store: &Store, val: Val) -> Self { + Self::from_value(store, val, Mutability::Const).unwrap() + } + + /// Create a mutable `Global` with the initial value [`Val`]. + pub fn new_mut(store: &Store, val: Val) -> Self { + Self::from_value(store, val, Mutability::Var).unwrap() + } + + /// Create a `Global` with the initial value [`Val`] and the provided [`Mutability`]. + fn from_value(store: &Store, val: Val, mutability: Mutability) -> Result { + if !val.comes_from_same_store(store) { + return Err(RuntimeError::new("cross-`Store` globals are not supported")); + } + let global = RuntimeGlobal::new(GlobalType { mutability, ty: val.ty() }); + unsafe { + global + .set_unchecked(val.clone()) + .map_err(|e| RuntimeError::new(format!("create global for {:?}: {}", val, e)))?; + }; + + Ok(Self { + store: store.clone(), + vm_global: VMGlobal { from: Arc::new(global), instance_ref: None }, + }) + } + + /// Returns the [`GlobalType`] of the `Global`. + pub fn ty(&self) -> &GlobalType { + self.vm_global.from.ty() + } + + /// Returns the [`Store`] where the `Global` belongs. + /// + /// # Example + /// + /// ``` + /// # use unc_vm_test_api::{Global, Store, Value}; + /// # let store = Store::default(); + /// # + /// let g = Global::new(&store, Value::I32(1)); + /// + /// assert_eq!(g.store(), &store); + /// ``` + pub fn store(&self) -> &Store { + &self.store + } + + /// Retrieves the current value [`Val`] that the Global has. + /// + /// # Example + /// + /// ``` + /// # use unc_vm_test_api::{Global, Store, Value}; + /// # let store = Store::default(); + /// # + /// let g = Global::new(&store, Value::I32(1)); + /// + /// assert_eq!(g.get(), Value::I32(1)); + /// ``` + pub fn get(&self) -> Val { + self.vm_global.from.get(&self.store) + } + + /// Sets a custom value [`Val`] to the runtime Global. + /// + /// # Example + /// + /// ``` + /// # use unc_vm_test_api::{Global, Store, Value}; + /// # let store = Store::default(); + /// # + /// let g = Global::new_mut(&store, Value::I32(1)); + /// + /// assert_eq!(g.get(), Value::I32(1)); + /// + /// g.set(Value::I32(2)); + /// + /// assert_eq!(g.get(), Value::I32(2)); + /// ``` + /// + /// # Errors + /// + /// Trying to mutate a immutable global will raise an error: + /// + /// ```should_panic + /// # use unc_vm_test_api::{Global, Store, Value}; + /// # let store = Store::default(); + /// # + /// let g = Global::new(&store, Value::I32(1)); + /// + /// g.set(Value::I32(2)).unwrap(); + /// ``` + /// + /// Trying to set a value of a incompatible type will raise an error: + /// + /// ```should_panic + /// # use unc_vm_test_api::{Global, Store, Value}; + /// # let store = Store::default(); + /// # + /// let g = Global::new(&store, Value::I32(1)); + /// + /// // This results in an error: `RuntimeError`. + /// g.set(Value::I64(2)).unwrap(); + /// ``` + pub fn set(&self, val: Val) -> Result<(), RuntimeError> { + if !val.comes_from_same_store(&self.store) { + return Err(RuntimeError::new("cross-`Store` values are not supported")); + } + unsafe { + self.vm_global.from.set(val).map_err(|e| RuntimeError::new(format!("{}", e)))?; + } + Ok(()) + } + + pub(crate) fn from_vm_export(store: &Store, vm_global: VMGlobal) -> Self { + Self { store: store.clone(), vm_global } + } +} + +impl Clone for Global { + fn clone(&self) -> Self { + let mut vm_global = self.vm_global.clone(); + vm_global.upgrade_instance_ref().unwrap(); + + Self { store: self.store.clone(), vm_global } + } +} + +impl fmt::Debug for Global { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter + .debug_struct("Global") + .field("ty", &self.ty()) + .field("value", &self.get()) + .finish() + } +} + +impl<'a> Exportable<'a> for Global { + fn to_export(&self) -> Export { + self.vm_global.clone().into() + } +} diff --git a/runtime/unc-vm/test-api/src/sys/externals/memory.rs b/runtime/unc-vm/test-api/src/sys/externals/memory.rs new file mode 100644 index 000000000..9c960c999 --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/externals/memory.rs @@ -0,0 +1,160 @@ +use super::super::exports::Exportable; +use super::super::store::Store; +use super::super::types::MemoryType; +use unc_vm_types::{MemoryView, Pages, ValueType}; +use unc_vm_vm::{Export, MemoryError, VMMemory}; +use std::convert::TryInto; +use std::slice; + +/// A WebAssembly `memory` instance. +/// +/// A memory instance is the runtime representation of a linear memory. +/// It consists of a vector of bytes and an optional maximum size. +/// +/// The length of the vector always is a multiple of the WebAssembly +/// page size, which is defined to be the constant 65536 – abbreviated 64Ki. +/// Like in a memory type, the maximum size in a memory instance is +/// given in units of this page size. +/// +/// A memory created by the host or in WebAssembly code will be accessible and +/// mutable from both host and WebAssembly. +/// +/// Spec: +#[derive(Debug)] +pub struct Memory { + store: Store, + vm_memory: VMMemory, +} + +impl Memory { + /// Creates a new host `Memory` from the provided [`MemoryType`]. + /// + /// This function will construct the `Memory` using the store + /// [`BaseTunables`][crate::sys::BaseTunables]. + pub fn new(store: &Store, ty: MemoryType) -> Result { + let tunables = store.tunables(); + let style = tunables.memory_style(&ty); + let memory = tunables.create_host_memory(&ty, &style)?; + + Ok(Self { + store: store.clone(), + vm_memory: VMMemory { + from: memory, + // We are creating it from the host, and therefore there is no + // associated instance with this memory + instance_ref: None, + }, + }) + } + + /// Create a `Memory` from `VMMemory`. + pub fn from_vmmemory(store: &Store, vm_memory: VMMemory) -> Self { + Self { store: store.clone(), vm_memory } + } + + /// Returns the [`MemoryType`] of the `Memory`. + pub fn ty(&self) -> MemoryType { + self.vm_memory.from.ty() + } + + /// Returns the [`Store`] where the `Memory` belongs. + pub fn store(&self) -> &Store { + &self.store + } + + /// Retrieve a slice of the memory contents. + /// + /// # Safety + /// + /// Until the returned slice is dropped, it is undefined behaviour to + /// modify the memory contents in any way including by calling a wasm + /// function that writes to the memory or by resizing the memory. + pub unsafe fn data_unchecked(&self) -> &[u8] { + self.data_unchecked_mut() + } + + /// Retrieve a mutable slice of the memory contents. + /// + /// # Safety + /// + /// This method provides interior mutability without an UnsafeCell. Until + /// the returned value is dropped, it is undefined behaviour to read or + /// write to the pointed-to memory in any way except through this slice, + /// including by calling a wasm function that reads the memory contents or + /// by resizing this Memory. + #[allow(clippy::mut_from_ref)] + pub unsafe fn data_unchecked_mut(&self) -> &mut [u8] { + let definition = self.vm_memory.from.vmmemory(); + let def = definition.as_ref(); + slice::from_raw_parts_mut(def.base, def.current_length.try_into().unwrap()) + } + + /// Returns the pointer to the raw bytes of the `Memory`. + pub fn data_ptr(&self) -> *mut u8 { + let definition = self.vm_memory.from.vmmemory(); + let def = unsafe { definition.as_ref() }; + def.base + } + + /// Returns the size (in bytes) of the `Memory`. + pub fn data_size(&self) -> u64 { + let definition = self.vm_memory.from.vmmemory(); + let def = unsafe { definition.as_ref() }; + def.current_length.try_into().unwrap() + } + + /// Returns the size (in [`Pages`]) of the `Memory`. + pub fn size(&self) -> Pages { + self.vm_memory.from.size() + } + + /// Return a "view" of the currently accessible memory. By + /// default, the view is unsynchronized, using regular memory + /// accesses. You can force a memory view to use atomic accesses + /// by calling the [`MemoryView::atomically`] method. + /// + /// # Notes: + /// + /// This method is safe (as in, it won't cause the host to crash or have UB), + /// but it doesn't obey rust's rules involving data races, especially concurrent ones. + /// Therefore, if this memory is shared between multiple threads, a single memory + /// location can be mutated concurrently without synchronization. + pub fn view(&self) -> MemoryView { + let base = self.data_ptr(); + + let length = self.size().bytes().0 / std::mem::size_of::(); + + unsafe { MemoryView::new(base as _, length as u32) } + } + + pub(crate) fn from_vm_export(store: &Store, vm_memory: VMMemory) -> Self { + Self { store: store.clone(), vm_memory } + } + + /// Get access to the backing VM value for this extern. This function is for + /// tests it should not be called by users of the Wasmer API. + /// + /// # Safety + /// This function is unsafe to call outside of tests for the wasmer crate + /// because there is no stability guarantee for the returned type and we may + /// make breaking changes to it at any time or remove this method. + #[doc(hidden)] + pub unsafe fn get_vm_memory(&self) -> &VMMemory { + &self.vm_memory + } +} + +impl Clone for Memory { + fn clone(&self) -> Self { + let mut vm_memory = self.vm_memory.clone(); + vm_memory.upgrade_instance_ref().unwrap(); + + Self { store: self.store.clone(), vm_memory } + } +} + +impl<'a> Exportable<'a> for Memory { + fn to_export(&self) -> Export { + self.vm_memory.clone().into() + } +} diff --git a/runtime/unc-vm/test-api/src/sys/externals/mod.rs b/runtime/unc-vm/test-api/src/sys/externals/mod.rs new file mode 100644 index 000000000..e20346854 --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/externals/mod.rs @@ -0,0 +1,105 @@ +pub(crate) mod function; +mod global; +mod memory; +mod table; + +pub use self::function::{FromToNativeWasmType, Function, WasmTypeList}; + +pub use self::global::Global; +pub use self::memory::Memory; +pub use self::table::Table; + +use super::exports::Exportable; +use super::store::{Store, StoreObject}; +use unc_vm_vm::Export; +use std::fmt; + +/// An `Extern` is the runtime representation of an entity that +/// can be imported or exported. +/// +/// Spec: +#[derive(Clone)] +pub enum Extern { + /// A external [`Function`]. + Function(Function), + /// A external [`Global`]. + Global(Global), + /// A external [`Table`]. + Table(Table), + /// A external [`Memory`]. + Memory(Memory), +} + +impl Extern { + /// Create an `Extern` from an `unc_vm_engine::Export`. + pub fn from_vm_export(store: &Store, export: Export) -> Self { + match export { + Export::Function(f) => Self::Function(Function::from_vm_export(store, f)), + Export::Memory(m) => Self::Memory(Memory::from_vm_export(store, m)), + Export::Global(g) => Self::Global(Global::from_vm_export(store, g)), + Export::Table(t) => Self::Table(Table::from_vm_export(store, t)), + } + } +} + +impl<'a> Exportable<'a> for Extern { + fn to_export(&self) -> Export { + match self { + Self::Function(f) => f.to_export(), + Self::Global(g) => g.to_export(), + Self::Memory(m) => m.to_export(), + Self::Table(t) => t.to_export(), + } + } +} + +impl StoreObject for Extern { + fn comes_from_same_store(&self, store: &Store) -> bool { + let my_store = match self { + Self::Function(f) => f.store(), + Self::Global(g) => g.store(), + Self::Memory(m) => m.store(), + Self::Table(t) => t.store(), + }; + Store::same(my_store, store) + } +} + +impl fmt::Debug for Extern { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::Function(_) => "Function(...)", + Self::Global(_) => "Global(...)", + Self::Memory(_) => "Memory(...)", + Self::Table(_) => "Table(...)", + } + ) + } +} + +impl From for Extern { + fn from(r: Function) -> Self { + Self::Function(r) + } +} + +impl From for Extern { + fn from(r: Global) -> Self { + Self::Global(r) + } +} + +impl From for Extern { + fn from(r: Memory) -> Self { + Self::Memory(r) + } +} + +impl From for Extern { + fn from(r: Table) -> Self { + Self::Table(r) + } +} diff --git a/runtime/unc-vm/test-api/src/sys/externals/table.rs b/runtime/unc-vm/test-api/src/sys/externals/table.rs new file mode 100644 index 000000000..553733249 --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/externals/table.rs @@ -0,0 +1,105 @@ +// This is test code... +#![allow(clippy::vtable_address_comparisons)] + +use super::super::exports::Exportable; +use super::super::store::Store; +use super::super::types::TableType; +use super::super::types::{Val, ValFuncRef}; +use unc_vm_engine::RuntimeError; +use unc_vm_vm::{Export, Table as RuntimeTable, TableElement, VMTable}; +use std::sync::Arc; + +/// A WebAssembly `table` instance. +/// +/// The `Table` struct is an array-like structure representing a WebAssembly Table, +/// which stores function references. +/// +/// A table created by the host or in WebAssembly code will be accessible and +/// mutable from both host and WebAssembly. +/// +/// Spec: +pub struct Table { + store: Store, + vm_table: VMTable, +} + +fn set_table_item( + table: &dyn RuntimeTable, + item_index: u32, + item: TableElement, +) -> Result<(), RuntimeError> { + table.set(item_index, item).map_err(|e| e.into()) +} + +impl Table { + /// Creates a new `Table` with the provided [`TableType`] definition. + /// + /// All the elements in the table will be set to the `init` value. + /// + /// This function will construct the `Table` using the store + /// [`BaseTunables`][crate::sys::BaseTunables]. + pub fn new(store: &Store, ty: TableType, init: Val) -> Result { + let item = init.into_table_reference(store)?; + let tunables = store.tunables(); + let style = tunables.table_style(&ty); + let table = tunables.create_host_table(&ty, &style).map_err(RuntimeError::new)?; + + let num_elements = table.size(); + for i in 0..num_elements { + set_table_item(table.as_ref(), i, item.clone())?; + } + + Ok(Self { store: store.clone(), vm_table: VMTable { from: table, instance_ref: None } }) + } + + /// Returns the [`TableType`] of the `Table`. + pub fn ty(&self) -> &TableType { + self.vm_table.from.ty() + } + + /// Returns the [`Store`] where the `Table` belongs. + pub fn store(&self) -> &Store { + &self.store + } + + /// Retrieves the size of the `Table` (in elements) + pub fn size(&self) -> u32 { + self.vm_table.from.size() + } + + pub(crate) fn from_vm_export(store: &Store, vm_table: VMTable) -> Self { + Self { store: store.clone(), vm_table } + } + + /// Returns whether or not these two tables refer to the same data. + pub fn same(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.vm_table.from, &other.vm_table.from) + } + + /// Get access to the backing VM value for this extern. This function is for + /// tests it should not be called by users of the Wasmer API. + /// + /// # Safety + /// This function is unsafe to call outside of tests for the unc-vm crate + /// because there is no stability guarantee for the returned type and we may + /// make breaking changes to it at any time or remove this method. + #[doc(hidden)] + pub unsafe fn get_vm_table(&self) -> &VMTable { + &self.vm_table + } +} + +impl Clone for Table { + fn clone(&self) -> Self { + let mut vm_table = self.vm_table.clone(); + vm_table.upgrade_instance_ref().unwrap(); + + Self { store: self.store.clone(), vm_table } + } +} + +impl<'a> Exportable<'a> for Table { + fn to_export(&self) -> Export { + self.vm_table.clone().into() + } +} diff --git a/runtime/unc-vm/test-api/src/sys/import_object.rs b/runtime/unc-vm/test-api/src/sys/import_object.rs new file mode 100644 index 000000000..86919a0cd --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/import_object.rs @@ -0,0 +1,403 @@ +//! The import module contains the implementation data structures and helper functions used to +//! manipulate and access a wasm module's imports including memories, tables, globals, and +//! functions. +use unc_vm_vm::{Export, NamedResolver}; +use std::borrow::BorrowMut; +use std::collections::VecDeque; +use std::collections::{hash_map::Entry, HashMap}; +use std::fmt; +use std::sync::{Arc, Mutex}; + +/// The `LikeNamespace` trait represents objects that act as a namespace for imports. +/// For example, an `Instance` or `Namespace` could be +/// considered namespaces that could provide imports to an instance. +pub trait LikeNamespace { + /// Gets an export by name. + fn get_namespace_export(&self, name: &str) -> Option; + /// Gets all exports in the namespace. + fn get_namespace_exports(&self) -> Vec<(String, Export)>; +} + +/// All of the import data used when instantiating. +/// +/// It's suggested that you use the [`imports!`] macro +/// instead of creating an `ImportObject` by hand. +/// +/// [`imports!`]: macro.imports.html +/// +/// # Usage: +/// ```ignore +/// use unc_vm_test_api::{Exports, ImportObject, Function}; +/// +/// let mut import_object = ImportObject::new(); +/// let mut env = Exports::new(); +/// +/// env.insert("foo", Function::new_native(foo)); +/// import_object.register("env", env); +/// +/// fn foo(n: i32) -> i32 { +/// n +/// } +/// ``` +#[derive(Clone, Default)] +pub struct ImportObject { + map: Arc>>>, +} + +impl ImportObject { + /// Create a new `ImportObject`. + pub fn new() -> Self { + Default::default() + } + + /// Gets an export given a module and a name + /// + /// # Usage + /// ```ignore + /// # use unc_vm_vm::{ImportObject, Instance, Namespace}; + /// let mut import_object = ImportObject::new(); + /// import_object.get_export("module", "name"); + /// ``` + pub fn get_export(&self, module: &str, name: &str) -> Option { + let map_ref = self.map.lock().unwrap(); + if map_ref.contains_key(module) { + let namespace = map_ref[module].as_ref(); + return namespace.get_namespace_export(name); + } + None + } + + /// Returns true if the ImportObject contains namespace with the provided name. + pub fn contains_namespace(&self, name: &str) -> bool { + self.map.lock().unwrap().contains_key(name) + } + + /// Register anything that implements `LikeNamespace` as a namespace. + /// + /// # Usage: + /// ```ignore + /// # use unc_vm_vm::{ImportObject, Instance, Namespace}; + /// let mut import_object = ImportObject::new(); + /// + /// import_object.register("namespace0", instance); + /// import_object.register("namespace1", namespace); + /// // ... + /// ``` + pub fn register(&mut self, name: S, namespace: N) -> Option> + where + S: Into, + N: LikeNamespace + Send + Sync + 'static, + { + let mut guard = self.map.lock().unwrap(); + let map = guard.borrow_mut(); + + match map.entry(name.into()) { + Entry::Vacant(empty) => { + empty.insert(Box::new(namespace)); + None + } + Entry::Occupied(mut occupied) => Some(occupied.insert(Box::new(namespace))), + } + } + + fn get_objects(&self) -> VecDeque<((String, String), Export)> { + let mut out = VecDeque::new(); + let map = self.map.lock().unwrap(); + for (name, ns) in map.iter() { + for (id, exp) in ns.get_namespace_exports() { + out.push_back(((name.clone(), id), exp)); + } + } + out + } +} + +impl NamedResolver for ImportObject { + fn resolve_by_name(&self, module: &str, name: &str) -> Option { + self.get_export(module, name) + } +} + +/// Iterator for an `ImportObject`'s exports. +pub struct ImportObjectIterator { + elements: VecDeque<((String, String), Export)>, +} + +impl Iterator for ImportObjectIterator { + type Item = ((String, String), Export); + fn next(&mut self) -> Option { + self.elements.pop_front() + } +} + +impl IntoIterator for ImportObject { + type IntoIter = ImportObjectIterator; + type Item = ((String, String), Export); + + fn into_iter(self) -> Self::IntoIter { + ImportObjectIterator { elements: self.get_objects() } + } +} + +impl fmt::Debug for ImportObject { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + enum SecretMap { + Empty, + Some(usize), + } + + impl SecretMap { + fn new(len: usize) -> Self { + if len == 0 { + Self::Empty + } else { + Self::Some(len) + } + } + } + + impl fmt::Debug for SecretMap { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Empty => write!(f, "(empty)"), + Self::Some(len) => write!(f, "(... {} item(s) ...)", len), + } + } + } + + f.debug_struct("ImportObject") + .field("map", &SecretMap::new(self.map.lock().unwrap().len())) + .finish() + } +} + +// The import! macro for ImportObject + +/// Generate an [`ImportObject`] easily with the `imports!` macro. +/// +/// [`ImportObject`]: struct.ImportObject.html +/// +/// # Usage +/// +/// ``` +/// # use unc_vm_test_api::{Function, Store}; +/// # let store = Store::default(); +/// use unc_vm_test_api::imports; +/// +/// let import_object = imports! { +/// "env" => { +/// "foo" => Function::new_native(&store, foo) +/// }, +/// }; +/// +/// fn foo(n: i32) -> i32 { +/// n +/// } +/// ``` +#[macro_export] +macro_rules! imports { + ( $( $ns_name:expr => $ns:tt ),* $(,)? ) => { + { + let mut import_object = $crate::ImportObject::new(); + + $({ + let namespace = $crate::import_namespace!($ns); + + import_object.register($ns_name, namespace); + })* + + import_object + } + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! namespace { + ($( $import_name:expr => $import_item:expr ),* $(,)? ) => { + $crate::import_namespace!( { $( $import_name => $import_item, )* } ) + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! import_namespace { + ( { $( $import_name:expr => $import_item:expr ),* $(,)? } ) => {{ + let mut namespace = $crate::Exports::new(); + + $( + namespace.insert($import_name, $import_item); + )* + + namespace + }}; + + ( $namespace:ident ) => { + $namespace + }; +} + +#[cfg(test)] +mod test { + use super::super::{Global, Store, Val}; + use super::*; + use unc_vm_types::Type; + use unc_vm_vm::ChainableNamedResolver; + + #[test] + fn chaining_works() { + let store = Store::default(); + let g = Global::new(&store, Val::I32(0)); + + let imports1 = imports! { + "dog" => { + "happy" => g.clone() + } + }; + + let imports2 = imports! { + "dog" => { + "small" => g.clone() + }, + "cat" => { + "small" => g + } + }; + + let resolver = imports1.chain_front(imports2); + + let small_cat_export = resolver.resolve_by_name("cat", "small"); + assert!(small_cat_export.is_some()); + + let happy = resolver.resolve_by_name("dog", "happy"); + let small = resolver.resolve_by_name("dog", "small"); + assert!(happy.is_some()); + assert!(small.is_some()); + } + + #[test] + fn extending_conflict_overwrites() { + let store = Store::default(); + let g1 = Global::new(&store, Val::I32(0)); + let g2 = Global::new(&store, Val::I64(0)); + + let imports1 = imports! { + "dog" => { + "happy" => g1, + }, + }; + + let imports2 = imports! { + "dog" => { + "happy" => g2, + }, + }; + + let resolver = imports1.chain_front(imports2); + let happy_dog_entry = resolver.resolve_by_name("dog", "happy").unwrap(); + + assert!(if let Export::Global(happy_dog_global) = happy_dog_entry { + happy_dog_global.from.ty().ty == Type::I64 + } else { + false + }); + + // now test it in reverse + let store = Store::default(); + let g1 = Global::new(&store, Val::I32(0)); + let g2 = Global::new(&store, Val::I64(0)); + + let imports1 = imports! { + "dog" => { + "happy" => g1, + }, + }; + + let imports2 = imports! { + "dog" => { + "happy" => g2, + }, + }; + + let resolver = imports1.chain_back(imports2); + let happy_dog_entry = resolver.resolve_by_name("dog", "happy").unwrap(); + + assert!(if let Export::Global(happy_dog_global) = happy_dog_entry { + happy_dog_global.from.ty().ty == Type::I32 + } else { + false + }); + } + + #[test] + fn namespace() { + let store = Store::default(); + let g1 = Global::new(&store, Val::I32(0)); + let namespace = namespace! { + "happy" => g1 + }; + let imports1 = imports! { + "dog" => namespace + }; + + let happy_dog_entry = imports1.resolve_by_name("dog", "happy").unwrap(); + + assert!(if let Export::Global(happy_dog_global) = happy_dog_entry { + happy_dog_global.from.ty().ty == Type::I32 + } else { + false + }); + } + + #[test] + fn imports_macro_allows_trailing_comma_and_none() { + use super::super::externals::Function; + + let store = Default::default(); + + fn func(arg: i32) -> i32 { + arg + 1 + } + + let _ = imports! { + "env" => { + "func" => Function::new_native(&store, func), + }, + }; + let _ = imports! { + "env" => { + "func" => Function::new_native(&store, func), + } + }; + let _ = imports! { + "env" => { + "func" => Function::new_native(&store, func), + }, + "abc" => { + "def" => Function::new_native(&store, func), + } + }; + let _ = imports! { + "env" => { + "func" => Function::new_native(&store, func) + }, + }; + let _ = imports! { + "env" => { + "func" => Function::new_native(&store, func) + } + }; + let _ = imports! { + "env" => { + "func1" => Function::new_native(&store, func), + "func2" => Function::new_native(&store, func) + } + }; + let _ = imports! { + "env" => { + "func1" => Function::new_native(&store, func), + "func2" => Function::new_native(&store, func), + } + }; + } +} diff --git a/runtime/unc-vm/test-api/src/sys/instance.rs b/runtime/unc-vm/test-api/src/sys/instance.rs new file mode 100644 index 000000000..0ad7707ec --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/instance.rs @@ -0,0 +1,172 @@ +use super::env::HostEnvInitError; +use super::exports::ExportError; +use super::externals::WasmTypeList; +use super::module::Module; +use super::native::NativeFunc; +use unc_vm_engine::{LinkError, RuntimeError}; +use unc_vm_vm::{Export, InstanceHandle, Resolver}; +use std::sync::{Arc, Mutex}; +use thiserror::Error; + +pub use unc_vm_types::InstanceConfig; + +/// A WebAssembly Instance is a stateful, executable +/// instance of a WebAssembly [`Module`]. +/// +/// Instance objects contain all the exported WebAssembly +/// functions, memories, tables and globals that allow +/// interacting with WebAssembly. +/// +/// Spec: +#[derive(Clone)] +pub struct Instance { + handle: Arc>, + module: Module, +} + +/// An error while instantiating a module. +/// +/// This is not a common WebAssembly error, however +/// we need to differentiate from a `LinkError` (an error +/// that happens while linking, on instantiation), a +/// Trap that occurs when calling the WebAssembly module +/// start function, and an error when initializing the user's +/// host environments. +#[derive(Error, Debug)] +pub enum InstantiationError { + /// A linking ocurred during instantiation. + #[error(transparent)] + Link(LinkError), + + /// A runtime error occured while invoking the start function + #[error("could not invoke the start function: {0}")] + Start(RuntimeError), + + /// The module was compiled with a CPU feature that is not available on + /// the current host. + #[error("missing requires CPU features: {0:?}")] + CpuFeature(String), + + /// Error occurred when initializing the host environment. + #[error(transparent)] + HostEnvInitialization(HostEnvInitError), +} + +impl From for InstantiationError { + fn from(other: unc_vm_engine::InstantiationError) -> Self { + match other { + unc_vm_engine::InstantiationError::Link(e) => Self::Link(e), + unc_vm_engine::InstantiationError::Start(e) => Self::Start(e), + unc_vm_engine::InstantiationError::CpuFeature(e) => Self::CpuFeature(e), + } + } +} + +impl From for InstantiationError { + fn from(other: HostEnvInitError) -> Self { + Self::HostEnvInitialization(other) + } +} + +impl Instance { + /// Creates a new `Instance` from a WebAssembly [`Module`] and a + /// set of imports resolved by the [`Resolver`]. + /// + /// The resolver can be anything that implements the [`Resolver`] trait, + /// so you can plug custom resolution for the imports, if you wish not + /// to use [`ImportObject`]. + /// + /// The [`ImportObject`] is the easiest way to provide imports to the instance. + /// + /// [`ImportObject`]: crate::ImportObject + /// + /// ``` + /// # use unc_vm_test_api::{imports, Store, Module, Global, Value, Instance, InstanceConfig}; + /// # fn main() -> anyhow::Result<()> { + /// let store = Store::default(); + /// let module = Module::new(&store, "(module)")?; + /// let imports = imports!{ + /// "host" => { + /// "var" => Global::new(&store, Value::I32(2)) + /// } + /// }; + /// let instance = Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000), &imports)?; + /// # Ok(()) + /// # } + /// ``` + /// + /// ## Errors + /// + /// The function can return [`InstantiationError`]s. + /// + /// Those are, as defined by the spec: + /// * Link errors that happen when plugging the imports into the instance + /// * Runtime errors that happen when running the module `start` function. + #[tracing::instrument(target = "unc_vm", level = "trace", skip_all)] + pub fn new_with_config( + module: &Module, + config: InstanceConfig, + resolver: &dyn Resolver, + ) -> Result { + let handle = module.instantiate(resolver, config)?; + let instance = Self { handle: Arc::new(Mutex::new(handle)), module: module.clone() }; + + // # Safety + // `initialize_host_envs` should be called after instantiation but before + // returning an `Instance` to the user. We set up the host environments + // via `WasmerEnv::init_with_instance`. + // + // This usage is correct because we pass a valid pointer to `instance` and the + // correct error type returned by `WasmerEnv::init_with_instance` as a generic + // parameter. + unsafe { + unc_vm_vm::initialize_host_envs::( + &*instance.handle, + &instance as *const _ as *const _, + )?; + } + + Ok(instance) + } + + /// Lookup an exported entity by its name. + pub fn lookup(&self, field: &str) -> Option { + let vmextern = self.handle.lock().unwrap().lookup(field)?; + Some(vmextern.into()) + } + + /// Lookup an exported function by its name. + pub fn lookup_function(&self, field: &str) -> Option { + if let Export::Function(f) = self.lookup(field)? { + Some(super::externals::Function::from_vm_export(self.module.store(), f)) + } else { + None + } + } + + /// Get an export as a `NativeFunc`. + pub fn get_native_function( + &self, + name: &str, + ) -> Result, ExportError> + where + Args: WasmTypeList, + Rets: WasmTypeList, + { + match self.lookup(name) { + Some(Export::Function(f)) => { + super::externals::Function::from_vm_export(self.module.store(), f) + .native() + .map_err(|_| ExportError::IncompatibleType) + } + Some(_) => Err(ExportError::IncompatibleType), + None => Err(ExportError::Missing("not found".into())), + } + } + + // Used internally by wast only + #[doc(hidden)] + pub fn handle(&self) -> std::sync::MutexGuard<'_, InstanceHandle> { + self.handle.lock().unwrap() + } +} diff --git a/runtime/unc-vm/test-api/src/sys/mod.rs b/runtime/unc-vm/test-api/src/sys/mod.rs new file mode 100644 index 000000000..563559c2e --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/mod.rs @@ -0,0 +1,34 @@ +mod env; +mod exports; +mod externals; +mod import_object; +mod instance; +mod module; +mod native; +mod ptr; +mod store; +mod tunables; +mod types; + +pub use crate::sys::env::{LazyInit, WasmerEnv}; +pub use crate::sys::exports::Exports; +pub use crate::sys::externals::{Function, Global, Memory, Table}; +pub use crate::sys::import_object::{ImportObject, LikeNamespace}; +pub use crate::sys::instance::{Instance, InstanceConfig, InstantiationError}; +pub use crate::sys::module::Module; +pub use crate::sys::native::NativeFunc; +pub use crate::sys::store::Store; +pub use crate::sys::tunables::BaseTunables; +pub use crate::sys::types::{FunctionType, MemoryType, TableType, Val, ValType}; +pub use crate::sys::types::{Val as Value, ValType as Type}; +pub use unc_vm_types::ExternRef; +pub use unc_vm_vm::{Export, NamedResolver}; + +#[cfg(feature = "wat")] +pub use wat::parse_bytes as wat2wasm; + +#[cfg(feature = "singlepass")] +pub use unc_vm_compiler_singlepass::Singlepass; + +#[cfg(feature = "universal")] +pub use unc_vm_engine::universal::{Universal, UniversalArtifact, UniversalEngine}; diff --git a/runtime/unc-vm/test-api/src/sys/module.rs b/runtime/unc-vm/test-api/src/sys/module.rs new file mode 100644 index 000000000..1f2829c83 --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/module.rs @@ -0,0 +1,161 @@ +#![allow(clippy::arc_with_non_send_sync)] + +use super::instance::InstantiationError; +use super::store::Store; +use unc_vm_compiler::CompileError; +#[cfg(feature = "wat")] +use unc_vm_compiler::WasmError; +use unc_vm_engine::RuntimeError; +use unc_vm_types::InstanceConfig; +use unc_vm_vm::{InstanceHandle, Instantiatable, Resolver}; +use std::fmt; +use std::io; +use std::sync::Arc; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum IoCompileError { + /// An IO error + #[error(transparent)] + Io(#[from] io::Error), + /// A compilation error + #[error(transparent)] + Compile(#[from] CompileError), +} + +/// A WebAssembly Module contains stateless WebAssembly +/// code that has already been compiled and can be instantiated +/// multiple times. +/// +/// ## Cloning a module +/// +/// Cloning a module is cheap: it does a shallow copy of the compiled +/// contents rather than a deep copy. +#[derive(Clone)] +pub struct Module { + store: Store, + artifact: Arc, +} + +impl Module { + /// Creates a new WebAssembly Module given the configuration + /// in the store. + /// + /// If the provided bytes are not WebAssembly-like (start with `b"\0asm"`), + /// and the "wat" feature is enabled for this crate, this function will try to + /// to convert the bytes assuming they correspond to the WebAssembly text + /// format. + /// + /// ## Security + /// + /// Before the code is compiled, it will be validated using the store + /// features. + /// + /// ## Errors + /// + /// Creating a WebAssembly module from bytecode can result in a + /// [`CompileError`] since this operation requires to transorm the Wasm + /// bytecode into code the machine can easily execute. + /// + /// ## Example + /// + /// Reading from a WAT file. + /// + /// ``` + /// use unc_vm_test_api::*; + /// # fn main() -> anyhow::Result<()> { + /// # let store = Store::default(); + /// let wat = "(module)"; + /// let module = Module::new(&store, wat)?; + /// # Ok(()) + /// # } + /// ``` + /// + /// Reading from bytes: + /// + /// ``` + /// use unc_vm_test_api::*; + /// # fn main() -> anyhow::Result<()> { + /// # let store = Store::default(); + /// // The following is the same as: + /// // (module + /// // (type $t0 (func (param i32) (result i32))) + /// // (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32) + /// // get_local $p0 + /// // i32.const 1 + /// // i32.add) + /// // ) + /// let bytes: Vec = vec![ + /// 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, + /// 0x01, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x0b, 0x01, 0x07, + /// 0x61, 0x64, 0x64, 0x5f, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x0a, 0x09, 0x01, + /// 0x07, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x0b, 0x00, 0x1a, 0x04, 0x6e, + /// 0x61, 0x6d, 0x65, 0x01, 0x0a, 0x01, 0x00, 0x07, 0x61, 0x64, 0x64, 0x5f, + /// 0x6f, 0x6e, 0x65, 0x02, 0x07, 0x01, 0x00, 0x01, 0x00, 0x02, 0x70, 0x30, + /// ]; + /// let module = Module::new(&store, bytes)?; + /// # Ok(()) + /// # } + /// ``` + #[allow(unreachable_code)] + #[tracing::instrument(target = "unc_vm", level = "trace", skip_all)] + pub fn new(store: &Store, bytes: impl AsRef<[u8]>) -> Result { + #[cfg(feature = "wat")] + let bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| { + CompileError::Wasm(WasmError::Generic(format!("Error when converting wat: {}", e))) + })?; + + Self::from_binary(store, bytes.as_ref()) + } + + /// Creates a new WebAssembly module from a binary. + /// + /// Opposed to [`Module::new`], this function is not compatible with + /// the WebAssembly text format (if the "wat" feature is enabled for + /// this crate). + #[tracing::instrument(target = "unc_vm", level = "trace", skip_all)] + pub(crate) fn from_binary(store: &Store, binary: &[u8]) -> Result { + let engine = store.engine(); + engine.validate(binary)?; + let executable = engine.compile_universal(binary, store.tunables())?; + let artifact = engine.load_universal_executable(&executable)?; + Ok(Self { store: store.clone(), artifact: Arc::new(artifact) }) + } + + pub(crate) fn instantiate( + &self, + resolver: &dyn Resolver, + config: InstanceConfig, + ) -> Result { + unsafe { + let instance_handle = Arc::clone(&self.artifact).instantiate( + self.store.tunables(), + resolver, + Box::new((self.store.clone(), Arc::clone(&self.artifact))), + config, + )?; + + // After the instance handle is created, we need to initialize + // the data, call the start function and so. However, if any + // of this steps traps, we still need to keep the instance alive + // as some of the Instance elements may have placed in other + // instance tables. + instance_handle + .finish_instantiation() + .map_err(|t| InstantiationError::Start(RuntimeError::from_trap(t)))?; + + Ok(instance_handle) + } + } + + /// Returns the [`Store`] where the `Instance` belongs. + pub fn store(&self) -> &Store { + &self.store + } +} + +impl fmt::Debug for Module { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Module").finish() + } +} diff --git a/runtime/unc-vm/test-api/src/sys/native.rs b/runtime/unc-vm/test-api/src/sys/native.rs new file mode 100644 index 000000000..4311bdbea --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/native.rs @@ -0,0 +1,232 @@ +//! Native Functions. +//! +//! This module creates the helper `NativeFunc` that let us call WebAssembly +//! functions with the native ABI, that is: +//! +//! ```ignore +//! let add_one = instance.exports.get_function("function_name")?; +//! let add_one_native: NativeFunc = add_one.native().unwrap(); +//! ``` +use std::marker::PhantomData; + +use super::externals::function::{DynamicFunction, VMDynamicFunction}; +use super::externals::{FromToNativeWasmType, Function, WasmTypeList}; +use super::store::Store; +use unc_vm_engine::RuntimeError; +use unc_vm_types::NativeWasmType; +use unc_vm_vm::{ + ExportFunction, VMDynamicFunctionContext, VMFunctionBody, VMFunctionEnvironment, VMFunctionKind, +}; +use std::panic::{catch_unwind, AssertUnwindSafe}; + +/// A WebAssembly function that can be called natively +/// (using the Native ABI). +pub struct NativeFunc { + store: Store, + exported: ExportFunction, + _phantom: PhantomData<(Args, Rets)>, +} + +unsafe impl Send for NativeFunc {} + +impl NativeFunc +where + Args: WasmTypeList, + Rets: WasmTypeList, +{ + pub(crate) fn new(store: Store, exported: ExportFunction) -> Self { + Self { store, exported, _phantom: PhantomData } + } + + pub(crate) fn is_host(&self) -> bool { + self.exported.vm_function.instance_ref.is_none() + } + + pub(crate) fn vmctx(&self) -> VMFunctionEnvironment { + self.exported.vm_function.vmctx + } + + pub(crate) fn address(&self) -> *const VMFunctionBody { + self.exported.vm_function.address + } + + pub(crate) fn arg_kind(&self) -> VMFunctionKind { + self.exported.vm_function.kind + } +} + +/* +impl From<&NativeFunc> for VMFunction +where + Args: WasmTypeList, + Rets: WasmTypeList, +{ + fn from(other: &NativeFunc) -> Self { + let signature = FunctionType::new(Args::wasm_types(), Rets::wasm_types()); + Self { + address: other.address, + vmctx: other.vmctx, + signature, + kind: other.arg_kind, + call_trampoline: None, + instance_ref: None, + } + } +}*/ + +impl Clone for NativeFunc { + fn clone(&self) -> Self { + let mut exported = self.exported.clone(); + exported.vm_function.upgrade_instance_ref().unwrap(); + + Self { store: self.store.clone(), exported, _phantom: PhantomData } + } +} + +impl From<&NativeFunc> for ExportFunction +where + Args: WasmTypeList, + Rets: WasmTypeList, +{ + fn from(other: &NativeFunc) -> Self { + other.exported.clone() + } +} + +impl From> for Function +where + Args: WasmTypeList, + Rets: WasmTypeList, +{ + fn from(other: NativeFunc) -> Self { + Self { store: other.store, exported: other.exported } + } +} + +macro_rules! impl_native_traits { + ( $( $x:ident ),* ) => { + #[allow(unused_parens, non_snake_case)] + impl<$( $x , )* Rets> NativeFunc<( $( $x ),* ), Rets> + where + $( $x: FromToNativeWasmType, )* + Rets: WasmTypeList, + { + /// Call the typed func and return results. + pub fn call(&self, $( $x: $x, )* ) -> Result { + if !self.is_host() { + // We assume the trampoline is always going to be present for + // Wasm functions + let trampoline = self.exported.vm_function.call_trampoline.expect("Call trampoline not found in wasm function"); + // TODO: when `const fn` related features mature more, we can declare a single array + // of the correct size here. + let mut params_list = [ $( $x.to_native().to_binary() ),* ]; + let mut rets_list_array = Rets::empty_array(); + let rets_list = rets_list_array.as_mut(); + let using_rets_array; + let args_rets: &mut [i128] = if params_list.len() > rets_list.len() { + using_rets_array = false; + params_list.as_mut() + } else { + using_rets_array = true; + for (i, &arg) in params_list.iter().enumerate() { + rets_list[i] = arg; + } + rets_list.as_mut() + }; + unsafe { + unc_vm_vm::unc_vm_call_trampoline( + self.vmctx(), + trampoline, + self.address(), + args_rets.as_mut_ptr() as *mut u8, + ) + }?; + let num_rets = rets_list.len(); + if !using_rets_array && num_rets > 0 { + let src_pointer = params_list.as_ptr(); + let rets_list = &mut rets_list_array.as_mut()[0] as *mut i128; + unsafe { + // TODO: we can probably remove this copy by doing some clever `transmute`s. + // we know it's not overlapping because `using_rets_array` is false + std::ptr::copy_nonoverlapping(src_pointer, + rets_list, + num_rets); + } + } + Ok(Rets::from_array(rets_list_array)) + // TODO: When the Host ABI and Wasm ABI are the same, we could do this instead: + // but we can't currently detect whether that's safe. + // + // let results = unsafe { + // unc_vm_vm::catch_traps_with_result(self.vmctx, || { + // let f = std::mem::transmute::<_, unsafe extern "C" fn( *mut VMContext, $( $x, )*) -> Rets::CStruct>(self.address()); + // // We always pass the vmctx + // f( self.vmctx, $( $x, )* ) + // }).map_err(RuntimeError::from_trap)? + // }; + // Ok(Rets::from_c_struct(results)) + + } + else { + match self.arg_kind() { + VMFunctionKind::Static => { + let results = catch_unwind(AssertUnwindSafe(|| unsafe { + let f = std::mem::transmute::<_, unsafe extern "C" fn( VMFunctionEnvironment, $( $x, )*) -> Rets::CStruct>(self.address()); + // We always pass the vmctx + f( self.vmctx(), $( $x, )* ) + })).map_err(|e| RuntimeError::new(format!("{:?}", e)))?; + Ok(Rets::from_c_struct(results)) + }, + VMFunctionKind::Dynamic => { + let params_list = [ $( $x.to_native().to_value() ),* ]; + let results = { + type VMContextWithEnv = VMDynamicFunctionContext>; + unsafe { + let ctx = self.vmctx().host_env as *mut VMContextWithEnv; + (*ctx).ctx.call(¶ms_list)? + } + }; + let mut rets_list_array = Rets::empty_array(); + let mut_rets = rets_list_array.as_mut() as *mut [i128] as *mut i128; + for (i, ret) in results.iter().enumerate() { + unsafe { + ret.write_value_to(mut_rets.add(i)); + } + } + Ok(Rets::from_array(rets_list_array)) + } + } + } + } + + } + }; +} + +impl_native_traits!(); +impl_native_traits!(A1); +impl_native_traits!(A1, A2); +impl_native_traits!(A1, A2, A3); +impl_native_traits!(A1, A2, A3, A4); +impl_native_traits!(A1, A2, A3, A4, A5); +impl_native_traits!(A1, A2, A3, A4, A5, A6); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16); +impl_native_traits!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17); +impl_native_traits!( + A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18 +); +impl_native_traits!( + A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19 +); +impl_native_traits!( + A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20 +); diff --git a/runtime/unc-vm/test-api/src/sys/ptr.rs b/runtime/unc-vm/test-api/src/sys/ptr.rs new file mode 100644 index 000000000..720c8a6ba --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/ptr.rs @@ -0,0 +1,44 @@ +//! Types for a reusable pointer abstraction for accessing Wasm linear memory. +//! +//! This abstraction is safe: it ensures the memory is in bounds and that the pointer +//! is aligned (avoiding undefined behavior). +//! +//! Therefore, you should use this abstraction whenever possible to avoid memory +//! related bugs when implementing an ABI. + +use super::externals::FromToNativeWasmType; +use unc_vm_types::ValueType; +use std::marker::PhantomData; + +/// The `Item` marker type. This is the default and does not usually need to be +/// specified. +pub struct Item; + +/// A zero-cost type that represents a pointer to something in Wasm linear +/// memory. +#[repr(transparent)] +pub struct WasmPtr { + offset: u32, + _phantom: PhantomData<(T, Ty)>, +} + +unsafe impl FromToNativeWasmType for WasmPtr { + type Native = i32; + + fn to_native(self) -> Self::Native { + self.offset as i32 + } + fn from_native(n: Self::Native) -> Self { + Self { offset: n as u32, _phantom: PhantomData } + } +} + +unsafe impl ValueType for WasmPtr {} + +impl Clone for WasmPtr { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for WasmPtr {} diff --git a/runtime/unc-vm/test-api/src/sys/store.rs b/runtime/unc-vm/test-api/src/sys/store.rs new file mode 100644 index 000000000..fffa0449e --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/store.rs @@ -0,0 +1,117 @@ +use crate::sys::tunables::BaseTunables; +#[cfg(all(feature = "compiler", feature = "engine"))] +use unc_vm_compiler::CompilerConfig; +use unc_vm_engine::universal::UniversalEngine; +use unc_vm_vm::Tunables; +use std::fmt; +use std::sync::Arc; + +/// The store represents all global state that can be manipulated by +/// WebAssembly programs. It consists of the runtime representation +/// of all instances of functions, tables, memories, and globals that +/// have been allocated during the lifetime of the abstract machine. +/// +/// The `Store` holds the engine (that is —amongst many things— used to compile +/// the Wasm bytes into a valid module artifact), in addition to the +/// [`Tunables`] (that are used to create the memories, tables and globals). +/// +/// Spec: +#[derive(Clone)] +pub struct Store { + engine: Arc, + tunables: Arc, +} + +impl Store { + /// Creates a new `Store` with a specific [`Engine`]. + pub fn new(engine: Arc) -> Self { + Self::new_with_tunables(Arc::clone(&engine), BaseTunables::for_target(engine.target())) + } + + /// Creates a new `Store` with a specific [`Engine`] and [`Tunables`]. + pub fn new_with_tunables( + engine: Arc, + tunables: impl Tunables + Send + Sync + 'static, + ) -> Self { + Self { engine, tunables: Arc::new(tunables) } + } + + /// Returns the [`Tunables`]. + pub fn tunables(&self) -> &dyn Tunables { + self.tunables.as_ref() + } + + /// Returns the [`Engine`]. + pub fn engine(&self) -> Arc { + Arc::clone(&self.engine) + } + + /// Checks whether two stores are identical. A store is considered + /// equal to another store if both have the same engine. The + /// tunables are excluded from the logic. + pub fn same(a: &Self, b: &Self) -> bool { + a.engine.id() == b.engine.id() + } +} + +impl PartialEq for Store { + fn eq(&self, other: &Self) -> bool { + Self::same(self, other) + } +} + +unsafe impl Send for Store {} +unsafe impl Sync for Store {} + +// We only implement default if we have assigned a default compiler and engine +#[cfg(all(feature = "default-compiler", feature = "default-engine"))] +impl Default for Store { + fn default() -> Self { + // We store them on a function that returns to make + // sure this function doesn't emit a compile error even if + // more than one compiler is enabled. + fn get_config() -> impl CompilerConfig + 'static { + cfg_if::cfg_if! { + if #[cfg(feature = "default-singlepass")] { + unc_vm_compiler_singlepass::Singlepass::default() + } else { + compile_error!("No default compiler chosen") + } + } + } + + #[allow(unused_mut)] + fn get_engine(mut config: impl CompilerConfig + 'static) -> UniversalEngine { + cfg_if::cfg_if! { + if #[cfg(feature = "default-universal")] { + let pool = unc_vm_engine::universal::LimitedMemoryPool::new(1, 0x10000).unwrap(); + unc_vm_engine::universal::Universal::new(config) + .code_memory_pool(pool) + .engine() + } else if #[cfg(feature = "default-dylib")] { + unc_vm_engine_dylib::Dylib::new(config) + .engine() + } else { + compile_error!("No default engine chosen") + } + } + } + + let config = get_config(); + let engine = get_engine(config); + let tunables = BaseTunables::for_target(engine.target()); + Self::new_with_tunables(engine.into(), tunables) + } +} + +impl fmt::Debug for Store { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Store").finish() + } +} + +/// A trait represinting any object that lives in the `Store`. +pub trait StoreObject { + /// Return true if the object `Store` is the same as the provided `Store`. + fn comes_from_same_store(&self, store: &Store) -> bool; +} diff --git a/runtime/unc-vm/test-api/src/sys/tunables.rs b/runtime/unc-vm/test-api/src/sys/tunables.rs new file mode 100644 index 000000000..660c8b4d6 --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/tunables.rs @@ -0,0 +1,260 @@ +use super::types::{MemoryType, TableType}; +use unc_vm_compiler::Target; +use unc_vm_types::Pages; +use unc_vm_vm::MemoryError; +use unc_vm_vm::{ + LinearMemory, LinearTable, Memory, MemoryStyle, Table, TableStyle, Tunables, + VMMemoryDefinition, VMTableDefinition, +}; +use std::ptr::NonNull; +use std::sync::Arc; +use target_lexicon::PointerWidth; + +/// Tunable parameters for WebAssembly compilation. +/// This is the reference implementation of the `Tunables` trait, +/// used by default. +/// +/// You can use this as a template for creating a custom Tunables +/// implementation or use composition to wrap your Tunables around +/// this one. The later approach is demonstrated in the +/// tunables-limit-memory example. +#[derive(Clone)] +pub struct BaseTunables { + /// For static heaps, the size in wasm pages of the heap protected by bounds checking. + pub static_memory_bound: Pages, + + /// The size in bytes of the offset guard for static heaps. + pub static_memory_offset_guard_size: u64, + + /// The size in bytes of the offset guard for dynamic heaps. + pub dynamic_memory_offset_guard_size: u64, + + /// The cost of a regular op. + pub regular_op_cost: u64, +} + +impl BaseTunables { + /// Get the `BaseTunables` for a specific Target + pub fn for_target(target: &Target) -> Self { + let triple = target.triple(); + let pointer_width: PointerWidth = triple.pointer_width().unwrap(); + let (static_memory_bound, static_memory_offset_guard_size): (Pages, u64) = + match pointer_width { + PointerWidth::U16 => (0x400.into(), 0x1000), + PointerWidth::U32 => (0x4000.into(), 0x1_0000), + // Static Memory Bound: + // Allocating 4 GiB of address space let us avoid the + // need for explicit bounds checks. + // Static Memory Guard size: + // Allocating 2 GiB of address space lets us translate wasm + // offsets into x86 offsets as aggressively as we can. + PointerWidth::U64 => (0x1_0000.into(), 0x8000_0000), + }; + + // Allocate a small guard to optimize common cases but without + // wasting too much memory. + // The Windows memory manager seems more laxed than the other ones + // And a guard of just 1 page may not be enough is some borderline cases + // So using 2 pages for guard on this platform + #[cfg(target_os = "windows")] + let dynamic_memory_offset_guard_size: u64 = 0x2_0000; + #[cfg(not(target_os = "windows"))] + let dynamic_memory_offset_guard_size: u64 = 0x1_0000; + + Self { + static_memory_bound, + static_memory_offset_guard_size, + dynamic_memory_offset_guard_size, + regular_op_cost: 0, + } + } + + /// Set the regular op cost for this compiler + pub fn set_regular_op_cost(&mut self, cost: u64) -> &mut Self { + self.regular_op_cost = cost; + self + } +} + +impl Tunables for BaseTunables { + /// Get a `MemoryStyle` for the provided `MemoryType` + fn memory_style(&self, memory: &MemoryType) -> MemoryStyle { + // A heap with a maximum that doesn't exceed the static memory bound specified by the + // tunables make it static. + // + // If the module doesn't declare an explicit maximum treat it as 4GiB. + let maximum = memory.maximum.unwrap_or_else(Pages::max_value); + if maximum <= self.static_memory_bound { + MemoryStyle::Static { + // Bound can be larger than the maximum for performance reasons + bound: self.static_memory_bound, + offset_guard_size: self.static_memory_offset_guard_size, + } + } else { + MemoryStyle::Dynamic { offset_guard_size: self.dynamic_memory_offset_guard_size } + } + } + + /// Get a [`TableStyle`] for the provided [`TableType`]. + fn table_style(&self, _table: &TableType) -> TableStyle { + TableStyle::CallerChecksSignature + } + + /// Create a memory owned by the host given a [`MemoryType`] and a [`MemoryStyle`]. + fn create_host_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + ) -> Result, MemoryError> { + Ok(Arc::new(LinearMemory::new(&ty, &style)?)) + } + + /// Create a memory owned by the VM given a [`MemoryType`] and a [`MemoryStyle`]. + /// + /// # Safety + /// - `vm_definition_location` must point to a valid, owned `VMMemoryDefinition`, + /// for example in `VMContext`. + unsafe fn create_vm_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + vm_definition_location: NonNull, + ) -> Result, MemoryError> { + Ok(Arc::new(LinearMemory::from_definition(&ty, &style, vm_definition_location)?)) + } + + /// Create a table owned by the host given a [`TableType`] and a [`TableStyle`]. + fn create_host_table( + &self, + ty: &TableType, + style: &TableStyle, + ) -> Result, String> { + Ok(Arc::new(LinearTable::new(&ty, &style)?)) + } + + /// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`]. + /// + /// # Safety + /// - `vm_definition_location` must point to a valid, owned `VMTableDefinition`, + /// for example in `VMContext`. + unsafe fn create_vm_table( + &self, + ty: &TableType, + style: &TableStyle, + vm_definition_location: NonNull, + ) -> Result, String> { + Ok(Arc::new(LinearTable::from_definition(&ty, &style, vm_definition_location)?)) + } + + fn stack_init_gas_cost(&self, stack_size: u64) -> u64 { + (self.regular_op_cost / 8).saturating_mul(stack_size) + } + + /// Instrumentation configuration: stack limiter config + fn stack_limiter_cfg(&self) -> Box { + Box::new(SimpleMaxStackCfg) + } + + /// Instrumentation configuration: gas accounting config + fn gas_cfg(&self) -> Box> { + Box::new(SimpleGasCostCfg(self.regular_op_cost)) + } +} + +struct SimpleMaxStackCfg; + +impl finite_wasm::max_stack::SizeConfig for SimpleMaxStackCfg { + fn size_of_value(&self, ty: finite_wasm::wasmparser::ValType) -> u8 { + use finite_wasm::wasmparser::ValType; + match ty { + ValType::I32 => 4, + ValType::I64 => 8, + ValType::F32 => 4, + ValType::F64 => 8, + ValType::V128 => 16, + ValType::Ref(_) => 8, + } + } + fn size_of_function_activation( + &self, + locals: &prefix_sum_vec::PrefixSumVec, + ) -> u64 { + let mut res = 0; + res += locals.max_index().map_or(0, |l| u64::from(*l).saturating_add(1)) * 8; + // TODO: make the above take into account the types of locals by adding an iter on PrefixSumVec that returns (count, type) + res += 64; // Rough accounting for rip, rbp and some registers spilled. Not exact. + res + } +} + +struct SimpleGasCostCfg(u64); + +macro_rules! gas_cost { + ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { + $( + fn $visit(&mut self $($(, $arg: $argty)*)?) -> u64 { + gas_cost!(@@$proposal $op self $({ $($arg: $argty),* })? => $visit) + } + )* + }; + + (@@mvp $_op:ident $_self:ident $({ $($_arg:ident: $_argty:ty),* })? => visit_block) => { + 0 + }; + (@@mvp $_op:ident $_self:ident $({ $($_arg:ident: $_argty:ty),* })? => visit_end) => { + 0 + }; + (@@mvp $_op:ident $_self:ident $({ $($_arg:ident: $_argty:ty),* })? => visit_else) => { + 0 + }; + (@@$_proposal:ident $_op:ident $self:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident) => { + $self.0 + }; +} + +impl<'a> finite_wasm::wasmparser::VisitOperator<'a> for SimpleGasCostCfg { + type Output = u64; + finite_wasm::wasmparser::for_each_operator!(gas_cost); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn memory_style() { + let tunables = BaseTunables { + static_memory_bound: Pages(2048), + static_memory_offset_guard_size: 128, + dynamic_memory_offset_guard_size: 256, + regular_op_cost: 0, + }; + + // No maximum + let requested = MemoryType::new(3, None, true); + let style = tunables.memory_style(&requested); + match style { + MemoryStyle::Dynamic { offset_guard_size } => assert_eq!(offset_guard_size, 256), + s => panic!("Unexpected memory style: {:?}", s), + } + + // Large maximum + let requested = MemoryType::new(3, Some(5_000_000), true); + let style = tunables.memory_style(&requested); + match style { + MemoryStyle::Dynamic { offset_guard_size } => assert_eq!(offset_guard_size, 256), + s => panic!("Unexpected memory style: {:?}", s), + } + + // Small maximum + let requested = MemoryType::new(3, Some(16), true); + let style = tunables.memory_style(&requested); + match style { + MemoryStyle::Static { bound, offset_guard_size } => { + assert_eq!(bound, Pages(2048)); + assert_eq!(offset_guard_size, 128); + } + s => panic!("Unexpected memory style: {:?}", s), + } + } +} diff --git a/runtime/unc-vm/test-api/src/sys/types.rs b/runtime/unc-vm/test-api/src/sys/types.rs new file mode 100644 index 000000000..aa2b11bf2 --- /dev/null +++ b/runtime/unc-vm/test-api/src/sys/types.rs @@ -0,0 +1,95 @@ +use super::externals::Function; +use super::store::{Store, StoreObject}; +use unc_vm_engine::RuntimeError; +use unc_vm_types::Value; +pub use unc_vm_types::{ + FunctionType, GlobalType, MemoryType, Mutability, TableType, Type as ValType, +}; +use unc_vm_vm::VMFuncRef; + +/// WebAssembly computations manipulate values of basic value types: +/// * Integers (32 or 64 bit width) +/// * Floating-point (32 or 64 bit width) +/// * Vectors (128 bits, with 32 or 64 bit lanes) +/// +/// Spec: +pub type Val = Value; + +impl StoreObject for Val { + fn comes_from_same_store(&self, store: &Store) -> bool { + match self { + Self::FuncRef(None) => true, + Self::FuncRef(Some(f)) => Store::same(store, f.store()), + // `ExternRef`s are not tied to specific stores + Self::ExternRef(_) => true, + Self::I32(_) | Self::I64(_) | Self::F32(_) | Self::F64(_) | Self::V128(_) => true, + } + } +} + +impl From for Val { + fn from(val: Function) -> Self { + Self::FuncRef(Some(val)) + } +} + +/// It provides useful functions for converting back and forth +/// from [`Val`] into `FuncRef`. +pub trait ValFuncRef { + fn into_vm_funcref(&self, store: &Store) -> Result; + + unsafe fn from_vm_funcref(item: VMFuncRef, store: &Store) -> Self; + + fn into_table_reference(&self, store: &Store) + -> Result; + + unsafe fn from_table_reference(item: unc_vm_vm::TableElement, store: &Store) -> Self; +} + +impl ValFuncRef for Val { + fn into_vm_funcref(&self, store: &Store) -> Result { + if !self.comes_from_same_store(store) { + return Err(RuntimeError::new("cross-`Store` values are not supported")); + } + Ok(match self { + Self::FuncRef(None) => VMFuncRef::null(), + Self::FuncRef(Some(f)) => f.vm_funcref(), + _ => return Err(RuntimeError::new("val is not func ref")), + }) + } + + /// # Safety + /// + /// The returned `Val` must outlive the containing instance. + unsafe fn from_vm_funcref(func_ref: VMFuncRef, store: &Store) -> Self { + Self::FuncRef(Function::from_vm_funcref(store, func_ref)) + } + + fn into_table_reference( + &self, + store: &Store, + ) -> Result { + if !self.comes_from_same_store(store) { + return Err(RuntimeError::new("cross-`Store` values are not supported")); + } + Ok(match self { + // TODO(reftypes): review this clone + Self::ExternRef(extern_ref) => { + unc_vm_vm::TableElement::ExternRef(extern_ref.clone().into()) + } + Self::FuncRef(None) => unc_vm_vm::TableElement::FuncRef(VMFuncRef::null()), + Self::FuncRef(Some(f)) => unc_vm_vm::TableElement::FuncRef(f.vm_funcref()), + _ => return Err(RuntimeError::new("val is not reference")), + }) + } + + /// # Safety + /// + /// The returned `Val` may not outlive the containing instance. + unsafe fn from_table_reference(item: unc_vm_vm::TableElement, store: &Store) -> Self { + match item { + unc_vm_vm::TableElement::FuncRef(f) => Self::from_vm_funcref(f, store), + unc_vm_vm::TableElement::ExternRef(extern_ref) => Self::ExternRef(extern_ref.into()), + } + } +} diff --git a/runtime/unc-vm/test-generator/Cargo.toml b/runtime/unc-vm/test-generator/Cargo.toml new file mode 100644 index 000000000..4f9e5c22a --- /dev/null +++ b/runtime/unc-vm/test-generator/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "unc-vm-test-generator" +version.workspace = true +edition = "2021" +authors = ["Wasmer Engineering Team ", "Hello Inc "] +publish = false +license = "Apache-2.0 WITH LLVM-exception" + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +target-lexicon.workspace = true + +[features] +test-dylib = [] +test-universal = [] diff --git a/runtime/unc-vm/test-generator/src/lib.rs b/runtime/unc-vm/test-generator/src/lib.rs new file mode 100644 index 000000000..bf69fa6bf --- /dev/null +++ b/runtime/unc-vm/test-generator/src/lib.rs @@ -0,0 +1,95 @@ +//! Build library to generate a program which runs all the testsuites. +//! +//! By generating a separate `#[test]` test for each file, we allow cargo test +//! to automatically run the files in parallel. +//! +//! > This program is inspired/forked from: +//! > https://github.com/bytecodealliance/wasmtime/blob/master/build.rs +mod processors; + +pub use crate::processors::{emscripten_processor, wasi_processor, wast_processor}; +use anyhow::Context; +use std::fmt::Write; +use std::path::{Path, PathBuf}; + +pub struct Testsuite { + pub buffer: String, + pub path: Vec, +} + +#[derive(PartialEq, Eq, PartialOrd, Ord)] +pub struct Test { + pub name: String, + pub body: String, +} + +pub fn test_directory_module( + out: &mut Testsuite, + path: impl AsRef, + processor: impl Fn(&mut Testsuite, PathBuf) -> Option, +) -> anyhow::Result { + let path = path.as_ref(); + let testsuite = &extract_name(path); + with_test_module(out, testsuite, |out| test_directory(out, path, processor)) +} + +fn write_test(out: &mut Testsuite, testname: &str, body: &str) -> anyhow::Result<()> { + writeln!(out.buffer, "#[compiler_test({})]", out.path[..out.path.len() - 1].join("::"))?; + writeln!(out.buffer, "fn r#{}(config: crate::Config) -> anyhow::Result<()> {{", &testname)?; + writeln!(out.buffer, "{}", body)?; + writeln!(out.buffer, "}}")?; + writeln!(out.buffer)?; + Ok(()) +} + +pub fn test_directory( + out: &mut Testsuite, + path: impl AsRef, + processor: impl Fn(&mut Testsuite, PathBuf) -> Option, +) -> anyhow::Result { + let path = path.as_ref(); + let mut dir_entries: Vec<_> = path + .read_dir() + .context(format!("failed to read {:?}", path))? + .map(|r| r.expect("reading testsuite directory entry")) + .filter_map(|dir_entry| processor(out, dir_entry.path())) + .collect(); + + dir_entries.sort(); + + for Test { name: testname, body } in dir_entries.iter() { + out.path.push(testname.to_string()); + write_test(out, &testname, &body).unwrap(); + out.path.pop().unwrap(); + } + + Ok(dir_entries.len()) +} + +/// Extract a valid Rust identifier from the stem of a path. +pub fn extract_name(path: impl AsRef) -> String { + path.as_ref() + .file_stem() + .expect("filename should have a stem") + .to_str() + .expect("filename should be representable as a string") + .replace("-", "_") + .replace("/", "_") +} + +pub fn with_test_module( + out: &mut Testsuite, + testsuite: &str, + f: impl FnOnce(&mut Testsuite) -> anyhow::Result, +) -> anyhow::Result { + out.path.push(testsuite.to_string()); + out.buffer.push_str("mod "); + out.buffer.push_str(testsuite); + out.buffer.push_str(" {\n"); + + let result = f(out)?; + + out.buffer.push_str("}\n"); + out.path.pop().unwrap(); + Ok(result) +} diff --git a/runtime/unc-vm/test-generator/src/processors.rs b/runtime/unc-vm/test-generator/src/processors.rs new file mode 100644 index 000000000..020c5a47f --- /dev/null +++ b/runtime/unc-vm/test-generator/src/processors.rs @@ -0,0 +1,86 @@ +//! Here we define the processors usable for each test genrator +use crate::{extract_name, Test, Testsuite}; +use std::path::PathBuf; + +/// Given a Testsuite and a path, process the path in case is a wast +/// file. +pub fn wast_processor(_out: &mut Testsuite, p: PathBuf) -> Option { + let ext = p.extension()?; + // Only look at wast files. + if ext != "wast" { + return None; + } + + // Ignore files starting with `.`, which could be editor temporary files + if p.file_stem()?.to_str()?.starts_with('.') { + return None; + } + + let testname = extract_name(&p); + + // The implementation of `run_wast` lives in /tests/spectest.rs + let body = format!("crate::run_wast(config, r#\"{}\"#)", p.display()); + + Some(Test { name: testname, body }) +} + +/// Given a Testsuite and a path, process the path in case is a Emscripten +/// wasm file. +pub fn emscripten_processor(_out: &mut Testsuite, p: PathBuf) -> Option { + let ext = p.extension()?; + // Only look at wast files. + if ext != "wasm" { + return None; + } + + let outfile = { + let mut out_ext = p.clone(); + out_ext.set_extension("out"); + if out_ext.exists() { + out_ext + } else { + return None; + } + }; + + let testname = extract_name(&p); + + // The implementation of `run_emscripten` lives in /tests/emtest.rs + let body = format!( + "crate::emscripten::run_emscripten(config, r#\"{}\"#, r#\"{}\"#)", + p.display(), + outfile.display() + ); + + Some(Test { name: testname, body }) +} + +/// Given a Testsuite and a path, process the path in case is a WASI +/// wasm file. +pub fn wasi_processor( + _out: &mut Testsuite, + p: PathBuf, + wasi_filesystem_kind: &str, +) -> Option { + let ext = p.extension()?; + // Only look at wast files. + if ext != "wast" { + return None; + } + + let wasm_dir = { + let mut inner = p.clone(); + inner.pop(); + inner + }; + let testname = extract_name(&p); + + let body = format!( + "crate::run_wasi(config, r#\"{}\"#, \"{}\", crate::{})", + p.display(), + wasm_dir.display(), + wasi_filesystem_kind, + ); + + Some(Test { name: testname, body }) +} diff --git a/runtime/unc-vm/tests/compilers/compilation.rs b/runtime/unc-vm/tests/compilers/compilation.rs new file mode 100644 index 000000000..d28a6e7a4 --- /dev/null +++ b/runtime/unc-vm/tests/compilers/compilation.rs @@ -0,0 +1,108 @@ +use std::sync::Arc; + +use unc_vm_compiler::CompileError; +use unc_vm_engine::universal::{LimitedMemoryPool, Universal}; +use unc_vm_test_api::*; +use unc_vm_vm::Artifact; + +fn slow_to_compile_contract(n_fns: usize, n_locals: usize) -> Vec { + let fns = format!("(func (local {}))\n", "i32 ".repeat(n_locals)).repeat(n_fns); + let wat = format!(r#"(module {} (func (export "main")))"#, fns); + wat2wasm(wat.as_bytes()).unwrap().to_vec() +} + +fn compile_uncached<'a>( + store: &'a Store, + engine: &'a UniversalEngine, + code: &'a [u8], + time: bool, +) -> Result { + use std::time::Instant; + let now = Instant::now(); + engine.validate(code)?; + let validate = now.elapsed().as_millis(); + let now = Instant::now(); + let res = engine.compile_universal(code, store.tunables()); + let compile = now.elapsed().as_millis(); + if time { + println!("validate {}ms compile {}ms", validate, compile); + } + res +} + +#[test] +#[ignore] +fn compilation_test() { + let compiler = Singlepass::default(); + let engine = Arc::new(Universal::new(compiler).engine()); + let store = Store::new(Arc::clone(&engine)); + for factor in 1..1000 { + let code = slow_to_compile_contract(3, 25 * factor); + match compile_uncached(&store, &engine, &code, false) { + Ok(art) => { + let serialized = art.serialize().unwrap(); + println!("{}: artifact is compiled, size is {}", factor, serialized.len()); + } + Err(err) => { + println!("err is {:?}", err); + } + } + } +} + +/* +Code to create perf map. + +fn write_perf_profiler_map(functions: &Vec) -> Result<(), Box>{ + let pid = process::id(); + let filename = format!("/tmp/perf-{}.map", pid); + let mut file = File::create(filename).expect("Unable to create file"); + for f in functions { + file.write_fmt(format_args!("{:x} {:x} {}\n", f.address, f.size, f.name))?; + } + Ok(()) +} +*/ + +#[test] +fn profiling() { + let wat = r#" + (import "env" "impf" (func)) + (func $f0) + (func (export "f1")) + (func (export "f2")) + (func (export "f3")) + "#; + let wasm = wat2wasm(wat.as_bytes()).unwrap(); + let compiler = Singlepass::default(); + let pool = LimitedMemoryPool::new(1, 0x10000).unwrap(); + let engine = Arc::new(Universal::new(compiler).code_memory_pool(pool).engine()); + let store = Store::new(Arc::clone(&engine)); + match compile_uncached(&store, &engine, &wasm, false) { + Ok(art) => unsafe { + let serialized = art.serialize().unwrap(); + let executable = + unc_vm_engine::universal::UniversalExecutableRef::deserialize(&serialized) + .unwrap(); + let artifact = engine.load_universal_executable_ref(&executable).unwrap(); + let info = artifact + .functions() + .iter() + .filter_map(|(idx, _)| { + let extent = artifact.function_extent(idx)?; + let idx = artifact.import_counts().function_index(idx); + let name = executable.function_name(idx)?; + Some((name, extent)) + }) + .collect::>(); + assert_eq!(4, info.len()); + assert_eq!("f0", info[0].0); + assert_eq!("f1", info[1].0); + assert_eq!("f2", info[2].0); + assert_eq!("f3", info[3].0); + }, + Err(_) => { + assert!(false) + } + } +} diff --git a/runtime/unc-vm/tests/compilers/config.rs b/runtime/unc-vm/tests/compilers/config.rs new file mode 100644 index 000000000..fb618a27a --- /dev/null +++ b/runtime/unc-vm/tests/compilers/config.rs @@ -0,0 +1,72 @@ +use unc_vm_compiler::{CompilerConfig, Features}; +use unc_vm_engine::universal::UniversalEngine; +use unc_vm_test_api::Store; + +#[derive(Clone, Debug, PartialEq)] +pub enum Compiler { + Singlepass, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum Engine { + Universal, +} + +#[derive(Clone)] +pub struct Config { + pub compiler: Compiler, + pub engine: Engine, + pub features: Option, + pub canonicalize_nans: bool, +} + +impl Config { + pub fn new(engine: Engine, compiler: Compiler) -> Self { + Self { compiler, engine, features: None, canonicalize_nans: false } + } + + pub fn set_features(&mut self, features: Features) { + self.features = Some(features); + } + + pub fn set_nan_canonicalization(&mut self, canonicalize_nans: bool) { + self.canonicalize_nans = canonicalize_nans; + } + + pub fn store(&self) -> Store { + let compiler_config = self.compiler_config(self.canonicalize_nans); + let engine = self.engine(compiler_config); + Store::new(engine.into()) + } + + pub fn headless_store(&self) -> Store { + let engine = self.engine_headless(); + Store::new(engine.into()) + } + + pub fn engine(&self, compiler_config: Box) -> UniversalEngine { + let mut engine = unc_vm_engine::universal::Universal::new(compiler_config) + .code_memory_pool( + unc_vm_engine::universal::LimitedMemoryPool::new(128, 16 * 4096).unwrap(), + ); + if let Some(ref features) = self.features { + engine = engine.features(features.clone()) + } + engine.engine() + } + + pub fn engine_headless(&self) -> UniversalEngine { + unc_vm_engine::universal::Universal::headless().engine() + } + + pub fn compiler_config(&self, canonicalize_nans: bool) -> Box { + match &self.compiler { + Compiler::Singlepass => { + let mut compiler = unc_vm_compiler_singlepass::Singlepass::new(); + compiler.canonicalize_nans(canonicalize_nans); + compiler.enable_verifier(); + Box::new(compiler) + } + } + } +} diff --git a/runtime/unc-vm/tests/compilers/deterministic.rs b/runtime/unc-vm/tests/compilers/deterministic.rs new file mode 100644 index 000000000..57e5a8173 --- /dev/null +++ b/runtime/unc-vm/tests/compilers/deterministic.rs @@ -0,0 +1,49 @@ +use anyhow::Result; +use unc_vm_compiler_singlepass::Singlepass; +use unc_vm_engine::universal::{LimitedMemoryPool, Universal}; +use unc_vm_test_api::{wat2wasm, BaseTunables}; + +fn compile_and_compare(wasm: &[u8]) -> Result<()> { + let compiler = Singlepass::default(); + let pool = LimitedMemoryPool::new(1, 0x10000).unwrap(); + let engine = Universal::new(compiler).code_memory_pool(pool).engine(); + let tunables = BaseTunables::for_target(engine.target()); + + // compile for first time + let executable = engine.compile_universal(wasm, &tunables).unwrap(); + let serialized1 = executable.serialize().unwrap(); + + // compile for second time + let executable = engine.compile_universal(wasm, &tunables).unwrap(); + let serialized2 = executable.serialize().unwrap(); + + assert_eq!(serialized1, serialized2); + + Ok(()) +} + +#[test] +fn deterministic_empty() -> Result<()> { + let wasm_bytes = wat2wasm( + br#" + (module) + "#, + )?; + + compile_and_compare(&wasm_bytes) +} + +#[test] +fn deterministic_table() -> Result<()> { + let wasm_bytes = wat2wasm( + br#" +(module + (table 2 funcref) + (func $f1) + (func $f2) + (elem (i32.const 0) $f1 $f2)) +"#, + )?; + + compile_and_compare(&wasm_bytes) +} diff --git a/runtime/unc-vm/tests/compilers/imports.rs b/runtime/unc-vm/tests/compilers/imports.rs new file mode 100644 index 000000000..82465d28c --- /dev/null +++ b/runtime/unc-vm/tests/compilers/imports.rs @@ -0,0 +1,395 @@ +//! Testing the imports with different provided functions. +//! This tests checks that the provided functions (both native and +//! dynamic ones) work properly. + +use anyhow::Result; +use unc_vm_engine::RuntimeError; +use unc_vm_test_api::*; +use std::convert::Infallible; +use std::sync::atomic::AtomicBool; +use std::sync::{ + atomic::{AtomicUsize, Ordering::SeqCst}, + Arc, +}; + +fn get_module(store: &Store) -> Result { + let wat = r#" + (import "host" "0" (func)) + (import "host" "1" (func (param i32) (result i32))) + (import "host" "2" (func (param i32) (param i64))) + (import "host" "3" (func (param i32 i64 i32 f32 f64))) + (memory $mem 1) + (export "memory" (memory $mem)) + + (func $foo + call 0 + i32.const 0 + call 1 + i32.const 1 + i32.add + i64.const 3 + call 2 + + i32.const 100 + i64.const 200 + i32.const 300 + f32.const 400 + f64.const 500 + call 3 + ) + (start $foo) + "#; + + let module = Module::new(&store, &wat)?; + Ok(module) +} + +#[compiler_test(imports)] +#[serial_test::serial(dynamic_function)] +fn dynamic_function(config: crate::Config) -> Result<()> { + let store = config.store(); + let module = get_module(&store)?; + static HITS: AtomicUsize = AtomicUsize::new(0); + Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! { + "host" => { + "0" => Function::new(&store, FunctionType::new(vec![], vec![]), |_values| { + assert_eq!(HITS.fetch_add(1, SeqCst), 0); + Ok(vec![]) + }), + "1" => Function::new(&store, FunctionType::new(vec![ValType::I32], vec![ValType::I32]), |values| { + assert_eq!(values[0], Value::I32(0)); + assert_eq!(HITS.fetch_add(1, SeqCst), 1); + Ok(vec![Value::I32(1)]) + }), + "2" => Function::new(&store, FunctionType::new(vec![ValType::I32, ValType::I64], vec![]), |values| { + assert_eq!(values[0], Value::I32(2)); + assert_eq!(values[1], Value::I64(3)); + assert_eq!(HITS.fetch_add(1, SeqCst), 2); + Ok(vec![]) + }), + "3" => Function::new(&store, FunctionType::new(vec![ValType::I32, ValType::I64, ValType::I32, ValType::F32, ValType::F64], vec![]), |values| { + assert_eq!(values[0], Value::I32(100)); + assert_eq!(values[1], Value::I64(200)); + assert_eq!(values[2], Value::I32(300)); + assert_eq!(values[3], Value::F32(400.0)); + assert_eq!(values[4], Value::F64(500.0)); + assert_eq!(HITS.fetch_add(1, SeqCst), 3); + Ok(vec![]) + }), + }, + }, + )?; + assert_eq!(HITS.swap(0, SeqCst), 4); + Ok(()) +} + +#[compiler_test(imports)] +fn dynamic_function_with_env(config: crate::Config) -> Result<()> { + let store = config.store(); + let module = get_module(&store)?; + + #[derive(Clone)] + struct Env { + counter: Arc, + } + impl WasmerEnv for Env {} + + impl std::ops::Deref for Env { + type Target = Arc; + fn deref(&self) -> &Self::Target { + &self.counter + } + } + + let env: Env = Env { counter: Arc::new(AtomicUsize::new(0)) }; + Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! { + "host" => { + "0" => Function::new_with_env(&store, FunctionType::new(vec![], vec![]), env.clone(), |env, _values| { + assert_eq!(env.fetch_add(1, SeqCst), 0); + Ok(vec![]) + }), + "1" => Function::new_with_env(&store, FunctionType::new(vec![ValType::I32], vec![ValType::I32]), env.clone(), |env, values| { + assert_eq!(values[0], Value::I32(0)); + assert_eq!(env.fetch_add(1, SeqCst), 1); + Ok(vec![Value::I32(1)]) + }), + "2" => Function::new_with_env(&store, FunctionType::new(vec![ValType::I32, ValType::I64], vec![]), env.clone(), |env, values| { + assert_eq!(values[0], Value::I32(2)); + assert_eq!(values[1], Value::I64(3)); + assert_eq!(env.fetch_add(1, SeqCst), 2); + Ok(vec![]) + }), + "3" => Function::new_with_env(&store, FunctionType::new(vec![ValType::I32, ValType::I64, ValType::I32, ValType::F32, ValType::F64], vec![]), env.clone(), |env, values| { + assert_eq!(values[0], Value::I32(100)); + assert_eq!(values[1], Value::I64(200)); + assert_eq!(values[2], Value::I32(300)); + assert_eq!(values[3], Value::F32(400.0)); + assert_eq!(values[4], Value::F64(500.0)); + assert_eq!(env.fetch_add(1, SeqCst), 3); + Ok(vec![]) + }), + }, + }, + )?; + assert_eq!(env.load(SeqCst), 4); + Ok(()) +} + +#[compiler_test(imports)] +#[serial_test::serial(static_function)] +fn static_function(config: crate::Config) -> Result<()> { + let store = config.store(); + let module = get_module(&store)?; + + static HITS: AtomicUsize = AtomicUsize::new(0); + Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! { + "host" => { + "0" => Function::new_native(&store, || { + assert_eq!(HITS.fetch_add(1, SeqCst), 0); + }), + "1" => Function::new_native(&store, |x: i32| -> i32 { + assert_eq!(x, 0); + assert_eq!(HITS.fetch_add(1, SeqCst), 1); + 1 + }), + "2" => Function::new_native(&store, |x: i32, y: i64| { + assert_eq!(x, 2); + assert_eq!(y, 3); + assert_eq!(HITS.fetch_add(1, SeqCst), 2); + }), + "3" => Function::new_native(&store, |a: i32, b: i64, c: i32, d: f32, e: f64| { + assert_eq!(a, 100); + assert_eq!(b, 200); + assert_eq!(c, 300); + assert_eq!(d, 400.0); + assert_eq!(e, 500.0); + assert_eq!(HITS.fetch_add(1, SeqCst), 3); + }), + }, + }, + )?; + assert_eq!(HITS.swap(0, SeqCst), 4); + Ok(()) +} + +#[compiler_test(imports)] +#[serial_test::serial(static_function_with_results)] +fn static_function_with_results(config: crate::Config) -> Result<()> { + let store = config.store(); + let module = get_module(&store)?; + + static HITS: AtomicUsize = AtomicUsize::new(0); + Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! { + "host" => { + "0" => Function::new_native(&store, || { + assert_eq!(HITS.fetch_add(1, SeqCst), 0); + }), + "1" => Function::new_native(&store, |x: i32| -> Result { + assert_eq!(x, 0); + assert_eq!(HITS.fetch_add(1, SeqCst), 1); + Ok(1) + }), + "2" => Function::new_native(&store, |x: i32, y: i64| { + assert_eq!(x, 2); + assert_eq!(y, 3); + assert_eq!(HITS.fetch_add(1, SeqCst), 2); + }), + "3" => Function::new_native(&store, |a: i32, b: i64, c: i32, d: f32, e: f64| { + assert_eq!(a, 100); + assert_eq!(b, 200); + assert_eq!(c, 300); + assert_eq!(d, 400.0); + assert_eq!(e, 500.0); + assert_eq!(HITS.fetch_add(1, SeqCst), 3); + }), + }, + }, + )?; + assert_eq!(HITS.swap(0, SeqCst), 4); + Ok(()) +} + +#[compiler_test(imports)] +fn static_function_with_env(config: crate::Config) -> Result<()> { + let store = config.store(); + let module = get_module(&store)?; + + #[derive(Clone)] + struct Env(Arc); + impl WasmerEnv for Env {} + + impl std::ops::Deref for Env { + type Target = Arc; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + let env: Env = Env(Arc::new(AtomicUsize::new(0))); + Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! { + "host" => { + "0" => Function::new_native_with_env(&store, env.clone(), |env: &Env| { + assert_eq!(env.fetch_add(1, SeqCst), 0); + }), + "1" => Function::new_native_with_env(&store, env.clone(), |env: &Env, x: i32| -> i32 { + assert_eq!(x, 0); + assert_eq!(env.fetch_add(1, SeqCst), 1); + 1 + }), + "2" => Function::new_native_with_env(&store, env.clone(), |env: &Env, x: i32, y: i64| { + assert_eq!(x, 2); + assert_eq!(y, 3); + assert_eq!(env.fetch_add(1, SeqCst), 2); + }), + "3" => Function::new_native_with_env(&store, env.clone(), |env: &Env, a: i32, b: i64, c: i32, d: f32, e: f64| { + assert_eq!(a, 100); + assert_eq!(b, 200); + assert_eq!(c, 300); + assert_eq!(d, 400.0); + assert_eq!(e, 500.0); + assert_eq!(env.fetch_add(1, SeqCst), 3); + }), + }, + }, + )?; + assert_eq!(env.load(SeqCst), 4); + Ok(()) +} + +#[compiler_test(imports)] +fn static_function_that_fails(config: crate::Config) -> Result<()> { + let store = config.store(); + let wat = r#" + (import "host" "0" (func)) + + (func $foo + call 0 + ) + (start $foo) + "#; + + let module = Module::new(&store, &wat)?; + + let result = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! { + "host" => { + "0" => Function::new_native(&store, || -> Result { + Err(RuntimeError::new("oops")) + }), + }, + }, + ); + + assert!(result.is_err()); + + match result { + Err(InstantiationError::Start(runtime_error)) => { + assert_eq!(runtime_error.message(), "oops") + } + _ => assert!(false), + } + + Ok(()) +} + +fn get_module2(store: &Store) -> Result { + let wat = r#" + (import "host" "fn" (func)) + (memory $mem 1) + (export "memory" (memory $mem)) + (export "main" (func $main)) + (func $main (param) (result) + (call 0)) + "#; + + let module = Module::new(&store, &wat)?; + Ok(module) +} + +#[compiler_test(imports)] +fn dynamic_function_with_env_wasmer_env_init_works(config: crate::Config) -> Result<()> { + let store = config.store(); + let module = get_module2(&store)?; + + #[allow(dead_code)] + #[derive(Clone)] + struct Env { + memory: Memory, + } + impl WasmerEnv for Env {} + + let env: Env = Env { + memory: Memory::new( + &store, + MemoryType { minimum: 0.into(), maximum: None, shared: false }, + )?, + }; + let function_fn = + Function::new_with_env(&store, FunctionType::new(vec![], vec![]), env, |env, _values| { + Ok(vec![]) + }); + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! { + "host" => { + "fn" => function_fn, + }, + }, + )?; + let f: NativeFunc<(), ()> = instance.get_native_function("main")?; + f.call()?; + Ok(()) +} + +static REGRESSION_IMPORT_TRAMPOLINES: &str = r#"(module + (type (;0;) (func)) + (type (;1;) (func (param i32))) + (import "env" "panic" (func (;0;) (type 0))) + (import "env" "gas" (func (;1;) (type 1))) + (export "panic" (func 0)) + (export "gas" (func 1)) +)"#; + +#[compiler_test(imports)] +fn regression_import_trampolines(config: crate::Config) -> Result<()> { + let store = config.store(); + let module = Module::new(&store, ®RESSION_IMPORT_TRAMPOLINES)?; + let panic = Function::new_native(&store, || ()); + static GAS_CALLED: AtomicBool = AtomicBool::new(false); + let gas = Function::new_native(&store, |p: i32| { + GAS_CALLED.store(true, SeqCst); + assert_eq!(p, 42) + }); + let imports = imports! { + "env" => { + "panic" => panic, + "gas" => gas, + } + }; + let instance = + Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000000), &imports)?; + let panic = instance.lookup_function("panic").unwrap(); + panic.call(&[])?; + let gas = instance.lookup_function("gas").unwrap(); + gas.call(&[Value::I32(42)])?; + assert_eq!(GAS_CALLED.load(SeqCst), true); + Ok(()) +} diff --git a/runtime/unc-vm/tests/compilers/issues.rs b/runtime/unc-vm/tests/compilers/issues.rs new file mode 100644 index 000000000..6b2d03b2f --- /dev/null +++ b/runtime/unc-vm/tests/compilers/issues.rs @@ -0,0 +1,112 @@ +//! This file is mainly to assure specific issues are working well +use anyhow::Result; +use unc_vm_test_api::*; + +#[derive(Clone)] +struct Env { + memory: LazyInit, +} + +impl WasmerEnv for Env {} + +/// Corruption of WasmerEnv when using call indirect. +/// +/// Note: this one is specific to Singlepass, but we want to test in all +/// available compilers. +/// +/// https://github.com/wasmerio/wasmer/issues/2329 +#[compiler_test(issues)] +fn issue_2329(mut config: crate::Config) -> Result<()> { + let store = config.store(); + + fn read_memory(env: &Env, guest_ptr: u32) -> u32 { + dbg!(env.memory.get_ref()); + dbg!(guest_ptr); + 0 + } + + let wat = r#" + (module + (type (;0;) (func (param i32) (result i32))) + (type (;1;) (func)) + (type (;2;) (func (param i32 i32) (result i32))) + (import "env" "__read_memory" (func $__read_memory (type 0))) + (func $read_memory (type 1) + (drop + (call $_ZN5other8dispatch17h053cb34ef5d0d7b0E + (i32.const 1) + (i32.const 2))) + (drop + (call $__read_memory + (i32.const 1)))) + (func $_ZN5other8dispatch17h053cb34ef5d0d7b0E (type 2) (param i32 i32) (result i32) + (call_indirect (type 0) + (local.get 1) + (local.get 0))) + (table (;0;) 2 2 funcref) + (memory (;0;) 16) + (global (;0;) (mut i32) (i32.const 1048576)) + (global (;1;) i32 (i32.const 1048576)) + (global (;2;) i32 (i32.const 1048576)) + (export "memory" (memory 0)) + (export "read_memory" (func $read_memory)) + (export "__data_end" (global 1)) + (export "__heap_base" (global 2)) + (elem (;0;) (i32.const 1) func $__read_memory)) + "#; + let module = Module::new(&store, wat)?; + let env = Env { memory: LazyInit::new() }; + let imports: ImportObject = imports! { + "env" => { + "__read_memory" => Function::new_native_with_env( + &store, + env, + read_memory + ), + } + }; + let instance = + Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000000), &imports)?; + instance.lookup_function("read_memory").unwrap().call(&[])?; + Ok(()) +} + +/// Exhaustion of GPRs when calling a function with many floating point arguments +/// +/// Note: this one is specific to Singlepass, but we want to test in all +/// available compilers. +#[compiler_test(issues)] +fn regression_gpr_exhaustion_for_calls(mut config: crate::Config) -> Result<()> { + let store = config.store(); + let wat = r#" + (module + (type (;0;) (func (param f64) (result i32))) + (type (;1;) (func (param f64 f64 f64 f64 f64 f64))) + (func (;0;) (type 0) (param f64) (result i32) + local.get 0 + local.get 0 + local.get 0 + local.get 0 + f64.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + f64.const 0 + i32.const 0 + call_indirect (type 0) + call_indirect (type 1) + drop + drop + drop + drop + i32.const 0) + (table (;0;) 1 1 funcref)) + "#; + let module = Module::new(&store, wat)?; + let imports: ImportObject = imports! {}; + let instance = + Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000000), &imports)?; + Ok(()) +} diff --git a/runtime/unc-vm/tests/compilers/main.rs b/runtime/unc-vm/tests/compilers/main.rs new file mode 100644 index 000000000..c4eb337ae --- /dev/null +++ b/runtime/unc-vm/tests/compilers/main.rs @@ -0,0 +1,20 @@ +#![cfg(target_arch = "x86_64")] +//! This test suite does all the tests that involve any compiler +//! implementation, such as: singlepass. +#[macro_use] +extern crate unc_vm_compiler_test_derive; + +mod config; +mod deterministic; +mod imports; +mod issues; +// mod multi_value_imports; +mod compilation; +mod native_functions; +mod serialize; +mod stack_limiter; +mod traps; +mod wast; + +pub use crate::config::{Compiler, Config, Engine}; +pub use crate::wast::run_wast; diff --git a/runtime/unc-vm/tests/compilers/multi_value_imports.rs b/runtime/unc-vm/tests/compilers/multi_value_imports.rs new file mode 100644 index 000000000..dcb8514c9 --- /dev/null +++ b/runtime/unc-vm/tests/compilers/multi_value_imports.rs @@ -0,0 +1,205 @@ +//! Testing the imports with different provided functions. +//! This tests checks that the provided functions (both native and +//! dynamic ones) work properly. + +use unc_vm_test_api::*; + +macro_rules! mvr_test { + ($test_name:ident, $( $result_type:ty ),* ) => { + mod $test_name { + use unc_vm_test_api::*; + + fn get_module(store: &Store) -> anyhow::Result { + let wat: String = r#" + (type $type (func (param i32) (result +"#.to_string() + + &stringify!( $( $result_type ),* ).replace(",", "").replace("(", "").replace(")", "") + &r#"))) + (import "host" "callback_fn" (func $callback_fn (type $type))) + (func (export "test_call") (type $type) + get_local 0 + call $callback_fn) + (func (export "test_call_indirect") (type $type) + (i32.const 1) + (call_indirect (type $type) (i32.const 0)) + ) + (table funcref + (elem + $callback_fn + ) + ) +"#.to_string(); + Ok(wasmer::Module::new(&store, &wat)?) + } + + fn callback_fn(n: i32) -> ( $( $result_type ),* ) { + ( $( <$result_type>::expected_value(n) ),* ) + } + + #[compiler_test(multi_value_imports)] + fn native(config: crate::Config) -> anyhow::Result<()> { + let store = config.store(); + let module = get_module(&store)?; + let instance = wasmer::Instance::new_with_config( + &module, InstanceConfig::with_stack_limit(1000000), + &wasmer::imports! { + "host" => { + "callback_fn" => wasmer::Function::new_native(&store, callback_fn) + } + } + )?; + let expected_value = vec![ $( <$result_type>::expected_val(1) ),* ].into_boxed_slice(); + assert_eq!(instance.exports.get_function("test_call")?.call(&[wasmer::Val::I32(1)])?, + expected_value); + assert_eq!(instance.exports.get_function("test_call_indirect")?.call(&[wasmer::Val::I32(1)])?, + expected_value); + Ok(()) + } + + fn dynamic_callback_fn(values: &[wasmer::Value]) -> anyhow::Result, wasmer::RuntimeError> { + assert_eq!(values[0], wasmer::Value::I32(1)); + Ok(vec![ $( <$result_type>::expected_val(1) ),* ]) + } + + #[compiler_test(multi_value_imports)] + fn dynamic(config: crate::Config) -> anyhow::Result<()> { + let store = config.store(); + let module = get_module(&store)?; + let callback_fn = wasmer::Function::new(&store, &wasmer::FunctionType::new(vec![wasmer::ValType::I32], vec![ $( <$result_type>::expected_valtype() ),* ]), dynamic_callback_fn); + let instance = wasmer::Instance::new_with_config( + &module, InstanceConfig::with_stack_limit(1000000), + &wasmer::imports! { + "host" => { + "callback_fn" => callback_fn + } + } + )?; + let expected_value = vec![ $( <$result_type>::expected_val(1) ),* ].into_boxed_slice(); + assert_eq!(instance.exports.get_function("test_call")?.call(&[wasmer::Val::I32(1)])?, + expected_value); + assert_eq!(instance.exports.get_function("test_call_indirect")?.call(&[wasmer::Val::I32(1)])?, + expected_value); + Ok(()) + } + } + } +} + +trait ExpectedExpr { + fn expected_value(n: i32) -> Self; + fn expected_val(n: i32) -> wasmer::Val; + fn expected_valtype() -> wasmer::ValType; +} +impl ExpectedExpr for i32 { + fn expected_value(n: i32) -> i32 { + n + 1 + } + fn expected_val(n: i32) -> wasmer::Val { + wasmer::Val::I32(Self::expected_value(n)) + } + fn expected_valtype() -> wasmer::ValType { + wasmer::ValType::I32 + } +} +impl ExpectedExpr for i64 { + fn expected_value(n: i32) -> i64 { + n as i64 + 2i64 + } + fn expected_val(n: i32) -> wasmer::Val { + wasmer::Val::I64(Self::expected_value(n)) + } + fn expected_valtype() -> wasmer::ValType { + wasmer::ValType::I64 + } +} +impl ExpectedExpr for f32 { + fn expected_value(n: i32) -> f32 { + n as f32 * 0.1 + } + fn expected_val(n: i32) -> wasmer::Val { + wasmer::Val::F32(Self::expected_value(n)) + } + fn expected_valtype() -> wasmer::ValType { + wasmer::ValType::F32 + } +} +impl ExpectedExpr for f64 { + fn expected_value(n: i32) -> f64 { + n as f64 * 0.12 + } + fn expected_val(n: i32) -> wasmer::Val { + wasmer::Val::F64(Self::expected_value(n)) + } + fn expected_valtype() -> wasmer::ValType { + wasmer::ValType::F64 + } +} + +mvr_test!(test_mvr_i32_i32, i32, i32); +mvr_test!(test_mvr_i32_f32, i32, f32); +mvr_test!(test_mvr_f32_i32, f32, i32); +mvr_test!(test_mvr_f32_f32, f32, f32); + +mvr_test!(test_mvr_i64_i32, i64, i32); +mvr_test!(test_mvr_i64_f32, i64, f32); +mvr_test!(test_mvr_f64_i32, f64, i32); +mvr_test!(test_mvr_f64_f32, f64, f32); + +mvr_test!(test_mvr_i32_i64, i32, i64); +mvr_test!(test_mvr_f32_i64, f32, i64); +mvr_test!(test_mvr_i32_f64, i32, f64); +mvr_test!(test_mvr_f32_f64, f32, f64); + +mvr_test!(test_mvr_i32_i32_i32, i32, i32, i32); +mvr_test!(test_mvr_i32_i32_f32, i32, i32, f32); +mvr_test!(test_mvr_i32_f32_i32, i32, f32, i32); +mvr_test!(test_mvr_i32_f32_f32, i32, f32, f32); +mvr_test!(test_mvr_f32_i32_i32, f32, i32, i32); +mvr_test!(test_mvr_f32_i32_f32, f32, i32, f32); +mvr_test!(test_mvr_f32_f32_i32, f32, f32, i32); +mvr_test!(test_mvr_f32_f32_f32, f32, f32, f32); + +mvr_test!(test_mvr_i32_i32_i64, i32, i32, i64); +mvr_test!(test_mvr_i32_f32_i64, i32, f32, i64); +mvr_test!(test_mvr_f32_i32_i64, f32, i32, i64); +mvr_test!(test_mvr_f32_f32_i64, f32, f32, i64); +mvr_test!(test_mvr_i32_i32_f64, i32, i32, f64); +mvr_test!(test_mvr_i32_f32_f64, i32, f32, f64); +mvr_test!(test_mvr_f32_i32_f64, f32, i32, f64); +mvr_test!(test_mvr_f32_f32_f64, f32, f32, f64); + +mvr_test!(test_mvr_i32_i64_i32, i32, i64, i32); +mvr_test!(test_mvr_i32_i64_f32, i32, i64, f32); +mvr_test!(test_mvr_f32_i64_i32, f32, i64, i32); +mvr_test!(test_mvr_f32_i64_f32, f32, i64, f32); +mvr_test!(test_mvr_i32_f64_i32, i32, f64, i32); +mvr_test!(test_mvr_i32_f64_f32, i32, f64, f32); +mvr_test!(test_mvr_f32_f64_i32, f32, f64, i32); +mvr_test!(test_mvr_f32_f64_f32, f32, f64, f32); + +mvr_test!(test_mvr_i64_i32_i32, i64, i32, i32); +mvr_test!(test_mvr_i64_i32_f32, i64, i32, f32); +mvr_test!(test_mvr_i64_f32_i32, i64, f32, i32); +mvr_test!(test_mvr_i64_f32_f32, i64, f32, f32); +mvr_test!(test_mvr_f64_i32_i32, f64, i32, i32); +mvr_test!(test_mvr_f64_i32_f32, f64, i32, f32); +mvr_test!(test_mvr_f64_f32_i32, f64, f32, i32); +mvr_test!(test_mvr_f64_f32_f32, f64, f32, f32); + +mvr_test!(test_mvr_i32_i32_i32_i32, i32, i32, i32, i32); +mvr_test!(test_mvr_i32_i32_i32_f32, i32, i32, i32, f32); +mvr_test!(test_mvr_i32_i32_f32_i32, i32, i32, f32, i32); +mvr_test!(test_mvr_i32_i32_f32_f32, i32, i32, f32, f32); +mvr_test!(test_mvr_i32_f32_i32_i32, i32, f32, i32, i32); +mvr_test!(test_mvr_i32_f32_i32_f32, i32, f32, i32, f32); +mvr_test!(test_mvr_i32_f32_f32_i32, i32, f32, f32, i32); +mvr_test!(test_mvr_i32_f32_f32_f32, i32, f32, f32, f32); +mvr_test!(test_mvr_f32_i32_i32_i32, f32, i32, i32, i32); +mvr_test!(test_mvr_f32_i32_i32_f32, f32, i32, i32, f32); +mvr_test!(test_mvr_f32_i32_f32_i32, f32, i32, f32, i32); +mvr_test!(test_mvr_f32_i32_f32_f32, f32, i32, f32, f32); +mvr_test!(test_mvr_f32_f32_i32_i32, f32, f32, i32, i32); +mvr_test!(test_mvr_f32_f32_i32_f32, f32, f32, i32, f32); +mvr_test!(test_mvr_f32_f32_f32_i32, f32, f32, f32, i32); +mvr_test!(test_mvr_f32_f32_f32_f32, f32, f32, f32, f32); + +mvr_test!(test_mvr_i32_i32_i32_i32_i32, i32, i32, i32, i32, i32); diff --git a/runtime/unc-vm/tests/compilers/native_functions.rs b/runtime/unc-vm/tests/compilers/native_functions.rs new file mode 100644 index 000000000..42531574c --- /dev/null +++ b/runtime/unc-vm/tests/compilers/native_functions.rs @@ -0,0 +1,456 @@ +use anyhow::Result; +use unc_vm_engine::RuntimeError; +use unc_vm_test_api::*; +use std::convert::Infallible; +use std::sync::{Arc, Mutex}; + +fn long_f(a: u32, b: u32, c: u32, d: u32, e: u32, f: u16, g: u64, h: u64, i: u16, j: u32) -> u64 { + j as u64 + + i as u64 * 10 + + h * 100 + + g * 1000 + + f as u64 * 10000 + + e as u64 * 100000 + + d as u64 * 1000000 + + c as u64 * 10000000 + + b as u64 * 100000000 + + a as u64 * 1000000000 +} + +fn long_f_dynamic(values: &[Value]) -> Result, RuntimeError> { + Ok(vec![Value::I64( + values[9].unwrap_i32() as i64 + + values[8].unwrap_i32() as i64 * 10 + + values[7].unwrap_i64() * 100 + + values[6].unwrap_i64() * 1000 + + values[5].unwrap_i32() as i64 * 10000 + + values[4].unwrap_i32() as i64 * 100000 + + values[3].unwrap_i32() as i64 * 1000000 + + values[2].unwrap_i32() as i64 * 10000000 + + values[1].unwrap_i32() as i64 * 100000000 + + values[0].unwrap_i32() as i64 * 1000000000, + )]) +} + +#[compiler_test(native_functions)] +fn native_function_works_for_wasm(config: crate::Config) -> anyhow::Result<()> { + let store = config.store(); + let wat = r#"(module + (func $multiply (import "env" "multiply") (param i32 i32) (result i32)) + (func (export "add") (param i32 i32) (result i32) + (i32.add (local.get 0) + (local.get 1))) + (func (export "double_then_add") (param i32 i32) (result i32) + (i32.add (call $multiply (local.get 0) (i32.const 2)) + (call $multiply (local.get 1) (i32.const 2)))) +)"#; + let module = Module::new(&store, wat).unwrap(); + + let import_object = imports! { + "env" => { + "multiply" => Function::new_native(&store, |a: i32, b: i32| a * b), + }, + }; + + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &import_object, + )?; + + { + let f: NativeFunc<(i32, i32), i32> = instance.get_native_function("add")?; + let result = f.call(4, 6)?; + assert_eq!(result, 10); + } + + { + let f: Function = instance.lookup_function("double_then_add").expect("lookup function"); + let result = f.call(&[Val::I32(4), Val::I32(6)])?; + assert_eq!(result[0], Val::I32(20)); + } + + { + let dyn_f: Function = instance.lookup_function("double_then_add").expect("lookup function"); + let f: NativeFunc<(i32, i32), i32> = dyn_f.native().unwrap(); + let result = f.call(4, 6)?; + assert_eq!(result, 20); + } + + Ok(()) +} + +#[should_panic( + expected = "Closures (functions with captured environments) are currently unsupported with native functions. See: https://github.com/wasmerio/wasmer/issues/1840" +)] +#[compiler_test(native_functions)] +fn native_host_function_closure_panics(config: crate::Config) { + let store = config.store(); + let state = 3; + Function::new_native(&store, move |_: i32| { + println!("{}", state); + }); +} + +#[should_panic( + expected = "Closures (functions with captured environments) are currently unsupported with native functions. See: https://github.com/wasmerio/wasmer/issues/1840" +)] +#[compiler_test(native_functions)] +fn native_with_env_host_function_closure_panics(config: crate::Config) { + let store = config.store(); + let state = 3; + let env = 4; + Function::new_native_with_env(&store, env, move |_env: &i32, _: i32| { + println!("{}", state); + }); +} + +#[compiler_test(native_functions)] +fn non_native_functions_and_closures_with_no_env_work(config: crate::Config) -> anyhow::Result<()> { + let store = config.store(); + let wat = r#"(module + (func $multiply1 (import "env" "multiply1") (param i32 i32) (result i32)) + (func $multiply2 (import "env" "multiply2") (param i32 i32) (result i32)) + (func $multiply3 (import "env" "multiply3") (param i32 i32) (result i32)) + (func $multiply4 (import "env" "multiply4") (param i32 i32) (result i32)) + + (func (export "test") (param i32 i32 i32 i32 i32) (result i32) + (call $multiply4 + (call $multiply3 + (call $multiply2 + (call $multiply1 + (local.get 0) + (local.get 1)) + (local.get 2)) + (local.get 3)) + (local.get 4))) +)"#; + let module = Module::new(&store, wat).unwrap(); + + let ty = FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]); + let env = 10; + let captured_by_closure = 20; + let import_object = imports! { + "env" => { + "multiply1" => Function::new(&store, &ty, move |args| { + if let (Value::I32(v1), Value::I32(v2)) = (&args[0], &args[1]) { + Ok(vec![Value::I32(v1 * v2 * captured_by_closure)]) + } else { + panic!("Invalid arguments"); + } + }), + "multiply2" => Function::new_with_env(&store, &ty, env, move |&env, args| { + if let (Value::I32(v1), Value::I32(v2)) = (&args[0], &args[1]) { + Ok(vec![Value::I32(v1 * v2 * captured_by_closure * env)]) + } else { + panic!("Invalid arguments"); + } + }), + "multiply3" => Function::new_native(&store, |arg1: i32, arg2: i32| -> i32 + {arg1 * arg2 }), + "multiply4" => Function::new_native_with_env(&store, env, |&env: &i32, arg1: i32, arg2: i32| -> i32 + {arg1 * arg2 * env }), + }, + }; + + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &import_object, + )?; + + let test: NativeFunc<(i32, i32, i32, i32, i32), i32> = instance.get_native_function("test")?; + + let result = test.call(2, 3, 4, 5, 6)?; + let manually_computed_result = 6 * (5 * (4 * (3 * 2 * 20) * 10 * 20)) * 10; + assert_eq!(result, manually_computed_result); + Ok(()) +} + +#[compiler_test(native_functions)] +fn native_function_works_for_wasm_function_manyparams(config: crate::Config) -> anyhow::Result<()> { + let store = config.store(); + let wat = r#"(module + (func $longf (import "env" "longf") (param i32 i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i64)) + (func (export "longf_pure") (param i32 i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i64) + (call $longf (local.get 0) (local.get 1) (local.get 2) (local.get 3) (local.get 4) (local.get 5) (local.get 6) (local.get 7) (local.get 8) (local.get 9))) + (func (export "longf") (result i64) + (call $longf (i32.const 1) (i32.const 2) (i32.const 3) (i32.const 4) (i32.const 5) (i32.const 6) (i64.const 7) (i64.const 8) (i32.const 9) (i32.const 0))) +)"#; + let module = Module::new(&store, wat).unwrap(); + + let import_object = imports! { + "env" => { + "longf" => Function::new_native(&store, long_f), + }, + }; + + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &import_object, + )?; + + { + let dyn_f: Function = instance.lookup_function("longf").unwrap(); + let f: NativeFunc<(), i64> = dyn_f.native().unwrap(); + let result = f.call()?; + assert_eq!(result, 1234567890); + } + + { + let dyn_f: Function = instance.lookup_function("longf_pure").unwrap(); + let f: NativeFunc<(u32, u32, u32, u32, u32, u16, u64, u64, u16, u32), i64> = + dyn_f.native().unwrap(); + let result = f.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)?; + assert_eq!(result, 1234567890); + } + + Ok(()) +} + +#[compiler_test(native_functions)] +fn native_function_works_for_wasm_function_manyparams_dynamic( + config: crate::Config, +) -> anyhow::Result<()> { + let store = config.store(); + let wat = r#"(module + (func $longf (import "env" "longf") (param i32 i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i64)) + (func (export "longf_pure") (param i32 i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i64) + (call $longf (local.get 0) (local.get 1) (local.get 2) (local.get 3) (local.get 4) (local.get 5) (local.get 6) (local.get 7) (local.get 8) (local.get 9))) + (func (export "longf") (result i64) + (call $longf (i32.const 1) (i32.const 2) (i32.const 3) (i32.const 4) (i32.const 5) (i32.const 6) (i64.const 7) (i64.const 8) (i32.const 9) (i32.const 0))) +)"#; + let module = Module::new(&store, wat).unwrap(); + + let import_object = imports! { + "env" => { + "longf" => Function::new(&store, FunctionType::new(vec![ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I32, ValType::I64 , ValType::I64 ,ValType::I32, ValType::I32], vec![ValType::I64]), long_f_dynamic), + }, + }; + + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &import_object, + )?; + + { + let dyn_f: Function = instance.lookup_function("longf").unwrap(); + let f: NativeFunc<(), i64> = dyn_f.native().unwrap(); + let result = f.call()?; + assert_eq!(result, 1234567890); + } + + { + let dyn_f: Function = instance.lookup_function("longf_pure").unwrap(); + let f: NativeFunc<(u32, u32, u32, u32, u32, u16, u64, u64, u16, u32), i64> = + dyn_f.native().unwrap(); + let result = f.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)?; + assert_eq!(result, 1234567890); + } + + Ok(()) +} + +#[compiler_test(native_functions)] +fn static_host_function_without_env(config: crate::Config) -> anyhow::Result<()> { + let store = config.store(); + + fn f(a: i32, b: i64, c: f32, d: f64) -> (f64, f32, i64, i32) { + (d * 4.0, c * 3.0, b * 2, a * 1) + } + + fn f_ok(a: i32, b: i64, c: f32, d: f64) -> Result<(f64, f32, i64, i32), Infallible> { + Ok((d * 4.0, c * 3.0, b * 2, a * 1)) + } + + fn long_f( + a: u32, + b: u32, + c: u32, + d: u32, + e: u32, + f: u16, + g: u64, + h: u64, + i: u16, + j: u32, + ) -> (u32, u64, u32) { + ( + a + b * 10 + c * 100 + d * 1000 + e * 10000 + f as u32 * 100000, + g + h * 10, + i as u32 + j * 10, + ) + } + + // Native static host function that returns a tuple. + { + let f = Function::new_native(&store, f); + let f_native: NativeFunc<(i32, i64, f32, f64), (f64, f32, i64, i32)> = f.native().unwrap(); + let result = f_native.call(1, 3, 5.0, 7.0)?; + assert_eq!(result, (28.0, 15.0, 6, 1)); + } + + // Native static host function that returns a tuple. + { + let long_f = Function::new_native(&store, long_f); + let long_f_native: NativeFunc< + (u32, u32, u32, u32, u32, u16, u64, u64, u16, u32), + (u32, u64, u32), + > = long_f.native().unwrap(); + let result = long_f_native.call(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)?; + assert_eq!(result, (654321, 87, 09)); + } + + // Native static host function that returns a result of a tuple. + { + let f = Function::new_native(&store, f_ok); + let f_native: NativeFunc<(i32, i64, f32, f64), (f64, f32, i64, i32)> = f.native().unwrap(); + let result = f_native.call(1, 3, 5.0, 7.0)?; + assert_eq!(result, (28.0, 15.0, 6, 1)); + } + + Ok(()) +} + +#[compiler_test(native_functions)] +fn static_host_function_with_env(config: crate::Config) -> anyhow::Result<()> { + let store = config.store(); + + fn f(env: &Env, a: i32, b: i64, c: f32, d: f64) -> (f64, f32, i64, i32) { + let mut guard = env.0.lock().unwrap(); + assert_eq!(*guard, 100); + *guard = 101; + + (d * 4.0, c * 3.0, b * 2, a * 1) + } + + fn f_ok(env: &Env, a: i32, b: i64, c: f32, d: f64) -> Result<(f64, f32, i64, i32), Infallible> { + let mut guard = env.0.lock().unwrap(); + assert_eq!(*guard, 100); + *guard = 101; + + Ok((d * 4.0, c * 3.0, b * 2, a * 1)) + } + + #[derive(Clone)] + struct Env(Arc>); + impl WasmerEnv for Env {} + + impl std::ops::Deref for Env { + type Target = Arc>; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + // Native static host function that returns a tuple. + { + let env = Env(Arc::new(Mutex::new(100))); + + let f = Function::new_native_with_env(&store, env.clone(), f); + let f_native: NativeFunc<(i32, i64, f32, f64), (f64, f32, i64, i32)> = f.native().unwrap(); + + assert_eq!(*env.0.lock().unwrap(), 100); + + let result = f_native.call(1, 3, 5.0, 7.0)?; + + assert_eq!(result, (28.0, 15.0, 6, 1)); + assert_eq!(*env.0.lock().unwrap(), 101); + } + + // Native static host function that returns a result of a tuple. + { + let env = Env(Arc::new(Mutex::new(100))); + + let f = Function::new_native_with_env(&store, env.clone(), f_ok); + let f_native: NativeFunc<(i32, i64, f32, f64), (f64, f32, i64, i32)> = f.native().unwrap(); + + assert_eq!(*env.0.lock().unwrap(), 100); + + let result = f_native.call(1, 3, 5.0, 7.0)?; + + assert_eq!(result, (28.0, 15.0, 6, 1)); + assert_eq!(*env.0.lock().unwrap(), 101); + } + + Ok(()) +} + +#[compiler_test(native_functions)] +fn dynamic_host_function_without_env(config: crate::Config) -> anyhow::Result<()> { + let store = config.store(); + + let f = Function::new( + &store, + FunctionType::new( + vec![ValType::I32, ValType::I64, ValType::F32, ValType::F64], + vec![ValType::F64, ValType::F32, ValType::I64, ValType::I32], + ), + |values| { + Ok(vec![ + Value::F64(values[3].unwrap_f64() * 4.0), + Value::F32(values[2].unwrap_f32() * 3.0), + Value::I64(values[1].unwrap_i64() * 2), + Value::I32(values[0].unwrap_i32() * 1), + ]) + }, + ); + let f_native: NativeFunc<(i32, i64, f32, f64), (f64, f32, i64, i32)> = f.native().unwrap(); + let result = f_native.call(1, 3, 5.0, 7.0)?; + + assert_eq!(result, (28.0, 15.0, 6, 1)); + + Ok(()) +} + +#[compiler_test(native_functions)] +fn dynamic_host_function_with_env(config: crate::Config) -> anyhow::Result<()> { + let store = config.store(); + + #[derive(Clone)] + struct Env(Arc>); + impl WasmerEnv for Env {} + + impl std::ops::Deref for Env { + type Target = Arc>; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + let env = Env(Arc::new(Mutex::new(100))); + let f = Function::new_with_env( + &store, + FunctionType::new( + vec![ValType::I32, ValType::I64, ValType::F32, ValType::F64], + vec![ValType::F64, ValType::F32, ValType::I64, ValType::I32], + ), + env.clone(), + |env, values| { + let mut guard = env.0.lock().unwrap(); + assert_eq!(*guard, 100); + + *guard = 101; + + Ok(vec![ + Value::F64(values[3].unwrap_f64() * 4.0), + Value::F32(values[2].unwrap_f32() * 3.0), + Value::I64(values[1].unwrap_i64() * 2), + Value::I32(values[0].unwrap_i32() * 1), + ]) + }, + ); + + let f_native: NativeFunc<(i32, i64, f32, f64), (f64, f32, i64, i32)> = f.native().unwrap(); + + assert_eq!(*env.0.lock().unwrap(), 100); + + let result = f_native.call(1, 3, 5.0, 7.0)?; + + assert_eq!(result, (28.0, 15.0, 6, 1)); + assert_eq!(*env.0.lock().unwrap(), 101); + + Ok(()) +} diff --git a/runtime/unc-vm/tests/compilers/serialize.rs b/runtime/unc-vm/tests/compilers/serialize.rs new file mode 100644 index 000000000..ddd1902aa --- /dev/null +++ b/runtime/unc-vm/tests/compilers/serialize.rs @@ -0,0 +1,88 @@ +use anyhow::Result; +use unc_vm_test_api::*; + +#[compiler_test(serialize)] +fn test_serialize(config: crate::Config) -> Result<()> { + let store = config.store(); + let wasm = wat2wasm( + r#" + (module + (func $hello (import "" "hello")) + (func (export "run") (call $hello)) + ) + "# + .as_bytes(), + ) + .unwrap(); + let engine = store.engine(); + let tunables = BaseTunables::for_target(engine.target()); + let executable = engine.compile_universal(&wasm, &tunables).unwrap(); + let serialized = executable.serialize().unwrap(); + assert!(!serialized.is_empty()); + Ok(()) +} + +// #[compiler_test(serialize)] +// fn test_deserialize(config: crate::Config) -> Result<()> { +// let store = config.store(); +// let wasm = wat2wasm(r#" +// (module $name +// (import "host" "sum_part" (func (param i32 i64 i32 f32 f64) (result i64))) +// (func (export "test_call") (result i64) +// i32.const 100 +// i64.const 200 +// i32.const 300 +// f32.const 400 +// f64.const 500 +// call 0 +// ) +// ) +// "#.as_bytes()).unwrap(); +// +// let engine = store.engine(); +// let tunables = BaseTunables::for_target(engine.target()); +// let executable = engine.compile(&wasm, &tunables).unwrap(); +// let writer = std::io::Cursor::new(vec![]); +// executable.serialize(&mut writer).unwrap(); +// let serialized_bytes = writer.into_inner(); +// +// let headless_store = config.headless_store(); +// +// +// let deserialized_module = unsafe { Module::deserialize(&headless_store, &serialized_bytes)? }; +// assert_eq!(deserialized_module.name(), Some("name")); +// assert_eq!( +// deserialized_module.artifact().module_ref().exports().collect::>(), +// module.exports().collect::>() +// ); +// assert_eq!( +// deserialized_module.artifact().module_ref().imports().collect::>(), +// module.imports().collect::>() +// ); +// +// let func_type = FunctionType::new( +// vec![Type::I32, Type::I64, Type::I32, Type::F32, Type::F64], +// vec![Type::I64], +// ); +// let instance = Instance::new_with_config( +// &module, +// InstanceConfig::with_stack_limit(1000000), +// &imports! { +// "host" => { +// "sum_part" => Function::new(&store, &func_type, |params| { +// let param_0: i64 = params[0].unwrap_i32() as i64; +// let param_1: i64 = params[1].unwrap_i64() as i64; +// let param_2: i64 = params[2].unwrap_i32() as i64; +// let param_3: i64 = params[3].unwrap_f32() as i64; +// let param_4: i64 = params[4].unwrap_f64() as i64; +// Ok(vec![Value::I64(param_0 + param_1 + param_2 + param_3 + param_4)]) +// }) +// } +// }, +// )?; +// +// let test_call = instance.exports.get_function("test_call")?; +// let result = test_call.call(&[])?; +// assert_eq!(result.to_vec(), vec![Value::I64(1500)]); +// Ok(()) +// } diff --git a/runtime/unc-vm/tests/compilers/stack_limiter.rs b/runtime/unc-vm/tests/compilers/stack_limiter.rs new file mode 100644 index 000000000..357cb996d --- /dev/null +++ b/runtime/unc-vm/tests/compilers/stack_limiter.rs @@ -0,0 +1,212 @@ +use unc_vm_compiler_singlepass::Singlepass; +use unc_vm_engine::universal::{LimitedMemoryPool, Universal}; +use unc_vm_test_api::*; +use unc_vm_types::InstanceConfig; +use unc_vm_vm::TrapCode; + +fn get_store() -> Store { + let compiler = Singlepass::default(); + let pool = LimitedMemoryPool::new(6, 0x100000).expect("foo"); + let store = Store::new(Universal::new(compiler).code_memory_pool(pool).engine().into()); + store +} + +#[test] +fn stack_limit_hit() { + /* This contracts is + (module + (type (;0;) (func)) + (func (;0;) (type 0) + (local f64 <32750 times>) + local.get 1 + local.get 0 + f64.copysign + call 0 + unreachable) + (memory (;0;) 16 144) + (export "main" (func 0))) + */ + let wasm: [u8; 53] = [ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00, 0x03, + 0x02, 0x01, 0x00, 0x05, 0x05, 0x01, 0x01, 0x10, 0x90, 0x01, 0x07, 0x08, 0x01, 0x04, 0x6d, + 0x61, 0x69, 0x6e, 0x00, 0x00, 0x0a, 0x10, 0x01, 0x0e, 0x01, 0xee, 0xff, 0x01, 0x7c, 0x20, + 0x01, 0x20, 0x00, 0xa6, 0x10, 0x00, 0x00, 0x0b, + ]; + let store = get_store(); + let module = Module::new(&store, &wasm).unwrap(); + let instance = + Instance::new_with_config(&module, InstanceConfig::with_stack_limit(100000), &imports! {}); + assert!(instance.is_ok()); + let instance = instance.unwrap(); + let main_func = instance.lookup_function("main").expect("expected function main"); + match main_func.call(&[]) { + Err(err) => { + let trap = err.to_trap().unwrap(); + assert_eq!(trap, TrapCode::StackOverflow); + } + _ => assert!(false), + } +} + +#[test] +fn stack_limit_operand_stack() { + let wat = format!( + r#" + (func $foo (param $depth i32) + block + (br_if 1 (i32.eq (local.get $depth) (i32.const 0))) + local.get $depth + i32.const 1 + i32.sub + local.set $depth + {extra_operand_stack} + local.get $depth + call $foo + {depopulate_operand_stack} + end + ) + (func (export "main") + (call $foo (i32.const 1000)) + ) + "#, + extra_operand_stack = "local.get $depth\n".repeat(10000), + depopulate_operand_stack = "drop\n".repeat(10000) + ); + + let store = get_store(); + let module = Module::new(&store, &wat).unwrap(); + let instance = + Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000), &imports! {}); + assert!(instance.is_ok()); + let instance = instance.unwrap(); + let main_func = instance.lookup_function("main").expect("expected function main"); + match main_func.call(&[]) { + Err(err) => { + let trap = err.to_trap().unwrap(); + assert_eq!(trap, TrapCode::StackOverflow); + } + _ => assert!(false), + } +} + +const OK_WAT: &str = r#" + (memory (;0;) 1000 10000) + (func $foo + (local f64) + i32.const 0 + i32.const 1 + i32.add + drop + ) + (func (export "main") + (local $v0 i32) + i32.const 1000000 + local.set $v0 + loop $L0 + local.get $v0 + i32.const 1 + i32.sub + local.set $v0 + call $foo + local.get $v0 + i32.const 0 + i32.gt_s + br_if $L0 + end + ) +"#; + +#[test] +fn stack_limit_ok() { + let wat = OK_WAT; + let store = get_store(); + let module = Module::new(&store, &wat).unwrap(); + let instance = + Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000), &imports! {}); + assert!(instance.is_ok()); + let instance = instance.unwrap(); + let main_func = instance.lookup_function("main").expect("expected function main"); + let e = main_func.call(&[]); + assert!(e.is_ok(), "got stack limit result: {:?}", e); +} + +#[test] +fn stack_limit_huge_limit() { + let wat = OK_WAT; + let store = get_store(); + let module = Module::new(&store, &wat).unwrap(); + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(0x7FFF_FFFF), + &imports! {}, + ); + assert!(instance.is_ok()); + let instance = instance.unwrap(); + let main_func = instance.lookup_function("main").expect("expected function main"); + main_func.call(&[]).unwrap(); +} + +#[test] +fn stack_limit_no_args() { + let wat = r#" + (func $foo + call $foo + ) + (func (export "main") + call $foo + ) + "#; + + let store = get_store(); + let module = Module::new(&store, &wat).unwrap(); + let instance = + Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000), &imports! {}); + assert!(instance.is_ok()); + let instance = instance.unwrap(); + let main_func = instance.lookup_function("main").expect("expected function main"); + match main_func.call(&[]) { + Err(err) => { + let trap = err.to_trap().unwrap(); + assert_eq!(trap, TrapCode::StackOverflow); + } + _ => assert!(false), + } +} + +#[test] +fn deep_but_sane() { + let wat = r#" + (func $foo (param $p0 i32) (result i32) + local.get $p0 + i32.const 1 + i32.sub + local.set $p0 + block $B0 + local.get $p0 + i32.const 0 + i32.le_s + br_if $B0 + local.get $p0 + call $foo + drop + end + local.get $p0 + ) + (func (export "main") + i32.const 1000 + call $foo + drop + ) + "#; + + let store = get_store(); + let module = Module::new(&store, &wat).unwrap(); + let instance = + Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000000), &imports! {}); + assert!(instance.is_ok()); + let instance = instance.unwrap(); + let main_func = instance.lookup_function("main").expect("expected function main"); + + let e = main_func.call(&[]); + assert!(e.is_ok(), "expected successful result was instead {:?}", e); +} diff --git a/runtime/unc-vm/tests/compilers/traps.rs b/runtime/unc-vm/tests/compilers/traps.rs new file mode 100644 index 000000000..d47df315a --- /dev/null +++ b/runtime/unc-vm/tests/compilers/traps.rs @@ -0,0 +1,480 @@ +use anyhow::Result; +use unc_vm_engine::RuntimeError; +use unc_vm_test_api::*; +use std::panic::{self, AssertUnwindSafe}; + +#[compiler_test(traps)] +fn test_trap_return(config: crate::Config) -> Result<()> { + let store = config.store(); + let wat = r#" + (module + (func $hello (import "" "hello")) + (func (export "run") (call $hello)) + ) + "#; + + let module = Module::new(&store, wat)?; + let hello_type = FunctionType::new(vec![], vec![]); + let hello_func = Function::new(&store, &hello_type, |_| Err(RuntimeError::new("test 123"))); + + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! { + "" => { + "hello" => hello_func + } + }, + )?; + let run_func = instance.lookup_function("run").expect("expected function export"); + + let e = run_func.call(&[]).err().expect("error calling function"); + + assert_eq!(e.message(), "test 123"); + + Ok(()) +} + +#[cfg_attr(target_env = "musl", ignore)] +#[compiler_test(traps)] +fn test_trap_trace(config: crate::Config) -> Result<()> { + let store = config.store(); + let wat = r#" + (module $hello_mod + (func (export "run") (call $hello)) + (func $hello (unreachable)) + ) + "#; + + let module = Module::new(&store, wat)?; + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! {}, + )?; + let run_func = instance.lookup_function("run").expect("expected function export"); + + let e = run_func.call(&[]).err().expect("error calling function"); + + let trace = e.trace(); + assert_eq!(trace.len(), 2); + assert_eq!(trace[0].module_name(), "hello_mod"); + assert_eq!(trace[0].func_index(), 1); + assert_eq!(trace[0].function_name(), Some("hello")); + assert_eq!(trace[1].module_name(), "hello_mod"); + assert_eq!(trace[1].func_index(), 0); + assert_eq!(trace[1].function_name(), None); + assert!(e.message().contains("unreachable"), "wrong message: {}", e.message()); + + Ok(()) +} + +#[compiler_test(traps)] +fn test_trap_trace_cb(config: crate::Config) -> Result<()> { + let store = config.store(); + let wat = r#" + (module $hello_mod + (import "" "throw" (func $throw)) + (func (export "run") (call $hello)) + (func $hello (call $throw)) + ) + "#; + + let fn_type = FunctionType::new(vec![], vec![]); + let fn_func = Function::new(&store, &fn_type, |_| Err(RuntimeError::new("cb throw"))); + + let module = Module::new(&store, wat)?; + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! { + "" => { + "throw" => fn_func + } + }, + )?; + let run_func = instance.lookup_function("run").expect("expected function export"); + + let e = run_func.call(&[]).err().expect("error calling function"); + + let trace = e.trace(); + println!("Trace {:?}", trace); + // TODO: Reenable this (disabled as it was not working with llvm/singlepass) + // assert_eq!(trace.len(), 2); + // assert_eq!(trace[0].module_name(), "hello_mod"); + // assert_eq!(trace[0].func_index(), 2); + // assert_eq!(trace[1].module_name(), "hello_mod"); + // assert_eq!(trace[1].func_index(), 1); + assert_eq!(e.message(), "cb throw"); + + Ok(()) +} + +#[cfg_attr(target_env = "musl", ignore)] +#[compiler_test(traps)] +fn test_trap_stack_overflow(config: crate::Config) -> Result<()> { + let store = config.store(); + let wat = r#" + (module $rec_mod + (func $run (export "run") (call $run)) + ) + "#; + + let module = Module::new(&store, wat)?; + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! {}, + )?; + let run_func = instance.lookup_function("run").expect("expected function export"); + + let e = run_func.call(&[]).err().expect("error calling function"); + + let trace = e.trace(); + assert!(trace.len() >= 32); + for i in 0..trace.len() { + assert_eq!(trace[i].module_name(), "rec_mod"); + assert_eq!(trace[i].func_index(), 0); + assert_eq!(trace[i].function_name(), Some("run")); + } + assert!(e.message().contains("call stack exhausted")); + + Ok(()) +} + +#[cfg_attr(target_env = "musl", ignore)] +#[compiler_test(traps)] +fn trap_display_pretty(config: crate::Config) -> Result<()> { + let store = config.store(); + let wat = r#" + (module $m + (func $die unreachable) + (func call $die) + (func $foo call 1) + (func (export "bar") call $foo) + ) + "#; + + let module = Module::new(&store, wat)?; + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! {}, + )?; + let run_func = instance.lookup_function("bar").expect("expected function export"); + + let e = run_func.call(&[]).err().expect("error calling function"); + assert_eq!( + e.to_string(), + "\ +RuntimeError: unreachable + at die (m[0]:0x23) + at (m[1]:0x27) + at foo (m[2]:0x2c) + at (m[3]:0x31)" + ); + Ok(()) +} + +#[cfg_attr(target_env = "musl", ignore)] +#[compiler_test(traps)] +fn trap_display_multi_module(config: crate::Config) -> Result<()> { + let store = config.store(); + let wat = r#" + (module $a + (func $die unreachable) + (func call $die) + (func $foo call 1) + (func (export "bar") call $foo) + ) + "#; + + let module = Module::new(&store, wat)?; + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! {}, + )?; + let bar = instance.lookup_function("bar").unwrap(); + + let wat = r#" + (module $b + (import "" "" (func $bar)) + (func $middle call $bar) + (func (export "bar2") call $middle) + ) + "#; + let module = Module::new(&store, wat)?; + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! { + "" => { + "" => bar + } + }, + )?; + let bar2 = instance.lookup_function("bar2").expect("expected function export"); + + let e = bar2.call(&[]).err().expect("error calling function"); + assert_eq!( + e.to_string(), + "\ +RuntimeError: unreachable + at die (a[0]:0x23) + at (a[1]:0x27) + at foo (a[2]:0x2c) + at (a[3]:0x31) + at middle (b[1]:0x29) + at (b[2]:0x2e)" + ); + Ok(()) +} + +#[compiler_test(traps)] +fn trap_start_function_import(config: crate::Config) -> Result<()> { + let store = config.store(); + let binary = r#" + (module $a + (import "" "" (func $foo)) + (start $foo) + ) + "#; + + let module = Module::new(&store, &binary)?; + let sig = FunctionType::new(vec![], vec![]); + let func = Function::new(&store, &sig, |_| Err(RuntimeError::new("user trap"))); + let err = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! { + "" => { + "" => func + } + }, + ) + .err() + .unwrap(); + match err { + InstantiationError::Start(err) => { + assert_eq!(err.message(), "user trap"); + } + _ => { + panic!("It should be a start error") + } + } + + Ok(()) +} + +#[compiler_test(traps)] +fn rust_panic_import(config: crate::Config) -> Result<()> { + let store = config.store(); + let binary = r#" + (module $a + (import "" "foo" (func $foo)) + (import "" "bar" (func $bar)) + (func (export "foo") call $foo) + (func (export "bar") call $bar) + ) + "#; + + let module = Module::new(&store, &binary)?; + let sig = FunctionType::new(vec![], vec![]); + let func = Function::new(&store, &sig, |_| panic!("this is a panic")); + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! { + "" => { + "foo" => func, + "bar" => Function::new_native(&store, || panic!("this is another panic")) + } + }, + )?; + let func = instance.lookup_function("foo").unwrap(); + let err = panic::catch_unwind(AssertUnwindSafe(|| { + drop(func.call(&[])); + })) + .unwrap_err(); + assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic")); + + // TODO: Reenable this (disabled as it was not working with llvm/singlepass) + // It doesn't work either with cranelift and `--test-threads=1`. + // let func = instance.lookup_function("bar")?.clone(); + // let err = panic::catch_unwind(AssertUnwindSafe(|| { + // drop(func.call(&[])); + // })) + // .unwrap_err(); + // assert_eq!( + // err.downcast_ref::<&'static str>(), + // Some(&"this is another panic") + // ); + Ok(()) +} + +#[compiler_test(traps)] +fn rust_panic_start_function(config: crate::Config) -> Result<()> { + let store = config.store(); + let binary = r#" + (module $a + (import "" "" (func $foo)) + (start $foo) + ) + "#; + + let module = Module::new(&store, &binary)?; + let sig = FunctionType::new(vec![], vec![]); + let func = Function::new(&store, &sig, |_| panic!("this is a panic")); + let err = panic::catch_unwind(AssertUnwindSafe(|| { + drop(Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! { + "" => { + "" => func + } + }, + )); + })) + .unwrap_err(); + assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic")); + + let func = Function::new_native(&store, || panic!("this is another panic")); + let err = panic::catch_unwind(AssertUnwindSafe(|| { + drop(Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! { + "" => { + "" => func + } + }, + )); + })) + .unwrap_err(); + assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is another panic")); + Ok(()) +} + +#[compiler_test(traps)] +fn mismatched_arguments(config: crate::Config) -> Result<()> { + let store = config.store(); + let binary = r#" + (module $a + (func (export "foo") (param i32)) + ) + "#; + + let module = Module::new(&store, &binary)?; + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! {}, + )?; + let func: Function = instance.lookup_function("foo").unwrap(); + assert_eq!( + func.call(&[]).unwrap_err().message(), + "Parameters of type [] did not match signature [I32] -> []" + ); + assert_eq!( + func.call(&[Val::F32(0.0)]).unwrap_err().message(), + "Parameters of type [F32] did not match signature [I32] -> []", + ); + assert_eq!( + func.call(&[Val::I32(0), Val::I32(1)]).unwrap_err().message(), + "Parameters of type [I32, I32] did not match signature [I32] -> []" + ); + Ok(()) +} + +#[cfg_attr(target_env = "musl", ignore)] +#[compiler_test(traps)] +fn call_signature_mismatch(config: crate::Config) -> Result<()> { + let store = config.store(); + let binary = r#" + (module $a + (func $foo + i32.const 0 + call_indirect) + (func $bar (param i32)) + (start $foo) + + (table 1 anyfunc) + (elem (i32.const 0) 1) + ) + "#; + + let module = Module::new(&store, &binary)?; + let err = + Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000000), &imports! {}) + .err() + .expect("expected error"); + assert_eq!( + format!("{}", err), + "\ +RuntimeError: indirect call type mismatch + at foo (a[0]:0x30)\ +" + ); + Ok(()) +} + +#[compiler_test(traps)] +#[cfg_attr(target_env = "musl", ignore)] +fn start_trap_pretty(config: crate::Config) -> Result<()> { + let store = config.store(); + let wat = r#" + (module $m + (func $die unreachable) + (func call $die) + (func $foo call 1) + (func $start call $foo) + (start $start) + ) + "#; + + let module = Module::new(&store, wat)?; + let err = + Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000000), &imports! {}) + .err() + .expect("expected error"); + + assert_eq!( + format!("{}", err), + "\ +RuntimeError: unreachable + at die (m[0]:0x1d) + at (m[1]:0x21) + at foo (m[2]:0x26) + at start (m[3]:0x2b)\ +" + ); + Ok(()) +} + +#[compiler_test(traps)] +fn present_after_module_drop(config: crate::Config) -> Result<()> { + let store = config.store(); + let module = Module::new(&store, r#"(func (export "foo") unreachable)"#)?; + let instance = Instance::new_with_config( + &module, + InstanceConfig::with_stack_limit(1000000), + &imports! {}, + )?; + let func: Function = instance.lookup_function("foo").unwrap(); + + println!("asserting before we drop modules"); + assert_trap(func.call(&[]).unwrap_err()); + drop((instance, module)); + + println!("asserting after drop"); + assert_trap(func.call(&[]).unwrap_err()); + return Ok(()); + + fn assert_trap(t: RuntimeError) { + println!("{}", t); + // assert_eq!(t.trace().len(), 1); + // assert_eq!(t.trace()[0].func_index(), 0); + } +} diff --git a/runtime/unc-vm/tests/compilers/wast.rs b/runtime/unc-vm/tests/compilers/wast.rs new file mode 100644 index 000000000..430536791 --- /dev/null +++ b/runtime/unc-vm/tests/compilers/wast.rs @@ -0,0 +1,66 @@ +use unc_vm_compiler::Features; +use unc_vm_wast::Wast; +use std::path::Path; + +// The generated tests (from build.rs) look like: +// #[cfg(test)] +// mod [compiler] { +// mod [spec] { +// mod [vfs] { +// #[test] +// fn [test_name]() -> anyhow::Result<()> { +// crate::run_wasi("tests/spectests/[test_name].wast", "[compiler]", WasiFileSystemKind::[vfs]) +// } +// } +// } +// } +include!(concat!(env!("OUT_DIR"), "/generated_spectests.rs")); + +pub fn run_wast(mut config: crate::Config, wast_path: &str) -> anyhow::Result<()> { + println!("Running wast `{}`", wast_path); + let try_nan_canonicalization = wast_path.contains("nan-canonicalization"); + let mut features = Features::default(); + let is_bulkmemory = wast_path.contains("bulk-memory"); + let is_simd = wast_path.contains("simd"); + if is_bulkmemory { + features.bulk_memory(true); + } + if is_simd { + features.simd(true); + } + if config.compiler == crate::Compiler::Singlepass { + features.multi_value(false); + } + config.set_features(features); + config.set_nan_canonicalization(try_nan_canonicalization); + + let store = config.store(); + let mut wast = Wast::new_with_spectest(store); + // `bulk-memory-operations/bulk.wast` checks for a message that + // specifies which element is uninitialized, but our traps don't + // shepherd that information out. + wast.allow_trap_message("uninitialized element 2", "uninitialized element"); + // `liking.wast` has different wording but the same meaning + wast.allow_trap_message("out of bounds memory access", "memory out of bounds"); + if cfg!(feature = "coverage") { + wast.disable_assert_and_exhaustion(); + } + if is_simd { + // We allow this, so tests can be run properly for `simd_const` test. + wast.allow_instantiation_failures(&[ + "Validation error: multiple tables", + "Validation error: unknown memory 0", + "Validation error: Invalid var_u32", + ]); + } + if config.compiler == crate::Compiler::Singlepass { + // We don't support multivalue yet in singlepass + wast.allow_instantiation_failures(&[ + "Validation error: func type returns multiple values but the multi-value feature is not enabled", + "Validation error: blocks, loops, and ifs may only produce a resulttype when multi-value is not enabled", + ]); + } + wast.fail_fast = false; + let path = Path::new(wast_path); + wast.run_file(path) +} diff --git a/runtime/unc-vm/tests/deprecated.rs b/runtime/unc-vm/tests/deprecated.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/runtime/unc-vm/tests/deprecated.rs @@ -0,0 +1 @@ + diff --git a/runtime/unc-vm/tests/deprecated/README.md b/runtime/unc-vm/tests/deprecated/README.md new file mode 100644 index 000000000..8ee7575c5 --- /dev/null +++ b/runtime/unc-vm/tests/deprecated/README.md @@ -0,0 +1,11 @@ +# Tests: Deprecated + +In this folder we will test deprecated APIs to verify that they +still work the way it should, so users of Wasmer are happy while +transitioning to newer APIs. + +As time passes and adoption of new APIs rises, tests on this folder +should start tending towards 0. + +Therefore, deprecated tests are intended to be deleted on the long +term. diff --git a/runtime/unc-vm/tests/examples/add.wat b/runtime/unc-vm/tests/examples/add.wat new file mode 100644 index 000000000..a544516fa --- /dev/null +++ b/runtime/unc-vm/tests/examples/add.wat @@ -0,0 +1,3 @@ +(module + (func (export "add") (param $x i64) (param $y i64) (result i64) (i64.add (local.get $x) (local.get $y))) +) \ No newline at end of file diff --git a/runtime/unc-vm/tests/examples/call_indirect.wat b/runtime/unc-vm/tests/examples/call_indirect.wat new file mode 100644 index 000000000..613b5bfb4 --- /dev/null +++ b/runtime/unc-vm/tests/examples/call_indirect.wat @@ -0,0 +1,30 @@ +(module + (type $multiply_signature (func (param i32 i32) (result i32))) + (table 1 1 anyfunc) + (elem (i32.const 0) $multiply) + (memory $0 1) + (export "memory" (memory $0)) + (export "dispatch" (func $dispatch)) + (export "multiply" (func $multiply)) + (export "main" (func $main)) + (func $dispatch (; 0 ;) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (call_indirect (type $multiply_signature) + (get_local $1) + (get_local $2) + (get_local $0) + ) + ) + (func $multiply (; 1 ;) (type $multiply_signature) (param $0 i32) (param $1 i32) (result i32) + (i32.mul + (get_local $1) + (get_local $0) + ) + ) + (func $main (; 2 ;) (result i32) + (call $dispatch + (i32.const 0) + (i32.const 20) + (i32.const 30) + ) + ) +) diff --git a/runtime/unc-vm/tests/examples/fac.wat b/runtime/unc-vm/tests/examples/fac.wat new file mode 100644 index 000000000..4de9c0fb7 --- /dev/null +++ b/runtime/unc-vm/tests/examples/fac.wat @@ -0,0 +1,82 @@ +(module + ;; Recursive factorial + (func (export "fac-rec") (param i64) (result i64) + (if (result i64) (i64.eq (local.get 0) (i64.const 0)) + (then (i64.const 1)) + (else + (i64.mul (local.get 0) (call 0 (i64.sub (local.get 0) (i64.const 1)))) + ) + ) + ) + + ;; Recursive factorial named + (func $fac-rec-named (export "fac-rec-named") (param $n i64) (result i64) + (if (result i64) (i64.eq (local.get $n) (i64.const 0)) + (then (i64.const 1)) + (else + (i64.mul + (local.get $n) + (call $fac-rec-named (i64.sub (local.get $n) (i64.const 1))) + ) + ) + ) + ) + + ;; Iterative factorial + (func (export "fac-iter") (param i64) (result i64) + (local i64 i64) + (local.set 1 (local.get 0)) + (local.set 2 (i64.const 1)) + (block + (loop + (if + (i64.eq (local.get 1) (i64.const 0)) + (then (br 2)) + (else + (local.set 2 (i64.mul (local.get 1) (local.get 2))) + (local.set 1 (i64.sub (local.get 1) (i64.const 1))) + ) + ) + (br 0) + ) + ) + (local.get 2) + ) + + ;; Iterative factorial named + (func (export "fac-iter-named") (param $n i64) (result i64) + (local $i i64) + (local $res i64) + (local.set $i (local.get $n)) + (local.set $res (i64.const 1)) + (block $done + (loop $loop + (if + (i64.eq (local.get $i) (i64.const 0)) + (then (br $done)) + (else + (local.set $res (i64.mul (local.get $i) (local.get $res))) + (local.set $i (i64.sub (local.get $i) (i64.const 1))) + ) + ) + (br $loop) + ) + ) + (local.get $res) + ) + + ;; Optimized factorial. + (func (export "fac-opt") (param i64) (result i64) + (local i64) + (local.set 1 (i64.const 1)) + (block + (br_if 0 (i64.lt_s (local.get 0) (i64.const 2))) + (loop + (local.set 1 (i64.mul (local.get 1) (local.get 0))) + (local.set 0 (i64.add (local.get 0) (i64.const -1))) + (br_if 0 (i64.gt_s (local.get 0) (i64.const 1))) + ) + ) + (local.get 1) + ) +) diff --git a/runtime/unc-vm/tests/examples/fib.wat b/runtime/unc-vm/tests/examples/fib.wat new file mode 100644 index 000000000..c23fbb66b --- /dev/null +++ b/runtime/unc-vm/tests/examples/fib.wat @@ -0,0 +1,20 @@ +(module + (func $main (result i32) + (call $fib (i32.const 40)) + ) + + (func $fib (param $n i32) (result i32) + (if (i32.eq (get_local $n) (i32.const 0)) + (then (return (i32.const 1))) + ) + (if (i32.eq (get_local $n) (i32.const 1)) + (then (return (i32.const 1))) + ) + (i32.add + (call $fib (i32.sub (get_local $n) (i32.const 1))) + (call $fib (i32.sub (get_local $n) (i32.const 2))) + ) + ) + + (export "_start" (func $main)) +) diff --git a/runtime/unc-vm/tests/examples/global.wat b/runtime/unc-vm/tests/examples/global.wat new file mode 100644 index 000000000..9260ed792 --- /dev/null +++ b/runtime/unc-vm/tests/examples/global.wat @@ -0,0 +1,8 @@ +(module + (global $xxx (mut i32) (i32.const 42)) + (func $main (result i32) + (global.set $xxx (i32.const 0)) + (i32.const 1) + ) + (export "_start" (func $main)) +) diff --git a/runtime/unc-vm/tests/examples/invalid.wat b/runtime/unc-vm/tests/examples/invalid.wat new file mode 100644 index 000000000..866bbe152 --- /dev/null +++ b/runtime/unc-vm/tests/examples/invalid.wat @@ -0,0 +1,5 @@ +(module + (func (export "sqrt") (param f32) (result f32) + f32.sqrt + ) +) diff --git a/runtime/unc-vm/tests/examples/memory.wat b/runtime/unc-vm/tests/examples/memory.wat new file mode 100755 index 000000000..09c3685e2 --- /dev/null +++ b/runtime/unc-vm/tests/examples/memory.wat @@ -0,0 +1,42 @@ +(module + (memory 1) + (table 20 anyfunc) + (elem (i32.const 9) $f) + (func $f (param i32) (result i32) + (get_local 0) + ) + (func $main (export "main") (result i32) + (local i32) + (set_local 0 (i32.const 100)) + (i32.store (get_local 0) (i32.const 1602)) + (i32.load (get_local 0)) + + (drop) + (memory.grow (i32.const 0)) + + (drop) + (memory.grow (i32.const 2)) + + (drop) + (memory.grow (i32.const 65536)) + + (drop) + (memory.grow (i32.const 12)) + ) + + + (func (export "no_dce.i32.load") (param $i i32) (drop (i32.load (local.get $i)))) + (func (export "no_dce.i32.load16_s") (param $i i32) (drop (i32.load16_s (local.get $i)))) + (func (export "no_dce.i32.load16_u") (param $i i32) (drop (i32.load16_u (local.get $i)))) + (func (export "no_dce.i32.load8_s") (param $i i32) (drop (i32.load8_s (local.get $i)))) + (func (export "no_dce.i32.load8_u") (param $i i32) (drop (i32.load8_u (local.get $i)))) + (func (export "no_dce.i64.load") (param $i i32) (drop (i64.load (local.get $i)))) + (func (export "no_dce.i64.load32_s") (param $i i32) (drop (i64.load32_s (local.get $i)))) + (func (export "no_dce.i64.load32_u") (param $i i32) (drop (i64.load32_u (local.get $i)))) + (func (export "no_dce.i64.load16_s") (param $i i32) (drop (i64.load16_s (local.get $i)))) + (func (export "no_dce.i64.load16_u") (param $i i32) (drop (i64.load16_u (local.get $i)))) + (func (export "no_dce.i64.load8_s") (param $i i32) (drop (i64.load8_s (local.get $i)))) + (func (export "no_dce.i64.load8_u") (param $i i32) (drop (i64.load8_u (local.get $i)))) + (func (export "no_dce.f32.load") (param $i i32) (drop (f32.load (local.get $i)))) + (func (export "no_dce.f64.load") (param $i i32) (drop (f64.load (local.get $i)))) +) diff --git a/runtime/unc-vm/tests/examples/no_start.wat b/runtime/unc-vm/tests/examples/no_start.wat new file mode 100644 index 000000000..b6e7ee108 --- /dev/null +++ b/runtime/unc-vm/tests/examples/no_start.wat @@ -0,0 +1,5 @@ +(module + (type (;0;) (func (param f64) (result i32))) + (func (;0;) (type 0) (param f64) (result i32) + unreachable)) + diff --git a/runtime/unc-vm/tests/examples/trap.wat b/runtime/unc-vm/tests/examples/trap.wat new file mode 100644 index 000000000..9210cfa11 --- /dev/null +++ b/runtime/unc-vm/tests/examples/trap.wat @@ -0,0 +1,5 @@ +(module + (func $main (export "_start") (result i32) + (i32.div_s (i32.const 0) (i32.const 0)) + ) +) diff --git a/runtime/unc-vm/tests/ignores.txt b/runtime/unc-vm/tests/ignores.txt new file mode 100644 index 000000000..6caee3651 --- /dev/null +++ b/runtime/unc-vm/tests/ignores.txt @@ -0,0 +1,93 @@ +# Compilers +singlepass spec::multi_value # Singlepass has not implemented multivalue (functions that returns "structs"/"tuples") +singlepass spec::simd # Singlepass doesn't support yet SIMD (no one asked for this feature) + +# Traps +## Traps. Tracing doesn't work properly in Singlepass +## Unwinding is not properly implemented in Singlepass +# Needs investigation +singlepass traps::test_trap_trace +aarch64 traps::test_trap_trace +singlepass traps::test_trap_stack_overflow # Need to investigate +aarch64 traps::test_trap_stack_overflow # Need to investigate +singlepass traps::trap_display_pretty +aarch64 traps::trap_display_pretty +singlepass traps::trap_display_multi_module +aarch64 traps::trap_display_multi_module +singlepass traps::call_signature_mismatch +macos+aarch64 traps::call_signature_mismatch +singlepass traps::start_trap_pretty +aarch64 traps::start_trap_pretty + +singlepass multi_value_imports::dylib # Singlepass doesn't support multivalue +singlepass multi_value_imports::dynamic # Singlepass doesn't support multivalue + +# TODO: We need to fix this in ARM. The issue is caused by libunwind overflowing +# the stack while creating the stacktrace. +# https://github.com/rust-lang/backtrace-rs/issues/356 +singlepass+windows spec::skip_stack_guard_page # Needs investigation. + +# Windows doesn't overcommit and fails to allocate 4GB of memory +windows wasmer::max_size_of_memory + +# Frontends + +## WASI + +### These tests don't pass due to race conditions in the new way we run tests. +### It's not built to be run in parallel with itself, so we disable it for now. + +wasitests::snapshot1::host_fs::writing +wasitests::unstable::host_fs::writing +wasitests::snapshot1::mem_fs::writing +wasitests::unstable::mem_fs::writing + +### due to hard-coded direct calls into WASI for wasi unstable + +wasitests::snapshot1::host_fs::fd_read +wasitests::snapshot1::host_fs::poll_oneoff +wasitests::snapshot1::host_fs::fd_pread +wasitests::snapshot1::host_fs::fd_close +wasitests::snapshot1::host_fs::fd_allocate +wasitests::snapshot1::host_fs::close_preopen_fd +wasitests::snapshot1::host_fs::envvar +wasitests::snapshot1::mem_fs::fd_read +wasitests::snapshot1::mem_fs::poll_oneoff +wasitests::snapshot1::mem_fs::fd_pread +wasitests::snapshot1::mem_fs::fd_close +wasitests::snapshot1::mem_fs::fd_allocate +wasitests::snapshot1::mem_fs::close_preopen_fd +wasitests::snapshot1::mem_fs::envvar + +### TODO: resolve the disabled tests below. These are newly disabled tests from the migration: + +### due to git clone not preserving symlinks: +wasitests::snapshot1::host_fs::readlink +wasitests::unstable::host_fs::readlink +wasitests::snapshot1::mem_fs::readlink +wasitests::unstable::mem_fs::readlink + +### failing due to `remove_dir_all`. this test is also bad for parallelism +wasitests::snapshot1::host_fs::create_dir +wasitests::unstable::host_fs::create_dir +wasitests::snapshot1::mem_fs::create_dir +wasitests::unstable::mem_fs::create_dir + +### failing because it closes `stdout` which breaks our testing system +wasitests::unstable::host_fs::fd_close +wasitests::unstable::mem_fs::fd_close + +### failing because we're operating on stdout which is now overridden. +### TODO: check WasiFile implementation +### Alterative: split test into 2 parts, one printing to stderr, the other printing to stdout to test the real versions +wasitests::unstable::host_fs::poll_oneoff +wasitests::unstable::mem_fs::poll_oneoff + +### randomly failed, mainly on windows but also on macos, due to a race condition when concurently testing multiple compiler / engines +wasitests::snapshot1::host_fs::fd_rename_path + +# This tests are disabled for now +wasitests::unstable::host_fs::unix_open_special_files +wasitests::snapshot1::host_fs::unix_open_special_files +wasitests::unstable::mem_fs::unix_open_special_files +wasitests::snapshot1::mem_fs::unix_open_special_files diff --git a/runtime/unc-vm/tests/lib/compiler-test-derive/src/lib.rs.orig b/runtime/unc-vm/tests/lib/compiler-test-derive/src/lib.rs.orig new file mode 100644 index 000000000..aa50ac8a0 --- /dev/null +++ b/runtime/unc-vm/tests/lib/compiler-test-derive/src/lib.rs.orig @@ -0,0 +1,131 @@ +<<<<<<< HEAD +======= +#[cfg(not(test))] +extern crate proc_macro; +>>>>>>> 27e96cad2 (compiler-test-derive: Switch to proc_macro exclusively) +use proc_macro::TokenStream; +use quote::quote; +use std::path::PathBuf; +use syn::parse; +use syn::*; + +mod ignores; + +// Reimplement parse_macro_input to use the imported `parse` +// function. This way parse_macro_input will parse a TokenStream2 when +// unit-testing. +macro_rules! parse_macro_input { + ( + $token_stream:ident as $T:ty + ) => { + match parse::<$T>($token_stream) { + Ok(data) => data, + Err(err) => { + return TokenStream::from(err.to_compile_error()); + } + } + }; + + ( + $token_stream:ident + ) => { + parse_macro_input!($token_stream as _) + }; +} + +#[proc_macro_attribute] +pub fn compiler_test(attrs: TokenStream, input: TokenStream) -> TokenStream { + let path: Option = parse::(attrs).ok(); + let mut my_fn: ItemFn = parse_macro_input!(input as ItemFn); + let fn_name = my_fn.sig.ident.clone(); + + // Let's build the ignores to append an `#[ignore]` macro to the + // autogenerated tests in case the test appears in the `ignores.txt` path; + + let mut ignores_txt_path = PathBuf::new(); + ignores_txt_path.push(env!("CARGO_MANIFEST_DIR")); + ignores_txt_path.push("../../ignores.txt"); + + let ignores = crate::ignores::Ignores::build_from_path(ignores_txt_path); + + let should_ignore = |test_name: &str, compiler_name: &str, engine_name: &str| { + let compiler_name = compiler_name.to_lowercase(); + let engine_name = engine_name.to_lowercase(); + // We construct the path manually because we can't get the + // source_file location from the `Span` (it's only available in nightly) + let full_path = + format!("{}::{}::{}::{}", quote! { #path }, test_name, compiler_name, engine_name) + .replace(" ", ""); + let should_ignore = ignores.should_ignore_host(&engine_name, &compiler_name, &full_path); + // println!("{} -> Should ignore: {}", full_path, should_ignore); + return should_ignore; + }; + let construct_engine_test = |func: &::syn::ItemFn, + compiler_name: &str, + engine_name: &str, + engine_feature_name: &str| + -> ::proc_macro2::TokenStream { + let config_compiler = ::quote::format_ident!("{}", compiler_name); + let config_engine = ::quote::format_ident!("{}", engine_name); + let test_name = ::quote::format_ident!("{}", engine_name.to_lowercase()); + let mut new_sig = func.sig.clone(); + let attrs = func.attrs.clone().iter().fold(quote! {}, |acc, new| quote! {#acc #new}); + new_sig.ident = test_name; + new_sig.inputs = ::syn::punctuated::Punctuated::new(); + let f = quote! { + #[test_log::test] + #attrs + #[cfg(feature = #engine_feature_name)] + #new_sig { + #fn_name(crate::Config::new(crate::Engine::#config_engine, crate::Compiler::#config_compiler)) + } + }; + if should_ignore(&func.sig.ident.to_string().replace("r#", ""), compiler_name, engine_name) + && !cfg!(test) + { + quote! { + #[ignore] + #f + } + } else { + f + } + }; + + let construct_compiler_test = + |func: &::syn::ItemFn, compiler_name: &str| -> ::proc_macro2::TokenStream { + let mod_name = ::quote::format_ident!("{}", compiler_name.to_lowercase()); + let universal_engine_test = + construct_engine_test(func, compiler_name, "Universal", "universal"); + let dylib_engine_test = construct_engine_test(func, compiler_name, "Dylib", "dylib"); + let compiler_name_lowercase = compiler_name.to_lowercase(); + + quote! { + #[cfg(feature = #compiler_name_lowercase)] + mod #mod_name { + use super::*; + + #universal_engine_test + #dylib_engine_test + } + } + }; + + let singlepass_compiler_test = construct_compiler_test(&my_fn, "Singlepass"); + + // We remove the method decorators + my_fn.attrs = vec![]; + + let x = quote! { + #[cfg(test)] + mod #fn_name { + use super::*; + + #[allow(unused)] + #my_fn + + #singlepass_compiler_test + } + }; + x.into() +} diff --git a/runtime/unc-vm/tests/wast/README.md b/runtime/unc-vm/tests/wast/README.md new file mode 100644 index 000000000..4c4636eb6 --- /dev/null +++ b/runtime/unc-vm/tests/wast/README.md @@ -0,0 +1,3 @@ +# WebAssembly testsuite + +Here is where all the `.wast` tests live. diff --git a/runtime/unc-vm/tests/wast/spec/.gitignore b/runtime/unc-vm/tests/wast/spec/.gitignore new file mode 100644 index 000000000..34e4053fd --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/.gitignore @@ -0,0 +1,2 @@ +/commit_message +/repos/ diff --git a/runtime/unc-vm/tests/wast/spec/Contributing.md b/runtime/unc-vm/tests/wast/spec/Contributing.md new file mode 100644 index 000000000..1cc607fa4 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/Contributing.md @@ -0,0 +1,8 @@ +# Contributing to WebAssembly + +Interested in participating? Please follow +[the same contributing guidelines as the design repository][]. + + [the same contributing guidelines as the design repository]: https://github.com/WebAssembly/design/blob/master/Contributing.md + +Also, please be sure to read [the README.md](README.md) for this repository. diff --git a/runtime/unc-vm/tests/wast/spec/LICENSE b/runtime/unc-vm/tests/wast/spec/LICENSE new file mode 100644 index 000000000..8f71f43fe --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/runtime/unc-vm/tests/wast/spec/README.md b/runtime/unc-vm/tests/wast/spec/README.md new file mode 100644 index 000000000..50a254173 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/README.md @@ -0,0 +1,18 @@ +Amalgamated WebAssembly Test Suite +================================== + +This repository holds a mirror of the WebAssembly core testsuite which is +maintained [here](https://github.com/WebAssembly/spec/tree/master/test/core), +as well as the tests from the various [proposals +repositories](https://github.com/WebAssembly/proposals/blob/master/README.md). + +In addition it also contains tests from various proposals which are currently +forks of the primary spec repo. + +To add new tests or report problems in existing tests, please file issues and +PRs within the spec or individual proposal repositories rather than within this +mirror repository. + +To update the tests in this repo from their upstream sources: + +1. Run `update-tests.sh` diff --git a/runtime/unc-vm/tests/wast/spec/address.wast b/runtime/unc-vm/tests/wast/spec/address.wast new file mode 100644 index 000000000..320ea36bc --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/address.wast @@ -0,0 +1,610 @@ +;; Load i32 data with different offset/align arguments + +(module + (memory 1) + (data (i32.const 0) "abcdefghijklmnopqrstuvwxyz") + + (func (export "8u_good1") (param $i i32) (result i32) + (i32.load8_u offset=0 (local.get $i)) ;; 97 'a' + ) + (func (export "8u_good2") (param $i i32) (result i32) + (i32.load8_u align=1 (local.get $i)) ;; 97 'a' + ) + (func (export "8u_good3") (param $i i32) (result i32) + (i32.load8_u offset=1 align=1 (local.get $i)) ;; 98 'b' + ) + (func (export "8u_good4") (param $i i32) (result i32) + (i32.load8_u offset=2 align=1 (local.get $i)) ;; 99 'c' + ) + (func (export "8u_good5") (param $i i32) (result i32) + (i32.load8_u offset=25 align=1 (local.get $i)) ;; 122 'z' + ) + + (func (export "8s_good1") (param $i i32) (result i32) + (i32.load8_s offset=0 (local.get $i)) ;; 97 'a' + ) + (func (export "8s_good2") (param $i i32) (result i32) + (i32.load8_s align=1 (local.get $i)) ;; 97 'a' + ) + (func (export "8s_good3") (param $i i32) (result i32) + (i32.load8_s offset=1 align=1 (local.get $i)) ;; 98 'b' + ) + (func (export "8s_good4") (param $i i32) (result i32) + (i32.load8_s offset=2 align=1 (local.get $i)) ;; 99 'c' + ) + (func (export "8s_good5") (param $i i32) (result i32) + (i32.load8_s offset=25 align=1 (local.get $i)) ;; 122 'z' + ) + + (func (export "16u_good1") (param $i i32) (result i32) + (i32.load16_u offset=0 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16u_good2") (param $i i32) (result i32) + (i32.load16_u align=1 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16u_good3") (param $i i32) (result i32) + (i32.load16_u offset=1 align=1 (local.get $i)) ;; 25442 'bc' + ) + (func (export "16u_good4") (param $i i32) (result i32) + (i32.load16_u offset=2 align=2 (local.get $i)) ;; 25699 'cd' + ) + (func (export "16u_good5") (param $i i32) (result i32) + (i32.load16_u offset=25 align=2 (local.get $i)) ;; 122 'z\0' + ) + + (func (export "16s_good1") (param $i i32) (result i32) + (i32.load16_s offset=0 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16s_good2") (param $i i32) (result i32) + (i32.load16_s align=1 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16s_good3") (param $i i32) (result i32) + (i32.load16_s offset=1 align=1 (local.get $i)) ;; 25442 'bc' + ) + (func (export "16s_good4") (param $i i32) (result i32) + (i32.load16_s offset=2 align=2 (local.get $i)) ;; 25699 'cd' + ) + (func (export "16s_good5") (param $i i32) (result i32) + (i32.load16_s offset=25 align=2 (local.get $i)) ;; 122 'z\0' + ) + + (func (export "32_good1") (param $i i32) (result i32) + (i32.load offset=0 (local.get $i)) ;; 1684234849 'abcd' + ) + (func (export "32_good2") (param $i i32) (result i32) + (i32.load align=1 (local.get $i)) ;; 1684234849 'abcd' + ) + (func (export "32_good3") (param $i i32) (result i32) + (i32.load offset=1 align=1 (local.get $i)) ;; 1701077858 'bcde' + ) + (func (export "32_good4") (param $i i32) (result i32) + (i32.load offset=2 align=2 (local.get $i)) ;; 1717920867 'cdef' + ) + (func (export "32_good5") (param $i i32) (result i32) + (i32.load offset=25 align=4 (local.get $i)) ;; 122 'z\0\0\0' + ) + + (func (export "8u_bad") (param $i i32) + (drop (i32.load8_u offset=4294967295 (local.get $i))) + ) + (func (export "8s_bad") (param $i i32) + (drop (i32.load8_s offset=4294967295 (local.get $i))) + ) + (func (export "16u_bad") (param $i i32) + (drop (i32.load16_u offset=4294967295 (local.get $i))) + ) + (func (export "16s_bad") (param $i i32) + (drop (i32.load16_s offset=4294967295 (local.get $i))) + ) + (func (export "32_bad") (param $i i32) + (drop (i32.load offset=4294967295 (local.get $i))) + ) +) + +(assert_return (invoke "8u_good1" (i32.const 0)) (i32.const 97)) +(assert_return (invoke "8u_good2" (i32.const 0)) (i32.const 97)) +(assert_return (invoke "8u_good3" (i32.const 0)) (i32.const 98)) +(assert_return (invoke "8u_good4" (i32.const 0)) (i32.const 99)) +(assert_return (invoke "8u_good5" (i32.const 0)) (i32.const 122)) + +(assert_return (invoke "8s_good1" (i32.const 0)) (i32.const 97)) +(assert_return (invoke "8s_good2" (i32.const 0)) (i32.const 97)) +(assert_return (invoke "8s_good3" (i32.const 0)) (i32.const 98)) +(assert_return (invoke "8s_good4" (i32.const 0)) (i32.const 99)) +(assert_return (invoke "8s_good5" (i32.const 0)) (i32.const 122)) + +(assert_return (invoke "16u_good1" (i32.const 0)) (i32.const 25185)) +(assert_return (invoke "16u_good2" (i32.const 0)) (i32.const 25185)) +(assert_return (invoke "16u_good3" (i32.const 0)) (i32.const 25442)) +(assert_return (invoke "16u_good4" (i32.const 0)) (i32.const 25699)) +(assert_return (invoke "16u_good5" (i32.const 0)) (i32.const 122)) + +(assert_return (invoke "16s_good1" (i32.const 0)) (i32.const 25185)) +(assert_return (invoke "16s_good2" (i32.const 0)) (i32.const 25185)) +(assert_return (invoke "16s_good3" (i32.const 0)) (i32.const 25442)) +(assert_return (invoke "16s_good4" (i32.const 0)) (i32.const 25699)) +(assert_return (invoke "16s_good5" (i32.const 0)) (i32.const 122)) + +(assert_return (invoke "32_good1" (i32.const 0)) (i32.const 1684234849)) +(assert_return (invoke "32_good2" (i32.const 0)) (i32.const 1684234849)) +(assert_return (invoke "32_good3" (i32.const 0)) (i32.const 1701077858)) +(assert_return (invoke "32_good4" (i32.const 0)) (i32.const 1717920867)) +(assert_return (invoke "32_good5" (i32.const 0)) (i32.const 122)) + +(assert_return (invoke "8u_good1" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "8u_good2" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "8u_good3" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "8u_good4" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "8u_good5" (i32.const 65507)) (i32.const 0)) + +(assert_return (invoke "8s_good1" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "8s_good2" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "8s_good3" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "8s_good4" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "8s_good5" (i32.const 65507)) (i32.const 0)) + +(assert_return (invoke "16u_good1" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "16u_good2" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "16u_good3" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "16u_good4" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "16u_good5" (i32.const 65507)) (i32.const 0)) + +(assert_return (invoke "16s_good1" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "16s_good2" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "16s_good3" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "16s_good4" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "16s_good5" (i32.const 65507)) (i32.const 0)) + +(assert_return (invoke "32_good1" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "32_good2" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "32_good3" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "32_good4" (i32.const 65507)) (i32.const 0)) +(assert_return (invoke "32_good5" (i32.const 65507)) (i32.const 0)) + +(assert_return (invoke "8u_good1" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "8u_good2" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "8u_good3" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "8u_good4" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "8u_good5" (i32.const 65508)) (i32.const 0)) + +(assert_return (invoke "8s_good1" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "8s_good2" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "8s_good3" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "8s_good4" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "8s_good5" (i32.const 65508)) (i32.const 0)) + +(assert_return (invoke "16u_good1" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "16u_good2" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "16u_good3" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "16u_good4" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "16u_good5" (i32.const 65508)) (i32.const 0)) + +(assert_return (invoke "16s_good1" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "16s_good2" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "16s_good3" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "16s_good4" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "16s_good5" (i32.const 65508)) (i32.const 0)) + +(assert_return (invoke "32_good1" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "32_good2" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "32_good3" (i32.const 65508)) (i32.const 0)) +(assert_return (invoke "32_good4" (i32.const 65508)) (i32.const 0)) +(assert_trap (invoke "32_good5" (i32.const 65508)) "out of bounds memory access") + +(assert_trap (invoke "8u_good3" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "8s_good3" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "16u_good3" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "16s_good3" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "32_good3" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "32_good3" (i32.const -1)) "out of bounds memory access") + +(assert_trap (invoke "8u_bad" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "8s_bad" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "16u_bad" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "16s_bad" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "32_bad" (i32.const 0)) "out of bounds memory access") + +(assert_trap (invoke "8u_bad" (i32.const 1)) "out of bounds memory access") +(assert_trap (invoke "8s_bad" (i32.const 1)) "out of bounds memory access") +(assert_trap (invoke "16u_bad" (i32.const 1)) "out of bounds memory access") +(assert_trap (invoke "16s_bad" (i32.const 1)) "out of bounds memory access") +(assert_trap (invoke "32_bad" (i32.const 1)) "out of bounds memory access") + +(assert_malformed + (module quote + "(memory 1)" + "(func (drop (i32.load offset=4294967296 (i32.const 0))))" + ) + "i32 constant" +) + +;; Load i64 data with different offset/align arguments + +(module + (memory 1) + (data (i32.const 0) "abcdefghijklmnopqrstuvwxyz") + + (func (export "8u_good1") (param $i i32) (result i64) + (i64.load8_u offset=0 (local.get $i)) ;; 97 'a' + ) + (func (export "8u_good2") (param $i i32) (result i64) + (i64.load8_u align=1 (local.get $i)) ;; 97 'a' + ) + (func (export "8u_good3") (param $i i32) (result i64) + (i64.load8_u offset=1 align=1 (local.get $i)) ;; 98 'b' + ) + (func (export "8u_good4") (param $i i32) (result i64) + (i64.load8_u offset=2 align=1 (local.get $i)) ;; 99 'c' + ) + (func (export "8u_good5") (param $i i32) (result i64) + (i64.load8_u offset=25 align=1 (local.get $i)) ;; 122 'z' + ) + + (func (export "8s_good1") (param $i i32) (result i64) + (i64.load8_s offset=0 (local.get $i)) ;; 97 'a' + ) + (func (export "8s_good2") (param $i i32) (result i64) + (i64.load8_s align=1 (local.get $i)) ;; 97 'a' + ) + (func (export "8s_good3") (param $i i32) (result i64) + (i64.load8_s offset=1 align=1 (local.get $i)) ;; 98 'b' + ) + (func (export "8s_good4") (param $i i32) (result i64) + (i64.load8_s offset=2 align=1 (local.get $i)) ;; 99 'c' + ) + (func (export "8s_good5") (param $i i32) (result i64) + (i64.load8_s offset=25 align=1 (local.get $i)) ;; 122 'z' + ) + + (func (export "16u_good1") (param $i i32) (result i64) + (i64.load16_u offset=0 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16u_good2") (param $i i32) (result i64) + (i64.load16_u align=1 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16u_good3") (param $i i32) (result i64) + (i64.load16_u offset=1 align=1 (local.get $i)) ;; 25442 'bc' + ) + (func (export "16u_good4") (param $i i32) (result i64) + (i64.load16_u offset=2 align=2 (local.get $i)) ;; 25699 'cd' + ) + (func (export "16u_good5") (param $i i32) (result i64) + (i64.load16_u offset=25 align=2 (local.get $i)) ;; 122 'z\0' + ) + + (func (export "16s_good1") (param $i i32) (result i64) + (i64.load16_s offset=0 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16s_good2") (param $i i32) (result i64) + (i64.load16_s align=1 (local.get $i)) ;; 25185 'ab' + ) + (func (export "16s_good3") (param $i i32) (result i64) + (i64.load16_s offset=1 align=1 (local.get $i)) ;; 25442 'bc' + ) + (func (export "16s_good4") (param $i i32) (result i64) + (i64.load16_s offset=2 align=2 (local.get $i)) ;; 25699 'cd' + ) + (func (export "16s_good5") (param $i i32) (result i64) + (i64.load16_s offset=25 align=2 (local.get $i)) ;; 122 'z\0' + ) + + (func (export "32u_good1") (param $i i32) (result i64) + (i64.load32_u offset=0 (local.get $i)) ;; 1684234849 'abcd' + ) + (func (export "32u_good2") (param $i i32) (result i64) + (i64.load32_u align=1 (local.get $i)) ;; 1684234849 'abcd' + ) + (func (export "32u_good3") (param $i i32) (result i64) + (i64.load32_u offset=1 align=1 (local.get $i)) ;; 1701077858 'bcde' + ) + (func (export "32u_good4") (param $i i32) (result i64) + (i64.load32_u offset=2 align=2 (local.get $i)) ;; 1717920867 'cdef' + ) + (func (export "32u_good5") (param $i i32) (result i64) + (i64.load32_u offset=25 align=4 (local.get $i)) ;; 122 'z\0\0\0' + ) + + (func (export "32s_good1") (param $i i32) (result i64) + (i64.load32_s offset=0 (local.get $i)) ;; 1684234849 'abcd' + ) + (func (export "32s_good2") (param $i i32) (result i64) + (i64.load32_s align=1 (local.get $i)) ;; 1684234849 'abcd' + ) + (func (export "32s_good3") (param $i i32) (result i64) + (i64.load32_s offset=1 align=1 (local.get $i)) ;; 1701077858 'bcde' + ) + (func (export "32s_good4") (param $i i32) (result i64) + (i64.load32_s offset=2 align=2 (local.get $i)) ;; 1717920867 'cdef' + ) + (func (export "32s_good5") (param $i i32) (result i64) + (i64.load32_s offset=25 align=4 (local.get $i)) ;; 122 'z\0\0\0' + ) + + (func (export "64_good1") (param $i i32) (result i64) + (i64.load offset=0 (local.get $i)) ;; 0x6867666564636261 'abcdefgh' + ) + (func (export "64_good2") (param $i i32) (result i64) + (i64.load align=1 (local.get $i)) ;; 0x6867666564636261 'abcdefgh' + ) + (func (export "64_good3") (param $i i32) (result i64) + (i64.load offset=1 align=1 (local.get $i)) ;; 0x6968676665646362 'bcdefghi' + ) + (func (export "64_good4") (param $i i32) (result i64) + (i64.load offset=2 align=2 (local.get $i)) ;; 0x6a69686766656463 'cdefghij' + ) + (func (export "64_good5") (param $i i32) (result i64) + (i64.load offset=25 align=8 (local.get $i)) ;; 122 'z\0\0\0\0\0\0\0' + ) + + (func (export "8u_bad") (param $i i32) + (drop (i64.load8_u offset=4294967295 (local.get $i))) + ) + (func (export "8s_bad") (param $i i32) + (drop (i64.load8_s offset=4294967295 (local.get $i))) + ) + (func (export "16u_bad") (param $i i32) + (drop (i64.load16_u offset=4294967295 (local.get $i))) + ) + (func (export "16s_bad") (param $i i32) + (drop (i64.load16_s offset=4294967295 (local.get $i))) + ) + (func (export "32u_bad") (param $i i32) + (drop (i64.load32_u offset=4294967295 (local.get $i))) + ) + (func (export "32s_bad") (param $i i32) + (drop (i64.load32_s offset=4294967295 (local.get $i))) + ) + (func (export "64_bad") (param $i i32) + (drop (i64.load offset=4294967295 (local.get $i))) + ) +) + +(assert_return (invoke "8u_good1" (i32.const 0)) (i64.const 97)) +(assert_return (invoke "8u_good2" (i32.const 0)) (i64.const 97)) +(assert_return (invoke "8u_good3" (i32.const 0)) (i64.const 98)) +(assert_return (invoke "8u_good4" (i32.const 0)) (i64.const 99)) +(assert_return (invoke "8u_good5" (i32.const 0)) (i64.const 122)) + +(assert_return (invoke "8s_good1" (i32.const 0)) (i64.const 97)) +(assert_return (invoke "8s_good2" (i32.const 0)) (i64.const 97)) +(assert_return (invoke "8s_good3" (i32.const 0)) (i64.const 98)) +(assert_return (invoke "8s_good4" (i32.const 0)) (i64.const 99)) +(assert_return (invoke "8s_good5" (i32.const 0)) (i64.const 122)) + +(assert_return (invoke "16u_good1" (i32.const 0)) (i64.const 25185)) +(assert_return (invoke "16u_good2" (i32.const 0)) (i64.const 25185)) +(assert_return (invoke "16u_good3" (i32.const 0)) (i64.const 25442)) +(assert_return (invoke "16u_good4" (i32.const 0)) (i64.const 25699)) +(assert_return (invoke "16u_good5" (i32.const 0)) (i64.const 122)) + +(assert_return (invoke "16s_good1" (i32.const 0)) (i64.const 25185)) +(assert_return (invoke "16s_good2" (i32.const 0)) (i64.const 25185)) +(assert_return (invoke "16s_good3" (i32.const 0)) (i64.const 25442)) +(assert_return (invoke "16s_good4" (i32.const 0)) (i64.const 25699)) +(assert_return (invoke "16s_good5" (i32.const 0)) (i64.const 122)) + +(assert_return (invoke "32u_good1" (i32.const 0)) (i64.const 1684234849)) +(assert_return (invoke "32u_good2" (i32.const 0)) (i64.const 1684234849)) +(assert_return (invoke "32u_good3" (i32.const 0)) (i64.const 1701077858)) +(assert_return (invoke "32u_good4" (i32.const 0)) (i64.const 1717920867)) +(assert_return (invoke "32u_good5" (i32.const 0)) (i64.const 122)) + +(assert_return (invoke "32s_good1" (i32.const 0)) (i64.const 1684234849)) +(assert_return (invoke "32s_good2" (i32.const 0)) (i64.const 1684234849)) +(assert_return (invoke "32s_good3" (i32.const 0)) (i64.const 1701077858)) +(assert_return (invoke "32s_good4" (i32.const 0)) (i64.const 1717920867)) +(assert_return (invoke "32s_good5" (i32.const 0)) (i64.const 122)) + +(assert_return (invoke "64_good1" (i32.const 0)) (i64.const 0x6867666564636261)) +(assert_return (invoke "64_good2" (i32.const 0)) (i64.const 0x6867666564636261)) +(assert_return (invoke "64_good3" (i32.const 0)) (i64.const 0x6968676665646362)) +(assert_return (invoke "64_good4" (i32.const 0)) (i64.const 0x6a69686766656463)) +(assert_return (invoke "64_good5" (i32.const 0)) (i64.const 122)) + +(assert_return (invoke "8u_good1" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "8u_good2" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "8u_good3" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "8u_good4" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "8u_good5" (i32.const 65503)) (i64.const 0)) + +(assert_return (invoke "8s_good1" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "8s_good2" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "8s_good3" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "8s_good4" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "8s_good5" (i32.const 65503)) (i64.const 0)) + +(assert_return (invoke "16u_good1" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "16u_good2" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "16u_good3" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "16u_good4" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "16u_good5" (i32.const 65503)) (i64.const 0)) + +(assert_return (invoke "16s_good1" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "16s_good2" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "16s_good3" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "16s_good4" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "16s_good5" (i32.const 65503)) (i64.const 0)) + +(assert_return (invoke "32u_good1" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "32u_good2" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "32u_good3" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "32u_good4" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "32u_good5" (i32.const 65503)) (i64.const 0)) + +(assert_return (invoke "32s_good1" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "32s_good2" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "32s_good3" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "32s_good4" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "32s_good5" (i32.const 65503)) (i64.const 0)) + +(assert_return (invoke "64_good1" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "64_good2" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "64_good3" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "64_good4" (i32.const 65503)) (i64.const 0)) +(assert_return (invoke "64_good5" (i32.const 65503)) (i64.const 0)) + +(assert_return (invoke "8u_good1" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "8u_good2" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "8u_good3" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "8u_good4" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "8u_good5" (i32.const 65504)) (i64.const 0)) + +(assert_return (invoke "8s_good1" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "8s_good2" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "8s_good3" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "8s_good4" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "8s_good5" (i32.const 65504)) (i64.const 0)) + +(assert_return (invoke "16u_good1" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "16u_good2" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "16u_good3" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "16u_good4" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "16u_good5" (i32.const 65504)) (i64.const 0)) + +(assert_return (invoke "16s_good1" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "16s_good2" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "16s_good3" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "16s_good4" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "16s_good5" (i32.const 65504)) (i64.const 0)) + +(assert_return (invoke "32u_good1" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "32u_good2" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "32u_good3" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "32u_good4" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "32u_good5" (i32.const 65504)) (i64.const 0)) + +(assert_return (invoke "32s_good1" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "32s_good2" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "32s_good3" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "32s_good4" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "32s_good5" (i32.const 65504)) (i64.const 0)) + +(assert_return (invoke "64_good1" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "64_good2" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "64_good3" (i32.const 65504)) (i64.const 0)) +(assert_return (invoke "64_good4" (i32.const 65504)) (i64.const 0)) +(assert_trap (invoke "64_good5" (i32.const 65504)) "out of bounds memory access") + +(assert_trap (invoke "8u_good3" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "8s_good3" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "16u_good3" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "16s_good3" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "32u_good3" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "32s_good3" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "64_good3" (i32.const -1)) "out of bounds memory access") + +(assert_trap (invoke "8u_bad" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "8s_bad" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "16u_bad" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "16s_bad" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "32u_bad" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "32s_bad" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "64_bad" (i32.const 0)) "out of bounds memory access") + +(assert_trap (invoke "8u_bad" (i32.const 1)) "out of bounds memory access") +(assert_trap (invoke "8s_bad" (i32.const 1)) "out of bounds memory access") +(assert_trap (invoke "16u_bad" (i32.const 1)) "out of bounds memory access") +(assert_trap (invoke "16s_bad" (i32.const 1)) "out of bounds memory access") +(assert_trap (invoke "32u_bad" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "32s_bad" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "64_bad" (i32.const 1)) "out of bounds memory access") + +;; Load f32 data with different offset/align arguments + +(module + (memory 1) + (data (i32.const 0) "\00\00\00\00\00\00\a0\7f\01\00\d0\7f") + + (func (export "32_good1") (param $i i32) (result f32) + (f32.load offset=0 (local.get $i)) ;; 0.0 '\00\00\00\00' + ) + (func (export "32_good2") (param $i i32) (result f32) + (f32.load align=1 (local.get $i)) ;; 0.0 '\00\00\00\00' + ) + (func (export "32_good3") (param $i i32) (result f32) + (f32.load offset=1 align=1 (local.get $i)) ;; 0.0 '\00\00\00\00' + ) + (func (export "32_good4") (param $i i32) (result f32) + (f32.load offset=2 align=2 (local.get $i)) ;; 0.0 '\00\00\00\00' + ) + (func (export "32_good5") (param $i i32) (result f32) + (f32.load offset=8 align=4 (local.get $i)) ;; nan:0x500001 '\01\00\d0\7f' + ) + (func (export "32_bad") (param $i i32) + (drop (f32.load offset=4294967295 (local.get $i))) + ) +) + +(assert_return (invoke "32_good1" (i32.const 0)) (f32.const 0.0)) +(assert_return (invoke "32_good2" (i32.const 0)) (f32.const 0.0)) +(assert_return (invoke "32_good3" (i32.const 0)) (f32.const 0.0)) +(assert_return (invoke "32_good4" (i32.const 0)) (f32.const 0.0)) +(assert_return (invoke "32_good5" (i32.const 0)) (f32.const nan:0x500001)) + +(assert_return (invoke "32_good1" (i32.const 65524)) (f32.const 0.0)) +(assert_return (invoke "32_good2" (i32.const 65524)) (f32.const 0.0)) +(assert_return (invoke "32_good3" (i32.const 65524)) (f32.const 0.0)) +(assert_return (invoke "32_good4" (i32.const 65524)) (f32.const 0.0)) +(assert_return (invoke "32_good5" (i32.const 65524)) (f32.const 0.0)) + +(assert_return (invoke "32_good1" (i32.const 65525)) (f32.const 0.0)) +(assert_return (invoke "32_good2" (i32.const 65525)) (f32.const 0.0)) +(assert_return (invoke "32_good3" (i32.const 65525)) (f32.const 0.0)) +(assert_return (invoke "32_good4" (i32.const 65525)) (f32.const 0.0)) +(assert_trap (invoke "32_good5" (i32.const 65525)) "out of bounds memory access") + +(assert_trap (invoke "32_good3" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "32_good3" (i32.const -1)) "out of bounds memory access") + +(assert_trap (invoke "32_bad" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "32_bad" (i32.const 1)) "out of bounds memory access") + +;; Load f64 data with different offset/align arguments + +(module + (memory 1) + (data (i32.const 0) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\f4\7f\01\00\00\00\00\00\fc\7f") + + (func (export "64_good1") (param $i i32) (result f64) + (f64.load offset=0 (local.get $i)) ;; 0.0 '\00\00\00\00\00\00\00\00' + ) + (func (export "64_good2") (param $i i32) (result f64) + (f64.load align=1 (local.get $i)) ;; 0.0 '\00\00\00\00\00\00\00\00' + ) + (func (export "64_good3") (param $i i32) (result f64) + (f64.load offset=1 align=1 (local.get $i)) ;; 0.0 '\00\00\00\00\00\00\00\00' + ) + (func (export "64_good4") (param $i i32) (result f64) + (f64.load offset=2 align=2 (local.get $i)) ;; 0.0 '\00\00\00\00\00\00\00\00' + ) + (func (export "64_good5") (param $i i32) (result f64) + (f64.load offset=18 align=8 (local.get $i)) ;; nan:0xc000000000001 '\01\00\00\00\00\00\fc\7f' + ) + (func (export "64_bad") (param $i i32) + (drop (f64.load offset=4294967295 (local.get $i))) + ) +) + +(assert_return (invoke "64_good1" (i32.const 0)) (f64.const 0.0)) +(assert_return (invoke "64_good2" (i32.const 0)) (f64.const 0.0)) +(assert_return (invoke "64_good3" (i32.const 0)) (f64.const 0.0)) +(assert_return (invoke "64_good4" (i32.const 0)) (f64.const 0.0)) +(assert_return (invoke "64_good5" (i32.const 0)) (f64.const nan:0xc000000000001)) + +(assert_return (invoke "64_good1" (i32.const 65510)) (f64.const 0.0)) +(assert_return (invoke "64_good2" (i32.const 65510)) (f64.const 0.0)) +(assert_return (invoke "64_good3" (i32.const 65510)) (f64.const 0.0)) +(assert_return (invoke "64_good4" (i32.const 65510)) (f64.const 0.0)) +(assert_return (invoke "64_good5" (i32.const 65510)) (f64.const 0.0)) + +(assert_return (invoke "64_good1" (i32.const 65511)) (f64.const 0.0)) +(assert_return (invoke "64_good2" (i32.const 65511)) (f64.const 0.0)) +(assert_return (invoke "64_good3" (i32.const 65511)) (f64.const 0.0)) +(assert_return (invoke "64_good4" (i32.const 65511)) (f64.const 0.0)) +(assert_trap (invoke "64_good5" (i32.const 65511)) "out of bounds memory access") + +(assert_trap (invoke "64_good3" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "64_good3" (i32.const -1)) "out of bounds memory access") + +(assert_trap (invoke "64_bad" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "64_bad" (i32.const 1)) "out of bounds memory access") diff --git a/runtime/unc-vm/tests/wast/spec/align.wast b/runtime/unc-vm/tests/wast/spec/align.wast new file mode 100644 index 000000000..93064649d --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/align.wast @@ -0,0 +1,866 @@ +;; Test alignment annotation rules + +(module (memory 0) (func (drop (i32.load8_s align=1 (i32.const 0))))) +(module (memory 0) (func (drop (i32.load8_u align=1 (i32.const 0))))) +(module (memory 0) (func (drop (i32.load16_s align=2 (i32.const 0))))) +(module (memory 0) (func (drop (i32.load16_u align=2 (i32.const 0))))) +(module (memory 0) (func (drop (i32.load align=4 (i32.const 0))))) +(module (memory 0) (func (drop (i64.load8_s align=1 (i32.const 0))))) +(module (memory 0) (func (drop (i64.load8_u align=1 (i32.const 0))))) +(module (memory 0) (func (drop (i64.load16_s align=2 (i32.const 0))))) +(module (memory 0) (func (drop (i64.load16_u align=2 (i32.const 0))))) +(module (memory 0) (func (drop (i64.load32_s align=4 (i32.const 0))))) +(module (memory 0) (func (drop (i64.load32_u align=4 (i32.const 0))))) +(module (memory 0) (func (drop (i64.load align=8 (i32.const 0))))) +(module (memory 0) (func (drop (f32.load align=4 (i32.const 0))))) +(module (memory 0) (func (drop (f64.load align=8 (i32.const 0))))) +(module (memory 0) (func (i32.store8 align=1 (i32.const 0) (i32.const 1)))) +(module (memory 0) (func (i32.store16 align=2 (i32.const 0) (i32.const 1)))) +(module (memory 0) (func (i32.store align=4 (i32.const 0) (i32.const 1)))) +(module (memory 0) (func (i64.store8 align=1 (i32.const 0) (i64.const 1)))) +(module (memory 0) (func (i64.store16 align=2 (i32.const 0) (i64.const 1)))) +(module (memory 0) (func (i64.store32 align=4 (i32.const 0) (i64.const 1)))) +(module (memory 0) (func (i64.store align=8 (i32.const 0) (i64.const 1)))) +(module (memory 0) (func (f32.store align=4 (i32.const 0) (f32.const 1.0)))) +(module (memory 0) (func (f64.store align=8 (i32.const 0) (f64.const 1.0)))) + +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i32.load8_s align=0 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i32.load8_s align=7 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i32.load8_u align=0 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i32.load8_u align=7 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i32.load16_s align=0 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i32.load16_s align=7 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i32.load16_u align=0 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i32.load16_u align=7 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i32.load align=0 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i32.load align=7 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i64.load8_s align=0 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i64.load8_s align=7 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i64.load8_u align=0 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i64.load8_u align=7 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i64.load16_s align=0 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i64.load16_s align=7 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i64.load16_u align=0 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i64.load16_u align=7 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i64.load32_s align=0 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i64.load32_s align=7 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i64.load32_u align=0 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i64.load32_u align=7 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i64.load align=0 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (i64.load align=7 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (f32.load align=0 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (f32.load align=7 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (f64.load align=0 (i32.const 0)))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (drop (f64.load align=7 (i32.const 0)))))" + ) + "alignment" +) + +(assert_malformed + (module quote + "(module (memory 0) (func (i32.store8 align=0 (i32.const 0) (i32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (i32.store8 align=7 (i32.const 0) (i32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (i32.store16 align=0 (i32.const 0) (i32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (i32.store16 align=7 (i32.const 0) (i32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (i32.store align=0 (i32.const 0) (i32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (i32.store align=7 (i32.const 0) (i32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (i64.store8 align=0 (i32.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (i64.store8 align=7 (i32.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (i64.store16 align=0 (i32.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (i64.store16 align=7 (i32.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (i64.store32 align=0 (i32.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (i64.store32 align=7 (i32.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (i64.store align=0 (i32.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (i64.store align=7 (i32.const 0) (i64.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (f32.store align=0 (i32.const 0) (f32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (f32.store align=7 (i32.const 0) (f32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (f64.store align=0 (i32.const 0) (f32.const 0))))" + ) + "alignment" +) +(assert_malformed + (module quote + "(module (memory 0) (func (f64.store align=7 (i32.const 0) (f32.const 0))))" + ) + "alignment" +) + +(assert_invalid + (module (memory 0) (func (drop (i32.load8_s align=2 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i32.load8_u align=2 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i32.load16_s align=4 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i32.load16_u align=4 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i32.load align=8 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i64.load8_s align=2 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i64.load8_u align=2 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i64.load16_s align=4 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i64.load16_u align=4 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i64.load32_s align=8 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i64.load32_u align=8 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i64.load align=16 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (f32.load align=8 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (f64.load align=16 (i32.const 0))))) + "alignment must not be larger than natural" +) + +(assert_invalid + (module (memory 0) (func (drop (i32.load8_s align=2 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i32.load8_u align=2 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i32.load16_s align=4 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i32.load16_u align=4 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i32.load align=8 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i64.load8_s align=2 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i64.load8_u align=2 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i64.load16_s align=4 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i64.load16_u align=4 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i64.load32_s align=8 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i64.load32_u align=8 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (i64.load align=16 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (f32.load align=8 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (drop (f64.load align=16 (i32.const 0))))) + "alignment must not be larger than natural" +) + +(assert_invalid + (module (memory 0) (func (i32.store8 align=2 (i32.const 0) (i32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (i32.store16 align=4 (i32.const 0) (i32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (i32.store align=8 (i32.const 0) (i32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (i64.store8 align=2 (i32.const 0) (i64.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (i64.store16 align=4 (i32.const 0) (i64.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (i64.store32 align=8 (i32.const 0) (i64.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (i64.store align=16 (i32.const 0) (i64.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (f32.store align=8 (i32.const 0) (f32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func (f64.store align=16 (i32.const 0) (f64.const 0)))) + "alignment must not be larger than natural" +) + +;; Test aligned and unaligned read/write + +(module + (memory 1) + + ;; $default: natural alignment, $1: align=1, $2: align=2, $4: align=4, $8: align=8 + + (func (export "f32_align_switch") (param i32) (result f32) + (local f32 f32) + (local.set 1 (f32.const 10.0)) + (block $4 + (block $2 + (block $1 + (block $default + (block $0 + (br_table $0 $default $1 $2 $4 (local.get 0)) + ) ;; 0 + (f32.store (i32.const 0) (local.get 1)) + (local.set 2 (f32.load (i32.const 0))) + (br $4) + ) ;; default + (f32.store align=1 (i32.const 0) (local.get 1)) + (local.set 2 (f32.load align=1 (i32.const 0))) + (br $4) + ) ;; 1 + (f32.store align=2 (i32.const 0) (local.get 1)) + (local.set 2 (f32.load align=2 (i32.const 0))) + (br $4) + ) ;; 2 + (f32.store align=4 (i32.const 0) (local.get 1)) + (local.set 2 (f32.load align=4 (i32.const 0))) + ) ;; 4 + (local.get 2) + ) + + (func (export "f64_align_switch") (param i32) (result f64) + (local f64 f64) + (local.set 1 (f64.const 10.0)) + (block $8 + (block $4 + (block $2 + (block $1 + (block $default + (block $0 + (br_table $0 $default $1 $2 $4 $8 (local.get 0)) + ) ;; 0 + (f64.store (i32.const 0) (local.get 1)) + (local.set 2 (f64.load (i32.const 0))) + (br $8) + ) ;; default + (f64.store align=1 (i32.const 0) (local.get 1)) + (local.set 2 (f64.load align=1 (i32.const 0))) + (br $8) + ) ;; 1 + (f64.store align=2 (i32.const 0) (local.get 1)) + (local.set 2 (f64.load align=2 (i32.const 0))) + (br $8) + ) ;; 2 + (f64.store align=4 (i32.const 0) (local.get 1)) + (local.set 2 (f64.load align=4 (i32.const 0))) + (br $8) + ) ;; 4 + (f64.store align=8 (i32.const 0) (local.get 1)) + (local.set 2 (f64.load align=8 (i32.const 0))) + ) ;; 8 + (local.get 2) + ) + + ;; $8s: i32/i64.load8_s, $8u: i32/i64.load8_u, $16s: i32/i64.load16_s, $16u: i32/i64.load16_u, $32: i32.load + ;; $32s: i64.load32_s, $32u: i64.load32_u, $64: i64.load + + (func (export "i32_align_switch") (param i32 i32) (result i32) + (local i32 i32) + (local.set 2 (i32.const 10)) + (block $32 + (block $16u + (block $16s + (block $8u + (block $8s + (block $0 + (br_table $0 $8s $8u $16s $16u $32 (local.get 0)) + ) ;; 0 + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i32.store8 (i32.const 0) (local.get 2)) + (local.set 3 (i32.load8_s (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i32.store8 align=1 (i32.const 0) (local.get 2)) + (local.set 3 (i32.load8_s align=1 (i32.const 0))) + ) + ) + (br $32) + ) ;; 8s + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i32.store8 (i32.const 0) (local.get 2)) + (local.set 3 (i32.load8_u (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i32.store8 align=1 (i32.const 0) (local.get 2)) + (local.set 3 (i32.load8_u align=1 (i32.const 0))) + ) + ) + (br $32) + ) ;; 8u + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i32.store16 (i32.const 0) (local.get 2)) + (local.set 3 (i32.load16_s (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i32.store16 align=1 (i32.const 0) (local.get 2)) + (local.set 3 (i32.load16_s align=1 (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i32.store16 align=2 (i32.const 0) (local.get 2)) + (local.set 3 (i32.load16_s align=2 (i32.const 0))) + ) + ) + (br $32) + ) ;; 16s + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i32.store16 (i32.const 0) (local.get 2)) + (local.set 3 (i32.load16_u (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i32.store16 align=1 (i32.const 0) (local.get 2)) + (local.set 3 (i32.load16_u align=1 (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i32.store16 align=2 (i32.const 0) (local.get 2)) + (local.set 3 (i32.load16_u align=2 (i32.const 0))) + ) + ) + (br $32) + ) ;; 16u + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i32.store (i32.const 0) (local.get 2)) + (local.set 3 (i32.load (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i32.store align=1 (i32.const 0) (local.get 2)) + (local.set 3 (i32.load align=1 (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i32.store align=2 (i32.const 0) (local.get 2)) + (local.set 3 (i32.load align=2 (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 4)) + (then + (i32.store align=4 (i32.const 0) (local.get 2)) + (local.set 3 (i32.load align=4 (i32.const 0))) + ) + ) + ) ;; 32 + (local.get 3) + ) + + (func (export "i64_align_switch") (param i32 i32) (result i64) + (local i64 i64) + (local.set 2 (i64.const 10)) + (block $64 + (block $32u + (block $32s + (block $16u + (block $16s + (block $8u + (block $8s + (block $0 + (br_table $0 $8s $8u $16s $16u $32s $32u $64 (local.get 0)) + ) ;; 0 + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i64.store8 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load8_s (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i64.store8 align=1 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load8_s align=1 (i32.const 0))) + ) + ) + (br $64) + ) ;; 8s + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i64.store8 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load8_u (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i64.store8 align=1 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load8_u align=1 (i32.const 0))) + ) + ) + (br $64) + ) ;; 8u + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i64.store16 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load16_s (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i64.store16 align=1 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load16_s align=1 (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i64.store16 align=2 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load16_s align=2 (i32.const 0))) + ) + ) + (br $64) + ) ;; 16s + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i64.store16 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load16_u (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i64.store16 align=1 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load16_u align=1 (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i64.store16 align=2 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load16_u align=2 (i32.const 0))) + ) + ) + (br $64) + ) ;; 16u + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i64.store32 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load32_s (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i64.store32 align=1 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load32_s align=1 (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i64.store32 align=2 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load32_s align=2 (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 4)) + (then + (i64.store32 align=4 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load32_s align=4 (i32.const 0))) + ) + ) + (br $64) + ) ;; 32s + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i64.store32 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load32_u (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i64.store32 align=1 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load32_u align=1 (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i64.store32 align=2 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load32_u align=2 (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 4)) + (then + (i64.store32 align=4 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load32_u align=4 (i32.const 0))) + ) + ) + (br $64) + ) ;; 32u + (if (i32.eq (local.get 1) (i32.const 0)) + (then + (i64.store (i32.const 0) (local.get 2)) + (local.set 3 (i64.load (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 1)) + (then + (i64.store align=1 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load align=1 (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 2)) + (then + (i64.store align=2 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load align=2 (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 4)) + (then + (i64.store align=4 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load align=4 (i32.const 0))) + ) + ) + (if (i32.eq (local.get 1) (i32.const 8)) + (then + (i64.store align=8 (i32.const 0) (local.get 2)) + (local.set 3 (i64.load align=8 (i32.const 0))) + ) + ) + ) ;; 64 + (local.get 3) + ) +) + +(assert_return (invoke "f32_align_switch" (i32.const 0)) (f32.const 10.0)) +(assert_return (invoke "f32_align_switch" (i32.const 1)) (f32.const 10.0)) +(assert_return (invoke "f32_align_switch" (i32.const 2)) (f32.const 10.0)) +(assert_return (invoke "f32_align_switch" (i32.const 3)) (f32.const 10.0)) + +(assert_return (invoke "f64_align_switch" (i32.const 0)) (f64.const 10.0)) +(assert_return (invoke "f64_align_switch" (i32.const 1)) (f64.const 10.0)) +(assert_return (invoke "f64_align_switch" (i32.const 2)) (f64.const 10.0)) +(assert_return (invoke "f64_align_switch" (i32.const 3)) (f64.const 10.0)) +(assert_return (invoke "f64_align_switch" (i32.const 4)) (f64.const 10.0)) + +(assert_return (invoke "i32_align_switch" (i32.const 0) (i32.const 0)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 0) (i32.const 1)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 1) (i32.const 0)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 1) (i32.const 1)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 2) (i32.const 0)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 2) (i32.const 1)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 2) (i32.const 2)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 3) (i32.const 0)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 3) (i32.const 1)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 3) (i32.const 2)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 4) (i32.const 0)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 4) (i32.const 1)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 4) (i32.const 2)) (i32.const 10)) +(assert_return (invoke "i32_align_switch" (i32.const 4) (i32.const 4)) (i32.const 10)) + +(assert_return (invoke "i64_align_switch" (i32.const 0) (i32.const 0)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 0) (i32.const 1)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 1) (i32.const 0)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 1) (i32.const 1)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 2) (i32.const 0)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 2) (i32.const 1)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 2) (i32.const 2)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 3) (i32.const 0)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 3) (i32.const 1)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 3) (i32.const 2)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 4) (i32.const 0)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 4) (i32.const 1)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 4) (i32.const 2)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 4) (i32.const 4)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 5) (i32.const 0)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 5) (i32.const 1)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 5) (i32.const 2)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 5) (i32.const 4)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 6) (i32.const 0)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 6) (i32.const 1)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 6) (i32.const 2)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 6) (i32.const 4)) (i64.const 10)) +(assert_return (invoke "i64_align_switch" (i32.const 6) (i32.const 8)) (i64.const 10)) + +;; Test that an i64 store with 4-byte alignment that's 4 bytes out of bounds traps without storing anything + +(module + (memory 1) + (func (export "store") (param i32 i64) + (i64.store align=4 (local.get 0) (local.get 1)) + ) + (func (export "load") (param i32) (result i32) + (i32.load (local.get 0)) + ) +) + +(assert_trap (invoke "store" (i32.const 65532) (i64.const -1)) "out of bounds memory access") +;; No memory was changed +(assert_return (invoke "load" (i32.const 65532)) (i32.const 0)) diff --git a/runtime/unc-vm/tests/wast/spec/binary-leb128.wast b/runtime/unc-vm/tests/wast/spec/binary-leb128.wast new file mode 100644 index 000000000..1d6721958 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/binary-leb128.wast @@ -0,0 +1,1002 @@ +;; Unsigned LEB128 can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\05\04\01" ;; Memory section with 1 entry + "\00\82\00" ;; no max, minimum 2 +) +(module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\00" ;; no max, minimum 2 +) +(module binary + "\00asm" "\01\00\00\00" + "\05\06\01" ;; Memory section with 1 entry + "\01\82\00" ;; minimum 2 + "\82\00" ;; max 2 +) +(module binary + "\00asm" "\01\00\00\00" + "\05\09\01" ;; Memory section with 1 entry + "\01\82\00" ;; minimum 2 + "\82\80\80\80\00" ;; max 2 +) +(module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; Memory section with 1 entry + "\00\00" ;; no max, minimum 0 + "\0b\07\01" ;; Data section with 1 entry + "\80\00" ;; Memory index 0, encoded with 2 bytes + "\41\00\0b\00" ;; (i32.const 0) with contents "" +) +(module binary + "\00asm" "\01\00\00\00" + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + "\09\09\01" ;; Element section with 1 entry + "\02" ;; Element with explicit table index + "\80\00" ;; Table index 0, encoded with 2 bytes + "\41\00\0b\00\00" ;; (i32.const 0) with no elements +) +(module binary + "\00asm" "\01\00\00\00" + "\00" ;; custom section + "\8a\00" ;; section size 10, encoded with 2 bytes + "\01" ;; name byte count + "1" ;; name + "23456789" ;; sequence of bytes +) +(module binary + "\00asm" "\01\00\00\00" + "\00" ;; custom section + "\0b" ;; section size + "\88\00" ;; name byte count 8, encoded with 2 bytes + "12345678" ;; name + "9" ;; sequence of bytes +) +(module binary + "\00asm" "\01\00\00\00" + "\01\08\01" ;; type section + "\60" ;; func type + "\82\00" ;; num params 2, encoded with 2 bytes + "\7f\7e" ;; param type + "\01" ;; num results + "\7f" ;; result type +) +(module binary + "\00asm" "\01\00\00\00" + "\01\08\01" ;; type section + "\60" ;; func type + "\02" ;; num params + "\7f\7e" ;; param type + "\81\00" ;; num results 1, encoded with 2 bytes + "\7f" ;; result type +) +(module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; function type + "\02\17\01" ;; import section + "\88\00" ;; module name length 8, encoded with 2 bytes + "\73\70\65\63\74\65\73\74" ;; module name + "\09" ;; entity name length + "\70\72\69\6e\74\5f\69\33\32" ;; entity name + "\00" ;; import kind + "\00" ;; import signature index +) +(module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; function type + "\02\17\01" ;; import section + "\08" ;; module name length + "\73\70\65\63\74\65\73\74" ;; module name + "\89\00" ;; entity name length 9, encoded with 2 bytes + "\70\72\69\6e\74\5f\69\33\32" ;; entity name + "\00" ;; import kind + "\00" ;; import signature index +) +(module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; function type + "\02\17\01" ;; import section + "\08" ;; module name length + "\73\70\65\63\74\65\73\74" ;; module name + "\09" ;; entity name length 9 + "\70\72\69\6e\74\5f\69\33\32" ;; entity name + "\00" ;; import kind + "\80\00" ;; import signature index, encoded with 2 bytes +) +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; function type + "\03\03\01" ;; function section + "\80\00" ;; function 0 signature index, encoded with 2 bytes + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body +) +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; fun type + "\03\02\01\00" ;; function section + "\07\07\01" ;; export section + "\82\00" ;; string length 2, encoded with 2 bytes + "\66\31" ;; export name f1 + "\00" ;; export kind + "\00" ;; export func index + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body +) +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; fun type + "\03\02\01\00" ;; function section + "\07\07\01" ;; export section + "\02" ;; string length 2 + "\66\31" ;; export name f1 + "\00" ;; export kind + "\80\00" ;; export func index, encoded with 2 bytes + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body +) +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; fun type + "\03\02\01\00" ;; function section + "\0a" ;; code section + "\05" ;; section size + "\81\00" ;; num functions, encoded with 2 bytes + "\02\00\0b" ;; function body +) + +;; Signed LEB128 can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\00" ;; i32.const 0 + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\7f" ;; i32.const -1 + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\00" ;; i32.const 0 + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\7f" ;; i32.const -1 + "\0b" ;; end +) + +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\00" ;; i64.const 0 with unused bits set + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\7f" ;; i64.const -1 with unused bits unset + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\00" ;; i64.const 0 with unused bits set + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\7f" ;; i64.const -1 with unused bits unset + "\0b" ;; end +) + +;; Unsigned LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\08\01" ;; Memory section with 1 entry + "\00\82\80\80\80\80\00" ;; no max, minimum 2 with one byte too many + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\0a\01" ;; Memory section with 1 entry + "\01\82\00" ;; minimum 2 + "\82\80\80\80\80\00" ;; max 2 with one byte too many + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; Memory section with 1 entry + "\00\00" ;; no max, minimum 0 + "\0b\0b\01" ;; Data section with 1 entry + "\80\80\80\80\80\00" ;; Memory index 0 with one byte too many + "\41\00\0b\00" ;; (i32.const 0) with contents "" + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + "\09\0b\01" ;; Element section with 1 entry + "\80\80\80\80\80\00" ;; Table index 0 with one byte too many + "\41\00\0b\00" ;; (i32.const 0) with no elements + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00" ;; custom section + "\83\80\80\80\80\00" ;; section size 3 with one byte too many + "\01" ;; name byte count + "1" ;; name + "2" ;; sequence of bytes + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00" ;; custom section + "\0A" ;; section size + "\83\80\80\80\80\00" ;; name byte count 3 with one byte too many + "123" ;; name + "4" ;; sequence of bytes + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\0c\01" ;; type section + "\60" ;; func type + "\82\80\80\80\80\00" ;; num params 2 with one byte too many + "\7f\7e" ;; param type + "\01" ;; num result + "\7f" ;; result type + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\08\01" ;; type section + "\60" ;; func type + "\02" ;; num params + "\7f\7e" ;; param type + "\81\80\80\80\80\00" ;; num result 1 with one byte too many + "\7f" ;; result type + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; function type + "\02\1b\01" ;; import section + "\88\80\80\80\80\00" ;; module name length 8 with one byte too many + "\73\70\65\63\74\65\73\74" ;; module name + "\09" ;; entity name length + "\70\72\69\6e\74\5f\69\33\32" ;; entity name + "\00" ;; import kind + "\00" ;; import signature index + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; function type + "\02\1b\01" ;; import section + "\08" ;; module name length + "\73\70\65\63\74\65\73\74" ;; module name + "\89\80\80\80\80\00" ;; entity name length 9 with one byte too many + "\70\72\69\6e\74\5f\69\33\32" ;; entity name + "\00" ;; import kind + "\00" ;; import signature index + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; function type + "\02\1b\01" ;; import section + "\08" ;; module name length + "\73\70\65\63\74\65\73\74" ;; module name + "\09" ;; entity name length 9 + "\70\72\69\6e\74\5f\69\33\32" ;; entity name + "\00" ;; import kind + "\80\80\80\80\80\00" ;; import signature index 0 with one byte too many + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; function type + "\03\03\01" ;; function section + "\80\80\80\80\80\00" ;; function 0 signature index with one byte too many + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; fun type + "\03\02\01\00" ;; function section + "\07\0b\01" ;; export section + "\82\80\80\80\80\00" ;; string length 2 with one byte too many + "\66\31" ;; export name f1 + "\00" ;; export kind + "\00" ;; export func index + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; fun type + "\03\02\01\00" ;; function section + "\07\0b\01" ;; export section + "\02" ;; string length 2 + "\66\31" ;; export name f1 + "\00" ;; export kind + "\80\80\80\80\80\00" ;; export func index 0 with one byte too many + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; fun type + "\03\02\01\00" ;; function section + "\0a" ;; code section + "\05" ;; section size + "\81\80\80\80\80\00" ;; num functions 1 with one byte too many + "\02\00\0b" ;; function body + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\02" ;; alignment 2 + "\82\80\80\80\80\00" ;; offset 2 with one byte too many + "\1a" ;; drop + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\82\80\80\80\80\00" ;; alignment 2 with one byte too many + "\00" ;; offset 0 + "\1a" ;; drop + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\12\01" ;; Code section + ;; function 0 + "\10\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\82\80\80\80\80\00" ;; alignment 2 with one byte too many + "\03" ;; offset 3 + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\12\01" ;; Code section + ;; function 0 + "\10\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\02" ;; alignment 2 + "\82\80\80\80\80\00" ;; offset 2 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +;; Signed LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\80\00" ;; i32.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\ff\7f" ;; i32.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\80\00" ;; i64.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\7f" ;; i64.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +;; Unsigned LEB128s zero-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\70" ;; no max, minimum 2 with unused bits set + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\40" ;; no max, minimum 2 with some unused bits set + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\09\01" ;; Memory section with 1 entry + "\01\82\00" ;; minimum 2 + "\82\80\80\80\10" ;; max 2 with unused bits set + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\09\01" ;; Memory section with 1 entry + "\01\82\00" ;; minimum 2 + "\82\80\80\80\40" ;; max 2 with some unused bits set + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; Memory section with 1 entry + "\00\00" ;; no max, minimum 0 + "\0b\0a\01" ;; Data section with 1 entry + "\80\80\80\80\10" ;; Memory index 0 with unused bits set + "\41\00\0b\00" ;; (i32.const 0) with contents "" + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + "\09\0a\01" ;; Element section with 1 entry + "\80\80\80\80\10" ;; Table index 0 with unused bits set + "\41\00\0b\00" ;; (i32.const 0) with no elements + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00" ;; custom section + "\83\80\80\80\10" ;; section size 3 with unused bits set + "\01" ;; name byte count + "1" ;; name + "2" ;; sequence of bytes + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00" ;; custom section + "\09" ;; section size + "\83\80\80\80\40" ;; name byte count 3 with unused bits set + "123" ;; name + "4" ;; sequence of bytes + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\0b\01" ;; type section + "\60" ;; func type + "\82\80\80\80\10" ;; num params 2 with unused bits set + "\7f\7e" ;; param type + "\01" ;; num result + "\7f" ;; result type + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\0b\01" ;; type section + "\60" ;; func type + "\02" ;; num params + "\7f\7e" ;; param type + "\81\80\80\80\40" ;; num result 1 with unused bits set + "\7f" ;; result type + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; function type + "\02\1a\01" ;; import section + "\88\80\80\80\10" ;; module name length 8 with unused bits set + "\73\70\65\63\74\65\73\74" ;; module name + "\09" ;; entity name length + "\70\72\69\6e\74\5f\69\33\32" ;; entity name + "\00" ;; import kind + "\00" ;; import signature index + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; function type + "\02\1a\01" ;; import section + "\08" ;; module name length + "\73\70\65\63\74\65\73\74" ;; module name + "\89\80\80\80\40" ;; entity name length 9 with unused bits set + "\70\72\69\6e\74\5f\69\33\32" ;; entity name + "\00" ;; import kind + "\00" ;; import signature index + ) + "integer too large" +) +(assert_malformed +(module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; function type + "\02\1a\01" ;; import section + "\08" ;; module name length + "\73\70\65\63\74\65\73\74" ;; module name + "\09" ;; entity name length 9 + "\70\72\69\6e\74\5f\69\33\32" ;; entity name + "\00" ;; import kind + "\80\80\80\80\10" ;; import signature index 0 with unused bits set +) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; function type + "\03\06\01" ;; function section + "\80\80\80\80\10" ;; function 0 signature index with unused bits set + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body + ) + "integer too large" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; fun type + "\03\02\01\00" ;; function section + "\07\0a\01" ;; export section + "\82\80\80\80\10" ;; string length 2 with unused bits set + "\66\31" ;; export name f1 + "\00" ;; export kind + "\00" ;; export func index + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; fun type + "\03\02\01\00" ;; function section + "\07\0a\01" ;; export section + "\02" ;; string length 2 + "\66\31" ;; export name f1 + "\00" ;; export kind + "\80\80\80\80\10" ;; export func index with unused bits set + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; fun type + "\03\02\01\00" ;; function section + "\0a" ;; code section + "\08" ;; section size + "\81\80\80\80\10" ;; num functions 1 with unused bits set + "\02\00\0b" ;; function body + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + ;; function 0 + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\02" ;; alignment 2 + "\82\80\80\80\10" ;; offset 2 with unused bits set + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + ;; function 0 + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\02" ;; alignment 2 + "\82\80\80\80\40" ;; offset 2 with some unused bits set + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\82\80\80\80\10" ;; alignment 2 with unused bits set + "\00" ;; offset 0 + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + ;; function 0 + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\82\80\80\80\40" ;; alignment 2 with some unused bits set + "\00" ;; offset 0 + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\82\80\80\80\10" ;; alignment 2 with unused bits set + "\03" ;; offset 3 + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\82\80\80\80\40" ;; alignment 2 with some unused bits set + "\03" ;; offset 3 + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\02" ;; alignment 2 + "\82\80\80\80\10" ;; offset 2 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\02" ;; alignment 2 + "\82\80\80\80\40" ;; offset 2 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) + +;; Signed LEB128s sign-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\70" ;; i32.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\0f" ;; i32.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\1f" ;; i32.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\4f" ;; i32.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\7e" ;; i64.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\01" ;; i64.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\02" ;; i64.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\41" ;; i64.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + + +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; empty function type + "\03\02\01" ;; function section + "\00" ;; function 0, type 0 + "\0a\1b\01\19" ;; code section + "\00" ;; no locals + "\00" ;; unreachable + "\fc\80\00" ;; i32_trunc_sat_f32_s with 2 bytes + "\00" ;; unreachable + "\fc\81\80\00" ;; i32_trunc_sat_f32_u with 3 bytes + "\00" ;; unreachable + "\fc\86\80\80\00" ;; i64_trunc_sat_f64_s with 4 bytes + "\00" ;; unreachable + "\fc\87\80\80\80\00" ;; i64_trunc_sat_f64_u with 5 bytes + "\00" ;; unreachable + "\0b" ;; end +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; empty function type + "\03\02\01" ;; function section + "\00" ;; function 0, type 0 + "\0a\0d\01\0b" ;; code section + "\00" ;; no locals + "\00" ;; unreachable + "\fc\87\80\80\80\80\00" ;; i64_trunc_sat_f64_u with 6 bytes + "\00" ;; unreachable + "\0b" ;; end + ) + "integer representation too long" +) diff --git a/runtime/unc-vm/tests/wast/spec/binary.wast b/runtime/unc-vm/tests/wast/spec/binary.wast new file mode 100644 index 000000000..c6f975570 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/binary.wast @@ -0,0 +1,1764 @@ +(module binary "\00asm\01\00\00\00") +(module binary "\00asm" "\01\00\00\00") +(module $M1 binary "\00asm\01\00\00\00") +(module $M2 binary "\00asm" "\01\00\00\00") + +(assert_malformed (module binary "") "unexpected end") +(assert_malformed (module binary "\01") "unexpected end") +(assert_malformed (module binary "\00as") "unexpected end") +(assert_malformed (module binary "asm\00") "magic header not detected") +(assert_malformed (module binary "msa\00") "magic header not detected") +(assert_malformed (module binary "msa\00\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "msa\00\00\00\00\01") "magic header not detected") +(assert_malformed (module binary "asm\01\00\00\00\00") "magic header not detected") +(assert_malformed (module binary "wasm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\7fasm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\80asm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\82asm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\ffasm\01\00\00\00") "magic header not detected") + +;; 8-byte endian-reversed. +(assert_malformed (module binary "\00\00\00\01msa\00") "magic header not detected") + +;; Middle-endian byte orderings. +(assert_malformed (module binary "a\00ms\00\01\00\00") "magic header not detected") +(assert_malformed (module binary "sm\00a\00\00\01\00") "magic header not detected") + +;; Upper-cased. +(assert_malformed (module binary "\00ASM\01\00\00\00") "magic header not detected") + +;; EBCDIC-encoded magic. +(assert_malformed (module binary "\00\81\a2\94\01\00\00\00") "magic header not detected") + +;; Leading UTF-8 BOM. +(assert_malformed (module binary "\ef\bb\bf\00asm\01\00\00\00") "magic header not detected") + +;; Malformed binary version. +(assert_malformed (module binary "\00asm") "unexpected end") +(assert_malformed (module binary "\00asm\01") "unexpected end") +(assert_malformed (module binary "\00asm\01\00\00") "unexpected end") +(assert_malformed (module binary "\00asm\00\00\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\0d\00\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\0e\00\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\00\01\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\00\00\01\00") "unknown binary version") +(assert_malformed (module binary "\00asm\00\00\00\01") "unknown binary version") + +;; Invalid section id. +(assert_malformed (module binary "\00asm" "\01\00\00\00" "\0d\00") "malformed section id") +(assert_malformed (module binary "\00asm" "\01\00\00\00" "\7f\00") "malformed section id") +(assert_malformed (module binary "\00asm" "\01\00\00\00" "\80\00\01\00") "malformed section id") +(assert_malformed (module binary "\00asm" "\01\00\00\00" "\81\00\01\00") "malformed section id") +(assert_malformed (module binary "\00asm" "\01\00\00\00" "\ff\00\01\00") "malformed section id") + +;; Unsigned LEB128 can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\05\04\01" ;; Memory section with 1 entry + "\00\82\00" ;; no max, minimum 2 +) +(module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\00" ;; no max, minimum 2 +) + +;; Signed LEB128 can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\00" ;; i32.const 0 + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\7f" ;; i32.const -1 + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\00" ;; i32.const 0 + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\7f" ;; i32.const -1 + "\0b" ;; end +) + +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\00" ;; i64.const 0 with unused bits set + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\7f" ;; i64.const -1 with unused bits unset + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\00" ;; i64.const 0 with unused bits set + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\7f" ;; i64.const -1 with unused bits unset + "\0b" ;; end +) + +(module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; Memory section with 1 entry + "\00\00" ;; no max, minimum 0 + "\0b\06\01" ;; Data section with 1 entry + "\00" ;; Memory index 0 + "\41\00\0b\00" ;; (i32.const 0) with contents "" +) + +(module binary + "\00asm" "\01\00\00\00" + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + "\09\06\01" ;; Element section with 1 entry + "\00" ;; Table index 0 + "\41\00\0b\00" ;; (i32.const 0) with no elements +) + +;; Data segment memory index can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; Memory section with 1 entry + "\00\00" ;; no max, minimum 0 + "\0b\07\01" ;; Data section with 1 entry + "\80\00" ;; Memory index 0, encoded with 2 bytes + "\41\00\0b\00" ;; (i32.const 0) with contents "" +) + +;; Element segment table index can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + "\09\09\01" ;; Element section with 1 entry + "\02\80\00" ;; Table index 0, encoded with 2 bytes + "\41\00\0b\00\00" ;; (i32.const 0) with no elements +) + +;; Type section with signed LEB128 encoded type +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01" ;; Type section id + "\05" ;; Type section length + "\01" ;; Types vector length + "\e0\7f" ;; Malformed functype, -0x20 in signed LEB128 encoding + "\00\00" + ) + "integer representation too long" +) + +;; Unsigned LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\08\01" ;; Memory section with 1 entry + "\00\82\80\80\80\80\00" ;; no max, minimum 2 with one byte too many + ) + "integer representation too long" +) + +;; Signed LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\80\00" ;; i32.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\ff\7f" ;; i32.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\80\00" ;; i64.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\7f" ;; i64.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +;; Unsigned LEB128s zero-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\70" ;; no max, minimum 2 with unused bits set + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\40" ;; no max, minimum 2 with some unused bits set + ) + "integer too large" +) + +;; Signed LEB128s sign-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\70" ;; i32.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\0f" ;; i32.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\1f" ;; i32.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\4f" ;; i32.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\7e" ;; i64.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\01" ;; i64.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\02" ;; i64.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\41" ;; i64.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\7e" ;; i64.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\01" ;; i64.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\02" ;; i64.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\41" ;; i64.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + + +;; Unsigned LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\08\01" ;; Memory section with 1 entry + "\00\82\80\80\80\80\00" ;; no max, minimum 2 with one byte too many + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\02" ;; alignment 2 + "\82\80\80\80\80\00" ;; offset 2 with one byte too many + "\1a" ;; drop + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\82\80\80\80\80\00" ;; alignment 2 with one byte too many + "\00" ;; offset 0 + "\1a" ;; drop + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\12\01" ;; Code section + ;; function 0 + "\10\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\82\80\80\80\80\00" ;; alignment 2 with one byte too many + "\03" ;; offset 3 + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\12\01" ;; Code section + ;; function 0 + "\10\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\02" ;; alignment 2 + "\82\80\80\80\80\00" ;; offset 2 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +;; Signed LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\80\00" ;; i32.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\ff\7f" ;; i32.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\80\00" ;; i64.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\7f" ;; i64.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +;; Unsigned LEB128s zero-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\70" ;; no max, minimum 2 with unused bits set + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\40" ;; no max, minimum 2 with some unused bits set + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + ;; function 0 + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\02" ;; alignment 2 + "\82\80\80\80\10" ;; offset 2 with unused bits set + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + ;; function 0 + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\02" ;; alignment 2 + "\82\80\80\80\40" ;; offset 2 with some unused bits set + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\82\80\80\80\10" ;; alignment 2 with unused bits set + "\00" ;; offset 0 + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + ;; function 0 + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\82\80\80\80\40" ;; alignment 2 with some unused bits set + "\00" ;; offset 0 + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\82\80\80\80\10" ;; alignment 2 with unused bits set + "\03" ;; offset 3 + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\82\80\80\80\40" ;; alignment 2 with some unused bits set + "\03" ;; offset 3 + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\02" ;; alignment 2 + "\82\80\80\80\10" ;; offset 2 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\02" ;; alignment 2 + "\82\80\80\80\40" ;; offset 2 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) + +;; Signed LEB128s sign-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\70" ;; i32.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\0f" ;; i32.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\1f" ;; i32.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\4f" ;; i32.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\7e" ;; i64.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\01" ;; i64.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\02" ;; i64.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\41" ;; i64.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + +;; memory.grow reserved byte equal to zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\09\01" ;; Code section + + ;; function 0 + "\07\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\01" ;; memory.grow reserved byte is not equal to zero! + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +;; memory.grow reserved byte should not be a "long" LEB128 zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\08\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +;; Same as above for 3, 4, and 5-byte zero encodings. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0b\01" ;; Code section + + ;; function 0 + "\09\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\0a\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\80\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0d\01" ;; Code section + + ;; function 0 + "\0b\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\80\80\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +;; memory.size reserved byte equal to zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\07\01" ;; Code section + + ;; function 0 + "\05\00" + "\3f" ;; memory.size + "\01" ;; memory.size reserved byte is not equal to zero! + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +;; memory.size reserved byte should not be a "long" LEB128 zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\08\01" ;; Code section + + ;; function 0 + "\06\00" + "\3f" ;; memory.size + "\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +;; Same as above for 3, 4, and 5-byte zero encodings. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\09\01" ;; Code section + + ;; function 0 + "\07\00" + "\3f" ;; memory.size + "\80\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\08\00" + "\3f" ;; memory.size + "\80\80\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0b\01" ;; Code section + + ;; function 0 + "\09\00" + "\3f" ;; memory.size + "\80\80\80\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +;; Local number is unsigned 32 bit +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\0a\02" + "\80\80\80\80\10\7f" ;; 0x100000000 i32 + "\02\7e" ;; 0x00000002 i64 + "\0b" ;; end + ) + "integer too large" +) + +;; Local number is unsigned 32 bit +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\0a\02" + "\80\80\80\80\10\7f" ;; 0x100000000 i32 + "\02\7e" ;; 0x00000002 i64 + "\0b" ;; end + ) + "integer too large" +) + +;; No more than 2^32-1 locals. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\0a\02" + "\ff\ff\ff\ff\0f\7f" ;; 0xFFFFFFFF i32 + "\02\7e" ;; 0x00000002 i64 + "\0b" ;; end + ) + "too many locals" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\06\01\60\02\7f\7f\00" ;; Type section: (param i32 i32) + "\03\02\01\00" ;; Function section + "\0a\1c\01" ;; Code section + + ;; function 0 + "\1a\04" + "\80\80\80\80\04\7f" ;; 0x40000000 i32 + "\80\80\80\80\04\7e" ;; 0x40000000 i64 + "\80\80\80\80\04\7d" ;; 0x40000000 f32 + "\80\80\80\80\04\7c" ;; 0x40000000 f64 + "\0b" ;; end + ) + "too many locals" +) + +;; Local count can be 0. +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\08\03" + "\00\7f" ;; 0 i32 + "\00\7e" ;; 0 i64 + "\02\7d" ;; 2 f32 + "\0b" ;; end +) + +;; Function section has non-zero count, but code section is absent. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\03\02\00\00" ;; Function section with 2 functions + ) + "function and code section have inconsistent lengths" +) + +;; Code section has non-zero count, but function section is absent. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0a\04\01\02\00\0b" ;; Code section with 1 empty function + ) + "function and code section have inconsistent lengths" +) + +;; Function section count > code section count +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\03\02\00\00" ;; Function section with 2 functions + "\0a\04\01\02\00\0b" ;; Code section with 1 empty function + ) + "function and code section have inconsistent lengths" +) + +;; Function section count < code section count +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section with 1 function + "\0a\07\02\02\00\0b\02\00\0b" ;; Code section with 2 empty functions + ) + "function and code section have inconsistent lengths" +) + +;; Function section has zero count, and code section is absent. +(module binary + "\00asm" "\01\00\00\00" + "\03\01\00" ;; Function section with 0 functions +) + +;; Code section has zero count, and function section is absent. +(module binary + "\00asm" "\01\00\00\00" + "\0a\01\00" ;; Code section with 0 functions +) + +;; Fewer passive segments than datacount +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0c\01\03" ;; Datacount section with value "3" + "\0b\05\02" ;; Data section with two entries + "\01\00" ;; Passive data section + "\01\00") ;; Passive data section + "data count and data section have inconsistent lengths") + +;; More passive segments than datacount +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0c\01\01" ;; Datacount section with value "1" + "\0b\05\02" ;; Data section with two entries + "\01\00" ;; Passive data section + "\01\00") ;; Passive data section + "data count and data section have inconsistent lengths") + +;; memory.init requires a datacount section +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0e\01" ;; Code section + + ;; function 0 + "\0c\00" + "\41\00" ;; zero args + "\41\00" + "\41\00" + "\fc\08\00\00" ;; memory.init + "\0b" + + "\0b\03\01\01\00" ;; Data section + ) ;; end + "data count section required") + +;; data.drop requires a datacount section +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\07\01" ;; Code section + + ;; function 0 + "\05\00" + "\fc\09\00" ;; data.drop + "\0b" + + "\0b\03\01\01\00" ;; Data section + ) ;; end + "data count section required") + +;; passive element segment containing opcode other than ref.func or ref.null +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\07\01" ;; Element section with one segment + "\05\70" ;; Passive, funcref + "\01" ;; 1 element + "\d3\00\0b" ;; bad opcode, index 0, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + "illegal opcode") + +;; passive element segment containing type other than funcref +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\07\01" ;; Element section with one segment + "\05\7f" ;; Passive, i32 + "\01" ;; 1 element + "\d2\00\0b" ;; ref.func, index 0, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + "malformed reference type") + +;; passive element segment containing opcode ref.func +(module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\07\01" ;; Element section with one segment + "\05\70" ;; Passive, funcref + "\01" ;; 1 element + "\d2\00\0b" ;; ref.func, index 0, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + +;; passive element segment containing opcode ref.null +(module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\07\01" ;; Element section with one segment + "\05\70" ;; Passive, funcref + "\01" ;; 1 element + "\d0\70\0b" ;; ref.null, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + + +;; Type count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\01\00" ;; type count can be zero +) + +;; 2 type declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\07\02" ;; type section with inconsistent count (2 declared, 1 given) + "\60\00\00" ;; 1st type + ;; "\60\00\00" ;; 2nd type (missed) + ) + "unexpected end of section or function" +) + +;; 1 type declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\07\01" ;; type section with inconsistent count (1 declared, 2 given) + "\60\00\00" ;; 1st type + "\60\00\00" ;; 2nd type (redundant) + ) + "section size mismatch" +) + +;; Import count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; type 0 + "\02\01\00" ;; import count can be zero +) + +;; Malformed import kind +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\04\01" ;; import section with single entry + "\00" ;; string length 0 + "\00" ;; string length 0 + "\04" ;; malformed import kind + ) + "malformed import kind" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\05\01" ;; import section with single entry + "\00" ;; string length 0 + "\00" ;; string length 0 + "\04" ;; malformed import kind + "\00" ;; dummy byte + ) + "malformed import kind" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\04\01" ;; import section with single entry + "\00" ;; string length 0 + "\00" ;; string length 0 + "\05" ;; malformed import kind + ) + "malformed import kind" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\05\01" ;; import section with single entry + "\00" ;; string length 0 + "\00" ;; string length 0 + "\05" ;; malformed import kind + "\00" ;; dummy byte + ) + "malformed import kind" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\04\01" ;; import section with single entry + "\00" ;; string length 0 + "\00" ;; string length 0 + "\80" ;; malformed import kind + ) + "malformed import kind" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\05\01" ;; import section with single entry + "\00" ;; string length 0 + "\00" ;; string length 0 + "\80" ;; malformed import kind + "\00" ;; dummy byte + ) + "malformed import kind" +) + +;; 2 import declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; type 0 + "\02\16\02" ;; import section with inconsistent count (2 declared, 1 given) + ;; 1st import + "\08" ;; string length + "\73\70\65\63\74\65\73\74" ;; spectest + "\09" ;; string length + "\70\72\69\6e\74\5f\69\33\32" ;; print_i32 + "\00\00" ;; import kind, import signature index + ;; 2nd import + ;; (missed) + ) + "unexpected end of section or function" +) + +;; 1 import declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\09\02" ;; type section + "\60\01\7f\00" ;; type 0 + "\60\01\7d\00" ;; type 1 + "\02\2b\01" ;; import section with inconsistent count (1 declared, 2 given) + ;; 1st import + "\08" ;; string length + "\73\70\65\63\74\65\73\74" ;; spectest + "\09" ;; string length + "\70\72\69\6e\74\5f\69\33\32" ;; print_i32 + "\00\00" ;; import kind, import signature index + ;; 2nd import + ;; (redundant) + "\08" ;; string length + "\73\70\65\63\74\65\73\74" ;; spectest + "\09" ;; string length + "\70\72\69\6e\74\5f\66\33\32" ;; print_f32 + "\00\01" ;; import kind, import signature index + ) + "section size mismatch" +) + +;; Table count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\04\01\00" ;; table count can be zero +) + +;; 1 table declared, 0 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\04\01\01" ;; table section with inconsistent count (1 declared, 0 given) + ;; "\70\01\00\00" ;; table entity + ) + "unexpected end of section or function" +) + +;; Malformed table limits flag +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; table section with one entry + "\70" ;; anyfunc + "\02" ;; malformed table limits flag + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\04\01" ;; table section with one entry + "\70" ;; anyfunc + "\02" ;; malformed table limits flag + "\00" ;; dummy byte + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\06\01" ;; table section with one entry + "\70" ;; anyfunc + "\81\00" ;; malformed table limits flag as LEB128 + "\00\00" ;; dummy bytes + ) + "integer too large" +) + +;; Memory count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\05\01\00" ;; memory count can be zero +) + +;; 1 memory declared, 0 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\01\01" ;; memory section with inconsistent count (1 declared, 0 given) + ;; "\00\00" ;; memory 0 (missed) + ) + "unexpected end of section or function" +) + +;; Malformed memory limits flag +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\02\01" ;; memory section with one entry + "\02" ;; malformed memory limits flag + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section with one entry + "\02" ;; malformed memory limits flag + "\00" ;; dummy byte + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\05\01" ;; memory section with one entry + "\81\00" ;; malformed memory limits flag as LEB128 + "\00\00" ;; dummy bytes + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\05\01" ;; memory section with one entry + "\81\01" ;; malformed memory limits flag as LEB128 + "\00\00" ;; dummy bytes + ) + "integer representation too long" +) + +;; Global count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\06\01\00" ;; global count can be zero +) + +;; 2 global declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\06\02" ;; global section with inconsistent count (2 declared, 1 given) + "\7f\00\41\00\0b" ;; global 0 + ;; "\7f\00\41\00\0b" ;; global 1 (missed) + ) + "unexpected end of section or function" +) + +;; 1 global declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; global section with inconsistent count (1 declared, 2 given) + "\7f\00\41\00\0b" ;; global 0 + "\7f\00\41\00\0b" ;; global 1 (redundant) + ) + "section size mismatch" +) + +;; Export count can be 0 +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\03\02\00\00" ;; func section + "\07\01\00" ;; export count can be zero + "\0a\07\02" ;; code section + "\02\00\0b" ;; function body 0 + "\02\00\0b" ;; function body 1 +) + +;; 2 export declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\03\02\00\00" ;; func section + "\07\06\02" ;; export section with inconsistent count (2 declared, 1 given) + "\02" ;; export 0 + "\66\31" ;; export name + "\00\00" ;; export kind, export func index + ;; "\02" ;; export 1 (missed) + ;; "\66\32" ;; export name + ;; "\00\01" ;; export kind, export func index + "\0a\07\02" ;; code section + "\02\00\0b" ;; function body 0 + "\02\00\0b" ;; function body 1 + ) + "unexpected end of section or function" +) + +;; 1 export declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\03\02\00\00" ;; func section + "\07\0b\01" ;; export section with inconsistent count (1 declared, 2 given) + "\02" ;; export 0 + "\66\31" ;; export name + "\00\00" ;; export kind, export func index + "\02" ;; export 1 (redundant) + "\66\32" ;; export name + "\00\01" ;; export kind, export func index + "\0a\07\02" ;; code section + "\02\00\0b" ;; function body 0 + "\02\00\0b" ;; function body 1 + ) + "section size mismatch" +) + +;; elem segment count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\01\00" ;; elem segment count can be zero + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body +) + +;; 2 elem segment declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\07\02" ;; elem with inconsistent segment count (2 declared, 1 given) + "\00\41\00\0b\01\00" ;; elem 0 + ;; "\00\41\00\0b\01\00" ;; elem 1 (missed) + ) + "unexpected end" +) + +;; 2 elem segment declared, 1.5 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\07\02" ;; elem with inconsistent segment count (2 declared, 1 given) + "\00\41\00\0b\01\00" ;; elem 0 + "\00\41\00" ;; elem 1 (partial) + ;; "\0b\01\00" ;; elem 1 (missing part) + ) + "unexpected end" +) + +;; 1 elem segment declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\0d\01" ;; elem with inconsistent segment count (1 declared, 2 given) + "\00\41\00\0b\01\00" ;; elem 0 + "\00\41\00\0b\01\00" ;; elem 1 (redundant) + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body + ) + "section size mismatch" +) + +;; data segment count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\01\00" ;; data segment count can be zero +) + +;; 2 data segment declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\07\02" ;; data with inconsistent segment count (2 declared, 1 given) + "\00\41\00\0b\01\61" ;; data 0 + ;; "\00\41\01\0b\01\62" ;; data 1 (missed) + ) + "unexpected end of section or function" +) + +;; 1 data segment declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\0d\01" ;; data with inconsistent segment count (1 declared, 2 given) + "\00\41\00\0b\01\61" ;; data 0 + "\00\41\01\0b\01\62" ;; data 1 (redundant) + ) + "section size mismatch" +) + +;; data segment has 7 bytes declared, but 6 bytes given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\0c\01" ;; data section + "\00\41\03\0b" ;; data segment 0 + "\07" ;; data segment size with inconsistent lengths (7 declared, 6 given) + "\61\62\63\64\65\66" ;; 6 bytes given + ) + "unexpected end of section or function" +) + +;; data segment has 5 bytes declared, but 6 bytes given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\0c\01" ;; data section + "\00\41\00\0b" ;; data segment 0 + "\05" ;; data segment size with inconsistent lengths (5 declared, 6 given) + "\61\62\63\64\65\66" ;; 6 bytes given + ) + "section size mismatch" +) + +;; br_table target count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\0a\11\01" ;; code section + "\0f\00" ;; func 0 + "\02\40" ;; block 0 + "\41\01" ;; condition of if 0 + "\04\40" ;; if 0 + "\41\01" ;; index of br_table element + "\0e\00" ;; br_table target count can be zero + "\02" ;; break depth for default + "\0b\0b\0b" ;; end +) + +;; 1 br_table target declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\0a\12\01" ;; code section + "\11\00" ;; func 0 + "\02\40" ;; block 0 + "\41\01" ;; condition of if 0 + "\04\40" ;; if 0 + "\41\01" ;; index of br_table element + "\0e\01" ;; br_table with inconsistent target count (1 declared, 2 given) + "\00" ;; break depth 0 + "\01" ;; break depth 1 + "\02" ;; break depth for default + "\0b\0b\0b" ;; end + ) + "unexpected end" +) + +;; Start section +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\08\01\00" ;; Start section: function 0 + + "\0a\04\01" ;; Code section + ;; function 0 + "\02\00" + "\0b" ;; end +) + +;; Multiple start sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\08\01\00" ;; Start section: function 0 + "\08\01\00" ;; Start section: function 0 + + "\0a\04\01" ;; Code section + ;; function 0 + "\02\00" + "\0b" ;; end + ) + "junk after last section" +) diff --git a/runtime/unc-vm/tests/wast/spec/block.wast b/runtime/unc-vm/tests/wast/spec/block.wast new file mode 100644 index 000000000..44915b991 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/block.wast @@ -0,0 +1,1491 @@ +;; Test `block` operator + +(module + ;; Auxiliary definition + (memory 1) + + (func $dummy) + + (func (export "empty") + (block) + (block $l) + ) + + (func (export "singular") (result i32) + (block (nop)) + (block (result i32) (i32.const 7)) + ) + + (func (export "multi") (result i32) + (block (call $dummy) (call $dummy) (call $dummy) (call $dummy)) + (block (result i32) + (call $dummy) (call $dummy) (call $dummy) (i32.const 7) (call $dummy) + ) + (drop) + (block (result i32 i64 i32) + (call $dummy) (call $dummy) (call $dummy) (i32.const 8) (call $dummy) + (call $dummy) (call $dummy) (call $dummy) (i64.const 7) (call $dummy) + (call $dummy) (call $dummy) (call $dummy) (i32.const 9) (call $dummy) + ) + (drop) (drop) + ) + + (func (export "nested") (result i32) + (block (result i32) + (block (call $dummy) (block) (nop)) + (block (result i32) (call $dummy) (i32.const 9)) + ) + ) + + (func (export "deep") (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (call $dummy) (i32.const 150) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + ) + + (func (export "as-select-first") (result i32) + (select (block (result i32) (i32.const 1)) (i32.const 2) (i32.const 3)) + ) + (func (export "as-select-mid") (result i32) + (select (i32.const 2) (block (result i32) (i32.const 1)) (i32.const 3)) + ) + (func (export "as-select-last") (result i32) + (select (i32.const 2) (i32.const 3) (block (result i32) (i32.const 1))) + ) + + (func (export "as-loop-first") (result i32) + (loop (result i32) (block (result i32) (i32.const 1)) (call $dummy) (call $dummy)) + ) + (func (export "as-loop-mid") (result i32) + (loop (result i32) (call $dummy) (block (result i32) (i32.const 1)) (call $dummy)) + ) + (func (export "as-loop-last") (result i32) + (loop (result i32) (call $dummy) (call $dummy) (block (result i32) (i32.const 1))) + ) + + (func (export "as-if-condition") + (block (result i32) (i32.const 1)) (if (then (call $dummy))) + ) + (func (export "as-if-then") (result i32) + (if (result i32) (i32.const 1) (then (block (result i32) (i32.const 1))) (else (i32.const 2))) + ) + (func (export "as-if-else") (result i32) + (if (result i32) (i32.const 1) (then (i32.const 2)) (else (block (result i32) (i32.const 1)))) + ) + + (func (export "as-br_if-first") (result i32) + (block (result i32) (br_if 0 (block (result i32) (i32.const 1)) (i32.const 2))) + ) + (func (export "as-br_if-last") (result i32) + (block (result i32) (br_if 0 (i32.const 2) (block (result i32) (i32.const 1)))) + ) + + (func (export "as-br_table-first") (result i32) + (block (result i32) (block (result i32) (i32.const 1)) (i32.const 2) (br_table 0 0)) + ) + (func (export "as-br_table-last") (result i32) + (block (result i32) (i32.const 2) (block (result i32) (i32.const 1)) (br_table 0 0)) + ) + + (func $func (param i32 i32) (result i32) (local.get 0)) + (type $check (func (param i32 i32) (result i32))) + (table funcref (elem $func)) + (func (export "as-call_indirect-first") (result i32) + (block (result i32) + (call_indirect (type $check) + (block (result i32) (i32.const 1)) (i32.const 2) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 2) (block (result i32) (i32.const 1)) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 1) (i32.const 2) (block (result i32) (i32.const 0)) + ) + ) + ) + + (func (export "as-store-first") + (block (result i32) (i32.const 1)) (i32.const 1) (i32.store) + ) + (func (export "as-store-last") + (i32.const 10) (block (result i32) (i32.const 1)) (i32.store) + ) + + (func (export "as-memory.grow-value") (result i32) + (memory.grow (block (result i32) (i32.const 1))) + ) + + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "as-call-value") (result i32) + (call $f (block (result i32) (i32.const 1))) + ) + (func (export "as-return-value") (result i32) + (block (result i32) (i32.const 1)) (return) + ) + (func (export "as-drop-operand") + (drop (block (result i32) (i32.const 1))) + ) + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (block (result i32) (i32.const 1)))) + ) + (func (export "as-local.set-value") (result i32) + (local i32) (local.set 0 (block (result i32) (i32.const 1))) (local.get 0) + ) + (func (export "as-local.tee-value") (result i32) + (local i32) (local.tee 0 (block (result i32) (i32.const 1))) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (result i32) + (global.set $a (block (result i32) (i32.const 1))) + (global.get $a) + ) + + (func (export "as-load-operand") (result i32) + (i32.load (block (result i32) (i32.const 1))) + ) + + (func (export "as-unary-operand") (result i32) + (i32.ctz (block (result i32) (call $dummy) (i32.const 13))) + ) + (func (export "as-binary-operand") (result i32) + (i32.mul + (block (result i32) (call $dummy) (i32.const 3)) + (block (result i32) (call $dummy) (i32.const 4)) + ) + ) + (func (export "as-test-operand") (result i32) + (i32.eqz (block (result i32) (call $dummy) (i32.const 13))) + ) + (func (export "as-compare-operand") (result i32) + (f32.gt + (block (result f32) (call $dummy) (f32.const 3)) + (block (result f32) (call $dummy) (f32.const 3)) + ) + ) + (func (export "as-binary-operands") (result i32) + (i32.mul + (block (result i32 i32) + (call $dummy) (i32.const 3) (call $dummy) (i32.const 4) + ) + ) + ) + (func (export "as-compare-operands") (result i32) + (f32.gt + (block (result f32 f32) + (call $dummy) (f32.const 3) (call $dummy) (f32.const 3) + ) + ) + ) + (func (export "as-mixed-operands") (result i32) + (block (result i32 i32) + (call $dummy) (i32.const 3) (call $dummy) (i32.const 4) + ) + (i32.const 5) + (i32.add) + (i32.mul) + ) + + (func (export "break-bare") (result i32) + (block (br 0) (unreachable)) + (block (br_if 0 (i32.const 1)) (unreachable)) + (block (br_table 0 (i32.const 0)) (unreachable)) + (block (br_table 0 0 0 (i32.const 1)) (unreachable)) + (i32.const 19) + ) + (func (export "break-value") (result i32) + (block (result i32) (br 0 (i32.const 18)) (i32.const 19)) + ) + (func (export "break-multi-value") (result i32 i32 i64) + (block (result i32 i32 i64) + (br 0 (i32.const 18) (i32.const -18) (i64.const 18)) + (i32.const 19) (i32.const -19) (i64.const 19) + ) + ) + (func (export "break-repeated") (result i32) + (block (result i32) + (br 0 (i32.const 18)) + (br 0 (i32.const 19)) + (drop (br_if 0 (i32.const 20) (i32.const 0))) + (drop (br_if 0 (i32.const 20) (i32.const 1))) + (br 0 (i32.const 21)) + (br_table 0 (i32.const 22) (i32.const 4)) + (br_table 0 0 0 (i32.const 23) (i32.const 1)) + (i32.const 21) + ) + ) + (func (export "break-inner") (result i32) + (local i32) + (local.set 0 (i32.const 0)) + (local.set 0 (i32.add (local.get 0) (block (result i32) (block (result i32) (br 1 (i32.const 0x1)))))) + (local.set 0 (i32.add (local.get 0) (block (result i32) (block (br 0)) (i32.const 0x2)))) + (local.set 0 + (i32.add (local.get 0) (block (result i32) (i32.ctz (br 0 (i32.const 0x4))))) + ) + (local.set 0 + (i32.add (local.get 0) (block (result i32) (i32.ctz (block (result i32) (br 1 (i32.const 0x8)))))) + ) + (local.get 0) + ) + + (func (export "param") (result i32) + (i32.const 1) + (block (param i32) (result i32) + (i32.const 2) + (i32.add) + ) + ) + (func (export "params") (result i32) + (i32.const 1) + (i32.const 2) + (block (param i32 i32) (result i32) + (i32.add) + ) + ) + (func (export "params-id") (result i32) + (i32.const 1) + (i32.const 2) + (block (param i32 i32) (result i32 i32)) + (i32.add) + ) + (func (export "param-break") (result i32) + (i32.const 1) + (block (param i32) (result i32) + (i32.const 2) + (i32.add) + (br 0) + ) + ) + (func (export "params-break") (result i32) + (i32.const 1) + (i32.const 2) + (block (param i32 i32) (result i32) + (i32.add) + (br 0) + ) + ) + (func (export "params-id-break") (result i32) + (i32.const 1) + (i32.const 2) + (block (param i32 i32) (result i32 i32) (br 0)) + (i32.add) + ) + + (func (export "effects") (result i32) + (local i32) + (block + (local.set 0 (i32.const 1)) + (local.set 0 (i32.mul (local.get 0) (i32.const 3))) + (local.set 0 (i32.sub (local.get 0) (i32.const 5))) + (local.set 0 (i32.mul (local.get 0) (i32.const 7))) + (br 0) + (local.set 0 (i32.mul (local.get 0) (i32.const 100))) + ) + (i32.eq (local.get 0) (i32.const -14)) + ) + + (type $block-sig-1 (func)) + (type $block-sig-2 (func (result i32))) + (type $block-sig-3 (func (param $x i32))) + (type $block-sig-4 (func (param i32 f64 i32) (result i32 f64 i32))) + + (func (export "type-use") + (block (type $block-sig-1)) + (block (type $block-sig-2) (i32.const 0)) + (block (type $block-sig-3) (drop)) + (i32.const 0) (f64.const 0) (i32.const 0) + (block (type $block-sig-4)) + (drop) (drop) (drop) + (block (type $block-sig-2) (result i32) (i32.const 0)) + (block (type $block-sig-3) (param i32) (drop)) + (i32.const 0) (f64.const 0) (i32.const 0) + (block (type $block-sig-4) + (param i32) (param f64 i32) (result i32 f64) (result i32) + ) + (drop) (drop) (drop) + ) +) + +(assert_return (invoke "empty")) +(assert_return (invoke "singular") (i32.const 7)) +(assert_return (invoke "multi") (i32.const 8)) +(assert_return (invoke "nested") (i32.const 9)) +(assert_return (invoke "deep") (i32.const 150)) + +(assert_return (invoke "as-select-first") (i32.const 1)) +(assert_return (invoke "as-select-mid") (i32.const 2)) +(assert_return (invoke "as-select-last") (i32.const 2)) + +(assert_return (invoke "as-loop-first") (i32.const 1)) +(assert_return (invoke "as-loop-mid") (i32.const 1)) +(assert_return (invoke "as-loop-last") (i32.const 1)) + +(assert_return (invoke "as-if-condition")) +(assert_return (invoke "as-if-then") (i32.const 1)) +(assert_return (invoke "as-if-else") (i32.const 2)) + +(assert_return (invoke "as-br_if-first") (i32.const 1)) +(assert_return (invoke "as-br_if-last") (i32.const 2)) + +(assert_return (invoke "as-br_table-first") (i32.const 1)) +(assert_return (invoke "as-br_table-last") (i32.const 2)) + +(assert_return (invoke "as-call_indirect-first") (i32.const 1)) +(assert_return (invoke "as-call_indirect-mid") (i32.const 2)) +(assert_return (invoke "as-call_indirect-last") (i32.const 1)) + +(assert_return (invoke "as-store-first")) +(assert_return (invoke "as-store-last")) + +(assert_return (invoke "as-memory.grow-value") (i32.const 1)) +(assert_return (invoke "as-call-value") (i32.const 1)) +(assert_return (invoke "as-return-value") (i32.const 1)) +(assert_return (invoke "as-drop-operand")) +(assert_return (invoke "as-br-value") (i32.const 1)) +(assert_return (invoke "as-local.set-value") (i32.const 1)) +(assert_return (invoke "as-local.tee-value") (i32.const 1)) +(assert_return (invoke "as-global.set-value") (i32.const 1)) +(assert_return (invoke "as-load-operand") (i32.const 1)) + +(assert_return (invoke "as-unary-operand") (i32.const 0)) +(assert_return (invoke "as-binary-operand") (i32.const 12)) +(assert_return (invoke "as-test-operand") (i32.const 0)) +(assert_return (invoke "as-compare-operand") (i32.const 0)) +(assert_return (invoke "as-binary-operands") (i32.const 12)) +(assert_return (invoke "as-compare-operands") (i32.const 0)) +(assert_return (invoke "as-mixed-operands") (i32.const 27)) + +(assert_return (invoke "break-bare") (i32.const 19)) +(assert_return (invoke "break-value") (i32.const 18)) +(assert_return (invoke "break-multi-value") + (i32.const 18) (i32.const -18) (i64.const 18) +) +(assert_return (invoke "break-repeated") (i32.const 18)) +(assert_return (invoke "break-inner") (i32.const 0xf)) + +(assert_return (invoke "param") (i32.const 3)) +(assert_return (invoke "params") (i32.const 3)) +(assert_return (invoke "params-id") (i32.const 3)) +(assert_return (invoke "param-break") (i32.const 3)) +(assert_return (invoke "params-break") (i32.const 3)) +(assert_return (invoke "params-id-break") (i32.const 3)) + +(assert_return (invoke "effects") (i32.const 1)) + +(assert_return (invoke "type-use")) + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (type $sig) (result i32) (param i32)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (param i32) (type $sig) (result i32)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (param i32) (result i32) (type $sig)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (result i32) (type $sig) (param i32)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (result i32) (param i32) (type $sig)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func (i32.const 0) (block (result i32) (param i32)))" + ) + "unexpected token" +) + +(assert_malformed + (module quote "(func (i32.const 0) (block (param $x i32) (drop)))") + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func))" + "(func (block (type $sig) (result i32) (i32.const 0)) (unreachable))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (block (type $sig) (result i32) (i32.const 0)) (unreachable))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (type $sig) (param i32) (drop)) (unreachable))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(func (i32.const 0) (block (type $sig) (param i32) (result i32)) (unreachable))" + ) + "inline function type" +) + +(assert_invalid + (module + (type $sig (func)) + (func (block (type $sig) (i32.const 0))) + ) + "type mismatch" +) + +(assert_invalid + (module (func $type-empty-i32 (result i32) (block))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-i64 (result i64) (block))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f32 (result f32) (block))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f64 (result f64) (block))) + "type mismatch" +) + +(assert_invalid + (module (func $type-value-i32-vs-void + (block (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-i64-vs-void + (block (i64.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f32-vs-void + (block (f32.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f64-vs-void + (block (f64.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-nums-vs-void + (block (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-empty-vs-i32 (result i32) + (block (result i32)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-empty-vs-i64 (result i64) + (block (result i64)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-empty-vs-f32 (result f32) + (block (result f32)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-empty-vs-f64 (result f64) + (block (result f64)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-empty-vs-nums (result i32 i32) + (block (result i32 i32)) + )) + "type mismatch" +) + +(assert_invalid + (module + (func $type-value-empty-in-block + (i32.const 0) + (block (block (result i32)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-in-loop + (i32.const 0) + (loop (block (result i32)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-in-then + (i32.const 0) (i32.const 0) + (if (then (block (result i32)) (drop))) + ) + ) + "type mismatch" +) + +(assert_invalid + (module (func $type-value-void-vs-i32 (result i32) + (block (result i32) (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-void-vs-i64 (result i64) + (block (result i64) (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-void-vs-f32 (result f32) + (block (result f32) (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-void-vs-f64 (result f64) + (block (result f64) (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-void-vs-nums (result i32 i32) + (block (result i32 i32) (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-i32-vs-i64 (result i32) + (block (result i32) (i64.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-i32-vs-f32 (result i32) + (block (result i32) (f32.const 0.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-i32-vs-f64 (result i32) + (block (result i32) (f64.const 0.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-i64-vs-i32 (result i64) + (block (result i64) (i32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-i64-vs-f32 (result i64) + (block (result i64) (f32.const 0.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-i64-vs-f64 (result i64) + (block (result i64) (f64.const 0.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f32-vs-i32 (result f32) + (block (result f32) (i32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f32-vs-i64 (result f32) + (block (result f32) (i64.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f32-vs-f64 (result f32) + (block (result f32) (f64.const 0.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f64-vs-i32 (result f64) + (block (result f64) (i32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f64-vs-i64 (result f64) + (block (result f64) (i64.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f64-vs-f32 (result f32) + (block (result f64) (f32.const 0.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-num-vs-nums (result i32 i32) + (block (result i32 i32) (i32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-partial-vs-nums (result i32 i32) + (i32.const 1) (block (result i32 i32) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-nums-vs-num (result i32) + (block (result i32) (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-value-unreached-select-i32-i64 (result i32) + (block (result i64) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-i32-f32 (result i32) + (block (result f32) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-i32-f64 (result i32) + (block (result f64) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-i64-i32 (result i64) + (block (result i32) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-i64-f32 (result i64) + (block (result f32) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-i64-f64 (result i64) + (block (result f64) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-f32-i32 (result f32) + (block (result i32) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-f32-i64 (result f32) + (block (result i64) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-f32-f64 (result f32) + (block (result f64) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-f64-i32 (result f64) + (block (result i32) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-f64-i64 (result f64) + (block (result i64) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-f64-f32 (result f64) + (block (result f32) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-last-void-vs-i32 (result i32) + (block (result i32) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-void-vs-i64 (result i64) + (block (result i64) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-void-vs-f32 (result f32) + (block (result f32) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-void-vs-f64 (result f64) + (block (result f64) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-void-vs-nums (result i32 i32) + (block (result i32 i32) (br 0)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-empty-vs-i32 (result i32) + (block (result i32) (br 0) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-empty-vs-i64 (result i64) + (block (result i64) (br 0) (i64.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-empty-vs-f32 (result f32) + (block (result f32) (br 0) (f32.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-empty-vs-f64 (result f64) + (block (result f64) (br 0) (f64.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-empty-vs-nums (result i32 i32) + (block (result i32 i32) (br 0) (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-void-vs-i32 (result i32) + (block (result i32) (br 0 (nop)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-void-vs-i64 (result i64) + (block (result i64) (br 0 (nop)) (i64.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-void-vs-f32 (result f32) + (block (result f32) (br 0 (nop)) (f32.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-void-vs-f64 (result f64) + (block (result f64) (br 0 (nop)) (f64.const 1.0)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-i32-vs-i64 (result i32) + (block (result i32) (br 0 (i64.const 1)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-i32-vs-f32 (result i32) + (block (result i32) (br 0 (f32.const 1.0)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-i32-vs-f64 (result i32) + (block (result i32) (br 0 (f64.const 1.0)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-i64-vs-i32 (result i64) + (block (result i64) (br 0 (i32.const 1)) (i64.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-i64-vs-f32 (result i64) + (block (result i64) (br 0 (f32.const 1.0)) (i64.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-i64-vs-f64 (result i64) + (block (result i64) (br 0 (f64.const 1.0)) (i64.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-f32-vs-i32 (result f32) + (block (result f32) (br 0 (i32.const 1)) (f32.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-f32-vs-i64 (result f32) + (block (result f32) (br 0 (i64.const 1)) (f32.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-f32-vs-f64 (result f32) + (block (result f32) (br 0 (f64.const 1.0)) (f32.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-f64-vs-i32 (result f64) + (block (result i64) (br 0 (i32.const 1)) (f64.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-f64-vs-i64 (result f64) + (block (result f64) (br 0 (i64.const 1)) (f64.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-f64-vs-f32 (result f64) + (block (result f64) (br 0 (f32.const 1.0)) (f64.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-num-vs-nums (result i32 i32) + (block (result i32 i32) (br 0 (i32.const 0)) (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-partial-vs-nums (result i32 i32) + (i32.const 1) (block (result i32 i32) (br 0 (i32.const 0)) (i32.const 2)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-first-void-vs-i32 (result i32) + (block (result i32) (br 0 (nop)) (br 0 (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-void-vs-i64 (result i64) + (block (result i64) (br 0 (nop)) (br 0 (i64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-void-vs-f32 (result f32) + (block (result f32) (br 0 (nop)) (br 0 (f32.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-void-vs-f64 (result f64) + (block (result f64) (br 0 (nop)) (br 0 (f64.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-void-vs-nums (result i32 i32) + (block (result i32 i32) (br 0 (nop)) (br 0 (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-first-i32-vs-i64 (result i32) + (block (result i32) (br 0 (i64.const 1)) (br 0 (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-i32-vs-f32 (result i32) + (block (result i32) (br 0 (f32.const 1.0)) (br 0 (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-i32-vs-f64 (result i32) + (block (result i32) (br 0 (f64.const 1.0)) (br 0 (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-i64-vs-i32 (result i64) + (block (result i64) (br 0 (i32.const 1)) (br 0 (i64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-i64-vs-f32 (result i64) + (block (result i64) (br 0 (f32.const 1.0)) (br 0 (i64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-i64-vs-f64 (result i64) + (block (result i64) (br 0 (f64.const 1.0)) (br 0 (i64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-f32-vs-i32 (result f32) + (block (result f32) (br 0 (i32.const 1)) (br 0 (f32.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-f32-vs-i64 (result f32) + (block (result f32) (br 0 (i64.const 1)) (br 0 (f32.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-f32-vs-f64 (result f32) + (block (result f32) (br 0 (f64.const 1.0)) (br 0 (f32.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-f64-vs-i32 (result f64) + (block (result f64) (br 0 (i32.const 1)) (br 0 (f64.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-f64-vs-i64 (result f64) + (block (result f64) (br 0 (i64.const 1)) (br 0 (f64.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-f64-vs-f32 (result f64) + (block (result f64) (br 0 (f32.const 1.0)) (br 0 (f64.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-num-vs-nums (result i32 i32) + (block (result i32 i32) (br 0 (i32.const 0)) (br 0 (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-nested-i32-vs-void + (block (result i32) (block (result i32) (br 1 (i32.const 1))) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-i64-vs-void + (block (result i64) (block (result i64) (br 1 (i64.const 1))) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f32-vs-void + (block (result f32) (block (result f32) (br 1 (f32.const 1.0))) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f64-vs-void + (block (result f64) (block (result f64) (br 1 (f64.const 1.0))) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-nums-vs-void + (block (result i32 i32) (block (result i32 i32) (br 1 (i32.const 1) (i32.const 2))) (br 0)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-nested-empty-vs-i32 (result i32) + (block (result i32) (block (br 1)) (br 0 (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-empty-vs-i64 (result i64) + (block (result i64) (block (br 1)) (br 0 (i64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-empty-vs-f32 (result f32) + (block (result f32) (block (br 1)) (br 0 (f32.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-empty-vs-f64 (result f64) + (block (result f64) (block (br 1)) (br 0 (f64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-empty-vs-nums (result i32 i32) + (block (result i32 i32) (block (br 1)) (br 0 (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-nested-void-vs-i32 (result i32) + (block (result i32) (block (result i32) (br 1 (nop))) (br 0 (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-void-vs-i64 (result i64) + (block (result i64) (block (result i64) (br 1 (nop))) (br 0 (i64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-void-vs-f32 (result f32) + (block (result f32) (block (result f32) (br 1 (nop))) (br 0 (f32.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-void-vs-f64 (result f64) + (block (result f64) (block (result f64) (br 1 (nop))) (br 0 (f64.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-void-vs-nums (result i32 i32) + (block (result i32 i32) (block (result i32 i32) (br 1 (nop))) (br 0 (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-nested-i32-vs-i64 (result i32) + (block (result i32) + (block (result i32) (br 1 (i64.const 1))) (br 0 (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-i32-vs-f32 (result i32) + (block (result i32) + (block (result i32) (br 1 (f32.const 1.0))) (br 0 (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-i32-vs-f64 (result i32) + (block (result i32) + (block (result i32) (br 1 (f64.const 1.0))) (br 0 (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-i64-vs-i32 (result i64) + (block (result i64) + (block (result i64) (br 1 (i32.const 1))) (br 0 (i64.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-i64-vs-f32 (result i64) + (block (result i64) + (block (result i64) (br 1 (f32.const 1.0))) (br 0 (i64.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-i64-vs-f64 (result i64) + (block (result i64) + (block (result i64) (br 1 (f64.const 1.0))) (br 0 (i64.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f32-vs-i32 (result f32) + (block (result f32) + (block (result f32) (br 1 (i32.const 1))) (br 0 (f32.const 1.0)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f32-vs-i64 (result f32) + (block (result f32) + (block (result f32) (br 1 (i64.const 1))) (br 0 (f32.const 1.0)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f32-vs-f64 (result f32) + (block (result f32) + (block (result f32) (br 1 (f64.const 1.0))) (br 0 (f32.const 1.0)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f64-vs-i32 (result f64) + (block (result f64) + (block (result f64) (br 1 (i32.const 1))) (br 0 (f64.const 1.0)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f64-vs-i64 (result f64) + (block (result f64) + (block (result f64) (br 1 (i64.const 1))) (br 0 (f64.const 1.0)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f64-vs-f32 (result f64) + (block (result f64) + (block (result f64) (br 1 (f32.const 1.0))) (br 0 (f64.const 1.0)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-num-vs-nums (result i32 i32) + (block (result i32 i32) + (block (result i32 i32) (br 1 (i32.const 0))) (br 0 (i32.const 1) (i32.const 2)) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-operand-empty-vs-i32 (result i32) + (i32.ctz (block (br 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-empty-vs-i64 (result i64) + (i64.ctz (block (br 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-empty-vs-f32 (result f32) + (f32.floor (block (br 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-empty-vs-f64 (result f64) + (f64.floor (block (br 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-empty-vs-nums (result i32) + (i32.add (block (br 0))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-operand-void-vs-i32 (result i32) + (i32.ctz (block (br 0 (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-void-vs-i64 (result i64) + (i64.ctz (block (br 0 (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-void-vs-f32 (result f32) + (f32.floor (block (br 0 (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-void-vs-f64 (result f64) + (f64.floor (block (br 0 (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-void-vs-nums (result i32) + (i32.add (block (br 0 (nop)))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-operand-i32-vs-i64 (result i32) + (i64.ctz (block (br 0 (i64.const 9)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-i32-vs-f32 (result i32) + (f32.floor (block (br 0 (f32.const 9.0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-i32-vs-f64 (result i32) + (f64.floor (block (br 0 (f64.const 9.0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-i64-vs-i32 (result i64) + (i32.ctz (block (br 0 (i32.const 9)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-i64-vs-f32 (result i64) + (f32.floor (block (br 0 (f32.const 9.0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-i64-vs-f64 (result i64) + (f64.floor (block (br 0 (f64.const 9.0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-f32-vs-i32 (result f32) + (i32.ctz (block (br 0 (i32.const 9)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-f32-vs-i64 (result f32) + (i64.ctz (block (br 0 (i64.const 9)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-f32-vs-f64 (result f32) + (f64.floor (block (br 0 (f64.const 9.0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-f64-vs-i32 (result f64) + (i32.ctz (block (br 0 (i32.const 9)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-f64-vs-i64 (result f64) + (i64.ctz (block (br 0 (i64.const 9)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-f64-vs-f32 (result f64) + (f32.floor (block (br 0 (f32.const 9.0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-num-vs-nums (result i32) + (i32.add (block (br 0 (i64.const 9) (i32.const 10)))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-param-void-vs-num + (block (param i32) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (block (param i32 f64) (drop) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (f32.const 0) (block (param i32) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (f32.const 0) (block (param f32 i32) (drop) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-nested-void-vs-num + (block (block (param i32) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (block (block (param i32 f64) (drop) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (block (f32.const 0) (block (param i32) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (block (f32.const 0) (block (param f32 i32) (drop) (drop))) + )) + "type mismatch" +) + +(assert_malformed + (module quote "(func (param i32) (result i32) block (param $x i32) end)") + "unexpected token" +) +(assert_malformed + (module quote "(func (param i32) (result i32) (block (param $x i32)))") + "unexpected token" +) + + +(assert_malformed + (module quote "(func block end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func block $a end $l)") + "mismatching label" +) diff --git a/runtime/unc-vm/tests/wast/spec/br.wast b/runtime/unc-vm/tests/wast/spec/br.wast new file mode 100644 index 000000000..35f707c92 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/br.wast @@ -0,0 +1,657 @@ +;; Test `br` operator + +(module + ;; Auxiliary definition + (func $dummy) + + (func (export "type-i32") (block (drop (i32.ctz (br 0))))) + (func (export "type-i64") (block (drop (i64.ctz (br 0))))) + (func (export "type-f32") (block (drop (f32.neg (br 0))))) + (func (export "type-f64") (block (drop (f64.neg (br 0))))) + (func (export "type-i32-i32") (block (drop (i32.add (br 0))))) + (func (export "type-i64-i64") (block (drop (i64.add (br 0))))) + (func (export "type-f32-f32") (block (drop (f32.add (br 0))))) + (func (export "type-f64-f64") (block (drop (f64.add (br 0))))) + + (func (export "type-i32-value") (result i32) + (block (result i32) (i32.ctz (br 0 (i32.const 1)))) + ) + (func (export "type-i64-value") (result i64) + (block (result i64) (i64.ctz (br 0 (i64.const 2)))) + ) + (func (export "type-f32-value") (result f32) + (block (result f32) (f32.neg (br 0 (f32.const 3)))) + ) + (func (export "type-f64-value") (result f64) + (block (result f64) (f64.neg (br 0 (f64.const 4)))) + ) + (func (export "type-f64-f64-value") (result f64 f64) + (block (result f64 f64) + (f64.add (br 0 (f64.const 4) (f64.const 5))) (f64.const 6) + ) + ) + + (func (export "as-block-first") + (block (br 0) (call $dummy)) + ) + (func (export "as-block-mid") + (block (call $dummy) (br 0) (call $dummy)) + ) + (func (export "as-block-last") + (block (nop) (call $dummy) (br 0)) + ) + (func (export "as-block-value") (result i32) + (block (result i32) (nop) (call $dummy) (br 0 (i32.const 2))) + ) + + (func (export "as-loop-first") (result i32) + (block (result i32) (loop (result i32) (br 1 (i32.const 3)) (i32.const 2))) + ) + (func (export "as-loop-mid") (result i32) + (block (result i32) + (loop (result i32) (call $dummy) (br 1 (i32.const 4)) (i32.const 2)) + ) + ) + (func (export "as-loop-last") (result i32) + (block (result i32) + (loop (result i32) (nop) (call $dummy) (br 1 (i32.const 5))) + ) + ) + + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (br 0 (i32.const 9)))) + ) + + (func (export "as-br_if-cond") + (block (br_if 0 (br 0))) + ) + (func (export "as-br_if-value") (result i32) + (block (result i32) + (drop (br_if 0 (br 0 (i32.const 8)) (i32.const 1))) (i32.const 7) + ) + ) + (func (export "as-br_if-value-cond") (result i32) + (block (result i32) + (drop (br_if 0 (i32.const 6) (br 0 (i32.const 9)))) (i32.const 7) + ) + ) + + (func (export "as-br_table-index") + (block (br_table 0 0 0 (br 0))) + ) + (func (export "as-br_table-value") (result i32) + (block (result i32) + (br_table 0 0 0 (br 0 (i32.const 10)) (i32.const 1)) (i32.const 7) + ) + ) + (func (export "as-br_table-value-index") (result i32) + (block (result i32) + (br_table 0 0 (i32.const 6) (br 0 (i32.const 11))) (i32.const 7) + ) + ) + + (func (export "as-return-value") (result i64) + (block (result i64) (return (br 0 (i64.const 7)))) + ) + (func (export "as-return-values") (result i32 i64) + (i32.const 2) + (block (result i64) (return (br 0 (i32.const 1) (i64.const 7)))) + ) + + (func (export "as-if-cond") (result i32) + (block (result i32) + (if (result i32) (br 0 (i32.const 2)) + (then (i32.const 0)) + (else (i32.const 1)) + ) + ) + ) + (func (export "as-if-then") (param i32 i32) (result i32) + (block (result i32) + (if (result i32) (local.get 0) + (then (br 1 (i32.const 3))) + (else (local.get 1)) + ) + ) + ) + (func (export "as-if-else") (param i32 i32) (result i32) + (block (result i32) + (if (result i32) (local.get 0) + (then (local.get 1)) + (else (br 1 (i32.const 4))) + ) + ) + ) + + (func (export "as-select-first") (param i32 i32) (result i32) + (block (result i32) + (select (br 0 (i32.const 5)) (local.get 0) (local.get 1)) + ) + ) + (func (export "as-select-second") (param i32 i32) (result i32) + (block (result i32) + (select (local.get 0) (br 0 (i32.const 6)) (local.get 1)) + ) + ) + (func (export "as-select-cond") (result i32) + (block (result i32) + (select (i32.const 0) (i32.const 1) (br 0 (i32.const 7))) + ) + ) + (func (export "as-select-all") (result i32) + (block (result i32) (select (br 0 (i32.const 8)))) + ) + + (func $f (param i32 i32 i32) (result i32) (i32.const -1)) + (func (export "as-call-first") (result i32) + (block (result i32) + (call $f (br 0 (i32.const 12)) (i32.const 2) (i32.const 3)) + ) + ) + (func (export "as-call-mid") (result i32) + (block (result i32) + (call $f (i32.const 1) (br 0 (i32.const 13)) (i32.const 3)) + ) + ) + (func (export "as-call-last") (result i32) + (block (result i32) + (call $f (i32.const 1) (i32.const 2) (br 0 (i32.const 14))) + ) + ) + (func (export "as-call-all") (result i32) + (block (result i32) (call $f (br 0 (i32.const 15)))) + ) + + (type $sig (func (param i32 i32 i32) (result i32))) + (table funcref (elem $f)) + (func (export "as-call_indirect-func") (result i32) + (block (result i32) + (call_indirect (type $sig) + (br 0 (i32.const 20)) + (i32.const 1) (i32.const 2) (i32.const 3) + ) + ) + ) + (func (export "as-call_indirect-first") (result i32) + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) + (br 0 (i32.const 21)) (i32.const 2) (i32.const 3) + ) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) + (i32.const 1) (br 0 (i32.const 22)) (i32.const 3) + ) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) + (i32.const 1) (i32.const 2) (br 0 (i32.const 23)) + ) + ) + ) + (func (export "as-call_indirect-all") (result i32) + (block (result i32) (call_indirect (type $sig) (br 0 (i32.const 24)))) + ) + + (func (export "as-local.set-value") (result i32) (local f32) + (block (result i32) (local.set 0 (br 0 (i32.const 17))) (i32.const -1)) + ) + (func (export "as-local.tee-value") (result i32) (local i32) + (block (result i32) (local.tee 0 (br 0 (i32.const 1)))) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (result i32) + (block (result i32) (global.set $a (br 0 (i32.const 1)))) + ) + + (memory 1) + (func (export "as-load-address") (result f32) + (block (result f32) (f32.load (br 0 (f32.const 1.7)))) + ) + (func (export "as-loadN-address") (result i64) + (block (result i64) (i64.load8_s (br 0 (i64.const 30)))) + ) + + (func (export "as-store-address") (result i32) + (block (result i32) + (f64.store (br 0 (i32.const 30)) (f64.const 7)) (i32.const -1) + ) + ) + (func (export "as-store-value") (result i32) + (block (result i32) + (i64.store (i32.const 2) (br 0 (i32.const 31))) (i32.const -1) + ) + ) + (func (export "as-store-both") (result i32) + (block (result i32) + (i64.store (br 0 (i32.const 32))) (i32.const -1) + ) + ) + + (func (export "as-storeN-address") (result i32) + (block (result i32) + (i32.store8 (br 0 (i32.const 32)) (i32.const 7)) (i32.const -1) + ) + ) + (func (export "as-storeN-value") (result i32) + (block (result i32) + (i64.store16 (i32.const 2) (br 0 (i32.const 33))) (i32.const -1) + ) + ) + (func (export "as-storeN-both") (result i32) + (block (result i32) + (i64.store16 (br 0 (i32.const 34))) (i32.const -1) + ) + ) + + (func (export "as-unary-operand") (result f32) + (block (result f32) (f32.neg (br 0 (f32.const 3.4)))) + ) + + (func (export "as-binary-left") (result i32) + (block (result i32) (i32.add (br 0 (i32.const 3)) (i32.const 10))) + ) + (func (export "as-binary-right") (result i64) + (block (result i64) (i64.sub (i64.const 10) (br 0 (i64.const 45)))) + ) + (func (export "as-binary-both") (result i32) + (block (result i32) (i32.add (br 0 (i32.const 46)))) + ) + + (func (export "as-test-operand") (result i32) + (block (result i32) (i32.eqz (br 0 (i32.const 44)))) + ) + + (func (export "as-compare-left") (result i32) + (block (result i32) (f64.le (br 0 (i32.const 43)) (f64.const 10))) + ) + (func (export "as-compare-right") (result i32) + (block (result i32) (f32.ne (f32.const 10) (br 0 (i32.const 42)))) + ) + (func (export "as-compare-both") (result i32) + (block (result i32) (f64.le (br 0 (i32.const 44)))) + ) + + (func (export "as-convert-operand") (result i32) + (block (result i32) (i32.wrap_i64 (br 0 (i32.const 41)))) + ) + + (func (export "as-memory.grow-size") (result i32) + (block (result i32) (memory.grow (br 0 (i32.const 40)))) + ) + + (func (export "nested-block-value") (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (call $dummy) + (i32.add (i32.const 4) (br 0 (i32.const 8))) + ) + ) + ) + + (func (export "nested-br-value") (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (block (result i32) + (drop (i32.const 4)) + (br 0 (br 1 (i32.const 8))) + ) + ) + (i32.const 16) + ) + ) + ) + + (func (export "nested-br_if-value") (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (block (result i32) + (drop (i32.const 4)) + (drop (br_if 0 (br 1 (i32.const 8)) (i32.const 1))) + (i32.const 32) + ) + ) + (i32.const 16) + ) + ) + ) + + (func (export "nested-br_if-value-cond") (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop (br_if 0 (i32.const 4) (br 0 (i32.const 8)))) + (i32.const 16) + ) + ) + ) + + (func (export "nested-br_table-value") (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (block (result i32) + (drop (i32.const 4)) + (br_table 0 (br 1 (i32.const 8)) (i32.const 1)) + ) + ) + (i32.const 16) + ) + ) + ) + + (func (export "nested-br_table-value-index") (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (br_table 0 (i32.const 4) (br 0 (i32.const 8))) + (i32.const 16) + ) + ) + ) +) + +(assert_return (invoke "type-i32")) +(assert_return (invoke "type-i64")) +(assert_return (invoke "type-f32")) +(assert_return (invoke "type-f64")) +(assert_return (invoke "type-i32-i32")) +(assert_return (invoke "type-i64-i64")) +(assert_return (invoke "type-f32-f32")) +(assert_return (invoke "type-f64-f64")) + +(assert_return (invoke "type-i32-value") (i32.const 1)) +(assert_return (invoke "type-i64-value") (i64.const 2)) +(assert_return (invoke "type-f32-value") (f32.const 3)) +(assert_return (invoke "type-f64-value") (f64.const 4)) +(assert_return (invoke "type-f64-f64-value") (f64.const 4) (f64.const 5)) + +(assert_return (invoke "as-block-first")) +(assert_return (invoke "as-block-mid")) +(assert_return (invoke "as-block-last")) +(assert_return (invoke "as-block-value") (i32.const 2)) + +(assert_return (invoke "as-loop-first") (i32.const 3)) +(assert_return (invoke "as-loop-mid") (i32.const 4)) +(assert_return (invoke "as-loop-last") (i32.const 5)) + +(assert_return (invoke "as-br-value") (i32.const 9)) + +(assert_return (invoke "as-br_if-cond")) +(assert_return (invoke "as-br_if-value") (i32.const 8)) +(assert_return (invoke "as-br_if-value-cond") (i32.const 9)) + +(assert_return (invoke "as-br_table-index")) +(assert_return (invoke "as-br_table-value") (i32.const 10)) +(assert_return (invoke "as-br_table-value-index") (i32.const 11)) + +(assert_return (invoke "as-return-value") (i64.const 7)) +(assert_return (invoke "as-return-values") (i32.const 2) (i64.const 7)) + +(assert_return (invoke "as-if-cond") (i32.const 2)) +(assert_return (invoke "as-if-then" (i32.const 1) (i32.const 6)) (i32.const 3)) +(assert_return (invoke "as-if-then" (i32.const 0) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-if-else" (i32.const 0) (i32.const 6)) (i32.const 4)) +(assert_return (invoke "as-if-else" (i32.const 1) (i32.const 6)) (i32.const 6)) + +(assert_return (invoke "as-select-first" (i32.const 0) (i32.const 6)) (i32.const 5)) +(assert_return (invoke "as-select-first" (i32.const 1) (i32.const 6)) (i32.const 5)) +(assert_return (invoke "as-select-second" (i32.const 0) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-select-second" (i32.const 1) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-select-cond") (i32.const 7)) +(assert_return (invoke "as-select-all") (i32.const 8)) + +(assert_return (invoke "as-call-first") (i32.const 12)) +(assert_return (invoke "as-call-mid") (i32.const 13)) +(assert_return (invoke "as-call-last") (i32.const 14)) +(assert_return (invoke "as-call-all") (i32.const 15)) + +(assert_return (invoke "as-call_indirect-func") (i32.const 20)) +(assert_return (invoke "as-call_indirect-first") (i32.const 21)) +(assert_return (invoke "as-call_indirect-mid") (i32.const 22)) +(assert_return (invoke "as-call_indirect-last") (i32.const 23)) +(assert_return (invoke "as-call_indirect-all") (i32.const 24)) + +(assert_return (invoke "as-local.set-value") (i32.const 17)) +(assert_return (invoke "as-local.tee-value") (i32.const 1)) +(assert_return (invoke "as-global.set-value") (i32.const 1)) + +(assert_return (invoke "as-load-address") (f32.const 1.7)) +(assert_return (invoke "as-loadN-address") (i64.const 30)) + +(assert_return (invoke "as-store-address") (i32.const 30)) +(assert_return (invoke "as-store-value") (i32.const 31)) +(assert_return (invoke "as-store-both") (i32.const 32)) +(assert_return (invoke "as-storeN-address") (i32.const 32)) +(assert_return (invoke "as-storeN-value") (i32.const 33)) +(assert_return (invoke "as-storeN-both") (i32.const 34)) + +(assert_return (invoke "as-unary-operand") (f32.const 3.4)) + +(assert_return (invoke "as-binary-left") (i32.const 3)) +(assert_return (invoke "as-binary-right") (i64.const 45)) +(assert_return (invoke "as-binary-both") (i32.const 46)) + +(assert_return (invoke "as-test-operand") (i32.const 44)) + +(assert_return (invoke "as-compare-left") (i32.const 43)) +(assert_return (invoke "as-compare-right") (i32.const 42)) +(assert_return (invoke "as-compare-both") (i32.const 44)) + +(assert_return (invoke "as-convert-operand") (i32.const 41)) + +(assert_return (invoke "as-memory.grow-size") (i32.const 40)) + +(assert_return (invoke "nested-block-value") (i32.const 9)) +(assert_return (invoke "nested-br-value") (i32.const 9)) +(assert_return (invoke "nested-br_if-value") (i32.const 9)) +(assert_return (invoke "nested-br_if-value-cond") (i32.const 9)) +(assert_return (invoke "nested-br_table-value") (i32.const 9)) +(assert_return (invoke "nested-br_table-value-index") (i32.const 9)) + +(assert_invalid + (module (func $type-arg-empty-vs-num (result i32) + (block (result i32) (br 0) (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-arg-void-vs-num (result i32) + (block (result i32) (br 0 (nop)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-void-vs-num-nested (result i32) + (block (result i32) (i32.const 0) (block (br 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-num-vs-num (result i32) + (block (result i32) (br 0 (i64.const 1)) (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module + (func $type-arg-empty-in-br + (i32.const 0) + (block (result i32) (br 0 (br 0))) (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-empty-in-br_if + (i32.const 0) + (block (result i32) (br_if 0 (br 0) (i32.const 1))) (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-empty-in-br_table + (i32.const 0) + (block (result i32) (br_table 0 (br 0))) (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-empty-in-return + (block (result i32) + (return (br 0)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-empty-in-select + (block (result i32) + (select (br 0) (i32.const 1) (i32.const 2)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-empty-in-call + (block (result i32) + (call 1 (br 0)) + ) + (i32.eqz) (drop) + ) + (func (param i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-arg-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (br 0) (i32.const 0) + ) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-empty-in-local.set + (local i32) + (block (result i32) + (local.set 0 (br 0)) (local.get 0) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-empty-in-local.tee + (local i32) + (block (result i32) + (local.tee 0 (br 0)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-arg-empty-in-global.set + (block (result i32) + (global.set $x (br 0)) (global.get $x) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-arg-empty-in-memory.grow + (block (result i32) + (memory.grow (br 0)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-arg-empty-in-load + (block (result i32) + (i32.load (br 0)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-arg-empty-in-store + (block (result i32) + (i32.store (br 0) (i32.const 0)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) + +(assert_invalid + (module (func $unbound-label (br 1))) + "unknown label" +) +(assert_invalid + (module (func $unbound-nested-label (block (block (br 5))))) + "unknown label" +) +(assert_invalid + (module (func $large-label (br 0x10000001))) + "unknown label" +) diff --git a/runtime/unc-vm/tests/wast/spec/br_if.wast b/runtime/unc-vm/tests/wast/spec/br_if.wast new file mode 100644 index 000000000..fba5deb9b --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/br_if.wast @@ -0,0 +1,665 @@ +;; Test `br_if` operator + +(module + (func $dummy) + + (func (export "type-i32") + (block (drop (i32.ctz (br_if 0 (i32.const 0) (i32.const 1))))) + ) + (func (export "type-i64") + (block (drop (i64.ctz (br_if 0 (i64.const 0) (i32.const 1))))) + ) + (func (export "type-f32") + (block (drop (f32.neg (br_if 0 (f32.const 0) (i32.const 1))))) + ) + (func (export "type-f64") + (block (drop (f64.neg (br_if 0 (f64.const 0) (i32.const 1))))) + ) + + (func (export "type-i32-value") (result i32) + (block (result i32) (i32.ctz (br_if 0 (i32.const 1) (i32.const 1)))) + ) + (func (export "type-i64-value") (result i64) + (block (result i64) (i64.ctz (br_if 0 (i64.const 2) (i32.const 1)))) + ) + (func (export "type-f32-value") (result f32) + (block (result f32) (f32.neg (br_if 0 (f32.const 3) (i32.const 1)))) + ) + (func (export "type-f64-value") (result f64) + (block (result f64) (f64.neg (br_if 0 (f64.const 4) (i32.const 1)))) + ) + + (func (export "as-block-first") (param i32) (result i32) + (block (br_if 0 (local.get 0)) (return (i32.const 2))) (i32.const 3) + ) + (func (export "as-block-mid") (param i32) (result i32) + (block (call $dummy) (br_if 0 (local.get 0)) (return (i32.const 2))) + (i32.const 3) + ) + (func (export "as-block-last") (param i32) + (block (call $dummy) (call $dummy) (br_if 0 (local.get 0))) + ) + (func (export "as-block-first-value") (param i32) (result i32) + (block (result i32) + (drop (br_if 0 (i32.const 10) (local.get 0))) (return (i32.const 11)) + ) + ) + (func (export "as-block-mid-value") (param i32) (result i32) + (block (result i32) + (call $dummy) + (drop (br_if 0 (i32.const 20) (local.get 0))) + (return (i32.const 21)) + ) + ) + (func (export "as-block-last-value") (param i32) (result i32) + (block (result i32) + (call $dummy) (call $dummy) (br_if 0 (i32.const 11) (local.get 0)) + ) + ) + + (func (export "as-loop-first") (param i32) (result i32) + (block (loop (br_if 1 (local.get 0)) (return (i32.const 2)))) (i32.const 3) + ) + (func (export "as-loop-mid") (param i32) (result i32) + (block (loop (call $dummy) (br_if 1 (local.get 0)) (return (i32.const 2)))) + (i32.const 4) + ) + (func (export "as-loop-last") (param i32) + (loop (call $dummy) (br_if 1 (local.get 0))) + ) + + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (br_if 0 (i32.const 1) (i32.const 2)))) + ) + + (func (export "as-br_if-cond") + (block (br_if 0 (br_if 0 (i32.const 1) (i32.const 1)))) + ) + (func (export "as-br_if-value") (result i32) + (block (result i32) + (drop (br_if 0 (br_if 0 (i32.const 1) (i32.const 2)) (i32.const 3))) + (i32.const 4) + ) + ) + (func (export "as-br_if-value-cond") (param i32) (result i32) + (block (result i32) + (drop (br_if 0 (i32.const 2) (br_if 0 (i32.const 1) (local.get 0)))) + (i32.const 4) + ) + ) + + (func (export "as-br_table-index") + (block (br_table 0 0 0 (br_if 0 (i32.const 1) (i32.const 2)))) + ) + (func (export "as-br_table-value") (result i32) + (block (result i32) + (br_table 0 0 0 (br_if 0 (i32.const 1) (i32.const 2)) (i32.const 3)) (i32.const 4) + ) + ) + (func (export "as-br_table-value-index") (result i32) + (block (result i32) + (br_table 0 0 (i32.const 2) (br_if 0 (i32.const 1) (i32.const 3))) (i32.const 4) + ) + ) + (func (export "as-return-value") (result i64) + (block (result i64) (return (br_if 0 (i64.const 1) (i32.const 2)))) + ) + + (func (export "as-if-cond") (param i32) (result i32) + (block (result i32) + (if (result i32) + (br_if 0 (i32.const 1) (local.get 0)) + (then (i32.const 2)) + (else (i32.const 3)) + ) + ) + ) + (func (export "as-if-then") (param i32 i32) + (block + (if (local.get 0) (then (br_if 1 (local.get 1))) (else (call $dummy))) + ) + ) + (func (export "as-if-else") (param i32 i32) + (block + (if (local.get 0) (then (call $dummy)) (else (br_if 1 (local.get 1)))) + ) + ) + + (func (export "as-select-first") (param i32) (result i32) + (block (result i32) + (select (br_if 0 (i32.const 3) (i32.const 10)) (i32.const 2) (local.get 0)) + ) + ) + (func (export "as-select-second") (param i32) (result i32) + (block (result i32) + (select (i32.const 1) (br_if 0 (i32.const 3) (i32.const 10)) (local.get 0)) + ) + ) + (func (export "as-select-cond") (result i32) + (block (result i32) + (select (i32.const 1) (i32.const 2) (br_if 0 (i32.const 3) (i32.const 10))) + ) + ) + + (func $f (param i32 i32 i32) (result i32) (i32.const -1)) + (func (export "as-call-first") (result i32) + (block (result i32) + (call $f + (br_if 0 (i32.const 12) (i32.const 1)) (i32.const 2) (i32.const 3) + ) + ) + ) + (func (export "as-call-mid") (result i32) + (block (result i32) + (call $f + (i32.const 1) (br_if 0 (i32.const 13) (i32.const 1)) (i32.const 3) + ) + ) + ) + (func (export "as-call-last") (result i32) + (block (result i32) + (call $f + (i32.const 1) (i32.const 2) (br_if 0 (i32.const 14) (i32.const 1)) + ) + ) + ) + + (func $func (param i32 i32 i32) (result i32) (local.get 0)) + (type $check (func (param i32 i32 i32) (result i32))) + (table funcref (elem $func)) + (func (export "as-call_indirect-func") (result i32) + (block (result i32) + (call_indirect (type $check) + (br_if 0 (i32.const 4) (i32.const 10)) + (i32.const 1) (i32.const 2) (i32.const 0) + ) + ) + ) + + (func (export "as-call_indirect-first") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 1) (br_if 0 (i32.const 4) (i32.const 10)) (i32.const 2) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 1) (i32.const 2) (br_if 0 (i32.const 4) (i32.const 10)) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 1) (i32.const 2) (i32.const 3) (br_if 0 (i32.const 4) (i32.const 10)) + ) + ) + ) + + (func (export "as-local.set-value") (param i32) (result i32) + (local i32) + (block (result i32) + (local.set 0 (br_if 0 (i32.const 17) (local.get 0))) + (i32.const -1) + ) + ) + (func (export "as-local.tee-value") (param i32) (result i32) + (block (result i32) + (local.tee 0 (br_if 0 (i32.const 1) (local.get 0))) + (return (i32.const -1)) + ) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (param i32) (result i32) + (block (result i32) + (global.set $a (br_if 0 (i32.const 1) (local.get 0))) + (return (i32.const -1)) + ) + ) + + (memory 1) + (func (export "as-load-address") (result i32) + (block (result i32) (i32.load (br_if 0 (i32.const 1) (i32.const 1)))) + ) + (func (export "as-loadN-address") (result i32) + (block (result i32) (i32.load8_s (br_if 0 (i32.const 30) (i32.const 1)))) + ) + + (func (export "as-store-address") (result i32) + (block (result i32) + (i32.store (br_if 0 (i32.const 30) (i32.const 1)) (i32.const 7)) (i32.const -1) + ) + ) + (func (export "as-store-value") (result i32) + (block (result i32) + (i32.store (i32.const 2) (br_if 0 (i32.const 31) (i32.const 1))) (i32.const -1) + ) + ) + + (func (export "as-storeN-address") (result i32) + (block (result i32) + (i32.store8 (br_if 0 (i32.const 32) (i32.const 1)) (i32.const 7)) (i32.const -1) + ) + ) + (func (export "as-storeN-value") (result i32) + (block (result i32) + (i32.store16 (i32.const 2) (br_if 0 (i32.const 33) (i32.const 1))) (i32.const -1) + ) + ) + + (func (export "as-unary-operand") (result f64) + (block (result f64) (f64.neg (br_if 0 (f64.const 1.0) (i32.const 1)))) + ) + (func (export "as-binary-left") (result i32) + (block (result i32) (i32.add (br_if 0 (i32.const 1) (i32.const 1)) (i32.const 10))) + ) + (func (export "as-binary-right") (result i32) + (block (result i32) (i32.sub (i32.const 10) (br_if 0 (i32.const 1) (i32.const 1)))) + ) + (func (export "as-test-operand") (result i32) + (block (result i32) (i32.eqz (br_if 0 (i32.const 0) (i32.const 1)))) + ) + (func (export "as-compare-left") (result i32) + (block (result i32) (i32.le_u (br_if 0 (i32.const 1) (i32.const 1)) (i32.const 10))) + ) + (func (export "as-compare-right") (result i32) + (block (result i32) (i32.ne (i32.const 10) (br_if 0 (i32.const 1) (i32.const 42)))) + ) + + (func (export "as-memory.grow-size") (result i32) + (block (result i32) (memory.grow (br_if 0 (i32.const 1) (i32.const 1)))) + ) + + (func (export "nested-block-value") (param i32) (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (i32.add + (i32.const 4) + (block (result i32) + (drop (br_if 1 (i32.const 8) (local.get 0))) + (i32.const 16) + ) + ) + ) + ) + ) + + (func (export "nested-br-value") (param i32) (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (br 0 + (block (result i32) + (drop (br_if 1 (i32.const 8) (local.get 0))) (i32.const 4) + ) + ) + (i32.const 16) + ) + ) + ) + + (func (export "nested-br_if-value") (param i32) (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop (br_if 0 + (block (result i32) + (drop (br_if 1 (i32.const 8) (local.get 0))) (i32.const 4) + ) + (i32.const 1) + )) + (i32.const 16) + ) + ) + ) + + (func (export "nested-br_if-value-cond") (param i32) (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop (br_if 0 + (i32.const 4) + (block (result i32) + (drop (br_if 1 (i32.const 8) (local.get 0))) (i32.const 1) + ) + )) + (i32.const 16) + ) + ) + ) + + (func (export "nested-br_table-value") (param i32) (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (br_table 0 + (block (result i32) + (drop (br_if 1 (i32.const 8) (local.get 0))) (i32.const 4) + ) + (i32.const 1) + ) + (i32.const 16) + ) + ) + ) + + (func (export "nested-br_table-value-index") (param i32) (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (br_table 0 + (i32.const 4) + (block (result i32) + (drop (br_if 1 (i32.const 8) (local.get 0))) (i32.const 1) + ) + ) + (i32.const 16) + ) + ) + ) + +) + +(assert_return (invoke "type-i32")) +(assert_return (invoke "type-i64")) +(assert_return (invoke "type-f32")) +(assert_return (invoke "type-f64")) + +(assert_return (invoke "type-i32-value") (i32.const 1)) +(assert_return (invoke "type-i64-value") (i64.const 2)) +(assert_return (invoke "type-f32-value") (f32.const 3)) +(assert_return (invoke "type-f64-value") (f64.const 4)) + +(assert_return (invoke "as-block-first" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-block-first" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "as-block-mid" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-block-mid" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "as-block-last" (i32.const 0))) +(assert_return (invoke "as-block-last" (i32.const 1))) + +(assert_return (invoke "as-block-first-value" (i32.const 0)) (i32.const 11)) +(assert_return (invoke "as-block-first-value" (i32.const 1)) (i32.const 10)) +(assert_return (invoke "as-block-mid-value" (i32.const 0)) (i32.const 21)) +(assert_return (invoke "as-block-mid-value" (i32.const 1)) (i32.const 20)) +(assert_return (invoke "as-block-last-value" (i32.const 0)) (i32.const 11)) +(assert_return (invoke "as-block-last-value" (i32.const 1)) (i32.const 11)) + +(assert_return (invoke "as-loop-first" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-loop-first" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "as-loop-mid" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-loop-mid" (i32.const 1)) (i32.const 4)) +(assert_return (invoke "as-loop-last" (i32.const 0))) +(assert_return (invoke "as-loop-last" (i32.const 1))) + +(assert_return (invoke "as-br-value") (i32.const 1)) + +(assert_return (invoke "as-br_if-cond")) +(assert_return (invoke "as-br_if-value") (i32.const 1)) +(assert_return (invoke "as-br_if-value-cond" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-br_if-value-cond" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-br_table-index")) +(assert_return (invoke "as-br_table-value") (i32.const 1)) +(assert_return (invoke "as-br_table-value-index") (i32.const 1)) + +(assert_return (invoke "as-return-value") (i64.const 1)) + +(assert_return (invoke "as-if-cond" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-if-cond" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-if-then" (i32.const 0) (i32.const 0))) +(assert_return (invoke "as-if-then" (i32.const 4) (i32.const 0))) +(assert_return (invoke "as-if-then" (i32.const 0) (i32.const 1))) +(assert_return (invoke "as-if-then" (i32.const 4) (i32.const 1))) +(assert_return (invoke "as-if-else" (i32.const 0) (i32.const 0))) +(assert_return (invoke "as-if-else" (i32.const 3) (i32.const 0))) +(assert_return (invoke "as-if-else" (i32.const 0) (i32.const 1))) +(assert_return (invoke "as-if-else" (i32.const 3) (i32.const 1))) + +(assert_return (invoke "as-select-first" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-select-first" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "as-select-second" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-select-second" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "as-select-cond") (i32.const 3)) + +(assert_return (invoke "as-call-first") (i32.const 12)) +(assert_return (invoke "as-call-mid") (i32.const 13)) +(assert_return (invoke "as-call-last") (i32.const 14)) + +(assert_return (invoke "as-call_indirect-func") (i32.const 4)) +(assert_return (invoke "as-call_indirect-first") (i32.const 4)) +(assert_return (invoke "as-call_indirect-mid") (i32.const 4)) +(assert_return (invoke "as-call_indirect-last") (i32.const 4)) + +(assert_return (invoke "as-local.set-value" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "as-local.set-value" (i32.const 1)) (i32.const 17)) + +(assert_return (invoke "as-local.tee-value" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "as-local.tee-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-global.set-value" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "as-global.set-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-load-address") (i32.const 1)) +(assert_return (invoke "as-loadN-address") (i32.const 30)) + +(assert_return (invoke "as-store-address") (i32.const 30)) +(assert_return (invoke "as-store-value") (i32.const 31)) +(assert_return (invoke "as-storeN-address") (i32.const 32)) +(assert_return (invoke "as-storeN-value") (i32.const 33)) + +(assert_return (invoke "as-unary-operand") (f64.const 1.0)) +(assert_return (invoke "as-binary-left") (i32.const 1)) +(assert_return (invoke "as-binary-right") (i32.const 1)) +(assert_return (invoke "as-test-operand") (i32.const 0)) +(assert_return (invoke "as-compare-left") (i32.const 1)) +(assert_return (invoke "as-compare-right") (i32.const 1)) +(assert_return (invoke "as-memory.grow-size") (i32.const 1)) + +(assert_return (invoke "nested-block-value" (i32.const 0)) (i32.const 21)) +(assert_return (invoke "nested-block-value" (i32.const 1)) (i32.const 9)) +(assert_return (invoke "nested-br-value" (i32.const 0)) (i32.const 5)) +(assert_return (invoke "nested-br-value" (i32.const 1)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value" (i32.const 0)) (i32.const 5)) +(assert_return (invoke "nested-br_if-value" (i32.const 1)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const 0)) (i32.const 5)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const 1)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value" (i32.const 0)) (i32.const 5)) +(assert_return (invoke "nested-br_table-value" (i32.const 1)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value-index" (i32.const 0)) (i32.const 5)) +(assert_return (invoke "nested-br_table-value-index" (i32.const 1)) (i32.const 9)) + +(assert_invalid + (module (func $type-false-i32 (block (i32.ctz (br_if 0 (i32.const 0)))))) + "type mismatch" +) +(assert_invalid + (module (func $type-false-i64 (block (i64.ctz (br_if 0 (i32.const 0)))))) + "type mismatch" +) +(assert_invalid + (module (func $type-false-f32 (block (f32.neg (br_if 0 (i32.const 0)))))) + "type mismatch" +) +(assert_invalid + (module (func $type-false-f64 (block (f64.neg (br_if 0 (i32.const 0)))))) + "type mismatch" +) + +(assert_invalid + (module (func $type-true-i32 (block (i32.ctz (br_if 0 (i32.const 1)))))) + "type mismatch" +) +(assert_invalid + (module (func $type-true-i64 (block (i64.ctz (br_if 0 (i64.const 1)))))) + "type mismatch" +) +(assert_invalid + (module (func $type-true-f32 (block (f32.neg (br_if 0 (f32.const 1)))))) + "type mismatch" +) +(assert_invalid + (module (func $type-true-f64 (block (f64.neg (br_if 0 (i64.const 1)))))) + "type mismatch" +) + +(assert_invalid + (module (func $type-false-arg-void-vs-num (result i32) + (block (result i32) (br_if 0 (i32.const 0)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-true-arg-void-vs-num (result i32) + (block (result i32) (br_if 0 (i32.const 1)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-false-arg-num-vs-void + (block (br_if 0 (i32.const 0) (i32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-true-arg-num-vs-void + (block (br_if 0 (i32.const 0) (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-false-arg-void-vs-num (result i32) + (block (result i32) (br_if 0 (nop) (i32.const 0)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-true-arg-void-vs-num (result i32) + (block (result i32) (br_if 0 (nop) (i32.const 1)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-false-arg-num-vs-num (result i32) + (block (result i32) + (drop (br_if 0 (i64.const 1) (i32.const 0))) (i32.const 1) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-true-arg-num-vs-num (result i32) + (block (result i32) + (drop (br_if 0 (i64.const 1) (i32.const 0))) (i32.const 1) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-cond-empty-vs-i32 + (block (br_if 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-cond-void-vs-i32 + (block (br_if 0 (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-cond-num-vs-i32 + (block (br_if 0 (i64.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-cond-void-vs-i32 (result i32) + (block (result i32) (br_if 0 (i32.const 0) (nop)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-void-vs-num-nested (result i32) + (block (result i32) (i32.const 0) (block (br_if 1 (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-cond-num-vs-i32 (result i32) + (block (result i32) (br_if 0 (i32.const 0) (i64.const 0)) (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module + (func $type-1st-cond-empty-in-then + (block + (i32.const 0) (i32.const 0) + (if (result i32) (then (br_if 0))) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-2nd-cond-empty-in-then + (block + (i32.const 0) (i32.const 0) + (if (result i32) (then (br_if 0 (i32.const 1)))) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-1st-cond-empty-in-return + (block (result i32) + (return (br_if 0)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-2nd-cond-empty-in-return + (block (result i32) + (return (br_if 0 (i32.const 1))) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) + + +(assert_invalid + (module (func $unbound-label (br_if 1 (i32.const 1)))) + "unknown label" +) +(assert_invalid + (module (func $unbound-nested-label (block (block (br_if 5 (i32.const 1)))))) + "unknown label" +) +(assert_invalid + (module (func $large-label (br_if 0x10000001 (i32.const 1)))) + "unknown label" +) + diff --git a/runtime/unc-vm/tests/wast/spec/br_table.wast b/runtime/unc-vm/tests/wast/spec/br_table.wast new file mode 100644 index 000000000..b70c102da --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/br_table.wast @@ -0,0 +1,1668 @@ +;; Test `br_table` operator + +(module + ;; Auxiliary definition + (func $dummy) + + (func (export "type-i32") + (block (drop (i32.ctz (br_table 0 0 (i32.const 0))))) + ) + (func (export "type-i64") + (block (drop (i64.ctz (br_table 0 0 (i32.const 0))))) + ) + (func (export "type-f32") + (block (drop (f32.neg (br_table 0 0 (i32.const 0))))) + ) + (func (export "type-f64") + (block (drop (f64.neg (br_table 0 0 (i32.const 0))))) + ) + + (func (export "type-i32-value") (result i32) + (block (result i32) (i32.ctz (br_table 0 0 (i32.const 1) (i32.const 0)))) + ) + (func (export "type-i64-value") (result i64) + (block (result i64) (i64.ctz (br_table 0 0 (i64.const 2) (i32.const 0)))) + ) + (func (export "type-f32-value") (result f32) + (block (result f32) (f32.neg (br_table 0 0 (f32.const 3) (i32.const 0)))) + ) + (func (export "type-f64-value") (result f64) + (block (result f64) (f64.neg (br_table 0 0 (f64.const 4) (i32.const 0)))) + ) + + (func (export "empty") (param i32) (result i32) + (block (br_table 0 (local.get 0)) (return (i32.const 21))) + (i32.const 22) + ) + (func (export "empty-value") (param i32) (result i32) + (block (result i32) + (br_table 0 (i32.const 33) (local.get 0)) (i32.const 31) + ) + ) + + (func (export "singleton") (param i32) (result i32) + (block + (block + (br_table 1 0 (local.get 0)) + (return (i32.const 21)) + ) + (return (i32.const 20)) + ) + (i32.const 22) + ) + + (func (export "singleton-value") (param i32) (result i32) + (block (result i32) + (drop + (block (result i32) + (br_table 0 1 (i32.const 33) (local.get 0)) + (return (i32.const 31)) + ) + ) + (i32.const 32) + ) + ) + + (func (export "multiple") (param i32) (result i32) + (block + (block + (block + (block + (block + (br_table 3 2 1 0 4 (local.get 0)) + (return (i32.const 99)) + ) + (return (i32.const 100)) + ) + (return (i32.const 101)) + ) + (return (i32.const 102)) + ) + (return (i32.const 103)) + ) + (i32.const 104) + ) + + (func (export "multiple-value") (param i32) (result i32) + (local i32) + (local.set 1 (block (result i32) + (local.set 1 (block (result i32) + (local.set 1 (block (result i32) + (local.set 1 (block (result i32) + (local.set 1 (block (result i32) + (br_table 3 2 1 0 4 (i32.const 200) (local.get 0)) + (return (i32.add (local.get 1) (i32.const 99))) + )) + (return (i32.add (local.get 1) (i32.const 10))) + )) + (return (i32.add (local.get 1) (i32.const 11))) + )) + (return (i32.add (local.get 1) (i32.const 12))) + )) + (return (i32.add (local.get 1) (i32.const 13))) + )) + (i32.add (local.get 1) (i32.const 14)) + ) + + (func (export "large") (param i32) (result i32) + (block + (block + (br_table + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + (local.get 0) + ) + (return (i32.const -1)) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) + + (func (export "as-block-first") + (block (br_table 0 0 0 (i32.const 0)) (call $dummy)) + ) + (func (export "as-block-mid") + (block (call $dummy) (br_table 0 0 0 (i32.const 0)) (call $dummy)) + ) + (func (export "as-block-last") + (block (nop) (call $dummy) (br_table 0 0 0 (i32.const 0))) + ) + (func (export "as-block-value") (result i32) + (block (result i32) + (nop) (call $dummy) (br_table 0 0 0 (i32.const 2) (i32.const 0)) + ) + ) + + (func (export "as-loop-first") (result i32) + (loop (result i32) (br_table 1 1 (i32.const 3) (i32.const 0)) (i32.const 1)) + ) + (func (export "as-loop-mid") (result i32) + (loop (result i32) + (call $dummy) + (br_table 1 1 1 (i32.const 4) (i32.const -1)) + (i32.const 2) + ) + ) + (func (export "as-loop-last") (result i32) + (loop (result i32) + (nop) (call $dummy) (br_table 1 1 1 (i32.const 5) (i32.const 1)) + ) + ) + + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (br_table 0 (i32.const 9) (i32.const 0)))) + ) + + (func (export "as-br_if-cond") + (block (br_if 0 (br_table 0 0 0 (i32.const 1)))) + ) + (func (export "as-br_if-value") (result i32) + (block (result i32) + (drop (br_if 0 (br_table 0 (i32.const 8) (i32.const 0)) (i32.const 1))) + (i32.const 7) + ) + ) + (func (export "as-br_if-value-cond") (result i32) + (block (result i32) + (drop (br_if 0 (i32.const 6) (br_table 0 0 (i32.const 9) (i32.const 0)))) + (i32.const 7) + ) + ) + + (func (export "as-br_table-index") + (block (br_table 0 0 0 (br_table 0 (i32.const 1)))) + ) + (func (export "as-br_table-value") (result i32) + (block (result i32) + (br_table 0 0 0 (br_table 0 (i32.const 10) (i32.const 0)) (i32.const 1)) + (i32.const 7) + ) + ) + (func (export "as-br_table-value-index") (result i32) + (block (result i32) + (br_table 0 0 (i32.const 6) (br_table 0 (i32.const 11) (i32.const 1))) + (i32.const 7) + ) + ) + + (func (export "as-return-value") (result i64) + (block (result i64) (return (br_table 0 (i64.const 7) (i32.const 0)))) + ) + + (func (export "as-if-cond") (result i32) + (block (result i32) + (if (result i32) + (br_table 0 (i32.const 2) (i32.const 0)) + (then (i32.const 0)) + (else (i32.const 1)) + ) + ) + ) + (func (export "as-if-then") (param i32 i32) (result i32) + (block (result i32) + (if (result i32) + (local.get 0) + (then (br_table 1 (i32.const 3) (i32.const 0))) + (else (local.get 1)) + ) + ) + ) + (func (export "as-if-else") (param i32 i32) (result i32) + (block (result i32) + (if (result i32) + (local.get 0) + (then (local.get 1)) + (else (br_table 1 0 (i32.const 4) (i32.const 0))) + ) + ) + ) + + (func (export "as-select-first") (param i32 i32) (result i32) + (block (result i32) + (select + (br_table 0 (i32.const 5) (i32.const 0)) (local.get 0) (local.get 1) + ) + ) + ) + (func (export "as-select-second") (param i32 i32) (result i32) + (block (result i32) + (select + (local.get 0) (br_table 0 (i32.const 6) (i32.const 1)) (local.get 1) + ) + ) + ) + (func (export "as-select-cond") (result i32) + (block (result i32) + (select + (i32.const 0) (i32.const 1) (br_table 0 (i32.const 7) (i32.const 1)) + ) + ) + ) + + (func $f (param i32 i32 i32) (result i32) (i32.const -1)) + (func (export "as-call-first") (result i32) + (block (result i32) + (call $f + (br_table 0 (i32.const 12) (i32.const 1)) (i32.const 2) (i32.const 3) + ) + ) + ) + (func (export "as-call-mid") (result i32) + (block (result i32) + (call $f + (i32.const 1) (br_table 0 (i32.const 13) (i32.const 1)) (i32.const 3) + ) + ) + ) + (func (export "as-call-last") (result i32) + (block (result i32) + (call $f + (i32.const 1) (i32.const 2) (br_table 0 (i32.const 14) (i32.const 1)) + ) + ) + ) + + (type $sig (func (param i32 i32 i32) (result i32))) + (table funcref (elem $f)) + (func (export "as-call_indirect-first") (result i32) + (block (result i32) + (call_indirect (type $sig) + (br_table 0 (i32.const 20) (i32.const 1)) (i32.const 1) (i32.const 2) + (i32.const 3) + ) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) (br_table 0 (i32.const 21) (i32.const 1)) (i32.const 2) + (i32.const 3) + ) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) (i32.const 1) (br_table 0 (i32.const 22) (i32.const 1)) + (i32.const 3) + ) + ) + ) + (func (export "as-call_indirect-func") (result i32) + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) (i32.const 1) (i32.const 2) + (br_table 0 (i32.const 23) (i32.const 1)) + ) + ) + ) + + (func (export "as-local.set-value") (result i32) + (local f32) + (block (result i32) + (local.set 0 (br_table 0 (i32.const 17) (i32.const 1))) + (i32.const -1) + ) + ) + (func (export "as-local.tee-value") (result i32) + (local i32) + (block (result i32) + (local.set 0 (br_table 0 (i32.const 1) (i32.const 1))) + (i32.const -1) + ) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (result i32) + (block (result i32) + (global.set $a (br_table 0 (i32.const 1) (i32.const 1))) + (i32.const -1) + ) + ) + + (memory 1) + (func (export "as-load-address") (result f32) + (block (result f32) (f32.load (br_table 0 (f32.const 1.7) (i32.const 1)))) + ) + (func (export "as-loadN-address") (result i64) + (block (result i64) (i64.load8_s (br_table 0 (i64.const 30) (i32.const 1)))) + ) + + (func (export "as-store-address") (result i32) + (block (result i32) + (f64.store (br_table 0 (i32.const 30) (i32.const 1)) (f64.const 7)) + (i32.const -1) + ) + ) + (func (export "as-store-value") (result i32) + (block (result i32) + (i64.store (i32.const 2) (br_table 0 (i32.const 31) (i32.const 1))) + (i32.const -1) + ) + ) + + (func (export "as-storeN-address") (result i32) + (block (result i32) + (i32.store8 (br_table 0 (i32.const 32) (i32.const 0)) (i32.const 7)) + (i32.const -1) + ) + ) + (func (export "as-storeN-value") (result i32) + (block (result i32) + (i64.store16 (i32.const 2) (br_table 0 (i32.const 33) (i32.const 0))) + (i32.const -1) + ) + ) + + (func (export "as-unary-operand") (result f32) + (block (result f32) (f32.neg (br_table 0 (f32.const 3.4) (i32.const 0)))) + ) + + (func (export "as-binary-left") (result i32) + (block (result i32) + (i32.add (br_table 0 0 (i32.const 3) (i32.const 0)) (i32.const 10)) + ) + ) + (func (export "as-binary-right") (result i64) + (block (result i64) + (i64.sub (i64.const 10) (br_table 0 (i64.const 45) (i32.const 0))) + ) + ) + + (func (export "as-test-operand") (result i32) + (block (result i32) (i32.eqz (br_table 0 (i32.const 44) (i32.const 0)))) + ) + + (func (export "as-compare-left") (result i32) + (block (result i32) + (f64.le (br_table 0 0 (i32.const 43) (i32.const 0)) (f64.const 10)) + ) + ) + (func (export "as-compare-right") (result i32) + (block (result i32) + (f32.ne (f32.const 10) (br_table 0 (i32.const 42) (i32.const 0))) + ) + ) + + (func (export "as-convert-operand") (result i32) + (block (result i32) + (i32.wrap_i64 (br_table 0 (i32.const 41) (i32.const 0))) + ) + ) + + (func (export "as-memory.grow-size") (result i32) + (block (result i32) (memory.grow (br_table 0 (i32.const 40) (i32.const 0)))) + ) + + (func (export "nested-block-value") (param i32) (result i32) + (block (result i32) + (drop (i32.const -1)) + (i32.add + (i32.const 1) + (block (result i32) + (i32.add + (i32.const 2) + (block (result i32) + (drop (i32.const 4)) + (i32.add + (i32.const 8) + (br_table 0 1 2 (i32.const 16) (local.get 0)) + ) + ) + ) + ) + ) + ) + ) + + (func (export "nested-br-value") (param i32) (result i32) + (block (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (block (result i32) + (drop (i32.const 4)) + (br 0 (br_table 2 1 0 (i32.const 8) (local.get 0))) + ) + ) + (i32.const 16) + ) + ) + ) + ) + + (func (export "nested-br_if-value") (param i32) (result i32) + (block (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (block (result i32) + (drop (i32.const 4)) + (drop + (br_if 0 + (br_table 0 1 2 (i32.const 8) (local.get 0)) + (i32.const 1) + ) + ) + (i32.const 32) + ) + ) + (i32.const 16) + ) + ) + ) + ) + + (func (export "nested-br_if-value-cond") (param i32) (result i32) + (block (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (br_if 0 (i32.const 4) (br_table 0 1 0 (i32.const 8) (local.get 0))) + ) + (i32.const 16) + ) + ) + ) + ) + + (func (export "nested-br_table-value") (param i32) (result i32) + (block (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (block (result i32) + (drop (i32.const 4)) + (br_table 0 (br_table 0 1 2 (i32.const 8) (local.get 0)) (i32.const 1)) + (i32.const 32) + ) + ) + (i32.const 16) + ) + ) + ) + ) + + (func (export "nested-br_table-value-index") (param i32) (result i32) + (block (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (br_table 0 (i32.const 4) (br_table 0 1 0 (i32.const 8) (local.get 0))) + (i32.const 16) + ) + ) + ) + ) + + (func (export "nested-br_table-loop-block") (param i32) (result i32) + (local.set 0 + (loop (result i32) + (block + (br_table 1 0 0 (local.get 0)) + ) + (i32.const 0) + ) + ) + (loop (result i32) + (block + (br_table 0 1 1 (local.get 0)) + ) + (i32.const 3) + ) + ) + + (func (export "meet-externref") (param i32) (param externref) (result externref) + (block $l1 (result externref) + (block $l2 (result externref) + (br_table $l1 $l2 $l1 (local.get 1) (local.get 0)) + ) + ) + ) + + ;; commenting this part out for now as it's a bit odd and we rely on wasmparser for + ;; wasm validation. Not meeting this requirement is okay for now + ;; (func (export "meet-bottom") + ;; (block (result f64) + ;; (block (result f32) + ;; (unreachable) + ;; (br_table 0 1 1 (i32.const 1)) + ;; ) + ;; (drop) + ;; (f64.const 0) + ;; ) + ;; (drop) + ;; ) +) + +(assert_return (invoke "type-i32")) +(assert_return (invoke "type-i64")) +(assert_return (invoke "type-f32")) +(assert_return (invoke "type-f64")) + +(assert_return (invoke "type-i32-value") (i32.const 1)) +(assert_return (invoke "type-i64-value") (i64.const 2)) +(assert_return (invoke "type-f32-value") (f32.const 3)) +(assert_return (invoke "type-f64-value") (f64.const 4)) + +(assert_return (invoke "empty" (i32.const 0)) (i32.const 22)) +(assert_return (invoke "empty" (i32.const 1)) (i32.const 22)) +(assert_return (invoke "empty" (i32.const 11)) (i32.const 22)) +(assert_return (invoke "empty" (i32.const -1)) (i32.const 22)) +(assert_return (invoke "empty" (i32.const -100)) (i32.const 22)) +(assert_return (invoke "empty" (i32.const 0xffffffff)) (i32.const 22)) + +(assert_return (invoke "empty-value" (i32.const 0)) (i32.const 33)) +(assert_return (invoke "empty-value" (i32.const 1)) (i32.const 33)) +(assert_return (invoke "empty-value" (i32.const 11)) (i32.const 33)) +(assert_return (invoke "empty-value" (i32.const -1)) (i32.const 33)) +(assert_return (invoke "empty-value" (i32.const -100)) (i32.const 33)) +(assert_return (invoke "empty-value" (i32.const 0xffffffff)) (i32.const 33)) + +(assert_return (invoke "singleton" (i32.const 0)) (i32.const 22)) +(assert_return (invoke "singleton" (i32.const 1)) (i32.const 20)) +(assert_return (invoke "singleton" (i32.const 11)) (i32.const 20)) +(assert_return (invoke "singleton" (i32.const -1)) (i32.const 20)) +(assert_return (invoke "singleton" (i32.const -100)) (i32.const 20)) +(assert_return (invoke "singleton" (i32.const 0xffffffff)) (i32.const 20)) + +(assert_return (invoke "singleton-value" (i32.const 0)) (i32.const 32)) +(assert_return (invoke "singleton-value" (i32.const 1)) (i32.const 33)) +(assert_return (invoke "singleton-value" (i32.const 11)) (i32.const 33)) +(assert_return (invoke "singleton-value" (i32.const -1)) (i32.const 33)) +(assert_return (invoke "singleton-value" (i32.const -100)) (i32.const 33)) +(assert_return (invoke "singleton-value" (i32.const 0xffffffff)) (i32.const 33)) + +(assert_return (invoke "multiple" (i32.const 0)) (i32.const 103)) +(assert_return (invoke "multiple" (i32.const 1)) (i32.const 102)) +(assert_return (invoke "multiple" (i32.const 2)) (i32.const 101)) +(assert_return (invoke "multiple" (i32.const 3)) (i32.const 100)) +(assert_return (invoke "multiple" (i32.const 4)) (i32.const 104)) +(assert_return (invoke "multiple" (i32.const 5)) (i32.const 104)) +(assert_return (invoke "multiple" (i32.const 6)) (i32.const 104)) +(assert_return (invoke "multiple" (i32.const 10)) (i32.const 104)) +(assert_return (invoke "multiple" (i32.const -1)) (i32.const 104)) +(assert_return (invoke "multiple" (i32.const 0xffffffff)) (i32.const 104)) + +(assert_return (invoke "multiple-value" (i32.const 0)) (i32.const 213)) +(assert_return (invoke "multiple-value" (i32.const 1)) (i32.const 212)) +(assert_return (invoke "multiple-value" (i32.const 2)) (i32.const 211)) +(assert_return (invoke "multiple-value" (i32.const 3)) (i32.const 210)) +(assert_return (invoke "multiple-value" (i32.const 4)) (i32.const 214)) +(assert_return (invoke "multiple-value" (i32.const 5)) (i32.const 214)) +(assert_return (invoke "multiple-value" (i32.const 6)) (i32.const 214)) +(assert_return (invoke "multiple-value" (i32.const 10)) (i32.const 214)) +(assert_return (invoke "multiple-value" (i32.const -1)) (i32.const 214)) +(assert_return (invoke "multiple-value" (i32.const 0xffffffff)) (i32.const 214)) + +(assert_return (invoke "large" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "large" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "large" (i32.const 100)) (i32.const 0)) +(assert_return (invoke "large" (i32.const 101)) (i32.const 1)) +(assert_return (invoke "large" (i32.const 10000)) (i32.const 0)) +(assert_return (invoke "large" (i32.const 10001)) (i32.const 1)) +(assert_return (invoke "large" (i32.const 1000000)) (i32.const 1)) +(assert_return (invoke "large" (i32.const 1000001)) (i32.const 1)) + +(assert_return (invoke "as-block-first")) +(assert_return (invoke "as-block-mid")) +(assert_return (invoke "as-block-last")) +(assert_return (invoke "as-block-value") (i32.const 2)) + +(assert_return (invoke "as-loop-first") (i32.const 3)) +(assert_return (invoke "as-loop-mid") (i32.const 4)) +(assert_return (invoke "as-loop-last") (i32.const 5)) + +(assert_return (invoke "as-br-value") (i32.const 9)) + +(assert_return (invoke "as-br_if-cond")) +(assert_return (invoke "as-br_if-value") (i32.const 8)) +(assert_return (invoke "as-br_if-value-cond") (i32.const 9)) + +(assert_return (invoke "as-br_table-index")) +(assert_return (invoke "as-br_table-value") (i32.const 10)) +(assert_return (invoke "as-br_table-value-index") (i32.const 11)) + +(assert_return (invoke "as-return-value") (i64.const 7)) + +(assert_return (invoke "as-if-cond") (i32.const 2)) +(assert_return (invoke "as-if-then" (i32.const 1) (i32.const 6)) (i32.const 3)) +(assert_return (invoke "as-if-then" (i32.const 0) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-if-else" (i32.const 0) (i32.const 6)) (i32.const 4)) +(assert_return (invoke "as-if-else" (i32.const 1) (i32.const 6)) (i32.const 6)) + +(assert_return (invoke "as-select-first" (i32.const 0) (i32.const 6)) (i32.const 5)) +(assert_return (invoke "as-select-first" (i32.const 1) (i32.const 6)) (i32.const 5)) +(assert_return (invoke "as-select-second" (i32.const 0) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-select-second" (i32.const 1) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-select-cond") (i32.const 7)) + +(assert_return (invoke "as-call-first") (i32.const 12)) +(assert_return (invoke "as-call-mid") (i32.const 13)) +(assert_return (invoke "as-call-last") (i32.const 14)) + +(assert_return (invoke "as-call_indirect-first") (i32.const 20)) +(assert_return (invoke "as-call_indirect-mid") (i32.const 21)) +(assert_return (invoke "as-call_indirect-last") (i32.const 22)) +(assert_return (invoke "as-call_indirect-func") (i32.const 23)) + +(assert_return (invoke "as-local.set-value") (i32.const 17)) +(assert_return (invoke "as-local.tee-value") (i32.const 1)) +(assert_return (invoke "as-global.set-value") (i32.const 1)) + +(assert_return (invoke "as-load-address") (f32.const 1.7)) +(assert_return (invoke "as-loadN-address") (i64.const 30)) + +(assert_return (invoke "as-store-address") (i32.const 30)) +(assert_return (invoke "as-store-value") (i32.const 31)) +(assert_return (invoke "as-storeN-address") (i32.const 32)) +(assert_return (invoke "as-storeN-value") (i32.const 33)) + +(assert_return (invoke "as-unary-operand") (f32.const 3.4)) + +(assert_return (invoke "as-binary-left") (i32.const 3)) +(assert_return (invoke "as-binary-right") (i64.const 45)) + +(assert_return (invoke "as-test-operand") (i32.const 44)) + +(assert_return (invoke "as-compare-left") (i32.const 43)) +(assert_return (invoke "as-compare-right") (i32.const 42)) + +(assert_return (invoke "as-convert-operand") (i32.const 41)) + +(assert_return (invoke "as-memory.grow-size") (i32.const 40)) + +(assert_return (invoke "nested-block-value" (i32.const 0)) (i32.const 19)) +(assert_return (invoke "nested-block-value" (i32.const 1)) (i32.const 17)) +(assert_return (invoke "nested-block-value" (i32.const 2)) (i32.const 16)) +(assert_return (invoke "nested-block-value" (i32.const 10)) (i32.const 16)) +(assert_return (invoke "nested-block-value" (i32.const -1)) (i32.const 16)) +(assert_return (invoke "nested-block-value" (i32.const 100000)) (i32.const 16)) + +(assert_return (invoke "nested-br-value" (i32.const 0)) (i32.const 8)) +(assert_return (invoke "nested-br-value" (i32.const 1)) (i32.const 9)) +(assert_return (invoke "nested-br-value" (i32.const 2)) (i32.const 17)) +(assert_return (invoke "nested-br-value" (i32.const 11)) (i32.const 17)) +(assert_return (invoke "nested-br-value" (i32.const -4)) (i32.const 17)) +(assert_return (invoke "nested-br-value" (i32.const 10213210)) (i32.const 17)) + +(assert_return (invoke "nested-br_if-value" (i32.const 0)) (i32.const 17)) +(assert_return (invoke "nested-br_if-value" (i32.const 1)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value" (i32.const 2)) (i32.const 8)) +(assert_return (invoke "nested-br_if-value" (i32.const 9)) (i32.const 8)) +(assert_return (invoke "nested-br_if-value" (i32.const -9)) (i32.const 8)) +(assert_return (invoke "nested-br_if-value" (i32.const 999999)) (i32.const 8)) + +(assert_return (invoke "nested-br_if-value-cond" (i32.const 0)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const 1)) (i32.const 8)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const 2)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const 3)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const -1000000)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const 9423975)) (i32.const 9)) + +(assert_return (invoke "nested-br_table-value" (i32.const 0)) (i32.const 17)) +(assert_return (invoke "nested-br_table-value" (i32.const 1)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value" (i32.const 2)) (i32.const 8)) +(assert_return (invoke "nested-br_table-value" (i32.const 9)) (i32.const 8)) +(assert_return (invoke "nested-br_table-value" (i32.const -9)) (i32.const 8)) +(assert_return (invoke "nested-br_table-value" (i32.const 999999)) (i32.const 8)) + +(assert_return (invoke "nested-br_table-value-index" (i32.const 0)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value-index" (i32.const 1)) (i32.const 8)) +(assert_return (invoke "nested-br_table-value-index" (i32.const 2)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value-index" (i32.const 3)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value-index" (i32.const -1000000)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value-index" (i32.const 9423975)) (i32.const 9)) + +(assert_return (invoke "nested-br_table-loop-block" (i32.const 1)) (i32.const 3)) + +(assert_return (invoke "meet-externref" (i32.const 0) (ref.extern 1)) (ref.extern 1)) +(assert_return (invoke "meet-externref" (i32.const 1) (ref.extern 1)) (ref.extern 1)) +(assert_return (invoke "meet-externref" (i32.const 2) (ref.extern 1)) (ref.extern 1)) + +(assert_invalid + (module (func $type-arg-void-vs-num (result i32) + (block (br_table 0 (i32.const 1)) (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-arg-empty-vs-num (result i32) + (block (br_table 0) (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-arg-void-vs-num (result i32) + (block (result i32) (br_table 0 (nop) (i32.const 1)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-num-vs-num (result i32) + (block (result i32) + (br_table 0 0 0 (i64.const 1) (i32.const 1)) (i32.const 1) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-num-vs-arg-num + (block + (block (result f32) + (br_table 0 1 (f32.const 0) (i32.const 0)) + ) + (drop) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func + (block (result i32) + (block (result i64) + (br_table 0 1 (i32.const 0) (i32.const 0)) + ) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-index-void-vs-i32 + (block (br_table 0 0 0 (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-index-num-vs-i32 + (block (br_table 0 (i64.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-index-void-vs-i32 (result i32) + (block (result i32) (br_table 0 0 (i32.const 0) (nop)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-void-vs-num-nested (result i32) + (block (result i32) (i32.const 0) (block (br_table 1 (i32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-index-num-vs-i32 (result i32) + (block (result i32) + (br_table 0 0 (i32.const 0) (i64.const 0)) (i32.const 1) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-arg-void-vs-num (result i32) + (block (br_table 0 (i32.const 1)) (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module + (func $type-arg-index-empty-in-then + (block + (i32.const 0) (i32.const 0) + (if (result i32) (then (br_table 0))) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-value-empty-in-then + (block + (i32.const 0) (i32.const 0) + (if (result i32) (then (br_table 0 (i32.const 1)))) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-index-empty-in-return + (block (result i32) + (return (br_table 0)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-value-empty-in-return + (block (result i32) + (return (br_table 0 (i32.const 1))) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (func (param i32) (result i32) + (loop (result i32) + (block (result i32) + (br_table 0 1 (i32.const 1) (local.get 0)) + ) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (param i32) (result i32) + (block (result i32) + (loop (result i32) + (br_table 0 1 (i32.const 1) (local.get 0)) + ) + ) + ) + ) + "type mismatch" +) + + +(assert_invalid + (module (func $meet-bottom (param i32) (result externref) + (block $l1 (result externref) + (drop + (block $l2 (result i32) + (br_table $l2 $l1 $l2 (ref.null extern) (local.get 0)) + ) + ) + (ref.null extern) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $unbound-label + (block (br_table 2 1 (i32.const 1))) + )) + "unknown label" +) +(assert_invalid + (module (func $unbound-nested-label + (block (block (br_table 0 5 (i32.const 1)))) + )) + "unknown label" +) +(assert_invalid + (module (func $large-label + (block (br_table 0 0x10000001 0 (i32.const 1))) + )) + "unknown label" +) + +(assert_invalid + (module (func $unbound-label-default + (block (br_table 1 2 (i32.const 1))) + )) + "unknown label" +) +(assert_invalid + (module (func $unbound-nested-label-default + (block (block (br_table 0 5 (i32.const 1)))) + )) + "unknown label" +) +(assert_invalid + (module (func $large-label-default + (block (br_table 0 0 0x10000001 (i32.const 1))) + )) + "unknown label" +) + diff --git a/runtime/unc-vm/tests/wast/spec/bulk.wast b/runtime/unc-vm/tests/wast/spec/bulk.wast new file mode 100644 index 000000000..5dcac724c --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/bulk.wast @@ -0,0 +1,351 @@ +;; segment syntax +(module + (memory 1) + (data "foo")) + +(module + (table 3 funcref) + (elem funcref (ref.func 0) (ref.null func) (ref.func 1)) + (func) + (func)) + +;; memory.fill +(module + (memory 1) + + (func (export "fill") (param i32 i32 i32) + (memory.fill + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0))) +) + +;; Basic fill test. +(invoke "fill" (i32.const 1) (i32.const 0xff) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0xff)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 0xff)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 0xff)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 0)) + +;; Fill value is stored as a byte. +(invoke "fill" (i32.const 0) (i32.const 0xbbaa) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0xaa)) + +;; Fill all of memory +(invoke "fill" (i32.const 0) (i32.const 0) (i32.const 0x10000)) + +;; Out-of-bounds writes trap, and nothing is written +(assert_trap (invoke "fill" (i32.const 0xff00) (i32.const 1) (i32.const 0x101)) + "out of bounds memory access") +(assert_return (invoke "load8_u" (i32.const 0xff00)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 0xffff)) (i32.const 0)) + +;; Succeed when writing 0 bytes at the end of the region. +(invoke "fill" (i32.const 0x10000) (i32.const 0) (i32.const 0)) + +;; Writing 0 bytes outside the memory traps. +(assert_trap (invoke "fill" (i32.const 0x10001) (i32.const 0) (i32.const 0)) + "out of bounds memory access") + + +;; memory.copy +(module + (memory (data "\aa\bb\cc\dd")) + + (func (export "copy") (param i32 i32 i32) + (memory.copy + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0))) +) + +;; Non-overlapping copy. +(invoke "copy" (i32.const 10) (i32.const 0) (i32.const 4)) + +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0xdd)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 0)) + +;; Overlap, source > dest +(invoke "copy" (i32.const 8) (i32.const 10) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0xdd)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0xdd)) + +;; Overlap, source < dest +(invoke "copy" (i32.const 10) (i32.const 7) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 0xdd)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 0)) + +;; Copy ending at memory limit is ok. +(invoke "copy" (i32.const 0xff00) (i32.const 0) (i32.const 0x100)) +(invoke "copy" (i32.const 0xfe00) (i32.const 0xff00) (i32.const 0x100)) + +;; Succeed when copying 0 bytes at the end of the region. +(invoke "copy" (i32.const 0x10000) (i32.const 0) (i32.const 0)) +(invoke "copy" (i32.const 0) (i32.const 0x10000) (i32.const 0)) + +;; Copying 0 bytes outside the memory traps. +(assert_trap (invoke "copy" (i32.const 0x10001) (i32.const 0) (i32.const 0)) + "out of bounds memory access") +(assert_trap (invoke "copy" (i32.const 0) (i32.const 0x10001) (i32.const 0)) + "out of bounds memory access") + + +;; memory.init +(module + (memory 1) + (data "\aa\bb\cc\dd") + + (func (export "init") (param i32 i32 i32) + (memory.init 0 + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0))) +) + +(invoke "init" (i32.const 0) (i32.const 1) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 0)) + +;; Init ending at memory limit and segment limit is ok. +(invoke "init" (i32.const 0xfffc) (i32.const 0) (i32.const 4)) + +;; Out-of-bounds writes trap, and nothing is written. +(assert_trap (invoke "init" (i32.const 0xfffe) (i32.const 0) (i32.const 3)) + "out of bounds memory access") +(assert_return (invoke "load8_u" (i32.const 0xfffe)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 0xffff)) (i32.const 0xdd)) + +;; Succeed when writing 0 bytes at the end of either region. +(invoke "init" (i32.const 0x10000) (i32.const 0) (i32.const 0)) +(invoke "init" (i32.const 0) (i32.const 4) (i32.const 0)) + +;; Writing 0 bytes outside the memory traps. +(assert_trap (invoke "init" (i32.const 0x10001) (i32.const 0) (i32.const 0)) + "out of bounds memory access") +(assert_trap (invoke "init" (i32.const 0) (i32.const 5) (i32.const 0)) + "out of bounds memory access") + +;; data.drop +(module + (memory 1) + (data $p "x") + (data $a (memory 0) (i32.const 0) "x") + + (func (export "drop_passive") (data.drop $p)) + (func (export "init_passive") (param $len i32) + (memory.init $p (i32.const 0) (i32.const 0) (local.get $len))) + + (func (export "drop_active") (data.drop $a)) + (func (export "init_active") (param $len i32) + (memory.init $a (i32.const 0) (i32.const 0) (local.get $len))) +) + +(invoke "init_passive" (i32.const 1)) +(invoke "drop_passive") +(invoke "drop_passive") +(assert_return (invoke "init_passive" (i32.const 0))) +(assert_trap (invoke "init_passive" (i32.const 1)) "out of bounds memory access") +(invoke "init_passive" (i32.const 0)) +(invoke "drop_active") +(assert_return (invoke "init_active" (i32.const 0))) +(assert_trap (invoke "init_active" (i32.const 1)) "out of bounds memory access") +(invoke "init_active" (i32.const 0)) + +;; Test that the data segment index is properly encoded as an unsigned (not +;; signed) LEB. +(module + ;; 65 data segments. 64 is the smallest positive number that is encoded + ;; differently as a signed LEB. + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") + (func (data.drop 64))) + +;; No memory is required for the data.drop instruction. +(module (data "goodbye") (func (data.drop 0))) + +;; table.init +(module + (table 3 funcref) + (elem funcref + (ref.func $zero) (ref.func $one) (ref.func $zero) (ref.func $one)) + + (func $zero (result i32) (i32.const 0)) + (func $one (result i32) (i32.const 1)) + + (func (export "init") (param i32 i32 i32) + (table.init 0 + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "call") (param i32) (result i32) + (call_indirect (result i32) + (local.get 0))) +) + +;; Out-of-bounds stores trap, and nothing is written. +(assert_trap (invoke "init" (i32.const 2) (i32.const 0) (i32.const 2)) + "out of bounds table access") +(assert_trap (invoke "call" (i32.const 2)) + "uninitialized element 2") + +(invoke "init" (i32.const 0) (i32.const 1) (i32.const 2)) +(assert_return (invoke "call" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "call" (i32.const 1)) (i32.const 0)) +(assert_trap (invoke "call" (i32.const 2)) "uninitialized element") + +;; Init ending at table limit and segment limit is ok. +(invoke "init" (i32.const 1) (i32.const 2) (i32.const 2)) + +;; Succeed when storing 0 elements at the end of either region. +(invoke "init" (i32.const 3) (i32.const 0) (i32.const 0)) +(invoke "init" (i32.const 0) (i32.const 4) (i32.const 0)) + +;; Writing 0 elements outside the table traps. +(assert_trap (invoke "init" (i32.const 4) (i32.const 0) (i32.const 0)) + "out of bounds table access") +(assert_trap (invoke "init" (i32.const 0) (i32.const 5) (i32.const 0)) + "out of bounds table access") + + +;; elem.drop +(module + (table 1 funcref) + (func $f) + (elem $p funcref (ref.func $f)) + (elem $a (table 0) (i32.const 0) func $f) + + (func (export "drop_passive") (elem.drop $p)) + (func (export "init_passive") (param $len i32) + (table.init $p (i32.const 0) (i32.const 0) (local.get $len)) + ) + + (func (export "drop_active") (elem.drop $a)) + (func (export "init_active") (param $len i32) + (table.init $a (i32.const 0) (i32.const 0) (local.get $len)) + ) +) + +(invoke "init_passive" (i32.const 1)) +(invoke "drop_passive") +(invoke "drop_passive") +(assert_return (invoke "init_passive" (i32.const 0))) +(assert_trap (invoke "init_passive" (i32.const 1)) "out of bounds table access") +(invoke "init_passive" (i32.const 0)) +(invoke "drop_active") +(assert_return (invoke "init_active" (i32.const 0))) +(assert_trap (invoke "init_active" (i32.const 1)) "out of bounds table access") +(invoke "init_active" (i32.const 0)) + +;; Test that the elem segment index is properly encoded as an unsigned (not +;; signed) LEB. +(module + ;; 65 elem segments. 64 is the smallest positive number that is encoded + ;; differently as a signed LEB. + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) + (func (elem.drop 64))) + +;; No table is required for the elem.drop instruction. +(module (elem funcref (ref.func 0)) (func (elem.drop 0))) + +;; table.copy +(module + (table 10 funcref) + (elem (i32.const 0) $zero $one $two) + (func $zero (result i32) (i32.const 0)) + (func $one (result i32) (i32.const 1)) + (func $two (result i32) (i32.const 2)) + + (func (export "copy") (param i32 i32 i32) + (table.copy + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "call") (param i32) (result i32) + (call_indirect (result i32) + (local.get 0))) +) + +;; Non-overlapping copy. +(invoke "copy" (i32.const 3) (i32.const 0) (i32.const 3)) +;; Now [$zero, $one, $two, $zero, $one, $two, ...] +(assert_return (invoke "call" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "call" (i32.const 4)) (i32.const 1)) +(assert_return (invoke "call" (i32.const 5)) (i32.const 2)) + +;; Overlap, source > dest +(invoke "copy" (i32.const 0) (i32.const 1) (i32.const 3)) +;; Now [$one, $two, $zero, $zero, $one, $two, ...] +(assert_return (invoke "call" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "call" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "call" (i32.const 2)) (i32.const 0)) + +;; Overlap, source < dest +(invoke "copy" (i32.const 2) (i32.const 0) (i32.const 3)) +;; Now [$one, $two, $one, $two, $zero, $two, ...] +(assert_return (invoke "call" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "call" (i32.const 3)) (i32.const 2)) +(assert_return (invoke "call" (i32.const 4)) (i32.const 0)) + +;; Copy ending at table limit is ok. +(invoke "copy" (i32.const 6) (i32.const 8) (i32.const 2)) +(invoke "copy" (i32.const 8) (i32.const 6) (i32.const 2)) + +;; Succeed when copying 0 elements at the end of the region. +(invoke "copy" (i32.const 10) (i32.const 0) (i32.const 0)) +(invoke "copy" (i32.const 0) (i32.const 10) (i32.const 0)) + +;; Fail on out-of-bounds when copying 0 elements outside of table. +(assert_trap (invoke "copy" (i32.const 11) (i32.const 0) (i32.const 0)) + "out of bounds table access") +(assert_trap (invoke "copy" (i32.const 0) (i32.const 11) (i32.const 0)) + "out of bounds table access") diff --git a/runtime/unc-vm/tests/wast/spec/call.wast b/runtime/unc-vm/tests/wast/spec/call.wast new file mode 100644 index 000000000..e4f854f7a --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/call.wast @@ -0,0 +1,518 @@ +;; Test `call` operator + +(module + ;; Auxiliary definitions + (func $const-i32 (result i32) (i32.const 0x132)) + (func $const-i64 (result i64) (i64.const 0x164)) + (func $const-f32 (result f32) (f32.const 0xf32)) + (func $const-f64 (result f64) (f64.const 0xf64)) + (func $const-i32-i64 (result i32 i64) (i32.const 0x132) (i64.const 0x164)) + + (func $id-i32 (param i32) (result i32) (local.get 0)) + (func $id-i64 (param i64) (result i64) (local.get 0)) + (func $id-f32 (param f32) (result f32) (local.get 0)) + (func $id-f64 (param f64) (result f64) (local.get 0)) + (func $id-i32-f64 (param i32 f64) (result i32 f64) + (local.get 0) (local.get 1) + ) + + (func $swap-i32-i32 (param i32 i32) (result i32 i32) + (local.get 1) (local.get 0) + ) + (func $swap-f32-f64 (param f32 f64) (result f64 f32) + (local.get 1) (local.get 0) + ) + (func $swap-f64-i32 (param f64 i32) (result i32 f64) + (local.get 1) (local.get 0) + ) + + (func $f32-i32 (param f32 i32) (result i32) (local.get 1)) + (func $i32-i64 (param i32 i64) (result i64) (local.get 1)) + (func $f64-f32 (param f64 f32) (result f32) (local.get 1)) + (func $i64-f64 (param i64 f64) (result f64) (local.get 1)) + + ;; Typing + + (func (export "type-i32") (result i32) (call $const-i32)) + (func (export "type-i64") (result i64) (call $const-i64)) + (func (export "type-f32") (result f32) (call $const-f32)) + (func (export "type-f64") (result f64) (call $const-f64)) + (func (export "type-i32-i64") (result i32 i64) (call $const-i32-i64)) + + (func (export "type-first-i32") (result i32) (call $id-i32 (i32.const 32))) + (func (export "type-first-i64") (result i64) (call $id-i64 (i64.const 64))) + (func (export "type-first-f32") (result f32) (call $id-f32 (f32.const 1.32))) + (func (export "type-first-f64") (result f64) (call $id-f64 (f64.const 1.64))) + + (func (export "type-second-i32") (result i32) + (call $f32-i32 (f32.const 32.1) (i32.const 32)) + ) + (func (export "type-second-i64") (result i64) + (call $i32-i64 (i32.const 32) (i64.const 64)) + ) + (func (export "type-second-f32") (result f32) + (call $f64-f32 (f64.const 64) (f32.const 32)) + ) + (func (export "type-second-f64") (result f64) + (call $i64-f64 (i64.const 64) (f64.const 64.1)) + ) + + (func (export "type-all-i32-f64") (result i32 f64) + (call $id-i32-f64 (i32.const 32) (f64.const 1.64)) + ) + (func (export "type-all-i32-i32") (result i32 i32) + (call $swap-i32-i32 (i32.const 1) (i32.const 2)) + ) + (func (export "type-all-f32-f64") (result f64 f32) + (call $swap-f32-f64 (f32.const 1) (f64.const 2)) + ) + (func (export "type-all-f64-i32") (result i32 f64) + (call $swap-f64-i32 (f64.const 1) (i32.const 2)) + ) + + ;; Composition + + (func (export "as-binary-all-operands") (result i32) + (i32.add (call $swap-i32-i32 (i32.const 3) (i32.const 4))) + ) + + (func (export "as-mixed-operands") (result i32) + (call $swap-i32-i32 (i32.const 3) (i32.const 4)) + (i32.const 5) + (i32.add) + (i32.mul) + ) + + (func (export "as-call-all-operands") (result i32 i32) + (call $swap-i32-i32 (call $swap-i32-i32 (i32.const 3) (i32.const 4))) + ) + + ;; Recursion + + (func $fac (export "fac") (param i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (i64.const 1)) + (else + (i64.mul + (local.get 0) + (call $fac (i64.sub (local.get 0) (i64.const 1))) + ) + ) + ) + ) + + (func $fac-acc (export "fac-acc") (param i64 i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (local.get 1)) + (else + (call $fac-acc + (i64.sub (local.get 0) (i64.const 1)) + (i64.mul (local.get 0) (local.get 1)) + ) + ) + ) + ) + + (func $fib (export "fib") (param i64) (result i64) + (if (result i64) (i64.le_u (local.get 0) (i64.const 1)) + (then (i64.const 1)) + (else + (i64.add + (call $fib (i64.sub (local.get 0) (i64.const 2))) + (call $fib (i64.sub (local.get 0) (i64.const 1))) + ) + ) + ) + ) + + (func $even (export "even") (param i64) (result i32) + (if (result i32) (i64.eqz (local.get 0)) + (then (i32.const 44)) + (else (call $odd (i64.sub (local.get 0) (i64.const 1)))) + ) + ) + (func $odd (export "odd") (param i64) (result i32) + (if (result i32) (i64.eqz (local.get 0)) + (then (i32.const 99)) + (else (call $even (i64.sub (local.get 0) (i64.const 1)))) + ) + ) + + ;; Stack exhaustion + + ;; Implementations are required to have every call consume some abstract + ;; resource towards exhausting some abstract finite limit, such that + ;; infinitely recursive test cases reliably trap in finite time. This is + ;; because otherwise applications could come to depend on it on those + ;; implementations and be incompatible with implementations that don't do + ;; it (or don't do it under the same circumstances). + + (func $runaway (export "runaway") (call $runaway)) + + (func $mutual-runaway1 (export "mutual-runaway") (call $mutual-runaway2)) + (func $mutual-runaway2 (call $mutual-runaway1)) + + ;; As parameter of control constructs and instructions + + (memory 1) + + (func (export "as-select-first") (result i32) + (select (call $const-i32) (i32.const 2) (i32.const 3)) + ) + (func (export "as-select-mid") (result i32) + (select (i32.const 2) (call $const-i32) (i32.const 3)) + ) + (func (export "as-select-last") (result i32) + (select (i32.const 2) (i32.const 3) (call $const-i32)) + ) + + (func (export "as-if-condition") (result i32) + (if (result i32) (call $const-i32) (then (i32.const 1)) (else (i32.const 2))) + ) + + (func (export "as-br_if-first") (result i32) + (block (result i32) (br_if 0 (call $const-i32) (i32.const 2))) + ) + (func (export "as-br_if-last") (result i32) + (block (result i32) (br_if 0 (i32.const 2) (call $const-i32))) + ) + + (func (export "as-br_table-first") (result i32) + (block (result i32) (call $const-i32) (i32.const 2) (br_table 0 0)) + ) + (func (export "as-br_table-last") (result i32) + (block (result i32) (i32.const 2) (call $const-i32) (br_table 0 0)) + ) + + (func $func (param i32 i32) (result i32) (local.get 0)) + (type $check (func (param i32 i32) (result i32))) + (table funcref (elem $func)) + (func (export "as-call_indirect-first") (result i32) + (block (result i32) + (call_indirect (type $check) + (call $const-i32) (i32.const 2) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 2) (call $const-i32) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 1) (i32.const 2) (call $const-i32) + ) + ) + ) + + (func (export "as-store-first") + (call $const-i32) (i32.const 1) (i32.store) + ) + (func (export "as-store-last") + (i32.const 10) (call $const-i32) (i32.store) + ) + + (func (export "as-memory.grow-value") (result i32) + (memory.grow (call $const-i32)) + ) + (func (export "as-return-value") (result i32) + (call $const-i32) (return) + ) + (func (export "as-drop-operand") + (call $const-i32) (drop) + ) + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (call $const-i32))) + ) + (func (export "as-local.set-value") (result i32) + (local i32) (local.set 0 (call $const-i32)) (local.get 0) + ) + (func (export "as-local.tee-value") (result i32) + (local i32) (local.tee 0 (call $const-i32)) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (result i32) + (global.set $a (call $const-i32)) + (global.get $a) + ) + (func (export "as-load-operand") (result i32) + (i32.load (call $const-i32)) + ) + + (func $dummy (param i32) (result i32) (local.get 0)) + (func $du (param f32) (result f32) (local.get 0)) + (func (export "as-unary-operand") (result f32) + (block (result f32) (f32.sqrt (call $du (f32.const 0x0p+0)))) + ) + + (func (export "as-binary-left") (result i32) + (block (result i32) (i32.add (call $dummy (i32.const 1)) (i32.const 10))) + ) + (func (export "as-binary-right") (result i32) + (block (result i32) (i32.sub (i32.const 10) (call $dummy (i32.const 1)))) + ) + + (func (export "as-test-operand") (result i32) + (block (result i32) (i32.eqz (call $dummy (i32.const 1)))) + ) + + (func (export "as-compare-left") (result i32) + (block (result i32) (i32.le_u (call $dummy (i32.const 1)) (i32.const 10))) + ) + (func (export "as-compare-right") (result i32) + (block (result i32) (i32.ne (i32.const 10) (call $dummy (i32.const 1)))) + ) + + (func (export "as-convert-operand") (result i64) + (block (result i64) (i64.extend_i32_s (call $dummy (i32.const 1)))) + ) + + ;; Test correct argument passing + + (func $return-from-long-argument-list-helper (param f32 i32 i32 f64 f32 f32 f32 f64 f32 i32 i32 f32 f64 i64 i64 i32 i64 i64 f32 i64 i64 i64 i32 f32 f32 f32 f64 f32 i32 i64 f32 f64 f64 f32 i32 f32 f32 f64 i64 f64 i32 i64 f32 f64 i32 i32 i32 i64 f64 i32 i64 i64 f64 f64 f64 f64 f64 f64 i32 f32 f64 f64 i32 i64 f32 f32 f32 i32 f64 f64 f64 f64 f64 f32 i64 i64 i32 i32 i32 f32 f64 i32 i64 f32 f32 f32 i32 i32 f32 f64 i64 f32 f64 f32 f32 f32 i32 f32 i64 i32) (result i32) + (local.get 99) + ) + + (func (export "return-from-long-argument-list") (param i32) (result i32) + (call $return-from-long-argument-list-helper (f32.const 0) (i32.const 0) (i32.const 0) (f64.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f64.const 0) (f32.const 0) (i32.const 0) (i32.const 0) (f32.const 0) (f64.const 0) (i64.const 0) (i64.const 0) (i32.const 0) (i64.const 0) (i64.const 0) (f32.const 0) (i64.const 0) (i64.const 0) (i64.const 0) (i32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f64.const 0) (f32.const 0) (i32.const 0) (i64.const 0) (f32.const 0) (f64.const 0) (f64.const 0) (f32.const 0) (i32.const 0) (f32.const 0) (f32.const 0) (f64.const 0) (i64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (f32.const 0) (f64.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (i64.const 0) (f64.const 0) (f64.const 0) (f64.const 0) (f64.const 0) (f64.const 0) (f64.const 0) (i32.const 0) (f32.const 0) (f64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (i32.const 0) (f64.const 0) (f64.const 0) (f64.const 0) (f64.const 0) (f64.const 0) (f32.const 0) (i64.const 0) (i64.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (f32.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (i32.const 0) (i32.const 0) (f32.const 0) (f64.const 0) (i64.const 0) (f32.const 0) (f64.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (i32.const 0) (f32.const 0) (i64.const 0) (local.get 0)) + ) +) + +(assert_return (invoke "type-i32") (i32.const 0x132)) +(assert_return (invoke "type-i64") (i64.const 0x164)) +(assert_return (invoke "type-f32") (f32.const 0xf32)) +(assert_return (invoke "type-f64") (f64.const 0xf64)) +(assert_return (invoke "type-i32-i64") (i32.const 0x132) (i64.const 0x164)) + +(assert_return (invoke "type-first-i32") (i32.const 32)) +(assert_return (invoke "type-first-i64") (i64.const 64)) +(assert_return (invoke "type-first-f32") (f32.const 1.32)) +(assert_return (invoke "type-first-f64") (f64.const 1.64)) + +(assert_return (invoke "type-second-i32") (i32.const 32)) +(assert_return (invoke "type-second-i64") (i64.const 64)) +(assert_return (invoke "type-second-f32") (f32.const 32)) +(assert_return (invoke "type-second-f64") (f64.const 64.1)) + +(assert_return (invoke "type-all-i32-f64") (i32.const 32) (f64.const 1.64)) +(assert_return (invoke "type-all-i32-i32") (i32.const 2) (i32.const 1)) +(assert_return (invoke "type-all-f32-f64") (f64.const 2) (f32.const 1)) +(assert_return (invoke "type-all-f64-i32") (i32.const 2) (f64.const 1)) + +(assert_return (invoke "as-binary-all-operands") (i32.const 7)) +(assert_return (invoke "as-mixed-operands") (i32.const 32)) +(assert_return (invoke "as-call-all-operands") (i32.const 3) (i32.const 4)) + +(assert_return (invoke "fac" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "fac" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac" (i64.const 5)) (i64.const 120)) +(assert_return (invoke "fac" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-acc" (i64.const 0) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 5) (i64.const 1)) (i64.const 120)) +(assert_return + (invoke "fac-acc" (i64.const 25) (i64.const 1)) + (i64.const 7034535277573963776) +) + +(assert_return (invoke "fib" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "fib" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fib" (i64.const 2)) (i64.const 2)) +(assert_return (invoke "fib" (i64.const 5)) (i64.const 8)) +(assert_return (invoke "fib" (i64.const 20)) (i64.const 10946)) + +(assert_return (invoke "even" (i64.const 0)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 1)) (i32.const 99)) +(assert_return (invoke "even" (i64.const 100)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 77)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 0)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 1)) (i32.const 44)) +(assert_return (invoke "odd" (i64.const 200)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 77)) (i32.const 44)) + +(assert_exhaustion (invoke "runaway") "call stack exhausted") +(assert_exhaustion (invoke "mutual-runaway") "call stack exhausted") + +(assert_return (invoke "as-select-first") (i32.const 0x132)) +(assert_return (invoke "as-select-mid") (i32.const 2)) +(assert_return (invoke "as-select-last") (i32.const 2)) + +(assert_return (invoke "as-if-condition") (i32.const 1)) + +(assert_return (invoke "as-br_if-first") (i32.const 0x132)) +(assert_return (invoke "as-br_if-last") (i32.const 2)) + +(assert_return (invoke "as-br_table-first") (i32.const 0x132)) +(assert_return (invoke "as-br_table-last") (i32.const 2)) + +(assert_return (invoke "as-call_indirect-first") (i32.const 0x132)) +(assert_return (invoke "as-call_indirect-mid") (i32.const 2)) +(assert_trap (invoke "as-call_indirect-last") "undefined element") + +(assert_return (invoke "as-store-first")) +(assert_return (invoke "as-store-last")) + +(assert_return (invoke "as-memory.grow-value") (i32.const 1)) +(assert_return (invoke "as-return-value") (i32.const 0x132)) +(assert_return (invoke "as-drop-operand")) +(assert_return (invoke "as-br-value") (i32.const 0x132)) +(assert_return (invoke "as-local.set-value") (i32.const 0x132)) +(assert_return (invoke "as-local.tee-value") (i32.const 0x132)) +(assert_return (invoke "as-global.set-value") (i32.const 0x132)) +(assert_return (invoke "as-load-operand") (i32.const 1)) + +(assert_return (invoke "as-unary-operand") (f32.const 0x0p+0)) +(assert_return (invoke "as-binary-left") (i32.const 11)) +(assert_return (invoke "as-binary-right") (i32.const 9)) +(assert_return (invoke "as-test-operand") (i32.const 0)) +(assert_return (invoke "as-compare-left") (i32.const 1)) +(assert_return (invoke "as-compare-right") (i32.const 1)) +(assert_return (invoke "as-convert-operand") (i64.const 1)) + +(assert_return (invoke "return-from-long-argument-list" (i32.const 42)) (i32.const 42)) + +;; Invalid typing + +(assert_invalid + (module + (func $type-void-vs-num (i32.eqz (call 1))) + (func) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-num-vs-num (i32.eqz (call 1))) + (func (result i64) (i64.const 1)) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $arity-0-vs-1 (call 1)) + (func (param i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $arity-0-vs-2 (call 1)) + (func (param f64 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $arity-1-vs-0 (call 1 (i32.const 1))) + (func) + ) + "type mismatch" +) +(assert_invalid + (module + (func $arity-2-vs-0 (call 1 (f64.const 2) (i32.const 1))) + (func) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $type-first-void-vs-num (call 1 (nop) (i32.const 1))) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-void-vs-num (call 1 (i32.const 1) (nop))) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-first-num-vs-num (call 1 (f64.const 1) (i32.const 1))) + (func (param i32 f64)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-num-vs-num (call 1 (i32.const 1) (f64.const 1))) + (func (param f64 i32)) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $type-first-empty-in-block + (block (call 1)) + ) + (func (param i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-empty-in-block + (block (call 1 (i32.const 0))) + ) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-first-empty-in-loop + (loop (call 1)) + ) + (func (param i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-empty-in-loop + (loop (call 1 (i32.const 0))) + ) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-first-empty-in-then + (if (i32.const 0) (then (call 1))) + ) + (func (param i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-empty-in-then + (if (i32.const 0) (then (call 1 (i32.const 0)))) + ) + (func (param i32 i32)) + ) + "type mismatch" +) + + +;; Unbound function + +(assert_invalid + (module (func $unbound-func (call 1))) + "unknown function" +) +(assert_invalid + (module (func $large-func (call 1012321300))) + "unknown function" +) diff --git a/runtime/unc-vm/tests/wast/spec/call_indirect.wast b/runtime/unc-vm/tests/wast/spec/call_indirect.wast new file mode 100644 index 000000000..1ecd9b7ba --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/call_indirect.wast @@ -0,0 +1,1017 @@ +;; Test `call_indirect` operator + +(module + ;; Auxiliary definitions + (type $proc (func)) + (type $out-i32 (func (result i32))) + (type $out-i64 (func (result i64))) + (type $out-f32 (func (result f32))) + (type $out-f64 (func (result f64))) + (type $out-f64-i32 (func (result f64 i32))) + (type $over-i32 (func (param i32) (result i32))) + (type $over-i64 (func (param i64) (result i64))) + (type $over-f32 (func (param f32) (result f32))) + (type $over-f64 (func (param f64) (result f64))) + (type $over-i32-f64 (func (param i32 f64) (result i32 f64))) + (type $swap-i32-i64 (func (param i32 i64) (result i64 i32))) + (type $f32-i32 (func (param f32 i32) (result i32))) + (type $i32-i64 (func (param i32 i64) (result i64))) + (type $f64-f32 (func (param f64 f32) (result f32))) + (type $i64-f64 (func (param i64 f64) (result f64))) + (type $over-i32-duplicate (func (param i32) (result i32))) + (type $over-i64-duplicate (func (param i64) (result i64))) + (type $over-f32-duplicate (func (param f32) (result f32))) + (type $over-f64-duplicate (func (param f64) (result f64))) + + (func $const-i32 (type $out-i32) (i32.const 0x132)) + (func $const-i64 (type $out-i64) (i64.const 0x164)) + (func $const-f32 (type $out-f32) (f32.const 0xf32)) + (func $const-f64 (type $out-f64) (f64.const 0xf64)) + (func $const-f64-i32 (type $out-f64-i32) (f64.const 0xf64) (i32.const 32)) + + (func $id-i32 (type $over-i32) (local.get 0)) + (func $id-i64 (type $over-i64) (local.get 0)) + (func $id-f32 (type $over-f32) (local.get 0)) + (func $id-f64 (type $over-f64) (local.get 0)) + (func $id-i32-f64 (type $over-i32-f64) (local.get 0) (local.get 1)) + (func $swap-i32-i64 (type $swap-i32-i64) (local.get 1) (local.get 0)) + + (func $i32-i64 (type $i32-i64) (local.get 1)) + (func $i64-f64 (type $i64-f64) (local.get 1)) + (func $f32-i32 (type $f32-i32) (local.get 1)) + (func $f64-f32 (type $f64-f32) (local.get 1)) + + (func $over-i32-duplicate (type $over-i32-duplicate) (local.get 0)) + (func $over-i64-duplicate (type $over-i64-duplicate) (local.get 0)) + (func $over-f32-duplicate (type $over-f32-duplicate) (local.get 0)) + (func $over-f64-duplicate (type $over-f64-duplicate) (local.get 0)) + + (table funcref + (elem + $const-i32 $const-i64 $const-f32 $const-f64 ;; 0..3 + $id-i32 $id-i64 $id-f32 $id-f64 ;; 4..7 + $f32-i32 $i32-i64 $f64-f32 $i64-f64 ;; 9..11 + $fac-i64 $fib-i64 $even $odd ;; 12..15 + $runaway $mutual-runaway1 $mutual-runaway2 ;; 16..18 + $over-i32-duplicate $over-i64-duplicate ;; 19..20 + $over-f32-duplicate $over-f64-duplicate ;; 21..22 + $fac-i32 $fac-f32 $fac-f64 ;; 23..25 + $fib-i32 $fib-f32 $fib-f64 ;; 26..28 + $const-f64-i32 $id-i32-f64 $swap-i32-i64 ;; 29..31 + ) + ) + + ;; Syntax + + (func + (call_indirect (i32.const 0)) + (call_indirect (param i64) (i64.const 0) (i32.const 0)) + (call_indirect (param i64) (param) (param f64 i32 i64) + (i64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (i32.const 0) + ) + (call_indirect (result) (i32.const 0)) + (drop (i32.eqz (call_indirect (result i32) (i32.const 0)))) + (drop (i32.eqz (call_indirect (result i32) (result) (i32.const 0)))) + (drop (i32.eqz + (call_indirect (param i64) (result i32) (i64.const 0) (i32.const 0)) + )) + (drop (i32.eqz + (call_indirect + (param) (param i64) (param) (param f64 i32 i64) (param) (param) + (result) (result i32) (result) (result) + (i64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (i32.const 0) + ) + )) + (drop (i64.eqz + (call_indirect (type $over-i64) (param i64) (result i64) + (i64.const 0) (i32.const 0) + ) + )) + ) + + ;; Typing + + (func (export "type-i32") (result i32) + (call_indirect (type $out-i32) (i32.const 0)) + ) + (func (export "type-i64") (result i64) + (call_indirect (type $out-i64) (i32.const 1)) + ) + (func (export "type-f32") (result f32) + (call_indirect (type $out-f32) (i32.const 2)) + ) + (func (export "type-f64") (result f64) + (call_indirect (type $out-f64) (i32.const 3)) + ) + (func (export "type-f64-i32") (result f64 i32) + (call_indirect (type $out-f64-i32) (i32.const 29)) + ) + + (func (export "type-index") (result i64) + (call_indirect (type $over-i64) (i64.const 100) (i32.const 5)) + ) + + (func (export "type-first-i32") (result i32) + (call_indirect (type $over-i32) (i32.const 32) (i32.const 4)) + ) + (func (export "type-first-i64") (result i64) + (call_indirect (type $over-i64) (i64.const 64) (i32.const 5)) + ) + (func (export "type-first-f32") (result f32) + (call_indirect (type $over-f32) (f32.const 1.32) (i32.const 6)) + ) + (func (export "type-first-f64") (result f64) + (call_indirect (type $over-f64) (f64.const 1.64) (i32.const 7)) + ) + + (func (export "type-second-i32") (result i32) + (call_indirect (type $f32-i32) (f32.const 32.1) (i32.const 32) (i32.const 8)) + ) + (func (export "type-second-i64") (result i64) + (call_indirect (type $i32-i64) (i32.const 32) (i64.const 64) (i32.const 9)) + ) + (func (export "type-second-f32") (result f32) + (call_indirect (type $f64-f32) (f64.const 64) (f32.const 32) (i32.const 10)) + ) + (func (export "type-second-f64") (result f64) + (call_indirect (type $i64-f64) (i64.const 64) (f64.const 64.1) (i32.const 11)) + ) + + (func (export "type-all-f64-i32") (result f64 i32) + (call_indirect (type $out-f64-i32) (i32.const 29)) + ) + (func (export "type-all-i32-f64") (result i32 f64) + (call_indirect (type $over-i32-f64) + (i32.const 1) (f64.const 2) (i32.const 30) + ) + ) + (func (export "type-all-i32-i64") (result i64 i32) + (call_indirect (type $swap-i32-i64) + (i32.const 1) (i64.const 2) (i32.const 31) + ) + ) + + ;; Dispatch + + (func (export "dispatch") (param i32 i64) (result i64) + (call_indirect (type $over-i64) (local.get 1) (local.get 0)) + ) + + (func (export "dispatch-structural-i64") (param i32) (result i64) + (call_indirect (type $over-i64-duplicate) (i64.const 9) (local.get 0)) + ) + (func (export "dispatch-structural-i32") (param i32) (result i32) + (call_indirect (type $over-i32-duplicate) (i32.const 9) (local.get 0)) + ) + (func (export "dispatch-structural-f32") (param i32) (result f32) + (call_indirect (type $over-f32-duplicate) (f32.const 9.0) (local.get 0)) + ) + (func (export "dispatch-structural-f64") (param i32) (result f64) + (call_indirect (type $over-f64-duplicate) (f64.const 9.0) (local.get 0)) + ) + + ;; Recursion + + (func $fac-i64 (export "fac-i64") (type $over-i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (i64.const 1)) + (else + (i64.mul + (local.get 0) + (call_indirect (type $over-i64) + (i64.sub (local.get 0) (i64.const 1)) + (i32.const 12) + ) + ) + ) + ) + ) + + (func $fib-i64 (export "fib-i64") (type $over-i64) + (if (result i64) (i64.le_u (local.get 0) (i64.const 1)) + (then (i64.const 1)) + (else + (i64.add + (call_indirect (type $over-i64) + (i64.sub (local.get 0) (i64.const 2)) + (i32.const 13) + ) + (call_indirect (type $over-i64) + (i64.sub (local.get 0) (i64.const 1)) + (i32.const 13) + ) + ) + ) + ) + ) + + (func $fac-i32 (export "fac-i32") (type $over-i32) + (if (result i32) (i32.eqz (local.get 0)) + (then (i32.const 1)) + (else + (i32.mul + (local.get 0) + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 23) + ) + ) + ) + ) + ) + + (func $fac-f32 (export "fac-f32") (type $over-f32) + (if (result f32) (f32.eq (local.get 0) (f32.const 0.0)) + (then (f32.const 1.0)) + (else + (f32.mul + (local.get 0) + (call_indirect (type $over-f32) + (f32.sub (local.get 0) (f32.const 1.0)) + (i32.const 24) + ) + ) + ) + ) + ) + + (func $fac-f64 (export "fac-f64") (type $over-f64) + (if (result f64) (f64.eq (local.get 0) (f64.const 0.0)) + (then (f64.const 1.0)) + (else + (f64.mul + (local.get 0) + (call_indirect (type $over-f64) + (f64.sub (local.get 0) (f64.const 1.0)) + (i32.const 25) + ) + ) + ) + ) + ) + + (func $fib-i32 (export "fib-i32") (type $over-i32) + (if (result i32) (i32.le_u (local.get 0) (i32.const 1)) + (then (i32.const 1)) + (else + (i32.add + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 2)) + (i32.const 26) + ) + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 26) + ) + ) + ) + ) + ) + + (func $fib-f32 (export "fib-f32") (type $over-f32) + (if (result f32) (f32.le (local.get 0) (f32.const 1.0)) + (then (f32.const 1.0)) + (else + (f32.add + (call_indirect (type $over-f32) + (f32.sub (local.get 0) (f32.const 2.0)) + (i32.const 27) + ) + (call_indirect (type $over-f32) + (f32.sub (local.get 0) (f32.const 1.0)) + (i32.const 27) + ) + ) + ) + ) + ) + + (func $fib-f64 (export "fib-f64") (type $over-f64) + (if (result f64) (f64.le (local.get 0) (f64.const 1.0)) + (then (f64.const 1.0)) + (else + (f64.add + (call_indirect (type $over-f64) + (f64.sub (local.get 0) (f64.const 2.0)) + (i32.const 28) + ) + (call_indirect (type $over-f64) + (f64.sub (local.get 0) (f64.const 1.0)) + (i32.const 28) + ) + ) + ) + ) + ) + + (func $even (export "even") (param i32) (result i32) + (if (result i32) (i32.eqz (local.get 0)) + (then (i32.const 44)) + (else + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 15) + ) + ) + ) + ) + (func $odd (export "odd") (param i32) (result i32) + (if (result i32) (i32.eqz (local.get 0)) + (then (i32.const 99)) + (else + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 14) + ) + ) + ) + ) + + ;; Stack exhaustion + + ;; Implementations are required to have every call consume some abstract + ;; resource towards exhausting some abstract finite limit, such that + ;; infinitely recursive test cases reliably trap in finite time. This is + ;; because otherwise applications could come to depend on it on those + ;; implementations and be incompatible with implementations that don't do + ;; it (or don't do it under the same circumstances). + + (func $runaway (export "runaway") (call_indirect (type $proc) (i32.const 16))) + + (func $mutual-runaway1 (export "mutual-runaway") (call_indirect (type $proc) (i32.const 18))) + (func $mutual-runaway2 (call_indirect (type $proc) (i32.const 17))) + + ;; As parameter of control constructs and instructions + + (memory 1) + + (func (export "as-select-first") (result i32) + (select (call_indirect (type $out-i32) (i32.const 0)) (i32.const 2) (i32.const 3)) + ) + (func (export "as-select-mid") (result i32) + (select (i32.const 2) (call_indirect (type $out-i32) (i32.const 0)) (i32.const 3)) + ) + (func (export "as-select-last") (result i32) + (select (i32.const 2) (i32.const 3) (call_indirect (type $out-i32) (i32.const 0))) + ) + + (func (export "as-if-condition") (result i32) + (if (result i32) (call_indirect (type $out-i32) (i32.const 0)) (then (i32.const 1)) (else (i32.const 2))) + ) + + (func (export "as-br_if-first") (result i64) + (block (result i64) (br_if 0 (call_indirect (type $out-i64) (i32.const 1)) (i32.const 2))) + ) + (func (export "as-br_if-last") (result i32) + (block (result i32) (br_if 0 (i32.const 2) (call_indirect (type $out-i32) (i32.const 0)))) + ) + + (func (export "as-br_table-first") (result f32) + (block (result f32) (call_indirect (type $out-f32) (i32.const 2)) (i32.const 2) (br_table 0 0)) + ) + (func (export "as-br_table-last") (result i32) + (block (result i32) (i32.const 2) (call_indirect (type $out-i32) (i32.const 0)) (br_table 0 0)) + ) + + (func (export "as-store-first") + (call_indirect (type $out-i32) (i32.const 0)) (i32.const 1) (i32.store) + ) + (func (export "as-store-last") + (i32.const 10) (call_indirect (type $out-f64) (i32.const 3)) (f64.store) + ) + + (func (export "as-memory.grow-value") (result i32) + (memory.grow (call_indirect (type $out-i32) (i32.const 0))) + ) + (func (export "as-return-value") (result i32) + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) (return) + ) + (func (export "as-drop-operand") + (call_indirect (type $over-i64) (i64.const 1) (i32.const 5)) (drop) + ) + (func (export "as-br-value") (result f32) + (block (result f32) (br 0 (call_indirect (type $over-f32) (f32.const 1) (i32.const 6)))) + ) + (func (export "as-local.set-value") (result f64) + (local f64) (local.set 0 (call_indirect (type $over-f64) (f64.const 1) (i32.const 7))) (local.get 0) + ) + (func (export "as-local.tee-value") (result f64) + (local f64) (local.tee 0 (call_indirect (type $over-f64) (f64.const 1) (i32.const 7))) + ) + (global $a (mut f64) (f64.const 10.0)) + (func (export "as-global.set-value") (result f64) + (global.set $a (call_indirect (type $over-f64) (f64.const 1.0) (i32.const 7))) + (global.get $a) + ) + + (func (export "as-load-operand") (result i32) + (i32.load (call_indirect (type $out-i32) (i32.const 0))) + ) + + (func (export "as-unary-operand") (result f32) + (block (result f32) + (f32.sqrt + (call_indirect (type $over-f32) (f32.const 0x0p+0) (i32.const 6)) + ) + ) + ) + + (func (export "as-binary-left") (result i32) + (block (result i32) + (i32.add + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + (i32.const 10) + ) + ) + ) + (func (export "as-binary-right") (result i32) + (block (result i32) + (i32.sub + (i32.const 10) + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + ) + ) + ) + + (func (export "as-test-operand") (result i32) + (block (result i32) + (i32.eqz + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + ) + ) + ) + + (func (export "as-compare-left") (result i32) + (block (result i32) + (i32.le_u + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + (i32.const 10) + ) + ) + ) + (func (export "as-compare-right") (result i32) + (block (result i32) + (i32.ne + (i32.const 10) + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + ) + ) + ) + + (func (export "as-convert-operand") (result i64) + (block (result i64) + (i64.extend_i32_s + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + ) + ) + ) + +) + +(assert_return (invoke "type-i32") (i32.const 0x132)) +(assert_return (invoke "type-i64") (i64.const 0x164)) +(assert_return (invoke "type-f32") (f32.const 0xf32)) +(assert_return (invoke "type-f64") (f64.const 0xf64)) +(assert_return (invoke "type-f64-i32") (f64.const 0xf64) (i32.const 32)) + +(assert_return (invoke "type-index") (i64.const 100)) + +(assert_return (invoke "type-first-i32") (i32.const 32)) +(assert_return (invoke "type-first-i64") (i64.const 64)) +(assert_return (invoke "type-first-f32") (f32.const 1.32)) +(assert_return (invoke "type-first-f64") (f64.const 1.64)) + +(assert_return (invoke "type-second-i32") (i32.const 32)) +(assert_return (invoke "type-second-i64") (i64.const 64)) +(assert_return (invoke "type-second-f32") (f32.const 32)) +(assert_return (invoke "type-second-f64") (f64.const 64.1)) + +(assert_return (invoke "type-all-f64-i32") (f64.const 0xf64) (i32.const 32)) +(assert_return (invoke "type-all-i32-f64") (i32.const 1) (f64.const 2)) +(assert_return (invoke "type-all-i32-i64") (i64.const 2) (i32.const 1)) + +(assert_return (invoke "dispatch" (i32.const 5) (i64.const 2)) (i64.const 2)) +(assert_return (invoke "dispatch" (i32.const 5) (i64.const 5)) (i64.const 5)) +(assert_return (invoke "dispatch" (i32.const 12) (i64.const 5)) (i64.const 120)) +(assert_return (invoke "dispatch" (i32.const 13) (i64.const 5)) (i64.const 8)) +(assert_return (invoke "dispatch" (i32.const 20) (i64.const 2)) (i64.const 2)) +(assert_trap (invoke "dispatch" (i32.const 0) (i64.const 2)) "indirect call type mismatch") +(assert_trap (invoke "dispatch" (i32.const 15) (i64.const 2)) "indirect call type mismatch") +(assert_trap (invoke "dispatch" (i32.const 32) (i64.const 2)) "undefined element") +(assert_trap (invoke "dispatch" (i32.const -1) (i64.const 2)) "undefined element") +(assert_trap (invoke "dispatch" (i32.const 1213432423) (i64.const 2)) "undefined element") + +(assert_return (invoke "dispatch-structural-i64" (i32.const 5)) (i64.const 9)) +(assert_return (invoke "dispatch-structural-i64" (i32.const 12)) (i64.const 362880)) +(assert_return (invoke "dispatch-structural-i64" (i32.const 13)) (i64.const 55)) +(assert_return (invoke "dispatch-structural-i64" (i32.const 20)) (i64.const 9)) +(assert_trap (invoke "dispatch-structural-i64" (i32.const 11)) "indirect call type mismatch") +(assert_trap (invoke "dispatch-structural-i64" (i32.const 22)) "indirect call type mismatch") + +(assert_return (invoke "dispatch-structural-i32" (i32.const 4)) (i32.const 9)) +(assert_return (invoke "dispatch-structural-i32" (i32.const 23)) (i32.const 362880)) +(assert_return (invoke "dispatch-structural-i32" (i32.const 26)) (i32.const 55)) +(assert_return (invoke "dispatch-structural-i32" (i32.const 19)) (i32.const 9)) +(assert_trap (invoke "dispatch-structural-i32" (i32.const 9)) "indirect call type mismatch") +(assert_trap (invoke "dispatch-structural-i32" (i32.const 21)) "indirect call type mismatch") + +(assert_return (invoke "dispatch-structural-f32" (i32.const 6)) (f32.const 9.0)) +(assert_return (invoke "dispatch-structural-f32" (i32.const 24)) (f32.const 362880.0)) +(assert_return (invoke "dispatch-structural-f32" (i32.const 27)) (f32.const 55.0)) +(assert_return (invoke "dispatch-structural-f32" (i32.const 21)) (f32.const 9.0)) +(assert_trap (invoke "dispatch-structural-f32" (i32.const 8)) "indirect call type mismatch") +(assert_trap (invoke "dispatch-structural-f32" (i32.const 19)) "indirect call type mismatch") + +(assert_return (invoke "dispatch-structural-f64" (i32.const 7)) (f64.const 9.0)) +(assert_return (invoke "dispatch-structural-f64" (i32.const 25)) (f64.const 362880.0)) +(assert_return (invoke "dispatch-structural-f64" (i32.const 28)) (f64.const 55.0)) +(assert_return (invoke "dispatch-structural-f64" (i32.const 22)) (f64.const 9.0)) +(assert_trap (invoke "dispatch-structural-f64" (i32.const 10)) "indirect call type mismatch") +(assert_trap (invoke "dispatch-structural-f64" (i32.const 18)) "indirect call type mismatch") + +(assert_return (invoke "fac-i64" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "fac-i64" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-i64" (i64.const 5)) (i64.const 120)) +(assert_return (invoke "fac-i64" (i64.const 25)) (i64.const 7034535277573963776)) + +(assert_return (invoke "fac-i32" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "fac-i32" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "fac-i32" (i32.const 5)) (i32.const 120)) +(assert_return (invoke "fac-i32" (i32.const 10)) (i32.const 3628800)) + +(assert_return (invoke "fac-f32" (f32.const 0.0)) (f32.const 1.0)) +(assert_return (invoke "fac-f32" (f32.const 1.0)) (f32.const 1.0)) +(assert_return (invoke "fac-f32" (f32.const 5.0)) (f32.const 120.0)) +(assert_return (invoke "fac-f32" (f32.const 10.0)) (f32.const 3628800.0)) + +(assert_return (invoke "fac-f64" (f64.const 0.0)) (f64.const 1.0)) +(assert_return (invoke "fac-f64" (f64.const 1.0)) (f64.const 1.0)) +(assert_return (invoke "fac-f64" (f64.const 5.0)) (f64.const 120.0)) +(assert_return (invoke "fac-f64" (f64.const 10.0)) (f64.const 3628800.0)) + +(assert_return (invoke "fib-i64" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "fib-i64" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fib-i64" (i64.const 2)) (i64.const 2)) +(assert_return (invoke "fib-i64" (i64.const 5)) (i64.const 8)) +(assert_return (invoke "fib-i64" (i64.const 20)) (i64.const 10946)) + +(assert_return (invoke "fib-i32" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "fib-i32" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "fib-i32" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "fib-i32" (i32.const 5)) (i32.const 8)) +(assert_return (invoke "fib-i32" (i32.const 20)) (i32.const 10946)) + +(assert_return (invoke "fib-f32" (f32.const 0.0)) (f32.const 1.0)) +(assert_return (invoke "fib-f32" (f32.const 1.0)) (f32.const 1.0)) +(assert_return (invoke "fib-f32" (f32.const 2.0)) (f32.const 2.0)) +(assert_return (invoke "fib-f32" (f32.const 5.0)) (f32.const 8.0)) +(assert_return (invoke "fib-f32" (f32.const 20.0)) (f32.const 10946.0)) + +(assert_return (invoke "fib-f64" (f64.const 0.0)) (f64.const 1.0)) +(assert_return (invoke "fib-f64" (f64.const 1.0)) (f64.const 1.0)) +(assert_return (invoke "fib-f64" (f64.const 2.0)) (f64.const 2.0)) +(assert_return (invoke "fib-f64" (f64.const 5.0)) (f64.const 8.0)) +(assert_return (invoke "fib-f64" (f64.const 20.0)) (f64.const 10946.0)) + +(assert_return (invoke "even" (i32.const 0)) (i32.const 44)) +(assert_return (invoke "even" (i32.const 1)) (i32.const 99)) +(assert_return (invoke "even" (i32.const 100)) (i32.const 44)) +(assert_return (invoke "even" (i32.const 77)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 0)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 1)) (i32.const 44)) +(assert_return (invoke "odd" (i32.const 200)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 77)) (i32.const 44)) + +(assert_exhaustion (invoke "runaway") "call stack exhausted") +(assert_exhaustion (invoke "mutual-runaway") "call stack exhausted") + +(assert_return (invoke "as-select-first") (i32.const 0x132)) +(assert_return (invoke "as-select-mid") (i32.const 2)) +(assert_return (invoke "as-select-last") (i32.const 2)) + +(assert_return (invoke "as-if-condition") (i32.const 1)) + +(assert_return (invoke "as-br_if-first") (i64.const 0x164)) +(assert_return (invoke "as-br_if-last") (i32.const 2)) + +(assert_return (invoke "as-br_table-first") (f32.const 0xf32)) +(assert_return (invoke "as-br_table-last") (i32.const 2)) + +(assert_return (invoke "as-store-first")) +(assert_return (invoke "as-store-last")) + +(assert_return (invoke "as-memory.grow-value") (i32.const 1)) +(assert_return (invoke "as-return-value") (i32.const 1)) +(assert_return (invoke "as-drop-operand")) +(assert_return (invoke "as-br-value") (f32.const 1)) +(assert_return (invoke "as-local.set-value") (f64.const 1)) +(assert_return (invoke "as-local.tee-value") (f64.const 1)) +(assert_return (invoke "as-global.set-value") (f64.const 1.0)) +(assert_return (invoke "as-load-operand") (i32.const 1)) + +(assert_return (invoke "as-unary-operand") (f32.const 0x0p+0)) +(assert_return (invoke "as-binary-left") (i32.const 11)) +(assert_return (invoke "as-binary-right") (i32.const 9)) +(assert_return (invoke "as-test-operand") (i32.const 0)) +(assert_return (invoke "as-compare-left") (i32.const 1)) +(assert_return (invoke "as-compare-right") (i32.const 1)) +(assert_return (invoke "as-convert-operand") (i64.const 1)) + + +;; Multiple tables + +(module + (type $ii-i (func (param i32 i32) (result i32))) + + (table $t1 funcref (elem $f $g)) + (table $t2 funcref (elem $h $i $j)) + (table $t3 4 funcref) + (elem (table $t3) (i32.const 0) func $g $h) + (elem (table $t3) (i32.const 3) func $z) + + (func $f (type $ii-i) (i32.add (local.get 0) (local.get 1))) + (func $g (type $ii-i) (i32.sub (local.get 0) (local.get 1))) + (func $h (type $ii-i) (i32.mul (local.get 0) (local.get 1))) + (func $i (type $ii-i) (i32.div_u (local.get 0) (local.get 1))) + (func $j (type $ii-i) (i32.rem_u (local.get 0) (local.get 1))) + (func $z) + + (func (export "call-1") (param i32 i32 i32) (result i32) + (call_indirect $t1 (type $ii-i) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "call-2") (param i32 i32 i32) (result i32) + (call_indirect $t2 (type $ii-i) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "call-3") (param i32 i32 i32) (result i32) + (call_indirect $t3 (type $ii-i) (local.get 0) (local.get 1) (local.get 2)) + ) +) + +(assert_return (invoke "call-1" (i32.const 2) (i32.const 3) (i32.const 0)) (i32.const 5)) +(assert_return (invoke "call-1" (i32.const 2) (i32.const 3) (i32.const 1)) (i32.const -1)) +(assert_trap (invoke "call-1" (i32.const 2) (i32.const 3) (i32.const 2)) "undefined element") + +(assert_return (invoke "call-2" (i32.const 2) (i32.const 3) (i32.const 0)) (i32.const 6)) +(assert_return (invoke "call-2" (i32.const 2) (i32.const 3) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "call-2" (i32.const 2) (i32.const 3) (i32.const 2)) (i32.const 2)) +(assert_trap (invoke "call-2" (i32.const 2) (i32.const 3) (i32.const 3)) "undefined element") + +(assert_return (invoke "call-3" (i32.const 2) (i32.const 3) (i32.const 0)) (i32.const -1)) +(assert_return (invoke "call-3" (i32.const 2) (i32.const 3) (i32.const 1)) (i32.const 6)) +(assert_trap (invoke "call-3" (i32.const 2) (i32.const 3) (i32.const 2)) "uninitialized element") +(assert_trap (invoke "call-3" (i32.const 2) (i32.const 3) (i32.const 3)) "indirect call type mismatch") +(assert_trap (invoke "call-3" (i32.const 2) (i32.const 3) (i32.const 4)) "undefined element") + + +;; Invalid syntax + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (type $sig) (result i32) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (param i32) (type $sig) (result i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (param i32) (result i32) (type $sig)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (result i32) (type $sig) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (result i32) (param i32) (type $sig)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (result i32) (param i32) (i32.const 0) (i32.const 0))" + ")" + ) + "unexpected token" +) + +(assert_malformed + (module quote + "(table 0 funcref)" + "(func (call_indirect (param $x i32) (i32.const 0) (i32.const 0)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (type $sig) (result i32) (i32.const 0))" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (type $sig) (result i32) (i32.const 0))" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func" + " (call_indirect (type $sig) (param i32) (i32.const 0) (i32.const 0))" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (type $sig) (param i32) (result i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "inline function type" +) + +;; Invalid typing + +(assert_invalid + (module + (type (func)) + (func $no-table (call_indirect (type 0) (i32.const 0))) + ) + "unknown table" +) + +(assert_invalid + (module + (type (func)) + (table 0 funcref) + (func $type-void-vs-num (i32.eqz (call_indirect (type 0) (i32.const 0)))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (result i64))) + (table 0 funcref) + (func $type-num-vs-num (i32.eqz (call_indirect (type 0) (i32.const 0)))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type (func (param i32))) + (table 0 funcref) + (func $arity-0-vs-1 (call_indirect (type 0) (i32.const 0))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param f64 i32))) + (table 0 funcref) + (func $arity-0-vs-2 (call_indirect (type 0) (i32.const 0))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func)) + (table 0 funcref) + (func $arity-1-vs-0 (call_indirect (type 0) (i32.const 1) (i32.const 0))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func)) + (table 0 funcref) + (func $arity-2-vs-0 + (call_indirect (type 0) (f64.const 2) (i32.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (type (func (param i32))) + (table 0 funcref) + (func $type-func-void-vs-i32 (call_indirect (type 0) (i32.const 1) (nop))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param i32))) + (table 0 funcref) + (func $type-func-num-vs-i32 (call_indirect (type 0) (i32.const 0) (i64.const 1))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type (func (param i32 i32))) + (table 0 funcref) + (func $type-first-void-vs-num + (call_indirect (type 0) (nop) (i32.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param i32 i32))) + (table 0 funcref) + (func $type-second-void-vs-num + (call_indirect (type 0) (i32.const 1) (nop) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param i32 f64))) + (table 0 funcref) + (func $type-first-num-vs-num + (call_indirect (type 0) (f64.const 1) (i32.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param f64 i32))) + (table 0 funcref) + (func $type-second-num-vs-num + (call_indirect (type 0) (i32.const 1) (f64.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $f (param i32)) + (type $sig (func (param i32))) + (table funcref (elem $f)) + (func $type-first-empty-in-block + (block + (call_indirect (type $sig) (i32.const 0)) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32 i32)) + (type $sig (func (param i32 i32))) + (table funcref (elem $f)) + (func $type-second-empty-in-block + (block + (call_indirect (type $sig) (i32.const 0) (i32.const 0)) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32)) + (type $sig (func (param i32))) + (table funcref (elem $f)) + (func $type-first-empty-in-loop + (loop + (call_indirect (type $sig) (i32.const 0)) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32 i32)) + (type $sig (func (param i32 i32))) + (table funcref (elem $f)) + (func $type-second-empty-in-loop + (loop + (call_indirect (type $sig) (i32.const 0) (i32.const 0)) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32)) + (type $sig (func (param i32))) + (table funcref (elem $f)) + (func $type-first-empty-in-then + (i32.const 0) (i32.const 0) + (if + (then + (call_indirect (type $sig) (i32.const 0)) + ) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32 i32)) + (type $sig (func (param i32 i32))) + (table funcref (elem $f)) + (func $type-second-empty-in-then + (i32.const 0) (i32.const 0) + (if + (then + (call_indirect (type $sig) (i32.const 0) (i32.const 0)) + ) + ) + ) + ) + "type mismatch" +) + + +;; Unbound type + +(assert_invalid + (module + (table 0 funcref) + (func $unbound-type (call_indirect (type 1) (i32.const 0))) + ) + "unknown type" +) +(assert_invalid + (module + (table 0 funcref) + (func $large-type (call_indirect (type 1012321300) (i32.const 0))) + ) + "unknown type" +) + + +;; Unbound function in table + +(assert_invalid + (module (table funcref (elem 0 0))) + "unknown function" +) diff --git a/runtime/unc-vm/tests/wast/spec/comments.wast b/runtime/unc-vm/tests/wast/spec/comments.wast new file mode 100644 index 000000000..90a64b42d Binary files /dev/null and b/runtime/unc-vm/tests/wast/spec/comments.wast differ diff --git a/runtime/unc-vm/tests/wast/spec/const.wast b/runtime/unc-vm/tests/wast/spec/const.wast new file mode 100644 index 000000000..565650298 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/const.wast @@ -0,0 +1,1061 @@ +;; Test t.const instructions + +;; Syntax error + +(module (func (i32.const 0_123_456_789) drop)) +(module (func (i32.const 0x0_9acf_fBDF) drop)) +(assert_malformed + (module quote "(func (i32.const) drop)") + "unexpected token" +) +(assert_malformed + (module quote "(func (i32.const 0x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (i32.const 1x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (i32.const 0xg) drop)") + "unknown operator" +) + +(module (func (i64.const 0_123_456_789) drop)) +(module (func (i64.const 0x0125_6789_ADEF_bcef) drop)) +(assert_malformed + (module quote "(func (i64.const) drop)") + "unexpected token" +) +(assert_malformed + (module quote "(func (i64.const 0x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (i64.const 1x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (i64.const 0xg) drop)") + "unknown operator" +) + +(module (func (f32.const 0123456789) drop)) +(module (func (f32.const 0123456789e019) drop)) +(module (func (f32.const 0123456789e+019) drop)) +(module (func (f32.const 0123456789e-019) drop)) +(module (func (f32.const 0123456789.) drop)) +(module (func (f32.const 0123456789.e019) drop)) +(module (func (f32.const 0123456789.e+019) drop)) +(module (func (f32.const 0123456789.e-019) drop)) +(module (func (f32.const 0123456789.0123456789) drop)) +(module (func (f32.const 0123456789.0123456789e019) drop)) +(module (func (f32.const 0123456789.0123456789e+019) drop)) +(module (func (f32.const 0123456789.0123456789e-019) drop)) +(module (func (f32.const 0x0123456789ABCDEF) drop)) +(module (func (f32.const 0x0123456789ABCDEFp019) drop)) +(module (func (f32.const 0x0123456789ABCDEFp+019) drop)) +(module (func (f32.const 0x0123456789ABCDEFp-019) drop)) +(module (func (f32.const 0x0123456789ABCDEF.) drop)) +(module (func (f32.const 0x0123456789ABCDEF.p019) drop)) +(module (func (f32.const 0x0123456789ABCDEF.p+019) drop)) +(module (func (f32.const 0x0123456789ABCDEF.p-019) drop)) +(module (func (f32.const 0x0123456789ABCDEF.019aF) drop)) +(module (func (f32.const 0x0123456789ABCDEF.019aFp019) drop)) +(module (func (f32.const 0x0123456789ABCDEF.019aFp+019) drop)) +(module (func (f32.const 0x0123456789ABCDEF.019aFp-019) drop)) +(assert_malformed + (module quote "(func (f32.const) drop)") + "unexpected token" +) +(assert_malformed + (module quote "(func (f32.const .0) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const .0e0) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 0e) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 0e+) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 0.0e) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 0.0e-) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 0x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 1x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 0xg) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 0x.) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 0x0.g) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 0x0p) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 0x0p+) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 0x0p-) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 0x0.0p) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 0x0.0p+) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 0x0.0p-) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f32.const 0x0pA) drop)") + "unknown operator" +) + + +(module (func (f64.const 0123456789) drop)) +(module (func (f64.const 0123456789e019) drop)) +(module (func (f64.const 0123456789e+019) drop)) +(module (func (f64.const 0123456789e-019) drop)) +(module (func (f64.const 0123456789.) drop)) +(module (func (f64.const 0123456789.e019) drop)) +(module (func (f64.const 0123456789.e+019) drop)) +(module (func (f64.const 0123456789.e-019) drop)) +(module (func (f64.const 0123456789.0123456789) drop)) +(module (func (f64.const 0123456789.0123456789e019) drop)) +(module (func (f64.const 0123456789.0123456789e+019) drop)) +(module (func (f64.const 0123456789.0123456789e-019) drop)) +(module (func (f64.const 0_1_2_3_4_5_6_7_8_9) drop)) +(module (func (f64.const 0_1_2_3_4_5_6_7_8_9.) drop)) +(module (func (f64.const 0_1_2_3_4_5_6_7_8_9.0_1_2_3_4_5_6_7_8_9) drop)) +(module (func (f64.const 0_1_2_3_4_5_6_7_8_9e+0_1_9) drop)) +(module (func (f64.const 0_1_2_3_4_5_6_7_8_9.e+0_1_9) drop)) +(module (func (f64.const 0_1_2_3_4_5_6_7_8_9.0_1_2_3_4_5_6_7_8_9e0_1_9) drop)) + +(module (func (f64.const 0x0123456789ABCDEFabcdef) drop)) +(module (func (f64.const 0x0123456789ABCDEFabcdefp019) drop)) +(module (func (f64.const 0x0123456789ABCDEFabcdefp+019) drop)) +(module (func (f64.const 0x0123456789ABCDEFabcdefp-019) drop)) +(module (func (f64.const 0x0123456789ABCDEFabcdef.) drop)) +(module (func (f64.const 0x0123456789ABCDEFabcdef.p019) drop)) +(module (func (f64.const 0x0123456789ABCDEFabcdef.p+019) drop)) +(module (func (f64.const 0x0123456789ABCDEFabcdef.p-019) drop)) +(module (func (f64.const 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) drop)) +(module (func (f64.const 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) drop)) +(module (func (f64.const 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) drop)) +(module (func (f64.const 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) drop)) +(module (func (f64.const 0x0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F_a_b_c_d_e_f) drop)) +(module (func (f64.const 0x0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F_a_b_c_d_e_f.) drop)) +(module (func (f64.const 0x0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F_a_b_c_d_e_f.0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F_a_b_c_d_e_f) drop)) +(module (func (f64.const 0x0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F_a_b_c_d_e_fp0_1_9) drop)) +(module (func (f64.const 0x0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F_a_b_c_d_e_f.p0_1_9) drop)) +(module (func (f64.const 0x0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F_a_b_c_d_e_f.0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F_a_b_c_d_e_fp0_1_9) drop)) + + +(assert_malformed + (module quote "(func (f64.const) drop)") + "unexpected token" +) +(assert_malformed + (module quote "(func (f64.const .0) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const .0e0) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 0e) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 0e+) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 0.0e) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 0.0e-) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 0x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 1x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 0xg) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 0x.) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 0x0.g) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 0x0p) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 0x0p+) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 0x0p-) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 0x0.0p) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 0x0.0p+) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 0x0.0p-) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const 0x0pA) drop)") + "unknown operator" +) + + +;; Range error + +(module (func (i32.const 0xffffffff) drop)) +(module (func (i32.const -0x80000000) drop)) +(assert_malformed + (module quote "(func (i32.const 0x100000000) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (i32.const -0x80000001) drop)") + "constant out of range" +) + +(module (func (i32.const 4294967295) drop)) +(module (func (i32.const -2147483648) drop)) +(assert_malformed + (module quote "(func (i32.const 4294967296) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (i32.const -2147483649) drop)") + "constant out of range" +) + +(module (func (i64.const 0xffffffffffffffff) drop)) +(module (func (i64.const -0x8000000000000000) drop)) +(assert_malformed + (module quote "(func (i64.const 0x10000000000000000) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (i64.const -0x8000000000000001) drop)") + "constant out of range" +) + +(module (func (i64.const 18446744073709551615) drop)) +(module (func (i64.const -9223372036854775808) drop)) +(assert_malformed + (module quote "(func (i64.const 18446744073709551616) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (i64.const -9223372036854775809) drop)") + "constant out of range" +) + +(module (func (f32.const 0x1p127) drop)) +(module (func (f32.const -0x1p127) drop)) +(module (func (f32.const 0x1.fffffep127) drop)) +(module (func (f32.const -0x1.fffffep127) drop)) +(module (func (f32.const 0x1.fffffe7p127) drop)) +(module (func (f32.const -0x1.fffffe7p127) drop)) +(module (func (f32.const 0x1.fffffefffffff8000000p127) drop)) +(module (func (f32.const -0x1.fffffefffffff8000000p127) drop)) +(module (func (f32.const 0x1.fffffefffffffffffffp127) drop)) +(module (func (f32.const -0x1.fffffefffffffffffffp127) drop)) +(assert_malformed + (module quote "(func (f32.const 0x1p128) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (f32.const -0x1p128) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (f32.const 0x1.ffffffp127) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (f32.const -0x1.ffffffp127) drop)") + "constant out of range" +) + +(module (func (f32.const 1e38) drop)) +(module (func (f32.const -1e38) drop)) +(assert_malformed + (module quote "(func (f32.const 1e39) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (f32.const -1e39) drop)") + "constant out of range" +) + +(module (func (f32.const 340282356779733623858607532500980858880) drop)) +(module (func (f32.const -340282356779733623858607532500980858880) drop)) +(assert_malformed + (module quote "(func (f32.const 340282356779733661637539395458142568448) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (f32.const -340282356779733661637539395458142568448) drop)") + "constant out of range" +) + +(module (func (f64.const 0x1p1023) drop)) +(module (func (f64.const -0x1p1023) drop)) +(module (func (f64.const 0x1.fffffffffffffp1023) drop)) +(module (func (f64.const -0x1.fffffffffffffp1023) drop)) +(module (func (f64.const 0x1.fffffffffffff7p1023) drop)) +(module (func (f64.const -0x1.fffffffffffff7p1023) drop)) +(module (func (f64.const 0x1.fffffffffffff7ffffffp1023) drop)) +(module (func (f64.const -0x1.fffffffffffff7ffffffp1023) drop)) +(assert_malformed + (module quote "(func (f64.const 0x1p1024) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (f64.const -0x1p1024) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (f64.const 0x1.fffffffffffff8p1023) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (f64.const -0x1.fffffffffffff8p1023) drop)") + "constant out of range" +) + +(module (func (f64.const 1e308) drop)) +(module (func (f64.const -1e308) drop)) +(assert_malformed + (module quote "(func (f64.const 1e309) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (f64.const -1e309) drop)") + "constant out of range" +) + +(module (func (f64.const 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368) drop)) +(module (func (f64.const -179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368) drop)) +(assert_malformed + (module quote "(func (f64.const 269653970229347356221791135597556535197105851288767494898376215204735891170042808140884337949150317257310688430271573696351481990334196274152701320055306275479074865864826923114368235135583993416113802762682700913456874855354834422248712838998185022412196739306217084753107265771378949821875606039276187287552) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (f64.const -269653970229347356221791135597556535197105851288767494898376215204735891170042808140884337949150317257310688430271573696351481990334196274152701320055306275479074865864826923114368235135583993416113802762682700913456874855354834422248712838998185022412196739306217084753107265771378949821875606039276187287552) drop)") + "constant out of range" +) + +(module (func (f32.const nan:0x1) drop)) +(module (func (f64.const nan:0x1) drop)) +(module (func (f32.const nan:0x7f_ffff) drop)) +(module (func (f64.const nan:0xf_ffff_ffff_ffff) drop)) + +(assert_malformed + (module quote "(func (f32.const nan:1) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (f64.const nan:1) drop)") + "unknown operator" +) + +(assert_malformed + (module quote "(func (f32.const nan:0x0) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (f64.const nan:0x0) drop)") + "constant out of range" +) + +(assert_malformed + (module quote "(func (f32.const nan:0x80_0000) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (f64.const nan:0x10_0000_0000_0000) drop)") + "constant out of range" +) + + +;; Rounding behaviour + +;; f32, small exponent +(module (func (export "f") (result f32) (f32.const +0x1.00000100000000000p-50))) +(assert_return (invoke "f") (f32.const +0x1.000000p-50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000100000000000p-50))) +(assert_return (invoke "f") (f32.const -0x1.000000p-50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000100000000001p-50))) +(assert_return (invoke "f") (f32.const +0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000100000000001p-50))) +(assert_return (invoke "f") (f32.const -0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const +0x1.000001fffffffffffp-50))) +(assert_return (invoke "f") (f32.const +0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const -0x1.000001fffffffffffp-50))) +(assert_return (invoke "f") (f32.const -0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000200000000000p-50))) +(assert_return (invoke "f") (f32.const +0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000200000000000p-50))) +(assert_return (invoke "f") (f32.const -0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000200000000001p-50))) +(assert_return (invoke "f") (f32.const +0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000200000000001p-50))) +(assert_return (invoke "f") (f32.const -0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const +0x1.000002fffffffffffp-50))) +(assert_return (invoke "f") (f32.const +0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const -0x1.000002fffffffffffp-50))) +(assert_return (invoke "f") (f32.const -0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000300000000000p-50))) +(assert_return (invoke "f") (f32.const +0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000300000000000p-50))) +(assert_return (invoke "f") (f32.const -0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000300000000001p-50))) +(assert_return (invoke "f") (f32.const +0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000300000000001p-50))) +(assert_return (invoke "f") (f32.const -0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const +0x1.000003fffffffffffp-50))) +(assert_return (invoke "f") (f32.const +0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const -0x1.000003fffffffffffp-50))) +(assert_return (invoke "f") (f32.const -0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000400000000000p-50))) +(assert_return (invoke "f") (f32.const +0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000400000000000p-50))) +(assert_return (invoke "f") (f32.const -0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000400000000001p-50))) +(assert_return (invoke "f") (f32.const +0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000400000000001p-50))) +(assert_return (invoke "f") (f32.const -0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const +0x1.000004fffffffffffp-50))) +(assert_return (invoke "f") (f32.const +0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const -0x1.000004fffffffffffp-50))) +(assert_return (invoke "f") (f32.const -0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000500000000000p-50))) +(assert_return (invoke "f") (f32.const +0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000500000000000p-50))) +(assert_return (invoke "f") (f32.const -0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000500000000001p-50))) +(assert_return (invoke "f") (f32.const +0x1.000006p-50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000500000000001p-50))) +(assert_return (invoke "f") (f32.const -0x1.000006p-50)) + +(module (func (export "f") (result f32) (f32.const +0x4000.004000000p-64))) +(assert_return (invoke "f") (f32.const +0x1.000000p-50)) +(module (func (export "f") (result f32) (f32.const -0x4000.004000000p-64))) +(assert_return (invoke "f") (f32.const -0x1.000000p-50)) +(module (func (export "f") (result f32) (f32.const +0x4000.004000001p-64))) +(assert_return (invoke "f") (f32.const +0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const -0x4000.004000001p-64))) +(assert_return (invoke "f") (f32.const -0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const +0x4000.007ffffffp-64))) +(assert_return (invoke "f") (f32.const +0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const -0x4000.007ffffffp-64))) +(assert_return (invoke "f") (f32.const -0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const +0x4000.008000000p-64))) +(assert_return (invoke "f") (f32.const +0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const -0x4000.008000000p-64))) +(assert_return (invoke "f") (f32.const -0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const +0x4000.008000001p-64))) +(assert_return (invoke "f") (f32.const +0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const -0x4000.008000001p-64))) +(assert_return (invoke "f") (f32.const -0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const +0x4000.00bffffffp-64))) +(assert_return (invoke "f") (f32.const +0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const -0x4000.00bffffffp-64))) +(assert_return (invoke "f") (f32.const -0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const +0x4000.00c000000p-64))) +(assert_return (invoke "f") (f32.const +0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const -0x4000.00c000000p-64))) +(assert_return (invoke "f") (f32.const -0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const +0x4000.00c000001p-64))) +(assert_return (invoke "f") (f32.const +0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const -0x4000.00c000001p-64))) +(assert_return (invoke "f") (f32.const -0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const +0x4000.00fffffffp-64))) +(assert_return (invoke "f") (f32.const +0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const -0x4000.00fffffffp-64))) +(assert_return (invoke "f") (f32.const -0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const +0x4000.010000001p-64))) +(assert_return (invoke "f") (f32.const +0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const -0x4000.010000001p-64))) +(assert_return (invoke "f") (f32.const -0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const +0x4000.013ffffffp-64))) +(assert_return (invoke "f") (f32.const +0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const -0x4000.013ffffffp-64))) +(assert_return (invoke "f") (f32.const -0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const +0x4000.014000001p-64))) +(assert_return (invoke "f") (f32.const +0x1.000006p-50)) +(module (func (export "f") (result f32) (f32.const -0x4000.014000001p-64))) +(assert_return (invoke "f") (f32.const -0x1.000006p-50)) + +(module (func (export "f") (result f32) (f32.const +8.8817847263968443573e-16))) +(assert_return (invoke "f") (f32.const +0x1.000000p-50)) +(module (func (export "f") (result f32) (f32.const -8.8817847263968443573e-16))) +(assert_return (invoke "f") (f32.const -0x1.000000p-50)) +(module (func (export "f") (result f32) (f32.const +8.8817847263968443574e-16))) +(assert_return (invoke "f") (f32.const +0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const -8.8817847263968443574e-16))) +(assert_return (invoke "f") (f32.const -0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const +8.8817857851880284252e-16))) +(assert_return (invoke "f") (f32.const +0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const -8.8817857851880284252e-16))) +(assert_return (invoke "f") (f32.const -0x1.000002p-50)) +(module (func (export "f") (result f32) (f32.const +8.8817857851880284253e-16))) +(assert_return (invoke "f") (f32.const +0x1.000004p-50)) +(module (func (export "f") (result f32) (f32.const -8.8817857851880284253e-16))) +(assert_return (invoke "f") (f32.const -0x1.000004p-50)) + +;; f32, large exponent +(module (func (export "f") (result f32) (f32.const +0x1.00000100000000000p+50))) +(assert_return (invoke "f") (f32.const +0x1.000000p+50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000100000000000p+50))) +(assert_return (invoke "f") (f32.const -0x1.000000p+50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000100000000001p+50))) +(assert_return (invoke "f") (f32.const +0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000100000000001p+50))) +(assert_return (invoke "f") (f32.const -0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const +0x1.000001fffffffffffp+50))) +(assert_return (invoke "f") (f32.const +0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const -0x1.000001fffffffffffp+50))) +(assert_return (invoke "f") (f32.const -0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000200000000000p+50))) +(assert_return (invoke "f") (f32.const +0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000200000000000p+50))) +(assert_return (invoke "f") (f32.const -0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000200000000001p+50))) +(assert_return (invoke "f") (f32.const +0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000200000000001p+50))) +(assert_return (invoke "f") (f32.const -0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const +0x1.000002fffffffffffp+50))) +(assert_return (invoke "f") (f32.const +0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const -0x1.000002fffffffffffp+50))) +(assert_return (invoke "f") (f32.const -0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000300000000000p+50))) +(assert_return (invoke "f") (f32.const +0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000300000000000p+50))) +(assert_return (invoke "f") (f32.const -0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000300000000001p+50))) +(assert_return (invoke "f") (f32.const +0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000300000000001p+50))) +(assert_return (invoke "f") (f32.const -0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const +0x1.000003fffffffffffp+50))) +(assert_return (invoke "f") (f32.const +0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const -0x1.000003fffffffffffp+50))) +(assert_return (invoke "f") (f32.const -0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000400000000000p+50))) +(assert_return (invoke "f") (f32.const +0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000400000000000p+50))) +(assert_return (invoke "f") (f32.const -0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000400000000001p+50))) +(assert_return (invoke "f") (f32.const +0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000400000000001p+50))) +(assert_return (invoke "f") (f32.const -0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const +0x1.000004fffffffffffp+50))) +(assert_return (invoke "f") (f32.const +0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const -0x1.000004fffffffffffp+50))) +(assert_return (invoke "f") (f32.const -0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000500000000000p+50))) +(assert_return (invoke "f") (f32.const +0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000500000000000p+50))) +(assert_return (invoke "f") (f32.const -0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const +0x1.00000500000000001p+50))) +(assert_return (invoke "f") (f32.const +0x1.000006p+50)) +(module (func (export "f") (result f32) (f32.const -0x1.00000500000000001p+50))) +(assert_return (invoke "f") (f32.const -0x1.000006p+50)) + +(module (func (export "f") (result f32) (f32.const +0x4000004000000))) +(assert_return (invoke "f") (f32.const +0x1.000000p+50)) +(module (func (export "f") (result f32) (f32.const -0x4000004000000))) +(assert_return (invoke "f") (f32.const -0x1.000000p+50)) +(module (func (export "f") (result f32) (f32.const +0x4000004000001))) +(assert_return (invoke "f") (f32.const +0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const -0x4000004000001))) +(assert_return (invoke "f") (f32.const -0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const +0x4000007ffffff))) +(assert_return (invoke "f") (f32.const +0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const -0x4000007ffffff))) +(assert_return (invoke "f") (f32.const -0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const +0x4000008000000))) +(assert_return (invoke "f") (f32.const +0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const -0x4000008000000))) +(assert_return (invoke "f") (f32.const -0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const +0x4000008000001))) +(assert_return (invoke "f") (f32.const +0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const -0x4000008000001))) +(assert_return (invoke "f") (f32.const -0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const +0x400000bffffff))) +(assert_return (invoke "f") (f32.const +0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const -0x400000bffffff))) +(assert_return (invoke "f") (f32.const -0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const +0x400000c000000))) +(assert_return (invoke "f") (f32.const +0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const -0x400000c000000))) +(assert_return (invoke "f") (f32.const -0x1.000004p+50)) + +(module (func (export "f") (result f32) (f32.const +1125899973951488))) +(assert_return (invoke "f") (f32.const +0x1.000000p+50)) +(module (func (export "f") (result f32) (f32.const -1125899973951488))) +(assert_return (invoke "f") (f32.const -0x1.000000p+50)) +(module (func (export "f") (result f32) (f32.const +1125899973951489))) +(assert_return (invoke "f") (f32.const +0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const -1125899973951489))) +(assert_return (invoke "f") (f32.const -0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const +1125900108169215))) +(assert_return (invoke "f") (f32.const +0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const -1125900108169215))) +(assert_return (invoke "f") (f32.const -0x1.000002p+50)) +(module (func (export "f") (result f32) (f32.const +1125900108169216))) +(assert_return (invoke "f") (f32.const +0x1.000004p+50)) +(module (func (export "f") (result f32) (f32.const -1125900108169216))) +(assert_return (invoke "f") (f32.const -0x1.000004p+50)) + +;; f32, subnormal +(module (func (export "f") (result f32) (f32.const +0x0.00000100000000000p-126))) +(assert_return (invoke "f") (f32.const +0x0.000000p-126)) +(module (func (export "f") (result f32) (f32.const -0x0.00000100000000000p-126))) +(assert_return (invoke "f") (f32.const -0x0.000000p-126)) +(module (func (export "f") (result f32) (f32.const +0x0.00000100000000001p-126))) +(assert_return (invoke "f") (f32.const +0x0.000002p-126)) +(module (func (export "f") (result f32) (f32.const -0x0.00000100000000001p-126))) +(assert_return (invoke "f") (f32.const -0x0.000002p-126)) +(module (func (export "f") (result f32) (f32.const +0x0.000001fffffffffffp-126))) +(assert_return (invoke "f") (f32.const +0x0.000002p-126)) +(module (func (export "f") (result f32) (f32.const -0x0.000001fffffffffffp-126))) +(assert_return (invoke "f") (f32.const -0x0.000002p-126)) +(module (func (export "f") (result f32) (f32.const +0x0.00000200000000000p-126))) +(assert_return (invoke "f") (f32.const +0x0.000002p-126)) +(module (func (export "f") (result f32) (f32.const -0x0.00000200000000000p-126))) +(assert_return (invoke "f") (f32.const -0x0.000002p-126)) +(module (func (export "f") (result f32) (f32.const +0x0.00000200000000001p-126))) +(assert_return (invoke "f") (f32.const +0x0.000002p-126)) +(module (func (export "f") (result f32) (f32.const -0x0.00000200000000001p-126))) +(assert_return (invoke "f") (f32.const -0x0.000002p-126)) +(module (func (export "f") (result f32) (f32.const +0x0.000002fffffffffffp-126))) +(assert_return (invoke "f") (f32.const +0x0.000002p-126)) +(module (func (export "f") (result f32) (f32.const -0x0.000002fffffffffffp-126))) +(assert_return (invoke "f") (f32.const -0x0.000002p-126)) +(module (func (export "f") (result f32) (f32.const +0x0.00000300000000000p-126))) +(assert_return (invoke "f") (f32.const +0x0.000004p-126)) +(module (func (export "f") (result f32) (f32.const -0x0.00000300000000000p-126))) +(assert_return (invoke "f") (f32.const -0x0.000004p-126)) +(module (func (export "f") (result f32) (f32.const +0x0.00000300000000001p-126))) +(assert_return (invoke "f") (f32.const +0x0.000004p-126)) +(module (func (export "f") (result f32) (f32.const -0x0.00000300000000001p-126))) +(assert_return (invoke "f") (f32.const -0x0.000004p-126)) +(module (func (export "f") (result f32) (f32.const +0x0.000003fffffffffffp-126))) +(assert_return (invoke "f") (f32.const +0x0.000004p-126)) +(module (func (export "f") (result f32) (f32.const -0x0.000003fffffffffffp-126))) +(assert_return (invoke "f") (f32.const -0x0.000004p-126)) +(module (func (export "f") (result f32) (f32.const +0x0.00000400000000000p-126))) +(assert_return (invoke "f") (f32.const +0x0.000004p-126)) +(module (func (export "f") (result f32) (f32.const -0x0.00000400000000000p-126))) +(assert_return (invoke "f") (f32.const -0x0.000004p-126)) +(module (func (export "f") (result f32) (f32.const +0x0.00000400000000001p-126))) +(assert_return (invoke "f") (f32.const +0x0.000004p-126)) +(module (func (export "f") (result f32) (f32.const -0x0.00000400000000001p-126))) +(assert_return (invoke "f") (f32.const -0x0.000004p-126)) +(module (func (export "f") (result f32) (f32.const +0x0.000004fffffffffffp-126))) +(assert_return (invoke "f") (f32.const +0x0.000004p-126)) +(module (func (export "f") (result f32) (f32.const -0x0.000004fffffffffffp-126))) +(assert_return (invoke "f") (f32.const -0x0.000004p-126)) +(module (func (export "f") (result f32) (f32.const +0x0.00000500000000000p-126))) +(assert_return (invoke "f") (f32.const +0x0.000004p-126)) +(module (func (export "f") (result f32) (f32.const -0x0.00000500000000000p-126))) +(assert_return (invoke "f") (f32.const -0x0.000004p-126)) +(module (func (export "f") (result f32) (f32.const +0x0.00000500000000001p-126))) +(assert_return (invoke "f") (f32.const +0x0.000006p-126)) +(module (func (export "f") (result f32) (f32.const -0x0.00000500000000001p-126))) +(assert_return (invoke "f") (f32.const -0x0.000006p-126)) + +;; f32, round down at limit to infinity +(module (func (export "f") (result f32) (f32.const +0x1.fffffe8p127))) +(assert_return (invoke "f") (f32.const +0x1.fffffep127)) +(module (func (export "f") (result f32) (f32.const -0x1.fffffe8p127))) +(assert_return (invoke "f") (f32.const -0x1.fffffep127)) +(module (func (export "f") (result f32) (f32.const +0x1.fffffefffffff8p127))) +(assert_return (invoke "f") (f32.const +0x1.fffffep127)) +(module (func (export "f") (result f32) (f32.const -0x1.fffffefffffff8p127))) +(assert_return (invoke "f") (f32.const -0x1.fffffep127)) +(module (func (export "f") (result f32) (f32.const +0x1.fffffefffffffffffp127))) +(assert_return (invoke "f") (f32.const +0x1.fffffep127)) +(module (func (export "f") (result f32) (f32.const -0x1.fffffefffffffffffp127))) +(assert_return (invoke "f") (f32.const -0x1.fffffep127)) + +;; f64, small exponent +(module (func (export "f") (result f64) (f64.const +0x1.000000000000080000000000p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000000p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000080000000000p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000000p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000080000000001p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000080000000001p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.0000000000000fffffffffffp-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.0000000000000fffffffffffp-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000100000000000p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000100000000000p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000100000000001p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000100000000001p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.00000000000017ffffffffffp-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.00000000000017ffffffffffp-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000180000000000p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000180000000000p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000180000000001p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000180000000001p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.0000000000001fffffffffffp-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.0000000000001fffffffffffp-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000200000000000p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000200000000000p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000200000000001p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000200000000001p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.00000000000027ffffffffffp-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.00000000000027ffffffffffp-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000280000000001p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000003p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000280000000001p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000003p-600)) + +(module (func (export "f") (result f64) (f64.const +0x8000000.000000400000000000p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000000p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000400000000000p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000000p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000000400000000001p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000400000000001p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.0000007fffffffffffp-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.0000007fffffffffffp-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000000800000000000p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000800000000000p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000000800000000001p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000800000000001p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000000bfffffffffffp-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000bfffffffffffp-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000000c00000000000p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000c00000000000p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000000c00000000001p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000c00000000001p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000000ffffffffffffp-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000ffffffffffffp-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000001000000000000p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000001000000000000p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000001000000000001p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000001000000000001p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.0000013fffffffffffp-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.0000013fffffffffffp-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000001400000000001p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000003p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000001400000000001p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000003p-600)) + +(module (func (export "f") (result f64) (f64.const +5.3575430359313371995e+300))) +(assert_return (invoke "f") (f64.const +0x1.0000000000000p+999)) +(module (func (export "f") (result f64) (f64.const -5.3575430359313371995e+300))) +(assert_return (invoke "f") (f64.const -0x1.0000000000000p+999)) +(module (func (export "f") (result f64) (f64.const +5.3575430359313371996e+300))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+999)) +(module (func (export "f") (result f64) (f64.const -5.3575430359313371996e+300))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+999)) +(module (func (export "f") (result f64) (f64.const +5.3575430359313383891e+300))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+999)) +(module (func (export "f") (result f64) (f64.const -5.3575430359313383891e+300))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+999)) +(module (func (export "f") (result f64) (f64.const +5.3575430359313383892e+300))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+999)) +(module (func (export "f") (result f64) (f64.const -5.3575430359313383892e+300))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+999)) + +;; f64, large exponent +(module (func (export "f") (result f64) (f64.const +0x1.000000000000080000000000p+600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000000p+600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000080000000000p+600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000000p+600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000080000000001p+600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000080000000001p+600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+600)) +(module (func (export "f") (result f64) (f64.const +0x1.0000000000000fffffffffffp+600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+600)) +(module (func (export "f") (result f64) (f64.const -0x1.0000000000000fffffffffffp+600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000100000000000p+600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000100000000000p+600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000100000000001p+600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000100000000001p+600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+600)) +(module (func (export "f") (result f64) (f64.const +0x1.00000000000017ffffffffffp+600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+600)) +(module (func (export "f") (result f64) (f64.const -0x1.00000000000017ffffffffffp+600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000180000000000p+600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000180000000000p+600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000180000000001p+600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000180000000001p+600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+600)) +(module (func (export "f") (result f64) (f64.const +0x1.0000000000001fffffffffffp+600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+600)) +(module (func (export "f") (result f64) (f64.const -0x1.0000000000001fffffffffffp+600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000200000000000p+600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000200000000000p+600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000200000000001p+600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000200000000001p+600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+600)) +(module (func (export "f") (result f64) (f64.const +0x1.00000000000027ffffffffffp+600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+600)) +(module (func (export "f") (result f64) (f64.const -0x1.00000000000027ffffffffffp+600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000280000000000p+600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000280000000000p+600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000280000000001p+600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000003p+600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000280000000001p+600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000003p+600)) + +(module (func (export "f") (result f64) (f64.const +0x2000000000000100000000000))) +(assert_return (invoke "f") (f64.const +0x1.0000000000000p+97)) +(module (func (export "f") (result f64) (f64.const -0x2000000000000100000000000))) +(assert_return (invoke "f") (f64.const -0x1.0000000000000p+97)) +(module (func (export "f") (result f64) (f64.const +0x2000000000000100000000001))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+97)) +(module (func (export "f") (result f64) (f64.const -0x2000000000000100000000001))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+97)) +(module (func (export "f") (result f64) (f64.const +0x20000000000001fffffffffff))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+97)) +(module (func (export "f") (result f64) (f64.const -0x20000000000001fffffffffff))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+97)) +(module (func (export "f") (result f64) (f64.const +0x2000000000000200000000000))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+97)) +(module (func (export "f") (result f64) (f64.const -0x2000000000000200000000000))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+97)) +(module (func (export "f") (result f64) (f64.const +0x2000000000000200000000001))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+97)) +(module (func (export "f") (result f64) (f64.const -0x2000000000000200000000001))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+97)) +(module (func (export "f") (result f64) (f64.const +0x20000000000002fffffffffff))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+97)) +(module (func (export "f") (result f64) (f64.const -0x20000000000002fffffffffff))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+97)) +(module (func (export "f") (result f64) (f64.const +0x2000000000000300000000000))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+97)) +(module (func (export "f") (result f64) (f64.const -0x2000000000000300000000000))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+97)) +(module (func (export "f") (result f64) (f64.const +0x2000000000000300000000001))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+97)) +(module (func (export "f") (result f64) (f64.const -0x2000000000000300000000001))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+97)) +(module (func (export "f") (result f64) (f64.const +0x20000000000003fffffffffff))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+97)) +(module (func (export "f") (result f64) (f64.const -0x20000000000003fffffffffff))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+97)) +(module (func (export "f") (result f64) (f64.const +0x2000000000000400000000000))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+97)) +(module (func (export "f") (result f64) (f64.const -0x2000000000000400000000000))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+97)) +(module (func (export "f") (result f64) (f64.const +0x2000000000000400000000001))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+97)) +(module (func (export "f") (result f64) (f64.const -0x2000000000000400000000001))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+97)) +(module (func (export "f") (result f64) (f64.const +0x20000000000004fffffffffff))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+97)) +(module (func (export "f") (result f64) (f64.const -0x20000000000004fffffffffff))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+97)) +(module (func (export "f") (result f64) (f64.const +0x2000000000000500000000000))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+97)) +(module (func (export "f") (result f64) (f64.const -0x2000000000000500000000000))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+97)) +(module (func (export "f") (result f64) (f64.const +0x2000000000000500000000001))) +(assert_return (invoke "f") (f64.const +0x1.0000000000003p+97)) +(module (func (export "f") (result f64) (f64.const -0x2000000000000500000000001))) +(assert_return (invoke "f") (f64.const -0x1.0000000000003p+97)) + +(module (func (export "f") (result f64) (f64.const +1152921504606847104))) +(assert_return (invoke "f") (f64.const +0x1.0000000000000p+60)) +(module (func (export "f") (result f64) (f64.const -1152921504606847104))) +(assert_return (invoke "f") (f64.const -0x1.0000000000000p+60)) +(module (func (export "f") (result f64) (f64.const +1152921504606847105))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+60)) +(module (func (export "f") (result f64) (f64.const -1152921504606847105))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+60)) +(module (func (export "f") (result f64) (f64.const +1152921504606847359))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+60)) +(module (func (export "f") (result f64) (f64.const -1152921504606847359))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+60)) +(module (func (export "f") (result f64) (f64.const +1152921504606847360))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+60)) +(module (func (export "f") (result f64) (f64.const -1152921504606847360))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+60)) + +;; f64, subnormal +(module (func (export "f") (result f64) (f64.const +0x0.000000000000080000000000p-1022))) +(assert_return (invoke "f") (f64.const +0x0.0000000000000p-1022)) +(module (func (export "f") (result f64) (f64.const -0x0.000000000000080000000000p-1022))) +(assert_return (invoke "f") (f64.const -0x0.0000000000000p-1022)) +(module (func (export "f") (result f64) (f64.const +0x0.000000000000080000000001p-1022))) +(assert_return (invoke "f") (f64.const +0x0.0000000000001p-1022)) +(module (func (export "f") (result f64) (f64.const -0x0.000000000000080000000001p-1022))) +(assert_return (invoke "f") (f64.const -0x0.0000000000001p-1022)) +(module (func (export "f") (result f64) (f64.const +0x0.0000000000000fffffffffffp-1022))) +(assert_return (invoke "f") (f64.const +0x0.0000000000001p-1022)) +(module (func (export "f") (result f64) (f64.const -0x0.0000000000000fffffffffffp-1022))) +(assert_return (invoke "f") (f64.const -0x0.0000000000001p-1022)) +(module (func (export "f") (result f64) (f64.const +0x0.000000000000100000000000p-1022))) +(assert_return (invoke "f") (f64.const +0x0.0000000000001p-1022)) +(module (func (export "f") (result f64) (f64.const -0x0.000000000000100000000000p-1022))) +(assert_return (invoke "f") (f64.const -0x0.0000000000001p-1022)) +(module (func (export "f") (result f64) (f64.const +0x0.000000000000100000000001p-1022))) +(assert_return (invoke "f") (f64.const +0x0.0000000000001p-1022)) +(module (func (export "f") (result f64) (f64.const -0x0.000000000000100000000001p-1022))) +(assert_return (invoke "f") (f64.const -0x0.0000000000001p-1022)) +(module (func (export "f") (result f64) (f64.const +0x0.00000000000017ffffffffffp-1022))) +(assert_return (invoke "f") (f64.const +0x0.0000000000001p-1022)) +(module (func (export "f") (result f64) (f64.const -0x0.00000000000017ffffffffffp-1022))) +(assert_return (invoke "f") (f64.const -0x0.0000000000001p-1022)) +(module (func (export "f") (result f64) (f64.const +0x0.000000000000180000000000p-1022))) +(assert_return (invoke "f") (f64.const +0x0.0000000000002p-1022)) +(module (func (export "f") (result f64) (f64.const -0x0.000000000000180000000000p-1022))) +(assert_return (invoke "f") (f64.const -0x0.0000000000002p-1022)) +(module (func (export "f") (result f64) (f64.const +0x0.000000000000180000000001p-1022))) +(assert_return (invoke "f") (f64.const +0x0.0000000000002p-1022)) +(module (func (export "f") (result f64) (f64.const -0x0.000000000000180000000001p-1022))) +(assert_return (invoke "f") (f64.const -0x0.0000000000002p-1022)) +(module (func (export "f") (result f64) (f64.const +0x0.0000000000001fffffffffffp-1022))) +(assert_return (invoke "f") (f64.const +0x0.0000000000002p-1022)) +(module (func (export "f") (result f64) (f64.const -0x0.0000000000001fffffffffffp-1022))) +(assert_return (invoke "f") (f64.const -0x0.0000000000002p-1022)) +(module (func (export "f") (result f64) (f64.const +0x0.000000000000200000000000p-1022))) +(assert_return (invoke "f") (f64.const +0x0.0000000000002p-1022)) +(module (func (export "f") (result f64) (f64.const -0x0.000000000000200000000000p-1022))) +(assert_return (invoke "f") (f64.const -0x0.0000000000002p-1022)) +(module (func (export "f") (result f64) (f64.const +0x0.000000000000200000000001p-1022))) +(assert_return (invoke "f") (f64.const +0x0.0000000000002p-1022)) +(module (func (export "f") (result f64) (f64.const -0x0.000000000000200000000001p-1022))) +(assert_return (invoke "f") (f64.const -0x0.0000000000002p-1022)) +(module (func (export "f") (result f64) (f64.const +0x0.00000000000027ffffffffffp-1022))) +(assert_return (invoke "f") (f64.const +0x0.0000000000002p-1022)) +(module (func (export "f") (result f64) (f64.const -0x0.00000000000027ffffffffffp-1022))) +(assert_return (invoke "f") (f64.const -0x0.0000000000002p-1022)) +(module (func (export "f") (result f64) (f64.const +0x0.000000000000280000000000p-1022))) +(assert_return (invoke "f") (f64.const +0x0.0000000000002p-1022)) +(module (func (export "f") (result f64) (f64.const -0x0.000000000000280000000000p-1022))) +(assert_return (invoke "f") (f64.const -0x0.0000000000002p-1022)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000280000000001p-1022))) +(assert_return (invoke "f") (f64.const +0x1.0000000000003p-1022)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000280000000001p-1022))) +(assert_return (invoke "f") (f64.const -0x1.0000000000003p-1022)) + +;; f64, round down at limit to infinity +(module (func (export "f") (result f64) (f64.const +0x1.fffffffffffff4p1023))) +(assert_return (invoke "f") (f64.const +0x1.fffffffffffffp1023)) +(module (func (export "f") (result f64) (f64.const -0x1.fffffffffffff4p1023))) +(assert_return (invoke "f") (f64.const -0x1.fffffffffffffp1023)) +(module (func (export "f") (result f64) (f64.const +0x1.fffffffffffff7ffffffp1023))) +(assert_return (invoke "f") (f64.const +0x1.fffffffffffffp1023)) +(module (func (export "f") (result f64) (f64.const -0x1.fffffffffffff7ffffffp1023))) +(assert_return (invoke "f") (f64.const -0x1.fffffffffffffp1023)) diff --git a/runtime/unc-vm/tests/wast/spec/conversions.wast b/runtime/unc-vm/tests/wast/spec/conversions.wast new file mode 100644 index 000000000..65d09339c --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/conversions.wast @@ -0,0 +1,702 @@ +(module + (func (export "i64.extend_i32_s") (param $x i32) (result i64) (i64.extend_i32_s (local.get $x))) + (func (export "i64.extend_i32_u") (param $x i32) (result i64) (i64.extend_i32_u (local.get $x))) + (func (export "i32.wrap_i64") (param $x i64) (result i32) (i32.wrap_i64 (local.get $x))) + (func (export "i32.trunc_f32_s") (param $x f32) (result i32) (i32.trunc_f32_s (local.get $x))) + (func (export "i32.trunc_f32_u") (param $x f32) (result i32) (i32.trunc_f32_u (local.get $x))) + (func (export "i32.trunc_f64_s") (param $x f64) (result i32) (i32.trunc_f64_s (local.get $x))) + (func (export "i32.trunc_f64_u") (param $x f64) (result i32) (i32.trunc_f64_u (local.get $x))) + (func (export "i64.trunc_f32_s") (param $x f32) (result i64) (i64.trunc_f32_s (local.get $x))) + (func (export "i64.trunc_f32_u") (param $x f32) (result i64) (i64.trunc_f32_u (local.get $x))) + (func (export "i64.trunc_f64_s") (param $x f64) (result i64) (i64.trunc_f64_s (local.get $x))) + (func (export "i64.trunc_f64_u") (param $x f64) (result i64) (i64.trunc_f64_u (local.get $x))) + (func (export "i32.trunc_sat_f32_s") (param $x f32) (result i32) (i32.trunc_sat_f32_s (local.get $x))) + (func (export "i32.trunc_sat_f32_u") (param $x f32) (result i32) (i32.trunc_sat_f32_u (local.get $x))) + (func (export "i32.trunc_sat_f64_s") (param $x f64) (result i32) (i32.trunc_sat_f64_s (local.get $x))) + (func (export "i32.trunc_sat_f64_u") (param $x f64) (result i32) (i32.trunc_sat_f64_u (local.get $x))) + (func (export "i64.trunc_sat_f32_s") (param $x f32) (result i64) (i64.trunc_sat_f32_s (local.get $x))) + (func (export "i64.trunc_sat_f32_u") (param $x f32) (result i64) (i64.trunc_sat_f32_u (local.get $x))) + (func (export "i64.trunc_sat_f64_s") (param $x f64) (result i64) (i64.trunc_sat_f64_s (local.get $x))) + (func (export "i64.trunc_sat_f64_u") (param $x f64) (result i64) (i64.trunc_sat_f64_u (local.get $x))) + (func (export "f32.convert_i32_s") (param $x i32) (result f32) (f32.convert_i32_s (local.get $x))) + (func (export "f32.convert_i64_s") (param $x i64) (result f32) (f32.convert_i64_s (local.get $x))) + (func (export "f64.convert_i32_s") (param $x i32) (result f64) (f64.convert_i32_s (local.get $x))) + (func (export "f64.convert_i64_s") (param $x i64) (result f64) (f64.convert_i64_s (local.get $x))) + (func (export "f32.convert_i32_u") (param $x i32) (result f32) (f32.convert_i32_u (local.get $x))) + (func (export "f32.convert_i64_u") (param $x i64) (result f32) (f32.convert_i64_u (local.get $x))) + (func (export "f64.convert_i32_u") (param $x i32) (result f64) (f64.convert_i32_u (local.get $x))) + (func (export "f64.convert_i64_u") (param $x i64) (result f64) (f64.convert_i64_u (local.get $x))) + (func (export "f64.promote_f32") (param $x f32) (result f64) (f64.promote_f32 (local.get $x))) + (func (export "f32.demote_f64") (param $x f64) (result f32) (f32.demote_f64 (local.get $x))) + (func (export "f32.reinterpret_i32") (param $x i32) (result f32) (f32.reinterpret_i32 (local.get $x))) + (func (export "f64.reinterpret_i64") (param $x i64) (result f64) (f64.reinterpret_i64 (local.get $x))) + (func (export "i32.reinterpret_f32") (param $x f32) (result i32) (i32.reinterpret_f32 (local.get $x))) + (func (export "i64.reinterpret_f64") (param $x f64) (result i64) (i64.reinterpret_f64 (local.get $x))) +) + +(assert_return (invoke "i64.extend_i32_s" (i32.const 0)) (i64.const 0)) +(assert_return (invoke "i64.extend_i32_s" (i32.const 10000)) (i64.const 10000)) +(assert_return (invoke "i64.extend_i32_s" (i32.const -10000)) (i64.const -10000)) +(assert_return (invoke "i64.extend_i32_s" (i32.const -1)) (i64.const -1)) +(assert_return (invoke "i64.extend_i32_s" (i32.const 0x7fffffff)) (i64.const 0x000000007fffffff)) +(assert_return (invoke "i64.extend_i32_s" (i32.const 0x80000000)) (i64.const 0xffffffff80000000)) + +(assert_return (invoke "i64.extend_i32_u" (i32.const 0)) (i64.const 0)) +(assert_return (invoke "i64.extend_i32_u" (i32.const 10000)) (i64.const 10000)) +(assert_return (invoke "i64.extend_i32_u" (i32.const -10000)) (i64.const 0x00000000ffffd8f0)) +(assert_return (invoke "i64.extend_i32_u" (i32.const -1)) (i64.const 0xffffffff)) +(assert_return (invoke "i64.extend_i32_u" (i32.const 0x7fffffff)) (i64.const 0x000000007fffffff)) +(assert_return (invoke "i64.extend_i32_u" (i32.const 0x80000000)) (i64.const 0x0000000080000000)) + +(assert_return (invoke "i32.wrap_i64" (i64.const -1)) (i32.const -1)) +(assert_return (invoke "i32.wrap_i64" (i64.const -100000)) (i32.const -100000)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0x80000000)) (i32.const 0x80000000)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0xffffffff7fffffff)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0xffffffff00000000)) (i32.const 0x00000000)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0xfffffffeffffffff)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0xffffffff00000001)) (i32.const 0x00000001)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0)) (i32.const 0)) +(assert_return (invoke "i32.wrap_i64" (i64.const 1311768467463790320)) (i32.const 0x9abcdef0)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0x00000000ffffffff)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0x0000000100000000)) (i32.const 0x00000000)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0x0000000100000001)) (i32.const 0x00000001)) + +(assert_return (invoke "i32.trunc_f32_s" (f32.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const 0x1.19999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -1.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -0x1.19999ap+0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -1.5)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -1.9)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -2.0)) (i32.const -2)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const 2147483520.0)) (i32.const 2147483520)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -2147483648.0)) (i32.const -2147483648)) +(assert_trap (invoke "i32.trunc_f32_s" (f32.const 2147483648.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_s" (f32.const -2147483904.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_s" (f32.const inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_s" (f32.const -inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_s" (f32.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f32_s" (f32.const nan:0x200000)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f32_s" (f32.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f32_s" (f32.const -nan:0x200000)) "invalid conversion to integer") + +(assert_return (invoke "i32.trunc_f32_u" (f32.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const 0x1.19999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const 1.9)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const 2.0)) (i32.const 2)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const 2147483648)) (i32.const -2147483648)) ;; 0x1.00000p+31 -> 8000 0000 +(assert_return (invoke "i32.trunc_f32_u" (f32.const 4294967040.0)) (i32.const -256)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const -0x1.ccccccp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const -0x1.fffffep-1)) (i32.const 0)) +(assert_trap (invoke "i32.trunc_f32_u" (f32.const 4294967296.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_u" (f32.const -1.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_u" (f32.const inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_u" (f32.const -inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_u" (f32.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f32_u" (f32.const nan:0x200000)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f32_u" (f32.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f32_u" (f32.const -nan:0x200000)) "invalid conversion to integer") + +(assert_return (invoke "i32.trunc_f64_s" (f64.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const 0x1.199999999999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -1.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -0x1.199999999999ap+0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -1.5)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -1.9)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -2.0)) (i32.const -2)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const 2147483647.0)) (i32.const 2147483647)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -2147483648.0)) (i32.const -2147483648)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -2147483648.9)) (i32.const -2147483648)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const 2147483647.9)) (i32.const 2147483647)) +(assert_trap (invoke "i32.trunc_f64_s" (f64.const 2147483648.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_s" (f64.const -2147483649.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_s" (f64.const inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_s" (f64.const -inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_s" (f64.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f64_s" (f64.const nan:0x4000000000000)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f64_s" (f64.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f64_s" (f64.const -nan:0x4000000000000)) "invalid conversion to integer") + +(assert_return (invoke "i32.trunc_f64_u" (f64.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 0x1.199999999999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 1.9)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 2.0)) (i32.const 2)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 2147483648)) (i32.const -2147483648)) ;; 0x1.00000p+31 -> 8000 0000 +(assert_return (invoke "i32.trunc_f64_u" (f64.const 4294967295.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const -0x1.ccccccccccccdp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const -0x1.fffffffffffffp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 1e8)) (i32.const 100000000)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const -0.9)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 4294967295.9)) (i32.const 4294967295)) +(assert_trap (invoke "i32.trunc_f64_u" (f64.const 4294967296.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const -1.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const 1e16)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const 1e30)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const 9223372036854775808)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const -inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const nan:0x4000000000000)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const -nan:0x4000000000000)) "invalid conversion to integer") + +(assert_return (invoke "i64.trunc_f32_s" (f32.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const 0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const 0x1.19999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -1.0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -0x1.19999ap+0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -1.5)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -1.9)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -2.0)) (i64.const -2)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const 4294967296)) (i64.const 4294967296)) ;; 0x1.00000p+32 -> 1 0000 0000 +(assert_return (invoke "i64.trunc_f32_s" (f32.const -4294967296)) (i64.const -4294967296)) ;; -0x1.00000p+32 -> ffff ffff 0000 0000 +(assert_return (invoke "i64.trunc_f32_s" (f32.const 9223371487098961920.0)) (i64.const 9223371487098961920)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -9223372036854775808.0)) (i64.const -9223372036854775808)) +(assert_trap (invoke "i64.trunc_f32_s" (f32.const 9223372036854775808.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_s" (f32.const -9223373136366403584.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_s" (f32.const inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_s" (f32.const -inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_s" (f32.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f32_s" (f32.const nan:0x200000)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f32_s" (f32.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f32_s" (f32.const -nan:0x200000)) "invalid conversion to integer") + +(assert_return (invoke "i64.trunc_f32_u" (f32.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const 0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const -0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const 0x1.19999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const 4294967296)) (i64.const 4294967296)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const 18446742974197923840.0)) (i64.const -1099511627776)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const -0x1.ccccccp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const -0x1.fffffep-1)) (i64.const 0)) +(assert_trap (invoke "i64.trunc_f32_u" (f32.const 18446744073709551616.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_u" (f32.const -1.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_u" (f32.const inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_u" (f32.const -inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_u" (f32.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f32_u" (f32.const nan:0x200000)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f32_u" (f32.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f32_u" (f32.const -nan:0x200000)) "invalid conversion to integer") + +(assert_return (invoke "i64.trunc_f64_s" (f64.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const 0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const 0x1.199999999999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -1.0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -0x1.199999999999ap+0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -1.5)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -1.9)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -2.0)) (i64.const -2)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const 4294967296)) (i64.const 4294967296)) ;; 0x1.00000p+32 -> 1 0000 0000 +(assert_return (invoke "i64.trunc_f64_s" (f64.const -4294967296)) (i64.const -4294967296)) ;; -0x1.00000p+32 -> ffff ffff 0000 0000 +(assert_return (invoke "i64.trunc_f64_s" (f64.const 9223372036854774784.0)) (i64.const 9223372036854774784)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -9223372036854775808.0)) (i64.const -9223372036854775808)) +(assert_trap (invoke "i64.trunc_f64_s" (f64.const 9223372036854775808.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_s" (f64.const -9223372036854777856.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_s" (f64.const inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_s" (f64.const -inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_s" (f64.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f64_s" (f64.const nan:0x4000000000000)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f64_s" (f64.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f64_s" (f64.const -nan:0x4000000000000)) "invalid conversion to integer") + +(assert_return (invoke "i64.trunc_f64_u" (f64.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const -0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 0x1.199999999999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 4294967295)) (i64.const 0xffffffff)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 4294967296)) (i64.const 0x100000000)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 18446744073709549568.0)) (i64.const -2048)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const -0x1.ccccccccccccdp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const -0x1.fffffffffffffp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 1e8)) (i64.const 100000000)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 1e16)) (i64.const 10000000000000000)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 9223372036854775808)) (i64.const -9223372036854775808)) +(assert_trap (invoke "i64.trunc_f64_u" (f64.const 18446744073709551616.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_u" (f64.const -1.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_u" (f64.const inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_u" (f64.const -inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_u" (f64.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f64_u" (f64.const nan:0x4000000000000)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f64_u" (f64.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f64_u" (f64.const -nan:0x4000000000000)) "invalid conversion to integer") + +(assert_return (invoke "f32.convert_i32_s" (i32.const 1)) (f32.const 1.0)) +(assert_return (invoke "f32.convert_i32_s" (i32.const -1)) (f32.const -1.0)) +(assert_return (invoke "f32.convert_i32_s" (i32.const 0)) (f32.const 0.0)) +(assert_return (invoke "f32.convert_i32_s" (i32.const 2147483647)) (f32.const 2147483648)) +(assert_return (invoke "f32.convert_i32_s" (i32.const -2147483648)) (f32.const -2147483648)) +(assert_return (invoke "f32.convert_i32_s" (i32.const 1234567890)) (f32.const 0x1.26580cp+30)) + +;; Saturating conversions: test all the same values as the non-saturating conversions. + +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 0x1.19999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -1.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -0x1.19999ap+0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -1.5)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -1.9)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -2.0)) (i32.const -2)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 2147483520.0)) (i32.const 2147483520)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -2147483648.0)) (i32.const -2147483648)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 2147483648.0)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -2147483904.0)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const inf)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -inf)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -nan:0x200000)) (i32.const 0)) + +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 0x1.19999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 1.9)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 2.0)) (i32.const 2)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 2147483648)) (i32.const -2147483648)) ;; 0x1.00000p+31 -> 8000 0000 +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 4294967040.0)) (i32.const -256)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -0x1.ccccccp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -0x1.fffffep-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 4294967296.0)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -1.0)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const inf)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -inf)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -nan:0x200000)) (i32.const 0)) + +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 0x1.199999999999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -1.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -0x1.199999999999ap+0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -1.5)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -1.9)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -2.0)) (i32.const -2)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 2147483647.0)) (i32.const 2147483647)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -2147483648.0)) (i32.const -2147483648)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 2147483648.0)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -2147483649.0)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const inf)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -inf)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -nan:0x4000000000000)) (i32.const 0)) + +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 0x1.199999999999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1.9)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 2.0)) (i32.const 2)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 2147483648)) (i32.const -2147483648)) ;; 0x1.00000p+31 -> 8000 0000 +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 4294967295.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -0x1.ccccccccccccdp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -0x1.fffffffffffffp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1e8)) (i32.const 100000000)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 4294967296.0)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -1.0)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1e16)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1e30)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 9223372036854775808)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const inf)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -inf)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -nan:0x4000000000000)) (i32.const 0)) + +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 0x1.19999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -1.0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -0x1.19999ap+0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -1.5)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -1.9)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -2.0)) (i64.const -2)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 4294967296)) (i64.const 4294967296)) ;; 0x1.00000p+32 -> 1 0000 0000 +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -4294967296)) (i64.const -4294967296)) ;; -0x1.00000p+32 -> ffff ffff 0000 0000 +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 9223371487098961920.0)) (i64.const 9223371487098961920)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -9223372036854775808.0)) (i64.const -9223372036854775808)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 9223372036854775808.0)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -9223373136366403584.0)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const inf)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -inf)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const nan:0x200000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -nan:0x200000)) (i64.const 0)) + +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 0x1.19999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 4294967296)) (i64.const 4294967296)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 18446742974197923840.0)) (i64.const -1099511627776)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -0x1.ccccccp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -0x1.fffffep-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 18446744073709551616.0)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -1.0)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const inf)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -inf)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const nan:0x200000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -nan:0x200000)) (i64.const 0)) + +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 0x1.199999999999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -1.0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -0x1.199999999999ap+0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -1.5)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -1.9)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -2.0)) (i64.const -2)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 4294967296)) (i64.const 4294967296)) ;; 0x1.00000p+32 -> 1 0000 0000 +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -4294967296)) (i64.const -4294967296)) ;; -0x1.00000p+32 -> ffff ffff 0000 0000 +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 9223372036854774784.0)) (i64.const 9223372036854774784)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -9223372036854775808.0)) (i64.const -9223372036854775808)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 9223372036854775808.0)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -9223372036854777856.0)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const inf)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -inf)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const nan:0x4000000000000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -nan:0x4000000000000)) (i64.const 0)) + +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 0x1.199999999999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 4294967295)) (i64.const 0xffffffff)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 4294967296)) (i64.const 0x100000000)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 18446744073709549568.0)) (i64.const -2048)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -0x1.ccccccccccccdp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -0x1.fffffffffffffp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 1e8)) (i64.const 100000000)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 1e16)) (i64.const 10000000000000000)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 9223372036854775808)) (i64.const -9223372036854775808)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 18446744073709551616.0)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -1.0)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const inf)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -inf)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const nan:0x4000000000000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -nan:0x4000000000000)) (i64.const 0)) + +;; Test rounding directions. +(assert_return (invoke "f32.convert_i32_s" (i32.const 16777217)) (f32.const 16777216.0)) +(assert_return (invoke "f32.convert_i32_s" (i32.const -16777217)) (f32.const -16777216.0)) +(assert_return (invoke "f32.convert_i32_s" (i32.const 16777219)) (f32.const 16777220.0)) +(assert_return (invoke "f32.convert_i32_s" (i32.const -16777219)) (f32.const -16777220.0)) + +(assert_return (invoke "f32.convert_i64_s" (i64.const 1)) (f32.const 1.0)) +(assert_return (invoke "f32.convert_i64_s" (i64.const -1)) (f32.const -1.0)) +(assert_return (invoke "f32.convert_i64_s" (i64.const 0)) (f32.const 0.0)) +(assert_return (invoke "f32.convert_i64_s" (i64.const 9223372036854775807)) (f32.const 9223372036854775807)) +(assert_return (invoke "f32.convert_i64_s" (i64.const -9223372036854775808)) (f32.const -9223372036854775808)) +(assert_return (invoke "f32.convert_i64_s" (i64.const 314159265358979)) (f32.const 0x1.1db9e8p+48)) ;; PI +;; Test rounding directions. +(assert_return (invoke "f32.convert_i64_s" (i64.const 16777217)) (f32.const 16777216.0)) +(assert_return (invoke "f32.convert_i64_s" (i64.const -16777217)) (f32.const -16777216.0)) +(assert_return (invoke "f32.convert_i64_s" (i64.const 16777219)) (f32.const 16777220.0)) +(assert_return (invoke "f32.convert_i64_s" (i64.const -16777219)) (f32.const -16777220.0)) + +(assert_return (invoke "f32.convert_i64_s" (i64.const 0x7fffff4000000001)) (f32.const 0x1.fffffep+62)) +(assert_return (invoke "f32.convert_i64_s" (i64.const 0x8000004000000001)) (f32.const -0x1.fffffep+62)) +(assert_return (invoke "f32.convert_i64_s" (i64.const 0x0020000020000001)) (f32.const 0x1.000002p+53)) +(assert_return (invoke "f32.convert_i64_s" (i64.const 0xffdfffffdfffffff)) (f32.const -0x1.000002p+53)) + +(assert_return (invoke "f64.convert_i32_s" (i32.const 1)) (f64.const 1.0)) +(assert_return (invoke "f64.convert_i32_s" (i32.const -1)) (f64.const -1.0)) +(assert_return (invoke "f64.convert_i32_s" (i32.const 0)) (f64.const 0.0)) +(assert_return (invoke "f64.convert_i32_s" (i32.const 2147483647)) (f64.const 2147483647)) +(assert_return (invoke "f64.convert_i32_s" (i32.const -2147483648)) (f64.const -2147483648)) +(assert_return (invoke "f64.convert_i32_s" (i32.const 987654321)) (f64.const 987654321)) + +(assert_return (invoke "f64.convert_i64_s" (i64.const 1)) (f64.const 1.0)) +(assert_return (invoke "f64.convert_i64_s" (i64.const -1)) (f64.const -1.0)) +(assert_return (invoke "f64.convert_i64_s" (i64.const 0)) (f64.const 0.0)) +(assert_return (invoke "f64.convert_i64_s" (i64.const 9223372036854775807)) (f64.const 9223372036854775807)) +(assert_return (invoke "f64.convert_i64_s" (i64.const -9223372036854775808)) (f64.const -9223372036854775808)) +(assert_return (invoke "f64.convert_i64_s" (i64.const 4669201609102990)) (f64.const 4669201609102990)) ;; Feigenbaum +;; Test rounding directions. +(assert_return (invoke "f64.convert_i64_s" (i64.const 9007199254740993)) (f64.const 9007199254740992)) +(assert_return (invoke "f64.convert_i64_s" (i64.const -9007199254740993)) (f64.const -9007199254740992)) +(assert_return (invoke "f64.convert_i64_s" (i64.const 9007199254740995)) (f64.const 9007199254740996)) +(assert_return (invoke "f64.convert_i64_s" (i64.const -9007199254740995)) (f64.const -9007199254740996)) + +(assert_return (invoke "f32.convert_i32_u" (i32.const 1)) (f32.const 1.0)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0)) (f32.const 0.0)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 2147483647)) (f32.const 2147483648)) +(assert_return (invoke "f32.convert_i32_u" (i32.const -2147483648)) (f32.const 2147483648)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0x12345678)) (f32.const 0x1.234568p+28)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0xffffffff)) (f32.const 4294967296.0)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0x80000080)) (f32.const 0x1.000000p+31)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0x80000081)) (f32.const 0x1.000002p+31)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0x80000082)) (f32.const 0x1.000002p+31)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0xfffffe80)) (f32.const 0x1.fffffcp+31)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0xfffffe81)) (f32.const 0x1.fffffep+31)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0xfffffe82)) (f32.const 0x1.fffffep+31)) +;; Test rounding directions. +(assert_return (invoke "f32.convert_i32_u" (i32.const 16777217)) (f32.const 16777216.0)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 16777219)) (f32.const 16777220.0)) + +(assert_return (invoke "f32.convert_i64_u" (i64.const 1)) (f32.const 1.0)) +(assert_return (invoke "f32.convert_i64_u" (i64.const 0)) (f32.const 0.0)) +(assert_return (invoke "f32.convert_i64_u" (i64.const 9223372036854775807)) (f32.const 9223372036854775807)) +(assert_return (invoke "f32.convert_i64_u" (i64.const -9223372036854775808)) (f32.const 9223372036854775808)) +(assert_return (invoke "f32.convert_i64_u" (i64.const 0xffffffffffffffff)) (f32.const 18446744073709551616.0)) +;; Test rounding directions. +(assert_return (invoke "f32.convert_i64_u" (i64.const 16777217)) (f32.const 16777216.0)) +(assert_return (invoke "f32.convert_i64_u" (i64.const 16777219)) (f32.const 16777220.0)) + +(assert_return (invoke "f32.convert_i64_u" (i64.const 0x0020000020000001)) (f32.const 0x1.000002p+53)) +(assert_return (invoke "f32.convert_i64_u" (i64.const 0x7fffffbfffffffff)) (f32.const 0x1.fffffep+62)) +(assert_return (invoke "f32.convert_i64_u" (i64.const 0x8000008000000001)) (f32.const 0x1.000002p+63)) +(assert_return (invoke "f32.convert_i64_u" (i64.const 0xfffffe8000000001)) (f32.const 0x1.fffffep+63)) + +(assert_return (invoke "f64.convert_i32_u" (i32.const 1)) (f64.const 1.0)) +(assert_return (invoke "f64.convert_i32_u" (i32.const 0)) (f64.const 0.0)) +(assert_return (invoke "f64.convert_i32_u" (i32.const 2147483647)) (f64.const 2147483647)) +(assert_return (invoke "f64.convert_i32_u" (i32.const -2147483648)) (f64.const 2147483648)) +(assert_return (invoke "f64.convert_i32_u" (i32.const 0xffffffff)) (f64.const 4294967295.0)) + +(assert_return (invoke "f64.convert_i64_u" (i64.const 1)) (f64.const 1.0)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0)) (f64.const 0.0)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 9223372036854775807)) (f64.const 9223372036854775807)) +(assert_return (invoke "f64.convert_i64_u" (i64.const -9223372036854775808)) (f64.const 9223372036854775808)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0xffffffffffffffff)) (f64.const 18446744073709551616.0)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0x8000000000000400)) (f64.const 0x1.0000000000000p+63)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0x8000000000000401)) (f64.const 0x1.0000000000001p+63)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0x8000000000000402)) (f64.const 0x1.0000000000001p+63)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0xfffffffffffff400)) (f64.const 0x1.ffffffffffffep+63)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0xfffffffffffff401)) (f64.const 0x1.fffffffffffffp+63)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0xfffffffffffff402)) (f64.const 0x1.fffffffffffffp+63)) +;; Test rounding directions. +(assert_return (invoke "f64.convert_i64_u" (i64.const 9007199254740993)) (f64.const 9007199254740992)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 9007199254740995)) (f64.const 9007199254740996)) + +(assert_return (invoke "f64.promote_f32" (f32.const 0.0)) (f64.const 0.0)) +(assert_return (invoke "f64.promote_f32" (f32.const -0.0)) (f64.const -0.0)) +(assert_return (invoke "f64.promote_f32" (f32.const 0x1p-149)) (f64.const 0x1p-149)) +(assert_return (invoke "f64.promote_f32" (f32.const -0x1p-149)) (f64.const -0x1p-149)) +(assert_return (invoke "f64.promote_f32" (f32.const 1.0)) (f64.const 1.0)) +(assert_return (invoke "f64.promote_f32" (f32.const -1.0)) (f64.const -1.0)) +(assert_return (invoke "f64.promote_f32" (f32.const -0x1.fffffep+127)) (f64.const -0x1.fffffep+127)) +(assert_return (invoke "f64.promote_f32" (f32.const 0x1.fffffep+127)) (f64.const 0x1.fffffep+127)) +;; Generated randomly by picking a random int and reinterpret it to float. +(assert_return (invoke "f64.promote_f32" (f32.const 0x1p-119)) (f64.const 0x1p-119)) +;; Generated randomly by picking a random float. +(assert_return (invoke "f64.promote_f32" (f32.const 0x1.8f867ep+125)) (f64.const 6.6382536710104395e+37)) +(assert_return (invoke "f64.promote_f32" (f32.const inf)) (f64.const inf)) +(assert_return (invoke "f64.promote_f32" (f32.const -inf)) (f64.const -inf)) +(assert_return (invoke "f64.promote_f32" (f32.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "f64.promote_f32" (f32.const nan:0x200000)) (f64.const nan:arithmetic)) +(assert_return (invoke "f64.promote_f32" (f32.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "f64.promote_f32" (f32.const -nan:0x200000)) (f64.const nan:arithmetic)) + +(assert_return (invoke "f32.demote_f64" (f64.const 0.0)) (f32.const 0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const -0.0)) (f32.const -0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x0.0000000000001p-1022)) (f32.const 0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x0.0000000000001p-1022)) (f32.const -0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const 1.0)) (f32.const 1.0)) +(assert_return (invoke "f32.demote_f64" (f64.const -1.0)) (f32.const -1.0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffe0000000p-127)) (f32.const 0x1p-126)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffe0000000p-127)) (f32.const -0x1p-126)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffdfffffffp-127)) (f32.const 0x1.fffffcp-127)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffdfffffffp-127)) (f32.const -0x1.fffffcp-127)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffd0000000p+127)) (f32.const 0x1.fffffcp+127)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffd0000000p+127)) (f32.const -0x1.fffffcp+127)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffd0000001p+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffd0000001p+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffefffffffp+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffefffffffp+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.ffffffp+127)) (f32.const inf)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.ffffffp+127)) (f32.const -inf)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1p-119)) (f32.const 0x1p-119)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.8f867ep+125)) (f32.const 0x1.8f867ep+125)) +(assert_return (invoke "f32.demote_f64" (f64.const inf)) (f32.const inf)) +(assert_return (invoke "f32.demote_f64" (f64.const -inf)) (f32.const -inf)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000000000001p+0)) (f32.const 1.0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffffffffffp-1)) (f32.const 1.0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000010000000p+0)) (f32.const 0x1.000000p+0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000010000001p+0)) (f32.const 0x1.000002p+0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.000002fffffffp+0)) (f32.const 0x1.000002p+0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000030000000p+0)) (f32.const 0x1.000004p+0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000050000000p+0)) (f32.const 0x1.000004p+0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000010000000p+24)) (f32.const 0x1.0p+24)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000010000001p+24)) (f32.const 0x1.000002p+24)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.000002fffffffp+24)) (f32.const 0x1.000002p+24)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000030000000p+24)) (f32.const 0x1.000004p+24)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.4eae4f7024c7p+108)) (f32.const 0x1.4eae5p+108)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.a12e71e358685p-113)) (f32.const 0x1.a12e72p-113)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.cb98354d521ffp-127)) (f32.const 0x1.cb9834p-127)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.6972b30cfb562p+1)) (f32.const -0x1.6972b4p+1)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.bedbe4819d4c4p+112)) (f32.const -0x1.bedbe4p+112)) +(assert_return (invoke "f32.demote_f64" (f64.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "f32.demote_f64" (f64.const nan:0x4000000000000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f32.demote_f64" (f64.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "f32.demote_f64" (f64.const -nan:0x4000000000000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1p-1022)) (f32.const 0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1p-1022)) (f32.const -0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0p-150)) (f32.const 0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.0p-150)) (f32.const -0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000000000001p-150)) (f32.const 0x1p-149)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.0000000000001p-150)) (f32.const -0x1p-149)) + +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0)) (f32.const 0.0)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0x80000000)) (f32.const -0.0)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 1)) (f32.const 0x1p-149)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const -1)) (f32.const -nan:0x7fffff)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 123456789)) (f32.const 0x1.b79a2ap-113)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const -2147483647)) (f32.const -0x1p-149)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0x7f800000)) (f32.const inf)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0xff800000)) (f32.const -inf)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0x7fc00000)) (f32.const nan)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0xffc00000)) (f32.const -nan)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0x7fa00000)) (f32.const nan:0x200000)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0xffa00000)) (f32.const -nan:0x200000)) + +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0)) (f64.const 0.0)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 1)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const -1)) (f64.const -nan:0xfffffffffffff)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0x8000000000000000)) (f64.const -0.0)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 1234567890)) (f64.const 0x0.00000499602d2p-1022)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const -9223372036854775807)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0x7ff0000000000000)) (f64.const inf)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0xfff0000000000000)) (f64.const -inf)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0x7ff8000000000000)) (f64.const nan)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0xfff8000000000000)) (f64.const -nan)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0x7ff4000000000000)) (f64.const nan:0x4000000000000)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0xfff4000000000000)) (f64.const -nan:0x4000000000000)) + +(assert_return (invoke "i32.reinterpret_f32" (f32.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const -0.0)) (i32.const 0x80000000)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const -nan:0x7fffff)) (i32.const -1)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const -0x1p-149)) (i32.const 0x80000001)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const 1.0)) (i32.const 1065353216)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const 3.1415926)) (i32.const 1078530010)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const 0x1.fffffep+127)) (i32.const 2139095039)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const -0x1.fffffep+127)) (i32.const -8388609)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const inf)) (i32.const 0x7f800000)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const -inf)) (i32.const 0xff800000)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const nan)) (i32.const 0x7fc00000)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const -nan)) (i32.const 0xffc00000)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const nan:0x200000)) (i32.const 0x7fa00000)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const -nan:0x200000)) (i32.const 0xffa00000)) + +(assert_return (invoke "i64.reinterpret_f64" (f64.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const -0.0)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const 0x0.0000000000001p-1022)) (i64.const 1)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const -nan:0xfffffffffffff)) (i64.const -1)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const -0x0.0000000000001p-1022)) (i64.const 0x8000000000000001)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const 1.0)) (i64.const 4607182418800017408)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const 3.14159265358979)) (i64.const 4614256656552045841)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const 0x1.fffffffffffffp+1023)) (i64.const 9218868437227405311)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const -0x1.fffffffffffffp+1023)) (i64.const -4503599627370497)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const inf)) (i64.const 0x7ff0000000000000)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const -inf)) (i64.const 0xfff0000000000000)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const nan)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const -nan)) (i64.const 0xfff8000000000000)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const nan:0x4000000000000)) (i64.const 0x7ff4000000000000)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const -nan:0x4000000000000)) (i64.const 0xfff4000000000000)) + +;; Type check + +(assert_invalid (module (func (result i32) (i32.wrap_i64 (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.trunc_f32_s (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.trunc_f32_u (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.trunc_f64_s (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.trunc_f64_u (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.reinterpret_f32 (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.extend_i32_s (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.extend_i32_u (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.trunc_f32_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.trunc_f32_u (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.trunc_f64_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.trunc_f64_u (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.reinterpret_f64 (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.convert_i32_s (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.convert_i32_u (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.convert_i64_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.convert_i64_u (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.demote_f64 (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.reinterpret_i32 (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.convert_i32_s (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.convert_i32_u (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.convert_i64_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.convert_i64_u (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.promote_f32 (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.reinterpret_i64 (i32.const 0)))) "type mismatch") diff --git a/runtime/unc-vm/tests/wast/spec/custom.wast b/runtime/unc-vm/tests/wast/spec/custom.wast new file mode 100644 index 000000000..b2394f5e3 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/custom.wast @@ -0,0 +1,130 @@ +(module binary + "\00asm" "\01\00\00\00" + "\00\24\10" "a custom section" "this is the payload" + "\00\20\10" "a custom section" "this is payload" + "\00\11\10" "a custom section" "" + "\00\10\00" "" "this is payload" + "\00\01\00" "" "" + "\00\24\10" "\00\00custom sectio\00" "this is the payload" + "\00\24\10" "\ef\bb\bfa custom sect" "this is the payload" + "\00\24\10" "a custom sect\e2\8c\a3" "this is the payload" + "\00\1f\16" "module within a module" "\00asm" "\01\00\00\00" +) + +(module binary + "\00asm" "\01\00\00\00" + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\01\01\00" ;; type section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\02\01\00" ;; import section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\03\01\00" ;; function section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\04\01\00" ;; table section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\05\01\00" ;; memory section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\06\01\00" ;; global section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\07\01\00" ;; export section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\09\01\00" ;; element section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\0a\01\00" ;; code section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\0b\01\00" ;; data section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" +) + +(module binary + "\00asm" "\01\00\00\00" + "\01\07\01\60\02\7f\7f\01\7f" ;; type section + "\00\1a\06" "custom" "this is the payload" ;; custom section + "\03\02\01\00" ;; function section + "\07\0a\01\06\61\64\64\54\77\6f\00\00" ;; export section + "\0a\09\01\07\00\20\00\20\01\6a\0b" ;; code section + "\00\1b\07" "custom2" "this is the payload" ;; custom section +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00" + ) + "unexpected end" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\00" + ) + "unexpected end" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\00\00\05\01\00\07\00\00" + ) + "unexpected end" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\26\10" "a custom section" "this is the payload" + ) + "unexpected end" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\25\10" "a custom section" "this is the payload" + "\00\24\10" "a custom section" "this is the payload" + ) + "malformed section id" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\07\01\60\02\7f\7f\01\7f" ;; type section + "\00\25\10" "a custom section" "this is the payload" ;; wrong length! + "\03\02\01\00" ;; function section + "\0a\09\01\07\00\20\00\20\01\6a\0b" ;; code section + "\00\1b\07" "custom2" "this is the payload" ;; custom section + ) + "function and code section have inconsistent lengths" +) + +;; Test concatenated modules. +(assert_malformed + (module binary + "\00asm\01\00\00\00" + "\00asm\01\00\00\00" + ) + "length out of bounds" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01\00\01" ;; memory section + "\0c\01\02" ;; data count section (2 segments) + "\0b\06\01\00\41\00\0b\00" ;; data section (1 segment) + ) + "data count and data section have inconsistent lengths" +) diff --git a/runtime/unc-vm/tests/wast/spec/data.wast b/runtime/unc-vm/tests/wast/spec/data.wast new file mode 100644 index 000000000..f7d1f09bc --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/data.wast @@ -0,0 +1,481 @@ +;; Test the data section + +;; Syntax + +(module + (memory $m 1) + (data (i32.const 0)) + (data (i32.const 1) "a" "" "bcd") + (data (offset (i32.const 0))) + (data (offset (i32.const 0)) "" "a" "bc" "") + (data (memory 0) (i32.const 0)) + (data (memory 0x0) (i32.const 1) "a" "" "bcd") + (data (memory 0x000) (offset (i32.const 0))) + (data (memory 0) (offset (i32.const 0)) "" "a" "bc" "") + (data (memory $m) (i32.const 0)) + (data (memory $m) (i32.const 1) "a" "" "bcd") + (data (memory $m) (offset (i32.const 0))) + (data (memory $m) (offset (i32.const 0)) "" "a" "bc" "") + (data $d1 (i32.const 0)) + (data $d2 (i32.const 1) "a" "" "bcd") + (data $d3 (offset (i32.const 0))) + (data $d4 (offset (i32.const 0)) "" "a" "bc" "") + (data $d5 (memory 0) (i32.const 0)) + (data $d6 (memory 0x0) (i32.const 1) "a" "" "bcd") + (data $d7 (memory 0x000) (offset (i32.const 0))) + (data $d8 (memory 0) (offset (i32.const 0)) "" "a" "bc" "") + (data $d9 (memory $m) (i32.const 0)) + (data $d10 (memory $m) (i32.const 1) "a" "" "bcd") + (data $d11 (memory $m) (offset (i32.const 0))) + (data $d12 (memory $m) (offset (i32.const 0)) "" "a" "bc" "") +) + +;; Basic use + +(module + (memory 1) + (data (i32.const 0) "a") +) +(module + (import "spectest" "memory" (memory 1)) + (data (i32.const 0) "a") +) + +(module + (memory 1) + (data (i32.const 0) "a") + (data (i32.const 3) "b") + (data (i32.const 100) "cde") + (data (i32.const 5) "x") + (data (i32.const 3) "c") +) +(module + (import "spectest" "memory" (memory 1)) + (data (i32.const 0) "a") + (data (i32.const 1) "b") + (data (i32.const 2) "cde") + (data (i32.const 3) "f") + (data (i32.const 2) "g") + (data (i32.const 1) "h") +) + +(module + (global (import "spectest" "global_i32") i32) + (memory 1) + (data (global.get 0) "a") +) +(module + (global (import "spectest" "global_i32") i32) + (import "spectest" "memory" (memory 1)) + (data (global.get 0) "a") +) + +(module + (global $g (import "spectest" "global_i32") i32) + (memory 1) + (data (global.get $g) "a") +) +(module + (global $g (import "spectest" "global_i32") i32) + (import "spectest" "memory" (memory 1)) + (data (global.get $g) "a") +) + +;; Use of internal globals in constant expressions is not allowed in MVP. +;; (module (memory 1) (data (global.get 0) "a") (global i32 (i32.const 0))) +;; (module (memory 1) (data (global.get $g) "a") (global $g i32 (i32.const 0))) + +;; Corner cases + +(module + (memory 1) + (data (i32.const 0) "a") + (data (i32.const 0xffff) "b") +) +(module + (import "spectest" "memory" (memory 1)) + (data (i32.const 0) "a") + (data (i32.const 0xffff) "b") +) + +(module + (memory 2) + (data (i32.const 0x1_ffff) "a") +) + +(module + (memory 0) + (data (i32.const 0)) +) +(module + (import "spectest" "memory" (memory 0)) + (data (i32.const 0)) +) + +(module + (memory 0 0) + (data (i32.const 0)) +) + +(module + (memory 1) + (data (i32.const 0x1_0000) "") +) + +(module + (memory 0) + (data (i32.const 0) "" "") +) +(module + (import "spectest" "memory" (memory 0)) + (data (i32.const 0) "" "") +) + +(module + (memory 0 0) + (data (i32.const 0) "" "") +) + +(module + (import "spectest" "memory" (memory 0)) + (data (i32.const 0) "a") +) + +(module + (import "spectest" "memory" (memory 0 3)) + (data (i32.const 0) "a") +) + +(module + (global (import "spectest" "global_i32") i32) + (import "spectest" "memory" (memory 0)) + (data (global.get 0) "a") +) + +(module + (global (import "spectest" "global_i32") i32) + (import "spectest" "memory" (memory 0 3)) + (data (global.get 0) "a") +) + +(module + (import "spectest" "memory" (memory 0)) + (data (i32.const 1) "a") +) + +(module + (import "spectest" "memory" (memory 0 3)) + (data (i32.const 1) "a") +) + +;; Invalid bounds for data + +(assert_trap + (module + (memory 0) + (data (i32.const 0) "a") + ) + "out of bounds memory access" +) + +(assert_trap + (module + (memory 0 0) + (data (i32.const 0) "a") + ) + "out of bounds memory access" +) + +(assert_trap + (module + (memory 0 1) + (data (i32.const 0) "a") + ) + "out of bounds memory access" +) +(assert_trap + (module + (memory 0) + (data (i32.const 1)) + ) + "out of bounds memory access" +) +(assert_trap + (module + (memory 0 1) + (data (i32.const 1)) + ) + "out of bounds memory access" +) + +;; This seems to cause a time-out on Travis. +(;assert_unlinkable + (module + (memory 0x10000) + (data (i32.const 0xffffffff) "ab") + ) + "" ;; either out of memory or out of bounds +;) + +(assert_trap + (module + (global (import "spectest" "global_i32") i32) + (memory 0) + (data (global.get 0) "a") + ) + "out of bounds memory access" +) + +(assert_trap + (module + (memory 1 2) + (data (i32.const 0x1_0000) "a") + ) + "out of bounds memory access" +) +(assert_trap + (module + (import "spectest" "memory" (memory 1)) + (data (i32.const 0x1_0000) "a") + ) + "out of bounds memory access" +) + +(assert_trap + (module + (memory 2) + (data (i32.const 0x2_0000) "a") + ) + "out of bounds memory access" +) + +(assert_trap + (module + (memory 2 3) + (data (i32.const 0x2_0000) "a") + ) + "out of bounds memory access" +) + +(assert_trap + (module + (memory 1) + (data (i32.const -1) "a") + ) + "out of bounds memory access" +) +(assert_trap + (module + (import "spectest" "memory" (memory 1)) + (data (i32.const -1) "a") + ) + "out of bounds memory access" +) + +(assert_trap + (module + (memory 2) + (data (i32.const -100) "a") + ) + "out of bounds memory access" +) +(assert_trap + (module + (import "spectest" "memory" (memory 1)) + (data (i32.const -100) "a") + ) + "out of bounds memory access" +) + +;; Data without memory + +(assert_invalid + (module + (data (i32.const 0) "") + ) + "unknown memory" +) + +;; Data segment with memory index 1 (only memory 0 available) +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\00" ;; memory 0 + "\0b\07\01" ;; data section + "\02\01\41\00\0b" ;; active data segment 0 for memory 1 + "\00" ;; empty vec(byte) + ) + "unknown memory 1" +) + +;; Data segment with memory index 0 (no memory section) +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" + "\0b\06\01" ;; data section + "\00\41\00\0b" ;; active data segment 0 for memory 0 + "\00" ;; empty vec(byte) + ) + "unknown memory 0" +) + +;; Data segment with memory index 1 (no memory section) +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" + "\0b\07\01" ;; data section + "\02\01\41\00\0b" ;; active data segment 0 for memory 1 + "\00" ;; empty vec(byte) + ) + "unknown memory 1" +) + +;; Data segment with memory index 1 and vec(byte) as above, +;; only memory 0 available. +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\00" ;; memory 0 + "\0b\45\01" ;; data section + "\02" ;; active segment + "\01" ;; memory index + "\41\00\0b" ;; offset constant expression + "\3e" ;; vec(byte) length + "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f" + "\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + "\20\21\22\23\24\25\26\27\28\29\2a\2b\2c\2d\2e\2f" + "\30\31\32\33\34\35\36\37\38\39\3a\3b\3c\3d" + ) + "unknown memory 1" +) + +;; Data segment with memory index 1 and specially crafted vec(byte) after. +;; This is to detect incorrect validation where memory index is interpreted +;; as a flag followed by "\41" interpreted as the size of vec(byte) +;; with the expected number of bytes following. +(assert_invalid + (module binary + "\00asm" "\01\00\00\00" + "\0b\45\01" ;; data section + "\02" ;; active segment + "\01" ;; memory index + "\41\00\0b" ;; offset constant expression + "\3e" ;; vec(byte) length + "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f" + "\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f" + "\20\21\22\23\24\25\26\27\28\29\2a\2b\2c\2d\2e\2f" + "\30\31\32\33\34\35\36\37\38\39\3a\3b\3c\3d" + ) + "unknown memory 1" +) + + +;; Invalid offsets + +(assert_invalid + (module + (memory 1) + (data (i64.const 0)) + ) + "type mismatch" +) + +(assert_invalid + (module + (memory 1) + (data (offset (;empty instruction sequence;))) + ) + "type mismatch" +) + +(assert_invalid + (module + (memory 1) + (data (offset (i32.const 0) (i32.const 0))) + ) + "type mismatch" +) + +(assert_invalid + (module + (global (import "test" "global-i32") i32) + (memory 1) + (data (offset (global.get 0) (global.get 0))) + ) + "type mismatch" +) + +(assert_invalid + (module + (global (import "test" "global-i32") i32) + (memory 1) + (data (offset (global.get 0) (i32.const 0))) + ) + "type mismatch" +) + +(assert_invalid + (module + (memory 1) + (data (i32.ctz (i32.const 0))) + ) + "constant expression required" +) + +(assert_invalid + (module + (memory 1) + (data (nop)) + ) + "constant expression required" +) + +(assert_invalid + (module + (memory 1) + (data (offset (nop) (i32.const 0))) + ) + "constant expression required" +) + +(assert_invalid + (module + (memory 1) + (data (offset (i32.const 0) (nop))) + ) + "constant expression required" +) + +;; Use of internal globals in constant expressions is not allowed in MVP. +;; (assert_invalid +;; (module (memory 1) (data (global.get $g)) (global $g (mut i32) (i32.const 0))) +;; "constant expression required" +;; ) + +(assert_invalid + (module + (memory 1) + (data (global.get 0)) + ) + "unknown global 0" +) + +(assert_invalid + (module + (global (import "test" "global-i32") i32) + (memory 1) + (data (global.get 1)) + ) + "unknown global 1" +) + +(assert_invalid + (module + (global (import "test" "global-mut-i32") (mut i32)) + (memory 1) + (data (global.get 0)) + ) + "constant expression required" +) diff --git a/runtime/unc-vm/tests/wast/spec/elem.wast b/runtime/unc-vm/tests/wast/spec/elem.wast new file mode 100644 index 000000000..19b5fe920 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/elem.wast @@ -0,0 +1,529 @@ +;; Test the element section + +;; Syntax +(module + (table $t 10 funcref) + (func $f) + (func $g) + + ;; Passive + (elem funcref) + (elem funcref (ref.func $f) (item ref.func $f) (item (ref.null func)) (ref.func $g)) + (elem func) + (elem func $f $f $g $g) + + (elem $p1 funcref) + (elem $p2 funcref (ref.func $f) (ref.func $f) (ref.null func) (ref.func $g)) + (elem $p3 func) + (elem $p4 func $f $f $g $g) + + ;; Active + (elem (table $t) (i32.const 0) funcref) + (elem (table $t) (i32.const 0) funcref (ref.func $f) (ref.null func)) + (elem (table $t) (i32.const 0) func) + (elem (table $t) (i32.const 0) func $f $g) + (elem (table $t) (offset (i32.const 0)) funcref) + (elem (table $t) (offset (i32.const 0)) func $f $g) + (elem (table 0) (i32.const 0) func) + (elem (table 0x0) (i32.const 0) func $f $f) + (elem (table 0x000) (offset (i32.const 0)) func) + (elem (table 0) (offset (i32.const 0)) func $f $f) + (elem (table $t) (i32.const 0) func) + (elem (table $t) (i32.const 0) func $f $f) + (elem (table $t) (offset (i32.const 0)) func) + (elem (table $t) (offset (i32.const 0)) func $f $f) + (elem (offset (i32.const 0))) + (elem (offset (i32.const 0)) funcref (ref.func $f) (ref.null func)) + (elem (offset (i32.const 0)) func $f $f) + (elem (offset (i32.const 0)) $f $f) + (elem (i32.const 0)) + (elem (i32.const 0) funcref (ref.func $f) (ref.null func)) + (elem (i32.const 0) func $f $f) + (elem (i32.const 0) $f $f) + + (elem $a1 (table $t) (i32.const 0) funcref) + (elem $a2 (table $t) (i32.const 0) funcref (ref.func $f) (ref.null func)) + (elem $a3 (table $t) (i32.const 0) func) + (elem $a4 (table $t) (i32.const 0) func $f $g) + (elem $a9 (table $t) (offset (i32.const 0)) funcref) + (elem $a10 (table $t) (offset (i32.const 0)) func $f $g) + (elem $a11 (table 0) (i32.const 0) func) + (elem $a12 (table 0x0) (i32.const 0) func $f $f) + (elem $a13 (table 0x000) (offset (i32.const 0)) func) + (elem $a14 (table 0) (offset (i32.const 0)) func $f $f) + (elem $a15 (table $t) (i32.const 0) func) + (elem $a16 (table $t) (i32.const 0) func $f $f) + (elem $a17 (table $t) (offset (i32.const 0)) func) + (elem $a18 (table $t) (offset (i32.const 0)) func $f $f) + (elem $a19 (offset (i32.const 0))) + (elem $a20 (offset (i32.const 0)) funcref (ref.func $f) (ref.null func)) + (elem $a21 (offset (i32.const 0)) func $f $f) + (elem $a22 (offset (i32.const 0)) $f $f) + (elem $a23 (i32.const 0)) + (elem $a24 (i32.const 0) funcref (ref.func $f) (ref.null func)) + (elem $a25 (i32.const 0) func $f $f) + (elem $a26 (i32.const 0) $f $f) + + ;; Declarative + (elem declare funcref) + (elem declare funcref (ref.func $f) (ref.func $f) (ref.null func) (ref.func $g)) + (elem declare func) + (elem declare func $f $f $g $g) + + (elem $d1 declare funcref) + (elem $d2 declare funcref (ref.func $f) (ref.func $f) (ref.null func) (ref.func $g)) + (elem $d3 declare func) + (elem $d4 declare func $f $f $g $g) +) + +(module + (func $f) + (func $g) + + (table $t funcref (elem (ref.func $f) (ref.null func) (ref.func $g))) +) + + +;; Basic use + +(module + (table 10 funcref) + (func $f) + (elem (i32.const 0) $f) +) +(module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 0) $f) +) + +(module + (table 10 funcref) + (func $f) + (elem (i32.const 0) $f) + (elem (i32.const 3) $f) + (elem (i32.const 7) $f) + (elem (i32.const 5) $f) + (elem (i32.const 3) $f) +) +(module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 9) $f) + (elem (i32.const 3) $f) + (elem (i32.const 7) $f) + (elem (i32.const 3) $f) + (elem (i32.const 5) $f) +) + +(module + (global (import "spectest" "global_i32") i32) + (table 1000 funcref) + (func $f) + (elem (global.get 0) $f) +) + +(module + (global $g (import "spectest" "global_i32") i32) + (table 1000 funcref) + (func $f) + (elem (global.get $g) $f) +) + +(module + (type $out-i32 (func (result i32))) + (table 10 funcref) + (elem (i32.const 7) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-7") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 7)) + ) + (func (export "call-9") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) +(assert_return (invoke "call-7") (i32.const 65)) +(assert_return (invoke "call-9") (i32.const 66)) + +;; Corner cases + +(module + (table 10 funcref) + (func $f) + (elem (i32.const 9) $f) +) +(module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 9) $f) +) + +(module + (table 0 funcref) + (elem (i32.const 0)) +) +(module + (import "spectest" "table" (table 0 funcref)) + (elem (i32.const 0)) +) + +(module + (table 0 0 funcref) + (elem (i32.const 0)) +) + +(module + (table 20 funcref) + (elem (i32.const 20)) +) + +(module + (import "spectest" "table" (table 0 funcref)) + (func $f) + (elem (i32.const 0) $f) +) + +(module + (import "spectest" "table" (table 0 100 funcref)) + (func $f) + (elem (i32.const 0) $f) +) + +(module + (import "spectest" "table" (table 0 funcref)) + (func $f) + (elem (i32.const 1) $f) +) + +(module + (import "spectest" "table" (table 0 30 funcref)) + (func $f) + (elem (i32.const 1) $f) +) + +;; Invalid bounds for elements + +(assert_trap + (module + (table 0 funcref) + (func $f) + (elem (i32.const 0) $f) + ) + "out of bounds table access" +) + +(assert_trap + (module + (table 0 0 funcref) + (func $f) + (elem (i32.const 0) $f) + ) + "out of bounds table access" +) + +(assert_trap + (module + (table 0 1 funcref) + (func $f) + (elem (i32.const 0) $f) + ) + "out of bounds table access" +) + +(assert_trap + (module + (table 0 funcref) + (elem (i32.const 1)) + ) + "out of bounds table access" +) +(assert_trap + (module + (table 10 funcref) + (func $f) + (elem (i32.const 10) $f) + ) + "out of bounds table access" +) +(assert_trap + (module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 10) $f) + ) + "out of bounds table access" +) + +(assert_trap + (module + (table 10 20 funcref) + (func $f) + (elem (i32.const 10) $f) + ) + "out of bounds table access" +) +(assert_trap + (module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 10) $f) + ) + "out of bounds table access" +) + +(assert_trap + (module + (table 10 funcref) + (func $f) + (elem (i32.const -1) $f) + ) + "out of bounds table access" +) +(assert_trap + (module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const -1) $f) + ) + "out of bounds table access" +) + +(assert_trap + (module + (table 10 funcref) + (func $f) + (elem (i32.const -10) $f) + ) + "out of bounds table access" +) +(assert_trap + (module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const -10) $f) + ) + "out of bounds table access" +) + +;; Implicitly dropped elements + +(module + (table 10 funcref) + (elem $e (i32.const 0) func $f) + (func $f) + (func (export "init") + (table.init $e (i32.const 0) (i32.const 0) (i32.const 1)) + ) +) +(assert_trap (invoke "init") "out of bounds table access") + +(module + (table 10 funcref) + (elem $e declare func $f) + (func $f) + (func (export "init") + (table.init $e (i32.const 0) (i32.const 0) (i32.const 1)) + ) +) +(assert_trap (invoke "init") "out of bounds table access") + +;; Element without table + +(assert_invalid + (module + (func $f) + (elem (i32.const 0) $f) + ) + "unknown table" +) + +;; Invalid offsets + +(assert_invalid + (module + (table 1 funcref) + (elem (i64.const 0)) + ) + "type mismatch" +) + +(assert_invalid + (module + (table 1 funcref) + (elem (offset (;empty instruction sequence;))) + ) + "type mismatch" +) + +(assert_invalid + (module + (table 1 funcref) + (elem (offset (i32.const 0) (i32.const 0))) + ) + "type mismatch" +) + +(assert_invalid + (module + (global (import "test" "global-i32") i32) + (table 1 funcref) + (elem (offset (global.get 0) (global.get 0))) + ) + "type mismatch" +) + +(assert_invalid + (module + (global (import "test" "global-i32") i32) + (table 1 funcref) + (elem (offset (global.get 0) (i32.const 0))) + ) + "type mismatch" +) + + +(assert_invalid + (module + (table 1 funcref) + (elem (i32.ctz (i32.const 0))) + ) + "constant expression required" +) + +(assert_invalid + (module + (table 1 funcref) + (elem (nop)) + ) + "constant expression required" +) + +(assert_invalid + (module + (table 1 funcref) + (elem (offset (nop) (i32.const 0))) + ) + "constant expression required" +) + +(assert_invalid + (module + (table 1 funcref) + (elem (offset (i32.const 0) (nop))) + ) + "constant expression required" +) + +;; Use of internal globals in constant expressions is not allowed in MVP. +;; (assert_invalid +;; (module (memory 1) (data (global.get $g)) (global $g (mut i32) (i32.const 0))) +;; "constant expression required" +;; ) + +(assert_invalid + (module + (table 1 funcref) + (elem (global.get 0)) + ) + "unknown global 0" +) + +(assert_invalid + (module + (global (import "test" "global-i32") i32) + (table 1 funcref) + (elem (global.get 1)) + ) + "unknown global 1" +) + +(assert_invalid + (module + (global (import "test" "global-mut-i32") (mut i32)) + (table 1 funcref) + (elem (global.get 0)) + ) + "constant expression required" +) + +;; Two elements target the same slot + +(module + (type $out-i32 (func (result i32))) + (table 10 funcref) + (elem (i32.const 9) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-overwritten") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) +(assert_return (invoke "call-overwritten") (i32.const 66)) + +(module + (type $out-i32 (func (result i32))) + (import "spectest" "table" (table 10 funcref)) + (elem (i32.const 9) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-overwritten-element") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) +(assert_return (invoke "call-overwritten-element") (i32.const 66)) + +;; Element sections across multiple modules change the same table + +(module $module1 + (type $out-i32 (func (result i32))) + (table (export "shared-table") 10 funcref) + (elem (i32.const 8) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-7") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 7)) + ) + (func (export "call-8") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 8)) + ) + (func (export "call-9") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) + +(register "module1" $module1) + +(assert_trap (invoke $module1 "call-7") "uninitialized element") +(assert_return (invoke $module1 "call-8") (i32.const 65)) +(assert_return (invoke $module1 "call-9") (i32.const 66)) + +(module $module2 + (type $out-i32 (func (result i32))) + (import "module1" "shared-table" (table 10 funcref)) + (elem (i32.const 7) $const-i32-c) + (elem (i32.const 8) $const-i32-d) + (func $const-i32-c (type $out-i32) (i32.const 67)) + (func $const-i32-d (type $out-i32) (i32.const 68)) +) + +(assert_return (invoke $module1 "call-7") (i32.const 67)) +(assert_return (invoke $module1 "call-8") (i32.const 68)) +(assert_return (invoke $module1 "call-9") (i32.const 66)) + +(module $module3 + (type $out-i32 (func (result i32))) + (import "module1" "shared-table" (table 10 funcref)) + (elem (i32.const 8) $const-i32-e) + (elem (i32.const 9) $const-i32-f) + (func $const-i32-e (type $out-i32) (i32.const 69)) + (func $const-i32-f (type $out-i32) (i32.const 70)) +) + +(assert_return (invoke $module1 "call-7") (i32.const 67)) +(assert_return (invoke $module1 "call-8") (i32.const 69)) +(assert_return (invoke $module1 "call-9") (i32.const 70)) diff --git a/runtime/unc-vm/tests/wast/spec/endianness.wast b/runtime/unc-vm/tests/wast/spec/endianness.wast new file mode 100644 index 000000000..4f28a168f --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/endianness.wast @@ -0,0 +1,217 @@ +(module + (memory 1) + + ;; Stores an i16 value in little-endian-format + (func $i16_store_little (param $address i32) (param $value i32) + (i32.store8 (local.get $address) (local.get $value)) + (i32.store8 (i32.add (local.get $address) (i32.const 1)) (i32.shr_u (local.get $value) (i32.const 8))) + ) + + ;; Stores an i32 value in little-endian format + (func $i32_store_little (param $address i32) (param $value i32) + (call $i16_store_little (local.get $address) (local.get $value)) + (call $i16_store_little (i32.add (local.get $address) (i32.const 2)) (i32.shr_u (local.get $value) (i32.const 16))) + ) + + ;; Stores an i64 value in little-endian format + (func $i64_store_little (param $address i32) (param $value i64) + (call $i32_store_little (local.get $address) (i32.wrap_i64 (local.get $value))) + (call $i32_store_little (i32.add (local.get $address) (i32.const 4)) (i32.wrap_i64 (i64.shr_u (local.get $value) (i64.const 32)))) + ) + + ;; Loads an i16 value in little-endian format + (func $i16_load_little (param $address i32) (result i32) + (i32.or + (i32.load8_u (local.get $address)) + (i32.shl (i32.load8_u (i32.add (local.get $address) (i32.const 1))) (i32.const 8)) + ) + ) + + ;; Loads an i32 value in little-endian format + (func $i32_load_little (param $address i32) (result i32) + (i32.or + (call $i16_load_little (local.get $address)) + (i32.shl (call $i16_load_little (i32.add (local.get $address) (i32.const 2))) (i32.const 16)) + ) + ) + + ;; Loads an i64 value in little-endian format + (func $i64_load_little (param $address i32) (result i64) + (i64.or + (i64.extend_i32_u (call $i32_load_little (local.get $address))) + (i64.shl (i64.extend_i32_u (call $i32_load_little (i32.add (local.get $address) (i32.const 4)))) (i64.const 32)) + ) + ) + + (func (export "i32_load16_s") (param $value i32) (result i32) + (call $i16_store_little (i32.const 0) (local.get $value)) + (i32.load16_s (i32.const 0)) + ) + + (func (export "i32_load16_u") (param $value i32) (result i32) + (call $i16_store_little (i32.const 0) (local.get $value)) + (i32.load16_u (i32.const 0)) + ) + + (func (export "i32_load") (param $value i32) (result i32) + (call $i32_store_little (i32.const 0) (local.get $value)) + (i32.load (i32.const 0)) + ) + + (func (export "i64_load16_s") (param $value i64) (result i64) + (call $i16_store_little (i32.const 0) (i32.wrap_i64 (local.get $value))) + (i64.load16_s (i32.const 0)) + ) + + (func (export "i64_load16_u") (param $value i64) (result i64) + (call $i16_store_little (i32.const 0) (i32.wrap_i64 (local.get $value))) + (i64.load16_u (i32.const 0)) + ) + + (func (export "i64_load32_s") (param $value i64) (result i64) + (call $i32_store_little (i32.const 0) (i32.wrap_i64 (local.get $value))) + (i64.load32_s (i32.const 0)) + ) + + (func (export "i64_load32_u") (param $value i64) (result i64) + (call $i32_store_little (i32.const 0) (i32.wrap_i64 (local.get $value))) + (i64.load32_u (i32.const 0)) + ) + + (func (export "i64_load") (param $value i64) (result i64) + (call $i64_store_little (i32.const 0) (local.get $value)) + (i64.load (i32.const 0)) + ) + + (func (export "f32_load") (param $value f32) (result f32) + (call $i32_store_little (i32.const 0) (i32.reinterpret_f32 (local.get $value))) + (f32.load (i32.const 0)) + ) + + (func (export "f64_load") (param $value f64) (result f64) + (call $i64_store_little (i32.const 0) (i64.reinterpret_f64 (local.get $value))) + (f64.load (i32.const 0)) + ) + + + (func (export "i32_store16") (param $value i32) (result i32) + (i32.store16 (i32.const 0) (local.get $value)) + (call $i16_load_little (i32.const 0)) + ) + + (func (export "i32_store") (param $value i32) (result i32) + (i32.store (i32.const 0) (local.get $value)) + (call $i32_load_little (i32.const 0)) + ) + + (func (export "i64_store16") (param $value i64) (result i64) + (i64.store16 (i32.const 0) (local.get $value)) + (i64.extend_i32_u (call $i16_load_little (i32.const 0))) + ) + + (func (export "i64_store32") (param $value i64) (result i64) + (i64.store32 (i32.const 0) (local.get $value)) + (i64.extend_i32_u (call $i32_load_little (i32.const 0))) + ) + + (func (export "i64_store") (param $value i64) (result i64) + (i64.store (i32.const 0) (local.get $value)) + (call $i64_load_little (i32.const 0)) + ) + + (func (export "f32_store") (param $value f32) (result f32) + (f32.store (i32.const 0) (local.get $value)) + (f32.reinterpret_i32 (call $i32_load_little (i32.const 0))) + ) + + (func (export "f64_store") (param $value f64) (result f64) + (f64.store (i32.const 0) (local.get $value)) + (f64.reinterpret_i64 (call $i64_load_little (i32.const 0))) + ) +) + +(assert_return (invoke "i32_load16_s" (i32.const -1)) (i32.const -1)) +(assert_return (invoke "i32_load16_s" (i32.const -4242)) (i32.const -4242)) +(assert_return (invoke "i32_load16_s" (i32.const 42)) (i32.const 42)) +(assert_return (invoke "i32_load16_s" (i32.const 0x3210)) (i32.const 0x3210)) + +(assert_return (invoke "i32_load16_u" (i32.const -1)) (i32.const 0xFFFF)) +(assert_return (invoke "i32_load16_u" (i32.const -4242)) (i32.const 61294)) +(assert_return (invoke "i32_load16_u" (i32.const 42)) (i32.const 42)) +(assert_return (invoke "i32_load16_u" (i32.const 0xCAFE)) (i32.const 0xCAFE)) + +(assert_return (invoke "i32_load" (i32.const -1)) (i32.const -1)) +(assert_return (invoke "i32_load" (i32.const -42424242)) (i32.const -42424242)) +(assert_return (invoke "i32_load" (i32.const 42424242)) (i32.const 42424242)) +(assert_return (invoke "i32_load" (i32.const 0xABAD1DEA)) (i32.const 0xABAD1DEA)) + +(assert_return (invoke "i64_load16_s" (i64.const -1)) (i64.const -1)) +(assert_return (invoke "i64_load16_s" (i64.const -4242)) (i64.const -4242)) +(assert_return (invoke "i64_load16_s" (i64.const 42)) (i64.const 42)) +(assert_return (invoke "i64_load16_s" (i64.const 0x3210)) (i64.const 0x3210)) + +(assert_return (invoke "i64_load16_u" (i64.const -1)) (i64.const 0xFFFF)) +(assert_return (invoke "i64_load16_u" (i64.const -4242)) (i64.const 61294)) +(assert_return (invoke "i64_load16_u" (i64.const 42)) (i64.const 42)) +(assert_return (invoke "i64_load16_u" (i64.const 0xCAFE)) (i64.const 0xCAFE)) + +(assert_return (invoke "i64_load32_s" (i64.const -1)) (i64.const -1)) +(assert_return (invoke "i64_load32_s" (i64.const -42424242)) (i64.const -42424242)) +(assert_return (invoke "i64_load32_s" (i64.const 42424242)) (i64.const 42424242)) +(assert_return (invoke "i64_load32_s" (i64.const 0x12345678)) (i64.const 0x12345678)) + +(assert_return (invoke "i64_load32_u" (i64.const -1)) (i64.const 0xFFFFFFFF)) +(assert_return (invoke "i64_load32_u" (i64.const -42424242)) (i64.const 4252543054)) +(assert_return (invoke "i64_load32_u" (i64.const 42424242)) (i64.const 42424242)) +(assert_return (invoke "i64_load32_u" (i64.const 0xABAD1DEA)) (i64.const 0xABAD1DEA)) + +(assert_return (invoke "i64_load" (i64.const -1)) (i64.const -1)) +(assert_return (invoke "i64_load" (i64.const -42424242)) (i64.const -42424242)) +(assert_return (invoke "i64_load" (i64.const 0xABAD1DEA)) (i64.const 0xABAD1DEA)) +(assert_return (invoke "i64_load" (i64.const 0xABADCAFEDEAD1DEA)) (i64.const 0xABADCAFEDEAD1DEA)) + +(assert_return (invoke "f32_load" (f32.const -1)) (f32.const -1)) +(assert_return (invoke "f32_load" (f32.const 1234e-5)) (f32.const 1234e-5)) +(assert_return (invoke "f32_load" (f32.const 4242.4242)) (f32.const 4242.4242)) +(assert_return (invoke "f32_load" (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) + +(assert_return (invoke "f64_load" (f64.const -1)) (f64.const -1)) +(assert_return (invoke "f64_load" (f64.const 123456789e-5)) (f64.const 123456789e-5)) +(assert_return (invoke "f64_load" (f64.const 424242.424242)) (f64.const 424242.424242)) +(assert_return (invoke "f64_load" (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) + + +(assert_return (invoke "i32_store16" (i32.const -1)) (i32.const 0xFFFF)) +(assert_return (invoke "i32_store16" (i32.const -4242)) (i32.const 61294)) +(assert_return (invoke "i32_store16" (i32.const 42)) (i32.const 42)) +(assert_return (invoke "i32_store16" (i32.const 0xCAFE)) (i32.const 0xCAFE)) + +(assert_return (invoke "i32_store" (i32.const -1)) (i32.const -1)) +(assert_return (invoke "i32_store" (i32.const -4242)) (i32.const -4242)) +(assert_return (invoke "i32_store" (i32.const 42424242)) (i32.const 42424242)) +(assert_return (invoke "i32_store" (i32.const 0xDEADCAFE)) (i32.const 0xDEADCAFE)) + +(assert_return (invoke "i64_store16" (i64.const -1)) (i64.const 0xFFFF)) +(assert_return (invoke "i64_store16" (i64.const -4242)) (i64.const 61294)) +(assert_return (invoke "i64_store16" (i64.const 42)) (i64.const 42)) +(assert_return (invoke "i64_store16" (i64.const 0xCAFE)) (i64.const 0xCAFE)) + +(assert_return (invoke "i64_store32" (i64.const -1)) (i64.const 0xFFFFFFFF)) +(assert_return (invoke "i64_store32" (i64.const -4242)) (i64.const 4294963054)) +(assert_return (invoke "i64_store32" (i64.const 42424242)) (i64.const 42424242)) +(assert_return (invoke "i64_store32" (i64.const 0xDEADCAFE)) (i64.const 0xDEADCAFE)) + +(assert_return (invoke "i64_store" (i64.const -1)) (i64.const -1)) +(assert_return (invoke "i64_store" (i64.const -42424242)) (i64.const -42424242)) +(assert_return (invoke "i64_store" (i64.const 0xABAD1DEA)) (i64.const 0xABAD1DEA)) +(assert_return (invoke "i64_store" (i64.const 0xABADCAFEDEAD1DEA)) (i64.const 0xABADCAFEDEAD1DEA)) + +(assert_return (invoke "f32_store" (f32.const -1)) (f32.const -1)) +(assert_return (invoke "f32_store" (f32.const 1234e-5)) (f32.const 1234e-5)) +(assert_return (invoke "f32_store" (f32.const 4242.4242)) (f32.const 4242.4242)) +(assert_return (invoke "f32_store" (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) + +(assert_return (invoke "f64_store" (f64.const -1)) (f64.const -1)) +(assert_return (invoke "f64_store" (f64.const 123456789e-5)) (f64.const 123456789e-5)) +(assert_return (invoke "f64_store" (f64.const 424242.424242)) (f64.const 424242.424242)) +(assert_return (invoke "f64_store" (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) diff --git a/runtime/unc-vm/tests/wast/spec/exports.wast b/runtime/unc-vm/tests/wast/spec/exports.wast new file mode 100644 index 000000000..80d3ae074 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/exports.wast @@ -0,0 +1,238 @@ +;; Functions + +(module (func) (export "a" (func 0))) +(module (func) (export "a" (func 0)) (export "b" (func 0))) +(module (func) (func) (export "a" (func 0)) (export "b" (func 1))) + +(module (func (export "a"))) +(module (func (export "a") (export "b") (export "c"))) +(module (func (export "a") (export "b") (param i32))) +(module (func) (export "a" (func 0))) +(module (func $a (export "a"))) +(module (func $a) (export "a" (func $a))) +(module (export "a" (func 0)) (func)) +(module (export "a" (func $a)) (func $a)) + +(module $Func + (export "e" (func $f)) + (func $f (param $n i32) (result i32) + (return (i32.add (local.get $n) (i32.const 1))) + ) +) +(assert_return (invoke "e" (i32.const 42)) (i32.const 43)) +(assert_return (invoke $Func "e" (i32.const 42)) (i32.const 43)) +(module) +(module $Other1) +(assert_return (invoke $Func "e" (i32.const 42)) (i32.const 43)) + +(module + (type (;0;) (func (result i32))) + (func (;0;) (type 0) (result i32) i32.const 42) + (export "a" (func 0)) + (export "b" (func 0)) + (export "c" (func 0))) +(assert_return (invoke "a") (i32.const 42)) +(assert_return (invoke "b") (i32.const 42)) +(assert_return (invoke "c") (i32.const 42)) + +(assert_invalid + (module (export "a" (func 0))) + "unknown function" +) +(assert_invalid + (module (func) (export "a" (func 1))) + "unknown function" +) +(assert_invalid + (module (import "spectest" "print_i32" (func (param i32))) (export "a" (func 1))) + "unknown function" +) +(assert_invalid + (module (func) (export "a" (func 0)) (export "a" (func 0))) + "duplicate export name" +) +(assert_invalid + (module (func) (func) (export "a" (func 0)) (export "a" (func 1))) + "duplicate export name" +) +(assert_invalid + (module (func) (global i32 (i32.const 0)) (export "a" (func 0)) (export "a" (global 0))) + "duplicate export name" +) +(assert_invalid + (module (func) (table 0 funcref) (export "a" (func 0)) (export "a" (table 0))) + "duplicate export name" +) +(assert_invalid + (module (func) (memory 0) (export "a" (func 0)) (export "a" (memory 0))) + "duplicate export name" +) + + +;; Globals + +(module (global i32 (i32.const 0)) (export "a" (global 0))) +(module (global i32 (i32.const 0)) (export "a" (global 0)) (export "b" (global 0))) +(module (global i32 (i32.const 0)) (global i32 (i32.const 0)) (export "a" (global 0)) (export "b" (global 1))) + +(module (global (export "a") i32 (i32.const 0))) +(module (global i32 (i32.const 0)) (export "a" (global 0))) +(module (global $a (export "a") i32 (i32.const 0))) +(module (global $a i32 (i32.const 0)) (export "a" (global $a))) +(module (export "a" (global 0)) (global i32 (i32.const 0))) +(module (export "a" (global $a)) (global $a i32 (i32.const 0))) + +(module $Global + (export "e" (global $g)) + (global $g i32 (i32.const 42)) +) +(assert_return (get "e") (i32.const 42)) +(assert_return (get $Global "e") (i32.const 42)) +(module) +(module $Other2) +(assert_return (get $Global "e") (i32.const 42)) + +(assert_invalid + (module (export "a" (global 0))) + "unknown global" +) +(assert_invalid + (module (global i32 (i32.const 0)) (export "a" (global 1))) + "unknown global" +) +(assert_invalid + (module (import "spectest" "global_i32" (global i32)) (export "a" (global 1))) + "unknown global" +) +(assert_invalid + (module (global i32 (i32.const 0)) (export "a" (global 0)) (export "a" (global 0))) + "duplicate export name" +) +(assert_invalid + (module (global i32 (i32.const 0)) (global i32 (i32.const 0)) (export "a" (global 0)) (export "a" (global 1))) + "duplicate export name" +) +(assert_invalid + (module (global i32 (i32.const 0)) (func) (export "a" (global 0)) (export "a" (func 0))) + "duplicate export name" +) +(assert_invalid + (module (global i32 (i32.const 0)) (table 0 funcref) (export "a" (global 0)) (export "a" (table 0))) + "duplicate export name" +) +(assert_invalid + (module (global i32 (i32.const 0)) (memory 0) (export "a" (global 0)) (export "a" (memory 0))) + "duplicate export name" +) + + +;; Tables + +(module (table 0 funcref) (export "a" (table 0))) +(module (table 0 funcref) (export "a" (table 0)) (export "b" (table 0))) +(module (table 0 funcref) (table 0 funcref) (export "a" (table 0)) (export "b" (table 1))) + +(module (table (export "a") 0 funcref)) +(module (table (export "a") 0 1 funcref)) +(module (table 0 funcref) (export "a" (table 0))) +(module (table 0 1 funcref) (export "a" (table 0))) +(module (table $a (export "a") 0 funcref)) +(module (table $a (export "a") 0 1 funcref)) +(module (table $a 0 funcref) (export "a" (table $a))) +(module (table $a 0 1 funcref) (export "a" (table $a))) +(module (export "a" (table 0)) (table 0 funcref)) +(module (export "a" (table 0)) (table 0 1 funcref)) +(module (export "a" (table $a)) (table $a 0 funcref)) +(module (export "a" (table $a)) (table $a 0 1 funcref)) + +(; TODO: access table ;) + +(assert_invalid + (module (export "a" (table 0))) + "unknown table" +) +(assert_invalid + (module (table 0 funcref) (export "a" (table 1))) + "unknown table" +) +(assert_invalid + (module (import "spectest" "table" (table 10 20 funcref)) (export "a" (table 1))) + "unknown table" +) +(assert_invalid + (module (table 0 funcref) (export "a" (table 0)) (export "a" (table 0))) + "duplicate export name" +) +(assert_invalid + (module (table 0 funcref) (table 0 funcref) (export "a" (table 0)) (export "a" (table 1))) + "duplicate export name" +) +(assert_invalid + (module (table 0 funcref) (func) (export "a" (table 0)) (export "a" (func 0))) + "duplicate export name" +) +(assert_invalid + (module (table 0 funcref) (global i32 (i32.const 0)) (export "a" (table 0)) (export "a" (global 0))) + "duplicate export name" +) +(assert_invalid + (module (table 0 funcref) (memory 0) (export "a" (table 0)) (export "a" (memory 0))) + "duplicate export name" +) + + +;; Memories + +(module (memory 0) (export "a" (memory 0))) +(module (memory 0) (export "a" (memory 0)) (export "b" (memory 0))) +;; No multiple memories yet. +;; (module (memory 0) (memory 0) (export "a" (memory 0)) (export "b" (memory 1))) + +(module (memory (export "a") 0)) +(module (memory (export "a") 0 1)) +(module (memory 0) (export "a" (memory 0))) +(module (memory 0 1) (export "a" (memory 0))) +(module (memory $a (export "a") 0)) +(module (memory $a (export "a") 0 1)) +(module (memory $a 0) (export "a" (memory $a))) +(module (memory $a 0 1) (export "a" (memory $a))) +(module (export "a" (memory 0)) (memory 0)) +(module (export "a" (memory 0)) (memory 0 1)) +(module (export "a" (memory $a)) (memory $a 0)) +(module (export "a" (memory $a)) (memory $a 0 1)) + +(; TODO: access memory ;) + +(assert_invalid + (module (export "a" (memory 0))) + "unknown memory" +) +(assert_invalid + (module (memory 0) (export "a" (memory 1))) + "unknown memory" +) +(assert_invalid + (module (import "spectest" "memory" (memory 1 2)) (export "a" (memory 1))) + "unknown memory" +) +(assert_invalid + (module (memory 0) (export "a" (memory 0)) (export "a" (memory 0))) + "duplicate export name" +) +;; No multiple memories yet. +;; (assert_invalid +;; (module (memory 0) (memory 0) (export "a" (memory 0)) (export "a" (memory 1))) +;; "duplicate export name" +;; ) +(assert_invalid + (module (memory 0) (func) (export "a" (memory 0)) (export "a" (func 0))) + "duplicate export name" +) +(assert_invalid + (module (memory 0) (global i32 (i32.const 0)) (export "a" (memory 0)) (export "a" (global 0))) + "duplicate export name" +) +(assert_invalid + (module (memory 0) (table 0 funcref) (export "a" (memory 0)) (export "a" (table 0))) + "duplicate export name" +) diff --git a/runtime/unc-vm/tests/wast/spec/extract-parts.sh b/runtime/unc-vm/tests/wast/spec/extract-parts.sh new file mode 100755 index 000000000..6aac1d083 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/extract-parts.sh @@ -0,0 +1,35 @@ +#!/bin/bash +set -e +set -u +set -o pipefail + +# This script extracts the modules from the testsuite test files into +# individual files in the following directories: +# - valid - valid wasm modules +# - invalid - wasm modules that fail to validate +# - malformed - wasm text tests that fail to parse + +wabt="../wabt" +wabtbin="$wabt/bin" + +mkdir -p valid invalid malformed +rm -f valid/*.wasm +rm -f invalid/*.wasm +rm -f malformed/*.wat + +for wast in *.wast; do + base="${wast##*/}" + json="invalid/${base%.wast}.json" + "$wabtbin/wast2json" "$wast" -o "$json" + rm "$json" +done + +mv invalid/*.wat malformed + +for wasm in invalid/*.wasm; do + if "$wabtbin/wasm2wat" "$wasm" -o invalid/t.wat 2>/dev/null && \ + "$wabtbin/wat2wasm" invalid/t.wat -o /dev/null 2>/dev/null ; then + mv "$wasm" valid + fi +done +rm invalid/t.wat diff --git a/runtime/unc-vm/tests/wast/spec/f32.wast b/runtime/unc-vm/tests/wast/spec/f32.wast new file mode 100644 index 000000000..36d81a05b --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/f32.wast @@ -0,0 +1,2533 @@ +;; Test all the f32 operators on major boundary values and all special +;; values (except comparison and bitwise operators, which are tested in +;; f32_bitwise.wast and f32_cmp.wast). + +(module + (func (export "add") (param $x f32) (param $y f32) (result f32) (f32.add (local.get $x) (local.get $y))) + (func (export "sub") (param $x f32) (param $y f32) (result f32) (f32.sub (local.get $x) (local.get $y))) + (func (export "mul") (param $x f32) (param $y f32) (result f32) (f32.mul (local.get $x) (local.get $y))) + (func (export "div") (param $x f32) (param $y f32) (result f32) (f32.div (local.get $x) (local.get $y))) + (func (export "sqrt") (param $x f32) (result f32) (f32.sqrt (local.get $x))) + (func (export "min") (param $x f32) (param $y f32) (result f32) (f32.min (local.get $x) (local.get $y))) + (func (export "max") (param $x f32) (param $y f32) (result f32) (f32.max (local.get $x) (local.get $y))) + (func (export "ceil") (param $x f32) (result f32) (f32.ceil (local.get $x))) + (func (export "floor") (param $x f32) (result f32) (f32.floor (local.get $x))) + (func (export "trunc") (param $x f32) (result f32) (f32.trunc (local.get $x))) + (func (export "nearest") (param $x f32) (result f32) (f32.nearest (local.get $x))) +) + +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const -0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -0x0p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const 0x0p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const -0x0p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const 0x0p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const -0x0p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const 0x0p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const -0x1p-149)) (f32.const -0x1p-148)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const 0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const -0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const 0x1p-149)) (f32.const 0x1p-148)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const -0x1p-126)) (f32.const -0x1.000002p-126)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const 0x1p-126)) (f32.const 0x1.fffffcp-127)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const -0x1p-126)) (f32.const -0x1.fffffcp-127)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const 0x1p-126)) (f32.const 0x1.000002p-126)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -0x1p-149) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const 0x1p-149) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const -0x0p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const 0x0p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const -0x0p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const 0x0p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const -0x1p-149)) (f32.const -0x1.000002p-126)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const 0x1p-149)) (f32.const -0x1.fffffcp-127)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const -0x1p-149)) (f32.const 0x1.fffffcp-127)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const 0x1p-149)) (f32.const 0x1.000002p-126)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const -0x1p-126)) (f32.const -0x1p-125)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const 0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const -0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const 0x1p-126)) (f32.const 0x1p-125)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -0x1p-126) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const 0x1p-126) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const -0x0p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const 0x0p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const -0x0p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const 0x0p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const -0x1p-149)) (f32.const -0x1p-1)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const 0x1p-149)) (f32.const -0x1p-1)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const -0x1p-149)) (f32.const 0x1p-1)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const 0x1p-149)) (f32.const 0x1p-1)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const -0x1p-126)) (f32.const -0x1p-1)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const 0x1p-126)) (f32.const -0x1p-1)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const -0x1p-126)) (f32.const 0x1p-1)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const 0x1p-126)) (f32.const 0x1p-1)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const -0x1p-1)) (f32.const -0x1p+0)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const 0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const -0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const 0x1p-1)) (f32.const 0x1p+0)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const -0x1p+0)) (f32.const -0x1.8p+0)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const 0x1p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const -0x1p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const 0x1p+0)) (f32.const 0x1.8p+0)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.b21fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.721fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.721fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.b21fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -0x1p-1) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const 0x1p-1) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const -0x0p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const 0x0p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const -0x0p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const 0x0p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const -0x1p-149)) (f32.const -0x1p+0)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const 0x1p-149)) (f32.const -0x1p+0)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const -0x1p-149)) (f32.const 0x1p+0)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const 0x1p-149)) (f32.const 0x1p+0)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const -0x1p-126)) (f32.const -0x1p+0)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const 0x1p-126)) (f32.const -0x1p+0)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const -0x1p-126)) (f32.const 0x1p+0)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const 0x1p-126)) (f32.const 0x1p+0)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const -0x1p-1)) (f32.const -0x1.8p+0)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const 0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const -0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const 0x1p-1)) (f32.const 0x1.8p+0)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const -0x1p+0)) (f32.const -0x1p+1)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const 0x1p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const -0x1p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const 0x1p+0)) (f32.const 0x1p+1)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.d21fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.521fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.521fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.d21fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -0x1p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const 0x1p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const -0x0p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const 0x0p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const -0x0p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const 0x0p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-149)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-149)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-149)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-149)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-126)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-126)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-126)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-126)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-1)) (f32.const -0x1.b21fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-1)) (f32.const -0x1.721fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-1)) (f32.const 0x1.721fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-1)) (f32.const 0x1.b21fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const -0x1p+0)) (f32.const -0x1.d21fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const 0x1p+0)) (f32.const -0x1.521fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const -0x1p+0)) (f32.const 0x1.521fb6p+2)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const 0x1p+0)) (f32.const 0x1.d21fb6p+2)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+3)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (f32.const 0x0p+0)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (f32.const 0x0p+0)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+3)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -0x1.921fb6p+2) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const 0x1.921fb6p+2) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const -0x0p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const 0x0p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const -0x0p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const 0x0p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const -0x1p-149)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const 0x1p-149)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const -0x1p-149)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const 0x1p-149)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const -0x1p-126)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const 0x1p-126)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const -0x1p-126)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const 0x1p-126)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const -0x1p-1)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const 0x1p-1)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const -0x1p-1)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const 0x1p-1)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const -0x1p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const 0x1p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const -0x1p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const 0x1p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -0x1.fffffep+127) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const 0x1.fffffep+127) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -inf) (f32.const -0x0p+0)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const 0x0p+0)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const inf) (f32.const -0x0p+0)) (f32.const inf)) +(assert_return (invoke "add" (f32.const inf) (f32.const 0x0p+0)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const -0x1p-149)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const 0x1p-149)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const inf) (f32.const -0x1p-149)) (f32.const inf)) +(assert_return (invoke "add" (f32.const inf) (f32.const 0x1p-149)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const -0x1p-126)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const 0x1p-126)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const inf) (f32.const -0x1p-126)) (f32.const inf)) +(assert_return (invoke "add" (f32.const inf) (f32.const 0x1p-126)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const -0x1p-1)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const 0x1p-1)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const inf) (f32.const -0x1p-1)) (f32.const inf)) +(assert_return (invoke "add" (f32.const inf) (f32.const 0x1p-1)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const -0x1p+0)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const 0x1p+0)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const inf) (f32.const -0x1p+0)) (f32.const inf)) +(assert_return (invoke "add" (f32.const inf) (f32.const 0x1p+0)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const -0x1.921fb6p+2)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const 0x1.921fb6p+2)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const inf) (f32.const -0x1.921fb6p+2)) (f32.const inf)) +(assert_return (invoke "add" (f32.const inf) (f32.const 0x1.921fb6p+2)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const -0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const 0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const inf) (f32.const -0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "add" (f32.const inf) (f32.const 0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const inf) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const inf) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "add" (f32.const -inf) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -inf) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -inf) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -inf) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const inf) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const inf) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const inf) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const inf) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const -0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const 0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const -0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const 0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const -0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const -0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const 0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const 0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const -0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const -0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const 0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const 0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const -0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const -0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const 0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const 0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const -0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const -0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const 0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const 0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const -0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const -0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const 0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const 0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const -0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const -0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const 0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const 0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const -0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const -0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const 0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const 0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const -0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const -0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const 0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const 0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const -0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const -0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const 0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const 0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const -0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const -0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const 0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const 0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const -0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const -0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const 0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const 0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const -0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const -0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const 0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const 0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const -inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const -inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const -nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const -nan:0x200000) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const -nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "add" (f32.const nan:0x200000) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const -0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const 0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const -0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const -0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const 0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const -0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const 0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const -0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const 0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const -0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const 0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const -0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const 0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const -0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const 0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const -0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const 0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const -0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const 0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -0x0p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const 0x0p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const -0x0p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const 0x0p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const -0x0p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const 0x0p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const -0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const 0x1p-149)) (f32.const -0x1p-148)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const -0x1p-149)) (f32.const 0x1p-148)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const 0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const -0x1p-126)) (f32.const 0x1.fffffcp-127)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const 0x1p-126)) (f32.const -0x1.000002p-126)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const -0x1p-126)) (f32.const 0x1.000002p-126)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const 0x1p-126)) (f32.const -0x1.fffffcp-127)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const -0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const 0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const -0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const 0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const -0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const 0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const -0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const 0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -0x1p-149) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const 0x1p-149) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const -0x0p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const 0x0p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const -0x0p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const 0x0p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const -0x1p-149)) (f32.const -0x1.fffffcp-127)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const 0x1p-149)) (f32.const -0x1.000002p-126)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const -0x1p-149)) (f32.const 0x1.000002p-126)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const 0x1p-149)) (f32.const 0x1.fffffcp-127)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const -0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const 0x1p-126)) (f32.const -0x1p-125)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const -0x1p-126)) (f32.const 0x1p-125)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const 0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const -0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const 0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const -0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const 0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const -0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const 0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const -0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const 0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -0x1p-126) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const 0x1p-126) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const -0x0p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const 0x0p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const -0x0p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const 0x0p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const -0x1p-149)) (f32.const -0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const 0x1p-149)) (f32.const -0x1p-1)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const -0x1p-149)) (f32.const 0x1p-1)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const 0x1p-149)) (f32.const 0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const -0x1p-126)) (f32.const -0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const 0x1p-126)) (f32.const -0x1p-1)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const -0x1p-126)) (f32.const 0x1p-1)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const 0x1p-126)) (f32.const 0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const -0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const 0x1p-1)) (f32.const -0x1p+0)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const -0x1p-1)) (f32.const 0x1p+0)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const 0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const -0x1p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const 0x1p+0)) (f32.const -0x1.8p+0)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const -0x1p+0)) (f32.const 0x1.8p+0)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const 0x1p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.721fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.b21fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.b21fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.721fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -0x1p-1) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const 0x1p-1) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const -0x0p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const 0x0p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const -0x0p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const 0x0p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const -0x1p-149)) (f32.const -0x1p+0)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const 0x1p-149)) (f32.const -0x1p+0)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const -0x1p-149)) (f32.const 0x1p+0)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const 0x1p-149)) (f32.const 0x1p+0)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const -0x1p-126)) (f32.const -0x1p+0)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const 0x1p-126)) (f32.const -0x1p+0)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const -0x1p-126)) (f32.const 0x1p+0)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const 0x1p-126)) (f32.const 0x1p+0)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const 0x1p-1)) (f32.const -0x1.8p+0)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const -0x1p-1)) (f32.const 0x1.8p+0)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const -0x1p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const 0x1p+0)) (f32.const -0x1p+1)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const -0x1p+0)) (f32.const 0x1p+1)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const 0x1p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.521fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.d21fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.d21fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.521fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -0x1p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const 0x1p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const -0x0p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const 0x0p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const -0x0p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const 0x0p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-149)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-149)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-149)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-149)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-126)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-126)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-126)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-126)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-1)) (f32.const -0x1.721fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-1)) (f32.const -0x1.b21fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-1)) (f32.const 0x1.b21fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-1)) (f32.const 0x1.721fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const -0x1p+0)) (f32.const -0x1.521fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const 0x1p+0)) (f32.const -0x1.d21fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const -0x1p+0)) (f32.const 0x1.d21fb6p+2)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const 0x1p+0)) (f32.const 0x1.521fb6p+2)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (f32.const 0x0p+0)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.921fb6p+3)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.921fb6p+3)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (f32.const 0x0p+0)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -0x1.921fb6p+2) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const 0x1.921fb6p+2) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const -0x0p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const 0x0p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const -0x0p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const 0x0p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const -0x1p-149)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const 0x1p-149)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const -0x1p-149)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const 0x1p-149)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const -0x1p-126)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const 0x1p-126)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const -0x1p-126)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const 0x1p-126)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const -0x1p-1)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const 0x1p-1)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const -0x1p-1)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const 0x1p-1)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const -0x1p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const 0x1p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const -0x1p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const 0x1p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -0x1.fffffep+127) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const 0x1.fffffep+127) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const -0x0p+0)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const 0x0p+0)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const -0x0p+0)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const 0x0p+0)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const -0x1p-149)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const 0x1p-149)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const -0x1p-149)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const 0x1p-149)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const -0x1p-126)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const 0x1p-126)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const -0x1p-126)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const 0x1p-126)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const -0x1p-1)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const 0x1p-1)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const -0x1p-1)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const 0x1p-1)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const -0x1p+0)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const 0x1p+0)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const -0x1p+0)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const 0x1p+0)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const -0x1.921fb6p+2)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const 0x1.921fb6p+2)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const -0x1.921fb6p+2)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const 0x1.921fb6p+2)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const -0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const 0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const -0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const 0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "sub" (f32.const inf) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -inf) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const inf) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const inf) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const inf) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const inf) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const -0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const 0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const -0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const 0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const -0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const -0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const 0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const 0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const -0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const -0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const 0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const 0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const -0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const -0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const 0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const 0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const -0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const -0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const 0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const 0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const -0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const -0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const 0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const 0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const -0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const -0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const 0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const 0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const -0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const -0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const 0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const 0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const -0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const -0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const 0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const 0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const -0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const -0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const 0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const 0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const -0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const -0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const 0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const 0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const -0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const -0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const 0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const 0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const -0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const -0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const 0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const 0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const -inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const -inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const -nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const -nan:0x200000) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const -nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sub" (f32.const nan:0x200000) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const -0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const 0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const -0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const 0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const -0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const 0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const -0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const 0x1p-126)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const -0x1p-126)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const 0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const -0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const 0x1p-1)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const -0x1p-1)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const 0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const -0x1p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const 0x1p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const -0x1p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const 0x1p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const -0x1.921fb6p+2)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const 0x1.921fb6p+2)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const -0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const 0x1.fffffep+127)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x0p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x0p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const -0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const 0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const -0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const 0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const -0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const 0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const -0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const 0x1p-126)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const -0x1p-126)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const 0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const -0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const 0x1p-1)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const -0x1p-1)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const 0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const -0x1p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const 0x1p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const -0x1p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const 0x1p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.8p-147)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.8p-147)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.8p-147)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.8p-147)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep-22)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep-22)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep-22)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep-22)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x1p-149) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x1p-149) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const -0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const 0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const -0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const 0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const -0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const 0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const -0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const 0x1p-126)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const -0x1p-126)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const 0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const -0x1p-1)) (f32.const 0x1p-127)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const 0x1p-1)) (f32.const -0x1p-127)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const -0x1p-1)) (f32.const -0x1p-127)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const 0x1p-1)) (f32.const 0x1p-127)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const -0x1p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const 0x1p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const -0x1p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const 0x1p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.921fb6p-124)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.921fb6p-124)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p-124)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p-124)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+1)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+1)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+1)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+1)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x1p-126) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x1p-126) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const -0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const 0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const -0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const 0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const -0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const 0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const -0x1p-126)) (f32.const 0x1p-127)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const 0x1p-126)) (f32.const -0x1p-127)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const -0x1p-126)) (f32.const -0x1p-127)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const 0x1p-126)) (f32.const 0x1p-127)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const -0x1p-1)) (f32.const 0x1p-2)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const 0x1p-1)) (f32.const -0x1p-2)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const -0x1p-1)) (f32.const -0x1p-2)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const 0x1p-1)) (f32.const 0x1p-2)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const -0x1p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const 0x1p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const -0x1p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const 0x1p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.921fb6p+1)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.921fb6p+1)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+1)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+1)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+126)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+126)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+126)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+126)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x1p-1) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x1p-1) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const -0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const 0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const -0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const 0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const -0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const 0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const -0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const 0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const -0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const 0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x1p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x1p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const -0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const 0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-149)) (f32.const 0x1.8p-147)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-149)) (f32.const -0x1.8p-147)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-149)) (f32.const -0x1.8p-147)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-149)) (f32.const 0x1.8p-147)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-126)) (f32.const 0x1.921fb6p-124)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-126)) (f32.const -0x1.921fb6p-124)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-126)) (f32.const -0x1.921fb6p-124)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-126)) (f32.const 0x1.921fb6p-124)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-1)) (f32.const 0x1.921fb6p+1)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-1)) (f32.const -0x1.921fb6p+1)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-1)) (f32.const -0x1.921fb6p+1)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-1)) (f32.const 0x1.921fb6p+1)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const -0x1p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const 0x1p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const -0x1p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const 0x1p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.3bd3cep+5)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.3bd3cep+5)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.3bd3cep+5)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.3bd3cep+5)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x1.921fb6p+2) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x1.921fb6p+2) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const -0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const 0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const -0x1p-149)) (f32.const 0x1.fffffep-22)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const 0x1p-149)) (f32.const -0x1.fffffep-22)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const -0x1p-149)) (f32.const -0x1.fffffep-22)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const 0x1p-149)) (f32.const 0x1.fffffep-22)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const -0x1p-126)) (f32.const 0x1.fffffep+1)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const 0x1p-126)) (f32.const -0x1.fffffep+1)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const -0x1p-126)) (f32.const -0x1.fffffep+1)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const 0x1p-126)) (f32.const 0x1.fffffep+1)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const -0x1p-1)) (f32.const 0x1.fffffep+126)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const 0x1p-1)) (f32.const -0x1.fffffep+126)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const -0x1p-1)) (f32.const -0x1.fffffep+126)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const 0x1p-1)) (f32.const 0x1.fffffep+126)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const -0x1p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const 0x1p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const -0x1p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const 0x1p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -0x1.fffffep+127) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const 0x1.fffffep+127) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const inf) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const inf) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const -0x1p-149)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const 0x1p-149)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const inf) (f32.const -0x1p-149)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const inf) (f32.const 0x1p-149)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const -0x1p-126)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const 0x1p-126)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const inf) (f32.const -0x1p-126)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const inf) (f32.const 0x1p-126)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const -0x1p-1)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const 0x1p-1)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const inf) (f32.const -0x1p-1)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const inf) (f32.const 0x1p-1)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const -0x1p+0)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const 0x1p+0)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const inf) (f32.const -0x1p+0)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const inf) (f32.const 0x1p+0)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const -0x1.921fb6p+2)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const 0x1.921fb6p+2)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const inf) (f32.const -0x1.921fb6p+2)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const inf) (f32.const 0x1.921fb6p+2)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const -0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const 0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const inf) (f32.const -0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const inf) (f32.const 0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const inf) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "mul" (f32.const inf) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -inf) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const inf) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const inf) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const inf) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const inf) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const -0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const 0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const -0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const 0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const -0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const -0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const 0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const 0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const -0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const -0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const 0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const 0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const -0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const -0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const 0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const 0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const -0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const -0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const 0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const 0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const -0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const -0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const 0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const 0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const -0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const -0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const 0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const 0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const -0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const -0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const 0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const 0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const -0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const -0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const 0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const 0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const -0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const -0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const 0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const 0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const -0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const -0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const 0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const 0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const -0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const -0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const 0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const 0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const -0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const -0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const 0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const 0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const -inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const -inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const -nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const -nan:0x200000) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const -nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "mul" (f32.const nan:0x200000) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const -0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const 0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const -0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const 0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const -0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const 0x1p-126)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const -0x1p-126)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const 0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const -0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const 0x1p-1)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const -0x1p-1)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const 0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const -0x1p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const 0x1p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const -0x1p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const 0x1p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const -0x1.921fb6p+2)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const 0x1.921fb6p+2)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const -0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const 0x1.fffffep+127)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const -inf)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const inf)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const -inf)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const inf)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x0p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x0p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const -0x0p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const 0x0p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const -0x0p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const 0x0p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const -0x1p-149)) (f32.const 0x1p+0)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const 0x1p-149)) (f32.const -0x1p+0)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const -0x1p-149)) (f32.const -0x1p+0)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const 0x1p-149)) (f32.const 0x1p+0)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const -0x1p-126)) (f32.const 0x1p-23)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const 0x1p-126)) (f32.const -0x1p-23)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const -0x1p-126)) (f32.const -0x1p-23)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const 0x1p-126)) (f32.const 0x1p-23)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const -0x1p-1)) (f32.const 0x1p-148)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const 0x1p-1)) (f32.const -0x1p-148)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const -0x1p-1)) (f32.const -0x1p-148)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const 0x1p-1)) (f32.const 0x1p-148)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const -0x1p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const 0x1p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const -0x1p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const 0x1p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const -0x1.921fb6p+2)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const 0x1.921fb6p+2)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const -0x1.921fb6p+2)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const 0x1.921fb6p+2)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const -0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const 0x1.fffffep+127)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const -0x1.fffffep+127)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const 0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const -inf)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const inf)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const -inf)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const inf)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x1p-149) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x1p-149) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const -0x0p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const 0x0p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const -0x0p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const 0x0p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const -0x1p-149)) (f32.const 0x1p+23)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const 0x1p-149)) (f32.const -0x1p+23)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const -0x1p-149)) (f32.const -0x1p+23)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const 0x1p-149)) (f32.const 0x1p+23)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const -0x1p-126)) (f32.const 0x1p+0)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const 0x1p-126)) (f32.const -0x1p+0)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const -0x1p-126)) (f32.const -0x1p+0)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const 0x1p-126)) (f32.const 0x1p+0)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const -0x1p-1)) (f32.const 0x1p-125)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const 0x1p-1)) (f32.const -0x1p-125)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const -0x1p-1)) (f32.const -0x1p-125)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const 0x1p-1)) (f32.const 0x1p-125)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const -0x1p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const 0x1p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const -0x1p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const 0x1p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.45f3p-129)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.45f3p-129)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.45f3p-129)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.45f3p-129)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const -0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const 0x1.fffffep+127)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const -0x1.fffffep+127)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const 0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const -inf)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const inf)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const -inf)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const inf)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x1p-126) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x1p-126) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const -0x0p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const 0x0p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const -0x0p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const 0x0p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const -0x1p-149)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const 0x1p-149)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const -0x1p-149)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const 0x1p-149)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const -0x1p-126)) (f32.const 0x1p+125)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const 0x1p-126)) (f32.const -0x1p+125)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const -0x1p-126)) (f32.const -0x1p+125)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const 0x1p-126)) (f32.const 0x1p+125)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const -0x1p-1)) (f32.const 0x1p+0)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const 0x1p-1)) (f32.const -0x1p+0)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const -0x1p-1)) (f32.const -0x1p+0)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const 0x1p-1)) (f32.const 0x1p+0)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const -0x1p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const 0x1p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const -0x1p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const 0x1p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.45f306p-4)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.45f306p-4)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.45f306p-4)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.45f306p-4)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const -0x1.fffffep+127)) (f32.const 0x1p-129)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const 0x1.fffffep+127)) (f32.const -0x1p-129)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const -0x1.fffffep+127)) (f32.const -0x1p-129)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const 0x1.fffffep+127)) (f32.const 0x1p-129)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const -inf)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const inf)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const -inf)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const inf)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x1p-1) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x1p-1) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const -0x0p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const 0x0p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const -0x0p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const 0x0p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const -0x1p-149)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const 0x1p-149)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const -0x1p-149)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const 0x1p-149)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const -0x1p-126)) (f32.const 0x1p+126)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const 0x1p-126)) (f32.const -0x1p+126)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const -0x1p-126)) (f32.const -0x1p+126)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const 0x1p-126)) (f32.const 0x1p+126)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const -0x1p-1)) (f32.const 0x1p+1)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const 0x1p-1)) (f32.const -0x1p+1)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const -0x1p-1)) (f32.const -0x1p+1)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const 0x1p-1)) (f32.const 0x1p+1)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const -0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const 0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.45f306p-3)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.45f306p-3)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.45f306p-3)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.45f306p-3)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const -0x1.fffffep+127)) (f32.const 0x1p-128)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const 0x1.fffffep+127)) (f32.const -0x1p-128)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x1p-128)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x1p-128)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const -inf)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const inf)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const -inf)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const inf)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x1p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x1p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const -0x0p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const 0x0p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const -0x0p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const 0x0p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-149)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-149)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-149)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-149)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-126)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-126)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-126)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-126)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-1)) (f32.const 0x1.921fb6p+3)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-1)) (f32.const -0x1.921fb6p+3)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-1)) (f32.const -0x1.921fb6p+3)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-1)) (f32.const 0x1.921fb6p+3)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const -0x1p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const 0x1p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const -0x1p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const 0x1p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (f32.const 0x1p+0)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (f32.const -0x1p+0)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (f32.const -0x1p+0)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (f32.const 0x1p+0)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (f32.const 0x1.921fb8p-126)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (f32.const -0x1.921fb8p-126)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (f32.const -0x1.921fb8p-126)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (f32.const 0x1.921fb8p-126)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const -inf)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const inf)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const -inf)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const inf)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x1.921fb6p+2) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x1.921fb6p+2) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const -0x0p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const 0x0p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const -0x0p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const 0x0p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const -0x1p-149)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const 0x1p-149)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const -0x1p-149)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const 0x1p-149)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const -0x1p-126)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const 0x1p-126)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const -0x1p-126)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const 0x1p-126)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const -0x1p-1)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const 0x1p-1)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const -0x1p-1)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const 0x1p-1)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const -0x1p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const 0x1p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const -0x1p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const 0x1p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.45f304p+125)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.45f304p+125)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.45f304p+125)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.45f304p+125)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (f32.const 0x1p+0)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (f32.const -0x1p+0)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (f32.const -0x1p+0)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (f32.const 0x1p+0)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const -inf)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const inf)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const -inf)) (f32.const -0x0p+0)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const inf)) (f32.const 0x0p+0)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -0x1.fffffep+127) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const 0x1.fffffep+127) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -inf) (f32.const -0x0p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -inf) (f32.const 0x0p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const inf) (f32.const -0x0p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const inf) (f32.const 0x0p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -inf) (f32.const -0x1p-149)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -inf) (f32.const 0x1p-149)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const inf) (f32.const -0x1p-149)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const inf) (f32.const 0x1p-149)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -inf) (f32.const -0x1p-126)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -inf) (f32.const 0x1p-126)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const inf) (f32.const -0x1p-126)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const inf) (f32.const 0x1p-126)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -inf) (f32.const -0x1p-1)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -inf) (f32.const 0x1p-1)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const inf) (f32.const -0x1p-1)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const inf) (f32.const 0x1p-1)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -inf) (f32.const -0x1p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -inf) (f32.const 0x1p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const inf) (f32.const -0x1p+0)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const inf) (f32.const 0x1p+0)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -inf) (f32.const -0x1.921fb6p+2)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -inf) (f32.const 0x1.921fb6p+2)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const inf) (f32.const -0x1.921fb6p+2)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const inf) (f32.const 0x1.921fb6p+2)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -inf) (f32.const -0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -inf) (f32.const 0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const inf) (f32.const -0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "div" (f32.const inf) (f32.const 0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "div" (f32.const -inf) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -inf) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const inf) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const inf) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -inf) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -inf) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -inf) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -inf) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const inf) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const inf) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const inf) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const inf) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const -0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const 0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const -0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const 0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const -0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const -0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const 0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const 0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const -0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const -0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const 0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const 0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const -0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const -0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const 0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const 0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const -0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const -0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const 0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const 0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const -0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const -0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const 0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const 0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const -0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const -0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const 0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const 0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const -0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const -0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const 0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const 0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const -0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const -0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const 0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const 0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const -0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const -0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const 0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const 0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const -0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const -0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const 0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const 0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const -0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const -0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const 0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const 0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const -0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const -0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const 0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const 0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const -inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const -inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const -nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const -nan:0x200000) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const -nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "div" (f32.const nan:0x200000) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const 0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const 0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const 0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const 0x1p-126)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const 0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const 0x1p-1)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const 0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const 0x1p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const 0x1p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const 0x1.921fb6p+2)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x0p+0)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const 0x1.fffffep+127)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const inf)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const inf)) (f32.const 0x0p+0)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -0x0p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const 0x0p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const -0x0p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const 0x0p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const 0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const 0x1p-126)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const 0x1p-126)) (f32.const 0x1p-149)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const 0x1p-1)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const 0x1p-1)) (f32.const 0x1p-149)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const 0x1p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const 0x1p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const 0x1.921fb6p+2)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const 0x1.921fb6p+2)) (f32.const 0x1p-149)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const 0x1.fffffep+127)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const 0x1.fffffep+127)) (f32.const 0x1p-149)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const inf)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const inf)) (f32.const 0x1p-149)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -0x1p-149) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const 0x1p-149) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const -0x0p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const 0x0p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const -0x1p-149)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const 0x1p-149)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const 0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const 0x1p-1)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const 0x1p-1)) (f32.const 0x1p-126)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const 0x1p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const 0x1p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const 0x1.921fb6p+2)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const 0x1.921fb6p+2)) (f32.const 0x1p-126)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const 0x1.fffffep+127)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const 0x1.fffffep+127)) (f32.const 0x1p-126)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const inf)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const inf)) (f32.const 0x1p-126)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -0x1p-126) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const 0x1p-126) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const -0x0p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const 0x0p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const -0x1p-149)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const 0x1p-149)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const -0x1p-126)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const 0x1p-126)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const 0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const 0x1p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const 0x1p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const 0x1.921fb6p+2)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const 0x1.921fb6p+2)) (f32.const 0x1p-1)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const 0x1.fffffep+127)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const 0x1.fffffep+127)) (f32.const 0x1p-1)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const inf)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const inf)) (f32.const 0x1p-1)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -0x1p-1) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const 0x1p-1) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const -0x0p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const 0x0p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const -0x1p-149)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const 0x1p-149)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const -0x1p-126)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const 0x1p-126)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const -0x1p-1)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const 0x1p-1)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const 0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const 0x1.921fb6p+2)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x1p+0)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const 0x1.fffffep+127)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x1p+0)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const inf)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const inf)) (f32.const 0x1p+0)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -0x1p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const 0x1p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const -0x0p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const 0x0p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-149)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-149)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-126)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-126)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-1)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-1)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const -0x1p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const 0x1p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const inf)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const inf)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -0x1.921fb6p+2) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const 0x1.921fb6p+2) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const -0x0p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const 0x0p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const -0x1p-149)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const 0x1p-149)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const -0x1p-126)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const 0x1p-126)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const -0x1p-1)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const 0x1p-1)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const -0x1p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const 0x1p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const inf)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const inf)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -0x1.fffffep+127) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const 0x1.fffffep+127) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -inf) (f32.const -0x0p+0)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const -inf) (f32.const 0x0p+0)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const inf) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "min" (f32.const inf) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "min" (f32.const -inf) (f32.const -0x1p-149)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const -inf) (f32.const 0x1p-149)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const inf) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "min" (f32.const inf) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "min" (f32.const -inf) (f32.const -0x1p-126)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const -inf) (f32.const 0x1p-126)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const inf) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "min" (f32.const inf) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "min" (f32.const -inf) (f32.const -0x1p-1)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const -inf) (f32.const 0x1p-1)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const inf) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "min" (f32.const inf) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "min" (f32.const -inf) (f32.const -0x1p+0)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const -inf) (f32.const 0x1p+0)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const inf) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "min" (f32.const inf) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "min" (f32.const -inf) (f32.const -0x1.921fb6p+2)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const -inf) (f32.const 0x1.921fb6p+2)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const inf) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const inf) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "min" (f32.const -inf) (f32.const -0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const -inf) (f32.const 0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const inf) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const inf) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "min" (f32.const -inf) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const -inf) (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const inf) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "min" (f32.const inf) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "min" (f32.const -inf) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -inf) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -inf) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -inf) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const inf) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const inf) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const inf) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const inf) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const -0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const 0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const -0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const 0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const -0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const -0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const 0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const 0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const -0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const -0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const 0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const 0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const -0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const -0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const 0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const 0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const -0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const -0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const 0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const 0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const -0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const -0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const 0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const 0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const -0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const -0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const 0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const 0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const -0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const -0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const 0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const 0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const -0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const -0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const 0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const 0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const -0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const -0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const 0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const 0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const -0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const -0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const 0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const 0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const -0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const -0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const 0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const 0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const -0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const -0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const 0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const 0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const -inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const -inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const -nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const -nan:0x200000) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const -nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "min" (f32.const nan:0x200000) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const -0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const -0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const -0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const -0x1p-126)) (f32.const -0x0p+0)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const -0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const -0x1p-1)) (f32.const -0x0p+0)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const -0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const -0x1p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const -0x1p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x0p+0)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const -0x1.921fb6p+2)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x0p+0)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const -0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const -inf)) (f32.const -0x0p+0)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const -inf)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -0x0p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const 0x0p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const -0x0p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const 0x0p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const -0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const -0x1p-126)) (f32.const -0x1p-149)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const -0x1p-126)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const -0x1p-1)) (f32.const -0x1p-149)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const -0x1p-1)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const -0x1p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const -0x1p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const -0x1.921fb6p+2)) (f32.const -0x1p-149)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const -0x1.921fb6p+2)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const -0x1.fffffep+127)) (f32.const -0x1p-149)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const -0x1.fffffep+127)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const -inf)) (f32.const -0x1p-149)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const -inf)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -0x1p-149) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const 0x1p-149) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const -0x0p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const 0x0p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const -0x1p-149)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const 0x1p-149)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const -0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const -0x1p-1)) (f32.const -0x1p-126)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const -0x1p-1)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const -0x1p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const -0x1p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const -0x1.921fb6p+2)) (f32.const -0x1p-126)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const -0x1.921fb6p+2)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const -0x1.fffffep+127)) (f32.const -0x1p-126)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const -0x1.fffffep+127)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const -inf)) (f32.const -0x1p-126)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const -inf)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -0x1p-126) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const 0x1p-126) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const -0x0p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const 0x0p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const -0x1p-149)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const 0x1p-149)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const -0x1p-126)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const 0x1p-126)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const -0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const -0x1p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const -0x1p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const -0x1.921fb6p+2)) (f32.const -0x1p-1)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const -0x1.921fb6p+2)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const -0x1.fffffep+127)) (f32.const -0x1p-1)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const -0x1.fffffep+127)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const -inf)) (f32.const -0x1p-1)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const -inf)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -0x1p-1) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const 0x1p-1) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const -0x0p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const 0x0p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const -0x1p-149)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const 0x1p-149)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const -0x1p-126)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const 0x1p-126)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const -0x1p-1)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const 0x1p-1)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const -0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x1p+0)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const -0x1.921fb6p+2)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x1p+0)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const -0x1.fffffep+127)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const -inf)) (f32.const -0x1p+0)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const -inf)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -0x1p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const 0x1p+0) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const -0x0p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const 0x0p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-149)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-149)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-126)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-126)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-1)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-1)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const -0x1p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const 0x1p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const -inf)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const -inf)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -0x1.921fb6p+2) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const 0x1.921fb6p+2) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const -0x0p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const 0x0p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const -0x1p-149)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const 0x1p-149)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const -0x1p-126)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const 0x1p-126)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const -0x1p-1)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const 0x1p-1)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const -0x1p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const 0x1p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const -inf)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const -inf)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -0x1.fffffep+127) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const 0x1.fffffep+127) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -inf) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "max" (f32.const -inf) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "max" (f32.const inf) (f32.const -0x0p+0)) (f32.const inf)) +(assert_return (invoke "max" (f32.const inf) (f32.const 0x0p+0)) (f32.const inf)) +(assert_return (invoke "max" (f32.const -inf) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "max" (f32.const -inf) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "max" (f32.const inf) (f32.const -0x1p-149)) (f32.const inf)) +(assert_return (invoke "max" (f32.const inf) (f32.const 0x1p-149)) (f32.const inf)) +(assert_return (invoke "max" (f32.const -inf) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "max" (f32.const -inf) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "max" (f32.const inf) (f32.const -0x1p-126)) (f32.const inf)) +(assert_return (invoke "max" (f32.const inf) (f32.const 0x1p-126)) (f32.const inf)) +(assert_return (invoke "max" (f32.const -inf) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "max" (f32.const -inf) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "max" (f32.const inf) (f32.const -0x1p-1)) (f32.const inf)) +(assert_return (invoke "max" (f32.const inf) (f32.const 0x1p-1)) (f32.const inf)) +(assert_return (invoke "max" (f32.const -inf) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "max" (f32.const -inf) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "max" (f32.const inf) (f32.const -0x1p+0)) (f32.const inf)) +(assert_return (invoke "max" (f32.const inf) (f32.const 0x1p+0)) (f32.const inf)) +(assert_return (invoke "max" (f32.const -inf) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const -inf) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "max" (f32.const inf) (f32.const -0x1.921fb6p+2)) (f32.const inf)) +(assert_return (invoke "max" (f32.const inf) (f32.const 0x1.921fb6p+2)) (f32.const inf)) +(assert_return (invoke "max" (f32.const -inf) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const -inf) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "max" (f32.const inf) (f32.const -0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "max" (f32.const inf) (f32.const 0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "max" (f32.const -inf) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "max" (f32.const -inf) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const inf) (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const inf) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "max" (f32.const -inf) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -inf) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -inf) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -inf) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const inf) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const inf) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const inf) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const inf) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const -0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const 0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const -0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const -0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const 0x0p+0)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const 0x0p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const -0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const -0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const 0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const 0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const -0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const -0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const 0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const 0x1p-149)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const -0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const -0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const 0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const 0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const -0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const -0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const 0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const 0x1p-126)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const -0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const -0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const 0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const 0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const -0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const -0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const 0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const 0x1p-1)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const -0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const -0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const 0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const 0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const -0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const -0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const 0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const 0x1p+0)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const -0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const -0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const 0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const 0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const -0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const -0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const 0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const 0x1.921fb6p+2)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const -0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const -0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const 0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const 0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const -0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const -0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const 0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const 0x1.fffffep+127)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const -inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const -inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const inf)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const -nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const -nan:0x200000) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const -nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const nan)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "max" (f32.const nan:0x200000) (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sqrt" (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "sqrt" (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "sqrt" (f32.const -0x1p-149)) (f32.const nan:canonical)) +(assert_return (invoke "sqrt" (f32.const 0x1p-149)) (f32.const 0x1.6a09e6p-75)) +(assert_return (invoke "sqrt" (f32.const -0x1p-126)) (f32.const nan:canonical)) +(assert_return (invoke "sqrt" (f32.const 0x1p-126)) (f32.const 0x1p-63)) +(assert_return (invoke "sqrt" (f32.const -0x1p-1)) (f32.const nan:canonical)) +(assert_return (invoke "sqrt" (f32.const 0x1p-1)) (f32.const 0x1.6a09e6p-1)) +(assert_return (invoke "sqrt" (f32.const -0x1p+0)) (f32.const nan:canonical)) +(assert_return (invoke "sqrt" (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "sqrt" (f32.const -0x1.921fb6p+2)) (f32.const nan:canonical)) +(assert_return (invoke "sqrt" (f32.const 0x1.921fb6p+2)) (f32.const 0x1.40d932p+1)) +(assert_return (invoke "sqrt" (f32.const -0x1.fffffep+127)) (f32.const nan:canonical)) +(assert_return (invoke "sqrt" (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+63)) +(assert_return (invoke "sqrt" (f32.const -inf)) (f32.const nan:canonical)) +(assert_return (invoke "sqrt" (f32.const inf)) (f32.const inf)) +(assert_return (invoke "sqrt" (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "sqrt" (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "sqrt" (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "sqrt" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "floor" (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "floor" (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "floor" (f32.const -0x1p-149)) (f32.const -0x1p+0)) +(assert_return (invoke "floor" (f32.const 0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "floor" (f32.const -0x1p-126)) (f32.const -0x1p+0)) +(assert_return (invoke "floor" (f32.const 0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "floor" (f32.const -0x1p-1)) (f32.const -0x1p+0)) +(assert_return (invoke "floor" (f32.const 0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "floor" (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "floor" (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "floor" (f32.const -0x1.921fb6p+2)) (f32.const -0x1.cp+2)) +(assert_return (invoke "floor" (f32.const 0x1.921fb6p+2)) (f32.const 0x1.8p+2)) +(assert_return (invoke "floor" (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "floor" (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "floor" (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "floor" (f32.const inf)) (f32.const inf)) +(assert_return (invoke "floor" (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "floor" (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "floor" (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "floor" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "ceil" (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "ceil" (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "ceil" (f32.const -0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "ceil" (f32.const 0x1p-149)) (f32.const 0x1p+0)) +(assert_return (invoke "ceil" (f32.const -0x1p-126)) (f32.const -0x0p+0)) +(assert_return (invoke "ceil" (f32.const 0x1p-126)) (f32.const 0x1p+0)) +(assert_return (invoke "ceil" (f32.const -0x1p-1)) (f32.const -0x0p+0)) +(assert_return (invoke "ceil" (f32.const 0x1p-1)) (f32.const 0x1p+0)) +(assert_return (invoke "ceil" (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "ceil" (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "ceil" (f32.const -0x1.921fb6p+2)) (f32.const -0x1.8p+2)) +(assert_return (invoke "ceil" (f32.const 0x1.921fb6p+2)) (f32.const 0x1.cp+2)) +(assert_return (invoke "ceil" (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "ceil" (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "ceil" (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "ceil" (f32.const inf)) (f32.const inf)) +(assert_return (invoke "ceil" (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "ceil" (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "ceil" (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "ceil" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "trunc" (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "trunc" (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "trunc" (f32.const -0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "trunc" (f32.const 0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "trunc" (f32.const -0x1p-126)) (f32.const -0x0p+0)) +(assert_return (invoke "trunc" (f32.const 0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "trunc" (f32.const -0x1p-1)) (f32.const -0x0p+0)) +(assert_return (invoke "trunc" (f32.const 0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "trunc" (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "trunc" (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "trunc" (f32.const -0x1.921fb6p+2)) (f32.const -0x1.8p+2)) +(assert_return (invoke "trunc" (f32.const 0x1.921fb6p+2)) (f32.const 0x1.8p+2)) +(assert_return (invoke "trunc" (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "trunc" (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "trunc" (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "trunc" (f32.const inf)) (f32.const inf)) +(assert_return (invoke "trunc" (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "trunc" (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "trunc" (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "trunc" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "nearest" (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "nearest" (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "nearest" (f32.const -0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "nearest" (f32.const 0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "nearest" (f32.const -0x1p-126)) (f32.const -0x0p+0)) +(assert_return (invoke "nearest" (f32.const 0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "nearest" (f32.const -0x1p-1)) (f32.const -0x0p+0)) +(assert_return (invoke "nearest" (f32.const 0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "nearest" (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "nearest" (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "nearest" (f32.const -0x1.921fb6p+2)) (f32.const -0x1.8p+2)) +(assert_return (invoke "nearest" (f32.const 0x1.921fb6p+2)) (f32.const 0x1.8p+2)) +(assert_return (invoke "nearest" (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "nearest" (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "nearest" (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "nearest" (f32.const inf)) (f32.const inf)) +(assert_return (invoke "nearest" (f32.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "nearest" (f32.const -nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "nearest" (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "nearest" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) + + +;; Type check + +(assert_invalid (module (func (result f32) (f32.add (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.div (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.max (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.min (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.mul (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.sub (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.ceil (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.floor (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.nearest (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.sqrt (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.trunc (i64.const 0)))) "type mismatch") diff --git a/runtime/unc-vm/tests/wast/spec/f32_bitwise.wast b/runtime/unc-vm/tests/wast/spec/f32_bitwise.wast new file mode 100644 index 000000000..8554d6741 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/f32_bitwise.wast @@ -0,0 +1,376 @@ +;; Test all the f32 bitwise operators on major boundary values and all special +;; values. + +(module + (func (export "abs") (param $x f32) (result f32) (f32.abs (local.get $x))) + (func (export "neg") (param $x f32) (result f32) (f32.neg (local.get $x))) + (func (export "copysign") (param $x f32) (param $y f32) (result f32) (f32.copysign (local.get $x) (local.get $y))) +) + +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const -0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const 0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const -0x1p-149)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const 0x1p-149)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const -0x1p-126)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const 0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const -0x1p-126)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const 0x1p-126)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const -0x1p-1)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const 0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const -0x1p-1)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const 0x1p-1)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const -0x1p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const 0x1p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const -0x1p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const 0x1p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const -inf)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const inf)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const -inf)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const inf)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const -nan)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x0p+0) (f32.const nan)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const -nan)) (f32.const -0x0p+0)) +(assert_return (invoke "copysign" (f32.const 0x0p+0) (f32.const nan)) (f32.const 0x0p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const -0x0p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const 0x0p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const -0x0p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const 0x0p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const -0x1p-126)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const 0x1p-126)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const -0x1p-126)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const 0x1p-126)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const -0x1p-1)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const 0x1p-1)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const -0x1p-1)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const 0x1p-1)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const -0x1p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const 0x1p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const -0x1p+0)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const 0x1p+0)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const -0x1.921fb6p+2)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const 0x1.921fb6p+2)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const -0x1.921fb6p+2)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const 0x1.921fb6p+2)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const -0x1.fffffep+127)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const 0x1.fffffep+127)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const -0x1.fffffep+127)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const 0x1.fffffep+127)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const -inf)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const inf)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const -inf)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const inf)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const -nan)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-149) (f32.const nan)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const -nan)) (f32.const -0x1p-149)) +(assert_return (invoke "copysign" (f32.const 0x1p-149) (f32.const nan)) (f32.const 0x1p-149)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const -0x0p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const 0x0p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const -0x0p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const 0x0p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const -0x1p-149)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const 0x1p-149)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const -0x1p-149)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const 0x1p-149)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const -0x1p-1)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const 0x1p-1)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const -0x1p-1)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const 0x1p-1)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const -0x1p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const 0x1p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const -0x1p+0)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const 0x1p+0)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const -0x1.921fb6p+2)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const 0x1.921fb6p+2)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const -0x1.921fb6p+2)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const 0x1.921fb6p+2)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const -0x1.fffffep+127)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const 0x1.fffffep+127)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const -0x1.fffffep+127)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const 0x1.fffffep+127)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const -inf)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const inf)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const -inf)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const inf)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const -nan)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-126) (f32.const nan)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const -nan)) (f32.const -0x1p-126)) +(assert_return (invoke "copysign" (f32.const 0x1p-126) (f32.const nan)) (f32.const 0x1p-126)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const -0x0p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const 0x0p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const -0x0p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const 0x0p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const -0x1p-149)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const 0x1p-149)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const -0x1p-149)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const 0x1p-149)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const -0x1p-126)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const 0x1p-126)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const -0x1p-126)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const 0x1p-126)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const -0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const -0x1p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const 0x1p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const -0x1p+0)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const 0x1p+0)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const -0x1.921fb6p+2)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const 0x1.921fb6p+2)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const -0x1.921fb6p+2)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const 0x1.921fb6p+2)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const -0x1.fffffep+127)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const 0x1.fffffep+127)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const -0x1.fffffep+127)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const 0x1.fffffep+127)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const -inf)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const inf)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const -inf)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const inf)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const -nan)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p-1) (f32.const nan)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const -nan)) (f32.const -0x1p-1)) +(assert_return (invoke "copysign" (f32.const 0x1p-1) (f32.const nan)) (f32.const 0x1p-1)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const -0x0p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const 0x0p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const -0x0p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const 0x0p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const -0x1p-149)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const 0x1p-149)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const -0x1p-149)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const 0x1p-149)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const -0x1p-126)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const 0x1p-126)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const -0x1p-126)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const 0x1p-126)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const -0x1p-1)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const 0x1p-1)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const -0x1p-1)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const 0x1p-1)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const -0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const -0x1.921fb6p+2)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const 0x1.921fb6p+2)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const -0x1.fffffep+127)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const 0x1.fffffep+127)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const -inf)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const inf)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const -inf)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const inf)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const -nan)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1p+0) (f32.const nan)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const -nan)) (f32.const -0x1p+0)) +(assert_return (invoke "copysign" (f32.const 0x1p+0) (f32.const nan)) (f32.const 0x1p+0)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const -0x0p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const 0x0p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const -0x0p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const 0x0p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-149)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-149)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-149)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-149)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-126)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-126)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-126)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-126)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-1)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-1)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-1)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-1)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const -0x1p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const 0x1p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const -0x1p+0)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const 0x1p+0)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const -inf)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const inf)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const -inf)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const inf)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const -nan)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.921fb6p+2) (f32.const nan)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const -nan)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const 0x1.921fb6p+2) (f32.const nan)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const -0x0p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const 0x0p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const -0x0p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const 0x0p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const -0x1p-149)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const 0x1p-149)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const -0x1p-149)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const 0x1p-149)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const -0x1p-126)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const 0x1p-126)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const -0x1p-126)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const 0x1p-126)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const -0x1p-1)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const 0x1p-1)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const -0x1p-1)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const 0x1p-1)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const -0x1p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const 0x1p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const -0x1p+0)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const 0x1p+0)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const -inf)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const inf)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const -inf)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const inf)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const -nan)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -0x1.fffffep+127) (f32.const nan)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const -nan)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const 0x1.fffffep+127) (f32.const nan)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const -0x0p+0)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const 0x0p+0)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const -0x0p+0)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const 0x0p+0)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const -0x1p-149)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const 0x1p-149)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const -0x1p-149)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const 0x1p-149)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const -0x1p-126)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const 0x1p-126)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const -0x1p-126)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const 0x1p-126)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const -0x1p-1)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const 0x1p-1)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const -0x1p-1)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const 0x1p-1)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const -0x1p+0)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const 0x1p+0)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const -0x1p+0)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const 0x1p+0)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const -0x1.921fb6p+2)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const 0x1.921fb6p+2)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const -0x1.921fb6p+2)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const 0x1.921fb6p+2)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const -0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const 0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const -0x1.fffffep+127)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const 0x1.fffffep+127)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const inf)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const -nan)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const -inf) (f32.const nan)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const -nan)) (f32.const -inf)) +(assert_return (invoke "copysign" (f32.const inf) (f32.const nan)) (f32.const inf)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const -0x0p+0)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const 0x0p+0)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const -0x0p+0)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const 0x0p+0)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const -0x1p-149)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const 0x1p-149)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const -0x1p-149)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const 0x1p-149)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const -0x1p-126)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const 0x1p-126)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const -0x1p-126)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const 0x1p-126)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const -0x1p-1)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const 0x1p-1)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const -0x1p-1)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const 0x1p-1)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const -0x1p+0)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const 0x1p+0)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const -0x1p+0)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const 0x1p+0)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const -0x1.921fb6p+2)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const 0x1.921fb6p+2)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const -0x1.921fb6p+2)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const 0x1.921fb6p+2)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const -0x1.fffffep+127)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const 0x1.fffffep+127)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const -0x1.fffffep+127)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const 0x1.fffffep+127)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const -inf)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const inf)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const -inf)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const inf)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const -nan)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const -nan) (f32.const nan)) (f32.const nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const -nan)) (f32.const -nan)) +(assert_return (invoke "copysign" (f32.const nan) (f32.const nan)) (f32.const nan)) +(assert_return (invoke "abs" (f32.const -0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "abs" (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "abs" (f32.const -0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "abs" (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "abs" (f32.const -0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "abs" (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "abs" (f32.const -0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "abs" (f32.const 0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "abs" (f32.const -0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "abs" (f32.const 0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "abs" (f32.const -0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "abs" (f32.const 0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "abs" (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "abs" (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "abs" (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "abs" (f32.const inf)) (f32.const inf)) +(assert_return (invoke "abs" (f32.const -nan)) (f32.const nan)) +(assert_return (invoke "abs" (f32.const nan)) (f32.const nan)) +(assert_return (invoke "neg" (f32.const -0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "neg" (f32.const 0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "neg" (f32.const -0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "neg" (f32.const 0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "neg" (f32.const -0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "neg" (f32.const 0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "neg" (f32.const -0x1p-1)) (f32.const 0x1p-1)) +(assert_return (invoke "neg" (f32.const 0x1p-1)) (f32.const -0x1p-1)) +(assert_return (invoke "neg" (f32.const -0x1p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "neg" (f32.const 0x1p+0)) (f32.const -0x1p+0)) +(assert_return (invoke "neg" (f32.const -0x1.921fb6p+2)) (f32.const 0x1.921fb6p+2)) +(assert_return (invoke "neg" (f32.const 0x1.921fb6p+2)) (f32.const -0x1.921fb6p+2)) +(assert_return (invoke "neg" (f32.const -0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "neg" (f32.const 0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "neg" (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "neg" (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "neg" (f32.const -nan)) (f32.const nan)) +(assert_return (invoke "neg" (f32.const nan)) (f32.const -nan)) + + +;; Type check + +(assert_invalid (module (func (result f32) (f32.copysign (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.abs (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.neg (i64.const 0)))) "type mismatch") diff --git a/runtime/unc-vm/tests/wast/spec/f32_cmp.wast b/runtime/unc-vm/tests/wast/spec/f32_cmp.wast new file mode 100644 index 000000000..0dd7167c0 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/f32_cmp.wast @@ -0,0 +1,2422 @@ +;; Test all the f32 comparison operators on major boundary values and all +;; special values. + +(module + (func (export "eq") (param $x f32) (param $y f32) (result i32) (f32.eq (local.get $x) (local.get $y))) + (func (export "ne") (param $x f32) (param $y f32) (result i32) (f32.ne (local.get $x) (local.get $y))) + (func (export "lt") (param $x f32) (param $y f32) (result i32) (f32.lt (local.get $x) (local.get $y))) + (func (export "le") (param $x f32) (param $y f32) (result i32) (f32.le (local.get $x) (local.get $y))) + (func (export "gt") (param $x f32) (param $y f32) (result i32) (f32.gt (local.get $x) (local.get $y))) + (func (export "ge") (param $x f32) (param $y f32) (result i32) (f32.ge (local.get $x) (local.get $y))) +) + +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x0p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x0p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-149) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-149) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-126) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-126) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p-1) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p-1) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.921fb6p+2) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.921fb6p+2) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -0x1.fffffep+127) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const 0x1.fffffep+127) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -inf) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const inf) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const -nan:0x200000) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "eq" (f32.const nan:0x200000) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x0p+0) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x0p+0) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-149) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-149) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-126) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-126) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p-1) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p-1) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1p+0) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1p+0) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.921fb6p+2) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.921fb6p+2) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -0x1.fffffep+127) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const 0x1.fffffep+127) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -inf) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const inf) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const -nan:0x200000) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const -nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "ne" (f32.const nan:0x200000) (f32.const nan:0x200000)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x0p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x0p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-149) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-149) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-126) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-126) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p-1) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p-1) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.921fb6p+2) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.921fb6p+2) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -0x1.fffffep+127) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const 0x1.fffffep+127) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const inf) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const inf) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const inf) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const inf) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const inf) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const inf) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const inf) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const inf) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const inf) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const inf) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const inf) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const inf) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const inf) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const inf) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f32.const inf) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const inf) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -inf) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const inf) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const inf) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const inf) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const inf) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const -nan:0x200000) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "lt" (f32.const nan:0x200000) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x0p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x0p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-149) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-149) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-126) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-126) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p-1) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p-1) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.921fb6p+2) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.921fb6p+2) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -0x1.fffffep+127) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const 0x1.fffffep+127) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -inf) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -inf) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const inf) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const inf) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -inf) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -inf) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "le" (f32.const inf) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const inf) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -inf) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -inf) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "le" (f32.const inf) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const inf) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -inf) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -inf) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f32.const inf) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const inf) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -inf) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -inf) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f32.const inf) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const inf) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -inf) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -inf) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "le" (f32.const inf) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const inf) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -inf) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -inf) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "le" (f32.const inf) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const inf) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -inf) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -inf) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const inf) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const inf) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f32.const -inf) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -inf) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -inf) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -inf) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const inf) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const inf) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const inf) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const inf) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const -nan:0x200000) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "le" (f32.const nan:0x200000) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x0p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x0p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-149) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-149) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-126) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-126) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p-1) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p-1) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.921fb6p+2) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.921fb6p+2) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -0x1.fffffep+127) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const 0x1.fffffep+127) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const inf) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const inf) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const inf) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const inf) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const inf) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const inf) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const inf) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const inf) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const inf) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const inf) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const inf) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const inf) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const inf) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const inf) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const inf) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f32.const inf) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -inf) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const inf) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const inf) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const inf) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const inf) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const -nan:0x200000) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "gt" (f32.const nan:0x200000) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x0p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x0p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-149) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-149) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-126) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-126) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p-1) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p-1) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1p+0) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.921fb6p+2) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.921fb6p+2) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -0x1.fffffep+127) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const 0x1.fffffep+127) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const inf) (f32.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const inf) (f32.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const inf) (f32.const -0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const inf) (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const inf) (f32.const -0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const inf) (f32.const 0x1p-126)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const inf) (f32.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const inf) (f32.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const inf) (f32.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const inf) (f32.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const inf) (f32.const -0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const inf) (f32.const 0x1.921fb6p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const inf) (f32.const -0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const inf) (f32.const 0x1.fffffep+127)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const inf) (f32.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const inf) (f32.const inf)) (i32.const 1)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -inf) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const inf) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const inf) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const inf) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const inf) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const -0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const 0x1p-126)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const -0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const 0x1.921fb6p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const -0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const 0x1.fffffep+127)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const -inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const -nan:0x200000) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const -nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan) (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "ge" (f32.const nan:0x200000) (f32.const nan:0x200000)) (i32.const 0)) + + +;; Type check + +(assert_invalid (module (func (result f32) (f32.eq (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.ge (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.gt (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.le (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.lt (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.ne (i64.const 0) (f64.const 0)))) "type mismatch") diff --git a/runtime/unc-vm/tests/wast/spec/f64.wast b/runtime/unc-vm/tests/wast/spec/f64.wast new file mode 100644 index 000000000..70bb477c7 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/f64.wast @@ -0,0 +1,2533 @@ +;; Test all the f64 operators on major boundary values and all special +;; values (except comparison and bitwise operators, which are tested in +;; f64_bitwise.wast and f64_cmp.wast). + +(module + (func (export "add") (param $x f64) (param $y f64) (result f64) (f64.add (local.get $x) (local.get $y))) + (func (export "sub") (param $x f64) (param $y f64) (result f64) (f64.sub (local.get $x) (local.get $y))) + (func (export "mul") (param $x f64) (param $y f64) (result f64) (f64.mul (local.get $x) (local.get $y))) + (func (export "div") (param $x f64) (param $y f64) (result f64) (f64.div (local.get $x) (local.get $y))) + (func (export "sqrt") (param $x f64) (result f64) (f64.sqrt (local.get $x))) + (func (export "min") (param $x f64) (param $y f64) (result f64) (f64.min (local.get $x) (local.get $y))) + (func (export "max") (param $x f64) (param $y f64) (result f64) (f64.max (local.get $x) (local.get $y))) + (func (export "ceil") (param $x f64) (result f64) (f64.ceil (local.get $x))) + (func (export "floor") (param $x f64) (result f64) (f64.floor (local.get $x))) + (func (export "trunc") (param $x f64) (result f64) (f64.trunc (local.get $x))) + (func (export "nearest") (param $x f64) (result f64) (f64.nearest (local.get $x))) +) + +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const -0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -0x0p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const 0x0p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000002p-1022)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000002p-1022)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (f64.const -0x1.0000000000001p-1022)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (f64.const 0x0.fffffffffffffp-1022)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (f64.const -0x0.fffffffffffffp-1022)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (f64.const 0x1.0000000000001p-1022)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const 0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const -0x0p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const 0x0p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const -0x0p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const 0x0p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1.0000000000001p-1022)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x0.fffffffffffffp-1022)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0.fffffffffffffp-1022)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1.0000000000001p-1022)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const -0x1p-1022)) (f64.const -0x1p-1021)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const 0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const -0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const 0x1p-1022)) (f64.const 0x1p-1021)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -0x1p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const 0x1p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const -0x0p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const 0x0p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const -0x0p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const 0x0p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const -0x1p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const 0x1p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const -0x1p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const 0x1p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const -0x1p-1)) (f64.const -0x1p+0)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const 0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const -0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const 0x1p-1)) (f64.const 0x1p+0)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const -0x1p+0)) (f64.const -0x1.8p+0)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const 0x1p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const -0x1p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const 0x1p+0)) (f64.const 0x1.8p+0)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.b21fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.721fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.721fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.b21fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -0x1p-1) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const 0x1p-1) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const -0x0p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const 0x0p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const -0x0p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const 0x0p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const -0x1p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const 0x1p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const -0x1p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const 0x1p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const -0x1p-1)) (f64.const -0x1.8p+0)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const 0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const -0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const 0x1p-1)) (f64.const 0x1.8p+0)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const -0x1p+0)) (f64.const -0x1p+1)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const 0x1p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const -0x1p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const 0x1p+0)) (f64.const 0x1p+1)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.d21fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.521fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.521fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.d21fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -0x1p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const 0x1p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (f64.const -0x1.b21fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (f64.const -0x1.721fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (f64.const 0x1.721fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (f64.const 0x1.b21fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (f64.const -0x1.d21fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (f64.const -0x1.521fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (f64.const 0x1.521fb54442d18p+2)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (f64.const 0x1.d21fb54442d18p+2)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+3)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x0p+0)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x0p+0)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+3)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const 0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -inf) (f64.const -0x0p+0)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const 0x0p+0)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const inf) (f64.const -0x0p+0)) (f64.const inf)) +(assert_return (invoke "add" (f64.const inf) (f64.const 0x0p+0)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const -0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const 0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const inf) (f64.const -0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "add" (f64.const inf) (f64.const 0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const -0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const 0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const inf) (f64.const -0x1p-1022)) (f64.const inf)) +(assert_return (invoke "add" (f64.const inf) (f64.const 0x1p-1022)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const -0x1p-1)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const 0x1p-1)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const inf) (f64.const -0x1p-1)) (f64.const inf)) +(assert_return (invoke "add" (f64.const inf) (f64.const 0x1p-1)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const -0x1p+0)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const 0x1p+0)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const inf) (f64.const -0x1p+0)) (f64.const inf)) +(assert_return (invoke "add" (f64.const inf) (f64.const 0x1p+0)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const -0x1.921fb54442d18p+2)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const 0x1.921fb54442d18p+2)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const inf) (f64.const -0x1.921fb54442d18p+2)) (f64.const inf)) +(assert_return (invoke "add" (f64.const inf) (f64.const 0x1.921fb54442d18p+2)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const inf) (f64.const -0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "add" (f64.const inf) (f64.const 0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const inf) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const inf) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "add" (f64.const -inf) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -inf) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -inf) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -inf) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const inf) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const inf) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const inf) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const inf) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const -0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const 0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const -0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const 0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const -0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const 0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const -0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const -0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const 0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const 0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const -0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const 0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const -0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const -0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const 0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const 0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const -0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const -0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const 0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const 0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const -0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const -0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const 0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const 0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const -inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const -inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const -nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const -nan:0x4000000000000) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const -nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "add" (f64.const nan:0x4000000000000) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const -0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const 0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const -0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const -0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const 0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const -0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const 0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const -0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const 0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const -0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const 0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const -0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const 0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const -0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const 0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -0x0p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const 0x0p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x0.0000000000002p-1022)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0.0000000000002p-1022)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (f64.const 0x0.fffffffffffffp-1022)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (f64.const -0x1.0000000000001p-1022)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (f64.const 0x1.0000000000001p-1022)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (f64.const -0x0.fffffffffffffp-1022)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const 0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const -0x0p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const 0x0p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const -0x0p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const 0x0p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.fffffffffffffp-1022)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1.0000000000001p-1022)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1.0000000000001p-1022)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.fffffffffffffp-1022)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const -0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const 0x1p-1022)) (f64.const -0x1p-1021)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const -0x1p-1022)) (f64.const 0x1p-1021)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const 0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const -0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const 0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const -0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const 0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const -0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const 0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const -0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const 0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -0x1p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const 0x1p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const -0x0p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const 0x0p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const -0x0p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const 0x0p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const -0x1p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const 0x1p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const -0x1p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const 0x1p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const -0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const 0x1p-1)) (f64.const -0x1p+0)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const -0x1p-1)) (f64.const 0x1p+0)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const 0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const -0x1p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const 0x1p+0)) (f64.const -0x1.8p+0)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const -0x1p+0)) (f64.const 0x1.8p+0)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const 0x1p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.721fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.b21fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.b21fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.721fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -0x1p-1) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const 0x1p-1) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const -0x0p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const 0x0p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const -0x0p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const 0x0p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const -0x1p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const 0x1p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const -0x1p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const 0x1p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const 0x1p-1)) (f64.const -0x1.8p+0)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const -0x1p-1)) (f64.const 0x1.8p+0)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const -0x1p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const 0x1p+0)) (f64.const -0x1p+1)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const -0x1p+0)) (f64.const 0x1p+1)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const 0x1p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.521fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.d21fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.d21fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.521fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -0x1p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const 0x1p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (f64.const -0x1.721fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (f64.const -0x1.b21fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (f64.const 0x1.b21fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (f64.const 0x1.721fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (f64.const -0x1.521fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (f64.const -0x1.d21fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (f64.const 0x1.d21fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (f64.const 0x1.521fb54442d18p+2)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x0p+0)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+3)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+3)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x0p+0)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const 0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const -0x0p+0)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const 0x0p+0)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const -0x0p+0)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const 0x0p+0)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const -0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const 0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const -0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const 0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const -0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const 0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const -0x1p-1022)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const 0x1p-1022)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const -0x1p-1)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const 0x1p-1)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const -0x1p-1)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const 0x1p-1)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const -0x1p+0)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const 0x1p+0)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const -0x1p+0)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const 0x1p+0)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const -0x1.921fb54442d18p+2)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const 0x1.921fb54442d18p+2)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const -0x1.921fb54442d18p+2)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const 0x1.921fb54442d18p+2)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const -0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const 0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "sub" (f64.const inf) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -inf) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const inf) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const inf) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const inf) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const inf) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const -0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const 0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const -0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const 0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const -0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const 0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const -0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const -0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const 0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const 0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const -0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const 0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const -0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const -0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const 0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const 0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const -0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const -0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const 0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const 0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const -0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const -0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const 0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const 0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const -inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const -inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const -nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const -nan:0x4000000000000) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const -nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sub" (f64.const nan:0x4000000000000) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const -0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const 0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const -0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const 0x1p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const -0x1p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const 0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const -0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const 0x1p-1)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const -0x1p-1)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const 0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const -0x1p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const 0x1p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const -0x1p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const 0x1p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x0p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x0p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x0.0000000000006p-1022)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x0.0000000000006p-1022)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x0.0000000000006p-1022)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x0.0000000000006p-1022)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp-51)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp-51)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp-51)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp-51)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const -0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const 0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const -0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const 0x1p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const -0x1p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const 0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const -0x1p-1)) (f64.const 0x0.8p-1022)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const 0x1p-1)) (f64.const -0x0.8p-1022)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const -0x1p-1)) (f64.const -0x0.8p-1022)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const 0x1p-1)) (f64.const 0x0.8p-1022)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const -0x1p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const 0x1p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const -0x1p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const 0x1p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p-1020)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p-1020)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p-1020)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p-1020)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x1p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x1p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const -0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const 0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const -0x1p-1022)) (f64.const 0x0.8p-1022)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const 0x1p-1022)) (f64.const -0x0.8p-1022)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const -0x1p-1022)) (f64.const -0x0.8p-1022)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const 0x1p-1022)) (f64.const 0x0.8p-1022)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const -0x1p-1)) (f64.const 0x1p-2)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const 0x1p-1)) (f64.const -0x1p-2)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const -0x1p-1)) (f64.const -0x1p-2)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const 0x1p-1)) (f64.const 0x1p-2)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const -0x1p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const 0x1p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const -0x1p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const 0x1p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+1)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+1)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+1)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+1)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1022)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1022)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1022)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1022)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x1p-1) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x1p-1) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const -0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const 0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const -0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const 0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const -0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const 0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const -0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const 0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x1p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x1p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0.0000000000006p-1022)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x0.0000000000006p-1022)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000006p-1022)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000006p-1022)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (f64.const 0x1.921fb54442d18p-1020)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (f64.const -0x1.921fb54442d18p-1020)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (f64.const -0x1.921fb54442d18p-1020)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (f64.const 0x1.921fb54442d18p-1020)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (f64.const 0x1.921fb54442d18p+1)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (f64.const -0x1.921fb54442d18p+1)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (f64.const -0x1.921fb54442d18p+1)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (f64.const 0x1.921fb54442d18p+1)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.3bd3cc9be45dep+5)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.3bd3cc9be45dep+5)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.3bd3cc9be45dep+5)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.3bd3cc9be45dep+5)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1.fffffffffffffp-51)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1.fffffffffffffp-51)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1.fffffffffffffp-51)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1.fffffffffffffp-51)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (f64.const 0x1.fffffffffffffp+1)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (f64.const -0x1.fffffffffffffp+1)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (f64.const -0x1.fffffffffffffp+1)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (f64.const 0x1.fffffffffffffp+1)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (f64.const 0x1.fffffffffffffp+1022)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (f64.const -0x1.fffffffffffffp+1022)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (f64.const -0x1.fffffffffffffp+1022)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (f64.const 0x1.fffffffffffffp+1022)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const inf) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const inf) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const -0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const 0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const inf) (f64.const -0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const inf) (f64.const 0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const -0x1p-1022)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const 0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const inf) (f64.const -0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const inf) (f64.const 0x1p-1022)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const -0x1p-1)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const 0x1p-1)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const inf) (f64.const -0x1p-1)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const inf) (f64.const 0x1p-1)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const -0x1p+0)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const 0x1p+0)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const inf) (f64.const -0x1p+0)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const inf) (f64.const 0x1p+0)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const -0x1.921fb54442d18p+2)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const 0x1.921fb54442d18p+2)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const inf) (f64.const -0x1.921fb54442d18p+2)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const inf) (f64.const 0x1.921fb54442d18p+2)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const -0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const inf) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const inf) (f64.const 0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const inf) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "mul" (f64.const inf) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -inf) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const inf) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const inf) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const inf) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const inf) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const -0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const 0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const -0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const 0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const -0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const 0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const -0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const -0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const 0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const 0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const -0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const 0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const -0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const -0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const 0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const 0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const -0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const -0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const 0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const 0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const -0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const -0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const 0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const 0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const -inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const -inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const -nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const -nan:0x4000000000000) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const -nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "mul" (f64.const nan:0x4000000000000) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const -0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const 0x1p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const -0x1p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const 0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const -0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const 0x1p-1)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const -0x1p-1)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const 0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const -0x1p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const 0x1p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const -0x1p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const 0x1p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const -inf)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const inf)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const -inf)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const inf)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x0p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x0p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (f64.const 0x1p-52)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (f64.const -0x1p-52)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (f64.const -0x1p-52)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (f64.const 0x1p-52)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1)) (f64.const 0x0.0000000000002p-1022)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1)) (f64.const -0x0.0000000000002p-1022)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1)) (f64.const -0x0.0000000000002p-1022)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1)) (f64.const 0x0.0000000000002p-1022)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const -inf)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const inf)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const -inf)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const inf)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const -0x0p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const 0x0p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const -0x0p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const 0x0p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1p+52)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1p+52)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p+52)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p+52)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const -0x1p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const 0x1p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const -0x1p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const 0x1p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const -0x1p-1)) (f64.const 0x1p-1021)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const 0x1p-1)) (f64.const -0x1p-1021)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const -0x1p-1)) (f64.const -0x1p-1021)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const 0x1p-1)) (f64.const 0x1p-1021)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const -0x1p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const 0x1p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const -0x1p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const 0x1p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x0.28be60db9391p-1022)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x0.28be60db9391p-1022)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x0.28be60db9391p-1022)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x0.28be60db9391p-1022)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const -inf)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const inf)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const -inf)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const inf)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x1p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x1p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const -0x0p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const 0x0p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const -0x0p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const 0x0p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const -0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const 0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const -0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const 0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const -0x1p-1022)) (f64.const 0x1p+1021)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const 0x1p-1022)) (f64.const -0x1p+1021)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const -0x1p-1022)) (f64.const -0x1p+1021)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const 0x1p-1022)) (f64.const 0x1p+1021)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const -0x1p-1)) (f64.const 0x1p+0)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const 0x1p-1)) (f64.const -0x1p+0)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const -0x1p-1)) (f64.const -0x1p+0)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const 0x1p-1)) (f64.const 0x1p+0)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const -0x1p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const 0x1p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const -0x1p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const 0x1p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.45f306dc9c883p-4)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.45f306dc9c883p-4)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.45f306dc9c883p-4)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.45f306dc9c883p-4)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x0.2p-1022)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x0.2p-1022)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x0.2p-1022)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0.2p-1022)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const -inf)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const inf)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const -inf)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const inf)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x1p-1) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x1p-1) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const -0x0p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const 0x0p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const -0x0p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const 0x0p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const -0x1p-1022)) (f64.const 0x1p+1022)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const 0x1p-1022)) (f64.const -0x1p+1022)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const -0x1p-1022)) (f64.const -0x1p+1022)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const 0x1p-1022)) (f64.const 0x1p+1022)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const -0x1p-1)) (f64.const 0x1p+1)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const 0x1p-1)) (f64.const -0x1p+1)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const -0x1p-1)) (f64.const -0x1p+1)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const 0x1p-1)) (f64.const 0x1p+1)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const -0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const 0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.45f306dc9c883p-3)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.45f306dc9c883p-3)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.45f306dc9c883p-3)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.45f306dc9c883p-3)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x0.4p-1022)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x0.4p-1022)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x0.4p-1022)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0.4p-1022)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const -inf)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const inf)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const -inf)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const inf)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x1p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x1p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (f64.const 0x1.921fb54442d18p+3)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (f64.const -0x1.921fb54442d18p+3)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (f64.const -0x1.921fb54442d18p+3)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (f64.const 0x1.921fb54442d18p+3)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1p+0)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1p+0)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1p+0)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1p+0)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.921fb54442d19p-1022)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.921fb54442d19p-1022)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.921fb54442d19p-1022)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.921fb54442d19p-1022)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const -inf)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const inf)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const -inf)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const inf)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.45f306dc9c882p+1021)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.45f306dc9c882p+1021)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.45f306dc9c882p+1021)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.45f306dc9c882p+1021)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1p+0)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1p+0)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1p+0)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1p+0)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const -inf)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const inf)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const -inf)) (f64.const -0x0p+0)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const inf)) (f64.const 0x0p+0)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -inf) (f64.const -0x0p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -inf) (f64.const 0x0p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const inf) (f64.const -0x0p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const inf) (f64.const 0x0p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -inf) (f64.const -0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -inf) (f64.const 0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const inf) (f64.const -0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const inf) (f64.const 0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -inf) (f64.const -0x1p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -inf) (f64.const 0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const inf) (f64.const -0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const inf) (f64.const 0x1p-1022)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -inf) (f64.const -0x1p-1)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -inf) (f64.const 0x1p-1)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const inf) (f64.const -0x1p-1)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const inf) (f64.const 0x1p-1)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -inf) (f64.const -0x1p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -inf) (f64.const 0x1p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const inf) (f64.const -0x1p+0)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const inf) (f64.const 0x1p+0)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -inf) (f64.const -0x1.921fb54442d18p+2)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -inf) (f64.const 0x1.921fb54442d18p+2)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const inf) (f64.const -0x1.921fb54442d18p+2)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const inf) (f64.const 0x1.921fb54442d18p+2)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -inf) (f64.const -0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -inf) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const inf) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "div" (f64.const inf) (f64.const 0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "div" (f64.const -inf) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -inf) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const inf) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const inf) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -inf) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -inf) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -inf) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -inf) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const inf) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const inf) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const inf) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const inf) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const -0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const 0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const -0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const 0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const -0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const 0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const -0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const -0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const 0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const 0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const -0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const 0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const -0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const -0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const 0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const 0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const -0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const -0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const 0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const 0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const -0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const -0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const 0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const 0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const -inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const -inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const -nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const -nan:0x4000000000000) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const -nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "div" (f64.const nan:0x4000000000000) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const 0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const 0x1p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const 0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const 0x1p-1)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const 0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const 0x1p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const 0x1p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x0p+0)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const inf)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const inf)) (f64.const 0x0p+0)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -0x0p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const 0x0p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const inf)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const inf)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const 0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const -0x0p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const 0x0p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const 0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const 0x1p-1)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const 0x1p-1)) (f64.const 0x1p-1022)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const 0x1p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const 0x1p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1p-1022)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1p-1022)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const inf)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const inf)) (f64.const 0x1p-1022)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -0x1p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const 0x1p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const -0x0p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const 0x0p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const -0x1p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const 0x1p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const 0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const 0x1p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const 0x1p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1p-1)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1p-1)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const inf)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const inf)) (f64.const 0x1p-1)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -0x1p-1) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const 0x1p-1) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const -0x0p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const 0x0p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const -0x1p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const 0x1p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const -0x1p-1)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const 0x1p-1)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const 0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1p+0)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1p+0)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const inf)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const inf)) (f64.const 0x1p+0)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -0x1p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const 0x1p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const inf)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const inf)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const 0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const inf)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const inf)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -inf) (f64.const -0x0p+0)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const -inf) (f64.const 0x0p+0)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const inf) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "min" (f64.const inf) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "min" (f64.const -inf) (f64.const -0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const -inf) (f64.const 0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const inf) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const inf) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "min" (f64.const -inf) (f64.const -0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const -inf) (f64.const 0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const inf) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "min" (f64.const inf) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "min" (f64.const -inf) (f64.const -0x1p-1)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const -inf) (f64.const 0x1p-1)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const inf) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "min" (f64.const inf) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "min" (f64.const -inf) (f64.const -0x1p+0)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const -inf) (f64.const 0x1p+0)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const inf) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "min" (f64.const inf) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "min" (f64.const -inf) (f64.const -0x1.921fb54442d18p+2)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const -inf) (f64.const 0x1.921fb54442d18p+2)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const inf) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const inf) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "min" (f64.const -inf) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const -inf) (f64.const 0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const inf) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const inf) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "min" (f64.const -inf) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const -inf) (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const inf) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "min" (f64.const inf) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "min" (f64.const -inf) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -inf) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -inf) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -inf) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const inf) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const inf) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const inf) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const inf) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const -0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const 0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const -0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const 0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const -0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const 0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const -0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const -0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const 0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const 0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const -0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const 0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const -0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const -0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const 0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const 0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const -0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const -0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const 0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const 0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const -0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const -0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const 0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const 0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const -inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const -inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const -nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const -nan:0x4000000000000) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const -nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "min" (f64.const nan:0x4000000000000) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const -0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const -0x1p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const -0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const -0x1p-1)) (f64.const -0x0p+0)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const -0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const -0x1p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const -0x1p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x0p+0)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x0p+0)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const -inf)) (f64.const -0x0p+0)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const -inf)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -0x0p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const 0x0p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const -inf)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const -inf)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const 0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const -0x0p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const 0x0p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const -0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const -0x1p-1)) (f64.const -0x1p-1022)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const -0x1p-1)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const -0x1p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const -0x1p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1p-1022)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1p-1022)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const -inf)) (f64.const -0x1p-1022)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const -inf)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -0x1p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const 0x1p-1022) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const -0x0p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const 0x0p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const -0x1p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const 0x1p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const -0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const -0x1p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const -0x1p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1p-1)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1p-1)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const -inf)) (f64.const -0x1p-1)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const -inf)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -0x1p-1) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const 0x1p-1) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const -0x0p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const 0x0p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const -0x1p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const 0x1p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const -0x1p-1)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const 0x1p-1)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const -0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1p+0)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1p+0)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const -inf)) (f64.const -0x1p+0)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const -inf)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -0x1p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const 0x1p+0) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const -inf)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const -inf)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const 0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const -inf)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const -inf)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -inf) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "max" (f64.const -inf) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "max" (f64.const inf) (f64.const -0x0p+0)) (f64.const inf)) +(assert_return (invoke "max" (f64.const inf) (f64.const 0x0p+0)) (f64.const inf)) +(assert_return (invoke "max" (f64.const -inf) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const -inf) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "max" (f64.const inf) (f64.const -0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "max" (f64.const inf) (f64.const 0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "max" (f64.const -inf) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "max" (f64.const -inf) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "max" (f64.const inf) (f64.const -0x1p-1022)) (f64.const inf)) +(assert_return (invoke "max" (f64.const inf) (f64.const 0x1p-1022)) (f64.const inf)) +(assert_return (invoke "max" (f64.const -inf) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "max" (f64.const -inf) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "max" (f64.const inf) (f64.const -0x1p-1)) (f64.const inf)) +(assert_return (invoke "max" (f64.const inf) (f64.const 0x1p-1)) (f64.const inf)) +(assert_return (invoke "max" (f64.const -inf) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "max" (f64.const -inf) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "max" (f64.const inf) (f64.const -0x1p+0)) (f64.const inf)) +(assert_return (invoke "max" (f64.const inf) (f64.const 0x1p+0)) (f64.const inf)) +(assert_return (invoke "max" (f64.const -inf) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const -inf) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "max" (f64.const inf) (f64.const -0x1.921fb54442d18p+2)) (f64.const inf)) +(assert_return (invoke "max" (f64.const inf) (f64.const 0x1.921fb54442d18p+2)) (f64.const inf)) +(assert_return (invoke "max" (f64.const -inf) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const -inf) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "max" (f64.const inf) (f64.const -0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "max" (f64.const inf) (f64.const 0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "max" (f64.const -inf) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "max" (f64.const -inf) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const inf) (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const inf) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "max" (f64.const -inf) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -inf) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -inf) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -inf) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const inf) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const inf) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const inf) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const inf) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const -0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const 0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const -0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const -0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const 0x0p+0)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const 0x0p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const -0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const 0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const -0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const -0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const 0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const 0x1p-1022)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const -0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const 0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const -0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const -0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const 0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const 0x1p-1)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const -0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const -0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const 0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const 0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const -0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const -0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const 0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const 0x1p+0)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const -inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const -inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const inf)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const -nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const -nan:0x4000000000000) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const -nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const nan)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "max" (f64.const nan:0x4000000000000) (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sqrt" (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "sqrt" (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "sqrt" (f64.const -0x0.0000000000001p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "sqrt" (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p-537)) +(assert_return (invoke "sqrt" (f64.const -0x1p-1022)) (f64.const nan:canonical)) +(assert_return (invoke "sqrt" (f64.const 0x1p-1022)) (f64.const 0x1p-511)) +(assert_return (invoke "sqrt" (f64.const -0x1p-1)) (f64.const nan:canonical)) +(assert_return (invoke "sqrt" (f64.const 0x1p-1)) (f64.const 0x1.6a09e667f3bcdp-1)) +(assert_return (invoke "sqrt" (f64.const -0x1p+0)) (f64.const nan:canonical)) +(assert_return (invoke "sqrt" (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "sqrt" (f64.const -0x1.921fb54442d18p+2)) (f64.const nan:canonical)) +(assert_return (invoke "sqrt" (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.40d931ff62705p+1)) +(assert_return (invoke "sqrt" (f64.const -0x1.fffffffffffffp+1023)) (f64.const nan:canonical)) +(assert_return (invoke "sqrt" (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+511)) +(assert_return (invoke "sqrt" (f64.const -inf)) (f64.const nan:canonical)) +(assert_return (invoke "sqrt" (f64.const inf)) (f64.const inf)) +(assert_return (invoke "sqrt" (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "sqrt" (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "sqrt" (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "sqrt" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "floor" (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "floor" (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "floor" (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "floor" (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "floor" (f64.const -0x1p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "floor" (f64.const 0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "floor" (f64.const -0x1p-1)) (f64.const -0x1p+0)) +(assert_return (invoke "floor" (f64.const 0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "floor" (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "floor" (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "floor" (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.cp+2)) +(assert_return (invoke "floor" (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.8p+2)) +(assert_return (invoke "floor" (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "floor" (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "floor" (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "floor" (f64.const inf)) (f64.const inf)) +(assert_return (invoke "floor" (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "floor" (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "floor" (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "floor" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "ceil" (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "ceil" (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "ceil" (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "ceil" (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "ceil" (f64.const -0x1p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "ceil" (f64.const 0x1p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "ceil" (f64.const -0x1p-1)) (f64.const -0x0p+0)) +(assert_return (invoke "ceil" (f64.const 0x1p-1)) (f64.const 0x1p+0)) +(assert_return (invoke "ceil" (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "ceil" (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "ceil" (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.8p+2)) +(assert_return (invoke "ceil" (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.cp+2)) +(assert_return (invoke "ceil" (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "ceil" (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "ceil" (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "ceil" (f64.const inf)) (f64.const inf)) +(assert_return (invoke "ceil" (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "ceil" (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "ceil" (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "ceil" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "trunc" (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "trunc" (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "trunc" (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "trunc" (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "trunc" (f64.const -0x1p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "trunc" (f64.const 0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "trunc" (f64.const -0x1p-1)) (f64.const -0x0p+0)) +(assert_return (invoke "trunc" (f64.const 0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "trunc" (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "trunc" (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "trunc" (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.8p+2)) +(assert_return (invoke "trunc" (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.8p+2)) +(assert_return (invoke "trunc" (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "trunc" (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "trunc" (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "trunc" (f64.const inf)) (f64.const inf)) +(assert_return (invoke "trunc" (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "trunc" (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "trunc" (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "trunc" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "nearest" (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "nearest" (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "nearest" (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "nearest" (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "nearest" (f64.const -0x1p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "nearest" (f64.const 0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "nearest" (f64.const -0x1p-1)) (f64.const -0x0p+0)) +(assert_return (invoke "nearest" (f64.const 0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "nearest" (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "nearest" (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "nearest" (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.8p+2)) +(assert_return (invoke "nearest" (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.8p+2)) +(assert_return (invoke "nearest" (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "nearest" (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "nearest" (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "nearest" (f64.const inf)) (f64.const inf)) +(assert_return (invoke "nearest" (f64.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "nearest" (f64.const -nan:0x4000000000000)) (f64.const nan:arithmetic)) +(assert_return (invoke "nearest" (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "nearest" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) + + +;; Type check + +(assert_invalid (module (func (result f64) (f64.add (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.div (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.max (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.min (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.mul (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.sub (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.ceil (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.floor (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.nearest (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.sqrt (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.trunc (i64.const 0)))) "type mismatch") diff --git a/runtime/unc-vm/tests/wast/spec/f64_bitwise.wast b/runtime/unc-vm/tests/wast/spec/f64_bitwise.wast new file mode 100644 index 000000000..f268a3e18 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/f64_bitwise.wast @@ -0,0 +1,376 @@ +;; Test all the f64 bitwise operators on major boundary values and all special +;; values. + +(module + (func (export "abs") (param $x f64) (result f64) (f64.abs (local.get $x))) + (func (export "neg") (param $x f64) (result f64) (f64.neg (local.get $x))) + (func (export "copysign") (param $x f64) (param $y f64) (result f64) (f64.copysign (local.get $x) (local.get $y))) +) + +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const -0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const -0x1p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const 0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const -0x1p-1022)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const 0x1p-1022)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const -0x1p-1)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const 0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const -0x1p-1)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const 0x1p-1)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const -0x1p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const 0x1p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const -0x1p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const 0x1p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const -inf)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const inf)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const -inf)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const inf)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const -nan)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0p+0) (f64.const nan)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const -nan)) (f64.const -0x0p+0)) +(assert_return (invoke "copysign" (f64.const 0x0p+0) (f64.const nan)) (f64.const 0x0p+0)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p+0)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p+0)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const -inf)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const inf)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const -inf)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const inf)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const -nan)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x0.0000000000001p-1022) (f64.const nan)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const -nan)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const 0x0.0000000000001p-1022) (f64.const nan)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const -0x0p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const 0x0p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const -0x0p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const 0x0p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const -0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const -0x1p-1)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const 0x1p-1)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const -0x1p-1)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const 0x1p-1)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const -0x1p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const 0x1p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const -0x1p+0)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const 0x1p+0)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const -inf)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const inf)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const -inf)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const inf)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const -nan)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1022) (f64.const nan)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const -nan)) (f64.const -0x1p-1022)) +(assert_return (invoke "copysign" (f64.const 0x1p-1022) (f64.const nan)) (f64.const 0x1p-1022)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const -0x0p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const 0x0p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const -0x0p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const 0x0p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const -0x1p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const 0x1p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const -0x1p-1022)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const 0x1p-1022)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const -0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const -0x1p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const 0x1p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const -0x1p+0)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const 0x1p+0)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const -inf)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const inf)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const -inf)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const inf)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const -nan)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p-1) (f64.const nan)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const -nan)) (f64.const -0x1p-1)) +(assert_return (invoke "copysign" (f64.const 0x1p-1) (f64.const nan)) (f64.const 0x1p-1)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const -0x0p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const 0x0p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const -0x0p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const 0x0p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const -0x1p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const 0x1p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const -0x1p-1022)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const 0x1p-1022)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const -0x1p-1)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const 0x1p-1)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const -0x1p-1)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const 0x1p-1)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const -0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const -inf)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const inf)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const -inf)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const inf)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const -nan)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1p+0) (f64.const nan)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const -nan)) (f64.const -0x1p+0)) +(assert_return (invoke "copysign" (f64.const 0x1p+0) (f64.const nan)) (f64.const 0x1p+0)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const -inf)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const inf)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const -inf)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const inf)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.921fb54442d18p+2) (f64.const nan)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const 0x1.921fb54442d18p+2) (f64.const nan)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const -inf)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const inf)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const -inf)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const inf)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const -0x0p+0)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const 0x0p+0)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const -0x0p+0)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const 0x0p+0)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const -0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const 0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const -0x0.0000000000001p-1022)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const 0x0.0000000000001p-1022)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const -0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const 0x1p-1022)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const -0x1p-1022)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const 0x1p-1022)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const -0x1p-1)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const 0x1p-1)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const -0x1p-1)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const 0x1p-1)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const -0x1p+0)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const 0x1p+0)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const -0x1p+0)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const 0x1p+0)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const -0x1.921fb54442d18p+2)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const 0x1.921fb54442d18p+2)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const -0x1.921fb54442d18p+2)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const 0x1.921fb54442d18p+2)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const 0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const 0x1.fffffffffffffp+1023)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const inf)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const -nan)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const -inf) (f64.const nan)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const -nan)) (f64.const -inf)) +(assert_return (invoke "copysign" (f64.const inf) (f64.const nan)) (f64.const inf)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const -0x0p+0)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const 0x0p+0)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const -0x0p+0)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const 0x0p+0)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const -0x0.0000000000001p-1022)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const 0x0.0000000000001p-1022)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const -0x0.0000000000001p-1022)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const 0x0.0000000000001p-1022)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const -0x1p-1022)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const 0x1p-1022)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const -0x1p-1022)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const 0x1p-1022)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const -0x1p-1)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const 0x1p-1)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const -0x1p-1)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const 0x1p-1)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const -0x1p+0)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const 0x1p+0)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const -0x1p+0)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const 0x1p+0)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const -0x1.921fb54442d18p+2)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const -0x1.921fb54442d18p+2)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const 0x1.921fb54442d18p+2)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const -0x1.fffffffffffffp+1023)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const 0x1.fffffffffffffp+1023)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const -inf)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const inf)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const -inf)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const inf)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const -nan)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const -nan) (f64.const nan)) (f64.const nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const -nan)) (f64.const -nan)) +(assert_return (invoke "copysign" (f64.const nan) (f64.const nan)) (f64.const nan)) +(assert_return (invoke "abs" (f64.const -0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "abs" (f64.const 0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "abs" (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "abs" (f64.const 0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "abs" (f64.const -0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "abs" (f64.const 0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "abs" (f64.const -0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "abs" (f64.const 0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "abs" (f64.const -0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "abs" (f64.const 0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "abs" (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "abs" (f64.const 0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "abs" (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "abs" (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "abs" (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "abs" (f64.const inf)) (f64.const inf)) +(assert_return (invoke "abs" (f64.const -nan)) (f64.const nan)) +(assert_return (invoke "abs" (f64.const nan)) (f64.const nan)) +(assert_return (invoke "neg" (f64.const -0x0p+0)) (f64.const 0x0p+0)) +(assert_return (invoke "neg" (f64.const 0x0p+0)) (f64.const -0x0p+0)) +(assert_return (invoke "neg" (f64.const -0x0.0000000000001p-1022)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "neg" (f64.const 0x0.0000000000001p-1022)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "neg" (f64.const -0x1p-1022)) (f64.const 0x1p-1022)) +(assert_return (invoke "neg" (f64.const 0x1p-1022)) (f64.const -0x1p-1022)) +(assert_return (invoke "neg" (f64.const -0x1p-1)) (f64.const 0x1p-1)) +(assert_return (invoke "neg" (f64.const 0x1p-1)) (f64.const -0x1p-1)) +(assert_return (invoke "neg" (f64.const -0x1p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "neg" (f64.const 0x1p+0)) (f64.const -0x1p+0)) +(assert_return (invoke "neg" (f64.const -0x1.921fb54442d18p+2)) (f64.const 0x1.921fb54442d18p+2)) +(assert_return (invoke "neg" (f64.const 0x1.921fb54442d18p+2)) (f64.const -0x1.921fb54442d18p+2)) +(assert_return (invoke "neg" (f64.const -0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "neg" (f64.const 0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "neg" (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "neg" (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "neg" (f64.const -nan)) (f64.const nan)) +(assert_return (invoke "neg" (f64.const nan)) (f64.const -nan)) + + +;; Type check + +(assert_invalid (module (func (result f64) (f64.copysign (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.abs (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.neg (i64.const 0)))) "type mismatch") diff --git a/runtime/unc-vm/tests/wast/spec/f64_cmp.wast b/runtime/unc-vm/tests/wast/spec/f64_cmp.wast new file mode 100644 index 000000000..dd79929d2 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/f64_cmp.wast @@ -0,0 +1,2422 @@ +;; Test all the f64 comparison operators on major boundary values and all +;; special values. + +(module + (func (export "eq") (param $x f64) (param $y f64) (result i32) (f64.eq (local.get $x) (local.get $y))) + (func (export "ne") (param $x f64) (param $y f64) (result i32) (f64.ne (local.get $x) (local.get $y))) + (func (export "lt") (param $x f64) (param $y f64) (result i32) (f64.lt (local.get $x) (local.get $y))) + (func (export "le") (param $x f64) (param $y f64) (result i32) (f64.le (local.get $x) (local.get $y))) + (func (export "gt") (param $x f64) (param $y f64) (result i32) (f64.gt (local.get $x) (local.get $y))) + (func (export "ge") (param $x f64) (param $y f64) (result i32) (f64.ge (local.get $x) (local.get $y))) +) + +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p-1) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p-1) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -inf) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const inf) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const -nan:0x4000000000000) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (f64.const nan:0x4000000000000) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0p+0) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0p+0) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1022) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1022) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p-1) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p-1) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1p+0) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1p+0) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -inf) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const inf) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const -nan:0x4000000000000) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const -nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (f64.const nan:0x4000000000000) (f64.const nan:0x4000000000000)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p-1) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p-1) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const inf) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const inf) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const inf) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const inf) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const inf) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const inf) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const inf) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const inf) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const inf) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const inf) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const inf) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const inf) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const inf) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const inf) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "lt" (f64.const inf) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const inf) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -inf) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const inf) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const inf) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const inf) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const inf) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const -nan:0x4000000000000) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "lt" (f64.const nan:0x4000000000000) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p-1) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p-1) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -inf) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -inf) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const inf) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const inf) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -inf) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -inf) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const inf) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const inf) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -inf) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -inf) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "le" (f64.const inf) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const inf) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -inf) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -inf) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "le" (f64.const inf) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const inf) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -inf) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -inf) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "le" (f64.const inf) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const inf) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -inf) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -inf) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "le" (f64.const inf) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const inf) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -inf) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -inf) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "le" (f64.const inf) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const inf) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -inf) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -inf) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const inf) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const inf) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "le" (f64.const -inf) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -inf) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -inf) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -inf) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const inf) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const inf) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const inf) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const inf) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const -nan:0x4000000000000) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "le" (f64.const nan:0x4000000000000) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p-1) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p-1) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const inf) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const inf) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const inf) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const inf) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const inf) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const inf) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const inf) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const inf) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const inf) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const inf) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const inf) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const inf) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const inf) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const inf) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const inf) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "gt" (f64.const inf) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -inf) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const inf) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const inf) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const inf) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const inf) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const -nan:0x4000000000000) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "gt" (f64.const nan:0x4000000000000) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x0.0000000000001p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1022) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p-1) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p-1) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1p+0) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.921fb54442d18p+2) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const 0x1.fffffffffffffp+1023) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const inf) (f64.const -0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const inf) (f64.const 0x0p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const inf) (f64.const -0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const inf) (f64.const 0x0.0000000000001p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const inf) (f64.const -0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const inf) (f64.const 0x1p-1022)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const inf) (f64.const -0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const inf) (f64.const 0x1p-1)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const inf) (f64.const -0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const inf) (f64.const 0x1p+0)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const inf) (f64.const -0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const inf) (f64.const 0x1.921fb54442d18p+2)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const inf) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const inf) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const inf) (f64.const -inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const inf) (f64.const inf)) (i32.const 1)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -inf) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const inf) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const inf) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const inf) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const inf) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const -0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const 0x0p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const -0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const 0x1p-1022)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const -0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const 0x1p-1)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const -0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const 0x1p+0)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const -0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const 0x1.921fb54442d18p+2)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const -0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const 0x1.fffffffffffffp+1023)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const -inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const inf)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const -nan:0x4000000000000) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const -nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan) (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "ge" (f64.const nan:0x4000000000000) (f64.const nan:0x4000000000000)) (i32.const 0)) + + +;; Type check + +(assert_invalid (module (func (result f64) (f64.eq (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.ge (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.gt (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.le (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.lt (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.ne (i64.const 0) (f32.const 0)))) "type mismatch") diff --git a/runtime/unc-vm/tests/wast/spec/fac.wast b/runtime/unc-vm/tests/wast/spec/fac.wast new file mode 100644 index 000000000..0e61c1f9e --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/fac.wast @@ -0,0 +1,109 @@ +(module + ;; Recursive factorial + (func (export "fac-rec") (param i64) (result i64) + (if (result i64) (i64.eq (local.get 0) (i64.const 0)) + (then (i64.const 1)) + (else + (i64.mul (local.get 0) (call 0 (i64.sub (local.get 0) (i64.const 1)))) + ) + ) + ) + + ;; Recursive factorial named + (func $fac-rec-named (export "fac-rec-named") (param $n i64) (result i64) + (if (result i64) (i64.eq (local.get $n) (i64.const 0)) + (then (i64.const 1)) + (else + (i64.mul + (local.get $n) + (call $fac-rec-named (i64.sub (local.get $n) (i64.const 1))) + ) + ) + ) + ) + + ;; Iterative factorial + (func (export "fac-iter") (param i64) (result i64) + (local i64 i64) + (local.set 1 (local.get 0)) + (local.set 2 (i64.const 1)) + (block + (loop + (if + (i64.eq (local.get 1) (i64.const 0)) + (then (br 2)) + (else + (local.set 2 (i64.mul (local.get 1) (local.get 2))) + (local.set 1 (i64.sub (local.get 1) (i64.const 1))) + ) + ) + (br 0) + ) + ) + (local.get 2) + ) + + ;; Iterative factorial named + (func (export "fac-iter-named") (param $n i64) (result i64) + (local $i i64) + (local $res i64) + (local.set $i (local.get $n)) + (local.set $res (i64.const 1)) + (block $done + (loop $loop + (if + (i64.eq (local.get $i) (i64.const 0)) + (then (br $done)) + (else + (local.set $res (i64.mul (local.get $i) (local.get $res))) + (local.set $i (i64.sub (local.get $i) (i64.const 1))) + ) + ) + (br $loop) + ) + ) + (local.get $res) + ) + + ;; Optimized factorial. + (func (export "fac-opt") (param i64) (result i64) + (local i64) + (local.set 1 (i64.const 1)) + (block + (br_if 0 (i64.lt_s (local.get 0) (i64.const 2))) + (loop + (local.set 1 (i64.mul (local.get 1) (local.get 0))) + (local.set 0 (i64.add (local.get 0) (i64.const -1))) + (br_if 0 (i64.gt_s (local.get 0) (i64.const 1))) + ) + ) + (local.get 1) + ) + + ;; Iterative factorial without locals. + (func $pick0 (param i64) (result i64 i64) + (local.get 0) (local.get 0) + ) + (func $pick1 (param i64 i64) (result i64 i64 i64) + (local.get 0) (local.get 1) (local.get 0) + ) + (func (export "fac-ssa") (param i64) (result i64) + (i64.const 1) (local.get 0) + (loop $l (param i64 i64) (result i64) + (call $pick1) (call $pick1) (i64.mul) + (call $pick1) (i64.const 1) (i64.sub) + (call $pick0) (i64.const 0) (i64.gt_u) + (br_if $l) + (drop) (return) + ) + ) +) + +(assert_return (invoke "fac-rec" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-iter" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-rec-named" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-iter-named" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-opt" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-ssa" (i64.const 25)) (i64.const 7034535277573963776)) + +(assert_exhaustion (invoke "fac-rec" (i64.const 1073741824)) "call stack exhausted") diff --git a/runtime/unc-vm/tests/wast/spec/float_exprs.wast b/runtime/unc-vm/tests/wast/spec/float_exprs.wast new file mode 100644 index 000000000..1f8829921 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/float_exprs.wast @@ -0,0 +1,2570 @@ +;; Test interesting floating-point "expressions". These tests contain code +;; patterns which tempt common value-changing optimizations. + +;; Test that x*y+z is not done with x87-style intermediate precision. + +(module + (func (export "f64.no_contraction") (param $x f64) (param $y f64) (param $z f64) (result f64) + (f64.add (f64.mul (local.get $x) (local.get $y)) (local.get $z))) +) + +(assert_return (invoke "f64.no_contraction" (f64.const -0x1.9e87ce14273afp-103) (f64.const 0x1.2515ad31db63ep+664) (f64.const 0x1.868c6685e6185p+533)) (f64.const -0x1.da94885b11493p+561)) +(assert_return (invoke "f64.no_contraction" (f64.const 0x1.da21c460a6f44p+52) (f64.const 0x1.60859d2e7714ap-321) (f64.const 0x1.e63f1b7b660e1p-302)) (f64.const 0x1.4672f256d1794p-268)) +(assert_return (invoke "f64.no_contraction" (f64.const -0x1.f3eaf43f327cp-594) (f64.const 0x1.dfcc009906b57p+533) (f64.const 0x1.5984e03c520a1p-104)) (f64.const -0x1.d4797fb3db166p-60)) +(assert_return (invoke "f64.no_contraction" (f64.const 0x1.dab6c772cb2e2p-69) (f64.const -0x1.d761663679a84p-101) (f64.const 0x1.f22f92c843226p-218)) (f64.const -0x1.b50d72dfcef68p-169)) +(assert_return (invoke "f64.no_contraction" (f64.const -0x1.87c5def1e4d3dp-950) (f64.const -0x1.50cd5dab2207fp+935) (f64.const 0x1.e629bd0da8c5dp-54)) (f64.const 0x1.01b6feb4e78a7p-14)) + +;; Test that x*y+z is not folded to fma. + +(module + (func (export "f32.no_fma") (param $x f32) (param $y f32) (param $z f32) (result f32) + (f32.add (f32.mul (local.get $x) (local.get $y)) (local.get $z))) + (func (export "f64.no_fma") (param $x f64) (param $y f64) (param $z f64) (result f64) + (f64.add (f64.mul (local.get $x) (local.get $y)) (local.get $z))) +) + +(assert_return (invoke "f32.no_fma" (f32.const 0x1.a78402p+124) (f32.const 0x1.cf8548p-23) (f32.const 0x1.992adap+107)) (f32.const 0x1.a5262cp+107)) +(assert_return (invoke "f32.no_fma" (f32.const 0x1.ed15a4p-28) (f32.const -0x1.613c72p-50) (f32.const 0x1.4757bp-88)) (f32.const -0x1.5406b8p-77)) +(assert_return (invoke "f32.no_fma" (f32.const 0x1.ae63a2p+37) (f32.const 0x1.b3a59ap-13) (f32.const 0x1.c16918p+10)) (f32.const 0x1.6e385cp+25)) +(assert_return (invoke "f32.no_fma" (f32.const 0x1.2a77fap-8) (f32.const -0x1.bb7356p+22) (f32.const -0x1.32be2ap+1)) (f32.const -0x1.0286d4p+15)) +(assert_return (invoke "f32.no_fma" (f32.const 0x1.298fb6p+126) (f32.const -0x1.03080cp-70) (f32.const -0x1.418de6p+34)) (f32.const -0x1.2d15c6p+56)) +(assert_return (invoke "f64.no_fma" (f64.const 0x1.ac357ff46eed4p+557) (f64.const 0x1.852c01a5e7297p+430) (f64.const -0x1.05995704eda8ap+987)) (f64.const 0x1.855d905d338ep+987)) +(assert_return (invoke "f64.no_fma" (f64.const 0x1.e2fd6bf32010cp+749) (f64.const 0x1.01c2238d405e4p-130) (f64.const 0x1.2ecc0db4b9f94p+573)) (f64.const 0x1.e64eb07e063bcp+619)) +(assert_return (invoke "f64.no_fma" (f64.const 0x1.92b7c7439ede3p-721) (f64.const -0x1.6aa97586d3de6p+1011) (f64.const 0x1.8de4823f6358ap+237)) (f64.const -0x1.1d4139fd20ecdp+291)) +(assert_return (invoke "f64.no_fma" (f64.const -0x1.466d30bddb453p-386) (f64.const -0x1.185a4d739c7aap+443) (f64.const 0x1.5f9c436fbfc7bp+55)) (f64.const 0x1.bd61a350fcc1ap+57)) +(assert_return (invoke "f64.no_fma" (f64.const 0x1.7e2c44058a799p+52) (f64.const 0x1.c73b71765b8b2p+685) (f64.const -0x1.16c641df0b108p+690)) (f64.const 0x1.53ccb53de0bd1p+738)) + +;; Test that x+0.0 is not folded to x. +;; See IEEE 754-2008 10.4 "Literal meaning and value-changing optimizations". + +(module + (func (export "f32.no_fold_add_zero") (param $x f32) (result f32) + (f32.add (local.get $x) (f32.const 0.0))) + (func (export "f64.no_fold_add_zero") (param $x f64) (result f64) + (f64.add (local.get $x) (f64.const 0.0))) +) + +(assert_return (invoke "f32.no_fold_add_zero" (f32.const -0.0)) (f32.const 0.0)) +(assert_return (invoke "f64.no_fold_add_zero" (f64.const -0.0)) (f64.const 0.0)) +(assert_return (invoke "f32.no_fold_add_zero" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f64.no_fold_add_zero" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) + +;; Test that 0.0 - x is not folded to -x. + +(module + (func (export "f32.no_fold_zero_sub") (param $x f32) (result f32) + (f32.sub (f32.const 0.0) (local.get $x))) + (func (export "f64.no_fold_zero_sub") (param $x f64) (result f64) + (f64.sub (f64.const 0.0) (local.get $x))) +) + +(assert_return (invoke "f32.no_fold_zero_sub" (f32.const 0.0)) (f32.const 0.0)) +(assert_return (invoke "f64.no_fold_zero_sub" (f64.const 0.0)) (f64.const 0.0)) +(assert_return (invoke "f32.no_fold_zero_sub" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f64.no_fold_zero_sub" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) + +;; Test that x - 0.0 is not folded to x. + +(module + (func (export "f32.no_fold_sub_zero") (param $x f32) (result f32) + (f32.sub (local.get $x) (f32.const 0.0))) + (func (export "f64.no_fold_sub_zero") (param $x f64) (result f64) + (f64.sub (local.get $x) (f64.const 0.0))) +) + +(assert_return (invoke "f32.no_fold_sub_zero" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f64.no_fold_sub_zero" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) + +;; Test that x*0.0 is not folded to 0.0. + +(module + (func (export "f32.no_fold_mul_zero") (param $x f32) (result f32) + (f32.mul (local.get $x) (f32.const 0.0))) + (func (export "f64.no_fold_mul_zero") (param $x f64) (result f64) + (f64.mul (local.get $x) (f64.const 0.0))) +) + +(assert_return (invoke "f32.no_fold_mul_zero" (f32.const -0.0)) (f32.const -0.0)) +(assert_return (invoke "f32.no_fold_mul_zero" (f32.const -1.0)) (f32.const -0.0)) +(assert_return (invoke "f32.no_fold_mul_zero" (f32.const -2.0)) (f32.const -0.0)) +(assert_return (invoke "f32.no_fold_mul_zero" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f64.no_fold_mul_zero" (f64.const -0.0)) (f64.const -0.0)) +(assert_return (invoke "f64.no_fold_mul_zero" (f64.const -1.0)) (f64.const -0.0)) +(assert_return (invoke "f64.no_fold_mul_zero" (f64.const -2.0)) (f64.const -0.0)) +(assert_return (invoke "f64.no_fold_mul_zero" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) + +;; Test that x*1.0 is not folded to x. +;; See IEEE 754-2008 10.4 "Literal meaning and value-changing optimizations". + +(module + (func (export "f32.no_fold_mul_one") (param $x f32) (result f32) + (f32.mul (local.get $x) (f32.const 1.0))) + (func (export "f64.no_fold_mul_one") (param $x f64) (result f64) + (f64.mul (local.get $x) (f64.const 1.0))) +) + +(assert_return (invoke "f32.no_fold_mul_one" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f64.no_fold_mul_one" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) + +;; Test that 0.0/x is not folded to 0.0. + +(module + (func (export "f32.no_fold_zero_div") (param $x f32) (result f32) + (f32.div (f32.const 0.0) (local.get $x))) + (func (export "f64.no_fold_zero_div") (param $x f64) (result f64) + (f64.div (f64.const 0.0) (local.get $x))) +) + +(assert_return (invoke "f32.no_fold_zero_div" (f32.const 0.0)) (f32.const nan:canonical)) +(assert_return (invoke "f32.no_fold_zero_div" (f32.const -0.0)) (f32.const nan:canonical)) +(assert_return (invoke "f32.no_fold_zero_div" (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "f32.no_fold_zero_div" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f64.no_fold_zero_div" (f64.const 0.0)) (f64.const nan:canonical)) +(assert_return (invoke "f64.no_fold_zero_div" (f64.const -0.0)) (f64.const nan:canonical)) +(assert_return (invoke "f64.no_fold_zero_div" (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "f64.no_fold_zero_div" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) + +;; Test that x/1.0 is not folded to x. + +(module + (func (export "f32.no_fold_div_one") (param $x f32) (result f32) + (f32.div (local.get $x) (f32.const 1.0))) + (func (export "f64.no_fold_div_one") (param $x f64) (result f64) + (f64.div (local.get $x) (f64.const 1.0))) +) + +(assert_return (invoke "f32.no_fold_div_one" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f64.no_fold_div_one" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) + +;; Test that x/-1.0 is not folded to -x. + +(module + (func (export "f32.no_fold_div_neg1") (param $x f32) (result f32) + (f32.div (local.get $x) (f32.const -1.0))) + (func (export "f64.no_fold_div_neg1") (param $x f64) (result f64) + (f64.div (local.get $x) (f64.const -1.0))) +) + +(assert_return (invoke "f32.no_fold_div_neg1" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f64.no_fold_div_neg1" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) + +;; Test that -0.0 - x is not folded to -x. + +(module + (func (export "f32.no_fold_neg0_sub") (param $x f32) (result f32) + (f32.sub (f32.const -0.0) (local.get $x))) + (func (export "f64.no_fold_neg0_sub") (param $x f64) (result f64) + (f64.sub (f64.const -0.0) (local.get $x))) +) + +(assert_return (invoke "f32.no_fold_neg0_sub" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f64.no_fold_neg0_sub" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) + +;; Test that -1.0 * x is not folded to -x. + +(module + (func (export "f32.no_fold_neg1_mul") (param $x f32) (result f32) + (f32.mul (f32.const -1.0) (local.get $x))) + (func (export "f64.no_fold_neg1_mul") (param $x f64) (result f64) + (f64.mul (f64.const -1.0) (local.get $x))) +) + +(assert_return (invoke "f32.no_fold_neg1_mul" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f64.no_fold_neg1_mul" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) + +;; Test that x == x is not folded to true. + +(module + (func (export "f32.no_fold_eq_self") (param $x f32) (result i32) + (f32.eq (local.get $x) (local.get $x))) + (func (export "f64.no_fold_eq_self") (param $x f64) (result i32) + (f64.eq (local.get $x) (local.get $x))) +) + +(assert_return (invoke "f32.no_fold_eq_self" (f32.const nan)) (i32.const 0)) +(assert_return (invoke "f64.no_fold_eq_self" (f64.const nan)) (i32.const 0)) + +;; Test that x != x is not folded to false. + +(module + (func (export "f32.no_fold_ne_self") (param $x f32) (result i32) + (f32.ne (local.get $x) (local.get $x))) + (func (export "f64.no_fold_ne_self") (param $x f64) (result i32) + (f64.ne (local.get $x) (local.get $x))) +) + +(assert_return (invoke "f32.no_fold_ne_self" (f32.const nan)) (i32.const 1)) +(assert_return (invoke "f64.no_fold_ne_self" (f64.const nan)) (i32.const 1)) + +;; Test that x - x is not folded to 0.0. + +(module + (func (export "f32.no_fold_sub_self") (param $x f32) (result f32) + (f32.sub (local.get $x) (local.get $x))) + (func (export "f64.no_fold_sub_self") (param $x f64) (result f64) + (f64.sub (local.get $x) (local.get $x))) +) + +(assert_return (invoke "f32.no_fold_sub_self" (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "f32.no_fold_sub_self" (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "f64.no_fold_sub_self" (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "f64.no_fold_sub_self" (f64.const nan)) (f64.const nan:canonical)) + +;; Test that x / x is not folded to 1.0. + +(module + (func (export "f32.no_fold_div_self") (param $x f32) (result f32) + (f32.div (local.get $x) (local.get $x))) + (func (export "f64.no_fold_div_self") (param $x f64) (result f64) + (f64.div (local.get $x) (local.get $x))) +) + +(assert_return (invoke "f32.no_fold_div_self" (f32.const inf)) (f32.const nan:canonical)) +(assert_return (invoke "f32.no_fold_div_self" (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "f32.no_fold_div_self" (f32.const 0.0)) (f32.const nan:canonical)) +(assert_return (invoke "f32.no_fold_div_self" (f32.const -0.0)) (f32.const nan:canonical)) +(assert_return (invoke "f64.no_fold_div_self" (f64.const inf)) (f64.const nan:canonical)) +(assert_return (invoke "f64.no_fold_div_self" (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "f64.no_fold_div_self" (f64.const 0.0)) (f64.const nan:canonical)) +(assert_return (invoke "f64.no_fold_div_self" (f64.const -0.0)) (f64.const nan:canonical)) + +;; Test that x/3 is not folded to x*(1/3). + +(module + (func (export "f32.no_fold_div_3") (param $x f32) (result f32) + (f32.div (local.get $x) (f32.const 3.0))) + (func (export "f64.no_fold_div_3") (param $x f64) (result f64) + (f64.div (local.get $x) (f64.const 3.0))) +) + +(assert_return (invoke "f32.no_fold_div_3" (f32.const -0x1.359c26p+50)) (f32.const -0x1.9cd032p+48)) +(assert_return (invoke "f32.no_fold_div_3" (f32.const -0x1.e45646p+93)) (f32.const -0x1.42e42ep+92)) +(assert_return (invoke "f32.no_fold_div_3" (f32.const -0x1.2a3916p-83)) (f32.const -0x1.8da172p-85)) +(assert_return (invoke "f32.no_fold_div_3" (f32.const -0x1.1f8b38p-124)) (f32.const -0x1.7f644ap-126)) +(assert_return (invoke "f32.no_fold_div_3" (f32.const -0x1.d64f64p-56)) (f32.const -0x1.398a42p-57)) +(assert_return (invoke "f64.no_fold_div_3" (f64.const -0x1.a8a88d29e2cc3p+632)) (f64.const -0x1.1b1b08c69732dp+631)) +(assert_return (invoke "f64.no_fold_div_3" (f64.const -0x1.bcf52dc950972p-167)) (f64.const -0x1.28a373db8b0f7p-168)) +(assert_return (invoke "f64.no_fold_div_3" (f64.const 0x1.bd3c0d989f7a4p-874)) (f64.const 0x1.28d2b3bb14fc3p-875)) +(assert_return (invoke "f64.no_fold_div_3" (f64.const -0x1.0138bf530a53cp+1007)) (f64.const -0x1.56f6546eb86fbp+1005)) +(assert_return (invoke "f64.no_fold_div_3" (f64.const 0x1.052b87f9d794dp+415)) (f64.const 0x1.5c3a0aa274c67p+413)) + +;; Test that (x*z)+(y*z) is not folded to (x+y)*z. + +(module + (func (export "f32.no_factor") (param $x f32) (param $y f32) (param $z f32) (result f32) + (f32.add (f32.mul (local.get $x) (local.get $z)) (f32.mul (local.get $y) (local.get $z)))) + (func (export "f64.no_factor") (param $x f64) (param $y f64) (param $z f64) (result f64) + (f64.add (f64.mul (local.get $x) (local.get $z)) (f64.mul (local.get $y) (local.get $z)))) +) + +(assert_return (invoke "f32.no_factor" (f32.const -0x1.4e2352p+40) (f32.const -0x1.842e2cp+49) (f32.const 0x1.eea602p+59)) (f32.const -0x1.77a7dp+109)) +(assert_return (invoke "f32.no_factor" (f32.const -0x1.b4e7f6p-6) (f32.const 0x1.8c990cp-5) (f32.const -0x1.70cc02p-9)) (f32.const -0x1.00a342p-14)) +(assert_return (invoke "f32.no_factor" (f32.const -0x1.06722ep-41) (f32.const 0x1.eed3cep-64) (f32.const 0x1.5c5558p+123)) (f32.const -0x1.651aaep+82)) +(assert_return (invoke "f32.no_factor" (f32.const -0x1.f8c6a4p-64) (f32.const 0x1.08c806p-83) (f32.const 0x1.b5ceccp+118)) (f32.const -0x1.afa15p+55)) +(assert_return (invoke "f32.no_factor" (f32.const -0x1.3aaa1ep-84) (f32.const 0x1.c6d5eep-71) (f32.const 0x1.8d2924p+20)) (f32.const 0x1.60c9cep-50)) +(assert_return (invoke "f64.no_factor" (f64.const 0x1.3adeda9144977p-424) (f64.const 0x1.c15af887049e1p-462) (f64.const -0x1.905179c4c4778p-225)) (f64.const -0x1.ec606bcb87b1ap-649)) +(assert_return (invoke "f64.no_factor" (f64.const 0x1.3c84821c1d348p-662) (f64.const -0x1.4ffd4c77ad037p-1009) (f64.const -0x1.dd275335c6f4p-957)) (f64.const 0x0p+0)) +(assert_return (invoke "f64.no_factor" (f64.const -0x1.074f372347051p-334) (f64.const -0x1.aaeef661f4c96p-282) (f64.const -0x1.9bd34abe8696dp+479)) (f64.const 0x1.5767029593e2p+198)) +(assert_return (invoke "f64.no_factor" (f64.const -0x1.c4ded58a6f389p-289) (f64.const 0x1.ba6fdef5d59c9p-260) (f64.const -0x1.c1201c0470205p-253)) (f64.const -0x1.841ada2e0f184p-512)) +(assert_return (invoke "f64.no_factor" (f64.const 0x1.9d3688f8e375ap-608) (f64.const 0x1.bf91311588256p-579) (f64.const -0x1.1605a6b5d5ff8p+489)) (f64.const -0x1.e6118ca76af53p-90)) + +;; Test that (x+y)*z is not folded to (x*z)+(y*z). + +(module + (func (export "f32.no_distribute") (param $x f32) (param $y f32) (param $z f32) (result f32) + (f32.mul (f32.add (local.get $x) (local.get $y)) (local.get $z))) + (func (export "f64.no_distribute") (param $x f64) (param $y f64) (param $z f64) (result f64) + (f64.mul (f64.add (local.get $x) (local.get $y)) (local.get $z))) +) + +(assert_return (invoke "f32.no_distribute" (f32.const -0x1.4e2352p+40) (f32.const -0x1.842e2cp+49) (f32.const 0x1.eea602p+59)) (f32.const -0x1.77a7d2p+109)) +(assert_return (invoke "f32.no_distribute" (f32.const -0x1.b4e7f6p-6) (f32.const 0x1.8c990cp-5) (f32.const -0x1.70cc02p-9)) (f32.const -0x1.00a34p-14)) +(assert_return (invoke "f32.no_distribute" (f32.const -0x1.06722ep-41) (f32.const 0x1.eed3cep-64) (f32.const 0x1.5c5558p+123)) (f32.const -0x1.651abp+82)) +(assert_return (invoke "f32.no_distribute" (f32.const -0x1.f8c6a4p-64) (f32.const 0x1.08c806p-83) (f32.const 0x1.b5ceccp+118)) (f32.const -0x1.afa14ep+55)) +(assert_return (invoke "f32.no_distribute" (f32.const -0x1.3aaa1ep-84) (f32.const 0x1.c6d5eep-71) (f32.const 0x1.8d2924p+20)) (f32.const 0x1.60c9ccp-50)) +(assert_return (invoke "f64.no_distribute" (f64.const 0x1.3adeda9144977p-424) (f64.const 0x1.c15af887049e1p-462) (f64.const -0x1.905179c4c4778p-225)) (f64.const -0x1.ec606bcb87b1bp-649)) +(assert_return (invoke "f64.no_distribute" (f64.const 0x1.3c84821c1d348p-662) (f64.const -0x1.4ffd4c77ad037p-1009) (f64.const -0x1.dd275335c6f4p-957)) (f64.const -0x0p+0)) +(assert_return (invoke "f64.no_distribute" (f64.const -0x1.074f372347051p-334) (f64.const -0x1.aaeef661f4c96p-282) (f64.const -0x1.9bd34abe8696dp+479)) (f64.const 0x1.5767029593e1fp+198)) +(assert_return (invoke "f64.no_distribute" (f64.const -0x1.c4ded58a6f389p-289) (f64.const 0x1.ba6fdef5d59c9p-260) (f64.const -0x1.c1201c0470205p-253)) (f64.const -0x1.841ada2e0f183p-512)) +(assert_return (invoke "f64.no_distribute" (f64.const 0x1.9d3688f8e375ap-608) (f64.const 0x1.bf91311588256p-579) (f64.const -0x1.1605a6b5d5ff8p+489)) (f64.const -0x1.e6118ca76af52p-90)) + +;; Test that x*(y/z) is not folded to (x*y)/z. + +(module + (func (export "f32.no_regroup_div_mul") (param $x f32) (param $y f32) (param $z f32) (result f32) + (f32.mul (local.get $x) (f32.div (local.get $y) (local.get $z)))) + (func (export "f64.no_regroup_div_mul") (param $x f64) (param $y f64) (param $z f64) (result f64) + (f64.mul (local.get $x) (f64.div (local.get $y) (local.get $z)))) +) + +(assert_return (invoke "f32.no_regroup_div_mul" (f32.const -0x1.2d14a6p-115) (f32.const -0x1.575a6cp-64) (f32.const 0x1.5cee0ep-116)) (f32.const 0x1.2844cap-63)) +(assert_return (invoke "f32.no_regroup_div_mul" (f32.const -0x1.454738p+91) (f32.const -0x1.b28a66p-115) (f32.const -0x1.f53908p+72)) (f32.const -0x0p+0)) +(assert_return (invoke "f32.no_regroup_div_mul" (f32.const -0x1.6be56ep+16) (f32.const -0x1.b46fc6p-21) (f32.const -0x1.a51df6p-123)) (f32.const -0x1.792258p+118)) +(assert_return (invoke "f32.no_regroup_div_mul" (f32.const -0x1.c343f8p-94) (f32.const 0x1.e4d906p+73) (f32.const 0x1.be69f8p+68)) (f32.const -0x1.ea1df2p-89)) +(assert_return (invoke "f32.no_regroup_div_mul" (f32.const 0x1.c6ae76p+112) (f32.const 0x1.fc953cp+24) (f32.const -0x1.60b3e8p+71)) (f32.const -0x1.47d0eap+66)) +(assert_return (invoke "f64.no_regroup_div_mul" (f64.const 0x1.3c04b815e30bp-423) (f64.const -0x1.379646fd98127p-119) (f64.const 0x1.bddb158506031p-642)) (f64.const -0x1.b9b3301f2dd2dp+99)) +(assert_return (invoke "f64.no_regroup_div_mul" (f64.const 0x1.46b3a402f86d5p+337) (f64.const 0x1.6fbf1b9e1798dp-447) (f64.const -0x1.bd9704a5a6a06p+797)) (f64.const -0x0p+0)) +(assert_return (invoke "f64.no_regroup_div_mul" (f64.const 0x1.6c9765bb4347fp-479) (f64.const 0x1.a4af42e34a141p+902) (f64.const 0x1.d2dde70eb68f9p-448)) (f64.const inf)) +(assert_return (invoke "f64.no_regroup_div_mul" (f64.const -0x1.706023645be72p+480) (f64.const -0x1.6c229f7d9101dp+611) (f64.const -0x1.4d50fa68d3d9ep+836)) (f64.const -0x1.926fa3cacc651p+255)) +(assert_return (invoke "f64.no_regroup_div_mul" (f64.const 0x1.8cc63d8caf4c7p-599) (f64.const 0x1.8671ac4c35753p-878) (f64.const -0x1.ef35b1695e659p-838)) (f64.const -0x1.38d55f56406dp-639)) + +;; Test that (x*y)/z is not folded to x*(y/z). + +(module + (func (export "f32.no_regroup_mul_div") (param $x f32) (param $y f32) (param $z f32) (result f32) + (f32.div (f32.mul (local.get $x) (local.get $y)) (local.get $z))) + (func (export "f64.no_regroup_mul_div") (param $x f64) (param $y f64) (param $z f64) (result f64) + (f64.div (f64.mul (local.get $x) (local.get $y)) (local.get $z))) +) + +(assert_return (invoke "f32.no_regroup_mul_div" (f32.const -0x1.2d14a6p-115) (f32.const -0x1.575a6cp-64) (f32.const 0x1.5cee0ep-116)) (f32.const 0x0p+0)) +(assert_return (invoke "f32.no_regroup_mul_div" (f32.const -0x1.454738p+91) (f32.const -0x1.b28a66p-115) (f32.const -0x1.f53908p+72)) (f32.const -0x1.1a00e8p-96)) +(assert_return (invoke "f32.no_regroup_mul_div" (f32.const -0x1.6be56ep+16) (f32.const -0x1.b46fc6p-21) (f32.const -0x1.a51df6p-123)) (f32.const -0x1.79225ap+118)) +(assert_return (invoke "f32.no_regroup_mul_div" (f32.const -0x1.c343f8p-94) (f32.const 0x1.e4d906p+73) (f32.const 0x1.be69f8p+68)) (f32.const -0x1.ea1df4p-89)) +(assert_return (invoke "f32.no_regroup_mul_div" (f32.const 0x1.c6ae76p+112) (f32.const 0x1.fc953cp+24) (f32.const -0x1.60b3e8p+71)) (f32.const -inf)) +(assert_return (invoke "f64.no_regroup_mul_div" (f64.const 0x1.3c04b815e30bp-423) (f64.const -0x1.379646fd98127p-119) (f64.const 0x1.bddb158506031p-642)) (f64.const -0x1.b9b3301f2dd2ep+99)) +(assert_return (invoke "f64.no_regroup_mul_div" (f64.const 0x1.46b3a402f86d5p+337) (f64.const 0x1.6fbf1b9e1798dp-447) (f64.const -0x1.bd9704a5a6a06p+797)) (f64.const -0x1.0da0b6328e09p-907)) +(assert_return (invoke "f64.no_regroup_mul_div" (f64.const 0x1.6c9765bb4347fp-479) (f64.const 0x1.a4af42e34a141p+902) (f64.const 0x1.d2dde70eb68f9p-448)) (f64.const 0x1.4886b6d9a9a79p+871)) +(assert_return (invoke "f64.no_regroup_mul_div" (f64.const -0x1.706023645be72p+480) (f64.const -0x1.6c229f7d9101dp+611) (f64.const -0x1.4d50fa68d3d9ep+836)) (f64.const -inf)) +(assert_return (invoke "f64.no_regroup_mul_div" (f64.const 0x1.8cc63d8caf4c7p-599) (f64.const 0x1.8671ac4c35753p-878) (f64.const -0x1.ef35b1695e659p-838)) (f64.const -0x0p+0)) + +;; Test that x+y+z+w is not reassociated. + +(module + (func (export "f32.no_reassociate_add") (param $x f32) (param $y f32) (param $z f32) (param $w f32) (result f32) + (f32.add (f32.add (f32.add (local.get $x) (local.get $y)) (local.get $z)) (local.get $w))) + (func (export "f64.no_reassociate_add") (param $x f64) (param $y f64) (param $z f64) (param $w f64) (result f64) + (f64.add (f64.add (f64.add (local.get $x) (local.get $y)) (local.get $z)) (local.get $w))) +) + +(assert_return (invoke "f32.no_reassociate_add" (f32.const -0x1.5f7ddcp+44) (f32.const 0x1.854e1p+34) (f32.const -0x1.b2068cp+47) (f32.const -0x1.209692p+41)) (f32.const -0x1.e26c76p+47)) +(assert_return (invoke "f32.no_reassociate_add" (f32.const 0x1.da3b78p-9) (f32.const -0x1.4312fap-7) (f32.const 0x1.0395e6p-4) (f32.const -0x1.6d5ea6p-7)) (f32.const 0x1.78b31ap-5)) +(assert_return (invoke "f32.no_reassociate_add" (f32.const -0x1.fdb93ap+34) (f32.const -0x1.b6fce6p+41) (f32.const 0x1.c131d8p+44) (f32.const 0x1.8835b6p+38)) (f32.const 0x1.8ff3a2p+44)) +(assert_return (invoke "f32.no_reassociate_add" (f32.const 0x1.1739fcp+47) (f32.const 0x1.a4b186p+49) (f32.const -0x1.0c623cp+35) (f32.const 0x1.16a102p+51)) (f32.const 0x1.913ff6p+51)) +(assert_return (invoke "f32.no_reassociate_add" (f32.const 0x1.733cfap+108) (f32.const -0x1.38d30cp+108) (f32.const 0x1.2f5854p+105) (f32.const -0x1.ccb058p+94)) (f32.const 0x1.813716p+106)) +(assert_return (invoke "f64.no_reassociate_add" (f64.const -0x1.697a4d9ff19a6p+841) (f64.const 0x1.b305466238397p+847) (f64.const 0x1.e0b2d9bfb4e72p+855) (f64.const -0x1.6e1f3ae2b06bbp+857)) (f64.const -0x1.eb0e5936f087ap+856)) +(assert_return (invoke "f64.no_reassociate_add" (f64.const 0x1.00ef6746b30e1p-543) (f64.const 0x1.cc1cfafdf3fe1p-544) (f64.const -0x1.f7726df3ecba6p-543) (f64.const -0x1.b26695f99d307p-594)) (f64.const -0x1.074892e3fad76p-547)) +(assert_return (invoke "f64.no_reassociate_add" (f64.const -0x1.e807b3bd6d854p+440) (f64.const 0x1.cedae26c2c5fp+407) (f64.const -0x1.00ab6e1442541p+437) (f64.const 0x1.28538a55997bdp+397)) (f64.const -0x1.040e90bf871ebp+441)) +(assert_return (invoke "f64.no_reassociate_add" (f64.const -0x1.ba2b6f35a2402p-317) (f64.const 0x1.ad1c3fea7cd9ep-307) (f64.const -0x1.93aace2bf1261p-262) (f64.const 0x1.9fddbe472847ep-260)) (f64.const 0x1.3af30abc2c01bp-260)) +(assert_return (invoke "f64.no_reassociate_add" (f64.const -0x1.ccb9c6092fb1dp+641) (f64.const -0x1.4b7c28c108244p+614) (f64.const 0x1.8a7cefef4bde1p+646) (f64.const -0x1.901b28b08b482p+644)) (f64.const 0x1.1810579194126p+646)) + +;; Test that x*y*z*w is not reassociated. + +(module + (func (export "f32.no_reassociate_mul") (param $x f32) (param $y f32) (param $z f32) (param $w f32) (result f32) + (f32.mul (f32.mul (f32.mul (local.get $x) (local.get $y)) (local.get $z)) (local.get $w))) + (func (export "f64.no_reassociate_mul") (param $x f64) (param $y f64) (param $z f64) (param $w f64) (result f64) + (f64.mul (f64.mul (f64.mul (local.get $x) (local.get $y)) (local.get $z)) (local.get $w))) +) + +(assert_return (invoke "f32.no_reassociate_mul" (f32.const 0x1.950ba8p-116) (f32.const 0x1.efdacep-33) (f32.const -0x1.5f9bcp+102) (f32.const 0x1.f04508p-56)) (f32.const -0x1.ff356ep-101)) +(assert_return (invoke "f32.no_reassociate_mul" (f32.const 0x1.5990aep-56) (f32.const -0x1.7dfb04p+102) (f32.const -0x1.4f774ap-125) (f32.const -0x1.595fe6p+70)) (f32.const -0x1.c7c8fcp-8)) +(assert_return (invoke "f32.no_reassociate_mul" (f32.const 0x1.6ad9a4p-48) (f32.const -0x1.9138aap+55) (f32.const -0x1.4a774ep-40) (f32.const 0x1.1ff08p+76)) (f32.const 0x1.9cd8ecp+44)) +(assert_return (invoke "f32.no_reassociate_mul" (f32.const 0x1.e1caecp-105) (f32.const 0x1.af0dd2p+77) (f32.const -0x1.016eep+56) (f32.const -0x1.ab70d6p+59)) (f32.const 0x1.54870ep+89)) +(assert_return (invoke "f32.no_reassociate_mul" (f32.const -0x1.3b1dcp-99) (f32.const 0x1.4e5a34p-49) (f32.const -0x1.38ba5ap+3) (f32.const 0x1.7fb8eep+59)) (f32.const 0x1.5bbf98p-85)) +(assert_return (invoke "f64.no_reassociate_mul" (f64.const -0x1.e7842ab7181p-667) (f64.const -0x1.fabf40ceeceafp+990) (f64.const -0x1.1a38a825ab01ap-376) (f64.const -0x1.27e8ea469b14fp+664)) (f64.const 0x1.336eb428af4f3p+613)) +(assert_return (invoke "f64.no_reassociate_mul" (f64.const 0x1.4ca2292a6acbcp+454) (f64.const 0x1.6ffbab850089ap-516) (f64.const -0x1.547c32e1f5b93p-899) (f64.const -0x1.c7571d9388375p+540)) (f64.const 0x1.1ac796954fc1p-419)) +(assert_return (invoke "f64.no_reassociate_mul" (f64.const 0x1.73881a52e0401p-501) (f64.const -0x1.1b68dd9efb1a7p+788) (f64.const 0x1.d1c5e6a3eb27cp-762) (f64.const -0x1.56cb2fcc7546fp+88)) (f64.const 0x1.f508db92c34efp-386)) +(assert_return (invoke "f64.no_reassociate_mul" (f64.const 0x1.2efa87859987cp+692) (f64.const 0x1.68e4373e241p-423) (f64.const 0x1.4e2d0fb383a57p+223) (f64.const -0x1.301d3265c737bp-23)) (f64.const -0x1.4b2b6c393f30cp+470)) +(assert_return (invoke "f64.no_reassociate_mul" (f64.const 0x1.1013f7498b95fp-234) (f64.const 0x1.d2d1c36fff138p-792) (f64.const -0x1.cbf1824ea7bfdp+728) (f64.const -0x1.440da9c8b836dp-599)) (f64.const 0x1.1a16512881c91p-895)) + +;; Test that x/0 is not folded away. + +(module + (func (export "f32.no_fold_div_0") (param $x f32) (result f32) + (f32.div (local.get $x) (f32.const 0.0))) + (func (export "f64.no_fold_div_0") (param $x f64) (result f64) + (f64.div (local.get $x) (f64.const 0.0))) +) + +(assert_return (invoke "f32.no_fold_div_0" (f32.const 1.0)) (f32.const inf)) +(assert_return (invoke "f32.no_fold_div_0" (f32.const -1.0)) (f32.const -inf)) +(assert_return (invoke "f32.no_fold_div_0" (f32.const inf)) (f32.const inf)) +(assert_return (invoke "f32.no_fold_div_0" (f32.const -inf)) (f32.const -inf)) +(assert_return (invoke "f32.no_fold_div_0" (f32.const 0)) (f32.const nan:canonical)) +(assert_return (invoke "f32.no_fold_div_0" (f32.const -0)) (f32.const nan:canonical)) +(assert_return (invoke "f32.no_fold_div_0" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f32.no_fold_div_0" (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "f64.no_fold_div_0" (f64.const 1.0)) (f64.const inf)) +(assert_return (invoke "f64.no_fold_div_0" (f64.const -1.0)) (f64.const -inf)) +(assert_return (invoke "f64.no_fold_div_0" (f64.const inf)) (f64.const inf)) +(assert_return (invoke "f64.no_fold_div_0" (f64.const -inf)) (f64.const -inf)) +(assert_return (invoke "f64.no_fold_div_0" (f64.const 0)) (f64.const nan:canonical)) +(assert_return (invoke "f64.no_fold_div_0" (f64.const -0)) (f64.const nan:canonical)) +(assert_return (invoke "f64.no_fold_div_0" (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "f64.no_fold_div_0" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) + +;; Test that x/-0 is not folded away. + +(module + (func (export "f32.no_fold_div_neg0") (param $x f32) (result f32) + (f32.div (local.get $x) (f32.const -0.0))) + (func (export "f64.no_fold_div_neg0") (param $x f64) (result f64) + (f64.div (local.get $x) (f64.const -0.0))) +) + +(assert_return (invoke "f32.no_fold_div_neg0" (f32.const 1.0)) (f32.const -inf)) +(assert_return (invoke "f32.no_fold_div_neg0" (f32.const -1.0)) (f32.const inf)) +(assert_return (invoke "f32.no_fold_div_neg0" (f32.const inf)) (f32.const -inf)) +(assert_return (invoke "f32.no_fold_div_neg0" (f32.const -inf)) (f32.const inf)) +(assert_return (invoke "f32.no_fold_div_neg0" (f32.const 0)) (f32.const nan:canonical)) +(assert_return (invoke "f32.no_fold_div_neg0" (f32.const -0)) (f32.const nan:canonical)) +(assert_return (invoke "f32.no_fold_div_neg0" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f32.no_fold_div_neg0" (f32.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "f64.no_fold_div_neg0" (f64.const 1.0)) (f64.const -inf)) +(assert_return (invoke "f64.no_fold_div_neg0" (f64.const -1.0)) (f64.const inf)) +(assert_return (invoke "f64.no_fold_div_neg0" (f64.const inf)) (f64.const -inf)) +(assert_return (invoke "f64.no_fold_div_neg0" (f64.const -inf)) (f64.const inf)) +(assert_return (invoke "f64.no_fold_div_neg0" (f64.const 0)) (f64.const nan:canonical)) +(assert_return (invoke "f64.no_fold_div_neg0" (f64.const -0)) (f64.const nan:canonical)) +(assert_return (invoke "f64.no_fold_div_neg0" (f64.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "f64.no_fold_div_neg0" (f64.const nan:0x4000000000000)) (f64.const nan:arithmetic)) + +;; Test that sqrt(x*x+y*y) is not folded to hypot. + +(module + (func (export "f32.no_fold_to_hypot") (param $x f32) (param $y f32) (result f32) + (f32.sqrt (f32.add (f32.mul (local.get $x) (local.get $x)) + (f32.mul (local.get $y) (local.get $y))))) + (func (export "f64.no_fold_to_hypot") (param $x f64) (param $y f64) (result f64) + (f64.sqrt (f64.add (f64.mul (local.get $x) (local.get $x)) + (f64.mul (local.get $y) (local.get $y))))) +) + +(assert_return (invoke "f32.no_fold_to_hypot" (f32.const 0x1.c2f338p-81) (f32.const 0x1.401b5ep-68)) (f32.const 0x1.401cccp-68)) +(assert_return (invoke "f32.no_fold_to_hypot" (f32.const -0x1.c38d1p-71) (f32.const -0x1.359ddp-107)) (f32.const 0x1.c36a62p-71)) +(assert_return (invoke "f32.no_fold_to_hypot" (f32.const -0x1.99e0cap-114) (f32.const -0x1.ed0c6cp-69)) (f32.const 0x1.ed0e48p-69)) +(assert_return (invoke "f32.no_fold_to_hypot" (f32.const -0x1.1b6ceap+5) (f32.const 0x1.5440bep+17)) (f32.const 0x1.5440cp+17)) +(assert_return (invoke "f32.no_fold_to_hypot" (f32.const 0x1.8f019ep-76) (f32.const -0x1.182308p-71)) (f32.const 0x1.17e2bcp-71)) +(assert_return (invoke "f64.no_fold_to_hypot" (f64.const 0x1.1a0ac4f7c8711p-636) (f64.const 0x1.1372ebafff551p-534)) (f64.const 0x1.13463fa37014ep-534)) +(assert_return (invoke "f64.no_fold_to_hypot" (f64.const 0x1.b793512167499p+395) (f64.const -0x1.11cbc52af4c36p+410)) (f64.const 0x1.11cbc530783a2p+410)) +(assert_return (invoke "f64.no_fold_to_hypot" (f64.const 0x1.76777f44ff40bp-536) (f64.const -0x1.c3896e4dc1fbp-766)) (f64.const 0x1.8p-536)) +(assert_return (invoke "f64.no_fold_to_hypot" (f64.const -0x1.889ac72cc6b5dp-521) (f64.const 0x1.8d7084e659f3bp-733)) (f64.const 0x1.889ac72ca843ap-521)) +(assert_return (invoke "f64.no_fold_to_hypot" (f64.const 0x1.5ee588c02cb08p-670) (f64.const -0x1.05ce25788d9ecp-514)) (f64.const 0x1.05ce25788d9dfp-514)) + +;; Test that 1.0/x isn't approximated. + +(module + (func (export "f32.no_approximate_reciprocal") (param $x f32) (result f32) + (f32.div (f32.const 1.0) (local.get $x))) +) + +(assert_return (invoke "f32.no_approximate_reciprocal" (f32.const -0x1.2900b6p-10)) (f32.const -0x1.b950d4p+9)) +(assert_return (invoke "f32.no_approximate_reciprocal" (f32.const 0x1.e7212p+127)) (f32.const 0x1.0d11f8p-128)) +(assert_return (invoke "f32.no_approximate_reciprocal" (f32.const -0x1.42a466p-93)) (f32.const -0x1.963ee6p+92)) +(assert_return (invoke "f32.no_approximate_reciprocal" (f32.const 0x1.5d0c32p+76)) (f32.const 0x1.778362p-77)) +(assert_return (invoke "f32.no_approximate_reciprocal" (f32.const -0x1.601de2p-82)) (f32.const -0x1.743d7ep+81)) + +;; Test that 1.0/sqrt(x) isn't approximated or fused. + +(module + (func (export "f32.no_approximate_reciprocal_sqrt") (param $x f32) (result f32) + (f32.div (f32.const 1.0) (f32.sqrt (local.get $x)))) + (func (export "f64.no_fuse_reciprocal_sqrt") (param $x f64) (result f64) + (f64.div (f64.const 1.0) (f64.sqrt (local.get $x)))) +) + +(assert_return (invoke "f32.no_approximate_reciprocal_sqrt" (f32.const 0x1.6af12ap-43)) (f32.const 0x1.300ed4p+21)) +(assert_return (invoke "f32.no_approximate_reciprocal_sqrt" (f32.const 0x1.e82fc6p-8)) (f32.const 0x1.72c376p+3)) +(assert_return (invoke "f32.no_approximate_reciprocal_sqrt" (f32.const 0x1.b9fa9cp-66)) (f32.const 0x1.85a9bap+32)) +(assert_return (invoke "f32.no_approximate_reciprocal_sqrt" (f32.const 0x1.f4f546p-44)) (f32.const 0x1.6e01c2p+21)) +(assert_return (invoke "f32.no_approximate_reciprocal_sqrt" (f32.const 0x1.5da7aap-86)) (f32.const 0x1.b618cap+42)) + +(assert_return (invoke "f64.no_fuse_reciprocal_sqrt" (f64.const 0x1.1568a63b55fa3p+889)) (f64.const 0x1.5bc9c74c9952p-445)) +(assert_return (invoke "f64.no_fuse_reciprocal_sqrt" (f64.const 0x1.239fcd0939cafp+311)) (f64.const 0x1.5334a922b4818p-156)) +(assert_return (invoke "f64.no_fuse_reciprocal_sqrt" (f64.const 0x1.6e36a24e11054p+104)) (f64.const 0x1.ac13f20977f29p-53)) +(assert_return (invoke "f64.no_fuse_reciprocal_sqrt" (f64.const 0x1.23ee173219f83p+668)) (f64.const 0x1.df753e055862dp-335)) +(assert_return (invoke "f64.no_fuse_reciprocal_sqrt" (f64.const 0x1.b30f74caf9babp+146)) (f64.const 0x1.88bfc3d1764a9p-74)) + +;; Test that sqrt(1.0/x) isn't approximated. + +(module + (func (export "f32.no_approximate_sqrt_reciprocal") (param $x f32) (result f32) + (f32.sqrt (f32.div (f32.const 1.0) (local.get $x)))) +) + +(assert_return (invoke "f32.no_approximate_sqrt_reciprocal" (f32.const 0x1.a4c986p+60)) (f32.const 0x1.8f5ac6p-31)) +(assert_return (invoke "f32.no_approximate_sqrt_reciprocal" (f32.const 0x1.50511ep-9)) (f32.const 0x1.3bdd46p+4)) +(assert_return (invoke "f32.no_approximate_sqrt_reciprocal" (f32.const 0x1.125ec2p+69)) (f32.const 0x1.5db572p-35)) +(assert_return (invoke "f32.no_approximate_sqrt_reciprocal" (f32.const 0x1.ba4c5p+13)) (f32.const 0x1.136f16p-7)) +(assert_return (invoke "f32.no_approximate_sqrt_reciprocal" (f32.const 0x1.4a5be2p+104)) (f32.const 0x1.c2b5bp-53)) + +;; Test that converting i32/i64 to f32/f64 and back isn't folded away. + +(module + (func (export "i32.no_fold_f32_s") (param i32) (result i32) + (i32.trunc_f32_s (f32.convert_i32_s (local.get 0)))) + (func (export "i32.no_fold_f32_u") (param i32) (result i32) + (i32.trunc_f32_u (f32.convert_i32_u (local.get 0)))) + (func (export "i64.no_fold_f64_s") (param i64) (result i64) + (i64.trunc_f64_s (f64.convert_i64_s (local.get 0)))) + (func (export "i64.no_fold_f64_u") (param i64) (result i64) + (i64.trunc_f64_u (f64.convert_i64_u (local.get 0)))) +) + +(assert_return (invoke "i32.no_fold_f32_s" (i32.const 0x1000000)) (i32.const 0x1000000)) +(assert_return (invoke "i32.no_fold_f32_s" (i32.const 0x1000001)) (i32.const 0x1000000)) +(assert_return (invoke "i32.no_fold_f32_s" (i32.const 0xf0000010)) (i32.const 0xf0000010)) + +(assert_return (invoke "i32.no_fold_f32_u" (i32.const 0x1000000)) (i32.const 0x1000000)) +(assert_return (invoke "i32.no_fold_f32_u" (i32.const 0x1000001)) (i32.const 0x1000000)) +(assert_return (invoke "i32.no_fold_f32_u" (i32.const 0xf0000010)) (i32.const 0xf0000000)) + +(assert_return (invoke "i64.no_fold_f64_s" (i64.const 0x20000000000000)) (i64.const 0x20000000000000)) +(assert_return (invoke "i64.no_fold_f64_s" (i64.const 0x20000000000001)) (i64.const 0x20000000000000)) +(assert_return (invoke "i64.no_fold_f64_s" (i64.const 0xf000000000000400)) (i64.const 0xf000000000000400)) + +(assert_return (invoke "i64.no_fold_f64_u" (i64.const 0x20000000000000)) (i64.const 0x20000000000000)) +(assert_return (invoke "i64.no_fold_f64_u" (i64.const 0x20000000000001)) (i64.const 0x20000000000000)) +(assert_return (invoke "i64.no_fold_f64_u" (i64.const 0xf000000000000400)) (i64.const 0xf000000000000000)) + +;; Test that x+y-y is not folded to x. + +(module + (func (export "f32.no_fold_add_sub") (param $x f32) (param $y f32) (result f32) + (f32.sub (f32.add (local.get $x) (local.get $y)) (local.get $y))) + (func (export "f64.no_fold_add_sub") (param $x f64) (param $y f64) (result f64) + (f64.sub (f64.add (local.get $x) (local.get $y)) (local.get $y))) +) + +(assert_return (invoke "f32.no_fold_add_sub" (f32.const 0x1.b553e4p-47) (f32.const -0x1.67db2cp-26)) (f32.const 0x1.cp-47)) +(assert_return (invoke "f32.no_fold_add_sub" (f32.const -0x1.a884dp-23) (f32.const 0x1.f2ae1ep-19)) (f32.const -0x1.a884ep-23)) +(assert_return (invoke "f32.no_fold_add_sub" (f32.const -0x1.fc04fp+82) (f32.const -0x1.65403ap+101)) (f32.const -0x1p+83)) +(assert_return (invoke "f32.no_fold_add_sub" (f32.const 0x1.870fa2p-78) (f32.const 0x1.c54916p-56)) (f32.const 0x1.8p-78)) +(assert_return (invoke "f32.no_fold_add_sub" (f32.const -0x1.17e966p-108) (f32.const -0x1.5fa61ap-84)) (f32.const -0x1p-107)) + +(assert_return (invoke "f64.no_fold_add_sub" (f64.const -0x1.1053ea172dba8p-874) (f64.const 0x1.113c413408ac8p-857)) (f64.const -0x1.1053ea172p-874)) +(assert_return (invoke "f64.no_fold_add_sub" (f64.const 0x1.e377d54807972p-546) (f64.const 0x1.040a0a4d1ff7p-526)) (f64.const 0x1.e377d548p-546)) +(assert_return (invoke "f64.no_fold_add_sub" (f64.const -0x1.75f53cd926b62p-30) (f64.const -0x1.66b176e602bb5p-3)) (f64.const -0x1.75f53dp-30)) +(assert_return (invoke "f64.no_fold_add_sub" (f64.const -0x1.c450ff28332ap-341) (f64.const 0x1.15a5855023baep-305)) (f64.const -0x1.c451p-341)) +(assert_return (invoke "f64.no_fold_add_sub" (f64.const -0x1.1ad4a596d3ea8p-619) (f64.const -0x1.17d81a41c0ea8p-588)) (f64.const -0x1.1ad4a8p-619)) + +;; Test that x-y+y is not folded to x. + +(module + (func (export "f32.no_fold_sub_add") (param $x f32) (param $y f32) (result f32) + (f32.add (f32.sub (local.get $x) (local.get $y)) (local.get $y))) + (func (export "f64.no_fold_sub_add") (param $x f64) (param $y f64) (result f64) + (f64.add (f64.sub (local.get $x) (local.get $y)) (local.get $y))) +) + +(assert_return (invoke "f32.no_fold_sub_add" (f32.const -0x1.523cb8p+9) (f32.const 0x1.93096cp+8)) (f32.const -0x1.523cbap+9)) +(assert_return (invoke "f32.no_fold_sub_add" (f32.const -0x1.a31a1p-111) (f32.const 0x1.745efp-95)) (f32.const -0x1.a4p-111)) +(assert_return (invoke "f32.no_fold_sub_add" (f32.const 0x1.3d5328p+26) (f32.const 0x1.58567p+35)) (f32.const 0x1.3d54p+26)) +(assert_return (invoke "f32.no_fold_sub_add" (f32.const 0x1.374e26p-39) (f32.const -0x1.66a5p-27)) (f32.const 0x1.374p-39)) +(assert_return (invoke "f32.no_fold_sub_add" (f32.const 0x1.320facp-3) (f32.const -0x1.ac069ap+14)) (f32.const 0x1.34p-3)) + +(assert_return (invoke "f64.no_fold_sub_add" (f64.const 0x1.8f92aad2c9b8dp+255) (f64.const -0x1.08cd4992266cbp+259)) (f64.const 0x1.8f92aad2c9b9p+255)) +(assert_return (invoke "f64.no_fold_sub_add" (f64.const 0x1.5aaff55742c8bp-666) (f64.const 0x1.8f5f47181f46dp-647)) (f64.const 0x1.5aaff5578p-666)) +(assert_return (invoke "f64.no_fold_sub_add" (f64.const 0x1.21bc52967a98dp+251) (f64.const -0x1.fcffaa32d0884p+300)) (f64.const 0x1.2p+251)) +(assert_return (invoke "f64.no_fold_sub_add" (f64.const 0x1.9c78361f47374p-26) (f64.const -0x1.69d69f4edc61cp-13)) (f64.const 0x1.9c78361f48p-26)) +(assert_return (invoke "f64.no_fold_sub_add" (f64.const 0x1.4dbe68e4afab2p-367) (f64.const -0x1.dc24e5b39cd02p-361)) (f64.const 0x1.4dbe68e4afacp-367)) + +;; Test that x*y/y is not folded to x. + +(module + (func (export "f32.no_fold_mul_div") (param $x f32) (param $y f32) (result f32) + (f32.div (f32.mul (local.get $x) (local.get $y)) (local.get $y))) + (func (export "f64.no_fold_mul_div") (param $x f64) (param $y f64) (result f64) + (f64.div (f64.mul (local.get $x) (local.get $y)) (local.get $y))) +) + +(assert_return (invoke "f32.no_fold_mul_div" (f32.const -0x1.cd859ap+54) (f32.const 0x1.6ca936p-47)) (f32.const -0x1.cd8598p+54)) +(assert_return (invoke "f32.no_fold_mul_div" (f32.const -0x1.0b56b8p-26) (f32.const 0x1.48264cp-106)) (f32.const -0x1.0b56a4p-26)) +(assert_return (invoke "f32.no_fold_mul_div" (f32.const -0x1.e7555cp-48) (f32.const -0x1.9161cp+48)) (f32.const -0x1.e7555ap-48)) +(assert_return (invoke "f32.no_fold_mul_div" (f32.const 0x1.aaa50ep+52) (f32.const -0x1.dfb39ep+60)) (f32.const 0x1.aaa50cp+52)) +(assert_return (invoke "f32.no_fold_mul_div" (f32.const -0x1.2b7dfap-92) (f32.const -0x1.7c4ca6p-37)) (f32.const -0x1.2b7dfep-92)) + +(assert_return (invoke "f64.no_fold_mul_div" (f64.const -0x1.3d79ff4118a1ap-837) (f64.const -0x1.b8b5dda31808cp-205)) (f64.const -0x1.3d79ff412263ep-837)) +(assert_return (invoke "f64.no_fold_mul_div" (f64.const 0x1.f894d1ee6b3a4p+384) (f64.const 0x1.8c2606d03d58ap+585)) (f64.const 0x1.f894d1ee6b3a5p+384)) +(assert_return (invoke "f64.no_fold_mul_div" (f64.const -0x1.a022260acc993p+238) (f64.const -0x1.5fbc128fc8e3cp-552)) (f64.const -0x1.a022260acc992p+238)) +(assert_return (invoke "f64.no_fold_mul_div" (f64.const 0x1.9d4b8ed174f54p-166) (f64.const 0x1.ee3d467aeeac6p-906)) (f64.const 0x1.8dcc95a053b2bp-166)) +(assert_return (invoke "f64.no_fold_mul_div" (f64.const -0x1.e95ea897cdcd4p+660) (f64.const -0x1.854d5df085f2ep-327)) (f64.const -0x1.e95ea897cdcd5p+660)) + +;; Test that x/y*y is not folded to x. + +(module + (func (export "f32.no_fold_div_mul") (param $x f32) (param $y f32) (result f32) + (f32.mul (f32.div (local.get $x) (local.get $y)) (local.get $y))) + (func (export "f64.no_fold_div_mul") (param $x f64) (param $y f64) (result f64) + (f64.mul (f64.div (local.get $x) (local.get $y)) (local.get $y))) +) + +(assert_return (invoke "f32.no_fold_div_mul" (f32.const -0x1.dc6364p+38) (f32.const 0x1.d630ecp+29)) (f32.const -0x1.dc6362p+38)) +(assert_return (invoke "f32.no_fold_div_mul" (f32.const -0x1.1f9836p-52) (f32.const -0x1.16c4e4p-18)) (f32.const -0x1.1f9838p-52)) +(assert_return (invoke "f32.no_fold_div_mul" (f32.const 0x1.c5972cp-126) (f32.const -0x1.d6659ep+7)) (f32.const 0x1.c5980ep-126)) +(assert_return (invoke "f32.no_fold_div_mul" (f32.const -0x1.2e3a9ep-74) (f32.const -0x1.353994p+59)) (f32.const -0x1.2e3a4p-74)) +(assert_return (invoke "f32.no_fold_div_mul" (f32.const 0x1.d96b82p-98) (f32.const 0x1.95d908p+27)) (f32.const 0x1.d96b84p-98)) + +(assert_return (invoke "f64.no_fold_div_mul" (f64.const 0x1.d01f913a52481p-876) (f64.const -0x1.2cd0668b28344p+184)) (f64.const 0x1.d020daf71cdcp-876)) +(assert_return (invoke "f64.no_fold_div_mul" (f64.const -0x1.81cb7d400918dp-714) (f64.const 0x1.7caa643586d6ep-53)) (f64.const -0x1.81cb7d400918ep-714)) +(assert_return (invoke "f64.no_fold_div_mul" (f64.const -0x1.66904c97b5c8ep-145) (f64.const 0x1.5c3481592ad4cp+428)) (f64.const -0x1.66904c97b5c8dp-145)) +(assert_return (invoke "f64.no_fold_div_mul" (f64.const -0x1.e75859d2f0765p-278) (f64.const -0x1.5f19b6ab497f9p+283)) (f64.const -0x1.e75859d2f0764p-278)) +(assert_return (invoke "f64.no_fold_div_mul" (f64.const -0x1.515fe9c3b5f5p+620) (f64.const 0x1.36be869c99f7ap+989)) (f64.const -0x1.515fe9c3b5f4fp+620)) + +;; Test that x/2*2 is not folded to x. + +(module + (func (export "f32.no_fold_div2_mul2") (param $x f32) (result f32) + (f32.mul (f32.div (local.get $x) (f32.const 2.0)) (f32.const 2.0))) + (func (export "f64.no_fold_div2_mul2") (param $x f64) (result f64) + (f64.mul (f64.div (local.get $x) (f64.const 2.0)) (f64.const 2.0))) +) + +(assert_return (invoke "f32.no_fold_div2_mul2" (f32.const 0x1.fffffep-126)) (f32.const 0x1p-125)) +(assert_return (invoke "f64.no_fold_div2_mul2" (f64.const 0x1.fffffffffffffp-1022)) (f64.const 0x1p-1021)) + +;; Test that promote(demote(x)) is not folded to x. + +(module + (func (export "no_fold_demote_promote") (param $x f64) (result f64) + (f64.promote_f32 (f32.demote_f64 (local.get $x)))) +) + +(assert_return (invoke "no_fold_demote_promote" (f64.const -0x1.dece272390f5dp-133)) (f64.const -0x1.decep-133)) +(assert_return (invoke "no_fold_demote_promote" (f64.const -0x1.19e6c79938a6fp-85)) (f64.const -0x1.19e6c8p-85)) +(assert_return (invoke "no_fold_demote_promote" (f64.const 0x1.49b297ec44dc1p+107)) (f64.const 0x1.49b298p+107)) +(assert_return (invoke "no_fold_demote_promote" (f64.const -0x1.74f5bd865163p-88)) (f64.const -0x1.74f5bep-88)) +(assert_return (invoke "no_fold_demote_promote" (f64.const 0x1.26d675662367ep+104)) (f64.const 0x1.26d676p+104)) + +;; Test that demote(promote(x)) is not folded to x, and aside from NaN is +;; bit-preserving. + +(module + (func (export "no_fold_promote_demote") (param $x f32) (result f32) + (f32.demote_f64 (f64.promote_f32 (local.get $x)))) +) + +(assert_return (invoke "no_fold_promote_demote" (f32.const nan:0x200000)) (f32.const nan:arithmetic)) +(assert_return (invoke "no_fold_promote_demote" (f32.const 0x0p+0)) (f32.const 0x0p+0)) +(assert_return (invoke "no_fold_promote_demote" (f32.const -0x0p+0)) (f32.const -0x0p+0)) +(assert_return (invoke "no_fold_promote_demote" (f32.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "no_fold_promote_demote" (f32.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "no_fold_promote_demote" (f32.const 0x1.fffffcp-127)) (f32.const 0x1.fffffcp-127)) +(assert_return (invoke "no_fold_promote_demote" (f32.const -0x1.fffffcp-127)) (f32.const -0x1.fffffcp-127)) +(assert_return (invoke "no_fold_promote_demote" (f32.const 0x1p-126)) (f32.const 0x1p-126)) +(assert_return (invoke "no_fold_promote_demote" (f32.const -0x1p-126)) (f32.const -0x1p-126)) +(assert_return (invoke "no_fold_promote_demote" (f32.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "no_fold_promote_demote" (f32.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "no_fold_promote_demote" (f32.const inf)) (f32.const inf)) +(assert_return (invoke "no_fold_promote_demote" (f32.const -inf)) (f32.const -inf)) + +;; Test that demote(x+promote(y)) is not folded to demote(x)+y. + +(module + (func (export "no_demote_mixed_add") (param $x f64) (param $y f32) (result f32) + (f32.demote_f64 (f64.add (local.get $x) (f64.promote_f32 (local.get $y))))) + (func (export "no_demote_mixed_add_commuted") (param $y f32) (param $x f64) (result f32) + (f32.demote_f64 (f64.add (f64.promote_f32 (local.get $y)) (local.get $x)))) +) + +(assert_return (invoke "no_demote_mixed_add" (f64.const 0x1.f51a9d04854f9p-95) (f32.const 0x1.3f4e9cp-119)) (f32.const 0x1.f51a9ep-95)) +(assert_return (invoke "no_demote_mixed_add" (f64.const 0x1.065b3d81ad8dp+37) (f32.const 0x1.758cd8p+38)) (f32.const 0x1.f8ba76p+38)) +(assert_return (invoke "no_demote_mixed_add" (f64.const 0x1.626c80963bd17p-119) (f32.const -0x1.9bbf86p-121)) (f32.const 0x1.f6f93ep-120)) +(assert_return (invoke "no_demote_mixed_add" (f64.const -0x1.0d5110e3385bbp-20) (f32.const 0x1.096f4ap-29)) (f32.const -0x1.0ccc5ap-20)) +(assert_return (invoke "no_demote_mixed_add" (f64.const -0x1.73852db4e5075p-20) (f32.const -0x1.24e474p-41)) (f32.const -0x1.738536p-20)) + +(assert_return (invoke "no_demote_mixed_add_commuted" (f32.const 0x1.3f4e9cp-119) (f64.const 0x1.f51a9d04854f9p-95)) (f32.const 0x1.f51a9ep-95)) +(assert_return (invoke "no_demote_mixed_add_commuted" (f32.const 0x1.758cd8p+38) (f64.const 0x1.065b3d81ad8dp+37)) (f32.const 0x1.f8ba76p+38)) +(assert_return (invoke "no_demote_mixed_add_commuted" (f32.const -0x1.9bbf86p-121) (f64.const 0x1.626c80963bd17p-119)) (f32.const 0x1.f6f93ep-120)) +(assert_return (invoke "no_demote_mixed_add_commuted" (f32.const 0x1.096f4ap-29) (f64.const -0x1.0d5110e3385bbp-20)) (f32.const -0x1.0ccc5ap-20)) +(assert_return (invoke "no_demote_mixed_add_commuted" (f32.const -0x1.24e474p-41) (f64.const -0x1.73852db4e5075p-20)) (f32.const -0x1.738536p-20)) + +;; Test that demote(x-promote(y)) is not folded to demote(x)-y. + +(module + (func (export "no_demote_mixed_sub") (param $x f64) (param $y f32) (result f32) + (f32.demote_f64 (f64.sub (local.get $x) (f64.promote_f32 (local.get $y))))) +) + +(assert_return (invoke "no_demote_mixed_sub" (f64.const 0x1.a0a183220e9b1p+82) (f32.const 0x1.c5acf8p+61)) (f32.const 0x1.a0a174p+82)) +(assert_return (invoke "no_demote_mixed_sub" (f64.const -0x1.6e2c5ac39f63ep+30) (f32.const 0x1.d48ca4p+17)) (f32.const -0x1.6e3bp+30)) +(assert_return (invoke "no_demote_mixed_sub" (f64.const -0x1.98c74350dde6ap+6) (f32.const 0x1.9d69bcp-12)) (f32.const -0x1.98c7aap+6)) +(assert_return (invoke "no_demote_mixed_sub" (f64.const 0x1.0459f34091dbfp-54) (f32.const 0x1.61ad08p-71)) (f32.const 0x1.045942p-54)) +(assert_return (invoke "no_demote_mixed_sub" (f64.const 0x1.a7498dca3fdb7p+14) (f32.const 0x1.ed21c8p+15)) (f32.const -0x1.197d02p+15)) + +;; Test that converting between integer and float and back isn't folded away. + +(module + (func (export "f32.i32.no_fold_trunc_s_convert_s") (param $x f32) (result f32) + (f32.convert_i32_s (i32.trunc_f32_s (local.get $x)))) + (func (export "f32.i32.no_fold_trunc_u_convert_s") (param $x f32) (result f32) + (f32.convert_i32_s (i32.trunc_f32_u (local.get $x)))) + (func (export "f32.i32.no_fold_trunc_s_convert_u") (param $x f32) (result f32) + (f32.convert_i32_u (i32.trunc_f32_s (local.get $x)))) + (func (export "f32.i32.no_fold_trunc_u_convert_u") (param $x f32) (result f32) + (f32.convert_i32_u (i32.trunc_f32_u (local.get $x)))) + (func (export "f64.i32.no_fold_trunc_s_convert_s") (param $x f64) (result f64) + (f64.convert_i32_s (i32.trunc_f64_s (local.get $x)))) + (func (export "f64.i32.no_fold_trunc_u_convert_s") (param $x f64) (result f64) + (f64.convert_i32_s (i32.trunc_f64_u (local.get $x)))) + (func (export "f64.i32.no_fold_trunc_s_convert_u") (param $x f64) (result f64) + (f64.convert_i32_u (i32.trunc_f64_s (local.get $x)))) + (func (export "f64.i32.no_fold_trunc_u_convert_u") (param $x f64) (result f64) + (f64.convert_i32_u (i32.trunc_f64_u (local.get $x)))) + (func (export "f32.i64.no_fold_trunc_s_convert_s") (param $x f32) (result f32) + (f32.convert_i64_s (i64.trunc_f32_s (local.get $x)))) + (func (export "f32.i64.no_fold_trunc_u_convert_s") (param $x f32) (result f32) + (f32.convert_i64_s (i64.trunc_f32_u (local.get $x)))) + (func (export "f32.i64.no_fold_trunc_s_convert_u") (param $x f32) (result f32) + (f32.convert_i64_u (i64.trunc_f32_s (local.get $x)))) + (func (export "f32.i64.no_fold_trunc_u_convert_u") (param $x f32) (result f32) + (f32.convert_i64_u (i64.trunc_f32_u (local.get $x)))) + (func (export "f64.i64.no_fold_trunc_s_convert_s") (param $x f64) (result f64) + (f64.convert_i64_s (i64.trunc_f64_s (local.get $x)))) + (func (export "f64.i64.no_fold_trunc_u_convert_s") (param $x f64) (result f64) + (f64.convert_i64_s (i64.trunc_f64_u (local.get $x)))) + (func (export "f64.i64.no_fold_trunc_s_convert_u") (param $x f64) (result f64) + (f64.convert_i64_u (i64.trunc_f64_s (local.get $x)))) + (func (export "f64.i64.no_fold_trunc_u_convert_u") (param $x f64) (result f64) + (f64.convert_i64_u (i64.trunc_f64_u (local.get $x)))) +) + +(assert_return (invoke "f32.i32.no_fold_trunc_s_convert_s" (f32.const 1.5)) (f32.const 1.0)) +(assert_return (invoke "f32.i32.no_fold_trunc_s_convert_s" (f32.const -1.5)) (f32.const -1.0)) +(assert_return (invoke "f32.i32.no_fold_trunc_u_convert_s" (f32.const 1.5)) (f32.const 1.0)) +(assert_return (invoke "f32.i32.no_fold_trunc_u_convert_s" (f32.const -0.5)) (f32.const 0.0)) +(assert_return (invoke "f32.i32.no_fold_trunc_s_convert_u" (f32.const 1.5)) (f32.const 1.0)) +(assert_return (invoke "f32.i32.no_fold_trunc_s_convert_u" (f32.const -1.5)) (f32.const 0x1p+32)) +(assert_return (invoke "f32.i32.no_fold_trunc_u_convert_u" (f32.const 1.5)) (f32.const 1.0)) +(assert_return (invoke "f32.i32.no_fold_trunc_u_convert_u" (f32.const -0.5)) (f32.const 0.0)) + +(assert_return (invoke "f64.i32.no_fold_trunc_s_convert_s" (f64.const 1.5)) (f64.const 1.0)) +(assert_return (invoke "f64.i32.no_fold_trunc_s_convert_s" (f64.const -1.5)) (f64.const -1.0)) +(assert_return (invoke "f64.i32.no_fold_trunc_u_convert_s" (f64.const 1.5)) (f64.const 1.0)) +(assert_return (invoke "f64.i32.no_fold_trunc_u_convert_s" (f64.const -0.5)) (f64.const 0.0)) +(assert_return (invoke "f64.i32.no_fold_trunc_s_convert_u" (f64.const 1.5)) (f64.const 1.0)) +(assert_return (invoke "f64.i32.no_fold_trunc_s_convert_u" (f64.const -1.5)) (f64.const 0x1.fffffffep+31)) +(assert_return (invoke "f64.i32.no_fold_trunc_u_convert_u" (f64.const 1.5)) (f64.const 1.0)) +(assert_return (invoke "f64.i32.no_fold_trunc_u_convert_u" (f64.const -0.5)) (f64.const 0.0)) + +(assert_return (invoke "f32.i64.no_fold_trunc_s_convert_s" (f32.const 1.5)) (f32.const 1.0)) +(assert_return (invoke "f32.i64.no_fold_trunc_s_convert_s" (f32.const -1.5)) (f32.const -1.0)) +(assert_return (invoke "f32.i64.no_fold_trunc_u_convert_s" (f32.const 1.5)) (f32.const 1.0)) +(assert_return (invoke "f32.i64.no_fold_trunc_u_convert_s" (f32.const -0.5)) (f32.const 0.0)) +(assert_return (invoke "f32.i64.no_fold_trunc_s_convert_u" (f32.const 1.5)) (f32.const 1.0)) +(assert_return (invoke "f32.i64.no_fold_trunc_s_convert_u" (f32.const -1.5)) (f32.const 0x1p+64)) +(assert_return (invoke "f32.i64.no_fold_trunc_u_convert_u" (f32.const 1.5)) (f32.const 1.0)) +(assert_return (invoke "f32.i64.no_fold_trunc_u_convert_u" (f32.const -0.5)) (f32.const 0.0)) + +(assert_return (invoke "f64.i64.no_fold_trunc_s_convert_s" (f64.const 1.5)) (f64.const 1.0)) +(assert_return (invoke "f64.i64.no_fold_trunc_s_convert_s" (f64.const -1.5)) (f64.const -1.0)) +(assert_return (invoke "f64.i64.no_fold_trunc_u_convert_s" (f64.const 1.5)) (f64.const 1.0)) +(assert_return (invoke "f64.i64.no_fold_trunc_u_convert_s" (f64.const -0.5)) (f64.const 0.0)) +(assert_return (invoke "f64.i64.no_fold_trunc_s_convert_u" (f64.const 1.5)) (f64.const 1.0)) +(assert_return (invoke "f64.i64.no_fold_trunc_s_convert_u" (f64.const -1.5)) (f64.const 0x1p+64)) +(assert_return (invoke "f64.i64.no_fold_trunc_u_convert_u" (f64.const 1.5)) (f64.const 1.0)) +(assert_return (invoke "f64.i64.no_fold_trunc_u_convert_u" (f64.const -0.5)) (f64.const 0.0)) + +;; Test that dividing by a loop-invariant constant isn't optimized to be a +;; multiplication by a reciprocal, which would be particularly tempting since +;; the reciprocal computation could be hoisted. + +(module + (memory 1 1) + (func (export "init") (param $i i32) (param $x f32) (f32.store (local.get $i) (local.get $x))) + + (func (export "run") (param $n i32) (param $z f32) + (local $i i32) + (block $exit + (loop $cont + (f32.store + (local.get $i) + (f32.div (f32.load (local.get $i)) (local.get $z)) + ) + (local.set $i (i32.add (local.get $i) (i32.const 4))) + (br_if $cont (i32.lt_u (local.get $i) (local.get $n))) + ) + ) + ) + + (func (export "check") (param $i i32) (result f32) (f32.load (local.get $i))) +) + +(invoke "init" (i32.const 0) (f32.const 15.1)) +(invoke "init" (i32.const 4) (f32.const 15.2)) +(invoke "init" (i32.const 8) (f32.const 15.3)) +(invoke "init" (i32.const 12) (f32.const 15.4)) +(assert_return (invoke "check" (i32.const 0)) (f32.const 15.1)) +(assert_return (invoke "check" (i32.const 4)) (f32.const 15.2)) +(assert_return (invoke "check" (i32.const 8)) (f32.const 15.3)) +(assert_return (invoke "check" (i32.const 12)) (f32.const 15.4)) +(invoke "run" (i32.const 16) (f32.const 3.0)) +(assert_return (invoke "check" (i32.const 0)) (f32.const 0x1.422222p+2)) +(assert_return (invoke "check" (i32.const 4)) (f32.const 0x1.444444p+2)) +(assert_return (invoke "check" (i32.const 8)) (f32.const 0x1.466666p+2)) +(assert_return (invoke "check" (i32.const 12)) (f32.const 0x1.488888p+2)) + +(module + (memory 1 1) + (func (export "init") (param $i i32) (param $x f64) (f64.store (local.get $i) (local.get $x))) + + (func (export "run") (param $n i32) (param $z f64) + (local $i i32) + (block $exit + (loop $cont + (f64.store + (local.get $i) + (f64.div (f64.load (local.get $i)) (local.get $z)) + ) + (local.set $i (i32.add (local.get $i) (i32.const 8))) + (br_if $cont (i32.lt_u (local.get $i) (local.get $n))) + ) + ) + ) + + (func (export "check") (param $i i32) (result f64) (f64.load (local.get $i))) +) + +(invoke "init" (i32.const 0) (f64.const 15.1)) +(invoke "init" (i32.const 8) (f64.const 15.2)) +(invoke "init" (i32.const 16) (f64.const 15.3)) +(invoke "init" (i32.const 24) (f64.const 15.4)) +(assert_return (invoke "check" (i32.const 0)) (f64.const 15.1)) +(assert_return (invoke "check" (i32.const 8)) (f64.const 15.2)) +(assert_return (invoke "check" (i32.const 16)) (f64.const 15.3)) +(assert_return (invoke "check" (i32.const 24)) (f64.const 15.4)) +(invoke "run" (i32.const 32) (f64.const 3.0)) +(assert_return (invoke "check" (i32.const 0)) (f64.const 0x1.4222222222222p+2)) +(assert_return (invoke "check" (i32.const 8)) (f64.const 0x1.4444444444444p+2)) +(assert_return (invoke "check" (i32.const 16)) (f64.const 0x1.4666666666667p+2)) +(assert_return (invoke "check" (i32.const 24)) (f64.const 0x1.4888888888889p+2)) + +;; Test that ult/ugt/etc. aren't folded to olt/ogt/etc. + +(module + (func (export "f32.ult") (param $x f32) (param $y f32) (result i32) (i32.eqz (f32.ge (local.get $x) (local.get $y)))) + (func (export "f32.ule") (param $x f32) (param $y f32) (result i32) (i32.eqz (f32.gt (local.get $x) (local.get $y)))) + (func (export "f32.ugt") (param $x f32) (param $y f32) (result i32) (i32.eqz (f32.le (local.get $x) (local.get $y)))) + (func (export "f32.uge") (param $x f32) (param $y f32) (result i32) (i32.eqz (f32.lt (local.get $x) (local.get $y)))) + + (func (export "f64.ult") (param $x f64) (param $y f64) (result i32) (i32.eqz (f64.ge (local.get $x) (local.get $y)))) + (func (export "f64.ule") (param $x f64) (param $y f64) (result i32) (i32.eqz (f64.gt (local.get $x) (local.get $y)))) + (func (export "f64.ugt") (param $x f64) (param $y f64) (result i32) (i32.eqz (f64.le (local.get $x) (local.get $y)))) + (func (export "f64.uge") (param $x f64) (param $y f64) (result i32) (i32.eqz (f64.lt (local.get $x) (local.get $y)))) +) + +(assert_return (invoke "f32.ult" (f32.const 3.0) (f32.const 2.0)) (i32.const 0)) +(assert_return (invoke "f32.ult" (f32.const 2.0) (f32.const 2.0)) (i32.const 0)) +(assert_return (invoke "f32.ult" (f32.const 2.0) (f32.const 3.0)) (i32.const 1)) +(assert_return (invoke "f32.ult" (f32.const 2.0) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "f32.ule" (f32.const 3.0) (f32.const 2.0)) (i32.const 0)) +(assert_return (invoke "f32.ule" (f32.const 2.0) (f32.const 2.0)) (i32.const 1)) +(assert_return (invoke "f32.ule" (f32.const 2.0) (f32.const 3.0)) (i32.const 1)) +(assert_return (invoke "f32.ule" (f32.const 2.0) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "f32.ugt" (f32.const 3.0) (f32.const 2.0)) (i32.const 1)) +(assert_return (invoke "f32.ugt" (f32.const 2.0) (f32.const 2.0)) (i32.const 0)) +(assert_return (invoke "f32.ugt" (f32.const 2.0) (f32.const 3.0)) (i32.const 0)) +(assert_return (invoke "f32.ugt" (f32.const 2.0) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "f32.uge" (f32.const 3.0) (f32.const 2.0)) (i32.const 1)) +(assert_return (invoke "f32.uge" (f32.const 2.0) (f32.const 2.0)) (i32.const 1)) +(assert_return (invoke "f32.uge" (f32.const 2.0) (f32.const 3.0)) (i32.const 0)) +(assert_return (invoke "f32.uge" (f32.const 2.0) (f32.const nan)) (i32.const 1)) +(assert_return (invoke "f64.ult" (f64.const 3.0) (f64.const 2.0)) (i32.const 0)) +(assert_return (invoke "f64.ult" (f64.const 2.0) (f64.const 2.0)) (i32.const 0)) +(assert_return (invoke "f64.ult" (f64.const 2.0) (f64.const 3.0)) (i32.const 1)) +(assert_return (invoke "f64.ult" (f64.const 2.0) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "f64.ule" (f64.const 3.0) (f64.const 2.0)) (i32.const 0)) +(assert_return (invoke "f64.ule" (f64.const 2.0) (f64.const 2.0)) (i32.const 1)) +(assert_return (invoke "f64.ule" (f64.const 2.0) (f64.const 3.0)) (i32.const 1)) +(assert_return (invoke "f64.ule" (f64.const 2.0) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "f64.ugt" (f64.const 3.0) (f64.const 2.0)) (i32.const 1)) +(assert_return (invoke "f64.ugt" (f64.const 2.0) (f64.const 2.0)) (i32.const 0)) +(assert_return (invoke "f64.ugt" (f64.const 2.0) (f64.const 3.0)) (i32.const 0)) +(assert_return (invoke "f64.ugt" (f64.const 2.0) (f64.const nan)) (i32.const 1)) +(assert_return (invoke "f64.uge" (f64.const 3.0) (f64.const 2.0)) (i32.const 1)) +(assert_return (invoke "f64.uge" (f64.const 2.0) (f64.const 2.0)) (i32.const 1)) +(assert_return (invoke "f64.uge" (f64.const 2.0) (f64.const 3.0)) (i32.const 0)) +(assert_return (invoke "f64.uge" (f64.const 2.0) (f64.const nan)) (i32.const 1)) + +;; Test that x= y+z is not optimized to x >= y (monotonicity). +;; http://cs.nyu.edu/courses/spring13/CSCI-UA.0201-003/lecture6.pdf + +(module + (func (export "f32.no_fold_add_le_monotonicity") (param $x f32) (param $y f32) (param $z f32) (result i32) + (f32.le (f32.add (local.get $x) (local.get $z)) (f32.add (local.get $y) (local.get $z)))) + + (func (export "f32.no_fold_add_ge_monotonicity") (param $x f32) (param $y f32) (param $z f32) (result i32) + (f32.ge (f32.add (local.get $x) (local.get $z)) (f32.add (local.get $y) (local.get $z)))) + + (func (export "f64.no_fold_add_le_monotonicity") (param $x f64) (param $y f64) (param $z f64) (result i32) + (f64.le (f64.add (local.get $x) (local.get $z)) (f64.add (local.get $y) (local.get $z)))) + + (func (export "f64.no_fold_add_ge_monotonicity") (param $x f64) (param $y f64) (param $z f64) (result i32) + (f64.ge (f64.add (local.get $x) (local.get $z)) (f64.add (local.get $y) (local.get $z)))) +) + +(assert_return (invoke "f32.no_fold_add_le_monotonicity" (f32.const 0.0) (f32.const 0.0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "f32.no_fold_add_le_monotonicity" (f32.const inf) (f32.const -inf) (f32.const inf)) (i32.const 0)) +(assert_return (invoke "f64.no_fold_add_le_monotonicity" (f64.const 0.0) (f64.const 0.0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "f64.no_fold_add_le_monotonicity" (f64.const inf) (f64.const -inf) (f64.const inf)) (i32.const 0)) + +;; Test that !(x < y) and friends are not optimized to x >= y and friends. + +(module + (func (export "f32.not_lt") (param $x f32) (param $y f32) (result i32) + (i32.eqz (f32.lt (local.get $x) (local.get $y)))) + + (func (export "f32.not_le") (param $x f32) (param $y f32) (result i32) + (i32.eqz (f32.le (local.get $x) (local.get $y)))) + + (func (export "f32.not_gt") (param $x f32) (param $y f32) (result i32) + (i32.eqz (f32.gt (local.get $x) (local.get $y)))) + + (func (export "f32.not_ge") (param $x f32) (param $y f32) (result i32) + (i32.eqz (f32.ge (local.get $x) (local.get $y)))) + + (func (export "f64.not_lt") (param $x f64) (param $y f64) (result i32) + (i32.eqz (f64.lt (local.get $x) (local.get $y)))) + + (func (export "f64.not_le") (param $x f64) (param $y f64) (result i32) + (i32.eqz (f64.le (local.get $x) (local.get $y)))) + + (func (export "f64.not_gt") (param $x f64) (param $y f64) (result i32) + (i32.eqz (f64.gt (local.get $x) (local.get $y)))) + + (func (export "f64.not_ge") (param $x f64) (param $y f64) (result i32) + (i32.eqz (f64.ge (local.get $x) (local.get $y)))) +) + +(assert_return (invoke "f32.not_lt" (f32.const nan) (f32.const 0.0)) (i32.const 1)) +(assert_return (invoke "f32.not_le" (f32.const nan) (f32.const 0.0)) (i32.const 1)) +(assert_return (invoke "f32.not_gt" (f32.const nan) (f32.const 0.0)) (i32.const 1)) +(assert_return (invoke "f32.not_ge" (f32.const nan) (f32.const 0.0)) (i32.const 1)) +(assert_return (invoke "f64.not_lt" (f64.const nan) (f64.const 0.0)) (i32.const 1)) +(assert_return (invoke "f64.not_le" (f64.const nan) (f64.const 0.0)) (i32.const 1)) +(assert_return (invoke "f64.not_gt" (f64.const nan) (f64.const 0.0)) (i32.const 1)) +(assert_return (invoke "f64.not_ge" (f64.const nan) (f64.const 0.0)) (i32.const 1)) + +;; Test that a method for approximating a "machine epsilon" produces the expected +;; approximation. +;; http://blogs.mathworks.com/cleve/2014/07/07/floating-point-numbers/#24cb4f4d-b8a9-4c19-b22b-9d2a9f7f3812 + +(module + (func (export "f32.epsilon") (result f32) + (f32.sub (f32.const 1.0) (f32.mul (f32.const 3.0) (f32.sub (f32.div (f32.const 4.0) (f32.const 3.0)) (f32.const 1.0))))) + + (func (export "f64.epsilon") (result f64) + (f64.sub (f64.const 1.0) (f64.mul (f64.const 3.0) (f64.sub (f64.div (f64.const 4.0) (f64.const 3.0)) (f64.const 1.0))))) +) + +(assert_return (invoke "f32.epsilon") (f32.const -0x1p-23)) +(assert_return (invoke "f64.epsilon") (f64.const 0x1p-52)) + +;; Test that a method for computing a "machine epsilon" produces the expected +;; result. +;; https://www.math.utah.edu/~beebe/software/ieee/ + +(module + (func (export "f32.epsilon") (result f32) + (local $x f32) + (local $result f32) + (local.set $x (f32.const 1)) + (loop $loop + (br_if $loop + (f32.gt + (f32.add + (local.tee $x + (f32.mul + (local.tee $result (local.get $x)) + (f32.const 0.5) + ) + ) + (f32.const 1) + ) + (f32.const 1) + ) + ) + ) + (local.get $result) + ) + + (func (export "f64.epsilon") (result f64) + (local $x f64) + (local $result f64) + (local.set $x (f64.const 1)) + (loop $loop + (br_if $loop + (f64.gt + (f64.add + (local.tee $x + (f64.mul + (local.tee $result (local.get $x)) + (f64.const 0.5) + ) + ) + (f64.const 1) + ) + (f64.const 1) + ) + ) + ) + (local.get $result) + ) +) + +(assert_return (invoke "f32.epsilon") (f32.const 0x1p-23)) +(assert_return (invoke "f64.epsilon") (f64.const 0x1p-52)) + +;; Test that floating-point numbers are not optimized as if they form a +;; trichotomy. + +(module + (func (export "f32.no_trichotomy_lt") (param $x f32) (param $y f32) (result i32) + (i32.or (f32.lt (local.get $x) (local.get $y)) (f32.ge (local.get $x) (local.get $y)))) + (func (export "f32.no_trichotomy_le") (param $x f32) (param $y f32) (result i32) + (i32.or (f32.le (local.get $x) (local.get $y)) (f32.gt (local.get $x) (local.get $y)))) + (func (export "f32.no_trichotomy_gt") (param $x f32) (param $y f32) (result i32) + (i32.or (f32.gt (local.get $x) (local.get $y)) (f32.le (local.get $x) (local.get $y)))) + (func (export "f32.no_trichotomy_ge") (param $x f32) (param $y f32) (result i32) + (i32.or (f32.ge (local.get $x) (local.get $y)) (f32.lt (local.get $x) (local.get $y)))) + + (func (export "f64.no_trichotomy_lt") (param $x f64) (param $y f64) (result i32) + (i32.or (f64.lt (local.get $x) (local.get $y)) (f64.ge (local.get $x) (local.get $y)))) + (func (export "f64.no_trichotomy_le") (param $x f64) (param $y f64) (result i32) + (i32.or (f64.le (local.get $x) (local.get $y)) (f64.gt (local.get $x) (local.get $y)))) + (func (export "f64.no_trichotomy_gt") (param $x f64) (param $y f64) (result i32) + (i32.or (f64.gt (local.get $x) (local.get $y)) (f64.le (local.get $x) (local.get $y)))) + (func (export "f64.no_trichotomy_ge") (param $x f64) (param $y f64) (result i32) + (i32.or (f64.ge (local.get $x) (local.get $y)) (f64.lt (local.get $x) (local.get $y)))) +) + +(assert_return (invoke "f32.no_trichotomy_lt" (f32.const 0.0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "f32.no_trichotomy_le" (f32.const 0.0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "f32.no_trichotomy_gt" (f32.const 0.0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "f32.no_trichotomy_ge" (f32.const 0.0) (f32.const nan)) (i32.const 0)) +(assert_return (invoke "f64.no_trichotomy_lt" (f64.const 0.0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "f64.no_trichotomy_le" (f64.const 0.0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "f64.no_trichotomy_gt" (f64.const 0.0) (f64.const nan)) (i32.const 0)) +(assert_return (invoke "f64.no_trichotomy_ge" (f64.const 0.0) (f64.const nan)) (i32.const 0)) + +;; Some test harnesses which can run this testsuite are unable to perform tests +;; of NaN bitpatterns. The following tests whether the underlying platform is +;; generally producing the kinds of NaNs expected. +(module + (func (export "f32.arithmetic_nan_bitpattern") + (param $x i32) (param $y i32) (result i32) + (i32.and (i32.reinterpret_f32 + (f32.div + (f32.reinterpret_i32 (local.get $x)) + (f32.reinterpret_i32 (local.get $y)))) + (i32.const 0x7fc00000))) + (func (export "f32.canonical_nan_bitpattern") + (param $x i32) (param $y i32) (result i32) + (i32.and (i32.reinterpret_f32 + (f32.div + (f32.reinterpret_i32 (local.get $x)) + (f32.reinterpret_i32 (local.get $y)))) + (i32.const 0x7fffffff))) + (func (export "f32.nonarithmetic_nan_bitpattern") + (param $x i32) (result i32) + (i32.reinterpret_f32 (f32.neg (f32.reinterpret_i32 (local.get $x))))) + + (func (export "f64.arithmetic_nan_bitpattern") + (param $x i64) (param $y i64) (result i64) + (i64.and (i64.reinterpret_f64 + (f64.div + (f64.reinterpret_i64 (local.get $x)) + (f64.reinterpret_i64 (local.get $y)))) + (i64.const 0x7ff8000000000000))) + (func (export "f64.canonical_nan_bitpattern") + (param $x i64) (param $y i64) (result i64) + (i64.and (i64.reinterpret_f64 + (f64.div + (f64.reinterpret_i64 (local.get $x)) + (f64.reinterpret_i64 (local.get $y)))) + (i64.const 0x7fffffffffffffff))) + (func (export "f64.nonarithmetic_nan_bitpattern") + (param $x i64) (result i64) + (i64.reinterpret_f64 (f64.neg (f64.reinterpret_i64 (local.get $x))))) + + ;; Versions of no_fold testcases that only care about NaN bitpatterns. + (func (export "f32.no_fold_sub_zero") (param $x i32) (result i32) + (i32.and (i32.reinterpret_f32 (f32.sub (f32.reinterpret_i32 (local.get $x)) (f32.const 0.0))) + (i32.const 0x7fc00000))) + (func (export "f32.no_fold_neg0_sub") (param $x i32) (result i32) + (i32.and (i32.reinterpret_f32 (f32.sub (f32.const -0.0) (f32.reinterpret_i32 (local.get $x)))) + (i32.const 0x7fc00000))) + (func (export "f32.no_fold_mul_one") (param $x i32) (result i32) + (i32.and (i32.reinterpret_f32 (f32.mul (f32.reinterpret_i32 (local.get $x)) (f32.const 1.0))) + (i32.const 0x7fc00000))) + (func (export "f32.no_fold_neg1_mul") (param $x i32) (result i32) + (i32.and (i32.reinterpret_f32 (f32.mul (f32.const -1.0) (f32.reinterpret_i32 (local.get $x)))) + (i32.const 0x7fc00000))) + (func (export "f32.no_fold_div_one") (param $x i32) (result i32) + (i32.and (i32.reinterpret_f32 (f32.div (f32.reinterpret_i32 (local.get $x)) (f32.const 1.0))) + (i32.const 0x7fc00000))) + (func (export "f32.no_fold_div_neg1") (param $x i32) (result i32) + (i32.and (i32.reinterpret_f32 (f32.div (f32.reinterpret_i32 (local.get $x)) (f32.const -1.0))) + (i32.const 0x7fc00000))) + (func (export "f64.no_fold_sub_zero") (param $x i64) (result i64) + (i64.and (i64.reinterpret_f64 (f64.sub (f64.reinterpret_i64 (local.get $x)) (f64.const 0.0))) + (i64.const 0x7ff8000000000000))) + (func (export "f64.no_fold_neg0_sub") (param $x i64) (result i64) + (i64.and (i64.reinterpret_f64 (f64.sub (f64.const -0.0) (f64.reinterpret_i64 (local.get $x)))) + (i64.const 0x7ff8000000000000))) + (func (export "f64.no_fold_mul_one") (param $x i64) (result i64) + (i64.and (i64.reinterpret_f64 (f64.mul (f64.reinterpret_i64 (local.get $x)) (f64.const 1.0))) + (i64.const 0x7ff8000000000000))) + (func (export "f64.no_fold_neg1_mul") (param $x i64) (result i64) + (i64.and (i64.reinterpret_f64 (f64.mul (f64.const -1.0) (f64.reinterpret_i64 (local.get $x)))) + (i64.const 0x7ff8000000000000))) + (func (export "f64.no_fold_div_one") (param $x i64) (result i64) + (i64.and (i64.reinterpret_f64 (f64.div (f64.reinterpret_i64 (local.get $x)) (f64.const 1.0))) + (i64.const 0x7ff8000000000000))) + (func (export "f64.no_fold_div_neg1") (param $x i64) (result i64) + (i64.and (i64.reinterpret_f64 (f64.div (f64.reinterpret_i64 (local.get $x)) (f64.const -1.0))) + (i64.const 0x7ff8000000000000))) + (func (export "no_fold_promote_demote") (param $x i32) (result i32) + (i32.and (i32.reinterpret_f32 (f32.demote_f64 (f64.promote_f32 (f32.reinterpret_i32 (local.get $x))))) + (i32.const 0x7fc00000))) +) + +(assert_return (invoke "f32.arithmetic_nan_bitpattern" (i32.const 0x7f803210) (i32.const 0x7f803210)) (i32.const 0x7fc00000)) +(assert_return (invoke "f32.canonical_nan_bitpattern" (i32.const 0) (i32.const 0)) (i32.const 0x7fc00000)) +(assert_return (invoke "f32.canonical_nan_bitpattern" (i32.const 0x7fc00000) (i32.const 0x7fc00000)) (i32.const 0x7fc00000)) +(assert_return (invoke "f32.canonical_nan_bitpattern" (i32.const 0xffc00000) (i32.const 0x7fc00000)) (i32.const 0x7fc00000)) +(assert_return (invoke "f32.canonical_nan_bitpattern" (i32.const 0x7fc00000) (i32.const 0xffc00000)) (i32.const 0x7fc00000)) +(assert_return (invoke "f32.canonical_nan_bitpattern" (i32.const 0xffc00000) (i32.const 0xffc00000)) (i32.const 0x7fc00000)) +(assert_return (invoke "f32.nonarithmetic_nan_bitpattern" (i32.const 0x7fc03210)) (i32.const 0xffc03210)) +(assert_return (invoke "f32.nonarithmetic_nan_bitpattern" (i32.const 0xffc03210)) (i32.const 0x7fc03210)) +(assert_return (invoke "f32.nonarithmetic_nan_bitpattern" (i32.const 0x7f803210)) (i32.const 0xff803210)) +(assert_return (invoke "f32.nonarithmetic_nan_bitpattern" (i32.const 0xff803210)) (i32.const 0x7f803210)) +(assert_return (invoke "f64.arithmetic_nan_bitpattern" (i64.const 0x7ff0000000003210) (i64.const 0x7ff0000000003210)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "f64.canonical_nan_bitpattern" (i64.const 0) (i64.const 0)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "f64.canonical_nan_bitpattern" (i64.const 0x7ff8000000000000) (i64.const 0x7ff8000000000000)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "f64.canonical_nan_bitpattern" (i64.const 0xfff8000000000000) (i64.const 0x7ff8000000000000)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "f64.canonical_nan_bitpattern" (i64.const 0x7ff8000000000000) (i64.const 0xfff8000000000000)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "f64.canonical_nan_bitpattern" (i64.const 0xfff8000000000000) (i64.const 0xfff8000000000000)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "f64.nonarithmetic_nan_bitpattern" (i64.const 0x7ff8000000003210)) (i64.const 0xfff8000000003210)) +(assert_return (invoke "f64.nonarithmetic_nan_bitpattern" (i64.const 0xfff8000000003210)) (i64.const 0x7ff8000000003210)) +(assert_return (invoke "f64.nonarithmetic_nan_bitpattern" (i64.const 0x7ff0000000003210)) (i64.const 0xfff0000000003210)) +(assert_return (invoke "f64.nonarithmetic_nan_bitpattern" (i64.const 0xfff0000000003210)) (i64.const 0x7ff0000000003210)) +(assert_return (invoke "f32.no_fold_sub_zero" (i32.const 0x7fa00000)) (i32.const 0x7fc00000)) +(assert_return (invoke "f32.no_fold_neg0_sub" (i32.const 0x7fa00000)) (i32.const 0x7fc00000)) +(assert_return (invoke "f32.no_fold_mul_one" (i32.const 0x7fa00000)) (i32.const 0x7fc00000)) +(assert_return (invoke "f32.no_fold_neg1_mul" (i32.const 0x7fa00000)) (i32.const 0x7fc00000)) +(assert_return (invoke "f32.no_fold_div_one" (i32.const 0x7fa00000)) (i32.const 0x7fc00000)) +(assert_return (invoke "f32.no_fold_div_neg1" (i32.const 0x7fa00000)) (i32.const 0x7fc00000)) +(assert_return (invoke "f64.no_fold_sub_zero" (i64.const 0x7ff4000000000000)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "f64.no_fold_neg0_sub" (i64.const 0x7ff4000000000000)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "f64.no_fold_mul_one" (i64.const 0x7ff4000000000000)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "f64.no_fold_neg1_mul" (i64.const 0x7ff4000000000000)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "f64.no_fold_div_one" (i64.const 0x7ff4000000000000)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "f64.no_fold_div_neg1" (i64.const 0x7ff4000000000000)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "no_fold_promote_demote" (i32.const 0x7fa00000)) (i32.const 0x7fc00000)) + +;; Test that IEEE 754 double precision does, in fact, compute a certain dot +;; product correctly. + +(module + (func (export "dot_product_example") + (param $x0 f64) (param $x1 f64) (param $x2 f64) (param $x3 f64) + (param $y0 f64) (param $y1 f64) (param $y2 f64) (param $y3 f64) + (result f64) + (f64.add (f64.add (f64.add + (f64.mul (local.get $x0) (local.get $y0)) + (f64.mul (local.get $x1) (local.get $y1))) + (f64.mul (local.get $x2) (local.get $y2))) + (f64.mul (local.get $x3) (local.get $y3))) + ) + + (func (export "with_binary_sum_collapse") + (param $x0 f64) (param $x1 f64) (param $x2 f64) (param $x3 f64) + (param $y0 f64) (param $y1 f64) (param $y2 f64) (param $y3 f64) + (result f64) + (f64.add (f64.add (f64.mul (local.get $x0) (local.get $y0)) + (f64.mul (local.get $x1) (local.get $y1))) + (f64.add (f64.mul (local.get $x2) (local.get $y2)) + (f64.mul (local.get $x3) (local.get $y3)))) + ) +) + +(assert_return (invoke "dot_product_example" + (f64.const 3.2e7) (f64.const 1.0) (f64.const -1.0) (f64.const 8.0e7) + (f64.const 4.0e7) (f64.const 1.0) (f64.const -1.0) (f64.const -1.6e7)) + (f64.const 2.0)) +(assert_return (invoke "with_binary_sum_collapse" + (f64.const 3.2e7) (f64.const 1.0) (f64.const -1.0) (f64.const 8.0e7) + (f64.const 4.0e7) (f64.const 1.0) (f64.const -1.0) (f64.const -1.6e7)) + (f64.const 2.0)) + +;; http://www.vinc17.org/research/fptest.en.html#contract2fma + +(module + (func (export "f32.contract2fma") + (param $x f32) (param $y f32) (result f32) + (f32.sqrt (f32.sub (f32.mul (local.get $x) (local.get $x)) + (f32.mul (local.get $y) (local.get $y))))) + (func (export "f64.contract2fma") + (param $x f64) (param $y f64) (result f64) + (f64.sqrt (f64.sub (f64.mul (local.get $x) (local.get $x)) + (f64.mul (local.get $y) (local.get $y))))) +) + +(assert_return (invoke "f32.contract2fma" (f32.const 1.0) (f32.const 1.0)) (f32.const 0.0)) +(assert_return (invoke "f32.contract2fma" (f32.const 0x1.19999ap+0) (f32.const 0x1.19999ap+0)) (f32.const 0.0)) +(assert_return (invoke "f32.contract2fma" (f32.const 0x1.333332p+0) (f32.const 0x1.333332p+0)) (f32.const 0.0)) +(assert_return (invoke "f64.contract2fma" (f64.const 1.0) (f64.const 1.0)) (f64.const 0.0)) +(assert_return (invoke "f64.contract2fma" (f64.const 0x1.199999999999ap+0) (f64.const 0x1.199999999999ap+0)) (f64.const 0.0)) +(assert_return (invoke "f64.contract2fma" (f64.const 0x1.3333333333333p+0) (f64.const 0x1.3333333333333p+0)) (f64.const 0.0)) + +;; Test that floating-point isn't implemented with QuickBasic for MS-DOS. +;; https://support.microsoft.com/en-us/help/42980/-complete-tutorial-to-understand-ieee-floating-point-errors + +(module + (func (export "f32.division_by_small_number") + (param $a f32) (param $b f32) (param $c f32) (result f32) + (f32.sub (local.get $a) (f32.div (local.get $b) (local.get $c)))) + (func (export "f64.division_by_small_number") + (param $a f64) (param $b f64) (param $c f64) (result f64) + (f64.sub (local.get $a) (f64.div (local.get $b) (local.get $c)))) +) + +(assert_return (invoke "f32.division_by_small_number" (f32.const 112000000) (f32.const 100000) (f32.const 0.0009)) (f32.const 888888)) +(assert_return (invoke "f64.division_by_small_number" (f64.const 112000000) (f64.const 100000) (f64.const 0.0009)) (f64.const 888888.8888888806)) + +;; Test a simple golden ratio computation. +;; http://mathworld.wolfram.com/GoldenRatio.html + +(module + (func (export "f32.golden_ratio") (param $a f32) (param $b f32) (param $c f32) (result f32) + (f32.mul (local.get 0) (f32.add (local.get 1) (f32.sqrt (local.get 2))))) + (func (export "f64.golden_ratio") (param $a f64) (param $b f64) (param $c f64) (result f64) + (f64.mul (local.get 0) (f64.add (local.get 1) (f64.sqrt (local.get 2))))) +) + +(assert_return (invoke "f32.golden_ratio" (f32.const 0.5) (f32.const 1.0) (f32.const 5.0)) (f32.const 1.618034)) +(assert_return (invoke "f64.golden_ratio" (f64.const 0.5) (f64.const 1.0) (f64.const 5.0)) (f64.const 1.618033988749895)) + +;; Test some silver means computations. +;; http://mathworld.wolfram.com/SilverRatio.html + +(module + (func (export "f32.silver_means") (param $n f32) (result f32) + (f32.mul (f32.const 0.5) + (f32.add (local.get $n) + (f32.sqrt (f32.add (f32.mul (local.get $n) (local.get $n)) + (f32.const 4.0)))))) + (func (export "f64.silver_means") (param $n f64) (result f64) + (f64.mul (f64.const 0.5) + (f64.add (local.get $n) + (f64.sqrt (f64.add (f64.mul (local.get $n) (local.get $n)) + (f64.const 4.0)))))) +) + +(assert_return (invoke "f32.silver_means" (f32.const 0.0)) (f32.const 1.0)) +(assert_return (invoke "f32.silver_means" (f32.const 1.0)) (f32.const 1.6180340)) +(assert_return (invoke "f32.silver_means" (f32.const 2.0)) (f32.const 2.4142136)) +(assert_return (invoke "f32.silver_means" (f32.const 3.0)) (f32.const 3.3027756)) +(assert_return (invoke "f32.silver_means" (f32.const 4.0)) (f32.const 4.2360680)) +(assert_return (invoke "f32.silver_means" (f32.const 5.0)) (f32.const 5.1925821)) +(assert_return (invoke "f64.silver_means" (f64.const 0.0)) (f64.const 1.0)) +(assert_return (invoke "f64.silver_means" (f64.const 1.0)) (f64.const 1.618033988749895)) +(assert_return (invoke "f64.silver_means" (f64.const 2.0)) (f64.const 2.414213562373095)) +(assert_return (invoke "f64.silver_means" (f64.const 3.0)) (f64.const 3.302775637731995)) +(assert_return (invoke "f64.silver_means" (f64.const 4.0)) (f64.const 4.236067977499790)) +(assert_return (invoke "f64.silver_means" (f64.const 5.0)) (f64.const 5.192582403567252)) + +;; Test that an f64 0.4 isn't double-rounded as via extended precision. +;; https://bugs.llvm.org/show_bug.cgi?id=11200 + +(module + (func (export "point_four") (param $four f64) (param $ten f64) (result i32) + (f64.lt (f64.div (local.get $four) (local.get $ten)) (f64.const 0.4))) +) + +(assert_return (invoke "point_four" (f64.const 4.0) (f64.const 10.0)) (i32.const 0)) + +;; Test an approximation function for tau; it should produces the correctly +;; rounded result after (and only after) the expected number of iterations. + +(module + (func (export "tau") (param i32) (result f64) + (local f64 f64 f64 f64) + f64.const 0x0p+0 + local.set 1 + block + local.get 0 + i32.const 1 + i32.lt_s + br_if 0 + f64.const 0x1p+0 + local.set 2 + f64.const 0x0p+0 + local.set 3 + loop + local.get 1 + local.get 2 + f64.const 0x1p+3 + local.get 3 + f64.const 0x1p+3 + f64.mul + local.tee 4 + f64.const 0x1p+0 + f64.add + f64.div + f64.const 0x1p+2 + local.get 4 + f64.const 0x1p+2 + f64.add + f64.div + f64.sub + f64.const 0x1p+1 + local.get 4 + f64.const 0x1.4p+2 + f64.add + f64.div + f64.sub + f64.const 0x1p+1 + local.get 4 + f64.const 0x1.8p+2 + f64.add + f64.div + f64.sub + f64.mul + f64.add + local.set 1 + local.get 3 + f64.const 0x1p+0 + f64.add + local.set 3 + local.get 2 + f64.const 0x1p-4 + f64.mul + local.set 2 + local.get 0 + i32.const -1 + i32.add + local.tee 0 + br_if 0 + end + end + local.get 1 + ) +) + +(assert_return (invoke "tau" (i32.const 10)) (f64.const 0x1.921fb54442d14p+2)) +(assert_return (invoke "tau" (i32.const 11)) (f64.const 0x1.921fb54442d18p+2)) + +;; Test that y < 0 ? x : (x + 1) is not folded to x + (y < 0). + +(module + (func (export "f32.no_fold_conditional_inc") (param $x f32) (param $y f32) (result f32) + (select (local.get $x) + (f32.add (local.get $x) (f32.const 1.0)) + (f32.lt (local.get $y) (f32.const 0.0)))) + (func (export "f64.no_fold_conditional_inc") (param $x f64) (param $y f64) (result f64) + (select (local.get $x) + (f64.add (local.get $x) (f64.const 1.0)) + (f64.lt (local.get $y) (f64.const 0.0)))) +) + +(assert_return (invoke "f32.no_fold_conditional_inc" (f32.const -0.0) (f32.const -1.0)) (f32.const -0.0)) +(assert_return (invoke "f64.no_fold_conditional_inc" (f64.const -0.0) (f64.const -1.0)) (f64.const -0.0)) diff --git a/runtime/unc-vm/tests/wast/spec/float_literals.wast b/runtime/unc-vm/tests/wast/spec/float_literals.wast new file mode 100644 index 000000000..fefb91fbb --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/float_literals.wast @@ -0,0 +1,507 @@ +;; Test floating-point literal parsing. + +(module + ;; f32 special values + (func (export "f32.nan") (result i32) (i32.reinterpret_f32 (f32.const nan))) + (func (export "f32.positive_nan") (result i32) (i32.reinterpret_f32 (f32.const +nan))) + (func (export "f32.negative_nan") (result i32) (i32.reinterpret_f32 (f32.const -nan))) + (func (export "f32.plain_nan") (result i32) (i32.reinterpret_f32 (f32.const nan:0x400000))) + (func (export "f32.informally_known_as_plain_snan") (result i32) (i32.reinterpret_f32 (f32.const nan:0x200000))) + (func (export "f32.all_ones_nan") (result i32) (i32.reinterpret_f32 (f32.const -nan:0x7fffff))) + (func (export "f32.misc_nan") (result i32) (i32.reinterpret_f32 (f32.const nan:0x012345))) + (func (export "f32.misc_positive_nan") (result i32) (i32.reinterpret_f32 (f32.const +nan:0x304050))) + (func (export "f32.misc_negative_nan") (result i32) (i32.reinterpret_f32 (f32.const -nan:0x2abcde))) + (func (export "f32.infinity") (result i32) (i32.reinterpret_f32 (f32.const inf))) + (func (export "f32.positive_infinity") (result i32) (i32.reinterpret_f32 (f32.const +inf))) + (func (export "f32.negative_infinity") (result i32) (i32.reinterpret_f32 (f32.const -inf))) + + ;; f32 numbers + (func (export "f32.zero") (result i32) (i32.reinterpret_f32 (f32.const 0x0.0p0))) + (func (export "f32.positive_zero") (result i32) (i32.reinterpret_f32 (f32.const +0x0.0p0))) + (func (export "f32.negative_zero") (result i32) (i32.reinterpret_f32 (f32.const -0x0.0p0))) + (func (export "f32.misc") (result i32) (i32.reinterpret_f32 (f32.const 0x1.921fb6p+2))) + (func (export "f32.min_positive") (result i32) (i32.reinterpret_f32 (f32.const 0x1p-149))) + (func (export "f32.min_normal") (result i32) (i32.reinterpret_f32 (f32.const 0x1p-126))) + (func (export "f32.max_finite") (result i32) (i32.reinterpret_f32 (f32.const 0x1.fffffep+127))) + (func (export "f32.max_subnormal") (result i32) (i32.reinterpret_f32 (f32.const 0x1.fffffcp-127))) + (func (export "f32.trailing_dot") (result i32) (i32.reinterpret_f32 (f32.const 0x1.p10))) + + ;; f32 in decimal format + (func (export "f32_dec.zero") (result i32) (i32.reinterpret_f32 (f32.const 0.0e0))) + (func (export "f32_dec.positive_zero") (result i32) (i32.reinterpret_f32 (f32.const +0.0e0))) + (func (export "f32_dec.negative_zero") (result i32) (i32.reinterpret_f32 (f32.const -0.0e0))) + (func (export "f32_dec.misc") (result i32) (i32.reinterpret_f32 (f32.const 6.28318548202514648))) + (func (export "f32_dec.min_positive") (result i32) (i32.reinterpret_f32 (f32.const 1.4013e-45))) + (func (export "f32_dec.min_normal") (result i32) (i32.reinterpret_f32 (f32.const 1.1754944e-38))) + (func (export "f32_dec.max_subnormal") (result i32) (i32.reinterpret_f32 (f32.const 1.1754942e-38))) + (func (export "f32_dec.max_finite") (result i32) (i32.reinterpret_f32 (f32.const 3.4028234e+38))) + (func (export "f32_dec.trailing_dot") (result i32) (i32.reinterpret_f32 (f32.const 1.e10))) + + ;; https://twitter.com/Archivd/status/994637336506912768 + (func (export "f32_dec.root_beer_float") (result i32) (i32.reinterpret_f32 (f32.const 1.000000119))) + + ;; f64 special values + (func (export "f64.nan") (result i64) (i64.reinterpret_f64 (f64.const nan))) + (func (export "f64.positive_nan") (result i64) (i64.reinterpret_f64 (f64.const +nan))) + (func (export "f64.negative_nan") (result i64) (i64.reinterpret_f64 (f64.const -nan))) + (func (export "f64.plain_nan") (result i64) (i64.reinterpret_f64 (f64.const nan:0x8000000000000))) + (func (export "f64.informally_known_as_plain_snan") (result i64) (i64.reinterpret_f64 (f64.const nan:0x4000000000000))) + (func (export "f64.all_ones_nan") (result i64) (i64.reinterpret_f64 (f64.const -nan:0xfffffffffffff))) + (func (export "f64.misc_nan") (result i64) (i64.reinterpret_f64 (f64.const nan:0x0123456789abc))) + (func (export "f64.misc_positive_nan") (result i64) (i64.reinterpret_f64 (f64.const +nan:0x3040506070809))) + (func (export "f64.misc_negative_nan") (result i64) (i64.reinterpret_f64 (f64.const -nan:0x2abcdef012345))) + (func (export "f64.infinity") (result i64) (i64.reinterpret_f64 (f64.const inf))) + (func (export "f64.positive_infinity") (result i64) (i64.reinterpret_f64 (f64.const +inf))) + (func (export "f64.negative_infinity") (result i64) (i64.reinterpret_f64 (f64.const -inf))) + + ;; f64 numbers + (func (export "f64.zero") (result i64) (i64.reinterpret_f64 (f64.const 0x0.0p0))) + (func (export "f64.positive_zero") (result i64) (i64.reinterpret_f64 (f64.const +0x0.0p0))) + (func (export "f64.negative_zero") (result i64) (i64.reinterpret_f64 (f64.const -0x0.0p0))) + (func (export "f64.misc") (result i64) (i64.reinterpret_f64 (f64.const 0x1.921fb54442d18p+2))) + (func (export "f64.min_positive") (result i64) (i64.reinterpret_f64 (f64.const 0x0.0000000000001p-1022))) + (func (export "f64.min_normal") (result i64) (i64.reinterpret_f64 (f64.const 0x1p-1022))) + (func (export "f64.max_subnormal") (result i64) (i64.reinterpret_f64 (f64.const 0x0.fffffffffffffp-1022))) + (func (export "f64.max_finite") (result i64) (i64.reinterpret_f64 (f64.const 0x1.fffffffffffffp+1023))) + (func (export "f64.trailing_dot") (result i64) (i64.reinterpret_f64 (f64.const 0x1.p100))) + + ;; f64 numbers in decimal format + (func (export "f64_dec.zero") (result i64) (i64.reinterpret_f64 (f64.const 0.0e0))) + (func (export "f64_dec.positive_zero") (result i64) (i64.reinterpret_f64 (f64.const +0.0e0))) + (func (export "f64_dec.negative_zero") (result i64) (i64.reinterpret_f64 (f64.const -0.0e0))) + (func (export "f64_dec.misc") (result i64) (i64.reinterpret_f64 (f64.const 6.28318530717958623))) + (func (export "f64_dec.min_positive") (result i64) (i64.reinterpret_f64 (f64.const 4.94066e-324))) + (func (export "f64_dec.min_normal") (result i64) (i64.reinterpret_f64 (f64.const 2.2250738585072012e-308))) + (func (export "f64_dec.max_subnormal") (result i64) (i64.reinterpret_f64 (f64.const 2.2250738585072011e-308))) + (func (export "f64_dec.max_finite") (result i64) (i64.reinterpret_f64 (f64.const 1.7976931348623157e+308))) + (func (export "f64_dec.trailing_dot") (result i64) (i64.reinterpret_f64 (f64.const 1.e100))) + + ;; https://twitter.com/Archivd/status/994637336506912768 + (func (export "f64_dec.root_beer_float") (result i64) (i64.reinterpret_f64 (f64.const 1.000000119))) + + (func (export "f32-dec-sep1") (result f32) (f32.const 1_000_000)) + (func (export "f32-dec-sep2") (result f32) (f32.const 1_0_0_0)) + (func (export "f32-dec-sep3") (result f32) (f32.const 100_3.141_592)) + (func (export "f32-dec-sep4") (result f32) (f32.const 99e+1_3)) + (func (export "f32-dec-sep5") (result f32) (f32.const 122_000.11_3_54E0_2_3)) + (func (export "f32-hex-sep1") (result f32) (f32.const 0xa_0f_00_99)) + (func (export "f32-hex-sep2") (result f32) (f32.const 0x1_a_A_0_f)) + (func (export "f32-hex-sep3") (result f32) (f32.const 0xa0_ff.f141_a59a)) + (func (export "f32-hex-sep4") (result f32) (f32.const 0xf0P+1_3)) + (func (export "f32-hex-sep5") (result f32) (f32.const 0x2a_f00a.1f_3_eep2_3)) + + (func (export "f64-dec-sep1") (result f64) (f64.const 1_000_000)) + (func (export "f64-dec-sep2") (result f64) (f64.const 1_0_0_0)) + (func (export "f64-dec-sep3") (result f64) (f64.const 100_3.141_592)) + (func (export "f64-dec-sep4") (result f64) (f64.const 99e-1_23)) + (func (export "f64-dec-sep5") (result f64) (f64.const 122_000.11_3_54e0_2_3)) + (func (export "f64-hex-sep1") (result f64) (f64.const 0xa_f00f_0000_9999)) + (func (export "f64-hex-sep2") (result f64) (f64.const 0x1_a_A_0_f)) + (func (export "f64-hex-sep3") (result f64) (f64.const 0xa0_ff.f141_a59a)) + (func (export "f64-hex-sep4") (result f64) (f64.const 0xf0P+1_3)) + (func (export "f64-hex-sep5") (result f64) (f64.const 0x2a_f00a.1f_3_eep2_3)) +) + +(assert_return (invoke "f32.nan") (i32.const 0x7fc00000)) +(assert_return (invoke "f32.positive_nan") (i32.const 0x7fc00000)) +(assert_return (invoke "f32.negative_nan") (i32.const 0xffc00000)) +(assert_return (invoke "f32.plain_nan") (i32.const 0x7fc00000)) +(assert_return (invoke "f32.informally_known_as_plain_snan") (i32.const 0x7fa00000)) +(assert_return (invoke "f32.all_ones_nan") (i32.const 0xffffffff)) +(assert_return (invoke "f32.misc_nan") (i32.const 0x7f812345)) +(assert_return (invoke "f32.misc_positive_nan") (i32.const 0x7fb04050)) +(assert_return (invoke "f32.misc_negative_nan") (i32.const 0xffaabcde)) +(assert_return (invoke "f32.infinity") (i32.const 0x7f800000)) +(assert_return (invoke "f32.positive_infinity") (i32.const 0x7f800000)) +(assert_return (invoke "f32.negative_infinity") (i32.const 0xff800000)) +(assert_return (invoke "f32.zero") (i32.const 0)) +(assert_return (invoke "f32.positive_zero") (i32.const 0)) +(assert_return (invoke "f32.negative_zero") (i32.const 0x80000000)) +(assert_return (invoke "f32.misc") (i32.const 0x40c90fdb)) +(assert_return (invoke "f32.min_positive") (i32.const 1)) +(assert_return (invoke "f32.min_normal") (i32.const 0x800000)) +(assert_return (invoke "f32.max_subnormal") (i32.const 0x7fffff)) +(assert_return (invoke "f32.max_finite") (i32.const 0x7f7fffff)) +(assert_return (invoke "f32.trailing_dot") (i32.const 0x44800000)) +(assert_return (invoke "f32_dec.zero") (i32.const 0)) +(assert_return (invoke "f32_dec.positive_zero") (i32.const 0)) +(assert_return (invoke "f32_dec.negative_zero") (i32.const 0x80000000)) +(assert_return (invoke "f32_dec.misc") (i32.const 0x40c90fdb)) +(assert_return (invoke "f32_dec.min_positive") (i32.const 1)) +(assert_return (invoke "f32_dec.min_normal") (i32.const 0x800000)) +(assert_return (invoke "f32_dec.max_subnormal") (i32.const 0x7fffff)) +(assert_return (invoke "f32_dec.max_finite") (i32.const 0x7f7fffff)) +(assert_return (invoke "f32_dec.trailing_dot") (i32.const 0x501502f9)) +(assert_return (invoke "f32_dec.root_beer_float") (i32.const 0x3f800001)) + +(assert_return (invoke "f64.nan") (i64.const 0x7ff8000000000000)) +(assert_return (invoke "f64.positive_nan") (i64.const 0x7ff8000000000000)) +(assert_return (invoke "f64.negative_nan") (i64.const 0xfff8000000000000)) +(assert_return (invoke "f64.plain_nan") (i64.const 0x7ff8000000000000)) +(assert_return (invoke "f64.informally_known_as_plain_snan") (i64.const 0x7ff4000000000000)) +(assert_return (invoke "f64.all_ones_nan") (i64.const 0xffffffffffffffff)) +(assert_return (invoke "f64.misc_nan") (i64.const 0x7ff0123456789abc)) +(assert_return (invoke "f64.misc_positive_nan") (i64.const 0x7ff3040506070809)) +(assert_return (invoke "f64.misc_negative_nan") (i64.const 0xfff2abcdef012345)) +(assert_return (invoke "f64.infinity") (i64.const 0x7ff0000000000000)) +(assert_return (invoke "f64.positive_infinity") (i64.const 0x7ff0000000000000)) +(assert_return (invoke "f64.negative_infinity") (i64.const 0xfff0000000000000)) +(assert_return (invoke "f64.zero") (i64.const 0)) +(assert_return (invoke "f64.positive_zero") (i64.const 0)) +(assert_return (invoke "f64.negative_zero") (i64.const 0x8000000000000000)) +(assert_return (invoke "f64.misc") (i64.const 0x401921fb54442d18)) +(assert_return (invoke "f64.min_positive") (i64.const 1)) +(assert_return (invoke "f64.min_normal") (i64.const 0x10000000000000)) +(assert_return (invoke "f64.max_subnormal") (i64.const 0xfffffffffffff)) +(assert_return (invoke "f64.max_finite") (i64.const 0x7fefffffffffffff)) +(assert_return (invoke "f64.trailing_dot") (i64.const 0x4630000000000000)) +(assert_return (invoke "f64_dec.zero") (i64.const 0)) +(assert_return (invoke "f64_dec.positive_zero") (i64.const 0)) +(assert_return (invoke "f64_dec.negative_zero") (i64.const 0x8000000000000000)) +(assert_return (invoke "f64_dec.misc") (i64.const 0x401921fb54442d18)) +(assert_return (invoke "f64_dec.min_positive") (i64.const 1)) +(assert_return (invoke "f64_dec.min_normal") (i64.const 0x10000000000000)) +(assert_return (invoke "f64_dec.max_subnormal") (i64.const 0xfffffffffffff)) +(assert_return (invoke "f64_dec.max_finite") (i64.const 0x7fefffffffffffff)) +(assert_return (invoke "f64_dec.trailing_dot") (i64.const 0x54b249ad2594c37d)) +(assert_return (invoke "f64_dec.root_beer_float") (i64.const 0x3ff000001ff19e24)) + +(assert_return (invoke "f32-dec-sep1") (f32.const 1000000)) +(assert_return (invoke "f32-dec-sep2") (f32.const 1000)) +(assert_return (invoke "f32-dec-sep3") (f32.const 1003.141592)) +(assert_return (invoke "f32-dec-sep4") (f32.const 99e+13)) +(assert_return (invoke "f32-dec-sep5") (f32.const 122000.11354e23)) +(assert_return (invoke "f32-hex-sep1") (f32.const 0xa0f0099)) +(assert_return (invoke "f32-hex-sep2") (f32.const 0x1aa0f)) +(assert_return (invoke "f32-hex-sep3") (f32.const 0xa0ff.f141a59a)) +(assert_return (invoke "f32-hex-sep4") (f32.const 0xf0P+13)) +(assert_return (invoke "f32-hex-sep5") (f32.const 0x2af00a.1f3eep23)) + +(assert_return (invoke "f64-dec-sep1") (f64.const 1000000)) +(assert_return (invoke "f64-dec-sep2") (f64.const 1000)) +(assert_return (invoke "f64-dec-sep3") (f64.const 1003.141592)) +(assert_return (invoke "f64-dec-sep4") (f64.const 99e-123)) +(assert_return (invoke "f64-dec-sep5") (f64.const 122000.11354e23)) +(assert_return (invoke "f64-hex-sep1") (f64.const 0xaf00f00009999)) +(assert_return (invoke "f64-hex-sep2") (f64.const 0x1aa0f)) +(assert_return (invoke "f64-hex-sep3") (f64.const 0xa0ff.f141a59a)) +(assert_return (invoke "f64-hex-sep4") (f64.const 0xf0P+13)) +(assert_return (invoke "f64-hex-sep5") (f64.const 0x2af00a.1f3eep23)) + +;; Test parsing a float from binary +(module binary + ;; (func (export "4294967249") (result f64) (f64.const 4294967249)) + "\00\61\73\6d\01\00\00\00\01\85\80\80\80\00\01\60" + "\00\01\7c\03\82\80\80\80\00\01\00\07\8e\80\80\80" + "\00\01\0a\34\32\39\34\39\36\37\32\34\39\00\00\0a" + "\91\80\80\80\00\01\8b\80\80\80\00\00\44\00\00\20" + "\fa\ff\ff\ef\41\0b" +) + +(assert_return (invoke "4294967249") (f64.const 4294967249)) + +(assert_malformed + (module quote "(global f32 (f32.const _100))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const +_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const -_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 99_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 1__000))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const _1.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 1.0_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 1_.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 1._0))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const _1e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 1e1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 1_e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 1e_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const _1.0e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 1.0e1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 1.0_e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 1.0e_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 1.0e+_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 1.0e_+1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const _0x100))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0_x100))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x00_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0xff__ffff))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x_1.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x1.0_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x1_.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x1._0))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x_1p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x1p1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x1_p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x1p_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x_1.0p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x1.0p1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x1.0_p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x1.0p_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x1.0p+_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f32 (f32.const 0x1.0p_+1))") + "unknown operator" +) + +(assert_malformed + (module quote "(global f64 (f64.const _100))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const +_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const -_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 99_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 1__000))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const _1.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 1.0_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 1_.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 1._0))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const _1e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 1e1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 1_e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 1e_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const _1.0e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 1.0e1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 1.0_e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 1.0e_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 1.0e+_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 1.0e_+1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const _0x100))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0_x100))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x00_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0xff__ffff))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x_1.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x1.0_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x1_.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x1._0))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x_1p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x1p1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x1_p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x1p_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x_1.0p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x1.0p1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x1.0_p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x1.0p_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x1.0p+_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global f64 (f64.const 0x1.0p_+1))") + "unknown operator" +) diff --git a/runtime/unc-vm/tests/wast/spec/float_memory.wast b/runtime/unc-vm/tests/wast/spec/float_memory.wast new file mode 100644 index 000000000..3801158f9 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/float_memory.wast @@ -0,0 +1,157 @@ +;; Test that floating-point load and store are bit-preserving. + +;; Test that load and store do not canonicalize NaNs as x87 does. + +(module + (memory (data "\00\00\a0\7f")) + + (func (export "f32.load") (result f32) (f32.load (i32.const 0))) + (func (export "i32.load") (result i32) (i32.load (i32.const 0))) + (func (export "f32.store") (f32.store (i32.const 0) (f32.const nan:0x200000))) + (func (export "i32.store") (i32.store (i32.const 0) (i32.const 0x7fa00000))) + (func (export "reset") (i32.store (i32.const 0) (i32.const 0))) +) + +(assert_return (invoke "i32.load") (i32.const 0x7fa00000)) +(assert_return (invoke "f32.load") (f32.const nan:0x200000)) +(invoke "reset") +(assert_return (invoke "i32.load") (i32.const 0x0)) +(assert_return (invoke "f32.load") (f32.const 0.0)) +(invoke "f32.store") +(assert_return (invoke "i32.load") (i32.const 0x7fa00000)) +(assert_return (invoke "f32.load") (f32.const nan:0x200000)) +(invoke "reset") +(assert_return (invoke "i32.load") (i32.const 0x0)) +(assert_return (invoke "f32.load") (f32.const 0.0)) +(invoke "i32.store") +(assert_return (invoke "i32.load") (i32.const 0x7fa00000)) +(assert_return (invoke "f32.load") (f32.const nan:0x200000)) + +(module + (memory (data "\00\00\00\00\00\00\f4\7f")) + + (func (export "f64.load") (result f64) (f64.load (i32.const 0))) + (func (export "i64.load") (result i64) (i64.load (i32.const 0))) + (func (export "f64.store") (f64.store (i32.const 0) (f64.const nan:0x4000000000000))) + (func (export "i64.store") (i64.store (i32.const 0) (i64.const 0x7ff4000000000000))) + (func (export "reset") (i64.store (i32.const 0) (i64.const 0))) +) + +(assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) +(assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) +(invoke "reset") +(assert_return (invoke "i64.load") (i64.const 0x0)) +(assert_return (invoke "f64.load") (f64.const 0.0)) +(invoke "f64.store") +(assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) +(assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) +(invoke "reset") +(assert_return (invoke "i64.load") (i64.const 0x0)) +(assert_return (invoke "f64.load") (f64.const 0.0)) +(invoke "i64.store") +(assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) +(assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) + +;; Test that unaligned load and store do not canonicalize NaNs. + +(module + (memory (data "\00\00\00\a0\7f")) + + (func (export "f32.load") (result f32) (f32.load (i32.const 1))) + (func (export "i32.load") (result i32) (i32.load (i32.const 1))) + (func (export "f32.store") (f32.store (i32.const 1) (f32.const nan:0x200000))) + (func (export "i32.store") (i32.store (i32.const 1) (i32.const 0x7fa00000))) + (func (export "reset") (i32.store (i32.const 1) (i32.const 0))) +) + +(assert_return (invoke "i32.load") (i32.const 0x7fa00000)) +(assert_return (invoke "f32.load") (f32.const nan:0x200000)) +(invoke "reset") +(assert_return (invoke "i32.load") (i32.const 0x0)) +(assert_return (invoke "f32.load") (f32.const 0.0)) +(invoke "f32.store") +(assert_return (invoke "i32.load") (i32.const 0x7fa00000)) +(assert_return (invoke "f32.load") (f32.const nan:0x200000)) +(invoke "reset") +(assert_return (invoke "i32.load") (i32.const 0x0)) +(assert_return (invoke "f32.load") (f32.const 0.0)) +(invoke "i32.store") +(assert_return (invoke "i32.load") (i32.const 0x7fa00000)) +(assert_return (invoke "f32.load") (f32.const nan:0x200000)) + +(module + (memory (data "\00\00\00\00\00\00\00\f4\7f")) + + (func (export "f64.load") (result f64) (f64.load (i32.const 1))) + (func (export "i64.load") (result i64) (i64.load (i32.const 1))) + (func (export "f64.store") (f64.store (i32.const 1) (f64.const nan:0x4000000000000))) + (func (export "i64.store") (i64.store (i32.const 1) (i64.const 0x7ff4000000000000))) + (func (export "reset") (i64.store (i32.const 1) (i64.const 0))) +) + +(assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) +(assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) +(invoke "reset") +(assert_return (invoke "i64.load") (i64.const 0x0)) +(assert_return (invoke "f64.load") (f64.const 0.0)) +(invoke "f64.store") +(assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) +(assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) +(invoke "reset") +(assert_return (invoke "i64.load") (i64.const 0x0)) +(assert_return (invoke "f64.load") (f64.const 0.0)) +(invoke "i64.store") +(assert_return (invoke "i64.load") (i64.const 0x7ff4000000000000)) +(assert_return (invoke "f64.load") (f64.const nan:0x4000000000000)) + +;; Test that load and store do not canonicalize NaNs as some JS engines do. + +(module + (memory (data "\01\00\d0\7f")) + + (func (export "f32.load") (result f32) (f32.load (i32.const 0))) + (func (export "i32.load") (result i32) (i32.load (i32.const 0))) + (func (export "f32.store") (f32.store (i32.const 0) (f32.const nan:0x500001))) + (func (export "i32.store") (i32.store (i32.const 0) (i32.const 0x7fd00001))) + (func (export "reset") (i32.store (i32.const 0) (i32.const 0))) +) + +(assert_return (invoke "i32.load") (i32.const 0x7fd00001)) +(assert_return (invoke "f32.load") (f32.const nan:0x500001)) +(invoke "reset") +(assert_return (invoke "i32.load") (i32.const 0x0)) +(assert_return (invoke "f32.load") (f32.const 0.0)) +(invoke "f32.store") +(assert_return (invoke "i32.load") (i32.const 0x7fd00001)) +(assert_return (invoke "f32.load") (f32.const nan:0x500001)) +(invoke "reset") +(assert_return (invoke "i32.load") (i32.const 0x0)) +(assert_return (invoke "f32.load") (f32.const 0.0)) +(invoke "i32.store") +(assert_return (invoke "i32.load") (i32.const 0x7fd00001)) +(assert_return (invoke "f32.load") (f32.const nan:0x500001)) + +(module + (memory (data "\01\00\00\00\00\00\fc\7f")) + + (func (export "f64.load") (result f64) (f64.load (i32.const 0))) + (func (export "i64.load") (result i64) (i64.load (i32.const 0))) + (func (export "f64.store") (f64.store (i32.const 0) (f64.const nan:0xc000000000001))) + (func (export "i64.store") (i64.store (i32.const 0) (i64.const 0x7ffc000000000001))) + (func (export "reset") (i64.store (i32.const 0) (i64.const 0))) +) + +(assert_return (invoke "i64.load") (i64.const 0x7ffc000000000001)) +(assert_return (invoke "f64.load") (f64.const nan:0xc000000000001)) +(invoke "reset") +(assert_return (invoke "i64.load") (i64.const 0x0)) +(assert_return (invoke "f64.load") (f64.const 0.0)) +(invoke "f64.store") +(assert_return (invoke "i64.load") (i64.const 0x7ffc000000000001)) +(assert_return (invoke "f64.load") (f64.const nan:0xc000000000001)) +(invoke "reset") +(assert_return (invoke "i64.load") (i64.const 0x0)) +(assert_return (invoke "f64.load") (f64.const 0.0)) +(invoke "i64.store") +(assert_return (invoke "i64.load") (i64.const 0x7ffc000000000001)) +(assert_return (invoke "f64.load") (f64.const nan:0xc000000000001)) diff --git a/runtime/unc-vm/tests/wast/spec/float_misc.wast b/runtime/unc-vm/tests/wast/spec/float_misc.wast new file mode 100644 index 000000000..3d83281d7 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/float_misc.wast @@ -0,0 +1,678 @@ +;; Platforms intended to run WebAssembly must support IEEE 754 arithmetic. +;; This testsuite is not currently sufficient for full IEEE 754 conformance +;; testing; platforms are currently expected to meet these requirements in +;; their own way (widely-used hardware platforms already do this). +;; +;; What this testsuite does test is that (a) the platform is basically IEEE 754 +;; rather than something else entirely, (b) it's configured correctly for +;; WebAssembly (rounding direction, exception masks, precision level, subnormal +;; mode, etc.), (c) the WebAssembly implementation doesn't perform any common +;; value-changing optimizations, and (d) that the WebAssembly implementation +;; doesn't exhibit any known implementation bugs. +;; +;; This file supplements f32.wast, f64.wast, f32_bitwise.wast, f64_bitwise.wast, +;; f32_cmp.wast, and f64_cmp.wast with additional single-instruction tests +;; covering additional miscellaneous interesting cases. + +(module + (func (export "f32.add") (param $x f32) (param $y f32) (result f32) (f32.add (local.get $x) (local.get $y))) + (func (export "f32.sub") (param $x f32) (param $y f32) (result f32) (f32.sub (local.get $x) (local.get $y))) + (func (export "f32.mul") (param $x f32) (param $y f32) (result f32) (f32.mul (local.get $x) (local.get $y))) + (func (export "f32.div") (param $x f32) (param $y f32) (result f32) (f32.div (local.get $x) (local.get $y))) + (func (export "f32.sqrt") (param $x f32) (result f32) (f32.sqrt (local.get $x))) + (func (export "f32.abs") (param $x f32) (result f32) (f32.abs (local.get $x))) + (func (export "f32.neg") (param $x f32) (result f32) (f32.neg (local.get $x))) + (func (export "f32.copysign") (param $x f32) (param $y f32) (result f32) (f32.copysign (local.get $x) (local.get $y))) + (func (export "f32.ceil") (param $x f32) (result f32) (f32.ceil (local.get $x))) + (func (export "f32.floor") (param $x f32) (result f32) (f32.floor (local.get $x))) + (func (export "f32.trunc") (param $x f32) (result f32) (f32.trunc (local.get $x))) + (func (export "f32.nearest") (param $x f32) (result f32) (f32.nearest (local.get $x))) + (func (export "f32.min") (param $x f32) (param $y f32) (result f32) (f32.min (local.get $x) (local.get $y))) + (func (export "f32.max") (param $x f32) (param $y f32) (result f32) (f32.max (local.get $x) (local.get $y))) + + (func (export "f64.add") (param $x f64) (param $y f64) (result f64) (f64.add (local.get $x) (local.get $y))) + (func (export "f64.sub") (param $x f64) (param $y f64) (result f64) (f64.sub (local.get $x) (local.get $y))) + (func (export "f64.mul") (param $x f64) (param $y f64) (result f64) (f64.mul (local.get $x) (local.get $y))) + (func (export "f64.div") (param $x f64) (param $y f64) (result f64) (f64.div (local.get $x) (local.get $y))) + (func (export "f64.sqrt") (param $x f64) (result f64) (f64.sqrt (local.get $x))) + (func (export "f64.abs") (param $x f64) (result f64) (f64.abs (local.get $x))) + (func (export "f64.neg") (param $x f64) (result f64) (f64.neg (local.get $x))) + (func (export "f64.copysign") (param $x f64) (param $y f64) (result f64) (f64.copysign (local.get $x) (local.get $y))) + (func (export "f64.ceil") (param $x f64) (result f64) (f64.ceil (local.get $x))) + (func (export "f64.floor") (param $x f64) (result f64) (f64.floor (local.get $x))) + (func (export "f64.trunc") (param $x f64) (result f64) (f64.trunc (local.get $x))) + (func (export "f64.nearest") (param $x f64) (result f64) (f64.nearest (local.get $x))) + (func (export "f64.min") (param $x f64) (param $y f64) (result f64) (f64.min (local.get $x) (local.get $y))) + (func (export "f64.max") (param $x f64) (param $y f64) (result f64) (f64.max (local.get $x) (local.get $y))) +) + +;; Miscellaneous values. +(assert_return (invoke "f32.add" (f32.const 1.1234567890) (f32.const 1.2345e-10)) (f32.const 1.123456789)) +(assert_return (invoke "f64.add" (f64.const 1.1234567890) (f64.const 1.2345e-10)) (f64.const 0x1.1f9add37c11f7p+0)) + +;; Test adding the greatest value to 1.0 that rounds back to 1.0, and the +;; least that rounds to something greater. +(assert_return (invoke "f32.add" (f32.const 1.0) (f32.const 0x1p-24)) (f32.const 0x1.0p+0)) +(assert_return (invoke "f32.add" (f32.const 1.0) (f32.const 0x1.000002p-24)) (f32.const 0x1.000002p+0)) +(assert_return (invoke "f64.add" (f64.const 1.0) (f64.const 0x1p-53)) (f64.const 0x1.0p+0)) +(assert_return (invoke "f64.add" (f64.const 1.0) (f64.const 0x1.0000000000001p-53)) (f64.const 0x1.0000000000001p+0)) + +;; Max subnormal + min subnormal = min normal. +(assert_return (invoke "f32.add" (f32.const 0x1p-149) (f32.const 0x1.fffffcp-127)) (f32.const 0x1p-126)) +(assert_return (invoke "f64.add" (f64.const 0x0.0000000000001p-1022) (f64.const 0x0.fffffffffffffp-1022)) (f64.const 0x1p-1022)) + +;; Test for a case of double rounding, example from: +;; http://perso.ens-lyon.fr/jean-michel.muller/Handbook.html +;; section 3.3.1: A typical problem: "double rounding" +(assert_return (invoke "f32.add" (f32.const 0x1p+31) (f32.const 1024.25)) (f32.const 0x1.000008p+31)) +(assert_return (invoke "f64.add" (f64.const 0x1p+63) (f64.const 1024.25)) (f64.const 0x1.0000000000001p+63)) + +;; Test a case that was "tricky" on MMIX. +;; http://mmix.cs.hm.edu/bugs/bug_rounding.html +(assert_return (invoke "f64.add" (f64.const -0x1p-1008) (f64.const 0x0.0000000001716p-1022)) (f64.const -0x1.fffffffffffffp-1009)) + +;; http://www.vinc17.org/software/tst-ieee754.xsl +(assert_return (invoke "f64.add" (f64.const 9007199254740992) (f64.const 1.00001)) (f64.const 9007199254740994)) + +;; http://www.vinc17.org/software/test.java +(assert_return (invoke "f64.add" (f64.const 9007199254740994) (f64.const 0x1.fffep-1)) (f64.const 9007199254740994)) + +;; Computations that round differently in ties-to-odd mode. +(assert_return (invoke "f32.add" (f32.const 0x1p23) (f32.const 0x1p-1)) (f32.const 0x1p23)) +(assert_return (invoke "f32.add" (f32.const 0x1.000002p+23) (f32.const 0x1p-1)) (f32.const 0x1.000004p+23)) +(assert_return (invoke "f64.add" (f64.const 0x1p52) (f64.const 0x1p-1)) (f64.const 0x1p52)) +(assert_return (invoke "f64.add" (f64.const 0x1.0000000000001p+52) (f64.const 0x1p-1)) (f64.const 0x1.0000000000002p+52)) + +;; Computations that round differently in round-upward mode. +(assert_return (invoke "f32.add" (f32.const -0x1.39675ap+102) (f32.const 0x1.76c94cp-99)) (f32.const -0x1.39675ap+102)) +(assert_return (invoke "f32.add" (f32.const 0x1.6c0f24p+67) (f32.const -0x1.2b92dp+52)) (f32.const 0x1.6c0cccp+67)) +(assert_return (invoke "f32.add" (f32.const 0x1.e62318p-83) (f32.const 0x1.f74abep-125)) (f32.const 0x1.e62318p-83)) +(assert_return (invoke "f32.add" (f32.const 0x1.2a71d4p+39) (f32.const -0x1.c9f10cp+55)) (f32.const -0x1.c9efe2p+55)) +(assert_return (invoke "f32.add" (f32.const 0x1.f8f736p-15) (f32.const 0x1.7bd45ep+106)) (f32.const 0x1.7bd45ep+106)) +(assert_return (invoke "f64.add" (f64.const 0x1.f33e1fbca27aap-413) (f64.const -0x1.6b192891ed61p+249)) (f64.const -0x1.6b192891ed61p+249)) +(assert_return (invoke "f64.add" (f64.const -0x1.46f75d130eeb1p+76) (f64.const 0x1.25275d6f7a4acp-184)) (f64.const -0x1.46f75d130eeb1p+76)) +(assert_return (invoke "f64.add" (f64.const 0x1.04dec9265a731p-148) (f64.const -0x1.11eed4e8c127cp-12)) (f64.const -0x1.11eed4e8c127cp-12)) +(assert_return (invoke "f64.add" (f64.const 0x1.05773b7166b0ap+497) (f64.const 0x1.134022f2da37bp+66)) (f64.const 0x1.05773b7166b0ap+497)) +(assert_return (invoke "f64.add" (f64.const 0x1.ef4f794282a82p+321) (f64.const 0x1.14a82266badep+394)) (f64.const 0x1.14a82266badep+394)) + +;; Computations that round differently in round-downward mode. +(assert_return (invoke "f32.add" (f32.const 0x1.1bf976p+72) (f32.const -0x1.7f5868p+20)) (f32.const 0x1.1bf976p+72)) +(assert_return (invoke "f32.add" (f32.const 0x1.7f9c6cp-45) (f32.const -0x1.b9bb0ep-78)) (f32.const 0x1.7f9c6cp-45)) +(assert_return (invoke "f32.add" (f32.const -0x1.32d1bcp-42) (f32.const 0x1.f7d214p+125)) (f32.const 0x1.f7d214p+125)) +(assert_return (invoke "f32.add" (f32.const -0x1.8e5c0ep-44) (f32.const -0x1.3afa4cp-106)) (f32.const -0x1.8e5c0ep-44)) +(assert_return (invoke "f32.add" (f32.const 0x1.13cd78p-10) (f32.const -0x1.3af316p-107)) (f32.const 0x1.13cd78p-10)) +(assert_return (invoke "f64.add" (f64.const 0x1.f8dd15ca97d4ap+179) (f64.const -0x1.367317d1fe8bfp-527)) (f64.const 0x1.f8dd15ca97d4ap+179)) +(assert_return (invoke "f64.add" (f64.const 0x1.5db08d739228cp+155) (f64.const -0x1.fb316fa147dcbp-61)) (f64.const 0x1.5db08d739228cp+155)) +(assert_return (invoke "f64.add" (f64.const 0x1.bbb403cb85c07p-404) (f64.const -0x1.7e44046b8bbf3p-979)) (f64.const 0x1.bbb403cb85c07p-404)) +(assert_return (invoke "f64.add" (f64.const -0x1.34d38af291831p+147) (f64.const -0x1.9890b47439953p+139)) (f64.const -0x1.366c1ba705bcap+147)) +(assert_return (invoke "f64.add" (f64.const -0x1.b61dedf4e0306p+3) (f64.const 0x1.09e2f31773c4ap+290)) (f64.const 0x1.09e2f31773c4ap+290)) + +;; Computations that round differently in round-toward-zero mode. +(assert_return (invoke "f32.add" (f32.const -0x1.129bd8p-117) (f32.const 0x1.c75012p-43)) (f32.const 0x1.c75012p-43)) +(assert_return (invoke "f32.add" (f32.const -0x1.c204a2p-16) (f32.const 0x1.80b132p-27)) (f32.const -0x1.c1d48cp-16)) +(assert_return (invoke "f32.add" (f32.const -0x1.decc1cp+36) (f32.const 0x1.c688dap-109)) (f32.const -0x1.decc1cp+36)) +(assert_return (invoke "f32.add" (f32.const 0x1.61ce6ap-118) (f32.const -0x1.772892p+30)) (f32.const -0x1.772892p+30)) +(assert_return (invoke "f32.add" (f32.const -0x1.3dc826p-120) (f32.const 0x1.fc3f66p+95)) (f32.const 0x1.fc3f66p+95)) +(assert_return (invoke "f64.add" (f64.const 0x1.bf68acc263a0fp-777) (f64.const -0x1.5f9352965e5a6p+1004)) (f64.const -0x1.5f9352965e5a6p+1004)) +(assert_return (invoke "f64.add" (f64.const -0x1.76eaa70911f51p+516) (f64.const -0x1.2d746324ce47ap+493)) (f64.const -0x1.76eaa963fabb6p+516)) +(assert_return (invoke "f64.add" (f64.const -0x1.b637d82c15a7ap-967) (f64.const 0x1.cc654ccab4152p-283)) (f64.const 0x1.cc654ccab4152p-283)) +(assert_return (invoke "f64.add" (f64.const -0x1.a5b1fb66e846ep-509) (f64.const 0x1.4bdd36f0bb5ccp-860)) (f64.const -0x1.a5b1fb66e846ep-509)) +(assert_return (invoke "f64.add" (f64.const -0x1.14108da880f9ep+966) (f64.const 0x1.417f35701e89fp+800)) (f64.const -0x1.14108da880f9ep+966)) + +;; Computations that round differently on x87. +(assert_return (invoke "f64.add" (f64.const -0x1.fa0caf21ffebcp+804) (f64.const 0x1.4ca8fdcff89f9p+826)) (f64.const 0x1.4ca8f5e7c5e31p+826)) +(assert_return (invoke "f64.add" (f64.const 0x1.016f1fcbdfd38p+784) (f64.const 0x1.375dffcbc9a2cp+746)) (f64.const 0x1.016f1fcbe4b0fp+784)) +(assert_return (invoke "f64.add" (f64.const -0x1.dffda6d5bff3ap+624) (f64.const 0x1.f9e8cc2dff782p+674)) (f64.const 0x1.f9e8cc2dff77bp+674)) +(assert_return (invoke "f64.add" (f64.const 0x1.fff4b43687dfbp+463) (f64.const 0x1.0fd5617c4a809p+517)) (f64.const 0x1.0fd5617c4a809p+517)) +(assert_return (invoke "f64.add" (f64.const 0x1.535d380035da2p-995) (f64.const 0x1.cce37dddbb73bp-963)) (f64.const 0x1.cce37ddf0ed0fp-963)) + +;; Computations that round differently when computed via f32. +(assert_return (invoke "f64.add" (f64.const -0x1.d91cd3fc0c66fp+752) (f64.const -0x1.4e18c80229734p+952)) (f64.const -0x1.4e18c80229734p+952)) +(assert_return (invoke "f64.add" (f64.const 0x1.afc70fd36e372p+193) (f64.const -0x1.bd10a9b377b46p+273)) (f64.const -0x1.bd10a9b377b46p+273)) +(assert_return (invoke "f64.add" (f64.const -0x1.2abd570b078b2p+302) (f64.const 0x1.b3c1ad759cb5bp-423)) (f64.const -0x1.2abd570b078b2p+302)) +(assert_return (invoke "f64.add" (f64.const -0x1.5b2ae84c0686cp-317) (f64.const -0x1.dba7a1c022823p+466)) (f64.const -0x1.dba7a1c022823p+466)) +(assert_return (invoke "f64.add" (f64.const -0x1.ac627bd7cbf38p-198) (f64.const 0x1.2312e265b8d59p-990)) (f64.const -0x1.ac627bd7cbf38p-198)) + +;; Computations that utilize the maximum exponent value to avoid overflow. +(assert_return (invoke "f32.add" (f32.const 0x1.2b91ap+116) (f32.const 0x1.cbcd52p+127)) (f32.const 0x1.cbf2c4p+127)) +(assert_return (invoke "f32.add" (f32.const 0x1.96f392p+127) (f32.const -0x1.6b3fecp+107)) (f32.const 0x1.96f37cp+127)) +(assert_return (invoke "f32.add" (f32.const 0x1.132f1cp+118) (f32.const -0x1.63d632p+127)) (f32.const -0x1.634c9ap+127)) +(assert_return (invoke "f32.add" (f32.const -0x1.1dda64p+120) (f32.const -0x1.ef02ep+127)) (f32.const -0x1.f13e94p+127)) +(assert_return (invoke "f32.add" (f32.const -0x1.4ad8dap+127) (f32.const -0x1.eae082p+125)) (f32.const -0x1.c590fap+127)) +(assert_return (invoke "f64.add" (f64.const 0x1.017099f2a4b8bp+1023) (f64.const 0x1.1f63b28f05454p+981)) (f64.const 0x1.017099f2a5009p+1023)) +(assert_return (invoke "f64.add" (f64.const 0x1.d88b6c74984efp+1023) (f64.const 0x1.33b444775eabcp+990)) (f64.const 0x1.d88b6c7532291p+1023)) +(assert_return (invoke "f64.add" (f64.const -0x1.84576422fdf5p+1023) (f64.const 0x1.60ee6aa12fb9cp+1012)) (f64.const -0x1.842b4655a9cf1p+1023)) +(assert_return (invoke "f64.add" (f64.const -0x1.9aaace3e79f7dp+1001) (f64.const 0x1.e4068af295cb6p+1023)) (f64.const 0x1.e4068487ea926p+1023)) +(assert_return (invoke "f64.add" (f64.const 0x1.06cdae79f27b9p+1023) (f64.const -0x1.e05cb0c96f975p+991)) (f64.const 0x1.06cdae78121eep+1023)) + +;; Computations that utilize the minimum exponent value. +(assert_return (invoke "f32.add" (f32.const 0x1.6a1a2p-127) (f32.const 0x1.378p-140)) (f32.const 0x1.6a23dcp-127)) +(assert_return (invoke "f32.add" (f32.const 0x1.28p-144) (f32.const -0x1p-148)) (f32.const 0x1.18p-144)) +(assert_return (invoke "f32.add" (f32.const -0x1p-146) (f32.const 0x1.c3cap-128)) (f32.const 0x1.c3c9cp-128)) +(assert_return (invoke "f32.add" (f32.const -0x1.4p-145) (f32.const 0x1.424052p-122)) (f32.const 0x1.42405p-122)) +(assert_return (invoke "f32.add" (f32.const 0x1.c5p-141) (f32.const -0x1.72f8p-135)) (f32.const -0x1.6be4p-135)) +(assert_return (invoke "f64.add" (f64.const 0x1.4774c681d1e21p-1022) (f64.const -0x1.271e58e9f58cap-1021)) (f64.const -0x1.06c7eb5219373p-1022)) +(assert_return (invoke "f64.add" (f64.const 0x1.10b3a75e31916p-1021) (f64.const -0x1.ffb82b0e868a7p-1021)) (f64.const -0x1.de090760a9f22p-1022)) +(assert_return (invoke "f64.add" (f64.const -0x0.6b58448b8098ap-1022) (f64.const -0x1.579796ed04cbep-1022)) (f64.const -0x1.c2efdb7885648p-1022)) +(assert_return (invoke "f64.add" (f64.const 0x1.9eb9e7baae8d1p-1020) (f64.const -0x1.d58e136f8c6eep-1020)) (f64.const -0x0.db50aed377874p-1022)) +(assert_return (invoke "f64.add" (f64.const -0x1.f1115deeafa0bp-1022) (f64.const 0x1.221b1c87dca29p-1022)) (f64.const -0x0.cef64166d2fe2p-1022)) + +;; Test an add of the second-greatest finite value with the distance to greatest +;; finite value. +(assert_return (invoke "f32.add" (f32.const 0x1.fffffcp+127) (f32.const 0x1p+104)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "f64.add" (f64.const 0x1.ffffffffffffep+1023) (f64.const 0x1p+971)) (f64.const 0x1.fffffffffffffp+1023)) + +;; http://news.harvard.edu/gazette/story/2013/09/dawn-of-a-revolution/ +(assert_return (invoke "f32.add" (f32.const 2.0) (f32.const 2.0)) (f32.const 4.0)) +(assert_return (invoke "f64.add" (f64.const 2.0) (f64.const 2.0)) (f64.const 4.0)) + +;; Test rounding above the greatest finite value. +(assert_return (invoke "f32.add" (f32.const 0x1.fffffep+127) (f32.const 0x1.fffffep+102)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "f32.add" (f32.const 0x1.fffffep+127) (f32.const 0x1p+103)) (f32.const inf)) +(assert_return (invoke "f64.add" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+969)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64.add" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p+970)) (f64.const inf)) + +;; Test for a historic spreadsheet bug. +;; https://blogs.office.com/2007/09/25/calculation-issue-update/ +(assert_return (invoke "f32.sub" (f32.const 65536.0) (f32.const 0x1p-37)) (f32.const 65536.0)) +(assert_return (invoke "f64.sub" (f64.const 65536.0) (f64.const 0x1p-37)) (f64.const 0x1.fffffffffffffp+15)) + +;; Test subtracting the greatest value from 1.0 that rounds back to 1.0, and the +;; least that rounds to something less. +(assert_return (invoke "f32.sub" (f32.const 1.0) (f32.const 0x1p-25)) (f32.const 0x1.0p+0)) +(assert_return (invoke "f32.sub" (f32.const 1.0) (f32.const 0x1.000002p-25)) (f32.const 0x1.fffffep-1)) +(assert_return (invoke "f64.sub" (f64.const 1.0) (f64.const 0x1p-54)) (f64.const 0x1.0p+0)) +(assert_return (invoke "f64.sub" (f64.const 1.0) (f64.const 0x1.0000000000001p-54)) (f64.const 0x1.fffffffffffffp-1)) + +;; Computations that round differently in round-upward mode. +(assert_return (invoke "f32.sub" (f32.const 0x1.ee2466p-106) (f32.const -0x1.16277ep+119)) (f32.const 0x1.16277ep+119)) +(assert_return (invoke "f32.sub" (f32.const -0x1.446f9ep+119) (f32.const -0x1.4396a4p+43)) (f32.const -0x1.446f9ep+119)) +(assert_return (invoke "f32.sub" (f32.const 0x1.74773cp+0) (f32.const -0x1.a25512p-82)) (f32.const 0x1.74773cp+0)) +(assert_return (invoke "f32.sub" (f32.const 0x1.9345c4p-117) (f32.const 0x1.6792c2p-76)) (f32.const -0x1.6792c2p-76)) +(assert_return (invoke "f32.sub" (f32.const 0x1.9ecfa4p-18) (f32.const -0x1.864b44p-107)) (f32.const 0x1.9ecfa4p-18)) +(assert_return (invoke "f64.sub" (f64.const -0x1.5b798875e7845p-333) (f64.const -0x1.b5147117452fep-903)) (f64.const -0x1.5b798875e7845p-333)) +(assert_return (invoke "f64.sub" (f64.const -0x1.6c87baeb6d72dp+552) (f64.const -0x1.64fb35d4b5571p-158)) (f64.const -0x1.6c87baeb6d72dp+552)) +(assert_return (invoke "f64.sub" (f64.const 0x1.b3d369fcf74bp-461) (f64.const -0x1.ea1668c0dec93p-837)) (f64.const 0x1.b3d369fcf74bp-461)) +(assert_return (invoke "f64.sub" (f64.const 0x1.0abd449353eadp-1005) (f64.const -0x1.0422ea3e82ee9p+154)) (f64.const 0x1.0422ea3e82ee9p+154)) +(assert_return (invoke "f64.sub" (f64.const -0x1.aadbc6b43cc3dp-143) (f64.const -0x1.e7f922ef1ee58p-539)) (f64.const -0x1.aadbc6b43cc3dp-143)) + +;; Computations that round differently in round-downward mode. +(assert_return (invoke "f32.sub" (f32.const -0x1.61e262p+108) (f32.const -0x1.baf3e4p+112)) (f32.const 0x1.a4d5bep+112)) +(assert_return (invoke "f32.sub" (f32.const -0x1.62c2f6p+109) (f32.const 0x1.6e514ap+6)) (f32.const -0x1.62c2f6p+109)) +(assert_return (invoke "f32.sub" (f32.const -0x1.287c94p-83) (f32.const 0x1.0f2f9cp-24)) (f32.const -0x1.0f2f9cp-24)) +(assert_return (invoke "f32.sub" (f32.const -0x1.c8825cp-77) (f32.const -0x1.4aead6p-12)) (f32.const 0x1.4aead6p-12)) +(assert_return (invoke "f32.sub" (f32.const -0x1.2976a4p+99) (f32.const 0x1.c6e3b8p-59)) (f32.const -0x1.2976a4p+99)) +(assert_return (invoke "f64.sub" (f64.const -0x1.76cb28ae6c045p+202) (f64.const -0x1.0611f2af4e9b9p+901)) (f64.const 0x1.0611f2af4e9b9p+901)) +(assert_return (invoke "f64.sub" (f64.const 0x1.baf35eff22e9ep-368) (f64.const 0x1.5c3e08ecf73ecp-451)) (f64.const 0x1.baf35eff22e9ep-368)) +(assert_return (invoke "f64.sub" (f64.const -0x1.8fd354b376f1fp-200) (f64.const 0x1.513c860f386ffp-508)) (f64.const -0x1.8fd354b376f1fp-200)) +(assert_return (invoke "f64.sub" (f64.const -0x1.760d447230ae6p-992) (f64.const -0x1.16f788438ae3ep-328)) (f64.const 0x1.16f788438ae3ep-328)) +(assert_return (invoke "f64.sub" (f64.const -0x1.73aab4fcfc7ap+112) (f64.const 0x1.7c589f990b884p+171)) (f64.const -0x1.7c589f990b884p+171)) + +;; Computations that round differently in round-toward-zero mode. +(assert_return (invoke "f32.sub" (f32.const 0x1.ea264cp+95) (f32.const 0x1.852988p-15)) (f32.const 0x1.ea264cp+95)) +(assert_return (invoke "f32.sub" (f32.const -0x1.14ec7cp+19) (f32.const -0x1.0ad3fep-35)) (f32.const -0x1.14ec7cp+19)) +(assert_return (invoke "f32.sub" (f32.const -0x1.3251dap-36) (f32.const -0x1.49c97ep-56)) (f32.const -0x1.3251c6p-36)) +(assert_return (invoke "f32.sub" (f32.const -0x1.13565ep-14) (f32.const 0x1.2f89a8p-13)) (f32.const -0x1.b934d8p-13)) +(assert_return (invoke "f32.sub" (f32.const -0x1.6032b6p-33) (f32.const -0x1.bb5196p-104)) (f32.const -0x1.6032b6p-33)) +(assert_return (invoke "f64.sub" (f64.const -0x1.b5b0797af491p-157) (f64.const -0x1.694b8348189e8p+722)) (f64.const 0x1.694b8348189e8p+722)) +(assert_return (invoke "f64.sub" (f64.const -0x1.72b142826ed73p+759) (f64.const -0x1.010477bc9afbdp+903)) (f64.const 0x1.010477bc9afbdp+903)) +(assert_return (invoke "f64.sub" (f64.const 0x1.83273b6bb94cfp-796) (f64.const 0x1.1a93f948a2abbp+181)) (f64.const -0x1.1a93f948a2abbp+181)) +(assert_return (invoke "f64.sub" (f64.const -0x1.207e7156cbf2p-573) (f64.const 0x1.cf3f12fd3814dp-544)) (f64.const -0x1.cf3f13063c086p-544)) +(assert_return (invoke "f64.sub" (f64.const -0x1.837e6844f1718p-559) (f64.const -0x1.1c29b757f98abp-14)) (f64.const 0x1.1c29b757f98abp-14)) + +;; Computations that round differently on x87. +(assert_return (invoke "f64.sub" (f64.const 0x1.c21151a709b6cp-78) (f64.const 0x1.0a12fff8910f6p-115)) (f64.const 0x1.c21151a701663p-78)) +(assert_return (invoke "f64.sub" (f64.const 0x1.c57912aae2f64p-982) (f64.const 0x1.dbfbd4800b7cfp-1010)) (f64.const 0x1.c579128d2338fp-982)) +(assert_return (invoke "f64.sub" (f64.const 0x1.ffef4399af9c6p-254) (f64.const 0x1.edb96dfaea8b1p-200)) (f64.const -0x1.edb96dfaea8b1p-200)) +(assert_return (invoke "f64.sub" (f64.const -0x1.363eee391cde2p-39) (f64.const -0x1.a65462000265fp-69)) (f64.const -0x1.363eee32838c9p-39)) +(assert_return (invoke "f64.sub" (f64.const 0x1.59016dba002a1p-25) (f64.const 0x1.5d4374f124cccp-3)) (f64.const -0x1.5d436f8d1f15dp-3)) + +;; Computations that round differently when computed via f32. +(assert_return (invoke "f64.sub" (f64.const -0x1.18196bca005cfp-814) (f64.const -0x1.db7b01ce3f52fp-766)) (f64.const 0x1.db7b01ce3f51dp-766)) +(assert_return (invoke "f64.sub" (f64.const -0x1.d17b3528d219p+33) (f64.const 0x1.fd739d4ea220ap+367)) (f64.const -0x1.fd739d4ea220ap+367)) +(assert_return (invoke "f64.sub" (f64.const 0x1.dea46994de319p+114) (f64.const 0x1.b5b19cd55c7d3p-590)) (f64.const 0x1.dea46994de319p+114)) +(assert_return (invoke "f64.sub" (f64.const 0x1.b60f9b2fbd9ecp-489) (f64.const -0x1.6f81c59ec5b8ep-694)) (f64.const 0x1.b60f9b2fbd9ecp-489)) +(assert_return (invoke "f64.sub" (f64.const 0x1.5e423fe8571f4p-57) (f64.const 0x1.9624ed7c162dfp-618)) (f64.const 0x1.5e423fe8571f4p-57)) + +;; pow(e, π) - π +;; https://xkcd.com/217/ +(assert_return (invoke "f32.sub" (f32.const 0x1.724046p+4) (f32.const 0x1.921fb6p+1)) (f32.const 0x1.3ffc5p+4)) +(assert_return (invoke "f64.sub" (f64.const 0x1.724046eb0933ap+4) (f64.const 0x1.921fb54442d18p+1)) (f64.const 0x1.3ffc504280d97p+4)) + +;; https://www.cnet.com/news/googles-calculator-muffs-some-math-problems/ +(assert_return (invoke "f32.sub" (f32.const 2999999) (f32.const 2999998)) (f32.const 1.0)) +(assert_return (invoke "f32.sub" (f32.const 1999999) (f32.const 1999995)) (f32.const 4.0)) +(assert_return (invoke "f32.sub" (f32.const 1999999) (f32.const 1999993)) (f32.const 6.0)) +(assert_return (invoke "f32.sub" (f32.const 400002) (f32.const 400001)) (f32.const 1.0)) +(assert_return (invoke "f32.sub" (f32.const 400002) (f32.const 400000)) (f32.const 2.0)) +(assert_return (invoke "f64.sub" (f64.const 2999999999999999) (f64.const 2999999999999998)) (f64.const 1.0)) +(assert_return (invoke "f64.sub" (f64.const 1999999999999999) (f64.const 1999999999999995)) (f64.const 4.0)) +(assert_return (invoke "f64.sub" (f64.const 1999999999999999) (f64.const 1999999999999993)) (f64.const 6.0)) +(assert_return (invoke "f64.sub" (f64.const 400000000000002) (f64.const 400000000000001)) (f64.const 1.0)) +(assert_return (invoke "f64.sub" (f64.const 400000000000002) (f64.const 400000000000000)) (f64.const 2.0)) + +;; Min normal - max subnormal = min subnormal. +(assert_return (invoke "f32.sub" (f32.const 0x1p-126) (f32.const 0x1.fffffcp-127)) (f32.const 0x1p-149)) +(assert_return (invoke "f64.sub" (f64.const 0x1p-1022) (f64.const 0x0.fffffffffffffp-1022)) (f64.const 0x0.0000000000001p-1022)) + +;; Test subtraction of numbers very close to 1. +(assert_return (invoke "f32.sub" (f32.const 0x1.000002p+0) (f32.const 0x1.fffffep-1)) (f32.const 0x1.8p-23)) +(assert_return (invoke "f32.sub" (f32.const 0x1.000002p+0) (f32.const 0x1.0p+0)) (f32.const 0x1p-23)) +(assert_return (invoke "f32.sub" (f32.const 0x1p+0) (f32.const 0x1.fffffep-1)) (f32.const 0x1p-24)) +(assert_return (invoke "f64.sub" (f64.const 0x1.0000000000001p+0) (f64.const 0x1.fffffffffffffp-1)) (f64.const 0x1.8p-52)) +(assert_return (invoke "f64.sub" (f64.const 0x1.0000000000001p+0) (f64.const 0x1.0p+0)) (f64.const 0x1p-52)) +(assert_return (invoke "f64.sub" (f64.const 0x1p+0) (f64.const 0x1.fffffffffffffp-1)) (f64.const 0x1p-53)) + +;; Test the least value that can be subtracted from the max value to produce a +;; different value. +(assert_return (invoke "f32.sub" (f32.const 0x1.fffffep+127) (f32.const 0x1.fffffep+102)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "f32.sub" (f32.const 0x1.fffffep+127) (f32.const 0x1p+103)) (f32.const 0x1.fffffcp+127)) +(assert_return (invoke "f64.sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1.fffffffffffffp+969)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64.sub" (f64.const 0x1.fffffffffffffp+1023) (f64.const 0x1p+970)) (f64.const 0x1.ffffffffffffep+1023)) + +;; Miscellaneous values. +(assert_return (invoke "f32.mul" (f32.const 1e15) (f32.const 1e15)) (f32.const 0x1.93e592p+99)) +(assert_return (invoke "f32.mul" (f32.const 1e20) (f32.const 1e20)) (f32.const inf)) +(assert_return (invoke "f32.mul" (f32.const 1e25) (f32.const 1e25)) (f32.const inf)) +(assert_return (invoke "f64.mul" (f64.const 1e15) (f64.const 1e15)) (f64.const 0x1.93e5939a08ceap+99)) +(assert_return (invoke "f64.mul" (f64.const 1e20) (f64.const 1e20)) (f64.const 0x1.d6329f1c35ca5p+132)) +(assert_return (invoke "f64.mul" (f64.const 1e25) (f64.const 1e25)) (f64.const 0x1.11b0ec57e649bp+166)) + +;; Test for a case of double rounding, example from: +;; http://perso.ens-lyon.fr/jean-michel.muller/Handbook.html +;; section 3.3.1: A typical problem: "double rounding" +(assert_return (invoke "f32.mul" (f32.const 1848874880.0) (f32.const 19954563072.0)) (f32.const 0x1.000002p+65)) +(assert_return (invoke "f64.mul" (f64.const 1848874847.0) (f64.const 19954562207.0)) (f64.const 3.6893488147419111424e+19)) + +;; Test for a historic spreadsheet bug. +;; http://www.joelonsoftware.com/items/2007/09/26b.html +(assert_return (invoke "f32.mul" (f32.const 77.1) (f32.const 850)) (f32.const 65535)) +(assert_return (invoke "f64.mul" (f64.const 77.1) (f64.const 850)) (f64.const 65534.99999999999272404)) + +;; Computations that round differently in round-upward mode. +(assert_return (invoke "f32.mul" (f32.const -0x1.14df2ep+61) (f32.const 0x1.748878p-36)) (f32.const -0x1.92e7e8p+25)) +(assert_return (invoke "f32.mul" (f32.const -0x1.5629e2p+102) (f32.const -0x1.c33012p-102)) (f32.const 0x1.2d8604p+1)) +(assert_return (invoke "f32.mul" (f32.const -0x1.b17694p+92) (f32.const -0x1.e4b56ap-97)) (f32.const 0x1.9a5baep-4)) +(assert_return (invoke "f32.mul" (f32.const -0x1.1626a6p+79) (f32.const -0x1.c57d7p-75)) (f32.const 0x1.ecbaaep+4)) +(assert_return (invoke "f32.mul" (f32.const 0x1.7acf72p+53) (f32.const 0x1.6c89acp+5)) (f32.const 0x1.0db556p+59)) +(assert_return (invoke "f64.mul" (f64.const -0x1.25c293f6f37e4p+425) (f64.const 0x1.f5fd4fa41c6d8p+945)) (f64.const -inf)) +(assert_return (invoke "f64.mul" (f64.const -0x1.cc1ae79fffc5bp-986) (f64.const -0x1.c36ccc2861ca6p-219)) (f64.const 0x0p+0)) +(assert_return (invoke "f64.mul" (f64.const 0x1.c0232b3e64b56p+606) (f64.const -0x1.f6939cf3affaap+106)) (f64.const -0x1.b7e3aedf190d3p+713)) +(assert_return (invoke "f64.mul" (f64.const -0x1.60f289966b271p-313) (f64.const 0x1.28a5497f0c259p+583)) (f64.const -0x1.98fc50bcec259p+270)) +(assert_return (invoke "f64.mul" (f64.const 0x1.37dab12d3afa2p+795) (f64.const 0x1.81e156bd393f1p-858)) (f64.const 0x1.d6126554b8298p-63)) + +;; Computations that round differently in round-downward mode. +(assert_return (invoke "f32.mul" (f32.const -0x1.3f57a2p-89) (f32.const -0x1.041d68p+92)) (f32.const 0x1.4479bp+3)) +(assert_return (invoke "f32.mul" (f32.const 0x1.4d0582p+73) (f32.const 0x1.6e043ap+19)) (f32.const 0x1.dc236p+92)) +(assert_return (invoke "f32.mul" (f32.const -0x1.2fdap-32) (f32.const -0x1.e1731cp+74)) (f32.const 0x1.1db89ep+43)) +(assert_return (invoke "f32.mul" (f32.const 0x1.7bc8fep+67) (f32.const -0x1.3ad592p+15)) (f32.const -0x1.d3115ep+82)) +(assert_return (invoke "f32.mul" (f32.const 0x1.936742p+30) (f32.const -0x1.a7a19p+66)) (f32.const -0x1.4dc71ap+97)) +(assert_return (invoke "f64.mul" (f64.const -0x1.ba737b4ca3b13p-639) (f64.const 0x1.8923309857438p-314)) (f64.const -0x1.53bc0d07baa37p-952)) +(assert_return (invoke "f64.mul" (f64.const 0x1.7c1932e610219p-276) (f64.const -0x1.2605db646489fp-635)) (f64.const -0x1.b48da2b0d2ae3p-911)) +(assert_return (invoke "f64.mul" (f64.const -0x1.e43cdf3b2108p+329) (f64.const -0x1.99d96abbd61d1p+835)) (f64.const inf)) +(assert_return (invoke "f64.mul" (f64.const 0x1.4c19466551da3p+947) (f64.const 0x1.0bdcd6c7646e9p-439)) (f64.const 0x1.5b7cd8c3f638ap+508)) +(assert_return (invoke "f64.mul" (f64.const 0x1.ff1da1726e3dfp+339) (f64.const -0x1.043c44f52b158p+169)) (f64.const -0x1.03c9364bb585cp+509)) + +;; Computations that round differently in round-toward-zero mode. +(assert_return (invoke "f32.mul" (f32.const -0x1.907e8ap+46) (f32.const -0x1.5d3668p+95)) (f32.const inf)) +(assert_return (invoke "f32.mul" (f32.const -0x1.8c9f74p-3) (f32.const 0x1.e2b452p-99)) (f32.const -0x1.75edccp-101)) +(assert_return (invoke "f32.mul" (f32.const -0x1.cc605ap-19) (f32.const 0x1.ec321ap+105)) (f32.const -0x1.ba91a4p+87)) +(assert_return (invoke "f32.mul" (f32.const -0x1.5fbb7ap+56) (f32.const 0x1.a8965ep-96)) (f32.const -0x1.23ae8ep-39)) +(assert_return (invoke "f32.mul" (f32.const -0x1.fb7f12p+16) (f32.const 0x1.3a701ap-119)) (f32.const -0x1.37ac0cp-102)) +(assert_return (invoke "f64.mul" (f64.const -0x1.5b0266454c26bp-496) (f64.const -0x1.af5787e3e0399p+433)) (f64.const 0x1.2457d81949e0bp-62)) +(assert_return (invoke "f64.mul" (f64.const 0x1.0d54a82393d45p+478) (f64.const -0x1.425760807ceaep-764)) (f64.const -0x1.532068c8d0d5dp-286)) +(assert_return (invoke "f64.mul" (f64.const -0x1.b532af981786p+172) (f64.const 0x1.ada95085ba36fp+359)) (f64.const -0x1.6ee38c1e01864p+532)) +(assert_return (invoke "f64.mul" (f64.const 0x1.e132f4d49d1cep+768) (f64.const -0x1.a75afe9a7d864p+374)) (f64.const -inf)) +(assert_return (invoke "f64.mul" (f64.const 0x1.68bbf1cfff90ap+81) (f64.const 0x1.09cd17d652c5p+70)) (f64.const 0x1.768b8d67d794p+151)) + +;; Computations that round differently on x87. +(assert_return (invoke "f64.mul" (f64.const 0x1.f99fb602c89b7p-341) (f64.const 0x1.6caab46a31a2ep-575)) (f64.const 0x1.68201f986e9d7p-915)) +(assert_return (invoke "f64.mul" (f64.const -0x1.86999c5eee379p-9) (f64.const 0x1.6e3b9e0d53e0dp+723)) (f64.const -0x1.17654a0ef35f5p+715)) +(assert_return (invoke "f64.mul" (f64.const -0x1.069571b176f9p+367) (f64.const -0x1.e248b6ab0a0e3p-652)) (f64.const 0x1.eeaff575cae1dp-285)) +(assert_return (invoke "f64.mul" (f64.const 0x1.c217645777dd2p+775) (f64.const 0x1.d93f5715dd646p+60)) (f64.const 0x1.a0064aa1d920dp+836)) +(assert_return (invoke "f64.mul" (f64.const -0x1.848981b6e694ap-276) (f64.const 0x1.f5aacb64a0d19p+896)) (f64.const -0x1.7cb2296e6c2e5p+621)) + +;; Computations that round differently on x87 in double-precision mode. +(assert_return (invoke "f64.mul" (f64.const 0x1.db3bd2a286944p-599) (f64.const 0x1.ce910af1d55cap-425)) (f64.const 0x0.d6accdd538a39p-1022)) +(assert_return (invoke "f64.mul" (f64.const -0x1.aca223916012p-57) (f64.const -0x1.2b2b4958dd228p-966)) (f64.const 0x0.fa74eccae5615p-1022)) +(assert_return (invoke "f64.mul" (f64.const -0x1.bd062def16cffp-488) (f64.const -0x1.7ddd91a0c4c0ep-536)) (f64.const 0x0.a5f4d7769d90dp-1022)) +(assert_return (invoke "f64.mul" (f64.const -0x1.c6a56169e9cep-772) (f64.const 0x1.517d55a474122p-255)) (f64.const -0x0.12baf260afb77p-1022)) +(assert_return (invoke "f64.mul" (f64.const -0x1.08951b0b41705p-516) (f64.const -0x1.102dc27168d09p-507)) (f64.const 0x0.8ca6dbf3f592bp-1022)) + +;; Computations that round differently when computed via f32. +(assert_return (invoke "f64.mul" (f64.const 0x1.8d0dea50c8c9bp+852) (f64.const 0x1.21cac31d87a24p-881)) (f64.const 0x1.c177311f7cd73p-29)) +(assert_return (invoke "f64.mul" (f64.const 0x1.98049118e3063p-7) (f64.const 0x1.6362525151b58p-149)) (f64.const 0x1.1b358514103f9p-155)) +(assert_return (invoke "f64.mul" (f64.const -0x1.ea65cb0631323p+1) (f64.const 0x1.fce683201a19bp-41)) (f64.const -0x1.e76dc8c223667p-39)) +(assert_return (invoke "f64.mul" (f64.const 0x1.e4d235961d543p-373) (f64.const 0x1.bc56f20ef9a48p-205)) (f64.const 0x1.a4c09efcb71d6p-577)) +(assert_return (invoke "f64.mul" (f64.const -0x1.b9612e66faba8p+77) (f64.const 0x1.e2bc6aa782273p-348)) (f64.const -0x1.a026ea4f81db1p-270)) + +;; Test the least positive value with a positive square. +(assert_return (invoke "f32.mul" (f32.const 0x1p-75) (f32.const 0x1p-75)) (f32.const 0x0p+0)) +(assert_return (invoke "f32.mul" (f32.const 0x1.000002p-75) (f32.const 0x1.000002p-75)) (f32.const 0x1p-149)) +(assert_return (invoke "f64.mul" (f64.const 0x1.6a09e667f3bccp-538) (f64.const 0x1.6a09e667f3bccp-538)) (f64.const 0x0p+0)) +(assert_return (invoke "f64.mul" (f64.const 0x1.6a09e667f3bcdp-538) (f64.const 0x1.6a09e667f3bcdp-538)) (f64.const 0x0.0000000000001p-1022)) + +;; Test the greatest positive value with a finite square. +(assert_return (invoke "f32.mul" (f32.const 0x1.fffffep+63) (f32.const 0x1.fffffep+63)) (f32.const 0x1.fffffcp+127)) +(assert_return (invoke "f32.mul" (f32.const 0x1p+64) (f32.const 0x1p+64)) (f32.const inf)) +(assert_return (invoke "f64.mul" (f64.const 0x1.fffffffffffffp+511) (f64.const 0x1.fffffffffffffp+511)) (f64.const 0x1.ffffffffffffep+1023)) +(assert_return (invoke "f64.mul" (f64.const 0x1p+512) (f64.const 0x1p+512)) (f64.const inf)) + +;; Test the squares of values very close to 1. +(assert_return (invoke "f32.mul" (f32.const 0x1.000002p+0) (f32.const 0x1.000002p+0)) (f32.const 0x1.000004p+0)) +(assert_return (invoke "f32.mul" (f32.const 0x1.fffffep-1) (f32.const 0x1.fffffep-1)) (f32.const 0x1.fffffcp-1)) +(assert_return (invoke "f64.mul" (f64.const 0x1.0000000000001p+0) (f64.const 0x1.0000000000001p+0)) (f64.const 0x1.0000000000002p+0)) +(assert_return (invoke "f64.mul" (f64.const 0x1.fffffffffffffp-1) (f64.const 0x1.fffffffffffffp-1)) (f64.const 0x1.ffffffffffffep-1)) + +;; Test multiplication of numbers very close to 1. +(assert_return (invoke "f32.mul" (f32.const 0x1.000002p+0) (f32.const 0x1.fffffep-1)) (f32.const 0x1p+0)) +(assert_return (invoke "f32.mul" (f32.const 0x1.000004p+0) (f32.const 0x1.fffffcp-1)) (f32.const 0x1.000002p+0)) +(assert_return (invoke "f64.mul" (f64.const 0x1.0000000000001p+0) (f64.const 0x1.fffffffffffffp-1)) (f64.const 0x1p+0)) +(assert_return (invoke "f64.mul" (f64.const 0x1.0000000000002p+0) (f64.const 0x1.ffffffffffffep-1)) (f64.const 0x1.0000000000001p+0)) + +;; Test MIN * EPSILON. +;; http://www.mpfr.org/mpfr-2.0.1/patch2 +(assert_return (invoke "f32.mul" (f32.const 0x1p-126) (f32.const 0x1p-23)) (f32.const 0x1p-149)) +(assert_return (invoke "f64.mul" (f64.const 0x1p-1022) (f64.const 0x1p-52)) (f64.const 0x0.0000000000001p-1022)) + +;; http://opencores.org/bug,view,2454 +(assert_return (invoke "f32.mul" (f32.const -0x1.0006p+4) (f32.const 0x1.ap-132)) (f32.const -0x1.a009cp-128)) + +;; Miscellaneous values. +(assert_return (invoke "f32.div" (f32.const 1.123456789) (f32.const 100)) (f32.const 0x1.702264p-7)) +(assert_return (invoke "f32.div" (f32.const 8391667.0) (f32.const 12582905.0)) (f32.const 0x1.55754p-1)) +(assert_return (invoke "f32.div" (f32.const 65536.0) (f32.const 0x1p-37)) (f32.const 0x1p+53)) +(assert_return (invoke "f32.div" (f32.const 0x1.dcbf6ap+0) (f32.const 0x1.fffffep+127)) (f32.const 0x1.dcbf68p-128)) +(assert_return (invoke "f32.div" (f32.const 4) (f32.const 3)) (f32.const 0x1.555556p+0)) +(assert_return (invoke "f64.div" (f64.const 1.123456789) (f64.const 100)) (f64.const 0.01123456789)) +(assert_return (invoke "f64.div" (f64.const 8391667.0) (f64.const 12582905.0)) (f64.const 0x1.55753f1d9ba27p-1)) +(assert_return (invoke "f64.div" (f64.const 65536.0) (f64.const 0x1p-37)) (f64.const 0x1p+53)) +(assert_return (invoke "f64.div" (f64.const 0x1.dcbf6ap+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0.772fda8p-1022)) +(assert_return (invoke "f64.div" (f64.const 4) (f64.const 3)) (f64.const 0x1.5555555555555p+0)) + +;; Test for a historic hardware bug. +;; https://en.wikipedia.org/wiki/Pentium_FDIV_bug +(assert_return (invoke "f32.div" (f32.const 4195835) (f32.const 3145727)) (f32.const 0x1.557542p+0)) +(assert_return (invoke "f64.div" (f64.const 4195835) (f64.const 3145727)) (f64.const 0x1.557541c7c6b43p+0)) + +;; Computations that round differently in round-upward mode. +(assert_return (invoke "f32.div" (f32.const 0x1.6a6c5ap-48) (f32.const 0x1.fa0b7p+127)) (f32.const 0x0p+0)) +(assert_return (invoke "f32.div" (f32.const 0x1.616fb2p-87) (f32.const 0x1.332172p+68)) (f32.const 0x0p+0)) +(assert_return (invoke "f32.div" (f32.const -0x1.96e778p+16) (f32.const 0x1.eb0c56p-80)) (f32.const -0x1.a8440ap+95)) +(assert_return (invoke "f32.div" (f32.const -0x1.e2624p-76) (f32.const -0x1.ed236ep-122)) (f32.const 0x1.f4d584p+45)) +(assert_return (invoke "f32.div" (f32.const -0x1.e2374ep+41) (f32.const 0x1.71fcdcp-80)) (f32.const -0x1.4da706p+121)) +(assert_return (invoke "f64.div" (f64.const 0x1.163c09d0c38c1p+147) (f64.const 0x1.e04cc737348e6p+223)) (f64.const 0x1.289921caeed23p-77)) +(assert_return (invoke "f64.div" (f64.const 0x1.d6867e741e0a9p-626) (f64.const 0x1.335eb19a9aae4p-972)) (f64.const 0x1.87e342d11f519p+346)) +(assert_return (invoke "f64.div" (f64.const -0x1.d5edf648aeb98p+298) (f64.const 0x1.0dda15b079355p+640)) (f64.const -0x1.bdceaf9734b5cp-342)) +(assert_return (invoke "f64.div" (f64.const -0x1.b683e3934aedap+691) (f64.const 0x1.c364e1df00dffp+246)) (f64.const -0x1.f16456e7afe3bp+444)) +(assert_return (invoke "f64.div" (f64.const -0x1.44ca7539cc851p+540) (f64.const 0x1.58501bccc58fep+453)) (f64.const -0x1.e2f8657e0924ep+86)) + +;; Computations that round differently in round-downward mode. +(assert_return (invoke "f32.div" (f32.const -0x1.c2c54ap+69) (f32.const -0x1.00d142p-86)) (f32.const inf)) +(assert_return (invoke "f32.div" (f32.const 0x1.e35abep-46) (f32.const 0x1.c69dfp+44)) (f32.const 0x1.102eb4p-90)) +(assert_return (invoke "f32.div" (f32.const 0x1.45ff2ap+0) (f32.const -0x1.1e8754p+89)) (f32.const -0x1.23434ep-89)) +(assert_return (invoke "f32.div" (f32.const 0x1.8db18ap-51) (f32.const 0x1.47c678p-128)) (f32.const 0x1.369b96p+77)) +(assert_return (invoke "f32.div" (f32.const 0x1.78599p+90) (f32.const 0x1.534144p+87)) (f32.const 0x1.1bfddcp+3)) +(assert_return (invoke "f64.div" (f64.const 0x0.f331c4f47eb51p-1022) (f64.const -0x1.c7ff45bf6f03ap+362)) (f64.const -0x0p+0)) +(assert_return (invoke "f64.div" (f64.const -0x1.0fc8707b9d19cp-987) (f64.const 0x1.77524d5f4a563p-536)) (f64.const -0x1.72c1a937d231p-452)) +(assert_return (invoke "f64.div" (f64.const -0x1.edb3aa64bb338p-403) (f64.const -0x1.1c7c164320e4p+45)) (f64.const 0x1.bc44cc1c5ae63p-448)) +(assert_return (invoke "f64.div" (f64.const -0x1.6534b34e8686bp+80) (f64.const 0x1.c34a7fc59e3c3p-791)) (f64.const -0x1.95421bf291b66p+870)) +(assert_return (invoke "f64.div" (f64.const -0x1.91f58d7ed1237p+236) (f64.const -0x1.f190d808383c8p+55)) (f64.const 0x1.9d9eb0836f906p+180)) + +;; Computations that round differently in round-toward-zero mode. +(assert_return (invoke "f32.div" (f32.const 0x1.64b2a4p+26) (f32.const 0x1.e95752p-119)) (f32.const inf)) +(assert_return (invoke "f32.div" (f32.const -0x1.53c9b6p+77) (f32.const 0x1.d689ap+27)) (f32.const -0x1.71baa4p+49)) +(assert_return (invoke "f32.div" (f32.const 0x1.664a8ap+38) (f32.const -0x1.59dba2p+96)) (f32.const -0x1.0933f4p-58)) +(assert_return (invoke "f32.div" (f32.const -0x1.99e0fap+111) (f32.const -0x1.c2b5a8p+9)) (f32.const 0x1.d19de6p+101)) +(assert_return (invoke "f32.div" (f32.const -0x1.5a815ap+92) (f32.const -0x1.b5820ap+13)) (f32.const 0x1.9580b8p+78)) +(assert_return (invoke "f64.div" (f64.const -0x1.81fd1e2af7bebp-655) (f64.const 0x1.edefc4eae536cp-691)) (f64.const -0x1.901abdd91b661p+35)) +(assert_return (invoke "f64.div" (f64.const -0x1.47cf932953c43p+782) (f64.const -0x1.bc40496b1f2a1p-553)) (f64.const inf)) +(assert_return (invoke "f64.div" (f64.const -0x1.2bd2e8fbdcad7p-746) (f64.const 0x1.b115674cc476ep-65)) (f64.const -0x1.62752bf19fa81p-682)) +(assert_return (invoke "f64.div" (f64.const -0x1.f923e3fea9efep+317) (f64.const -0x1.8044c74d27a39p-588)) (f64.const 0x1.5086518cc7186p+905)) +(assert_return (invoke "f64.div" (f64.const 0x1.516ed2051d6bbp+181) (f64.const -0x1.c9f455eb9c2eep+214)) (f64.const -0x1.79414d67f2889p-34)) + +;; Computations that round differently on x87. +(assert_return (invoke "f64.div" (f64.const -0x1.9c52726aed366p+585) (f64.const -0x1.7d0568c75660fp+195)) (f64.const 0x1.1507ca2a65f23p+390)) +(assert_return (invoke "f64.div" (f64.const -0x1.522672f461667p+546) (f64.const -0x1.36d36572c9f71p+330)) (f64.const 0x1.1681369370619p+216)) +(assert_return (invoke "f64.div" (f64.const 0x1.01051b4e8cd61p+185) (f64.const -0x1.2cbb5ca3d33ebp+965)) (f64.const -0x1.b59471598a2f3p-781)) +(assert_return (invoke "f64.div" (f64.const 0x1.5f93bb80fc2cbp+217) (f64.const 0x1.7e051aae9f0edp+427)) (f64.const 0x1.d732fa926ba4fp-211)) +(assert_return (invoke "f64.div" (f64.const -0x1.e251d762163ccp+825) (f64.const 0x1.3ee63581e1796p+349)) (f64.const -0x1.8330077d90a07p+476)) + +;; Computations that round differently on x87 in double-precision mode. +(assert_return (invoke "f64.div" (f64.const 0x1.dcbf69f10006dp+0) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0.772fda7c4001bp-1022)) +(assert_return (invoke "f64.div" (f64.const 0x1.e14169442fbcap-1011) (f64.const 0x1.505451d62ff7dp+12)) (f64.const 0x0.b727e85f38b39p-1022)) +(assert_return (invoke "f64.div" (f64.const -0x1.d3ebe726ec964p-144) (f64.const -0x1.4a7bfc0b83608p+880)) (f64.const 0x0.5a9d8c50cbf87p-1022)) +(assert_return (invoke "f64.div" (f64.const -0x1.6c3def770aee1p-393) (f64.const -0x1.8b84724347598p+631)) (f64.const 0x0.3af0707fcd0c7p-1022)) +(assert_return (invoke "f64.div" (f64.const 0x1.16abda1bb3cb3p-856) (f64.const 0x1.6c9c7198eb1e6p+166)) (f64.const 0x0.c3a8fd6741649p-1022)) +(assert_return (invoke "f64.div" (f64.const 0x1.7057d6ab553cap-1005) (f64.const -0x1.2abf1e98660ebp+23)) (f64.const -0x0.04ee8d8ec01cdp-1022)) + +;; Computations that round differently when div is mul by reciprocal. +(assert_return (invoke "f32.div" (f32.const 0x1.ada9aap+89) (f32.const 0x1.69884cp+42)) (f32.const 0x1.303e2ep+47)) +(assert_return (invoke "f32.div" (f32.const 0x1.8281c8p+90) (f32.const -0x1.62883cp+106)) (f32.const -0x1.17169cp-16)) +(assert_return (invoke "f32.div" (f32.const 0x1.5c6be2p+81) (f32.const 0x1.d01dfep-1)) (f32.const 0x1.805e32p+81)) +(assert_return (invoke "f32.div" (f32.const -0x1.bbd252p+19) (f32.const -0x1.fba95p+33)) (f32.const 0x1.bf9d56p-15)) +(assert_return (invoke "f32.div" (f32.const -0x1.0f41d6p-42) (f32.const -0x1.3f2dbep+56)) (f32.const 0x1.b320d8p-99)) +(assert_return (invoke "f64.div" (f64.const 0x1.b2348a1c81899p+61) (f64.const -0x1.4a58aad903dd3p-861)) (f64.const -0x1.507c1e2a41b35p+922)) +(assert_return (invoke "f64.div" (f64.const 0x1.23fa5137a918ap-130) (f64.const -0x1.7268db1951263p-521)) (f64.const -0x1.93965e0d896bep+390)) +(assert_return (invoke "f64.div" (f64.const 0x1.dcb3915d82deep+669) (f64.const 0x1.50caaa1dc6b19p+638)) (f64.const 0x1.6a58ec814b09dp+31)) +(assert_return (invoke "f64.div" (f64.const -0x1.046e378c0cc46p+182) (f64.const 0x1.ac925009a922bp+773)) (f64.const -0x1.3720aa94dab18p-592)) +(assert_return (invoke "f64.div" (f64.const -0x1.8945fd69d8e11p-871) (f64.const -0x1.0a37870af809ap-646)) (f64.const 0x1.7a2e286c62382p-225)) + +;; Computations that round differently when computed via f32. +(assert_return (invoke "f64.div" (f64.const 0x1.82002af0ea1f3p-57) (f64.const 0x1.d0a9b0c2fa339p+0)) (f64.const 0x1.a952fbd1fc17cp-58)) +(assert_return (invoke "f64.div" (f64.const 0x1.1e12b515db471p-102) (f64.const -0x1.41fc3c94fba5p-42)) (f64.const -0x1.c6e50cccb7cb6p-61)) +(assert_return (invoke "f64.div" (f64.const 0x1.aba5adcd6f583p-41) (f64.const 0x1.17dfac639ce0fp-112)) (f64.const 0x1.872b0a008c326p+71)) +(assert_return (invoke "f64.div" (f64.const 0x1.cf82510d0ae6bp+89) (f64.const 0x1.0207d86498053p+97)) (f64.const 0x1.cbdc804e2cf14p-8)) +(assert_return (invoke "f64.div" (f64.const 0x1.4c82cbb508e21p-11) (f64.const -0x1.6b57208c2d5d5p+52)) (f64.const -0x1.d48e8b369129ap-64)) + +;; Division involving the maximum subnormal value and the minimum normal value. +(assert_return (invoke "f32.div" (f32.const 0x1p-126) (f32.const 0x1.fffffcp-127)) (f32.const 0x1.000002p+0)) +(assert_return (invoke "f32.div" (f32.const 0x1.fffffcp-127) (f32.const 0x1p-126)) (f32.const 0x1.fffffcp-1)) +(assert_return (invoke "f64.div" (f64.const 0x1p-1022) (f64.const 0x0.fffffffffffffp-1022)) (f64.const 0x1.0000000000001p+0)) +(assert_return (invoke "f64.div" (f64.const 0x0.fffffffffffffp-1022) (f64.const 0x1p-1022)) (f64.const 0x1.ffffffffffffep-1)) + +;; Test the least positive value with a positive quotient with the maximum value. +(assert_return (invoke "f32.div" (f32.const 0x1.fffffep-23) (f32.const 0x1.fffffep+127)) (f32.const 0x0p+0)) +(assert_return (invoke "f32.div" (f32.const 0x1p-22) (f32.const 0x1.fffffep+127)) (f32.const 0x1p-149)) +(assert_return (invoke "f64.div" (f64.const 0x1.fffffffffffffp-52) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0p+0)) +(assert_return (invoke "f64.div" (f64.const 0x1p-51) (f64.const 0x1.fffffffffffffp+1023)) (f64.const 0x0.0000000000001p-1022)) + +;; Test the least positive value with a finite reciprocal. +(assert_return (invoke "f32.div" (f32.const 1.0) (f32.const 0x1p-128)) (f32.const inf)) +(assert_return (invoke "f32.div" (f32.const 1.0) (f32.const 0x1.000008p-128)) (f32.const 0x1.fffffp+127)) +(assert_return (invoke "f64.div" (f64.const 1.0) (f64.const 0x0.4p-1022)) (f64.const inf)) +(assert_return (invoke "f64.div" (f64.const 1.0) (f64.const 0x0.4000000000001p-1022)) (f64.const 0x1.ffffffffffff8p+1023)) + +;; Test the least positive value that has a subnormal reciprocal. +(assert_return (invoke "f32.div" (f32.const 1.0) (f32.const 0x1.000002p+126)) (f32.const 0x1.fffffcp-127)) +(assert_return (invoke "f32.div" (f32.const 1.0) (f32.const 0x1p+126)) (f32.const 0x1p-126)) +(assert_return (invoke "f64.div" (f64.const 1.0) (f64.const 0x1.0000000000001p+1022)) (f64.const 0x0.fffffffffffffp-1022)) +(assert_return (invoke "f64.div" (f64.const 1.0) (f64.const 0x1p+1022)) (f64.const 0x1p-1022)) + +;; Test that the last binary digit of 1.0/3.0 is even in f32, +;; https://en.wikipedia.org/wiki/Single-precision_floating-point_format#Single-precision_examples +;; +;; and odd in f64, +;; https://en.wikipedia.org/wiki/Double-precision_floating-point_format#Double-precision_examples +;; +;; and that 1.0/3.0, 3.0/9.0, and 9.0/27.0 all agree. +;; http://www.netlib.org/paranoia +(assert_return (invoke "f32.div" (f32.const 0x1p+0) (f32.const 0x1.8p+1)) (f32.const 0x1.555556p-2)) +(assert_return (invoke "f32.div" (f32.const 0x3p+0) (f32.const 0x1.2p+3)) (f32.const 0x1.555556p-2)) +(assert_return (invoke "f32.div" (f32.const 0x1.2p+3) (f32.const 0x1.bp+4)) (f32.const 0x1.555556p-2)) +(assert_return (invoke "f64.div" (f64.const 0x1p+0) (f64.const 0x1.8p+1)) (f64.const 0x1.5555555555555p-2)) +(assert_return (invoke "f64.div" (f64.const 0x3p+0) (f64.const 0x1.2p+3)) (f64.const 0x1.5555555555555p-2)) +(assert_return (invoke "f64.div" (f64.const 0x1.2p+3) (f64.const 0x1.bp+4)) (f64.const 0x1.5555555555555p-2)) + +;; Test division of numbers very close to 1. +(assert_return (invoke "f32.div" (f32.const 0x1.000002p+0) (f32.const 0x1.fffffep-1)) (f32.const 0x1.000004p+0)) +(assert_return (invoke "f32.div" (f32.const 0x1.fffffep-1) (f32.const 0x1.000002p+0)) (f32.const 0x1.fffffap-1)) +(assert_return (invoke "f32.div" (f32.const 0x1.0p+0) (f32.const 0x1.fffffep-1)) (f32.const 0x1.000002p+0)) +(assert_return (invoke "f32.div" (f32.const 0x1.0p+0) (f32.const 0x1.000002p+0)) (f32.const 0x1.fffffcp-1)) +(assert_return (invoke "f64.div" (f64.const 0x1.0000000000001p+0) (f64.const 0x1.fffffffffffffp-1)) (f64.const 0x1.0000000000002p+0)) +(assert_return (invoke "f64.div" (f64.const 0x1.fffffffffffffp-1) (f64.const 0x1.0000000000001p+0)) (f64.const 0x1.ffffffffffffdp-1)) +(assert_return (invoke "f64.div" (f64.const 0x1.0p+0) (f64.const 0x1.fffffffffffffp-1)) (f64.const 0x1.0000000000001p+0)) +(assert_return (invoke "f64.div" (f64.const 0x1.0p+0) (f64.const 0x1.0000000000001p+0)) (f64.const 0x1.ffffffffffffep-1)) + +;; Test for bugs found in an early RISC-V implementation. +;; https://github.com/riscv/riscv-tests/pull/8 +(assert_return (invoke "f32.sqrt" (f32.const 0x1.56p+7)) (f32.const 0x1.a2744cp+3)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.594dfcp-23)) (f32.const 0x1.a4789cp-12)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.56p+7)) (f64.const 0x1.a2744ce9674f5p+3)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.594dfc70aa105p-23)) (f64.const 0x1.a4789c0e37f99p-12)) + +;; Computations that round differently on x87. +(assert_return (invoke "f64.sqrt" (f64.const 0x1.0263fcc94f259p-164)) (f64.const 0x1.0131485de579fp-82)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.352dfa278c43dp+338)) (f64.const 0x1.195607dac5417p+169)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.b15daa23924fap+402)) (f64.const 0x1.4d143db561493p+201)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.518c8e68cb753p-37)) (f64.const 0x1.9fb8ef1ad5bfdp-19)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.86d8b6518078ep-370)) (f64.const 0x1.3c5142a48fcadp-185)) + +;; Test another sqrt case on x87. +;; https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52593 +(assert_return (invoke "f64.sqrt" (f64.const 0x1.fffffffffffffp-1)) (f64.const 0x1.fffffffffffffp-1)) + +;; Computations that round differently in round-upward mode. +(assert_return (invoke "f32.sqrt" (f32.const 0x1.098064p-3)) (f32.const 0x1.70b23p-2)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.d9befp+100)) (f32.const 0x1.5c4052p+50)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.42b5b6p-4)) (f32.const 0x1.1f6d0ep-2)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.3684dp-71)) (f32.const 0x1.8ebae2p-36)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.d8bc4ep-11)) (f32.const 0x1.ebf9eap-6)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.5c39f220d5704p-924)) (f64.const 0x1.2a92bc24ceae9p-462)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.53521a635745cp+727)) (f64.const 0x1.a0cfdc4ef8ff1p+363)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.dfd5bbc9f4678p+385)) (f64.const 0x1.efa817117c94cp+192)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.33f9640811cd4p+105)) (f64.const 0x1.8d17c9243baa3p+52)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.6c0ef0267ff45p+999)) (f64.const 0x1.afbcfae3f2b4p+499)) + +;; Computations that round differently in round-downward mode. +(assert_return (invoke "f32.sqrt" (f32.const 0x1.26a62ep+27)) (f32.const 0x1.84685p+13)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.166002p-113)) (f32.const 0x1.798762p-57)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.3dfb5p-15)) (f32.const 0x1.937e38p-8)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.30eb2cp-120)) (f32.const 0x1.176406p-60)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.cb705cp-123)) (f32.const 0x1.e5020ap-62)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.edae8aea0543p+695)) (f64.const 0x1.f6c1ea4fc8dd2p+347)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.f7ee4bda5c9c3p-763)) (f64.const 0x1.fbf30bdaf11c5p-382)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.a48f348266ad1p-30)) (f64.const 0x1.481ee7540baf7p-15)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.feb5a1ce3ed9cp-242)) (f64.const 0x1.6995060c20d46p-121)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.957d9796e3834p+930)) (f64.const 0x1.42305213157bap+465)) + +;; Computations that round differently in round-toward-zero mode. +(assert_return (invoke "f32.sqrt" (f32.const 0x1.65787cp+118)) (f32.const 0x1.2e82a4p+59)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.736044p+15)) (f32.const 0x1.b40e4p+7)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.a00edp-1)) (f32.const 0x1.cd8aecp-1)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.7a4c8p-87)) (f32.const 0x1.b819e4p-44)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.5d24d4p-94)) (f32.const 0x1.2af75ep-47)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.a008948ead274p+738)) (f64.const 0x1.4659b37c39b19p+369)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.70f6199ed21f5p-381)) (f64.const 0x1.b2a2bddf3300dp-191)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.35c1d49f2a352p+965)) (f64.const 0x1.8e3d9f01a9716p+482)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.3fbdcfb2b2a15p-45)) (f64.const 0x1.949ba4feca42ap-23)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.c201b94757145p-492)) (f64.const 0x1.5369ee6bf2967p-246)) + +;; Computations that round differently when computed via f32. +(assert_return (invoke "f64.sqrt" (f64.const -0x1.360e8d0032adp-963)) (f64.const nan:canonical)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.d9a6f5eef0503p+103)) (f64.const 0x1.ec73f56c166f6p+51)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.aa051a5c4ec27p-760)) (f64.const 0x1.4a3e771ff5149p-380)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.e5522a741babep-276)) (f64.const 0x1.607ae2b6feb7dp-138)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.4832badc0c061p+567)) (f64.const 0x1.99ec7934139b2p+283)) + +;; Test the least value with a sqrt that rounds to one. +(assert_return (invoke "f32.sqrt" (f32.const 0x1.000002p+0)) (f32.const 0x1p+0)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.000004p+0)) (f32.const 0x1.000002p+0)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.0000000000001p+0)) (f64.const 0x1p+0)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.0000000000002p+0)) (f64.const 0x1.0000000000001p+0)) + +;; Test the greatest value less than one for which sqrt is not an identity. +(assert_return (invoke "f32.sqrt" (f32.const 0x1.fffffcp-1)) (f32.const 0x1.fffffep-1)) +(assert_return (invoke "f32.sqrt" (f32.const 0x1.fffffap-1)) (f32.const 0x1.fffffcp-1)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.ffffffffffffep-1)) (f64.const 0x1.fffffffffffffp-1)) +(assert_return (invoke "f64.sqrt" (f64.const 0x1.ffffffffffffdp-1)) (f64.const 0x1.ffffffffffffep-1)) + +;; Test that the bitwise floating point operators are bitwise on NaN. + +(assert_return (invoke "f32.abs" (f32.const nan:0x0f1e2)) (f32.const nan:0x0f1e2)) +(assert_return (invoke "f32.abs" (f32.const -nan:0x0f1e2)) (f32.const nan:0x0f1e2)) +(assert_return (invoke "f64.abs" (f64.const nan:0x0f1e27a6b)) (f64.const nan:0x0f1e27a6b)) +(assert_return (invoke "f64.abs" (f64.const -nan:0x0f1e27a6b)) (f64.const nan:0x0f1e27a6b)) + +(assert_return (invoke "f32.neg" (f32.const nan:0x0f1e2)) (f32.const -nan:0x0f1e2)) +(assert_return (invoke "f32.neg" (f32.const -nan:0x0f1e2)) (f32.const nan:0x0f1e2)) +(assert_return (invoke "f64.neg" (f64.const nan:0x0f1e27a6b)) (f64.const -nan:0x0f1e27a6b)) +(assert_return (invoke "f64.neg" (f64.const -nan:0x0f1e27a6b)) (f64.const nan:0x0f1e27a6b)) + +(assert_return (invoke "f32.copysign" (f32.const nan:0x0f1e2) (f32.const nan)) (f32.const nan:0x0f1e2)) +(assert_return (invoke "f32.copysign" (f32.const nan:0x0f1e2) (f32.const -nan)) (f32.const -nan:0x0f1e2)) +(assert_return (invoke "f32.copysign" (f32.const -nan:0x0f1e2) (f32.const nan)) (f32.const nan:0x0f1e2)) +(assert_return (invoke "f32.copysign" (f32.const -nan:0x0f1e2) (f32.const -nan)) (f32.const -nan:0x0f1e2)) +(assert_return (invoke "f64.copysign" (f64.const nan:0x0f1e27a6b) (f64.const nan)) (f64.const nan:0x0f1e27a6b)) +(assert_return (invoke "f64.copysign" (f64.const nan:0x0f1e27a6b) (f64.const -nan)) (f64.const -nan:0x0f1e27a6b)) +(assert_return (invoke "f64.copysign" (f64.const -nan:0x0f1e27a6b) (f64.const nan)) (f64.const nan:0x0f1e27a6b)) +(assert_return (invoke "f64.copysign" (f64.const -nan:0x0f1e27a6b) (f64.const -nan)) (f64.const -nan:0x0f1e27a6b)) + +;; Test values close to 1.0. +(assert_return (invoke "f32.ceil" (f32.const 0x1.fffffep-1)) (f32.const 1.0)) +(assert_return (invoke "f32.ceil" (f32.const 0x1.000002p+0)) (f32.const 2.0)) +(assert_return (invoke "f64.ceil" (f64.const 0x1.fffffffffffffp-1)) (f64.const 1.0)) +(assert_return (invoke "f64.ceil" (f64.const 0x1.0000000000001p+0)) (f64.const 2.0)) + +;; Test the maximum and minimum value for which ceil is not an identity operator. +(assert_return (invoke "f32.ceil" (f32.const 0x1.fffffep+22)) (f32.const 0x1p+23)) +(assert_return (invoke "f32.ceil" (f32.const -0x1.fffffep+22)) (f32.const -0x1.fffffcp+22)) +(assert_return (invoke "f64.ceil" (f64.const 0x1.fffffffffffffp+51)) (f64.const 0x1p+52)) +(assert_return (invoke "f64.ceil" (f64.const -0x1.fffffffffffffp+51)) (f64.const -0x1.ffffffffffffep+51)) + +;; Test that implementations don't do the x+0x1p52-0x1p52 trick outside the +;; range where it's safe. +(assert_return (invoke "f32.ceil" (f32.const 0x1.fffffep+23)) (f32.const 0x1.fffffep+23)) +(assert_return (invoke "f32.ceil" (f32.const -0x1.fffffep+23)) (f32.const -0x1.fffffep+23)) +(assert_return (invoke "f64.ceil" (f64.const 0x1.fffffffffffffp+52)) (f64.const 0x1.fffffffffffffp+52)) +(assert_return (invoke "f64.ceil" (f64.const -0x1.fffffffffffffp+52)) (f64.const -0x1.fffffffffffffp+52)) + +;; Test values close to -1.0. +(assert_return (invoke "f32.floor" (f32.const -0x1.fffffep-1)) (f32.const -1.0)) +(assert_return (invoke "f32.floor" (f32.const -0x1.000002p+0)) (f32.const -2.0)) +(assert_return (invoke "f64.floor" (f64.const -0x1.fffffffffffffp-1)) (f64.const -1.0)) +(assert_return (invoke "f64.floor" (f64.const -0x1.0000000000001p+0)) (f64.const -2.0)) + +;; Test the maximum and minimum value for which floor is not an identity operator. +(assert_return (invoke "f32.floor" (f32.const -0x1.fffffep+22)) (f32.const -0x1p+23)) +(assert_return (invoke "f32.floor" (f32.const 0x1.fffffep+22)) (f32.const 0x1.fffffcp+22)) +(assert_return (invoke "f64.floor" (f64.const -0x1.fffffffffffffp+51)) (f64.const -0x1p+52)) +(assert_return (invoke "f64.floor" (f64.const 0x1.fffffffffffffp+51)) (f64.const 0x1.ffffffffffffep+51)) + +;; Test that floor isn't implemented as XMVectorFloor. +;; http://dss.stephanierct.com/DevBlog/?p=8#comment-4 +(assert_return (invoke "f32.floor" (f32.const 88607.0)) (f32.const 88607.0)) +(assert_return (invoke "f64.floor" (f64.const 88607.0)) (f64.const 88607.0)) + +;; Test the maximum and minimum value for which trunc is not an identity operator. +(assert_return (invoke "f32.trunc" (f32.const -0x1.fffffep+22)) (f32.const -0x1.fffffcp+22)) +(assert_return (invoke "f32.trunc" (f32.const 0x1.fffffep+22)) (f32.const 0x1.fffffcp+22)) +(assert_return (invoke "f64.trunc" (f64.const -0x1.fffffffffffffp+51)) (f64.const -0x1.ffffffffffffep+51)) +(assert_return (invoke "f64.trunc" (f64.const 0x1.fffffffffffffp+51)) (f64.const 0x1.ffffffffffffep+51)) + +;; Test that nearest isn't implemented naively. +;; http://blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1 +;; http://blog.frama-c.com/index.php?post/2013/05/04/nearbyintf3 +(assert_return (invoke "f32.nearest" (f32.const 0x1.000002p+23)) (f32.const 0x1.000002p+23)) +(assert_return (invoke "f32.nearest" (f32.const 0x1.000004p+23)) (f32.const 0x1.000004p+23)) +(assert_return (invoke "f32.nearest" (f32.const 0x1.fffffep-2)) (f32.const 0.0)) +(assert_return (invoke "f32.nearest" (f32.const 0x1.fffffep+47)) (f32.const 0x1.fffffep+47)) +(assert_return (invoke "f64.nearest" (f64.const 0x1.0000000000001p+52)) (f64.const 0x1.0000000000001p+52)) +(assert_return (invoke "f64.nearest" (f64.const 0x1.0000000000002p+52)) (f64.const 0x1.0000000000002p+52)) +(assert_return (invoke "f64.nearest" (f64.const 0x1.fffffffffffffp-2)) (f64.const 0.0)) +(assert_return (invoke "f64.nearest" (f64.const 0x1.fffffffffffffp+105)) (f64.const 0x1.fffffffffffffp+105)) + +;; Nearest should not round halfway cases away from zero (as C's round(3) does) +;; or up (as JS's Math.round does). +(assert_return (invoke "f32.nearest" (f32.const 4.5)) (f32.const 4.0)) +(assert_return (invoke "f32.nearest" (f32.const -4.5)) (f32.const -4.0)) +(assert_return (invoke "f32.nearest" (f32.const -3.5)) (f32.const -4.0)) +(assert_return (invoke "f64.nearest" (f64.const 4.5)) (f64.const 4.0)) +(assert_return (invoke "f64.nearest" (f64.const -4.5)) (f64.const -4.0)) +(assert_return (invoke "f64.nearest" (f64.const -3.5)) (f64.const -4.0)) + +;; Test the maximum and minimum value for which nearest is not an identity operator. +(assert_return (invoke "f32.nearest" (f32.const -0x1.fffffep+22)) (f32.const -0x1p+23)) +(assert_return (invoke "f32.nearest" (f32.const 0x1.fffffep+22)) (f32.const 0x1p+23)) +(assert_return (invoke "f64.nearest" (f64.const -0x1.fffffffffffffp+51)) (f64.const -0x1p+52)) +(assert_return (invoke "f64.nearest" (f64.const 0x1.fffffffffffffp+51)) (f64.const 0x1p+52)) diff --git a/runtime/unc-vm/tests/wast/spec/forward.wast b/runtime/unc-vm/tests/wast/spec/forward.wast new file mode 100644 index 000000000..7bb3770d7 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/forward.wast @@ -0,0 +1,20 @@ +(module + (func $even (export "even") (param $n i32) (result i32) + (if (result i32) (i32.eq (local.get $n) (i32.const 0)) + (then (i32.const 1)) + (else (call $odd (i32.sub (local.get $n) (i32.const 1)))) + ) + ) + + (func $odd (export "odd") (param $n i32) (result i32) + (if (result i32) (i32.eq (local.get $n) (i32.const 0)) + (then (i32.const 0)) + (else (call $even (i32.sub (local.get $n) (i32.const 1)))) + ) + ) +) + +(assert_return (invoke "even" (i32.const 13)) (i32.const 0)) +(assert_return (invoke "even" (i32.const 20)) (i32.const 1)) +(assert_return (invoke "odd" (i32.const 13)) (i32.const 1)) +(assert_return (invoke "odd" (i32.const 20)) (i32.const 0)) diff --git a/runtime/unc-vm/tests/wast/spec/func.wast b/runtime/unc-vm/tests/wast/spec/func.wast new file mode 100644 index 000000000..7189257c9 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/func.wast @@ -0,0 +1,961 @@ +;; Test `func` declarations, i.e. functions + +(module + ;; Auxiliary definition + (type $sig (func)) + (func $dummy) + + ;; Syntax + + (func) + (func (export "f")) + (func $f) + (func $h (export "g")) + + (func (local)) + (func (local) (local)) + (func (local i32)) + (func (local $x i32)) + (func (local i32 f64 i64)) + (func (local i32) (local f64)) + (func (local i32 f32) (local $x i64) (local) (local i32 f64)) + + (func (param)) + (func (param) (param)) + (func (param i32)) + (func (param $x i32)) + (func (param i32 f64 i64)) + (func (param i32) (param f64)) + (func (param i32 f32) (param $x i64) (param) (param i32 f64)) + + (func (result)) + (func (result) (result)) + (func (result i32) (unreachable)) + (func (result i32 f64 f32) (unreachable)) + (func (result i32) (result f64) (unreachable)) + (func (result i32 f32) (result i64) (result) (result i32 f64) (unreachable)) + + (type $sig-1 (func)) + (type $sig-2 (func (result i32))) + (type $sig-3 (func (param $x i32))) + (type $sig-4 (func (param i32 f64 i32) (result i32))) + + (func (export "type-use-1") (type $sig-1)) + (func (export "type-use-2") (type $sig-2) (i32.const 0)) + (func (export "type-use-3") (type $sig-3)) + (func (export "type-use-4") (type $sig-4) (i32.const 0)) + (func (export "type-use-5") (type $sig-2) (result i32) (i32.const 0)) + (func (export "type-use-6") (type $sig-3) (param i32)) + (func (export "type-use-7") + (type $sig-4) (param i32) (param f64 i32) (result i32) (i32.const 0) + ) + + (func (type $sig)) + (func (type $forward)) ;; forward reference + + (func $complex + (param i32 f32) (param $x i64) (param) (param i32) + (result) (result i32) (result) (result i64 i32) + (local f32) (local $y i32) (local i64 i32) (local) (local f64 i32) + (unreachable) (unreachable) + ) + (func $complex-sig + (type $sig) + (local f32) (local $y i32) (local i64 i32) (local) (local f64 i32) + (unreachable) (unreachable) + ) + + (type $forward (func)) + + ;; Typing of locals + + (func (export "local-first-i32") (result i32) (local i32 i32) (local.get 0)) + (func (export "local-first-i64") (result i64) (local i64 i64) (local.get 0)) + (func (export "local-first-f32") (result f32) (local f32 f32) (local.get 0)) + (func (export "local-first-f64") (result f64) (local f64 f64) (local.get 0)) + (func (export "local-second-i32") (result i32) (local i32 i32) (local.get 1)) + (func (export "local-second-i64") (result i64) (local i64 i64) (local.get 1)) + (func (export "local-second-f32") (result f32) (local f32 f32) (local.get 1)) + (func (export "local-second-f64") (result f64) (local f64 f64) (local.get 1)) + (func (export "local-mixed") (result f64) + (local f32) (local $x i32) (local i64 i32) (local) (local f64 i32) + (drop (f32.neg (local.get 0))) + (drop (i32.eqz (local.get 1))) + (drop (i64.eqz (local.get 2))) + (drop (i32.eqz (local.get 3))) + (drop (f64.neg (local.get 4))) + (drop (i32.eqz (local.get 5))) + (local.get 4) + ) + + ;; Typing of parameters + + (func (export "param-first-i32") (param i32 i32) (result i32) (local.get 0)) + (func (export "param-first-i64") (param i64 i64) (result i64) (local.get 0)) + (func (export "param-first-f32") (param f32 f32) (result f32) (local.get 0)) + (func (export "param-first-f64") (param f64 f64) (result f64) (local.get 0)) + (func (export "param-second-i32") (param i32 i32) (result i32) (local.get 1)) + (func (export "param-second-i64") (param i64 i64) (result i64) (local.get 1)) + (func (export "param-second-f32") (param f32 f32) (result f32) (local.get 1)) + (func (export "param-second-f64") (param f64 f64) (result f64) (local.get 1)) + (func (export "param-mixed") (param f32 i32) (param) (param $x i64) (param i32 f64 i32) + (result f64) + (drop (f32.neg (local.get 0))) + (drop (i32.eqz (local.get 1))) + (drop (i64.eqz (local.get 2))) + (drop (i32.eqz (local.get 3))) + (drop (f64.neg (local.get 4))) + (drop (i32.eqz (local.get 5))) + (local.get 4) + ) + + ;; Typing of results + + (func (export "empty")) + (func (export "value-void") (call $dummy)) + (func (export "value-i32") (result i32) (i32.const 77)) + (func (export "value-i64") (result i64) (i64.const 7777)) + (func (export "value-f32") (result f32) (f32.const 77.7)) + (func (export "value-f64") (result f64) (f64.const 77.77)) + (func (export "value-i32-f64") (result i32 f64) (i32.const 77) (f64.const 7)) + (func (export "value-i32-i32-i32") (result i32 i32 i32) + (i32.const 1) (i32.const 2) (i32.const 3) + ) + (func (export "value-block-void") (block (call $dummy) (call $dummy))) + (func (export "value-block-i32") (result i32) + (block (result i32) (call $dummy) (i32.const 77)) + ) + (func (export "value-block-i32-i64") (result i32 i64) + (block (result i32 i64) (call $dummy) (i32.const 1) (i64.const 2)) + ) + + (func (export "return-empty") (return)) + (func (export "return-i32") (result i32) (return (i32.const 78))) + (func (export "return-i64") (result i64) (return (i64.const 7878))) + (func (export "return-f32") (result f32) (return (f32.const 78.7))) + (func (export "return-f64") (result f64) (return (f64.const 78.78))) + (func (export "return-i32-f64") (result i32 f64) + (return (i32.const 78) (f64.const 78.78)) + ) + (func (export "return-i32-i32-i32") (result i32 i32 i32) + (return (i32.const 1) (i32.const 2) (i32.const 3)) + ) + (func (export "return-block-i32") (result i32) + (return (block (result i32) (call $dummy) (i32.const 77))) + ) + (func (export "return-block-i32-i64") (result i32 i64) + (return (block (result i32 i64) (call $dummy) (i32.const 1) (i64.const 2))) + ) + + (func (export "break-empty") (br 0)) + (func (export "break-i32") (result i32) (br 0 (i32.const 79))) + (func (export "break-i64") (result i64) (br 0 (i64.const 7979))) + (func (export "break-f32") (result f32) (br 0 (f32.const 79.9))) + (func (export "break-f64") (result f64) (br 0 (f64.const 79.79))) + (func (export "break-i32-f64") (result i32 f64) + (br 0 (i32.const 79) (f64.const 79.79)) + ) + (func (export "break-i32-i32-i32") (result i32 i32 i32) + (br 0 (i32.const 1) (i32.const 2) (i32.const 3)) + ) + (func (export "break-block-i32") (result i32) + (br 0 (block (result i32) (call $dummy) (i32.const 77))) + ) + (func (export "break-block-i32-i64") (result i32 i64) + (br 0 (block (result i32 i64) (call $dummy) (i32.const 1) (i64.const 2))) + ) + + (func (export "break-br_if-empty") (param i32) + (br_if 0 (local.get 0)) + ) + (func (export "break-br_if-num") (param i32) (result i32) + (drop (br_if 0 (i32.const 50) (local.get 0))) (i32.const 51) + ) + (func (export "break-br_if-num-num") (param i32) (result i32 i64) + (drop (drop (br_if 0 (i32.const 50) (i64.const 51) (local.get 0)))) + (i32.const 51) (i64.const 52) + ) + + (func (export "break-br_table-empty") (param i32) + (br_table 0 0 0 (local.get 0)) + ) + (func (export "break-br_table-num") (param i32) (result i32) + (br_table 0 0 (i32.const 50) (local.get 0)) (i32.const 51) + ) + (func (export "break-br_table-num-num") (param i32) (result i32 i64) + (br_table 0 0 (i32.const 50) (i64.const 51) (local.get 0)) + (i32.const 51) (i64.const 52) + ) + (func (export "break-br_table-nested-empty") (param i32) + (block (br_table 0 1 0 (local.get 0))) + ) + (func (export "break-br_table-nested-num") (param i32) (result i32) + (i32.add + (block (result i32) + (br_table 0 1 0 (i32.const 50) (local.get 0)) (i32.const 51) + ) + (i32.const 2) + ) + ) + (func (export "break-br_table-nested-num-num") (param i32) (result i32 i32) + (i32.add + (block (result i32 i32) + (br_table 0 1 0 (i32.const 50) (i32.const 51) (local.get 0)) + (i32.const 51) (i32.const -3) + ) + ) + (i32.const 52) + ) + + ;; Large signatures + + (func (export "large-sig") + (param i32 i64 f32 f32 i32 f64 f32 i32 i32 i32 f32 f64 f64 f64 i32 i32 f32) + (result f64 f32 i32 i32 i32 i64 f32 i32 i32 f32 f64 f64 i32 f32 i32 f64) + (local.get 5) + (local.get 2) + (local.get 0) + (local.get 8) + (local.get 7) + (local.get 1) + (local.get 3) + (local.get 9) + (local.get 4) + (local.get 6) + (local.get 13) + (local.get 11) + (local.get 15) + (local.get 16) + (local.get 14) + (local.get 12) + ) + + ;; Default initialization of locals + + (func (export "init-local-i32") (result i32) (local i32) (local.get 0)) + (func (export "init-local-i64") (result i64) (local i64) (local.get 0)) + (func (export "init-local-f32") (result f32) (local f32) (local.get 0)) + (func (export "init-local-f64") (result f64) (local f64) (local.get 0)) +) + +(assert_return (invoke "type-use-1")) +(assert_return (invoke "type-use-2") (i32.const 0)) +(assert_return (invoke "type-use-3" (i32.const 1))) +(assert_return + (invoke "type-use-4" (i32.const 1) (f64.const 1) (i32.const 1)) + (i32.const 0) +) +(assert_return (invoke "type-use-5") (i32.const 0)) +(assert_return (invoke "type-use-6" (i32.const 1))) +(assert_return + (invoke "type-use-7" (i32.const 1) (f64.const 1) (i32.const 1)) + (i32.const 0) +) + +(assert_return (invoke "local-first-i32") (i32.const 0)) +(assert_return (invoke "local-first-i64") (i64.const 0)) +(assert_return (invoke "local-first-f32") (f32.const 0)) +(assert_return (invoke "local-first-f64") (f64.const 0)) +(assert_return (invoke "local-second-i32") (i32.const 0)) +(assert_return (invoke "local-second-i64") (i64.const 0)) +(assert_return (invoke "local-second-f32") (f32.const 0)) +(assert_return (invoke "local-second-f64") (f64.const 0)) +(assert_return (invoke "local-mixed") (f64.const 0)) + +(assert_return + (invoke "param-first-i32" (i32.const 2) (i32.const 3)) (i32.const 2) +) +(assert_return + (invoke "param-first-i64" (i64.const 2) (i64.const 3)) (i64.const 2) +) +(assert_return + (invoke "param-first-f32" (f32.const 2) (f32.const 3)) (f32.const 2) +) +(assert_return + (invoke "param-first-f64" (f64.const 2) (f64.const 3)) (f64.const 2) +) +(assert_return + (invoke "param-second-i32" (i32.const 2) (i32.const 3)) (i32.const 3) +) +(assert_return + (invoke "param-second-i64" (i64.const 2) (i64.const 3)) (i64.const 3) +) +(assert_return + (invoke "param-second-f32" (f32.const 2) (f32.const 3)) (f32.const 3) +) +(assert_return + (invoke "param-second-f64" (f64.const 2) (f64.const 3)) (f64.const 3) +) + +(assert_return + (invoke "param-mixed" + (f32.const 1) (i32.const 2) (i64.const 3) + (i32.const 4) (f64.const 5.5) (i32.const 6) + ) + (f64.const 5.5) +) + +(assert_return (invoke "empty")) +(assert_return (invoke "value-void")) +(assert_return (invoke "value-i32") (i32.const 77)) +(assert_return (invoke "value-i64") (i64.const 7777)) +(assert_return (invoke "value-f32") (f32.const 77.7)) +(assert_return (invoke "value-f64") (f64.const 77.77)) +(assert_return (invoke "value-i32-f64") (i32.const 77) (f64.const 7)) +(assert_return (invoke "value-i32-i32-i32") + (i32.const 1) (i32.const 2) (i32.const 3) +) +(assert_return (invoke "value-block-void")) +(assert_return (invoke "value-block-i32") (i32.const 77)) +(assert_return (invoke "value-block-i32-i64") (i32.const 1) (i64.const 2)) + +(assert_return (invoke "return-empty")) +(assert_return (invoke "return-i32") (i32.const 78)) +(assert_return (invoke "return-i64") (i64.const 7878)) +(assert_return (invoke "return-f32") (f32.const 78.7)) +(assert_return (invoke "return-f64") (f64.const 78.78)) +(assert_return (invoke "return-i32-f64") (i32.const 78) (f64.const 78.78)) +(assert_return (invoke "return-i32-i32-i32") + (i32.const 1) (i32.const 2) (i32.const 3) +) +(assert_return (invoke "return-block-i32") (i32.const 77)) +(assert_return (invoke "return-block-i32-i64") (i32.const 1) (i64.const 2)) + +(assert_return (invoke "break-empty")) +(assert_return (invoke "break-i32") (i32.const 79)) +(assert_return (invoke "break-i64") (i64.const 7979)) +(assert_return (invoke "break-f32") (f32.const 79.9)) +(assert_return (invoke "break-f64") (f64.const 79.79)) +(assert_return (invoke "break-i32-f64") (i32.const 79) (f64.const 79.79)) +(assert_return (invoke "break-i32-i32-i32") + (i32.const 1) (i32.const 2) (i32.const 3) +) +(assert_return (invoke "break-block-i32") (i32.const 77)) +(assert_return (invoke "break-block-i32-i64") (i32.const 1) (i64.const 2)) + +(assert_return (invoke "break-br_if-empty" (i32.const 0))) +(assert_return (invoke "break-br_if-empty" (i32.const 2))) +(assert_return (invoke "break-br_if-num" (i32.const 0)) (i32.const 51)) +(assert_return (invoke "break-br_if-num" (i32.const 1)) (i32.const 50)) +(assert_return (invoke "break-br_if-num-num" (i32.const 0)) + (i32.const 51) (i64.const 52) +) +(assert_return (invoke "break-br_if-num-num" (i32.const 1)) + (i32.const 50) (i64.const 51) +) + +(assert_return (invoke "break-br_table-empty" (i32.const 0))) +(assert_return (invoke "break-br_table-empty" (i32.const 1))) +(assert_return (invoke "break-br_table-empty" (i32.const 5))) +(assert_return (invoke "break-br_table-empty" (i32.const -1))) +(assert_return (invoke "break-br_table-num" (i32.const 0)) (i32.const 50)) +(assert_return (invoke "break-br_table-num" (i32.const 1)) (i32.const 50)) +(assert_return (invoke "break-br_table-num" (i32.const 10)) (i32.const 50)) +(assert_return (invoke "break-br_table-num" (i32.const -100)) (i32.const 50)) +(assert_return (invoke "break-br_table-num-num" (i32.const 0)) + (i32.const 50) (i64.const 51) +) +(assert_return (invoke "break-br_table-num-num" (i32.const 1)) + (i32.const 50) (i64.const 51) +) +(assert_return (invoke "break-br_table-num-num" (i32.const 10)) + (i32.const 50) (i64.const 51) +) +(assert_return (invoke "break-br_table-num-num" (i32.const -100)) + (i32.const 50) (i64.const 51) +) +(assert_return (invoke "break-br_table-nested-empty" (i32.const 0))) +(assert_return (invoke "break-br_table-nested-empty" (i32.const 1))) +(assert_return (invoke "break-br_table-nested-empty" (i32.const 3))) +(assert_return (invoke "break-br_table-nested-empty" (i32.const -2))) +(assert_return + (invoke "break-br_table-nested-num" (i32.const 0)) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num" (i32.const 1)) (i32.const 50) +) +(assert_return + (invoke "break-br_table-nested-num" (i32.const 2)) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num" (i32.const -3)) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const 0)) + (i32.const 101) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const 1)) + (i32.const 50) (i32.const 51) +) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const 2)) + (i32.const 101) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const -3)) + (i32.const 101) (i32.const 52) +) + +(assert_return + (invoke "large-sig" + (i32.const 0) (i64.const 1) (f32.const 2) (f32.const 3) + (i32.const 4) (f64.const 5) (f32.const 6) (i32.const 7) + (i32.const 8) (i32.const 9) (f32.const 10) (f64.const 11) + (f64.const 12) (f64.const 13) (i32.const 14) (i32.const 15) + (f32.const 16) + ) + (f64.const 5) (f32.const 2) (i32.const 0) (i32.const 8) + (i32.const 7) (i64.const 1) (f32.const 3) (i32.const 9) + (i32.const 4) (f32.const 6) (f64.const 13) (f64.const 11) + (i32.const 15) (f32.const 16) (i32.const 14) (f64.const 12) +) + +(assert_return (invoke "init-local-i32") (i32.const 0)) +(assert_return (invoke "init-local-i64") (i64.const 0)) +(assert_return (invoke "init-local-f32") (f32.const 0)) +(assert_return (invoke "init-local-f64") (f64.const 0)) + + +;; Expansion of inline function types + +(module + (func $f (result f64) (f64.const 0)) ;; adds implicit type definition + (func $g (param i32)) ;; reuses explicit type definition + (type $t (func (param i32))) + + (func $i32->void (type 0)) ;; (param i32) + (func $void->f64 (type 1) (f64.const 0)) ;; (result f64) + (func $check + (call $i32->void (i32.const 0)) + (drop (call $void->f64)) + ) +) + +(assert_invalid + (module + (func $f (result f64) (f64.const 0)) ;; adds implicit type definition + (func $g (param i32)) ;; reuses explicit type definition + (func $h (result f64) (f64.const 1)) ;; reuses implicit type definition + (type $t (func (param i32))) + + (func (type 2)) ;; does not exist + ) + "unknown type" +) + +(assert_malformed + (module quote + "(func $f (result f64) (f64.const 0))" ;; adds implicit type definition + "(func $g (param i32))" ;; reuses explicit type definition + "(func $h (result f64) (f64.const 1))" ;; reuses implicit type definition + "(type $t (func (param i32)))" + + "(func (type 2) (param i32))" ;; does not exist + ) + "unknown type" +) + +(module + (type $proc (func (result i32))) + (type $sig (func (param i32) (result i32))) + + (func (export "f") (type $sig) + (local $var i32) + (local.get $var) + ) + + (func $g (type $sig) + (local $var i32) + (local.get $var) + ) + (func (export "g") (type $sig) + (call $g (local.get 0)) + ) + + (func (export "p") (type $proc) + (local $var i32) + (local.set 0 (i32.const 42)) + (local.get $var) + ) +) + +(assert_return (invoke "f" (i32.const 42)) (i32.const 0)) +(assert_return (invoke "g" (i32.const 42)) (i32.const 0)) +(assert_return (invoke "p") (i32.const 42)) + + +(module + (type $sig (func)) + + (func $empty-sig-1) ;; should be assigned type $sig + (func $complex-sig-1 (param f64 i64 f64 i64 f64 i64 f32 i32)) + (func $empty-sig-2) ;; should be assigned type $sig + (func $complex-sig-2 (param f64 i64 f64 i64 f64 i64 f32 i32)) + (func $complex-sig-3 (param f64 i64 f64 i64 f64 i64 f32 i32)) + (func $complex-sig-4 (param i64 i64 f64 i64 f64 i64 f32 i32)) + (func $complex-sig-5 (param i64 i64 f64 i64 f64 i64 f32 i32)) + + (type $empty-sig-duplicate (func)) + (type $complex-sig-duplicate (func (param i64 i64 f64 i64 f64 i64 f32 i32))) + (table funcref + (elem + $complex-sig-3 $empty-sig-2 $complex-sig-1 $complex-sig-3 $empty-sig-1 + $complex-sig-4 $complex-sig-5 + ) + ) + + (func (export "signature-explicit-reused") + (call_indirect (type $sig) (i32.const 1)) + (call_indirect (type $sig) (i32.const 4)) + ) + + (func (export "signature-implicit-reused") + ;; The implicit index 3 in this test depends on the function and + ;; type definitions, and may need adapting if they change. + (call_indirect (type 3) + (f64.const 0) (i64.const 0) (f64.const 0) (i64.const 0) + (f64.const 0) (i64.const 0) (f32.const 0) (i32.const 0) + (i32.const 0) + ) + (call_indirect (type 3) + (f64.const 0) (i64.const 0) (f64.const 0) (i64.const 0) + (f64.const 0) (i64.const 0) (f32.const 0) (i32.const 0) + (i32.const 2) + ) + (call_indirect (type 3) + (f64.const 0) (i64.const 0) (f64.const 0) (i64.const 0) + (f64.const 0) (i64.const 0) (f32.const 0) (i32.const 0) + (i32.const 3) + ) + ) + + (func (export "signature-explicit-duplicate") + (call_indirect (type $empty-sig-duplicate) (i32.const 1)) + ) + + (func (export "signature-implicit-duplicate") + (call_indirect (type $complex-sig-duplicate) + (i64.const 0) (i64.const 0) (f64.const 0) (i64.const 0) + (f64.const 0) (i64.const 0) (f32.const 0) (i32.const 0) + (i32.const 5) + ) + (call_indirect (type $complex-sig-duplicate) + (i64.const 0) (i64.const 0) (f64.const 0) (i64.const 0) + (f64.const 0) (i64.const 0) (f32.const 0) (i32.const 0) + (i32.const 6) + ) + ) +) + +(assert_return (invoke "signature-explicit-reused")) +(assert_return (invoke "signature-implicit-reused")) +(assert_return (invoke "signature-explicit-duplicate")) +(assert_return (invoke "signature-implicit-duplicate")) + + +;; Malformed type use + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (type $sig) (result i32) (param i32) (i32.const 0))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (param i32) (type $sig) (result i32) (i32.const 0))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (param i32) (result i32) (type $sig) (i32.const 0))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (result i32) (type $sig) (param i32) (i32.const 0))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (result i32) (param i32) (type $sig) (i32.const 0))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func (result i32) (param i32) (i32.const 0))" + ) + "unexpected token" +) + +(assert_malformed + (module quote + "(type $sig (func))" + "(func (type $sig) (result i32) (i32.const 0))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (type $sig) (result i32) (i32.const 0))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (type $sig) (param i32) (i32.const 0))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(func (type $sig) (param i32) (result i32) (unreachable))" + ) + "inline function type" +) + + +;; Invalid typing of locals + +(assert_invalid + (module (func $type-local-num-vs-num (result i64) (local i32) (local.get 0))) + "type mismatch" +) +(assert_invalid + (module (func $type-local-num-vs-num (local f32) (i32.eqz (local.get 0)))) + "type mismatch" +) +(assert_invalid + (module (func $type-local-num-vs-num (local f64 i64) (f64.neg (local.get 1)))) + "type mismatch" +) + + +;; Invalid typing of parameters + +(assert_invalid + (module (func $type-param-num-vs-num (param i32) (result i64) (local.get 0))) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num (param f32) (i32.eqz (local.get 0)))) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num (param f64 i64) (f64.neg (local.get 1)))) + "type mismatch" +) + + +;; Invalid typing of result + +(assert_invalid + (module (func $type-empty-i32 (result i32))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-i64 (result i64))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f32 (result f32))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f64 (result f64))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f64-i32 (result f64 i32))) + "type mismatch" +) + +(assert_invalid + (module (func $type-value-void-vs-num (result i32) + (nop) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-void-vs-nums (result i32 i32) + (nop) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-num-vs-void + (i32.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-nums-vs-void + (i32.const 0) (i64.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-num-vs-num (result i32) + (f32.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-num-vs-nums (result f32 f32) + (f32.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-nums-vs-num (result f32) + (f32.const 0) (f32.const 0) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-return-last-empty-vs-num (result i32) + (return) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-last-empty-vs-nums (result i32 i32) + (return) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-last-void-vs-num (result i32) + (return (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-last-void-vs-nums (result i32 i64) + (return (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-last-num-vs-num (result i32) + (return (i64.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-last-num-vs-nums (result i64 i64) + (return (i64.const 0)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-return-empty-vs-num (result i32) + (return) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-empty-vs-nums (result i32 i32) + (return) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-partial-vs-nums (result i32 i32) + (i32.const 1) (return) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-void-vs-num (result i32) + (return (nop)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-void-vs-nums (result i32 i32) + (return (nop)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-num-vs-num (result i32) + (return (i64.const 1)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-num-vs-nums (result i32 i32) + (return (i64.const 1)) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-first-num-vs-num (result i32) + (return (i64.const 1)) (return (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-first-num-vs-nums (result i32 i32) + (return (i32.const 1)) (return (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-last-void-vs-num (result i32) + (br 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-void-vs-nums (result i32 i32) + (br 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-num-vs-num (result i32) + (br 0 (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-num-vs-nums (result i32 i32) + (br 0 (i32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-void-vs-num (result i32) + (br 0) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-void-vs-nums (result i32 i32) + (br 0) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-num-vs-num (result i32) + (br 0 (i64.const 1)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-num-vs-nums (result i32 i32) + (br 0 (i32.const 1)) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-num-vs-num (result i32) + (br 0 (i64.const 1)) (br 0 (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-nested-empty-vs-num (result i32) + (block (br 1)) (br 0 (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-empty-vs-nums (result i32 i32) + (block (br 1)) (br 0 (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-void-vs-num (result i32) + (block (br 1 (nop))) (br 0 (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-void-vs-nums (result i32 i32) + (block (br 1 (nop))) (br 0 (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-num-vs-num (result i32) + (block (br 1 (i64.const 1))) (br 0 (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-num-vs-nums (result i32 i32) + (block (result i32) (br 1 (i32.const 1))) (br 0 (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) + + +;; Syntax errors + +(assert_malformed + (module quote "(func (nop) (local i32))") + "unexpected token" +) +(assert_malformed + (module quote "(func (nop) (param i32))") + "unexpected token" +) +(assert_malformed + (module quote "(func (nop) (result i32))") + "unexpected token" +) +(assert_malformed + (module quote "(func (local i32) (param i32))") + "unexpected token" +) +(assert_malformed + (module quote "(func (local i32) (result i32) (local.get 0))") + "unexpected token" +) +(assert_malformed + (module quote "(func (result i32) (param i32) (local.get 0))") + "unexpected token" +) + +;; Duplicate name errors + +(assert_malformed (module quote + "(func $foo)" + "(func $foo)") + "duplicate func") +(assert_malformed (module quote + "(import \"\" \"\" (func $foo))" + "(func $foo)") + "duplicate func") +(assert_malformed (module quote + "(import \"\" \"\" (func $foo))" + "(import \"\" \"\" (func $foo))") + "duplicate func") + +(assert_malformed (module quote "(func (param $foo i32) (param $foo i32))") + "duplicate local") +(assert_malformed (module quote "(func (param $foo i32) (local $foo i32))") + "duplicate local") +(assert_malformed (module quote "(func (local $foo i32) (local $foo i32))") + "duplicate local") diff --git a/runtime/unc-vm/tests/wast/spec/func_ptrs.wast b/runtime/unc-vm/tests/wast/spec/func_ptrs.wast new file mode 100644 index 000000000..f6f8e2c42 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/func_ptrs.wast @@ -0,0 +1,106 @@ +(module + (type (func)) ;; 0: void -> void + (type $S (func)) ;; 1: void -> void + (type (func (param))) ;; 2: void -> void + (type (func (result i32))) ;; 3: void -> i32 + (type (func (param) (result i32))) ;; 4: void -> i32 + (type $T (func (param i32) (result i32))) ;; 5: i32 -> i32 + (type $U (func (param i32))) ;; 6: i32 -> void + + (func $print (import "spectest" "print_i32") (type 6)) + + (func (type 0)) + (func (type $S)) + + (func (export "one") (type 4) (i32.const 13)) + (func (export "two") (type $T) (i32.add (local.get 0) (i32.const 1))) + + ;; Both signature and parameters are allowed (and required to match) + ;; since this allows the naming of parameters. + (func (export "three") (type $T) (param $a i32) (result i32) + (i32.sub (local.get 0) (i32.const 2)) + ) + + (func (export "four") (type $U) (call $print (local.get 0))) +) + +(assert_return (invoke "one") (i32.const 13)) +(assert_return (invoke "two" (i32.const 13)) (i32.const 14)) +(assert_return (invoke "three" (i32.const 13)) (i32.const 11)) +(invoke "four" (i32.const 83)) + +(assert_invalid (module (elem (i32.const 0))) "unknown table") +(assert_invalid (module (elem (i32.const 0) 0) (func)) "unknown table") + +(assert_invalid + (module (table 1 funcref) (elem (i64.const 0))) + "type mismatch" +) +(assert_invalid + (module (table 1 funcref) (elem (i32.ctz (i32.const 0)))) + "constant expression required" +) +(assert_invalid + (module (table 1 funcref) (elem (nop))) + "constant expression required" +) + +(assert_invalid (module (func (type 42))) "unknown type") +(assert_invalid (module (import "spectest" "print_i32" (func (type 43)))) "unknown type") + +(module + (type $T (func (param) (result i32))) + (type $U (func (param) (result i32))) + (table funcref (elem $t1 $t2 $t3 $u1 $u2 $t1 $t3)) + + (func $t1 (type $T) (i32.const 1)) + (func $t2 (type $T) (i32.const 2)) + (func $t3 (type $T) (i32.const 3)) + (func $u1 (type $U) (i32.const 4)) + (func $u2 (type $U) (i32.const 5)) + + (func (export "callt") (param $i i32) (result i32) + (call_indirect (type $T) (local.get $i)) + ) + + (func (export "callu") (param $i i32) (result i32) + (call_indirect (type $U) (local.get $i)) + ) +) + +(assert_return (invoke "callt" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "callt" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "callt" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "callt" (i32.const 3)) (i32.const 4)) +(assert_return (invoke "callt" (i32.const 4)) (i32.const 5)) +(assert_return (invoke "callt" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "callt" (i32.const 6)) (i32.const 3)) +(assert_trap (invoke "callt" (i32.const 7)) "undefined element") +(assert_trap (invoke "callt" (i32.const 100)) "undefined element") +(assert_trap (invoke "callt" (i32.const -1)) "undefined element") + +(assert_return (invoke "callu" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "callu" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "callu" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "callu" (i32.const 3)) (i32.const 4)) +(assert_return (invoke "callu" (i32.const 4)) (i32.const 5)) +(assert_return (invoke "callu" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "callu" (i32.const 6)) (i32.const 3)) +(assert_trap (invoke "callu" (i32.const 7)) "undefined element") +(assert_trap (invoke "callu" (i32.const 100)) "undefined element") +(assert_trap (invoke "callu" (i32.const -1)) "undefined element") + +(module + (type $T (func (result i32))) + (table funcref (elem 0 1)) + + (func $t1 (type $T) (i32.const 1)) + (func $t2 (type $T) (i32.const 2)) + + (func (export "callt") (param $i i32) (result i32) + (call_indirect (type $T) (local.get $i)) + ) +) + +(assert_return (invoke "callt" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "callt" (i32.const 1)) (i32.const 2)) diff --git a/runtime/unc-vm/tests/wast/spec/global.wast b/runtime/unc-vm/tests/wast/spec/global.wast new file mode 100644 index 000000000..9fa5e2231 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/global.wast @@ -0,0 +1,624 @@ +;; Test globals + +(module + (global (import "spectest" "global_i32") i32) + (global (import "spectest" "global_i64") i64) + + (global $a i32 (i32.const -2)) + (global (;3;) f32 (f32.const -3)) + (global (;4;) f64 (f64.const -4)) + (global $b i64 (i64.const -5)) + + (global $x (mut i32) (i32.const -12)) + (global (;7;) (mut f32) (f32.const -13)) + (global (;8;) (mut f64) (f64.const -14)) + (global $y (mut i64) (i64.const -15)) + + (global $z1 i32 (global.get 0)) + (global $z2 i64 (global.get 1)) + + (global $r externref (ref.null extern)) + (global $mr (mut externref) (ref.null extern)) + (global funcref (ref.null func)) + + (func (export "get-a") (result i32) (global.get $a)) + (func (export "get-b") (result i64) (global.get $b)) + (func (export "get-r") (result externref) (global.get $r)) + (func (export "get-mr") (result externref) (global.get $mr)) + (func (export "get-x") (result i32) (global.get $x)) + (func (export "get-y") (result i64) (global.get $y)) + (func (export "get-z1") (result i32) (global.get $z1)) + (func (export "get-z2") (result i64) (global.get $z2)) + (func (export "set-x") (param i32) (global.set $x (local.get 0))) + (func (export "set-y") (param i64) (global.set $y (local.get 0))) + (func (export "set-mr") (param externref) (global.set $mr (local.get 0))) + + (func (export "get-3") (result f32) (global.get 3)) + (func (export "get-4") (result f64) (global.get 4)) + (func (export "get-7") (result f32) (global.get 7)) + (func (export "get-8") (result f64) (global.get 8)) + (func (export "set-7") (param f32) (global.set 7 (local.get 0))) + (func (export "set-8") (param f64) (global.set 8 (local.get 0))) + + ;; As the argument of control constructs and instructions + + (memory 1) + + (func $dummy) + + (func (export "as-select-first") (result i32) + (select (global.get $x) (i32.const 2) (i32.const 3)) + ) + (func (export "as-select-mid") (result i32) + (select (i32.const 2) (global.get $x) (i32.const 3)) + ) + (func (export "as-select-last") (result i32) + (select (i32.const 2) (i32.const 3) (global.get $x)) + ) + + (func (export "as-loop-first") (result i32) + (loop (result i32) + (global.get $x) (call $dummy) (call $dummy) + ) + ) + (func (export "as-loop-mid") (result i32) + (loop (result i32) + (call $dummy) (global.get $x) (call $dummy) + ) + ) + (func (export "as-loop-last") (result i32) + (loop (result i32) + (call $dummy) (call $dummy) (global.get $x) + ) + ) + + (func (export "as-if-condition") (result i32) + (if (result i32) (global.get $x) + (then (call $dummy) (i32.const 2)) + (else (call $dummy) (i32.const 3)) + ) + ) + (func (export "as-if-then") (result i32) + (if (result i32) (i32.const 1) + (then (global.get $x)) (else (i32.const 2)) + ) + ) + (func (export "as-if-else") (result i32) + (if (result i32) (i32.const 0) + (then (i32.const 2)) (else (global.get $x)) + ) + ) + + (func (export "as-br_if-first") (result i32) + (block (result i32) + (br_if 0 (global.get $x) (i32.const 2)) + (return (i32.const 3)) + ) + ) + (func (export "as-br_if-last") (result i32) + (block (result i32) + (br_if 0 (i32.const 2) (global.get $x)) + (return (i32.const 3)) + ) + ) + + (func (export "as-br_table-first") (result i32) + (block (result i32) + (global.get $x) (i32.const 2) (br_table 0 0) + ) + ) + (func (export "as-br_table-last") (result i32) + (block (result i32) + (i32.const 2) (global.get $x) (br_table 0 0) + ) + ) + + (func $func (param i32 i32) (result i32) (local.get 0)) + (type $check (func (param i32 i32) (result i32))) + (table funcref (elem $func)) + (func (export "as-call_indirect-first") (result i32) + (block (result i32) + (call_indirect (type $check) + (global.get $x) (i32.const 2) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 2) (global.get $x) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 2) (i32.const 0) (global.get $x) + ) + ) + ) + + (func (export "as-store-first") + (global.get $x) (i32.const 1) (i32.store) + ) + (func (export "as-store-last") + (i32.const 0) (global.get $x) (i32.store) + ) + (func (export "as-load-operand") (result i32) + (i32.load (global.get $x)) + ) + (func (export "as-memory.grow-value") (result i32) + (memory.grow (global.get $x)) + ) + + (func $f (param i32) (result i32) (local.get 0)) + (func (export "as-call-value") (result i32) + (call $f (global.get $x)) + ) + + (func (export "as-return-value") (result i32) + (global.get $x) (return) + ) + (func (export "as-drop-operand") + (drop (global.get $x)) + ) + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (global.get $x))) + ) + + (func (export "as-local.set-value") (param i32) (result i32) + (local.set 0 (global.get $x)) + (local.get 0) + ) + (func (export "as-local.tee-value") (param i32) (result i32) + (local.tee 0 (global.get $x)) + ) + (func (export "as-global.set-value") (result i32) + (global.set $x (global.get $x)) + (global.get $x) + ) + + (func (export "as-unary-operand") (result i32) + (i32.eqz (global.get $x)) + ) + (func (export "as-binary-operand") (result i32) + (i32.mul + (global.get $x) (global.get $x) + ) + ) + (func (export "as-compare-operand") (result i32) + (i32.gt_u + (global.get 0) (i32.const 1) + ) + ) +) + +(assert_return (invoke "get-a") (i32.const -2)) +(assert_return (invoke "get-b") (i64.const -5)) +(assert_return (invoke "get-r") (ref.null extern)) +(assert_return (invoke "get-mr") (ref.null extern)) +(assert_return (invoke "get-x") (i32.const -12)) +(assert_return (invoke "get-y") (i64.const -15)) +(assert_return (invoke "get-z1") (i32.const 666)) +(assert_return (invoke "get-z2") (i64.const 666)) + +(assert_return (invoke "get-3") (f32.const -3)) +(assert_return (invoke "get-4") (f64.const -4)) +(assert_return (invoke "get-7") (f32.const -13)) +(assert_return (invoke "get-8") (f64.const -14)) + +(assert_return (invoke "set-x" (i32.const 6))) +(assert_return (invoke "set-y" (i64.const 7))) + +(assert_return (invoke "set-7" (f32.const 8))) +(assert_return (invoke "set-8" (f64.const 9))) + +(assert_return (invoke "get-x") (i32.const 6)) +(assert_return (invoke "get-y") (i64.const 7)) +(assert_return (invoke "get-7") (f32.const 8)) +(assert_return (invoke "get-8") (f64.const 9)) + +(assert_return (invoke "set-7" (f32.const 8))) +(assert_return (invoke "set-8" (f64.const 9))) +(assert_return (invoke "set-mr" (ref.extern 10))) + +(assert_return (invoke "get-x") (i32.const 6)) +(assert_return (invoke "get-y") (i64.const 7)) +(assert_return (invoke "get-7") (f32.const 8)) +(assert_return (invoke "get-8") (f64.const 9)) +(assert_return (invoke "get-mr") (ref.extern 10)) + +(assert_return (invoke "as-select-first") (i32.const 6)) +(assert_return (invoke "as-select-mid") (i32.const 2)) +(assert_return (invoke "as-select-last") (i32.const 2)) + +(assert_return (invoke "as-loop-first") (i32.const 6)) +(assert_return (invoke "as-loop-mid") (i32.const 6)) +(assert_return (invoke "as-loop-last") (i32.const 6)) + +(assert_return (invoke "as-if-condition") (i32.const 2)) +(assert_return (invoke "as-if-then") (i32.const 6)) +(assert_return (invoke "as-if-else") (i32.const 6)) + +(assert_return (invoke "as-br_if-first") (i32.const 6)) +(assert_return (invoke "as-br_if-last") (i32.const 2)) + +(assert_return (invoke "as-br_table-first") (i32.const 6)) +(assert_return (invoke "as-br_table-last") (i32.const 2)) + +(assert_return (invoke "as-call_indirect-first") (i32.const 6)) +(assert_return (invoke "as-call_indirect-mid") (i32.const 2)) +(assert_trap (invoke "as-call_indirect-last") "undefined element") + +(assert_return (invoke "as-store-first")) +(assert_return (invoke "as-store-last")) +(assert_return (invoke "as-load-operand") (i32.const 1)) +(assert_return (invoke "as-memory.grow-value") (i32.const 1)) + +(assert_return (invoke "as-call-value") (i32.const 6)) + +(assert_return (invoke "as-return-value") (i32.const 6)) +(assert_return (invoke "as-drop-operand")) +(assert_return (invoke "as-br-value") (i32.const 6)) + +(assert_return (invoke "as-local.set-value" (i32.const 1)) (i32.const 6)) +(assert_return (invoke "as-local.tee-value" (i32.const 1)) (i32.const 6)) +(assert_return (invoke "as-global.set-value") (i32.const 6)) + +(assert_return (invoke "as-unary-operand") (i32.const 0)) +(assert_return (invoke "as-binary-operand") (i32.const 36)) +(assert_return (invoke "as-compare-operand") (i32.const 1)) + +(assert_invalid + (module (global f32 (f32.const 0)) (func (global.set 0 (f32.const 1)))) + "global is immutable" +) + +(assert_invalid + (module (import "spectest" "global_i32" (global i32)) (func (global.set 0 (i32.const 1)))) + "global is immutable" +) + +;; mutable globals can be exported +(module (global (mut f32) (f32.const 0)) (export "a" (global 0))) +(module (global (export "a") (mut f32) (f32.const 0))) + +(assert_invalid + (module (global f32 (f32.neg (f32.const 0)))) + "constant expression required" +) + +(assert_invalid + (module (global f32 (local.get 0))) + "constant expression required" +) + +(assert_invalid + (module (global f32 (f32.neg (f32.const 1)))) + "constant expression required" +) + +(assert_invalid + (module (global i32 (i32.const 0) (nop))) + "constant expression required" +) + +(assert_invalid + (module (global i32 (i32.ctz (i32.const 0)))) + "constant expression required" +) + +(assert_invalid + (module (global i32 (nop))) + "constant expression required" +) + +(assert_invalid + (module (global i32 (f32.const 0))) + "type mismatch" +) + +(assert_invalid + (module (global i32 (i32.const 0) (i32.const 0))) + "type mismatch" +) + +(assert_invalid + (module (global i32 (;empty instruction sequence;))) + "type mismatch" +) + +(assert_invalid + (module (global (import "" "") externref) (global funcref (global.get 0))) + "type mismatch" +) + +(assert_invalid + (module (global (import "test" "global-i32") i32) (global i32 (global.get 0) (global.get 0))) + "type mismatch" +) + +(assert_invalid + (module (global (import "test" "global-i32") i32) (global i32 (i32.const 0) (global.get 0))) + "type mismatch" +) + +(assert_invalid + (module (global i32 (global.get 0))) + "unknown global" +) + +(assert_invalid + (module (global i32 (global.get 1)) (global i32 (i32.const 0))) + "unknown global" +) + +(assert_invalid + (module (global (import "test" "global-i32") i32) (global i32 (global.get 2))) + "unknown global" +) + +(assert_invalid + (module (global (import "test" "global-mut-i32") (mut i32)) (global i32 (global.get 0))) + "constant expression required" +) + +(module + (import "spectest" "global_i32" (global i32)) +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\98\80\80\80\00" ;; import section + "\01" ;; length 1 + "\08\73\70\65\63\74\65\73\74" ;; "spectest" + "\0a\67\6c\6f\62\61\6c\5f\69\33\32" ;; "global_i32" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\02" ;; malformed mutability + ) + "malformed mutability" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\98\80\80\80\00" ;; import section + "\01" ;; length 1 + "\08\73\70\65\63\74\65\73\74" ;; "spectest" + "\0a\67\6c\6f\62\61\6c\5f\69\33\32" ;; "global_i32" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\ff" ;; malformed mutability + ) + "malformed mutability" +) + +(module + (global i32 (i32.const 0)) +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\86\80\80\80\00" ;; global section + "\01" ;; length 1 + "\7f" ;; i32 + "\02" ;; malformed mutability + "\41\00" ;; i32.const 0 + "\0b" ;; end + ) + "malformed mutability" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\86\80\80\80\00" ;; global section + "\01" ;; length 1 + "\7f" ;; i32 + "\ff" ;; malformed mutability + "\41\00" ;; i32.const 0 + "\0b" ;; end + ) + "malformed mutability" +) + +;; global.get with invalid index +(assert_invalid + (module (func (result i32) (global.get 0))) + "unknown global" +) + +(assert_invalid + (module + (global i32 (i32.const 0)) + (func (result i32) (global.get 1)) + ) + "unknown global" +) + +(assert_invalid + (module + (import "spectest" "global_i32" (global i32)) + (func (result i32) (global.get 1)) + ) + "unknown global" +) + +(assert_invalid + (module + (import "spectest" "global_i32" (global i32)) + (global i32 (i32.const 0)) + (func (result i32) (global.get 2)) + ) + "unknown global" +) + +;; global.set with invalid index +(assert_invalid + (module (func (i32.const 0) (global.set 0))) + "unknown global" +) + +(assert_invalid + (module + (global i32 (i32.const 0)) + (func (i32.const 0) (global.set 1)) + ) + "unknown global" +) + +(assert_invalid + (module + (import "spectest" "global_i32" (global i32)) + (func (i32.const 0) (global.set 1)) + ) + "unknown global" +) + +(assert_invalid + (module + (import "spectest" "global_i32" (global i32)) + (global i32 (i32.const 0)) + (func (i32.const 0) (global.set 2)) + ) + "unknown global" +) + + +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty + (global.set $x) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-block + (i32.const 0) + (block (global.set $x)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-loop + (i32.const 0) + (loop (global.set $x)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-then + (i32.const 0) (i32.const 0) + (if (then (global.set $x))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-else + (i32.const 0) (i32.const 0) + (if (result i32) (then (i32.const 0)) (else (global.set $x))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-br + (i32.const 0) + (block (br 0 (global.set $x))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-br_if + (i32.const 0) + (block (br_if 0 (global.set $x))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-br_table + (i32.const 0) + (block (br_table 0 (global.set $x))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-return + (return (global.set $x)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-select + (select (global.set $x) (i32.const 1) (i32.const 2)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-call + (call 1 (global.set $x)) + ) + (func (param i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-global.set-value-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (global.set $x) (i32.const 0) + ) + ) + ) + ) + "type mismatch" +) + +;; Duplicate identifier errors + +(assert_malformed (module quote + "(global $foo i32 (i32.const 0))" + "(global $foo i32 (i32.const 0))") + "duplicate global") +(assert_malformed (module quote + "(import \"\" \"\" (global $foo i32))" + "(global $foo i32 (i32.const 0))") + "duplicate global") +(assert_malformed (module quote + "(import \"\" \"\" (global $foo i32))" + "(import \"\" \"\" (global $foo i32))") + "duplicate global") diff --git a/runtime/unc-vm/tests/wast/spec/i32.wast b/runtime/unc-vm/tests/wast/spec/i32.wast new file mode 100644 index 000000000..32862c347 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/i32.wast @@ -0,0 +1,976 @@ +;; i32 operations + +(module + (func (export "add") (param $x i32) (param $y i32) (result i32) (i32.add (local.get $x) (local.get $y))) + (func (export "sub") (param $x i32) (param $y i32) (result i32) (i32.sub (local.get $x) (local.get $y))) + (func (export "mul") (param $x i32) (param $y i32) (result i32) (i32.mul (local.get $x) (local.get $y))) + (func (export "div_s") (param $x i32) (param $y i32) (result i32) (i32.div_s (local.get $x) (local.get $y))) + (func (export "div_u") (param $x i32) (param $y i32) (result i32) (i32.div_u (local.get $x) (local.get $y))) + (func (export "rem_s") (param $x i32) (param $y i32) (result i32) (i32.rem_s (local.get $x) (local.get $y))) + (func (export "rem_u") (param $x i32) (param $y i32) (result i32) (i32.rem_u (local.get $x) (local.get $y))) + (func (export "and") (param $x i32) (param $y i32) (result i32) (i32.and (local.get $x) (local.get $y))) + (func (export "or") (param $x i32) (param $y i32) (result i32) (i32.or (local.get $x) (local.get $y))) + (func (export "xor") (param $x i32) (param $y i32) (result i32) (i32.xor (local.get $x) (local.get $y))) + (func (export "shl") (param $x i32) (param $y i32) (result i32) (i32.shl (local.get $x) (local.get $y))) + (func (export "shr_s") (param $x i32) (param $y i32) (result i32) (i32.shr_s (local.get $x) (local.get $y))) + (func (export "shr_u") (param $x i32) (param $y i32) (result i32) (i32.shr_u (local.get $x) (local.get $y))) + (func (export "rotl") (param $x i32) (param $y i32) (result i32) (i32.rotl (local.get $x) (local.get $y))) + (func (export "rotr") (param $x i32) (param $y i32) (result i32) (i32.rotr (local.get $x) (local.get $y))) + (func (export "clz") (param $x i32) (result i32) (i32.clz (local.get $x))) + (func (export "ctz") (param $x i32) (result i32) (i32.ctz (local.get $x))) + (func (export "popcnt") (param $x i32) (result i32) (i32.popcnt (local.get $x))) + (func (export "extend8_s") (param $x i32) (result i32) (i32.extend8_s (local.get $x))) + (func (export "extend16_s") (param $x i32) (result i32) (i32.extend16_s (local.get $x))) + (func (export "eqz") (param $x i32) (result i32) (i32.eqz (local.get $x))) + (func (export "eq") (param $x i32) (param $y i32) (result i32) (i32.eq (local.get $x) (local.get $y))) + (func (export "ne") (param $x i32) (param $y i32) (result i32) (i32.ne (local.get $x) (local.get $y))) + (func (export "lt_s") (param $x i32) (param $y i32) (result i32) (i32.lt_s (local.get $x) (local.get $y))) + (func (export "lt_u") (param $x i32) (param $y i32) (result i32) (i32.lt_u (local.get $x) (local.get $y))) + (func (export "le_s") (param $x i32) (param $y i32) (result i32) (i32.le_s (local.get $x) (local.get $y))) + (func (export "le_u") (param $x i32) (param $y i32) (result i32) (i32.le_u (local.get $x) (local.get $y))) + (func (export "gt_s") (param $x i32) (param $y i32) (result i32) (i32.gt_s (local.get $x) (local.get $y))) + (func (export "gt_u") (param $x i32) (param $y i32) (result i32) (i32.gt_u (local.get $x) (local.get $y))) + (func (export "ge_s") (param $x i32) (param $y i32) (result i32) (i32.ge_s (local.get $x) (local.get $y))) + (func (export "ge_u") (param $x i32) (param $y i32) (result i32) (i32.ge_u (local.get $x) (local.get $y))) +) + +(assert_return (invoke "add" (i32.const 1) (i32.const 1)) (i32.const 2)) +(assert_return (invoke "add" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "add" (i32.const -1) (i32.const -1)) (i32.const -2)) +(assert_return (invoke "add" (i32.const -1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "add" (i32.const 0x7fffffff) (i32.const 1)) (i32.const 0x80000000)) +(assert_return (invoke "add" (i32.const 0x80000000) (i32.const -1)) (i32.const 0x7fffffff)) +(assert_return (invoke "add" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "add" (i32.const 0x3fffffff) (i32.const 1)) (i32.const 0x40000000)) + +(assert_return (invoke "sub" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "sub" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "sub" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "sub" (i32.const 0x7fffffff) (i32.const -1)) (i32.const 0x80000000)) +(assert_return (invoke "sub" (i32.const 0x80000000) (i32.const 1)) (i32.const 0x7fffffff)) +(assert_return (invoke "sub" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "sub" (i32.const 0x3fffffff) (i32.const -1)) (i32.const 0x40000000)) + +(assert_return (invoke "mul" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "mul" (i32.const 1) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "mul" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "mul" (i32.const 0x10000000) (i32.const 4096)) (i32.const 0)) +(assert_return (invoke "mul" (i32.const 0x80000000) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "mul" (i32.const 0x80000000) (i32.const -1)) (i32.const 0x80000000)) +(assert_return (invoke "mul" (i32.const 0x7fffffff) (i32.const -1)) (i32.const 0x80000001)) +(assert_return (invoke "mul" (i32.const 0x01234567) (i32.const 0x76543210)) (i32.const 0x358e7470)) +(assert_return (invoke "mul" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 1)) + +(assert_trap (invoke "div_s" (i32.const 1) (i32.const 0)) "integer divide by zero") +(assert_trap (invoke "div_s" (i32.const 0) (i32.const 0)) "integer divide by zero") +(assert_trap (invoke "div_s" (i32.const 0x80000000) (i32.const -1)) "integer overflow") +(assert_trap (invoke "div_s" (i32.const 0x80000000) (i32.const 0)) "integer divide by zero") +(assert_return (invoke "div_s" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "div_s" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "div_s" (i32.const 0) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "div_s" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "div_s" (i32.const 0x80000000) (i32.const 2)) (i32.const 0xc0000000)) +(assert_return (invoke "div_s" (i32.const 0x80000001) (i32.const 1000)) (i32.const 0xffdf3b65)) +(assert_return (invoke "div_s" (i32.const 5) (i32.const 2)) (i32.const 2)) +(assert_return (invoke "div_s" (i32.const -5) (i32.const 2)) (i32.const -2)) +(assert_return (invoke "div_s" (i32.const 5) (i32.const -2)) (i32.const -2)) +(assert_return (invoke "div_s" (i32.const -5) (i32.const -2)) (i32.const 2)) +(assert_return (invoke "div_s" (i32.const 7) (i32.const 3)) (i32.const 2)) +(assert_return (invoke "div_s" (i32.const -7) (i32.const 3)) (i32.const -2)) +(assert_return (invoke "div_s" (i32.const 7) (i32.const -3)) (i32.const -2)) +(assert_return (invoke "div_s" (i32.const -7) (i32.const -3)) (i32.const 2)) +(assert_return (invoke "div_s" (i32.const 11) (i32.const 5)) (i32.const 2)) +(assert_return (invoke "div_s" (i32.const 17) (i32.const 7)) (i32.const 2)) + +(assert_trap (invoke "div_u" (i32.const 1) (i32.const 0)) "integer divide by zero") +(assert_trap (invoke "div_u" (i32.const 0) (i32.const 0)) "integer divide by zero") +(assert_return (invoke "div_u" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "div_u" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "div_u" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "div_u" (i32.const 0x80000000) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "div_u" (i32.const 0x80000000) (i32.const 2)) (i32.const 0x40000000)) +(assert_return (invoke "div_u" (i32.const 0x8ff00ff0) (i32.const 0x10001)) (i32.const 0x8fef)) +(assert_return (invoke "div_u" (i32.const 0x80000001) (i32.const 1000)) (i32.const 0x20c49b)) +(assert_return (invoke "div_u" (i32.const 5) (i32.const 2)) (i32.const 2)) +(assert_return (invoke "div_u" (i32.const -5) (i32.const 2)) (i32.const 0x7ffffffd)) +(assert_return (invoke "div_u" (i32.const 5) (i32.const -2)) (i32.const 0)) +(assert_return (invoke "div_u" (i32.const -5) (i32.const -2)) (i32.const 0)) +(assert_return (invoke "div_u" (i32.const 7) (i32.const 3)) (i32.const 2)) +(assert_return (invoke "div_u" (i32.const 11) (i32.const 5)) (i32.const 2)) +(assert_return (invoke "div_u" (i32.const 17) (i32.const 7)) (i32.const 2)) + +(assert_trap (invoke "rem_s" (i32.const 1) (i32.const 0)) "integer divide by zero") +(assert_trap (invoke "rem_s" (i32.const 0) (i32.const 0)) "integer divide by zero") +(assert_return (invoke "rem_s" (i32.const 0x7fffffff) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "rem_s" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "rem_s" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "rem_s" (i32.const 0) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "rem_s" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "rem_s" (i32.const 0x80000000) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "rem_s" (i32.const 0x80000000) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "rem_s" (i32.const 0x80000001) (i32.const 1000)) (i32.const -647)) +(assert_return (invoke "rem_s" (i32.const 5) (i32.const 2)) (i32.const 1)) +(assert_return (invoke "rem_s" (i32.const -5) (i32.const 2)) (i32.const -1)) +(assert_return (invoke "rem_s" (i32.const 5) (i32.const -2)) (i32.const 1)) +(assert_return (invoke "rem_s" (i32.const -5) (i32.const -2)) (i32.const -1)) +(assert_return (invoke "rem_s" (i32.const 7) (i32.const 3)) (i32.const 1)) +(assert_return (invoke "rem_s" (i32.const -7) (i32.const 3)) (i32.const -1)) +(assert_return (invoke "rem_s" (i32.const 7) (i32.const -3)) (i32.const 1)) +(assert_return (invoke "rem_s" (i32.const -7) (i32.const -3)) (i32.const -1)) +(assert_return (invoke "rem_s" (i32.const 11) (i32.const 5)) (i32.const 1)) +(assert_return (invoke "rem_s" (i32.const 17) (i32.const 7)) (i32.const 3)) + +(assert_trap (invoke "rem_u" (i32.const 1) (i32.const 0)) "integer divide by zero") +(assert_trap (invoke "rem_u" (i32.const 0) (i32.const 0)) "integer divide by zero") +(assert_return (invoke "rem_u" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "rem_u" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "rem_u" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "rem_u" (i32.const 0x80000000) (i32.const -1)) (i32.const 0x80000000)) +(assert_return (invoke "rem_u" (i32.const 0x80000000) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "rem_u" (i32.const 0x8ff00ff0) (i32.const 0x10001)) (i32.const 0x8001)) +(assert_return (invoke "rem_u" (i32.const 0x80000001) (i32.const 1000)) (i32.const 649)) +(assert_return (invoke "rem_u" (i32.const 5) (i32.const 2)) (i32.const 1)) +(assert_return (invoke "rem_u" (i32.const -5) (i32.const 2)) (i32.const 1)) +(assert_return (invoke "rem_u" (i32.const 5) (i32.const -2)) (i32.const 5)) +(assert_return (invoke "rem_u" (i32.const -5) (i32.const -2)) (i32.const -5)) +(assert_return (invoke "rem_u" (i32.const 7) (i32.const 3)) (i32.const 1)) +(assert_return (invoke "rem_u" (i32.const 11) (i32.const 5)) (i32.const 1)) +(assert_return (invoke "rem_u" (i32.const 17) (i32.const 7)) (i32.const 3)) + +(assert_return (invoke "and" (i32.const 1) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "and" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "and" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "and" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "and" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "and" (i32.const 0x7fffffff) (i32.const -1)) (i32.const 0x7fffffff)) +(assert_return (invoke "and" (i32.const 0xf0f0ffff) (i32.const 0xfffff0f0)) (i32.const 0xf0f0f0f0)) +(assert_return (invoke "and" (i32.const 0xffffffff) (i32.const 0xffffffff)) (i32.const 0xffffffff)) + +(assert_return (invoke "or" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "or" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "or" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "or" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "or" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const -1)) +(assert_return (invoke "or" (i32.const 0x80000000) (i32.const 0)) (i32.const 0x80000000)) +(assert_return (invoke "or" (i32.const 0xf0f0ffff) (i32.const 0xfffff0f0)) (i32.const 0xffffffff)) +(assert_return (invoke "or" (i32.const 0xffffffff) (i32.const 0xffffffff)) (i32.const 0xffffffff)) + +(assert_return (invoke "xor" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "xor" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "xor" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "xor" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "xor" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const -1)) +(assert_return (invoke "xor" (i32.const 0x80000000) (i32.const 0)) (i32.const 0x80000000)) +(assert_return (invoke "xor" (i32.const -1) (i32.const 0x80000000)) (i32.const 0x7fffffff)) +(assert_return (invoke "xor" (i32.const -1) (i32.const 0x7fffffff)) (i32.const 0x80000000)) +(assert_return (invoke "xor" (i32.const 0xf0f0ffff) (i32.const 0xfffff0f0)) (i32.const 0x0f0f0f0f)) +(assert_return (invoke "xor" (i32.const 0xffffffff) (i32.const 0xffffffff)) (i32.const 0)) + +(assert_return (invoke "shl" (i32.const 1) (i32.const 1)) (i32.const 2)) +(assert_return (invoke "shl" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "shl" (i32.const 0x7fffffff) (i32.const 1)) (i32.const 0xfffffffe)) +(assert_return (invoke "shl" (i32.const 0xffffffff) (i32.const 1)) (i32.const 0xfffffffe)) +(assert_return (invoke "shl" (i32.const 0x80000000) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "shl" (i32.const 0x40000000) (i32.const 1)) (i32.const 0x80000000)) +(assert_return (invoke "shl" (i32.const 1) (i32.const 31)) (i32.const 0x80000000)) +(assert_return (invoke "shl" (i32.const 1) (i32.const 32)) (i32.const 1)) +(assert_return (invoke "shl" (i32.const 1) (i32.const 33)) (i32.const 2)) +(assert_return (invoke "shl" (i32.const 1) (i32.const -1)) (i32.const 0x80000000)) +(assert_return (invoke "shl" (i32.const 1) (i32.const 0x7fffffff)) (i32.const 0x80000000)) + +(assert_return (invoke "shr_s" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "shr_s" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "shr_s" (i32.const -1) (i32.const 1)) (i32.const -1)) +(assert_return (invoke "shr_s" (i32.const 0x7fffffff) (i32.const 1)) (i32.const 0x3fffffff)) +(assert_return (invoke "shr_s" (i32.const 0x80000000) (i32.const 1)) (i32.const 0xc0000000)) +(assert_return (invoke "shr_s" (i32.const 0x40000000) (i32.const 1)) (i32.const 0x20000000)) +(assert_return (invoke "shr_s" (i32.const 1) (i32.const 32)) (i32.const 1)) +(assert_return (invoke "shr_s" (i32.const 1) (i32.const 33)) (i32.const 0)) +(assert_return (invoke "shr_s" (i32.const 1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "shr_s" (i32.const 1) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "shr_s" (i32.const 1) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "shr_s" (i32.const 0x80000000) (i32.const 31)) (i32.const -1)) +(assert_return (invoke "shr_s" (i32.const -1) (i32.const 32)) (i32.const -1)) +(assert_return (invoke "shr_s" (i32.const -1) (i32.const 33)) (i32.const -1)) +(assert_return (invoke "shr_s" (i32.const -1) (i32.const -1)) (i32.const -1)) +(assert_return (invoke "shr_s" (i32.const -1) (i32.const 0x7fffffff)) (i32.const -1)) +(assert_return (invoke "shr_s" (i32.const -1) (i32.const 0x80000000)) (i32.const -1)) + +(assert_return (invoke "shr_u" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "shr_u" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "shr_u" (i32.const -1) (i32.const 1)) (i32.const 0x7fffffff)) +(assert_return (invoke "shr_u" (i32.const 0x7fffffff) (i32.const 1)) (i32.const 0x3fffffff)) +(assert_return (invoke "shr_u" (i32.const 0x80000000) (i32.const 1)) (i32.const 0x40000000)) +(assert_return (invoke "shr_u" (i32.const 0x40000000) (i32.const 1)) (i32.const 0x20000000)) +(assert_return (invoke "shr_u" (i32.const 1) (i32.const 32)) (i32.const 1)) +(assert_return (invoke "shr_u" (i32.const 1) (i32.const 33)) (i32.const 0)) +(assert_return (invoke "shr_u" (i32.const 1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "shr_u" (i32.const 1) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "shr_u" (i32.const 1) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "shr_u" (i32.const 0x80000000) (i32.const 31)) (i32.const 1)) +(assert_return (invoke "shr_u" (i32.const -1) (i32.const 32)) (i32.const -1)) +(assert_return (invoke "shr_u" (i32.const -1) (i32.const 33)) (i32.const 0x7fffffff)) +(assert_return (invoke "shr_u" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "shr_u" (i32.const -1) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "shr_u" (i32.const -1) (i32.const 0x80000000)) (i32.const -1)) + +(assert_return (invoke "rotl" (i32.const 1) (i32.const 1)) (i32.const 2)) +(assert_return (invoke "rotl" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "rotl" (i32.const -1) (i32.const 1)) (i32.const -1)) +(assert_return (invoke "rotl" (i32.const 1) (i32.const 32)) (i32.const 1)) +(assert_return (invoke "rotl" (i32.const 0xabcd9876) (i32.const 1)) (i32.const 0x579b30ed)) +(assert_return (invoke "rotl" (i32.const 0xfe00dc00) (i32.const 4)) (i32.const 0xe00dc00f)) +(assert_return (invoke "rotl" (i32.const 0xb0c1d2e3) (i32.const 5)) (i32.const 0x183a5c76)) +(assert_return (invoke "rotl" (i32.const 0x00008000) (i32.const 37)) (i32.const 0x00100000)) +(assert_return (invoke "rotl" (i32.const 0xb0c1d2e3) (i32.const 0xff05)) (i32.const 0x183a5c76)) +(assert_return (invoke "rotl" (i32.const 0x769abcdf) (i32.const 0xffffffed)) (i32.const 0x579beed3)) +(assert_return (invoke "rotl" (i32.const 0x769abcdf) (i32.const 0x8000000d)) (i32.const 0x579beed3)) +(assert_return (invoke "rotl" (i32.const 1) (i32.const 31)) (i32.const 0x80000000)) +(assert_return (invoke "rotl" (i32.const 0x80000000) (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "rotr" (i32.const 1) (i32.const 1)) (i32.const 0x80000000)) +(assert_return (invoke "rotr" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "rotr" (i32.const -1) (i32.const 1)) (i32.const -1)) +(assert_return (invoke "rotr" (i32.const 1) (i32.const 32)) (i32.const 1)) +(assert_return (invoke "rotr" (i32.const 0xff00cc00) (i32.const 1)) (i32.const 0x7f806600)) +(assert_return (invoke "rotr" (i32.const 0x00080000) (i32.const 4)) (i32.const 0x00008000)) +(assert_return (invoke "rotr" (i32.const 0xb0c1d2e3) (i32.const 5)) (i32.const 0x1d860e97)) +(assert_return (invoke "rotr" (i32.const 0x00008000) (i32.const 37)) (i32.const 0x00000400)) +(assert_return (invoke "rotr" (i32.const 0xb0c1d2e3) (i32.const 0xff05)) (i32.const 0x1d860e97)) +(assert_return (invoke "rotr" (i32.const 0x769abcdf) (i32.const 0xffffffed)) (i32.const 0xe6fbb4d5)) +(assert_return (invoke "rotr" (i32.const 0x769abcdf) (i32.const 0x8000000d)) (i32.const 0xe6fbb4d5)) +(assert_return (invoke "rotr" (i32.const 1) (i32.const 31)) (i32.const 2)) +(assert_return (invoke "rotr" (i32.const 0x80000000) (i32.const 31)) (i32.const 1)) + +(assert_return (invoke "clz" (i32.const 0xffffffff)) (i32.const 0)) +(assert_return (invoke "clz" (i32.const 0)) (i32.const 32)) +(assert_return (invoke "clz" (i32.const 0x00008000)) (i32.const 16)) +(assert_return (invoke "clz" (i32.const 0xff)) (i32.const 24)) +(assert_return (invoke "clz" (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "clz" (i32.const 1)) (i32.const 31)) +(assert_return (invoke "clz" (i32.const 2)) (i32.const 30)) +(assert_return (invoke "clz" (i32.const 0x7fffffff)) (i32.const 1)) + +(assert_return (invoke "ctz" (i32.const -1)) (i32.const 0)) +(assert_return (invoke "ctz" (i32.const 0)) (i32.const 32)) +(assert_return (invoke "ctz" (i32.const 0x00008000)) (i32.const 15)) +(assert_return (invoke "ctz" (i32.const 0x00010000)) (i32.const 16)) +(assert_return (invoke "ctz" (i32.const 0x80000000)) (i32.const 31)) +(assert_return (invoke "ctz" (i32.const 0x7fffffff)) (i32.const 0)) + +(assert_return (invoke "popcnt" (i32.const -1)) (i32.const 32)) +(assert_return (invoke "popcnt" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "popcnt" (i32.const 0x00008000)) (i32.const 1)) +(assert_return (invoke "popcnt" (i32.const 0x80008000)) (i32.const 2)) +(assert_return (invoke "popcnt" (i32.const 0x7fffffff)) (i32.const 31)) +(assert_return (invoke "popcnt" (i32.const 0xAAAAAAAA)) (i32.const 16)) +(assert_return (invoke "popcnt" (i32.const 0x55555555)) (i32.const 16)) +(assert_return (invoke "popcnt" (i32.const 0xDEADBEEF)) (i32.const 24)) + +(assert_return (invoke "extend8_s" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "extend8_s" (i32.const 0x7f)) (i32.const 127)) +(assert_return (invoke "extend8_s" (i32.const 0x80)) (i32.const -128)) +(assert_return (invoke "extend8_s" (i32.const 0xff)) (i32.const -1)) +(assert_return (invoke "extend8_s" (i32.const 0x012345_00)) (i32.const 0)) +(assert_return (invoke "extend8_s" (i32.const 0xfedcba_80)) (i32.const -0x80)) +(assert_return (invoke "extend8_s" (i32.const -1)) (i32.const -1)) + +(assert_return (invoke "extend16_s" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "extend16_s" (i32.const 0x7fff)) (i32.const 32767)) +(assert_return (invoke "extend16_s" (i32.const 0x8000)) (i32.const -32768)) +(assert_return (invoke "extend16_s" (i32.const 0xffff)) (i32.const -1)) +(assert_return (invoke "extend16_s" (i32.const 0x0123_0000)) (i32.const 0)) +(assert_return (invoke "extend16_s" (i32.const 0xfedc_8000)) (i32.const -0x8000)) +(assert_return (invoke "extend16_s" (i32.const -1)) (i32.const -1)) + +(assert_return (invoke "eqz" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "eqz" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eqz" (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "eqz" (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "eqz" (i32.const 0xffffffff)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const -1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0x80000000) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0x80000000) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const -1) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 0)) + +(assert_return (invoke "ne" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "ne" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "ne" (i32.const -1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "ne" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "ne" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "ne" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const 0x80000000) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const 0) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const 0x80000000) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const -1) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 1)) + +(assert_return (invoke "lt_s" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const -1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "lt_s" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const 1) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "lt_s" (i32.const 0x80000000) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "lt_s" (i32.const 0) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const 0x80000000) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "lt_s" (i32.const -1) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "lt_s" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 0)) + +(assert_return (invoke "lt_u" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const -1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 1) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "lt_u" (i32.const 0x80000000) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 0) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "lt_u" (i32.const 0x80000000) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "lt_u" (i32.const -1) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 1)) + +(assert_return (invoke "le_s" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const -1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const 1) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "le_s" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const 0x80000000) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const 0) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "le_s" (i32.const 0x80000000) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const -1) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "le_s" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 0)) + +(assert_return (invoke "le_u" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const -1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "le_u" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const 1) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "le_u" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const 0x80000000) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "le_u" (i32.const 0) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const 0x80000000) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const -1) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "le_u" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "le_u" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 1)) + +(assert_return (invoke "gt_s" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const -1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "gt_s" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const 0x80000000) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const 0) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "gt_s" (i32.const 0x80000000) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const -1) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "gt_s" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 1)) + +(assert_return (invoke "gt_u" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const -1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "gt_u" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "gt_u" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const 0x80000000) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "gt_u" (i32.const 0) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const 0x80000000) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const -1) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "gt_u" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "gt_u" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 0)) + +(assert_return (invoke "ge_s" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const -1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "ge_s" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "ge_s" (i32.const 0x80000000) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "ge_s" (i32.const 0) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const 0x80000000) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "ge_s" (i32.const -1) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "ge_s" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 1)) + +(assert_return (invoke "ge_u" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const -1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "ge_u" (i32.const 0x80000000) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 0) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "ge_u" (i32.const 0x80000000) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "ge_u" (i32.const -1) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 0)) + + +(assert_invalid + (module + (func $type-unary-operand-empty + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-block + (i32.const 0) + (block (i32.eqz) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-loop + (i32.const 0) + (loop (i32.eqz) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-if + (i32.const 0) (i32.const 0) + (if (then (i32.eqz) (drop))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-else + (i32.const 0) (i32.const 0) + (if (result i32) (then (i32.const 0)) (else (i32.eqz))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-br + (i32.const 0) + (block (br 0 (i32.eqz)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-br_if + (i32.const 0) + (block (br_if 0 (i32.eqz) (i32.const 1)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-br_table + (i32.const 0) + (block (br_table 0 (i32.eqz)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-return + (return (i32.eqz)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-select + (select (i32.eqz) (i32.const 1) (i32.const 2)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-call + (call 1 (i32.eqz)) (drop) + ) + (func (param i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-unary-operand-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (i32.eqz) (i32.const 0) + ) + (drop) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-local.set + (local i32) + (local.set 0 (i32.eqz)) (local.get 0) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-local.tee + (local i32) + (local.tee 0 (i32.eqz)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-unary-operand-empty-in-global.set + (global.set $x (i32.eqz)) (global.get $x) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-unary-operand-empty-in-memory.grow + (memory.grow (i32.eqz)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-unary-operand-empty-in-load + (i32.load (i32.eqz)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-unary-operand-empty-in-store + (i32.store (i32.eqz) (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $type-binary-1st-operand-empty + (i32.add) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty + (i32.const 0) (i32.add) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-block + (i32.const 0) (i32.const 0) + (block (i32.add) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-block + (i32.const 0) + (block (i32.const 0) (i32.add) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-loop + (i32.const 0) (i32.const 0) + (loop (i32.add) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-loop + (i32.const 0) + (loop (i32.const 0) (i32.add) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-if + (i32.const 0) (i32.const 0) (i32.const 0) + (if (i32.add) (then (drop))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-if + (i32.const 0) (i32.const 0) + (if (i32.const 0) (then (i32.add)) (else (drop))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-else + (i32.const 0) (i32.const 0) (i32.const 0) + (if (result i32) (then (i32.const 0)) (else (i32.add) (i32.const 0))) + (drop) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-else + (i32.const 0) (i32.const 0) + (if (result i32) (then (i32.const 0)) (else (i32.add))) + (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-br + (i32.const 0) (i32.const 0) + (block (br 0 (i32.add)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-br + (i32.const 0) + (block (br 0 (i32.const 0) (i32.add)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-br_if + (i32.const 0) (i32.const 0) + (block (br_if 0 (i32.add) (i32.const 1)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-br_if + (i32.const 0) + (block (br_if 0 (i32.const 0) (i32.add) (i32.const 1)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-br_table + (i32.const 0) (i32.const 0) + (block (br_table 0 (i32.add)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-br_table + (i32.const 0) + (block (br_table 0 (i32.const 0) (i32.add)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-return + (return (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-return + (return (i32.const 0) (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-select + (select (i32.add) (i32.const 1) (i32.const 2)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-select + (select (i32.const 0) (i32.add) (i32.const 1) (i32.const 2)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-call + (call 1 (i32.add)) (drop) + ) + (func (param i32 i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-call + (call 1 (i32.const 0) (i32.add)) (drop) + ) + (func (param i32 i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-binary-1st-operand-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (i32.add) (i32.const 0) + ) + (drop) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-binary-2nd-operand-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) (i32.add) (i32.const 0) + ) + (drop) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-local.set + (local i32) + (local.set 0 (i32.add)) (local.get 0) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-local.set + (local i32) + (local.set 0 (i32.const 0) (i32.add)) (local.get 0) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-local.tee + (local i32) + (local.tee 0 (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-local.tee + (local i32) + (local.tee 0 (i32.const 0) (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-binary-1st-operand-empty-in-global.set + (global.set $x (i32.add)) (global.get $x) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-binary-2nd-operand-empty-in-global.set + (global.set $x (i32.const 0) (i32.add)) (global.get $x) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-binary-1st-operand-empty-in-memory.grow + (memory.grow (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-binary-2nd-operand-empty-in-memory.grow + (memory.grow (i32.const 0) (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-binary-1st-operand-empty-in-load + (i32.load (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-binary-2nd-operand-empty-in-load + (i32.load (i32.const 0) (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-binary-1st-operand-empty-in-store + (i32.store (i32.add) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-binary-2nd-operand-empty-in-store + (i32.store (i32.const 1) (i32.add) (i32.const 0)) + ) + ) + "type mismatch" +) + + +;; Type check + +(assert_invalid (module (func (result i32) (i32.add (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.and (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.div_s (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.div_u (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.mul (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.or (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.rem_s (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.rem_u (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.rotl (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.rotr (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.shl (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.shr_s (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.shr_u (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.sub (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.xor (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.eqz (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.clz (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.ctz (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.popcnt (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.eq (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.ge_s (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.ge_u (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.gt_s (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.gt_u (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.le_s (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.le_u (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.lt_s (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.lt_u (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.ne (i64.const 0) (f32.const 0)))) "type mismatch") diff --git a/runtime/unc-vm/tests/wast/spec/i64.wast b/runtime/unc-vm/tests/wast/spec/i64.wast new file mode 100644 index 000000000..baeed0ce1 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/i64.wast @@ -0,0 +1,485 @@ +;; i64 operations + +(module + (func (export "add") (param $x i64) (param $y i64) (result i64) (i64.add (local.get $x) (local.get $y))) + (func (export "sub") (param $x i64) (param $y i64) (result i64) (i64.sub (local.get $x) (local.get $y))) + (func (export "mul") (param $x i64) (param $y i64) (result i64) (i64.mul (local.get $x) (local.get $y))) + (func (export "div_s") (param $x i64) (param $y i64) (result i64) (i64.div_s (local.get $x) (local.get $y))) + (func (export "div_u") (param $x i64) (param $y i64) (result i64) (i64.div_u (local.get $x) (local.get $y))) + (func (export "rem_s") (param $x i64) (param $y i64) (result i64) (i64.rem_s (local.get $x) (local.get $y))) + (func (export "rem_u") (param $x i64) (param $y i64) (result i64) (i64.rem_u (local.get $x) (local.get $y))) + (func (export "and") (param $x i64) (param $y i64) (result i64) (i64.and (local.get $x) (local.get $y))) + (func (export "or") (param $x i64) (param $y i64) (result i64) (i64.or (local.get $x) (local.get $y))) + (func (export "xor") (param $x i64) (param $y i64) (result i64) (i64.xor (local.get $x) (local.get $y))) + (func (export "shl") (param $x i64) (param $y i64) (result i64) (i64.shl (local.get $x) (local.get $y))) + (func (export "shr_s") (param $x i64) (param $y i64) (result i64) (i64.shr_s (local.get $x) (local.get $y))) + (func (export "shr_u") (param $x i64) (param $y i64) (result i64) (i64.shr_u (local.get $x) (local.get $y))) + (func (export "rotl") (param $x i64) (param $y i64) (result i64) (i64.rotl (local.get $x) (local.get $y))) + (func (export "rotr") (param $x i64) (param $y i64) (result i64) (i64.rotr (local.get $x) (local.get $y))) + (func (export "clz") (param $x i64) (result i64) (i64.clz (local.get $x))) + (func (export "ctz") (param $x i64) (result i64) (i64.ctz (local.get $x))) + (func (export "popcnt") (param $x i64) (result i64) (i64.popcnt (local.get $x))) + (func (export "extend8_s") (param $x i64) (result i64) (i64.extend8_s (local.get $x))) + (func (export "extend16_s") (param $x i64) (result i64) (i64.extend16_s (local.get $x))) + (func (export "extend32_s") (param $x i64) (result i64) (i64.extend32_s (local.get $x))) + (func (export "eqz") (param $x i64) (result i32) (i64.eqz (local.get $x))) + (func (export "eq") (param $x i64) (param $y i64) (result i32) (i64.eq (local.get $x) (local.get $y))) + (func (export "ne") (param $x i64) (param $y i64) (result i32) (i64.ne (local.get $x) (local.get $y))) + (func (export "lt_s") (param $x i64) (param $y i64) (result i32) (i64.lt_s (local.get $x) (local.get $y))) + (func (export "lt_u") (param $x i64) (param $y i64) (result i32) (i64.lt_u (local.get $x) (local.get $y))) + (func (export "le_s") (param $x i64) (param $y i64) (result i32) (i64.le_s (local.get $x) (local.get $y))) + (func (export "le_u") (param $x i64) (param $y i64) (result i32) (i64.le_u (local.get $x) (local.get $y))) + (func (export "gt_s") (param $x i64) (param $y i64) (result i32) (i64.gt_s (local.get $x) (local.get $y))) + (func (export "gt_u") (param $x i64) (param $y i64) (result i32) (i64.gt_u (local.get $x) (local.get $y))) + (func (export "ge_s") (param $x i64) (param $y i64) (result i32) (i64.ge_s (local.get $x) (local.get $y))) + (func (export "ge_u") (param $x i64) (param $y i64) (result i32) (i64.ge_u (local.get $x) (local.get $y))) +) + +(assert_return (invoke "add" (i64.const 1) (i64.const 1)) (i64.const 2)) +(assert_return (invoke "add" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "add" (i64.const -1) (i64.const -1)) (i64.const -2)) +(assert_return (invoke "add" (i64.const -1) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "add" (i64.const 0x7fffffffffffffff) (i64.const 1)) (i64.const 0x8000000000000000)) +(assert_return (invoke "add" (i64.const 0x8000000000000000) (i64.const -1)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "add" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i64.const 0)) +(assert_return (invoke "add" (i64.const 0x3fffffff) (i64.const 1)) (i64.const 0x40000000)) + +(assert_return (invoke "sub" (i64.const 1) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "sub" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "sub" (i64.const -1) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "sub" (i64.const 0x7fffffffffffffff) (i64.const -1)) (i64.const 0x8000000000000000)) +(assert_return (invoke "sub" (i64.const 0x8000000000000000) (i64.const 1)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "sub" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i64.const 0)) +(assert_return (invoke "sub" (i64.const 0x3fffffff) (i64.const -1)) (i64.const 0x40000000)) + +(assert_return (invoke "mul" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "mul" (i64.const 1) (i64.const 0)) (i64.const 0)) +(assert_return (invoke "mul" (i64.const -1) (i64.const -1)) (i64.const 1)) +(assert_return (invoke "mul" (i64.const 0x1000000000000000) (i64.const 4096)) (i64.const 0)) +(assert_return (invoke "mul" (i64.const 0x8000000000000000) (i64.const 0)) (i64.const 0)) +(assert_return (invoke "mul" (i64.const 0x8000000000000000) (i64.const -1)) (i64.const 0x8000000000000000)) +(assert_return (invoke "mul" (i64.const 0x7fffffffffffffff) (i64.const -1)) (i64.const 0x8000000000000001)) +(assert_return (invoke "mul" (i64.const 0x0123456789abcdef) (i64.const 0xfedcba9876543210)) (i64.const 0x2236d88fe5618cf0)) +(assert_return (invoke "mul" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i64.const 1)) + +(assert_trap (invoke "div_s" (i64.const 1) (i64.const 0)) "integer divide by zero") +(assert_trap (invoke "div_s" (i64.const 0) (i64.const 0)) "integer divide by zero") +(assert_trap (invoke "div_s" (i64.const 0x8000000000000000) (i64.const -1)) "integer overflow") +(assert_trap (invoke "div_s" (i64.const 0x8000000000000000) (i64.const 0)) "integer divide by zero") +(assert_return (invoke "div_s" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "div_s" (i64.const 0) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "div_s" (i64.const 0) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "div_s" (i64.const -1) (i64.const -1)) (i64.const 1)) +(assert_return (invoke "div_s" (i64.const 0x8000000000000000) (i64.const 2)) (i64.const 0xc000000000000000)) +(assert_return (invoke "div_s" (i64.const 0x8000000000000001) (i64.const 1000)) (i64.const 0xffdf3b645a1cac09)) +(assert_return (invoke "div_s" (i64.const 5) (i64.const 2)) (i64.const 2)) +(assert_return (invoke "div_s" (i64.const -5) (i64.const 2)) (i64.const -2)) +(assert_return (invoke "div_s" (i64.const 5) (i64.const -2)) (i64.const -2)) +(assert_return (invoke "div_s" (i64.const -5) (i64.const -2)) (i64.const 2)) +(assert_return (invoke "div_s" (i64.const 7) (i64.const 3)) (i64.const 2)) +(assert_return (invoke "div_s" (i64.const -7) (i64.const 3)) (i64.const -2)) +(assert_return (invoke "div_s" (i64.const 7) (i64.const -3)) (i64.const -2)) +(assert_return (invoke "div_s" (i64.const -7) (i64.const -3)) (i64.const 2)) +(assert_return (invoke "div_s" (i64.const 11) (i64.const 5)) (i64.const 2)) +(assert_return (invoke "div_s" (i64.const 17) (i64.const 7)) (i64.const 2)) + +(assert_trap (invoke "div_u" (i64.const 1) (i64.const 0)) "integer divide by zero") +(assert_trap (invoke "div_u" (i64.const 0) (i64.const 0)) "integer divide by zero") +(assert_return (invoke "div_u" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "div_u" (i64.const 0) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "div_u" (i64.const -1) (i64.const -1)) (i64.const 1)) +(assert_return (invoke "div_u" (i64.const 0x8000000000000000) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "div_u" (i64.const 0x8000000000000000) (i64.const 2)) (i64.const 0x4000000000000000)) +(assert_return (invoke "div_u" (i64.const 0x8ff00ff00ff00ff0) (i64.const 0x100000001)) (i64.const 0x8ff00fef)) +(assert_return (invoke "div_u" (i64.const 0x8000000000000001) (i64.const 1000)) (i64.const 0x20c49ba5e353f7)) +(assert_return (invoke "div_u" (i64.const 5) (i64.const 2)) (i64.const 2)) +(assert_return (invoke "div_u" (i64.const -5) (i64.const 2)) (i64.const 0x7ffffffffffffffd)) +(assert_return (invoke "div_u" (i64.const 5) (i64.const -2)) (i64.const 0)) +(assert_return (invoke "div_u" (i64.const -5) (i64.const -2)) (i64.const 0)) +(assert_return (invoke "div_u" (i64.const 7) (i64.const 3)) (i64.const 2)) +(assert_return (invoke "div_u" (i64.const 11) (i64.const 5)) (i64.const 2)) +(assert_return (invoke "div_u" (i64.const 17) (i64.const 7)) (i64.const 2)) + +(assert_trap (invoke "rem_s" (i64.const 1) (i64.const 0)) "integer divide by zero") +(assert_trap (invoke "rem_s" (i64.const 0) (i64.const 0)) "integer divide by zero") +(assert_return (invoke "rem_s" (i64.const 0x7fffffffffffffff) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "rem_s" (i64.const 1) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "rem_s" (i64.const 0) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "rem_s" (i64.const 0) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "rem_s" (i64.const -1) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "rem_s" (i64.const 0x8000000000000000) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "rem_s" (i64.const 0x8000000000000000) (i64.const 2)) (i64.const 0)) +(assert_return (invoke "rem_s" (i64.const 0x8000000000000001) (i64.const 1000)) (i64.const -807)) +(assert_return (invoke "rem_s" (i64.const 5) (i64.const 2)) (i64.const 1)) +(assert_return (invoke "rem_s" (i64.const -5) (i64.const 2)) (i64.const -1)) +(assert_return (invoke "rem_s" (i64.const 5) (i64.const -2)) (i64.const 1)) +(assert_return (invoke "rem_s" (i64.const -5) (i64.const -2)) (i64.const -1)) +(assert_return (invoke "rem_s" (i64.const 7) (i64.const 3)) (i64.const 1)) +(assert_return (invoke "rem_s" (i64.const -7) (i64.const 3)) (i64.const -1)) +(assert_return (invoke "rem_s" (i64.const 7) (i64.const -3)) (i64.const 1)) +(assert_return (invoke "rem_s" (i64.const -7) (i64.const -3)) (i64.const -1)) +(assert_return (invoke "rem_s" (i64.const 11) (i64.const 5)) (i64.const 1)) +(assert_return (invoke "rem_s" (i64.const 17) (i64.const 7)) (i64.const 3)) + +(assert_trap (invoke "rem_u" (i64.const 1) (i64.const 0)) "integer divide by zero") +(assert_trap (invoke "rem_u" (i64.const 0) (i64.const 0)) "integer divide by zero") +(assert_return (invoke "rem_u" (i64.const 1) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "rem_u" (i64.const 0) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "rem_u" (i64.const -1) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "rem_u" (i64.const 0x8000000000000000) (i64.const -1)) (i64.const 0x8000000000000000)) +(assert_return (invoke "rem_u" (i64.const 0x8000000000000000) (i64.const 2)) (i64.const 0)) +(assert_return (invoke "rem_u" (i64.const 0x8ff00ff00ff00ff0) (i64.const 0x100000001)) (i64.const 0x80000001)) +(assert_return (invoke "rem_u" (i64.const 0x8000000000000001) (i64.const 1000)) (i64.const 809)) +(assert_return (invoke "rem_u" (i64.const 5) (i64.const 2)) (i64.const 1)) +(assert_return (invoke "rem_u" (i64.const -5) (i64.const 2)) (i64.const 1)) +(assert_return (invoke "rem_u" (i64.const 5) (i64.const -2)) (i64.const 5)) +(assert_return (invoke "rem_u" (i64.const -5) (i64.const -2)) (i64.const -5)) +(assert_return (invoke "rem_u" (i64.const 7) (i64.const 3)) (i64.const 1)) +(assert_return (invoke "rem_u" (i64.const 11) (i64.const 5)) (i64.const 1)) +(assert_return (invoke "rem_u" (i64.const 17) (i64.const 7)) (i64.const 3)) + +(assert_return (invoke "and" (i64.const 1) (i64.const 0)) (i64.const 0)) +(assert_return (invoke "and" (i64.const 0) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "and" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "and" (i64.const 0) (i64.const 0)) (i64.const 0)) +(assert_return (invoke "and" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i64.const 0)) +(assert_return (invoke "and" (i64.const 0x7fffffffffffffff) (i64.const -1)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "and" (i64.const 0xf0f0ffff) (i64.const 0xfffff0f0)) (i64.const 0xf0f0f0f0)) +(assert_return (invoke "and" (i64.const 0xffffffffffffffff) (i64.const 0xffffffffffffffff)) (i64.const 0xffffffffffffffff)) + +(assert_return (invoke "or" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "or" (i64.const 0) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "or" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "or" (i64.const 0) (i64.const 0)) (i64.const 0)) +(assert_return (invoke "or" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i64.const -1)) +(assert_return (invoke "or" (i64.const 0x8000000000000000) (i64.const 0)) (i64.const 0x8000000000000000)) +(assert_return (invoke "or" (i64.const 0xf0f0ffff) (i64.const 0xfffff0f0)) (i64.const 0xffffffff)) +(assert_return (invoke "or" (i64.const 0xffffffffffffffff) (i64.const 0xffffffffffffffff)) (i64.const 0xffffffffffffffff)) + +(assert_return (invoke "xor" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "xor" (i64.const 0) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "xor" (i64.const 1) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "xor" (i64.const 0) (i64.const 0)) (i64.const 0)) +(assert_return (invoke "xor" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i64.const -1)) +(assert_return (invoke "xor" (i64.const 0x8000000000000000) (i64.const 0)) (i64.const 0x8000000000000000)) +(assert_return (invoke "xor" (i64.const -1) (i64.const 0x8000000000000000)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "xor" (i64.const -1) (i64.const 0x7fffffffffffffff)) (i64.const 0x8000000000000000)) +(assert_return (invoke "xor" (i64.const 0xf0f0ffff) (i64.const 0xfffff0f0)) (i64.const 0x0f0f0f0f)) +(assert_return (invoke "xor" (i64.const 0xffffffffffffffff) (i64.const 0xffffffffffffffff)) (i64.const 0)) + +(assert_return (invoke "shl" (i64.const 1) (i64.const 1)) (i64.const 2)) +(assert_return (invoke "shl" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "shl" (i64.const 0x7fffffffffffffff) (i64.const 1)) (i64.const 0xfffffffffffffffe)) +(assert_return (invoke "shl" (i64.const 0xffffffffffffffff) (i64.const 1)) (i64.const 0xfffffffffffffffe)) +(assert_return (invoke "shl" (i64.const 0x8000000000000000) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "shl" (i64.const 0x4000000000000000) (i64.const 1)) (i64.const 0x8000000000000000)) +(assert_return (invoke "shl" (i64.const 1) (i64.const 63)) (i64.const 0x8000000000000000)) +(assert_return (invoke "shl" (i64.const 1) (i64.const 64)) (i64.const 1)) +(assert_return (invoke "shl" (i64.const 1) (i64.const 65)) (i64.const 2)) +(assert_return (invoke "shl" (i64.const 1) (i64.const -1)) (i64.const 0x8000000000000000)) +(assert_return (invoke "shl" (i64.const 1) (i64.const 0x7fffffffffffffff)) (i64.const 0x8000000000000000)) + +(assert_return (invoke "shr_s" (i64.const 1) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "shr_s" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "shr_s" (i64.const -1) (i64.const 1)) (i64.const -1)) +(assert_return (invoke "shr_s" (i64.const 0x7fffffffffffffff) (i64.const 1)) (i64.const 0x3fffffffffffffff)) +(assert_return (invoke "shr_s" (i64.const 0x8000000000000000) (i64.const 1)) (i64.const 0xc000000000000000)) +(assert_return (invoke "shr_s" (i64.const 0x4000000000000000) (i64.const 1)) (i64.const 0x2000000000000000)) +(assert_return (invoke "shr_s" (i64.const 1) (i64.const 64)) (i64.const 1)) +(assert_return (invoke "shr_s" (i64.const 1) (i64.const 65)) (i64.const 0)) +(assert_return (invoke "shr_s" (i64.const 1) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "shr_s" (i64.const 1) (i64.const 0x7fffffffffffffff)) (i64.const 0)) +(assert_return (invoke "shr_s" (i64.const 1) (i64.const 0x8000000000000000)) (i64.const 1)) +(assert_return (invoke "shr_s" (i64.const 0x8000000000000000) (i64.const 63)) (i64.const -1)) +(assert_return (invoke "shr_s" (i64.const -1) (i64.const 64)) (i64.const -1)) +(assert_return (invoke "shr_s" (i64.const -1) (i64.const 65)) (i64.const -1)) +(assert_return (invoke "shr_s" (i64.const -1) (i64.const -1)) (i64.const -1)) +(assert_return (invoke "shr_s" (i64.const -1) (i64.const 0x7fffffffffffffff)) (i64.const -1)) +(assert_return (invoke "shr_s" (i64.const -1) (i64.const 0x8000000000000000)) (i64.const -1)) + +(assert_return (invoke "shr_u" (i64.const 1) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "shr_u" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "shr_u" (i64.const -1) (i64.const 1)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "shr_u" (i64.const 0x7fffffffffffffff) (i64.const 1)) (i64.const 0x3fffffffffffffff)) +(assert_return (invoke "shr_u" (i64.const 0x8000000000000000) (i64.const 1)) (i64.const 0x4000000000000000)) +(assert_return (invoke "shr_u" (i64.const 0x4000000000000000) (i64.const 1)) (i64.const 0x2000000000000000)) +(assert_return (invoke "shr_u" (i64.const 1) (i64.const 64)) (i64.const 1)) +(assert_return (invoke "shr_u" (i64.const 1) (i64.const 65)) (i64.const 0)) +(assert_return (invoke "shr_u" (i64.const 1) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "shr_u" (i64.const 1) (i64.const 0x7fffffffffffffff)) (i64.const 0)) +(assert_return (invoke "shr_u" (i64.const 1) (i64.const 0x8000000000000000)) (i64.const 1)) +(assert_return (invoke "shr_u" (i64.const 0x8000000000000000) (i64.const 63)) (i64.const 1)) +(assert_return (invoke "shr_u" (i64.const -1) (i64.const 64)) (i64.const -1)) +(assert_return (invoke "shr_u" (i64.const -1) (i64.const 65)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "shr_u" (i64.const -1) (i64.const -1)) (i64.const 1)) +(assert_return (invoke "shr_u" (i64.const -1) (i64.const 0x7fffffffffffffff)) (i64.const 1)) +(assert_return (invoke "shr_u" (i64.const -1) (i64.const 0x8000000000000000)) (i64.const -1)) + +(assert_return (invoke "rotl" (i64.const 1) (i64.const 1)) (i64.const 2)) +(assert_return (invoke "rotl" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "rotl" (i64.const -1) (i64.const 1)) (i64.const -1)) +(assert_return (invoke "rotl" (i64.const 1) (i64.const 64)) (i64.const 1)) +(assert_return (invoke "rotl" (i64.const 0xabcd987602468ace) (i64.const 1)) (i64.const 0x579b30ec048d159d)) +(assert_return (invoke "rotl" (i64.const 0xfe000000dc000000) (i64.const 4)) (i64.const 0xe000000dc000000f)) +(assert_return (invoke "rotl" (i64.const 0xabcd1234ef567809) (i64.const 53)) (i64.const 0x013579a2469deacf)) +(assert_return (invoke "rotl" (i64.const 0xabd1234ef567809c) (i64.const 63)) (i64.const 0x55e891a77ab3c04e)) +(assert_return (invoke "rotl" (i64.const 0xabcd1234ef567809) (i64.const 0xf5)) (i64.const 0x013579a2469deacf)) +(assert_return (invoke "rotl" (i64.const 0xabcd7294ef567809) (i64.const 0xffffffffffffffed)) (i64.const 0xcf013579ae529dea)) +(assert_return (invoke "rotl" (i64.const 0xabd1234ef567809c) (i64.const 0x800000000000003f)) (i64.const 0x55e891a77ab3c04e)) +(assert_return (invoke "rotl" (i64.const 1) (i64.const 63)) (i64.const 0x8000000000000000)) +(assert_return (invoke "rotl" (i64.const 0x8000000000000000) (i64.const 1)) (i64.const 1)) + +(assert_return (invoke "rotr" (i64.const 1) (i64.const 1)) (i64.const 0x8000000000000000)) +(assert_return (invoke "rotr" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "rotr" (i64.const -1) (i64.const 1)) (i64.const -1)) +(assert_return (invoke "rotr" (i64.const 1) (i64.const 64)) (i64.const 1)) +(assert_return (invoke "rotr" (i64.const 0xabcd987602468ace) (i64.const 1)) (i64.const 0x55e6cc3b01234567)) +(assert_return (invoke "rotr" (i64.const 0xfe000000dc000000) (i64.const 4)) (i64.const 0x0fe000000dc00000)) +(assert_return (invoke "rotr" (i64.const 0xabcd1234ef567809) (i64.const 53)) (i64.const 0x6891a77ab3c04d5e)) +(assert_return (invoke "rotr" (i64.const 0xabd1234ef567809c) (i64.const 63)) (i64.const 0x57a2469deacf0139)) +(assert_return (invoke "rotr" (i64.const 0xabcd1234ef567809) (i64.const 0xf5)) (i64.const 0x6891a77ab3c04d5e)) +(assert_return (invoke "rotr" (i64.const 0xabcd7294ef567809) (i64.const 0xffffffffffffffed)) (i64.const 0x94a77ab3c04d5e6b)) +(assert_return (invoke "rotr" (i64.const 0xabd1234ef567809c) (i64.const 0x800000000000003f)) (i64.const 0x57a2469deacf0139)) +(assert_return (invoke "rotr" (i64.const 1) (i64.const 63)) (i64.const 2)) +(assert_return (invoke "rotr" (i64.const 0x8000000000000000) (i64.const 63)) (i64.const 1)) + +(assert_return (invoke "clz" (i64.const 0xffffffffffffffff)) (i64.const 0)) +(assert_return (invoke "clz" (i64.const 0)) (i64.const 64)) +(assert_return (invoke "clz" (i64.const 0x00008000)) (i64.const 48)) +(assert_return (invoke "clz" (i64.const 0xff)) (i64.const 56)) +(assert_return (invoke "clz" (i64.const 0x8000000000000000)) (i64.const 0)) +(assert_return (invoke "clz" (i64.const 1)) (i64.const 63)) +(assert_return (invoke "clz" (i64.const 2)) (i64.const 62)) +(assert_return (invoke "clz" (i64.const 0x7fffffffffffffff)) (i64.const 1)) + +(assert_return (invoke "ctz" (i64.const -1)) (i64.const 0)) +(assert_return (invoke "ctz" (i64.const 0)) (i64.const 64)) +(assert_return (invoke "ctz" (i64.const 0x00008000)) (i64.const 15)) +(assert_return (invoke "ctz" (i64.const 0x00010000)) (i64.const 16)) +(assert_return (invoke "ctz" (i64.const 0x8000000000000000)) (i64.const 63)) +(assert_return (invoke "ctz" (i64.const 0x7fffffffffffffff)) (i64.const 0)) + +(assert_return (invoke "popcnt" (i64.const -1)) (i64.const 64)) +(assert_return (invoke "popcnt" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "popcnt" (i64.const 0x00008000)) (i64.const 1)) +(assert_return (invoke "popcnt" (i64.const 0x8000800080008000)) (i64.const 4)) +(assert_return (invoke "popcnt" (i64.const 0x7fffffffffffffff)) (i64.const 63)) +(assert_return (invoke "popcnt" (i64.const 0xAAAAAAAA55555555)) (i64.const 32)) +(assert_return (invoke "popcnt" (i64.const 0x99999999AAAAAAAA)) (i64.const 32)) +(assert_return (invoke "popcnt" (i64.const 0xDEADBEEFDEADBEEF)) (i64.const 48)) + +(assert_return (invoke "extend8_s" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "extend8_s" (i64.const 0x7f)) (i64.const 127)) +(assert_return (invoke "extend8_s" (i64.const 0x80)) (i64.const -128)) +(assert_return (invoke "extend8_s" (i64.const 0xff)) (i64.const -1)) +(assert_return (invoke "extend8_s" (i64.const 0x01234567_89abcd_00)) (i64.const 0)) +(assert_return (invoke "extend8_s" (i64.const 0xfedcba98_765432_80)) (i64.const -0x80)) +(assert_return (invoke "extend8_s" (i64.const -1)) (i64.const -1)) + +(assert_return (invoke "extend16_s" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "extend16_s" (i64.const 0x7fff)) (i64.const 32767)) +(assert_return (invoke "extend16_s" (i64.const 0x8000)) (i64.const -32768)) +(assert_return (invoke "extend16_s" (i64.const 0xffff)) (i64.const -1)) +(assert_return (invoke "extend16_s" (i64.const 0x12345678_9abc_0000)) (i64.const 0)) +(assert_return (invoke "extend16_s" (i64.const 0xfedcba98_7654_8000)) (i64.const -0x8000)) +(assert_return (invoke "extend16_s" (i64.const -1)) (i64.const -1)) + +(assert_return (invoke "extend32_s" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "extend32_s" (i64.const 0x7fff)) (i64.const 32767)) +(assert_return (invoke "extend32_s" (i64.const 0x8000)) (i64.const 32768)) +(assert_return (invoke "extend32_s" (i64.const 0xffff)) (i64.const 65535)) +(assert_return (invoke "extend32_s" (i64.const 0x7fffffff)) (i64.const 0x7fffffff)) +(assert_return (invoke "extend32_s" (i64.const 0x80000000)) (i64.const -0x80000000)) +(assert_return (invoke "extend32_s" (i64.const 0xffffffff)) (i64.const -1)) +(assert_return (invoke "extend32_s" (i64.const 0x01234567_00000000)) (i64.const 0)) +(assert_return (invoke "extend32_s" (i64.const 0xfedcba98_80000000)) (i64.const -0x80000000)) +(assert_return (invoke "extend32_s" (i64.const -1)) (i64.const -1)) + +(assert_return (invoke "eqz" (i64.const 0)) (i32.const 1)) +(assert_return (invoke "eqz" (i64.const 1)) (i32.const 0)) +(assert_return (invoke "eqz" (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "eqz" (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "eqz" (i64.const 0xffffffffffffffff)) (i32.const 0)) + +(assert_return (invoke "eq" (i64.const 0) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "eq" (i64.const 1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "eq" (i64.const -1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "eq" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "eq" (i64.const -1) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "eq" (i64.const 1) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const 0) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 0)) + +(assert_return (invoke "ne" (i64.const 0) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "ne" (i64.const 1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "ne" (i64.const -1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "ne" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "ne" (i64.const -1) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "ne" (i64.const 1) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const 0) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 1)) + +(assert_return (invoke "lt_s" (i64.const 0) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const 1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const -1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "lt_s" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const -1) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const 1) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const 0) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "lt_s" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "lt_s" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "lt_s" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "lt_s" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 0)) + +(assert_return (invoke "lt_u" (i64.const 0) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const -1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const -1) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 1) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 0) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "lt_u" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "lt_u" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "lt_u" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 1)) + +(assert_return (invoke "le_s" (i64.const 0) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const 1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const -1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const -1) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const 1) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "le_s" (i64.const 0) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "le_s" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "le_s" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 0)) + +(assert_return (invoke "le_u" (i64.const 0) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const 1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const -1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "le_u" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const -1) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const 1) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "le_u" (i64.const 0) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "le_u" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "le_u" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "le_u" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 1)) + +(assert_return (invoke "gt_s" (i64.const 0) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const 1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const -1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const -1) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const 1) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "gt_s" (i64.const 0) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "gt_s" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "gt_s" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 1)) + +(assert_return (invoke "gt_u" (i64.const 0) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const 1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const -1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "gt_u" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const -1) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const 1) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "gt_u" (i64.const 0) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "gt_u" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "gt_u" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "gt_u" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 0)) + +(assert_return (invoke "ge_s" (i64.const 0) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const 1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const -1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "ge_s" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const -1) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const 1) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const 0) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "ge_s" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "ge_s" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "ge_s" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "ge_s" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 1)) + +(assert_return (invoke "ge_u" (i64.const 0) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const -1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const -1) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 1) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 0) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "ge_u" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "ge_u" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "ge_u" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 0)) + + +;; Type check + +(assert_invalid (module (func (result i64) (i64.add (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.and (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.div_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.div_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.mul (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.or (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.rem_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.rem_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.rotl (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.rotr (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.shl (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.shr_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.shr_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.sub (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.xor (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.eqz (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.clz (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.ctz (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.popcnt (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.eq (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.ge_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.ge_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.gt_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.gt_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.le_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.le_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.lt_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.lt_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.ne (i32.const 0) (f32.const 0)))) "type mismatch") diff --git a/runtime/unc-vm/tests/wast/spec/if.wast b/runtime/unc-vm/tests/wast/spec/if.wast new file mode 100644 index 000000000..1cbb617a7 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/if.wast @@ -0,0 +1,1550 @@ +;; Test `if` operator + +(module + ;; Auxiliary definition + (memory 1) + + (func $dummy) + + (func (export "empty") (param i32) + (if (local.get 0) (then)) + (if (local.get 0) (then) (else)) + (if $l (local.get 0) (then)) + (if $l (local.get 0) (then) (else)) + ) + + (func (export "singular") (param i32) (result i32) + (if (local.get 0) (then (nop))) + (if (local.get 0) (then (nop)) (else (nop))) + (if (result i32) (local.get 0) (then (i32.const 7)) (else (i32.const 8))) + ) + + (func (export "multi") (param i32) (result i32 i32) + (if (local.get 0) (then (call $dummy) (call $dummy) (call $dummy))) + (if (local.get 0) (then) (else (call $dummy) (call $dummy) (call $dummy))) + (if (result i32) (local.get 0) + (then (call $dummy) (call $dummy) (i32.const 8) (call $dummy)) + (else (call $dummy) (call $dummy) (i32.const 9) (call $dummy)) + ) + (if (result i32 i64 i32) (local.get 0) + (then + (call $dummy) (call $dummy) (i32.const 1) (call $dummy) + (call $dummy) (call $dummy) (i64.const 2) (call $dummy) + (call $dummy) (call $dummy) (i32.const 3) (call $dummy) + ) + (else + (call $dummy) (call $dummy) (i32.const -1) (call $dummy) + (call $dummy) (call $dummy) (i64.const -2) (call $dummy) + (call $dummy) (call $dummy) (i32.const -3) (call $dummy) + ) + ) + (drop) (drop) + ) + + (func (export "nested") (param i32 i32) (result i32) + (if (result i32) (local.get 0) + (then + (if (local.get 1) (then (call $dummy) (block) (nop))) + (if (local.get 1) (then) (else (call $dummy) (block) (nop))) + (if (result i32) (local.get 1) + (then (call $dummy) (i32.const 9)) + (else (call $dummy) (i32.const 10)) + ) + ) + (else + (if (local.get 1) (then (call $dummy) (block) (nop))) + (if (local.get 1) (then) (else (call $dummy) (block) (nop))) + (if (result i32) (local.get 1) + (then (call $dummy) (i32.const 10)) + (else (call $dummy) (i32.const 11)) + ) + ) + ) + ) + + (func (export "as-select-first") (param i32) (result i32) + (select + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 2) (i32.const 3) + ) + ) + (func (export "as-select-mid") (param i32) (result i32) + (select + (i32.const 2) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 3) + ) + ) + (func (export "as-select-last") (param i32) (result i32) + (select + (i32.const 2) (i32.const 3) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + ) + ) + + (func (export "as-loop-first") (param i32) (result i32) + (loop (result i32) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (call $dummy) (call $dummy) + ) + ) + (func (export "as-loop-mid") (param i32) (result i32) + (loop (result i32) + (call $dummy) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (call $dummy) + ) + ) + (func (export "as-loop-last") (param i32) (result i32) + (loop (result i32) + (call $dummy) (call $dummy) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + ) + ) + + (func (export "as-if-condition") (param i32) (result i32) + (if (result i32) + (if (result i32) (local.get 0) + (then (i32.const 1)) (else (i32.const 0)) + ) + (then (call $dummy) (i32.const 2)) + (else (call $dummy) (i32.const 3)) + ) + ) + + (func (export "as-br_if-first") (param i32) (result i32) + (block (result i32) + (br_if 0 + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 2) + ) + (return (i32.const 3)) + ) + ) + (func (export "as-br_if-last") (param i32) (result i32) + (block (result i32) + (br_if 0 + (i32.const 2) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + ) + (return (i32.const 3)) + ) + ) + + (func (export "as-br_table-first") (param i32) (result i32) + (block (result i32) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 2) + (br_table 0 0) + ) + ) + (func (export "as-br_table-last") (param i32) (result i32) + (block (result i32) + (i32.const 2) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (br_table 0 0) + ) + ) + + (func $func (param i32 i32) (result i32) (local.get 0)) + (type $check (func (param i32 i32) (result i32))) + (table funcref (elem $func)) + (func (export "as-call_indirect-first") (param i32) (result i32) + (block (result i32) + (call_indirect (type $check) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 2) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-mid") (param i32) (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 2) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-last") (param i32) (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 2) (i32.const 0) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + ) + ) + ) + + (func (export "as-store-first") (param i32) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 2) + (i32.store) + ) + (func (export "as-store-last") (param i32) + (i32.const 2) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.store) + ) + + (func (export "as-memory.grow-value") (param i32) (result i32) + (memory.grow + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ) + + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "as-call-value") (param i32) (result i32) + (call $f + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ) + (func (export "as-return-value") (param i32) (result i32) + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0))) + (return) + ) + (func (export "as-drop-operand") (param i32) + (drop + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ) + (func (export "as-br-value") (param i32) (result i32) + (block (result i32) + (br 0 + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ) + ) + (func (export "as-local.set-value") (param i32) (result i32) + (local i32) + (local.set 0 + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + (local.get 0) + ) + (func (export "as-local.tee-value") (param i32) (result i32) + (local.tee 0 + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (param i32) (result i32) + (global.set $a + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) (global.get $a) + ) + (func (export "as-load-operand") (param i32) (result i32) + (i32.load + (if (result i32) (local.get 0) + (then (i32.const 11)) + (else (i32.const 10)) + ) + ) + ) + + (func (export "as-unary-operand") (param i32) (result i32) + (i32.ctz + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 13)) + (else (call $dummy) (i32.const -13)) + ) + ) + ) + (func (export "as-binary-operand") (param i32 i32) (result i32) + (i32.mul + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 3)) + (else (call $dummy) (i32.const -3)) + ) + (if (result i32) (local.get 1) + (then (call $dummy) (i32.const 4)) + (else (call $dummy) (i32.const -5)) + ) + ) + ) + (func (export "as-test-operand") (param i32) (result i32) + (i32.eqz + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 13)) + (else (call $dummy) (i32.const 0)) + ) + ) + ) + (func (export "as-compare-operand") (param i32 i32) (result i32) + (f32.gt + (if (result f32) (local.get 0) + (then (call $dummy) (f32.const 3)) + (else (call $dummy) (f32.const -3)) + ) + (if (result f32) (local.get 1) + (then (call $dummy) (f32.const 4)) + (else (call $dummy) (f32.const -4)) + ) + ) + ) + (func (export "as-binary-operands") (param i32) (result i32) + (i32.mul + (if (result i32 i32) (local.get 0) + (then (call $dummy) (i32.const 3) (call $dummy) (i32.const 4)) + (else (call $dummy) (i32.const 3) (call $dummy) (i32.const -4)) + ) + ) + ) + (func (export "as-compare-operands") (param i32) (result i32) + (f32.gt + (if (result f32 f32) (local.get 0) + (then (call $dummy) (f32.const 3) (call $dummy) (f32.const 3)) + (else (call $dummy) (f32.const -2) (call $dummy) (f32.const -3)) + ) + ) + ) + (func (export "as-mixed-operands") (param i32) (result i32) + (if (result i32 i32) (local.get 0) + (then (call $dummy) (i32.const 3) (call $dummy) (i32.const 4)) + (else (call $dummy) (i32.const -3) (call $dummy) (i32.const -4)) + ) + (i32.const 5) + (i32.add) + (i32.mul) + ) + + (func (export "break-bare") (result i32) + (if (i32.const 1) (then (br 0) (unreachable))) + (if (i32.const 1) (then (br 0) (unreachable)) (else (unreachable))) + (if (i32.const 0) (then (unreachable)) (else (br 0) (unreachable))) + (if (i32.const 1) (then (br_if 0 (i32.const 1)) (unreachable))) + (if (i32.const 1) (then (br_if 0 (i32.const 1)) (unreachable)) (else (unreachable))) + (if (i32.const 0) (then (unreachable)) (else (br_if 0 (i32.const 1)) (unreachable))) + (if (i32.const 1) (then (br_table 0 (i32.const 0)) (unreachable))) + (if (i32.const 1) (then (br_table 0 (i32.const 0)) (unreachable)) (else (unreachable))) + (if (i32.const 0) (then (unreachable)) (else (br_table 0 (i32.const 0)) (unreachable))) + (i32.const 19) + ) + + (func (export "break-value") (param i32) (result i32) + (if (result i32) (local.get 0) + (then (br 0 (i32.const 18)) (i32.const 19)) + (else (br 0 (i32.const 21)) (i32.const 20)) + ) + ) + (func (export "break-multi-value") (param i32) (result i32 i32 i64) + (if (result i32 i32 i64) (local.get 0) + (then + (br 0 (i32.const 18) (i32.const -18) (i64.const 18)) + (i32.const 19) (i32.const -19) (i64.const 19) + ) + (else + (br 0 (i32.const -18) (i32.const 18) (i64.const -18)) + (i32.const -19) (i32.const 19) (i64.const -19) + ) + ) + ) + + (func (export "param") (param i32) (result i32) + (i32.const 1) + (if (param i32) (result i32) (local.get 0) + (then (i32.const 2) (i32.add)) + (else (i32.const -2) (i32.add)) + ) + ) + (func (export "params") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32) (local.get 0) + (then (i32.add)) + (else (i32.sub)) + ) + ) + (func (export "params-id") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32 i32) (local.get 0) (then)) + (i32.add) + ) + (func (export "param-break") (param i32) (result i32) + (i32.const 1) + (if (param i32) (result i32) (local.get 0) + (then (i32.const 2) (i32.add) (br 0)) + (else (i32.const -2) (i32.add) (br 0)) + ) + ) + (func (export "params-break") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32) (local.get 0) + (then (i32.add) (br 0)) + (else (i32.sub) (br 0)) + ) + ) + (func (export "params-id-break") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32 i32) (local.get 0) (then (br 0))) + (i32.add) + ) + + (func (export "effects") (param i32) (result i32) + (local i32) + (if + (block (result i32) (local.set 1 (i32.const 1)) (local.get 0)) + (then + (local.set 1 (i32.mul (local.get 1) (i32.const 3))) + (local.set 1 (i32.sub (local.get 1) (i32.const 5))) + (local.set 1 (i32.mul (local.get 1) (i32.const 7))) + (br 0) + (local.set 1 (i32.mul (local.get 1) (i32.const 100))) + ) + (else + (local.set 1 (i32.mul (local.get 1) (i32.const 5))) + (local.set 1 (i32.sub (local.get 1) (i32.const 7))) + (local.set 1 (i32.mul (local.get 1) (i32.const 3))) + (br 0) + (local.set 1 (i32.mul (local.get 1) (i32.const 1000))) + ) + ) + (local.get 1) + ) + + ;; Examples + + (func $add64_u_with_carry (export "add64_u_with_carry") + (param $i i64) (param $j i64) (param $c i32) (result i64 i32) + (local $k i64) + (local.set $k + (i64.add + (i64.add (local.get $i) (local.get $j)) + (i64.extend_i32_u (local.get $c)) + ) + ) + (return (local.get $k) (i64.lt_u (local.get $k) (local.get $i))) + ) + + (func $add64_u_saturated (export "add64_u_saturated") + (param i64 i64) (result i64) + (call $add64_u_with_carry (local.get 0) (local.get 1) (i32.const 0)) + (if (param i64) (result i64) + (then (drop) (i64.const -1)) + ) + ) + + ;; Block signature syntax + + (type $block-sig-1 (func)) + (type $block-sig-2 (func (result i32))) + (type $block-sig-3 (func (param $x i32))) + (type $block-sig-4 (func (param i32 f64 i32) (result i32 f64 i32))) + + (func (export "type-use") + (if (type $block-sig-1) (i32.const 1) (then)) + (if (type $block-sig-2) (i32.const 1) + (then (i32.const 0)) (else (i32.const 2)) + ) + (if (type $block-sig-3) (i32.const 1) (then (drop)) (else (drop))) + (i32.const 0) (f64.const 0) (i32.const 0) + (if (type $block-sig-4) (i32.const 1) (then)) + (drop) (drop) (drop) + (if (type $block-sig-2) (result i32) (i32.const 1) + (then (i32.const 0)) (else (i32.const 2)) + ) + (if (type $block-sig-3) (param i32) (i32.const 1) + (then (drop)) (else (drop)) + ) + (i32.const 0) (f64.const 0) (i32.const 0) + (if (type $block-sig-4) + (param i32) (param f64 i32) (result i32 f64) (result i32) + (i32.const 1) (then) + ) + (drop) (drop) (drop) + ) +) + +(assert_return (invoke "empty" (i32.const 0))) +(assert_return (invoke "empty" (i32.const 1))) +(assert_return (invoke "empty" (i32.const 100))) +(assert_return (invoke "empty" (i32.const -2))) + +(assert_return (invoke "singular" (i32.const 0)) (i32.const 8)) +(assert_return (invoke "singular" (i32.const 1)) (i32.const 7)) +(assert_return (invoke "singular" (i32.const 10)) (i32.const 7)) +(assert_return (invoke "singular" (i32.const -10)) (i32.const 7)) + +(assert_return (invoke "multi" (i32.const 0)) (i32.const 9) (i32.const -1)) +(assert_return (invoke "multi" (i32.const 1)) (i32.const 8) (i32.const 1)) +(assert_return (invoke "multi" (i32.const 13)) (i32.const 8) (i32.const 1)) +(assert_return (invoke "multi" (i32.const -5)) (i32.const 8) (i32.const 1)) + +(assert_return (invoke "nested" (i32.const 0) (i32.const 0)) (i32.const 11)) +(assert_return (invoke "nested" (i32.const 1) (i32.const 0)) (i32.const 10)) +(assert_return (invoke "nested" (i32.const 0) (i32.const 1)) (i32.const 10)) +(assert_return (invoke "nested" (i32.const 3) (i32.const 2)) (i32.const 9)) +(assert_return (invoke "nested" (i32.const 0) (i32.const -100)) (i32.const 10)) +(assert_return (invoke "nested" (i32.const 10) (i32.const 10)) (i32.const 9)) +(assert_return (invoke "nested" (i32.const 0) (i32.const -1)) (i32.const 10)) +(assert_return (invoke "nested" (i32.const -111) (i32.const -2)) (i32.const 9)) + +(assert_return (invoke "as-select-first" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-select-first" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-select-mid" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-select-mid" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-select-last" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-select-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-loop-first" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-loop-first" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-loop-mid" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-loop-mid" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-loop-last" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-loop-last" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-if-condition" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-if-condition" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-br_if-first" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-br_if-first" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-br_if-last" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-br_if-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-br_table-first" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-br_table-first" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-br_table-last" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-br_table-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-call_indirect-first" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-call_indirect-first" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-call_indirect-mid" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-call_indirect-mid" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-call_indirect-last" (i32.const 0)) (i32.const 2)) +(assert_trap (invoke "as-call_indirect-last" (i32.const 1)) "undefined element") + +(assert_return (invoke "as-store-first" (i32.const 0))) +(assert_return (invoke "as-store-first" (i32.const 1))) +(assert_return (invoke "as-store-last" (i32.const 0))) +(assert_return (invoke "as-store-last" (i32.const 1))) + +(assert_return (invoke "as-memory.grow-value" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-memory.grow-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-call-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-call-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-return-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-return-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-drop-operand" (i32.const 0))) +(assert_return (invoke "as-drop-operand" (i32.const 1))) + +(assert_return (invoke "as-br-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-br-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-local.set-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-local.set-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-local.tee-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-local.tee-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-global.set-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-global.set-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-load-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-load-operand" (i32.const 1)) (i32.const 0)) + +(assert_return (invoke "as-unary-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-unary-operand" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "as-unary-operand" (i32.const -1)) (i32.const 0)) + +(assert_return (invoke "as-binary-operand" (i32.const 0) (i32.const 0)) (i32.const 15)) +(assert_return (invoke "as-binary-operand" (i32.const 0) (i32.const 1)) (i32.const -12)) +(assert_return (invoke "as-binary-operand" (i32.const 1) (i32.const 0)) (i32.const -15)) +(assert_return (invoke "as-binary-operand" (i32.const 1) (i32.const 1)) (i32.const 12)) + +(assert_return (invoke "as-test-operand" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-test-operand" (i32.const 1)) (i32.const 0)) + +(assert_return (invoke "as-compare-operand" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-compare-operand" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "as-compare-operand" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-compare-operand" (i32.const 1) (i32.const 1)) (i32.const 0)) + +(assert_return (invoke "as-binary-operands" (i32.const 0)) (i32.const -12)) +(assert_return (invoke "as-binary-operands" (i32.const 1)) (i32.const 12)) + +(assert_return (invoke "as-compare-operands" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-compare-operands" (i32.const 1)) (i32.const 0)) + +(assert_return (invoke "as-mixed-operands" (i32.const 0)) (i32.const -3)) +(assert_return (invoke "as-mixed-operands" (i32.const 1)) (i32.const 27)) + +(assert_return (invoke "break-bare") (i32.const 19)) +(assert_return (invoke "break-value" (i32.const 1)) (i32.const 18)) +(assert_return (invoke "break-value" (i32.const 0)) (i32.const 21)) +(assert_return (invoke "break-multi-value" (i32.const 0)) + (i32.const -18) (i32.const 18) (i64.const -18) +) +(assert_return (invoke "break-multi-value" (i32.const 1)) + (i32.const 18) (i32.const -18) (i64.const 18) +) + +(assert_return (invoke "param" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "param" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "params" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params-id" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "params-id" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "param-break" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "param-break" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params-break" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "params-break" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params-id-break" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "params-id-break" (i32.const 1)) (i32.const 3)) + +(assert_return (invoke "effects" (i32.const 1)) (i32.const -14)) +(assert_return (invoke "effects" (i32.const 0)) (i32.const -6)) + +(assert_return + (invoke "add64_u_with_carry" (i64.const 0) (i64.const 0) (i32.const 0)) + (i64.const 0) (i32.const 0) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const 100) (i64.const 124) (i32.const 0)) + (i64.const 224) (i32.const 0) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 0) (i32.const 0)) + (i64.const -1) (i32.const 0) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 1) (i32.const 0)) + (i64.const 0) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const -1) (i32.const 0)) + (i64.const -2) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 0) (i32.const 1)) + (i64.const 0) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 1) (i32.const 1)) + (i64.const 1) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000) (i32.const 0)) + (i64.const 0) (i32.const 1) +) + +(assert_return + (invoke "add64_u_saturated" (i64.const 0) (i64.const 0)) (i64.const 0) +) +(assert_return + (invoke "add64_u_saturated" (i64.const 1230) (i64.const 23)) (i64.const 1253) +) +(assert_return + (invoke "add64_u_saturated" (i64.const -1) (i64.const 0)) (i64.const -1) +) +(assert_return + (invoke "add64_u_saturated" (i64.const -1) (i64.const 1)) (i64.const -1) +) +(assert_return + (invoke "add64_u_saturated" (i64.const -1) (i64.const -1)) (i64.const -1) +) +(assert_return + (invoke "add64_u_saturated" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i64.const -1) +) + +(assert_return (invoke "type-use")) + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (type $sig) (result i32) (param i32) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (param i32) (type $sig) (result i32) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (param i32) (result i32) (type $sig) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (result i32) (type $sig) (param i32) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (result i32) (param i32) (type $sig) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func (i32.const 0) (if (result i32) (param i32) (i32.const 1) (then)))" + ) + "unexpected token" +) + +(assert_malformed + (module quote + "(func (i32.const 0) (i32.const 1)" + " (if (param $x i32) (then (drop)) (else (drop)))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func))" + "(func (i32.const 1)" + " (if (type $sig) (result i32) (then (i32.const 0)) (else (i32.const 2)))" + " (unreachable)" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 1)" + " (if (type $sig) (result i32) (then (i32.const 0)) (else (i32.const 2)))" + " (unreachable)" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (i32.const 1)" + " (if (type $sig) (param i32) (then (drop)) (else (drop)))" + " (unreachable)" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(func (i32.const 0) (i32.const 1)" + " (if (type $sig) (param i32) (result i32) (then)) (unreachable)" + ")" + ) + "inline function type" +) + +(assert_invalid + (module + (type $sig (func)) + (func (i32.const 1) (if (type $sig) (i32.const 0) (then))) + ) + "type mismatch" +) + +(assert_invalid + (module (func $type-empty-i32 (result i32) (if (i32.const 0) (then)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-i64 (result i64) (if (i32.const 0) (then)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f32 (result f32) (if (i32.const 0) (then)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f64 (result f64) (if (i32.const 0) (then)))) + "type mismatch" +) + +(assert_invalid + (module (func $type-empty-i32 (result i32) (if (i32.const 0) (then) (else)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-i64 (result i64) (if (i32.const 0) (then) (else)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f32 (result f32) (if (i32.const 0) (then) (else)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f64 (result f64) (if (i32.const 0) (then) (else)))) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-num-vs-void + (if (i32.const 1) (then (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-value-num-vs-void-else + (if (i32.const 1) (then (i32.const 1)) (else)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-num-vs-void + (if (i32.const 1) (then) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-num-vs-void + (if (i32.const 1) (then (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-nums-vs-void + (if (i32.const 1) (then (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-value-nums-vs-void-else + (if (i32.const 1) (then (i32.const 1) (i32.const 2)) (else)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-nums-vs-void + (if (i32.const 1) (then) (else (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-nums-vs-void + (if (i32.const 1) (then (i32.const 1) (i32.const 2)) (else (i32.const 2) (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-empty-vs-num (result i32) + (if (result i32) (i32.const 1) (then) (else (i32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-empty-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 0)) (else)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-empty-vs-num (result i32) + (if (result i32) (i32.const 1) (then) (else)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then) (else (i32.const 0) (i32.const 2))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 0) (i32.const 1)) (else)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then) (else)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-no-else-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-no-else-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-void-vs-num (result i32) + (if (result i32) (i32.const 1) (then (nop)) (else (i32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-void-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 0)) (else (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-void-vs-num (result i32) + (if (result i32) (i32.const 1) (then (nop)) (else (nop))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (nop)) (else (i32.const 0) (i32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 0) (i32.const 0)) (else (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (nop)) (else (nop))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-num-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i64.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-num-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1)) (else (i64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-num-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i64.const 1)) (else (i64.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-partial-vs-nums (result i32 i32) + (i32.const 0) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-partial-vs-nums (result i32 i32) + (i32.const 0) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-partial-vs-nums (result i32 i32) + (i32.const 0) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-nums-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-nums-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-nums-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-both-different-value-num-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i64.const 1)) (else (f64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-different-value-nums-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-unreached-select (result i32) + (if (result i64) + (i32.const 0) + (then (select (unreachable) (unreachable) (unreachable))) + (else (i64.const 0)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-unreached-select (result i32) + (if (result i64) + (i32.const 1) + (then (i64.const 0)) + (else (select (unreachable) (unreachable) (unreachable))) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-unreached-select (result i32) + (if (result i64) + (i32.const 1) + (then (select (unreachable) (unreachable) (unreachable))) + (else (select (unreachable) (unreachable) (unreachable))) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-break-last-void-vs-num (result i32) + (if (result i32) (i32.const 1) (then (br 0)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-last-void-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1)) (else (br 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-last-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (br 0)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-last-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (br 0))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-break-empty-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (br 0) (i32.const 1)) + (else (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-empty-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (i32.const 1)) + (else (br 0) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (br 0) (i32.const 1) (i32.const 1)) + (else (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1) (i32.const 1)) + (else (br 0) (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-break-void-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (br 0 (nop)) (i32.const 1)) + (else (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-void-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (i32.const 1)) + (else (br 0 (nop)) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (br 0 (nop)) (i32.const 1) (i32.const 1)) + (else (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1) (i32.const 1)) + (else (br 0 (nop)) (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-break-num-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (br 0 (i64.const 1)) (i32.const 1)) + (else (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-num-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (i32.const 1)) + (else (br 0 (i64.const 1)) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (br 0 (i64.const 1)) (i32.const 1) (i32.const 1)) + (else (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1) (i32.const 1)) + (else (br 0 (i64.const 1)) (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-partial-vs-nums (result i32 i32) + (i32.const 1) + (if (result i32 i32) (i32.const 1) + (then (br 0 (i64.const 1)) (i32.const 1)) + (else (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-partial-vs-nums (result i32 i32) + (i32.const 1) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1)) + (else (br 0 (i64.const 1)) (i32.const 1)) + ) + )) + "type mismatch" +) + +(assert_invalid + (module + (func $type-condition-empty + (if (then)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-block + (i32.const 0) + (block (if (then))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-loop + (i32.const 0) + (loop (if (then))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-then + (i32.const 0) (i32.const 0) + (if (then (if (then)))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-else + (i32.const 0) (i32.const 0) + (if (result i32) (then (i32.const 0)) (else (if (then)) (i32.const 0))) + (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-br + (i32.const 0) + (block (br 0 (if(then))) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-br_if + (i32.const 0) + (block (br_if 0 (if(then)) (i32.const 1)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-br_table + (i32.const 0) + (block (br_table 0 (if(then))) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-return + (return (if(then))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-select + (select (if(then)) (i32.const 1) (i32.const 2)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-call + (call 1 (if(then))) (drop) + ) + (func (param i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-condition-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (if(then)) (i32.const 0) + ) + (drop) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-local.set + (local i32) + (local.set 0 (if(then))) (local.get 0) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-local.tee + (local i32) + (local.tee 0 (if(then))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-condition-empty-in-global.set + (global.set $x (if(then))) (global.get $x) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-condition-empty-in-memory.grow + (memory.grow (if(then))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-condition-empty-in-load + (i32.load (if(then))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-condition-empty-in-store + (i32.store (if(then)) (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module (func $type-param-void-vs-num + (if (param i32) (i32.const 1) (then (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (if (param i32 f64) (i32.const 1) (then (drop) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (f32.const 0) (if (param i32) (i32.const 1) (then (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (f32.const 0) (if (param f32 i32) (i32.const 1) (then (drop) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-nested-void-vs-num + (block (if (param i32) (i32.const 1) (then (drop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (block (if (param i32 f64) (i32.const 1) (then (drop) (drop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (block (f32.const 0) (if (param i32) (i32.const 1) (then (drop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (block (f32.const 0) (if (param f32 i32) (i32.const 1) (then (drop) (drop)))) + )) + "type mismatch" +) + +(assert_malformed + (module quote "(func (param i32) (result i32) if (param $x i32) end)") + "unexpected token" +) +(assert_malformed + (module quote "(func (param i32) (result i32) (if (param $x i32) (then)))") + "unexpected token" +) + +(assert_malformed + (module quote "(func i32.const 0 if end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if $a end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if else $l end)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if $a else $l end)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if else end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if else $l end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if else $l1 end $l2)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if $a else end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if $a else $a end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if $a else $l end $l)") + "mismatching label" +) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/imports.wast b/runtime/unc-vm/tests/wast/spec/imports.wast new file mode 100644 index 000000000..35e8c917f --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/imports.wast @@ -0,0 +1,696 @@ +;; Auxiliary module to import from + +(module + (func (export "func")) + (func (export "func-i32") (param i32)) + (func (export "func-f32") (param f32)) + (func (export "func->i32") (result i32) (i32.const 22)) + (func (export "func->f32") (result f32) (f32.const 11)) + (func (export "func-i32->i32") (param i32) (result i32) (local.get 0)) + (func (export "func-i64->i64") (param i64) (result i64) (local.get 0)) + (global (export "global-i32") i32 (i32.const 55)) + (global (export "global-f32") f32 (f32.const 44)) + (global (export "global-mut-i64") (mut i64) (i64.const 66)) + (table (export "table-10-inf") 10 funcref) + (table (export "table-10-20") 10 20 funcref) + (memory (export "memory-2-inf") 2) + ;; Multiple memories are not yet supported + ;; (memory (export "memory-2-4") 2 4) +) + +(register "test") + + +;; Functions + +(module + (type $func_i32 (func (param i32))) + (type $func_i64 (func (param i64))) + (type $func_f32 (func (param f32))) + (type $func_f64 (func (param f64))) + + (import "spectest" "print_i32" (func (param i32))) + ;; JavaScript can't handle i64 yet. + ;; (func (import "spectest" "print_i64") (param i64)) + (import "spectest" "print_i32" (func $print_i32 (param i32))) + ;; JavaScript can't handle i64 yet. + ;; (import "spectest" "print_i64" (func $print_i64 (param i64))) + (import "spectest" "print_f32" (func $print_f32 (param f32))) + (import "spectest" "print_f64" (func $print_f64 (param f64))) + (import "spectest" "print_i32_f32" (func $print_i32_f32 (param i32 f32))) + (import "spectest" "print_f64_f64" (func $print_f64_f64 (param f64 f64))) + (func $print_i32-2 (import "spectest" "print_i32") (param i32)) + (func $print_f64-2 (import "spectest" "print_f64") (param f64)) + (import "test" "func-i64->i64" (func $i64->i64 (param i64) (result i64))) + + (func (export "p1") (import "spectest" "print_i32") (param i32)) + (func $p (export "p2") (import "spectest" "print_i32") (param i32)) + (func (export "p3") (export "p4") (import "spectest" "print_i32") (param i32)) + (func (export "p5") (import "spectest" "print_i32") (type 0)) + (func (export "p6") (import "spectest" "print_i32") (type 0) (param i32) (result)) + + (import "spectest" "print_i32" (func (type $forward))) + (func (import "spectest" "print_i32") (type $forward)) + (type $forward (func (param i32))) + + (table funcref (elem $print_i32 $print_f64)) + + (func (export "print32") (param $i i32) + (local $x f32) + (local.set $x (f32.convert_i32_s (local.get $i))) + (call 0 (local.get $i)) + (call $print_i32_f32 + (i32.add (local.get $i) (i32.const 1)) + (f32.const 42) + ) + (call $print_i32 (local.get $i)) + (call $print_i32-2 (local.get $i)) + (call $print_f32 (local.get $x)) + (call_indirect (type $func_i32) (local.get $i) (i32.const 0)) + ) + + (func (export "print64") (param $i i64) + (local $x f64) + (local.set $x (f64.convert_i64_s (call $i64->i64 (local.get $i)))) + ;; JavaScript can't handle i64 yet. + ;; (call 1 (local.get $i)) + (call $print_f64_f64 + (f64.add (local.get $x) (f64.const 1)) + (f64.const 53) + ) + ;; JavaScript can't handle i64 yet. + ;; (call $print_i64 (local.get $i)) + (call $print_f64 (local.get $x)) + (call $print_f64-2 (local.get $x)) + (call_indirect (type $func_f64) (local.get $x) (i32.const 1)) + ) +) + +(assert_return (invoke "print32" (i32.const 13))) +(assert_return (invoke "print64" (i64.const 24))) + +(assert_invalid + (module + (type (func (result i32))) + (import "test" "func" (func (type 1))) + ) + "unknown type" +) + +;; Export sharing name with import +(module + (import "spectest" "print_i32" (func $imported_print (param i32))) + (func (export "print_i32") (param $i i32) + (call $imported_print (local.get $i)) + ) +) + +(assert_return (invoke "print_i32" (i32.const 13))) + +;; Export sharing name with import +(module + (import "spectest" "print_i32" (func $imported_print (param i32))) + (func (export "print_i32") (param $i i32) (param $j i32) (result i32) + (i32.add (local.get $i) (local.get $j)) + ) +) + +(assert_return (invoke "print_i32" (i32.const 5) (i32.const 11)) (i32.const 16)) + +(module (import "test" "func" (func))) +(module (import "test" "func-i32" (func (param i32)))) +(module (import "test" "func-f32" (func (param f32)))) +(module (import "test" "func->i32" (func (result i32)))) +(module (import "test" "func->f32" (func (result f32)))) +(module (import "test" "func-i32->i32" (func (param i32) (result i32)))) +(module (import "test" "func-i64->i64" (func (param i64) (result i64)))) + +(assert_unlinkable + (module (import "test" "unknown" (func))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (func))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "func" (func (param i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func" (func (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func" (func (param i32) (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (param f32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (param i64)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (param i32) (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (param i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (result f32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (result i64)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (param i32) (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32->i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32->i32" (func (param i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32->i32" (func (result i32)))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "test" "global-i32" (func (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "global_i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (func))) + "incompatible import type" +) + + +;; Globals + +(module + (import "spectest" "global_i32" (global i32)) + (global (import "spectest" "global_i32") i32) + + (import "spectest" "global_i32" (global $x i32)) + (global $y (import "spectest" "global_i32") i32) + + ;; JavaScript can't handle i64 yet. + ;; (import "spectest" "global_i64" (global i64)) + (import "spectest" "global_f32" (global f32)) + (import "spectest" "global_f64" (global f64)) + + (func (export "get-0") (result i32) (global.get 0)) + (func (export "get-1") (result i32) (global.get 1)) + (func (export "get-x") (result i32) (global.get $x)) + (func (export "get-y") (result i32) (global.get $y)) +) + +(assert_return (invoke "get-0") (i32.const 666)) +(assert_return (invoke "get-1") (i32.const 666)) +(assert_return (invoke "get-x") (i32.const 666)) +(assert_return (invoke "get-y") (i32.const 666)) + +(module (import "test" "global-i32" (global i32))) +(module (import "test" "global-f32" (global f32))) +(module (import "test" "global-mut-i64" (global (mut i64)))) + +(assert_unlinkable + (module (import "test" "unknown" (global i32))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (global i32))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "global-i32" (global i64))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-i32" (global f32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-i32" (global f64))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-i32" (global (mut i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-f32" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-f32" (global i64))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-f32" (global f64))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-f32" (global (mut f32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-mut-i64" (global (mut i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-mut-i64" (global (mut f32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-mut-i64" (global (mut f64)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-mut-i64" (global i64))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "test" "func" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "print_i32" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (global i32))) + "incompatible import type" +) + + +;; Tables + +(module + (type (func (result i32))) + (import "spectest" "table" (table $tab 10 20 funcref)) + (elem (table $tab) (i32.const 1) func $f $g) + + (func (export "call") (param i32) (result i32) + (call_indirect $tab (type 0) (local.get 0)) + ) + (func $f (result i32) (i32.const 11)) + (func $g (result i32) (i32.const 22)) +) + +(assert_trap (invoke "call" (i32.const 0)) "uninitialized element") +(assert_return (invoke "call" (i32.const 1)) (i32.const 11)) +(assert_return (invoke "call" (i32.const 2)) (i32.const 22)) +(assert_trap (invoke "call" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "call" (i32.const 100)) "undefined element") + + +(module + (type (func (result i32))) + (table $tab (import "spectest" "table") 10 20 funcref) + (elem (table $tab) (i32.const 1) func $f $g) + + (func (export "call") (param i32) (result i32) + (call_indirect $tab (type 0) (local.get 0)) + ) + (func $f (result i32) (i32.const 11)) + (func $g (result i32) (i32.const 22)) +) + +(assert_trap (invoke "call" (i32.const 0)) "uninitialized element") +(assert_return (invoke "call" (i32.const 1)) (i32.const 11)) +(assert_return (invoke "call" (i32.const 2)) (i32.const 22)) +(assert_trap (invoke "call" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "call" (i32.const 100)) "undefined element") + + +(module + (import "spectest" "table" (table 0 funcref)) + (import "spectest" "table" (table 0 funcref)) + (table 10 funcref) + (table 10 funcref) +) + +(module (import "test" "table-10-inf" (table 10 funcref))) +(module (import "test" "table-10-inf" (table 5 funcref))) +(module (import "test" "table-10-inf" (table 0 funcref))) +(module (import "test" "table-10-20" (table 10 funcref))) +(module (import "test" "table-10-20" (table 5 funcref))) +(module (import "test" "table-10-20" (table 0 funcref))) +(module (import "test" "table-10-20" (table 10 20 funcref))) +(module (import "test" "table-10-20" (table 5 20 funcref))) +(module (import "test" "table-10-20" (table 0 20 funcref))) +(module (import "test" "table-10-20" (table 10 25 funcref))) +(module (import "test" "table-10-20" (table 5 25 funcref))) +(module (import "test" "table-10-20" (table 0 25 funcref))) +(module (import "spectest" "table" (table 10 funcref))) +(module (import "spectest" "table" (table 5 funcref))) +(module (import "spectest" "table" (table 0 funcref))) +(module (import "spectest" "table" (table 10 20 funcref))) +(module (import "spectest" "table" (table 5 20 funcref))) +(module (import "spectest" "table" (table 0 20 funcref))) +(module (import "spectest" "table" (table 10 25 funcref))) +(module (import "spectest" "table" (table 5 25 funcref))) + +(assert_unlinkable + (module (import "test" "unknown" (table 10 funcref))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (table 10 funcref))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "table-10-inf" (table 12 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (table 10 20 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-20" (table 12 20 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-20" (table 10 18 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (table 12 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (table 10 15 funcref))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "test" "func" (table 10 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-i32" (table 10 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (table 10 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "print_i32" (table 10 funcref))) + "incompatible import type" +) + + + +;; Memories + +(module + (import "spectest" "memory" (memory 1 2)) + (data (memory 0) (i32.const 10) "\10") + + (func (export "load") (param i32) (result i32) (i32.load (local.get 0))) +) + +(assert_return (invoke "load" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load" (i32.const 10)) (i32.const 16)) +(assert_return (invoke "load" (i32.const 8)) (i32.const 0x100000)) +(assert_trap (invoke "load" (i32.const 1000000)) "out of bounds memory access") + +(module + (memory (import "spectest" "memory") 1 2) + (data (memory 0) (i32.const 10) "\10") + + (func (export "load") (param i32) (result i32) (i32.load (local.get 0))) +) +(assert_return (invoke "load" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load" (i32.const 10)) (i32.const 16)) +(assert_return (invoke "load" (i32.const 8)) (i32.const 0x100000)) +(assert_trap (invoke "load" (i32.const 1000000)) "out of bounds memory access") + +(assert_invalid + (module (import "" "" (memory 1)) (import "" "" (memory 1))) + "multiple memories" +) +(assert_invalid + (module (import "" "" (memory 1)) (memory 0)) + "multiple memories" +) +(assert_invalid + (module (memory 0) (memory 0)) + "multiple memories" +) + +(module (import "test" "memory-2-inf" (memory 2))) +(module (import "test" "memory-2-inf" (memory 1))) +(module (import "test" "memory-2-inf" (memory 0))) +(module (import "spectest" "memory" (memory 1))) +(module (import "spectest" "memory" (memory 0))) +(module (import "spectest" "memory" (memory 1 2))) +(module (import "spectest" "memory" (memory 0 2))) +(module (import "spectest" "memory" (memory 1 3))) +(module (import "spectest" "memory" (memory 0 3))) + +(assert_unlinkable + (module (import "test" "unknown" (memory 1))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (memory 1))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "memory-2-inf" (memory 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (memory 2 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (memory 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (memory 1 1))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "test" "func-i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "print_i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "global_i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (memory 1))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "spectest" "memory" (memory 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (memory 1 1))) + "incompatible import type" +) + +(module + (import "spectest" "memory" (memory 0 3)) ;; actual has max size 2 + (func (export "grow") (param i32) (result i32) (memory.grow (local.get 0))) +) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 2)) + +(module $Mgm + (memory (export "memory") 1) ;; initial size is 1 + (func (export "grow") (result i32) (memory.grow (i32.const 1))) +) +(register "grown-memory" $Mgm) +(assert_return (invoke $Mgm "grow") (i32.const 1)) ;; now size is 2 +(module $Mgim1 + ;; imported memory limits should match, because external memory size is 2 now + (memory (export "memory") (import "grown-memory" "memory") 2) + (func (export "grow") (result i32) (memory.grow (i32.const 1))) +) +(register "grown-imported-memory" $Mgim1) +(assert_return (invoke $Mgim1 "grow") (i32.const 2)) ;; now size is 3 +(module $Mgim2 + ;; imported memory limits should match, because external memory size is 3 now + (import "grown-imported-memory" "memory" (memory 3)) + (func (export "size") (result i32) (memory.size)) +) +(assert_return (invoke $Mgim2 "size") (i32.const 3)) + + +;; Syntax errors + +(assert_malformed + (module quote "(func) (import \"\" \"\" (func))") + "import after function" +) +(assert_malformed + (module quote "(func) (import \"\" \"\" (global i64))") + "import after function" +) +(assert_malformed + (module quote "(func) (import \"\" \"\" (table 0 funcref))") + "import after function" +) +(assert_malformed + (module quote "(func) (import \"\" \"\" (memory 0))") + "import after function" +) + +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (func))") + "import after global" +) +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (global f32))") + "import after global" +) +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (table 0 funcref))") + "import after global" +) +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (memory 0))") + "import after global" +) + +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (func))") + "import after table" +) +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (global i32))") + "import after table" +) +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (table 0 funcref))") + "import after table" +) +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (memory 0))") + "import after table" +) + +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (func))") + "import after memory" +) +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (global i32))") + "import after memory" +) +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (table 1 3 funcref))") + "import after memory" +) +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (memory 1 2))") + "import after memory" +) + +;; This module is required to validate, regardless of whether it can be +;; linked. Overloading is not possible in wasm itself, but it is possible +;; in modules from which wasm can import. +(module) +(register "not wasm") +(assert_unlinkable + (module + (import "not wasm" "overloaded" (func)) + (import "not wasm" "overloaded" (func (param i32))) + (import "not wasm" "overloaded" (func (param i32 i32))) + (import "not wasm" "overloaded" (func (param i64))) + (import "not wasm" "overloaded" (func (param f32))) + (import "not wasm" "overloaded" (func (param f64))) + (import "not wasm" "overloaded" (func (result i32))) + (import "not wasm" "overloaded" (func (result i64))) + (import "not wasm" "overloaded" (func (result f32))) + (import "not wasm" "overloaded" (func (result f64))) + (import "not wasm" "overloaded" (global i32)) + (import "not wasm" "overloaded" (global i64)) + (import "not wasm" "overloaded" (global f32)) + (import "not wasm" "overloaded" (global f64)) + (import "not wasm" "overloaded" (table 0 funcref)) + (import "not wasm" "overloaded" (memory 0)) + ) + "unknown import" +) diff --git a/runtime/unc-vm/tests/wast/spec/inline-module.wast b/runtime/unc-vm/tests/wast/spec/inline-module.wast new file mode 100644 index 000000000..dc7ead776 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/inline-module.wast @@ -0,0 +1 @@ +(func) (memory 0) (func (export "f")) diff --git a/runtime/unc-vm/tests/wast/spec/int_exprs.wast b/runtime/unc-vm/tests/wast/spec/int_exprs.wast new file mode 100644 index 000000000..22e1fbcb5 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/int_exprs.wast @@ -0,0 +1,350 @@ +;; Test interesting integer "expressions". These tests contain code +;; patterns which tempt common value-changing optimizations. + +;; Test that x+1>n is not folded to x. + +(module + (func (export "i32.no_fold_shl_shr_s") (param $x i32) (result i32) + (i32.shr_s (i32.shl (local.get $x) (i32.const 1)) (i32.const 1))) + (func (export "i32.no_fold_shl_shr_u") (param $x i32) (result i32) + (i32.shr_u (i32.shl (local.get $x) (i32.const 1)) (i32.const 1))) + + (func (export "i64.no_fold_shl_shr_s") (param $x i64) (result i64) + (i64.shr_s (i64.shl (local.get $x) (i64.const 1)) (i64.const 1))) + (func (export "i64.no_fold_shl_shr_u") (param $x i64) (result i64) + (i64.shr_u (i64.shl (local.get $x) (i64.const 1)) (i64.const 1))) +) + +(assert_return (invoke "i32.no_fold_shl_shr_s" (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "i32.no_fold_shl_shr_u" (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "i64.no_fold_shl_shr_s" (i64.const 0x8000000000000000)) (i64.const 0)) +(assert_return (invoke "i64.no_fold_shl_shr_u" (i64.const 0x8000000000000000)) (i64.const 0)) + +;; Test that x>>n<?,./ ") (result i32) (i32.const 6)) + + ;; Test that we can use names that have special meaning in JS. + (func (export "NaN") (result i32) (i32.const 7)) + (func (export "Infinity") (result i32) (i32.const 8)) + (func (export "if") (result i32) (i32.const 9)) + + ;; Test that we can use common libc names without conflict. + (func (export "malloc") (result i32) (i32.const 10)) + + ;; Test that we can use some libc hidden names without conflict. + (func (export "_malloc") (result i32) (i32.const 11)) + (func (export "__malloc") (result i32) (i32.const 12)) + + ;; Test that names are case-sensitive. + (func (export "a") (result i32) (i32.const 13)) + (func (export "A") (result i32) (i32.const 14)) + + ;; Test that UTF-8 BOM code points can appear in identifiers. + (func (export "") (result i32) (i32.const 15)) + + ;; Test that Unicode normalization is not applied. These function names + ;; contain different codepoints which normalize to the same thing under + ;; NFC or NFD. + (func (export "Å") (result i32) (i32.const 16)) + (func (export "Å") (result i32) (i32.const 17)) + (func (export "Å") (result i32) (i32.const 18)) + + ;; Test that Unicode compatibility normalization is not applied. These + ;; function names contain different codepoints which normalize to the + ;; same thing under NFKC or NFKD. + (func (export "ffi") (result i32) (i32.const 19)) + (func (export "ffi") (result i32) (i32.const 20)) + (func (export "ffi") (result i32) (i32.const 21)) + + ;; Test the C0 control codes. + (func (export "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f") (result i32) (i32.const 22)) + (func (export "\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f") (result i32) (i32.const 23)) + ;; Test miscellaneous control codes. + (func (export " \7f") (result i32) (i32.const 24)) + ;; Test the C1 control codes. + (func (export "\c2\80\c2\81\c2\82\c2\83\c2\84\c2\85\c2\86\c2\87\c2\88\c2\89\c2\8a\c2\8b\c2\8c\c2\8d\c2\8e\c2\8f") (result i32) (i32.const 25)) + (func (export "\c2\90\c2\91\c2\92\c2\93\c2\94\c2\95\c2\96\c2\97\c2\98\c2\99\c2\9a\c2\9b\c2\9c\c2\9d\c2\9e\c2\9f") (result i32) (i32.const 26)) + ;; Test the Unicode Specials. + (func (export "\ef\bf\b0\ef\bf\b1\ef\bf\b2\ef\bf\b3\ef\bf\b4\ef\bf\b5\ef\bf\b6\ef\bf\b7") (result i32) (i32.const 27)) + (func (export "\ef\bf\b8\ef\bf\b9\ef\bf\ba\ef\bf\bb\ef\bf\bc\ef\bf\bd\ef\bf\be\ef\bf\bf") (result i32) (i32.const 28)) + + ;; Test that the control pictures are distinct from the control codes they + ;; depict. These correspond to the C0 and miscellaneous control code tests + ;; above. + (func (export "␀␁␂␃␄␅␆␇␈␉␊␋␌␍␎␏") (result i32) (i32.const 29)) + (func (export "␐␑␒␓␔␕␖␗␘␙␚␛␜␝␞␟") (result i32) (i32.const 30)) + (func (export "␠␡") (result i32) (i32.const 31)) + + ;; Test the Unicode Specials in non-escaped form (excluding U+FFFE and + ;; U+FFFF, so that generic tools don't detect this file as non-UTF-8). + (func (export "￰￱￲￳￴￵￶￷￸�") (result i32) (i32.const 32)) + + ;; Test a bare ZWJ code point. + (func (export "‍") (result i32) (i32.const 33)) + ;; Test a bare ZWNJ code point. + (func (export "‌") (result i32) (i32.const 34)) + + ;; Test various bare joiner code points. + (func (export "͏") (result i32) (i32.const 35)) + (func (export "⁠") (result i32) (i32.const 36)) + (func (export "⵿") (result i32) (i32.const 37)) + (func (export "𑁿") (result i32) (i32.const 38)) + (func (export "᠎") (result i32) (i32.const 39)) + + ;; Test various interesting code points: reverse BOM, zero-width space, + ;; no-break space, soft hyphen, word joiner, ogham space mark, + ;; right-to-left override, left-to-right override. + (func (export "￯​ ­⁠ ‮‭") (result i32) (i32.const 40)) + + ;; Test more interesting code points: left-to-right mark, right-to-left mark, + ;; non-breaking hyphen, line separator, paragraph separator, + ;; left-to-right embedding, right-to-left embedding, + ;; pop directional formatting, narrow no-break space, left-to-right isolate, + ;; right-to-left isolate, first strong isolate, pop directional isolate. + (func (export "‎‏‑

‪‫‬ ⁦⁧⁨⁩") (result i32) (i32.const 41)) + + ;; Test some deprecated code points: inhibit symmetric swapping, + ;; activate symmetric swapping, inhibit arabic form shaping, + ;; activate arabic form shaping, national digit shapes, nominal digit shapes. + (func (export "") (result i32) (i32.const 42)) + + ;; Test "invisible" operator code points. + (func (export "⁡⁢⁣⁤") (result i32) (i32.const 43)) + + ;; Test that code points outside the BMP are supported. + (func (export "𐀀󟿿􏿿") (result i32) (i32.const 44)) + + ;; Test that WebAssembly implementations cope in the presence of Zalgo. + (func (export "Z̴͇̫̥̪͓͈͔͎̗̞̺̯̱̞̙̱̜̖̠̏͆̆͛͌͘͞ḁ̶̰̳̭͙̲̱̹̝͎̼͗ͨ̎̄̆͗̿̀́͟͡l̶̷͉̩̹̫̝͖̙̲̼͇͚͍̮͎̥̞̈́͊͗ͦ̈́ͫ̇́̚ͅͅg̶͕͔͚̩̓̐̅ͮ̔̐̎̂̏̾͊̍͋͊ͧ́̆ͦ͞o̡͋̔͐ͪͩ͏̢̧̫̙̤̮͖͙͓̺̜̩̼̘̠́") (result i32) (i32.const 45)) + + ;; Test Hangul filler code points. + (func (export "ᅟᅠㅤᅠ") (result i32) (i32.const 46)) + + ;; Test variation selectors (which are also ID_Continue code points). + (func (export "︀") (result i32) (i32.const 47)) + (func (export "︄") (result i32) (i32.const 48)) + (func (export "󠄀") (result i32) (i32.const 49)) + (func (export "󠇯") (result i32) (i32.const 50)) + + ;; Test an uncombined combining code point. + (func (export "̈") (result i32) (i32.const 51)) + + ;; Test that numerous different present and historical representations of the + ;; "newline" concept are distinct. Tests largely inspired by: + ;; https://en.wikipedia.org/wiki/Newline#Representations + ;; https://en.wikipedia.org/wiki/Newline#Unicode and + ;; https://en.wikipedia.org/wiki/Newline#Reverse_and_partial_line_feeds + (func (export "\0a") (result i32) (i32.const 52)) + (func (export "␤") (result i32) (i32.const 53)) + (func (export "
") (result i32) (i32.const 54)) + (func (export "\0d") (result i32) (i32.const 55)) + (func (export "\0d\0a") (result i32) (i32.const 56)) + (func (export "\0a\0d") (result i32) (i32.const 57)) + (func (export "\1e") (result i32) (i32.const 58)) + (func (export "\0b") (result i32) (i32.const 59)) + (func (export "\0c") (result i32) (i32.const 60)) + (func (export "\c2\85") (result i32) (i32.const 61)) + (func (export "
") (result i32) (i32.const 62)) + (func (export "…") (result i32) (i32.const 63)) + (func (export "⏎") (result i32) (i32.const 64)) + (func (export "\c2\8b") (result i32) (i32.const 65)) + (func (export "\c2\8c") (result i32) (i32.const 66)) + (func (export "\c2\8d") (result i32) (i32.const 67)) + (func (export "↵") (result i32) (i32.const 68)) + (func (export "↩") (result i32) (i32.const 69)) + (func (export "⌤") (result i32) (i32.const 70)) + (func (export "⤶") (result i32) (i32.const 71)) + (func (export "↲") (result i32) (i32.const 72)) + (func (export "⮨") (result i32) (i32.const 73)) + (func (export "⮰") (result i32) (i32.const 74)) + + ;; Test that non-characters are not replaced by the replacement character. + (func (export "�") (result i32) (i32.const 75)) + (func (export "\ef\b7\90") (result i32) (i32.const 76)) + (func (export "\ef\b7\91") (result i32) (i32.const 77)) + (func (export "\ef\b7\92") (result i32) (i32.const 78)) + (func (export "\ef\b7\93") (result i32) (i32.const 79)) + (func (export "\ef\b7\94") (result i32) (i32.const 80)) + (func (export "\ef\b7\95") (result i32) (i32.const 81)) + (func (export "\ef\b7\96") (result i32) (i32.const 82)) + (func (export "\ef\b7\97") (result i32) (i32.const 83)) + (func (export "\ef\b7\98") (result i32) (i32.const 84)) + (func (export "\ef\b7\99") (result i32) (i32.const 85)) + (func (export "\ef\b7\9a") (result i32) (i32.const 86)) + (func (export "\ef\b7\9b") (result i32) (i32.const 87)) + (func (export "\ef\b7\9c") (result i32) (i32.const 88)) + (func (export "\ef\b7\9d") (result i32) (i32.const 89)) + (func (export "\ef\b7\9e") (result i32) (i32.const 90)) + (func (export "\ef\b7\9f") (result i32) (i32.const 91)) + (func (export "\ef\b7\a0") (result i32) (i32.const 92)) + (func (export "\ef\b7\a1") (result i32) (i32.const 93)) + (func (export "\ef\b7\a2") (result i32) (i32.const 94)) + (func (export "\ef\b7\a3") (result i32) (i32.const 95)) + (func (export "\ef\b7\a4") (result i32) (i32.const 96)) + (func (export "\ef\b7\a5") (result i32) (i32.const 97)) + (func (export "\ef\b7\a6") (result i32) (i32.const 98)) + (func (export "\ef\b7\a7") (result i32) (i32.const 99)) + (func (export "\ef\b7\a8") (result i32) (i32.const 100)) + (func (export "\ef\b7\a9") (result i32) (i32.const 101)) + (func (export "\ef\b7\aa") (result i32) (i32.const 102)) + (func (export "\ef\b7\ab") (result i32) (i32.const 103)) + (func (export "\ef\b7\ac") (result i32) (i32.const 104)) + (func (export "\ef\b7\ad") (result i32) (i32.const 105)) + (func (export "\ef\b7\ae") (result i32) (i32.const 106)) + (func (export "\ef\b7\af") (result i32) (i32.const 107)) + (func (export "\ef\bf\be") (result i32) (i32.const 108)) + (func (export "\ef\bf\bf") (result i32) (i32.const 109)) + (func (export "\f0\9f\bf\be") (result i32) (i32.const 110)) + (func (export "\f0\9f\bf\bf") (result i32) (i32.const 111)) + (func (export "\f0\af\bf\be") (result i32) (i32.const 112)) + (func (export "\f0\af\bf\bf") (result i32) (i32.const 113)) + (func (export "\f0\bf\bf\be") (result i32) (i32.const 114)) + (func (export "\f0\bf\bf\bf") (result i32) (i32.const 115)) + (func (export "\f1\8f\bf\be") (result i32) (i32.const 116)) + (func (export "\f1\8f\bf\bf") (result i32) (i32.const 117)) + (func (export "\f1\9f\bf\be") (result i32) (i32.const 118)) + (func (export "\f1\9f\bf\bf") (result i32) (i32.const 119)) + (func (export "\f1\af\bf\be") (result i32) (i32.const 120)) + (func (export "\f1\af\bf\bf") (result i32) (i32.const 121)) + (func (export "\f1\bf\bf\be") (result i32) (i32.const 122)) + (func (export "\f1\bf\bf\bf") (result i32) (i32.const 123)) + (func (export "\f2\8f\bf\be") (result i32) (i32.const 124)) + (func (export "\f2\8f\bf\bf") (result i32) (i32.const 125)) + (func (export "\f2\9f\bf\be") (result i32) (i32.const 126)) + (func (export "\f2\9f\bf\bf") (result i32) (i32.const 127)) + (func (export "\f2\af\bf\be") (result i32) (i32.const 128)) + (func (export "\f2\af\bf\bf") (result i32) (i32.const 129)) + (func (export "\f2\bf\bf\be") (result i32) (i32.const 130)) + (func (export "\f2\bf\bf\bf") (result i32) (i32.const 131)) + (func (export "\f3\8f\bf\be") (result i32) (i32.const 132)) + (func (export "\f3\8f\bf\bf") (result i32) (i32.const 133)) + (func (export "\f3\9f\bf\be") (result i32) (i32.const 134)) + (func (export "\f3\9f\bf\bf") (result i32) (i32.const 135)) + (func (export "\f3\af\bf\be") (result i32) (i32.const 136)) + (func (export "\f3\af\bf\bf") (result i32) (i32.const 137)) + (func (export "\f3\bf\bf\be") (result i32) (i32.const 138)) + (func (export "\f3\bf\bf\bf") (result i32) (i32.const 139)) + (func (export "\f4\8f\bf\be") (result i32) (i32.const 140)) + (func (export "\f4\8f\bf\bf") (result i32) (i32.const 141)) + + ;; Test an interrobang with combining diacritical marks above. + ;; https://xkcd.com/1209/ + (func (export "̈‽̈̉") (result i32) (i32.const 142)) + + ;; Test that RLM/LRM don't change the logical byte order. + (func (export "abc") (result i32) (i32.const 143)) + (func (export "‭abc") (result i32) (i32.const 144)) + (func (export "‮cba") (result i32) (i32.const 145)) + (func (export "‭abc‮") (result i32) (i32.const 146)) + (func (export "‮cba‭") (result i32) (i32.const 147)) + + ;; Test that Unicode font variations are preserved. + (func (export "𝑨") (result i32) (i32.const 148)) + (func (export "𝐴") (result i32) (i32.const 149)) + (func (export "𝘈") (result i32) (i32.const 150)) + (func (export "𝘼") (result i32) (i32.const 151)) + (func (export "𝐀") (result i32) (i32.const 152)) + (func (export "𝓐") (result i32) (i32.const 153)) + (func (export "𝕬") (result i32) (i32.const 154)) + (func (export "𝗔") (result i32) (i32.const 155)) + (func (export "𝒜") (result i32) (i32.const 156)) + (func (export "𝔄") (result i32) (i32.const 157)) + (func (export "𝔸") (result i32) (i32.const 158)) + (func (export "𝖠") (result i32) (i32.const 159)) + (func (export "𝙰") (result i32) (i32.const 160)) + (func (export "ᴀ") (result i32) (i32.const 161)) + + ;; Test that various additional letter variations are preserved. + ;; (U+0040, U+0061, U+0041, U+00C5, U+0041 U+030A, U+212B, and the font + ;; variations are covered above.) + (func (export "ᴬ") (result i32) (i32.const 162)) + (func (export "Ⓐ") (result i32) (i32.const 163)) + (func (export "A") (result i32) (i32.const 164)) + (func (export "🄐") (result i32) (i32.const 165)) + (func (export "🄰") (result i32) (i32.const 166)) + (func (export "󠁁") (result i32) (i32.const 167)) + (func (export "U+0041") (result i32) (i32.const 168)) + (func (export "A​") (result i32) (i32.const 169)) + (func (export "А") (result i32) (i32.const 170)) + (func (export "Ꙗ") (result i32) (i32.const 171)) + (func (export "ⷼ") (result i32) (i32.const 172)) + (func (export "ⷶ") (result i32) (i32.const 173)) + (func (export "Ɐ") (result i32) (i32.const 174)) + (func (export "🅐") (result i32) (i32.const 175)) + (func (export "🅰") (result i32) (i32.const 176)) + (func (export "Ⱝ") (result i32) (i32.const 177)) + (func (export "𐐂") (result i32) (i32.const 178)) + (func (export "𐐈") (result i32) (i32.const 179)) + (func (export "𐒰") (result i32) (i32.const 180)) + (func (export "À") (result i32) (i32.const 181)) + (func (export "Á") (result i32) (i32.const 182)) + (func (export "Â") (result i32) (i32.const 183)) + (func (export "Ã") (result i32) (i32.const 184)) + (func (export "Ä") (result i32) (i32.const 185)) + (func (export "Ā") (result i32) (i32.const 186)) + (func (export "Ă") (result i32) (i32.const 187)) + (func (export "Ą") (result i32) (i32.const 188)) + (func (export "Ǎ") (result i32) (i32.const 189)) + (func (export "Ǟ") (result i32) (i32.const 190)) + (func (export "Ǡ") (result i32) (i32.const 191)) + (func (export "Ǻ") (result i32) (i32.const 192)) + (func (export "Ȁ") (result i32) (i32.const 193)) + (func (export "Ȃ") (result i32) (i32.const 194)) + (func (export "Ȧ") (result i32) (i32.const 195)) + (func (export "Ⱥ") (result i32) (i32.const 196)) + (func (export "Ӑ") (result i32) (i32.const 197)) + (func (export "Ӓ") (result i32) (i32.const 198)) + (func (export "ߊ") (result i32) (i32.const 199)) + (func (export "ࠡ") (result i32) (i32.const 200)) + (func (export "ࠢ") (result i32) (i32.const 201)) + (func (export "ࠣ") (result i32) (i32.const 202)) + (func (export "ࠤ") (result i32) (i32.const 203)) + (func (export "ࠥ") (result i32) (i32.const 204)) + (func (export "ऄ") (result i32) (i32.const 205)) + (func (export "अ") (result i32) (i32.const 206)) + (func (export "ॲ") (result i32) (i32.const 207)) + (func (export "অ") (result i32) (i32.const 208)) + (func (export "ਅ") (result i32) (i32.const 209)) + (func (export "અ") (result i32) (i32.const 210)) + (func (export "ଅ") (result i32) (i32.const 211)) + (func (export "அ") (result i32) (i32.const 212)) + (func (export "అ") (result i32) (i32.const 213)) + (func (export "ಅ") (result i32) (i32.const 214)) + (func (export "അ") (result i32) (i32.const 215)) + (func (export "ะ") (result i32) (i32.const 216)) + (func (export "ະ") (result i32) (i32.const 217)) + (func (export "༁") (result i32) (i32.const 218)) + (func (export "ཨ") (result i32) (i32.const 219)) + (func (export "ྸ") (result i32) (i32.const 220)) + (func (export "အ") (result i32) (i32.const 221)) + (func (export "ဢ") (result i32) (i32.const 222)) + (func (export "ႜ") (result i32) (i32.const 223)) + (func (export "ᅡ") (result i32) (i32.const 224)) + (func (export "አ") (result i32) (i32.const 225)) + (func (export "ዐ") (result i32) (i32.const 226)) + (func (export "Ꭰ") (result i32) (i32.const 227)) + (func (export "ᐊ") (result i32) (i32.const 228)) + (func (export "ᖳ") (result i32) (i32.const 229)) + (func (export "ᚨ") (result i32) (i32.const 230)) + (func (export "ᚪ") (result i32) (i32.const 231)) + (func (export "ᛆ") (result i32) (i32.const 232)) + (func (export "ᜀ") (result i32) (i32.const 233)) + (func (export "ᜠ") (result i32) (i32.const 234)) + (func (export "ᝀ") (result i32) (i32.const 235)) + (func (export "ᝠ") (result i32) (i32.const 236)) + (func (export "ᠠ") (result i32) (i32.const 237)) + (func (export "ᢇ") (result i32) (i32.const 238)) + (func (export "ᤠ") (result i32) (i32.const 239)) + (func (export "ᥣ") (result i32) (i32.const 240)) + (func (export "ᨕ") (result i32) (i32.const 241)) + (func (export "ᩋ") (result i32) (i32.const 242)) + (func (export "ᩡ") (result i32) (i32.const 243)) + (func (export "ᮃ") (result i32) (i32.const 244)) + (func (export "ᯀ") (result i32) (i32.const 245)) + (func (export "ᯁ") (result i32) (i32.const 246)) + (func (export "ᰣ") (result i32) (i32.const 247)) + (func (export "Ḁ") (result i32) (i32.const 248)) + (func (export "Ạ") (result i32) (i32.const 249)) + (func (export "Ả") (result i32) (i32.const 250)) + (func (export "Ấ") (result i32) (i32.const 251)) + (func (export "Ầ") (result i32) (i32.const 252)) + (func (export "Ẩ") (result i32) (i32.const 253)) + (func (export "Ẫ") (result i32) (i32.const 254)) + (func (export "Ậ") (result i32) (i32.const 255)) + (func (export "Ắ") (result i32) (i32.const 256)) + (func (export "Ằ") (result i32) (i32.const 257)) + (func (export "Ẳ") (result i32) (i32.const 258)) + (func (export "Ẵ") (result i32) (i32.const 259)) + (func (export "Ặ") (result i32) (i32.const 260)) + (func (export "あ") (result i32) (i32.const 261)) + (func (export "ア") (result i32) (i32.const 262)) + (func (export "ㄚ") (result i32) (i32.const 263)) + (func (export "ㅏ") (result i32) (i32.const 264)) + (func (export "㈎") (result i32) (i32.const 265)) + (func (export "㈏") (result i32) (i32.const 266)) + (func (export "㈐") (result i32) (i32.const 267)) + (func (export "㈑") (result i32) (i32.const 268)) + (func (export "㈒") (result i32) (i32.const 269)) + (func (export "㈓") (result i32) (i32.const 270)) + (func (export "㈔") (result i32) (i32.const 271)) + (func (export "㈕") (result i32) (i32.const 272)) + (func (export "㈖") (result i32) (i32.const 273)) + (func (export "㈗") (result i32) (i32.const 274)) + (func (export "㈘") (result i32) (i32.const 275)) + (func (export "㈙") (result i32) (i32.const 276)) + (func (export "㈚") (result i32) (i32.const 277)) + (func (export "㈛") (result i32) (i32.const 278)) + (func (export "㉮") (result i32) (i32.const 279)) + (func (export "㉯") (result i32) (i32.const 280)) + (func (export "㉰") (result i32) (i32.const 281)) + (func (export "㉱") (result i32) (i32.const 282)) + (func (export "㉲") (result i32) (i32.const 283)) + (func (export "㉳") (result i32) (i32.const 284)) + (func (export "㉴") (result i32) (i32.const 285)) + (func (export "㉵") (result i32) (i32.const 286)) + (func (export "㉶") (result i32) (i32.const 287)) + (func (export "㉷") (result i32) (i32.const 288)) + (func (export "㉸") (result i32) (i32.const 289)) + (func (export "㉹") (result i32) (i32.const 290)) + (func (export "㉺") (result i32) (i32.const 291)) + (func (export "㉻") (result i32) (i32.const 292)) + (func (export "㋐") (result i32) (i32.const 293)) + (func (export "ꀊ") (result i32) (i32.const 294)) + (func (export "ꓮ") (result i32) (i32.const 295)) + (func (export "ꕉ") (result i32) (i32.const 296)) + (func (export "ꚠ") (result i32) (i32.const 297)) + (func (export "ꠀ") (result i32) (i32.const 298)) + (func (export "ꠣ") (result i32) (i32.const 299)) + (func (export "ꡝ") (result i32) (i32.const 300)) + (func (export "ꢂ") (result i32) (i32.const 301)) + (func (export "꣪") (result i32) (i32.const 302)) + (func (export "ꤢ") (result i32) (i32.const 303)) + (func (export "ꥆ") (result i32) (i32.const 304)) + (func (export "ꦄ") (result i32) (i32.const 305)) + (func (export "ꨀ") (result i32) (i32.const 306)) + (func (export "ア") (result i32) (i32.const 307)) + (func (export "ᅡ") (result i32) (i32.const 308)) + (func (export "𐀀") (result i32) (i32.const 309)) + (func (export "𐊀") (result i32) (i32.const 310)) + (func (export "𐊠") (result i32) (i32.const 311)) + (func (export "𐌀") (result i32) (i32.const 312)) + (func (export "𐎠") (result i32) (i32.const 313)) + (func (export "𐒖") (result i32) (i32.const 314)) + (func (export "𐔀") (result i32) (i32.const 315)) + (func (export "𐝀") (result i32) (i32.const 316)) + (func (export "𐠀") (result i32) (i32.const 317)) + (func (export "𐤠") (result i32) (i32.const 318)) + (func (export "𐦀") (result i32) (i32.const 319)) + (func (export "𐦠") (result i32) (i32.const 320)) + (func (export "𐨀") (result i32) (i32.const 321)) + (func (export "𐬀") (result i32) (i32.const 322)) + (func (export "𐰀") (result i32) (i32.const 323)) + (func (export "𐰁") (result i32) (i32.const 324)) + (func (export "𐲀") (result i32) (i32.const 325)) + (func (export "𑀅") (result i32) (i32.const 326)) + (func (export "𑂃") (result i32) (i32.const 327)) + (func (export "𑄧") (result i32) (i32.const 328)) + (func (export "𑅐") (result i32) (i32.const 329)) + (func (export "𑆃") (result i32) (i32.const 330)) + (func (export "𑈀") (result i32) (i32.const 331)) + (func (export "𑊀") (result i32) (i32.const 332)) + (func (export "𑊰") (result i32) (i32.const 333)) + (func (export "𑌅") (result i32) (i32.const 334)) + (func (export "𑍰") (result i32) (i32.const 335)) + (func (export "𑐀") (result i32) (i32.const 336)) + (func (export "𑒁") (result i32) (i32.const 337)) + (func (export "𑖀") (result i32) (i32.const 338)) + (func (export "𑘀") (result i32) (i32.const 339)) + (func (export "𑚀") (result i32) (i32.const 340)) + (func (export "𑜒") (result i32) (i32.const 341)) + (func (export "𑜠") (result i32) (i32.const 342)) + (func (export "𑢡") (result i32) (i32.const 343)) + (func (export "𑫕") (result i32) (i32.const 344)) + (func (export "𑰀") (result i32) (i32.const 345)) + (func (export "𑲏") (result i32) (i32.const 346)) + (func (export "𑲯") (result i32) (i32.const 347)) + (func (export "𒀀") (result i32) (i32.const 348)) + (func (export "𖧕") (result i32) (i32.const 349)) + (func (export "𖩆") (result i32) (i32.const 350)) + (func (export "𖫧") (result i32) (i32.const 351)) + (func (export "𖽔") (result i32) (i32.const 352)) + (func (export "𛱁") (result i32) (i32.const 353)) + (func (export "𛱤") (result i32) (i32.const 354)) + (func (export "𞠣") (result i32) (i32.const 355)) + (func (export "🇦") (result i32) (i32.const 356)) + (func (export "Ɑ") (result i32) (i32.const 357)) + (func (export "Λ") (result i32) (i32.const 358)) + (func (export "Ɒ") (result i32) (i32.const 359)) + (func (export "ª") (result i32) (i32.const 360)) + (func (export "∀") (result i32) (i32.const 361)) + (func (export "₳") (result i32) (i32.const 362)) + (func (export "𐤀") (result i32) (i32.const 363)) + (func (export "Ⲁ") (result i32) (i32.const 364)) + (func (export "𐌰") (result i32) (i32.const 365)) + (func (export "Ά") (result i32) (i32.const 366)) + (func (export "Α") (result i32) (i32.const 367)) + (func (export "Ἀ") (result i32) (i32.const 368)) + (func (export "Ἁ") (result i32) (i32.const 369)) + (func (export "Ἂ") (result i32) (i32.const 370)) + (func (export "Ἃ") (result i32) (i32.const 371)) + (func (export "Ἄ") (result i32) (i32.const 372)) + (func (export "Ἅ") (result i32) (i32.const 373)) + (func (export "Ἆ") (result i32) (i32.const 374)) + (func (export "Ἇ") (result i32) (i32.const 375)) + (func (export "ᾈ") (result i32) (i32.const 376)) + (func (export "ᾉ") (result i32) (i32.const 377)) + (func (export "ᾊ") (result i32) (i32.const 378)) + (func (export "ᾋ") (result i32) (i32.const 379)) + (func (export "ᾌ") (result i32) (i32.const 380)) + (func (export "ᾍ") (result i32) (i32.const 381)) + (func (export "ᾎ") (result i32) (i32.const 382)) + (func (export "ᾏ") (result i32) (i32.const 383)) + (func (export "Ᾰ") (result i32) (i32.const 384)) + (func (export "Ᾱ") (result i32) (i32.const 385)) + (func (export "Ὰ") (result i32) (i32.const 386)) + (func (export "Ά") (result i32) (i32.const 387)) + (func (export "ᾼ") (result i32) (i32.const 388)) + (func (export "𝚨") (result i32) (i32.const 389)) + (func (export "𝛢") (result i32) (i32.const 390)) + (func (export "𝜜") (result i32) (i32.const 391)) + (func (export "𝝖") (result i32) (i32.const 392)) + (func (export "𝞐") (result i32) (i32.const 393)) + (func (export "⍶") (result i32) (i32.const 394)) + (func (export "⍺") (result i32) (i32.const 395)) + (func (export "⩜") (result i32) (i32.const 396)) + (func (export "ᗅ") (result i32) (i32.const 397)) + (func (export "Ꭺ") (result i32) (i32.const 398)) + + ;; Test unmatched "closing" and "opening" code points. + (func (export ")˺˼𔗏𝅴𝅶𝅸𝅺⁾₎❩❫⟯﴿︶﹚)⦆󠀩❳❵⟧⟩⟫⟭⦈⦊⦖⸣⸥︘︸︺︼︾﹀﹂﹄﹈﹜﹞]}」󠁝󠁽»’”›❯") (result i32) (i32.const 399)) + (func (export "(˹˻𔗎𝅳𝅵𝅷𝅹⁽₍❨❪⟮﴾︵﹙(⦅󠀨❲❴⟦⟨⟪⟬⦇⦉⦕⸢⸤︗︷︹︻︽︿﹁﹃﹇﹛﹝[{「󠁛󠁻«‘“‹❮") (result i32) (i32.const 400)) + (func (export "𝪋𝪤") (result i32) (i32.const 401)) + (func (export "𝪋") (result i32) (i32.const 402)) + + ;; Test that Unicode fraction normalization is not applied. + (func (export "½") (result i32) (i32.const 403)) + (func (export "1⁄2") (result i32) (i32.const 404)) + (func (export "1/2") (result i32) (i32.const 405)) + (func (export "୳") (result i32) (i32.const 406)) + (func (export "൴") (result i32) (i32.const 407)) + (func (export "⳽") (result i32) (i32.const 408)) + (func (export "꠱") (result i32) (i32.const 409)) + (func (export "𐅁") (result i32) (i32.const 410)) + (func (export "𐅵") (result i32) (i32.const 411)) + (func (export "𐅶") (result i32) (i32.const 412)) + (func (export "𐦽") (result i32) (i32.const 413)) + (func (export "𐹻") (result i32) (i32.const 414)) + + ;; Test a full-width quote. + (func (export """) (result i32) (i32.const 415)) + + ;; Test that different present and historical representations of the "delete" + ;; concept are distinct. + (func (export "\7f") (result i32) (i32.const 416)) + (func (export "\08") (result i32) (i32.const 417)) + (func (export "⌫") (result i32) (i32.const 418)) + (func (export "⌦") (result i32) (i32.const 419)) + (func (export "␈") (result i32) (i32.const 420)) + (func (export "␡") (result i32) (i32.const 421)) + (func (export "᷻") (result i32) (i32.const 422)) + (func (export "\0f") (result i32) (i32.const 423)) + (func (export "←") (result i32) (i32.const 424)) + (func (export "⌧") (result i32) (i32.const 425)) + (func (export "⍒") (result i32) (i32.const 426)) + (func (export "⍔") (result i32) (i32.const 427)) + (func (export "⍢") (result i32) (i32.const 428)) + (func (export "⍫") (result i32) (i32.const 429)) + + ;; Test that different representations of the "substitute" concept are + ;; distinct. (U+FFFD is covered above.) + (func (export "\1a") (result i32) (i32.const 430)) + (func (export "␦") (result i32) (i32.const 431)) + (func (export "␚") (result i32) (i32.const 432)) + (func (export "") (result i32) (i32.const 433)) + (func (export "?") (result i32) (i32.const 434)) + (func (export "¿") (result i32) (i32.const 435)) + (func (export "᥅") (result i32) (i32.const 436)) + (func (export ";") (result i32) (i32.const 437)) + (func (export "՞") (result i32) (i32.const 438)) + (func (export "؟") (result i32) (i32.const 439)) + (func (export "፧") (result i32) (i32.const 440)) + (func (export "⁇") (result i32) (i32.const 441)) + (func (export "⍰") (result i32) (i32.const 442)) + (func (export "❓") (result i32) (i32.const 443)) + (func (export "❔") (result i32) (i32.const 444)) + (func (export "⳺") (result i32) (i32.const 445)) + (func (export "⳻") (result i32) (i32.const 446)) + (func (export "⸮") (result i32) (i32.const 447)) + (func (export "㉄") (result i32) (i32.const 448)) + (func (export "꘏") (result i32) (i32.const 449)) + (func (export "꛷") (result i32) (i32.const 450)) + (func (export "︖") (result i32) (i32.const 451)) + (func (export "﹖") (result i32) (i32.const 452)) + (func (export "?") (result i32) (i32.const 453)) + (func (export "𑅃") (result i32) (i32.const 454)) + (func (export "𞥟") (result i32) (i32.const 455)) + (func (export "󠀿") (result i32) (i32.const 456)) + (func (export "𖡄") (result i32) (i32.const 457)) + (func (export "⯑") (result i32) (i32.const 458)) + + ;; Test that different present and historical representations of the + ;; "paragraph" concept are distinct. (U+2029 is covered above). + (func (export "¶") (result i32) (i32.const 459)) + (func (export "⁋") (result i32) (i32.const 460)) + (func (export "܀") (result i32) (i32.const 461)) + (func (export "჻") (result i32) (i32.const 462)) + (func (export "፨") (result i32) (i32.const 463)) + (func (export "〷") (result i32) (i32.const 464)) + (func (export "❡") (result i32) (i32.const 465)) + (func (export "⸏") (result i32) (i32.const 466)) + (func (export "⸐") (result i32) (i32.const 467)) + (func (export "⸑") (result i32) (i32.const 468)) + (func (export "⸎") (result i32) (i32.const 469)) + (func (export "\14") (result i32) (i32.const 470)) ;; ¶ in CP437 + (func (export "☙") (result i32) (i32.const 471)) + (func (export "⸿") (result i32) (i32.const 472)) + (func (export "〇") (result i32) (i32.const 473)) + (func (export "๛") (result i32) (i32.const 474)) + + ;; Test an unusual character. + (func (export "ꙮ") (result i32) (i32.const 475)) + + ;; Test the three characters whose normalization forms under NFC, NFD, NFKC, + ;; and NFKD are all different. + ;; http://unicode.org/faq/normalization.html#6 + (func (export "ϓ") (result i32) (i32.const 476)) + (func (export "ϔ") (result i32) (i32.const 477)) + (func (export "ẛ") (result i32) (i32.const 478)) +) + +(assert_return (invoke "") (i32.const 0)) +(assert_return (invoke "0") (i32.const 1)) +(assert_return (invoke "-0") (i32.const 2)) +(assert_return (invoke "_") (i32.const 3)) +(assert_return (invoke "$") (i32.const 4)) +(assert_return (invoke "@") (i32.const 5)) +(assert_return (invoke "~!@#$%^&*()_+`-={}|[]\\:\";'<>?,./ ") (i32.const 6)) +(assert_return (invoke "NaN") (i32.const 7)) +(assert_return (invoke "Infinity") (i32.const 8)) +(assert_return (invoke "if") (i32.const 9)) +(assert_return (invoke "malloc") (i32.const 10)) +(assert_return (invoke "_malloc") (i32.const 11)) +(assert_return (invoke "__malloc") (i32.const 12)) +(assert_return (invoke "a") (i32.const 13)) +(assert_return (invoke "A") (i32.const 14)) +(assert_return (invoke "") (i32.const 15)) +(assert_return (invoke "Å") (i32.const 16)) +(assert_return (invoke "Å") (i32.const 17)) +(assert_return (invoke "Å") (i32.const 18)) +(assert_return (invoke "ffi") (i32.const 19)) +(assert_return (invoke "ffi") (i32.const 20)) +(assert_return (invoke "ffi") (i32.const 21)) +(assert_return (invoke "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f") (i32.const 22)) +(assert_return (invoke "\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f") (i32.const 23)) +(assert_return (invoke " \7f") (i32.const 24)) +(assert_return (invoke "\c2\80\c2\81\c2\82\c2\83\c2\84\c2\85\c2\86\c2\87\c2\88\c2\89\c2\8a\c2\8b\c2\8c\c2\8d\c2\8e\c2\8f") (i32.const 25)) +(assert_return (invoke "\c2\90\c2\91\c2\92\c2\93\c2\94\c2\95\c2\96\c2\97\c2\98\c2\99\c2\9a\c2\9b\c2\9c\c2\9d\c2\9e\c2\9f") (i32.const 26)) +(assert_return (invoke "\ef\bf\b0\ef\bf\b1\ef\bf\b2\ef\bf\b3\ef\bf\b4\ef\bf\b5\ef\bf\b6\ef\bf\b7") (i32.const 27)) +(assert_return (invoke "\ef\bf\b8\ef\bf\b9\ef\bf\ba\ef\bf\bb\ef\bf\bc\ef\bf\bd\ef\bf\be\ef\bf\bf") (i32.const 28)) +(assert_return (invoke "␀␁␂␃␄␅␆␇␈␉␊␋␌␍␎␏") (i32.const 29)) +(assert_return (invoke "␐␑␒␓␔␕␖␗␘␙␚␛␜␝␞␟") (i32.const 30)) +(assert_return (invoke "␠␡") (i32.const 31)) +(assert_return (invoke "￰￱￲￳￴￵￶￷￸�") (i32.const 32)) +(assert_return (invoke "‍") (i32.const 33)) +(assert_return (invoke "‌") (i32.const 34)) +(assert_return (invoke "͏") (i32.const 35)) +(assert_return (invoke "⁠") (i32.const 36)) +(assert_return (invoke "⵿") (i32.const 37)) +(assert_return (invoke "𑁿") (i32.const 38)) +(assert_return (invoke "᠎") (i32.const 39)) +(assert_return (invoke "￯​ ­⁠ ‮‭") (i32.const 40)) +(assert_return (invoke "‎‏‑

‪‫‬ ⁦⁧⁨⁩") (i32.const 41)) +(assert_return (invoke "") (i32.const 42)) +(assert_return (invoke "⁡⁢⁣⁤") (i32.const 43)) +(assert_return (invoke "𐀀󟿿􏿿") (i32.const 44)) +(assert_return (invoke "Z̴͇̫̥̪͓͈͔͎̗̞̺̯̱̞̙̱̜̖̠̏͆̆͛͌͘͞ḁ̶̰̳̭͙̲̱̹̝͎̼͗ͨ̎̄̆͗̿̀́͟͡l̶̷͉̩̹̫̝͖̙̲̼͇͚͍̮͎̥̞̈́͊͗ͦ̈́ͫ̇́̚ͅͅg̶͕͔͚̩̓̐̅ͮ̔̐̎̂̏̾͊̍͋͊ͧ́̆ͦ͞o̡͋̔͐ͪͩ͏̢̧̫̙̤̮͖͙͓̺̜̩̼̘̠́") (i32.const 45)) +(assert_return (invoke "ᅟᅠㅤᅠ") (i32.const 46)) +(assert_return (invoke "︀") (i32.const 47)) +(assert_return (invoke "︄") (i32.const 48)) +(assert_return (invoke "󠄀") (i32.const 49)) +(assert_return (invoke "󠇯") (i32.const 50)) +(assert_return (invoke "̈") (i32.const 51)) +(assert_return (invoke "\0a") (i32.const 52)) +(assert_return (invoke "␤") (i32.const 53)) +(assert_return (invoke "
") (i32.const 54)) +(assert_return (invoke "\0d") (i32.const 55)) +(assert_return (invoke "\0d\0a") (i32.const 56)) +(assert_return (invoke "\0a\0d") (i32.const 57)) +(assert_return (invoke "\1e") (i32.const 58)) +(assert_return (invoke "\0b") (i32.const 59)) +(assert_return (invoke "\0c") (i32.const 60)) +(assert_return (invoke "\c2\85") (i32.const 61)) +(assert_return (invoke "
") (i32.const 62)) +(assert_return (invoke "…") (i32.const 63)) +(assert_return (invoke "⏎") (i32.const 64)) +(assert_return (invoke "\c2\8b") (i32.const 65)) +(assert_return (invoke "\c2\8c") (i32.const 66)) +(assert_return (invoke "\c2\8d") (i32.const 67)) +(assert_return (invoke "↵") (i32.const 68)) +(assert_return (invoke "↩") (i32.const 69)) +(assert_return (invoke "⌤") (i32.const 70)) +(assert_return (invoke "⤶") (i32.const 71)) +(assert_return (invoke "↲") (i32.const 72)) +(assert_return (invoke "⮨") (i32.const 73)) +(assert_return (invoke "⮰") (i32.const 74)) +(assert_return (invoke "�") (i32.const 75)) +(assert_return (invoke "\ef\b7\90") (i32.const 76)) +(assert_return (invoke "\ef\b7\91") (i32.const 77)) +(assert_return (invoke "\ef\b7\92") (i32.const 78)) +(assert_return (invoke "\ef\b7\93") (i32.const 79)) +(assert_return (invoke "\ef\b7\94") (i32.const 80)) +(assert_return (invoke "\ef\b7\95") (i32.const 81)) +(assert_return (invoke "\ef\b7\96") (i32.const 82)) +(assert_return (invoke "\ef\b7\97") (i32.const 83)) +(assert_return (invoke "\ef\b7\98") (i32.const 84)) +(assert_return (invoke "\ef\b7\99") (i32.const 85)) +(assert_return (invoke "\ef\b7\9a") (i32.const 86)) +(assert_return (invoke "\ef\b7\9b") (i32.const 87)) +(assert_return (invoke "\ef\b7\9c") (i32.const 88)) +(assert_return (invoke "\ef\b7\9d") (i32.const 89)) +(assert_return (invoke "\ef\b7\9e") (i32.const 90)) +(assert_return (invoke "\ef\b7\9f") (i32.const 91)) +(assert_return (invoke "\ef\b7\a0") (i32.const 92)) +(assert_return (invoke "\ef\b7\a1") (i32.const 93)) +(assert_return (invoke "\ef\b7\a2") (i32.const 94)) +(assert_return (invoke "\ef\b7\a3") (i32.const 95)) +(assert_return (invoke "\ef\b7\a4") (i32.const 96)) +(assert_return (invoke "\ef\b7\a5") (i32.const 97)) +(assert_return (invoke "\ef\b7\a6") (i32.const 98)) +(assert_return (invoke "\ef\b7\a7") (i32.const 99)) +(assert_return (invoke "\ef\b7\a8") (i32.const 100)) +(assert_return (invoke "\ef\b7\a9") (i32.const 101)) +(assert_return (invoke "\ef\b7\aa") (i32.const 102)) +(assert_return (invoke "\ef\b7\ab") (i32.const 103)) +(assert_return (invoke "\ef\b7\ac") (i32.const 104)) +(assert_return (invoke "\ef\b7\ad") (i32.const 105)) +(assert_return (invoke "\ef\b7\ae") (i32.const 106)) +(assert_return (invoke "\ef\b7\af") (i32.const 107)) +(assert_return (invoke "\ef\bf\be") (i32.const 108)) +(assert_return (invoke "\ef\bf\bf") (i32.const 109)) +(assert_return (invoke "\f0\9f\bf\be") (i32.const 110)) +(assert_return (invoke "\f0\9f\bf\bf") (i32.const 111)) +(assert_return (invoke "\f0\af\bf\be") (i32.const 112)) +(assert_return (invoke "\f0\af\bf\bf") (i32.const 113)) +(assert_return (invoke "\f0\bf\bf\be") (i32.const 114)) +(assert_return (invoke "\f0\bf\bf\bf") (i32.const 115)) +(assert_return (invoke "\f1\8f\bf\be") (i32.const 116)) +(assert_return (invoke "\f1\8f\bf\bf") (i32.const 117)) +(assert_return (invoke "\f1\9f\bf\be") (i32.const 118)) +(assert_return (invoke "\f1\9f\bf\bf") (i32.const 119)) +(assert_return (invoke "\f1\af\bf\be") (i32.const 120)) +(assert_return (invoke "\f1\af\bf\bf") (i32.const 121)) +(assert_return (invoke "\f1\bf\bf\be") (i32.const 122)) +(assert_return (invoke "\f1\bf\bf\bf") (i32.const 123)) +(assert_return (invoke "\f2\8f\bf\be") (i32.const 124)) +(assert_return (invoke "\f2\8f\bf\bf") (i32.const 125)) +(assert_return (invoke "\f2\9f\bf\be") (i32.const 126)) +(assert_return (invoke "\f2\9f\bf\bf") (i32.const 127)) +(assert_return (invoke "\f2\af\bf\be") (i32.const 128)) +(assert_return (invoke "\f2\af\bf\bf") (i32.const 129)) +(assert_return (invoke "\f2\bf\bf\be") (i32.const 130)) +(assert_return (invoke "\f2\bf\bf\bf") (i32.const 131)) +(assert_return (invoke "\f3\8f\bf\be") (i32.const 132)) +(assert_return (invoke "\f3\8f\bf\bf") (i32.const 133)) +(assert_return (invoke "\f3\9f\bf\be") (i32.const 134)) +(assert_return (invoke "\f3\9f\bf\bf") (i32.const 135)) +(assert_return (invoke "\f3\af\bf\be") (i32.const 136)) +(assert_return (invoke "\f3\af\bf\bf") (i32.const 137)) +(assert_return (invoke "\f3\bf\bf\be") (i32.const 138)) +(assert_return (invoke "\f3\bf\bf\bf") (i32.const 139)) +(assert_return (invoke "\f4\8f\bf\be") (i32.const 140)) +(assert_return (invoke "\f4\8f\bf\bf") (i32.const 141)) +(assert_return (invoke "̈‽̈̉") (i32.const 142)) +(assert_return (invoke "abc") (i32.const 143)) +(assert_return (invoke "‭abc") (i32.const 144)) +(assert_return (invoke "‮cba") (i32.const 145)) +(assert_return (invoke "‭abc‮") (i32.const 146)) +(assert_return (invoke "‮cba‭") (i32.const 147)) +(assert_return (invoke "𝑨") (i32.const 148)) +(assert_return (invoke "𝐴") (i32.const 149)) +(assert_return (invoke "𝘈") (i32.const 150)) +(assert_return (invoke "𝘼") (i32.const 151)) +(assert_return (invoke "𝐀") (i32.const 152)) +(assert_return (invoke "𝓐") (i32.const 153)) +(assert_return (invoke "𝕬") (i32.const 154)) +(assert_return (invoke "𝗔") (i32.const 155)) +(assert_return (invoke "𝒜") (i32.const 156)) +(assert_return (invoke "𝔄") (i32.const 157)) +(assert_return (invoke "𝔸") (i32.const 158)) +(assert_return (invoke "𝖠") (i32.const 159)) +(assert_return (invoke "𝙰") (i32.const 160)) +(assert_return (invoke "ᴀ") (i32.const 161)) +(assert_return (invoke "ᴬ") (i32.const 162)) +(assert_return (invoke "Ⓐ") (i32.const 163)) +(assert_return (invoke "A") (i32.const 164)) +(assert_return (invoke "🄐") (i32.const 165)) +(assert_return (invoke "🄰") (i32.const 166)) +(assert_return (invoke "󠁁") (i32.const 167)) +(assert_return (invoke "U+0041") (i32.const 168)) +(assert_return (invoke "A​") (i32.const 169)) +(assert_return (invoke "А") (i32.const 170)) +(assert_return (invoke "Ꙗ") (i32.const 171)) +(assert_return (invoke "ⷼ") (i32.const 172)) +(assert_return (invoke "ⷶ") (i32.const 173)) +(assert_return (invoke "Ɐ") (i32.const 174)) +(assert_return (invoke "🅐") (i32.const 175)) +(assert_return (invoke "🅰") (i32.const 176)) +(assert_return (invoke "Ⱝ") (i32.const 177)) +(assert_return (invoke "𐐂") (i32.const 178)) +(assert_return (invoke "𐐈") (i32.const 179)) +(assert_return (invoke "𐒰") (i32.const 180)) +(assert_return (invoke "À") (i32.const 181)) +(assert_return (invoke "Á") (i32.const 182)) +(assert_return (invoke "Â") (i32.const 183)) +(assert_return (invoke "Ã") (i32.const 184)) +(assert_return (invoke "Ä") (i32.const 185)) +(assert_return (invoke "Ā") (i32.const 186)) +(assert_return (invoke "Ă") (i32.const 187)) +(assert_return (invoke "Ą") (i32.const 188)) +(assert_return (invoke "Ǎ") (i32.const 189)) +(assert_return (invoke "Ǟ") (i32.const 190)) +(assert_return (invoke "Ǡ") (i32.const 191)) +(assert_return (invoke "Ǻ") (i32.const 192)) +(assert_return (invoke "Ȁ") (i32.const 193)) +(assert_return (invoke "Ȃ") (i32.const 194)) +(assert_return (invoke "Ȧ") (i32.const 195)) +(assert_return (invoke "Ⱥ") (i32.const 196)) +(assert_return (invoke "Ӑ") (i32.const 197)) +(assert_return (invoke "Ӓ") (i32.const 198)) +(assert_return (invoke "ߊ") (i32.const 199)) +(assert_return (invoke "ࠡ") (i32.const 200)) +(assert_return (invoke "ࠢ") (i32.const 201)) +(assert_return (invoke "ࠣ") (i32.const 202)) +(assert_return (invoke "ࠤ") (i32.const 203)) +(assert_return (invoke "ࠥ") (i32.const 204)) +(assert_return (invoke "ऄ") (i32.const 205)) +(assert_return (invoke "अ") (i32.const 206)) +(assert_return (invoke "ॲ") (i32.const 207)) +(assert_return (invoke "অ") (i32.const 208)) +(assert_return (invoke "ਅ") (i32.const 209)) +(assert_return (invoke "અ") (i32.const 210)) +(assert_return (invoke "ଅ") (i32.const 211)) +(assert_return (invoke "அ") (i32.const 212)) +(assert_return (invoke "అ") (i32.const 213)) +(assert_return (invoke "ಅ") (i32.const 214)) +(assert_return (invoke "അ") (i32.const 215)) +(assert_return (invoke "ะ") (i32.const 216)) +(assert_return (invoke "ະ") (i32.const 217)) +(assert_return (invoke "༁") (i32.const 218)) +(assert_return (invoke "ཨ") (i32.const 219)) +(assert_return (invoke "ྸ") (i32.const 220)) +(assert_return (invoke "အ") (i32.const 221)) +(assert_return (invoke "ဢ") (i32.const 222)) +(assert_return (invoke "ႜ") (i32.const 223)) +(assert_return (invoke "ᅡ") (i32.const 224)) +(assert_return (invoke "አ") (i32.const 225)) +(assert_return (invoke "ዐ") (i32.const 226)) +(assert_return (invoke "Ꭰ") (i32.const 227)) +(assert_return (invoke "ᐊ") (i32.const 228)) +(assert_return (invoke "ᖳ") (i32.const 229)) +(assert_return (invoke "ᚨ") (i32.const 230)) +(assert_return (invoke "ᚪ") (i32.const 231)) +(assert_return (invoke "ᛆ") (i32.const 232)) +(assert_return (invoke "ᜀ") (i32.const 233)) +(assert_return (invoke "ᜠ") (i32.const 234)) +(assert_return (invoke "ᝀ") (i32.const 235)) +(assert_return (invoke "ᝠ") (i32.const 236)) +(assert_return (invoke "ᠠ") (i32.const 237)) +(assert_return (invoke "ᢇ") (i32.const 238)) +(assert_return (invoke "ᤠ") (i32.const 239)) +(assert_return (invoke "ᥣ") (i32.const 240)) +(assert_return (invoke "ᨕ") (i32.const 241)) +(assert_return (invoke "ᩋ") (i32.const 242)) +(assert_return (invoke "ᩡ") (i32.const 243)) +(assert_return (invoke "ᮃ") (i32.const 244)) +(assert_return (invoke "ᯀ") (i32.const 245)) +(assert_return (invoke "ᯁ") (i32.const 246)) +(assert_return (invoke "ᰣ") (i32.const 247)) +(assert_return (invoke "Ḁ") (i32.const 248)) +(assert_return (invoke "Ạ") (i32.const 249)) +(assert_return (invoke "Ả") (i32.const 250)) +(assert_return (invoke "Ấ") (i32.const 251)) +(assert_return (invoke "Ầ") (i32.const 252)) +(assert_return (invoke "Ẩ") (i32.const 253)) +(assert_return (invoke "Ẫ") (i32.const 254)) +(assert_return (invoke "Ậ") (i32.const 255)) +(assert_return (invoke "Ắ") (i32.const 256)) +(assert_return (invoke "Ằ") (i32.const 257)) +(assert_return (invoke "Ẳ") (i32.const 258)) +(assert_return (invoke "Ẵ") (i32.const 259)) +(assert_return (invoke "Ặ") (i32.const 260)) +(assert_return (invoke "あ") (i32.const 261)) +(assert_return (invoke "ア") (i32.const 262)) +(assert_return (invoke "ㄚ") (i32.const 263)) +(assert_return (invoke "ㅏ") (i32.const 264)) +(assert_return (invoke "㈎") (i32.const 265)) +(assert_return (invoke "㈏") (i32.const 266)) +(assert_return (invoke "㈐") (i32.const 267)) +(assert_return (invoke "㈑") (i32.const 268)) +(assert_return (invoke "㈒") (i32.const 269)) +(assert_return (invoke "㈓") (i32.const 270)) +(assert_return (invoke "㈔") (i32.const 271)) +(assert_return (invoke "㈕") (i32.const 272)) +(assert_return (invoke "㈖") (i32.const 273)) +(assert_return (invoke "㈗") (i32.const 274)) +(assert_return (invoke "㈘") (i32.const 275)) +(assert_return (invoke "㈙") (i32.const 276)) +(assert_return (invoke "㈚") (i32.const 277)) +(assert_return (invoke "㈛") (i32.const 278)) +(assert_return (invoke "㉮") (i32.const 279)) +(assert_return (invoke "㉯") (i32.const 280)) +(assert_return (invoke "㉰") (i32.const 281)) +(assert_return (invoke "㉱") (i32.const 282)) +(assert_return (invoke "㉲") (i32.const 283)) +(assert_return (invoke "㉳") (i32.const 284)) +(assert_return (invoke "㉴") (i32.const 285)) +(assert_return (invoke "㉵") (i32.const 286)) +(assert_return (invoke "㉶") (i32.const 287)) +(assert_return (invoke "㉷") (i32.const 288)) +(assert_return (invoke "㉸") (i32.const 289)) +(assert_return (invoke "㉹") (i32.const 290)) +(assert_return (invoke "㉺") (i32.const 291)) +(assert_return (invoke "㉻") (i32.const 292)) +(assert_return (invoke "㋐") (i32.const 293)) +(assert_return (invoke "ꀊ") (i32.const 294)) +(assert_return (invoke "ꓮ") (i32.const 295)) +(assert_return (invoke "ꕉ") (i32.const 296)) +(assert_return (invoke "ꚠ") (i32.const 297)) +(assert_return (invoke "ꠀ") (i32.const 298)) +(assert_return (invoke "ꠣ") (i32.const 299)) +(assert_return (invoke "ꡝ") (i32.const 300)) +(assert_return (invoke "ꢂ") (i32.const 301)) +(assert_return (invoke "꣪") (i32.const 302)) +(assert_return (invoke "ꤢ") (i32.const 303)) +(assert_return (invoke "ꥆ") (i32.const 304)) +(assert_return (invoke "ꦄ") (i32.const 305)) +(assert_return (invoke "ꨀ") (i32.const 306)) +(assert_return (invoke "ア") (i32.const 307)) +(assert_return (invoke "ᅡ") (i32.const 308)) +(assert_return (invoke "𐀀") (i32.const 309)) +(assert_return (invoke "𐊀") (i32.const 310)) +(assert_return (invoke "𐊠") (i32.const 311)) +(assert_return (invoke "𐌀") (i32.const 312)) +(assert_return (invoke "𐎠") (i32.const 313)) +(assert_return (invoke "𐒖") (i32.const 314)) +(assert_return (invoke "𐔀") (i32.const 315)) +(assert_return (invoke "𐝀") (i32.const 316)) +(assert_return (invoke "𐠀") (i32.const 317)) +(assert_return (invoke "𐤠") (i32.const 318)) +(assert_return (invoke "𐦀") (i32.const 319)) +(assert_return (invoke "𐦠") (i32.const 320)) +(assert_return (invoke "𐨀") (i32.const 321)) +(assert_return (invoke "𐬀") (i32.const 322)) +(assert_return (invoke "𐰀") (i32.const 323)) +(assert_return (invoke "𐰁") (i32.const 324)) +(assert_return (invoke "𐲀") (i32.const 325)) +(assert_return (invoke "𑀅") (i32.const 326)) +(assert_return (invoke "𑂃") (i32.const 327)) +(assert_return (invoke "𑄧") (i32.const 328)) +(assert_return (invoke "𑅐") (i32.const 329)) +(assert_return (invoke "𑆃") (i32.const 330)) +(assert_return (invoke "𑈀") (i32.const 331)) +(assert_return (invoke "𑊀") (i32.const 332)) +(assert_return (invoke "𑊰") (i32.const 333)) +(assert_return (invoke "𑌅") (i32.const 334)) +(assert_return (invoke "𑍰") (i32.const 335)) +(assert_return (invoke "𑐀") (i32.const 336)) +(assert_return (invoke "𑒁") (i32.const 337)) +(assert_return (invoke "𑖀") (i32.const 338)) +(assert_return (invoke "𑘀") (i32.const 339)) +(assert_return (invoke "𑚀") (i32.const 340)) +(assert_return (invoke "𑜒") (i32.const 341)) +(assert_return (invoke "𑜠") (i32.const 342)) +(assert_return (invoke "𑢡") (i32.const 343)) +(assert_return (invoke "𑫕") (i32.const 344)) +(assert_return (invoke "𑰀") (i32.const 345)) +(assert_return (invoke "𑲏") (i32.const 346)) +(assert_return (invoke "𑲯") (i32.const 347)) +(assert_return (invoke "𒀀") (i32.const 348)) +(assert_return (invoke "𖧕") (i32.const 349)) +(assert_return (invoke "𖩆") (i32.const 350)) +(assert_return (invoke "𖫧") (i32.const 351)) +(assert_return (invoke "𖽔") (i32.const 352)) +(assert_return (invoke "𛱁") (i32.const 353)) +(assert_return (invoke "𛱤") (i32.const 354)) +(assert_return (invoke "𞠣") (i32.const 355)) +(assert_return (invoke "🇦") (i32.const 356)) +(assert_return (invoke "Ɑ") (i32.const 357)) +(assert_return (invoke "Λ") (i32.const 358)) +(assert_return (invoke "Ɒ") (i32.const 359)) +(assert_return (invoke "ª") (i32.const 360)) +(assert_return (invoke "∀") (i32.const 361)) +(assert_return (invoke "₳") (i32.const 362)) +(assert_return (invoke "𐤀") (i32.const 363)) +(assert_return (invoke "Ⲁ") (i32.const 364)) +(assert_return (invoke "𐌰") (i32.const 365)) +(assert_return (invoke "Ά") (i32.const 366)) +(assert_return (invoke "Α") (i32.const 367)) +(assert_return (invoke "Ἀ") (i32.const 368)) +(assert_return (invoke "Ἁ") (i32.const 369)) +(assert_return (invoke "Ἂ") (i32.const 370)) +(assert_return (invoke "Ἃ") (i32.const 371)) +(assert_return (invoke "Ἄ") (i32.const 372)) +(assert_return (invoke "Ἅ") (i32.const 373)) +(assert_return (invoke "Ἆ") (i32.const 374)) +(assert_return (invoke "Ἇ") (i32.const 375)) +(assert_return (invoke "ᾈ") (i32.const 376)) +(assert_return (invoke "ᾉ") (i32.const 377)) +(assert_return (invoke "ᾊ") (i32.const 378)) +(assert_return (invoke "ᾋ") (i32.const 379)) +(assert_return (invoke "ᾌ") (i32.const 380)) +(assert_return (invoke "ᾍ") (i32.const 381)) +(assert_return (invoke "ᾎ") (i32.const 382)) +(assert_return (invoke "ᾏ") (i32.const 383)) +(assert_return (invoke "Ᾰ") (i32.const 384)) +(assert_return (invoke "Ᾱ") (i32.const 385)) +(assert_return (invoke "Ὰ") (i32.const 386)) +(assert_return (invoke "Ά") (i32.const 387)) +(assert_return (invoke "ᾼ") (i32.const 388)) +(assert_return (invoke "𝚨") (i32.const 389)) +(assert_return (invoke "𝛢") (i32.const 390)) +(assert_return (invoke "𝜜") (i32.const 391)) +(assert_return (invoke "𝝖") (i32.const 392)) +(assert_return (invoke "𝞐") (i32.const 393)) +(assert_return (invoke "⍶") (i32.const 394)) +(assert_return (invoke "⍺") (i32.const 395)) +(assert_return (invoke "⩜") (i32.const 396)) +(assert_return (invoke "ᗅ") (i32.const 397)) +(assert_return (invoke "Ꭺ") (i32.const 398)) +(assert_return (invoke ")˺˼𔗏𝅴𝅶𝅸𝅺⁾₎❩❫⟯﴿︶﹚)⦆󠀩❳❵⟧⟩⟫⟭⦈⦊⦖⸣⸥︘︸︺︼︾﹀﹂﹄﹈﹜﹞]}」󠁝󠁽»’”›❯") (i32.const 399)) +(assert_return (invoke "(˹˻𔗎𝅳𝅵𝅷𝅹⁽₍❨❪⟮﴾︵﹙(⦅󠀨❲❴⟦⟨⟪⟬⦇⦉⦕⸢⸤︗︷︹︻︽︿﹁﹃﹇﹛﹝[{「󠁛󠁻«‘“‹❮") (i32.const 400)) +(assert_return (invoke "𝪋𝪤") (i32.const 401)) +(assert_return (invoke "𝪋") (i32.const 402)) +(assert_return (invoke "½") (i32.const 403)) +(assert_return (invoke "1⁄2") (i32.const 404)) +(assert_return (invoke "1/2") (i32.const 405)) +(assert_return (invoke "୳") (i32.const 406)) +(assert_return (invoke "൴") (i32.const 407)) +(assert_return (invoke "⳽") (i32.const 408)) +(assert_return (invoke "꠱") (i32.const 409)) +(assert_return (invoke "𐅁") (i32.const 410)) +(assert_return (invoke "𐅵") (i32.const 411)) +(assert_return (invoke "𐅶") (i32.const 412)) +(assert_return (invoke "𐦽") (i32.const 413)) +(assert_return (invoke "𐹻") (i32.const 414)) +(assert_return (invoke """) (i32.const 415)) +(assert_return (invoke "\7f") (i32.const 416)) +(assert_return (invoke "\08") (i32.const 417)) +(assert_return (invoke "⌫") (i32.const 418)) +(assert_return (invoke "⌦") (i32.const 419)) +(assert_return (invoke "␈") (i32.const 420)) +(assert_return (invoke "␡") (i32.const 421)) +(assert_return (invoke "᷻") (i32.const 422)) +(assert_return (invoke "\0f") (i32.const 423)) +(assert_return (invoke "←") (i32.const 424)) +(assert_return (invoke "⌧") (i32.const 425)) +(assert_return (invoke "⍒") (i32.const 426)) +(assert_return (invoke "⍔") (i32.const 427)) +(assert_return (invoke "⍢") (i32.const 428)) +(assert_return (invoke "⍫") (i32.const 429)) +(assert_return (invoke "\1a") (i32.const 430)) +(assert_return (invoke "␦") (i32.const 431)) +(assert_return (invoke "␚") (i32.const 432)) +(assert_return (invoke "") (i32.const 433)) +(assert_return (invoke "?") (i32.const 434)) +(assert_return (invoke "¿") (i32.const 435)) +(assert_return (invoke "᥅") (i32.const 436)) +(assert_return (invoke ";") (i32.const 437)) +(assert_return (invoke "՞") (i32.const 438)) +(assert_return (invoke "؟") (i32.const 439)) +(assert_return (invoke "፧") (i32.const 440)) +(assert_return (invoke "⁇") (i32.const 441)) +(assert_return (invoke "⍰") (i32.const 442)) +(assert_return (invoke "❓") (i32.const 443)) +(assert_return (invoke "❔") (i32.const 444)) +(assert_return (invoke "⳺") (i32.const 445)) +(assert_return (invoke "⳻") (i32.const 446)) +(assert_return (invoke "⸮") (i32.const 447)) +(assert_return (invoke "㉄") (i32.const 448)) +(assert_return (invoke "꘏") (i32.const 449)) +(assert_return (invoke "꛷") (i32.const 450)) +(assert_return (invoke "︖") (i32.const 451)) +(assert_return (invoke "﹖") (i32.const 452)) +(assert_return (invoke "?") (i32.const 453)) +(assert_return (invoke "𑅃") (i32.const 454)) +(assert_return (invoke "𞥟") (i32.const 455)) +(assert_return (invoke "󠀿") (i32.const 456)) +(assert_return (invoke "𖡄") (i32.const 457)) +(assert_return (invoke "⯑") (i32.const 458)) +(assert_return (invoke "¶") (i32.const 459)) +(assert_return (invoke "⁋") (i32.const 460)) +(assert_return (invoke "܀") (i32.const 461)) +(assert_return (invoke "჻") (i32.const 462)) +(assert_return (invoke "፨") (i32.const 463)) +(assert_return (invoke "〷") (i32.const 464)) +(assert_return (invoke "❡") (i32.const 465)) +(assert_return (invoke "⸏") (i32.const 466)) +(assert_return (invoke "⸐") (i32.const 467)) +(assert_return (invoke "⸑") (i32.const 468)) +(assert_return (invoke "⸎") (i32.const 469)) +(assert_return (invoke "\14") (i32.const 470)) +(assert_return (invoke "☙") (i32.const 471)) +(assert_return (invoke "⸿") (i32.const 472)) +(assert_return (invoke "〇") (i32.const 473)) +(assert_return (invoke "๛") (i32.const 474)) +(assert_return (invoke "ꙮ") (i32.const 475)) +(assert_return (invoke "ϓ") (i32.const 476)) +(assert_return (invoke "ϔ") (i32.const 477)) +(assert_return (invoke "ẛ") (i32.const 478)) + +(module + ;; Test that we can use indices instead of names to reference imports, + ;; exports, functions and parameters. + (import "spectest" "print_i32" (func (param i32))) + (func (import "spectest" "print_i32") (param i32)) + (func (param i32) (param i32) + (call 0 (local.get 0)) + (call 1 (local.get 1)) + ) + (export "print32" (func 2)) +) + +(assert_return (invoke "print32" (i32.const 42) (i32.const 123))) diff --git a/runtime/unc-vm/tests/wast/spec/nop.wast b/runtime/unc-vm/tests/wast/spec/nop.wast new file mode 100644 index 000000000..e8fe2de4b --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/nop.wast @@ -0,0 +1,426 @@ +;; Test `nop` operator. + +(module + ;; Auxiliary definitions + (func $dummy) + (func $3-ary (param i32 i32 i32) (result i32) + local.get 0 local.get 1 local.get 2 i32.sub i32.add + ) + (memory 1) + + (func (export "as-func-first") (result i32) + (nop) (i32.const 1) + ) + (func (export "as-func-mid") (result i32) + (call $dummy) (nop) (i32.const 2) + ) + (func (export "as-func-last") (result i32) + (call $dummy) (i32.const 3) (nop) + ) + (func (export "as-func-everywhere") (result i32) + (nop) (nop) (call $dummy) (nop) (i32.const 4) (nop) (nop) + ) + + (func (export "as-drop-first") (param i32) + (nop) (local.get 0) (drop) + ) + (func (export "as-drop-last") (param i32) + (local.get 0) (nop) (drop) + ) + (func (export "as-drop-everywhere") (param i32) + (nop) (nop) (local.get 0) (nop) (nop) (drop) + ) + + (func (export "as-select-first") (param i32) (result i32) + (nop) (local.get 0) (local.get 0) (local.get 0) (select) + ) + (func (export "as-select-mid1") (param i32) (result i32) + (local.get 0) (nop) (local.get 0) (local.get 0) (select) + ) + (func (export "as-select-mid2") (param i32) (result i32) + (local.get 0) (local.get 0) (nop) (local.get 0) (select) + ) + (func (export "as-select-last") (param i32) (result i32) + (local.get 0) (local.get 0) (local.get 0) (nop) (select) + ) + (func (export "as-select-everywhere") (param i32) (result i32) + (nop) (local.get 0) (nop) (nop) (local.get 0) + (nop) (nop) (local.get 0) (nop) (nop) (select) + ) + + (func (export "as-block-first") (result i32) + (block (result i32) (nop) (i32.const 2)) + ) + (func (export "as-block-mid") (result i32) + (block (result i32) (call $dummy) (nop) (i32.const 2)) + ) + (func (export "as-block-last") (result i32) + (block (result i32) (nop) (call $dummy) (i32.const 3) (nop)) + ) + (func (export "as-block-everywhere") (result i32) + (block (result i32) + (nop) (nop) (call $dummy) (nop) (i32.const 4) (nop) (nop) + ) + ) + + (func (export "as-loop-first") (result i32) + (loop (result i32) (nop) (i32.const 2)) + ) + (func (export "as-loop-mid") (result i32) + (loop (result i32) (call $dummy) (nop) (i32.const 2)) + ) + (func (export "as-loop-last") (result i32) + (loop (result i32) (call $dummy) (i32.const 3) (nop)) + ) + (func (export "as-loop-everywhere") (result i32) + (loop (result i32) + (nop) (nop) (call $dummy) (nop) (i32.const 4) (nop) (nop) + ) + ) + + (func (export "as-if-condition") (param i32) + (local.get 0) (nop) (if (then (call $dummy))) + ) + (func (export "as-if-then") (param i32) + (if (local.get 0) (then (nop)) (else (call $dummy))) + ) + (func (export "as-if-else") (param i32) + (if (local.get 0) (then (call $dummy)) (else (nop))) + ) + + (func (export "as-br-first") (param i32) (result i32) + (block (result i32) (nop) (local.get 0) (br 0)) + ) + (func (export "as-br-last") (param i32) (result i32) + (block (result i32) (local.get 0) (nop) (br 0)) + ) + (func (export "as-br-everywhere") (param i32) (result i32) + (block (result i32) (nop) (nop) (local.get 0) (nop) (nop) (br 0)) + ) + + (func (export "as-br_if-first") (param i32) (result i32) + (block (result i32) (nop) (local.get 0) (local.get 0) (br_if 0)) + ) + (func (export "as-br_if-mid") (param i32) (result i32) + (block (result i32) (local.get 0) (nop) (local.get 0) (br_if 0)) + ) + (func (export "as-br_if-last") (param i32) (result i32) + (block (result i32) (local.get 0) (local.get 0) (nop) (br_if 0)) + ) + (func (export "as-br_if-everywhere") (param i32) (result i32) + (block (result i32) + (nop) (nop) (local.get 0) (nop) (nop) (local.get 0) (nop) (nop) + (br_if 0) + ) + ) + + (func (export "as-br_table-first") (param i32) (result i32) + (block (result i32) (nop) (local.get 0) (local.get 0) (br_table 0 0)) + ) + (func (export "as-br_table-mid") (param i32) (result i32) + (block (result i32) (local.get 0) (nop) (local.get 0) (br_table 0 0)) + ) + (func (export "as-br_table-last") (param i32) (result i32) + (block (result i32) (local.get 0) (local.get 0) (nop) (br_table 0 0)) + ) + (func (export "as-br_table-everywhere") (param i32) (result i32) + (block (result i32) + (nop) (nop) (local.get 0) (nop) (nop) (local.get 0) (nop) (nop) + (br_table 0 0) + ) + ) + + (func (export "as-return-first") (param i32) (result i32) + (nop) (local.get 0) (return) + ) + (func (export "as-return-last") (param i32) (result i32) + (local.get 0) (nop) (return) + ) + (func (export "as-return-everywhere") (param i32) (result i32) + (nop) (nop) (local.get 0) (nop) (nop) (return) + ) + + (func (export "as-call-first") (param i32 i32 i32) (result i32) + (nop) (local.get 0) (local.get 1) (local.get 2) (call $3-ary) + ) + (func (export "as-call-mid1") (param i32 i32 i32) (result i32) + (local.get 0) (nop) (local.get 1) (local.get 2) (call $3-ary) + ) + (func (export "as-call-mid2") (param i32 i32 i32) (result i32) + (local.get 0) (local.get 1) (nop) (local.get 2) (call $3-ary) + ) + (func (export "as-call-last") (param i32 i32 i32) (result i32) + (local.get 0) (local.get 1) (local.get 2) (nop) (call $3-ary) + ) + (func (export "as-call-everywhere") (param i32 i32 i32) (result i32) + (nop) (nop) (local.get 0) (nop) (nop) (local.get 1) + (nop) (nop) (local.get 2) (nop) (nop) (call $3-ary) + ) + + (func (export "as-unary-first") (param i32) (result i32) + (nop) (local.get 0) (i32.ctz) + ) + (func (export "as-unary-last") (param i32) (result i32) + (local.get 0) (nop) (i32.ctz) + ) + (func (export "as-unary-everywhere") (param i32) (result i32) + (nop) (nop) (local.get 0) (nop) (nop) (i32.ctz) + ) + + (func (export "as-binary-first") (param i32) (result i32) + (nop) (local.get 0) (local.get 0) (i32.add) + ) + (func (export "as-binary-mid") (param i32) (result i32) + (local.get 0) (nop) (local.get 0) (i32.add) + ) + (func (export "as-binary-last") (param i32) (result i32) + (local.get 0) (local.get 0) (nop) (i32.add) + ) + (func (export "as-binary-everywhere") (param i32) (result i32) + (nop) (local.get 0) (nop) (nop) (local.get 0) (nop) (nop) (i32.add) + ) + + (func (export "as-test-first") (param i32) (result i32) + (nop) (local.get 0) (i32.eqz) + ) + (func (export "as-test-last") (param i32) (result i32) + (local.get 0) (nop) (i32.eqz) + ) + (func (export "as-test-everywhere") (param i32) (result i32) + (nop) (nop) (local.get 0) (nop) (nop) i32.eqz + ) + + (func (export "as-compare-first") (param i32) (result i32) + (nop) (local.get 0) (local.get 0) (i32.ne) + ) + (func (export "as-compare-mid") (param i32) (result i32) + (local.get 0) (nop) (local.get 0) (i32.ne) + ) + (func (export "as-compare-last") (param i32) (result i32) + (local.get 0) (local.get 0) (nop) (i32.lt_u) + ) + (func (export "as-compare-everywhere") (param i32) (result i32) + (nop) (local.get 0) (nop) (nop) (local.get 0) (nop) (nop) (i32.le_s) + ) + + (func (export "as-memory.grow-first") (param i32) (result i32) + (nop) (local.get 0) (memory.grow) + ) + (func (export "as-memory.grow-last") (param i32) (result i32) + (local.get 0) (nop) (memory.grow) + ) + (func (export "as-memory.grow-everywhere") (param i32) (result i32) + (nop) (nop) (local.get 0) (nop) (nop) (memory.grow) + ) + + (func $func (param i32 i32) (result i32) (local.get 0)) + (type $check (func (param i32 i32) (result i32))) + (table funcref (elem $func)) + (func (export "as-call_indirect-first") (result i32) + (block (result i32) + (nop) (i32.const 1) (i32.const 2) (i32.const 0) + (call_indirect (type $check)) + ) + ) + (func (export "as-call_indirect-mid1") (result i32) + (block (result i32) + (i32.const 1) (nop) (i32.const 2) (i32.const 0) + (call_indirect (type $check)) + ) + ) + (func (export "as-call_indirect-mid2") (result i32) + (block (result i32) + (i32.const 1) (i32.const 2) (nop) (i32.const 0) + (call_indirect (type $check)) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (block (result i32) + (i32.const 1) (i32.const 2) (i32.const 0) (nop) + (call_indirect (type $check)) + ) + ) + (func (export "as-call_indirect-everywhere") (result i32) + (block (result i32) + (nop) (nop) (i32.const 1) (nop) (nop) (i32.const 2) (nop) (nop) (i32.const 0) (nop) (nop) + (call_indirect (type $check)) + ) + ) + + (func (export "as-local.set-first") (param i32) (result i32) + (nop) (i32.const 2) (local.set 0) (local.get 0) + ) + (func (export "as-local.set-last") (param i32) (result i32) + (i32.const 2) (nop) (local.set 0) (local.get 0) + ) + (func (export "as-local.set-everywhere") (param i32) (result i32) + (nop) (nop) (i32.const 2) (nop) (nop) (local.set 0) (local.get 0) + ) + + (func (export "as-local.tee-first") (param i32) (result i32) + (nop) (i32.const 2) (local.tee 0) + ) + (func (export "as-local.tee-last") (param i32) (result i32) + (i32.const 2) (nop) (local.tee 0) + ) + (func (export "as-local.tee-everywhere") (param i32) (result i32) + (nop) (nop) (i32.const 2) (nop) (nop) (local.tee 0) + ) + + (global $a (mut i32) (i32.const 0)) + (func (export "as-global.set-first") (result i32) + (nop) (i32.const 2) (global.set $a) (global.get $a) + ) + (func (export "as-global.set-last") (result i32) + (i32.const 2) (nop) (global.set $a) (global.get $a) + ) + (func (export "as-global.set-everywhere") (result i32) + (nop) (nop) (i32.const 2) (nop) (nop) (global.set 0) + (global.get $a) + ) + + (func (export "as-load-first") (param i32) (result i32) + (nop) (local.get 0) (i32.load) + ) + (func (export "as-load-last") (param i32) (result i32) + (local.get 0) (nop) (i32.load) + ) + (func (export "as-load-everywhere") (param i32) (result i32) + (nop) (nop) (local.get 0) (nop) (nop) (i32.load) + ) + + (func (export "as-store-first") (param i32 i32) + (nop) (local.get 0) (local.get 1) (i32.store) + ) + (func (export "as-store-mid") (param i32 i32) + (local.get 0) (nop) (local.get 1) (i32.store) + ) + (func (export "as-store-last") (param i32 i32) + (local.get 0) (local.get 1) (nop) (i32.store) + ) + (func (export "as-store-everywhere") (param i32 i32) + (nop) (nop) (local.get 0) (nop) (nop) (local.get 1) (nop) (nop) (i32.store) + ) +) + +(assert_return (invoke "as-func-first") (i32.const 1)) +(assert_return (invoke "as-func-mid") (i32.const 2)) +(assert_return (invoke "as-func-last") (i32.const 3)) +(assert_return (invoke "as-func-everywhere") (i32.const 4)) + +(assert_return (invoke "as-drop-first" (i32.const 0))) +(assert_return (invoke "as-drop-last" (i32.const 0))) +(assert_return (invoke "as-drop-everywhere" (i32.const 0))) + +(assert_return (invoke "as-select-first" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "as-select-mid1" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "as-select-mid2" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "as-select-last" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "as-select-everywhere" (i32.const 3)) (i32.const 3)) + +(assert_return (invoke "as-block-first") (i32.const 2)) +(assert_return (invoke "as-block-mid") (i32.const 2)) +(assert_return (invoke "as-block-last") (i32.const 3)) +(assert_return (invoke "as-block-everywhere") (i32.const 4)) + +(assert_return (invoke "as-loop-first") (i32.const 2)) +(assert_return (invoke "as-loop-mid") (i32.const 2)) +(assert_return (invoke "as-loop-last") (i32.const 3)) +(assert_return (invoke "as-loop-everywhere") (i32.const 4)) + +(assert_return (invoke "as-if-condition" (i32.const 0))) +(assert_return (invoke "as-if-condition" (i32.const -1))) +(assert_return (invoke "as-if-then" (i32.const 0))) +(assert_return (invoke "as-if-then" (i32.const 4))) +(assert_return (invoke "as-if-else" (i32.const 0))) +(assert_return (invoke "as-if-else" (i32.const 3))) + +(assert_return (invoke "as-br-first" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "as-br-last" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-br-everywhere" (i32.const 7)) (i32.const 7)) + +(assert_return (invoke "as-br_if-first" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "as-br_if-mid" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "as-br_if-last" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-br_if-everywhere" (i32.const 7)) (i32.const 7)) + +(assert_return (invoke "as-br_table-first" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "as-br_table-mid" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "as-br_table-last" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-br_table-everywhere" (i32.const 7)) (i32.const 7)) + +(assert_return (invoke "as-return-first" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "as-return-last" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-return-everywhere" (i32.const 7)) (i32.const 7)) + +(assert_return (invoke "as-call-first" (i32.const 3) (i32.const 1) (i32.const 2)) (i32.const 2)) +(assert_return (invoke "as-call-mid1" (i32.const 3) (i32.const 1) (i32.const 2)) (i32.const 2)) +(assert_return (invoke "as-call-mid2" (i32.const 0) (i32.const 3) (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-call-last" (i32.const 10) (i32.const 9) (i32.const -1)) (i32.const 20)) +(assert_return (invoke "as-call-everywhere" (i32.const 2) (i32.const 1) (i32.const 5)) (i32.const -2)) + +(assert_return (invoke "as-unary-first" (i32.const 30)) (i32.const 1)) +(assert_return (invoke "as-unary-last" (i32.const 30)) (i32.const 1)) +(assert_return (invoke "as-unary-everywhere" (i32.const 12)) (i32.const 2)) + +(assert_return (invoke "as-binary-first" (i32.const 3)) (i32.const 6)) +(assert_return (invoke "as-binary-mid" (i32.const 3)) (i32.const 6)) +(assert_return (invoke "as-binary-last" (i32.const 3)) (i32.const 6)) +(assert_return (invoke "as-binary-everywhere" (i32.const 3)) (i32.const 6)) + +(assert_return (invoke "as-test-first" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-test-last" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-test-everywhere" (i32.const 0)) (i32.const 1)) + +(assert_return (invoke "as-compare-first" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "as-compare-mid" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "as-compare-last" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "as-compare-everywhere" (i32.const 3)) (i32.const 1)) + +(assert_return (invoke "as-memory.grow-first" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-memory.grow-last" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "as-memory.grow-everywhere" (i32.const 12)) (i32.const 3)) + +(assert_return (invoke "as-call_indirect-first") (i32.const 1)) +(assert_return (invoke "as-call_indirect-mid1") (i32.const 1)) +(assert_return (invoke "as-call_indirect-mid2") (i32.const 1)) +(assert_return (invoke "as-call_indirect-last") (i32.const 1)) +(assert_return (invoke "as-call_indirect-everywhere") (i32.const 1)) + +(assert_return (invoke "as-local.set-first" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-local.set-last" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-local.set-everywhere" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-local.tee-first" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-local.tee-last" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-local.tee-everywhere" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-global.set-first") (i32.const 2)) +(assert_return (invoke "as-global.set-last") (i32.const 2)) +(assert_return (invoke "as-global.set-everywhere") (i32.const 2)) + +(assert_return (invoke "as-load-first" (i32.const 100)) (i32.const 0)) +(assert_return (invoke "as-load-last" (i32.const 100)) (i32.const 0)) +(assert_return (invoke "as-load-everywhere" (i32.const 100)) (i32.const 0)) + +(assert_return (invoke "as-store-first" (i32.const 0) (i32.const 1))) +(assert_return (invoke "as-store-mid" (i32.const 0) (i32.const 2))) +(assert_return (invoke "as-store-last" (i32.const 0) (i32.const 3))) +(assert_return (invoke "as-store-everywhere" (i32.const 0) (i32.const 4))) + +(assert_invalid + (module (func $type-i32 (result i32) (nop))) + "type mismatch" +) +(assert_invalid + (module (func $type-i64 (result i64) (nop))) + "type mismatch" +) +(assert_invalid + (module (func $type-f32 (result f32) (nop))) + "type mismatch" +) +(assert_invalid + (module (func $type-f64 (result f64) (nop))) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/annotations/annotations.wast b/runtime/unc-vm/tests/wast/spec/proposals/annotations/annotations.wast new file mode 100644 index 000000000..ce538d937 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/annotations/annotations.wast @@ -0,0 +1,195 @@ +(@a) + +(@aas-3!@$d-@#4) +(@@) (@$) (@+) (@0) (@.) (@!$@#$23414@#$) +(@a x y z) +(@a x-y $yz "aa" -2 0.3 0x3) +(@a x-y$yz"aa"-2) +(@a block func module i32.add) +(@a 0x 8q 0xfa #4g0-.@f#^&@#$*0sf -- @#) +(@a , ; ] [ }} }x{ ({) ,{{};}] ;) +(@a (bla) () (5-g) ("aa" a) ($x) (bla bla) (x (y)) ")" "(" x")"y) +(@a @ @x (@x) (@x y) (@) (@ x) (@(@(@(@))))) +(@a (;bla;) (; ) ;) + ;; bla) + ;; bla (@x +) + +(assert_malformed (module quote "(@a \00)") "illegal character") +(assert_malformed (module quote "(@a \01)") "illegal character") +(assert_malformed (module quote "(@a \02)") "illegal character") +(assert_malformed (module quote "(@a \03)") "illegal character") +(assert_malformed (module quote "(@a \04)") "illegal character") +(assert_malformed (module quote "(@a \05)") "illegal character") +(assert_malformed (module quote "(@a \06)") "illegal character") +(assert_malformed (module quote "(@a \07)") "illegal character") +(assert_malformed (module quote "(@a \08)") "illegal character") +(module quote "(@a \09)") ;; \t +(module quote "(@a \0a)") ;; \n +(assert_malformed (module quote "(@a \0b)") "illegal character") +(assert_malformed (module quote "(@a \0c)") "illegal character") +(module quote "(@a \0d)") ;; \r +(assert_malformed (module quote "(@a \0e)") "illegal character") +(assert_malformed (module quote "(@a \0f)") "illegal character") +(assert_malformed (module quote "(@a \10)") "illegal character") +(assert_malformed (module quote "(@a \11)") "illegal character") +(assert_malformed (module quote "(@a \12)") "illegal character") +(assert_malformed (module quote "(@a \13)") "illegal character") +(assert_malformed (module quote "(@a \14)") "illegal character") +(assert_malformed (module quote "(@a \15)") "illegal character") +(assert_malformed (module quote "(@a \16)") "illegal character") +(assert_malformed (module quote "(@a \17)") "illegal character") +(assert_malformed (module quote "(@a \18)") "illegal character") +(assert_malformed (module quote "(@a \19)") "illegal character") +(assert_malformed (module quote "(@a \1a)") "illegal character") +(assert_malformed (module quote "(@a \1b)") "illegal character") +(assert_malformed (module quote "(@a \1c)") "illegal character") +(assert_malformed (module quote "(@a \1d)") "illegal character") +(assert_malformed (module quote "(@a \1e)") "illegal character") +(assert_malformed (module quote "(@a \1f)") "illegal character") +(module quote "(@a \20)") ;; space +(assert_malformed (module quote "(@a \7f)") "illegal character") +(assert_malformed (module quote "(@a \80)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \81)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \90)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \a0)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \b0)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \c0)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \d0)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \e0)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \f0)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a \ff)") "malformed UTF-8 encoding") +(assert_malformed (module quote "(@a Heiße Würstchen)") "illegal character") +(assert_malformed (module quote "(@a )") "illegal character") + +(assert_malformed (module quote "( @a)") "unknown operator") + +(assert_malformed (module quote "(@)") "malformed annotation id") +(assert_malformed (module quote "(@ )") "malformed annotation id") +(assert_malformed (module quote "(@ x)") "malformed annotation id") +(assert_malformed (module quote "(@(@a)x)") "malformed annotation id") + +(assert_malformed (module quote "(@x ") "unclosed annotation") +(assert_malformed (module quote "(@x ()") "unclosed annotation") +(assert_malformed (module quote "(@x (y (z))") "unclosed annotation") +(assert_malformed (module quote "(@x (@y )") "unclosed annotation") + +(assert_malformed (module quote "(@x))") "unexpected token") +(assert_malformed (module quote "(@x ()))") "unexpected token") +(assert_malformed (module quote "(@x (y (z))))") "unexpected token") +(assert_malformed (module quote "(@x (@y )))") "unexpected token") + +(assert_malformed (module quote "(@x \"") "unclosed string") +(assert_malformed (module quote "(@x \")") "unclosed string") + +(assert_malformed (module quote "((@a)@b)") "unknown operator") +(assert_malformed (module quote "(func $(@a))") "unknown operator") +(assert_malformed (module quote "(func $(@a)f)") "unknown operator") + +((@a) module (@a) $m (@a) (@a) + ((@a) import (@a) "spectest" (@a) "global_i32" (@a) + ((@a) global (@a) $g (@a) i32 (@a)) (@a) + ) (@a) + ((@a) import (@a) "spectest" (@a) "table" (@a) + ((@a) table (@a) $t (@a) 10 (@a) 20 (@a) funcref (@a)) (@a) + ) (@a) + ((@a) import (@a) "spectest" (@a) "memory" (@a) + ((@a) memory (@a) $m (@a) 1 (@a) 2 (@a)) (@a) + ) (@a) + ((@a) import (@a) "spectest" (@a) "print_i32_f32" (@a) + ((@a) func (@a) $f (@a) + ((@a) param (@a) i32 (@a) f32 (@a)) (@a) + ((@a) result (@a)) (@a) + ) (@a) + ) (@a) + + ((@a) export (@a) "g" (@a) + ((@a) global (@a) $g (@a)) (@a) + ) (@a) + ((@a) export (@a) "t" (@a) + ((@a) table (@a) $t (@a)) (@a) + ) (@a) + ((@a) export (@a) "m" (@a) + ((@a) memory (@a) $m (@a)) (@a) + ) (@a) + ((@a) export (@a) "f" (@a) + ((@a) func (@a) $f (@a)) (@a) + ) (@a) +) (@a) + +((@a) module (@a) $m (@a) (@a) + ((@a) global (@a) $g (@a) + ((@a) export (@a) "g" (@a)) (@a) + ((@a) import (@a) "spectest" (@a) "global_i32" (@a)) (@a) + i32 (@a) + ) (@a) + ((@a) table (@a) $t (@a) + ((@a) export (@a) "t" (@a)) (@a) + ((@a) import (@a) "spectest" (@a) "table" (@a)) (@a) + 10 (@a) 20 (@a) + funcref (@a) + ) (@a) + ((@a) memory (@a) $m (@a) + ((@a) export (@a) "m" (@a)) (@a) + ((@a) import (@a) "spectest" (@a) "memory" (@a)) (@a) + 1 (@a) 2 (@a) + ) (@a) + ((@a) func (@a) $f (@a) + ((@a) export (@a) "f" (@a)) (@a) + ((@a) import (@a) "spectest" (@a) "print_i32_f32" (@a)) (@a) + ((@a) param (@a) i32 (@a) f32 (@a)) (@a) + ((@a) result (@a)) (@a) + ) (@a) +) (@a) + +((@a) module (@a) $m (@a) (@a) + ((@a) type (@a) $T (@a) + ((@a) func (@a) + ((@a) param (@a) i32 (@a) i64 (@a)) (@a) + ((@a) param (@a) $x (@a) i32 (@a)) (@a) + ((@a) result (@a) i32 (@a)) (@a) + ) (@a) + ) (@a) + + ((@a) global (@a) $g (@a) + ((@a) export (@a) "g" (@a)) (@a) + i32 (@a) + ((@a) i32.const (@a) 42 (@a)) (@a) + ) (@a) + ((@a) table (@a) $t (@a) + ((@a) export (@a) "t" (@a)) (@a) + 10 (@a) 20 (@a) + funcref (@a) + ) (@a) + ((@a) memory (@a) $m (@a) + ((@a) export (@a) "m" (@a)) (@a) + 1 (@a) 2 (@a) + ) (@a) + ((@a) func (@a) $f (@a) + ((@a) export (@a) "f" (@a)) (@a) + ((@a) param (@a) i32 (@a) i64 (@a)) (@a) + ((@a) param (@a) $x (@a) i32 (@a)) (@a) + ((@a) result (@a) i32 (@a)) (@a) + ((@a) local (@a) i32 (@a) i32 (@a)) (@a) + ((@a) local (@a) $y (@a) i32 (@a)) (@a) + ((@a) block (@a) + ((@a) result (@a) i32 (@a)) (@a) + ((@a) i32.add (@a) + ((@a) local.get (@a) $x (@a)) (@a) + ((@a) local.get (@a) 0 (@a)) (@a) + ) + ) + ) (@a) + + ((@a) elem (@a) + ((@a) offset (@a) ((@a) i32.const (@a) 0 (@a)) (@a)) (@a) + $f (@a) $f (@a) (@a) $f (@a) + ) (@a) + ((@a) data (@a) + ((@a) offset (@a) ((@a) i32.const (@a) 0 (@a)) (@a)) (@a) + "bla" (@a) "\43" (@a) (@a) "" (@a) + ) (@a) + + (func $s) + ((@a) start (@a) $s (@a)) (@a) +) (@a) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/binary.wast b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/binary.wast new file mode 100644 index 000000000..838582639 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/binary.wast @@ -0,0 +1,1218 @@ +(module binary "\00asm\01\00\00\00") +(module binary "\00asm" "\01\00\00\00") +(module $M1 binary "\00asm\01\00\00\00") +(module $M2 binary "\00asm" "\01\00\00\00") + +(assert_malformed (module binary "") "unexpected end") +(assert_malformed (module binary "\01") "unexpected end") +(assert_malformed (module binary "\00as") "unexpected end") +(assert_malformed (module binary "asm\00") "magic header not detected") +(assert_malformed (module binary "msa\00") "magic header not detected") +(assert_malformed (module binary "msa\00\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "msa\00\00\00\00\01") "magic header not detected") +(assert_malformed (module binary "asm\01\00\00\00\00") "magic header not detected") +(assert_malformed (module binary "wasm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\7fasm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\80asm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\82asm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\ffasm\01\00\00\00") "magic header not detected") + +;; 8-byte endian-reversed. +(assert_malformed (module binary "\00\00\00\01msa\00") "magic header not detected") + +;; Middle-endian byte orderings. +(assert_malformed (module binary "a\00ms\00\01\00\00") "magic header not detected") +(assert_malformed (module binary "sm\00a\00\00\01\00") "magic header not detected") + +;; Upper-cased. +(assert_malformed (module binary "\00ASM\01\00\00\00") "magic header not detected") + +;; EBCDIC-encoded magic. +(assert_malformed (module binary "\00\81\a2\94\01\00\00\00") "magic header not detected") + +;; Leading UTF-8 BOM. +(assert_malformed (module binary "\ef\bb\bf\00asm\01\00\00\00") "magic header not detected") + +;; Malformed binary version. +(assert_malformed (module binary "\00asm") "unexpected end") +(assert_malformed (module binary "\00asm\01") "unexpected end") +(assert_malformed (module binary "\00asm\01\00\00") "unexpected end") +(assert_malformed (module binary "\00asm\00\00\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\0d\00\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\0e\00\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\00\01\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\00\00\01\00") "unknown binary version") +(assert_malformed (module binary "\00asm\00\00\00\01") "unknown binary version") + +;; Unsigned LEB128 can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\05\04\01" ;; Memory section with 1 entry + "\00\82\00" ;; no max, minimum 2 +) +(module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\00" ;; no max, minimum 2 +) + +;; Signed LEB128 can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\00" ;; i32.const 0 + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\7f" ;; i32.const -1 + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\00" ;; i32.const 0 + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\7f" ;; i32.const -1 + "\0b" ;; end +) + +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\00" ;; i64.const 0 with unused bits set + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\7f" ;; i64.const -1 with unused bits unset + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\00" ;; i64.const 0 with unused bits set + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\7f" ;; i64.const -1 with unused bits unset + "\0b" ;; end +) + +;; Data segment memory index can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; Memory section with 1 entry + "\00\00" ;; no max, minimum 0 + "\0b\07\01" ;; Data section with 1 entry + "\80\00" ;; Memory index 0, encoded with 2 bytes + "\41\00\0b\00" ;; (i32.const 0) with contents "" +) + +;; Element segment table index can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + "\09\09\01" ;; Element section with 1 entry + "\02\80\00" ;; Table index 0, encoded with 2 bytes + "\41\00\0b\00\00" ;; (i32.const 0) with no elements +) + +;; Unsigned LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\08\01" ;; Memory section with 1 entry + "\00\82\80\80\80\80\00" ;; no max, minimum 2 with one byte too many + ) + "integer representation too long" +) + +;; Signed LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\80\00" ;; i32.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\ff\7f" ;; i32.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\80\00" ;; i64.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\7f" ;; i64.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +;; Unsigned LEB128s zero-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\70" ;; no max, minimum 2 with unused bits set + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\40" ;; no max, minimum 2 with some unused bits set + ) + "integer too large" +) + +;; Signed LEB128s sign-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\70" ;; i32.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\0f" ;; i32.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\1f" ;; i32.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\4f" ;; i32.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\7e" ;; i64.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\01" ;; i64.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\02" ;; i64.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\41" ;; i64.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + + +;; call_indirect reserved byte equal to zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\04\04\01\70\00\00" ;; Table section + "\0a\09\01" ;; Code section + + ;; function 0 + "\07\00" + "\41\00" ;; i32.const 0 + "\11\00" ;; call_indirect (type 0) + "\01" ;; call_indirect reserved byte is not equal to zero! + "\0b" ;; end + ) + "zero flag expected" +) + +;; call_indirect reserved byte should not be a "long" LEB128 zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\04\04\01\70\00\00" ;; Table section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\07\00" + "\41\00" ;; i32.const 0 + "\11\00" ;; call_indirect (type 0) + "\80\00" ;; call_indirect reserved byte + "\0b" ;; end + ) + "zero flag expected" +) + +;; Same as above for 3, 4, and 5-byte zero encodings. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\04\04\01\70\00\00" ;; Table section + "\0a\0b\01" ;; Code section + + ;; function 0 + "\08\00" + "\41\00" ;; i32.const 0 + "\11\00" ;; call_indirect (type 0) + "\80\80\00" ;; call_indirect reserved byte + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\04\04\01\70\00\00" ;; Table section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\09\00" + "\41\00" ;; i32.const 0 + "\11\00" ;; call_indirect (type 0) + "\80\80\80\00" ;; call_indirect reserved byte + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\04\04\01\70\00\00" ;; Table section + "\0a\0d\01" ;; Code section + + ;; function 0 + "\0a\00" + "\41\00" ;; i32.const 0 + "\11\00" ;; call_indirect (type 0) + "\80\80\80\80\00" ;; call_indirect reserved byte + "\0b" ;; end + ) + "zero flag expected" +) + +;; memory.grow reserved byte equal to zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\09\01" ;; Code section + + ;; function 0 + "\07\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\01" ;; memory.grow reserved byte is not equal to zero! + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; memory.grow reserved byte should not be a "long" LEB128 zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\08\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; Same as above for 3, 4, and 5-byte zero encodings. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0b\01" ;; Code section + + ;; function 0 + "\09\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\0a\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\80\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0d\01" ;; Code section + + ;; function 0 + "\0b\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\80\80\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; memory.size reserved byte equal to zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\07\01" ;; Code section + + ;; function 0 + "\05\00" + "\3f" ;; memory.size + "\01" ;; memory.size reserved byte is not equal to zero! + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; memory.size reserved byte should not be a "long" LEB128 zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\08\01" ;; Code section + + ;; function 0 + "\06\00" + "\3f" ;; memory.size + "\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; Same as above for 3, 4, and 5-byte zero encodings. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\09\01" ;; Code section + + ;; function 0 + "\07\00" + "\3f" ;; memory.size + "\80\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\08\00" + "\3f" ;; memory.size + "\80\80\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0b\01" ;; Code section + + ;; function 0 + "\09\00" + "\3f" ;; memory.size + "\80\80\80\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; No more than 2^32 locals. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\0a\02" + "\ff\ff\ff\ff\0f\7f" ;; 0xFFFFFFFF i32 + "\02\7e" ;; 0x00000002 i64 + "\0b" ;; end + ) + "too many locals" +) + +;; Local count can be 0. +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\08\03" + "\00\7f" ;; 0 i32 + "\00\7e" ;; 0 i64 + "\02\7d" ;; 2 f32 + "\0b" ;; end +) + +;; Function section has non-zero count, but code section is absent. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\03\02\00\00" ;; Function section with 2 functions + ) + "function and code section have inconsistent lengths" +) + +;; Code section has non-zero count, but function section is absent. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0a\04\01\02\00\0b" ;; Code section with 1 empty function + ) + "function and code section have inconsistent lengths" +) + +;; Function section count > code section count +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\03\02\00\00" ;; Function section with 2 functions + "\0a\04\01\02\00\0b" ;; Code section with 1 empty function + ) + "function and code section have inconsistent lengths" +) + +;; Function section count < code section count +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section with 1 function + "\0a\07\02\02\00\0b\02\00\0b" ;; Code section with 2 empty functions + ) + "function and code section have inconsistent lengths" +) + +;; Function section has zero count, and code section is absent. +(module binary + "\00asm" "\01\00\00\00" + "\03\01\00" ;; Function section with 0 functions +) + +;; Code section has zero count, and function section is absent. +(module binary + "\00asm" "\01\00\00\00" + "\0a\01\00" ;; Code section with 0 functions +) + +;; Fewer passive segments than datacount +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0c\01\03" ;; Datacount section with value "3" + "\0b\05\02" ;; Data section with two entries + "\01\00" ;; Passive data section + "\01\00") ;; Passive data section + "data count and data section have inconsistent lengths") + +;; More passive segments than datacount +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0c\01\01" ;; Datacount section with value "1" + "\0b\05\02" ;; Data section with two entries + "\01\00" ;; Passive data section + "\01\00") ;; Passive data section + "data count and data section have inconsistent lengths") + +;; memory.init requires a datacount section +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0e\01" ;; Code section + + ;; function 0 + "\0c\00" + "\41\00" ;; zero args + "\41\00" + "\41\00" + "\fc\08\00\00" ;; memory.init + "\0b" + + "\0b\03\01\01\00" ;; Data section + ) ;; end + "data count section required") + +;; data.drop requires a datacount section +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\07\01" ;; Code section + + ;; function 0 + "\05\00" + "\fc\09\00" ;; data.drop + "\0b" + + "\0b\03\01\01\00" ;; Data section + ) ;; end + "data count section required") + +;; passive element segment containing opcode other than ref.func or ref.null +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\07\01" ;; Element section with one segment + "\05\70" ;; Passive, funcref + "\01" ;; 1 element + "\d3\00\0b" ;; bad opcode, index 0, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + "invalid elem") + +;; passive element segment containing type other than funcref +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\07\01" ;; Element section with one segment + "\05\7f" ;; Passive, i32 + "\01" ;; 1 element + "\d2\00\0b" ;; ref.func, index 0, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + "malformed element type") + +;; passive element segment containing opcode ref.func +(module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\07\01" ;; Element section with one segment + "\05\70" ;; Passive, funcref + "\01" ;; 1 element + "\d2\00\0b" ;; ref.func, index 0, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + +;; passive element segment containing opcode ref.null +(module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\06\01" ;; Element section with one segment + "\05\70" ;; Passive, funcref + "\01" ;; 1 element + "\d0\0b" ;; ref.null, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + + +;; Type count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\01\00" ;; type count can be zero +) + +;; 2 type declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\07\02" ;; type section with inconsistent count (2 declared, 1 given) + "\60\00\00" ;; 1st type + ;; "\60\00\00" ;; 2nd type (missed) + ) + "unexpected end of section or function" +) + +;; 1 type declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\07\01" ;; type section with inconsistent count (1 declared, 2 given) + "\60\00\00" ;; 1st type + "\60\00\00" ;; 2nd type (redundant) + ) + "section size mismatch" +) + +;; Import count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; type 0 + "\02\01\00" ;; import count can be zero +) + +;; 2 import declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; type 0 + "\02\16\02" ;; import section with inconsistent count (2 declared, 1 given) + ;; 1st import + "\08" ;; string length + "\73\70\65\63\74\65\73\74" ;; spectest + "\09" ;; string length + "\70\72\69\6e\74\5f\69\33\32" ;; print_i32 + "\00\00" ;; import kind, import signature index + ;; 2nd import + ;; (missed) + ) + "unexpected end of section or function" +) + +;; 1 import declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\09\02" ;; type section + "\60\01\7f\00" ;; type 0 + "\60\01\7d\00" ;; type 1 + "\02\2b\01" ;; import section with inconsistent count (1 declared, 2 given) + ;; 1st import + "\08" ;; string length + "\73\70\65\63\74\65\73\74" ;; spectest + "\09" ;; string length + "\70\72\69\6e\74\5f\69\33\32" ;; print_i32 + "\00\00" ;; import kind, import signature index + ;; 2nd import + ;; (redundant) + "\08" ;; string length + "\73\70\65\63\74\65\73\74" ;; spectest + "\09" ;; string length + "\70\72\69\6e\74\5f\66\33\32" ;; print_f32 + "\00\01" ;; import kind, import signature index + ) + "section size mismatch" +) + +;; Table count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\04\01\00" ;; table count can be zero +) + +;; 1 table declared, 0 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\04\01\01" ;; table section with inconsistent count (1 declared, 0 given) + ;; "\70\01\00\00" ;; table entity + ) + "unexpected end of section or function" +) + +;; Memory count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\05\01\00" ;; memory count can be zero +) + +;; 1 memory declared, 0 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\01\01" ;; memory section with inconsistent count (1 declared, 0 given) + ;; "\00\00" ;; memory 0 (missed) + ) + "unexpected end of section or function" +) + +;; Global count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\06\01\00" ;; global count can be zero +) + +;; 2 global declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\06\02" ;; global section with inconsistent count (2 declared, 1 given) + "\7f\00\41\00\0b" ;; global 0 + ;; "\7f\00\41\00\0b" ;; global 1 (missed) + ) + "unexpected end of section or function" +) + +;; 1 global declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; global section with inconsistent count (1 declared, 2 given) + "\7f\00\41\00\0b" ;; global 0 + "\7f\00\41\00\0b" ;; global 1 (redundant) + ) + "section size mismatch" +) + +;; Export count can be 0 +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\03\02\00\00" ;; func section + "\07\01\00" ;; export count can be zero + "\0a\07\02" ;; code section + "\02\00\0b" ;; function body 0 + "\02\00\0b" ;; function body 1 +) + +;; 2 export declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\03\02\00\00" ;; func section + "\07\06\02" ;; export section with inconsistent count (2 declared, 1 given) + "\02" ;; export 0 + "\66\31" ;; export name + "\00\00" ;; export kind, export func index + ;; "\02" ;; export 1 (missed) + ;; "\66\32" ;; export name + ;; "\00\01" ;; export kind, export func index + "\0a\07\02" ;; code section + "\02\00\0b" ;; function body 0 + "\02\00\0b" ;; function body 1 + ) + "unexpected end of section or function" +) + +;; 1 export declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\03\02\00\00" ;; func section + "\07\0b\01" ;; export section with inconsistent count (1 declared, 2 given) + "\02" ;; export 0 + "\66\31" ;; export name + "\00\00" ;; export kind, export func index + "\02" ;; export 1 (redundant) + "\66\32" ;; export name + "\00\01" ;; export kind, export func index + "\0a\07\02" ;; code section + "\02\00\0b" ;; function body 0 + "\02\00\0b" ;; function body 1 + ) + "section size mismatch" +) + +;; elem segment count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\01\00" ;; elem segment count can be zero + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body +) + +;; 2 elem segment declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\07\02" ;; elem with inconsistent segment count (2 declared, 1 given) + "\00\41\00\0b\01\00" ;; elem 0 + ;; "\00\41\00\0b\01\00" ;; elem 1 (missed) + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body + ) + "invalid elements segment kind" +) + +;; 1 elem segment declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\0d\01" ;; elem with inconsistent segment count (1 declared, 2 given) + "\00\41\00\0b\01\00" ;; elem 0 + "\00\41\00\0b\01\00" ;; elem 1 (redundant) + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body + ) + "section size mismatch" +) + +;; data segment count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\01\00" ;; data segment count can be zero +) + +;; 2 data segment declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\07\02" ;; data with inconsistent segment count (2 declared, 1 given) + "\00\41\00\0b\01\61" ;; data 0 + ;; "\00\41\01\0b\01\62" ;; data 1 (missed) + ) + "unexpected end of section or function" +) + +;; 1 data segment declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\0d\01" ;; data with inconsistent segment count (1 declared, 2 given) + "\00\41\00\0b\01\61" ;; data 0 + "\00\41\01\0b\01\62" ;; data 1 (redundant) + ) + "section size mismatch" +) + +;; data segment has 7 bytes declared, but 6 bytes given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\0c\01" ;; data section + "\00\41\03\0b" ;; data segment 0 + "\07" ;; data segment size with inconsistent lengths (7 declared, 6 given) + "\61\62\63\64\65\66" ;; 6 bytes given + ) + "unexpected end of section or function" +) + +;; data segment has 5 bytes declared, but 6 bytes given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\0c\01" ;; data section + "\00\41\00\0b" ;; data segment 0 + "\05" ;; data segment size with inconsistent lengths (5 declared, 6 given) + "\61\62\63\64\65\66" ;; 6 bytes given + ) + "section size mismatch" +) + +;; br_table target count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\0a\11\01" ;; code section + "\0f\00" ;; func 0 + "\02\40" ;; block 0 + "\41\01" ;; condition of if 0 + "\04\40" ;; if 0 + "\41\01" ;; index of br_table element + "\0e\00" ;; br_table target count can be zero + "\02" ;; break depth for default + "\0b\0b\0b" ;; end +) + +;; 2 br_table target declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\0a\12\01" ;; code section + "\10\00" ;; func 0 + "\02\40" ;; block 0 + "\41\01" ;; condition of if 0 + "\04\40" ;; if 0 + "\41\01" ;; index of br_table element + "\0e\02" ;; br_table with inconsistent target count (2 declared, 1 given) + "\00" ;; break depth 0 + ;; "\01" ;; break depth 1 (missed) + "\02" ;; break depth for default + "\0b\0b\0b" ;; end + ) + "unexpected end of section or function" +) + +;; 1 br_table target declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\0a\12\01" ;; code section + "\11\00" ;; func 0 + "\02\40" ;; block 0 + "\41\01" ;; condition of if 0 + "\04\40" ;; if 0 + "\41\01" ;; index of br_table element + "\0e\01" ;; br_table with inconsistent target count (1 declared, 2 given) + "\00" ;; break depth 0 + "\01" ;; break depth 1 + "\02" ;; break depth for default + "\0b\0b\0b" ;; end + ) + "malformed value type" +) + +;; Start section +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\08\01\00" ;; Start section: function 0 + + "\0a\04\01" ;; Code section + ;; function 0 + "\02\00" + "\0b" ;; end +) + +;; Multiple start sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\08\01\00" ;; Start section: function 0 + "\08\01\00" ;; Start section: function 0 + + "\0a\04\01" ;; Code section + ;; function 0 + "\02\00" + "\0b" ;; end + ) + "junk after last section" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/bulk.wast b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/bulk.wast new file mode 100644 index 000000000..6fb1f0dec --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/bulk.wast @@ -0,0 +1,351 @@ +;; segment syntax +(module + (memory 1) + (data "foo")) + +(module + (table 3 funcref) + (elem funcref (ref.func 0) (ref.null) (ref.func 1)) + (func) + (func)) + +;; memory.fill +(module + (memory 1) + + (func (export "fill") (param i32 i32 i32) + (memory.fill + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0))) +) + +;; Basic fill test. +(invoke "fill" (i32.const 1) (i32.const 0xff) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0xff)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 0xff)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 0xff)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 0)) + +;; Fill value is stored as a byte. +(invoke "fill" (i32.const 0) (i32.const 0xbbaa) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0xaa)) + +;; Fill all of memory +(invoke "fill" (i32.const 0) (i32.const 0) (i32.const 0x10000)) + +;; Out-of-bounds writes trap, and nothing is written +(assert_trap (invoke "fill" (i32.const 0xff00) (i32.const 1) (i32.const 0x101)) + "out of bounds memory access") +(assert_return (invoke "load8_u" (i32.const 0xff00)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 0xffff)) (i32.const 0)) + +;; Succeed when writing 0 bytes at the end of the region. +(invoke "fill" (i32.const 0x10000) (i32.const 0) (i32.const 0)) + +;; Writing 0 bytes outside the memory traps. +(assert_trap (invoke "fill" (i32.const 0x10001) (i32.const 0) (i32.const 0)) + "out of bounds memory access") + + +;; memory.copy +(module + (memory (data "\aa\bb\cc\dd")) + + (func (export "copy") (param i32 i32 i32) + (memory.copy + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0))) +) + +;; Non-overlapping copy. +(invoke "copy" (i32.const 10) (i32.const 0) (i32.const 4)) + +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0xdd)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 0)) + +;; Overlap, source > dest +(invoke "copy" (i32.const 8) (i32.const 10) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0xdd)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0xdd)) + +;; Overlap, source < dest +(invoke "copy" (i32.const 10) (i32.const 7) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 0xdd)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 0)) + +;; Copy ending at memory limit is ok. +(invoke "copy" (i32.const 0xff00) (i32.const 0) (i32.const 0x100)) +(invoke "copy" (i32.const 0xfe00) (i32.const 0xff00) (i32.const 0x100)) + +;; Succeed when copying 0 bytes at the end of the region. +(invoke "copy" (i32.const 0x10000) (i32.const 0) (i32.const 0)) +(invoke "copy" (i32.const 0) (i32.const 0x10000) (i32.const 0)) + +;; Copying 0 bytes outside the memory traps. +(assert_trap (invoke "copy" (i32.const 0x10001) (i32.const 0) (i32.const 0)) + "out of bounds memory access") +(assert_trap (invoke "copy" (i32.const 0) (i32.const 0x10001) (i32.const 0)) + "out of bounds memory access") + + +;; memory.init +(module + (memory 1) + (data "\aa\bb\cc\dd") + + (func (export "init") (param i32 i32 i32) + (memory.init 0 + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0))) +) + +(invoke "init" (i32.const 0) (i32.const 1) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 0)) + +;; Init ending at memory limit and segment limit is ok. +(invoke "init" (i32.const 0xfffc) (i32.const 0) (i32.const 4)) + +;; Out-of-bounds writes trap, and nothing is written. +(assert_trap (invoke "init" (i32.const 0xfffe) (i32.const 0) (i32.const 3)) + "out of bounds memory access") +(assert_return (invoke "load8_u" (i32.const 0xfffe)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 0xffff)) (i32.const 0xdd)) + +;; Succeed when writing 0 bytes at the end of either region. +(invoke "init" (i32.const 0x10000) (i32.const 0) (i32.const 0)) +(invoke "init" (i32.const 0) (i32.const 4) (i32.const 0)) + +;; Writing 0 bytes outside the memory traps. +(assert_trap (invoke "init" (i32.const 0x10001) (i32.const 0) (i32.const 0)) + "out of bounds memory access") +(assert_trap (invoke "init" (i32.const 0) (i32.const 5) (i32.const 0)) + "out of bounds memory access") + +;; data.drop +(module + (memory 1) + (data $p "x") + (data $a (memory 0) (i32.const 0) "x") + + (func (export "drop_passive") (data.drop $p)) + (func (export "init_passive") (param $len i32) + (memory.init $p (i32.const 0) (i32.const 0) (local.get $len))) + + (func (export "drop_active") (data.drop $a)) + (func (export "init_active") (param $len i32) + (memory.init $a (i32.const 0) (i32.const 0) (local.get $len))) +) + +(invoke "init_passive" (i32.const 1)) +(invoke "drop_passive") +(invoke "drop_passive") +(assert_return (invoke "init_passive" (i32.const 0))) +(assert_trap (invoke "init_passive" (i32.const 1)) "out of bounds") +(invoke "init_passive" (i32.const 0)) +(invoke "drop_active") +(assert_return (invoke "init_active" (i32.const 0))) +(assert_trap (invoke "init_active" (i32.const 1)) "out of bounds") +(invoke "init_active" (i32.const 0)) + +;; Test that the data segment index is properly encoded as an unsigned (not +;; signed) LEB. +(module + ;; 65 data segments. 64 is the smallest positive number that is encoded + ;; differently as a signed LEB. + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") + (func (data.drop 64))) + +;; No memory is required for the data.drop instruction. +(module (data "goodbye") (func (data.drop 0))) + +;; table.init +(module + (table 3 funcref) + (elem funcref + (ref.func $zero) (ref.func $one) (ref.func $zero) (ref.func $one)) + + (func $zero (result i32) (i32.const 0)) + (func $one (result i32) (i32.const 1)) + + (func (export "init") (param i32 i32 i32) + (table.init 0 + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "call") (param i32) (result i32) + (call_indirect (result i32) + (local.get 0))) +) + +;; Out-of-bounds stores trap, and nothing is written. +(assert_trap (invoke "init" (i32.const 2) (i32.const 0) (i32.const 2)) + "out of bounds table access") +(assert_trap (invoke "call" (i32.const 2)) + "uninitialized element 2") + +(invoke "init" (i32.const 0) (i32.const 1) (i32.const 2)) +(assert_return (invoke "call" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "call" (i32.const 1)) (i32.const 0)) +(assert_trap (invoke "call" (i32.const 2)) "uninitialized element") + +;; Init ending at table limit and segment limit is ok. +(invoke "init" (i32.const 1) (i32.const 2) (i32.const 2)) + +;; Succeed when storing 0 elements at the end of either region. +(invoke "init" (i32.const 3) (i32.const 0) (i32.const 0)) +(invoke "init" (i32.const 0) (i32.const 4) (i32.const 0)) + +;; Writing 0 elements outside the table traps. +(assert_trap (invoke "init" (i32.const 4) (i32.const 0) (i32.const 0)) + "out of bounds table access") +(assert_trap (invoke "init" (i32.const 0) (i32.const 5) (i32.const 0)) + "out of bounds table access") + + +;; elem.drop +(module + (table 1 funcref) + (func $f) + (elem $p funcref (ref.func $f)) + (elem $a (table 0) (i32.const 0) func $f) + + (func (export "drop_passive") (elem.drop $p)) + (func (export "init_passive") (param $len i32) + (table.init $p (i32.const 0) (i32.const 0) (local.get $len)) + ) + + (func (export "drop_active") (elem.drop $a)) + (func (export "init_active") (param $len i32) + (table.init $a (i32.const 0) (i32.const 0) (local.get $len)) + ) +) + +(invoke "init_passive" (i32.const 1)) +(invoke "drop_passive") +(invoke "drop_passive") +(assert_return (invoke "init_passive" (i32.const 0))) +(assert_trap (invoke "init_passive" (i32.const 1)) "out of bounds") +(invoke "init_passive" (i32.const 0)) +(invoke "drop_active") +(assert_return (invoke "init_active" (i32.const 0))) +(assert_trap (invoke "init_active" (i32.const 1)) "out of bounds") +(invoke "init_active" (i32.const 0)) + +;; Test that the elem segment index is properly encoded as an unsigned (not +;; signed) LEB. +(module + ;; 65 elem segments. 64 is the smallest positive number that is encoded + ;; differently as a signed LEB. + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) + (func (elem.drop 64))) + +;; No table is required for the elem.drop instruction. +(module (elem funcref (ref.func 0)) (func (elem.drop 0))) + +;; table.copy +(module + (table 10 funcref) + (elem (i32.const 0) $zero $one $two) + (func $zero (result i32) (i32.const 0)) + (func $one (result i32) (i32.const 1)) + (func $two (result i32) (i32.const 2)) + + (func (export "copy") (param i32 i32 i32) + (table.copy + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "call") (param i32) (result i32) + (call_indirect (result i32) + (local.get 0))) +) + +;; Non-overlapping copy. +(invoke "copy" (i32.const 3) (i32.const 0) (i32.const 3)) +;; Now [$zero, $one, $two, $zero, $one, $two, ...] +(assert_return (invoke "call" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "call" (i32.const 4)) (i32.const 1)) +(assert_return (invoke "call" (i32.const 5)) (i32.const 2)) + +;; Overlap, source > dest +(invoke "copy" (i32.const 0) (i32.const 1) (i32.const 3)) +;; Now [$one, $two, $zero, $zero, $one, $two, ...] +(assert_return (invoke "call" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "call" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "call" (i32.const 2)) (i32.const 0)) + +;; Overlap, source < dest +(invoke "copy" (i32.const 2) (i32.const 0) (i32.const 3)) +;; Now [$one, $two, $one, $two, $zero, $two, ...] +(assert_return (invoke "call" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "call" (i32.const 3)) (i32.const 2)) +(assert_return (invoke "call" (i32.const 4)) (i32.const 0)) + +;; Copy ending at table limit is ok. +(invoke "copy" (i32.const 6) (i32.const 8) (i32.const 2)) +(invoke "copy" (i32.const 8) (i32.const 6) (i32.const 2)) + +;; Succeed when copying 0 elements at the end of the region. +(invoke "copy" (i32.const 10) (i32.const 0) (i32.const 0)) +(invoke "copy" (i32.const 0) (i32.const 10) (i32.const 0)) + +;; Fail on out-of-bounds when copying 0 elements outside of table. +(assert_trap (invoke "copy" (i32.const 11) (i32.const 0) (i32.const 0)) + "out of bounds") +(assert_trap (invoke "copy" (i32.const 0) (i32.const 11) (i32.const 0)) + "out of bounds") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/custom.wast b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/custom.wast new file mode 100644 index 000000000..b2394f5e3 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/custom.wast @@ -0,0 +1,130 @@ +(module binary + "\00asm" "\01\00\00\00" + "\00\24\10" "a custom section" "this is the payload" + "\00\20\10" "a custom section" "this is payload" + "\00\11\10" "a custom section" "" + "\00\10\00" "" "this is payload" + "\00\01\00" "" "" + "\00\24\10" "\00\00custom sectio\00" "this is the payload" + "\00\24\10" "\ef\bb\bfa custom sect" "this is the payload" + "\00\24\10" "a custom sect\e2\8c\a3" "this is the payload" + "\00\1f\16" "module within a module" "\00asm" "\01\00\00\00" +) + +(module binary + "\00asm" "\01\00\00\00" + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\01\01\00" ;; type section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\02\01\00" ;; import section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\03\01\00" ;; function section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\04\01\00" ;; table section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\05\01\00" ;; memory section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\06\01\00" ;; global section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\07\01\00" ;; export section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\09\01\00" ;; element section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\0a\01\00" ;; code section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\0b\01\00" ;; data section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" +) + +(module binary + "\00asm" "\01\00\00\00" + "\01\07\01\60\02\7f\7f\01\7f" ;; type section + "\00\1a\06" "custom" "this is the payload" ;; custom section + "\03\02\01\00" ;; function section + "\07\0a\01\06\61\64\64\54\77\6f\00\00" ;; export section + "\0a\09\01\07\00\20\00\20\01\6a\0b" ;; code section + "\00\1b\07" "custom2" "this is the payload" ;; custom section +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00" + ) + "unexpected end" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\00" + ) + "unexpected end" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\00\00\05\01\00\07\00\00" + ) + "unexpected end" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\26\10" "a custom section" "this is the payload" + ) + "unexpected end" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\25\10" "a custom section" "this is the payload" + "\00\24\10" "a custom section" "this is the payload" + ) + "malformed section id" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\07\01\60\02\7f\7f\01\7f" ;; type section + "\00\25\10" "a custom section" "this is the payload" ;; wrong length! + "\03\02\01\00" ;; function section + "\0a\09\01\07\00\20\00\20\01\6a\0b" ;; code section + "\00\1b\07" "custom2" "this is the payload" ;; custom section + ) + "function and code section have inconsistent lengths" +) + +;; Test concatenated modules. +(assert_malformed + (module binary + "\00asm\01\00\00\00" + "\00asm\01\00\00\00" + ) + "length out of bounds" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01\00\01" ;; memory section + "\0c\01\02" ;; data count section (2 segments) + "\0b\06\01\00\41\00\0b\00" ;; data section (1 segment) + ) + "data count and data section have inconsistent lengths" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/data.wast b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/data.wast new file mode 100644 index 000000000..aabe1021b --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/data.wast @@ -0,0 +1,345 @@ +;; Test the data section + +;; Syntax + +(module + (memory $m 1) + (data (i32.const 0)) + (data (i32.const 1) "a" "" "bcd") + (data (offset (i32.const 0))) + (data (offset (i32.const 0)) "" "a" "bc" "") + (data (memory 0) (i32.const 0)) + (data (memory 0x0) (i32.const 1) "a" "" "bcd") + (data (memory 0x000) (offset (i32.const 0))) + (data (memory 0) (offset (i32.const 0)) "" "a" "bc" "") + (data (memory $m) (i32.const 0)) + (data (memory $m) (i32.const 1) "a" "" "bcd") + (data (memory $m) (offset (i32.const 0))) + (data (memory $m) (offset (i32.const 0)) "" "a" "bc" "") + (data $d1 (i32.const 0)) + (data $d2 (i32.const 1) "a" "" "bcd") + (data $d3 (offset (i32.const 0))) + (data $d4 (offset (i32.const 0)) "" "a" "bc" "") + (data $d5 (memory 0) (i32.const 0)) + (data $d6 (memory 0x0) (i32.const 1) "a" "" "bcd") + (data $d7 (memory 0x000) (offset (i32.const 0))) + (data $d8 (memory 0) (offset (i32.const 0)) "" "a" "bc" "") + (data $d9 (memory $m) (i32.const 0)) + (data $d10 (memory $m) (i32.const 1) "a" "" "bcd") + (data $d11 (memory $m) (offset (i32.const 0))) + (data $d12 (memory $m) (offset (i32.const 0)) "" "a" "bc" "") +) + +;; Basic use + +(module + (memory 1) + (data (i32.const 0) "a") +) +(module + (import "spectest" "memory" (memory 1)) + (data (i32.const 0) "a") +) + +(module + (memory 1) + (data (i32.const 0) "a") + (data (i32.const 3) "b") + (data (i32.const 100) "cde") + (data (i32.const 5) "x") + (data (i32.const 3) "c") +) +(module + (import "spectest" "memory" (memory 1)) + (data (i32.const 0) "a") + (data (i32.const 1) "b") + (data (i32.const 2) "cde") + (data (i32.const 3) "f") + (data (i32.const 2) "g") + (data (i32.const 1) "h") +) + +(module + (global (import "spectest" "global_i32") i32) + (memory 1) + (data (global.get 0) "a") +) +(module + (global (import "spectest" "global_i32") i32) + (import "spectest" "memory" (memory 1)) + (data (global.get 0) "a") +) + +(module + (global $g (import "spectest" "global_i32") i32) + (memory 1) + (data (global.get $g) "a") +) +(module + (global $g (import "spectest" "global_i32") i32) + (import "spectest" "memory" (memory 1)) + (data (global.get $g) "a") +) + +;; Use of internal globals in constant expressions is not allowed in MVP. +;; (module (memory 1) (data (global.get 0) "a") (global i32 (i32.const 0))) +;; (module (memory 1) (data (global.get $g) "a") (global $g i32 (i32.const 0))) + +;; Corner cases + +(module + (memory 1) + (data (i32.const 0) "a") + (data (i32.const 0xffff) "b") +) +(module + (import "spectest" "memory" (memory 1)) + (data (i32.const 0) "a") + (data (i32.const 0xffff) "b") +) + +(module + (memory 2) + (data (i32.const 0x1_ffff) "a") +) + +(module + (memory 0) + (data (i32.const 0)) +) +(module + (import "spectest" "memory" (memory 0)) + (data (i32.const 0)) +) + +(module + (memory 0 0) + (data (i32.const 0)) +) + +(module + (memory 1) + (data (i32.const 0x1_0000) "") +) + +(module + (memory 0) + (data (i32.const 0) "" "") +) +(module + (import "spectest" "memory" (memory 0)) + (data (i32.const 0) "" "") +) + +(module + (memory 0 0) + (data (i32.const 0) "" "") +) + +(module + (import "spectest" "memory" (memory 0)) + (data (i32.const 0) "a") +) + +(module + (import "spectest" "memory" (memory 0 3)) + (data (i32.const 0) "a") +) + +(module + (global (import "spectest" "global_i32") i32) + (import "spectest" "memory" (memory 0)) + (data (global.get 0) "a") +) + +(module + (global (import "spectest" "global_i32") i32) + (import "spectest" "memory" (memory 0 3)) + (data (global.get 0) "a") +) + +(module + (import "spectest" "memory" (memory 0)) + (data (i32.const 1) "a") +) + +(module + (import "spectest" "memory" (memory 0 3)) + (data (i32.const 1) "a") +) + +;; Invalid bounds for data + +(assert_trap + (module + (memory 0) + (data (i32.const 0) "a") + ) + "out of bounds" +) + +(assert_trap + (module + (memory 0 0) + (data (i32.const 0) "a") + ) + "out of bounds" +) + +(assert_trap + (module + (memory 0 1) + (data (i32.const 0) "a") + ) + "out of bounds" +) +(assert_trap + (module + (memory 0) + (data (i32.const 1)) + ) + "out of bounds" +) +(assert_trap + (module + (memory 0 1) + (data (i32.const 1)) + ) + "out of bounds" +) + +;; This seems to cause a time-out on Travis. +(;assert_unlinkable + (module + (memory 0x10000) + (data (i32.const 0xffffffff) "ab") + ) + "" ;; either out of memory or out of bounds +;) + +(assert_trap + (module + (global (import "spectest" "global_i32") i32) + (memory 0) + (data (global.get 0) "a") + ) + "out of bounds" +) + +(assert_trap + (module + (memory 1 2) + (data (i32.const 0x1_0000) "a") + ) + "out of bounds" +) +(assert_trap + (module + (import "spectest" "memory" (memory 1)) + (data (i32.const 0x1_0000) "a") + ) + "out of bounds" +) + +(assert_trap + (module + (memory 2) + (data (i32.const 0x2_0000) "a") + ) + "out of bounds" +) + +(assert_trap + (module + (memory 2 3) + (data (i32.const 0x2_0000) "a") + ) + "out of bounds" +) + +(assert_trap + (module + (memory 1) + (data (i32.const -1) "a") + ) + "out of bounds" +) +(assert_trap + (module + (import "spectest" "memory" (memory 1)) + (data (i32.const -1) "a") + ) + "out of bounds" +) + +(assert_trap + (module + (memory 2) + (data (i32.const -100) "a") + ) + "out of bounds" +) +(assert_trap + (module + (import "spectest" "memory" (memory 1)) + (data (i32.const -100) "a") + ) + "out of bounds" +) + +;; Data without memory + +(assert_invalid + (module + (data (i32.const 0) "") + ) + "unknown memory" +) + +;; Invalid offsets + +(assert_invalid + (module + (memory 1) + (data (i64.const 0)) + ) + "type mismatch" +) + +(assert_invalid + (module + (memory 1) + (data (i32.ctz (i32.const 0))) + ) + "constant expression required" +) + +(assert_invalid + (module + (memory 1) + (data (nop)) + ) + "constant expression required" +) + +(assert_invalid + (module + (memory 1) + (data (offset (nop) (i32.const 0))) + ) + "constant expression required" +) + +(assert_invalid + (module + (memory 1) + (data (offset (i32.const 0) (nop))) + ) + "constant expression required" +) + +;; Use of internal globals in constant expressions is not allowed in MVP. +;; (assert_invalid +;; (module (memory 1) (data (global.get $g)) (global $g (mut i32) (i32.const 0))) +;; "constant expression required" +;; ) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/elem.wast b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/elem.wast new file mode 100644 index 000000000..bb622ee66 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/elem.wast @@ -0,0 +1,433 @@ +;; Test the element section + +;; Syntax +(module + (table $t 10 funcref) + (func $f) + (func $g) + + ;; Passive + (elem funcref) + (elem funcref (ref.func $f) (ref.func $f) (ref.null) (ref.func $g)) + (elem func) + (elem func $f $f $g $g) + + (elem $p1 funcref) + (elem $p2 funcref (ref.func $f) (ref.func $f) (ref.null) (ref.func $g)) + (elem $p3 func) + (elem $p4 func $f $f $g $g) + + ;; Active + (elem (table $t) (i32.const 0) funcref) + (elem (table $t) (i32.const 0) funcref (ref.func $f) (ref.null)) + (elem (table $t) (i32.const 0) func) + (elem (table $t) (i32.const 0) func $f $g) + (elem (table $t) (offset (i32.const 0)) funcref) + (elem (table $t) (offset (i32.const 0)) func $f $g) + (elem (table 0) (i32.const 0) func) + (elem (table 0x0) (i32.const 0) func $f $f) + (elem (table 0x000) (offset (i32.const 0)) func) + (elem (table 0) (offset (i32.const 0)) func $f $f) + (elem (table $t) (i32.const 0) func) + (elem (table $t) (i32.const 0) func $f $f) + (elem (table $t) (offset (i32.const 0)) func) + (elem (table $t) (offset (i32.const 0)) func $f $f) + (elem (offset (i32.const 0))) + (elem (offset (i32.const 0)) funcref (ref.func $f) (ref.null)) + (elem (offset (i32.const 0)) func $f $f) + (elem (offset (i32.const 0)) $f $f) + (elem (i32.const 0)) + (elem (i32.const 0) funcref (ref.func $f) (ref.null)) + (elem (i32.const 0) func $f $f) + (elem (i32.const 0) $f $f) + + (elem $a1 (table $t) (i32.const 0) funcref) + (elem $a2 (table $t) (i32.const 0) funcref (ref.func $f) (ref.null)) + (elem $a3 (table $t) (i32.const 0) func) + (elem $a4 (table $t) (i32.const 0) func $f $g) + (elem $a9 (table $t) (offset (i32.const 0)) funcref) + (elem $a10 (table $t) (offset (i32.const 0)) func $f $g) + (elem $a11 (table 0) (i32.const 0) func) + (elem $a12 (table 0x0) (i32.const 0) func $f $f) + (elem $a13 (table 0x000) (offset (i32.const 0)) func) + (elem $a14 (table 0) (offset (i32.const 0)) func $f $f) + (elem $a15 (table $t) (i32.const 0) func) + (elem $a16 (table $t) (i32.const 0) func $f $f) + (elem $a17 (table $t) (offset (i32.const 0)) func) + (elem $a18 (table $t) (offset (i32.const 0)) func $f $f) + (elem $a19 (offset (i32.const 0))) + (elem $a20 (offset (i32.const 0)) funcref (ref.func $f) (ref.null)) + (elem $a21 (offset (i32.const 0)) func $f $f) + (elem $a22 (offset (i32.const 0)) $f $f) + (elem $a23 (i32.const 0)) + (elem $a24 (i32.const 0) funcref (ref.func $f) (ref.null)) + (elem $a25 (i32.const 0) func $f $f) + (elem $a26 (i32.const 0) $f $f) +) + +(module + (func $f) + (func $g) + + (table $t funcref (elem (ref.func $f) (ref.null) (ref.func $g))) +) +;; Basic use + +(module + (table 10 funcref) + (func $f) + (elem (i32.const 0) $f) +) +(module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 0) $f) +) + +(module + (table 10 funcref) + (func $f) + (elem (i32.const 0) $f) + (elem (i32.const 3) $f) + (elem (i32.const 7) $f) + (elem (i32.const 5) $f) + (elem (i32.const 3) $f) +) +(module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 9) $f) + (elem (i32.const 3) $f) + (elem (i32.const 7) $f) + (elem (i32.const 3) $f) + (elem (i32.const 5) $f) +) + +(module + (global (import "spectest" "global_i32") i32) + (table 1000 funcref) + (func $f) + (elem (global.get 0) $f) +) + +(module + (global $g (import "spectest" "global_i32") i32) + (table 1000 funcref) + (func $f) + (elem (global.get $g) $f) +) + +(module + (type $out-i32 (func (result i32))) + (table 10 funcref) + (elem (i32.const 7) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-7") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 7)) + ) + (func (export "call-9") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) +(assert_return (invoke "call-7") (i32.const 65)) +(assert_return (invoke "call-9") (i32.const 66)) + +;; Corner cases + +(module + (table 10 funcref) + (func $f) + (elem (i32.const 9) $f) +) +(module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 9) $f) +) + +(module + (table 0 funcref) + (elem (i32.const 0)) +) +(module + (import "spectest" "table" (table 0 funcref)) + (elem (i32.const 0)) +) + +(module + (table 0 0 funcref) + (elem (i32.const 0)) +) + +(module + (table 20 funcref) + (elem (i32.const 20)) +) + +(module + (import "spectest" "table" (table 0 funcref)) + (func $f) + (elem (i32.const 0) $f) +) + +(module + (import "spectest" "table" (table 0 100 funcref)) + (func $f) + (elem (i32.const 0) $f) +) + +(module + (import "spectest" "table" (table 0 funcref)) + (func $f) + (elem (i32.const 1) $f) +) + +(module + (import "spectest" "table" (table 0 30 funcref)) + (func $f) + (elem (i32.const 1) $f) +) + +;; Invalid bounds for elements + +(assert_trap + (module + (table 0 funcref) + (func $f) + (elem (i32.const 0) $f) + ) + "out of bounds" +) + +(assert_trap + (module + (table 0 0 funcref) + (func $f) + (elem (i32.const 0) $f) + ) + "out of bounds" +) + +(assert_trap + (module + (table 0 1 funcref) + (func $f) + (elem (i32.const 0) $f) + ) + "out of bounds" +) + +(assert_trap + (module + (table 0 funcref) + (elem (i32.const 1)) + ) + "out of bounds" +) +(assert_trap + (module + (table 10 funcref) + (func $f) + (elem (i32.const 10) $f) + ) + "out of bounds" +) +(assert_trap + (module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 10) $f) + ) + "out of bounds" +) + +(assert_trap + (module + (table 10 20 funcref) + (func $f) + (elem (i32.const 10) $f) + ) + "out of bounds" +) +(assert_trap + (module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 10) $f) + ) + "out of bounds" +) + +(assert_trap + (module + (table 10 funcref) + (func $f) + (elem (i32.const -1) $f) + ) + "out of bounds" +) +(assert_trap + (module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const -1) $f) + ) + "out of bounds" +) + +(assert_trap + (module + (table 10 funcref) + (func $f) + (elem (i32.const -10) $f) + ) + "out of bounds" +) +(assert_trap + (module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const -10) $f) + ) + "out of bounds" +) + +;; Element without table + +(assert_invalid + (module + (func $f) + (elem (i32.const 0) $f) + ) + "unknown table" +) + +;; Invalid offsets + +(assert_invalid + (module + (table 1 funcref) + (elem (i64.const 0)) + ) + "type mismatch" +) + +(assert_invalid + (module + (table 1 funcref) + (elem (i32.ctz (i32.const 0))) + ) + "constant expression required" +) + +(assert_invalid + (module + (table 1 funcref) + (elem (nop)) + ) + "constant expression required" +) + +(assert_invalid + (module + (table 1 funcref) + (elem (offset (nop) (i32.const 0))) + ) + "constant expression required" +) + +(assert_invalid + (module + (table 1 funcref) + (elem (offset (i32.const 0) (nop))) + ) + "constant expression required" +) + +;; Use of internal globals in constant expressions is not allowed in MVP. +;; (assert_invalid +;; (module (memory 1) (data (global.get $g)) (global $g (mut i32) (i32.const 0))) +;; "constant expression required" +;; ) + +;; Two elements target the same slot + +(module + (type $out-i32 (func (result i32))) + (table 10 funcref) + (elem (i32.const 9) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-overwritten") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) +(assert_return (invoke "call-overwritten") (i32.const 66)) + +(module + (type $out-i32 (func (result i32))) + (import "spectest" "table" (table 10 funcref)) + (elem (i32.const 9) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-overwritten-element") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) +(assert_return (invoke "call-overwritten-element") (i32.const 66)) + +;; Element sections across multiple modules change the same table + +(module $module1 + (type $out-i32 (func (result i32))) + (table (export "shared-table") 10 funcref) + (elem (i32.const 8) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-7") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 7)) + ) + (func (export "call-8") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 8)) + ) + (func (export "call-9") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) + +(register "module1" $module1) + +(assert_trap (invoke $module1 "call-7") "uninitialized element") +(assert_return (invoke $module1 "call-8") (i32.const 65)) +(assert_return (invoke $module1 "call-9") (i32.const 66)) + +(module $module2 + (type $out-i32 (func (result i32))) + (import "module1" "shared-table" (table 10 funcref)) + (elem (i32.const 7) $const-i32-c) + (elem (i32.const 8) $const-i32-d) + (func $const-i32-c (type $out-i32) (i32.const 67)) + (func $const-i32-d (type $out-i32) (i32.const 68)) +) + +(assert_return (invoke $module1 "call-7") (i32.const 67)) +(assert_return (invoke $module1 "call-8") (i32.const 68)) +(assert_return (invoke $module1 "call-9") (i32.const 66)) + +(module $module3 + (type $out-i32 (func (result i32))) + (import "module1" "shared-table" (table 10 funcref)) + (elem (i32.const 8) $const-i32-e) + (elem (i32.const 9) $const-i32-f) + (func $const-i32-e (type $out-i32) (i32.const 69)) + (func $const-i32-f (type $out-i32) (i32.const 70)) +) + +(assert_return (invoke $module1 "call-7") (i32.const 67)) +(assert_return (invoke $module1 "call-8") (i32.const 69)) +(assert_return (invoke $module1 "call-9") (i32.const 70)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/imports.wast b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/imports.wast new file mode 100644 index 000000000..d987775ec --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/imports.wast @@ -0,0 +1,593 @@ +;; Auxiliary module to import from + +(module + (func (export "func")) + (func (export "func-i32") (param i32)) + (func (export "func-f32") (param f32)) + (func (export "func->i32") (result i32) (i32.const 22)) + (func (export "func->f32") (result f32) (f32.const 11)) + (func (export "func-i32->i32") (param i32) (result i32) (local.get 0)) + (func (export "func-i64->i64") (param i64) (result i64) (local.get 0)) + (global (export "global-i32") i32 (i32.const 55)) + (global (export "global-f32") f32 (f32.const 44)) + (table (export "table-10-inf") 10 funcref) + ;; (table (export "table-10-20") 10 20 funcref) + (memory (export "memory-2-inf") 2) + ;; (memory (export "memory-2-4") 2 4) +) + +(register "test") + + +;; Functions + +(module + (type $func_i32 (func (param i32))) + (type $func_i64 (func (param i64))) + (type $func_f32 (func (param f32))) + (type $func_f64 (func (param f64))) + + (import "spectest" "print_i32" (func (param i32))) + ;; JavaScript can't handle i64 yet. + ;; (func (import "spectest" "print_i64") (param i64)) + (import "spectest" "print_i32" (func $print_i32 (param i32))) + ;; JavaScript can't handle i64 yet. + ;; (import "spectest" "print_i64" (func $print_i64 (param i64))) + (import "spectest" "print_f32" (func $print_f32 (param f32))) + (import "spectest" "print_f64" (func $print_f64 (param f64))) + (import "spectest" "print_i32_f32" (func $print_i32_f32 (param i32 f32))) + (import "spectest" "print_f64_f64" (func $print_f64_f64 (param f64 f64))) + (func $print_i32-2 (import "spectest" "print_i32") (param i32)) + (func $print_f64-2 (import "spectest" "print_f64") (param f64)) + (import "test" "func-i64->i64" (func $i64->i64 (param i64) (result i64))) + + (func (export "p1") (import "spectest" "print_i32") (param i32)) + (func $p (export "p2") (import "spectest" "print_i32") (param i32)) + (func (export "p3") (export "p4") (import "spectest" "print_i32") (param i32)) + (func (export "p5") (import "spectest" "print_i32") (type 0)) + (func (export "p6") (import "spectest" "print_i32") (type 0) (param i32) (result)) + + (import "spectest" "print_i32" (func (type $forward))) + (func (import "spectest" "print_i32") (type $forward)) + (type $forward (func (param i32))) + + (table funcref (elem $print_i32 $print_f64)) + + (func (export "print32") (param $i i32) + (local $x f32) + (local.set $x (f32.convert_i32_s (local.get $i))) + (call 0 (local.get $i)) + (call $print_i32_f32 + (i32.add (local.get $i) (i32.const 1)) + (f32.const 42) + ) + (call $print_i32 (local.get $i)) + (call $print_i32-2 (local.get $i)) + (call $print_f32 (local.get $x)) + (call_indirect (type $func_i32) (local.get $i) (i32.const 0)) + ) + + (func (export "print64") (param $i i64) + (local $x f64) + (local.set $x (f64.convert_i64_s (call $i64->i64 (local.get $i)))) + ;; JavaScript can't handle i64 yet. + ;; (call 1 (local.get $i)) + (call $print_f64_f64 + (f64.add (local.get $x) (f64.const 1)) + (f64.const 53) + ) + ;; JavaScript can't handle i64 yet. + ;; (call $print_i64 (local.get $i)) + (call $print_f64 (local.get $x)) + (call $print_f64-2 (local.get $x)) + (call_indirect (type $func_f64) (local.get $x) (i32.const 1)) + ) +) + +(assert_return (invoke "print32" (i32.const 13))) +(assert_return (invoke "print64" (i64.const 24))) + +(assert_invalid + (module + (type (func (result i32))) + (import "test" "func" (func (type 1))) + ) + "unknown type" +) + +(module (import "test" "func" (func))) +(module (import "test" "func-i32" (func (param i32)))) +(module (import "test" "func-f32" (func (param f32)))) +(module (import "test" "func->i32" (func (result i32)))) +(module (import "test" "func->f32" (func (result f32)))) +(module (import "test" "func-i32->i32" (func (param i32) (result i32)))) +(module (import "test" "func-i64->i64" (func (param i64) (result i64)))) + +(assert_unlinkable + (module (import "test" "unknown" (func))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (func))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "func" (func (param i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func" (func (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func" (func (param i32) (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (param f32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (param i64)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (param i32) (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (param i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (result f32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (result i64)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (param i32) (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32->i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32->i32" (func (param i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32->i32" (func (result i32)))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "test" "global-i32" (func (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "global_i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (func))) + "incompatible import type" +) + + +;; Globals + +(module + (import "spectest" "global_i32" (global i32)) + (global (import "spectest" "global_i32") i32) + + (import "spectest" "global_i32" (global $x i32)) + (global $y (import "spectest" "global_i32") i32) + + ;; JavaScript can't handle i64 yet. + ;; (import "spectest" "global_i64" (global i64)) + (import "spectest" "global_f32" (global f32)) + (import "spectest" "global_f64" (global f64)) + + (func (export "get-0") (result i32) (global.get 0)) + (func (export "get-1") (result i32) (global.get 1)) + (func (export "get-x") (result i32) (global.get $x)) + (func (export "get-y") (result i32) (global.get $y)) +) + +(assert_return (invoke "get-0") (i32.const 666)) +(assert_return (invoke "get-1") (i32.const 666)) +(assert_return (invoke "get-x") (i32.const 666)) +(assert_return (invoke "get-y") (i32.const 666)) + +(module (import "test" "global-i32" (global i32))) +(module (import "test" "global-f32" (global f32))) + +(assert_unlinkable + (module (import "test" "unknown" (global i32))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (global i32))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "func" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "print_i32" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (global i32))) + "incompatible import type" +) + + +;; Tables + +(module + (type (func (result i32))) + (import "spectest" "table" (table 10 20 funcref)) + (elem (table 0) (i32.const 1) func $f $g) + + (func (export "call") (param i32) (result i32) + (call_indirect (type 0) (local.get 0)) + ) + (func $f (result i32) (i32.const 11)) + (func $g (result i32) (i32.const 22)) +) + +(assert_trap (invoke "call" (i32.const 0)) "uninitialized element") +(assert_return (invoke "call" (i32.const 1)) (i32.const 11)) +(assert_return (invoke "call" (i32.const 2)) (i32.const 22)) +(assert_trap (invoke "call" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "call" (i32.const 100)) "undefined element") + + +(module + (type (func (result i32))) + (table (import "spectest" "table") 10 20 funcref) + (elem (table 0) (i32.const 1) func $f $g) + + (func (export "call") (param i32) (result i32) + (call_indirect (type 0) (local.get 0)) + ) + (func $f (result i32) (i32.const 11)) + (func $g (result i32) (i32.const 22)) +) + +(assert_trap (invoke "call" (i32.const 0)) "uninitialized element") +(assert_return (invoke "call" (i32.const 1)) (i32.const 11)) +(assert_return (invoke "call" (i32.const 2)) (i32.const 22)) +(assert_trap (invoke "call" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "call" (i32.const 100)) "undefined element") + + +(assert_invalid + (module (import "" "" (table 10 funcref)) (import "" "" (table 10 funcref))) + "multiple tables" +) +(assert_invalid + (module (import "" "" (table 10 funcref)) (table 10 funcref)) + "multiple tables" +) +(assert_invalid + (module (table 10 funcref) (table 10 funcref)) + "multiple tables" +) + +(module (import "test" "table-10-inf" (table 10 funcref))) +(module (import "test" "table-10-inf" (table 5 funcref))) +(module (import "test" "table-10-inf" (table 0 funcref))) +(module (import "spectest" "table" (table 10 funcref))) +(module (import "spectest" "table" (table 5 funcref))) +(module (import "spectest" "table" (table 0 funcref))) +(module (import "spectest" "table" (table 10 20 funcref))) +(module (import "spectest" "table" (table 5 20 funcref))) +(module (import "spectest" "table" (table 0 20 funcref))) +(module (import "spectest" "table" (table 10 25 funcref))) +(module (import "spectest" "table" (table 5 25 funcref))) + +(assert_unlinkable + (module (import "test" "unknown" (table 10 funcref))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (table 10 funcref))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "table-10-inf" (table 12 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (table 10 20 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (table 12 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (table 10 15 funcref))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "test" "func" (table 10 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-i32" (table 10 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (table 10 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "print_i32" (table 10 funcref))) + "incompatible import type" +) + + + +;; Memories + +(module + (import "spectest" "memory" (memory 1 2)) + (data (memory 0) (i32.const 10) "\10") + + (func (export "load") (param i32) (result i32) (i32.load (local.get 0))) +) + +(assert_return (invoke "load" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load" (i32.const 10)) (i32.const 16)) +(assert_return (invoke "load" (i32.const 8)) (i32.const 0x100000)) +(assert_trap (invoke "load" (i32.const 1000000)) "out of bounds memory access") + +(module + (memory (import "spectest" "memory") 1 2) + (data (memory 0) (i32.const 10) "\10") + + (func (export "load") (param i32) (result i32) (i32.load (local.get 0))) +) +(assert_return (invoke "load" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load" (i32.const 10)) (i32.const 16)) +(assert_return (invoke "load" (i32.const 8)) (i32.const 0x100000)) +(assert_trap (invoke "load" (i32.const 1000000)) "out of bounds memory access") + +(assert_invalid + (module (import "" "" (memory 1)) (import "" "" (memory 1))) + "multiple memories" +) +(assert_invalid + (module (import "" "" (memory 1)) (memory 0)) + "multiple memories" +) +(assert_invalid + (module (memory 0) (memory 0)) + "multiple memories" +) + +(module (import "test" "memory-2-inf" (memory 2))) +(module (import "test" "memory-2-inf" (memory 1))) +(module (import "test" "memory-2-inf" (memory 0))) +(module (import "spectest" "memory" (memory 1))) +(module (import "spectest" "memory" (memory 0))) +(module (import "spectest" "memory" (memory 1 2))) +(module (import "spectest" "memory" (memory 0 2))) +(module (import "spectest" "memory" (memory 1 3))) +(module (import "spectest" "memory" (memory 0 3))) + +(assert_unlinkable + (module (import "test" "unknown" (memory 1))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (memory 1))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "memory-2-inf" (memory 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (memory 2 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (memory 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (memory 1 1))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "test" "func-i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "print_i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "global_i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (memory 1))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "spectest" "memory" (memory 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (memory 1 1))) + "incompatible import type" +) + +(module + (import "spectest" "memory" (memory 0 3)) ;; actual has max size 2 + (func (export "grow") (param i32) (result i32) (memory.grow (local.get 0))) +) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 2)) + + +;; Syntax errors + +(assert_malformed + (module quote "(func) (import \"\" \"\" (func))") + "import after function" +) +(assert_malformed + (module quote "(func) (import \"\" \"\" (global i64))") + "import after function" +) +(assert_malformed + (module quote "(func) (import \"\" \"\" (table 0 funcref))") + "import after function" +) +(assert_malformed + (module quote "(func) (import \"\" \"\" (memory 0))") + "import after function" +) + +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (func))") + "import after global" +) +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (global f32))") + "import after global" +) +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (table 0 funcref))") + "import after global" +) +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (memory 0))") + "import after global" +) + +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (func))") + "import after table" +) +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (global i32))") + "import after table" +) +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (table 0 funcref))") + "import after table" +) +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (memory 0))") + "import after table" +) + +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (func))") + "import after memory" +) +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (global i32))") + "import after memory" +) +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (table 1 3 funcref))") + "import after memory" +) +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (memory 1 2))") + "import after memory" +) + +;; This module is required to validate, regardless of whether it can be +;; linked. Overloading is not possible in wasm itself, but it is possible +;; in modules from which wasm can import. +(module) +(register "not wasm") +(assert_unlinkable + (module + (import "not wasm" "overloaded" (func)) + (import "not wasm" "overloaded" (func (param i32))) + (import "not wasm" "overloaded" (func (param i32 i32))) + (import "not wasm" "overloaded" (func (param i64))) + (import "not wasm" "overloaded" (func (param f32))) + (import "not wasm" "overloaded" (func (param f64))) + (import "not wasm" "overloaded" (func (result i32))) + (import "not wasm" "overloaded" (func (result i64))) + (import "not wasm" "overloaded" (func (result f32))) + (import "not wasm" "overloaded" (func (result f64))) + (import "not wasm" "overloaded" (global i32)) + (import "not wasm" "overloaded" (global i64)) + (import "not wasm" "overloaded" (global f32)) + (import "not wasm" "overloaded" (global f64)) + (import "not wasm" "overloaded" (table 0 funcref)) + (import "not wasm" "overloaded" (memory 0)) + ) + "unknown import" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/linking.wast b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/linking.wast new file mode 100644 index 000000000..2d92078f9 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/linking.wast @@ -0,0 +1,392 @@ +;; Functions + +(module $Mf + (func (export "call") (result i32) (call $g)) + (func $g (result i32) (i32.const 2)) +) +(register "Mf" $Mf) + +(module $Nf + (func $f (import "Mf" "call") (result i32)) + (export "Mf.call" (func $f)) + (func (export "call Mf.call") (result i32) (call $f)) + (func (export "call") (result i32) (call $g)) + (func $g (result i32) (i32.const 3)) +) + +(assert_return (invoke $Mf "call") (i32.const 2)) +(assert_return (invoke $Nf "Mf.call") (i32.const 2)) +(assert_return (invoke $Nf "call") (i32.const 3)) +(assert_return (invoke $Nf "call Mf.call") (i32.const 2)) + +(module + (import "spectest" "print_i32" (func $f (param i32))) + (export "print" (func $f)) +) +(register "reexport_f") +(assert_unlinkable + (module (import "reexport_f" "print" (func (param i64)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "reexport_f" "print" (func (param i32) (result i32)))) + "incompatible import type" +) + + +;; Globals + +(module $Mg + (global $glob (export "glob") i32 (i32.const 42)) + (func (export "get") (result i32) (global.get $glob)) + + ;; export mutable globals + (global $mut_glob (export "mut_glob") (mut i32) (i32.const 142)) + (func (export "get_mut") (result i32) (global.get $mut_glob)) + (func (export "set_mut") (param i32) (global.set $mut_glob (local.get 0))) +) +(register "Mg" $Mg) + +(module $Ng + (global $x (import "Mg" "glob") i32) + (global $mut_glob (import "Mg" "mut_glob") (mut i32)) + (func $f (import "Mg" "get") (result i32)) + (func $get_mut (import "Mg" "get_mut") (result i32)) + (func $set_mut (import "Mg" "set_mut") (param i32)) + + (export "Mg.glob" (global $x)) + (export "Mg.get" (func $f)) + (global $glob (export "glob") i32 (i32.const 43)) + (func (export "get") (result i32) (global.get $glob)) + + (export "Mg.mut_glob" (global $mut_glob)) + (export "Mg.get_mut" (func $get_mut)) + (export "Mg.set_mut" (func $set_mut)) +) + +(assert_return (get $Mg "glob") (i32.const 42)) +(assert_return (get $Ng "Mg.glob") (i32.const 42)) +(assert_return (get $Ng "glob") (i32.const 43)) +(assert_return (invoke $Mg "get") (i32.const 42)) +(assert_return (invoke $Ng "Mg.get") (i32.const 42)) +(assert_return (invoke $Ng "get") (i32.const 43)) + +(assert_return (get $Mg "mut_glob") (i32.const 142)) +(assert_return (get $Ng "Mg.mut_glob") (i32.const 142)) +(assert_return (invoke $Mg "get_mut") (i32.const 142)) +(assert_return (invoke $Ng "Mg.get_mut") (i32.const 142)) + +(assert_return (invoke $Mg "set_mut" (i32.const 241))) +(assert_return (get $Mg "mut_glob") (i32.const 241)) +(assert_return (get $Ng "Mg.mut_glob") (i32.const 241)) +(assert_return (invoke $Mg "get_mut") (i32.const 241)) +(assert_return (invoke $Ng "Mg.get_mut") (i32.const 241)) + + +(assert_unlinkable + (module (import "Mg" "mut_glob" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "Mg" "glob" (global (mut i32)))) + "incompatible import type" +) + +;; Tables + +(module $Mt + (type (func (result i32))) + (type (func)) + + (table (export "tab") 10 funcref) + (elem (i32.const 2) $g $g $g $g) + (func $g (result i32) (i32.const 4)) + (func (export "h") (result i32) (i32.const -4)) + + (func (export "call") (param i32) (result i32) + (call_indirect (type 0) (local.get 0)) + ) +) +(register "Mt" $Mt) + +(module $Nt + (type (func)) + (type (func (result i32))) + + (func $f (import "Mt" "call") (param i32) (result i32)) + (func $h (import "Mt" "h") (result i32)) + + (table funcref (elem $g $g $g $h $f)) + (func $g (result i32) (i32.const 5)) + + (export "Mt.call" (func $f)) + (func (export "call Mt.call") (param i32) (result i32) + (call $f (local.get 0)) + ) + (func (export "call") (param i32) (result i32) + (call_indirect (type 1) (local.get 0)) + ) +) + +(assert_return (invoke $Mt "call" (i32.const 2)) (i32.const 4)) +(assert_return (invoke $Nt "Mt.call" (i32.const 2)) (i32.const 4)) +(assert_return (invoke $Nt "call" (i32.const 2)) (i32.const 5)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 2)) (i32.const 4)) + +(assert_trap (invoke $Mt "call" (i32.const 1)) "uninitialized") +(assert_trap (invoke $Nt "Mt.call" (i32.const 1)) "uninitialized") +(assert_return (invoke $Nt "call" (i32.const 1)) (i32.const 5)) +(assert_trap (invoke $Nt "call Mt.call" (i32.const 1)) "uninitialized") + +(assert_trap (invoke $Mt "call" (i32.const 0)) "uninitialized") +(assert_trap (invoke $Nt "Mt.call" (i32.const 0)) "uninitialized") +(assert_return (invoke $Nt "call" (i32.const 0)) (i32.const 5)) +(assert_trap (invoke $Nt "call Mt.call" (i32.const 0)) "uninitialized") + +(assert_trap (invoke $Mt "call" (i32.const 20)) "undefined") +(assert_trap (invoke $Nt "Mt.call" (i32.const 20)) "undefined") +(assert_trap (invoke $Nt "call" (i32.const 7)) "undefined") +(assert_trap (invoke $Nt "call Mt.call" (i32.const 20)) "undefined") + +(assert_return (invoke $Nt "call" (i32.const 3)) (i32.const -4)) +(assert_trap (invoke $Nt "call" (i32.const 4)) "indirect call") + +(module $Ot + (type (func (result i32))) + + (func $h (import "Mt" "h") (result i32)) + (table (import "Mt" "tab") 5 funcref) + (elem (i32.const 1) $i $h) + (func $i (result i32) (i32.const 6)) + + (func (export "call") (param i32) (result i32) + (call_indirect (type 0) (local.get 0)) + ) +) + +(assert_return (invoke $Mt "call" (i32.const 3)) (i32.const 4)) +(assert_return (invoke $Nt "Mt.call" (i32.const 3)) (i32.const 4)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 3)) (i32.const 4)) +(assert_return (invoke $Ot "call" (i32.const 3)) (i32.const 4)) + +(assert_return (invoke $Mt "call" (i32.const 2)) (i32.const -4)) +(assert_return (invoke $Nt "Mt.call" (i32.const 2)) (i32.const -4)) +(assert_return (invoke $Nt "call" (i32.const 2)) (i32.const 5)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 2)) (i32.const -4)) +(assert_return (invoke $Ot "call" (i32.const 2)) (i32.const -4)) + +(assert_return (invoke $Mt "call" (i32.const 1)) (i32.const 6)) +(assert_return (invoke $Nt "Mt.call" (i32.const 1)) (i32.const 6)) +(assert_return (invoke $Nt "call" (i32.const 1)) (i32.const 5)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 1)) (i32.const 6)) +(assert_return (invoke $Ot "call" (i32.const 1)) (i32.const 6)) + +(assert_trap (invoke $Mt "call" (i32.const 0)) "uninitialized") +(assert_trap (invoke $Nt "Mt.call" (i32.const 0)) "uninitialized") +(assert_return (invoke $Nt "call" (i32.const 0)) (i32.const 5)) +(assert_trap (invoke $Nt "call Mt.call" (i32.const 0)) "uninitialized") +(assert_trap (invoke $Ot "call" (i32.const 0)) "uninitialized") + +(assert_trap (invoke $Ot "call" (i32.const 20)) "undefined") + +(module + (table (import "Mt" "tab") 0 funcref) + (elem (i32.const 9) $f) + (func $f) +) + +(module $G1 (global (export "g") i32 (i32.const 5))) +(register "G1" $G1) +(module $G2 + (global (import "G1" "g") i32) + (global (export "g") i32 (global.get 0)) +) +(assert_return (get $G2 "g") (i32.const 5)) + +(assert_trap + (module + (table (import "Mt" "tab") 0 funcref) + (elem (i32.const 10) $f) + (func $f) + ) + "out of bounds" +) + +(assert_unlinkable + (module + (table (import "Mt" "tab") 10 funcref) + (memory (import "Mt" "mem") 1) ;; does not exist + (func $f (result i32) (i32.const 0)) + (elem (i32.const 7) $f) + (elem (i32.const 9) $f) + ) + "unknown import" +) +(assert_trap (invoke $Mt "call" (i32.const 7)) "uninitialized") + +;; Unlike in the v1 spec, the elements stored before an out-of-bounds access +;; persist after the instantiation failure. +(assert_trap + (module + (table (import "Mt" "tab") 10 funcref) + (func $f (result i32) (i32.const 0)) + (elem (i32.const 7) $f) + (elem (i32.const 12) $f) ;; out of bounds + ) + "out of bounds" +) +(assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0)) + +(assert_trap + (module + (table (import "Mt" "tab") 10 funcref) + (func $f (result i32) (i32.const 0)) + (elem (i32.const 7) $f) + (memory 1) + (data (i32.const 0x10000) "d") ;; out of bounds + ) + "out of bounds" +) +(assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0)) + + +;; Memories + +(module $Mm + (memory (export "mem") 1 5) + (data (i32.const 10) "\00\01\02\03\04\05\06\07\08\09") + + (func (export "load") (param $a i32) (result i32) + (i32.load8_u (local.get 0)) + ) +) +(register "Mm" $Mm) + +(module $Nm + (func $loadM (import "Mm" "load") (param i32) (result i32)) + + (memory 1) + (data (i32.const 10) "\f0\f1\f2\f3\f4\f5") + + (export "Mm.load" (func $loadM)) + (func (export "load") (param $a i32) (result i32) + (i32.load8_u (local.get 0)) + ) +) + +(assert_return (invoke $Mm "load" (i32.const 12)) (i32.const 2)) +(assert_return (invoke $Nm "Mm.load" (i32.const 12)) (i32.const 2)) +(assert_return (invoke $Nm "load" (i32.const 12)) (i32.const 0xf2)) + +(module $Om + (memory (import "Mm" "mem") 1) + (data (i32.const 5) "\a0\a1\a2\a3\a4\a5\a6\a7") + + (func (export "load") (param $a i32) (result i32) + (i32.load8_u (local.get 0)) + ) +) + +(assert_return (invoke $Mm "load" (i32.const 12)) (i32.const 0xa7)) +(assert_return (invoke $Nm "Mm.load" (i32.const 12)) (i32.const 0xa7)) +(assert_return (invoke $Nm "load" (i32.const 12)) (i32.const 0xf2)) +(assert_return (invoke $Om "load" (i32.const 12)) (i32.const 0xa7)) + +(module + (memory (import "Mm" "mem") 0) + (data (i32.const 0xffff) "a") +) + +(assert_trap + (module + (memory (import "Mm" "mem") 0) + (data (i32.const 0x10000) "a") + ) + "out of bounds" +) + +(module $Pm + (memory (import "Mm" "mem") 1 8) + + (func (export "grow") (param $a i32) (result i32) + (memory.grow (local.get 0)) + ) +) + +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 1)) +(assert_return (invoke $Pm "grow" (i32.const 2)) (i32.const 1)) +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 3)) +(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const 3)) +(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const 4)) +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5)) +(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const -1)) +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5)) + +(assert_unlinkable + (module + (func $host (import "spectest" "print")) + (memory (import "Mm" "mem") 1) + (table (import "Mm" "tab") 0 funcref) ;; does not exist + (data (i32.const 0) "abc") + ) + "unknown import" +) +(assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 0)) + +;; Unlike in v1 spec, bytes written before an out-of-bounds access persist +;; after the instantiation failure. +(assert_trap + (module + (memory (import "Mm" "mem") 1) + (data (i32.const 0) "abc") + (data (i32.const 0x50000) "d") ;; out of bounds + ) + "out of bounds" +) +(assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 97)) + +(assert_trap + (module + (memory (import "Mm" "mem") 1) + (data (i32.const 0) "abc") + (table 0 funcref) + (func) + (elem (i32.const 0) 0) ;; out of bounds + ) + "out of bounds" +) +(assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 97)) + +;; Store is modified if the start function traps. +(module $Ms + (type $t (func (result i32))) + (memory (export "memory") 1) + (table (export "table") 1 funcref) + (func (export "get memory[0]") (type $t) + (i32.load8_u (i32.const 0)) + ) + (func (export "get table[0]") (type $t) + (call_indirect (type $t) (i32.const 0)) + ) +) +(register "Ms" $Ms) + +(assert_trap + (module + (import "Ms" "memory" (memory 1)) + (import "Ms" "table" (table 1 funcref)) + (data (i32.const 0) "hello") + (elem (i32.const 0) $f) + (func $f (result i32) + (i32.const 0xdead) + ) + (func $main + (unreachable) + ) + (start $main) + ) + "unreachable" +) + +(assert_return (invoke $Ms "get memory[0]") (i32.const 104)) ;; 'h' +(assert_return (invoke $Ms "get table[0]") (i32.const 0xdead)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/memory_copy.wast b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/memory_copy.wast new file mode 100644 index 000000000..692e1ad85 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/memory_copy.wast @@ -0,0 +1,5577 @@ +;; +;; Generated by ../meta/generate_memory_copy.js +;; + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (nop)) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 13) (i32.const 2) (i32.const 3))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 25) (i32.const 15) (i32.const 2))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 13) (i32.const 25) (i32.const 3))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 20) (i32.const 22) (i32.const 4))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 25) (i32.const 1) (i32.const 3))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 10) (i32.const 12) (i32.const 7))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 12) (i32.const 10) (i32.const 7))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65516) (i32.const 0) (i32.const 40)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 19)) +(assert_return (invoke "load8_u" (i32.const 218)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 417)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 616)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 815)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1014)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1213)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1412)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1611)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1810)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2009)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2208)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2407)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2606)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2805)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3004)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3203)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3402)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3601)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3800)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3999)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65490)) (i32.const 0)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65515) (i32.const 0) (i32.const 39)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 19)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 20)) +(assert_return (invoke "load8_u" (i32.const 219)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 418)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 617)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 816)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1015)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1214)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1413)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1612)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1811)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2010)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2209)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2408)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2607)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2806)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3005)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3204)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3403)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3602)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3801)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4000)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4199)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4398)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4597)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4796)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4995)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5194)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5393)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5592)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5791)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5990)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6189)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6388)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6587)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6786)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6985)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7184)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7383)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7582)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7781)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7980)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8179)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8378)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8577)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8776)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8975)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9174)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9373)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9572)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9771)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9970)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10169)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10368)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10567)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10766)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10965)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11164)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11363)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11562)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11761)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11960)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12159)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12358)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12557)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12756)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12955)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13154)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13353)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13552)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13751)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13950)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14149)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14348)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14547)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14746)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14945)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15144)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15343)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15542)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15741)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15940)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16139)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16338)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16537)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16736)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16935)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17134)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17333)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17532)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17731)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17930)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18129)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18328)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18527)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18726)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18925)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19124)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19323)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19522)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19721)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19920)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20119)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20318)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20517)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20716)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20915)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21114)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21313)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21512)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21711)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21910)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22109)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22308)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22507)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22706)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22905)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23104)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23303)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23502)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23701)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23900)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24099)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24298)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24497)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24696)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24895)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25094)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25293)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25492)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25691)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25890)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26089)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26288)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26487)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26686)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26885)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27084)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27283)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27482)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27681)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27880)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28079)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28278)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28477)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28676)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28875)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29074)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29273)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29472)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29671)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29870)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30069)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30268)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30467)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30666)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30865)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31064)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31263)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31462)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31661)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31860)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32059)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32258)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32457)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32656)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32855)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33054)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33253)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33452)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33651)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33850)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34049)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34248)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34447)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34646)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34845)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35044)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35243)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35442)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35641)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35840)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36039)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36238)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36437)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36636)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36835)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37034)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37233)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37432)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37631)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37830)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38029)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38228)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38427)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38626)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38825)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39024)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39223)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39422)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39621)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39820)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40019)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40218)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40417)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40616)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40815)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41014)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41213)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41412)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41611)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41810)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42009)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42208)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42407)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42606)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42805)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43004)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43203)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43402)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43601)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43800)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43999)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65491)) (i32.const 0)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 65516) (i32.const 40)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65515) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 65515) (i32.const 39)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 19)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 20)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65486) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65516) (i32.const 65486) (i32.const 40)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65487)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65488)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65489)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65490)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65491)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65492)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65493)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65494)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65495)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65496)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65497)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65498)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65499)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65500)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65501)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65502)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65503)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65504)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65505)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65486) (i32.const 65516) (i32.const 40)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65506) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65516) (i32.const 65506) (i32.const 40)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65507)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65508)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65509)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65510)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65511)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65512)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65513)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65514)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65515)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65506) (i32.const 65516) (i32.const 40)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65516) (i32.const 65516) (i32.const 40)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 19)) + +(module + (memory (export "mem") 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 65516) (i32.const 4294963200)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 61440) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65516) (i32.const 61440) (i32.const 4294967040)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61440)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61441)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 61442)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 61443)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 61444)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 61445)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 61446)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 61447)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 61448)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 61449)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 61450)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 61451)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 61452)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 61453)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 61454)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 61455)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 61456)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 61457)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 61458)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 61459)) (i32.const 19)) +(assert_return (invoke "load8_u" (i32.const 61510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65490)) (i32.const 0)) + +(assert_invalid + (module + (func (export "testfn") + (memory.copy (i32.const 10) (i32.const 20) (i32.const 30)))) + "unknown memory 0") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + + +(module + (memory 1 1) + (func (export "test") + (memory.fill (i32.const 10) (i32.const 0x55) (i32.const 10)) + (memory.copy (i32.const 9) (i32.const 10) (i32.const 5))) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) +) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 9) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 9) (i32.const 20) (i32.const 85)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 20) (i32.const 65536) (i32.const 0)) + (i32.const -1)) + +(module + (memory 1 1) + (func (export "test") + (memory.fill (i32.const 10) (i32.const 0x55) (i32.const 10)) + (memory.copy (i32.const 16) (i32.const 15) (i32.const 5))) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) +) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 10) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 10) (i32.const 21) (i32.const 85)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 21) (i32.const 65536) (i32.const 0)) + (i32.const -1)) + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0xFF00) (i32.const 0x8000) (i32.const 257)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0xFFFFFF00) (i32.const 0x4000) (i32.const 257)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x8000) (i32.const 0xFF00) (i32.const 257)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x4000) (i32.const 0xFFFFFF00) (i32.const 257)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + (func (export "test") + (memory.fill (i32.const 0x0000) (i32.const 0x55) (i32.const 0x8000)) + (memory.fill (i32.const 0x8000) (i32.const 0xAA) (i32.const 0x8000)) + (memory.copy (i32.const 0x9000) (i32.const 0x7000) (i32.const 0))) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) +) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 32768) (i32.const 85)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 32768) (i32.const 65536) (i32.const 170)) + (i32.const -1)) +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x10000) (i32.const 0x7000) (i32.const 0)))) +(invoke "test") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x20000) (i32.const 0x7000) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x9000) (i32.const 0x10000) (i32.const 0)))) +(invoke "test") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x9000) (i32.const 0x20000) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x10000) (i32.const 0x10000) (i32.const 0)))) +(invoke "test") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x20000) (i32.const 0x20000) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + (func (export "test") + (memory.fill (i32.const 17767) (i32.const 1) (i32.const 1344)) + (memory.fill (i32.const 39017) (i32.const 2) (i32.const 1055)) + (memory.fill (i32.const 56401) (i32.const 3) (i32.const 988)) + (memory.fill (i32.const 37962) (i32.const 4) (i32.const 322)) + (memory.fill (i32.const 7977) (i32.const 5) (i32.const 1994)) + (memory.fill (i32.const 22714) (i32.const 6) (i32.const 3036)) + (memory.fill (i32.const 16882) (i32.const 7) (i32.const 2372)) + (memory.fill (i32.const 43491) (i32.const 8) (i32.const 835)) + (memory.fill (i32.const 124) (i32.const 9) (i32.const 1393)) + (memory.fill (i32.const 2132) (i32.const 10) (i32.const 2758)) + (memory.fill (i32.const 8987) (i32.const 11) (i32.const 3098)) + (memory.fill (i32.const 52711) (i32.const 12) (i32.const 741)) + (memory.fill (i32.const 3958) (i32.const 13) (i32.const 2823)) + (memory.fill (i32.const 49715) (i32.const 14) (i32.const 1280)) + (memory.fill (i32.const 50377) (i32.const 15) (i32.const 1466)) + (memory.fill (i32.const 20493) (i32.const 16) (i32.const 3158)) + (memory.fill (i32.const 47665) (i32.const 17) (i32.const 544)) + (memory.fill (i32.const 12451) (i32.const 18) (i32.const 2669)) + (memory.fill (i32.const 24869) (i32.const 19) (i32.const 2651)) + (memory.fill (i32.const 45317) (i32.const 20) (i32.const 1570)) + (memory.fill (i32.const 43096) (i32.const 21) (i32.const 1691)) + (memory.fill (i32.const 33886) (i32.const 22) (i32.const 646)) + (memory.fill (i32.const 48555) (i32.const 23) (i32.const 1858)) + (memory.fill (i32.const 53453) (i32.const 24) (i32.const 2657)) + (memory.fill (i32.const 30363) (i32.const 25) (i32.const 981)) + (memory.fill (i32.const 9300) (i32.const 26) (i32.const 1807)) + (memory.fill (i32.const 50190) (i32.const 27) (i32.const 487)) + (memory.fill (i32.const 62753) (i32.const 28) (i32.const 530)) + (memory.fill (i32.const 36316) (i32.const 29) (i32.const 943)) + (memory.fill (i32.const 6768) (i32.const 30) (i32.const 381)) + (memory.fill (i32.const 51262) (i32.const 31) (i32.const 3089)) + (memory.fill (i32.const 49729) (i32.const 32) (i32.const 658)) + (memory.fill (i32.const 44540) (i32.const 33) (i32.const 1702)) + (memory.fill (i32.const 33342) (i32.const 34) (i32.const 1092)) + (memory.fill (i32.const 50814) (i32.const 35) (i32.const 1410)) + (memory.fill (i32.const 47594) (i32.const 36) (i32.const 2204)) + (memory.fill (i32.const 54123) (i32.const 37) (i32.const 2394)) + (memory.fill (i32.const 55183) (i32.const 38) (i32.const 250)) + (memory.fill (i32.const 22620) (i32.const 39) (i32.const 2097)) + (memory.fill (i32.const 17132) (i32.const 40) (i32.const 3264)) + (memory.fill (i32.const 54331) (i32.const 41) (i32.const 3299)) + (memory.fill (i32.const 39474) (i32.const 42) (i32.const 2796)) + (memory.fill (i32.const 36156) (i32.const 43) (i32.const 2070)) + (memory.fill (i32.const 35308) (i32.const 44) (i32.const 2763)) + (memory.fill (i32.const 32731) (i32.const 45) (i32.const 312)) + (memory.fill (i32.const 63746) (i32.const 46) (i32.const 192)) + (memory.fill (i32.const 30974) (i32.const 47) (i32.const 596)) + (memory.fill (i32.const 16635) (i32.const 48) (i32.const 501)) + (memory.fill (i32.const 57002) (i32.const 49) (i32.const 686)) + (memory.fill (i32.const 34299) (i32.const 50) (i32.const 385)) + (memory.fill (i32.const 60881) (i32.const 51) (i32.const 903)) + (memory.fill (i32.const 61445) (i32.const 52) (i32.const 2390)) + (memory.fill (i32.const 46972) (i32.const 53) (i32.const 1441)) + (memory.fill (i32.const 25973) (i32.const 54) (i32.const 3162)) + (memory.fill (i32.const 5566) (i32.const 55) (i32.const 2135)) + (memory.fill (i32.const 35977) (i32.const 56) (i32.const 519)) + (memory.fill (i32.const 44892) (i32.const 57) (i32.const 3280)) + (memory.fill (i32.const 46760) (i32.const 58) (i32.const 1678)) + (memory.fill (i32.const 46607) (i32.const 59) (i32.const 3168)) + (memory.fill (i32.const 22449) (i32.const 60) (i32.const 1441)) + (memory.fill (i32.const 58609) (i32.const 61) (i32.const 663)) + (memory.fill (i32.const 32261) (i32.const 62) (i32.const 1671)) + (memory.fill (i32.const 3063) (i32.const 63) (i32.const 721)) + (memory.fill (i32.const 34025) (i32.const 64) (i32.const 84)) + (memory.fill (i32.const 33338) (i32.const 65) (i32.const 2029)) + (memory.fill (i32.const 36810) (i32.const 66) (i32.const 29)) + (memory.fill (i32.const 19147) (i32.const 67) (i32.const 3034)) + (memory.fill (i32.const 12616) (i32.const 68) (i32.const 1043)) + (memory.fill (i32.const 18276) (i32.const 69) (i32.const 3324)) + (memory.fill (i32.const 4639) (i32.const 70) (i32.const 1091)) + (memory.fill (i32.const 16158) (i32.const 71) (i32.const 1997)) + (memory.fill (i32.const 18204) (i32.const 72) (i32.const 2259)) + (memory.fill (i32.const 50532) (i32.const 73) (i32.const 3189)) + (memory.fill (i32.const 11028) (i32.const 74) (i32.const 1968)) + (memory.fill (i32.const 15962) (i32.const 75) (i32.const 1455)) + (memory.fill (i32.const 45406) (i32.const 76) (i32.const 1177)) + (memory.fill (i32.const 54137) (i32.const 77) (i32.const 1568)) + (memory.fill (i32.const 33083) (i32.const 78) (i32.const 1642)) + (memory.fill (i32.const 61028) (i32.const 79) (i32.const 3284)) + (memory.fill (i32.const 51729) (i32.const 80) (i32.const 223)) + (memory.fill (i32.const 4361) (i32.const 81) (i32.const 2171)) + (memory.fill (i32.const 57514) (i32.const 82) (i32.const 1322)) + (memory.fill (i32.const 55724) (i32.const 83) (i32.const 2648)) + (memory.fill (i32.const 24091) (i32.const 84) (i32.const 1045)) + (memory.fill (i32.const 43183) (i32.const 85) (i32.const 3097)) + (memory.fill (i32.const 32307) (i32.const 86) (i32.const 2796)) + (memory.fill (i32.const 3811) (i32.const 87) (i32.const 2010)) + (memory.fill (i32.const 54856) (i32.const 88) (i32.const 0)) + (memory.fill (i32.const 49941) (i32.const 89) (i32.const 2069)) + (memory.fill (i32.const 20411) (i32.const 90) (i32.const 2896)) + (memory.fill (i32.const 33826) (i32.const 91) (i32.const 192)) + (memory.fill (i32.const 9402) (i32.const 92) (i32.const 2195)) + (memory.fill (i32.const 12413) (i32.const 93) (i32.const 24)) + (memory.fill (i32.const 14091) (i32.const 94) (i32.const 577)) + (memory.fill (i32.const 44058) (i32.const 95) (i32.const 2089)) + (memory.fill (i32.const 36735) (i32.const 96) (i32.const 3436)) + (memory.fill (i32.const 23288) (i32.const 97) (i32.const 2765)) + (memory.fill (i32.const 6392) (i32.const 98) (i32.const 830)) + (memory.fill (i32.const 33307) (i32.const 99) (i32.const 1938)) + (memory.fill (i32.const 21941) (i32.const 100) (i32.const 2750)) + (memory.copy (i32.const 59214) (i32.const 54248) (i32.const 2098)) + (memory.copy (i32.const 63026) (i32.const 39224) (i32.const 230)) + (memory.copy (i32.const 51833) (i32.const 23629) (i32.const 2300)) + (memory.copy (i32.const 6708) (i32.const 23996) (i32.const 639)) + (memory.copy (i32.const 6990) (i32.const 33399) (i32.const 1097)) + (memory.copy (i32.const 19403) (i32.const 10348) (i32.const 3197)) + (memory.copy (i32.const 27308) (i32.const 54406) (i32.const 100)) + (memory.copy (i32.const 27221) (i32.const 43682) (i32.const 1717)) + (memory.copy (i32.const 60528) (i32.const 8629) (i32.const 119)) + (memory.copy (i32.const 5947) (i32.const 2308) (i32.const 658)) + (memory.copy (i32.const 4787) (i32.const 51631) (i32.const 2269)) + (memory.copy (i32.const 12617) (i32.const 19197) (i32.const 833)) + (memory.copy (i32.const 11854) (i32.const 46505) (i32.const 3300)) + (memory.copy (i32.const 11376) (i32.const 45012) (i32.const 2281)) + (memory.copy (i32.const 34186) (i32.const 6697) (i32.const 2572)) + (memory.copy (i32.const 4936) (i32.const 1690) (i32.const 1328)) + (memory.copy (i32.const 63164) (i32.const 7637) (i32.const 1670)) + (memory.copy (i32.const 44568) (i32.const 18344) (i32.const 33)) + (memory.copy (i32.const 43918) (i32.const 22348) (i32.const 1427)) + (memory.copy (i32.const 46637) (i32.const 49819) (i32.const 1434)) + (memory.copy (i32.const 63684) (i32.const 8755) (i32.const 834)) + (memory.copy (i32.const 33485) (i32.const 20131) (i32.const 3317)) + (memory.copy (i32.const 40575) (i32.const 54317) (i32.const 3201)) + (memory.copy (i32.const 25812) (i32.const 59254) (i32.const 2452)) + (memory.copy (i32.const 19678) (i32.const 56882) (i32.const 346)) + (memory.copy (i32.const 15852) (i32.const 35914) (i32.const 2430)) + (memory.copy (i32.const 11824) (i32.const 35574) (i32.const 300)) + (memory.copy (i32.const 59427) (i32.const 13957) (i32.const 3153)) + (memory.copy (i32.const 34299) (i32.const 60594) (i32.const 1281)) + (memory.copy (i32.const 8964) (i32.const 12276) (i32.const 943)) + (memory.copy (i32.const 2827) (i32.const 10425) (i32.const 1887)) + (memory.copy (i32.const 43194) (i32.const 43910) (i32.const 738)) + (memory.copy (i32.const 63038) (i32.const 18949) (i32.const 122)) + (memory.copy (i32.const 24044) (i32.const 44761) (i32.const 1755)) + (memory.copy (i32.const 22608) (i32.const 14755) (i32.const 702)) + (memory.copy (i32.const 11284) (i32.const 26579) (i32.const 1830)) + (memory.copy (i32.const 23092) (i32.const 20471) (i32.const 1064)) + (memory.copy (i32.const 57248) (i32.const 54770) (i32.const 2631)) + (memory.copy (i32.const 25492) (i32.const 1025) (i32.const 3113)) + (memory.copy (i32.const 49588) (i32.const 44220) (i32.const 975)) + (memory.copy (i32.const 28280) (i32.const 41722) (i32.const 2336)) + (memory.copy (i32.const 61289) (i32.const 230) (i32.const 2872)) + (memory.copy (i32.const 22480) (i32.const 52506) (i32.const 2197)) + (memory.copy (i32.const 40553) (i32.const 9578) (i32.const 1958)) + (memory.copy (i32.const 29004) (i32.const 20862) (i32.const 2186)) + (memory.copy (i32.const 53029) (i32.const 43955) (i32.const 1037)) + (memory.copy (i32.const 25476) (i32.const 35667) (i32.const 1650)) + (memory.copy (i32.const 58516) (i32.const 45819) (i32.const 1986)) + (memory.copy (i32.const 38297) (i32.const 5776) (i32.const 1955)) + (memory.copy (i32.const 28503) (i32.const 55364) (i32.const 2368)) + (memory.copy (i32.const 62619) (i32.const 18108) (i32.const 1356)) + (memory.copy (i32.const 50149) (i32.const 13861) (i32.const 382)) + (memory.copy (i32.const 16904) (i32.const 36341) (i32.const 1900)) + (memory.copy (i32.const 48098) (i32.const 11358) (i32.const 2807)) + (memory.copy (i32.const 28512) (i32.const 40362) (i32.const 323)) + (memory.copy (i32.const 35506) (i32.const 27856) (i32.const 1670)) + (memory.copy (i32.const 62970) (i32.const 53332) (i32.const 1341)) + (memory.copy (i32.const 14133) (i32.const 46312) (i32.const 644)) + (memory.copy (i32.const 29030) (i32.const 19074) (i32.const 496)) + (memory.copy (i32.const 44952) (i32.const 47577) (i32.const 2784)) + (memory.copy (i32.const 39559) (i32.const 44661) (i32.const 1350)) + (memory.copy (i32.const 10352) (i32.const 29274) (i32.const 1475)) + (memory.copy (i32.const 46911) (i32.const 46178) (i32.const 1467)) + (memory.copy (i32.const 4905) (i32.const 28740) (i32.const 1895)) + (memory.copy (i32.const 38012) (i32.const 57253) (i32.const 1751)) + (memory.copy (i32.const 26446) (i32.const 27223) (i32.const 1127)) + (memory.copy (i32.const 58835) (i32.const 24657) (i32.const 1063)) + (memory.copy (i32.const 61356) (i32.const 38790) (i32.const 766)) + (memory.copy (i32.const 44160) (i32.const 2284) (i32.const 1520)) + (memory.copy (i32.const 32740) (i32.const 47237) (i32.const 3014)) + (memory.copy (i32.const 11148) (i32.const 21260) (i32.const 1011)) + (memory.copy (i32.const 7665) (i32.const 31612) (i32.const 3034)) + (memory.copy (i32.const 18044) (i32.const 12987) (i32.const 3320)) + (memory.copy (i32.const 57306) (i32.const 55905) (i32.const 308)) + (memory.copy (i32.const 24675) (i32.const 16815) (i32.const 1155)) + (memory.copy (i32.const 19900) (i32.const 10115) (i32.const 722)) + (memory.copy (i32.const 2921) (i32.const 5935) (i32.const 2370)) + (memory.copy (i32.const 32255) (i32.const 50095) (i32.const 2926)) + (memory.copy (i32.const 15126) (i32.const 17299) (i32.const 2607)) + (memory.copy (i32.const 45575) (i32.const 28447) (i32.const 2045)) + (memory.copy (i32.const 55149) (i32.const 36113) (i32.const 2596)) + (memory.copy (i32.const 28461) (i32.const 54157) (i32.const 1168)) + (memory.copy (i32.const 47951) (i32.const 53385) (i32.const 3137)) + (memory.copy (i32.const 30646) (i32.const 45155) (i32.const 2649)) + (memory.copy (i32.const 5057) (i32.const 4295) (i32.const 52)) + (memory.copy (i32.const 6692) (i32.const 24195) (i32.const 441)) + (memory.copy (i32.const 32984) (i32.const 27117) (i32.const 3445)) + (memory.copy (i32.const 32530) (i32.const 59372) (i32.const 2785)) + (memory.copy (i32.const 34361) (i32.const 8962) (i32.const 2406)) + (memory.copy (i32.const 17893) (i32.const 54538) (i32.const 3381)) + (memory.copy (i32.const 22685) (i32.const 44151) (i32.const 136)) + (memory.copy (i32.const 59089) (i32.const 7077) (i32.const 1045)) + (memory.copy (i32.const 42945) (i32.const 55028) (i32.const 2389)) + (memory.copy (i32.const 44693) (i32.const 20138) (i32.const 877)) + (memory.copy (i32.const 36810) (i32.const 25196) (i32.const 3447)) + (memory.copy (i32.const 45742) (i32.const 31888) (i32.const 854)) + (memory.copy (i32.const 24236) (i32.const 31866) (i32.const 1377)) + (memory.copy (i32.const 33778) (i32.const 692) (i32.const 1594)) + (memory.copy (i32.const 60618) (i32.const 18585) (i32.const 2987)) + (memory.copy (i32.const 50370) (i32.const 41271) (i32.const 1406)) + ) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) +) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 124) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 124) (i32.const 1517) (i32.const 9)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 1517) (i32.const 2132) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 2132) (i32.const 2827) (i32.const 10)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 2827) (i32.const 2921) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 2921) (i32.const 3538) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 3538) (i32.const 3786) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 3786) (i32.const 4042) (i32.const 97)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 4042) (i32.const 4651) (i32.const 99)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 4651) (i32.const 5057) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 5057) (i32.const 5109) (i32.const 99)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 5109) (i32.const 5291) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 5291) (i32.const 5524) (i32.const 72)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 5524) (i32.const 5691) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 5691) (i32.const 6552) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 6552) (i32.const 7133) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 7133) (i32.const 7665) (i32.const 99)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 7665) (i32.const 8314) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 8314) (i32.const 8360) (i32.const 62)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 8360) (i32.const 8793) (i32.const 86)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 8793) (i32.const 8979) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 8979) (i32.const 9373) (i32.const 79)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 9373) (i32.const 9518) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 9518) (i32.const 9934) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 9934) (i32.const 10087) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 10087) (i32.const 10206) (i32.const 5)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 10206) (i32.const 10230) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 10230) (i32.const 10249) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 10249) (i32.const 11148) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 11148) (i32.const 11356) (i32.const 74)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 11356) (i32.const 11380) (i32.const 93)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 11380) (i32.const 11939) (i32.const 74)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 11939) (i32.const 12159) (i32.const 68)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 12159) (i32.const 12575) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 12575) (i32.const 12969) (i32.const 79)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 12969) (i32.const 13114) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 13114) (i32.const 14133) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 14133) (i32.const 14404) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 14404) (i32.const 14428) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 14428) (i32.const 14458) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 14458) (i32.const 14580) (i32.const 32)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 14580) (i32.const 14777) (i32.const 89)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 14777) (i32.const 15124) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 15124) (i32.const 15126) (i32.const 36)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 15126) (i32.const 15192) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 15192) (i32.const 15871) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 15871) (i32.const 15998) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 15998) (i32.const 17017) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17017) (i32.const 17288) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17288) (i32.const 17312) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17312) (i32.const 17342) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17342) (i32.const 17464) (i32.const 32)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17464) (i32.const 17661) (i32.const 89)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17661) (i32.const 17727) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17727) (i32.const 17733) (i32.const 5)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17733) (i32.const 17893) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17893) (i32.const 18553) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18553) (i32.const 18744) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18744) (i32.const 18801) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18801) (i32.const 18825) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18825) (i32.const 18876) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18876) (i32.const 18885) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18885) (i32.const 18904) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18904) (i32.const 19567) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 19567) (i32.const 20403) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 20403) (i32.const 21274) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 21274) (i32.const 21364) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 21364) (i32.const 21468) (i32.const 74)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 21468) (i32.const 21492) (i32.const 93)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 21492) (i32.const 22051) (i32.const 74)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 22051) (i32.const 22480) (i32.const 68)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 22480) (i32.const 22685) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 22685) (i32.const 22694) (i32.const 68)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 22694) (i32.const 22821) (i32.const 10)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 22821) (i32.const 22869) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 22869) (i32.const 24107) (i32.const 97)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 24107) (i32.const 24111) (i32.const 37)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 24111) (i32.const 24236) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 24236) (i32.const 24348) (i32.const 72)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 24348) (i32.const 24515) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 24515) (i32.const 24900) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 24900) (i32.const 25136) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 25136) (i32.const 25182) (i32.const 85)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 25182) (i32.const 25426) (i32.const 68)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 25426) (i32.const 25613) (i32.const 89)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 25613) (i32.const 25830) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 25830) (i32.const 26446) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 26446) (i32.const 26517) (i32.const 10)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 26517) (i32.const 27468) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 27468) (i32.const 27503) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 27503) (i32.const 27573) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 27573) (i32.const 28245) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 28245) (i32.const 28280) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 28280) (i32.const 29502) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 29502) (i32.const 29629) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 29629) (i32.const 30387) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 30387) (i32.const 30646) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 30646) (i32.const 31066) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31066) (i32.const 31131) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31131) (i32.const 31322) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31322) (i32.const 31379) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31379) (i32.const 31403) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31403) (i32.const 31454) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31454) (i32.const 31463) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31463) (i32.const 31482) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31482) (i32.const 31649) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31649) (i32.const 31978) (i32.const 72)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31978) (i32.const 32145) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 32145) (i32.const 32530) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 32530) (i32.const 32766) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 32766) (i32.const 32812) (i32.const 85)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 32812) (i32.const 33056) (i32.const 68)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 33056) (i32.const 33660) (i32.const 89)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 33660) (i32.const 33752) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 33752) (i32.const 33775) (i32.const 36)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 33775) (i32.const 33778) (i32.const 32)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 33778) (i32.const 34603) (i32.const 9)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 34603) (i32.const 35218) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 35218) (i32.const 35372) (i32.const 10)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 35372) (i32.const 35486) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 35486) (i32.const 35605) (i32.const 5)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 35605) (i32.const 35629) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 35629) (i32.const 35648) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 35648) (i32.const 36547) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 36547) (i32.const 36755) (i32.const 74)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 36755) (i32.const 36767) (i32.const 93)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 36767) (i32.const 36810) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 36810) (i32.const 36839) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 36839) (i32.const 37444) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 37444) (i32.const 38060) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 38060) (i32.const 38131) (i32.const 10)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 38131) (i32.const 39082) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 39082) (i32.const 39117) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 39117) (i32.const 39187) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 39187) (i32.const 39859) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 39859) (i32.const 39894) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 39894) (i32.const 40257) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 40257) (i32.const 40344) (i32.const 89)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 40344) (i32.const 40371) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 40371) (i32.const 40804) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 40804) (i32.const 40909) (i32.const 5)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 40909) (i32.const 42259) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 42259) (i32.const 42511) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 42511) (i32.const 42945) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 42945) (i32.const 43115) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 43115) (i32.const 43306) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 43306) (i32.const 43363) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 43363) (i32.const 43387) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 43387) (i32.const 43438) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 43438) (i32.const 43447) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 43447) (i32.const 43466) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 43466) (i32.const 44129) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 44129) (i32.const 44958) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 44958) (i32.const 45570) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 45570) (i32.const 45575) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 45575) (i32.const 45640) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 45640) (i32.const 45742) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 45742) (i32.const 45832) (i32.const 72)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 45832) (i32.const 45999) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 45999) (i32.const 46384) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 46384) (i32.const 46596) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 46596) (i32.const 46654) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 46654) (i32.const 47515) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 47515) (i32.const 47620) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 47620) (i32.const 47817) (i32.const 79)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 47817) (i32.const 47951) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 47951) (i32.const 48632) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 48632) (i32.const 48699) (i32.const 97)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 48699) (i32.const 48703) (i32.const 37)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 48703) (i32.const 49764) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 49764) (i32.const 49955) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 49955) (i32.const 50012) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 50012) (i32.const 50036) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 50036) (i32.const 50087) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 50087) (i32.const 50096) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 50096) (i32.const 50115) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 50115) (i32.const 50370) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 50370) (i32.const 51358) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 51358) (i32.const 51610) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 51610) (i32.const 51776) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 51776) (i32.const 51833) (i32.const 89)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 51833) (i32.const 52895) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 52895) (i32.const 53029) (i32.const 97)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 53029) (i32.const 53244) (i32.const 68)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 53244) (i32.const 54066) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 54066) (i32.const 54133) (i32.const 97)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 54133) (i32.const 54137) (i32.const 37)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 54137) (i32.const 55198) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 55198) (i32.const 55389) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 55389) (i32.const 55446) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 55446) (i32.const 55470) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 55470) (i32.const 55521) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 55521) (i32.const 55530) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 55530) (i32.const 55549) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 55549) (i32.const 56212) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 56212) (i32.const 57048) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 57048) (i32.const 58183) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 58183) (i32.const 58202) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 58202) (i32.const 58516) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 58516) (i32.const 58835) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 58835) (i32.const 58855) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 58855) (i32.const 59089) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 59089) (i32.const 59145) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 59145) (i32.const 59677) (i32.const 99)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 59677) (i32.const 60134) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60134) (i32.const 60502) (i32.const 89)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60502) (i32.const 60594) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60594) (i32.const 60617) (i32.const 36)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60617) (i32.const 60618) (i32.const 32)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60618) (i32.const 60777) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60777) (i32.const 60834) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60834) (i32.const 60858) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60858) (i32.const 60909) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60909) (i32.const 60918) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60918) (i32.const 60937) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60937) (i32.const 61600) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 61600) (i32.const 62436) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 62436) (i32.const 63307) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63307) (i32.const 63397) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63397) (i32.const 63501) (i32.const 74)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63501) (i32.const 63525) (i32.const 93)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63525) (i32.const 63605) (i32.const 74)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63605) (i32.const 63704) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63704) (i32.const 63771) (i32.const 97)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63771) (i32.const 63775) (i32.const 37)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63775) (i32.const 64311) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 64311) (i32.const 64331) (i32.const 26)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 64331) (i32.const 64518) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 64518) (i32.const 64827) (i32.const 11)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 64827) (i32.const 64834) (i32.const 26)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 64834) (i32.const 65536) (i32.const 0)) + (i32.const -1)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/memory_fill.wast b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/memory_fill.wast new file mode 100644 index 000000000..caca80b5d --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/memory_fill.wast @@ -0,0 +1,685 @@ +;; +;; Generated by ../meta/generate_memory_fill.js +;; + +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0xFF00) (i32.const 0x55) (i32.const 256)))) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 65280) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 65280) (i32.const 65536) (i32.const 85)) + (i32.const -1)) +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0xFF00) (i32.const 0x55) (i32.const 257)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0xFFFFFF00) (i32.const 0x55) (i32.const 257)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0x12) (i32.const 0x55) (i32.const 0)))) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 65536) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0x10000) (i32.const 0x55) (i32.const 0)))) +(invoke "test") + +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0x20000) (i32.const 0x55) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0x1) (i32.const 0xAA) (i32.const 0xFFFE)))) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 1) (i32.const 65535) (i32.const 170)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 65535) (i32.const 65536) (i32.const 0)) + (i32.const -1)) + +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0x12) (i32.const 0x55) (i32.const 10)) + (memory.fill (i32.const 0x15) (i32.const 0xAA) (i32.const 4)))) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 18) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18) (i32.const 21) (i32.const 85)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 21) (i32.const 25) (i32.const 170)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 25) (i32.const 28) (i32.const 85)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 28) (i32.const 65536) (i32.const 0)) + (i32.const -1)) +(assert_invalid + (module + (func (export "testfn") + (memory.fill (i32.const 10) (i32.const 20) (i32.const 30)))) + "unknown memory 0") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(module + (memory 1 1 ) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $val i32) (param $len i32) + (memory.fill (local.get $offs) (local.get $val) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65280) (i32.const 37) (i32.const 512)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 1 ) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $val i32) (param $len i32) + (memory.fill (local.get $offs) (local.get $val) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65279) (i32.const 37) (i32.const 514)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 1 ) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $val i32) (param $len i32) + (memory.fill (local.get $offs) (local.get $val) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65279) (i32.const 37) (i32.const 4294967295)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/memory_init.wast b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/memory_init.wast new file mode 100644 index 000000000..60b60578c --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/memory_init.wast @@ -0,0 +1,967 @@ +;; +;; Generated by ../meta/generate_memory_init.js +;; + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i32.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (nop)) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i32.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (memory.init 1 (i32.const 7) (i32.const 0) (i32.const 4))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i32.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (memory.init 3 (i32.const 15) (i32.const 1) (i32.const 3))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i32.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (memory.init 1 (i32.const 7) (i32.const 0) (i32.const 4)) + (data.drop 1) + (memory.init 3 (i32.const 15) (i32.const 1) (i32.const 3)) + (data.drop 3) + (memory.copy (i32.const 20) (i32.const 15) (i32.const 5)) + (memory.copy (i32.const 21) (i32.const 29) (i32.const 1)) + (memory.copy (i32.const 24) (i32.const 10) (i32.const 1)) + (memory.copy (i32.const 13) (i32.const 11) (i32.const 4)) + (memory.copy (i32.const 19) (i32.const 20) (i32.const 5))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) +(assert_invalid + (module + (func (export "test") + (data.drop 0))) + "unknown data segment") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (data.drop 4))) + "unknown data segment") + +(module + (memory 1) + (data "\37") + (func (export "test") + (data.drop 0) + (data.drop 0))) +(invoke "test") + +(module + (memory 1) + (data "\37") + (func (export "test") + (data.drop 0) + (memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 1)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1) + (data (i32.const 0) "\37") + (func (export "test") + (memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 1)))) +(assert_trap (invoke "test") "out of bounds") + +(assert_invalid + (module + (func (export "test") + (memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1)))) + "unknown memory 0") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1)))) + "unknown data segment 1") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i32.const 0) (i32.const 1)) + (memory.init 0 (i32.const 1) (i32.const 0) (i32.const 1)))) +(invoke "test") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1234) (i32.const 0) (i32.const 5)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1234) (i32.const 2) (i32.const 3)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 0xFFFE) (i32.const 1) (i32.const 3)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1234) (i32.const 4) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 0)))) +(invoke "test") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 0x10001) (i32.const 0) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 0x10000) (i32.const 0) (i32.const 0)))) +(invoke "test") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 0x10000) (i32.const 1) (i32.const 0)))) +(invoke "test") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 0x10001) (i32.const 4) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(module + (memory 1 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65528) (i32.const 16)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65527) (i32.const 16)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65472) (i32.const 30)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65473) (i32.const 31)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65528) (i32.const 4294967040)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 4294967292)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) + +(module + (memory 1) + ;; 65 data segments. 64 is the smallest positive number that is encoded + ;; differently as a signed LEB. + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") (data "") (data "") (data "") (data "") (data "") (data "") (data "") + (data "") + (func (memory.init 64 (i32.const 0) (i32.const 0) (i32.const 0)))) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/table_copy.wast b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/table_copy.wast new file mode 100644 index 000000000..d0aa77b20 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/table_copy.wast @@ -0,0 +1,1595 @@ +;; +;; Generated by ../meta/generate_table_copy.js +;; + +(module + (func (export "ef0") (result i32) (i32.const 0)) + (func (export "ef1") (result i32) (i32.const 1)) + (func (export "ef2") (result i32) (i32.const 2)) + (func (export "ef3") (result i32) (i32.const 3)) + (func (export "ef4") (result i32) (i32.const 4)) +) +(register "a") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (nop)) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy (i32.const 13) (i32.const 2) (i32.const 3))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy (i32.const 25) (i32.const 15) (i32.const 2))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_return (invoke "check" (i32.const 25)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 26)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy (i32.const 13) (i32.const 25) (i32.const 3))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 15)) "uninitialized element") +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy (i32.const 20) (i32.const 22) (i32.const 4))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy (i32.const 25) (i32.const 1) (i32.const 3))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_return (invoke "check" (i32.const 26)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 27)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy (i32.const 10) (i32.const 12) (i32.const 7))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_return (invoke "check" (i32.const 10)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 11)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 12)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy (i32.const 12) (i32.const 10) (i32.const 7))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 13)) "uninitialized element") +(assert_return (invoke "check" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 17)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 18)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 28) (i32.const 1) (i32.const 3)) + )) + +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 0xFFFFFFFE) (i32.const 1) (i32.const 2)) + )) + +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 15) (i32.const 25) (i32.const 6)) + )) + +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 15) (i32.const 0xFFFFFFFE) (i32.const 2)) + )) + +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 15) (i32.const 25) (i32.const 0)) + )) + +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 30) (i32.const 15) (i32.const 0)) + )) + +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 31) (i32.const 15) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 15) (i32.const 30) (i32.const 0)) + )) + +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 15) (i32.const 31) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 30) (i32.const 30) (i32.const 0)) + )) + +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 31) (i32.const 31) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 0) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 24) (i32.const 0) (i32.const 16)) + "out of bounds") +(assert_return (invoke "test" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 7)) (i32.const 7)) +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 0) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 23) (i32.const 0) (i32.const 15)) + "out of bounds") +(assert_return (invoke "test" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 7)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 8)) (i32.const 8)) +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 24) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 24) (i32.const 16)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_return (invoke "test" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 7)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 23) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 23) (i32.const 15)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_return (invoke "test" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 24)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 8)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 11) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 24) (i32.const 11) (i32.const 16)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_return (invoke "test" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 12)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 14)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 15)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 16)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 17)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 18)) (i32.const 7)) +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 24) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 11) (i32.const 24) (i32.const 16)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_return (invoke "test" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 7)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 21) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 24) (i32.const 21) (i32.const 16)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_return (invoke "test" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 22)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 23)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 24)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 7)) +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 24) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 21) (i32.const 24) (i32.const 16)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_return (invoke "test" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 7)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 21) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $f10) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 21) (i32.const 21) (i32.const 16)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_return (invoke "test" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 22)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 23)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 24)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 8)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 9)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 10)) + +(module + (type (func (result i32))) + (table 128 128 funcref) + (elem (i32.const 112) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $f10 $f11 $f12 $f13 $f14 $f15) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 112) (i32.const 4294967264)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_return (invoke "test" (i32.const 112)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 113)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 114)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 115)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 116)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 117)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 118)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 119)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 120)) (i32.const 8)) +(assert_return (invoke "test" (i32.const 121)) (i32.const 9)) +(assert_return (invoke "test" (i32.const 122)) (i32.const 10)) +(assert_return (invoke "test" (i32.const 123)) (i32.const 11)) +(assert_return (invoke "test" (i32.const 124)) (i32.const 12)) +(assert_return (invoke "test" (i32.const 125)) (i32.const 13)) +(assert_return (invoke "test" (i32.const 126)) (i32.const 14)) +(assert_return (invoke "test" (i32.const 127)) (i32.const 15)) + +(module + (type (func (result i32))) + (table 128 128 funcref) + (elem (i32.const 0) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $f10 $f11 $f12 $f13 $f14 $f15) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 112) (i32.const 0) (i32.const 4294967264)) + "out of bounds") +(assert_return (invoke "test" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 7)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 8)) (i32.const 8)) +(assert_return (invoke "test" (i32.const 9)) (i32.const 9)) +(assert_return (invoke "test" (i32.const 10)) (i32.const 10)) +(assert_return (invoke "test" (i32.const 11)) (i32.const 11)) +(assert_return (invoke "test" (i32.const 12)) (i32.const 12)) +(assert_return (invoke "test" (i32.const 13)) (i32.const 13)) +(assert_return (invoke "test" (i32.const 14)) (i32.const 14)) +(assert_return (invoke "test" (i32.const 15)) (i32.const 15)) +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 112)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 113)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 114)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 115)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 116)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 117)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 118)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 119)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 120)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 121)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 122)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 123)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 124)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 125)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 126)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 127)) "uninitialized element") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/table_init.wast b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/table_init.wast new file mode 100644 index 000000000..90f86ac17 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/bulk-memory-operations/table_init.wast @@ -0,0 +1,1776 @@ +;; +;; Generated by ../meta/generate_table_init.js +;; + +(module + (func (export "ef0") (result i32) (i32.const 0)) + (func (export "ef1") (result i32) (i32.const 1)) + (func (export "ef2") (result i32) (i32.const 2)) + (func (export "ef3") (result i32) (i32.const 3)) + (func (export "ef4") (result i32) (i32.const 4)) +) +(register "a") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init 1 (i32.const 7) (i32.const 0) (i32.const 4))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_return (invoke "check" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 10)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init 3 (i32.const 15) (i32.const 1) (i32.const 3))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 9)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 17)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init 1 (i32.const 7) (i32.const 0) (i32.const 4)) + (elem.drop 1) + (table.init 3 (i32.const 15) (i32.const 1) (i32.const 3)) + (elem.drop 3) + (table.copy (i32.const 20) (i32.const 15) (i32.const 5)) + (table.copy (i32.const 21) (i32.const 29) (i32.const 1)) + (table.copy (i32.const 24) (i32.const 10) (i32.const 1)) + (table.copy (i32.const 13) (i32.const 11) (i32.const 4)) + (table.copy (i32.const 19) (i32.const 20) (i32.const 5))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_return (invoke "check" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 10)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 13)) "uninitialized element") +(assert_return (invoke "check" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 17)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_return (invoke "check" (i32.const 19)) (i32.const 9)) +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_return (invoke "check" (i32.const 21)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_return (invoke "check" (i32.const 23)) (i32.const 8)) +(assert_return (invoke "check" (i32.const 24)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") +(assert_invalid + (module + (func (export "test") + (elem.drop 0))) + "unknown elem segment 0") + +(assert_invalid + (module + (func (export "test") + (table.init 0 (i32.const 12) (i32.const 1) (i32.const 1)))) + "unknown table 0") + +(assert_invalid + (module + (elem funcref (ref.func 0)) + (func (result i32) (i32.const 0)) + (func (export "test") + (elem.drop 4))) + "unknown elem segment 4") + +(assert_invalid + (module + (elem funcref (ref.func 0)) + (func (result i32) (i32.const 0)) + (func (export "test") + (table.init 4 (i32.const 12) (i32.const 1) (i32.const 1)))) + "unknown table 0") + + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (elem.drop 2) + )) +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 2 (i32.const 12) (i32.const 1) (i32.const 1)) + )) +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 1) (i32.const 1)) + (table.init 1 (i32.const 21) (i32.const 1) (i32.const 1)))) +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (elem.drop 1) + (elem.drop 1))) +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (elem.drop 1) + (table.init 1 (i32.const 12) (i32.const 1) (i32.const 1)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 0) (i32.const 5)) + )) +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 2) (i32.const 3)) + )) +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 28) (i32.const 1) (i32.const 3)) + )) +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 4) (i32.const 0)) + )) +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 5) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 30) (i32.const 2) (i32.const 0)) + )) +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 31) (i32.const 2) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 30) (i32.const 4) (i32.const 0)) + )) +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 31) (i32.const 5) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 24) (i32.const 16)) "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 25) (i32.const 16)) "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 160 320 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 96) (i32.const 32)) "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 112)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 113)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 114)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 115)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 116)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 117)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 118)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 119)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 120)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 121)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 122)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 123)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 124)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 125)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 126)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 127)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 128)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 129)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 130)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 131)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 132)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 133)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 134)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 135)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 136)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 137)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 138)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 139)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 140)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 141)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 142)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 143)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 144)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 145)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 146)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 147)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 148)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 149)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 150)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 151)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 152)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 153)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 154)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 155)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 156)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 157)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 158)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 159)) "uninitialized element") + +(module + (type (func (result i32))) + (table 160 320 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 97) (i32.const 31)) "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 112)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 113)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 114)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 115)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 116)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 117)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 118)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 119)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 120)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 121)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 122)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 123)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 124)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 125)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 126)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 127)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 128)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 129)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 130)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 131)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 132)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 133)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 134)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 135)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 136)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 137)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 138)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 139)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 140)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 141)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 142)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 143)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 144)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 145)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 146)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 147)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 148)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 149)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 150)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 151)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 152)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 153)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 154)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 155)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 156)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 157)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 158)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 159)) "uninitialized element") + +(module + (type (func (result i32))) + (table 64 64 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 48) (i32.const 4294967280)) "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") + +(module + (type (func (result i32))) + (table 16 16 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 8) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 0) (i32.const 4294967292)) "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") + +(module + (table 1 funcref) + ;; 65 elem segments. 64 is the smallest positive number that is encoded + ;; differently as a signed LEB. + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) + (func (table.init 64 (i32.const 0) (i32.const 0) (i32.const 0)))) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/array.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/array.wast new file mode 100644 index 000000000..48a58108b --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/array.wast @@ -0,0 +1,144 @@ +;; Type syntax + +(module + (type (array i8)) + (type (array i16)) + (type (array i32)) + (type (array i64)) + (type (array f32)) + (type (array f64)) + (type (array anyref)) + (type (array (ref data))) + (type (array (ref 0))) + (type (array (ref null 1))) + (type (array (rtt 1))) + (type (array (rtt 10 1))) + (type (array (mut i8))) + (type (array (mut i16))) + (type (array (mut i32))) + (type (array (mut i64))) + (type (array (mut i32))) + (type (array (mut i64))) + (type (array (mut anyref))) + (type (array (mut (ref data)))) + (type (array (mut (ref 0)))) + (type (array (mut (ref null i31)))) + (type (array (mut (rtt 0)))) + (type (array (mut (rtt 10 0)))) +) + + +(assert_invalid + (module + (type (array (mut (ref null 10)))) + ) + "unknown type" +) + + +;; Binding structure + +(module + (type $s0 (array (ref $s1))) + (type $s1 (array (ref $s0))) + + (func (param (ref $forward))) + + (type $forward (array i32)) +) + +(assert_invalid + (module (type (array (ref 1)))) + "unknown type" +) +(assert_invalid + (module (type (array (mut (ref 1))))) + "unknown type" +) + + +;; Basic instructions + +(module + (type $vec (array f32)) + (type $mvec (array (mut f32))) + + (func $get (param $i i32) (param $v (ref $vec)) (result f32) + (array.get $vec (local.get $v) (local.get $i)) + ) + (func (export "get") (param $i i32) (result f32) + (call $get (local.get $i) + (array.new_default $vec (i32.const 3) (rtt.canon $vec)) + ) + ) + + (func $set_get (param $i i32) (param $v (ref $mvec)) (param $y f32) (result f32) + (array.set $mvec (local.get $v) (local.get $i) (local.get $y)) + (array.get $mvec (local.get $v) (local.get $i)) + ) + (func (export "set_get") (param $i i32) (param $y f32) (result f32) + (call $set_get (local.get $i) + (array.new_default $mvec (i32.const 3) (rtt.canon $mvec)) + (local.get $y) + ) + ) + + (func $len (param $v (ref $vec)) (result i32) + (array.len $vec (local.get $v)) + ) + (func (export "len") (result i32) + (call $len (array.new_default $vec (i32.const 3) (rtt.canon $vec))) + ) +) + +(assert_return (invoke "get" (i32.const 0)) (f32.const 0)) +(assert_return (invoke "set_get" (i32.const 1) (f32.const 7)) (f32.const 7)) +(assert_return (invoke "len") (i32.const 3)) + +(assert_trap (invoke "get" (i32.const 10)) "out of bounds") +(assert_trap (invoke "set_get" (i32.const 10) (f32.const 7)) "out of bounds") + +(assert_invalid + (module + (type $a (array i64)) + (func (export "array.set-immutable") (param $a (ref $a)) + (array.set $a (local.get $a) (i32.const 0) (i64.const 1)) + ) + ) + "array is immutable" +) + + +;; Null dereference + +(module + (type $t (array (mut i32))) + (func (export "array.get-null") + (local (ref null $t)) (drop (array.get $t (local.get 0) (i32.const 0))) + ) + (func (export "array.set-null") + (local (ref null $t)) (array.set $t (local.get 0) (i32.const 0) (i32.const 0)) + ) +) + +(assert_trap (invoke "array.get-null") "null array") +(assert_trap (invoke "array.set-null") "null array") + +(assert_invalid + (module + (type $t (array i32)) + (func (export "array.new-null") + (local (ref null (rtt $t))) (drop (array.new_default $t (i32.const 1) (i32.const 3) (local.get 0))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type $t (array (mut i32))) + (func (export "array.new_default-null") + (local (ref null (rtt $t))) (drop (array.new_default $t (i32.const 3) (local.get 0))) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/binary.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/binary.wast new file mode 100644 index 000000000..1f76dc136 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/binary.wast @@ -0,0 +1,1765 @@ +(module binary "\00asm\01\00\00\00") +(module binary "\00asm" "\01\00\00\00") +(module $M1 binary "\00asm\01\00\00\00") +(module $M2 binary "\00asm" "\01\00\00\00") + +(assert_malformed (module binary "") "unexpected end") +(assert_malformed (module binary "\01") "unexpected end") +(assert_malformed (module binary "\00as") "unexpected end") +(assert_malformed (module binary "asm\00") "magic header not detected") +(assert_malformed (module binary "msa\00") "magic header not detected") +(assert_malformed (module binary "msa\00\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "msa\00\00\00\00\01") "magic header not detected") +(assert_malformed (module binary "asm\01\00\00\00\00") "magic header not detected") +(assert_malformed (module binary "wasm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\7fasm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\80asm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\82asm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\ffasm\01\00\00\00") "magic header not detected") + +;; 8-byte endian-reversed. +(assert_malformed (module binary "\00\00\00\01msa\00") "magic header not detected") + +;; Middle-endian byte orderings. +(assert_malformed (module binary "a\00ms\00\01\00\00") "magic header not detected") +(assert_malformed (module binary "sm\00a\00\00\01\00") "magic header not detected") + +;; Upper-cased. +(assert_malformed (module binary "\00ASM\01\00\00\00") "magic header not detected") + +;; EBCDIC-encoded magic. +(assert_malformed (module binary "\00\81\a2\94\01\00\00\00") "magic header not detected") + +;; Leading UTF-8 BOM. +(assert_malformed (module binary "\ef\bb\bf\00asm\01\00\00\00") "magic header not detected") + +;; Malformed binary version. +(assert_malformed (module binary "\00asm") "unexpected end") +(assert_malformed (module binary "\00asm\01") "unexpected end") +(assert_malformed (module binary "\00asm\01\00\00") "unexpected end") +(assert_malformed (module binary "\00asm\00\00\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\0d\00\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\0e\00\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\00\01\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\00\00\01\00") "unknown binary version") +(assert_malformed (module binary "\00asm\00\00\00\01") "unknown binary version") + +;; Invalid section id. +(assert_malformed (module binary "\00asm" "\01\00\00\00" "\0d\00") "malformed section id") +(assert_malformed (module binary "\00asm" "\01\00\00\00" "\7f\00") "malformed section id") +(assert_malformed (module binary "\00asm" "\01\00\00\00" "\80\00\01\00") "malformed section id") +(assert_malformed (module binary "\00asm" "\01\00\00\00" "\81\00\01\00") "malformed section id") +(assert_malformed (module binary "\00asm" "\01\00\00\00" "\ff\00\01\00") "malformed section id") + +;; Unsigned LEB128 can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\05\04\01" ;; Memory section with 1 entry + "\00\82\00" ;; no max, minimum 2 +) +(module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\00" ;; no max, minimum 2 +) + +;; Signed LEB128 can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\00" ;; i32.const 0 + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\7f" ;; i32.const -1 + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\00" ;; i32.const 0 + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\7f" ;; i32.const -1 + "\0b" ;; end +) + +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\00" ;; i64.const 0 with unused bits set + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\7f" ;; i64.const -1 with unused bits unset + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\00" ;; i64.const 0 with unused bits set + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\7f" ;; i64.const -1 with unused bits unset + "\0b" ;; end +) + +(module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; Memory section with 1 entry + "\00\00" ;; no max, minimum 0 + "\0b\06\01" ;; Data section with 1 entry + "\00" ;; Memory index 0 + "\41\00\0b\00" ;; (i32.const 0) with contents "" +) + +(module binary + "\00asm" "\01\00\00\00" + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + "\09\06\01" ;; Element section with 1 entry + "\00" ;; Table index 0 + "\41\00\0b\00" ;; (i32.const 0) with no elements +) + +;; Data segment memory index can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; Memory section with 1 entry + "\00\00" ;; no max, minimum 0 + "\0b\07\01" ;; Data section with 1 entry + "\80\00" ;; Memory index 0, encoded with 2 bytes + "\41\00\0b\00" ;; (i32.const 0) with contents "" +) + +;; Element segment table index can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + "\09\09\01" ;; Element section with 1 entry + "\02\80\00" ;; Table index 0, encoded with 2 bytes + "\41\00\0b\00\00" ;; (i32.const 0) with no elements +) + +;; Type section with signed LEB128 encoded type +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01" ;; Type section id + "\05" ;; Type section length + "\01" ;; Types vector length + "\e0\7f" ;; Malformed functype, -0x20 in signed LEB128 encoding + "\00\00" + ) + "integer representation too long" +) + +;; Unsigned LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\08\01" ;; Memory section with 1 entry + "\00\82\80\80\80\80\00" ;; no max, minimum 2 with one byte too many + ) + "integer representation too long" +) + +;; Signed LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\80\00" ;; i32.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\ff\7f" ;; i32.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\80\00" ;; i64.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\7f" ;; i64.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +;; Unsigned LEB128s zero-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\70" ;; no max, minimum 2 with unused bits set + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\40" ;; no max, minimum 2 with some unused bits set + ) + "integer too large" +) + +;; Signed LEB128s sign-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\70" ;; i32.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\0f" ;; i32.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\1f" ;; i32.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\4f" ;; i32.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\7e" ;; i64.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\01" ;; i64.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\02" ;; i64.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\41" ;; i64.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\7e" ;; i64.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\01" ;; i64.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\02" ;; i64.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\41" ;; i64.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + + +;; Unsigned LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\08\01" ;; Memory section with 1 entry + "\00\82\80\80\80\80\00" ;; no max, minimum 2 with one byte too many + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\02" ;; alignment 2 + "\82\80\80\80\80\00" ;; offset 2 with one byte too many + "\1a" ;; drop + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\82\80\80\80\80\00" ;; alignment 2 with one byte too many + "\00" ;; offset 0 + "\1a" ;; drop + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\12\01" ;; Code section + ;; function 0 + "\10\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\82\80\80\80\80\00" ;; alignment 2 with one byte too many + "\03" ;; offset 3 + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\12\01" ;; Code section + ;; function 0 + "\10\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\02" ;; alignment 2 + "\82\80\80\80\80\00" ;; offset 2 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +;; Signed LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\80\00" ;; i32.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\ff\7f" ;; i32.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\80\00" ;; i64.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\7f" ;; i64.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +;; Unsigned LEB128s zero-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\70" ;; no max, minimum 2 with unused bits set + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\40" ;; no max, minimum 2 with some unused bits set + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + ;; function 0 + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\02" ;; alignment 2 + "\82\80\80\80\10" ;; offset 2 with unused bits set + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + ;; function 0 + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\02" ;; alignment 2 + "\82\80\80\80\40" ;; offset 2 with some unused bits set + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\82\80\80\80\10" ;; alignment 2 with unused bits set + "\00" ;; offset 0 + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + ;; function 0 + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\82\80\80\80\40" ;; alignment 2 with some unused bits set + "\00" ;; offset 0 + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\82\80\80\80\10" ;; alignment 2 with unused bits set + "\03" ;; offset 3 + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\82\80\80\80\40" ;; alignment 2 with some unused bits set + "\03" ;; offset 3 + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\02" ;; alignment 2 + "\82\80\80\80\10" ;; offset 2 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\02" ;; alignment 2 + "\82\80\80\80\40" ;; offset 2 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) + +;; Signed LEB128s sign-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\70" ;; i32.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\0f" ;; i32.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\1f" ;; i32.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\4f" ;; i32.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\7e" ;; i64.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\01" ;; i64.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\02" ;; i64.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\41" ;; i64.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + +;; memory.grow reserved byte equal to zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\09\01" ;; Code section + + ;; function 0 + "\07\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\01" ;; memory.grow reserved byte is not equal to zero! + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +;; memory.grow reserved byte should not be a "long" LEB128 zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\08\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +;; Same as above for 3, 4, and 5-byte zero encodings. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0b\01" ;; Code section + + ;; function 0 + "\09\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\0a\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\80\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0d\01" ;; Code section + + ;; function 0 + "\0b\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\80\80\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +;; memory.size reserved byte equal to zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\07\01" ;; Code section + + ;; function 0 + "\05\00" + "\3f" ;; memory.size + "\01" ;; memory.size reserved byte is not equal to zero! + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +;; memory.size reserved byte should not be a "long" LEB128 zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\08\01" ;; Code section + + ;; function 0 + "\06\00" + "\3f" ;; memory.size + "\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +;; Same as above for 3, 4, and 5-byte zero encodings. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\09\01" ;; Code section + + ;; function 0 + "\07\00" + "\3f" ;; memory.size + "\80\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\08\00" + "\3f" ;; memory.size + "\80\80\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0b\01" ;; Code section + + ;; function 0 + "\09\00" + "\3f" ;; memory.size + "\80\80\80\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero byte expected" +) + +;; Local number is unsigned 32 bit +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\0a\02" + "\80\80\80\80\10\7f" ;; 0x100000000 i32 + "\02\7e" ;; 0x00000002 i64 + "\0b" ;; end + ) + "integer too large" +) + +;; Local number is unsigned 32 bit +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\0a\02" + "\80\80\80\80\10\7f" ;; 0x100000000 i32 + "\02\7e" ;; 0x00000002 i64 + "\0b" ;; end + ) + "integer too large" +) + +;; No more than 2^32-1 locals. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\0a\02" + "\ff\ff\ff\ff\0f\7f" ;; 0xFFFFFFFF i32 + "\02\7e" ;; 0x00000002 i64 + "\0b" ;; end + ) + "too many locals" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\06\01\60\02\7f\7f\00" ;; Type section: (param i32 i32) + "\03\02\01\00" ;; Function section + "\0a\1c\01" ;; Code section + + ;; function 0 + "\1a\04" + "\80\80\80\80\04\7f" ;; 0x40000000 i32 + "\80\80\80\80\04\7e" ;; 0x40000000 i64 + "\80\80\80\80\04\7d" ;; 0x40000000 f32 + "\80\80\80\80\04\7c" ;; 0x40000000 f64 + "\0b" ;; end + ) + "too many locals" +) + +;; Local count can be 0. +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\08\03" + "\00\7f" ;; 0 i32 + "\00\7e" ;; 0 i64 + "\02\7d" ;; 2 f32 + "\0b" ;; end +) + +;; Function section has non-zero count, but code section is absent. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\03\02\00\00" ;; Function section with 2 functions + ) + "function and code section have inconsistent lengths" +) + +;; Code section has non-zero count, but function section is absent. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0a\04\01\02\00\0b" ;; Code section with 1 empty function + ) + "function and code section have inconsistent lengths" +) + +;; Function section count > code section count +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\03\02\00\00" ;; Function section with 2 functions + "\0a\04\01\02\00\0b" ;; Code section with 1 empty function + ) + "function and code section have inconsistent lengths" +) + +;; Function section count < code section count +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section with 1 function + "\0a\07\02\02\00\0b\02\00\0b" ;; Code section with 2 empty functions + ) + "function and code section have inconsistent lengths" +) + +;; Function section has zero count, and code section is absent. +(module binary + "\00asm" "\01\00\00\00" + "\03\01\00" ;; Function section with 0 functions +) + +;; Code section has zero count, and function section is absent. +(module binary + "\00asm" "\01\00\00\00" + "\0a\01\00" ;; Code section with 0 functions +) + +;; Fewer passive segments than datacount +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0c\01\03" ;; Datacount section with value "3" + "\0b\05\02" ;; Data section with two entries + "\01\00" ;; Passive data section + "\01\00") ;; Passive data section + "data count and data section have inconsistent lengths") + +;; More passive segments than datacount +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0c\01\01" ;; Datacount section with value "1" + "\0b\05\02" ;; Data section with two entries + "\01\00" ;; Passive data section + "\01\00") ;; Passive data section + "data count and data section have inconsistent lengths") + +;; memory.init requires a datacount section +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0e\01" ;; Code section + + ;; function 0 + "\0c\00" + "\41\00" ;; zero args + "\41\00" + "\41\00" + "\fc\08\00\00" ;; memory.init + "\0b" + + "\0b\03\01\01\00" ;; Data section + ) ;; end + "data count section required") + +;; data.drop requires a datacount section +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\07\01" ;; Code section + + ;; function 0 + "\05\00" + "\fc\09\00" ;; data.drop + "\0b" + + "\0b\03\01\01\00" ;; Data section + ) ;; end + "data count section required") + +;; passive element segment containing opcode other than ref.func or ref.null +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\07\01" ;; Element section with one segment + "\05\70" ;; Passive, funcref + "\01" ;; 1 element + "\ff\00\0b" ;; bad opcode, index 0, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + "illegal opcode") + +;; passive element segment containing type other than funcref +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\07\01" ;; Element section with one segment + "\05\7f" ;; Passive, i32 + "\01" ;; 1 element + "\d2\00\0b" ;; ref.func, index 0, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + "malformed reference type") + +;; passive element segment containing opcode ref.func +(module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\07\01" ;; Element section with one segment + "\05\70" ;; Passive, funcref + "\01" ;; 1 element + "\d2\00\0b" ;; ref.func, index 0, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + +;; passive element segment containing opcode ref.null +(module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\07\01" ;; Element section with one segment + "\05\70" ;; Passive, funcref + "\01" ;; 1 element + "\d0\70\0b" ;; ref.null, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + + +;; Type count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\01\00" ;; type count can be zero +) + +;; 2 type declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\07\02" ;; type section with inconsistent count (2 declared, 1 given) + "\60\00\00" ;; 1st type + ;; "\60\00\00" ;; 2nd type (missed) + ) + "unexpected end of section or function" +) + +;; 1 type declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\07\01" ;; type section with inconsistent count (1 declared, 2 given) + "\60\00\00" ;; 1st type + "\60\00\00" ;; 2nd type (redundant) + ) + "section size mismatch" +) + +;; Import count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; type 0 + "\02\01\00" ;; import count can be zero +) + +;; Malformed import kind +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\04\01" ;; import section with single entry + "\00" ;; string length 0 + "\00" ;; string length 0 + "\04" ;; malformed import kind + ) + "malformed import kind" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\05\01" ;; import section with single entry + "\00" ;; string length 0 + "\00" ;; string length 0 + "\04" ;; malformed import kind + "\00" ;; dummy byte + ) + "malformed import kind" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\04\01" ;; import section with single entry + "\00" ;; string length 0 + "\00" ;; string length 0 + "\05" ;; malformed import kind + ) + "malformed import kind" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\05\01" ;; import section with single entry + "\00" ;; string length 0 + "\00" ;; string length 0 + "\05" ;; malformed import kind + "\00" ;; dummy byte + ) + "malformed import kind" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\04\01" ;; import section with single entry + "\00" ;; string length 0 + "\00" ;; string length 0 + "\80" ;; malformed import kind + ) + "malformed import kind" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\05\01" ;; import section with single entry + "\00" ;; string length 0 + "\00" ;; string length 0 + "\80" ;; malformed import kind + "\00" ;; dummy byte + ) + "malformed import kind" +) + +;; 2 import declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; type 0 + "\02\16\02" ;; import section with inconsistent count (2 declared, 1 given) + ;; 1st import + "\08" ;; string length + "\73\70\65\63\74\65\73\74" ;; spectest + "\09" ;; string length + "\70\72\69\6e\74\5f\69\33\32" ;; print_i32 + "\00\00" ;; import kind, import signature index + ;; 2nd import + ;; (missed) + ) + "unexpected end of section or function" +) + +;; 1 import declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\09\02" ;; type section + "\60\01\7f\00" ;; type 0 + "\60\01\7d\00" ;; type 1 + "\02\2b\01" ;; import section with inconsistent count (1 declared, 2 given) + ;; 1st import + "\08" ;; string length + "\73\70\65\63\74\65\73\74" ;; spectest + "\09" ;; string length + "\70\72\69\6e\74\5f\69\33\32" ;; print_i32 + "\00\00" ;; import kind, import signature index + ;; 2nd import + ;; (redundant) + "\08" ;; string length + "\73\70\65\63\74\65\73\74" ;; spectest + "\09" ;; string length + "\70\72\69\6e\74\5f\66\33\32" ;; print_f32 + "\00\01" ;; import kind, import signature index + ) + "section size mismatch" +) + +;; Table count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\04\01\00" ;; table count can be zero +) + +;; 1 table declared, 0 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\04\01\01" ;; table section with inconsistent count (1 declared, 0 given) + ;; "\70\01\00\00" ;; table entity + ) + "unexpected end of section or function" +) + +;; Malformed table limits flag +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; table section with one entry + "\70" ;; anyfunc + "\02" ;; malformed table limits flag + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\04\01" ;; table section with one entry + "\70" ;; anyfunc + "\02" ;; malformed table limits flag + "\00" ;; dummy byte + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\06\01" ;; table section with one entry + "\70" ;; anyfunc + "\81\00" ;; malformed table limits flag as LEB128 + "\00\00" ;; dummy bytes + ) + "integer too large" +) + +;; Memory count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\05\01\00" ;; memory count can be zero +) + +;; 1 memory declared, 0 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\01\01" ;; memory section with inconsistent count (1 declared, 0 given) + ;; "\00\00" ;; memory 0 (missed) + ) + "unexpected end of section or function" +) + +;; Malformed memory limits flag +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\02\01" ;; memory section with one entry + "\02" ;; malformed memory limits flag + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section with one entry + "\02" ;; malformed memory limits flag + "\00" ;; dummy byte + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\05\01" ;; memory section with one entry + "\81\00" ;; malformed memory limits flag as LEB128 + "\00\00" ;; dummy bytes + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\05\01" ;; memory section with one entry + "\81\01" ;; malformed memory limits flag as LEB128 + "\00\00" ;; dummy bytes + ) + "integer representation too long" +) + +;; Global count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\06\01\00" ;; global count can be zero +) + +;; 2 global declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\06\02" ;; global section with inconsistent count (2 declared, 1 given) + "\7f\00\41\00\0b" ;; global 0 + ;; "\7f\00\41\00\0b" ;; global 1 (missed) + ) + "unexpected end of section or function" +) + +;; 1 global declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; global section with inconsistent count (1 declared, 2 given) + "\7f\00\41\00\0b" ;; global 0 + "\7f\00\41\00\0b" ;; global 1 (redundant) + ) + "section size mismatch" +) + +;; Export count can be 0 +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\03\02\00\00" ;; func section + "\07\01\00" ;; export count can be zero + "\0a\07\02" ;; code section + "\02\00\0b" ;; function body 0 + "\02\00\0b" ;; function body 1 +) + +;; 2 export declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\03\02\00\00" ;; func section + "\07\06\02" ;; export section with inconsistent count (2 declared, 1 given) + "\02" ;; export 0 + "\66\31" ;; export name + "\00\00" ;; export kind, export func index + ;; "\02" ;; export 1 (missed) + ;; "\66\32" ;; export name + ;; "\00\01" ;; export kind, export func index + "\0a\07\02" ;; code section + "\02\00\0b" ;; function body 0 + "\02\00\0b" ;; function body 1 + ) + "unexpected end of section or function" +) + +;; 1 export declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\03\02\00\00" ;; func section + "\07\0b\01" ;; export section with inconsistent count (1 declared, 2 given) + "\02" ;; export 0 + "\66\31" ;; export name + "\00\00" ;; export kind, export func index + "\02" ;; export 1 (redundant) + "\66\32" ;; export name + "\00\01" ;; export kind, export func index + "\0a\07\02" ;; code section + "\02\00\0b" ;; function body 0 + "\02\00\0b" ;; function body 1 + ) + "section size mismatch" +) + +;; elem segment count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\01\00" ;; elem segment count can be zero + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body +) + +;; 2 elem segment declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\07\02" ;; elem with inconsistent segment count (2 declared, 1 given) + "\00\41\00\0b\01\00" ;; elem 0 + ;; "\00\41\00\0b\01\00" ;; elem 1 (missed) + ) + "unexpected end" +) + +;; 2 elem segment declared, 1.5 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\07\02" ;; elem with inconsistent segment count (2 declared, 1 given) + "\00\41\00\0b\01\00" ;; elem 0 + "\00\41\00" ;; elem 1 (partial) + ;; "\0b\01\00" ;; elem 1 (missing part) + ) + "unexpected end" +) + +;; 1 elem segment declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\0d\01" ;; elem with inconsistent segment count (1 declared, 2 given) + "\00\41\00\0b\01\00" ;; elem 0 + "\00\41\00\0b\01\00" ;; elem 1 (redundant) + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body + ) + "section size mismatch" +) + +;; data segment count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\01\00" ;; data segment count can be zero +) + +;; 2 data segment declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\07\02" ;; data with inconsistent segment count (2 declared, 1 given) + "\00\41\00\0b\01\61" ;; data 0 + ;; "\00\41\01\0b\01\62" ;; data 1 (missed) + ) + "unexpected end of section or function" +) + +;; 1 data segment declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\0d\01" ;; data with inconsistent segment count (1 declared, 2 given) + "\00\41\00\0b\01\61" ;; data 0 + "\00\41\01\0b\01\62" ;; data 1 (redundant) + ) + "section size mismatch" +) + +;; data segment has 7 bytes declared, but 6 bytes given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\0c\01" ;; data section + "\00\41\03\0b" ;; data segment 0 + "\07" ;; data segment size with inconsistent lengths (7 declared, 6 given) + "\61\62\63\64\65\66" ;; 6 bytes given + ) + "unexpected end of section or function" +) + +;; data segment has 5 bytes declared, but 6 bytes given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\0c\01" ;; data section + "\00\41\00\0b" ;; data segment 0 + "\05" ;; data segment size with inconsistent lengths (5 declared, 6 given) + "\61\62\63\64\65\66" ;; 6 bytes given + ) + "section size mismatch" +) + +;; br_table target count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\0a\11\01" ;; code section + "\0f\00" ;; func 0 + "\02\40" ;; block 0 + "\41\01" ;; condition of if 0 + "\04\40" ;; if 0 + "\41\01" ;; index of br_table element + "\0e\00" ;; br_table target count can be zero + "\02" ;; break depth for default + "\0b\0b\0b" ;; end +) + +;; 1 br_table target declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\0a\12\01" ;; code section + "\11\00" ;; func 0 + "\02\40" ;; block 0 + "\41\01" ;; condition of if 0 + "\04\40" ;; if 0 + "\41\01" ;; index of br_table element + "\0e\01" ;; br_table with inconsistent target count (1 declared, 2 given) + "\00" ;; break depth 0 + "\01" ;; break depth 1 + "\02" ;; break depth for default + "\0b\0b\0b" ;; end + ) + "unexpected end of section or function" +) + +;; Start section +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\08\01\00" ;; Start section: function 0 + + "\0a\04\01" ;; Code section + ;; function 0 + "\02\00" + "\0b" ;; end +) + +;; Multiple start sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\08\01\00" ;; Start section: function 0 + "\08\01\00" ;; Start section: function 0 + + "\0a\04\01" ;; Code section + ;; function 0 + "\02\00" + "\0b" ;; end + ) + "junk after last section" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/br_on.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/br_on.wast new file mode 100644 index 000000000..370eb9396 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/br_on.wast @@ -0,0 +1,92 @@ +(module + (type $ft (func (result i32))) + (type $st (struct (field i16))) + (type $at (array i8)) + + (table 10 anyref) + + (elem declare func $f) + (func $f (result i32) (i32.const 9)) + + (func (export "init") (param $x externref) + (table.set (i32.const 0) (ref.null any)) + (table.set (i32.const 1) (i31.new (i32.const 7))) + (table.set (i32.const 2) (struct.new $st (i32.const 6) (rtt.canon $st))) + (table.set (i32.const 3) (array.new $at (i32.const 5) (i32.const 1) (rtt.canon $at))) + (table.set (i32.const 4) (ref.func $f)) + (table.set (i32.const 5) (rtt.canon $ft)) + (table.set (i32.const 6) (local.get $x)) + ) + + (func (export "br_on_null") (param $i i32) (result i32) + (block $l + (br_on_null $l (table.get (local.get $i))) + (return (i32.const -1)) + ) + (i32.const 0) + ) + (func (export "br_on_i31") (param $i i32) (result i32) + (block $l (result (ref i31)) + (br_on_i31 $l (table.get (local.get $i))) + (return (i32.const -1)) + ) + (i31.get_u) + ) + (func (export "br_on_data") (param $i i32) (result i32) + (block $l (result (ref data)) + (br_on_data $l (table.get (local.get $i))) + (return (i32.const -1)) + ) + (block $l2 (param dataref) (result (ref $st)) + (block $l3 (param dataref) (result (ref $at)) + (br_on_cast $l2 (rtt.canon $st)) + (br_on_cast $l3 (rtt.canon $at)) + (return (i32.const -2)) + ) + (return (array.get_u $at (i32.const 0))) + ) + (struct.get_s $st 0) + ) + (func (export "br_on_func") (param $i i32) (result i32) + (block $l (result (ref func)) + (br_on_func $l (table.get (local.get $i))) + (return (i32.const -1)) + ) + (ref.cast (rtt.canon $ft)) + (call_ref) + ) +) + +(invoke "init" (ref.extern 0)) + +(assert_return (invoke "br_on_null" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "br_on_null" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "br_on_null" (i32.const 2)) (i32.const -1)) +(assert_return (invoke "br_on_null" (i32.const 3)) (i32.const -1)) +(assert_return (invoke "br_on_null" (i32.const 4)) (i32.const -1)) +(assert_return (invoke "br_on_null" (i32.const 5)) (i32.const -1)) +(assert_return (invoke "br_on_null" (i32.const 6)) (i32.const -1)) + +(assert_return (invoke "br_on_i31" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "br_on_i31" (i32.const 1)) (i32.const 7)) +(assert_return (invoke "br_on_i31" (i32.const 2)) (i32.const -1)) +(assert_return (invoke "br_on_i31" (i32.const 3)) (i32.const -1)) +(assert_return (invoke "br_on_i31" (i32.const 4)) (i32.const -1)) +(assert_return (invoke "br_on_i31" (i32.const 5)) (i32.const -1)) +(assert_return (invoke "br_on_i31" (i32.const 6)) (i32.const -1)) + +(assert_return (invoke "br_on_data" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "br_on_data" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "br_on_data" (i32.const 2)) (i32.const 6)) +(assert_return (invoke "br_on_data" (i32.const 3)) (i32.const 5)) +(assert_return (invoke "br_on_data" (i32.const 4)) (i32.const -1)) +(assert_return (invoke "br_on_data" (i32.const 5)) (i32.const -1)) +(assert_return (invoke "br_on_data" (i32.const 6)) (i32.const -1)) + +(assert_return (invoke "br_on_func" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "br_on_func" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "br_on_func" (i32.const 2)) (i32.const -1)) +(assert_return (invoke "br_on_func" (i32.const 3)) (i32.const -1)) +(assert_return (invoke "br_on_func" (i32.const 4)) (i32.const 9)) +(assert_return (invoke "br_on_func" (i32.const 5)) (i32.const -1)) +(assert_return (invoke "br_on_func" (i32.const 6)) (i32.const -1)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/br_on_cast.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/br_on_cast.wast new file mode 100644 index 000000000..89ebc608a --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/br_on_cast.wast @@ -0,0 +1,110 @@ +(module + (type $t0 (struct)) + (type $t1 (struct (field i32))) + (type $t1' (struct (field i32))) + (type $t2 (struct (field i32 i32))) + (type $t2' (struct (field i32 i32))) + (type $t3 (struct (field i32 i32))) + + (global $t0 (rtt $t0) (rtt.canon $t0)) + (global $t0' (rtt $t0) (rtt.canon $t0)) + (global $t1 (rtt $t1) (rtt.sub $t1 (global.get $t0))) + (global $t1' (rtt $t1') (rtt.sub $t1' (global.get $t0))) + (global $t2 (rtt $t2) (rtt.sub $t2 (global.get $t1))) + (global $t2' (rtt $t2') (rtt.sub $t2' (global.get $t1'))) + (global $t3 (rtt $t3) (rtt.sub $t3 (global.get $t0))) + (global $t4 (rtt $t3) (rtt.sub $t3 (rtt.sub $t0 (global.get $t0)))) + + (table 20 dataref) + + (func $init + (table.set (i32.const 0) (struct.new_default $t0 (global.get $t0))) + (table.set (i32.const 10) (struct.new_default $t0 (global.get $t0'))) + (table.set (i32.const 1) (struct.new_default $t1 (global.get $t1))) + (table.set (i32.const 11) (struct.new_default $t1' (global.get $t1'))) + (table.set (i32.const 2) (struct.new_default $t2 (global.get $t2))) + (table.set (i32.const 12) (struct.new_default $t2' (global.get $t2'))) + (table.set (i32.const 3) (struct.new_default $t3 (global.get $t3))) + (table.set (i32.const 4) (struct.new_default $t3 (global.get $t4))) + ) + + (func (export "test-sub") + (call $init) + (block $l (result dataref) + ;; must succeed + (drop (block (result dataref) (br_on_cast 0 (ref.null data) (global.get $t0)))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 0)) (global.get $t0)))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 1)) (global.get $t0)))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 2)) (global.get $t0)))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 3)) (global.get $t0)))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 4)) (global.get $t0)))) + + (drop (block (result dataref) (br_on_cast 0 (ref.null data) (global.get $t1)))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 1)) (global.get $t1)))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 2)) (global.get $t1)))) + + (drop (block (result dataref) (br_on_cast 0 (ref.null data) (global.get $t2)))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 2)) (global.get $t2)))) + + (drop (block (result dataref) (br_on_cast 0 (ref.null data) (global.get $t3)))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 3)) (global.get $t3)))) + + (drop (block (result dataref) (br_on_cast 0 (ref.null data) (global.get $t4)))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 4)) (global.get $t4)))) + + ;; must not succeed + (br_on_cast $l (table.get (i32.const 0)) (global.get $t1)) + (br_on_cast $l (table.get (i32.const 3)) (global.get $t1)) + (br_on_cast $l (table.get (i32.const 4)) (global.get $t1)) + + (br_on_cast $l (table.get (i32.const 0)) (global.get $t2)) + (br_on_cast $l (table.get (i32.const 1)) (global.get $t2)) + (br_on_cast $l (table.get (i32.const 3)) (global.get $t2)) + (br_on_cast $l (table.get (i32.const 4)) (global.get $t2)) + + (br_on_cast $l (table.get (i32.const 0)) (global.get $t3)) + (br_on_cast $l (table.get (i32.const 1)) (global.get $t3)) + (br_on_cast $l (table.get (i32.const 2)) (global.get $t3)) + (br_on_cast $l (table.get (i32.const 4)) (global.get $t3)) + + (br_on_cast $l (table.get (i32.const 0)) (global.get $t4)) + (br_on_cast $l (table.get (i32.const 1)) (global.get $t4)) + (br_on_cast $l (table.get (i32.const 2)) (global.get $t4)) + (br_on_cast $l (table.get (i32.const 3)) (global.get $t4)) + + (return) + ) + (unreachable) + ) + + (func (export "test-canon") + (call $init) + (block $l + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 0)) (global.get $t0')))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 1)) (global.get $t0')))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 2)) (global.get $t0')))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 3)) (global.get $t0')))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 4)) (global.get $t0')))) + + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 10)) (global.get $t0)))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 11)) (global.get $t0)))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 12)) (global.get $t0)))) + + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 1)) (global.get $t1')))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 2)) (global.get $t1')))) + + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 11)) (global.get $t1)))) + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 12)) (global.get $t1)))) + + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 2)) (global.get $t2')))) + + (drop (block (result dataref) (br_on_cast 0 (table.get (i32.const 12)) (global.get $t2)))) + + (return) + ) + (unreachable) + ) +) + +(invoke "test-sub") +(invoke "test-canon") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/br_on_null.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/br_on_null.wast new file mode 100644 index 000000000..3bae85000 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/br_on_null.wast @@ -0,0 +1,75 @@ +(module + (type $t (func (result i32))) + + (func $nn (param $r (ref $t)) (result i32) + (block $l + (return (call_ref (br_on_null $l (local.get $r)))) + ) + (i32.const -1) + ) + (func $n (param $r (ref null $t)) (result i32) + (block $l + (return (call_ref (br_on_null $l (local.get $r)))) + ) + (i32.const -1) + ) + + (elem func $f) + (func $f (result i32) (i32.const 7)) + + (func (export "nullable-null") (result i32) (call $n (ref.null $t))) + (func (export "nonnullable-f") (result i32) (call $nn (ref.func $f))) + (func (export "nullable-f") (result i32) (call $n (ref.func $f))) + + (func (export "unreachable") (result i32) + (block $l + (return (call_ref (br_on_null $l (unreachable)))) + ) + (i32.const -1) + ) +) + +(assert_trap (invoke "unreachable") "unreachable") + +(assert_return (invoke "nullable-null") (i32.const -1)) +(assert_return (invoke "nonnullable-f") (i32.const 7)) +(assert_return (invoke "nullable-f") (i32.const 7)) + +(assert_invalid + (module + (type $t (func (result i32))) + (func $g (param $r (ref $t)) (drop (br_on_null 0 (local.get $r)))) + (func (call $g (ref.null $t))) + ) + "type mismatch" +) + +(module + (type $t (func)) + (func (param $r (ref $t)) (drop (br_on_null 0 (local.get $r)))) + (func (param $r (ref func)) (drop (br_on_null 0 (local.get $r)))) + (func (param $r (ref extern)) (drop (br_on_null 0 (local.get $r)))) +) + + +(module + (type $t (func (param i32) (result i32))) + (elem func $f) + (func $f (param i32) (result i32) (i32.mul (local.get 0) (local.get 0))) + + (func $a (param $n i32) (param $r (ref null $t)) (result i32) + (block $l (result i32) + (return (call_ref (br_on_null $l (local.get $n) (local.get $r)))) + ) + ) + + (func (export "args-null") (param $n i32) (result i32) + (call $a (local.get $n) (ref.null $t)) + ) + (func (export "args-f") (param $n i32) (result i32) + (call $a (local.get $n) (ref.func $f)) + ) +) + +(assert_return (invoke "args-null" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "args-f" (i32.const 3)) (i32.const 9)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/br_table.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/br_table.wast new file mode 100644 index 000000000..33650e215 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/br_table.wast @@ -0,0 +1,1728 @@ +;; Test `br_table` operator + +(module + ;; Auxiliary definition + (func $dummy) + + (func (export "type-i32") + (block (drop (i32.ctz (br_table 0 0 (i32.const 0))))) + ) + (func (export "type-i64") + (block (drop (i64.ctz (br_table 0 0 (i32.const 0))))) + ) + (func (export "type-f32") + (block (drop (f32.neg (br_table 0 0 (i32.const 0))))) + ) + (func (export "type-f64") + (block (drop (f64.neg (br_table 0 0 (i32.const 0))))) + ) + + (func (export "type-i32-value") (result i32) + (block (result i32) (i32.ctz (br_table 0 0 (i32.const 1) (i32.const 0)))) + ) + (func (export "type-i64-value") (result i64) + (block (result i64) (i64.ctz (br_table 0 0 (i64.const 2) (i32.const 0)))) + ) + (func (export "type-f32-value") (result f32) + (block (result f32) (f32.neg (br_table 0 0 (f32.const 3) (i32.const 0)))) + ) + (func (export "type-f64-value") (result f64) + (block (result f64) (f64.neg (br_table 0 0 (f64.const 4) (i32.const 0)))) + ) + + (func (export "empty") (param i32) (result i32) + (block (br_table 0 (local.get 0)) (return (i32.const 21))) + (i32.const 22) + ) + (func (export "empty-value") (param i32) (result i32) + (block (result i32) + (br_table 0 (i32.const 33) (local.get 0)) (i32.const 31) + ) + ) + + (func (export "singleton") (param i32) (result i32) + (block + (block + (br_table 1 0 (local.get 0)) + (return (i32.const 21)) + ) + (return (i32.const 20)) + ) + (i32.const 22) + ) + + (func (export "singleton-value") (param i32) (result i32) + (block (result i32) + (drop + (block (result i32) + (br_table 0 1 (i32.const 33) (local.get 0)) + (return (i32.const 31)) + ) + ) + (i32.const 32) + ) + ) + + (func (export "multiple") (param i32) (result i32) + (block + (block + (block + (block + (block + (br_table 3 2 1 0 4 (local.get 0)) + (return (i32.const 99)) + ) + (return (i32.const 100)) + ) + (return (i32.const 101)) + ) + (return (i32.const 102)) + ) + (return (i32.const 103)) + ) + (i32.const 104) + ) + + (func (export "multiple-value") (param i32) (result i32) + (local i32) + (local.set 1 (block (result i32) + (local.set 1 (block (result i32) + (local.set 1 (block (result i32) + (local.set 1 (block (result i32) + (local.set 1 (block (result i32) + (br_table 3 2 1 0 4 (i32.const 200) (local.get 0)) + (return (i32.add (local.get 1) (i32.const 99))) + )) + (return (i32.add (local.get 1) (i32.const 10))) + )) + (return (i32.add (local.get 1) (i32.const 11))) + )) + (return (i32.add (local.get 1) (i32.const 12))) + )) + (return (i32.add (local.get 1) (i32.const 13))) + )) + (i32.add (local.get 1) (i32.const 14)) + ) + + (func (export "large") (param i32) (result i32) + (block + (block + (br_table + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + (local.get 0) + ) + (return (i32.const -1)) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) + + (func (export "as-block-first") + (block (br_table 0 0 0 (i32.const 0)) (call $dummy)) + ) + (func (export "as-block-mid") + (block (call $dummy) (br_table 0 0 0 (i32.const 0)) (call $dummy)) + ) + (func (export "as-block-last") + (block (nop) (call $dummy) (br_table 0 0 0 (i32.const 0))) + ) + (func (export "as-block-value") (result i32) + (block (result i32) + (nop) (call $dummy) (br_table 0 0 0 (i32.const 2) (i32.const 0)) + ) + ) + + (func (export "as-loop-first") (result i32) + (loop (result i32) (br_table 1 1 (i32.const 3) (i32.const 0)) (i32.const 1)) + ) + (func (export "as-loop-mid") (result i32) + (loop (result i32) + (call $dummy) + (br_table 1 1 1 (i32.const 4) (i32.const -1)) + (i32.const 2) + ) + ) + (func (export "as-loop-last") (result i32) + (loop (result i32) + (nop) (call $dummy) (br_table 1 1 1 (i32.const 5) (i32.const 1)) + ) + ) + + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (br_table 0 (i32.const 9) (i32.const 0)))) + ) + + (func (export "as-br_if-cond") + (block (br_if 0 (br_table 0 0 0 (i32.const 1)))) + ) + (func (export "as-br_if-value") (result i32) + (block (result i32) + (drop (br_if 0 (br_table 0 (i32.const 8) (i32.const 0)) (i32.const 1))) + (i32.const 7) + ) + ) + (func (export "as-br_if-value-cond") (result i32) + (block (result i32) + (drop (br_if 0 (i32.const 6) (br_table 0 0 (i32.const 9) (i32.const 0)))) + (i32.const 7) + ) + ) + + (func (export "as-br_table-index") + (block (br_table 0 0 0 (br_table 0 (i32.const 1)))) + ) + (func (export "as-br_table-value") (result i32) + (block (result i32) + (br_table 0 0 0 (br_table 0 (i32.const 10) (i32.const 0)) (i32.const 1)) + (i32.const 7) + ) + ) + (func (export "as-br_table-value-index") (result i32) + (block (result i32) + (br_table 0 0 (i32.const 6) (br_table 0 (i32.const 11) (i32.const 1))) + (i32.const 7) + ) + ) + + (func (export "as-return-value") (result i64) + (block (result i64) (return (br_table 0 (i64.const 7) (i32.const 0)))) + ) + + (func (export "as-if-cond") (result i32) + (block (result i32) + (if (result i32) + (br_table 0 (i32.const 2) (i32.const 0)) + (then (i32.const 0)) + (else (i32.const 1)) + ) + ) + ) + (func (export "as-if-then") (param i32 i32) (result i32) + (block (result i32) + (if (result i32) + (local.get 0) + (then (br_table 1 (i32.const 3) (i32.const 0))) + (else (local.get 1)) + ) + ) + ) + (func (export "as-if-else") (param i32 i32) (result i32) + (block (result i32) + (if (result i32) + (local.get 0) + (then (local.get 1)) + (else (br_table 1 0 (i32.const 4) (i32.const 0))) + ) + ) + ) + + (func (export "as-select-first") (param i32 i32) (result i32) + (block (result i32) + (select + (br_table 0 (i32.const 5) (i32.const 0)) (local.get 0) (local.get 1) + ) + ) + ) + (func (export "as-select-second") (param i32 i32) (result i32) + (block (result i32) + (select + (local.get 0) (br_table 0 (i32.const 6) (i32.const 1)) (local.get 1) + ) + ) + ) + (func (export "as-select-cond") (result i32) + (block (result i32) + (select + (i32.const 0) (i32.const 1) (br_table 0 (i32.const 7) (i32.const 1)) + ) + ) + ) + + (func $f (param i32 i32 i32) (result i32) (i32.const -1)) + (func (export "as-call-first") (result i32) + (block (result i32) + (call $f + (br_table 0 (i32.const 12) (i32.const 1)) (i32.const 2) (i32.const 3) + ) + ) + ) + (func (export "as-call-mid") (result i32) + (block (result i32) + (call $f + (i32.const 1) (br_table 0 (i32.const 13) (i32.const 1)) (i32.const 3) + ) + ) + ) + (func (export "as-call-last") (result i32) + (block (result i32) + (call $f + (i32.const 1) (i32.const 2) (br_table 0 (i32.const 14) (i32.const 1)) + ) + ) + ) + + (type $sig (func (param i32 i32 i32) (result i32))) + (table funcref (elem $f)) + (func (export "as-call_indirect-first") (result i32) + (block (result i32) + (call_indirect (type $sig) + (br_table 0 (i32.const 20) (i32.const 1)) (i32.const 1) (i32.const 2) + (i32.const 3) + ) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) (br_table 0 (i32.const 21) (i32.const 1)) (i32.const 2) + (i32.const 3) + ) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) (i32.const 1) (br_table 0 (i32.const 22) (i32.const 1)) + (i32.const 3) + ) + ) + ) + (func (export "as-call_indirect-func") (result i32) + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) (i32.const 1) (i32.const 2) + (br_table 0 (i32.const 23) (i32.const 1)) + ) + ) + ) + + (func (export "as-local.set-value") (result i32) + (local f32) + (block (result i32) + (local.set 0 (br_table 0 (i32.const 17) (i32.const 1))) + (i32.const -1) + ) + ) + (func (export "as-local.tee-value") (result i32) + (local i32) + (block (result i32) + (local.set 0 (br_table 0 (i32.const 1) (i32.const 1))) + (i32.const -1) + ) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (result i32) + (block (result i32) + (global.set $a (br_table 0 (i32.const 1) (i32.const 1))) + (i32.const -1) + ) + ) + + (memory 1) + (func (export "as-load-address") (result f32) + (block (result f32) (f32.load (br_table 0 (f32.const 1.7) (i32.const 1)))) + ) + (func (export "as-loadN-address") (result i64) + (block (result i64) (i64.load8_s (br_table 0 (i64.const 30) (i32.const 1)))) + ) + + (func (export "as-store-address") (result i32) + (block (result i32) + (f64.store (br_table 0 (i32.const 30) (i32.const 1)) (f64.const 7)) + (i32.const -1) + ) + ) + (func (export "as-store-value") (result i32) + (block (result i32) + (i64.store (i32.const 2) (br_table 0 (i32.const 31) (i32.const 1))) + (i32.const -1) + ) + ) + + (func (export "as-storeN-address") (result i32) + (block (result i32) + (i32.store8 (br_table 0 (i32.const 32) (i32.const 0)) (i32.const 7)) + (i32.const -1) + ) + ) + (func (export "as-storeN-value") (result i32) + (block (result i32) + (i64.store16 (i32.const 2) (br_table 0 (i32.const 33) (i32.const 0))) + (i32.const -1) + ) + ) + + (func (export "as-unary-operand") (result f32) + (block (result f32) (f32.neg (br_table 0 (f32.const 3.4) (i32.const 0)))) + ) + + (func (export "as-binary-left") (result i32) + (block (result i32) + (i32.add (br_table 0 0 (i32.const 3) (i32.const 0)) (i32.const 10)) + ) + ) + (func (export "as-binary-right") (result i64) + (block (result i64) + (i64.sub (i64.const 10) (br_table 0 (i64.const 45) (i32.const 0))) + ) + ) + + (func (export "as-test-operand") (result i32) + (block (result i32) (i32.eqz (br_table 0 (i32.const 44) (i32.const 0)))) + ) + + (func (export "as-compare-left") (result i32) + (block (result i32) + (f64.le (br_table 0 0 (i32.const 43) (i32.const 0)) (f64.const 10)) + ) + ) + (func (export "as-compare-right") (result i32) + (block (result i32) + (f32.ne (f32.const 10) (br_table 0 (i32.const 42) (i32.const 0))) + ) + ) + + (func (export "as-convert-operand") (result i32) + (block (result i32) + (i32.wrap_i64 (br_table 0 (i32.const 41) (i32.const 0))) + ) + ) + + (func (export "as-memory.grow-size") (result i32) + (block (result i32) (memory.grow (br_table 0 (i32.const 40) (i32.const 0)))) + ) + + (func (export "nested-block-value") (param i32) (result i32) + (block (result i32) + (drop (i32.const -1)) + (i32.add + (i32.const 1) + (block (result i32) + (i32.add + (i32.const 2) + (block (result i32) + (drop (i32.const 4)) + (i32.add + (i32.const 8) + (br_table 0 1 2 (i32.const 16) (local.get 0)) + ) + ) + ) + ) + ) + ) + ) + + (func (export "nested-br-value") (param i32) (result i32) + (block (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (block (result i32) + (drop (i32.const 4)) + (br 0 (br_table 2 1 0 (i32.const 8) (local.get 0))) + ) + ) + (i32.const 16) + ) + ) + ) + ) + + (func (export "nested-br_if-value") (param i32) (result i32) + (block (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (block (result i32) + (drop (i32.const 4)) + (drop + (br_if 0 + (br_table 0 1 2 (i32.const 8) (local.get 0)) + (i32.const 1) + ) + ) + (i32.const 32) + ) + ) + (i32.const 16) + ) + ) + ) + ) + + (func (export "nested-br_if-value-cond") (param i32) (result i32) + (block (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (br_if 0 (i32.const 4) (br_table 0 1 0 (i32.const 8) (local.get 0))) + ) + (i32.const 16) + ) + ) + ) + ) + + (func (export "nested-br_table-value") (param i32) (result i32) + (block (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (block (result i32) + (drop (i32.const 4)) + (br_table 0 (br_table 0 1 2 (i32.const 8) (local.get 0)) (i32.const 1)) + (i32.const 32) + ) + ) + (i32.const 16) + ) + ) + ) + ) + + (func (export "nested-br_table-value-index") (param i32) (result i32) + (block (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (br_table 0 (i32.const 4) (br_table 0 1 0 (i32.const 8) (local.get 0))) + (i32.const 16) + ) + ) + ) + ) + + (func (export "nested-br_table-loop-block") (param i32) (result i32) + (local.set 0 + (loop (result i32) + (block + (br_table 1 0 0 (local.get 0)) + ) + (i32.const 0) + ) + ) + (loop (result i32) + (block + (br_table 0 1 1 (local.get 0)) + ) + (i32.const 3) + ) + ) + + (func (export "meet-externref") (param i32) (param externref) (result externref) + (block $l1 (result externref) + (block $l2 (result externref) + (br_table $l1 $l2 $l1 (local.get 1) (local.get 0)) + ) + ) + ) + + (func (export "meet-bottom") + (block (result f64) + (block (result f32) + (unreachable) + (br_table 0 1 1 (i32.const 1)) + ) + (drop) + (f64.const 0) + ) + (drop) + ) + + (type $t (func)) + (func $tf) + (table $t (ref null $t) (elem $tf)) + (func (export "meet-funcref-1") (param i32) (result (ref null func)) + (block $l1 (result (ref null func)) + (block $l2 (result (ref null $t)) + (br_table $l1 $l1 $l2 (table.get $t (i32.const 0)) (local.get 0)) + ) + ) + ) + (func (export "meet-funcref-2") (param i32) (result (ref null func)) + (block $l1 (result (ref null func)) + (block $l2 (result (ref null $t)) + (br_table $l2 $l2 $l1 (table.get $t (i32.const 0)) (local.get 0)) + ) + ) + ) + (func (export "meet-funcref-3") (param i32) (result (ref null func)) + (block $l1 (result (ref null func)) + (block $l2 (result (ref null $t)) + (br_table $l2 $l1 $l2 (table.get $t (i32.const 0)) (local.get 0)) + ) + ) + ) + (func (export "meet-funcref-4") (param i32) (result (ref null func)) + (block $l1 (result (ref null func)) + (block $l2 (result (ref null $t)) + (br_table $l1 $l2 $l1 (table.get $t (i32.const 0)) (local.get 0)) + ) + ) + ) + + (func (export "meet-nullref") (param i32) (result (ref null func)) + (block $l1 (result (ref null func)) + (block $l2 (result (ref null $t)) + (br_table $l1 $l2 $l1 (ref.null $t) (local.get 0)) + ) + ) + ) + + (func (export "meet-multi-ref") (param i32) (result (ref null func)) + (block $l1 (result (ref null func)) + (block $l2 (result (ref null $t)) + (block $l3 (result (ref $t)) + (br_table $l3 $l2 $l1 (ref.func $tf) (local.get 0)) + ) + ) + ) + ) +) + +(assert_return (invoke "type-i32")) +(assert_return (invoke "type-i64")) +(assert_return (invoke "type-f32")) +(assert_return (invoke "type-f64")) + +(assert_return (invoke "type-i32-value") (i32.const 1)) +(assert_return (invoke "type-i64-value") (i64.const 2)) +(assert_return (invoke "type-f32-value") (f32.const 3)) +(assert_return (invoke "type-f64-value") (f64.const 4)) + +(assert_return (invoke "empty" (i32.const 0)) (i32.const 22)) +(assert_return (invoke "empty" (i32.const 1)) (i32.const 22)) +(assert_return (invoke "empty" (i32.const 11)) (i32.const 22)) +(assert_return (invoke "empty" (i32.const -1)) (i32.const 22)) +(assert_return (invoke "empty" (i32.const -100)) (i32.const 22)) +(assert_return (invoke "empty" (i32.const 0xffffffff)) (i32.const 22)) + +(assert_return (invoke "empty-value" (i32.const 0)) (i32.const 33)) +(assert_return (invoke "empty-value" (i32.const 1)) (i32.const 33)) +(assert_return (invoke "empty-value" (i32.const 11)) (i32.const 33)) +(assert_return (invoke "empty-value" (i32.const -1)) (i32.const 33)) +(assert_return (invoke "empty-value" (i32.const -100)) (i32.const 33)) +(assert_return (invoke "empty-value" (i32.const 0xffffffff)) (i32.const 33)) + +(assert_return (invoke "singleton" (i32.const 0)) (i32.const 22)) +(assert_return (invoke "singleton" (i32.const 1)) (i32.const 20)) +(assert_return (invoke "singleton" (i32.const 11)) (i32.const 20)) +(assert_return (invoke "singleton" (i32.const -1)) (i32.const 20)) +(assert_return (invoke "singleton" (i32.const -100)) (i32.const 20)) +(assert_return (invoke "singleton" (i32.const 0xffffffff)) (i32.const 20)) + +(assert_return (invoke "singleton-value" (i32.const 0)) (i32.const 32)) +(assert_return (invoke "singleton-value" (i32.const 1)) (i32.const 33)) +(assert_return (invoke "singleton-value" (i32.const 11)) (i32.const 33)) +(assert_return (invoke "singleton-value" (i32.const -1)) (i32.const 33)) +(assert_return (invoke "singleton-value" (i32.const -100)) (i32.const 33)) +(assert_return (invoke "singleton-value" (i32.const 0xffffffff)) (i32.const 33)) + +(assert_return (invoke "multiple" (i32.const 0)) (i32.const 103)) +(assert_return (invoke "multiple" (i32.const 1)) (i32.const 102)) +(assert_return (invoke "multiple" (i32.const 2)) (i32.const 101)) +(assert_return (invoke "multiple" (i32.const 3)) (i32.const 100)) +(assert_return (invoke "multiple" (i32.const 4)) (i32.const 104)) +(assert_return (invoke "multiple" (i32.const 5)) (i32.const 104)) +(assert_return (invoke "multiple" (i32.const 6)) (i32.const 104)) +(assert_return (invoke "multiple" (i32.const 10)) (i32.const 104)) +(assert_return (invoke "multiple" (i32.const -1)) (i32.const 104)) +(assert_return (invoke "multiple" (i32.const 0xffffffff)) (i32.const 104)) + +(assert_return (invoke "multiple-value" (i32.const 0)) (i32.const 213)) +(assert_return (invoke "multiple-value" (i32.const 1)) (i32.const 212)) +(assert_return (invoke "multiple-value" (i32.const 2)) (i32.const 211)) +(assert_return (invoke "multiple-value" (i32.const 3)) (i32.const 210)) +(assert_return (invoke "multiple-value" (i32.const 4)) (i32.const 214)) +(assert_return (invoke "multiple-value" (i32.const 5)) (i32.const 214)) +(assert_return (invoke "multiple-value" (i32.const 6)) (i32.const 214)) +(assert_return (invoke "multiple-value" (i32.const 10)) (i32.const 214)) +(assert_return (invoke "multiple-value" (i32.const -1)) (i32.const 214)) +(assert_return (invoke "multiple-value" (i32.const 0xffffffff)) (i32.const 214)) + +(assert_return (invoke "large" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "large" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "large" (i32.const 100)) (i32.const 0)) +(assert_return (invoke "large" (i32.const 101)) (i32.const 1)) +(assert_return (invoke "large" (i32.const 10000)) (i32.const 0)) +(assert_return (invoke "large" (i32.const 10001)) (i32.const 1)) +(assert_return (invoke "large" (i32.const 1000000)) (i32.const 1)) +(assert_return (invoke "large" (i32.const 1000001)) (i32.const 1)) + +(assert_return (invoke "as-block-first")) +(assert_return (invoke "as-block-mid")) +(assert_return (invoke "as-block-last")) +(assert_return (invoke "as-block-value") (i32.const 2)) + +(assert_return (invoke "as-loop-first") (i32.const 3)) +(assert_return (invoke "as-loop-mid") (i32.const 4)) +(assert_return (invoke "as-loop-last") (i32.const 5)) + +(assert_return (invoke "as-br-value") (i32.const 9)) + +(assert_return (invoke "as-br_if-cond")) +(assert_return (invoke "as-br_if-value") (i32.const 8)) +(assert_return (invoke "as-br_if-value-cond") (i32.const 9)) + +(assert_return (invoke "as-br_table-index")) +(assert_return (invoke "as-br_table-value") (i32.const 10)) +(assert_return (invoke "as-br_table-value-index") (i32.const 11)) + +(assert_return (invoke "as-return-value") (i64.const 7)) + +(assert_return (invoke "as-if-cond") (i32.const 2)) +(assert_return (invoke "as-if-then" (i32.const 1) (i32.const 6)) (i32.const 3)) +(assert_return (invoke "as-if-then" (i32.const 0) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-if-else" (i32.const 0) (i32.const 6)) (i32.const 4)) +(assert_return (invoke "as-if-else" (i32.const 1) (i32.const 6)) (i32.const 6)) + +(assert_return (invoke "as-select-first" (i32.const 0) (i32.const 6)) (i32.const 5)) +(assert_return (invoke "as-select-first" (i32.const 1) (i32.const 6)) (i32.const 5)) +(assert_return (invoke "as-select-second" (i32.const 0) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-select-second" (i32.const 1) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-select-cond") (i32.const 7)) + +(assert_return (invoke "as-call-first") (i32.const 12)) +(assert_return (invoke "as-call-mid") (i32.const 13)) +(assert_return (invoke "as-call-last") (i32.const 14)) + +(assert_return (invoke "as-call_indirect-first") (i32.const 20)) +(assert_return (invoke "as-call_indirect-mid") (i32.const 21)) +(assert_return (invoke "as-call_indirect-last") (i32.const 22)) +(assert_return (invoke "as-call_indirect-func") (i32.const 23)) + +(assert_return (invoke "as-local.set-value") (i32.const 17)) +(assert_return (invoke "as-local.tee-value") (i32.const 1)) +(assert_return (invoke "as-global.set-value") (i32.const 1)) + +(assert_return (invoke "as-load-address") (f32.const 1.7)) +(assert_return (invoke "as-loadN-address") (i64.const 30)) + +(assert_return (invoke "as-store-address") (i32.const 30)) +(assert_return (invoke "as-store-value") (i32.const 31)) +(assert_return (invoke "as-storeN-address") (i32.const 32)) +(assert_return (invoke "as-storeN-value") (i32.const 33)) + +(assert_return (invoke "as-unary-operand") (f32.const 3.4)) + +(assert_return (invoke "as-binary-left") (i32.const 3)) +(assert_return (invoke "as-binary-right") (i64.const 45)) + +(assert_return (invoke "as-test-operand") (i32.const 44)) + +(assert_return (invoke "as-compare-left") (i32.const 43)) +(assert_return (invoke "as-compare-right") (i32.const 42)) + +(assert_return (invoke "as-convert-operand") (i32.const 41)) + +(assert_return (invoke "as-memory.grow-size") (i32.const 40)) + +(assert_return (invoke "nested-block-value" (i32.const 0)) (i32.const 19)) +(assert_return (invoke "nested-block-value" (i32.const 1)) (i32.const 17)) +(assert_return (invoke "nested-block-value" (i32.const 2)) (i32.const 16)) +(assert_return (invoke "nested-block-value" (i32.const 10)) (i32.const 16)) +(assert_return (invoke "nested-block-value" (i32.const -1)) (i32.const 16)) +(assert_return (invoke "nested-block-value" (i32.const 100000)) (i32.const 16)) + +(assert_return (invoke "nested-br-value" (i32.const 0)) (i32.const 8)) +(assert_return (invoke "nested-br-value" (i32.const 1)) (i32.const 9)) +(assert_return (invoke "nested-br-value" (i32.const 2)) (i32.const 17)) +(assert_return (invoke "nested-br-value" (i32.const 11)) (i32.const 17)) +(assert_return (invoke "nested-br-value" (i32.const -4)) (i32.const 17)) +(assert_return (invoke "nested-br-value" (i32.const 10213210)) (i32.const 17)) + +(assert_return (invoke "nested-br_if-value" (i32.const 0)) (i32.const 17)) +(assert_return (invoke "nested-br_if-value" (i32.const 1)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value" (i32.const 2)) (i32.const 8)) +(assert_return (invoke "nested-br_if-value" (i32.const 9)) (i32.const 8)) +(assert_return (invoke "nested-br_if-value" (i32.const -9)) (i32.const 8)) +(assert_return (invoke "nested-br_if-value" (i32.const 999999)) (i32.const 8)) + +(assert_return (invoke "nested-br_if-value-cond" (i32.const 0)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const 1)) (i32.const 8)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const 2)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const 3)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const -1000000)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const 9423975)) (i32.const 9)) + +(assert_return (invoke "nested-br_table-value" (i32.const 0)) (i32.const 17)) +(assert_return (invoke "nested-br_table-value" (i32.const 1)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value" (i32.const 2)) (i32.const 8)) +(assert_return (invoke "nested-br_table-value" (i32.const 9)) (i32.const 8)) +(assert_return (invoke "nested-br_table-value" (i32.const -9)) (i32.const 8)) +(assert_return (invoke "nested-br_table-value" (i32.const 999999)) (i32.const 8)) + +(assert_return (invoke "nested-br_table-value-index" (i32.const 0)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value-index" (i32.const 1)) (i32.const 8)) +(assert_return (invoke "nested-br_table-value-index" (i32.const 2)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value-index" (i32.const 3)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value-index" (i32.const -1000000)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value-index" (i32.const 9423975)) (i32.const 9)) + +(assert_return (invoke "nested-br_table-loop-block" (i32.const 1)) (i32.const 3)) + +(assert_return (invoke "meet-externref" (i32.const 0) (ref.extern 1)) (ref.extern 1)) +(assert_return (invoke "meet-externref" (i32.const 1) (ref.extern 1)) (ref.extern 1)) +(assert_return (invoke "meet-externref" (i32.const 2) (ref.extern 1)) (ref.extern 1)) + +(assert_return (invoke "meet-funcref-1" (i32.const 0)) (ref.func)) +(assert_return (invoke "meet-funcref-1" (i32.const 1)) (ref.func)) +(assert_return (invoke "meet-funcref-1" (i32.const 2)) (ref.func)) +(assert_return (invoke "meet-funcref-2" (i32.const 0)) (ref.func)) +(assert_return (invoke "meet-funcref-2" (i32.const 1)) (ref.func)) +(assert_return (invoke "meet-funcref-2" (i32.const 2)) (ref.func)) +(assert_return (invoke "meet-funcref-3" (i32.const 0)) (ref.func)) +(assert_return (invoke "meet-funcref-3" (i32.const 1)) (ref.func)) +(assert_return (invoke "meet-funcref-3" (i32.const 2)) (ref.func)) +(assert_return (invoke "meet-funcref-4" (i32.const 0)) (ref.func)) +(assert_return (invoke "meet-funcref-4" (i32.const 1)) (ref.func)) +(assert_return (invoke "meet-funcref-4" (i32.const 2)) (ref.func)) + +(assert_invalid + (module (func $type-arg-void-vs-num (result i32) + (block (br_table 0 (i32.const 1)) (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-arg-empty-vs-num (result i32) + (block (br_table 0) (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-arg-void-vs-num (result i32) + (block (result i32) (br_table 0 (nop) (i32.const 1)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-num-vs-num (result i32) + (block (result i32) + (br_table 0 0 0 (i64.const 1) (i32.const 1)) (i32.const 1) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-num-vs-arg-num + (block + (block (result f32) + (br_table 0 1 (f32.const 0) (i32.const 0)) + ) + (drop) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func + (block (result i32) + (block (result i64) + (br_table 0 1 (i32.const 0) (i32.const 0)) + ) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-index-void-vs-i32 + (block (br_table 0 0 0 (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-index-num-vs-i32 + (block (br_table 0 (i64.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-index-void-vs-i32 (result i32) + (block (result i32) (br_table 0 0 (i32.const 0) (nop)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-void-vs-num-nested (result i32) + (block (result i32) (i32.const 0) (block (br_table 1 (i32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-index-num-vs-i32 (result i32) + (block (result i32) + (br_table 0 0 (i32.const 0) (i64.const 0)) (i32.const 1) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-arg-void-vs-num (result i32) + (block (br_table 0 (i32.const 1)) (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module + (func $type-arg-index-empty-in-then + (block + (i32.const 0) (i32.const 0) + (if (result i32) (then (br_table 0))) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-value-empty-in-then + (block + (i32.const 0) (i32.const 0) + (if (result i32) (then (br_table 0 (i32.const 1)))) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-index-empty-in-return + (block (result i32) + (return (br_table 0)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-value-empty-in-return + (block (result i32) + (return (br_table 0 (i32.const 1))) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (func (param i32) (result i32) + (loop (result i32) + (block (result i32) + (br_table 0 1 (i32.const 1) (local.get 0)) + ) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (param i32) (result i32) + (block (result i32) + (loop (result i32) + (br_table 0 1 (i32.const 1) (local.get 0)) + ) + ) + ) + ) + "type mismatch" +) + + +(assert_invalid + (module (func $meet-bottom (param i32) (result externref) + (block $l1 (result externref) + (drop + (block $l2 (result i32) + (br_table $l2 $l1 $l2 (ref.null extern) (local.get 0)) + ) + ) + (ref.null extern) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $unbound-label + (block (br_table 2 1 (i32.const 1))) + )) + "unknown label" +) +(assert_invalid + (module (func $unbound-nested-label + (block (block (br_table 0 5 (i32.const 1)))) + )) + "unknown label" +) +(assert_invalid + (module (func $large-label + (block (br_table 0 0x10000001 0 (i32.const 1))) + )) + "unknown label" +) + +(assert_invalid + (module (func $unbound-label-default + (block (br_table 1 2 (i32.const 1))) + )) + "unknown label" +) +(assert_invalid + (module (func $unbound-nested-label-default + (block (block (br_table 0 5 (i32.const 1)))) + )) + "unknown label" +) +(assert_invalid + (module (func $large-label-default + (block (br_table 0 0 0x10000001 (i32.const 1))) + )) + "unknown label" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/call_ref.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/call_ref.wast new file mode 100644 index 000000000..503c4e861 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/call_ref.wast @@ -0,0 +1,202 @@ +(module + (type $ii (func (param i32) (result i32))) + + (func $apply (param $f (ref $ii)) (param $x i32) (result i32) + (call_ref (local.get $x) (local.get $f)) + ) + + (func $f (type $ii) (i32.mul (local.get 0) (local.get 0))) + (func $g (type $ii) (i32.sub (i32.const 0) (local.get 0))) + + (elem declare func $f $g) + + (func (export "run") (param $x i32) (result i32) + (local $rf (ref null $ii)) + (local $rg (ref null $ii)) + (local.set $rf (ref.func $f)) + (local.set $rg (ref.func $g)) + (call_ref (call_ref (local.get $x) (local.get $rf)) (local.get $rg)) + ) + + (func (export "null") (result i32) + (call_ref (i32.const 1) (ref.null $ii)) + ) + + ;; Recursion + + (type $ll (func (param i64) (result i64))) + (type $lll (func (param i64 i64) (result i64))) + + (elem declare func $fac) + (global $fac (ref $ll) (ref.func $fac)) + + (func $fac (export "fac") (type $ll) + (if (result i64) (i64.eqz (local.get 0)) + (then (i64.const 1)) + (else + (i64.mul + (local.get 0) + (call_ref (i64.sub (local.get 0) (i64.const 1)) (global.get $fac)) + ) + ) + ) + ) + + (elem declare func $fac-acc) + (global $fac-acc (ref $lll) (ref.func $fac-acc)) + + (func $fac-acc (export "fac-acc") (type $lll) + (if (result i64) (i64.eqz (local.get 0)) + (then (local.get 1)) + (else + (call_ref + (i64.sub (local.get 0) (i64.const 1)) + (i64.mul (local.get 0) (local.get 1)) + (global.get $fac-acc) + ) + ) + ) + ) + + (elem declare func $fib) + (global $fib (ref $ll) (ref.func $fib)) + + (func $fib (export "fib") (type $ll) + (if (result i64) (i64.le_u (local.get 0) (i64.const 1)) + (then (i64.const 1)) + (else + (i64.add + (call_ref (i64.sub (local.get 0) (i64.const 2)) (global.get $fib)) + (call_ref (i64.sub (local.get 0) (i64.const 1)) (global.get $fib)) + ) + ) + ) + ) + + (elem declare func $even $odd) + (global $even (ref $ll) (ref.func $even)) + (global $odd (ref $ll) (ref.func $odd)) + + (func $even (export "even") (type $ll) + (if (result i64) (i64.eqz (local.get 0)) + (then (i64.const 44)) + (else (call_ref (i64.sub (local.get 0) (i64.const 1)) (global.get $odd))) + ) + ) + (func $odd (export "odd") (type $ll) + (if (result i64) (i64.eqz (local.get 0)) + (then (i64.const 99)) + (else (call_ref (i64.sub (local.get 0) (i64.const 1)) (global.get $even))) + ) + ) +) + +(assert_return (invoke "run" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "run" (i32.const 3)) (i32.const -9)) + +(assert_trap (invoke "null") "null function") + +(assert_return (invoke "fac" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "fac" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac" (i64.const 5)) (i64.const 120)) +(assert_return (invoke "fac" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-acc" (i64.const 0) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 5) (i64.const 1)) (i64.const 120)) +(assert_return + (invoke "fac-acc" (i64.const 25) (i64.const 1)) + (i64.const 7034535277573963776) +) + +(assert_return (invoke "fib" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "fib" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fib" (i64.const 2)) (i64.const 2)) +(assert_return (invoke "fib" (i64.const 5)) (i64.const 8)) +(assert_return (invoke "fib" (i64.const 20)) (i64.const 10946)) + +(assert_return (invoke "even" (i64.const 0)) (i64.const 44)) +(assert_return (invoke "even" (i64.const 1)) (i64.const 99)) +(assert_return (invoke "even" (i64.const 100)) (i64.const 44)) +(assert_return (invoke "even" (i64.const 77)) (i64.const 99)) +(assert_return (invoke "odd" (i64.const 0)) (i64.const 99)) +(assert_return (invoke "odd" (i64.const 1)) (i64.const 44)) +(assert_return (invoke "odd" (i64.const 200)) (i64.const 99)) +(assert_return (invoke "odd" (i64.const 77)) (i64.const 44)) + + +;; Unreachable typing. + +(module + (func (export "unreachable") (result i32) + (unreachable) + (call_ref) + ) +) +(assert_trap (invoke "unreachable") "unreachable") + +(module + (elem declare func $f) + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "unreachable") (result i32) + (unreachable) + (ref.func $f) + (call_ref) + ) +) +(assert_trap (invoke "unreachable") "unreachable") + +(module + (elem declare func $f) + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "unreachable") (result i32) + (unreachable) + (i32.const 0) + (ref.func $f) + (call_ref) + (drop) + (i32.const 0) + ) +) +(assert_trap (invoke "unreachable") "unreachable") + +(assert_invalid + (module + (elem declare func $f) + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "unreachable") (result i32) + (unreachable) + (i64.const 0) + (ref.func $f) + (call_ref) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (elem declare func $f) + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "unreachable") (result i32) + (unreachable) + (ref.func $f) + (call_ref) + (drop) + (i64.const 0) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $f (param $r externref) + (call_ref (local.get $r)) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/func.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/func.wast new file mode 100644 index 000000000..a5e0ec757 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/func.wast @@ -0,0 +1,988 @@ +;; Test `func` declarations, i.e. functions + +(module + ;; Auxiliary definition + (type $sig (func)) + (func $dummy) + + ;; Syntax + + (func) + (func (export "f")) + (func $f) + (func $h (export "g")) + + (func (local)) + (func (local) (local)) + (func (local i32)) + (func (local $x i32)) + (func (local i32 f64 i64)) + (func (local i32) (local f64)) + (func (local i32 f32) (local $x i64) (local) (local i32 f64)) + + (func (param)) + (func (param) (param)) + (func (param i32)) + (func (param $x i32)) + (func (param i32 f64 i64)) + (func (param i32) (param f64)) + (func (param i32 f32) (param $x i64) (param) (param i32 f64)) + + (func (result)) + (func (result) (result)) + (func (result i32) (unreachable)) + (func (result i32 f64 f32) (unreachable)) + (func (result i32) (result f64) (unreachable)) + (func (result i32 f32) (result i64) (result) (result i32 f64) (unreachable)) + + (type $sig-1 (func)) + (type $sig-2 (func (result i32))) + (type $sig-3 (func (param $x i32))) + (type $sig-4 (func (param i32 f64 i32) (result i32))) + + (func (export "type-use-1") (type $sig-1)) + (func (export "type-use-2") (type $sig-2) (i32.const 0)) + (func (export "type-use-3") (type $sig-3)) + (func (export "type-use-4") (type $sig-4) (i32.const 0)) + (func (export "type-use-5") (type $sig-2) (result i32) (i32.const 0)) + (func (export "type-use-6") (type $sig-3) (param i32)) + (func (export "type-use-7") + (type $sig-4) (param i32) (param f64 i32) (result i32) (i32.const 0) + ) + + (func (type $sig)) + (func (type $forward)) ;; forward reference + + (func $complex + (param i32 f32) (param $x i64) (param) (param i32) + (result) (result i32) (result) (result i64 i32) + (local f32) (local $y i32) (local i64 i32) (local) (local f64 i32) + (unreachable) (unreachable) + ) + (func $complex-sig + (type $sig) + (local f32) (local $y i32) (local i64 i32) (local) (local f64 i32) + (unreachable) (unreachable) + ) + + (type $forward (func)) + + ;; Typing of locals + + (func (export "local-first-i32") (result i32) (local i32 i32) (local.get 0)) + (func (export "local-first-i64") (result i64) (local i64 i64) (local.get 0)) + (func (export "local-first-f32") (result f32) (local f32 f32) (local.get 0)) + (func (export "local-first-f64") (result f64) (local f64 f64) (local.get 0)) + (func (export "local-second-i32") (result i32) (local i32 i32) (local.get 1)) + (func (export "local-second-i64") (result i64) (local i64 i64) (local.get 1)) + (func (export "local-second-f32") (result f32) (local f32 f32) (local.get 1)) + (func (export "local-second-f64") (result f64) (local f64 f64) (local.get 1)) + (func (export "local-mixed") (result f64) + (local f32) (local $x i32) (local i64 i32) (local) (local f64 i32) + (drop (f32.neg (local.get 0))) + (drop (i32.eqz (local.get 1))) + (drop (i64.eqz (local.get 2))) + (drop (i32.eqz (local.get 3))) + (drop (f64.neg (local.get 4))) + (drop (i32.eqz (local.get 5))) + (local.get 4) + ) + + ;; Typing of parameters + + (func (export "param-first-i32") (param i32 i32) (result i32) (local.get 0)) + (func (export "param-first-i64") (param i64 i64) (result i64) (local.get 0)) + (func (export "param-first-f32") (param f32 f32) (result f32) (local.get 0)) + (func (export "param-first-f64") (param f64 f64) (result f64) (local.get 0)) + (func (export "param-second-i32") (param i32 i32) (result i32) (local.get 1)) + (func (export "param-second-i64") (param i64 i64) (result i64) (local.get 1)) + (func (export "param-second-f32") (param f32 f32) (result f32) (local.get 1)) + (func (export "param-second-f64") (param f64 f64) (result f64) (local.get 1)) + (func (export "param-mixed") (param f32 i32) (param) (param $x i64) (param i32 f64 i32) + (result f64) + (drop (f32.neg (local.get 0))) + (drop (i32.eqz (local.get 1))) + (drop (i64.eqz (local.get 2))) + (drop (i32.eqz (local.get 3))) + (drop (f64.neg (local.get 4))) + (drop (i32.eqz (local.get 5))) + (local.get 4) + ) + + ;; Typing of results + + (func (export "empty")) + (func (export "value-void") (call $dummy)) + (func (export "value-i32") (result i32) (i32.const 77)) + (func (export "value-i64") (result i64) (i64.const 7777)) + (func (export "value-f32") (result f32) (f32.const 77.7)) + (func (export "value-f64") (result f64) (f64.const 77.77)) + (func (export "value-i32-f64") (result i32 f64) (i32.const 77) (f64.const 7)) + (func (export "value-i32-i32-i32") (result i32 i32 i32) + (i32.const 1) (i32.const 2) (i32.const 3) + ) + (func (export "value-block-void") (block (call $dummy) (call $dummy))) + (func (export "value-block-i32") (result i32) + (block (result i32) (call $dummy) (i32.const 77)) + ) + (func (export "value-block-i32-i64") (result i32 i64) + (block (result i32 i64) (call $dummy) (i32.const 1) (i64.const 2)) + ) + + (func (export "return-empty") (return)) + (func (export "return-i32") (result i32) (return (i32.const 78))) + (func (export "return-i64") (result i64) (return (i64.const 7878))) + (func (export "return-f32") (result f32) (return (f32.const 78.7))) + (func (export "return-f64") (result f64) (return (f64.const 78.78))) + (func (export "return-i32-f64") (result i32 f64) + (return (i32.const 78) (f64.const 78.78)) + ) + (func (export "return-i32-i32-i32") (result i32 i32 i32) + (return (i32.const 1) (i32.const 2) (i32.const 3)) + ) + (func (export "return-block-i32") (result i32) + (return (block (result i32) (call $dummy) (i32.const 77))) + ) + (func (export "return-block-i32-i64") (result i32 i64) + (return (block (result i32 i64) (call $dummy) (i32.const 1) (i64.const 2))) + ) + + (func (export "break-empty") (br 0)) + (func (export "break-i32") (result i32) (br 0 (i32.const 79))) + (func (export "break-i64") (result i64) (br 0 (i64.const 7979))) + (func (export "break-f32") (result f32) (br 0 (f32.const 79.9))) + (func (export "break-f64") (result f64) (br 0 (f64.const 79.79))) + (func (export "break-i32-f64") (result i32 f64) + (br 0 (i32.const 79) (f64.const 79.79)) + ) + (func (export "break-i32-i32-i32") (result i32 i32 i32) + (br 0 (i32.const 1) (i32.const 2) (i32.const 3)) + ) + (func (export "break-block-i32") (result i32) + (br 0 (block (result i32) (call $dummy) (i32.const 77))) + ) + (func (export "break-block-i32-i64") (result i32 i64) + (br 0 (block (result i32 i64) (call $dummy) (i32.const 1) (i64.const 2))) + ) + + (func (export "break-br_if-empty") (param i32) + (br_if 0 (local.get 0)) + ) + (func (export "break-br_if-num") (param i32) (result i32) + (drop (br_if 0 (i32.const 50) (local.get 0))) (i32.const 51) + ) + (func (export "break-br_if-num-num") (param i32) (result i32 i64) + (drop (drop (br_if 0 (i32.const 50) (i64.const 51) (local.get 0)))) + (i32.const 51) (i64.const 52) + ) + + (func (export "break-br_table-empty") (param i32) + (br_table 0 0 0 (local.get 0)) + ) + (func (export "break-br_table-num") (param i32) (result i32) + (br_table 0 0 (i32.const 50) (local.get 0)) (i32.const 51) + ) + (func (export "break-br_table-num-num") (param i32) (result f32 i64) + (br_table 0 0 (f32.const 50) (i64.const 51) (local.get 0)) + (f32.const 51) (i64.const 52) + ) + (func (export "break-br_table-nested-empty") (param i32) + (block (br_table 0 1 0 (local.get 0))) + ) + (func (export "break-br_table-nested-num") (param i32) (result i32) + (i32.add + (block (result i32) + (br_table 0 1 0 (i32.const 50) (local.get 0)) (i32.const 51) + ) + (i32.const 2) + ) + ) + (func (export "break-br_table-nested-num-num") (param i32) (result i32 i32) + (i32.add + (block (result i32 i32) + (br_table 0 1 0 (i32.const 50) (i32.const 51) (local.get 0)) + (i32.const 51) (i32.const -3) + ) + ) + (i32.const 52) + ) + + ;; Large signatures + + (func (export "large-sig") + (param i32 i64 f32 f32 i32 f64 f32 i32 i32 i32 f32 f64 f64 f64 i32 i32 f32) + (result f64 f32 i32 i32 i32 i64 f32 i32 i32 f32 f64 f64 i32 f32 i32 f64) + (local.get 5) + (local.get 2) + (local.get 0) + (local.get 8) + (local.get 7) + (local.get 1) + (local.get 3) + (local.get 9) + (local.get 4) + (local.get 6) + (local.get 13) + (local.get 11) + (local.get 15) + (local.get 16) + (local.get 14) + (local.get 12) + ) + + ;; Default initialization of locals + + (func (export "init-local-i32") (result i32) (local i32) (local.get 0)) + (func (export "init-local-i64") (result i64) (local i64) (local.get 0)) + (func (export "init-local-f32") (result f32) (local f32) (local.get 0)) + (func (export "init-local-f64") (result f64) (local f64) (local.get 0)) +) + +(assert_return (invoke "type-use-1")) +(assert_return (invoke "type-use-2") (i32.const 0)) +(assert_return (invoke "type-use-3" (i32.const 1))) +(assert_return + (invoke "type-use-4" (i32.const 1) (f64.const 1) (i32.const 1)) + (i32.const 0) +) +(assert_return (invoke "type-use-5") (i32.const 0)) +(assert_return (invoke "type-use-6" (i32.const 1))) +(assert_return + (invoke "type-use-7" (i32.const 1) (f64.const 1) (i32.const 1)) + (i32.const 0) +) + +(assert_return (invoke "local-first-i32") (i32.const 0)) +(assert_return (invoke "local-first-i64") (i64.const 0)) +(assert_return (invoke "local-first-f32") (f32.const 0)) +(assert_return (invoke "local-first-f64") (f64.const 0)) +(assert_return (invoke "local-second-i32") (i32.const 0)) +(assert_return (invoke "local-second-i64") (i64.const 0)) +(assert_return (invoke "local-second-f32") (f32.const 0)) +(assert_return (invoke "local-second-f64") (f64.const 0)) +(assert_return (invoke "local-mixed") (f64.const 0)) + +(assert_return + (invoke "param-first-i32" (i32.const 2) (i32.const 3)) (i32.const 2) +) +(assert_return + (invoke "param-first-i64" (i64.const 2) (i64.const 3)) (i64.const 2) +) +(assert_return + (invoke "param-first-f32" (f32.const 2) (f32.const 3)) (f32.const 2) +) +(assert_return + (invoke "param-first-f64" (f64.const 2) (f64.const 3)) (f64.const 2) +) +(assert_return + (invoke "param-second-i32" (i32.const 2) (i32.const 3)) (i32.const 3) +) +(assert_return + (invoke "param-second-i64" (i64.const 2) (i64.const 3)) (i64.const 3) +) +(assert_return + (invoke "param-second-f32" (f32.const 2) (f32.const 3)) (f32.const 3) +) +(assert_return + (invoke "param-second-f64" (f64.const 2) (f64.const 3)) (f64.const 3) +) + +(assert_return + (invoke "param-mixed" + (f32.const 1) (i32.const 2) (i64.const 3) + (i32.const 4) (f64.const 5.5) (i32.const 6) + ) + (f64.const 5.5) +) + +(assert_return (invoke "empty")) +(assert_return (invoke "value-void")) +(assert_return (invoke "value-i32") (i32.const 77)) +(assert_return (invoke "value-i64") (i64.const 7777)) +(assert_return (invoke "value-f32") (f32.const 77.7)) +(assert_return (invoke "value-f64") (f64.const 77.77)) +(assert_return (invoke "value-i32-f64") (i32.const 77) (f64.const 7)) +(assert_return (invoke "value-i32-i32-i32") + (i32.const 1) (i32.const 2) (i32.const 3) +) +(assert_return (invoke "value-block-void")) +(assert_return (invoke "value-block-i32") (i32.const 77)) +(assert_return (invoke "value-block-i32-i64") (i32.const 1) (i64.const 2)) + +(assert_return (invoke "return-empty")) +(assert_return (invoke "return-i32") (i32.const 78)) +(assert_return (invoke "return-i64") (i64.const 7878)) +(assert_return (invoke "return-f32") (f32.const 78.7)) +(assert_return (invoke "return-f64") (f64.const 78.78)) +(assert_return (invoke "return-i32-f64") (i32.const 78) (f64.const 78.78)) +(assert_return (invoke "return-i32-i32-i32") + (i32.const 1) (i32.const 2) (i32.const 3) +) +(assert_return (invoke "return-block-i32") (i32.const 77)) +(assert_return (invoke "return-block-i32-i64") (i32.const 1) (i64.const 2)) + +(assert_return (invoke "break-empty")) +(assert_return (invoke "break-i32") (i32.const 79)) +(assert_return (invoke "break-i64") (i64.const 7979)) +(assert_return (invoke "break-f32") (f32.const 79.9)) +(assert_return (invoke "break-f64") (f64.const 79.79)) +(assert_return (invoke "break-i32-f64") (i32.const 79) (f64.const 79.79)) +(assert_return (invoke "break-i32-i32-i32") + (i32.const 1) (i32.const 2) (i32.const 3) +) +(assert_return (invoke "break-block-i32") (i32.const 77)) +(assert_return (invoke "break-block-i32-i64") (i32.const 1) (i64.const 2)) + +(assert_return (invoke "break-br_if-empty" (i32.const 0))) +(assert_return (invoke "break-br_if-empty" (i32.const 2))) +(assert_return (invoke "break-br_if-num" (i32.const 0)) (i32.const 51)) +(assert_return (invoke "break-br_if-num" (i32.const 1)) (i32.const 50)) +(assert_return (invoke "break-br_if-num-num" (i32.const 0)) + (i32.const 51) (i64.const 52) +) +(assert_return (invoke "break-br_if-num-num" (i32.const 1)) + (i32.const 50) (i64.const 51) +) + +(assert_return (invoke "break-br_table-empty" (i32.const 0))) +(assert_return (invoke "break-br_table-empty" (i32.const 1))) +(assert_return (invoke "break-br_table-empty" (i32.const 5))) +(assert_return (invoke "break-br_table-empty" (i32.const -1))) +(assert_return (invoke "break-br_table-num" (i32.const 0)) (i32.const 50)) +(assert_return (invoke "break-br_table-num" (i32.const 1)) (i32.const 50)) +(assert_return (invoke "break-br_table-num" (i32.const 10)) (i32.const 50)) +(assert_return (invoke "break-br_table-num" (i32.const -100)) (i32.const 50)) +(assert_return (invoke "break-br_table-num-num" (i32.const 0)) + (f32.const 50) (i64.const 51) +) +(assert_return (invoke "break-br_table-num-num" (i32.const 1)) + (f32.const 50) (i64.const 51) +) +(assert_return (invoke "break-br_table-num-num" (i32.const 10)) + (f32.const 50) (i64.const 51) +) +(assert_return (invoke "break-br_table-num-num" (i32.const -100)) + (f32.const 50) (i64.const 51) +) +(assert_return (invoke "break-br_table-nested-empty" (i32.const 0))) +(assert_return (invoke "break-br_table-nested-empty" (i32.const 1))) +(assert_return (invoke "break-br_table-nested-empty" (i32.const 3))) +(assert_return (invoke "break-br_table-nested-empty" (i32.const -2))) +(assert_return + (invoke "break-br_table-nested-num" (i32.const 0)) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num" (i32.const 1)) (i32.const 50) +) +(assert_return + (invoke "break-br_table-nested-num" (i32.const 2)) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num" (i32.const -3)) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const 0)) + (i32.const 101) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const 1)) + (i32.const 50) (i32.const 51) +) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const 2)) + (i32.const 101) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const -3)) + (i32.const 101) (i32.const 52) +) + +(assert_return + (invoke "large-sig" + (i32.const 0) (i64.const 1) (f32.const 2) (f32.const 3) + (i32.const 4) (f64.const 5) (f32.const 6) (i32.const 7) + (i32.const 8) (i32.const 9) (f32.const 10) (f64.const 11) + (f64.const 12) (f64.const 13) (i32.const 14) (i32.const 15) + (f32.const 16) + ) + (f64.const 5) (f32.const 2) (i32.const 0) (i32.const 8) + (i32.const 7) (i64.const 1) (f32.const 3) (i32.const 9) + (i32.const 4) (f32.const 6) (f64.const 13) (f64.const 11) + (i32.const 15) (f32.const 16) (i32.const 14) (f64.const 12) +) + +(assert_return (invoke "init-local-i32") (i32.const 0)) +(assert_return (invoke "init-local-i64") (i64.const 0)) +(assert_return (invoke "init-local-f32") (f32.const 0)) +(assert_return (invoke "init-local-f64") (f64.const 0)) + + +;; Expansion of inline function types + +(module + (func $f (result f64) (f64.const 0)) ;; adds implicit type definition + (func $g (param i32)) ;; reuses explicit type definition + (type $t (func (param i32))) + + (func $i32->void (type 0)) ;; (param i32) + (func $void->f64 (type 1) (f64.const 0)) ;; (result f64) + (func $check + (call $i32->void (i32.const 0)) + (drop (call $void->f64)) + ) +) + +(assert_invalid + (module + (func $f (result f64) (f64.const 0)) ;; adds implicit type definition + (func $g (param i32)) ;; reuses explicit type definition + (func $h (result f64) (f64.const 1)) ;; reuses implicit type definition + (type $t (func (param i32))) + + (func (type 2)) ;; does not exist + ) + "unknown type" +) + +(assert_malformed + (module quote + "(func $f (result f64) (f64.const 0))" ;; adds implicit type definition + "(func $g (param i32))" ;; reuses explicit type definition + "(func $h (result f64) (f64.const 1))" ;; reuses implicit type definition + "(type $t (func (param i32)))" + + "(func (type 2) (param i32))" ;; does not exist + ) + "unknown type" +) + +(module + (type $proc (func (result i32))) + (type $sig (func (param i32) (result i32))) + + (func (export "f") (type $sig) + (local $var i32) + (local.get $var) + ) + + (func $g (type $sig) + (local $var i32) + (local.get $var) + ) + (func (export "g") (type $sig) + (call $g (local.get 0)) + ) + + (func (export "p") (type $proc) + (local $var i32) + (local.set 0 (i32.const 42)) + (local.get $var) + ) +) + +(assert_return (invoke "f" (i32.const 42)) (i32.const 0)) +(assert_return (invoke "g" (i32.const 42)) (i32.const 0)) +(assert_return (invoke "p") (i32.const 42)) + + +(module + (type $sig (func)) + + (func $empty-sig-1) ;; should be assigned type $sig + (func $complex-sig-1 (param f64 i64 f64 i64 f64 i64 f32 i32)) + (func $empty-sig-2) ;; should be assigned type $sig + (func $complex-sig-2 (param f64 i64 f64 i64 f64 i64 f32 i32)) + (func $complex-sig-3 (param f64 i64 f64 i64 f64 i64 f32 i32)) + (func $complex-sig-4 (param i64 i64 f64 i64 f64 i64 f32 i32)) + (func $complex-sig-5 (param i64 i64 f64 i64 f64 i64 f32 i32)) + + (type $empty-sig-duplicate (func)) + (type $complex-sig-duplicate (func (param i64 i64 f64 i64 f64 i64 f32 i32))) + (table funcref + (elem + $complex-sig-3 $empty-sig-2 $complex-sig-1 $complex-sig-3 $empty-sig-1 + $complex-sig-4 $complex-sig-5 + ) + ) + + (func (export "signature-explicit-reused") + (call_indirect (type $sig) (i32.const 1)) + (call_indirect (type $sig) (i32.const 4)) + ) + + (func (export "signature-implicit-reused") + ;; The implicit index 3 in this test depends on the function and + ;; type definitions, and may need adapting if they change. + (call_indirect (type 3) + (f64.const 0) (i64.const 0) (f64.const 0) (i64.const 0) + (f64.const 0) (i64.const 0) (f32.const 0) (i32.const 0) + (i32.const 0) + ) + (call_indirect (type 3) + (f64.const 0) (i64.const 0) (f64.const 0) (i64.const 0) + (f64.const 0) (i64.const 0) (f32.const 0) (i32.const 0) + (i32.const 2) + ) + (call_indirect (type 3) + (f64.const 0) (i64.const 0) (f64.const 0) (i64.const 0) + (f64.const 0) (i64.const 0) (f32.const 0) (i32.const 0) + (i32.const 3) + ) + ) + + (func (export "signature-explicit-duplicate") + (call_indirect (type $empty-sig-duplicate) (i32.const 1)) + ) + + (func (export "signature-implicit-duplicate") + (call_indirect (type $complex-sig-duplicate) + (i64.const 0) (i64.const 0) (f64.const 0) (i64.const 0) + (f64.const 0) (i64.const 0) (f32.const 0) (i32.const 0) + (i32.const 5) + ) + (call_indirect (type $complex-sig-duplicate) + (i64.const 0) (i64.const 0) (f64.const 0) (i64.const 0) + (f64.const 0) (i64.const 0) (f32.const 0) (i32.const 0) + (i32.const 6) + ) + ) +) + +(assert_return (invoke "signature-explicit-reused")) +(assert_return (invoke "signature-implicit-reused")) +(assert_return (invoke "signature-explicit-duplicate")) +(assert_return (invoke "signature-implicit-duplicate")) + + +;; Malformed type use + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (type $sig) (result i32) (param i32) (i32.const 0))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (param i32) (type $sig) (result i32) (i32.const 0))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (param i32) (result i32) (type $sig) (i32.const 0))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (result i32) (type $sig) (param i32) (i32.const 0))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (result i32) (param i32) (type $sig) (i32.const 0))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func (result i32) (param i32) (i32.const 0))" + ) + "unexpected token" +) + +(assert_malformed + (module quote + "(type $sig (func))" + "(func (type $sig) (result i32) (i32.const 0))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (type $sig) (result i32) (i32.const 0))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (type $sig) (param i32) (i32.const 0))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(func (type $sig) (param i32) (result i32) (unreachable))" + ) + "inline function type" +) + +(assert_invalid + (module (func $g (type 4))) + "unknown type" +) +(assert_invalid + (module + (func $f (drop (ref.func $g))) + (func $g (type 4)) + (elem declare func $g) + ) + "unknown type" +) +(assert_invalid + (module + (type $t (func)) + (func $f (drop (let (result (ref $t)) (ref.func $g)))) + (func $g (type 4)) + (elem declare func $g) + ) + "unknown type" +) + + +;; Invalid typing of locals + +(assert_invalid + (module (func $type-local-num-vs-num (result i64) (local i32) (local.get 0))) + "type mismatch" +) +(assert_invalid + (module (func $type-local-num-vs-num (local f32) (i32.eqz (local.get 0)))) + "type mismatch" +) +(assert_invalid + (module (func $type-local-num-vs-num (local f64 i64) (f64.neg (local.get 1)))) + "type mismatch" +) + +(assert_invalid + (module (type $t (func)) (func $type-local-no-default (local (ref $t)))) + "non-defaultable local type" +) + + +;; Invalid typing of parameters + +(assert_invalid + (module (func $type-param-num-vs-num (param i32) (result i64) (local.get 0))) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num (param f32) (i32.eqz (local.get 0)))) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num (param f64 i64) (f64.neg (local.get 1)))) + "type mismatch" +) + + +;; Invalid typing of result + +(assert_invalid + (module (func $type-empty-i32 (result i32))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-i64 (result i64))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f32 (result f32))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f64 (result f64))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f64-i32 (result f64 i32))) + "type mismatch" +) + +(assert_invalid + (module (func $type-value-void-vs-num (result i32) + (nop) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-void-vs-nums (result i32 i32) + (nop) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-num-vs-void + (i32.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-nums-vs-void + (i32.const 0) (i64.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-num-vs-num (result i32) + (f32.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-num-vs-nums (result f32 f32) + (f32.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-nums-vs-num (result f32) + (f32.const 0) (f32.const 0) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-return-last-empty-vs-num (result i32) + (return) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-last-empty-vs-nums (result i32 i32) + (return) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-last-void-vs-num (result i32) + (return (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-last-void-vs-nums (result i32 i64) + (return (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-last-num-vs-num (result i32) + (return (i64.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-last-num-vs-nums (result i64 i64) + (return (i64.const 0)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-return-empty-vs-num (result i32) + (return) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-empty-vs-nums (result i32 i32) + (return) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-partial-vs-nums (result i32 i32) + (i32.const 1) (return) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-void-vs-num (result i32) + (return (nop)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-void-vs-nums (result i32 i32) + (return (nop)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-num-vs-num (result i32) + (return (i64.const 1)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-num-vs-nums (result i32 i32) + (return (i64.const 1)) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-first-num-vs-num (result i32) + (return (i64.const 1)) (return (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-first-num-vs-nums (result i32 i32) + (return (i32.const 1)) (return (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-last-void-vs-num (result i32) + (br 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-void-vs-nums (result i32 i32) + (br 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-num-vs-num (result i32) + (br 0 (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-num-vs-nums (result i32 i32) + (br 0 (i32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-void-vs-num (result i32) + (br 0) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-void-vs-nums (result i32 i32) + (br 0) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-num-vs-num (result i32) + (br 0 (i64.const 1)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-num-vs-nums (result i32 i32) + (br 0 (i32.const 1)) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-num-vs-num (result i32) + (br 0 (i64.const 1)) (br 0 (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-nested-empty-vs-num (result i32) + (block (br 1)) (br 0 (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-empty-vs-nums (result i32 i32) + (block (br 1)) (br 0 (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-void-vs-num (result i32) + (block (br 1 (nop))) (br 0 (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-void-vs-nums (result i32 i32) + (block (br 1 (nop))) (br 0 (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-num-vs-num (result i32) + (block (br 1 (i64.const 1))) (br 0 (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-num-vs-nums (result i32 i32) + (block (result i32) (br 1 (i32.const 1))) (br 0 (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) + + +;; Syntax errors + +(assert_malformed + (module quote "(func (nop) (local i32))") + "unexpected token" +) +(assert_malformed + (module quote "(func (nop) (param i32))") + "unexpected token" +) +(assert_malformed + (module quote "(func (nop) (result i32))") + "unexpected token" +) +(assert_malformed + (module quote "(func (local i32) (param i32))") + "unexpected token" +) +(assert_malformed + (module quote "(func (local i32) (result i32) (local.get 0))") + "unexpected token" +) +(assert_malformed + (module quote "(func (result i32) (param i32) (local.get 0))") + "unexpected token" +) + +;; Duplicate name errors + +(assert_malformed (module quote + "(func $foo)" + "(func $foo)") + "duplicate func") +(assert_malformed (module quote + "(import \"\" \"\" (func $foo))" + "(func $foo)") + "duplicate func") +(assert_malformed (module quote + "(import \"\" \"\" (func $foo))" + "(import \"\" \"\" (func $foo))") + "duplicate func") + +(assert_malformed (module quote "(func (param $foo i32) (param $foo i32))") + "duplicate local") +(assert_malformed (module quote "(func (param $foo i32) (local $foo i32))") + "duplicate local") +(assert_malformed (module quote "(func (local $foo i32) (local $foo i32))") + "duplicate local") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/func_bind.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/func_bind.wast new file mode 100644 index 000000000..18743219f --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/func_bind.wast @@ -0,0 +1,540 @@ +(module + (type $unop (func (param i32) (result i32))) + + (elem func $add) + (func $add (param i32 i32) (result i32) (i32.add (local.get 0) (local.get 1))) + + (func $mk-adder (param $i i32) (result (ref $unop)) + (func.bind (type $unop) (local.get $i) (ref.func $add)) + ) + + (global $f (mut (ref null $unop)) (ref.null $unop)) + + (func (export "make") (param $i i32) + (global.set $f (call $mk-adder (local.get $i))) + ) + + (func (export "call") (param $j i32) (result i32) + (call_ref (local.get $j) (global.get $f)) + ) + + (func (export "call2") (param $i i32) (param $j i32) (param $k i32) (result i32) + (call $mk-adder (local.get $k)) + (let (result i32) (local $f (ref $unop)) ;; binds $f to top of stack + (i32.mul + (call_ref (local.get $i) (local.get $f)) + (call_ref (local.get $j) (local.get $f)) + ) + ) + ) + + (func (export "null") (result i32) + (func.bind (type $unop) (i32.const 1) (ref.null $unop)) + (drop) + ) +) + +(assert_trap (invoke "call" (i32.const 0)) "null function") + +(assert_return (invoke "make" (i32.const 3))) +(assert_return (invoke "call" (i32.const 2)) (i32.const 5)) +(assert_return (invoke "call" (i32.const 10)) (i32.const 13)) + +(assert_return (invoke "make" (i32.const 0))) +(assert_return (invoke "call" (i32.const 10)) (i32.const 10)) + +(assert_return (invoke "make" (i32.const -3))) +(assert_return (invoke "call" (i32.const 10)) (i32.const 7)) + +(assert_return (invoke "call2" (i32.const 2) (i32.const 3) (i32.const 0)) (i32.const 6)) +(assert_return (invoke "call2" (i32.const 2) (i32.const 5) (i32.const 1)) (i32.const 18)) +(assert_return (invoke "call2" (i32.const 2) (i32.const 5) (i32.const 7)) (i32.const 108)) + +(assert_trap (invoke "null") "null function") + +(module + (elem declare func $p $f) + (func $p (import "spectest" "print_f64_f64") (param f64 f64)) + (func $f (param f64 f64 f64 f64) (result f64) + (f64.const 0) + (f64.add (f64.mul (local.get 0) (f64.const 1000))) + (f64.add (f64.mul (local.get 1) (f64.const 100))) + (f64.add (f64.mul (local.get 2) (f64.const 10))) + (f64.add (f64.mul (local.get 3) (f64.const 1))) + ) + + (type $p0 (func (param))) + (type $p1 (func (param f64))) + (type $p2 (func (param f64 f64))) + (type $f0 (func (param) (result f64))) + (type $f1 (func (param f64) (result f64))) + (type $f2 (func (param f64 f64) (result f64))) + (type $f3 (func (param f64 f64 f64) (result f64))) + (type $f4 (func (param f64 f64 f64 f64) (result f64))) + + (table $tp 30 funcref) + (table $tf 50 funcref) + + (func (export "call-p0") (param $i i32) + (call_indirect $tp (type $p0) (local.get $i)) + ) + (func (export "call-p1") (param $i i32) (param f64) + (call_indirect $tp (type $p1) (local.get 1) (local.get $i)) + ) + (func (export "call-p2") (param $i i32) (param f64 f64) + (call_indirect $tp (type $p2) (local.get 1) (local.get 2) (local.get $i)) + ) + + (func (export "call-f0") (param $i i32) (result f64) + (call_indirect $tf (type $f0) (local.get $i)) + ) + (func (export "call-f1") (param $i i32) (param f64) (result f64) + (call_indirect $tf (type $f1) (local.get 1) (local.get $i)) + ) + (func (export "call-f2") (param $i i32) (param f64 f64) (result f64) + (call_indirect $tf (type $f2) (local.get 1) (local.get 2) (local.get $i)) + ) + (func (export "call-f3") (param $i i32) (param f64 f64 f64) (result f64) + (call_indirect $tf (type $f3) (local.get 1) (local.get 2) (local.get 3) (local.get $i)) + ) + (func (export "call-f4") (param $i i32) (param f64 f64 f64 f64) (result f64) + (call_indirect $tf (type $f4) (local.get 1) (local.get 2) (local.get 3) (local.get 4) (local.get $i)) + ) + + (func (export "init") + ;; Host closures with arity 2 + (table.set $tp (i32.const 20) + (ref.func $p) + ) + (table.set $tp (i32.const 21) + (func.bind (type $p2) + (ref.func $p)) + ) + + ;; Host closures with arity 1 + (table.set $tp (i32.const 10) + (func.bind (type $p1) (f64.const 1) + (ref.func $p)) + ) + (table.set $tp (i32.const 11) + (func.bind (type $p1) (f64.const 1) + (func.bind (type $p2) + (ref.func $p))) + ) + (table.set $tp (i32.const 12) + (func.bind (type $p1) + (func.bind (type $p1) (f64.const 1) + (func.bind (type $p2) + (ref.func $p)))) + ) + + ;; Host closures with arity 0 + (table.set $tp (i32.const 00) + (func.bind (type $p0) (f64.const 1) (f64.const 2) + (ref.func $p)) + ) + (table.set $tp (i32.const 01) + (func.bind (type $p0) (f64.const 2) + (func.bind (type $p1) (f64.const 1) + (ref.func $p))) + ) + (table.set $tp (i32.const 02) + (func.bind (type $p0) + (func.bind (type $p0) (f64.const 2) + (func.bind (type $p1) + (func.bind (type $p1) (f64.const 1) + (func.bind (type $p2) + (func.bind (type $p2) + (ref.func $p))))))) + ) + + ;; Wasm closures with arity 4 + (table.set $tf (i32.const 40) + (ref.func $f) + ) + (table.set $tf (i32.const 41) + (func.bind (type $f4) + (ref.func $f)) + ) + + ;; Wasm closures with arity 3 + (table.set $tf (i32.const 30) + (func.bind (type $f3) (f64.const 1) + (ref.func $f)) + ) + (table.set $tf (i32.const 31) + (func.bind (type $f3) (f64.const 1) + (func.bind (type $f4) + (ref.func $f))) + ) + (table.set $tf (i32.const 32) + (func.bind (type $f3) + (func.bind (type $f3) (f64.const 1) + (func.bind (type $f4) + (ref.func $f)))) + ) + + ;; Wasm closures with arity 2 + (table.set $tf (i32.const 20) + (func.bind (type $f2) (f64.const 1) (f64.const 2) + (ref.func $f)) + ) + (table.set $tf (i32.const 21) + (func.bind (type $f2) (f64.const 2) + (func.bind (type $f3) (f64.const 1) + (ref.func $f))) + ) + (table.set $tf (i32.const 22) + (func.bind (type $f2) + (func.bind (type $f2) (f64.const 2) + (func.bind (type $f3) + (func.bind (type $f3) (f64.const 1) + (func.bind (type $f4) + (func.bind (type $f4) + (ref.func $f))))))) + ) + + ;; Wasm closures with arity 1 + (table.set $tf (i32.const 10) + (func.bind (type $f1) (f64.const 1) (f64.const 2) (f64.const 3) + (ref.func $f)) + ) + (table.set $tf (i32.const 11) + (func.bind (type $f1) (f64.const 2) (f64.const 3) + (func.bind (type $f3) (f64.const 1) + (ref.func $f))) + ) + (table.set $tf (i32.const 12) + (func.bind (type $f1) (f64.const 3) + (func.bind (type $f2) (f64.const 1) (f64.const 2) + (ref.func $f))) + ) + (table.set $tf (i32.const 13) + (func.bind (type $f1) (f64.const 3) + (func.bind (type $f2) (f64.const 2) + (func.bind (type $f3) (f64.const 1) + (ref.func $f)))) + ) + (table.set $tf (i32.const 14) + (func.bind (type $f1) + (func.bind (type $f1) + (func.bind (type $f1) (f64.const 3) + (func.bind (type $f2) + (func.bind (type $f2) + (func.bind (type $f2) (f64.const 2) + (func.bind (type $f3) + (func.bind (type $f3) + (func.bind (type $f3) (f64.const 1) + (func.bind (type $f4) + (func.bind (type $f4) + (ref.func $f)))))))))))) + ) + + ;; Wasm closures with arity 0 + (table.set $tf (i32.const 00) + (func.bind (type $f0) (f64.const 1) (f64.const 2) (f64.const 3) (f64.const 4) + (ref.func $f)) + ) + (table.set $tf (i32.const 01) + (func.bind (type $f0) (f64.const 2) (f64.const 3) (f64.const 4) + (func.bind (type $f3) (f64.const 1) + (ref.func $f))) + ) + (table.set $tf (i32.const 02) + (func.bind (type $f0) (f64.const 3) (f64.const 4) + (func.bind (type $f2) (f64.const 1) (f64.const 2) + (ref.func $f))) + ) + (table.set $tf (i32.const 03) + (func.bind (type $f0) (f64.const 4) + (func.bind (type $f1) (f64.const 1) (f64.const 2) (f64.const 3) + (ref.func $f))) + ) + (table.set $tf (i32.const 04) + (func.bind (type $f0) (f64.const 3) (f64.const 4) + (func.bind (type $f2) (f64.const 2) + (func.bind (type $f3) (f64.const 1) + (ref.func $f)))) + ) + (table.set $tf (i32.const 05) + (func.bind (type $f0) (f64.const 4) + (func.bind (type $f1) (f64.const 2) (f64.const 3) + (func.bind (type $f3) (f64.const 1) + (ref.func $f)))) + ) + (table.set $tf (i32.const 06) + (func.bind (type $f0) (f64.const 4) + (func.bind (type $f1) (f64.const 3) + (func.bind (type $f2) (f64.const 1) (f64.const 2) + (ref.func $f)))) + ) + (table.set $tf (i32.const 07) + (func.bind (type $f0) (f64.const 4) + (func.bind (type $f1) (f64.const 3) + (func.bind (type $f2) (f64.const 2) + (func.bind (type $f3) (f64.const 1) + (func.bind (type $f4) + (ref.func $f)))))) + ) + (table.set $tf (i32.const 08) + (func.bind (type $f0) + (func.bind (type $f0) + (func.bind (type $f0) (f64.const 4) + (func.bind (type $f1) + (func.bind (type $f1) + (func.bind (type $f1) (f64.const 2) (f64.const 3) + (func.bind (type $f3) + (func.bind (type $f3) + (func.bind (type $f3) (f64.const 1) + (func.bind (type $f4) + (func.bind (type $f4) + (ref.func $f)))))))))))) + ) + ) +) + +(invoke "init") + +(assert_return (invoke "call-p0" (i32.const 00))) +(assert_return (invoke "call-p0" (i32.const 01))) +(assert_return (invoke "call-p0" (i32.const 02))) +(assert_return (invoke "call-p1" (i32.const 10) (f64.const 2))) +(assert_return (invoke "call-p1" (i32.const 11) (f64.const 2))) +(assert_return (invoke "call-p1" (i32.const 12) (f64.const 2))) +(assert_return (invoke "call-p2" (i32.const 20) (f64.const 1) (f64.const 2))) +(assert_return (invoke "call-p2" (i32.const 21) (f64.const 1) (f64.const 2))) + +(assert_return (invoke "call-f0" (i32.const 00)) (f64.const 1234)) +(assert_return (invoke "call-f0" (i32.const 01)) (f64.const 1234)) +(assert_return (invoke "call-f0" (i32.const 02)) (f64.const 1234)) +(assert_return (invoke "call-f0" (i32.const 03)) (f64.const 1234)) +(assert_return (invoke "call-f0" (i32.const 04)) (f64.const 1234)) +(assert_return (invoke "call-f0" (i32.const 05)) (f64.const 1234)) +(assert_return (invoke "call-f0" (i32.const 06)) (f64.const 1234)) +(assert_return (invoke "call-f0" (i32.const 07)) (f64.const 1234)) +(assert_return (invoke "call-f0" (i32.const 08)) (f64.const 1234)) +(assert_return (invoke "call-f1" (i32.const 10) (f64.const 4)) (f64.const 1234)) +(assert_return (invoke "call-f1" (i32.const 11) (f64.const 4)) (f64.const 1234)) +(assert_return (invoke "call-f1" (i32.const 12) (f64.const 4)) (f64.const 1234)) +(assert_return (invoke "call-f1" (i32.const 13) (f64.const 4)) (f64.const 1234)) +(assert_return (invoke "call-f1" (i32.const 14) (f64.const 4)) (f64.const 1234)) +(assert_return (invoke "call-f2" (i32.const 20) (f64.const 3) (f64.const 4)) (f64.const 1234)) +(assert_return (invoke "call-f2" (i32.const 21) (f64.const 3) (f64.const 4)) (f64.const 1234)) +(assert_return (invoke "call-f2" (i32.const 22) (f64.const 3) (f64.const 4)) (f64.const 1234)) +(assert_return (invoke "call-f3" (i32.const 30) (f64.const 2) (f64.const 3) (f64.const 4)) (f64.const 1234)) +(assert_return (invoke "call-f3" (i32.const 31) (f64.const 2) (f64.const 3) (f64.const 4)) (f64.const 1234)) +(assert_return (invoke "call-f3" (i32.const 32) (f64.const 2) (f64.const 3) (f64.const 4)) (f64.const 1234)) +(assert_return (invoke "call-f4" (i32.const 40) (f64.const 1) (f64.const 2) (f64.const 3) (f64.const 4)) (f64.const 1234)) +(assert_return (invoke "call-f4" (i32.const 41) (f64.const 1) (f64.const 2) (f64.const 3) (f64.const 4)) (f64.const 1234)) + + +(; Once we allow subtyping on function types: +;; The runtime type of a closure is its internal one, +;; not the static bind annotation. +(module + (type $ii (func (param i32) (result i32))) + (type $fl (func (param i32 anyref) (result (ref null $ii)))) + (type $fu (func (param i32 (ref $ii)) (result anyref))) + (type $fl' (func (param anyref) (result (ref null $ii)))) + (type $fu' (func (param (ref $ii)) (result anyref))) + + (elem declare func $sqr $f) + (func $sqr (param i32) (result i32) (i32.mul (local.get 0) (local.get 0))) + (func $f (type $fl) (ref.func $sqr)) + + (table $t 10 funcref) + + (func (export "run") (result i32) + (table.set $t (i32.const 0) (func.bind (type $fu) (ref.func $f))) + (table.set $t (i32.const 1) (func.bind (type $fu') (i32.const 0) (ref.func $f))) + + (i32.add + (call_ref (i32.const 2) + (call_indirect $t (type $fl) (i32.const 0) (ref.null $fl) (i32.const 0)) + ) + (call_ref (i32.const 3) + (call_indirect $t (type $fl') (ref.null $fl') (i32.const 1)) + ) + ) + ) +) + +(assert_return (invoke "run") (i32.const 13)) +;) + +(; Instead, for now: ;) +(assert_invalid + (module + (type $ii (func (param i32) (result i32))) + (type $fl (func (param i32 funcref) (result (ref null $ii)))) + (type $fu (func (param i32 (ref $ii)) (result funcref))) + + (elem declare func $sqr $f) + (func $sqr (param i32) (result i32) (i32.mul (local.get 0) (local.get 0))) + (func $f (type $fl) (ref.func $sqr)) + + (func (drop (func.bind (type $fu) (ref.func $f)))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $ii (func (param i32) (result i32))) + (type $fl (func (param i32 funcref) (result (ref null $ii)))) + (type $fu' (func (param (ref $ii)) (result funcref))) + + (elem declare func $sqr $f) + (func $sqr (param i32) (result i32) (i32.mul (local.get 0) (local.get 0))) + (func $f (type $fl) (ref.func $sqr)) + + (func (drop (func.bind (type $fu') (i32.const 0) (ref.func $f)))) + ) + "type mismatch" +) + + +;; Null and unreachable typing. + +(module + (type $t (func)) + (func (export "null") (result (ref $t)) + (ref.null $t) + (func.bind) + ) +) +(assert_trap (invoke "null") "null function") + +(module + (type $t (func (param f32))) + (func (export "null") (result (ref $t)) + (ref.null $t) + (func.bind (type $t)) + ) +) +(assert_trap (invoke "null") "null function") + +(module + (type $t0 (func)) + (type $t1 (func (param i64))) + (func (export "null") (result (ref $t0)) + (i64.const 0) + (ref.null $t1) + (func.bind) + ) +) +(assert_trap (invoke "null") "null function") + +(module + (type $t0 (func)) + (type $t1 (func (param i64))) + (func (export "null") (result (ref $t0)) + (i64.const 0) + (ref.null $t1) + (func.bind (type $t0)) + ) +) +(assert_trap (invoke "null") "null function") + +(assert_invalid + (module + (type $t (func (result f32))) + (func (export "null") (result i32) + (ref.null $t) + (func.bind) + ) + ) + "type mismatch" +) + + +(module + (type $t (func (param f32))) + (func (export "unreachable") (result (ref $t)) + (unreachable) + (func.bind (type $t)) + ) +) +(assert_trap (invoke "unreachable") "unreachable") + +(module + (type $t (func (param f32) (result i32))) + (elem declare func $f) + (func $f (param i32 f32) (result i32) (local.get 0)) + + (func (export "unreachable") (result (ref $t)) + (unreachable) + (ref.func $f) + (func.bind (type $t)) + ) +) +(assert_trap (invoke "unreachable") "unreachable") + +(assert_invalid + (module + (type $t (func (param f32))) + (elem declare func $f) + (func $f (param i32 f32) (result i32) (local.get 0)) + + (func (export "unreachable") (result (ref $t)) + (unreachable) + (i64.const 0) + (ref.func $f) + (func.bind (type $t)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $t (func)) + (elem declare func $f) + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "unreachable") (result (ref $t)) + (unreachable) + (ref.func $f) + (func.bind (type $t)) + (drop) + (i64.const 0) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (func (export "null") (result i32) + (unreachable) + (func.bind) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $t (func (param f32))) + (elem declare func $f) + (func $f (param i32 i32) (result i32) (local.get 0)) + + (func (export "unreachable") (result (ref $t)) + (unreachable) + (ref.func $f) + (func.bind (type $t)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $t (func)) + (func $f (param $r externref) + (func.bind (type $t) (local.get $r)) + (drop) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/i31.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/i31.wast new file mode 100644 index 000000000..ade957fc4 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/i31.wast @@ -0,0 +1,32 @@ +(module + (func (export "new") (param $i i32) (result (ref i31)) + (i31.new (local.get $i)) + ) + + (func (export "get_u") (param $i i32) (result i32) + (i31.get_u (i31.new (local.get $i))) + ) + (func (export "get_s") (param $i i32) (result i32) + (i31.get_s (i31.new (local.get $i))) + ) +) + +(assert_return (invoke "new" (i32.const 1)) (ref.i31)) + +(assert_return (invoke "get_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "get_u" (i32.const 100)) (i32.const 100)) +(assert_return (invoke "get_u" (i32.const -1)) (i32.const 0x7fff_ffff)) +(assert_return (invoke "get_u" (i32.const 0x3fff_ffff)) (i32.const 0x3fff_ffff)) +(assert_return (invoke "get_u" (i32.const 0x4000_0000)) (i32.const 0x4000_0000)) +(assert_return (invoke "get_u" (i32.const 0x7fff_ffff)) (i32.const 0x7fff_ffff)) +(assert_return (invoke "get_u" (i32.const 0xaaaa_aaaa)) (i32.const 0x2aaa_aaaa)) +(assert_return (invoke "get_u" (i32.const 0xcaaa_aaaa)) (i32.const 0x4aaa_aaaa)) + +(assert_return (invoke "get_s" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "get_s" (i32.const 100)) (i32.const 100)) +(assert_return (invoke "get_s" (i32.const -1)) (i32.const -1)) +(assert_return (invoke "get_s" (i32.const 0x3fff_ffff)) (i32.const 0x3fff_ffff)) +(assert_return (invoke "get_s" (i32.const 0x4000_0000)) (i32.const -0x4000_0000)) +(assert_return (invoke "get_s" (i32.const 0x7fff_ffff)) (i32.const -1)) +(assert_return (invoke "get_s" (i32.const 0xaaaa_aaaa)) (i32.const 0x2aaa_aaaa)) +(assert_return (invoke "get_s" (i32.const 0xcaaa_aaaa)) (i32.const 0xcaaa_aaaa)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/if.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/if.wast new file mode 100644 index 000000000..553f90d20 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/if.wast @@ -0,0 +1,1550 @@ +;; Test `if` operator + +(module + ;; Auxiliary definition + (memory 1) + + (func $dummy) + + (func (export "empty") (param i32) + (if (local.get 0) (then)) + (if (local.get 0) (then) (else)) + (if $l (local.get 0) (then)) + (if $l (local.get 0) (then) (else)) + ) + + (func (export "singular") (param i32) (result i32) + (if (local.get 0) (then (nop))) + (if (local.get 0) (then (nop)) (else (nop))) + (if (result i32) (local.get 0) (then (i32.const 7)) (else (i32.const 8))) + ) + + (func (export "multi") (param i32) (result i32 i32) + (if (local.get 0) (then (call $dummy) (call $dummy) (call $dummy))) + (if (local.get 0) (then) (else (call $dummy) (call $dummy) (call $dummy))) + (if (result i32) (local.get 0) + (then (call $dummy) (call $dummy) (i32.const 8) (call $dummy)) + (else (call $dummy) (call $dummy) (i32.const 9) (call $dummy)) + ) + (if (result i32 i64 i32) (local.get 0) + (then + (call $dummy) (call $dummy) (i32.const 1) (call $dummy) + (call $dummy) (call $dummy) (i64.const 2) (call $dummy) + (call $dummy) (call $dummy) (i32.const 3) (call $dummy) + ) + (else + (call $dummy) (call $dummy) (i32.const -1) (call $dummy) + (call $dummy) (call $dummy) (i64.const -2) (call $dummy) + (call $dummy) (call $dummy) (i32.const -3) (call $dummy) + ) + ) + (drop) (drop) + ) + + (func (export "nested") (param i32 i32) (result i32) + (if (result i32) (local.get 0) + (then + (if (local.get 1) (then (call $dummy) (block) (nop))) + (if (local.get 1) (then) (else (call $dummy) (block) (nop))) + (if (result i32) (local.get 1) + (then (call $dummy) (i32.const 9)) + (else (call $dummy) (i32.const 10)) + ) + ) + (else + (if (local.get 1) (then (call $dummy) (block) (nop))) + (if (local.get 1) (then) (else (call $dummy) (block) (nop))) + (if (result i32) (local.get 1) + (then (call $dummy) (i32.const 10)) + (else (call $dummy) (i32.const 11)) + ) + ) + ) + ) + + (func (export "as-select-first") (param i32) (result i32) + (select + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 2) (i32.const 3) + ) + ) + (func (export "as-select-mid") (param i32) (result i32) + (select + (i32.const 2) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 3) + ) + ) + (func (export "as-select-last") (param i32) (result i32) + (select + (i32.const 2) (i32.const 3) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + ) + ) + + (func (export "as-loop-first") (param i32) (result i32) + (loop (result i32) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (call $dummy) (call $dummy) + ) + ) + (func (export "as-loop-mid") (param i32) (result i32) + (loop (result i32) + (call $dummy) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (call $dummy) + ) + ) + (func (export "as-loop-last") (param i32) (result i32) + (loop (result i32) + (call $dummy) (call $dummy) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + ) + ) + + (func (export "as-if-condition") (param i32) (result i32) + (if (result i32) + (if (result i32) (local.get 0) + (then (i32.const 1)) (else (i32.const 0)) + ) + (then (call $dummy) (i32.const 2)) + (else (call $dummy) (i32.const 3)) + ) + ) + + (func (export "as-br_if-first") (param i32) (result i32) + (block (result i32) + (br_if 0 + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 2) + ) + (return (i32.const 3)) + ) + ) + (func (export "as-br_if-last") (param i32) (result i32) + (block (result i32) + (br_if 0 + (i32.const 2) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + ) + (return (i32.const 3)) + ) + ) + + (func (export "as-br_table-first") (param i32) (result i32) + (block (result i32) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 2) + (br_table 0 0) + ) + ) + (func (export "as-br_table-last") (param i32) (result i32) + (block (result i32) + (i32.const 2) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (br_table 0 0) + ) + ) + + (func $func (param i32 i32) (result i32) (local.get 0)) + (type $check (func (param i32 i32) (result i32))) + (table funcref (elem $func)) + (func (export "as-call_indirect-first") (param i32) (result i32) + (block (result i32) + (call_indirect (type $check) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 2) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-mid") (param i32) (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 2) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-last") (param i32) (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 2) (i32.const 0) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + ) + ) + ) + + (func (export "as-store-first") (param i32) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 2) + (i32.store) + ) + (func (export "as-store-last") (param i32) + (i32.const 2) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.store) + ) + + (func (export "as-memory.grow-value") (param i32) (result i32) + (memory.grow + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ) + + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "as-call-value") (param i32) (result i32) + (call $f + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ) + (func (export "as-return-value") (param i32) (result i32) + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0))) + (return) + ) + (func (export "as-drop-operand") (param i32) + (drop + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ) + (func (export "as-br-value") (param i32) (result i32) + (block (result i32) + (br 0 + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ) + ) + (func (export "as-local.set-value") (param i32) (result i32) + (local i32) + (local.set 0 + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + (local.get 0) + ) + (func (export "as-local.tee-value") (param i32) (result i32) + (local.tee 0 + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (param i32) (result i32) + (global.set $a + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) (global.get $a) + ) + (func (export "as-load-operand") (param i32) (result i32) + (i32.load + (if (result i32) (local.get 0) + (then (i32.const 11)) + (else (i32.const 10)) + ) + ) + ) + + (func (export "as-unary-operand") (param i32) (result i32) + (i32.ctz + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 13)) + (else (call $dummy) (i32.const -13)) + ) + ) + ) + (func (export "as-binary-operand") (param i32 i32) (result i32) + (i32.mul + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 3)) + (else (call $dummy) (i32.const -3)) + ) + (if (result i32) (local.get 1) + (then (call $dummy) (i32.const 4)) + (else (call $dummy) (i32.const -5)) + ) + ) + ) + (func (export "as-test-operand") (param i32) (result i32) + (i32.eqz + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 13)) + (else (call $dummy) (i32.const 0)) + ) + ) + ) + (func (export "as-compare-operand") (param i32 i32) (result i32) + (f32.gt + (if (result f32) (local.get 0) + (then (call $dummy) (f32.const 3)) + (else (call $dummy) (f32.const -3)) + ) + (if (result f32) (local.get 1) + (then (call $dummy) (f32.const 4)) + (else (call $dummy) (f32.const -4)) + ) + ) + ) + (func (export "as-binary-operands") (param i32) (result i32) + (i32.mul + (if (result i32 i32) (local.get 0) + (then (call $dummy) (i32.const 3) (call $dummy) (i32.const 4)) + (else (call $dummy) (i32.const 3) (call $dummy) (i32.const -4)) + ) + ) + ) + (func (export "as-compare-operands") (param i32) (result i32) + (f32.gt + (if (result f32 f32) (local.get 0) + (then (call $dummy) (f32.const 3) (call $dummy) (f32.const 3)) + (else (call $dummy) (f32.const -2) (call $dummy) (f32.const -3)) + ) + ) + ) + (func (export "as-mixed-operands") (param i32) (result i32) + (if (result i32 i32) (local.get 0) + (then (call $dummy) (i32.const 3) (call $dummy) (i32.const 4)) + (else (call $dummy) (i32.const -3) (call $dummy) (i32.const -4)) + ) + (i32.const 5) + (i32.add) + (i32.mul) + ) + + (func (export "break-bare") (result i32) + (if (i32.const 1) (then (br 0) (unreachable))) + (if (i32.const 1) (then (br 0) (unreachable)) (else (unreachable))) + (if (i32.const 0) (then (unreachable)) (else (br 0) (unreachable))) + (if (i32.const 1) (then (br_if 0 (i32.const 1)) (unreachable))) + (if (i32.const 1) (then (br_if 0 (i32.const 1)) (unreachable)) (else (unreachable))) + (if (i32.const 0) (then (unreachable)) (else (br_if 0 (i32.const 1)) (unreachable))) + (if (i32.const 1) (then (br_table 0 (i32.const 0)) (unreachable))) + (if (i32.const 1) (then (br_table 0 (i32.const 0)) (unreachable)) (else (unreachable))) + (if (i32.const 0) (then (unreachable)) (else (br_table 0 (i32.const 0)) (unreachable))) + (i32.const 19) + ) + + (func (export "break-value") (param i32) (result i32) + (if (result i32) (local.get 0) + (then (br 0 (i32.const 18)) (i32.const 19)) + (else (br 0 (i32.const 21)) (i32.const 20)) + ) + ) + (func (export "break-multi-value") (param i32) (result i32 i32 i64) + (if (result i32 i32 i64) (local.get 0) + (then + (br 0 (i32.const 18) (i32.const -18) (i64.const 18)) + (i32.const 19) (i32.const -19) (i64.const 19) + ) + (else + (br 0 (i32.const -18) (i32.const 18) (i64.const -18)) + (i32.const -19) (i32.const 19) (i64.const -19) + ) + ) + ) + + (func (export "param") (param i32) (result i32) + (i32.const 1) + (if (param i32) (result i32) (local.get 0) + (then (i32.const 2) (i32.add)) + (else (i32.const -2) (i32.add)) + ) + ) + (func (export "params") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32) (local.get 0) + (then (i32.add)) + (else (i32.sub)) + ) + ) + (func (export "params-id") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32 i32) (local.get 0) (then)) + (i32.add) + ) + (func (export "param-break") (param i32) (result i32) + (i32.const 1) + (if (param i32) (result i32) (local.get 0) + (then (i32.const 2) (i32.add) (br 0)) + (else (i32.const -2) (i32.add) (br 0)) + ) + ) + (func (export "params-break") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32) (local.get 0) + (then (i32.add) (br 0)) + (else (i32.sub) (br 0)) + ) + ) + (func (export "params-id-break") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32 i32) (local.get 0) (then (br 0))) + (i32.add) + ) + + (func (export "effects") (param i32) (result i32) + (local i32) + (if + (block (result i32) (local.set 1 (i32.const 1)) (local.get 0)) + (then + (local.set 1 (i32.mul (local.get 1) (i32.const 3))) + (local.set 1 (i32.sub (local.get 1) (i32.const 5))) + (local.set 1 (i32.mul (local.get 1) (i32.const 7))) + (br 0) + (local.set 1 (i32.mul (local.get 1) (i32.const 100))) + ) + (else + (local.set 1 (i32.mul (local.get 1) (i32.const 5))) + (local.set 1 (i32.sub (local.get 1) (i32.const 7))) + (local.set 1 (i32.mul (local.get 1) (i32.const 3))) + (br 0) + (local.set 1 (i32.mul (local.get 1) (i32.const 1000))) + ) + ) + (local.get 1) + ) + + ;; Examples + + (func $add64_u_with_carry (export "add64_u_with_carry") + (param $i i64) (param $j i64) (param $c i32) (result i64 i32) + (local $k i64) + (local.set $k + (i64.add + (i64.add (local.get $i) (local.get $j)) + (i64.extend_i32_u (local.get $c)) + ) + ) + (return (local.get $k) (i64.lt_u (local.get $k) (local.get $i))) + ) + + (func $add64_u_saturated (export "add64_u_saturated") + (param i64 i64) (result i64) + (call $add64_u_with_carry (local.get 0) (local.get 1) (i32.const 0)) + (if (param i64) (result i64) + (then (drop) (i64.const -1)) + ) + ) + + ;; Block signature syntax + + (type $block-sig-1 (func)) + (type $block-sig-2 (func (result i32))) + (type $block-sig-3 (func (param $x i32))) + (type $block-sig-4 (func (param i32 f64 i32) (result i32 f64 i32))) + + (func (export "type-use") + (if (type $block-sig-1) (i32.const 1) (then)) + (if (type $block-sig-2) (i32.const 1) + (then (i32.const 0)) (else (i32.const 2)) + ) + (if (type $block-sig-3) (i32.const 1) (then (drop)) (else (drop))) + (i32.const 0) (f64.const 0) (i32.const 0) + (if (type $block-sig-4) (i32.const 1) (then)) + (drop) (drop) (drop) + (if (type $block-sig-2) (result i32) (i32.const 1) + (then (i32.const 0)) (else (i32.const 2)) + ) + (if (type $block-sig-3) (param i32) (i32.const 1) + (then (drop)) (else (drop)) + ) + (i32.const 0) (f64.const 0) (i32.const 0) + (if (type $block-sig-4) + (param i32) (param f64 i32) (result i32 f64) (result i32) + (i32.const 1) (then) + ) + (drop) (drop) (drop) + ) +) + +(assert_return (invoke "empty" (i32.const 0))) +(assert_return (invoke "empty" (i32.const 1))) +(assert_return (invoke "empty" (i32.const 100))) +(assert_return (invoke "empty" (i32.const -2))) + +(assert_return (invoke "singular" (i32.const 0)) (i32.const 8)) +(assert_return (invoke "singular" (i32.const 1)) (i32.const 7)) +(assert_return (invoke "singular" (i32.const 10)) (i32.const 7)) +(assert_return (invoke "singular" (i32.const -10)) (i32.const 7)) + +(assert_return (invoke "multi" (i32.const 0)) (i32.const 9) (i32.const -1)) +(assert_return (invoke "multi" (i32.const 1)) (i32.const 8) (i32.const 1)) +(assert_return (invoke "multi" (i32.const 13)) (i32.const 8) (i32.const 1)) +(assert_return (invoke "multi" (i32.const -5)) (i32.const 8) (i32.const 1)) + +(assert_return (invoke "nested" (i32.const 0) (i32.const 0)) (i32.const 11)) +(assert_return (invoke "nested" (i32.const 1) (i32.const 0)) (i32.const 10)) +(assert_return (invoke "nested" (i32.const 0) (i32.const 1)) (i32.const 10)) +(assert_return (invoke "nested" (i32.const 3) (i32.const 2)) (i32.const 9)) +(assert_return (invoke "nested" (i32.const 0) (i32.const -100)) (i32.const 10)) +(assert_return (invoke "nested" (i32.const 10) (i32.const 10)) (i32.const 9)) +(assert_return (invoke "nested" (i32.const 0) (i32.const -1)) (i32.const 10)) +(assert_return (invoke "nested" (i32.const -111) (i32.const -2)) (i32.const 9)) + +(assert_return (invoke "as-select-first" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-select-first" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-select-mid" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-select-mid" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-select-last" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-select-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-loop-first" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-loop-first" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-loop-mid" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-loop-mid" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-loop-last" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-loop-last" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-if-condition" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-if-condition" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-br_if-first" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-br_if-first" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-br_if-last" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-br_if-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-br_table-first" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-br_table-first" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-br_table-last" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-br_table-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-call_indirect-first" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-call_indirect-first" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-call_indirect-mid" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-call_indirect-mid" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-call_indirect-last" (i32.const 0)) (i32.const 2)) +(assert_trap (invoke "as-call_indirect-last" (i32.const 1)) "undefined element") + +(assert_return (invoke "as-store-first" (i32.const 0))) +(assert_return (invoke "as-store-first" (i32.const 1))) +(assert_return (invoke "as-store-last" (i32.const 0))) +(assert_return (invoke "as-store-last" (i32.const 1))) + +(assert_return (invoke "as-memory.grow-value" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-memory.grow-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-call-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-call-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-return-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-return-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-drop-operand" (i32.const 0))) +(assert_return (invoke "as-drop-operand" (i32.const 1))) + +(assert_return (invoke "as-br-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-br-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-local.set-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-local.set-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-local.tee-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-local.tee-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-global.set-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-global.set-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-load-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-load-operand" (i32.const 1)) (i32.const 0)) + +(assert_return (invoke "as-unary-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-unary-operand" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "as-unary-operand" (i32.const -1)) (i32.const 0)) + +(assert_return (invoke "as-binary-operand" (i32.const 0) (i32.const 0)) (i32.const 15)) +(assert_return (invoke "as-binary-operand" (i32.const 0) (i32.const 1)) (i32.const -12)) +(assert_return (invoke "as-binary-operand" (i32.const 1) (i32.const 0)) (i32.const -15)) +(assert_return (invoke "as-binary-operand" (i32.const 1) (i32.const 1)) (i32.const 12)) + +(assert_return (invoke "as-test-operand" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-test-operand" (i32.const 1)) (i32.const 0)) + +(assert_return (invoke "as-compare-operand" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-compare-operand" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "as-compare-operand" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-compare-operand" (i32.const 1) (i32.const 1)) (i32.const 0)) + +(assert_return (invoke "as-binary-operands" (i32.const 0)) (i32.const -12)) +(assert_return (invoke "as-binary-operands" (i32.const 1)) (i32.const 12)) + +(assert_return (invoke "as-compare-operands" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-compare-operands" (i32.const 1)) (i32.const 0)) + +(assert_return (invoke "as-mixed-operands" (i32.const 0)) (i32.const -3)) +(assert_return (invoke "as-mixed-operands" (i32.const 1)) (i32.const 27)) + +(assert_return (invoke "break-bare") (i32.const 19)) +(assert_return (invoke "break-value" (i32.const 1)) (i32.const 18)) +(assert_return (invoke "break-value" (i32.const 0)) (i32.const 21)) +(assert_return (invoke "break-multi-value" (i32.const 0)) + (i32.const -18) (i32.const 18) (i64.const -18) +) +(assert_return (invoke "break-multi-value" (i32.const 1)) + (i32.const 18) (i32.const -18) (i64.const 18) +) + +(assert_return (invoke "param" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "param" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "params" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params-id" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "params-id" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "param-break" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "param-break" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params-break" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "params-break" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params-id-break" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "params-id-break" (i32.const 1)) (i32.const 3)) + +(assert_return (invoke "effects" (i32.const 1)) (i32.const -14)) +(assert_return (invoke "effects" (i32.const 0)) (i32.const -6)) + +(assert_return + (invoke "add64_u_with_carry" (i64.const 0) (i64.const 0) (i32.const 0)) + (i64.const 0) (i32.const 0) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const 100) (i64.const 124) (i32.const 0)) + (i64.const 224) (i32.const 0) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 0) (i32.const 0)) + (i64.const -1) (i32.const 0) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 1) (i32.const 0)) + (i64.const 0) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const -1) (i32.const 0)) + (i64.const -2) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 0) (i32.const 1)) + (i64.const 0) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 1) (i32.const 1)) + (i64.const 1) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000) (i32.const 0)) + (i64.const 0) (i32.const 1) +) + +(assert_return + (invoke "add64_u_saturated" (i64.const 0) (i64.const 0)) (i64.const 0) +) +(assert_return + (invoke "add64_u_saturated" (i64.const 1230) (i64.const 23)) (i64.const 1253) +) +(assert_return + (invoke "add64_u_saturated" (i64.const -1) (i64.const 0)) (i64.const -1) +) +(assert_return + (invoke "add64_u_saturated" (i64.const -1) (i64.const 1)) (i64.const -1) +) +(assert_return + (invoke "add64_u_saturated" (i64.const -1) (i64.const -1)) (i64.const -1) +) +(assert_return + (invoke "add64_u_saturated" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i64.const -1) +) + +(assert_return (invoke "type-use")) + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (type $sig) (result i32) (param i32) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (param i32) (type $sig) (result i32) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (param i32) (result i32) (type $sig) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (result i32) (type $sig) (param i32) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (result i32) (param i32) (type $sig) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func (i32.const 0) (if (result i32) (param i32) (i32.const 1) (then)))" + ) + "unexpected token" +) + +(assert_malformed + (module quote + "(func (i32.const 0) (i32.const 1)" + " (if (param $x i32) (then (drop)) (else (drop)))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func))" + "(func (i32.const 1)" + " (if (type $sig) (result i32) (then (i32.const 0)) (else (i32.const 2)))" + " (unreachable)" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 1)" + " (if (type $sig) (result i32) (then (i32.const 0)) (else (i32.const 2)))" + " (unreachable)" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (i32.const 1)" + " (if (type $sig) (param i32) (then (drop)) (else (drop)))" + " (unreachable)" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(func (i32.const 0) (i32.const 1)" + " (if (type $sig) (param i32) (result i32) (then)) (unreachable)" + ")" + ) + "inline function type" +) + +(assert_invalid + (module + (type $sig (func)) + (func (i32.const 1) (if (type $sig) (i32.const 0) (then))) + ) + "type mismatch" +) + +(assert_invalid + (module (func $type-empty-i32 (result i32) (if (i32.const 0) (then)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-i64 (result i64) (if (i32.const 0) (then)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f32 (result f32) (if (i32.const 0) (then)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f64 (result f64) (if (i32.const 0) (then)))) + "type mismatch" +) + +(assert_invalid + (module (func $type-empty-i32 (result i32) (if (i32.const 0) (then) (else)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-i64 (result i64) (if (i32.const 0) (then) (else)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f32 (result f32) (if (i32.const 0) (then) (else)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f64 (result f64) (if (i32.const 0) (then) (else)))) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-num-vs-void + (if (i32.const 1) (then (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-value-num-vs-void-else + (if (i32.const 1) (then (i32.const 1)) (else)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-num-vs-void + (if (i32.const 1) (then) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-num-vs-void + (if (i32.const 1) (then (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-nums-vs-void + (if (i32.const 1) (then (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-value-nums-vs-void-else + (if (i32.const 1) (then (i32.const 1) (i32.const 2)) (else)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-nums-vs-void + (if (i32.const 1) (then) (else (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-nums-vs-void + (if (i32.const 1) (then (i32.const 1) (i32.const 2)) (else (i32.const 2) (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-empty-vs-num (result i32) + (if (result i32) (i32.const 1) (then) (else (i32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-empty-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 0)) (else)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-empty-vs-num (result i32) + (if (result i32) (i32.const 1) (then) (else)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then) (else (i32.const 0) (i32.const 2))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 0) (i32.const 1)) (else)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then) (else)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-no-else-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-no-else-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-void-vs-num (result i32) + (if (result i32) (i32.const 1) (then (nop)) (else (i32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-void-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 0)) (else (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-void-vs-num (result i32) + (if (result i32) (i32.const 1) (then (nop)) (else (nop))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (nop)) (else (i32.const 0) (i32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 0) (i32.const 0)) (else (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (nop)) (else (nop))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-num-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i64.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-num-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1)) (else (i64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-num-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i64.const 1)) (else (i64.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-partial-vs-nums (result i32 i32) + (i32.const 0) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-partial-vs-nums (result i32 i32) + (i32.const 0) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-partial-vs-nums (result i32 i32) + (i32.const 0) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-nums-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-nums-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-nums-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-both-different-value-num-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i64.const 1)) (else (f64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-different-value-nums-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-unreached-select (result i32) + (if (result i64) + (i32.const 0) + (then (select (unreachable) (unreachable) (unreachable))) + (else (i64.const 0)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-unreached-select (result i32) + (if (result i64) + (i32.const 1) + (then (i64.const 0)) + (else (select (unreachable) (unreachable) (unreachable))) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-unreached-select (result i32) + (if (result i64) + (i32.const 1) + (then (select (unreachable) (unreachable) (unreachable))) + (else (select (unreachable) (unreachable) (unreachable))) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-break-last-void-vs-num (result i32) + (if (result i32) (i32.const 1) (then (br 0)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-last-void-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1)) (else (br 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-last-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (br 0)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-last-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (br 0))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-break-empty-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (br 0) (i32.const 1)) + (else (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-empty-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (i32.const 1)) + (else (br 0) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (br 0) (i32.const 1) (i32.const 1)) + (else (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1) (i32.const 1)) + (else (br 0) (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-break-void-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (br 0 (nop)) (i32.const 1)) + (else (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-void-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (i32.const 1)) + (else (br 0 (nop)) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (br 0 (nop)) (i32.const 1) (i32.const 1)) + (else (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1) (i32.const 1)) + (else (br 0 (nop)) (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-break-num-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (br 0 (i64.const 1)) (i32.const 1)) + (else (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-num-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (i32.const 1)) + (else (br 0 (i64.const 1)) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (br 0 (i64.const 1)) (i32.const 1) (i32.const 1)) + (else (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1) (i32.const 1)) + (else (br 0 (i64.const 1)) (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-partial-vs-nums (result i32 i32) + (i32.const 1) + (if (result i32 i32) (i32.const 1) + (then (br 0 (i64.const 1)) (i32.const 1)) + (else (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-partial-vs-nums (result i32 i32) + (i32.const 1) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1)) + (else (br 0 (i64.const 1)) (i32.const 1)) + ) + )) + "type mismatch" +) + +(assert_invalid + (module + (func $type-condition-empty + (if (then)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-block + (i32.const 0) + (block (if (then))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-loop + (i32.const 0) + (loop (if (then))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-then + (i32.const 0) (i32.const 0) + (if (then (if (then)))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-else + (i32.const 0) (i32.const 0) + (if (result i32) (then (i32.const 0)) (else (if (then)) (i32.const 0))) + (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-br + (i32.const 0) + (block (br 0 (if(then))) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-br_if + (i32.const 0) + (block (br_if 0 (if(then)) (i32.const 1)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-br_table + (i32.const 0) + (block (br_table 0 (if(then))) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-return + (return (if(then))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-select + (select (if(then)) (i32.const 1) (i32.const 2)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-call + (call 1 (if(then))) (drop) + ) + (func (param i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-condition-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (if(then)) (i32.const 0) + ) + (drop) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-local.set + (local i32) + (local.set 0 (if(then))) (local.get 0) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-local.tee + (local i32) + (local.tee 0 (if(then))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-condition-empty-in-global.set + (global.set $x (if(then))) (global.get $x) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-condition-empty-in-memory.grow + (memory.grow (if(then))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-condition-empty-in-load + (i32.load (if(then))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-condition-empty-in-store + (i32.store (if(then)) (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module (func $type-param-void-vs-num + (if (param i32) (i32.const 1) (then (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (if (param i32 f64) (i32.const 1) (then (drop) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (f32.const 0) (if (param i32) (i32.const 1) (then (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (f32.const 0) (if (param f32 i32) (i32.const 1) (then (drop) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-nested-void-vs-num + (block (if (param i32) (i32.const 1) (then (drop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (block (if (param i32 f64) (i32.const 1) (then (drop) (drop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (block (f32.const 0) (if (param i32) (i32.const 1) (then (drop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (block (f32.const 0) (if (param f32 i32) (i32.const 1) (then (drop) (drop)))) + )) + "type mismatch" +) + +(assert_malformed + (module quote "(func (param i32) (result i32) if (param $x i32) end)") + "unexpected token" +) +(assert_malformed + (module quote "(func (param i32) (result i32) (if (param $x i32) (then)))") + "unexpected token" +) + +(assert_malformed + (module quote "(func i32.const 0 if end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if $a end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if else $l end)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if $a else $l end)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if else end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if else $l end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if else $l1 end $l2)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if $a else end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if $a else $a end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if $a else $l end $l)") + "mismatching label" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/let.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/let.wast new file mode 100644 index 000000000..451bc5d6f --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/let.wast @@ -0,0 +1,302 @@ +(module + ;; Auxiliary + (type $dummy (func)) + (elem declare func $dummy) + (func $dummy) + (func $consume (param i32)) + (func $produce (result i32) (i32.const 7)) + + (func (export "syntax") (param $x1 i32) (param $x2 i64) + (local $y1 i64) + (local $y2 i32) + (local.set $y2 (i32.const 5)) + + (let) + (let $l) + (let (local)) + (let $l (local)) + + (let (result) (local)) + (let $l (result) (local)) + + (let (call $dummy) (call $consume (local.get $x1))) + (let $l (call $dummy) (call $consume (local.get $y2))) + (let (local) (call $dummy) (call $consume (local.get $y2))) + (let $l (local) (call $dummy) (call $consume (local.get $x1))) + + (let (call $dummy) (call $dummy) (br 0)) + (let $l (call $dummy) (call $dummy) (br $l)) + (let (local) (call $dummy) (call $dummy) (br 0)) + (let $l (local) (call $dummy) (call $dummy) (br $l)) + + (i32.const 1) + (f32.const 2) + (let $l (param i32) (result i64) (local f32) (br $l (i64.const 3))) + (drop) + + (let (result i32) (call $dummy) (call $produce) (call $dummy)) + (drop) + (let $l (result i32) (call $dummy) (call $produce) (call $dummy)) + (drop) + (let (result i32) (local) (call $dummy) (call $produce) (call $dummy)) + (drop) + (let $l (result i32) (local) (call $dummy) (call $produce) (call $dummy)) + (drop) + + (i32.const 1) + (let (local i32) (call $dummy) (call $consume (local.get 0))) + (i32.const 2) + (let $l (local i32) (call $dummy) (call $consume (local.get 0))) + (i32.const 3) + (let (local i32) (call $dummy) (call $consume (local.get 0))) + (i32.const 4) + (let $l (local i32) (call $dummy) (call $consume (local.get 0))) + + (i32.const 1) (f32.const 2) (i32.const 3) (i64.const 4) + (let (local i32 f32) (local) (local $z i32) (local i64) + (call $consume (local.get 0)) + (call $consume (local.get 2)) ;; $z + (call $consume (local.get 4)) ;; $x1 + (call $consume (local.get 7)) ;; $y2 + (call $consume (local.get $z)) + (call $consume (local.get $x1)) + (call $consume (local.get $y2)) + ) + + (f32.const 1) (i32.const 2) (i64.const 3) (i32.const 4) + (let (result i32) (local $z1 f32) (local $z2 i32) (local) (local i64 i32) + (call $produce) + (call $consume (local.get 1)) ;; $z2 + (call $consume (local.get 3)) + (call $consume (local.get 4)) ;; $x1 + (call $consume (local.get 7)) ;; $y2 + (call $consume (local.get $z2)) + (call $consume (local.get $x1)) + (call $consume (local.get $y2)) + ) + (drop) + + (ref.func $dummy) + (let (local (ref $dummy))) + ) + + (func $pow (export "pow") (param $x i64) (param $n i32) (result i64) + (local $y i64) + (local.set $y + (if (result i64) (i32.and (local.get $n) (i32.const 1)) + (then (local.get $x)) (else (i64.const 1)) + ) + ) + (i64.mul + (local.get $y) + (if (result i64) (i32.le_u (local.get $n) (i32.const 1)) + (then (i64.const 1)) + (else + (call $pow + (i64.mul (local.get $x) (local.get $x)) + (i32.shr_u (local.get $n) (i32.const 1)) + ) + ) + ) + ) + ) + + (func (export "semantics-idx") (param i64 i64) (result i64) + (local i64 i64) + (local.set 2 (i64.const 5)) + (local.set 3 (i64.const 7)) + + (i64.const 11) (i64.const 13) + (let (result i64) (local i64 i64) + (i64.const 17) (i64.const 19) + (let (result i64) (local i64 i64) + (i64.const 0) + (i64.add (call $pow (local.get 0) (i32.const 0))) ;; 17^0 = 1 + (i64.add (call $pow (local.get 1) (i32.const 1))) ;; 19^1 = 19 + (i64.add (call $pow (local.get 2) (i32.const 2))) ;; 11^2 = 121 + (i64.add (call $pow (local.get 3) (i32.const 3))) ;; 13^3 = 2197 + (i64.add (call $pow (local.get 4) (i32.const 4))) ;; 2^4 = 16 + (i64.add (call $pow (local.get 5) (i32.const 5))) ;; 3^5 = 243 + (i64.add (call $pow (local.get 6) (i32.const 6))) ;; 5^6 = 15625 + (i64.add (call $pow (local.get 7) (i32.const 7))) ;; 7^7 = 823543 + ) + ) + ) + + (func (export "semantics-sym") (param $x1 i64) (param $x2 i64) (result i64) + (local $y1 i64) + (local $y2 i64) + (local.set $y1 (i64.const 5)) + (local.set $y2 (i64.const 7)) + + (i64.const 11) (i64.const 13) + (let (result i64) (local $z1 i64) (local $z2 i64) + (i64.const 17) (i64.const 19) + (let (result i64) (local $u1 i64) (local $u2 i64) + (i64.const 0) + (i64.add (call $pow (local.get $u1) (i32.const 0))) ;; 17^0 = 1 + (i64.add (call $pow (local.get $u2) (i32.const 1))) ;; 19^1 = 19 + (i64.add (call $pow (local.get $z1) (i32.const 2))) ;; 11^2 = 121 + (i64.add (call $pow (local.get $z2) (i32.const 3))) ;; 13^3 = 2197 + (i64.add (call $pow (local.get $x1) (i32.const 4))) ;; 2^4 = 16 + (i64.add (call $pow (local.get $x2) (i32.const 5))) ;; 3^5 = 243 + (i64.add (call $pow (local.get $y1) (i32.const 6))) ;; 5^6 = 15625 + (i64.add (call $pow (local.get $y2) (i32.const 7))) ;; 7^7 = 823543 + ) + ) + ) + + (func (export "mutate") (param $x1 i64) (param $x2 i64) (result i64) + (local $y1 i64) + (local $y2 i64) + + (i64.const 0) (i64.const 0) + (let (result i64) (local $z1 i64) (local $z2 i64) + (i64.const 0) (i64.const 0) + (let (result i64) (local $u1 i64) (local $u2 i64) + (local.set $y1 (i64.const 5)) + (local.set $y2 (i64.const 7)) + (local.set $z1 (i64.const 11)) + (local.set $z2 (i64.const 13)) + (local.set $u1 (i64.const 17)) + (local.set $u2 (i64.const 19)) + (i64.const 0) + (i64.add (call $pow (local.get $u1) (i32.const 0))) ;; 17^0 = 1 + (i64.add (call $pow (local.get $u2) (i32.const 1))) ;; 19^1 = 19 + (i64.add (call $pow (local.get $z1) (i32.const 2))) ;; 11^2 = 121 + (i64.add (call $pow (local.get $z2) (i32.const 3))) ;; 13^3 = 2197 + (i64.add (call $pow (local.get $x1) (i32.const 4))) ;; 2^4 = 16 + (i64.add (call $pow (local.get $x2) (i32.const 5))) ;; 3^5 = 243 + (i64.add (call $pow (local.get $y1) (i32.const 6))) ;; 5^6 = 15625 + (i64.add (call $pow (local.get $y2) (i32.const 7))) ;; 7^7 = 823543 + ) + ) + ) +) + +(assert_return (invoke "syntax" (i32.const 1) (i64.const 2))) + +(assert_return (invoke "pow" (i64.const 17) (i32.const 0)) (i64.const 1)) +(assert_return (invoke "pow" (i64.const 19) (i32.const 1)) (i64.const 19)) +(assert_return (invoke "pow" (i64.const 11) (i32.const 2)) (i64.const 121)) +(assert_return (invoke "pow" (i64.const 13) (i32.const 3)) (i64.const 2197)) +(assert_return (invoke "pow" (i64.const 2) (i32.const 4)) (i64.const 16)) +(assert_return (invoke "pow" (i64.const 3) (i32.const 5)) (i64.const 243)) +(assert_return (invoke "pow" (i64.const 5) (i32.const 6)) (i64.const 15625)) +(assert_return (invoke "pow" (i64.const 7) (i32.const 7)) (i64.const 823543)) + +(assert_return (invoke "semantics-idx" (i64.const 2) (i64.const 3)) (i64.const 841_765)) +(assert_return (invoke "semantics-sym" (i64.const 2) (i64.const 3)) (i64.const 841_765)) +(assert_return (invoke "mutate" (i64.const 2) (i64.const 3)) (i64.const 841_765)) + + +;; Shadowing. + +;; Shadowing is fine across nested let blocks (analogous to labels). +(module + (func (export "f1") (param $x i32) (result i32) + (i32.const 1) + (let (local $x i32) (return (local.get $x))) + (unreachable) + ) + (func (export "f2") (result i32) + (local $x i32) + (i32.const 1) + (let (local $x i32) (return (local.get $x))) + (unreachable) + ) + (func (export "f3") (result i32) + (i32.const 0) + (let (local $x i32) + (i32.const 1) + (let (local $x i32) (return (local.get $x))) + ) + (unreachable) + ) + (func (export "f4") (result i32) + (local $x i32) + (i32.const 1) + (let (local $x i32) + (i32.const 2) + (let (local $x i32) (return (local.get $x))) + ) + (unreachable) + ) +) + +(assert_return (invoke "f1" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "f2") (i32.const 1)) +(assert_return (invoke "f3") (i32.const 1)) +(assert_return (invoke "f4") (i32.const 2)) + +;; Duplicate labels within a single let block are still disallowed. +(assert_malformed + (module quote "(func (let (local $x i32) (local $x i64)))") + "duplicate local" +) + + +;; Syntax + +(assert_malformed + (module quote "(func (let (local) (param)))") + "unexpected token" +) +(assert_malformed + (module quote + "(func" + " (i32.const 0) (i32.const 0)" + " (let (local i32) (param i32) (drop))" + ")" + ) + "unexpected token" +) + +(assert_malformed + (module quote "(func (let (local) (result)))") + "unexpected token" +) +(assert_malformed + (module quote + "(func (result i32)" + " (i32.const 0)" + " (let (local i32) (result i32) (local.get 0))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func (result i32)" + " (let (local $x i32) (result i32) (local.get 0))" + ")" + ) + "unexpected token" +) + +(assert_malformed + (module quote "(func (let (result) (param)))") + "unexpected token" +) +(assert_malformed + (module quote + "(func (result i32)" + " (i32.const 0)" + " (let (result i32) (param i32))" + ")" + ) + "unexpected token" +) + +(assert_malformed + (module quote "(func (let (param) $l))") + "unexpected token" +) +(assert_malformed + (module quote "(func (let (result) $l))") + "unexpected token" +) +(assert_malformed + (module quote "(func (let (local) $l))") + "unexpected token" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/linking.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/linking.wast new file mode 100644 index 000000000..85de43d8f --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/linking.wast @@ -0,0 +1,627 @@ +;; Functions + +(module $Mf + (func (export "call") (result i32) (call $g)) + (func $g (result i32) (i32.const 2)) +) +(register "Mf" $Mf) + +(module $Nf + (func $f (import "Mf" "call") (result i32)) + (export "Mf.call" (func $f)) + (func (export "call Mf.call") (result i32) (call $f)) + (func (export "call") (result i32) (call $g)) + (func $g (result i32) (i32.const 3)) +) + +(assert_return (invoke $Mf "call") (i32.const 2)) +(assert_return (invoke $Nf "Mf.call") (i32.const 2)) +(assert_return (invoke $Nf "call") (i32.const 3)) +(assert_return (invoke $Nf "call Mf.call") (i32.const 2)) + +(module + (import "spectest" "print_i32" (func $f (param i32))) + (export "print" (func $f)) +) +(register "reexport_f") +(assert_unlinkable + (module (import "reexport_f" "print" (func (param i64)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "reexport_f" "print" (func (param i32) (result i32)))) + "incompatible import type" +) + + +;; Globals + +(module $Mg + (global $glob (export "glob") i32 (i32.const 42)) + (func (export "get") (result i32) (global.get $glob)) + + ;; export mutable globals + (global $mut_glob (export "mut_glob") (mut i32) (i32.const 142)) + (func (export "get_mut") (result i32) (global.get $mut_glob)) + (func (export "set_mut") (param i32) (global.set $mut_glob (local.get 0))) +) +(register "Mg" $Mg) + +(module $Ng + (global $x (import "Mg" "glob") i32) + (global $mut_glob (import "Mg" "mut_glob") (mut i32)) + (func $f (import "Mg" "get") (result i32)) + (func $get_mut (import "Mg" "get_mut") (result i32)) + (func $set_mut (import "Mg" "set_mut") (param i32)) + + (export "Mg.glob" (global $x)) + (export "Mg.get" (func $f)) + (global $glob (export "glob") i32 (i32.const 43)) + (func (export "get") (result i32) (global.get $glob)) + + (export "Mg.mut_glob" (global $mut_glob)) + (export "Mg.get_mut" (func $get_mut)) + (export "Mg.set_mut" (func $set_mut)) +) + +(assert_return (get $Mg "glob") (i32.const 42)) +(assert_return (get $Ng "Mg.glob") (i32.const 42)) +(assert_return (get $Ng "glob") (i32.const 43)) +(assert_return (invoke $Mg "get") (i32.const 42)) +(assert_return (invoke $Ng "Mg.get") (i32.const 42)) +(assert_return (invoke $Ng "get") (i32.const 43)) + +(assert_return (get $Mg "mut_glob") (i32.const 142)) +(assert_return (get $Ng "Mg.mut_glob") (i32.const 142)) +(assert_return (invoke $Mg "get_mut") (i32.const 142)) +(assert_return (invoke $Ng "Mg.get_mut") (i32.const 142)) + +(assert_return (invoke $Mg "set_mut" (i32.const 241))) +(assert_return (get $Mg "mut_glob") (i32.const 241)) +(assert_return (get $Ng "Mg.mut_glob") (i32.const 241)) +(assert_return (invoke $Mg "get_mut") (i32.const 241)) +(assert_return (invoke $Ng "Mg.get_mut") (i32.const 241)) + + +(assert_unlinkable + (module (import "Mg" "mut_glob" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "Mg" "glob" (global (mut i32)))) + "incompatible import type" +) + + +(module $Mref_ex + (type $t (func)) + (func $f) (elem declare func $f) + (global (export "g-const-funcnull") (ref null func) (ref.null func)) + (global (export "g-const-func") (ref func) (ref.func $f)) + (global (export "g-const-refnull") (ref null $t) (ref.null $t)) + (global (export "g-const-ref") (ref $t) (ref.func $f)) + (global (export "g-const-extern") externref (ref.null extern)) + (global (export "g-var-funcnull") (mut (ref null func)) (ref.null func)) + (global (export "g-var-func") (mut (ref func)) (ref.func $f)) + (global (export "g-var-refnull") (mut (ref null $t)) (ref.null $t)) + (global (export "g-var-ref") (mut (ref $t)) (ref.func $f)) + (global (export "g-var-extern") (mut externref) (ref.null extern)) +) +(register "Mref_ex" $Mref_ex) + +(module $Mref_im + (type $t (func)) + (global (import "Mref_ex" "g-const-funcnull") (ref null func)) + (global (import "Mref_ex" "g-const-func") (ref null func)) + (global (import "Mref_ex" "g-const-refnull") (ref null func)) + (global (import "Mref_ex" "g-const-ref") (ref null func)) + (global (import "Mref_ex" "g-const-func") (ref func)) + (global (import "Mref_ex" "g-const-ref") (ref func)) + (global (import "Mref_ex" "g-const-refnull") (ref null $t)) + (global (import "Mref_ex" "g-const-ref") (ref null $t)) + (global (import "Mref_ex" "g-const-ref") (ref $t)) + (global (import "Mref_ex" "g-const-extern") externref) + + (global (import "Mref_ex" "g-var-funcnull") (mut (ref null func))) + (global (import "Mref_ex" "g-var-func") (mut (ref func))) + (global (import "Mref_ex" "g-var-refnull") (mut (ref null $t))) + (global (import "Mref_ex" "g-var-ref") (mut (ref $t))) + (global (import "Mref_ex" "g-var-extern") (mut externref)) +) + +(assert_unlinkable + (module (global (import "Mref_ex" "g-const-extern") (ref null func))) + "incompatible import type" +) + +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-const-funcnull") (ref func))) + "incompatible import type" +) +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-const-refnull") (ref func))) + "incompatible import type" +) +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-const-extern") (ref func))) + "incompatible import type" +) + +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-const-funcnull") (ref null $t))) + "incompatible import type" +) +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-const-func") (ref null $t))) + "incompatible import type" +) +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-const-extern") (ref null $t))) + "incompatible import type" +) + +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-const-funcnull") (ref $t))) + "incompatible import type" +) +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-const-func") (ref $t))) + "incompatible import type" +) +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-const-refnull") (ref $t))) + "incompatible import type" +) +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-const-extern") (ref $t))) + "incompatible import type" +) + +(assert_unlinkable + (module (global (import "Mref_ex" "g-const-funcnull") externref)) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-const-func") externref)) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-const-refnull") externref)) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-const-ref") externref)) + "incompatible import type" +) + + +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-func") (mut (ref null func)))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-refnull") (mut (ref null func)))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-ref") (mut (ref null func)))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-extern") (mut (ref null func)))) + "incompatible import type" +) + +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-funcnull") (mut (ref func)))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-refnull") (mut (ref func)))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-ref") (mut (ref func)))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-extern") (mut (ref func)))) + "incompatible import type" +) + +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-var-funcnull") (mut (ref null $t)))) + "incompatible import type" +) +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-var-func") (mut (ref null $t)))) + "incompatible import type" +) +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-var-ref") (mut (ref null $t)))) + "incompatible import type" +) +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-var-extern") (mut (ref null $t)))) + "incompatible import type" +) + +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-var-funcnull") (mut (ref $t)))) + "incompatible import type" +) +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-var-func") (mut (ref $t)))) + "incompatible import type" +) +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-var-refnull") (mut (ref $t)))) + "incompatible import type" +) +(assert_unlinkable + (module (type $t (func)) (global (import "Mref_ex" "g-var-extern") (mut (ref $t)))) + "incompatible import type" +) + +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-funcnull") (mut externref))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-func") (mut externref))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-refnull") (mut externref))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-ref") (mut externref))) + "incompatible import type" +) + + +;; Tables + +(module $Mt + (type (func (result i32))) + (type (func)) + + (table (export "tab") 10 funcref) + (elem (i32.const 2) $g $g $g $g) + (func $g (result i32) (i32.const 4)) + (func (export "h") (result i32) (i32.const -4)) + + (func (export "call") (param i32) (result i32) + (call_indirect (type 0) (local.get 0)) + ) +) +(register "Mt" $Mt) + +(module $Nt + (type (func)) + (type (func (result i32))) + + (func $f (import "Mt" "call") (param i32) (result i32)) + (func $h (import "Mt" "h") (result i32)) + + (table funcref (elem $g $g $g $h $f)) + (func $g (result i32) (i32.const 5)) + + (export "Mt.call" (func $f)) + (func (export "call Mt.call") (param i32) (result i32) + (call $f (local.get 0)) + ) + (func (export "call") (param i32) (result i32) + (call_indirect (type 1) (local.get 0)) + ) +) + +(assert_return (invoke $Mt "call" (i32.const 2)) (i32.const 4)) +(assert_return (invoke $Nt "Mt.call" (i32.const 2)) (i32.const 4)) +(assert_return (invoke $Nt "call" (i32.const 2)) (i32.const 5)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 2)) (i32.const 4)) + +(assert_trap (invoke $Mt "call" (i32.const 1)) "uninitialized element") +(assert_trap (invoke $Nt "Mt.call" (i32.const 1)) "uninitialized element") +(assert_return (invoke $Nt "call" (i32.const 1)) (i32.const 5)) +(assert_trap (invoke $Nt "call Mt.call" (i32.const 1)) "uninitialized element") + +(assert_trap (invoke $Mt "call" (i32.const 0)) "uninitialized element") +(assert_trap (invoke $Nt "Mt.call" (i32.const 0)) "uninitialized element") +(assert_return (invoke $Nt "call" (i32.const 0)) (i32.const 5)) +(assert_trap (invoke $Nt "call Mt.call" (i32.const 0)) "uninitialized element") + +(assert_trap (invoke $Mt "call" (i32.const 20)) "undefined element") +(assert_trap (invoke $Nt "Mt.call" (i32.const 20)) "undefined element") +(assert_trap (invoke $Nt "call" (i32.const 7)) "undefined element") +(assert_trap (invoke $Nt "call Mt.call" (i32.const 20)) "undefined element") + +(assert_return (invoke $Nt "call" (i32.const 3)) (i32.const -4)) +(assert_trap (invoke $Nt "call" (i32.const 4)) "indirect call type mismatch") + +(module $Ot + (type (func (result i32))) + + (func $h (import "Mt" "h") (result i32)) + (table (import "Mt" "tab") 5 funcref) + (elem (i32.const 1) $i $h) + (func $i (result i32) (i32.const 6)) + + (func (export "call") (param i32) (result i32) + (call_indirect (type 0) (local.get 0)) + ) +) + +(assert_return (invoke $Mt "call" (i32.const 3)) (i32.const 4)) +(assert_return (invoke $Nt "Mt.call" (i32.const 3)) (i32.const 4)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 3)) (i32.const 4)) +(assert_return (invoke $Ot "call" (i32.const 3)) (i32.const 4)) + +(assert_return (invoke $Mt "call" (i32.const 2)) (i32.const -4)) +(assert_return (invoke $Nt "Mt.call" (i32.const 2)) (i32.const -4)) +(assert_return (invoke $Nt "call" (i32.const 2)) (i32.const 5)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 2)) (i32.const -4)) +(assert_return (invoke $Ot "call" (i32.const 2)) (i32.const -4)) + +(assert_return (invoke $Mt "call" (i32.const 1)) (i32.const 6)) +(assert_return (invoke $Nt "Mt.call" (i32.const 1)) (i32.const 6)) +(assert_return (invoke $Nt "call" (i32.const 1)) (i32.const 5)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 1)) (i32.const 6)) +(assert_return (invoke $Ot "call" (i32.const 1)) (i32.const 6)) + +(assert_trap (invoke $Mt "call" (i32.const 0)) "uninitialized element") +(assert_trap (invoke $Nt "Mt.call" (i32.const 0)) "uninitialized element") +(assert_return (invoke $Nt "call" (i32.const 0)) (i32.const 5)) +(assert_trap (invoke $Nt "call Mt.call" (i32.const 0)) "uninitialized element") +(assert_trap (invoke $Ot "call" (i32.const 0)) "uninitialized element") + +(assert_trap (invoke $Ot "call" (i32.const 20)) "undefined element") + +(module + (table (import "Mt" "tab") 0 funcref) + (elem (i32.const 9) $f) + (func $f) +) + +(module $G1 (global (export "g") i32 (i32.const 5))) +(register "G1" $G1) +(module $G2 + (global (import "G1" "g") i32) + (global (export "g") i32 (global.get 0)) +) +(assert_return (get $G2 "g") (i32.const 5)) + +(assert_trap + (module + (table (import "Mt" "tab") 0 funcref) + (elem (i32.const 10) $f) + (func $f) + ) + "out of bounds table access" +) + +(assert_unlinkable + (module + (table (import "Mt" "tab") 10 funcref) + (memory (import "Mt" "mem") 1) ;; does not exist + (func $f (result i32) (i32.const 0)) + (elem (i32.const 7) $f) + (elem (i32.const 9) $f) + ) + "unknown import" +) +(assert_trap (invoke $Mt "call" (i32.const 7)) "uninitialized element") + +;; Unlike in the v1 spec, active element segments stored before an +;; out-of-bounds access persist after the instantiation failure. +(assert_trap + (module + (table (import "Mt" "tab") 10 funcref) + (func $f (result i32) (i32.const 0)) + (elem (i32.const 7) $f) + (elem (i32.const 8) $f $f $f $f $f) ;; (partially) out of bounds + ) + "out of bounds table access" +) +(assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0)) +(assert_trap (invoke $Mt "call" (i32.const 8)) "uninitialized element") + +(assert_trap + (module + (table (import "Mt" "tab") 10 funcref) + (func $f (result i32) (i32.const 0)) + (elem (i32.const 7) $f) + (memory 1) + (data (i32.const 0x10000) "d") ;; out of bounds + ) + "out of bounds memory access" +) +(assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0)) + + +(module $Mtable_ex + (type $t (func)) + (table (export "t-funcnull") 1 (ref null func)) + (table (export "t-refnull") 1 (ref null $t)) + (table (export "t-extern") 1 externref) +) +(register "Mtable_ex" $Mtable_ex) + +(module + (type $t (func)) + (table (import "Mtable_ex" "t-funcnull") 1 (ref null func)) + (table (import "Mtable_ex" "t-refnull") 1 (ref null $t)) + (table (import "Mtable_ex" "t-extern") 1 externref) +) + +(assert_unlinkable + (module (table (import "Mtable_ex" "t-refnull") 1 (ref null func))) + "incompatible import type" +) +(assert_unlinkable + (module (table (import "Mtable_ex" "t-extern") 1 (ref null func))) + "incompatible import type" +) + +(assert_unlinkable + (module (type $t (func)) (table (import "Mtable_ex" "t-funcnull") 1 (ref null $t))) + "incompatible import type" +) +(assert_unlinkable + (module (type $t (func)) (table (import "Mtable_ex" "t-extern") 1 (ref null $t))) + "incompatible import type" +) + +(assert_unlinkable + (module (table (import "Mtable_ex" "t-funcnull") 1 externref)) + "incompatible import type" +) +(assert_unlinkable + (module (table (import "Mtable_ex" "t-refnull") 1 externref)) + "incompatible import type" +) + + +;; Memories + +(module $Mm + (memory (export "mem") 1 5) + (data (i32.const 10) "\00\01\02\03\04\05\06\07\08\09") + + (func (export "load") (param $a i32) (result i32) + (i32.load8_u (local.get 0)) + ) +) +(register "Mm" $Mm) + +(module $Nm + (func $loadM (import "Mm" "load") (param i32) (result i32)) + + (memory 1) + (data (i32.const 10) "\f0\f1\f2\f3\f4\f5") + + (export "Mm.load" (func $loadM)) + (func (export "load") (param $a i32) (result i32) + (i32.load8_u (local.get 0)) + ) +) + +(assert_return (invoke $Mm "load" (i32.const 12)) (i32.const 2)) +(assert_return (invoke $Nm "Mm.load" (i32.const 12)) (i32.const 2)) +(assert_return (invoke $Nm "load" (i32.const 12)) (i32.const 0xf2)) + +(module $Om + (memory (import "Mm" "mem") 1) + (data (i32.const 5) "\a0\a1\a2\a3\a4\a5\a6\a7") + + (func (export "load") (param $a i32) (result i32) + (i32.load8_u (local.get 0)) + ) +) + +(assert_return (invoke $Mm "load" (i32.const 12)) (i32.const 0xa7)) +(assert_return (invoke $Nm "Mm.load" (i32.const 12)) (i32.const 0xa7)) +(assert_return (invoke $Nm "load" (i32.const 12)) (i32.const 0xf2)) +(assert_return (invoke $Om "load" (i32.const 12)) (i32.const 0xa7)) + +(module + (memory (import "Mm" "mem") 0) + (data (i32.const 0xffff) "a") +) + +(assert_trap + (module + (memory (import "Mm" "mem") 0) + (data (i32.const 0x10000) "a") + ) + "out of bounds memory access" +) + +(module $Pm + (memory (import "Mm" "mem") 1 8) + + (func (export "grow") (param $a i32) (result i32) + (memory.grow (local.get 0)) + ) +) + +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 1)) +(assert_return (invoke $Pm "grow" (i32.const 2)) (i32.const 1)) +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 3)) +(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const 3)) +(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const 4)) +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5)) +(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const -1)) +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5)) + +(assert_unlinkable + (module + (func $host (import "spectest" "print")) + (memory (import "Mm" "mem") 1) + (table (import "Mm" "tab") 0 funcref) ;; does not exist + (data (i32.const 0) "abc") + ) + "unknown import" +) +(assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 0)) + +;; Unlike in v1 spec, active data segments written before an +;; out-of-bounds access persist after the instantiation failure. +(assert_trap + (module + ;; Note: the memory is 5 pages large by the time we get here. + (memory (import "Mm" "mem") 1) + (data (i32.const 0) "abc") + (data (i32.const 327670) "zzzzzzzzzzzzzzzzzz") ;; (partially) out of bounds + ) + "out of bounds memory access" +) +(assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 97)) +(assert_return (invoke $Mm "load" (i32.const 327670)) (i32.const 0)) + +(assert_trap + (module + (memory (import "Mm" "mem") 1) + (data (i32.const 0) "abc") + (table 0 funcref) + (func) + (elem (i32.const 0) 0) ;; out of bounds + ) + "out of bounds table access" +) +(assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 97)) + +;; Store is modified if the start function traps. +(module $Ms + (type $t (func (result i32))) + (memory (export "memory") 1) + (table (export "table") 1 funcref) + (func (export "get memory[0]") (type $t) + (i32.load8_u (i32.const 0)) + ) + (func (export "get table[0]") (type $t) + (call_indirect (type $t) (i32.const 0)) + ) +) +(register "Ms" $Ms) + +(assert_trap + (module + (import "Ms" "memory" (memory 1)) + (import "Ms" "table" (table 1 funcref)) + (data (i32.const 0) "hello") + (elem (i32.const 0) $f) + (func $f (result i32) + (i32.const 0xdead) + ) + (func $main + (unreachable) + ) + (start $main) + ) + "unreachable" +) + +(assert_return (invoke $Ms "get memory[0]") (i32.const 104)) ;; 'h' +(assert_return (invoke $Ms "get table[0]") (i32.const 0xdead)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/ref.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref.wast new file mode 100644 index 000000000..aef1b392c --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref.wast @@ -0,0 +1,80 @@ +;; Syntax + +(module + (type $t (func)) + + (func + (param + funcref + externref + (ref func) + (ref extern) + (ref 0) + (ref $t) + (ref 0) + (ref $t) + (ref null func) + (ref null extern) + (ref null 0) + (ref null $t) + ) + ) +) + + +;; Undefined type index. + +(assert_invalid + (module (type $type-func-param-invalid (func (param (ref 1))))) + "unknown type" +) +(assert_invalid + (module (type $type-func-result-invalid (func (result (ref 1))))) + "unknown type" +) + +(assert_invalid + (module (global $global-invalid (ref null 1) (ref.null 1))) + "unknown type" +) + +(assert_invalid + (module (table $table-invalid 10 (ref null 1))) + "unknown type" +) + +(assert_invalid + (module (elem $elem-invalid (ref 1))) + "unknown type" +) + +(assert_invalid + (module (func $func-param-invalid (param (ref 1)))) + "unknown type" +) +(assert_invalid + (module (func $func-result-invalid (result (ref 1)))) + "unknown type" +) +(assert_invalid + (module (func $func-local-invalid (local (ref null 1)))) + "unknown type" +) + +(assert_invalid + (module (func $block-result-invalid (drop (block (result (ref 1)) (unreachable))))) + "unknown type" +) +(assert_invalid + (module (func $loop-result-invalid (drop (loop (result (ref 1)) (unreachable))))) + "unknown type" +) +(assert_invalid + (module (func $if-invalid (drop (if (result (ref 1)) (then) (else))))) + "unknown type" +) + +(assert_invalid + (module (func $select-result-invalid (drop (select (result (ref 1)) (unreachable))))) + "unknown type" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_as.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_as.wast new file mode 100644 index 000000000..47064c328 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_as.wast @@ -0,0 +1,67 @@ +(module + (type $ft (func)) + (type $st (struct)) + (type $at (array i8)) + + (table 10 anyref) + + (elem declare func $f) + (func $f) + + (func (export "init") (param $x externref) + (table.set (i32.const 0) (ref.null any)) + (table.set (i32.const 1) (i31.new (i32.const 7))) + (table.set (i32.const 2) (struct.new_default $st (rtt.canon $st))) + (table.set (i32.const 3) (array.new_default $at (i32.const 0) (rtt.canon $at))) + (table.set (i32.const 4) (ref.func $f)) + (table.set (i32.const 5) (rtt.canon $ft)) + (table.set (i32.const 6) (local.get $x)) + ) + + (func (export "ref_as_non_null") (param $i i32) + (drop (ref.as_non_null (table.get (local.get $i)))) + ) + (func (export "ref_as_i31") (param $i i32) + (drop (ref.as_i31 (table.get (local.get $i)))) + ) + (func (export "ref_as_data") (param $i i32) + (drop (ref.as_data (table.get (local.get $i)))) + ) + (func (export "ref_as_func") (param $i i32) + (drop (ref.as_func (table.get (local.get $i)))) + ) +) + +(invoke "init" (ref.extern 0)) + +(assert_trap (invoke "ref_as_non_null" (i32.const 0)) "null reference") +(assert_return (invoke "ref_as_non_null" (i32.const 1))) +(assert_return (invoke "ref_as_non_null" (i32.const 2))) +(assert_return (invoke "ref_as_non_null" (i32.const 3))) +(assert_return (invoke "ref_as_non_null" (i32.const 4))) +(assert_return (invoke "ref_as_non_null" (i32.const 5))) +(assert_return (invoke "ref_as_non_null" (i32.const 6))) + +(assert_trap (invoke "ref_as_i31" (i32.const 0)) "cast failure") +(assert_return (invoke "ref_as_i31" (i32.const 1))) +(assert_trap (invoke "ref_as_i31" (i32.const 2)) "cast failure") +(assert_trap (invoke "ref_as_i31" (i32.const 3)) "cast failure") +(assert_trap (invoke "ref_as_i31" (i32.const 4)) "cast failure") +(assert_trap (invoke "ref_as_i31" (i32.const 5)) "cast failure") +(assert_trap (invoke "ref_as_i31" (i32.const 6)) "cast failure") + +(assert_trap (invoke "ref_as_data" (i32.const 0)) "cast failure") +(assert_trap (invoke "ref_as_data" (i32.const 1)) "cast failure") +(assert_return (invoke "ref_as_data" (i32.const 2))) +(assert_return (invoke "ref_as_data" (i32.const 3))) +(assert_trap (invoke "ref_as_data" (i32.const 4)) "cast failure") +(assert_trap (invoke "ref_as_data" (i32.const 5)) "cast failure") +(assert_trap (invoke "ref_as_data" (i32.const 6)) "cast failure") + +(assert_trap (invoke "ref_as_func" (i32.const 0)) "cast failure") +(assert_trap (invoke "ref_as_func" (i32.const 1)) "cast failure") +(assert_trap (invoke "ref_as_func" (i32.const 2)) "cast failure") +(assert_trap (invoke "ref_as_func" (i32.const 3)) "cast failure") +(assert_return (invoke "ref_as_func" (i32.const 4))) +(assert_trap (invoke "ref_as_func" (i32.const 5)) "cast failure") +(assert_trap (invoke "ref_as_func" (i32.const 6)) "cast failure") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_as_non_null.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_as_non_null.wast new file mode 100644 index 000000000..ba5c45470 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_as_non_null.wast @@ -0,0 +1,46 @@ +(module + (type $t (func (result i32))) + + (func $nn (param $r (ref $t)) (result i32) + (call_ref (ref.as_non_null (local.get $r))) + ) + (func $n (param $r (ref null $t)) (result i32) + (call_ref (ref.as_non_null (local.get $r))) + ) + + (elem func $f) + (func $f (result i32) (i32.const 7)) + + (func (export "nullable-null") (result i32) (call $n (ref.null $t))) + (func (export "nonnullable-f") (result i32) (call $nn (ref.func $f))) + (func (export "nullable-f") (result i32) (call $n (ref.func $f))) + + (func (export "unreachable") (result i32) + (unreachable) + (ref.as_non_null) + (call $nn) + ) +) + +(assert_trap (invoke "unreachable") "unreachable") + +(assert_trap (invoke "nullable-null") "null reference") +(assert_return (invoke "nonnullable-f") (i32.const 7)) +(assert_return (invoke "nullable-f") (i32.const 7)) + +(assert_invalid + (module + (type $t (func (result i32))) + (func $g (param $r (ref $t)) (drop (ref.as_non_null (local.get $r)))) + (func (call $g (ref.null $t))) + ) + "type mismatch" +) + + +(module + (type $t (func)) + (func (param $r (ref $t)) (drop (ref.as_non_null (local.get $r)))) + (func (param $r (ref func)) (drop (ref.as_non_null (local.get $r)))) + (func (param $r (ref extern)) (drop (ref.as_non_null (local.get $r)))) +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_cast.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_cast.wast new file mode 100644 index 000000000..9a74a2800 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_cast.wast @@ -0,0 +1,81 @@ +(module + (type $t0 (struct)) + (type $t1 (struct (field i32))) + (type $t1' (struct (field i32))) + (type $t2 (struct (field i32 i32))) + (type $t2' (struct (field i32 i32))) + (type $t3 (struct (field i32 i32))) + + (global $t0 (rtt $t0) (rtt.canon $t0)) + (global $t0' (rtt $t0) (rtt.canon $t0)) + (global $t1 (rtt $t1) (rtt.sub $t1 (global.get $t0))) + (global $t1' (rtt $t1') (rtt.sub $t1' (global.get $t0))) + (global $t2 (rtt $t2) (rtt.sub $t2 (global.get $t1))) + (global $t2' (rtt $t2') (rtt.sub $t2' (global.get $t1'))) + (global $t3 (rtt $t3) (rtt.sub $t3 (global.get $t0))) + (global $t4 (rtt $t3) (rtt.sub $t3 (rtt.sub $t0 (global.get $t0)))) + + (table 20 dataref) + + (func $init + (table.set (i32.const 0) (struct.new_default $t0 (global.get $t0))) + (table.set (i32.const 10) (struct.new_default $t0 (global.get $t0'))) + (table.set (i32.const 1) (struct.new_default $t1 (global.get $t1))) + (table.set (i32.const 11) (struct.new_default $t1' (global.get $t1'))) + (table.set (i32.const 2) (struct.new_default $t2 (global.get $t2))) + (table.set (i32.const 12) (struct.new_default $t2' (global.get $t2'))) + (table.set (i32.const 3) (struct.new_default $t3 (global.get $t3))) + (table.set (i32.const 4) (struct.new_default $t3 (global.get $t4))) + ) + + (func (export "test-sub") + (call $init) + + (drop (ref.cast (ref.null data) (global.get $t0))) + (drop (ref.cast (table.get (i32.const 0)) (global.get $t0))) + (drop (ref.cast (table.get (i32.const 1)) (global.get $t0))) + (drop (ref.cast (table.get (i32.const 2)) (global.get $t0))) + (drop (ref.cast (table.get (i32.const 3)) (global.get $t0))) + (drop (ref.cast (table.get (i32.const 4)) (global.get $t0))) + + (drop (ref.cast (ref.null data) (global.get $t0))) + (drop (ref.cast (table.get (i32.const 1)) (global.get $t1))) + (drop (ref.cast (table.get (i32.const 2)) (global.get $t1))) + + (drop (ref.cast (ref.null data) (global.get $t0))) + (drop (ref.cast (table.get (i32.const 2)) (global.get $t2))) + + (drop (ref.cast (ref.null data) (global.get $t0))) + (drop (ref.cast (table.get (i32.const 3)) (global.get $t3))) + + (drop (ref.cast (ref.null data) (global.get $t0))) + (drop (ref.cast (table.get (i32.const 4)) (global.get $t4))) + ) + + (func (export "test-canon") + (call $init) + + (drop (ref.cast (table.get (i32.const 0)) (global.get $t0'))) + (drop (ref.cast (table.get (i32.const 1)) (global.get $t0'))) + (drop (ref.cast (table.get (i32.const 2)) (global.get $t0'))) + (drop (ref.cast (table.get (i32.const 3)) (global.get $t0'))) + (drop (ref.cast (table.get (i32.const 4)) (global.get $t0'))) + + (drop (ref.cast (table.get (i32.const 10)) (global.get $t0))) + (drop (ref.cast (table.get (i32.const 11)) (global.get $t0))) + (drop (ref.cast (table.get (i32.const 12)) (global.get $t0))) + + (drop (ref.cast (table.get (i32.const 1)) (global.get $t1'))) + (drop (ref.cast (table.get (i32.const 2)) (global.get $t1'))) + + (drop (ref.cast (table.get (i32.const 11)) (global.get $t1))) + (drop (ref.cast (table.get (i32.const 12)) (global.get $t1))) + + (drop (ref.cast (table.get (i32.const 2)) (global.get $t2'))) + + (drop (ref.cast (table.get (i32.const 12)) (global.get $t2))) + ) +) + +(invoke "test-sub") +(invoke "test-canon") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_eq.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_eq.wast new file mode 100644 index 000000000..5542f264a --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_eq.wast @@ -0,0 +1,353 @@ +(module + (type $st (struct)) + (type $st' (struct (field i32))) + (type $at (array i8)) + + (table 20 (ref null eq)) + + (func (export "init") + (table.set (i32.const 0) (ref.null eq)) + (table.set (i32.const 1) (ref.null i31)) + (table.set (i32.const 2) (i31.new (i32.const 7))) + (table.set (i32.const 3) (i31.new (i32.const 7))) + (table.set (i32.const 4) (i31.new (i32.const 8))) + (table.set (i32.const 5) (struct.new_default $st (rtt.canon $st))) + (table.set (i32.const 6) (struct.new_default $st (rtt.canon $st))) + (table.set (i32.const 7) (array.new_default $at (i32.const 0) (rtt.canon $at))) + (table.set (i32.const 8) (array.new_default $at (i32.const 0) (rtt.canon $at))) + (table.set (i32.const 9) (rtt.canon $st)) + (table.set (i32.const 10) (rtt.canon $st)) + (table.set (i32.const 11) (rtt.canon $at)) + (table.set (i32.const 12) (rtt.sub $st (rtt.canon $st))) + (table.set (i32.const 13) (rtt.sub $st (rtt.canon $st))) + (table.set (i32.const 14) (rtt.sub $st' (rtt.canon $st))) + (table.set (i32.const 15) (rtt.sub $st' (rtt.canon $st))) + ) + + (func (export "eq") (param $i i32) (param $j i32) (result i32) + (ref.eq (table.get (local.get $i)) (table.get (local.get $j))) + ) +) + +(invoke "init") + +(assert_return (invoke "eq" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 8)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 9)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 10)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 11)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 12)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 13)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 14)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 15)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 8)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 9)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 10)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 11)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 12)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 13)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 14)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 15)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 2) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 2)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 3)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 8)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 9)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 10)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 11)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 12)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 13)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 14)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 2) (i32.const 15)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 3) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 2)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 3)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 8)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 9)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 10)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 11)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 12)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 13)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 14)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 3) (i32.const 15)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 4) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 4)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 8)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 9)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 10)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 11)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 12)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 13)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 14)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 4) (i32.const 15)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 5) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 5)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 8)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 9)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 10)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 11)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 12)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 13)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 14)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 5) (i32.const 15)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 6) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 6)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 8)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 9)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 10)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 11)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 12)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 13)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 14)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 6) (i32.const 15)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 7) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 7)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 8)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 9)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 10)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 11)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 12)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 13)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 14)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 7) (i32.const 15)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 8) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 8)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 9)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 10)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 11)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 12)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 13)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 14)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 8) (i32.const 15)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 9) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 9) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 9) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 9) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 9) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 9) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 9) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 9) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 9) (i32.const 8)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 9) (i32.const 9)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 9) (i32.const 10)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 9) (i32.const 11)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 9) (i32.const 12)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 9) (i32.const 13)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 9) (i32.const 14)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 9) (i32.const 15)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 10) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 10) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 10) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 10) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 10) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 10) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 10) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 10) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 10) (i32.const 8)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 10) (i32.const 9)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 10) (i32.const 10)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 10) (i32.const 11)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 10) (i32.const 12)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 10) (i32.const 13)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 10) (i32.const 14)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 10) (i32.const 15)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 11) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 11) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 11) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 11) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 11) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 11) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 11) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 11) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 11) (i32.const 8)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 11) (i32.const 9)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 11) (i32.const 10)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 11) (i32.const 11)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 11) (i32.const 12)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 11) (i32.const 13)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 11) (i32.const 14)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 11) (i32.const 15)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 12) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 12) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 12) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 12) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 12) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 12) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 12) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 12) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 12) (i32.const 8)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 12) (i32.const 9)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 12) (i32.const 10)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 12) (i32.const 11)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 12) (i32.const 12)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 12) (i32.const 13)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 12) (i32.const 14)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 12) (i32.const 15)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 13) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 13) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 13) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 13) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 13) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 13) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 13) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 13) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 13) (i32.const 8)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 13) (i32.const 9)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 13) (i32.const 10)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 13) (i32.const 11)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 13) (i32.const 12)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 13) (i32.const 13)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 13) (i32.const 14)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 13) (i32.const 15)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 14) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 14) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 14) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 14) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 14) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 14) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 14) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 14) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 14) (i32.const 8)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 14) (i32.const 9)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 14) (i32.const 10)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 14) (i32.const 11)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 14) (i32.const 12)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 14) (i32.const 13)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 14) (i32.const 14)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 14) (i32.const 15)) (i32.const 1)) + +(assert_return (invoke "eq" (i32.const 15) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 15) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 15) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 15) (i32.const 3)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 15) (i32.const 4)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 15) (i32.const 5)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 15) (i32.const 6)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 15) (i32.const 7)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 15) (i32.const 8)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 15) (i32.const 9)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 15) (i32.const 10)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 15) (i32.const 11)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 15) (i32.const 12)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 15) (i32.const 13)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 15) (i32.const 14)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 15) (i32.const 15)) (i32.const 1)) + +(assert_invalid + (module + (func (export "eq") (param $r (ref any)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (export "eq") (param $r (ref null any)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (export "eq") (param $r (ref func)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (export "eq") (param $r (ref null func)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (export "eq") (param $r (ref extern)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func (export "eq") (param $r (ref null extern)) (result i32) + (ref.eq (local.get $r) (local.get $r)) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_is.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_is.wast new file mode 100644 index 000000000..bfdc728e0 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_is.wast @@ -0,0 +1,67 @@ +(module + (type $ft (func)) + (type $st (struct)) + (type $at (array i8)) + + (table 10 anyref) + + (elem declare func $f) + (func $f) + + (func (export "init") (param $x externref) + (table.set (i32.const 0) (ref.null any)) + (table.set (i32.const 1) (i31.new (i32.const 7))) + (table.set (i32.const 2) (struct.new_default $st (rtt.canon $st))) + (table.set (i32.const 3) (array.new_default $at (i32.const 0) (rtt.canon $at))) + (table.set (i32.const 4) (ref.func $f)) + (table.set (i32.const 5) (rtt.canon $ft)) + (table.set (i32.const 6) (local.get $x)) + ) + + (func (export "ref_is_null") (param $i i32) (result i32) + (ref.is_null (table.get (local.get $i))) + ) + (func (export "ref_is_i31") (param $i i32) (result i32) + (ref.is_i31 (table.get (local.get $i))) + ) + (func (export "ref_is_data") (param $i i32) (result i32) + (ref.is_data (table.get (local.get $i))) + ) + (func (export "ref_is_func") (param $i i32) (result i32) + (ref.is_func (table.get (local.get $i))) + ) +) + +(invoke "init" (ref.extern 0)) + +(assert_return (invoke "ref_is_null" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref_is_null" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "ref_is_null" (i32.const 2)) (i32.const 0)) +(assert_return (invoke "ref_is_null" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "ref_is_null" (i32.const 4)) (i32.const 0)) +(assert_return (invoke "ref_is_null" (i32.const 5)) (i32.const 0)) +(assert_return (invoke "ref_is_null" (i32.const 6)) (i32.const 0)) + +(assert_return (invoke "ref_is_i31" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "ref_is_i31" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ref_is_i31" (i32.const 2)) (i32.const 0)) +(assert_return (invoke "ref_is_i31" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "ref_is_i31" (i32.const 4)) (i32.const 0)) +(assert_return (invoke "ref_is_i31" (i32.const 5)) (i32.const 0)) +(assert_return (invoke "ref_is_i31" (i32.const 6)) (i32.const 0)) + +(assert_return (invoke "ref_is_data" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "ref_is_data" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "ref_is_data" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "ref_is_data" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "ref_is_data" (i32.const 4)) (i32.const 0)) +(assert_return (invoke "ref_is_data" (i32.const 5)) (i32.const 0)) +(assert_return (invoke "ref_is_data" (i32.const 6)) (i32.const 0)) + +(assert_return (invoke "ref_is_func" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "ref_is_func" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "ref_is_func" (i32.const 2)) (i32.const 0)) +(assert_return (invoke "ref_is_func" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "ref_is_func" (i32.const 4)) (i32.const 1)) +(assert_return (invoke "ref_is_func" (i32.const 5)) (i32.const 0)) +(assert_return (invoke "ref_is_func" (i32.const 6)) (i32.const 0)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_is_null.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_is_null.wast new file mode 100644 index 000000000..730170df4 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_is_null.wast @@ -0,0 +1,85 @@ +(module + (type $t (func)) + (func $dummy) + + (func $f1 (export "funcref") (param $x funcref) (result i32) + (ref.is_null (local.get $x)) + ) + (func $f2 (export "externref") (param $x externref) (result i32) + (ref.is_null (local.get $x)) + ) + (func $f3 (param $x (ref null $t)) (result i32) + (ref.is_null (local.get $x)) + ) + (func $f3' (export "ref-null") (result i32) + (call $f3 (ref.null $t)) + ) + + (table $t1 2 funcref) + (table $t2 2 externref) + (table $t3 2 (ref null $t)) + (elem (table $t1) (i32.const 1) func $dummy) + (elem (table $t3) (i32.const 1) (ref $t) (ref.func $dummy)) + + (func (export "init") (param $r externref) + (table.set $t2 (i32.const 1) (local.get $r)) + ) + (func (export "deinit") + (table.set $t1 (i32.const 1) (ref.null func)) + (table.set $t2 (i32.const 1) (ref.null extern)) + (table.set $t3 (i32.const 1) (ref.null $t)) + ) + + (func (export "funcref-elem") (param $x i32) (result i32) + (call $f1 (table.get $t1 (local.get $x))) + ) + (func (export "externref-elem") (param $x i32) (result i32) + (call $f2 (table.get $t2 (local.get $x))) + ) + (func (export "ref-elem") (param $x i32) (result i32) + (call $f3 (table.get $t3 (local.get $x))) + ) +) + +(assert_return (invoke "funcref" (ref.null func)) (i32.const 1)) +(assert_return (invoke "externref" (ref.null extern)) (i32.const 1)) +(assert_return (invoke "ref-null") (i32.const 1)) + +(assert_return (invoke "externref" (ref.extern 1)) (i32.const 0)) + +(invoke "init" (ref.extern 0)) + +(assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "externref-elem" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref-elem" (i32.const 0)) (i32.const 1)) + +(assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "externref-elem" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "ref-elem" (i32.const 1)) (i32.const 0)) + +(invoke "deinit") + +(assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "externref-elem" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ref-elem" (i32.const 0)) (i32.const 1)) + +(assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "externref-elem" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ref-elem" (i32.const 1)) (i32.const 1)) + + +(module + (type $t (func)) + (func (param $r (ref $t)) (drop (ref.is_null (local.get $r)))) + (func (param $r (ref func)) (drop (ref.is_null (local.get $r)))) + (func (param $r (ref extern)) (drop (ref.is_null (local.get $r)))) +) + +(assert_invalid + (module (func $ref-vs-num (param i32) (ref.is_null (local.get 0)))) + "type mismatch" +) +(assert_invalid + (module (func $ref-vs-empty (ref.is_null))) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_null.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_null.wast new file mode 100644 index 000000000..ab3a8b4ea --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_null.wast @@ -0,0 +1,14 @@ +(module + (type $t (func)) + (func (export "externref") (result externref) (ref.null extern)) + (func (export "funcref") (result funcref) (ref.null func)) + (func (export "ref") (result (ref null $t)) (ref.null $t)) + + (global externref (ref.null extern)) + (global funcref (ref.null func)) + (global (ref null $t) (ref.null $t)) +) + +(assert_return (invoke "externref") (ref.null extern)) +(assert_return (invoke "funcref") (ref.null func)) +(assert_return (invoke "ref") (ref.null)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_test.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_test.wast new file mode 100644 index 000000000..701293957 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/ref_test.wast @@ -0,0 +1,111 @@ +(module + (type $t0 (struct)) + (type $t1 (struct (field i32))) + (type $t1' (struct (field i32))) + (type $t2 (struct (field i32 i32))) + (type $t2' (struct (field i32 i32))) + (type $t3 (struct (field i32 i32))) + + (global $t0 (rtt $t0) (rtt.canon $t0)) + (global $t0' (rtt $t0) (rtt.canon $t0)) + (global $t1 (rtt $t1) (rtt.sub $t1 (global.get $t0))) + (global $t1' (rtt $t1') (rtt.sub $t1' (global.get $t0))) + (global $t2 (rtt $t2) (rtt.sub $t2 (global.get $t1))) + (global $t2' (rtt $t2') (rtt.sub $t2' (global.get $t1'))) + (global $t3 (rtt $t3) (rtt.sub $t3 (global.get $t0))) + (global $t4 (rtt $t3) (rtt.sub $t3 (rtt.sub $t0 (global.get $t0)))) + + (table 20 dataref) + + (func $init + (table.set (i32.const 0) (struct.new_default $t0 (global.get $t0))) + (table.set (i32.const 10) (struct.new_default $t0 (global.get $t0'))) + (table.set (i32.const 1) (struct.new_default $t1 (global.get $t1))) + (table.set (i32.const 11) (struct.new_default $t1' (global.get $t1'))) + (table.set (i32.const 2) (struct.new_default $t2 (global.get $t2))) + (table.set (i32.const 12) (struct.new_default $t2' (global.get $t2'))) + (table.set (i32.const 3) (struct.new_default $t3 (global.get $t3))) + (table.set (i32.const 4) (struct.new_default $t3 (global.get $t4))) + ) + + (func (export "test-sub") + (call $init) + (block $l + ;; must hold + (br_if $l (i32.eqz (ref.test (table.get (i32.const 0)) (global.get $t0)))) + (br_if $l (i32.eqz (ref.test (table.get (i32.const 1)) (global.get $t0)))) + (br_if $l (i32.eqz (ref.test (table.get (i32.const 2)) (global.get $t0)))) + (br_if $l (i32.eqz (ref.test (table.get (i32.const 3)) (global.get $t0)))) + (br_if $l (i32.eqz (ref.test (table.get (i32.const 4)) (global.get $t0)))) + + (br_if $l (i32.eqz (ref.test (table.get (i32.const 1)) (global.get $t1)))) + (br_if $l (i32.eqz (ref.test (table.get (i32.const 2)) (global.get $t1)))) + + (br_if $l (i32.eqz (ref.test (table.get (i32.const 2)) (global.get $t2)))) + + (br_if $l (i32.eqz (ref.test (table.get (i32.const 3)) (global.get $t3)))) + + (br_if $l (i32.eqz (ref.test (table.get (i32.const 4)) (global.get $t4)))) + + ;; must not hold + (br_if $l (ref.test (ref.null data) (global.get $t0))) + (br_if $l (ref.test (ref.null data) (global.get $t1))) + (br_if $l (ref.test (ref.null data) (global.get $t2))) + (br_if $l (ref.test (ref.null data) (global.get $t3))) + (br_if $l (ref.test (ref.null data) (global.get $t4))) + + (br_if $l (ref.test (table.get (i32.const 0)) (global.get $t1))) + (br_if $l (ref.test (table.get (i32.const 3)) (global.get $t1))) + (br_if $l (ref.test (table.get (i32.const 4)) (global.get $t1))) + + (br_if $l (ref.test (table.get (i32.const 0)) (global.get $t2))) + (br_if $l (ref.test (table.get (i32.const 1)) (global.get $t2))) + (br_if $l (ref.test (table.get (i32.const 3)) (global.get $t2))) + (br_if $l (ref.test (table.get (i32.const 4)) (global.get $t2))) + + (br_if $l (ref.test (table.get (i32.const 0)) (global.get $t3))) + (br_if $l (ref.test (table.get (i32.const 1)) (global.get $t3))) + (br_if $l (ref.test (table.get (i32.const 2)) (global.get $t3))) + (br_if $l (ref.test (table.get (i32.const 4)) (global.get $t3))) + + (br_if $l (ref.test (table.get (i32.const 0)) (global.get $t4))) + (br_if $l (ref.test (table.get (i32.const 1)) (global.get $t4))) + (br_if $l (ref.test (table.get (i32.const 2)) (global.get $t4))) + (br_if $l (ref.test (table.get (i32.const 3)) (global.get $t4))) + + (return) + ) + (unreachable) + ) + + (func (export "test-canon") + (call $init) + (block $l + (br_if $l (i32.eqz (ref.test (table.get (i32.const 0)) (global.get $t0')))) + (br_if $l (i32.eqz (ref.test (table.get (i32.const 1)) (global.get $t0')))) + (br_if $l (i32.eqz (ref.test (table.get (i32.const 2)) (global.get $t0')))) + (br_if $l (i32.eqz (ref.test (table.get (i32.const 3)) (global.get $t0')))) + (br_if $l (i32.eqz (ref.test (table.get (i32.const 4)) (global.get $t0')))) + + (br_if $l (i32.eqz (ref.test (table.get (i32.const 10)) (global.get $t0)))) + (br_if $l (i32.eqz (ref.test (table.get (i32.const 11)) (global.get $t0)))) + (br_if $l (i32.eqz (ref.test (table.get (i32.const 12)) (global.get $t0)))) + + (br_if $l (i32.eqz (ref.test (table.get (i32.const 1)) (global.get $t1')))) + (br_if $l (i32.eqz (ref.test (table.get (i32.const 2)) (global.get $t1')))) + + (br_if $l (i32.eqz (ref.test (table.get (i32.const 11)) (global.get $t1)))) + (br_if $l (i32.eqz (ref.test (table.get (i32.const 12)) (global.get $t1)))) + + (br_if $l (i32.eqz (ref.test (table.get (i32.const 2)) (global.get $t2')))) + + (br_if $l (i32.eqz (ref.test (table.get (i32.const 12)) (global.get $t2)))) + + (return) + ) + (unreachable) + ) +) + +(invoke "test-sub") +(invoke "test-canon") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/return_call_ref.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/return_call_ref.wast new file mode 100644 index 000000000..8e7aac821 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/return_call_ref.wast @@ -0,0 +1,360 @@ +;; Test `return_call_ref` operator + +(module + ;; Auxiliary definitions + (type $proc (func)) + (type $-i32 (func (result i32))) + (type $-i64 (func (result i64))) + (type $-f32 (func (result f32))) + (type $-f64 (func (result f64))) + + (type $i32-i32 (func (param i32) (result i32))) + (type $i64-i64 (func (param i64) (result i64))) + (type $f32-f32 (func (param f32) (result f32))) + (type $f64-f64 (func (param f64) (result f64))) + + (type $f32-i32 (func (param f32 i32) (result i32))) + (type $i32-i64 (func (param i32 i64) (result i64))) + (type $f64-f32 (func (param f64 f32) (result f32))) + (type $i64-f64 (func (param i64 f64) (result f64))) + + (type $i64i64-i64 (func (param i64 i64) (result i64))) + + (func $const-i32 (result i32) (i32.const 0x132)) + (func $const-i64 (result i64) (i64.const 0x164)) + (func $const-f32 (result f32) (f32.const 0xf32)) + (func $const-f64 (result f64) (f64.const 0xf64)) + + (func $id-i32 (param i32) (result i32) (local.get 0)) + (func $id-i64 (param i64) (result i64) (local.get 0)) + (func $id-f32 (param f32) (result f32) (local.get 0)) + (func $id-f64 (param f64) (result f64) (local.get 0)) + + (func $f32-i32 (param f32 i32) (result i32) (local.get 1)) + (func $i32-i64 (param i32 i64) (result i64) (local.get 1)) + (func $f64-f32 (param f64 f32) (result f32) (local.get 1)) + (func $i64-f64 (param i64 f64) (result f64) (local.get 1)) + + (global $const-i32 (ref $-i32) (ref.func $const-i32)) + (global $const-i64 (ref $-i64) (ref.func $const-i64)) + (global $const-f32 (ref $-f32) (ref.func $const-f32)) + (global $const-f64 (ref $-f64) (ref.func $const-f64)) + + (global $id-i32 (ref $i32-i32) (ref.func $id-i32)) + (global $id-i64 (ref $i64-i64) (ref.func $id-i64)) + (global $id-f32 (ref $f32-f32) (ref.func $id-f32)) + (global $id-f64 (ref $f64-f64) (ref.func $id-f64)) + + (global $f32-i32 (ref $f32-i32) (ref.func $f32-i32)) + (global $i32-i64 (ref $i32-i64) (ref.func $i32-i64)) + (global $f64-f32 (ref $f64-f32) (ref.func $f64-f32)) + (global $i64-f64 (ref $i64-f64) (ref.func $i64-f64)) + + (elem declare func + $const-i32 $const-i64 $const-f32 $const-f64 + $id-i32 $id-i64 $id-f32 $id-f64 + $f32-i32 $i32-i64 $f64-f32 $i64-f64 + ) + + ;; Typing + + (func (export "type-i32") (result i32) + (return_call_ref (global.get $const-i32)) + ) + (func (export "type-i64") (result i64) + (return_call_ref (global.get $const-i64)) + ) + (func (export "type-f32") (result f32) + (return_call_ref (global.get $const-f32)) + ) + (func (export "type-f64") (result f64) + (return_call_ref (global.get $const-f64)) + ) + + (func (export "type-first-i32") (result i32) + (return_call_ref (i32.const 32) (global.get $id-i32)) + ) + (func (export "type-first-i64") (result i64) + (return_call_ref (i64.const 64) (global.get $id-i64)) + ) + (func (export "type-first-f32") (result f32) + (return_call_ref (f32.const 1.32) (global.get $id-f32)) + ) + (func (export "type-first-f64") (result f64) + (return_call_ref (f64.const 1.64) (global.get $id-f64)) + ) + + (func (export "type-second-i32") (result i32) + (return_call_ref (f32.const 32.1) (i32.const 32) (global.get $f32-i32)) + ) + (func (export "type-second-i64") (result i64) + (return_call_ref (i32.const 32) (i64.const 64) (global.get $i32-i64)) + ) + (func (export "type-second-f32") (result f32) + (return_call_ref (f64.const 64) (f32.const 32) (global.get $f64-f32)) + ) + (func (export "type-second-f64") (result f64) + (return_call_ref (i64.const 64) (f64.const 64.1) (global.get $i64-f64)) + ) + + ;; Null + + (func (export "null") + (return_call_ref (ref.null $proc)) + ) + + ;; Recursion + + (global $fac-acc (ref $i64i64-i64) (ref.func $fac-acc)) + + (elem declare func $fac-acc) + (func $fac-acc (export "fac-acc") (param i64 i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (local.get 1)) + (else + (return_call_ref + (i64.sub (local.get 0) (i64.const 1)) + (i64.mul (local.get 0) (local.get 1)) + (global.get $fac-acc) + ) + ) + ) + ) + + (global $count (ref $i64-i64) (ref.func $count)) + + (elem declare func $count) + (func $count (export "count") (param i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (local.get 0)) + (else + (return_call_ref + (i64.sub (local.get 0) (i64.const 1)) + (global.get $count) + ) + ) + ) + ) + + (global $even (ref $i64-i64) (ref.func $even)) + (global $odd (ref $i64-i64) (ref.func $odd)) + + (elem declare func $even) + (func $even (export "even") (param i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (i64.const 44)) + (else + (return_call_ref + (i64.sub (local.get 0) (i64.const 1)) + (global.get $odd) + ) + ) + ) + ) + (elem declare func $odd) + (func $odd (export "odd") (param i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (i64.const 99)) + (else + (return_call_ref + (i64.sub (local.get 0) (i64.const 1)) + (global.get $even) + ) + ) + ) + ) +) + +(assert_return (invoke "type-i32") (i32.const 0x132)) +(assert_return (invoke "type-i64") (i64.const 0x164)) +(assert_return (invoke "type-f32") (f32.const 0xf32)) +(assert_return (invoke "type-f64") (f64.const 0xf64)) + +(assert_return (invoke "type-first-i32") (i32.const 32)) +(assert_return (invoke "type-first-i64") (i64.const 64)) +(assert_return (invoke "type-first-f32") (f32.const 1.32)) +(assert_return (invoke "type-first-f64") (f64.const 1.64)) + +(assert_return (invoke "type-second-i32") (i32.const 32)) +(assert_return (invoke "type-second-i64") (i64.const 64)) +(assert_return (invoke "type-second-f32") (f32.const 32)) +(assert_return (invoke "type-second-f64") (f64.const 64.1)) + +(assert_trap (invoke "null") "null function") + +(assert_return (invoke "fac-acc" (i64.const 0) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 5) (i64.const 1)) (i64.const 120)) +(assert_return + (invoke "fac-acc" (i64.const 25) (i64.const 1)) + (i64.const 7034535277573963776) +) + +(assert_return (invoke "count" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "count" (i64.const 1000)) (i64.const 0)) +(assert_return (invoke "count" (i64.const 1_000_000)) (i64.const 0)) + +(assert_return (invoke "even" (i64.const 0)) (i64.const 44)) +(assert_return (invoke "even" (i64.const 1)) (i64.const 99)) +(assert_return (invoke "even" (i64.const 100)) (i64.const 44)) +(assert_return (invoke "even" (i64.const 77)) (i64.const 99)) +(assert_return (invoke "even" (i64.const 1_000_000)) (i64.const 44)) +(assert_return (invoke "even" (i64.const 1_000_001)) (i64.const 99)) +(assert_return (invoke "odd" (i64.const 0)) (i64.const 99)) +(assert_return (invoke "odd" (i64.const 1)) (i64.const 44)) +(assert_return (invoke "odd" (i64.const 200)) (i64.const 99)) +(assert_return (invoke "odd" (i64.const 77)) (i64.const 44)) +(assert_return (invoke "odd" (i64.const 1_000_000)) (i64.const 99)) +(assert_return (invoke "odd" (i64.const 999_999)) (i64.const 44)) + + +;; More typing + +(module + (type $t (func)) + (elem declare func $f11 $f22 $f33 $f44) + (func $f11 (result (ref $t)) (return_call_ref (ref.func $f11))) + (func $f21 (result (ref null $t)) (return_call_ref (ref.func $f11))) + (func $f22 (result (ref null $t)) (return_call_ref (ref.func $f22))) + (func $f31 (result (ref func)) (return_call_ref (ref.func $f11))) + (func $f33 (result (ref func)) (return_call_ref (ref.func $f33))) + (func $f41 (result (ref null func)) (return_call_ref (ref.func $f11))) + (func $f42 (result (ref null func)) (return_call_ref (ref.func $f22))) + (func $f43 (result (ref null func)) (return_call_ref (ref.func $f33))) + (func $f44 (result (ref null func)) (return_call_ref (ref.func $f44))) +) + +(assert_invalid + (module + (type $t (func)) + (elem declare func $f22) + (func $f12 (result (ref $t)) (return_call_ref (ref.func $f22))) + (func $f22 (result (ref null $t)) (return_call_ref (ref.func $f22))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $t (func)) + (elem declare func $f33) + (func $f13 (result (ref $t)) (return_call_ref (ref.func $f33))) + (func $f33 (result (ref func)) (return_call_ref (ref.func $f33))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $t (func)) + (elem declare func $f44) + (func $f14 (result (ref $t)) (return_call_ref (ref.func $f44))) + (func $f44 (result (ref null func)) (return_call_ref (ref.func $f44))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $t (func)) + (elem declare func $f33) + (func $f23 (result (ref null $t)) (return_call_ref (ref.func $f33))) + (func $f33 (result (ref func)) (return_call_ref (ref.func $f33))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $t (func)) + (elem declare func $f44) + (func $f24 (result (ref null $t)) (return_call_ref (ref.func $f44))) + (func $f44 (result (ref null func)) (return_call_ref (ref.func $f44))) + ) + "type mismatch" +) + +(assert_invalid + (module + (elem declare func $f44) + (func $f34 (result (ref func)) (return_call_ref (ref.func $f44))) + (func $f44 (result (ref null func)) (return_call_ref (ref.func $f44))) + ) + "type mismatch" +) + + +;; Unreachable typing. + +(module + (func (export "unreachable") (result i32) + (unreachable) + (return_call_ref) + ) +) +(assert_trap (invoke "unreachable") "unreachable") + +(module + (elem declare func $f) + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "unreachable") (result i32) + (unreachable) + (ref.func $f) + (return_call_ref) + ) +) +(assert_trap (invoke "unreachable") "unreachable") + +(module + (elem declare func $f) + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "unreachable") (result i32) + (unreachable) + (i32.const 0) + (ref.func $f) + (return_call_ref) + (i32.const 0) + ) +) +(assert_trap (invoke "unreachable") "unreachable") + +(assert_invalid + (module + (elem declare func $f) + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "unreachable") (result i32) + (unreachable) + (i64.const 0) + (ref.func $f) + (return_call_ref) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (elem declare func $f) + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "unreachable") (result i32) + (unreachable) + (ref.func $f) + (return_call_ref) + (i64.const 0) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $f (param $r externref) + (return_call_ref (local.get $r)) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/select.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/select.wast new file mode 100644 index 000000000..e8130ba9b --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/select.wast @@ -0,0 +1,625 @@ +(module + ;; Auxiliary + (func $dummy) + (table $tab funcref (elem $dummy)) + (memory 1) + + (func (export "select-i32") (param i32 i32 i32) (result i32) + (select (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-i64") (param i64 i64 i32) (result i64) + (select (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-f32") (param f32 f32 i32) (result f32) + (select (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-f64") (param f64 f64 i32) (result f64) + (select (local.get 0) (local.get 1) (local.get 2)) + ) + + (func (export "select-i32-t") (param i32 i32 i32) (result i32) + (select (result i32) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-i64-t") (param i64 i64 i32) (result i64) + (select (result i64) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-f32-t") (param f32 f32 i32) (result f32) + (select (result f32) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-f64-t") (param f64 f64 i32) (result f64) + (select (result f64) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-funcref") (param funcref funcref i32) (result funcref) + (select (result funcref) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-externref") (param externref externref i32) (result externref) + (select (result externref) (local.get 0) (local.get 1) (local.get 2)) + ) + + (type $t (func)) + (func $tf) (elem declare func $tf) + (func (export "join-funcnull") (param i32) (result (ref null func)) + (select (result (ref null func)) + (ref.func $tf) + (ref.null func) + (local.get 0) + ) + ) + + ;; Check that both sides of the select are evaluated + (func (export "select-trap-left") (param $cond i32) (result i32) + (select (unreachable) (i32.const 0) (local.get $cond)) + ) + (func (export "select-trap-right") (param $cond i32) (result i32) + (select (i32.const 0) (unreachable) (local.get $cond)) + ) + + (func (export "select-unreached") + (unreachable) (select) + (unreachable) (i32.const 0) (select) + (unreachable) (i32.const 0) (i32.const 0) (select) + (unreachable) (i32.const 0) (i32.const 0) (i32.const 0) (select) + (unreachable) (f32.const 0) (i32.const 0) (select) + (unreachable) + ) + + (func (export "select_unreached_result_1") (result i32) + (unreachable) (i32.add (select)) + ) + + (func (export "select_unreached_result_2") (result i64) + (unreachable) (i64.add (select (i64.const 0) (i32.const 0))) + ) + + ;; As the argument of control constructs and instructions + + (func (export "as-select-first") (param i32) (result i32) + (select (select (i32.const 0) (i32.const 1) (local.get 0)) (i32.const 2) (i32.const 3)) + ) + (func (export "as-select-mid") (param i32) (result i32) + (select (i32.const 2) (select (i32.const 0) (i32.const 1) (local.get 0)) (i32.const 3)) + ) + (func (export "as-select-last") (param i32) (result i32) + (select (i32.const 2) (i32.const 3) (select (i32.const 0) (i32.const 1) (local.get 0))) + ) + + (func (export "as-loop-first") (param i32) (result i32) + (loop (result i32) (select (i32.const 2) (i32.const 3) (local.get 0)) (call $dummy) (call $dummy)) + ) + (func (export "as-loop-mid") (param i32) (result i32) + (loop (result i32) (call $dummy) (select (i32.const 2) (i32.const 3) (local.get 0)) (call $dummy)) + ) + (func (export "as-loop-last") (param i32) (result i32) + (loop (result i32) (call $dummy) (call $dummy) (select (i32.const 2) (i32.const 3) (local.get 0))) + ) + + (func (export "as-if-condition") (param i32) + (select (i32.const 2) (i32.const 3) (local.get 0)) (if (then (call $dummy))) + ) + (func (export "as-if-then") (param i32) (result i32) + (if (result i32) (i32.const 1) (then (select (i32.const 2) (i32.const 3) (local.get 0))) (else (i32.const 4))) + ) + (func (export "as-if-else") (param i32) (result i32) + (if (result i32) (i32.const 0) (then (i32.const 2)) (else (select (i32.const 2) (i32.const 3) (local.get 0)))) + ) + + (func (export "as-br_if-first") (param i32) (result i32) + (block (result i32) (br_if 0 (select (i32.const 2) (i32.const 3) (local.get 0)) (i32.const 4))) + ) + (func (export "as-br_if-last") (param i32) (result i32) + (block (result i32) (br_if 0 (i32.const 2) (select (i32.const 2) (i32.const 3) (local.get 0)))) + ) + + (func (export "as-br_table-first") (param i32) (result i32) + (block (result i32) (select (i32.const 2) (i32.const 3) (local.get 0)) (i32.const 2) (br_table 0 0)) + ) + (func (export "as-br_table-last") (param i32) (result i32) + (block (result i32) (i32.const 2) (select (i32.const 2) (i32.const 3) (local.get 0)) (br_table 0 0)) + ) + + (func $func (param i32 i32) (result i32) (local.get 0)) + (type $check (func (param i32 i32) (result i32))) + (table $t funcref (elem $func)) + (func (export "as-call_indirect-first") (param i32) (result i32) + (block (result i32) + (call_indirect $t (type $check) + (select (i32.const 2) (i32.const 3) (local.get 0)) (i32.const 1) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-mid") (param i32) (result i32) + (block (result i32) + (call_indirect $t (type $check) + (i32.const 1) (select (i32.const 2) (i32.const 3) (local.get 0)) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-last") (param i32) (result i32) + (block (result i32) + (call_indirect $t (type $check) + (i32.const 1) (i32.const 4) (select (i32.const 2) (i32.const 3) (local.get 0)) + ) + ) + ) + + (func (export "as-store-first") (param i32) + (select (i32.const 0) (i32.const 4) (local.get 0)) (i32.const 1) (i32.store) + ) + (func (export "as-store-last") (param i32) + (i32.const 8) (select (i32.const 1) (i32.const 2) (local.get 0)) (i32.store) + ) + + (func (export "as-memory.grow-value") (param i32) (result i32) + (memory.grow (select (i32.const 1) (i32.const 2) (local.get 0))) + ) + + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "as-call-value") (param i32) (result i32) + (call $f (select (i32.const 1) (i32.const 2) (local.get 0))) + ) + (func (export "as-return-value") (param i32) (result i32) + (select (i32.const 1) (i32.const 2) (local.get 0)) (return) + ) + (func (export "as-drop-operand") (param i32) + (drop (select (i32.const 1) (i32.const 2) (local.get 0))) + ) + (func (export "as-br-value") (param i32) (result i32) + (block (result i32) (br 0 (select (i32.const 1) (i32.const 2) (local.get 0)))) + ) + (func (export "as-local.set-value") (param i32) (result i32) + (local i32) (local.set 0 (select (i32.const 1) (i32.const 2) (local.get 0))) (local.get 0) + ) + (func (export "as-local.tee-value") (param i32) (result i32) + (local.tee 0 (select (i32.const 1) (i32.const 2) (local.get 0))) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (param i32) (result i32) + (global.set $a (select (i32.const 1) (i32.const 2) (local.get 0))) + (global.get $a) + ) + (func (export "as-load-operand") (param i32) (result i32) + (i32.load (select (i32.const 0) (i32.const 4) (local.get 0))) + ) + + (func (export "as-unary-operand") (param i32) (result i32) + (i32.eqz (select (i32.const 0) (i32.const 1) (local.get 0))) + ) + (func (export "as-binary-operand") (param i32) (result i32) + (i32.mul + (select (i32.const 1) (i32.const 2) (local.get 0)) + (select (i32.const 1) (i32.const 2) (local.get 0)) + ) + ) + (func (export "as-test-operand") (param i32) (result i32) + (block (result i32) + (i32.eqz (select (i32.const 0) (i32.const 1) (local.get 0))) + ) + ) + + (func (export "as-compare-left") (param i32) (result i32) + (block (result i32) + (i32.le_s (select (i32.const 1) (i32.const 2) (local.get 0)) (i32.const 1)) + ) + ) + (func (export "as-compare-right") (param i32) (result i32) + (block (result i32) + (i32.ne (i32.const 1) (select (i32.const 0) (i32.const 1) (local.get 0))) + ) + ) + + (func (export "as-convert-operand") (param i32) (result i32) + (block (result i32) + (i32.wrap_i64 (select (i64.const 1) (i64.const 0) (local.get 0))) + ) + ) + + (func (export "unreachable-num") + (unreachable) + (select) + (i32.eqz) + (drop) + ) + (func (export "unreachable-ref") + (unreachable) + (select) + (ref.is_null) + (drop) + ) +) + +(assert_return (invoke "select-i32" (i32.const 1) (i32.const 2) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "select-i64" (i64.const 2) (i64.const 1) (i32.const 1)) (i64.const 2)) +(assert_return (invoke "select-f32" (f32.const 1) (f32.const 2) (i32.const 1)) (f32.const 1)) +(assert_return (invoke "select-f64" (f64.const 1) (f64.const 2) (i32.const 1)) (f64.const 1)) + +(assert_return (invoke "select-i32" (i32.const 1) (i32.const 2) (i32.const 0)) (i32.const 2)) +(assert_return (invoke "select-i32" (i32.const 2) (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "select-i64" (i64.const 2) (i64.const 1) (i32.const -1)) (i64.const 2)) +(assert_return (invoke "select-i64" (i64.const 2) (i64.const 1) (i32.const 0xf0f0f0f0)) (i64.const 2)) + +(assert_return (invoke "select-f32" (f32.const nan) (f32.const 1) (i32.const 1)) (f32.const nan)) +(assert_return (invoke "select-f32" (f32.const nan:0x20304) (f32.const 1) (i32.const 1)) (f32.const nan:0x20304)) +(assert_return (invoke "select-f32" (f32.const nan) (f32.const 1) (i32.const 0)) (f32.const 1)) +(assert_return (invoke "select-f32" (f32.const nan:0x20304) (f32.const 1) (i32.const 0)) (f32.const 1)) +(assert_return (invoke "select-f32" (f32.const 2) (f32.const nan) (i32.const 1)) (f32.const 2)) +(assert_return (invoke "select-f32" (f32.const 2) (f32.const nan:0x20304) (i32.const 1)) (f32.const 2)) +(assert_return (invoke "select-f32" (f32.const 2) (f32.const nan) (i32.const 0)) (f32.const nan)) +(assert_return (invoke "select-f32" (f32.const 2) (f32.const nan:0x20304) (i32.const 0)) (f32.const nan:0x20304)) + +(assert_return (invoke "select-f64" (f64.const nan) (f64.const 1) (i32.const 1)) (f64.const nan)) +(assert_return (invoke "select-f64" (f64.const nan:0x20304) (f64.const 1) (i32.const 1)) (f64.const nan:0x20304)) +(assert_return (invoke "select-f64" (f64.const nan) (f64.const 1) (i32.const 0)) (f64.const 1)) +(assert_return (invoke "select-f64" (f64.const nan:0x20304) (f64.const 1) (i32.const 0)) (f64.const 1)) +(assert_return (invoke "select-f64" (f64.const 2) (f64.const nan) (i32.const 1)) (f64.const 2)) +(assert_return (invoke "select-f64" (f64.const 2) (f64.const nan:0x20304) (i32.const 1)) (f64.const 2)) +(assert_return (invoke "select-f64" (f64.const 2) (f64.const nan) (i32.const 0)) (f64.const nan)) +(assert_return (invoke "select-f64" (f64.const 2) (f64.const nan:0x20304) (i32.const 0)) (f64.const nan:0x20304)) + +(assert_return (invoke "select-i32-t" (i32.const 1) (i32.const 2) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "select-i64-t" (i64.const 2) (i64.const 1) (i32.const 1)) (i64.const 2)) +(assert_return (invoke "select-f32-t" (f32.const 1) (f32.const 2) (i32.const 1)) (f32.const 1)) +(assert_return (invoke "select-f64-t" (f64.const 1) (f64.const 2) (i32.const 1)) (f64.const 1)) +(assert_return (invoke "select-funcref" (ref.null func) (ref.null func) (i32.const 1)) (ref.null func)) +(assert_return (invoke "select-externref" (ref.extern 1) (ref.extern 2) (i32.const 1)) (ref.extern 1)) + +(assert_return (invoke "select-i32-t" (i32.const 1) (i32.const 2) (i32.const 0)) (i32.const 2)) +(assert_return (invoke "select-i32-t" (i32.const 2) (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "select-i64-t" (i64.const 2) (i64.const 1) (i32.const -1)) (i64.const 2)) +(assert_return (invoke "select-i64-t" (i64.const 2) (i64.const 1) (i32.const 0xf0f0f0f0)) (i64.const 2)) +(assert_return (invoke "select-externref" (ref.extern 1) (ref.extern 2) (i32.const 0)) (ref.extern 2)) +(assert_return (invoke "select-externref" (ref.extern 2) (ref.extern 1) (i32.const 0)) (ref.extern 1)) + +(assert_return (invoke "select-f32-t" (f32.const nan) (f32.const 1) (i32.const 1)) (f32.const nan)) +(assert_return (invoke "select-f32-t" (f32.const nan:0x20304) (f32.const 1) (i32.const 1)) (f32.const nan:0x20304)) +(assert_return (invoke "select-f32-t" (f32.const nan) (f32.const 1) (i32.const 0)) (f32.const 1)) +(assert_return (invoke "select-f32-t" (f32.const nan:0x20304) (f32.const 1) (i32.const 0)) (f32.const 1)) +(assert_return (invoke "select-f32-t" (f32.const 2) (f32.const nan) (i32.const 1)) (f32.const 2)) +(assert_return (invoke "select-f32-t" (f32.const 2) (f32.const nan:0x20304) (i32.const 1)) (f32.const 2)) +(assert_return (invoke "select-f32-t" (f32.const 2) (f32.const nan) (i32.const 0)) (f32.const nan)) +(assert_return (invoke "select-f32-t" (f32.const 2) (f32.const nan:0x20304) (i32.const 0)) (f32.const nan:0x20304)) + +(assert_return (invoke "select-f64-t" (f64.const nan) (f64.const 1) (i32.const 1)) (f64.const nan)) +(assert_return (invoke "select-f64-t" (f64.const nan:0x20304) (f64.const 1) (i32.const 1)) (f64.const nan:0x20304)) +(assert_return (invoke "select-f64-t" (f64.const nan) (f64.const 1) (i32.const 0)) (f64.const 1)) +(assert_return (invoke "select-f64-t" (f64.const nan:0x20304) (f64.const 1) (i32.const 0)) (f64.const 1)) +(assert_return (invoke "select-f64-t" (f64.const 2) (f64.const nan) (i32.const 1)) (f64.const 2)) +(assert_return (invoke "select-f64-t" (f64.const 2) (f64.const nan:0x20304) (i32.const 1)) (f64.const 2)) +(assert_return (invoke "select-f64-t" (f64.const 2) (f64.const nan) (i32.const 0)) (f64.const nan)) +(assert_return (invoke "select-f64-t" (f64.const 2) (f64.const nan:0x20304) (i32.const 0)) (f64.const nan:0x20304)) + +(assert_return (invoke "join-funcnull" (i32.const 1)) (ref.func)) +(assert_return (invoke "join-funcnull" (i32.const 0)) (ref.null)) + +(assert_trap (invoke "select-trap-left" (i32.const 1)) "unreachable") +(assert_trap (invoke "select-trap-left" (i32.const 0)) "unreachable") +(assert_trap (invoke "select-trap-right" (i32.const 1)) "unreachable") +(assert_trap (invoke "select-trap-right" (i32.const 0)) "unreachable") + +(assert_return (invoke "as-select-first" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-select-first" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "as-select-mid" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-select-mid" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-select-last" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-select-last" (i32.const 1)) (i32.const 3)) + +(assert_return (invoke "as-loop-first" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-loop-first" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-loop-mid" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-loop-mid" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-loop-last" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-loop-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-if-condition" (i32.const 0))) +(assert_return (invoke "as-if-condition" (i32.const 1))) +(assert_return (invoke "as-if-then" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-if-then" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-if-else" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-if-else" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-br_if-first" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-br_if-first" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-br_if-last" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-br_if-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-br_table-first" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-br_table-first" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-br_table-last" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-br_table-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-call_indirect-first" (i32.const 0)) (i32.const 3)) +;;(assert_return (invoke "as-call_indirect-first" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-call_indirect-mid" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-call_indirect-mid" (i32.const 1)) (i32.const 1)) +(assert_trap (invoke "as-call_indirect-last" (i32.const 0)) "undefined element") +(assert_trap (invoke "as-call_indirect-last" (i32.const 1)) "undefined element") + +(assert_return (invoke "as-store-first" (i32.const 0))) +(assert_return (invoke "as-store-first" (i32.const 1))) +(assert_return (invoke "as-store-last" (i32.const 0))) +(assert_return (invoke "as-store-last" (i32.const 1))) + +(assert_return (invoke "as-memory.grow-value" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-memory.grow-value" (i32.const 1)) (i32.const 3)) + +(assert_return (invoke "as-call-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-call-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-return-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-return-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-drop-operand" (i32.const 0))) +(assert_return (invoke "as-drop-operand" (i32.const 1))) +(assert_return (invoke "as-br-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-br-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-local.set-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-local.set-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-local.tee-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-local.tee-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-global.set-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-global.set-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-load-operand" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-load-operand" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-unary-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-unary-operand" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-binary-operand" (i32.const 0)) (i32.const 4)) +(assert_return (invoke "as-binary-operand" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-test-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-test-operand" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-compare-left" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-compare-left" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-compare-right" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-compare-right" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-convert-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-convert-operand" (i32.const 1)) (i32.const 1)) + +(assert_invalid + (module (func $arity-0-implicit (select (nop) (nop) (i32.const 1)))) + "type mismatch" +) +(assert_invalid + (module (func $arity-0 (select (result) (nop) (nop) (i32.const 1)))) + "invalid result arity" +) +(assert_invalid + (module (func $arity-2 (result i32 i32) + (select (result i32 i32) + (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) + (i32.const 1) + ) + )) + "invalid result arity" +) + + +(assert_invalid + (module (type $t (func)) + (func $type-ref-implicit (param $r (ref $t)) + (drop (select (local.get $r) (local.get $r) (i32.const 1))) + ) + ) + "type mismatch" +) +(assert_invalid + (module (func $type-funcref-implicit (param $r funcref) + (drop (select (local.get $r) (local.get $r) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-externref-implicit (param $r externref) + (drop (select (local.get $r) (local.get $r) (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-num-vs-num + (drop (select (i32.const 1) (i64.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-num-vs-num + (drop (select (i32.const 1) (f32.const 1.0) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-num-vs-num + (drop (select (i32.const 1) (f64.const 1.0) (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-num-vs-num (select (i32.const 1) (i64.const 1) (i32.const 1)) (drop))) + "type mismatch" +) +(assert_invalid + (module (func $type-num-vs-num (select (i32.const 1) (f32.const 1.0) (i32.const 1)) (drop))) + "type mismatch" +) +(assert_invalid + (module (func $type-num-vs-num (select (i32.const 1) (i64.const 1) (i32.const 1)) (drop))) + "type mismatch" +) +(assert_invalid + (module (func $type-num-vs-num (select (i32.const 1) (f32.const 1.0) (i32.const 1)) (drop))) + "type mismatch" +) +(assert_invalid + (module (func $type-num-vs-num (select (i32.const 1) (f64.const 1.0) (i32.const 1)) (drop))) + "type mismatch" +) + + +(assert_invalid + (module + (func $type-1st-operand-empty + (select) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-2nd-operand-empty + (i32.const 0) (select) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-3rd-operand-empty + (i32.const 0) (i32.const 0) (select) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-1st-operand-empty-in-block + (i32.const 0) (i32.const 0) (i32.const 0) + (block (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-2nd-operand-empty-in-block + (i32.const 0) (i32.const 0) + (block (i32.const 0) (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-3rd-operand-empty-in-block + (i32.const 0) + (block (i32.const 0) (i32.const 0) (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-1st-operand-empty-in-loop + (i32.const 0) (i32.const 0) (i32.const 0) + (loop (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-2nd-operand-empty-in-loop + (i32.const 0) (i32.const 0) + (loop (i32.const 0) (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-3rd-operand-empty-in-loop + (i32.const 0) + (loop (i32.const 0) (i32.const 0) (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-1st-operand-empty-in-then + (i32.const 0) (i32.const 0) (i32.const 0) + (if (then (select) (drop))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-2nd-operand-empty-in-then + (i32.const 0) (i32.const 0) + (if (then (i32.const 0) (select) (drop))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-3rd-operand-empty-in-then + (i32.const 0) + (if (then (i32.const 0) (i32.const 0) (select) (drop))) + ) + ) + "type mismatch" +) + +;; Third operand must be i32 + +(assert_invalid + (module (func (select (i32.const 1) (i32.const 1) (i64.const 1)) (drop))) + "type mismatch" +) +(assert_invalid + (module (func (select (i32.const 1) (i32.const 1) (f32.const 1)) (drop))) + "type mismatch" +) +(assert_invalid + (module (func (select (i32.const 1) (i32.const 1) (f64.const 1)) (drop))) + "type mismatch" +) + +;; Result of select has type of first two operands + +(assert_invalid + (module (func (result i32) (select (i64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch" +) + +;; Validation after unreachable + +;; The first two operands should have the same type as each other +(assert_invalid + (module (func (unreachable) (select (i32.const 1) (i64.const 1) (i32.const 1)) (drop))) + "type mismatch" +) + +(assert_invalid + (module (func (unreachable) (select (i64.const 1) (i32.const 1) (i32.const 1)) (drop))) + "type mismatch" +) + +;; Third operand must be i32 +(assert_invalid + (module (func (unreachable) (select (i32.const 1) (i32.const 1) (i64.const 1)) (drop))) + "type mismatch" +) + +(assert_invalid + (module (func (unreachable) (select (i32.const 1) (i64.const 1)) (drop))) + "type mismatch" +) + +(assert_invalid + (module (func (unreachable) (select (i64.const 1)) (drop))) + "type mismatch" +) + +;; Result of select has type of first two operands (type of second operand when first one is omitted) +(assert_invalid + (module (func (result i32) (unreachable) (select (i64.const 1) (i32.const 1)))) + "type mismatch" +) + +;; select always has non-empty result +(assert_invalid + (module (func (unreachable) (select))) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/struct.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/struct.wast new file mode 100644 index 000000000..50a0e1d5c --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/struct.wast @@ -0,0 +1,127 @@ +;; Type syntax + +(module + (type (struct)) + (type (struct (field))) + (type (struct (field i8))) + (type (struct (field i8 i8 i8 i8))) + (type (struct (field $x1 i32) (field $y1 i32))) + (type (struct (field i8 i16 i32 i64 f32 f64 anyref funcref (ref 0) (ref null 1)))) + (type (struct (field i32 i64 i8) (field) (field) (field (ref null i31) anyref))) + (type (struct (field $x2 i32) (field f32 f64) (field $y2 i32))) +) + + +(assert_malformed + (module quote + "(type (struct (field $x i32) (field $x i32)))" + ) + "duplicate field" +) +(assert_malformed + (module quote + "(type (struct (field $x i32)))" + "(type (struct (field $x i32)))" + ) + "duplicate field" +) + + +;; Binding structure + +(module + (type $s0 (struct (field (ref 0) (ref 1) (ref $s0) (ref $s1)))) + (type $s1 (struct (field (ref 0) (ref 1) (ref $s0) (ref $s1)))) + + (func (param (ref $forward))) + + (type $forward (struct)) +) + +(assert_invalid + (module (type (struct (field (ref 1))))) + "unknown type" +) +(assert_invalid + (module (type (struct (field (mut (ref 1)))))) + "unknown type" +) + + +;; Basic instructions + +(module + (type $vec (struct (field f32) (field $y (mut f32)) (field $z f32))) + + (func $get_0 (param $v (ref $vec)) (result f32) + (struct.get $vec 0 (local.get $v)) + ) + (func (export "get_0") (result f32) + (call $get_0 (struct.new_default $vec (rtt.canon $vec))) + ) + + (func $set_get_y (param $v (ref $vec)) (param $y f32) (result f32) + (struct.set $vec $y (local.get $v) (local.get $y)) + (struct.get $vec $y (local.get $v)) + ) + (func (export "set_get_y") (param $y f32) (result f32) + (call $set_get_y (struct.new_default $vec (rtt.canon $vec)) (local.get $y)) + ) + + (func $set_get_1 (param $v (ref $vec)) (param $y f32) (result f32) + (struct.set $vec 1 (local.get $v) (local.get $y)) + (struct.get $vec $y (local.get $v)) + ) + (func (export "set_get_1") (param $y f32) (result f32) + (call $set_get_1 (struct.new_default $vec (rtt.canon $vec)) (local.get $y)) + ) +) + +(assert_return (invoke "get_0") (f32.const 0)) +(assert_return (invoke "set_get_y" (f32.const 7)) (f32.const 7)) +(assert_return (invoke "set_get_1" (f32.const 7)) (f32.const 7)) + +(assert_invalid + (module + (type $s (struct (field i64))) + (func (export "struct.set-immutable") (param $s (ref $s)) + (struct.set $s 0 (local.get $s) (i64.const 1)) + ) + ) + "field is immutable" +) + + +;; Null dereference + +(module + (type $t (struct (field i32 (mut i32)))) + (func (export "struct.get-null") + (local (ref null $t)) (drop (struct.get $t 1 (local.get 0))) + ) + (func (export "struct.set-null") + (local (ref null $t)) (struct.set $t 1 (local.get 0) (i32.const 0)) + ) +) + +(assert_trap (invoke "struct.get-null") "null structure") +(assert_trap (invoke "struct.set-null") "null structure") + +(assert_invalid + (module + (type $t (struct (field i32 (mut i32)))) + (func (export "struct.new-null") + (local (ref null (rtt $t))) (drop (struct.new $t (i32.const 1) (i32.const 2) (local.get 0))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type $t (struct (field i32 (mut i32)))) + (func (export "struct.new_default-null") + (local (ref null (rtt $t))) (drop (struct.new_default $t (local.get 0))) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/table-sub.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/table-sub.wast new file mode 100644 index 000000000..c3a7440ce --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/table-sub.wast @@ -0,0 +1,32 @@ +(module + (type $t (func)) + (table $t1 10 (ref null func)) + (table $t2 10 (ref null $t)) + (elem $el funcref) + (func $f + (table.init $t1 $el (i32.const 0) (i32.const 1) (i32.const 2)) + (table.copy $t1 $t2 (i32.const 0) (i32.const 1) (i32.const 2)) + ) +) + +(assert_invalid + (module + (table $t1 10 funcref) + (table $t2 10 externref) + (func $f + (table.copy $t1 $t2 (i32.const 0) (i32.const 1) (i32.const 2)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 10 funcref) + (elem $el externref) + (func $f + (table.init $t $el (i32.const 0) (i32.const 1) (i32.const 2)) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/table.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/table.wast new file mode 100644 index 000000000..532c13d09 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/table.wast @@ -0,0 +1,70 @@ +;; Test table section structure + +(module (table 0 funcref)) +(module (table 1 funcref)) +(module (table 0 0 funcref)) +(module (table 0 1 funcref)) +(module (table 1 256 funcref)) +(module (table 0 65536 funcref)) +(module (table 0 0xffff_ffff funcref)) + +(module (table 1 (ref null func))) +(module (table 1 (ref null extern))) +(module (table 1 (ref null $t)) (type $t (func))) + +(module (table 0 funcref) (table 0 funcref)) +(module (table (import "spectest" "table") 0 funcref) (table 0 funcref)) + +(assert_invalid (module (elem (i32.const 0))) "unknown table") +(assert_invalid (module (elem (i32.const 0) $f) (func $f)) "unknown table") + + +(assert_invalid + (module (table 1 0 funcref)) + "size minimum must not be greater than maximum" +) +(assert_invalid + (module (table 0xffff_ffff 0 funcref)) + "size minimum must not be greater than maximum" +) + +(assert_malformed + (module quote "(table 0x1_0000_0000 funcref)") + "i32 constant out of range" +) +(assert_malformed + (module quote "(table 0x1_0000_0000 0x1_0000_0000 funcref)") + "i32 constant out of range" +) +(assert_malformed + (module quote "(table 0 0x1_0000_0000 funcref)") + "i32 constant out of range" +) + +(assert_invalid + (module (table 0 (ref func))) + "non-defaultable element type" +) +(assert_invalid + (module (table 0 (ref extern))) + "non-defaultable element type" +) +(assert_invalid + (module (type $t (func)) (table 0 (ref $t))) + "non-defaultable element type" +) + +;; Duplicate table identifiers + +(assert_malformed (module quote + "(table $foo 1 funcref)" + "(table $foo 1 funcref)") + "duplicate table") +(assert_malformed (module quote + "(import \"\" \"\" (table $foo 1 funcref))" + "(table $foo 1 funcref)") + "duplicate table") +(assert_malformed (module quote + "(import \"\" \"\" (table $foo 1 funcref))" + "(import \"\" \"\" (table $foo 1 funcref))") + "duplicate table") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/type-equivalence.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/type-equivalence.wast new file mode 100644 index 000000000..e43871a36 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/type-equivalence.wast @@ -0,0 +1,447 @@ +;; Syntactic types (validation time) + +;; Simple types. + +(module + (type $t1 (func (param f32 f32) (result f32))) + (type $t2 (func (param $x f32) (param $y f32) (result f32))) + + (func $f1 (param $r (ref $t1)) (call $f2 (local.get $r))) + (func $f2 (param $r (ref $t2)) (call $f1 (local.get $r))) +) + + +;; Indirect types. + +(module + (type $s0 (func (param i32) (result f32))) + (type $s1 (func (param i32 (ref $s0)) (result (ref $s0)))) + (type $s2 (func (param i32 (ref $s0)) (result (ref $s0)))) + (type $t1 (func (param (ref $s1)) (result (ref $s2)))) + (type $t2 (func (param (ref $s2)) (result (ref $s1)))) + + (func $f1 (param $r (ref $t1)) (call $f2 (local.get $r))) + (func $f2 (param $r (ref $t2)) (call $f1 (local.get $r))) +) + + +;; Recursive types. + +(module + (type $t1 (func (param i32 (ref $t1)))) + (type $t2 (func (param i32 (ref $t2)))) + + (func $f1 (param $r (ref $t1)) (call $f2 (local.get $r))) + (func $f2 (param $r (ref $t2)) (call $f1 (local.get $r))) +) + + +;; Isomorphic recursive types. + +(module + (type $t1 (func (param i32 (ref $t1)))) + (type $t2 (func (param i32 (ref $t3)))) + (type $t3 (func (param i32 (ref $t2)))) + + (func $f1 (param $r (ref $t1)) + (call $f2 (local.get $r)) + (call $f3 (local.get $r)) + ) + (func $f2 (param $r (ref $t2)) + (call $f1 (local.get $r)) + (call $f3 (local.get $r)) + ) + (func $f3 (param $r (ref $t3)) + (call $f1 (local.get $r)) + (call $f2 (local.get $r)) + ) +) + +(module + (type $t1 (func (param i32 (ref $t3)))) + (type $t2 (func (param i32 (ref $t1)))) + (type $t3 (func (param i32 (ref $t2)))) + + (func $f1 (param $r (ref $t1)) + (call $f2 (local.get $r)) + (call $f3 (local.get $r)) + ) + (func $f2 (param $r (ref $t2)) + (call $f1 (local.get $r)) + (call $f3 (local.get $r)) + ) + (func $f3 (param $r (ref $t3)) + (call $f1 (local.get $r)) + (call $f2 (local.get $r)) + ) +) + +(module + (type $t1 (func (param i32 (ref $u1)))) + (type $u1 (func (param f32 (ref $t1)))) + + (type $t2 (func (param i32 (ref $u3)))) + (type $u2 (func (param f32 (ref $t3)))) + (type $t3 (func (param i32 (ref $u2)))) + (type $u3 (func (param f32 (ref $t2)))) + + (func $f1 (param $r (ref $t1)) + (call $f2 (local.get $r)) + (call $f3 (local.get $r)) + ) + (func $f2 (param $r (ref $t2)) + (call $f1 (local.get $r)) + (call $f3 (local.get $r)) + ) + (func $f3 (param $r (ref $t3)) + (call $f1 (local.get $r)) + (call $f2 (local.get $r)) + ) +) + + +;; Semantic types (run time) + +;; Simple types. + +(module + (type $t1 (func (param f32 f32))) + (type $t2 (func (param $x f32) (param $y f32))) + + (func $f1 (type $t1)) + (func $f2 (type $t2)) + (table funcref (elem $f1 $f2)) + + (func (export "run") + (call_indirect (type $t1) (f32.const 1) (f32.const 2) (i32.const 1)) + (call_indirect (type $t2) (f32.const 1) (f32.const 2) (i32.const 0)) + ) +) +(assert_return (invoke "run")) + + +;; Indirect types. + +(module + (type $s0 (func (param i32))) + (type $s1 (func (param i32 (ref $s0)))) + (type $s2 (func (param i32 (ref $s0)))) + (type $t1 (func (param (ref $s1)))) + (type $t2 (func (param (ref $s2)))) + + (func $s1 (type $s1)) + (func $s2 (type $s2)) + (func $f1 (type $t1)) + (func $f2 (type $t2)) + (table funcref (elem $f1 $f2 $s1 $s2)) + + (func (export "run") + (call_indirect (type $t1) (ref.func $s1) (i32.const 0)) + (call_indirect (type $t1) (ref.func $s1) (i32.const 1)) + (call_indirect (type $t1) (ref.func $s2) (i32.const 0)) + (call_indirect (type $t1) (ref.func $s2) (i32.const 1)) + (call_indirect (type $t2) (ref.func $s1) (i32.const 0)) + (call_indirect (type $t2) (ref.func $s1) (i32.const 1)) + (call_indirect (type $t2) (ref.func $s2) (i32.const 0)) + (call_indirect (type $t2) (ref.func $s2) (i32.const 1)) + ) +) +(assert_return (invoke "run")) + + +;; Recursive types. + +(module + (type $t1 (func (param i32 (ref $t1)))) + (type $t2 (func (param i32 (ref $t2)))) + + (func $f1 (type $t1)) + (func $f2 (type $t2)) + (table funcref (elem $f1 $f2)) + + (func (export "run") + (call_indirect (type $t1) (i32.const 1) (ref.func $f1) (i32.const 0)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f1) (i32.const 1)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f2) (i32.const 0)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f2) (i32.const 1)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f1) (i32.const 0)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f1) (i32.const 1)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f2) (i32.const 0)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f2) (i32.const 1)) + ) +) +(assert_return (invoke "run")) + + +;; Isomorphic recursive types. + +(module + (type $t1 (func (param i32 (ref $t1)))) + (type $t2 (func (param i32 (ref $t3)))) + (type $t3 (func (param i32 (ref $t2)))) + + (func $f1 (type $t1)) + (func $f2 (type $t2)) + (func $f3 (type $t3)) + (table funcref (elem $f1 $f2 $f3)) + + (func (export "run") + (call_indirect (type $t1) (i32.const 1) (ref.func $f1) (i32.const 0)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f1) (i32.const 1)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f1) (i32.const 2)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f2) (i32.const 0)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f2) (i32.const 1)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f2) (i32.const 2)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f3) (i32.const 0)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f3) (i32.const 1)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f3) (i32.const 2)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f1) (i32.const 0)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f1) (i32.const 1)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f1) (i32.const 2)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f2) (i32.const 0)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f2) (i32.const 1)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f2) (i32.const 2)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f3) (i32.const 0)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f3) (i32.const 1)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f3) (i32.const 2)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f1) (i32.const 0)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f1) (i32.const 1)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f1) (i32.const 2)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f2) (i32.const 0)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f2) (i32.const 1)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f2) (i32.const 2)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f3) (i32.const 0)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f3) (i32.const 1)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f3) (i32.const 2)) + ) +) +(assert_return (invoke "run")) + +(module + (type $t1 (func (param i32 (ref $t3)))) + (type $t2 (func (param i32 (ref $t1)))) + (type $t3 (func (param i32 (ref $t2)))) + + (func $f1 (type $t1)) + (func $f2 (type $t2)) + (func $f3 (type $t3)) + (table funcref (elem $f1 $f2 $f3)) + + (func (export "run") + (call_indirect (type $t1) (i32.const 1) (ref.func $f1) (i32.const 0)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f1) (i32.const 1)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f1) (i32.const 2)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f2) (i32.const 0)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f2) (i32.const 1)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f2) (i32.const 2)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f3) (i32.const 0)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f3) (i32.const 1)) + (call_indirect (type $t1) (i32.const 1) (ref.func $f3) (i32.const 2)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f1) (i32.const 0)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f1) (i32.const 1)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f1) (i32.const 2)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f2) (i32.const 0)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f2) (i32.const 1)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f2) (i32.const 2)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f3) (i32.const 0)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f3) (i32.const 1)) + (call_indirect (type $t2) (i32.const 1) (ref.func $f3) (i32.const 2)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f1) (i32.const 0)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f1) (i32.const 1)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f1) (i32.const 2)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f2) (i32.const 0)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f2) (i32.const 1)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f2) (i32.const 2)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f3) (i32.const 0)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f3) (i32.const 1)) + (call_indirect (type $t3) (i32.const 1) (ref.func $f3) (i32.const 2)) + ) +) +(assert_return (invoke "run")) + +(module + (type $t1 (func (param i32 (ref $u1)))) + (type $u1 (func (param f32 (ref $t1)))) + + (type $t2 (func (param i32 (ref $u3)))) + (type $u2 (func (param f32 (ref $t3)))) + (type $t3 (func (param i32 (ref $u2)))) + (type $u3 (func (param f32 (ref $t2)))) + + (func $f1 (type $t1)) + (func $f2 (type $t2)) + (func $f3 (type $t3)) + (func $g1 (type $u1)) + (func $g2 (type $u2)) + (func $g3 (type $u3)) + (table funcref (elem $f1 $f2 $f3 $g1 $g2 $g3)) + + (func (export "run") + (call_indirect (type $t1) (i32.const 1) (ref.func $g1) (i32.const 0)) + (call_indirect (type $t1) (i32.const 1) (ref.func $g1) (i32.const 1)) + (call_indirect (type $t1) (i32.const 1) (ref.func $g1) (i32.const 2)) + (call_indirect (type $t1) (i32.const 1) (ref.func $g2) (i32.const 0)) + (call_indirect (type $t1) (i32.const 1) (ref.func $g2) (i32.const 1)) + (call_indirect (type $t1) (i32.const 1) (ref.func $g2) (i32.const 2)) + (call_indirect (type $t1) (i32.const 1) (ref.func $g3) (i32.const 0)) + (call_indirect (type $t1) (i32.const 1) (ref.func $g3) (i32.const 1)) + (call_indirect (type $t1) (i32.const 1) (ref.func $g3) (i32.const 2)) + (call_indirect (type $t2) (i32.const 1) (ref.func $g1) (i32.const 0)) + (call_indirect (type $t2) (i32.const 1) (ref.func $g1) (i32.const 1)) + (call_indirect (type $t2) (i32.const 1) (ref.func $g1) (i32.const 2)) + (call_indirect (type $t2) (i32.const 1) (ref.func $g2) (i32.const 0)) + (call_indirect (type $t2) (i32.const 1) (ref.func $g2) (i32.const 1)) + (call_indirect (type $t2) (i32.const 1) (ref.func $g2) (i32.const 2)) + (call_indirect (type $t2) (i32.const 1) (ref.func $g3) (i32.const 0)) + (call_indirect (type $t2) (i32.const 1) (ref.func $g3) (i32.const 1)) + (call_indirect (type $t2) (i32.const 1) (ref.func $g3) (i32.const 2)) + (call_indirect (type $t3) (i32.const 1) (ref.func $g1) (i32.const 0)) + (call_indirect (type $t3) (i32.const 1) (ref.func $g1) (i32.const 1)) + (call_indirect (type $t3) (i32.const 1) (ref.func $g1) (i32.const 2)) + (call_indirect (type $t3) (i32.const 1) (ref.func $g2) (i32.const 0)) + (call_indirect (type $t3) (i32.const 1) (ref.func $g2) (i32.const 1)) + (call_indirect (type $t3) (i32.const 1) (ref.func $g2) (i32.const 2)) + (call_indirect (type $t3) (i32.const 1) (ref.func $g3) (i32.const 0)) + (call_indirect (type $t3) (i32.const 1) (ref.func $g3) (i32.const 1)) + (call_indirect (type $t3) (i32.const 1) (ref.func $g3) (i32.const 2)) + ) +) +(assert_return (invoke "run")) + + +;; Semantic types (link time) + +;; Simple types. + +(module + (type $t1 (func (param f32 f32) (result f32))) + (func (export "f") (param (ref $t1))) +) +(register "M") +(module + (type $t2 (func (param $x f32) (param $y f32) (result f32))) + (func (import "M" "f") (param (ref $t2))) +) + + +;; Indirect types. + +(module + (type $s0 (func (param i32) (result f32))) + (type $s1 (func (param i32 (ref $s0)) (result (ref $s0)))) + (type $s2 (func (param i32 (ref $s0)) (result (ref $s0)))) + (type $t1 (func (param (ref $s1)) (result (ref $s2)))) + (type $t2 (func (param (ref $s2)) (result (ref $s1)))) + (func (export "f1") (param (ref $t1))) + (func (export "f2") (param (ref $t1))) +) +(register "M") +(module + (type $s0 (func (param i32) (result f32))) + (type $s1 (func (param i32 (ref $s0)) (result (ref $s0)))) + (type $s2 (func (param i32 (ref $s0)) (result (ref $s0)))) + (type $t1 (func (param (ref $s1)) (result (ref $s2)))) + (type $t2 (func (param (ref $s2)) (result (ref $s1)))) + (func (import "M" "f1") (param (ref $t1))) + (func (import "M" "f1") (param (ref $t2))) + (func (import "M" "f2") (param (ref $t1))) + (func (import "M" "f2") (param (ref $t1))) +) + + +;; Recursive types. + +(module + (type $t1 (func (param i32 (ref $t1)))) + (func (export "f") (param (ref $t1))) +) +(register "M") +(module + (type $t2 (func (param i32 (ref $t2)))) + (func (import "M" "f") (param (ref $t2))) +) + + +;; Isomorphic recursive types. + +(module + (type $t1 (func (param i32 (ref $t1)))) + (type $t2 (func (param i32 (ref $t3)))) + (type $t3 (func (param i32 (ref $t2)))) + (func (export "f1") (param (ref $t1))) + (func (export "f2") (param (ref $t2))) + (func (export "f3") (param (ref $t3))) +) +(register "M") +(module + (type $t1 (func (param i32 (ref $t1)))) + (type $t2 (func (param i32 (ref $t3)))) + (type $t3 (func (param i32 (ref $t2)))) + (func (import "M" "f1") (param (ref $t1))) + (func (import "M" "f1") (param (ref $t2))) + (func (import "M" "f1") (param (ref $t3))) + (func (import "M" "f2") (param (ref $t1))) + (func (import "M" "f2") (param (ref $t2))) + (func (import "M" "f2") (param (ref $t3))) + (func (import "M" "f3") (param (ref $t1))) + (func (import "M" "f3") (param (ref $t2))) + (func (import "M" "f3") (param (ref $t3))) +) + +(module + (type $t1 (func (param i32 (ref $t3)))) + (type $t2 (func (param i32 (ref $t1)))) + (type $t3 (func (param i32 (ref $t2)))) + (func (export "f1") (param (ref $t1))) + (func (export "f2") (param (ref $t2))) + (func (export "f3") (param (ref $t3))) +) +(register "M") +(module + (type $t1 (func (param i32 (ref $t3)))) + (type $t2 (func (param i32 (ref $t1)))) + (type $t3 (func (param i32 (ref $t2)))) + (func (import "M" "f1") (param (ref $t1))) + (func (import "M" "f1") (param (ref $t2))) + (func (import "M" "f1") (param (ref $t3))) + (func (import "M" "f2") (param (ref $t1))) + (func (import "M" "f2") (param (ref $t2))) + (func (import "M" "f2") (param (ref $t3))) + (func (import "M" "f3") (param (ref $t1))) + (func (import "M" "f3") (param (ref $t2))) + (func (import "M" "f3") (param (ref $t3))) +) + +(module + (type $t1 (func (param i32 (ref $u1)))) + (type $u1 (func (param f32 (ref $t1)))) + + (type $t2 (func (param i32 (ref $u3)))) + (type $u2 (func (param f32 (ref $t3)))) + (type $t3 (func (param i32 (ref $u2)))) + (type $u3 (func (param f32 (ref $t2)))) + + (func (export "f1") (param (ref $t1))) + (func (export "f2") (param (ref $t2))) + (func (export "f3") (param (ref $t3))) +) +(register "M") +(module + (type $t1 (func (param i32 (ref $u1)))) + (type $u1 (func (param f32 (ref $t1)))) + + (type $t2 (func (param i32 (ref $u3)))) + (type $u2 (func (param f32 (ref $t3)))) + (type $t3 (func (param i32 (ref $u2)))) + (type $u3 (func (param f32 (ref $t2)))) + + (func (import "M" "f1") (param (ref $t1))) + (func (import "M" "f1") (param (ref $t2))) + (func (import "M" "f1") (param (ref $t3))) + (func (import "M" "f2") (param (ref $t1))) + (func (import "M" "f2") (param (ref $t2))) + (func (import "M" "f2") (param (ref $t3))) + (func (import "M" "f3") (param (ref $t1))) + (func (import "M" "f3") (param (ref $t2))) + (func (import "M" "f3") (param (ref $t3))) +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/gc/unreached-invalid.wast b/runtime/unc-vm/tests/wast/spec/proposals/gc/unreached-invalid.wast new file mode 100644 index 000000000..519656399 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/gc/unreached-invalid.wast @@ -0,0 +1,706 @@ +;; Failures in unreachable code. + +(assert_invalid + (module (func $local-index (unreachable) (drop (local.get 0)))) + "unknown local" +) +(assert_invalid + (module (func $global-index (unreachable) (drop (global.get 0)))) + "unknown global" +) +(assert_invalid + (module (func $func-index (unreachable) (call 1))) + "unknown function" +) +(assert_invalid + (module (func $label-index (unreachable) (br 1))) + "unknown label" +) + +(assert_invalid + (module (func $type-num-vs-num + (unreachable) (drop (i64.eqz (i32.const 0)))) + ) + "type mismatch" +) +(assert_invalid + (module (func $type-poly-num-vs-num (result i32) + (unreachable) (i64.const 0) (i32.const 0) (select) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-poly-transitive-num-vs-num (result i32) + (unreachable) + (i64.const 0) (i32.const 0) (select) + (i32.const 0) (i32.const 0) (select) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unconsumed-const (unreachable) (i32.const 0))) + "type mismatch" +) +(assert_invalid + (module (func $type-unconsumed-result (unreachable) (i32.eqz))) + "type mismatch" +) +(assert_invalid + (module (func $type-unconsumed-result2 + (unreachable) (i32.const 0) (i32.add) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unconsumed-poly0 (unreachable) (select))) + "type mismatch" +) +(assert_invalid + (module (func $type-unconsumed-poly1 (unreachable) (i32.const 0) (select))) + "type mismatch" +) +(assert_invalid + (module (func $type-unconsumed-poly2 + (unreachable) (i32.const 0) (i32.const 0) (select) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-after-break + (block (br 0) (block (drop (i32.eqz (nop))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-after-break + (block (br 0) (drop (i32.eqz (f32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-after-break + (block (br 0) (block (drop (f32.eq (i32.const 1))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-after-break + (block (br 0) (drop (f32.eq (i32.const 1) (f32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-after-break + (block (br 0) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-after-break (result i32) + (block (result i32) (i32.const 1) (br 0) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-void-after-break + (block (loop (br 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-num-after-break (result i32) + (loop (result i32) (br 1 (i32.const 1)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-void-after-break + (br 0) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-num-after-break (result i32) + (br 0 (i32.const 1)) (f32.const 0) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-after-return + (return) (block (drop (i32.eqz (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-after-return + (return) (drop (i32.eqz (f32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-after-return + (return) (block (drop (f32.eq (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-after-return + (return) (drop (f32.eq (i32.const 1) (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-after-return + (block (return) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-after-return (result i32) + (block (result i32) (i32.const 1) (return (i32.const 0)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-void-after-return + (block (loop (return) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-num-after-return (result i32) + (loop (result i32) (return (i32.const 1)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-void-after-return + (return) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-num-after-return (result i32) + (return (i32.const 1)) (f32.const 0) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-after-unreachable + (unreachable) (block (drop (i32.eqz (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-void-in-loop-after-unreachable + (unreachable) (loop (drop (i32.eqz (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-void-in-i32-loop-after-unreachable + (unreachable) (loop (result i32) (i32.eqz (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-after-unreachable + (unreachable) (drop (i32.eqz (f32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-after-unreachable + (unreachable) (block (drop (f32.eq (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-after-unreachable + (unreachable) (drop (f32.eq (i32.const 1) (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-after-unreachable + (block (unreachable) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-after-unreachable (result i32) + (block (result i32) (i32.const 1) (unreachable) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-void-after-unreachable + (block (loop (unreachable) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-num-after-unreachable (result i32) + (loop (result i32) (unreachable) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-void-after-unreachable + (unreachable) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-num-after-unreachable (result i32) + (unreachable) (f32.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-void-in-if-after-unreachable + (unreachable) (if (i32.const 0) (then (drop (i32.eqz (nop))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-void-in-else-after-unreachable + (unreachable) (if (i32.const 0) (then (nop)) (else (drop (i32.eqz (nop))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-void-in-else-after-unreachable-if + (if (i32.const 0) (then (unreachable)) (else (drop (i32.eqz (nop))))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-after-nested-unreachable + (block (unreachable)) (block (drop (i32.eqz (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-after-nested-unreachable + (block (unreachable)) (drop (i32.eqz (f32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-after-nested-unreachable + (block (unreachable)) (block (drop (f32.eq (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-after-nested-unreachable + (block (unreachable)) (drop (f32.eq (i32.const 1) (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-after-nested-unreachable + (block (block (unreachable)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-after-nested-unreachable + (result i32) + (block (result i32) (i32.const 1) (block (unreachable)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-void-after-nested-unreachable + (block (loop (block (unreachable)) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-num-after-nested-unreachable + (result i32) + (loop (result i32) (block (unreachable)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-void-after-nested-unreachable + (block (unreachable)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-num-after-nested-unreachable + (result i32) + (block (unreachable)) (f32.const 0) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-after-infinite-loop + (loop (br 0)) (block (drop (i32.eqz (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-after-infinite-loop + (loop (br 0)) (drop (i32.eqz (f32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-after-infinite-loop + (loop (br 0)) (block (drop (f32.eq (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-after-infinite-loop + (loop (br 0)) (drop (f32.eq (i32.const 1) (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-after-infinite-loop + (block (loop (br 0)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-after-infinite-loop (result i32) + (block (result i32) (i32.const 1) (loop (br 0)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-void-after-infinite-loop + (block (loop (loop (br 0)) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-num-after-infinite-loop (result i32) + (loop (result i32) (loop (br 0)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-void-after-infinite-loop + (loop (br 0)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-num-after-infinite-loop (result i32) + (loop (br 0)) (f32.const 0) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-in-dead-body + (if (i32.const 0) (then (drop (i32.eqz (nop))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-in-dead-body + (if (i32.const 0) (then (drop (i32.eqz (f32.const 1))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-in-dead-body + (if (i32.const 0) (then (drop (f32.eq (i32.const 1))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-in-dead-body + (if (i32.const 0) (then (drop (f32.eq (i32.const 1) (f32.const 0))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-if-value-num-vs-void-in-dead-body + (if (i32.const 0) (then (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-if-value-num-vs-num-in-dead-body (result i32) + (if (result i32) (i32.const 0) (then (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-in-dead-body + (if (i32.const 0) (then (block (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-in-dead-body (result i32) + (if (result i32) (i32.const 0) (then (block (result i32) (f32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-in-dead-body + (if (i32.const 0) (then (loop (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-in-dead-body (result i32) + (if (result i32) (i32.const 0) (then (loop (result i32) (f32.const 0)))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-return-second-num-vs-num (result i32) + (return (i32.const 1)) (return (f64.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-br-second-num-vs-num (result i32) + (block (result i32) (br 0 (i32.const 1)) (br 0 (f64.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-br_if-cond-num-vs-num-after-unreachable + (block (br_if 0 (unreachable) (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_if-num-vs-void-after-unreachable (result i32) + (block (result i32) + (block (unreachable) (br_if 1 (i32.const 0) (i32.const 0))) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_if-num-vs-num-after-unreachable (result i32) + (block (result i32) + (block (result f32) (unreachable) (br_if 1 (i32.const 0) (i32.const 0))) + (drop) (i32.const 0) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_if-num2-vs-num-after-unreachable (result i32) + (block (result i32) + (unreachable) (br_if 0 (i32.const 0) (i32.const 0)) (i32.const 0) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_table-num-vs-num-after-unreachable + (block (br_table 0 (unreachable) (f32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_table-label-num-vs-num-after-unreachable (result i32) + (block (result i32) (unreachable) (br_table 0 (f32.const 0) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_table-label-num-vs-label-void-after-unreachable + (block + (block (result f32) + (unreachable) + (br_table 0 1 0 (i32.const 1)) + ) + (drop) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-block-value-nested-unreachable-num-vs-void + (block (i32.const 3) (block (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-unreachable-void-vs-num (result i32) + (block (block (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-unreachable-num-vs-num (result i32) + (block (result i64) (i64.const 0) (block (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-unreachable-num2-vs-void (result i32) + (block (i32.const 3) (block (i64.const 1) (unreachable))) (i32.const 9) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-block-value-nested-br-num-vs-void + (block (i32.const 3) (block (br 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-br-void-vs-num (result i32) + (block (result i32) (block (br 1 (i32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-br-num-vs-num (result i32) + (block (result i32) (i64.const 0) (block (br 1 (i32.const 0)))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-block-value-nested2-br-num-vs-void + (block (block (i32.const 3) (block (br 2)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested2-br-void-vs-num (result i32) + (block (result i32) (block (block (br 2 (i32.const 0))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested2-br-num-vs-num (result i32) + (block (result i32) + (block (result i64) (i64.const 0) (block (br 2 (i32.const 0)))) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested2-br-num2-vs-void (result i32) + (block (i32.const 3) (block (i64.const 1) (br 1))) (i32.const 9) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-block-value-nested-return-num-vs-void + (block (i32.const 3) (block (return))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-return-void-vs-num (result i32) + (block (block (return (i32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-return-num-vs-num (result i32) + (block (result i64) (i64.const 0) (block (return (i32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-return-num2-vs-void (result i32) + (block (i32.const 3) (block (i64.const 1) (return (i32.const 0)))) + (i32.const 9) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-loop-value-nested-unreachable-num-vs-void + (loop (i32.const 3) (block (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-nested-unreachable-void-vs-num (result i32) + (loop (block (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-nested-unreachable-num-vs-num (result i32) + (loop (result i64) (i64.const 0) (block (unreachable))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-cont-last-void-vs-empty (result i32) + (loop (br 0 (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-cont-last-num-vs-empty (result i32) + (loop (br 0 (i32.const 0))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $tee-local-unreachable-value + (local i32) + (local.tee 0 (unreachable)) + )) + "type mismatch" +) +(assert_invalid + (module (func $br_if-unreachable (result i32) + (block (result i32) + (block + (br_if 1 (unreachable) (i32.const 0)) + ) + (i32.const 0) + ) + )) + "type mismatch" +) +(assert_invalid + (module + (func $type-br_if-after-unreachable (result i64) + (unreachable) + (br_if 0) + (i64.extend_i32_u) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $type-after-ref.as_non_null + (unreachable) + (ref.as_non_null) + (f32.abs) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/multi-value/binary.wast b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/binary.wast new file mode 100644 index 000000000..de625bf28 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/binary.wast @@ -0,0 +1,811 @@ +(module binary "\00asm\01\00\00\00") +(module binary "\00asm" "\01\00\00\00") +(module $M1 binary "\00asm\01\00\00\00") +(module $M2 binary "\00asm" "\01\00\00\00") + +(assert_malformed (module binary "") "unexpected end") +(assert_malformed (module binary "\01") "unexpected end") +(assert_malformed (module binary "\00as") "unexpected end") +(assert_malformed (module binary "asm\00") "magic header not detected") +(assert_malformed (module binary "msa\00") "magic header not detected") +(assert_malformed (module binary "msa\00\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "msa\00\00\00\00\01") "magic header not detected") +(assert_malformed (module binary "asm\01\00\00\00\00") "magic header not detected") +(assert_malformed (module binary "wasm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\7fasm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\80asm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\82asm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\ffasm\01\00\00\00") "magic header not detected") + +;; 8-byte endian-reversed. +(assert_malformed (module binary "\00\00\00\01msa\00") "magic header not detected") + +;; Middle-endian byte orderings. +(assert_malformed (module binary "a\00ms\00\01\00\00") "magic header not detected") +(assert_malformed (module binary "sm\00a\00\00\01\00") "magic header not detected") + +;; Upper-cased. +(assert_malformed (module binary "\00ASM\01\00\00\00") "magic header not detected") + +;; EBCDIC-encoded magic. +(assert_malformed (module binary "\00\81\a2\94\01\00\00\00") "magic header not detected") + +;; Leading UTF-8 BOM. +(assert_malformed (module binary "\ef\bb\bf\00asm\01\00\00\00") "magic header not detected") + +;; Malformed binary version. +(assert_malformed (module binary "\00asm") "unexpected end") +(assert_malformed (module binary "\00asm\01") "unexpected end") +(assert_malformed (module binary "\00asm\01\00\00") "unexpected end") +(assert_malformed (module binary "\00asm\00\00\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\0d\00\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\0e\00\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\00\01\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\00\00\01\00") "unknown binary version") +(assert_malformed (module binary "\00asm\00\00\00\01") "unknown binary version") + + +;; call_indirect reserved byte equal to zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\04\04\01\70\00\00" ;; Table section + "\0a\09\01" ;; Code section + + ;; function 0 + "\07\00" + "\41\00" ;; i32.const 0 + "\11\00" ;; call_indirect (type 0) + "\01" ;; call_indirect reserved byte is not equal to zero! + "\0b" ;; end + ) + "zero flag expected" +) + +;; call_indirect reserved byte should not be a "long" LEB128 zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\04\04\01\70\00\00" ;; Table section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\07\00" + "\41\00" ;; i32.const 0 + "\11\00" ;; call_indirect (type 0) + "\80\00" ;; call_indirect reserved byte + "\0b" ;; end + ) + "zero flag expected" +) + +;; Same as above for 3, 4, and 5-byte zero encodings. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\04\04\01\70\00\00" ;; Table section + "\0a\0b\01" ;; Code section + + ;; function 0 + "\08\00" + "\41\00" ;; i32.const 0 + "\11\00" ;; call_indirect (type 0) + "\80\80\00" ;; call_indirect reserved byte + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\04\04\01\70\00\00" ;; Table section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\09\00" + "\41\00" ;; i32.const 0 + "\11\00" ;; call_indirect (type 0) + "\80\80\80\00" ;; call_indirect reserved byte + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\04\04\01\70\00\00" ;; Table section + "\0a\0d\01" ;; Code section + + ;; function 0 + "\0a\00" + "\41\00" ;; i32.const 0 + "\11\00" ;; call_indirect (type 0) + "\80\80\80\80\00" ;; call_indirect reserved byte + "\0b" ;; end + ) + "zero flag expected" +) + +;; memory.grow reserved byte equal to zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\09\01" ;; Code section + + ;; function 0 + "\07\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\01" ;; memory.grow reserved byte is not equal to zero! + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; memory.grow reserved byte should not be a "long" LEB128 zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\08\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; Same as above for 3, 4, and 5-byte zero encodings. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0b\01" ;; Code section + + ;; function 0 + "\09\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\0a\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\80\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0d\01" ;; Code section + + ;; function 0 + "\0b\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\80\80\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; memory.size reserved byte equal to zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\07\01" ;; Code section + + ;; function 0 + "\05\00" + "\3f" ;; memory.size + "\01" ;; memory.size reserved byte is not equal to zero! + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; memory.size reserved byte should not be a "long" LEB128 zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\08\01" ;; Code section + + ;; function 0 + "\06\00" + "\3f" ;; memory.size + "\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; Same as above for 3, 4, and 5-byte zero encodings. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\09\01" ;; Code section + + ;; function 0 + "\07\00" + "\3f" ;; memory.size + "\80\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\08\00" + "\3f" ;; memory.size + "\80\80\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0b\01" ;; Code section + + ;; function 0 + "\09\00" + "\3f" ;; memory.size + "\80\80\80\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; No more than 2^32 locals. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\0a\02" + "\ff\ff\ff\ff\0f\7f" ;; 0xFFFFFFFF i32 + "\02\7e" ;; 0x00000002 i64 + "\0b" ;; end + ) + "too many locals" +) + +;; Local count can be 0. +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\08\03" + "\00\7f" ;; 0 i32 + "\00\7e" ;; 0 i64 + "\02\7d" ;; 2 f32 + "\0b" ;; end +) + +;; Function section has non-zero count, but code section is absent. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\03\02\00\00" ;; Function section with 2 functions + ) + "function and code section have inconsistent lengths" +) + +;; Code section has non-zero count, but function section is absent. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0a\04\01\02\00\0b" ;; Code section with 1 empty function + ) + "function and code section have inconsistent lengths" +) + +;; Function section count > code section count +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\03\02\00\00" ;; Function section with 2 functions + "\0a\04\01\02\00\0b" ;; Code section with 1 empty function + ) + "function and code section have inconsistent lengths" +) + +;; Function section count < code section count +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section with 1 function + "\0a\07\02\02\00\0b\02\00\0b" ;; Code section with 2 empty functions + ) + "function and code section have inconsistent lengths" +) + +;; Function section has zero count, and code section is absent. +(module binary + "\00asm" "\01\00\00\00" + "\03\01\00" ;; Function section with 0 functions +) + +;; Code section has zero count, and function section is absent. +(module binary + "\00asm" "\01\00\00\00" + "\0a\01\00" ;; Code section with 0 functions +) + +;; Type count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\01\00" ;; type count can be zero +) + +;; 2 type declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\07\02" ;; type section with inconsistent count (2 declared, 1 given) + "\60\00\00" ;; 1st type + ;; "\60\00\00" ;; 2nd type (missed) + ) + "unexpected end of section or function" +) + +;; 1 type declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\07\01" ;; type section with inconsistent count (1 declared, 2 given) + "\60\00\00" ;; 1st type + "\60\00\00" ;; 2nd type (redundant) + ) + "section size mismatch" +) + +;; Import count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; type 0 + "\02\01\00" ;; import count can be zero +) + +;; 2 import declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; type 0 + "\02\16\02" ;; import section with inconsistent count (2 declared, 1 given) + ;; 1st import + "\08" ;; string length + "\73\70\65\63\74\65\73\74" ;; spectest + "\09" ;; string length + "\70\72\69\6e\74\5f\69\33\32" ;; print_i32 + "\00\00" ;; import kind, import signature index + ;; 2nd import + ;; (missed) + ) + "unexpected end of section or function" +) + +;; 1 import declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\09\02" ;; type section + "\60\01\7f\00" ;; type 0 + "\60\01\7d\00" ;; type 1 + "\02\2b\01" ;; import section with inconsistent count (1 declared, 2 given) + ;; 1st import + "\08" ;; string length + "\73\70\65\63\74\65\73\74" ;; spectest + "\09" ;; string length + "\70\72\69\6e\74\5f\69\33\32" ;; print_i32 + "\00\00" ;; import kind, import signature index + ;; 2nd import + ;; (redundant) + "\08" ;; string length + "\73\70\65\63\74\65\73\74" ;; spectest + "\09" ;; string length + "\70\72\69\6e\74\5f\66\33\32" ;; print_f32 + "\00\01" ;; import kind, import signature index + ) + "section size mismatch" +) + +;; Table count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\04\01\00" ;; table count can be zero +) + +;; 1 table declared, 0 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\04\01\01" ;; table section with inconsistent count (1 declared, 0 given) + ;; "\70\01\00\00" ;; table entity + ) + "unexpected end of section or function" +) + +;; Memory count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\05\01\00" ;; memory count can be zero +) + +;; 1 memory declared, 0 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\01\01" ;; memory section with inconsistent count (1 declared, 0 given) + ;; "\00\00" ;; memory 0 (missed) + ) + "unexpected end of section or function" +) + +;; Global count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\06\01\00" ;; global count can be zero +) + +;; 2 global declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\06\02" ;; global section with inconsistent count (2 declared, 1 given) + "\7f\00\41\00\0b" ;; global 0 + ;; "\7f\00\41\00\0b" ;; global 1 (missed) + ) + "unexpected end of section or function" +) + +;; 1 global declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; global section with inconsistent count (1 declared, 2 given) + "\7f\00\41\00\0b" ;; global 0 + "\7f\00\41\00\0b" ;; global 1 (redundant) + ) + "section size mismatch" +) + +;; Export count can be 0 +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\03\02\00\00" ;; func section + "\07\01\00" ;; export count can be zero + "\0a\07\02" ;; code section + "\02\00\0b" ;; function body 0 + "\02\00\0b" ;; function body 1 +) + +;; 2 export declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\03\02\00\00" ;; func section + "\07\06\02" ;; export section with inconsistent count (2 declared, 1 given) + "\02" ;; export 0 + "\66\31" ;; export name + "\00\00" ;; export kind, export func index + ;; "\02" ;; export 1 (missed) + ;; "\66\32" ;; export name + ;; "\00\01" ;; export kind, export func index + "\0a\07\02" ;; code section + "\02\00\0b" ;; function body 0 + "\02\00\0b" ;; function body 1 + ) + "unexpected end of section or function" +) + +;; 1 export declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\03\02\00\00" ;; func section + "\07\0b\01" ;; export section with inconsistent count (1 declared, 2 given) + "\02" ;; export 0 + "\66\31" ;; export name + "\00\00" ;; export kind, export func index + "\02" ;; export 1 (redundant) + "\66\32" ;; export name + "\00\01" ;; export kind, export func index + "\0a\07\02" ;; code section + "\02\00\0b" ;; function body 0 + "\02\00\0b" ;; function body 1 + ) + "section size mismatch" +) + +;; elem segment count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\01\00" ;; elem segment count can be zero + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body +) + +;; 2 elem segment declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\07\02" ;; elem with inconsistent segment count (2 declared, 1 given) + "\00\41\00\0b\01\00" ;; elem 0 + ;; "\00\41\00\0b\01\00" ;; elem 1 (missed) + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body + ) + "unexpected end" +) + +;; 1 elem segment declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\0d\01" ;; elem with inconsistent segment count (1 declared, 2 given) + "\00\41\00\0b\01\00" ;; elem 0 + "\00\41\00\0b\01\00" ;; elem 1 (redundant) + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body + ) + "section size mismatch" +) + +;; data segment count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\01\00" ;; data segment count can be zero +) + +;; 2 data segment declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\07\02" ;; data with inconsistent segment count (2 declared, 1 given) + "\00\41\00\0b\01\61" ;; data 0 + ;; "\00\41\01\0b\01\62" ;; data 1 (missed) + ) + "unexpected end of section or function" +) + +;; 1 data segment declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\0d\01" ;; data with inconsistent segment count (1 declared, 2 given) + "\00\41\00\0b\01\61" ;; data 0 + "\00\41\01\0b\01\62" ;; data 1 (redundant) + ) + "section size mismatch" +) + +;; data segment has 7 bytes declared, but 6 bytes given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\0c\01" ;; data section + "\00\41\03\0b" ;; data segment 0 + "\07" ;; data segment size with inconsistent lengths (7 declared, 6 given) + "\61\62\63\64\65\66" ;; 6 bytes given + ) + "unexpected end of section or function" +) + +;; data segment has 5 bytes declared, but 6 bytes given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\0c\01" ;; data section + "\00\41\00\0b" ;; data segment 0 + "\05" ;; data segment size with inconsistent lengths (5 declared, 6 given) + "\61\62\63\64\65\66" ;; 6 bytes given + ) + "section size mismatch" +) + +;; br_table target count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\0a\11\01" ;; code section + "\0f\00" ;; func 0 + "\02\40" ;; block 0 + "\41\01" ;; condition of if 0 + "\04\40" ;; if 0 + "\41\01" ;; index of br_table element + "\0e\00" ;; br_table target count can be zero + "\02" ;; break depth for default + "\0b\0b\0b" ;; end +) + +;; 2 br_table target declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\0a\12\01" ;; code section + "\10\00" ;; func 0 + "\02\40" ;; block 0 + "\41\01" ;; condition of if 0 + "\04\40" ;; if 0 + "\41\01" ;; index of br_table element + "\0e\02" ;; br_table with inconsistent target count (2 declared, 1 given) + "\00" ;; break depth 0 + ;; "\01" ;; break depth 1 (missed) + "\02" ;; break depth for default + "\0b\0b\0b" ;; end + ) + "unexpected end of section or function" +) + +;; 1 br_table target declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\0a\12\01" ;; code section + "\11\00" ;; func 0 + "\02\40" ;; block 0 + "\41\01" ;; condition of if 0 + "\04\40" ;; if 0 + "\41\01" ;; index of br_table element + "\0e\01" ;; br_table with inconsistent target count (1 declared, 2 given) + "\00" ;; break depth 0 + "\01" ;; break depth 1 + "\02" ;; break depth for default + "\0b\0b\0b" ;; end + ) + "unexpected end" +) + +;; Start section +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\08\01\00" ;; Start section: function 0 + + "\0a\04\01" ;; Code section + ;; function 0 + "\02\00" + "\0b" ;; end +) + +;; Multiple start sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\08\01\00" ;; Start section: function 0 + "\08\01\00" ;; Start section: function 0 + + "\0a\04\01" ;; Code section + ;; function 0 + "\02\00" + "\0b" ;; end + ) + "junk after last section" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/multi-value/block.wast b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/block.wast new file mode 100644 index 000000000..44915b991 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/block.wast @@ -0,0 +1,1491 @@ +;; Test `block` operator + +(module + ;; Auxiliary definition + (memory 1) + + (func $dummy) + + (func (export "empty") + (block) + (block $l) + ) + + (func (export "singular") (result i32) + (block (nop)) + (block (result i32) (i32.const 7)) + ) + + (func (export "multi") (result i32) + (block (call $dummy) (call $dummy) (call $dummy) (call $dummy)) + (block (result i32) + (call $dummy) (call $dummy) (call $dummy) (i32.const 7) (call $dummy) + ) + (drop) + (block (result i32 i64 i32) + (call $dummy) (call $dummy) (call $dummy) (i32.const 8) (call $dummy) + (call $dummy) (call $dummy) (call $dummy) (i64.const 7) (call $dummy) + (call $dummy) (call $dummy) (call $dummy) (i32.const 9) (call $dummy) + ) + (drop) (drop) + ) + + (func (export "nested") (result i32) + (block (result i32) + (block (call $dummy) (block) (nop)) + (block (result i32) (call $dummy) (i32.const 9)) + ) + ) + + (func (export "deep") (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (call $dummy) (i32.const 150) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + ) + + (func (export "as-select-first") (result i32) + (select (block (result i32) (i32.const 1)) (i32.const 2) (i32.const 3)) + ) + (func (export "as-select-mid") (result i32) + (select (i32.const 2) (block (result i32) (i32.const 1)) (i32.const 3)) + ) + (func (export "as-select-last") (result i32) + (select (i32.const 2) (i32.const 3) (block (result i32) (i32.const 1))) + ) + + (func (export "as-loop-first") (result i32) + (loop (result i32) (block (result i32) (i32.const 1)) (call $dummy) (call $dummy)) + ) + (func (export "as-loop-mid") (result i32) + (loop (result i32) (call $dummy) (block (result i32) (i32.const 1)) (call $dummy)) + ) + (func (export "as-loop-last") (result i32) + (loop (result i32) (call $dummy) (call $dummy) (block (result i32) (i32.const 1))) + ) + + (func (export "as-if-condition") + (block (result i32) (i32.const 1)) (if (then (call $dummy))) + ) + (func (export "as-if-then") (result i32) + (if (result i32) (i32.const 1) (then (block (result i32) (i32.const 1))) (else (i32.const 2))) + ) + (func (export "as-if-else") (result i32) + (if (result i32) (i32.const 1) (then (i32.const 2)) (else (block (result i32) (i32.const 1)))) + ) + + (func (export "as-br_if-first") (result i32) + (block (result i32) (br_if 0 (block (result i32) (i32.const 1)) (i32.const 2))) + ) + (func (export "as-br_if-last") (result i32) + (block (result i32) (br_if 0 (i32.const 2) (block (result i32) (i32.const 1)))) + ) + + (func (export "as-br_table-first") (result i32) + (block (result i32) (block (result i32) (i32.const 1)) (i32.const 2) (br_table 0 0)) + ) + (func (export "as-br_table-last") (result i32) + (block (result i32) (i32.const 2) (block (result i32) (i32.const 1)) (br_table 0 0)) + ) + + (func $func (param i32 i32) (result i32) (local.get 0)) + (type $check (func (param i32 i32) (result i32))) + (table funcref (elem $func)) + (func (export "as-call_indirect-first") (result i32) + (block (result i32) + (call_indirect (type $check) + (block (result i32) (i32.const 1)) (i32.const 2) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 2) (block (result i32) (i32.const 1)) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 1) (i32.const 2) (block (result i32) (i32.const 0)) + ) + ) + ) + + (func (export "as-store-first") + (block (result i32) (i32.const 1)) (i32.const 1) (i32.store) + ) + (func (export "as-store-last") + (i32.const 10) (block (result i32) (i32.const 1)) (i32.store) + ) + + (func (export "as-memory.grow-value") (result i32) + (memory.grow (block (result i32) (i32.const 1))) + ) + + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "as-call-value") (result i32) + (call $f (block (result i32) (i32.const 1))) + ) + (func (export "as-return-value") (result i32) + (block (result i32) (i32.const 1)) (return) + ) + (func (export "as-drop-operand") + (drop (block (result i32) (i32.const 1))) + ) + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (block (result i32) (i32.const 1)))) + ) + (func (export "as-local.set-value") (result i32) + (local i32) (local.set 0 (block (result i32) (i32.const 1))) (local.get 0) + ) + (func (export "as-local.tee-value") (result i32) + (local i32) (local.tee 0 (block (result i32) (i32.const 1))) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (result i32) + (global.set $a (block (result i32) (i32.const 1))) + (global.get $a) + ) + + (func (export "as-load-operand") (result i32) + (i32.load (block (result i32) (i32.const 1))) + ) + + (func (export "as-unary-operand") (result i32) + (i32.ctz (block (result i32) (call $dummy) (i32.const 13))) + ) + (func (export "as-binary-operand") (result i32) + (i32.mul + (block (result i32) (call $dummy) (i32.const 3)) + (block (result i32) (call $dummy) (i32.const 4)) + ) + ) + (func (export "as-test-operand") (result i32) + (i32.eqz (block (result i32) (call $dummy) (i32.const 13))) + ) + (func (export "as-compare-operand") (result i32) + (f32.gt + (block (result f32) (call $dummy) (f32.const 3)) + (block (result f32) (call $dummy) (f32.const 3)) + ) + ) + (func (export "as-binary-operands") (result i32) + (i32.mul + (block (result i32 i32) + (call $dummy) (i32.const 3) (call $dummy) (i32.const 4) + ) + ) + ) + (func (export "as-compare-operands") (result i32) + (f32.gt + (block (result f32 f32) + (call $dummy) (f32.const 3) (call $dummy) (f32.const 3) + ) + ) + ) + (func (export "as-mixed-operands") (result i32) + (block (result i32 i32) + (call $dummy) (i32.const 3) (call $dummy) (i32.const 4) + ) + (i32.const 5) + (i32.add) + (i32.mul) + ) + + (func (export "break-bare") (result i32) + (block (br 0) (unreachable)) + (block (br_if 0 (i32.const 1)) (unreachable)) + (block (br_table 0 (i32.const 0)) (unreachable)) + (block (br_table 0 0 0 (i32.const 1)) (unreachable)) + (i32.const 19) + ) + (func (export "break-value") (result i32) + (block (result i32) (br 0 (i32.const 18)) (i32.const 19)) + ) + (func (export "break-multi-value") (result i32 i32 i64) + (block (result i32 i32 i64) + (br 0 (i32.const 18) (i32.const -18) (i64.const 18)) + (i32.const 19) (i32.const -19) (i64.const 19) + ) + ) + (func (export "break-repeated") (result i32) + (block (result i32) + (br 0 (i32.const 18)) + (br 0 (i32.const 19)) + (drop (br_if 0 (i32.const 20) (i32.const 0))) + (drop (br_if 0 (i32.const 20) (i32.const 1))) + (br 0 (i32.const 21)) + (br_table 0 (i32.const 22) (i32.const 4)) + (br_table 0 0 0 (i32.const 23) (i32.const 1)) + (i32.const 21) + ) + ) + (func (export "break-inner") (result i32) + (local i32) + (local.set 0 (i32.const 0)) + (local.set 0 (i32.add (local.get 0) (block (result i32) (block (result i32) (br 1 (i32.const 0x1)))))) + (local.set 0 (i32.add (local.get 0) (block (result i32) (block (br 0)) (i32.const 0x2)))) + (local.set 0 + (i32.add (local.get 0) (block (result i32) (i32.ctz (br 0 (i32.const 0x4))))) + ) + (local.set 0 + (i32.add (local.get 0) (block (result i32) (i32.ctz (block (result i32) (br 1 (i32.const 0x8)))))) + ) + (local.get 0) + ) + + (func (export "param") (result i32) + (i32.const 1) + (block (param i32) (result i32) + (i32.const 2) + (i32.add) + ) + ) + (func (export "params") (result i32) + (i32.const 1) + (i32.const 2) + (block (param i32 i32) (result i32) + (i32.add) + ) + ) + (func (export "params-id") (result i32) + (i32.const 1) + (i32.const 2) + (block (param i32 i32) (result i32 i32)) + (i32.add) + ) + (func (export "param-break") (result i32) + (i32.const 1) + (block (param i32) (result i32) + (i32.const 2) + (i32.add) + (br 0) + ) + ) + (func (export "params-break") (result i32) + (i32.const 1) + (i32.const 2) + (block (param i32 i32) (result i32) + (i32.add) + (br 0) + ) + ) + (func (export "params-id-break") (result i32) + (i32.const 1) + (i32.const 2) + (block (param i32 i32) (result i32 i32) (br 0)) + (i32.add) + ) + + (func (export "effects") (result i32) + (local i32) + (block + (local.set 0 (i32.const 1)) + (local.set 0 (i32.mul (local.get 0) (i32.const 3))) + (local.set 0 (i32.sub (local.get 0) (i32.const 5))) + (local.set 0 (i32.mul (local.get 0) (i32.const 7))) + (br 0) + (local.set 0 (i32.mul (local.get 0) (i32.const 100))) + ) + (i32.eq (local.get 0) (i32.const -14)) + ) + + (type $block-sig-1 (func)) + (type $block-sig-2 (func (result i32))) + (type $block-sig-3 (func (param $x i32))) + (type $block-sig-4 (func (param i32 f64 i32) (result i32 f64 i32))) + + (func (export "type-use") + (block (type $block-sig-1)) + (block (type $block-sig-2) (i32.const 0)) + (block (type $block-sig-3) (drop)) + (i32.const 0) (f64.const 0) (i32.const 0) + (block (type $block-sig-4)) + (drop) (drop) (drop) + (block (type $block-sig-2) (result i32) (i32.const 0)) + (block (type $block-sig-3) (param i32) (drop)) + (i32.const 0) (f64.const 0) (i32.const 0) + (block (type $block-sig-4) + (param i32) (param f64 i32) (result i32 f64) (result i32) + ) + (drop) (drop) (drop) + ) +) + +(assert_return (invoke "empty")) +(assert_return (invoke "singular") (i32.const 7)) +(assert_return (invoke "multi") (i32.const 8)) +(assert_return (invoke "nested") (i32.const 9)) +(assert_return (invoke "deep") (i32.const 150)) + +(assert_return (invoke "as-select-first") (i32.const 1)) +(assert_return (invoke "as-select-mid") (i32.const 2)) +(assert_return (invoke "as-select-last") (i32.const 2)) + +(assert_return (invoke "as-loop-first") (i32.const 1)) +(assert_return (invoke "as-loop-mid") (i32.const 1)) +(assert_return (invoke "as-loop-last") (i32.const 1)) + +(assert_return (invoke "as-if-condition")) +(assert_return (invoke "as-if-then") (i32.const 1)) +(assert_return (invoke "as-if-else") (i32.const 2)) + +(assert_return (invoke "as-br_if-first") (i32.const 1)) +(assert_return (invoke "as-br_if-last") (i32.const 2)) + +(assert_return (invoke "as-br_table-first") (i32.const 1)) +(assert_return (invoke "as-br_table-last") (i32.const 2)) + +(assert_return (invoke "as-call_indirect-first") (i32.const 1)) +(assert_return (invoke "as-call_indirect-mid") (i32.const 2)) +(assert_return (invoke "as-call_indirect-last") (i32.const 1)) + +(assert_return (invoke "as-store-first")) +(assert_return (invoke "as-store-last")) + +(assert_return (invoke "as-memory.grow-value") (i32.const 1)) +(assert_return (invoke "as-call-value") (i32.const 1)) +(assert_return (invoke "as-return-value") (i32.const 1)) +(assert_return (invoke "as-drop-operand")) +(assert_return (invoke "as-br-value") (i32.const 1)) +(assert_return (invoke "as-local.set-value") (i32.const 1)) +(assert_return (invoke "as-local.tee-value") (i32.const 1)) +(assert_return (invoke "as-global.set-value") (i32.const 1)) +(assert_return (invoke "as-load-operand") (i32.const 1)) + +(assert_return (invoke "as-unary-operand") (i32.const 0)) +(assert_return (invoke "as-binary-operand") (i32.const 12)) +(assert_return (invoke "as-test-operand") (i32.const 0)) +(assert_return (invoke "as-compare-operand") (i32.const 0)) +(assert_return (invoke "as-binary-operands") (i32.const 12)) +(assert_return (invoke "as-compare-operands") (i32.const 0)) +(assert_return (invoke "as-mixed-operands") (i32.const 27)) + +(assert_return (invoke "break-bare") (i32.const 19)) +(assert_return (invoke "break-value") (i32.const 18)) +(assert_return (invoke "break-multi-value") + (i32.const 18) (i32.const -18) (i64.const 18) +) +(assert_return (invoke "break-repeated") (i32.const 18)) +(assert_return (invoke "break-inner") (i32.const 0xf)) + +(assert_return (invoke "param") (i32.const 3)) +(assert_return (invoke "params") (i32.const 3)) +(assert_return (invoke "params-id") (i32.const 3)) +(assert_return (invoke "param-break") (i32.const 3)) +(assert_return (invoke "params-break") (i32.const 3)) +(assert_return (invoke "params-id-break") (i32.const 3)) + +(assert_return (invoke "effects") (i32.const 1)) + +(assert_return (invoke "type-use")) + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (type $sig) (result i32) (param i32)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (param i32) (type $sig) (result i32)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (param i32) (result i32) (type $sig)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (result i32) (type $sig) (param i32)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (result i32) (param i32) (type $sig)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func (i32.const 0) (block (result i32) (param i32)))" + ) + "unexpected token" +) + +(assert_malformed + (module quote "(func (i32.const 0) (block (param $x i32) (drop)))") + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func))" + "(func (block (type $sig) (result i32) (i32.const 0)) (unreachable))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (block (type $sig) (result i32) (i32.const 0)) (unreachable))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (block (type $sig) (param i32) (drop)) (unreachable))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(func (i32.const 0) (block (type $sig) (param i32) (result i32)) (unreachable))" + ) + "inline function type" +) + +(assert_invalid + (module + (type $sig (func)) + (func (block (type $sig) (i32.const 0))) + ) + "type mismatch" +) + +(assert_invalid + (module (func $type-empty-i32 (result i32) (block))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-i64 (result i64) (block))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f32 (result f32) (block))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f64 (result f64) (block))) + "type mismatch" +) + +(assert_invalid + (module (func $type-value-i32-vs-void + (block (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-i64-vs-void + (block (i64.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f32-vs-void + (block (f32.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f64-vs-void + (block (f64.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-nums-vs-void + (block (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-empty-vs-i32 (result i32) + (block (result i32)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-empty-vs-i64 (result i64) + (block (result i64)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-empty-vs-f32 (result f32) + (block (result f32)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-empty-vs-f64 (result f64) + (block (result f64)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-empty-vs-nums (result i32 i32) + (block (result i32 i32)) + )) + "type mismatch" +) + +(assert_invalid + (module + (func $type-value-empty-in-block + (i32.const 0) + (block (block (result i32)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-in-loop + (i32.const 0) + (loop (block (result i32)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-in-then + (i32.const 0) (i32.const 0) + (if (then (block (result i32)) (drop))) + ) + ) + "type mismatch" +) + +(assert_invalid + (module (func $type-value-void-vs-i32 (result i32) + (block (result i32) (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-void-vs-i64 (result i64) + (block (result i64) (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-void-vs-f32 (result f32) + (block (result f32) (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-void-vs-f64 (result f64) + (block (result f64) (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-void-vs-nums (result i32 i32) + (block (result i32 i32) (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-i32-vs-i64 (result i32) + (block (result i32) (i64.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-i32-vs-f32 (result i32) + (block (result i32) (f32.const 0.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-i32-vs-f64 (result i32) + (block (result i32) (f64.const 0.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-i64-vs-i32 (result i64) + (block (result i64) (i32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-i64-vs-f32 (result i64) + (block (result i64) (f32.const 0.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-i64-vs-f64 (result i64) + (block (result i64) (f64.const 0.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f32-vs-i32 (result f32) + (block (result f32) (i32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f32-vs-i64 (result f32) + (block (result f32) (i64.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f32-vs-f64 (result f32) + (block (result f32) (f64.const 0.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f64-vs-i32 (result f64) + (block (result f64) (i32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f64-vs-i64 (result f64) + (block (result f64) (i64.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-f64-vs-f32 (result f32) + (block (result f64) (f32.const 0.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-num-vs-nums (result i32 i32) + (block (result i32 i32) (i32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-partial-vs-nums (result i32 i32) + (i32.const 1) (block (result i32 i32) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-nums-vs-num (result i32) + (block (result i32) (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-value-unreached-select-i32-i64 (result i32) + (block (result i64) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-i32-f32 (result i32) + (block (result f32) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-i32-f64 (result i32) + (block (result f64) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-i64-i32 (result i64) + (block (result i32) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-i64-f32 (result i64) + (block (result f32) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-i64-f64 (result i64) + (block (result f64) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-f32-i32 (result f32) + (block (result i32) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-f32-i64 (result f32) + (block (result i64) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-f32-f64 (result f32) + (block (result f64) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-f64-i32 (result f64) + (block (result i32) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-f64-i64 (result f64) + (block (result i64) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select-f64-f32 (result f64) + (block (result f32) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-last-void-vs-i32 (result i32) + (block (result i32) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-void-vs-i64 (result i64) + (block (result i64) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-void-vs-f32 (result f32) + (block (result f32) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-void-vs-f64 (result f64) + (block (result f64) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-void-vs-nums (result i32 i32) + (block (result i32 i32) (br 0)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-empty-vs-i32 (result i32) + (block (result i32) (br 0) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-empty-vs-i64 (result i64) + (block (result i64) (br 0) (i64.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-empty-vs-f32 (result f32) + (block (result f32) (br 0) (f32.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-empty-vs-f64 (result f64) + (block (result f64) (br 0) (f64.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-empty-vs-nums (result i32 i32) + (block (result i32 i32) (br 0) (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-void-vs-i32 (result i32) + (block (result i32) (br 0 (nop)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-void-vs-i64 (result i64) + (block (result i64) (br 0 (nop)) (i64.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-void-vs-f32 (result f32) + (block (result f32) (br 0 (nop)) (f32.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-void-vs-f64 (result f64) + (block (result f64) (br 0 (nop)) (f64.const 1.0)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-i32-vs-i64 (result i32) + (block (result i32) (br 0 (i64.const 1)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-i32-vs-f32 (result i32) + (block (result i32) (br 0 (f32.const 1.0)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-i32-vs-f64 (result i32) + (block (result i32) (br 0 (f64.const 1.0)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-i64-vs-i32 (result i64) + (block (result i64) (br 0 (i32.const 1)) (i64.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-i64-vs-f32 (result i64) + (block (result i64) (br 0 (f32.const 1.0)) (i64.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-i64-vs-f64 (result i64) + (block (result i64) (br 0 (f64.const 1.0)) (i64.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-f32-vs-i32 (result f32) + (block (result f32) (br 0 (i32.const 1)) (f32.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-f32-vs-i64 (result f32) + (block (result f32) (br 0 (i64.const 1)) (f32.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-f32-vs-f64 (result f32) + (block (result f32) (br 0 (f64.const 1.0)) (f32.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-f64-vs-i32 (result f64) + (block (result i64) (br 0 (i32.const 1)) (f64.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-f64-vs-i64 (result f64) + (block (result f64) (br 0 (i64.const 1)) (f64.const 1.0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-f64-vs-f32 (result f64) + (block (result f64) (br 0 (f32.const 1.0)) (f64.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-num-vs-nums (result i32 i32) + (block (result i32 i32) (br 0 (i32.const 0)) (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-partial-vs-nums (result i32 i32) + (i32.const 1) (block (result i32 i32) (br 0 (i32.const 0)) (i32.const 2)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-first-void-vs-i32 (result i32) + (block (result i32) (br 0 (nop)) (br 0 (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-void-vs-i64 (result i64) + (block (result i64) (br 0 (nop)) (br 0 (i64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-void-vs-f32 (result f32) + (block (result f32) (br 0 (nop)) (br 0 (f32.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-void-vs-f64 (result f64) + (block (result f64) (br 0 (nop)) (br 0 (f64.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-void-vs-nums (result i32 i32) + (block (result i32 i32) (br 0 (nop)) (br 0 (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-first-i32-vs-i64 (result i32) + (block (result i32) (br 0 (i64.const 1)) (br 0 (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-i32-vs-f32 (result i32) + (block (result i32) (br 0 (f32.const 1.0)) (br 0 (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-i32-vs-f64 (result i32) + (block (result i32) (br 0 (f64.const 1.0)) (br 0 (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-i64-vs-i32 (result i64) + (block (result i64) (br 0 (i32.const 1)) (br 0 (i64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-i64-vs-f32 (result i64) + (block (result i64) (br 0 (f32.const 1.0)) (br 0 (i64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-i64-vs-f64 (result i64) + (block (result i64) (br 0 (f64.const 1.0)) (br 0 (i64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-f32-vs-i32 (result f32) + (block (result f32) (br 0 (i32.const 1)) (br 0 (f32.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-f32-vs-i64 (result f32) + (block (result f32) (br 0 (i64.const 1)) (br 0 (f32.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-f32-vs-f64 (result f32) + (block (result f32) (br 0 (f64.const 1.0)) (br 0 (f32.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-f64-vs-i32 (result f64) + (block (result f64) (br 0 (i32.const 1)) (br 0 (f64.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-f64-vs-i64 (result f64) + (block (result f64) (br 0 (i64.const 1)) (br 0 (f64.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-f64-vs-f32 (result f64) + (block (result f64) (br 0 (f32.const 1.0)) (br 0 (f64.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-num-vs-nums (result i32 i32) + (block (result i32 i32) (br 0 (i32.const 0)) (br 0 (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-nested-i32-vs-void + (block (result i32) (block (result i32) (br 1 (i32.const 1))) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-i64-vs-void + (block (result i64) (block (result i64) (br 1 (i64.const 1))) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f32-vs-void + (block (result f32) (block (result f32) (br 1 (f32.const 1.0))) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f64-vs-void + (block (result f64) (block (result f64) (br 1 (f64.const 1.0))) (br 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-nums-vs-void + (block (result i32 i32) (block (result i32 i32) (br 1 (i32.const 1) (i32.const 2))) (br 0)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-nested-empty-vs-i32 (result i32) + (block (result i32) (block (br 1)) (br 0 (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-empty-vs-i64 (result i64) + (block (result i64) (block (br 1)) (br 0 (i64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-empty-vs-f32 (result f32) + (block (result f32) (block (br 1)) (br 0 (f32.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-empty-vs-f64 (result f64) + (block (result f64) (block (br 1)) (br 0 (f64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-empty-vs-nums (result i32 i32) + (block (result i32 i32) (block (br 1)) (br 0 (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-nested-void-vs-i32 (result i32) + (block (result i32) (block (result i32) (br 1 (nop))) (br 0 (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-void-vs-i64 (result i64) + (block (result i64) (block (result i64) (br 1 (nop))) (br 0 (i64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-void-vs-f32 (result f32) + (block (result f32) (block (result f32) (br 1 (nop))) (br 0 (f32.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-void-vs-f64 (result f64) + (block (result f64) (block (result f64) (br 1 (nop))) (br 0 (f64.const 1.0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-void-vs-nums (result i32 i32) + (block (result i32 i32) (block (result i32 i32) (br 1 (nop))) (br 0 (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-nested-i32-vs-i64 (result i32) + (block (result i32) + (block (result i32) (br 1 (i64.const 1))) (br 0 (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-i32-vs-f32 (result i32) + (block (result i32) + (block (result i32) (br 1 (f32.const 1.0))) (br 0 (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-i32-vs-f64 (result i32) + (block (result i32) + (block (result i32) (br 1 (f64.const 1.0))) (br 0 (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-i64-vs-i32 (result i64) + (block (result i64) + (block (result i64) (br 1 (i32.const 1))) (br 0 (i64.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-i64-vs-f32 (result i64) + (block (result i64) + (block (result i64) (br 1 (f32.const 1.0))) (br 0 (i64.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-i64-vs-f64 (result i64) + (block (result i64) + (block (result i64) (br 1 (f64.const 1.0))) (br 0 (i64.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f32-vs-i32 (result f32) + (block (result f32) + (block (result f32) (br 1 (i32.const 1))) (br 0 (f32.const 1.0)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f32-vs-i64 (result f32) + (block (result f32) + (block (result f32) (br 1 (i64.const 1))) (br 0 (f32.const 1.0)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f32-vs-f64 (result f32) + (block (result f32) + (block (result f32) (br 1 (f64.const 1.0))) (br 0 (f32.const 1.0)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f64-vs-i32 (result f64) + (block (result f64) + (block (result f64) (br 1 (i32.const 1))) (br 0 (f64.const 1.0)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f64-vs-i64 (result f64) + (block (result f64) + (block (result f64) (br 1 (i64.const 1))) (br 0 (f64.const 1.0)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-f64-vs-f32 (result f64) + (block (result f64) + (block (result f64) (br 1 (f32.const 1.0))) (br 0 (f64.const 1.0)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-num-vs-nums (result i32 i32) + (block (result i32 i32) + (block (result i32 i32) (br 1 (i32.const 0))) (br 0 (i32.const 1) (i32.const 2)) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-operand-empty-vs-i32 (result i32) + (i32.ctz (block (br 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-empty-vs-i64 (result i64) + (i64.ctz (block (br 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-empty-vs-f32 (result f32) + (f32.floor (block (br 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-empty-vs-f64 (result f64) + (f64.floor (block (br 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-empty-vs-nums (result i32) + (i32.add (block (br 0))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-operand-void-vs-i32 (result i32) + (i32.ctz (block (br 0 (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-void-vs-i64 (result i64) + (i64.ctz (block (br 0 (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-void-vs-f32 (result f32) + (f32.floor (block (br 0 (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-void-vs-f64 (result f64) + (f64.floor (block (br 0 (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-void-vs-nums (result i32) + (i32.add (block (br 0 (nop)))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-operand-i32-vs-i64 (result i32) + (i64.ctz (block (br 0 (i64.const 9)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-i32-vs-f32 (result i32) + (f32.floor (block (br 0 (f32.const 9.0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-i32-vs-f64 (result i32) + (f64.floor (block (br 0 (f64.const 9.0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-i64-vs-i32 (result i64) + (i32.ctz (block (br 0 (i32.const 9)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-i64-vs-f32 (result i64) + (f32.floor (block (br 0 (f32.const 9.0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-i64-vs-f64 (result i64) + (f64.floor (block (br 0 (f64.const 9.0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-f32-vs-i32 (result f32) + (i32.ctz (block (br 0 (i32.const 9)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-f32-vs-i64 (result f32) + (i64.ctz (block (br 0 (i64.const 9)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-f32-vs-f64 (result f32) + (f64.floor (block (br 0 (f64.const 9.0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-f64-vs-i32 (result f64) + (i32.ctz (block (br 0 (i32.const 9)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-f64-vs-i64 (result f64) + (i64.ctz (block (br 0 (i64.const 9)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-f64-vs-f32 (result f64) + (f32.floor (block (br 0 (f32.const 9.0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-operand-num-vs-nums (result i32) + (i32.add (block (br 0 (i64.const 9) (i32.const 10)))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-param-void-vs-num + (block (param i32) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (block (param i32 f64) (drop) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (f32.const 0) (block (param i32) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (f32.const 0) (block (param f32 i32) (drop) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-nested-void-vs-num + (block (block (param i32) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (block (block (param i32 f64) (drop) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (block (f32.const 0) (block (param i32) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (block (f32.const 0) (block (param f32 i32) (drop) (drop))) + )) + "type mismatch" +) + +(assert_malformed + (module quote "(func (param i32) (result i32) block (param $x i32) end)") + "unexpected token" +) +(assert_malformed + (module quote "(func (param i32) (result i32) (block (param $x i32)))") + "unexpected token" +) + + +(assert_malformed + (module quote "(func block end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func block $a end $l)") + "mismatching label" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/multi-value/br.wast b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/br.wast new file mode 100644 index 000000000..35f707c92 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/br.wast @@ -0,0 +1,657 @@ +;; Test `br` operator + +(module + ;; Auxiliary definition + (func $dummy) + + (func (export "type-i32") (block (drop (i32.ctz (br 0))))) + (func (export "type-i64") (block (drop (i64.ctz (br 0))))) + (func (export "type-f32") (block (drop (f32.neg (br 0))))) + (func (export "type-f64") (block (drop (f64.neg (br 0))))) + (func (export "type-i32-i32") (block (drop (i32.add (br 0))))) + (func (export "type-i64-i64") (block (drop (i64.add (br 0))))) + (func (export "type-f32-f32") (block (drop (f32.add (br 0))))) + (func (export "type-f64-f64") (block (drop (f64.add (br 0))))) + + (func (export "type-i32-value") (result i32) + (block (result i32) (i32.ctz (br 0 (i32.const 1)))) + ) + (func (export "type-i64-value") (result i64) + (block (result i64) (i64.ctz (br 0 (i64.const 2)))) + ) + (func (export "type-f32-value") (result f32) + (block (result f32) (f32.neg (br 0 (f32.const 3)))) + ) + (func (export "type-f64-value") (result f64) + (block (result f64) (f64.neg (br 0 (f64.const 4)))) + ) + (func (export "type-f64-f64-value") (result f64 f64) + (block (result f64 f64) + (f64.add (br 0 (f64.const 4) (f64.const 5))) (f64.const 6) + ) + ) + + (func (export "as-block-first") + (block (br 0) (call $dummy)) + ) + (func (export "as-block-mid") + (block (call $dummy) (br 0) (call $dummy)) + ) + (func (export "as-block-last") + (block (nop) (call $dummy) (br 0)) + ) + (func (export "as-block-value") (result i32) + (block (result i32) (nop) (call $dummy) (br 0 (i32.const 2))) + ) + + (func (export "as-loop-first") (result i32) + (block (result i32) (loop (result i32) (br 1 (i32.const 3)) (i32.const 2))) + ) + (func (export "as-loop-mid") (result i32) + (block (result i32) + (loop (result i32) (call $dummy) (br 1 (i32.const 4)) (i32.const 2)) + ) + ) + (func (export "as-loop-last") (result i32) + (block (result i32) + (loop (result i32) (nop) (call $dummy) (br 1 (i32.const 5))) + ) + ) + + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (br 0 (i32.const 9)))) + ) + + (func (export "as-br_if-cond") + (block (br_if 0 (br 0))) + ) + (func (export "as-br_if-value") (result i32) + (block (result i32) + (drop (br_if 0 (br 0 (i32.const 8)) (i32.const 1))) (i32.const 7) + ) + ) + (func (export "as-br_if-value-cond") (result i32) + (block (result i32) + (drop (br_if 0 (i32.const 6) (br 0 (i32.const 9)))) (i32.const 7) + ) + ) + + (func (export "as-br_table-index") + (block (br_table 0 0 0 (br 0))) + ) + (func (export "as-br_table-value") (result i32) + (block (result i32) + (br_table 0 0 0 (br 0 (i32.const 10)) (i32.const 1)) (i32.const 7) + ) + ) + (func (export "as-br_table-value-index") (result i32) + (block (result i32) + (br_table 0 0 (i32.const 6) (br 0 (i32.const 11))) (i32.const 7) + ) + ) + + (func (export "as-return-value") (result i64) + (block (result i64) (return (br 0 (i64.const 7)))) + ) + (func (export "as-return-values") (result i32 i64) + (i32.const 2) + (block (result i64) (return (br 0 (i32.const 1) (i64.const 7)))) + ) + + (func (export "as-if-cond") (result i32) + (block (result i32) + (if (result i32) (br 0 (i32.const 2)) + (then (i32.const 0)) + (else (i32.const 1)) + ) + ) + ) + (func (export "as-if-then") (param i32 i32) (result i32) + (block (result i32) + (if (result i32) (local.get 0) + (then (br 1 (i32.const 3))) + (else (local.get 1)) + ) + ) + ) + (func (export "as-if-else") (param i32 i32) (result i32) + (block (result i32) + (if (result i32) (local.get 0) + (then (local.get 1)) + (else (br 1 (i32.const 4))) + ) + ) + ) + + (func (export "as-select-first") (param i32 i32) (result i32) + (block (result i32) + (select (br 0 (i32.const 5)) (local.get 0) (local.get 1)) + ) + ) + (func (export "as-select-second") (param i32 i32) (result i32) + (block (result i32) + (select (local.get 0) (br 0 (i32.const 6)) (local.get 1)) + ) + ) + (func (export "as-select-cond") (result i32) + (block (result i32) + (select (i32.const 0) (i32.const 1) (br 0 (i32.const 7))) + ) + ) + (func (export "as-select-all") (result i32) + (block (result i32) (select (br 0 (i32.const 8)))) + ) + + (func $f (param i32 i32 i32) (result i32) (i32.const -1)) + (func (export "as-call-first") (result i32) + (block (result i32) + (call $f (br 0 (i32.const 12)) (i32.const 2) (i32.const 3)) + ) + ) + (func (export "as-call-mid") (result i32) + (block (result i32) + (call $f (i32.const 1) (br 0 (i32.const 13)) (i32.const 3)) + ) + ) + (func (export "as-call-last") (result i32) + (block (result i32) + (call $f (i32.const 1) (i32.const 2) (br 0 (i32.const 14))) + ) + ) + (func (export "as-call-all") (result i32) + (block (result i32) (call $f (br 0 (i32.const 15)))) + ) + + (type $sig (func (param i32 i32 i32) (result i32))) + (table funcref (elem $f)) + (func (export "as-call_indirect-func") (result i32) + (block (result i32) + (call_indirect (type $sig) + (br 0 (i32.const 20)) + (i32.const 1) (i32.const 2) (i32.const 3) + ) + ) + ) + (func (export "as-call_indirect-first") (result i32) + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) + (br 0 (i32.const 21)) (i32.const 2) (i32.const 3) + ) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) + (i32.const 1) (br 0 (i32.const 22)) (i32.const 3) + ) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) + (i32.const 1) (i32.const 2) (br 0 (i32.const 23)) + ) + ) + ) + (func (export "as-call_indirect-all") (result i32) + (block (result i32) (call_indirect (type $sig) (br 0 (i32.const 24)))) + ) + + (func (export "as-local.set-value") (result i32) (local f32) + (block (result i32) (local.set 0 (br 0 (i32.const 17))) (i32.const -1)) + ) + (func (export "as-local.tee-value") (result i32) (local i32) + (block (result i32) (local.tee 0 (br 0 (i32.const 1)))) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (result i32) + (block (result i32) (global.set $a (br 0 (i32.const 1)))) + ) + + (memory 1) + (func (export "as-load-address") (result f32) + (block (result f32) (f32.load (br 0 (f32.const 1.7)))) + ) + (func (export "as-loadN-address") (result i64) + (block (result i64) (i64.load8_s (br 0 (i64.const 30)))) + ) + + (func (export "as-store-address") (result i32) + (block (result i32) + (f64.store (br 0 (i32.const 30)) (f64.const 7)) (i32.const -1) + ) + ) + (func (export "as-store-value") (result i32) + (block (result i32) + (i64.store (i32.const 2) (br 0 (i32.const 31))) (i32.const -1) + ) + ) + (func (export "as-store-both") (result i32) + (block (result i32) + (i64.store (br 0 (i32.const 32))) (i32.const -1) + ) + ) + + (func (export "as-storeN-address") (result i32) + (block (result i32) + (i32.store8 (br 0 (i32.const 32)) (i32.const 7)) (i32.const -1) + ) + ) + (func (export "as-storeN-value") (result i32) + (block (result i32) + (i64.store16 (i32.const 2) (br 0 (i32.const 33))) (i32.const -1) + ) + ) + (func (export "as-storeN-both") (result i32) + (block (result i32) + (i64.store16 (br 0 (i32.const 34))) (i32.const -1) + ) + ) + + (func (export "as-unary-operand") (result f32) + (block (result f32) (f32.neg (br 0 (f32.const 3.4)))) + ) + + (func (export "as-binary-left") (result i32) + (block (result i32) (i32.add (br 0 (i32.const 3)) (i32.const 10))) + ) + (func (export "as-binary-right") (result i64) + (block (result i64) (i64.sub (i64.const 10) (br 0 (i64.const 45)))) + ) + (func (export "as-binary-both") (result i32) + (block (result i32) (i32.add (br 0 (i32.const 46)))) + ) + + (func (export "as-test-operand") (result i32) + (block (result i32) (i32.eqz (br 0 (i32.const 44)))) + ) + + (func (export "as-compare-left") (result i32) + (block (result i32) (f64.le (br 0 (i32.const 43)) (f64.const 10))) + ) + (func (export "as-compare-right") (result i32) + (block (result i32) (f32.ne (f32.const 10) (br 0 (i32.const 42)))) + ) + (func (export "as-compare-both") (result i32) + (block (result i32) (f64.le (br 0 (i32.const 44)))) + ) + + (func (export "as-convert-operand") (result i32) + (block (result i32) (i32.wrap_i64 (br 0 (i32.const 41)))) + ) + + (func (export "as-memory.grow-size") (result i32) + (block (result i32) (memory.grow (br 0 (i32.const 40)))) + ) + + (func (export "nested-block-value") (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (call $dummy) + (i32.add (i32.const 4) (br 0 (i32.const 8))) + ) + ) + ) + + (func (export "nested-br-value") (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (block (result i32) + (drop (i32.const 4)) + (br 0 (br 1 (i32.const 8))) + ) + ) + (i32.const 16) + ) + ) + ) + + (func (export "nested-br_if-value") (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (block (result i32) + (drop (i32.const 4)) + (drop (br_if 0 (br 1 (i32.const 8)) (i32.const 1))) + (i32.const 32) + ) + ) + (i32.const 16) + ) + ) + ) + + (func (export "nested-br_if-value-cond") (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop (br_if 0 (i32.const 4) (br 0 (i32.const 8)))) + (i32.const 16) + ) + ) + ) + + (func (export "nested-br_table-value") (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (block (result i32) + (drop (i32.const 4)) + (br_table 0 (br 1 (i32.const 8)) (i32.const 1)) + ) + ) + (i32.const 16) + ) + ) + ) + + (func (export "nested-br_table-value-index") (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (br_table 0 (i32.const 4) (br 0 (i32.const 8))) + (i32.const 16) + ) + ) + ) +) + +(assert_return (invoke "type-i32")) +(assert_return (invoke "type-i64")) +(assert_return (invoke "type-f32")) +(assert_return (invoke "type-f64")) +(assert_return (invoke "type-i32-i32")) +(assert_return (invoke "type-i64-i64")) +(assert_return (invoke "type-f32-f32")) +(assert_return (invoke "type-f64-f64")) + +(assert_return (invoke "type-i32-value") (i32.const 1)) +(assert_return (invoke "type-i64-value") (i64.const 2)) +(assert_return (invoke "type-f32-value") (f32.const 3)) +(assert_return (invoke "type-f64-value") (f64.const 4)) +(assert_return (invoke "type-f64-f64-value") (f64.const 4) (f64.const 5)) + +(assert_return (invoke "as-block-first")) +(assert_return (invoke "as-block-mid")) +(assert_return (invoke "as-block-last")) +(assert_return (invoke "as-block-value") (i32.const 2)) + +(assert_return (invoke "as-loop-first") (i32.const 3)) +(assert_return (invoke "as-loop-mid") (i32.const 4)) +(assert_return (invoke "as-loop-last") (i32.const 5)) + +(assert_return (invoke "as-br-value") (i32.const 9)) + +(assert_return (invoke "as-br_if-cond")) +(assert_return (invoke "as-br_if-value") (i32.const 8)) +(assert_return (invoke "as-br_if-value-cond") (i32.const 9)) + +(assert_return (invoke "as-br_table-index")) +(assert_return (invoke "as-br_table-value") (i32.const 10)) +(assert_return (invoke "as-br_table-value-index") (i32.const 11)) + +(assert_return (invoke "as-return-value") (i64.const 7)) +(assert_return (invoke "as-return-values") (i32.const 2) (i64.const 7)) + +(assert_return (invoke "as-if-cond") (i32.const 2)) +(assert_return (invoke "as-if-then" (i32.const 1) (i32.const 6)) (i32.const 3)) +(assert_return (invoke "as-if-then" (i32.const 0) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-if-else" (i32.const 0) (i32.const 6)) (i32.const 4)) +(assert_return (invoke "as-if-else" (i32.const 1) (i32.const 6)) (i32.const 6)) + +(assert_return (invoke "as-select-first" (i32.const 0) (i32.const 6)) (i32.const 5)) +(assert_return (invoke "as-select-first" (i32.const 1) (i32.const 6)) (i32.const 5)) +(assert_return (invoke "as-select-second" (i32.const 0) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-select-second" (i32.const 1) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-select-cond") (i32.const 7)) +(assert_return (invoke "as-select-all") (i32.const 8)) + +(assert_return (invoke "as-call-first") (i32.const 12)) +(assert_return (invoke "as-call-mid") (i32.const 13)) +(assert_return (invoke "as-call-last") (i32.const 14)) +(assert_return (invoke "as-call-all") (i32.const 15)) + +(assert_return (invoke "as-call_indirect-func") (i32.const 20)) +(assert_return (invoke "as-call_indirect-first") (i32.const 21)) +(assert_return (invoke "as-call_indirect-mid") (i32.const 22)) +(assert_return (invoke "as-call_indirect-last") (i32.const 23)) +(assert_return (invoke "as-call_indirect-all") (i32.const 24)) + +(assert_return (invoke "as-local.set-value") (i32.const 17)) +(assert_return (invoke "as-local.tee-value") (i32.const 1)) +(assert_return (invoke "as-global.set-value") (i32.const 1)) + +(assert_return (invoke "as-load-address") (f32.const 1.7)) +(assert_return (invoke "as-loadN-address") (i64.const 30)) + +(assert_return (invoke "as-store-address") (i32.const 30)) +(assert_return (invoke "as-store-value") (i32.const 31)) +(assert_return (invoke "as-store-both") (i32.const 32)) +(assert_return (invoke "as-storeN-address") (i32.const 32)) +(assert_return (invoke "as-storeN-value") (i32.const 33)) +(assert_return (invoke "as-storeN-both") (i32.const 34)) + +(assert_return (invoke "as-unary-operand") (f32.const 3.4)) + +(assert_return (invoke "as-binary-left") (i32.const 3)) +(assert_return (invoke "as-binary-right") (i64.const 45)) +(assert_return (invoke "as-binary-both") (i32.const 46)) + +(assert_return (invoke "as-test-operand") (i32.const 44)) + +(assert_return (invoke "as-compare-left") (i32.const 43)) +(assert_return (invoke "as-compare-right") (i32.const 42)) +(assert_return (invoke "as-compare-both") (i32.const 44)) + +(assert_return (invoke "as-convert-operand") (i32.const 41)) + +(assert_return (invoke "as-memory.grow-size") (i32.const 40)) + +(assert_return (invoke "nested-block-value") (i32.const 9)) +(assert_return (invoke "nested-br-value") (i32.const 9)) +(assert_return (invoke "nested-br_if-value") (i32.const 9)) +(assert_return (invoke "nested-br_if-value-cond") (i32.const 9)) +(assert_return (invoke "nested-br_table-value") (i32.const 9)) +(assert_return (invoke "nested-br_table-value-index") (i32.const 9)) + +(assert_invalid + (module (func $type-arg-empty-vs-num (result i32) + (block (result i32) (br 0) (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-arg-void-vs-num (result i32) + (block (result i32) (br 0 (nop)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-void-vs-num-nested (result i32) + (block (result i32) (i32.const 0) (block (br 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-num-vs-num (result i32) + (block (result i32) (br 0 (i64.const 1)) (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module + (func $type-arg-empty-in-br + (i32.const 0) + (block (result i32) (br 0 (br 0))) (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-empty-in-br_if + (i32.const 0) + (block (result i32) (br_if 0 (br 0) (i32.const 1))) (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-empty-in-br_table + (i32.const 0) + (block (result i32) (br_table 0 (br 0))) (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-empty-in-return + (block (result i32) + (return (br 0)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-empty-in-select + (block (result i32) + (select (br 0) (i32.const 1) (i32.const 2)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-empty-in-call + (block (result i32) + (call 1 (br 0)) + ) + (i32.eqz) (drop) + ) + (func (param i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-arg-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (br 0) (i32.const 0) + ) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-empty-in-local.set + (local i32) + (block (result i32) + (local.set 0 (br 0)) (local.get 0) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-empty-in-local.tee + (local i32) + (block (result i32) + (local.tee 0 (br 0)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-arg-empty-in-global.set + (block (result i32) + (global.set $x (br 0)) (global.get $x) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-arg-empty-in-memory.grow + (block (result i32) + (memory.grow (br 0)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-arg-empty-in-load + (block (result i32) + (i32.load (br 0)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-arg-empty-in-store + (block (result i32) + (i32.store (br 0) (i32.const 0)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) + +(assert_invalid + (module (func $unbound-label (br 1))) + "unknown label" +) +(assert_invalid + (module (func $unbound-nested-label (block (block (br 5))))) + "unknown label" +) +(assert_invalid + (module (func $large-label (br 0x10000001))) + "unknown label" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/multi-value/call.wast b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/call.wast new file mode 100644 index 000000000..e4f854f7a --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/call.wast @@ -0,0 +1,518 @@ +;; Test `call` operator + +(module + ;; Auxiliary definitions + (func $const-i32 (result i32) (i32.const 0x132)) + (func $const-i64 (result i64) (i64.const 0x164)) + (func $const-f32 (result f32) (f32.const 0xf32)) + (func $const-f64 (result f64) (f64.const 0xf64)) + (func $const-i32-i64 (result i32 i64) (i32.const 0x132) (i64.const 0x164)) + + (func $id-i32 (param i32) (result i32) (local.get 0)) + (func $id-i64 (param i64) (result i64) (local.get 0)) + (func $id-f32 (param f32) (result f32) (local.get 0)) + (func $id-f64 (param f64) (result f64) (local.get 0)) + (func $id-i32-f64 (param i32 f64) (result i32 f64) + (local.get 0) (local.get 1) + ) + + (func $swap-i32-i32 (param i32 i32) (result i32 i32) + (local.get 1) (local.get 0) + ) + (func $swap-f32-f64 (param f32 f64) (result f64 f32) + (local.get 1) (local.get 0) + ) + (func $swap-f64-i32 (param f64 i32) (result i32 f64) + (local.get 1) (local.get 0) + ) + + (func $f32-i32 (param f32 i32) (result i32) (local.get 1)) + (func $i32-i64 (param i32 i64) (result i64) (local.get 1)) + (func $f64-f32 (param f64 f32) (result f32) (local.get 1)) + (func $i64-f64 (param i64 f64) (result f64) (local.get 1)) + + ;; Typing + + (func (export "type-i32") (result i32) (call $const-i32)) + (func (export "type-i64") (result i64) (call $const-i64)) + (func (export "type-f32") (result f32) (call $const-f32)) + (func (export "type-f64") (result f64) (call $const-f64)) + (func (export "type-i32-i64") (result i32 i64) (call $const-i32-i64)) + + (func (export "type-first-i32") (result i32) (call $id-i32 (i32.const 32))) + (func (export "type-first-i64") (result i64) (call $id-i64 (i64.const 64))) + (func (export "type-first-f32") (result f32) (call $id-f32 (f32.const 1.32))) + (func (export "type-first-f64") (result f64) (call $id-f64 (f64.const 1.64))) + + (func (export "type-second-i32") (result i32) + (call $f32-i32 (f32.const 32.1) (i32.const 32)) + ) + (func (export "type-second-i64") (result i64) + (call $i32-i64 (i32.const 32) (i64.const 64)) + ) + (func (export "type-second-f32") (result f32) + (call $f64-f32 (f64.const 64) (f32.const 32)) + ) + (func (export "type-second-f64") (result f64) + (call $i64-f64 (i64.const 64) (f64.const 64.1)) + ) + + (func (export "type-all-i32-f64") (result i32 f64) + (call $id-i32-f64 (i32.const 32) (f64.const 1.64)) + ) + (func (export "type-all-i32-i32") (result i32 i32) + (call $swap-i32-i32 (i32.const 1) (i32.const 2)) + ) + (func (export "type-all-f32-f64") (result f64 f32) + (call $swap-f32-f64 (f32.const 1) (f64.const 2)) + ) + (func (export "type-all-f64-i32") (result i32 f64) + (call $swap-f64-i32 (f64.const 1) (i32.const 2)) + ) + + ;; Composition + + (func (export "as-binary-all-operands") (result i32) + (i32.add (call $swap-i32-i32 (i32.const 3) (i32.const 4))) + ) + + (func (export "as-mixed-operands") (result i32) + (call $swap-i32-i32 (i32.const 3) (i32.const 4)) + (i32.const 5) + (i32.add) + (i32.mul) + ) + + (func (export "as-call-all-operands") (result i32 i32) + (call $swap-i32-i32 (call $swap-i32-i32 (i32.const 3) (i32.const 4))) + ) + + ;; Recursion + + (func $fac (export "fac") (param i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (i64.const 1)) + (else + (i64.mul + (local.get 0) + (call $fac (i64.sub (local.get 0) (i64.const 1))) + ) + ) + ) + ) + + (func $fac-acc (export "fac-acc") (param i64 i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (local.get 1)) + (else + (call $fac-acc + (i64.sub (local.get 0) (i64.const 1)) + (i64.mul (local.get 0) (local.get 1)) + ) + ) + ) + ) + + (func $fib (export "fib") (param i64) (result i64) + (if (result i64) (i64.le_u (local.get 0) (i64.const 1)) + (then (i64.const 1)) + (else + (i64.add + (call $fib (i64.sub (local.get 0) (i64.const 2))) + (call $fib (i64.sub (local.get 0) (i64.const 1))) + ) + ) + ) + ) + + (func $even (export "even") (param i64) (result i32) + (if (result i32) (i64.eqz (local.get 0)) + (then (i32.const 44)) + (else (call $odd (i64.sub (local.get 0) (i64.const 1)))) + ) + ) + (func $odd (export "odd") (param i64) (result i32) + (if (result i32) (i64.eqz (local.get 0)) + (then (i32.const 99)) + (else (call $even (i64.sub (local.get 0) (i64.const 1)))) + ) + ) + + ;; Stack exhaustion + + ;; Implementations are required to have every call consume some abstract + ;; resource towards exhausting some abstract finite limit, such that + ;; infinitely recursive test cases reliably trap in finite time. This is + ;; because otherwise applications could come to depend on it on those + ;; implementations and be incompatible with implementations that don't do + ;; it (or don't do it under the same circumstances). + + (func $runaway (export "runaway") (call $runaway)) + + (func $mutual-runaway1 (export "mutual-runaway") (call $mutual-runaway2)) + (func $mutual-runaway2 (call $mutual-runaway1)) + + ;; As parameter of control constructs and instructions + + (memory 1) + + (func (export "as-select-first") (result i32) + (select (call $const-i32) (i32.const 2) (i32.const 3)) + ) + (func (export "as-select-mid") (result i32) + (select (i32.const 2) (call $const-i32) (i32.const 3)) + ) + (func (export "as-select-last") (result i32) + (select (i32.const 2) (i32.const 3) (call $const-i32)) + ) + + (func (export "as-if-condition") (result i32) + (if (result i32) (call $const-i32) (then (i32.const 1)) (else (i32.const 2))) + ) + + (func (export "as-br_if-first") (result i32) + (block (result i32) (br_if 0 (call $const-i32) (i32.const 2))) + ) + (func (export "as-br_if-last") (result i32) + (block (result i32) (br_if 0 (i32.const 2) (call $const-i32))) + ) + + (func (export "as-br_table-first") (result i32) + (block (result i32) (call $const-i32) (i32.const 2) (br_table 0 0)) + ) + (func (export "as-br_table-last") (result i32) + (block (result i32) (i32.const 2) (call $const-i32) (br_table 0 0)) + ) + + (func $func (param i32 i32) (result i32) (local.get 0)) + (type $check (func (param i32 i32) (result i32))) + (table funcref (elem $func)) + (func (export "as-call_indirect-first") (result i32) + (block (result i32) + (call_indirect (type $check) + (call $const-i32) (i32.const 2) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 2) (call $const-i32) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 1) (i32.const 2) (call $const-i32) + ) + ) + ) + + (func (export "as-store-first") + (call $const-i32) (i32.const 1) (i32.store) + ) + (func (export "as-store-last") + (i32.const 10) (call $const-i32) (i32.store) + ) + + (func (export "as-memory.grow-value") (result i32) + (memory.grow (call $const-i32)) + ) + (func (export "as-return-value") (result i32) + (call $const-i32) (return) + ) + (func (export "as-drop-operand") + (call $const-i32) (drop) + ) + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (call $const-i32))) + ) + (func (export "as-local.set-value") (result i32) + (local i32) (local.set 0 (call $const-i32)) (local.get 0) + ) + (func (export "as-local.tee-value") (result i32) + (local i32) (local.tee 0 (call $const-i32)) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (result i32) + (global.set $a (call $const-i32)) + (global.get $a) + ) + (func (export "as-load-operand") (result i32) + (i32.load (call $const-i32)) + ) + + (func $dummy (param i32) (result i32) (local.get 0)) + (func $du (param f32) (result f32) (local.get 0)) + (func (export "as-unary-operand") (result f32) + (block (result f32) (f32.sqrt (call $du (f32.const 0x0p+0)))) + ) + + (func (export "as-binary-left") (result i32) + (block (result i32) (i32.add (call $dummy (i32.const 1)) (i32.const 10))) + ) + (func (export "as-binary-right") (result i32) + (block (result i32) (i32.sub (i32.const 10) (call $dummy (i32.const 1)))) + ) + + (func (export "as-test-operand") (result i32) + (block (result i32) (i32.eqz (call $dummy (i32.const 1)))) + ) + + (func (export "as-compare-left") (result i32) + (block (result i32) (i32.le_u (call $dummy (i32.const 1)) (i32.const 10))) + ) + (func (export "as-compare-right") (result i32) + (block (result i32) (i32.ne (i32.const 10) (call $dummy (i32.const 1)))) + ) + + (func (export "as-convert-operand") (result i64) + (block (result i64) (i64.extend_i32_s (call $dummy (i32.const 1)))) + ) + + ;; Test correct argument passing + + (func $return-from-long-argument-list-helper (param f32 i32 i32 f64 f32 f32 f32 f64 f32 i32 i32 f32 f64 i64 i64 i32 i64 i64 f32 i64 i64 i64 i32 f32 f32 f32 f64 f32 i32 i64 f32 f64 f64 f32 i32 f32 f32 f64 i64 f64 i32 i64 f32 f64 i32 i32 i32 i64 f64 i32 i64 i64 f64 f64 f64 f64 f64 f64 i32 f32 f64 f64 i32 i64 f32 f32 f32 i32 f64 f64 f64 f64 f64 f32 i64 i64 i32 i32 i32 f32 f64 i32 i64 f32 f32 f32 i32 i32 f32 f64 i64 f32 f64 f32 f32 f32 i32 f32 i64 i32) (result i32) + (local.get 99) + ) + + (func (export "return-from-long-argument-list") (param i32) (result i32) + (call $return-from-long-argument-list-helper (f32.const 0) (i32.const 0) (i32.const 0) (f64.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f64.const 0) (f32.const 0) (i32.const 0) (i32.const 0) (f32.const 0) (f64.const 0) (i64.const 0) (i64.const 0) (i32.const 0) (i64.const 0) (i64.const 0) (f32.const 0) (i64.const 0) (i64.const 0) (i64.const 0) (i32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f64.const 0) (f32.const 0) (i32.const 0) (i64.const 0) (f32.const 0) (f64.const 0) (f64.const 0) (f32.const 0) (i32.const 0) (f32.const 0) (f32.const 0) (f64.const 0) (i64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (f32.const 0) (f64.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (i64.const 0) (f64.const 0) (f64.const 0) (f64.const 0) (f64.const 0) (f64.const 0) (f64.const 0) (i32.const 0) (f32.const 0) (f64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (i32.const 0) (f64.const 0) (f64.const 0) (f64.const 0) (f64.const 0) (f64.const 0) (f32.const 0) (i64.const 0) (i64.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (f32.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (i32.const 0) (i32.const 0) (f32.const 0) (f64.const 0) (i64.const 0) (f32.const 0) (f64.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (i32.const 0) (f32.const 0) (i64.const 0) (local.get 0)) + ) +) + +(assert_return (invoke "type-i32") (i32.const 0x132)) +(assert_return (invoke "type-i64") (i64.const 0x164)) +(assert_return (invoke "type-f32") (f32.const 0xf32)) +(assert_return (invoke "type-f64") (f64.const 0xf64)) +(assert_return (invoke "type-i32-i64") (i32.const 0x132) (i64.const 0x164)) + +(assert_return (invoke "type-first-i32") (i32.const 32)) +(assert_return (invoke "type-first-i64") (i64.const 64)) +(assert_return (invoke "type-first-f32") (f32.const 1.32)) +(assert_return (invoke "type-first-f64") (f64.const 1.64)) + +(assert_return (invoke "type-second-i32") (i32.const 32)) +(assert_return (invoke "type-second-i64") (i64.const 64)) +(assert_return (invoke "type-second-f32") (f32.const 32)) +(assert_return (invoke "type-second-f64") (f64.const 64.1)) + +(assert_return (invoke "type-all-i32-f64") (i32.const 32) (f64.const 1.64)) +(assert_return (invoke "type-all-i32-i32") (i32.const 2) (i32.const 1)) +(assert_return (invoke "type-all-f32-f64") (f64.const 2) (f32.const 1)) +(assert_return (invoke "type-all-f64-i32") (i32.const 2) (f64.const 1)) + +(assert_return (invoke "as-binary-all-operands") (i32.const 7)) +(assert_return (invoke "as-mixed-operands") (i32.const 32)) +(assert_return (invoke "as-call-all-operands") (i32.const 3) (i32.const 4)) + +(assert_return (invoke "fac" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "fac" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac" (i64.const 5)) (i64.const 120)) +(assert_return (invoke "fac" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-acc" (i64.const 0) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 5) (i64.const 1)) (i64.const 120)) +(assert_return + (invoke "fac-acc" (i64.const 25) (i64.const 1)) + (i64.const 7034535277573963776) +) + +(assert_return (invoke "fib" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "fib" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fib" (i64.const 2)) (i64.const 2)) +(assert_return (invoke "fib" (i64.const 5)) (i64.const 8)) +(assert_return (invoke "fib" (i64.const 20)) (i64.const 10946)) + +(assert_return (invoke "even" (i64.const 0)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 1)) (i32.const 99)) +(assert_return (invoke "even" (i64.const 100)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 77)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 0)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 1)) (i32.const 44)) +(assert_return (invoke "odd" (i64.const 200)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 77)) (i32.const 44)) + +(assert_exhaustion (invoke "runaway") "call stack exhausted") +(assert_exhaustion (invoke "mutual-runaway") "call stack exhausted") + +(assert_return (invoke "as-select-first") (i32.const 0x132)) +(assert_return (invoke "as-select-mid") (i32.const 2)) +(assert_return (invoke "as-select-last") (i32.const 2)) + +(assert_return (invoke "as-if-condition") (i32.const 1)) + +(assert_return (invoke "as-br_if-first") (i32.const 0x132)) +(assert_return (invoke "as-br_if-last") (i32.const 2)) + +(assert_return (invoke "as-br_table-first") (i32.const 0x132)) +(assert_return (invoke "as-br_table-last") (i32.const 2)) + +(assert_return (invoke "as-call_indirect-first") (i32.const 0x132)) +(assert_return (invoke "as-call_indirect-mid") (i32.const 2)) +(assert_trap (invoke "as-call_indirect-last") "undefined element") + +(assert_return (invoke "as-store-first")) +(assert_return (invoke "as-store-last")) + +(assert_return (invoke "as-memory.grow-value") (i32.const 1)) +(assert_return (invoke "as-return-value") (i32.const 0x132)) +(assert_return (invoke "as-drop-operand")) +(assert_return (invoke "as-br-value") (i32.const 0x132)) +(assert_return (invoke "as-local.set-value") (i32.const 0x132)) +(assert_return (invoke "as-local.tee-value") (i32.const 0x132)) +(assert_return (invoke "as-global.set-value") (i32.const 0x132)) +(assert_return (invoke "as-load-operand") (i32.const 1)) + +(assert_return (invoke "as-unary-operand") (f32.const 0x0p+0)) +(assert_return (invoke "as-binary-left") (i32.const 11)) +(assert_return (invoke "as-binary-right") (i32.const 9)) +(assert_return (invoke "as-test-operand") (i32.const 0)) +(assert_return (invoke "as-compare-left") (i32.const 1)) +(assert_return (invoke "as-compare-right") (i32.const 1)) +(assert_return (invoke "as-convert-operand") (i64.const 1)) + +(assert_return (invoke "return-from-long-argument-list" (i32.const 42)) (i32.const 42)) + +;; Invalid typing + +(assert_invalid + (module + (func $type-void-vs-num (i32.eqz (call 1))) + (func) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-num-vs-num (i32.eqz (call 1))) + (func (result i64) (i64.const 1)) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $arity-0-vs-1 (call 1)) + (func (param i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $arity-0-vs-2 (call 1)) + (func (param f64 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $arity-1-vs-0 (call 1 (i32.const 1))) + (func) + ) + "type mismatch" +) +(assert_invalid + (module + (func $arity-2-vs-0 (call 1 (f64.const 2) (i32.const 1))) + (func) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $type-first-void-vs-num (call 1 (nop) (i32.const 1))) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-void-vs-num (call 1 (i32.const 1) (nop))) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-first-num-vs-num (call 1 (f64.const 1) (i32.const 1))) + (func (param i32 f64)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-num-vs-num (call 1 (i32.const 1) (f64.const 1))) + (func (param f64 i32)) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $type-first-empty-in-block + (block (call 1)) + ) + (func (param i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-empty-in-block + (block (call 1 (i32.const 0))) + ) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-first-empty-in-loop + (loop (call 1)) + ) + (func (param i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-empty-in-loop + (loop (call 1 (i32.const 0))) + ) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-first-empty-in-then + (if (i32.const 0) (then (call 1))) + ) + (func (param i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-empty-in-then + (if (i32.const 0) (then (call 1 (i32.const 0)))) + ) + (func (param i32 i32)) + ) + "type mismatch" +) + + +;; Unbound function + +(assert_invalid + (module (func $unbound-func (call 1))) + "unknown function" +) +(assert_invalid + (module (func $large-func (call 1012321300))) + "unknown function" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/multi-value/call_indirect.wast b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/call_indirect.wast new file mode 100644 index 000000000..224e3bef0 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/call_indirect.wast @@ -0,0 +1,971 @@ +;; Test `call_indirect` operator + +(module + ;; Auxiliary definitions + (type $proc (func)) + (type $out-i32 (func (result i32))) + (type $out-i64 (func (result i64))) + (type $out-f32 (func (result f32))) + (type $out-f64 (func (result f64))) + (type $out-f64-i32 (func (result f64 i32))) + (type $over-i32 (func (param i32) (result i32))) + (type $over-i64 (func (param i64) (result i64))) + (type $over-f32 (func (param f32) (result f32))) + (type $over-f64 (func (param f64) (result f64))) + (type $over-i32-f64 (func (param i32 f64) (result i32 f64))) + (type $swap-i32-i64 (func (param i32 i64) (result i64 i32))) + (type $f32-i32 (func (param f32 i32) (result i32))) + (type $i32-i64 (func (param i32 i64) (result i64))) + (type $f64-f32 (func (param f64 f32) (result f32))) + (type $i64-f64 (func (param i64 f64) (result f64))) + (type $over-i32-duplicate (func (param i32) (result i32))) + (type $over-i64-duplicate (func (param i64) (result i64))) + (type $over-f32-duplicate (func (param f32) (result f32))) + (type $over-f64-duplicate (func (param f64) (result f64))) + + (func $const-i32 (type $out-i32) (i32.const 0x132)) + (func $const-i64 (type $out-i64) (i64.const 0x164)) + (func $const-f32 (type $out-f32) (f32.const 0xf32)) + (func $const-f64 (type $out-f64) (f64.const 0xf64)) + (func $const-f64-i32 (type $out-f64-i32) (f64.const 0xf64) (i32.const 32)) + + (func $id-i32 (type $over-i32) (local.get 0)) + (func $id-i64 (type $over-i64) (local.get 0)) + (func $id-f32 (type $over-f32) (local.get 0)) + (func $id-f64 (type $over-f64) (local.get 0)) + (func $id-i32-f64 (type $over-i32-f64) (local.get 0) (local.get 1)) + (func $swap-i32-i64 (type $swap-i32-i64) (local.get 1) (local.get 0)) + + (func $i32-i64 (type $i32-i64) (local.get 1)) + (func $i64-f64 (type $i64-f64) (local.get 1)) + (func $f32-i32 (type $f32-i32) (local.get 1)) + (func $f64-f32 (type $f64-f32) (local.get 1)) + + (func $over-i32-duplicate (type $over-i32-duplicate) (local.get 0)) + (func $over-i64-duplicate (type $over-i64-duplicate) (local.get 0)) + (func $over-f32-duplicate (type $over-f32-duplicate) (local.get 0)) + (func $over-f64-duplicate (type $over-f64-duplicate) (local.get 0)) + + (table funcref + (elem + $const-i32 $const-i64 $const-f32 $const-f64 ;; 0..3 + $id-i32 $id-i64 $id-f32 $id-f64 ;; 4..7 + $f32-i32 $i32-i64 $f64-f32 $i64-f64 ;; 9..11 + $fac-i64 $fib-i64 $even $odd ;; 12..15 + $runaway $mutual-runaway1 $mutual-runaway2 ;; 16..18 + $over-i32-duplicate $over-i64-duplicate ;; 19..20 + $over-f32-duplicate $over-f64-duplicate ;; 21..22 + $fac-i32 $fac-f32 $fac-f64 ;; 23..25 + $fib-i32 $fib-f32 $fib-f64 ;; 26..28 + $const-f64-i32 $id-i32-f64 $swap-i32-i64 ;; 29..31 + ) + ) + + ;; Syntax + + (func + (call_indirect (i32.const 0)) + (call_indirect (param i64) (i64.const 0) (i32.const 0)) + (call_indirect (param i64) (param) (param f64 i32 i64) + (i64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (i32.const 0) + ) + (call_indirect (result) (i32.const 0)) + (drop (i32.eqz (call_indirect (result i32) (i32.const 0)))) + (drop (i32.eqz (call_indirect (result i32) (result) (i32.const 0)))) + (drop (i32.eqz + (call_indirect (param i64) (result i32) (i64.const 0) (i32.const 0)) + )) + (drop (i32.eqz + (call_indirect + (param) (param i64) (param) (param f64 i32 i64) (param) (param) + (result) (result i32) (result) (result) + (i64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (i32.const 0) + ) + )) + (drop (i64.eqz + (call_indirect (type $over-i64) (param i64) (result i64) + (i64.const 0) (i32.const 0) + ) + )) + ) + + ;; Typing + + (func (export "type-i32") (result i32) + (call_indirect (type $out-i32) (i32.const 0)) + ) + (func (export "type-i64") (result i64) + (call_indirect (type $out-i64) (i32.const 1)) + ) + (func (export "type-f32") (result f32) + (call_indirect (type $out-f32) (i32.const 2)) + ) + (func (export "type-f64") (result f64) + (call_indirect (type $out-f64) (i32.const 3)) + ) + (func (export "type-f64-i32") (result f64 i32) + (call_indirect (type $out-f64-i32) (i32.const 29)) + ) + + (func (export "type-index") (result i64) + (call_indirect (type $over-i64) (i64.const 100) (i32.const 5)) + ) + + (func (export "type-first-i32") (result i32) + (call_indirect (type $over-i32) (i32.const 32) (i32.const 4)) + ) + (func (export "type-first-i64") (result i64) + (call_indirect (type $over-i64) (i64.const 64) (i32.const 5)) + ) + (func (export "type-first-f32") (result f32) + (call_indirect (type $over-f32) (f32.const 1.32) (i32.const 6)) + ) + (func (export "type-first-f64") (result f64) + (call_indirect (type $over-f64) (f64.const 1.64) (i32.const 7)) + ) + + (func (export "type-second-i32") (result i32) + (call_indirect (type $f32-i32) (f32.const 32.1) (i32.const 32) (i32.const 8)) + ) + (func (export "type-second-i64") (result i64) + (call_indirect (type $i32-i64) (i32.const 32) (i64.const 64) (i32.const 9)) + ) + (func (export "type-second-f32") (result f32) + (call_indirect (type $f64-f32) (f64.const 64) (f32.const 32) (i32.const 10)) + ) + (func (export "type-second-f64") (result f64) + (call_indirect (type $i64-f64) (i64.const 64) (f64.const 64.1) (i32.const 11)) + ) + + (func (export "type-all-f64-i32") (result f64 i32) + (call_indirect (type $out-f64-i32) (i32.const 29)) + ) + (func (export "type-all-i32-f64") (result i32 f64) + (call_indirect (type $over-i32-f64) + (i32.const 1) (f64.const 2) (i32.const 30) + ) + ) + (func (export "type-all-i32-i64") (result i64 i32) + (call_indirect (type $swap-i32-i64) + (i32.const 1) (i64.const 2) (i32.const 31) + ) + ) + + ;; Dispatch + + (func (export "dispatch") (param i32 i64) (result i64) + (call_indirect (type $over-i64) (local.get 1) (local.get 0)) + ) + + (func (export "dispatch-structural-i64") (param i32) (result i64) + (call_indirect (type $over-i64-duplicate) (i64.const 9) (local.get 0)) + ) + (func (export "dispatch-structural-i32") (param i32) (result i32) + (call_indirect (type $over-i32-duplicate) (i32.const 9) (local.get 0)) + ) + (func (export "dispatch-structural-f32") (param i32) (result f32) + (call_indirect (type $over-f32-duplicate) (f32.const 9.0) (local.get 0)) + ) + (func (export "dispatch-structural-f64") (param i32) (result f64) + (call_indirect (type $over-f64-duplicate) (f64.const 9.0) (local.get 0)) + ) + + ;; Recursion + + (func $fac-i64 (export "fac-i64") (type $over-i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (i64.const 1)) + (else + (i64.mul + (local.get 0) + (call_indirect (type $over-i64) + (i64.sub (local.get 0) (i64.const 1)) + (i32.const 12) + ) + ) + ) + ) + ) + + (func $fib-i64 (export "fib-i64") (type $over-i64) + (if (result i64) (i64.le_u (local.get 0) (i64.const 1)) + (then (i64.const 1)) + (else + (i64.add + (call_indirect (type $over-i64) + (i64.sub (local.get 0) (i64.const 2)) + (i32.const 13) + ) + (call_indirect (type $over-i64) + (i64.sub (local.get 0) (i64.const 1)) + (i32.const 13) + ) + ) + ) + ) + ) + + (func $fac-i32 (export "fac-i32") (type $over-i32) + (if (result i32) (i32.eqz (local.get 0)) + (then (i32.const 1)) + (else + (i32.mul + (local.get 0) + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 23) + ) + ) + ) + ) + ) + + (func $fac-f32 (export "fac-f32") (type $over-f32) + (if (result f32) (f32.eq (local.get 0) (f32.const 0.0)) + (then (f32.const 1.0)) + (else + (f32.mul + (local.get 0) + (call_indirect (type $over-f32) + (f32.sub (local.get 0) (f32.const 1.0)) + (i32.const 24) + ) + ) + ) + ) + ) + + (func $fac-f64 (export "fac-f64") (type $over-f64) + (if (result f64) (f64.eq (local.get 0) (f64.const 0.0)) + (then (f64.const 1.0)) + (else + (f64.mul + (local.get 0) + (call_indirect (type $over-f64) + (f64.sub (local.get 0) (f64.const 1.0)) + (i32.const 25) + ) + ) + ) + ) + ) + + (func $fib-i32 (export "fib-i32") (type $over-i32) + (if (result i32) (i32.le_u (local.get 0) (i32.const 1)) + (then (i32.const 1)) + (else + (i32.add + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 2)) + (i32.const 26) + ) + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 26) + ) + ) + ) + ) + ) + + (func $fib-f32 (export "fib-f32") (type $over-f32) + (if (result f32) (f32.le (local.get 0) (f32.const 1.0)) + (then (f32.const 1.0)) + (else + (f32.add + (call_indirect (type $over-f32) + (f32.sub (local.get 0) (f32.const 2.0)) + (i32.const 27) + ) + (call_indirect (type $over-f32) + (f32.sub (local.get 0) (f32.const 1.0)) + (i32.const 27) + ) + ) + ) + ) + ) + + (func $fib-f64 (export "fib-f64") (type $over-f64) + (if (result f64) (f64.le (local.get 0) (f64.const 1.0)) + (then (f64.const 1.0)) + (else + (f64.add + (call_indirect (type $over-f64) + (f64.sub (local.get 0) (f64.const 2.0)) + (i32.const 28) + ) + (call_indirect (type $over-f64) + (f64.sub (local.get 0) (f64.const 1.0)) + (i32.const 28) + ) + ) + ) + ) + ) + + (func $even (export "even") (param i32) (result i32) + (if (result i32) (i32.eqz (local.get 0)) + (then (i32.const 44)) + (else + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 15) + ) + ) + ) + ) + (func $odd (export "odd") (param i32) (result i32) + (if (result i32) (i32.eqz (local.get 0)) + (then (i32.const 99)) + (else + (call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 14) + ) + ) + ) + ) + + ;; Stack exhaustion + + ;; Implementations are required to have every call consume some abstract + ;; resource towards exhausting some abstract finite limit, such that + ;; infinitely recursive test cases reliably trap in finite time. This is + ;; because otherwise applications could come to depend on it on those + ;; implementations and be incompatible with implementations that don't do + ;; it (or don't do it under the same circumstances). + + (func $runaway (export "runaway") (call_indirect (type $proc) (i32.const 16))) + + (func $mutual-runaway1 (export "mutual-runaway") (call_indirect (type $proc) (i32.const 18))) + (func $mutual-runaway2 (call_indirect (type $proc) (i32.const 17))) + + ;; As parameter of control constructs and instructions + + (memory 1) + + (func (export "as-select-first") (result i32) + (select (call_indirect (type $out-i32) (i32.const 0)) (i32.const 2) (i32.const 3)) + ) + (func (export "as-select-mid") (result i32) + (select (i32.const 2) (call_indirect (type $out-i32) (i32.const 0)) (i32.const 3)) + ) + (func (export "as-select-last") (result i32) + (select (i32.const 2) (i32.const 3) (call_indirect (type $out-i32) (i32.const 0))) + ) + + (func (export "as-if-condition") (result i32) + (if (result i32) (call_indirect (type $out-i32) (i32.const 0)) (then (i32.const 1)) (else (i32.const 2))) + ) + + (func (export "as-br_if-first") (result i64) + (block (result i64) (br_if 0 (call_indirect (type $out-i64) (i32.const 1)) (i32.const 2))) + ) + (func (export "as-br_if-last") (result i32) + (block (result i32) (br_if 0 (i32.const 2) (call_indirect (type $out-i32) (i32.const 0)))) + ) + + (func (export "as-br_table-first") (result f32) + (block (result f32) (call_indirect (type $out-f32) (i32.const 2)) (i32.const 2) (br_table 0 0)) + ) + (func (export "as-br_table-last") (result i32) + (block (result i32) (i32.const 2) (call_indirect (type $out-i32) (i32.const 0)) (br_table 0 0)) + ) + + (func (export "as-store-first") + (call_indirect (type $out-i32) (i32.const 0)) (i32.const 1) (i32.store) + ) + (func (export "as-store-last") + (i32.const 10) (call_indirect (type $out-f64) (i32.const 3)) (f64.store) + ) + + (func (export "as-memory.grow-value") (result i32) + (memory.grow (call_indirect (type $out-i32) (i32.const 0))) + ) + (func (export "as-return-value") (result i32) + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) (return) + ) + (func (export "as-drop-operand") + (call_indirect (type $over-i64) (i64.const 1) (i32.const 5)) (drop) + ) + (func (export "as-br-value") (result f32) + (block (result f32) (br 0 (call_indirect (type $over-f32) (f32.const 1) (i32.const 6)))) + ) + (func (export "as-local.set-value") (result f64) + (local f64) (local.set 0 (call_indirect (type $over-f64) (f64.const 1) (i32.const 7))) (local.get 0) + ) + (func (export "as-local.tee-value") (result f64) + (local f64) (local.tee 0 (call_indirect (type $over-f64) (f64.const 1) (i32.const 7))) + ) + (global $a (mut f64) (f64.const 10.0)) + (func (export "as-global.set-value") (result f64) + (global.set $a (call_indirect (type $over-f64) (f64.const 1.0) (i32.const 7))) + (global.get $a) + ) + + (func (export "as-load-operand") (result i32) + (i32.load (call_indirect (type $out-i32) (i32.const 0))) + ) + + (func (export "as-unary-operand") (result f32) + (block (result f32) + (f32.sqrt + (call_indirect (type $over-f32) (f32.const 0x0p+0) (i32.const 6)) + ) + ) + ) + + (func (export "as-binary-left") (result i32) + (block (result i32) + (i32.add + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + (i32.const 10) + ) + ) + ) + (func (export "as-binary-right") (result i32) + (block (result i32) + (i32.sub + (i32.const 10) + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + ) + ) + ) + + (func (export "as-test-operand") (result i32) + (block (result i32) + (i32.eqz + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + ) + ) + ) + + (func (export "as-compare-left") (result i32) + (block (result i32) + (i32.le_u + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + (i32.const 10) + ) + ) + ) + (func (export "as-compare-right") (result i32) + (block (result i32) + (i32.ne + (i32.const 10) + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + ) + ) + ) + + (func (export "as-convert-operand") (result i64) + (block (result i64) + (i64.extend_i32_s + (call_indirect (type $over-i32) (i32.const 1) (i32.const 4)) + ) + ) + ) + +) + +(assert_return (invoke "type-i32") (i32.const 0x132)) +(assert_return (invoke "type-i64") (i64.const 0x164)) +(assert_return (invoke "type-f32") (f32.const 0xf32)) +(assert_return (invoke "type-f64") (f64.const 0xf64)) +(assert_return (invoke "type-f64-i32") (f64.const 0xf64) (i32.const 32)) + +(assert_return (invoke "type-index") (i64.const 100)) + +(assert_return (invoke "type-first-i32") (i32.const 32)) +(assert_return (invoke "type-first-i64") (i64.const 64)) +(assert_return (invoke "type-first-f32") (f32.const 1.32)) +(assert_return (invoke "type-first-f64") (f64.const 1.64)) + +(assert_return (invoke "type-second-i32") (i32.const 32)) +(assert_return (invoke "type-second-i64") (i64.const 64)) +(assert_return (invoke "type-second-f32") (f32.const 32)) +(assert_return (invoke "type-second-f64") (f64.const 64.1)) + +(assert_return (invoke "type-all-f64-i32") (f64.const 0xf64) (i32.const 32)) +(assert_return (invoke "type-all-i32-f64") (i32.const 1) (f64.const 2)) +(assert_return (invoke "type-all-i32-i64") (i64.const 2) (i32.const 1)) + +(assert_return (invoke "dispatch" (i32.const 5) (i64.const 2)) (i64.const 2)) +(assert_return (invoke "dispatch" (i32.const 5) (i64.const 5)) (i64.const 5)) +(assert_return (invoke "dispatch" (i32.const 12) (i64.const 5)) (i64.const 120)) +(assert_return (invoke "dispatch" (i32.const 13) (i64.const 5)) (i64.const 8)) +(assert_return (invoke "dispatch" (i32.const 20) (i64.const 2)) (i64.const 2)) +(assert_trap (invoke "dispatch" (i32.const 0) (i64.const 2)) "indirect call type mismatch") +(assert_trap (invoke "dispatch" (i32.const 15) (i64.const 2)) "indirect call type mismatch") +(assert_trap (invoke "dispatch" (i32.const 32) (i64.const 2)) "undefined element") +(assert_trap (invoke "dispatch" (i32.const -1) (i64.const 2)) "undefined element") +(assert_trap (invoke "dispatch" (i32.const 1213432423) (i64.const 2)) "undefined element") + +(assert_return (invoke "dispatch-structural-i64" (i32.const 5)) (i64.const 9)) +(assert_return (invoke "dispatch-structural-i64" (i32.const 12)) (i64.const 362880)) +(assert_return (invoke "dispatch-structural-i64" (i32.const 13)) (i64.const 55)) +(assert_return (invoke "dispatch-structural-i64" (i32.const 20)) (i64.const 9)) +(assert_trap (invoke "dispatch-structural-i64" (i32.const 11)) "indirect call type mismatch") +(assert_trap (invoke "dispatch-structural-i64" (i32.const 22)) "indirect call type mismatch") + +(assert_return (invoke "dispatch-structural-i32" (i32.const 4)) (i32.const 9)) +(assert_return (invoke "dispatch-structural-i32" (i32.const 23)) (i32.const 362880)) +(assert_return (invoke "dispatch-structural-i32" (i32.const 26)) (i32.const 55)) +(assert_return (invoke "dispatch-structural-i32" (i32.const 19)) (i32.const 9)) +(assert_trap (invoke "dispatch-structural-i32" (i32.const 9)) "indirect call type mismatch") +(assert_trap (invoke "dispatch-structural-i32" (i32.const 21)) "indirect call type mismatch") + +(assert_return (invoke "dispatch-structural-f32" (i32.const 6)) (f32.const 9.0)) +(assert_return (invoke "dispatch-structural-f32" (i32.const 24)) (f32.const 362880.0)) +(assert_return (invoke "dispatch-structural-f32" (i32.const 27)) (f32.const 55.0)) +(assert_return (invoke "dispatch-structural-f32" (i32.const 21)) (f32.const 9.0)) +(assert_trap (invoke "dispatch-structural-f32" (i32.const 8)) "indirect call type mismatch") +(assert_trap (invoke "dispatch-structural-f32" (i32.const 19)) "indirect call type mismatch") + +(assert_return (invoke "dispatch-structural-f64" (i32.const 7)) (f64.const 9.0)) +(assert_return (invoke "dispatch-structural-f64" (i32.const 25)) (f64.const 362880.0)) +(assert_return (invoke "dispatch-structural-f64" (i32.const 28)) (f64.const 55.0)) +(assert_return (invoke "dispatch-structural-f64" (i32.const 22)) (f64.const 9.0)) +(assert_trap (invoke "dispatch-structural-f64" (i32.const 10)) "indirect call type mismatch") +(assert_trap (invoke "dispatch-structural-f64" (i32.const 18)) "indirect call type mismatch") + +(assert_return (invoke "fac-i64" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "fac-i64" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-i64" (i64.const 5)) (i64.const 120)) +(assert_return (invoke "fac-i64" (i64.const 25)) (i64.const 7034535277573963776)) + +(assert_return (invoke "fac-i32" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "fac-i32" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "fac-i32" (i32.const 5)) (i32.const 120)) +(assert_return (invoke "fac-i32" (i32.const 10)) (i32.const 3628800)) + +(assert_return (invoke "fac-f32" (f32.const 0.0)) (f32.const 1.0)) +(assert_return (invoke "fac-f32" (f32.const 1.0)) (f32.const 1.0)) +(assert_return (invoke "fac-f32" (f32.const 5.0)) (f32.const 120.0)) +(assert_return (invoke "fac-f32" (f32.const 10.0)) (f32.const 3628800.0)) + +(assert_return (invoke "fac-f64" (f64.const 0.0)) (f64.const 1.0)) +(assert_return (invoke "fac-f64" (f64.const 1.0)) (f64.const 1.0)) +(assert_return (invoke "fac-f64" (f64.const 5.0)) (f64.const 120.0)) +(assert_return (invoke "fac-f64" (f64.const 10.0)) (f64.const 3628800.0)) + +(assert_return (invoke "fib-i64" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "fib-i64" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fib-i64" (i64.const 2)) (i64.const 2)) +(assert_return (invoke "fib-i64" (i64.const 5)) (i64.const 8)) +(assert_return (invoke "fib-i64" (i64.const 20)) (i64.const 10946)) + +(assert_return (invoke "fib-i32" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "fib-i32" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "fib-i32" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "fib-i32" (i32.const 5)) (i32.const 8)) +(assert_return (invoke "fib-i32" (i32.const 20)) (i32.const 10946)) + +(assert_return (invoke "fib-f32" (f32.const 0.0)) (f32.const 1.0)) +(assert_return (invoke "fib-f32" (f32.const 1.0)) (f32.const 1.0)) +(assert_return (invoke "fib-f32" (f32.const 2.0)) (f32.const 2.0)) +(assert_return (invoke "fib-f32" (f32.const 5.0)) (f32.const 8.0)) +(assert_return (invoke "fib-f32" (f32.const 20.0)) (f32.const 10946.0)) + +(assert_return (invoke "fib-f64" (f64.const 0.0)) (f64.const 1.0)) +(assert_return (invoke "fib-f64" (f64.const 1.0)) (f64.const 1.0)) +(assert_return (invoke "fib-f64" (f64.const 2.0)) (f64.const 2.0)) +(assert_return (invoke "fib-f64" (f64.const 5.0)) (f64.const 8.0)) +(assert_return (invoke "fib-f64" (f64.const 20.0)) (f64.const 10946.0)) + +(assert_return (invoke "even" (i32.const 0)) (i32.const 44)) +(assert_return (invoke "even" (i32.const 1)) (i32.const 99)) +(assert_return (invoke "even" (i32.const 100)) (i32.const 44)) +(assert_return (invoke "even" (i32.const 77)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 0)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 1)) (i32.const 44)) +(assert_return (invoke "odd" (i32.const 200)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 77)) (i32.const 44)) + +(assert_exhaustion (invoke "runaway") "call stack exhausted") +(assert_exhaustion (invoke "mutual-runaway") "call stack exhausted") + +(assert_return (invoke "as-select-first") (i32.const 0x132)) +(assert_return (invoke "as-select-mid") (i32.const 2)) +(assert_return (invoke "as-select-last") (i32.const 2)) + +(assert_return (invoke "as-if-condition") (i32.const 1)) + +(assert_return (invoke "as-br_if-first") (i64.const 0x164)) +(assert_return (invoke "as-br_if-last") (i32.const 2)) + +(assert_return (invoke "as-br_table-first") (f32.const 0xf32)) +(assert_return (invoke "as-br_table-last") (i32.const 2)) + +(assert_return (invoke "as-store-first")) +(assert_return (invoke "as-store-last")) + +(assert_return (invoke "as-memory.grow-value") (i32.const 1)) +(assert_return (invoke "as-return-value") (i32.const 1)) +(assert_return (invoke "as-drop-operand")) +(assert_return (invoke "as-br-value") (f32.const 1)) +(assert_return (invoke "as-local.set-value") (f64.const 1)) +(assert_return (invoke "as-local.tee-value") (f64.const 1)) +(assert_return (invoke "as-global.set-value") (f64.const 1.0)) +(assert_return (invoke "as-load-operand") (i32.const 1)) + +(assert_return (invoke "as-unary-operand") (f32.const 0x0p+0)) +(assert_return (invoke "as-binary-left") (i32.const 11)) +(assert_return (invoke "as-binary-right") (i32.const 9)) +(assert_return (invoke "as-test-operand") (i32.const 0)) +(assert_return (invoke "as-compare-left") (i32.const 1)) +(assert_return (invoke "as-compare-right") (i32.const 1)) +(assert_return (invoke "as-convert-operand") (i64.const 1)) + +;; Invalid syntax + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (type $sig) (result i32) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (param i32) (type $sig) (result i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (param i32) (result i32) (type $sig)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (result i32) (type $sig) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (result i32) (param i32) (type $sig)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (result i32) (param i32) (i32.const 0) (i32.const 0))" + ")" + ) + "unexpected token" +) + +(assert_malformed + (module quote + "(table 0 funcref)" + "(func (call_indirect (param $x i32) (i32.const 0) (i32.const 0)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (type $sig) (result i32) (i32.const 0))" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (type $sig) (result i32) (i32.const 0))" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func" + " (call_indirect (type $sig) (param i32) (i32.const 0) (i32.const 0))" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (call_indirect (type $sig) (param i32) (result i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "inline function type" +) + +;; Invalid typing + +(assert_invalid + (module + (type (func)) + (func $no-table (call_indirect (type 0) (i32.const 0))) + ) + "unknown table" +) + +(assert_invalid + (module + (type (func)) + (table 0 funcref) + (func $type-void-vs-num (i32.eqz (call_indirect (type 0) (i32.const 0)))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (result i64))) + (table 0 funcref) + (func $type-num-vs-num (i32.eqz (call_indirect (type 0) (i32.const 0)))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type (func (param i32))) + (table 0 funcref) + (func $arity-0-vs-1 (call_indirect (type 0) (i32.const 0))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param f64 i32))) + (table 0 funcref) + (func $arity-0-vs-2 (call_indirect (type 0) (i32.const 0))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func)) + (table 0 funcref) + (func $arity-1-vs-0 (call_indirect (type 0) (i32.const 1) (i32.const 0))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func)) + (table 0 funcref) + (func $arity-2-vs-0 + (call_indirect (type 0) (f64.const 2) (i32.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (type (func (param i32))) + (table 0 funcref) + (func $type-func-void-vs-i32 (call_indirect (type 0) (i32.const 1) (nop))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param i32))) + (table 0 funcref) + (func $type-func-num-vs-i32 (call_indirect (type 0) (i32.const 0) (i64.const 1))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type (func (param i32 i32))) + (table 0 funcref) + (func $type-first-void-vs-num + (call_indirect (type 0) (nop) (i32.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param i32 i32))) + (table 0 funcref) + (func $type-second-void-vs-num + (call_indirect (type 0) (i32.const 1) (nop) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param i32 f64))) + (table 0 funcref) + (func $type-first-num-vs-num + (call_indirect (type 0) (f64.const 1) (i32.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param f64 i32))) + (table 0 funcref) + (func $type-second-num-vs-num + (call_indirect (type 0) (i32.const 1) (f64.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $f (param i32)) + (type $sig (func (param i32))) + (table funcref (elem $f)) + (func $type-first-empty-in-block + (block + (call_indirect (type $sig) (i32.const 0)) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32 i32)) + (type $sig (func (param i32 i32))) + (table funcref (elem $f)) + (func $type-second-empty-in-block + (block + (call_indirect (type $sig) (i32.const 0) (i32.const 0)) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32)) + (type $sig (func (param i32))) + (table funcref (elem $f)) + (func $type-first-empty-in-loop + (loop + (call_indirect (type $sig) (i32.const 0)) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32 i32)) + (type $sig (func (param i32 i32))) + (table funcref (elem $f)) + (func $type-second-empty-in-loop + (loop + (call_indirect (type $sig) (i32.const 0) (i32.const 0)) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32)) + (type $sig (func (param i32))) + (table funcref (elem $f)) + (func $type-first-empty-in-then + (i32.const 0) (i32.const 0) + (if + (then + (call_indirect (type $sig) (i32.const 0)) + ) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32 i32)) + (type $sig (func (param i32 i32))) + (table funcref (elem $f)) + (func $type-second-empty-in-then + (i32.const 0) (i32.const 0) + (if + (then + (call_indirect (type $sig) (i32.const 0) (i32.const 0)) + ) + ) + ) + ) + "type mismatch" +) + + +;; Unbound type + +(assert_invalid + (module + (table 0 funcref) + (func $unbound-type (call_indirect (type 1) (i32.const 0))) + ) + "unknown type" +) +(assert_invalid + (module + (table 0 funcref) + (func $large-type (call_indirect (type 1012321300) (i32.const 0))) + ) + "unknown type" +) + + +;; Unbound function in table + +(assert_invalid + (module (table funcref (elem 0 0))) + "unknown function" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/multi-value/fac.wast b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/fac.wast new file mode 100644 index 000000000..0e61c1f9e --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/fac.wast @@ -0,0 +1,109 @@ +(module + ;; Recursive factorial + (func (export "fac-rec") (param i64) (result i64) + (if (result i64) (i64.eq (local.get 0) (i64.const 0)) + (then (i64.const 1)) + (else + (i64.mul (local.get 0) (call 0 (i64.sub (local.get 0) (i64.const 1)))) + ) + ) + ) + + ;; Recursive factorial named + (func $fac-rec-named (export "fac-rec-named") (param $n i64) (result i64) + (if (result i64) (i64.eq (local.get $n) (i64.const 0)) + (then (i64.const 1)) + (else + (i64.mul + (local.get $n) + (call $fac-rec-named (i64.sub (local.get $n) (i64.const 1))) + ) + ) + ) + ) + + ;; Iterative factorial + (func (export "fac-iter") (param i64) (result i64) + (local i64 i64) + (local.set 1 (local.get 0)) + (local.set 2 (i64.const 1)) + (block + (loop + (if + (i64.eq (local.get 1) (i64.const 0)) + (then (br 2)) + (else + (local.set 2 (i64.mul (local.get 1) (local.get 2))) + (local.set 1 (i64.sub (local.get 1) (i64.const 1))) + ) + ) + (br 0) + ) + ) + (local.get 2) + ) + + ;; Iterative factorial named + (func (export "fac-iter-named") (param $n i64) (result i64) + (local $i i64) + (local $res i64) + (local.set $i (local.get $n)) + (local.set $res (i64.const 1)) + (block $done + (loop $loop + (if + (i64.eq (local.get $i) (i64.const 0)) + (then (br $done)) + (else + (local.set $res (i64.mul (local.get $i) (local.get $res))) + (local.set $i (i64.sub (local.get $i) (i64.const 1))) + ) + ) + (br $loop) + ) + ) + (local.get $res) + ) + + ;; Optimized factorial. + (func (export "fac-opt") (param i64) (result i64) + (local i64) + (local.set 1 (i64.const 1)) + (block + (br_if 0 (i64.lt_s (local.get 0) (i64.const 2))) + (loop + (local.set 1 (i64.mul (local.get 1) (local.get 0))) + (local.set 0 (i64.add (local.get 0) (i64.const -1))) + (br_if 0 (i64.gt_s (local.get 0) (i64.const 1))) + ) + ) + (local.get 1) + ) + + ;; Iterative factorial without locals. + (func $pick0 (param i64) (result i64 i64) + (local.get 0) (local.get 0) + ) + (func $pick1 (param i64 i64) (result i64 i64 i64) + (local.get 0) (local.get 1) (local.get 0) + ) + (func (export "fac-ssa") (param i64) (result i64) + (i64.const 1) (local.get 0) + (loop $l (param i64 i64) (result i64) + (call $pick1) (call $pick1) (i64.mul) + (call $pick1) (i64.const 1) (i64.sub) + (call $pick0) (i64.const 0) (i64.gt_u) + (br_if $l) + (drop) (return) + ) + ) +) + +(assert_return (invoke "fac-rec" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-iter" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-rec-named" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-iter-named" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-opt" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-ssa" (i64.const 25)) (i64.const 7034535277573963776)) + +(assert_exhaustion (invoke "fac-rec" (i64.const 1073741824)) "call stack exhausted") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/multi-value/func.wast b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/func.wast new file mode 100644 index 000000000..5cc5d7432 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/func.wast @@ -0,0 +1,899 @@ +;; Test `func` declarations, i.e. functions + +(module + ;; Auxiliary definition + (type $sig (func)) + (func $dummy) + + ;; Syntax + + (func) + (func (export "f")) + (func $f) + (func $h (export "g")) + + (func (local)) + (func (local) (local)) + (func (local i32)) + (func (local $x i32)) + (func (local i32 f64 i64)) + (func (local i32) (local f64)) + (func (local i32 f32) (local $x i64) (local) (local i32 f64)) + + (func (param)) + (func (param) (param)) + (func (param i32)) + (func (param $x i32)) + (func (param i32 f64 i64)) + (func (param i32) (param f64)) + (func (param i32 f32) (param $x i64) (param) (param i32 f64)) + + (func (result)) + (func (result) (result)) + (func (result i32) (unreachable)) + (func (result i32 f64 f32) (unreachable)) + (func (result i32) (result f64) (unreachable)) + (func (result i32 f32) (result i64) (result) (result i32 f64) (unreachable)) + + (type $sig-1 (func)) + (type $sig-2 (func (result i32))) + (type $sig-3 (func (param $x i32))) + (type $sig-4 (func (param i32 f64 i32) (result i32))) + + (func (export "type-use-1") (type $sig-1)) + (func (export "type-use-2") (type $sig-2) (i32.const 0)) + (func (export "type-use-3") (type $sig-3)) + (func (export "type-use-4") (type $sig-4) (i32.const 0)) + (func (export "type-use-5") (type $sig-2) (result i32) (i32.const 0)) + (func (export "type-use-6") (type $sig-3) (param i32)) + (func (export "type-use-7") + (type $sig-4) (param i32) (param f64 i32) (result i32) (i32.const 0) + ) + + (func (type $sig)) + (func (type $forward)) ;; forward reference + + (func $complex + (param i32 f32) (param $x i64) (param) (param i32) + (result) (result i32) (result) (result i64 i32) + (local f32) (local $y i32) (local i64 i32) (local) (local f64 i32) + (unreachable) (unreachable) + ) + (func $complex-sig + (type $sig) + (local f32) (local $y i32) (local i64 i32) (local) (local f64 i32) + (unreachable) (unreachable) + ) + + (type $forward (func)) + + ;; Typing of locals + + (func (export "local-first-i32") (result i32) (local i32 i32) (local.get 0)) + (func (export "local-first-i64") (result i64) (local i64 i64) (local.get 0)) + (func (export "local-first-f32") (result f32) (local f32 f32) (local.get 0)) + (func (export "local-first-f64") (result f64) (local f64 f64) (local.get 0)) + (func (export "local-second-i32") (result i32) (local i32 i32) (local.get 1)) + (func (export "local-second-i64") (result i64) (local i64 i64) (local.get 1)) + (func (export "local-second-f32") (result f32) (local f32 f32) (local.get 1)) + (func (export "local-second-f64") (result f64) (local f64 f64) (local.get 1)) + (func (export "local-mixed") (result f64) + (local f32) (local $x i32) (local i64 i32) (local) (local f64 i32) + (drop (f32.neg (local.get 0))) + (drop (i32.eqz (local.get 1))) + (drop (i64.eqz (local.get 2))) + (drop (i32.eqz (local.get 3))) + (drop (f64.neg (local.get 4))) + (drop (i32.eqz (local.get 5))) + (local.get 4) + ) + + ;; Typing of parameters + + (func (export "param-first-i32") (param i32 i32) (result i32) (local.get 0)) + (func (export "param-first-i64") (param i64 i64) (result i64) (local.get 0)) + (func (export "param-first-f32") (param f32 f32) (result f32) (local.get 0)) + (func (export "param-first-f64") (param f64 f64) (result f64) (local.get 0)) + (func (export "param-second-i32") (param i32 i32) (result i32) (local.get 1)) + (func (export "param-second-i64") (param i64 i64) (result i64) (local.get 1)) + (func (export "param-second-f32") (param f32 f32) (result f32) (local.get 1)) + (func (export "param-second-f64") (param f64 f64) (result f64) (local.get 1)) + (func (export "param-mixed") (param f32 i32) (param) (param $x i64) (param i32 f64 i32) + (result f64) + (drop (f32.neg (local.get 0))) + (drop (i32.eqz (local.get 1))) + (drop (i64.eqz (local.get 2))) + (drop (i32.eqz (local.get 3))) + (drop (f64.neg (local.get 4))) + (drop (i32.eqz (local.get 5))) + (local.get 4) + ) + + ;; Typing of results + + (func (export "empty")) + (func (export "value-void") (call $dummy)) + (func (export "value-i32") (result i32) (i32.const 77)) + (func (export "value-i64") (result i64) (i64.const 7777)) + (func (export "value-f32") (result f32) (f32.const 77.7)) + (func (export "value-f64") (result f64) (f64.const 77.77)) + (func (export "value-i32-f64") (result i32 f64) (i32.const 77) (f64.const 7)) + (func (export "value-i32-i32-i32") (result i32 i32 i32) + (i32.const 1) (i32.const 2) (i32.const 3) + ) + (func (export "value-block-void") (block (call $dummy) (call $dummy))) + (func (export "value-block-i32") (result i32) + (block (result i32) (call $dummy) (i32.const 77)) + ) + (func (export "value-block-i32-i64") (result i32 i64) + (block (result i32 i64) (call $dummy) (i32.const 1) (i64.const 2)) + ) + + (func (export "return-empty") (return)) + (func (export "return-i32") (result i32) (return (i32.const 78))) + (func (export "return-i64") (result i64) (return (i64.const 7878))) + (func (export "return-f32") (result f32) (return (f32.const 78.7))) + (func (export "return-f64") (result f64) (return (f64.const 78.78))) + (func (export "return-i32-f64") (result i32 f64) + (return (i32.const 78) (f64.const 78.78)) + ) + (func (export "return-i32-i32-i32") (result i32 i32 i32) + (return (i32.const 1) (i32.const 2) (i32.const 3)) + ) + (func (export "return-block-i32") (result i32) + (return (block (result i32) (call $dummy) (i32.const 77))) + ) + (func (export "return-block-i32-i64") (result i32 i64) + (return (block (result i32 i64) (call $dummy) (i32.const 1) (i64.const 2))) + ) + + (func (export "break-empty") (br 0)) + (func (export "break-i32") (result i32) (br 0 (i32.const 79))) + (func (export "break-i64") (result i64) (br 0 (i64.const 7979))) + (func (export "break-f32") (result f32) (br 0 (f32.const 79.9))) + (func (export "break-f64") (result f64) (br 0 (f64.const 79.79))) + (func (export "break-i32-f64") (result i32 f64) + (br 0 (i32.const 79) (f64.const 79.79)) + ) + (func (export "break-i32-i32-i32") (result i32 i32 i32) + (br 0 (i32.const 1) (i32.const 2) (i32.const 3)) + ) + (func (export "break-block-i32") (result i32) + (br 0 (block (result i32) (call $dummy) (i32.const 77))) + ) + (func (export "break-block-i32-i64") (result i32 i64) + (br 0 (block (result i32 i64) (call $dummy) (i32.const 1) (i64.const 2))) + ) + + (func (export "break-br_if-empty") (param i32) + (br_if 0 (local.get 0)) + ) + (func (export "break-br_if-num") (param i32) (result i32) + (drop (br_if 0 (i32.const 50) (local.get 0))) (i32.const 51) + ) + (func (export "break-br_if-num-num") (param i32) (result i32 i64) + (drop (drop (br_if 0 (i32.const 50) (i64.const 51) (local.get 0)))) + (i32.const 51) (i64.const 52) + ) + + (func (export "break-br_table-empty") (param i32) + (br_table 0 0 0 (local.get 0)) + ) + (func (export "break-br_table-num") (param i32) (result i32) + (br_table 0 0 (i32.const 50) (local.get 0)) (i32.const 51) + ) + (func (export "break-br_table-num-num") (param i32) (result i32 i64) + (br_table 0 0 (i32.const 50) (i64.const 51) (local.get 0)) + (i32.const 51) (i64.const 52) + ) + (func (export "break-br_table-nested-empty") (param i32) + (block (br_table 0 1 0 (local.get 0))) + ) + (func (export "break-br_table-nested-num") (param i32) (result i32) + (i32.add + (block (result i32) + (br_table 0 1 0 (i32.const 50) (local.get 0)) (i32.const 51) + ) + (i32.const 2) + ) + ) + (func (export "break-br_table-nested-num-num") (param i32) (result i32 i32) + (i32.add + (block (result i32 i32) + (br_table 0 1 0 (i32.const 50) (i32.const 51) (local.get 0)) + (i32.const 51) (i32.const -3) + ) + ) + (i32.const 52) + ) + + ;; Large signatures + + (func (export "large-sig") + (param i32 i64 f32 f32 i32 f64 f32 i32 i32 i32 f32 f64 f64 f64 i32 i32 f32) + (result f64 f32 i32 i32 i32 i64 f32 i32 i32 f32 f64 f64 i32 f32 i32 f64) + (local.get 5) + (local.get 2) + (local.get 0) + (local.get 8) + (local.get 7) + (local.get 1) + (local.get 3) + (local.get 9) + (local.get 4) + (local.get 6) + (local.get 13) + (local.get 11) + (local.get 15) + (local.get 16) + (local.get 14) + (local.get 12) + ) + + ;; Default initialization of locals + + (func (export "init-local-i32") (result i32) (local i32) (local.get 0)) + (func (export "init-local-i64") (result i64) (local i64) (local.get 0)) + (func (export "init-local-f32") (result f32) (local f32) (local.get 0)) + (func (export "init-local-f64") (result f64) (local f64) (local.get 0)) +) + +(assert_return (invoke "type-use-1")) +(assert_return (invoke "type-use-2") (i32.const 0)) +(assert_return (invoke "type-use-3" (i32.const 1))) +(assert_return + (invoke "type-use-4" (i32.const 1) (f64.const 1) (i32.const 1)) + (i32.const 0) +) +(assert_return (invoke "type-use-5") (i32.const 0)) +(assert_return (invoke "type-use-6" (i32.const 1))) +(assert_return + (invoke "type-use-7" (i32.const 1) (f64.const 1) (i32.const 1)) + (i32.const 0) +) + +(assert_return (invoke "local-first-i32") (i32.const 0)) +(assert_return (invoke "local-first-i64") (i64.const 0)) +(assert_return (invoke "local-first-f32") (f32.const 0)) +(assert_return (invoke "local-first-f64") (f64.const 0)) +(assert_return (invoke "local-second-i32") (i32.const 0)) +(assert_return (invoke "local-second-i64") (i64.const 0)) +(assert_return (invoke "local-second-f32") (f32.const 0)) +(assert_return (invoke "local-second-f64") (f64.const 0)) +(assert_return (invoke "local-mixed") (f64.const 0)) + +(assert_return + (invoke "param-first-i32" (i32.const 2) (i32.const 3)) (i32.const 2) +) +(assert_return + (invoke "param-first-i64" (i64.const 2) (i64.const 3)) (i64.const 2) +) +(assert_return + (invoke "param-first-f32" (f32.const 2) (f32.const 3)) (f32.const 2) +) +(assert_return + (invoke "param-first-f64" (f64.const 2) (f64.const 3)) (f64.const 2) +) +(assert_return + (invoke "param-second-i32" (i32.const 2) (i32.const 3)) (i32.const 3) +) +(assert_return + (invoke "param-second-i64" (i64.const 2) (i64.const 3)) (i64.const 3) +) +(assert_return + (invoke "param-second-f32" (f32.const 2) (f32.const 3)) (f32.const 3) +) +(assert_return + (invoke "param-second-f64" (f64.const 2) (f64.const 3)) (f64.const 3) +) + +(assert_return + (invoke "param-mixed" + (f32.const 1) (i32.const 2) (i64.const 3) + (i32.const 4) (f64.const 5.5) (i32.const 6) + ) + (f64.const 5.5) +) + +(assert_return (invoke "empty")) +(assert_return (invoke "value-void")) +(assert_return (invoke "value-i32") (i32.const 77)) +(assert_return (invoke "value-i64") (i64.const 7777)) +(assert_return (invoke "value-f32") (f32.const 77.7)) +(assert_return (invoke "value-f64") (f64.const 77.77)) +(assert_return (invoke "value-i32-f64") (i32.const 77) (f64.const 7)) +(assert_return (invoke "value-i32-i32-i32") + (i32.const 1) (i32.const 2) (i32.const 3) +) +(assert_return (invoke "value-block-void")) +(assert_return (invoke "value-block-i32") (i32.const 77)) +(assert_return (invoke "value-block-i32-i64") (i32.const 1) (i64.const 2)) + +(assert_return (invoke "return-empty")) +(assert_return (invoke "return-i32") (i32.const 78)) +(assert_return (invoke "return-i64") (i64.const 7878)) +(assert_return (invoke "return-f32") (f32.const 78.7)) +(assert_return (invoke "return-f64") (f64.const 78.78)) +(assert_return (invoke "return-i32-f64") (i32.const 78) (f64.const 78.78)) +(assert_return (invoke "return-i32-i32-i32") + (i32.const 1) (i32.const 2) (i32.const 3) +) +(assert_return (invoke "return-block-i32") (i32.const 77)) +(assert_return (invoke "return-block-i32-i64") (i32.const 1) (i64.const 2)) + +(assert_return (invoke "break-empty")) +(assert_return (invoke "break-i32") (i32.const 79)) +(assert_return (invoke "break-i64") (i64.const 7979)) +(assert_return (invoke "break-f32") (f32.const 79.9)) +(assert_return (invoke "break-f64") (f64.const 79.79)) +(assert_return (invoke "break-i32-f64") (i32.const 79) (f64.const 79.79)) +(assert_return (invoke "break-i32-i32-i32") + (i32.const 1) (i32.const 2) (i32.const 3) +) +(assert_return (invoke "break-block-i32") (i32.const 77)) +(assert_return (invoke "break-block-i32-i64") (i32.const 1) (i64.const 2)) + +(assert_return (invoke "break-br_if-empty" (i32.const 0))) +(assert_return (invoke "break-br_if-empty" (i32.const 2))) +(assert_return (invoke "break-br_if-num" (i32.const 0)) (i32.const 51)) +(assert_return (invoke "break-br_if-num" (i32.const 1)) (i32.const 50)) +(assert_return (invoke "break-br_if-num-num" (i32.const 0)) + (i32.const 51) (i64.const 52) +) +(assert_return (invoke "break-br_if-num-num" (i32.const 1)) + (i32.const 50) (i64.const 51) +) + +(assert_return (invoke "break-br_table-empty" (i32.const 0))) +(assert_return (invoke "break-br_table-empty" (i32.const 1))) +(assert_return (invoke "break-br_table-empty" (i32.const 5))) +(assert_return (invoke "break-br_table-empty" (i32.const -1))) +(assert_return (invoke "break-br_table-num" (i32.const 0)) (i32.const 50)) +(assert_return (invoke "break-br_table-num" (i32.const 1)) (i32.const 50)) +(assert_return (invoke "break-br_table-num" (i32.const 10)) (i32.const 50)) +(assert_return (invoke "break-br_table-num" (i32.const -100)) (i32.const 50)) +(assert_return (invoke "break-br_table-num-num" (i32.const 0)) + (i32.const 50) (i64.const 51) +) +(assert_return (invoke "break-br_table-num-num" (i32.const 1)) + (i32.const 50) (i64.const 51) +) +(assert_return (invoke "break-br_table-num-num" (i32.const 10)) + (i32.const 50) (i64.const 51) +) +(assert_return (invoke "break-br_table-num-num" (i32.const -100)) + (i32.const 50) (i64.const 51) +) +(assert_return (invoke "break-br_table-nested-empty" (i32.const 0))) +(assert_return (invoke "break-br_table-nested-empty" (i32.const 1))) +(assert_return (invoke "break-br_table-nested-empty" (i32.const 3))) +(assert_return (invoke "break-br_table-nested-empty" (i32.const -2))) +(assert_return + (invoke "break-br_table-nested-num" (i32.const 0)) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num" (i32.const 1)) (i32.const 50) +) +(assert_return + (invoke "break-br_table-nested-num" (i32.const 2)) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num" (i32.const -3)) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const 0)) + (i32.const 101) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const 1)) + (i32.const 50) (i32.const 51) +) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const 2)) + (i32.const 101) (i32.const 52) +) +(assert_return + (invoke "break-br_table-nested-num-num" (i32.const -3)) + (i32.const 101) (i32.const 52) +) + +(assert_return + (invoke "large-sig" + (i32.const 0) (i64.const 1) (f32.const 2) (f32.const 3) + (i32.const 4) (f64.const 5) (f32.const 6) (i32.const 7) + (i32.const 8) (i32.const 9) (f32.const 10) (f64.const 11) + (f64.const 12) (f64.const 13) (i32.const 14) (i32.const 15) + (f32.const 16) + ) + (f64.const 5) (f32.const 2) (i32.const 0) (i32.const 8) + (i32.const 7) (i64.const 1) (f32.const 3) (i32.const 9) + (i32.const 4) (f32.const 6) (f64.const 13) (f64.const 11) + (i32.const 15) (f32.const 16) (i32.const 14) (f64.const 12) +) + +(assert_return (invoke "init-local-i32") (i32.const 0)) +(assert_return (invoke "init-local-i64") (i64.const 0)) +(assert_return (invoke "init-local-f32") (f32.const 0)) +(assert_return (invoke "init-local-f64") (f64.const 0)) + + +;; Expansion of inline function types + +(module + (func $f (result f64) (f64.const 0)) ;; adds implicit type definition + (func $g (param i32)) ;; reuses explicit type definition + (type $t (func (param i32))) + + (func $i32->void (type 0)) ;; (param i32) + (func $void->f64 (type 1) (f64.const 0)) ;; (result f64) + (func $check + (call $i32->void (i32.const 0)) + (drop (call $void->f64)) + ) +) + +(assert_invalid + (module + (func $f (result f64) (f64.const 0)) ;; adds implicit type definition + (func $g (param i32)) ;; reuses explicit type definition + (func $h (result f64) (f64.const 1)) ;; reuses implicit type definition + (type $t (func (param i32))) + + (func (type 2)) ;; does not exist + ) + "unknown type" +) + + +(module + (type $sig (func)) + + (func $empty-sig-1) ;; should be assigned type $sig + (func $complex-sig-1 (param f64 i64 f64 i64 f64 i64 f32 i32)) + (func $empty-sig-2) ;; should be assigned type $sig + (func $complex-sig-2 (param f64 i64 f64 i64 f64 i64 f32 i32)) + (func $complex-sig-3 (param f64 i64 f64 i64 f64 i64 f32 i32)) + (func $complex-sig-4 (param i64 i64 f64 i64 f64 i64 f32 i32)) + (func $complex-sig-5 (param i64 i64 f64 i64 f64 i64 f32 i32)) + + (type $empty-sig-duplicate (func)) + (type $complex-sig-duplicate (func (param i64 i64 f64 i64 f64 i64 f32 i32))) + (table funcref + (elem + $complex-sig-3 $empty-sig-2 $complex-sig-1 $complex-sig-3 $empty-sig-1 + $complex-sig-4 $complex-sig-5 + ) + ) + + (func (export "signature-explicit-reused") + (call_indirect (type $sig) (i32.const 1)) + (call_indirect (type $sig) (i32.const 4)) + ) + + (func (export "signature-implicit-reused") + ;; The implicit index 3 in this test depends on the function and + ;; type definitions, and may need adapting if they change. + (call_indirect (type 3) + (f64.const 0) (i64.const 0) (f64.const 0) (i64.const 0) + (f64.const 0) (i64.const 0) (f32.const 0) (i32.const 0) + (i32.const 0) + ) + (call_indirect (type 3) + (f64.const 0) (i64.const 0) (f64.const 0) (i64.const 0) + (f64.const 0) (i64.const 0) (f32.const 0) (i32.const 0) + (i32.const 2) + ) + (call_indirect (type 3) + (f64.const 0) (i64.const 0) (f64.const 0) (i64.const 0) + (f64.const 0) (i64.const 0) (f32.const 0) (i32.const 0) + (i32.const 3) + ) + ) + + (func (export "signature-explicit-duplicate") + (call_indirect (type $empty-sig-duplicate) (i32.const 1)) + ) + + (func (export "signature-implicit-duplicate") + (call_indirect (type $complex-sig-duplicate) + (i64.const 0) (i64.const 0) (f64.const 0) (i64.const 0) + (f64.const 0) (i64.const 0) (f32.const 0) (i32.const 0) + (i32.const 5) + ) + (call_indirect (type $complex-sig-duplicate) + (i64.const 0) (i64.const 0) (f64.const 0) (i64.const 0) + (f64.const 0) (i64.const 0) (f32.const 0) (i32.const 0) + (i32.const 6) + ) + ) +) + +(assert_return (invoke "signature-explicit-reused")) +(assert_return (invoke "signature-implicit-reused")) +(assert_return (invoke "signature-explicit-duplicate")) +(assert_return (invoke "signature-implicit-duplicate")) + + +;; Malformed type use + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (type $sig) (result i32) (param i32) (i32.const 0))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (param i32) (type $sig) (result i32) (i32.const 0))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (param i32) (result i32) (type $sig) (i32.const 0))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (result i32) (type $sig) (param i32) (i32.const 0))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (result i32) (param i32) (type $sig) (i32.const 0))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func (result i32) (param i32) (i32.const 0))" + ) + "unexpected token" +) + +(assert_malformed + (module quote + "(type $sig (func))" + "(func (type $sig) (result i32) (i32.const 0))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (type $sig) (result i32) (i32.const 0))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (type $sig) (param i32) (i32.const 0))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(func (type $sig) (param i32) (result i32) (unreachable))" + ) + "inline function type" +) + + +;; Invalid typing of locals + +(assert_invalid + (module (func $type-local-num-vs-num (result i64) (local i32) (local.get 0))) + "type mismatch" +) +(assert_invalid + (module (func $type-local-num-vs-num (local f32) (i32.eqz (local.get 0)))) + "type mismatch" +) +(assert_invalid + (module (func $type-local-num-vs-num (local f64 i64) (f64.neg (local.get 1)))) + "type mismatch" +) + + +;; Invalid typing of parameters + +(assert_invalid + (module (func $type-param-num-vs-num (param i32) (result i64) (local.get 0))) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num (param f32) (i32.eqz (local.get 0)))) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num (param f64 i64) (f64.neg (local.get 1)))) + "type mismatch" +) + + +;; Invalid typing of result + +(assert_invalid + (module (func $type-empty-i32 (result i32))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-i64 (result i64))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f32 (result f32))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f64 (result f64))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f64-i32 (result f64 i32))) + "type mismatch" +) + +(assert_invalid + (module (func $type-value-void-vs-num (result i32) + (nop) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-void-vs-nums (result i32 i32) + (nop) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-num-vs-void + (i32.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-nums-vs-void + (i32.const 0) (i64.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-num-vs-num (result i32) + (f32.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-num-vs-nums (result f32 f32) + (f32.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-nums-vs-num (result f32) + (f32.const 0) (f32.const 0) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-return-last-empty-vs-num (result i32) + (return) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-last-empty-vs-nums (result i32 i32) + (return) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-last-void-vs-num (result i32) + (return (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-last-void-vs-nums (result i32 i64) + (return (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-last-num-vs-num (result i32) + (return (i64.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-last-num-vs-nums (result i64 i64) + (return (i64.const 0)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-return-empty-vs-num (result i32) + (return) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-empty-vs-nums (result i32 i32) + (return) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-partial-vs-nums (result i32 i32) + (i32.const 1) (return) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-void-vs-num (result i32) + (return (nop)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-void-vs-nums (result i32 i32) + (return (nop)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-num-vs-num (result i32) + (return (i64.const 1)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-num-vs-nums (result i32 i32) + (return (i64.const 1)) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-first-num-vs-num (result i32) + (return (i64.const 1)) (return (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-return-first-num-vs-nums (result i32 i32) + (return (i32.const 1)) (return (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-last-void-vs-num (result i32) + (br 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-void-vs-nums (result i32 i32) + (br 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-num-vs-num (result i32) + (br 0 (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-last-num-vs-nums (result i32 i32) + (br 0 (i32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-void-vs-num (result i32) + (br 0) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-void-vs-nums (result i32 i32) + (br 0) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-num-vs-num (result i32) + (br 0 (i64.const 1)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-num-vs-nums (result i32 i32) + (br 0 (i32.const 1)) (i32.const 1) (i32.const 2) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-first-num-vs-num (result i32) + (br 0 (i64.const 1)) (br 0 (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-break-nested-empty-vs-num (result i32) + (block (br 1)) (br 0 (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-empty-vs-nums (result i32 i32) + (block (br 1)) (br 0 (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-void-vs-num (result i32) + (block (br 1 (nop))) (br 0 (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-void-vs-nums (result i32 i32) + (block (br 1 (nop))) (br 0 (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-num-vs-num (result i32) + (block (br 1 (i64.const 1))) (br 0 (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-break-nested-num-vs-nums (result i32 i32) + (block (result i32) (br 1 (i32.const 1))) (br 0 (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) + + +;; Syntax errors + +(assert_malformed + (module quote "(func (nop) (local i32))") + "unexpected token" +) +(assert_malformed + (module quote "(func (nop) (param i32))") + "unexpected token" +) +(assert_malformed + (module quote "(func (nop) (result i32))") + "unexpected token" +) +(assert_malformed + (module quote "(func (local i32) (param i32))") + "unexpected token" +) +(assert_malformed + (module quote "(func (local i32) (result i32) (local.get 0))") + "unexpected token" +) +(assert_malformed + (module quote "(func (result i32) (param i32) (local.get 0))") + "unexpected token" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/multi-value/if.wast b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/if.wast new file mode 100644 index 000000000..1cbb617a7 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/if.wast @@ -0,0 +1,1550 @@ +;; Test `if` operator + +(module + ;; Auxiliary definition + (memory 1) + + (func $dummy) + + (func (export "empty") (param i32) + (if (local.get 0) (then)) + (if (local.get 0) (then) (else)) + (if $l (local.get 0) (then)) + (if $l (local.get 0) (then) (else)) + ) + + (func (export "singular") (param i32) (result i32) + (if (local.get 0) (then (nop))) + (if (local.get 0) (then (nop)) (else (nop))) + (if (result i32) (local.get 0) (then (i32.const 7)) (else (i32.const 8))) + ) + + (func (export "multi") (param i32) (result i32 i32) + (if (local.get 0) (then (call $dummy) (call $dummy) (call $dummy))) + (if (local.get 0) (then) (else (call $dummy) (call $dummy) (call $dummy))) + (if (result i32) (local.get 0) + (then (call $dummy) (call $dummy) (i32.const 8) (call $dummy)) + (else (call $dummy) (call $dummy) (i32.const 9) (call $dummy)) + ) + (if (result i32 i64 i32) (local.get 0) + (then + (call $dummy) (call $dummy) (i32.const 1) (call $dummy) + (call $dummy) (call $dummy) (i64.const 2) (call $dummy) + (call $dummy) (call $dummy) (i32.const 3) (call $dummy) + ) + (else + (call $dummy) (call $dummy) (i32.const -1) (call $dummy) + (call $dummy) (call $dummy) (i64.const -2) (call $dummy) + (call $dummy) (call $dummy) (i32.const -3) (call $dummy) + ) + ) + (drop) (drop) + ) + + (func (export "nested") (param i32 i32) (result i32) + (if (result i32) (local.get 0) + (then + (if (local.get 1) (then (call $dummy) (block) (nop))) + (if (local.get 1) (then) (else (call $dummy) (block) (nop))) + (if (result i32) (local.get 1) + (then (call $dummy) (i32.const 9)) + (else (call $dummy) (i32.const 10)) + ) + ) + (else + (if (local.get 1) (then (call $dummy) (block) (nop))) + (if (local.get 1) (then) (else (call $dummy) (block) (nop))) + (if (result i32) (local.get 1) + (then (call $dummy) (i32.const 10)) + (else (call $dummy) (i32.const 11)) + ) + ) + ) + ) + + (func (export "as-select-first") (param i32) (result i32) + (select + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 2) (i32.const 3) + ) + ) + (func (export "as-select-mid") (param i32) (result i32) + (select + (i32.const 2) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 3) + ) + ) + (func (export "as-select-last") (param i32) (result i32) + (select + (i32.const 2) (i32.const 3) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + ) + ) + + (func (export "as-loop-first") (param i32) (result i32) + (loop (result i32) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (call $dummy) (call $dummy) + ) + ) + (func (export "as-loop-mid") (param i32) (result i32) + (loop (result i32) + (call $dummy) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (call $dummy) + ) + ) + (func (export "as-loop-last") (param i32) (result i32) + (loop (result i32) + (call $dummy) (call $dummy) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + ) + ) + + (func (export "as-if-condition") (param i32) (result i32) + (if (result i32) + (if (result i32) (local.get 0) + (then (i32.const 1)) (else (i32.const 0)) + ) + (then (call $dummy) (i32.const 2)) + (else (call $dummy) (i32.const 3)) + ) + ) + + (func (export "as-br_if-first") (param i32) (result i32) + (block (result i32) + (br_if 0 + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 2) + ) + (return (i32.const 3)) + ) + ) + (func (export "as-br_if-last") (param i32) (result i32) + (block (result i32) + (br_if 0 + (i32.const 2) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + ) + (return (i32.const 3)) + ) + ) + + (func (export "as-br_table-first") (param i32) (result i32) + (block (result i32) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 2) + (br_table 0 0) + ) + ) + (func (export "as-br_table-last") (param i32) (result i32) + (block (result i32) + (i32.const 2) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (br_table 0 0) + ) + ) + + (func $func (param i32 i32) (result i32) (local.get 0)) + (type $check (func (param i32 i32) (result i32))) + (table funcref (elem $func)) + (func (export "as-call_indirect-first") (param i32) (result i32) + (block (result i32) + (call_indirect (type $check) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 2) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-mid") (param i32) (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 2) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-last") (param i32) (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 2) (i32.const 0) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + ) + ) + ) + + (func (export "as-store-first") (param i32) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.const 2) + (i32.store) + ) + (func (export "as-store-last") (param i32) + (i32.const 2) + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 1)) + (else (call $dummy) (i32.const 0)) + ) + (i32.store) + ) + + (func (export "as-memory.grow-value") (param i32) (result i32) + (memory.grow + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ) + + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "as-call-value") (param i32) (result i32) + (call $f + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ) + (func (export "as-return-value") (param i32) (result i32) + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0))) + (return) + ) + (func (export "as-drop-operand") (param i32) + (drop + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ) + (func (export "as-br-value") (param i32) (result i32) + (block (result i32) + (br 0 + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ) + ) + (func (export "as-local.set-value") (param i32) (result i32) + (local i32) + (local.set 0 + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + (local.get 0) + ) + (func (export "as-local.tee-value") (param i32) (result i32) + (local.tee 0 + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (param i32) (result i32) + (global.set $a + (if (result i32) (local.get 0) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) (global.get $a) + ) + (func (export "as-load-operand") (param i32) (result i32) + (i32.load + (if (result i32) (local.get 0) + (then (i32.const 11)) + (else (i32.const 10)) + ) + ) + ) + + (func (export "as-unary-operand") (param i32) (result i32) + (i32.ctz + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 13)) + (else (call $dummy) (i32.const -13)) + ) + ) + ) + (func (export "as-binary-operand") (param i32 i32) (result i32) + (i32.mul + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 3)) + (else (call $dummy) (i32.const -3)) + ) + (if (result i32) (local.get 1) + (then (call $dummy) (i32.const 4)) + (else (call $dummy) (i32.const -5)) + ) + ) + ) + (func (export "as-test-operand") (param i32) (result i32) + (i32.eqz + (if (result i32) (local.get 0) + (then (call $dummy) (i32.const 13)) + (else (call $dummy) (i32.const 0)) + ) + ) + ) + (func (export "as-compare-operand") (param i32 i32) (result i32) + (f32.gt + (if (result f32) (local.get 0) + (then (call $dummy) (f32.const 3)) + (else (call $dummy) (f32.const -3)) + ) + (if (result f32) (local.get 1) + (then (call $dummy) (f32.const 4)) + (else (call $dummy) (f32.const -4)) + ) + ) + ) + (func (export "as-binary-operands") (param i32) (result i32) + (i32.mul + (if (result i32 i32) (local.get 0) + (then (call $dummy) (i32.const 3) (call $dummy) (i32.const 4)) + (else (call $dummy) (i32.const 3) (call $dummy) (i32.const -4)) + ) + ) + ) + (func (export "as-compare-operands") (param i32) (result i32) + (f32.gt + (if (result f32 f32) (local.get 0) + (then (call $dummy) (f32.const 3) (call $dummy) (f32.const 3)) + (else (call $dummy) (f32.const -2) (call $dummy) (f32.const -3)) + ) + ) + ) + (func (export "as-mixed-operands") (param i32) (result i32) + (if (result i32 i32) (local.get 0) + (then (call $dummy) (i32.const 3) (call $dummy) (i32.const 4)) + (else (call $dummy) (i32.const -3) (call $dummy) (i32.const -4)) + ) + (i32.const 5) + (i32.add) + (i32.mul) + ) + + (func (export "break-bare") (result i32) + (if (i32.const 1) (then (br 0) (unreachable))) + (if (i32.const 1) (then (br 0) (unreachable)) (else (unreachable))) + (if (i32.const 0) (then (unreachable)) (else (br 0) (unreachable))) + (if (i32.const 1) (then (br_if 0 (i32.const 1)) (unreachable))) + (if (i32.const 1) (then (br_if 0 (i32.const 1)) (unreachable)) (else (unreachable))) + (if (i32.const 0) (then (unreachable)) (else (br_if 0 (i32.const 1)) (unreachable))) + (if (i32.const 1) (then (br_table 0 (i32.const 0)) (unreachable))) + (if (i32.const 1) (then (br_table 0 (i32.const 0)) (unreachable)) (else (unreachable))) + (if (i32.const 0) (then (unreachable)) (else (br_table 0 (i32.const 0)) (unreachable))) + (i32.const 19) + ) + + (func (export "break-value") (param i32) (result i32) + (if (result i32) (local.get 0) + (then (br 0 (i32.const 18)) (i32.const 19)) + (else (br 0 (i32.const 21)) (i32.const 20)) + ) + ) + (func (export "break-multi-value") (param i32) (result i32 i32 i64) + (if (result i32 i32 i64) (local.get 0) + (then + (br 0 (i32.const 18) (i32.const -18) (i64.const 18)) + (i32.const 19) (i32.const -19) (i64.const 19) + ) + (else + (br 0 (i32.const -18) (i32.const 18) (i64.const -18)) + (i32.const -19) (i32.const 19) (i64.const -19) + ) + ) + ) + + (func (export "param") (param i32) (result i32) + (i32.const 1) + (if (param i32) (result i32) (local.get 0) + (then (i32.const 2) (i32.add)) + (else (i32.const -2) (i32.add)) + ) + ) + (func (export "params") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32) (local.get 0) + (then (i32.add)) + (else (i32.sub)) + ) + ) + (func (export "params-id") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32 i32) (local.get 0) (then)) + (i32.add) + ) + (func (export "param-break") (param i32) (result i32) + (i32.const 1) + (if (param i32) (result i32) (local.get 0) + (then (i32.const 2) (i32.add) (br 0)) + (else (i32.const -2) (i32.add) (br 0)) + ) + ) + (func (export "params-break") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32) (local.get 0) + (then (i32.add) (br 0)) + (else (i32.sub) (br 0)) + ) + ) + (func (export "params-id-break") (param i32) (result i32) + (i32.const 1) + (i32.const 2) + (if (param i32 i32) (result i32 i32) (local.get 0) (then (br 0))) + (i32.add) + ) + + (func (export "effects") (param i32) (result i32) + (local i32) + (if + (block (result i32) (local.set 1 (i32.const 1)) (local.get 0)) + (then + (local.set 1 (i32.mul (local.get 1) (i32.const 3))) + (local.set 1 (i32.sub (local.get 1) (i32.const 5))) + (local.set 1 (i32.mul (local.get 1) (i32.const 7))) + (br 0) + (local.set 1 (i32.mul (local.get 1) (i32.const 100))) + ) + (else + (local.set 1 (i32.mul (local.get 1) (i32.const 5))) + (local.set 1 (i32.sub (local.get 1) (i32.const 7))) + (local.set 1 (i32.mul (local.get 1) (i32.const 3))) + (br 0) + (local.set 1 (i32.mul (local.get 1) (i32.const 1000))) + ) + ) + (local.get 1) + ) + + ;; Examples + + (func $add64_u_with_carry (export "add64_u_with_carry") + (param $i i64) (param $j i64) (param $c i32) (result i64 i32) + (local $k i64) + (local.set $k + (i64.add + (i64.add (local.get $i) (local.get $j)) + (i64.extend_i32_u (local.get $c)) + ) + ) + (return (local.get $k) (i64.lt_u (local.get $k) (local.get $i))) + ) + + (func $add64_u_saturated (export "add64_u_saturated") + (param i64 i64) (result i64) + (call $add64_u_with_carry (local.get 0) (local.get 1) (i32.const 0)) + (if (param i64) (result i64) + (then (drop) (i64.const -1)) + ) + ) + + ;; Block signature syntax + + (type $block-sig-1 (func)) + (type $block-sig-2 (func (result i32))) + (type $block-sig-3 (func (param $x i32))) + (type $block-sig-4 (func (param i32 f64 i32) (result i32 f64 i32))) + + (func (export "type-use") + (if (type $block-sig-1) (i32.const 1) (then)) + (if (type $block-sig-2) (i32.const 1) + (then (i32.const 0)) (else (i32.const 2)) + ) + (if (type $block-sig-3) (i32.const 1) (then (drop)) (else (drop))) + (i32.const 0) (f64.const 0) (i32.const 0) + (if (type $block-sig-4) (i32.const 1) (then)) + (drop) (drop) (drop) + (if (type $block-sig-2) (result i32) (i32.const 1) + (then (i32.const 0)) (else (i32.const 2)) + ) + (if (type $block-sig-3) (param i32) (i32.const 1) + (then (drop)) (else (drop)) + ) + (i32.const 0) (f64.const 0) (i32.const 0) + (if (type $block-sig-4) + (param i32) (param f64 i32) (result i32 f64) (result i32) + (i32.const 1) (then) + ) + (drop) (drop) (drop) + ) +) + +(assert_return (invoke "empty" (i32.const 0))) +(assert_return (invoke "empty" (i32.const 1))) +(assert_return (invoke "empty" (i32.const 100))) +(assert_return (invoke "empty" (i32.const -2))) + +(assert_return (invoke "singular" (i32.const 0)) (i32.const 8)) +(assert_return (invoke "singular" (i32.const 1)) (i32.const 7)) +(assert_return (invoke "singular" (i32.const 10)) (i32.const 7)) +(assert_return (invoke "singular" (i32.const -10)) (i32.const 7)) + +(assert_return (invoke "multi" (i32.const 0)) (i32.const 9) (i32.const -1)) +(assert_return (invoke "multi" (i32.const 1)) (i32.const 8) (i32.const 1)) +(assert_return (invoke "multi" (i32.const 13)) (i32.const 8) (i32.const 1)) +(assert_return (invoke "multi" (i32.const -5)) (i32.const 8) (i32.const 1)) + +(assert_return (invoke "nested" (i32.const 0) (i32.const 0)) (i32.const 11)) +(assert_return (invoke "nested" (i32.const 1) (i32.const 0)) (i32.const 10)) +(assert_return (invoke "nested" (i32.const 0) (i32.const 1)) (i32.const 10)) +(assert_return (invoke "nested" (i32.const 3) (i32.const 2)) (i32.const 9)) +(assert_return (invoke "nested" (i32.const 0) (i32.const -100)) (i32.const 10)) +(assert_return (invoke "nested" (i32.const 10) (i32.const 10)) (i32.const 9)) +(assert_return (invoke "nested" (i32.const 0) (i32.const -1)) (i32.const 10)) +(assert_return (invoke "nested" (i32.const -111) (i32.const -2)) (i32.const 9)) + +(assert_return (invoke "as-select-first" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-select-first" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-select-mid" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-select-mid" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-select-last" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-select-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-loop-first" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-loop-first" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-loop-mid" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-loop-mid" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-loop-last" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-loop-last" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-if-condition" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-if-condition" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-br_if-first" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-br_if-first" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-br_if-last" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-br_if-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-br_table-first" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-br_table-first" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-br_table-last" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-br_table-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-call_indirect-first" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-call_indirect-first" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-call_indirect-mid" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-call_indirect-mid" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-call_indirect-last" (i32.const 0)) (i32.const 2)) +(assert_trap (invoke "as-call_indirect-last" (i32.const 1)) "undefined element") + +(assert_return (invoke "as-store-first" (i32.const 0))) +(assert_return (invoke "as-store-first" (i32.const 1))) +(assert_return (invoke "as-store-last" (i32.const 0))) +(assert_return (invoke "as-store-last" (i32.const 1))) + +(assert_return (invoke "as-memory.grow-value" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-memory.grow-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-call-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-call-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-return-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-return-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-drop-operand" (i32.const 0))) +(assert_return (invoke "as-drop-operand" (i32.const 1))) + +(assert_return (invoke "as-br-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-br-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-local.set-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-local.set-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-local.tee-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-local.tee-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-global.set-value" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-global.set-value" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-load-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-load-operand" (i32.const 1)) (i32.const 0)) + +(assert_return (invoke "as-unary-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-unary-operand" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "as-unary-operand" (i32.const -1)) (i32.const 0)) + +(assert_return (invoke "as-binary-operand" (i32.const 0) (i32.const 0)) (i32.const 15)) +(assert_return (invoke "as-binary-operand" (i32.const 0) (i32.const 1)) (i32.const -12)) +(assert_return (invoke "as-binary-operand" (i32.const 1) (i32.const 0)) (i32.const -15)) +(assert_return (invoke "as-binary-operand" (i32.const 1) (i32.const 1)) (i32.const 12)) + +(assert_return (invoke "as-test-operand" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-test-operand" (i32.const 1)) (i32.const 0)) + +(assert_return (invoke "as-compare-operand" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-compare-operand" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "as-compare-operand" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-compare-operand" (i32.const 1) (i32.const 1)) (i32.const 0)) + +(assert_return (invoke "as-binary-operands" (i32.const 0)) (i32.const -12)) +(assert_return (invoke "as-binary-operands" (i32.const 1)) (i32.const 12)) + +(assert_return (invoke "as-compare-operands" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-compare-operands" (i32.const 1)) (i32.const 0)) + +(assert_return (invoke "as-mixed-operands" (i32.const 0)) (i32.const -3)) +(assert_return (invoke "as-mixed-operands" (i32.const 1)) (i32.const 27)) + +(assert_return (invoke "break-bare") (i32.const 19)) +(assert_return (invoke "break-value" (i32.const 1)) (i32.const 18)) +(assert_return (invoke "break-value" (i32.const 0)) (i32.const 21)) +(assert_return (invoke "break-multi-value" (i32.const 0)) + (i32.const -18) (i32.const 18) (i64.const -18) +) +(assert_return (invoke "break-multi-value" (i32.const 1)) + (i32.const 18) (i32.const -18) (i64.const 18) +) + +(assert_return (invoke "param" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "param" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "params" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params-id" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "params-id" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "param-break" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "param-break" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params-break" (i32.const 0)) (i32.const -1)) +(assert_return (invoke "params-break" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "params-id-break" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "params-id-break" (i32.const 1)) (i32.const 3)) + +(assert_return (invoke "effects" (i32.const 1)) (i32.const -14)) +(assert_return (invoke "effects" (i32.const 0)) (i32.const -6)) + +(assert_return + (invoke "add64_u_with_carry" (i64.const 0) (i64.const 0) (i32.const 0)) + (i64.const 0) (i32.const 0) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const 100) (i64.const 124) (i32.const 0)) + (i64.const 224) (i32.const 0) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 0) (i32.const 0)) + (i64.const -1) (i32.const 0) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 1) (i32.const 0)) + (i64.const 0) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const -1) (i32.const 0)) + (i64.const -2) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 0) (i32.const 1)) + (i64.const 0) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const -1) (i64.const 1) (i32.const 1)) + (i64.const 1) (i32.const 1) +) +(assert_return + (invoke "add64_u_with_carry" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000) (i32.const 0)) + (i64.const 0) (i32.const 1) +) + +(assert_return + (invoke "add64_u_saturated" (i64.const 0) (i64.const 0)) (i64.const 0) +) +(assert_return + (invoke "add64_u_saturated" (i64.const 1230) (i64.const 23)) (i64.const 1253) +) +(assert_return + (invoke "add64_u_saturated" (i64.const -1) (i64.const 0)) (i64.const -1) +) +(assert_return + (invoke "add64_u_saturated" (i64.const -1) (i64.const 1)) (i64.const -1) +) +(assert_return + (invoke "add64_u_saturated" (i64.const -1) (i64.const -1)) (i64.const -1) +) +(assert_return + (invoke "add64_u_saturated" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i64.const -1) +) + +(assert_return (invoke "type-use")) + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (type $sig) (result i32) (param i32) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (param i32) (type $sig) (result i32) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (param i32) (result i32) (type $sig) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (result i32) (type $sig) (param i32) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0)" + " (if (result i32) (param i32) (type $sig) (i32.const 1) (then))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func (i32.const 0) (if (result i32) (param i32) (i32.const 1) (then)))" + ) + "unexpected token" +) + +(assert_malformed + (module quote + "(func (i32.const 0) (i32.const 1)" + " (if (param $x i32) (then (drop)) (else (drop)))" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func))" + "(func (i32.const 1)" + " (if (type $sig) (result i32) (then (i32.const 0)) (else (i32.const 2)))" + " (unreachable)" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 1)" + " (if (type $sig) (result i32) (then (i32.const 0)) (else (i32.const 2)))" + " (unreachable)" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (i32.const 1)" + " (if (type $sig) (param i32) (then (drop)) (else (drop)))" + " (unreachable)" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(func (i32.const 0) (i32.const 1)" + " (if (type $sig) (param i32) (result i32) (then)) (unreachable)" + ")" + ) + "inline function type" +) + +(assert_invalid + (module + (type $sig (func)) + (func (i32.const 1) (if (type $sig) (i32.const 0) (then))) + ) + "type mismatch" +) + +(assert_invalid + (module (func $type-empty-i32 (result i32) (if (i32.const 0) (then)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-i64 (result i64) (if (i32.const 0) (then)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f32 (result f32) (if (i32.const 0) (then)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f64 (result f64) (if (i32.const 0) (then)))) + "type mismatch" +) + +(assert_invalid + (module (func $type-empty-i32 (result i32) (if (i32.const 0) (then) (else)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-i64 (result i64) (if (i32.const 0) (then) (else)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f32 (result f32) (if (i32.const 0) (then) (else)))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f64 (result f64) (if (i32.const 0) (then) (else)))) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-num-vs-void + (if (i32.const 1) (then (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-value-num-vs-void-else + (if (i32.const 1) (then (i32.const 1)) (else)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-num-vs-void + (if (i32.const 1) (then) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-num-vs-void + (if (i32.const 1) (then (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-nums-vs-void + (if (i32.const 1) (then (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-value-nums-vs-void-else + (if (i32.const 1) (then (i32.const 1) (i32.const 2)) (else)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-nums-vs-void + (if (i32.const 1) (then) (else (i32.const 1) (i32.const 2))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-nums-vs-void + (if (i32.const 1) (then (i32.const 1) (i32.const 2)) (else (i32.const 2) (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-empty-vs-num (result i32) + (if (result i32) (i32.const 1) (then) (else (i32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-empty-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 0)) (else)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-empty-vs-num (result i32) + (if (result i32) (i32.const 1) (then) (else)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then) (else (i32.const 0) (i32.const 2))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 0) (i32.const 1)) (else)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then) (else)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-no-else-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-no-else-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-void-vs-num (result i32) + (if (result i32) (i32.const 1) (then (nop)) (else (i32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-void-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 0)) (else (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-void-vs-num (result i32) + (if (result i32) (i32.const 1) (then (nop)) (else (nop))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (nop)) (else (i32.const 0) (i32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 0) (i32.const 0)) (else (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (nop)) (else (nop))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-num-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i64.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-num-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1)) (else (i64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-num-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i64.const 1)) (else (i64.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-partial-vs-nums (result i32 i32) + (i32.const 0) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-partial-vs-nums (result i32 i32) + (i32.const 0) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-partial-vs-nums (result i32 i32) + (i32.const 0) + (if (result i32 i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-nums-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-nums-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-value-nums-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-both-different-value-num-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i64.const 1)) (else (f64.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-both-different-value-nums-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1) (i32.const 1)) (else (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-value-unreached-select (result i32) + (if (result i64) + (i32.const 0) + (then (select (unreachable) (unreachable) (unreachable))) + (else (i64.const 0)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-unreached-select (result i32) + (if (result i64) + (i32.const 1) + (then (i64.const 0)) + (else (select (unreachable) (unreachable) (unreachable))) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-value-unreached-select (result i32) + (if (result i64) + (i32.const 1) + (then (select (unreachable) (unreachable) (unreachable))) + (else (select (unreachable) (unreachable) (unreachable))) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-break-last-void-vs-num (result i32) + (if (result i32) (i32.const 1) (then (br 0)) (else (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-last-void-vs-num (result i32) + (if (result i32) (i32.const 1) (then (i32.const 1)) (else (br 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-last-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (br 0)) (else (i32.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-last-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) (then (i32.const 1) (i32.const 1)) (else (br 0))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-break-empty-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (br 0) (i32.const 1)) + (else (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-empty-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (i32.const 1)) + (else (br 0) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (br 0) (i32.const 1) (i32.const 1)) + (else (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-empty-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1) (i32.const 1)) + (else (br 0) (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-break-void-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (br 0 (nop)) (i32.const 1)) + (else (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-void-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (i32.const 1)) + (else (br 0 (nop)) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (br 0 (nop)) (i32.const 1) (i32.const 1)) + (else (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-void-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1) (i32.const 1)) + (else (br 0 (nop)) (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-then-break-num-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (br 0 (i64.const 1)) (i32.const 1)) + (else (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-num-vs-num (result i32) + (if (result i32) (i32.const 1) + (then (i32.const 1)) + (else (br 0 (i64.const 1)) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (br 0 (i64.const 1)) (i32.const 1) (i32.const 1)) + (else (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-num-vs-nums (result i32 i32) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1) (i32.const 1)) + (else (br 0 (i64.const 1)) (i32.const 1) (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-then-break-partial-vs-nums (result i32 i32) + (i32.const 1) + (if (result i32 i32) (i32.const 1) + (then (br 0 (i64.const 1)) (i32.const 1)) + (else (i32.const 1)) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-else-break-partial-vs-nums (result i32 i32) + (i32.const 1) + (if (result i32 i32) (i32.const 1) + (then (i32.const 1)) + (else (br 0 (i64.const 1)) (i32.const 1)) + ) + )) + "type mismatch" +) + +(assert_invalid + (module + (func $type-condition-empty + (if (then)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-block + (i32.const 0) + (block (if (then))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-loop + (i32.const 0) + (loop (if (then))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-then + (i32.const 0) (i32.const 0) + (if (then (if (then)))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-else + (i32.const 0) (i32.const 0) + (if (result i32) (then (i32.const 0)) (else (if (then)) (i32.const 0))) + (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-br + (i32.const 0) + (block (br 0 (if(then))) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-br_if + (i32.const 0) + (block (br_if 0 (if(then)) (i32.const 1)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-br_table + (i32.const 0) + (block (br_table 0 (if(then))) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-return + (return (if(then))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-select + (select (if(then)) (i32.const 1) (i32.const 2)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-call + (call 1 (if(then))) (drop) + ) + (func (param i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-condition-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (if(then)) (i32.const 0) + ) + (drop) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-local.set + (local i32) + (local.set 0 (if(then))) (local.get 0) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-condition-empty-in-local.tee + (local i32) + (local.tee 0 (if(then))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-condition-empty-in-global.set + (global.set $x (if(then))) (global.get $x) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-condition-empty-in-memory.grow + (memory.grow (if(then))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-condition-empty-in-load + (i32.load (if(then))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-condition-empty-in-store + (i32.store (if(then)) (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module (func $type-param-void-vs-num + (if (param i32) (i32.const 1) (then (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (if (param i32 f64) (i32.const 1) (then (drop) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (f32.const 0) (if (param i32) (i32.const 1) (then (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (f32.const 0) (if (param f32 i32) (i32.const 1) (then (drop) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-nested-void-vs-num + (block (if (param i32) (i32.const 1) (then (drop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (block (if (param i32 f64) (i32.const 1) (then (drop) (drop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (block (f32.const 0) (if (param i32) (i32.const 1) (then (drop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (block (f32.const 0) (if (param f32 i32) (i32.const 1) (then (drop) (drop)))) + )) + "type mismatch" +) + +(assert_malformed + (module quote "(func (param i32) (result i32) if (param $x i32) end)") + "unexpected token" +) +(assert_malformed + (module quote "(func (param i32) (result i32) (if (param $x i32) (then)))") + "unexpected token" +) + +(assert_malformed + (module quote "(func i32.const 0 if end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if $a end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if else $l end)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if $a else $l end)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if else end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if else $l end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if else $l1 end $l2)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if $a else end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if $a else $a end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func i32.const 0 if $a else $l end $l)") + "mismatching label" +) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/multi-value/loop.wast b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/loop.wast new file mode 100644 index 000000000..4557869b3 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/loop.wast @@ -0,0 +1,785 @@ +;; Test `loop` opcode + +(module + (memory 1) + + (func $dummy) + + (func (export "empty") + (loop) + (loop $l) + ) + + (func (export "singular") (result i32) + (loop (nop)) + (loop (result i32) (i32.const 7)) + ) + + (func (export "multi") (result i32) + (loop (call $dummy) (call $dummy) (call $dummy) (call $dummy)) + (loop (result i32) (call $dummy) (call $dummy) (i32.const 8) (call $dummy)) + (drop) + (loop (result i32 i64 i32) + (call $dummy) (call $dummy) (call $dummy) (i32.const 8) (call $dummy) + (call $dummy) (call $dummy) (call $dummy) (i64.const 7) (call $dummy) + (call $dummy) (call $dummy) (call $dummy) (i32.const 9) (call $dummy) + ) + (drop) (drop) + ) + + (func (export "nested") (result i32) + (loop (result i32) + (loop (call $dummy) (block) (nop)) + (loop (result i32) (call $dummy) (i32.const 9)) + ) + ) + + (func (export "deep") (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (call $dummy) (i32.const 150) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + ) + + (func (export "as-select-first") (result i32) + (select (loop (result i32) (i32.const 1)) (i32.const 2) (i32.const 3)) + ) + (func (export "as-select-mid") (result i32) + (select (i32.const 2) (loop (result i32) (i32.const 1)) (i32.const 3)) + ) + (func (export "as-select-last") (result i32) + (select (i32.const 2) (i32.const 3) (loop (result i32) (i32.const 1))) + ) + + (func (export "as-if-condition") + (loop (result i32) (i32.const 1)) (if (then (call $dummy))) + ) + (func (export "as-if-then") (result i32) + (if (result i32) (i32.const 1) (then (loop (result i32) (i32.const 1))) (else (i32.const 2))) + ) + (func (export "as-if-else") (result i32) + (if (result i32) (i32.const 1) (then (i32.const 2)) (else (loop (result i32) (i32.const 1)))) + ) + + (func (export "as-br_if-first") (result i32) + (block (result i32) (br_if 0 (loop (result i32) (i32.const 1)) (i32.const 2))) + ) + (func (export "as-br_if-last") (result i32) + (block (result i32) (br_if 0 (i32.const 2) (loop (result i32) (i32.const 1)))) + ) + + (func (export "as-br_table-first") (result i32) + (block (result i32) (loop (result i32) (i32.const 1)) (i32.const 2) (br_table 0 0)) + ) + (func (export "as-br_table-last") (result i32) + (block (result i32) (i32.const 2) (loop (result i32) (i32.const 1)) (br_table 0 0)) + ) + + (func $func (param i32 i32) (result i32) (local.get 0)) + (type $check (func (param i32 i32) (result i32))) + (table funcref (elem $func)) + (func (export "as-call_indirect-first") (result i32) + (block (result i32) + (call_indirect (type $check) + (loop (result i32) (i32.const 1)) (i32.const 2) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 2) (loop (result i32) (i32.const 1)) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 1) (i32.const 2) (loop (result i32) (i32.const 0)) + ) + ) + ) + + (func (export "as-store-first") + (loop (result i32) (i32.const 1)) (i32.const 1) (i32.store) + ) + (func (export "as-store-last") + (i32.const 10) (loop (result i32) (i32.const 1)) (i32.store) + ) + + (func (export "as-memory.grow-value") (result i32) + (memory.grow (loop (result i32) (i32.const 1))) + ) + + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "as-call-value") (result i32) + (call $f (loop (result i32) (i32.const 1))) + ) + (func (export "as-return-value") (result i32) + (loop (result i32) (i32.const 1)) (return) + ) + (func (export "as-drop-operand") + (drop (loop (result i32) (i32.const 1))) + ) + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (loop (result i32) (i32.const 1)))) + ) + (func (export "as-local.set-value") (result i32) + (local i32) (local.set 0 (loop (result i32) (i32.const 1))) (local.get 0) + ) + (func (export "as-local.tee-value") (result i32) + (local i32) (local.tee 0 (loop (result i32) (i32.const 1))) + ) + (global $a (mut i32) (i32.const 0)) + (func (export "as-global.set-value") (result i32) + (global.set $a (loop (result i32) (i32.const 1))) + (global.get $a) + ) + (func (export "as-load-operand") (result i32) + (i32.load (loop (result i32) (i32.const 1))) + ) + + (func (export "as-unary-operand") (result i32) + (i32.ctz (loop (result i32) (call $dummy) (i32.const 13))) + ) + (func (export "as-binary-operand") (result i32) + (i32.mul + (loop (result i32) (call $dummy) (i32.const 3)) + (loop (result i32) (call $dummy) (i32.const 4)) + ) + ) + (func (export "as-test-operand") (result i32) + (i32.eqz (loop (result i32) (call $dummy) (i32.const 13))) + ) + (func (export "as-compare-operand") (result i32) + (f32.gt + (loop (result f32) (call $dummy) (f32.const 3)) + (loop (result f32) (call $dummy) (f32.const 3)) + ) + ) + (func (export "as-binary-operands") (result i32) + (i32.mul + (loop (result i32 i32) + (call $dummy) (i32.const 3) (call $dummy) (i32.const 4) + ) + ) + ) + (func (export "as-compare-operands") (result i32) + (f32.gt + (loop (result f32 f32) + (call $dummy) (f32.const 3) (call $dummy) (f32.const 3) + ) + ) + ) + (func (export "as-mixed-operands") (result i32) + (loop (result i32 i32) + (call $dummy) (i32.const 3) (call $dummy) (i32.const 4) + ) + (i32.const 5) + (i32.add) + (i32.mul) + ) + + (func (export "break-bare") (result i32) + (block (loop (br 1) (br 0) (unreachable))) + (block (loop (br_if 1 (i32.const 1)) (unreachable))) + (block (loop (br_table 1 (i32.const 0)) (unreachable))) + (block (loop (br_table 1 1 1 (i32.const 1)) (unreachable))) + (i32.const 19) + ) + (func (export "break-value") (result i32) + (block (result i32) + (i32.const 0) + (loop (param i32) + (block (br 2 (i32.const 18))) + (br 0 (i32.const 20)) + ) + (i32.const 19) + ) + ) + (func (export "break-multi-value") (result i32 i32 i64) + (block (result i32 i32 i64) + (i32.const 0) (i32.const 0) (i64.const 0) + (loop (param i32 i32 i64) + (block (br 2 (i32.const 18) (i32.const -18) (i64.const 18))) + (br 0 (i32.const 20) (i32.const -20) (i64.const 20)) + ) + (i32.const 19) (i32.const -19) (i64.const 19) + ) + ) + (func (export "break-repeated") (result i32) + (block (result i32) + (loop (result i32) + (br 1 (i32.const 18)) + (br 1 (i32.const 19)) + (drop (br_if 1 (i32.const 20) (i32.const 0))) + (drop (br_if 1 (i32.const 20) (i32.const 1))) + (br 1 (i32.const 21)) + (br_table 1 (i32.const 22) (i32.const 0)) + (br_table 1 1 1 (i32.const 23) (i32.const 1)) + (i32.const 21) + ) + ) + ) + (func (export "break-inner") (result i32) + (local i32) + (local.set 0 (i32.const 0)) + (local.set 0 (i32.add (local.get 0) (block (result i32) (loop (result i32) (block (result i32) (br 2 (i32.const 0x1))))))) + (local.set 0 (i32.add (local.get 0) (block (result i32) (loop (result i32) (loop (result i32) (br 2 (i32.const 0x2))))))) + (local.set 0 (i32.add (local.get 0) (block (result i32) (loop (result i32) (block (result i32) (loop (result i32) (br 1 (i32.const 0x4)))))))) + (local.set 0 (i32.add (local.get 0) (block (result i32) (loop (result i32) (i32.ctz (br 1 (i32.const 0x8))))))) + (local.set 0 (i32.add (local.get 0) (block (result i32) (loop (result i32) (i32.ctz (loop (result i32) (br 2 (i32.const 0x10)))))))) + (local.get 0) + ) + (func (export "cont-inner") (result i32) + (local i32) + (local.set 0 (i32.const 0)) + (local.set 0 (i32.add (local.get 0) (loop (result i32) (loop (result i32) (br 1))))) + (local.set 0 (i32.add (local.get 0) (loop (result i32) (i32.ctz (br 0))))) + (local.set 0 (i32.add (local.get 0) (loop (result i32) (i32.ctz (loop (result i32) (br 1)))))) + (local.get 0) + ) + + (func (export "param") (result i32) + (i32.const 1) + (loop (param i32) (result i32) + (i32.const 2) + (i32.add) + ) + ) + (func (export "params") (result i32) + (i32.const 1) + (i32.const 2) + (loop (param i32 i32) (result i32) + (i32.add) + ) + ) + (func (export "params-id") (result i32) + (i32.const 1) + (i32.const 2) + (loop (param i32 i32) (result i32 i32)) + (i32.add) + ) + (func (export "param-break") (result i32) + (local $x i32) + (i32.const 1) + (loop (param i32) (result i32) + (i32.const 4) + (i32.add) + (local.tee $x) + (local.get $x) + (i32.const 10) + (i32.lt_u) + (br_if 0) + ) + ) + (func (export "params-break") (result i32) + (local $x i32) + (i32.const 1) + (i32.const 2) + (loop (param i32 i32) (result i32) + (i32.add) + (local.tee $x) + (i32.const 3) + (local.get $x) + (i32.const 10) + (i32.lt_u) + (br_if 0) + (drop) + ) + ) + (func (export "params-id-break") (result i32) + (local $x i32) + (local.set $x (i32.const 0)) + (i32.const 1) + (i32.const 2) + (loop (param i32 i32) (result i32 i32) + (local.set $x (i32.add (local.get $x) (i32.const 1))) + (br_if 0 (i32.lt_u (local.get $x) (i32.const 10))) + ) + (i32.add) + ) + + (func $fx (export "effects") (result i32) + (local i32) + (block + (loop + (local.set 0 (i32.const 1)) + (local.set 0 (i32.mul (local.get 0) (i32.const 3))) + (local.set 0 (i32.sub (local.get 0) (i32.const 5))) + (local.set 0 (i32.mul (local.get 0) (i32.const 7))) + (br 1) + (local.set 0 (i32.mul (local.get 0) (i32.const 100))) + ) + ) + (i32.eq (local.get 0) (i32.const -14)) + ) + + (func (export "while") (param i64) (result i64) + (local i64) + (local.set 1 (i64.const 1)) + (block + (loop + (br_if 1 (i64.eqz (local.get 0))) + (local.set 1 (i64.mul (local.get 0) (local.get 1))) + (local.set 0 (i64.sub (local.get 0) (i64.const 1))) + (br 0) + ) + ) + (local.get 1) + ) + + (func (export "for") (param i64) (result i64) + (local i64 i64) + (local.set 1 (i64.const 1)) + (local.set 2 (i64.const 2)) + (block + (loop + (br_if 1 (i64.gt_u (local.get 2) (local.get 0))) + (local.set 1 (i64.mul (local.get 1) (local.get 2))) + (local.set 2 (i64.add (local.get 2) (i64.const 1))) + (br 0) + ) + ) + (local.get 1) + ) + + (func (export "nesting") (param f32 f32) (result f32) + (local f32 f32) + (block + (loop + (br_if 1 (f32.eq (local.get 0) (f32.const 0))) + (local.set 2 (local.get 1)) + (block + (loop + (br_if 1 (f32.eq (local.get 2) (f32.const 0))) + (br_if 3 (f32.lt (local.get 2) (f32.const 0))) + (local.set 3 (f32.add (local.get 3) (local.get 2))) + (local.set 2 (f32.sub (local.get 2) (f32.const 2))) + (br 0) + ) + ) + (local.set 3 (f32.div (local.get 3) (local.get 0))) + (local.set 0 (f32.sub (local.get 0) (f32.const 1))) + (br 0) + ) + ) + (local.get 3) + ) + + (type $block-sig-1 (func)) + (type $block-sig-2 (func (result i32))) + (type $block-sig-3 (func (param $x i32))) + (type $block-sig-4 (func (param i32 f64 i32) (result i32 f64 i32))) + + (func (export "type-use") + (loop (type $block-sig-1)) + (loop (type $block-sig-2) (i32.const 0)) + (loop (type $block-sig-3) (drop)) + (i32.const 0) (f64.const 0) (i32.const 0) + (loop (type $block-sig-4)) + (drop) (drop) (drop) + (loop (type $block-sig-2) (result i32) (i32.const 0)) + (loop (type $block-sig-3) (param i32) (drop)) + (i32.const 0) (f64.const 0) (i32.const 0) + (loop (type $block-sig-4) + (param i32) (param f64 i32) (result i32 f64) (result i32) + ) + (drop) (drop) (drop) + ) +) + +(assert_return (invoke "empty")) +(assert_return (invoke "singular") (i32.const 7)) +(assert_return (invoke "multi") (i32.const 8)) +(assert_return (invoke "nested") (i32.const 9)) +(assert_return (invoke "deep") (i32.const 150)) + +(assert_return (invoke "as-select-first") (i32.const 1)) +(assert_return (invoke "as-select-mid") (i32.const 2)) +(assert_return (invoke "as-select-last") (i32.const 2)) + +(assert_return (invoke "as-if-condition")) +(assert_return (invoke "as-if-then") (i32.const 1)) +(assert_return (invoke "as-if-else") (i32.const 2)) + +(assert_return (invoke "as-br_if-first") (i32.const 1)) +(assert_return (invoke "as-br_if-last") (i32.const 2)) + +(assert_return (invoke "as-br_table-first") (i32.const 1)) +(assert_return (invoke "as-br_table-last") (i32.const 2)) + +(assert_return (invoke "as-call_indirect-first") (i32.const 1)) +(assert_return (invoke "as-call_indirect-mid") (i32.const 2)) +(assert_return (invoke "as-call_indirect-last") (i32.const 1)) + +(assert_return (invoke "as-store-first")) +(assert_return (invoke "as-store-last")) + +(assert_return (invoke "as-memory.grow-value") (i32.const 1)) +(assert_return (invoke "as-call-value") (i32.const 1)) +(assert_return (invoke "as-return-value") (i32.const 1)) +(assert_return (invoke "as-drop-operand")) +(assert_return (invoke "as-br-value") (i32.const 1)) +(assert_return (invoke "as-local.set-value") (i32.const 1)) +(assert_return (invoke "as-local.tee-value") (i32.const 1)) +(assert_return (invoke "as-global.set-value") (i32.const 1)) +(assert_return (invoke "as-load-operand") (i32.const 1)) + +(assert_return (invoke "as-unary-operand") (i32.const 0)) +(assert_return (invoke "as-binary-operand") (i32.const 12)) +(assert_return (invoke "as-test-operand") (i32.const 0)) +(assert_return (invoke "as-compare-operand") (i32.const 0)) +(assert_return (invoke "as-binary-operands") (i32.const 12)) +(assert_return (invoke "as-compare-operands") (i32.const 0)) +(assert_return (invoke "as-mixed-operands") (i32.const 27)) + +(assert_return (invoke "break-bare") (i32.const 19)) +(assert_return (invoke "break-value") (i32.const 18)) +(assert_return (invoke "break-multi-value") + (i32.const 18) (i32.const -18) (i64.const 18) +) +(assert_return (invoke "break-repeated") (i32.const 18)) +(assert_return (invoke "break-inner") (i32.const 0x1f)) + +(assert_return (invoke "param") (i32.const 3)) +(assert_return (invoke "params") (i32.const 3)) +(assert_return (invoke "params-id") (i32.const 3)) +(assert_return (invoke "param-break") (i32.const 13)) +(assert_return (invoke "params-break") (i32.const 12)) +(assert_return (invoke "params-id-break") (i32.const 3)) + +(assert_return (invoke "effects") (i32.const 1)) + +(assert_return (invoke "while" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "while" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "while" (i64.const 2)) (i64.const 2)) +(assert_return (invoke "while" (i64.const 3)) (i64.const 6)) +(assert_return (invoke "while" (i64.const 5)) (i64.const 120)) +(assert_return (invoke "while" (i64.const 20)) (i64.const 2432902008176640000)) + +(assert_return (invoke "for" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "for" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "for" (i64.const 2)) (i64.const 2)) +(assert_return (invoke "for" (i64.const 3)) (i64.const 6)) +(assert_return (invoke "for" (i64.const 5)) (i64.const 120)) +(assert_return (invoke "for" (i64.const 20)) (i64.const 2432902008176640000)) + +(assert_return (invoke "nesting" (f32.const 0) (f32.const 7)) (f32.const 0)) +(assert_return (invoke "nesting" (f32.const 7) (f32.const 0)) (f32.const 0)) +(assert_return (invoke "nesting" (f32.const 1) (f32.const 1)) (f32.const 1)) +(assert_return (invoke "nesting" (f32.const 1) (f32.const 2)) (f32.const 2)) +(assert_return (invoke "nesting" (f32.const 1) (f32.const 3)) (f32.const 4)) +(assert_return (invoke "nesting" (f32.const 1) (f32.const 4)) (f32.const 6)) +(assert_return (invoke "nesting" (f32.const 1) (f32.const 100)) (f32.const 2550)) +(assert_return (invoke "nesting" (f32.const 1) (f32.const 101)) (f32.const 2601)) +(assert_return (invoke "nesting" (f32.const 2) (f32.const 1)) (f32.const 1)) +(assert_return (invoke "nesting" (f32.const 3) (f32.const 1)) (f32.const 1)) +(assert_return (invoke "nesting" (f32.const 10) (f32.const 1)) (f32.const 1)) +(assert_return (invoke "nesting" (f32.const 2) (f32.const 2)) (f32.const 3)) +(assert_return (invoke "nesting" (f32.const 2) (f32.const 3)) (f32.const 4)) +(assert_return (invoke "nesting" (f32.const 7) (f32.const 4)) (f32.const 10.3095235825)) +(assert_return (invoke "nesting" (f32.const 7) (f32.const 100)) (f32.const 4381.54785156)) +(assert_return (invoke "nesting" (f32.const 7) (f32.const 101)) (f32.const 2601)) + +(assert_return (invoke "type-use")) + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (loop (type $sig) (result i32) (param i32)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (loop (param i32) (type $sig) (result i32)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (loop (param i32) (result i32) (type $sig)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (loop (result i32) (type $sig) (param i32)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (loop (result i32) (param i32) (type $sig)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func (i32.const 0) (loop (result i32) (param i32)))" + ) + "unexpected token" +) + +(assert_malformed + (module quote "(func (i32.const 0) (loop (param $x i32) (drop)))") + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func))" + "(func (loop (type $sig) (result i32) (i32.const 0)) (unreachable))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (loop (type $sig) (result i32) (i32.const 0)) (unreachable))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(func (i32.const 0) (loop (type $sig) (param i32) (drop)) (unreachable))" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(func (i32.const 0) (loop (type $sig) (param i32) (result i32)) (unreachable))" + ) + "inline function type" +) + +(assert_invalid + (module + (type $sig (func)) + (func (loop (type $sig) (i32.const 0))) + ) + "type mismatch" +) + +(assert_invalid + (module (func $type-empty-i32 (result i32) (loop))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-i64 (result i64) (loop))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f32 (result f32) (loop))) + "type mismatch" +) +(assert_invalid + (module (func $type-empty-f64 (result f64) (loop))) + "type mismatch" +) + +(assert_invalid + (module (func $type-value-num-vs-void + (loop (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-nums-vs-void + (loop (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-empty-vs-num (result i32) + (loop (result i32)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-empty-vs-nums (result i32 i32) + (loop (result i32 i32)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-void-vs-num (result i32) + (loop (result i32) (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-void-vs-nums (result i32 i32) + (loop (result i32 i32) (nop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-num-vs-num (result i32) + (loop (result i32) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-num-vs-nums (result i32 i32) + (loop (result i32 i32) (i32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-partial-vs-nums (result i32 i32) + (i32.const 1) (loop (result i32 i32) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-nums-vs-num (result i32) + (loop (result i32) (i32.const 1) (i32.const 2)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-value-unreached-select (result i32) + (loop (result i64) (select (unreachable) (unreachable) (unreachable))) + )) + "type mismatch" +) + +(assert_invalid + (module + (func $type-value-empty-in-block + (i32.const 0) + (block (loop (result i32)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-in-loop + (i32.const 0) + (loop (loop (result i32)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-in-then + (i32.const 0) (i32.const 0) + (if (then (loop (result i32)) (drop))) + ) + ) + "type mismatch" +) + +(assert_invalid + (module (func $type-param-void-vs-num + (loop (param i32) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (loop (param i32 f64) (drop) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (f32.const 0) (loop (param i32) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (f32.const 0) (loop (param f32 i32) (drop) (drop)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-nested-void-vs-num + (block (loop (param i32) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-void-vs-nums + (block (loop (param i32 f64) (drop) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-num + (block (f32.const 0) (loop (param i32) (drop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-param-num-vs-nums + (block (f32.const 0) (loop (param f32 i32) (drop) (drop))) + )) + "type mismatch" +) + +(assert_malformed + (module quote "(func (param i32) (result i32) loop (param $x i32) end)") + "unexpected token" +) +(assert_malformed + (module quote "(func (param i32) (result i32) (loop (param $x i32)))") + "unexpected token" +) + +(assert_malformed + (module quote "(func loop end $l)") + "mismatching label" +) +(assert_malformed + (module quote "(func loop $a end $l)") + "mismatching label" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/multi-value/type.wast b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/type.wast new file mode 100644 index 000000000..b94063e6a --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/multi-value/type.wast @@ -0,0 +1,50 @@ +;; Test type definitions + +(module + (type (func)) + (type $t (func)) + + (type (func (param i32))) + (type (func (param $x i32))) + (type (func (result i32))) + (type (func (param i32) (result i32))) + (type (func (param $x i32) (result i32))) + + (type (func (param f32 f64))) + (type (func (result i64 f32))) + (type (func (param i32 i64) (result f32 f64))) + + (type (func (param f32) (param f64))) + (type (func (param $x f32) (param f64))) + (type (func (param f32) (param $y f64))) + (type (func (param $x f32) (param $y f64))) + (type (func (result i64) (result f32))) + (type (func (param i32) (param i64) (result f32) (result f64))) + (type (func (param $x i32) (param $y i64) (result f32) (result f64))) + + (type (func (param f32 f64) (param $x i32) (param f64 i32 i32))) + (type (func (result i64 i64 f32) (result f32 i32))) + (type + (func (param i32 i32) (param i64 i32) (result f32 f64) (result f64 i32)) + ) + + (type (func (param) (param $x f32) (param) (param) (param f64 i32) (param))) + (type + (func (result) (result) (result i64 i64) (result) (result f32) (result)) + ) + (type + (func + (param i32 i32) (param i64 i32) (param) (param $x i32) (param) + (result) (result f32 f64) (result f64 i32) (result) + ) + ) +) + +(assert_malformed + (module quote "(type (func (result i32) (param i32)))") + "result before parameter" +) +(assert_malformed + (module quote "(type (func (result $x i32)))") + "unexpected token" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/mutable-global/globals.wast b/runtime/unc-vm/tests/wast/spec/proposals/mutable-global/globals.wast new file mode 100644 index 000000000..175af3679 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/mutable-global/globals.wast @@ -0,0 +1,164 @@ +;; Test globals + +(module + (global $a i32 (i32.const -2)) + (global (;1;) f32 (f32.const -3)) + (global (;2;) f64 (f64.const -4)) + (global $b i64 (i64.const -5)) + + (global $x (mut i32) (i32.const -12)) + (global (;5;) (mut f32) (f32.const -13)) + (global (;6;) (mut f64) (f64.const -14)) + (global $y (mut i64) (i64.const -15)) + + (func (export "get-a") (result i32) (get_global $a)) + (func (export "get-b") (result i64) (get_global $b)) + (func (export "get-x") (result i32) (get_global $x)) + (func (export "get-y") (result i64) (get_global $y)) + (func (export "set-x") (param i32) (set_global $x (get_local 0))) + (func (export "set-y") (param i64) (set_global $y (get_local 0))) + + (func (export "get-1") (result f32) (get_global 1)) + (func (export "get-2") (result f64) (get_global 2)) + (func (export "get-5") (result f32) (get_global 5)) + (func (export "get-6") (result f64) (get_global 6)) + (func (export "set-5") (param f32) (set_global 5 (get_local 0))) + (func (export "set-6") (param f64) (set_global 6 (get_local 0))) +) + +(assert_return (invoke "get-a") (i32.const -2)) +(assert_return (invoke "get-b") (i64.const -5)) +(assert_return (invoke "get-x") (i32.const -12)) +(assert_return (invoke "get-y") (i64.const -15)) + +(assert_return (invoke "get-1") (f32.const -3)) +(assert_return (invoke "get-2") (f64.const -4)) +(assert_return (invoke "get-5") (f32.const -13)) +(assert_return (invoke "get-6") (f64.const -14)) + +(assert_return (invoke "set-x" (i32.const 6))) +(assert_return (invoke "set-y" (i64.const 7))) +(assert_return (invoke "set-5" (f32.const 8))) +(assert_return (invoke "set-6" (f64.const 9))) + +(assert_return (invoke "get-x") (i32.const 6)) +(assert_return (invoke "get-y") (i64.const 7)) +(assert_return (invoke "get-5") (f32.const 8)) +(assert_return (invoke "get-6") (f64.const 9)) + +(assert_invalid + (module (global f32 (f32.const 0)) (func (set_global 0 (i32.const 1)))) + "global is immutable" +) + +;; mutable globals can be exported +(module (global (mut f32) (f32.const 0)) (export "a" (global 0))) +(module (global (export "a") (mut f32) (f32.const 0))) + +(assert_invalid + (module (global f32 (f32.neg (f32.const 0)))) + "constant expression required" +) + +(assert_invalid + (module (global f32 (get_local 0))) + "constant expression required" +) + +(assert_invalid + (module (global f32 (f32.neg (f32.const 1)))) + "constant expression required" +) + +(assert_invalid + (module (global i32 (i32.const 0) (nop))) + "constant expression required" +) + +(assert_invalid + (module (global i32 (nop))) + "constant expression required" +) + +(assert_invalid + (module (global i32 (f32.const 0))) + "type mismatch" +) + +(assert_invalid + (module (global i32 (i32.const 0) (i32.const 0))) + "type mismatch" +) + +(assert_invalid + (module (global i32 (;empty instruction sequence;))) + "type mismatch" +) + +(assert_invalid + (module (global i32 (get_global 0))) + "unknown global" +) + +(assert_invalid + (module (global i32 (get_global 1)) (global i32 (i32.const 0))) + "unknown global" +) + +(module + (import "spectest" "global_i32" (global i32)) +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\94\80\80\80\00" ;; import section + "\01" ;; length 1 + "\08\73\70\65\63\74\65\73\74" ;; "spectest" + "\0a\67\6c\6f\62\61\6c\5f\69\33\32" ;; "global_i32" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\02" ;; invalid mutability + ) + "invalid mutability" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\94\80\80\80\00" ;; import section + "\01" ;; length 1 + "\08\73\70\65\63\74\65\73\74" ;; "spectest" + "\0a\67\6c\6f\62\61\6c\5f\69\33\32" ;; "global_i32" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\ff" ;; invalid mutability + ) + "invalid mutability" +) + +(module + (global i32 (i32.const 0)) +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\86\80\80\80\00" ;; global section + "\01" ;; length 1 + "\7f" ;; i32 + "\02" ;; invalid mutability + "\41\00" ;; i32.const 0 + "\0b" ;; end + ) + "invalid mutability" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\86\80\80\80\00" ;; global section + "\01" ;; length 1 + "\7f" ;; i32 + "\ff" ;; invalid mutability + "\41\00" ;; i32.const 0 + "\0b" ;; end + ) + "invalid mutability" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/mutable-global/linking.wast b/runtime/unc-vm/tests/wast/spec/proposals/mutable-global/linking.wast new file mode 100644 index 000000000..e2fc8b8f2 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/mutable-global/linking.wast @@ -0,0 +1,354 @@ +;; Functions + +(module $Mf + (func (export "call") (result i32) (call $g)) + (func $g (result i32) (i32.const 2)) +) +(register "Mf" $Mf) + +(module $Nf + (func $f (import "Mf" "call") (result i32)) + (export "Mf.call" (func $f)) + (func (export "call Mf.call") (result i32) (call $f)) + (func (export "call") (result i32) (call $g)) + (func $g (result i32) (i32.const 3)) +) + +(assert_return (invoke $Mf "call") (i32.const 2)) +(assert_return (invoke $Nf "Mf.call") (i32.const 2)) +(assert_return (invoke $Nf "call") (i32.const 3)) +(assert_return (invoke $Nf "call Mf.call") (i32.const 2)) + +(module + (import "spectest" "print_i32" (func $f (param i32))) + (export "print" (func $f)) +) +(register "reexport_f") +(assert_unlinkable + (module (import "reexport_f" "print" (func (param i64)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "reexport_f" "print" (func (param i32) (result i32)))) + "incompatible import type" +) + + +;; Globals + +(module $Mg + (global $glob (export "glob") i32 (i32.const 42)) + (func (export "get") (result i32) (get_global $glob)) + + ;; export mutable globals + (global $mut_glob (export "mut_glob") (mut i32) (i32.const 142)) + (func (export "get_mut") (result i32) (get_global $mut_glob)) + (func (export "set_mut") (param i32) (set_global $mut_glob (get_local 0))) +) +(register "Mg" $Mg) + +(module $Ng + (global $x (import "Mg" "glob") i32) + (global $mut_glob (import "Mg" "mut_glob") (mut i32)) + (func $f (import "Mg" "get") (result i32)) + (func $get_mut (import "Mg" "get_mut") (result i32)) + (func $set_mut (import "Mg" "set_mut") (param i32)) + + (export "Mg.glob" (global $x)) + (export "Mg.get" (func $f)) + (global $glob (export "glob") i32 (i32.const 43)) + (func (export "get") (result i32) (get_global $glob)) + + (export "Mg.mut_glob" (global $mut_glob)) + (export "Mg.get_mut" (func $get_mut)) + (export "Mg.set_mut" (func $set_mut)) +) + +(assert_return (get $Mg "glob") (i32.const 42)) +(assert_return (get $Ng "Mg.glob") (i32.const 42)) +(assert_return (get $Ng "glob") (i32.const 43)) +(assert_return (invoke $Mg "get") (i32.const 42)) +(assert_return (invoke $Ng "Mg.get") (i32.const 42)) +(assert_return (invoke $Ng "get") (i32.const 43)) + +(assert_return (get $Mg "mut_glob") (i32.const 142)) +(assert_return (get $Ng "Mg.mut_glob") (i32.const 142)) +(assert_return (invoke $Mg "get_mut") (i32.const 142)) +(assert_return (invoke $Ng "Mg.get_mut") (i32.const 142)) + +(assert_return (invoke $Mg "set_mut" (i32.const 241))) +(assert_return (get $Mg "mut_glob") (i32.const 241)) +(assert_return (get $Ng "Mg.mut_glob") (i32.const 241)) +(assert_return (invoke $Mg "get_mut") (i32.const 241)) +(assert_return (invoke $Ng "Mg.get_mut") (i32.const 241)) + + +(assert_unlinkable + (module (import "Mg" "mut_glob" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "Mg" "glob" (global (mut i32)))) + "incompatible import type" +) + +;; Tables + +(module $Mt + (type (func (result i32))) + (type (func)) + + (table (export "tab") 10 anyfunc) + (elem (i32.const 2) $g $g $g $g) + (func $g (result i32) (i32.const 4)) + (func (export "h") (result i32) (i32.const -4)) + + (func (export "call") (param i32) (result i32) + (call_indirect (type 0) (get_local 0)) + ) +) +(register "Mt" $Mt) + +(module $Nt + (type (func)) + (type (func (result i32))) + + (func $f (import "Mt" "call") (param i32) (result i32)) + (func $h (import "Mt" "h") (result i32)) + + (table anyfunc (elem $g $g $g $h $f)) + (func $g (result i32) (i32.const 5)) + + (export "Mt.call" (func $f)) + (func (export "call Mt.call") (param i32) (result i32) + (call $f (get_local 0)) + ) + (func (export "call") (param i32) (result i32) + (call_indirect (type 1) (get_local 0)) + ) +) + +(assert_return (invoke $Mt "call" (i32.const 2)) (i32.const 4)) +(assert_return (invoke $Nt "Mt.call" (i32.const 2)) (i32.const 4)) +(assert_return (invoke $Nt "call" (i32.const 2)) (i32.const 5)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 2)) (i32.const 4)) + +(assert_trap (invoke $Mt "call" (i32.const 1)) "uninitialized") +(assert_trap (invoke $Nt "Mt.call" (i32.const 1)) "uninitialized") +(assert_return (invoke $Nt "call" (i32.const 1)) (i32.const 5)) +(assert_trap (invoke $Nt "call Mt.call" (i32.const 1)) "uninitialized") + +(assert_trap (invoke $Mt "call" (i32.const 0)) "uninitialized") +(assert_trap (invoke $Nt "Mt.call" (i32.const 0)) "uninitialized") +(assert_return (invoke $Nt "call" (i32.const 0)) (i32.const 5)) +(assert_trap (invoke $Nt "call Mt.call" (i32.const 0)) "uninitialized") + +(assert_trap (invoke $Mt "call" (i32.const 20)) "undefined") +(assert_trap (invoke $Nt "Mt.call" (i32.const 20)) "undefined") +(assert_trap (invoke $Nt "call" (i32.const 7)) "undefined") +(assert_trap (invoke $Nt "call Mt.call" (i32.const 20)) "undefined") + +(assert_return (invoke $Nt "call" (i32.const 3)) (i32.const -4)) +(assert_trap (invoke $Nt "call" (i32.const 4)) "indirect call") + +(module $Ot + (type (func (result i32))) + + (func $h (import "Mt" "h") (result i32)) + (table (import "Mt" "tab") 5 anyfunc) + (elem (i32.const 1) $i $h) + (func $i (result i32) (i32.const 6)) + + (func (export "call") (param i32) (result i32) + (call_indirect (type 0) (get_local 0)) + ) +) + +(assert_return (invoke $Mt "call" (i32.const 3)) (i32.const 4)) +(assert_return (invoke $Nt "Mt.call" (i32.const 3)) (i32.const 4)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 3)) (i32.const 4)) +(assert_return (invoke $Ot "call" (i32.const 3)) (i32.const 4)) + +(assert_return (invoke $Mt "call" (i32.const 2)) (i32.const -4)) +(assert_return (invoke $Nt "Mt.call" (i32.const 2)) (i32.const -4)) +(assert_return (invoke $Nt "call" (i32.const 2)) (i32.const 5)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 2)) (i32.const -4)) +(assert_return (invoke $Ot "call" (i32.const 2)) (i32.const -4)) + +(assert_return (invoke $Mt "call" (i32.const 1)) (i32.const 6)) +(assert_return (invoke $Nt "Mt.call" (i32.const 1)) (i32.const 6)) +(assert_return (invoke $Nt "call" (i32.const 1)) (i32.const 5)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 1)) (i32.const 6)) +(assert_return (invoke $Ot "call" (i32.const 1)) (i32.const 6)) + +(assert_trap (invoke $Mt "call" (i32.const 0)) "uninitialized") +(assert_trap (invoke $Nt "Mt.call" (i32.const 0)) "uninitialized") +(assert_return (invoke $Nt "call" (i32.const 0)) (i32.const 5)) +(assert_trap (invoke $Nt "call Mt.call" (i32.const 0)) "uninitialized") +(assert_trap (invoke $Ot "call" (i32.const 0)) "uninitialized") + +(assert_trap (invoke $Ot "call" (i32.const 20)) "undefined") + +(module + (table (import "Mt" "tab") 0 anyfunc) + (elem (i32.const 9) $f) + (func $f) +) + +(module $G1 (global (export "g") i32 (i32.const 5))) +(register "G1" $G1) +(module $G2 + (global (import "G1" "g") i32) + (global (export "g") i32 (get_global 0)) +) +(assert_return (get $G2 "g") (i32.const 5)) + +(assert_unlinkable + (module + (table (import "Mt" "tab") 0 anyfunc) + (elem (i32.const 10) $f) + (func $f) + ) + "elements segment does not fit" +) + +(assert_unlinkable + (module + (table (import "Mt" "tab") 10 anyfunc) + (memory (import "Mt" "mem") 1) ;; does not exist + (func $f (result i32) (i32.const 0)) + (elem (i32.const 7) $f) + (elem (i32.const 9) $f) + ) + "unknown import" +) +(assert_trap (invoke $Mt "call" (i32.const 7)) "uninitialized") + +(assert_unlinkable + (module + (table (import "Mt" "tab") 10 anyfunc) + (func $f (result i32) (i32.const 0)) + (elem (i32.const 7) $f) + (elem (i32.const 12) $f) ;; out of bounds + ) + "elements segment does not fit" +) +(assert_trap (invoke $Mt "call" (i32.const 7)) "uninitialized") + +(assert_unlinkable + (module + (table (import "Mt" "tab") 10 anyfunc) + (func $f (result i32) (i32.const 0)) + (elem (i32.const 7) $f) + (memory 1) + (data (i32.const 0x10000) "d") ;; out of bounds + ) + "data segment does not fit" +) +(assert_trap (invoke $Mt "call" (i32.const 7)) "uninitialized") + + +;; Memories + +(module $Mm + (memory (export "mem") 1 5) + (data (i32.const 10) "\00\01\02\03\04\05\06\07\08\09") + + (func (export "load") (param $a i32) (result i32) + (i32.load8_u (get_local 0)) + ) +) +(register "Mm" $Mm) + +(module $Nm + (func $loadM (import "Mm" "load") (param i32) (result i32)) + + (memory 1) + (data (i32.const 10) "\f0\f1\f2\f3\f4\f5") + + (export "Mm.load" (func $loadM)) + (func (export "load") (param $a i32) (result i32) + (i32.load8_u (get_local 0)) + ) +) + +(assert_return (invoke $Mm "load" (i32.const 12)) (i32.const 2)) +(assert_return (invoke $Nm "Mm.load" (i32.const 12)) (i32.const 2)) +(assert_return (invoke $Nm "load" (i32.const 12)) (i32.const 0xf2)) + +(module $Om + (memory (import "Mm" "mem") 1) + (data (i32.const 5) "\a0\a1\a2\a3\a4\a5\a6\a7") + + (func (export "load") (param $a i32) (result i32) + (i32.load8_u (get_local 0)) + ) +) + +(assert_return (invoke $Mm "load" (i32.const 12)) (i32.const 0xa7)) +(assert_return (invoke $Nm "Mm.load" (i32.const 12)) (i32.const 0xa7)) +(assert_return (invoke $Nm "load" (i32.const 12)) (i32.const 0xf2)) +(assert_return (invoke $Om "load" (i32.const 12)) (i32.const 0xa7)) + +(module + (memory (import "Mm" "mem") 0) + (data (i32.const 0xffff) "a") +) + +(assert_unlinkable + (module + (memory (import "Mm" "mem") 0) + (data (i32.const 0x10000) "a") + ) + "data segment does not fit" +) + +(module $Pm + (memory (import "Mm" "mem") 1 8) + + (func (export "grow") (param $a i32) (result i32) + (memory.grow (get_local 0)) + ) +) + +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 1)) +(assert_return (invoke $Pm "grow" (i32.const 2)) (i32.const 1)) +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 3)) +(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const 3)) +(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const 4)) +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5)) +(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const -1)) +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5)) + +(assert_unlinkable + (module + (func $host (import "spectest" "print")) + (memory (import "Mm" "mem") 1) + (table (import "Mm" "tab") 0 anyfunc) ;; does not exist + (data (i32.const 0) "abc") + ) + "unknown import" +) +(assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 0)) + +(assert_unlinkable + (module + (memory (import "Mm" "mem") 1) + (data (i32.const 0) "abc") + (data (i32.const 0x50000) "d") ;; out of bounds + ) + "data segment does not fit" +) +(assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 0)) + +(assert_unlinkable + (module + (memory (import "Mm" "mem") 1) + (data (i32.const 0) "abc") + (table 0 anyfunc) + (func) + (elem (i32.const 0) 0) ;; out of bounds + ) + "elements segment does not fit" +) +(assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 0)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/nontrapping-float-to-int-conversions/conversions.wast b/runtime/unc-vm/tests/wast/spec/proposals/nontrapping-float-to-int-conversions/conversions.wast new file mode 100644 index 000000000..99355a085 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/nontrapping-float-to-int-conversions/conversions.wast @@ -0,0 +1,698 @@ +(module + (func (export "i64.extend_i32_s") (param $x i32) (result i64) (i64.extend_i32_s (local.get $x))) + (func (export "i64.extend_i32_u") (param $x i32) (result i64) (i64.extend_i32_u (local.get $x))) + (func (export "i32.wrap_i64") (param $x i64) (result i32) (i32.wrap_i64 (local.get $x))) + (func (export "i32.trunc_f32_s") (param $x f32) (result i32) (i32.trunc_f32_s (local.get $x))) + (func (export "i32.trunc_f32_u") (param $x f32) (result i32) (i32.trunc_f32_u (local.get $x))) + (func (export "i32.trunc_f64_s") (param $x f64) (result i32) (i32.trunc_f64_s (local.get $x))) + (func (export "i32.trunc_f64_u") (param $x f64) (result i32) (i32.trunc_f64_u (local.get $x))) + (func (export "i64.trunc_f32_s") (param $x f32) (result i64) (i64.trunc_f32_s (local.get $x))) + (func (export "i64.trunc_f32_u") (param $x f32) (result i64) (i64.trunc_f32_u (local.get $x))) + (func (export "i64.trunc_f64_s") (param $x f64) (result i64) (i64.trunc_f64_s (local.get $x))) + (func (export "i64.trunc_f64_u") (param $x f64) (result i64) (i64.trunc_f64_u (local.get $x))) + (func (export "i32.trunc_sat_f32_s") (param $x f32) (result i32) (i32.trunc_sat_f32_s (local.get $x))) + (func (export "i32.trunc_sat_f32_u") (param $x f32) (result i32) (i32.trunc_sat_f32_u (local.get $x))) + (func (export "i32.trunc_sat_f64_s") (param $x f64) (result i32) (i32.trunc_sat_f64_s (local.get $x))) + (func (export "i32.trunc_sat_f64_u") (param $x f64) (result i32) (i32.trunc_sat_f64_u (local.get $x))) + (func (export "i64.trunc_sat_f32_s") (param $x f32) (result i64) (i64.trunc_sat_f32_s (local.get $x))) + (func (export "i64.trunc_sat_f32_u") (param $x f32) (result i64) (i64.trunc_sat_f32_u (local.get $x))) + (func (export "i64.trunc_sat_f64_s") (param $x f64) (result i64) (i64.trunc_sat_f64_s (local.get $x))) + (func (export "i64.trunc_sat_f64_u") (param $x f64) (result i64) (i64.trunc_sat_f64_u (local.get $x))) + (func (export "f32.convert_i32_s") (param $x i32) (result f32) (f32.convert_i32_s (local.get $x))) + (func (export "f32.convert_i64_s") (param $x i64) (result f32) (f32.convert_i64_s (local.get $x))) + (func (export "f64.convert_i32_s") (param $x i32) (result f64) (f64.convert_i32_s (local.get $x))) + (func (export "f64.convert_i64_s") (param $x i64) (result f64) (f64.convert_i64_s (local.get $x))) + (func (export "f32.convert_i32_u") (param $x i32) (result f32) (f32.convert_i32_u (local.get $x))) + (func (export "f32.convert_i64_u") (param $x i64) (result f32) (f32.convert_i64_u (local.get $x))) + (func (export "f64.convert_i32_u") (param $x i32) (result f64) (f64.convert_i32_u (local.get $x))) + (func (export "f64.convert_i64_u") (param $x i64) (result f64) (f64.convert_i64_u (local.get $x))) + (func (export "f64.promote_f32") (param $x f32) (result f64) (f64.promote_f32 (local.get $x))) + (func (export "f32.demote_f64") (param $x f64) (result f32) (f32.demote_f64 (local.get $x))) + (func (export "f32.reinterpret_i32") (param $x i32) (result f32) (f32.reinterpret_i32 (local.get $x))) + (func (export "f64.reinterpret_i64") (param $x i64) (result f64) (f64.reinterpret_i64 (local.get $x))) + (func (export "i32.reinterpret_f32") (param $x f32) (result i32) (i32.reinterpret_f32 (local.get $x))) + (func (export "i64.reinterpret_f64") (param $x f64) (result i64) (i64.reinterpret_f64 (local.get $x))) +) + +(assert_return (invoke "i64.extend_i32_s" (i32.const 0)) (i64.const 0)) +(assert_return (invoke "i64.extend_i32_s" (i32.const 10000)) (i64.const 10000)) +(assert_return (invoke "i64.extend_i32_s" (i32.const -10000)) (i64.const -10000)) +(assert_return (invoke "i64.extend_i32_s" (i32.const -1)) (i64.const -1)) +(assert_return (invoke "i64.extend_i32_s" (i32.const 0x7fffffff)) (i64.const 0x000000007fffffff)) +(assert_return (invoke "i64.extend_i32_s" (i32.const 0x80000000)) (i64.const 0xffffffff80000000)) + +(assert_return (invoke "i64.extend_i32_u" (i32.const 0)) (i64.const 0)) +(assert_return (invoke "i64.extend_i32_u" (i32.const 10000)) (i64.const 10000)) +(assert_return (invoke "i64.extend_i32_u" (i32.const -10000)) (i64.const 0x00000000ffffd8f0)) +(assert_return (invoke "i64.extend_i32_u" (i32.const -1)) (i64.const 0xffffffff)) +(assert_return (invoke "i64.extend_i32_u" (i32.const 0x7fffffff)) (i64.const 0x000000007fffffff)) +(assert_return (invoke "i64.extend_i32_u" (i32.const 0x80000000)) (i64.const 0x0000000080000000)) + +(assert_return (invoke "i32.wrap_i64" (i64.const -1)) (i32.const -1)) +(assert_return (invoke "i32.wrap_i64" (i64.const -100000)) (i32.const -100000)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0x80000000)) (i32.const 0x80000000)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0xffffffff7fffffff)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0xffffffff00000000)) (i32.const 0x00000000)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0xfffffffeffffffff)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0xffffffff00000001)) (i32.const 0x00000001)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0)) (i32.const 0)) +(assert_return (invoke "i32.wrap_i64" (i64.const 1311768467463790320)) (i32.const 0x9abcdef0)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0x00000000ffffffff)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0x0000000100000000)) (i32.const 0x00000000)) +(assert_return (invoke "i32.wrap_i64" (i64.const 0x0000000100000001)) (i32.const 0x00000001)) + +(assert_return (invoke "i32.trunc_f32_s" (f32.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const 0x1.19999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -1.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -0x1.19999ap+0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -1.5)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -1.9)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -2.0)) (i32.const -2)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const 2147483520.0)) (i32.const 2147483520)) +(assert_return (invoke "i32.trunc_f32_s" (f32.const -2147483648.0)) (i32.const -2147483648)) +(assert_trap (invoke "i32.trunc_f32_s" (f32.const 2147483648.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_s" (f32.const -2147483904.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_s" (f32.const inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_s" (f32.const -inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_s" (f32.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f32_s" (f32.const nan:0x200000)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f32_s" (f32.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f32_s" (f32.const -nan:0x200000)) "invalid conversion to integer") + +(assert_return (invoke "i32.trunc_f32_u" (f32.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const 0x1.19999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const 1.9)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const 2.0)) (i32.const 2)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const 2147483648)) (i32.const -2147483648)) ;; 0x1.00000p+31 -> 8000 0000 +(assert_return (invoke "i32.trunc_f32_u" (f32.const 4294967040.0)) (i32.const -256)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const -0x1.ccccccp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f32_u" (f32.const -0x1.fffffep-1)) (i32.const 0)) +(assert_trap (invoke "i32.trunc_f32_u" (f32.const 4294967296.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_u" (f32.const -1.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_u" (f32.const inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_u" (f32.const -inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f32_u" (f32.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f32_u" (f32.const nan:0x200000)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f32_u" (f32.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f32_u" (f32.const -nan:0x200000)) "invalid conversion to integer") + +(assert_return (invoke "i32.trunc_f64_s" (f64.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const 0x1.199999999999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -1.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -0x1.199999999999ap+0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -1.5)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -1.9)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -2.0)) (i32.const -2)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const 2147483647.0)) (i32.const 2147483647)) +(assert_return (invoke "i32.trunc_f64_s" (f64.const -2147483648.0)) (i32.const -2147483648)) +(assert_trap (invoke "i32.trunc_f64_s" (f64.const 2147483648.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_s" (f64.const -2147483649.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_s" (f64.const inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_s" (f64.const -inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_s" (f64.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f64_s" (f64.const nan:0x4000000000000)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f64_s" (f64.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f64_s" (f64.const -nan:0x4000000000000)) "invalid conversion to integer") + +(assert_return (invoke "i32.trunc_f64_u" (f64.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 0x1.199999999999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 1.9)) (i32.const 1)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 2.0)) (i32.const 2)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 2147483648)) (i32.const -2147483648)) ;; 0x1.00000p+31 -> 8000 0000 +(assert_return (invoke "i32.trunc_f64_u" (f64.const 4294967295.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const -0x1.ccccccccccccdp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const -0x1.fffffffffffffp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_f64_u" (f64.const 1e8)) (i32.const 100000000)) +(assert_trap (invoke "i32.trunc_f64_u" (f64.const 4294967296.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const -1.0)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const 1e16)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const 1e30)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const 9223372036854775808)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const -inf)) "integer overflow") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const nan:0x4000000000000)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i32.trunc_f64_u" (f64.const -nan:0x4000000000000)) "invalid conversion to integer") + +(assert_return (invoke "i64.trunc_f32_s" (f32.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const 0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const 0x1.19999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -1.0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -0x1.19999ap+0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -1.5)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -1.9)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -2.0)) (i64.const -2)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const 4294967296)) (i64.const 4294967296)) ;; 0x1.00000p+32 -> 1 0000 0000 +(assert_return (invoke "i64.trunc_f32_s" (f32.const -4294967296)) (i64.const -4294967296)) ;; -0x1.00000p+32 -> ffff ffff 0000 0000 +(assert_return (invoke "i64.trunc_f32_s" (f32.const 9223371487098961920.0)) (i64.const 9223371487098961920)) +(assert_return (invoke "i64.trunc_f32_s" (f32.const -9223372036854775808.0)) (i64.const -9223372036854775808)) +(assert_trap (invoke "i64.trunc_f32_s" (f32.const 9223372036854775808.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_s" (f32.const -9223373136366403584.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_s" (f32.const inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_s" (f32.const -inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_s" (f32.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f32_s" (f32.const nan:0x200000)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f32_s" (f32.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f32_s" (f32.const -nan:0x200000)) "invalid conversion to integer") + +(assert_return (invoke "i64.trunc_f32_u" (f32.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const 0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const -0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const 0x1.19999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const 4294967296)) (i64.const 4294967296)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const 18446742974197923840.0)) (i64.const -1099511627776)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const -0x1.ccccccp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f32_u" (f32.const -0x1.fffffep-1)) (i64.const 0)) +(assert_trap (invoke "i64.trunc_f32_u" (f32.const 18446744073709551616.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_u" (f32.const -1.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_u" (f32.const inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_u" (f32.const -inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f32_u" (f32.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f32_u" (f32.const nan:0x200000)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f32_u" (f32.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f32_u" (f32.const -nan:0x200000)) "invalid conversion to integer") + +(assert_return (invoke "i64.trunc_f64_s" (f64.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const 0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const 0x1.199999999999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -1.0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -0x1.199999999999ap+0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -1.5)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -1.9)) (i64.const -1)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -2.0)) (i64.const -2)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const 4294967296)) (i64.const 4294967296)) ;; 0x1.00000p+32 -> 1 0000 0000 +(assert_return (invoke "i64.trunc_f64_s" (f64.const -4294967296)) (i64.const -4294967296)) ;; -0x1.00000p+32 -> ffff ffff 0000 0000 +(assert_return (invoke "i64.trunc_f64_s" (f64.const 9223372036854774784.0)) (i64.const 9223372036854774784)) +(assert_return (invoke "i64.trunc_f64_s" (f64.const -9223372036854775808.0)) (i64.const -9223372036854775808)) +(assert_trap (invoke "i64.trunc_f64_s" (f64.const 9223372036854775808.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_s" (f64.const -9223372036854777856.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_s" (f64.const inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_s" (f64.const -inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_s" (f64.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f64_s" (f64.const nan:0x4000000000000)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f64_s" (f64.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f64_s" (f64.const -nan:0x4000000000000)) "invalid conversion to integer") + +(assert_return (invoke "i64.trunc_f64_u" (f64.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const -0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 0x1.199999999999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 4294967295)) (i64.const 0xffffffff)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 4294967296)) (i64.const 0x100000000)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 18446744073709549568.0)) (i64.const -2048)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const -0x1.ccccccccccccdp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const -0x1.fffffffffffffp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 1e8)) (i64.const 100000000)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 1e16)) (i64.const 10000000000000000)) +(assert_return (invoke "i64.trunc_f64_u" (f64.const 9223372036854775808)) (i64.const -9223372036854775808)) +(assert_trap (invoke "i64.trunc_f64_u" (f64.const 18446744073709551616.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_u" (f64.const -1.0)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_u" (f64.const inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_u" (f64.const -inf)) "integer overflow") +(assert_trap (invoke "i64.trunc_f64_u" (f64.const nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f64_u" (f64.const nan:0x4000000000000)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f64_u" (f64.const -nan)) "invalid conversion to integer") +(assert_trap (invoke "i64.trunc_f64_u" (f64.const -nan:0x4000000000000)) "invalid conversion to integer") + +(assert_return (invoke "f32.convert_i32_s" (i32.const 1)) (f32.const 1.0)) +(assert_return (invoke "f32.convert_i32_s" (i32.const -1)) (f32.const -1.0)) +(assert_return (invoke "f32.convert_i32_s" (i32.const 0)) (f32.const 0.0)) +(assert_return (invoke "f32.convert_i32_s" (i32.const 2147483647)) (f32.const 2147483648)) +(assert_return (invoke "f32.convert_i32_s" (i32.const -2147483648)) (f32.const -2147483648)) +(assert_return (invoke "f32.convert_i32_s" (i32.const 1234567890)) (f32.const 0x1.26580cp+30)) + +;; Saturating conversions: test all the same values as the non-saturating conversions. + +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 0x1.19999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -1.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -0x1.19999ap+0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -1.5)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -1.9)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -2.0)) (i32.const -2)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 2147483520.0)) (i32.const 2147483520)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -2147483648.0)) (i32.const -2147483648)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const 2147483648.0)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -2147483904.0)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const inf)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -inf)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_s" (f32.const -nan:0x200000)) (i32.const 0)) + +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 0x1.19999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 1.9)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 2.0)) (i32.const 2)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 2147483648)) (i32.const -2147483648)) ;; 0x1.00000p+31 -> 8000 0000 +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 4294967040.0)) (i32.const -256)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -0x1.ccccccp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -0x1.fffffep-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const 4294967296.0)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -1.0)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const inf)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -inf)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f32_u" (f32.const -nan:0x200000)) (i32.const 0)) + +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 0x1.199999999999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -1.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -0x1.199999999999ap+0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -1.5)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -1.9)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -2.0)) (i32.const -2)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 2147483647.0)) (i32.const 2147483647)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -2147483648.0)) (i32.const -2147483648)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const 2147483648.0)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -2147483649.0)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const inf)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -inf)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_s" (f64.const -nan:0x4000000000000)) (i32.const 0)) + +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 0x1.199999999999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1.9)) (i32.const 1)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 2.0)) (i32.const 2)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 2147483648)) (i32.const -2147483648)) ;; 0x1.00000p+31 -> 8000 0000 +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 4294967295.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -0x1.ccccccccccccdp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -0x1.fffffffffffffp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1e8)) (i32.const 100000000)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 4294967296.0)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -1.0)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1e16)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 1e30)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const 9223372036854775808)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const inf)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -inf)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_sat_f64_u" (f64.const -nan:0x4000000000000)) (i32.const 0)) + +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 0x1.19999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -1.0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -0x1.19999ap+0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -1.5)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -1.9)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -2.0)) (i64.const -2)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 4294967296)) (i64.const 4294967296)) ;; 0x1.00000p+32 -> 1 0000 0000 +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -4294967296)) (i64.const -4294967296)) ;; -0x1.00000p+32 -> ffff ffff 0000 0000 +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 9223371487098961920.0)) (i64.const 9223371487098961920)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -9223372036854775808.0)) (i64.const -9223372036854775808)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const 9223372036854775808.0)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -9223373136366403584.0)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const inf)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -inf)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const nan:0x200000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_s" (f32.const -nan:0x200000)) (i64.const 0)) + +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 0x1.19999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 4294967296)) (i64.const 4294967296)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 18446742974197923840.0)) (i64.const -1099511627776)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -0x1.ccccccp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -0x1.fffffep-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const 18446744073709551616.0)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -1.0)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const inf)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -inf)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const nan:0x200000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f32_u" (f32.const -nan:0x200000)) (i64.const 0)) + +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 0x1.199999999999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -1.0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -0x1.199999999999ap+0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -1.5)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -1.9)) (i64.const -1)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -2.0)) (i64.const -2)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 4294967296)) (i64.const 4294967296)) ;; 0x1.00000p+32 -> 1 0000 0000 +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -4294967296)) (i64.const -4294967296)) ;; -0x1.00000p+32 -> ffff ffff 0000 0000 +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 9223372036854774784.0)) (i64.const 9223372036854774784)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -9223372036854775808.0)) (i64.const -9223372036854775808)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const 9223372036854775808.0)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -9223372036854777856.0)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const inf)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -inf)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const nan:0x4000000000000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_s" (f64.const -nan:0x4000000000000)) (i64.const 0)) + +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 0x1.199999999999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 4294967295)) (i64.const 0xffffffff)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 4294967296)) (i64.const 0x100000000)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 18446744073709549568.0)) (i64.const -2048)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -0x1.ccccccccccccdp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -0x1.fffffffffffffp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 1e8)) (i64.const 100000000)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 1e16)) (i64.const 10000000000000000)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 9223372036854775808)) (i64.const -9223372036854775808)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const 18446744073709551616.0)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -1.0)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const inf)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -inf)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const nan:0x4000000000000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_sat_f64_u" (f64.const -nan:0x4000000000000)) (i64.const 0)) + +;; Test rounding directions. +(assert_return (invoke "f32.convert_i32_s" (i32.const 16777217)) (f32.const 16777216.0)) +(assert_return (invoke "f32.convert_i32_s" (i32.const -16777217)) (f32.const -16777216.0)) +(assert_return (invoke "f32.convert_i32_s" (i32.const 16777219)) (f32.const 16777220.0)) +(assert_return (invoke "f32.convert_i32_s" (i32.const -16777219)) (f32.const -16777220.0)) + +(assert_return (invoke "f32.convert_i64_s" (i64.const 1)) (f32.const 1.0)) +(assert_return (invoke "f32.convert_i64_s" (i64.const -1)) (f32.const -1.0)) +(assert_return (invoke "f32.convert_i64_s" (i64.const 0)) (f32.const 0.0)) +(assert_return (invoke "f32.convert_i64_s" (i64.const 9223372036854775807)) (f32.const 9223372036854775807)) +(assert_return (invoke "f32.convert_i64_s" (i64.const -9223372036854775808)) (f32.const -9223372036854775808)) +(assert_return (invoke "f32.convert_i64_s" (i64.const 314159265358979)) (f32.const 0x1.1db9e8p+48)) ;; PI +;; Test rounding directions. +(assert_return (invoke "f32.convert_i64_s" (i64.const 16777217)) (f32.const 16777216.0)) +(assert_return (invoke "f32.convert_i64_s" (i64.const -16777217)) (f32.const -16777216.0)) +(assert_return (invoke "f32.convert_i64_s" (i64.const 16777219)) (f32.const 16777220.0)) +(assert_return (invoke "f32.convert_i64_s" (i64.const -16777219)) (f32.const -16777220.0)) + +(assert_return (invoke "f32.convert_i64_s" (i64.const 0x7fffff4000000001)) (f32.const 0x1.fffffep+62)) +(assert_return (invoke "f32.convert_i64_s" (i64.const 0x8000004000000001)) (f32.const -0x1.fffffep+62)) +(assert_return (invoke "f32.convert_i64_s" (i64.const 0x0020000020000001)) (f32.const 0x1.000002p+53)) +(assert_return (invoke "f32.convert_i64_s" (i64.const 0xffdfffffdfffffff)) (f32.const -0x1.000002p+53)) + +(assert_return (invoke "f64.convert_i32_s" (i32.const 1)) (f64.const 1.0)) +(assert_return (invoke "f64.convert_i32_s" (i32.const -1)) (f64.const -1.0)) +(assert_return (invoke "f64.convert_i32_s" (i32.const 0)) (f64.const 0.0)) +(assert_return (invoke "f64.convert_i32_s" (i32.const 2147483647)) (f64.const 2147483647)) +(assert_return (invoke "f64.convert_i32_s" (i32.const -2147483648)) (f64.const -2147483648)) +(assert_return (invoke "f64.convert_i32_s" (i32.const 987654321)) (f64.const 987654321)) + +(assert_return (invoke "f64.convert_i64_s" (i64.const 1)) (f64.const 1.0)) +(assert_return (invoke "f64.convert_i64_s" (i64.const -1)) (f64.const -1.0)) +(assert_return (invoke "f64.convert_i64_s" (i64.const 0)) (f64.const 0.0)) +(assert_return (invoke "f64.convert_i64_s" (i64.const 9223372036854775807)) (f64.const 9223372036854775807)) +(assert_return (invoke "f64.convert_i64_s" (i64.const -9223372036854775808)) (f64.const -9223372036854775808)) +(assert_return (invoke "f64.convert_i64_s" (i64.const 4669201609102990)) (f64.const 4669201609102990)) ;; Feigenbaum +;; Test rounding directions. +(assert_return (invoke "f64.convert_i64_s" (i64.const 9007199254740993)) (f64.const 9007199254740992)) +(assert_return (invoke "f64.convert_i64_s" (i64.const -9007199254740993)) (f64.const -9007199254740992)) +(assert_return (invoke "f64.convert_i64_s" (i64.const 9007199254740995)) (f64.const 9007199254740996)) +(assert_return (invoke "f64.convert_i64_s" (i64.const -9007199254740995)) (f64.const -9007199254740996)) + +(assert_return (invoke "f32.convert_i32_u" (i32.const 1)) (f32.const 1.0)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0)) (f32.const 0.0)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 2147483647)) (f32.const 2147483648)) +(assert_return (invoke "f32.convert_i32_u" (i32.const -2147483648)) (f32.const 2147483648)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0x12345678)) (f32.const 0x1.234568p+28)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0xffffffff)) (f32.const 4294967296.0)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0x80000080)) (f32.const 0x1.000000p+31)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0x80000081)) (f32.const 0x1.000002p+31)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0x80000082)) (f32.const 0x1.000002p+31)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0xfffffe80)) (f32.const 0x1.fffffcp+31)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0xfffffe81)) (f32.const 0x1.fffffep+31)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 0xfffffe82)) (f32.const 0x1.fffffep+31)) +;; Test rounding directions. +(assert_return (invoke "f32.convert_i32_u" (i32.const 16777217)) (f32.const 16777216.0)) +(assert_return (invoke "f32.convert_i32_u" (i32.const 16777219)) (f32.const 16777220.0)) + +(assert_return (invoke "f32.convert_i64_u" (i64.const 1)) (f32.const 1.0)) +(assert_return (invoke "f32.convert_i64_u" (i64.const 0)) (f32.const 0.0)) +(assert_return (invoke "f32.convert_i64_u" (i64.const 9223372036854775807)) (f32.const 9223372036854775807)) +(assert_return (invoke "f32.convert_i64_u" (i64.const -9223372036854775808)) (f32.const 9223372036854775808)) +(assert_return (invoke "f32.convert_i64_u" (i64.const 0xffffffffffffffff)) (f32.const 18446744073709551616.0)) +;; Test rounding directions. +(assert_return (invoke "f32.convert_i64_u" (i64.const 16777217)) (f32.const 16777216.0)) +(assert_return (invoke "f32.convert_i64_u" (i64.const 16777219)) (f32.const 16777220.0)) + +(assert_return (invoke "f32.convert_i64_u" (i64.const 0x0020000020000001)) (f32.const 0x1.000002p+53)) +(assert_return (invoke "f32.convert_i64_u" (i64.const 0x7fffffbfffffffff)) (f32.const 0x1.fffffep+62)) +(assert_return (invoke "f32.convert_i64_u" (i64.const 0x8000008000000001)) (f32.const 0x1.000002p+63)) +(assert_return (invoke "f32.convert_i64_u" (i64.const 0xfffffe8000000001)) (f32.const 0x1.fffffep+63)) + +(assert_return (invoke "f64.convert_i32_u" (i32.const 1)) (f64.const 1.0)) +(assert_return (invoke "f64.convert_i32_u" (i32.const 0)) (f64.const 0.0)) +(assert_return (invoke "f64.convert_i32_u" (i32.const 2147483647)) (f64.const 2147483647)) +(assert_return (invoke "f64.convert_i32_u" (i32.const -2147483648)) (f64.const 2147483648)) +(assert_return (invoke "f64.convert_i32_u" (i32.const 0xffffffff)) (f64.const 4294967295.0)) + +(assert_return (invoke "f64.convert_i64_u" (i64.const 1)) (f64.const 1.0)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0)) (f64.const 0.0)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 9223372036854775807)) (f64.const 9223372036854775807)) +(assert_return (invoke "f64.convert_i64_u" (i64.const -9223372036854775808)) (f64.const 9223372036854775808)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0xffffffffffffffff)) (f64.const 18446744073709551616.0)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0x8000000000000400)) (f64.const 0x1.0000000000000p+63)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0x8000000000000401)) (f64.const 0x1.0000000000001p+63)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0x8000000000000402)) (f64.const 0x1.0000000000001p+63)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0xfffffffffffff400)) (f64.const 0x1.ffffffffffffep+63)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0xfffffffffffff401)) (f64.const 0x1.fffffffffffffp+63)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 0xfffffffffffff402)) (f64.const 0x1.fffffffffffffp+63)) +;; Test rounding directions. +(assert_return (invoke "f64.convert_i64_u" (i64.const 9007199254740993)) (f64.const 9007199254740992)) +(assert_return (invoke "f64.convert_i64_u" (i64.const 9007199254740995)) (f64.const 9007199254740996)) + +(assert_return (invoke "f64.promote_f32" (f32.const 0.0)) (f64.const 0.0)) +(assert_return (invoke "f64.promote_f32" (f32.const -0.0)) (f64.const -0.0)) +(assert_return (invoke "f64.promote_f32" (f32.const 0x1p-149)) (f64.const 0x1p-149)) +(assert_return (invoke "f64.promote_f32" (f32.const -0x1p-149)) (f64.const -0x1p-149)) +(assert_return (invoke "f64.promote_f32" (f32.const 1.0)) (f64.const 1.0)) +(assert_return (invoke "f64.promote_f32" (f32.const -1.0)) (f64.const -1.0)) +(assert_return (invoke "f64.promote_f32" (f32.const -0x1.fffffep+127)) (f64.const -0x1.fffffep+127)) +(assert_return (invoke "f64.promote_f32" (f32.const 0x1.fffffep+127)) (f64.const 0x1.fffffep+127)) +;; Generated randomly by picking a random int and reinterpret it to float. +(assert_return (invoke "f64.promote_f32" (f32.const 0x1p-119)) (f64.const 0x1p-119)) +;; Generated randomly by picking a random float. +(assert_return (invoke "f64.promote_f32" (f32.const 0x1.8f867ep+125)) (f64.const 6.6382536710104395e+37)) +(assert_return (invoke "f64.promote_f32" (f32.const inf)) (f64.const inf)) +(assert_return (invoke "f64.promote_f32" (f32.const -inf)) (f64.const -inf)) +(assert_return (invoke "f64.promote_f32" (f32.const nan)) (f64.const nan:canonical)) +(assert_return (invoke "f64.promote_f32" (f32.const nan:0x200000)) (f64.const nan:arithmetic)) +(assert_return (invoke "f64.promote_f32" (f32.const -nan)) (f64.const nan:canonical)) +(assert_return (invoke "f64.promote_f32" (f32.const -nan:0x200000)) (f64.const nan:arithmetic)) + +(assert_return (invoke "f32.demote_f64" (f64.const 0.0)) (f32.const 0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const -0.0)) (f32.const -0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x0.0000000000001p-1022)) (f32.const 0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x0.0000000000001p-1022)) (f32.const -0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const 1.0)) (f32.const 1.0)) +(assert_return (invoke "f32.demote_f64" (f64.const -1.0)) (f32.const -1.0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffe0000000p-127)) (f32.const 0x1p-126)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffe0000000p-127)) (f32.const -0x1p-126)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffdfffffffp-127)) (f32.const 0x1.fffffcp-127)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffdfffffffp-127)) (f32.const -0x1.fffffcp-127)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1p-149)) (f32.const 0x1p-149)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1p-149)) (f32.const -0x1p-149)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffd0000000p+127)) (f32.const 0x1.fffffcp+127)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffd0000000p+127)) (f32.const -0x1.fffffcp+127)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffd0000001p+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffd0000001p+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffep+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffep+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffefffffffp+127)) (f32.const 0x1.fffffep+127)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.fffffefffffffp+127)) (f32.const -0x1.fffffep+127)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.ffffffp+127)) (f32.const inf)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.ffffffp+127)) (f32.const -inf)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1p-119)) (f32.const 0x1p-119)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.8f867ep+125)) (f32.const 0x1.8f867ep+125)) +(assert_return (invoke "f32.demote_f64" (f64.const inf)) (f32.const inf)) +(assert_return (invoke "f32.demote_f64" (f64.const -inf)) (f32.const -inf)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000000000001p+0)) (f32.const 1.0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.fffffffffffffp-1)) (f32.const 1.0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000010000000p+0)) (f32.const 0x1.000000p+0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000010000001p+0)) (f32.const 0x1.000002p+0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.000002fffffffp+0)) (f32.const 0x1.000002p+0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000030000000p+0)) (f32.const 0x1.000004p+0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000050000000p+0)) (f32.const 0x1.000004p+0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000010000000p+24)) (f32.const 0x1.0p+24)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000010000001p+24)) (f32.const 0x1.000002p+24)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.000002fffffffp+24)) (f32.const 0x1.000002p+24)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000030000000p+24)) (f32.const 0x1.000004p+24)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.4eae4f7024c7p+108)) (f32.const 0x1.4eae5p+108)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.a12e71e358685p-113)) (f32.const 0x1.a12e72p-113)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.cb98354d521ffp-127)) (f32.const 0x1.cb9834p-127)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.6972b30cfb562p+1)) (f32.const -0x1.6972b4p+1)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.bedbe4819d4c4p+112)) (f32.const -0x1.bedbe4p+112)) +(assert_return (invoke "f32.demote_f64" (f64.const nan)) (f32.const nan:canonical)) +(assert_return (invoke "f32.demote_f64" (f64.const nan:0x4000000000000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f32.demote_f64" (f64.const -nan)) (f32.const nan:canonical)) +(assert_return (invoke "f32.demote_f64" (f64.const -nan:0x4000000000000)) (f32.const nan:arithmetic)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1p-1022)) (f32.const 0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1p-1022)) (f32.const -0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0p-150)) (f32.const 0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.0p-150)) (f32.const -0.0)) +(assert_return (invoke "f32.demote_f64" (f64.const 0x1.0000000000001p-150)) (f32.const 0x1p-149)) +(assert_return (invoke "f32.demote_f64" (f64.const -0x1.0000000000001p-150)) (f32.const -0x1p-149)) + +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0)) (f32.const 0.0)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0x80000000)) (f32.const -0.0)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 1)) (f32.const 0x1p-149)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const -1)) (f32.const -nan:0x7fffff)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 123456789)) (f32.const 0x1.b79a2ap-113)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const -2147483647)) (f32.const -0x1p-149)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0x7f800000)) (f32.const inf)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0xff800000)) (f32.const -inf)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0x7fc00000)) (f32.const nan)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0xffc00000)) (f32.const -nan)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0x7fa00000)) (f32.const nan:0x200000)) +(assert_return (invoke "f32.reinterpret_i32" (i32.const 0xffa00000)) (f32.const -nan:0x200000)) + +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0)) (f64.const 0.0)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 1)) (f64.const 0x0.0000000000001p-1022)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const -1)) (f64.const -nan:0xfffffffffffff)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0x8000000000000000)) (f64.const -0.0)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 1234567890)) (f64.const 0x0.00000499602d2p-1022)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const -9223372036854775807)) (f64.const -0x0.0000000000001p-1022)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0x7ff0000000000000)) (f64.const inf)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0xfff0000000000000)) (f64.const -inf)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0x7ff8000000000000)) (f64.const nan)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0xfff8000000000000)) (f64.const -nan)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0x7ff4000000000000)) (f64.const nan:0x4000000000000)) +(assert_return (invoke "f64.reinterpret_i64" (i64.const 0xfff4000000000000)) (f64.const -nan:0x4000000000000)) + +(assert_return (invoke "i32.reinterpret_f32" (f32.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const -0.0)) (i32.const 0x80000000)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const 0x1p-149)) (i32.const 1)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const -nan:0x7fffff)) (i32.const -1)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const -0x1p-149)) (i32.const 0x80000001)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const 1.0)) (i32.const 1065353216)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const 3.1415926)) (i32.const 1078530010)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const 0x1.fffffep+127)) (i32.const 2139095039)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const -0x1.fffffep+127)) (i32.const -8388609)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const inf)) (i32.const 0x7f800000)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const -inf)) (i32.const 0xff800000)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const nan)) (i32.const 0x7fc00000)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const -nan)) (i32.const 0xffc00000)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const nan:0x200000)) (i32.const 0x7fa00000)) +(assert_return (invoke "i32.reinterpret_f32" (f32.const -nan:0x200000)) (i32.const 0xffa00000)) + +(assert_return (invoke "i64.reinterpret_f64" (f64.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const -0.0)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const 0x0.0000000000001p-1022)) (i64.const 1)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const -nan:0xfffffffffffff)) (i64.const -1)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const -0x0.0000000000001p-1022)) (i64.const 0x8000000000000001)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const 1.0)) (i64.const 4607182418800017408)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const 3.14159265358979)) (i64.const 4614256656552045841)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const 0x1.fffffffffffffp+1023)) (i64.const 9218868437227405311)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const -0x1.fffffffffffffp+1023)) (i64.const -4503599627370497)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const inf)) (i64.const 0x7ff0000000000000)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const -inf)) (i64.const 0xfff0000000000000)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const nan)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const -nan)) (i64.const 0xfff8000000000000)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const nan:0x4000000000000)) (i64.const 0x7ff4000000000000)) +(assert_return (invoke "i64.reinterpret_f64" (f64.const -nan:0x4000000000000)) (i64.const 0xfff4000000000000)) + +;; Type check + +(assert_invalid (module (func (result i32) (i32.wrap_i64 (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.trunc_f32_s (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.trunc_f32_u (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.trunc_f64_s (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.trunc_f64_u (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.reinterpret_f32 (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.extend_i32_s (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.extend_i32_u (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.trunc_f32_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.trunc_f32_u (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.trunc_f64_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.trunc_f64_u (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.reinterpret_f64 (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.convert_i32_s (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.convert_i32_u (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.convert_i64_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.convert_i64_u (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.demote_f64 (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32.reinterpret_i32 (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.convert_i32_s (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.convert_i32_u (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.convert_i64_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.convert_i64_u (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.promote_f32 (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64.reinterpret_i64 (i32.const 0)))) "type mismatch") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/binary.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/binary.wast new file mode 100644 index 000000000..002a9f26e --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/binary.wast @@ -0,0 +1,1525 @@ +(module binary "\00asm\01\00\00\00") +(module binary "\00asm" "\01\00\00\00") +(module $M1 binary "\00asm\01\00\00\00") +(module $M2 binary "\00asm" "\01\00\00\00") + +(assert_malformed (module binary "") "unexpected end") +(assert_malformed (module binary "\01") "unexpected end") +(assert_malformed (module binary "\00as") "unexpected end") +(assert_malformed (module binary "asm\00") "magic header not detected") +(assert_malformed (module binary "msa\00") "magic header not detected") +(assert_malformed (module binary "msa\00\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "msa\00\00\00\00\01") "magic header not detected") +(assert_malformed (module binary "asm\01\00\00\00\00") "magic header not detected") +(assert_malformed (module binary "wasm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\7fasm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\80asm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\82asm\01\00\00\00") "magic header not detected") +(assert_malformed (module binary "\ffasm\01\00\00\00") "magic header not detected") + +;; 8-byte endian-reversed. +(assert_malformed (module binary "\00\00\00\01msa\00") "magic header not detected") + +;; Middle-endian byte orderings. +(assert_malformed (module binary "a\00ms\00\01\00\00") "magic header not detected") +(assert_malformed (module binary "sm\00a\00\00\01\00") "magic header not detected") + +;; Upper-cased. +(assert_malformed (module binary "\00ASM\01\00\00\00") "magic header not detected") + +;; EBCDIC-encoded magic. +(assert_malformed (module binary "\00\81\a2\94\01\00\00\00") "magic header not detected") + +;; Leading UTF-8 BOM. +(assert_malformed (module binary "\ef\bb\bf\00asm\01\00\00\00") "magic header not detected") + +;; Malformed binary version. +(assert_malformed (module binary "\00asm") "unexpected end") +(assert_malformed (module binary "\00asm\01") "unexpected end") +(assert_malformed (module binary "\00asm\01\00\00") "unexpected end") +(assert_malformed (module binary "\00asm\00\00\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\0d\00\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\0e\00\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\00\01\00\00") "unknown binary version") +(assert_malformed (module binary "\00asm\00\00\01\00") "unknown binary version") +(assert_malformed (module binary "\00asm\00\00\00\01") "unknown binary version") + +;; Unsigned LEB128 can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\05\04\01" ;; Memory section with 1 entry + "\00\82\00" ;; no max, minimum 2 +) +(module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\00" ;; no max, minimum 2 +) + +;; Signed LEB128 can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\00" ;; i32.const 0 + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\7f" ;; i32.const -1 + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\00" ;; i32.const 0 + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\7f" ;; i32.const -1 + "\0b" ;; end +) + +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\00" ;; i64.const 0 with unused bits set + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\07\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\7f" ;; i64.const -1 with unused bits unset + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\00" ;; i64.const 0 with unused bits set + "\0b" ;; end +) +(module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\7f" ;; i64.const -1 with unused bits unset + "\0b" ;; end +) + +(module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; Memory section with 1 entry + "\00\00" ;; no max, minimum 0 + "\0b\06\01" ;; Data section with 1 entry + "\00" ;; Memory index 0 + "\41\00\0b\00" ;; (i32.const 0) with contents "" +) + +(module binary + "\00asm" "\01\00\00\00" + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + "\09\06\01" ;; Element section with 1 entry + "\00" ;; Table index 0 + "\41\00\0b\00" ;; (i32.const 0) with no elements +) + +;; Data segment memory index can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; Memory section with 1 entry + "\00\00" ;; no max, minimum 0 + "\0b\07\01" ;; Data section with 1 entry + "\80\00" ;; Memory index 0, encoded with 2 bytes + "\41\00\0b\00" ;; (i32.const 0) with contents "" +) + +;; Element segment table index can have non-minimal length +(module binary + "\00asm" "\01\00\00\00" + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + "\09\09\01" ;; Element section with 1 entry + "\02\80\00" ;; Table index 0, encoded with 2 bytes + "\41\00\0b\00\00" ;; (i32.const 0) with no elements +) + +;; Unsigned LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\08\01" ;; Memory section with 1 entry + "\00\82\80\80\80\80\00" ;; no max, minimum 2 with one byte too many + ) + "integer representation too long" +) + +;; Signed LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\80\00" ;; i32.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\ff\7f" ;; i32.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\80\00" ;; i64.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\7f" ;; i64.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +;; Unsigned LEB128s zero-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\70" ;; no max, minimum 2 with unused bits set + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\40" ;; no max, minimum 2 with some unused bits set + ) + "integer too large" +) + +;; Signed LEB128s sign-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\70" ;; i32.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\0f" ;; i32.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\1f" ;; i32.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\4f" ;; i32.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\7e" ;; i64.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\01" ;; i64.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\02" ;; i64.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\41" ;; i64.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + + +;; Unsigned LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\08\01" ;; Memory section with 1 entry + "\00\82\80\80\80\80\00" ;; no max, minimum 2 with one byte too many + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\02" ;; alignment 2 + "\82\80\80\80\80\00" ;; offset 2 with one byte too many + "\1a" ;; drop + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\82\80\80\80\80\00" ;; alignment 2 with one byte too many + "\00" ;; offset 0 + "\1a" ;; drop + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\12\01" ;; Code section + ;; function 0 + "\10\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\82\80\80\80\80\00" ;; alignment 2 with one byte too many + "\03" ;; offset 3 + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\12\01" ;; Code section + ;; function 0 + "\10\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\02" ;; alignment 2 + "\82\80\80\80\80\00" ;; offset 2 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +;; Signed LEB128 must not be overlong +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\80\00" ;; i32.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\ff\7f" ;; i32.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\80\00" ;; i64.const 0 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\10\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\7f" ;; i64.const -1 with one byte too many + "\0b" ;; end + ) + "integer representation too long" +) + +;; Unsigned LEB128s zero-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\70" ;; no max, minimum 2 with unused bits set + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\07\01" ;; Memory section with 1 entry + "\00\82\80\80\80\40" ;; no max, minimum 2 with some unused bits set + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + ;; function 0 + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\02" ;; alignment 2 + "\82\80\80\80\10" ;; offset 2 with unused bits set + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + ;; function 0 + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\02" ;; alignment 2 + "\82\80\80\80\40" ;; offset 2 with some unused bits set + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\82\80\80\80\10" ;; alignment 2 with unused bits set + "\00" ;; offset 0 + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\10\01" ;; Code section + ;; function 0 + "\0e\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\28" ;; i32.load + "\82\80\80\80\40" ;; alignment 2 with some unused bits set + "\00" ;; offset 0 + "\1a" ;; drop + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\82\80\80\80\10" ;; alignment 2 with unused bits set + "\03" ;; offset 3 + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\82\80\80\80\40" ;; alignment 2 with some unused bits set + "\03" ;; offset 3 + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\03" ;; alignment 2 + "\82\80\80\80\10" ;; offset 2 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\01" ;; Memory section + "\0a\11\01" ;; Code section + + ;; function 0 + "\0f\01\01" ;; local type count + "\7f" ;; i32 + "\41\00" ;; i32.const 0 + "\41\03" ;; i32.const 3 + "\36" ;; i32.store + "\02" ;; alignment 2 + "\82\80\80\80\40" ;; offset 2 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) + +;; Signed LEB128s sign-extend +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\70" ;; i32.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\0f" ;; i32.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\80\80\80\80\1f" ;; i32.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0a\01" ;; Global section with 1 entry + "\7f\00" ;; i32, immutable + "\41\ff\ff\ff\ff\4f" ;; i32.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\7e" ;; i64.const 0 with unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\01" ;; i64.const -1 with unused bits unset + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\80\80\80\80\80\80\80\80\80\02" ;; i64.const 0 with some unused bits set + "\0b" ;; end + ) + "integer too large" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0f\01" ;; Global section with 1 entry + "\7e\00" ;; i64, immutable + "\42\ff\ff\ff\ff\ff\ff\ff\ff\ff\41" ;; i64.const -1 with some unused bits unset + "\0b" ;; end + ) + "integer too large" +) + +;; memory.grow reserved byte equal to zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\09\01" ;; Code section + + ;; function 0 + "\07\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\01" ;; memory.grow reserved byte is not equal to zero! + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; memory.grow reserved byte should not be a "long" LEB128 zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\08\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; Same as above for 3, 4, and 5-byte zero encodings. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0b\01" ;; Code section + + ;; function 0 + "\09\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\0a\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\80\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0d\01" ;; Code section + + ;; function 0 + "\0b\00" + "\41\00" ;; i32.const 0 + "\40" ;; memory.grow + "\80\80\80\80\00" ;; memory.grow reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; memory.size reserved byte equal to zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\07\01" ;; Code section + + ;; function 0 + "\05\00" + "\3f" ;; memory.size + "\01" ;; memory.size reserved byte is not equal to zero! + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; memory.size reserved byte should not be a "long" LEB128 zero. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\08\01" ;; Code section + + ;; function 0 + "\06\00" + "\3f" ;; memory.size + "\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; Same as above for 3, 4, and 5-byte zero encodings. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\09\01" ;; Code section + + ;; function 0 + "\07\00" + "\3f" ;; memory.size + "\80\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\08\00" + "\3f" ;; memory.size + "\80\80\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0b\01" ;; Code section + + ;; function 0 + "\09\00" + "\3f" ;; memory.size + "\80\80\80\80\00" ;; memory.size reserved byte + "\1a" ;; drop + "\0b" ;; end + ) + "zero flag expected" +) + +;; No more than 2^32 locals. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\0a\0c\01" ;; Code section + + ;; function 0 + "\0a\02" + "\ff\ff\ff\ff\0f\7f" ;; 0xFFFFFFFF i32 + "\02\7e" ;; 0x00000002 i64 + "\0b" ;; end + ) + "too many locals" +) + +;; Local count can be 0. +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\0a\0a\01" ;; Code section + + ;; function 0 + "\08\03" + "\00\7f" ;; 0 i32 + "\00\7e" ;; 0 i64 + "\02\7d" ;; 2 f32 + "\0b" ;; end +) + +;; Function section has non-zero count, but code section is absent. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\03\02\00\00" ;; Function section with 2 functions + ) + "function and code section have inconsistent lengths" +) + +;; Code section has non-zero count, but function section is absent. +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0a\04\01\02\00\0b" ;; Code section with 1 empty function + ) + "function and code section have inconsistent lengths" +) + +;; Function section count > code section count +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\03\02\00\00" ;; Function section with 2 functions + "\0a\04\01\02\00\0b" ;; Code section with 1 empty function + ) + "function and code section have inconsistent lengths" +) + +;; Function section count < code section count +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section with 1 function + "\0a\07\02\02\00\0b\02\00\0b" ;; Code section with 2 empty functions + ) + "function and code section have inconsistent lengths" +) + +;; Function section has zero count, and code section is absent. +(module binary + "\00asm" "\01\00\00\00" + "\03\01\00" ;; Function section with 0 functions +) + +;; Code section has zero count, and function section is absent. +(module binary + "\00asm" "\01\00\00\00" + "\0a\01\00" ;; Code section with 0 functions +) + +;; Fewer passive segments than datacount +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0c\01\03" ;; Datacount section with value "3" + "\0b\05\02" ;; Data section with two entries + "\01\00" ;; Passive data section + "\01\00") ;; Passive data section + "data count and data section have inconsistent lengths") + +;; More passive segments than datacount +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\0c\01\01" ;; Datacount section with value "1" + "\0b\05\02" ;; Data section with two entries + "\01\00" ;; Passive data section + "\01\00") ;; Passive data section + "data count and data section have inconsistent lengths") + +;; memory.init requires a datacount section +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\0e\01" ;; Code section + + ;; function 0 + "\0c\00" + "\41\00" ;; zero args + "\41\00" + "\41\00" + "\fc\08\00\00" ;; memory.init + "\0b" + + "\0b\03\01\01\00" ;; Data section + ) ;; end + "data count section required") + +;; data.drop requires a datacount section +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\05\03\01\00\00" ;; Memory section + "\0a\07\01" ;; Code section + + ;; function 0 + "\05\00" + "\fc\09\00" ;; data.drop + "\0b" + + "\0b\03\01\01\00" ;; Data section + ) ;; end + "data count section required") + +;; passive element segment containing opcode other than ref.func or ref.null +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\07\01" ;; Element section with one segment + "\05\70" ;; Passive, funcref + "\01" ;; 1 element + "\d3\00\0b" ;; bad opcode, index 0, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + "illegal opcode") + +;; passive element segment containing type other than funcref +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\07\01" ;; Element section with one segment + "\05\7f" ;; Passive, i32 + "\01" ;; 1 element + "\d2\00\0b" ;; ref.func, index 0, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + "invalid reference type") + +;; passive element segment containing opcode ref.func +(module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\07\01" ;; Element section with one segment + "\05\70" ;; Passive, funcref + "\01" ;; 1 element + "\d2\00\0b" ;; ref.func, index 0, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + +;; passive element segment containing opcode ref.null +(module binary + "\00asm" "\01\00\00\00" + + "\01\04\01\60\00\00" ;; Type section + + "\03\02\01\00" ;; Function section + + "\04\04\01" ;; Table section with 1 entry + "\70\00\00" ;; no max, minimum 0, funcref + + "\05\03\01\00\00" ;; Memory section + + "\09\06\01" ;; Element section with one segment + "\05\70" ;; Passive, funcref + "\01" ;; 1 element + "\d0\0b" ;; ref.null, end + + "\0a\04\01" ;; Code section + + ;; function 0 + "\02\00" + "\0b") ;; end + + +;; Type count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\01\00" ;; type count can be zero +) + +;; 2 type declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\07\02" ;; type section with inconsistent count (2 declared, 1 given) + "\60\00\00" ;; 1st type + ;; "\60\00\00" ;; 2nd type (missed) + ) + "unexpected end of section or function" +) + +;; 1 type declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\07\01" ;; type section with inconsistent count (1 declared, 2 given) + "\60\00\00" ;; 1st type + "\60\00\00" ;; 2nd type (redundant) + ) + "section size mismatch" +) + +;; Import count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; type 0 + "\02\01\00" ;; import count can be zero +) + +;; 2 import declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\01\7f\00" ;; type 0 + "\02\16\02" ;; import section with inconsistent count (2 declared, 1 given) + ;; 1st import + "\08" ;; string length + "\73\70\65\63\74\65\73\74" ;; spectest + "\09" ;; string length + "\70\72\69\6e\74\5f\69\33\32" ;; print_i32 + "\00\00" ;; import kind, import signature index + ;; 2nd import + ;; (missed) + ) + "unexpected end of section or function" +) + +;; 1 import declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\09\02" ;; type section + "\60\01\7f\00" ;; type 0 + "\60\01\7d\00" ;; type 1 + "\02\2b\01" ;; import section with inconsistent count (1 declared, 2 given) + ;; 1st import + "\08" ;; string length + "\73\70\65\63\74\65\73\74" ;; spectest + "\09" ;; string length + "\70\72\69\6e\74\5f\69\33\32" ;; print_i32 + "\00\00" ;; import kind, import signature index + ;; 2nd import + ;; (redundant) + "\08" ;; string length + "\73\70\65\63\74\65\73\74" ;; spectest + "\09" ;; string length + "\70\72\69\6e\74\5f\66\33\32" ;; print_f32 + "\00\01" ;; import kind, import signature index + ) + "section size mismatch" +) + +;; Table count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\04\01\00" ;; table count can be zero +) + +;; 1 table declared, 0 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\04\01\01" ;; table section with inconsistent count (1 declared, 0 given) + ;; "\70\01\00\00" ;; table entity + ) + "unexpected end of section or function" +) + +;; Memory count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\05\01\00" ;; memory count can be zero +) + +;; 1 memory declared, 0 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\01\01" ;; memory section with inconsistent count (1 declared, 0 given) + ;; "\00\00" ;; memory 0 (missed) + ) + "unexpected end of section or function" +) + +;; Global count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\06\01\00" ;; global count can be zero +) + +;; 2 global declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\06\02" ;; global section with inconsistent count (2 declared, 1 given) + "\7f\00\41\00\0b" ;; global 0 + ;; "\7f\00\41\00\0b" ;; global 1 (missed) + ) + "unexpected end of section or function" +) + +;; 1 global declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\0b\01" ;; global section with inconsistent count (1 declared, 2 given) + "\7f\00\41\00\0b" ;; global 0 + "\7f\00\41\00\0b" ;; global 1 (redundant) + ) + "section size mismatch" +) + +;; Export count can be 0 +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\03\02\00\00" ;; func section + "\07\01\00" ;; export count can be zero + "\0a\07\02" ;; code section + "\02\00\0b" ;; function body 0 + "\02\00\0b" ;; function body 1 +) + +;; 2 export declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\03\02\00\00" ;; func section + "\07\06\02" ;; export section with inconsistent count (2 declared, 1 given) + "\02" ;; export 0 + "\66\31" ;; export name + "\00\00" ;; export kind, export func index + ;; "\02" ;; export 1 (missed) + ;; "\66\32" ;; export name + ;; "\00\01" ;; export kind, export func index + "\0a\07\02" ;; code section + "\02\00\0b" ;; function body 0 + "\02\00\0b" ;; function body 1 + ) + "unexpected end of section or function" +) + +;; 1 export declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\03\02\00\00" ;; func section + "\07\0b\01" ;; export section with inconsistent count (1 declared, 2 given) + "\02" ;; export 0 + "\66\31" ;; export name + "\00\00" ;; export kind, export func index + "\02" ;; export 1 (redundant) + "\66\32" ;; export name + "\00\01" ;; export kind, export func index + "\0a\07\02" ;; code section + "\02\00\0b" ;; function body 0 + "\02\00\0b" ;; function body 1 + ) + "section size mismatch" +) + +;; elem segment count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\01\00" ;; elem segment count can be zero + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body +) + +;; 2 elem segment declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\07\02" ;; elem with inconsistent segment count (2 declared, 1 given) + "\00\41\00\0b\01\00" ;; elem 0 + ;; "\00\41\00\0b\01\00" ;; elem 1 (missed) + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body + ) + "invalid elements segment kind" +) + +;; 1 elem segment declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\04\04\01" ;; table section + "\70\00\01" ;; table 0 + "\09\0d\01" ;; elem with inconsistent segment count (1 declared, 2 given) + "\00\41\00\0b\01\00" ;; elem 0 + "\00\41\00\0b\01\00" ;; elem 1 (redundant) + "\0a\04\01" ;; code section + "\02\00\0b" ;; function body + ) + "section size mismatch" +) + +;; data segment count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\01\00" ;; data segment count can be zero +) + +;; 2 data segment declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\07\02" ;; data with inconsistent segment count (2 declared, 1 given) + "\00\41\00\0b\01\61" ;; data 0 + ;; "\00\41\01\0b\01\62" ;; data 1 (missed) + ) + "unexpected end of section or function" +) + +;; 1 data segment declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\0d\01" ;; data with inconsistent segment count (1 declared, 2 given) + "\00\41\00\0b\01\61" ;; data 0 + "\00\41\01\0b\01\62" ;; data 1 (redundant) + ) + "section size mismatch" +) + +;; data segment has 7 bytes declared, but 6 bytes given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\0c\01" ;; data section + "\00\41\03\0b" ;; data segment 0 + "\07" ;; data segment size with inconsistent lengths (7 declared, 6 given) + "\61\62\63\64\65\66" ;; 6 bytes given + ) + "unexpected end of section or function" +) + +;; data segment has 5 bytes declared, but 6 bytes given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01" ;; memory section + "\00\01" ;; memory 0 + "\0b\0c\01" ;; data section + "\00\41\00\0b" ;; data segment 0 + "\05" ;; data segment size with inconsistent lengths (5 declared, 6 given) + "\61\62\63\64\65\66" ;; 6 bytes given + ) + "section size mismatch" +) + +;; br_table target count can be zero +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\0a\11\01" ;; code section + "\0f\00" ;; func 0 + "\02\40" ;; block 0 + "\41\01" ;; condition of if 0 + "\04\40" ;; if 0 + "\41\01" ;; index of br_table element + "\0e\00" ;; br_table target count can be zero + "\02" ;; break depth for default + "\0b\0b\0b" ;; end +) + +;; 2 br_table target declared, 1 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\0a\12\01" ;; code section + "\10\00" ;; func 0 + "\02\40" ;; block 0 + "\41\01" ;; condition of if 0 + "\04\40" ;; if 0 + "\41\01" ;; index of br_table element + "\0e\02" ;; br_table with inconsistent target count (2 declared, 1 given) + "\00" ;; break depth 0 + ;; "\01" ;; break depth 1 (missed) + "\02" ;; break depth for default + "\0b\0b\0b" ;; end + ) + "unexpected end of section or function" +) + +;; 1 br_table target declared, 2 given +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01" ;; type section + "\60\00\00" ;; type 0 + "\03\02\01\00" ;; func section + "\0a\12\01" ;; code section + "\11\00" ;; func 0 + "\02\40" ;; block 0 + "\41\01" ;; condition of if 0 + "\04\40" ;; if 0 + "\41\01" ;; index of br_table element + "\0e\01" ;; br_table with inconsistent target count (1 declared, 2 given) + "\00" ;; break depth 0 + "\01" ;; break depth 1 + "\02" ;; break depth for default + "\0b\0b\0b" ;; end + ) + "invalid reference type" +) + +;; Start section +(module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\08\01\00" ;; Start section: function 0 + + "\0a\04\01" ;; Code section + ;; function 0 + "\02\00" + "\0b" ;; end +) + +;; Multiple start sections +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\04\01\60\00\00" ;; Type section + "\03\02\01\00" ;; Function section + "\08\01\00" ;; Start section: function 0 + "\08\01\00" ;; Start section: function 0 + + "\0a\04\01" ;; Code section + ;; function 0 + "\02\00" + "\0b" ;; end + ) + "junk after last section" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/br_table.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/br_table.wast new file mode 100644 index 000000000..48e7a874a --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/br_table.wast @@ -0,0 +1,1690 @@ +;; Test `br_table` operator + +(module + ;; Auxiliary definition + (func $dummy) + + (func (export "type-i32") + (block (drop (i32.ctz (br_table 0 0 (i32.const 0))))) + ) + (func (export "type-i64") + (block (drop (i64.ctz (br_table 0 0 (i32.const 0))))) + ) + (func (export "type-f32") + (block (drop (f32.neg (br_table 0 0 (i32.const 0))))) + ) + (func (export "type-f64") + (block (drop (f64.neg (br_table 0 0 (i32.const 0))))) + ) + + (func (export "type-i32-value") (result i32) + (block (result i32) (i32.ctz (br_table 0 0 (i32.const 1) (i32.const 0)))) + ) + (func (export "type-i64-value") (result i64) + (block (result i64) (i64.ctz (br_table 0 0 (i64.const 2) (i32.const 0)))) + ) + (func (export "type-f32-value") (result f32) + (block (result f32) (f32.neg (br_table 0 0 (f32.const 3) (i32.const 0)))) + ) + (func (export "type-f64-value") (result f64) + (block (result f64) (f64.neg (br_table 0 0 (f64.const 4) (i32.const 0)))) + ) + + (func (export "empty") (param i32) (result i32) + (block (br_table 0 (local.get 0)) (return (i32.const 21))) + (i32.const 22) + ) + (func (export "empty-value") (param i32) (result i32) + (block (result i32) + (br_table 0 (i32.const 33) (local.get 0)) (i32.const 31) + ) + ) + + (func (export "singleton") (param i32) (result i32) + (block + (block + (br_table 1 0 (local.get 0)) + (return (i32.const 21)) + ) + (return (i32.const 20)) + ) + (i32.const 22) + ) + + (func (export "singleton-value") (param i32) (result i32) + (block (result i32) + (drop + (block (result i32) + (br_table 0 1 (i32.const 33) (local.get 0)) + (return (i32.const 31)) + ) + ) + (i32.const 32) + ) + ) + + (func (export "multiple") (param i32) (result i32) + (block + (block + (block + (block + (block + (br_table 3 2 1 0 4 (local.get 0)) + (return (i32.const 99)) + ) + (return (i32.const 100)) + ) + (return (i32.const 101)) + ) + (return (i32.const 102)) + ) + (return (i32.const 103)) + ) + (i32.const 104) + ) + + (func (export "multiple-value") (param i32) (result i32) + (local i32) + (local.set 1 (block (result i32) + (local.set 1 (block (result i32) + (local.set 1 (block (result i32) + (local.set 1 (block (result i32) + (local.set 1 (block (result i32) + (br_table 3 2 1 0 4 (i32.const 200) (local.get 0)) + (return (i32.add (local.get 1) (i32.const 99))) + )) + (return (i32.add (local.get 1) (i32.const 10))) + )) + (return (i32.add (local.get 1) (i32.const 11))) + )) + (return (i32.add (local.get 1) (i32.const 12))) + )) + (return (i32.add (local.get 1) (i32.const 13))) + )) + (i32.add (local.get 1) (i32.const 14)) + ) + + (func (export "large") (param i32) (result i32) + (block + (block + (br_table + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 + (local.get 0) + ) + (return (i32.const -1)) + ) + (return (i32.const 0)) + ) + (return (i32.const 1)) + ) + + (func (export "as-block-first") + (block (br_table 0 0 0 (i32.const 0)) (call $dummy)) + ) + (func (export "as-block-mid") + (block (call $dummy) (br_table 0 0 0 (i32.const 0)) (call $dummy)) + ) + (func (export "as-block-last") + (block (nop) (call $dummy) (br_table 0 0 0 (i32.const 0))) + ) + (func (export "as-block-value") (result i32) + (block (result i32) + (nop) (call $dummy) (br_table 0 0 0 (i32.const 2) (i32.const 0)) + ) + ) + + (func (export "as-loop-first") (result i32) + (loop (result i32) (br_table 1 1 (i32.const 3) (i32.const 0)) (i32.const 1)) + ) + (func (export "as-loop-mid") (result i32) + (loop (result i32) + (call $dummy) + (br_table 1 1 1 (i32.const 4) (i32.const -1)) + (i32.const 2) + ) + ) + (func (export "as-loop-last") (result i32) + (loop (result i32) + (nop) (call $dummy) (br_table 1 1 1 (i32.const 5) (i32.const 1)) + ) + ) + + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (br_table 0 (i32.const 9) (i32.const 0)))) + ) + + (func (export "as-br_if-cond") + (block (br_if 0 (br_table 0 0 0 (i32.const 1)))) + ) + (func (export "as-br_if-value") (result i32) + (block (result i32) + (drop (br_if 0 (br_table 0 (i32.const 8) (i32.const 0)) (i32.const 1))) + (i32.const 7) + ) + ) + (func (export "as-br_if-value-cond") (result i32) + (block (result i32) + (drop (br_if 0 (i32.const 6) (br_table 0 0 (i32.const 9) (i32.const 0)))) + (i32.const 7) + ) + ) + + (func (export "as-br_table-index") + (block (br_table 0 0 0 (br_table 0 (i32.const 1)))) + ) + (func (export "as-br_table-value") (result i32) + (block (result i32) + (br_table 0 0 0 (br_table 0 (i32.const 10) (i32.const 0)) (i32.const 1)) + (i32.const 7) + ) + ) + (func (export "as-br_table-value-index") (result i32) + (block (result i32) + (br_table 0 0 (i32.const 6) (br_table 0 (i32.const 11) (i32.const 1))) + (i32.const 7) + ) + ) + + (func (export "as-return-value") (result i64) + (block (result i64) (return (br_table 0 (i64.const 7) (i32.const 0)))) + ) + + (func (export "as-if-cond") (result i32) + (block (result i32) + (if (result i32) + (br_table 0 (i32.const 2) (i32.const 0)) + (then (i32.const 0)) + (else (i32.const 1)) + ) + ) + ) + (func (export "as-if-then") (param i32 i32) (result i32) + (block (result i32) + (if (result i32) + (local.get 0) + (then (br_table 1 (i32.const 3) (i32.const 0))) + (else (local.get 1)) + ) + ) + ) + (func (export "as-if-else") (param i32 i32) (result i32) + (block (result i32) + (if (result i32) + (local.get 0) + (then (local.get 1)) + (else (br_table 1 0 (i32.const 4) (i32.const 0))) + ) + ) + ) + + (func (export "as-select-first") (param i32 i32) (result i32) + (block (result i32) + (select + (br_table 0 (i32.const 5) (i32.const 0)) (local.get 0) (local.get 1) + ) + ) + ) + (func (export "as-select-second") (param i32 i32) (result i32) + (block (result i32) + (select + (local.get 0) (br_table 0 (i32.const 6) (i32.const 1)) (local.get 1) + ) + ) + ) + (func (export "as-select-cond") (result i32) + (block (result i32) + (select + (i32.const 0) (i32.const 1) (br_table 0 (i32.const 7) (i32.const 1)) + ) + ) + ) + + (func $f (param i32 i32 i32) (result i32) (i32.const -1)) + (func (export "as-call-first") (result i32) + (block (result i32) + (call $f + (br_table 0 (i32.const 12) (i32.const 1)) (i32.const 2) (i32.const 3) + ) + ) + ) + (func (export "as-call-mid") (result i32) + (block (result i32) + (call $f + (i32.const 1) (br_table 0 (i32.const 13) (i32.const 1)) (i32.const 3) + ) + ) + ) + (func (export "as-call-last") (result i32) + (block (result i32) + (call $f + (i32.const 1) (i32.const 2) (br_table 0 (i32.const 14) (i32.const 1)) + ) + ) + ) + + (type $sig (func (param i32 i32 i32) (result i32))) + (table funcref (elem $f)) + (func (export "as-call_indirect-first") (result i32) + (block (result i32) + (call_indirect (type $sig) + (br_table 0 (i32.const 20) (i32.const 1)) (i32.const 1) (i32.const 2) + (i32.const 3) + ) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) (br_table 0 (i32.const 21) (i32.const 1)) (i32.const 2) + (i32.const 3) + ) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) (i32.const 1) (br_table 0 (i32.const 22) (i32.const 1)) + (i32.const 3) + ) + ) + ) + (func (export "as-call_indirect-func") (result i32) + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) (i32.const 1) (i32.const 2) + (br_table 0 (i32.const 23) (i32.const 1)) + ) + ) + ) + + (func (export "as-local.set-value") (result i32) + (local f32) + (block (result i32) + (local.set 0 (br_table 0 (i32.const 17) (i32.const 1))) + (i32.const -1) + ) + ) + (func (export "as-local.tee-value") (result i32) + (local i32) + (block (result i32) + (local.set 0 (br_table 0 (i32.const 1) (i32.const 1))) + (i32.const -1) + ) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (result i32) + (block (result i32) + (global.set $a (br_table 0 (i32.const 1) (i32.const 1))) + (i32.const -1) + ) + ) + + (memory 1) + (func (export "as-load-address") (result f32) + (block (result f32) (f32.load (br_table 0 (f32.const 1.7) (i32.const 1)))) + ) + (func (export "as-loadN-address") (result i64) + (block (result i64) (i64.load8_s (br_table 0 (i64.const 30) (i32.const 1)))) + ) + + (func (export "as-store-address") (result i32) + (block (result i32) + (f64.store (br_table 0 (i32.const 30) (i32.const 1)) (f64.const 7)) + (i32.const -1) + ) + ) + (func (export "as-store-value") (result i32) + (block (result i32) + (i64.store (i32.const 2) (br_table 0 (i32.const 31) (i32.const 1))) + (i32.const -1) + ) + ) + + (func (export "as-storeN-address") (result i32) + (block (result i32) + (i32.store8 (br_table 0 (i32.const 32) (i32.const 0)) (i32.const 7)) + (i32.const -1) + ) + ) + (func (export "as-storeN-value") (result i32) + (block (result i32) + (i64.store16 (i32.const 2) (br_table 0 (i32.const 33) (i32.const 0))) + (i32.const -1) + ) + ) + + (func (export "as-unary-operand") (result f32) + (block (result f32) (f32.neg (br_table 0 (f32.const 3.4) (i32.const 0)))) + ) + + (func (export "as-binary-left") (result i32) + (block (result i32) + (i32.add (br_table 0 0 (i32.const 3) (i32.const 0)) (i32.const 10)) + ) + ) + (func (export "as-binary-right") (result i64) + (block (result i64) + (i64.sub (i64.const 10) (br_table 0 (i64.const 45) (i32.const 0))) + ) + ) + + (func (export "as-test-operand") (result i32) + (block (result i32) (i32.eqz (br_table 0 (i32.const 44) (i32.const 0)))) + ) + + (func (export "as-compare-left") (result i32) + (block (result i32) + (f64.le (br_table 0 0 (i32.const 43) (i32.const 0)) (f64.const 10)) + ) + ) + (func (export "as-compare-right") (result i32) + (block (result i32) + (f32.ne (f32.const 10) (br_table 0 (i32.const 42) (i32.const 0))) + ) + ) + + (func (export "as-convert-operand") (result i32) + (block (result i32) + (i32.wrap_i64 (br_table 0 (i32.const 41) (i32.const 0))) + ) + ) + + (func (export "as-memory.grow-size") (result i32) + (block (result i32) (memory.grow (br_table 0 (i32.const 40) (i32.const 0)))) + ) + + (func (export "nested-block-value") (param i32) (result i32) + (block (result i32) + (drop (i32.const -1)) + (i32.add + (i32.const 1) + (block (result i32) + (i32.add + (i32.const 2) + (block (result i32) + (drop (i32.const 4)) + (i32.add + (i32.const 8) + (br_table 0 1 2 (i32.const 16) (local.get 0)) + ) + ) + ) + ) + ) + ) + ) + + (func (export "nested-br-value") (param i32) (result i32) + (block (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (block (result i32) + (drop (i32.const 4)) + (br 0 (br_table 2 1 0 (i32.const 8) (local.get 0))) + ) + ) + (i32.const 16) + ) + ) + ) + ) + + (func (export "nested-br_if-value") (param i32) (result i32) + (block (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (block (result i32) + (drop (i32.const 4)) + (drop + (br_if 0 + (br_table 0 1 2 (i32.const 8) (local.get 0)) + (i32.const 1) + ) + ) + (i32.const 32) + ) + ) + (i32.const 16) + ) + ) + ) + ) + + (func (export "nested-br_if-value-cond") (param i32) (result i32) + (block (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (br_if 0 (i32.const 4) (br_table 0 1 0 (i32.const 8) (local.get 0))) + ) + (i32.const 16) + ) + ) + ) + ) + + (func (export "nested-br_table-value") (param i32) (result i32) + (block (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (drop + (block (result i32) + (drop (i32.const 4)) + (br_table 0 (br_table 0 1 2 (i32.const 8) (local.get 0)) (i32.const 1)) + (i32.const 32) + ) + ) + (i32.const 16) + ) + ) + ) + ) + + (func (export "nested-br_table-value-index") (param i32) (result i32) + (block (result i32) + (i32.add + (i32.const 1) + (block (result i32) + (drop (i32.const 2)) + (br_table 0 (i32.const 4) (br_table 0 1 0 (i32.const 8) (local.get 0))) + (i32.const 16) + ) + ) + ) + ) + + (func (export "nested-br_table-loop-block") (param i32) (result i32) + (local.set 0 + (loop (result i32) + (block + (br_table 1 0 0 (local.get 0)) + ) + (i32.const 0) + ) + ) + (loop (result i32) + (block + (br_table 0 1 1 (local.get 0)) + ) + (i32.const 3) + ) + ) + + (func (export "meet-anyref") (param i32) (param anyref) (result anyref) + (block $l1 (result anyref) + (block $l2 (result anyref) + (br_table $l1 $l2 $l1 (local.get 1) (local.get 0)) + ) + ) + ) + + (func (export "meet-funcref-1") (param i32) (result anyref) + (block $l1 (result anyref) + (block $l2 (result funcref) + (br_table $l1 $l1 $l2 (table.get 0 (i32.const 0)) (local.get 0)) + ) + ) + ) + (func (export "meet-funcref-2") (param i32) (result anyref) + (block $l1 (result anyref) + (block $l2 (result funcref) + (br_table $l2 $l2 $l1 (table.get 0 (i32.const 0)) (local.get 0)) + ) + ) + ) + (func (export "meet-funcref-3") (param i32) (result anyref) + (block $l1 (result anyref) + (block $l2 (result funcref) + (br_table $l2 $l1 $l2 (table.get 0 (i32.const 0)) (local.get 0)) + ) + ) + ) + (func (export "meet-funcref-4") (param i32) (result anyref) + (block $l1 (result anyref) + (block $l2 (result funcref) + (br_table $l1 $l2 $l1 (table.get 0 (i32.const 0)) (local.get 0)) + ) + ) + ) + + (func (export "meet-nullref") (param i32) (result funcref) + (block $l1 (result funcref) + (block $l2 (result nullref) + (br_table $l1 $l2 $l1 (ref.null) (local.get 0)) + ) + ) + ) + + (func (export "meet-multi-ref") (param i32) (result anyref) + (block $l1 (result anyref) + (block $l2 (result funcref) + (block $l3 (result nullref) + (br_table $l3 $l2 $l1 (ref.null) (local.get 0)) + ) + ) + ) + ) + + (func (export "meet-bottom") + (block (result f64) + (block (result f32) + (unreachable) + (br_table 0 1 1 (i32.const 1)) + ) + (drop) + (f64.const 0) + ) + (drop) + ) +) + +(assert_return (invoke "type-i32")) +(assert_return (invoke "type-i64")) +(assert_return (invoke "type-f32")) +(assert_return (invoke "type-f64")) + +(assert_return (invoke "type-i32-value") (i32.const 1)) +(assert_return (invoke "type-i64-value") (i64.const 2)) +(assert_return (invoke "type-f32-value") (f32.const 3)) +(assert_return (invoke "type-f64-value") (f64.const 4)) + +(assert_return (invoke "empty" (i32.const 0)) (i32.const 22)) +(assert_return (invoke "empty" (i32.const 1)) (i32.const 22)) +(assert_return (invoke "empty" (i32.const 11)) (i32.const 22)) +(assert_return (invoke "empty" (i32.const -1)) (i32.const 22)) +(assert_return (invoke "empty" (i32.const -100)) (i32.const 22)) +(assert_return (invoke "empty" (i32.const 0xffffffff)) (i32.const 22)) + +(assert_return (invoke "empty-value" (i32.const 0)) (i32.const 33)) +(assert_return (invoke "empty-value" (i32.const 1)) (i32.const 33)) +(assert_return (invoke "empty-value" (i32.const 11)) (i32.const 33)) +(assert_return (invoke "empty-value" (i32.const -1)) (i32.const 33)) +(assert_return (invoke "empty-value" (i32.const -100)) (i32.const 33)) +(assert_return (invoke "empty-value" (i32.const 0xffffffff)) (i32.const 33)) + +(assert_return (invoke "singleton" (i32.const 0)) (i32.const 22)) +(assert_return (invoke "singleton" (i32.const 1)) (i32.const 20)) +(assert_return (invoke "singleton" (i32.const 11)) (i32.const 20)) +(assert_return (invoke "singleton" (i32.const -1)) (i32.const 20)) +(assert_return (invoke "singleton" (i32.const -100)) (i32.const 20)) +(assert_return (invoke "singleton" (i32.const 0xffffffff)) (i32.const 20)) + +(assert_return (invoke "singleton-value" (i32.const 0)) (i32.const 32)) +(assert_return (invoke "singleton-value" (i32.const 1)) (i32.const 33)) +(assert_return (invoke "singleton-value" (i32.const 11)) (i32.const 33)) +(assert_return (invoke "singleton-value" (i32.const -1)) (i32.const 33)) +(assert_return (invoke "singleton-value" (i32.const -100)) (i32.const 33)) +(assert_return (invoke "singleton-value" (i32.const 0xffffffff)) (i32.const 33)) + +(assert_return (invoke "multiple" (i32.const 0)) (i32.const 103)) +(assert_return (invoke "multiple" (i32.const 1)) (i32.const 102)) +(assert_return (invoke "multiple" (i32.const 2)) (i32.const 101)) +(assert_return (invoke "multiple" (i32.const 3)) (i32.const 100)) +(assert_return (invoke "multiple" (i32.const 4)) (i32.const 104)) +(assert_return (invoke "multiple" (i32.const 5)) (i32.const 104)) +(assert_return (invoke "multiple" (i32.const 6)) (i32.const 104)) +(assert_return (invoke "multiple" (i32.const 10)) (i32.const 104)) +(assert_return (invoke "multiple" (i32.const -1)) (i32.const 104)) +(assert_return (invoke "multiple" (i32.const 0xffffffff)) (i32.const 104)) + +(assert_return (invoke "multiple-value" (i32.const 0)) (i32.const 213)) +(assert_return (invoke "multiple-value" (i32.const 1)) (i32.const 212)) +(assert_return (invoke "multiple-value" (i32.const 2)) (i32.const 211)) +(assert_return (invoke "multiple-value" (i32.const 3)) (i32.const 210)) +(assert_return (invoke "multiple-value" (i32.const 4)) (i32.const 214)) +(assert_return (invoke "multiple-value" (i32.const 5)) (i32.const 214)) +(assert_return (invoke "multiple-value" (i32.const 6)) (i32.const 214)) +(assert_return (invoke "multiple-value" (i32.const 10)) (i32.const 214)) +(assert_return (invoke "multiple-value" (i32.const -1)) (i32.const 214)) +(assert_return (invoke "multiple-value" (i32.const 0xffffffff)) (i32.const 214)) + +(assert_return (invoke "large" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "large" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "large" (i32.const 100)) (i32.const 0)) +(assert_return (invoke "large" (i32.const 101)) (i32.const 1)) +(assert_return (invoke "large" (i32.const 10000)) (i32.const 0)) +(assert_return (invoke "large" (i32.const 10001)) (i32.const 1)) +(assert_return (invoke "large" (i32.const 1000000)) (i32.const 1)) +(assert_return (invoke "large" (i32.const 1000001)) (i32.const 1)) + +(assert_return (invoke "as-block-first")) +(assert_return (invoke "as-block-mid")) +(assert_return (invoke "as-block-last")) +(assert_return (invoke "as-block-value") (i32.const 2)) + +(assert_return (invoke "as-loop-first") (i32.const 3)) +(assert_return (invoke "as-loop-mid") (i32.const 4)) +(assert_return (invoke "as-loop-last") (i32.const 5)) + +(assert_return (invoke "as-br-value") (i32.const 9)) + +(assert_return (invoke "as-br_if-cond")) +(assert_return (invoke "as-br_if-value") (i32.const 8)) +(assert_return (invoke "as-br_if-value-cond") (i32.const 9)) + +(assert_return (invoke "as-br_table-index")) +(assert_return (invoke "as-br_table-value") (i32.const 10)) +(assert_return (invoke "as-br_table-value-index") (i32.const 11)) + +(assert_return (invoke "as-return-value") (i64.const 7)) + +(assert_return (invoke "as-if-cond") (i32.const 2)) +(assert_return (invoke "as-if-then" (i32.const 1) (i32.const 6)) (i32.const 3)) +(assert_return (invoke "as-if-then" (i32.const 0) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-if-else" (i32.const 0) (i32.const 6)) (i32.const 4)) +(assert_return (invoke "as-if-else" (i32.const 1) (i32.const 6)) (i32.const 6)) + +(assert_return (invoke "as-select-first" (i32.const 0) (i32.const 6)) (i32.const 5)) +(assert_return (invoke "as-select-first" (i32.const 1) (i32.const 6)) (i32.const 5)) +(assert_return (invoke "as-select-second" (i32.const 0) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-select-second" (i32.const 1) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-select-cond") (i32.const 7)) + +(assert_return (invoke "as-call-first") (i32.const 12)) +(assert_return (invoke "as-call-mid") (i32.const 13)) +(assert_return (invoke "as-call-last") (i32.const 14)) + +(assert_return (invoke "as-call_indirect-first") (i32.const 20)) +(assert_return (invoke "as-call_indirect-mid") (i32.const 21)) +(assert_return (invoke "as-call_indirect-last") (i32.const 22)) +(assert_return (invoke "as-call_indirect-func") (i32.const 23)) + +(assert_return (invoke "as-local.set-value") (i32.const 17)) +(assert_return (invoke "as-local.tee-value") (i32.const 1)) +(assert_return (invoke "as-global.set-value") (i32.const 1)) + +(assert_return (invoke "as-load-address") (f32.const 1.7)) +(assert_return (invoke "as-loadN-address") (i64.const 30)) + +(assert_return (invoke "as-store-address") (i32.const 30)) +(assert_return (invoke "as-store-value") (i32.const 31)) +(assert_return (invoke "as-storeN-address") (i32.const 32)) +(assert_return (invoke "as-storeN-value") (i32.const 33)) + +(assert_return (invoke "as-unary-operand") (f32.const 3.4)) + +(assert_return (invoke "as-binary-left") (i32.const 3)) +(assert_return (invoke "as-binary-right") (i64.const 45)) + +(assert_return (invoke "as-test-operand") (i32.const 44)) + +(assert_return (invoke "as-compare-left") (i32.const 43)) +(assert_return (invoke "as-compare-right") (i32.const 42)) + +(assert_return (invoke "as-convert-operand") (i32.const 41)) + +(assert_return (invoke "as-memory.grow-size") (i32.const 40)) + +(assert_return (invoke "nested-block-value" (i32.const 0)) (i32.const 19)) +(assert_return (invoke "nested-block-value" (i32.const 1)) (i32.const 17)) +(assert_return (invoke "nested-block-value" (i32.const 2)) (i32.const 16)) +(assert_return (invoke "nested-block-value" (i32.const 10)) (i32.const 16)) +(assert_return (invoke "nested-block-value" (i32.const -1)) (i32.const 16)) +(assert_return (invoke "nested-block-value" (i32.const 100000)) (i32.const 16)) + +(assert_return (invoke "nested-br-value" (i32.const 0)) (i32.const 8)) +(assert_return (invoke "nested-br-value" (i32.const 1)) (i32.const 9)) +(assert_return (invoke "nested-br-value" (i32.const 2)) (i32.const 17)) +(assert_return (invoke "nested-br-value" (i32.const 11)) (i32.const 17)) +(assert_return (invoke "nested-br-value" (i32.const -4)) (i32.const 17)) +(assert_return (invoke "nested-br-value" (i32.const 10213210)) (i32.const 17)) + +(assert_return (invoke "nested-br_if-value" (i32.const 0)) (i32.const 17)) +(assert_return (invoke "nested-br_if-value" (i32.const 1)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value" (i32.const 2)) (i32.const 8)) +(assert_return (invoke "nested-br_if-value" (i32.const 9)) (i32.const 8)) +(assert_return (invoke "nested-br_if-value" (i32.const -9)) (i32.const 8)) +(assert_return (invoke "nested-br_if-value" (i32.const 999999)) (i32.const 8)) + +(assert_return (invoke "nested-br_if-value-cond" (i32.const 0)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const 1)) (i32.const 8)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const 2)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const 3)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const -1000000)) (i32.const 9)) +(assert_return (invoke "nested-br_if-value-cond" (i32.const 9423975)) (i32.const 9)) + +(assert_return (invoke "nested-br_table-value" (i32.const 0)) (i32.const 17)) +(assert_return (invoke "nested-br_table-value" (i32.const 1)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value" (i32.const 2)) (i32.const 8)) +(assert_return (invoke "nested-br_table-value" (i32.const 9)) (i32.const 8)) +(assert_return (invoke "nested-br_table-value" (i32.const -9)) (i32.const 8)) +(assert_return (invoke "nested-br_table-value" (i32.const 999999)) (i32.const 8)) + +(assert_return (invoke "nested-br_table-value-index" (i32.const 0)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value-index" (i32.const 1)) (i32.const 8)) +(assert_return (invoke "nested-br_table-value-index" (i32.const 2)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value-index" (i32.const 3)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value-index" (i32.const -1000000)) (i32.const 9)) +(assert_return (invoke "nested-br_table-value-index" (i32.const 9423975)) (i32.const 9)) + +(assert_return (invoke "nested-br_table-loop-block" (i32.const 1)) (i32.const 3)) + +(assert_return (invoke "meet-anyref" (i32.const 0) (ref.host 1)) (ref.host 1)) +(assert_return (invoke "meet-anyref" (i32.const 1) (ref.host 1)) (ref.host 1)) +(assert_return (invoke "meet-anyref" (i32.const 2) (ref.host 1)) (ref.host 1)) + +(assert_return (invoke "meet-funcref-1" (i32.const 0)) (ref.func)) +(assert_return (invoke "meet-funcref-1" (i32.const 1)) (ref.func)) +(assert_return (invoke "meet-funcref-1" (i32.const 2)) (ref.func)) +(assert_return (invoke "meet-funcref-2" (i32.const 0)) (ref.func)) +(assert_return (invoke "meet-funcref-2" (i32.const 1)) (ref.func)) +(assert_return (invoke "meet-funcref-2" (i32.const 2)) (ref.func)) +(assert_return (invoke "meet-funcref-3" (i32.const 0)) (ref.func)) +(assert_return (invoke "meet-funcref-3" (i32.const 1)) (ref.func)) +(assert_return (invoke "meet-funcref-3" (i32.const 2)) (ref.func)) +(assert_return (invoke "meet-funcref-4" (i32.const 0)) (ref.func)) +(assert_return (invoke "meet-funcref-4" (i32.const 1)) (ref.func)) +(assert_return (invoke "meet-funcref-4" (i32.const 2)) (ref.func)) + +(assert_invalid + (module (func $type-arg-void-vs-num (result i32) + (block (br_table 0 (i32.const 1)) (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-arg-empty-vs-num (result i32) + (block (br_table 0) (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-arg-void-vs-num (result i32) + (block (result i32) (br_table 0 (nop) (i32.const 1)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-num-vs-num (result i32) + (block (result i32) + (br_table 0 0 0 (i64.const 1) (i32.const 1)) (i32.const 1) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-num-vs-arg-num + (block + (block (result f32) + (br_table 0 1 (f32.const 0) (i32.const 0)) + ) + (drop) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-index-void-vs-i32 + (block (br_table 0 0 0 (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-index-num-vs-i32 + (block (br_table 0 (i64.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-index-void-vs-i32 (result i32) + (block (result i32) (br_table 0 0 (i32.const 0) (nop)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-void-vs-num-nested (result i32) + (block (result i32) (i32.const 0) (block (br_table 1 (i32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-arg-index-num-vs-i32 (result i32) + (block (result i32) + (br_table 0 0 (i32.const 0) (i64.const 0)) (i32.const 1) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-arg-void-vs-num (result i32) + (block (br_table 0 (i32.const 1)) (i32.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module + (func $type-arg-index-empty-in-then + (block + (i32.const 0) (i32.const 0) + (if (result i32) (then (br_table 0))) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-value-empty-in-then + (block + (i32.const 0) (i32.const 0) + (if (result i32) (then (br_table 0 (i32.const 1)))) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-index-empty-in-return + (block (result i32) + (return (br_table 0)) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-arg-value-empty-in-return + (block (result i32) + (return (br_table 0 (i32.const 1))) + ) + (i32.eqz) (drop) + ) + ) + "type mismatch" +) + +(assert_invalid + (module (func $meet-bottom (param i32) (result anyref) + (block $l1 (result anyref) + (drop + (block $l2 (result i32) + (br_table $l2 $l1 $l2 (ref.null) (local.get 0)) + ) + ) + (ref.null) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $unbound-label + (block (br_table 2 1 (i32.const 1))) + )) + "unknown label" +) +(assert_invalid + (module (func $unbound-nested-label + (block (block (br_table 0 5 (i32.const 1)))) + )) + "unknown label" +) +(assert_invalid + (module (func $large-label + (block (br_table 0 0x10000001 0 (i32.const 1))) + )) + "unknown label" +) + +(assert_invalid + (module (func $unbound-label-default + (block (br_table 1 2 (i32.const 1))) + )) + "unknown label" +) +(assert_invalid + (module (func $unbound-nested-label-default + (block (block (br_table 0 5 (i32.const 1)))) + )) + "unknown label" +) +(assert_invalid + (module (func $large-label-default + (block (br_table 0 0 0x10000001 (i32.const 1))) + )) + "unknown label" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/bulk.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/bulk.wast new file mode 100644 index 000000000..fe71939d3 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/bulk.wast @@ -0,0 +1,307 @@ +;; segment syntax +(module + (memory 1) + (data "foo")) + +(module + (table 3 funcref) + (elem funcref (ref.func 0) (ref.null) (ref.func 1)) + (func) + (func)) + +;; memory.fill +(module + (memory 1) + + (func (export "fill") (param i32 i32 i32) + (memory.fill + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0))) +) + +;; Basic fill test. +(invoke "fill" (i32.const 1) (i32.const 0xff) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0xff)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 0xff)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 0xff)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 0)) + +;; Fill value is stored as a byte. +(invoke "fill" (i32.const 0) (i32.const 0xbbaa) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0xaa)) + +;; Fill all of memory +(invoke "fill" (i32.const 0) (i32.const 0) (i32.const 0x10000)) + +;; Out-of-bounds writes trap, and nothing is written +(assert_trap (invoke "fill" (i32.const 0xff00) (i32.const 1) (i32.const 0x101)) + "out of bounds memory access") +(assert_return (invoke "load8_u" (i32.const 0xff00)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 0xffff)) (i32.const 0)) + +;; Succeed when writing 0 bytes at the end of the region. +(invoke "fill" (i32.const 0x10000) (i32.const 0) (i32.const 0)) + +;; Writing 0 bytes outside the memory traps. +(assert_trap (invoke "fill" (i32.const 0x10001) (i32.const 0) (i32.const 0)) + "out of bounds memory access") + + +;; memory.copy +(module + (memory (data "\aa\bb\cc\dd")) + + (func (export "copy") (param i32 i32 i32) + (memory.copy + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0))) +) + +;; Non-overlapping copy. +(invoke "copy" (i32.const 10) (i32.const 0) (i32.const 4)) + +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0xdd)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 0)) + +;; Overlap, source > dest +(invoke "copy" (i32.const 8) (i32.const 10) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0xdd)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0xdd)) + +;; Overlap, source < dest +(invoke "copy" (i32.const 10) (i32.const 7) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0xaa)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 0xdd)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 0)) + +;; Copy ending at memory limit is ok. +(invoke "copy" (i32.const 0xff00) (i32.const 0) (i32.const 0x100)) +(invoke "copy" (i32.const 0xfe00) (i32.const 0xff00) (i32.const 0x100)) + +;; Succeed when copying 0 bytes at the end of the region. +(invoke "copy" (i32.const 0x10000) (i32.const 0) (i32.const 0)) +(invoke "copy" (i32.const 0) (i32.const 0x10000) (i32.const 0)) + +;; Copying 0 bytes outside the memory traps. +(assert_trap (invoke "copy" (i32.const 0x10001) (i32.const 0) (i32.const 0)) + "out of bounds memory access") +(assert_trap (invoke "copy" (i32.const 0) (i32.const 0x10001) (i32.const 0)) + "out of bounds memory access") + + +;; memory.init +(module + (memory 1) + (data "\aa\bb\cc\dd") + + (func (export "init") (param i32 i32 i32) + (memory.init 0 + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0))) +) + +(invoke "init" (i32.const 0) (i32.const 1) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0xbb)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 0)) + +;; Init ending at memory limit and segment limit is ok. +(invoke "init" (i32.const 0xfffc) (i32.const 0) (i32.const 4)) + +;; Out-of-bounds writes trap, and nothing is written. +(assert_trap (invoke "init" (i32.const 0xfffe) (i32.const 0) (i32.const 3)) + "out of bounds memory access") +(assert_return (invoke "load8_u" (i32.const 0xfffe)) (i32.const 0xcc)) +(assert_return (invoke "load8_u" (i32.const 0xffff)) (i32.const 0xdd)) + +;; Succeed when writing 0 bytes at the end of either region. +(invoke "init" (i32.const 0x10000) (i32.const 0) (i32.const 0)) +(invoke "init" (i32.const 0) (i32.const 4) (i32.const 0)) + +;; Writing 0 bytes outside the memory traps. +(assert_trap (invoke "init" (i32.const 0x10001) (i32.const 0) (i32.const 0)) + "out of bounds memory access") +(assert_trap (invoke "init" (i32.const 0) (i32.const 5) (i32.const 0)) + "out of bounds memory access") + +;; data.drop +(module + (memory 1) + (data $p "x") + (data $a (memory 0) (i32.const 0) "x") + + (func (export "drop_passive") (data.drop $p)) + (func (export "init_passive") (param $len i32) + (memory.init $p (i32.const 0) (i32.const 0) (local.get $len))) + + (func (export "drop_active") (data.drop $a)) + (func (export "init_active") (param $len i32) + (memory.init $a (i32.const 0) (i32.const 0) (local.get $len))) +) + +(invoke "init_passive" (i32.const 1)) +(invoke "drop_passive") +(invoke "drop_passive") +(assert_return (invoke "init_passive" (i32.const 0))) +(assert_trap (invoke "init_passive" (i32.const 1)) "out of bounds") +(invoke "init_passive" (i32.const 0)) +(invoke "drop_active") +(assert_return (invoke "init_active" (i32.const 0))) +(assert_trap (invoke "init_active" (i32.const 1)) "out of bounds") +(invoke "init_active" (i32.const 0)) + + +;; table.init +(module + (table 3 funcref) + (elem funcref + (ref.func $zero) (ref.func $one) (ref.func $zero) (ref.func $one)) + + (func $zero (result i32) (i32.const 0)) + (func $one (result i32) (i32.const 1)) + + (func (export "init") (param i32 i32 i32) + (table.init 0 + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "call") (param i32) (result i32) + (call_indirect (result i32) + (local.get 0))) +) + +;; Out-of-bounds stores trap, and nothing is written. +(assert_trap (invoke "init" (i32.const 2) (i32.const 0) (i32.const 2)) + "out of bounds table access") +(assert_trap (invoke "call" (i32.const 2)) + "uninitialized element 2") + +(invoke "init" (i32.const 0) (i32.const 1) (i32.const 2)) +(assert_return (invoke "call" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "call" (i32.const 1)) (i32.const 0)) +(assert_trap (invoke "call" (i32.const 2)) "uninitialized element") + +;; Init ending at table limit and segment limit is ok. +(invoke "init" (i32.const 1) (i32.const 2) (i32.const 2)) + +;; Succeed when storing 0 elements at the end of either region. +(invoke "init" (i32.const 3) (i32.const 0) (i32.const 0)) +(invoke "init" (i32.const 0) (i32.const 4) (i32.const 0)) + +;; Writing 0 elements outside the table traps. +(assert_trap (invoke "init" (i32.const 4) (i32.const 0) (i32.const 0)) + "out of bounds table access") +(assert_trap (invoke "init" (i32.const 0) (i32.const 5) (i32.const 0)) + "out of bounds table access") + + +;; elem.drop +(module + (table 1 funcref) + (func $f) + (elem $p funcref (ref.func $f)) + (elem $a (table 0) (i32.const 0) func $f) + + (func (export "drop_passive") (elem.drop $p)) + (func (export "init_passive") (param $len i32) + (table.init $p (i32.const 0) (i32.const 0) (local.get $len)) + ) + + (func (export "drop_active") (elem.drop $a)) + (func (export "init_active") (param $len i32) + (table.init $a (i32.const 0) (i32.const 0) (local.get $len)) + ) +) + +(invoke "init_passive" (i32.const 1)) +(invoke "drop_passive") +(invoke "drop_passive") +(assert_return (invoke "init_passive" (i32.const 0))) +(assert_trap (invoke "init_passive" (i32.const 1)) "out of bounds") +(invoke "init_passive" (i32.const 0)) +(invoke "drop_active") +(assert_return (invoke "init_active" (i32.const 0))) +(assert_trap (invoke "init_active" (i32.const 1)) "out of bounds") +(invoke "init_active" (i32.const 0)) + + +;; table.copy +(module + (table 10 funcref) + (elem (i32.const 0) $zero $one $two) + (func $zero (result i32) (i32.const 0)) + (func $one (result i32) (i32.const 1)) + (func $two (result i32) (i32.const 2)) + + (func (export "copy") (param i32 i32 i32) + (table.copy + (local.get 0) + (local.get 1) + (local.get 2))) + + (func (export "call") (param i32) (result i32) + (call_indirect (result i32) + (local.get 0))) +) + +;; Non-overlapping copy. +(invoke "copy" (i32.const 3) (i32.const 0) (i32.const 3)) +;; Now [$zero, $one, $two, $zero, $one, $two, ...] +(assert_return (invoke "call" (i32.const 3)) (i32.const 0)) +(assert_return (invoke "call" (i32.const 4)) (i32.const 1)) +(assert_return (invoke "call" (i32.const 5)) (i32.const 2)) + +;; Overlap, source > dest +(invoke "copy" (i32.const 0) (i32.const 1) (i32.const 3)) +;; Now [$one, $two, $zero, $zero, $one, $two, ...] +(assert_return (invoke "call" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "call" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "call" (i32.const 2)) (i32.const 0)) + +;; Overlap, source < dest +(invoke "copy" (i32.const 2) (i32.const 0) (i32.const 3)) +;; Now [$one, $two, $one, $two, $zero, $two, ...] +(assert_return (invoke "call" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "call" (i32.const 3)) (i32.const 2)) +(assert_return (invoke "call" (i32.const 4)) (i32.const 0)) + +;; Copy ending at table limit is ok. +(invoke "copy" (i32.const 6) (i32.const 8) (i32.const 2)) +(invoke "copy" (i32.const 8) (i32.const 6) (i32.const 2)) + +;; Succeed when copying 0 elements at the end of the region. +(invoke "copy" (i32.const 10) (i32.const 0) (i32.const 0)) +(invoke "copy" (i32.const 0) (i32.const 10) (i32.const 0)) + +;; Fail on out-of-bounds when copying 0 elements outside of table. +(assert_trap (invoke "copy" (i32.const 11) (i32.const 0) (i32.const 0)) + "out of bounds") +(assert_trap (invoke "copy" (i32.const 0) (i32.const 11) (i32.const 0)) + "out of bounds") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/custom.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/custom.wast new file mode 100644 index 000000000..0310f76b5 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/custom.wast @@ -0,0 +1,130 @@ +(module binary + "\00asm" "\01\00\00\00" + "\00\24\10" "a custom section" "this is the payload" + "\00\20\10" "a custom section" "this is payload" + "\00\11\10" "a custom section" "" + "\00\10\00" "" "this is payload" + "\00\01\00" "" "" + "\00\24\10" "\00\00custom sectio\00" "this is the payload" + "\00\24\10" "\ef\bb\bfa custom sect" "this is the payload" + "\00\24\10" "a custom sect\e2\8c\a3" "this is the payload" + "\00\1f\16" "module within a module" "\00asm" "\01\00\00\00" +) + +(module binary + "\00asm" "\01\00\00\00" + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\01\01\00" ;; type section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\02\01\00" ;; import section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\03\01\00" ;; function section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\04\01\00" ;; table section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\05\01\00" ;; memory section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\06\01\00" ;; global section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\07\01\00" ;; export section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\09\01\00" ;; element section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\0a\01\00" ;; code section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" + "\0b\01\00" ;; data section + "\00\0e\06" "custom" "payload" + "\00\0e\06" "custom" "payload" +) + +(module binary + "\00asm" "\01\00\00\00" + "\01\07\01\60\02\7f\7f\01\7f" ;; type section + "\00\1a\06" "custom" "this is the payload" ;; custom section + "\03\02\01\00" ;; function section + "\07\0a\01\06\61\64\64\54\77\6f\00\00" ;; export section + "\0a\09\01\07\00\20\00\20\01\6a\0b" ;; code section + "\00\1b\07" "custom2" "this is the payload" ;; custom section +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00" + ) + "unexpected end" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\00" + ) + "unexpected end" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\00\00\05\01\00\07\00\00" + ) + "unexpected end" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\26\10" "a custom section" "this is the payload" + ) + "unexpected end" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\25\10" "a custom section" "this is the payload" + "\00\24\10" "a custom section" "this is the payload" + ) + "invalid section id" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\01\07\01\60\02\7f\7f\01\7f" ;; type section + "\00\25\10" "a custom section" "this is the payload" ;; invalid length! + "\03\02\01\00" ;; function section + "\0a\09\01\07\00\20\00\20\01\6a\0b" ;; code section + "\00\1b\07" "custom2" "this is the payload" ;; custom section + ) + "function and code section have inconsistent lengths" +) + +;; Test concatenated modules. +(assert_malformed + (module binary + "\00asm\01\00\00\00" + "\00asm\01\00\00\00" + ) + "length out of bounds" +) + +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\05\03\01\00\01" ;; memory section + "\0c\01\02" ;; data count section (2 segments) + "\0b\06\01\00\41\00\0b\00" ;; data section (1 segment) + ) + "data count and data section have inconsistent lengths" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/data.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/data.wast new file mode 100644 index 000000000..aabe1021b --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/data.wast @@ -0,0 +1,345 @@ +;; Test the data section + +;; Syntax + +(module + (memory $m 1) + (data (i32.const 0)) + (data (i32.const 1) "a" "" "bcd") + (data (offset (i32.const 0))) + (data (offset (i32.const 0)) "" "a" "bc" "") + (data (memory 0) (i32.const 0)) + (data (memory 0x0) (i32.const 1) "a" "" "bcd") + (data (memory 0x000) (offset (i32.const 0))) + (data (memory 0) (offset (i32.const 0)) "" "a" "bc" "") + (data (memory $m) (i32.const 0)) + (data (memory $m) (i32.const 1) "a" "" "bcd") + (data (memory $m) (offset (i32.const 0))) + (data (memory $m) (offset (i32.const 0)) "" "a" "bc" "") + (data $d1 (i32.const 0)) + (data $d2 (i32.const 1) "a" "" "bcd") + (data $d3 (offset (i32.const 0))) + (data $d4 (offset (i32.const 0)) "" "a" "bc" "") + (data $d5 (memory 0) (i32.const 0)) + (data $d6 (memory 0x0) (i32.const 1) "a" "" "bcd") + (data $d7 (memory 0x000) (offset (i32.const 0))) + (data $d8 (memory 0) (offset (i32.const 0)) "" "a" "bc" "") + (data $d9 (memory $m) (i32.const 0)) + (data $d10 (memory $m) (i32.const 1) "a" "" "bcd") + (data $d11 (memory $m) (offset (i32.const 0))) + (data $d12 (memory $m) (offset (i32.const 0)) "" "a" "bc" "") +) + +;; Basic use + +(module + (memory 1) + (data (i32.const 0) "a") +) +(module + (import "spectest" "memory" (memory 1)) + (data (i32.const 0) "a") +) + +(module + (memory 1) + (data (i32.const 0) "a") + (data (i32.const 3) "b") + (data (i32.const 100) "cde") + (data (i32.const 5) "x") + (data (i32.const 3) "c") +) +(module + (import "spectest" "memory" (memory 1)) + (data (i32.const 0) "a") + (data (i32.const 1) "b") + (data (i32.const 2) "cde") + (data (i32.const 3) "f") + (data (i32.const 2) "g") + (data (i32.const 1) "h") +) + +(module + (global (import "spectest" "global_i32") i32) + (memory 1) + (data (global.get 0) "a") +) +(module + (global (import "spectest" "global_i32") i32) + (import "spectest" "memory" (memory 1)) + (data (global.get 0) "a") +) + +(module + (global $g (import "spectest" "global_i32") i32) + (memory 1) + (data (global.get $g) "a") +) +(module + (global $g (import "spectest" "global_i32") i32) + (import "spectest" "memory" (memory 1)) + (data (global.get $g) "a") +) + +;; Use of internal globals in constant expressions is not allowed in MVP. +;; (module (memory 1) (data (global.get 0) "a") (global i32 (i32.const 0))) +;; (module (memory 1) (data (global.get $g) "a") (global $g i32 (i32.const 0))) + +;; Corner cases + +(module + (memory 1) + (data (i32.const 0) "a") + (data (i32.const 0xffff) "b") +) +(module + (import "spectest" "memory" (memory 1)) + (data (i32.const 0) "a") + (data (i32.const 0xffff) "b") +) + +(module + (memory 2) + (data (i32.const 0x1_ffff) "a") +) + +(module + (memory 0) + (data (i32.const 0)) +) +(module + (import "spectest" "memory" (memory 0)) + (data (i32.const 0)) +) + +(module + (memory 0 0) + (data (i32.const 0)) +) + +(module + (memory 1) + (data (i32.const 0x1_0000) "") +) + +(module + (memory 0) + (data (i32.const 0) "" "") +) +(module + (import "spectest" "memory" (memory 0)) + (data (i32.const 0) "" "") +) + +(module + (memory 0 0) + (data (i32.const 0) "" "") +) + +(module + (import "spectest" "memory" (memory 0)) + (data (i32.const 0) "a") +) + +(module + (import "spectest" "memory" (memory 0 3)) + (data (i32.const 0) "a") +) + +(module + (global (import "spectest" "global_i32") i32) + (import "spectest" "memory" (memory 0)) + (data (global.get 0) "a") +) + +(module + (global (import "spectest" "global_i32") i32) + (import "spectest" "memory" (memory 0 3)) + (data (global.get 0) "a") +) + +(module + (import "spectest" "memory" (memory 0)) + (data (i32.const 1) "a") +) + +(module + (import "spectest" "memory" (memory 0 3)) + (data (i32.const 1) "a") +) + +;; Invalid bounds for data + +(assert_trap + (module + (memory 0) + (data (i32.const 0) "a") + ) + "out of bounds" +) + +(assert_trap + (module + (memory 0 0) + (data (i32.const 0) "a") + ) + "out of bounds" +) + +(assert_trap + (module + (memory 0 1) + (data (i32.const 0) "a") + ) + "out of bounds" +) +(assert_trap + (module + (memory 0) + (data (i32.const 1)) + ) + "out of bounds" +) +(assert_trap + (module + (memory 0 1) + (data (i32.const 1)) + ) + "out of bounds" +) + +;; This seems to cause a time-out on Travis. +(;assert_unlinkable + (module + (memory 0x10000) + (data (i32.const 0xffffffff) "ab") + ) + "" ;; either out of memory or out of bounds +;) + +(assert_trap + (module + (global (import "spectest" "global_i32") i32) + (memory 0) + (data (global.get 0) "a") + ) + "out of bounds" +) + +(assert_trap + (module + (memory 1 2) + (data (i32.const 0x1_0000) "a") + ) + "out of bounds" +) +(assert_trap + (module + (import "spectest" "memory" (memory 1)) + (data (i32.const 0x1_0000) "a") + ) + "out of bounds" +) + +(assert_trap + (module + (memory 2) + (data (i32.const 0x2_0000) "a") + ) + "out of bounds" +) + +(assert_trap + (module + (memory 2 3) + (data (i32.const 0x2_0000) "a") + ) + "out of bounds" +) + +(assert_trap + (module + (memory 1) + (data (i32.const -1) "a") + ) + "out of bounds" +) +(assert_trap + (module + (import "spectest" "memory" (memory 1)) + (data (i32.const -1) "a") + ) + "out of bounds" +) + +(assert_trap + (module + (memory 2) + (data (i32.const -100) "a") + ) + "out of bounds" +) +(assert_trap + (module + (import "spectest" "memory" (memory 1)) + (data (i32.const -100) "a") + ) + "out of bounds" +) + +;; Data without memory + +(assert_invalid + (module + (data (i32.const 0) "") + ) + "unknown memory" +) + +;; Invalid offsets + +(assert_invalid + (module + (memory 1) + (data (i64.const 0)) + ) + "type mismatch" +) + +(assert_invalid + (module + (memory 1) + (data (i32.ctz (i32.const 0))) + ) + "constant expression required" +) + +(assert_invalid + (module + (memory 1) + (data (nop)) + ) + "constant expression required" +) + +(assert_invalid + (module + (memory 1) + (data (offset (nop) (i32.const 0))) + ) + "constant expression required" +) + +(assert_invalid + (module + (memory 1) + (data (offset (i32.const 0) (nop))) + ) + "constant expression required" +) + +;; Use of internal globals in constant expressions is not allowed in MVP. +;; (assert_invalid +;; (module (memory 1) (data (global.get $g)) (global $g (mut i32) (i32.const 0))) +;; "constant expression required" +;; ) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/elem.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/elem.wast new file mode 100644 index 000000000..6661c9a89 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/elem.wast @@ -0,0 +1,468 @@ +;; Test the element section + +;; Syntax +(module + (table $t 10 funcref) + (func $f) + (func $g) + + ;; Passive + (elem funcref) + (elem funcref (ref.func $f) (item ref.func $f) (item (ref.null)) (ref.func $g)) + (elem func) + (elem func $f $f $g $g) + + (elem $p1 funcref) + (elem $p2 funcref (ref.func $f) (ref.func $f) (ref.null) (ref.func $g)) + (elem $p3 func) + (elem $p4 func $f $f $g $g) + + ;; Active + (elem (table $t) (i32.const 0) funcref) + (elem (table $t) (i32.const 0) funcref (ref.func $f) (ref.null)) + (elem (table $t) (i32.const 0) func) + (elem (table $t) (i32.const 0) func $f $g) + (elem (table $t) (offset (i32.const 0)) funcref) + (elem (table $t) (offset (i32.const 0)) func $f $g) + (elem (table 0) (i32.const 0) func) + (elem (table 0x0) (i32.const 0) func $f $f) + (elem (table 0x000) (offset (i32.const 0)) func) + (elem (table 0) (offset (i32.const 0)) func $f $f) + (elem (table $t) (i32.const 0) func) + (elem (table $t) (i32.const 0) func $f $f) + (elem (table $t) (offset (i32.const 0)) func) + (elem (table $t) (offset (i32.const 0)) func $f $f) + (elem (offset (i32.const 0))) + (elem (offset (i32.const 0)) funcref (ref.func $f) (ref.null)) + (elem (offset (i32.const 0)) func $f $f) + (elem (offset (i32.const 0)) $f $f) + (elem (i32.const 0)) + (elem (i32.const 0) funcref (ref.func $f) (ref.null)) + (elem (i32.const 0) func $f $f) + (elem (i32.const 0) $f $f) + + (elem $a1 (table $t) (i32.const 0) funcref) + (elem $a2 (table $t) (i32.const 0) funcref (ref.func $f) (ref.null)) + (elem $a3 (table $t) (i32.const 0) func) + (elem $a4 (table $t) (i32.const 0) func $f $g) + (elem $a9 (table $t) (offset (i32.const 0)) funcref) + (elem $a10 (table $t) (offset (i32.const 0)) func $f $g) + (elem $a11 (table 0) (i32.const 0) func) + (elem $a12 (table 0x0) (i32.const 0) func $f $f) + (elem $a13 (table 0x000) (offset (i32.const 0)) func) + (elem $a14 (table 0) (offset (i32.const 0)) func $f $f) + (elem $a15 (table $t) (i32.const 0) func) + (elem $a16 (table $t) (i32.const 0) func $f $f) + (elem $a17 (table $t) (offset (i32.const 0)) func) + (elem $a18 (table $t) (offset (i32.const 0)) func $f $f) + (elem $a19 (offset (i32.const 0))) + (elem $a20 (offset (i32.const 0)) funcref (ref.func $f) (ref.null)) + (elem $a21 (offset (i32.const 0)) func $f $f) + (elem $a22 (offset (i32.const 0)) $f $f) + (elem $a23 (i32.const 0)) + (elem $a24 (i32.const 0) funcref (ref.func $f) (ref.null)) + (elem $a25 (i32.const 0) func $f $f) + (elem $a26 (i32.const 0) $f $f) + + ;; Declarative + (elem declare funcref) + (elem declare funcref (ref.func $f) (ref.func $f) (ref.null) (ref.func $g)) + (elem declare func) + (elem declare func $f $f $g $g) + + (elem $d1 declare funcref) + (elem $d2 declare funcref (ref.func $f) (ref.func $f) (ref.null) (ref.func $g)) + (elem $d3 declare func) + (elem $d4 declare func $f $f $g $g) +) + +(module + (func $f) + (func $g) + + (table $t funcref (elem (ref.func $f) (ref.null) (ref.func $g))) +) + + +;; Basic use + +(module + (table 10 funcref) + (func $f) + (elem (i32.const 0) $f) +) +(module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 0) $f) +) + +(module + (table 10 funcref) + (func $f) + (elem (i32.const 0) $f) + (elem (i32.const 3) $f) + (elem (i32.const 7) $f) + (elem (i32.const 5) $f) + (elem (i32.const 3) $f) +) +(module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 9) $f) + (elem (i32.const 3) $f) + (elem (i32.const 7) $f) + (elem (i32.const 3) $f) + (elem (i32.const 5) $f) +) + +(module + (global (import "spectest" "global_i32") i32) + (table 1000 funcref) + (func $f) + (elem (global.get 0) $f) +) + +(module + (global $g (import "spectest" "global_i32") i32) + (table 1000 funcref) + (func $f) + (elem (global.get $g) $f) +) + +(module + (type $out-i32 (func (result i32))) + (table 10 funcref) + (elem (i32.const 7) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-7") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 7)) + ) + (func (export "call-9") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) +(assert_return (invoke "call-7") (i32.const 65)) +(assert_return (invoke "call-9") (i32.const 66)) + +;; Corner cases + +(module + (table 10 funcref) + (func $f) + (elem (i32.const 9) $f) +) +(module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 9) $f) +) + +(module + (table 0 funcref) + (elem (i32.const 0)) +) +(module + (import "spectest" "table" (table 0 funcref)) + (elem (i32.const 0)) +) + +(module + (table 0 0 funcref) + (elem (i32.const 0)) +) + +(module + (table 20 funcref) + (elem (i32.const 20)) +) + +(module + (import "spectest" "table" (table 0 funcref)) + (func $f) + (elem (i32.const 0) $f) +) + +(module + (import "spectest" "table" (table 0 100 funcref)) + (func $f) + (elem (i32.const 0) $f) +) + +(module + (import "spectest" "table" (table 0 funcref)) + (func $f) + (elem (i32.const 1) $f) +) + +(module + (import "spectest" "table" (table 0 30 funcref)) + (func $f) + (elem (i32.const 1) $f) +) + +;; Invalid bounds for elements + +(assert_trap + (module + (table 0 funcref) + (func $f) + (elem (i32.const 0) $f) + ) + "out of bounds" +) + +(assert_trap + (module + (table 0 0 funcref) + (func $f) + (elem (i32.const 0) $f) + ) + "out of bounds" +) + +(assert_trap + (module + (table 0 1 funcref) + (func $f) + (elem (i32.const 0) $f) + ) + "out of bounds" +) + +(assert_trap + (module + (table 0 funcref) + (elem (i32.const 1)) + ) + "out of bounds" +) +(assert_trap + (module + (table 10 funcref) + (func $f) + (elem (i32.const 10) $f) + ) + "out of bounds" +) +(assert_trap + (module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 10) $f) + ) + "out of bounds" +) + +(assert_trap + (module + (table 10 20 funcref) + (func $f) + (elem (i32.const 10) $f) + ) + "out of bounds" +) +(assert_trap + (module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const 10) $f) + ) + "out of bounds" +) + +(assert_trap + (module + (table 10 funcref) + (func $f) + (elem (i32.const -1) $f) + ) + "out of bounds" +) +(assert_trap + (module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const -1) $f) + ) + "out of bounds" +) + +(assert_trap + (module + (table 10 funcref) + (func $f) + (elem (i32.const -10) $f) + ) + "out of bounds" +) +(assert_trap + (module + (import "spectest" "table" (table 10 funcref)) + (func $f) + (elem (i32.const -10) $f) + ) + "out of bounds" +) + +;; Implicitly dropped elements + +(module + (table 10 funcref) + (elem $e (i32.const 0) func $f) + (func $f) + (func (export "init") + (table.init $e (i32.const 0) (i32.const 0) (i32.const 1)) + ) +) +(assert_trap (invoke "init") "out of bounds") + +(module + (table 10 funcref) + (elem $e declare func $f) + (func $f) + (func (export "init") + (table.init $e (i32.const 0) (i32.const 0) (i32.const 1)) + ) +) +(assert_trap (invoke "init") "out of bounds") + +;; Element without table + +(assert_invalid + (module + (func $f) + (elem (i32.const 0) $f) + ) + "unknown table" +) + +;; Invalid offsets + +(assert_invalid + (module + (table 1 funcref) + (elem (i64.const 0)) + ) + "type mismatch" +) + +(assert_invalid + (module + (table 1 funcref) + (elem (i32.ctz (i32.const 0))) + ) + "constant expression required" +) + +(assert_invalid + (module + (table 1 funcref) + (elem (nop)) + ) + "constant expression required" +) + +(assert_invalid + (module + (table 1 funcref) + (elem (offset (nop) (i32.const 0))) + ) + "constant expression required" +) + +(assert_invalid + (module + (table 1 funcref) + (elem (offset (i32.const 0) (nop))) + ) + "constant expression required" +) + +;; Use of internal globals in constant expressions is not allowed in MVP. +;; (assert_invalid +;; (module (memory 1) (data (global.get $g)) (global $g (mut i32) (i32.const 0))) +;; "constant expression required" +;; ) + +;; Two elements target the same slot + +(module + (type $out-i32 (func (result i32))) + (table 10 funcref) + (elem (i32.const 9) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-overwritten") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) +(assert_return (invoke "call-overwritten") (i32.const 66)) + +(module + (type $out-i32 (func (result i32))) + (import "spectest" "table" (table 10 funcref)) + (elem (i32.const 9) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-overwritten-element") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) +(assert_return (invoke "call-overwritten-element") (i32.const 66)) + +;; Element sections across multiple modules change the same table + +(module $module1 + (type $out-i32 (func (result i32))) + (table (export "shared-table") 10 funcref) + (elem (i32.const 8) $const-i32-a) + (elem (i32.const 9) $const-i32-b) + (func $const-i32-a (type $out-i32) (i32.const 65)) + (func $const-i32-b (type $out-i32) (i32.const 66)) + (func (export "call-7") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 7)) + ) + (func (export "call-8") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 8)) + ) + (func (export "call-9") (type $out-i32) + (call_indirect (type $out-i32) (i32.const 9)) + ) +) + +(register "module1" $module1) + +(assert_trap (invoke $module1 "call-7") "uninitialized element") +(assert_return (invoke $module1 "call-8") (i32.const 65)) +(assert_return (invoke $module1 "call-9") (i32.const 66)) + +(module $module2 + (type $out-i32 (func (result i32))) + (import "module1" "shared-table" (table 10 funcref)) + (elem (i32.const 7) $const-i32-c) + (elem (i32.const 8) $const-i32-d) + (func $const-i32-c (type $out-i32) (i32.const 67)) + (func $const-i32-d (type $out-i32) (i32.const 68)) +) + +(assert_return (invoke $module1 "call-7") (i32.const 67)) +(assert_return (invoke $module1 "call-8") (i32.const 68)) +(assert_return (invoke $module1 "call-9") (i32.const 66)) + +(module $module3 + (type $out-i32 (func (result i32))) + (import "module1" "shared-table" (table 10 funcref)) + (elem (i32.const 8) $const-i32-e) + (elem (i32.const 9) $const-i32-f) + (func $const-i32-e (type $out-i32) (i32.const 69)) + (func $const-i32-f (type $out-i32) (i32.const 70)) +) + +(assert_return (invoke $module1 "call-7") (i32.const 67)) +(assert_return (invoke $module1 "call-8") (i32.const 69)) +(assert_return (invoke $module1 "call-9") (i32.const 70)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/exports.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/exports.wast new file mode 100644 index 000000000..a099172ac --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/exports.wast @@ -0,0 +1,196 @@ +;; Functions + +(module (func) (export "a" (func 0))) +(module (func) (export "a" (func 0)) (export "b" (func 0))) +(module (func) (func) (export "a" (func 0)) (export "b" (func 1))) + +(module (func (export "a"))) +(module (func (export "a") (export "b") (export "c"))) +(module (func (export "a") (export "b") (param i32))) +(module (func) (export "a" (func 0))) +(module (func $a (export "a"))) +(module (func $a) (export "a" (func $a))) +(module (export "a" (func 0)) (func)) +(module (export "a" (func $a)) (func $a)) + +(module $Func + (export "e" (func $f)) + (func $f (param $n i32) (result i32) + (return (i32.add (local.get $n) (i32.const 1))) + ) +) +(assert_return (invoke "e" (i32.const 42)) (i32.const 43)) +(assert_return (invoke $Func "e" (i32.const 42)) (i32.const 43)) +(module) +(module $Other1) +(assert_return (invoke $Func "e" (i32.const 42)) (i32.const 43)) + +(assert_invalid + (module (func) (export "a" (func 1))) + "unknown function" +) +(assert_invalid + (module (func) (export "a" (func 0)) (export "a" (func 0))) + "duplicate export name" +) +(assert_invalid + (module (func) (func) (export "a" (func 0)) (export "a" (func 1))) + "duplicate export name" +) +(assert_invalid + (module (func) (global i32 (i32.const 0)) (export "a" (func 0)) (export "a" (global 0))) + "duplicate export name" +) +(assert_invalid + (module (func) (table 0 funcref) (export "a" (func 0)) (export "a" (table 0))) + "duplicate export name" +) +(assert_invalid + (module (func) (memory 0) (export "a" (func 0)) (export "a" (memory 0))) + "duplicate export name" +) + + +;; Globals + +(module (global i32 (i32.const 0)) (export "a" (global 0))) +(module (global i32 (i32.const 0)) (export "a" (global 0)) (export "b" (global 0))) +(module (global i32 (i32.const 0)) (global i32 (i32.const 0)) (export "a" (global 0)) (export "b" (global 1))) + +(module (global (export "a") i32 (i32.const 0))) +(module (global i32 (i32.const 0)) (export "a" (global 0))) +(module (global $a (export "a") i32 (i32.const 0))) +(module (global $a i32 (i32.const 0)) (export "a" (global $a))) +(module (export "a" (global 0)) (global i32 (i32.const 0))) +(module (export "a" (global $a)) (global $a i32 (i32.const 0))) + +(module $Global + (export "e" (global $g)) + (global $g i32 (i32.const 42)) +) +(assert_return (get "e") (i32.const 42)) +(assert_return (get $Global "e") (i32.const 42)) +(module) +(module $Other2) +(assert_return (get $Global "e") (i32.const 42)) + +(assert_invalid + (module (global i32 (i32.const 0)) (export "a" (global 1))) + "unknown global" +) +(assert_invalid + (module (global i32 (i32.const 0)) (export "a" (global 0)) (export "a" (global 0))) + "duplicate export name" +) +(assert_invalid + (module (global i32 (i32.const 0)) (global i32 (i32.const 0)) (export "a" (global 0)) (export "a" (global 1))) + "duplicate export name" +) +(assert_invalid + (module (global i32 (i32.const 0)) (func) (export "a" (global 0)) (export "a" (func 0))) + "duplicate export name" +) +(assert_invalid + (module (global i32 (i32.const 0)) (table 0 funcref) (export "a" (global 0)) (export "a" (table 0))) + "duplicate export name" +) +(assert_invalid + (module (global i32 (i32.const 0)) (memory 0) (export "a" (global 0)) (export "a" (memory 0))) + "duplicate export name" +) + + +;; Tables + +(module (table 0 funcref) (export "a" (table 0))) +(module (table 0 funcref) (export "a" (table 0)) (export "b" (table 0))) +(module (table 0 funcref) (table 0 funcref) (export "a" (table 0)) (export "b" (table 1))) + +(module (table (export "a") 0 funcref)) +(module (table (export "a") 0 1 funcref)) +(module (table 0 funcref) (export "a" (table 0))) +(module (table 0 1 funcref) (export "a" (table 0))) +(module (table $a (export "a") 0 funcref)) +(module (table $a (export "a") 0 1 funcref)) +(module (table $a 0 funcref) (export "a" (table $a))) +(module (table $a 0 1 funcref) (export "a" (table $a))) +(module (export "a" (table 0)) (table 0 funcref)) +(module (export "a" (table 0)) (table 0 1 funcref)) +(module (export "a" (table $a)) (table $a 0 funcref)) +(module (export "a" (table $a)) (table $a 0 1 funcref)) + +(; TODO: access table ;) + +(assert_invalid + (module (table 0 funcref) (export "a" (table 1))) + "unknown table" +) +(assert_invalid + (module (table 0 funcref) (export "a" (table 0)) (export "a" (table 0))) + "duplicate export name" +) +(assert_invalid + (module (table 0 funcref) (table 0 funcref) (export "a" (table 0)) (export "a" (table 1))) + "duplicate export name" +) +(assert_invalid + (module (table 0 funcref) (func) (export "a" (table 0)) (export "a" (func 0))) + "duplicate export name" +) +(assert_invalid + (module (table 0 funcref) (global i32 (i32.const 0)) (export "a" (table 0)) (export "a" (global 0))) + "duplicate export name" +) +(assert_invalid + (module (table 0 funcref) (memory 0) (export "a" (table 0)) (export "a" (memory 0))) + "duplicate export name" +) + + +;; Memories + +(module (memory 0) (export "a" (memory 0))) +(module (memory 0) (export "a" (memory 0)) (export "b" (memory 0))) +;; No multiple memories yet. +;; (module (memory 0) (memory 0) (export "a" (memory 0)) (export "b" (memory 1))) + +(module (memory (export "a") 0)) +(module (memory (export "a") 0 1)) +(module (memory 0) (export "a" (memory 0))) +(module (memory 0 1) (export "a" (memory 0))) +(module (memory $a (export "a") 0)) +(module (memory $a (export "a") 0 1)) +(module (memory $a 0) (export "a" (memory $a))) +(module (memory $a 0 1) (export "a" (memory $a))) +(module (export "a" (memory 0)) (memory 0)) +(module (export "a" (memory 0)) (memory 0 1)) +(module (export "a" (memory $a)) (memory $a 0)) +(module (export "a" (memory $a)) (memory $a 0 1)) + +(; TODO: access memory ;) + +(assert_invalid + (module (memory 0) (export "a" (memory 1))) + "unknown memory" +) +(assert_invalid + (module (memory 0) (export "a" (memory 0)) (export "a" (memory 0))) + "duplicate export name" +) +;; No multiple memories yet. +;; (assert_invalid +;; (module (memory 0) (memory 0) (export "a" (memory 0)) (export "a" (memory 1))) +;; "duplicate export name" +;; ) +(assert_invalid + (module (memory 0) (func) (export "a" (memory 0)) (export "a" (func 0))) + "duplicate export name" +) +(assert_invalid + (module (memory 0) (global i32 (i32.const 0)) (export "a" (memory 0)) (export "a" (global 0))) + "duplicate export name" +) +(assert_invalid + (module (memory 0) (table 0 funcref) (export "a" (memory 0)) (export "a" (table 0))) + "duplicate export name" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/globals.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/globals.wast new file mode 100644 index 000000000..63aabb934 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/globals.wast @@ -0,0 +1,492 @@ +;; Test globals + +(module + (global $a i32 (i32.const -2)) + (global (;1;) f32 (f32.const -3)) + (global (;2;) f64 (f64.const -4)) + (global $b i64 (i64.const -5)) + + (global $x (mut i32) (i32.const -12)) + (global (;5;) (mut f32) (f32.const -13)) + (global (;6;) (mut f64) (f64.const -14)) + (global $y (mut i64) (i64.const -15)) + + (global $r anyref (ref.null)) + (global funcref (ref.null)) + + (func (export "get-a") (result i32) (global.get $a)) + (func (export "get-b") (result i64) (global.get $b)) + (func (export "get-r") (result anyref) (global.get $r)) + (func (export "get-x") (result i32) (global.get $x)) + (func (export "get-y") (result i64) (global.get $y)) + (func (export "set-x") (param i32) (global.set $x (local.get 0))) + (func (export "set-y") (param i64) (global.set $y (local.get 0))) + + (func (export "get-1") (result f32) (global.get 1)) + (func (export "get-2") (result f64) (global.get 2)) + (func (export "get-5") (result f32) (global.get 5)) + (func (export "get-6") (result f64) (global.get 6)) + (func (export "set-5") (param f32) (global.set 5 (local.get 0))) + (func (export "set-6") (param f64) (global.set 6 (local.get 0))) + + ;; As the argument of control constructs and instructions + + (memory 1) + + (func $dummy) + + (func (export "as-select-first") (result i32) + (select (global.get $x) (i32.const 2) (i32.const 3)) + ) + (func (export "as-select-mid") (result i32) + (select (i32.const 2) (global.get $x) (i32.const 3)) + ) + (func (export "as-select-last") (result i32) + (select (i32.const 2) (i32.const 3) (global.get $x)) + ) + + (func (export "as-loop-first") (result i32) + (loop (result i32) + (global.get $x) (call $dummy) (call $dummy) + ) + ) + (func (export "as-loop-mid") (result i32) + (loop (result i32) + (call $dummy) (global.get $x) (call $dummy) + ) + ) + (func (export "as-loop-last") (result i32) + (loop (result i32) + (call $dummy) (call $dummy) (global.get $x) + ) + ) + + (func (export "as-if-condition") (result i32) + (if (result i32) (global.get $x) + (then (call $dummy) (i32.const 2)) + (else (call $dummy) (i32.const 3)) + ) + ) + (func (export "as-if-then") (result i32) + (if (result i32) (i32.const 1) + (then (global.get $x)) (else (i32.const 2)) + ) + ) + (func (export "as-if-else") (result i32) + (if (result i32) (i32.const 0) + (then (i32.const 2)) (else (global.get $x)) + ) + ) + + (func (export "as-br_if-first") (result i32) + (block (result i32) + (br_if 0 (global.get $x) (i32.const 2)) + (return (i32.const 3)) + ) + ) + (func (export "as-br_if-last") (result i32) + (block (result i32) + (br_if 0 (i32.const 2) (global.get $x)) + (return (i32.const 3)) + ) + ) + + (func (export "as-br_table-first") (result i32) + (block (result i32) + (global.get $x) (i32.const 2) (br_table 0 0) + ) + ) + (func (export "as-br_table-last") (result i32) + (block (result i32) + (i32.const 2) (global.get $x) (br_table 0 0) + ) + ) + + (func $func (param i32 i32) (result i32) (local.get 0)) + (type $check (func (param i32 i32) (result i32))) + (table funcref (elem $func)) + (func (export "as-call_indirect-first") (result i32) + (block (result i32) + (call_indirect (type $check) + (global.get $x) (i32.const 2) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 2) (global.get $x) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (block (result i32) + (call_indirect (type $check) + (i32.const 2) (i32.const 0) (global.get $x) + ) + ) + ) + + (func (export "as-store-first") + (global.get $x) (i32.const 1) (i32.store) + ) + (func (export "as-store-last") + (i32.const 0) (global.get $x) (i32.store) + ) + (func (export "as-load-operand") (result i32) + (i32.load (global.get $x)) + ) + (func (export "as-memory.grow-value") (result i32) + (memory.grow (global.get $x)) + ) + + (func $f (param i32) (result i32) (local.get 0)) + (func (export "as-call-value") (result i32) + (call $f (global.get $x)) + ) + + (func (export "as-return-value") (result i32) + (global.get $x) (return) + ) + (func (export "as-drop-operand") + (drop (global.get $x)) + ) + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (global.get $x))) + ) + + (func (export "as-local.set-value") (param i32) (result i32) + (local.set 0 (global.get $x)) + (local.get 0) + ) + (func (export "as-local.tee-value") (param i32) (result i32) + (local.tee 0 (global.get $x)) + ) + (func (export "as-global.set-value") (result i32) + (global.set $x (global.get $x)) + (global.get $x) + ) + + (func (export "as-unary-operand") (result i32) + (i32.eqz (global.get $x)) + ) + (func (export "as-binary-operand") (result i32) + (i32.mul + (global.get $x) (global.get $x) + ) + ) + (func (export "as-compare-operand") (result i32) + (i32.gt_u + (global.get 0) (i32.const 1) + ) + ) +) + +(assert_return (invoke "get-a") (i32.const -2)) +(assert_return (invoke "get-b") (i64.const -5)) +(assert_return (invoke "get-r") (ref.null)) +(assert_return (invoke "get-x") (i32.const -12)) +(assert_return (invoke "get-y") (i64.const -15)) + +(assert_return (invoke "get-1") (f32.const -3)) +(assert_return (invoke "get-2") (f64.const -4)) +(assert_return (invoke "get-5") (f32.const -13)) +(assert_return (invoke "get-6") (f64.const -14)) + +(assert_return (invoke "set-x" (i32.const 6))) +(assert_return (invoke "set-y" (i64.const 7))) +(assert_return (invoke "set-5" (f32.const 8))) +(assert_return (invoke "set-6" (f64.const 9))) + +(assert_return (invoke "get-x") (i32.const 6)) +(assert_return (invoke "get-y") (i64.const 7)) +(assert_return (invoke "get-5") (f32.const 8)) +(assert_return (invoke "get-6") (f64.const 9)) + +(assert_return (invoke "as-select-first") (i32.const 6)) +(assert_return (invoke "as-select-mid") (i32.const 2)) +(assert_return (invoke "as-select-last") (i32.const 2)) + +(assert_return (invoke "as-loop-first") (i32.const 6)) +(assert_return (invoke "as-loop-mid") (i32.const 6)) +(assert_return (invoke "as-loop-last") (i32.const 6)) + +(assert_return (invoke "as-if-condition") (i32.const 2)) +(assert_return (invoke "as-if-then") (i32.const 6)) +(assert_return (invoke "as-if-else") (i32.const 6)) + +(assert_return (invoke "as-br_if-first") (i32.const 6)) +(assert_return (invoke "as-br_if-last") (i32.const 2)) + +(assert_return (invoke "as-br_table-first") (i32.const 6)) +(assert_return (invoke "as-br_table-last") (i32.const 2)) + +(assert_return (invoke "as-call_indirect-first") (i32.const 6)) +(assert_return (invoke "as-call_indirect-mid") (i32.const 2)) +(assert_trap (invoke "as-call_indirect-last") "undefined element") + +(assert_return (invoke "as-store-first")) +(assert_return (invoke "as-store-last")) +(assert_return (invoke "as-load-operand") (i32.const 1)) +(assert_return (invoke "as-memory.grow-value") (i32.const 1)) + +(assert_return (invoke "as-call-value") (i32.const 6)) + +(assert_return (invoke "as-return-value") (i32.const 6)) +(assert_return (invoke "as-drop-operand")) +(assert_return (invoke "as-br-value") (i32.const 6)) + +(assert_return (invoke "as-local.set-value" (i32.const 1)) (i32.const 6)) +(assert_return (invoke "as-local.tee-value" (i32.const 1)) (i32.const 6)) +(assert_return (invoke "as-global.set-value") (i32.const 6)) + +(assert_return (invoke "as-unary-operand") (i32.const 0)) +(assert_return (invoke "as-binary-operand") (i32.const 36)) +(assert_return (invoke "as-compare-operand") (i32.const 1)) + +(assert_invalid + (module (global f32 (f32.const 0)) (func (global.set 0 (f32.const 1)))) + "global is immutable" +) + +;; mutable globals can be exported +(module (global (mut f32) (f32.const 0)) (export "a" (global 0))) +(module (global (export "a") (mut f32) (f32.const 0))) + +(assert_invalid + (module (global f32 (f32.neg (f32.const 0)))) + "constant expression required" +) + +(assert_invalid + (module (global f32 (local.get 0))) + "constant expression required" +) + +(assert_invalid + (module (global f32 (f32.neg (f32.const 1)))) + "constant expression required" +) + +(assert_invalid + (module (global i32 (i32.const 0) (nop))) + "constant expression required" +) + +(assert_invalid + (module (global i32 (nop))) + "constant expression required" +) + +(assert_invalid + (module (global i32 (f32.const 0))) + "type mismatch" +) + +(assert_invalid + (module (global i32 (i32.const 0) (i32.const 0))) + "type mismatch" +) + +(assert_invalid + (module (global i32 (;empty instruction sequence;))) + "type mismatch" +) + +(assert_invalid + (module (global (import "" "") anyref) (global funcref (global.get 0))) + "type mismatch" +) + +(assert_invalid + (module (global i32 (global.get 0))) + "unknown global" +) + +(assert_invalid + (module (global i32 (global.get 1)) (global i32 (i32.const 0))) + "unknown global" +) + +(module + (import "spectest" "global_i32" (global i32)) +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\98\80\80\80\00" ;; import section + "\01" ;; length 1 + "\08\73\70\65\63\74\65\73\74" ;; "spectest" + "\0a\67\6c\6f\62\61\6c\5f\69\33\32" ;; "global_i32" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\02" ;; invalid mutability + ) + "invalid mutability" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\98\80\80\80\00" ;; import section + "\01" ;; length 1 + "\08\73\70\65\63\74\65\73\74" ;; "spectest" + "\0a\67\6c\6f\62\61\6c\5f\69\33\32" ;; "global_i32" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\ff" ;; invalid mutability + ) + "invalid mutability" +) + +(module + (global i32 (i32.const 0)) +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\86\80\80\80\00" ;; global section + "\01" ;; length 1 + "\7f" ;; i32 + "\02" ;; invalid mutability + "\41\00" ;; i32.const 0 + "\0b" ;; end + ) + "invalid mutability" +) +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\06\86\80\80\80\00" ;; global section + "\01" ;; length 1 + "\7f" ;; i32 + "\ff" ;; invalid mutability + "\41\00" ;; i32.const 0 + "\0b" ;; end + ) + "invalid mutability" +) + + +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty + (global.set $x) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-block + (i32.const 0) + (block (global.set $x)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-loop + (i32.const 0) + (loop (global.set $x)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-then + (i32.const 0) (i32.const 0) + (if (then (global.set $x))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-else + (i32.const 0) (i32.const 0) + (if (result i32) (then (i32.const 0)) (else (global.set $x))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-br + (i32.const 0) + (block (br 0 (global.set $x))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-br_if + (i32.const 0) + (block (br_if 0 (global.set $x))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-br_table + (i32.const 0) + (block (br_table 0 (global.set $x))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-return + (return (global.set $x)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-select + (select (global.set $x) (i32.const 1) (i32.const 2)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-global.set-value-empty-in-call + (call 1 (global.set $x)) + ) + (func (param i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-global.set-value-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (global.set $x) (i32.const 0) + ) + ) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/imports.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/imports.wast new file mode 100644 index 000000000..c3a011d73 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/imports.wast @@ -0,0 +1,605 @@ +;; Auxiliary module to import from + +(module + (func (export "func")) + (func (export "func-i32") (param i32)) + (func (export "func-f32") (param f32)) + (func (export "func->i32") (result i32) (i32.const 22)) + (func (export "func->f32") (result f32) (f32.const 11)) + (func (export "func-i32->i32") (param i32) (result i32) (local.get 0)) + (func (export "func-i64->i64") (param i64) (result i64) (local.get 0)) + (global (export "global-i32") i32 (i32.const 55)) + (global (export "global-f32") f32 (f32.const 44)) + (table (export "table-10-inf") 10 funcref) + (table (export "table-10-20") 10 20 funcref) + (memory (export "memory-2-inf") 2) + ;; Multiple memories are not yet supported + ;; (memory (export "memory-2-4") 2 4) +) + +(register "test") + + +;; Functions + +(module + (type $func_i32 (func (param i32))) + (type $func_i64 (func (param i64))) + (type $func_f32 (func (param f32))) + (type $func_f64 (func (param f64))) + + (import "spectest" "print_i32" (func (param i32))) + ;; JavaScript can't handle i64 yet. + ;; (func (import "spectest" "print_i64") (param i64)) + (import "spectest" "print_i32" (func $print_i32 (param i32))) + ;; JavaScript can't handle i64 yet. + ;; (import "spectest" "print_i64" (func $print_i64 (param i64))) + (import "spectest" "print_f32" (func $print_f32 (param f32))) + (import "spectest" "print_f64" (func $print_f64 (param f64))) + (import "spectest" "print_i32_f32" (func $print_i32_f32 (param i32 f32))) + (import "spectest" "print_f64_f64" (func $print_f64_f64 (param f64 f64))) + (func $print_i32-2 (import "spectest" "print_i32") (param i32)) + (func $print_f64-2 (import "spectest" "print_f64") (param f64)) + (import "test" "func-i64->i64" (func $i64->i64 (param i64) (result i64))) + + (func (export "p1") (import "spectest" "print_i32") (param i32)) + (func $p (export "p2") (import "spectest" "print_i32") (param i32)) + (func (export "p3") (export "p4") (import "spectest" "print_i32") (param i32)) + (func (export "p5") (import "spectest" "print_i32") (type 0)) + (func (export "p6") (import "spectest" "print_i32") (type 0) (param i32) (result)) + + (import "spectest" "print_i32" (func (type $forward))) + (func (import "spectest" "print_i32") (type $forward)) + (type $forward (func (param i32))) + + (table funcref (elem $print_i32 $print_f64)) + + (func (export "print32") (param $i i32) + (local $x f32) + (local.set $x (f32.convert_i32_s (local.get $i))) + (call 0 (local.get $i)) + (call $print_i32_f32 + (i32.add (local.get $i) (i32.const 1)) + (f32.const 42) + ) + (call $print_i32 (local.get $i)) + (call $print_i32-2 (local.get $i)) + (call $print_f32 (local.get $x)) + (call_indirect (type $func_i32) (local.get $i) (i32.const 0)) + ) + + (func (export "print64") (param $i i64) + (local $x f64) + (local.set $x (f64.convert_i64_s (call $i64->i64 (local.get $i)))) + ;; JavaScript can't handle i64 yet. + ;; (call 1 (local.get $i)) + (call $print_f64_f64 + (f64.add (local.get $x) (f64.const 1)) + (f64.const 53) + ) + ;; JavaScript can't handle i64 yet. + ;; (call $print_i64 (local.get $i)) + (call $print_f64 (local.get $x)) + (call $print_f64-2 (local.get $x)) + (call_indirect (type $func_f64) (local.get $x) (i32.const 1)) + ) +) + +(assert_return (invoke "print32" (i32.const 13))) +(assert_return (invoke "print64" (i64.const 24))) + +(assert_invalid + (module + (type (func (result i32))) + (import "test" "func" (func (type 1))) + ) + "unknown type" +) + +(module (import "test" "func" (func))) +(module (import "test" "func-i32" (func (param i32)))) +(module (import "test" "func-f32" (func (param f32)))) +(module (import "test" "func->i32" (func (result i32)))) +(module (import "test" "func->f32" (func (result f32)))) +(module (import "test" "func-i32->i32" (func (param i32) (result i32)))) +(module (import "test" "func-i64->i64" (func (param i64) (result i64)))) + +(assert_unlinkable + (module (import "test" "unknown" (func))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (func))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "func" (func (param i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func" (func (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func" (func (param i32) (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (param f32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (param i64)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (param i32) (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (param i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (result f32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (result i64)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (param i32) (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32->i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32->i32" (func (param i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32->i32" (func (result i32)))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "test" "global-i32" (func (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "global_i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (func))) + "incompatible import type" +) + + +;; Globals + +(module + (import "spectest" "global_i32" (global i32)) + (global (import "spectest" "global_i32") i32) + + (import "spectest" "global_i32" (global $x i32)) + (global $y (import "spectest" "global_i32") i32) + + ;; JavaScript can't handle i64 yet. + ;; (import "spectest" "global_i64" (global i64)) + (import "spectest" "global_f32" (global f32)) + (import "spectest" "global_f64" (global f64)) + + (func (export "get-0") (result i32) (global.get 0)) + (func (export "get-1") (result i32) (global.get 1)) + (func (export "get-x") (result i32) (global.get $x)) + (func (export "get-y") (result i32) (global.get $y)) +) + +(assert_return (invoke "get-0") (i32.const 666)) +(assert_return (invoke "get-1") (i32.const 666)) +(assert_return (invoke "get-x") (i32.const 666)) +(assert_return (invoke "get-y") (i32.const 666)) + +(module (import "test" "global-i32" (global i32))) +(module (import "test" "global-f32" (global f32))) + +(assert_unlinkable + (module (import "test" "unknown" (global i32))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (global i32))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "func" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "print_i32" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (global i32))) + "incompatible import type" +) + + +;; Tables + +(module + (type (func (result i32))) + (import "spectest" "table" (table $tab 10 20 funcref)) + (elem (table $tab) (i32.const 1) func $f $g) + + (func (export "call") (param i32) (result i32) + (call_indirect $tab (type 0) (local.get 0)) + ) + (func $f (result i32) (i32.const 11)) + (func $g (result i32) (i32.const 22)) +) + +(assert_trap (invoke "call" (i32.const 0)) "uninitialized element") +(assert_return (invoke "call" (i32.const 1)) (i32.const 11)) +(assert_return (invoke "call" (i32.const 2)) (i32.const 22)) +(assert_trap (invoke "call" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "call" (i32.const 100)) "undefined element") + + +(module + (type (func (result i32))) + (table $tab (import "spectest" "table") 10 20 funcref) + (elem (table $tab) (i32.const 1) func $f $g) + + (func (export "call") (param i32) (result i32) + (call_indirect $tab (type 0) (local.get 0)) + ) + (func $f (result i32) (i32.const 11)) + (func $g (result i32) (i32.const 22)) +) + +(assert_trap (invoke "call" (i32.const 0)) "uninitialized element") +(assert_return (invoke "call" (i32.const 1)) (i32.const 11)) +(assert_return (invoke "call" (i32.const 2)) (i32.const 22)) +(assert_trap (invoke "call" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "call" (i32.const 100)) "undefined element") + + +(module + (import "spectest" "table" (table 0 funcref)) + (import "spectest" "table" (table 0 funcref)) + (table 10 funcref) + (table 10 funcref) +) + +(module (import "test" "table-10-inf" (table 10 funcref))) +(module (import "test" "table-10-inf" (table 5 funcref))) +(module (import "test" "table-10-inf" (table 0 funcref))) +(module (import "test" "table-10-20" (table 10 funcref))) +(module (import "test" "table-10-20" (table 5 funcref))) +(module (import "test" "table-10-20" (table 0 funcref))) +(module (import "test" "table-10-20" (table 10 20 funcref))) +(module (import "test" "table-10-20" (table 5 20 funcref))) +(module (import "test" "table-10-20" (table 0 20 funcref))) +(module (import "test" "table-10-20" (table 10 25 funcref))) +(module (import "test" "table-10-20" (table 5 25 funcref))) +(module (import "test" "table-10-20" (table 0 25 funcref))) +(module (import "spectest" "table" (table 10 funcref))) +(module (import "spectest" "table" (table 5 funcref))) +(module (import "spectest" "table" (table 0 funcref))) +(module (import "spectest" "table" (table 10 20 funcref))) +(module (import "spectest" "table" (table 5 20 funcref))) +(module (import "spectest" "table" (table 0 20 funcref))) +(module (import "spectest" "table" (table 10 25 funcref))) +(module (import "spectest" "table" (table 5 25 funcref))) + +(assert_unlinkable + (module (import "test" "unknown" (table 10 funcref))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (table 10 funcref))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "table-10-inf" (table 12 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (table 10 20 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-20" (table 12 20 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-20" (table 10 18 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (table 12 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (table 10 15 funcref))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "test" "func" (table 10 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-i32" (table 10 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (table 10 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "print_i32" (table 10 funcref))) + "incompatible import type" +) + + + +;; Memories + +(module + (import "spectest" "memory" (memory 1 2)) + (data (memory 0) (i32.const 10) "\10") + + (func (export "load") (param i32) (result i32) (i32.load (local.get 0))) +) + +(assert_return (invoke "load" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load" (i32.const 10)) (i32.const 16)) +(assert_return (invoke "load" (i32.const 8)) (i32.const 0x100000)) +(assert_trap (invoke "load" (i32.const 1000000)) "out of bounds memory access") + +(module + (memory (import "spectest" "memory") 1 2) + (data (memory 0) (i32.const 10) "\10") + + (func (export "load") (param i32) (result i32) (i32.load (local.get 0))) +) +(assert_return (invoke "load" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load" (i32.const 10)) (i32.const 16)) +(assert_return (invoke "load" (i32.const 8)) (i32.const 0x100000)) +(assert_trap (invoke "load" (i32.const 1000000)) "out of bounds memory access") + +(assert_invalid + (module (import "" "" (memory 1)) (import "" "" (memory 1))) + "multiple memories" +) +(assert_invalid + (module (import "" "" (memory 1)) (memory 0)) + "multiple memories" +) +(assert_invalid + (module (memory 0) (memory 0)) + "multiple memories" +) + +(module (import "test" "memory-2-inf" (memory 2))) +(module (import "test" "memory-2-inf" (memory 1))) +(module (import "test" "memory-2-inf" (memory 0))) +(module (import "spectest" "memory" (memory 1))) +(module (import "spectest" "memory" (memory 0))) +(module (import "spectest" "memory" (memory 1 2))) +(module (import "spectest" "memory" (memory 0 2))) +(module (import "spectest" "memory" (memory 1 3))) +(module (import "spectest" "memory" (memory 0 3))) + +(assert_unlinkable + (module (import "test" "unknown" (memory 1))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (memory 1))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "memory-2-inf" (memory 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (memory 2 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (memory 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (memory 1 1))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "test" "func-i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "print_i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "global_i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (memory 1))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "spectest" "memory" (memory 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (memory 1 1))) + "incompatible import type" +) + +(module + (import "spectest" "memory" (memory 0 3)) ;; actual has max size 2 + (func (export "grow") (param i32) (result i32) (memory.grow (local.get 0))) +) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 2)) + + +;; Syntax errors + +(assert_malformed + (module quote "(func) (import \"\" \"\" (func))") + "import after function" +) +(assert_malformed + (module quote "(func) (import \"\" \"\" (global i64))") + "import after function" +) +(assert_malformed + (module quote "(func) (import \"\" \"\" (table 0 funcref))") + "import after function" +) +(assert_malformed + (module quote "(func) (import \"\" \"\" (memory 0))") + "import after function" +) + +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (func))") + "import after global" +) +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (global f32))") + "import after global" +) +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (table 0 funcref))") + "import after global" +) +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (memory 0))") + "import after global" +) + +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (func))") + "import after table" +) +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (global i32))") + "import after table" +) +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (table 0 funcref))") + "import after table" +) +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (memory 0))") + "import after table" +) + +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (func))") + "import after memory" +) +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (global i32))") + "import after memory" +) +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (table 1 3 funcref))") + "import after memory" +) +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (memory 1 2))") + "import after memory" +) + +;; This module is required to validate, regardless of whether it can be +;; linked. Overloading is not possible in wasm itself, but it is possible +;; in modules from which wasm can import. +(module) +(register "not wasm") +(assert_unlinkable + (module + (import "not wasm" "overloaded" (func)) + (import "not wasm" "overloaded" (func (param i32))) + (import "not wasm" "overloaded" (func (param i32 i32))) + (import "not wasm" "overloaded" (func (param i64))) + (import "not wasm" "overloaded" (func (param f32))) + (import "not wasm" "overloaded" (func (param f64))) + (import "not wasm" "overloaded" (func (result i32))) + (import "not wasm" "overloaded" (func (result i64))) + (import "not wasm" "overloaded" (func (result f32))) + (import "not wasm" "overloaded" (func (result f64))) + (import "not wasm" "overloaded" (global i32)) + (import "not wasm" "overloaded" (global i64)) + (import "not wasm" "overloaded" (global f32)) + (import "not wasm" "overloaded" (global f64)) + (import "not wasm" "overloaded" (table 0 funcref)) + (import "not wasm" "overloaded" (memory 0)) + ) + "unknown import" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/linking.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/linking.wast new file mode 100644 index 000000000..49d55723d --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/linking.wast @@ -0,0 +1,495 @@ +;; Functions + +(module $Mf + (func (export "call") (result i32) (call $g)) + (func $g (result i32) (i32.const 2)) +) +(register "Mf" $Mf) + +(module $Nf + (func $f (import "Mf" "call") (result i32)) + (export "Mf.call" (func $f)) + (func (export "call Mf.call") (result i32) (call $f)) + (func (export "call") (result i32) (call $g)) + (func $g (result i32) (i32.const 3)) +) + +(assert_return (invoke $Mf "call") (i32.const 2)) +(assert_return (invoke $Nf "Mf.call") (i32.const 2)) +(assert_return (invoke $Nf "call") (i32.const 3)) +(assert_return (invoke $Nf "call Mf.call") (i32.const 2)) + +(module + (import "spectest" "print_i32" (func $f (param i32))) + (export "print" (func $f)) +) +(register "reexport_f") +(assert_unlinkable + (module (import "reexport_f" "print" (func (param i64)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "reexport_f" "print" (func (param i32) (result i32)))) + "incompatible import type" +) + + +;; Globals + +(module $Mg + (global $glob (export "glob") i32 (i32.const 42)) + (func (export "get") (result i32) (global.get $glob)) + + ;; export mutable globals + (global $mut_glob (export "mut_glob") (mut i32) (i32.const 142)) + (func (export "get_mut") (result i32) (global.get $mut_glob)) + (func (export "set_mut") (param i32) (global.set $mut_glob (local.get 0))) +) +(register "Mg" $Mg) + +(module $Ng + (global $x (import "Mg" "glob") i32) + (global $mut_glob (import "Mg" "mut_glob") (mut i32)) + (func $f (import "Mg" "get") (result i32)) + (func $get_mut (import "Mg" "get_mut") (result i32)) + (func $set_mut (import "Mg" "set_mut") (param i32)) + + (export "Mg.glob" (global $x)) + (export "Mg.get" (func $f)) + (global $glob (export "glob") i32 (i32.const 43)) + (func (export "get") (result i32) (global.get $glob)) + + (export "Mg.mut_glob" (global $mut_glob)) + (export "Mg.get_mut" (func $get_mut)) + (export "Mg.set_mut" (func $set_mut)) +) + +(assert_return (get $Mg "glob") (i32.const 42)) +(assert_return (get $Ng "Mg.glob") (i32.const 42)) +(assert_return (get $Ng "glob") (i32.const 43)) +(assert_return (invoke $Mg "get") (i32.const 42)) +(assert_return (invoke $Ng "Mg.get") (i32.const 42)) +(assert_return (invoke $Ng "get") (i32.const 43)) + +(assert_return (get $Mg "mut_glob") (i32.const 142)) +(assert_return (get $Ng "Mg.mut_glob") (i32.const 142)) +(assert_return (invoke $Mg "get_mut") (i32.const 142)) +(assert_return (invoke $Ng "Mg.get_mut") (i32.const 142)) + +(assert_return (invoke $Mg "set_mut" (i32.const 241))) +(assert_return (get $Mg "mut_glob") (i32.const 241)) +(assert_return (get $Ng "Mg.mut_glob") (i32.const 241)) +(assert_return (invoke $Mg "get_mut") (i32.const 241)) +(assert_return (invoke $Ng "Mg.get_mut") (i32.const 241)) + + +(assert_unlinkable + (module (import "Mg" "mut_glob" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "Mg" "glob" (global (mut i32)))) + "incompatible import type" +) + + +(module $Mref_ex + (global (export "g-const-null") nullref (ref.null)) + (global (export "g-var-null") (mut nullref) (ref.null)) + (global (export "g-const-func") funcref (ref.null)) + (global (export "g-var-func") (mut funcref) (ref.null)) + (global (export "g-const-any") anyref (ref.null)) + (global (export "g-var-any") (mut anyref) (ref.null)) +) +(register "Mref_ex" $Mref_ex) + +(module $Mref_im + (global (import "Mref_ex" "g-const-null") nullref) + (global (import "Mref_ex" "g-const-null") funcref) + (global (import "Mref_ex" "g-const-null") anyref) + (global (import "Mref_ex" "g-const-func") funcref) + (global (import "Mref_ex" "g-const-func") anyref) + (global (import "Mref_ex" "g-const-any") anyref) + + (global (import "Mref_ex" "g-var-null") (mut nullref)) + (global (import "Mref_ex" "g-var-func") (mut funcref)) + (global (import "Mref_ex" "g-var-any") (mut anyref)) +) + +(assert_unlinkable + (module (global (import "Mref_ex" "g-const-func") nullref)) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-const-any") nullref)) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-const-any") funcref)) + "incompatible import type" +) + + +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-null") (mut funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-null") (mut anyref))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-func") (mut nullref))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-func") (mut anyref))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-any") (mut nullref))) + "incompatible import type" +) +(assert_unlinkable + (module (global (import "Mref_ex" "g-var-any") (mut funcref))) + "incompatible import type" +) + + +;; Tables + +(module $Mt + (type (func (result i32))) + (type (func)) + + (table (export "tab") 10 funcref) + (elem (i32.const 2) $g $g $g $g) + (func $g (result i32) (i32.const 4)) + (func (export "h") (result i32) (i32.const -4)) + + (func (export "call") (param i32) (result i32) + (call_indirect (type 0) (local.get 0)) + ) +) +(register "Mt" $Mt) + +(module $Nt + (type (func)) + (type (func (result i32))) + + (func $f (import "Mt" "call") (param i32) (result i32)) + (func $h (import "Mt" "h") (result i32)) + + (table funcref (elem $g $g $g $h $f)) + (func $g (result i32) (i32.const 5)) + + (export "Mt.call" (func $f)) + (func (export "call Mt.call") (param i32) (result i32) + (call $f (local.get 0)) + ) + (func (export "call") (param i32) (result i32) + (call_indirect (type 1) (local.get 0)) + ) +) + +(assert_return (invoke $Mt "call" (i32.const 2)) (i32.const 4)) +(assert_return (invoke $Nt "Mt.call" (i32.const 2)) (i32.const 4)) +(assert_return (invoke $Nt "call" (i32.const 2)) (i32.const 5)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 2)) (i32.const 4)) + +(assert_trap (invoke $Mt "call" (i32.const 1)) "uninitialized") +(assert_trap (invoke $Nt "Mt.call" (i32.const 1)) "uninitialized") +(assert_return (invoke $Nt "call" (i32.const 1)) (i32.const 5)) +(assert_trap (invoke $Nt "call Mt.call" (i32.const 1)) "uninitialized") + +(assert_trap (invoke $Mt "call" (i32.const 0)) "uninitialized") +(assert_trap (invoke $Nt "Mt.call" (i32.const 0)) "uninitialized") +(assert_return (invoke $Nt "call" (i32.const 0)) (i32.const 5)) +(assert_trap (invoke $Nt "call Mt.call" (i32.const 0)) "uninitialized") + +(assert_trap (invoke $Mt "call" (i32.const 20)) "undefined") +(assert_trap (invoke $Nt "Mt.call" (i32.const 20)) "undefined") +(assert_trap (invoke $Nt "call" (i32.const 7)) "undefined") +(assert_trap (invoke $Nt "call Mt.call" (i32.const 20)) "undefined") + +(assert_return (invoke $Nt "call" (i32.const 3)) (i32.const -4)) +(assert_trap (invoke $Nt "call" (i32.const 4)) "indirect call") + +(module $Ot + (type (func (result i32))) + + (func $h (import "Mt" "h") (result i32)) + (table (import "Mt" "tab") 5 funcref) + (elem (i32.const 1) $i $h) + (func $i (result i32) (i32.const 6)) + + (func (export "call") (param i32) (result i32) + (call_indirect (type 0) (local.get 0)) + ) +) + +(assert_return (invoke $Mt "call" (i32.const 3)) (i32.const 4)) +(assert_return (invoke $Nt "Mt.call" (i32.const 3)) (i32.const 4)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 3)) (i32.const 4)) +(assert_return (invoke $Ot "call" (i32.const 3)) (i32.const 4)) + +(assert_return (invoke $Mt "call" (i32.const 2)) (i32.const -4)) +(assert_return (invoke $Nt "Mt.call" (i32.const 2)) (i32.const -4)) +(assert_return (invoke $Nt "call" (i32.const 2)) (i32.const 5)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 2)) (i32.const -4)) +(assert_return (invoke $Ot "call" (i32.const 2)) (i32.const -4)) + +(assert_return (invoke $Mt "call" (i32.const 1)) (i32.const 6)) +(assert_return (invoke $Nt "Mt.call" (i32.const 1)) (i32.const 6)) +(assert_return (invoke $Nt "call" (i32.const 1)) (i32.const 5)) +(assert_return (invoke $Nt "call Mt.call" (i32.const 1)) (i32.const 6)) +(assert_return (invoke $Ot "call" (i32.const 1)) (i32.const 6)) + +(assert_trap (invoke $Mt "call" (i32.const 0)) "uninitialized") +(assert_trap (invoke $Nt "Mt.call" (i32.const 0)) "uninitialized") +(assert_return (invoke $Nt "call" (i32.const 0)) (i32.const 5)) +(assert_trap (invoke $Nt "call Mt.call" (i32.const 0)) "uninitialized") +(assert_trap (invoke $Ot "call" (i32.const 0)) "uninitialized") + +(assert_trap (invoke $Ot "call" (i32.const 20)) "undefined") + +(module + (table (import "Mt" "tab") 0 funcref) + (elem (i32.const 9) $f) + (func $f) +) + +(module $G1 (global (export "g") i32 (i32.const 5))) +(register "G1" $G1) +(module $G2 + (global (import "G1" "g") i32) + (global (export "g") i32 (global.get 0)) +) +(assert_return (get $G2 "g") (i32.const 5)) + +(assert_trap + (module + (table (import "Mt" "tab") 0 funcref) + (elem (i32.const 10) $f) + (func $f) + ) + "out of bounds" +) + +(assert_unlinkable + (module + (table (import "Mt" "tab") 10 funcref) + (memory (import "Mt" "mem") 1) ;; does not exist + (func $f (result i32) (i32.const 0)) + (elem (i32.const 7) $f) + (elem (i32.const 9) $f) + ) + "unknown import" +) +(assert_trap (invoke $Mt "call" (i32.const 7)) "uninitialized") + +;; Unlike in the v1 spec, active element segments stored before an +;; out-of-bounds access persist after the instantiation failure. +(assert_trap + (module + (table (import "Mt" "tab") 10 funcref) + (func $f (result i32) (i32.const 0)) + (elem (i32.const 7) $f) + (elem (i32.const 12) $f) ;; out of bounds + ) + "out of bounds" +) +(assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0)) + +(assert_trap + (module + (table (import "Mt" "tab") 10 funcref) + (func $f (result i32) (i32.const 0)) + (elem (i32.const 7) $f) + (memory 1) + (data (i32.const 0x10000) "d") ;; out of bounds + ) + "out of bounds" +) +(assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0)) + + +(module $Mtable_ex + (table $t1 (export "t-null") 1 nullref) + (table $t2 (export "t-func") 1 funcref) + (table $t3 (export "t-any") 1 anyref) +) +(register "Mtable_ex" $Mtable_ex) + +(module + (table (import "Mtable_ex" "t-null") 1 nullref) + (table (import "Mtable_ex" "t-func") 1 funcref) + (table (import "Mtable_ex" "t-any") 1 anyref) +) + +(assert_unlinkable + (module (table (import "Mtable_ex" "t-null") 1 funcref)) + "incompatible import type" +) +(assert_unlinkable + (module (table (import "Mtable_ex" "t-null") 1 anyref)) + "incompatible import type" +) +(assert_unlinkable + (module (table (import "Mtable_ex" "t-func") 1 nullref)) + "incompatible import type" +) +(assert_unlinkable + (module (table (import "Mtable_ex" "t-func") 1 anyref)) + "incompatible import type" +) +(assert_unlinkable + (module (table (import "Mtable_ex" "t-any") 1 nullref)) + "incompatible import type" +) +(assert_unlinkable + (module (table (import "Mtable_ex" "t-any") 1 funcref)) + "incompatible import type" +) + + +;; Memories + +(module $Mm + (memory (export "mem") 1 5) + (data (i32.const 10) "\00\01\02\03\04\05\06\07\08\09") + + (func (export "load") (param $a i32) (result i32) + (i32.load8_u (local.get 0)) + ) +) +(register "Mm" $Mm) + +(module $Nm + (func $loadM (import "Mm" "load") (param i32) (result i32)) + + (memory 1) + (data (i32.const 10) "\f0\f1\f2\f3\f4\f5") + + (export "Mm.load" (func $loadM)) + (func (export "load") (param $a i32) (result i32) + (i32.load8_u (local.get 0)) + ) +) + +(assert_return (invoke $Mm "load" (i32.const 12)) (i32.const 2)) +(assert_return (invoke $Nm "Mm.load" (i32.const 12)) (i32.const 2)) +(assert_return (invoke $Nm "load" (i32.const 12)) (i32.const 0xf2)) + +(module $Om + (memory (import "Mm" "mem") 1) + (data (i32.const 5) "\a0\a1\a2\a3\a4\a5\a6\a7") + + (func (export "load") (param $a i32) (result i32) + (i32.load8_u (local.get 0)) + ) +) + +(assert_return (invoke $Mm "load" (i32.const 12)) (i32.const 0xa7)) +(assert_return (invoke $Nm "Mm.load" (i32.const 12)) (i32.const 0xa7)) +(assert_return (invoke $Nm "load" (i32.const 12)) (i32.const 0xf2)) +(assert_return (invoke $Om "load" (i32.const 12)) (i32.const 0xa7)) + +(module + (memory (import "Mm" "mem") 0) + (data (i32.const 0xffff) "a") +) + +(assert_trap + (module + (memory (import "Mm" "mem") 0) + (data (i32.const 0x10000) "a") + ) + "out of bounds" +) + +(module $Pm + (memory (import "Mm" "mem") 1 8) + + (func (export "grow") (param $a i32) (result i32) + (memory.grow (local.get 0)) + ) +) + +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 1)) +(assert_return (invoke $Pm "grow" (i32.const 2)) (i32.const 1)) +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 3)) +(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const 3)) +(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const 4)) +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5)) +(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const -1)) +(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5)) + +(assert_unlinkable + (module + (func $host (import "spectest" "print")) + (memory (import "Mm" "mem") 1) + (table (import "Mm" "tab") 0 funcref) ;; does not exist + (data (i32.const 0) "abc") + ) + "unknown import" +) +(assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 0)) + +;; Unlike in v1 spec, active data segments written before an +;; out-of-bounds access persist after the instantiation failure. +(assert_trap + (module + (memory (import "Mm" "mem") 1) + (data (i32.const 0) "abc") + (data (i32.const 0x50000) "d") ;; out of bounds + ) + "out of bounds" +) +(assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 97)) + +(assert_trap + (module + (memory (import "Mm" "mem") 1) + (data (i32.const 0) "abc") + (table 0 funcref) + (func) + (elem (i32.const 0) 0) ;; out of bounds + ) + "out of bounds" +) +(assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 97)) + +;; Store is modified if the start function traps. +(module $Ms + (type $t (func (result i32))) + (memory (export "memory") 1) + (table (export "table") 1 funcref) + (func (export "get memory[0]") (type $t) + (i32.load8_u (i32.const 0)) + ) + (func (export "get table[0]") (type $t) + (call_indirect (type $t) (i32.const 0)) + ) +) +(register "Ms" $Ms) + +(assert_trap + (module + (import "Ms" "memory" (memory 1)) + (import "Ms" "table" (table 1 funcref)) + (data (i32.const 0) "hello") + (elem (i32.const 0) $f) + (func $f (result i32) + (i32.const 0xdead) + ) + (func $main + (unreachable) + ) + (start $main) + ) + "unreachable" +) + +(assert_return (invoke $Ms "get memory[0]") (i32.const 104)) ;; 'h' +(assert_return (invoke $Ms "get table[0]") (i32.const 0xdead)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/memory_copy.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/memory_copy.wast new file mode 100644 index 000000000..692e1ad85 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/memory_copy.wast @@ -0,0 +1,5577 @@ +;; +;; Generated by ../meta/generate_memory_copy.js +;; + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (nop)) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 13) (i32.const 2) (i32.const 3))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 25) (i32.const 15) (i32.const 2))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 13) (i32.const 25) (i32.const 3))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 20) (i32.const 22) (i32.const 4))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 25) (i32.const 1) (i32.const 3))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 10) (i32.const 12) (i32.const 7))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data (i32.const 12) "\07\05\02\03\06") + (func (export "test") + (memory.copy (i32.const 12) (i32.const 10) (i32.const 7))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65516) (i32.const 0) (i32.const 40)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 19)) +(assert_return (invoke "load8_u" (i32.const 218)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 417)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 616)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 815)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1014)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1213)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1412)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1611)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1810)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2009)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2208)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2407)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2606)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2805)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3004)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3203)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3402)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3601)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3800)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3999)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65490)) (i32.const 0)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65515) (i32.const 0) (i32.const 39)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 19)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 20)) +(assert_return (invoke "load8_u" (i32.const 219)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 418)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 617)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 816)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1015)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1214)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1413)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1612)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1811)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2010)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2209)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2408)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2607)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2806)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3005)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3204)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3403)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3602)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3801)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4000)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4199)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4398)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4597)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4796)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4995)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5194)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5393)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5592)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5791)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5990)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6189)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6388)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6587)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6786)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6985)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7184)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7383)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7582)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7781)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7980)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8179)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8378)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8577)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8776)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8975)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9174)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9373)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9572)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9771)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9970)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10169)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10368)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10567)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10766)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10965)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11164)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11363)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11562)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11761)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11960)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12159)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12358)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12557)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12756)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12955)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13154)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13353)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13552)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13751)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13950)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14149)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14348)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14547)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14746)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14945)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15144)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15343)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15542)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15741)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15940)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16139)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16338)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16537)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16736)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16935)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17134)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17333)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17532)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17731)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17930)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18129)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18328)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18527)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18726)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18925)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19124)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19323)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19522)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19721)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19920)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20119)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20318)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20517)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20716)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20915)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21114)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21313)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21512)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21711)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21910)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22109)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22308)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22507)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22706)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22905)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23104)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23303)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23502)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23701)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23900)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24099)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24298)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24497)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24696)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24895)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25094)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25293)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25492)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25691)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25890)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26089)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26288)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26487)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26686)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26885)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27084)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27283)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27482)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27681)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27880)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28079)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28278)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28477)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28676)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28875)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29074)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29273)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29472)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29671)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29870)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30069)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30268)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30467)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30666)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30865)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31064)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31263)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31462)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31661)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31860)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32059)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32258)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32457)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32656)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32855)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33054)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33253)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33452)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33651)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33850)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34049)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34248)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34447)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34646)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34845)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35044)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35243)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35442)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35641)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35840)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36039)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36238)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36437)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36636)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36835)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37034)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37233)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37432)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37631)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37830)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38029)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38228)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38427)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38626)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38825)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39024)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39223)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39422)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39621)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39820)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40019)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40218)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40417)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40616)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40815)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41014)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41213)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41412)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41611)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41810)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42009)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42208)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42407)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42606)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42805)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43004)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43203)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43402)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43601)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43800)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43999)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65491)) (i32.const 0)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 65516) (i32.const 40)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65515) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 65515) (i32.const 39)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 19)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 20)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65486) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65516) (i32.const 65486) (i32.const 40)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65487)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65488)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65489)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65490)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65491)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65492)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65493)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65494)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65495)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65496)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65497)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65498)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65499)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65500)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65501)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65502)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65503)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65504)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65505)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65486) (i32.const 65516) (i32.const 40)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65506) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65516) (i32.const 65506) (i32.const 40)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65507)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65508)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65509)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65510)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65511)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65512)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65513)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65514)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65515)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65506) (i32.const 65516) (i32.const 40)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65516) (i32.const 65516) (i32.const 40)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 19)) + +(module + (memory (export "mem") 1 ) + (data (i32.const 65516) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 65516) (i32.const 4294963200)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61490)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61689)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61888)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62087)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62286)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62485)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62684)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62883)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63082)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63281)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63480)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63679)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63878)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64077)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64276)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64475)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64674)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64873)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65072)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65271)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65470)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65517)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 65518)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 65519)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 65520)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 65521)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 65522)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 65523)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 65524)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 65525)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 65526)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 65527)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 65528)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 65529)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 65530)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 65531)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 65532)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 65533)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 65534)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 65535)) (i32.const 19)) + +(module + (memory (export "mem") 1 1 ) + (data (i32.const 61440) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13") + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (memory.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(assert_trap (invoke "run" (i32.const 65516) (i32.const 61440) (i32.const 4294967040)) + "out of bounds") + +(assert_return (invoke "load8_u" (i32.const 198)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 397)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 596)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 795)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 994)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1193)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1392)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1591)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1790)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1989)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2188)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2387)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2586)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2785)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2984)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3183)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3382)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3581)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3780)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 3979)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4178)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4377)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4576)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4775)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 4974)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5173)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5372)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5571)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5770)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 5969)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6168)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6367)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6566)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6765)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 6964)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7163)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7362)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7561)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7760)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7959)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8158)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8357)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8556)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8755)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8954)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9153)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9352)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9551)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9750)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9949)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10148)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10347)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10546)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10745)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10944)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11143)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11342)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11541)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11740)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11939)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12138)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12337)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12536)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12735)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12934)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13133)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13332)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13531)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13730)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 13929)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14128)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14327)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14526)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14725)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14924)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15123)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15322)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15521)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15720)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 15919)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16118)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16317)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16516)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16715)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 16914)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17113)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17312)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17511)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17710)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 17909)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18108)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18307)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18506)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18705)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18904)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19103)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19302)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19501)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19700)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19899)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20098)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20297)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20496)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20695)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20894)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21093)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21292)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21491)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21690)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21889)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22088)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22287)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22486)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22685)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22884)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23083)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23282)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23481)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23680)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23879)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24078)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24277)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24476)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24675)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24874)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25073)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25272)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25471)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25670)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25869)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26068)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26267)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26466)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26665)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26864)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27063)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27262)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27461)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27660)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27859)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28058)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28257)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28456)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28655)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28854)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29053)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29252)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29451)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29650)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29849)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30048)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30247)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30446)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30645)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 30844)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31043)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31242)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31441)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31640)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 31839)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32038)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32237)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32436)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32635)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 32834)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33033)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33232)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33431)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33630)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 33829)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34028)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34227)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34426)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34625)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 34824)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35023)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35222)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35421)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35620)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 35819)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36018)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36217)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36416)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36615)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 36814)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37013)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37212)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37411)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37610)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 37809)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38008)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38207)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38406)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38605)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 38804)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39003)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39202)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39401)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39600)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39799)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 39998)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40197)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40396)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40595)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40794)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 40993)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41192)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41391)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41590)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41789)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 41988)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42187)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42386)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42585)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42784)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 42983)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43182)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43381)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43580)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43779)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 43978)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44177)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44376)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44575)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44774)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 44973)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45172)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45371)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45570)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45769)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 45968)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46167)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46366)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46565)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46764)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 46963)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47162)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47361)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47560)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47759)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 47958)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48157)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48356)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48555)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48754)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 48953)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49152)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49351)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49550)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49749)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 49948)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50147)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50346)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50545)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50744)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 50943)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51142)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51341)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51540)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51739)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 51938)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52137)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52336)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52535)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52734)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 52933)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53132)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53331)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53530)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53729)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 53928)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54127)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54326)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54525)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54724)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 54923)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55122)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55321)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55520)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55719)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 55918)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56117)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56316)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56515)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56714)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 56913)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57112)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57311)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 57908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 58903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 59898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 60893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61440)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61441)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 61442)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 61443)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 61444)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 61445)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 61446)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 61447)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 61448)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 61449)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 61450)) (i32.const 10)) +(assert_return (invoke "load8_u" (i32.const 61451)) (i32.const 11)) +(assert_return (invoke "load8_u" (i32.const 61452)) (i32.const 12)) +(assert_return (invoke "load8_u" (i32.const 61453)) (i32.const 13)) +(assert_return (invoke "load8_u" (i32.const 61454)) (i32.const 14)) +(assert_return (invoke "load8_u" (i32.const 61455)) (i32.const 15)) +(assert_return (invoke "load8_u" (i32.const 61456)) (i32.const 16)) +(assert_return (invoke "load8_u" (i32.const 61457)) (i32.const 17)) +(assert_return (invoke "load8_u" (i32.const 61458)) (i32.const 18)) +(assert_return (invoke "load8_u" (i32.const 61459)) (i32.const 19)) +(assert_return (invoke "load8_u" (i32.const 61510)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61709)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 61908)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62107)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62306)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62505)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62704)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 62903)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63102)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63301)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63500)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63699)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 63898)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64097)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64296)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64495)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64694)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 64893)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65092)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65291)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 65490)) (i32.const 0)) + +(assert_invalid + (module + (func (export "testfn") + (memory.copy (i32.const 10) (i32.const 20) (i32.const 30)))) + "unknown memory 0") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i32.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f32.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (i64.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.copy (f64.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + + +(module + (memory 1 1) + (func (export "test") + (memory.fill (i32.const 10) (i32.const 0x55) (i32.const 10)) + (memory.copy (i32.const 9) (i32.const 10) (i32.const 5))) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) +) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 9) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 9) (i32.const 20) (i32.const 85)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 20) (i32.const 65536) (i32.const 0)) + (i32.const -1)) + +(module + (memory 1 1) + (func (export "test") + (memory.fill (i32.const 10) (i32.const 0x55) (i32.const 10)) + (memory.copy (i32.const 16) (i32.const 15) (i32.const 5))) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) +) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 10) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 10) (i32.const 21) (i32.const 85)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 21) (i32.const 65536) (i32.const 0)) + (i32.const -1)) + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0xFF00) (i32.const 0x8000) (i32.const 257)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0xFFFFFF00) (i32.const 0x4000) (i32.const 257)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x8000) (i32.const 0xFF00) (i32.const 257)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x4000) (i32.const 0xFFFFFF00) (i32.const 257)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + (func (export "test") + (memory.fill (i32.const 0x0000) (i32.const 0x55) (i32.const 0x8000)) + (memory.fill (i32.const 0x8000) (i32.const 0xAA) (i32.const 0x8000)) + (memory.copy (i32.const 0x9000) (i32.const 0x7000) (i32.const 0))) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) +) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 32768) (i32.const 85)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 32768) (i32.const 65536) (i32.const 170)) + (i32.const -1)) +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x10000) (i32.const 0x7000) (i32.const 0)))) +(invoke "test") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x20000) (i32.const 0x7000) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x9000) (i32.const 0x10000) (i32.const 0)))) +(invoke "test") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x9000) (i32.const 0x20000) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x10000) (i32.const 0x10000) (i32.const 0)))) +(invoke "test") + +(module + (memory 1 1) + (func (export "test") + (memory.copy (i32.const 0x20000) (i32.const 0x20000) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + (func (export "test") + (memory.fill (i32.const 17767) (i32.const 1) (i32.const 1344)) + (memory.fill (i32.const 39017) (i32.const 2) (i32.const 1055)) + (memory.fill (i32.const 56401) (i32.const 3) (i32.const 988)) + (memory.fill (i32.const 37962) (i32.const 4) (i32.const 322)) + (memory.fill (i32.const 7977) (i32.const 5) (i32.const 1994)) + (memory.fill (i32.const 22714) (i32.const 6) (i32.const 3036)) + (memory.fill (i32.const 16882) (i32.const 7) (i32.const 2372)) + (memory.fill (i32.const 43491) (i32.const 8) (i32.const 835)) + (memory.fill (i32.const 124) (i32.const 9) (i32.const 1393)) + (memory.fill (i32.const 2132) (i32.const 10) (i32.const 2758)) + (memory.fill (i32.const 8987) (i32.const 11) (i32.const 3098)) + (memory.fill (i32.const 52711) (i32.const 12) (i32.const 741)) + (memory.fill (i32.const 3958) (i32.const 13) (i32.const 2823)) + (memory.fill (i32.const 49715) (i32.const 14) (i32.const 1280)) + (memory.fill (i32.const 50377) (i32.const 15) (i32.const 1466)) + (memory.fill (i32.const 20493) (i32.const 16) (i32.const 3158)) + (memory.fill (i32.const 47665) (i32.const 17) (i32.const 544)) + (memory.fill (i32.const 12451) (i32.const 18) (i32.const 2669)) + (memory.fill (i32.const 24869) (i32.const 19) (i32.const 2651)) + (memory.fill (i32.const 45317) (i32.const 20) (i32.const 1570)) + (memory.fill (i32.const 43096) (i32.const 21) (i32.const 1691)) + (memory.fill (i32.const 33886) (i32.const 22) (i32.const 646)) + (memory.fill (i32.const 48555) (i32.const 23) (i32.const 1858)) + (memory.fill (i32.const 53453) (i32.const 24) (i32.const 2657)) + (memory.fill (i32.const 30363) (i32.const 25) (i32.const 981)) + (memory.fill (i32.const 9300) (i32.const 26) (i32.const 1807)) + (memory.fill (i32.const 50190) (i32.const 27) (i32.const 487)) + (memory.fill (i32.const 62753) (i32.const 28) (i32.const 530)) + (memory.fill (i32.const 36316) (i32.const 29) (i32.const 943)) + (memory.fill (i32.const 6768) (i32.const 30) (i32.const 381)) + (memory.fill (i32.const 51262) (i32.const 31) (i32.const 3089)) + (memory.fill (i32.const 49729) (i32.const 32) (i32.const 658)) + (memory.fill (i32.const 44540) (i32.const 33) (i32.const 1702)) + (memory.fill (i32.const 33342) (i32.const 34) (i32.const 1092)) + (memory.fill (i32.const 50814) (i32.const 35) (i32.const 1410)) + (memory.fill (i32.const 47594) (i32.const 36) (i32.const 2204)) + (memory.fill (i32.const 54123) (i32.const 37) (i32.const 2394)) + (memory.fill (i32.const 55183) (i32.const 38) (i32.const 250)) + (memory.fill (i32.const 22620) (i32.const 39) (i32.const 2097)) + (memory.fill (i32.const 17132) (i32.const 40) (i32.const 3264)) + (memory.fill (i32.const 54331) (i32.const 41) (i32.const 3299)) + (memory.fill (i32.const 39474) (i32.const 42) (i32.const 2796)) + (memory.fill (i32.const 36156) (i32.const 43) (i32.const 2070)) + (memory.fill (i32.const 35308) (i32.const 44) (i32.const 2763)) + (memory.fill (i32.const 32731) (i32.const 45) (i32.const 312)) + (memory.fill (i32.const 63746) (i32.const 46) (i32.const 192)) + (memory.fill (i32.const 30974) (i32.const 47) (i32.const 596)) + (memory.fill (i32.const 16635) (i32.const 48) (i32.const 501)) + (memory.fill (i32.const 57002) (i32.const 49) (i32.const 686)) + (memory.fill (i32.const 34299) (i32.const 50) (i32.const 385)) + (memory.fill (i32.const 60881) (i32.const 51) (i32.const 903)) + (memory.fill (i32.const 61445) (i32.const 52) (i32.const 2390)) + (memory.fill (i32.const 46972) (i32.const 53) (i32.const 1441)) + (memory.fill (i32.const 25973) (i32.const 54) (i32.const 3162)) + (memory.fill (i32.const 5566) (i32.const 55) (i32.const 2135)) + (memory.fill (i32.const 35977) (i32.const 56) (i32.const 519)) + (memory.fill (i32.const 44892) (i32.const 57) (i32.const 3280)) + (memory.fill (i32.const 46760) (i32.const 58) (i32.const 1678)) + (memory.fill (i32.const 46607) (i32.const 59) (i32.const 3168)) + (memory.fill (i32.const 22449) (i32.const 60) (i32.const 1441)) + (memory.fill (i32.const 58609) (i32.const 61) (i32.const 663)) + (memory.fill (i32.const 32261) (i32.const 62) (i32.const 1671)) + (memory.fill (i32.const 3063) (i32.const 63) (i32.const 721)) + (memory.fill (i32.const 34025) (i32.const 64) (i32.const 84)) + (memory.fill (i32.const 33338) (i32.const 65) (i32.const 2029)) + (memory.fill (i32.const 36810) (i32.const 66) (i32.const 29)) + (memory.fill (i32.const 19147) (i32.const 67) (i32.const 3034)) + (memory.fill (i32.const 12616) (i32.const 68) (i32.const 1043)) + (memory.fill (i32.const 18276) (i32.const 69) (i32.const 3324)) + (memory.fill (i32.const 4639) (i32.const 70) (i32.const 1091)) + (memory.fill (i32.const 16158) (i32.const 71) (i32.const 1997)) + (memory.fill (i32.const 18204) (i32.const 72) (i32.const 2259)) + (memory.fill (i32.const 50532) (i32.const 73) (i32.const 3189)) + (memory.fill (i32.const 11028) (i32.const 74) (i32.const 1968)) + (memory.fill (i32.const 15962) (i32.const 75) (i32.const 1455)) + (memory.fill (i32.const 45406) (i32.const 76) (i32.const 1177)) + (memory.fill (i32.const 54137) (i32.const 77) (i32.const 1568)) + (memory.fill (i32.const 33083) (i32.const 78) (i32.const 1642)) + (memory.fill (i32.const 61028) (i32.const 79) (i32.const 3284)) + (memory.fill (i32.const 51729) (i32.const 80) (i32.const 223)) + (memory.fill (i32.const 4361) (i32.const 81) (i32.const 2171)) + (memory.fill (i32.const 57514) (i32.const 82) (i32.const 1322)) + (memory.fill (i32.const 55724) (i32.const 83) (i32.const 2648)) + (memory.fill (i32.const 24091) (i32.const 84) (i32.const 1045)) + (memory.fill (i32.const 43183) (i32.const 85) (i32.const 3097)) + (memory.fill (i32.const 32307) (i32.const 86) (i32.const 2796)) + (memory.fill (i32.const 3811) (i32.const 87) (i32.const 2010)) + (memory.fill (i32.const 54856) (i32.const 88) (i32.const 0)) + (memory.fill (i32.const 49941) (i32.const 89) (i32.const 2069)) + (memory.fill (i32.const 20411) (i32.const 90) (i32.const 2896)) + (memory.fill (i32.const 33826) (i32.const 91) (i32.const 192)) + (memory.fill (i32.const 9402) (i32.const 92) (i32.const 2195)) + (memory.fill (i32.const 12413) (i32.const 93) (i32.const 24)) + (memory.fill (i32.const 14091) (i32.const 94) (i32.const 577)) + (memory.fill (i32.const 44058) (i32.const 95) (i32.const 2089)) + (memory.fill (i32.const 36735) (i32.const 96) (i32.const 3436)) + (memory.fill (i32.const 23288) (i32.const 97) (i32.const 2765)) + (memory.fill (i32.const 6392) (i32.const 98) (i32.const 830)) + (memory.fill (i32.const 33307) (i32.const 99) (i32.const 1938)) + (memory.fill (i32.const 21941) (i32.const 100) (i32.const 2750)) + (memory.copy (i32.const 59214) (i32.const 54248) (i32.const 2098)) + (memory.copy (i32.const 63026) (i32.const 39224) (i32.const 230)) + (memory.copy (i32.const 51833) (i32.const 23629) (i32.const 2300)) + (memory.copy (i32.const 6708) (i32.const 23996) (i32.const 639)) + (memory.copy (i32.const 6990) (i32.const 33399) (i32.const 1097)) + (memory.copy (i32.const 19403) (i32.const 10348) (i32.const 3197)) + (memory.copy (i32.const 27308) (i32.const 54406) (i32.const 100)) + (memory.copy (i32.const 27221) (i32.const 43682) (i32.const 1717)) + (memory.copy (i32.const 60528) (i32.const 8629) (i32.const 119)) + (memory.copy (i32.const 5947) (i32.const 2308) (i32.const 658)) + (memory.copy (i32.const 4787) (i32.const 51631) (i32.const 2269)) + (memory.copy (i32.const 12617) (i32.const 19197) (i32.const 833)) + (memory.copy (i32.const 11854) (i32.const 46505) (i32.const 3300)) + (memory.copy (i32.const 11376) (i32.const 45012) (i32.const 2281)) + (memory.copy (i32.const 34186) (i32.const 6697) (i32.const 2572)) + (memory.copy (i32.const 4936) (i32.const 1690) (i32.const 1328)) + (memory.copy (i32.const 63164) (i32.const 7637) (i32.const 1670)) + (memory.copy (i32.const 44568) (i32.const 18344) (i32.const 33)) + (memory.copy (i32.const 43918) (i32.const 22348) (i32.const 1427)) + (memory.copy (i32.const 46637) (i32.const 49819) (i32.const 1434)) + (memory.copy (i32.const 63684) (i32.const 8755) (i32.const 834)) + (memory.copy (i32.const 33485) (i32.const 20131) (i32.const 3317)) + (memory.copy (i32.const 40575) (i32.const 54317) (i32.const 3201)) + (memory.copy (i32.const 25812) (i32.const 59254) (i32.const 2452)) + (memory.copy (i32.const 19678) (i32.const 56882) (i32.const 346)) + (memory.copy (i32.const 15852) (i32.const 35914) (i32.const 2430)) + (memory.copy (i32.const 11824) (i32.const 35574) (i32.const 300)) + (memory.copy (i32.const 59427) (i32.const 13957) (i32.const 3153)) + (memory.copy (i32.const 34299) (i32.const 60594) (i32.const 1281)) + (memory.copy (i32.const 8964) (i32.const 12276) (i32.const 943)) + (memory.copy (i32.const 2827) (i32.const 10425) (i32.const 1887)) + (memory.copy (i32.const 43194) (i32.const 43910) (i32.const 738)) + (memory.copy (i32.const 63038) (i32.const 18949) (i32.const 122)) + (memory.copy (i32.const 24044) (i32.const 44761) (i32.const 1755)) + (memory.copy (i32.const 22608) (i32.const 14755) (i32.const 702)) + (memory.copy (i32.const 11284) (i32.const 26579) (i32.const 1830)) + (memory.copy (i32.const 23092) (i32.const 20471) (i32.const 1064)) + (memory.copy (i32.const 57248) (i32.const 54770) (i32.const 2631)) + (memory.copy (i32.const 25492) (i32.const 1025) (i32.const 3113)) + (memory.copy (i32.const 49588) (i32.const 44220) (i32.const 975)) + (memory.copy (i32.const 28280) (i32.const 41722) (i32.const 2336)) + (memory.copy (i32.const 61289) (i32.const 230) (i32.const 2872)) + (memory.copy (i32.const 22480) (i32.const 52506) (i32.const 2197)) + (memory.copy (i32.const 40553) (i32.const 9578) (i32.const 1958)) + (memory.copy (i32.const 29004) (i32.const 20862) (i32.const 2186)) + (memory.copy (i32.const 53029) (i32.const 43955) (i32.const 1037)) + (memory.copy (i32.const 25476) (i32.const 35667) (i32.const 1650)) + (memory.copy (i32.const 58516) (i32.const 45819) (i32.const 1986)) + (memory.copy (i32.const 38297) (i32.const 5776) (i32.const 1955)) + (memory.copy (i32.const 28503) (i32.const 55364) (i32.const 2368)) + (memory.copy (i32.const 62619) (i32.const 18108) (i32.const 1356)) + (memory.copy (i32.const 50149) (i32.const 13861) (i32.const 382)) + (memory.copy (i32.const 16904) (i32.const 36341) (i32.const 1900)) + (memory.copy (i32.const 48098) (i32.const 11358) (i32.const 2807)) + (memory.copy (i32.const 28512) (i32.const 40362) (i32.const 323)) + (memory.copy (i32.const 35506) (i32.const 27856) (i32.const 1670)) + (memory.copy (i32.const 62970) (i32.const 53332) (i32.const 1341)) + (memory.copy (i32.const 14133) (i32.const 46312) (i32.const 644)) + (memory.copy (i32.const 29030) (i32.const 19074) (i32.const 496)) + (memory.copy (i32.const 44952) (i32.const 47577) (i32.const 2784)) + (memory.copy (i32.const 39559) (i32.const 44661) (i32.const 1350)) + (memory.copy (i32.const 10352) (i32.const 29274) (i32.const 1475)) + (memory.copy (i32.const 46911) (i32.const 46178) (i32.const 1467)) + (memory.copy (i32.const 4905) (i32.const 28740) (i32.const 1895)) + (memory.copy (i32.const 38012) (i32.const 57253) (i32.const 1751)) + (memory.copy (i32.const 26446) (i32.const 27223) (i32.const 1127)) + (memory.copy (i32.const 58835) (i32.const 24657) (i32.const 1063)) + (memory.copy (i32.const 61356) (i32.const 38790) (i32.const 766)) + (memory.copy (i32.const 44160) (i32.const 2284) (i32.const 1520)) + (memory.copy (i32.const 32740) (i32.const 47237) (i32.const 3014)) + (memory.copy (i32.const 11148) (i32.const 21260) (i32.const 1011)) + (memory.copy (i32.const 7665) (i32.const 31612) (i32.const 3034)) + (memory.copy (i32.const 18044) (i32.const 12987) (i32.const 3320)) + (memory.copy (i32.const 57306) (i32.const 55905) (i32.const 308)) + (memory.copy (i32.const 24675) (i32.const 16815) (i32.const 1155)) + (memory.copy (i32.const 19900) (i32.const 10115) (i32.const 722)) + (memory.copy (i32.const 2921) (i32.const 5935) (i32.const 2370)) + (memory.copy (i32.const 32255) (i32.const 50095) (i32.const 2926)) + (memory.copy (i32.const 15126) (i32.const 17299) (i32.const 2607)) + (memory.copy (i32.const 45575) (i32.const 28447) (i32.const 2045)) + (memory.copy (i32.const 55149) (i32.const 36113) (i32.const 2596)) + (memory.copy (i32.const 28461) (i32.const 54157) (i32.const 1168)) + (memory.copy (i32.const 47951) (i32.const 53385) (i32.const 3137)) + (memory.copy (i32.const 30646) (i32.const 45155) (i32.const 2649)) + (memory.copy (i32.const 5057) (i32.const 4295) (i32.const 52)) + (memory.copy (i32.const 6692) (i32.const 24195) (i32.const 441)) + (memory.copy (i32.const 32984) (i32.const 27117) (i32.const 3445)) + (memory.copy (i32.const 32530) (i32.const 59372) (i32.const 2785)) + (memory.copy (i32.const 34361) (i32.const 8962) (i32.const 2406)) + (memory.copy (i32.const 17893) (i32.const 54538) (i32.const 3381)) + (memory.copy (i32.const 22685) (i32.const 44151) (i32.const 136)) + (memory.copy (i32.const 59089) (i32.const 7077) (i32.const 1045)) + (memory.copy (i32.const 42945) (i32.const 55028) (i32.const 2389)) + (memory.copy (i32.const 44693) (i32.const 20138) (i32.const 877)) + (memory.copy (i32.const 36810) (i32.const 25196) (i32.const 3447)) + (memory.copy (i32.const 45742) (i32.const 31888) (i32.const 854)) + (memory.copy (i32.const 24236) (i32.const 31866) (i32.const 1377)) + (memory.copy (i32.const 33778) (i32.const 692) (i32.const 1594)) + (memory.copy (i32.const 60618) (i32.const 18585) (i32.const 2987)) + (memory.copy (i32.const 50370) (i32.const 41271) (i32.const 1406)) + ) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) +) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 124) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 124) (i32.const 1517) (i32.const 9)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 1517) (i32.const 2132) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 2132) (i32.const 2827) (i32.const 10)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 2827) (i32.const 2921) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 2921) (i32.const 3538) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 3538) (i32.const 3786) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 3786) (i32.const 4042) (i32.const 97)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 4042) (i32.const 4651) (i32.const 99)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 4651) (i32.const 5057) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 5057) (i32.const 5109) (i32.const 99)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 5109) (i32.const 5291) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 5291) (i32.const 5524) (i32.const 72)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 5524) (i32.const 5691) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 5691) (i32.const 6552) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 6552) (i32.const 7133) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 7133) (i32.const 7665) (i32.const 99)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 7665) (i32.const 8314) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 8314) (i32.const 8360) (i32.const 62)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 8360) (i32.const 8793) (i32.const 86)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 8793) (i32.const 8979) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 8979) (i32.const 9373) (i32.const 79)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 9373) (i32.const 9518) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 9518) (i32.const 9934) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 9934) (i32.const 10087) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 10087) (i32.const 10206) (i32.const 5)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 10206) (i32.const 10230) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 10230) (i32.const 10249) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 10249) (i32.const 11148) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 11148) (i32.const 11356) (i32.const 74)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 11356) (i32.const 11380) (i32.const 93)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 11380) (i32.const 11939) (i32.const 74)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 11939) (i32.const 12159) (i32.const 68)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 12159) (i32.const 12575) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 12575) (i32.const 12969) (i32.const 79)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 12969) (i32.const 13114) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 13114) (i32.const 14133) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 14133) (i32.const 14404) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 14404) (i32.const 14428) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 14428) (i32.const 14458) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 14458) (i32.const 14580) (i32.const 32)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 14580) (i32.const 14777) (i32.const 89)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 14777) (i32.const 15124) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 15124) (i32.const 15126) (i32.const 36)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 15126) (i32.const 15192) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 15192) (i32.const 15871) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 15871) (i32.const 15998) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 15998) (i32.const 17017) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17017) (i32.const 17288) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17288) (i32.const 17312) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17312) (i32.const 17342) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17342) (i32.const 17464) (i32.const 32)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17464) (i32.const 17661) (i32.const 89)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17661) (i32.const 17727) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17727) (i32.const 17733) (i32.const 5)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17733) (i32.const 17893) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 17893) (i32.const 18553) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18553) (i32.const 18744) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18744) (i32.const 18801) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18801) (i32.const 18825) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18825) (i32.const 18876) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18876) (i32.const 18885) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18885) (i32.const 18904) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18904) (i32.const 19567) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 19567) (i32.const 20403) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 20403) (i32.const 21274) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 21274) (i32.const 21364) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 21364) (i32.const 21468) (i32.const 74)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 21468) (i32.const 21492) (i32.const 93)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 21492) (i32.const 22051) (i32.const 74)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 22051) (i32.const 22480) (i32.const 68)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 22480) (i32.const 22685) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 22685) (i32.const 22694) (i32.const 68)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 22694) (i32.const 22821) (i32.const 10)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 22821) (i32.const 22869) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 22869) (i32.const 24107) (i32.const 97)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 24107) (i32.const 24111) (i32.const 37)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 24111) (i32.const 24236) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 24236) (i32.const 24348) (i32.const 72)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 24348) (i32.const 24515) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 24515) (i32.const 24900) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 24900) (i32.const 25136) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 25136) (i32.const 25182) (i32.const 85)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 25182) (i32.const 25426) (i32.const 68)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 25426) (i32.const 25613) (i32.const 89)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 25613) (i32.const 25830) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 25830) (i32.const 26446) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 26446) (i32.const 26517) (i32.const 10)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 26517) (i32.const 27468) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 27468) (i32.const 27503) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 27503) (i32.const 27573) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 27573) (i32.const 28245) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 28245) (i32.const 28280) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 28280) (i32.const 29502) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 29502) (i32.const 29629) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 29629) (i32.const 30387) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 30387) (i32.const 30646) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 30646) (i32.const 31066) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31066) (i32.const 31131) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31131) (i32.const 31322) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31322) (i32.const 31379) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31379) (i32.const 31403) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31403) (i32.const 31454) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31454) (i32.const 31463) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31463) (i32.const 31482) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31482) (i32.const 31649) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31649) (i32.const 31978) (i32.const 72)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 31978) (i32.const 32145) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 32145) (i32.const 32530) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 32530) (i32.const 32766) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 32766) (i32.const 32812) (i32.const 85)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 32812) (i32.const 33056) (i32.const 68)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 33056) (i32.const 33660) (i32.const 89)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 33660) (i32.const 33752) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 33752) (i32.const 33775) (i32.const 36)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 33775) (i32.const 33778) (i32.const 32)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 33778) (i32.const 34603) (i32.const 9)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 34603) (i32.const 35218) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 35218) (i32.const 35372) (i32.const 10)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 35372) (i32.const 35486) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 35486) (i32.const 35605) (i32.const 5)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 35605) (i32.const 35629) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 35629) (i32.const 35648) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 35648) (i32.const 36547) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 36547) (i32.const 36755) (i32.const 74)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 36755) (i32.const 36767) (i32.const 93)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 36767) (i32.const 36810) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 36810) (i32.const 36839) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 36839) (i32.const 37444) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 37444) (i32.const 38060) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 38060) (i32.const 38131) (i32.const 10)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 38131) (i32.const 39082) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 39082) (i32.const 39117) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 39117) (i32.const 39187) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 39187) (i32.const 39859) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 39859) (i32.const 39894) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 39894) (i32.const 40257) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 40257) (i32.const 40344) (i32.const 89)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 40344) (i32.const 40371) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 40371) (i32.const 40804) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 40804) (i32.const 40909) (i32.const 5)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 40909) (i32.const 42259) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 42259) (i32.const 42511) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 42511) (i32.const 42945) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 42945) (i32.const 43115) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 43115) (i32.const 43306) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 43306) (i32.const 43363) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 43363) (i32.const 43387) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 43387) (i32.const 43438) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 43438) (i32.const 43447) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 43447) (i32.const 43466) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 43466) (i32.const 44129) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 44129) (i32.const 44958) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 44958) (i32.const 45570) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 45570) (i32.const 45575) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 45575) (i32.const 45640) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 45640) (i32.const 45742) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 45742) (i32.const 45832) (i32.const 72)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 45832) (i32.const 45999) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 45999) (i32.const 46384) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 46384) (i32.const 46596) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 46596) (i32.const 46654) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 46654) (i32.const 47515) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 47515) (i32.const 47620) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 47620) (i32.const 47817) (i32.const 79)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 47817) (i32.const 47951) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 47951) (i32.const 48632) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 48632) (i32.const 48699) (i32.const 97)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 48699) (i32.const 48703) (i32.const 37)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 48703) (i32.const 49764) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 49764) (i32.const 49955) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 49955) (i32.const 50012) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 50012) (i32.const 50036) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 50036) (i32.const 50087) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 50087) (i32.const 50096) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 50096) (i32.const 50115) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 50115) (i32.const 50370) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 50370) (i32.const 51358) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 51358) (i32.const 51610) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 51610) (i32.const 51776) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 51776) (i32.const 51833) (i32.const 89)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 51833) (i32.const 52895) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 52895) (i32.const 53029) (i32.const 97)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 53029) (i32.const 53244) (i32.const 68)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 53244) (i32.const 54066) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 54066) (i32.const 54133) (i32.const 97)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 54133) (i32.const 54137) (i32.const 37)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 54137) (i32.const 55198) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 55198) (i32.const 55389) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 55389) (i32.const 55446) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 55446) (i32.const 55470) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 55470) (i32.const 55521) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 55521) (i32.const 55530) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 55530) (i32.const 55549) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 55549) (i32.const 56212) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 56212) (i32.const 57048) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 57048) (i32.const 58183) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 58183) (i32.const 58202) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 58202) (i32.const 58516) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 58516) (i32.const 58835) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 58835) (i32.const 58855) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 58855) (i32.const 59089) (i32.const 95)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 59089) (i32.const 59145) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 59145) (i32.const 59677) (i32.const 99)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 59677) (i32.const 60134) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60134) (i32.const 60502) (i32.const 89)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60502) (i32.const 60594) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60594) (i32.const 60617) (i32.const 36)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60617) (i32.const 60618) (i32.const 32)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60618) (i32.const 60777) (i32.const 42)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60777) (i32.const 60834) (i32.const 76)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60834) (i32.const 60858) (i32.const 57)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60858) (i32.const 60909) (i32.const 59)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60909) (i32.const 60918) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60918) (i32.const 60937) (i32.const 41)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 60937) (i32.const 61600) (i32.const 83)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 61600) (i32.const 62436) (i32.const 96)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 62436) (i32.const 63307) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63307) (i32.const 63397) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63397) (i32.const 63501) (i32.const 74)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63501) (i32.const 63525) (i32.const 93)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63525) (i32.const 63605) (i32.const 74)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63605) (i32.const 63704) (i32.const 100)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63704) (i32.const 63771) (i32.const 97)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63771) (i32.const 63775) (i32.const 37)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 63775) (i32.const 64311) (i32.const 77)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 64311) (i32.const 64331) (i32.const 26)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 64331) (i32.const 64518) (i32.const 92)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 64518) (i32.const 64827) (i32.const 11)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 64827) (i32.const 64834) (i32.const 26)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 64834) (i32.const 65536) (i32.const 0)) + (i32.const -1)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/memory_fill.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/memory_fill.wast new file mode 100644 index 000000000..caca80b5d --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/memory_fill.wast @@ -0,0 +1,685 @@ +;; +;; Generated by ../meta/generate_memory_fill.js +;; + +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0xFF00) (i32.const 0x55) (i32.const 256)))) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 65280) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 65280) (i32.const 65536) (i32.const 85)) + (i32.const -1)) +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0xFF00) (i32.const 0x55) (i32.const 257)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0xFFFFFF00) (i32.const 0x55) (i32.const 257)))) +(assert_trap (invoke "test") "out of bounds memory access") + +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0x12) (i32.const 0x55) (i32.const 0)))) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 65536) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0x10000) (i32.const 0x55) (i32.const 0)))) +(invoke "test") + +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0x20000) (i32.const 0x55) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0x1) (i32.const 0xAA) (i32.const 0xFFFE)))) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 1) (i32.const 65535) (i32.const 170)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 65535) (i32.const 65536) (i32.const 0)) + (i32.const -1)) + +(module + (memory 1 1) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "test") + (memory.fill (i32.const 0x12) (i32.const 0x55) (i32.const 10)) + (memory.fill (i32.const 0x15) (i32.const 0xAA) (i32.const 4)))) +(invoke "test") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 18) (i32.const 0)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 18) (i32.const 21) (i32.const 85)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 21) (i32.const 25) (i32.const 170)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 25) (i32.const 28) (i32.const 85)) + (i32.const -1)) +(assert_return (invoke "checkRange" (i32.const 28) (i32.const 65536) (i32.const 0)) + (i32.const -1)) +(assert_invalid + (module + (func (export "testfn") + (memory.fill (i32.const 10) (i32.const 20) (i32.const 30)))) + "unknown memory 0") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i32.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f32.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (i64.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f32.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f32.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f32.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f32.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (i64.const 20) (f64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f64.const 20) (i32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f64.const 20) (f32.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f64.const 20) (i64.const 30)))) + "type mismatch") + +(assert_invalid + (module + (memory 1 1) + (func (export "testfn") + (memory.fill (f64.const 10) (f64.const 20) (f64.const 30)))) + "type mismatch") + +(module + (memory 1 1 ) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $val i32) (param $len i32) + (memory.fill (local.get $offs) (local.get $val) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65280) (i32.const 37) (i32.const 512)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 1 ) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $val i32) (param $len i32) + (memory.fill (local.get $offs) (local.get $val) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65279) (i32.const 37) (i32.const 514)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 1 ) + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $val i32) (param $len i32) + (memory.fill (local.get $offs) (local.get $val) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65279) (i32.const 37) (i32.const 4294967295)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/memory_grow.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/memory_grow.wast new file mode 100644 index 000000000..aa56297d2 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/memory_grow.wast @@ -0,0 +1,379 @@ +(module + (memory 0) + + (func (export "load_at_zero") (result i32) (i32.load (i32.const 0))) + (func (export "store_at_zero") (i32.store (i32.const 0) (i32.const 2))) + + (func (export "load_at_page_size") (result i32) (i32.load (i32.const 0x10000))) + (func (export "store_at_page_size") (i32.store (i32.const 0x10000) (i32.const 3))) + + (func (export "grow") (param $sz i32) (result i32) (memory.grow (local.get $sz))) + (func (export "size") (result i32) (memory.size)) +) + +(assert_return (invoke "size") (i32.const 0)) +(assert_trap (invoke "store_at_zero") "out of bounds memory access") +(assert_trap (invoke "load_at_zero") "out of bounds memory access") +(assert_trap (invoke "store_at_page_size") "out of bounds memory access") +(assert_trap (invoke "load_at_page_size") "out of bounds memory access") +(assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "size") (i32.const 1)) +(assert_return (invoke "load_at_zero") (i32.const 0)) +(assert_return (invoke "store_at_zero")) +(assert_return (invoke "load_at_zero") (i32.const 2)) +(assert_trap (invoke "store_at_page_size") "out of bounds memory access") +(assert_trap (invoke "load_at_page_size") "out of bounds memory access") +(assert_return (invoke "grow" (i32.const 4)) (i32.const 1)) +(assert_return (invoke "size") (i32.const 5)) +(assert_return (invoke "load_at_zero") (i32.const 2)) +(assert_return (invoke "store_at_zero")) +(assert_return (invoke "load_at_zero") (i32.const 2)) +(assert_return (invoke "load_at_page_size") (i32.const 0)) +(assert_return (invoke "store_at_page_size")) +(assert_return (invoke "load_at_page_size") (i32.const 3)) + + +(module + (memory 0) + (func (export "grow") (param i32) (result i32) (memory.grow (local.get 0))) +) + +(assert_return (invoke "grow" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 800)) (i32.const 3)) +(assert_return (invoke "grow" (i32.const 0x10000)) (i32.const -1)) +(assert_return (invoke "grow" (i32.const 64736)) (i32.const -1)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 803)) + +(module + (memory 0 10) + (func (export "grow") (param i32) (result i32) (memory.grow (local.get 0))) +) + +(assert_return (invoke "grow" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "grow" (i32.const 6)) (i32.const 4)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 10)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "grow" (i32.const 0x10000)) (i32.const -1)) + +;; Test that newly allocated memory (program start and memory.grow) is zeroed + +(module + (memory 1) + (func (export "grow") (param i32) (result i32) + (memory.grow (local.get 0)) + ) + (func (export "check-memory-zero") (param i32 i32) (result i32) + (local i32) + (local.set 2 (i32.const 1)) + (block + (loop + (local.set 2 (i32.load8_u (local.get 0))) + (br_if 1 (i32.ne (local.get 2) (i32.const 0))) + (br_if 1 (i32.ge_u (local.get 0) (local.get 1))) + (local.set 0 (i32.add (local.get 0) (i32.const 1))) + (br_if 0 (i32.le_u (local.get 0) (local.get 1))) + ) + ) + (local.get 2) + ) +) + +(assert_return (invoke "check-memory-zero" (i32.const 0) (i32.const 0xffff)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "check-memory-zero" (i32.const 0x10000) (i32.const 0x1_ffff)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "check-memory-zero" (i32.const 0x20000) (i32.const 0x2_ffff)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 3)) +(assert_return (invoke "check-memory-zero" (i32.const 0x30000) (i32.const 0x3_ffff)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 4)) +(assert_return (invoke "check-memory-zero" (i32.const 0x40000) (i32.const 0x4_ffff)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 5)) +(assert_return (invoke "check-memory-zero" (i32.const 0x50000) (i32.const 0x5_ffff)) (i32.const 0)) + +;; As the argument of control constructs and instructions + +(module + (memory 1) + + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (memory.grow (i32.const 0)))) + ) + + (func (export "as-br_if-cond") + (block (br_if 0 (memory.grow (i32.const 0)))) + ) + (func (export "as-br_if-value") (result i32) + (block (result i32) + (drop (br_if 0 (memory.grow (i32.const 0)) (i32.const 1))) (i32.const 7) + ) + ) + (func (export "as-br_if-value-cond") (result i32) + (block (result i32) + (drop (br_if 0 (i32.const 6) (memory.grow (i32.const 0)))) (i32.const 7) + ) + ) + + (func (export "as-br_table-index") + (block (br_table 0 0 0 (memory.grow (i32.const 0)))) + ) + (func (export "as-br_table-value") (result i32) + (block (result i32) + (br_table 0 0 0 (memory.grow (i32.const 0)) (i32.const 1)) (i32.const 7) + ) + ) + (func (export "as-br_table-value-index") (result i32) + (block (result i32) + (br_table 0 0 (i32.const 6) (memory.grow (i32.const 0))) (i32.const 7) + ) + ) + + (func (export "as-return-value") (result i32) + (return (memory.grow (i32.const 0))) + ) + + (func (export "as-if-cond") (result i32) + (if (result i32) (memory.grow (i32.const 0)) + (then (i32.const 0)) (else (i32.const 1)) + ) + ) + (func (export "as-if-then") (result i32) + (if (result i32) (i32.const 1) + (then (memory.grow (i32.const 0))) (else (i32.const 0)) + ) + ) + (func (export "as-if-else") (result i32) + (if (result i32) (i32.const 0) + (then (i32.const 0)) (else (memory.grow (i32.const 0))) + ) + ) + + (func (export "as-select-first") (param i32 i32) (result i32) + (select (memory.grow (i32.const 0)) (local.get 0) (local.get 1)) + ) + (func (export "as-select-second") (param i32 i32) (result i32) + (select (local.get 0) (memory.grow (i32.const 0)) (local.get 1)) + ) + (func (export "as-select-cond") (result i32) + (select (i32.const 0) (i32.const 1) (memory.grow (i32.const 0))) + ) + + (func $f (param i32 i32 i32) (result i32) (i32.const -1)) + (func (export "as-call-first") (result i32) + (call $f (memory.grow (i32.const 0)) (i32.const 2) (i32.const 3)) + ) + (func (export "as-call-mid") (result i32) + (call $f (i32.const 1) (memory.grow (i32.const 0)) (i32.const 3)) + ) + (func (export "as-call-last") (result i32) + (call $f (i32.const 1) (i32.const 2) (memory.grow (i32.const 0))) + ) + + (type $sig (func (param i32 i32 i32) (result i32))) + (table funcref (elem $f)) + (func (export "as-call_indirect-first") (result i32) + (call_indirect (type $sig) + (memory.grow (i32.const 0)) (i32.const 2) (i32.const 3) (i32.const 0) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (call_indirect (type $sig) + (i32.const 1) (memory.grow (i32.const 0)) (i32.const 3) (i32.const 0) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (call_indirect (type $sig) + (i32.const 1) (i32.const 2) (memory.grow (i32.const 0)) (i32.const 0) + ) + ) + (func (export "as-call_indirect-index") (result i32) + (call_indirect (type $sig) + (i32.const 1) (i32.const 2) (i32.const 3) (memory.grow (i32.const 0)) + ) + ) + + (func (export "as-local.set-value") (local i32) + (local.set 0 (memory.grow (i32.const 0))) + ) + (func (export "as-local.tee-value") (result i32) (local i32) + (local.tee 0 (memory.grow (i32.const 0))) + ) + (global $g (mut i32) (i32.const 0)) + (func (export "as-global.set-value") (local i32) + (global.set $g (memory.grow (i32.const 0))) + ) + + (func (export "as-load-address") (result i32) + (i32.load (memory.grow (i32.const 0))) + ) + (func (export "as-loadN-address") (result i32) + (i32.load8_s (memory.grow (i32.const 0))) + ) + + (func (export "as-store-address") + (i32.store (memory.grow (i32.const 0)) (i32.const 7)) + ) + (func (export "as-store-value") + (i32.store (i32.const 2) (memory.grow (i32.const 0))) + ) + + (func (export "as-storeN-address") + (i32.store8 (memory.grow (i32.const 0)) (i32.const 7)) + ) + (func (export "as-storeN-value") + (i32.store16 (i32.const 2) (memory.grow (i32.const 0))) + ) + + (func (export "as-unary-operand") (result i32) + (i32.clz (memory.grow (i32.const 0))) + ) + + (func (export "as-binary-left") (result i32) + (i32.add (memory.grow (i32.const 0)) (i32.const 10)) + ) + (func (export "as-binary-right") (result i32) + (i32.sub (i32.const 10) (memory.grow (i32.const 0))) + ) + + (func (export "as-test-operand") (result i32) + (i32.eqz (memory.grow (i32.const 0))) + ) + + (func (export "as-compare-left") (result i32) + (i32.le_s (memory.grow (i32.const 0)) (i32.const 10)) + ) + (func (export "as-compare-right") (result i32) + (i32.ne (i32.const 10) (memory.grow (i32.const 0))) + ) + + (func (export "as-memory.grow-size") (result i32) + (memory.grow (memory.grow (i32.const 0))) + ) +) + +(assert_return (invoke "as-br-value") (i32.const 1)) + +(assert_return (invoke "as-br_if-cond")) +(assert_return (invoke "as-br_if-value") (i32.const 1)) +(assert_return (invoke "as-br_if-value-cond") (i32.const 6)) + +(assert_return (invoke "as-br_table-index")) +(assert_return (invoke "as-br_table-value") (i32.const 1)) +(assert_return (invoke "as-br_table-value-index") (i32.const 6)) + +(assert_return (invoke "as-return-value") (i32.const 1)) + +(assert_return (invoke "as-if-cond") (i32.const 0)) +(assert_return (invoke "as-if-then") (i32.const 1)) +(assert_return (invoke "as-if-else") (i32.const 1)) + +(assert_return (invoke "as-select-first" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-select-second" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-select-cond") (i32.const 0)) + +(assert_return (invoke "as-call-first") (i32.const -1)) +(assert_return (invoke "as-call-mid") (i32.const -1)) +(assert_return (invoke "as-call-last") (i32.const -1)) + +(assert_return (invoke "as-call_indirect-first") (i32.const -1)) +(assert_return (invoke "as-call_indirect-mid") (i32.const -1)) +(assert_return (invoke "as-call_indirect-last") (i32.const -1)) +(assert_trap (invoke "as-call_indirect-index") "undefined element") + +(assert_return (invoke "as-local.set-value")) +(assert_return (invoke "as-local.tee-value") (i32.const 1)) +(assert_return (invoke "as-global.set-value")) + +(assert_return (invoke "as-load-address") (i32.const 0)) +(assert_return (invoke "as-loadN-address") (i32.const 0)) +(assert_return (invoke "as-store-address")) +(assert_return (invoke "as-store-value")) +(assert_return (invoke "as-storeN-address")) +(assert_return (invoke "as-storeN-value")) + +(assert_return (invoke "as-unary-operand") (i32.const 31)) + +(assert_return (invoke "as-binary-left") (i32.const 11)) +(assert_return (invoke "as-binary-right") (i32.const 9)) + +(assert_return (invoke "as-test-operand") (i32.const 0)) + +(assert_return (invoke "as-compare-left") (i32.const 1)) +(assert_return (invoke "as-compare-right") (i32.const 1)) + +(assert_return (invoke "as-memory.grow-size") (i32.const 1)) + + +(assert_invalid + (module + (memory 0) + (func $type-size-empty-vs-i32 (result i32) + (memory.grow) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-size-empty-vs-i32-in-block (result i32) + (i32.const 0) + (block (result i32) (memory.grow)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-size-empty-vs-i32-in-loop (result i32) + (i32.const 0) + (loop (result i32) (memory.grow)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-size-empty-vs-i32-in-then (result i32) + (i32.const 0) (i32.const 0) + (if (result i32) (then (memory.grow))) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (memory 1) + (func $type-size-f32-vs-i32 (result i32) + (memory.grow (f32.const 0)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (memory 1) + (func $type-result-i32-vs-empty + (memory.grow (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-result-i32-vs-f32 (result f32) + (memory.grow (i32.const 0)) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/memory_init.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/memory_init.wast new file mode 100644 index 000000000..c647079d9 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/memory_init.wast @@ -0,0 +1,951 @@ +;; +;; Generated by ../meta/generate_memory_init.js +;; + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i32.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (nop)) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i32.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (memory.init 1 (i32.const 7) (i32.const 0) (i32.const 4))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 6)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i32.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (memory.init 3 (i32.const 15) (i32.const 1) (i32.const 3))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) + +(module + (memory (export "memory0") 1 1) + (data (i32.const 2) "\03\01\04\01") + (data "\02\07\01\08") + (data (i32.const 12) "\07\05\02\03\06") + (data "\05\09\02\07\06") + (func (export "test") + (memory.init 1 (i32.const 7) (i32.const 0) (i32.const 4)) + (data.drop 1) + (memory.init 3 (i32.const 15) (i32.const 1) (i32.const 3)) + (data.drop 3) + (memory.copy (i32.const 20) (i32.const 15) (i32.const 5)) + (memory.copy (i32.const 21) (i32.const 29) (i32.const 1)) + (memory.copy (i32.const 24) (i32.const 10) (i32.const 1)) + (memory.copy (i32.const 13) (i32.const 11) (i32.const 4)) + (memory.copy (i32.const 19) (i32.const 20) (i32.const 5))) + (func (export "load8_u") (param i32) (result i32) + (i32.load8_u (local.get 0)))) + +(invoke "test") + +(assert_return (invoke "load8_u" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "load8_u" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "load8_u" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 6)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "load8_u" (i32.const 10)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 13)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "load8_u" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "load8_u" (i32.const 17)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 18)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 19)) (i32.const 9)) +(assert_return (invoke "load8_u" (i32.const 20)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 21)) (i32.const 7)) +(assert_return (invoke "load8_u" (i32.const 22)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 23)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 24)) (i32.const 8)) +(assert_return (invoke "load8_u" (i32.const 25)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 26)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 27)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 28)) (i32.const 0)) +(assert_return (invoke "load8_u" (i32.const 29)) (i32.const 0)) +(assert_invalid + (module + (func (export "test") + (data.drop 0))) + "unknown memory 0") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (data.drop 4))) + "unknown data segment") + +(module + (memory 1) + (data "\37") + (func (export "test") + (data.drop 0) + (data.drop 0))) +(invoke "test") + +(module + (memory 1) + (data "\37") + (func (export "test") + (data.drop 0) + (memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 1)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1) + (data (i32.const 0) "\37") + (func (export "test") + (memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 1)))) +(assert_trap (invoke "test") "out of bounds") + +(assert_invalid + (module + (func (export "test") + (memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1)))) + "unknown memory 0") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1)))) + "unknown data segment 1") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i32.const 0) (i32.const 1)) + (memory.init 0 (i32.const 1) (i32.const 0) (i32.const 1)))) +(invoke "test") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1234) (i32.const 0) (i32.const 5)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1234) (i32.const 2) (i32.const 3)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 0xFFFE) (i32.const 1) (i32.const 3)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1234) (i32.const 4) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1234) (i32.const 1) (i32.const 0)))) +(invoke "test") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 0x10001) (i32.const 0) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 0x10000) (i32.const 0) (i32.const 0)))) +(invoke "test") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 0x10000) (i32.const 1) (i32.const 0)))) +(invoke "test") + +(module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 0x10001) (i32.const 4) (i32.const 0)))) +(assert_trap (invoke "test") "out of bounds") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i32.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f32.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (i64.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (memory 1) + (data "\37") + (func (export "test") + (memory.init 0 (f64.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(module + (memory 1 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65528) (i32.const 16)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65527) (i32.const 16)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65472) (i32.const 30)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65473) (i32.const 31)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 65528) (i32.const 4294967040)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) +(module + (memory 1 ) + (data "\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42\42") + + (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) + (loop $cont + (if (i32.eq (local.get $from) (local.get $to)) + (then + (return (i32.const -1)))) + (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) + (then + (local.set $from (i32.add (local.get $from) (i32.const 1))) + (br $cont)))) + (return (local.get $from))) + + (func (export "run") (param $offs i32) (param $len i32) + (memory.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 4294967292)) + "out of bounds") + +(assert_return (invoke "checkRange" (i32.const 0) (i32.const 1) (i32.const 0)) + (i32.const -1)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/ref_func.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/ref_func.wast new file mode 100644 index 000000000..e9033a3b0 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/ref_func.wast @@ -0,0 +1,86 @@ +(module + (func (export "f") (param $x i32) (result i32) (local.get $x)) +) +(register "M") + +(module + (func $f (import "M" "f") (param i32) (result i32)) + (func $g (param $x i32) (result i32) + (i32.add (local.get $x) (i32.const 1)) + ) + + (global anyref (ref.func $f)) + (global anyref (ref.func $g)) + (global funcref (ref.func $f)) + (global funcref (ref.func $g)) + (global $v (mut funcref) (ref.func $f)) + + (global funcref (ref.func $gf1)) + (global funcref (ref.func $gf2)) + (func (drop (ref.func $ff1)) (drop (ref.func $ff2))) + (elem declare func $gf1 $ff1) + (elem declare funcref (ref.func $gf2) (ref.func $ff2)) + (func $gf1) + (func $gf2) + (func $ff1) + (func $ff2) + + (func (export "is_null-f") (result i32) + (ref.is_null (ref.func $f)) + ) + (func (export "is_null-g") (result i32) + (ref.is_null (ref.func $g)) + ) + (func (export "is_null-v") (result i32) + (ref.is_null (global.get $v)) + ) + + (func (export "set-f") (global.set $v (ref.func $f))) + (func (export "set-g") (global.set $v (ref.func $g))) + + (table $t 1 funcref) + (elem declare func $f $g) + + (func (export "call-f") (param $x i32) (result i32) + (table.set $t (i32.const 0) (ref.func $f)) + (call_indirect $t (param i32) (result i32) (local.get $x) (i32.const 0)) + ) + (func (export "call-g") (param $x i32) (result i32) + (table.set $t (i32.const 0) (ref.func $g)) + (call_indirect $t (param i32) (result i32) (local.get $x) (i32.const 0)) + ) + (func (export "call-v") (param $x i32) (result i32) + (table.set $t (i32.const 0) (global.get $v)) + (call_indirect $t (param i32) (result i32) (local.get $x) (i32.const 0)) + ) +) + +(assert_return (invoke "is_null-f") (i32.const 0)) +(assert_return (invoke "is_null-g") (i32.const 0)) +(assert_return (invoke "is_null-v") (i32.const 0)) + +(assert_return (invoke "call-f" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "call-g" (i32.const 4)) (i32.const 5)) +(assert_return (invoke "call-v" (i32.const 4)) (i32.const 4)) +(invoke "set-g") +(assert_return (invoke "call-v" (i32.const 4)) (i32.const 5)) +(invoke "set-f") +(assert_return (invoke "call-v" (i32.const 4)) (i32.const 4)) + +(assert_invalid + (module + (func $f (import "M" "f") (param i32) (result i32)) + (func $g (import "M" "g") (param i32) (result i32)) + (global funcref (ref.func 7)) + ) + "unknown function 7" +) + +(assert_invalid + (module (func $f) (global funcref (ref.func $f))) + "undeclared function reference" +) +(assert_invalid + (module (func $f (drop (ref.func $f)))) + "undeclared function reference" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/ref_is_null.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/ref_is_null.wast new file mode 100644 index 000000000..7a42ca7d5 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/ref_is_null.wast @@ -0,0 +1,62 @@ +(module + (func $f1 (export "nullref") (param $x nullref) (result i32) + (ref.is_null (local.get $x)) + ) + (func $f2 (export "anyref") (param $x anyref) (result i32) + (ref.is_null (local.get $x)) + ) + (func $f3 (export "funcref") (param $x funcref) (result i32) + (ref.is_null (local.get $x)) + ) + + (table $t1 2 nullref) + (table $t2 2 anyref) + (table $t3 2 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + + (func (export "init") (param $r anyref) + (table.set $t2 (i32.const 1) (local.get $r)) + ) + (func (export "deinit") + (table.set $t1 (i32.const 1) (ref.null)) + (table.set $t2 (i32.const 1) (ref.null)) + (table.set $t3 (i32.const 1) (ref.null)) + ) + + (func (export "nullref-elem") (param $x i32) (result i32) + (call $f1 (table.get $t1 (local.get $x))) + ) + (func (export "anyref-elem") (param $x i32) (result i32) + (call $f2 (table.get $t2 (local.get $x))) + ) + (func (export "funcref-elem") (param $x i32) (result i32) + (call $f3 (table.get $t3 (local.get $x))) + ) +) + +(assert_return (invoke "nullref" (ref.null)) (i32.const 1)) +(assert_return (invoke "anyref" (ref.null)) (i32.const 1)) +(assert_return (invoke "funcref" (ref.null)) (i32.const 1)) + +(assert_return (invoke "anyref" (ref.host 1)) (i32.const 0)) + +(invoke "init" (ref.host 0)) + +(assert_return (invoke "nullref-elem" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "anyref-elem" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) + +(assert_return (invoke "nullref-elem" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "anyref-elem" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 0)) + +(invoke "deinit") + +(assert_return (invoke "nullref-elem" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "anyref-elem" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) + +(assert_return (invoke "nullref-elem" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "anyref-elem" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 1)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/ref_null.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/ref_null.wast new file mode 100644 index 000000000..96cac314a --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/ref_null.wast @@ -0,0 +1,13 @@ +(module + (func (export "anyref") (result anyref) (ref.null)) + (func (export "funcref") (result funcref) (ref.null)) + (func (export "nullref") (result nullref) (ref.null)) + + (global anyref (ref.null)) + (global funcref (ref.null)) + (global nullref (ref.null)) +) + +(assert_return (invoke "anyref") (ref.null)) +(assert_return (invoke "funcref") (ref.null)) +(assert_return (invoke "nullref") (ref.null)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/select.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/select.wast new file mode 100644 index 000000000..267dd5738 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/select.wast @@ -0,0 +1,529 @@ +(module + ;; Auxiliary + (func $dummy) + (table $tab funcref (elem $dummy)) + (memory 1) + + (func (export "select-i32") (param i32 i32 i32) (result i32) + (select (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-i64") (param i64 i64 i32) (result i64) + (select (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-f32") (param f32 f32 i32) (result f32) + (select (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-f64") (param f64 f64 i32) (result f64) + (select (local.get 0) (local.get 1) (local.get 2)) + ) + + (func (export "select-i32-t") (param i32 i32 i32) (result i32) + (select (result i32) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-i64-t") (param i64 i64 i32) (result i64) + (select (result i64) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-f32-t") (param f32 f32 i32) (result f32) + (select (result f32) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-f64-t") (param f64 f64 i32) (result f64) + (select (result f64) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-nullref") (param nullref nullref i32) (result nullref) + (select (result nullref) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-funcref") (param funcref funcref i32) (result funcref) + (select (result funcref) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-anyref") (param anyref anyref i32) (result anyref) + (select (result anyref) (local.get 0) (local.get 1) (local.get 2)) + ) + + (func (export "join-nullref") (param i32) (result anyref) + (select (result nullref) (ref.null) (ref.null) (local.get 0)) + ) + (func (export "join-funcref") (param i32) (result anyref) + (select (result funcref) + (table.get $tab (i32.const 0)) + (ref.null) + (local.get 0) + ) + ) + (func (export "join-anyref") (param i32) (param anyref) (result anyref) + (select (result anyref) + (table.get $tab (i32.const 0)) + (local.get 1) + (local.get 0) + ) + ) + + ;; Check that both sides of the select are evaluated + (func (export "select-trap-left") (param $cond i32) (result i32) + (select (unreachable) (i32.const 0) (local.get $cond)) + ) + (func (export "select-trap-right") (param $cond i32) (result i32) + (select (i32.const 0) (unreachable) (local.get $cond)) + ) + + (func (export "select-unreached") + (unreachable) (select) + (unreachable) (i32.const 0) (select) + (unreachable) (i32.const 0) (i32.const 0) (select) + (unreachable) (f32.const 0) (i32.const 0) (select) + (unreachable) + ) + + + ;; As the argument of control constructs and instructions + + (func (export "as-select-first") (param i32) (result i32) + (select (select (i32.const 0) (i32.const 1) (local.get 0)) (i32.const 2) (i32.const 3)) + ) + (func (export "as-select-mid") (param i32) (result i32) + (select (i32.const 2) (select (i32.const 0) (i32.const 1) (local.get 0)) (i32.const 3)) + ) + (func (export "as-select-last") (param i32) (result i32) + (select (i32.const 2) (i32.const 3) (select (i32.const 0) (i32.const 1) (local.get 0))) + ) + + (func (export "as-loop-first") (param i32) (result i32) + (loop (result i32) (select (i32.const 2) (i32.const 3) (local.get 0)) (call $dummy) (call $dummy)) + ) + (func (export "as-loop-mid") (param i32) (result i32) + (loop (result i32) (call $dummy) (select (i32.const 2) (i32.const 3) (local.get 0)) (call $dummy)) + ) + (func (export "as-loop-last") (param i32) (result i32) + (loop (result i32) (call $dummy) (call $dummy) (select (i32.const 2) (i32.const 3) (local.get 0))) + ) + + (func (export "as-if-condition") (param i32) + (select (i32.const 2) (i32.const 3) (local.get 0)) (if (then (call $dummy))) + ) + (func (export "as-if-then") (param i32) (result i32) + (if (result i32) (i32.const 1) (then (select (i32.const 2) (i32.const 3) (local.get 0))) (else (i32.const 4))) + ) + (func (export "as-if-else") (param i32) (result i32) + (if (result i32) (i32.const 0) (then (i32.const 2)) (else (select (i32.const 2) (i32.const 3) (local.get 0)))) + ) + + (func (export "as-br_if-first") (param i32) (result i32) + (block (result i32) (br_if 0 (select (i32.const 2) (i32.const 3) (local.get 0)) (i32.const 4))) + ) + (func (export "as-br_if-last") (param i32) (result i32) + (block (result i32) (br_if 0 (i32.const 2) (select (i32.const 2) (i32.const 3) (local.get 0)))) + ) + + (func (export "as-br_table-first") (param i32) (result i32) + (block (result i32) (select (i32.const 2) (i32.const 3) (local.get 0)) (i32.const 2) (br_table 0 0)) + ) + (func (export "as-br_table-last") (param i32) (result i32) + (block (result i32) (i32.const 2) (select (i32.const 2) (i32.const 3) (local.get 0)) (br_table 0 0)) + ) + + (func $func (param i32 i32) (result i32) (local.get 0)) + (type $check (func (param i32 i32) (result i32))) + (table $t funcref (elem $func)) + (func (export "as-call_indirect-first") (param i32) (result i32) + (block (result i32) + (call_indirect $t (type $check) + (select (i32.const 2) (i32.const 3) (local.get 0)) (i32.const 1) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-mid") (param i32) (result i32) + (block (result i32) + (call_indirect $t (type $check) + (i32.const 1) (select (i32.const 2) (i32.const 3) (local.get 0)) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-last") (param i32) (result i32) + (block (result i32) + (call_indirect $t (type $check) + (i32.const 1) (i32.const 4) (select (i32.const 2) (i32.const 3) (local.get 0)) + ) + ) + ) + + (func (export "as-store-first") (param i32) + (select (i32.const 0) (i32.const 4) (local.get 0)) (i32.const 1) (i32.store) + ) + (func (export "as-store-last") (param i32) + (i32.const 8) (select (i32.const 1) (i32.const 2) (local.get 0)) (i32.store) + ) + + (func (export "as-memory.grow-value") (param i32) (result i32) + (memory.grow (select (i32.const 1) (i32.const 2) (local.get 0))) + ) + + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "as-call-value") (param i32) (result i32) + (call $f (select (i32.const 1) (i32.const 2) (local.get 0))) + ) + (func (export "as-return-value") (param i32) (result i32) + (select (i32.const 1) (i32.const 2) (local.get 0)) (return) + ) + (func (export "as-drop-operand") (param i32) + (drop (select (i32.const 1) (i32.const 2) (local.get 0))) + ) + (func (export "as-br-value") (param i32) (result i32) + (block (result i32) (br 0 (select (i32.const 1) (i32.const 2) (local.get 0)))) + ) + (func (export "as-local.set-value") (param i32) (result i32) + (local i32) (local.set 0 (select (i32.const 1) (i32.const 2) (local.get 0))) (local.get 0) + ) + (func (export "as-local.tee-value") (param i32) (result i32) + (local.tee 0 (select (i32.const 1) (i32.const 2) (local.get 0))) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (param i32) (result i32) + (global.set $a (select (i32.const 1) (i32.const 2) (local.get 0))) + (global.get $a) + ) + (func (export "as-load-operand") (param i32) (result i32) + (i32.load (select (i32.const 0) (i32.const 4) (local.get 0))) + ) + + (func (export "as-unary-operand") (param i32) (result i32) + (i32.eqz (select (i32.const 0) (i32.const 1) (local.get 0))) + ) + (func (export "as-binary-operand") (param i32) (result i32) + (i32.mul + (select (i32.const 1) (i32.const 2) (local.get 0)) + (select (i32.const 1) (i32.const 2) (local.get 0)) + ) + ) + (func (export "as-test-operand") (param i32) (result i32) + (block (result i32) + (i32.eqz (select (i32.const 0) (i32.const 1) (local.get 0))) + ) + ) + + (func (export "as-compare-left") (param i32) (result i32) + (block (result i32) + (i32.le_s (select (i32.const 1) (i32.const 2) (local.get 0)) (i32.const 1)) + ) + ) + (func (export "as-compare-right") (param i32) (result i32) + (block (result i32) + (i32.ne (i32.const 1) (select (i32.const 0) (i32.const 1) (local.get 0))) + ) + ) + + (func (export "as-convert-operand") (param i32) (result i32) + (block (result i32) + (i32.wrap_i64 (select (i64.const 1) (i64.const 0) (local.get 0))) + ) + ) +) + +(assert_return (invoke "select-i32" (i32.const 1) (i32.const 2) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "select-i64" (i64.const 2) (i64.const 1) (i32.const 1)) (i64.const 2)) +(assert_return (invoke "select-f32" (f32.const 1) (f32.const 2) (i32.const 1)) (f32.const 1)) +(assert_return (invoke "select-f64" (f64.const 1) (f64.const 2) (i32.const 1)) (f64.const 1)) + +(assert_return (invoke "select-i32" (i32.const 1) (i32.const 2) (i32.const 0)) (i32.const 2)) +(assert_return (invoke "select-i32" (i32.const 2) (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "select-i64" (i64.const 2) (i64.const 1) (i32.const -1)) (i64.const 2)) +(assert_return (invoke "select-i64" (i64.const 2) (i64.const 1) (i32.const 0xf0f0f0f0)) (i64.const 2)) + +(assert_return (invoke "select-f32" (f32.const nan) (f32.const 1) (i32.const 1)) (f32.const nan)) +(assert_return (invoke "select-f32" (f32.const nan:0x20304) (f32.const 1) (i32.const 1)) (f32.const nan:0x20304)) +(assert_return (invoke "select-f32" (f32.const nan) (f32.const 1) (i32.const 0)) (f32.const 1)) +(assert_return (invoke "select-f32" (f32.const nan:0x20304) (f32.const 1) (i32.const 0)) (f32.const 1)) +(assert_return (invoke "select-f32" (f32.const 2) (f32.const nan) (i32.const 1)) (f32.const 2)) +(assert_return (invoke "select-f32" (f32.const 2) (f32.const nan:0x20304) (i32.const 1)) (f32.const 2)) +(assert_return (invoke "select-f32" (f32.const 2) (f32.const nan) (i32.const 0)) (f32.const nan)) +(assert_return (invoke "select-f32" (f32.const 2) (f32.const nan:0x20304) (i32.const 0)) (f32.const nan:0x20304)) + +(assert_return (invoke "select-f64" (f64.const nan) (f64.const 1) (i32.const 1)) (f64.const nan)) +(assert_return (invoke "select-f64" (f64.const nan:0x20304) (f64.const 1) (i32.const 1)) (f64.const nan:0x20304)) +(assert_return (invoke "select-f64" (f64.const nan) (f64.const 1) (i32.const 0)) (f64.const 1)) +(assert_return (invoke "select-f64" (f64.const nan:0x20304) (f64.const 1) (i32.const 0)) (f64.const 1)) +(assert_return (invoke "select-f64" (f64.const 2) (f64.const nan) (i32.const 1)) (f64.const 2)) +(assert_return (invoke "select-f64" (f64.const 2) (f64.const nan:0x20304) (i32.const 1)) (f64.const 2)) +(assert_return (invoke "select-f64" (f64.const 2) (f64.const nan) (i32.const 0)) (f64.const nan)) +(assert_return (invoke "select-f64" (f64.const 2) (f64.const nan:0x20304) (i32.const 0)) (f64.const nan:0x20304)) + +(assert_return (invoke "select-i32-t" (i32.const 1) (i32.const 2) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "select-i64-t" (i64.const 2) (i64.const 1) (i32.const 1)) (i64.const 2)) +(assert_return (invoke "select-f32-t" (f32.const 1) (f32.const 2) (i32.const 1)) (f32.const 1)) +(assert_return (invoke "select-f64-t" (f64.const 1) (f64.const 2) (i32.const 1)) (f64.const 1)) +(assert_return (invoke "select-nullref" (ref.null) (ref.null) (i32.const 1)) (ref.null)) +(assert_return (invoke "select-funcref" (ref.null) (ref.null) (i32.const 1)) (ref.null)) +(assert_return (invoke "select-anyref" (ref.host 1) (ref.host 2) (i32.const 1)) (ref.host 1)) + +(assert_return (invoke "select-i32-t" (i32.const 1) (i32.const 2) (i32.const 0)) (i32.const 2)) +(assert_return (invoke "select-i32-t" (i32.const 2) (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "select-i64-t" (i64.const 2) (i64.const 1) (i32.const -1)) (i64.const 2)) +(assert_return (invoke "select-i64-t" (i64.const 2) (i64.const 1) (i32.const 0xf0f0f0f0)) (i64.const 2)) +(assert_return (invoke "select-anyref" (ref.host 1) (ref.host 2) (i32.const 0)) (ref.host 2)) +(assert_return (invoke "select-anyref" (ref.host 2) (ref.host 1) (i32.const 0)) (ref.host 1)) + +(assert_return (invoke "select-f32-t" (f32.const nan) (f32.const 1) (i32.const 1)) (f32.const nan)) +(assert_return (invoke "select-f32-t" (f32.const nan:0x20304) (f32.const 1) (i32.const 1)) (f32.const nan:0x20304)) +(assert_return (invoke "select-f32-t" (f32.const nan) (f32.const 1) (i32.const 0)) (f32.const 1)) +(assert_return (invoke "select-f32-t" (f32.const nan:0x20304) (f32.const 1) (i32.const 0)) (f32.const 1)) +(assert_return (invoke "select-f32-t" (f32.const 2) (f32.const nan) (i32.const 1)) (f32.const 2)) +(assert_return (invoke "select-f32-t" (f32.const 2) (f32.const nan:0x20304) (i32.const 1)) (f32.const 2)) +(assert_return (invoke "select-f32-t" (f32.const 2) (f32.const nan) (i32.const 0)) (f32.const nan)) +(assert_return (invoke "select-f32-t" (f32.const 2) (f32.const nan:0x20304) (i32.const 0)) (f32.const nan:0x20304)) + +(assert_return (invoke "select-f64-t" (f64.const nan) (f64.const 1) (i32.const 1)) (f64.const nan)) +(assert_return (invoke "select-f64-t" (f64.const nan:0x20304) (f64.const 1) (i32.const 1)) (f64.const nan:0x20304)) +(assert_return (invoke "select-f64-t" (f64.const nan) (f64.const 1) (i32.const 0)) (f64.const 1)) +(assert_return (invoke "select-f64-t" (f64.const nan:0x20304) (f64.const 1) (i32.const 0)) (f64.const 1)) +(assert_return (invoke "select-f64-t" (f64.const 2) (f64.const nan) (i32.const 1)) (f64.const 2)) +(assert_return (invoke "select-f64-t" (f64.const 2) (f64.const nan:0x20304) (i32.const 1)) (f64.const 2)) +(assert_return (invoke "select-f64-t" (f64.const 2) (f64.const nan) (i32.const 0)) (f64.const nan)) +(assert_return (invoke "select-f64-t" (f64.const 2) (f64.const nan:0x20304) (i32.const 0)) (f64.const nan:0x20304)) + +(assert_return (invoke "join-nullref" (i32.const 1)) (ref.null)) +(assert_return (invoke "join-nullref" (i32.const 0)) (ref.null)) + +(assert_return (invoke "join-funcref" (i32.const 1)) (ref.func)) +(assert_return (invoke "join-funcref" (i32.const 0)) (ref.null)) + +(assert_return (invoke "join-anyref" (i32.const 1) (ref.host 1)) (ref.func)) +(assert_return (invoke "join-anyref" (i32.const 0) (ref.host 1)) (ref.host 1)) + +(assert_trap (invoke "select-trap-left" (i32.const 1)) "unreachable") +(assert_trap (invoke "select-trap-left" (i32.const 0)) "unreachable") +(assert_trap (invoke "select-trap-right" (i32.const 1)) "unreachable") +(assert_trap (invoke "select-trap-right" (i32.const 0)) "unreachable") + +(assert_return (invoke "as-select-first" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-select-first" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "as-select-mid" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-select-mid" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-select-last" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-select-last" (i32.const 1)) (i32.const 3)) + +(assert_return (invoke "as-loop-first" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-loop-first" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-loop-mid" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-loop-mid" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-loop-last" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-loop-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-if-condition" (i32.const 0))) +(assert_return (invoke "as-if-condition" (i32.const 1))) +(assert_return (invoke "as-if-then" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-if-then" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-if-else" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-if-else" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-br_if-first" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-br_if-first" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-br_if-last" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-br_if-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-br_table-first" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-br_table-first" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-br_table-last" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-br_table-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-call_indirect-first" (i32.const 0)) (i32.const 3)) +;;(assert_return (invoke "as-call_indirect-first" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-call_indirect-mid" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-call_indirect-mid" (i32.const 1)) (i32.const 1)) +(assert_trap (invoke "as-call_indirect-last" (i32.const 0)) "undefined element") +(assert_trap (invoke "as-call_indirect-last" (i32.const 1)) "undefined element") + +(assert_return (invoke "as-store-first" (i32.const 0))) +(assert_return (invoke "as-store-first" (i32.const 1))) +(assert_return (invoke "as-store-last" (i32.const 0))) +(assert_return (invoke "as-store-last" (i32.const 1))) + +(assert_return (invoke "as-memory.grow-value" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-memory.grow-value" (i32.const 1)) (i32.const 3)) + +(assert_return (invoke "as-call-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-call-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-return-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-return-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-drop-operand" (i32.const 0))) +(assert_return (invoke "as-drop-operand" (i32.const 1))) +(assert_return (invoke "as-br-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-br-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-local.set-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-local.set-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-local.tee-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-local.tee-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-global.set-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-global.set-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-load-operand" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-load-operand" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-unary-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-unary-operand" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-binary-operand" (i32.const 0)) (i32.const 4)) +(assert_return (invoke "as-binary-operand" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-test-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-test-operand" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-compare-left" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-compare-left" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-compare-right" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-compare-right" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-convert-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-convert-operand" (i32.const 1)) (i32.const 1)) + +(assert_invalid + (module (func $arity-0-implicit (select (nop) (nop) (i32.const 1)))) + "type mismatch" +) +(assert_invalid + (module (func $arity-0 (select (result) (nop) (nop) (i32.const 1)))) + "invalid result arity" +) +(assert_invalid + (module (func $arity-2 (result i32 i32) + (select (result i32 i32) + (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) + (i32.const 1) + ) + )) + "invalid result arity" +) + + +(assert_invalid + (module (func $type-nullref-implicit + (drop (select (ref.null) (ref.null) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-anyref-implicit (param $r anyref) + (drop (select (local.get $r) (local.get $r) (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-num-vs-num + (drop (select (i32.const 1) (i64.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-num-vs-num + (drop (select (i32.const 1) (f32.const 1.0) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-num-vs-num + (drop (select (i32.const 1) (f64.const 1.0) (i32.const 1))) + )) + "type mismatch" +) + + +(assert_invalid + (module + (func $type-1st-operand-empty + (select) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-2nd-operand-empty + (i32.const 0) (select) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-3rd-operand-empty + (i32.const 0) (i32.const 0) (select) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-1st-operand-empty-in-block + (i32.const 0) (i32.const 0) (i32.const 0) + (block (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-2nd-operand-empty-in-block + (i32.const 0) (i32.const 0) + (block (i32.const 0) (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-3rd-operand-empty-in-block + (i32.const 0) + (block (i32.const 0) (i32.const 0) (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-1st-operand-empty-in-loop + (i32.const 0) (i32.const 0) (i32.const 0) + (loop (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-2nd-operand-empty-in-loop + (i32.const 0) (i32.const 0) + (loop (i32.const 0) (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-3rd-operand-empty-in-loop + (i32.const 0) + (loop (i32.const 0) (i32.const 0) (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-1st-operand-empty-in-then + (i32.const 0) (i32.const 0) (i32.const 0) + (if (then (select) (drop))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-2nd-operand-empty-in-then + (i32.const 0) (i32.const 0) + (if (then (i32.const 0) (select) (drop))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-3rd-operand-empty-in-then + (i32.const 0) + (if (then (i32.const 0) (i32.const 0) (select) (drop))) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table-sub.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table-sub.wast new file mode 100644 index 000000000..66f183108 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table-sub.wast @@ -0,0 +1,31 @@ +(module + (table $t1 10 anyref) + (table $t2 10 funcref) + (elem $el funcref) + (func $f + (table.init $t1 $el (i32.const 0) (i32.const 1) (i32.const 2)) + (table.copy $t1 $t2 (i32.const 0) (i32.const 1) (i32.const 2)) + ) +) + +(assert_invalid + (module + (table $t1 10 funcref) + (table $t2 10 anyref) + (func $f + (table.copy $t1 $t2 (i32.const 0) (i32.const 1) (i32.const 2)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 10 funcref) + (elem $el anyref) + (func $f + (table.init $t $el (i32.const 0) (i32.const 1) (i32.const 2)) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_copy.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_copy.wast new file mode 100644 index 000000000..d0aa77b20 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_copy.wast @@ -0,0 +1,1595 @@ +;; +;; Generated by ../meta/generate_table_copy.js +;; + +(module + (func (export "ef0") (result i32) (i32.const 0)) + (func (export "ef1") (result i32) (i32.const 1)) + (func (export "ef2") (result i32) (i32.const 2)) + (func (export "ef3") (result i32) (i32.const 3)) + (func (export "ef4") (result i32) (i32.const 4)) +) +(register "a") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (nop)) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy (i32.const 13) (i32.const 2) (i32.const 3))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy (i32.const 25) (i32.const 15) (i32.const 2))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_return (invoke "check" (i32.const 25)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 26)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy (i32.const 13) (i32.const 25) (i32.const 3))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 15)) "uninitialized element") +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy (i32.const 20) (i32.const 22) (i32.const 4))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy (i32.const 25) (i32.const 1) (i32.const 3))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_return (invoke "check" (i32.const 26)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 27)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy (i32.const 10) (i32.const 12) (i32.const 7))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_return (invoke "check" (i32.const 10)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 11)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 12)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy (i32.const 12) (i32.const 10) (i32.const 7))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 13)) "uninitialized element") +(assert_return (invoke "check" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 17)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 18)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 28) (i32.const 1) (i32.const 3)) + )) + +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 0xFFFFFFFE) (i32.const 1) (i32.const 2)) + )) + +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 15) (i32.const 25) (i32.const 6)) + )) + +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 15) (i32.const 0xFFFFFFFE) (i32.const 2)) + )) + +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 15) (i32.const 25) (i32.const 0)) + )) + +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 30) (i32.const 15) (i32.const 0)) + )) + +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 31) (i32.const 15) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 15) (i32.const 30) (i32.const 0)) + )) + +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 15) (i32.const 31) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 30) (i32.const 30) (i32.const 0)) + )) + +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy (i32.const 31) (i32.const 31) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 0) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 24) (i32.const 0) (i32.const 16)) + "out of bounds") +(assert_return (invoke "test" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 7)) (i32.const 7)) +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 0) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 23) (i32.const 0) (i32.const 15)) + "out of bounds") +(assert_return (invoke "test" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 7)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 8)) (i32.const 8)) +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 24) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 24) (i32.const 16)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_return (invoke "test" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 7)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 23) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 23) (i32.const 15)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_return (invoke "test" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 24)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 8)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 11) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 24) (i32.const 11) (i32.const 16)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_return (invoke "test" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 12)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 14)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 15)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 16)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 17)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 18)) (i32.const 7)) +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 24) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 11) (i32.const 24) (i32.const 16)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_return (invoke "test" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 7)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 21) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 24) (i32.const 21) (i32.const 16)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_return (invoke "test" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 22)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 23)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 24)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 7)) +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 24) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 21) (i32.const 24) (i32.const 16)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_return (invoke "test" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 7)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 21) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $f10) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 21) (i32.const 21) (i32.const 16)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_return (invoke "test" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 22)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 23)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 24)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 8)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 9)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 10)) + +(module + (type (func (result i32))) + (table 128 128 funcref) + (elem (i32.const 112) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $f10 $f11 $f12 $f13 $f14 $f15) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 112) (i32.const 4294967264)) + "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_return (invoke "test" (i32.const 112)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 113)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 114)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 115)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 116)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 117)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 118)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 119)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 120)) (i32.const 8)) +(assert_return (invoke "test" (i32.const 121)) (i32.const 9)) +(assert_return (invoke "test" (i32.const 122)) (i32.const 10)) +(assert_return (invoke "test" (i32.const 123)) (i32.const 11)) +(assert_return (invoke "test" (i32.const 124)) (i32.const 12)) +(assert_return (invoke "test" (i32.const 125)) (i32.const 13)) +(assert_return (invoke "test" (i32.const 126)) (i32.const 14)) +(assert_return (invoke "test" (i32.const 127)) (i32.const 15)) + +(module + (type (func (result i32))) + (table 128 128 funcref) + (elem (i32.const 0) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $f10 $f11 $f12 $f13 $f14 $f15) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 112) (i32.const 0) (i32.const 4294967264)) + "out of bounds") +(assert_return (invoke "test" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 7)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 8)) (i32.const 8)) +(assert_return (invoke "test" (i32.const 9)) (i32.const 9)) +(assert_return (invoke "test" (i32.const 10)) (i32.const 10)) +(assert_return (invoke "test" (i32.const 11)) (i32.const 11)) +(assert_return (invoke "test" (i32.const 12)) (i32.const 12)) +(assert_return (invoke "test" (i32.const 13)) (i32.const 13)) +(assert_return (invoke "test" (i32.const 14)) (i32.const 14)) +(assert_return (invoke "test" (i32.const 15)) (i32.const 15)) +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 112)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 113)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 114)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 115)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 116)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 117)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 118)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 119)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 120)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 121)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 122)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 123)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 124)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 125)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 126)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 127)) "uninitialized element") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_fill.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_fill.wast new file mode 100644 index 000000000..7aff20246 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_fill.wast @@ -0,0 +1,153 @@ +(module + (table $t 10 anyref) + + (func (export "fill") (param $i i32) (param $r anyref) (param $n i32) + (table.fill $t (local.get $i) (local.get $r) (local.get $n)) + ) + + (func (export "get") (param $i i32) (result anyref) + (table.get $t (local.get $i)) + ) +) + +(assert_return (invoke "get" (i32.const 1)) (ref.null)) +(assert_return (invoke "get" (i32.const 2)) (ref.null)) +(assert_return (invoke "get" (i32.const 3)) (ref.null)) +(assert_return (invoke "get" (i32.const 4)) (ref.null)) +(assert_return (invoke "get" (i32.const 5)) (ref.null)) + +(assert_return (invoke "fill" (i32.const 2) (ref.host 1) (i32.const 3))) +(assert_return (invoke "get" (i32.const 1)) (ref.null)) +(assert_return (invoke "get" (i32.const 2)) (ref.host 1)) +(assert_return (invoke "get" (i32.const 3)) (ref.host 1)) +(assert_return (invoke "get" (i32.const 4)) (ref.host 1)) +(assert_return (invoke "get" (i32.const 5)) (ref.null)) + +(assert_return (invoke "fill" (i32.const 4) (ref.host 2) (i32.const 2))) +(assert_return (invoke "get" (i32.const 3)) (ref.host 1)) +(assert_return (invoke "get" (i32.const 4)) (ref.host 2)) +(assert_return (invoke "get" (i32.const 5)) (ref.host 2)) +(assert_return (invoke "get" (i32.const 6)) (ref.null)) + +(assert_return (invoke "fill" (i32.const 4) (ref.host 3) (i32.const 0))) +(assert_return (invoke "get" (i32.const 3)) (ref.host 1)) +(assert_return (invoke "get" (i32.const 4)) (ref.host 2)) +(assert_return (invoke "get" (i32.const 5)) (ref.host 2)) + +(assert_return (invoke "fill" (i32.const 8) (ref.host 4) (i32.const 2))) +(assert_return (invoke "get" (i32.const 7)) (ref.null)) +(assert_return (invoke "get" (i32.const 8)) (ref.host 4)) +(assert_return (invoke "get" (i32.const 9)) (ref.host 4)) + +(assert_return (invoke "fill" (i32.const 9) (ref.null) (i32.const 1))) +(assert_return (invoke "get" (i32.const 8)) (ref.host 4)) +(assert_return (invoke "get" (i32.const 9)) (ref.null)) + +(assert_return (invoke "fill" (i32.const 10) (ref.host 5) (i32.const 0))) +(assert_return (invoke "get" (i32.const 9)) (ref.null)) + +(assert_trap + (invoke "fill" (i32.const 8) (ref.host 6) (i32.const 3)) + "out of bounds" +) +(assert_return (invoke "get" (i32.const 7)) (ref.null)) +(assert_return (invoke "get" (i32.const 8)) (ref.host 4)) +(assert_return (invoke "get" (i32.const 9)) (ref.null)) + +(assert_trap + (invoke "fill" (i32.const 11) (ref.null) (i32.const 0)) + "out of bounds" +) + +(assert_trap + (invoke "fill" (i32.const 11) (ref.null) (i32.const 10)) + "out of bounds" +) + + +;; Type errors + +(assert_invalid + (module + (table $t 10 anyref) + (func $type-index-value-length-empty-vs-i32-i32 + (table.fill $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 anyref) + (func $type-index-empty-vs-i32 + (table.fill $t (ref.null) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 anyref) + (func $type-value-empty-vs + (table.fill $t (i32.const 1) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 anyref) + (func $type-length-empty-vs-i32 + (table.fill $t (i32.const 1) (ref.null)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 anyref) + (func $type-index-f32-vs-i32 + (table.fill $t (f32.const 1) (ref.null) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 funcref) + (func $type-value-vs-funcref (param $r anyref) + (table.fill $t (i32.const 1) (local.get $r) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 anyref) + (func $type-length-f32-vs-i32 + (table.fill $t (i32.const 1) (ref.null) (f32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t1 1 anyref) + (table $t2 1 funcref) + (func $type-value-anyref-vs-funcref-multi (param $r anyref) + (table.fill $t2 (i32.const 0) (local.get $r) (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 1 anyref) + (func $type-result-empty-vs-num (result i32) + (table.fill $t (i32.const 0) (ref.null) (i32.const 1)) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_get.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_get.wast new file mode 100644 index 000000000..c95fce723 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_get.wast @@ -0,0 +1,88 @@ +(module + (table $t2 2 anyref) + (table $t3 3 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + + (func (export "init") (param $r anyref) + (table.set $t2 (i32.const 1) (local.get $r)) + (table.set $t3 (i32.const 2) (table.get $t3 (i32.const 1))) + ) + + (func (export "get-anyref") (param $i i32) (result anyref) + (table.get $t2 (local.get $i)) + ) + (func $f3 (export "get-funcref") (param $i i32) (result funcref) + (table.get $t3 (local.get $i)) + ) + + (func (export "is_null-funcref") (param $i i32) (result i32) + (ref.is_null (call $f3 (local.get $i))) + ) +) + +(invoke "init" (ref.host 1)) + +(assert_return (invoke "get-anyref" (i32.const 0)) (ref.null)) +(assert_return (invoke "get-anyref" (i32.const 1)) (ref.host 1)) + +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null)) +(assert_return (invoke "is_null-funcref" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "is_null-funcref" (i32.const 2)) (i32.const 0)) + +(assert_trap (invoke "get-anyref" (i32.const 2)) "out of bounds") +(assert_trap (invoke "get-funcref" (i32.const 3)) "out of bounds") +(assert_trap (invoke "get-anyref" (i32.const -1)) "out of bounds") +(assert_trap (invoke "get-funcref" (i32.const -1)) "out of bounds") + + +;; Type errors + +(assert_invalid + (module + (table $t 10 anyref) + (func $type-index-empty-vs-i32 (result anyref) + (table.get $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 anyref) + (func $type-index-f32-vs-i32 (result anyref) + (table.get $t (f32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 10 anyref) + (func $type-result-anyref-vs-empty + (table.get $t (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 anyref) + (func $type-result-anyref-vs-funcref (result funcref) + (table.get $t (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t1 1 funcref) + (table $t2 1 anyref) + (func $type-result-anyref-vs-funcref-multi (result funcref) + (table.get $t2 (i32.const 0)) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_grow.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_grow.wast new file mode 100644 index 000000000..91fc096f4 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_grow.wast @@ -0,0 +1,173 @@ +(module + (table $t 0 anyref) + + (func (export "get") (param $i i32) (result anyref) (table.get $t (local.get $i))) + (func (export "set") (param $i i32) (param $r anyref) (table.set $t (local.get $i) (local.get $r))) + + (func (export "grow") (param $sz i32) (param $init anyref) (result i32) + (table.grow $t (local.get $init) (local.get $sz)) + ) + (func (export "size") (result i32) (table.size $t)) +) + +(assert_return (invoke "size") (i32.const 0)) +(assert_trap (invoke "set" (i32.const 0) (ref.host 2)) "out of bounds table access") +(assert_trap (invoke "get" (i32.const 0)) "out of bounds table access") + +(assert_return (invoke "grow" (i32.const 1) (ref.null)) (i32.const 0)) +(assert_return (invoke "size") (i32.const 1)) +(assert_return (invoke "get" (i32.const 0)) (ref.null)) +(assert_return (invoke "set" (i32.const 0) (ref.host 2))) +(assert_return (invoke "get" (i32.const 0)) (ref.host 2)) +(assert_trap (invoke "set" (i32.const 1) (ref.host 2)) "out of bounds table access") +(assert_trap (invoke "get" (i32.const 1)) "out of bounds table access") + +(assert_return (invoke "grow" (i32.const 4) (ref.host 3)) (i32.const 1)) +(assert_return (invoke "size") (i32.const 5)) +(assert_return (invoke "get" (i32.const 0)) (ref.host 2)) +(assert_return (invoke "set" (i32.const 0) (ref.host 2))) +(assert_return (invoke "get" (i32.const 0)) (ref.host 2)) +(assert_return (invoke "get" (i32.const 1)) (ref.host 3)) +(assert_return (invoke "get" (i32.const 4)) (ref.host 3)) +(assert_return (invoke "set" (i32.const 4) (ref.host 4))) +(assert_return (invoke "get" (i32.const 4)) (ref.host 4)) +(assert_trap (invoke "set" (i32.const 5) (ref.host 2)) "out of bounds table access") +(assert_trap (invoke "get" (i32.const 5)) "out of bounds table access") + + +;; Reject growing to size outside i32 value range +(module + (table $t 0x10 anyref) + (elem declare func $f) + (func $f (export "grow") (result i32) + (table.grow $t (ref.func $f) (i32.const 0xffff_fff0)) + ) +) + +(assert_return (invoke "grow") (i32.const -1)) + + +(module + (table $t 0 anyref) + (func (export "grow") (param i32) (result i32) + (table.grow $t (ref.null) (local.get 0)) + ) +) + +(assert_return (invoke "grow" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 800)) (i32.const 3)) + + +(module + (table $t 0 10 anyref) + (func (export "grow") (param i32) (result i32) + (table.grow $t (ref.null) (local.get 0)) + ) +) + +(assert_return (invoke "grow" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "grow" (i32.const 6)) (i32.const 4)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 10)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "grow" (i32.const 0x10000)) (i32.const -1)) + + +(module + (table $t 10 anyref) + (func (export "grow") (param i32) (result i32) + (table.grow $t (ref.null) (local.get 0)) + ) + (elem declare func 1) + (func (export "check-table-null") (param i32 i32) (result anyref) + (local anyref) + (local.set 2 (ref.func 1)) + (block + (loop + (local.set 2 (table.get $t (local.get 0))) + (br_if 1 (i32.eqz (ref.is_null (local.get 2)))) + (br_if 1 (i32.ge_u (local.get 0) (local.get 1))) + (local.set 0 (i32.add (local.get 0) (i32.const 1))) + (br_if 0 (i32.le_u (local.get 0) (local.get 1))) + ) + ) + (local.get 2) + ) +) + +(assert_return (invoke "check-table-null" (i32.const 0) (i32.const 9)) (ref.null)) +(assert_return (invoke "grow" (i32.const 10)) (i32.const 10)) +(assert_return (invoke "check-table-null" (i32.const 0) (i32.const 19)) (ref.null)) + + +;; Type errors + +(assert_invalid + (module + (table $t 0 anyref) + (func $type-init-size-empty-vs-i32-anyref (result i32) + (table.grow $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 anyref) + (func $type-size-empty-vs-i32 (result i32) + (table.grow $t (ref.null)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 anyref) + (func $type-init-empty-vs-anyref (result i32) + (table.grow $t (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 anyref) + (func $type-size-f32-vs-i32 (result i32) + (table.grow $t (ref.null) (f32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 funcref) + (func $type-init-anyref-vs-funcref (param $r anyref) (result i32) + (table.grow $t (local.get $r) (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 1 anyref) + (func $type-result-i32-vs-empty + (table.grow $t (ref.null) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 1 anyref) + (func $type-result-i32-vs-f32 (result f32) + (table.grow $t (ref.null) (i32.const 0)) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_init.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_init.wast new file mode 100644 index 000000000..80eaf065d --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_init.wast @@ -0,0 +1,1752 @@ +;; +;; Generated by ../meta/generate_table_init.js +;; + +(module + (func (export "ef0") (result i32) (i32.const 0)) + (func (export "ef1") (result i32) (i32.const 1)) + (func (export "ef2") (result i32) (i32.const 2)) + (func (export "ef3") (result i32) (i32.const 3)) + (func (export "ef4") (result i32) (i32.const 4)) +) +(register "a") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init 1 (i32.const 7) (i32.const 0) (i32.const 4))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_return (invoke "check" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 10)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init 3 (i32.const 15) (i32.const 1) (i32.const 3))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 9)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 17)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init 1 (i32.const 7) (i32.const 0) (i32.const 4)) + (elem.drop 1) + (table.init 3 (i32.const 15) (i32.const 1) (i32.const 3)) + (elem.drop 3) + (table.copy (i32.const 20) (i32.const 15) (i32.const 5)) + (table.copy (i32.const 21) (i32.const 29) (i32.const 1)) + (table.copy (i32.const 24) (i32.const 10) (i32.const 1)) + (table.copy (i32.const 13) (i32.const 11) (i32.const 4)) + (table.copy (i32.const 19) (i32.const 20) (i32.const 5))) + (func (export "check") (param i32) (result i32) + (call_indirect (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_return (invoke "check" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 10)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 13)) "uninitialized element") +(assert_return (invoke "check" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 17)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_return (invoke "check" (i32.const 19)) (i32.const 9)) +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_return (invoke "check" (i32.const 21)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_return (invoke "check" (i32.const 23)) (i32.const 8)) +(assert_return (invoke "check" (i32.const 24)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") +(assert_invalid + (module + (func (export "test") + (elem.drop 0))) + "unknown table 0") + +(assert_invalid + (module + (func (export "test") + (table.init 0 (i32.const 12) (i32.const 1) (i32.const 1)))) + "unknown table 0") + +(assert_invalid + (module + (elem funcref (ref.func 0)) + (func (result i32) (i32.const 0)) + (func (export "test") + (elem.drop 4))) + "unknown table 0") + +(assert_invalid + (module + (elem funcref (ref.func 0)) + (func (result i32) (i32.const 0)) + (func (export "test") + (table.init 4 (i32.const 12) (i32.const 1) (i32.const 1)))) + "unknown table 0") + + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (elem.drop 2) + )) +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 2 (i32.const 12) (i32.const 1) (i32.const 1)) + )) +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 1) (i32.const 1)) + (table.init 1 (i32.const 21) (i32.const 1) (i32.const 1)))) +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (elem.drop 1) + (elem.drop 1))) +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (elem.drop 1) + (table.init 1 (i32.const 12) (i32.const 1) (i32.const 1)))) +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 0) (i32.const 5)) + )) +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 2) (i32.const 3)) + )) +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 28) (i32.const 1) (i32.const 3)) + )) +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 4) (i32.const 0)) + )) +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 5) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 30) (i32.const 2) (i32.const 0)) + )) +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 31) (i32.const 2) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 30) (i32.const 4) (i32.const 0)) + )) +(invoke "test") + +(module + (table 30 30 funcref) + (elem (i32.const 2) 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (i32.const 12) 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 31) (i32.const 5) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 24) (i32.const 16)) "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 25) (i32.const 16)) "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 160 320 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 96) (i32.const 32)) "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 112)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 113)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 114)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 115)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 116)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 117)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 118)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 119)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 120)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 121)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 122)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 123)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 124)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 125)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 126)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 127)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 128)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 129)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 130)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 131)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 132)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 133)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 134)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 135)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 136)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 137)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 138)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 139)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 140)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 141)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 142)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 143)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 144)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 145)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 146)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 147)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 148)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 149)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 150)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 151)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 152)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 153)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 154)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 155)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 156)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 157)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 158)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 159)) "uninitialized element") + +(module + (type (func (result i32))) + (table 160 320 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 97) (i32.const 31)) "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 112)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 113)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 114)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 115)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 116)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 117)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 118)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 119)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 120)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 121)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 122)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 123)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 124)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 125)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 126)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 127)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 128)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 129)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 130)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 131)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 132)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 133)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 134)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 135)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 136)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 137)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 138)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 139)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 140)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 141)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 142)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 143)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 144)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 145)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 146)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 147)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 148)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 149)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 150)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 151)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 152)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 153)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 154)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 155)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 156)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 157)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 158)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 159)) "uninitialized element") + +(module + (type (func (result i32))) + (table 64 64 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 48) (i32.const 4294967280)) "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") + +(module + (type (func (result i32))) + (table 16 16 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 8) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 0) (i32.const 4294967292)) "out of bounds") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_set.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_set.wast new file mode 100644 index 000000000..848830e2f --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_set.wast @@ -0,0 +1,119 @@ +(module + (table $t2 1 anyref) + (table $t3 2 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + + (func (export "get-anyref") (param $i i32) (result anyref) + (table.get $t2 (local.get $i)) + ) + (func $f3 (export "get-funcref") (param $i i32) (result funcref) + (table.get $t3 (local.get $i)) + ) + + (func (export "set-anyref") (param $i i32) (param $r anyref) + (table.set $t2 (local.get $i) (local.get $r)) + ) + (func (export "set-funcref") (param $i i32) (param $r funcref) + (table.set $t3 (local.get $i) (local.get $r)) + ) + (func (export "set-funcref-from") (param $i i32) (param $j i32) + (table.set $t3 (local.get $i) (table.get $t3 (local.get $j))) + ) + + (func (export "is_null-funcref") (param $i i32) (result i32) + (ref.is_null (call $f3 (local.get $i))) + ) +) + +(assert_return (invoke "get-anyref" (i32.const 0)) (ref.null)) +(assert_return (invoke "set-anyref" (i32.const 0) (ref.host 1))) +(assert_return (invoke "get-anyref" (i32.const 0)) (ref.host 1)) +(assert_return (invoke "set-anyref" (i32.const 0) (ref.null))) +(assert_return (invoke "get-anyref" (i32.const 0)) (ref.null)) + +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null)) +(assert_return (invoke "set-funcref-from" (i32.const 0) (i32.const 1))) +(assert_return (invoke "is_null-funcref" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "set-funcref" (i32.const 0) (ref.null))) +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null)) + +(assert_trap (invoke "set-anyref" (i32.const 2) (ref.null)) "out of bounds") +(assert_trap (invoke "set-funcref" (i32.const 3) (ref.null)) "out of bounds") +(assert_trap (invoke "set-anyref" (i32.const -1) (ref.null)) "out of bounds") +(assert_trap (invoke "set-funcref" (i32.const -1) (ref.null)) "out of bounds") + +(assert_trap (invoke "set-anyref" (i32.const 2) (ref.host 0)) "out of bounds") +(assert_trap (invoke "set-funcref-from" (i32.const 3) (i32.const 1)) "out of bounds") +(assert_trap (invoke "set-anyref" (i32.const -1) (ref.host 0)) "out of bounds") +(assert_trap (invoke "set-funcref-from" (i32.const -1) (i32.const 1)) "out of bounds") + + +;; Type errors + +(assert_invalid + (module + (table $t 10 anyref) + (func $type-index-value-empty-vs-i32-anyref + (table.set $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 anyref) + (func $type-index-empty-vs-i32 + (table.set $t (ref.null)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 anyref) + (func $type-value-empty-vs-anyref + (table.set $t (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 anyref) + (func $type-size-f32-vs-i32 + (table.set $t (f32.const 1) (ref.null)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 funcref) + (func $type-value-anyref-vs-funcref (param $r anyref) + (table.set $t (i32.const 1) (local.get $r)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t1 1 anyref) + (table $t2 1 funcref) + (func $type-value-anyref-vs-funcref-multi (param $r anyref) + (table.set $t2 (i32.const 0) (local.get $r)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 10 anyref) + (func $type-result-empty-vs-num (result i32) + (table.set $t (i32.const 0) (ref.null)) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_size.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_size.wast new file mode 100644 index 000000000..5817104a4 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/table_size.wast @@ -0,0 +1,86 @@ +(module + (table $t0 0 anyref) + (table $t1 1 anyref) + (table $t2 0 2 anyref) + (table $t3 3 8 anyref) + + (func (export "size-t0") (result i32) (table.size $t0)) + (func (export "size-t1") (result i32) (table.size $t1)) + (func (export "size-t2") (result i32) (table.size $t2)) + (func (export "size-t3") (result i32) (table.size $t3)) + + (func (export "grow-t0") (param $sz i32) + (drop (table.grow $t0 (ref.null) (local.get $sz))) + ) + (func (export "grow-t1") (param $sz i32) + (drop (table.grow $t1 (ref.null) (local.get $sz))) + ) + (func (export "grow-t2") (param $sz i32) + (drop (table.grow $t2 (ref.null) (local.get $sz))) + ) + (func (export "grow-t3") (param $sz i32) + (drop (table.grow $t3 (ref.null) (local.get $sz))) + ) +) + +(assert_return (invoke "size-t0") (i32.const 0)) +(assert_return (invoke "grow-t0" (i32.const 1))) +(assert_return (invoke "size-t0") (i32.const 1)) +(assert_return (invoke "grow-t0" (i32.const 4))) +(assert_return (invoke "size-t0") (i32.const 5)) +(assert_return (invoke "grow-t0" (i32.const 0))) +(assert_return (invoke "size-t0") (i32.const 5)) + +(assert_return (invoke "size-t1") (i32.const 1)) +(assert_return (invoke "grow-t1" (i32.const 1))) +(assert_return (invoke "size-t1") (i32.const 2)) +(assert_return (invoke "grow-t1" (i32.const 4))) +(assert_return (invoke "size-t1") (i32.const 6)) +(assert_return (invoke "grow-t1" (i32.const 0))) +(assert_return (invoke "size-t1") (i32.const 6)) + +(assert_return (invoke "size-t2") (i32.const 0)) +(assert_return (invoke "grow-t2" (i32.const 3))) +(assert_return (invoke "size-t2") (i32.const 0)) +(assert_return (invoke "grow-t2" (i32.const 1))) +(assert_return (invoke "size-t2") (i32.const 1)) +(assert_return (invoke "grow-t2" (i32.const 0))) +(assert_return (invoke "size-t2") (i32.const 1)) +(assert_return (invoke "grow-t2" (i32.const 4))) +(assert_return (invoke "size-t2") (i32.const 1)) +(assert_return (invoke "grow-t2" (i32.const 1))) +(assert_return (invoke "size-t2") (i32.const 2)) + +(assert_return (invoke "size-t3") (i32.const 3)) +(assert_return (invoke "grow-t3" (i32.const 1))) +(assert_return (invoke "size-t3") (i32.const 4)) +(assert_return (invoke "grow-t3" (i32.const 3))) +(assert_return (invoke "size-t3") (i32.const 7)) +(assert_return (invoke "grow-t3" (i32.const 0))) +(assert_return (invoke "size-t3") (i32.const 7)) +(assert_return (invoke "grow-t3" (i32.const 2))) +(assert_return (invoke "size-t3") (i32.const 7)) +(assert_return (invoke "grow-t3" (i32.const 1))) +(assert_return (invoke "size-t3") (i32.const 8)) + + +;; Type errors + +(assert_invalid + (module + (table $t 1 anyref) + (func $type-result-i32-vs-empty + (table.size $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 1 anyref) + (func $type-result-i32-vs-f32 (result f32) + (table.size $t) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/reference-types/unreached-invalid.wast b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/unreached-invalid.wast new file mode 100644 index 000000000..3ddd77385 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/reference-types/unreached-invalid.wast @@ -0,0 +1,695 @@ +;; Failures in unreachable code. + +(assert_invalid + (module (func $local-index (unreachable) (drop (local.get 0)))) + "unknown local" +) +(assert_invalid + (module (func $global-index (unreachable) (drop (global.get 0)))) + "unknown global" +) +(assert_invalid + (module (func $func-index (unreachable) (call 1))) + "unknown function" +) +(assert_invalid + (module (func $label-index (unreachable) (br 1))) + "unknown label" +) + +(assert_invalid + (module (func $type-num-vs-num + (unreachable) (drop (i64.eqz (i32.const 0)))) + ) + "type mismatch" +) +(assert_invalid + (module (func $type-poly-num-vs-num (result i32) + (unreachable) (i64.const 0) (i32.const 0) (select) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-poly-transitive-num-vs-num (result i32) + (unreachable) + (i64.const 0) (i32.const 0) (select) + (i32.const 0) (i32.const 0) (select) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unconsumed-const (unreachable) (i32.const 0))) + "type mismatch" +) +(assert_invalid + (module (func $type-unconsumed-result (unreachable) (i32.eqz))) + "type mismatch" +) +(assert_invalid + (module (func $type-unconsumed-result2 + (unreachable) (i32.const 0) (i32.add) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unconsumed-poly0 (unreachable) (select))) + "type mismatch" +) +(assert_invalid + (module (func $type-unconsumed-poly1 (unreachable) (i32.const 0) (select))) + "type mismatch" +) +(assert_invalid + (module (func $type-unconsumed-poly2 + (unreachable) (i32.const 0) (i32.const 0) (select) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-after-break + (block (br 0) (block (drop (i32.eqz (nop))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-after-break + (block (br 0) (drop (i32.eqz (f32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-after-break + (block (br 0) (block (drop (f32.eq (i32.const 1))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-after-break + (block (br 0) (drop (f32.eq (i32.const 1) (f32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-after-break + (block (br 0) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-after-break (result i32) + (block (result i32) (i32.const 1) (br 0) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-void-after-break + (block (loop (br 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-num-after-break (result i32) + (loop (result i32) (br 1 (i32.const 1)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-void-after-break + (br 0) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-num-after-break (result i32) + (br 0 (i32.const 1)) (f32.const 0) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-after-return + (return) (block (drop (i32.eqz (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-after-return + (return) (drop (i32.eqz (f32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-after-return + (return) (block (drop (f32.eq (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-after-return + (return) (drop (f32.eq (i32.const 1) (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-after-return + (block (return) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-after-return (result i32) + (block (result i32) (i32.const 1) (return (i32.const 0)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-void-after-return + (block (loop (return) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-num-after-return (result i32) + (loop (result i32) (return (i32.const 1)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-void-after-return + (return) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-num-after-return (result i32) + (return (i32.const 1)) (f32.const 0) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-after-unreachable + (unreachable) (block (drop (i32.eqz (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-void-in-loop-after-unreachable + (unreachable) (loop (drop (i32.eqz (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-void-in-i32-loop-after-unreachable + (unreachable) (loop (result i32) (i32.eqz (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-after-unreachable + (unreachable) (drop (i32.eqz (f32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-after-unreachable + (unreachable) (block (drop (f32.eq (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-after-unreachable + (unreachable) (drop (f32.eq (i32.const 1) (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-after-unreachable + (block (unreachable) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-after-unreachable (result i32) + (block (result i32) (i32.const 1) (unreachable) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-void-after-unreachable + (block (loop (unreachable) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-num-after-unreachable (result i32) + (loop (result i32) (unreachable) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-void-after-unreachable + (unreachable) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-num-after-unreachable (result i32) + (unreachable) (f32.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-void-in-if-after-unreachable + (unreachable) (if (i32.const 0) (then (drop (i32.eqz (nop))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-void-in-else-after-unreachable + (unreachable) (if (i32.const 0) (then (nop)) (else (drop (i32.eqz (nop))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-void-in-else-after-unreachable-if + (if (i32.const 0) (then (unreachable)) (else (drop (i32.eqz (nop))))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-after-nested-unreachable + (block (unreachable)) (block (drop (i32.eqz (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-after-nested-unreachable + (block (unreachable)) (drop (i32.eqz (f32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-after-nested-unreachable + (block (unreachable)) (block (drop (f32.eq (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-after-nested-unreachable + (block (unreachable)) (drop (f32.eq (i32.const 1) (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-after-nested-unreachable + (block (block (unreachable)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-after-nested-unreachable + (result i32) + (block (result i32) (i32.const 1) (block (unreachable)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-void-after-nested-unreachable + (block (loop (block (unreachable)) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-num-after-nested-unreachable + (result i32) + (loop (result i32) (block (unreachable)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-void-after-nested-unreachable + (block (unreachable)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-num-after-nested-unreachable + (result i32) + (block (unreachable)) (f32.const 0) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-after-infinite-loop + (loop (br 0)) (block (drop (i32.eqz (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-after-infinite-loop + (loop (br 0)) (drop (i32.eqz (f32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-after-infinite-loop + (loop (br 0)) (block (drop (f32.eq (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-after-infinite-loop + (loop (br 0)) (drop (f32.eq (i32.const 1) (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-after-infinite-loop + (block (loop (br 0)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-after-infinite-loop (result i32) + (block (result i32) (i32.const 1) (loop (br 0)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-void-after-infinite-loop + (block (loop (loop (br 0)) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-num-after-infinite-loop (result i32) + (loop (result i32) (loop (br 0)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-void-after-infinite-loop + (loop (br 0)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-num-after-infinite-loop (result i32) + (loop (br 0)) (f32.const 0) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-in-dead-body + (if (i32.const 0) (then (drop (i32.eqz (nop))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-in-dead-body + (if (i32.const 0) (then (drop (i32.eqz (f32.const 1))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-in-dead-body + (if (i32.const 0) (then (drop (f32.eq (i32.const 1))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-in-dead-body + (if (i32.const 0) (then (drop (f32.eq (i32.const 1) (f32.const 0))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-if-value-num-vs-void-in-dead-body + (if (i32.const 0) (then (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-if-value-num-vs-num-in-dead-body (result i32) + (if (result i32) (i32.const 0) (then (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-in-dead-body + (if (i32.const 0) (then (block (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-in-dead-body (result i32) + (if (result i32) (i32.const 0) (then (block (result i32) (f32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-in-dead-body + (if (i32.const 0) (then (loop (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-in-dead-body (result i32) + (if (result i32) (i32.const 0) (then (loop (result i32) (f32.const 0)))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-return-second-num-vs-num (result i32) + (return (i32.const 1)) (return (f64.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-br-second-num-vs-num (result i32) + (block (result i32) (br 0 (i32.const 1)) (br 0 (f64.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-br_if-cond-num-vs-num-after-unreachable + (block (br_if 0 (unreachable) (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_if-num-vs-void-after-unreachable (result i32) + (block (result i32) + (block (unreachable) (br_if 1 (i32.const 0) (i32.const 0))) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_if-num-vs-num-after-unreachable (result i32) + (block (result i32) + (block (result f32) (unreachable) (br_if 1 (i32.const 0) (i32.const 0))) + (drop) (i32.const 0) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_if-num2-vs-num-after-unreachable (result i32) + (block (result i32) + (unreachable) (br_if 0 (i32.const 0) (i32.const 0)) (i32.const 0) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_table-num-vs-num-after-unreachable + (block (br_table 0 (unreachable) (f32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_table-label-num-vs-num-after-unreachable (result i32) + (block (result i32) (unreachable) (br_table 0 (f32.const 0) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_table-label-num-vs-label-void-after-unreachable + (block + (block (result f32) + (unreachable) + (br_table 0 1 0 (i32.const 1)) + ) + (drop) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-block-value-nested-unreachable-num-vs-void + (block (i32.const 3) (block (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-unreachable-void-vs-num (result i32) + (block (block (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-unreachable-num-vs-num (result i32) + (block (result i64) (i64.const 0) (block (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-unreachable-num2-vs-void (result i32) + (block (i32.const 3) (block (i64.const 1) (unreachable))) (i32.const 9) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-block-value-nested-br-num-vs-void + (block (i32.const 3) (block (br 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-br-void-vs-num (result i32) + (block (result i32) (block (br 1 (i32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-br-num-vs-num (result i32) + (block (result i32) (i64.const 0) (block (br 1 (i32.const 0)))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-block-value-nested2-br-num-vs-void + (block (block (i32.const 3) (block (br 2)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested2-br-void-vs-num (result i32) + (block (result i32) (block (block (br 2 (i32.const 0))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested2-br-num-vs-num (result i32) + (block (result i32) + (block (result i64) (i64.const 0) (block (br 2 (i32.const 0)))) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested2-br-num2-vs-void (result i32) + (block (i32.const 3) (block (i64.const 1) (br 1))) (i32.const 9) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-block-value-nested-return-num-vs-void + (block (i32.const 3) (block (return))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-return-void-vs-num (result i32) + (block (block (return (i32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-return-num-vs-num (result i32) + (block (result i64) (i64.const 0) (block (return (i32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-return-num2-vs-void (result i32) + (block (i32.const 3) (block (i64.const 1) (return (i32.const 0)))) + (i32.const 9) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-loop-value-nested-unreachable-num-vs-void + (loop (i32.const 3) (block (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-nested-unreachable-void-vs-num (result i32) + (loop (block (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-nested-unreachable-num-vs-num (result i32) + (loop (result i64) (i64.const 0) (block (unreachable))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-cont-last-void-vs-empty (result i32) + (loop (br 0 (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-cont-last-num-vs-empty (result i32) + (loop (br 0 (i32.const 0))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $tee-local-unreachable-value + (local i32) + (local.tee 0 (unreachable)) + )) + "type mismatch" +) +(assert_invalid + (module (func $br_if-unreachable (result i32) + (block (result i32) + (block + (br_if 1 (unreachable) (i32.const 0)) + ) + (i32.const 0) + ) + )) + "type mismatch" +) +(assert_invalid + (module + (func $type-br_if-after-unreachable (result i64) + unreachable + br_if 0 + i64.extend_i32_u + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/sign-extension-ops/i32.wast b/runtime/unc-vm/tests/wast/spec/proposals/sign-extension-ops/i32.wast new file mode 100644 index 000000000..32862c347 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/sign-extension-ops/i32.wast @@ -0,0 +1,976 @@ +;; i32 operations + +(module + (func (export "add") (param $x i32) (param $y i32) (result i32) (i32.add (local.get $x) (local.get $y))) + (func (export "sub") (param $x i32) (param $y i32) (result i32) (i32.sub (local.get $x) (local.get $y))) + (func (export "mul") (param $x i32) (param $y i32) (result i32) (i32.mul (local.get $x) (local.get $y))) + (func (export "div_s") (param $x i32) (param $y i32) (result i32) (i32.div_s (local.get $x) (local.get $y))) + (func (export "div_u") (param $x i32) (param $y i32) (result i32) (i32.div_u (local.get $x) (local.get $y))) + (func (export "rem_s") (param $x i32) (param $y i32) (result i32) (i32.rem_s (local.get $x) (local.get $y))) + (func (export "rem_u") (param $x i32) (param $y i32) (result i32) (i32.rem_u (local.get $x) (local.get $y))) + (func (export "and") (param $x i32) (param $y i32) (result i32) (i32.and (local.get $x) (local.get $y))) + (func (export "or") (param $x i32) (param $y i32) (result i32) (i32.or (local.get $x) (local.get $y))) + (func (export "xor") (param $x i32) (param $y i32) (result i32) (i32.xor (local.get $x) (local.get $y))) + (func (export "shl") (param $x i32) (param $y i32) (result i32) (i32.shl (local.get $x) (local.get $y))) + (func (export "shr_s") (param $x i32) (param $y i32) (result i32) (i32.shr_s (local.get $x) (local.get $y))) + (func (export "shr_u") (param $x i32) (param $y i32) (result i32) (i32.shr_u (local.get $x) (local.get $y))) + (func (export "rotl") (param $x i32) (param $y i32) (result i32) (i32.rotl (local.get $x) (local.get $y))) + (func (export "rotr") (param $x i32) (param $y i32) (result i32) (i32.rotr (local.get $x) (local.get $y))) + (func (export "clz") (param $x i32) (result i32) (i32.clz (local.get $x))) + (func (export "ctz") (param $x i32) (result i32) (i32.ctz (local.get $x))) + (func (export "popcnt") (param $x i32) (result i32) (i32.popcnt (local.get $x))) + (func (export "extend8_s") (param $x i32) (result i32) (i32.extend8_s (local.get $x))) + (func (export "extend16_s") (param $x i32) (result i32) (i32.extend16_s (local.get $x))) + (func (export "eqz") (param $x i32) (result i32) (i32.eqz (local.get $x))) + (func (export "eq") (param $x i32) (param $y i32) (result i32) (i32.eq (local.get $x) (local.get $y))) + (func (export "ne") (param $x i32) (param $y i32) (result i32) (i32.ne (local.get $x) (local.get $y))) + (func (export "lt_s") (param $x i32) (param $y i32) (result i32) (i32.lt_s (local.get $x) (local.get $y))) + (func (export "lt_u") (param $x i32) (param $y i32) (result i32) (i32.lt_u (local.get $x) (local.get $y))) + (func (export "le_s") (param $x i32) (param $y i32) (result i32) (i32.le_s (local.get $x) (local.get $y))) + (func (export "le_u") (param $x i32) (param $y i32) (result i32) (i32.le_u (local.get $x) (local.get $y))) + (func (export "gt_s") (param $x i32) (param $y i32) (result i32) (i32.gt_s (local.get $x) (local.get $y))) + (func (export "gt_u") (param $x i32) (param $y i32) (result i32) (i32.gt_u (local.get $x) (local.get $y))) + (func (export "ge_s") (param $x i32) (param $y i32) (result i32) (i32.ge_s (local.get $x) (local.get $y))) + (func (export "ge_u") (param $x i32) (param $y i32) (result i32) (i32.ge_u (local.get $x) (local.get $y))) +) + +(assert_return (invoke "add" (i32.const 1) (i32.const 1)) (i32.const 2)) +(assert_return (invoke "add" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "add" (i32.const -1) (i32.const -1)) (i32.const -2)) +(assert_return (invoke "add" (i32.const -1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "add" (i32.const 0x7fffffff) (i32.const 1)) (i32.const 0x80000000)) +(assert_return (invoke "add" (i32.const 0x80000000) (i32.const -1)) (i32.const 0x7fffffff)) +(assert_return (invoke "add" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "add" (i32.const 0x3fffffff) (i32.const 1)) (i32.const 0x40000000)) + +(assert_return (invoke "sub" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "sub" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "sub" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "sub" (i32.const 0x7fffffff) (i32.const -1)) (i32.const 0x80000000)) +(assert_return (invoke "sub" (i32.const 0x80000000) (i32.const 1)) (i32.const 0x7fffffff)) +(assert_return (invoke "sub" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "sub" (i32.const 0x3fffffff) (i32.const -1)) (i32.const 0x40000000)) + +(assert_return (invoke "mul" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "mul" (i32.const 1) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "mul" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "mul" (i32.const 0x10000000) (i32.const 4096)) (i32.const 0)) +(assert_return (invoke "mul" (i32.const 0x80000000) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "mul" (i32.const 0x80000000) (i32.const -1)) (i32.const 0x80000000)) +(assert_return (invoke "mul" (i32.const 0x7fffffff) (i32.const -1)) (i32.const 0x80000001)) +(assert_return (invoke "mul" (i32.const 0x01234567) (i32.const 0x76543210)) (i32.const 0x358e7470)) +(assert_return (invoke "mul" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 1)) + +(assert_trap (invoke "div_s" (i32.const 1) (i32.const 0)) "integer divide by zero") +(assert_trap (invoke "div_s" (i32.const 0) (i32.const 0)) "integer divide by zero") +(assert_trap (invoke "div_s" (i32.const 0x80000000) (i32.const -1)) "integer overflow") +(assert_trap (invoke "div_s" (i32.const 0x80000000) (i32.const 0)) "integer divide by zero") +(assert_return (invoke "div_s" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "div_s" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "div_s" (i32.const 0) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "div_s" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "div_s" (i32.const 0x80000000) (i32.const 2)) (i32.const 0xc0000000)) +(assert_return (invoke "div_s" (i32.const 0x80000001) (i32.const 1000)) (i32.const 0xffdf3b65)) +(assert_return (invoke "div_s" (i32.const 5) (i32.const 2)) (i32.const 2)) +(assert_return (invoke "div_s" (i32.const -5) (i32.const 2)) (i32.const -2)) +(assert_return (invoke "div_s" (i32.const 5) (i32.const -2)) (i32.const -2)) +(assert_return (invoke "div_s" (i32.const -5) (i32.const -2)) (i32.const 2)) +(assert_return (invoke "div_s" (i32.const 7) (i32.const 3)) (i32.const 2)) +(assert_return (invoke "div_s" (i32.const -7) (i32.const 3)) (i32.const -2)) +(assert_return (invoke "div_s" (i32.const 7) (i32.const -3)) (i32.const -2)) +(assert_return (invoke "div_s" (i32.const -7) (i32.const -3)) (i32.const 2)) +(assert_return (invoke "div_s" (i32.const 11) (i32.const 5)) (i32.const 2)) +(assert_return (invoke "div_s" (i32.const 17) (i32.const 7)) (i32.const 2)) + +(assert_trap (invoke "div_u" (i32.const 1) (i32.const 0)) "integer divide by zero") +(assert_trap (invoke "div_u" (i32.const 0) (i32.const 0)) "integer divide by zero") +(assert_return (invoke "div_u" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "div_u" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "div_u" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "div_u" (i32.const 0x80000000) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "div_u" (i32.const 0x80000000) (i32.const 2)) (i32.const 0x40000000)) +(assert_return (invoke "div_u" (i32.const 0x8ff00ff0) (i32.const 0x10001)) (i32.const 0x8fef)) +(assert_return (invoke "div_u" (i32.const 0x80000001) (i32.const 1000)) (i32.const 0x20c49b)) +(assert_return (invoke "div_u" (i32.const 5) (i32.const 2)) (i32.const 2)) +(assert_return (invoke "div_u" (i32.const -5) (i32.const 2)) (i32.const 0x7ffffffd)) +(assert_return (invoke "div_u" (i32.const 5) (i32.const -2)) (i32.const 0)) +(assert_return (invoke "div_u" (i32.const -5) (i32.const -2)) (i32.const 0)) +(assert_return (invoke "div_u" (i32.const 7) (i32.const 3)) (i32.const 2)) +(assert_return (invoke "div_u" (i32.const 11) (i32.const 5)) (i32.const 2)) +(assert_return (invoke "div_u" (i32.const 17) (i32.const 7)) (i32.const 2)) + +(assert_trap (invoke "rem_s" (i32.const 1) (i32.const 0)) "integer divide by zero") +(assert_trap (invoke "rem_s" (i32.const 0) (i32.const 0)) "integer divide by zero") +(assert_return (invoke "rem_s" (i32.const 0x7fffffff) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "rem_s" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "rem_s" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "rem_s" (i32.const 0) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "rem_s" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "rem_s" (i32.const 0x80000000) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "rem_s" (i32.const 0x80000000) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "rem_s" (i32.const 0x80000001) (i32.const 1000)) (i32.const -647)) +(assert_return (invoke "rem_s" (i32.const 5) (i32.const 2)) (i32.const 1)) +(assert_return (invoke "rem_s" (i32.const -5) (i32.const 2)) (i32.const -1)) +(assert_return (invoke "rem_s" (i32.const 5) (i32.const -2)) (i32.const 1)) +(assert_return (invoke "rem_s" (i32.const -5) (i32.const -2)) (i32.const -1)) +(assert_return (invoke "rem_s" (i32.const 7) (i32.const 3)) (i32.const 1)) +(assert_return (invoke "rem_s" (i32.const -7) (i32.const 3)) (i32.const -1)) +(assert_return (invoke "rem_s" (i32.const 7) (i32.const -3)) (i32.const 1)) +(assert_return (invoke "rem_s" (i32.const -7) (i32.const -3)) (i32.const -1)) +(assert_return (invoke "rem_s" (i32.const 11) (i32.const 5)) (i32.const 1)) +(assert_return (invoke "rem_s" (i32.const 17) (i32.const 7)) (i32.const 3)) + +(assert_trap (invoke "rem_u" (i32.const 1) (i32.const 0)) "integer divide by zero") +(assert_trap (invoke "rem_u" (i32.const 0) (i32.const 0)) "integer divide by zero") +(assert_return (invoke "rem_u" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "rem_u" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "rem_u" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "rem_u" (i32.const 0x80000000) (i32.const -1)) (i32.const 0x80000000)) +(assert_return (invoke "rem_u" (i32.const 0x80000000) (i32.const 2)) (i32.const 0)) +(assert_return (invoke "rem_u" (i32.const 0x8ff00ff0) (i32.const 0x10001)) (i32.const 0x8001)) +(assert_return (invoke "rem_u" (i32.const 0x80000001) (i32.const 1000)) (i32.const 649)) +(assert_return (invoke "rem_u" (i32.const 5) (i32.const 2)) (i32.const 1)) +(assert_return (invoke "rem_u" (i32.const -5) (i32.const 2)) (i32.const 1)) +(assert_return (invoke "rem_u" (i32.const 5) (i32.const -2)) (i32.const 5)) +(assert_return (invoke "rem_u" (i32.const -5) (i32.const -2)) (i32.const -5)) +(assert_return (invoke "rem_u" (i32.const 7) (i32.const 3)) (i32.const 1)) +(assert_return (invoke "rem_u" (i32.const 11) (i32.const 5)) (i32.const 1)) +(assert_return (invoke "rem_u" (i32.const 17) (i32.const 7)) (i32.const 3)) + +(assert_return (invoke "and" (i32.const 1) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "and" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "and" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "and" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "and" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "and" (i32.const 0x7fffffff) (i32.const -1)) (i32.const 0x7fffffff)) +(assert_return (invoke "and" (i32.const 0xf0f0ffff) (i32.const 0xfffff0f0)) (i32.const 0xf0f0f0f0)) +(assert_return (invoke "and" (i32.const 0xffffffff) (i32.const 0xffffffff)) (i32.const 0xffffffff)) + +(assert_return (invoke "or" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "or" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "or" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "or" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "or" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const -1)) +(assert_return (invoke "or" (i32.const 0x80000000) (i32.const 0)) (i32.const 0x80000000)) +(assert_return (invoke "or" (i32.const 0xf0f0ffff) (i32.const 0xfffff0f0)) (i32.const 0xffffffff)) +(assert_return (invoke "or" (i32.const 0xffffffff) (i32.const 0xffffffff)) (i32.const 0xffffffff)) + +(assert_return (invoke "xor" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "xor" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "xor" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "xor" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "xor" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const -1)) +(assert_return (invoke "xor" (i32.const 0x80000000) (i32.const 0)) (i32.const 0x80000000)) +(assert_return (invoke "xor" (i32.const -1) (i32.const 0x80000000)) (i32.const 0x7fffffff)) +(assert_return (invoke "xor" (i32.const -1) (i32.const 0x7fffffff)) (i32.const 0x80000000)) +(assert_return (invoke "xor" (i32.const 0xf0f0ffff) (i32.const 0xfffff0f0)) (i32.const 0x0f0f0f0f)) +(assert_return (invoke "xor" (i32.const 0xffffffff) (i32.const 0xffffffff)) (i32.const 0)) + +(assert_return (invoke "shl" (i32.const 1) (i32.const 1)) (i32.const 2)) +(assert_return (invoke "shl" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "shl" (i32.const 0x7fffffff) (i32.const 1)) (i32.const 0xfffffffe)) +(assert_return (invoke "shl" (i32.const 0xffffffff) (i32.const 1)) (i32.const 0xfffffffe)) +(assert_return (invoke "shl" (i32.const 0x80000000) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "shl" (i32.const 0x40000000) (i32.const 1)) (i32.const 0x80000000)) +(assert_return (invoke "shl" (i32.const 1) (i32.const 31)) (i32.const 0x80000000)) +(assert_return (invoke "shl" (i32.const 1) (i32.const 32)) (i32.const 1)) +(assert_return (invoke "shl" (i32.const 1) (i32.const 33)) (i32.const 2)) +(assert_return (invoke "shl" (i32.const 1) (i32.const -1)) (i32.const 0x80000000)) +(assert_return (invoke "shl" (i32.const 1) (i32.const 0x7fffffff)) (i32.const 0x80000000)) + +(assert_return (invoke "shr_s" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "shr_s" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "shr_s" (i32.const -1) (i32.const 1)) (i32.const -1)) +(assert_return (invoke "shr_s" (i32.const 0x7fffffff) (i32.const 1)) (i32.const 0x3fffffff)) +(assert_return (invoke "shr_s" (i32.const 0x80000000) (i32.const 1)) (i32.const 0xc0000000)) +(assert_return (invoke "shr_s" (i32.const 0x40000000) (i32.const 1)) (i32.const 0x20000000)) +(assert_return (invoke "shr_s" (i32.const 1) (i32.const 32)) (i32.const 1)) +(assert_return (invoke "shr_s" (i32.const 1) (i32.const 33)) (i32.const 0)) +(assert_return (invoke "shr_s" (i32.const 1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "shr_s" (i32.const 1) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "shr_s" (i32.const 1) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "shr_s" (i32.const 0x80000000) (i32.const 31)) (i32.const -1)) +(assert_return (invoke "shr_s" (i32.const -1) (i32.const 32)) (i32.const -1)) +(assert_return (invoke "shr_s" (i32.const -1) (i32.const 33)) (i32.const -1)) +(assert_return (invoke "shr_s" (i32.const -1) (i32.const -1)) (i32.const -1)) +(assert_return (invoke "shr_s" (i32.const -1) (i32.const 0x7fffffff)) (i32.const -1)) +(assert_return (invoke "shr_s" (i32.const -1) (i32.const 0x80000000)) (i32.const -1)) + +(assert_return (invoke "shr_u" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "shr_u" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "shr_u" (i32.const -1) (i32.const 1)) (i32.const 0x7fffffff)) +(assert_return (invoke "shr_u" (i32.const 0x7fffffff) (i32.const 1)) (i32.const 0x3fffffff)) +(assert_return (invoke "shr_u" (i32.const 0x80000000) (i32.const 1)) (i32.const 0x40000000)) +(assert_return (invoke "shr_u" (i32.const 0x40000000) (i32.const 1)) (i32.const 0x20000000)) +(assert_return (invoke "shr_u" (i32.const 1) (i32.const 32)) (i32.const 1)) +(assert_return (invoke "shr_u" (i32.const 1) (i32.const 33)) (i32.const 0)) +(assert_return (invoke "shr_u" (i32.const 1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "shr_u" (i32.const 1) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "shr_u" (i32.const 1) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "shr_u" (i32.const 0x80000000) (i32.const 31)) (i32.const 1)) +(assert_return (invoke "shr_u" (i32.const -1) (i32.const 32)) (i32.const -1)) +(assert_return (invoke "shr_u" (i32.const -1) (i32.const 33)) (i32.const 0x7fffffff)) +(assert_return (invoke "shr_u" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "shr_u" (i32.const -1) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "shr_u" (i32.const -1) (i32.const 0x80000000)) (i32.const -1)) + +(assert_return (invoke "rotl" (i32.const 1) (i32.const 1)) (i32.const 2)) +(assert_return (invoke "rotl" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "rotl" (i32.const -1) (i32.const 1)) (i32.const -1)) +(assert_return (invoke "rotl" (i32.const 1) (i32.const 32)) (i32.const 1)) +(assert_return (invoke "rotl" (i32.const 0xabcd9876) (i32.const 1)) (i32.const 0x579b30ed)) +(assert_return (invoke "rotl" (i32.const 0xfe00dc00) (i32.const 4)) (i32.const 0xe00dc00f)) +(assert_return (invoke "rotl" (i32.const 0xb0c1d2e3) (i32.const 5)) (i32.const 0x183a5c76)) +(assert_return (invoke "rotl" (i32.const 0x00008000) (i32.const 37)) (i32.const 0x00100000)) +(assert_return (invoke "rotl" (i32.const 0xb0c1d2e3) (i32.const 0xff05)) (i32.const 0x183a5c76)) +(assert_return (invoke "rotl" (i32.const 0x769abcdf) (i32.const 0xffffffed)) (i32.const 0x579beed3)) +(assert_return (invoke "rotl" (i32.const 0x769abcdf) (i32.const 0x8000000d)) (i32.const 0x579beed3)) +(assert_return (invoke "rotl" (i32.const 1) (i32.const 31)) (i32.const 0x80000000)) +(assert_return (invoke "rotl" (i32.const 0x80000000) (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "rotr" (i32.const 1) (i32.const 1)) (i32.const 0x80000000)) +(assert_return (invoke "rotr" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "rotr" (i32.const -1) (i32.const 1)) (i32.const -1)) +(assert_return (invoke "rotr" (i32.const 1) (i32.const 32)) (i32.const 1)) +(assert_return (invoke "rotr" (i32.const 0xff00cc00) (i32.const 1)) (i32.const 0x7f806600)) +(assert_return (invoke "rotr" (i32.const 0x00080000) (i32.const 4)) (i32.const 0x00008000)) +(assert_return (invoke "rotr" (i32.const 0xb0c1d2e3) (i32.const 5)) (i32.const 0x1d860e97)) +(assert_return (invoke "rotr" (i32.const 0x00008000) (i32.const 37)) (i32.const 0x00000400)) +(assert_return (invoke "rotr" (i32.const 0xb0c1d2e3) (i32.const 0xff05)) (i32.const 0x1d860e97)) +(assert_return (invoke "rotr" (i32.const 0x769abcdf) (i32.const 0xffffffed)) (i32.const 0xe6fbb4d5)) +(assert_return (invoke "rotr" (i32.const 0x769abcdf) (i32.const 0x8000000d)) (i32.const 0xe6fbb4d5)) +(assert_return (invoke "rotr" (i32.const 1) (i32.const 31)) (i32.const 2)) +(assert_return (invoke "rotr" (i32.const 0x80000000) (i32.const 31)) (i32.const 1)) + +(assert_return (invoke "clz" (i32.const 0xffffffff)) (i32.const 0)) +(assert_return (invoke "clz" (i32.const 0)) (i32.const 32)) +(assert_return (invoke "clz" (i32.const 0x00008000)) (i32.const 16)) +(assert_return (invoke "clz" (i32.const 0xff)) (i32.const 24)) +(assert_return (invoke "clz" (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "clz" (i32.const 1)) (i32.const 31)) +(assert_return (invoke "clz" (i32.const 2)) (i32.const 30)) +(assert_return (invoke "clz" (i32.const 0x7fffffff)) (i32.const 1)) + +(assert_return (invoke "ctz" (i32.const -1)) (i32.const 0)) +(assert_return (invoke "ctz" (i32.const 0)) (i32.const 32)) +(assert_return (invoke "ctz" (i32.const 0x00008000)) (i32.const 15)) +(assert_return (invoke "ctz" (i32.const 0x00010000)) (i32.const 16)) +(assert_return (invoke "ctz" (i32.const 0x80000000)) (i32.const 31)) +(assert_return (invoke "ctz" (i32.const 0x7fffffff)) (i32.const 0)) + +(assert_return (invoke "popcnt" (i32.const -1)) (i32.const 32)) +(assert_return (invoke "popcnt" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "popcnt" (i32.const 0x00008000)) (i32.const 1)) +(assert_return (invoke "popcnt" (i32.const 0x80008000)) (i32.const 2)) +(assert_return (invoke "popcnt" (i32.const 0x7fffffff)) (i32.const 31)) +(assert_return (invoke "popcnt" (i32.const 0xAAAAAAAA)) (i32.const 16)) +(assert_return (invoke "popcnt" (i32.const 0x55555555)) (i32.const 16)) +(assert_return (invoke "popcnt" (i32.const 0xDEADBEEF)) (i32.const 24)) + +(assert_return (invoke "extend8_s" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "extend8_s" (i32.const 0x7f)) (i32.const 127)) +(assert_return (invoke "extend8_s" (i32.const 0x80)) (i32.const -128)) +(assert_return (invoke "extend8_s" (i32.const 0xff)) (i32.const -1)) +(assert_return (invoke "extend8_s" (i32.const 0x012345_00)) (i32.const 0)) +(assert_return (invoke "extend8_s" (i32.const 0xfedcba_80)) (i32.const -0x80)) +(assert_return (invoke "extend8_s" (i32.const -1)) (i32.const -1)) + +(assert_return (invoke "extend16_s" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "extend16_s" (i32.const 0x7fff)) (i32.const 32767)) +(assert_return (invoke "extend16_s" (i32.const 0x8000)) (i32.const -32768)) +(assert_return (invoke "extend16_s" (i32.const 0xffff)) (i32.const -1)) +(assert_return (invoke "extend16_s" (i32.const 0x0123_0000)) (i32.const 0)) +(assert_return (invoke "extend16_s" (i32.const 0xfedc_8000)) (i32.const -0x8000)) +(assert_return (invoke "extend16_s" (i32.const -1)) (i32.const -1)) + +(assert_return (invoke "eqz" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "eqz" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eqz" (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "eqz" (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "eqz" (i32.const 0xffffffff)) (i32.const 0)) + +(assert_return (invoke "eq" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const -1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "eq" (i32.const 1) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0x80000000) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0x80000000) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const -1) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "eq" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 0)) + +(assert_return (invoke "ne" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "ne" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "ne" (i32.const -1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "ne" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "ne" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "ne" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const 0x80000000) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const 0) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const 0x80000000) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const -1) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "ne" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 1)) + +(assert_return (invoke "lt_s" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const -1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "lt_s" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const 1) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "lt_s" (i32.const 0x80000000) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "lt_s" (i32.const 0) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const 0x80000000) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "lt_s" (i32.const -1) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "lt_s" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "lt_s" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 0)) + +(assert_return (invoke "lt_u" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const -1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 1) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "lt_u" (i32.const 0x80000000) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 0) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "lt_u" (i32.const 0x80000000) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "lt_u" (i32.const -1) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "lt_u" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 1)) + +(assert_return (invoke "le_s" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const -1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const 1) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "le_s" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const 0x80000000) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const 0) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "le_s" (i32.const 0x80000000) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const -1) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "le_s" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "le_s" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 0)) + +(assert_return (invoke "le_u" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const -1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "le_u" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const 1) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "le_u" (i32.const 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const 0x80000000) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "le_u" (i32.const 0) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const 0x80000000) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "le_u" (i32.const -1) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "le_u" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "le_u" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 1)) + +(assert_return (invoke "gt_s" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const -1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "gt_s" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const 0x80000000) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const 0) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "gt_s" (i32.const 0x80000000) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const -1) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "gt_s" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "gt_s" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 1)) + +(assert_return (invoke "gt_u" (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const 1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const -1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "gt_u" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const -1) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "gt_u" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const 0x80000000) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "gt_u" (i32.const 0) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const 0x80000000) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i32.const -1) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "gt_u" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "gt_u" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 0)) + +(assert_return (invoke "ge_s" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const -1) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "ge_s" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "ge_s" (i32.const 0x80000000) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "ge_s" (i32.const 0) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const 0x80000000) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "ge_s" (i32.const -1) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "ge_s" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 0)) +(assert_return (invoke "ge_s" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 1)) + +(assert_return (invoke "ge_u" (i32.const 0) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const -1) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 0x80000000) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 0x7fffffff) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const -1) (i32.const -1)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 0) (i32.const 1)) (i32.const 0)) +(assert_return (invoke "ge_u" (i32.const 0x80000000) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 0) (i32.const 0x80000000)) (i32.const 0)) +(assert_return (invoke "ge_u" (i32.const 0x80000000) (i32.const -1)) (i32.const 0)) +(assert_return (invoke "ge_u" (i32.const -1) (i32.const 0x80000000)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 0x80000000) (i32.const 0x7fffffff)) (i32.const 1)) +(assert_return (invoke "ge_u" (i32.const 0x7fffffff) (i32.const 0x80000000)) (i32.const 0)) + + +(assert_invalid + (module + (func $type-unary-operand-empty + (i32.eqz) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-block + (i32.const 0) + (block (i32.eqz) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-loop + (i32.const 0) + (loop (i32.eqz) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-if + (i32.const 0) (i32.const 0) + (if (then (i32.eqz) (drop))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-else + (i32.const 0) (i32.const 0) + (if (result i32) (then (i32.const 0)) (else (i32.eqz))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-br + (i32.const 0) + (block (br 0 (i32.eqz)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-br_if + (i32.const 0) + (block (br_if 0 (i32.eqz) (i32.const 1)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-br_table + (i32.const 0) + (block (br_table 0 (i32.eqz)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-return + (return (i32.eqz)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-select + (select (i32.eqz) (i32.const 1) (i32.const 2)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-call + (call 1 (i32.eqz)) (drop) + ) + (func (param i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-unary-operand-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (i32.eqz) (i32.const 0) + ) + (drop) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-local.set + (local i32) + (local.set 0 (i32.eqz)) (local.get 0) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-unary-operand-empty-in-local.tee + (local i32) + (local.tee 0 (i32.eqz)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-unary-operand-empty-in-global.set + (global.set $x (i32.eqz)) (global.get $x) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-unary-operand-empty-in-memory.grow + (memory.grow (i32.eqz)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-unary-operand-empty-in-load + (i32.load (i32.eqz)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-unary-operand-empty-in-store + (i32.store (i32.eqz) (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $type-binary-1st-operand-empty + (i32.add) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty + (i32.const 0) (i32.add) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-block + (i32.const 0) (i32.const 0) + (block (i32.add) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-block + (i32.const 0) + (block (i32.const 0) (i32.add) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-loop + (i32.const 0) (i32.const 0) + (loop (i32.add) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-loop + (i32.const 0) + (loop (i32.const 0) (i32.add) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-if + (i32.const 0) (i32.const 0) (i32.const 0) + (if (i32.add) (then (drop))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-if + (i32.const 0) (i32.const 0) + (if (i32.const 0) (then (i32.add)) (else (drop))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-else + (i32.const 0) (i32.const 0) (i32.const 0) + (if (result i32) (then (i32.const 0)) (else (i32.add) (i32.const 0))) + (drop) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-else + (i32.const 0) (i32.const 0) + (if (result i32) (then (i32.const 0)) (else (i32.add))) + (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-br + (i32.const 0) (i32.const 0) + (block (br 0 (i32.add)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-br + (i32.const 0) + (block (br 0 (i32.const 0) (i32.add)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-br_if + (i32.const 0) (i32.const 0) + (block (br_if 0 (i32.add) (i32.const 1)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-br_if + (i32.const 0) + (block (br_if 0 (i32.const 0) (i32.add) (i32.const 1)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-br_table + (i32.const 0) (i32.const 0) + (block (br_table 0 (i32.add)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-br_table + (i32.const 0) + (block (br_table 0 (i32.const 0) (i32.add)) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-return + (return (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-return + (return (i32.const 0) (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-select + (select (i32.add) (i32.const 1) (i32.const 2)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-select + (select (i32.const 0) (i32.add) (i32.const 1) (i32.const 2)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-call + (call 1 (i32.add)) (drop) + ) + (func (param i32 i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-call + (call 1 (i32.const 0) (i32.add)) (drop) + ) + (func (param i32 i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-binary-1st-operand-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (i32.add) (i32.const 0) + ) + (drop) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-binary-2nd-operand-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) (i32.add) (i32.const 0) + ) + (drop) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-local.set + (local i32) + (local.set 0 (i32.add)) (local.get 0) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-local.set + (local i32) + (local.set 0 (i32.const 0) (i32.add)) (local.get 0) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-1st-operand-empty-in-local.tee + (local i32) + (local.tee 0 (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-binary-2nd-operand-empty-in-local.tee + (local i32) + (local.tee 0 (i32.const 0) (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-binary-1st-operand-empty-in-global.set + (global.set $x (i32.add)) (global.get $x) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-binary-2nd-operand-empty-in-global.set + (global.set $x (i32.const 0) (i32.add)) (global.get $x) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-binary-1st-operand-empty-in-memory.grow + (memory.grow (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-binary-2nd-operand-empty-in-memory.grow + (memory.grow (i32.const 0) (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-binary-1st-operand-empty-in-load + (i32.load (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-binary-2nd-operand-empty-in-load + (i32.load (i32.const 0) (i32.add)) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-binary-1st-operand-empty-in-store + (i32.store (i32.add) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-binary-2nd-operand-empty-in-store + (i32.store (i32.const 1) (i32.add) (i32.const 0)) + ) + ) + "type mismatch" +) + + +;; Type check + +(assert_invalid (module (func (result i32) (i32.add (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.and (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.div_s (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.div_u (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.mul (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.or (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.rem_s (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.rem_u (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.rotl (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.rotr (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.shl (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.shr_s (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.shr_u (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.sub (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.xor (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.eqz (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.clz (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.ctz (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.popcnt (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.eq (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.ge_s (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.ge_u (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.gt_s (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.gt_u (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.le_s (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.le_u (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.lt_s (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.lt_u (i64.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32.ne (i64.const 0) (f32.const 0)))) "type mismatch") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/sign-extension-ops/i64.wast b/runtime/unc-vm/tests/wast/spec/proposals/sign-extension-ops/i64.wast new file mode 100644 index 000000000..baeed0ce1 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/sign-extension-ops/i64.wast @@ -0,0 +1,485 @@ +;; i64 operations + +(module + (func (export "add") (param $x i64) (param $y i64) (result i64) (i64.add (local.get $x) (local.get $y))) + (func (export "sub") (param $x i64) (param $y i64) (result i64) (i64.sub (local.get $x) (local.get $y))) + (func (export "mul") (param $x i64) (param $y i64) (result i64) (i64.mul (local.get $x) (local.get $y))) + (func (export "div_s") (param $x i64) (param $y i64) (result i64) (i64.div_s (local.get $x) (local.get $y))) + (func (export "div_u") (param $x i64) (param $y i64) (result i64) (i64.div_u (local.get $x) (local.get $y))) + (func (export "rem_s") (param $x i64) (param $y i64) (result i64) (i64.rem_s (local.get $x) (local.get $y))) + (func (export "rem_u") (param $x i64) (param $y i64) (result i64) (i64.rem_u (local.get $x) (local.get $y))) + (func (export "and") (param $x i64) (param $y i64) (result i64) (i64.and (local.get $x) (local.get $y))) + (func (export "or") (param $x i64) (param $y i64) (result i64) (i64.or (local.get $x) (local.get $y))) + (func (export "xor") (param $x i64) (param $y i64) (result i64) (i64.xor (local.get $x) (local.get $y))) + (func (export "shl") (param $x i64) (param $y i64) (result i64) (i64.shl (local.get $x) (local.get $y))) + (func (export "shr_s") (param $x i64) (param $y i64) (result i64) (i64.shr_s (local.get $x) (local.get $y))) + (func (export "shr_u") (param $x i64) (param $y i64) (result i64) (i64.shr_u (local.get $x) (local.get $y))) + (func (export "rotl") (param $x i64) (param $y i64) (result i64) (i64.rotl (local.get $x) (local.get $y))) + (func (export "rotr") (param $x i64) (param $y i64) (result i64) (i64.rotr (local.get $x) (local.get $y))) + (func (export "clz") (param $x i64) (result i64) (i64.clz (local.get $x))) + (func (export "ctz") (param $x i64) (result i64) (i64.ctz (local.get $x))) + (func (export "popcnt") (param $x i64) (result i64) (i64.popcnt (local.get $x))) + (func (export "extend8_s") (param $x i64) (result i64) (i64.extend8_s (local.get $x))) + (func (export "extend16_s") (param $x i64) (result i64) (i64.extend16_s (local.get $x))) + (func (export "extend32_s") (param $x i64) (result i64) (i64.extend32_s (local.get $x))) + (func (export "eqz") (param $x i64) (result i32) (i64.eqz (local.get $x))) + (func (export "eq") (param $x i64) (param $y i64) (result i32) (i64.eq (local.get $x) (local.get $y))) + (func (export "ne") (param $x i64) (param $y i64) (result i32) (i64.ne (local.get $x) (local.get $y))) + (func (export "lt_s") (param $x i64) (param $y i64) (result i32) (i64.lt_s (local.get $x) (local.get $y))) + (func (export "lt_u") (param $x i64) (param $y i64) (result i32) (i64.lt_u (local.get $x) (local.get $y))) + (func (export "le_s") (param $x i64) (param $y i64) (result i32) (i64.le_s (local.get $x) (local.get $y))) + (func (export "le_u") (param $x i64) (param $y i64) (result i32) (i64.le_u (local.get $x) (local.get $y))) + (func (export "gt_s") (param $x i64) (param $y i64) (result i32) (i64.gt_s (local.get $x) (local.get $y))) + (func (export "gt_u") (param $x i64) (param $y i64) (result i32) (i64.gt_u (local.get $x) (local.get $y))) + (func (export "ge_s") (param $x i64) (param $y i64) (result i32) (i64.ge_s (local.get $x) (local.get $y))) + (func (export "ge_u") (param $x i64) (param $y i64) (result i32) (i64.ge_u (local.get $x) (local.get $y))) +) + +(assert_return (invoke "add" (i64.const 1) (i64.const 1)) (i64.const 2)) +(assert_return (invoke "add" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "add" (i64.const -1) (i64.const -1)) (i64.const -2)) +(assert_return (invoke "add" (i64.const -1) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "add" (i64.const 0x7fffffffffffffff) (i64.const 1)) (i64.const 0x8000000000000000)) +(assert_return (invoke "add" (i64.const 0x8000000000000000) (i64.const -1)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "add" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i64.const 0)) +(assert_return (invoke "add" (i64.const 0x3fffffff) (i64.const 1)) (i64.const 0x40000000)) + +(assert_return (invoke "sub" (i64.const 1) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "sub" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "sub" (i64.const -1) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "sub" (i64.const 0x7fffffffffffffff) (i64.const -1)) (i64.const 0x8000000000000000)) +(assert_return (invoke "sub" (i64.const 0x8000000000000000) (i64.const 1)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "sub" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i64.const 0)) +(assert_return (invoke "sub" (i64.const 0x3fffffff) (i64.const -1)) (i64.const 0x40000000)) + +(assert_return (invoke "mul" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "mul" (i64.const 1) (i64.const 0)) (i64.const 0)) +(assert_return (invoke "mul" (i64.const -1) (i64.const -1)) (i64.const 1)) +(assert_return (invoke "mul" (i64.const 0x1000000000000000) (i64.const 4096)) (i64.const 0)) +(assert_return (invoke "mul" (i64.const 0x8000000000000000) (i64.const 0)) (i64.const 0)) +(assert_return (invoke "mul" (i64.const 0x8000000000000000) (i64.const -1)) (i64.const 0x8000000000000000)) +(assert_return (invoke "mul" (i64.const 0x7fffffffffffffff) (i64.const -1)) (i64.const 0x8000000000000001)) +(assert_return (invoke "mul" (i64.const 0x0123456789abcdef) (i64.const 0xfedcba9876543210)) (i64.const 0x2236d88fe5618cf0)) +(assert_return (invoke "mul" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i64.const 1)) + +(assert_trap (invoke "div_s" (i64.const 1) (i64.const 0)) "integer divide by zero") +(assert_trap (invoke "div_s" (i64.const 0) (i64.const 0)) "integer divide by zero") +(assert_trap (invoke "div_s" (i64.const 0x8000000000000000) (i64.const -1)) "integer overflow") +(assert_trap (invoke "div_s" (i64.const 0x8000000000000000) (i64.const 0)) "integer divide by zero") +(assert_return (invoke "div_s" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "div_s" (i64.const 0) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "div_s" (i64.const 0) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "div_s" (i64.const -1) (i64.const -1)) (i64.const 1)) +(assert_return (invoke "div_s" (i64.const 0x8000000000000000) (i64.const 2)) (i64.const 0xc000000000000000)) +(assert_return (invoke "div_s" (i64.const 0x8000000000000001) (i64.const 1000)) (i64.const 0xffdf3b645a1cac09)) +(assert_return (invoke "div_s" (i64.const 5) (i64.const 2)) (i64.const 2)) +(assert_return (invoke "div_s" (i64.const -5) (i64.const 2)) (i64.const -2)) +(assert_return (invoke "div_s" (i64.const 5) (i64.const -2)) (i64.const -2)) +(assert_return (invoke "div_s" (i64.const -5) (i64.const -2)) (i64.const 2)) +(assert_return (invoke "div_s" (i64.const 7) (i64.const 3)) (i64.const 2)) +(assert_return (invoke "div_s" (i64.const -7) (i64.const 3)) (i64.const -2)) +(assert_return (invoke "div_s" (i64.const 7) (i64.const -3)) (i64.const -2)) +(assert_return (invoke "div_s" (i64.const -7) (i64.const -3)) (i64.const 2)) +(assert_return (invoke "div_s" (i64.const 11) (i64.const 5)) (i64.const 2)) +(assert_return (invoke "div_s" (i64.const 17) (i64.const 7)) (i64.const 2)) + +(assert_trap (invoke "div_u" (i64.const 1) (i64.const 0)) "integer divide by zero") +(assert_trap (invoke "div_u" (i64.const 0) (i64.const 0)) "integer divide by zero") +(assert_return (invoke "div_u" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "div_u" (i64.const 0) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "div_u" (i64.const -1) (i64.const -1)) (i64.const 1)) +(assert_return (invoke "div_u" (i64.const 0x8000000000000000) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "div_u" (i64.const 0x8000000000000000) (i64.const 2)) (i64.const 0x4000000000000000)) +(assert_return (invoke "div_u" (i64.const 0x8ff00ff00ff00ff0) (i64.const 0x100000001)) (i64.const 0x8ff00fef)) +(assert_return (invoke "div_u" (i64.const 0x8000000000000001) (i64.const 1000)) (i64.const 0x20c49ba5e353f7)) +(assert_return (invoke "div_u" (i64.const 5) (i64.const 2)) (i64.const 2)) +(assert_return (invoke "div_u" (i64.const -5) (i64.const 2)) (i64.const 0x7ffffffffffffffd)) +(assert_return (invoke "div_u" (i64.const 5) (i64.const -2)) (i64.const 0)) +(assert_return (invoke "div_u" (i64.const -5) (i64.const -2)) (i64.const 0)) +(assert_return (invoke "div_u" (i64.const 7) (i64.const 3)) (i64.const 2)) +(assert_return (invoke "div_u" (i64.const 11) (i64.const 5)) (i64.const 2)) +(assert_return (invoke "div_u" (i64.const 17) (i64.const 7)) (i64.const 2)) + +(assert_trap (invoke "rem_s" (i64.const 1) (i64.const 0)) "integer divide by zero") +(assert_trap (invoke "rem_s" (i64.const 0) (i64.const 0)) "integer divide by zero") +(assert_return (invoke "rem_s" (i64.const 0x7fffffffffffffff) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "rem_s" (i64.const 1) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "rem_s" (i64.const 0) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "rem_s" (i64.const 0) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "rem_s" (i64.const -1) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "rem_s" (i64.const 0x8000000000000000) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "rem_s" (i64.const 0x8000000000000000) (i64.const 2)) (i64.const 0)) +(assert_return (invoke "rem_s" (i64.const 0x8000000000000001) (i64.const 1000)) (i64.const -807)) +(assert_return (invoke "rem_s" (i64.const 5) (i64.const 2)) (i64.const 1)) +(assert_return (invoke "rem_s" (i64.const -5) (i64.const 2)) (i64.const -1)) +(assert_return (invoke "rem_s" (i64.const 5) (i64.const -2)) (i64.const 1)) +(assert_return (invoke "rem_s" (i64.const -5) (i64.const -2)) (i64.const -1)) +(assert_return (invoke "rem_s" (i64.const 7) (i64.const 3)) (i64.const 1)) +(assert_return (invoke "rem_s" (i64.const -7) (i64.const 3)) (i64.const -1)) +(assert_return (invoke "rem_s" (i64.const 7) (i64.const -3)) (i64.const 1)) +(assert_return (invoke "rem_s" (i64.const -7) (i64.const -3)) (i64.const -1)) +(assert_return (invoke "rem_s" (i64.const 11) (i64.const 5)) (i64.const 1)) +(assert_return (invoke "rem_s" (i64.const 17) (i64.const 7)) (i64.const 3)) + +(assert_trap (invoke "rem_u" (i64.const 1) (i64.const 0)) "integer divide by zero") +(assert_trap (invoke "rem_u" (i64.const 0) (i64.const 0)) "integer divide by zero") +(assert_return (invoke "rem_u" (i64.const 1) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "rem_u" (i64.const 0) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "rem_u" (i64.const -1) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "rem_u" (i64.const 0x8000000000000000) (i64.const -1)) (i64.const 0x8000000000000000)) +(assert_return (invoke "rem_u" (i64.const 0x8000000000000000) (i64.const 2)) (i64.const 0)) +(assert_return (invoke "rem_u" (i64.const 0x8ff00ff00ff00ff0) (i64.const 0x100000001)) (i64.const 0x80000001)) +(assert_return (invoke "rem_u" (i64.const 0x8000000000000001) (i64.const 1000)) (i64.const 809)) +(assert_return (invoke "rem_u" (i64.const 5) (i64.const 2)) (i64.const 1)) +(assert_return (invoke "rem_u" (i64.const -5) (i64.const 2)) (i64.const 1)) +(assert_return (invoke "rem_u" (i64.const 5) (i64.const -2)) (i64.const 5)) +(assert_return (invoke "rem_u" (i64.const -5) (i64.const -2)) (i64.const -5)) +(assert_return (invoke "rem_u" (i64.const 7) (i64.const 3)) (i64.const 1)) +(assert_return (invoke "rem_u" (i64.const 11) (i64.const 5)) (i64.const 1)) +(assert_return (invoke "rem_u" (i64.const 17) (i64.const 7)) (i64.const 3)) + +(assert_return (invoke "and" (i64.const 1) (i64.const 0)) (i64.const 0)) +(assert_return (invoke "and" (i64.const 0) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "and" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "and" (i64.const 0) (i64.const 0)) (i64.const 0)) +(assert_return (invoke "and" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i64.const 0)) +(assert_return (invoke "and" (i64.const 0x7fffffffffffffff) (i64.const -1)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "and" (i64.const 0xf0f0ffff) (i64.const 0xfffff0f0)) (i64.const 0xf0f0f0f0)) +(assert_return (invoke "and" (i64.const 0xffffffffffffffff) (i64.const 0xffffffffffffffff)) (i64.const 0xffffffffffffffff)) + +(assert_return (invoke "or" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "or" (i64.const 0) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "or" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "or" (i64.const 0) (i64.const 0)) (i64.const 0)) +(assert_return (invoke "or" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i64.const -1)) +(assert_return (invoke "or" (i64.const 0x8000000000000000) (i64.const 0)) (i64.const 0x8000000000000000)) +(assert_return (invoke "or" (i64.const 0xf0f0ffff) (i64.const 0xfffff0f0)) (i64.const 0xffffffff)) +(assert_return (invoke "or" (i64.const 0xffffffffffffffff) (i64.const 0xffffffffffffffff)) (i64.const 0xffffffffffffffff)) + +(assert_return (invoke "xor" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "xor" (i64.const 0) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "xor" (i64.const 1) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "xor" (i64.const 0) (i64.const 0)) (i64.const 0)) +(assert_return (invoke "xor" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i64.const -1)) +(assert_return (invoke "xor" (i64.const 0x8000000000000000) (i64.const 0)) (i64.const 0x8000000000000000)) +(assert_return (invoke "xor" (i64.const -1) (i64.const 0x8000000000000000)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "xor" (i64.const -1) (i64.const 0x7fffffffffffffff)) (i64.const 0x8000000000000000)) +(assert_return (invoke "xor" (i64.const 0xf0f0ffff) (i64.const 0xfffff0f0)) (i64.const 0x0f0f0f0f)) +(assert_return (invoke "xor" (i64.const 0xffffffffffffffff) (i64.const 0xffffffffffffffff)) (i64.const 0)) + +(assert_return (invoke "shl" (i64.const 1) (i64.const 1)) (i64.const 2)) +(assert_return (invoke "shl" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "shl" (i64.const 0x7fffffffffffffff) (i64.const 1)) (i64.const 0xfffffffffffffffe)) +(assert_return (invoke "shl" (i64.const 0xffffffffffffffff) (i64.const 1)) (i64.const 0xfffffffffffffffe)) +(assert_return (invoke "shl" (i64.const 0x8000000000000000) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "shl" (i64.const 0x4000000000000000) (i64.const 1)) (i64.const 0x8000000000000000)) +(assert_return (invoke "shl" (i64.const 1) (i64.const 63)) (i64.const 0x8000000000000000)) +(assert_return (invoke "shl" (i64.const 1) (i64.const 64)) (i64.const 1)) +(assert_return (invoke "shl" (i64.const 1) (i64.const 65)) (i64.const 2)) +(assert_return (invoke "shl" (i64.const 1) (i64.const -1)) (i64.const 0x8000000000000000)) +(assert_return (invoke "shl" (i64.const 1) (i64.const 0x7fffffffffffffff)) (i64.const 0x8000000000000000)) + +(assert_return (invoke "shr_s" (i64.const 1) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "shr_s" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "shr_s" (i64.const -1) (i64.const 1)) (i64.const -1)) +(assert_return (invoke "shr_s" (i64.const 0x7fffffffffffffff) (i64.const 1)) (i64.const 0x3fffffffffffffff)) +(assert_return (invoke "shr_s" (i64.const 0x8000000000000000) (i64.const 1)) (i64.const 0xc000000000000000)) +(assert_return (invoke "shr_s" (i64.const 0x4000000000000000) (i64.const 1)) (i64.const 0x2000000000000000)) +(assert_return (invoke "shr_s" (i64.const 1) (i64.const 64)) (i64.const 1)) +(assert_return (invoke "shr_s" (i64.const 1) (i64.const 65)) (i64.const 0)) +(assert_return (invoke "shr_s" (i64.const 1) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "shr_s" (i64.const 1) (i64.const 0x7fffffffffffffff)) (i64.const 0)) +(assert_return (invoke "shr_s" (i64.const 1) (i64.const 0x8000000000000000)) (i64.const 1)) +(assert_return (invoke "shr_s" (i64.const 0x8000000000000000) (i64.const 63)) (i64.const -1)) +(assert_return (invoke "shr_s" (i64.const -1) (i64.const 64)) (i64.const -1)) +(assert_return (invoke "shr_s" (i64.const -1) (i64.const 65)) (i64.const -1)) +(assert_return (invoke "shr_s" (i64.const -1) (i64.const -1)) (i64.const -1)) +(assert_return (invoke "shr_s" (i64.const -1) (i64.const 0x7fffffffffffffff)) (i64.const -1)) +(assert_return (invoke "shr_s" (i64.const -1) (i64.const 0x8000000000000000)) (i64.const -1)) + +(assert_return (invoke "shr_u" (i64.const 1) (i64.const 1)) (i64.const 0)) +(assert_return (invoke "shr_u" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "shr_u" (i64.const -1) (i64.const 1)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "shr_u" (i64.const 0x7fffffffffffffff) (i64.const 1)) (i64.const 0x3fffffffffffffff)) +(assert_return (invoke "shr_u" (i64.const 0x8000000000000000) (i64.const 1)) (i64.const 0x4000000000000000)) +(assert_return (invoke "shr_u" (i64.const 0x4000000000000000) (i64.const 1)) (i64.const 0x2000000000000000)) +(assert_return (invoke "shr_u" (i64.const 1) (i64.const 64)) (i64.const 1)) +(assert_return (invoke "shr_u" (i64.const 1) (i64.const 65)) (i64.const 0)) +(assert_return (invoke "shr_u" (i64.const 1) (i64.const -1)) (i64.const 0)) +(assert_return (invoke "shr_u" (i64.const 1) (i64.const 0x7fffffffffffffff)) (i64.const 0)) +(assert_return (invoke "shr_u" (i64.const 1) (i64.const 0x8000000000000000)) (i64.const 1)) +(assert_return (invoke "shr_u" (i64.const 0x8000000000000000) (i64.const 63)) (i64.const 1)) +(assert_return (invoke "shr_u" (i64.const -1) (i64.const 64)) (i64.const -1)) +(assert_return (invoke "shr_u" (i64.const -1) (i64.const 65)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "shr_u" (i64.const -1) (i64.const -1)) (i64.const 1)) +(assert_return (invoke "shr_u" (i64.const -1) (i64.const 0x7fffffffffffffff)) (i64.const 1)) +(assert_return (invoke "shr_u" (i64.const -1) (i64.const 0x8000000000000000)) (i64.const -1)) + +(assert_return (invoke "rotl" (i64.const 1) (i64.const 1)) (i64.const 2)) +(assert_return (invoke "rotl" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "rotl" (i64.const -1) (i64.const 1)) (i64.const -1)) +(assert_return (invoke "rotl" (i64.const 1) (i64.const 64)) (i64.const 1)) +(assert_return (invoke "rotl" (i64.const 0xabcd987602468ace) (i64.const 1)) (i64.const 0x579b30ec048d159d)) +(assert_return (invoke "rotl" (i64.const 0xfe000000dc000000) (i64.const 4)) (i64.const 0xe000000dc000000f)) +(assert_return (invoke "rotl" (i64.const 0xabcd1234ef567809) (i64.const 53)) (i64.const 0x013579a2469deacf)) +(assert_return (invoke "rotl" (i64.const 0xabd1234ef567809c) (i64.const 63)) (i64.const 0x55e891a77ab3c04e)) +(assert_return (invoke "rotl" (i64.const 0xabcd1234ef567809) (i64.const 0xf5)) (i64.const 0x013579a2469deacf)) +(assert_return (invoke "rotl" (i64.const 0xabcd7294ef567809) (i64.const 0xffffffffffffffed)) (i64.const 0xcf013579ae529dea)) +(assert_return (invoke "rotl" (i64.const 0xabd1234ef567809c) (i64.const 0x800000000000003f)) (i64.const 0x55e891a77ab3c04e)) +(assert_return (invoke "rotl" (i64.const 1) (i64.const 63)) (i64.const 0x8000000000000000)) +(assert_return (invoke "rotl" (i64.const 0x8000000000000000) (i64.const 1)) (i64.const 1)) + +(assert_return (invoke "rotr" (i64.const 1) (i64.const 1)) (i64.const 0x8000000000000000)) +(assert_return (invoke "rotr" (i64.const 1) (i64.const 0)) (i64.const 1)) +(assert_return (invoke "rotr" (i64.const -1) (i64.const 1)) (i64.const -1)) +(assert_return (invoke "rotr" (i64.const 1) (i64.const 64)) (i64.const 1)) +(assert_return (invoke "rotr" (i64.const 0xabcd987602468ace) (i64.const 1)) (i64.const 0x55e6cc3b01234567)) +(assert_return (invoke "rotr" (i64.const 0xfe000000dc000000) (i64.const 4)) (i64.const 0x0fe000000dc00000)) +(assert_return (invoke "rotr" (i64.const 0xabcd1234ef567809) (i64.const 53)) (i64.const 0x6891a77ab3c04d5e)) +(assert_return (invoke "rotr" (i64.const 0xabd1234ef567809c) (i64.const 63)) (i64.const 0x57a2469deacf0139)) +(assert_return (invoke "rotr" (i64.const 0xabcd1234ef567809) (i64.const 0xf5)) (i64.const 0x6891a77ab3c04d5e)) +(assert_return (invoke "rotr" (i64.const 0xabcd7294ef567809) (i64.const 0xffffffffffffffed)) (i64.const 0x94a77ab3c04d5e6b)) +(assert_return (invoke "rotr" (i64.const 0xabd1234ef567809c) (i64.const 0x800000000000003f)) (i64.const 0x57a2469deacf0139)) +(assert_return (invoke "rotr" (i64.const 1) (i64.const 63)) (i64.const 2)) +(assert_return (invoke "rotr" (i64.const 0x8000000000000000) (i64.const 63)) (i64.const 1)) + +(assert_return (invoke "clz" (i64.const 0xffffffffffffffff)) (i64.const 0)) +(assert_return (invoke "clz" (i64.const 0)) (i64.const 64)) +(assert_return (invoke "clz" (i64.const 0x00008000)) (i64.const 48)) +(assert_return (invoke "clz" (i64.const 0xff)) (i64.const 56)) +(assert_return (invoke "clz" (i64.const 0x8000000000000000)) (i64.const 0)) +(assert_return (invoke "clz" (i64.const 1)) (i64.const 63)) +(assert_return (invoke "clz" (i64.const 2)) (i64.const 62)) +(assert_return (invoke "clz" (i64.const 0x7fffffffffffffff)) (i64.const 1)) + +(assert_return (invoke "ctz" (i64.const -1)) (i64.const 0)) +(assert_return (invoke "ctz" (i64.const 0)) (i64.const 64)) +(assert_return (invoke "ctz" (i64.const 0x00008000)) (i64.const 15)) +(assert_return (invoke "ctz" (i64.const 0x00010000)) (i64.const 16)) +(assert_return (invoke "ctz" (i64.const 0x8000000000000000)) (i64.const 63)) +(assert_return (invoke "ctz" (i64.const 0x7fffffffffffffff)) (i64.const 0)) + +(assert_return (invoke "popcnt" (i64.const -1)) (i64.const 64)) +(assert_return (invoke "popcnt" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "popcnt" (i64.const 0x00008000)) (i64.const 1)) +(assert_return (invoke "popcnt" (i64.const 0x8000800080008000)) (i64.const 4)) +(assert_return (invoke "popcnt" (i64.const 0x7fffffffffffffff)) (i64.const 63)) +(assert_return (invoke "popcnt" (i64.const 0xAAAAAAAA55555555)) (i64.const 32)) +(assert_return (invoke "popcnt" (i64.const 0x99999999AAAAAAAA)) (i64.const 32)) +(assert_return (invoke "popcnt" (i64.const 0xDEADBEEFDEADBEEF)) (i64.const 48)) + +(assert_return (invoke "extend8_s" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "extend8_s" (i64.const 0x7f)) (i64.const 127)) +(assert_return (invoke "extend8_s" (i64.const 0x80)) (i64.const -128)) +(assert_return (invoke "extend8_s" (i64.const 0xff)) (i64.const -1)) +(assert_return (invoke "extend8_s" (i64.const 0x01234567_89abcd_00)) (i64.const 0)) +(assert_return (invoke "extend8_s" (i64.const 0xfedcba98_765432_80)) (i64.const -0x80)) +(assert_return (invoke "extend8_s" (i64.const -1)) (i64.const -1)) + +(assert_return (invoke "extend16_s" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "extend16_s" (i64.const 0x7fff)) (i64.const 32767)) +(assert_return (invoke "extend16_s" (i64.const 0x8000)) (i64.const -32768)) +(assert_return (invoke "extend16_s" (i64.const 0xffff)) (i64.const -1)) +(assert_return (invoke "extend16_s" (i64.const 0x12345678_9abc_0000)) (i64.const 0)) +(assert_return (invoke "extend16_s" (i64.const 0xfedcba98_7654_8000)) (i64.const -0x8000)) +(assert_return (invoke "extend16_s" (i64.const -1)) (i64.const -1)) + +(assert_return (invoke "extend32_s" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "extend32_s" (i64.const 0x7fff)) (i64.const 32767)) +(assert_return (invoke "extend32_s" (i64.const 0x8000)) (i64.const 32768)) +(assert_return (invoke "extend32_s" (i64.const 0xffff)) (i64.const 65535)) +(assert_return (invoke "extend32_s" (i64.const 0x7fffffff)) (i64.const 0x7fffffff)) +(assert_return (invoke "extend32_s" (i64.const 0x80000000)) (i64.const -0x80000000)) +(assert_return (invoke "extend32_s" (i64.const 0xffffffff)) (i64.const -1)) +(assert_return (invoke "extend32_s" (i64.const 0x01234567_00000000)) (i64.const 0)) +(assert_return (invoke "extend32_s" (i64.const 0xfedcba98_80000000)) (i64.const -0x80000000)) +(assert_return (invoke "extend32_s" (i64.const -1)) (i64.const -1)) + +(assert_return (invoke "eqz" (i64.const 0)) (i32.const 1)) +(assert_return (invoke "eqz" (i64.const 1)) (i32.const 0)) +(assert_return (invoke "eqz" (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "eqz" (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "eqz" (i64.const 0xffffffffffffffff)) (i32.const 0)) + +(assert_return (invoke "eq" (i64.const 0) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "eq" (i64.const 1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "eq" (i64.const -1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "eq" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "eq" (i64.const -1) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "eq" (i64.const 1) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const 0) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "eq" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 0)) + +(assert_return (invoke "ne" (i64.const 0) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "ne" (i64.const 1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "ne" (i64.const -1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "ne" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "ne" (i64.const -1) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "ne" (i64.const 1) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const 0) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "ne" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 1)) + +(assert_return (invoke "lt_s" (i64.const 0) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const 1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const -1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "lt_s" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const -1) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const 1) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const 0) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "lt_s" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "lt_s" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "lt_s" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "lt_s" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "lt_s" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 0)) + +(assert_return (invoke "lt_u" (i64.const 0) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const -1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const -1) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 1) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 0) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "lt_u" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "lt_u" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "lt_u" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "lt_u" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 1)) + +(assert_return (invoke "le_s" (i64.const 0) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const 1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const -1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const -1) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const 1) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "le_s" (i64.const 0) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "le_s" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "le_s" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "le_s" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 0)) + +(assert_return (invoke "le_u" (i64.const 0) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const 1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const -1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "le_u" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const -1) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const 1) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "le_u" (i64.const 0) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "le_u" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "le_u" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "le_u" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "le_u" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 1)) + +(assert_return (invoke "gt_s" (i64.const 0) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const 1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const -1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const -1) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const 1) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "gt_s" (i64.const 0) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "gt_s" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "gt_s" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "gt_s" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 1)) + +(assert_return (invoke "gt_u" (i64.const 0) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const 1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const -1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "gt_u" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const -1) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const 1) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "gt_u" (i64.const 0) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "gt_u" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "gt_u" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "gt_u" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "gt_u" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 0)) + +(assert_return (invoke "ge_s" (i64.const 0) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const 1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const -1) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "ge_s" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const -1) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const 1) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const 0) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "ge_s" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 0)) +(assert_return (invoke "ge_s" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "ge_s" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "ge_s" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 0)) +(assert_return (invoke "ge_s" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 1)) + +(assert_return (invoke "ge_u" (i64.const 0) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const -1) (i64.const 1)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 0x8000000000000000) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 0x7fffffffffffffff) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const -1) (i64.const -1)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 1) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 0) (i64.const 1)) (i32.const 0)) +(assert_return (invoke "ge_u" (i64.const 0x8000000000000000) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 0) (i64.const 0x8000000000000000)) (i32.const 0)) +(assert_return (invoke "ge_u" (i64.const 0x8000000000000000) (i64.const -1)) (i32.const 0)) +(assert_return (invoke "ge_u" (i64.const -1) (i64.const 0x8000000000000000)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff)) (i32.const 1)) +(assert_return (invoke "ge_u" (i64.const 0x7fffffffffffffff) (i64.const 0x8000000000000000)) (i32.const 0)) + + +;; Type check + +(assert_invalid (module (func (result i64) (i64.add (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.and (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.div_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.div_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.mul (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.or (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.rem_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.rem_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.rotl (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.rotr (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.shl (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.shr_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.shr_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.sub (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.xor (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.eqz (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.clz (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.ctz (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.popcnt (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.eq (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.ge_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.ge_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.gt_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.gt_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.le_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.le_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.lt_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.lt_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64.ne (i32.const 0) (f32.const 0)))) "type mismatch") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_address.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_address.wast new file mode 100644 index 000000000..9e023008b --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_address.wast @@ -0,0 +1,157 @@ +;; Load/Store v128 data with different valid offset/alignment + +(module + (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\10\11\12\13\14\15") + (data (offset (i32.const 65505)) "\16\17\18\19\20\21\22\23\24\25\26\27\28\29\30\31") + + (func (export "load_data_1") (param $i i32) (result v128) + (v128.load offset=0 (local.get $i)) ;; 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x10 0x11 0x12 0x13 0x14 0x15 + ) + (func (export "load_data_2") (param $i i32) (result v128) + (v128.load align=1 (local.get $i)) ;; 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x10 0x11 0x12 0x13 0x14 0x15 + ) + (func (export "load_data_3") (param $i i32) (result v128) + (v128.load offset=1 align=1 (local.get $i)) ;; 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x10 0x11 0x12 0x13 0x14 0x15 0x00 + ) + (func (export "load_data_4") (param $i i32) (result v128) + (v128.load offset=2 align=1 (local.get $i)) ;; 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x10 0x11 0x12 0x13 0x14 0x15 0x00 0x00 + ) + (func (export "load_data_5") (param $i i32) (result v128) + (v128.load offset=15 align=1 (local.get $i)) ;; 0x15 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + ) + + (func (export "store_data_0") (result v128) + (v128.store offset=0 (i32.const 0) (v128.const f32x4 0 1 2 3)) + (v128.load offset=0 (i32.const 0)) + ) + (func (export "store_data_1") (result v128) + (v128.store align=1 (i32.const 0) (v128.const i32x4 0 1 2 3)) + (v128.load align=1 (i32.const 0)) + ) + (func (export "store_data_2") (result v128) + (v128.store offset=1 align=1 (i32.const 0) (v128.const i16x8 0 1 2 3 4 5 6 7)) + (v128.load offset=1 align=1 (i32.const 0)) + ) + (func (export "store_data_3") (result v128) + (v128.store offset=2 align=1 (i32.const 0) (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) + (v128.load offset=2 align=1 (i32.const 0)) + ) + (func (export "store_data_4") (result v128) + (v128.store offset=15 align=1 (i32.const 0) (v128.const i32x4 0 1 2 3)) + (v128.load offset=15 (i32.const 0)) + ) + (func (export "store_data_5") (result v128) + (v128.store offset=65520 align=1 (i32.const 0) (v128.const i32x4 0 1 2 3)) + (v128.load offset=65520 (i32.const 0)) + ) + (func (export "store_data_6") (param $i i32) + (v128.store offset=1 align=1 (local.get $i) (v128.const i32x4 0 1 2 3)) + ) +) + +(assert_return (invoke "load_data_1" (i32.const 0)) (v128.const i32x4 0x03020100 0x07060504 0x11100908 0x15141312)) +(assert_return (invoke "load_data_2" (i32.const 0)) (v128.const i32x4 0x03020100 0x07060504 0x11100908 0x15141312)) +(assert_return (invoke "load_data_3" (i32.const 0)) (v128.const i32x4 0x04030201 0x08070605 0x12111009 0x00151413)) +(assert_return (invoke "load_data_4" (i32.const 0)) (v128.const i32x4 0x05040302 0x09080706 0x13121110 0x00001514)) +(assert_return (invoke "load_data_5" (i32.const 0)) (v128.const i32x4 0x00000015 0x00000000 0x00000000 0x00000000)) + +(assert_return (invoke "load_data_1" (i32.const 0)) (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x1110 0x1312 0x1514)) +(assert_return (invoke "load_data_2" (i32.const 0)) (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x1110 0x1312 0x1514)) +(assert_return (invoke "load_data_3" (i32.const 0)) (v128.const i16x8 0x0201 0x0403 0x0605 0x0807 0x1009 0x1211 0x1413 0x0015)) +(assert_return (invoke "load_data_4" (i32.const 0)) (v128.const i16x8 0x0302 0x0504 0x0706 0x0908 0x1110 0x1312 0x1514 0x0000)) +(assert_return (invoke "load_data_5" (i32.const 0)) (v128.const i16x8 0x0015 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000)) + +(assert_return (invoke "load_data_1" (i32.const 0)) (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x10 0x11 0x12 0x13 0x14 0x15)) +(assert_return (invoke "load_data_2" (i32.const 0)) (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x10 0x11 0x12 0x13 0x14 0x15)) +(assert_return (invoke "load_data_3" (i32.const 0)) (v128.const i8x16 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x10 0x11 0x12 0x13 0x14 0x15 0x00)) +(assert_return (invoke "load_data_4" (i32.const 0)) (v128.const i8x16 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x10 0x11 0x12 0x13 0x14 0x15 0x00 0x00)) +(assert_return (invoke "load_data_5" (i32.const 0)) (v128.const i8x16 0x15 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + +(assert_return (invoke "load_data_1" (i32.const 65505)) (v128.const i32x4 0x19181716 0x23222120 0x27262524 0x31302928)) +(assert_return (invoke "load_data_2" (i32.const 65505)) (v128.const i32x4 0x19181716 0x23222120 0x27262524 0x31302928)) +(assert_return (invoke "load_data_3" (i32.const 65505)) (v128.const i32x4 0x20191817 0x24232221 0x28272625 0x00313029)) +(assert_return (invoke "load_data_4" (i32.const 65505)) (v128.const i32x4 0x21201918 0x25242322 0x29282726 0x00003130)) +(assert_return (invoke "load_data_5" (i32.const 65505)) (v128.const i32x4 0x00000031 0x00000000 0x00000000 0x00000000)) + +(assert_return (invoke "load_data_1" (i32.const 65505)) (v128.const i16x8 0x1716 0x1918 0x2120 0x2322 0x2524 0x2726 0x2928 0x3130)) +(assert_return (invoke "load_data_2" (i32.const 65505)) (v128.const i16x8 0x1716 0x1918 0x2120 0x2322 0x2524 0x2726 0x2928 0x3130)) +(assert_return (invoke "load_data_3" (i32.const 65505)) (v128.const i16x8 0x1817 0x2019 0x2221 0x2423 0x2625 0x2827 0x3029 0x0031)) +(assert_return (invoke "load_data_4" (i32.const 65505)) (v128.const i16x8 0x1918 0x2120 0x2322 0x2524 0x2726 0x2928 0x3130 0x0000)) +(assert_return (invoke "load_data_5" (i32.const 65505)) (v128.const i16x8 0x0031 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000)) + +(assert_return (invoke "load_data_1" (i32.const 65505)) (v128.const i8x16 0x16 0x17 0x18 0x19 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x30 0x31)) +(assert_return (invoke "load_data_2" (i32.const 65505)) (v128.const i8x16 0x16 0x17 0x18 0x19 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x30 0x31)) +(assert_return (invoke "load_data_3" (i32.const 65505)) (v128.const i8x16 0x17 0x18 0x19 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x30 0x31 0x00)) +(assert_return (invoke "load_data_4" (i32.const 65505)) (v128.const i8x16 0x18 0x19 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x30 0x31 0x00 0x00)) +(assert_return (invoke "load_data_5" (i32.const 65505)) (v128.const i8x16 0x31 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + +(assert_trap (invoke "load_data_3" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "load_data_5" (i32.const 65506)) "out of bounds memory access") + +(assert_return (invoke "store_data_0") (v128.const f32x4 0 1 2 3)) +(assert_return (invoke "store_data_1") (v128.const i32x4 0 1 2 3)) +(assert_return (invoke "store_data_2") (v128.const i16x8 0 1 2 3 4 5 6 7)) +(assert_return (invoke "store_data_3") (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) +(assert_return (invoke "store_data_4") (v128.const i32x4 0 1 2 3)) +(assert_return (invoke "store_data_5") (v128.const i32x4 0 1 2 3)) + +(assert_trap (invoke "store_data_6" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "store_data_6" (i32.const 65535)) "out of bounds memory access") + +;; Load/Store v128 data with invalid offset + +(module + (memory 1) + (func (export "v128.load_offset_65521") + (drop (v128.load offset=65521 (i32.const 0))) + ) +) +(assert_trap (invoke "v128.load_offset_65521") "out of bounds memory access") + +(assert_malformed + (module quote + "(memory 1)" + "(func" + " (drop (v128.load offset=-1 (i32.const 0)))" + ")" + ) + "unknown operator" +) + +(module + (memory 1) + (func (export "v128.store_offset_65521") + (v128.store offset=65521 (i32.const 0) (v128.const i32x4 0 0 0 0)) + ) +) +(assert_trap (invoke "v128.store_offset_65521") "out of bounds memory access") + +(assert_malformed + (module quote + "(memory 1)" + "(func" + " (v128.store offset=-1 (i32.const 0) (v128.const i32x4 0 0 0 0))" + ")" + ) + "unknown operator" +) + + +;; Offset constant out of range + +(assert_malformed + (module quote + "(memory 1)" + "(func (drop (v128.load offset=4294967296 (i32.const 0))))" + ) + "i32 constant" +) + +(assert_malformed + (module quote + "(memory 1)" + "(func (v128.store offset=4294967296 (i32.const 0) (v128.const i32x4 0 0 0 0)))" + ) + "i32 constant" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_align.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_align.wast new file mode 100644 index 000000000..ed91ed5c6 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_align.wast @@ -0,0 +1,355 @@ +;; Valid alignment + +(module (memory 1) (func (drop (v128.load align=1 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load align=2 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load align=4 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load align=8 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load align=16 (i32.const 0))))) + +(module (memory 1) (func (v128.store align=1 (i32.const 0) (v128.const i32x4 0 1 2 3)))) +(module (memory 1) (func (v128.store align=2 (i32.const 0) (v128.const i32x4 0 1 2 3)))) +(module (memory 1) (func (v128.store align=4 (i32.const 0) (v128.const i32x4 0 1 2 3)))) +(module (memory 1) (func (v128.store align=8 (i32.const 0) (v128.const i32x4 0 1 2 3)))) +(module (memory 1) (func (v128.store align=16 (i32.const 0) (v128.const i32x4 0 1 2 3)))) + +(module (memory 1) (func (drop (v128.load8x8_s align=1 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load8x8_s align=2 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load8x8_s align=4 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load8x8_s align=8 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load8x8_u align=1 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load8x8_u align=2 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load8x8_u align=4 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load8x8_u align=8 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load16x4_s align=1 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load16x4_s align=2 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load16x4_s align=4 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load16x4_s align=8 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load16x4_u align=1 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load16x4_u align=2 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load16x4_u align=4 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load16x4_u align=8 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load32x2_s align=1 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load32x2_s align=2 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load32x2_s align=4 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load32x2_s align=8 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load32x2_u align=1 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load32x2_u align=2 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load32x2_u align=4 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load32x2_u align=8 (i32.const 0))))) + +(module (memory 1) (func (drop (v128.load8_splat align=1 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load16_splat align=1 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load16_splat align=2 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load32_splat align=1 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load32_splat align=2 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load32_splat align=4 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load64_splat align=1 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load64_splat align=2 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load64_splat align=4 (i32.const 0))))) +(module (memory 1) (func (drop (v128.load64_splat align=8 (i32.const 0))))) + +;; Invalid alignment + +(assert_invalid + (module (memory 1) (func (drop (v128.load align=32 (i32.const 0))))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 0) (func(v128.store align=32 (i32.const 0) (v128.const i32x4 0 0 0 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 1) (func (result v128) (v128.load8x8_s align=16 (i32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 1) (func (result v128) (v128.load8x8_u align=16 (i32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 1) (func (result v128) (v128.load16x4_s align=16 (i32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 1) (func (result v128) (v128.load16x4_u align=16 (i32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 1) (func (result v128) (v128.load32x2_s align=16 (i32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 1) (func (result v128) (v128.load32x2_u align=16 (i32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 1) (func (result v128) (v128.load8_splat align=2 (i32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 1) (func (result v128) (v128.load16_splat align=4 (i32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 1) (func (result v128) (v128.load32_splat align=8 (i32.const 0)))) + "alignment must not be larger than natural" +) +(assert_invalid + (module (memory 1) (func (result v128) (v128.load64_splat align=16 (i32.const 0)))) + "alignment must not be larger than natural" +) + +;; Malformed alignment + +(assert_malformed + (module quote + "(memory 1) (func (drop (v128.load align=-1 (i32.const 0))))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1) (func (drop (v128.load align=0 (i32.const 0))))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (drop (v128.load align=7 (i32.const 0))))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (v128.store align=-1 (i32.const 0) (v128.const i32x4 0 0 0 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 0) (func (v128.store align=0 (i32.const 0) (v128.const i32x4 0 0 0 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 0) (func (v128.store align=7 (i32.const 0) (v128.const i32x4 0 0 0 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load8x8_s align=-1 (i32.const 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load8x8_s align=0 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load8x8_s align=7 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load8x8_u align=-1 (i32.const 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load8x8_u align=0 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load8x8_u align=7 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load16x4_s align=-1 (i32.const 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load16x4_s align=0 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load16x4_s align=7 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load16x4_u align=-1 (i32.const 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load16x4_u align=0 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load16x4_u align=7 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load32x2_s align=-1 (i32.const 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load32x2_s align=0 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load32x2_s align=7 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load32x2_u align=-1 (i32.const 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load32x2_u align=0 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load32x2_u align=7 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load8_splat align=-1 (i32.const 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load8_splat align=0 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load16_splat align=-1 (i32.const 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load16_splat align=0 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load32_splat align=-1 (i32.const 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load32_splat align=0 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load32_splat align=3 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load64_splat align=-1 (i32.const 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load64_splat align=0 (i32.const 0)))" + ) + "alignment must be a power of two" +) +(assert_malformed + (module quote + "(memory 1) (func (result v128) (v128.load64_splat align=7 (i32.const 0)))" + ) + "alignment must be a power of two" +) + +;; Test that misaligned SIMD loads/stores don't trap + +(module + (memory 1 1) + (func (export "v128.load align=16") (param $address i32) (result v128) + (v128.load align=16 (local.get $address)) + ) + (func (export "v128.store align=16") (param $address i32) (param $value v128) + (v128.store align=16 (local.get $address) (local.get $value)) + ) +) + +(assert_return (invoke "v128.load align=16" (i32.const 0)) (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "v128.load align=16" (i32.const 1)) (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "v128.store align=16" (i32.const 1) (v128.const i8x16 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16))) +(assert_return (invoke "v128.load align=16" (i32.const 0)) (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) + +;; Test aligned and unaligned read/write + +(module + (memory 1) + (func (export "v128_unaligned_read_and_write") (result v128) + (local v128) + (v128.store (i32.const 0) (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) + (v128.load (i32.const 0)) + ) + (func (export "v128_aligned_read_and_write") (result v128) + (local v128) + (v128.store align=2 (i32.const 0) (v128.const i16x8 0 1 2 3 4 5 6 7)) + (v128.load align=2 (i32.const 0)) + ) + (func (export "v128_aligned_read_and_unaligned_write") (result v128) + (local v128) + (v128.store (i32.const 0) (v128.const i32x4 0 1 2 3)) + (v128.load align=2 (i32.const 0)) + ) + (func (export "v128_unaligned_read_and_aligned_write") (result v128) + (local v128) + (v128.store align=2 (i32.const 0) (v128.const i32x4 0 1 2 3)) + (v128.load (i32.const 0)) + ) +) + +(assert_return (invoke "v128_unaligned_read_and_write") (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) +(assert_return (invoke "v128_aligned_read_and_write") (v128.const i16x8 0 1 2 3 4 5 6 7)) +(assert_return (invoke "v128_aligned_read_and_unaligned_write") (v128.const i32x4 0 1 2 3)) +(assert_return (invoke "v128_unaligned_read_and_aligned_write") (v128.const i32x4 0 1 2 3)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_bit_shift.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_bit_shift.wast new file mode 100644 index 000000000..98e155651 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_bit_shift.wast @@ -0,0 +1,1104 @@ +;; Test all the bit shift operators on major boundary values and all special values. + +(module + (func (export "i8x16.shl") (param $0 v128) (param $1 i32) (result v128) (i8x16.shl (local.get $0) (local.get $1))) + (func (export "i8x16.shr_s") (param $0 v128) (param $1 i32) (result v128) (i8x16.shr_s (local.get $0) (local.get $1))) + (func (export "i8x16.shr_u") (param $0 v128) (param $1 i32) (result v128) (i8x16.shr_u (local.get $0) (local.get $1))) + + (func (export "i16x8.shl") (param $0 v128) (param $1 i32) (result v128) (i16x8.shl (local.get $0) (local.get $1))) + (func (export "i16x8.shr_s") (param $0 v128) (param $1 i32) (result v128) (i16x8.shr_s (local.get $0) (local.get $1))) + (func (export "i16x8.shr_u") (param $0 v128) (param $1 i32) (result v128) (i16x8.shr_u (local.get $0) (local.get $1))) + + (func (export "i32x4.shl") (param $0 v128) (param $1 i32) (result v128) (i32x4.shl (local.get $0) (local.get $1))) + (func (export "i32x4.shr_s") (param $0 v128) (param $1 i32) (result v128) (i32x4.shr_s (local.get $0) (local.get $1))) + (func (export "i32x4.shr_u") (param $0 v128) (param $1 i32) (result v128) (i32x4.shr_u (local.get $0) (local.get $1))) + + (func (export "i64x2.shl") (param $0 v128) (param $1 i32) (result v128) (i64x2.shl (local.get $0) (local.get $1))) + (func (export "i64x2.shr_s") (param $0 v128) (param $1 i32) (result v128) (i64x2.shr_s (local.get $0) (local.get $1))) + (func (export "i64x2.shr_u") (param $0 v128) (param $1 i32) (result v128) (i64x2.shr_u (local.get $0) (local.get $1))) + + ;; shifting by a constant amount + ;; i8x16 + (func (export "i8x16.shl_1") (param $0 v128) (result v128) (i8x16.shl (local.get $0) (i32.const 1))) + (func (export "i8x16.shr_u_8") (param $0 v128) (result v128) (i8x16.shr_u (local.get $0) (i32.const 8))) + (func (export "i8x16.shr_s_9") (param $0 v128) (result v128) (i8x16.shr_s (local.get $0) (i32.const 9))) + + ;; i16x8 + (func (export "i16x8.shl_1") (param $0 v128) (result v128) (i16x8.shl (local.get $0) (i32.const 1))) + (func (export "i16x8.shr_u_16") (param $0 v128) (result v128) (i16x8.shr_u (local.get $0) (i32.const 16))) + (func (export "i16x8.shr_s_17") (param $0 v128) (result v128) (i16x8.shr_s (local.get $0) (i32.const 17))) + + ;; i32x4 + (func (export "i32x4.shl_1") (param $0 v128) (result v128) (i32x4.shl (local.get $0) (i32.const 1))) + (func (export "i32x4.shr_u_32") (param $0 v128) (result v128) (i32x4.shr_u (local.get $0) (i32.const 32))) + (func (export "i32x4.shr_s_33") (param $0 v128) (result v128) (i32x4.shr_s (local.get $0) (i32.const 33))) + + ;; i64x2 + (func (export "i64x2.shl_1") (param $0 v128) (result v128) (i64x2.shl (local.get $0) (i32.const 1))) + (func (export "i64x2.shr_u_64") (param $0 v128) (result v128) (i64x2.shr_u (local.get $0) (i32.const 64))) + (func (export "i64x2.shr_s_65") (param $0 v128) (result v128) (i64x2.shr_s (local.get $0) (i32.const 65))) +) + +;; i8x16 shl +;; amount less than lane width +(assert_return (invoke "i8x16.shl" (v128.const i8x16 -128 -64 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D) + (i32.const 1)) + (v128.const i8x16 0 -128 0 2 4 6 8 10 12 14 16 18 0x14 0x16 0x18 0x1A)) +(assert_return (invoke "i8x16.shl" (v128.const i8x16 0xAA 0xBB 0xCC 0xDD 0xEE 0xFF 0xA0 0xB0 0xC0 0xD0 0xE0 0xF0 0x0A 0x0B 0x0C 0x0D) + (i32.const 4)) + (v128.const i8x16 0xA0 0xB0 0xC0 0xD0 0xE0 0xF0 0x00 0x00 0x00 0x00 0x00 0x00 0xA0 0xB0 0xC0 0xD0)) +;; amount is multiple of lane width +(assert_return (invoke "i8x16.shl" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 8)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) +(assert_return (invoke "i8x16.shl" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 32)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) +(assert_return (invoke "i8x16.shl" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 128)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) +(assert_return (invoke "i8x16.shl" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 256)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) +;; amount greater than but not a multiple of lane width +(assert_return (invoke "i8x16.shl" (v128.const i8x16 -128 -64 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D) + (i32.const 9)) + (v128.const i8x16 0 -128 0 2 4 6 8 10 12 14 16 18 0x14 0x16 0x18 0x1A)) +(assert_return (invoke "i8x16.shl" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 9)) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 0x14 0x16 0x18 0x1A 0x1C 0x1E)) +(assert_return (invoke "i8x16.shl" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 17)) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 0x14 0x16 0x18 0x1A 0x1C 0x1E)) +(assert_return (invoke "i8x16.shl" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 33)) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 0x14 0x16 0x18 0x1A 0x1C 0x1E)) +(assert_return (invoke "i8x16.shl" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 129)) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 0x14 0x16 0x18 0x1A 0x1C 0x1E)) +(assert_return (invoke "i8x16.shl" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 257)) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 0x14 0x16 0x18 0x1A 0x1C 0x1E)) +(assert_return (invoke "i8x16.shl" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 513)) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 0x14 0x16 0x18 0x1A 0x1C 0x1E)) +(assert_return (invoke "i8x16.shl" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 514)) + (v128.const i8x16 0 4 8 12 16 20 24 28 32 36 0x28 0x2C 0x30 0x34 0x38 0x3C)) +;; i8x16 shr_u +;; amount less than lane width +(assert_return (invoke "i8x16.shr_u" (v128.const i8x16 -128 -64 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D) + (i32.const 1)) + (v128.const i8x16 64 96 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06)) +(assert_return (invoke "i8x16.shr_u" (v128.const i8x16 0xAA 0xBB 0xCC 0xDD 0xEE 0xFF 0xA0 0xB0 0xC0 0xD0 0xE0 0xF0 0x0A 0x0B 0x0C 0x0D) + (i32.const 4)) + (v128.const i8x16 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x00 0x00 0x00 0x00)) +;; amount is multiple of lane width +(assert_return (invoke "i8x16.shr_u" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 8)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) +(assert_return (invoke "i8x16.shr_u" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 32)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) +(assert_return (invoke "i8x16.shr_u" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 128)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) +(assert_return (invoke "i8x16.shr_u" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 256)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) +;; amount greater than but not a multiple of lane width +(assert_return (invoke "i8x16.shr_u" (v128.const i8x16 -128 -64 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D) + (i32.const 9)) + (v128.const i8x16 64 96 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06)) +(assert_return (invoke "i8x16.shr_u" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 9)) + (v128.const i8x16 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06 0x07 0x07)) +(assert_return (invoke "i8x16.shr_u" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 17)) + (v128.const i8x16 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06 0x07 0x07)) +(assert_return (invoke "i8x16.shr_u" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 33)) + (v128.const i8x16 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06 0x07 0x07)) +(assert_return (invoke "i8x16.shr_u" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 129)) + (v128.const i8x16 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06 0x07 0x07)) +(assert_return (invoke "i8x16.shr_u" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 257)) + (v128.const i8x16 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06 0x07 0x07)) +(assert_return (invoke "i8x16.shr_u" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 513)) + (v128.const i8x16 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06 0x07 0x07)) +(assert_return (invoke "i8x16.shr_u" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 514)) + (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 0x02 0x02 0x03 0x03 0x03 0x03)) +;; i8x16 shr_s +;; amount less than lane width +(assert_return (invoke "i8x16.shr_s" (v128.const i8x16 -128 -64 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D) + (i32.const 1)) + (v128.const i8x16 192 224 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06)) +(assert_return (invoke "i8x16.shr_s" (v128.const i8x16 0xAA 0xBB 0xCC 0xDD 0xEE 0xFF 0xA0 0xB0 0xC0 0xD0 0xE0 0xF0 0x0A 0x0B 0x0C 0x0D) + (i32.const 4)) + (v128.const i8x16 0xFA 0xFB 0xFC 0xFD 0xFE 0xFF 0xFA 0xFB 0xFC 0xFD 0xFE 0xFF 0x00 0x00 0x00 0x00)) +;; amount is multiple of lane width +(assert_return (invoke "i8x16.shr_s" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 8)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) +(assert_return (invoke "i8x16.shr_s" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 32)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) +(assert_return (invoke "i8x16.shr_s" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 128)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) +(assert_return (invoke "i8x16.shr_s" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 256)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) +;; amount greater than but not a multiple of lane width +(assert_return (invoke "i8x16.shr_s" (v128.const i8x16 -128 -64 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D) + (i32.const 9)) + (v128.const i8x16 192 224 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06)) +(assert_return (invoke "i8x16.shr_s" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 9)) + (v128.const i8x16 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06 0x07 0x07)) +(assert_return (invoke "i8x16.shr_s" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 17)) + (v128.const i8x16 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06 0x07 0x07)) +(assert_return (invoke "i8x16.shr_s" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 33)) + (v128.const i8x16 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06 0x07 0x07)) +(assert_return (invoke "i8x16.shr_s" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 129)) + (v128.const i8x16 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06 0x07 0x07)) +(assert_return (invoke "i8x16.shr_s" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 257)) + (v128.const i8x16 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06 0x07 0x07)) +(assert_return (invoke "i8x16.shr_s" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 513)) + (v128.const i8x16 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06 0x07 0x07)) +(assert_return (invoke "i8x16.shr_s" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F) + (i32.const 514)) + (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 0x02 0x02 0x03 0x03 0x03 0x03)) +;; shifting by a constant amount +(assert_return (invoke "i8x16.shl_1" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 0x14 0x16 0x18 0x1A 0x1C 0x1E)) +(assert_return (invoke "i8x16.shr_u_8" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) +(assert_return (invoke "i8x16.shr_s_9" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 0x0A 0x0B 0x0C 0x0D 0x0e 0x0F)) + (v128.const i8x16 0 0 1 1 2 2 3 3 4 4 0x05 0x05 0x06 0x06 0x07 0x07)) + +;; i16x8 shl +;; amount less than lane width +(assert_return (invoke "i16x8.shl" (v128.const i16x8 -128 -64 0 1 2 3 4 5) + (i32.const 1)) + (v128.const i16x8 65280 65408 0 2 4 6 8 10)) +(assert_return (invoke "i16x8.shl" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (i32.const 2)) + (v128.const i16x8 49380 49380 49380 49380 49380 49380 49380 49380)) +(assert_return (invoke "i16x8.shl" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (i32.const 2)) + (v128.const i16x8 0x48d0 0x48d0 0x48d0 0x48d0 0x48d0 0x48d0 0x48d0 0x48d0)) +(assert_return (invoke "i16x8.shl" (v128.const i16x8 0xAABB 0xCCDD 0xEEFF 0xA0B0 0xC0D0 0xE0F0 0x0A0B 0x0C0D) + (i32.const 4)) + (v128.const i16x8 0xABB0 0xCDD0 0xEFF0 0xB00 0xD00 0xF00 0xA0B0 0xC0D0)) +(assert_return (invoke "i16x8.shl" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 8)) + (v128.const i16x8 0 256 512 768 1024 1280 1536 1792)) +;; amount is multiple of lane width +(assert_return (invoke "i16x8.shl" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 32)) + (v128.const i16x8 0 1 2 3 4 5 6 7)) +(assert_return (invoke "i16x8.shl" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 128)) + (v128.const i16x8 0 1 2 3 4 5 6 7)) +(assert_return (invoke "i16x8.shl" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 256)) + (v128.const i16x8 0 1 2 3 4 5 6 7)) +;; amount greater than but not a multiple of lane width +(assert_return (invoke "i16x8.shl" (v128.const i16x8 -128 -64 0 1 2 3 4 5) + (i32.const 17)) + (v128.const i16x8 65280 65408 0 2 4 6 8 10)) +(assert_return (invoke "i16x8.shl" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 17)) + (v128.const i16x8 0 2 4 6 8 10 12 14)) +(assert_return (invoke "i16x8.shl" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 33)) + (v128.const i16x8 0 2 4 6 8 10 12 14)) +(assert_return (invoke "i16x8.shl" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 129)) + (v128.const i16x8 0 2 4 6 8 10 12 14)) +(assert_return (invoke "i16x8.shl" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 257)) + (v128.const i16x8 0 2 4 6 8 10 12 14)) +(assert_return (invoke "i16x8.shl" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 513)) + (v128.const i16x8 0 2 4 6 8 10 12 14)) +(assert_return (invoke "i16x8.shl" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 514)) + (v128.const i16x8 0 4 8 12 16 20 24 28)) + +;; i16x8 shr_u +;; amount less than lane width +(assert_return (invoke "i16x8.shr_u" (v128.const i16x8 -128 -64 0 1 2 3 4 5) + (i32.const 1)) + (v128.const i16x8 32704 32736 0 0 1 1 2 2)) +(assert_return (invoke "i16x8.shr_u" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (i32.const 2)) + (v128.const i16x8 3086 3086 3086 3086 3086 3086 3086 3086)) +(assert_return (invoke "i16x8.shr_u" (v128.const i16x8 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB) + (i32.const 2)) + (v128.const i16x8 0x242a 0x242a 0x242a 0x242a 0x242a 0x242a 0x242a 0x242a)) +(assert_return (invoke "i16x8.shr_u" (v128.const i16x8 0xAABB 0xCCDD 0xEEFF 0xA0B0 0xC0D0 0xE0F0 0x0A0B 0x0C0D) + (i32.const 4)) + (v128.const i16x8 0xAAB 0xCCD 0xEEF 0xA0B 0xC0D 0xE0F 0x0A0 0x0C0)) +(assert_return (invoke "i16x8.shr_u" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 8)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +;; amount is multiple of lane width +(assert_return (invoke "i16x8.shr_u" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 32)) + (v128.const i16x8 0 1 2 3 4 5 6 7)) +(assert_return (invoke "i16x8.shr_u" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 128)) + (v128.const i16x8 0 1 2 3 4 5 6 7)) +(assert_return (invoke "i16x8.shr_u" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 256)) + (v128.const i16x8 0 1 2 3 4 5 6 7)) +;; amount greater than but not a multiple of lane width +(assert_return (invoke "i16x8.shr_u" (v128.const i16x8 -128 -64 0 1 2 3 4 5) + (i32.const 17)) + (v128.const i16x8 32704 32736 0 0 1 1 2 2)) +(assert_return (invoke "i16x8.shr_u" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 17)) + (v128.const i16x8 0 0 1 1 2 2 3 3)) +(assert_return (invoke "i16x8.shr_u" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 33)) + (v128.const i16x8 0 0 1 1 2 2 3 3)) +(assert_return (invoke "i16x8.shr_u" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 129)) + (v128.const i16x8 0 0 1 1 2 2 3 3)) +(assert_return (invoke "i16x8.shr_u" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 257)) + (v128.const i16x8 0 0 1 1 2 2 3 3)) +(assert_return (invoke "i16x8.shr_u" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 513)) + (v128.const i16x8 0 0 1 1 2 2 3 3)) +(assert_return (invoke "i16x8.shr_u" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 514)) + (v128.const i16x8 0 0 0 0 1 1 1 1)) + +;; i16x8 shr_s +;; amount less than lane width +(assert_return (invoke "i16x8.shr_s" (v128.const i16x8 -128 -64 0 1 2 3 4 5) + (i32.const 1)) + (v128.const i16x8 65472 65504 0 0 1 1 2 2)) +(assert_return (invoke "i16x8.shr_s" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (i32.const 2)) + (v128.const i16x8 3086 3086 3086 3086 3086 3086 3086 3086)) +(assert_return (invoke "i16x8.shr_s" (v128.const i16x8 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB) + (i32.const 2)) + (v128.const i16x8 0xe42a 0xe42a 0xe42a 0xe42a 0xe42a 0xe42a 0xe42a 0xe42a)) +(assert_return (invoke "i16x8.shr_s" (v128.const i16x8 0xAABB 0xCCDD 0xEEFF 0xA0B0 0xC0D0 0xE0F0 0x0A0B 0x0C0D) + (i32.const 4)) + (v128.const i16x8 0xFAAB 0xFCCD 0xFEEF 0xFA0B 0xFC0D 0xFE0F 0x00A0 0x00C0)) +(assert_return (invoke "i16x8.shr_s" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 8)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +;; amount is multiple of lane width +(assert_return (invoke "i16x8.shr_s" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 32)) + (v128.const i16x8 0 1 2 3 4 5 6 7)) +(assert_return (invoke "i16x8.shr_s" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 128)) + (v128.const i16x8 0 1 2 3 4 5 6 7)) +(assert_return (invoke "i16x8.shr_s" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 256)) + (v128.const i16x8 0 1 2 3 4 5 6 7)) +;; amount greater than but not a multiple of lane width +(assert_return (invoke "i16x8.shr_s" (v128.const i16x8 -128 -64 0 1 2 3 4 5) + (i32.const 17)) + (v128.const i16x8 65472 65504 0 0 1 1 2 2)) +(assert_return (invoke "i16x8.shr_s" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 17)) + (v128.const i16x8 0 0 1 1 2 2 3 3)) +(assert_return (invoke "i16x8.shr_s" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 33)) + (v128.const i16x8 0 0 1 1 2 2 3 3)) +(assert_return (invoke "i16x8.shr_s" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 129)) + (v128.const i16x8 0 0 1 1 2 2 3 3)) +(assert_return (invoke "i16x8.shr_s" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 257)) + (v128.const i16x8 0 0 1 1 2 2 3 3)) +(assert_return (invoke "i16x8.shr_s" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 513)) + (v128.const i16x8 0 0 1 1 2 2 3 3)) +(assert_return (invoke "i16x8.shr_s" (v128.const i16x8 0 1 2 3 4 5 6 7) + (i32.const 514)) + (v128.const i16x8 0 0 0 0 1 1 1 1)) + +;; shifting by a constant amount +(assert_return (invoke "i16x8.shl_1" (v128.const i16x8 0 1 2 3 4 5 6 7)) + (v128.const i16x8 0 2 4 6 8 10 12 14)) +(assert_return (invoke "i16x8.shr_u_16" (v128.const i16x8 0 1 2 3 4 5 6 7)) + (v128.const i16x8 0 1 2 3 4 5 6 7)) +(assert_return (invoke "i16x8.shr_s_17" (v128.const i16x8 0 1 2 3 4 5 6 7)) + (v128.const i16x8 0 0 1 1 2 2 3 3)) + +;; i32x4 shl +;; amount less than lane width +(assert_return (invoke "i32x4.shl" (v128.const i32x4 -2147483648 -32768 0 0x0A0B0C0D) + (i32.const 1)) + (v128.const i32x4 0 4294901760 0 0x1416181A)) +(assert_return (invoke "i32x4.shl" (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890) + (i32.const 2)) + (v128.const i32x4 643304264 643304264 643304264 643304264)) +(assert_return (invoke "i32x4.shl" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678) + (i32.const 2)) + (v128.const i32x4 0x48d159e0 0x48d159e0 0x48d159e0 0x48d159e0)) +(assert_return (invoke "i32x4.shl" (v128.const i32x4 0xAABBCCDD 0xEEFFA0B0 0xC0D0E0F0 0x0A0B0C0D) + (i32.const 4)) + (v128.const i32x4 0xABBCCDD0 0xEFFA0B00 0x0D0E0F00 0xA0B0C0D0)) +(assert_return (invoke "i32x4.shl" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 8)) + (v128.const i32x4 0 256 0x00000E00 0x00000F00)) +;; amount is multiple of lane width +(assert_return (invoke "i32x4.shl" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 32)) + (v128.const i32x4 0 1 0x0E 0x0F)) +(assert_return (invoke "i32x4.shl" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 128)) + (v128.const i32x4 0 1 0x0E 0x0F)) +(assert_return (invoke "i32x4.shl" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 256)) + (v128.const i32x4 0 1 0x0E 0x0F)) +;; amount greater than but not a multiple of lane width +(assert_return (invoke "i32x4.shl" (v128.const i32x4 -2147483648 -32768 0 0x0A0B0C0D) + (i32.const 33)) + (v128.const i32x4 0 4294901760 0 0x1416181A)) +(assert_return (invoke "i32x4.shl" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 33)) + (v128.const i32x4 0 2 0x1C 0x1E)) +(assert_return (invoke "i32x4.shl" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 65)) + (v128.const i32x4 0 2 0x1C 0x1E)) +(assert_return (invoke "i32x4.shl" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 129)) + (v128.const i32x4 0 2 0x1C 0x1E)) +(assert_return (invoke "i32x4.shl" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 257)) + (v128.const i32x4 0 2 0x1C 0x1E)) +(assert_return (invoke "i32x4.shl" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 513)) + (v128.const i32x4 0 2 0x1C 0x1E)) +(assert_return (invoke "i32x4.shl" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 514)) + (v128.const i32x4 0 4 0x38 0x3C)) + +;; i32x4 shr_u +;; amount less than lane width +(assert_return (invoke "i32x4.shr_u" (v128.const i32x4 -2147483648 -32768 0x0000000C 0x0000000D) + (i32.const 1)) + (v128.const i32x4 1073741824 2147467264 0x00000006 0x00000006)) +(assert_return (invoke "i32x4.shr_u" (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890) + (i32.const 2)) + (v128.const i32x4 308641972 308641972 308641972 308641972)) +(assert_return (invoke "i32x4.shr_u" (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef) + (i32.const 2)) + (v128.const i32x4 0x242af37b 0x242af37b 0x242af37b 0x242af37b)) +(assert_return (invoke "i32x4.shr_u" (v128.const i32x4 0xAABBCCDD 0xEEFFA0B0 0xC0D0E0F0 0x0A0B0C0D) + (i32.const 4)) + (v128.const i32x4 0x0AABBCCD 0x0EEFFA0B 0x0C0D0E0F 0x00A0B0C0)) +(assert_return (invoke "i32x4.shr_u" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 8)) + (v128.const i32x4 0 0 0x00000000 0x00000000)) +;; amount is multiple of lane width +(assert_return (invoke "i32x4.shr_u" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 32)) + (v128.const i32x4 0 1 0x0E 0x0F)) +(assert_return (invoke "i32x4.shr_u" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 128)) + (v128.const i32x4 0 1 0x0E 0x0F)) +(assert_return (invoke "i32x4.shr_u" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 256)) + (v128.const i32x4 0 1 0x0E 0x0F)) +;; amount greater than but not a multiple of lane width +(assert_return (invoke "i32x4.shr_u" (v128.const i32x4 -2147483648 -32768 0x0000000C 0x0000000D) + (i32.const 33)) + (v128.const i32x4 1073741824 2147467264 0x00000006 0x00000006)) +(assert_return (invoke "i32x4.shr_u" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 33)) + (v128.const i32x4 0 0 0x07 0x07)) +(assert_return (invoke "i32x4.shr_u" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 65)) + (v128.const i32x4 0 0 0x07 0x07)) +(assert_return (invoke "i32x4.shr_u" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 129)) + (v128.const i32x4 0 0 0x07 0x07)) +(assert_return (invoke "i32x4.shr_u" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 257)) + (v128.const i32x4 0 0 0x07 0x07)) +(assert_return (invoke "i32x4.shr_u" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 513)) + (v128.const i32x4 0 0 0x07 0x07)) +(assert_return (invoke "i32x4.shr_u" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 514)) + (v128.const i32x4 0 0 0x03 0x03)) + +;; i32x4 shr_s +;; amount less than lane width +(assert_return (invoke "i32x4.shr_s" (v128.const i32x4 -2147483648 -32768 0x0C 0x0D) + (i32.const 1)) + (v128.const i32x4 3221225472 4294950912 0x06 0x06)) +(assert_return (invoke "i32x4.shr_s" (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890) + (i32.const 2)) + (v128.const i32x4 308641972 308641972 308641972 308641972)) +(assert_return (invoke "i32x4.shr_s" (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef) + (i32.const 2)) + (v128.const i32x4 0xe42af37b 0xe42af37b 0xe42af37b 0xe42af37b)) +(assert_return (invoke "i32x4.shr_s" (v128.const i32x4 0xAABBCCDD 0xEEFFA0B0 0xC0D0E0F0 0x0A0B0C0D) + (i32.const 4)) + (v128.const i32x4 0xfaabbccd 0xFEEFFA0B 0xFC0D0E0F 0x00A0B0C0)) +;; amount is multiple of lane width +(assert_return (invoke "i32x4.shr_s" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 8)) + (v128.const i32x4 0 0 0x00000000 0x00000000)) +(assert_return (invoke "i32x4.shr_s" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 32)) + (v128.const i32x4 0 1 0x0E 0x0F)) +(assert_return (invoke "i32x4.shr_s" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 128)) + (v128.const i32x4 0 1 0x0E 0x0F)) +(assert_return (invoke "i32x4.shr_s" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 256)) + (v128.const i32x4 0 1 0x0E 0x0F)) +;; amount greater than but not a multiple of lane width +(assert_return (invoke "i32x4.shr_s" (v128.const i32x4 -2147483648 -32768 0x0C 0x0D) + (i32.const 33)) + (v128.const i32x4 3221225472 4294950912 0x06 0x06)) +(assert_return (invoke "i32x4.shr_s" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 33)) + (v128.const i32x4 0 0 0x07 0x07)) +(assert_return (invoke "i32x4.shr_s" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 65)) + (v128.const i32x4 0 0 0x07 0x07)) +(assert_return (invoke "i32x4.shr_s" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 129)) + (v128.const i32x4 0 0 0x07 0x07)) +(assert_return (invoke "i32x4.shr_s" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 257)) + (v128.const i32x4 0 0 0x07 0x07)) +(assert_return (invoke "i32x4.shr_s" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 513)) + (v128.const i32x4 0 0 0x07 0x07)) +(assert_return (invoke "i32x4.shr_s" (v128.const i32x4 0 1 0x0E 0x0F) + (i32.const 514)) + (v128.const i32x4 0 0 0x03 0x03)) + +;; shifting by a constant amount +(assert_return (invoke "i32x4.shl_1" (v128.const i32x4 0 1 0x0E 0x0F)) + (v128.const i32x4 0 2 28 30)) +(assert_return (invoke "i32x4.shr_u_32" (v128.const i32x4 0 1 0x0E 0x0F)) + (v128.const i32x4 0 1 0x0E 0x0F)) +(assert_return (invoke "i32x4.shr_s_33" (v128.const i32x4 0 1 0x0E 0x0F)) + (v128.const i32x4 0 0 7 7)) + +;; i64x2 shl +;; amount less than lane width +(assert_return (invoke "i64x2.shl" (v128.const i64x2 -9223372036854775808 -2147483648) + (i32.const 1)) + (v128.const i64x2 0 18446744069414584320)) +(assert_return (invoke "i64x2.shl" (v128.const i64x2 01_234_567_890_123_456_789 01_234_567_890_123_456_789) + (i32.const 2)) + (v128.const i64x2 4938271560493827156 4938271560493827156)) +(assert_return (invoke "i64x2.shl" (v128.const i64x2 0x0_1234_5678_90AB_cdef 0x0_1234_5678_90AB_cdef) + (i32.const 2)) + (v128.const i64x2 0x48d159e242af37bc 0x48d159e242af37bc)) +(assert_return (invoke "i64x2.shl" (v128.const i64x2 0xAABBCCDDEEFFA0B0 0xC0D0E0F00A0B0C0D) + (i32.const 4)) + (v128.const i64x2 0xABBCCDDEEFFA0B00 0xD0E0F00A0B0C0D0)) +(assert_return (invoke "i64x2.shl" (v128.const i64x2 0xAABBCCDDEEFFA0B0 0xC0D0E0F00A0B0C0D) + (i32.const 8)) + (v128.const i64x2 0xBBCCDDEEFFA0B000 0xD0E0F00A0B0C0D00)) +(assert_return (invoke "i64x2.shl" (v128.const i64x2 1 0x0F) + (i32.const 16)) + (v128.const i64x2 65536 0xF0000)) +(assert_return (invoke "i64x2.shl" (v128.const i64x2 1 0x0F) + (i32.const 32)) + (v128.const i64x2 4294967296 0xF00000000)) +;; amount is multiple of lane width +(assert_return (invoke "i64x2.shl" (v128.const i64x2 1 0x0F) + (i32.const 128)) + (v128.const i64x2 1 0x0F)) +(assert_return (invoke "i64x2.shl" (v128.const i64x2 1 0x0F) + (i32.const 256)) + (v128.const i64x2 1 0x0F)) +;; amount greater than but not a multiple of lane width +(assert_return (invoke "i64x2.shl" (v128.const i64x2 1 0x0F) + (i32.const 65)) + (v128.const i64x2 2 0x1E)) +(assert_return (invoke "i64x2.shl" (v128.const i64x2 1 0x0F) + (i32.const 129)) + (v128.const i64x2 2 0x1E)) +(assert_return (invoke "i64x2.shl" (v128.const i64x2 1 0x0F) + (i32.const 257)) + (v128.const i64x2 2 0x1E)) +(assert_return (invoke "i64x2.shl" (v128.const i64x2 1 0x0F) + (i32.const 513)) + (v128.const i64x2 2 0x1E)) +(assert_return (invoke "i64x2.shl" (v128.const i64x2 1 0x0F) + (i32.const 514)) + (v128.const i64x2 4 0x3C)) + +;; i64x2 shr_u +;; amount less than lane width +(assert_return (invoke "i64x2.shr_u" (v128.const i64x2 -9223372036854775808 -2147483648) + (i32.const 1)) + (v128.const i64x2 4611686018427387904 9223372035781033984)) +(assert_return (invoke "i64x2.shr_u" (v128.const i64x2 01_234_567_890_123_456_789 01_234_567_890_123_456_789) + (i32.const 2)) + (v128.const i64x2 308641972530864197 308641972530864197)) +(assert_return (invoke "i64x2.shr_u" (v128.const i64x2 0x0_90AB_cdef_8765_4321 0x0_90AB_cdef_8765_4321) + (i32.const 2)) + (v128.const i64x2 0x242af37be1d950c8 0x242af37be1d950c8)) +(assert_return (invoke "i64x2.shr_u" (v128.const i64x2 0xAABBCCDDEEFFA0B0 0xC0D0E0F00A0B0C0D) + (i32.const 4)) + (v128.const i64x2 0xAABBCCDDEEFFA0B 0xC0D0E0F00A0B0C0)) +(assert_return (invoke "i64x2.shr_u" (v128.const i64x2 0xAABBCCDDEEFFA0B0 0xC0D0E0F00A0B0C0D) + (i32.const 8)) + (v128.const i64x2 0xAABBCCDDEEFFA0 0xC0D0E0F00A0B0C)) +(assert_return (invoke "i64x2.shr_u" (v128.const i64x2 1 0x0F) + (i32.const 16)) + (v128.const i64x2 0 0x00)) +(assert_return (invoke "i64x2.shr_u" (v128.const i64x2 1 0x0F) + (i32.const 32)) + (v128.const i64x2 0 0x00)) +;; amount is multiple of lane width +(assert_return (invoke "i64x2.shr_u" (v128.const i64x2 1 0x0F) + (i32.const 128)) + (v128.const i64x2 1 0x0F)) +(assert_return (invoke "i64x2.shr_u" (v128.const i64x2 1 0x0F) + (i32.const 256)) + (v128.const i64x2 1 0x0F)) +;; amount greater than but not a multiple of lane width +(assert_return (invoke "i64x2.shr_u" (v128.const i64x2 1 0x0F) + (i32.const 65)) + (v128.const i64x2 0 0x07)) +(assert_return (invoke "i64x2.shr_u" (v128.const i64x2 1 0x0F) + (i32.const 129)) + (v128.const i64x2 0 0x07)) +(assert_return (invoke "i64x2.shr_u" (v128.const i64x2 1 0x0F) + (i32.const 257)) + (v128.const i64x2 0 0x07)) +(assert_return (invoke "i64x2.shr_u" (v128.const i64x2 1 0x0F) + (i32.const 513)) + (v128.const i64x2 0 0x07)) +(assert_return (invoke "i64x2.shr_u" (v128.const i64x2 0 0x0F) + (i32.const 514)) + (v128.const i64x2 0 0x03)) + +;; i64x2 shr_s +;; amount less than lane width +(assert_return (invoke "i64x2.shr_s" (v128.const i64x2 -9223372036854775808 -2147483648) + (i32.const 1)) + (v128.const i64x2 13835058055282163712 18446744072635809792)) +(assert_return (invoke "i64x2.shr_s" (v128.const i64x2 01_234_567_890_123_456_789 01_234_567_890_123_456_789) + (i32.const 2)) + (v128.const i64x2 308641972530864197 308641972530864197)) +(assert_return (invoke "i64x2.shr_s" (v128.const i64x2 0x0_90AB_cdef_8765_4321 0x0_90AB_cdef_8765_4321) + (i32.const 2)) + (v128.const i64x2 0xe42af37be1d950c8 0xe42af37be1d950c8)) +(assert_return (invoke "i64x2.shr_s" (v128.const i64x2 0xAABBCCDDEEFFA0B0 0xC0D0E0F00A0B0C0D) + (i32.const 4)) + (v128.const i64x2 0xFAABBCCDDEEFFA0B 0xFC0D0E0F00A0B0C0)) +(assert_return (invoke "i64x2.shr_s" (v128.const i64x2 0xFFAABBCCDDEEFFA0 0xC0D0E0F00A0B0C0D) + (i32.const 8)) + (v128.const i64x2 0xFFFFAABBCCDDEEFF 0xFFC0D0E0F00A0B0C)) +(assert_return (invoke "i64x2.shr_s" (v128.const i64x2 1 0x0F) + (i32.const 16)) + (v128.const i64x2 0 0x00)) +(assert_return (invoke "i64x2.shr_s" (v128.const i64x2 1 0x0F) + (i32.const 32)) + (v128.const i64x2 0 0x00)) +;; amount is multiple of lane width +(assert_return (invoke "i64x2.shr_s" (v128.const i64x2 1 0x0F) + (i32.const 128)) + (v128.const i64x2 1 0x0F)) +(assert_return (invoke "i64x2.shr_s" (v128.const i64x2 1 0x0F) + (i32.const 256)) + (v128.const i64x2 1 0x0F)) +;; amount greater than but not a multiple of lane width +(assert_return (invoke "i64x2.shr_s" (v128.const i64x2 -9223372036854775808 -2147483648) + (i32.const 65)) + (v128.const i64x2 13835058055282163712 18446744072635809792)) +(assert_return (invoke "i64x2.shr_s" (v128.const i64x2 0x0C 0x0D) + (i32.const 65)) + (v128.const i64x2 0x06 0x06)) +(assert_return (invoke "i64x2.shr_s" (v128.const i64x2 1 0x0F) + (i32.const 129)) + (v128.const i64x2 0 0x07)) +(assert_return (invoke "i64x2.shr_s" (v128.const i64x2 1 0x0F) + (i32.const 257)) + (v128.const i64x2 0 0x07)) +(assert_return (invoke "i64x2.shr_s" (v128.const i64x2 1 0x0F) + (i32.const 513)) + (v128.const i64x2 0 0x07)) +(assert_return (invoke "i64x2.shr_s" (v128.const i64x2 1 0x0F) + (i32.const 514)) + (v128.const i64x2 0 0x03)) + +;; shifting by a constant amount +(assert_return (invoke "i64x2.shl_1" (v128.const i64x2 1 0x0F)) + (v128.const i64x2 2 0x1E)) +(assert_return (invoke "i64x2.shr_u_64" (v128.const i64x2 1 0x0F)) + (v128.const i64x2 1 0x0F)) +(assert_return (invoke "i64x2.shr_s_65" (v128.const i64x2 1 0x0F)) + (v128.const i64x2 0 0x07)) + +;; Combination + +(module (memory 1) + (func (export "i8x16.shl-in-block") + (block + (drop + (block (result v128) + (i8x16.shl + (block (result v128) (v128.load (i32.const 0))) (i32.const 1) + ) + ) + ) + ) + ) + (func (export "i8x16.shr_s-in-block") + (block + (drop + (block (result v128) + (i8x16.shr_s + (block (result v128) (v128.load (i32.const 0))) (i32.const 1) + ) + ) + ) + ) + ) + (func (export "i8x16.shr_u-in-block") + (block + (drop + (block (result v128) + (i8x16.shr_u + (block (result v128) (v128.load (i32.const 0))) (i32.const 1) + ) + ) + ) + ) + ) + (func (export "i16x8.shl-in-block") + (block + (drop + (block (result v128) + (i16x8.shl + (block (result v128) (v128.load (i32.const 0))) (i32.const 1) + ) + ) + ) + ) + ) + (func (export "i16x8.shr_s-in-block") + (block + (drop + (block (result v128) + (i16x8.shr_s + (block (result v128) (v128.load (i32.const 0))) (i32.const 1) + ) + ) + ) + ) + ) + (func (export "i16x8.shr_u-in-block") + (block + (drop + (block (result v128) + (i16x8.shr_u + (block (result v128) (v128.load (i32.const 0))) (i32.const 1) + ) + ) + ) + ) + ) + (func (export "i32x4.shl-in-block") + (block + (drop + (block (result v128) + (i32x4.shl + (block (result v128) (v128.load (i32.const 0))) (i32.const 1) + ) + ) + ) + ) + ) + (func (export "i32x4.shr_s-in-block") + (block + (drop + (block (result v128) + (i32x4.shr_s + (block (result v128) (v128.load (i32.const 0))) (i32.const 1) + ) + ) + ) + ) + ) + (func (export "i32x4.shr_u-in-block") + (block + (drop + (block (result v128) + (i32x4.shr_u + (block (result v128) (v128.load (i32.const 0))) (i32.const 1) + ) + ) + ) + ) + ) + (func (export "i64x2.shl-in-block") + (block + (drop + (block (result v128) + (i64x2.shl + (block (result v128) (v128.load (i32.const 0))) (i32.const 1) + ) + ) + ) + ) + ) + (func (export "i64x2.shr_s-in-block") + (block + (drop + (block (result v128) + (i64x2.shr_s + (block (result v128) (v128.load (i32.const 0))) (i32.const 1) + ) + ) + ) + ) + ) + (func (export "i64x2.shr_u-in-block") + (block + (drop + (block (result v128) + (i64x2.shr_u + (block (result v128) (v128.load (i32.const 0))) (i32.const 1) + ) + ) + ) + ) + ) + (func (export "nested-i8x16.shl") + (drop + (i8x16.shl + (i8x16.shl + (i8x16.shl + (v128.load (i32.const 0)) (i32.const 1) + ) + (i32.const 1) + ) + (i32.const 1) + ) + ) + ) + (func (export "nested-i8x16.shr_s") + (drop + (i8x16.shr_s + (i8x16.shr_s + (i8x16.shr_s + (v128.load (i32.const 0)) (i32.const 1) + ) + (i32.const 1) + ) + (i32.const 1) + ) + ) + ) + (func (export "nested-i8x16.shr_u") + (drop + (i8x16.shr_u + (i8x16.shr_u + (i8x16.shr_u + (v128.load (i32.const 0)) (i32.const 1) + ) + (i32.const 1) + ) + (i32.const 1) + ) + ) + ) + (func (export "nested-i16x8.shl") + (drop + (i16x8.shl + (i16x8.shl + (i16x8.shl + (v128.load (i32.const 0)) (i32.const 1) + ) + (i32.const 1) + ) + (i32.const 1) + ) + ) + ) + (func (export "nested-i16x8.shr_s") + (drop + (i16x8.shr_s + (i16x8.shr_s + (i16x8.shr_s + (v128.load (i32.const 0)) (i32.const 1) + ) + (i32.const 1) + ) + (i32.const 1) + ) + ) + ) + (func (export "nested-i16x8.shr_u") + (drop + (i16x8.shr_u + (i16x8.shr_u + (i16x8.shr_u + (v128.load (i32.const 0)) (i32.const 1) + ) + (i32.const 1) + ) + (i32.const 1) + ) + ) + ) + (func (export "nested-i32x4.shl") + (drop + (i32x4.shl + (i32x4.shl + (i32x4.shl + (v128.load (i32.const 0)) (i32.const 1) + ) + (i32.const 1) + ) + (i32.const 1) + ) + ) + ) + (func (export "nested-i32x4.shr_s") + (drop + (i32x4.shr_s + (i32x4.shr_s + (i32x4.shr_s + (v128.load (i32.const 0)) (i32.const 1) + ) + (i32.const 1) + ) + (i32.const 1) + ) + ) + ) + (func (export "nested-i32x4.shr_u") + (drop + (i32x4.shr_u + (i32x4.shr_u + (i32x4.shr_u + (v128.load (i32.const 0)) (i32.const 1) + ) + (i32.const 1) + ) + (i32.const 1) + ) + ) + ) + (func (export "nested-i64x2.shl") + (drop + (i64x2.shl + (i64x2.shl + (i64x2.shl + (v128.load (i32.const 0)) (i32.const 1) + ) + (i32.const 1) + ) + (i32.const 1) + ) + ) + ) + (func (export "nested-i64x2.shr_s") + (drop + (i64x2.shr_s + (i64x2.shr_s + (i64x2.shr_s + (v128.load (i32.const 0)) (i32.const 1) + ) + (i32.const 1) + ) + (i32.const 1) + ) + ) + ) + (func (export "nested-i64x2.shr_u") + (drop + (i64x2.shr_u + (i64x2.shr_u + (i64x2.shr_u + (v128.load (i32.const 0)) (i32.const 1) + ) + (i32.const 1) + ) + (i32.const 1) + ) + ) + ) +) + +(assert_return (invoke "i8x16.shl-in-block")) +(assert_return (invoke "i8x16.shr_s-in-block")) +(assert_return (invoke "i8x16.shr_u-in-block")) +(assert_return (invoke "i16x8.shl-in-block")) +(assert_return (invoke "i16x8.shr_s-in-block")) +(assert_return (invoke "i16x8.shr_u-in-block")) +(assert_return (invoke "i32x4.shl-in-block")) +(assert_return (invoke "i32x4.shr_s-in-block")) +(assert_return (invoke "i32x4.shr_u-in-block")) +(assert_return (invoke "i64x2.shl-in-block")) +(assert_return (invoke "i64x2.shr_s-in-block")) +(assert_return (invoke "i64x2.shr_u-in-block")) +(assert_return (invoke "nested-i8x16.shl")) +(assert_return (invoke "nested-i8x16.shr_s")) +(assert_return (invoke "nested-i8x16.shr_u")) +(assert_return (invoke "nested-i16x8.shl")) +(assert_return (invoke "nested-i16x8.shr_s")) +(assert_return (invoke "nested-i16x8.shr_u")) +(assert_return (invoke "nested-i32x4.shl")) +(assert_return (invoke "nested-i32x4.shr_s")) +(assert_return (invoke "nested-i32x4.shr_u")) +(assert_return (invoke "nested-i64x2.shl")) +(assert_return (invoke "nested-i64x2.shr_s")) +(assert_return (invoke "nested-i64x2.shr_u")) + +;; Type check + +(assert_invalid (module (func (result v128) (i8x16.shl (i32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.shr_s (i32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.shr_u (i32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.shl (i32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.shr_s (i32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.shr_u (i32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.shl (i32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.shr_s (i32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.shr_u (i32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.shl (i32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.shr_s (i32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.shr_u (i32.const 0) (i32.const 0)))) "type mismatch") + +;; Unknown operators + +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.shl_s (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.shl_r (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.shr (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.shl_s (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.shl_r (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.shr (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.shl_s (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.shl_r (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.shr (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.shl_s (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.shl_r (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.shr (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (f32x4.shl (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (f32x4.shr_s (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (f32x4.shr_u (v128.const i32x4 0 0 0 0)))") "unknown operator") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i8x16.shl-1st-arg-empty (result v128) + (i8x16.shl (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.shl-last-arg-empty (result v128) + (i8x16.shl (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.shl-arg-empty (result v128) + (i8x16.shl) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.shr_u-1st-arg-empty (result v128) + (i16x8.shr_u (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.shr_u-last-arg-empty (result v128) + (i16x8.shr_u (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.shr_u-arg-empty (result v128) + (i16x8.shr_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.shr_s-1st-arg-empty (result v128) + (i32x4.shr_s (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.shr_s-last-arg-empty (result v128) + (i32x4.shr_s (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.shr_s-arg-empty (result v128) + (i32x4.shr_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.shl-1st-arg-empty (result v128) + (i64x2.shl (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.shr_u-last-arg-empty (result v128) + (i64x2.shr_u (v128.const i64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.shr_s-arg-empty (result v128) + (i64x2.shr_s) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_bitwise.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_bitwise.wast new file mode 100644 index 000000000..f646bf61d --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_bitwise.wast @@ -0,0 +1,812 @@ +;; Test all the bitwise operators on major boundary values and all special values. + +(module + (func (export "not") (param $0 v128) (result v128) (v128.not (local.get $0))) + (func (export "and") (param $0 v128) (param $1 v128) (result v128) (v128.and (local.get $0) (local.get $1))) + (func (export "or") (param $0 v128) (param $1 v128) (result v128) (v128.or (local.get $0) (local.get $1))) + (func (export "xor") (param $0 v128) (param $1 v128) (result v128) (v128.xor (local.get $0) (local.get $1))) + (func (export "bitselect") (param $0 v128) (param $1 v128) (param $2 v128) (result v128) + (v128.bitselect (local.get $0) (local.get $1) (local.get $2)) + ) + (func (export "andnot") (param $0 v128) (param $1 v128) (result v128) (v128.andnot (local.get $0) (local.get $1))) +) + +;; i32x4 +(assert_return (invoke "not" (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "not" (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "not" (v128.const i32x4 -1 0 -1 0)) + (v128.const i32x4 0 -1 0 -1)) +(assert_return (invoke "not" (v128.const i32x4 0 -1 0 -1)) + (v128.const i32x4 -1 0 -1 0)) +(assert_return (invoke "not" (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555)) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) +(assert_return (invoke "not" (v128.const i32x4 3435973836 3435973836 3435973836 3435973836)) + (v128.const i32x4 858993459 858993459 858993459 858993459)) +(assert_return (invoke "not" (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890)) + (v128.const i32x4 3060399405 3060399405 3060399405 3060399405)) +(assert_return (invoke "not" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678)) + (v128.const i32x4 0xedcba987 0xedcba987 0xedcba987 0xedcba987)) +(assert_return (invoke "and" (v128.const i32x4 0 0 -1 -1) + (v128.const i32x4 0 -1 0 -1)) + (v128.const i32x4 0 0 0 -1)) +(assert_return (invoke "and" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "and" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "and" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "and" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "and" (v128.const i32x4 255 255 255 255) + (v128.const i32x4 85 85 85 85)) + (v128.const i32x4 85 85 85 85)) +(assert_return (invoke "and" (v128.const i32x4 255 255 255 255) + (v128.const i32x4 128 128 128 128)) + (v128.const i32x4 128 128 128 128)) +(assert_return (invoke "and" (v128.const i32x4 2863311530 2863311530 2863311530 2863311530) + (v128.const i32x4 10 128 5 165)) + (v128.const i32x4 10 128 0 160)) +(assert_return (invoke "and" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555)) + (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555)) +(assert_return (invoke "and" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) +(assert_return (invoke "and" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x0 0x0 0x0 0x0)) + (v128.const i32x4 0x0 0x0 0x0 0x0)) +(assert_return (invoke "and" (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i32x4 0x5555 0xFFFF 0x55FF 0x5FFF)) + (v128.const i32x4 0x5555 0x5555 0x5555 0x5555)) +(assert_return (invoke "and" (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890) + (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890)) + (v128.const i32x4 1234567890 1234567890 1234567890 1234567890)) +(assert_return (invoke "and" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678) + (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef)) + (v128.const i32x4 0x10204468 0x10204468 0x10204468 0x10204468)) +(assert_return (invoke "or" (v128.const i32x4 0 0 -1 -1) + (v128.const i32x4 0 -1 0 -1)) + (v128.const i32x4 0 -1 -1 -1)) +(assert_return (invoke "or" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "or" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "or" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) +(assert_return (invoke "or" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "or" (v128.const i32x4 255 255 255 255) + (v128.const i32x4 85 85 85 85)) + (v128.const i32x4 255 255 255 255)) +(assert_return (invoke "or" (v128.const i32x4 255 255 255 255) + (v128.const i32x4 128 128 128 128)) + (v128.const i32x4 255 255 255 255)) +(assert_return (invoke "or" (v128.const i32x4 2863311530 2863311530 2863311530 2863311530) + (v128.const i32x4 10 128 5 165)) + (v128.const i32x4 2863311530 2863311530 2863311535 2863311535)) +(assert_return (invoke "or" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555)) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) +(assert_return (invoke "or" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) +(assert_return (invoke "or" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x0 0x0 0x0 0x0)) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) +(assert_return (invoke "or" (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i32x4 0x5555 0xFFFF 0x55FF 0x5FFF)) + (v128.const i32x4 0x55555555 0x5555ffff 0x555555ff 0x55555fff)) +(assert_return (invoke "or" (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890) + (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890)) + (v128.const i32x4 1234567890 1234567890 1234567890 1234567890)) +(assert_return (invoke "or" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678) + (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef)) + (v128.const i32x4 0x92bfdfff 0x92bfdfff 0x92bfdfff 0x92bfdfff)) +(assert_return (invoke "xor" (v128.const i32x4 0 0 -1 -1) + (v128.const i32x4 0 -1 0 -1)) + (v128.const i32x4 0 -1 -1 0)) +(assert_return (invoke "xor" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "xor" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "xor" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) +(assert_return (invoke "xor" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "xor" (v128.const i32x4 255 255 255 255) + (v128.const i32x4 85 85 85 85)) + (v128.const i32x4 170 170 170 170)) +(assert_return (invoke "xor" (v128.const i32x4 255 255 255 255) + (v128.const i32x4 128 128 128 128)) + (v128.const i32x4 127 127 127 127)) +(assert_return (invoke "xor" (v128.const i32x4 2863311530 2863311530 2863311530 2863311530) + (v128.const i32x4 10 128 5 165)) + (v128.const i32x4 2863311520 2863311402 2863311535 2863311375)) +(assert_return (invoke "xor" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555)) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) +(assert_return (invoke "xor" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555)) +(assert_return (invoke "xor" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x0 0x0 0x0 0x0)) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) +(assert_return (invoke "xor" (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i32x4 0x5555 0xFFFF 0x55FF 0x5FFF)) + (v128.const i32x4 0x55550000 0x5555AAAA 0x555500AA 0x55550AAA)) +(assert_return (invoke "xor" (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890) + (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "xor" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678) + (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef)) + (v128.const i32x4 0x829f9b97 0x829f9b97 0x829f9b97 0x829f9b97)) +(assert_return (invoke "bitselect" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i32x4 0xBBBBBBBB 0xBBBBBBBB 0xBBBBBBBB 0xBBBBBBBB) + (v128.const i32x4 0x00112345 0xF00FFFFF 0x10112021 0xBBAABBAA)) + (v128.const i32x4 0xBBAABABA 0xABBAAAAA 0xABAABBBA 0xAABBAABB)) +(assert_return (invoke "bitselect" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i32x4 0xBBBBBBBB 0xBBBBBBBB 0xBBBBBBBB 0xBBBBBBBB) + (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)) + (v128.const i32x4 0xBBBBBBBB 0xBBBBBBBB 0xBBBBBBBB 0xBBBBBBBB)) +(assert_return (invoke "bitselect" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i32x4 0xBBBBBBBB 0xBBBBBBBB 0xBBBBBBBB 0xBBBBBBBB) + (v128.const i32x4 0x11111111 0x11111111 0x11111111 0x11111111)) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) +(assert_return (invoke "bitselect" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i32x4 0xBBBBBBBB 0xBBBBBBBB 0xBBBBBBBB 0xBBBBBBBB) + (v128.const i32x4 0x01234567 0x89ABCDEF 0xFEDCBA98 0x76543210)) + (v128.const i32x4 0xBABABABA 0xBABABABA 0xABABABAB 0xABABABAB)) +(assert_return (invoke "bitselect" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i32x4 0x01234567 0x89ABCDEF 0xFEDCBA98 0x76543210)) + (v128.const i32x4 0x54761032 0xDCFE98BA 0xAB89EFCD 0x23016745)) +(assert_return (invoke "bitselect" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i32x4 0x55555555 0xAAAAAAAA 0x00000000 0xFFFFFFFF)) + (v128.const i32x4 0x00000000 0xFFFFFFFF 0x55555555 0xAAAAAAAA)) +(assert_return (invoke "bitselect" (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890) + (v128.const i32x4 03_060_399_406 03_060_399_406 03_060_399_406 03_060_399_406) + (v128.const i32x4 0xcdefcdef 0xcdefcdef 0xcdefcdef 0xcdefcdef)) + (v128.const i32x4 2072391874 2072391874 2072391874 2072391874)) +(assert_return (invoke "bitselect" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678) + (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef) + (v128.const i32x4 0xcdefcdef 0xcdefcdef 0xcdefcdef 0xcdefcdef)) + (v128.const i32x4 0x10244468 0x10244468 0x10244468 0x10244468)) +(assert_return (invoke "andnot" (v128.const i32x4 0 0 -1 -1) + (v128.const i32x4 0 -1 0 -1)) + (v128.const i32x4 0 0 -1 0)) +(assert_return (invoke "andnot" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "andnot" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "andnot" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "andnot" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "andnot" (v128.const i32x4 255 255 255 255) + (v128.const i32x4 85 85 85 85)) + (v128.const i32x4 170 170 170 170)) +(assert_return (invoke "andnot" (v128.const i32x4 255 255 255 255) + (v128.const i32x4 128 128 128 128)) + (v128.const i32x4 127 127 127 127)) +(assert_return (invoke "andnot" (v128.const i32x4 2863311530 2863311530 2863311530 2863311530) + (v128.const i32x4 10 128 5 165)) + (v128.const i32x4 2863311520 2863311402 2863311530 2863311370)) +(assert_return (invoke "andnot" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555)) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) +(assert_return (invoke "andnot" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555)) +(assert_return (invoke "andnot" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x0 0x0 0x0 0x0)) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) +(assert_return (invoke "andnot" (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i32x4 0x5555 0xFFFF 0x55FF 0x5FFF)) + (v128.const i32x4 0x55550000 0x55550000 0x55550000 0x55550000)) +(assert_return (invoke "andnot" (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890) + (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "andnot" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678) + (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef)) + (v128.const i32x4 0x02141210 0x02141210 0x02141210 0x02141210)) + +;; for float special data [e.g. -nan nan -inf inf] +(assert_return (invoke "not" (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 5.87747e-39 5.87747e-39 5.87747e-39 5.87747e-39)) +(assert_return (invoke "not" (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -5.87747e-39 -5.87747e-39 -5.87747e-39 -5.87747e-39)) +(assert_return (invoke "not" (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0x007fffff 0x007fffff 0x007fffff 0x007fffff)) +(assert_return (invoke "not" (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0x807fffff 0x807fffff 0x807fffff 0x807fffff)) +(assert_return (invoke "and" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0xffc00000 0xffc00000 0xffc00000 0xffc00000)) +(assert_return (invoke "and" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "and" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "and" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "and" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "and" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "and" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "and" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "and" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "and" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "or" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0xffc00000 0xffc00000 0xffc00000 0xffc00000)) +(assert_return (invoke "or" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0xffc00000 0xffc00000 0xffc00000 0xffc00000)) +(assert_return (invoke "or" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0xffc00000 0xffc00000 0xffc00000 0xffc00000)) +(assert_return (invoke "or" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0xffc00000 0xffc00000 0xffc00000 0xffc00000)) +(assert_return (invoke "or" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "or" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0xffc00000 0xffc00000 0xffc00000 0xffc00000)) +(assert_return (invoke "or" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "or" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "or" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "or" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "xor" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0 0 0 0)) +(assert_return (invoke "xor" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0 -0 -0 -0)) +(assert_return (invoke "xor" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0x00400000 0x00400000 0x00400000 0x00400000)) +(assert_return (invoke "xor" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0x80400000 0x80400000 0x80400000 0x80400000)) +(assert_return (invoke "xor" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0 0 0 0)) +(assert_return (invoke "xor" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0x80400000 0x80400000 0x80400000 0x80400000)) +(assert_return (invoke "xor" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0x00400000 0x00400000 0x00400000 0x00400000)) +(assert_return (invoke "xor" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0 0 0 0)) +(assert_return (invoke "xor" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) +(assert_return (invoke "xor" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0 0 0 0)) +(assert_return (invoke "bitselect" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5)) + (v128.const i32x4 0xffc00000 0xffc00000 0xffc00000 0xffc00000)) +(assert_return (invoke "bitselect" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "bitselect" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "bitselect" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "bitselect" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "bitselect" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "bitselect" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "bitselect" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "bitselect" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "bitselect" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5 0xA5A5A5A5)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "andnot" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)) +(assert_return (invoke "andnot" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0 -0 -0 -0)) +(assert_return (invoke "andnot" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0x00400000 0x00400000 0x00400000 0x00400000)) +(assert_return (invoke "andnot" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0x80400000 0x80400000 0x80400000 0x80400000)) +(assert_return (invoke "andnot" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x00000000 0x00000000 0x00000000 0x00000000)) +(assert_return (invoke "andnot" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0x00400000 0x00400000 0x00400000 0x00400000)) +(assert_return (invoke "andnot" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0x00400000 0x00400000 0x00400000 0x00400000)) +(assert_return (invoke "andnot" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x00000000 0x00000000 0x00000000 0x00000000)) +(assert_return (invoke "andnot" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) +(assert_return (invoke "andnot" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)) + +;; Type check + +;; not +(assert_invalid (module (func (result v128) (v128.not (i32.const 0)))) "type mismatch") +;; and +(assert_invalid (module (func (result v128) (v128.and (i32.const 0) (v128.const i32x4 0 0 0 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (v128.and (v128.const i32x4 0 0 0 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (v128.and (i32.const 0) (i32.const 0)))) "type mismatch") +;; or +(assert_invalid (module (func (result v128) (v128.or (i32.const 0) (v128.const i32x4 0 0 0 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (v128.or (v128.const i32x4 0 0 0 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (v128.or (i32.const 0) (i32.const 0)))) "type mismatch") +;; xor +(assert_invalid (module (func (result v128) (v128.xor (i32.const 0) (v128.const i32x4 0 0 0 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (v128.xor (v128.const i32x4 0 0 0 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (v128.xor (i32.const 0) (i32.const 0)))) "type mismatch") +;; bitselect +(assert_invalid (module (func (result v128) (v128.bitselect (i32.const 0) (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (v128.bitselect (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (v128.bitselect (i32.const 0) (i32.const 0) (i32.const 0)))) "type mismatch") +;; andnot +(assert_invalid (module (func (result v128) (v128.andnot (i32.const 0) (v128.const i32x4 0 0 0 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (v128.andnot (v128.const i32x4 0 0 0 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (v128.andnot (i32.const 0) (i32.const 0)))) "type mismatch") + +;; Combination + +(module (memory 1) + (func (export "v128.not-in-block") + (block + (drop + (block (result v128) + (v128.not + (block (result v128) (v128.load (i32.const 0))) + ) + ) + ) + ) + ) + (func (export "v128.and-in-block") + (block + (drop + (block (result v128) + (v128.and + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "v128.or-in-block") + (block + (drop + (block (result v128) + (v128.or + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "v128.xor-in-block") + (block + (drop + (block (result v128) + (v128.xor + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "v128.bitselect-in-block") + (block + (drop + (block (result v128) + (v128.bitselect + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + (block (result v128) (v128.load (i32.const 2))) + ) + ) + ) + ) + ) + (func (export "v128.andnot-in-block") + (block + (drop + (block (result v128) + (v128.andnot + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "nested-v128.not") + (drop + (v128.not + (v128.not + (v128.not + (v128.load (i32.const 0)) + ) + ) + ) + ) + ) + (func (export "nested-v128.and") + (drop + (v128.and + (v128.and + (v128.and + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (v128.and + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + ) + (v128.and + (v128.and + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (v128.and + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + ) + ) + ) + ) + (func (export "nested-v128.or") + (drop + (v128.or + (v128.or + (v128.or + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (v128.or + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + ) + (v128.or + (v128.or + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (v128.or + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + ) + ) + ) + ) + (func (export "nested-v128.xor") + (drop + (v128.xor + (v128.xor + (v128.xor + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (v128.xor + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + ) + (v128.xor + (v128.xor + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (v128.xor + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + ) + ) + ) + ) + (func (export "nested-v128.bitselect") + (drop + (v128.bitselect + (v128.bitselect + (v128.bitselect + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + (v128.load (i32.const 2)) + ) + (v128.bitselect + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + (v128.load (i32.const 2)) + ) + (v128.bitselect + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + (v128.load (i32.const 2)) + ) + ) + (v128.bitselect + (v128.bitselect + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + (v128.load (i32.const 2)) + ) + (v128.bitselect + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + (v128.load (i32.const 2)) + ) + (v128.bitselect + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + (v128.load (i32.const 2)) + ) + ) + (v128.bitselect + (v128.bitselect + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + (v128.load (i32.const 2)) + ) + (v128.bitselect + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + (v128.load (i32.const 2)) + ) + (v128.bitselect + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + (v128.load (i32.const 2)) + ) + ) + ) + ) + ) + (func (export "nested-v128.andnot") + (drop + (v128.andnot + (v128.andnot + (v128.andnot + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (v128.andnot + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + ) + (v128.andnot + (v128.andnot + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (v128.andnot + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + ) + ) + ) + ) + (func (export "as-param") + (drop + (v128.or + (v128.and + (v128.not + (v128.load (i32.const 0)) + ) + (v128.not + (v128.load (i32.const 1)) + ) + ) + (v128.xor + (v128.bitselect + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + (v128.load (i32.const 2)) + ) + (v128.andnot + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + ) + ) + ) + ) +) +(assert_return (invoke "v128.not-in-block")) +(assert_return (invoke "v128.and-in-block")) +(assert_return (invoke "v128.or-in-block")) +(assert_return (invoke "v128.xor-in-block")) +(assert_return (invoke "v128.bitselect-in-block")) +(assert_return (invoke "v128.andnot-in-block")) +(assert_return (invoke "nested-v128.not")) +(assert_return (invoke "nested-v128.and")) +(assert_return (invoke "nested-v128.or")) +(assert_return (invoke "nested-v128.xor")) +(assert_return (invoke "nested-v128.bitselect")) +(assert_return (invoke "nested-v128.andnot")) +(assert_return (invoke "as-param")) + + +;; Test operation with empty argument + +(assert_invalid + (module + (func $v128.not-arg-empty (result v128) + (v128.not) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $v128.and-1st-arg-empty (result v128) + (v128.and (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $v128.and-arg-empty (result v128) + (v128.and) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $v128.or-1st-arg-empty (result v128) + (v128.or (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $v128.or-arg-empty (result v128) + (v128.or) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $v128.xor-1st-arg-empty (result v128) + (v128.xor (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $v128.xor-arg-empty (result v128) + (v128.xor) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $v128.andnot-1st-arg-empty (result v128) + (v128.andnot (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $v128.andnot-arg-empty (result v128) + (v128.andnot) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $v128.bitselect-1st-arg-empty (result v128) + (v128.bitselect (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $v128.bitselect-two-args-empty (result v128) + (v128.bitselect (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $v128.bitselect-arg-empty (result v128) + (v128.bitselect) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_boolean.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_boolean.wast new file mode 100644 index 000000000..6ab330fe5 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_boolean.wast @@ -0,0 +1,1058 @@ +;; Test all the boolean operators on major boundary values and all special values. + +(module + (func (export "i8x16.any_true") (param $0 v128) (result i32) (v128.any_true (local.get $0))) + (func (export "i8x16.all_true") (param $0 v128) (result i32) (i8x16.all_true (local.get $0))) + (func (export "i8x16.bitmask") (param $0 v128) (result i32) (i8x16.bitmask (local.get $0))) + + (func (export "i16x8.any_true") (param $0 v128) (result i32) (v128.any_true (local.get $0))) + (func (export "i16x8.all_true") (param $0 v128) (result i32) (i16x8.all_true (local.get $0))) + (func (export "i16x8.bitmask") (param $0 v128) (result i32) (i16x8.bitmask (local.get $0))) + + (func (export "i32x4.any_true") (param $0 v128) (result i32) (v128.any_true (local.get $0))) + (func (export "i32x4.all_true") (param $0 v128) (result i32) (i32x4.all_true (local.get $0))) + (func (export "i32x4.bitmask") (param $0 v128) (result i32) (i32x4.bitmask (local.get $0))) + + (func (export "i64x2.all_true") (param $0 v128) (result i32) (i64x2.all_true (local.get $0))) + (func (export "i64x2.bitmask") (param $0 v128) (result i32) (i64x2.bitmask (local.get $0))) +) + +;; i8x16 +(assert_return (invoke "i8x16.any_true" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i8x16.any_true" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i8x16.any_true" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1)) + (i32.const 1)) +(assert_return (invoke "i8x16.any_true" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i8x16.any_true" (v128.const i8x16 -1 0 1 2 3 4 5 6 7 8 9 0xA 0xB 0xC 0xD 0xF)) + (i32.const 1)) +(assert_return (invoke "i8x16.any_true" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (i32.const 0)) +(assert_return (invoke "i8x16.any_true" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (i32.const 1)) +(assert_return (invoke "i8x16.any_true" (v128.const i8x16 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB)) + (i32.const 1)) +(assert_return (invoke "i8x16.any_true" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55)) + (i32.const 1)) +(assert_return (invoke "i8x16.all_true" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i8x16.all_true" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0)) + (i32.const 0)) +(assert_return (invoke "i8x16.all_true" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1)) + (i32.const 0)) +(assert_return (invoke "i8x16.all_true" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i8x16.all_true" (v128.const i8x16 -1 0 1 2 3 4 5 6 7 8 9 0xA 0xB 0xC 0xD 0xF)) + (i32.const 0)) +(assert_return (invoke "i8x16.all_true" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (i32.const 0)) +(assert_return (invoke "i8x16.all_true" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (i32.const 1)) +(assert_return (invoke "i8x16.all_true" (v128.const i8x16 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB)) + (i32.const 1)) +(assert_return (invoke "i8x16.all_true" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55)) + (i32.const 1)) +(assert_return (invoke "i8x16.bitmask" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (i32.const 0x0000FFFF)) +(assert_return (invoke "i8x16.bitmask" (v128.const i8x16 -1 0 1 2 3 4 5 6 7 8 9 0xA 0xB 0xC 0xD 0xF)) + (i32.const 0x00000001)) + +;; i16x8 +(assert_return (invoke "i16x8.any_true" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8.any_true" (v128.const i16x8 0 0 0 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i16x8.any_true" (v128.const i16x8 1 1 1 1 1 1 0 1)) + (i32.const 1)) +(assert_return (invoke "i16x8.any_true" (v128.const i16x8 1 1 1 1 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i16x8.any_true" (v128.const i16x8 -1 0 1 2 0xB 0xC 0xD 0xF)) + (i32.const 1)) +(assert_return (invoke "i16x8.any_true" (v128.const i16x8 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (i32.const 0)) +(assert_return (invoke "i16x8.any_true" (v128.const i16x8 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (i32.const 1)) +(assert_return (invoke "i16x8.any_true" (v128.const i16x8 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB)) + (i32.const 1)) +(assert_return (invoke "i16x8.any_true" (v128.const i16x8 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55)) + (i32.const 1)) +(assert_return (invoke "i16x8.any_true" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345)) + (i32.const 1)) +(assert_return (invoke "i16x8.any_true" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234)) + (i32.const 1)) +(assert_return (invoke "i16x8.all_true" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8.all_true" (v128.const i16x8 0 0 0 0 0 0 1 0)) + (i32.const 0)) +(assert_return (invoke "i16x8.all_true" (v128.const i16x8 1 1 1 1 1 1 0 1)) + (i32.const 0)) +(assert_return (invoke "i16x8.all_true" (v128.const i16x8 1 1 1 1 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i16x8.all_true" (v128.const i16x8 -1 0 1 2 0xB 0xC 0xD 0xF)) + (i32.const 0)) +(assert_return (invoke "i16x8.all_true" (v128.const i16x8 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (i32.const 0)) +(assert_return (invoke "i16x8.all_true" (v128.const i16x8 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (i32.const 1)) +(assert_return (invoke "i16x8.all_true" (v128.const i16x8 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB)) + (i32.const 1)) +(assert_return (invoke "i16x8.all_true" (v128.const i16x8 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55)) + (i32.const 1)) +(assert_return (invoke "i16x8.all_true" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345)) + (i32.const 1)) +(assert_return (invoke "i16x8.all_true" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234)) + (i32.const 1)) +(assert_return (invoke "i16x8.bitmask" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (i32.const 0x000000FF)) +(assert_return (invoke "i16x8.bitmask" (v128.const i16x8 -1 0 1 2 0xB 0xC 0xD 0xF)) + (i32.const 0x00000001)) + +;; i32x4 +(assert_return (invoke "i32x4.any_true" (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4.any_true" (v128.const i32x4 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i32x4.any_true" (v128.const i32x4 1 1 0 1)) + (i32.const 1)) +(assert_return (invoke "i32x4.any_true" (v128.const i32x4 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i32x4.any_true" (v128.const i32x4 -1 0 1 0xF)) + (i32.const 1)) +(assert_return (invoke "i32x4.any_true" (v128.const i32x4 0x00 0x00 0x00 0x00)) + (i32.const 0)) +(assert_return (invoke "i32x4.any_true" (v128.const i32x4 0xFF 0xFF 0xFF 0xFF)) + (i32.const 1)) +(assert_return (invoke "i32x4.any_true" (v128.const i32x4 0xAB 0xAB 0xAB 0xAB)) + (i32.const 1)) +(assert_return (invoke "i32x4.any_true" (v128.const i32x4 0x55 0x55 0x55 0x55)) + (i32.const 1)) +(assert_return (invoke "i32x4.any_true" (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890)) + (i32.const 1)) +(assert_return (invoke "i32x4.any_true" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678)) + (i32.const 1)) +(assert_return (invoke "i32x4.all_true" (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4.all_true" (v128.const i32x4 0 0 1 0)) + (i32.const 0)) +(assert_return (invoke "i32x4.all_true" (v128.const i32x4 1 1 0 1)) + (i32.const 0)) +(assert_return (invoke "i32x4.all_true" (v128.const i32x4 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i32x4.all_true" (v128.const i32x4 -1 0 1 0xF)) + (i32.const 0)) +(assert_return (invoke "i32x4.all_true" (v128.const i32x4 0x00 0x00 0x00 0x00)) + (i32.const 0)) +(assert_return (invoke "i32x4.all_true" (v128.const i32x4 0xFF 0xFF 0xFF 0xFF)) + (i32.const 1)) +(assert_return (invoke "i32x4.all_true" (v128.const i32x4 0xAB 0xAB 0xAB 0xAB)) + (i32.const 1)) +(assert_return (invoke "i32x4.all_true" (v128.const i32x4 0x55 0x55 0x55 0x55)) + (i32.const 1)) +(assert_return (invoke "i32x4.all_true" (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890)) + (i32.const 1)) +(assert_return (invoke "i32x4.all_true" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678)) + (i32.const 1)) +(assert_return (invoke "i32x4.bitmask" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (i32.const 0x0000000F)) +(assert_return (invoke "i32x4.bitmask" (v128.const i32x4 -1 0 1 0xF)) + (i32.const 0x00000001)) + +;; i64x2 +(assert_return (invoke "i64x2.all_true" (v128.const i64x2 0 0)) + (i32.const 0)) +(assert_return (invoke "i64x2.all_true" (v128.const i64x2 0 1)) + (i32.const 0)) +(assert_return (invoke "i64x2.all_true" (v128.const i64x2 1 0)) + (i32.const 0)) +(assert_return (invoke "i64x2.all_true" (v128.const i64x2 1 1)) + (i32.const 1)) +(assert_return (invoke "i64x2.all_true" (v128.const i64x2 -1 0)) + (i32.const 0)) +(assert_return (invoke "i64x2.all_true" (v128.const i64x2 0x00 0x00)) + (i32.const 0)) +(assert_return (invoke "i64x2.all_true" (v128.const i64x2 0xFF 0xFF)) + (i32.const 1)) +(assert_return (invoke "i64x2.all_true" (v128.const i64x2 0xAB 0xAB)) + (i32.const 1)) +(assert_return (invoke "i64x2.all_true" (v128.const i64x2 0x55 0x55)) + (i32.const 1)) +(assert_return (invoke "i64x2.bitmask" (v128.const i64x2 0xFFFFFFFF_FFFFFFFF 0xFFFFFFFF_FFFFFFFF)) + (i32.const 0x00000003)) +(assert_return (invoke "i64x2.bitmask" (v128.const i64x2 -1 0xF)) + (i32.const 0x00000001)) + +;; Combination + +(module (memory 1) + ;; as if condition + (func (export "i8x16_any_true_as_if_cond") (param v128) (result i32) + (if (result i32) (v128.any_true (local.get 0)) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + (func (export "i16x8_any_true_as_if_cond") (param v128) (result i32) + (if (result i32) (v128.any_true (local.get 0)) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + (func (export "i32x4_any_true_as_if_cond") (param v128) (result i32) + (if (result i32) (v128.any_true (local.get 0)) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + (func (export "i8x16_all_true_as_if_cond") (param v128) (result i32) + (if (result i32) (i8x16.all_true (local.get 0)) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + (func (export "i16x8_all_true_as_if_cond") (param v128) (result i32) + (if (result i32) (i16x8.all_true (local.get 0)) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + (func (export "i32x4_all_true_as_if_cond") (param v128) (result i32) + (if (result i32) (i32x4.all_true (local.get 0)) + (then (i32.const 1)) + (else (i32.const 0)) + ) + ) + ;; any_true as select condition + (func (export "i8x16_any_true_as_select_cond") (param v128) (result i32) + (select (i32.const 1) (i32.const 0) (v128.any_true (local.get 0))) + ) + (func (export "i16x8_any_true_as_select_cond") (param v128) (result i32) + (select (i32.const 1) (i32.const 0) (v128.any_true (local.get 0))) + ) + (func (export "i32x4_any_true_as_select_cond") (param v128) (result i32) + (select (i32.const 1) (i32.const 0) (v128.any_true (local.get 0))) + ) + ;; all_true as select condition + (func (export "i8x16_all_true_as_select_cond") (param v128) (result i32) + (select (i32.const 1) (i32.const 0) (i8x16.all_true (local.get 0))) + ) + (func (export "i16x8_all_true_as_select_cond") (param v128) (result i32) + (select (i32.const 1) (i32.const 0) (i16x8.all_true (local.get 0))) + ) + (func (export "i32x4_all_true_as_select_cond") (param v128) (result i32) + (select (i32.const 1) (i32.const 0) (i32x4.all_true (local.get 0))) + ) + ;; any_true as br_if condition + (func (export "i8x16_any_true_as_br_if_cond") (param $0 v128) (result i32) + (local $1 i32) + (local.set $1 (i32.const 2)) + (block + (local.set $1 (i32.const 1)) + (br_if 0 (v128.any_true (local.get $0))) + (local.set $1 (i32.const 0)) + ) + (local.get $1) + ) + (func (export "i16x8_any_true_as_br_if_cond") (param $0 v128) (result i32) + (local $1 i32) + (local.set $1 (i32.const 2)) + (block + (local.set $1 (i32.const 1)) + (br_if 0 (v128.any_true (local.get $0))) + (local.set $1 (i32.const 0)) + ) + (local.get $1) + ) + (func (export "i32x4_any_true_as_br_if_cond") (param $0 v128) (result i32) + (local $1 i32) + (local.set $1 (i32.const 2)) + (block + (local.set $1 (i32.const 1)) + (br_if 0 (v128.any_true (local.get $0))) + (local.set $1 (i32.const 0)) + ) + (local.get $1) + ) + ;; all_true as br_if condition + (func (export "i8x16_all_true_as_br_if_cond") (param $0 v128) (result i32) + (local $1 i32) + (local.set $1 (i32.const 2)) + (block + (local.set $1 (i32.const 1)) + (br_if 0 (i8x16.all_true (local.get $0))) + (local.set $1 (i32.const 0)) + ) + (local.get $1) + ) + (func (export "i16x8_all_true_as_br_if_cond") (param $0 v128) (result i32) + (local $1 i32) + (local.set $1 (i32.const 2)) + (block + (local.set $1 (i32.const 1)) + (br_if 0 (i16x8.all_true (local.get $0))) + (local.set $1 (i32.const 0)) + ) + (local.get $1) + ) + (func (export "i32x4_all_true_as_br_if_cond") (param $0 v128) (result i32) + (local $1 i32) + (local.set $1 (i32.const 2)) + (block + (local.set $1 (i32.const 1)) + (br_if 0 (i32x4.all_true (local.get $0))) + (local.set $1 (i32.const 0)) + ) + (local.get $1) + ) + ;; any_true as i32.and operand + (func (export "i8x16_any_true_as_i32.and_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.and (v128.any_true (local.get $0)) (v128.any_true (local.get $1))) + ) + (func (export "i16x8_any_true_as_i32.and_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.and (v128.any_true (local.get $0)) (v128.any_true (local.get $1))) + ) + (func (export "i32x4_any_true_as_i32.and_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.and (v128.any_true (local.get $0)) (v128.any_true (local.get $1))) + ) + ;; any_true as i32.or operand + (func (export "i8x16_any_true_as_i32.or_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.or (v128.any_true (local.get $0)) (v128.any_true (local.get $1))) + ) + (func (export "i16x8_any_true_as_i32.or_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.or (v128.any_true (local.get $0)) (v128.any_true (local.get $1))) + ) + (func (export "i32x4_any_true_as_i32.or_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.or (v128.any_true (local.get $0)) (v128.any_true (local.get $1))) + ) + ;; any_true as i32.xor operand + (func (export "i8x16_any_true_as_i32.xor_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.xor (v128.any_true (local.get $0)) (v128.any_true (local.get $1))) + ) + (func (export "i16x8_any_true_as_i32.xor_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.xor (v128.any_true (local.get $0)) (v128.any_true (local.get $1))) + ) + (func (export "i32x4_any_true_as_i32.xor_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.xor (v128.any_true (local.get $0)) (v128.any_true (local.get $1))) + ) + ;; all_true as i32.and operand + (func (export "i8x16_all_true_as_i32.and_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.and (i8x16.all_true (local.get $0)) (i8x16.all_true (local.get $1))) + ) + (func (export "i16x8_all_true_as_i32.and_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.and (i16x8.all_true (local.get $0)) (i16x8.all_true (local.get $1))) + ) + (func (export "i32x4_all_true_as_i32.and_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.and (i32x4.all_true (local.get $0)) (i32x4.all_true (local.get $1))) + ) + ;; all_true as i32.or operand + (func (export "i8x16_all_true_as_i32.or_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.or (i8x16.all_true (local.get $0)) (i8x16.all_true (local.get $1))) + ) + (func (export "i16x8_all_true_as_i32.or_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.or (i16x8.all_true (local.get $0)) (i16x8.all_true (local.get $1))) + ) + (func (export "i32x4_all_true_as_i32.or_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.or (i32x4.all_true (local.get $0)) (i32x4.all_true (local.get $1))) + ) + ;; all_true as i32.xor operand + (func (export "i8x16_all_true_as_i32.xor_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.xor (i8x16.all_true (local.get $0)) (i8x16.all_true (local.get $1))) + ) + (func (export "i16x8_all_true_as_i32.xor_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.xor (i16x8.all_true (local.get $0)) (i16x8.all_true (local.get $1))) + ) + (func (export "i32x4_all_true_as_i32.xor_operand") (param $0 v128) (param $1 v128) (result i32) + (i32.xor (i32x4.all_true (local.get $0)) (i32x4.all_true (local.get $1))) + ) + ;; any_true with v128.not + (func (export "i8x16_any_true_with_v128.not") (param $0 v128) (result i32) + (v128.any_true (v128.not (local.get $0))) + ) + (func (export "i16x8_any_true_with_v128.not") (param $0 v128) (result i32) + (v128.any_true (v128.not (local.get $0))) + ) + (func (export "i32x4_any_true_with_v128.not") (param $0 v128) (result i32) + (v128.any_true (v128.not (local.get $0))) + ) + ;; any_true with v128.and + (func (export "i8x16_any_true_with_v128.and") (param $0 v128) (param $1 v128) (result i32) + (v128.any_true (v128.and (local.get $0) (local.get $1))) + ) + (func (export "i16x8_any_true_with_v128.and") (param $0 v128) (param $1 v128) (result i32) + (v128.any_true (v128.and (local.get $0) (local.get $1))) + ) + (func (export "i32x4_any_true_with_v128.and") (param $0 v128) (param $1 v128) (result i32) + (v128.any_true (v128.and (local.get $0) (local.get $1))) + ) + ;; any_true with v128.or + (func (export "i8x16_any_true_with_v128.or") (param $0 v128) (param $1 v128) (result i32) + (v128.any_true (v128.or (local.get $0) (local.get $1))) + ) + (func (export "i16x8_any_true_with_v128.or") (param $0 v128) (param $1 v128) (result i32) + (v128.any_true (v128.or (local.get $0) (local.get $1))) + ) + (func (export "i32x4_any_true_with_v128.or") (param $0 v128) (param $1 v128) (result i32) + (v128.any_true (v128.or (local.get $0) (local.get $1))) + ) + ;; any_true with v128.xor + (func (export "i8x16_any_true_with_v128.xor") (param $0 v128) (param $1 v128) (result i32) + (v128.any_true (v128.xor (local.get $0) (local.get $1))) + ) + (func (export "i16x8_any_true_with_v128.xor") (param $0 v128) (param $1 v128) (result i32) + (v128.any_true (v128.xor (local.get $0) (local.get $1))) + ) + (func (export "i32x4_any_true_with_v128.xor") (param $0 v128) (param $1 v128) (result i32) + (v128.any_true (v128.xor (local.get $0) (local.get $1))) + ) + ;; any_true with v128.bitselect + (func (export "i8x16_any_true_with_v128.bitselect") (param $0 v128) (param $1 v128) (param $2 v128) (result i32) + (v128.any_true (v128.bitselect (local.get $0) (local.get $1) (local.get $2))) + ) + (func (export "i16x8_any_true_with_v128.bitselect") (param $0 v128) (param $1 v128) (param $2 v128) (result i32) + (v128.any_true (v128.bitselect (local.get $0) (local.get $1) (local.get $2))) + ) + (func (export "i32x4_any_true_with_v128.bitselect") (param $0 v128) (param $1 v128) (param $2 v128) (result i32) + (v128.any_true (v128.bitselect (local.get $0) (local.get $1) (local.get $2))) + ) + ;; all_true with v128.not + (func (export "i8x16_all_true_with_v128.not") (param $0 v128) (result i32) + (i8x16.all_true (v128.not (local.get $0))) + ) + (func (export "i16x8_all_true_with_v128.not") (param $0 v128) (result i32) + (i16x8.all_true (v128.not (local.get $0))) + ) + (func (export "i32x4_all_true_with_v128.not") (param $0 v128) (result i32) + (i32x4.all_true (v128.not (local.get $0))) + ) + ;; all_true with v128.and + (func (export "i8x16_all_true_with_v128.and") (param $0 v128) (param $1 v128) (result i32) + (i8x16.all_true (v128.and (local.get $0) (local.get $1))) + ) + (func (export "i16x8_all_true_with_v128.and") (param $0 v128) (param $1 v128) (result i32) + (i16x8.all_true (v128.and (local.get $0) (local.get $1))) + ) + (func (export "i32x4_all_true_with_v128.and") (param $0 v128) (param $1 v128) (result i32) + (i32x4.all_true (v128.and (local.get $0) (local.get $1))) + ) + ;; all_true with v128.or + (func (export "i8x16_all_true_with_v128.or") (param $0 v128) (param $1 v128) (result i32) + (i8x16.all_true (v128.or (local.get $0) (local.get $1))) + ) + (func (export "i16x8_all_true_with_v128.or") (param $0 v128) (param $1 v128) (result i32) + (i16x8.all_true (v128.or (local.get $0) (local.get $1))) + ) + (func (export "i32x4_all_true_with_v128.or") (param $0 v128) (param $1 v128) (result i32) + (i32x4.all_true (v128.or (local.get $0) (local.get $1))) + ) + ;; all_true with v128.xor + (func (export "i8x16_all_true_with_v128.xor") (param $0 v128) (param $1 v128) (result i32) + (i8x16.all_true (v128.xor (local.get $0) (local.get $1))) + ) + (func (export "i16x8_all_true_with_v128.xor") (param $0 v128) (param $1 v128) (result i32) + (i16x8.all_true (v128.xor (local.get $0) (local.get $1))) + ) + (func (export "i32x4_all_true_with_v128.xor") (param $0 v128) (param $1 v128) (result i32) + (i32x4.all_true (v128.xor (local.get $0) (local.get $1))) + ) + ;; all_true with v128.bitselect + (func (export "i8x16_all_true_with_v128.bitselect") (param $0 v128) (param $1 v128) (param $2 v128) (result i32) + (i8x16.all_true (v128.bitselect (local.get $0) (local.get $1) (local.get $2))) + ) + (func (export "i16x8_all_true_with_v128.bitselect") (param $0 v128) (param $1 v128) (param $2 v128) (result i32) + (i16x8.all_true (v128.bitselect (local.get $0) (local.get $1) (local.get $2))) + ) + (func (export "i32x4_all_true_with_v128.bitselect") (param $0 v128) (param $1 v128) (param $2 v128) (result i32) + (i32x4.all_true (v128.bitselect (local.get $0) (local.get $1) (local.get $2))) + ) +) + +;; 'any_true' as 'if' condition +;; i8x16 +(assert_return (invoke "i8x16_any_true_as_if_cond" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i8x16_any_true_as_if_cond" (v128.const i8x16 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i8x16_any_true_as_if_cond" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (i32.const 1)) +;; i16x8 +(assert_return (invoke "i16x8_any_true_as_if_cond" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_any_true_as_if_cond" (v128.const i16x8 0 0 1 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i16x8_any_true_as_if_cond" (v128.const i16x8 1 1 1 1 1 1 1 1)) + (i32.const 1)) +;; i32x4 +(assert_return (invoke "i32x4_any_true_as_if_cond" (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_any_true_as_if_cond" (v128.const i32x4 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i32x4_any_true_as_if_cond" (v128.const i32x4 1 1 1 1)) + (i32.const 1)) + +;; 'all_true' as 'if' condition +;; i8x16 +(assert_return (invoke "i8x16_all_true_as_if_cond" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i8x16_all_true_as_if_cond" (v128.const i8x16 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0)) + (i32.const 0)) +(assert_return (invoke "i8x16_all_true_as_if_cond" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (i32.const 1)) +;; i16x8 +(assert_return (invoke "i16x8_all_true_as_if_cond" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_as_if_cond" (v128.const i16x8 1 1 1 0 1 1 1 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_as_if_cond" (v128.const i16x8 1 1 1 1 1 1 1 1)) + (i32.const 1)) +;; i32x4 +(assert_return (invoke "i32x4_all_true_as_if_cond" (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_as_if_cond" (v128.const i32x4 1 1 1 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_as_if_cond" (v128.const i32x4 1 1 1 1)) + (i32.const 1)) + +;; any_true as select condition +(assert_return (invoke "i8x16_any_true_as_select_cond" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i8x16_any_true_as_select_cond" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i16x8_any_true_as_select_cond" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_any_true_as_select_cond" (v128.const i16x8 0 0 0 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i32x4_any_true_as_select_cond" (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_any_true_as_select_cond" (v128.const i32x4 0 0 1 0)) + (i32.const 1)) +;; all_true as select condition +(assert_return (invoke "i8x16_all_true_as_select_cond" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i8x16_all_true_as_select_cond" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_as_select_cond" (v128.const i16x8 1 1 1 1 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i16x8_all_true_as_select_cond" (v128.const i16x8 1 1 1 1 1 1 0 1)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_as_select_cond" (v128.const i32x4 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i32x4_all_true_as_select_cond" (v128.const i32x4 1 1 0 1)) + (i32.const 0)) +;; any_true as br_if condition +(assert_return (invoke "i8x16_any_true_as_br_if_cond" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i8x16_any_true_as_br_if_cond" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i16x8_any_true_as_br_if_cond" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_any_true_as_br_if_cond" (v128.const i16x8 0 0 0 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i32x4_any_true_as_br_if_cond" (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_any_true_as_br_if_cond" (v128.const i32x4 0 0 1 0)) + (i32.const 1)) +;; all_true as br_if condition +(assert_return (invoke "i8x16_all_true_as_br_if_cond" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i8x16_all_true_as_br_if_cond" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_as_br_if_cond" (v128.const i16x8 1 1 1 1 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i16x8_all_true_as_br_if_cond" (v128.const i16x8 1 1 1 1 1 1 0 1)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_as_br_if_cond" (v128.const i32x4 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i32x4_all_true_as_br_if_cond" (v128.const i32x4 1 1 0 1)) + (i32.const 0)) +;; any_true as and operand +(assert_return (invoke "i8x16_any_true_as_i32.and_operand" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i8x16_any_true_as_i32.and_operand" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0)) + (i32.const 0)) +(assert_return (invoke "i8x16_any_true_as_i32.and_operand" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i16x8_any_true_as_i32.and_operand" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_any_true_as_i32.and_operand" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 1 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_any_true_as_i32.and_operand" (v128.const i16x8 0 0 0 0 0 0 1 0) + (v128.const i16x8 0 0 0 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i32x4_any_true_as_i32.and_operand" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_any_true_as_i32.and_operand" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 1 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_any_true_as_i32.and_operand" (v128.const i32x4 0 0 1 0) + (v128.const i32x4 0 0 1 0)) + (i32.const 1)) +;; any_true as or operand +(assert_return (invoke "i8x16_any_true_as_i32.or_operand" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i8x16_any_true_as_i32.or_operand" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i8x16_any_true_as_i32.or_operand" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i16x8_any_true_as_i32.or_operand" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_any_true_as_i32.or_operand" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i16x8_any_true_as_i32.or_operand" (v128.const i16x8 0 0 0 0 0 0 1 0) + (v128.const i16x8 0 0 0 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i32x4_any_true_as_i32.or_operand" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_any_true_as_i32.or_operand" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i32x4_any_true_as_i32.or_operand" (v128.const i32x4 0 0 1 0) + (v128.const i32x4 0 0 1 0)) + (i32.const 1)) +;; any_true as xor operand +(assert_return (invoke "i8x16_any_true_as_i32.xor_operand" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i8x16_any_true_as_i32.xor_operand" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i8x16_any_true_as_i32.xor_operand" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_any_true_as_i32.xor_operand" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_any_true_as_i32.xor_operand" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i16x8_any_true_as_i32.xor_operand" (v128.const i16x8 0 0 0 0 0 0 1 0) + (v128.const i16x8 0 0 0 0 0 0 1 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_any_true_as_i32.xor_operand" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_any_true_as_i32.xor_operand" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 1 0)) + (i32.const 1)) +(assert_return (invoke "i32x4_any_true_as_i32.xor_operand" (v128.const i32x4 0 0 1 0) + (v128.const i32x4 0 0 1 0)) + (i32.const 0)) +;; all_true as and operand +(assert_return (invoke "i8x16_all_true_as_i32.and_operand" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i8x16_all_true_as_i32.and_operand" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1)) + (i32.const 0)) +(assert_return (invoke "i8x16_all_true_as_i32.and_operand" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_as_i32.and_operand" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i16x8_all_true_as_i32.and_operand" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 0 1)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_as_i32.and_operand" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 1 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_as_i32.and_operand" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i32x4_all_true_as_i32.and_operand" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 0 1)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_as_i32.and_operand" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 1 0)) + (i32.const 0)) +;; all_true as or operand +(assert_return (invoke "i8x16_all_true_as_i32.or_operand" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i8x16_all_true_as_i32.or_operand" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1)) + (i32.const 1)) +(assert_return (invoke "i8x16_all_true_as_i32.or_operand" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_as_i32.or_operand" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i16x8_all_true_as_i32.or_operand" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 0 1)) + (i32.const 1)) +(assert_return (invoke "i16x8_all_true_as_i32.or_operand" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_as_i32.or_operand" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (i32.const 1)) +(assert_return (invoke "i32x4_all_true_as_i32.or_operand" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 0 1)) + (i32.const 1)) +(assert_return (invoke "i32x4_all_true_as_i32.or_operand" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +;; all_true as xor operand +(assert_return (invoke "i8x16_all_true_as_i32.xor_operand" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (i32.const 0)) +(assert_return (invoke "i8x16_all_true_as_i32.xor_operand" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1)) + (i32.const 1)) +(assert_return (invoke "i8x16_all_true_as_i32.xor_operand" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_as_i32.xor_operand" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_as_i32.xor_operand" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 0 1)) + (i32.const 1)) +(assert_return (invoke "i16x8_all_true_as_i32.xor_operand" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_as_i32.xor_operand" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_as_i32.xor_operand" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 0 1)) + (i32.const 1)) +(assert_return (invoke "i32x4_all_true_as_i32.xor_operand" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +;; any_true with v128.not +(assert_return (invoke "i8x16_any_true_with_v128.not" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 1)) +(assert_return (invoke "i8x16_any_true_with_v128.not" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 0)) +(assert_return (invoke "i8x16_any_true_with_v128.not" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0)) + (i32.const 1)) +(assert_return (invoke "i16x8_any_true_with_v128.not" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 1)) +(assert_return (invoke "i16x8_any_true_with_v128.not" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 0)) +(assert_return (invoke "i16x8_any_true_with_v128.not" (v128.const i16x8 0 0 0 0 0 0 -1 0)) + (i32.const 1)) +(assert_return (invoke "i32x4_any_true_with_v128.not" (v128.const i32x4 0 0 0 0)) + (i32.const 1)) +(assert_return (invoke "i32x4_any_true_with_v128.not" (v128.const i32x4 -1 -1 -1 -1)) + (i32.const 0)) +(assert_return (invoke "i32x4_any_true_with_v128.not" (v128.const i32x4 0 0 -1 0)) + (i32.const 1)) +;; any_true with v128.and +(assert_return (invoke "i8x16_any_true_with_v128.and" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i8x16_any_true_with_v128.and" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 1)) +(assert_return (invoke "i8x16_any_true_with_v128.and" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0)) + (i32.const 1)) +(assert_return (invoke "i16x8_any_true_with_v128.and" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_any_true_with_v128.and" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 1)) +(assert_return (invoke "i16x8_any_true_with_v128.and" (v128.const i16x8 0 0 0 0 0 0 -1 0) + (v128.const i16x8 0 0 0 0 0 0 -1 0)) + (i32.const 1)) +(assert_return (invoke "i32x4_any_true_with_v128.and" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_any_true_with_v128.and" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (i32.const 1)) +(assert_return (invoke "i32x4_any_true_with_v128.and" (v128.const i32x4 0 0 -1 0) + (v128.const i32x4 0 0 -1 0)) + (i32.const 1)) +;; any_true with v128.or +(assert_return (invoke "i8x16_any_true_with_v128.or" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i8x16_any_true_with_v128.or" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 1)) +(assert_return (invoke "i8x16_any_true_with_v128.or" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0)) + (i32.const 1)) +(assert_return (invoke "i16x8_any_true_with_v128.or" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_any_true_with_v128.or" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 1)) +(assert_return (invoke "i16x8_any_true_with_v128.or" (v128.const i16x8 0 0 0 0 0 0 -1 0) + (v128.const i16x8 0 0 0 0 0 0 -1 0)) + (i32.const 1)) +(assert_return (invoke "i32x4_any_true_with_v128.or" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_any_true_with_v128.or" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (i32.const 1)) +(assert_return (invoke "i32x4_any_true_with_v128.or" (v128.const i32x4 0 0 -1 0) + (v128.const i32x4 0 0 -1 0)) + (i32.const 1)) +;; any_true with v128.xor +(assert_return (invoke "i8x16_any_true_with_v128.xor" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i8x16_any_true_with_v128.xor" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 0)) +(assert_return (invoke "i8x16_any_true_with_v128.xor" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0)) + (i32.const 1)) +(assert_return (invoke "i16x8_any_true_with_v128.xor" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_any_true_with_v128.xor" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 0)) +(assert_return (invoke "i16x8_any_true_with_v128.xor" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 -1 0)) + (i32.const 1)) +(assert_return (invoke "i32x4_any_true_with_v128.xor" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_any_true_with_v128.xor" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (i32.const 0)) +(assert_return (invoke "i32x4_any_true_with_v128.xor" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 -1 0)) + (i32.const 1)) +;; any_true with v128.bitselect +(assert_return (invoke "i8x16_any_true_with_v128.bitselect" (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA) + (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55)) + (i32.const 0)) +(assert_return (invoke "i8x16_any_true_with_v128.bitselect" (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA) + (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0xFF 0x55)) + (i32.const 1)) +(assert_return (invoke "i16x8_any_true_with_v128.bitselect" (v128.const i16x8 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA) + (v128.const i16x8 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i16x8 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55)) + (i32.const 0)) +(assert_return (invoke "i16x8_any_true_with_v128.bitselect" (v128.const i16x8 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA) + (v128.const i16x8 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i16x8 0x55 0x55 0x55 0x55 0x55 0x55 0xFF 0x55)) + (i32.const 1)) +(assert_return (invoke "i32x4_any_true_with_v128.bitselect" (v128.const i32x4 0xAA 0xAA 0xAA 0xAA) + (v128.const i32x4 0x55 0x55 0x55 0x55) + (v128.const i32x4 0x55 0x55 0x55 0x55)) + (i32.const 0)) +(assert_return (invoke "i32x4_any_true_with_v128.bitselect" (v128.const i32x4 0xAA 0xAA 0xAA 0xAA) + (v128.const i32x4 0x55 0x55 0x55 0x55) + (v128.const i32x4 0x55 0x55 0xFF 0x55)) + (i32.const 1)) +;; all_true with v128.not +(assert_return (invoke "i8x16_all_true_with_v128.not" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 1)) +(assert_return (invoke "i8x16_all_true_with_v128.not" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 0)) +(assert_return (invoke "i8x16_all_true_with_v128.not" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_with_v128.not" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 1)) +(assert_return (invoke "i16x8_all_true_with_v128.not" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_with_v128.not" (v128.const i16x8 0 0 0 0 0 0 -1 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_with_v128.not" (v128.const i32x4 0 0 0 0)) + (i32.const 1)) +(assert_return (invoke "i32x4_all_true_with_v128.not" (v128.const i32x4 -1 -1 -1 -1)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_with_v128.not" (v128.const i32x4 0 0 -1 0)) + (i32.const 0)) +;; all_true with v128.and +(assert_return (invoke "i8x16_all_true_with_v128.and" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i8x16_all_true_with_v128.and" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 1)) +(assert_return (invoke "i8x16_all_true_with_v128.and" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_with_v128.and" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_with_v128.and" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 1)) +(assert_return (invoke "i16x8_all_true_with_v128.and" (v128.const i16x8 0 0 0 0 0 0 -1 0) + (v128.const i16x8 0 0 0 0 0 0 -1 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_with_v128.and" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_with_v128.and" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (i32.const 1)) +(assert_return (invoke "i32x4_all_true_with_v128.and" (v128.const i32x4 0 0 -1 0) + (v128.const i32x4 0 0 -1 0)) + (i32.const 0)) +;; all_true with v128.or +(assert_return (invoke "i8x16_all_true_with_v128.or" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i8x16_all_true_with_v128.or" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 1)) +(assert_return (invoke "i8x16_all_true_with_v128.or" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_with_v128.or" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_with_v128.or" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 1)) +(assert_return (invoke "i16x8_all_true_with_v128.or" (v128.const i16x8 0 0 0 0 0 0 -1 0) + (v128.const i16x8 0 0 0 0 0 0 -1 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_with_v128.or" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_with_v128.or" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (i32.const 1)) +(assert_return (invoke "i32x4_all_true_with_v128.or" (v128.const i32x4 0 0 -1 0) + (v128.const i32x4 0 0 -1 0)) + (i32.const 0)) +;; all_true with v128.xor +(assert_return (invoke "i8x16_all_true_with_v128.xor" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i8x16_all_true_with_v128.xor" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 0)) +(assert_return (invoke "i8x16_all_true_with_v128.xor" (v128.const i8x16 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1) + (v128.const i8x16 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0 -1 0)) + (i32.const 1)) +(assert_return (invoke "i16x8_all_true_with_v128.xor" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_with_v128.xor" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_with_v128.xor" (v128.const i16x8 0 -1 0 -1 0 -1 0 -1) + (v128.const i16x8 -1 0 -1 0 -1 0 -1 0)) + (i32.const 1)) +(assert_return (invoke "i32x4_all_true_with_v128.xor" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_with_v128.xor" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_with_v128.xor" (v128.const i32x4 0 -1 0 -1) + (v128.const i32x4 -1 0 -1 0)) + (i32.const 1)) +;; all_true with v128.bitselect +(assert_return (invoke "i8x16_all_true_with_v128.bitselect" (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA) + (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55)) + (i32.const 0)) +(assert_return (invoke "i8x16_all_true_with_v128.bitselect" (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA) + (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (i32.const 1)) +(assert_return (invoke "i16x8_all_true_with_v128.bitselect" (v128.const i16x8 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA) + (v128.const i16x8 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i16x8 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55)) + (i32.const 0)) +(assert_return (invoke "i16x8_all_true_with_v128.bitselect" (v128.const i16x8 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA) + (v128.const i16x8 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i16x8 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (i32.const 1)) +(assert_return (invoke "i32x4_all_true_with_v128.bitselect" (v128.const i32x4 0xAA 0xAA 0xAA 0xAA) + (v128.const i32x4 0x55 0x55 0x55 0x55) + (v128.const i32x4 0x55 0x55 0x55 0x55)) + (i32.const 0)) +(assert_return (invoke "i32x4_all_true_with_v128.bitselect" (v128.const i32x4 0xAA 0xAA 0xAA 0xAA) + (v128.const i32x4 0x55 0x55 0x55 0x55) + (v128.const i32x4 0xAA 0xAA 0xAA 0xAA)) + (i32.const 1)) + +;; Type check + +(assert_invalid (module (func (result i32) (v128.any_true (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i8x16.all_true (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (v128.any_true (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i16x8.all_true (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (v128.any_true (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32x4.all_true (i32.const 0)))) "type mismatch") + +;; Unknown operators + +(assert_malformed (module quote "(memory 1) (func (result i32) (f32x4.any_true (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result i32) (f32x4.all_true (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result i32) (f64x2.any_true (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result i32) (f64x2.all_true (v128.const i32x4 0 0 0 0)))") "unknown operator") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $v128.any_true-arg-empty (result v128) + (v128.any_true) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.all_true-arg-empty (result v128) + (i8x16.all_true) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $v128.any_true-arg-empty (result v128) + (v128.any_true) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.all_true-arg-empty (result v128) + (i16x8.all_true) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $v128.any_true-arg-empty (result v128) + (v128.any_true) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.all_true-arg-empty (result v128) + (i32x4.all_true) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_const.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_const.wast new file mode 100644 index 000000000..8080cfb0f --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_const.wast @@ -0,0 +1,1664 @@ +;; v128.const normal parameter (e.g. (i8x16, i16x8 i32x4, f32x4)) + +(module (func (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) drop)) +(module (func (v128.const i8x16 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80) drop)) +(module (func (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) drop)) +(module (func (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) drop)) +(module (func (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) drop)) +(module (func (v128.const i16x8 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000) drop)) +(module (func (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) drop)) +(module (func (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) drop)) +(module (func (v128.const i16x8 65_535 65_535 65_535 65_535 65_535 65_535 65_535 65_535) drop)) +(module (func (v128.const i16x8 -32_768 -32_768 -32_768 -32_768 -32_768 -32_768 -32_768 -32_768) drop)) +(module (func (v128.const i16x8 0_123_45 0_123_45 0_123_45 0_123_45 0_123_45 0_123_45 0_123_45 0_123_45) drop)) +(module (func (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) drop)) +(module (func (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff) drop)) +(module (func (v128.const i32x4 -0x80000000 -0x80000000 -0x80000000 -0x80000000) drop)) +(module (func (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) drop)) +(module (func (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) drop)) +(module (func (v128.const i32x4 0xffff_ffff 0xffff_ffff 0xffff_ffff 0xffff_ffff) drop)) +(module (func (v128.const i32x4 -0x8000_0000 -0x8000_0000 -0x8000_0000 -0x8000_0000) drop)) +(module (func (v128.const i32x4 4_294_967_295 4_294_967_295 4_294_967_295 4_294_967_295) drop)) +(module (func (v128.const i32x4 -2_147_483_648 -2_147_483_648 -2_147_483_648 -2_147_483_648) drop)) +(module (func (v128.const i32x4 0_123_456_789 0_123_456_789 0_123_456_789 0_123_456_789) drop)) +(module (func (v128.const i32x4 0x0_9acf_fBDF 0x0_9acf_fBDF 0x0_9acf_fBDF 0x0_9acf_fBDF) drop)) +(module (func (v128.const i64x2 0xffffffffffffffff 0xffffffffffffffff) drop)) +(module (func (v128.const i64x2 -0x8000000000000000 -0x8000000000000000) drop)) +(module (func (v128.const i64x2 18446744073709551615 18446744073709551615) drop)) +(module (func (v128.const i64x2 -9223372036854775808 -9223372036854775808) drop)) +(module (func (v128.const i64x2 0xffff_ffff_ffff_ffff 0xffff_ffff_ffff_ffff) drop)) +(module (func (v128.const i64x2 -0x8000_0000_0000_0000 -0x8000_0000_0000_0000) drop)) +(module (func (v128.const i64x2 18_446_744_073_709_551_615 18_446_744_073_709_551_615) drop)) +(module (func (v128.const i64x2 -9_223_372_036_854_775_808 -9_223_372_036_854_775_808) drop)) +(module (func (v128.const i64x2 0_123_456_789 0_123_456_789) drop)) +(module (func (v128.const i64x2 0x0125_6789_ADEF_bcef 0x0125_6789_ADEF_bcef) drop)) +(module (func (v128.const f32x4 0x1p127 0x1p127 0x1p127 0x1p127) drop)) +(module (func (v128.const f32x4 -0x1p127 -0x1p127 -0x1p127 -0x1p127) drop)) +(module (func (v128.const f32x4 1e38 1e38 1e38 1e38) drop)) +(module (func (v128.const f32x4 -1e38 -1e38 -1e38 -1e38) drop)) +(module (func (v128.const f32x4 340282356779733623858607532500980858880 340282356779733623858607532500980858880 + 340282356779733623858607532500980858880 340282356779733623858607532500980858880) drop)) +(module (func (v128.const f32x4 -340282356779733623858607532500980858880 -340282356779733623858607532500980858880 + -340282356779733623858607532500980858880 -340282356779733623858607532500980858880) drop)) +(module (func (v128.const f32x4 nan:0x1 nan:0x1 nan:0x1 nan:0x1) drop)) +(module (func (v128.const f32x4 nan:0x7f_ffff nan:0x7f_ffff nan:0x7f_ffff nan:0x7f_ffff) drop)) +(module (func (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) drop)) +(module (func (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) drop)) +(module (func (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) drop)) +(module (func (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) drop)) +(module (func (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) drop)) +(module (func (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) drop)) +(module (func (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) drop)) +(module (func (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) drop)) +(module (func (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) drop)) +(module (func (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) drop)) +(module (func (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) drop)) +(module (func (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) drop)) +(module (func (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) drop)) +(module (func (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) drop)) +(module (func (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) drop)) +(module (func (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) drop)) +(module (func (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) drop)) +(module (func (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) drop)) +(module (func (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) drop)) +(module (func (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) drop)) +(module (func (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) drop)) +(module (func (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) drop)) +(module (func (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) drop)) +(module (func (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) drop)) +(module (func (v128.const f64x2 0x1p1023 0x1p1023) drop)) +(module (func (v128.const f64x2 -0x1p1023 -0x1p1023) drop)) +(module (func (v128.const f64x2 1e308 1e308) drop)) +(module (func (v128.const f64x2 -1e308 -1e308) drop)) +(module (func (v128.const f64x2 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368 + 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368) drop)) +(module (func (v128.const f64x2 -179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368 + -179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368) drop)) +(module (func (v128.const f64x2 nan:0x1 nan:0x1) drop)) +(module (func (v128.const f64x2 nan:0xf_ffff_ffff_ffff nan:0xf_ffff_ffff_ffff) drop)) +(module (func (v128.const f64x2 0123456789 0123456789) drop)) +(module (func (v128.const f64x2 0123456789e019 0123456789e019) drop)) +(module (func (v128.const f64x2 0123456789e+019 0123456789e+019) drop)) +(module (func (v128.const f64x2 0123456789e-019 0123456789e-019) drop)) +(module (func (v128.const f64x2 0123456789. 0123456789.) drop)) +(module (func (v128.const f64x2 0123456789.e019 0123456789.e019) drop)) +(module (func (v128.const f64x2 0123456789.e+019 0123456789.e+019) drop)) +(module (func (v128.const f64x2 0123456789.e-019 0123456789.e-019) drop)) +(module (func (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) drop)) +(module (func (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) drop)) +(module (func (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) drop)) +(module (func (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) drop)) +(module (func (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) drop)) +(module (func (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) drop)) +(module (func (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) drop)) +(module (func (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) drop)) +(module (func (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) drop)) +(module (func (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) drop)) +(module (func (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) drop)) +(module (func (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) drop)) +(module (func (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) drop)) +(module (func (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) drop)) +(module (func (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) drop)) +(module (func (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) drop)) + +;; Non-splat cases + +(module (func (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF + -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80) drop)) +(module (func (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 255 255 255 255 + -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80) drop)) +(module (func (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 255 255 255 255 + -0x80 -0x80 -0x80 -0x80 -128 -128 -128 -128) drop)) +(module (func (v128.const i16x8 0xFF 0xFF 0xFF 0xFF -0x8000 -0x8000 -0x8000 -0x8000) drop)) +(module (func (v128.const i16x8 0xFF 0xFF 65535 65535 -0x8000 -0x8000 -0x8000 -0x8000) drop)) +(module (func (v128.const i16x8 0xFF 0xFF 65535 65535 -0x8000 -0x8000 -32768 -32768) drop)) +(module (func (v128.const i32x4 0xffffffff 0xffffffff -0x80000000 -0x80000000) drop)) +(module (func (v128.const i32x4 0xffffffff 4294967295 -0x80000000 -0x80000000) drop)) +(module (func (v128.const i32x4 0xffffffff 4294967295 -0x80000000 -2147483648) drop)) +(module (func (v128.const f32x4 0x1p127 0x1p127 -0x1p127 -1e38) drop)) +(module (func (v128.const f32x4 0x1p127 340282356779733623858607532500980858880 -1e38 -340282356779733623858607532500980858880) drop)) +(module (func (v128.const f32x4 nan -nan inf -inf) drop)) +(module (func (v128.const i64x2 0xffffffffffffffff 0x8000000000000000) drop)) +(module (func (v128.const i64x2 0xffffffffffffffff -9223372036854775808) drop)) +(module (func (v128.const f64x2 0x1p1023 -1e308) drop)) +(module (func (v128.const f64x2 nan -inf) drop)) + +;; Constant out of range (int literal is too large) + +(module (memory 1)) +(assert_malformed + (module quote "(func (v128.const i8x16 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const i8x16 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const i8x16 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256 256) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const i8x16 -129 -129 -129 -129 -129 -129 -129 -129 -129 -129 -129 -129 -129 -129 -129 -129) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const i16x8 0x10000 0x10000 0x10000 0x10000 0x10000 0x10000 0x10000 0x10000) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const i16x8 -0x8001 -0x8001 -0x8001 -0x8001 -0x8001 -0x8001 -0x8001 -0x8001) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const i16x8 65536 65536 65536 65536 65536 65536 65536 65536) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const i16x8 -32769 -32769 -32769 -32769 -32769 -32769 -32769 -32769) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const i32x4 0x100000000 0x100000000 0x100000000 0x100000000) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const i32x4 -0x80000001 -0x80000001 -0x80000001 -0x80000001) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const i32x4 4294967296 4294967296 4294967296 4294967296) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const i32x4 -2147483649 -2147483649 -2147483649 -2147483649) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0x1p128 0x1p128 0x1p128 0x1p128) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const f32x4 -0x1p128 -0x1p128 -0x1p128 -0x1p128) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const f32x4 1e39 1e39 1e39 1e39) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const f32x4 -1e39 -1e39 -1e39 -1e39) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const f32x4 340282356779733661637539395458142568448 340282356779733661637539395458142568448" + " 340282356779733661637539395458142568448 340282356779733661637539395458142568448) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const f32x4 -340282356779733661637539395458142568448 -340282356779733661637539395458142568448" + " -340282356779733661637539395458142568448 -340282356779733661637539395458142568448) drop)") + "constant out of range" +) + +(assert_malformed + (module quote "(func (v128.const f32x4 nan:0x80_0000 nan:0x80_0000 nan:0x80_0000 nan:0x80_0000) drop)") + "constant out of range" +) + +(assert_malformed + (module quote "(func (v128.const f64x2 269653970229347356221791135597556535197105851288767494898376215204735891170042808140884337949150317257310688430271573696351481990334196274152701320055306275479074865864826923114368235135583993416113802762682700913456874855354834422248712838998185022412196739306217084753107265771378949821875606039276187287552" + " 269653970229347356221791135597556535197105851288767494898376215204735891170042808140884337949150317257310688430271573696351481990334196274152701320055306275479074865864826923114368235135583993416113802762682700913456874855354834422248712838998185022412196739306217084753107265771378949821875606039276187287552) drop)") + "constant out of range" +) +(assert_malformed + (module quote "(func (v128.const f64x2 -269653970229347356221791135597556535197105851288767494898376215204735891170042808140884337949150317257310688430271573696351481990334196274152701320055306275479074865864826923114368235135583993416113802762682700913456874855354834422248712838998185022412196739306217084753107265771378949821875606039276187287552" + " -269653970229347356221791135597556535197105851288767494898376215204735891170042808140884337949150317257310688430271573696351481990334196274152701320055306275479074865864826923114368235135583993416113802762682700913456874855354834422248712838998185022412196739306217084753107265771378949821875606039276187287552) drop)") + "constant out of range" +) + +(assert_malformed + (module quote "(func (v128.const f64x2 nan:0x10_0000_0000_0000 nan:0x10_0000_0000_0000) drop)") + "constant out of range" +) + +;; More malformed v128.const forms +(assert_malformed + (module quote "(func (v128.const) drop)") + "unexpected token" +) + +(assert_malformed + (module quote "(func (v128.const 0 0 0 0) drop)") + "unexpected token" +) +(assert_malformed + (module quote "(func (v128.const i8x16) drop)") + "wrong number of lane literals" +) +(assert_malformed + (module quote "(func (v128.const i8x16 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const i8x16 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const i8x16 0xg 0xg 0xg 0xg 0xg 0xg 0xg 0xg 0xg 0xg 0xg 0xg 0xg 0xg 0xg 0xg) drop)") + "unknown operator" +) + +(assert_malformed + (module quote "(func (v128.const i16x8) drop)") + "wrong number of lane literals" +) +(assert_malformed + (module quote "(func (v128.const i16x8 0x 0x 0x 0x 0x 0x 0x 0x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const i16x8 1x 1x 1x 1x 1x 1x 1x 1x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const i16x8 0xg 0xg 0xg 0xg 0xg 0xg 0xg 0xg) drop)") + "unknown operator" +) + +(assert_malformed + (module quote "(func (v128.const i32x4) drop)") + "wrong number of lane literals" +) +(assert_malformed + (module quote "(func (v128.const i32x4 0x 0x 0x 0x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const i32x4 1x 1x 1x 1x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const i32x4 0xg 0xg 0xg 0xg) drop)") + "unknown operator" +) + +(assert_malformed + (module quote "(func (v128.const i64x2) drop)") + "wrong number of lane literals" +) +(assert_malformed + (module quote "(func (v128.const i64x2 0x 0x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 1x 1x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0xg 0xg) drop)") + "unknown operator" +) + +(assert_malformed + (module quote "(func (v128.const f32x4) drop)") + "wrong number of lane literals" +) +(assert_malformed + (module quote "(func (v128.const f32x4 .0 .0 .0 .0) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 .0e0 .0e0 .0e0 .0e0) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0e 0e 0e 0e) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0e+ 0e+ 0e+ 0e+) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0.0e 0.0e 0.0e 0.0e) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0.0e- 0.0e- 0.0e- 0.0e-) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0x 0x 0x 0x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 1x 1x 1x 1x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0xg 0xg 0xg 0xg) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0x. 0x. 0x. 0x.) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0x0.g 0x0.g 0x0.g 0x0.g) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0x0p 0x0p 0x0p 0x0p) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0x0p+ 0x0p+ 0x0p+ 0x0p+) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0x0p- 0x0p- 0x0p- 0x0p-) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0x0.0p 0x0.0p 0x0.0p 0x0.0p) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0x0.0p+ 0x0.0p+ 0x0.0p+ 0x0.0p+) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0x0.0p- 0x0.0p- 0x0.0p- 0x0.0p-) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 0x0pA 0x0pA 0x0pA 0x0pA) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 nan:1 nan:1 nan:1 nan:1) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f32x4 nan:0x0 nan:0x0 nan:0x0 nan:0x0) drop)") + "constant out of range" +) + +(assert_malformed + (module quote "(func (v128.const f64x2) drop)") + "wrong number of lane literals" +) +(assert_malformed + (module quote "(func (v128.const f64x2 .0 .0) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 .0e0 .0e0) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0e 0e) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0e+ 0e+) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0.0e+ 0.0e+) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0.0e- 0.0e-) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0x 0x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 1x 1x) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0xg 0xg) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0x. 0x.) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0x0.g 0x0.g) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0x0p 0x0p) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0x0p+ 0x0p+) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0x0p- 0x0p-) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0x0.0p 0x0.0p) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0x0.0p+ 0x0.0p+) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0x0.0p- 0x0.0p-) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 0x0pA 0x0pA) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 nan:1 nan:1) drop)") + "unknown operator" +) +(assert_malformed + (module quote "(func (v128.const f64x2 nan:0x0 nan:0x0) drop)") + "constant out of range" +) + +;; too little arguments + +(assert_malformed + (module quote "(func (v128.const i32x4 0x10000000000000000 0x10000000000000000) drop)") + "wrong number of lane literals" +) + +;; too many arguments +(assert_malformed + (module quote "(func (v128.const i32x4 0x1 0x1 0x1 0x1 0x1) drop)") + "wrong number of lane literals" +) + +;; Rounding behaviour + +;; f32x4, small exponent +(module (func (export "f") (result v128) (v128.const f32x4 +0x1.00000100000000000p-50 +0x1.00000100000000000p-50 +0x1.00000100000000000p-50 +0x1.00000100000000000p-50))) +(assert_return (invoke "f") (v128.const f32x4 +0x1.000000p-50 +0x1.000000p-50 +0x1.000000p-50 +0x1.000000p-50)) +(module (func (export "f") (result v128) (v128.const f32x4 -0x1.00000100000000000p-50 -0x1.00000100000000000p-50 -0x1.00000100000000000p-50 -0x1.00000100000000000p-50))) +(assert_return (invoke "f") (v128.const f32x4 -0x1.000000p-50 -0x1.000000p-50 -0x1.000000p-50 -0x1.000000p-50)) +(module (func (export "f") (result v128) (v128.const f32x4 +0x1.00000500000000001p-50 +0x1.00000500000000001p-50 +0x1.00000500000000001p-50 +0x1.00000500000000001p-50))) +(assert_return (invoke "f") (v128.const f32x4 +0x1.000006p-50 +0x1.000006p-50 +0x1.000006p-50 +0x1.000006p-50)) +(module (func (export "f") (result v128) (v128.const f32x4 -0x1.00000500000000001p-50 -0x1.00000500000000001p-50 -0x1.00000500000000001p-50 -0x1.00000500000000001p-50))) +(assert_return (invoke "f") (v128.const f32x4 -0x1.000006p-50 -0x1.000006p-50 -0x1.000006p-50 -0x1.000006p-50)) + +(module (func (export "f") (result v128) (v128.const f32x4 +0x4000.004000000p-64 +0x4000.004000000p-64 +0x4000.004000000p-64 +0x4000.004000000p-64))) +(assert_return (invoke "f") (v128.const f32x4 +0x1.000000p-50 +0x1.000000p-50 +0x1.000000p-50 +0x1.000000p-50)) +(module (func (export "f") (result v128) (v128.const f32x4 -0x4000.004000000p-64 -0x4000.004000000p-64 -0x4000.004000000p-64 -0x4000.004000000p-64))) +(assert_return (invoke "f") (v128.const f32x4 -0x1.000000p-50 -0x1.000000p-50 -0x1.000000p-50 -0x1.000000p-50)) +(module (func (export "f") (result v128) (v128.const f32x4 +0x4000.014000001p-64 +0x4000.014000001p-64 +0x4000.014000001p-64 +0x4000.014000001p-64))) +(assert_return (invoke "f") (v128.const f32x4 +0x1.000006p-50 +0x1.000006p-50 +0x1.000006p-50 +0x1.000006p-50)) +(module (func (export "f") (result v128) (v128.const f32x4 -0x4000.014000001p-64 -0x4000.014000001p-64 -0x4000.014000001p-64 -0x4000.014000001p-64))) +(assert_return (invoke "f") (v128.const f32x4 -0x1.000006p-50 -0x1.000006p-50 -0x1.000006p-50 -0x1.000006p-50)) + +(module (func (export "f") (result v128) (v128.const f32x4 +8.8817847263968443573e-16 +8.8817847263968443573e-16 +8.8817847263968443573e-16 +8.8817847263968443573e-16))) +(assert_return (invoke "f") (v128.const f32x4 +0x1.000000p-50 +0x1.000000p-50 +0x1.000000p-50 +0x1.000000p-50)) +(module (func (export "f") (result v128) (v128.const f32x4 -8.8817847263968443573e-16 -8.8817847263968443573e-16 -8.8817847263968443573e-16 -8.8817847263968443573e-16))) +(assert_return (invoke "f") (v128.const f32x4 -0x1.000000p-50 -0x1.000000p-50 -0x1.000000p-50 -0x1.000000p-50)) +(module (func (export "f") (result v128) (v128.const f32x4 +8.8817857851880284253e-16 +8.8817857851880284253e-16 +8.8817857851880284253e-16 +8.8817857851880284253e-16))) +(assert_return (invoke "f") (v128.const f32x4 +0x1.000004p-50 +0x1.000004p-50 +0x1.000004p-50 +0x1.000004p-50)) +(module (func (export "f") (result v128) (v128.const f32x4 -8.8817857851880284253e-16 -8.8817857851880284253e-16 -8.8817857851880284253e-16 -8.8817857851880284253e-16))) +(assert_return (invoke "f") (v128.const f32x4 -0x1.000004p-50 -0x1.000004p-50 -0x1.000004p-50 -0x1.000004p-50)) + +;; f32x4, large exponent +(module (func (export "f") (result v128) (v128.const f32x4 +0x1.00000100000000000p+50 +0x1.00000100000000000p+50 +0x1.00000100000000000p+50 +0x1.00000100000000000p+50))) +(assert_return (invoke "f") (v128.const f32x4 +0x1.000000p+50 +0x1.000000p+50 +0x1.000000p+50 +0x1.000000p+50)) +(module (func (export "f") (result v128) (v128.const f32x4 -0x1.00000100000000000p+50 -0x1.00000100000000000p+50 -0x1.00000100000000000p+50 -0x1.00000100000000000p+50))) +(assert_return (invoke "f") (v128.const f32x4 -0x1.000000p+50 -0x1.000000p+50 -0x1.000000p+50 -0x1.000000p+50)) +(module (func (export "f") (result v128) (v128.const f32x4 +0x1.00000500000000001p+50 +0x1.00000500000000001p+50 +0x1.00000500000000001p+50 +0x1.00000500000000001p+50))) +(assert_return (invoke "f") (v128.const f32x4 +0x1.000006p+50 +0x1.000006p+50 +0x1.000006p+50 +0x1.000006p+50)) +(module (func (export "f") (result v128) (v128.const f32x4 -0x1.00000500000000001p+50 -0x1.00000500000000001p+50 -0x1.00000500000000001p+50 -0x1.00000500000000001p+50))) +(assert_return (invoke "f") (v128.const f32x4 -0x1.000006p+50 -0x1.000006p+50 -0x1.000006p+50 -0x1.000006p+50)) + +(module (func (export "f") (result v128) (v128.const f32x4 +0x4000004000000 +0x4000004000000 +0x4000004000000 +0x4000004000000))) +(assert_return (invoke "f") (v128.const f32x4 +0x1.000000p+50 +0x1.000000p+50 +0x1.000000p+50 +0x1.000000p+50)) +(module (func (export "f") (result v128) (v128.const f32x4 -0x4000004000000 -0x4000004000000 -0x4000004000000 -0x4000004000000))) +(assert_return (invoke "f") (v128.const f32x4 -0x1.000000p+50 -0x1.000000p+50 -0x1.000000p+50 -0x1.000000p+50)) +(module (func (export "f") (result v128) (v128.const f32x4 +0x400000c000000 +0x400000c000000 +0x400000c000000 +0x400000c000000))) +(assert_return (invoke "f") (v128.const f32x4 +0x1.000004p+50 +0x1.000004p+50 +0x1.000004p+50 +0x1.000004p+50)) +(module (func (export "f") (result v128) (v128.const f32x4 -0x400000c000000 -0x400000c000000 -0x400000c000000 -0x400000c000000))) +(assert_return (invoke "f") (v128.const f32x4 -0x1.000004p+50 -0x1.000004p+50 -0x1.000004p+50 -0x1.000004p+50)) + +(module (func (export "f") (result v128) (v128.const f32x4 +1125899973951488 +1125899973951488 +1125899973951488 +1125899973951488))) +(assert_return (invoke "f") (v128.const f32x4 +0x1.000000p+50 +0x1.000000p+50 +0x1.000000p+50 +0x1.000000p+50)) +(module (func (export "f") (result v128) (v128.const f32x4 -1125899973951488 -1125899973951488 -1125899973951488 -1125899973951488))) +(assert_return (invoke "f") (v128.const f32x4 -0x1.000000p+50 -0x1.000000p+50 -0x1.000000p+50 -0x1.000000p+50)) +(module (func (export "f") (result v128) (v128.const f32x4 +1125900108169216 +1125900108169216 +1125900108169216 +1125900108169216))) +(assert_return (invoke "f") (v128.const f32x4 +0x1.000004p+50 +0x1.000004p+50 +0x1.000004p+50 +0x1.000004p+50)) +(module (func (export "f") (result v128) (v128.const f32x4 -1125900108169216 -1125900108169216 -1125900108169216 -1125900108169216))) +(assert_return (invoke "f") (v128.const f32x4 -0x1.000004p+50 -0x1.000004p+50 -0x1.000004p+50 -0x1.000004p+50)) + +;; f32x4, subnormal +(module (func (export "f") (result v128) (v128.const f32x4 +0x0.00000100000000000p-126 +0x0.00000100000000000p-126 +0x0.00000100000000000p-126 +0x0.00000100000000000p-126))) +(assert_return (invoke "f") (v128.const f32x4 +0x0.000000p-126 +0x0.000000p-126 +0x0.000000p-126 +0x0.000000p-126)) +(module (func (export "f") (result v128) (v128.const f32x4 -0x0.00000100000000000p-126 -0x0.00000100000000000p-126 -0x0.00000100000000000p-126 -0x0.00000100000000000p-126))) +(assert_return (invoke "f") (v128.const f32x4 -0x0.000000p-126 -0x0.000000p-126 -0x0.000000p-126 -0x0.000000p-126)) +(module (func (export "f") (result v128) (v128.const f32x4 +0x0.00000500000000001p-126 +0x0.00000500000000001p-126 +0x0.00000500000000001p-126 +0x0.00000500000000001p-126))) +(assert_return (invoke "f") (v128.const f32x4 +0x0.000006p-126 +0x0.000006p-126 +0x0.000006p-126 +0x0.000006p-126)) +(module (func (export "f") (result v128) (v128.const f32x4 -0x0.00000500000000001p-126 -0x0.00000500000000001p-126 -0x0.00000500000000001p-126 -0x0.00000500000000001p-126))) +(assert_return (invoke "f") (v128.const f32x4 -0x0.000006p-126 -0x0.000006p-126 -0x0.000006p-126 -0x0.000006p-126)) + +;; f32x4, round down at limit to infinity +(module (func (export "f") (result v128) (v128.const f32x4 +0x1.fffffe8p127 +0x1.fffffe8p127 +0x1.fffffe8p127 +0x1.fffffe8p127))) +(assert_return (invoke "f") (v128.const f32x4 +0x1.fffffep127 +0x1.fffffep127 +0x1.fffffep127 +0x1.fffffep127)) +(module (func (export "f") (result v128) (v128.const f32x4 -0x1.fffffe8p127 -0x1.fffffe8p127 -0x1.fffffe8p127 -0x1.fffffe8p127))) +(assert_return (invoke "f") (v128.const f32x4 -0x1.fffffep127 -0x1.fffffep127 -0x1.fffffep127 -0x1.fffffep127)) +(module (func (export "f") (result v128) (v128.const f32x4 +0x1.fffffefffffffffffp127 +0x1.fffffefffffffffffp127 +0x1.fffffefffffffffffp127 +0x1.fffffefffffffffffp127))) +(assert_return (invoke "f") (v128.const f32x4 +0x1.fffffep127 +0x1.fffffep127 +0x1.fffffep127 +0x1.fffffep127)) +(module (func (export "f") (result v128) (v128.const f32x4 -0x1.fffffefffffffffffp127 -0x1.fffffefffffffffffp127 -0x1.fffffefffffffffffp127 -0x1.fffffefffffffffffp127))) +(assert_return (invoke "f") (v128.const f32x4 -0x1.fffffep127 -0x1.fffffep127 -0x1.fffffep127 -0x1.fffffep127)) + +;; f64x2, small exponent +(module (func (export "f") (result f64) (f64.const +0x1.000000000000080000000000p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000000p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000080000000000p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000000p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000080000000001p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000080000000001p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.0000000000000fffffffffffp-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.0000000000000fffffffffffp-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000100000000000p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000100000000000p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000100000000001p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000100000000001p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.00000000000017ffffffffffp-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.00000000000017ffffffffffp-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000180000000000p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000180000000000p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000180000000001p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000180000000001p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.0000000000001fffffffffffp-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.0000000000001fffffffffffp-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000200000000000p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000200000000000p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000200000000001p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000200000000001p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.00000000000027ffffffffffp-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.00000000000027ffffffffffp-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x1.000000000000280000000001p-600))) +(assert_return (invoke "f") (f64.const +0x1.0000000000003p-600)) +(module (func (export "f") (result f64) (f64.const -0x1.000000000000280000000001p-600))) +(assert_return (invoke "f") (f64.const -0x1.0000000000003p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000000400000000000p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000000p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000400000000000p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000000p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000000400000000001p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000400000000001p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.0000007fffffffffffp-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.0000007fffffffffffp-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000000800000000000p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000800000000000p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000000800000000001p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000800000000001p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000000bfffffffffffp-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000bfffffffffffp-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000000c00000000000p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000c00000000000p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000000c00000000001p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000c00000000001p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000000ffffffffffffp-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000000ffffffffffffp-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000001000000000000p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000001000000000000p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000001000000000001p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000001000000000001p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.0000013fffffffffffp-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.0000013fffffffffffp-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p-600)) +(module (func (export "f") (result f64) (f64.const +0x8000000.000001400000000001p-627))) +(assert_return (invoke "f") (f64.const +0x1.0000000000003p-600)) +(module (func (export "f") (result f64) (f64.const -0x8000000.000001400000000001p-627))) +(assert_return (invoke "f") (f64.const -0x1.0000000000003p-600)) +(module (func (export "f") (result f64) (f64.const +5.3575430359313371995e+300))) +(assert_return (invoke "f") (f64.const +0x1.0000000000000p+999)) +(module (func (export "f") (result f64) (f64.const -5.3575430359313371995e+300))) +(assert_return (invoke "f") (f64.const -0x1.0000000000000p+999)) +(module (func (export "f") (result f64) (f64.const +5.3575430359313371996e+300))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+999)) +(module (func (export "f") (result f64) (f64.const -5.3575430359313371996e+300))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+999)) +(module (func (export "f") (result f64) (f64.const +5.3575430359313383891e+300))) +(assert_return (invoke "f") (f64.const +0x1.0000000000001p+999)) +(module (func (export "f") (result f64) (f64.const -5.3575430359313383891e+300))) +(assert_return (invoke "f") (f64.const -0x1.0000000000001p+999)) +(module (func (export "f") (result f64) (f64.const +5.3575430359313383892e+300))) +(assert_return (invoke "f") (f64.const +0x1.0000000000002p+999)) +(module (func (export "f") (result f64) (f64.const -5.3575430359313383892e+300))) +(assert_return (invoke "f") (f64.const -0x1.0000000000002p+999)) + +;; f64, large exponent +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.000000000000080000000000p+600 +0x1.000000000000080000000000p+600))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000000p+600 +0x1.0000000000000p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.000000000000080000000000p+600 -0x1.000000000000080000000000p+600))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000000p+600 -0x1.0000000000000p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.000000000000080000000001p+600 +0x1.000000000000080000000001p+600))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000001p+600 +0x1.0000000000001p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.000000000000080000000001p+600 -0x1.000000000000080000000001p+600))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000001p+600 -0x1.0000000000001p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.0000000000000fffffffffffp+600 +0x1.0000000000000fffffffffffp+600))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000001p+600 +0x1.0000000000001p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.0000000000000fffffffffffp+600 -0x1.0000000000000fffffffffffp+600))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000001p+600 -0x1.0000000000001p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.000000000000100000000000p+600 +0x1.000000000000100000000000p+600))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000001p+600 +0x1.0000000000001p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.000000000000100000000000p+600 -0x1.000000000000100000000000p+600))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000001p+600 -0x1.0000000000001p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.000000000000100000000001p+600 +0x1.000000000000100000000001p+600))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000001p+600 +0x1.0000000000001p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.000000000000100000000001p+600 -0x1.000000000000100000000001p+600))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000001p+600 -0x1.0000000000001p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.00000000000017ffffffffffp+600 +0x1.00000000000017ffffffffffp+600))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000001p+600 +0x1.0000000000001p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.00000000000017ffffffffffp+600 -0x1.00000000000017ffffffffffp+600))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000001p+600 -0x1.0000000000001p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.000000000000180000000000p+600 +0x1.000000000000180000000000p+600))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000002p+600 +0x1.0000000000002p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.000000000000180000000000p+600 -0x1.000000000000180000000000p+600))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000002p+600 -0x1.0000000000002p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.000000000000180000000001p+600 +0x1.000000000000180000000001p+600))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000002p+600 +0x1.0000000000002p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.000000000000180000000001p+600 -0x1.000000000000180000000001p+600))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000002p+600 -0x1.0000000000002p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.0000000000001fffffffffffp+600 +0x1.0000000000001fffffffffffp+600))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000002p+600 +0x1.0000000000002p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.0000000000001fffffffffffp+600 -0x1.0000000000001fffffffffffp+600))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000002p+600 -0x1.0000000000002p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.000000000000200000000000p+600 +0x1.000000000000200000000000p+600))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000002p+600 +0x1.0000000000002p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.000000000000200000000000p+600 -0x1.000000000000200000000000p+600))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000002p+600 -0x1.0000000000002p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.000000000000200000000001p+600 +0x1.000000000000200000000001p+600))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000002p+600 +0x1.0000000000002p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.000000000000200000000001p+600 -0x1.000000000000200000000001p+600))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000002p+600 -0x1.0000000000002p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.00000000000027ffffffffffp+600 +0x1.00000000000027ffffffffffp+600))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000002p+600 +0x1.0000000000002p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.00000000000027ffffffffffp+600 -0x1.00000000000027ffffffffffp+600))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000002p+600 -0x1.0000000000002p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.000000000000280000000000p+600 +0x1.000000000000280000000000p+600))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000002p+600 +0x1.0000000000002p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.000000000000280000000000p+600 -0x1.000000000000280000000000p+600))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000002p+600 -0x1.0000000000002p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.000000000000280000000001p+600 +0x1.000000000000280000000001p+600))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000003p+600 +0x1.0000000000003p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.000000000000280000000001p+600 -0x1.000000000000280000000001p+600))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000003p+600 -0x1.0000000000003p+600)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x2000000000000100000000000 +0x2000000000000100000000000))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000000p+97 +0x1.0000000000000p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x2000000000000100000000000 -0x2000000000000100000000000))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000000p+97 -0x1.0000000000000p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x2000000000000100000000001 +0x2000000000000100000000001))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000001p+97 +0x1.0000000000001p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x2000000000000100000000001 -0x2000000000000100000000001))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000001p+97 -0x1.0000000000001p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x20000000000001fffffffffff +0x20000000000001fffffffffff))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000001p+97 +0x1.0000000000001p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x20000000000001fffffffffff -0x20000000000001fffffffffff))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000001p+97 -0x1.0000000000001p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x2000000000000200000000000 +0x2000000000000200000000000))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000001p+97 +0x1.0000000000001p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x2000000000000200000000000 -0x2000000000000200000000000))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000001p+97 -0x1.0000000000001p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x2000000000000200000000001 +0x2000000000000200000000001))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000001p+97 +0x1.0000000000001p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x2000000000000200000000001 -0x2000000000000200000000001))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000001p+97 -0x1.0000000000001p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x20000000000002fffffffffff +0x20000000000002fffffffffff))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000001p+97 +0x1.0000000000001p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x20000000000002fffffffffff -0x20000000000002fffffffffff))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000001p+97 -0x1.0000000000001p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x2000000000000300000000000 +0x2000000000000300000000000))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000002p+97 +0x1.0000000000002p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x2000000000000300000000000 -0x2000000000000300000000000))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000002p+97 -0x1.0000000000002p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x2000000000000300000000001 +0x2000000000000300000000001))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000002p+97 +0x1.0000000000002p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x2000000000000300000000001 -0x2000000000000300000000001))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000002p+97 -0x1.0000000000002p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x20000000000003fffffffffff +0x20000000000003fffffffffff))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000002p+97 +0x1.0000000000002p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x20000000000003fffffffffff -0x20000000000003fffffffffff))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000002p+97 -0x1.0000000000002p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x2000000000000400000000000 +0x2000000000000400000000000))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000002p+97 +0x1.0000000000002p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x2000000000000400000000000 -0x2000000000000400000000000))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000002p+97 -0x1.0000000000002p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x2000000000000400000000001 +0x2000000000000400000000001))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000002p+97 +0x1.0000000000002p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x2000000000000400000000001 -0x2000000000000400000000001))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000002p+97 -0x1.0000000000002p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x20000000000004fffffffffff +0x20000000000004fffffffffff))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000002p+97 +0x1.0000000000002p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x20000000000004fffffffffff -0x20000000000004fffffffffff))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000002p+97 -0x1.0000000000002p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x2000000000000500000000000 +0x2000000000000500000000000))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000002p+97 +0x1.0000000000002p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x2000000000000500000000000 -0x2000000000000500000000000))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000002p+97 -0x1.0000000000002p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x2000000000000500000000001 +0x2000000000000500000000001))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000003p+97 +0x1.0000000000003p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x2000000000000500000000001 -0x2000000000000500000000001))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000003p+97 -0x1.0000000000003p+97)) +(module (func (export "f") (result v128) (v128.const f64x2 +1152921504606847104 +1152921504606847104))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000000p+60 +0x1.0000000000000p+60)) +(module (func (export "f") (result v128) (v128.const f64x2 -1152921504606847104 -1152921504606847104))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000000p+60 -0x1.0000000000000p+60)) +(module (func (export "f") (result v128) (v128.const f64x2 +1152921504606847105 +1152921504606847105))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000001p+60 +0x1.0000000000001p+60)) +(module (func (export "f") (result v128) (v128.const f64x2 -1152921504606847105 -1152921504606847105))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000001p+60 -0x1.0000000000001p+60)) +(module (func (export "f") (result v128) (v128.const f64x2 +1152921504606847359 +1152921504606847359))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000001p+60 +0x1.0000000000001p+60)) +(module (func (export "f") (result v128) (v128.const f64x2 -1152921504606847359 -1152921504606847359))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000001p+60 -0x1.0000000000001p+60)) +(module (func (export "f") (result v128) (v128.const f64x2 +1152921504606847360 +1152921504606847360))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000002p+60 +0x1.0000000000002p+60)) +(module (func (export "f") (result v128) (v128.const f64x2 -1152921504606847360 -1152921504606847360))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000002p+60 -0x1.0000000000002p+60)) + +;; f64x2, subnormal +(module (func (export "f") (result v128) (v128.const f64x2 +0x0.000000000000080000000000p-1022 +0x0.000000000000080000000000p-1022))) +(assert_return (invoke "f") (v128.const f64x2 +0x0.0000000000000p-1022 +0x0.0000000000000p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x0.000000000000080000000000p-1022 -0x0.000000000000080000000000p-1022))) +(assert_return (invoke "f") (v128.const f64x2 -0x0.0000000000000p-1022 -0x0.0000000000000p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x0.000000000000080000000001p-1022 +0x0.000000000000080000000001p-1022))) +(assert_return (invoke "f") (v128.const f64x2 +0x0.0000000000001p-1022 +0x0.0000000000001p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x0.000000000000080000000001p-1022 -0x0.000000000000080000000001p-1022))) +(assert_return (invoke "f") (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x0.0000000000000fffffffffffp-1022 +0x0.0000000000000fffffffffffp-1022))) +(assert_return (invoke "f") (v128.const f64x2 +0x0.0000000000001p-1022 +0x0.0000000000001p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x0.0000000000000fffffffffffp-1022 -0x0.0000000000000fffffffffffp-1022))) +(assert_return (invoke "f") (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x0.000000000000100000000000p-1022 +0x0.000000000000100000000000p-1022))) +(assert_return (invoke "f") (v128.const f64x2 +0x0.0000000000001p-1022 +0x0.0000000000001p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x0.000000000000100000000000p-1022 -0x0.000000000000100000000000p-1022))) +(assert_return (invoke "f") (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x0.000000000000100000000001p-1022 +0x0.000000000000100000000001p-1022))) +(assert_return (invoke "f") (v128.const f64x2 +0x0.0000000000001p-1022 +0x0.0000000000001p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x0.000000000000100000000001p-1022 -0x0.000000000000100000000001p-1022))) +(assert_return (invoke "f") (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x0.00000000000017ffffffffffp-1022 +0x0.00000000000017ffffffffffp-1022))) +(assert_return (invoke "f") (v128.const f64x2 +0x0.0000000000001p-1022 +0x0.0000000000001p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x0.00000000000017ffffffffffp-1022 -0x0.00000000000017ffffffffffp-1022))) +(assert_return (invoke "f") (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x0.000000000000180000000000p-1022 +0x0.000000000000180000000000p-1022))) +(assert_return (invoke "f") (v128.const f64x2 +0x0.0000000000002p-1022 +0x0.0000000000002p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x0.000000000000180000000000p-1022 -0x0.000000000000180000000000p-1022))) +(assert_return (invoke "f") (v128.const f64x2 -0x0.0000000000002p-1022 -0x0.0000000000002p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x0.000000000000180000000001p-1022 +0x0.000000000000180000000001p-1022))) +(assert_return (invoke "f") (v128.const f64x2 +0x0.0000000000002p-1022 +0x0.0000000000002p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x0.000000000000180000000001p-1022 -0x0.000000000000180000000001p-1022))) +(assert_return (invoke "f") (v128.const f64x2 -0x0.0000000000002p-1022 -0x0.0000000000002p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x0.0000000000001fffffffffffp-1022 +0x0.0000000000001fffffffffffp-1022))) +(assert_return (invoke "f") (v128.const f64x2 +0x0.0000000000002p-1022 +0x0.0000000000002p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x0.0000000000001fffffffffffp-1022 -0x0.0000000000001fffffffffffp-1022))) +(assert_return (invoke "f") (v128.const f64x2 -0x0.0000000000002p-1022 -0x0.0000000000002p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x0.000000000000200000000000p-1022 +0x0.000000000000200000000000p-1022))) +(assert_return (invoke "f") (v128.const f64x2 +0x0.0000000000002p-1022 +0x0.0000000000002p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x0.000000000000200000000000p-1022 -0x0.000000000000200000000000p-1022))) +(assert_return (invoke "f") (v128.const f64x2 -0x0.0000000000002p-1022 -0x0.0000000000002p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x0.000000000000200000000001p-1022 +0x0.000000000000200000000001p-1022))) +(assert_return (invoke "f") (v128.const f64x2 +0x0.0000000000002p-1022 +0x0.0000000000002p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x0.000000000000200000000001p-1022 -0x0.000000000000200000000001p-1022))) +(assert_return (invoke "f") (v128.const f64x2 -0x0.0000000000002p-1022 -0x0.0000000000002p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x0.00000000000027ffffffffffp-1022 +0x0.00000000000027ffffffffffp-1022))) +(assert_return (invoke "f") (v128.const f64x2 +0x0.0000000000002p-1022 +0x0.0000000000002p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x0.00000000000027ffffffffffp-1022 -0x0.00000000000027ffffffffffp-1022))) +(assert_return (invoke "f") (v128.const f64x2 -0x0.0000000000002p-1022 -0x0.0000000000002p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x0.000000000000280000000000p-1022 +0x0.000000000000280000000000p-1022))) +(assert_return (invoke "f") (v128.const f64x2 +0x0.0000000000002p-1022 +0x0.0000000000002p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x0.000000000000280000000000p-1022 -0x0.000000000000280000000000p-1022))) +(assert_return (invoke "f") (v128.const f64x2 -0x0.0000000000002p-1022 -0x0.0000000000002p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.000000000000280000000001p-1022 +0x1.000000000000280000000001p-1022))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.0000000000003p-1022 +0x1.0000000000003p-1022)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.000000000000280000000001p-1022 -0x1.000000000000280000000001p-1022))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.0000000000003p-1022 -0x1.0000000000003p-1022)) + +;; f64x2, round down at limit to infinity +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.fffffffffffff4p1023 +0x1.fffffffffffff4p1023))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.fffffffffffffp1023 +0x1.fffffffffffffp1023)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.fffffffffffff4p1023 -0x1.fffffffffffff4p1023))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.fffffffffffffp1023 -0x1.fffffffffffffp1023)) +(module (func (export "f") (result v128) (v128.const f64x2 +0x1.fffffffffffff7ffffffp1023 +0x1.fffffffffffff7ffffffp1023))) +(assert_return (invoke "f") (v128.const f64x2 +0x1.fffffffffffffp1023 +0x1.fffffffffffffp1023)) +(module (func (export "f") (result v128) (v128.const f64x2 -0x1.fffffffffffff7ffffffp1023 -0x1.fffffffffffff7ffffffp1023))) +(assert_return (invoke "f") (v128.const f64x2 -0x1.fffffffffffffp1023 -0x1.fffffffffffffp1023)) + +;; As parameters of control constructs + +(module (memory 1) + (func (export "as-br-retval") (result v128) + (block (result v128) (br 0 (v128.const i32x4 0x03020100 0x07060504 0x0b0a0908 0x0f0e0d0c))) + ) + (func (export "as-br_if-retval") (result v128) + (block (result v128) + (br_if 0 (v128.const i32x4 0 1 2 3) (i32.const 1)) + ) + ) + (func (export "as-return-retval") (result v128) + (return (v128.const i32x4 0 1 2 3)) + ) + (func (export "as-if-then-retval") (result v128) + (if (result v128) (i32.const 1) + (then (v128.const i32x4 0 1 2 3)) (else (v128.const i32x4 3 2 1 0)) + ) + ) + (func (export "as-if-else-retval") (result v128) + (if (result v128) (i32.const 0) + (then (v128.const i32x4 0 1 2 3)) (else (v128.const i32x4 3 2 1 0)) + ) + ) + (func $f (param v128 v128 v128) (result v128) (v128.const i32x4 0 1 2 3)) + (func (export "as-call-param") (result v128) + (call $f (v128.const i32x4 0 1 2 3) (v128.const i32x4 0 1 2 3) (v128.const i32x4 0 1 2 3)) + ) + (func (export "as-block-retval") (result v128) + (block (result v128) (v128.const i32x4 0 1 2 3)) + ) + (func (export "as-loop-retval") (result v128) + (loop (result v128) (v128.const i32x4 0 1 2 3)) + ) + (func (export "as-drop-operand") + (drop (v128.const i32x4 0 1 2 3)) + ) + + (func (export "as-br-retval2") (result v128) + (block (result v128) (br 0 (v128.const i64x2 0x0302010007060504 0x0b0a09080f0e0d0c))) + ) + (func (export "as-br_if-retval2") (result v128) + (block (result v128) + (br_if 0 (v128.const i64x2 0 1) (i32.const 1)) + ) + ) + (func (export "as-return-retval2") (result v128) + (return (v128.const i64x2 0 1)) + ) + (func (export "as-if-then-retval2") (result v128) + (if (result v128) (i32.const 1) + (then (v128.const i64x2 0 1)) (else (v128.const i64x2 1 0)) + ) + ) + (func (export "as-if-else-retval2") (result v128) + (if (result v128) (i32.const 0) + (then (v128.const i64x2 0 1)) (else (v128.const i64x2 1 0)) + ) + ) + (func $f2 (param v128 v128 v128) (result v128) (v128.const i64x2 0 1)) + (func (export "as-call-param2") (result v128) + (call $f2 (v128.const i64x2 0 1) (v128.const i64x2 0 1) (v128.const i64x2 0 1)) + ) + + (type $sig (func (param v128 v128 v128) (result v128))) + (table funcref (elem $f $f2)) + (func (export "as-call_indirect-param") (result v128) + (call_indirect (type $sig) + (v128.const i32x4 0 1 2 3) (v128.const i32x4 0 1 2 3) (v128.const i32x4 0 1 2 3) (i32.const 0) + ) + ) + (func (export "as-call_indirect-param2") (result v128) + (call_indirect (type $sig) + (v128.const i64x2 0 1) (v128.const i64x2 0 1) (v128.const i64x2 0 1) (i32.const 1) + ) + ) + (func (export "as-block-retval2") (result v128) + (block (result v128) (v128.const i64x2 0 1)) + ) + (func (export "as-loop-retval2") (result v128) + (loop (result v128) (v128.const i64x2 0 1)) + ) + (func (export "as-drop-operand2") + (drop (v128.const i64x2 0 1)) + ) +) + +(assert_return (invoke "as-br-retval") (v128.const i32x4 0x03020100 0x07060504 0x0b0a0908 0x0f0e0d0c)) +(assert_return (invoke "as-br_if-retval") (v128.const i32x4 0 1 2 3)) +(assert_return (invoke "as-return-retval") (v128.const i32x4 0 1 2 3)) +(assert_return (invoke "as-if-then-retval") (v128.const i32x4 0 1 2 3)) +(assert_return (invoke "as-if-else-retval") (v128.const i32x4 3 2 1 0)) +(assert_return (invoke "as-call-param") (v128.const i32x4 0 1 2 3)) +(assert_return (invoke "as-call_indirect-param") (v128.const i32x4 0 1 2 3)) +(assert_return (invoke "as-block-retval") (v128.const i32x4 0 1 2 3)) +(assert_return (invoke "as-loop-retval") (v128.const i32x4 0 1 2 3)) +(assert_return (invoke "as-drop-operand")) + +(assert_return (invoke "as-br-retval2") (v128.const i64x2 0x0302010007060504 0x0b0a09080f0e0d0c)) +(assert_return (invoke "as-br_if-retval2") (v128.const i64x2 0 1)) +(assert_return (invoke "as-return-retval2") (v128.const i64x2 0 1)) +(assert_return (invoke "as-if-then-retval2") (v128.const i64x2 0 1)) +(assert_return (invoke "as-if-else-retval2") (v128.const i64x2 1 0)) +(assert_return (invoke "as-call-param2") (v128.const i64x2 0 1)) +(assert_return (invoke "as-call_indirect-param2") (v128.const i64x2 0 1)) +(assert_return (invoke "as-block-retval2") (v128.const i64x2 0 1)) +(assert_return (invoke "as-loop-retval2") (v128.const i64x2 0 1)) +(assert_return (invoke "as-drop-operand2")) + +;; v128 locals + +(module (memory 1) + (func (export "as-local.set/get-value_0_0") (param $0 v128) (result v128) + (local v128 v128 v128 v128) + (local.set 0 (local.get $0)) + (local.get 0) + ) + (func (export "as-local.set/get-value_0_1") (param $0 v128) (result v128) + (local v128 v128 v128 v128) + (local.set 0 (local.get $0)) + (local.set 1 (local.get 0)) + (local.set 2 (local.get 1)) + (local.set 3 (local.get 2)) + (local.get 0) + ) + (func (export "as-local.set/get-value_3_0") (param $0 v128) (result v128) + (local v128 v128 v128 v128) + (local.set 0 (local.get $0)) + (local.set 1 (local.get 0)) + (local.set 2 (local.get 1)) + (local.set 3 (local.get 2)) + (local.get 3) + ) + (func (export "as-local.tee-value") (result v128) + (local v128) + (local.tee 0 (v128.const i32x4 0 1 2 3)) + ) +) + +(assert_return (invoke "as-local.set/get-value_0_0" (v128.const i32x4 0 0 0 0)) (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "as-local.set/get-value_0_1" (v128.const i32x4 1 1 1 1)) (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "as-local.set/get-value_3_0" (v128.const i32x4 2 2 2 2)) (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "as-local.tee-value") (v128.const i32x4 0 1 2 3)) + + +;; v128 globals + +(module (memory 1) + (global $g0 (mut v128) (v128.const i32x4 0 1 2 3)) + (global $g1 (mut v128) (v128.const i32x4 4 5 6 7)) + (global $g2 (mut v128) (v128.const i32x4 8 9 10 11)) + (global $g3 (mut v128) (v128.const i32x4 12 13 14 15)) + (global $g4 (mut v128) (v128.const i32x4 16 17 18 19)) + + (func $set_g0 (export "as-global.set_value_$g0") (param $0 v128) + (global.set $g0 (local.get $0)) + ) + (func $set_g1_g2 (export "as-global.set_value_$g1_$g2") (param $0 v128) (param $1 v128) + (global.set $g1 (local.get $0)) + (global.set $g2 (local.get $1)) + ) + (func $set_g0_g1_g2_g3 (export "as-global.set_value_$g0_$g1_$g2_$g3") (param $0 v128) (param $1 v128) (param $2 v128) (param $3 v128) + (call $set_g0 (local.get $0)) + (call $set_g1_g2 (local.get $1) (local.get $2)) + (global.set $g3 (local.get $3)) + ) + (func (export "global.get_g0") (result v128) + (global.get $g0) + ) + (func (export "global.get_g1") (result v128) + (global.get $g1) + ) + (func (export "global.get_g2") (result v128) + (global.get $g2) + ) + (func (export "global.get_g3") (result v128) + (global.get $g3) + ) +) + +(assert_return (invoke "as-global.set_value_$g0_$g1_$g2_$g3" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2) + (v128.const i32x4 3 3 3 3) + (v128.const i32x4 4 4 4 4))) +(assert_return (invoke "global.get_g0") (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "global.get_g1") (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "global.get_g2") (v128.const i32x4 3 3 3 3)) +(assert_return (invoke "global.get_g3") (v128.const i32x4 4 4 4 4)) + + +;; Test integer literal parsing. + +(module + (func (export "i32x4.test") (result v128) (return (v128.const i32x4 0x0bAdD00D 0x0bAdD00D 0x0bAdD00D 0x0bAdD00D))) + (func (export "i32x4.smax") (result v128) (return (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff))) + (func (export "i32x4.neg_smax") (result v128) (return (v128.const i32x4 -0x7fffffff -0x7fffffff -0x7fffffff -0x7fffffff))) + (func (export "i32x4.inc_smin") (result v128) (return (i32x4.add (v128.const i32x4 -0x80000000 -0x80000000 -0x80000000 -0x80000000) (v128.const i32x4 1 1 1 1)))) + (func (export "i32x4.neg_zero") (result v128) (return (v128.const i32x4 -0x0 -0x0 -0x0 -0x0))) + (func (export "i32x4.not_octal") (result v128) (return (v128.const i32x4 010 010 010 010))) + (func (export "i32x4.plus_sign") (result v128) (return (v128.const i32x4 +42 +42 +42 +42))) + + (func (export "i32x4-dec-sep1") (result v128) (v128.const i32x4 1_000_000 1_000_000 1_000_000 1_000_000)) + (func (export "i32x4-dec-sep2") (result v128) (v128.const i32x4 1_0_0_0 1_0_0_0 1_0_0_0 1_0_0_0)) + (func (export "i32x4-hex-sep1") (result v128) (v128.const i32x4 0xa_0f_00_99 0xa_0f_00_99 0xa_0f_00_99 0xa_0f_00_99)) + (func (export "i32x4-hex-sep2") (result v128) (v128.const i32x4 0x1_a_A_0_f 0x1_a_A_0_f 0x1_a_A_0_f 0x1_a_A_0_f)) + + (func (export "i64x2.test") (result v128) (return (v128.const i64x2 0x0bAdD00D0bAdD00D 0x0bAdD00D0bAdD00D))) + (func (export "i64x2.smax") (result v128) (return (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff))) + (func (export "i64x2.neg_smax") (result v128) (return (v128.const i64x2 -0x7fffffffffffffff -0x7fffffffffffffff))) + (func (export "i64x2.inc_smin") (result v128) (return (i64x2.add (v128.const i64x2 -0x8000000000000000 -0x8000000000000000) (v128.const i64x2 1 1)))) + (func (export "i64x2.neg_zero") (result v128) (return (v128.const i64x2 -0x0 -0x0))) + (func (export "i64x2.not_octal") (result v128) (return (v128.const i64x2 010010 010010))) + (func (export "i64x2.plus_sign") (result v128) (return (v128.const i64x2 +42 +42))) + + (func (export "i64x2-dec-sep1") (result v128) (v128.const i64x2 10_000_000_000_000 10_000_000_000_000)) + (func (export "i64x2-dec-sep2") (result v128) (v128.const i64x2 1_0_0_0_0_0_0_0 1_0_0_0_0_0_0_0)) + (func (export "i64x2-hex-sep1") (result v128) (v128.const i64x2 0xa_0f_00_99_0a_0f_00_99 0xa_0f_00_99_0a_0f_00_99)) + (func (export "i64x2-hex-sep2") (result v128) (v128.const i64x2 0x1_a_A_0_f_1_a_A_0_f 0x1_a_A_0_f_1_a_A_0_f)) +) + +(assert_return (invoke "i32x4.test") (v128.const i32x4 195940365 195940365 195940365 195940365)) +(assert_return (invoke "i32x4.smax") (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.neg_smax") (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) +(assert_return (invoke "i32x4.inc_smin") (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) +(assert_return (invoke "i32x4.neg_zero") (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.not_octal") (v128.const i32x4 10 10 10 10)) +(assert_return (invoke "i32x4.plus_sign") (v128.const i32x4 42 42 42 42)) + +(assert_return (invoke "i32x4-dec-sep1") (v128.const i32x4 1000000 1000000 1000000 1000000)) +(assert_return (invoke "i32x4-dec-sep2") (v128.const i32x4 1000 1000 1000 1000)) +(assert_return (invoke "i32x4-hex-sep1") (v128.const i32x4 0xa0f0099 0xa0f0099 0xa0f0099 0xa0f0099)) +(assert_return (invoke "i32x4-hex-sep2") (v128.const i32x4 0x1aa0f 0x1aa0f 0x1aa0f 0x1aa0f)) + +(assert_return (invoke "i64x2.test") (v128.const i64x2 841557459837243405 841557459837243405)) +(assert_return (invoke "i64x2.smax") (v128.const i64x2 9223372036854775807 9223372036854775807)) +(assert_return (invoke "i64x2.neg_smax") (v128.const i64x2 -9223372036854775807 -9223372036854775807)) +(assert_return (invoke "i64x2.inc_smin") (v128.const i64x2 -9223372036854775807 -9223372036854775807)) +(assert_return (invoke "i64x2.neg_zero") (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.not_octal") (v128.const i64x2 10010 10010)) +(assert_return (invoke "i64x2.plus_sign") (v128.const i64x2 42 42)) + +(assert_return (invoke "i64x2-dec-sep1") (v128.const i64x2 10000000000000 10000000000000)) +(assert_return (invoke "i64x2-dec-sep2") (v128.const i64x2 10000000 10000000)) +(assert_return (invoke "i64x2-hex-sep1") (v128.const i64x2 0xa0f00990a0f0099 0xa0f00990a0f0099)) +(assert_return (invoke "i64x2-hex-sep2") (v128.const i64x2 0x1aa0f1aa0f 0x1aa0f1aa0f)) + +(assert_malformed + (module quote "(global v128 (v128.const i32x4 _100 _100 _100 _100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i32x4 +_100 +_100 +_100 +_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i32x4 -_100 -_100 -_100 -_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i32x4 99_ 99_ 99_ 99_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i32x4 1__000 1__000 1__000 1__000))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i32x4 _0x100 _0x100 _0x100 _0x100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i32x4 0_x100 0_x100 0_x100 0_x100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i32x4 0x_100 0x_100 0x_100 0x_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i32x4 0x00_ 0x00_ 0x00_ 0x00_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i32x4 0xff__ffff 0xff__ffff 0xff__ffff 0xff__ffff))") + "unknown operator" +) + +(assert_malformed + (module quote "(global v128 (v128.const i64x2 _100_100 _100_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i64x2 +_100_100 +_100_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i64x2 -_100_100 -_100_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i64x2 99_99_ 99_99_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i64x2 1__000_000 1__000_000))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i64x2 _0x100000 _0x100000))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i64x2 0_x100000 0_x100000))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i64x2 0x_100000 0x_100000))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i64x2 0x00_ 0x00_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const i64x2 0xff__ffff_ffff_ffff 0xff__ffff_ffff_ffff))") + "unknown operator" +) + +;; Test floating-point literal parsing. + +(module + (func (export "f32-dec-sep1") (result v128) (v128.const f32x4 1_000_000 1_000_000 1_000_000 1_000_000)) + (func (export "f32-dec-sep2") (result v128) (v128.const f32x4 1_0_0_0 1_0_0_0 1_0_0_0 1_0_0_0)) + (func (export "f32-dec-sep3") (result v128) (v128.const f32x4 100_3.141_592 100_3.141_592 100_3.141_592 100_3.141_592)) + (func (export "f32-dec-sep4") (result v128) (v128.const f32x4 99e+1_3 99e+1_3 99e+1_3 99e+1_3)) + (func (export "f32-dec-sep5") (result v128) (v128.const f32x4 122_000.11_3_54E0_2_3 122_000.11_3_54E0_2_3 122_000.11_3_54E0_2_3 122_000.11_3_54E0_2_3)) + (func (export "f32-hex-sep1") (result v128) (v128.const f32x4 0xa_0f_00_99 0xa_0f_00_99 0xa_0f_00_99 0xa_0f_00_99)) + (func (export "f32-hex-sep2") (result v128) (v128.const f32x4 0x1_a_A_0_f 0x1_a_A_0_f 0x1_a_A_0_f 0x1_a_A_0_f)) + (func (export "f32-hex-sep3") (result v128) (v128.const f32x4 0xa0_ff.f141_a59a 0xa0_ff.f141_a59a 0xa0_ff.f141_a59a 0xa0_ff.f141_a59a)) + (func (export "f32-hex-sep4") (result v128) (v128.const f32x4 0xf0P+1_3 0xf0P+1_3 0xf0P+1_3 0xf0P+1_3)) + (func (export "f32-hex-sep5") (result v128) (v128.const f32x4 0x2a_f00a.1f_3_eep2_3 0x2a_f00a.1f_3_eep2_3 0x2a_f00a.1f_3_eep2_3 0x2a_f00a.1f_3_eep2_3)) + (func (export "f64-dec-sep1") (result v128) (v128.const f64x2 1_000_000 1_000_000)) + (func (export "f64-dec-sep2") (result v128) (v128.const f64x2 1_0_0_0 1_0_0_0)) + (func (export "f64-dec-sep3") (result v128) (v128.const f64x2 100_3.141_592 100_3.141_592)) + (func (export "f64-dec-sep4") (result v128) (v128.const f64x2 99e+1_3 99e+1_3)) + (func (export "f64-dec-sep5") (result v128) (v128.const f64x2 122_000.11_3_54E0_2_3 122_000.11_3_54E0_2_3)) + (func (export "f64-hex-sep1") (result v128) (v128.const f64x2 0xa_0f_00_99 0xa_0f_00_99)) + (func (export "f64-hex-sep2") (result v128) (v128.const f64x2 0x1_a_A_0_f 0x1_a_A_0_f)) + (func (export "f64-hex-sep3") (result v128) (v128.const f64x2 0xa0_ff.f141_a59a 0xa0_ff.f141_a59a)) + (func (export "f64-hex-sep4") (result v128) (v128.const f64x2 0xf0P+1_3 0xf0P+1_3)) + (func (export "f64-hex-sep5") (result v128) (v128.const f64x2 0x2a_f00a.1f_3_eep2_3 0x2a_f00a.1f_3_eep2_3)) +) + +(assert_return (invoke "f32-dec-sep1") (v128.const f32x4 1000000 1000000 1000000 1000000)) +(assert_return (invoke "f32-dec-sep2") (v128.const f32x4 1000 1000 1000 1000)) +(assert_return (invoke "f32-dec-sep3") (v128.const f32x4 1003.141592 1003.141592 1003.141592 1003.141592)) +(assert_return (invoke "f32-dec-sep4") (v128.const f32x4 99e+13 99e+13 99e+13 99e+13)) +(assert_return (invoke "f32-dec-sep5") (v128.const f32x4 122000.11354e23 122000.11354e23 122000.11354e23 122000.11354e23)) +(assert_return (invoke "f32-hex-sep1") (v128.const f32x4 0xa0f0099 0xa0f0099 0xa0f0099 0xa0f0099)) +(assert_return (invoke "f32-hex-sep2") (v128.const f32x4 0x1aa0f 0x1aa0f 0x1aa0f 0x1aa0f)) +(assert_return (invoke "f32-hex-sep3") (v128.const f32x4 0xa0ff.f141a59a 0xa0ff.f141a59a 0xa0ff.f141a59a 0xa0ff.f141a59a)) +(assert_return (invoke "f32-hex-sep4") (v128.const f32x4 0xf0P+13 0xf0P+13 0xf0P+13 0xf0P+13)) +(assert_return (invoke "f32-hex-sep5") (v128.const f32x4 0x2af00a.1f3eep23 0x2af00a.1f3eep23 0x2af00a.1f3eep23 0x2af00a.1f3eep23)) +(assert_return (invoke "f64-dec-sep1") (v128.const f64x2 1000000 1000000)) +(assert_return (invoke "f64-dec-sep2") (v128.const f64x2 1000 1000)) +(assert_return (invoke "f64-dec-sep3") (v128.const f64x2 1003.141592 1003.141592)) +(assert_return (invoke "f64-dec-sep4") (v128.const f64x2 99e+13 99e+13)) +(assert_return (invoke "f64-dec-sep5") (v128.const f64x2 122000.11354e23 122000.11354e23)) +(assert_return (invoke "f64-hex-sep1") (v128.const f64x2 0xa0f0099 0xa0f0099)) +(assert_return (invoke "f64-hex-sep2") (v128.const f64x2 0x1aa0f 0x1aa0f)) +(assert_return (invoke "f64-hex-sep3") (v128.const f64x2 0xa0ff.f141a59a 0xa0ff.f141a59a)) +(assert_return (invoke "f64-hex-sep4") (v128.const f64x2 0xf0P+13 0xf0P+13)) +(assert_return (invoke "f64-hex-sep5") (v128.const f64x2 0x2af00a.1f3eep23 0x2af00a.1f3eep23)) + +(assert_malformed + (module quote "(global v128 (v128.const f32x4 _100 _100 _100 _100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 +_100 +_100 +_100 +_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 -_100 -_100 -_100 -_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 99_ 99_ 99_ 99_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 1__000 1__000 1__000 1__000))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 _1.0 _1.0 _1.0 _1.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 1.0_ 1.0_ 1.0_ 1.0_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 1_.0 1_.0 1_.0 1_.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 1._0 1._0 1._0 1._0))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 _1e1 _1e1 _1e1 _1e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 1e1_ 1e1_ 1e1_ 1e1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 1_e1 1_e1 1_e1 1_e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 1e_1 1e_1 1e_1 1e_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 _1.0e1 _1.0e1 _1.0e1 _1.0e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 1.0e1_ 1.0e1_ 1.0e1_ 1.0e1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 1.0_e1 1.0_e1 1.0_e1 1.0_e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 1.0e_1 1.0e_1 1.0e_1 1.0e_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 1.0e+_1 1.0e+_1 1.0e+_1 1.0e+_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 1.0e_+1 1.0e_+1 1.0e_+1 1.0e_+1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 _0x100 _0x100 _0x100 _0x100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0_x100 0_x100 0_x100 0_x100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x_100 0x_100 0x_100 0x_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x00_ 0x00_ 0x00_ 0x00_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0xff__ffff 0xff__ffff 0xff__ffff 0xff__ffff))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x_1.0 0x_1.0 0x_1.0 0x_1.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x1.0_ 0x1.0_ 0x1.0_ 0x1.0_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x1_.0 0x1_.0 0x1_.0 0x1_.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x1._0 0x1._0 0x1._0 0x1._0))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x_1p1 0x_1p1 0x_1p1 0x_1p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x1p1_ 0x1p1_ 0x1p1_ 0x1p1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x1_p1 0x1_p1 0x1_p1 0x1_p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x1p_1 0x1p_1 0x1p_1 0x1p_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x_1.0p1 0x_1.0p1 0x_1.0p1 0x_1.0p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x1.0p1_ 0x1.0p1_ 0x1.0p1_ 0x1.0p1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x1.0_p1 0x1.0_p1 0x1.0_p1 0x1.0_p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x1.0p_1 0x1.0p_1 0x1.0p_1 0x1.0p_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x1.0p+_1 0x1.0p+_1 0x1.0p+_1 0x1.0p+_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f32x4 0x1.0p_+1 0x1.0p_+1 0x1.0p_+1 0x1.0p_+1))") + "unknown operator" +) + +(assert_malformed + (module quote "(global v128 (v128.const f64x2 _100 _100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 +_100 +_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 -_100 -_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 99_ 99_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 1__000 1__000))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 _1.0 _1.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 1.0_ 1.0_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 1_.0 1_.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 1._0 1._0))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 _1e1 _1e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 1e1_ 1e1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 1_e1 1_e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 1e_1 1e_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 _1.0e1 _1.0e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 1.0e1_ 1.0e1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 1.0_e1 1.0_e1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 1.0e_1 1.0e_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 1.0e+_1 1.0e+_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 1.0e_+1 1.0e_+1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 _0x100 _0x100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0_x100 0_x100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x_100 0x_100))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x00_ 0x00_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0xff__ffff 0xff__ffff))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x_1.0 0x_1.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x1.0_ 0x1.0_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x1_.0 0x1_.0))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x1._0 0x1._0))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x_1p1 0x_1p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x1p1_ 0x1p1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x1_p1 0x1_p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x1p_1 0x1p_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x_1.0p1 0x_1.0p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x1.0p1_ 0x1.0p1_))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x1.0_p1 0x1.0_p1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x1.0p_1 0x1.0p_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x1.0p+_1 0x1.0p+_1))") + "unknown operator" +) +(assert_malformed + (module quote "(global v128 (v128.const f64x2 0x1.0p_+1 0x1.0p_+1))") + "unknown operator" +) + +;; Test parsing an integer from binary + +(module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\00\01\7b" ;; type 0 (func) + "\03\02\01\00" ;; func section + "\07\0f\01\0b" ;; export section + "\70\61\72\73\65\5f\69\38\78\31\36\00\00" ;; export name (parse_i8x16) + "\0a\16\01" ;; code section + "\14\00\fd\0c" ;; func body + "\00\00\00\00" ;; data lane 0~3 (0, 0, 0, 0) + "\80\80\80\80" ;; data lane 4~7 (-128, -128, -128, -128) + "\ff\ff\ff\ff" ;; data lane 8~11 (0xff, 0xff, 0xff, 0xff) + "\ff\ff\ff\ff" ;; data lane 12~15 (255, 255, 255, 255) + "\0b" ;; end +) +(assert_return (invoke "parse_i8x16") (v128.const i8x16 0 0 0 0 -128 -128 -128 -128 0xff 0xff 0xff 0xff 255 255 255 255)) + +(module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\00\01\7b" ;; type 0 (func) + "\03\02\01\00" ;; func section + "\07\0f\01\0b" ;; export section + "\70\61\72\73\65\5f\69\31\36\78\38\00\00" ;; export name (parse_i16x8) + "\0a\16\01" ;; code section + "\14\00\fd\0c" ;; func body + "\00\00\00\00" ;; data lane 0, 1 (0, 0) + "\00\80\00\80" ;; data lane 2, 3 (-32768, -32768) + "\ff\ff\ff\ff" ;; data lane 4, 5 (65535, 65535) + "\ff\ff\ff\ff" ;; data lane 6, 7 (0xffff, 0xffff) + "\0b" ;; end +) +(assert_return (invoke "parse_i16x8") (v128.const i16x8 0 0 -32768 -32768 65535 65535 0xffff 0xffff)) + +(module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\00\01\7b" ;; type 0 (func) + "\03\02\01\00" ;; func section + "\07\0f\01\0b" ;; export section + "\70\61\72\73\65\5f\69\33\32\78\34\00\00" ;; export name (parse_i32x4) + "\0a\16\01" ;; code section + "\14\00\fd\0c" ;; func body + "\d1\ff\ff\ff" ;; data lane 0 (4294967249) + "\d1\ff\ff\ff" ;; data lane 1 (4294967249) + "\d1\ff\ff\ff" ;; data lane 2 (4294967249) + "\d1\ff\ff\ff" ;; data lane 3 (4294967249) + "\0b" ;; end +) +(assert_return (invoke "parse_i32x4") (v128.const i32x4 4294967249 4294967249 4294967249 4294967249)) + +(module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\00\01\7b" ;; type 0 (func) + "\03\02\01\00" ;; func section + "\07\0f\01\0b" ;; export section + "\70\61\72\73\65\5f\69\36\34\78\32\00\00" ;; export name (parse_i64x2) + "\0a\16\01" ;; code section + "\14\00\fd\0c" ;; func body + "\ff\ff\ff\ff\ff\ff\ff\7f" ;; data lane 0 (9223372036854775807) + "\ff\ff\ff\ff\ff\ff\ff\7f" ;; data lane 1 (9223372036854775807) + "\0b" ;; end +) +(assert_return (invoke "parse_i64x2") (v128.const i64x2 9223372036854775807 9223372036854775807)) + +;; Test parsing a float from binary + +(module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\00\01\7b" ;; type 0 (func) + "\03\02\01\00" ;; func section + "\07\0f\01\0b" ;; export section + "\70\61\72\73\65\5f\66\33\32\78\34\00\00" ;; export name (parse_f32x4) + "\0a\16\01" ;; code section + "\14\00\fd\0c" ;; func body + "\00\00\80\4f" ;; data lane 0 (4294967249) + "\00\00\80\4f" ;; data lane 1 (4294967249) + "\00\00\80\4f" ;; data lane 2 (4294967249) + "\00\00\80\4f" ;; data lane 3 (4294967249) + "\0b" ;; end +) +(assert_return (invoke "parse_f32x4") (v128.const f32x4 4294967249 4294967249 4294967249 4294967249)) + +(module binary + "\00asm" "\01\00\00\00" + "\01\05\01" ;; type section + "\60\00\01\7b" ;; type 0 (func) + "\03\02\01\00" ;; func section + "\07\0f\01\0b" ;; export section + "\70\61\72\73\65\5f\66\36\34\78\32\00\00" ;; export name (parse_f64x2) + "\0a\16\01" ;; code section + "\14\00\fd\0c" ;; func body + "\ff\ff\ff\ff\ff\ff\ef\7f" ;; data lane 0 (0x1.fffffffffffffp+1023) + "\ff\ff\ff\ff\ff\ff\ef\7f" ;; data lane 1 (0x1.fffffffffffffp+1023) + "\0b" ;; end +) +(assert_return (invoke "parse_f64x2") (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_conversions.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_conversions.wast new file mode 100644 index 000000000..552e94361 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_conversions.wast @@ -0,0 +1,899 @@ +;; Web Assembly SIMD-related type conversion tests + +(module + ;; Integer to floating point + (func (export "f32x4.convert_i32x4_s") (param v128) (result v128) + (f32x4.convert_i32x4_s (local.get 0))) + (func (export "f32x4.convert_i32x4_u") (param v128) (result v128) + (f32x4.convert_i32x4_u (local.get 0))) + + (func (export "f64x2.convert_low_i32x4_s") (param v128) (result v128) + (f64x2.convert_low_i32x4_s (local.get 0))) + (func (export "f64x2.convert_low_i32x4_u") (param v128) (result v128) + (f64x2.convert_low_i32x4_u (local.get 0))) + + ;; Integer to integer narrowing + (func (export "i8x16.narrow_i16x8_s") (param v128 v128) (result v128) + (i8x16.narrow_i16x8_s (local.get 0) (local.get 1))) + (func (export "i8x16.narrow_i16x8_u") (param v128 v128) (result v128) + (i8x16.narrow_i16x8_u (local.get 0) (local.get 1))) + (func (export "i16x8.narrow_i32x4_s") (param v128 v128) (result v128) + (i16x8.narrow_i32x4_s (local.get 0) (local.get 1))) + (func (export "i16x8.narrow_i32x4_u") (param v128 v128) (result v128) + (i16x8.narrow_i32x4_u (local.get 0)(local.get 1))) + + ;; Float to float promote/demote + (func (export "f64x2.promote_low_f32x4") (param v128) (result v128) + (f64x2.promote_low_f32x4 (local.get 0))) + (func (export "f32x4.demote_f64x2_zero") (param v128) (result v128) + (f32x4.demote_f64x2_zero (local.get 0))) +) + +;; f64x2.promote_low_f32x4 +;; Float constants copied from test/core/conversions.wast. + +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 0.0 0.0 0.0 0.0)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const f64x2 -0.0 -0.0)) +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f64x2 0x1p-149 0x1p-149)) +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f64x2 -0x1p-149 -0x1p-149)) +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 1.0 1.0 1.0 1.0)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 -1.0 -1.0 -1.0 -1.0)) + (v128.const f64x2 -1.0 -1.0)) +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f64x2 -0x1.fffffep+127 -0x1.fffffep+127)) +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f64x2 0x1.fffffep+127 0x1.fffffep+127)) +;; Generated randomly by picking a random int and reinterpret it to float. +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 0x1p-119 0x1p-119 0x1p-119 0x1p-119)) + (v128.const f64x2 0x1p-119 0x1p-119)) +;; Generated randomly by picking a random float. +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 0x1.8f867ep+125 0x1.8f867ep+125 0x1.8f867ep+125 0x1.8f867ep+125)) + (v128.const f64x2 6.6382536710104395e+37 6.6382536710104395e+37)) +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 inf inf inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 nan nan nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.promote_low_f32x4" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) + +;; f32x4.demote_f64x2_zero +;; Float constants copied from test/core/conversions.wast. + +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0.0 0.0)) + (v128.const f32x4 0.0 0.0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -0.0 -0.0)) + (v128.const f32x4 -0.0 -0.0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f32x4 0.0 0.0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) + (v128.const f32x4 -0.0 -0.0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 1.0 1.0)) + (v128.const f32x4 1.0 1.0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -1.0 -1.0)) + (v128.const f32x4 -1.0 -1.0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.fffffe0000000p-127 0x1.fffffe0000000p-127)) + (v128.const f32x4 0x1p-126 0x1p-126 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -0x1.fffffe0000000p-127 -0x1.fffffe0000000p-127)) + (v128.const f32x4 -0x1p-126 -0x1p-126 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.fffffdfffffffp-127 0x1.fffffdfffffffp-127)) + (v128.const f32x4 0x1.fffffcp-127 0x1.fffffcp-127 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -0x1.fffffdfffffffp-127 -0x1.fffffdfffffffp-127)) + (v128.const f32x4 -0x1.fffffcp-127 -0x1.fffffcp-127 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1p-149 0x1p-149 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1p-149 -0x1p-149 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.fffffd0000000p+127 0x1.fffffd0000000p+127)) + (v128.const f32x4 0x1.fffffcp+127 0x1.fffffcp+127 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -0x1.fffffd0000000p+127 -0x1.fffffd0000000p+127)) + (v128.const f32x4 -0x1.fffffcp+127 -0x1.fffffcp+127 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.fffffd0000001p+127 0x1.fffffd0000001p+127)) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -0x1.fffffd0000001p+127 -0x1.fffffd0000001p+127)) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.fffffefffffffp+127 0x1.fffffefffffffp+127)) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -0x1.fffffefffffffp+127 -0x1.fffffefffffffp+127)) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.ffffffp+127 0x1.ffffffp+127)) + (v128.const f32x4 inf inf 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -0x1.ffffffp+127 -0x1.ffffffp+127)) + (v128.const f32x4 -inf -inf 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1p-119 0x1p-119)) + (v128.const f32x4 0x1p-119 0x1p-119 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.8f867ep+125 0x1.8f867ep+125)) + (v128.const f32x4 0x1.8f867ep+125 0x1.8f867ep+125 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 inf inf)) + (v128.const f32x4 inf inf 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -inf -inf)) + (v128.const f32x4 -inf -inf 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.0000000000001p+0 0x1.0000000000001p+0)) + (v128.const f32x4 1.0 1.0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.fffffffffffffp-1 0x1.fffffffffffffp-1)) + (v128.const f32x4 1.0 1.0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.0000010000000p+0 0x1.0000010000000p+0)) + (v128.const f32x4 0x1.000000p+0 0x1.000000p+0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.0000010000001p+0 0x1.0000010000001p+0)) + (v128.const f32x4 0x1.000002p+0 0x1.000002p+0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.000002fffffffp+0 0x1.000002fffffffp+0)) + (v128.const f32x4 0x1.000002p+0 0x1.000002p+0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.0000030000000p+0 0x1.0000030000000p+0)) + (v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.0000050000000p+0 0x1.0000050000000p+0)) + (v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.0000010000000p+24 0x1.0000010000000p+24)) + (v128.const f32x4 0x1.0p+24 0x1.0p+24 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.0000010000001p+24 0x1.0000010000001p+24)) + (v128.const f32x4 0x1.000002p+24 0x1.000002p+24 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.000002fffffffp+24 0x1.000002fffffffp+24)) + (v128.const f32x4 0x1.000002p+24 0x1.000002p+24 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.0000030000000p+24 0x1.0000030000000p+24)) + (v128.const f32x4 0x1.000004p+24 0x1.000004p+24 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.4eae4f7024c7p+108 0x1.4eae4f7024c7p+108)) + (v128.const f32x4 0x1.4eae5p+108 0x1.4eae5p+108 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.a12e71e358685p-113 0x1.a12e71e358685p-113)) + (v128.const f32x4 0x1.a12e72p-113 0x1.a12e72p-113 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.cb98354d521ffp-127 0x1.cb98354d521ffp-127)) + (v128.const f32x4 0x1.cb9834p-127 0x1.cb9834p-127 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -0x1.6972b30cfb562p+1 -0x1.6972b30cfb562p+1)) + (v128.const f32x4 -0x1.6972b4p+1 -0x1.6972b4p+1 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -0x1.bedbe4819d4c4p+112 -0x1.bedbe4819d4c4p+112)) + (v128.const f32x4 -0x1.bedbe4p+112 -0x1.bedbe4p+112 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 nan nan)) + (v128.const f32x4 nan:canonical nan:canonical 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f32x4 0.0 0.0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f32x4 -0.0 -0.0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.0p-150 0x1.0p-150)) + (v128.const f32x4 0.0 0.0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -0x1.0p-150 -0x1.0p-150)) + (v128.const f32x4 -0.0 -0.0 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 0x1.0000000000001p-150 0x1.0000000000001p-150)) + (v128.const f32x4 0x1p-149 0x1p-149 0 0)) +(assert_return (invoke "f32x4.demote_f64x2_zero" (v128.const f64x2 -0x1.0000000000001p-150 -0x1.0000000000001p-150)) + (v128.const f32x4 -0x1p-149 -0x1p-149 0 0)) + + +;; Integer to floating point +;; f32x4.convert_i32x4_s + +(assert_return (invoke "f32x4.convert_i32x4_s" (v128.const i32x4 0 0 0 0)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.convert_i32x4_s" (v128.const i32x4 1 1 1 1)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.convert_i32x4_s" (v128.const i32x4 -1 -1 -1 -1)) + (v128.const f32x4 -1.0 -1.0 -1.0 -1.0)) +(assert_return (invoke "f32x4.convert_i32x4_s" (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const f32x4 2147483647.0 2147483647.0 2147483647.0 2147483647.0)) +(assert_return (invoke "f32x4.convert_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const f32x4 -2147483648.0 -2147483648.0 -2147483648.0 -2147483648.0)) +(assert_return (invoke "f32x4.convert_i32x4_s" (v128.const i32x4 1234567890 1234567890 1234567890 1234567890)) + (v128.const f32x4 0x1.26580cp+30 0x1.26580cp+30 0x1.26580cp+30 0x1.26580cp+30)) +(assert_return (invoke "f32x4.convert_i32x4_s" (v128.const i32x4 0_123_456_792 0_123_456_792 0_123_456_792 0_123_456_792)) + (v128.const f32x4 123456792.0 123456792.0 123456792.0 123456792.0)) +(assert_return (invoke "f32x4.convert_i32x4_s" (v128.const i32x4 0x0_1234_5680 0x0_1234_5680 0x0_1234_5680 0x0_1234_5680)) + (v128.const f32x4 305419904.0 305419904.0 305419904.0 305419904.0)) +;; Test rounding directions. + +(assert_return (invoke "f32x4.convert_i32x4_s" (v128.const i32x4 16777217 16777217 16777217 16777217)) + (v128.const f32x4 16777216.0 16777216.0 16777216.0 16777216.0)) +(assert_return (invoke "f32x4.convert_i32x4_s" (v128.const i32x4 -16777217 -16777217 -16777217 -16777217)) + (v128.const f32x4 -16777216.0 -16777216.0 -16777216.0 -16777216.0)) +(assert_return (invoke "f32x4.convert_i32x4_s" (v128.const i32x4 16777219 16777219 16777219 16777219)) + (v128.const f32x4 16777220.0 16777220.0 16777220.0 16777220.0)) +(assert_return (invoke "f32x4.convert_i32x4_s" (v128.const i32x4 -16777219 -16777219 -16777219 -16777219)) + (v128.const f32x4 -16777220.0 -16777220.0 -16777220.0 -16777220.0)) +(assert_return (invoke "f32x4.convert_i32x4_s" (v128.const i32x4 0 -1 0x7fffffff 0x80000000)) + (v128.const f32x4 0.0 -1.0 2147483647.0 -2147483648.0)) + +;; f32x4.convert_i32x4_u + +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 0 0 0 0)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 1 1 1 1)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 -1 -1 -1 -1)) + (v128.const f32x4 4294967295.0 4294967295.0 4294967295.0 4294967295.0)) +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const f32x4 2147483648.0 2147483648.0 2147483648.0 2147483648.0)) +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const f32x4 2147483648.0 2147483648.0 2147483648.0 2147483648.0)) +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 0x12345678 0x12345678 0x12345678 0x12345678)) + (v128.const f32x4 0x1.234568p+28 0x1.234568p+28 0x1.234568p+28 0x1.234568p+28)) +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 0x80000080 0x80000080 0x80000080 0x80000080)) + (v128.const f32x4 0x1.000000p+31 0x1.000000p+31 0x1.000000p+31 0x1.000000p+31)) +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 0x80000081 0x80000081 0x80000081 0x80000081)) + (v128.const f32x4 0x1.000002p+31 0x1.000002p+31 0x1.000002p+31 0x1.000002p+31)) +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 0x80000082 0x80000082 0x80000082 0x80000082)) + (v128.const f32x4 0x1.000002p+31 0x1.000002p+31 0x1.000002p+31 0x1.000002p+31)) +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 0xfffffe80 0xfffffe80 0xfffffe80 0xfffffe80)) + (v128.const f32x4 0x1.fffffcp+31 0x1.fffffcp+31 0x1.fffffcp+31 0x1.fffffcp+31)) +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 0xfffffe81 0xfffffe81 0xfffffe81 0xfffffe81)) + (v128.const f32x4 0x1.fffffep+31 0x1.fffffep+31 0x1.fffffep+31 0x1.fffffep+31)) +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 0xfffffe82 0xfffffe82 0xfffffe82 0xfffffe82)) + (v128.const f32x4 0x1.fffffep+31 0x1.fffffep+31 0x1.fffffep+31 0x1.fffffep+31)) +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 0_123_456_792 0_123_456_792 0_123_456_792 0_123_456_792)) + (v128.const f32x4 123456792.0 123456792.0 123456792.0 123456792.0)) +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef)) + (v128.const f32x4 2427178496.0 2427178496.0 2427178496.0 2427178496.0)) +;; Test rounding directions. + +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 16777217 16777217 16777217 16777217)) + (v128.const f32x4 16777216.0 16777216.0 16777216.0 16777216.0)) +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 16777219 16777219 16777219 16777219)) + (v128.const f32x4 16777220.0 16777220.0 16777220.0 16777220.0)) +(assert_return (invoke "f32x4.convert_i32x4_u" (v128.const i32x4 0 -1 0x7fffffff 0x80000000)) + (v128.const f32x4 0.0 4294967295.0 2147483647.0 2147483648.0)) + +;; f64x2.convert_i32x4_s +;; constants copied from test/core/conversions.wast. + +(assert_return (invoke "f64x2.convert_low_i32x4_s" (v128.const i32x4 1 1 0 0)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.convert_low_i32x4_s" (v128.const i32x4 -1 -1 0 0)) + (v128.const f64x2 -1.0 -1.0)) +(assert_return (invoke "f64x2.convert_low_i32x4_s" (v128.const i32x4 0 0 0 0)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.convert_low_i32x4_s" (v128.const i32x4 2147483647 2147483647 0 0)) + (v128.const f64x2 2147483647 2147483647)) +(assert_return (invoke "f64x2.convert_low_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 0 0)) + (v128.const f64x2 -2147483648 -2147483648)) +(assert_return (invoke "f64x2.convert_low_i32x4_s" (v128.const i32x4 987654321 987654321 0 0)) + (v128.const f64x2 987654321 987654321)) + +;; f64x2.convert_i32x4_u +;; constants copied from test/core/conversions.wast. + +(assert_return (invoke "f64x2.convert_low_i32x4_u" (v128.const i32x4 1 1 0 0)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.convert_low_i32x4_u" (v128.const i32x4 0 0 0 0)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.convert_low_i32x4_u" (v128.const i32x4 2147483647 2147483647 0 0)) + (v128.const f64x2 2147483647 2147483647)) +(assert_return (invoke "f64x2.convert_low_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 0 0)) + (v128.const f64x2 2147483648 2147483648)) +(assert_return (invoke "f64x2.convert_low_i32x4_u" (v128.const i32x4 0xffffffff 0xffffffff 0 0)) + (v128.const f64x2 4294967295.0 4294967295.0)) + +;; Integer to integer narrowing +;; i8x16.narrow_i16x8_s + +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e) + (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i16x8 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e)) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i16x8 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f) + (v128.const i16x8 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80)) + (v128.const i8x16 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80) + (v128.const i16x8 -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80) + (v128.const i16x8 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81) + (v128.const i16x8 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81) + (v128.const i16x8 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80) + (v128.const i16x8 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80) + (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80) + (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81) + (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80) + (v128.const i16x8 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81 -0x81) + (v128.const i16x8 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 056_789 056_789 056_789 056_789 056_789 056_789 056_789 056_789)) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.narrow_i16x8_s" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678)) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +;; i8x16.narrow_i16x8_u + +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e) + (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i16x8 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e)) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e 0x7e)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i16x8 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe) + (v128.const i16x8 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i16x8 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe)) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i16x8 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100) + (v128.const i16x8 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100)) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i16x8 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100)) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100) + (v128.const i16x8 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 056_789 056_789 056_789 056_789 056_789 056_789 056_789 056_789) + (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) +(assert_return (invoke "i8x16.narrow_i16x8_u" (v128.const i16x8 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB) + (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) +;; i16x8.narrow_i32x4_s + +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1)) + (v128.const i16x8 0 0 0 0 1 1 1 1)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 1 1 1 1 0 0 0 0)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 0 0 0 0)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 1 1 1 1)) + (v128.const i16x8 -1 -1 -1 -1 1 1 1 1)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 0x7ffe 0x7ffe 0x7ffe 0x7ffe) + (v128.const i32x4 0x7fff 0x7fff 0x7fff 0x7fff)) + (v128.const i16x8 0x7ffe 0x7ffe 0x7ffe 0x7ffe 0x7fff 0x7fff 0x7fff 0x7fff)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i32x4 0x7ffe 0x7ffe 0x7ffe 0x7ffe)) + (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7ffe 0x7ffe 0x7ffe 0x7ffe)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i32x4 0x7fff 0x7fff 0x7fff 0x7fff)) + (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 0x8000 0x8000 0x8000 0x8000) + (v128.const i32x4 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i32x4 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 0x8000 0x8000 0x8000 0x8000) + (v128.const i32x4 0x7fff 0x7fff 0x7fff 0x7fff)) + (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i32x4 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 0xffff 0xffff 0xffff 0xffff) + (v128.const i32x4 0x7fff 0x7fff 0x7fff 0x7fff)) + (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 -0x7fff -0x7fff -0x7fff -0x7fff) + (v128.const i32x4 -0x8000 -0x8000 -0x8000 -0x8000)) + (v128.const i16x8 0x8001 0x8001 0x8001 0x8001 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 -0x8000 -0x8000 -0x8000 -0x8000) + (v128.const i32x4 -0x7fff -0x7fff -0x7fff -0x7fff)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8001 0x8001 0x8001 0x8001)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 -0x8000 -0x8000 -0x8000 -0x8000) + (v128.const i32x4 -0x8000 -0x8000 -0x8000 -0x8000)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 -0x8001 -0x8001 -0x8001 -0x8001) + (v128.const i32x4 -0x8001 -0x8001 -0x8001 -0x8001)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 -0x8001 -0x8001 -0x8001 -0x8001) + (v128.const i32x4 -0x8000 -0x8000 -0x8000 -0x8000)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 -0x8000 -0x8000 -0x8000 -0x8000) + (v128.const i32x4 -0x8001 -0x8001 -0x8001 -0x8001)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 -0x8000 -0x8000 -0x8000 -0x8000) + (v128.const i32x4 0x7fff 0x7fff 0x7fff 0x7fff)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x7fff 0x7fff 0x7fff 0x7fff)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 -0x8000 -0x8000 -0x8000 -0x8000) + (v128.const i32x4 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x7fff 0x7fff 0x7fff 0x7fff)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 -0x8001 -0x8001 -0x8001 -0x8001) + (v128.const i32x4 0x7fff 0x7fff 0x7fff 0x7fff)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x7fff 0x7fff 0x7fff 0x7fff)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 -0x8000 -0x8000 -0x8000 -0x8000) + (v128.const i32x4 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x7fff 0x7fff 0x7fff 0x7fff)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 -0x8001 -0x8001 -0x8001 -0x8001) + (v128.const i32x4 0x10000 0x10000 0x10000 0x10000)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x7fff 0x7fff 0x7fff 0x7fff)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 -0x8000000 -0x8000000 -0x8000000 -0x8000000) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 0_123_456_789 0_123_456_789 0_123_456_789 0_123_456_789) + (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890)) + (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) +(assert_return (invoke "i16x8.narrow_i32x4_s" (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef) + (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x7fff 0x7fff 0x7fff 0x7fff)) +;; i16x8.narrow_i32x4_u + +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1)) + (v128.const i16x8 0 0 0 0 1 1 1 1)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 1 1 1 1 0 0 0 0)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 0 0 0 0)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 1 1 1 1)) + (v128.const i16x8 0 0 0 0 1 1 1 1)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 0xfffe 0xfffe 0xfffe 0xfffe) + (v128.const i32x4 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0xfffe 0xfffe 0xfffe 0xfffe 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 0xffff 0xffff 0xffff 0xffff) + (v128.const i32x4 0xfffe 0xfffe 0xfffe 0xfffe)) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xfffe 0xfffe 0xfffe 0xfffe)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 0xffff 0xffff 0xffff 0xffff) + (v128.const i32x4 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 0x10000 0x10000 0x10000 0x10000) + (v128.const i32x4 0x10000 0x10000 0x10000 0x10000)) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 0xffff 0xffff 0xffff 0xffff) + (v128.const i32x4 0x10000 0x10000 0x10000 0x10000)) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 0x10000 0x10000 0x10000 0x10000) + (v128.const i32x4 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0 0 0 0 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0x10000 0x10000 0x10000 0x10000)) + (v128.const i16x8 0 0 0 0 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0 0 0 0 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 0x10000 0x10000 0x10000 0x10000)) + (v128.const i16x8 0 0 0 0 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 -0x80000000 -0x80000000 -0x80000000 -0x80000000) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 0_123_456_789 0_123_456_789 0_123_456_789 0_123_456_789) + (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890)) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "i16x8.narrow_i32x4_u" (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef) + (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678)) + (v128.const i16x8 0 0 0 0 0xffff 0xffff 0xffff 0xffff)) + + +;; Unknown operator + +(assert_malformed (module quote + "(func (result v128) (i32x4.trunc_sat_f32x4 (v128.const f32x4 0.0 0.0 0.0 0.0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i32x4.trunc_s_sat_f32x4 (v128.const f32x4 -2.0 -1.0 1.0 2.0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i32x4.trunc_u_sat_f32x4 (v128.const f32x4 -2.0 -1.0 1.0 2.0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i32x4.convert_f32x4 (v128.const f32x4 -1 0 1 2)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i32x4.convert_s_f32x4 (v128.const f32x4 -1 0 1 2)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i32x4.convert_u_f32x4 (v128.const f32x4 -1 0 1 2)))") + "unknown operator") + +(assert_malformed (module quote + "(func (result v128) (i64x2.trunc_sat_f64x2_s (v128.const f64x2 0.0 0.0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i64x2.trunc_sat_f64x2_u (v128.const f64x2 -2.0 -1.0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (f64x2.convert_i64x2_s (v128.const i64x2 1 2)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (f64x2.convert_i64x2_u (v128.const i64x2 1 2)))") + "unknown operator") + +(assert_malformed (module quote + "(func (result v128) (i8x16.narrow_i16x8 (v128.const i16x8 0 0 0 0 0 0 0 0) (v128.const i16x8 0 0 0 0 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i16x8.narrow_i8x16 (v128.const i16x8 0 0 0 0 0 0 0 0) (v128.const i16x8 0 0 0 0 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i16x8.narrow_i8x16_s (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i16x8.narrow_i8x16_u (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i16x8.narrow_i32x4 (v128.const i16x8 0 0 0 0 0 0 0 0) (v128.const i16x8 0 0 0 0 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i32x4.narrow_i16x8 (v128.const i16x8 0 0 0 0 0 0 0 0) (v128.const i16x8 0 0 0 0 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i32x4.narrow_i16x8_s (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i32x4.narrow_i16x8_u (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") + "unknown operator") + +(assert_malformed (module quote + "(func (result v128) (i16x8.extend_low_i8x16 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i8x16.extend_low_i16x8_s (v128.const i16x8 0 0 0 0 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i8x16.extend_low_i16x8_u (v128.const i16x8 0 0 0 0 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i16x8.extend_high_i8x16 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i8x16.extend_high_i16x8_s (v128.const i16x8 0 0 0 0 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i8x16.extend_high_i16x8_u (v128.const i16x8 0 0 0 0 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i32x4.extend_low_i16x8 (v128.const i16x8 0 0 0 0 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i16x8.extend_low_i32x4_s (v128.const i32x4 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i16x8.extend_low_i32x4_u (v128.const i32x4 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i32x4.extend_high_i16x8 (v128.const i16x8 0 0 0 0 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i16x8.extend_high_i32x4_s (v128.const i32x4 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i16x8.extend_high_i32x4_u (v128.const i32x4 0 0 0 0)))") + "unknown operator") + + +;; Type mismatch + +(assert_invalid (module (func (result v128) (f32x4.convert_i32x4_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.convert_i32x4_s (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.convert_i32x4_u (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.convert_i32x4_u (i64.const 0)))) "type mismatch") + +(assert_invalid (module (func (result v128) (i8x16.narrow_i16x8_s (i32.const 0) (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.narrow_i16x8_u (i32.const 0) (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.narrow_i32x4_s (f32.const 0.0) (f64.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.narrow_i32x4_s (f32.const 0.0) (f64.const 0.0)))) "type mismatch") + + +;; Combinations + +(module + (func (export "f32x4_convert_i32x4_s_add") (param v128 v128) (result v128) + (f32x4.convert_i32x4_s (i32x4.add (local.get 0) (local.get 1)))) + (func (export "f32x4_convert_i32x4_s_sub") (param v128 v128) (result v128) + (f32x4.convert_i32x4_s (i32x4.sub (local.get 0) (local.get 1)))) + (func (export "f32x4_convert_i32x4_u_mul") (param v128 v128) (result v128) + (f32x4.convert_i32x4_u (i32x4.mul (local.get 0) (local.get 1)))) + + (func (export "i16x8_low_extend_narrow_ss") (param v128 v128) (result v128) + (i16x8.extend_low_i8x16_s (i8x16.narrow_i16x8_s (local.get 0) (local.get 1)))) + (func (export "i16x8_low_extend_narrow_su") (param v128 v128) (result v128) + (i16x8.extend_low_i8x16_s (i8x16.narrow_i16x8_u (local.get 0) (local.get 1)))) + (func (export "i16x8_high_extend_narrow_ss") (param v128 v128) (result v128) + (i16x8.extend_low_i8x16_s (i8x16.narrow_i16x8_s (local.get 0) (local.get 1)))) + (func (export "i16x8_high_extend_narrow_su") (param v128 v128) (result v128) + (i16x8.extend_low_i8x16_s (i8x16.narrow_i16x8_u (local.get 0) (local.get 1)))) + (func (export "i16x8_low_extend_narrow_uu") (param v128 v128) (result v128) + (i16x8.extend_low_i8x16_u (i8x16.narrow_i16x8_u (local.get 0) (local.get 1)))) + (func (export "i16x8_low_extend_narrow_us") (param v128 v128) (result v128) + (i16x8.extend_low_i8x16_u (i8x16.narrow_i16x8_s (local.get 0) (local.get 1)))) + (func (export "i16x8_high_extend_narrow_uu") (param v128 v128) (result v128) + (i16x8.extend_low_i8x16_u (i8x16.narrow_i16x8_u (local.get 0) (local.get 1)))) + (func (export "i16x8_high_extend_narrow_us") (param v128 v128) (result v128) + (i16x8.extend_low_i8x16_u (i8x16.narrow_i16x8_s (local.get 0) (local.get 1)))) + + (func (export "i32x4_low_extend_narrow_ss") (param v128 v128) (result v128) + (i32x4.extend_low_i16x8_s (i16x8.narrow_i32x4_s (local.get 0) (local.get 1)))) + (func (export "i32x4_low_extend_narrow_su") (param v128 v128) (result v128) + (i32x4.extend_low_i16x8_s (i16x8.narrow_i32x4_u (local.get 0) (local.get 1)))) + (func (export "i32x4_high_extend_narrow_ss") (param v128 v128) (result v128) + (i32x4.extend_low_i16x8_s (i16x8.narrow_i32x4_s (local.get 0) (local.get 1)))) + (func (export "i32x4_high_extend_narrow_su") (param v128 v128) (result v128) + (i32x4.extend_low_i16x8_s (i16x8.narrow_i32x4_u (local.get 0) (local.get 1)))) + (func (export "i32x4_low_extend_narrow_uu") (param v128 v128) (result v128) + (i32x4.extend_low_i16x8_u (i16x8.narrow_i32x4_u (local.get 0) (local.get 1)))) + (func (export "i32x4_low_extend_narrow_us") (param v128 v128) (result v128) + (i32x4.extend_low_i16x8_u (i16x8.narrow_i32x4_s (local.get 0) (local.get 1)))) + (func (export "i32x4_high_extend_narrow_uu") (param v128 v128) (result v128) + (i32x4.extend_low_i16x8_u (i16x8.narrow_i32x4_u (local.get 0) (local.get 1)))) + (func (export "i32x4_high_extend_narrow_us") (param v128 v128) (result v128) + (i32x4.extend_low_i16x8_u (i16x8.narrow_i32x4_s (local.get 0) (local.get 1)))) +) + +(assert_return (invoke "f32x4_convert_i32x4_s_add" (v128.const i32x4 1 2 3 4) + (v128.const i32x4 2 3 4 5)) + (v128.const f32x4 3.0 5.0 7.0 9.0)) +(assert_return (invoke "f32x4_convert_i32x4_s_sub" (v128.const i32x4 0 1 2 3) + (v128.const i32x4 1 1 1 1)) + (v128.const f32x4 -1.0 0.0 1.0 2.0)) +(assert_return (invoke "f32x4_convert_i32x4_u_mul" (v128.const i32x4 1 2 3 4) + (v128.const i32x4 1 2 3 4)) + (v128.const f32x4 1.0 4.0 9.0 16.0)) + +(assert_return (invoke "i16x8_low_extend_narrow_ss" (v128.const i16x8 -0x8000 -0x7fff 0x7fff 0x8000 -0x8000 -0x7fff 0x7fff 0x8000) + (v128.const i16x8 -0x8000 -0x7fff 0x7fff 0x8000 -0x8000 -0x7fff 0x7fff 0x8000)) + (v128.const i16x8 0xff80 0xff80 0x7f 0xff80 0xff80 0xff80 0x7f 0xff80)) +(assert_return (invoke "i16x8_low_extend_narrow_su" (v128.const i16x8 -0x8000 -0x7fff 0x7fff 0xffff -0x8000 -0x7fff 0x7fff 0xffff) + (v128.const i16x8 -0x8000 -0x7fff 0x7fff 0xffff -0x8000 -0x7fff 0x7fff 0xffff)) + (v128.const i16x8 0 0 0xffff 0 0 0 0xffff 0)) +(assert_return (invoke "i16x8_high_extend_narrow_ss" (v128.const i16x8 -0x8000 -0x7fff 0x7fff 0x8000 -0x8000 -0x7fff 0x7fff 0x8000) + (v128.const i16x8 -0x8000 -0x7fff 0x7fff 0x8000 -0x8000 -0x7fff 0x7fff 0x8000)) + (v128.const i16x8 0xff80 0xff80 0x7f 0xff80 0xff80 0xff80 0x7f 0xff80)) +(assert_return (invoke "i16x8_high_extend_narrow_su" (v128.const i16x8 -0x8000 -0x7fff 0x7fff 0xffff -0x8000 -0x7fff 0x7fff 0xffff) + (v128.const i16x8 -0x8000 -0x7fff 0x7fff 0xffff -0x8000 -0x7fff 0x7fff 0xffff)) + (v128.const i16x8 0 0 0xffff 0 0 0 0xffff 0)) +(assert_return (invoke "i16x8_low_extend_narrow_uu" (v128.const i16x8 -0x8000 -0x7fff 0x8000 0xffff -0x8000 -0x7fff 0x8000 0xffff) + (v128.const i16x8 -0x8000 -0x7fff 0x8000 0xffff -0x8000 -0x7fff 0x8000 0xffff)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8_low_extend_narrow_us" (v128.const i16x8 -0x8000 -0x7fff 0x7fff 0x8000 -0x8000 -0x7fff 0x7fff 0x8000) + (v128.const i16x8 -0x8000 -0x7fff 0x7fff 0x8000 -0x8000 -0x7fff 0x7fff 0x8000)) + (v128.const i16x8 0x80 0x80 0x7f 0x80 0x80 0x80 0x7f 0x80)) +(assert_return (invoke "i16x8_high_extend_narrow_uu" (v128.const i16x8 -0x8000 -0x7fff 0x7fff 0xffff -0x8000 -0x7fff 0x7fff 0xffff) + (v128.const i16x8 -0x8000 -0x7fff 0x7fff 0xffff -0x8000 -0x7fff 0x7fff 0xffff)) + (v128.const i16x8 0 0 0xff 0 0 0 0xff 0)) +(assert_return (invoke "i16x8_high_extend_narrow_us" (v128.const i16x8 -0x8000 -0x7fff 0x7fff 0x8000 -0x8000 -0x7fff 0x7fff 0x8000) + (v128.const i16x8 -0x8000 -0x7fff 0x7fff 0x8000 -0x8000 -0x7fff 0x7fff 0x8000)) + (v128.const i16x8 0x80 0x80 0x7f 0x80 0x80 0x80 0x7f 0x80)) + +(assert_return (invoke "i32x4_low_extend_narrow_ss" (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0x8000000) + (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0x8000000)) + (v128.const i32x4 0xffff8000 0xffff8000 0x7fff 0x7fff)) +(assert_return (invoke "i32x4_low_extend_narrow_su" (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0xffffffff) + (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0xffffffff)) + (v128.const i32x4 0 0 0xffffffff 0)) +(assert_return (invoke "i32x4_high_extend_narrow_ss" (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0x8000000) + (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0x8000000)) + (v128.const i32x4 0xffff8000 0xffff8000 0x7fff 0x7fff)) +(assert_return (invoke "i32x4_high_extend_narrow_su" (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0xffffffff) + (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0xffffffff)) + (v128.const i32x4 0 0 0xffffffff 0)) +(assert_return (invoke "i32x4_low_extend_narrow_uu" (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0xffffffff) + (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0xffffffff)) + (v128.const i32x4 0 0 0xffff 0)) +(assert_return (invoke "i32x4_low_extend_narrow_us" (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0x8000000) + (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0x8000000)) + (v128.const i32x4 0x8000 0x8000 0x7fff 0x7fff)) +(assert_return (invoke "i32x4_high_extend_narrow_uu" (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0xffffffff) + (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0xffffffff)) + (v128.const i32x4 0 0 0xffff 0)) +(assert_return (invoke "i32x4_high_extend_narrow_us" (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0x8000000) + (v128.const i32x4 -0x80000000 -0x7fffffff 0x7fffffff 0x8000000)) + (v128.const i32x4 0x8000 0x8000 0x7fff 0x7fff)) + +;; Test operation with empty argument + +(assert_invalid + (module + (func $f32x4.convert_i32x4_s-arg-empty (result v128) + (f32x4.convert_i32x4_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.convert_i32x4_u-arg-empty (result v128) + (f32x4.convert_i32x4_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.narrow_i16x8_s-1st-arg-empty (result v128) + (i8x16.narrow_i16x8_s (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.narrow_i16x8_s-arg-empty (result v128) + (i8x16.narrow_i16x8_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.narrow_i16x8_u-1st-arg-empty (result v128) + (i8x16.narrow_i16x8_u (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.narrow_i16x8_u-arg-empty (result v128) + (i8x16.narrow_i16x8_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.narrow_i32x4_s-1st-arg-empty (result v128) + (i16x8.narrow_i32x4_s (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.narrow_i32x4_s-arg-empty (result v128) + (i16x8.narrow_i32x4_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.narrow_i32x4_u-1st-arg-empty (result v128) + (i16x8.narrow_i32x4_u (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.narrow_i32x4_u-arg-empty (result v128) + (i16x8.narrow_i32x4_u) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f32x4.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f32x4.wast new file mode 100644 index 000000000..819db8cfd --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f32x4.wast @@ -0,0 +1,2407 @@ +;; Tests for f32x4 [abs, min, max] operations on major boundary values and all special values. + + +(module + (func (export "f32x4.min") (param v128 v128) (result v128) (f32x4.min (local.get 0) (local.get 1))) + (func (export "f32x4.max") (param v128 v128) (result v128) (f32x4.max (local.get 0) (local.get 1))) + (func (export "f32x4.abs") (param v128) (result v128) (f32x4.abs (local.get 0))) + ;; f32x4.min const vs const + (func (export "f32x4.min_with_const_0") (result v128) (f32x4.min (v128.const f32x4 0 1 2 -3) (v128.const f32x4 0 2 1 3))) + (func (export "f32x4.min_with_const_1") (result v128) (f32x4.min (v128.const f32x4 0 1 2 3) (v128.const f32x4 0 1 2 3))) + (func (export "f32x4.min_with_const_2") (result v128) (f32x4.min (v128.const f32x4 0x00 0x01 0x02 0x80000000) (v128.const f32x4 0x00 0x02 0x01 2147483648))) + (func (export "f32x4.min_with_const_3") (result v128) (f32x4.min (v128.const f32x4 0x00 0x01 0x02 0x80000000) (v128.const f32x4 0x00 0x01 0x02 0x80000000))) + ;; f32x4.min param vs const + (func (export "f32x4.min_with_const_5")(param v128) (result v128) (f32x4.min (local.get 0) (v128.const f32x4 0 1 2 -3))) + (func (export "f32x4.min_with_const_6")(param v128) (result v128) (f32x4.min (v128.const f32x4 0 1 2 3) (local.get 0))) + (func (export "f32x4.min_with_const_7")(param v128) (result v128) (f32x4.min (v128.const f32x4 0x00 0x01 0x02 0x80000000) (local.get 0))) + (func (export "f32x4.min_with_const_8")(param v128) (result v128) (f32x4.min (local.get 0) (v128.const f32x4 0x00 0x01 0x02 0x80000000))) + ;; f32x4.max const vs const + (func (export "f32x4.max_with_const_10") (result v128) (f32x4.max (v128.const f32x4 0 1 2 -3) (v128.const f32x4 0 2 1 3))) + (func (export "f32x4.max_with_const_11") (result v128) (f32x4.max (v128.const f32x4 0 1 2 3) (v128.const f32x4 0 1 2 3))) + (func (export "f32x4.max_with_const_12") (result v128) (f32x4.max (v128.const f32x4 0x00 0x01 0x02 0x80000000) (v128.const f32x4 0x00 0x02 0x01 2147483648))) + (func (export "f32x4.max_with_const_13") (result v128) (f32x4.max (v128.const f32x4 0x00 0x01 0x02 0x80000000) (v128.const f32x4 0x00 0x01 0x02 0x80000000))) + ;; f32x4.max param vs const + (func (export "f32x4.max_with_const_15")(param v128) (result v128) (f32x4.max (local.get 0) (v128.const f32x4 0 1 2 -3))) + (func (export "f32x4.max_with_const_16")(param v128) (result v128) (f32x4.max (v128.const f32x4 0 1 2 3) (local.get 0))) + (func (export "f32x4.max_with_const_17")(param v128) (result v128) (f32x4.max (v128.const f32x4 0x00 0x01 0x02 0x80000000) (local.get 0))) + (func (export "f32x4.max_with_const_18")(param v128) (result v128) (f32x4.max (local.get 0) (v128.const f32x4 0x00 0x01 0x02 0x80000000))) + + (func (export "f32x4.abs_with_const") (result v128) (f32x4.abs (v128.const f32x4 -0 -1 -2 -3))) +) + +;; f32x4.min const vs const +(assert_return (invoke "f32x4.min_with_const_0") (v128.const f32x4 0 1 1 -3)) +(assert_return (invoke "f32x4.min_with_const_1") (v128.const f32x4 0 1 2 3)) +(assert_return (invoke "f32x4.min_with_const_2") (v128.const f32x4 0x00 0x01 0x01 0x80000000)) +(assert_return (invoke "f32x4.min_with_const_3") (v128.const f32x4 0x00 0x01 0x02 0x80000000)) +;; f32x4.min param vs const +(assert_return (invoke "f32x4.min_with_const_5" (v128.const f32x4 0 2 1 3)) + (v128.const f32x4 0 1 1 -3)) +(assert_return (invoke "f32x4.min_with_const_6" (v128.const f32x4 0 1 2 3)) + (v128.const f32x4 0 1 2 3)) +(assert_return (invoke "f32x4.min_with_const_7" (v128.const f32x4 0x00 0x02 0x01 2147483648)) + (v128.const f32x4 0x00 0x01 0x01 0x80000000)) +(assert_return (invoke "f32x4.min_with_const_8" (v128.const f32x4 0x00 0x01 0x02 0x80000000)) + (v128.const f32x4 0x00 0x01 0x02 0x80000000)) +;; f32x4.max const vs const +(assert_return (invoke "f32x4.max_with_const_10") (v128.const f32x4 0 2 2 3)) +(assert_return (invoke "f32x4.max_with_const_11") (v128.const f32x4 0 1 2 3)) +(assert_return (invoke "f32x4.max_with_const_12") (v128.const f32x4 0x00 0x02 0x02 2147483648)) +(assert_return (invoke "f32x4.max_with_const_13") (v128.const f32x4 0x00 0x01 0x02 0x80000000)) +;; f32x4.max param vs const +(assert_return (invoke "f32x4.max_with_const_15" (v128.const f32x4 0 2 1 3)) + (v128.const f32x4 0 2 2 3)) +(assert_return (invoke "f32x4.max_with_const_16" (v128.const f32x4 0 1 2 3)) + (v128.const f32x4 0 1 2 3)) +(assert_return (invoke "f32x4.max_with_const_17" (v128.const f32x4 0x00 0x02 0x01 2147483648)) + (v128.const f32x4 0x00 0x02 0x02 2147483648)) +(assert_return (invoke "f32x4.max_with_const_18" (v128.const f32x4 0x00 0x01 0x02 0x80000000)) + (v128.const f32x4 0x00 0x01 0x02 0x80000000)) + +(assert_return (invoke "f32x4.abs_with_const") (v128.const f32x4 0 1 2 3)) + +;; Test different lanes go through different if-then clauses +;; f32x4.min +(assert_return + (invoke "f32x4.min" + (v128.const f32x4 nan 0 0 1) + (v128.const f32x4 0 -nan 1 0) + ) + (v128.const f32x4 nan:canonical nan:canonical 0 0) +) +;; f32x4.min +(assert_return + (invoke "f32x4.min" + (v128.const f32x4 nan 0 0 0) + (v128.const f32x4 0 -nan 1 0) + ) + (v128.const f32x4 nan:canonical nan:canonical 0 0) +) +;; f32x4.max +(assert_return + (invoke "f32x4.max" + (v128.const f32x4 nan 0 0 1) + (v128.const f32x4 0 -nan 1 0) + ) + (v128.const f32x4 nan:canonical nan:canonical 1 1) +) +;; f32x4.max +(assert_return + (invoke "f32x4.max" + (v128.const f32x4 nan 0 0 0) + (v128.const f32x4 0 -nan 1 0) + ) + (v128.const f32x4 nan:canonical nan:canonical 1 0) +) + +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) + (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) + +;; Test opposite signs of zero +(assert_return (invoke "f32x4.min" (v128.const f32x4 0 0 -0 +0) + (v128.const f32x4 +0 -0 +0 -0)) + (v128.const f32x4 0 -0 -0 -0)) +(assert_return (invoke "f32x4.min" (v128.const f32x4 -0 -0 -0 -0) + (v128.const f32x4 +0 +0 +0 +0)) + (v128.const f32x4 -0 -0 -0 -0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 0 0 -0 +0) + (v128.const f32x4 +0 -0 +0 -0)) + (v128.const f32x4 0 0 0 0)) +(assert_return (invoke "f32x4.max" (v128.const f32x4 -0 -0 -0 -0) + (v128.const f32x4 +0 +0 +0 +0)) + (v128.const f32x4 +0 +0 +0 +0)) + + +(assert_return (invoke "f32x4.abs" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.abs" (v128.const f32x4 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789 -0123456789.0123456789)) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + + +;; Unknown operators + +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.min (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.max (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.min (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.max (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.min (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.max (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.min (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.max (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") + +;; type check +(assert_invalid (module (func (result v128) (f32x4.abs (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.min (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.max (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $f32x4.abs-arg-empty (result v128) + (f32x4.abs) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.min-1st-arg-empty (result v128) + (f32x4.min (v128.const f32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.min-arg-empty (result v128) + (f32x4.min) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.max-1st-arg-empty (result v128) + (f32x4.max (v128.const f32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.max-arg-empty (result v128) + (f32x4.max) + ) + ) + "type mismatch" +) + +;; combination +(module + (func (export "max-min") (param v128 v128 v128) (result v128) + (f32x4.max (f32x4.min (local.get 0) (local.get 1))(local.get 2))) + (func (export "min-max") (param v128 v128 v128) (result v128) + (f32x4.min (f32x4.max (local.get 0) (local.get 1))(local.get 2))) + (func (export "max-abs") (param v128 v128) (result v128) + (f32x4.max (f32x4.abs (local.get 0)) (local.get 1))) + (func (export "min-abs") (param v128 v128) (result v128) + (f32x4.min (f32x4.abs (local.get 0)) (local.get 1))) +) + +(assert_return (invoke "max-min" (v128.const f32x4 1.125 1.125 1.125 1.125) + (v128.const f32x4 0.25 0.25 0.25 0.25) + (v128.const f32x4 0.125 0.125 0.125 0.125)) + (v128.const f32x4 0.25 0.25 0.25 0.25)) +(assert_return (invoke "min-max" (v128.const f32x4 1.125 1.125 1.125 1.125) + (v128.const f32x4 0.25 0.25 0.25 0.25) + (v128.const f32x4 0.125 0.125 0.125 0.125)) + (v128.const f32x4 0.125 0.125 0.125 0.125)) +(assert_return (invoke "max-abs" (v128.const f32x4 -1.125 -1.125 -1.125 -1.125) + (v128.const f32x4 0.125 0.125 0.125 0.125)) + (v128.const f32x4 1.125 1.125 1.125 1.125)) +(assert_return (invoke "min-abs" (v128.const f32x4 -1.125 -1.125 -1.125 -1.125) + (v128.const f32x4 0.125 0.125 0.125 0.125)) + (v128.const f32x4 0.125 0.125 0.125 0.125)) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f32x4_arith.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f32x4_arith.wast new file mode 100644 index 000000000..56f80f547 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f32x4_arith.wast @@ -0,0 +1,5476 @@ +;; Tests for f32x4 arithmetic operations on major boundary values and all special values. + + +(module + (func (export "f32x4.add") (param v128 v128) (result v128) (f32x4.add (local.get 0) (local.get 1))) + (func (export "f32x4.sub") (param v128 v128) (result v128) (f32x4.sub (local.get 0) (local.get 1))) + (func (export "f32x4.mul") (param v128 v128) (result v128) (f32x4.mul (local.get 0) (local.get 1))) + (func (export "f32x4.div") (param v128 v128) (result v128) (f32x4.div (local.get 0) (local.get 1))) + (func (export "f32x4.neg") (param v128) (result v128) (f32x4.neg (local.get 0))) + (func (export "f32x4.sqrt") (param v128) (result v128) (f32x4.sqrt (local.get 0))) +) + +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-148 0x1.0000000000000p-148 0x1.0000000000000p-148 0x1.0000000000000p-148)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000020000000p-126 0x1.0000020000000p-126 0x1.0000020000000p-126 0x1.0000020000000p-126)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.fffffc0000000p-127 -0x1.fffffc0000000p-127 -0x1.fffffc0000000p-127 -0x1.fffffc0000000p-127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-148 -0x1.0000000000000p-148 -0x1.0000000000000p-148 -0x1.0000000000000p-148)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.fffffc0000000p-127 0x1.fffffc0000000p-127 0x1.fffffc0000000p-127 0x1.fffffc0000000p-127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000020000000p-126 -0x1.0000020000000p-126 -0x1.0000020000000p-126 -0x1.0000020000000p-126)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000020000000p-126 0x1.0000020000000p-126 0x1.0000020000000p-126 0x1.0000020000000p-126)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.fffffc0000000p-127 0x1.fffffc0000000p-127 0x1.fffffc0000000p-127 0x1.fffffc0000000p-127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-125 0x1.0000000000000p-125 0x1.0000000000000p-125 0x1.0000000000000p-125)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.fffffc0000000p-127 -0x1.fffffc0000000p-127 -0x1.fffffc0000000p-127 -0x1.fffffc0000000p-127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000020000000p-126 -0x1.0000020000000p-126 -0x1.0000020000000p-126 -0x1.0000020000000p-126)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-125 -0x1.0000000000000p-125 -0x1.0000000000000p-125 -0x1.0000000000000p-125)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.8000000000000p+0 0x1.8000000000000p+0 0x1.8000000000000p+0 0x1.8000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.b21fb60000000p+2 0x1.b21fb60000000p+2 0x1.b21fb60000000p+2 0x1.b21fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.721fb60000000p+2 -0x1.721fb60000000p+2 -0x1.721fb60000000p+2 -0x1.721fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.8000000000000p+0 -0x1.8000000000000p+0 -0x1.8000000000000p+0 -0x1.8000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.721fb60000000p+2 0x1.721fb60000000p+2 0x1.721fb60000000p+2 0x1.721fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.b21fb60000000p+2 -0x1.b21fb60000000p+2 -0x1.b21fb60000000p+2 -0x1.b21fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.8000000000000p+0 0x1.8000000000000p+0 0x1.8000000000000p+0 0x1.8000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+1 0x1.0000000000000p+1 0x1.0000000000000p+1 0x1.0000000000000p+1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.d21fb60000000p+2 0x1.d21fb60000000p+2 0x1.d21fb60000000p+2 0x1.d21fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.521fb60000000p+2 -0x1.521fb60000000p+2 -0x1.521fb60000000p+2 -0x1.521fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.8000000000000p+0 -0x1.8000000000000p+0 -0x1.8000000000000p+0 -0x1.8000000000000p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+1 -0x1.0000000000000p+1 -0x1.0000000000000p+1 -0x1.0000000000000p+1)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.521fb60000000p+2 0x1.521fb60000000p+2 0x1.521fb60000000p+2 0x1.521fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.d21fb60000000p+2 -0x1.d21fb60000000p+2 -0x1.d21fb60000000p+2 -0x1.d21fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.b21fb60000000p+2 0x1.b21fb60000000p+2 0x1.b21fb60000000p+2 0x1.b21fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.721fb60000000p+2 0x1.721fb60000000p+2 0x1.721fb60000000p+2 0x1.721fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.d21fb60000000p+2 0x1.d21fb60000000p+2 0x1.d21fb60000000p+2 0x1.d21fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.521fb60000000p+2 0x1.521fb60000000p+2 0x1.521fb60000000p+2 0x1.521fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+3 0x1.921fb60000000p+3 0x1.921fb60000000p+3 0x1.921fb60000000p+3)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.721fb60000000p+2 -0x1.721fb60000000p+2 -0x1.721fb60000000p+2 -0x1.721fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.b21fb60000000p+2 -0x1.b21fb60000000p+2 -0x1.b21fb60000000p+2 -0x1.b21fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.521fb60000000p+2 -0x1.521fb60000000p+2 -0x1.521fb60000000p+2 -0x1.521fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.d21fb60000000p+2 -0x1.d21fb60000000p+2 -0x1.d21fb60000000p+2 -0x1.d21fb60000000p+2)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+3 -0x1.921fb60000000p+3 -0x1.921fb60000000p+3 -0x1.921fb60000000p+3)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 246913578.0 246913578.0 246913578.0 246913578.0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 2.46913578e+27 2.46913578e+27 2.46913578e+27 2.46913578e+27)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 2.46913578e+27 2.46913578e+27 2.46913578e+27 2.46913578e+27)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 2.46913578e-11 2.46913578e-11 2.46913578e-11 2.46913578e-11)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 246913578.0 246913578.0 246913578.0 246913578.0)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 2.46913578e+27 2.46913578e+27 2.46913578e+27 2.46913578e+27)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 2.46913578e+27 2.46913578e+27 2.46913578e+27 2.46913578e+27)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 2.46913578e-11 2.46913578e-11 2.46913578e-11 2.46913578e-11)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 246913578.02469134 246913578.02469134 246913578.02469134 246913578.02469134)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 2.4691357802469137e+27 2.4691357802469137e+27 2.4691357802469137e+27 2.4691357802469137e+27)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 2.4691357802469137e+27 2.4691357802469137e+27 2.4691357802469137e+27 2.4691357802469137e+27)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 2.4691357802469137e-11 2.4691357802469137e-11 2.4691357802469137e-11 2.4691357802469137e-11)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+57 0x1.23456789abcdfp+57 0x1.23456789abcdfp+57 0x1.23456789abcdfp+57)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+38 0x1.23456789abcdfp+38 0x1.23456789abcdfp+38 0x1.23456789abcdfp+38)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+57 0x1.23456789abcdfp+57 0x1.23456789abcdfp+57 0x1.23456789abcdfp+57)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+38 0x1.23456789abcdfp+38 0x1.23456789abcdfp+38 0x1.23456789abcdfp+38)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+57 0x1.23456789abcdfp+57 0x1.23456789abcdfp+57 0x1.23456789abcdfp+57)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76 0x1.23456789abcdfp+76)) +(assert_return (invoke "f32x4.add" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+38 0x1.23456789abcdfp+38 0x1.23456789abcdfp+38 0x1.23456789abcdfp+38)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-148 0x1.0000000000000p-148 0x1.0000000000000p-148 0x1.0000000000000p-148)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.fffffc0000000p-127 -0x1.fffffc0000000p-127 -0x1.fffffc0000000p-127 -0x1.fffffc0000000p-127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000020000000p-126 0x1.0000020000000p-126 0x1.0000020000000p-126 0x1.0000020000000p-126)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-148 -0x1.0000000000000p-148 -0x1.0000000000000p-148 -0x1.0000000000000p-148)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000020000000p-126 -0x1.0000020000000p-126 -0x1.0000020000000p-126 -0x1.0000020000000p-126)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.fffffc0000000p-127 0x1.fffffc0000000p-127 0x1.fffffc0000000p-127 0x1.fffffc0000000p-127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.fffffc0000000p-127 0x1.fffffc0000000p-127 0x1.fffffc0000000p-127 0x1.fffffc0000000p-127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000020000000p-126 0x1.0000020000000p-126 0x1.0000020000000p-126 0x1.0000020000000p-126)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-125 0x1.0000000000000p-125 0x1.0000000000000p-125 0x1.0000000000000p-125)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000020000000p-126 -0x1.0000020000000p-126 -0x1.0000020000000p-126 -0x1.0000020000000p-126)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.fffffc0000000p-127 -0x1.fffffc0000000p-127 -0x1.fffffc0000000p-127 -0x1.fffffc0000000p-127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-125 -0x1.0000000000000p-125 -0x1.0000000000000p-125 -0x1.0000000000000p-125)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.8000000000000p+0 0x1.8000000000000p+0 0x1.8000000000000p+0 0x1.8000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.721fb60000000p+2 -0x1.721fb60000000p+2 -0x1.721fb60000000p+2 -0x1.721fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.b21fb60000000p+2 0x1.b21fb60000000p+2 0x1.b21fb60000000p+2 0x1.b21fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.8000000000000p+0 -0x1.8000000000000p+0 -0x1.8000000000000p+0 -0x1.8000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.b21fb60000000p+2 -0x1.b21fb60000000p+2 -0x1.b21fb60000000p+2 -0x1.b21fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.721fb60000000p+2 0x1.721fb60000000p+2 0x1.721fb60000000p+2 0x1.721fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.8000000000000p+0 0x1.8000000000000p+0 0x1.8000000000000p+0 0x1.8000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+1 0x1.0000000000000p+1 0x1.0000000000000p+1 0x1.0000000000000p+1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.521fb60000000p+2 -0x1.521fb60000000p+2 -0x1.521fb60000000p+2 -0x1.521fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.d21fb60000000p+2 0x1.d21fb60000000p+2 0x1.d21fb60000000p+2 0x1.d21fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.8000000000000p+0 -0x1.8000000000000p+0 -0x1.8000000000000p+0 -0x1.8000000000000p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+1 -0x1.0000000000000p+1 -0x1.0000000000000p+1 -0x1.0000000000000p+1)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.d21fb60000000p+2 -0x1.d21fb60000000p+2 -0x1.d21fb60000000p+2 -0x1.d21fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.521fb60000000p+2 0x1.521fb60000000p+2 0x1.521fb60000000p+2 0x1.521fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.721fb60000000p+2 0x1.721fb60000000p+2 0x1.721fb60000000p+2 0x1.721fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.b21fb60000000p+2 0x1.b21fb60000000p+2 0x1.b21fb60000000p+2 0x1.b21fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.521fb60000000p+2 0x1.521fb60000000p+2 0x1.521fb60000000p+2 0x1.521fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.d21fb60000000p+2 0x1.d21fb60000000p+2 0x1.d21fb60000000p+2 0x1.d21fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+3 0x1.921fb60000000p+3 0x1.921fb60000000p+3 0x1.921fb60000000p+3)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.b21fb60000000p+2 -0x1.b21fb60000000p+2 -0x1.b21fb60000000p+2 -0x1.b21fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.721fb60000000p+2 -0x1.721fb60000000p+2 -0x1.721fb60000000p+2 -0x1.721fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.d21fb60000000p+2 -0x1.d21fb60000000p+2 -0x1.d21fb60000000p+2 -0x1.d21fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.521fb60000000p+2 -0x1.521fb60000000p+2 -0x1.521fb60000000p+2 -0x1.521fb60000000p+2)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+3 -0x1.921fb60000000p+3 -0x1.921fb60000000p+3 -0x1.921fb60000000p+3)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sub" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-298 0x1.0000000000000p-298 0x1.0000000000000p-298 0x1.0000000000000p-298)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-298 -0x1.0000000000000p-298 -0x1.0000000000000p-298 -0x1.0000000000000p-298)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-275 0x1.0000000000000p-275 0x1.0000000000000p-275 0x1.0000000000000p-275)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-275 -0x1.0000000000000p-275 -0x1.0000000000000p-275 -0x1.0000000000000p-275)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-150 0x1.0000000000000p-150 0x1.0000000000000p-150 0x1.0000000000000p-150)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-150 -0x1.0000000000000p-150 -0x1.0000000000000p-150 -0x1.0000000000000p-150)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p-147 0x1.921fb60000000p-147 0x1.921fb60000000p-147 0x1.921fb60000000p-147)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p-147 -0x1.921fb60000000p-147 -0x1.921fb60000000p-147 -0x1.921fb60000000p-147)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p-22 0x1.fffffe0000000p-22 0x1.fffffe0000000p-22 0x1.fffffe0000000p-22)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p-22 -0x1.fffffe0000000p-22 -0x1.fffffe0000000p-22 -0x1.fffffe0000000p-22)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-298 -0x1.0000000000000p-298 -0x1.0000000000000p-298 -0x1.0000000000000p-298)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-298 0x1.0000000000000p-298 0x1.0000000000000p-298 0x1.0000000000000p-298)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-275 -0x1.0000000000000p-275 -0x1.0000000000000p-275 -0x1.0000000000000p-275)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-275 0x1.0000000000000p-275 0x1.0000000000000p-275 0x1.0000000000000p-275)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-150 -0x1.0000000000000p-150 -0x1.0000000000000p-150 -0x1.0000000000000p-150)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-150 0x1.0000000000000p-150 0x1.0000000000000p-150 0x1.0000000000000p-150)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p-147 -0x1.921fb60000000p-147 -0x1.921fb60000000p-147 -0x1.921fb60000000p-147)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p-147 0x1.921fb60000000p-147 0x1.921fb60000000p-147 0x1.921fb60000000p-147)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p-22 -0x1.fffffe0000000p-22 -0x1.fffffe0000000p-22 -0x1.fffffe0000000p-22)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p-22 0x1.fffffe0000000p-22 0x1.fffffe0000000p-22 0x1.fffffe0000000p-22)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-275 0x1.0000000000000p-275 0x1.0000000000000p-275 0x1.0000000000000p-275)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-275 -0x1.0000000000000p-275 -0x1.0000000000000p-275 -0x1.0000000000000p-275)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-252 0x1.0000000000000p-252 0x1.0000000000000p-252 0x1.0000000000000p-252)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-252 -0x1.0000000000000p-252 -0x1.0000000000000p-252 -0x1.0000000000000p-252)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-127 0x1.0000000000000p-127 0x1.0000000000000p-127 0x1.0000000000000p-127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-127 -0x1.0000000000000p-127 -0x1.0000000000000p-127 -0x1.0000000000000p-127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p-124 0x1.921fb60000000p-124 0x1.921fb60000000p-124 0x1.921fb60000000p-124)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p-124 -0x1.921fb60000000p-124 -0x1.921fb60000000p-124 -0x1.921fb60000000p-124)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+1 0x1.fffffe0000000p+1 0x1.fffffe0000000p+1 0x1.fffffe0000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+1 -0x1.fffffe0000000p+1 -0x1.fffffe0000000p+1 -0x1.fffffe0000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-275 -0x1.0000000000000p-275 -0x1.0000000000000p-275 -0x1.0000000000000p-275)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-275 0x1.0000000000000p-275 0x1.0000000000000p-275 0x1.0000000000000p-275)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-252 -0x1.0000000000000p-252 -0x1.0000000000000p-252 -0x1.0000000000000p-252)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-252 0x1.0000000000000p-252 0x1.0000000000000p-252 0x1.0000000000000p-252)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-127 -0x1.0000000000000p-127 -0x1.0000000000000p-127 -0x1.0000000000000p-127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-127 0x1.0000000000000p-127 0x1.0000000000000p-127 0x1.0000000000000p-127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p-124 -0x1.921fb60000000p-124 -0x1.921fb60000000p-124 -0x1.921fb60000000p-124)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p-124 0x1.921fb60000000p-124 0x1.921fb60000000p-124 0x1.921fb60000000p-124)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+1 -0x1.fffffe0000000p+1 -0x1.fffffe0000000p+1 -0x1.fffffe0000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+1 0x1.fffffe0000000p+1 0x1.fffffe0000000p+1 0x1.fffffe0000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-150 0x1.0000000000000p-150 0x1.0000000000000p-150 0x1.0000000000000p-150)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-150 -0x1.0000000000000p-150 -0x1.0000000000000p-150 -0x1.0000000000000p-150)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-127 0x1.0000000000000p-127 0x1.0000000000000p-127 0x1.0000000000000p-127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-127 -0x1.0000000000000p-127 -0x1.0000000000000p-127 -0x1.0000000000000p-127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-2 0x1.0000000000000p-2 0x1.0000000000000p-2 0x1.0000000000000p-2)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-2 -0x1.0000000000000p-2 -0x1.0000000000000p-2 -0x1.0000000000000p-2)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+1 0x1.921fb60000000p+1 0x1.921fb60000000p+1 0x1.921fb60000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+1 -0x1.921fb60000000p+1 -0x1.921fb60000000p+1 -0x1.921fb60000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+126 0x1.fffffe0000000p+126 0x1.fffffe0000000p+126 0x1.fffffe0000000p+126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+126 -0x1.fffffe0000000p+126 -0x1.fffffe0000000p+126 -0x1.fffffe0000000p+126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-150 -0x1.0000000000000p-150 -0x1.0000000000000p-150 -0x1.0000000000000p-150)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-150 0x1.0000000000000p-150 0x1.0000000000000p-150 0x1.0000000000000p-150)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-127 -0x1.0000000000000p-127 -0x1.0000000000000p-127 -0x1.0000000000000p-127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-127 0x1.0000000000000p-127 0x1.0000000000000p-127 0x1.0000000000000p-127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-2 -0x1.0000000000000p-2 -0x1.0000000000000p-2 -0x1.0000000000000p-2)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-2 0x1.0000000000000p-2 0x1.0000000000000p-2 0x1.0000000000000p-2)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+1 -0x1.921fb60000000p+1 -0x1.921fb60000000p+1 -0x1.921fb60000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+1 0x1.921fb60000000p+1 0x1.921fb60000000p+1 0x1.921fb60000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+126 -0x1.fffffe0000000p+126 -0x1.fffffe0000000p+126 -0x1.fffffe0000000p+126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+126 0x1.fffffe0000000p+126 0x1.fffffe0000000p+126 0x1.fffffe0000000p+126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.921fb60000000p-147 0x1.921fb60000000p-147 0x1.921fb60000000p-147 0x1.921fb60000000p-147)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.921fb60000000p-147 -0x1.921fb60000000p-147 -0x1.921fb60000000p-147 -0x1.921fb60000000p-147)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.921fb60000000p-124 0x1.921fb60000000p-124 0x1.921fb60000000p-124 0x1.921fb60000000p-124)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.921fb60000000p-124 -0x1.921fb60000000p-124 -0x1.921fb60000000p-124 -0x1.921fb60000000p-124)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.921fb60000000p+1 0x1.921fb60000000p+1 0x1.921fb60000000p+1 0x1.921fb60000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.921fb60000000p+1 -0x1.921fb60000000p+1 -0x1.921fb60000000p+1 -0x1.921fb60000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.3bd3cdc2cab20p+5 0x1.3bd3cdc2cab20p+5 0x1.3bd3cdc2cab20p+5 0x1.3bd3cdc2cab20p+5)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.3bd3cdc2cab20p+5 -0x1.3bd3cdc2cab20p+5 -0x1.3bd3cdc2cab20p+5 -0x1.3bd3cdc2cab20p+5)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.921fb60000000p-147 -0x1.921fb60000000p-147 -0x1.921fb60000000p-147 -0x1.921fb60000000p-147)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.921fb60000000p-147 0x1.921fb60000000p-147 0x1.921fb60000000p-147 0x1.921fb60000000p-147)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.921fb60000000p-124 -0x1.921fb60000000p-124 -0x1.921fb60000000p-124 -0x1.921fb60000000p-124)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.921fb60000000p-124 0x1.921fb60000000p-124 0x1.921fb60000000p-124 0x1.921fb60000000p-124)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.921fb60000000p+1 -0x1.921fb60000000p+1 -0x1.921fb60000000p+1 -0x1.921fb60000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.921fb60000000p+1 0x1.921fb60000000p+1 0x1.921fb60000000p+1 0x1.921fb60000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.3bd3cdc2cab20p+5 -0x1.3bd3cdc2cab20p+5 -0x1.3bd3cdc2cab20p+5 -0x1.3bd3cdc2cab20p+5)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.3bd3cdc2cab20p+5 0x1.3bd3cdc2cab20p+5 0x1.3bd3cdc2cab20p+5 0x1.3bd3cdc2cab20p+5)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.fffffe0000000p-22 0x1.fffffe0000000p-22 0x1.fffffe0000000p-22 0x1.fffffe0000000p-22)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.fffffe0000000p-22 -0x1.fffffe0000000p-22 -0x1.fffffe0000000p-22 -0x1.fffffe0000000p-22)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.fffffe0000000p+1 0x1.fffffe0000000p+1 0x1.fffffe0000000p+1 0x1.fffffe0000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.fffffe0000000p+1 -0x1.fffffe0000000p+1 -0x1.fffffe0000000p+1 -0x1.fffffe0000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.fffffe0000000p+126 0x1.fffffe0000000p+126 0x1.fffffe0000000p+126 0x1.fffffe0000000p+126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.fffffe0000000p+126 -0x1.fffffe0000000p+126 -0x1.fffffe0000000p+126 -0x1.fffffe0000000p+126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.fffffe0000000p-22 -0x1.fffffe0000000p-22 -0x1.fffffe0000000p-22 -0x1.fffffe0000000p-22)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.fffffe0000000p-22 0x1.fffffe0000000p-22 0x1.fffffe0000000p-22 0x1.fffffe0000000p-22)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.fffffe0000000p+1 -0x1.fffffe0000000p+1 -0x1.fffffe0000000p+1 -0x1.fffffe0000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.fffffe0000000p+1 0x1.fffffe0000000p+1 0x1.fffffe0000000p+1 0x1.fffffe0000000p+1)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.fffffe0000000p+126 -0x1.fffffe0000000p+126 -0x1.fffffe0000000p+126 -0x1.fffffe0000000p+126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.fffffe0000000p+126 0x1.fffffe0000000p+126 0x1.fffffe0000000p+126 0x1.fffffe0000000p+126)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 1.5241579434344448e+16 1.5241579434344448e+16 1.5241579434344448e+16 1.5241579434344448e+16)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 1.5241579025420272e-22 1.5241579025420272e-22 1.5241579025420272e-22 1.5241579025420272e-22)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 1.5241579434344448e+16 1.5241579434344448e+16 1.5241579434344448e+16 1.5241579434344448e+16)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 1.5241579025420272e-22 1.5241579025420272e-22 1.5241579025420272e-22 1.5241579025420272e-22)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 1.5241579434344448e+16 1.5241579434344448e+16 1.5241579434344448e+16 1.5241579434344448e+16)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 1.5241579025420272e-22 1.5241579025420272e-22 1.5241579025420272e-22 1.5241579025420272e-22)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.4b66de0000000p+112 0x1.4b66de0000000p+112 0x1.4b66de0000000p+112 0x1.4b66de0000000p+112)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.4b66de0000000p+74 0x1.4b66de0000000p+74 0x1.4b66de0000000p+74 0x1.4b66de0000000p+74)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.4b66de0000000p+112 0x1.4b66de0000000p+112 0x1.4b66de0000000p+112 0x1.4b66de0000000p+112)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.4b66de0000000p+74 0x1.4b66de0000000p+74 0x1.4b66de0000000p+74 0x1.4b66de0000000p+74)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.4b66de0000000p+112 0x1.4b66de0000000p+112 0x1.4b66de0000000p+112 0x1.4b66de0000000p+112)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.mul" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.4b66de0000000p+74 0x1.4b66de0000000p+74 0x1.4b66de0000000p+74 0x1.4b66de0000000p+74)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-23 0x1.0000000000000p-23 0x1.0000000000000p-23 0x1.0000000000000p-23)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-23 -0x1.0000000000000p-23 -0x1.0000000000000p-23 -0x1.0000000000000p-23)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-148 0x1.0000000000000p-148 0x1.0000000000000p-148 0x1.0000000000000p-148)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-148 -0x1.0000000000000p-148 -0x1.0000000000000p-148 -0x1.0000000000000p-148)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.45f306446f9b4p-152 0x1.45f306446f9b4p-152 0x1.45f306446f9b4p-152 0x1.45f306446f9b4p-152)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.45f306446f9b4p-152 -0x1.45f306446f9b4p-152 -0x1.45f306446f9b4p-152 -0x1.45f306446f9b4p-152)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.0000010000010p-277 0x1.0000010000010p-277 0x1.0000010000010p-277 0x1.0000010000010p-277)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000010000010p-277 -0x1.0000010000010p-277 -0x1.0000010000010p-277 -0x1.0000010000010p-277)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-23 -0x1.0000000000000p-23 -0x1.0000000000000p-23 -0x1.0000000000000p-23)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-23 0x1.0000000000000p-23 0x1.0000000000000p-23 0x1.0000000000000p-23)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-148 -0x1.0000000000000p-148 -0x1.0000000000000p-148 -0x1.0000000000000p-148)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-148 0x1.0000000000000p-148 0x1.0000000000000p-148 0x1.0000000000000p-148)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.45f306446f9b4p-152 -0x1.45f306446f9b4p-152 -0x1.45f306446f9b4p-152 -0x1.45f306446f9b4p-152)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.45f306446f9b4p-152 0x1.45f306446f9b4p-152 0x1.45f306446f9b4p-152 0x1.45f306446f9b4p-152)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000010000010p-277 -0x1.0000010000010p-277 -0x1.0000010000010p-277 -0x1.0000010000010p-277)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.0000010000010p-277 0x1.0000010000010p-277 0x1.0000010000010p-277 0x1.0000010000010p-277)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p+23 0x1.0000000000000p+23 0x1.0000000000000p+23 0x1.0000000000000p+23)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p+23 -0x1.0000000000000p+23 -0x1.0000000000000p+23 -0x1.0000000000000p+23)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-125 0x1.0000000000000p-125 0x1.0000000000000p-125 0x1.0000000000000p-125)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-125 -0x1.0000000000000p-125 -0x1.0000000000000p-125 -0x1.0000000000000p-125)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.45f306446f9b4p-129 0x1.45f306446f9b4p-129 0x1.45f306446f9b4p-129 0x1.45f306446f9b4p-129)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.45f306446f9b4p-129 -0x1.45f306446f9b4p-129 -0x1.45f306446f9b4p-129 -0x1.45f306446f9b4p-129)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.0000010000010p-254 0x1.0000010000010p-254 0x1.0000010000010p-254 0x1.0000010000010p-254)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000010000010p-254 -0x1.0000010000010p-254 -0x1.0000010000010p-254 -0x1.0000010000010p-254)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p+23 -0x1.0000000000000p+23 -0x1.0000000000000p+23 -0x1.0000000000000p+23)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p+23 0x1.0000000000000p+23 0x1.0000000000000p+23 0x1.0000000000000p+23)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-125 -0x1.0000000000000p-125 -0x1.0000000000000p-125 -0x1.0000000000000p-125)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-125 0x1.0000000000000p-125 0x1.0000000000000p-125 0x1.0000000000000p-125)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.45f306446f9b4p-129 -0x1.45f306446f9b4p-129 -0x1.45f306446f9b4p-129 -0x1.45f306446f9b4p-129)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.45f306446f9b4p-129 0x1.45f306446f9b4p-129 0x1.45f306446f9b4p-129 0x1.45f306446f9b4p-129)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000010000010p-254 -0x1.0000010000010p-254 -0x1.0000010000010p-254 -0x1.0000010000010p-254)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.0000010000010p-254 0x1.0000010000010p-254 0x1.0000010000010p-254 0x1.0000010000010p-254)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p+125 0x1.0000000000000p+125 0x1.0000000000000p+125 0x1.0000000000000p+125)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p+125 -0x1.0000000000000p+125 -0x1.0000000000000p+125 -0x1.0000000000000p+125)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.45f306446f9b4p-4 0x1.45f306446f9b4p-4 0x1.45f306446f9b4p-4 0x1.45f306446f9b4p-4)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.45f306446f9b4p-4 -0x1.45f306446f9b4p-4 -0x1.45f306446f9b4p-4 -0x1.45f306446f9b4p-4)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.0000010000010p-129 0x1.0000010000010p-129 0x1.0000010000010p-129 0x1.0000010000010p-129)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000010000010p-129 -0x1.0000010000010p-129 -0x1.0000010000010p-129 -0x1.0000010000010p-129)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p+125 -0x1.0000000000000p+125 -0x1.0000000000000p+125 -0x1.0000000000000p+125)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p+125 0x1.0000000000000p+125 0x1.0000000000000p+125 0x1.0000000000000p+125)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.45f306446f9b4p-4 -0x1.45f306446f9b4p-4 -0x1.45f306446f9b4p-4 -0x1.45f306446f9b4p-4)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.45f306446f9b4p-4 0x1.45f306446f9b4p-4 0x1.45f306446f9b4p-4 0x1.45f306446f9b4p-4)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000010000010p-129 -0x1.0000010000010p-129 -0x1.0000010000010p-129 -0x1.0000010000010p-129)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.0000010000010p-129 0x1.0000010000010p-129 0x1.0000010000010p-129 0x1.0000010000010p-129)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p+126 0x1.0000000000000p+126 0x1.0000000000000p+126 0x1.0000000000000p+126)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p+126 -0x1.0000000000000p+126 -0x1.0000000000000p+126 -0x1.0000000000000p+126)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p+1 0x1.0000000000000p+1 0x1.0000000000000p+1 0x1.0000000000000p+1)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p+1 -0x1.0000000000000p+1 -0x1.0000000000000p+1 -0x1.0000000000000p+1)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.45f306446f9b4p-3 0x1.45f306446f9b4p-3 0x1.45f306446f9b4p-3 0x1.45f306446f9b4p-3)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.45f306446f9b4p-3 -0x1.45f306446f9b4p-3 -0x1.45f306446f9b4p-3 -0x1.45f306446f9b4p-3)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.0000010000010p-128 0x1.0000010000010p-128 0x1.0000010000010p-128 0x1.0000010000010p-128)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000010000010p-128 -0x1.0000010000010p-128 -0x1.0000010000010p-128 -0x1.0000010000010p-128)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p+126 -0x1.0000000000000p+126 -0x1.0000000000000p+126 -0x1.0000000000000p+126)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p+126 0x1.0000000000000p+126 0x1.0000000000000p+126 0x1.0000000000000p+126)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p+1 -0x1.0000000000000p+1 -0x1.0000000000000p+1 -0x1.0000000000000p+1)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p+1 0x1.0000000000000p+1 0x1.0000000000000p+1 0x1.0000000000000p+1)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.45f306446f9b4p-3 -0x1.45f306446f9b4p-3 -0x1.45f306446f9b4p-3 -0x1.45f306446f9b4p-3)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.45f306446f9b4p-3 0x1.45f306446f9b4p-3 0x1.45f306446f9b4p-3 0x1.45f306446f9b4p-3)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000010000010p-128 -0x1.0000010000010p-128 -0x1.0000010000010p-128 -0x1.0000010000010p-128)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.0000010000010p-128 0x1.0000010000010p-128 0x1.0000010000010p-128 0x1.0000010000010p-128)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.921fb60000000p+3 0x1.921fb60000000p+3 0x1.921fb60000000p+3 0x1.921fb60000000p+3)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.921fb60000000p+3 -0x1.921fb60000000p+3 -0x1.921fb60000000p+3 -0x1.921fb60000000p+3)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.921fb7921fb79p-126 0x1.921fb7921fb79p-126 0x1.921fb7921fb79p-126 0x1.921fb7921fb79p-126)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.921fb7921fb79p-126 -0x1.921fb7921fb79p-126 -0x1.921fb7921fb79p-126 -0x1.921fb7921fb79p-126)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.921fb60000000p+3 -0x1.921fb60000000p+3 -0x1.921fb60000000p+3 -0x1.921fb60000000p+3)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.921fb60000000p+3 0x1.921fb60000000p+3 0x1.921fb60000000p+3 0x1.921fb60000000p+3)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.921fb7921fb79p-126 -0x1.921fb7921fb79p-126 -0x1.921fb7921fb79p-126 -0x1.921fb7921fb79p-126)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.921fb7921fb79p-126 0x1.921fb7921fb79p-126 0x1.921fb7921fb79p-126 0x1.921fb7921fb79p-126)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.45f304fe7c950p+125 0x1.45f304fe7c950p+125 0x1.45f304fe7c950p+125 0x1.45f304fe7c950p+125)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.45f304fe7c950p+125 -0x1.45f304fe7c950p+125 -0x1.45f304fe7c950p+125 -0x1.45f304fe7c950p+125)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.45f304fe7c950p+125 -0x1.45f304fe7c950p+125 -0x1.45f304fe7c950p+125 -0x1.45f304fe7c950p+125)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.45f304fe7c950p+125 0x1.45f304fe7c950p+125 0x1.45f304fe7c950p+125 0x1.45f304fe7c950p+125)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.div" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.6a09e667f3bcdp-75 0x1.6a09e667f3bcdp-75 0x1.6a09e667f3bcdp-75 0x1.6a09e667f3bcdp-75)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-63 0x1.0000000000000p-63 0x1.0000000000000p-63 0x1.0000000000000p-63)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.6a09e667f3bcdp-1 0x1.6a09e667f3bcdp-1 0x1.6a09e667f3bcdp-1 0x1.6a09e667f3bcdp-1)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.40d9324a48138p+1 0x1.40d9324a48138p+1 0x1.40d9324a48138p+1 0x1.40d9324a48138p+1)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffeffffffcp+63 0x1.fffffeffffffcp+63 0x1.fffffeffffffcp+63 0x1.fffffeffffffcp+63)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 11111.111060555555 11111.111060555555 11111.111060555555 11111.111060555555)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 35136418286444.62 35136418286444.62 35136418286444.62 35136418286444.62)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 35136418286444.62 35136418286444.62 35136418286444.62 35136418286444.62)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 3.5136418286444623e-06 3.5136418286444623e-06 3.5136418286444623e-06 3.5136418286444623e-06)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 11111.111060555555 11111.111060555555 11111.111060555555 11111.111060555555)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 35136418286444.62 35136418286444.62 35136418286444.62 35136418286444.62)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 35136418286444.62 35136418286444.62 35136418286444.62 35136418286444.62)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 3.5136418286444623e-06 3.5136418286444623e-06 3.5136418286444623e-06 3.5136418286444623e-06)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 11111.11106111111 11111.11106111111 11111.11106111111 11111.11106111111)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 35136418288201.445 35136418288201.445 35136418288201.445 35136418288201.445)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 35136418288201.445 35136418288201.445 35136418288201.445 35136418288201.445)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 3.513641828820144e-06 3.513641828820144e-06 3.513641828820144e-06 3.513641828820144e-06)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.1111111111111p+28 0x1.1111111111111p+28 0x1.1111111111111p+28 0x1.1111111111111p+28)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.822cb17ff2eb8p+18 0x1.822cb17ff2eb8p+18 0x1.822cb17ff2eb8p+18 0x1.822cb17ff2eb8p+18)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.1111111111111p+28 0x1.1111111111111p+28 0x1.1111111111111p+28 0x1.1111111111111p+28)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.822cb17ff2eb8p+18 0x1.822cb17ff2eb8p+18 0x1.822cb17ff2eb8p+18 0x1.822cb17ff2eb8p+18)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.1111111111111p+28 0x1.1111111111111p+28 0x1.1111111111111p+28 0x1.1111111111111p+28)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37 0x1.822cb17ff2eb8p+37)) +(assert_return (invoke "f32x4.sqrt" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.822cb17ff2eb8p+18 0x1.822cb17ff2eb8p+18 0x1.822cb17ff2eb8p+18 0x1.822cb17ff2eb8p+18)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 -123456789.0 -123456789.0 -123456789.0 -123456789.0)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 -1.23456789e+27 -1.23456789e+27 -1.23456789e+27 -1.23456789e+27)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 -1.23456789e+27 -1.23456789e+27 -1.23456789e+27 -1.23456789e+27)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 -1.23456789e-11 -1.23456789e-11 -1.23456789e-11 -1.23456789e-11)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 -123456789.0 -123456789.0 -123456789.0 -123456789.0)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 -1.23456789e+27 -1.23456789e+27 -1.23456789e+27 -1.23456789e+27)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 -1.23456789e+27 -1.23456789e+27 -1.23456789e+27 -1.23456789e+27)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 -1.23456789e-11 -1.23456789e-11 -1.23456789e-11 -1.23456789e-11)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 -123456789.01234567 -123456789.01234567 -123456789.01234567 -123456789.01234567)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 -1.2345678901234569e+27 -1.2345678901234569e+27 -1.2345678901234569e+27 -1.2345678901234569e+27)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 -1.2345678901234569e+27 -1.2345678901234569e+27 -1.2345678901234569e+27 -1.2345678901234569e+27)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 -1.2345678901234568e-11 -1.2345678901234568e-11 -1.2345678901234568e-11 -1.2345678901234568e-11)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 -0x1.23456789abcdfp+56 -0x1.23456789abcdfp+56 -0x1.23456789abcdfp+56 -0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 -0x1.23456789abcdfp+37 -0x1.23456789abcdfp+37 -0x1.23456789abcdfp+37 -0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 -0x1.23456789abcdfp+56 -0x1.23456789abcdfp+56 -0x1.23456789abcdfp+56 -0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 -0x1.23456789abcdfp+37 -0x1.23456789abcdfp+37 -0x1.23456789abcdfp+37 -0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 -0x1.23456789abcdfp+56 -0x1.23456789abcdfp+56 -0x1.23456789abcdfp+56 -0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75 -0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.neg" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 -0x1.23456789abcdfp+37 -0x1.23456789abcdfp+37 -0x1.23456789abcdfp+37 -0x1.23456789abcdfp+37)) + + +;; Mixed f32x4 tests when some lanes are NaNs +(module + + (func (export "f32x4_sqrt_arith") (result v128) + (f32x4.sqrt (v128.const f32x4 nan:0x200000 -nan:0x200000 16.0 25.0))) + (func (export "f32x4_sqrt_canon") (result v128) + (f32x4.sqrt (v128.const f32x4 -1.0 nan 4.0 9.0))) + (func (export "f32x4_sqrt_mixed") (result v128) + (f32x4.sqrt (v128.const f32x4 -inf nan:0x200000 36.0 49.0))) +) + +(assert_return (invoke "f32x4_sqrt_arith") (v128.const f32x4 nan:arithmetic nan:arithmetic 4.0 5.0)) +(assert_return (invoke "f32x4_sqrt_canon") (v128.const f32x4 nan:canonical nan:canonical 2.0 3.0)) +(assert_return (invoke "f32x4_sqrt_mixed") (v128.const f32x4 nan:canonical nan:arithmetic 6.0 7.0)) + +;; type check +(assert_invalid (module (func (result v128) (f32x4.neg (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.sqrt (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.add (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.sub (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.mul (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.div (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $f32x4.neg-arg-empty (result v128) + (f32x4.neg) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.sqrt-arg-empty (result v128) + (f32x4.sqrt) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.add-1st-arg-empty (result v128) + (f32x4.add (v128.const f32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.add-arg-empty (result v128) + (f32x4.add) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.sub-1st-arg-empty (result v128) + (f32x4.sub (v128.const f32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.sub-arg-empty (result v128) + (f32x4.sub) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.mul-1st-arg-empty (result v128) + (f32x4.mul (v128.const f32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.mul-arg-empty (result v128) + (f32x4.mul) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.div-1st-arg-empty (result v128) + (f32x4.div (v128.const f32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.div-arg-empty (result v128) + (f32x4.div) + ) + ) + "type mismatch" +) + +;; combination +(module + (func (export "add-sub") (param v128 v128 v128) (result v128) + (f32x4.add (f32x4.sub (local.get 0) (local.get 1))(local.get 2))) + (func (export "div-add") (param v128 v128 v128) (result v128) + (f32x4.div (f32x4.add (local.get 0) (local.get 1))(local.get 2))) + (func (export "div-mul") (param v128 v128 v128) (result v128) + (f32x4.div (f32x4.mul (local.get 0) (local.get 1))(local.get 2))) + (func (export "div-sub") (param v128 v128 v128) (result v128) + (f32x4.div (f32x4.sub (local.get 0) (local.get 1))(local.get 2))) + (func (export "mul-add") (param v128 v128 v128) (result v128) + (f32x4.mul (f32x4.add (local.get 0) (local.get 1))(local.get 2))) + (func (export "mul-div") (param v128 v128 v128) (result v128) + (f32x4.mul (f32x4.div (local.get 0) (local.get 1))(local.get 2))) + (func (export "mul-sub") (param v128 v128 v128) (result v128) + (f32x4.mul (f32x4.sub (local.get 0) (local.get 1))(local.get 2))) + (func (export "sub-add") (param v128 v128 v128) (result v128) + (f32x4.sub (f32x4.add (local.get 0) (local.get 1))(local.get 2))) + (func (export "add-neg") (param v128 v128) (result v128) + (f32x4.add (f32x4.neg (local.get 0)) (local.get 1))) + (func (export "add-sqrt") (param v128 v128) (result v128) + (f32x4.add (f32x4.sqrt (local.get 0)) (local.get 1))) + (func (export "div-neg") (param v128 v128) (result v128) + (f32x4.div (f32x4.neg (local.get 0)) (local.get 1))) + (func (export "div-sqrt") (param v128 v128) (result v128) + (f32x4.div (f32x4.sqrt (local.get 0)) (local.get 1))) + (func (export "mul-neg") (param v128 v128) (result v128) + (f32x4.mul (f32x4.neg (local.get 0)) (local.get 1))) + (func (export "mul-sqrt") (param v128 v128) (result v128) + (f32x4.mul (f32x4.sqrt (local.get 0)) (local.get 1))) + (func (export "sub-neg") (param v128 v128) (result v128) + (f32x4.sub (f32x4.neg (local.get 0)) (local.get 1))) + (func (export "sub-sqrt") (param v128 v128) (result v128) + (f32x4.sub (f32x4.sqrt (local.get 0)) (local.get 1))) +) + +(assert_return (invoke "add-sub" (v128.const f32x4 1.125 1.125 1.125 1.125) + (v128.const f32x4 0.25 0.25 0.25 0.25) + (v128.const f32x4 0.125 0.125 0.125 0.125)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "div-add" (v128.const f32x4 1.125 1.125 1.125 1.125) + (v128.const f32x4 0.125 0.125 0.125 0.125) + (v128.const f32x4 0.25 0.25 0.25 0.25)) + (v128.const f32x4 5.0 5.0 5.0 5.0)) +(assert_return (invoke "div-mul" (v128.const f32x4 1.125 1.125 1.125 1.125) + (v128.const f32x4 4 4 4 4) + (v128.const f32x4 0.25 0.25 0.25 0.25)) + (v128.const f32x4 18.0 18.0 18.0 18.0)) +(assert_return (invoke "div-sub" (v128.const f32x4 1.125 1.125 1.125 1.125) + (v128.const f32x4 0.125 0.125 0.125 0.125) + (v128.const f32x4 0.25 0.25 0.25 0.25)) + (v128.const f32x4 4.0 4.0 4.0 4.0)) +(assert_return (invoke "mul-add" (v128.const f32x4 1.25 1.25 1.25 1.25) + (v128.const f32x4 0.25 0.25 0.25 0.25) + (v128.const f32x4 0.25 0.25 0.25 0.25)) + (v128.const f32x4 0.375 0.375 0.375 0.375)) +(assert_return (invoke "mul-div" (v128.const f32x4 1.125 1.125 1.125 1.125) + (v128.const f32x4 0.125 0.125 0.125 0.125) + (v128.const f32x4 0.25 0.25 0.25 0.25)) + (v128.const f32x4 2.25 2.25 2.25 2.25)) +(assert_return (invoke "mul-sub" (v128.const f32x4 1.125 1.125 1.125 1.125) + (v128.const f32x4 0.125 0.125 0.125 0.125) + (v128.const f32x4 0.25 0.25 0.25 0.25)) + (v128.const f32x4 0.25 0.25 0.25 0.25)) +(assert_return (invoke "sub-add" (v128.const f32x4 1.125 1.125 1.125 1.125) + (v128.const f32x4 0.25 0.25 0.25 0.25) + (v128.const f32x4 0.125 0.125 0.125 0.125)) + (v128.const f32x4 1.25 1.25 1.25 1.25)) +(assert_return (invoke "add-neg" (v128.const f32x4 1.125 1.125 1.125 1.125) + (v128.const f32x4 0.125 0.125 0.125 0.125)) + (v128.const f32x4 -1.0 -1.0 -1.0 -1.0)) +(assert_return (invoke "add-sqrt" (v128.const f32x4 2.25 2.25 2.25 2.25) + (v128.const f32x4 0.25 0.25 0.25 0.25)) + (v128.const f32x4 1.75 1.75 1.75 1.75)) +(assert_return (invoke "div-neg" (v128.const f32x4 1.5 1.5 1.5 1.5) + (v128.const f32x4 0.25 0.25 0.25 0.25)) + (v128.const f32x4 -6 -6 -6 -6)) +(assert_return (invoke "div-sqrt" (v128.const f32x4 2.25 2.25 2.25 2.25) + (v128.const f32x4 0.25 0.25 0.25 0.25)) + (v128.const f32x4 6 6 6 6)) +(assert_return (invoke "mul-neg" (v128.const f32x4 1.5 1.5 1.5 1.5) + (v128.const f32x4 0.25 0.25 0.25 0.25)) + (v128.const f32x4 -0.375 -0.375 -0.375 -0.375)) +(assert_return (invoke "mul-sqrt" (v128.const f32x4 2.25 2.25 2.25 2.25) + (v128.const f32x4 0.25 0.25 0.25 0.25)) + (v128.const f32x4 0.375 0.375 0.375 0.375)) +(assert_return (invoke "sub-neg" (v128.const f32x4 1.125 1.125 1.125 1.125) + (v128.const f32x4 0.125 0.125 0.125 0.125)) + (v128.const f32x4 -1.25 -1.25 -1.25 -1.25)) +(assert_return (invoke "sub-sqrt" (v128.const f32x4 2.25 2.25 2.25 2.25) + (v128.const f32x4 0.25 0.25 0.25 0.25)) + (v128.const f32x4 1.25 1.25 1.25 1.25)) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f32x4_cmp.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f32x4_cmp.wast new file mode 100644 index 000000000..9e9a8735b --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f32x4_cmp.wast @@ -0,0 +1,8167 @@ +;; Test all the f32x4 comparison operators on major boundary values and all special values. + +(module + (func (export "eq") (param $x v128) (param $y v128) (result v128) (f32x4.eq (local.get $x) (local.get $y))) + (func (export "ne") (param $x v128) (param $y v128) (result v128) (f32x4.ne (local.get $x) (local.get $y))) + (func (export "lt") (param $x v128) (param $y v128) (result v128) (f32x4.lt (local.get $x) (local.get $y))) + (func (export "le") (param $x v128) (param $y v128) (result v128) (f32x4.le (local.get $x) (local.get $y))) + (func (export "gt") (param $x v128) (param $y v128) (result v128) (f32x4.gt (local.get $x) (local.get $y))) + (func (export "ge") (param $x v128) (param $y v128) (result v128) (f32x4.ge (local.get $x) (local.get $y))) +) + +;; eq +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; ne +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 0 0 0 0)) + +;; lt +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 0 0 0 0)) + +;; le +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; gt +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 0 0 0 0)) + +;; ge +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; eq + +;; f32x4.eq (f32x4) (i8x16) +(assert_return (invoke "eq" (v128.const f32x4 -1 0 1 2.0) + (v128.const i8x16 -1 -1 -1 -1 0 0 0 0 1 1 1 1 2 2 2 2)) + (v128.const i32x4 0 -1 0 0)) + +;; f32x4.eq (f32x4) (i16x8) +(assert_return (invoke "eq" (v128.const f32x4 -1 0 1 2.0) + (v128.const i16x8 -1 -1 0 0 1 1 2 2)) + (v128.const i32x4 0 -1 0 0)) + +;; f32x4.eq (f32x4) (i32x4) +(assert_return (invoke "eq" (v128.const f32x4 -1 0 1 2.0) + (v128.const i32x4 3212836864 0 1 2)) + (v128.const i32x4 -1 -1 0 0 )) + +;; ne + +;; f32x4.ne (f32x4) (i8x16) +(assert_return (invoke "ne" (v128.const f32x4 -1 0 1 2.0) + (v128.const i8x16 -1 -1 -1 -1 0 0 0 0 1 1 1 1 2 2 2 2)) + (v128.const i32x4 -1 0 -1 -1)) + +;; f32x4.ne (f32x4) (i16x8) +(assert_return (invoke "ne" (v128.const f32x4 -1 0 1 2.0) + (v128.const i16x8 -1 -1 0 0 1 1 2 2)) + (v128.const i32x4 -1 0 -1 -1)) + +;; f32x4.ne (f32x4) (i32x4) +(assert_return (invoke "ne" (v128.const f32x4 -1 0 1 2.0) + (v128.const i32x4 3212836864 0 1 2)) + (v128.const i32x4 0 0 -1 -1)) + +;; lt + +;; f32x4.lt (f32x4) (i8x16) +(assert_return (invoke "lt" (v128.const f32x4 -1 0 1 2.0) + (v128.const i8x16 -1 -1 -1 -1 0 0 0 0 1 1 1 1 2 2 2 2)) + (v128.const i32x4 0 0 0 0)) + +;; f32x4.lt (f32x4) (i16x8) +(assert_return (invoke "lt" (v128.const f32x4 -1 0 1 2.0) + (v128.const i16x8 -1 -1 0 0 1 1 2 2)) + (v128.const i32x4 0 0 0 0)) + +;; f32x4.lt (f32x4) (i32x4) +(assert_return (invoke "lt" (v128.const f32x4 -1 0 1 2.0) + (v128.const i32x4 3212836864 0 1 2)) + (v128.const i32x4 0 0 0 0)) + +;; le + +;; f32x4.le (f32x4) (i8x16) +(assert_return (invoke "le" (v128.const f32x4 -1 0 1 2.0) + (v128.const i8x16 -1 -1 -1 -1 0 0 0 0 1 1 1 1 2 2 2 2)) + (v128.const i32x4 0 -1 0 0)) + +;; f32x4.le (f32x4) (i16x8) +(assert_return (invoke "le" (v128.const f32x4 -1 0 1 2.0) + (v128.const i16x8 -1 -1 0 0 1 1 2 2)) + (v128.const i32x4 0 -1 0 0)) + +;; f32x4.le (f32x4) (i32x4) +(assert_return (invoke "le" (v128.const f32x4 -1 0 1 2.0) + (v128.const i32x4 3212836864 0 1 2)) + (v128.const i32x4 -1 -1 0 0)) + +;; gt + +;; f32x4.gt (f32x4) (i8x16) +(assert_return (invoke "gt" (v128.const f32x4 -1 0 1 2.0) + (v128.const i8x16 -1 -1 -1 -1 0 0 0 0 1 1 1 1 2 2 2 2)) + (v128.const i32x4 0 0 -1 -1)) + +;; f32x4.gt (f32x4) (i16x8) +(assert_return (invoke "gt" (v128.const f32x4 -1 0 1 2.0) + (v128.const i16x8 -1 -1 0 0 1 1 2 2)) + (v128.const i32x4 0 0 -1 -1)) + +;; f32x4.gt (f32x4) (i32x4) +(assert_return (invoke "gt" (v128.const f32x4 -1 0 1 2.0) + (v128.const i32x4 3212836864 0 1 2)) + (v128.const i32x4 0 0 -1 -1)) + +;; ge + +;; f32x4.ge (f32x4) (i8x16) +(assert_return (invoke "ge" (v128.const f32x4 -1 0 1 2.0) + (v128.const i8x16 -1 -1 -1 -1 0 0 0 0 1 1 1 1 2 2 2 2)) + (v128.const i32x4 0 -1 -1 -1)) + +;; f32x4.ge (f32x4) (i16x8) +(assert_return (invoke "ge" (v128.const f32x4 -1 0 1 2.0) + (v128.const i16x8 -1 -1 0 0 1 1 2 2)) + (v128.const i32x4 0 -1 -1 -1)) + +;; f32x4.ge (f32x4) (i32x4) +(assert_return (invoke "ge" (v128.const f32x4 -1 0 1 2.0) + (v128.const i32x4 3212836864 0 1 2)) + (v128.const i32x4 -1 -1 -1 -1)) + + +;; Type check + +(assert_invalid (module (func (result v128) (f32x4.eq (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.ge (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.gt (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.le (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.lt (i64.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.ne (i64.const 0) (f64.const 0)))) "type mismatch") + + +;; Unknown operators + +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (f4x32.eq (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (f4x32.ge (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (f4x32.gt (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (f4x32.le (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (f4x32.lt (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (f4x32.ne (local.get $x) (local.get $y)))") "unknown operator") + + +;; Combination + +(module (memory 1) + (func (export "eq-in-block") + (block + (drop + (block (result v128) + (f32x4.eq + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "ne-in-block") + (block + (drop + (block (result v128) + (f32x4.ne + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "lt-in-block") + (block + (drop + (block (result v128) + (f32x4.lt + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "le-in-block") + (block + (drop + (block (result v128) + (f32x4.le + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "gt-in-block") + (block + (drop + (block (result v128) + (f32x4.gt + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "ge-in-block") + (block + (drop + (block (result v128) + (f32x4.ge + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "nested-eq") + (drop + (f32x4.eq + (f32x4.eq + (f32x4.eq + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f32x4.eq + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (f32x4.eq + (f32x4.eq + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f32x4.eq + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-ne") + (drop + (f32x4.ne + (f32x4.ne + (f32x4.ne + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f32x4.ne + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (f32x4.ne + (f32x4.ne + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f32x4.ne + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-lt") + (drop + (f32x4.lt + (f32x4.lt + (f32x4.lt + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f32x4.lt + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (f32x4.lt + (f32x4.lt + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f32x4.lt + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-le") + (drop + (f32x4.le + (f32x4.le + (f32x4.le + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f32x4.le + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (f32x4.le + (f32x4.le + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f32x4.le + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-gt") + (drop + (f32x4.gt + (f32x4.gt + (f32x4.gt + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f32x4.gt + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (f32x4.gt + (f32x4.gt + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f32x4.gt + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-ge") + (drop + (f32x4.ge + (f32x4.ge + (f32x4.ge + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f32x4.ge + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (f32x4.ge + (f32x4.ge + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f32x4.ge + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "as-param") + (drop + (f32x4.ge + (f32x4.eq + (f32x4.lt + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f32x4.le + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (f32x4.ne + (f32x4.gt + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f32x4.lt + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) +) + +(assert_return (invoke "eq-in-block")) +(assert_return (invoke "ne-in-block")) +(assert_return (invoke "lt-in-block")) +(assert_return (invoke "le-in-block")) +(assert_return (invoke "gt-in-block")) +(assert_return (invoke "ge-in-block")) +(assert_return (invoke "nested-eq")) +(assert_return (invoke "nested-ne")) +(assert_return (invoke "nested-lt")) +(assert_return (invoke "nested-le")) +(assert_return (invoke "nested-gt")) +(assert_return (invoke "nested-ge")) +(assert_return (invoke "as-param")) + +;; Test operation with empty argument + +(assert_invalid + (module + (func $f32x4.eq-1st-arg-empty (result v128) + (f32x4.eq (v128.const f32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.eq-arg-empty (result v128) + (f32x4.eq) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.ne-1st-arg-empty (result v128) + (f32x4.ne (v128.const f32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.ne-arg-empty (result v128) + (f32x4.ne) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.lt-1st-arg-empty (result v128) + (f32x4.lt (v128.const f32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.lt-arg-empty (result v128) + (f32x4.lt) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.le-1st-arg-empty (result v128) + (f32x4.le (v128.const f32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.le-arg-empty (result v128) + (f32x4.le) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.gt-1st-arg-empty (result v128) + (f32x4.gt (v128.const f32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.gt-arg-empty (result v128) + (f32x4.gt) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.ge-1st-arg-empty (result v128) + (f32x4.ge (v128.const f32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.ge-arg-empty (result v128) + (f32x4.ge) + ) + ) + "type mismatch" +) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f32x4_pmin_pmax.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f32x4_pmin_pmax.wast new file mode 100644 index 000000000..b0f47b380 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f32x4_pmin_pmax.wast @@ -0,0 +1,11676 @@ +;; Tests for f32x4 [pmin, pmax] operations on major boundary values and all special values. + + +(module + (func (export "f32x4.pmin") (param v128 v128) (result v128) (f32x4.pmin (local.get 0) (local.get 1))) + (func (export "f32x4.pmax") (param v128 v128) (result v128) (f32x4.pmax (local.get 0) (local.get 1))) +) + +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) +(assert_return (invoke "f32x4.pmin" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149 0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149 -0x1.0000000000000p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126 0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126 -0x1.0000000000000p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2 -0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2 0x1.921fb60000000p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56 0x1.23456789abcdfp+56)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75 0x1.23456789abcdfp+75)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37 0x1.23456789abcdfp+37)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan nan nan nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan -nan -nan -nan) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -nan -nan -nan -nan)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 inf inf inf inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 -inf -inf -inf -inf) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) +(assert_return (invoke "f32x4.pmax" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019) + (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + + +;; Unknown operators + +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.pmin (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.pmax (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.pmin (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.pmax (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.pmin (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.pmax (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.pmin (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.pmax (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") + +;; type check +(assert_invalid (module (func (result v128) (f32x4.pmin (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.pmax (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $f32x4.pmin-1st-arg-empty (result v128) + (f32x4.pmin (v128.const f32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.pmin-arg-empty (result v128) + (f32x4.pmin) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.pmax-1st-arg-empty (result v128) + (f32x4.pmax (v128.const f32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.pmax-arg-empty (result v128) + (f32x4.pmax) + ) + ) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f32x4_rounding.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f32x4_rounding.wast new file mode 100644 index 000000000..e59f99a77 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f32x4_rounding.wast @@ -0,0 +1,424 @@ +;; Tests for f32x4 [ceil, floor, trunc, nearest] operations on major boundary values and all special values. + + +(module + (func (export "f32x4.ceil") (param v128) (result v128) (f32x4.ceil (local.get 0))) + (func (export "f32x4.floor") (param v128) (result v128) (f32x4.floor (local.get 0))) + (func (export "f32x4.trunc") (param v128) (result v128) (f32x4.trunc (local.get 0))) + (func (export "f32x4.nearest") (param v128) (result v128) (f32x4.nearest (local.get 0))) +) + +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.c000000000000p+2 0x1.c000000000000p+2 0x1.c000000000000p+2 0x1.c000000000000p+2)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.8000000000000p+2 -0x1.8000000000000p+2 -0x1.8000000000000p+2 -0x1.8000000000000p+2)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 123456789.0 123456789.0 123456789.0 123456789.0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 123456789.0 123456789.0 123456789.0 123456789.0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 123456790.0 123456790.0 123456790.0 123456790.0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 156374987062.0 156374987062.0 156374987062.0 156374987062.0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 156374987062.0 156374987062.0 156374987062.0 156374987062.0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 156374987062.0 156374987062.0 156374987062.0 156374987062.0)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.ceil" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.8000000000000p+2 0x1.8000000000000p+2 0x1.8000000000000p+2 0x1.8000000000000p+2)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.c000000000000p+2 -0x1.c000000000000p+2 -0x1.c000000000000p+2 -0x1.c000000000000p+2)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 123456789.0 123456789.0 123456789.0 123456789.0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 123456789.0 123456789.0 123456789.0 123456789.0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 123456789.0 123456789.0 123456789.0 123456789.0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 156374987061.0 156374987061.0 156374987061.0 156374987061.0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 156374987061.0 156374987061.0 156374987061.0 156374987061.0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 156374987061.0 156374987061.0 156374987061.0 156374987061.0)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.floor" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.8000000000000p+2 0x1.8000000000000p+2 0x1.8000000000000p+2 0x1.8000000000000p+2)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.8000000000000p+2 -0x1.8000000000000p+2 -0x1.8000000000000p+2 -0x1.8000000000000p+2)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 123456789.0 123456789.0 123456789.0 123456789.0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 123456789.0 123456789.0 123456789.0 123456789.0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 123456789.0 123456789.0 123456789.0 123456789.0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 156374987061.0 156374987061.0 156374987061.0 156374987061.0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 156374987061.0 156374987061.0 156374987061.0 156374987061.0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 156374987061.0 156374987061.0 156374987061.0 156374987061.0)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.trunc" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x0p+0 0x0p+0 0x0p+0 0x0p+0)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 -0x0p+0 -0x0p+0 -0x0p+0 -0x0p+0)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const f32x4 0x0.0p+0 0x0.0p+0 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const f32x4 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const f32x4 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const f32x4 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const f32x4 0x1.8000000000000p+2 0x1.8000000000000p+2 0x1.8000000000000p+2 0x1.8000000000000p+2)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const f32x4 -0x1.8000000000000p+2 -0x1.8000000000000p+2 -0x1.8000000000000p+2 -0x1.8000000000000p+2)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const f32x4 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127 0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const f32x4 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127 -0x1.fffffe0000000p+127)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 inf inf inf inf)) + (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) + (v128.const f32x4 123456789.0 123456789.0 123456789.0 123456789.0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0123456789e+019 0123456789e+019 0123456789e+019 0123456789e+019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0123456789e-019 0123456789e-019 0123456789e-019 0123456789e-019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) + (v128.const f32x4 123456789.0 123456789.0 123456789.0 123456789.0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0123456789.e019 0123456789.e019 0123456789.e019 0123456789.e019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) + (v128.const f32x4 1.23456789e+27 1.23456789e+27 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0123456789.e-019 0123456789.e-019 0123456789.e-019 0123456789.e-019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789 0123456789.0123456789)) + (v128.const f32x4 123456789.0 123456789.0 123456789.0 123456789.0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f32x4 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f32x4 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) + (v128.const f32x4 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019 0x0123456789ABCDEFp+019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019 0x0123456789ABCDEFp-019)) + (v128.const f32x4 156374987061.0 156374987061.0 156374987061.0 156374987061.0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) + (v128.const f32x4 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019 0x0123456789ABCDEF.p019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019 0x0123456789ABCDEF.p+019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + (v128.const f32x4 156374987061.0 156374987061.0 156374987061.0 156374987061.0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF 0x0123456789ABCDEF.019aF)) + (v128.const f32x4 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16 8.19855292164869e+16)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019 0x0123456789ABCDEF.019aFp019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019 0x0123456789ABCDEF.019aFp+019)) + (v128.const f32x4 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22 4.298402914185348e+22)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019 0x0123456789ABCDEF.019aFp-019)) + (v128.const f32x4 156374987061.0 156374987061.0 156374987061.0 156374987061.0)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 nan nan nan nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 nan:0x200000 nan:0x200000 nan:0x200000 nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f32x4.nearest" (v128.const f32x4 -nan:0x200000 -nan:0x200000 -nan:0x200000 -nan:0x200000)) + (v128.const f32x4 nan:arithmetic nan:arithmetic nan:arithmetic nan:arithmetic)) + + +;; Unknown operators + +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.ceil (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.floor (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.trunc (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.nearest (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.ceil (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.floor (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.trunc (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.nearest (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.ceil (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.floor (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.trunc (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.nearest (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.ceil (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.floor (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.trunc (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.nearest (v128.const i32x4 0 0 0 0)))") "unknown operator") + +;; type check +(assert_invalid (module (func (result v128) (f32x4.ceil (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.floor (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.trunc (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.nearest (i32.const 0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $f32x4.ceil-arg-empty (result v128) + (f32x4.ceil) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.floor-arg-empty (result v128) + (f32x4.floor) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.trunc-arg-empty (result v128) + (f32x4.trunc) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.nearest-arg-empty (result v128) + (f32x4.nearest) + ) + ) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f64x2.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f64x2.wast new file mode 100644 index 000000000..8ebd33b36 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f64x2.wast @@ -0,0 +1,2459 @@ +;; Tests for f64x2 [abs, min, max] operations on major boundary values and all special values. + + +(module + (func (export "f64x2.min") (param v128 v128) (result v128) (f64x2.min (local.get 0) (local.get 1))) + (func (export "f64x2.max") (param v128 v128) (result v128) (f64x2.max (local.get 0) (local.get 1))) + (func (export "f64x2.abs") (param v128) (result v128) (f64x2.abs (local.get 0))) + ;; f64x2.min const vs const + (func (export "f64x2.min_with_const_0") (result v128) (f64x2.min (v128.const f64x2 0 1) (v128.const f64x2 0 2))) + (func (export "f64x2.min_with_const_1") (result v128) (f64x2.min (v128.const f64x2 2 -3) (v128.const f64x2 1 3))) + (func (export "f64x2.min_with_const_2") (result v128) (f64x2.min (v128.const f64x2 0 1) (v128.const f64x2 0 1))) + (func (export "f64x2.min_with_const_3") (result v128) (f64x2.min (v128.const f64x2 2 3) (v128.const f64x2 2 3))) + (func (export "f64x2.min_with_const_4") (result v128) (f64x2.min (v128.const f64x2 0x00 0x01) (v128.const f64x2 0x00 0x02))) + (func (export "f64x2.min_with_const_5") (result v128) (f64x2.min (v128.const f64x2 0x02 0x80000000) (v128.const f64x2 0x01 2147483648))) + (func (export "f64x2.min_with_const_6") (result v128) (f64x2.min (v128.const f64x2 0x00 0x01) (v128.const f64x2 0x00 0x01))) + (func (export "f64x2.min_with_const_7") (result v128) (f64x2.min (v128.const f64x2 0x02 0x80000000) (v128.const f64x2 0x02 0x80000000))) + ;; f64x2.min param vs const + (func (export "f64x2.min_with_const_9") (param v128) (result v128) (f64x2.min (local.get 0) (v128.const f64x2 0 1))) + (func (export "f64x2.min_with_const_10") (param v128) (result v128) (f64x2.min (v128.const f64x2 2 -3) (local.get 0))) + (func (export "f64x2.min_with_const_11") (param v128) (result v128) (f64x2.min (v128.const f64x2 0 1) (local.get 0))) + (func (export "f64x2.min_with_const_12") (param v128) (result v128) (f64x2.min (local.get 0) (v128.const f64x2 2 3))) + (func (export "f64x2.min_with_const_13") (param v128) (result v128) (f64x2.min (v128.const f64x2 0x00 0x01) (local.get 0))) + (func (export "f64x2.min_with_const_14") (param v128) (result v128) (f64x2.min (v128.const f64x2 0x02 0x80000000) (local.get 0))) + (func (export "f64x2.min_with_const_15") (param v128) (result v128) (f64x2.min (v128.const f64x2 0x00 0x01) (local.get 0))) + (func (export "f64x2.min_with_const_16") (param v128) (result v128) (f64x2.min (v128.const f64x2 0x02 0x80000000) (local.get 0))) + ;; f64x2.max const vs const + (func (export "f64x2.max_with_const_18") (result v128) (f64x2.max (v128.const f64x2 0 1) (v128.const f64x2 0 2))) + (func (export "f64x2.max_with_const_19") (result v128) (f64x2.max (v128.const f64x2 2 -3) (v128.const f64x2 1 3))) + (func (export "f64x2.max_with_const_20") (result v128) (f64x2.max (v128.const f64x2 0 1) (v128.const f64x2 0 1))) + (func (export "f64x2.max_with_const_21") (result v128) (f64x2.max (v128.const f64x2 2 3) (v128.const f64x2 2 3))) + (func (export "f64x2.max_with_const_22") (result v128) (f64x2.max (v128.const f64x2 0x00 0x01) (v128.const f64x2 0x00 0x02))) + (func (export "f64x2.max_with_const_23") (result v128) (f64x2.max (v128.const f64x2 0x02 0x80000000) (v128.const f64x2 0x01 2147483648))) + (func (export "f64x2.max_with_const_24") (result v128) (f64x2.max (v128.const f64x2 0x00 0x01) (v128.const f64x2 0x00 0x01))) + (func (export "f64x2.max_with_const_25") (result v128) (f64x2.max (v128.const f64x2 0x02 0x80000000) (v128.const f64x2 0x02 0x80000000))) + ;; f64x2.max param vs const + (func (export "f64x2.max_with_const_27") (param v128) (result v128) (f64x2.max (local.get 0) (v128.const f64x2 0 1))) + (func (export "f64x2.max_with_const_28") (param v128) (result v128) (f64x2.max (v128.const f64x2 2 -3) (local.get 0))) + (func (export "f64x2.max_with_const_29") (param v128) (result v128) (f64x2.max (v128.const f64x2 0 1) (local.get 0))) + (func (export "f64x2.max_with_const_30") (param v128) (result v128) (f64x2.max (local.get 0) (v128.const f64x2 2 3))) + (func (export "f64x2.max_with_const_31") (param v128) (result v128) (f64x2.max (v128.const f64x2 0x00 0x01) (local.get 0))) + (func (export "f64x2.max_with_const_32") (param v128) (result v128) (f64x2.max (v128.const f64x2 0x02 0x80000000) (local.get 0))) + (func (export "f64x2.max_with_const_33") (param v128) (result v128) (f64x2.max (v128.const f64x2 0x00 0x01) (local.get 0))) + (func (export "f64x2.max_with_const_34") (param v128) (result v128) (f64x2.max (v128.const f64x2 0x02 0x80000000) (local.get 0))) + + (func (export "f64x2.abs_with_const_35") (result v128) (f64x2.abs (v128.const f64x2 -0 -1))) + (func (export "f64x2.abs_with_const_36") (result v128) (f64x2.abs (v128.const f64x2 -2 -3))) +) + +;; f64x2.min const vs const +(assert_return (invoke "f64x2.min_with_const_0") (v128.const f64x2 0 1)) +(assert_return (invoke "f64x2.min_with_const_1") (v128.const f64x2 1 -3)) +(assert_return (invoke "f64x2.min_with_const_2") (v128.const f64x2 0 1)) +(assert_return (invoke "f64x2.min_with_const_3") (v128.const f64x2 2 3)) +;; f64x2.min param vs const +(assert_return (invoke "f64x2.min_with_const_4") (v128.const f64x2 0x00 0x01)) +(assert_return (invoke "f64x2.min_with_const_5") (v128.const f64x2 0x01 0x80000000)) +(assert_return (invoke "f64x2.min_with_const_6") (v128.const f64x2 0x00 0x01)) +(assert_return (invoke "f64x2.min_with_const_7") (v128.const f64x2 0x02 0x80000000)) +(assert_return (invoke "f64x2.min_with_const_9" (v128.const f64x2 0 2)) + (v128.const f64x2 0 1)) +(assert_return (invoke "f64x2.min_with_const_10" (v128.const f64x2 1 3)) + (v128.const f64x2 1 -3)) +(assert_return (invoke "f64x2.min_with_const_11" (v128.const f64x2 0 1)) + (v128.const f64x2 0 1)) +(assert_return (invoke "f64x2.min_with_const_12" (v128.const f64x2 2 3)) + (v128.const f64x2 2 3)) +(assert_return (invoke "f64x2.min_with_const_13" (v128.const f64x2 0x00 0x02)) + (v128.const f64x2 0x00 0x01)) +(assert_return (invoke "f64x2.min_with_const_14" (v128.const f64x2 0x01 2147483648)) + (v128.const f64x2 0x01 0x80000000)) +(assert_return (invoke "f64x2.min_with_const_15" (v128.const f64x2 0x00 0x01)) + (v128.const f64x2 0x00 0x01)) +(assert_return (invoke "f64x2.min_with_const_16" (v128.const f64x2 0x02 0x80000000)) + (v128.const f64x2 0x02 0x80000000)) +;; f64x2.max const vs const +(assert_return (invoke "f64x2.max_with_const_18") (v128.const f64x2 0 2)) +(assert_return (invoke "f64x2.max_with_const_19") (v128.const f64x2 2 3)) +(assert_return (invoke "f64x2.max_with_const_20") (v128.const f64x2 0 1)) +(assert_return (invoke "f64x2.max_with_const_21") (v128.const f64x2 2 3)) +;; f64x2.max param vs const +(assert_return (invoke "f64x2.max_with_const_22") (v128.const f64x2 0x00 0x02)) +(assert_return (invoke "f64x2.max_with_const_23") (v128.const f64x2 0x02 2147483648)) +(assert_return (invoke "f64x2.max_with_const_24") (v128.const f64x2 0x00 0x01)) +(assert_return (invoke "f64x2.max_with_const_25") (v128.const f64x2 0x02 0x80000000)) +(assert_return (invoke "f64x2.max_with_const_27" (v128.const f64x2 0 2)) + (v128.const f64x2 0 2)) +(assert_return (invoke "f64x2.max_with_const_28" (v128.const f64x2 1 3)) + (v128.const f64x2 2 3)) +(assert_return (invoke "f64x2.max_with_const_29" (v128.const f64x2 0 1)) + (v128.const f64x2 0 1)) +(assert_return (invoke "f64x2.max_with_const_30" (v128.const f64x2 2 3)) + (v128.const f64x2 2 3)) +(assert_return (invoke "f64x2.max_with_const_31" (v128.const f64x2 0x00 0x02)) + (v128.const f64x2 0x00 0x02)) +(assert_return (invoke "f64x2.max_with_const_32" (v128.const f64x2 0x01 2147483648)) + (v128.const f64x2 0x02 2147483648)) +(assert_return (invoke "f64x2.max_with_const_33" (v128.const f64x2 0x00 0x01)) + (v128.const f64x2 0x00 0x01)) +(assert_return (invoke "f64x2.max_with_const_34" (v128.const f64x2 0x02 0x80000000)) + (v128.const f64x2 0x02 0x80000000)) + +(assert_return (invoke "f64x2.abs_with_const_35") (v128.const f64x2 0 1)) +(assert_return (invoke "f64x2.abs_with_const_36") (v128.const f64x2 2 3)) + +;; Test different lanes go through different if-then clauses +;; f64x2.min +(assert_return + (invoke "f64x2.min" + (v128.const f64x2 nan 0) + (v128.const f64x2 0 1) + ) + (v128.const f64x2 nan:canonical 0) +) +;; f64x2.min +(assert_return + (invoke "f64x2.min" + (v128.const f64x2 0 1) + (v128.const f64x2 -nan 0) + ) + (v128.const f64x2 nan:canonical 0) +) +;; f64x2.min +(assert_return + (invoke "f64x2.min" + (v128.const f64x2 0 1) + (v128.const f64x2 -nan 1) + ) + (v128.const f64x2 nan:canonical 1) +) +;; f64x2.max +(assert_return + (invoke "f64x2.max" + (v128.const f64x2 nan 0) + (v128.const f64x2 0 1) + ) + (v128.const f64x2 nan:canonical 1) +) +;; f64x2.max +(assert_return + (invoke "f64x2.max" + (v128.const f64x2 0 1) + (v128.const f64x2 -nan 0) + ) + (v128.const f64x2 nan:canonical 1) +) +;; f64x2.max +(assert_return + (invoke "f64x2.max" + (v128.const f64x2 0 1) + (v128.const f64x2 -nan 1) + ) + (v128.const f64x2 nan:canonical 1) +) + +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 inf inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const f64x2 0123456789.e038 0123456789.e038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const f64x2 0123456789.e038 0123456789.e038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const f64x2 0123456789.e038 0123456789.e038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const f64x2 0123456789.e038 0123456789.e038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0p+0 0x0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0p+0 0x0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 inf inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const f64x2 0123456789.e038 0123456789.e038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const f64x2 0123456789.e038 0123456789.e038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const f64x2 0123456789.e038 0123456789.e038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) + (v128.const f64x2 0123456789.e038 0123456789.e038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const f64x2 0123456789.e038 0123456789.e038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const f64x2 0123456789.e038 0123456789.e038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) + (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) + +;; Test opposite signs of zero +(assert_return (invoke "f64x2.min" (v128.const f64x2 0 0) + (v128.const f64x2 +0 -0)) + (v128.const f64x2 0 -0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0 +0) + (v128.const f64x2 +0 -0)) + (v128.const f64x2 -0 -0)) +(assert_return (invoke "f64x2.min" (v128.const f64x2 -0 -0) + (v128.const f64x2 +0 +0)) + (v128.const f64x2 -0 -0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 0 0) + (v128.const f64x2 +0 -0)) + (v128.const f64x2 0 0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0 +0) + (v128.const f64x2 +0 -0)) + (v128.const f64x2 0 0)) +(assert_return (invoke "f64x2.max" (v128.const f64x2 -0 -0) + (v128.const f64x2 +0 +0)) + (v128.const f64x2 +0 +0)) + + +(assert_return (invoke "f64x2.abs" (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const f64x2 0123456789.e038 0123456789.e038)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) +(assert_return (invoke "f64x2.abs" (v128.const f64x2 -01234567890123456789.01234567890123456789 -01234567890123456789.01234567890123456789)) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + +;; type check +(assert_invalid (module (func (result v128) (f64x2.abs (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.min (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.max (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $f64x2.abs-arg-empty (result v128) + (f64x2.abs) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.min-1st-arg-empty (result v128) + (f64x2.min (v128.const f64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.min-arg-empty (result v128) + (f64x2.min) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.max-1st-arg-empty (result v128) + (f64x2.max (v128.const f64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.max-arg-empty (result v128) + (f64x2.max) + ) + ) + "type mismatch" +) + +;; combination +(module + (func (export "max-min") (param v128 v128 v128) (result v128) + (f64x2.max (f64x2.min (local.get 0) (local.get 1))(local.get 2))) + (func (export "min-max") (param v128 v128 v128) (result v128) + (f64x2.min (f64x2.max (local.get 0) (local.get 1))(local.get 2))) + (func (export "max-abs") (param v128 v128) (result v128) + (f64x2.max (f64x2.abs (local.get 0)) (local.get 1))) + (func (export "min-abs") (param v128 v128) (result v128) + (f64x2.min (f64x2.abs (local.get 0)) (local.get 1))) +) + +(assert_return (invoke "max-min" (v128.const f64x2 1.125 1.125) + (v128.const f64x2 0.25 0.25) + (v128.const f64x2 0.125 0.125)) + (v128.const f64x2 0.25 0.25)) +(assert_return (invoke "min-max" (v128.const f64x2 1.125 1.125) + (v128.const f64x2 0.25 0.25) + (v128.const f64x2 0.125 0.125)) + (v128.const f64x2 0.125 0.125)) +(assert_return (invoke "max-abs" (v128.const f64x2 -1.125 -1.125) + (v128.const f64x2 0.125 0.125)) + (v128.const f64x2 1.125 1.125)) +(assert_return (invoke "min-abs" (v128.const f64x2 -1.125 -1.125) + (v128.const f64x2 0.125 0.125)) + (v128.const f64x2 0.125 0.125)) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f64x2_arith.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f64x2_arith.wast new file mode 100644 index 000000000..4ab3b01e9 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f64x2_arith.wast @@ -0,0 +1,5483 @@ +;; Tests for f64x2 arithmetic operations on major boundary values and all special values. + + +(module + (func (export "f64x2.add") (param v128 v128) (result v128) (f64x2.add (local.get 0) (local.get 1))) + (func (export "f64x2.sub") (param v128 v128) (result v128) (f64x2.sub (local.get 0) (local.get 1))) + (func (export "f64x2.mul") (param v128 v128) (result v128) (f64x2.mul (local.get 0) (local.get 1))) + (func (export "f64x2.div") (param v128 v128) (result v128) (f64x2.div (local.get 0) (local.get 1))) + (func (export "f64x2.neg") (param v128) (result v128) (f64x2.neg (local.get 0))) + (func (export "f64x2.sqrt") (param v128) (result v128) (f64x2.sqrt (local.get 0))) +) + +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1021 0x1.0000000000000p-1021)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000001p-1022 0x1.0000000000001p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000001p-1022 0x1.0000000000001p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1021 -0x1.0000000000000p-1021)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.fffffffffffffp-1022 -0x0.fffffffffffffp-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.fffffffffffffp-1022 -0x0.fffffffffffffp-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.8000000000000p+0 0x1.8000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.b21fb54442d18p+2 0x1.b21fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.721fb54442d18p+2 -0x1.721fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.8000000000000p+0 -0x1.8000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.721fb54442d18p+2 0x1.721fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.b21fb54442d18p+2 -0x1.b21fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.8000000000000p+0 0x1.8000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+1 0x1.0000000000000p+1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.d21fb54442d18p+2 0x1.d21fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.521fb54442d18p+2 -0x1.521fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.8000000000000p+0 -0x1.8000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+1 -0x1.0000000000000p+1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.521fb54442d18p+2 0x1.521fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.d21fb54442d18p+2 -0x1.d21fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.b21fb54442d18p+2 0x1.b21fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.721fb54442d18p+2 0x1.721fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.d21fb54442d18p+2 0x1.d21fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.521fb54442d18p+2 0x1.521fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+3 0x1.921fb54442d18p+3)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.721fb54442d18p+2 -0x1.721fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.b21fb54442d18p+2 -0x1.b21fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.521fb54442d18p+2 -0x1.521fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.d21fb54442d18p+2 -0x1.d21fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+3 -0x1.921fb54442d18p+3)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000001p-1022 0x1.0000000000001p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x0.fffffffffffffp-1022 -0x0.fffffffffffffp-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000002p-1022 0x0.0000000000002p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000002p-1022 0x0.0000000000002p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000001p-1022 0x1.0000000000001p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x0.fffffffffffffp-1022 -0x0.fffffffffffffp-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000002p-1022 0x0.0000000000002p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000002p-1022 0x0.0000000000002p-1022)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 246913578.0 246913578.0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 2.46913578e+27 2.46913578e+27)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 2.46913578e+27 2.46913578e+27)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 2.46913578e-11 2.46913578e-11)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 246913578.0 246913578.0)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 2.46913578e+27 2.46913578e+27)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 2.46913578e+27 2.46913578e+27)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 2.46913578e-11 2.46913578e-11)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 246913578.02469134 246913578.02469134)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 2.4691357802469137e+27 2.4691357802469137e+27)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 2.4691357802469137e+27 2.4691357802469137e+27)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 2.4691357802469137e-11 2.4691357802469137e-11)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+81 0x1.23456789abcdfp+81)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+100 0x1.23456789abcdfp+100)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+100 0x1.23456789abcdfp+100)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+62 0x1.23456789abcdfp+62)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+81 0x1.23456789abcdfp+81)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+100 0x1.23456789abcdfp+100)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+100 0x1.23456789abcdfp+100)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+62 0x1.23456789abcdfp+62)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+81 0x1.23456789abcdfp+81)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+100 0x1.23456789abcdfp+100)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+100 0x1.23456789abcdfp+100)) +(assert_return (invoke "f64x2.add" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+62 0x1.23456789abcdfp+62)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1021 0x1.0000000000000p-1021)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.fffffffffffffp-1022 0x0.fffffffffffffp-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.fffffffffffffp-1022 0x0.fffffffffffffp-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1021 -0x1.0000000000000p-1021)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000001p-1022 -0x1.0000000000001p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000001p-1022 -0x1.0000000000001p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.8000000000000p+0 0x1.8000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.721fb54442d18p+2 -0x1.721fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.b21fb54442d18p+2 0x1.b21fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.8000000000000p+0 -0x1.8000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.b21fb54442d18p+2 -0x1.b21fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.721fb54442d18p+2 0x1.721fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.8000000000000p+0 0x1.8000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+1 0x1.0000000000000p+1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.521fb54442d18p+2 -0x1.521fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.d21fb54442d18p+2 0x1.d21fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.8000000000000p+0 -0x1.8000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+1 -0x1.0000000000000p+1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.d21fb54442d18p+2 -0x1.d21fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.521fb54442d18p+2 0x1.521fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.721fb54442d18p+2 0x1.721fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.b21fb54442d18p+2 0x1.b21fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.521fb54442d18p+2 0x1.521fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.d21fb54442d18p+2 0x1.d21fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+3 0x1.921fb54442d18p+3)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.b21fb54442d18p+2 -0x1.b21fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.721fb54442d18p+2 -0x1.721fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.d21fb54442d18p+2 -0x1.d21fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.521fb54442d18p+2 -0x1.521fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+3 -0x1.921fb54442d18p+3)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x0.fffffffffffffp-1022 -0x0.fffffffffffffp-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000001p-1022 0x1.0000000000001p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x0.fffffffffffffp-1022 -0x0.fffffffffffffp-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000001p-1022 0x1.0000000000001p-1022)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sub" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.8000000000000p-1022 0x0.8000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x0.8000000000000p-1022 -0x0.8000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p-1020 0x1.921fb54442d18p-1020)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p-1020 -0x1.921fb54442d18p-1020)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1 0x1.fffffffffffffp+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1 -0x1.fffffffffffffp+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x0.8000000000000p-1022 -0x0.8000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x0.8000000000000p-1022 0x0.8000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p-1020 -0x1.921fb54442d18p-1020)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p-1020 0x1.921fb54442d18p-1020)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1 -0x1.fffffffffffffp+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1 0x1.fffffffffffffp+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.8000000000000p-1022 0x0.8000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x0.8000000000000p-1022 -0x0.8000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-2 0x1.0000000000000p-2)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-2 -0x1.0000000000000p-2)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+1 0x1.921fb54442d18p+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+1 -0x1.921fb54442d18p+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1022 0x1.fffffffffffffp+1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1022 -0x1.fffffffffffffp+1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x0.8000000000000p-1022 -0x0.8000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x0.8000000000000p-1022 0x0.8000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-2 -0x1.0000000000000p-2)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-2 0x1.0000000000000p-2)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+1 -0x1.921fb54442d18p+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+1 0x1.921fb54442d18p+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1022 -0x1.fffffffffffffp+1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1022 0x1.fffffffffffffp+1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.921fb54442d18p-1020 0x1.921fb54442d18p-1020)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p-1020 -0x1.921fb54442d18p-1020)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.921fb54442d18p+1 0x1.921fb54442d18p+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.921fb54442d18p+1 -0x1.921fb54442d18p+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.3bd3cc9be45dep+5 0x1.3bd3cc9be45dep+5)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.3bd3cc9be45dep+5 -0x1.3bd3cc9be45dep+5)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000006p-1022 0x0.0000000000006p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000006p-1022 0x0.0000000000006p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p-1020 -0x1.921fb54442d18p-1020)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.921fb54442d18p-1020 0x1.921fb54442d18p-1020)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.921fb54442d18p+1 -0x1.921fb54442d18p+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.921fb54442d18p+1 0x1.921fb54442d18p+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.3bd3cc9be45dep+5 -0x1.3bd3cc9be45dep+5)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.3bd3cc9be45dep+5 0x1.3bd3cc9be45dep+5)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0000000000006p-1022 -0x0.0000000000006p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0000000000006p-1022 -0x0.0000000000006p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1 0x1.fffffffffffffp+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1 -0x1.fffffffffffffp+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.fffffffffffffp+1022 0x1.fffffffffffffp+1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.fffffffffffffp+1022 -0x1.fffffffffffffp+1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fffffffffffffp-51 0x1.fffffffffffffp-51)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fffffffffffffp-51 0x1.fffffffffffffp-51)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1 -0x1.fffffffffffffp+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1 0x1.fffffffffffffp+1)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.fffffffffffffp+1022 -0x1.fffffffffffffp+1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.fffffffffffffp+1022 0x1.fffffffffffffp+1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp-51 -0x1.fffffffffffffp-51)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp-51 -0x1.fffffffffffffp-51)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0000000000006p-1022 0x0.0000000000006p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.0000000000006p-1022 -0x0.0000000000006p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp-51 0x1.fffffffffffffp-51)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp-51 -0x1.fffffffffffffp-51)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0000000000006p-1022 0x0.0000000000006p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.0000000000006p-1022 -0x0.0000000000006p-1022)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp-51 0x1.fffffffffffffp-51)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp-51 -0x1.fffffffffffffp-51)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 1.524157875019052e+16 1.524157875019052e+16)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 1.524157875019052e+54 1.524157875019052e+54)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 1.524157875019052e+54 1.524157875019052e+54)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 1.524157875019052e-22 1.524157875019052e-22)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 1.524157875019052e+16 1.524157875019052e+16)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 1.524157875019052e+54 1.524157875019052e+54)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 1.524157875019052e+54 1.524157875019052e+54)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 1.524157875019052e-22 1.524157875019052e-22)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 1.5241578753238834e+16 1.5241578753238834e+16)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 1.5241578753238838e+54 1.5241578753238838e+54)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 1.5241578753238838e+54 1.5241578753238838e+54)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 1.524157875323884e-22 1.524157875323884e-22)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.4b66dc33f6acep+160 0x1.4b66dc33f6acep+160)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.4b66dc33f6acep+198 0x1.4b66dc33f6acep+198)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.4b66dc33f6acep+198 0x1.4b66dc33f6acep+198)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.4b66dc33f6acep+122 0x1.4b66dc33f6acep+122)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.4b66dc33f6acep+160 0x1.4b66dc33f6acep+160)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.4b66dc33f6acep+198 0x1.4b66dc33f6acep+198)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.4b66dc33f6acep+198 0x1.4b66dc33f6acep+198)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.4b66dc33f6acep+122 0x1.4b66dc33f6acep+122)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.4b66dc33f6acep+160 0x1.4b66dc33f6acep+160)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.4b66dc33f6acep+198 0x1.4b66dc33f6acep+198)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.4b66dc33f6acep+198 0x1.4b66dc33f6acep+198)) +(assert_return (invoke "f64x2.mul" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.4b66dc33f6acep+122 0x1.4b66dc33f6acep+122)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1021 0x1.0000000000000p-1021)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1021 -0x1.0000000000000p-1021)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.28be60db93910p-1022 0x0.28be60db93910p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.28be60db93910p-1022 -0x0.28be60db93910p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p+52 0x1.0000000000000p+52)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p+52 0x1.0000000000000p+52)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1021 -0x1.0000000000000p-1021)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1021 0x1.0000000000000p-1021)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.28be60db93910p-1022 -0x0.28be60db93910p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.28be60db93910p-1022 0x0.28be60db93910p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p+52 -0x1.0000000000000p+52)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p+52 -0x1.0000000000000p+52)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p+1021 0x1.0000000000000p+1021)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p+1021 -0x1.0000000000000p+1021)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.45f306dc9c883p-4 0x1.45f306dc9c883p-4)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.45f306dc9c883p-4 -0x1.45f306dc9c883p-4)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.2000000000000p-1022 0x0.2000000000000p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.2000000000000p-1022 -0x0.2000000000000p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p+1021 -0x1.0000000000000p+1021)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p+1021 0x1.0000000000000p+1021)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.45f306dc9c883p-4 -0x1.45f306dc9c883p-4)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.45f306dc9c883p-4 0x1.45f306dc9c883p-4)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.2000000000000p-1022 -0x0.2000000000000p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.2000000000000p-1022 0x0.2000000000000p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p+1022 0x1.0000000000000p+1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p+1022 -0x1.0000000000000p+1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p+1 0x1.0000000000000p+1)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p+1 -0x1.0000000000000p+1)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.45f306dc9c883p-3 0x1.45f306dc9c883p-3)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.45f306dc9c883p-3 -0x1.45f306dc9c883p-3)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.4000000000000p-1022 0x0.4000000000000p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.4000000000000p-1022 -0x0.4000000000000p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p+1022 -0x1.0000000000000p+1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p+1022 0x1.0000000000000p+1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p+1 -0x1.0000000000000p+1)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p+1 0x1.0000000000000p+1)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.45f306dc9c883p-3 -0x1.45f306dc9c883p-3)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.45f306dc9c883p-3 0x1.45f306dc9c883p-3)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.4000000000000p-1022 -0x0.4000000000000p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.4000000000000p-1022 0x0.4000000000000p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.921fb54442d18p+3 0x1.921fb54442d18p+3)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.921fb54442d18p+3 -0x1.921fb54442d18p+3)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.921fb54442d19p-1022 0x1.921fb54442d19p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.921fb54442d19p-1022 -0x1.921fb54442d19p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.921fb54442d18p+3 -0x1.921fb54442d18p+3)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.921fb54442d18p+3 0x1.921fb54442d18p+3)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.921fb54442d19p-1022 -0x1.921fb54442d19p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.921fb54442d19p-1022 0x1.921fb54442d19p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.45f306dc9c882p+1021 0x1.45f306dc9c882p+1021)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.45f306dc9c882p+1021 -0x1.45f306dc9c882p+1021)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.45f306dc9c882p+1021 -0x1.45f306dc9c882p+1021)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.45f306dc9c882p+1021 0x1.45f306dc9c882p+1021)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-52 0x1.0000000000000p-52)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-52 -0x1.0000000000000p-52)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0000000000002p-1022 0x0.0000000000002p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x0.0000000000002p-1022 -0x0.0000000000002p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-52 0x1.0000000000000p-52)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-52 -0x1.0000000000000p-52)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0000000000002p-1022 0x0.0000000000002p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x0.0000000000002p-1022 -0x0.0000000000002p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.div" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-511 0x1.0000000000000p-511)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.6a09e667f3bcdp-1 0x1.6a09e667f3bcdp-1)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.40d931ff62705p+1 0x1.40d931ff62705p+1)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+511 0x1.fffffffffffffp+511)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p-537 0x1.0000000000000p-537)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p-537 0x1.0000000000000p-537)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 11111.111060555555 11111.111060555555)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 35136418286444.62 35136418286444.62)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 35136418286444.62 35136418286444.62)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 3.5136418286444623e-06 3.5136418286444623e-06)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 11111.111060555555 11111.111060555555)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 35136418286444.62 35136418286444.62)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 35136418286444.62 35136418286444.62)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 3.5136418286444623e-06 3.5136418286444623e-06)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 11111.11106111111 11111.11106111111)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 35136418288201.445 35136418288201.445)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 35136418288201.445 35136418288201.445)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 3.513641828820144e-06 3.513641828820144e-06)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.1111111111111p+40 0x1.1111111111111p+40)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.822cb17ff2eb8p+49 0x1.822cb17ff2eb8p+49)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.822cb17ff2eb8p+49 0x1.822cb17ff2eb8p+49)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.822cb17ff2eb8p+30 0x1.822cb17ff2eb8p+30)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.1111111111111p+40 0x1.1111111111111p+40)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.822cb17ff2eb8p+49 0x1.822cb17ff2eb8p+49)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.822cb17ff2eb8p+49 0x1.822cb17ff2eb8p+49)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.822cb17ff2eb8p+30 0x1.822cb17ff2eb8p+30)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.1111111111111p+40 0x1.1111111111111p+40)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.822cb17ff2eb8p+49 0x1.822cb17ff2eb8p+49)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.822cb17ff2eb8p+49 0x1.822cb17ff2eb8p+49)) +(assert_return (invoke "f64x2.sqrt" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.822cb17ff2eb8p+30 0x1.822cb17ff2eb8p+30)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 nan nan)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 -123456789.0 -123456789.0)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 -1.23456789e+27 -1.23456789e+27)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 -1.23456789e+27 -1.23456789e+27)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 -1.23456789e-11 -1.23456789e-11)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 -123456789.0 -123456789.0)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 -1.23456789e+27 -1.23456789e+27)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 -1.23456789e+27 -1.23456789e+27)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 -1.23456789e-11 -1.23456789e-11)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 -123456789.01234567 -123456789.01234567)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 -1.2345678901234569e+27 -1.2345678901234569e+27)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 -1.2345678901234569e+27 -1.2345678901234569e+27)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 -1.2345678901234568e-11 -1.2345678901234568e-11)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 -0x1.23456789abcdfp+80 -0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -0x1.23456789abcdfp+99 -0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -0x1.23456789abcdfp+99 -0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -0x1.23456789abcdfp+61 -0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 -0x1.23456789abcdfp+80 -0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 -0x1.23456789abcdfp+99 -0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 -0x1.23456789abcdfp+99 -0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 -0x1.23456789abcdfp+61 -0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 -0x1.23456789abcdfp+80 -0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -0x1.23456789abcdfp+99 -0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -0x1.23456789abcdfp+99 -0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.neg" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -0x1.23456789abcdfp+61 -0x1.23456789abcdfp+61)) + +;; Mixed f64x2 tests when some lanes are NaNs +(module + (func (export "f64x2_add_arith") (result v128) + (f64x2.add (v128.const f64x2 nan:0x8000000000000 1.0) (v128.const f64x2 nan 1.0))) + (func (export "f64x2_div_mixed") (result v128) + (f64x2.div (v128.const f64x2 nan 1.0) (v128.const f64x2 2.0 -nan:0x8000000000000))) + (func (export "f64x2_mul_mixed") (result v128) + (f64x2.mul (v128.const f64x2 nan:0x8000000000000 1.0) (v128.const f64x2 2.0 nan))) + (func (export "f64x2_neg_canon") (result v128) + (f64x2.neg (v128.const f64x2 nan 1.0))) + (func (export "f64x2_sqrt_canon") (result v128) + (f64x2.sqrt (v128.const f64x2 4.0 -nan))) + (func (export "f64x2_sub_arith") (result v128) + (f64x2.sub (v128.const f64x2 1.0 -1.0) (v128.const f64x2 -nan 1.0))) +) + +(assert_return (invoke "f64x2_add_arith") (v128.const f64x2 nan:arithmetic 2.0)) +(assert_return (invoke "f64x2_div_mixed") (v128.const f64x2 nan:canonical nan:arithmetic)) +(assert_return (invoke "f64x2_mul_mixed") (v128.const f64x2 nan:arithmetic nan:canonical)) +(assert_return (invoke "f64x2_neg_canon") (v128.const f64x2 nan:canonical -1.0)) +(assert_return (invoke "f64x2_sqrt_canon") (v128.const f64x2 2.0 nan:canonical)) +(assert_return (invoke "f64x2_sub_arith") (v128.const f64x2 nan:canonical -2.0)) + +;; type check +(assert_invalid (module (func (result v128) (f64x2.neg (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.sqrt (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.add (i64.const 0) (f64.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.sub (i64.const 0) (f64.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.mul (i64.const 0) (f64.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.div (i64.const 0) (f64.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $f64x2.neg-arg-empty (result v128) + (f64x2.neg) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.sqrt-arg-empty (result v128) + (f64x2.sqrt) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.add-1st-arg-empty (result v128) + (f64x2.add (v128.const f64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.add-arg-empty (result v128) + (f64x2.add) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.sub-1st-arg-empty (result v128) + (f64x2.sub (v128.const f64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.sub-arg-empty (result v128) + (f64x2.sub) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.mul-1st-arg-empty (result v128) + (f64x2.mul (v128.const f64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.mul-arg-empty (result v128) + (f64x2.mul) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.div-1st-arg-empty (result v128) + (f64x2.div (v128.const f64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.div-arg-empty (result v128) + (f64x2.div) + ) + ) + "type mismatch" +) + +;; combination +(module + (func (export "add-sub") (param v128 v128 v128) (result v128) + (f64x2.add (f64x2.sub (local.get 0) (local.get 1))(local.get 2))) + (func (export "div-add") (param v128 v128 v128) (result v128) + (f64x2.div (f64x2.add (local.get 0) (local.get 1))(local.get 2))) + (func (export "div-mul") (param v128 v128 v128) (result v128) + (f64x2.div (f64x2.mul (local.get 0) (local.get 1))(local.get 2))) + (func (export "div-sub") (param v128 v128 v128) (result v128) + (f64x2.div (f64x2.sub (local.get 0) (local.get 1))(local.get 2))) + (func (export "mul-add") (param v128 v128 v128) (result v128) + (f64x2.mul (f64x2.add (local.get 0) (local.get 1))(local.get 2))) + (func (export "mul-div") (param v128 v128 v128) (result v128) + (f64x2.mul (f64x2.div (local.get 0) (local.get 1))(local.get 2))) + (func (export "mul-sub") (param v128 v128 v128) (result v128) + (f64x2.mul (f64x2.sub (local.get 0) (local.get 1))(local.get 2))) + (func (export "sub-add") (param v128 v128 v128) (result v128) + (f64x2.sub (f64x2.add (local.get 0) (local.get 1))(local.get 2))) + (func (export "add-neg") (param v128 v128) (result v128) + (f64x2.add (f64x2.neg (local.get 0)) (local.get 1))) + (func (export "add-sqrt") (param v128 v128) (result v128) + (f64x2.add (f64x2.sqrt (local.get 0)) (local.get 1))) + (func (export "div-neg") (param v128 v128) (result v128) + (f64x2.div (f64x2.neg (local.get 0)) (local.get 1))) + (func (export "div-sqrt") (param v128 v128) (result v128) + (f64x2.div (f64x2.sqrt (local.get 0)) (local.get 1))) + (func (export "mul-neg") (param v128 v128) (result v128) + (f64x2.mul (f64x2.neg (local.get 0)) (local.get 1))) + (func (export "mul-sqrt") (param v128 v128) (result v128) + (f64x2.mul (f64x2.sqrt (local.get 0)) (local.get 1))) + (func (export "sub-neg") (param v128 v128) (result v128) + (f64x2.sub (f64x2.neg (local.get 0)) (local.get 1))) + (func (export "sub-sqrt") (param v128 v128) (result v128) + (f64x2.sub (f64x2.sqrt (local.get 0)) (local.get 1))) +) + +(assert_return (invoke "add-sub" (v128.const f64x2 1.125 1.125) + (v128.const f64x2 0.25 0.25) + (v128.const f64x2 0.125 0.125)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "div-add" (v128.const f64x2 1.125 1.125) + (v128.const f64x2 0.125 0.125) + (v128.const f64x2 0.25 0.25)) + (v128.const f64x2 5.0 5.0)) +(assert_return (invoke "div-mul" (v128.const f64x2 1.125 1.125) + (v128.const f64x2 4 4) + (v128.const f64x2 0.25 0.25)) + (v128.const f64x2 18.0 18.0)) +(assert_return (invoke "div-sub" (v128.const f64x2 1.125 1.125) + (v128.const f64x2 0.125 0.125) + (v128.const f64x2 0.25 0.25)) + (v128.const f64x2 4.0 4.0)) +(assert_return (invoke "mul-add" (v128.const f64x2 1.25 1.25) + (v128.const f64x2 0.25 0.25) + (v128.const f64x2 0.25 0.25)) + (v128.const f64x2 0.375 0.375)) +(assert_return (invoke "mul-div" (v128.const f64x2 1.125 1.125) + (v128.const f64x2 0.125 0.125) + (v128.const f64x2 0.25 0.25)) + (v128.const f64x2 2.25 2.25)) +(assert_return (invoke "mul-sub" (v128.const f64x2 1.125 1.125) + (v128.const f64x2 0.125 0.125) + (v128.const f64x2 0.25 0.25)) + (v128.const f64x2 0.25 0.25)) +(assert_return (invoke "sub-add" (v128.const f64x2 1.125 1.125) + (v128.const f64x2 0.25 0.25) + (v128.const f64x2 0.125 0.125)) + (v128.const f64x2 1.25 1.25)) +(assert_return (invoke "add-neg" (v128.const f64x2 1.125 1.125) + (v128.const f64x2 0.125 0.125)) + (v128.const f64x2 -1.0 -1.0)) +(assert_return (invoke "add-sqrt" (v128.const f64x2 2.25 2.25) + (v128.const f64x2 0.25 0.25)) + (v128.const f64x2 1.75 1.75)) +(assert_return (invoke "div-neg" (v128.const f64x2 1.5 1.5) + (v128.const f64x2 0.25 0.25)) + (v128.const f64x2 -6 -6)) +(assert_return (invoke "div-sqrt" (v128.const f64x2 2.25 2.25) + (v128.const f64x2 0.25 0.25)) + (v128.const f64x2 6 6)) +(assert_return (invoke "mul-neg" (v128.const f64x2 1.5 1.5) + (v128.const f64x2 0.25 0.25)) + (v128.const f64x2 -0.375 -0.375)) +(assert_return (invoke "mul-sqrt" (v128.const f64x2 2.25 2.25) + (v128.const f64x2 0.25 0.25)) + (v128.const f64x2 0.375 0.375)) +(assert_return (invoke "sub-neg" (v128.const f64x2 1.125 1.125) + (v128.const f64x2 0.125 0.125)) + (v128.const f64x2 -1.25 -1.25)) +(assert_return (invoke "sub-sqrt" (v128.const f64x2 2.25 2.25) + (v128.const f64x2 0.25 0.25)) + (v128.const f64x2 1.25 1.25)) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f64x2_cmp.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f64x2_cmp.wast new file mode 100644 index 000000000..cab815ea5 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f64x2_cmp.wast @@ -0,0 +1,8337 @@ +;; Tests for f64x2 comparison operations on major boundary values and all special values. + + +(module + (func (export "f64x2.eq") (param v128 v128) (result v128) (f64x2.eq (local.get 0) (local.get 1))) + (func (export "f64x2.ne") (param v128 v128) (result v128) (f64x2.ne (local.get 0) (local.get 1))) + (func (export "f64x2.lt") (param v128 v128) (result v128) (f64x2.lt (local.get 0) (local.get 1))) + (func (export "f64x2.le") (param v128 v128) (result v128) (f64x2.le (local.get 0) (local.get 1))) + (func (export "f64x2.gt") (param v128 v128) (result v128) (f64x2.gt (local.get 0) (local.get 1))) + (func (export "f64x2.ge") (param v128 v128) (result v128) (f64x2.ge (local.get 0) (local.get 1))) +) + +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1074 0x1p-1074) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1074 -0x1p-1074) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 inf inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 01234567890123456789e038 01234567890123456789e038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0123456789.e038 0123456789.e038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0123456789.e+038 0123456789.e+038) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e038 01234567890123456789e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789e-038 01234567890123456789e-038)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e038 0123456789.e038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 0123456789.e+038 0123456789.e+038)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789) + (v128.const f64x2 01234567890123456789.01234567890123456789 01234567890123456789.01234567890123456789)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1074 0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1074 -0x1p-1074)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -1 -1) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -1 -1) + (v128.const f64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -1 -1) + (v128.const f64x2 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 -1 -1) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0 0) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0 0) + (v128.const f64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0 0) + (v128.const f64x2 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 0 0) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 1 1) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 1 1) + (v128.const f64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 1 1) + (v128.const f64x2 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 1 1) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.eq" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -1 -1) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -1 -1) + (v128.const f64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -1 -1) + (v128.const f64x2 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 -1 -1) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0 0) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0 0) + (v128.const f64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0 0) + (v128.const f64x2 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 0 0) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 1 1) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 1 1) + (v128.const f64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 1 1) + (v128.const f64x2 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 1 1) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ne" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -1 -1) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -1 -1) + (v128.const f64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -1 -1) + (v128.const f64x2 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 -1 -1) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0 0) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0 0) + (v128.const f64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0 0) + (v128.const f64x2 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 0 0) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 1 1) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 1 1) + (v128.const f64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 1 1) + (v128.const f64x2 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 1 1) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.lt" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -1 -1) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -1 -1) + (v128.const f64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -1 -1) + (v128.const f64x2 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 -1 -1) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0 0) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0 0) + (v128.const f64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0 0) + (v128.const f64x2 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 0 0) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 1 1) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 1 1) + (v128.const f64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 1 1) + (v128.const f64x2 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 1 1) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.le" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -1 -1) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -1 -1) + (v128.const f64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -1 -1) + (v128.const f64x2 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 -1 -1) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0 0) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0 0) + (v128.const f64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0 0) + (v128.const f64x2 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 0 0) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 1 1) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 1 1) + (v128.const f64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 1 1) + (v128.const f64x2 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 1 1) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.gt" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -1 -1) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -1 -1) + (v128.const f64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -1 -1) + (v128.const f64x2 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 -1 -1) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0 0) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0 0) + (v128.const f64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0 0) + (v128.const f64x2 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 0 0) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 1 1) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 1 1) + (v128.const f64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 1 1) + (v128.const f64x2 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 1 1) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "f64x2.ge" (v128.const f64x2 2.0 2.0) + (v128.const f64x2 2.0 2.0)) + (v128.const i64x2 -1 -1)) + + +;; unknown operators +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (f2x64.eq (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (f2x64.ne (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (f2x64.lt (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (f2x64.le (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (f2x64.gt (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (f2x64.ge (local.get $x) (local.get $y)))") "unknown operator") + +;; type check +(assert_invalid (module (func (result v128) (f64x2.eq (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.ne (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.lt (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.le (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.gt (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.ge (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $f64x2.eq-1st-arg-empty (result v128) + (f64x2.eq (v128.const f64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.eq-arg-empty (result v128) + (f64x2.eq) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.ne-1st-arg-empty (result v128) + (f64x2.ne (v128.const f64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.ne-arg-empty (result v128) + (f64x2.ne) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.lt-1st-arg-empty (result v128) + (f64x2.lt (v128.const f64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.lt-arg-empty (result v128) + (f64x2.lt) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.le-1st-arg-empty (result v128) + (f64x2.le (v128.const f64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.le-arg-empty (result v128) + (f64x2.le) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.gt-1st-arg-empty (result v128) + (f64x2.gt (v128.const f64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.gt-arg-empty (result v128) + (f64x2.gt) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.ge-1st-arg-empty (result v128) + (f64x2.ge (v128.const f64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.ge-arg-empty (result v128) + (f64x2.ge) + ) + ) + "type mismatch" +) + +;; combination +(module (memory 1) + (func (export "f64x2.eq-in-block") + (block + (drop + (block (result v128) + (f64x2.eq + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "f64x2.ne-in-block") + (block + (drop + (block (result v128) + (f64x2.ne + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "f64x2.lt-in-block") + (block + (drop + (block (result v128) + (f64x2.lt + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "f64x2.le-in-block") + (block + (drop + (block (result v128) + (f64x2.le + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "f64x2.gt-in-block") + (block + (drop + (block (result v128) + (f64x2.gt + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "f64x2.ge-in-block") + (block + (drop + (block (result v128) + (f64x2.ge + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "nested-f64x2.eq") + (drop + (f64x2.eq + (f64x2.eq + (f64x2.eq + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f64x2.eq + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (f64x2.eq + (f64x2.eq + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f64x2.eq + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-f64x2.ne") + (drop + (f64x2.ne + (f64x2.ne + (f64x2.ne + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f64x2.ne + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (f64x2.ne + (f64x2.ne + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f64x2.ne + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-f64x2.lt") + (drop + (f64x2.lt + (f64x2.lt + (f64x2.lt + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f64x2.lt + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (f64x2.lt + (f64x2.lt + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f64x2.lt + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-f64x2.le") + (drop + (f64x2.le + (f64x2.le + (f64x2.le + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f64x2.le + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (f64x2.le + (f64x2.le + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f64x2.le + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-f64x2.gt") + (drop + (f64x2.gt + (f64x2.gt + (f64x2.gt + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f64x2.gt + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (f64x2.gt + (f64x2.gt + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f64x2.gt + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-f64x2.ge") + (drop + (f64x2.ge + (f64x2.ge + (f64x2.ge + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f64x2.ge + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (f64x2.ge + (f64x2.ge + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f64x2.ge + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "as-param") + (drop + (f64x2.eq + (f64x2.ne + (f64x2.lt + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f64x2.le + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (f64x2.gt + (f64x2.ge + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (f64x2.eq + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) +) +(assert_return (invoke "f64x2.eq-in-block")) +(assert_return (invoke "f64x2.ne-in-block")) +(assert_return (invoke "f64x2.lt-in-block")) +(assert_return (invoke "f64x2.le-in-block")) +(assert_return (invoke "f64x2.gt-in-block")) +(assert_return (invoke "f64x2.ge-in-block")) +(assert_return (invoke "nested-f64x2.eq")) +(assert_return (invoke "nested-f64x2.ne")) +(assert_return (invoke "nested-f64x2.lt")) +(assert_return (invoke "nested-f64x2.le")) +(assert_return (invoke "nested-f64x2.gt")) +(assert_return (invoke "nested-f64x2.ge")) +(assert_return (invoke "as-param")) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f64x2_pmin_pmax.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f64x2_pmin_pmax.wast new file mode 100644 index 000000000..e8167b0e5 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f64x2_pmin_pmax.wast @@ -0,0 +1,11676 @@ +;; Tests for f64x2 [pmin, pmax] operations on major boundary values and all special values. + + +(module + (func (export "f64x2.pmin") (param v128 v128) (result v128) (f64x2.pmin (local.get 0) (local.get 1))) + (func (export "f64x2.pmax") (param v128 v128) (result v128) (f64x2.pmax (local.get 0) (local.get 1))) +) + +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0p+0 0x0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x1p-1022 0x1p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x1p-1 0x1p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x1p+0 0x1p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 nan nan)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789 0123456789)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789e019 0123456789e019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789. 0123456789.)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0p+0 0x0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x1p-1022 0x1p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x1p-1 0x1p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x1p+0 0x1p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789 0123456789)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789e019 0123456789e019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789. 0123456789.)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0p+0 0x0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x1p-1022 0x1p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x1p-1 0x1p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x1p+0 0x1p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789 0123456789)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789e019 0123456789e019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789. 0123456789.)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0p+0 0x0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x1p-1022 0x1p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x1p-1 0x1p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x1p+0 0x1p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789 0123456789)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789e019 0123456789e019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789. 0123456789.)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmin" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p-1022 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p-1022 -0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p-1 -0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd62b4311p-37 0x1.b25ffd62b4311p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p-1 0x1.0000000000000p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.d6f3454000000p+26 0x1.d6f3454000000p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.d6f34540ca458p+26 0x1.d6f34540ca458p+26)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.b25ffd636ec12p-37 0x1.b25ffd636ec12p-37)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 0x1.fe9af5b5e16fap+89 0x1.fe9af5b5e16fap+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 0x1.fe9af5b6bcbd5p+89 0x1.fe9af5b6bcbd5p+89)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 0x1.23456789abcdfp+80 0x1.23456789abcdfp+80)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 0x1.23456789abcdfp+99 0x1.23456789abcdfp+99)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 0x1.23456789abcdfp+61 0x1.23456789abcdfp+61)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan -nan) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 inf inf)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0p+0 0x0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x1p-1022 0x1p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x1p-1 0x1p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x1p+0 0x1p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 nan nan)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan nan)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789 0123456789)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789e019 0123456789e019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789. 0123456789.)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 nan nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0p+0 0x0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x1p-1022 0x1p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x1p-1 0x1p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x1p+0 0x1p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789 0123456789)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789e019 0123456789e019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789. 0123456789.)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -nan -nan)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0p+0 0x0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x1p-1022 0x1p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x1p-1 0x1p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x1p+0 0x1p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789 0123456789)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789e019 0123456789e019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789. 0123456789.)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0p+0 0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0p+0 0x0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x0p+0 -0x0p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -0x0p+0 -0x0p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1022 0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x1p-1022 0x1p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1022 -0x1p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -0x1p-1022 -0x1p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p-1 0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x1p-1 0x1p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p-1 -0x1p-1) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1p+0 0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x1p+0 0x1p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1p+0 -0x1p+0) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 inf inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 -inf -inf) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789 0123456789) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789 0123456789)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e019 0123456789e019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789e019 0123456789e019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e+019 0123456789e+019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789e-019 0123456789e-019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789e-019 0123456789e-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789. 0123456789.) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789. 0123456789.)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e019 0123456789.e019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789.e019 0123456789.e019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e+019 0123456789.e+019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.e-019 0123456789.e-019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789.e-019 0123456789.e-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) +(assert_return (invoke "f64x2.pmax" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019) + (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + + +;; Unknown operators + +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.pmin (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.pmax (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.pmin (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.pmax (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.pmin (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.pmax (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.pmin (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.pmax (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") "unknown operator") + +;; type check +(assert_invalid (module (func (result v128) (f64x2.pmin (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.pmax (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $f64x2.pmin-1st-arg-empty (result v128) + (f64x2.pmin (v128.const f64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.pmin-arg-empty (result v128) + (f64x2.pmin) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.pmax-1st-arg-empty (result v128) + (f64x2.pmax (v128.const f64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.pmax-arg-empty (result v128) + (f64x2.pmax) + ) + ) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f64x2_rounding.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f64x2_rounding.wast new file mode 100644 index 000000000..f82c6d294 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_f64x2_rounding.wast @@ -0,0 +1,424 @@ +;; Tests for f64x2 [ceil, floor, trunc, nearest] operations on major boundary values and all special values. + + +(module + (func (export "f64x2.ceil") (param v128) (result v128) (f64x2.ceil (local.get 0))) + (func (export "f64x2.floor") (param v128) (result v128) (f64x2.floor (local.get 0))) + (func (export "f64x2.trunc") (param v128) (result v128) (f64x2.trunc (local.get 0))) + (func (export "f64x2.nearest") (param v128) (result v128) (f64x2.nearest (local.get 0))) +) + +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.c000000000000p+2 0x1.c000000000000p+2)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.8000000000000p+2 -0x1.8000000000000p+2)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 123456789.0 123456789.0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 123456789.0 123456789.0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 123456790.0 123456790.0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 1.0 1.0)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 1.3754889325393114e+24 1.3754889325393114e+24)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 2.6235369349275807e+18 2.6235369349275807e+18)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 1.3754889325393114e+24 1.3754889325393114e+24)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 2.6235369349275807e+18 2.6235369349275807e+18)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 1.3754889325393114e+24 1.3754889325393114e+24)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 2.6235369349275807e+18 2.6235369349275807e+18)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.ceil" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.8000000000000p+2 0x1.8000000000000p+2)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.c000000000000p+2 -0x1.c000000000000p+2)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 123456789.0 123456789.0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 123456789.0 123456789.0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 123456789.0 123456789.0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 1.3754889325393114e+24 1.3754889325393114e+24)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 2.6235369349275807e+18 2.6235369349275807e+18)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 1.3754889325393114e+24 1.3754889325393114e+24)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 2.6235369349275807e+18 2.6235369349275807e+18)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 1.3754889325393114e+24 1.3754889325393114e+24)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 2.6235369349275807e+18 2.6235369349275807e+18)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.floor" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.8000000000000p+2 0x1.8000000000000p+2)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.8000000000000p+2 -0x1.8000000000000p+2)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 123456789.0 123456789.0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 123456789.0 123456789.0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 123456789.0 123456789.0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 1.3754889325393114e+24 1.3754889325393114e+24)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 2.6235369349275807e+18 2.6235369349275807e+18)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 1.3754889325393114e+24 1.3754889325393114e+24)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 2.6235369349275807e+18 2.6235369349275807e+18)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 1.3754889325393114e+24 1.3754889325393114e+24)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 2.6235369349275807e+18 2.6235369349275807e+18)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.trunc" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x0p+0 0x0p+0)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 -0x0p+0 -0x0p+0)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x1p-1022 0x1p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 -0x1p-1022 -0x1p-1022)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const f64x2 -0x0.0p+0 -0x0.0p+0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const f64x2 0x1.0000000000000p+0 0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const f64x2 -0x1.0000000000000p+0 -0x1.0000000000000p+0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) + (v128.const f64x2 0x1.8000000000000p+2 0x1.8000000000000p+2)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) + (v128.const f64x2 -0x1.8000000000000p+2 -0x1.8000000000000p+2)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) + (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) + (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) + (v128.const f64x2 0x0.0p+0 0x0.0p+0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 inf inf)) + (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 -inf -inf)) + (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0123456789 0123456789)) + (v128.const f64x2 123456789.0 123456789.0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0123456789e019 0123456789e019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0123456789e+019 0123456789e+019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0123456789e-019 0123456789e-019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0123456789. 0123456789.)) + (v128.const f64x2 123456789.0 123456789.0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0123456789.e019 0123456789.e019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0123456789.e+019 0123456789.e+019)) + (v128.const f64x2 1.23456789e+27 1.23456789e+27)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0123456789.e-019 0123456789.e-019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0123456789.0123456789 0123456789.0123456789)) + (v128.const f64x2 123456789.0 123456789.0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0123456789.0123456789e019 0123456789.0123456789e019)) + (v128.const f64x2 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0123456789.0123456789e+019 0123456789.0123456789e+019)) + (v128.const f64x2 1.2345678901234569e+27 1.2345678901234569e+27)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0123456789.0123456789e-019 0123456789.0123456789e-019)) + (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) + (v128.const f64x2 1.3754889325393114e+24 1.3754889325393114e+24)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdefp019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 2.6235369349275807e+18 2.6235369349275807e+18)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) + (v128.const f64x2 1.3754889325393114e+24 1.3754889325393114e+24)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x0123456789ABCDEFabcdef.p+019 0x0123456789ABCDEFabcdef.p+019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + (v128.const f64x2 2.6235369349275807e+18 2.6235369349275807e+18)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdef)) + (v128.const f64x2 1.3754889325393114e+24 1.3754889325393114e+24)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp+019)) + (v128.const f64x2 7.211523414631705e+29 7.211523414631705e+29)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) + (v128.const f64x2 2.6235369349275807e+18 2.6235369349275807e+18)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 nan nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 -nan -nan)) + (v128.const f64x2 nan:canonical nan:canonical)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) +(assert_return (invoke "f64x2.nearest" (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) + (v128.const f64x2 nan:arithmetic nan:arithmetic)) + + +;; Unknown operators + +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.ceil (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.floor (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.trunc (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.nearest (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.ceil (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.floor (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.trunc (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.nearest (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.ceil (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.floor (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.trunc (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.nearest (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.ceil (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.floor (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.trunc (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.nearest (v128.const i32x4 0 0 0 0)))") "unknown operator") + +;; type check +(assert_invalid (module (func (result v128) (f64x2.ceil (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.floor (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.trunc (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.nearest (i32.const 0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $f64x2.ceil-arg-empty (result v128) + (f64x2.ceil) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.floor-arg-empty (result v128) + (f64x2.floor) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.trunc-arg-empty (result v128) + (f64x2.trunc) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.nearest-arg-empty (result v128) + (f64x2.nearest) + ) + ) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_arith.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_arith.wast new file mode 100644 index 000000000..3dc4d5ed7 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_arith.wast @@ -0,0 +1,634 @@ +;; Tests for i16x8 arithmetic operations on major boundary values and all special values. + + +(module + (func (export "i16x8.add") (param v128 v128) (result v128) (i16x8.add (local.get 0) (local.get 1))) + (func (export "i16x8.sub") (param v128 v128) (result v128) (i16x8.sub (local.get 0) (local.get 1))) + (func (export "i16x8.mul") (param v128 v128) (result v128) (i16x8.mul (local.get 0) (local.get 1))) + (func (export "i16x8.neg") (param v128) (result v128) (i16x8.neg (local.get 0))) +) + + +;; i16x8.add +(assert_return (invoke "i16x8.add" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 16383 16383 16383 16383 16383 16383 16383 16383) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 -16383 -16383 -16383 -16383 -16383 -16383 -16383 -16383) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 -16385 -16385 -16385 -16385 -16385 -16385 -16385 -16385) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff) + (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000) + (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff) + (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000)) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000) + (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000) + (v128.const i16x8 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i8x16 0 0x80 0 0x80 0 0x80 0 0x80 0 0x80 0 0x80 0 0x80 0 0x80)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i32x4 0x80008000 0x80008000 0x80008000 0x80008000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 +0.0 +0.0 +0.0 +0.0)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i16x8 0x8000 0 0x8000 0 0x8000 0 0x8000 0)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 1.0 1.0 1.0 1.0)) + (v128.const i16x8 0x8000 0xbf80 0x8000 0xbf80 0x8000 0xbf80 0x8000 0xbf80)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 -1.0 -1.0 -1.0 -1.0)) + (v128.const i16x8 0x8000 0x3f80 0x8000 0x3f80 0x8000 0x3f80 0x8000 0x3f80)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i16x8 0x01 0x7f81 0x01 0x7f81 0x01 0x7f81 0x01 0x7f81)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i16x8 0x01 0xff81 0x01 0xff81 0x01 0xff81 0x01 0xff81)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i16x8 0x01 0x7fc1 0x01 0x7fc1 0x01 0x7fc1 0x01 0x7fc1)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 0xffff 0xfffe 0xfffd 0xfffc 0xfffb 0xfffa 0xfff9)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 2 4 6 8 10 12 14)) + (v128.const i16x8 0 3 6 9 12 15 18 21)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 056_789 056_789 056_789 056_789 056_789 056_789 056_789 056_789)) + (v128.const i16x8 03_598 03_598 03_598 03_598 03_598 03_598 03_598 03_598)) +(assert_return (invoke "i16x8.add" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678)) + (v128.const i16x8 0x0_68ac 0x0_68ac 0x0_68ac 0x0_68ac 0x0_68ac 0x0_68ac 0x0_68ac 0x0_68ac)) + +;; i16x8.sub +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 16383 16383 16383 16383 16383 16383 16383 16383) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 -16383 -16383 -16383 -16383 -16383 -16383 -16383 -16383) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 -16385 -16385 -16385 -16385 -16385 -16385 -16385 -16385) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32764 32764 32764 32764 32764 32764 32764 32764)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32765 -32765 -32765 -32765 -32765 -32765 -32765 -32765)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff) + (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000) + (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff) + (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000) + (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000) + (v128.const i16x8 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01)) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i8x16 0 0x80 0 0x80 0 0x80 0 0x80 0 0x80 0 0x80 0 0x80 0 0x80)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i32x4 0x80008000 0x80008000 0x80008000 0x80008000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i16x8 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 +0.0 +0.0 +0.0 +0.0)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i16x8 0x8000 0 0x8000 0 0x8000 0 0x8000 0)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 1.0 1.0 1.0 1.0)) + (v128.const i16x8 0x8000 0x4080 0x8000 0x4080 0x8000 0x4080 0x8000 0x4080)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 -1.0 -1.0 -1.0 -1.0)) + (v128.const i16x8 0x8000 0xc080 0x8000 0xc080 0x8000 0xc080 0x8000 0xc080)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i16x8 0x01 0x8081 0x01 0x8081 0x01 0x8081 0x01 0x8081)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i16x8 0x01 0x81 0x01 0x81 0x01 0x81 0x01 0x81)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i16x8 0x01 0x8041 0x01 0x8041 0x01 0x8041 0x01 0x8041)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 0xffff 0xfffe 0xfffd 0xfffc 0xfffb 0xfffa 0xfff9)) + (v128.const i16x8 0 0x02 0x04 0x06 0x08 0x0a 0x0c 0x0e)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 2 4 6 8 10 12 14)) + (v128.const i16x8 0 -1 -2 -3 -4 -5 -6 -7)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 056_789 056_789 056_789 056_789 056_789 056_789 056_789 056_789) + (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345)) + (v128.const i16x8 044_444 044_444 044_444 044_444 044_444 044_444 044_444 044_444)) +(assert_return (invoke "i16x8.sub" (v128.const i16x8 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678) + (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234)) + (v128.const i16x8 0x0_4444 0x0_4444 0x0_4444 0x0_4444 0x0_4444 0x0_4444 0x0_4444 0x0_4444)) + +;; i16x8.mul +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 16383 16383 16383 16383 16383 16383 16383 16383) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 -16383 -16383 -16383 -16383 -16383 -16383 -16383 -16383) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 -16385 -16385 -16385 -16385 -16385 -16385 -16385 -16385) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff) + (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000)) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000) + (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff) + (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000)) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000) + (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000) + (v128.const i16x8 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001)) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0x1000 0x1000 0x1000 0x1000 0x1000 0x1000 0x1000 0x1000) + (v128.const i8x16 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i32x4 0x00020002 0x00020002 0x00020002 0x00020002)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 +0.0 +0.0 +0.0 +0.0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 1.0 1.0 1.0 1.0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 -1.0 -1.0 -1.0 -1.0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i16x8 0 0x7f80 0 0x7f80 0 0x7f80 0 0x7f80)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i16x8 0 0xff80 0 0xff80 0 0xff80 0 0xff80)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i16x8 0 0x7fc0 0 0x7fc0 0 0x7fc0 0 0x7fc0)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 0xffff 0xfffe 0xfffd 0xfffc 0xfffb 0xfffa 0xfff9)) + (v128.const i16x8 0 0xffff 0xfffc 0xfff7 0xfff0 0xffe7 0xffdc 0xffcf)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 2 4 6 8 10 12 14)) + (v128.const i16x8 0 0x02 0x08 0x12 0x20 0x32 0x48 0x62)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 056_789 056_789 056_789 056_789 056_789 056_789 056_789 056_789)) + (v128.const i16x8 021_613 021_613 021_613 021_613 021_613 021_613 021_613 021_613)) +(assert_return (invoke "i16x8.mul" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef)) + (v128.const i16x8 0x0_a28c 0x0_a28c 0x0_a28c 0x0_a28c 0x0_a28c 0x0_a28c 0x0_a28c 0x0_a28c)) + +;; i16x8.neg +(assert_return (invoke "i16x8.neg" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.neg" (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.neg" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.neg" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766)) + (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766)) +(assert_return (invoke "i16x8.neg" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.neg" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.neg" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i16x8.neg" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.neg" (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.neg" (v128.const i16x8 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.neg" (v128.const i16x8 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.neg" (v128.const i16x8 -0x7fff -0x7fff -0x7fff -0x7fff -0x7fff -0x7fff -0x7fff -0x7fff)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.neg" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i16x8.neg" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.neg" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + +;; type check +(assert_invalid (module (func (result v128) (i16x8.neg (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.add (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.sub (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.mul (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i16x8.neg-arg-empty (result v128) + (i16x8.neg) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.add-1st-arg-empty (result v128) + (i16x8.add (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.add-arg-empty (result v128) + (i16x8.add) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.sub-1st-arg-empty (result v128) + (i16x8.sub (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.sub-arg-empty (result v128) + (i16x8.sub) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.mul-1st-arg-empty (result v128) + (i16x8.mul (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.mul-arg-empty (result v128) + (i16x8.mul) + ) + ) + "type mismatch" +) + +;; combination +(module + (func (export "add-sub") (param v128 v128 v128) (result v128) + (i16x8.add (i16x8.sub (local.get 0) (local.get 1))(local.get 2))) + (func (export "mul-add") (param v128 v128 v128) (result v128) + (i16x8.mul (i16x8.add (local.get 0) (local.get 1))(local.get 2))) + (func (export "mul-sub") (param v128 v128 v128) (result v128) + (i16x8.mul (i16x8.sub (local.get 0) (local.get 1))(local.get 2))) + (func (export "sub-add") (param v128 v128 v128) (result v128) + (i16x8.sub (i16x8.add (local.get 0) (local.get 1))(local.get 2))) + (func (export "add-neg") (param v128 v128) (result v128) + (i16x8.add (i16x8.neg (local.get 0)) (local.get 1))) + (func (export "mul-neg") (param v128 v128) (result v128) + (i16x8.mul (i16x8.neg (local.get 0)) (local.get 1))) + (func (export "sub-neg") (param v128 v128) (result v128) + (i16x8.sub (i16x8.neg (local.get 0)) (local.get 1))) +) + +(assert_return (invoke "add-sub" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 2 4 6 8 10 12 14) + (v128.const i16x8 0 2 4 6 8 10 12 14)) + (v128.const i16x8 0 1 2 3 4 5 6 7)) +(assert_return (invoke "mul-add" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 0 4 8 12 16 20 24 28)) +(assert_return (invoke "mul-sub" (v128.const i16x8 0 2 4 6 8 10 12 14) + (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 1 2 3 4 5 6 7)) + (v128.const i16x8 0 1 4 9 16 25 36 49)) +(assert_return (invoke "sub-add" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 2 4 6 8 10 12 14) + (v128.const i16x8 0 2 4 6 8 10 12 14)) + (v128.const i16x8 0 1 2 3 4 5 6 7)) +(assert_return (invoke "add-neg" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 1 2 3 4 5 6 7)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "mul-neg" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 0 -2 -4 -6 -8 -10 -12 -14)) +(assert_return (invoke "sub-neg" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 1 2 3 4 5 6 7)) + (v128.const i16x8 0 -2 -4 -6 -8 -10 -12 -14)) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_arith2.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_arith2.wast new file mode 100644 index 000000000..454f598df --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_arith2.wast @@ -0,0 +1,610 @@ +;; Tests for i16x8 [min_s, min_u, max_s, max_u, avgr_u, abs] operations. + +(module + (func (export "i16x8.min_s") (param v128 v128) (result v128) (i16x8.min_s (local.get 0) (local.get 1))) + (func (export "i16x8.min_u") (param v128 v128) (result v128) (i16x8.min_u (local.get 0) (local.get 1))) + (func (export "i16x8.max_s") (param v128 v128) (result v128) (i16x8.max_s (local.get 0) (local.get 1))) + (func (export "i16x8.max_u") (param v128 v128) (result v128) (i16x8.max_u (local.get 0) (local.get 1))) + (func (export "i16x8.avgr_u") (param v128 v128) (result v128) (i16x8.avgr_u (local.get 0) (local.get 1))) + (func (export "i16x8.abs") (param v128) (result v128) (i16x8.abs (local.get 0))) + (func (export "i16x8.min_s_with_const_0") (result v128) (i16x8.min_s (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535) (v128.const i16x8 65535 65535 16384 16384 32767 32767 -32768 -32768))) + (func (export "i16x8.min_s_with_const_1") (result v128) (i16x8.min_s (v128.const i16x8 0 0 1 1 2 2 3 3) (v128.const i16x8 3 3 2 2 1 1 0 0))) + (func (export "i16x8.min_u_with_const_2") (result v128) (i16x8.min_u (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535) (v128.const i16x8 65535 65535 16384 16384 32767 32767 -32768 -32768))) + (func (export "i16x8.min_u_with_const_3") (result v128) (i16x8.min_u (v128.const i16x8 0 0 1 1 2 2 3 3) (v128.const i16x8 3 3 2 2 1 1 0 0))) + (func (export "i16x8.max_s_with_const_4") (result v128) (i16x8.max_s (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535) (v128.const i16x8 65535 65535 16384 16384 32767 32767 -32768 -32768))) + (func (export "i16x8.max_s_with_const_5") (result v128) (i16x8.max_s (v128.const i16x8 0 0 1 1 2 2 3 3) (v128.const i16x8 3 3 2 2 1 1 0 0))) + (func (export "i16x8.max_u_with_const_6") (result v128) (i16x8.max_u (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535) (v128.const i16x8 65535 65535 16384 16384 32767 32767 -32768 -32768))) + (func (export "i16x8.max_u_with_const_7") (result v128) (i16x8.max_u (v128.const i16x8 0 0 1 1 2 2 3 3) (v128.const i16x8 3 3 2 2 1 1 0 0))) + (func (export "i16x8.avgr_u_with_const_8") (result v128) (i16x8.avgr_u (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535) (v128.const i16x8 65535 65535 16384 16384 32767 32767 -32768 -32768))) + (func (export "i16x8.avgr_u_with_const_9") (result v128) (i16x8.avgr_u (v128.const i16x8 0 0 1 1 2 2 3 3) (v128.const i16x8 3 3 2 2 1 1 0 0))) + (func (export "i16x8.abs_with_const_10") (result v128) (i16x8.abs (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535))) + (func (export "i16x8.min_s_with_const_11") (param v128) (result v128) (i16x8.min_s (local.get 0) (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535))) + (func (export "i16x8.min_s_with_const_12") (param v128) (result v128) (i16x8.min_s (local.get 0) (v128.const i16x8 0 0 1 1 2 2 3 3))) + (func (export "i16x8.min_u_with_const_13") (param v128) (result v128) (i16x8.min_u (local.get 0) (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535))) + (func (export "i16x8.min_u_with_const_14") (param v128) (result v128) (i16x8.min_u (local.get 0) (v128.const i16x8 0 0 1 1 2 2 3 3))) + (func (export "i16x8.max_s_with_const_15") (param v128) (result v128) (i16x8.max_s (local.get 0) (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535))) + (func (export "i16x8.max_s_with_const_16") (param v128) (result v128) (i16x8.max_s (local.get 0) (v128.const i16x8 0 0 1 1 2 2 3 3))) + (func (export "i16x8.max_u_with_const_17") (param v128) (result v128) (i16x8.max_u (local.get 0) (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535))) + (func (export "i16x8.max_u_with_const_18") (param v128) (result v128) (i16x8.max_u (local.get 0) (v128.const i16x8 0 0 1 1 2 2 3 3))) + (func (export "i16x8.avgr_u_with_const_19") (param v128) (result v128) (i16x8.avgr_u (local.get 0) (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535))) + (func (export "i16x8.avgr_u_with_const_20") (param v128) (result v128) (i16x8.avgr_u (local.get 0) (v128.const i16x8 0 0 1 1 2 2 3 3))) +) + +(assert_return (invoke "i16x8.min_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.min_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.min_s" (v128.const i16x8 0 0 0 0 -1 -1 -1 -1) + (v128.const i16x8 0 0 -1 -1 0 0 -1 -1)) + (v128.const i16x8 0 0 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.min_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "i16x8.min_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.min_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.min_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 128 128 128 128 128 128 128 128)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.min_s" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768)) +(assert_return (invoke "i16x8.min_s" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.min_s" (v128.const i16x8 123 123 123 123 123 123 123 123) + (v128.const i16x8 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i16x8 123 123 123 123 123 123 123 123)) +(assert_return (invoke "i16x8.min_s" (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i16x8 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i16x8.min_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.min_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.min_u" (v128.const i16x8 0 0 0 0 -1 -1 -1 -1) + (v128.const i16x8 0 0 -1 -1 0 0 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 -1 -1)) +(assert_return (invoke "i16x8.min_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.min_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.min_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.min_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 128 128 128 128 128 128 128 128)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i16x8.min_u" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768)) +(assert_return (invoke "i16x8.min_u" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.min_u" (v128.const i16x8 123 123 123 123 123 123 123 123) + (v128.const i16x8 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i16x8 123 123 123 123 123 123 123 123)) +(assert_return (invoke "i16x8.min_u" (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i16x8 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i16x8.max_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.max_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.max_s" (v128.const i16x8 0 0 0 0 -1 -1 -1 -1) + (v128.const i16x8 0 0 -1 -1 0 0 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 -1 -1)) +(assert_return (invoke "i16x8.max_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.max_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.max_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.max_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 128 128 128 128 128 128 128 128)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i16x8.max_s" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768)) +(assert_return (invoke "i16x8.max_s" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.max_s" (v128.const i16x8 123 123 123 123 123 123 123 123) + (v128.const i16x8 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i16x8 123 123 123 123 123 123 123 123)) +(assert_return (invoke "i16x8.max_s" (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i16x8 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i16x8.max_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.max_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.max_u" (v128.const i16x8 0 0 0 0 -1 -1 -1 -1) + (v128.const i16x8 0 0 -1 -1 0 0 -1 -1)) + (v128.const i16x8 0 0 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.max_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "i16x8.max_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.max_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.max_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 128 128 128 128 128 128 128 128)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.max_u" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768)) +(assert_return (invoke "i16x8.max_u" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.max_u" (v128.const i16x8 123 123 123 123 123 123 123 123) + (v128.const i16x8 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i16x8 123 123 123 123 123 123 123 123)) +(assert_return (invoke "i16x8.max_u" (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i16x8 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i16x8.avgr_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.avgr_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768)) +(assert_return (invoke "i16x8.avgr_u" (v128.const i16x8 0 0 0 0 -1 -1 -1 -1) + (v128.const i16x8 0 0 -1 -1 0 0 -1 -1)) + (v128.const i16x8 0 0 32768 32768 32768 32768 65535 65535)) +(assert_return (invoke "i16x8.avgr_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.avgr_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.avgr_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768)) +(assert_return (invoke "i16x8.avgr_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 128 128 128 128 128 128 128 128)) + (v128.const i16x8 32832 32832 32832 32832 32832 32832 32832 32832)) +(assert_return (invoke "i16x8.avgr_u" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768)) +(assert_return (invoke "i16x8.avgr_u" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.avgr_u" (v128.const i16x8 123 123 123 123 123 123 123 123) + (v128.const i16x8 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i16x8 123 123 123 123 123 123 123 123)) +(assert_return (invoke "i16x8.avgr_u" (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i16x8 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0x1 0x1 0x1 0x1 0x1 0x1 0x1 0x1)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768)) + (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000 -0x8000)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i16x8 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3)) + (v128.const i16x8 123 123 123 123 123 123 123 123)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80)) + (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i16x8 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0)) + (v128.const i16x8 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + +;; Const vs const +(assert_return (invoke "i16x8.min_s_with_const_0") (v128.const i16x8 -32768 -32768 16384 16384 16384 16384 -32768 -32768)) +(assert_return (invoke "i16x8.min_s_with_const_1") (v128.const i16x8 0 0 1 1 1 1 0 0)) +(assert_return (invoke "i16x8.min_u_with_const_2") (v128.const i16x8 -32768 -32768 16384 16384 16384 16384 -32768 -32768)) +(assert_return (invoke "i16x8.min_u_with_const_3") (v128.const i16x8 0 0 1 1 1 1 0 0)) +(assert_return (invoke "i16x8.max_s_with_const_4") (v128.const i16x8 65535 65535 32767 32767 32767 32767 65535 65535)) +(assert_return (invoke "i16x8.max_s_with_const_5") (v128.const i16x8 3 3 2 2 2 2 3 3)) +(assert_return (invoke "i16x8.max_u_with_const_6") (v128.const i16x8 65535 65535 32767 32767 32767 32767 65535 65535)) +(assert_return (invoke "i16x8.max_u_with_const_7") (v128.const i16x8 3 3 2 2 2 2 3 3)) +(assert_return (invoke "i16x8.avgr_u_with_const_8") (v128.const i16x8 49152 49152 24576 24576 24576 24576 49152 49152)) +(assert_return (invoke "i16x8.avgr_u_with_const_9") (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.abs_with_const_10") (v128.const i16x8 32768 32768 32767 32767 16384 16384 1 1)) + +;; Param vs const +(assert_return (invoke "i16x8.min_s_with_const_11" (v128.const i16x8 65535 65535 16384 16384 32767 32767 -32768 -32768)) + (v128.const i16x8 -32768 -32768 16384 16384 16384 16384 -32768 -32768)) +(assert_return (invoke "i16x8.min_s_with_const_12" (v128.const i16x8 3 3 2 2 1 1 0 0)) + (v128.const i16x8 0 0 1 1 1 1 0 0)) +(assert_return (invoke "i16x8.min_u_with_const_13" (v128.const i16x8 65535 65535 16384 16384 32767 32767 -32768 -32768)) + (v128.const i16x8 -32768 -32768 16384 16384 16384 16384 -32768 -32768)) +(assert_return (invoke "i16x8.min_u_with_const_14" (v128.const i16x8 3 3 2 2 1 1 0 0)) + (v128.const i16x8 0 0 1 1 1 1 0 0)) +(assert_return (invoke "i16x8.max_s_with_const_15" (v128.const i16x8 65535 65535 16384 16384 32767 32767 -32768 -32768)) + (v128.const i16x8 65535 65535 32767 32767 32767 32767 65535 65535)) +(assert_return (invoke "i16x8.max_s_with_const_16" (v128.const i16x8 3 3 2 2 1 1 0 0)) + (v128.const i16x8 3 3 2 2 2 2 3 3)) +(assert_return (invoke "i16x8.max_u_with_const_17" (v128.const i16x8 65535 65535 16384 16384 32767 32767 -32768 -32768)) + (v128.const i16x8 65535 65535 32767 32767 32767 32767 65535 65535)) +(assert_return (invoke "i16x8.max_u_with_const_18" (v128.const i16x8 3 3 2 2 1 1 0 0)) + (v128.const i16x8 3 3 2 2 2 2 3 3)) +(assert_return (invoke "i16x8.avgr_u_with_const_19" (v128.const i16x8 65535 65535 16384 16384 32767 32767 -32768 -32768)) + (v128.const i16x8 49152 49152 24576 24576 24576 24576 49152 49152)) +(assert_return (invoke "i16x8.avgr_u_with_const_20" (v128.const i16x8 3 3 2 2 1 1 0 0)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + +;; Test different lanes go through different if-then clauses +(assert_return (invoke "i16x8.min_s" (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535) + (v128.const i16x8 65535 65535 16384 16384 32767 32767 -32768 -32768)) + (v128.const i16x8 -32768 -32768 16384 16384 16384 16384 -32768 -32768)) +(assert_return (invoke "i16x8.min_s" (v128.const i16x8 0 0 1 1 2 2 128 128) + (v128.const i16x8 0 0 2 2 1 1 128 128)) + (v128.const i16x8 0 0 1 1 1 1 128 128)) +(assert_return (invoke "i16x8.min_u" (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535) + (v128.const i16x8 65535 65535 16384 16384 32767 32767 -32768 -32768)) + (v128.const i16x8 -32768 -32768 16384 16384 16384 16384 -32768 -32768)) +(assert_return (invoke "i16x8.min_u" (v128.const i16x8 0 0 1 1 2 2 128 128) + (v128.const i16x8 0 0 2 2 1 1 128 128)) + (v128.const i16x8 0 0 1 1 1 1 128 128)) +(assert_return (invoke "i16x8.max_s" (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535) + (v128.const i16x8 65535 65535 16384 16384 32767 32767 -32768 -32768)) + (v128.const i16x8 65535 65535 32767 32767 32767 32767 65535 65535)) +(assert_return (invoke "i16x8.max_s" (v128.const i16x8 0 0 1 1 2 2 128 128) + (v128.const i16x8 0 0 2 2 1 1 128 128)) + (v128.const i16x8 0 0 2 2 2 2 128 128)) +(assert_return (invoke "i16x8.max_u" (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535) + (v128.const i16x8 65535 65535 16384 16384 32767 32767 -32768 -32768)) + (v128.const i16x8 65535 65535 32767 32767 32767 32767 65535 65535)) +(assert_return (invoke "i16x8.max_u" (v128.const i16x8 0 0 1 1 2 2 128 128) + (v128.const i16x8 0 0 2 2 1 1 128 128)) + (v128.const i16x8 0 0 2 2 2 2 128 128)) +(assert_return (invoke "i16x8.avgr_u" (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535) + (v128.const i16x8 65535 65535 16384 16384 32767 32767 -32768 -32768)) + (v128.const i16x8 49152 49152 24576 24576 24576 24576 49152 49152)) +(assert_return (invoke "i16x8.avgr_u" (v128.const i16x8 0 0 1 1 2 2 128 128) + (v128.const i16x8 0 0 2 2 1 1 128 128)) + (v128.const i16x8 0 0 2 2 2 2 128 128)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 -32768 -32768 32767 32767 16384 16384 65535 65535)) + (v128.const i16x8 32768 32768 32767 32767 16384 16384 1 1)) + +;; Test opposite signs of zero +(assert_return (invoke "i16x8.min_s" (v128.const i16x8 -0 -0 -0 -0 +0 +0 +0 +0) + (v128.const i16x8 +0 +0 0 0 -0 -0 0 0)) + (v128.const i16x8 -0 -0 -0 -0 +0 +0 +0 +0)) +(assert_return (invoke "i16x8.min_s" (v128.const i16x8 -0 -0 -0 -0 -0 -0 -0 -0) + (v128.const i16x8 +0 +0 +0 +0 +0 +0 +0 +0)) + (v128.const i16x8 -0 -0 -0 -0 -0 -0 -0 -0)) +(assert_return (invoke "i16x8.min_u" (v128.const i16x8 -0 -0 -0 -0 +0 +0 +0 +0) + (v128.const i16x8 +0 +0 0 0 -0 -0 0 0)) + (v128.const i16x8 -0 -0 -0 -0 +0 +0 +0 +0)) +(assert_return (invoke "i16x8.min_u" (v128.const i16x8 -0 -0 -0 -0 -0 -0 -0 -0) + (v128.const i16x8 +0 +0 +0 +0 +0 +0 +0 +0)) + (v128.const i16x8 -0 -0 -0 -0 -0 -0 -0 -0)) +(assert_return (invoke "i16x8.max_s" (v128.const i16x8 -0 -0 -0 -0 +0 +0 +0 +0) + (v128.const i16x8 +0 +0 0 0 -0 -0 0 0)) + (v128.const i16x8 -0 -0 -0 -0 +0 +0 +0 +0)) +(assert_return (invoke "i16x8.max_s" (v128.const i16x8 -0 -0 -0 -0 -0 -0 -0 -0) + (v128.const i16x8 +0 +0 +0 +0 +0 +0 +0 +0)) + (v128.const i16x8 -0 -0 -0 -0 -0 -0 -0 -0)) +(assert_return (invoke "i16x8.max_u" (v128.const i16x8 -0 -0 -0 -0 +0 +0 +0 +0) + (v128.const i16x8 +0 +0 0 0 -0 -0 0 0)) + (v128.const i16x8 -0 -0 -0 -0 +0 +0 +0 +0)) +(assert_return (invoke "i16x8.max_u" (v128.const i16x8 -0 -0 -0 -0 -0 -0 -0 -0) + (v128.const i16x8 +0 +0 +0 +0 +0 +0 +0 +0)) + (v128.const i16x8 -0 -0 -0 -0 -0 -0 -0 -0)) +(assert_return (invoke "i16x8.avgr_u" (v128.const i16x8 -0 -0 -0 -0 +0 +0 +0 +0) + (v128.const i16x8 +0 +0 0 0 -0 -0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.avgr_u" (v128.const i16x8 -0 -0 -0 -0 -0 -0 -0 -0) + (v128.const i16x8 +0 +0 +0 +0 +0 +0 +0 +0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 -0 -0 -0 -0 +0 +0 +0 +0)) + (v128.const i16x8 -0 -0 -0 -0 +0 +0 +0 +0)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 +0 +0 0 0 -0 -0 0 0)) + (v128.const i16x8 +0 +0 0 0 -0 -0 0 0)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 -0 -0 -0 -0 -0 -0 -0 -0)) + (v128.const i16x8 -0 -0 -0 -0 -0 -0 -0 -0)) +(assert_return (invoke "i16x8.abs" (v128.const i16x8 +0 +0 +0 +0 +0 +0 +0 +0)) + (v128.const i16x8 +0 +0 +0 +0 +0 +0 +0 +0)) + +;; Unknown operators +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.avgr (v128.const i16x8 0 0 0 0 0 0 0 0) (v128.const i16x8 1 1 1 1 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i16x8.avgr_s (v128.const i16x8 0 0 0 0 0 0 0 0) (v128.const i16x8 1 1 1 1 1 1 1 1)))") "unknown operator") + +;; Type check +(assert_invalid (module (func (result v128) (i16x8.min_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.min_u (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.max_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.max_u (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.avgr_u (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.abs (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i16x8.min_s-1st-arg-empty (result v128) + (i16x8.min_s (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.min_s-arg-empty (result v128) + (i16x8.min_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.min_u-1st-arg-empty (result v128) + (i16x8.min_u (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.min_u-arg-empty (result v128) + (i16x8.min_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.max_s-1st-arg-empty (result v128) + (i16x8.max_s (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.max_s-arg-empty (result v128) + (i16x8.max_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.max_u-1st-arg-empty (result v128) + (i16x8.max_u (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.max_u-arg-empty (result v128) + (i16x8.max_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.avgr_u-1st-arg-empty (result v128) + (i16x8.avgr_u (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.avgr_u-arg-empty (result v128) + (i16x8.avgr_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.abs-arg-empty (result v128) + (i16x8.abs) + ) + ) + "type mismatch" +) + +;; Combination +(module + (func (export "i16x8.min_s-i16x8.avgr_u") (param v128 v128 v128) (result v128) (i16x8.min_s (i16x8.avgr_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.min_s-i16x8.max_u") (param v128 v128 v128) (result v128) (i16x8.min_s (i16x8.max_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.min_s-i16x8.max_s") (param v128 v128 v128) (result v128) (i16x8.min_s (i16x8.max_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.min_s-i16x8.min_u") (param v128 v128 v128) (result v128) (i16x8.min_s (i16x8.min_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.min_s-i16x8.min_s") (param v128 v128 v128) (result v128) (i16x8.min_s (i16x8.min_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.min_s-i16x8.abs") (param v128 v128) (result v128) (i16x8.min_s (i16x8.abs (local.get 0))(local.get 1))) + (func (export "i16x8.abs-i16x8.min_s") (param v128 v128) (result v128) (i16x8.abs (i16x8.min_s (local.get 0) (local.get 1)))) + (func (export "i16x8.min_u-i16x8.avgr_u") (param v128 v128 v128) (result v128) (i16x8.min_u (i16x8.avgr_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.min_u-i16x8.max_u") (param v128 v128 v128) (result v128) (i16x8.min_u (i16x8.max_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.min_u-i16x8.max_s") (param v128 v128 v128) (result v128) (i16x8.min_u (i16x8.max_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.min_u-i16x8.min_u") (param v128 v128 v128) (result v128) (i16x8.min_u (i16x8.min_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.min_u-i16x8.min_s") (param v128 v128 v128) (result v128) (i16x8.min_u (i16x8.min_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.min_u-i16x8.abs") (param v128 v128) (result v128) (i16x8.min_u (i16x8.abs (local.get 0))(local.get 1))) + (func (export "i16x8.abs-i16x8.min_u") (param v128 v128) (result v128) (i16x8.abs (i16x8.min_u (local.get 0) (local.get 1)))) + (func (export "i16x8.max_s-i16x8.avgr_u") (param v128 v128 v128) (result v128) (i16x8.max_s (i16x8.avgr_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.max_s-i16x8.max_u") (param v128 v128 v128) (result v128) (i16x8.max_s (i16x8.max_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.max_s-i16x8.max_s") (param v128 v128 v128) (result v128) (i16x8.max_s (i16x8.max_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.max_s-i16x8.min_u") (param v128 v128 v128) (result v128) (i16x8.max_s (i16x8.min_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.max_s-i16x8.min_s") (param v128 v128 v128) (result v128) (i16x8.max_s (i16x8.min_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.max_s-i16x8.abs") (param v128 v128) (result v128) (i16x8.max_s (i16x8.abs (local.get 0))(local.get 1))) + (func (export "i16x8.abs-i16x8.max_s") (param v128 v128) (result v128) (i16x8.abs (i16x8.max_s (local.get 0) (local.get 1)))) + (func (export "i16x8.max_u-i16x8.avgr_u") (param v128 v128 v128) (result v128) (i16x8.max_u (i16x8.avgr_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.max_u-i16x8.max_u") (param v128 v128 v128) (result v128) (i16x8.max_u (i16x8.max_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.max_u-i16x8.max_s") (param v128 v128 v128) (result v128) (i16x8.max_u (i16x8.max_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.max_u-i16x8.min_u") (param v128 v128 v128) (result v128) (i16x8.max_u (i16x8.min_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.max_u-i16x8.min_s") (param v128 v128 v128) (result v128) (i16x8.max_u (i16x8.min_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.max_u-i16x8.abs") (param v128 v128) (result v128) (i16x8.max_u (i16x8.abs (local.get 0))(local.get 1))) + (func (export "i16x8.abs-i16x8.max_u") (param v128 v128) (result v128) (i16x8.abs (i16x8.max_u (local.get 0) (local.get 1)))) + (func (export "i16x8.avgr_u-i16x8.avgr_u") (param v128 v128 v128) (result v128) (i16x8.avgr_u (i16x8.avgr_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.avgr_u-i16x8.max_u") (param v128 v128 v128) (result v128) (i16x8.avgr_u (i16x8.max_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.avgr_u-i16x8.max_s") (param v128 v128 v128) (result v128) (i16x8.avgr_u (i16x8.max_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.avgr_u-i16x8.min_u") (param v128 v128 v128) (result v128) (i16x8.avgr_u (i16x8.min_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.avgr_u-i16x8.min_s") (param v128 v128 v128) (result v128) (i16x8.avgr_u (i16x8.min_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i16x8.avgr_u-i16x8.abs") (param v128 v128) (result v128) (i16x8.avgr_u (i16x8.abs (local.get 0))(local.get 1))) + (func (export "i16x8.abs-i16x8.avgr_u") (param v128 v128) (result v128) (i16x8.abs (i16x8.avgr_u (local.get 0) (local.get 1)))) + (func (export "i16x8.abs-i16x8.abs") (param v128) (result v128) (i16x8.abs (i16x8.abs (local.get 0)))) +) + +(assert_return (invoke "i16x8.min_s-i16x8.avgr_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.min_s-i16x8.max_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.min_s-i16x8.max_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.min_s-i16x8.min_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.min_s-i16x8.min_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.min_s-i16x8.abs" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.abs-i16x8.min_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.min_u-i16x8.avgr_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.min_u-i16x8.max_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.min_u-i16x8.max_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.min_u-i16x8.min_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.min_u-i16x8.min_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.min_u-i16x8.abs" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.abs-i16x8.min_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.max_s-i16x8.avgr_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.max_s-i16x8.max_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.max_s-i16x8.max_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.max_s-i16x8.min_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.max_s-i16x8.min_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.max_s-i16x8.abs" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.abs-i16x8.max_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.max_u-i16x8.avgr_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.max_u-i16x8.max_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.max_u-i16x8.max_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.max_u-i16x8.min_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.max_u-i16x8.min_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.max_u-i16x8.abs" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.abs-i16x8.max_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.avgr_u-i16x8.avgr_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.avgr_u-i16x8.max_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.avgr_u-i16x8.max_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.avgr_u-i16x8.min_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.avgr_u-i16x8.min_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 2 2 2 2 2 2 2 2)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.avgr_u-i16x8.abs" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.abs-i16x8.avgr_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768)) +(assert_return (invoke "i16x8.abs-i16x8.abs" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_cmp.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_cmp.wast new file mode 100644 index 000000000..24068ce32 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_cmp.wast @@ -0,0 +1,1901 @@ + +;; Test all the i16x8 comparison operators on major boundary values and all special values. + +(module + (func (export "eq") (param $x v128) (param $y v128) (result v128) (i16x8.eq (local.get $x) (local.get $y))) + (func (export "ne") (param $x v128) (param $y v128) (result v128) (i16x8.ne (local.get $x) (local.get $y))) + (func (export "lt_s") (param $x v128) (param $y v128) (result v128) (i16x8.lt_s (local.get $x) (local.get $y))) + (func (export "lt_u") (param $x v128) (param $y v128) (result v128) (i16x8.lt_u (local.get $x) (local.get $y))) + (func (export "le_s") (param $x v128) (param $y v128) (result v128) (i16x8.le_s (local.get $x) (local.get $y))) + (func (export "le_u") (param $x v128) (param $y v128) (result v128) (i16x8.le_u (local.get $x) (local.get $y))) + (func (export "gt_s") (param $x v128) (param $y v128) (result v128) (i16x8.gt_s (local.get $x) (local.get $y))) + (func (export "gt_u") (param $x v128) (param $y v128) (result v128) (i16x8.gt_u (local.get $x) (local.get $y))) + (func (export "ge_s") (param $x v128) (param $y v128) (result v128) (i16x8.ge_s (local.get $x) (local.get $y))) + (func (export "ge_u") (param $x v128) (param $y v128) (result v128) (i16x8.ge_u (local.get $x) (local.get $y))) +) + + +;; eq + +;; i16x8.eq (i16x8) (i16x8) + +;; hex vs hex +(assert_return (invoke "eq" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB) + (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs dec +(assert_return (invoke "eq" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 32896 32896 32896 32896 32896 32896 32896 32896)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 -32640 -32640 -32640 -32640 -32640 -32640 -32640 -32640)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0x8180 0x8382 0xFEFD 0x00FF 0x0100 0x7F02 0xFD80 0xFFFE) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; dec vs dec +(assert_return (invoke "eq" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 65535 65535 65535 65535 0 0 0 0) + (v128.const i16x8 65535 65535 65535 65535 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0 0 0 0 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 -32768 65534 -1 -0 0 1 2 65535) + (v128.const i16x8 32768 -2 -1 -0 0 1 2 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "eq" (v128.const i16x8 0x0000 0xc300 0x0000 0xc2fe 0x0000 0xbf80 0x0000 0x0000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0x0000 0x3f80 0x0000 0x42fe 0x0000 0x4300 0x0000 0x437f) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; not equal +(assert_return (invoke "eq" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "eq" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "eq" (v128.const i16x8 0x0001 0x0203 0x0409 0x1011 0x120A 0x0B1A 0x1BAA 0xABFF) + (v128.const i16x8 0xFFAB 0xAA1B 0x1A0B 0x0A12 0x1110 0x0904 0x0302 0x0100)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "eq" (v128.const i16x8 0x8000 0x8001 0x8002 0x8003 0x8004 0x8005 0x8006 0x8007) + (v128.const i16x8 32775 32774 32773 32772 32771 32770 32769 32768)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "eq" (v128.const i16x8 32768 32769 65534 65535 0 -1 -32767 -32768) + (v128.const i16x8 -32768 -32767 -1 0 65535 65534 32769 32768)) + (v128.const i16x8 -1 -1 0 0 0 0 -1 -1)) + +;; i16x8.eq (i16x8) (i8x16) +(assert_return (invoke "eq" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i16x8 0 0 -1 -1 0 0 0 0)) +(assert_return (invoke "eq" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; i16x8.eq (i16x8) (i32x4) +(assert_return (invoke "eq" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 65535 65535 0 0 1 1 32768 32768) + (v128.const i32x4 65535 0 1 32768)) + (v128.const i16x8 -1 0 -1 -1 -1 0 -1 0)) +(assert_return (invoke "eq" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "eq" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 12345 12345 12345 12345 12345 12345 12345 12345)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; ne + +;; i16x8.ne (i16x8) (i16x8) + +;; hex vs hex +(assert_return (invoke "ne" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB) + (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; hex vs dec +(assert_return (invoke "ne" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 32896 32896 32896 32896 32896 32896 32896 32896)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 -32640 -32640 -32640 -32640 -32640 -32640 -32640 -32640)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0x8180 0x8382 0xFEFD 0x00FF 0x0100 0x7F02 0xFD80 0xFFFE) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; dec vs dec +(assert_return (invoke "ne" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 255 255 255 255 255 255 255 255) + (v128.const i16x8 255 255 255 255 255 255 255 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 255 255 255 255 0 0 0 0) + (v128.const i16x8 255 255 255 255 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0 0 0 0 255 255 255 255) + (v128.const i16x8 0 0 0 0 255 255 255 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 255 32767 -0 0 1 2 65534 65535) + (v128.const i16x8 255 32767 0 0 1 2 -2 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; hex vs float +(assert_return (invoke "ne" (v128.const i16x8 0x0000 0xc300 0x0000 0xc2fe 0x0000 0xbf80 0x0000 0x0000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0x0000 0x3f80 0x0000 0x42fe 0x0000 0x4300 0x0000 0x437f) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; not equal +(assert_return (invoke "ne" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i16x8 0x0001 0x0203 0x0409 0x1011 0x120A 0x0B1A 0x1BAA 0xABFF) + (v128.const i16x8 0xFFAB 0xAA1B 0x1A0B 0x0A12 0x1110 0x0904 0x0302 0x0100)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i16x8 0x8081 0x8283 0xFDFE 0xFF00 0x0001 0x027F 0x80FD 0xFEFF) + (v128.const i16x8 65279 33021 639 1 65280 65022 33411 32897)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i16x8 128 129 130 131 -0 255 32766 32767) + (v128.const i16x8 32767 32766 255 -0 131 130 129 28)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; i16x8.ne (i16x8) (i8x16) +(assert_return (invoke "ne" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i16x8 -1 -1 0 0 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; i16x8.ne (i16x8) (i32x4) +(assert_return (invoke "ne" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i16x8 0 -1 0 0 0 -1 0 -1)) +(assert_return (invoke "ne" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 12345 12345 12345 12345 12345 12345 12345 12345)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; lt_s + +;; i16x8.lt_s (i16x8) (i16x8) + +;; hex vs hex +(assert_return (invoke "lt_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB) + (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 32896 32896 32896 32896 32896 32896 32896 32896)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 -32640 -32640 -32640 -32640 -32640 -32640 -32640 -32640)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x8180 0x8382 0xFEFD 0x00FF 0x0100 0x7F02 0xFD80 0xFFFE) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 255 255 255 255 255 255 255 255) + (v128.const i16x8 255 255 255 255 255 255 255 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 255 255 255 255 0 0 0 0) + (v128.const i16x8 255 255 255 255 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0 0 0 0 255 255 255 255) + (v128.const i16x8 0 0 0 0 255 255 255 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 255 32767 -0 0 1 2 65534 65535) + (v128.const i16x8 255 32767 0 0 1 2 -2 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x0000 0xc300 0x0000 0xc2fe 0x0000 0xbf80 0x0000 0x0000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x0000 0x3f80 0x0000 0x42fe 0x0000 0x4300 0x0000 0x437f) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; not equal +(assert_return (invoke "lt_s" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 0 0 0 0 -1 -1 -1 -1)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x0001 0x0203 0x0409 0x1011 0x120A 0x0B1A 0x1BAA 0xABFF) + (v128.const i16x8 0xFFAB 0xAA1B 0x1A0B 0x0A12 0x1110 0x0904 0x0302 0x0100)) + (v128.const i16x8 0 0 -1 0 0 0 0 -1)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x8081 0x8283 0xFDFE 0xFF00 0x0001 0x027F 0x80FD 0xFEFF) + (v128.const i16x8 65279 33021 639 1 65280 65022 33411 32897)) + (v128.const i16x8 -1 0 -1 -1 0 0 -1 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 128 129 130 131 -0 255 32766 32767) + (v128.const i16x8 32767 32766 255 -0 131 130 129 28)) + (v128.const i16x8 -1 -1 -1 0 -1 0 0 0)) + +;; i16x8.lt_s (i16x8) (i8x16) +(assert_return (invoke "lt_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i16x8 0 0 0 0 -1 -1 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; i16x8.lt_s (i16x8) (i32x4) +(assert_return (invoke "lt_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i16x8 0 -1 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 12345 12345 12345 12345 12345 12345 12345 12345)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; lt_u + +;; i16x8.lt_u (i16x8) (i16x8) + +;; hex vs hex +(assert_return (invoke "lt_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB) + (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; hex vs dec +(assert_return (invoke "lt_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 32896 32896 32896 32896 32896 32896 32896 32896)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 -32640 -32640 -32640 -32640 -32640 -32640 -32640 -32640)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x8180 0x8382 0xFEFD 0x00FF 0x0100 0x7F02 0xFD80 0xFFFE) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; dec vs dec +(assert_return (invoke "lt_u" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 255 255 255 255 255 255 255 255) + (v128.const i16x8 255 255 255 255 255 255 255 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 255 255 255 255 0 0 0 0) + (v128.const i16x8 255 255 255 255 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0 0 0 0 255 255 255 255) + (v128.const i16x8 0 0 0 0 255 255 255 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 255 32767 -0 0 1 2 65534 65535) + (v128.const i16x8 255 32767 0 0 1 2 -2 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; hex vs float +(assert_return (invoke "lt_u" (v128.const i16x8 0x0000 0xc300 0x0000 0xc2fe 0x0000 0xbf80 0x0000 0x0000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x0000 0x3f80 0x0000 0x42fe 0x0000 0x4300 0x0000 0x437f) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; not equal +(assert_return (invoke "lt_u" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 -1 -1 -1 -1 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x0001 0x0203 0x0409 0x1011 0x120A 0x0B1A 0x1BAA 0xABFF) + (v128.const i16x8 0xFFAB 0xAA1B 0x1A0B 0x0A12 0x1110 0x0904 0x0302 0x0100)) + (v128.const i16x8 -1 -1 -1 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x8081 0x8283 0xFDFE 0xFF00 0x0001 0x027F 0x80FD 0xFEFF) + (v128.const i16x8 65279 33021 639 1 65280 65022 33411 32897)) + (v128.const i16x8 -1 0 0 0 -1 -1 -1 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 128 129 130 131 -0 255 32766 32767) + (v128.const i16x8 32767 32766 255 -0 131 130 129 28)) + (v128.const i16x8 -1 -1 -1 0 -1 0 0 0)) + +;; i16x8.lt_u (i16x8) (i8x16) +(assert_return (invoke "lt_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i16x8 0 0 0 0 -1 -1 -1 -1)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; i16x8.lt_u (i16x8) (i32x4) +(assert_return (invoke "lt_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i16x8 0 -1 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "lt_u" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 12345 12345 12345 12345 12345 12345 12345 12345)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; le_s + +;; i16x8.le_s (i16x8) (i16x8) + +;; hex vs hex +(assert_return (invoke "le_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB) + (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs dec +(assert_return (invoke "le_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 32896 32896 32896 32896 32896 32896 32896 32896)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 -32640 -32640 -32640 -32640 -32640 -32640 -32640 -32640)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0x8180 0x8382 0xFEFD 0x00FF 0x0100 0x7F02 0xFD80 0xFFFE) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; dec vs dec +(assert_return (invoke "le_s" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 65535 65535 65535 65535 0 0 0 0) + (v128.const i16x8 65535 65535 65535 65535 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0 0 0 0 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 -32768 65534 -1 -0 0 1 2 65535) + (v128.const i16x8 32768 -2 -1 -0 0 1 2 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "le_s" (v128.const i16x8 0x0000 0xc300 0x0000 0xc2fe 0x0000 0xbf80 0x0000 0x0000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0x0000 0x3f80 0x0000 0x42fe 0x0000 0x4300 0x0000 0x437f) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; not equal +(assert_return (invoke "le_s" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "le_s" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 0 0 0 0 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0x0001 0x0203 0x0409 0x1011 0x120A 0x0B1A 0x1BAA 0xABFF) + (v128.const i16x8 0xFFAB 0xAA1B 0x1A0B 0x0A12 0x1110 0x0904 0x0302 0x0100)) + (v128.const i16x8 0 0 -1 0 0 0 0 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0x8000 0x8001 0x8002 0x8003 0x8004 0x8005 0x8006 0x8007) + (v128.const i16x8 32775 32774 32773 32772 32771 32770 32769 32768)) + (v128.const i16x8 -1 -1 -1 -1 0 0 0 0)) +(assert_return (invoke "le_s" (v128.const i16x8 32768 32769 65534 65535 0 -1 -32767 -32768) + (v128.const i16x8 -32768 -32767 -1 0 65535 65534 32769 32768)) + (v128.const i16x8 -1 -1 -1 -1 0 0 -1 -1)) + +;; i16x8.le_s (i16x8) (i8x16) +(assert_return (invoke "le_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i16x8 0 0 -1 -1 -1 -1 0 0)) +(assert_return (invoke "le_s" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; i16x8.le_s (i16x8) (i32x4) +(assert_return (invoke "le_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i16x8 -1 -1 -1 -1 -1 0 -1 0)) +(assert_return (invoke "le_s" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "le_s" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 12345 12345 12345 12345 12345 12345 12345 12345)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; le_u + +;; i16x8.le_u (i16x8) (i16x8) + +;; hex vs hex +(assert_return (invoke "le_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB) + (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs dec +(assert_return (invoke "le_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 32896 32896 32896 32896 32896 32896 32896 32896)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 -32640 -32640 -32640 -32640 -32640 -32640 -32640 -32640)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0x8180 0x8382 0xFEFD 0x00FF 0x0100 0x7F02 0xFD80 0xFFFE) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; dec vs dec +(assert_return (invoke "le_u" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 65535 65535 65535 65535 0 0 0 0) + (v128.const i16x8 65535 65535 65535 65535 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0 0 0 0 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 -32768 65534 -1 -0 0 1 2 65535) + (v128.const i16x8 32768 -2 -1 -0 0 1 2 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "le_u" (v128.const i16x8 0x0000 0xc300 0x0000 0xc2fe 0x0000 0xbf80 0x0000 0x0000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0x0000 0x3f80 0x0000 0x42fe 0x0000 0x4300 0x0000 0x437f) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; not equal +(assert_return (invoke "le_u" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 -1 -1 -1 -1 0 0 0 0)) +(assert_return (invoke "le_u" (v128.const i16x8 0x0001 0x0203 0x0409 0x1011 0x120A 0x0B1A 0x1BAA 0xABFF) + (v128.const i16x8 0xFFAB 0xAA1B 0x1A0B 0x0A12 0x1110 0x0904 0x0302 0x0100)) + (v128.const i16x8 -1 -1 -1 0 0 0 0 0)) +(assert_return (invoke "le_u" (v128.const i16x8 0x8000 0x8001 0x8002 0x8003 0x8004 0x8005 0x8006 0x8007) + (v128.const i16x8 32775 32774 32773 32772 32771 32770 32769 32768)) + (v128.const i16x8 -1 -1 -1 -1 0 0 0 0)) +(assert_return (invoke "le_u" (v128.const i16x8 32768 32769 65534 65535 0 -1 -32767 -32768) + (v128.const i16x8 -32768 -32767 -1 0 65535 65534 32769 32768)) + (v128.const i16x8 -1 -1 -1 0 -1 0 -1 -1)) + +;; i16x8.le_u (i16x8) (i8x16) +(assert_return (invoke "le_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i16x8 0 0 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; i16x8.le_u (i16x8) (i32x4) +(assert_return (invoke "le_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i16x8 -1 -1 -1 -1 -1 0 -1 0)) +(assert_return (invoke "le_u" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 12345 12345 12345 12345 12345 12345 12345 12345)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i16x8 0x0_edcb 0x0_edcb 0x0_edcb 0x0_edcb 0x0_edcb 0x0_edcb 0x0_edcb 0x0_edcb) + (v128.const i16x8 -0x1234 -0x1234 -0x1234 -0x1234 -0x1234 -0x1234 -0x1234 -0x1234)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; gt_s + +;; i16x8.gt_s (i16x8) (i16x8) + +;; hex vs hex +(assert_return (invoke "gt_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB) + (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; hex vs dec +(assert_return (invoke "gt_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 32896 32896 32896 32896 32896 32896 32896 32896)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 -32640 -32640 -32640 -32640 -32640 -32640 -32640 -32640)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x8180 0x8382 0xFEFD 0x00FF 0x0100 0x7F02 0xFD80 0xFFFE) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; dec vs dec +(assert_return (invoke "gt_s" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 65535 65535 65535 65535 0 0 0 0) + (v128.const i16x8 65535 65535 65535 65535 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0 0 0 0 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 65535 65535 65535 65535)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 -32768 65534 -1 -0 0 1 2 65535) + (v128.const i16x8 32768 -2 -1 -0 0 1 2 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; hex vs float +(assert_return (invoke "gt_s" (v128.const i16x8 0x0000 0xc300 0x0000 0xc2fe 0x0000 0xbf80 0x0000 0x0000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x0000 0x3f80 0x0000 0x42fe 0x0000 0x4300 0x0000 0x437f) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; not equal +(assert_return (invoke "gt_s" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 -1 -1 -1 -1 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x0001 0x0203 0x0409 0x1011 0x120A 0x0B1A 0x1BAA 0xABFF) + (v128.const i16x8 0xFFAB 0xAA1B 0x1A0B 0x0A12 0x1110 0x0904 0x0302 0x0100)) + (v128.const i16x8 -1 -1 0 -1 -1 -1 -1 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x8000 0x8001 0x8002 0x8003 0x8004 0x8005 0x8006 0x8007) + (v128.const i16x8 32775 32774 32773 32772 32771 32770 32769 32768)) + (v128.const i16x8 0 0 0 0 -1 -1 -1 -1)) +(assert_return (invoke "gt_s" (v128.const i16x8 32768 32769 65534 65535 0 -1 -32767 -32768) + (v128.const i16x8 -32768 -32767 -1 0 65535 65534 32769 32768)) + (v128.const i16x8 0 0 0 0 -1 -1 0 0)) + +;; i16x8.gt_s (i16x8) (i8x16) +(assert_return (invoke "gt_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i16x8 -1 -1 0 0 0 0 -1 -1)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; i16x8.gt_s (i16x8) (i32x4) +(assert_return (invoke "gt_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 65535 65535 0 0 1 1 32768 32768) + (v128.const i32x4 65535 0 1 32768)) + (v128.const i16x8 0 0 0 0 0 -1 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "gt_s" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 12345 12345 12345 12345 12345 12345 12345 12345)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; gt_u + +;; i16x8.gt_u (i16x8) (i16x8) + +;; hex vs hex +(assert_return (invoke "gt_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB) + (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; hex vs dec +(assert_return (invoke "gt_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 32896 32896 32896 32896 32896 32896 32896 32896)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 -32640 -32640 -32640 -32640 -32640 -32640 -32640 -32640)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x8180 0x8382 0xFEFD 0x00FF 0x0100 0x7F02 0xFD80 0xFFFE) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; dec vs dec +(assert_return (invoke "eq" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 65535 65535 65535 65535 0 0 0 0) + (v128.const i16x8 65535 65535 65535 65535 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 0 0 0 0 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i16x8 -32768 65534 -1 -0 0 1 2 65535) + (v128.const i16x8 32768 -2 -1 -0 0 1 2 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "gt_u" (v128.const i16x8 0x0000 0xc300 0x0000 0xc2fe 0x0000 0xbf80 0x0000 0x0000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x0000 0x3f80 0x0000 0x42fe 0x0000 0x4300 0x0000 0x437f) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; not equal +(assert_return (invoke "gt_u" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 0 0 0 0 -1 -1 -1 -1)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x0001 0x0203 0x0409 0x1011 0x120A 0x0B1A 0x1BAA 0xABFF) + (v128.const i16x8 0xFFAB 0xAA1B 0x1A0B 0x0A12 0x1110 0x0904 0x0302 0x0100)) + (v128.const i16x8 0 0 0 -1 -1 -1 -1 -1)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x8000 0x8001 0x8002 0x8003 0x8004 0x8005 0x8006 0x8007) + (v128.const i16x8 32775 32774 32773 32772 32771 32770 32769 32768)) + (v128.const i16x8 0 0 0 0 -1 -1 -1 -1)) +(assert_return (invoke "gt_u" (v128.const i16x8 32768 32769 65534 65535 0 -1 -32767 -32768) + (v128.const i16x8 -32768 -32767 -1 0 65535 65534 32769 32768)) + (v128.const i16x8 0 0 0 -1 0 -1 0 0)) + +;; i16x8.gt_u (i16x8) (i8x16) +(assert_return (invoke "gt_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i16x8 -1 -1 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; i16x8.gt_u (i16x8) (i32x4) +(assert_return (invoke "gt_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i16x8 0 0 0 0 0 -1 0 -1)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 12345 12345 12345 12345 12345 12345 12345 12345)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; ge_s + +;; i16x8.ge_s (i16x8) (i16x8) + +;; hex vs hex +(assert_return (invoke "ge_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB) + (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs dec +(assert_return (invoke "ge_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 32896 32896 32896 32896 32896 32896 32896 32896)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 -32640 -32640 -32640 -32640 -32640 -32640 -32640 -32640)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0x8180 0x8382 0xFEFD 0x00FF 0x0100 0x7F02 0xFD80 0xFFFE) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; dec vs dec +(assert_return (invoke "ge_s" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 65535 65535 65535 65535 0 0 0 0) + (v128.const i16x8 65535 65535 65535 65535 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0 0 0 0 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 -32768 65534 -1 -0 0 1 2 65535) + (v128.const i16x8 32768 -2 -1 -0 0 1 2 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "ge_s" (v128.const i16x8 0x0000 0xc300 0x0000 0xc2fe 0x0000 0xbf80 0x0000 0x0000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0x0000 0x3f80 0x0000 0x42fe 0x0000 0x4300 0x0000 0x437f) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; not equal +(assert_return (invoke "ge_s" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 -1 -1 -1 -1 0 0 0 0)) +(assert_return (invoke "ge_s" (v128.const i16x8 0x0001 0x0203 0x0409 0x1011 0x120A 0x0B1A 0x1BAA 0xABFF) + (v128.const i16x8 0xFFAB 0xAA1B 0x1A0B 0x0A12 0x1110 0x0904 0x0302 0x0100)) + (v128.const i16x8 -1 -1 0 -1 -1 -1 -1 0)) +(assert_return (invoke "ge_s" (v128.const i16x8 0x8000 0x8001 0x8002 0x8003 0x8004 0x8005 0x8006 0x8007) + (v128.const i16x8 32775 32774 32773 32772 32771 32770 32769 32768)) + (v128.const i16x8 0 0 0 0 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 32768 32769 65534 65535 0 -1 -32767 -32768) + (v128.const i16x8 -32768 -32767 -1 0 65535 65534 32769 32768)) + (v128.const i16x8 -1 -1 0 0 -1 -1 -1 -1)) + +;; i16x8.ge_s (i16x8) (i8x16) +(assert_return (invoke "ge_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i16x8 -1 -1 -1 -1 0 0 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA) + (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; i16x8.ge_s (i16x8) (i32x4) +(assert_return (invoke "ge_s" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 65535 65535 0 0 1 1 32768 32768) + (v128.const i32x4 65535 0 1 32768)) + (v128.const i16x8 -1 0 -1 -1 -1 -1 -1 0)) +(assert_return (invoke "ge_s" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 12345 12345 12345 12345 12345 12345 12345 12345)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; ge_u + +;; i16x8.ge_u (i16x8) (i16x8) + +;; hex vs hex +(assert_return (invoke "ge_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB) + (v128.const i16x8 0x0100 0x0302 0x0904 0x1110 0x0A12 0x1A0B 0xAA1B 0xFFAB)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs dec +(assert_return (invoke "ge_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 32896 32896 32896 32896 32896 32896 32896 32896)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080) + (v128.const i16x8 -32640 -32640 -32640 -32640 -32640 -32640 -32640 -32640)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0x8180 0x8382 0xFEFD 0x00FF 0x0100 0x7F02 0xFD80 0xFFFE) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; dec vs dec +(assert_return (invoke "ge_u" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 65535 65535 65535 65535 0 0 0 0) + (v128.const i16x8 65535 65535 65535 65535 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0 0 0 0 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 65535 65535 65535 65535)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 -32768 65534 -1 -0 0 1 2 65535) + (v128.const i16x8 32768 -2 -1 -0 0 1 2 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "ge_u" (v128.const i16x8 0x0000 0xc300 0x0000 0xc2fe 0x0000 0xbf80 0x0000 0x0000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0x0000 0x3f80 0x0000 0x42fe 0x0000 0x4300 0x0000 0x437f) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; not equal +(assert_return (invoke "ge_u" (v128.const i16x8 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F 0x0F0F) + (v128.const i16x8 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0 0xF0F0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ge_u" (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0x0000 0x0000 0x0000 0x0000)) + (v128.const i16x8 0 0 0 0 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0x0001 0x0203 0x0409 0x1011 0x120A 0x0B1A 0x1BAA 0xABFF) + (v128.const i16x8 0xFFAB 0xAA1B 0x1A0B 0x0A12 0x1110 0x0904 0x0302 0x0100)) + (v128.const i16x8 0 0 0 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0x8000 0x8001 0x8002 0x8003 0x8004 0x8005 0x8006 0x8007) + (v128.const i16x8 32775 32774 32773 32772 32771 32770 32769 32768)) + (v128.const i16x8 0 0 0 0 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 32768 32769 65534 65535 0 -1 -32767 -32768) + (v128.const i16x8 -32768 -32767 -1 0 65535 65534 32769 32768)) + (v128.const i16x8 -1 -1 0 -1 0 -1 -1 -1)) + +;; i16x8.ge_u (i16x8) (i8x16) +(assert_return (invoke "ge_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 -128 -128 0 0 1 1 255 255) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i16x8 -1 -1 -1 -1 0 0 0 0)) +(assert_return (invoke "ge_u" (v128.const i16x8 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA) + (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; i16x8.ge_u (i16x8) (i32x4) +(assert_return (invoke "ge_u" (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 65535 65535 0 0 1 1 32768 32768) + (v128.const i32x4 -128 0 1 255)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ge_u" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 12345 12345 12345 12345 12345 12345 12345 12345)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + + +;; Type check + +(assert_invalid (module (func (result v128) (i16x8.eq (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.ge_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.ge_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.gt_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.gt_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.le_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.le_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.lt_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.lt_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.ne (i32.const 0) (f32.const 0)))) "type mismatch") + + +;; combination + +(module (memory 1) + (func (export "eq-in-block") + (block + (drop + (block (result v128) + (i16x8.eq + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "ne-in-block") + (block + (drop + (block (result v128) + (i16x8.ne + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "lt_s-in-block") + (block + (drop + (block (result v128) + (i16x8.lt_s + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "le_u-in-block") + (block + (drop + (block (result v128) + (i16x8.le_u + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "gt_u-in-block") + (block + (drop + (block (result v128) + (i16x8.gt_u + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "ge_s-in-block") + (block + (drop + (block (result v128) + (i16x8.ge_s + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "nested-eq") + (drop + (i16x8.eq + (i16x8.eq + (i16x8.eq + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i16x8.eq + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i16x8.eq + (i16x8.eq + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i16x8.eq + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-ne") + (drop + (i16x8.ne + (i16x8.ne + (i16x8.ne + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i16x8.ne + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i16x8.ne + (i16x8.ne + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i16x8.ne + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-lt_s") + (drop + (i16x8.lt_s + (i16x8.lt_s + (i16x8.lt_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i16x8.lt_s + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i16x8.lt_s + (i16x8.lt_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i16x8.lt_s + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-le_u") + (drop + (i16x8.le_u + (i16x8.le_u + (i16x8.le_u + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i16x8.le_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i16x8.le_u + (i16x8.le_u + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i16x8.le_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-gt_u") + (drop + (i16x8.gt_u + (i16x8.gt_u + (i16x8.gt_u + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i16x8.gt_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i16x8.gt_u + (i16x8.gt_u + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i16x8.gt_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-ge_s") + (drop + (i16x8.ge_s + (i16x8.ge_s + (i16x8.ge_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i16x8.ge_s + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i16x8.ge_s + (i16x8.ge_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i16x8.ge_s + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "as-param") + (drop + (i16x8.ge_u + (i16x8.eq + (i16x8.lt_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i16x8.le_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i16x8.ne + (i16x8.gt_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i16x8.lt_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) +) +(assert_return (invoke "eq-in-block")) +(assert_return (invoke "ne-in-block")) +(assert_return (invoke "lt_s-in-block")) +(assert_return (invoke "le_u-in-block")) +(assert_return (invoke "gt_u-in-block")) +(assert_return (invoke "ge_s-in-block")) +(assert_return (invoke "nested-eq")) +(assert_return (invoke "nested-ne")) +(assert_return (invoke "nested-lt_s")) +(assert_return (invoke "nested-le_u")) +(assert_return (invoke "nested-gt_u")) +(assert_return (invoke "nested-ge_s")) +(assert_return (invoke "as-param")) + + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i16x8.eq-1st-arg-empty (result v128) + (i16x8.eq (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.eq-arg-empty (result v128) + (i16x8.eq) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.ne-1st-arg-empty (result v128) + (i16x8.ne (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.ne-arg-empty (result v128) + (i16x8.ne) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.lt_s-1st-arg-empty (result v128) + (i16x8.lt_s (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.lt_s-arg-empty (result v128) + (i16x8.lt_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.lt_u-1st-arg-empty (result v128) + (i16x8.lt_u (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.lt_u-arg-empty (result v128) + (i16x8.lt_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.le_s-1st-arg-empty (result v128) + (i16x8.le_s (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.le_s-arg-empty (result v128) + (i16x8.le_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.le_u-1st-arg-empty (result v128) + (i16x8.le_u (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.le_u-arg-empty (result v128) + (i16x8.le_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.gt_s-1st-arg-empty (result v128) + (i16x8.gt_s (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.gt_s-arg-empty (result v128) + (i16x8.gt_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.gt_u-1st-arg-empty (result v128) + (i16x8.gt_u (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.gt_u-arg-empty (result v128) + (i16x8.gt_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.ge_s-1st-arg-empty (result v128) + (i16x8.ge_s (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.ge_s-arg-empty (result v128) + (i16x8.ge_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.ge_u-1st-arg-empty (result v128) + (i16x8.ge_u (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.ge_u-arg-empty (result v128) + (i16x8.ge_u) + ) + ) + "type mismatch" +) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_extadd_pairwise_i8x16.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_extadd_pairwise_i8x16.wast new file mode 100644 index 000000000..c2267de9c --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_extadd_pairwise_i8x16.wast @@ -0,0 +1,68 @@ +;; Tests for i16x8 arithmetic operations on major boundary values and all special values. + + +(module + (func (export "i16x8.extadd_pairwise_i8x16_s") (param v128) (result v128) (i16x8.extadd_pairwise_i8x16_s (local.get 0))) + (func (export "i16x8.extadd_pairwise_i8x16_u") (param v128) (result v128) (i16x8.extadd_pairwise_i8x16_u (local.get 0))) +) + + +;; i16x8.extadd_pairwise_i8x16_s +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_s" (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126)) + (v128.const i16x8 252 252 252 252 252 252 252 252)) +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_s" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i16x8 -254 -254 -254 -254 -254 -254 -254 -254)) +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 -256 -256 -256 -256 -256 -256 -256 -256)) +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_s" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i16x8 254 254 254 254 254 254 254 254)) +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) + +;; i16x8.extadd_pairwise_i8x16_u +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 510 510 510 510 510 510 510 510)) +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_u" (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126)) + (v128.const i16x8 252 252 252 252 252 252 252 252)) +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_u" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i16x8 258 258 258 258 258 258 258 258)) +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 256 256 256 256 256 256 256 256)) +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_u" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i16x8 254 254 254 254 254 254 254 254)) +(assert_return (invoke "i16x8.extadd_pairwise_i8x16_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 510 510 510 510 510 510 510 510)) + +;; type check +(assert_invalid (module (func (result v128) (i16x8.extadd_pairwise_i8x16_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.extadd_pairwise_i8x16_u (i32.const 0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i16x8.extadd_pairwise_i8x16_s-arg-empty (result v128) + (i16x8.extadd_pairwise_i8x16_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.extadd_pairwise_i8x16_u-arg-empty (result v128) + (i16x8.extadd_pairwise_i8x16_u) + ) + ) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_extmul_i8x16.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_extmul_i8x16.wast new file mode 100644 index 000000000..cc8cf8f40 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_extmul_i8x16.wast @@ -0,0 +1,404 @@ +;; Tests for i16x8 arithmetic operations on major boundary values and all special values. + + +(module + (func (export "i16x8.extmul_low_i8x16_s") (param v128 v128) (result v128) (i16x8.extmul_low_i8x16_s (local.get 0) (local.get 1))) + (func (export "i16x8.extmul_high_i8x16_s") (param v128 v128) (result v128) (i16x8.extmul_high_i8x16_s (local.get 0) (local.get 1))) + (func (export "i16x8.extmul_low_i8x16_u") (param v128 v128) (result v128) (i16x8.extmul_low_i8x16_u (local.get 0) (local.get 1))) + (func (export "i16x8.extmul_high_i8x16_u") (param v128 v128) (result v128) (i16x8.extmul_high_i8x16_u (local.get 0) (local.get 1))) +) + + +;; i16x8.extmul_low_i8x16_s +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i16x8 4032 4032 4032 4032 4032 4032 4032 4032)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i16x8 4096 4096 4096 4096 4096 4096 4096 4096)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i16x8 4032 4032 4032 4032 4032 4032 4032 4032)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i16x8 4096 4096 4096 4096 4096 4096 4096 4096)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i16x8 4160 4160 4160 4160 4160 4160 4160 4160)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 125 125 125 125 125 125 125 125)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i16x8 16129 16129 16129 16129 16129 16129 16129 16129)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i16x8 16256 16256 16256 16256 16256 16256 16256 16256)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i16x8 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i16x8.extmul_low_i8x16_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + +;; i16x8.extmul_high_i8x16_s +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i16x8 4032 4032 4032 4032 4032 4032 4032 4032)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i16x8 4096 4096 4096 4096 4096 4096 4096 4096)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i16x8 4032 4032 4032 4032 4032 4032 4032 4032)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i16x8 4096 4096 4096 4096 4096 4096 4096 4096)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i16x8 4160 4160 4160 4160 4160 4160 4160 4160)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 125 125 125 125 125 125 125 125)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i16x8 16129 16129 16129 16129 16129 16129 16129 16129)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i16x8 16256 16256 16256 16256 16256 16256 16256 16256)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i16x8 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i16x8.extmul_high_i8x16_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + +;; i16x8.extmul_low_i8x16_u +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -511 -511 -511 -511 -511 -511 -511 -511)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i16x8 4032 4032 4032 4032 4032 4032 4032 4032)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i16x8 4096 4096 4096 4096 4096 4096 4096 4096)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i16x8 -28480 -28480 -28480 -28480 -28480 -28480 -28480 -28480)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i16x8 -28672 -28672 -28672 -28672 -28672 -28672 -28672 -28672)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i16x8 -28864 -28864 -28864 -28864 -28864 -28864 -28864 -28864)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 125 125 125 125 125 125 125 125)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32386 -32386 -32386 -32386 -32386 -32386 -32386 -32386)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32641 -32641 -32641 -32641 -32641 -32641 -32641 -32641)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 32640 32640 32640 32640 32640 32640 32640 32640)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i16x8 16129 16129 16129 16129 16129 16129 16129 16129)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i16x8 16512 16512 16512 16512 16512 16512 16512 16512)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -511 -511 -511 -511 -511 -511 -511 -511)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i16x8 32385 32385 32385 32385 32385 32385 32385 32385)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 32640 32640 32640 32640 32640 32640 32640 32640)) +(assert_return (invoke "i16x8.extmul_low_i8x16_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 -511 -511 -511 -511 -511 -511 -511 -511)) + +;; i16x8.extmul_high_i8x16_u +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -511 -511 -511 -511 -511 -511 -511 -511)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i16x8 4032 4032 4032 4032 4032 4032 4032 4032)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i16x8 4096 4096 4096 4096 4096 4096 4096 4096)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i16x8 -28480 -28480 -28480 -28480 -28480 -28480 -28480 -28480)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i16x8 -28672 -28672 -28672 -28672 -28672 -28672 -28672 -28672)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i16x8 -28864 -28864 -28864 -28864 -28864 -28864 -28864 -28864)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 125 125 125 125 125 125 125 125)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32386 -32386 -32386 -32386 -32386 -32386 -32386 -32386)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32641 -32641 -32641 -32641 -32641 -32641 -32641 -32641)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 32640 32640 32640 32640 32640 32640 32640 32640)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i16x8 16129 16129 16129 16129 16129 16129 16129 16129)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i16x8 16512 16512 16512 16512 16512 16512 16512 16512)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -511 -511 -511 -511 -511 -511 -511 -511)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i16x8 32385 32385 32385 32385 32385 32385 32385 32385)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 32640 32640 32640 32640 32640 32640 32640 32640)) +(assert_return (invoke "i16x8.extmul_high_i8x16_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i16x8 -511 -511 -511 -511 -511 -511 -511 -511)) + +;; type check +(assert_invalid (module (func (result v128) (i16x8.extmul_low_i8x16_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.extmul_high_i8x16_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.extmul_low_i8x16_u (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.extmul_high_i8x16_u (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i16x8.extmul_low_i8x16_s-1st-arg-empty (result v128) + (i16x8.extmul_low_i8x16_s (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.extmul_low_i8x16_s-arg-empty (result v128) + (i16x8.extmul_low_i8x16_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.extmul_high_i8x16_s-1st-arg-empty (result v128) + (i16x8.extmul_high_i8x16_s (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.extmul_high_i8x16_s-arg-empty (result v128) + (i16x8.extmul_high_i8x16_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.extmul_low_i8x16_u-1st-arg-empty (result v128) + (i16x8.extmul_low_i8x16_u (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.extmul_low_i8x16_u-arg-empty (result v128) + (i16x8.extmul_low_i8x16_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.extmul_high_i8x16_u-1st-arg-empty (result v128) + (i16x8.extmul_high_i8x16_u (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.extmul_high_i8x16_u-arg-empty (result v128) + (i16x8.extmul_high_i8x16_u) + ) + ) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_q15mulr_sat_s.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_q15mulr_sat_s.wast new file mode 100644 index 000000000..2cea8cc74 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_q15mulr_sat_s.wast @@ -0,0 +1,110 @@ +;; Tests for i16x8 arithmetic operations on major boundary values and all special values. + + +(module + (func (export "i16x8.q15mulr_sat_s") (param v128 v128) (result v128) (i16x8.q15mulr_sat_s (local.get 0) (local.get 1))) +) + + +;; i16x8.q15mulr_sat_s +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 16383 16383 16383 16383 16383 16383 16383 16383) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 8192 8192 8192 8192 8192 8192 8192 8192)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 8192 8192 8192 8192 8192 8192 8192 8192)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 -16383 -16383 -16383 -16383 -16383 -16383 -16383 -16383) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 8192 8192 8192 8192 8192 8192 8192 8192)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 8192 8192 8192 8192 8192 8192 8192 8192)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 -16385 -16385 -16385 -16385 -16385 -16385 -16385 -16385) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 8193 8193 8193 8193 8193 8193 8193 8193)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.q15mulr_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + +;; type check +(assert_invalid (module (func (result v128) (i16x8.q15mulr_sat_s (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i16x8.q15mulr_sat_s-1st-arg-empty (result v128) + (i16x8.q15mulr_sat_s (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.q15mulr_sat_s-arg-empty (result v128) + (i16x8.q15mulr_sat_s) + ) + ) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_sat_arith.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_sat_arith.wast new file mode 100644 index 000000000..cea4ebbc7 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i16x8_sat_arith.wast @@ -0,0 +1,742 @@ +;; Tests for i16x8 arithmetic operations on major boundary values and all special values. + + +(module + (func (export "i16x8.add_sat_s") (param v128 v128) (result v128) (i16x8.add_sat_s (local.get 0) (local.get 1))) + (func (export "i16x8.add_sat_u") (param v128 v128) (result v128) (i16x8.add_sat_u (local.get 0) (local.get 1))) + (func (export "i16x8.sub_sat_s") (param v128 v128) (result v128) (i16x8.sub_sat_s (local.get 0) (local.get 1))) + (func (export "i16x8.sub_sat_u") (param v128 v128) (result v128) (i16x8.sub_sat_u (local.get 0) (local.get 1))) +) + + +;; i16x8.add_sat_s +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 16383 16383 16383 16383 16383 16383 16383 16383) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 -16383 -16383 -16383 -16383 -16383 -16383 -16383 -16383) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 -16385 -16385 -16385 -16385 -16385 -16385 -16385 -16385) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff) + (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000) + (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff) + (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000)) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000) + (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000) + (v128.const i16x8 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i16x8 0x01 0x7f81 0x01 0x7f81 0x01 0x7f81 0x01 0x7f81)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i16x8 0x01 0xff81 0x01 0xff81 0x01 0xff81 0x01 0xff81)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i16x8 0x01 0x7fc1 0x01 0x7fc1 0x01 0x7fc1 0x01 0x7fc1)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i16x8 0x01 0xffc1 0x01 0xffc1 0x01 0xffc1 0x01 0xffc1)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 0xffff 0xfffe 0xfffd 0xfffc 0xfffb 0xfffa 0xfff9)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 2 4 6 8 10 12 14)) + (v128.const i16x8 0 3 6 9 12 15 18 21)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 032_123 032_123 032_123 032_123 032_123 032_123 032_123 032_123)) + (v128.const i16x8 032_767 032_767 032_767 032_767 032_767 032_767 032_767 032_767)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 056_789 056_789 056_789 056_789 056_789 056_789 056_789 056_789)) + (v128.const i16x8 03_598 03_598 03_598 03_598 03_598 03_598 03_598 03_598)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678)) + (v128.const i16x8 0x0_68ac 0x0_68ac 0x0_68ac 0x0_68ac 0x0_68ac 0x0_68ac 0x0_68ac 0x0_68ac)) +(assert_return (invoke "i16x8.add_sat_s" (v128.const i16x8 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB) + (v128.const i16x8 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef)) + (v128.const i16x8 -0x0_8000 -0x0_8000 -0x0_8000 -0x0_8000 -0x0_8000 -0x0_8000 -0x0_8000 -0x0_8000)) + +;; i16x8.add_sat_u +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 16383 16383 16383 16383 16383 16383 16383 16383) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 -16383 -16383 -16383 -16383 -16383 -16383 -16383 -16383) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 -16385 -16385 -16385 -16385 -16385 -16385 -16385 -16385) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32769 32769 32769 32769 32769 32769 32769 32769)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 65534 65534 65534 65534 65534 65534 65534 65534)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff) + (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000) + (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000)) + (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff) + (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000) + (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000) + (v128.const i16x8 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) + (v128.const i16x8 65534 65534 65534 65534 65534 65534 65534 65534)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i16x8 0x8000 0xffff 0x8000 0xffff 0x8000 0xffff 0x8000 0xffff)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i16x8 0x01 0x7f81 0x01 0x7f81 0x01 0x7f81 0x01 0x7f81)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i16x8 0x01 0xff81 0x01 0xff81 0x01 0xff81 0x01 0xff81)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i16x8 0x01 0x7fc1 0x01 0x7fc1 0x01 0x7fc1 0x01 0x7fc1)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i16x8 0x01 0x7fc1 0x01 0x7fc1 0x01 0x7fc1 0x01 0x7fc1)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 0xffff 0xfffe 0xfffd 0xfffc 0xfffb 0xfffa 0xfff9)) + (v128.const i16x8 0 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 2 4 6 8 10 12 14)) + (v128.const i16x8 0 3 6 9 12 15 18 21)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 056_789 056_789 056_789 056_789 056_789 056_789 056_789 056_789)) + (v128.const i16x8 065_535 065_535 065_535 065_535 065_535 065_535 065_535 065_535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 -012_345 -012_345 -012_345 -012_345 -012_345 -012_345 -012_345 -012_345)) + (v128.const i16x8 065_535 065_535 065_535 065_535 065_535 065_535 065_535 065_535)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678)) + (v128.const i16x8 0x0_68ac 0x0_68ac 0x0_68ac 0x0_68ac 0x0_68ac 0x0_68ac 0x0_68ac 0x0_68ac)) +(assert_return (invoke "i16x8.add_sat_u" (v128.const i16x8 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB) + (v128.const i16x8 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef)) + (v128.const i16x8 0x0_ffff 0x0_ffff 0x0_ffff 0x0_ffff 0x0_ffff 0x0_ffff 0x0_ffff 0x0_ffff)) + +;; i16x8.sub_sat_s +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 16383 16383 16383 16383 16383 16383 16383 16383) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 -16383 -16383 -16383 -16383 -16383 -16383 -16383 -16383) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 -16385 -16385 -16385 -16385 -16385 -16385 -16385 -16385) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32764 32764 32764 32764 32764 32764 32764 32764)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32765 -32765 -32765 -32765 -32765 -32765 -32765 -32765)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff) + (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000) + (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff) + (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000) + (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000) + (v128.const i16x8 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01)) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i16x8 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i16x8 0x8000 0 0x8000 0 0x8000 0 0x8000 0)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i16x8 0x01 0x8081 0x01 0x8081 0x01 0x8081 0x01 0x8081)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i16x8 0x01 0x81 0x01 0x81 0x01 0x81 0x01 0x81)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i16x8 0x01 0x8041 0x01 0x8041 0x01 0x8041 0x01 0x8041)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i16x8 0x01 0x41 0x01 0x41 0x01 0x41 0x01 0x41)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 0xffff 0xfffe 0xfffd 0xfffc 0xfffb 0xfffa 0xfff9)) + (v128.const i16x8 0 2 4 6 8 10 12 14)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 2 4 6 8 10 12 14)) + (v128.const i16x8 0 -1 -2 -3 -4 -5 -6 -7)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 056_789 056_789 056_789 056_789 056_789 056_789 056_789 056_789)) + (v128.const i16x8 021_092 021_092 021_092 021_092 021_092 021_092 021_092 021_092)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 -012_345 -012_345 -012_345 -012_345 -012_345 -012_345 -012_345 -012_345)) + (v128.const i16x8 024_690 024_690 024_690 024_690 024_690 024_690 024_690 024_690)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678 0x0_5678)) + (v128.const i16x8 0x0_bbbc 0x0_bbbc 0x0_bbbc 0x0_bbbc 0x0_bbbc 0x0_bbbc 0x0_bbbc 0x0_bbbc)) +(assert_return (invoke "i16x8.sub_sat_s" (v128.const i16x8 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB) + (v128.const i16x8 -0x1234 -0x1234 -0x1234 -0x1234 -0x1234 -0x1234 -0x1234 -0x1234)) + (v128.const i16x8 0xa2df 0xa2df 0xa2df 0xa2df 0xa2df 0xa2df 0xa2df 0xa2df)) + +;; i16x8.sub_sat_u +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 16383 16383 16383 16383 16383 16383 16383 16383) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 -16383 -16383 -16383 -16383 -16383 -16383 -16383 -16383) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 -16385 -16385 -16385 -16385 -16385 -16385 -16385 -16385) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32764 32764 32764 32764 32764 32764 32764 32764)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 65534 65534 65534 65534 65534 65534 65534 65534)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff 0x3fff) + (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000) + (v128.const i16x8 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000 0x4000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff -0x3fff) + (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000) + (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000 -0x4000) + (v128.const i16x8 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001 -0x4001)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff) + (v128.const i16x8 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i16x8 65534 65534 65534 65534 65534 65534 65534 65534)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i16x8 0x8000 0 0x8000 0 0x8000 0 0x8000 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i16x8 0x01 0 0x01 0 0x01 0 0x01 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i16x8 0x01 0 0x01 0 0x01 0 0x01 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i16x8 0x01 0 0x01 0 0x01 0 0x01 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i16x8 0x01 0 0x01 0 0x01 0 0x01 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 0xffff 0xfffe 0xfffd 0xfffc 0xfffb 0xfffa 0xfff9)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0 1 2 3 4 5 6 7) + (v128.const i16x8 0 2 4 6 8 10 12 14)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345) + (v128.const i16x8 056_789 056_789 056_789 056_789 056_789 056_789 056_789 056_789)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 056_789 056_789 056_789 056_789 056_789 056_789 056_789 056_789) + (v128.const i16x8 -12_345 -12_345 -12_345 -12_345 -12_345 -12_345 -12_345 -12_345)) + (v128.const i16x8 03_598 03_598 03_598 03_598 03_598 03_598 03_598 03_598)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234) + (v128.const i16x8 -0x0_5678 -0x0_5678 -0x0_5678 -0x0_5678 -0x0_5678 -0x0_5678 -0x0_5678 -0x0_5678)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.sub_sat_u" (v128.const i16x8 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef 0x0_cdef) + (v128.const i16x8 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB 0x0_90AB)) + (v128.const i16x8 0x0_3d44 0x0_3d44 0x0_3d44 0x0_3d44 0x0_3d44 0x0_3d44 0x0_3d44 0x0_3d44)) + +;; Malformed cases: non-existent op names +(assert_malformed (module quote + "(func (result v128) (i16x8.add_sat (v128.const i16x8 1 1 1 1 1 1 1 1) (v128.const i16x8 2 2 2 2 2 2 2 2)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i16x8.sub_sat (v128.const i16x8 1 1 1 1 1 1 1 1) (v128.const i16x8 2 2 2 2 2 2 2 2)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i16x8.mul_sat (v128.const i16x8 1 1 1 1 1 1 1 1) (v128.const i16x8 2 2 2 2 2 2 2 2)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i16x8.div_sat (v128.const i16x8 1 1 1 1 1 1 1 1) (v128.const i16x8 2 2 2 2 2 2 2 2)))") + "unknown operator") + +;; type check +(assert_invalid (module (func (result v128) (i16x8.add_sat_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.add_sat_u (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.sub_sat_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.sub_sat_u (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i16x8.add_sat_s-1st-arg-empty (result v128) + (i16x8.add_sat_s (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.add_sat_s-arg-empty (result v128) + (i16x8.add_sat_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.add_sat_u-1st-arg-empty (result v128) + (i16x8.add_sat_u (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.add_sat_u-arg-empty (result v128) + (i16x8.add_sat_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.sub_sat_s-1st-arg-empty (result v128) + (i16x8.sub_sat_s (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.sub_sat_s-arg-empty (result v128) + (i16x8.sub_sat_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.sub_sat_u-1st-arg-empty (result v128) + (i16x8.sub_sat_u (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.sub_sat_u-arg-empty (result v128) + (i16x8.sub_sat_u) + ) + ) + "type mismatch" +) + +;; combination +(module + (func (export "sat-add_s-sub_s") (param v128 v128 v128) (result v128) + (i16x8.add_sat_s (i16x8.sub_sat_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "sat-add_s-sub_u") (param v128 v128 v128) (result v128) + (i16x8.add_sat_s (i16x8.sub_sat_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "sat-add_u-sub_s") (param v128 v128 v128) (result v128) + (i16x8.add_sat_u (i16x8.sub_sat_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "sat-add_u-sub_u") (param v128 v128 v128) (result v128) + (i16x8.add_sat_u (i16x8.sub_sat_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "sat-add_s-neg") (param v128 v128) (result v128) + (i16x8.add_sat_s (i16x8.neg (local.get 0)) (local.get 1))) + (func (export "sat-add_u-neg") (param v128 v128) (result v128) + (i16x8.add_sat_u (i16x8.neg (local.get 0)) (local.get 1))) + (func (export "sat-sub_s-neg") (param v128 v128) (result v128) + (i16x8.sub_sat_s (i16x8.neg (local.get 0)) (local.get 1))) + (func (export "sat-sub_u-neg") (param v128 v128) (result v128) + (i16x8.sub_sat_u (i16x8.neg (local.get 0)) (local.get 1))) +) + +(assert_return (invoke "sat-add_s-sub_s" (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "sat-add_s-sub_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "sat-add_u-sub_s" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 65534 65534 65534 65534 65534 65534 65534 65534)) +(assert_return (invoke "sat-add_u-sub_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "sat-add_s-neg" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "sat-add_u-neg" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) +(assert_return (invoke "sat-sub_s-neg" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "sat-sub_u-neg" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_arith.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_arith.wast new file mode 100644 index 000000000..f0e09b8bf --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_arith.wast @@ -0,0 +1,634 @@ +;; Tests for i32x4 arithmetic operations on major boundary values and all special values. + + +(module + (func (export "i32x4.add") (param v128 v128) (result v128) (i32x4.add (local.get 0) (local.get 1))) + (func (export "i32x4.sub") (param v128 v128) (result v128) (i32x4.sub (local.get 0) (local.get 1))) + (func (export "i32x4.mul") (param v128 v128) (result v128) (i32x4.mul (local.get 0) (local.get 1))) + (func (export "i32x4.neg") (param v128) (result v128) (i32x4.neg (local.get 0))) +) + + +;; i32x4.add +(assert_return (invoke "i32x4.add" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -2 -2 -2 -2)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 1073741823 1073741823 1073741823 1073741823) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 1073741824 1073741824 1073741824 1073741824) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 -1073741823 -1073741823 -1073741823 -1073741823) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 -1073741825 -1073741825 -1073741825 -1073741825) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 2147483645 2147483645 2147483645 2147483645) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 2147483646 2147483646 2147483646 2147483646)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 2147483646 2147483646 2147483646 2147483646) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 2147483648 2147483648 2147483648 2147483648) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 -2147483646 -2147483646 -2147483646 -2147483646) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 2147483647 2147483647 2147483647 2147483647) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i32x4 -2 -2 -2 -2)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -2 -2 -2 -2)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i32x4 2147483646 2147483646 2147483646 2147483646)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 -2 -2 -2 -2)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0x3fffffff 0x3fffffff 0x3fffffff 0x3fffffff) + (v128.const i32x4 0x40000000 0x40000000 0x40000000 0x40000000)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0x40000000 0x40000000 0x40000000 0x40000000) + (v128.const i32x4 0x40000000 0x40000000 0x40000000 0x40000000)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 -0x3fffffff -0x3fffffff -0x3fffffff -0x3fffffff) + (v128.const i32x4 -0x40000000 -0x40000000 -0x40000000 -0x40000000)) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 -0x40000000 -0x40000000 -0x40000000 -0x40000000) + (v128.const i32x4 -0x40000000 -0x40000000 -0x40000000 -0x40000000)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 -0x40000000 -0x40000000 -0x40000000 -0x40000000) + (v128.const i32x4 -0x40000001 -0x40000001 -0x40000001 -0x40000001)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff) + (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff)) + (v128.const i32x4 -2 -2 -2 -2)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff) + (v128.const i32x4 0x01 0x01 0x01 0x01)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const i32x4 -0x01 -0x01 -0x01 -0x01)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff) + (v128.const i32x4 0x01 0x01 0x01 0x01)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i32x4 -2 -2 -2 -2)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff) + (v128.const i8x16 0 0 0 0x80 0 0 0 0x80 0 0 0 0x80 0 0 0 0x80)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 1 1 1 1) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff) + (v128.const i16x8 0 0x8000 0 0x8000 0 0x8000 0 0x8000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 1 1 1 1) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const f32x4 +0.0 +0.0 +0.0 +0.0)) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const f32x4 1.0 1.0 1.0 1.0)) + (v128.const i32x4 0xbf800000 0xbf800000 0xbf800000 0xbf800000)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const f32x4 -1.0 -1.0 -1.0 -1.0)) + (v128.const i32x4 0x3f800000 0x3f800000 0x3f800000 0x3f800000)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 1 1 1 1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i32x4 0x7f800001 0x7f800001 0x7f800001 0x7f800001)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 1 1 1 1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0xff800001 0xff800001 0xff800001 0xff800001)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 1 1 1 1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0x7fc00001 0x7fc00001 0x7fc00001 0x7fc00001)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0 1 2 3) + (v128.const i32x4 0 0xffffffff 0xfffffffe 0xfffffffd)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0 1 2 3) + (v128.const i32x4 0 2 4 6)) + (v128.const i32x4 0 3 6 9)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890) + (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890)) + (v128.const i32x4 02_469_135_780 02_469_135_780 02_469_135_780 02_469_135_780)) +(assert_return (invoke "i32x4.add" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678) + (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef)) + (v128.const i32x4 0x0_a2e0_2467 0x0_a2e0_2467 0x0_a2e0_2467 0x0_a2e0_2467)) + +;; i32x4.sub +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 1073741823 1073741823 1073741823 1073741823) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 1073741824 1073741824 1073741824 1073741824) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 -1073741823 -1073741823 -1073741823 -1073741823) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 -1073741825 -1073741825 -1073741825 -1073741825) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 2147483645 2147483645 2147483645 2147483645) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 2147483644 2147483644 2147483644 2147483644)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 2147483646 2147483646 2147483646 2147483646) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 2147483645 2147483645 2147483645 2147483645)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 2147483648 2147483648 2147483648 2147483648) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 -2147483646 -2147483646 -2147483646 -2147483646) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -2147483645 -2147483645 -2147483645 -2147483645)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -2147483646 -2147483646 -2147483646 -2147483646)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 2147483647 2147483647 2147483647 2147483647) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 -2 -2 -2 -2)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x3fffffff 0x3fffffff 0x3fffffff 0x3fffffff) + (v128.const i32x4 0x40000000 0x40000000 0x40000000 0x40000000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x40000000 0x40000000 0x40000000 0x40000000) + (v128.const i32x4 0x40000000 0x40000000 0x40000000 0x40000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 -0x3fffffff -0x3fffffff -0x3fffffff -0x3fffffff) + (v128.const i32x4 -0x40000000 -0x40000000 -0x40000000 -0x40000000)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 -0x40000000 -0x40000000 -0x40000000 -0x40000000) + (v128.const i32x4 -0x40000000 -0x40000000 -0x40000000 -0x40000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 -0x40000000 -0x40000000 -0x40000000 -0x40000000) + (v128.const i32x4 -0x40000001 -0x40000001 -0x40000001 -0x40000001)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff) + (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff) + (v128.const i32x4 0x01 0x01 0x01 0x01)) + (v128.const i32x4 2147483646 2147483646 2147483646 2147483646)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const i32x4 -0x01 -0x01 -0x01 -0x01)) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff) + (v128.const i32x4 0x01 0x01 0x01 0x01)) + (v128.const i32x4 -2 -2 -2 -2)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff) + (v128.const i8x16 0 0 0 0x80 0 0 0 0x80 0 0 0 0x80 0 0 0 0x80)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 1 1 1 1) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff) + (v128.const i16x8 0 0x8000 0 0x8000 0 0x8000 0 0x8000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 1 1 1 1) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i32x4 0x02 0x02 0x02 0x02)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const f32x4 +0.0 +0.0 +0.0 +0.0)) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const f32x4 1.0 1.0 1.0 1.0)) + (v128.const i32x4 0x40800000 0x40800000 0x40800000 0x40800000)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const f32x4 -1.0 -1.0 -1.0 -1.0)) + (v128.const i32x4 0xc0800000 0xc0800000 0xc0800000 0xc0800000)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x1 0x1 0x1 0x1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i32x4 0x80800001 0x80800001 0x80800001 0x80800001)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x1 0x1 0x1 0x1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0x00800001 0x00800001 0x00800001 0x00800001)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x1 0x1 0x1 0x1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0x80400001 0x80400001 0x80400001 0x80400001)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0 1 2 3) + (v128.const i32x4 0 0xffffffff 0xfffffffe 0xfffffffd)) + (v128.const i32x4 0 0x02 0x04 0x06)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0 1 2 3) + (v128.const i32x4 0 2 4 6)) + (v128.const i32x4 0 -1 -2 -3)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 03_214_567_890 03_214_567_890 03_214_567_890 03_214_567_890 ) + (v128.const i32x4 01_234_567_890 01_234_567_890 01_234_567_890 01_234_567_890 )) + (v128.const i32x4 01_980_000_000 01_980_000_000 01_980_000_000 01_980_000_000)) +(assert_return (invoke "i32x4.sub" (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef) + (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678)) + (v128.const i32x4 0x0_7e77_7777 0x0_7e77_7777 0x0_7e77_7777 0x0_7e77_7777)) + +;; i32x4.mul +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 1073741823 1073741823 1073741823 1073741823) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 1073741824 1073741824 1073741824 1073741824) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 -1073741823 -1073741823 -1073741823 -1073741823) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 -1073741825 -1073741825 -1073741825 -1073741825) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 2147483645 2147483645 2147483645 2147483645) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 2147483645 2147483645 2147483645 2147483645)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 2147483646 2147483646 2147483646 2147483646) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 2147483646 2147483646 2147483646 2147483646)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 2147483648 2147483648 2147483648 2147483648) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 -2147483646 -2147483646 -2147483646 -2147483646) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 2147483646 2147483646 2147483646 2147483646)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 2147483647 2147483647 2147483647 2147483647) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x3fffffff 0x3fffffff 0x3fffffff 0x3fffffff) + (v128.const i32x4 0x40000000 0x40000000 0x40000000 0x40000000)) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x40000000 0x40000000 0x40000000 0x40000000) + (v128.const i32x4 0x40000000 0x40000000 0x40000000 0x40000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 -0x3fffffff -0x3fffffff -0x3fffffff -0x3fffffff) + (v128.const i32x4 -0x40000000 -0x40000000 -0x40000000 -0x40000000)) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 -0x40000000 -0x40000000 -0x40000000 -0x40000000) + (v128.const i32x4 -0x40000000 -0x40000000 -0x40000000 -0x40000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 -0x40000000 -0x40000000 -0x40000000 -0x40000000) + (v128.const i32x4 -0x40000001 -0x40000001 -0x40000001 -0x40000001)) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff) + (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff) + (v128.const i32x4 0x01 0x01 0x01 0x01)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const i32x4 -0x01 -0x01 -0x01 -0x01)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff) + (v128.const i32x4 0x01 0x01 0x01 0x01)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x10000000 0x10000000 0x10000000 0x10000000) + (v128.const i8x16 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const i16x8 0 0x02 0 0x02 0 0x02 0 0x02)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 +0.0 +0.0 +0.0 +0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 1.0 1.0 1.0 1.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x8000 0x8000 0x8000 0x8000) + (v128.const f32x4 -1.0 -1.0 -1.0 -1.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x1 0x1 0x1 0x1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i32x4 0x7f800000 0x7f800000 0x7f800000 0x7f800000)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x1 0x1 0x1 0x1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0xff800000 0xff800000 0xff800000 0xff800000)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x1 0x1 0x1 0x1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i32x4 0x7fc00000 0x7fc00000 0x7fc00000 0x7fc00000)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0 1 2 3) + (v128.const i32x4 0 0xffffffff 0xfffffffe 0xfffffffd)) + (v128.const i32x4 0 0xffffffff 0xfffffffc 0xfffffff7)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0 1 2 3) + (v128.const i32x4 0 2 4 6)) + (v128.const i32x4 0 0x02 0x08 0x12)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0_123_456_789 0_123_456_789 0_123_456_789 0_123_456_789) + (v128.const i32x4 0_987_654_321 0_987_654_321 0_987_654_321 0_987_654_321)) + (v128.const i32x4 04_227_814_277 04_227_814_277 04_227_814_277 04_227_814_277)) +(assert_return (invoke "i32x4.mul" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678) + (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef)) + (v128.const i32x4 0x0_2a42_d208 0x0_2a42_d208 0x0_2a42_d208 0x0_2a42_d208)) + +;; i32x4.neg +(assert_return (invoke "i32x4.neg" (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.neg" (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.neg" (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.neg" (v128.const i32x4 2147483646 2147483646 2147483646 2147483646)) + (v128.const i32x4 -2147483646 -2147483646 -2147483646 -2147483646)) +(assert_return (invoke "i32x4.neg" (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.neg" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.neg" (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) +(assert_return (invoke "i32x4.neg" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.neg" (v128.const i32x4 0x01 0x01 0x01 0x01)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.neg" (v128.const i32x4 -0x01 -0x01 -0x01 -0x01)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.neg" (v128.const i32x4 -0x80000000 -0x80000000 -0x80000000 -0x80000000)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.neg" (v128.const i32x4 -0x7fffffff -0x7fffffff -0x7fffffff -0x7fffffff)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.neg" (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff)) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) +(assert_return (invoke "i32x4.neg" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.neg" (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i32x4 1 1 1 1)) + +;; type check +(assert_invalid (module (func (result v128) (i32x4.neg (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.add (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.sub (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.mul (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i32x4.neg-arg-empty (result v128) + (i32x4.neg) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.add-1st-arg-empty (result v128) + (i32x4.add (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.add-arg-empty (result v128) + (i32x4.add) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.sub-1st-arg-empty (result v128) + (i32x4.sub (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.sub-arg-empty (result v128) + (i32x4.sub) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.mul-1st-arg-empty (result v128) + (i32x4.mul (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.mul-arg-empty (result v128) + (i32x4.mul) + ) + ) + "type mismatch" +) + +;; combination +(module + (func (export "add-sub") (param v128 v128 v128) (result v128) + (i32x4.add (i32x4.sub (local.get 0) (local.get 1))(local.get 2))) + (func (export "mul-add") (param v128 v128 v128) (result v128) + (i32x4.mul (i32x4.add (local.get 0) (local.get 1))(local.get 2))) + (func (export "mul-sub") (param v128 v128 v128) (result v128) + (i32x4.mul (i32x4.sub (local.get 0) (local.get 1))(local.get 2))) + (func (export "sub-add") (param v128 v128 v128) (result v128) + (i32x4.sub (i32x4.add (local.get 0) (local.get 1))(local.get 2))) + (func (export "add-neg") (param v128 v128) (result v128) + (i32x4.add (i32x4.neg (local.get 0)) (local.get 1))) + (func (export "mul-neg") (param v128 v128) (result v128) + (i32x4.mul (i32x4.neg (local.get 0)) (local.get 1))) + (func (export "sub-neg") (param v128 v128) (result v128) + (i32x4.sub (i32x4.neg (local.get 0)) (local.get 1))) +) + +(assert_return (invoke "add-sub" (v128.const i32x4 0 1 2 3) + (v128.const i32x4 0 2 4 6) + (v128.const i32x4 0 2 4 6)) + (v128.const i32x4 0 1 2 3)) +(assert_return (invoke "mul-add" (v128.const i32x4 0 1 2 3) + (v128.const i32x4 0 1 2 3) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 0 4 8 12)) +(assert_return (invoke "mul-sub" (v128.const i32x4 0 2 4 6) + (v128.const i32x4 0 1 2 3) + (v128.const i32x4 0 1 2 3)) + (v128.const i32x4 0 1 4 9)) +(assert_return (invoke "sub-add" (v128.const i32x4 0 1 2 3) + (v128.const i32x4 0 2 4 6) + (v128.const i32x4 0 2 4 6)) + (v128.const i32x4 0 1 2 3)) +(assert_return (invoke "add-neg" (v128.const i32x4 0 1 2 3) + (v128.const i32x4 0 1 2 3)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "mul-neg" (v128.const i32x4 0 1 2 3) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 0 -2 -4 -6)) +(assert_return (invoke "sub-neg" (v128.const i32x4 0 1 2 3) + (v128.const i32x4 0 1 2 3)) + (v128.const i32x4 0 -2 -4 -6)) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_arith2.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_arith2.wast new file mode 100644 index 000000000..63c3f4a42 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_arith2.wast @@ -0,0 +1,494 @@ +;; Tests for i32x4 [min_s, min_u, max_s, max_u, abs] operations. + +(module + (func (export "i32x4.min_s") (param v128 v128) (result v128) (i32x4.min_s (local.get 0) (local.get 1))) + (func (export "i32x4.min_u") (param v128 v128) (result v128) (i32x4.min_u (local.get 0) (local.get 1))) + (func (export "i32x4.max_s") (param v128 v128) (result v128) (i32x4.max_s (local.get 0) (local.get 1))) + (func (export "i32x4.max_u") (param v128 v128) (result v128) (i32x4.max_u (local.get 0) (local.get 1))) + (func (export "i32x4.abs") (param v128) (result v128) (i32x4.abs (local.get 0))) + (func (export "i32x4.min_s_with_const_0") (result v128) (i32x4.min_s (v128.const i32x4 -2147483648 2147483647 1073741824 4294967295) (v128.const i32x4 4294967295 1073741824 2147483647 -2147483648))) + (func (export "i32x4.min_s_with_const_1") (result v128) (i32x4.min_s (v128.const i32x4 0 1 2 3) (v128.const i32x4 3 2 1 0))) + (func (export "i32x4.min_u_with_const_2") (result v128) (i32x4.min_u (v128.const i32x4 -2147483648 2147483647 1073741824 4294967295) (v128.const i32x4 4294967295 1073741824 2147483647 -2147483648))) + (func (export "i32x4.min_u_with_const_3") (result v128) (i32x4.min_u (v128.const i32x4 0 1 2 3) (v128.const i32x4 3 2 1 0))) + (func (export "i32x4.max_s_with_const_4") (result v128) (i32x4.max_s (v128.const i32x4 -2147483648 2147483647 1073741824 4294967295) (v128.const i32x4 4294967295 1073741824 2147483647 -2147483648))) + (func (export "i32x4.max_s_with_const_5") (result v128) (i32x4.max_s (v128.const i32x4 0 1 2 3) (v128.const i32x4 3 2 1 0))) + (func (export "i32x4.max_u_with_const_6") (result v128) (i32x4.max_u (v128.const i32x4 -2147483648 2147483647 1073741824 4294967295) (v128.const i32x4 4294967295 1073741824 2147483647 -2147483648))) + (func (export "i32x4.max_u_with_const_7") (result v128) (i32x4.max_u (v128.const i32x4 0 1 2 3) (v128.const i32x4 3 2 1 0))) + (func (export "i32x4.abs_with_const_8") (result v128) (i32x4.abs (v128.const i32x4 -2147483648 2147483647 1073741824 4294967295))) + (func (export "i32x4.min_s_with_const_9") (param v128) (result v128) (i32x4.min_s (local.get 0) (v128.const i32x4 -2147483648 2147483647 1073741824 4294967295))) + (func (export "i32x4.min_s_with_const_10") (param v128) (result v128) (i32x4.min_s (local.get 0) (v128.const i32x4 0 1 2 3))) + (func (export "i32x4.min_u_with_const_11") (param v128) (result v128) (i32x4.min_u (local.get 0) (v128.const i32x4 -2147483648 2147483647 1073741824 4294967295))) + (func (export "i32x4.min_u_with_const_12") (param v128) (result v128) (i32x4.min_u (local.get 0) (v128.const i32x4 0 1 2 3))) + (func (export "i32x4.max_s_with_const_13") (param v128) (result v128) (i32x4.max_s (local.get 0) (v128.const i32x4 -2147483648 2147483647 1073741824 4294967295))) + (func (export "i32x4.max_s_with_const_14") (param v128) (result v128) (i32x4.max_s (local.get 0) (v128.const i32x4 0 1 2 3))) + (func (export "i32x4.max_u_with_const_15") (param v128) (result v128) (i32x4.max_u (local.get 0) (v128.const i32x4 -2147483648 2147483647 1073741824 4294967295))) + (func (export "i32x4.max_u_with_const_16") (param v128) (result v128) (i32x4.max_u (local.get 0) (v128.const i32x4 0 1 2 3))) +) + +(assert_return (invoke "i32x4.min_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.min_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.min_s" (v128.const i32x4 0 0 -1 -1) + (v128.const i32x4 0 -1 0 -1)) + (v128.const i32x4 0 -1 -1 -1)) +(assert_return (invoke "i32x4.min_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) +(assert_return (invoke "i32x4.min_s" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.min_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) +(assert_return (invoke "i32x4.min_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 128 128 128 128)) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) +(assert_return (invoke "i32x4.min_s" (v128.const i32x4 2147483648 2147483648 2147483648 2147483648) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 2147483648 2147483648 2147483648 2147483648)) +(assert_return (invoke "i32x4.min_s" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) +(assert_return (invoke "i32x4.min_s" (v128.const i32x4 123 123 123 123) + (v128.const i32x4 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i32x4 123 123 123 123)) +(assert_return (invoke "i32x4.min_s" (v128.const i32x4 0x80 0x80 0x80 0x80) + (v128.const i32x4 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i32x4 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i32x4.min_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.min_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.min_u" (v128.const i32x4 0 0 -1 -1) + (v128.const i32x4 0 -1 0 -1)) + (v128.const i32x4 0 0 0 -1)) +(assert_return (invoke "i32x4.min_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.min_u" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.min_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.min_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 128 128 128 128)) + (v128.const i32x4 128 128 128 128)) +(assert_return (invoke "i32x4.min_u" (v128.const i32x4 2147483648 2147483648 2147483648 2147483648) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 2147483648 2147483648 2147483648 2147483648)) +(assert_return (invoke "i32x4.min_u" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) +(assert_return (invoke "i32x4.min_u" (v128.const i32x4 123 123 123 123) + (v128.const i32x4 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i32x4 123 123 123 123)) +(assert_return (invoke "i32x4.min_u" (v128.const i32x4 0x80 0x80 0x80 0x80) + (v128.const i32x4 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i32x4 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i32x4.max_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.max_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.max_s" (v128.const i32x4 0 0 -1 -1) + (v128.const i32x4 0 -1 0 -1)) + (v128.const i32x4 0 0 0 -1)) +(assert_return (invoke "i32x4.max_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.max_s" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.max_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.max_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 128 128 128 128)) + (v128.const i32x4 128 128 128 128)) +(assert_return (invoke "i32x4.max_s" (v128.const i32x4 2147483648 2147483648 2147483648 2147483648) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 2147483648 2147483648 2147483648 2147483648)) +(assert_return (invoke "i32x4.max_s" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) +(assert_return (invoke "i32x4.max_s" (v128.const i32x4 123 123 123 123) + (v128.const i32x4 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i32x4 123 123 123 123)) +(assert_return (invoke "i32x4.max_s" (v128.const i32x4 0x80 0x80 0x80 0x80) + (v128.const i32x4 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i32x4 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i32x4.max_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.max_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.max_u" (v128.const i32x4 0 0 -1 -1) + (v128.const i32x4 0 -1 0 -1)) + (v128.const i32x4 0 -1 -1 -1)) +(assert_return (invoke "i32x4.max_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) +(assert_return (invoke "i32x4.max_u" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.max_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) +(assert_return (invoke "i32x4.max_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 128 128 128 128)) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) +(assert_return (invoke "i32x4.max_u" (v128.const i32x4 2147483648 2147483648 2147483648 2147483648) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 2147483648 2147483648 2147483648 2147483648)) +(assert_return (invoke "i32x4.max_u" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) +(assert_return (invoke "i32x4.max_u" (v128.const i32x4 123 123 123 123) + (v128.const i32x4 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i32x4 123 123 123 123)) +(assert_return (invoke "i32x4.max_u" (v128.const i32x4 0x80 0x80 0x80 0x80) + (v128.const i32x4 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i32x4 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i32x4 0x1 0x1 0x1 0x1)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 2147483648 2147483648 2147483648 2147483648)) + (v128.const i32x4 2147483648 2147483648 2147483648 2147483648)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i32x4 2147483648 2147483648 2147483648 2147483648)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 -0x80000000 -0x80000000 -0x80000000 -0x80000000)) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) + (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i32x4 01_2_3 01_2_3 01_2_3 01_2_3)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 -01_2_3 -01_2_3 -01_2_3 -01_2_3)) + (v128.const i32x4 123 123 123 123)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 0x80 0x80 0x80 0x80)) + (v128.const i32x4 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 -0x80 -0x80 -0x80 -0x80)) + (v128.const i32x4 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i32x4 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0)) + (v128.const i32x4 0x80 0x80 0x80 0x80)) + +;; Const vs const +(assert_return (invoke "i32x4.min_s_with_const_0") (v128.const i32x4 -2147483648 1073741824 1073741824 -2147483648)) +(assert_return (invoke "i32x4.min_s_with_const_1") (v128.const i32x4 0 1 1 0)) +(assert_return (invoke "i32x4.min_u_with_const_2") (v128.const i32x4 -2147483648 1073741824 1073741824 -2147483648)) +(assert_return (invoke "i32x4.min_u_with_const_3") (v128.const i32x4 0 1 1 0)) +(assert_return (invoke "i32x4.max_s_with_const_4") (v128.const i32x4 4294967295 2147483647 2147483647 4294967295)) +(assert_return (invoke "i32x4.max_s_with_const_5") (v128.const i32x4 3 2 2 3)) +(assert_return (invoke "i32x4.max_u_with_const_6") (v128.const i32x4 4294967295 2147483647 2147483647 4294967295)) +(assert_return (invoke "i32x4.max_u_with_const_7") (v128.const i32x4 3 2 2 3)) +(assert_return (invoke "i32x4.abs_with_const_8") (v128.const i32x4 2147483648 2147483647 1073741824 1)) + +;; Param vs const +(assert_return (invoke "i32x4.min_s_with_const_9" (v128.const i32x4 4294967295 1073741824 2147483647 -2147483648)) + (v128.const i32x4 -2147483648 1073741824 1073741824 -2147483648)) +(assert_return (invoke "i32x4.min_s_with_const_10" (v128.const i32x4 3 2 1 0)) + (v128.const i32x4 0 1 1 0)) +(assert_return (invoke "i32x4.min_u_with_const_11" (v128.const i32x4 4294967295 1073741824 2147483647 -2147483648)) + (v128.const i32x4 -2147483648 1073741824 1073741824 -2147483648)) +(assert_return (invoke "i32x4.min_u_with_const_12" (v128.const i32x4 3 2 1 0)) + (v128.const i32x4 0 1 1 0)) +(assert_return (invoke "i32x4.max_s_with_const_13" (v128.const i32x4 4294967295 1073741824 2147483647 -2147483648)) + (v128.const i32x4 4294967295 2147483647 2147483647 4294967295)) +(assert_return (invoke "i32x4.max_s_with_const_14" (v128.const i32x4 3 2 1 0)) + (v128.const i32x4 3 2 2 3)) +(assert_return (invoke "i32x4.max_u_with_const_15" (v128.const i32x4 4294967295 1073741824 2147483647 -2147483648)) + (v128.const i32x4 4294967295 2147483647 2147483647 4294967295)) +(assert_return (invoke "i32x4.max_u_with_const_16" (v128.const i32x4 3 2 1 0)) + (v128.const i32x4 3 2 2 3)) + +;; Test different lanes go through different if-then clauses +(assert_return (invoke "i32x4.min_s" (v128.const i32x4 -2147483648 2147483647 1073741824 4294967295) + (v128.const i32x4 4294967295 1073741824 2147483647 -2147483648)) + (v128.const i32x4 -2147483648 1073741824 1073741824 -2147483648)) +(assert_return (invoke "i32x4.min_s" (v128.const i32x4 0 1 2 128) + (v128.const i32x4 0 2 1 128)) + (v128.const i32x4 0 1 1 128)) +(assert_return (invoke "i32x4.min_u" (v128.const i32x4 -2147483648 2147483647 1073741824 4294967295) + (v128.const i32x4 4294967295 1073741824 2147483647 -2147483648)) + (v128.const i32x4 -2147483648 1073741824 1073741824 -2147483648)) +(assert_return (invoke "i32x4.min_u" (v128.const i32x4 0 1 2 128) + (v128.const i32x4 0 2 1 128)) + (v128.const i32x4 0 1 1 128)) +(assert_return (invoke "i32x4.max_s" (v128.const i32x4 -2147483648 2147483647 1073741824 4294967295) + (v128.const i32x4 4294967295 1073741824 2147483647 -2147483648)) + (v128.const i32x4 4294967295 2147483647 2147483647 4294967295)) +(assert_return (invoke "i32x4.max_s" (v128.const i32x4 0 1 2 128) + (v128.const i32x4 0 2 1 128)) + (v128.const i32x4 0 2 2 128)) +(assert_return (invoke "i32x4.max_u" (v128.const i32x4 -2147483648 2147483647 1073741824 4294967295) + (v128.const i32x4 4294967295 1073741824 2147483647 -2147483648)) + (v128.const i32x4 4294967295 2147483647 2147483647 4294967295)) +(assert_return (invoke "i32x4.max_u" (v128.const i32x4 0 1 2 128) + (v128.const i32x4 0 2 1 128)) + (v128.const i32x4 0 2 2 128)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 -2147483648 2147483647 1073741824 4294967295)) + (v128.const i32x4 2147483648 2147483647 1073741824 1)) + +;; Test opposite signs of zero +(assert_return (invoke "i32x4.min_s" (v128.const i32x4 -0 -0 +0 +0) + (v128.const i32x4 +0 0 -0 0)) + (v128.const i32x4 -0 -0 +0 +0)) +(assert_return (invoke "i32x4.min_s" (v128.const i32x4 -0 -0 -0 -0) + (v128.const i32x4 +0 +0 +0 +0)) + (v128.const i32x4 -0 -0 -0 -0)) +(assert_return (invoke "i32x4.min_u" (v128.const i32x4 -0 -0 +0 +0) + (v128.const i32x4 +0 0 -0 0)) + (v128.const i32x4 -0 -0 +0 +0)) +(assert_return (invoke "i32x4.min_u" (v128.const i32x4 -0 -0 -0 -0) + (v128.const i32x4 +0 +0 +0 +0)) + (v128.const i32x4 -0 -0 -0 -0)) +(assert_return (invoke "i32x4.max_s" (v128.const i32x4 -0 -0 +0 +0) + (v128.const i32x4 +0 0 -0 0)) + (v128.const i32x4 -0 -0 +0 +0)) +(assert_return (invoke "i32x4.max_s" (v128.const i32x4 -0 -0 -0 -0) + (v128.const i32x4 +0 +0 +0 +0)) + (v128.const i32x4 -0 -0 -0 -0)) +(assert_return (invoke "i32x4.max_u" (v128.const i32x4 -0 -0 +0 +0) + (v128.const i32x4 +0 0 -0 0)) + (v128.const i32x4 -0 -0 +0 +0)) +(assert_return (invoke "i32x4.max_u" (v128.const i32x4 -0 -0 -0 -0) + (v128.const i32x4 +0 +0 +0 +0)) + (v128.const i32x4 -0 -0 -0 -0)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 -0 -0 +0 +0)) + (v128.const i32x4 -0 -0 +0 +0)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 +0 0 -0 0)) + (v128.const i32x4 +0 0 -0 0)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 -0 -0 -0 -0)) + (v128.const i32x4 -0 -0 -0 -0)) +(assert_return (invoke "i32x4.abs" (v128.const i32x4 +0 +0 +0 +0)) + (v128.const i32x4 +0 +0 +0 +0)) + +;; Unknown operators +(assert_malformed (module quote "(memory 1) (func (result v128) (f32x4.min_s (v128.const i32x4 0 0 0 0) (v128.const i32x4 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (f32x4.min_u (v128.const i32x4 0 0 0 0) (v128.const i32x4 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (f32x4.max_s (v128.const i32x4 0 0 0 0) (v128.const i32x4 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (f32x4.max_u (v128.const i32x4 0 0 0 0) (v128.const i32x4 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.min_s (v128.const i32x4 0 0 0 0) (v128.const i32x4 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.min_u (v128.const i32x4 0 0 0 0) (v128.const i32x4 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.max_s (v128.const i32x4 0 0 0 0) (v128.const i32x4 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.max_u (v128.const i32x4 0 0 0 0) (v128.const i32x4 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (f64x2.min_s (v128.const i32x4 0 0 0 0) (v128.const i32x4 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (f64x2.min_u (v128.const i32x4 0 0 0 0) (v128.const i32x4 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (f64x2.max_s (v128.const i32x4 0 0 0 0) (v128.const i32x4 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (f64x2.max_u (v128.const i32x4 0 0 0 0) (v128.const i32x4 1 1 1 1)))") "unknown operator") + +;; Type check +(assert_invalid (module (func (result v128) (i32x4.min_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.min_u (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.max_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.max_u (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.abs (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i32x4.min_s-1st-arg-empty (result v128) + (i32x4.min_s (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.min_s-arg-empty (result v128) + (i32x4.min_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.min_u-1st-arg-empty (result v128) + (i32x4.min_u (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.min_u-arg-empty (result v128) + (i32x4.min_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.max_s-1st-arg-empty (result v128) + (i32x4.max_s (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.max_s-arg-empty (result v128) + (i32x4.max_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.max_u-1st-arg-empty (result v128) + (i32x4.max_u (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.max_u-arg-empty (result v128) + (i32x4.max_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.abs-arg-empty (result v128) + (i32x4.abs) + ) + ) + "type mismatch" +) + +;; Combination +(module + (func (export "i32x4.min_s-i32x4.max_u") (param v128 v128 v128) (result v128) (i32x4.min_s (i32x4.max_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.min_s-i32x4.max_s") (param v128 v128 v128) (result v128) (i32x4.min_s (i32x4.max_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.min_s-i32x4.min_u") (param v128 v128 v128) (result v128) (i32x4.min_s (i32x4.min_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.min_s-i32x4.min_s") (param v128 v128 v128) (result v128) (i32x4.min_s (i32x4.min_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.min_s-i32x4.abs") (param v128 v128) (result v128) (i32x4.min_s (i32x4.abs (local.get 0))(local.get 1))) + (func (export "i32x4.abs-i32x4.min_s") (param v128 v128) (result v128) (i32x4.abs (i32x4.min_s (local.get 0) (local.get 1)))) + (func (export "i32x4.min_u-i32x4.max_u") (param v128 v128 v128) (result v128) (i32x4.min_u (i32x4.max_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.min_u-i32x4.max_s") (param v128 v128 v128) (result v128) (i32x4.min_u (i32x4.max_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.min_u-i32x4.min_u") (param v128 v128 v128) (result v128) (i32x4.min_u (i32x4.min_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.min_u-i32x4.min_s") (param v128 v128 v128) (result v128) (i32x4.min_u (i32x4.min_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.min_u-i32x4.abs") (param v128 v128) (result v128) (i32x4.min_u (i32x4.abs (local.get 0))(local.get 1))) + (func (export "i32x4.abs-i32x4.min_u") (param v128 v128) (result v128) (i32x4.abs (i32x4.min_u (local.get 0) (local.get 1)))) + (func (export "i32x4.max_s-i32x4.max_u") (param v128 v128 v128) (result v128) (i32x4.max_s (i32x4.max_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.max_s-i32x4.max_s") (param v128 v128 v128) (result v128) (i32x4.max_s (i32x4.max_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.max_s-i32x4.min_u") (param v128 v128 v128) (result v128) (i32x4.max_s (i32x4.min_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.max_s-i32x4.min_s") (param v128 v128 v128) (result v128) (i32x4.max_s (i32x4.min_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.max_s-i32x4.abs") (param v128 v128) (result v128) (i32x4.max_s (i32x4.abs (local.get 0))(local.get 1))) + (func (export "i32x4.abs-i32x4.max_s") (param v128 v128) (result v128) (i32x4.abs (i32x4.max_s (local.get 0) (local.get 1)))) + (func (export "i32x4.max_u-i32x4.max_u") (param v128 v128 v128) (result v128) (i32x4.max_u (i32x4.max_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.max_u-i32x4.max_s") (param v128 v128 v128) (result v128) (i32x4.max_u (i32x4.max_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.max_u-i32x4.min_u") (param v128 v128 v128) (result v128) (i32x4.max_u (i32x4.min_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.max_u-i32x4.min_s") (param v128 v128 v128) (result v128) (i32x4.max_u (i32x4.min_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i32x4.max_u-i32x4.abs") (param v128 v128) (result v128) (i32x4.max_u (i32x4.abs (local.get 0))(local.get 1))) + (func (export "i32x4.abs-i32x4.max_u") (param v128 v128) (result v128) (i32x4.abs (i32x4.max_u (local.get 0) (local.get 1)))) + (func (export "i32x4.abs-i32x4.abs") (param v128) (result v128) (i32x4.abs (i32x4.abs (local.get 0)))) +) + +(assert_return (invoke "i32x4.min_s-i32x4.max_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.min_s-i32x4.max_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.min_s-i32x4.min_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.min_s-i32x4.min_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.min_s-i32x4.abs" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.abs-i32x4.min_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.min_u-i32x4.max_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.min_u-i32x4.max_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.min_u-i32x4.min_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.min_u-i32x4.min_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.min_u-i32x4.abs" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.abs-i32x4.min_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.max_s-i32x4.max_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.max_s-i32x4.max_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.max_s-i32x4.min_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.max_s-i32x4.min_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.max_s-i32x4.abs" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.abs-i32x4.max_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.max_u-i32x4.max_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.max_u-i32x4.max_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.max_u-i32x4.min_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.max_u-i32x4.min_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1) + (v128.const i32x4 2 2 2 2)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.max_u-i32x4.abs" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.abs-i32x4.max_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.abs-i32x4.abs" (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 1 1 1 1)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_cmp.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_cmp.wast new file mode 100644 index 000000000..fca45ab96 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_cmp.wast @@ -0,0 +1,1920 @@ + +;; Test all the i32x4 comparison operators on major boundary values and all special values. + +(module + (func (export "eq") (param $x v128) (param $y v128) (result v128) (i32x4.eq (local.get $x) (local.get $y))) + (func (export "ne") (param $x v128) (param $y v128) (result v128) (i32x4.ne (local.get $x) (local.get $y))) + (func (export "lt_s") (param $x v128) (param $y v128) (result v128) (i32x4.lt_s (local.get $x) (local.get $y))) + (func (export "lt_u") (param $x v128) (param $y v128) (result v128) (i32x4.lt_u (local.get $x) (local.get $y))) + (func (export "le_s") (param $x v128) (param $y v128) (result v128) (i32x4.le_s (local.get $x) (local.get $y))) + (func (export "le_u") (param $x v128) (param $y v128) (result v128) (i32x4.le_u (local.get $x) (local.get $y))) + (func (export "gt_s") (param $x v128) (param $y v128) (result v128) (i32x4.gt_s (local.get $x) (local.get $y))) + (func (export "gt_u") (param $x v128) (param $y v128) (result v128) (i32x4.gt_u (local.get $x) (local.get $y))) + (func (export "ge_s") (param $x v128) (param $y v128) (result v128) (i32x4.ge_s (local.get $x) (local.get $y))) + (func (export "ge_u") (param $x v128) (param $y v128) (result v128) (i32x4.ge_u (local.get $x) (local.get $y))) +) + + +;; eq + +;; i32x4.eq (i32x4) (i32x4) + +;; hex vs hex +(assert_return (invoke "eq" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000) + (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B) + (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; hex vs dec +(assert_return (invoke "eq" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 2155905152 2155905152 2155905152 2155905152)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 -2139062144 -2139062144 -2139062144 -2139062144)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0x83828180 0x00FFFEFD 0x7F020100 0xFFFEFD80) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; dec vs dec +(assert_return (invoke "eq" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 4294967295 4294967295 0 0) + (v128.const i32x4 4294967295 4294967295 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0 0 4294967295 4294967295) + (v128.const i32x4 0 0 4294967295 4294967295)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 -2147483647 4294967295 0 -1) + (v128.const i32x4 2147483649 -1 0 -1)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "eq" (v128.const i32x4 0xc3000000 0xc2fe0000 0xbf800000 0x00000000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0x3f800000 0x42fe0000 0x43000000 0x437f0000) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; not equal +(assert_return (invoke "eq" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const i32x4 0x02030001 0x10110409 0x0B1A120A 0xABFF1BAA) + (v128.const i32x4 0xAA1BFFAB 0x0A121A0B 0x09041110 0x01000302)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const i32x4 0x80018000 0x80038002 0x80058004 0x80078006) + (v128.const i32x4 2147975174 2147844100 2147713026 2147581952)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const i32x4 2147483648 2147483647 0 -1) + (v128.const i32x4 -2147483648 -2147483647 -1 0)) + (v128.const i32x4 -1 0 0 0)) + +;; i32x4.eq (i32x4) (i8x16) +(assert_return (invoke "eq" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 -8323200 0 1 4294967295) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i32x4 0 -1 0 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i32x4 0 0 0 0)) + +;; i32x4.eq (i32x4) (i16x8) +(assert_return (invoke "eq" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 4294967295 0 1 65535) + (v128.const i16x8 65535 65535 0 0 1 0 65535 65535)) + (v128.const i32x4 -1 -1 -1 0)) +(assert_return (invoke "eq" (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i16x8 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "eq" (v128.const i32x4 0_123_456_789 0_123_456_789 0_123_456_789 0_123_456_789) + (v128.const i32x4 123456789 123456789 123456789 123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678) + (v128.const i32x4 0x12345678 0x12345678 0x12345678 0x12345678)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; ne + +;; i32x4.ne (i32x4) (i32x4) + +;; hex vs hex +(assert_return (invoke "ne" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000) + (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B) + (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B)) + (v128.const i32x4 0 0 0 0)) + +;; hex vs dec +(assert_return (invoke "ne" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 2155905152 2155905152 2155905152 2155905152)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 -2139062144 -2139062144 -2139062144 -2139062144)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0x83828180 0x00FFFEFD 0x7F020100 0xFFFEFD80) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i32x4 0 0 0 0)) + +;; dec vs dec +(assert_return (invoke "ne" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 4294967295 4294967295 0 0) + (v128.const i32x4 4294967295 4294967295 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0 0 4294967295 4294967295) + (v128.const i32x4 0 0 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 -2147483647 4294967295 0 -1) + (v128.const i32x4 2147483649 -1 0 -1)) + (v128.const i32x4 0 0 0 0)) + +;; hex vs float +(assert_return (invoke "ne" (v128.const i32x4 0xc3000000 0xc2fe0000 0xbf800000 0x00000000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0x3f800000 0x42fe0000 0x43000000 0x437f0000) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i32x4 0 0 0 0)) + +;; not equal +(assert_return (invoke "ne" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i32x4 0x02030001 0x10110409 0x0B1A120A 0xABFF1BAA) + (v128.const i32x4 0xAA1BFFAB 0x0A121A0B 0x09041110 0x01000302)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i32x4 0x80018000 0x80038002 0x80058004 0x80078006) + (v128.const i32x4 2147975174 2147844100 2147713026 2147581952)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i32x4 2147483648 2147483647 0 -1) + (v128.const i32x4 -2147483648 -2147483647 -1 0)) + (v128.const i32x4 0 -1 -1 -1)) + +;; i32x4.ne (i32x4) (i8x16) +(assert_return (invoke "ne" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 -8323200 0 1 4294967295) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i32x4 -1 0 -1 0)) +(assert_return (invoke "ne" (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; i32x4.ne (i32x4) (i16x8) +(assert_return (invoke "ne" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 -128 0 1 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i32x4 -1 0 -1 -1)) +(assert_return (invoke "ne" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i32x4 0_123_456_789 0_123_456_789 0_123_456_789 0_123_456_789) + (v128.const i32x4 123456789 123456789 123456789 123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678) + (v128.const i32x4 0x12345678 0x12345678 0x12345678 0x12345678)) + (v128.const i32x4 0 0 0 0)) + +;; lt_s + +;; i32x4.lt_s (i32x4) (i32x4) + +;; hex vs hex +(assert_return (invoke "lt_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000) + (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B) + (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B)) + (v128.const i32x4 0 0 0 0)) + +;; hex vs dec +(assert_return (invoke "lt_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 2155905152 2155905152 2155905152 2155905152)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 -2139062144 -2139062144 -2139062144 -2139062144)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0x83828180 0x00FFFEFD 0x7F020100 0xFFFEFD80) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i32x4 0 0 0 0)) + +;; dec vs dec +(assert_return (invoke "lt_s" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 4294967295 4294967295 0 0) + (v128.const i32x4 4294967295 4294967295 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0 0 4294967295 4294967295) + (v128.const i32x4 0 0 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 -2147483647 4294967295 0 -1) + (v128.const i32x4 2147483649 -1 0 -1)) + (v128.const i32x4 0 0 0 0)) + +;; hex vs float +(assert_return (invoke "lt_s" (v128.const i32x4 0xc3000000 0xc2fe0000 0xbf800000 0x00000000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0x3f800000 0x42fe0000 0x43000000 0x437f0000) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i32x4 0 0 0 0)) + +;; not equal +(assert_return (invoke "lt_s" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 0 0 -1 -1)) +(assert_return (invoke "lt_s" (v128.const i32x4 0x02030001 0x10110409 0x0B1A120A 0xABFF1BAA) + (v128.const i32x4 0xAA1BFFAB 0x0A121A0B 0x09041110 0x01000302)) + (v128.const i32x4 0 0 0 -1)) +(assert_return (invoke "lt_s" (v128.const i32x4 0x80018000 0x80038002 0x80058004 0x80078006) + (v128.const i32x4 2147975174 2147844100 2147713026 2147581952)) + (v128.const i32x4 -1 -1 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 2147483648 2147483647 0 -1) + (v128.const i32x4 -2147483648 -2147483647 -1 0)) + (v128.const i32x4 0 0 0 -1)) + +;; i32x4.lt_s (i32x4) (i8x16) +(assert_return (invoke "lt_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 -8323200 0 1 4294967295) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i32x4 0 0 -1 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i32x4 0 0 0 0)) + +;; i32x4.lt_s (i32x4) (i16x8) +(assert_return (invoke "lt_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 -128 0 1 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i32x4 0 0 -1 -1)) +(assert_return (invoke "lt_s" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt_s" (v128.const i32x4 0_123_456_789 0_123_456_789 0_123_456_789 0_123_456_789) + (v128.const i32x4 123456789 123456789 123456789 123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef) + (v128.const i32x4 -0x6f543210 -0x6f543210 -0x6f543210 -0x6f543210)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; lt_u + +;; i32x4.lt_u (i32x4) (i32x4) + +;; hex vs hex +(assert_return (invoke "lt_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000) + (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B) + (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B)) + (v128.const i32x4 0 0 0 0)) + +;; hex vs dec +(assert_return (invoke "lt_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 2155905152 2155905152 2155905152 2155905152)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 -2139062144 -2139062144 -2139062144 -2139062144)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0x83828180 0x00FFFEFD 0x7F020100 0xFFFEFD80) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i32x4 0 0 0 0)) + +;; dec vs dec +(assert_return (invoke "lt_u" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 4294967295 4294967295 0 0) + (v128.const i32x4 4294967295 4294967295 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0 0 4294967295 4294967295) + (v128.const i32x4 0 0 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 -2147483647 4294967295 0 -1) + (v128.const i32x4 2147483649 -1 0 -1)) + (v128.const i32x4 0 0 0 0)) + +;; hex vs float +(assert_return (invoke "lt_u" (v128.const i32x4 0xc3000000 0xc2fe0000 0xbf800000 0x00000000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0x3f800000 0x42fe0000 0x43000000 0x437f0000) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i32x4 0 0 0 0)) + +;; not equal +(assert_return (invoke "lt_u" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "lt_u" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 -1 -1 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0x02030001 0x10110409 0x0B1A120A 0xABFF1BAA) + (v128.const i32x4 0xAA1BFFAB 0x0A121A0B 0x09041110 0x01000302)) + (v128.const i32x4 -1 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0x80018000 0x80038002 0x80058004 0x80078006) + (v128.const i32x4 2147975174 2147844100 2147713026 2147581952)) + (v128.const i32x4 -1 -1 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 2147483648 2147483647 0 -1) + (v128.const i32x4 -2147483648 -2147483647 -1 0)) + (v128.const i32x4 0 -1 -1 0)) + +;; i32x4.lt_u (i32x4) (i8x16) +(assert_return (invoke "lt_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 -8323200 0 1 4294967295) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i32x4 0 0 -1 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; i32x4.lt_u (i32x4) (i16x8) +(assert_return (invoke "lt_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 -128 0 1 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i32x4 0 0 -1 -1)) +(assert_return (invoke "lt_u" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0_123_456_789 0_123_456_789 0_123_456_789 0_123_456_789) + (v128.const i32x4 123456789 123456789 123456789 123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef) + (v128.const i32x4 -0x6f543210 -0x6f543210 -0x6f543210 -0x6f543210)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; le_s + +;; i32x4.le_s (i32x4) (i32x4) + +;; hex vs hex +(assert_return (invoke "le_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000) + (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B) + (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; hex vs dec +(assert_return (invoke "le_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 2155905152 2155905152 2155905152 2155905152)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 -2139062144 -2139062144 -2139062144 -2139062144)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0x83828180 0x00FFFEFD 0x7F020100 0xFFFEFD80) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; dec vs dec +(assert_return (invoke "le_s" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 4294967295 4294967295 0 0) + (v128.const i32x4 4294967295 4294967295 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0 0 4294967295 4294967295) + (v128.const i32x4 0 0 4294967295 4294967295)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 -2147483647 4294967295 0 -1) + (v128.const i32x4 2147483649 -1 0 -1)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "le_s" (v128.const i32x4 0xc3000000 0xc2fe0000 0xbf800000 0x00000000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0x3f800000 0x42fe0000 0x43000000 0x437f0000) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; not equal +(assert_return (invoke "le_s" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le_s" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 0 0 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0x02030001 0x10110409 0x0B1A120A 0xABFF1BAA) + (v128.const i32x4 0xAA1BFFAB 0x0A121A0B 0x09041110 0x01000302)) + (v128.const i32x4 0 0 0 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0x80018000 0x80038002 0x80058004 0x80078006) + (v128.const i32x4 2147975174 2147844100 2147713026 2147581952)) + (v128.const i32x4 -1 -1 0 0)) +(assert_return (invoke "le_s" (v128.const i32x4 2147483648 2147483647 0 -1) + (v128.const i32x4 -2147483648 -2147483647 -1 0)) + (v128.const i32x4 -1 0 0 -1)) + +;; i32x4.le_s (i32x4)(i8x16) +(assert_return (invoke "le_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 -8323200 0 1 4294967295) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i32x4 0 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i32x4 0 0 0 0)) + +;; i32x4.le_s (i32x4) (i16x8) +(assert_return (invoke "le_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 -128 0 1 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i32x4 0 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0_123_456_789 0_123_456_789 0_123_456_789 0_123_456_789) + (v128.const i32x4 123456789 123456789 123456789 123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678) + (v128.const i32x4 0x12345678 0x12345678 0x12345678 0x12345678)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; le_u + +;; i32x4.le_u (i32x4) (i32x4) + +;; hex vs hex +(assert_return (invoke "le_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000) + (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B) + (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; hex vs dec +(assert_return (invoke "le_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 2155905152 2155905152 2155905152 2155905152)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 -2139062144 -2139062144 -2139062144 -2139062144)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0x83828180 0x00FFFEFD 0x7F020100 0xFFFEFD80) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; dec vs dec +(assert_return (invoke "le_u" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 4294967295 4294967295 0 0) + (v128.const i32x4 4294967295 4294967295 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0 0 4294967295 4294967295) + (v128.const i32x4 0 0 4294967295 4294967295)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 -2147483647 4294967295 0 -1) + (v128.const i32x4 2147483649 -1 0 -1)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "le_u" (v128.const i32x4 0xc3000000 0xc2fe0000 0xbf800000 0x00000000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0x3f800000 0x42fe0000 0x43000000 0x437f0000) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; not equal +(assert_return (invoke "le_u" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 -1 -1 0 0)) +(assert_return (invoke "le_u" (v128.const i32x4 0x02030001 0x10110409 0x0B1A120A 0xABFF1BAA) + (v128.const i32x4 0xAA1BFFAB 0x0A121A0B 0x09041110 0x01000302)) + (v128.const i32x4 -1 0 0 0)) +(assert_return (invoke "le_u" (v128.const i32x4 0x80018000 0x80038002 0x80058004 0x80078006) + (v128.const i32x4 2147975174 2147844100 2147713026 2147581952)) + (v128.const i32x4 -1 -1 0 0)) +(assert_return (invoke "le_u" (v128.const i32x4 2147483648 2147483647 0 -1) + (v128.const i32x4 -2147483648 -2147483647 -1 0)) + (v128.const i32x4 -1 -1 -1 0)) + +;; i32x4.le_u (i32x4) (i8x16) +(assert_return (invoke "le_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 -8323200 0 1 4294967295) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i32x4 0 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; i32x4.le_u (i32x4) (i16x8) +(assert_return (invoke "le_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 -128 0 1 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i32x4 0 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "le_u" (v128.const i32x4 0_123_456_789 0_123_456_789 0_123_456_789 0_123_456_789) + (v128.const i32x4 123456789 123456789 123456789 123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef) + (v128.const i32x4 0x90ABcdef 0x90ABcdef 0x90ABcdef 0x90ABcdef)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; gt_s + +;; i32x4.gt_s (i32x4) (i32x4) + +;; hex vs hex +(assert_return (invoke "gt_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000) + (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B) + (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B)) + (v128.const i32x4 0 0 0 0)) + +;; hex vs dec +(assert_return (invoke "gt_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 2155905152 2155905152 2155905152 2155905152)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 -2139062144 -2139062144 -2139062144 -2139062144)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0x83828180 0x00FFFEFD 0x7F020100 0xFFFEFD80) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i32x4 0 0 0 0)) + +;; dec vs dec +(assert_return (invoke "gt_s" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 4294967295 4294967295 0 0) + (v128.const i32x4 4294967295 4294967295 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0 0 4294967295 4294967295) + (v128.const i32x4 0 0 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 -2147483647 4294967295 0 -1) + (v128.const i32x4 2147483649 -1 0 -1)) + (v128.const i32x4 0 0 0 0)) + +;; hex vs float +(assert_return (invoke "gt_s" (v128.const i32x4 0xc3000000 0xc2fe0000 0xbf800000 0x00000000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0x3f800000 0x42fe0000 0x43000000 0x437f0000) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i32x4 0 0 0 0)) + +;; not equal +(assert_return (invoke "gt_s" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt_s" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 -1 -1 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0x02030001 0x10110409 0x0B1A120A 0xABFF1BAA) + (v128.const i32x4 0xAA1BFFAB 0x0A121A0B 0x09041110 0x01000302)) + (v128.const i32x4 -1 -1 -1 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0x80018000 0x80038002 0x80058004 0x80078006) + (v128.const i32x4 2147975174 2147844100 2147713026 2147581952)) + (v128.const i32x4 0 0 -1 -1)) +(assert_return (invoke "gt_s" (v128.const i32x4 2147483648 2147483647 0 -1) + (v128.const i32x4 -2147483648 -2147483647 -1 0)) + (v128.const i32x4 0 -1 -1 0)) + +;; i32x4.gt_s (i32x4) (i8x16) +(assert_return (invoke "gt_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 -8323200 0 1 4294967295) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i32x4 -1 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; i32x4.gt_s (i32x4) (i16x8) +(assert_return (invoke "gt_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 65535 0 1 32768) + (v128.const i16x8 65535 65535 0 0 1 1 32768 32768)) + (v128.const i32x4 -1 0 0 -1)) +(assert_return (invoke "gt_s" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0_123_456_789 0_123_456_789 0_123_456_789 0_123_456_789) + (v128.const i32x4 123456789 123456789 123456789 123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i32x4 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef 0x0_90AB_cdef) + (v128.const i32x4 -0x6f543211 -0x6f543211 -0x6f543211 -0x6f543211)) + (v128.const i32x4 0 0 0 0)) + +;; gt_u + +;; i32x4.gt_u (i32x4) (i32x4) + +;; hex vs hex +(assert_return (invoke "gt_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000) + (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B) + (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B)) + (v128.const i32x4 0 0 0 0)) + +;; hex vs dec +(assert_return (invoke "gt_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 2155905152 2155905152 2155905152 2155905152)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 -2139062144 -2139062144 -2139062144 -2139062144)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0x83828180 0x00FFFEFD 0x7F020100 0xFFFEFD80) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i32x4 0 0 0 0)) + +;; dec vs dec +(assert_return (invoke "gt_u" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 4294967295 4294967295 0 0) + (v128.const i32x4 4294967295 4294967295 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0 0 4294967295 4294967295) + (v128.const i32x4 0 0 4294967295 4294967295)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 -2147483647 4294967295 0 -1) + (v128.const i32x4 2147483649 -1 0 -1)) + (v128.const i32x4 0 0 0 0)) + +;; hex vs float +(assert_return (invoke "gt_u" (v128.const i32x4 0xc3000000 0xc2fe0000 0xbf800000 0x00000000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0x3f800000 0x42fe0000 0x43000000 0x437f0000) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i32x4 0 0 0 0)) + +;; not equal +(assert_return (invoke "gt_u" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 0 0 -1 -1)) +(assert_return (invoke "gt_u" (v128.const i32x4 0x02030001 0x10110409 0x0B1A120A 0xABFF1BAA) + (v128.const i32x4 0xAA1BFFAB 0x0A121A0B 0x09041110 0x01000302)) + (v128.const i32x4 0 -1 -1 -1)) +(assert_return (invoke "gt_u" (v128.const i32x4 0x80018000 0x80038002 0x80058004 0x80078006) + (v128.const i32x4 2147975174 2147844100 2147713026 2147581952)) + (v128.const i32x4 0 0 -1 -1)) +(assert_return (invoke "gt_u" (v128.const i32x4 2147483648 2147483647 0 -1) + (v128.const i32x4 -2147483648 -2147483647 -1 0)) + (v128.const i32x4 0 0 0 -1)) + +;; i32x4.gt_u (i32x4) (i8x16) +(assert_return (invoke "gt_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 -8323200 0 1 4294967295) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i32x4 -1 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i8x16 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA)) + (v128.const i32x4 0 0 0 0)) + +;; i32x4.gt_u (i32x4) (i16x8) +(assert_return (invoke "gt_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 -128 0 1 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i32x4 -1 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "gt_u" (v128.const i32x4 0_123_456_789 0_123_456_789 0_123_456_789 0_123_456_789) + (v128.const i32x4 123456789 123456789 123456789 123456789)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678) + (v128.const i32x4 0x12345678 0x12345678 0x12345678 0x12345678)) + (v128.const i32x4 0 0 0 0)) + +;; ge_s + +;; i32x4.ge_s (i32x4) (i32x4) + +;; hex vs hex +(assert_return (invoke "ge_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000) + (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B) + (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; hex vs dec +(assert_return (invoke "ge_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 2155905152 2155905152 2155905152 2155905152)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 -2139062144 -2139062144 -2139062144 -2139062144)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0x83828180 0x00FFFEFD 0x7F020100 0xFFFEFD80) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; dec vs dec +(assert_return (invoke "ge_s" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 4294967295 4294967295 0 0) + (v128.const i32x4 4294967295 4294967295 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0 0 4294967295 4294967295) + (v128.const i32x4 0 0 4294967295 4294967295)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 -2147483647 4294967295 0 -1) + (v128.const i32x4 2147483649 -1 0 -1)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "ge_s" (v128.const i32x4 0xc3000000 0xc2fe0000 0xbf800000 0x00000000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0x3f800000 0x42fe0000 0x43000000 0x437f0000) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; not equal +(assert_return (invoke "ge_s" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 -1 -1 0 0)) +(assert_return (invoke "ge_s" (v128.const i32x4 0x02030001 0x10110409 0x0B1A120A 0xABFF1BAA) + (v128.const i32x4 0xAA1BFFAB 0x0A121A0B 0x09041110 0x01000302)) + (v128.const i32x4 -1 -1 -1 0)) +(assert_return (invoke "ge_s" (v128.const i32x4 0x80018000 0x80038002 0x80058004 0x80078006) + (v128.const i32x4 2147975174 2147844100 2147713026 2147581952)) + (v128.const i32x4 0 0 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 2147483648 2147483647 0 -1) + (v128.const i32x4 -2147483648 -2147483647 -1 0)) + (v128.const i32x4 -1 -1 -1 0)) + +;; i32x4.ge_s (i32x4) (i8x16) +(assert_return (invoke "ge_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 -8323200 0 1 4294967295) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i32x4 -1 -1 0 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0x55555555 0x55555555 0x55555555 0x55555555) + (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; i32x4.ge_s (i32x4) (i16x8) +(assert_return (invoke "ge_s" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 65535 0 1 32768) + (v128.const i16x8 65535 65535 0 0 1 1 32768 32768)) + (v128.const i32x4 -1 -1 0 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge_s" (v128.const i32x4 0_123_456_789 0_123_456_789 0_123_456_789 0_123_456_789) + (v128.const i32x4 123456789 123456789 123456789 123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678) + (v128.const i32x4 0x12345678 0x12345678 0x12345678 0x12345678)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; ge_u + +;; i32x4.ge_u (i32x4) (i32x4) + +;; hex vs hex +(assert_return (invoke "ge_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000) + (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B) + (v128.const i32x4 0x03020100 0x11100904 0x1A0B0A12 0xFFABAA1B)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; hex vs dec +(assert_return (invoke "ge_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 2155905152 2155905152 2155905152 2155905152)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080) + (v128.const i32x4 -2139062144 -2139062144 -2139062144 -2139062144)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0x83828180 0x00FFFEFD 0x7F020100 0xFFFEFD80) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; dec vs dec +(assert_return (invoke "ge_u" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 4294967295 4294967295 0 0) + (v128.const i32x4 4294967295 4294967295 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0 0 4294967295 4294967295) + (v128.const i32x4 0 0 4294967295 4294967295)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 -2147483647 4294967295 0 -1) + (v128.const i32x4 2147483649 -1 0 -1)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "ge_u" (v128.const i32x4 0xc3000000 0xc2fe0000 0xbf800000 0x00000000) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0x3f800000 0x42fe0000 0x43000000 0x437f0000) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; not equal +(assert_return (invoke "ge_u" (v128.const i32x4 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F 0x0F0F0F0F) + (v128.const i32x4 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0 0xF0F0F0F0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "ge_u" (v128.const i32x4 0x00000000 0x00000000 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0x00000000 0x00000000)) + (v128.const i32x4 0 0 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0x02030001 0x10110409 0x0B1A120A 0xABFF1BAA) + (v128.const i32x4 0xAA1BFFAB 0x0A121A0B 0x09041110 0x01000302)) + (v128.const i32x4 0 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0x80018000 0x80038002 0x80058004 0x80078006) + (v128.const i32x4 2147975174 2147844100 2147713026 2147581952)) + (v128.const i32x4 0 0 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 2147483648 2147483647 0 -1) + (v128.const i32x4 -2147483648 -2147483647 -1 0)) + (v128.const i32x4 -1 0 0 -1)) + +;; i32x4.ge_u (i32x4) (i8x16) +(assert_return (invoke "ge_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 -8323200 0 1 4294967295) + (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255)) + (v128.const i32x4 -1 -1 0 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55)) + (v128.const i32x4 -1 -1 -1 -1)) + +;; i32x4.ge_u (i32x4) (i16x8) +(assert_return (invoke "ge_u" (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 2206368128 16776957 2130837760 4294901120) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 -128 0 1 255) + (v128.const i16x8 65535 65535 0 0 1 1 32768 32768)) + (v128.const i32x4 0 -1 0 0)) +(assert_return (invoke "ge_u" (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA) + (v128.const i16x8 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555 0x5555)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0_123_456_789 0_123_456_789 0_123_456_789 0_123_456_789) + (v128.const i32x4 123456789 123456789 123456789 123456789)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678) + (v128.const i32x4 0x12345678 0x12345678 0x12345678 0x12345678)) + (v128.const i32x4 -1 -1 -1 -1)) + + +;; Type check + +(assert_invalid (module (func (result v128) (i32x4.eq (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.ge_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.ge_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.gt_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.gt_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.le_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.le_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.lt_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.lt_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.ne (i32.const 0) (f32.const 0)))) "type mismatch") + + +;; combination + +(module (memory 1) + (func (export "eq-in-block") + (block + (drop + (block (result v128) + (i32x4.eq + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "ne-in-block") + (block + (drop + (block (result v128) + (i32x4.ne + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "lt_s-in-block") + (block + (drop + (block (result v128) + (i32x4.lt_s + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "le_u-in-block") + (block + (drop + (block (result v128) + (i32x4.le_u + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "gt_u-in-block") + (block + (drop + (block (result v128) + (i32x4.gt_u + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "ge_s-in-block") + (block + (drop + (block (result v128) + (i32x4.ge_s + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "nested-eq") + (drop + (i32x4.eq + (i32x4.eq + (i32x4.eq + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i32x4.eq + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i32x4.eq + (i32x4.eq + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i32x4.eq + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-ne") + (drop + (i32x4.ne + (i32x4.ne + (i32x4.ne + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i32x4.ne + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i32x4.ne + (i32x4.ne + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i32x4.ne + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-lt_s") + (drop + (i32x4.lt_s + (i32x4.lt_s + (i32x4.lt_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i32x4.lt_s + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i32x4.lt_s + (i32x4.lt_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i32x4.lt_s + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-le_u") + (drop + (i32x4.le_u + (i32x4.le_u + (i32x4.le_u + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i32x4.le_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i32x4.le_u + (i32x4.le_u + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i32x4.le_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-gt_u") + (drop + (i32x4.gt_u + (i32x4.gt_u + (i32x4.gt_u + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i32x4.gt_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i32x4.gt_u + (i32x4.gt_u + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i32x4.gt_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-ge_s") + (drop + (i32x4.ge_s + (i32x4.ge_s + (i32x4.ge_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i32x4.ge_s + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i32x4.ge_s + (i32x4.ge_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i32x4.ge_s + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "as-param") + (drop + (i32x4.ge_u + (i32x4.eq + (i32x4.lt_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i32x4.le_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i32x4.ne + (i32x4.gt_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i32x4.lt_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) +) +(assert_return (invoke "eq-in-block")) +(assert_return (invoke "ne-in-block")) +(assert_return (invoke "lt_s-in-block")) +(assert_return (invoke "le_u-in-block")) +(assert_return (invoke "gt_u-in-block")) +(assert_return (invoke "ge_s-in-block")) +(assert_return (invoke "nested-eq")) +(assert_return (invoke "nested-ne")) +(assert_return (invoke "nested-lt_s")) +(assert_return (invoke "nested-le_u")) +(assert_return (invoke "nested-gt_u")) +(assert_return (invoke "nested-ge_s")) +(assert_return (invoke "as-param")) + + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i32x4.eq-1st-arg-empty (result v128) + (i32x4.eq (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.eq-arg-empty (result v128) + (i32x4.eq) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.ne-1st-arg-empty (result v128) + (i32x4.ne (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.ne-arg-empty (result v128) + (i32x4.ne) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.lt_s-1st-arg-empty (result v128) + (i32x4.lt_s (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.lt_s-arg-empty (result v128) + (i32x4.lt_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.lt_u-1st-arg-empty (result v128) + (i32x4.lt_u (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.lt_u-arg-empty (result v128) + (i32x4.lt_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.le_s-1st-arg-empty (result v128) + (i32x4.le_s (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.le_s-arg-empty (result v128) + (i32x4.le_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.le_u-1st-arg-empty (result v128) + (i32x4.le_u (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.le_u-arg-empty (result v128) + (i32x4.le_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.gt_s-1st-arg-empty (result v128) + (i32x4.gt_s (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.gt_s-arg-empty (result v128) + (i32x4.gt_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.gt_u-1st-arg-empty (result v128) + (i32x4.gt_u (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.gt_u-arg-empty (result v128) + (i32x4.gt_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.ge_s-1st-arg-empty (result v128) + (i32x4.ge_s (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.ge_s-arg-empty (result v128) + (i32x4.ge_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.ge_u-1st-arg-empty (result v128) + (i32x4.ge_u (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.ge_u-arg-empty (result v128) + (i32x4.ge_u) + ) + ) + "type mismatch" +) +;; Unknown operators + +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (i4x32.eq (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (i4x32.ne (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (i4x32.lt_s (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (i4x32.lt_u (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (i4x32.le_s (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (i4x32.le_u (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (i4x32.gt_s (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (i4x32.gt_u (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (i4x32.ge_s (local.get $x) (local.get $y)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (param $x v128) (param $y v128) (result v128) (i4x32.ge_u (local.get $x) (local.get $y)))") "unknown operator") + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_dot_i16x8.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_dot_i16x8.wast new file mode 100644 index 000000000..b41de74d0 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_dot_i16x8.wast @@ -0,0 +1,110 @@ +;; Tests for i32x4 arithmetic operations on major boundary values and all special values. + + +(module + (func (export "i32x4.dot_i16x8_s") (param v128 v128) (result v128) (i32x4.dot_i16x8_s (local.get 0) (local.get 1))) +) + + +;; i32x4.dot_i16x8_s +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 -2 -2 -2 -2)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 16383 16383 16383 16383 16383 16383 16383 16383) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i32x4 536838144 536838144 536838144 536838144)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i32x4 536870912 536870912 536870912 536870912)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 -16383 -16383 -16383 -16383 -16383 -16383 -16383 -16383) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i32x4 536838144 536838144 536838144 536838144)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i32x4 536870912 536870912 536870912 536870912)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 -16385 -16385 -16385 -16385 -16385 -16385 -16385 -16385) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i32x4 536903680 536903680 536903680 536903680)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 65530 65530 65530 65530)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 65532 65532 65532 65532)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 -65536 -65536 -65536 -65536)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 65532 65532 65532 65532)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 65534 65534 65534 65534)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 65536 65536 65536 65536)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 2147352578 2147352578 2147352578 2147352578)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 2147483648 2147483648 2147483648 2147483648)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i32x4 2147418112 2147418112 2147418112 2147418112)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 -2 -2 -2 -2)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 -65534 -65534 -65534 -65534)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 65536 65536 65536 65536)) +(assert_return (invoke "i32x4.dot_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 2 2 2 2)) + +;; type check +(assert_invalid (module (func (result v128) (i32x4.dot_i16x8_s (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i32x4.dot_i16x8_s-1st-arg-empty (result v128) + (i32x4.dot_i16x8_s (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.dot_i16x8_s-arg-empty (result v128) + (i32x4.dot_i16x8_s) + ) + ) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_extadd_pairwise_i16x8.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_extadd_pairwise_i16x8.wast new file mode 100644 index 000000000..2d1682d40 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_extadd_pairwise_i16x8.wast @@ -0,0 +1,68 @@ +;; Tests for i32x4 arithmetic operations on major boundary values and all special values. + + +(module + (func (export "i32x4.extadd_pairwise_i16x8_s") (param v128) (result v128) (i32x4.extadd_pairwise_i16x8_s (local.get 0))) + (func (export "i32x4.extadd_pairwise_i16x8_u") (param v128) (result v128) (i32x4.extadd_pairwise_i16x8_u (local.get 0))) +) + + +;; i32x4.extadd_pairwise_i16x8_s +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_s" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_s" (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_s" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 -2 -2 -2 -2)) +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_s" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766)) + (v128.const i32x4 65532 65532 65532 65532)) +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_s" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i32x4 -65534 -65534 -65534 -65534)) +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 -65536 -65536 -65536 -65536)) +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_s" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 65534 65534 65534 65534)) +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 -2 -2 -2 -2)) + +;; i32x4.extadd_pairwise_i16x8_u +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_u" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_u" (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_u" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 131070 131070 131070 131070)) +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_u" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766)) + (v128.const i32x4 65532 65532 65532 65532)) +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_u" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i32x4 65538 65538 65538 65538)) +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 65536 65536 65536 65536)) +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_u" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 65534 65534 65534 65534)) +(assert_return (invoke "i32x4.extadd_pairwise_i16x8_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 131070 131070 131070 131070)) + +;; type check +(assert_invalid (module (func (result v128) (i32x4.extadd_pairwise_i16x8_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.extadd_pairwise_i16x8_u (i32.const 0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i32x4.extadd_pairwise_i16x8_s-arg-empty (result v128) + (i32x4.extadd_pairwise_i16x8_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.extadd_pairwise_i16x8_u-arg-empty (result v128) + (i32x4.extadd_pairwise_i16x8_u) + ) + ) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_extmul_i16x8.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_extmul_i16x8.wast new file mode 100644 index 000000000..f04db6770 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_extmul_i16x8.wast @@ -0,0 +1,404 @@ +;; Tests for i32x4 arithmetic operations on major boundary values and all special values. + + +(module + (func (export "i32x4.extmul_low_i16x8_s") (param v128 v128) (result v128) (i32x4.extmul_low_i16x8_s (local.get 0) (local.get 1))) + (func (export "i32x4.extmul_high_i16x8_s") (param v128 v128) (result v128) (i32x4.extmul_high_i16x8_s (local.get 0) (local.get 1))) + (func (export "i32x4.extmul_low_i16x8_u") (param v128 v128) (result v128) (i32x4.extmul_low_i16x8_u (local.get 0) (local.get 1))) + (func (export "i32x4.extmul_high_i16x8_u") (param v128 v128) (result v128) (i32x4.extmul_high_i16x8_u (local.get 0) (local.get 1))) +) + + +;; i32x4.extmul_low_i16x8_s +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 16383 16383 16383 16383 16383 16383 16383 16383) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i32x4 268419072 268419072 268419072 268419072)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i32x4 268435456 268435456 268435456 268435456)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 -16383 -16383 -16383 -16383 -16383 -16383 -16383 -16383) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i32x4 268419072 268419072 268419072 268419072)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i32x4 268435456 268435456 268435456 268435456)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 -16385 -16385 -16385 -16385 -16385 -16385 -16385 -16385) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i32x4 268451840 268451840 268451840 268451840)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 32765 32765 32765 32765)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 32766 32766 32766 32766)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 32766 32766 32766 32766)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 32768 32768 32768 32768)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 1073676289 1073676289 1073676289 1073676289)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i32x4 1073709056 1073709056 1073709056 1073709056)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 32768 32768 32768 32768)) +(assert_return (invoke "i32x4.extmul_low_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 1 1 1 1)) + +;; i32x4.extmul_high_i16x8_s +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 16383 16383 16383 16383 16383 16383 16383 16383) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i32x4 268419072 268419072 268419072 268419072)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i32x4 268435456 268435456 268435456 268435456)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 -16383 -16383 -16383 -16383 -16383 -16383 -16383 -16383) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i32x4 268419072 268419072 268419072 268419072)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i32x4 268435456 268435456 268435456 268435456)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 -16385 -16385 -16385 -16385 -16385 -16385 -16385 -16385) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i32x4 268451840 268451840 268451840 268451840)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 32765 32765 32765 32765)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 32766 32766 32766 32766)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 32766 32766 32766 32766)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 32768 32768 32768 32768)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 1073676289 1073676289 1073676289 1073676289)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i32x4 1073709056 1073709056 1073709056 1073709056)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 32768 32768 32768 32768)) +(assert_return (invoke "i32x4.extmul_high_i16x8_s" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 1 1 1 1)) + +;; i32x4.extmul_low_i16x8_u +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 65535 65535 65535 65535)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 -131071 -131071 -131071 -131071)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 16383 16383 16383 16383 16383 16383 16383 16383) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i32x4 268419072 268419072 268419072 268419072)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i32x4 268435456 268435456 268435456 268435456)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 -16383 -16383 -16383 -16383 -16383 -16383 -16383 -16383) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i32x4 -1878999040 -1878999040 -1878999040 -1878999040)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i32x4 -1879048192 -1879048192 -1879048192 -1879048192)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 -16385 -16385 -16385 -16385 -16385 -16385 -16385 -16385) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i32x4 -1879097344 -1879097344 -1879097344 -1879097344)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 32765 32765 32765 32765)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 32766 32766 32766 32766)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 32768 32768 32768 32768)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 -2147385346 -2147385346 -2147385346 -2147385346)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 -2147450881 -2147450881 -2147450881 -2147450881)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 2147450880 2147450880 2147450880 2147450880)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 1073676289 1073676289 1073676289 1073676289)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i32x4 1073774592 1073774592 1073774592 1073774592)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 65535 65535 65535 65535)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 -131071 -131071 -131071 -131071)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 2147385345 2147385345 2147385345 2147385345)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 2147450880 2147450880 2147450880 2147450880)) +(assert_return (invoke "i32x4.extmul_low_i16x8_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 -131071 -131071 -131071 -131071)) + +;; i32x4.extmul_high_i16x8_u +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 0 0 0 0 0 0 0 0) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 1 1 1 1 1 1 1 1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 65535 65535 65535 65535)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 -131071 -131071 -131071 -131071)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 16383 16383 16383 16383 16383 16383 16383 16383) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i32x4 268419072 268419072 268419072 268419072)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384) + (v128.const i16x8 16384 16384 16384 16384 16384 16384 16384 16384)) + (v128.const i32x4 268435456 268435456 268435456 268435456)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 -16383 -16383 -16383 -16383 -16383 -16383 -16383 -16383) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i32x4 -1878999040 -1878999040 -1878999040 -1878999040)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i32x4 -1879048192 -1879048192 -1879048192 -1879048192)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 -16385 -16385 -16385 -16385 -16385 -16385 -16385 -16385) + (v128.const i16x8 -16384 -16384 -16384 -16384 -16384 -16384 -16384 -16384)) + (v128.const i32x4 -1879097344 -1879097344 -1879097344 -1879097344)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 32765 32765 32765 32765 32765 32765 32765 32765) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 32765 32765 32765 32765)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 32766 32766 32766 32766 32766 32766 32766 32766) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 32766 32766 32766 32766)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 32768 32768 32768 32768 32768 32768 32768 32768) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 32768 32768 32768 32768)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 -32766 -32766 -32766 -32766 -32766 -32766 -32766 -32766) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 -2147385346 -2147385346 -2147385346 -2147385346)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 -2147450881 -2147450881 -2147450881 -2147450881)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 2147450880 2147450880 2147450880 2147450880)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 1073676289 1073676289 1073676289 1073676289)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768) + (v128.const i16x8 -32767 -32767 -32767 -32767 -32767 -32767 -32767 -32767)) + (v128.const i32x4 1073774592 1073774592 1073774592 1073774592)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 1 1 1 1 1 1 1 1)) + (v128.const i32x4 65535 65535 65535 65535)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i32x4 -131071 -131071 -131071 -131071)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 2147385345 2147385345 2147385345 2147385345)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 2147450880 2147450880 2147450880 2147450880)) +(assert_return (invoke "i32x4.extmul_high_i16x8_u" (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i32x4 -131071 -131071 -131071 -131071)) + +;; type check +(assert_invalid (module (func (result v128) (i32x4.extmul_low_i16x8_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.extmul_high_i16x8_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.extmul_low_i16x8_u (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.extmul_high_i16x8_u (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i32x4.extmul_low_i16x8_s-1st-arg-empty (result v128) + (i32x4.extmul_low_i16x8_s (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.extmul_low_i16x8_s-arg-empty (result v128) + (i32x4.extmul_low_i16x8_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.extmul_high_i16x8_s-1st-arg-empty (result v128) + (i32x4.extmul_high_i16x8_s (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.extmul_high_i16x8_s-arg-empty (result v128) + (i32x4.extmul_high_i16x8_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.extmul_low_i16x8_u-1st-arg-empty (result v128) + (i32x4.extmul_low_i16x8_u (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.extmul_low_i16x8_u-arg-empty (result v128) + (i32x4.extmul_low_i16x8_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.extmul_high_i16x8_u-1st-arg-empty (result v128) + (i32x4.extmul_high_i16x8_u (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.extmul_high_i16x8_u-arg-empty (result v128) + (i32x4.extmul_high_i16x8_u) + ) + ) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_trunc_sat_f32x4.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_trunc_sat_f32x4.wast new file mode 100644 index 000000000..40af590f5 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_trunc_sat_f32x4.wast @@ -0,0 +1,239 @@ +;; Tests for i32x4 trunc sat conversions from float. + +(module + (func (export "i32x4.trunc_sat_f32x4_s") (param v128) (result v128) (i32x4.trunc_sat_f32x4_s (local.get 0))) + (func (export "i32x4.trunc_sat_f32x4_u") (param v128) (result v128) (i32x4.trunc_sat_f32x4_u (local.get 0))) +) + + +;; i32x4.trunc_sat_f32x4_s +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 0.0 0.0 0.0 0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 1.5 1.5 1.5 1.5)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -1.5 -1.5 -1.5 -1.5)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 1.9 1.9 1.9 1.9)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 2.0 2.0 2.0 2.0)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -1.9 -1.9 -1.9 -1.9)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -2.0 -2.0 -2.0 -2.0)) + (v128.const i32x4 -2 -2 -2 -2)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 2147483520.0 2147483520.0 2147483520.0 2147483520.0)) + (v128.const i32x4 2147483520 2147483520 2147483520 2147483520)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -2147483520.0 -2147483520.0 -2147483520.0 -2147483520.0)) + (v128.const i32x4 -2147483520 -2147483520 -2147483520 -2147483520)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 2147483648.0 2147483648.0 2147483648.0 2147483648.0)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -2147483648.0 -2147483648.0 -2147483648.0 -2147483648.0)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 4294967294.0 4294967294.0 4294967294.0 4294967294.0)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -4294967294.0 -4294967294.0 -4294967294.0 -4294967294.0)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 2147483647.0 2147483647.0 2147483647.0 2147483647.0)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -2147483647.0 -2147483647.0 -2147483647.0 -2147483647.0)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 4294967294.0 4294967294.0 4294967294.0 4294967294.0)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 4294967295.0 4294967295.0 4294967295.0 4294967295.0)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 4294967296.0 4294967296.0 4294967296.0 4294967296.0)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 0x1.19999ap+0 0x1.19999ap+0 0x1.19999ap+0 0x1.19999ap+0)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -0x1.19999ap+0 -0x1.19999ap+0 -0x1.19999ap+0 -0x1.19999ap+0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 6 6 6 6)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -6 -6 -6 -6)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 0x1.ccccccp-1 0x1.ccccccp-1 0x1.ccccccp-1 0x1.ccccccp-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -0x1.ccccccp-1 -0x1.ccccccp-1 -0x1.ccccccp-1 -0x1.ccccccp-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 0x1.fffffep-1 0x1.fffffep-1 0x1.fffffep-1 0x1.fffffep-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -0x1.fffffep-1 -0x1.fffffep-1 -0x1.fffffep-1 -0x1.fffffep-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 6 6 6 6)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -6 -6 -6 -6)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 +nan +nan +nan +nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 nan:0x444444 nan:0x444444 nan:0x444444 nan:0x444444)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -nan:0x444444 -nan:0x444444 -nan:0x444444 -nan:0x444444)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 42 42 42 42)) + (v128.const i32x4 42 42 42 42)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 -42 -42 -42 -42)) + (v128.const i32x4 -42 -42 -42 -42)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 0123456792.0 0123456792.0 0123456792.0 0123456792.0)) + (v128.const i32x4 123456792 123456792 123456792 123456792)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_s" (v128.const f32x4 01234567890.0 01234567890.0 01234567890.0 01234567890.0)) + (v128.const i32x4 1234567936 1234567936 1234567936 1234567936)) + +;; i32x4.trunc_sat_f32x4_u +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 0.0 0.0 0.0 0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 1.5 1.5 1.5 1.5)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -1.5 -1.5 -1.5 -1.5)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 1.9 1.9 1.9 1.9)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 2.0 2.0 2.0 2.0)) + (v128.const i32x4 2 2 2 2)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -1.9 -1.9 -1.9 -1.9)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -2.0 -2.0 -2.0 -2.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 2147483520.0 2147483520.0 2147483520.0 2147483520.0)) + (v128.const i32x4 2147483520 2147483520 2147483520 2147483520)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -2147483520.0 -2147483520.0 -2147483520.0 -2147483520.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 2147483648.0 2147483648.0 2147483648.0 2147483648.0)) + (v128.const i32x4 2147483648 2147483648 2147483648 2147483648)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -2147483648.0 -2147483648.0 -2147483648.0 -2147483648.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 4294967294.0 4294967294.0 4294967294.0 4294967294.0)) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -4294967294.0 -4294967294.0 -4294967294.0 -4294967294.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 2147483647.0 2147483647.0 2147483647.0 2147483647.0)) + (v128.const i32x4 2147483648 2147483648 2147483648 2147483648)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -2147483647.0 -2147483647.0 -2147483647.0 -2147483647.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 4294967294.0 4294967294.0 4294967294.0 4294967294.0)) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 4294967295.0 4294967295.0 4294967295.0 4294967295.0)) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 4294967296.0 4294967296.0 4294967296.0 4294967296.0)) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 0x1p-149 0x1p-149 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -0x1p-149 -0x1p-149 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 0x1p-126 0x1p-126 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -0x1p-126 -0x1p-126 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 0x1p-1 0x1p-1 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -0x1p-1 -0x1p-1 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 0x1p+0 0x1p+0 0x1p+0 0x1p+0)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -0x1p+0 -0x1p+0 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 0x1.19999ap+0 0x1.19999ap+0 0x1.19999ap+0 0x1.19999ap+0)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -0x1.19999ap+0 -0x1.19999ap+0 -0x1.19999ap+0 -0x1.19999ap+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 6 6 6 6)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 0x1.ccccccp-1 0x1.ccccccp-1 0x1.ccccccp-1 0x1.ccccccp-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -0x1.ccccccp-1 -0x1.ccccccp-1 -0x1.ccccccp-1 -0x1.ccccccp-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 0x1.fffffep-1 0x1.fffffep-1 0x1.fffffep-1 0x1.fffffep-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -0x1.fffffep-1 -0x1.fffffep-1 -0x1.fffffep-1 -0x1.fffffep-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 6 6 6 6)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 +nan +nan +nan +nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 nan:0x444444 nan:0x444444 nan:0x444444 nan:0x444444)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -nan:0x444444 -nan:0x444444 -nan:0x444444 -nan:0x444444)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 42 42 42 42)) + (v128.const i32x4 42 42 42 42)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 -42 -42 -42 -42)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 0123456792.0 0123456792.0 0123456792.0 0123456792.0)) + (v128.const i32x4 123456792 123456792 123456792 123456792)) +(assert_return (invoke "i32x4.trunc_sat_f32x4_u" (v128.const f32x4 01234567890.0 01234567890.0 01234567890.0 01234567890.0)) + (v128.const i32x4 1234567936 1234567936 1234567936 1234567936)) + +;; type check +(assert_invalid (module (func (result v128) (i32x4.trunc_sat_f32x4_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.trunc_sat_f32x4_u (i32.const 0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i32x4.trunc_sat_f32x4_s-arg-empty (result v128) + (i32x4.trunc_sat_f32x4_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.trunc_sat_f32x4_u-arg-empty (result v128) + (i32x4.trunc_sat_f32x4_u) + ) + ) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_trunc_sat_f64x2.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_trunc_sat_f64x2.wast new file mode 100644 index 000000000..9bf507d77 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i32x4_trunc_sat_f64x2.wast @@ -0,0 +1,239 @@ +;; Tests for i32x4 trunc sat conversions from float. + +(module + (func (export "i32x4.trunc_sat_f64x2_s_zero") (param v128) (result v128) (i32x4.trunc_sat_f64x2_s_zero (local.get 0))) + (func (export "i32x4.trunc_sat_f64x2_u_zero") (param v128) (result v128) (i32x4.trunc_sat_f64x2_u_zero (local.get 0))) +) + + +;; i32x4.trunc_sat_f64x2_s_zero +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 0.0 0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -0.0 -0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 1.5 1.5)) + (v128.const i32x4 1 1 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -1.5 -1.5)) + (v128.const i32x4 -1 -1 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 1.9 1.9)) + (v128.const i32x4 1 1 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 2.0 2.0)) + (v128.const i32x4 2 2 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -1.9 -1.9)) + (v128.const i32x4 -1 -1 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -2.0 -2.0)) + (v128.const i32x4 -2 -2 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 2147483520.0 2147483520.0)) + (v128.const i32x4 2147483520 2147483520 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -2147483520.0 -2147483520.0)) + (v128.const i32x4 -2147483520 -2147483520 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 2147483648.0 2147483648.0)) + (v128.const i32x4 2147483647 2147483647 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -2147483648.0 -2147483648.0)) + (v128.const i32x4 -2147483648 -2147483648 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 4294967294.0 4294967294.0)) + (v128.const i32x4 2147483647 2147483647 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -4294967294.0 -4294967294.0)) + (v128.const i32x4 -2147483648 -2147483648 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 2147483647.0 2147483647.0)) + (v128.const i32x4 2147483647 2147483647 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -2147483647.0 -2147483647.0)) + (v128.const i32x4 -2147483647 -2147483647 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 4294967294.0 4294967294.0)) + (v128.const i32x4 2147483647 2147483647 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 4294967295.0 4294967295.0)) + (v128.const i32x4 2147483647 2147483647 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 4294967296.0 4294967296.0)) + (v128.const i32x4 2147483647 2147483647 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i32x4 1 1 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i32x4 -1 -1 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 0x1.19999ap+0 0x1.19999ap+0)) + (v128.const i32x4 1 1 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -0x1.19999ap+0 -0x1.19999ap+0)) + (v128.const i32x4 -1 -1 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 6 6 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -6 -6 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 2147483647 2147483647 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -2147483648 -2147483648 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 0x1.ccccccp-1 0x1.ccccccp-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -0x1.ccccccp-1 -0x1.ccccccp-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 0x1.fffffep-1 0x1.fffffep-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -0x1.fffffep-1 -0x1.fffffep-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 6 6 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 -6 -6 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 2147483647 2147483647 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 -2147483648 -2147483648 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 +inf +inf)) + (v128.const i32x4 2147483647 2147483647 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -inf -inf)) + (v128.const i32x4 -2147483648 -2147483648 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 +nan +nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 nan:0x444444 nan:0x444444)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -nan:0x444444 -nan:0x444444)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 42 42)) + (v128.const i32x4 42 42 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 -42 -42)) + (v128.const i32x4 -42 -42 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 0123456792.0 0123456792.0)) + (v128.const i32x4 123456792 123456792 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_s_zero" (v128.const f64x2 01234567890.0 01234567890.0)) + (v128.const i32x4 1234567890 1234567890 0 0)) + +;; i32x4.trunc_sat_f64x2_u_zero +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 0.0 0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -0.0 -0.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 1.5 1.5)) + (v128.const i32x4 1 1 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -1.5 -1.5)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 1.9 1.9)) + (v128.const i32x4 1 1 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 2.0 2.0)) + (v128.const i32x4 2 2 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -1.9 -1.9)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -2.0 -2.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 2147483520.0 2147483520.0)) + (v128.const i32x4 2147483520 2147483520 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -2147483520.0 -2147483520.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 2147483648.0 2147483648.0)) + (v128.const i32x4 2147483648 2147483648 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -2147483648.0 -2147483648.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 4294967294.0 4294967294.0)) + (v128.const i32x4 4294967294 4294967294 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -4294967294.0 -4294967294.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 2147483647.0 2147483647.0)) + (v128.const i32x4 2147483647 2147483647 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -2147483647.0 -2147483647.0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 4294967294.0 4294967294.0)) + (v128.const i32x4 4294967294 4294967294 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 4294967295.0 4294967295.0)) + (v128.const i32x4 4294967295 4294967295 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 4294967296.0 4294967296.0)) + (v128.const i32x4 4294967295 4294967295 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 0x1p-149 0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -0x1p-149 -0x1p-149)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 0x1p-126 0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -0x1p-126 -0x1p-126)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 0x1p-1 0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -0x1p-1 -0x1p-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 0x1p+0 0x1p+0)) + (v128.const i32x4 1 1 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -0x1p+0 -0x1p+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 0x1.19999ap+0 0x1.19999ap+0)) + (v128.const i32x4 1 1 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -0x1.19999ap+0 -0x1.19999ap+0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 6 6 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 4294967295 4294967295 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 0x1.ccccccp-1 0x1.ccccccp-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -0x1.ccccccp-1 -0x1.ccccccp-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 0x1.fffffep-1 0x1.fffffep-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -0x1.fffffep-1 -0x1.fffffep-1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 0x1.921fb6p+2 0x1.921fb6p+2)) + (v128.const i32x4 6 6 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -0x1.921fb6p+2 -0x1.921fb6p+2)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 0x1.fffffep+127 0x1.fffffep+127)) + (v128.const i32x4 4294967295 4294967295 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -0x1.fffffep+127 -0x1.fffffep+127)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 +inf +inf)) + (v128.const i32x4 4294967295 4294967295 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -inf -inf)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 +nan +nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -nan -nan)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 nan:0x444444 nan:0x444444)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -nan:0x444444 -nan:0x444444)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 42 42)) + (v128.const i32x4 42 42 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 -42 -42)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 0123456792.0 0123456792.0)) + (v128.const i32x4 123456792 123456792 0 0)) +(assert_return (invoke "i32x4.trunc_sat_f64x2_u_zero" (v128.const f64x2 01234567890.0 01234567890.0)) + (v128.const i32x4 1234567890 1234567890 0 0)) + +;; type check +(assert_invalid (module (func (result v128) (i32x4.trunc_sat_f64x2_s_zero (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.trunc_sat_f64x2_u_zero (i32.const 0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i32x4.trunc_sat_f64x2_s_zero-arg-empty (result v128) + (i32x4.trunc_sat_f64x2_s_zero) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.trunc_sat_f64x2_u_zero-arg-empty (result v128) + (i32x4.trunc_sat_f64x2_u_zero) + ) + ) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i64x2_arith.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i64x2_arith.wast new file mode 100644 index 000000000..00963a0d0 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i64x2_arith.wast @@ -0,0 +1,652 @@ +;; Tests for i64x2 arithmetic operations on major boundary values and all special values. + + +(module + (func (export "i64x2.add") (param v128 v128) (result v128) (i64x2.add (local.get 0) (local.get 1))) + (func (export "i64x2.sub") (param v128 v128) (result v128) (i64x2.sub (local.get 0) (local.get 1))) + (func (export "i64x2.mul") (param v128 v128) (result v128) (i64x2.mul (local.get 0) (local.get 1))) + (func (export "i64x2.neg") (param v128) (result v128) (i64x2.neg (local.get 0))) +) + + +;; i64x2.add +(assert_return (invoke "i64x2.add" (v128.const i64x2 0 0) + (v128.const i64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0 0) + (v128.const i64x2 1 1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 1 1) + (v128.const i64x2 1 1)) + (v128.const i64x2 2 2)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0 0) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 1 1) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 -1 -1) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -2 -2)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 4611686018427387903 4611686018427387903) + (v128.const i64x2 4611686018427387904 4611686018427387904)) + (v128.const i64x2 9223372036854775807 9223372036854775807)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 4611686018427387904 4611686018427387904) + (v128.const i64x2 4611686018427387904 4611686018427387904)) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 -4611686018427387903 -4611686018427387903) + (v128.const i64x2 -4611686018427387904 -4611686018427387904)) + (v128.const i64x2 -9223372036854775807 -9223372036854775807)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 -4611686018427387904 -4611686018427387904) + (v128.const i64x2 -4611686018427387904 -4611686018427387904)) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 -4611686018427387905 -4611686018427387905) + (v128.const i64x2 -4611686018427387904 -4611686018427387904)) + (v128.const i64x2 9223372036854775807 9223372036854775807)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 9223372036854775805 9223372036854775805) + (v128.const i64x2 1 1)) + (v128.const i64x2 9223372036854775806 9223372036854775806)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 9223372036854775806 9223372036854775806) + (v128.const i64x2 1 1)) + (v128.const i64x2 9223372036854775807 9223372036854775807)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 9223372036854775808 9223372036854775808) + (v128.const i64x2 1 1)) + (v128.const i64x2 -9223372036854775807 -9223372036854775807)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 -9223372036854775806 -9223372036854775806) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -9223372036854775807 -9223372036854775807)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 -9223372036854775807 -9223372036854775807) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 -9223372036854775808 -9223372036854775808) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 9223372036854775807 9223372036854775807)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 9223372036854775807 9223372036854775807) + (v128.const i64x2 9223372036854775807 9223372036854775807)) + (v128.const i64x2 -2 -2)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 -9223372036854775808 -9223372036854775808) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 -9223372036854775808 -9223372036854775808) + (v128.const i64x2 -9223372036854775807 -9223372036854775807)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -2 -2)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 9223372036854775807 9223372036854775807)) + (v128.const i64x2 9223372036854775806 9223372036854775806)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) + (v128.const i64x2 9223372036854775807 9223372036854775807)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 18446744073709551615 18446744073709551615)) + (v128.const i64x2 -2 -2)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0x3fffffffffffffff 0x3fffffffffffffff) + (v128.const i64x2 0x4000000000000000 0x4000000000000000)) + (v128.const i64x2 9223372036854775807 9223372036854775807)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0x4000000000000000 0x4000000000000000) + (v128.const i64x2 0x4000000000000000 0x4000000000000000)) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 -0x3fffffffffffffff -0x3fffffffffffffff) + (v128.const i64x2 -0x40000000fffffff -0x40000000fffffff)) + (v128.const i64x2 -4899916394847535102 -4899916394847535102)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 -0x4000000000000000 -0x4000000000000000) + (v128.const i64x2 -0x400000000000000 -0x400000000000000)) + (v128.const i64x2 -4899916394579099648 -4899916394579099648)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 -0x4000000000000000 -0x4000000000000000) + (v128.const i64x2 -0x400000000000001 -0x400000000000001)) + (v128.const i64x2 -4899916394579099649 -4899916394579099649)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff) + (v128.const i64x2 0x7ffffffffffffff 0x7ffffffffffffff)) + (v128.const i64x2 -8646911284551352322 -8646911284551352322)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff) + (v128.const i64x2 0x01 0x01)) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const i64x2 -0x01 -0x01)) + (v128.const i64x2 9223372036854775807 9223372036854775807)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff) + (v128.const i64x2 0x8000000000000000 0x8000000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const i64x2 0x8000000000000000 0x8000000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0xffffffffffffffff 0xffffffffffffffff) + (v128.const i64x2 0x01 0x01)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0xffffffffffffffff 0xffffffffffffffff) + (v128.const i64x2 0xffffffffffffffff 0xffffffffffffffff)) + (v128.const i64x2 -2 -2)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff) + (v128.const i8x16 0 0 0 0 0 0 0 0x80 0 0 0 0 0 0 0 0x80)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 1 1) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff) + (v128.const i16x8 0 0 0 0x8000 0 0 0 0x8000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 1 1) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff) + (v128.const i32x4 0 0x80000000 0 0x80000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 1 1) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const f64x2 +0.0 +0.0)) + (v128.const i64x2 0x8000000000000000 0x8000000000000000)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const f64x2 -0.0 -0.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const f64x2 1.0 1.0)) + (v128.const i64x2 0xbff0000000000000 0xbff0000000000000)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const f64x2 -1.0 -1.0)) + (v128.const i64x2 0x3ff0000000000000 0x3ff0000000000000)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 1 1) + (v128.const f64x2 +inf +inf)) + (v128.const i64x2 0x7ff0000000000001 0x7ff0000000000001)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 1 1) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0xfff0000000000001 0xfff0000000000001)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 1 1) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0x7ff8000000000001 0x7ff8000000000001)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0 1) + (v128.const i64x2 0 0xffffffffffffffff)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0 1) + (v128.const i64x2 0 2)) + (v128.const i64x2 0 3)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 01_234_567_890_123_456_789 01_234_567_890_123_456_789) + (v128.const i64x2 01_234_567_890_123_456_789 01_234_567_890_123_456_789)) + (v128.const i64x2 02_469_135_780_246_913_578 02_469_135_780_246_913_578)) +(assert_return (invoke "i64x2.add" (v128.const i64x2 0x0_1234_5678_90AB_cdef 0x0_1234_5678_90AB_cdef) + (v128.const i64x2 0x0_90AB_cdef_1234_5678 0x0_90AB_cdef_1234_5678)) + (v128.const i64x2 0x0_a2e0_2467_a2e0_2467 0x0_a2e0_2467_a2e0_2467)) + +;; i64x2.sub +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0 0) + (v128.const i64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0 0) + (v128.const i64x2 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 1 1) + (v128.const i64x2 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0 0) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 1 1) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 2 2)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 -1 -1) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 4611686018427387903 4611686018427387903) + (v128.const i64x2 4611686018427387904 4611686018427387904)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 4611686018427387904 4611686018427387904) + (v128.const i64x2 4611686018427387904 4611686018427387904)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 -4611686018427387903 -4611686018427387903) + (v128.const i64x2 -4611686018427387904 -4611686018427387904)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 -4611686018427387904 -4611686018427387904) + (v128.const i64x2 -4611686018427387904 -4611686018427387904)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 -4611686018427387905 -4611686018427387905) + (v128.const i64x2 -4611686018427387904 -4611686018427387904)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 9223372036854775805 9223372036854775805) + (v128.const i64x2 1 1)) + (v128.const i64x2 9223372036854775804 9223372036854775804)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 9223372036854775806 9223372036854775806) + (v128.const i64x2 1 1)) + (v128.const i64x2 9223372036854775805 9223372036854775805)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 9223372036854775808 9223372036854775808) + (v128.const i64x2 1 1)) + (v128.const i64x2 9223372036854775807 9223372036854775807)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 -9223372036854775806 -9223372036854775806) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -9223372036854775805 -9223372036854775805)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 -9223372036854775807 -9223372036854775807) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -9223372036854775806 -9223372036854775806)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 -9223372036854775808 -9223372036854775808) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -9223372036854775807 -9223372036854775807)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 9223372036854775807 9223372036854775807) + (v128.const i64x2 9223372036854775807 9223372036854775807)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 -9223372036854775808 -9223372036854775808) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 -9223372036854775808 -9223372036854775808) + (v128.const i64x2 -9223372036854775807 -9223372036854775807)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 1 1)) + (v128.const i64x2 -2 -2)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 9223372036854775807 9223372036854775807)) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) + (v128.const i64x2 9223372036854775807 9223372036854775807)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 18446744073709551615 18446744073709551615)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x3fffffffffffffff 0x3fffffffffffffff) + (v128.const i64x2 0x4000000000000000 0x4000000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x4000000000000000 0x4000000000000000) + (v128.const i64x2 0x4000000000000000 0x4000000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 -0x3fffffffffffffff -0x3fffffffffffffff) + (v128.const i64x2 -0x40000000fffffff -0x40000000fffffff)) + (v128.const i64x2 -4323455642007240704 -4323455642007240704)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 -0x4000000000000000 -0x4000000000000000) + (v128.const i64x2 -0x400000000000000 -0x400000000000000)) + (v128.const i64x2 -4323455642275676160 -4323455642275676160)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 -0x4000000000000000 -0x4000000000000000) + (v128.const i64x2 -0x400000000000001 -0x400000000000001)) + (v128.const i64x2 -4323455642275676159 -4323455642275676159)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff) + (v128.const i64x2 0x7ffffffffffffff 0x7ffffffffffffff)) + (v128.const i64x2 8646911284551352320 8646911284551352320)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff) + (v128.const i64x2 0x01 0x01)) + (v128.const i64x2 9223372036854775806 9223372036854775806)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const i64x2 -0x01 -0x01)) + (v128.const i64x2 -9223372036854775807 -9223372036854775807)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff) + (v128.const i64x2 0x8000000000000000 0x8000000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const i64x2 0x8000000000000000 0x8000000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0xffffffffffffffff 0xffffffffffffffff) + (v128.const i64x2 0x01 0x01)) + (v128.const i64x2 -2 -2)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0xffffffffffffffff 0xffffffffffffffff) + (v128.const i64x2 0xffffffffffffffff 0xffffffffffffffff)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff) + (v128.const i8x16 0 0 0 0 0 0 0 0x80 0 0 0 0 0 0 0 0x80)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 1 1) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i64x2 2 2)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff) + (v128.const i16x8 0 0 0 0x8000 0 0 0 0x8000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 1 1) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i64x2 2 2)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff) + (v128.const i32x4 0 0x80000000 0 0x80000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 1 1) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i64x2 2 2)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const f64x2 +0.0 +0.0)) + (v128.const i64x2 0x8000000000000000 0x8000000000000000)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const f64x2 -0.0 -0.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const f64x2 1.0 1.0)) + (v128.const i64x2 0x4010000000000000 0x4010000000000000)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const f64x2 -1.0 -1.0)) + (v128.const i64x2 0xc010000000000000 0xc010000000000000)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x1 0x1) + (v128.const f64x2 +inf +inf)) + (v128.const i64x2 0x8010000000000001 0x8010000000000001)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x1 0x1) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0x0010000000000001 0x0010000000000001)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x1 0x1) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0x8008000000000001 0x8008000000000001)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0 1) + (v128.const i64x2 0 0xffffffffffffffff)) + (v128.const i64x2 0 0x02)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0 1) + (v128.const i64x2 0 2)) + (v128.const i64x2 0 -1)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 03_214_567_890_123_456_789 03_214_567_890_123_456_789) + (v128.const i64x2 01_234_567_890_123_456_789 01_234_567_890_123_456_789)) + (v128.const i64x2 01_980_000_000_000_000_000 01_980_000_000_000_000_000)) +(assert_return (invoke "i64x2.sub" (v128.const i64x2 0x0_90AB_cdef_8765_4321 0x0_90AB_cdef_8765_4321) + (v128.const i64x2 0x0_1234_5678_90AB_cdef 0x0_1234_5678_90AB_cdef)) + (v128.const i64x2 0x0_7e77_7776_f6b9_7532 0x0_7e77_7776_f6b9_7532)) + +;; i64x2.mul +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0 0) + (v128.const i64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0 0) + (v128.const i64x2 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 1 1) + (v128.const i64x2 1 1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0 0) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 1 1) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 -1 -1) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 4611686018427387903 4611686018427387903) + (v128.const i64x2 4611686018427387904 4611686018427387904)) + (v128.const i64x2 -4611686018427387904 -4611686018427387904)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 4611686018427387904 4611686018427387904) + (v128.const i64x2 4611686018427387904 4611686018427387904)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 -4611686018427387903 -4611686018427387903) + (v128.const i64x2 -4611686018427387904 -4611686018427387904)) + (v128.const i64x2 -4611686018427387904 -4611686018427387904)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 -4611686018427387904 -4611686018427387904) + (v128.const i64x2 -4611686018427387904 -4611686018427387904)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 -4611686018427387905 -4611686018427387905) + (v128.const i64x2 -4611686018427387904 -4611686018427387904)) + (v128.const i64x2 4611686018427387904 4611686018427387904)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 9223372036854775805 9223372036854775805) + (v128.const i64x2 1 1)) + (v128.const i64x2 9223372036854775805 9223372036854775805)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 9223372036854775806 9223372036854775806) + (v128.const i64x2 1 1)) + (v128.const i64x2 9223372036854775806 9223372036854775806)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 9223372036854775808 9223372036854775808) + (v128.const i64x2 1 1)) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 -9223372036854775806 -9223372036854775806) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 9223372036854775806 9223372036854775806)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 -9223372036854775807 -9223372036854775807) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 9223372036854775807 9223372036854775807)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 -9223372036854775808 -9223372036854775808) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 9223372036854775807 9223372036854775807) + (v128.const i64x2 9223372036854775807 9223372036854775807)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 -9223372036854775808 -9223372036854775808) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 -9223372036854775808 -9223372036854775808) + (v128.const i64x2 -9223372036854775807 -9223372036854775807)) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 9223372036854775807 9223372036854775807)) + (v128.const i64x2 -9223372036854775807 -9223372036854775807)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 18446744073709551615 18446744073709551615)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x3fffffffffffffff 0x3fffffffffffffff) + (v128.const i64x2 0x4000000000000000 0x4000000000000000)) + (v128.const i64x2 -4611686018427387904 -4611686018427387904)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x4000000000000000 0x4000000000000000) + (v128.const i64x2 0x4000000000000000 0x4000000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 -0x3fffffffffffffff -0x3fffffffffffffff) + (v128.const i64x2 -0x40000000fffffff -0x40000000fffffff)) + (v128.const i64x2 -4899916394847535103 -4899916394847535103)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 -0x4000000000000000 -0x4000000000000000) + (v128.const i64x2 -0x400000000000000 -0x400000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 -0x4000000000000000 -0x4000000000000000) + (v128.const i64x2 -0x400000000000001 -0x400000000000001)) + (v128.const i64x2 4611686018427387904 4611686018427387904)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff) + (v128.const i64x2 0x7ffffffffffffff 0x7ffffffffffffff)) + (v128.const i64x2 8646911284551352321 8646911284551352321)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff) + (v128.const i64x2 0x01 0x01)) + (v128.const i64x2 9223372036854775807 9223372036854775807)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const i64x2 -0x01 -0x01)) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff) + (v128.const i64x2 0x8000000000000000 0x8000000000000000)) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const i64x2 0x8000000000000000 0x8000000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0xffffffffffffffff 0xffffffffffffffff) + (v128.const i64x2 0x01 0x01)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0xffffffffffffffff 0xffffffffffffffff) + (v128.const i64x2 0xffffffffffffffff 0xffffffffffffffff)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const i8x16 0x2 0x2 0x2 0x2 0x2 0x2 0x2 0x2 0x2 0x2 0x2 0x2 0x2 0x2 0x2 0x2)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0xffffffffffffffff 0xffffffffffffffff) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const i16x8 0 0 0 0x02 0 0 0 0x02)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0xffffffffffffffff 0xffffffffffffffff) + (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x8000000000000000 0x8000000000000000) + (v128.const i32x4 0 0x02 0 0x02)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0xffffffffffffffff 0xffffffffffffffff) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x80000000 0x80000000) + (v128.const f64x2 +0.0 +0.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x80000000 0x80000000) + (v128.const f64x2 -0.0 -0.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x80000000 0x80000000) + (v128.const f64x2 1.0 1.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x80000000 0x80000000) + (v128.const f64x2 -1.0 -1.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x1 0x1) + (v128.const f64x2 +inf +inf)) + (v128.const i64x2 0x7ff0000000000000 0x7ff0000000000000)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x1 0x1) + (v128.const f64x2 -inf -inf)) + (v128.const i64x2 0xfff0000000000000 0xfff0000000000000)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x1 0x1) + (v128.const f64x2 nan nan)) + (v128.const i64x2 0x7ff8000000000000 0x7ff8000000000000)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0 1) + (v128.const i64x2 0 0xffffffffffffffff)) + (v128.const i64x2 0 0xffffffffffffffff)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0 1) + (v128.const i64x2 0 2)) + (v128.const i64x2 0 0x02)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 01_234_567_890_123_456_789 01_234_567_890_123_456_789) + (v128.const i64x2 01_234_567_890_123_456_789 01_234_567_890_123_456_789)) + (v128.const i64x2 09_710_478_858_155_731_897 09_710_478_858_155_731_897)) +(assert_return (invoke "i64x2.mul" (v128.const i64x2 0x0_1234_5678_90AB_cdef 0x0_1234_5678_90AB_cdef) + (v128.const i64x2 0x0_90AB_cdef_8765_4321 0x0_90AB_cdef_8765_4321)) + (v128.const i64x2 0x0_602f_05e9_e556_18cf 0x0_602f_05e9_e556_18cf)) + +;; i64x2.neg +(assert_return (invoke "i64x2.neg" (v128.const i64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.neg" (v128.const i64x2 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.neg" (v128.const i64x2 -1 -1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.neg" (v128.const i64x2 9223372036854775806 9223372036854775806)) + (v128.const i64x2 -9223372036854775806 -9223372036854775806)) +(assert_return (invoke "i64x2.neg" (v128.const i64x2 -9223372036854775807 -9223372036854775807)) + (v128.const i64x2 9223372036854775807 9223372036854775807)) +(assert_return (invoke "i64x2.neg" (v128.const i64x2 -9223372036854775808 -9223372036854775808)) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.neg" (v128.const i64x2 9223372036854775807 9223372036854775807)) + (v128.const i64x2 -9223372036854775807 -9223372036854775807)) +(assert_return (invoke "i64x2.neg" (v128.const i64x2 18446744073709551615 18446744073709551615)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.neg" (v128.const i64x2 0x01 0x01)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.neg" (v128.const i64x2 -0x01 -0x01)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.neg" (v128.const i64x2 -0x8000000000000000 -0x8000000000000000)) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.neg" (v128.const i64x2 -0x7fffffffffffffff -0x7fffffffffffffff)) + (v128.const i64x2 9223372036854775807 9223372036854775807)) +(assert_return (invoke "i64x2.neg" (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff)) + (v128.const i64x2 -9223372036854775807 -9223372036854775807)) +(assert_return (invoke "i64x2.neg" (v128.const i64x2 0x8000000000000000 0x8000000000000000)) + (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.neg" (v128.const i64x2 0xffffffffffffffff 0xffffffffffffffff)) + (v128.const i64x2 1 1)) + +;; type check +(assert_invalid (module (func (result v128) (i64x2.neg (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.add (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.sub (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.mul (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i64x2.neg-arg-empty (result v128) + (i64x2.neg) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.add-1st-arg-empty (result v128) + (i64x2.add (v128.const i64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.add-arg-empty (result v128) + (i64x2.add) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.sub-1st-arg-empty (result v128) + (i64x2.sub (v128.const i64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.sub-arg-empty (result v128) + (i64x2.sub) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.mul-1st-arg-empty (result v128) + (i64x2.mul (v128.const i64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.mul-arg-empty (result v128) + (i64x2.mul) + ) + ) + "type mismatch" +) + +;; combination +(module + (func (export "add-sub") (param v128 v128 v128) (result v128) + (i64x2.add (i64x2.sub (local.get 0) (local.get 1))(local.get 2))) + (func (export "mul-add") (param v128 v128 v128) (result v128) + (i64x2.mul (i64x2.add (local.get 0) (local.get 1))(local.get 2))) + (func (export "mul-sub") (param v128 v128 v128) (result v128) + (i64x2.mul (i64x2.sub (local.get 0) (local.get 1))(local.get 2))) + (func (export "sub-add") (param v128 v128 v128) (result v128) + (i64x2.sub (i64x2.add (local.get 0) (local.get 1))(local.get 2))) + (func (export "add-neg") (param v128 v128) (result v128) + (i64x2.add (i64x2.neg (local.get 0)) (local.get 1))) + (func (export "mul-neg") (param v128 v128) (result v128) + (i64x2.mul (i64x2.neg (local.get 0)) (local.get 1))) + (func (export "sub-neg") (param v128 v128) (result v128) + (i64x2.sub (i64x2.neg (local.get 0)) (local.get 1))) +) + +(assert_return (invoke "add-sub" (v128.const i64x2 0 1) + (v128.const i64x2 0 2) + (v128.const i64x2 0 2)) + (v128.const i64x2 0 1)) +(assert_return (invoke "mul-add" (v128.const i64x2 0 1) + (v128.const i64x2 0 1) + (v128.const i64x2 2 2)) + (v128.const i64x2 0 4)) +(assert_return (invoke "mul-sub" (v128.const i64x2 0 2) + (v128.const i64x2 0 1) + (v128.const i64x2 0 1)) + (v128.const i64x2 0 1)) +(assert_return (invoke "sub-add" (v128.const i64x2 0 1) + (v128.const i64x2 0 2) + (v128.const i64x2 0 2)) + (v128.const i64x2 0 1)) +(assert_return (invoke "add-neg" (v128.const i64x2 0 1) + (v128.const i64x2 0 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "mul-neg" (v128.const i64x2 0 1) + (v128.const i64x2 2 2)) + (v128.const i64x2 0 -2)) +(assert_return (invoke "sub-neg" (v128.const i64x2 0 1) + (v128.const i64x2 0 1)) + (v128.const i64x2 0 -2)) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i64x2_arith2.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i64x2_arith2.wast new file mode 100644 index 000000000..14398d8a4 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i64x2_arith2.wast @@ -0,0 +1,78 @@ +;; Tests for i64x2 [abs] operations. + +(module + (func (export "i64x2.abs") (param v128) (result v128) (i64x2.abs (local.get 0))) + (func (export "i64x2.abs_with_const_0") (result v128) (i64x2.abs (v128.const i64x2 -9223372036854775808 9223372036854775807))) +) + +(assert_return (invoke "i64x2.abs" (v128.const i64x2 1 1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 -1 -1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 18446744073709551615 18446744073709551615)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 0xffffffffffffffff 0xffffffffffffffff)) + (v128.const i64x2 0x1 0x1)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 9223372036854775808 9223372036854775808)) + (v128.const i64x2 9223372036854775808 9223372036854775808)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 -9223372036854775808 -9223372036854775808)) + (v128.const i64x2 9223372036854775808 9223372036854775808)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 -0x8000000000000000 -0x8000000000000000)) + (v128.const i64x2 0x8000000000000000 0x8000000000000000)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 0x8000000000000000 0x8000000000000000)) + (v128.const i64x2 0x8000000000000000 0x8000000000000000)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 01_2_3 01_2_3)) + (v128.const i64x2 01_2_3 01_2_3)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 -01_2_3 -01_2_3)) + (v128.const i64x2 123 123)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 0x80 0x80)) + (v128.const i64x2 0x80 0x80)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 -0x80 -0x80)) + (v128.const i64x2 0x80 0x80)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 0x0_8_0 0x0_8_0)) + (v128.const i64x2 0x0_8_0 0x0_8_0)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 -0x0_8_0 -0x0_8_0)) + (v128.const i64x2 0x80 0x80)) + +;; Const vs const +(assert_return (invoke "i64x2.abs_with_const_0") (v128.const i64x2 9223372036854775808 9223372036854775807)) + +;; Param vs const + +;; Test different lanes go through different if-then clauses +(assert_return (invoke "i64x2.abs" (v128.const i64x2 -9223372036854775808 9223372036854775807)) + (v128.const i64x2 9223372036854775808 9223372036854775807)) + +;; Test opposite signs of zero +(assert_return (invoke "i64x2.abs" (v128.const i64x2 -0 -0)) + (v128.const i64x2 -0 -0)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 +0 0)) + (v128.const i64x2 +0 0)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 -0 -0)) + (v128.const i64x2 -0 -0)) +(assert_return (invoke "i64x2.abs" (v128.const i64x2 +0 +0)) + (v128.const i64x2 +0 +0)) + +;; Unknown operators + +;; Type check +(assert_invalid (module (func (result v128) (i64x2.abs (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i64x2.abs-arg-empty (result v128) + (i64x2.abs) + ) + ) + "type mismatch" +) + +;; Combination +(module + (func (export "i64x2.abs-i64x2.abs") (param v128) (result v128) (i64x2.abs (i64x2.abs (local.get 0)))) +) + +(assert_return (invoke "i64x2.abs-i64x2.abs" (v128.const i64x2 -1 -1)) + (v128.const i64x2 1 1)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i64x2_cmp.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i64x2_cmp.wast new file mode 100644 index 000000000..50e58b64b --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i64x2_cmp.wast @@ -0,0 +1,420 @@ + +;; Test all the i64x2 comparison operators on major boundary values and all special values. + +(module + (func (export "eq") (param $x v128) (param $y v128) (result v128) (i64x2.eq (local.get $x) (local.get $y))) + (func (export "ne") (param $x v128) (param $y v128) (result v128) (i64x2.ne (local.get $x) (local.get $y))) + (func (export "lt_s") (param $x v128) (param $y v128) (result v128) (i64x2.lt_s (local.get $x) (local.get $y))) + (func (export "le_s") (param $x v128) (param $y v128) (result v128) (i64x2.le_s (local.get $x) (local.get $y))) + (func (export "gt_s") (param $x v128) (param $y v128) (result v128) (i64x2.gt_s (local.get $x) (local.get $y))) + (func (export "ge_s") (param $x v128) (param $y v128) (result v128) (i64x2.ge_s (local.get $x) (local.get $y))) +) + + +;; eq + +;; i64x2.eq (i64x2) (i64x2) +(assert_return (invoke "eq" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "eq" (v128.const i64x2 0x0000000000000000 0x0000000000000000) + (v128.const i64x2 0x0000000000000000 0x0000000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "eq" (v128.const i64x2 0xF0F0F0F0F0F0F0F0 0xF0F0F0F0F0F0F0F0) + (v128.const i64x2 0xF0F0F0F0F0F0F0F0 0xF0F0F0F0F0F0F0F0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "eq" (v128.const i64x2 0x0F0F0F0F0F0F0F0F 0x0F0F0F0F0F0F0F0F) + (v128.const i64x2 0x0F0F0F0F0F0F0F0F 0x0F0F0F0F0F0F0F0F)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "eq" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0x0000000000000000) + (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0x0000000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "eq" (v128.const i64x2 0x0000000000000000 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 0x0000000000000000 0xFFFFFFFFFFFFFFFF)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "eq" (v128.const i64x2 0x03020100 0x11100904) + (v128.const i64x2 0x03020100 0x11100904)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "eq" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 0x0FFFFFFFFFFFFFFF 0x0FFFFFFFFFFFFFFF)) + (v128.const i64x2 0 0)) +(assert_return (invoke "eq" (v128.const i64x2 0x1 0x1) + (v128.const i64x2 0x2 0x2)) + (v128.const i64x2 0 0)) + +;; ne + +;; i64x2.ne (i64x2) (i64x2) + +;; hex vs hex +(assert_return (invoke "ne" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF)) + (v128.const i64x2 0 0)) +(assert_return (invoke "ne" (v128.const i64x2 0x0000000000000000 0x0000000000000000) + (v128.const i64x2 0x0000000000000000 0x0000000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "ne" (v128.const i64x2 0xF0F0F0F0F0F0F0F0 0xF0F0F0F0F0F0F0F0) + (v128.const i64x2 0xF0F0F0F0F0F0F0F0 0xF0F0F0F0F0F0F0F0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "ne" (v128.const i64x2 0x0F0F0F0F0F0F0F0F 0x0F0F0F0F0F0F0F0F) + (v128.const i64x2 0x0F0F0F0F0F0F0F0F 0x0F0F0F0F0F0F0F0F)) + (v128.const i64x2 0 0)) +(assert_return (invoke "ne" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0x0000000000000000) + (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0x0000000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "ne" (v128.const i64x2 0x0000000000000000 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 0x0000000000000000 0xFFFFFFFFFFFFFFFF)) + (v128.const i64x2 0 0)) +(assert_return (invoke "ne" (v128.const i64x2 0x03020100 0x11100904) + (v128.const i64x2 0x03020100 0x11100904)) + (v128.const i64x2 0 0)) + +;; lt_s + +;; i64x2.lt_s (i64x2) (i64x2) + +;; hex vs hex +(assert_return (invoke "lt_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 0x0000000000000000 0x0000000000000000) + (v128.const i64x2 0x0000000000000000 0x0000000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 0xF0F0F0F0F0F0F0F0 0xF0F0F0F0F0F0F0F0) + (v128.const i64x2 0xF0F0F0F0F0F0F0F0 0xF0F0F0F0F0F0F0F0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 0x0F0F0F0F0F0F0F0F 0x0F0F0F0F0F0F0F0F) + (v128.const i64x2 0x0F0F0F0F0F0F0F0F 0x0F0F0F0F0F0F0F0F)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0x0000000000000000) + (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0x0000000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 0x0000000000000000 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 0x0000000000000000 0xFFFFFFFFFFFFFFFF)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 0x0302010011100904 0x1A0B0A12FFABAA1B) + (v128.const i64x2 0x0302010011100904 0x1A0B0A12FFABAA1B)) + (v128.const i64x2 0 0)) + +;; hex vs dec +(assert_return (invoke "lt_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 18446744073709551615 18446744073709551615)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 0x8080808080808080 0x8080808080808080) + (v128.const i64x2 9259542123273814144 9259542123273814144)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 0x8080808080808080 0x8080808080808080) + (v128.const i64x2 -9187201950435737472 -9187201950435737472)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 0x8382818000FFFEFD 0x7F020100FFFEFD80) + (v128.const i64x2 -8970465120996032771 9151878496576798080)) + (v128.const i64x2 0 0)) + +;; dec vs dec +(assert_return (invoke "lt_s" (v128.const i64x2 -1 -1) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 0 0) + (v128.const i64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 18446744073709551615 18446744073709551615)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 18446744073709551615 0) + (v128.const i64x2 18446744073709551615 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 0 18446744073709551615) + (v128.const i64x2 0 18446744073709551615)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 -9223372036854775807 18446744073709551615) + (v128.const i64x2 9223372036854775809 -1)) + (v128.const i64x2 0 0)) + +;; hex vs float +(assert_return (invoke "lt_s" (v128.const i64x2 0xc060000000000000 0xc05fc00000000000) + (v128.const f64x2 -128.0 -127.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "lt_s" (v128.const i64x2 0x3ff0000000000000 0x405fc00000000000) + (v128.const f64x2 1.0 127.0)) + (v128.const i64x2 0 0)) + +;; le_s + +;; i64x2.le_s (i64x2) (i64x2) + +;; hex vs hex +(assert_return (invoke "le_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 0x0000000000000000 0x0000000000000000) + (v128.const i64x2 0x0000000000000000 0x0000000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 0xF0F0F0F0F0F0F0F0 0xF0F0F0F0F0F0F0F0) + (v128.const i64x2 0xF0F0F0F0F0F0F0F0 0xF0F0F0F0F0F0F0F0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 0x0F0F0F0F0F0F0F0F 0x0F0F0F0F0F0F0F0F) + (v128.const i64x2 0x0F0F0F0F0F0F0F0F 0x0F0F0F0F0F0F0F0F)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0x0000000000000000) + (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0x0000000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 0x0000000000000000 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 0x0000000000000000 0xFFFFFFFFFFFFFFFF)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 0x0302010011100904 0x1A0B0A12FFABAA1B) + (v128.const i64x2 0x0302010011100904 0x1A0B0A12FFABAA1B)) + (v128.const i64x2 -1 -1)) + +;; hex vs dec +(assert_return (invoke "le_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 18446744073709551615 18446744073709551615)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 0x8080808080808080 0x8080808080808080) + (v128.const i64x2 9259542123273814144 9259542123273814144)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 0x8080808080808080 0x8080808080808080) + (v128.const i64x2 -9187201950435737472 -9187201950435737472)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 0x8382818000FFFEFD 0x7F020100FFFEFD80) + (v128.const i64x2 -8970465120996032771 9151878496576798080)) + (v128.const i64x2 -1 -1)) + +;; dec vs dec +(assert_return (invoke "le_s" (v128.const i64x2 -1 -1) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 0 0) + (v128.const i64x2 0 -1)) + (v128.const i64x2 -1 0)) +(assert_return (invoke "le_s" (v128.const i64x2 0 0) + (v128.const i64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 18446744073709551615 18446744073709551615)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 18446744073709551615 0) + (v128.const i64x2 18446744073709551615 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 0 18446744073709551615) + (v128.const i64x2 0 18446744073709551615)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 -9223372036854775807 18446744073709551615) + (v128.const i64x2 9223372036854775809 -1)) + (v128.const i64x2 -1 -1)) + +;; hex vs float +(assert_return (invoke "le_s" (v128.const i64x2 0xc060000000000000 0xc05fc00000000000) + (v128.const f64x2 -128.0 -127.0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "le_s" (v128.const i64x2 0x3ff0000000000000 0x405fc00000000000) + (v128.const f64x2 1.0 127.0)) + (v128.const i64x2 -1 -1)) + +;; gt_s + +;; i64x2.gt_s (i64x2) (i64x2) + +;; hex vs hex +(assert_return (invoke "gt_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 0x0000000000000000 0x0000000000000000) + (v128.const i64x2 0x0000000000000000 0x0000000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 0xF0F0F0F0F0F0F0F0 0xF0F0F0F0F0F0F0F0) + (v128.const i64x2 0xF0F0F0F0F0F0F0F0 0xF0F0F0F0F0F0F0F0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 0x0F0F0F0F0F0F0F0F 0x0F0F0F0F0F0F0F0F) + (v128.const i64x2 0x0F0F0F0F0F0F0F0F 0x0F0F0F0F0F0F0F0F)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0x0000000000000000) + (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0x0000000000000000)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 0x0000000000000000 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 0x0000000000000000 0xFFFFFFFFFFFFFFFF)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 0x0302010011100904 0x1A0B0A12FFABAA1B) + (v128.const i64x2 0x0302010011100904 0x1A0B0A12FFABAA1B)) + (v128.const i64x2 0 0)) + +;; hex vs dec +(assert_return (invoke "gt_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 18446744073709551615 18446744073709551615)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 0x8080808080808080 0x8080808080808080) + (v128.const i64x2 9259542123273814144 9259542123273814144)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 0x8080808080808080 0x8080808080808080) + (v128.const i64x2 -9187201950435737472 -9187201950435737472)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 0x8382818000FFFEFD 0x7F020100FFFEFD80) + (v128.const i64x2 -8970465120996032771 9151878496576798080)) + (v128.const i64x2 0 0)) + +;; dec vs dec +(assert_return (invoke "gt_s" (v128.const i64x2 -1 -1) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 0 0) + (v128.const i64x2 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 18446744073709551615 18446744073709551615)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 18446744073709551615 0) + (v128.const i64x2 18446744073709551615 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 0 18446744073709551615) + (v128.const i64x2 0 18446744073709551615)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 -9223372036854775807 18446744073709551615) + (v128.const i64x2 9223372036854775809 -1)) + (v128.const i64x2 0 0)) + +;; hex vs float +(assert_return (invoke "gt_s" (v128.const i64x2 0xc060000000000000 0xc05fc00000000000) + (v128.const f64x2 -128.0 -127.0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "gt_s" (v128.const i64x2 0x3ff0000000000000 0x405fc00000000000) + (v128.const f64x2 1.0 127.0)) + (v128.const i64x2 0 0)) + +;; ge_s + +;; i64x2.ge_s (i64x2) (i64x2) + +;; hex vs hex +(assert_return (invoke "ge_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 0x0000000000000000 0x0000000000000000) + (v128.const i64x2 0x0000000000000000 0x0000000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 0xF0F0F0F0F0F0F0F0 0xF0F0F0F0F0F0F0F0) + (v128.const i64x2 0xF0F0F0F0F0F0F0F0 0xF0F0F0F0F0F0F0F0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 0x0F0F0F0F0F0F0F0F 0x0F0F0F0F0F0F0F0F) + (v128.const i64x2 0x0F0F0F0F0F0F0F0F 0x0F0F0F0F0F0F0F0F)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0x0000000000000000) + (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0x0000000000000000)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 0x0000000000000000 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 0x0000000000000000 0xFFFFFFFFFFFFFFFF)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 0x0302010011100904 0x1A0B0A12FFABAA1B) + (v128.const i64x2 0x0302010011100904 0x1A0B0A12FFABAA1B)) + (v128.const i64x2 -1 -1)) + +;; hex vs dec +(assert_return (invoke "ge_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 18446744073709551615 18446744073709551615)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 0x8080808080808080 0x8080808080808080) + (v128.const i64x2 9259542123273814144 9259542123273814144)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 0x8080808080808080 0x8080808080808080) + (v128.const i64x2 -9187201950435737472 -9187201950435737472)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 0x8382818000FFFEFD 0x7F020100FFFEFD80) + (v128.const i64x2 -8970465120996032771 9151878496576798080)) + (v128.const i64x2 -1 -1)) + +;; dec vs dec +(assert_return (invoke "ge_s" (v128.const i64x2 -1 -1) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 -1 -1) + (v128.const i64x2 0 -1)) + (v128.const i64x2 0 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 0 0) + (v128.const i64x2 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 18446744073709551615 18446744073709551615)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 18446744073709551615 18446744073709551615) + (v128.const i64x2 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 18446744073709551615 0) + (v128.const i64x2 18446744073709551615 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 0 18446744073709551615) + (v128.const i64x2 0 18446744073709551615)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 -9223372036854775807 18446744073709551615) + (v128.const i64x2 9223372036854775809 -1)) + (v128.const i64x2 -1 -1)) + +;; hex vs float +(assert_return (invoke "ge_s" (v128.const i64x2 0xc060000000000000 0xc05fc00000000000) + (v128.const f64x2 -128.0 -127.0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i64x2 0x3ff0000000000000 0x405fc00000000000) + (v128.const f64x2 1.0 127.0)) + (v128.const i64x2 -1 -1)) + +;; Type check + +(assert_invalid (module (func (result v128) (i64x2.eq (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.ne (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.ge_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.gt_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.le_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.lt_s (i32.const 0) (f32.const 0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i64x2.eq-1st-arg-empty (result v128) + (i64x2.eq (v128.const i64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.eq-arg-empty (result v128) + (i64x2.eq) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.ne-1st-arg-empty (result v128) + (i64x2.ne (v128.const i64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.ne-arg-empty (result v128) + (i64x2.ne) + ) + ) + "type mismatch" +) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i64x2_extmul_i32x4.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i64x2_extmul_i32x4.wast new file mode 100644 index 000000000..9259279c9 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i64x2_extmul_i32x4.wast @@ -0,0 +1,404 @@ +;; Tests for i64x2 arithmetic operations on major boundary values and all special values. + + +(module + (func (export "i64x2.extmul_low_i32x4_s") (param v128 v128) (result v128) (i64x2.extmul_low_i32x4_s (local.get 0) (local.get 1))) + (func (export "i64x2.extmul_high_i32x4_s") (param v128 v128) (result v128) (i64x2.extmul_high_i32x4_s (local.get 0) (local.get 1))) + (func (export "i64x2.extmul_low_i32x4_u") (param v128 v128) (result v128) (i64x2.extmul_low_i32x4_u (local.get 0) (local.get 1))) + (func (export "i64x2.extmul_high_i32x4_u") (param v128 v128) (result v128) (i64x2.extmul_high_i32x4_u (local.get 0) (local.get 1))) +) + + +;; i64x2.extmul_low_i32x4_s +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 1073741823 1073741823 1073741823 1073741823) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) + (v128.const i64x2 1152921503533105152 1152921503533105152)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 1073741824 1073741824 1073741824 1073741824) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) + (v128.const i64x2 1152921504606846976 1152921504606846976)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 -1073741823 -1073741823 -1073741823 -1073741823) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i64x2 1152921503533105152 1152921503533105152)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i64x2 1152921504606846976 1152921504606846976)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 -1073741825 -1073741825 -1073741825 -1073741825) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i64x2 1152921505680588800 1152921505680588800)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 2147483645 2147483645 2147483645 2147483645) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 2147483645 2147483645)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 2147483646 2147483646 2147483646 2147483646) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 2147483646 2147483646)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 2147483648 2147483648 2147483648 2147483648) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 -2147483648 -2147483648)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 -2147483646 -2147483646 -2147483646 -2147483646) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 2147483646 2147483646)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 2147483648 2147483648)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 2147483647 2147483647 2147483647 2147483647) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i64x2 4611686014132420609 4611686014132420609)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i64x2 4611686018427387904 4611686018427387904)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) + (v128.const i64x2 4611686016279904256 4611686016279904256)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 0 0 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i64x2 -2147483647 -2147483647)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i64x2 2147483648 2147483648)) +(assert_return (invoke "i64x2.extmul_low_i32x4_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i64x2 1 1)) + +;; i64x2.extmul_high_i32x4_s +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 1073741823 1073741823 1073741823 1073741823) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) + (v128.const i64x2 1152921503533105152 1152921503533105152)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 1073741824 1073741824 1073741824 1073741824) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) + (v128.const i64x2 1152921504606846976 1152921504606846976)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 -1073741823 -1073741823 -1073741823 -1073741823) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i64x2 1152921503533105152 1152921503533105152)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i64x2 1152921504606846976 1152921504606846976)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 -1073741825 -1073741825 -1073741825 -1073741825) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i64x2 1152921505680588800 1152921505680588800)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 2147483645 2147483645 2147483645 2147483645) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 2147483645 2147483645)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 2147483646 2147483646 2147483646 2147483646) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 2147483646 2147483646)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 2147483648 2147483648 2147483648 2147483648) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 -2147483648 -2147483648)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 -2147483646 -2147483646 -2147483646 -2147483646) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 2147483646 2147483646)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 2147483648 2147483648)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 2147483647 2147483647 2147483647 2147483647) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i64x2 4611686014132420609 4611686014132420609)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i64x2 4611686018427387904 4611686018427387904)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) + (v128.const i64x2 4611686016279904256 4611686016279904256)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 0 0 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i64x2 -2147483647 -2147483647)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i64x2 2147483648 2147483648)) +(assert_return (invoke "i64x2.extmul_high_i32x4_s" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i64x2 1 1)) + +;; i64x2.extmul_low_i32x4_u +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 4294967295 4294967295)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 -8589934591 -8589934591)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 1073741823 1073741823 1073741823 1073741823) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) + (v128.const i64x2 1152921503533105152 1152921503533105152)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 1073741824 1073741824 1073741824 1073741824) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) + (v128.const i64x2 1152921504606846976 1152921504606846976)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 -1073741823 -1073741823 -1073741823 -1073741823) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i64x2 -8070450529026703360 -8070450529026703360)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i64x2 -8070450532247928832 -8070450532247928832)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 -1073741825 -1073741825 -1073741825 -1073741825) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i64x2 -8070450535469154304 -8070450535469154304)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 2147483645 2147483645 2147483645 2147483645) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 2147483645 2147483645)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 2147483646 2147483646 2147483646 2147483646) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 2147483646 2147483646)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 2147483648 2147483648 2147483648 2147483648) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 2147483648 2147483648)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 -2147483646 -2147483646 -2147483646 -2147483646) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 -9223372030412324866 -9223372030412324866)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 -9223372034707292161 -9223372034707292161)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 9223372034707292160 9223372034707292160)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 2147483647 2147483647 2147483647 2147483647) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i64x2 4611686014132420609 4611686014132420609)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i64x2 4611686018427387904 4611686018427387904)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) + (v128.const i64x2 4611686020574871552 4611686020574871552)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 0 0 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 4294967295 4294967295)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 -8589934591 -8589934591)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i64x2 9223372030412324865 9223372030412324865)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i64x2 9223372034707292160 9223372034707292160)) +(assert_return (invoke "i64x2.extmul_low_i32x4_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i64x2 -8589934591 -8589934591)) + +;; i64x2.extmul_high_i32x4_u +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 0 0 0 0) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 1 1 1 1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 4294967295 4294967295)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 -1 -1 -1 -1) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 -8589934591 -8589934591)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 1073741823 1073741823 1073741823 1073741823) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) + (v128.const i64x2 1152921503533105152 1152921503533105152)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 1073741824 1073741824 1073741824 1073741824) + (v128.const i32x4 1073741824 1073741824 1073741824 1073741824)) + (v128.const i64x2 1152921504606846976 1152921504606846976)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 -1073741823 -1073741823 -1073741823 -1073741823) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i64x2 -8070450529026703360 -8070450529026703360)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i64x2 -8070450532247928832 -8070450532247928832)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 -1073741825 -1073741825 -1073741825 -1073741825) + (v128.const i32x4 -1073741824 -1073741824 -1073741824 -1073741824)) + (v128.const i64x2 -8070450535469154304 -8070450535469154304)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 2147483645 2147483645 2147483645 2147483645) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 2147483645 2147483645)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 2147483646 2147483646 2147483646 2147483646) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 2147483646 2147483646)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 2147483648 2147483648 2147483648 2147483648) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 2147483648 2147483648)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 -2147483646 -2147483646 -2147483646 -2147483646) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 -9223372030412324866 -9223372030412324866)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 -9223372034707292161 -9223372034707292161)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 9223372034707292160 9223372034707292160)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 2147483647 2147483647 2147483647 2147483647) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i64x2 4611686014132420609 4611686014132420609)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i64x2 4611686018427387904 4611686018427387904)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648) + (v128.const i32x4 -2147483647 -2147483647 -2147483647 -2147483647)) + (v128.const i64x2 4611686020574871552 4611686020574871552)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 0 0 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 1 1 1 1)) + (v128.const i64x2 4294967295 4294967295)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -1 -1 -1 -1)) + (v128.const i64x2 -8589934591 -8589934591)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i64x2 9223372030412324865 9223372030412324865)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i64x2 9223372034707292160 9223372034707292160)) +(assert_return (invoke "i64x2.extmul_high_i32x4_u" (v128.const i32x4 4294967295 4294967295 4294967295 4294967295) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i64x2 -8589934591 -8589934591)) + +;; type check +(assert_invalid (module (func (result v128) (i64x2.extmul_low_i32x4_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.extmul_high_i32x4_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.extmul_low_i32x4_u (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.extmul_high_i32x4_u (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i64x2.extmul_low_i32x4_s-1st-arg-empty (result v128) + (i64x2.extmul_low_i32x4_s (v128.const i64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.extmul_low_i32x4_s-arg-empty (result v128) + (i64x2.extmul_low_i32x4_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.extmul_high_i32x4_s-1st-arg-empty (result v128) + (i64x2.extmul_high_i32x4_s (v128.const i64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.extmul_high_i32x4_s-arg-empty (result v128) + (i64x2.extmul_high_i32x4_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.extmul_low_i32x4_u-1st-arg-empty (result v128) + (i64x2.extmul_low_i32x4_u (v128.const i64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.extmul_low_i32x4_u-arg-empty (result v128) + (i64x2.extmul_low_i32x4_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.extmul_high_i32x4_u-1st-arg-empty (result v128) + (i64x2.extmul_high_i32x4_u (v128.const i64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.extmul_high_i32x4_u-arg-empty (result v128) + (i64x2.extmul_high_i32x4_u) + ) + ) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i8x16_arith.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i8x16_arith.wast new file mode 100644 index 000000000..1e56b4c00 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i8x16_arith.wast @@ -0,0 +1,426 @@ +;; Tests for i8x16 arithmetic operations on major boundary values and all special values. + + +(module + (func (export "i8x16.add") (param v128 v128) (result v128) (i8x16.add (local.get 0) (local.get 1))) + (func (export "i8x16.sub") (param v128 v128) (result v128) (i8x16.sub (local.get 0) (local.get 1))) + (func (export "i8x16.neg") (param v128) (result v128) (i8x16.neg (local.get 0))) +) + + +;; i8x16.add +(assert_return (invoke "i8x16.add" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f) + (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40) + (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f) + (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40)) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40) + (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40) + (v128.const i8x16 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i8x16 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const f32x4 +0.0 +0.0 +0.0 +0.0)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i8x16 0x80 0x80 0x80 0 0x80 0x80 0x80 0 0x80 0x80 0x80 0 0x80 0x80 0x80 0)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const f32x4 1.0 1.0 1.0 1.0)) + (v128.const i8x16 0x80 0x80 0 0xbf 0x80 0x80 0 0xbf 0x80 0x80 0 0xbf 0x80 0x80 0 0xbf)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const f32x4 -1.0 -1.0 -1.0 -1.0)) + (v128.const i8x16 0x80 0x80 0 0x3f 0x80 0x80 0 0x3f 0x80 0x80 0 0x3f 0x80 0x80 0 0x3f)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i8x16 0x01 0x01 0x81 0x80 0x01 0x01 0x81 0x80 0x01 0x01 0x81 0x80 0x01 0x01 0x81 0x80)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i8x16 0x01 0x01 0x81 0 0x01 0x01 0x81 0 0x01 0x01 0x81 0 0x01 0x01 0x81 0)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i8x16 0x01 0x01 0xc1 0x80 0x01 0x01 0xc1 0x80 0x01 0x01 0xc1 0x80 0x01 0x01 0xc1 0x80)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 0xff 0xfe 0xfd 0xfc 0xfb 0xfa 0xf9 0xf8 0xf7 0xf6 0xf5 0xf4 0xf3 0xf2 0xf1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.add" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30)) + (v128.const i8x16 0 3 6 9 12 15 18 21 24 27 30 33 36 39 42 45)) + +;; i8x16.sub +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 124 124 124 124 124 124 124 124 124 124 124 124 124 124 124 124)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -125 -125 -125 -125 -125 -125 -125 -125 -125 -125 -125 -125 -125 -125 -125 -125)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f) + (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40) + (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f) + (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40) + (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40) + (v128.const i8x16 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01)) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i8x16 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i8x16 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i16x8 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080 0x8080)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const f32x4 +0.0 +0.0 +0.0 +0.0)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i8x16 0x80 0x80 0x80 0 0x80 0x80 0x80 0 0x80 0x80 0x80 0 0x80 0x80 0x80 0)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const f32x4 1.0 1.0 1.0 1.0)) + (v128.const i8x16 0x80 0x80 0 0x41 0x80 0x80 0 0x41 0x80 0x80 0 0x41 0x80 0x80 0 0x41)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const f32x4 -1.0 -1.0 -1.0 -1.0)) + (v128.const i8x16 0x80 0x80 0 0xc1 0x80 0x80 0 0xc1 0x80 0x80 0 0xc1 0x80 0x80 0 0xc1)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i8x16 0x01 0x01 0x81 0x82 0x01 0x01 0x81 0x82 0x01 0x01 0x81 0x82 0x01 0x01 0x81 0x82)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i8x16 0x01 0x01 0x81 0x02 0x01 0x01 0x81 0x02 0x01 0x01 0x81 0x02 0x01 0x01 0x81 0x02)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i8x16 0x01 0x01 0x41 0x82 0x01 0x01 0x41 0x82 0x01 0x01 0x41 0x82 0x01 0x01 0x41 0x82)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 0xff 0xfe 0xfd 0xfc 0xfb 0xfa 0xf9 0xf8 0xf7 0xf6 0xf5 0xf4 0xf3 0xf2 0xf1)) + (v128.const i8x16 0 0x02 0x04 0x06 0x08 0x0a 0x0c 0x0e 0x10 0x12 0x14 0x16 0x18 0x1a 0x1c 0x1e)) +(assert_return (invoke "i8x16.sub" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30)) + (v128.const i8x16 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15)) + +;; i8x16.neg +(assert_return (invoke "i8x16.neg" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.neg" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.neg" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.neg" (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126)) + (v128.const i8x16 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126)) +(assert_return (invoke "i8x16.neg" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.neg" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.neg" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i8x16.neg" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.neg" (v128.const i8x16 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.neg" (v128.const i8x16 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.neg" (v128.const i8x16 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.neg" (v128.const i8x16 -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f -0x7f)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.neg" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i8x16.neg" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.neg" (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + +;; type check +(assert_invalid (module (func (result v128) (i8x16.neg (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.add (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.sub (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i8x16.neg-arg-empty (result v128) + (i8x16.neg) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.add-1st-arg-empty (result v128) + (i8x16.add (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.add-arg-empty (result v128) + (i8x16.add) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.sub-1st-arg-empty (result v128) + (i8x16.sub (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.sub-arg-empty (result v128) + (i8x16.sub) + ) + ) + "type mismatch" +) + +;; combination +(module + (func (export "add-sub") (param v128 v128 v128) (result v128) + (i8x16.add (i8x16.sub (local.get 0) (local.get 1))(local.get 2))) + (func (export "sub-add") (param v128 v128 v128) (result v128) + (i8x16.sub (i8x16.add (local.get 0) (local.get 1))(local.get 2))) + (func (export "add-neg") (param v128 v128) (result v128) + (i8x16.add (i8x16.neg (local.get 0)) (local.get 1))) + (func (export "sub-neg") (param v128 v128) (result v128) + (i8x16.sub (i8x16.neg (local.get 0)) (local.get 1))) +) + +(assert_return (invoke "add-sub" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) +(assert_return (invoke "sub-add" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) +(assert_return (invoke "add-neg" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "sub-neg" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) + (v128.const i8x16 0 -2 -4 -6 -8 -10 -12 -14 -16 -18 -20 -22 -24 -26 -28 -30)) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i8x16_arith2.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i8x16_arith2.wast new file mode 100644 index 000000000..991ec14bb --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i8x16_arith2.wast @@ -0,0 +1,713 @@ +;; Tests for i8x16 [min_s, min_u, max_s, max_u, avgr_u, abs] operations. + +(module + (func (export "i8x16.min_s") (param v128 v128) (result v128) (i8x16.min_s (local.get 0) (local.get 1))) + (func (export "i8x16.min_u") (param v128 v128) (result v128) (i8x16.min_u (local.get 0) (local.get 1))) + (func (export "i8x16.max_s") (param v128 v128) (result v128) (i8x16.max_s (local.get 0) (local.get 1))) + (func (export "i8x16.max_u") (param v128 v128) (result v128) (i8x16.max_u (local.get 0) (local.get 1))) + (func (export "i8x16.avgr_u") (param v128 v128) (result v128) (i8x16.avgr_u (local.get 0) (local.get 1))) + (func (export "i8x16.abs") (param v128) (result v128) (i8x16.abs (local.get 0))) + (func (export "i8x16.popcnt") (param v128) (result v128) (i8x16.popcnt (local.get 0))) + (func (export "i8x16.min_s_with_const_0") (result v128) (i8x16.min_s (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255) (v128.const i8x16 255 255 255 255 64 64 64 64 127 127 127 127 -128 -128 -128 -128))) + (func (export "i8x16.min_s_with_const_1") (result v128) (i8x16.min_s (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3) (v128.const i8x16 3 3 3 3 2 2 2 2 1 1 1 1 0 0 0 0))) + (func (export "i8x16.min_u_with_const_2") (result v128) (i8x16.min_u (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255) (v128.const i8x16 255 255 255 255 64 64 64 64 127 127 127 127 -128 -128 -128 -128))) + (func (export "i8x16.min_u_with_const_3") (result v128) (i8x16.min_u (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3) (v128.const i8x16 3 3 3 3 2 2 2 2 1 1 1 1 0 0 0 0))) + (func (export "i8x16.max_s_with_const_4") (result v128) (i8x16.max_s (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255) (v128.const i8x16 255 255 255 255 64 64 64 64 127 127 127 127 -128 -128 -128 -128))) + (func (export "i8x16.max_s_with_const_5") (result v128) (i8x16.max_s (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3) (v128.const i8x16 3 3 3 3 2 2 2 2 1 1 1 1 0 0 0 0))) + (func (export "i8x16.max_u_with_const_6") (result v128) (i8x16.max_u (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255) (v128.const i8x16 255 255 255 255 64 64 64 64 127 127 127 127 -128 -128 -128 -128))) + (func (export "i8x16.max_u_with_const_7") (result v128) (i8x16.max_u (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3) (v128.const i8x16 3 3 3 3 2 2 2 2 1 1 1 1 0 0 0 0))) + (func (export "i8x16.avgr_u_with_const_8") (result v128) (i8x16.avgr_u (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255) (v128.const i8x16 255 255 255 255 64 64 64 64 127 127 127 127 -128 -128 -128 -128))) + (func (export "i8x16.avgr_u_with_const_9") (result v128) (i8x16.avgr_u (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3) (v128.const i8x16 3 3 3 3 2 2 2 2 1 1 1 1 0 0 0 0))) + (func (export "i8x16.abs_with_const_10") (result v128) (i8x16.abs (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255))) + (func (export "i8x16.popcnt_with_const_11") (result v128) (i8x16.popcnt (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255))) + (func (export "i8x16.min_s_with_const_12") (param v128) (result v128) (i8x16.min_s (local.get 0) (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255))) + (func (export "i8x16.min_s_with_const_13") (param v128) (result v128) (i8x16.min_s (local.get 0) (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3))) + (func (export "i8x16.min_u_with_const_14") (param v128) (result v128) (i8x16.min_u (local.get 0) (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255))) + (func (export "i8x16.min_u_with_const_15") (param v128) (result v128) (i8x16.min_u (local.get 0) (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3))) + (func (export "i8x16.max_s_with_const_16") (param v128) (result v128) (i8x16.max_s (local.get 0) (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255))) + (func (export "i8x16.max_s_with_const_17") (param v128) (result v128) (i8x16.max_s (local.get 0) (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3))) + (func (export "i8x16.max_u_with_const_18") (param v128) (result v128) (i8x16.max_u (local.get 0) (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255))) + (func (export "i8x16.max_u_with_const_19") (param v128) (result v128) (i8x16.max_u (local.get 0) (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3))) + (func (export "i8x16.avgr_u_with_const_20") (param v128) (result v128) (i8x16.avgr_u (local.get 0) (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255))) + (func (export "i8x16.avgr_u_with_const_21") (param v128) (result v128) (i8x16.avgr_u (local.get 0) (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3))) +) + +(assert_return (invoke "i8x16.min_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.min_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.min_s" (v128.const i8x16 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 0 0 0 0 -1 -1 -1 -1 0 0 0 0 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.min_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) +(assert_return (invoke "i8x16.min_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.min_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.min_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.min_s" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.min_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.min_s" (v128.const i8x16 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123) + (v128.const i8x16 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i8x16 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123)) +(assert_return (invoke "i8x16.min_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.min_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.min_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.min_u" (v128.const i8x16 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 0 0 0 0 -1 -1 -1 -1 0 0 0 0 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.min_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.min_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.min_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.min_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.min_u" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.min_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.min_u" (v128.const i8x16 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123) + (v128.const i8x16 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i8x16 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123)) +(assert_return (invoke "i8x16.min_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.max_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.max_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.max_s" (v128.const i8x16 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 0 0 0 0 -1 -1 -1 -1 0 0 0 0 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.max_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.max_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.max_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.max_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.max_s" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.max_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.max_s" (v128.const i8x16 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123) + (v128.const i8x16 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i8x16 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123)) +(assert_return (invoke "i8x16.max_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.max_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.max_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.max_u" (v128.const i8x16 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 0 0 0 0 -1 -1 -1 -1 0 0 0 0 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.max_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) +(assert_return (invoke "i8x16.max_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.max_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.max_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.max_u" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.max_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.max_u" (v128.const i8x16 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123) + (v128.const i8x16 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i8x16 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123)) +(assert_return (invoke "i8x16.max_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.avgr_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.avgr_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.avgr_u" (v128.const i8x16 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 0 0 0 0 -1 -1 -1 -1 0 0 0 0 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 128 128 128 128 128 128 128 128 255 255 255 255)) +(assert_return (invoke "i8x16.avgr_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.avgr_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.avgr_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.avgr_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 192 192 192 192 192 192 192 192 192 192 192 192 192 192 192 192)) +(assert_return (invoke "i8x16.avgr_u" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.avgr_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.avgr_u" (v128.const i8x16 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123) + (v128.const i8x16 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i8x16 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123)) +(assert_return (invoke "i8x16.avgr_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0x1 0x1 0x1 0x1 0x1 0x1 0x1 0x1 0x1 0x1 0x1 0x1 0x1 0x1 0x1 0x1)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i8x16 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3)) + (v128.const i8x16 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3 01_2_3)) + (v128.const i8x16 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3 -01_2_3)) + (v128.const i8x16 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80 -0x80)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0 0x0_8_0)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0 -0x0_8_0)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + +;; Const vs const +(assert_return (invoke "i8x16.min_s_with_const_0") (v128.const i8x16 -128 -128 -128 -128 64 64 64 64 64 64 64 64 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.min_s_with_const_1") (v128.const i8x16 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0)) +(assert_return (invoke "i8x16.min_u_with_const_2") (v128.const i8x16 -128 -128 -128 -128 64 64 64 64 64 64 64 64 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.min_u_with_const_3") (v128.const i8x16 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0)) +(assert_return (invoke "i8x16.max_s_with_const_4") (v128.const i8x16 255 255 255 255 127 127 127 127 127 127 127 127 255 255 255 255)) +(assert_return (invoke "i8x16.max_s_with_const_5") (v128.const i8x16 3 3 3 3 2 2 2 2 2 2 2 2 3 3 3 3)) +(assert_return (invoke "i8x16.max_u_with_const_6") (v128.const i8x16 255 255 255 255 127 127 127 127 127 127 127 127 255 255 255 255)) +(assert_return (invoke "i8x16.max_u_with_const_7") (v128.const i8x16 3 3 3 3 2 2 2 2 2 2 2 2 3 3 3 3)) +(assert_return (invoke "i8x16.avgr_u_with_const_8") (v128.const i8x16 192 192 192 192 96 96 96 96 96 96 96 96 192 192 192 192)) +(assert_return (invoke "i8x16.avgr_u_with_const_9") (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.abs_with_const_10") (v128.const i8x16 128 128 128 128 127 127 127 127 64 64 64 64 1 1 1 1)) +(assert_return (invoke "i8x16.popcnt_with_const_11") (v128.const i8x16 1 1 1 1 7 7 7 7 1 1 1 1 8 8 8 8)) + +;; Param vs const +(assert_return (invoke "i8x16.min_s_with_const_12" (v128.const i8x16 255 255 255 255 64 64 64 64 127 127 127 127 -128 -128 -128 -128)) + (v128.const i8x16 -128 -128 -128 -128 64 64 64 64 64 64 64 64 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.min_s_with_const_13" (v128.const i8x16 3 3 3 3 2 2 2 2 1 1 1 1 0 0 0 0)) + (v128.const i8x16 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0)) +(assert_return (invoke "i8x16.min_u_with_const_14" (v128.const i8x16 255 255 255 255 64 64 64 64 127 127 127 127 -128 -128 -128 -128)) + (v128.const i8x16 -128 -128 -128 -128 64 64 64 64 64 64 64 64 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.min_u_with_const_15" (v128.const i8x16 3 3 3 3 2 2 2 2 1 1 1 1 0 0 0 0)) + (v128.const i8x16 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0)) +(assert_return (invoke "i8x16.max_s_with_const_16" (v128.const i8x16 255 255 255 255 64 64 64 64 127 127 127 127 -128 -128 -128 -128)) + (v128.const i8x16 255 255 255 255 127 127 127 127 127 127 127 127 255 255 255 255)) +(assert_return (invoke "i8x16.max_s_with_const_17" (v128.const i8x16 3 3 3 3 2 2 2 2 1 1 1 1 0 0 0 0)) + (v128.const i8x16 3 3 3 3 2 2 2 2 2 2 2 2 3 3 3 3)) +(assert_return (invoke "i8x16.max_u_with_const_18" (v128.const i8x16 255 255 255 255 64 64 64 64 127 127 127 127 -128 -128 -128 -128)) + (v128.const i8x16 255 255 255 255 127 127 127 127 127 127 127 127 255 255 255 255)) +(assert_return (invoke "i8x16.max_u_with_const_19" (v128.const i8x16 3 3 3 3 2 2 2 2 1 1 1 1 0 0 0 0)) + (v128.const i8x16 3 3 3 3 2 2 2 2 2 2 2 2 3 3 3 3)) +(assert_return (invoke "i8x16.avgr_u_with_const_20" (v128.const i8x16 255 255 255 255 64 64 64 64 127 127 127 127 -128 -128 -128 -128)) + (v128.const i8x16 192 192 192 192 96 96 96 96 96 96 96 96 192 192 192 192)) +(assert_return (invoke "i8x16.avgr_u_with_const_21" (v128.const i8x16 3 3 3 3 2 2 2 2 1 1 1 1 0 0 0 0)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + +;; Test different lanes go through different if-then clauses +(assert_return (invoke "i8x16.min_s" (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255) + (v128.const i8x16 255 255 255 255 64 64 64 64 127 127 127 127 -128 -128 -128 -128)) + (v128.const i8x16 -128 -128 -128 -128 64 64 64 64 64 64 64 64 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.min_s" (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 2 2 128 128 128 128) + (v128.const i8x16 0 0 0 0 2 2 2 2 1 1 1 1 128 128 128 128)) + (v128.const i8x16 0 0 0 0 1 1 1 1 1 1 1 1 128 128 128 128)) +(assert_return (invoke "i8x16.min_u" (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255) + (v128.const i8x16 255 255 255 255 64 64 64 64 127 127 127 127 -128 -128 -128 -128)) + (v128.const i8x16 -128 -128 -128 -128 64 64 64 64 64 64 64 64 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.min_u" (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 2 2 128 128 128 128) + (v128.const i8x16 0 0 0 0 2 2 2 2 1 1 1 1 128 128 128 128)) + (v128.const i8x16 0 0 0 0 1 1 1 1 1 1 1 1 128 128 128 128)) +(assert_return (invoke "i8x16.max_s" (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255) + (v128.const i8x16 255 255 255 255 64 64 64 64 127 127 127 127 -128 -128 -128 -128)) + (v128.const i8x16 255 255 255 255 127 127 127 127 127 127 127 127 255 255 255 255)) +(assert_return (invoke "i8x16.max_s" (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 2 2 128 128 128 128) + (v128.const i8x16 0 0 0 0 2 2 2 2 1 1 1 1 128 128 128 128)) + (v128.const i8x16 0 0 0 0 2 2 2 2 2 2 2 2 128 128 128 128)) +(assert_return (invoke "i8x16.max_u" (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255) + (v128.const i8x16 255 255 255 255 64 64 64 64 127 127 127 127 -128 -128 -128 -128)) + (v128.const i8x16 255 255 255 255 127 127 127 127 127 127 127 127 255 255 255 255)) +(assert_return (invoke "i8x16.max_u" (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 2 2 128 128 128 128) + (v128.const i8x16 0 0 0 0 2 2 2 2 1 1 1 1 128 128 128 128)) + (v128.const i8x16 0 0 0 0 2 2 2 2 2 2 2 2 128 128 128 128)) +(assert_return (invoke "i8x16.avgr_u" (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255) + (v128.const i8x16 255 255 255 255 64 64 64 64 127 127 127 127 -128 -128 -128 -128)) + (v128.const i8x16 192 192 192 192 96 96 96 96 96 96 96 96 192 192 192 192)) +(assert_return (invoke "i8x16.avgr_u" (v128.const i8x16 0 0 0 0 1 1 1 1 2 2 2 2 128 128 128 128) + (v128.const i8x16 0 0 0 0 2 2 2 2 1 1 1 1 128 128 128 128)) + (v128.const i8x16 0 0 0 0 2 2 2 2 2 2 2 2 128 128 128 128)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255)) + (v128.const i8x16 128 128 128 128 127 127 127 127 64 64 64 64 1 1 1 1)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 64 64 64 64 255 255 255 255)) + (v128.const i8x16 1 1 1 1 7 7 7 7 1 1 1 1 8 8 8 8)) + +;; Test opposite signs of zero +(assert_return (invoke "i8x16.min_s" (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 +0 +0 +0 +0 +0 +0 +0 +0) + (v128.const i8x16 +0 +0 +0 +0 0 0 0 0 -0 -0 -0 -0 0 0 0 0)) + (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 +0 +0 +0 +0 +0 +0 +0 +0)) +(assert_return (invoke "i8x16.min_s" (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0) + (v128.const i8x16 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0)) + (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0)) +(assert_return (invoke "i8x16.min_u" (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 +0 +0 +0 +0 +0 +0 +0 +0) + (v128.const i8x16 +0 +0 +0 +0 0 0 0 0 -0 -0 -0 -0 0 0 0 0)) + (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 +0 +0 +0 +0 +0 +0 +0 +0)) +(assert_return (invoke "i8x16.min_u" (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0) + (v128.const i8x16 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0)) + (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0)) +(assert_return (invoke "i8x16.max_s" (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 +0 +0 +0 +0 +0 +0 +0 +0) + (v128.const i8x16 +0 +0 +0 +0 0 0 0 0 -0 -0 -0 -0 0 0 0 0)) + (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 +0 +0 +0 +0 +0 +0 +0 +0)) +(assert_return (invoke "i8x16.max_s" (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0) + (v128.const i8x16 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0)) + (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0)) +(assert_return (invoke "i8x16.max_u" (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 +0 +0 +0 +0 +0 +0 +0 +0) + (v128.const i8x16 +0 +0 +0 +0 0 0 0 0 -0 -0 -0 -0 0 0 0 0)) + (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 +0 +0 +0 +0 +0 +0 +0 +0)) +(assert_return (invoke "i8x16.max_u" (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0) + (v128.const i8x16 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0)) + (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0)) +(assert_return (invoke "i8x16.avgr_u" (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 +0 +0 +0 +0 +0 +0 +0 +0) + (v128.const i8x16 +0 +0 +0 +0 0 0 0 0 -0 -0 -0 -0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.avgr_u" (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0) + (v128.const i8x16 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 +0 +0 +0 +0 +0 +0 +0 +0)) + (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 +0 +0 +0 +0 +0 +0 +0 +0)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 +0 +0 +0 +0 0 0 0 0 -0 -0 -0 -0 0 0 0 0)) + (v128.const i8x16 +0 +0 +0 +0 0 0 0 0 -0 -0 -0 -0 0 0 0 0)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0)) + (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0)) +(assert_return (invoke "i8x16.abs" (v128.const i8x16 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0)) + (v128.const i8x16 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 +0 +0 +0 +0 +0 +0 +0 +0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 +0 +0 +0 +0 0 0 0 0 -0 -0 -0 -0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.popcnt" (v128.const i8x16 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; Unknown operators +(assert_malformed (module quote "(memory 1) (func (result v128) (i32x4.avgr_u (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (f32x4.avgr_u (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i64x2.avgr_u (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (f64x2.avgr_u (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.avgr (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (result v128) (i8x16.avgr_s (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)))") "unknown operator") + +;; Type check +(assert_invalid (module (func (result v128) (i8x16.min_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.min_u (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.max_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.max_u (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.avgr_u (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.abs (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.popcnt (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i8x16.min_s-1st-arg-empty (result v128) + (i8x16.min_s (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.min_s-arg-empty (result v128) + (i8x16.min_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.min_u-1st-arg-empty (result v128) + (i8x16.min_u (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.min_u-arg-empty (result v128) + (i8x16.min_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.max_s-1st-arg-empty (result v128) + (i8x16.max_s (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.max_s-arg-empty (result v128) + (i8x16.max_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.max_u-1st-arg-empty (result v128) + (i8x16.max_u (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.max_u-arg-empty (result v128) + (i8x16.max_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.avgr_u-1st-arg-empty (result v128) + (i8x16.avgr_u (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.avgr_u-arg-empty (result v128) + (i8x16.avgr_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.abs-arg-empty (result v128) + (i8x16.abs) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.popcnt-arg-empty (result v128) + (i8x16.popcnt) + ) + ) + "type mismatch" +) + +;; Combination +(module + (func (export "i8x16.min_s-i8x16.avgr_u") (param v128 v128 v128) (result v128) (i8x16.min_s (i8x16.avgr_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.min_s-i8x16.max_u") (param v128 v128 v128) (result v128) (i8x16.min_s (i8x16.max_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.min_s-i8x16.max_s") (param v128 v128 v128) (result v128) (i8x16.min_s (i8x16.max_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.min_s-i8x16.min_u") (param v128 v128 v128) (result v128) (i8x16.min_s (i8x16.min_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.min_s-i8x16.min_s") (param v128 v128 v128) (result v128) (i8x16.min_s (i8x16.min_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.min_s-i8x16.abs") (param v128 v128) (result v128) (i8x16.min_s (i8x16.abs (local.get 0))(local.get 1))) + (func (export "i8x16.abs-i8x16.min_s") (param v128 v128) (result v128) (i8x16.abs (i8x16.min_s (local.get 0) (local.get 1)))) + (func (export "i8x16.min_s-i8x16.popcnt") (param v128 v128) (result v128) (i8x16.min_s (i8x16.popcnt (local.get 0))(local.get 1))) + (func (export "i8x16.popcnt-i8x16.min_s") (param v128 v128) (result v128) (i8x16.popcnt (i8x16.min_s (local.get 0) (local.get 1)))) + (func (export "i8x16.min_u-i8x16.avgr_u") (param v128 v128 v128) (result v128) (i8x16.min_u (i8x16.avgr_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.min_u-i8x16.max_u") (param v128 v128 v128) (result v128) (i8x16.min_u (i8x16.max_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.min_u-i8x16.max_s") (param v128 v128 v128) (result v128) (i8x16.min_u (i8x16.max_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.min_u-i8x16.min_u") (param v128 v128 v128) (result v128) (i8x16.min_u (i8x16.min_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.min_u-i8x16.min_s") (param v128 v128 v128) (result v128) (i8x16.min_u (i8x16.min_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.min_u-i8x16.abs") (param v128 v128) (result v128) (i8x16.min_u (i8x16.abs (local.get 0))(local.get 1))) + (func (export "i8x16.abs-i8x16.min_u") (param v128 v128) (result v128) (i8x16.abs (i8x16.min_u (local.get 0) (local.get 1)))) + (func (export "i8x16.min_u-i8x16.popcnt") (param v128 v128) (result v128) (i8x16.min_u (i8x16.popcnt (local.get 0))(local.get 1))) + (func (export "i8x16.popcnt-i8x16.min_u") (param v128 v128) (result v128) (i8x16.popcnt (i8x16.min_u (local.get 0) (local.get 1)))) + (func (export "i8x16.max_s-i8x16.avgr_u") (param v128 v128 v128) (result v128) (i8x16.max_s (i8x16.avgr_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.max_s-i8x16.max_u") (param v128 v128 v128) (result v128) (i8x16.max_s (i8x16.max_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.max_s-i8x16.max_s") (param v128 v128 v128) (result v128) (i8x16.max_s (i8x16.max_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.max_s-i8x16.min_u") (param v128 v128 v128) (result v128) (i8x16.max_s (i8x16.min_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.max_s-i8x16.min_s") (param v128 v128 v128) (result v128) (i8x16.max_s (i8x16.min_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.max_s-i8x16.abs") (param v128 v128) (result v128) (i8x16.max_s (i8x16.abs (local.get 0))(local.get 1))) + (func (export "i8x16.abs-i8x16.max_s") (param v128 v128) (result v128) (i8x16.abs (i8x16.max_s (local.get 0) (local.get 1)))) + (func (export "i8x16.max_s-i8x16.popcnt") (param v128 v128) (result v128) (i8x16.max_s (i8x16.popcnt (local.get 0))(local.get 1))) + (func (export "i8x16.popcnt-i8x16.max_s") (param v128 v128) (result v128) (i8x16.popcnt (i8x16.max_s (local.get 0) (local.get 1)))) + (func (export "i8x16.max_u-i8x16.avgr_u") (param v128 v128 v128) (result v128) (i8x16.max_u (i8x16.avgr_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.max_u-i8x16.max_u") (param v128 v128 v128) (result v128) (i8x16.max_u (i8x16.max_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.max_u-i8x16.max_s") (param v128 v128 v128) (result v128) (i8x16.max_u (i8x16.max_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.max_u-i8x16.min_u") (param v128 v128 v128) (result v128) (i8x16.max_u (i8x16.min_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.max_u-i8x16.min_s") (param v128 v128 v128) (result v128) (i8x16.max_u (i8x16.min_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.max_u-i8x16.abs") (param v128 v128) (result v128) (i8x16.max_u (i8x16.abs (local.get 0))(local.get 1))) + (func (export "i8x16.abs-i8x16.max_u") (param v128 v128) (result v128) (i8x16.abs (i8x16.max_u (local.get 0) (local.get 1)))) + (func (export "i8x16.max_u-i8x16.popcnt") (param v128 v128) (result v128) (i8x16.max_u (i8x16.popcnt (local.get 0))(local.get 1))) + (func (export "i8x16.popcnt-i8x16.max_u") (param v128 v128) (result v128) (i8x16.popcnt (i8x16.max_u (local.get 0) (local.get 1)))) + (func (export "i8x16.avgr_u-i8x16.avgr_u") (param v128 v128 v128) (result v128) (i8x16.avgr_u (i8x16.avgr_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.avgr_u-i8x16.max_u") (param v128 v128 v128) (result v128) (i8x16.avgr_u (i8x16.max_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.avgr_u-i8x16.max_s") (param v128 v128 v128) (result v128) (i8x16.avgr_u (i8x16.max_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.avgr_u-i8x16.min_u") (param v128 v128 v128) (result v128) (i8x16.avgr_u (i8x16.min_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.avgr_u-i8x16.min_s") (param v128 v128 v128) (result v128) (i8x16.avgr_u (i8x16.min_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "i8x16.avgr_u-i8x16.abs") (param v128 v128) (result v128) (i8x16.avgr_u (i8x16.abs (local.get 0))(local.get 1))) + (func (export "i8x16.abs-i8x16.avgr_u") (param v128 v128) (result v128) (i8x16.abs (i8x16.avgr_u (local.get 0) (local.get 1)))) + (func (export "i8x16.avgr_u-i8x16.popcnt") (param v128 v128) (result v128) (i8x16.avgr_u (i8x16.popcnt (local.get 0))(local.get 1))) + (func (export "i8x16.popcnt-i8x16.avgr_u") (param v128 v128) (result v128) (i8x16.popcnt (i8x16.avgr_u (local.get 0) (local.get 1)))) + (func (export "i8x16.abs-i8x16.popcnt") (param v128) (result v128) (i8x16.abs (i8x16.popcnt (local.get 0)))) + (func (export "i8x16.abs-i8x16.abs") (param v128) (result v128) (i8x16.abs (i8x16.abs (local.get 0)))) + (func (export "i8x16.popcnt-i8x16.popcnt") (param v128) (result v128) (i8x16.popcnt (i8x16.popcnt (local.get 0)))) + (func (export "i8x16.popcnt-i8x16.abs") (param v128) (result v128) (i8x16.popcnt (i8x16.abs (local.get 0)))) +) + +(assert_return (invoke "i8x16.min_s-i8x16.avgr_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.min_s-i8x16.max_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.min_s-i8x16.max_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.min_s-i8x16.min_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.min_s-i8x16.min_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.min_s-i8x16.abs" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.abs-i8x16.min_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.min_s-i8x16.popcnt" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.popcnt-i8x16.min_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8)) +(assert_return (invoke "i8x16.min_u-i8x16.avgr_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.min_u-i8x16.max_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.min_u-i8x16.max_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.min_u-i8x16.min_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.min_u-i8x16.min_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.min_u-i8x16.abs" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.abs-i8x16.min_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.min_u-i8x16.popcnt" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.popcnt-i8x16.min_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.max_s-i8x16.avgr_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.max_s-i8x16.max_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.max_s-i8x16.max_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.max_s-i8x16.min_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.max_s-i8x16.min_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.max_s-i8x16.abs" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.abs-i8x16.max_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.max_s-i8x16.popcnt" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8)) +(assert_return (invoke "i8x16.popcnt-i8x16.max_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.max_u-i8x16.avgr_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.max_u-i8x16.max_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.max_u-i8x16.max_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.max_u-i8x16.min_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.max_u-i8x16.min_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.max_u-i8x16.abs" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.abs-i8x16.max_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.max_u-i8x16.popcnt" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8)) +(assert_return (invoke "i8x16.popcnt-i8x16.max_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8)) +(assert_return (invoke "i8x16.avgr_u-i8x16.avgr_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.avgr_u-i8x16.max_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.avgr_u-i8x16.max_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.avgr_u-i8x16.min_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.avgr_u-i8x16.min_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.avgr_u-i8x16.abs" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.abs-i8x16.avgr_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.avgr_u-i8x16.popcnt" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4)) +(assert_return (invoke "i8x16.popcnt-i8x16.avgr_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.abs-i8x16.popcnt" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8)) +(assert_return (invoke "i8x16.abs-i8x16.abs" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.popcnt-i8x16.popcnt" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.popcnt-i8x16.abs" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i8x16_cmp.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i8x16_cmp.wast new file mode 100644 index 000000000..8683accea --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i8x16_cmp.wast @@ -0,0 +1,1847 @@ + +;; Test all the i8x16 comparison operators on major boundary values and all special values. + +(module + (func (export "eq") (param $x v128) (param $y v128) (result v128) (i8x16.eq (local.get $x) (local.get $y))) + (func (export "ne") (param $x v128) (param $y v128) (result v128) (i8x16.ne (local.get $x) (local.get $y))) + (func (export "lt_s") (param $x v128) (param $y v128) (result v128) (i8x16.lt_s (local.get $x) (local.get $y))) + (func (export "lt_u") (param $x v128) (param $y v128) (result v128) (i8x16.lt_u (local.get $x) (local.get $y))) + (func (export "le_s") (param $x v128) (param $y v128) (result v128) (i8x16.le_s (local.get $x) (local.get $y))) + (func (export "le_u") (param $x v128) (param $y v128) (result v128) (i8x16.le_u (local.get $x) (local.get $y))) + (func (export "gt_s") (param $x v128) (param $y v128) (result v128) (i8x16.gt_s (local.get $x) (local.get $y))) + (func (export "gt_u") (param $x v128) (param $y v128) (result v128) (i8x16.gt_u (local.get $x) (local.get $y))) + (func (export "ge_s") (param $x v128) (param $y v128) (result v128) (i8x16.ge_s (local.get $x) (local.get $y))) + (func (export "ge_u") (param $x v128) (param $y v128) (result v128) (i8x16.ge_u (local.get $x) (local.get $y))) +) + + +;; eq + +;; i8x16.eq (i8x16) (i8x16) + +;; hex vs hex +(assert_return (invoke "eq" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs dec +(assert_return (invoke "eq" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; dec vs dec +(assert_return (invoke "eq" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0) + (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 -0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "eq" (v128.const i8x16 0x00 0x00 0x00 0xc3 0x00 0x00 0xfe 0xc2 0x00 0x00 0x80 0xbf 0x00 0x00 0x00 0x00) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0x00 0x00 0x80 0x3f 0x00 0x00 0xfe 0x42 0x00 0x00 0x00 0x43 0x00 0x00 0x7f 0x43) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; not equal +(assert_return (invoke "eq" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "eq" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "eq" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0xFF 0xAB 0xAA 0x1B 0x1A 0x0B 0x0A 0x12 0x11 0x10 0x09 0x04 0x03 0x02 0x01 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "eq" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 255 254 253 128 127 2 1 0 0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 0 0 0 0 0 0 0 -1 -1 0 0 0 0 0 0 0)) +(assert_return (invoke "eq" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 255 254 253 128 127 2 1 0 -0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 0 0 0 0 0 0 0 -1 -1 0 0 0 0 0 0 0)) + +;; i8x16.eq (i8x16) (i16x8) +(assert_return (invoke "eq" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i8x16 -1 0 -1 0 -1 -1 -1 -1 -1 0 -1 0 -1 0 -1 0)) +(assert_return (invoke "eq" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i16x8 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; i8x16.eq (i8x16) (i32x4) +(assert_return (invoke "eq" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "eq" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i8x16 -1 0 0 0 -1 -1 -1 -1 -1 0 0 0 -1 0 0 0)) +(assert_return (invoke "eq" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; ne + +;; i8x16.ne (i8x16) (i8x16) + +;; hex vs hex +(assert_return (invoke "ne" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; hex vs dec +(assert_return (invoke "ne" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; dec vs dec +(assert_return (invoke "ne" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0) + (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 -0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; hex vs float +(assert_return (invoke "ne" (v128.const i8x16 0x00 0x00 0x00 0xc3 0x00 0x00 0xfe 0xc2 0x00 0x00 0x80 0xbf 0x00 0x00 0x00 0x00) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0x00 0x00 0x80 0x3f 0x00 0x00 0xfe 0x42 0x00 0x00 0x00 0x43 0x00 0x00 0x7f 0x43) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; not equal +(assert_return (invoke "ne" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0xFF 0xAB 0xAA 0x1B 0x1A 0x0B 0x0A 0x12 0x11 0x10 0x09 0x04 0x03 0x02 0x01 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 255 254 253 128 127 2 1 0 0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 0 0 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 255 254 253 128 127 2 1 0 -0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 0 0 -1 -1 -1 -1 -1 -1 -1)) + +;; i8x16.ne (i8x16) (i16x8) +(assert_return (invoke "ne" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i8x16 0 -1 0 -1 0 0 0 0 0 -1 0 -1 0 -1 0 -1)) +(assert_return (invoke "ne" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i16x8 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; i8x16.ne (i8x16) (i32x4) +(assert_return (invoke "ne" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ne" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i8x16 0 -1 -1 -1 0 0 0 0 0 -1 -1 -1 0 -1 -1 -1)) +(assert_return (invoke "ne" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; lt_s + +;; i8x16.lt_s (i8x16) (i8x16) + +;; hex vs hex +(assert_return (invoke "lt_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; hex vs dec +(assert_return (invoke "lt_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; dec vs dec +(assert_return (invoke "lt_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0) + (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 -0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; hex vs float +(assert_return (invoke "lt_s" (v128.const i8x16 0x00 0x00 0x00 0xc3 0x00 0x00 0xfe 0xc2 0x00 0x00 0x80 0xbf 0x00 0x00 0x00 0x00) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0x00 0x00 0x80 0x3f 0x00 0x00 0xfe 0x42 0x00 0x00 0x00 0x43 0x00 0x00 0x7f 0x43) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; not equal +(assert_return (invoke "lt_s" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "lt_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0xFF 0xAB 0xAA 0x1B 0x1A 0x0B 0x0A 0x12 0x11 0x10 0x09 0x04 0x03 0x02 0x01 0x00)) + (v128.const i8x16 0 0 0 -1 -1 -1 0 -1 0 -1 0 0 0 -1 -1 -1)) +(assert_return (invoke "lt_s" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 255 254 253 128 127 2 1 0 0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 -1 -1 -1 0 -1 -1 -1 0 0 0 0 0 -1 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 255 254 253 128 127 2 1 0 -0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 -1 -1 -1 0 -1 -1 -1 0 0 0 0 0 -1 0 0 0)) + +;; i8x16.lt_s (i8x16) (i16x8) +(assert_return (invoke "lt_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i8x16 0 -1 0 -1 0 0 0 0 0 0 0 0 0 -1 0 -1)) +(assert_return (invoke "lt_s" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i16x8 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; i8x16.lt_s (i8x16) (i32x4) +(assert_return (invoke "lt_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_s" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i8x16 0 -1 -1 -1 0 0 0 0 0 0 0 0 0 -1 -1 -1)) +(assert_return (invoke "lt_s" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; lt_u + +;; i8x16.lt_u (i8x16) (i8x16) + +;; hex vs hex +(assert_return (invoke "lt_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; hex vs dec +(assert_return (invoke "lt_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; dec vs dec +(assert_return (invoke "lt_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0) + (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 -0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; hex vs float +(assert_return (invoke "lt_u" (v128.const i8x16 0x00 0x00 0x00 0xc3 0x00 0x00 0xfe 0xc2 0x00 0x00 0x80 0xbf 0x00 0x00 0x00 0x00) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0x00 0x00 0x80 0x3f 0x00 0x00 0xfe 0x42 0x00 0x00 0x00 0x43 0x00 0x00 0x7f 0x43) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; not equal +(assert_return (invoke "lt_u" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "lt_u" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0xFF 0xAB 0xAA 0x1B 0x1A 0x0B 0x0A 0x12 0x11 0x10 0x09 0x04 0x03 0x02 0x01 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 0 -1 0 -1 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 255 254 253 128 127 2 1 0 0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 -1 -1 -1 0 0 0 0 0 0 -1 -1 -1 -1 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 255 254 253 128 127 2 1 0 -0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 -1 -1 -1 0 0 0 0 0 0 -1 -1 -1 -1 0 0 0)) + +;; i8x16.lt_u (i8x16) (i16x8) +(assert_return (invoke "lt_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i8x16 0 -1 0 -1 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i16x8 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; i8x16.lt_u (i8x16) (i32x4) +(assert_return (invoke "lt_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i8x16 0 -1 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "lt_u" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; le_s + +;; i8x16.le_s (i8x16) (i8x16) + +;; hex vs hex +(assert_return (invoke "le_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs dec +(assert_return (invoke "le_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; dec vs dec +(assert_return (invoke "le_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0) + (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 -0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "le_s" (v128.const i8x16 0x00 0x00 0x00 0xc3 0x00 0x00 0xfe 0xc2 0x00 0x00 0x80 0xbf 0x00 0x00 0x00 0x00) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0x00 0x00 0x80 0x3f 0x00 0x00 0xfe 0x42 0x00 0x00 0x00 0x43 0x00 0x00 0x7f 0x43) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; not equal +(assert_return (invoke "le_s" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "le_s" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0xFF 0xAB 0xAA 0x1B 0x1A 0x0B 0x0A 0x12 0x11 0x10 0x09 0x04 0x03 0x02 0x01 0x00)) + (v128.const i8x16 0 0 0 -1 -1 -1 0 -1 0 -1 0 0 0 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 255 254 253 128 127 2 1 0 0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 -1 -1 -1 0 -1 -1 -1 -1 -1 0 0 0 -1 0 0 0)) +(assert_return (invoke "le_s" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 255 254 253 128 127 2 1 0 -0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 -1 -1 -1 0 -1 -1 -1 -1 -1 0 0 0 -1 0 0 0)) + +;; i8x16.le_s (i8x16) (i16x8) +(assert_return (invoke "le_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 -1 0 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i16x8 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; i8x16.le_s (i8x16) (i32x4) +(assert_return (invoke "le_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 -1 -1 -1 -1)) +(assert_return (invoke "le_s" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; le_u + +;; i8x16.le_u (i8x16) (i8x16) + +;; hex vs hex +(assert_return (invoke "le_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs dec +(assert_return (invoke "le_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; dec vs dec +(assert_return (invoke "le_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0) + (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 -0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "le_u" (v128.const i8x16 0x00 0x00 0x00 0xc3 0x00 0x00 0xfe 0xc2 0x00 0x00 0x80 0xbf 0x00 0x00 0x00 0x00) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0x00 0x00 0x80 0x3f 0x00 0x00 0xfe 0x42 0x00 0x00 0x00 0x43 0x00 0x00 0x7f 0x43) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; not equal +(assert_return (invoke "le_u" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0)) +(assert_return (invoke "le_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0xFF 0xAB 0xAA 0x1B 0x1A 0x0B 0x0A 0x12 0x11 0x10 0x09 0x04 0x03 0x02 0x01 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 0 -1 0 -1 0 0 0 0 0 0)) +(assert_return (invoke "le_u" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 255 254 253 128 127 2 1 0 0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 -1 -1 -1 0 0 0 0 -1 -1 -1 -1 -1 -1 0 0 0)) +(assert_return (invoke "le_u" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 255 254 253 128 127 2 1 0 -0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 -1 -1 -1 0 0 0 0 -1 -1 -1 -1 -1 -1 0 0 0)) + +;; i8x16.le_u (i8x16) (i16x8) +(assert_return (invoke "le_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 -1 0 -1 0 -1 0)) +(assert_return (invoke "le_u" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i16x8 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; i8x16.le_u (i8x16) (i32x4) +(assert_return (invoke "le_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "le_u" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 -1 0 0 0)) +(assert_return (invoke "le_u" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; gt_s + +;; i8x16.gt_s (i8x16) (i8x16) + +;; hex vs hex +(assert_return (invoke "gt_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; hex vs dec +(assert_return (invoke "gt_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; dec vs dec +(assert_return (invoke "gt_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0) + (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 -0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; hex vs float +(assert_return (invoke "gt_s" (v128.const i8x16 0x00 0x00 0x00 0xc3 0x00 0x00 0xfe 0xc2 0x00 0x00 0x80 0xbf 0x00 0x00 0x00 0x00) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0x00 0x00 0x80 0x3f 0x00 0x00 0xfe 0x42 0x00 0x00 0x00 0x43 0x00 0x00 0x7f 0x43) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; not equal +(assert_return (invoke "gt_s" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "gt_s" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0xFF 0xAB 0xAA 0x1B 0x1A 0x0B 0x0A 0x12 0x11 0x10 0x09 0x04 0x03 0x02 0x01 0x00)) + (v128.const i8x16 -1 -1 -1 0 0 0 -1 0 -1 0 -1 -1 -1 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 255 254 253 128 127 2 1 0 0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 0 0 0 -1 0 0 0 0 0 -1 -1 -1 0 -1 -1 -1)) +(assert_return (invoke "gt_s" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 255 254 253 128 127 2 1 0 -0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 0 0 0 -1 0 0 0 0 0 -1 -1 -1 0 -1 -1 -1)) + +;; i8x16.gt_s (i8x16) (i16x8) +(assert_return (invoke "gt_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 -1 0 -1 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i16x8 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; i8x16.gt_s (i8x16) (i32x4) +(assert_return (invoke "gt_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 -1 -1 -1 0 0 0 0)) +(assert_return (invoke "gt_s" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; gt_u + +;; i8x16.gt_u (i8x16) (i8x16) + +;; hex vs hex +(assert_return (invoke "gt_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; hex vs dec +(assert_return (invoke "gt_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; dec vs dec +(assert_return (invoke "gt_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0) + (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 -0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; hex vs float +(assert_return (invoke "gt_u" (v128.const i8x16 0x00 0x00 0x00 0xc3 0x00 0x00 0xfe 0xc2 0x00 0x00 0x80 0xbf 0x00 0x00 0x00 0x00) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0x00 0x00 0x80 0x3f 0x00 0x00 0xfe 0x42 0x00 0x00 0x00 0x43 0x00 0x00 0x7f 0x43) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; not equal +(assert_return (invoke "gt_u" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "gt_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0xFF 0xAB 0xAA 0x1B 0x1A 0x0B 0x0A 0x12 0x11 0x10 0x09 0x04 0x03 0x02 0x01 0x00)) + (v128.const i8x16 0 0 0 0 0 0 -1 0 -1 0 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "gt_u" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 255 254 253 128 127 2 1 0 0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 0 0 0 -1 -1 -1 -1 0 0 0 0 0 0 -1 -1 -1)) +(assert_return (invoke "gt_u" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 255 254 253 128 127 2 1 0 -0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 0 0 0 -1 -1 -1 -1 0 0 0 0 0 0 -1 -1 -1)) + +;; i8x16.gt_u (i8x16) (i16x8) +(assert_return (invoke "gt_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 -1 0 -1 0 -1 0 -1)) +(assert_return (invoke "gt_u" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i16x8 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; i8x16.gt_u (i8x16) (i32x4) +(assert_return (invoke "gt_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "gt_u" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 -1 -1 -1 0 -1 -1 -1)) +(assert_return (invoke "gt_u" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; ge_s + +;; i8x16.ge_s (i8x16) (i8x16) + +;; hex vs hex +(assert_return (invoke "ge_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs dec +(assert_return (invoke "ge_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; dec vs dec +(assert_return (invoke "ge_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0) + (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 -0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "ge_s" (v128.const i8x16 0x00 0x00 0x00 0xc3 0x00 0x00 0xfe 0xc2 0x00 0x00 0x80 0xbf 0x00 0x00 0x00 0x00) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0x00 0x00 0x80 0x3f 0x00 0x00 0xfe 0x42 0x00 0x00 0x00 0x43 0x00 0x00 0x7f 0x43) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; not equal +(assert_return (invoke "ge_s" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ge_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0xFF 0xAB 0xAA 0x1B 0x1A 0x0B 0x0A 0x12 0x11 0x10 0x09 0x04 0x03 0x02 0x01 0x00)) + (v128.const i8x16 -1 -1 -1 0 0 0 -1 0 -1 0 -1 -1 -1 0 0 0)) +(assert_return (invoke "ge_s" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 255 254 253 128 127 2 1 0 0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 0 0 0 -1 0 0 0 -1 -1 -1 -1 -1 0 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 255 254 253 128 127 2 1 0 -0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 0 0 0 -1 0 0 0 -1 -1 -1 -1 -1 0 -1 -1 -1)) + +;; i8x16.ge_s (i8x16) (i16x8) +(assert_return (invoke "ge_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i8x16 -1 0 -1 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 -1 0)) +(assert_return (invoke "ge_s" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i16x8 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; i8x16.ge_s (i8x16) (i32x4) +(assert_return (invoke "ge_s" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_s" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i8x16 -1 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0)) +(assert_return (invoke "ge_s" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; ge_u + +;; i8x16.ge_u (i8x16) (i8x16) + +;; hex vs hex +(assert_return (invoke "ge_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs dec +(assert_return (invoke "ge_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; dec vs dec +(assert_return (invoke "ge_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0) + (v128.const i8x16 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 -0 0 1 2 127 128 253 254 255)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; hex vs float +(assert_return (invoke "ge_u" (v128.const i8x16 0x00 0x00 0x00 0xc3 0x00 0x00 0xfe 0xc2 0x00 0x00 0x80 0xbf 0x00 0x00 0x00 0x00) + (v128.const f32x4 -128.0 -127.0 -1.0 0.0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0x00 0x00 0x80 0x3f 0x00 0x00 0xfe 0x42 0x00 0x00 0x00 0x43 0x00 0x00 0x7f 0x43) + (v128.const f32x4 1.0 127.0 128.0 255.0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + +;; not equal +(assert_return (invoke "ge_u" (v128.const i8x16 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F 0x0F) + (v128.const i8x16 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0 0xF0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "ge_u" (v128.const i8x16 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00)) + (v128.const i8x16 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x09 0x10 0x11 0x12 0x0A 0x0B 0x1A 0x1B 0xAA 0xAB 0xFF) + (v128.const i8x16 0xFF 0xAB 0xAA 0x1B 0x1A 0x0B 0x0A 0x12 0x11 0x10 0x09 0x04 0x03 0x02 0x01 0x00)) + (v128.const i8x16 0 0 0 0 0 0 -1 0 -1 0 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0x80 0x81 0x82 0x83 0xFD 0xFE 0xFF 0x00 0x00 0x01 0x02 0x7F 0x80 0xFD 0xFE 0xFF) + (v128.const i8x16 255 254 253 128 127 2 1 0 0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 0 0 0 -1 -1 -1 -1 -1 -1 0 0 0 0 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 128 129 130 131 253 254 255 -0 0 1 2 127 128 253 254 255) + (v128.const i8x16 255 254 253 128 127 2 1 0 -0 -1 -2 -3 -125 -126 -127 -128)) + (v128.const i8x16 0 0 0 -1 -1 -1 -1 -1 -1 0 0 0 0 -1 -1 -1)) + +;; i8x16.ge_u (i8x16) (i16x8) +(assert_return (invoke "ge_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i16x8 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i16x8 65535 65535 65535 65535 65535 65535 65535 65535)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0B0A 0x0D0C 0x0F0E)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i16x8 33152 33666 65277 255 256 32514 64896 65534)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i16x8 -128 -128 0 0 1 1 255 255)) + (v128.const i8x16 -1 0 -1 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i16x8 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA 0xAAAA)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; i8x16.ge_u (i8x16) (i32x4) +(assert_return (invoke "ge_u" (v128.const i8x16 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF) + (v128.const i32x4 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i32x4 4294967295 4294967295 4294967295 4294967295)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F) + (v128.const i32x4 0x03020100 0x07060504 0x0B0A0908 0x0F0E0D0C)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 -128 -127 -126 -125 -3 -2 -1 0 0 1 2 127 128 253 254 255) + (v128.const i32x4 2206368128 16776957 2130837760 4294901120)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 1 1 1 1 255 255 255 255) + (v128.const i32x4 -128 0 1 255)) + (v128.const i8x16 -1 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "ge_u" (v128.const i8x16 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55) + (v128.const i32x4 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA 0xAAAAAAAA)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + + +;; Type check + +(assert_invalid (module (func (result v128) (i8x16.eq (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.ge_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.ge_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.gt_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.gt_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.le_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.le_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.lt_s (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.lt_u (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.ne (i32.const 0) (f32.const 0)))) "type mismatch") + + +;; combination + +(module (memory 1) + (func (export "eq-in-block") + (block + (drop + (block (result v128) + (i8x16.eq + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "ne-in-block") + (block + (drop + (block (result v128) + (i8x16.ne + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "lt_s-in-block") + (block + (drop + (block (result v128) + (i8x16.lt_s + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "le_u-in-block") + (block + (drop + (block (result v128) + (i8x16.le_u + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "gt_u-in-block") + (block + (drop + (block (result v128) + (i8x16.gt_u + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "ge_s-in-block") + (block + (drop + (block (result v128) + (i8x16.ge_s + (block (result v128) (v128.load (i32.const 0))) + (block (result v128) (v128.load (i32.const 1))) + ) + ) + ) + ) + ) + (func (export "nested-eq") + (drop + (i8x16.eq + (i8x16.eq + (i8x16.eq + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i8x16.eq + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i8x16.eq + (i8x16.eq + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i8x16.eq + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-ne") + (drop + (i8x16.ne + (i8x16.ne + (i8x16.ne + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i8x16.ne + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i8x16.ne + (i8x16.ne + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i8x16.ne + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-lt_s") + (drop + (i8x16.lt_s + (i8x16.lt_s + (i8x16.lt_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i8x16.lt_s + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i8x16.lt_s + (i8x16.lt_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i8x16.lt_s + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-le_u") + (drop + (i8x16.le_u + (i8x16.le_u + (i8x16.le_u + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i8x16.le_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i8x16.le_u + (i8x16.le_u + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i8x16.le_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-gt_u") + (drop + (i8x16.gt_u + (i8x16.gt_u + (i8x16.gt_u + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i8x16.gt_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i8x16.gt_u + (i8x16.gt_u + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i8x16.gt_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "nested-ge_s") + (drop + (i8x16.ge_s + (i8x16.ge_s + (i8x16.ge_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i8x16.ge_s + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i8x16.ge_s + (i8x16.ge_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i8x16.ge_s + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) + (func (export "as-param") + (drop + (i8x16.ge_u + (i8x16.eq + (i8x16.lt_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i8x16.le_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + (i8x16.ne + (i8x16.gt_s + (v128.load (i32.const 0)) + (v128.load (i32.const 1)) + ) + (i8x16.lt_u + (v128.load (i32.const 2)) + (v128.load (i32.const 3)) + ) + ) + ) + ) + ) +) +(assert_return (invoke "eq-in-block")) +(assert_return (invoke "ne-in-block")) +(assert_return (invoke "lt_s-in-block")) +(assert_return (invoke "le_u-in-block")) +(assert_return (invoke "gt_u-in-block")) +(assert_return (invoke "ge_s-in-block")) +(assert_return (invoke "nested-eq")) +(assert_return (invoke "nested-ne")) +(assert_return (invoke "nested-lt_s")) +(assert_return (invoke "nested-le_u")) +(assert_return (invoke "nested-gt_u")) +(assert_return (invoke "nested-ge_s")) +(assert_return (invoke "as-param")) + + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i8x16.eq-1st-arg-empty (result v128) + (i8x16.eq (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.eq-arg-empty (result v128) + (i8x16.eq) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.ne-1st-arg-empty (result v128) + (i8x16.ne (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.ne-arg-empty (result v128) + (i8x16.ne) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.lt_s-1st-arg-empty (result v128) + (i8x16.lt_s (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.lt_s-arg-empty (result v128) + (i8x16.lt_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.lt_u-1st-arg-empty (result v128) + (i8x16.lt_u (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.lt_u-arg-empty (result v128) + (i8x16.lt_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.le_s-1st-arg-empty (result v128) + (i8x16.le_s (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.le_s-arg-empty (result v128) + (i8x16.le_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.le_u-1st-arg-empty (result v128) + (i8x16.le_u (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.le_u-arg-empty (result v128) + (i8x16.le_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.gt_s-1st-arg-empty (result v128) + (i8x16.gt_s (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.gt_s-arg-empty (result v128) + (i8x16.gt_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.gt_u-1st-arg-empty (result v128) + (i8x16.gt_u (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.gt_u-arg-empty (result v128) + (i8x16.gt_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.ge_s-1st-arg-empty (result v128) + (i8x16.ge_s (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.ge_s-arg-empty (result v128) + (i8x16.ge_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.ge_u-1st-arg-empty (result v128) + (i8x16.ge_u (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.ge_u-arg-empty (result v128) + (i8x16.ge_u) + ) + ) + "type mismatch" +) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i8x16_sat_arith.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i8x16_sat_arith.wast new file mode 100644 index 000000000..a799a8b73 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_i8x16_sat_arith.wast @@ -0,0 +1,718 @@ +;; Tests for i8x16 arithmetic operations on major boundary values and all special values. + + +(module + (func (export "i8x16.add_sat_s") (param v128 v128) (result v128) (i8x16.add_sat_s (local.get 0) (local.get 1))) + (func (export "i8x16.add_sat_u") (param v128 v128) (result v128) (i8x16.add_sat_u (local.get 0) (local.get 1))) + (func (export "i8x16.sub_sat_s") (param v128 v128) (result v128) (i8x16.sub_sat_s (local.get 0) (local.get 1))) + (func (export "i8x16.sub_sat_u") (param v128 v128) (result v128) (i8x16.sub_sat_u (local.get 0) (local.get 1))) +) + + +;; i8x16.add_sat_s +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f) + (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40) + (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f) + (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40)) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40) + (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40) + (v128.const i8x16 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i8x16 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i8x16 0x01 0x01 0x81 0x7f 0x01 0x01 0x81 0x7f 0x01 0x01 0x81 0x7f 0x01 0x01 0x81 0x7f)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i8x16 0x01 0x01 0x81 0 0x01 0x01 0x81 0 0x01 0x01 0x81 0 0x01 0x01 0x81 0)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i8x16 0x01 0x01 0xc1 0x7f 0x01 0x01 0xc1 0x7f 0x01 0x01 0xc1 0x7f 0x01 0x01 0xc1 0x7f)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i8x16 0x01 0x01 0xc1 0 0x01 0x01 0xc1 0 0x01 0x01 0xc1 0 0x01 0x01 0xc1 0)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 0xff 0xfe 0xfd 0xfc 0xfb 0xfa 0xf9 0xf8 0xf7 0xf6 0xf5 0xf4 0xf3 0xf2 0xf1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.add_sat_s" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30)) + (v128.const i8x16 0 3 6 9 12 15 18 21 24 27 30 33 36 39 42 45)) + +;; i8x16.add_sat_u +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 129 129 129 129 129 129 129 129 129 129 129 129 129 129 129 129)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 254 254 254 254 254 254 254 254 254 254 254 254 254 254 254 254)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f) + (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40) + (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f) + (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40) + (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40) + (v128.const i8x16 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 254 254 254 254 254 254 254 254 254 254 254 254 254 254 254 254)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i8x16 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i8x16 0x80 0x80 0x80 0xff 0x80 0x80 0x80 0xff 0x80 0x80 0x80 0xff 0x80 0x80 0x80 0xff)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i8x16 0x01 0x01 0x81 0x80 0x01 0x01 0x81 0x80 0x01 0x01 0x81 0x80 0x01 0x01 0x81 0x80)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i8x16 0x01 0x01 0x81 0xff 0x01 0x01 0x81 0xff 0x01 0x01 0x81 0xff 0x01 0x01 0x81 0xff)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i8x16 0x01 0x01 0xc1 0x80 0x01 0x01 0xc1 0x80 0x01 0x01 0xc1 0x80 0x01 0x01 0xc1 0x80)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i8x16 0x01 0x01 0xc1 0xff 0x01 0x01 0xc1 0xff 0x01 0x01 0xc1 0xff 0x01 0x01 0xc1 0xff)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 0xff 0xfe 0xfd 0xfc 0xfb 0xfa 0xf9 0xf8 0xf7 0xf6 0xf5 0xf4 0xf3 0xf2 0xf1)) + (v128.const i8x16 0 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) +(assert_return (invoke "i8x16.add_sat_u" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30)) + (v128.const i8x16 0 3 6 9 12 15 18 21 24 27 30 33 36 39 42 45)) + +;; i8x16.sub_sat_s +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 124 124 124 124 124 124 124 124 124 124 124 124 124 124 124 124)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -125 -125 -125 -125 -125 -125 -125 -125 -125 -125 -125 -125 -125 -125 -125 -125)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f) + (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40) + (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f) + (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40) + (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40) + (v128.const i8x16 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01)) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i8x16 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i8x16 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i8x16 0x80 0x80 0x80 0 0x80 0x80 0x80 0 0x80 0x80 0x80 0 0x80 0x80 0x80 0)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i8x16 0x01 0x01 0x7f 0x82 0x01 0x01 0x7f 0x82 0x01 0x01 0x7f 0x82 0x01 0x01 0x7f 0x82)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i8x16 0x01 0x01 0x7f 0x02 0x01 0x01 0x7f 0x02 0x01 0x01 0x7f 0x02 0x01 0x01 0x7f 0x02)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i8x16 0x01 0x01 0x41 0x82 0x01 0x01 0x41 0x82 0x01 0x01 0x41 0x82 0x01 0x01 0x41 0x82)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i8x16 0x01 0x01 0x41 0x02 0x01 0x01 0x41 0x02 0x01 0x01 0x41 0x02 0x01 0x01 0x41 0x02)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 0xff 0xfe 0xfd 0xfc 0xfb 0xfa 0xf9 0xf8 0xf7 0xf6 0xf5 0xf4 0xf3 0xf2 0xf1)) + (v128.const i8x16 0 0x02 0x04 0x06 0x08 0x0a 0x0c 0x0e 0x10 0x12 0x14 0x16 0x18 0x1a 0x1c 0x1e)) +(assert_return (invoke "i8x16.sub_sat_s" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30)) + (v128.const i8x16 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15)) + +;; i8x16.sub_sat_u +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64) + (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63 -63) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65 -65) + (v128.const i8x16 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64 -64)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 124 124 124 124 124 124 124 124 124 124 124 124 124 124 124 124)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125 125)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126 -126) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 254 254 254 254 254 254 254 254 254 254 254 254 254 254 254 254)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f) + (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40) + (v128.const i8x16 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f -0x3f) + (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40) + (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40 -0x40) + (v128.const i8x16 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41 -0x41)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i8x16 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01 -0x01)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i8x16 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01)) + (v128.const i8x16 254 254 254 254 254 254 254 254 254 254 254 254 254 254 254 254)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff) + (v128.const i8x16 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80) + (v128.const f32x4 -0.0 -0.0 -0.0 -0.0)) + (v128.const i8x16 0x80 0x80 0x80 0 0x80 0x80 0x80 0 0x80 0x80 0x80 0 0x80 0x80 0x80 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 +inf +inf +inf +inf)) + (v128.const i8x16 0x01 0x01 0 0 0x01 0x01 0 0 0x01 0x01 0 0 0x01 0x01 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 -inf -inf -inf -inf)) + (v128.const i8x16 0x01 0x01 0 0 0x01 0x01 0 0 0x01 0x01 0 0 0x01 0x01 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 nan nan nan nan)) + (v128.const i8x16 0x01 0x01 0 0 0x01 0x01 0 0 0x01 0x01 0 0 0x01 0x01 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (v128.const f32x4 -nan -nan -nan -nan)) + (v128.const i8x16 0x01 0x01 0 0 0x01 0x01 0 0 0x01 0x01 0 0 0x01 0x01 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 0xff 0xfe 0xfd 0xfc 0xfb 0xfa 0xf9 0xf8 0xf7 0xf6 0xf5 0xf4 0xf3 0xf2 0xf1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.sub_sat_u" (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +;; Malformed cases: non-existent op names +(assert_malformed (module quote + "(func (result v128) (i8x16.add_sat (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i8x16.sub_sat (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i8x16.mul_sat (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i8x16.div_sat (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i32x4.add_sat_s (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i32x4.add_sat_u (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i32x4.sub_sat_s (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (i32x4.sub_sat_u (v128.const i32x4 0 0 0 0) (v128.const i32x4 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (f32x4.add_sat_s (v128.const f32x4 0 0 0 0) (v128.const f32x4 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (f32x4.add_sat_u (v128.const f32x4 0 0 0 0) (v128.const f32x4 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (f32x4.sub_sat_s (v128.const f32x4 0 0 0 0) (v128.const f32x4 0 0 0 0)))") + "unknown operator") +(assert_malformed (module quote + "(func (result v128) (f32x4.sub_sat_u (v128.const f32x4 0 0 0 0) (v128.const f32x4 0 0 0 0)))") + "unknown operator") + +;; type check +(assert_invalid (module (func (result v128) (i8x16.add_sat_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.add_sat_u (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.sub_sat_s (i32.const 0) (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.sub_sat_u (i32.const 0) (f32.const 0.0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i8x16.add_sat_s-1st-arg-empty (result v128) + (i8x16.add_sat_s (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.add_sat_s-arg-empty (result v128) + (i8x16.add_sat_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.add_sat_u-1st-arg-empty (result v128) + (i8x16.add_sat_u (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.add_sat_u-arg-empty (result v128) + (i8x16.add_sat_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.sub_sat_s-1st-arg-empty (result v128) + (i8x16.sub_sat_s (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.sub_sat_s-arg-empty (result v128) + (i8x16.sub_sat_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.sub_sat_u-1st-arg-empty (result v128) + (i8x16.sub_sat_u (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.sub_sat_u-arg-empty (result v128) + (i8x16.sub_sat_u) + ) + ) + "type mismatch" +) + +;; combination +(module + (func (export "sat-add_s-sub_s") (param v128 v128 v128) (result v128) + (i8x16.add_sat_s (i8x16.sub_sat_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "sat-add_s-sub_u") (param v128 v128 v128) (result v128) + (i8x16.add_sat_s (i8x16.sub_sat_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "sat-add_u-sub_s") (param v128 v128 v128) (result v128) + (i8x16.add_sat_u (i8x16.sub_sat_s (local.get 0) (local.get 1))(local.get 2))) + (func (export "sat-add_u-sub_u") (param v128 v128 v128) (result v128) + (i8x16.add_sat_u (i8x16.sub_sat_u (local.get 0) (local.get 1))(local.get 2))) + (func (export "sat-add_s-neg") (param v128 v128) (result v128) + (i8x16.add_sat_s (i8x16.neg (local.get 0)) (local.get 1))) + (func (export "sat-add_u-neg") (param v128 v128) (result v128) + (i8x16.add_sat_u (i8x16.neg (local.get 0)) (local.get 1))) + (func (export "sat-sub_s-neg") (param v128 v128) (result v128) + (i8x16.sub_sat_s (i8x16.neg (local.get 0)) (local.get 1))) + (func (export "sat-sub_u-neg") (param v128 v128) (result v128) + (i8x16.sub_sat_u (i8x16.neg (local.get 0)) (local.get 1))) +) + +(assert_return (invoke "sat-add_s-sub_s" (v128.const i8x16 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "sat-add_s-sub_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "sat-add_u-sub_s" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 254 254 254 254 254 254 254 254 254 254 254 254 254 254 254 254)) +(assert_return (invoke "sat-add_u-sub_u" (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "sat-add_s-neg" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "sat-add_u-neg" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255)) +(assert_return (invoke "sat-sub_s-neg" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "sat-sub_u-neg" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_int_to_int_extend.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_int_to_int_extend.wast new file mode 100644 index 000000000..be8d554ca --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_int_to_int_extend.wast @@ -0,0 +1,599 @@ +;; Tests for int-to-int extension operations. + +(module + (func (export "i16x8.extend_high_i8x16_s") (param v128) (result v128) (i16x8.extend_high_i8x16_s (local.get 0))) + (func (export "i16x8.extend_high_i8x16_u") (param v128) (result v128) (i16x8.extend_high_i8x16_u (local.get 0))) + (func (export "i16x8.extend_low_i8x16_s") (param v128) (result v128) (i16x8.extend_low_i8x16_s (local.get 0))) + (func (export "i16x8.extend_low_i8x16_u") (param v128) (result v128) (i16x8.extend_low_i8x16_u (local.get 0))) + (func (export "i32x4.extend_high_i16x8_s") (param v128) (result v128) (i32x4.extend_high_i16x8_s (local.get 0))) + (func (export "i32x4.extend_high_i16x8_u") (param v128) (result v128) (i32x4.extend_high_i16x8_u (local.get 0))) + (func (export "i32x4.extend_low_i16x8_s") (param v128) (result v128) (i32x4.extend_low_i16x8_s (local.get 0))) + (func (export "i32x4.extend_low_i16x8_u") (param v128) (result v128) (i32x4.extend_low_i16x8_u (local.get 0))) + (func (export "i64x2.extend_high_i32x4_s") (param v128) (result v128) (i64x2.extend_high_i32x4_s (local.get 0))) + (func (export "i64x2.extend_high_i32x4_u") (param v128) (result v128) (i64x2.extend_high_i32x4_u (local.get 0))) + (func (export "i64x2.extend_low_i32x4_s") (param v128) (result v128) (i64x2.extend_low_i32x4_s (local.get 0))) + (func (export "i64x2.extend_low_i32x4_u") (param v128) (result v128) (i64x2.extend_low_i32x4_u (local.get 0))) +) + +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 1 1 1 1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 126 126 126 126 126 126 126 126 127 127 127 127 127 127 127 127)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 127 127 127 127 127 127 127 127 126 126 126 126 126 126 126 126)) + (v128.const i16x8 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 127 127 127 127 127 127 127 127 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 127 127 127 127 127 127 127 127)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 127 127 127 127 127 127 127 127 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 127 127 127 127 127 127 127 127)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i16x8 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.extend_high_i8x16_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 -128 -128 -128 -128 -128 -128 -128 -128)) + +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 1 1 1 1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 126 126 126 126 126 126 126 126 127 127 127 127 127 127 127 127)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 127 127 127 127 127 127 127 127 126 126 126 126 126 126 126 126)) + (v128.const i16x8 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 127 127 127 127 127 127 127 127 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 127 127 127 127 127 127 127 127)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 127 127 127 127 127 127 127 127 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 127 127 127 127 127 127 127 127)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i16x8 129 129 129 129 129 129 129 129)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i16x8.extend_high_i8x16_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) + +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 1 1 1 1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 126 126 126 126 126 126 126 126 127 127 127 127 127 127 127 127)) + (v128.const i16x8 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 127 127 127 127 127 127 127 127 126 126 126 126 126 126 126 126)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 127 127 127 127 127 127 127 127 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 127 127 127 127 127 127 127 127)) + (v128.const i16x8 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 127 127 127 127 127 127 127 127 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 127 127 127 127 127 127 127 127)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 -127 -127 -127 -127 -127 -127 -127 -127)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i16x8 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i16x8.extend_low_i8x16_s" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) + +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 0 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 0 0 0 0 0 0 0 0)) + (v128.const i16x8 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 1 1 1 1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 1 1 1 1 1 1 1 1)) + (v128.const i16x8 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 126 126 126 126 126 126 126 126 127 127 127 127 127 127 127 127)) + (v128.const i16x8 126 126 126 126 126 126 126 126)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 127 127 127 127 127 127 127 127 126 126 126 126 126 126 126 126)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 127 127 127 127 127 127 127 127 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 127 127 127 127 127 127 127 127)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 127 127 127 127 127 127 127 127 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 127 127 127 127 127 127 127 127)) + (v128.const i16x8 255 255 255 255 255 255 255 255)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 -127 -127 -127 -127 -127 -127 -127 -127 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 129 129 129 129 129 129 129 129)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -127 -127 -127 -127 -127 -127 -127 -127)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -1 -1 -1 -1 -1 -1 -1 -1)) + (v128.const i16x8 128 128 128 128 128 128 128 128)) +(assert_return (invoke "i16x8.extend_low_i8x16_u" (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -128 -128 -128 -128 -128 -128 -128 -128)) + (v128.const i16x8 255 255 255 255 255 255 255 255)) + +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 0 0 0 0 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 0 0 0 0 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 1 1 1 1 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 -1 -1 -1 -1 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 1 1 1 1 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 -1 -1 -1 -1 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 32766 32766 32766 32766 32767 32767 32767 32767)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 32767 32767 32767 32767 32766 32766 32766 32766)) + (v128.const i32x4 32766 32766 32766 32766)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 32767 32767 32767 32767 -32768 -32768 -32768 -32768)) + (v128.const i32x4 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 32767 32767 32767 32767)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 32767 32767 32767 32767 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 -1 -1 -1 -1 32767 32767 32767 32767)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 -32767 -32767 -32767 -32767 -32768 -32768 -32768 -32768)) + (v128.const i32x4 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32767 -32767 -32767 -32767)) + (v128.const i32x4 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -1 -1 -1 -1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.extend_high_i16x8_s" (v128.const i16x8 -1 -1 -1 -1 -32768 -32768 -32768 -32768)) + (v128.const i32x4 -32768 -32768 -32768 -32768)) + +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 0 0 0 0 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 0 0 0 0 -1 -1 -1 -1)) + (v128.const i32x4 65535 65535 65535 65535)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 1 1 1 1 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 -1 -1 -1 -1 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 1 1 1 1 -1 -1 -1 -1)) + (v128.const i32x4 65535 65535 65535 65535)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 -1 -1 -1 -1 1 1 1 1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 32766 32766 32766 32766 32767 32767 32767 32767)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 32767 32767 32767 32767 32766 32766 32766 32766)) + (v128.const i32x4 32766 32766 32766 32766)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 32768 32768 32768 32768)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 32767 32767 32767 32767 -32768 -32768 -32768 -32768)) + (v128.const i32x4 32768 32768 32768 32768)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 -32768 -32768 -32768 -32768 32767 32767 32767 32767)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 32767 32767 32767 32767 -1 -1 -1 -1)) + (v128.const i32x4 65535 65535 65535 65535)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 -1 -1 -1 -1 32767 32767 32767 32767)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 -32767 -32767 -32767 -32767 -32768 -32768 -32768 -32768)) + (v128.const i32x4 32768 32768 32768 32768)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32767 -32767 -32767 -32767)) + (v128.const i32x4 32769 32769 32769 32769)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -1 -1 -1 -1)) + (v128.const i32x4 65535 65535 65535 65535)) +(assert_return (invoke "i32x4.extend_high_i16x8_u" (v128.const i16x8 -1 -1 -1 -1 -32768 -32768 -32768 -32768)) + (v128.const i32x4 32768 32768 32768 32768)) + +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 0 0 0 0 1 1 1 1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 0 0 0 0 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 1 1 1 1 0 0 0 0)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 -1 -1 -1 -1 0 0 0 0)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 1 1 1 1 -1 -1 -1 -1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 -1 -1 -1 -1 1 1 1 1)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 32766 32766 32766 32766 32767 32767 32767 32767)) + (v128.const i32x4 32766 32766 32766 32766)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 32767 32767 32767 32767 32766 32766 32766 32766)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 32767 32767 32767 32767 -32768 -32768 -32768 -32768)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 32767 32767 32767 32767)) + (v128.const i32x4 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 32767 32767 32767 32767 -1 -1 -1 -1)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 -1 -1 -1 -1 32767 32767 32767 32767)) + (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 -32767 -32767 -32767 -32767 -32768 -32768 -32768 -32768)) + (v128.const i32x4 -32767 -32767 -32767 -32767)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -32767 -32767 -32767 -32767)) + (v128.const i32x4 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 -32768 -32768 -32768 -32768 -1 -1 -1 -1)) + (v128.const i32x4 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i32x4.extend_low_i16x8_s" (v128.const i16x8 -1 -1 -1 -1 -32768 -32768 -32768 -32768)) + (v128.const i32x4 -1 -1 -1 -1)) + +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 0 0 0 0 1 1 1 1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 0 0 0 0 -1 -1 -1 -1)) + (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 1 1 1 1 0 0 0 0)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 -1 -1 -1 -1 0 0 0 0)) + (v128.const i32x4 65535 65535 65535 65535)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 1 1 1 1 -1 -1 -1 -1)) + (v128.const i32x4 1 1 1 1)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 -1 -1 -1 -1 1 1 1 1)) + (v128.const i32x4 65535 65535 65535 65535)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 32766 32766 32766 32766 32767 32767 32767 32767)) + (v128.const i32x4 32766 32766 32766 32766)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 32767 32767 32767 32767 32766 32766 32766 32766)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) + (v128.const i32x4 32768 32768 32768 32768)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 32767 32767 32767 32767 -32768 -32768 -32768 -32768)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 -32768 -32768 -32768 -32768 32767 32767 32767 32767)) + (v128.const i32x4 32768 32768 32768 32768)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 32767 32767 32767 32767 -1 -1 -1 -1)) + (v128.const i32x4 32767 32767 32767 32767)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 -1 -1 -1 -1 32767 32767 32767 32767)) + (v128.const i32x4 65535 65535 65535 65535)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 -32767 -32767 -32767 -32767 -32768 -32768 -32768 -32768)) + (v128.const i32x4 32769 32769 32769 32769)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -32767 -32767 -32767 -32767)) + (v128.const i32x4 32768 32768 32768 32768)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 -32768 -32768 -32768 -32768 -1 -1 -1 -1)) + (v128.const i32x4 32768 32768 32768 32768)) +(assert_return (invoke "i32x4.extend_low_i16x8_u" (v128.const i16x8 -1 -1 -1 -1 -32768 -32768 -32768 -32768)) + (v128.const i32x4 65535 65535 65535 65535)) + +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 0 0 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 0 0 1 1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 0 0 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 1 1 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 -1 -1 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 1 1 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 -1 -1 1 1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 2147483646 2147483646 2147483647 2147483647)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 2147483647 2147483647 2147483646 2147483646)) + (v128.const i64x2 2147483646 2147483646)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i64x2 -2147483648 -2147483648)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 2147483647 2147483647 -2147483648 -2147483648)) + (v128.const i64x2 -2147483648 -2147483648)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 2147483647 2147483647)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 2147483647 2147483647 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 -1 -1 2147483647 2147483647)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 -2147483647 -2147483647 -2147483648 -2147483648)) + (v128.const i64x2 -2147483648 -2147483648)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 -2147483647 -2147483647)) + (v128.const i64x2 -2147483647 -2147483647)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 -1 -1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.extend_high_i32x4_s" (v128.const i32x4 -1 -1 -2147483648 -2147483648)) + (v128.const i64x2 -2147483648 -2147483648)) + +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 0 0 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 0 0 1 1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 0 0 -1 -1)) + (v128.const i64x2 4294967295 4294967295)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 1 1 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 -1 -1 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 1 1 -1 -1)) + (v128.const i64x2 4294967295 4294967295)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 -1 -1 1 1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 2147483646 2147483646 2147483647 2147483647)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 2147483647 2147483647 2147483646 2147483646)) + (v128.const i64x2 2147483646 2147483646)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i64x2 2147483648 2147483648)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 2147483647 2147483647 -2147483648 -2147483648)) + (v128.const i64x2 2147483648 2147483648)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 2147483647 2147483647)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 2147483647 2147483647 -1 -1)) + (v128.const i64x2 4294967295 4294967295)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 -1 -1 2147483647 2147483647)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 -2147483647 -2147483647 -2147483648 -2147483648)) + (v128.const i64x2 2147483648 2147483648)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 -2147483647 -2147483647)) + (v128.const i64x2 2147483649 2147483649)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 -1 -1)) + (v128.const i64x2 4294967295 4294967295)) +(assert_return (invoke "i64x2.extend_high_i32x4_u" (v128.const i32x4 -1 -1 -2147483648 -2147483648)) + (v128.const i64x2 2147483648 2147483648)) + +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 0 0 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 0 0 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 0 0 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 1 1 0 0)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 -1 -1 0 0)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 1 1 -1 -1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 -1 -1 1 1)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 2147483646 2147483646 2147483647 2147483647)) + (v128.const i64x2 2147483646 2147483646)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 2147483647 2147483647 2147483646 2147483646)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i64x2 -2147483648 -2147483648)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 2147483647 2147483647 -2147483648 -2147483648)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 2147483647 2147483647)) + (v128.const i64x2 -2147483648 -2147483648)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 2147483647 2147483647 -1 -1)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 -1 -1 2147483647 2147483647)) + (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 -2147483647 -2147483647 -2147483648 -2147483648)) + (v128.const i64x2 -2147483647 -2147483647)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 -2147483647 -2147483647)) + (v128.const i64x2 -2147483648 -2147483648)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 -2147483648 -2147483648 -1 -1)) + (v128.const i64x2 -2147483648 -2147483648)) +(assert_return (invoke "i64x2.extend_low_i32x4_s" (v128.const i32x4 -1 -1 -2147483648 -2147483648)) + (v128.const i64x2 -1 -1)) + +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 0 0 0 0)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 0 0 1 1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 0 0 -1 -1)) + (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 1 1 0 0)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 -1 -1 0 0)) + (v128.const i64x2 4294967295 4294967295)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 1 1 -1 -1)) + (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 -1 -1 1 1)) + (v128.const i64x2 4294967295 4294967295)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 2147483646 2147483646 2147483647 2147483647)) + (v128.const i64x2 2147483646 2147483646)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 2147483647 2147483647 2147483646 2147483646)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 2147483647 2147483647 2147483647 2147483647)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 -2147483648 -2147483648)) + (v128.const i64x2 2147483648 2147483648)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 2147483647 2147483647 -2147483648 -2147483648)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 2147483647 2147483647)) + (v128.const i64x2 2147483648 2147483648)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 2147483647 2147483647 -1 -1)) + (v128.const i64x2 2147483647 2147483647)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 -1 -1 2147483647 2147483647)) + (v128.const i64x2 4294967295 4294967295)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 -2147483647 -2147483647 -2147483648 -2147483648)) + (v128.const i64x2 2147483649 2147483649)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 -2147483647 -2147483647)) + (v128.const i64x2 2147483648 2147483648)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 -2147483648 -2147483648 -1 -1)) + (v128.const i64x2 2147483648 2147483648)) +(assert_return (invoke "i64x2.extend_low_i32x4_u" (v128.const i32x4 -1 -1 -2147483648 -2147483648)) + (v128.const i64x2 4294967295 4294967295)) + + +;; type check +(assert_invalid (module (func (result v128) (i16x8.extend_high_i8x16_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.extend_high_i8x16_u (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.extend_low_i8x16_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.extend_low_i8x16_u (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.extend_high_i16x8_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.extend_high_i16x8_u (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.extend_low_i16x8_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.extend_low_i16x8_u (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.extend_high_i32x4_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.extend_high_i32x4_u (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.extend_low_i32x4_s (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i64x2.extend_low_i32x4_u (i32.const 0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i16x8.extend_high_i8x16_s-arg-empty (result v128) + (i16x8.extend_high_i8x16_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.extend_high_i8x16_u-arg-empty (result v128) + (i16x8.extend_high_i8x16_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.extend_low_i8x16_s-arg-empty (result v128) + (i16x8.extend_low_i8x16_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.extend_low_i8x16_u-arg-empty (result v128) + (i16x8.extend_low_i8x16_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.extend_high_i16x8_s-arg-empty (result v128) + (i32x4.extend_high_i16x8_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.extend_high_i16x8_u-arg-empty (result v128) + (i32x4.extend_high_i16x8_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.extend_low_i16x8_s-arg-empty (result v128) + (i32x4.extend_low_i16x8_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.extend_low_i16x8_u-arg-empty (result v128) + (i32x4.extend_low_i16x8_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.extend_high_i32x4_s-arg-empty (result v128) + (i64x2.extend_high_i32x4_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.extend_high_i32x4_u-arg-empty (result v128) + (i64x2.extend_high_i32x4_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.extend_low_i32x4_s-arg-empty (result v128) + (i64x2.extend_low_i32x4_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.extend_low_i32x4_u-arg-empty (result v128) + (i64x2.extend_low_i32x4_u) + ) + ) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_lane.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_lane.wast new file mode 100644 index 000000000..9d4b5fd72 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_lane.wast @@ -0,0 +1,1265 @@ +;; Tests for the extract_lane, replace_lane, swizzle and shuffle group instructions + + +(module + (func (export "i8x16_extract_lane_s-first") (param v128) (result i32) + (i8x16.extract_lane_s 0 (local.get 0))) + (func (export "i8x16_extract_lane_s-last") (param v128) (result i32) + (i8x16.extract_lane_s 15 (local.get 0))) + (func (export "i8x16_extract_lane_u-first") (param v128) (result i32) + (i8x16.extract_lane_u 0 (local.get 0))) + (func (export "i8x16_extract_lane_u-last") (param v128) (result i32) + (i8x16.extract_lane_u 15 (local.get 0))) + (func (export "i16x8_extract_lane_s-first") (param v128) (result i32) + (i16x8.extract_lane_s 0 (local.get 0))) + (func (export "i16x8_extract_lane_s-last") (param v128) (result i32) + (i16x8.extract_lane_s 7 (local.get 0))) + (func (export "i16x8_extract_lane_u-first") (param v128) (result i32) + (i16x8.extract_lane_u 0 (local.get 0))) + (func (export "i16x8_extract_lane_u-last") (param v128) (result i32) + (i16x8.extract_lane_u 7 (local.get 0))) + (func (export "i32x4_extract_lane-first") (param v128) (result i32) + (i32x4.extract_lane 0 (local.get 0))) + (func (export "i32x4_extract_lane-last") (param v128) (result i32) + (i32x4.extract_lane 3 (local.get 0))) + (func (export "f32x4_extract_lane-first") (param v128) (result f32) + (f32x4.extract_lane 0 (local.get 0))) + (func (export "f32x4_extract_lane-last") (param v128) (result f32) + (f32x4.extract_lane 3 (local.get 0))) + (func (export "i8x16_replace_lane-first") (param v128 i32) (result v128) + (i8x16.replace_lane 0 (local.get 0) (local.get 1))) + (func (export "i8x16_replace_lane-last") (param v128 i32) (result v128) + (i8x16.replace_lane 15 (local.get 0) (local.get 1))) + (func (export "i16x8_replace_lane-first") (param v128 i32) (result v128) + (i16x8.replace_lane 0 (local.get 0) (local.get 1))) + (func (export "i16x8_replace_lane-last") (param v128 i32) (result v128) + (i16x8.replace_lane 7 (local.get 0) (local.get 1))) + (func (export "i32x4_replace_lane-first") (param v128 i32) (result v128) + (i32x4.replace_lane 0 (local.get 0) (local.get 1))) + (func (export "i32x4_replace_lane-last") (param v128 i32) (result v128) + (i32x4.replace_lane 3 (local.get 0) (local.get 1))) + (func (export "f32x4_replace_lane-first") (param v128 f32) (result v128) + (f32x4.replace_lane 0 (local.get 0) (local.get 1))) + (func (export "f32x4_replace_lane-last") (param v128 f32) (result v128) + (f32x4.replace_lane 3 (local.get 0) (local.get 1))) + (func (export "i64x2_extract_lane-first") (param v128) (result i64) + (i64x2.extract_lane 0 (local.get 0))) + (func (export "i64x2_extract_lane-last") (param v128) (result i64) + (i64x2.extract_lane 1 (local.get 0))) + (func (export "f64x2_extract_lane-first") (param v128) (result f64) + (f64x2.extract_lane 0 (local.get 0))) + (func (export "f64x2_extract_lane-last") (param v128) (result f64) + (f64x2.extract_lane 1 (local.get 0))) + (func (export "i64x2_replace_lane-first") (param v128 i64) (result v128) + (i64x2.replace_lane 0 (local.get 0) (local.get 1))) + (func (export "i64x2_replace_lane-last") (param v128 i64) (result v128) + (i64x2.replace_lane 1 (local.get 0) (local.get 1))) + (func (export "f64x2_replace_lane-first") (param v128 f64) (result v128) + (f64x2.replace_lane 0 (local.get 0) (local.get 1))) + (func (export "f64x2_replace_lane-last") (param v128 f64) (result v128) + (f64x2.replace_lane 1 (local.get 0) (local.get 1))) + + ;; Swizzle and shuffle + (func (export "v8x16_swizzle") (param v128 v128) (result v128) + (i8x16.swizzle (local.get 0) (local.get 1))) + (func (export "v8x16_shuffle-1") (param v128 v128) (result v128) + (i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 (local.get 0) (local.get 1))) + (func (export "v8x16_shuffle-2") (param v128 v128) (result v128) + (i8x16.shuffle 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 (local.get 0) (local.get 1))) + (func (export "v8x16_shuffle-3") (param v128 v128) (result v128) + (i8x16.shuffle 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 (local.get 0) (local.get 1))) + (func (export "v8x16_shuffle-4") (param v128 v128) (result v128) + (i8x16.shuffle 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 (local.get 0) (local.get 1))) + (func (export "v8x16_shuffle-5") (param v128 v128) (result v128) + (i8x16.shuffle 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 (local.get 0) (local.get 1))) + (func (export "v8x16_shuffle-6") (param v128 v128) (result v128) + (i8x16.shuffle 16 16 16 16 16 16 16 16 16 16 16 16 16 16 16 16 (local.get 0) (local.get 1))) + (func (export "v8x16_shuffle-7") (param v128 v128) (result v128) + (i8x16.shuffle 0 0 0 0 0 0 0 0 16 16 16 16 16 16 16 16 (local.get 0) (local.get 1))) +) + +(assert_return (invoke "i8x16_extract_lane_s-first" (v128.const i8x16 127 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) (i32.const 127)) +(assert_return (invoke "i8x16_extract_lane_s-first" (v128.const i8x16 0x7f 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) (i32.const 127)) +(assert_return (invoke "i8x16_extract_lane_s-first" (v128.const i8x16 255 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) (i32.const -1)) +(assert_return (invoke "i8x16_extract_lane_s-first" (v128.const i8x16 0xff 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) (i32.const -1)) +(assert_return (invoke "i8x16_extract_lane_u-first" (v128.const i8x16 255 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) (i32.const 255)) +(assert_return (invoke "i8x16_extract_lane_u-first" (v128.const i8x16 0xff 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) (i32.const 255)) +(assert_return (invoke "i8x16_extract_lane_s-last" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -128)) (i32.const -128)) +(assert_return (invoke "i8x16_extract_lane_s-last" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x80)) (i32.const -128)) +(assert_return (invoke "i8x16_extract_lane_u-last" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1)) (i32.const 255)) +(assert_return (invoke "i8x16_extract_lane_u-last" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0xff)) (i32.const 255)) +(assert_return (invoke "i8x16_extract_lane_u-last" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -128)) (i32.const 128)) +(assert_return (invoke "i8x16_extract_lane_u-last" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x80)) (i32.const 128)) + +(assert_return (invoke "i16x8_extract_lane_s-first" (v128.const i16x8 32767 0 0 0 0 0 0 0)) (i32.const 32767)) +(assert_return (invoke "i16x8_extract_lane_s-first" (v128.const i16x8 0x7fff 0 0 0 0 0 0 0)) (i32.const 32767)) +(assert_return (invoke "i16x8_extract_lane_s-first" (v128.const i16x8 65535 0 0 0 0 0 0 0)) (i32.const -1)) +(assert_return (invoke "i16x8_extract_lane_s-first" (v128.const i16x8 0xffff 0 0 0 0 0 0 0)) (i32.const -1)) +(assert_return (invoke "i16x8_extract_lane_s-first" (v128.const i16x8 012_345 0 0 0 0 0 0 0)) (i32.const 12345)) +(assert_return (invoke "i16x8_extract_lane_s-first" (v128.const i16x8 -0x0_1234 0 0 0 0 0 0 0)) (i32.const -0x1234)) +(assert_return (invoke "i16x8_extract_lane_u-first" (v128.const i16x8 65535 0 0 0 0 0 0 0)) (i32.const 65535)) +(assert_return (invoke "i16x8_extract_lane_u-first" (v128.const i16x8 0xffff 0 0 0 0 0 0 0)) (i32.const 65535)) +(assert_return (invoke "i16x8_extract_lane_u-first" (v128.const i16x8 012_345 0 0 0 0 0 0 0)) (i32.const 12345)) +(assert_return (invoke "i16x8_extract_lane_u-first" (v128.const i16x8 -0x0_1234 0 0 0 0 0 0 0)) (i32.const 60876)) +(assert_return (invoke "i16x8_extract_lane_s-last" (v128.const i16x8 0 0 0 0 0 0 0 -32768)) (i32.const -32768)) +(assert_return (invoke "i16x8_extract_lane_s-last" (v128.const i16x8 0 0 0 0 0 0 0 0x8000)) (i32.const -32768)) +(assert_return (invoke "i16x8_extract_lane_s-last" (v128.const i16x8 0 0 0 0 0 0 0 06_789)) (i32.const 6789)) +(assert_return (invoke "i16x8_extract_lane_s-last" (v128.const i16x8 0 0 0 0 0 0 0 -0x0_6789)) (i32.const -0x6789)) +(assert_return (invoke "i16x8_extract_lane_u-last" (v128.const i16x8 0 0 0 0 0 0 0 -1)) (i32.const 65535)) +(assert_return (invoke "i16x8_extract_lane_u-last" (v128.const i16x8 0 0 0 0 0 0 0 0xffff)) (i32.const 65535)) +(assert_return (invoke "i16x8_extract_lane_u-last" (v128.const i16x8 0 0 0 0 0 0 0 -32768)) (i32.const 32768)) +(assert_return (invoke "i16x8_extract_lane_u-last" (v128.const i16x8 0 0 0 0 0 0 0 0x8000)) (i32.const 32768)) +(assert_return (invoke "i16x8_extract_lane_u-last" (v128.const i16x8 0 0 0 0 0 0 0 06_789)) (i32.const 6789)) +(assert_return (invoke "i16x8_extract_lane_u-last" (v128.const i16x8 0 0 0 0 0 0 0 -0x0_6789)) (i32.const 39031)) + +(assert_return (invoke "i32x4_extract_lane-first" (v128.const i32x4 2147483647 0 0 0)) (i32.const 2147483647)) +(assert_return (invoke "i32x4_extract_lane-first" (v128.const i32x4 0x7fffffff 0 0 0)) (i32.const 2147483647)) +(assert_return (invoke "i32x4_extract_lane-first" (v128.const i32x4 4294967295 0 0 0)) (i32.const -1)) +(assert_return (invoke "i32x4_extract_lane-first" (v128.const i32x4 0xffffffff 0 0 0)) (i32.const -1)) +(assert_return (invoke "i32x4_extract_lane-first" (v128.const i32x4 01_234_567_890 0 0 0)) (i32.const 1234567890)) +(assert_return (invoke "i32x4_extract_lane-first" (v128.const i32x4 -0x0_1234_5678 0 0 0)) (i32.const -0x12345678)) +(assert_return (invoke "i32x4_extract_lane-last" (v128.const i32x4 0 0 0 -2147483648)) (i32.const -2147483648)) +(assert_return (invoke "i32x4_extract_lane-last" (v128.const i32x4 0 0 0 0x80000000)) (i32.const -2147483648)) +(assert_return (invoke "i32x4_extract_lane-last" (v128.const i32x4 0 0 0 -1)) (i32.const -1)) +(assert_return (invoke "i32x4_extract_lane-last" (v128.const i32x4 0 0 0 0xffffffff)) (i32.const -1)) +(assert_return (invoke "i32x4_extract_lane-last" (v128.const i32x4 0 0 0 0_987_654_321)) (i32.const 987654321)) +(assert_return (invoke "i32x4_extract_lane-last" (v128.const i32x4 0 0 0 -0x0_1234_5678)) (i32.const -0x12345678)) + +(assert_return (invoke "i64x2_extract_lane-first" (v128.const i64x2 9223372036854775807 0)) (i64.const 9223372036854775807)) +(assert_return (invoke "i64x2_extract_lane-first" (v128.const i64x2 0x7ffffffffffffffe 0)) (i64.const 0x7ffffffffffffffe)) +(assert_return (invoke "i64x2_extract_lane-first" (v128.const i64x2 18446744073709551615 0)) (i64.const -1)) +(assert_return (invoke "i64x2_extract_lane-first" (v128.const i64x2 0xffffffffffffffff 0)) (i64.const -1)) +(assert_return (invoke "i64x2_extract_lane-first" (v128.const i64x2 01_234_567_890_123_456_789 0)) (i64.const 1234567890123456789)) +(assert_return (invoke "i64x2_extract_lane-first" (v128.const i64x2 0x0_1234_5678_90AB_cdef 0)) (i64.const 0x1234567890abcdef)) +(assert_return (invoke "i64x2_extract_lane-last" (v128.const i64x2 0 9223372036854775808)) (i64.const -9223372036854775808)) +(assert_return (invoke "i64x2_extract_lane-last" (v128.const i64x2 0 0x8000000000000000)) (i64.const -0x8000000000000000)) +(assert_return (invoke "i64x2_extract_lane-last" (v128.const i64x2 0 0x8000000000000000)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64x2_extract_lane-last" (v128.const i8x16 0 0 0 0 0 0 0 0 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0x7f)) (i64.const 9223372036854775807)) +(assert_return (invoke "i64x2_extract_lane-last" (v128.const i16x8 0 0 0 0 0 0 0 0x8000)) (i64.const -9223372036854775808)) +(assert_return (invoke "i64x2_extract_lane-last" (v128.const i32x4 0 0 0xffffffff 0x7fffffff)) (i64.const 9223372036854775807)) +(assert_return (invoke "i64x2_extract_lane-last" (v128.const f64x2 -inf +inf)) (i64.const 0x7ff0000000000000)) +(assert_return (invoke "i64x2_extract_lane-last" (v128.const i64x2 0 01_234_567_890_123_456_789)) (i64.const 1234567890123456789)) +(assert_return (invoke "i64x2_extract_lane-last" (v128.const i64x2 0 0x0_1234_5678_90AB_cdef)) (i64.const 0x1234567890abcdef)) + +(assert_return (invoke "f32x4_extract_lane-first" (v128.const f32x4 -5.0 0.0 0.0 0.0)) (f32.const -5.0)) +(assert_return (invoke "f32x4_extract_lane-first" (v128.const f32x4 1e38 0.0 0.0 0.0)) (f32.const 1e38)) +(assert_return (invoke "f32x4_extract_lane-first" (v128.const f32x4 0x1.fffffep127 0.0 0.0 0.0)) (f32.const 0x1.fffffep127)) +(assert_return (invoke "f32x4_extract_lane-first" (v128.const f32x4 0x1p127 0.0 0.0 0.0)) (f32.const 0x1p127)) +(assert_return (invoke "f32x4_extract_lane-first" (v128.const f32x4 inf 0.0 0.0 0.0)) (f32.const inf)) +(assert_return (invoke "f32x4_extract_lane-first" (v128.const f32x4 nan inf 0.0 0.0)) (f32.const nan)) +(assert_return (invoke "f32x4_extract_lane-first" (v128.const f32x4 0123456789.0123456789e+019 0.0 0.0 0.0)) (f32.const 123456789.0123456789e+019)) +(assert_return (invoke "f32x4_extract_lane-first" (v128.const f32x4 0x0123456789ABCDEF.019aFp-019 0.0 0.0 0.0)) (f32.const 0x123456789ABCDEF.019aFp-019)) +(assert_return (invoke "f32x4_extract_lane-last" (v128.const f32x4 0.0 0.0 0.0 -1e38)) (f32.const -1e38)) +(assert_return (invoke "f32x4_extract_lane-last" (v128.const f32x4 0.0 0.0 0.0 -0x1.fffffep127)) (f32.const -0x1.fffffep127)) +(assert_return (invoke "f32x4_extract_lane-last" (v128.const f32x4 0.0 0.0 0.0 -0x1p127)) (f32.const -0x1p127)) +(assert_return (invoke "f32x4_extract_lane-last" (v128.const f32x4 0.0 0.0 0.0 -inf)) (f32.const -inf)) +(assert_return (invoke "f32x4_extract_lane-last" (v128.const f32x4 0.0 0.0 -inf nan)) (f32.const nan)) +(assert_return (invoke "f32x4_extract_lane-last" (v128.const f32x4 0.0 0.0 0.0 0123456789.)) (f32.const 123456789.0)) +(assert_return (invoke "f32x4_extract_lane-last" (v128.const f32x4 0.0 0.0 0.0 0x0123456789ABCDEF.)) (f32.const 0x123456789ABCDEF.0p0)) + +(assert_return (invoke "f64x2_extract_lane-first" (v128.const f64x2 -1.5 0.0)) (f64.const -1.5)) +(assert_return (invoke "f64x2_extract_lane-first" (v128.const f64x2 1.5 0.0)) (f64.const 1.5)) +(assert_return (invoke "f64x2_extract_lane-first" (v128.const f64x2 -1.7976931348623157e-308 0x0p+0)) (f64.const -1.7976931348623157e-308)) +(assert_return (invoke "f64x2_extract_lane-first" (v128.const f64x2 1.7976931348623157e-308 0x0p-0)) (f64.const 1.7976931348623157e-308)) +(assert_return (invoke "f64x2_extract_lane-first" (v128.const f64x2 -0x1.fffffffffffffp-1023 0x0p+0)) (f64.const -0x1.fffffffffffffp-1023)) +(assert_return (invoke "f64x2_extract_lane-first" (v128.const f64x2 0x1.fffffffffffffp-1023 0x0p-0)) (f64.const 0x1.fffffffffffffp-1023)) +(assert_return (invoke "f64x2_extract_lane-first" (v128.const f64x2 -inf 0.0)) (f64.const -inf)) +(assert_return (invoke "f64x2_extract_lane-first" (v128.const f64x2 inf 0.0)) (f64.const inf)) +(assert_return (invoke "f64x2_extract_lane-first" (v128.const f64x2 -nan -0.0)) (f64.const -nan)) +(assert_return (invoke "f64x2_extract_lane-first" (v128.const f64x2 nan 0.0)) (f64.const nan)) +(assert_return (invoke "f64x2_extract_lane-first" (v128.const f64x2 0123456789.0123456789e+019 0.0)) (f64.const 123456789.0123456789e+019)) +(assert_return (invoke "f64x2_extract_lane-first" (v128.const f64x2 0x0123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019 0.0)) (f64.const 0x123456789ABCDEFabcdef.0123456789ABCDEFabcdefp-019)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const f64x2 0.0 2.25)) (f64.const 2.25)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const f64x2 0.0 -2.25)) (f64.const -2.25)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const f64x2 0x0p-0 -1.7976931348623157e+308)) (f64.const -1.7976931348623157e+308)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const f64x2 0x0p+0 1.7976931348623157e+308)) (f64.const 1.7976931348623157e+308)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const f64x2 0x0p-0 -0x1.fffffffffffffp+1023)) (f64.const -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const f64x2 0x0p+0 0x1.fffffffffffffp+1023)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const f64x2 -0.0 -inf)) (f64.const -inf)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const f64x2 0.0 inf)) (f64.const inf)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const f64x2 -0.0 -nan)) (f64.const -nan)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const f64x2 0.0 nan)) (f64.const nan)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const f64x2 0.0 0123456789.)) (f64.const 123456789.0)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const f64x2 0.0 0x0123456789ABCDEFabcdef.)) (f64.const 0x123456789ABCDEFabcdef.0)) + +(assert_return (invoke "f64x2_extract_lane-last" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) (f64.const 0.0)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x80)) (f64.const -0.0)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const i16x8 0 0 0 0 0 0 0 0x4000)) (f64.const 2.0)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const i16x8 0 0 0 0 0 0 0 0xc000)) (f64.const -2.0)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const i32x4 0 0 0xffffffff 0x7fefffff)) (f64.const 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const i32x4 0 0 0 0x00100000)) (f64.const 0x1.0000000000000p-1022)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const i32x4 0 0 0xffffffff 0x000fffff)) (f64.const 0x1.ffffffffffffep-1023)) +(assert_return (invoke "f64x2_extract_lane-last" (v128.const i32x4 0 0 1 0)) (f64.const 0x0.0000000000002p-1023)) + +(assert_return (invoke "i8x16_replace_lane-first" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 127)) (v128.const i8x16 127 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16_replace_lane-first" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 128)) (v128.const i8x16 -128 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16_replace_lane-first" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 255)) (v128.const i8x16 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16_replace_lane-first" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 256)) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16_replace_lane-last" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const -128)) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -128)) +(assert_return (invoke "i8x16_replace_lane-last" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const -129)) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127)) +(assert_return (invoke "i8x16_replace_lane-last" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 32767)) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0xff)) +(assert_return (invoke "i8x16_replace_lane-last" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const -32768)) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + +(assert_return (invoke "i16x8_replace_lane-first" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 32767)) (v128.const i16x8 32767 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8_replace_lane-first" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 32768)) (v128.const i16x8 -32768 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8_replace_lane-first" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 65535)) (v128.const i16x8 -1 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8_replace_lane-first" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 65536)) (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8_replace_lane-first" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 012345)) (v128.const i16x8 012_345 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8_replace_lane-first" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const -0x01234)) (v128.const i16x8 -0x0_1234 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8_replace_lane-last" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const -32768)) (v128.const i16x8 0 0 0 0 0 0 0 -32768)) +(assert_return (invoke "i16x8_replace_lane-last" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const -32769)) (v128.const i16x8 0 0 0 0 0 0 0 32767)) +(assert_return (invoke "i16x8_replace_lane-last" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 0x7fffffff)) (v128.const i16x8 0 0 0 0 0 0 0 0xffff)) +(assert_return (invoke "i16x8_replace_lane-last" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 0x80000000)) (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8_replace_lane-last" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 054321)) (v128.const i16x8 0 0 0 0 0 0 0 054_321)) +(assert_return (invoke "i16x8_replace_lane-last" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const -0x04321)) (v128.const i16x8 0 0 0 0 0 0 0 -0x0_4321)) + +(assert_return (invoke "i32x4_replace_lane-first" (v128.const i32x4 0 0 0 0) (i32.const 2147483647)) (v128.const i32x4 2147483647 0 0 0)) +(assert_return (invoke "i32x4_replace_lane-first" (v128.const i32x4 0 0 0 0) (i32.const 4294967295)) (v128.const i32x4 -1 0 0 0)) +(assert_return (invoke "i32x4_replace_lane-first" (v128.const i32x4 0 0 0 0) (i32.const 01234567890)) (v128.const i32x4 01_234_567_890 0 0 0)) +(assert_return (invoke "i32x4_replace_lane-first" (v128.const i32x4 0 0 0 0) (i32.const -0x012345678)) (v128.const i32x4 -0x0_1234_5678 0 0 0)) +(assert_return (invoke "i32x4_replace_lane-last" (v128.const i32x4 0 0 0 0) (i32.const 2147483648)) (v128.const i32x4 0 0 0 2147483648)) +(assert_return (invoke "i32x4_replace_lane-last" (v128.const i32x4 0 0 0 0) (i32.const -2147483648)) (v128.const i32x4 0 0 0 -2147483648)) +(assert_return (invoke "i32x4_replace_lane-last" (v128.const i32x4 0 0 0 0) (i32.const 01234567890)) (v128.const i32x4 0 0 0 01_234_567_890)) +(assert_return (invoke "i32x4_replace_lane-last" (v128.const i32x4 0 0 0 0) (i32.const -0x012345678)) (v128.const i32x4 0 0 0 -0x0_1234_5678)) + +(assert_return (invoke "f32x4_replace_lane-first" (v128.const f32x4 0.0 0.0 0.0 0.0) (f32.const 53.0)) (v128.const f32x4 53.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4_replace_lane-first" (v128.const i32x4 0 0 0 0 ) (f32.const 53.0)) (v128.const f32x4 53.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4_replace_lane-first" (v128.const f32x4 0.0 0.0 0.0 0.0) (f32.const nan)) (v128.const f32x4 nan 0.0 0.0 0.0)) +(assert_return (invoke "f32x4_replace_lane-first" (v128.const f32x4 0.0 0.0 0.0 0.0) (f32.const inf)) (v128.const f32x4 inf 0.0 0.0 0.0)) +(assert_return (invoke "f32x4_replace_lane-first" (v128.const f32x4 nan 0.0 0.0 0.0) (f32.const 3.14)) (v128.const f32x4 3.14 0.0 0.0 0.0)) +(assert_return (invoke "f32x4_replace_lane-first" (v128.const f32x4 inf 0.0 0.0 0.0) (f32.const 1e38)) (v128.const f32x4 1e38 0.0 0.0 0.0)) +(assert_return (invoke "f32x4_replace_lane-first" (v128.const f32x4 inf 0.0 0.0 0.0) (f32.const 0x1.fffffep127)) (v128.const f32x4 0x1.fffffep127 0.0 0.0 0.0)) +(assert_return (invoke "f32x4_replace_lane-first" (v128.const f32x4 inf 0.0 0.0 0.0) (f32.const 0x1p127)) (v128.const f32x4 0x1p127 0.0 0.0 0.0)) +(assert_return (invoke "f32x4_replace_lane-first" (v128.const f32x4 0.0 0.0 0.0 0.0) (f32.const 0123456789)) (v128.const f32x4 0123456789 0.0 0.0 0.0)) +(assert_return (invoke "f32x4_replace_lane-first" (v128.const f32x4 0.0 0.0 0.0 0.0) (f32.const 0123456789.)) (v128.const f32x4 0123456789. 0.0 0.0 0.0)) +(assert_return (invoke "f32x4_replace_lane-first" (v128.const f32x4 0.0 0.0 0.0 0.0) (f32.const 0x0123456789ABCDEF)) (v128.const f32x4 0x0123456789ABCDEF 0.0 0.0 0.0)) +(assert_return (invoke "f32x4_replace_lane-first" (v128.const f32x4 0.0 0.0 0.0 0.0) (f32.const 0x0123456789ABCDEF.)) (v128.const f32x4 0x0123456789ABCDEF. 0.0 0.0 0.0)) +(assert_return (invoke "f32x4_replace_lane-last" (v128.const f32x4 0.0 0.0 0.0 0.0) (f32.const -53.0)) (v128.const f32x4 0.0 0.0 0.0 -53.0)) +(assert_return (invoke "f32x4_replace_lane-last" (v128.const i32x4 0 0 0 0) (f32.const -53.0)) (v128.const f32x4 0.0 0.0 0.0 -53.0)) +(assert_return (invoke "f32x4_replace_lane-last" (v128.const f32x4 0.0 0.0 0.0 0.0) (f32.const nan)) (v128.const f32x4 0.0 0.0 0.0 nan)) +(assert_return (invoke "f32x4_replace_lane-last" (v128.const f32x4 0.0 0.0 0.0 0.0) (f32.const -inf)) (v128.const f32x4 0.0 0.0 0.0 -inf)) +(assert_return (invoke "f32x4_replace_lane-last" (v128.const f32x4 0.0 0.0 0.0 nan) (f32.const 3.14)) (v128.const f32x4 0.0 0.0 0.0 3.14)) +(assert_return (invoke "f32x4_replace_lane-last" (v128.const f32x4 0.0 0.0 0.0 -inf) (f32.const -1e38)) (v128.const f32x4 0.0 0.0 0.0 -1e38)) +(assert_return (invoke "f32x4_replace_lane-last" (v128.const f32x4 0.0 0.0 0.0 -inf) (f32.const -0x1.fffffep127)) (v128.const f32x4 0.0 0.0 0.0 -0x1.fffffep127)) +(assert_return (invoke "f32x4_replace_lane-last" (v128.const f32x4 0.0 0.0 0.0 -inf) (f32.const -0x1p127)) (v128.const f32x4 0.0 0.0 0.0 -0x1p127)) +(assert_return (invoke "f32x4_replace_lane-last" (v128.const f32x4 0.0 0.0 0.0 0.0) (f32.const 0123456789e019)) (v128.const f32x4 0.0 0.0 0.0 0123456789e019)) +(assert_return (invoke "f32x4_replace_lane-last" (v128.const f32x4 0.0 0.0 0.0 0.0) (f32.const 0123456789.e+019)) (v128.const f32x4 0.0 0.0 0.0 0123456789.e+019)) +(assert_return (invoke "f32x4_replace_lane-last" (v128.const f32x4 0.0 0.0 0.0 0.0) (f32.const 0x0123456789ABCDEFp019)) (v128.const f32x4 0.0 0.0 0.0 0x0123456789ABCDEFp019)) +(assert_return (invoke "f32x4_replace_lane-last" (v128.const f32x4 0.0 0.0 0.0 0.0) (f32.const 0x0123456789ABCDEF.p-019)) (v128.const f32x4 0.0 0.0 0.0 0x0123456789ABCDEF.p-019)) + +(assert_return (invoke "i64x2_replace_lane-first" (v128.const i64x2 0 0) (i64.const 9223372036854775807)) (v128.const i64x2 9223372036854775807 0)) +(assert_return (invoke "i64x2_replace_lane-first" (v128.const i64x2 0 0) (i64.const 18446744073709551615)) (v128.const i64x2 -1 0)) +(assert_return (invoke "i64x2_replace_lane-first" (v128.const i64x2 0 0) (i64.const 01234567890123456789)) (v128.const i64x2 01_234_567_890_123_456_789 0)) +(assert_return (invoke "i64x2_replace_lane-first" (v128.const i64x2 0 0) (i64.const 0x01234567890abcdef)) (v128.const i64x2 0x0_1234_5678_90AB_cdef 0)) +(assert_return (invoke "i64x2_replace_lane-last" (v128.const i64x2 0 0) (i64.const 9223372036854775808)) (v128.const i64x2 0 9223372036854775808)) +(assert_return (invoke "i64x2_replace_lane-last" (v128.const i64x2 0 0) (i64.const 9223372036854775808)) (v128.const i64x2 0 -9223372036854775808)) +(assert_return (invoke "i64x2_replace_lane-last" (v128.const i64x2 0 0) (i64.const 01234567890123456789)) (v128.const i64x2 0 01_234_567_890_123_456_789)) +(assert_return (invoke "i64x2_replace_lane-last" (v128.const i64x2 0 0) (i64.const 0x01234567890abcdef)) (v128.const i64x2 0 0x0_1234_5678_90AB_cdef)) + +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 1.0 1.0) (f64.const 0x0p+0)) (v128.const f64x2 0.0 1.0)) +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 -1.0 -1.0) (f64.const -0x0p-0)) (v128.const f64x2 -0.0 -1.0)) +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 0.0 0.0) (f64.const 1.25)) (v128.const f64x2 1.25 0.0)) +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 0.0 0.0) (f64.const -1.25)) (v128.const f64x2 -1.25 0.0)) +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 -nan 0.0) (f64.const -1.7976931348623157e+308)) (v128.const f64x2 -1.7976931348623157e+308 0.0)) +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 nan 0.0) (f64.const 1.7976931348623157e+308)) (v128.const f64x2 1.7976931348623157e+308 0.0)) +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 -inf 0.0) (f64.const -0x1.fffffffffffffp-1023)) (v128.const f64x2 -0x1.fffffffffffffp-1023 0.0)) +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 inf 0.0) (f64.const 0x1.fffffffffffffp-1023)) (v128.const f64x2 0x1.fffffffffffffp-1023 0.0)) +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 0.0 0.0) (f64.const -nan)) (v128.const f64x2 -nan 0.0)) +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 0.0 0.0) (f64.const nan)) (v128.const f64x2 nan 0.0)) +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 0.0 0.0) (f64.const -inf)) (v128.const f64x2 -inf 0.0)) +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 0.0 0.0) (f64.const inf)) (v128.const f64x2 inf 0.0)) +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 0.0 0.0) (f64.const 0123456789)) (v128.const f64x2 0123456789 0.0)) +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 0.0 0.0) (f64.const 0123456789.)) (v128.const f64x2 0123456789. 0.0)) +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 0.0 0.0) (f64.const 0x0123456789ABCDEFabcdef)) (v128.const f64x2 0x0123456789ABCDEFabcdef 0.0)) +(assert_return (invoke "f64x2_replace_lane-first" (v128.const f64x2 0.0 0.0) (f64.const 0x0123456789ABCDEFabcdef.)) (v128.const f64x2 0x0123456789ABCDEFabcdef. 0.0)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 2.0 2.0) (f64.const 0.0)) (v128.const f64x2 2.0 0.0)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 -2.0 -2.0) (f64.const -0.0)) (v128.const f64x2 -2.0 -0.0)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 0.0 0.0) (f64.const 2.25)) (v128.const f64x2 0.0 2.25)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 0.0 0.0) (f64.const -2.25)) (v128.const f64x2 0.0 -2.25)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 0.0 -nan) (f64.const -1.7976931348623157e+308)) (v128.const f64x2 0.0 -1.7976931348623157e+308)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 0.0 nan) (f64.const 1.7976931348623157e+308)) (v128.const f64x2 0.0 1.7976931348623157e+308)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 0.0 -inf) (f64.const -0x1.fffffffffffffp-1023)) (v128.const f64x2 0.0 -0x1.fffffffffffffp-1023)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 0.0 inf) (f64.const 0x1.fffffffffffffp-1023)) (v128.const f64x2 0.0 0x1.fffffffffffffp-1023)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 0.0 0.0) (f64.const -nan)) (v128.const f64x2 0.0 -nan)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 0.0 0.0) (f64.const nan)) (v128.const f64x2 0.0 nan)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 0.0 0.0) (f64.const -inf)) (v128.const f64x2 0.0 -inf)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 0.0 0.0) (f64.const inf)) (v128.const f64x2 0.0 inf)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 0.0 0.0) (f64.const 0123456789e019)) (v128.const f64x2 0.0 0123456789e019)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 0.0 0.0) (f64.const 0123456789e+019)) (v128.const f64x2 0.0 0123456789e+019)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 0.0 0.0) (f64.const 0123456789.e019)) (v128.const f64x2 0.0 0123456789.e019)) +(assert_return (invoke "f64x2_replace_lane-last" (v128.const f64x2 0.0 0.0) (f64.const 0123456789.e-019)) (v128.const f64x2 0.0 0123456789.e-019)) + +(assert_return (invoke "v8x16_swizzle" + (v128.const i8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) + (v128.const i8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31)) +(assert_return (invoke "v8x16_swizzle" + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1) + (v128.const i8x16 -8 -7 -6 -5 -4 -3 -2 -1 16 17 18 19 20 21 22 23)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v8x16_swizzle" + (v128.const i8x16 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115) + (v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0)) + (v128.const i8x16 115 114 113 112 111 110 109 108 107 106 105 104 103 102 101 100)) +(assert_return (invoke "v8x16_swizzle" + (v128.const i8x16 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115) + (v128.const i8x16 -1 1 -2 2 -3 3 -4 4 -5 5 -6 6 -7 7 -8 8)) + (v128.const i8x16 0 101 0 102 0 103 0 104 0 105 0 106 0 107 0 108)) +(assert_return (invoke "v8x16_swizzle" + (v128.const i8x16 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115) + (v128.const i8x16 9 16 10 17 11 18 12 19 13 20 14 21 15 22 16 23)) + (v128.const i8x16 109 0 110 0 111 0 112 0 113 0 114 0 115 0 0 0)) +(assert_return (invoke "v8x16_swizzle" + (v128.const i8x16 0x64 0x65 0x66 0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f 0x70 0x71 0x72 0x73) + (v128.const i8x16 9 16 10 17 11 18 12 19 13 20 14 21 15 22 16 23)) + (v128.const i8x16 0x6d 0 0x6e 0 0x6f 0 0x70 0 0x71 0 0x72 0 0x73 0 0 0)) +(assert_return (invoke "v8x16_swizzle" + (v128.const i16x8 0x6465 0x6667 0x6869 0x6a6b 0x6c6d 0x6e6f 0x7071 0x7273) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) + (v128.const i16x8 0x6465 0x6667 0x6869 0x6a6b 0x6c6d 0x6e6f 0x7071 0x7273)) +(assert_return (invoke "v8x16_swizzle" + (v128.const i32x4 0x64656667 0x68696a6b 0x6c6d6e6f 0x70717273) + (v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0)) + (v128.const i32x4 0x73727170 0x6f6e6d6c 0x6b6a6968 0x67666564)) +(assert_return (invoke "v8x16_swizzle" + (v128.const f32x4 nan -nan inf -inf) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) + (v128.const i32x4 0x7fc00000 0xffc00000 0x7f800000 0xff800000)) +(assert_return (invoke "v8x16_swizzle" + (v128.const i32x4 0x67666564 0x6b6a6968 0x6f6e6d5c 0x73727170) + (v128.const f32x4 0.0 -0.0 inf -inf)) + (v128.const i32x4 0x64646464 0x00646464 0x00006464 0x00006464)) + +(assert_return (invoke "v8x16_shuffle-1" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31)) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) +(assert_return (invoke "v8x16_shuffle-2" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1)) + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1)) +(assert_return (invoke "v8x16_shuffle-3" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1)) + (v128.const i8x16 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16)) +(assert_return (invoke "v8x16_shuffle-4" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1)) + (v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0)) +(assert_return (invoke "v8x16_shuffle-5" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v8x16_shuffle-6" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1)) + (v128.const i8x16 -16 -16 -16 -16 -16 -16 -16 -16 -16 -16 -16 -16 -16 -16 -16 -16)) +(assert_return (invoke "v8x16_shuffle-7" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1)) + (v128.const i8x16 0 0 0 0 0 0 0 0 -16 -16 -16 -16 -16 -16 -16 -16)) +(assert_return (invoke "v8x16_shuffle-1" + (v128.const i8x16 0x64 0x65 0x66 0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f 0x70 0x71 0x72 0x73) + (v128.const i8x16 0xf0 0xf1 0xf2 0xf3 0xf4 0xf5 0xf6 0xf7 0xf8 0xf9 0xfa 0xfb 0xfc 0xfd 0xfe 0xff)) + (v128.const i8x16 0x64 0x65 0x66 0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f 0x70 0x71 0x72 0x73)) +(assert_return (invoke "v8x16_shuffle-1" + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0b0a 0x0d0c 0x0f0e) + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1)) + (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0b0a 0x0d0c 0x0f0e)) +(assert_return (invoke "v8x16_shuffle-2" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i32x4 0xf3f2f1f0 0xf7f6f5f4 0xfbfaf9f8 0xfffefdfc)) + (v128.const i32x4 0xf3f2f1f0 0xf7f6f5f4 0xfbfaf9f8 0xfffefdfc)) +(assert_return (invoke "v8x16_shuffle-1" + (v128.const i32x4 0x10203 0x4050607 0x8090a0b 0xc0d0e0f) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) + (v128.const i32x4 0x10203 0x4050607 0x8090a0b 0xc0d0e0f)) +(assert_return (invoke "v8x16_shuffle-1" + (v128.const f32x4 1.0 nan inf -inf) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) + (v128.const i32x4 0x3f800000 0x7fc00000 0x7f800000 0xff800000)) +(assert_return (invoke "v8x16_shuffle-1" + (v128.const i32x4 0x10203 0x4050607 0x8090a0b 0xc0d0e0f) + (v128.const f32x4 -0.0 nan inf -inf)) + (v128.const i32x4 0x10203 0x4050607 0x8090a0b 0xc0d0e0f)) + +;; More literals +(assert_return (invoke "v8x16_swizzle" + (v128.const i32x4 1_234_567_890 0x1234_5678 01_234_567_890 0x0_1234_5678) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) + (v128.const i32x4 0x4996_02d2 0x1234_5678 0x4996_02d2 0x1234_5678)) +(assert_return (invoke "v8x16_shuffle-1" + (v128.const i64x2 1_234_567_890_123_456_789_0 0x1234_5678_90AB_cdef) + (v128.const i64x2 01_234_567_890_123_456_789_0 0x0_1234_5678_90AB_cdef)) + (v128.const i32x4 0xeb1f_0ad2 0xab54_a98c 0x90ab_cdef 0x1234_5678)) + +;; Syntax errors for negative values + +(assert_malformed (module quote "(func (result i32) (i8x16.extract_lane_s -1 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result i32) (i8x16.extract_lane_u -1 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result i32) (i16x8.extract_lane_s -1 (v128.const i16x8 0 0 0 0 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result i32) (i16x8.extract_lane_u -1 (v128.const i16x8 0 0 0 0 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result i32) (i32x4.extract_lane -1 (v128.const i32x4 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result f32) (f32x4.extract_lane -1 (v128.const f32x4 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result v128) (i8x16.replace_lane -1 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 1)))") "unexpected token") +(assert_malformed (module quote "(func (result v128) (i16x8.replace_lane -1 (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 1)))") "unexpected token") +(assert_malformed (module quote "(func (result v128) (i32x4.replace_lane -1 (v128.const i32x4 0 0 0 0) (i32.const 1)))") "unexpected token") +(assert_malformed (module quote "(func (result v128) (f32x4.replace_lane -1 (v128.const f32x4 0 0 0 0) (i32.const 1)))") "unexpected token") +(assert_malformed (module quote "(func (result i64) (i64x2.extract_lane -1 (v128.const i64x2 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result f64) (f64x2.extract_lane -1 (v128.const f64x2 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result v128) (i64x2.replace_lane -1 (v128.const i64x2 0 0) (i64.const 1)))") "unexpected token") +(assert_malformed (module quote "(func (result v128) (f64x2.replace_lane -1 (v128.const f64x2 0 0) (f64.const 1)))") "unexpected token") + +;; Malformed lane index value + +(assert_malformed (module quote "(func (result i32) (i8x16.extract_lane_s 256 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") "malformed lane index") +(assert_malformed (module quote "(func (result i32) (i8x16.extract_lane_u 256 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") "malformed lane index") +(assert_malformed (module quote "(func (result i32) (i16x8.extract_lane_s 256 (v128.const i16x8 0 0 0 0 0 0 0 0)))") "malformed lane index") +(assert_malformed (module quote "(func (result i32) (i16x8.extract_lane_u 256 (v128.const i16x8 0 0 0 0 0 0 0 0)))") "malformed lane index") +(assert_malformed (module quote "(func (result i32) (i32x4.extract_lane 256 (v128.const i32x4 0 0 0 0)))") "malformed lane index") +(assert_malformed (module quote "(func (result f32) (f32x4.extract_lane 256 (v128.const f32x4 0 0 0 0)))") "malformed lane index") +(assert_malformed (module quote "(func (result v128) (i8x16.replace_lane 256 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 1)))") "malformed lane index") +(assert_malformed (module quote "(func (result v128) (i16x8.replace_lane 256 (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 1)))") "malformed lane index") +(assert_malformed (module quote "(func (result v128) (i32x4.replace_lane 256 (v128.const i32x4 0 0 0 0) (i32.const 1)))") "malformed lane index") +(assert_malformed (module quote "(func (result v128) (f32x4.replace_lane 256 (v128.const f32x4 0 0 0 0) (i32.const 1)))") "malformed lane index") +(assert_malformed (module quote "(func (result i64) (i64x2.extract_lane 256 (v128.const i64x2 0 0)))") "malformed lane index") +(assert_malformed (module quote "(func (result f64) (f64x2.extract_lane 256 (v128.const f64x2 0 0)))") "malformed lane index") +(assert_malformed (module quote "(func (result v128) (i64x2.replace_lane 256 (v128.const i64x2 0 0) (i64.const 1)))") "malformed lane index") +(assert_malformed (module quote "(func (result v128) (f64x2.replace_lane 256 (v128.const f64x2 0 0) (f64.const 1)))") "malformed lane index") + +;; Invalid lane index value + +(assert_invalid (module (func (result i32) (i8x16.extract_lane_s 16 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result i32) (i8x16.extract_lane_s 255 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result i32) (i8x16.extract_lane_u 16 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result i32) (i8x16.extract_lane_u 255 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result i32) (i16x8.extract_lane_s 8 (v128.const i16x8 0 0 0 0 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result i32) (i16x8.extract_lane_s 255 (v128.const i16x8 0 0 0 0 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result i32) (i16x8.extract_lane_u 8 (v128.const i16x8 0 0 0 0 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result i32) (i16x8.extract_lane_u 255 (v128.const i16x8 0 0 0 0 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result i32) (i32x4.extract_lane 4 (v128.const i32x4 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result i32) (i32x4.extract_lane 255 (v128.const i32x4 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result f32) (f32x4.extract_lane 4 (v128.const f32x4 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result f32) (f32x4.extract_lane 255 (v128.const f32x4 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result v128) (i8x16.replace_lane 16 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 1)))) "invalid lane index") +(assert_invalid (module (func (result v128) (i8x16.replace_lane 255 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 1)))) "invalid lane index") +(assert_invalid (module (func (result v128) (i16x8.replace_lane 16 (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 1)))) "invalid lane index") +(assert_invalid (module (func (result v128) (i16x8.replace_lane 255 (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 1)))) "invalid lane index") +(assert_invalid (module (func (result v128) (i32x4.replace_lane 4 (v128.const i32x4 0 0 0 0) (i32.const 1)))) "invalid lane index") +(assert_invalid (module (func (result v128) (i32x4.replace_lane 255 (v128.const i32x4 0 0 0 0) (i32.const 1)))) "invalid lane index") +(assert_invalid (module (func (result v128) (f32x4.replace_lane 4 (v128.const f32x4 0 0 0 0) (i32.const 1)))) "invalid lane index") +(assert_invalid (module (func (result v128) (f32x4.replace_lane 255 (v128.const f32x4 0 0 0 0) (i32.const 1)))) "invalid lane index") +(assert_invalid (module (func (result i64) (i64x2.extract_lane 2 (v128.const i64x2 0 0)))) "invalid lane index") +(assert_invalid (module (func (result i64) (i64x2.extract_lane 255 (v128.const i64x2 0 0)))) "invalid lane index") +(assert_invalid (module (func (result f64) (f64x2.extract_lane 2 (v128.const f64x2 0 0)))) "invalid lane index") +(assert_invalid (module (func (result f64) (f64x2.extract_lane 255 (v128.const f64x2 0 0)))) "invalid lane index") +(assert_invalid (module (func (result v128) (i64x2.replace_lane 2 (v128.const i64x2 0 0) (i64.const 1)))) "invalid lane index") +(assert_invalid (module (func (result v128) (i64x2.replace_lane 255 (v128.const i64x2 0 0) (i64.const 1)))) "invalid lane index") +(assert_invalid (module (func (result v128) (f64x2.replace_lane 2 (v128.const f64x2 0 0) (f64.const 1)))) "invalid lane index") +(assert_invalid (module (func (result v128) (f64x2.replace_lane 255 (v128.const f64x2 0 0) (f64.const 1.0)))) "invalid lane index") + +;; Lane index is determined by the instruction's interpretation only. + +(assert_invalid (module (func (result i32) (i16x8.extract_lane_s 8 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result i32) (i16x8.extract_lane_u 8 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result i32) (i32x4.extract_lane 4 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result i32) (f32x4.extract_lane 4 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))) "invalid lane index") +(assert_invalid (module (func (result v128) (i16x8.replace_lane 8 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 1)))) "invalid lane index") +(assert_invalid (module (func (result v128) (i32x4.replace_lane 4 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 1)))) "invalid lane index") +(assert_invalid (module (func (result v128) (f32x4.replace_lane 4 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 1)))) "invalid lane index") +(assert_invalid (module (func (result i64) (i64x2.extract_lane 2 (v128.const i64x2 0 0)))) "invalid lane index") +(assert_invalid (module (func (result f64) (f64x2.extract_lane 2 (v128.const f64x2 0 0)))) "invalid lane index") +(assert_invalid (module (func (result v128) (i64x2.replace_lane 2 (v128.const i64x2 0 0) (i64.const 1)))) "invalid lane index") +(assert_invalid (module (func (result v128) (f64x2.replace_lane 2 (v128.const f64x2 0 0) (f64.const 1.0)))) "invalid lane index") + +;; Invalid parameters: required v128 but pass other types + +(assert_invalid (module (func (result i32) (i8x16.extract_lane_s 0 (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i8x16.extract_lane_u 0 (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i8x16.extract_lane_s 0 (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i8x16.extract_lane_u 0 (f64.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result i32) (i32x4.extract_lane 0 (i32.const 0)))) "type mismatch") +(assert_invalid (module (func (result f32) (f32x4.extract_lane 0 (f32.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i8x16.replace_lane 0 (i32.const 0) (i32.const 1)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.replace_lane 0 (i64.const 0) (i32.const 1)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.replace_lane 0 (i32.const 0) (i32.const 1)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.replace_lane 0 (f32.const 0.0) (i32.const 1)))) "type mismatch") +(assert_invalid (module (func (result i64) (i64x2.extract_lane 0 (i64.const 0)))) "type mismatch") +(assert_invalid (module (func (result f64) (f64x2.extract_lane 0 (f64.const 0.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.replace_lane 0 (i32.const 0) (i32.const 1)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.replace_lane 0 (f32.const 0.0) (i32.const 1)))) "type mismatch") + +;; Invalid types for the replaced value + +(assert_invalid (module (func (result v128) (i8x16.replace_lane 0 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (f32.const 1.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i16x8.replace_lane 0 (v128.const i16x8 0 0 0 0 0 0 0 0) (f64.const 1.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (i32x4.replace_lane 0 (v128.const i32x4 0 0 0 0) (f32.const 1.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f32x4.replace_lane 0 (v128.const f32x4 0 0 0 0) (i32.const 1)))) "type mismatch") + +(assert_invalid (module (func (result v128) (i64x2.replace_lane 0 (v128.const i64x2 0 0) (f64.const 1.0)))) "type mismatch") +(assert_invalid (module (func (result v128) (f64x2.replace_lane 0 (v128.const f64x2 0 0) (i64.const 1)))) "type mismatch") + +;; Invalid types for swizzle and shuffle values +(assert_invalid (module (func (result v128) + (i8x16.swizzle (i32.const 1) (v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0)))) "type mismatch") +(assert_invalid (module (func (result v128) + (i8x16.swizzle (v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) (i32.const 2)))) "type mismatch") +(assert_invalid (module (func (result v128) + (i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 (f32.const 3.0) + (v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0)))) "type mismatch") +(assert_invalid (module (func (result v128) + (i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + (v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) (f32.const 4.0)))) "type mismatch") + +;; i8x16.shuffle: the 1st argument must be 16-byte literals in 0..32 +(assert_malformed (module quote "(func (param v128) (result v128)" + "(i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 (local.get 0) (local.get 0)))") + "invalid lane length") +(assert_malformed (module quote "(func (param v128) (result v128)" + "(i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 (local.get 0) (local.get 0)))") + "invalid lane length") +(assert_malformed (module quote "(func (result v128)" + "(i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -1" + "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0)" + "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))") "malformed lane index") +(assert_malformed (module quote "(func (result v128)" + "(i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 256" + "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0)" + "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))") "malformed lane index") +(assert_invalid (module (func (result v128) + (i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 255 + (v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))) "invalid lane index") + +;; Possible wrong instruction names that'd be used + +(assert_malformed (module quote "(func (result i32) (i8x16.extract_lane 0 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(func (result i32) (i16x8.extract_lane 0 (v128.const i16x8 0 0 0 0 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(func (result i32) (i32x4.extract_lane_s 0 (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(func (result i32) (i32x4.extract_lane_u 0 (v128.const i32x4 0 0 0 0)))") "unknown operator") +(assert_malformed (module quote "(func (result i32) (i64x2.extract_lane_s 0 (v128.const i64x2 0 0)))") "unknown operator") +(assert_malformed (module quote "(func (result i32) (i64x2.extract_lane_u 0 (v128.const i64x2 0 0)))") "unknown operator") + + +;; Old shuffle instruction names will not work +(assert_malformed (module quote "(func (result v128) " + "(i8x16.shuffle1 (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) " + "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0)))") + "unknown operator") +(assert_malformed (module quote "(func (result v128) " + "(i8x16.shuffle2_imm 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 " + "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) " + "(v128.const i8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31)))") + "unknown operator") +;; i8x16 not v8x16 +(assert_malformed (module quote "(func (result v128) " + "(v8x16.swizzle (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) " + "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0)))") + "unknown operator") +(assert_malformed (module quote "(func (result v128) " + "(v8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 " + "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) " + "(v128.const i8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31)))") + "unknown operator") + + +;; Malformed lane index + +;; Pass params as the lane index + +(assert_malformed (module quote "(func (param i32) (result i32) (i8x16.extract_lane_s (local.get 0) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (param i32) (result i32) (i8x16.extract_lane_u (local.get 0) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (param i32) (result i32) (i16x8.extract_lane_s (local.get 0) (v128.const i16x8 0 0 0 0 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (param i32) (result i32) (i16x8.extract_lane_u (local.get 0) (v128.const i16x8 0 0 0 0 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (param i32) (result i32) (i32x4.extract_lane (local.get 0) (v128.const i32x4 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (param i32) (result f32) (f32x4.extract_lane (local.get 0) (v128.const f32x4 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (param i32) (result v128) (i8x16.replace_lane (local.get 0) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 1)))") "unexpected token") +(assert_malformed (module quote "(func (param i32) (result v128) (i16x8.replace_lane (local.get 0) (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 1)))") "unexpected token") +(assert_malformed (module quote "(func (param i32) (result v128) (i32x4.replace_lane (local.get 0) (v128.const i32x4 0 0 0 0) (i32.const 1)))") "unexpected token") +(assert_malformed (module quote "(func (param i32) (result v128) (f32x4.replace_lane (local.get 0) (v128.const f32x4 0 0 0 0) (f32.const 1.0)))") "unexpected token") + +(assert_malformed (module quote "(func (param i32) (result i64) (i64x2.extract_lane (local.get 0) (v128.const i64x2 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (param i32) (result f64) (f64x2.extract_lane (local.get 0) (v128.const f64x2 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (param i32) (result v128) (i64x2.replace_lane (local.get 0) (v128.const i64x2 0 0) (i64.const 1)))") "unexpected token") +(assert_malformed (module quote "(func (param i32) (result v128) (f64x2.replace_lane (local.get 0) (v128.const f64x2 0 0) (f64.const 1.0)))") "unexpected token") + +;; Pass non-literal as the lane index + +(assert_malformed (module quote "(func (result i32) (i8x16.extract_lane_s 1.5 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result i32) (i8x16.extract_lane_u nan (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result i32) (i16x8.extract_lane_s inf (v128.const i16x8 0 0 0 0 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result i32) (i16x8.extract_lane_u -inf (v128.const i16x8 0 0 0 0 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result i32) (i32x4.extract_lane nan (v128.const i32x4 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result f32) (f32x4.extract_lane nan (v128.const f32x4 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result v128) (i8x16.replace_lane -2.5 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 1)))") "unexpected token") +(assert_malformed (module quote "(func (result v128) (i16x8.replace_lane nan (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 1)))") "unexpected token") +(assert_malformed (module quote "(func (result v128) (i32x4.replace_lane inf (v128.const i32x4 0 0 0 0) (i32.const 1)))") "unexpected token") +(assert_malformed (module quote "(func (result v128) (f32x4.replace_lane -inf (v128.const f32x4 0 0 0 0) (f32.const 1.1)))") "unexpected token") + +;; i8x16.shuffle expects a 16-byte literals as first argument +(assert_malformed (module quote "(func (result v128) " + "(i8x16.shuffle (v128.const i8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31) " + "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) " + "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))") "invalid lane length") +(assert_malformed (module quote "(func (result v128) " + "(i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15.0) " + "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) " + "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))") "malformed lane index") +(assert_malformed (module quote "(func (result v128) " + "(i8x16.shuffle 0.5 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) " + "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) " + "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))") "malformed lane index") +(assert_malformed (module quote "(func (result v128) " + "(i8x16.shuffle -inf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) " + "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) " + "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))") "malformed lane index") +(assert_malformed (module quote "(func (result v128) " + "(i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 inf) " + "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) " + "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))") "malformed lane index") +(assert_malformed (module quote "(func (result v128) " + "(i8x16.shuffle nan 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) " + "(v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) " + "(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))") "malformed lane index") + + +;; Combination with each other + +(module + ;; as *.replace_lane's operand + (func (export "i8x16_extract_lane_s") (param v128 v128) (result v128) + (i8x16.replace_lane 0 (local.get 0) (i8x16.extract_lane_s 0 (local.get 1)))) + (func (export "i8x16_extract_lane_u") (param v128 v128) (result v128) + (i8x16.replace_lane 0 (local.get 0) (i8x16.extract_lane_u 0 (local.get 1)))) + (func (export "i16x8_extract_lane_s") (param v128 v128) (result v128) + (i16x8.replace_lane 0 (local.get 0) (i16x8.extract_lane_s 0 (local.get 1)))) + (func (export "i16x8_extract_lane_u") (param v128 v128) (result v128) + (i16x8.replace_lane 0 (local.get 0) (i16x8.extract_lane_u 0 (local.get 1)))) + (func (export "i32x4_extract_lane") (param v128 v128) (result v128) + (i32x4.replace_lane 0 (local.get 0) (i32x4.extract_lane 0 (local.get 1)))) + (func (export "f32x4_extract_lane") (param v128 v128) (result v128) + (i32x4.replace_lane 0 (local.get 0) (i32x4.extract_lane 0 (local.get 1)))) + (func (export "i64x2_extract_lane") (param v128 v128) (result v128) + (i64x2.replace_lane 0 (local.get 0) (i64x2.extract_lane 0 (local.get 1)))) + (func (export "f64x2_extract_lane") (param v128 v128) (result v128) + (f64x2.replace_lane 0 (local.get 0) (f64x2.extract_lane 0 (local.get 1)))) + + ;; as *.extract_lane's operand + (func (export "i8x16_replace_lane-s") (param v128 i32) (result i32) + (i8x16.extract_lane_s 15 (i8x16.replace_lane 15 (local.get 0) (local.get 1)))) + (func (export "i8x16_replace_lane-u") (param v128 i32) (result i32) + (i8x16.extract_lane_u 15 (i8x16.replace_lane 15 (local.get 0) (local.get 1)))) + (func (export "i16x8_replace_lane-s") (param v128 i32) (result i32) + (i16x8.extract_lane_s 7 (i16x8.replace_lane 7 (local.get 0) (local.get 1)))) + (func (export "i16x8_replace_lane-u") (param v128 i32) (result i32) + (i16x8.extract_lane_u 7 (i16x8.replace_lane 7 (local.get 0) (local.get 1)))) + (func (export "i32x4_replace_lane") (param v128 i32) (result i32) + (i32x4.extract_lane 3 (i32x4.replace_lane 3 (local.get 0) (local.get 1)))) + (func (export "f32x4_replace_lane") (param v128 f32) (result f32) + (f32x4.extract_lane 3 (f32x4.replace_lane 3 (local.get 0) (local.get 1)))) + (func (export "i64x2_replace_lane") (param v128 i64) (result i64) + (i64x2.extract_lane 1 (i64x2.replace_lane 1 (local.get 0) (local.get 1)))) + (func (export "f64x2_replace_lane") (param v128 f64) (result f64) + (f64x2.extract_lane 1 (f64x2.replace_lane 1 (local.get 0) (local.get 1)))) + + ;; i8x16.replace outputs as shuffle operand + (func (export "as-v8x16_swizzle-operand") (param v128 i32 v128) (result v128) + (i8x16.swizzle (i8x16.replace_lane 0 (local.get 0) (local.get 1)) (local.get 2))) + (func (export "as-v8x16_shuffle-operands") (param v128 i32 v128 i32) (result v128) + (i8x16.shuffle 16 1 18 3 20 5 22 7 24 9 26 11 28 13 30 15 + (i8x16.replace_lane 0 (local.get 0) (local.get 1)) + (i8x16.replace_lane 15 (local.get 2) (local.get 3)))) +) + +(assert_return (invoke "i8x16_extract_lane_s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) (v128.const i8x16 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16_extract_lane_u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) (v128.const i8x16 255 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8_extract_lane_s" (v128.const i16x8 0 0 0 0 0 0 0 0) (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) (v128.const i16x8 -1 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8_extract_lane_u" (v128.const i16x8 0 0 0 0 0 0 0 0) (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) (v128.const i16x8 65535 0 0 0 0 0 0 0)) +(assert_return (invoke "i32x4_extract_lane" (v128.const i32x4 0 0 0 0) (v128.const i32x4 0x10000 -1 -1 -1)) (v128.const i32x4 65536 0 0 0)) +(assert_return (invoke "f32x4_extract_lane" (v128.const f32x4 0 0 0 0) (v128.const f32x4 1e38 nan nan nan)) (v128.const f32x4 1e38 0 0 0)) +(assert_return (invoke "i8x16_replace_lane-s" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 255)) (i32.const -1)) +(assert_return (invoke "i8x16_replace_lane-u" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 255)) (i32.const 255)) +(assert_return (invoke "i16x8_replace_lane-s" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 65535)) (i32.const -1)) +(assert_return (invoke "i16x8_replace_lane-u" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 65535)) (i32.const 65535)) +(assert_return (invoke "i32x4_replace_lane" (v128.const i32x4 0 0 0 0) (i32.const -1)) (i32.const -1)) +(assert_return (invoke "f32x4_replace_lane" (v128.const f32x4 0 0 0 0) (f32.const 1.25)) (f32.const 1.25)) + +(assert_return (invoke "i64x2_extract_lane" (v128.const i64x2 0 0) (v128.const i64x2 0xffffffffffffffff -1)) (v128.const i64x2 0xffffffffffffffff 0)) +(assert_return (invoke "f64x2_extract_lane" (v128.const f64x2 0 0) (v128.const f64x2 1e308 nan)) (v128.const f64x2 1e308 0)) +(assert_return (invoke "i64x2_replace_lane" (v128.const i64x2 0 0) (i64.const -1)) (i64.const -1)) +(assert_return (invoke "f64x2_replace_lane" (v128.const f64x2 0 0) (f64.const 2.5)) (f64.const 2.5)) + +(assert_return (invoke "as-v8x16_swizzle-operand" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) (i32.const 255) + (v128.const i8x16 -1 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1)) + (v128.const i8x16 0 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1)) +(assert_return (invoke "as-v8x16_shuffle-operands" + (v128.const i8x16 0 255 0 255 15 255 0 255 255 255 0 255 127 255 0 255) (i32.const 1) + (v128.const i8x16 0x55 0 0x55 0 0x55 0 0x55 0 0x55 0 0x55 0 0x55 1 0x55 -1) (i32.const 0)) + (v128.const i8x16 0x55 0xff 0x55 0xff 0x55 0xff 0x55 0xff 0x55 0xff 0x55 0xff 0x55 0xff 0x55 0xff)) + +;; Combination with other SIMD instructions + +(module + ;; Constructing SIMD values + (func (export "as-i8x16_splat-operand") (param v128) (result v128) + (i8x16.splat (i8x16.extract_lane_s 0 (local.get 0)))) + (func (export "as-i16x8_splat-operand") (param v128) (result v128) + (i16x8.splat (i16x8.extract_lane_u 0 (local.get 0)))) + (func (export "as-i32x4_splat-operand") (param v128) (result v128) + (i32x4.splat (i32x4.extract_lane 0 (local.get 0)))) + (func (export "as-f32x4_splat-operand") (param v128) (result v128) + (f32x4.splat (f32x4.extract_lane 0 (local.get 0)))) + (func (export "as-i64x2_splat-operand") (param v128) (result v128) + (i64x2.splat (i64x2.extract_lane 0 (local.get 0)))) + (func (export "as-f64x2_splat-operand") (param v128) (result v128) + (f64x2.splat (f64x2.extract_lane 0 (local.get 0)))) + + ;; Integer arithmetic + (func (export "as-i8x16_add-operands") (param v128 i32 v128 i32) (result v128) + (i8x16.add (i8x16.replace_lane 0 (local.get 0) (local.get 1)) (i8x16.replace_lane 15 (local.get 2) (local.get 3)))) + (func (export "as-i16x8_add-operands") (param v128 i32 v128 i32) (result v128) + (i16x8.add (i16x8.replace_lane 0 (local.get 0) (local.get 1)) (i16x8.replace_lane 7 (local.get 2) (local.get 3)))) + (func (export "as-i32x4_add-operands") (param v128 i32 v128 i32) (result v128) + (i32x4.add (i32x4.replace_lane 0 (local.get 0) (local.get 1)) (i32x4.replace_lane 3 (local.get 2) (local.get 3)))) + (func (export "as-i64x2_add-operands") (param v128 i64 v128 i64) (result v128) + (i64x2.add (i64x2.replace_lane 0 (local.get 0) (local.get 1)) (i64x2.replace_lane 1 (local.get 2) (local.get 3)))) + + (func (export "swizzle-as-i8x16_add-operands") (param v128 v128 v128 v128) (result v128) + (i8x16.add (i8x16.swizzle (local.get 0) (local.get 1)) (i8x16.swizzle (local.get 2) (local.get 3)))) + (func (export "shuffle-as-i8x16_sub-operands") (param v128 v128 v128 v128) (result v128) + (i8x16.sub (i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 (local.get 0) (local.get 1)) + (i8x16.shuffle 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 (local.get 2) (local.get 3)))) + + ;; Boolean horizontal reductions + (func (export "as-i8x16_any_true-operand") (param v128 i32) (result i32) + (v128.any_true (i8x16.replace_lane 0 (local.get 0) (local.get 1)))) + (func (export "as-i16x8_any_true-operand") (param v128 i32) (result i32) + (v128.any_true (i16x8.replace_lane 0 (local.get 0) (local.get 1)))) + (func (export "as-i32x4_any_true-operand1") (param v128 i32) (result i32) + (v128.any_true (i32x4.replace_lane 0 (local.get 0) (local.get 1)))) + (func (export "as-i32x4_any_true-operand2") (param v128 i64) (result i32) + (v128.any_true (i64x2.replace_lane 0 (local.get 0) (local.get 1)))) + + (func (export "swizzle-as-i8x16_all_true-operands") (param v128 v128) (result i32) + (i8x16.all_true (i8x16.swizzle (local.get 0) (local.get 1)))) + (func (export "shuffle-as-i8x16_any_true-operands") (param v128 v128) (result i32) + (v128.any_true (i8x16.shuffle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 (local.get 0) (local.get 1)))) +) + +(assert_return (invoke "as-i8x16_splat-operand" (v128.const i8x16 0xff 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "as-i16x8_splat-operand" (v128.const i16x8 -1 -1 -1 -1 0 0 0 0)) (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "as-i32x4_splat-operand" (v128.const i32x4 0x10000 0 0 0)) (v128.const i32x4 65536 65536 65536 65536)) +(assert_return (invoke "as-f32x4_splat-operand" (v128.const f32x4 3.14 nan nan nan)) (v128.const f32x4 3.14 3.14 3.14 3.14)) +(assert_return (invoke "as-i64x2_splat-operand" (v128.const i64x2 -1 0)) (v128.const i64x2 -1 -1)) +(assert_return (invoke "as-f64x2_splat-operand" (v128.const f64x2 inf nan)) (v128.const f64x2 inf inf)) +(assert_return (invoke "as-i8x16_add-operands" + (v128.const i8x16 0xff 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16) (i32.const 1) + (v128.const i8x16 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 0xff) (i32.const 1)) + (v128.const i8x16 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17)) +(assert_return (invoke "as-i16x8_add-operands" + (v128.const i16x8 -1 4 9 16 25 36 49 64) (i32.const 1) + (v128.const i16x8 64 49 36 25 16 9 4 -1) (i32.const 1)) + (v128.const i16x8 65 53 45 41 41 45 53 65)) +(assert_return (invoke "as-i32x4_add-operands" + (v128.const i32x4 -1 8 27 64) (i32.const 1) (v128.const i32x4 64 27 8 -1) (i32.const 1)) (v128.const i32x4 65 35 35 65)) +(assert_return (invoke "as-i64x2_add-operands" + (v128.const i64x2 -1 8) (i64.const 1) (v128.const i64x2 64 27) (i64.const 1)) (v128.const i64x2 65 9)) + +(assert_return (invoke "swizzle-as-i8x16_add-operands" + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0)) + (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "shuffle-as-i8x16_sub-operands" + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + (v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0) + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1) + (v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0)) + (v128.const i8x16 -15 -13 -11 -9 -7 -5 -3 -1 1 3 5 7 9 11 13 15)) + +(assert_return (invoke "as-i8x16_any_true-operand" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-i16x8_any_true-operand" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-i32x4_any_true-operand1" (v128.const i32x4 1 0 0 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-i32x4_any_true-operand2" (v128.const i64x2 1 0) (i64.const 0)) (i32.const 0)) + +(assert_return (invoke "swizzle-as-i8x16_all_true-operands" + (v128.const i8x16 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) (i32.const 1)) +(assert_return (invoke "swizzle-as-i8x16_all_true-operands" + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 16)) (i32.const 0)) +(assert_return (invoke "shuffle-as-i8x16_any_true-operands" + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1) + (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) (i32.const 1)) + +;; Load and store + +(module + (memory 1) + (func (export "as-v128_store-operand-1") (param v128 i32) (result v128) + (v128.store (i32.const 0) (i8x16.replace_lane 0 (local.get 0) (local.get 1))) + (v128.load (i32.const 0))) + (func (export "as-v128_store-operand-2") (param v128 i32) (result v128) + (v128.store (i32.const 0) (i16x8.replace_lane 0 (local.get 0) (local.get 1))) + (v128.load (i32.const 0))) + (func (export "as-v128_store-operand-3") (param v128 i32) (result v128) + (v128.store (i32.const 0) (i32x4.replace_lane 0 (local.get 0) (local.get 1))) + (v128.load (i32.const 0))) + (func (export "as-v128_store-operand-4") (param v128 f32) (result v128) + (v128.store (i32.const 0) (f32x4.replace_lane 0 (local.get 0) (local.get 1))) + (v128.load (i32.const 0))) + (func (export "as-v128_store-operand-5") (param v128 i64) (result v128) + (v128.store (i32.const 0) (i64x2.replace_lane 0 (local.get 0) (local.get 1))) + (v128.load (i32.const 0))) + (func (export "as-v128_store-operand-6") (param v128 f64) (result v128) + (v128.store (i32.const 0) (f64x2.replace_lane 0 (local.get 0) (local.get 1))) + (v128.load (i32.const 0))) +) + +(assert_return (invoke "as-v128_store-operand-1" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 1)) (v128.const i8x16 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "as-v128_store-operand-2" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 256)) (v128.const i16x8 0x100 0 0 0 0 0 0 0)) +(assert_return (invoke "as-v128_store-operand-3" (v128.const i32x4 0 0 0 0) (i32.const 0xffffffff)) (v128.const i32x4 -1 0 0 0)) +(assert_return (invoke "as-v128_store-operand-4" (v128.const f32x4 0 0 0 0) (f32.const 3.14)) (v128.const f32x4 3.14 0 0 0)) +(assert_return (invoke "as-v128_store-operand-5" (v128.const i64x2 0 0) (i64.const 0xffffffffffffffff)) (v128.const i64x2 -1 0)) +(assert_return (invoke "as-v128_store-operand-6" (v128.const f64x2 0 0) (f64.const 3.14)) (v128.const f64x2 3.14 0)) + +;; As the argument of wasm core ops + +(module + (global $g (mut v128) (v128.const f32x4 0.0 0.0 0.0 0.0)) + (global $h (mut v128) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (func (export "as-if-condition-value") (param v128) (result i32) + (if (result i32) (i8x16.extract_lane_s 0 (local.get 0)) (then (i32.const 0xff)) (else (i32.const 0)))) + (func (export "as-return-value-1") (param v128 i32) (result v128) + (return (i16x8.replace_lane 0 (local.get 0) (local.get 1)))) + (func (export "as-local_set-value") (param v128) (result i32) (local i32) + (local.set 1 (i32x4.extract_lane 0 (local.get 0))) + (return (local.get 1))) + (func (export "as-global_set-value-1") (param v128 f32) (result v128) + (global.set $g (f32x4.replace_lane 0 (local.get 0) (local.get 1))) + (return (global.get $g))) + + (func (export "as-return-value-2") (param v128 v128) (result v128) + (return (i8x16.swizzle (local.get 0) (local.get 1)))) + (func (export "as-global_set-value-2") (param v128 v128) (result v128) + (global.set $h (i8x16.shuffle 0 1 2 3 4 5 6 7 24 25 26 27 28 29 30 31 (local.get 0) (local.get 1))) + (return (global.get $h))) + + (func (export "as-local_set-value-1") (param v128) (result i64) (local i64) + (local.set 1 (i64x2.extract_lane 0 (local.get 0))) + (return (local.get 1))) + (func (export "as-global_set-value-3") (param v128 f64) (result v128) + (global.set $g (f64x2.replace_lane 0 (local.get 0) (local.get 1))) + (return (global.get $g))) +) + +(assert_return (invoke "as-if-condition-value" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) (i32.const 0)) +(assert_return (invoke "as-return-value-1" (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 1)) (v128.const i16x8 1 0 0 0 0 0 0 0)) +(assert_return (invoke "as-local_set-value" (v128.const i32x4 -1 -1 -1 -1)) (i32.const -1)) +(assert_return (invoke "as-global_set-value-1" (v128.const f32x4 0 0 0 0)(f32.const 3.14)) (v128.const f32x4 3.14 0 0 0)) + +(assert_return (invoke "as-return-value-2" + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1) + (v128.const i8x16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0)) + (v128.const i8x16 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16)) +(assert_return (invoke "as-global_set-value-2" + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1) + (v128.const i8x16 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1)) + (v128.const i8x16 -16 -15 -14 -13 -12 -11 -10 -9 8 7 6 5 4 3 2 1)) + +(assert_return (invoke "as-local_set-value-1" (v128.const i64x2 -1 -1)) (i64.const -1)) +(assert_return (invoke "as-global_set-value-3" (v128.const f64x2 0 0)(f64.const 3.14)) (v128.const f64x2 3.14 0)) + +;; Non-nat lane index + +(assert_malformed (module quote "(func (result i32) (i8x16.extract_lane_u +0x0f (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result f32) (f32x4.extract_lane +03 (v128.const f32x4 0 0 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result i64) (i64x2.extract_lane +1 (v128.const i64x2 0 0)))") "unexpected token") +(assert_malformed (module quote "(func (result v128) (i8x16.replace_lane +015 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 1)))") "unexpected token") +(assert_malformed (module quote "(func (result v128) (i16x8.replace_lane +0x7 (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 1)))") "unexpected token") +(assert_malformed (module quote "(func (result v128) (i32x4.replace_lane +3 (v128.const i32x4 0 0 0 0) (i32.const 1)))") "unexpected token") +(assert_malformed (module quote "(func (result v128) (f64x2.replace_lane +0x01 (v128.const f64x2 0 0) (f64.const 1.0)))") "unexpected token") + +;; Lane index literal + +(module (func (result i32) (i8x16.extract_lane_s 0x0f (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))) +(module (func (result i32) (i16x8.extract_lane_s 0x07 (v128.const i16x8 0 0 0 0 0 0 0 0)))) +(module (func (result i32) (i16x8.extract_lane_u 0x0_7 (v128.const i16x8 0 0 0 0 0 0 0 0)))) +(module (func (result i32) (i32x4.extract_lane 03 (v128.const i32x4 0 0 0 0)))) +(module (func (result f64) (f64x2.extract_lane 0x1 (v128.const f64x2 0 0)))) +(module (func (result v128) (f32x4.replace_lane 0x3 (v128.const f32x4 0 0 0 0) (f32.const 1.0)))) +(module (func (result v128) (i64x2.replace_lane 01 (v128.const i64x2 0 0) (i64.const 1)))) + +;; 1.0 is malformed lane index + +(assert_malformed (module quote "(func (result i32) (i8x16.extract_lane_s 1.0 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))") "unexpected token") + +;; Test operation with empty argument + +(assert_malformed + (module quote + "(func $i8x16.extract_lane_s-1st-arg-empty (result i32)" + " (i8x16.extract_lane_s (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0))" + ")" + ) + "unexpected token" +) +(assert_invalid + (module + (func $i8x16.extract_lane_s-2nd-arg-empty (result i32) + (i8x16.extract_lane_s 0) + ) + ) + "type mismatch" +) +(assert_malformed + (module quote + "(func $i8x16.extract_lane_s-arg-empty (result i32)" + " (i8x16.extract_lane_s)" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func $i16x8.extract_lane_u-1st-arg-empty (result i32)" + " (i16x8.extract_lane_u (v128.const i16x8 0 0 0 0 0 0 0 0))" + ")" + ) + "unexpected token" +) +(assert_invalid + (module + (func $i16x8.extract_lane_u-2nd-arg-empty (result i32) + (i16x8.extract_lane_u 0) + ) + ) + "type mismatch" +) +(assert_malformed + (module quote + "(func $i16x8.extract_lane_u-arg-empty (result i32)" + " (i16x8.extract_lane_u)" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func $i32x4.extract_lane-1st-arg-empty (result i32)" + " (i32x4.extract_lane (v128.const i32x4 0 0 0 0))" + ")" + ) + "unexpected token" +) +(assert_invalid + (module + (func $i32x4.extract_lane-2nd-arg-empty (result i32) + (i32x4.extract_lane 0) + ) + ) + "type mismatch" +) +(assert_malformed + (module quote + "(func $i32x4.extract_lane-arg-empty (result i32)" + " (i32x4.extract_lane)" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func $i64x2.extract_lane-1st-arg-empty (result i64)" + " (i64x2.extract_lane (v128.const i64x2 0 0))" + ")" + ) + "unexpected token" +) +(assert_invalid + (module + (func $i64x2.extract_lane-2nd-arg-empty (result i64) + (i64x2.extract_lane 0) + ) + ) + "type mismatch" +) +(assert_malformed + (module quote + "(func $i64x2.extract_lane-arg-empty (result i64)" + " (i64x2.extract_lane)" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func $f32x4.extract_lane-1st-arg-empty (result f32)" + " (f32x4.extract_lane (v128.const f32x4 0 0 0 0))" + ")" + ) + "unexpected token" +) +(assert_invalid + (module + (func $f32x4.extract_lane-2nd-arg-empty (result f32) + (f32x4.extract_lane 0) + ) + ) + "type mismatch" +) +(assert_malformed + (module quote + "(func $f32x4.extract_lane-arg-empty (result f32)" + " (f32x4.extract_lane)" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func $f64x2.extract_lane-1st-arg-empty (result f64)" + " (f64x2.extract_lane (v128.const f64x2 0 0))" + ")" + ) + "unexpected token" +) +(assert_invalid + (module + (func $f64x2.extract_lane-2nd-arg-empty (result f64) + (f64x2.extract_lane 0) + ) + ) + "type mismatch" +) +(assert_malformed + (module quote + "(func $f64x2.extract_lane-arg-empty (result f64)" + " (f64x2.extract_lane)" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func $i8x16.replace_lane-1st-arg-empty (result v128)" + " (i8x16.replace_lane (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) (i32.const 1))" + ")" + ) + "unexpected token" +) +(assert_invalid + (module + (func $i8x16.replace_lane-2nd-arg-empty (result v128) + (i8x16.replace_lane 0 (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i8x16.replace_lane-3rd-arg-empty (result v128) + (i8x16.replace_lane 0 (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_malformed + (module quote + "(func $i8x16.replace_lane-arg-empty (result v128)" + " (i8x16.replace_lane)" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func $i16x8.replace_lane-1st-arg-empty (result v128)" + " (i16x8.replace_lane (v128.const i16x8 0 0 0 0 0 0 0 0) (i32.const 1))" + ")" + ) + "unexpected token" +) +(assert_invalid + (module + (func $i16x8.replace_lane-2nd-arg-empty (result v128) + (i16x8.replace_lane 0 (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.replace_lane-3rd-arg-empty (result v128) + (i16x8.replace_lane 0 (v128.const i16x8 0 0 0 0 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_malformed + (module quote + "(func $i16x8.replace_lane-arg-empty (result v128)" + " (i16x8.replace_lane)" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func $i32x4.replace_lane-1st-arg-empty (result v128)" + " (i32x4.replace_lane (v128.const i32x4 0 0 0 0) (i32.const 1))" + ")" + ) + "unexpected token" +) +(assert_invalid + (module + (func $i32x4.replace_lane-2nd-arg-empty (result v128) + (i32x4.replace_lane 0 (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.replace_lane-3rd-arg-empty (result v128) + (i32x4.replace_lane 0 (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_malformed + (module quote + "(func $i32x4.replace_lane-arg-empty (result v128)" + " (i32x4.replace_lane)" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func $f32x4.replace_lane-1st-arg-empty (result v128)" + " (f32x4.replace_lane (v128.const f32x4 0 0 0 0) (f32.const 1.0))" + ")" + ) + "unexpected token" +) +(assert_invalid + (module + (func $f32x4.replace_lane-2nd-arg-empty (result v128) + (f32x4.replace_lane 0 (f32.const 1.0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.replace_lane-3rd-arg-empty (result v128) + (f32x4.replace_lane 0 (v128.const f32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_malformed + (module quote + "(func $f32x4.replace_lane-arg-empty (result v128)" + " (f32x4.replace_lane)" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func $i64x2.replace_lane-1st-arg-empty (result v128)" + " (i64x2.replace_lane (v128.const i64x2 0 0) (i64.const 1))" + ")" + ) + "unexpected token" +) +(assert_invalid + (module + (func $i64x2.replace_lane-2nd-arg-empty (result v128) + (i64x2.replace_lane 0 (i64.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.replace_lane-3rd-arg-empty (result v128) + (i64x2.replace_lane 0 (v128.const i64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_malformed + (module quote + "(func $i64x2.replace_lane-arg-empty (result v128)" + " (i64x2.replace_lane)" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func $f64x2.replace_lane-1st-arg-empty (result v128)" + " (f64x2.replace_lane (v128.const f64x2 0 0) (f64.const 1.0))" + ")" + ) + "unexpected token" +) +(assert_invalid + (module + (func $f64x2.replace_lane-2nd-arg-empty (result v128) + (f64x2.replace_lane 0 (f64.const 1.0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.replace_lane-3rd-arg-empty (result v128) + (f64x2.replace_lane 0 (v128.const f64x2 0 0)) + ) + ) + "type mismatch" +) +(assert_malformed + (module quote + "(func $f64x2.replace_lane-arg-empty (result v128)" + " (f64x2.replace_lane)" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(func $i8x16.shuffle-1st-arg-empty (result v128)" + " (i8x16.shuffle" + " (v128.const i8x16 0 1 2 3 5 6 6 7 8 9 10 11 12 13 14 15)" + " (v128.const i8x16 1 2 3 5 6 6 7 8 9 10 11 12 13 14 15 16)" + " )" + ")" + ) + "invalid lane length" +) +(assert_invalid + (module + (func $i8x16.shuffle-2nd-arg-empty (result v128) + (i8x16.shuffle 0 1 2 3 5 6 6 7 8 9 10 11 12 13 14 15 + (v128.const i8x16 1 2 3 5 6 6 7 8 9 10 11 12 13 14 15 16) + ) + ) + ) + "type mismatch" +) +(assert_malformed + (module quote + "(func $i8x16.shuffle-arg-empty (result v128)" + " (i8x16.shuffle)" + ")" + ) + "invalid lane length" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load.wast new file mode 100644 index 000000000..4b2edc160 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load.wast @@ -0,0 +1,188 @@ +;; v128.load operater with normal argument (e.g. (i8x16, i16x8 i32x4)) + +(module + (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\00\01\02\03") + (func (export "v128.load") (result v128) + (v128.load (i32.const 0)) + ) +) + +(assert_return (invoke "v128.load") (v128.const i8x16 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f)) +(assert_return (invoke "v128.load") (v128.const i16x8 0x0100 0x0302 0x0504 0x0706 0x0908 0x0b0a 0x0d0c 0x0f0e)) +(assert_return (invoke "v128.load") (v128.const i32x4 0x03020100 0x07060504 0x0b0a0908 0x0f0e0d0c)) + + +;; v128.load operater as the argument of other SIMD instructions + +(module (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\00\01\02\03") + (func (export "as-i8x16_extract_lane_s-value/0") (result i32) + (i8x16.extract_lane_s 0 (v128.load (i32.const 0))) + ) +) +(assert_return (invoke "as-i8x16_extract_lane_s-value/0") (i32.const 0x00)) + +(module (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\00\01\02\03") + (func (export "as-i8x16.eq-operand") (result v128) + (i8x16.eq (v128.load offset=0 (i32.const 0)) (v128.load offset=16 (i32.const 0))) + ) +) +(assert_return (invoke "as-i8x16.eq-operand") (v128.const i32x4 0xffffffff 0x00000000 0x00000000 0x00000000)) + +(module (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\00\01\02\03") + (func (export "as-v128.not-operand") (result v128) + (v128.not (v128.load (i32.const 0))) + ) + (func (export "as-i8x16.all_true-operand") (result i32) + (i8x16.all_true (v128.load (i32.const 0))) + ) +) +(assert_return (invoke "as-v128.not-operand") (v128.const i32x4 0xfcfdfeff 0xf8f9fafb 0xf4f5f6f7 0xf0f1f2f3)) +(assert_return (invoke "as-i8x16.all_true-operand") (i32.const 0)) + +(module (memory 1) + (data (offset (i32.const 0)) "\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA") + (data (offset (i32.const 16)) "\BB\BB\BB\BB\BB\BB\BB\BB\BB\BB\BB\BB\BB\BB\BB\BB") + (data (offset (i32.const 32)) "\F0\F0\F0\F0\FF\FF\FF\FF\00\00\00\00\FF\00\FF\00") + (func (export "as-v128.bitselect-operand") (result v128) + (v128.bitselect (v128.load (i32.const 0)) (v128.load (i32.const 16)) (v128.load (i32.const 32))) + ) +) +(assert_return (invoke "as-v128.bitselect-operand") (v128.const i32x4 0xabababab 0xaaaaaaaa 0xbbbbbbbb 0xbbaabbaa)) + +(module (memory 1) + (data (offset (i32.const 0)) "\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA") + (func (export "as-i8x16.shl-operand") (result v128) + (i8x16.shl (v128.load (i32.const 0)) (i32.const 1)) + ) +) +(assert_return (invoke "as-i8x16.shl-operand") (v128.const i32x4 0x54545454 0x54545454 0x54545454 0x54545454)) ;; 1010 1000 << 1010 1010 + +(module (memory 1) + (data (offset (i32.const 0)) "\02\00\00\00\02\00\00\00\02\00\00\00\02\00\00\00") + (data (offset (i32.const 16)) "\03\00\00\00\03\00\00\00\03\00\00\00\03\00\00\00") + (func (export "as-add/sub-operand") (result v128) + ;; 2 2 2 2 + 3 3 3 3 = 5 5 5 5 + ;; 5 5 5 5 - 3 3 3 3 = 2 2 2 2 + (i8x16.sub + (i8x16.add (v128.load (i32.const 0)) (v128.load (i32.const 16))) + (v128.load (i32.const 16)) + ) + ) +) +(assert_return (invoke "as-add/sub-operand") (v128.const i32x4 2 2 2 2)) + +(module (memory 1) + (data (offset (i32.const 0)) "\00\00\00\43\00\00\80\3f\66\66\e6\3f\00\00\80\bf") ;; 128 1.0 1.8 -1 + (data (offset (i32.const 16)) "\00\00\00\40\00\00\00\40\00\00\00\40\00\00\00\40") ;; 2.0 2.0 2.0 2.0 + (func (export "as-f32x4.mul-operand") (result v128) + (f32x4.mul (v128.load (i32.const 0)) (v128.load (i32.const 16))) + ) +) +(assert_return (invoke "as-f32x4.mul-operand") (v128.const f32x4 256 2 3.6 -2)) + +(module (memory 1) + (data (offset (i32.const 0)) "\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff") ;; 1111 ... + (func (export "as-f32x4.abs-operand") (result v128) + (f32x4.abs (v128.load (i32.const 0))) + ) +) +(assert_return (invoke "as-f32x4.abs-operand") (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff)) ;; 1111 -> 0111 + +(module (memory 1) + (data (offset (i32.const 0)) "\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA\AA") + (data (offset (i32.const 16)) "\02\00\00\00\02\00\00\00\02\00\00\00\02\00\00\00") + (func (export "as-f32x4.min-operand") (result v128) + (f32x4.min (v128.load (i32.const 0)) (v128.load offset=16 (i32.const 1))) + ) +) +(assert_return (invoke "as-f32x4.min-operand") (v128.const i32x4 0xaaaaaaaa 0xaaaaaaaa 0xaaaaaaaa 0xaaaaaaaa)) ;; signed 1010 < 0010 + +(module (memory 1) + (data (offset (i32.const 0)) "\00\00\00\43\00\00\80\3f\66\66\e6\3f\00\00\80\bf") ;; 128 1.0 1.8 -1 + (func (export "as-i32x4.trunc_sat_f32x4_s-operand") (result v128) + (i32x4.trunc_sat_f32x4_s (v128.load (i32.const 0))) + ) +) +(assert_return (invoke "as-i32x4.trunc_sat_f32x4_s-operand") (v128.const i32x4 128 1 1 -1)) ;; 128 1.0 1.8 -1 -> 128 1 1 -1 + +(module (memory 1) + (data (offset (i32.const 0)) "\02\00\00\00\02\00\00\00\02\00\00\00\02\00\00\00") + (func (export "as-f32x4.convert_i32x4_u-operand") (result v128) + (f32x4.convert_i32x4_u (v128.load (i32.const 0))) + ) +) +(assert_return (invoke "as-f32x4.convert_i32x4_u-operand") (v128.const f32x4 2 2 2 2)) + +(module (memory 1) + (data (offset (i32.const 0)) "\64\65\66\67\68\69\6a\6b\6c\6d\6e\6f\70\71\72\73") ;; 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 + (data (offset (i32.const 16)) "\0f\0e\0d\0c\0b\0a\09\08\07\06\05\04\03\02\01\00") ;; 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 + (func (export "as-i8x16.swizzle-operand") (result v128) + (i8x16.swizzle (v128.load (i32.const 0)) (v128.load offset=15 (i32.const 1))) + ) +) +(assert_return(invoke "as-i8x16.swizzle-operand") (v128.const i8x16 115 114 113 112 111 110 109 108 107 106 105 104 103 102 101 100)) + +(module (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\00\01\02\03") + (func (export "as-br-value") (result v128) + (block (result v128) (br 0 (v128.load (i32.const 0)))) + ) +) +(assert_return (invoke "as-br-value") (v128.const i32x4 0x03020100 0x07060504 0x0b0a0908 0x0f0e0d0c)) + + +;; Unknown operator(e.g. v128.load8, v128.load16, v128.load32) + +(assert_malformed + (module quote + "(memory 1)" + "(func (local v128) (drop (v128.load8 (i32.const 0))))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1)" + "(func (local v128) (drop (v128.load16 (i32.const 0))))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1)" + "(func (local v128) (drop (v128.load32 (i32.const 0))))" + ) + "unknown operator" +) + + +;; Type mismatched (e.g. v128.load(f32.const 0), type address empty) + +(assert_invalid + (module (memory 1) (func (local v128) (drop (v128.load (f32.const 0))))) + "type mismatch" +) +(assert_invalid + (module (memory 1) (func (local v128) (block (br_if 0 (v128.load (i32.const 0)))))) + "type mismatch" +) +(assert_invalid + (module (memory 1) (func (local v128) (v128.load (i32.const 0)))) + "type mismatch" +) + + +;; Type address empty + +(assert_invalid + (module (memory 1) (func (drop (v128.load (local.get 2))))) + "unknown local 2" +) +(assert_invalid + (module (memory 1) (func (drop (v128.load)))) + "type mismatch" +) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load16_lane.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load16_lane.wast new file mode 100644 index 000000000..8c72a5733 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load16_lane.wast @@ -0,0 +1,211 @@ +;; Tests for load lane operations. + + +(module + (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F") + (func (export "v128.load16_lane_0") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane 0 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_1") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane 1 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_2") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane 2 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_3") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane 3 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_4") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane 4 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_5") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane 5 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_6") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane 6 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_7") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane 7 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_0_offset_0") + (param $x v128) (result v128) + (v128.load16_lane offset=0 0 (i32.const 0) (local.get $x))) + (func (export "v128.load16_lane_1_offset_1") + (param $x v128) (result v128) + (v128.load16_lane offset=1 1 (i32.const 0) (local.get $x))) + (func (export "v128.load16_lane_2_offset_2") + (param $x v128) (result v128) + (v128.load16_lane offset=2 2 (i32.const 0) (local.get $x))) + (func (export "v128.load16_lane_3_offset_3") + (param $x v128) (result v128) + (v128.load16_lane offset=3 3 (i32.const 0) (local.get $x))) + (func (export "v128.load16_lane_4_offset_4") + (param $x v128) (result v128) + (v128.load16_lane offset=4 4 (i32.const 0) (local.get $x))) + (func (export "v128.load16_lane_5_offset_5") + (param $x v128) (result v128) + (v128.load16_lane offset=5 5 (i32.const 0) (local.get $x))) + (func (export "v128.load16_lane_6_offset_6") + (param $x v128) (result v128) + (v128.load16_lane offset=6 6 (i32.const 0) (local.get $x))) + (func (export "v128.load16_lane_7_offset_7") + (param $x v128) (result v128) + (v128.load16_lane offset=7 7 (i32.const 0) (local.get $x))) + (func (export "v128.load16_lane_0_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=1 0 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_0_align_2") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=2 0 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_1_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=1 1 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_1_align_2") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=2 1 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_2_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=1 2 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_2_align_2") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=2 2 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_3_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=1 3 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_3_align_2") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=2 3 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_4_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=1 4 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_4_align_2") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=2 4 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_5_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=1 5 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_5_align_2") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=2 5 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_6_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=1 6 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_6_align_2") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=2 6 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_7_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=1 7 (local.get $address) (local.get $x))) + (func (export "v128.load16_lane_7_align_2") + (param $address i32) (param $x v128) (result v128) + (v128.load16_lane align=2 7 (local.get $address) (local.get $x))) +) + +(assert_return (invoke "v128.load16_lane_0" (i32.const 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 256 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_1" (i32.const 1) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 513 0 0 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_2" (i32.const 2) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 770 0 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_3" (i32.const 3) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 1027 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_4" (i32.const 4) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 1284 0 0 0)) +(assert_return (invoke "v128.load16_lane_5" (i32.const 5) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 1541 0 0)) +(assert_return (invoke "v128.load16_lane_6" (i32.const 6) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 1798 0)) +(assert_return (invoke "v128.load16_lane_7" (i32.const 7) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 2055)) +(assert_return (invoke "v128.load16_lane_0_offset_0" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 256 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_1_offset_1" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 513 0 0 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_2_offset_2" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 770 0 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_3_offset_3" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 1027 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_4_offset_4" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 1284 0 0 0)) +(assert_return (invoke "v128.load16_lane_5_offset_5" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 1541 0 0)) +(assert_return (invoke "v128.load16_lane_6_offset_6" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 1798 0)) +(assert_return (invoke "v128.load16_lane_7_offset_7" (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 2055)) +(assert_return (invoke "v128.load16_lane_0_align_1" (i32.const 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 256 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_0_align_2" (i32.const 0) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 256 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_1_align_1" (i32.const 1) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 513 0 0 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_1_align_2" (i32.const 1) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 513 0 0 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_2_align_1" (i32.const 2) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 770 0 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_2_align_2" (i32.const 2) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 770 0 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_3_align_1" (i32.const 3) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 1027 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_3_align_2" (i32.const 3) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 1027 0 0 0 0)) +(assert_return (invoke "v128.load16_lane_4_align_1" (i32.const 4) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 1284 0 0 0)) +(assert_return (invoke "v128.load16_lane_4_align_2" (i32.const 4) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 1284 0 0 0)) +(assert_return (invoke "v128.load16_lane_5_align_1" (i32.const 5) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 1541 0 0)) +(assert_return (invoke "v128.load16_lane_5_align_2" (i32.const 5) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 1541 0 0)) +(assert_return (invoke "v128.load16_lane_6_align_1" (i32.const 6) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 1798 0)) +(assert_return (invoke "v128.load16_lane_6_align_2" (i32.const 6) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 1798 0)) +(assert_return (invoke "v128.load16_lane_7_align_1" (i32.const 7) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 2055)) +(assert_return (invoke "v128.load16_lane_7_align_2" (i32.const 7) + (v128.const i16x8 0 0 0 0 0 0 0 0)) + (v128.const i16x8 0 0 0 0 0 0 0 2055)) + +;; type check +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.load16_lane 0 (local.get $x) (i32.const 0)))) + "type mismatch") + +;; invalid lane index +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.load16_lane 8 (i32.const 0) (local.get $x)))) + "invalid lane index") + +;; invalid memarg alignment +(assert_invalid + (module (memory 1) + (func (param $x v128) (result v128) + (v128.load16_lane align=4 0 (i32.const 0) (local.get $x)))) + "alignment must not be larger than natural") \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load32_lane.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load32_lane.wast new file mode 100644 index 000000000..e67690ff9 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load32_lane.wast @@ -0,0 +1,143 @@ +;; Tests for load lane operations. + + +(module + (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F") + (func (export "v128.load32_lane_0") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane 0 (local.get $address) (local.get $x))) + (func (export "v128.load32_lane_1") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane 1 (local.get $address) (local.get $x))) + (func (export "v128.load32_lane_2") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane 2 (local.get $address) (local.get $x))) + (func (export "v128.load32_lane_3") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane 3 (local.get $address) (local.get $x))) + (func (export "v128.load32_lane_0_offset_0") + (param $x v128) (result v128) + (v128.load32_lane offset=0 0 (i32.const 0) (local.get $x))) + (func (export "v128.load32_lane_1_offset_1") + (param $x v128) (result v128) + (v128.load32_lane offset=1 1 (i32.const 0) (local.get $x))) + (func (export "v128.load32_lane_2_offset_2") + (param $x v128) (result v128) + (v128.load32_lane offset=2 2 (i32.const 0) (local.get $x))) + (func (export "v128.load32_lane_3_offset_3") + (param $x v128) (result v128) + (v128.load32_lane offset=3 3 (i32.const 0) (local.get $x))) + (func (export "v128.load32_lane_0_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane align=1 0 (local.get $address) (local.get $x))) + (func (export "v128.load32_lane_0_align_2") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane align=2 0 (local.get $address) (local.get $x))) + (func (export "v128.load32_lane_0_align_4") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane align=4 0 (local.get $address) (local.get $x))) + (func (export "v128.load32_lane_1_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane align=1 1 (local.get $address) (local.get $x))) + (func (export "v128.load32_lane_1_align_2") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane align=2 1 (local.get $address) (local.get $x))) + (func (export "v128.load32_lane_1_align_4") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane align=4 1 (local.get $address) (local.get $x))) + (func (export "v128.load32_lane_2_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane align=1 2 (local.get $address) (local.get $x))) + (func (export "v128.load32_lane_2_align_2") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane align=2 2 (local.get $address) (local.get $x))) + (func (export "v128.load32_lane_2_align_4") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane align=4 2 (local.get $address) (local.get $x))) + (func (export "v128.load32_lane_3_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane align=1 3 (local.get $address) (local.get $x))) + (func (export "v128.load32_lane_3_align_2") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane align=2 3 (local.get $address) (local.get $x))) + (func (export "v128.load32_lane_3_align_4") + (param $address i32) (param $x v128) (result v128) + (v128.load32_lane align=4 3 (local.get $address) (local.get $x))) +) + +(assert_return (invoke "v128.load32_lane_0" (i32.const 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 50462976 0 0 0)) +(assert_return (invoke "v128.load32_lane_1" (i32.const 1) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 67305985 0 0)) +(assert_return (invoke "v128.load32_lane_2" (i32.const 2) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 84148994 0)) +(assert_return (invoke "v128.load32_lane_3" (i32.const 3) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 100992003)) +(assert_return (invoke "v128.load32_lane_0_offset_0" (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 50462976 0 0 0)) +(assert_return (invoke "v128.load32_lane_1_offset_1" (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 67305985 0 0)) +(assert_return (invoke "v128.load32_lane_2_offset_2" (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 84148994 0)) +(assert_return (invoke "v128.load32_lane_3_offset_3" (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 100992003)) +(assert_return (invoke "v128.load32_lane_0_align_1" (i32.const 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 50462976 0 0 0)) +(assert_return (invoke "v128.load32_lane_0_align_2" (i32.const 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 50462976 0 0 0)) +(assert_return (invoke "v128.load32_lane_0_align_4" (i32.const 0) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 50462976 0 0 0)) +(assert_return (invoke "v128.load32_lane_1_align_1" (i32.const 1) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 67305985 0 0)) +(assert_return (invoke "v128.load32_lane_1_align_2" (i32.const 1) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 67305985 0 0)) +(assert_return (invoke "v128.load32_lane_1_align_4" (i32.const 1) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 67305985 0 0)) +(assert_return (invoke "v128.load32_lane_2_align_1" (i32.const 2) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 84148994 0)) +(assert_return (invoke "v128.load32_lane_2_align_2" (i32.const 2) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 84148994 0)) +(assert_return (invoke "v128.load32_lane_2_align_4" (i32.const 2) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 84148994 0)) +(assert_return (invoke "v128.load32_lane_3_align_1" (i32.const 3) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 100992003)) +(assert_return (invoke "v128.load32_lane_3_align_2" (i32.const 3) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 100992003)) +(assert_return (invoke "v128.load32_lane_3_align_4" (i32.const 3) + (v128.const i32x4 0 0 0 0)) + (v128.const i32x4 0 0 0 100992003)) + +;; type check +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.load32_lane 0 (local.get $x) (i32.const 0)))) + "type mismatch") + +;; invalid lane index +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.load32_lane 4 (i32.const 0) (local.get $x)))) + "invalid lane index") + +;; invalid memarg alignment +(assert_invalid + (module (memory 1) + (func (param $x v128) (result v128) + (v128.load32_lane align=8 0 (i32.const 0) (local.get $x)))) + "alignment must not be larger than natural") \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load64_lane.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load64_lane.wast new file mode 100644 index 000000000..5883a6eae --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load64_lane.wast @@ -0,0 +1,97 @@ +;; Tests for load lane operations. + + +(module + (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F") + (func (export "v128.load64_lane_0") + (param $address i32) (param $x v128) (result v128) + (v128.load64_lane 0 (local.get $address) (local.get $x))) + (func (export "v128.load64_lane_1") + (param $address i32) (param $x v128) (result v128) + (v128.load64_lane 1 (local.get $address) (local.get $x))) + (func (export "v128.load64_lane_0_offset_0") + (param $x v128) (result v128) + (v128.load64_lane offset=0 0 (i32.const 0) (local.get $x))) + (func (export "v128.load64_lane_1_offset_1") + (param $x v128) (result v128) + (v128.load64_lane offset=1 1 (i32.const 0) (local.get $x))) + (func (export "v128.load64_lane_0_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load64_lane align=1 0 (local.get $address) (local.get $x))) + (func (export "v128.load64_lane_0_align_2") + (param $address i32) (param $x v128) (result v128) + (v128.load64_lane align=2 0 (local.get $address) (local.get $x))) + (func (export "v128.load64_lane_0_align_4") + (param $address i32) (param $x v128) (result v128) + (v128.load64_lane align=4 0 (local.get $address) (local.get $x))) + (func (export "v128.load64_lane_0_align_8") + (param $address i32) (param $x v128) (result v128) + (v128.load64_lane align=8 0 (local.get $address) (local.get $x))) + (func (export "v128.load64_lane_1_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load64_lane align=1 1 (local.get $address) (local.get $x))) + (func (export "v128.load64_lane_1_align_2") + (param $address i32) (param $x v128) (result v128) + (v128.load64_lane align=2 1 (local.get $address) (local.get $x))) + (func (export "v128.load64_lane_1_align_4") + (param $address i32) (param $x v128) (result v128) + (v128.load64_lane align=4 1 (local.get $address) (local.get $x))) + (func (export "v128.load64_lane_1_align_8") + (param $address i32) (param $x v128) (result v128) + (v128.load64_lane align=8 1 (local.get $address) (local.get $x))) +) + +(assert_return (invoke "v128.load64_lane_0" (i32.const 0) + (v128.const i64x2 0 0)) + (v128.const i64x2 506097522914230528 0)) +(assert_return (invoke "v128.load64_lane_1" (i32.const 1) + (v128.const i64x2 0 0)) + (v128.const i64x2 0 578437695752307201)) +(assert_return (invoke "v128.load64_lane_0_offset_0" (v128.const i64x2 0 0)) + (v128.const i64x2 506097522914230528 0)) +(assert_return (invoke "v128.load64_lane_1_offset_1" (v128.const i64x2 0 0)) + (v128.const i64x2 0 578437695752307201)) +(assert_return (invoke "v128.load64_lane_0_align_1" (i32.const 0) + (v128.const i64x2 0 0)) + (v128.const i64x2 506097522914230528 0)) +(assert_return (invoke "v128.load64_lane_0_align_2" (i32.const 0) + (v128.const i64x2 0 0)) + (v128.const i64x2 506097522914230528 0)) +(assert_return (invoke "v128.load64_lane_0_align_4" (i32.const 0) + (v128.const i64x2 0 0)) + (v128.const i64x2 506097522914230528 0)) +(assert_return (invoke "v128.load64_lane_0_align_8" (i32.const 0) + (v128.const i64x2 0 0)) + (v128.const i64x2 506097522914230528 0)) +(assert_return (invoke "v128.load64_lane_1_align_1" (i32.const 1) + (v128.const i64x2 0 0)) + (v128.const i64x2 0 578437695752307201)) +(assert_return (invoke "v128.load64_lane_1_align_2" (i32.const 1) + (v128.const i64x2 0 0)) + (v128.const i64x2 0 578437695752307201)) +(assert_return (invoke "v128.load64_lane_1_align_4" (i32.const 1) + (v128.const i64x2 0 0)) + (v128.const i64x2 0 578437695752307201)) +(assert_return (invoke "v128.load64_lane_1_align_8" (i32.const 1) + (v128.const i64x2 0 0)) + (v128.const i64x2 0 578437695752307201)) + +;; type check +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.load64_lane 0 (local.get $x) (i32.const 0)))) + "type mismatch") + +;; invalid lane index +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.load64_lane 2 (i32.const 0) (local.get $x)))) + "invalid lane index") + +;; invalid memarg alignment +(assert_invalid + (module (memory 1) + (func (param $x v128) (result v128) + (v128.load64_lane align=16 0 (i32.const 0) (local.get $x)))) + "alignment must not be larger than natural") \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load8_lane.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load8_lane.wast new file mode 100644 index 000000000..d0706f535 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load8_lane.wast @@ -0,0 +1,299 @@ +;; Tests for load lane operations. + + +(module + (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F") + (func (export "v128.load8_lane_0") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 0 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 1 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_2") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 2 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_3") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 3 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_4") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 4 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_5") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 5 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_6") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 6 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_7") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 7 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_8") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 8 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_9") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 9 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_10") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 10 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_11") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 11 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_12") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 12 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_13") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 13 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_14") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 14 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_15") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane 15 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_0_offset_0") + (param $x v128) (result v128) + (v128.load8_lane offset=0 0 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_1_offset_1") + (param $x v128) (result v128) + (v128.load8_lane offset=1 1 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_2_offset_2") + (param $x v128) (result v128) + (v128.load8_lane offset=2 2 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_3_offset_3") + (param $x v128) (result v128) + (v128.load8_lane offset=3 3 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_4_offset_4") + (param $x v128) (result v128) + (v128.load8_lane offset=4 4 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_5_offset_5") + (param $x v128) (result v128) + (v128.load8_lane offset=5 5 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_6_offset_6") + (param $x v128) (result v128) + (v128.load8_lane offset=6 6 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_7_offset_7") + (param $x v128) (result v128) + (v128.load8_lane offset=7 7 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_8_offset_8") + (param $x v128) (result v128) + (v128.load8_lane offset=8 8 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_9_offset_9") + (param $x v128) (result v128) + (v128.load8_lane offset=9 9 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_10_offset_10") + (param $x v128) (result v128) + (v128.load8_lane offset=10 10 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_11_offset_11") + (param $x v128) (result v128) + (v128.load8_lane offset=11 11 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_12_offset_12") + (param $x v128) (result v128) + (v128.load8_lane offset=12 12 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_13_offset_13") + (param $x v128) (result v128) + (v128.load8_lane offset=13 13 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_14_offset_14") + (param $x v128) (result v128) + (v128.load8_lane offset=14 14 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_15_offset_15") + (param $x v128) (result v128) + (v128.load8_lane offset=15 15 (i32.const 0) (local.get $x))) + (func (export "v128.load8_lane_0_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 0 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_1_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 1 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_2_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 2 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_3_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 3 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_4_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 4 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_5_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 5 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_6_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 6 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_7_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 7 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_8_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 8 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_9_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 9 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_10_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 10 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_11_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 11 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_12_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 12 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_13_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 13 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_14_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 14 (local.get $address) (local.get $x))) + (func (export "v128.load8_lane_15_align_1") + (param $address i32) (param $x v128) (result v128) + (v128.load8_lane align=1 15 (local.get $address) (local.get $x))) +) + +(assert_return (invoke "v128.load8_lane_0" (i32.const 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_1" (i32.const 1) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_2" (i32.const 2) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_3" (i32.const 3) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_4" (i32.const 4) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_5" (i32.const 5) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_6" (i32.const 6) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_7" (i32.const 7) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_8" (i32.const 8) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_9" (i32.const 9) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_10" (i32.const 10) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_11" (i32.const 11) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_12" (i32.const 12) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0)) +(assert_return (invoke "v128.load8_lane_13" (i32.const 13) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0)) +(assert_return (invoke "v128.load8_lane_14" (i32.const 14) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 0)) +(assert_return (invoke "v128.load8_lane_15" (i32.const 15) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15)) +(assert_return (invoke "v128.load8_lane_0_offset_0" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_1_offset_1" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_2_offset_2" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_3_offset_3" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_4_offset_4" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_5_offset_5" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_6_offset_6" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_7_offset_7" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_8_offset_8" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_9_offset_9" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_10_offset_10" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_11_offset_11" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_12_offset_12" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0)) +(assert_return (invoke "v128.load8_lane_13_offset_13" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0)) +(assert_return (invoke "v128.load8_lane_14_offset_14" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 0)) +(assert_return (invoke "v128.load8_lane_15_offset_15" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15)) +(assert_return (invoke "v128.load8_lane_0_align_1" (i32.const 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_1_align_1" (i32.const 1) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_2_align_1" (i32.const 2) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_3_align_1" (i32.const 3) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_4_align_1" (i32.const 4) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_5_align_1" (i32.const 5) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_6_align_1" (i32.const 6) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_7_align_1" (i32.const 7) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_8_align_1" (i32.const 8) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_9_align_1" (i32.const 9) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_10_align_1" (i32.const 10) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_11_align_1" (i32.const 11) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0)) +(assert_return (invoke "v128.load8_lane_12_align_1" (i32.const 12) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0)) +(assert_return (invoke "v128.load8_lane_13_align_1" (i32.const 13) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0)) +(assert_return (invoke "v128.load8_lane_14_align_1" (i32.const 14) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 0)) +(assert_return (invoke "v128.load8_lane_15_align_1" (i32.const 15) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15)) + +;; type check +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.load8_lane 0 (local.get $x) (i32.const 0)))) + "type mismatch") + +;; invalid lane index +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.load8_lane 16 (i32.const 0) (local.get $x)))) + "invalid lane index") + +;; invalid memarg alignment +(assert_invalid + (module (memory 1) + (func (param $x v128) (result v128) + (v128.load8_lane align=2 0 (i32.const 0) (local.get $x)))) + "alignment must not be larger than natural") \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load_extend.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load_extend.wast new file mode 100644 index 000000000..b9982bbfd --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load_extend.wast @@ -0,0 +1,384 @@ +;; Load and Extend test cases + +(module + (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F\80\81\82\83\84\85\86\87\88\89") + (data (i32.const 65520) "\0A\0B\0C\0D\0E\0F\80\81\82\83\84\85\86\87\88\89") + + (func (export "v128.load8x8_s") (param $0 i32) (result v128) + (v128.load8x8_s (local.get $0)) + ) + (func (export "v128.load8x8_u") (param $0 i32) (result v128) + (v128.load8x8_u (local.get $0)) + ) + (func (export "v128.load16x4_s") (param $0 i32) (result v128) + (v128.load16x4_s (local.get $0)) + ) + (func (export "v128.load16x4_u") (param $0 i32) (result v128) + (v128.load16x4_u (local.get $0)) + ) + (func (export "v128.load32x2_s") (param $0 i32) (result v128) + (v128.load32x2_s (local.get $0)) + ) + (func (export "v128.load32x2_u") (param $0 i32) (result v128) + (v128.load32x2_u (local.get $0)) + ) + + ;; load by a constant amount + (func (export "v128.load8x8_s_const0") (result v128) + (v128.load8x8_s (i32.const 0)) + ) + (func (export "v128.load8x8_u_const8") (result v128) + (v128.load8x8_u (i32.const 8)) + ) + (func (export "v128.load16x4_s_const10") (result v128) + (v128.load16x4_s (i32.const 10)) + ) + (func (export "v128.load16x4_u_const20") (result v128) + (v128.load16x4_u (i32.const 20)) + ) + (func (export "v128.load32x2_s_const65520") (result v128) + (v128.load32x2_s (i32.const 65520)) + ) + (func (export "v128.load32x2_u_const65526") (result v128) + (v128.load32x2_u (i32.const 65526)) + ) + + ;; load data with different offset/align arguments + ;; i16x8 + (func (export "v128.load8x8_s_offset0") (param $0 i32) (result v128) + (v128.load8x8_s offset=0 (local.get $0)) + ) + (func (export "v128.load8x8_s_align1") (param $0 i32) (result v128) + (v128.load8x8_s align=1 (local.get $0)) + ) + (func (export "v128.load8x8_s_offset0_align1") (param $0 i32) (result v128) + (v128.load8x8_s offset=0 align=1 (local.get $0)) + ) + (func (export "v128.load8x8_s_offset1_align1") (param $0 i32) (result v128) + (v128.load8x8_s offset=1 align=1 (local.get $0)) + ) + (func (export "v128.load8x8_s_offset10_align4") (param $0 i32) (result v128) + (v128.load8x8_s offset=10 align=4 (local.get $0)) + ) + (func (export "v128.load8x8_s_offset20_align8") (param $0 i32) (result v128) + (v128.load8x8_s offset=20 align=8 (local.get $0)) + ) + (func (export "v128.load8x8_u_offset0") (param $0 i32) (result v128) + (v128.load8x8_u offset=0 (local.get $0)) + ) + (func (export "v128.load8x8_u_align1") (param $0 i32) (result v128) + (v128.load8x8_u align=1 (local.get $0)) + ) + (func (export "v128.load8x8_u_offset0_align1") (param $0 i32) (result v128) + (v128.load8x8_u offset=0 align=1 (local.get $0)) + ) + (func (export "v128.load8x8_u_offset1_align1") (param $0 i32) (result v128) + (v128.load8x8_u offset=1 align=1 (local.get $0)) + ) + (func (export "v128.load8x8_u_offset10_align4") (param $0 i32) (result v128) + (v128.load8x8_u offset=10 align=4 (local.get $0)) + ) + (func (export "v128.load8x8_u_offset20_align8") (param $0 i32) (result v128) + (v128.load8x8_u offset=20 align=8 (local.get $0)) + ) + ;; i32x4 + (func (export "v128.load16x4_s_offset0") (param $0 i32) (result v128) + (v128.load16x4_s offset=0 (local.get $0)) + ) + (func (export "v128.load16x4_s_align1") (param $0 i32) (result v128) + (v128.load16x4_s align=1 (local.get $0)) + ) + (func (export "v128.load16x4_s_offset0_align1") (param $0 i32) (result v128) + (v128.load16x4_s offset=0 align=1 (local.get $0)) + ) + (func (export "v128.load16x4_s_offset1_align1") (param $0 i32) (result v128) + (v128.load16x4_s offset=1 align=1 (local.get $0)) + ) + (func (export "v128.load16x4_s_offset10_align4") (param $0 i32) (result v128) + (v128.load16x4_s offset=10 align=4 (local.get $0)) + ) + (func (export "v128.load16x4_s_offset20_align8") (param $0 i32) (result v128) + (v128.load16x4_s offset=20 align=8 (local.get $0)) + ) + (func (export "v128.load16x4_u_offset0") (param $0 i32) (result v128) + (v128.load16x4_u offset=0 (local.get $0)) + ) + (func (export "v128.load16x4_u_align1") (param $0 i32) (result v128) + (v128.load16x4_u align=1 (local.get $0)) + ) + (func (export "v128.load16x4_u_offset0_align1") (param $0 i32) (result v128) + (v128.load16x4_u offset=0 align=1 (local.get $0)) + ) + (func (export "v128.load16x4_u_offset1_align1") (param $0 i32) (result v128) + (v128.load16x4_u offset=1 align=1 (local.get $0)) + ) + (func (export "v128.load16x4_u_offset10_align4") (param $0 i32) (result v128) + (v128.load16x4_u offset=10 align=4 (local.get $0)) + ) + (func (export "v128.load16x4_u_offset20_align8") (param $0 i32) (result v128) + (v128.load16x4_u offset=20 align=8 (local.get $0)) + ) + ;; i64x2 + (func (export "v128.load32x2_s_offset0") (param $0 i32) (result v128) + (v128.load32x2_s offset=0 (local.get $0)) + ) + (func (export "v128.load32x2_s_align1") (param $0 i32) (result v128) + (v128.load32x2_s align=1 (local.get $0)) + ) + (func (export "v128.load32x2_s_offset0_align1") (param $0 i32) (result v128) + (v128.load32x2_s offset=0 align=1 (local.get $0)) + ) + (func (export "v128.load32x2_s_offset1_align1") (param $0 i32) (result v128) + (v128.load32x2_s offset=1 align=1 (local.get $0)) + ) + (func (export "v128.load32x2_s_offset10_align4") (param $0 i32) (result v128) + (v128.load32x2_s offset=10 align=4 (local.get $0)) + ) + (func (export "v128.load32x2_s_offset20_align8") (param $0 i32) (result v128) + (v128.load32x2_s offset=20 align=8 (local.get $0)) + ) + (func (export "v128.load32x2_u_offset0") (param $0 i32) (result v128) + (v128.load32x2_u offset=0 (local.get $0)) + ) + (func (export "v128.load32x2_u_align1") (param $0 i32) (result v128) + (v128.load32x2_u align=1 (local.get $0)) + ) + (func (export "v128.load32x2_u_offset0_align1") (param $0 i32) (result v128) + (v128.load32x2_u offset=0 align=1 (local.get $0)) + ) + (func (export "v128.load32x2_u_offset1_align1") (param $0 i32) (result v128) + (v128.load32x2_u offset=1 align=1 (local.get $0)) + ) + (func (export "v128.load32x2_u_offset10_align4") (param $0 i32) (result v128) + (v128.load32x2_u offset=10 align=4 (local.get $0)) + ) + (func (export "v128.load32x2_u_offset20_align8") (param $0 i32) (result v128) + (v128.load32x2_u offset=20 align=8 (local.get $0)) + ) +) + + +;; normal +(assert_return (invoke "v128.load8x8_s" (i32.const 0)) (v128.const i16x8 0x0000 0x0001 0x0002 0x0003 0x0004 0x0005 0x0006 0x0007)) +(assert_return (invoke "v128.load8x8_u" (i32.const 0)) (v128.const i16x8 0x0000 0x0001 0x0002 0x0003 0x0004 0x0005 0x0006 0x0007)) +(assert_return (invoke "v128.load16x4_s" (i32.const 0)) (v128.const i32x4 0x00000100 0x00000302 0x00000504 0x00000706)) +(assert_return (invoke "v128.load16x4_u" (i32.const 0)) (v128.const i32x4 0x00000100 0x00000302 0x00000504 0x00000706)) +(assert_return (invoke "v128.load32x2_s" (i32.const 0)) (v128.const i64x2 0x0000000003020100 0x0000000007060504)) +(assert_return (invoke "v128.load32x2_u" (i32.const 0)) (v128.const i64x2 0x0000000003020100 0x0000000007060504)) +(assert_return (invoke "v128.load8x8_s" (i32.const 10)) (v128.const i16x8 0x000A 0x000B 0x000C 0x000D 0x000E 0x000F 0xFF80 0xFF81)) +(assert_return (invoke "v128.load8x8_u" (i32.const 10)) (v128.const i16x8 0x000A 0x000B 0x000C 0x000D 0x000E 0x000F 0x0080 0x0081)) +(assert_return (invoke "v128.load16x4_s" (i32.const 10)) (v128.const i32x4 0x00000B0A 0x00000D0C 0x00000F0E 0xFFFF8180)) +(assert_return (invoke "v128.load16x4_u" (i32.const 10)) (v128.const i32x4 0x00000B0A 0x00000D0C 0x00000F0E 0x00008180)) +(assert_return (invoke "v128.load32x2_s" (i32.const 10)) (v128.const i64x2 0x000000000D0C0B0A 0xFFFFFFFF81800F0E)) +(assert_return (invoke "v128.load32x2_u" (i32.const 10)) (v128.const i64x2 0x000000000D0C0B0A 0x0000000081800F0E)) +(assert_return (invoke "v128.load8x8_s" (i32.const 20)) (v128.const i16x8 0xff84 0xff85 0xff86 0xff87 0xff88 0xff89 0x0000 0x0000)) +(assert_return (invoke "v128.load8x8_u" (i32.const 20)) (v128.const i16x8 0x0084 0x0085 0x0086 0x0087 0x0088 0x0089 0x0000 0x0000)) +(assert_return (invoke "v128.load16x4_s" (i32.const 20)) (v128.const i32x4 0xffff8584 0xffff8786 0xffff8988 0x00000000)) +(assert_return (invoke "v128.load16x4_u" (i32.const 20)) (v128.const i32x4 0x00008584 0x00008786 0x00008988 0x00000000)) +(assert_return (invoke "v128.load32x2_s" (i32.const 20)) (v128.const i64x2 0xFFFFFFFF87868584 0x0000000000008988)) +(assert_return (invoke "v128.load32x2_u" (i32.const 20)) (v128.const i64x2 0x0000000087868584 0x0000000000008988)) + +;; load by a constant amount +(assert_return (invoke "v128.load8x8_s_const0") (v128.const i16x8 0x0000 0x0001 0x0002 0x0003 0x0004 0x0005 0x0006 0x0007)) +(assert_return (invoke "v128.load8x8_u_const8") (v128.const i16x8 0x0008 0x0009 0x000A 0x000B 0x000C 0x000D 0x000E 0x000F)) +(assert_return (invoke "v128.load16x4_s_const10") (v128.const i32x4 0x00000B0A 0x00000D0C 0x00000F0E 0xFFFF8180)) +(assert_return (invoke "v128.load16x4_u_const20") (v128.const i32x4 0x00008584 0x00008786 0x00008988 0x00000000)) +(assert_return (invoke "v128.load32x2_s_const65520") (v128.const i64x2 0x000000000D0C0B0A 0xFFFFFFFF81800F0E)) +(assert_return (invoke "v128.load32x2_u_const65526") (v128.const i64x2 0x0000000083828180 0x0000000087868584)) + +;; load data with different offset/align arguments +;; i16x8 +(assert_return (invoke "v128.load8x8_s_offset0" (i32.const 0)) (v128.const i16x8 0x0000 0x0001 0x0002 0x0003 0x0004 0x0005 0x0006 0x0007)) +(assert_return (invoke "v128.load8x8_s_align1" (i32.const 1)) (v128.const i16x8 0x0001 0x0002 0x0003 0x0004 0x0005 0x0006 0x0007 0x0008)) +(assert_return (invoke "v128.load8x8_s_offset0_align1" (i32.const 2)) (v128.const i16x8 0x0002 0x0003 0x0004 0x0005 0x0006 0x0007 0x0008 0x0009)) +(assert_return (invoke "v128.load8x8_s_offset10_align4" (i32.const 3)) (v128.const i16x8 0x000D 0x000E 0x000F 0xFF80 0xFF81 0xFF82 0xFF83 0xFF84)) +(assert_return (invoke "v128.load8x8_s_offset20_align8" (i32.const 4)) (v128.const i16x8 0xFF88 0xFF89 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000)) +(assert_return (invoke "v128.load8x8_u_offset0" (i32.const 0)) (v128.const i16x8 0x0000 0x0001 0x0002 0x0003 0x0004 0x0005 0x0006 0x0007)) +(assert_return (invoke "v128.load8x8_u_align1" (i32.const 1)) (v128.const i16x8 0x0001 0x0002 0x0003 0x0004 0x0005 0x0006 0x0007 0x0008)) +(assert_return (invoke "v128.load8x8_u_offset0_align1" (i32.const 2)) (v128.const i16x8 0x0002 0x0003 0x0004 0x0005 0x0006 0x0007 0x0008 0x0009)) +(assert_return (invoke "v128.load8x8_u_offset10_align4" (i32.const 3)) (v128.const i16x8 0x000D 0x000E 0x000F 0x0080 0x0081 0x0082 0x0083 0x0084)) +(assert_return (invoke "v128.load8x8_u_offset20_align8" (i32.const 4)) (v128.const i16x8 0x0088 0x0089 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000)) +;; i32x4 +(assert_return (invoke "v128.load16x4_s_offset0" (i32.const 0)) (v128.const i32x4 0x00000100 0x00000302 0x00000504 0x00000706)) +(assert_return (invoke "v128.load16x4_s_align1" (i32.const 1)) (v128.const i32x4 0x00000201 0x00000403 0x00000605 0x00000807)) +(assert_return (invoke "v128.load16x4_s_offset0_align1" (i32.const 2)) (v128.const i32x4 0x00000302 0x00000504 0x00000706 0x00000908)) +(assert_return (invoke "v128.load16x4_s_offset10_align4" (i32.const 3)) (v128.const i32x4 0x00000E0D 0xFFFF800F 0xFFFF8281 0xFFFF8483)) +(assert_return (invoke "v128.load16x4_s_offset20_align8" (i32.const 4)) (v128.const i32x4 0xFFFF8988 0x00000000 0x00000000 0x00000000)) +(assert_return (invoke "v128.load16x4_u_offset0" (i32.const 0)) (v128.const i32x4 0x00000100 0x00000302 0x00000504 0x00000706)) +(assert_return (invoke "v128.load16x4_u_align1" (i32.const 1)) (v128.const i32x4 0x00000201 0x00000403 0x00000605 0x00000807)) +(assert_return (invoke "v128.load16x4_u_offset0_align1" (i32.const 2)) (v128.const i32x4 0x00000302 0x00000504 0x00000706 0x00000908)) +(assert_return (invoke "v128.load16x4_u_offset10_align4" (i32.const 3)) (v128.const i32x4 0x00000E0D 0x0000800F 0x00008281 0x00008483)) +(assert_return (invoke "v128.load16x4_u_offset20_align8" (i32.const 4)) (v128.const i32x4 0x00008988 0x00000000 0x00000000 0x00000000)) +;; i64x2 +(assert_return (invoke "v128.load32x2_s_offset0" (i32.const 0)) (v128.const i64x2 0x0000000003020100 0x0000000007060504)) +(assert_return (invoke "v128.load32x2_s_align1" (i32.const 1)) (v128.const i64x2 0x0000000004030201 0x0000000008070605)) +(assert_return (invoke "v128.load32x2_s_offset0_align1" (i32.const 2)) (v128.const i64x2 0x0000000005040302 0x0000000009080706)) +(assert_return (invoke "v128.load32x2_s_offset10_align4" (i32.const 3)) (v128.const i64x2 0xFFFFFFFF800F0E0D 0xFFFFFFFF84838281)) +(assert_return (invoke "v128.load32x2_s_offset20_align8" (i32.const 4)) (v128.const i64x2 0x0000000000008988 0x0000000000000000)) +(assert_return (invoke "v128.load32x2_u_offset0" (i32.const 0)) (v128.const i64x2 0x0000000003020100 0x0000000007060504)) +(assert_return (invoke "v128.load32x2_u_align1" (i32.const 1)) (v128.const i64x2 0x0000000004030201 0x0000000008070605)) +(assert_return (invoke "v128.load32x2_u_offset0_align1" (i32.const 2)) (v128.const i64x2 0x0000000005040302 0x0000000009080706)) +(assert_return (invoke "v128.load32x2_u_offset10_align4" (i32.const 3)) (v128.const i64x2 0x00000000800F0E0D 0x0000000084838281)) +(assert_return (invoke "v128.load32x2_u_offset20_align8" (i32.const 4)) (v128.const i64x2 0x0000000000008988 0x0000000000000000)) + +;; out of bounds memory access +(assert_trap (invoke "v128.load8x8_s" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v128.load8x8_u" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v128.load16x4_s" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "v128.load16x4_u" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "v128.load32x2_s" (i32.const 65529)) "out of bounds memory access") +(assert_trap (invoke "v128.load32x2_u" (i32.const 65529)) "out of bounds memory access") + +(assert_trap (invoke "v128.load8x8_s_offset1_align1" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v128.load8x8_u_offset1_align1" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v128.load16x4_s_offset1_align1" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v128.load16x4_u_offset1_align1" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v128.load32x2_s_offset1_align1" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v128.load32x2_u_offset1_align1" (i32.const -1)) "out of bounds memory access") + +;; type check +(assert_invalid (module (memory 0) (func (result v128) (v128.load8x8_s (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory 0) (func (result v128) (v128.load8x8_u (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory 0) (func (result v128) (v128.load16x4_s (f64.const 0)))) "type mismatch") +(assert_invalid (module (memory 0) (func (result v128) (v128.load16x4_u (f64.const 0)))) "type mismatch") +(assert_invalid (module (memory 0) (func (result v128) (v128.load32x2_s (v128.const i32x4 0 0 0 0)))) "type mismatch") +(assert_invalid (module (memory 0) (func (result v128) (v128.load32x2_u (v128.const i32x4 0 0 0 0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module (memory 0) + (func $v128.load8x8_s-arg-empty (result v128) + (v128.load8x8_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module (memory 0) + (func $v128.load8x8_u-arg-empty (result v128) + (v128.load8x8_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module (memory 0) + (func $v128.load16x4_s-arg-empty (result v128) + (v128.load16x4_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module (memory 0) + (func $v128.load16x4_u-arg-empty (result v128) + (v128.load16x4_u) + ) + ) + "type mismatch" +) +(assert_invalid + (module (memory 0) + (func $v128.load32x2_s-arg-empty (result v128) + (v128.load32x2_s) + ) + ) + "type mismatch" +) +(assert_invalid + (module (memory 0) + (func $v128.load32x2_u-arg-empty (result v128) + (v128.load32x2_u) + ) + ) + "type mismatch" +) + +;; Unknown operator + +(assert_malformed (module quote "(memory 1) (func (drop (i16x8.load16x4_s (i32.const 0))))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (drop (i16x8.load16x4_u (i32.const 0))))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (drop (i32x4.load32x2_s (i32.const 0))))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (drop (i32x4.load32x2_u (i32.const 0))))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (drop (i64x2.load64x1_s (i32.const 0))))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (drop (i64x2.load64x1_u (i32.const 0))))") "unknown operator") + +;; combination +(module + (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F\80\81\82\83\84\85\86\87\88\89") + (func (export "v128.load8x8_s-in-block") (result v128) + (block (result v128) (block (result v128) (v128.load8x8_s (i32.const 0)))) + ) + (func (export "v128.load8x8_u-in-block") (result v128) + (block (result v128) (block (result v128) (v128.load8x8_u (i32.const 1)))) + ) + (func (export "v128.load16x4_s-in-block") (result v128) + (block (result v128) (block (result v128) (v128.load16x4_s (i32.const 2)))) + ) + (func (export "v128.load16x4_u-in-block") (result v128) + (block (result v128) (block (result v128) (v128.load16x4_u (i32.const 3)))) + ) + (func (export "v128.load32x2_s-in-block") (result v128) + (block (result v128) (block (result v128) (v128.load32x2_s (i32.const 4)))) + ) + (func (export "v128.load32x2_u-in-block") (result v128) + (block (result v128) (block (result v128) (v128.load32x2_u (i32.const 5)))) + ) + (func (export "v128.load8x8_s-as-br-value") (result v128) + (block (result v128) (br 0 (v128.load8x8_s (i32.const 6)))) + ) + (func (export "v128.load8x8_u-as-br-value") (result v128) + (block (result v128) (br 0 (v128.load8x8_u (i32.const 7)))) + ) + (func (export "v128.load16x4_s-as-br-value") (result v128) + (block (result v128) (br 0 (v128.load16x4_s (i32.const 8)))) + ) + (func (export "v128.load16x4_u-as-br-value") (result v128) + (block (result v128) (br 0 (v128.load16x4_u (i32.const 9)))) + ) + (func (export "v128.load32x2_s-as-br-value") (result v128) + (block (result v128) (br 0 (v128.load32x2_s (i32.const 10)))) + ) + (func (export "v128.load32x2_u-as-br-value") (result v128) + (block (result v128) (br 0 (v128.load32x2_u (i32.const 11)))) + ) + (func (export "v128.load8x8_s-extract_lane_s-operand") (result i32) + (i8x16.extract_lane_s 0 (v128.load8x8_s (i32.const 12))) + ) + (func (export "v128.load8x8_u-extract_lane_s-operand") (result i32) + (i8x16.extract_lane_s 0 (v128.load8x8_u (i32.const 13))) + ) + (func (export "v128.load16x4_s-extract_lane_s-operand") (result i32) + (i8x16.extract_lane_s 0 (v128.load16x4_s (i32.const 14))) + ) + (func (export "v128.load16x4_u-extract_lane_s-operand") (result i32) + (i8x16.extract_lane_s 0 (v128.load16x4_u (i32.const 15))) + ) + (func (export "v128.load32x2_s-extract_lane_s-operand") (result i32) + (i8x16.extract_lane_s 0 (v128.load32x2_s (i32.const 16))) + ) + (func (export "v128.load32x2_u-extract_lane_s-operand") (result i32) + (i8x16.extract_lane_s 0 (v128.load32x2_u (i32.const 17))) + ) +) +(assert_return (invoke "v128.load8x8_s-in-block") (v128.const i16x8 0x0000 0x0001 0x0002 0x0003 0x0004 0x0005 0x0006 0x0007)) +(assert_return (invoke "v128.load8x8_u-in-block") (v128.const i16x8 0x0001 0x0002 0x0003 0x0004 0x0005 0x0006 0x0007 0x0008)) +(assert_return (invoke "v128.load16x4_s-in-block") (v128.const i32x4 0x00000302 0x00000504 0x00000706 0x00000908)) +(assert_return (invoke "v128.load16x4_u-in-block") (v128.const i32x4 0x00000403 0x00000605 0x00000807 0x00000A09)) +(assert_return (invoke "v128.load32x2_s-in-block") (v128.const i64x2 0x0000000007060504 0x000000000B0A0908)) +(assert_return (invoke "v128.load32x2_u-in-block") (v128.const i64x2 0x0000000008070605 0x000000000C0B0A09)) +(assert_return (invoke "v128.load8x8_s-as-br-value") (v128.const i16x8 0x0006 0x0007 0x0008 0x0009 0x000A 0x000B 0x000C 0x000D)) +(assert_return (invoke "v128.load8x8_u-as-br-value") (v128.const i16x8 0x0007 0x0008 0x0009 0x000A 0x000B 0x000C 0x000D 0x000E)) +(assert_return (invoke "v128.load16x4_s-as-br-value") (v128.const i32x4 0x00000908 0x00000B0A 0x00000D0C 0x00000F0E)) +(assert_return (invoke "v128.load16x4_u-as-br-value") (v128.const i32x4 0x00000A09 0x00000C0B 0x00000E0D 0x0000800F)) +(assert_return (invoke "v128.load32x2_s-as-br-value") (v128.const i64x2 0x000000000D0C0B0A 0xFFFFFFFF81800F0E)) +(assert_return (invoke "v128.load32x2_u-as-br-value") (v128.const i64x2 0x000000000E0D0C0B 0x000000008281800F)) +(assert_return (invoke "v128.load8x8_s-extract_lane_s-operand") (i32.const 12)) +(assert_return (invoke "v128.load8x8_u-extract_lane_s-operand") (i32.const 13)) +(assert_return (invoke "v128.load16x4_s-extract_lane_s-operand") (i32.const 14)) +(assert_return (invoke "v128.load16x4_u-extract_lane_s-operand") (i32.const 15)) +(assert_return (invoke "v128.load32x2_s-extract_lane_s-operand") (i32.const -128)) +(assert_return (invoke "v128.load32x2_u-extract_lane_s-operand") (i32.const -127)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load_splat.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load_splat.wast new file mode 100644 index 000000000..d9642f8bd --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load_splat.wast @@ -0,0 +1,261 @@ +;; Tests for the load_splat instructions + +(module + (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F") + (data (i32.const 65520) "\10\11\12\13\14\15\16\17\18\19\1A\1B\1C\1D\1E\1F") + + (func (export "v128.load8_splat") (param $address i32) (result v128) (v128.load8_splat (local.get $address))) + (func (export "v128.load16_splat") (param $address i32) (result v128) (v128.load16_splat (local.get $address))) + (func (export "v128.load32_splat") (param $address i32) (result v128) (v128.load32_splat (local.get $address))) + (func (export "v128.load64_splat") (param $address i32) (result v128) (v128.load64_splat (local.get $address))) + + ;; Load data with different offset/align arguments + (func (export "v8x16.offset0") (param $address i32) (result v128) (v128.load8_splat offset=0 (local.get $address))) + (func (export "v8x16.align1") (param $address i32) (result v128) (v128.load8_splat align=1 (local.get $address))) + (func (export "v8x16.offset1_align1") (param $address i32) (result v128) (v128.load8_splat offset=1 align=1 (local.get $address))) + (func (export "v8x16.offset2_align1") (param $address i32) (result v128) (v128.load8_splat offset=2 align=1 (local.get $address))) + (func (export "v8x16.offset15_align1") (param $address i32) (result v128) (v128.load8_splat offset=15 align=1 (local.get $address))) + + (func (export "v16x8.offset0") (param $address i32) (result v128) (v128.load16_splat offset=0 (local.get $address))) + (func (export "v16x8.align1") (param $address i32) (result v128) (v128.load16_splat align=1 (local.get $address))) + (func (export "v16x8.offset1_align1") (param $address i32) (result v128) (v128.load16_splat offset=1 align=1 (local.get $address))) + (func (export "v16x8.offset2_align1") (param $address i32) (result v128) (v128.load16_splat offset=2 align=1 (local.get $address))) + (func (export "v16x8.offset15_align2") (param $address i32) (result v128) (v128.load16_splat offset=15 align=2 (local.get $address))) + + (func (export "v32x4.offset0") (param $address i32) (result v128) (v128.load32_splat offset=0 (local.get $address))) + (func (export "v32x4.align1") (param $address i32) (result v128) (v128.load32_splat align=1 (local.get $address))) + (func (export "v32x4.offset1_align1") (param $address i32) (result v128) (v128.load32_splat offset=1 align=1 (local.get $address))) + (func (export "v32x4.offset2_align2") (param $address i32) (result v128) (v128.load32_splat offset=2 align=2 (local.get $address))) + (func (export "v32x4.offset15_align4") (param $address i32) (result v128) (v128.load32_splat offset=15 align=4 (local.get $address))) + + (func (export "v64x2.offset0") (param $address i32) (result v128) (v128.load64_splat offset=0 (local.get $address))) + (func (export "v64x2.align1") (param $address i32) (result v128) (v128.load64_splat align=1 (local.get $address))) + (func (export "v64x2.offset1_align2") (param $address i32) (result v128) (v128.load64_splat offset=1 align=2 (local.get $address))) + (func (export "v64x2.offset2_align4") (param $address i32) (result v128) (v128.load64_splat offset=2 align=4 (local.get $address))) + (func (export "v64x2.offset15_align8") (param $address i32) (result v128) (v128.load64_splat offset=15 align=8 (local.get $address))) + + (func (export "v8x16.offset65536") (param $address i32) (result v128) (v128.load8_splat offset=65536 (local.get $address))) + (func (export "v16x8.offset65535") (param $address i32) (result v128) (v128.load16_splat offset=65535 (local.get $address))) + (func (export "v32x4.offset65533") (param $address i32) (result v128) (v128.load32_splat offset=65533 (local.get $address))) + (func (export "v64x2.offset65529") (param $address i32) (result v128) (v128.load64_splat offset=65529 (local.get $address))) +) +(assert_return (invoke "v128.load8_splat" (i32.const 0)) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load8_splat" (i32.const 1)) (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "v128.load8_splat" (i32.const 2)) (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "v128.load8_splat" (i32.const 3)) (v128.const i8x16 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3)) +(assert_return (invoke "v128.load8_splat" (i32.const 65535)) (v128.const i8x16 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31)) +(assert_return (invoke "v128.load16_splat" (i32.const 4)) (v128.const i16x8 0x0504 0x0504 0x0504 0x0504 0x0504 0x0504 0x0504 0x0504)) +(assert_return (invoke "v128.load16_splat" (i32.const 5)) (v128.const i16x8 0x0605 0x0605 0x0605 0x0605 0x0605 0x0605 0x0605 0x0605)) +(assert_return (invoke "v128.load16_splat" (i32.const 6)) (v128.const i16x8 0x0706 0x0706 0x0706 0x0706 0x0706 0x0706 0x0706 0x0706)) +(assert_return (invoke "v128.load16_splat" (i32.const 7)) (v128.const i16x8 0x0807 0x0807 0x0807 0x0807 0x0807 0x0807 0x0807 0x0807)) +(assert_return (invoke "v128.load16_splat" (i32.const 65534)) (v128.const i16x8 0x1F1E 0x1F1E 0x1F1E 0x1F1E 0x1F1E 0x1F1E 0x1F1E 0x1F1E)) +(assert_return (invoke "v128.load32_splat" (i32.const 8)) (v128.const i32x4 0x0B0A0908 0x0B0A0908 0x0B0A0908 0x0B0A0908)) +(assert_return (invoke "v128.load32_splat" (i32.const 9)) (v128.const i32x4 0x0C0B0A09 0x0C0B0A09 0x0C0B0A09 0x0C0B0A09)) +(assert_return (invoke "v128.load32_splat" (i32.const 10)) (v128.const i32x4 0x0D0C0B0A 0x0D0C0B0A 0x0D0C0B0A 0x0D0C0B0A)) +(assert_return (invoke "v128.load32_splat" (i32.const 11)) (v128.const i32x4 0x0E0D0C0B 0x0E0D0C0B 0x0E0D0C0B 0x0E0D0C0B)) +(assert_return (invoke "v128.load32_splat" (i32.const 65532)) (v128.const i32x4 0x1F1E1D1C 0x1F1E1D1C 0x1F1E1D1C 0x1F1E1D1C)) +(assert_return (invoke "v128.load64_splat" (i32.const 12)) (v128.const i64x2 0x000000000F0E0D0C 0x000000000F0E0D0C)) +(assert_return (invoke "v128.load64_splat" (i32.const 13)) (v128.const i64x2 0x00000000000F0E0D 0x00000000000F0E0D)) +(assert_return (invoke "v128.load64_splat" (i32.const 14)) (v128.const i64x2 0x0000000000000F0E 0x0000000000000F0E)) +(assert_return (invoke "v128.load64_splat" (i32.const 15)) (v128.const i64x2 0x000000000000000F 0x000000000000000F)) +(assert_return (invoke "v128.load64_splat" (i32.const 65528)) (v128.const i64x2 0x1F1E1D1C1B1A1918 0x1F1E1D1C1B1A1918)) + +;; v8x16 +(assert_return (invoke "v8x16.offset0" (i32.const 0)) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v8x16.align1" (i32.const 0)) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v8x16.offset1_align1" (i32.const 0)) (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "v8x16.offset2_align1" (i32.const 0)) (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "v8x16.offset15_align1" (i32.const 0)) (v128.const i8x16 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15 15)) +(assert_return (invoke "v8x16.offset0" (i32.const 1)) (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "v8x16.align1" (i32.const 1)) (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "v8x16.offset1_align1" (i32.const 1)) (v128.const i8x16 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2)) +(assert_return (invoke "v8x16.offset2_align1" (i32.const 1)) (v128.const i8x16 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3)) +(assert_return (invoke "v8x16.offset15_align1" (i32.const 1)) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v8x16.offset0" (i32.const 65535)) (v128.const i8x16 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31)) +(assert_return (invoke "v8x16.align1" (i32.const 65535)) (v128.const i8x16 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31)) +;; v16x8 +(assert_return (invoke "v16x8.offset0" (i32.const 0)) (v128.const i16x8 0x0100 0x0100 0x0100 0x0100 0x0100 0x0100 0x0100 0x0100)) +(assert_return (invoke "v16x8.align1" (i32.const 0)) (v128.const i16x8 0x0100 0x0100 0x0100 0x0100 0x0100 0x0100 0x0100 0x0100)) +(assert_return (invoke "v16x8.offset1_align1" (i32.const 0)) (v128.const i16x8 0x0201 0x0201 0x0201 0x0201 0x0201 0x0201 0x0201 0x0201)) +(assert_return (invoke "v16x8.offset2_align1" (i32.const 0)) (v128.const i16x8 0x0302 0x0302 0x0302 0x0302 0x0302 0x0302 0x0302 0x0302)) +(assert_return (invoke "v16x8.offset15_align2" (i32.const 0)) (v128.const i16x8 0x000F 0x000F 0x000F 0x000F 0x000F 0x000F 0x000F 0x000F)) +(assert_return (invoke "v16x8.offset0" (i32.const 1)) (v128.const i16x8 0x0201 0x0201 0x0201 0x0201 0x0201 0x0201 0x0201 0x0201)) +(assert_return (invoke "v16x8.align1" (i32.const 1)) (v128.const i16x8 0x0201 0x0201 0x0201 0x0201 0x0201 0x0201 0x0201 0x0201)) +(assert_return (invoke "v16x8.offset1_align1" (i32.const 1)) (v128.const i16x8 0x0302 0x0302 0x0302 0x0302 0x0302 0x0302 0x0302 0x0302)) +(assert_return (invoke "v16x8.offset2_align1" (i32.const 1)) (v128.const i16x8 0x0403 0x0403 0x0403 0x0403 0x0403 0x0403 0x0403 0x0403)) +(assert_return (invoke "v16x8.offset15_align2" (i32.const 1)) (v128.const i16x8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000)) +(assert_return (invoke "v16x8.offset0" (i32.const 65534)) (v128.const i16x8 0x1F1E 0x1F1E 0x1F1E 0x1F1E 0x1F1E 0x1F1E 0x1F1E 0x1F1E)) +(assert_return (invoke "v16x8.align1" (i32.const 65534)) (v128.const i16x8 0x1F1E 0x1F1E 0x1F1E 0x1F1E 0x1F1E 0x1F1E 0x1F1E 0x1F1E)) +;; v32x4 +(assert_return (invoke "v32x4.offset0" (i32.const 0)) (v128.const i32x4 0x03020100 0x03020100 0x03020100 0x03020100)) +(assert_return (invoke "v32x4.align1" (i32.const 0)) (v128.const i32x4 0x03020100 0x03020100 0x03020100 0x03020100)) +(assert_return (invoke "v32x4.offset1_align1" (i32.const 0)) (v128.const i32x4 0x04030201 0x04030201 0x04030201 0x04030201)) +(assert_return (invoke "v32x4.offset2_align2" (i32.const 0)) (v128.const i32x4 0x05040302 0x05040302 0x05040302 0x05040302)) +(assert_return (invoke "v32x4.offset15_align4" (i32.const 0)) (v128.const i32x4 0x0000000F 0x0000000F 0x0000000F 0x0000000F)) +(assert_return (invoke "v32x4.offset0" (i32.const 1)) (v128.const i32x4 0x04030201 0x04030201 0x04030201 0x04030201)) +(assert_return (invoke "v32x4.align1" (i32.const 1)) (v128.const i32x4 0x04030201 0x04030201 0x04030201 0x04030201)) +(assert_return (invoke "v32x4.offset1_align1" (i32.const 1)) (v128.const i32x4 0x05040302 0x05040302 0x05040302 0x05040302)) +(assert_return (invoke "v32x4.offset2_align2" (i32.const 1)) (v128.const i32x4 0x06050403 0x06050403 0x06050403 0x06050403)) +(assert_return (invoke "v32x4.offset15_align4" (i32.const 1)) (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)) +(assert_return (invoke "v32x4.offset0" (i32.const 65532)) (v128.const i32x4 0x1F1E1D1C 0x1F1E1D1C 0x1F1E1D1C 0x1F1E1D1C)) +(assert_return (invoke "v32x4.align1" (i32.const 65532)) (v128.const i32x4 0x1F1E1D1C 0x1F1E1D1C 0x1F1E1D1C 0x1F1E1D1C)) +;; v64x2 +(assert_return (invoke "v64x2.offset0" (i32.const 0)) (v128.const i64x2 0x0706050403020100 0x0706050403020100)) +(assert_return (invoke "v64x2.align1" (i32.const 0)) (v128.const i64x2 0x0706050403020100 0x0706050403020100)) +(assert_return (invoke "v64x2.offset1_align2" (i32.const 0)) (v128.const i64x2 0x0807060504030201 0x0807060504030201)) +(assert_return (invoke "v64x2.offset2_align4" (i32.const 0)) (v128.const i64x2 0x0908070605040302 0x0908070605040302)) +(assert_return (invoke "v64x2.offset15_align8" (i32.const 0)) (v128.const i64x2 0x000000000000000F 0x000000000000000F)) +(assert_return (invoke "v64x2.offset0" (i32.const 1)) (v128.const i64x2 0x0807060504030201 0x0807060504030201)) +(assert_return (invoke "v64x2.align1" (i32.const 1)) (v128.const i64x2 0x0807060504030201 0x0807060504030201)) +(assert_return (invoke "v64x2.offset1_align2" (i32.const 1)) (v128.const i64x2 0x0908070605040302 0x0908070605040302)) +(assert_return (invoke "v64x2.offset2_align4" (i32.const 1)) (v128.const i64x2 0x0A09080706050403 0x0A09080706050403)) +(assert_return (invoke "v64x2.offset15_align8" (i32.const 1)) (v128.const i64x2 0x0000000000000000 0x0000000000000000)) +(assert_return (invoke "v64x2.offset0" (i32.const 65528)) (v128.const i64x2 0x1F1E1D1C1B1A1918 0x1F1E1D1C1B1A1918)) +(assert_return (invoke "v64x2.align1" (i32.const 65528)) (v128.const i64x2 0x1F1E1D1C1B1A1918 0x1F1E1D1C1B1A1918)) + + +;; Out of bounds memory access +(assert_trap (invoke "v128.load8_splat" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v128.load16_splat" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v128.load32_splat" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v128.load64_splat" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v128.load8_splat" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "v128.load16_splat" (i32.const 65535)) "out of bounds memory access") +(assert_trap (invoke "v128.load32_splat" (i32.const 65533)) "out of bounds memory access") +(assert_trap (invoke "v128.load64_splat" (i32.const 65529)) "out of bounds memory access") + +(assert_trap (invoke "v8x16.offset1_align1" (i32.const 65535)) "out of bounds memory access") +(assert_trap (invoke "v8x16.offset2_align1" (i32.const 65535)) "out of bounds memory access") +(assert_trap (invoke "v8x16.offset15_align1" (i32.const 65535)) "out of bounds memory access") +(assert_trap (invoke "v16x8.offset1_align1" (i32.const 65534)) "out of bounds memory access") +(assert_trap (invoke "v16x8.offset2_align1" (i32.const 65534)) "out of bounds memory access") +(assert_trap (invoke "v16x8.offset15_align2" (i32.const 65534)) "out of bounds memory access") +(assert_trap (invoke "v32x4.offset1_align1" (i32.const 65532)) "out of bounds memory access") +(assert_trap (invoke "v32x4.offset2_align2" (i32.const 65532)) "out of bounds memory access") +(assert_trap (invoke "v32x4.offset15_align4" (i32.const 65532)) "out of bounds memory access") +(assert_trap (invoke "v64x2.offset1_align2" (i32.const 65528)) "out of bounds memory access") +(assert_trap (invoke "v64x2.offset2_align4" (i32.const 65528)) "out of bounds memory access") +(assert_trap (invoke "v64x2.offset15_align8" (i32.const 65528)) "out of bounds memory access") + +(assert_trap (invoke "v8x16.offset1_align1" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v16x8.offset1_align1" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v32x4.offset1_align1" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v64x2.offset1_align2" (i32.const -1)) "out of bounds memory access") + +(assert_trap (invoke "v8x16.offset65536" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "v16x8.offset65535" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "v32x4.offset65533" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "v64x2.offset65529" (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "v8x16.offset65536" (i32.const 1)) "out of bounds memory access") +(assert_trap (invoke "v16x8.offset65535" (i32.const 1)) "out of bounds memory access") +(assert_trap (invoke "v32x4.offset65533" (i32.const 1)) "out of bounds memory access") +(assert_trap (invoke "v64x2.offset65529" (i32.const 1)) "out of bounds memory access") + + +;; Combination + +(module (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0A") + + (func (export "v128.load8_splat-in-block") (result v128) + (block (result v128) (block (result v128) (v128.load8_splat (i32.const 0)))) + ) + (func (export "v128.load16_splat-in-block") (result v128) + (block (result v128) (block (result v128) (v128.load16_splat (i32.const 1)))) + ) + (func (export "v128.load32_splat-in-block") (result v128) + (block (result v128) (block (result v128) (v128.load32_splat (i32.const 2)))) + ) + (func (export "v128.load64_splat-in-block") (result v128) + (block (result v128) (block (result v128) (v128.load64_splat (i32.const 9)))) + ) + (func (export "v128.load8_splat-as-br-value") (result v128) + (block (result v128) (br 0 (v128.load8_splat (i32.const 3)))) + ) + (func (export "v128.load16_splat-as-br-value") (result v128) + (block (result v128) (br 0 (v128.load16_splat (i32.const 4)))) + ) + (func (export "v128.load32_splat-as-br-value") (result v128) + (block (result v128) (br 0 (v128.load32_splat (i32.const 5)))) + ) + (func (export "v128.load64_splat-as-br-value") (result v128) + (block (result v128) (br 0 (v128.load64_splat (i32.const 10)))) + ) + (func (export "v128.load8_splat-extract_lane_s-operand") (result i32) + (i8x16.extract_lane_s 0 (v128.load8_splat (i32.const 6))) + ) + (func (export "v128.load16_splat-extract_lane_s-operand") (result i32) + (i8x16.extract_lane_s 0 (v128.load16_splat (i32.const 7))) + ) + (func (export "v128.load32_splat-extract_lane_s-operand") (result i32) + (i8x16.extract_lane_s 0 (v128.load32_splat (i32.const 8))) + ) + (func (export "v128.load64_splat-extract_lane_s-operand") (result i32) + (i8x16.extract_lane_s 0 (v128.load64_splat (i32.const 11))) + ) +) +(assert_return (invoke "v128.load8_splat-in-block") (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "v128.load16_splat-in-block") (v128.const i16x8 0x0201 0x0201 0x0201 0x0201 0x0201 0x0201 0x0201 0x0201)) +(assert_return (invoke "v128.load32_splat-in-block") (v128.const i32x4 0x05040302 0x05040302 0x05040302 0x05040302)) +(assert_return (invoke "v128.load64_splat-in-block") (v128.const i64x2 0x0000000000000A09 0x0000000000000A09)) +(assert_return (invoke "v128.load8_splat-as-br-value") (v128.const i8x16 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3)) +(assert_return (invoke "v128.load16_splat-as-br-value") (v128.const i16x8 0x0504 0x0504 0x0504 0x0504 0x0504 0x0504 0x0504 0x0504)) +(assert_return (invoke "v128.load32_splat-as-br-value") (v128.const i32x4 0x08070605 0x08070605 0x08070605 0x08070605)) +(assert_return (invoke "v128.load64_splat-as-br-value") (v128.const i64x2 0x000000000000000A 0x000000000000000A)) +(assert_return (invoke "v128.load8_splat-extract_lane_s-operand") (i32.const 6)) +(assert_return (invoke "v128.load16_splat-extract_lane_s-operand") (i32.const 7)) +(assert_return (invoke "v128.load32_splat-extract_lane_s-operand") (i32.const 8)) +(assert_return (invoke "v128.load64_splat-extract_lane_s-operand") (i32.const 0)) + + +;; Type check + +(assert_invalid (module (memory 0) (func (result v128) (v128.load8_splat (v128.const i32x4 0 0 0 0)))) "type mismatch") +(assert_invalid (module (memory 0) (func (result v128) (v128.load16_splat (v128.const i32x4 0 0 0 0)))) "type mismatch") +(assert_invalid (module (memory 0) (func (result v128) (v128.load32_splat (v128.const i32x4 0 0 0 0)))) "type mismatch") +(assert_invalid (module (memory 0) (func (result v128) (v128.load64_splat (v128.const i32x4 0 0 0 0)))) "type mismatch") + + +;; Unknown operator + +(assert_malformed (module quote "(memory 1) (func (drop (i8x16.load_splat (i32.const 0))))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (drop (i16x8.load_splat (i32.const 0))))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (drop (i32x4.load_splat (i32.const 0))))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (drop (i64x2.load_splat (i32.const 0))))") "unknown operator") + + +;; Test operation with empty argument + +(assert_invalid + (module (memory 0) + (func $v128.load8_splat-arg-empty (result v128) + (v128.load8_splat) + ) + ) + "type mismatch" +) +(assert_invalid + (module (memory 0) + (func $v128.load16_splat-arg-empty (result v128) + (v128.load16_splat) + ) + ) + "type mismatch" +) +(assert_invalid + (module (memory 0) + (func $v128.load32_splat-arg-empty (result v128) + (v128.load32_splat) + ) + ) + "type mismatch" +) +(assert_invalid + (module (memory 0) + (func $v128.load64_splat-arg-empty (result v128) + (v128.load64_splat) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load_zero.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load_zero.wast new file mode 100644 index 000000000..6276a6863 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_load_zero.wast @@ -0,0 +1,154 @@ +;; Load and Zero extend test cases + +(module + (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F\80\81\82\83\84\85\86\87\88\89") + (data (i32.const 65520) "\0A\0B\0C\0D\0E\0F\80\81\82\83\84\85\86\87\88\89") + + (func (export "v128.load32_zero") (param $0 i32) (result v128) + (v128.load32_zero (local.get $0)) + ) + (func (export "v128.load64_zero") (param $0 i32) (result v128) + (v128.load64_zero (local.get $0)) + ) + + ;; load by a constant amount + (func (export "v128.load32_zero_const0") (result v128) + (v128.load32_zero (i32.const 0)) + ) + (func (export "v128.load64_zero_const8") (result v128) + (v128.load64_zero (i32.const 8)) + ) + + ;; load data with different offset/align arguments + ;; i16x8 + (func (export "v128.load32_zero_offset0") (param $0 i32) (result v128) + (v128.load32_zero offset=0 (local.get $0)) + ) + (func (export "v128.load32_zero_align1") (param $0 i32) (result v128) + (v128.load32_zero align=1 (local.get $0)) + ) + (func (export "v128.load32_zero_offset0_align1") (param $0 i32) (result v128) + (v128.load32_zero offset=0 align=1 (local.get $0)) + ) + (func (export "v128.load32_zero_offset1_align1") (param $0 i32) (result v128) + (v128.load32_zero offset=1 align=1 (local.get $0)) + ) + (func (export "v128.load32_zero_offset10_align4") (param $0 i32) (result v128) + (v128.load32_zero offset=10 align=4 (local.get $0)) + ) + (func (export "v128.load64_zero_offset0") (param $0 i32) (result v128) + (v128.load64_zero offset=0 (local.get $0)) + ) + (func (export "v128.load64_zero_align1") (param $0 i32) (result v128) + (v128.load64_zero align=1 (local.get $0)) + ) + (func (export "v128.load64_zero_offset0_align1") (param $0 i32) (result v128) + (v128.load64_zero offset=0 align=1 (local.get $0)) + ) + (func (export "v128.load64_zero_offset1_align1") (param $0 i32) (result v128) + (v128.load64_zero offset=1 align=1 (local.get $0)) + ) + (func (export "v128.load64_zero_offset10_align4") (param $0 i32) (result v128) + (v128.load64_zero offset=10 align=4 (local.get $0)) + ) + (func (export "v128.load64_zero_offset20_align8") (param $0 i32) (result v128) + (v128.load64_zero offset=20 align=8 (local.get $0)) + ) +) + + +;; normal +(assert_return (invoke "v128.load32_zero" (i32.const 0)) (v128.const i32x4 0x03020100 0x00000000 0x00000000 0x00000000)) +(assert_return (invoke "v128.load64_zero" (i32.const 0)) (v128.const i64x2 0x0706050403020100 0x0000000000000000)) +(assert_return (invoke "v128.load32_zero" (i32.const 10)) (v128.const i32x4 0x0D0C0B0A 0x00000000 0x00000000 0x00000000)) +(assert_return (invoke "v128.load64_zero" (i32.const 10)) (v128.const i64x2 0x81800F0E0D0C0B0A 0x0000000000000000)) +(assert_return (invoke "v128.load32_zero" (i32.const 20)) (v128.const i32x4 0x87868584 0x00000000 0x00000000 0x00000000)) +(assert_return (invoke "v128.load64_zero" (i32.const 20)) (v128.const i64x2 0x0000898887868584 0x0000000000000000)) + +;; load by a constant amount +(assert_return (invoke "v128.load32_zero_const0") (v128.const i32x4 0x03020100 0x00000000 0x00000000 0x00000000)) +(assert_return (invoke "v128.load64_zero_const8") (v128.const i64x2 0x0F0E0D0C0B0A0908 0x0000000000000000)) + +;; load data with different offset/align arguments +;; load32_zero +(assert_return (invoke "v128.load32_zero_offset0" (i32.const 0)) (v128.const i32x4 0x03020100 0x00000000 0x00000000 0x00000000)) +(assert_return (invoke "v128.load32_zero_align1" (i32.const 1)) (v128.const i32x4 0x04030201 0x00000000 0x00000000 0x00000000)) +(assert_return (invoke "v128.load32_zero_offset0_align1" (i32.const 2)) (v128.const i32x4 0x05040302 0x00000000 0x00000000 0x00000000)) +(assert_return (invoke "v128.load32_zero_offset10_align4" (i32.const 3)) (v128.const i32x4 0x800F0E0D 0x00000000 0x00000000 0x00000000)) + +;; load64_zero +(assert_return (invoke "v128.load64_zero_offset0" (i32.const 0)) (v128.const i64x2 0x0706050403020100 0x0000000000000000)) +(assert_return (invoke "v128.load64_zero_align1" (i32.const 1)) (v128.const i64x2 0x0807060504030201 0x0000000000000000)) +(assert_return (invoke "v128.load64_zero_offset0_align1" (i32.const 2)) (v128.const i64x2 0x0908070605040302 0x0000000000000000)) +(assert_return (invoke "v128.load64_zero_offset10_align4" (i32.const 3)) (v128.const i64x2 0x84838281800F0E0D 0x0000000000000000)) +(assert_return (invoke "v128.load64_zero_offset20_align8" (i32.const 4)) (v128.const i64x2 0x0000000000008988 0x0000000000000000)) + +;; out of bounds memory access +(assert_trap (invoke "v128.load32_zero" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v128.load64_zero" (i32.const -1)) "out of bounds memory access") + +(assert_trap (invoke "v128.load32_zero_offset1_align1" (i32.const -1)) "out of bounds memory access") +(assert_trap (invoke "v128.load64_zero_offset1_align1" (i32.const -1)) "out of bounds memory access") + +;; type check +(assert_invalid (module (memory 0) (func (result v128) (v128.load32_zero (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory 0) (func (result v128) (v128.load64_zero (f32.const 0)))) "type mismatch") + +;; Test operation with empty argument + +(assert_invalid + (module (memory 0) + (func $v128.load32_zero-arg-empty (result v128) + (v128.load32_zero) + ) + ) + "type mismatch" +) +(assert_invalid + (module (memory 0) + (func $v128.load64_zero-arg-empty (result v128) + (v128.load64_zero) + ) + ) + "type mismatch" +) + +;; Unknown operator + +(assert_malformed (module quote "(memory 1) (func (drop (i16x8.load16x4_s (i32.const 0))))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (drop (i16x8.load16x4_u (i32.const 0))))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (drop (i32x4.load32x2_s (i32.const 0))))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (drop (i32x4.load32x2_u (i32.const 0))))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (drop (i64x2.load64x1_s (i32.const 0))))") "unknown operator") +(assert_malformed (module quote "(memory 1) (func (drop (i64x2.load64x1_u (i32.const 0))))") "unknown operator") + +;; combination +(module + (memory 1) + (data (i32.const 0) "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F\80\81\82\83\84\85\86\87\88\89") + (func (export "v128.load32_zero-in-block") (result v128) + (block (result v128) (block (result v128) (v128.load32_zero (i32.const 0)))) + ) + (func (export "v128.load64_zero-in-block") (result v128) + (block (result v128) (block (result v128) (v128.load64_zero (i32.const 1)))) + ) + (func (export "v128.load32_zero-as-br-value") (result v128) + (block (result v128) (br 0 (v128.load32_zero (i32.const 6)))) + ) + (func (export "v128.load64_zero-as-br-value") (result v128) + (block (result v128) (br 0 (v128.load64_zero (i32.const 7)))) + ) + (func (export "v128.load32_zero-extract_lane_s-operand") (result i32) + (i32x4.extract_lane 0 (v128.load32_zero (i32.const 12))) + ) + (func (export "v128.load64_zero-extract_lane_s-operand") (result i64) + (i64x2.extract_lane 0 (v128.load64_zero (i32.const 13))) + ) +) +(assert_return (invoke "v128.load32_zero-in-block") (v128.const i32x4 0x03020100 0x00000000 0x00000000 0x00000000)) +(assert_return (invoke "v128.load64_zero-in-block") (v128.const i64x2 0x0807060504030201 0x0000000000000000)) +(assert_return (invoke "v128.load32_zero-as-br-value") (v128.const i32x4 0x09080706 0x00000000 0x00000000 0x00000000)) +(assert_return (invoke "v128.load64_zero-as-br-value") (v128.const i64x2 0x0E0D0C0B0A090807 0x0000000000000000)) +(assert_return (invoke "v128.load32_zero-extract_lane_s-operand") (i32.const 0x0F0E0D0C)) +(assert_return (invoke "v128.load64_zero-extract_lane_s-operand") (i64.const 0x84838281800F0E0D)) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_splat.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_splat.wast new file mode 100644 index 000000000..4be04ef17 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_splat.wast @@ -0,0 +1,430 @@ +;; Tests for the *_splat instructions + +(module + (func (export "i8x16.splat") (param i32) (result v128) (i8x16.splat (local.get 0))) + (func (export "i16x8.splat") (param i32) (result v128) (i16x8.splat (local.get 0))) + (func (export "i32x4.splat") (param i32) (result v128) (i32x4.splat (local.get 0))) + (func (export "f32x4.splat") (param f32) (result v128) (f32x4.splat (local.get 0))) + (func (export "i64x2.splat") (param i64) (result v128) (i64x2.splat (local.get 0))) + (func (export "f64x2.splat") (param f64) (result v128) (f64x2.splat (local.get 0))) +) + +(assert_return (invoke "i8x16.splat" (i32.const 0)) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i8x16.splat" (i32.const 5)) (v128.const i8x16 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5)) +(assert_return (invoke "i8x16.splat" (i32.const -5)) (v128.const i8x16 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5)) +(assert_return (invoke "i8x16.splat" (i32.const 257)) (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i8x16.splat" (i32.const 0xff)) (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i8x16.splat" (i32.const -128)) (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.splat" (i32.const 127)) (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.splat" (i32.const -129)) (v128.const i8x16 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127)) +(assert_return (invoke "i8x16.splat" (i32.const 128)) (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)) +(assert_return (invoke "i8x16.splat" (i32.const 0xff7f)) (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "i8x16.splat" (i32.const 0x80)) (v128.const i8x16 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80)) +(assert_return (invoke "i8x16.splat" (i32.const 0xAB)) (v128.const i32x4 0xABABABAB 0xABABABAB 0xABABABAB 0xABABABAB)) + +(assert_return (invoke "i16x8.splat" (i32.const 0)) (v128.const i16x8 0 0 0 0 0 0 0 0)) +(assert_return (invoke "i16x8.splat" (i32.const 5)) (v128.const i16x8 5 5 5 5 5 5 5 5)) +(assert_return (invoke "i16x8.splat" (i32.const -5)) (v128.const i16x8 -5 -5 -5 -5 -5 -5 -5 -5)) +(assert_return (invoke "i16x8.splat" (i32.const 65537)) (v128.const i16x8 1 1 1 1 1 1 1 1)) +(assert_return (invoke "i16x8.splat" (i32.const 0xffff)) (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1)) +(assert_return (invoke "i16x8.splat" (i32.const -32768)) (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.splat" (i32.const 32767)) (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.splat" (i32.const -32769)) (v128.const i16x8 32767 32767 32767 32767 32767 32767 32767 32767)) +(assert_return (invoke "i16x8.splat" (i32.const 32768)) (v128.const i16x8 -32768 -32768 -32768 -32768 -32768 -32768 -32768 -32768)) +(assert_return (invoke "i16x8.splat" (i32.const 0xffff7fff)) (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) +(assert_return (invoke "i16x8.splat" (i32.const 0x8000)) (v128.const i16x8 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000 0x8000)) +(assert_return (invoke "i16x8.splat" (i32.const 0xABCD)) (v128.const i32x4 0xABCDABCD 0xABCDABCD 0xABCDABCD 0xABCDABCD)) +(assert_return (invoke "i16x8.splat" (i32.const 012345)) (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345)) +(assert_return (invoke "i16x8.splat" (i32.const 0x01234)) (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234)) + +(assert_return (invoke "i32x4.splat" (i32.const 0)) (v128.const i32x4 0 0 0 0)) +(assert_return (invoke "i32x4.splat" (i32.const 5)) (v128.const i32x4 5 5 5 5)) +(assert_return (invoke "i32x4.splat" (i32.const -5)) (v128.const i32x4 -5 -5 -5 -5)) +(assert_return (invoke "i32x4.splat" (i32.const 0xffffffff)) (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.splat" (i32.const 4294967295)) (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "i32x4.splat" (i32.const -2147483648)) (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) +(assert_return (invoke "i32x4.splat" (i32.const 2147483647)) (v128.const i32x4 0x7fffffff 0x7fffffff 0x7fffffff 0x7fffffff)) +(assert_return (invoke "i32x4.splat" (i32.const 2147483648)) (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)) +(assert_return (invoke "i32x4.splat" (i32.const 01234567890)) (v128.const i32x4 012_3456_7890 012_3456_7890 012_3456_7890 012_3456_7890)) +(assert_return (invoke "i32x4.splat" (i32.const 0x012345678)) (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678)) + +(assert_return (invoke "f32x4.splat" (f32.const 0.0)) (v128.const f32x4 0.0 0.0 0.0 0.0)) +(assert_return (invoke "f32x4.splat" (f32.const 1.1)) (v128.const f32x4 1.1 1.1 1.1 1.1)) +(assert_return (invoke "f32x4.splat" (f32.const -1.1)) (v128.const f32x4 -1.1 -1.1 -1.1 -1.1)) +(assert_return (invoke "f32x4.splat" (f32.const 1e38)) (v128.const f32x4 1e38 1e38 1e38 1e38)) +(assert_return (invoke "f32x4.splat" (f32.const -1e38)) (v128.const f32x4 -1e38 -1e38 -1e38 -1e38)) +(assert_return (invoke "f32x4.splat" (f32.const 0x1.fffffep127)) (v128.const f32x4 0x1.fffffep127 0x1.fffffep127 0x1.fffffep127 0x1.fffffep127)) +(assert_return (invoke "f32x4.splat" (f32.const -0x1.fffffep127)) (v128.const f32x4 -0x1.fffffep127 -0x1.fffffep127 -0x1.fffffep127 -0x1.fffffep127)) +(assert_return (invoke "f32x4.splat" (f32.const 0x1p127)) (v128.const f32x4 0x1p127 0x1p127 0x1p127 0x1p127)) +(assert_return (invoke "f32x4.splat" (f32.const -0x1p127)) (v128.const f32x4 -0x1p127 -0x1p127 -0x1p127 -0x1p127)) +(assert_return (invoke "f32x4.splat" (f32.const inf)) (v128.const f32x4 inf inf inf inf)) +(assert_return (invoke "f32x4.splat" (f32.const -inf)) (v128.const f32x4 -inf -inf -inf -inf)) +(assert_return (invoke "f32x4.splat" (f32.const nan)) (v128.const f32x4 nan nan nan nan)) +(assert_return (invoke "f32x4.splat" (f32.const nan:0x1)) (v128.const f32x4 nan:0x1 nan:0x1 nan:0x1 nan:0x1)) +(assert_return (invoke "f32x4.splat" (f32.const nan:0x7f_ffff)) (v128.const f32x4 nan:0x7f_ffff nan:0x7f_ffff nan:0x7f_ffff nan:0x7f_ffff)) +(assert_return (invoke "f32x4.splat" (f32.const 0123456789)) (v128.const f32x4 0123456789 0123456789 0123456789 0123456789)) +(assert_return (invoke "f32x4.splat" (f32.const 0123456789.)) (v128.const f32x4 0123456789. 0123456789. 0123456789. 0123456789.)) +(assert_return (invoke "f32x4.splat" (f32.const 0x0123456789ABCDEF)) (v128.const f32x4 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF 0x0123456789ABCDEF)) +(assert_return (invoke "f32x4.splat" (f32.const 0x0123456789ABCDEF.)) (v128.const f32x4 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF. 0x0123456789ABCDEF.)) +(assert_return (invoke "f32x4.splat" (f32.const 0123456789e019)) (v128.const f32x4 0123456789e019 0123456789e019 0123456789e019 0123456789e019)) +(assert_return (invoke "f32x4.splat" (f32.const 0123456789.e+019)) (v128.const f32x4 0123456789.e+019 0123456789.e+019 0123456789.e+019 0123456789.e+019)) +(assert_return (invoke "f32x4.splat" (f32.const 0x0123456789ABCDEFp019)) (v128.const f32x4 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019 0x0123456789ABCDEFp019)) +(assert_return (invoke "f32x4.splat" (f32.const 0x0123456789ABCDEF.p-019)) (v128.const f32x4 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019 0x0123456789ABCDEF.p-019)) + +(assert_return (invoke "i64x2.splat" (i64.const 0)) (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.splat" (i64.const -0)) (v128.const i64x2 0 0)) +(assert_return (invoke "i64x2.splat" (i64.const 1)) (v128.const i64x2 1 1)) +(assert_return (invoke "i64x2.splat" (i64.const -1)) (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.splat" (i64.const -9223372036854775808)) (v128.const i64x2 -9223372036854775808 -9223372036854775808)) +(assert_return (invoke "i64x2.splat" (i64.const -9223372036854775808)) (v128.const i64x2 9223372036854775808 9223372036854775808)) +(assert_return (invoke "i64x2.splat" (i64.const 9223372036854775807)) (v128.const i64x2 9223372036854775807 9223372036854775807)) +(assert_return (invoke "i64x2.splat" (i64.const 18446744073709551615)) (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.splat" (i64.const 0x7fffffffffffffff)) (v128.const i64x2 0x7fffffffffffffff 0x7fffffffffffffff)) +(assert_return (invoke "i64x2.splat" (i64.const 0xffffffffffffffff)) (v128.const i64x2 -1 -1)) +(assert_return (invoke "i64x2.splat" (i64.const -0x8000000000000000)) (v128.const i64x2 -0x8000000000000000 -0x8000000000000000)) +(assert_return (invoke "i64x2.splat" (i64.const -0x8000000000000000)) (v128.const i64x2 0x8000000000000000 0x8000000000000000)) +(assert_return (invoke "i64x2.splat" (i64.const 01234567890123456789)) (v128.const i64x2 01_234_567_890_123_456_789 01_234_567_890_123_456_789)) +(assert_return (invoke "i64x2.splat" (i64.const 0x01234567890ABcdef)) (v128.const i64x2 0x0_1234_5678_90AB_cdef 0x0_1234_5678_90AB_cdef)) + +(assert_return (invoke "f64x2.splat" (f64.const 0.0)) (v128.const f64x2 0.0 0.0)) +(assert_return (invoke "f64x2.splat" (f64.const -0.0)) (v128.const f64x2 -0.0 -0.0)) +(assert_return (invoke "f64x2.splat" (f64.const 1.1)) (v128.const f64x2 1.1 1.1)) +(assert_return (invoke "f64x2.splat" (f64.const -1.1)) (v128.const f64x2 -1.1 -1.1)) +(assert_return (invoke "f64x2.splat" (f64.const 0x0.0000000000001p-1022)) (v128.const f64x2 0x0.0000000000001p-1022 0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.splat" (f64.const -0x0.0000000000001p-1022)) (v128.const f64x2 -0x0.0000000000001p-1022 -0x0.0000000000001p-1022)) +(assert_return (invoke "f64x2.splat" (f64.const 0x1p-1022)) (v128.const f64x2 0x1p-1022 0x1p-1022)) +(assert_return (invoke "f64x2.splat" (f64.const -0x1p-1022)) (v128.const f64x2 -0x1p-1022 -0x1p-1022)) +(assert_return (invoke "f64x2.splat" (f64.const 0x1p-1)) (v128.const f64x2 0x1p-1 0x1p-1)) +(assert_return (invoke "f64x2.splat" (f64.const -0x1p-1)) (v128.const f64x2 -0x1p-1 -0x1p-1)) +(assert_return (invoke "f64x2.splat" (f64.const 0x1p+0)) (v128.const f64x2 0x1p+0 0x1p+0)) +(assert_return (invoke "f64x2.splat" (f64.const -0x1p+0)) (v128.const f64x2 -0x1p+0 -0x1p+0)) +(assert_return (invoke "f64x2.splat" (f64.const 0x1.921fb54442d18p+2)) (v128.const f64x2 0x1.921fb54442d18p+2 0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.splat" (f64.const -0x1.921fb54442d18p+2)) (v128.const f64x2 -0x1.921fb54442d18p+2 -0x1.921fb54442d18p+2)) +(assert_return (invoke "f64x2.splat" (f64.const 0x1.fffffffffffffp+1023)) (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.splat" (f64.const -0x1.fffffffffffffp+1023)) (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023)) +(assert_return (invoke "f64x2.splat" (f64.const inf)) (v128.const f64x2 inf inf)) +(assert_return (invoke "f64x2.splat" (f64.const -inf)) (v128.const f64x2 -inf -inf)) +(assert_return (invoke "f64x2.splat" (f64.const nan)) (v128.const f64x2 nan nan)) +(assert_return (invoke "f64x2.splat" (f64.const -nan)) (v128.const f64x2 -nan -nan)) +(assert_return (invoke "f64x2.splat" (f64.const nan:0x4000000000000)) (v128.const f64x2 nan:0x4000000000000 nan:0x4000000000000)) +(assert_return (invoke "f64x2.splat" (f64.const -nan:0x4000000000000)) (v128.const f64x2 -nan:0x4000000000000 -nan:0x4000000000000)) +(assert_return (invoke "f64x2.splat" (f64.const 0123456789)) (v128.const f64x2 0123456789 0123456789)) +(assert_return (invoke "f64x2.splat" (f64.const 0123456789.)) (v128.const f64x2 0123456789. 0123456789.)) +(assert_return (invoke "f64x2.splat" (f64.const 0x0123456789ABCDEFabcdef)) (v128.const f64x2 0x0123456789ABCDEFabcdef 0x0123456789ABCDEFabcdef)) +(assert_return (invoke "f64x2.splat" (f64.const 0x0123456789ABCDEFabcdef.)) (v128.const f64x2 0x0123456789ABCDEFabcdef. 0x0123456789ABCDEFabcdef.)) +(assert_return (invoke "f64x2.splat" (f64.const 0123456789e019)) (v128.const f64x2 0123456789e019 0123456789e019)) +(assert_return (invoke "f64x2.splat" (f64.const 0123456789e+019)) (v128.const f64x2 0123456789e+019 0123456789e+019)) +(assert_return (invoke "f64x2.splat" (f64.const 0x0123456789ABCDEFabcdef.p019)) (v128.const f64x2 0x0123456789ABCDEFabcdef.p019 0x0123456789ABCDEFabcdef.p019)) +(assert_return (invoke "f64x2.splat" (f64.const 0x0123456789ABCDEFabcdef.p-019)) (v128.const f64x2 0x0123456789ABCDEFabcdef.p-019 0x0123456789ABCDEFabcdef.p-019)) + +;; Unknown operator + +(assert_malformed (module quote "(func (result v128) (v128.splat (i32.const 0)))") "unknown operator") + + +;; Type mismatched + +(assert_invalid (module (func (result v128) i8x16.splat (i64.const 0))) "type mismatch") +(assert_invalid (module (func (result v128) i8x16.splat (f32.const 0.0))) "type mismatch") +(assert_invalid (module (func (result v128) i8x16.splat (f64.const 0.0))) "type mismatch") +(assert_invalid (module (func (result v128) i16x8.splat (i64.const 1))) "type mismatch") +(assert_invalid (module (func (result v128) i16x8.splat (f32.const 1.0))) "type mismatch") +(assert_invalid (module (func (result v128) i16x8.splat (f64.const 1.0))) "type mismatch") +(assert_invalid (module (func (result v128) i32x4.splat (i64.const 2))) "type mismatch") +(assert_invalid (module (func (result v128) i32x4.splat (f32.const 2.0))) "type mismatch") +(assert_invalid (module (func (result v128) i32x4.splat (f64.const 2.0))) "type mismatch") +(assert_invalid (module (func (result v128) f32x4.splat (i32.const 4))) "type mismatch") +(assert_invalid (module (func (result v128) f32x4.splat (i64.const 4))) "type mismatch") +(assert_invalid (module (func (result v128) f32x4.splat (f64.const 4.0))) "type mismatch") +(assert_invalid (module (func (result v128) i64x2.splat (i32.const 0))) "type mismatch") +(assert_invalid (module (func (result v128) i64x2.splat (f64.const 0.0))) "type mismatch") +(assert_invalid (module (func (result v128) f64x2.splat (i32.const 0))) "type mismatch") +(assert_invalid (module (func (result v128) f64x2.splat (f32.const 0.0))) "type mismatch") + + +;; V128 splat operators as the argument of other SIMD instructions + +;; v128.store and v128.load +(module (memory 1) + (func (export "as-v128_store-operand-1") (param i32) (result v128) + (v128.store (i32.const 0) (i8x16.splat (local.get 0))) + (v128.load (i32.const 0))) + (func (export "as-v128_store-operand-2") (param i32) (result v128) + (v128.store (i32.const 0) (i16x8.splat (local.get 0))) + (v128.load (i32.const 0))) + (func (export "as-v128_store-operand-3") (param i32) (result v128) + (v128.store (i32.const 0) (i32x4.splat (local.get 0))) + (v128.load (i32.const 0))) + (func (export "as-v128_store-operand-4") (param i64) (result v128) + (v128.store (i32.const 0) (i64x2.splat (local.get 0))) + (v128.load (i32.const 0))) + (func (export "as-v128_store-operand-5") (param f64) (result v128) + (v128.store (i32.const 0) (f64x2.splat (local.get 0))) + (v128.load (i32.const 0))) +) + +(assert_return (invoke "as-v128_store-operand-1" (i32.const 1)) (v128.const i8x16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1)) +(assert_return (invoke "as-v128_store-operand-2" (i32.const 256)) (v128.const i16x8 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0x100)) +(assert_return (invoke "as-v128_store-operand-3" (i32.const 0xffffffff)) (v128.const i32x4 -1 -1 -1 -1)) +(assert_return (invoke "as-v128_store-operand-4" (i64.const 1)) (v128.const i64x2 1 1)) +(assert_return (invoke "as-v128_store-operand-5" (f64.const -0x1p+0)) (v128.const f64x2 -0x1p+0 -0x1p+0)) + +(module + ;; Accessing lane + (func (export "as-i8x16_extract_lane_s-operand-first") (param i32) (result i32) + (i8x16.extract_lane_s 0 (i8x16.splat (local.get 0)))) + (func (export "as-i8x16_extract_lane_s-operand-last") (param i32) (result i32) + (i8x16.extract_lane_s 15 (i8x16.splat (local.get 0)))) + (func (export "as-i16x8_extract_lane_s-operand-first") (param i32) (result i32) + (i16x8.extract_lane_s 0 (i16x8.splat (local.get 0)))) + (func (export "as-i16x8_extract_lane_s-operand-last") (param i32) (result i32) + (i16x8.extract_lane_s 7 (i16x8.splat (local.get 0)))) + (func (export "as-i32x4_extract_lane_s-operand-first") (param i32) (result i32) + (i32x4.extract_lane 0 (i32x4.splat (local.get 0)))) + (func (export "as-i32x4_extract_lane_s-operand-last") (param i32) (result i32) + (i32x4.extract_lane 3 (i32x4.splat (local.get 0)))) + (func (export "as-f32x4_extract_lane_s-operand-first") (param f32) (result f32) + (f32x4.extract_lane 0 (f32x4.splat (local.get 0)))) + (func (export "as-f32x4_extract_lane_s-operand-last") (param f32) (result f32) + (f32x4.extract_lane 3 (f32x4.splat (local.get 0)))) + (func (export "as-v8x16_swizzle-operands") (param i32) (param i32) (result v128) + (i8x16.swizzle (i8x16.splat (local.get 0)) (i8x16.splat (local.get 1)))) + (func (export "as-i64x2_extract_lane-operand-first") (param i64) (result i64) + (i64x2.extract_lane 0 (i64x2.splat (local.get 0)))) + (func (export "as-i64x2_extract_lane-operand-last") (param i64) (result i64) + (i64x2.extract_lane 1 (i64x2.splat (local.get 0)))) + (func (export "as-f64x2_extract_lane-operand-first") (param f64) (result f64) + (f64x2.extract_lane 0 (f64x2.splat (local.get 0)))) + (func (export "as-f64x2_extract_lane-operand-last") (param f64) (result f64) + (f64x2.extract_lane 1 (f64x2.splat (local.get 0)))) + + ;; Integer arithmetic + (func (export "as-i8x16_add_sub-operands") (param i32 i32 i32) (result v128) + (i8x16.add (i8x16.splat (local.get 0)) + (i8x16.sub (i8x16.splat (local.get 1)) (i8x16.splat (local.get 2))))) + (func (export "as-i16x8_add_sub_mul-operands") (param i32 i32 i32 i32) (result v128) + (i16x8.add (i16x8.splat (local.get 0)) + (i16x8.sub (i16x8.splat (local.get 1)) + (i16x8.mul (i16x8.splat (local.get 2)) (i16x8.splat (local.get 3)))))) + (func (export "as-i32x4_add_sub_mul-operands") (param i32 i32 i32 i32) (result v128) + (i32x4.add (i32x4.splat (local.get 0)) + (i32x4.sub (i32x4.splat (local.get 1)) + (i32x4.mul (i32x4.splat (local.get 2)) (i32x4.splat (local.get 3)))))) + + (func (export "as-i64x2_add_sub_mul-operands") (param i64 i64 i64 i64) (result v128) + (i64x2.add (i64x2.splat (local.get 0)) + (i64x2.sub (i64x2.splat (local.get 1)) + (i64x2.mul (i64x2.splat (local.get 2)) (i64x2.splat (local.get 3)))))) + (func (export "as-f64x2_add_sub_mul-operands") (param f64 f64 f64 f64) (result v128) + (f64x2.add (f64x2.splat (local.get 0)) + (f64x2.sub (f64x2.splat (local.get 1)) + (f64x2.mul (f64x2.splat (local.get 2)) (f64x2.splat (local.get 3)))))) + + ;; Saturating integer arithmetic + (func (export "as-i8x16_add_sat_s-operands") (param i32 i32) (result v128) + (i8x16.add_sat_s (i8x16.splat (local.get 0)) (i8x16.splat (local.get 1)))) + (func (export "as-i16x8_add_sat_s-operands") (param i32 i32) (result v128) + (i16x8.add_sat_s (i16x8.splat (local.get 0)) (i16x8.splat (local.get 1)))) + (func (export "as-i8x16_sub_sat_u-operands") (param i32 i32) (result v128) + (i8x16.sub_sat_u (i8x16.splat (local.get 0)) (i8x16.splat (local.get 1)))) + (func (export "as-i16x8_sub_sat_u-operands") (param i32 i32) (result v128) + (i16x8.sub_sat_u (i16x8.splat (local.get 0)) (i16x8.splat (local.get 1)))) + + ;; Bit shifts + (func (export "as-i8x16_shr_s-operand") (param i32 i32) (result v128) + (i8x16.shr_s (i8x16.splat (local.get 0)) (local.get 1))) + (func (export "as-i16x8_shr_s-operand") (param i32 i32) (result v128) + (i16x8.shr_s (i16x8.splat (local.get 0)) (local.get 1))) + (func (export "as-i32x4_shr_s-operand") (param i32 i32) (result v128) + (i32x4.shr_s (i32x4.splat (local.get 0)) (local.get 1))) + + ;; Bitwise operantions + (func (export "as-v128_and-operands") (param i32 i32) (result v128) + (v128.and (i8x16.splat (local.get 0)) (i8x16.splat (local.get 1)))) + (func (export "as-v128_or-operands") (param i32 i32) (result v128) + (v128.or (i16x8.splat (local.get 0)) (i16x8.splat (local.get 1)))) + (func (export "as-v128_xor-operands") (param i32 i32) (result v128) + (v128.xor (i32x4.splat (local.get 0)) (i32x4.splat (local.get 1)))) + + ;; Boolean horizontal reductions + (func (export "as-i8x16_all_true-operand") (param i32) (result i32) + (i8x16.all_true (i8x16.splat (local.get 0)))) + (func (export "as-i16x8_all_true-operand") (param i32) (result i32) + (i16x8.all_true (i16x8.splat (local.get 0)))) + (func (export "as-i32x4_all_true-operand1") (param i32) (result i32) + (i32x4.all_true (i32x4.splat (local.get 0)))) + (func (export "as-i32x4_all_true-operand2") (param i64) (result i32) + (i32x4.all_true (i64x2.splat (local.get 0)))) + + ;; Comparisons + (func (export "as-i8x16_eq-operands") (param i32 i32) (result v128) + (i8x16.eq (i8x16.splat (local.get 0)) (i8x16.splat (local.get 1)))) + (func (export "as-i16x8_eq-operands") (param i32 i32) (result v128) + (i16x8.eq (i16x8.splat (local.get 0)) (i16x8.splat (local.get 1)))) + (func (export "as-i32x4_eq-operands1") (param i32 i32) (result v128) + (i32x4.eq (i32x4.splat (local.get 0)) (i32x4.splat (local.get 1)))) + (func (export "as-i32x4_eq-operands2") (param i64 i64) (result v128) + (i32x4.eq (i64x2.splat (local.get 0)) (i64x2.splat (local.get 1)))) + (func (export "as-f32x4_eq-operands") (param f32 f32) (result v128) + (f32x4.eq (f32x4.splat (local.get 0)) (f32x4.splat (local.get 1)))) + (func (export "as-f64x2_eq-operands") (param f64 f64) (result v128) + (f64x2.eq (f64x2.splat (local.get 0)) (f64x2.splat (local.get 1)))) + + ;; Floating-point sign bit operations + (func (export "as-f32x4_abs-operand") (param f32) (result v128) + (f32x4.abs (f32x4.splat (local.get 0)))) + + ;; Floating-point min + (func (export "as-f32x4_min-operands") (param f32 f32) (result v128) + (f32x4.min (f32x4.splat (local.get 0)) (f32x4.splat (local.get 1)))) + + ;; Floating-point arithmetic + (func (export "as-f32x4_div-operands") (param f32 f32) (result v128) + (f32x4.div (f32x4.splat (local.get 0)) (f32x4.splat (local.get 1)))) + + ;; Conversions + (func (export "as-f32x4_convert_s_i32x4-operand") (param i32) (result v128) + (f32x4.convert_i32x4_s (i32x4.splat (local.get 0)))) + (func (export "as-i32x4_trunc_s_f32x4_sat-operand") (param f32) (result v128) + (i32x4.trunc_sat_f32x4_s (f32x4.splat (local.get 0)))) +) + +(assert_return (invoke "as-i8x16_extract_lane_s-operand-first" (i32.const 42)) (i32.const 42)) +(assert_return (invoke "as-i8x16_extract_lane_s-operand-last" (i32.const -42)) (i32.const -42)) +(assert_return (invoke "as-i16x8_extract_lane_s-operand-first" (i32.const 0xffff7fff)) (i32.const 32767)) +(assert_return (invoke "as-i16x8_extract_lane_s-operand-last" (i32.const 0x8000)) (i32.const -32768)) +(assert_return (invoke "as-i32x4_extract_lane_s-operand-first" (i32.const 0x7fffffff)) (i32.const 2147483647)) +(assert_return (invoke "as-i32x4_extract_lane_s-operand-last" (i32.const 0x80000000)) (i32.const -2147483648)) +(assert_return (invoke "as-f32x4_extract_lane_s-operand-first" (f32.const 1.5)) (f32.const 1.5)) +(assert_return (invoke "as-f32x4_extract_lane_s-operand-last" (f32.const -0.25)) (f32.const -0.25)) +(assert_return (invoke "as-v8x16_swizzle-operands" (i32.const 1) (i32.const -1)) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "as-i64x2_extract_lane-operand-last" (i64.const -42)) (i64.const -42)) +(assert_return (invoke "as-i64x2_extract_lane-operand-first" (i64.const 42)) (i64.const 42)) +(assert_return (invoke "as-f64x2_extract_lane-operand-first" (f64.const 1.5)) (f64.const 1.5)) +(assert_return (invoke "as-f64x2_extract_lane-operand-last" (f64.const -0x1p+0)) (f64.const -0x1p+0)) + +(assert_return (invoke "as-i8x16_add_sub-operands" (i32.const 3) (i32.const 2) (i32.const 1)) (v128.const i8x16 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4)) +(assert_return (invoke "as-i16x8_add_sub_mul-operands" (i32.const 257) (i32.const 128) (i32.const 16) (i32.const 16)) (v128.const i16x8 129 129 129 129 129 129 129 129)) +(assert_return (invoke "as-i32x4_add_sub_mul-operands" (i32.const 65535) (i32.const 65537) (i32.const 256) (i32.const 256)) (v128.const i32x4 0x10000 0x10000 0x10000 0x10000)) +(assert_return (invoke "as-i64x2_add_sub_mul-operands" (i64.const 0x7fffffff) (i64.const 0x1_0000_0001) (i64.const 65536) (i64.const 65536)) (v128.const i64x2 0x8000_0000 0x8000_0000)) +(assert_return (invoke "as-f64x2_add_sub_mul-operands" (f64.const 0x1p-1) (f64.const 0.75) (f64.const 0x1p-1) (f64.const 0.5)) (v128.const f64x2 0x1p+0 0x1p+0)) + +(assert_return (invoke "as-i8x16_add_sat_s-operands" (i32.const 0x7f) (i32.const 1)) (v128.const i8x16 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f 0x7f)) +(assert_return (invoke "as-i16x8_add_sat_s-operands" (i32.const 0x7fff) (i32.const 1)) (v128.const i16x8 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff)) +(assert_return (invoke "as-i8x16_sub_sat_u-operands" (i32.const 0x7f) (i32.const 0xff)) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "as-i16x8_sub_sat_u-operands" (i32.const 0x7fff) (i32.const 0xffff)) (v128.const i16x8 0 0 0 0 0 0 0 0)) + +(assert_return (invoke "as-i8x16_shr_s-operand" (i32.const 0xf0) (i32.const 3)) (v128.const i8x16 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2)) +(assert_return (invoke "as-i16x8_shr_s-operand" (i32.const 0x100) (i32.const 4)) (v128.const i16x8 16 16 16 16 16 16 16 16)) +(assert_return (invoke "as-i32x4_shr_s-operand" (i32.const -1) (i32.const 16)) (v128.const i32x4 -1 -1 -1 -1)) + +(assert_return (invoke "as-v128_and-operands" (i32.const 0x11) (i32.const 0xff)) (v128.const i8x16 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17)) +(assert_return (invoke "as-v128_or-operands" (i32.const 0) (i32.const 0xffff)) (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "as-v128_xor-operands" (i32.const 0xf0f0f0f0) (i32.const 0xffffffff)) (v128.const i32x4 0xf0f0f0f 0xf0f0f0f 0xf0f0f0f 0xf0f0f0f)) + +(assert_return (invoke "as-i8x16_all_true-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-i16x8_all_true-operand" (i32.const 0xffff)) (i32.const 1)) +(assert_return (invoke "as-i32x4_all_true-operand1" (i32.const 0xf0f0f0f0)) (i32.const 1)) +(assert_return (invoke "as-i32x4_all_true-operand2" (i64.const -1)) (i32.const 1)) + +(assert_return (invoke "as-i8x16_eq-operands" (i32.const 1) (i32.const 2)) (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) +(assert_return (invoke "as-i16x8_eq-operands" (i32.const -1) (i32.const 65535)) (v128.const i16x8 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff)) +(assert_return (invoke "as-i32x4_eq-operands1" (i32.const -1) (i32.const 0xffffffff)) (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) +(assert_return (invoke "as-f32x4_eq-operands" (f32.const +0.0) (f32.const -0.0)) (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)) +(assert_return (invoke "as-i32x4_eq-operands2" (i64.const 1) (i64.const 2)) (v128.const i64x2 0xffffffff00000000 0xffffffff00000000)) +(assert_return (invoke "as-f64x2_eq-operands" (f64.const +0.0) (f64.const -0.0)) (v128.const i64x2 -1 -1)) + +(assert_return (invoke "as-f32x4_abs-operand" (f32.const -1.125)) (v128.const f32x4 1.125 1.125 1.125 1.125)) +(assert_return (invoke "as-f32x4_min-operands" (f32.const 0.25) (f32.const 1e-38)) (v128.const f32x4 1e-38 1e-38 1e-38 1e-38)) +(assert_return (invoke "as-f32x4_div-operands" (f32.const 1.0) (f32.const 8.0)) (v128.const f32x4 0.125 0.125 0.125 0.125)) + +(assert_return (invoke "as-f32x4_convert_s_i32x4-operand" (i32.const 12345)) (v128.const f32x4 12345.0 12345.0 12345.0 12345.0)) +(assert_return (invoke "as-i32x4_trunc_s_f32x4_sat-operand" (f32.const 1.1)) (v128.const i32x4 1 1 1 1)) + + +;; As the argument of control constructs and WASM instructions + +(module + (global $g (mut v128) (v128.const f32x4 0.0 0.0 0.0 0.0)) + (func (export "as-br-value1") (param i32) (result v128) + (block (result v128) (br 0 (i8x16.splat (local.get 0))))) + (func (export "as-return-value1") (param i32) (result v128) + (return (i16x8.splat (local.get 0)))) + (func (export "as-local_set-value1") (param i32) (result v128) (local v128) + (local.set 1 (i32x4.splat (local.get 0))) + (return (local.get 1))) + (func (export "as-global_set-value1") (param f32) (result v128) + (global.set $g (f32x4.splat (local.get 0))) + (return (global.get $g))) + (func (export "as-br-value2") (param i64) (result v128) + (block (result v128) (br 0 (i64x2.splat (local.get 0))))) + (func (export "as-return-value2") (param i64) (result v128) + (return (i64x2.splat (local.get 0)))) + (func (export "as-local_set-value2") (param i64) (result v128) (local v128) + (local.set 1 (i64x2.splat (local.get 0))) + (return (local.get 1))) + (func (export "as-global_set-value2") (param f64) (result v128) + (global.set $g (f64x2.splat (local.get 0))) + (return (global.get $g))) +) + +(assert_return (invoke "as-br-value1" (i32.const 0xAB)) (v128.const i8x16 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB 0xAB)) +(assert_return (invoke "as-return-value1" (i32.const 0xABCD)) (v128.const i16x8 0xABCD 0xABCD 0xABCD 0xABCD 0xABCD 0xABCD 0xABCD 0xABCD)) +(assert_return (invoke "as-local_set-value1" (i32.const 0x10000)) (v128.const i32x4 0x10000 0x10000 0x10000 0x10000)) +(assert_return (invoke "as-global_set-value1" (f32.const 1.0)) (v128.const f32x4 1.0 1.0 1.0 1.0)) +(assert_return (invoke "as-br-value2" (i64.const 0xABCD)) (v128.const i64x2 0xABCD 0xABCD)) +(assert_return (invoke "as-return-value2" (i64.const 0xABCD)) (v128.const i64x2 0xABCD 0xABCD)) +(assert_return (invoke "as-local_set-value2" (i64.const 0x10000)) (v128.const i64x2 0x10000 0x10000)) +(assert_return (invoke "as-global_set-value2" (f64.const 1.0)) (v128.const f64x2 1.0 1.0)) + + +;; Test operation with empty argument + +(assert_invalid + (module + (func $i8x16.splat-arg-empty (result v128) + (i8x16.splat) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i16x8.splat-arg-empty (result v128) + (i16x8.splat) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i32x4.splat-arg-empty (result v128) + (i32x4.splat) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f32x4.splat-arg-empty (result v128) + (f32x4.splat) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $i64x2.splat-arg-empty (result v128) + (i64x2.splat) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f64x2.splat-arg-empty (result v128) + (f64x2.splat) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_store.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_store.wast new file mode 100644 index 000000000..50349c41b --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_store.wast @@ -0,0 +1,166 @@ +;; v128.store operater with normal argument (e.g. (i8x16, i16x8, i32x4, f32x4)) + +(module + (memory 1) + (func (export "v128.store_i8x16") (result v128) + (v128.store (i32.const 0) (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) + (v128.load (i32.const 0)) + ) + (func (export "v128.store_i16x8") (result v128) + (v128.store (i32.const 0) (v128.const i16x8 0 1 2 3 4 5 6 7)) + (v128.load (i32.const 0)) + ) + (func (export "v128.store_i16x8_2") (result v128) + (v128.store (i32.const 0) (v128.const i16x8 012_345 012_345 012_345 012_345 012_345 012_345 012_345 012_345)) + (v128.load (i32.const 0)) + ) + (func (export "v128.store_i16x8_3") (result v128) + (v128.store (i32.const 0) (v128.const i16x8 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234 0x0_1234)) + (v128.load (i32.const 0)) + ) + (func (export "v128.store_i32x4") (result v128) + (v128.store (i32.const 0) (v128.const i32x4 0 1 2 3)) + (v128.load (i32.const 0)) + ) + (func (export "v128.store_i32x4_2") (result v128) + (v128.store (i32.const 0) (v128.const i32x4 0_123_456_789 0_123_456_789 0_123_456_789 0_123_456_789)) + (v128.load (i32.const 0)) + ) + (func (export "v128.store_i32x4_3") (result v128) + (v128.store (i32.const 0) (v128.const i32x4 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678 0x0_1234_5678)) + (v128.load (i32.const 0)) + ) + + (func (export "v128.store_f32x4") (result v128) + (v128.store (i32.const 0) (v128.const f32x4 0 1 2 3)) + (v128.load (i32.const 0)) + ) +) + +(assert_return (invoke "v128.store_i8x16") (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) +(assert_return (invoke "v128.store_i16x8") (v128.const i16x8 0 1 2 3 4 5 6 7)) +(assert_return (invoke "v128.store_i16x8_2") (v128.const i16x8 12345 12345 12345 12345 12345 12345 12345 12345)) +(assert_return (invoke "v128.store_i16x8_3") (v128.const i16x8 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234 0x1234)) +(assert_return (invoke "v128.store_i32x4") (v128.const i32x4 0 1 2 3)) +(assert_return (invoke "v128.store_i32x4_2") (v128.const i32x4 123456789 123456789 123456789 123456789)) +(assert_return (invoke "v128.store_i32x4_3") (v128.const i32x4 0x12345678 0x12345678 0x12345678 0x12345678)) +(assert_return (invoke "v128.store_f32x4") (v128.const f32x4 0 1 2 3)) + + +;; v128.store operator as the argument of control constructs and instructions + +(module + (memory 1) + (func (export "as-block-value") + (block (v128.store (i32.const 0) (v128.const i32x4 0 0 0 0))) + ) + (func (export "as-loop-value") + (loop (v128.store (i32.const 0) (v128.const i32x4 0 0 0 0))) + ) + (func (export "as-br-value") + (block (br 0 (v128.store (i32.const 0) (v128.const i32x4 0 0 0 0)))) + ) + (func (export "as-br_if-value") + (block + (br_if 0 (v128.store (i32.const 0) (v128.const i32x4 0 0 0 0)) (i32.const 1)) + ) + ) + (func (export "as-br_if-value-cond") + (block + (br_if 0 (i32.const 6) (v128.store (i32.const 0) (v128.const i32x4 0 0 0 0))) + ) + ) + (func (export "as-br_table-value") + (block + (br_table 0 (v128.store (i32.const 0) (v128.const i32x4 0 0 0 0)) (i32.const 1)) + ) + ) + (func (export "as-return-value") + (return (v128.store (i32.const 0) (v128.const i32x4 0 0 0 0))) + ) + (func (export "as-if-then") + (if (i32.const 1) (then (v128.store (i32.const 0) (v128.const i32x4 0 0 0 0)))) + ) + (func (export "as-if-else") + (if (i32.const 0) (then) (else (v128.store (i32.const 0) (v128.const i32x4 0 0 0 0)))) + ) +) + +(assert_return (invoke "as-block-value")) +(assert_return (invoke "as-loop-value")) +(assert_return (invoke "as-br-value")) +(assert_return (invoke "as-br_if-value")) +(assert_return (invoke "as-br_if-value-cond")) +(assert_return (invoke "as-br_table-value")) +(assert_return (invoke "as-return-value")) +(assert_return (invoke "as-if-then")) +(assert_return (invoke "as-if-else")) + + +;; Unknown operator(e.g. v128.store8, v128.store16, v128.store32) + +(assert_malformed + (module quote + "(memory 1)" + "(func (v128.store8 (i32.const 0) (v128.const i32x4 0 0 0 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1)" + "(func (v128.store16 (i32.const 0) (v128.const i32x4 0 0 0 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1)" + "(func (v128.store32 (i32.const 0) (v128.const i32x4 0 0 0 0)))" + ) + "unknown operator" +) + + +;; Type mismatched (e.g. v128.load(f32.const 0), type address empty) + +(assert_invalid + (module (memory 1) (func (v128.store (f32.const 0) (v128.const i32x4 0 0 0 0)))) + "type mismatch" +) +(assert_invalid + (module (memory 1) (func (local v128) (block (br_if 0 (v128.store))))) + "type mismatch" +) +(assert_invalid + (module (memory 1) (func (result v128) (v128.store (i32.const 0) (v128.const i32x4 0 0 0 0)))) + "type mismatch" +) + + +;; Test operation with empty argument + +(assert_invalid + (module (memory 0) + (func $v128.store-1st-arg-empty + (v128.store (v128.const i32x4 0 0 0 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module (memory 0) + (func $v128.store-2nd-arg-empty + (v128.store (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module (memory 0) + (func $v128.store-arg-empty + (v128.store) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_store16_lane.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_store16_lane.wast new file mode 100644 index 000000000..d8ea35c3f --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_store16_lane.wast @@ -0,0 +1,299 @@ +;; Tests for store lane operations. + + +(module + (memory 1) + (global $zero (mut v128) (v128.const i32x4 0 0 0 0)) + (func (export "v128.store16_lane_0") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane 0 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store16_lane_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane 1 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store16_lane_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane 2 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store16_lane_3") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane 3 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store16_lane_4") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane 4 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store16_lane_5") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane 5 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store16_lane_6") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane 6 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store16_lane_7") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane 7 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store16_lane_0_offset_0") + (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane offset=0 0 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=0 (i32.const 0))) + (v128.store offset=0 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_1_offset_1") + (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane offset=1 1 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=1 (i32.const 0))) + (v128.store offset=1 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_2_offset_2") + (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane offset=2 2 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=2 (i32.const 0))) + (v128.store offset=2 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_3_offset_3") + (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane offset=3 3 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=3 (i32.const 0))) + (v128.store offset=3 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_4_offset_4") + (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane offset=4 4 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=4 (i32.const 0))) + (v128.store offset=4 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_5_offset_5") + (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane offset=5 5 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=5 (i32.const 0))) + (v128.store offset=5 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_6_offset_6") + (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane offset=6 6 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=6 (i32.const 0))) + (v128.store offset=6 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_7_offset_7") + (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane offset=7 7 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=7 (i32.const 0))) + (v128.store offset=7 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_0_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=1 0 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=0 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_0_align_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=2 0 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=0 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_1_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=1 1 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=1 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_1_align_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=2 1 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=1 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_2_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=1 2 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=2 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_2_align_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=2 2 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=2 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_3_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=1 3 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=3 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_3_align_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=2 3 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=3 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_4_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=1 4 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=4 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_4_align_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=2 4 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=4 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_5_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=1 5 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=5 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_5_align_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=2 5 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=5 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_6_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=1 6 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=6 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_6_align_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=2 6 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=6 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_7_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=1 7 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=7 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store16_lane_7_align_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store16_lane align=2 7 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=7 (i32.const 0) (global.get $zero)) + (local.get $ret)) +) + +(assert_return (invoke "v128.store16_lane_0" (i32.const 0) + (v128.const i16x8 256 0 0 0 0 0 0 0)) + (i64.const 256)) +(assert_return (invoke "v128.store16_lane_1" (i32.const 1) + (v128.const i16x8 0 513 0 0 0 0 0 0)) + (i64.const 513)) +(assert_return (invoke "v128.store16_lane_2" (i32.const 2) + (v128.const i16x8 0 0 770 0 0 0 0 0)) + (i64.const 770)) +(assert_return (invoke "v128.store16_lane_3" (i32.const 3) + (v128.const i16x8 0 0 0 1027 0 0 0 0)) + (i64.const 1027)) +(assert_return (invoke "v128.store16_lane_4" (i32.const 4) + (v128.const i16x8 0 0 0 0 1284 0 0 0)) + (i64.const 1284)) +(assert_return (invoke "v128.store16_lane_5" (i32.const 5) + (v128.const i16x8 0 0 0 0 0 1541 0 0)) + (i64.const 1541)) +(assert_return (invoke "v128.store16_lane_6" (i32.const 6) + (v128.const i16x8 0 0 0 0 0 0 1798 0)) + (i64.const 1798)) +(assert_return (invoke "v128.store16_lane_7" (i32.const 7) + (v128.const i16x8 0 0 0 0 0 0 0 2055)) + (i64.const 2055)) +(assert_return (invoke "v128.store16_lane_0_offset_0" (v128.const i16x8 256 0 0 0 0 0 0 0)) + (i64.const 256)) +(assert_return (invoke "v128.store16_lane_1_offset_1" (v128.const i16x8 0 513 0 0 0 0 0 0)) + (i64.const 513)) +(assert_return (invoke "v128.store16_lane_2_offset_2" (v128.const i16x8 0 0 770 0 0 0 0 0)) + (i64.const 770)) +(assert_return (invoke "v128.store16_lane_3_offset_3" (v128.const i16x8 0 0 0 1027 0 0 0 0)) + (i64.const 1027)) +(assert_return (invoke "v128.store16_lane_4_offset_4" (v128.const i16x8 0 0 0 0 1284 0 0 0)) + (i64.const 1284)) +(assert_return (invoke "v128.store16_lane_5_offset_5" (v128.const i16x8 0 0 0 0 0 1541 0 0)) + (i64.const 1541)) +(assert_return (invoke "v128.store16_lane_6_offset_6" (v128.const i16x8 0 0 0 0 0 0 1798 0)) + (i64.const 1798)) +(assert_return (invoke "v128.store16_lane_7_offset_7" (v128.const i16x8 0 0 0 0 0 0 0 2055)) + (i64.const 2055)) +(assert_return (invoke "v128.store16_lane_0_align_1" (i32.const 0) + (v128.const i16x8 256 0 0 0 0 0 0 0)) + (i64.const 256)) +(assert_return (invoke "v128.store16_lane_0_align_2" (i32.const 0) + (v128.const i16x8 256 0 0 0 0 0 0 0)) + (i64.const 256)) +(assert_return (invoke "v128.store16_lane_1_align_1" (i32.const 1) + (v128.const i16x8 0 513 0 0 0 0 0 0)) + (i64.const 513)) +(assert_return (invoke "v128.store16_lane_1_align_2" (i32.const 1) + (v128.const i16x8 0 513 0 0 0 0 0 0)) + (i64.const 513)) +(assert_return (invoke "v128.store16_lane_2_align_1" (i32.const 2) + (v128.const i16x8 0 0 770 0 0 0 0 0)) + (i64.const 770)) +(assert_return (invoke "v128.store16_lane_2_align_2" (i32.const 2) + (v128.const i16x8 0 0 770 0 0 0 0 0)) + (i64.const 770)) +(assert_return (invoke "v128.store16_lane_3_align_1" (i32.const 3) + (v128.const i16x8 0 0 0 1027 0 0 0 0)) + (i64.const 1027)) +(assert_return (invoke "v128.store16_lane_3_align_2" (i32.const 3) + (v128.const i16x8 0 0 0 1027 0 0 0 0)) + (i64.const 1027)) +(assert_return (invoke "v128.store16_lane_4_align_1" (i32.const 4) + (v128.const i16x8 0 0 0 0 1284 0 0 0)) + (i64.const 1284)) +(assert_return (invoke "v128.store16_lane_4_align_2" (i32.const 4) + (v128.const i16x8 0 0 0 0 1284 0 0 0)) + (i64.const 1284)) +(assert_return (invoke "v128.store16_lane_5_align_1" (i32.const 5) + (v128.const i16x8 0 0 0 0 0 1541 0 0)) + (i64.const 1541)) +(assert_return (invoke "v128.store16_lane_5_align_2" (i32.const 5) + (v128.const i16x8 0 0 0 0 0 1541 0 0)) + (i64.const 1541)) +(assert_return (invoke "v128.store16_lane_6_align_1" (i32.const 6) + (v128.const i16x8 0 0 0 0 0 0 1798 0)) + (i64.const 1798)) +(assert_return (invoke "v128.store16_lane_6_align_2" (i32.const 6) + (v128.const i16x8 0 0 0 0 0 0 1798 0)) + (i64.const 1798)) +(assert_return (invoke "v128.store16_lane_7_align_1" (i32.const 7) + (v128.const i16x8 0 0 0 0 0 0 0 2055)) + (i64.const 2055)) +(assert_return (invoke "v128.store16_lane_7_align_2" (i32.const 7) + (v128.const i16x8 0 0 0 0 0 0 0 2055)) + (i64.const 2055)) + +;; type check +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.store16_lane 0 (local.get $x) (i32.const 0)))) + "type mismatch") + +;; invalid lane index +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.store16_lane 8 (i32.const 0) (local.get $x)))) + "invalid lane index") + +;; invalid memarg alignment +(assert_invalid + (module (memory 1) + (func (param $x v128) (result v128) + (v128.store16_lane align=4 0 (i32.const 0) (local.get $x)))) + "alignment must not be larger than natural") \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_store32_lane.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_store32_lane.wast new file mode 100644 index 000000000..847bab9e6 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_store32_lane.wast @@ -0,0 +1,199 @@ +;; Tests for store lane operations. + + +(module + (memory 1) + (global $zero (mut v128) (v128.const i32x4 0 0 0 0)) + (func (export "v128.store32_lane_0") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane 0 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store32_lane_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane 1 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store32_lane_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane 2 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store32_lane_3") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane 3 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store32_lane_0_offset_0") + (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane offset=0 0 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=0 (i32.const 0))) + (v128.store offset=0 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store32_lane_1_offset_1") + (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane offset=1 1 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=1 (i32.const 0))) + (v128.store offset=1 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store32_lane_2_offset_2") + (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane offset=2 2 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=2 (i32.const 0))) + (v128.store offset=2 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store32_lane_3_offset_3") + (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane offset=3 3 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=3 (i32.const 0))) + (v128.store offset=3 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store32_lane_0_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane align=1 0 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=0 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store32_lane_0_align_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane align=2 0 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=0 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store32_lane_0_align_4") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane align=4 0 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=0 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store32_lane_1_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane align=1 1 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=1 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store32_lane_1_align_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane align=2 1 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=1 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store32_lane_1_align_4") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane align=4 1 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=1 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store32_lane_2_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane align=1 2 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=2 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store32_lane_2_align_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane align=2 2 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=2 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store32_lane_2_align_4") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane align=4 2 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=2 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store32_lane_3_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane align=1 3 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=3 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store32_lane_3_align_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane align=2 3 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=3 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store32_lane_3_align_4") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store32_lane align=4 3 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=3 (i32.const 0) (global.get $zero)) + (local.get $ret)) +) + +(assert_return (invoke "v128.store32_lane_0" (i32.const 0) + (v128.const i32x4 50462976 0 0 0)) + (i64.const 50462976)) +(assert_return (invoke "v128.store32_lane_1" (i32.const 1) + (v128.const i32x4 0 67305985 0 0)) + (i64.const 67305985)) +(assert_return (invoke "v128.store32_lane_2" (i32.const 2) + (v128.const i32x4 0 0 84148994 0)) + (i64.const 84148994)) +(assert_return (invoke "v128.store32_lane_3" (i32.const 3) + (v128.const i32x4 0 0 0 100992003)) + (i64.const 100992003)) +(assert_return (invoke "v128.store32_lane_0_offset_0" (v128.const i32x4 50462976 0 0 0)) + (i64.const 50462976)) +(assert_return (invoke "v128.store32_lane_1_offset_1" (v128.const i32x4 0 67305985 0 0)) + (i64.const 67305985)) +(assert_return (invoke "v128.store32_lane_2_offset_2" (v128.const i32x4 0 0 84148994 0)) + (i64.const 84148994)) +(assert_return (invoke "v128.store32_lane_3_offset_3" (v128.const i32x4 0 0 0 100992003)) + (i64.const 100992003)) +(assert_return (invoke "v128.store32_lane_0_align_1" (i32.const 0) + (v128.const i32x4 50462976 0 0 0)) + (i64.const 50462976)) +(assert_return (invoke "v128.store32_lane_0_align_2" (i32.const 0) + (v128.const i32x4 50462976 0 0 0)) + (i64.const 50462976)) +(assert_return (invoke "v128.store32_lane_0_align_4" (i32.const 0) + (v128.const i32x4 50462976 0 0 0)) + (i64.const 50462976)) +(assert_return (invoke "v128.store32_lane_1_align_1" (i32.const 1) + (v128.const i32x4 0 67305985 0 0)) + (i64.const 67305985)) +(assert_return (invoke "v128.store32_lane_1_align_2" (i32.const 1) + (v128.const i32x4 0 67305985 0 0)) + (i64.const 67305985)) +(assert_return (invoke "v128.store32_lane_1_align_4" (i32.const 1) + (v128.const i32x4 0 67305985 0 0)) + (i64.const 67305985)) +(assert_return (invoke "v128.store32_lane_2_align_1" (i32.const 2) + (v128.const i32x4 0 0 84148994 0)) + (i64.const 84148994)) +(assert_return (invoke "v128.store32_lane_2_align_2" (i32.const 2) + (v128.const i32x4 0 0 84148994 0)) + (i64.const 84148994)) +(assert_return (invoke "v128.store32_lane_2_align_4" (i32.const 2) + (v128.const i32x4 0 0 84148994 0)) + (i64.const 84148994)) +(assert_return (invoke "v128.store32_lane_3_align_1" (i32.const 3) + (v128.const i32x4 0 0 0 100992003)) + (i64.const 100992003)) +(assert_return (invoke "v128.store32_lane_3_align_2" (i32.const 3) + (v128.const i32x4 0 0 0 100992003)) + (i64.const 100992003)) +(assert_return (invoke "v128.store32_lane_3_align_4" (i32.const 3) + (v128.const i32x4 0 0 0 100992003)) + (i64.const 100992003)) + +;; type check +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.store32_lane 0 (local.get $x) (i32.const 0)))) + "type mismatch") + +;; invalid lane index +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.store32_lane 4 (i32.const 0) (local.get $x)))) + "invalid lane index") + +;; invalid memarg alignment +(assert_invalid + (module (memory 1) + (func (param $x v128) (result v128) + (v128.store32_lane align=8 0 (i32.const 0) (local.get $x)))) + "alignment must not be larger than natural") \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_store64_lane.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_store64_lane.wast new file mode 100644 index 000000000..2ed1dcd68 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_store64_lane.wast @@ -0,0 +1,131 @@ +;; Tests for store lane operations. + + +(module + (memory 1) + (global $zero (mut v128) (v128.const i32x4 0 0 0 0)) + (func (export "v128.store64_lane_0") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store64_lane 0 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store64_lane_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store64_lane 1 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store64_lane_0_offset_0") + (param $x v128) (result i64) (local $ret i64) + (v128.store64_lane offset=0 0 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=0 (i32.const 0))) + (v128.store offset=0 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store64_lane_1_offset_1") + (param $x v128) (result i64) (local $ret i64) + (v128.store64_lane offset=1 1 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=1 (i32.const 0))) + (v128.store offset=1 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store64_lane_0_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store64_lane align=1 0 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=0 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store64_lane_0_align_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store64_lane align=2 0 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=0 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store64_lane_0_align_4") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store64_lane align=4 0 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=0 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store64_lane_0_align_8") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store64_lane align=8 0 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=0 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store64_lane_1_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store64_lane align=1 1 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=1 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store64_lane_1_align_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store64_lane align=2 1 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=1 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store64_lane_1_align_4") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store64_lane align=4 1 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=1 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store64_lane_1_align_8") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store64_lane align=8 1 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=1 (i32.const 0) (global.get $zero)) + (local.get $ret)) +) + +(assert_return (invoke "v128.store64_lane_0" (i32.const 0) + (v128.const i64x2 506097522914230528 0)) + (i64.const 506097522914230528)) +(assert_return (invoke "v128.store64_lane_1" (i32.const 1) + (v128.const i64x2 0 578437695752307201)) + (i64.const 578437695752307201)) +(assert_return (invoke "v128.store64_lane_0_offset_0" (v128.const i64x2 506097522914230528 0)) + (i64.const 506097522914230528)) +(assert_return (invoke "v128.store64_lane_1_offset_1" (v128.const i64x2 0 578437695752307201)) + (i64.const 578437695752307201)) +(assert_return (invoke "v128.store64_lane_0_align_1" (i32.const 0) + (v128.const i64x2 506097522914230528 0)) + (i64.const 506097522914230528)) +(assert_return (invoke "v128.store64_lane_0_align_2" (i32.const 0) + (v128.const i64x2 506097522914230528 0)) + (i64.const 506097522914230528)) +(assert_return (invoke "v128.store64_lane_0_align_4" (i32.const 0) + (v128.const i64x2 506097522914230528 0)) + (i64.const 506097522914230528)) +(assert_return (invoke "v128.store64_lane_0_align_8" (i32.const 0) + (v128.const i64x2 506097522914230528 0)) + (i64.const 506097522914230528)) +(assert_return (invoke "v128.store64_lane_1_align_1" (i32.const 1) + (v128.const i64x2 0 578437695752307201)) + (i64.const 578437695752307201)) +(assert_return (invoke "v128.store64_lane_1_align_2" (i32.const 1) + (v128.const i64x2 0 578437695752307201)) + (i64.const 578437695752307201)) +(assert_return (invoke "v128.store64_lane_1_align_4" (i32.const 1) + (v128.const i64x2 0 578437695752307201)) + (i64.const 578437695752307201)) +(assert_return (invoke "v128.store64_lane_1_align_8" (i32.const 1) + (v128.const i64x2 0 578437695752307201)) + (i64.const 578437695752307201)) + +;; type check +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.store64_lane 0 (local.get $x) (i32.const 0)))) + "type mismatch") + +;; invalid lane index +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.store64_lane 2 (i32.const 0) (local.get $x)))) + "invalid lane index") + +;; invalid memarg alignment +(assert_invalid + (module (memory 1) + (func (param $x v128) (result v128) + (v128.store64_lane align=16 0 (i32.const 0) (local.get $x)))) + "alignment must not be larger than natural") \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_store8_lane.wast b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_store8_lane.wast new file mode 100644 index 000000000..7258a40dd --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/simd/simd_store8_lane.wast @@ -0,0 +1,427 @@ +;; Tests for store lane operations. + + +(module + (memory 1) + (global $zero (mut v128) (v128.const i32x4 0 0 0 0)) + (func (export "v128.store8_lane_0") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 0 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 1 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_2") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 2 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_3") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 3 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_4") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 4 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_5") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 5 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_6") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 6 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_7") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 7 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_8") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 8 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_9") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 9 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_10") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 10 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_11") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 11 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_12") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 12 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_13") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 13 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_14") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 14 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_15") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane 15 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store (local.get $address) (global.get $zero)) (local.get $ret)) + (func (export "v128.store8_lane_0_offset_0") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=0 0 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=0 (i32.const 0))) + (v128.store offset=0 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_1_offset_1") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=1 1 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=1 (i32.const 0))) + (v128.store offset=1 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_2_offset_2") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=2 2 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=2 (i32.const 0))) + (v128.store offset=2 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_3_offset_3") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=3 3 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=3 (i32.const 0))) + (v128.store offset=3 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_4_offset_4") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=4 4 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=4 (i32.const 0))) + (v128.store offset=4 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_5_offset_5") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=5 5 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=5 (i32.const 0))) + (v128.store offset=5 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_6_offset_6") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=6 6 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=6 (i32.const 0))) + (v128.store offset=6 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_7_offset_7") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=7 7 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=7 (i32.const 0))) + (v128.store offset=7 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_8_offset_8") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=8 8 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=8 (i32.const 0))) + (v128.store offset=8 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_9_offset_9") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=9 9 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=9 (i32.const 0))) + (v128.store offset=9 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_10_offset_10") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=10 10 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=10 (i32.const 0))) + (v128.store offset=10 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_11_offset_11") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=11 11 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=11 (i32.const 0))) + (v128.store offset=11 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_12_offset_12") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=12 12 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=12 (i32.const 0))) + (v128.store offset=12 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_13_offset_13") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=13 13 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=13 (i32.const 0))) + (v128.store offset=13 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_14_offset_14") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=14 14 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=14 (i32.const 0))) + (v128.store offset=14 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_15_offset_15") + (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane offset=15 15 (i32.const 0) (local.get $x)) + (local.set $ret (i64.load offset=15 (i32.const 0))) + (v128.store offset=15 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_0_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 0 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=0 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_1_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 1 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=1 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_2_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 2 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=2 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_3_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 3 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=3 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_4_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 4 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=4 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_5_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 5 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=5 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_6_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 6 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=6 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_7_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 7 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=7 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_8_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 8 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=8 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_9_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 9 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=9 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_10_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 10 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=10 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_11_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 11 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=11 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_12_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 12 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=12 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_13_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 13 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=13 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_14_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 14 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=14 (i32.const 0) (global.get $zero)) + (local.get $ret)) + (func (export "v128.store8_lane_15_align_1") + (param $address i32) (param $x v128) (result i64) (local $ret i64) + (v128.store8_lane align=1 15 (local.get $address) (local.get $x)) + (local.set $ret (i64.load (local.get $address))) + (v128.store offset=15 (i32.const 0) (global.get $zero)) + (local.get $ret)) +) + +(assert_return (invoke "v128.store8_lane_0" (i32.const 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i64.const 0)) +(assert_return (invoke "v128.store8_lane_1" (i32.const 1) + (v128.const i8x16 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i64.const 1)) +(assert_return (invoke "v128.store8_lane_2" (i32.const 2) + (v128.const i8x16 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i64.const 2)) +(assert_return (invoke "v128.store8_lane_3" (i32.const 3) + (v128.const i8x16 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0)) + (i64.const 3)) +(assert_return (invoke "v128.store8_lane_4" (i32.const 4) + (v128.const i8x16 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0)) + (i64.const 4)) +(assert_return (invoke "v128.store8_lane_5" (i32.const 5) + (v128.const i8x16 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0)) + (i64.const 5)) +(assert_return (invoke "v128.store8_lane_6" (i32.const 6) + (v128.const i8x16 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0)) + (i64.const 6)) +(assert_return (invoke "v128.store8_lane_7" (i32.const 7) + (v128.const i8x16 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0)) + (i64.const 7)) +(assert_return (invoke "v128.store8_lane_8" (i32.const 8) + (v128.const i8x16 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0)) + (i64.const 8)) +(assert_return (invoke "v128.store8_lane_9" (i32.const 9) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0)) + (i64.const 9)) +(assert_return (invoke "v128.store8_lane_10" (i32.const 10) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0)) + (i64.const 10)) +(assert_return (invoke "v128.store8_lane_11" (i32.const 11) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0)) + (i64.const 11)) +(assert_return (invoke "v128.store8_lane_12" (i32.const 12) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0)) + (i64.const 12)) +(assert_return (invoke "v128.store8_lane_13" (i32.const 13) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0)) + (i64.const 13)) +(assert_return (invoke "v128.store8_lane_14" (i32.const 14) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 0)) + (i64.const 14)) +(assert_return (invoke "v128.store8_lane_15" (i32.const 15) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15)) + (i64.const 15)) +(assert_return (invoke "v128.store8_lane_0_offset_0" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i64.const 0)) +(assert_return (invoke "v128.store8_lane_1_offset_1" (v128.const i8x16 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i64.const 1)) +(assert_return (invoke "v128.store8_lane_2_offset_2" (v128.const i8x16 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i64.const 2)) +(assert_return (invoke "v128.store8_lane_3_offset_3" (v128.const i8x16 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0)) + (i64.const 3)) +(assert_return (invoke "v128.store8_lane_4_offset_4" (v128.const i8x16 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0)) + (i64.const 4)) +(assert_return (invoke "v128.store8_lane_5_offset_5" (v128.const i8x16 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0)) + (i64.const 5)) +(assert_return (invoke "v128.store8_lane_6_offset_6" (v128.const i8x16 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0)) + (i64.const 6)) +(assert_return (invoke "v128.store8_lane_7_offset_7" (v128.const i8x16 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0)) + (i64.const 7)) +(assert_return (invoke "v128.store8_lane_8_offset_8" (v128.const i8x16 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0)) + (i64.const 8)) +(assert_return (invoke "v128.store8_lane_9_offset_9" (v128.const i8x16 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0)) + (i64.const 9)) +(assert_return (invoke "v128.store8_lane_10_offset_10" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0)) + (i64.const 10)) +(assert_return (invoke "v128.store8_lane_11_offset_11" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0)) + (i64.const 11)) +(assert_return (invoke "v128.store8_lane_12_offset_12" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0)) + (i64.const 12)) +(assert_return (invoke "v128.store8_lane_13_offset_13" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0)) + (i64.const 13)) +(assert_return (invoke "v128.store8_lane_14_offset_14" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 0)) + (i64.const 14)) +(assert_return (invoke "v128.store8_lane_15_offset_15" (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15)) + (i64.const 15)) +(assert_return (invoke "v128.store8_lane_0_align_1" (i32.const 0) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i64.const 0)) +(assert_return (invoke "v128.store8_lane_1_align_1" (i32.const 1) + (v128.const i8x16 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i64.const 1)) +(assert_return (invoke "v128.store8_lane_2_align_1" (i32.const 2) + (v128.const i8x16 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0)) + (i64.const 2)) +(assert_return (invoke "v128.store8_lane_3_align_1" (i32.const 3) + (v128.const i8x16 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0)) + (i64.const 3)) +(assert_return (invoke "v128.store8_lane_4_align_1" (i32.const 4) + (v128.const i8x16 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0)) + (i64.const 4)) +(assert_return (invoke "v128.store8_lane_5_align_1" (i32.const 5) + (v128.const i8x16 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0)) + (i64.const 5)) +(assert_return (invoke "v128.store8_lane_6_align_1" (i32.const 6) + (v128.const i8x16 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0)) + (i64.const 6)) +(assert_return (invoke "v128.store8_lane_7_align_1" (i32.const 7) + (v128.const i8x16 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0)) + (i64.const 7)) +(assert_return (invoke "v128.store8_lane_8_align_1" (i32.const 8) + (v128.const i8x16 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0)) + (i64.const 8)) +(assert_return (invoke "v128.store8_lane_9_align_1" (i32.const 9) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0)) + (i64.const 9)) +(assert_return (invoke "v128.store8_lane_10_align_1" (i32.const 10) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0)) + (i64.const 10)) +(assert_return (invoke "v128.store8_lane_11_align_1" (i32.const 11) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0)) + (i64.const 11)) +(assert_return (invoke "v128.store8_lane_12_align_1" (i32.const 12) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 12 0 0 0)) + (i64.const 12)) +(assert_return (invoke "v128.store8_lane_13_align_1" (i32.const 13) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0)) + (i64.const 13)) +(assert_return (invoke "v128.store8_lane_14_align_1" (i32.const 14) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 0)) + (i64.const 14)) +(assert_return (invoke "v128.store8_lane_15_align_1" (i32.const 15) + (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15)) + (i64.const 15)) + +;; type check +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.store8_lane 0 (local.get $x) (i32.const 0)))) + "type mismatch") + +;; invalid lane index +(assert_invalid (module (memory 1) + (func (param $x v128) (result v128) + (v128.store8_lane 16 (i32.const 0) (local.get $x)))) + "invalid lane index") + +;; invalid memarg alignment +(assert_invalid + (module (memory 1) + (func (param $x v128) (result v128) + (v128.store8_lane align=2 0 (i32.const 0) (local.get $x)))) + "alignment must not be larger than natural") \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/spec/proposals/tail-call/return_call.wast b/runtime/unc-vm/tests/wast/spec/proposals/tail-call/return_call.wast new file mode 100644 index 000000000..2f91f4dea --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/tail-call/return_call.wast @@ -0,0 +1,202 @@ +;; Test `return_call` operator + +(module + ;; Auxiliary definitions + (func $const-i32 (result i32) (i32.const 0x132)) + (func $const-i64 (result i64) (i64.const 0x164)) + (func $const-f32 (result f32) (f32.const 0xf32)) + (func $const-f64 (result f64) (f64.const 0xf64)) + + (func $id-i32 (param i32) (result i32) (local.get 0)) + (func $id-i64 (param i64) (result i64) (local.get 0)) + (func $id-f32 (param f32) (result f32) (local.get 0)) + (func $id-f64 (param f64) (result f64) (local.get 0)) + + (func $f32-i32 (param f32 i32) (result i32) (local.get 1)) + (func $i32-i64 (param i32 i64) (result i64) (local.get 1)) + (func $f64-f32 (param f64 f32) (result f32) (local.get 1)) + (func $i64-f64 (param i64 f64) (result f64) (local.get 1)) + + ;; Typing + + (func (export "type-i32") (result i32) (return_call $const-i32)) + (func (export "type-i64") (result i64) (return_call $const-i64)) + (func (export "type-f32") (result f32) (return_call $const-f32)) + (func (export "type-f64") (result f64) (return_call $const-f64)) + + (func (export "type-first-i32") (result i32) (return_call $id-i32 (i32.const 32))) + (func (export "type-first-i64") (result i64) (return_call $id-i64 (i64.const 64))) + (func (export "type-first-f32") (result f32) (return_call $id-f32 (f32.const 1.32))) + (func (export "type-first-f64") (result f64) (return_call $id-f64 (f64.const 1.64))) + + (func (export "type-second-i32") (result i32) + (return_call $f32-i32 (f32.const 32.1) (i32.const 32)) + ) + (func (export "type-second-i64") (result i64) + (return_call $i32-i64 (i32.const 32) (i64.const 64)) + ) + (func (export "type-second-f32") (result f32) + (return_call $f64-f32 (f64.const 64) (f32.const 32)) + ) + (func (export "type-second-f64") (result f64) + (return_call $i64-f64 (i64.const 64) (f64.const 64.1)) + ) + + ;; Recursion + + (func $fac-acc (export "fac-acc") (param i64 i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (local.get 1)) + (else + (return_call $fac-acc + (i64.sub (local.get 0) (i64.const 1)) + (i64.mul (local.get 0) (local.get 1)) + ) + ) + ) + ) + + (func $count (export "count") (param i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (local.get 0)) + (else (return_call $count (i64.sub (local.get 0) (i64.const 1)))) + ) + ) + + (func $even (export "even") (param i64) (result i32) + (if (result i32) (i64.eqz (local.get 0)) + (then (i32.const 44)) + (else (return_call $odd (i64.sub (local.get 0) (i64.const 1)))) + ) + ) + (func $odd (export "odd") (param i64) (result i32) + (if (result i32) (i64.eqz (local.get 0)) + (then (i32.const 99)) + (else (return_call $even (i64.sub (local.get 0) (i64.const 1)))) + ) + ) +) + +(assert_return (invoke "type-i32") (i32.const 0x132)) +(assert_return (invoke "type-i64") (i64.const 0x164)) +(assert_return (invoke "type-f32") (f32.const 0xf32)) +(assert_return (invoke "type-f64") (f64.const 0xf64)) + +(assert_return (invoke "type-first-i32") (i32.const 32)) +(assert_return (invoke "type-first-i64") (i64.const 64)) +(assert_return (invoke "type-first-f32") (f32.const 1.32)) +(assert_return (invoke "type-first-f64") (f64.const 1.64)) + +(assert_return (invoke "type-second-i32") (i32.const 32)) +(assert_return (invoke "type-second-i64") (i64.const 64)) +(assert_return (invoke "type-second-f32") (f32.const 32)) +(assert_return (invoke "type-second-f64") (f64.const 64.1)) + +(assert_return (invoke "fac-acc" (i64.const 0) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 5) (i64.const 1)) (i64.const 120)) +(assert_return + (invoke "fac-acc" (i64.const 25) (i64.const 1)) + (i64.const 7034535277573963776) +) + +(assert_return (invoke "count" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "count" (i64.const 1000)) (i64.const 0)) +(assert_return (invoke "count" (i64.const 1_000_000)) (i64.const 0)) + +(assert_return (invoke "even" (i64.const 0)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 1)) (i32.const 99)) +(assert_return (invoke "even" (i64.const 100)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 77)) (i32.const 99)) +(assert_return (invoke "even" (i64.const 1_000_000)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 1_000_001)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 0)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 1)) (i32.const 44)) +(assert_return (invoke "odd" (i64.const 200)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 77)) (i32.const 44)) +(assert_return (invoke "odd" (i64.const 1_000_000)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 999_999)) (i32.const 44)) + + +;; Invalid typing + +(assert_invalid + (module + (func $type-void-vs-num (result i32) (return_call 1) (i32.const 0)) + (func) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-num-vs-num (result i32) (return_call 1) (i32.const 0)) + (func (result i64) (i64.const 1)) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $arity-0-vs-1 (return_call 1)) + (func (param i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $arity-0-vs-2 (return_call 1)) + (func (param f64 i32)) + ) + "type mismatch" +) + +(module + (func $arity-1-vs-0 (i32.const 1) (return_call 1)) + (func) +) + +(module + (func $arity-2-vs-0 (f64.const 2) (i32.const 1) (return_call 1)) + (func) +) + +(assert_invalid + (module + (func $type-first-void-vs-num (return_call 1 (nop) (i32.const 1))) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-void-vs-num (return_call 1 (i32.const 1) (nop))) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-first-num-vs-num (return_call 1 (f64.const 1) (i32.const 1))) + (func (param i32 f64)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-num-vs-num (return_call 1 (i32.const 1) (f64.const 1))) + (func (param f64 i32)) + ) + "type mismatch" +) + + +;; Unbound function + +(assert_invalid + (module (func $unbound-func (return_call 1))) + "unknown function" +) +(assert_invalid + (module (func $large-func (return_call 1012321300))) + "unknown function" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/tail-call/return_call_indirect.wast b/runtime/unc-vm/tests/wast/spec/proposals/tail-call/return_call_indirect.wast new file mode 100644 index 000000000..acf0a72e0 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/tail-call/return_call_indirect.wast @@ -0,0 +1,536 @@ +;; Test `return_call_indirect` operator + +(module + ;; Auxiliary definitions + (type $proc (func)) + (type $out-i32 (func (result i32))) + (type $out-i64 (func (result i64))) + (type $out-f32 (func (result f32))) + (type $out-f64 (func (result f64))) + (type $over-i32 (func (param i32) (result i32))) + (type $over-i64 (func (param i64) (result i64))) + (type $over-f32 (func (param f32) (result f32))) + (type $over-f64 (func (param f64) (result f64))) + (type $f32-i32 (func (param f32 i32) (result i32))) + (type $i32-i64 (func (param i32 i64) (result i64))) + (type $f64-f32 (func (param f64 f32) (result f32))) + (type $i64-f64 (func (param i64 f64) (result f64))) + (type $over-i32-duplicate (func (param i32) (result i32))) + (type $over-i64-duplicate (func (param i64) (result i64))) + (type $over-f32-duplicate (func (param f32) (result f32))) + (type $over-f64-duplicate (func (param f64) (result f64))) + + (func $const-i32 (type $out-i32) (i32.const 0x132)) + (func $const-i64 (type $out-i64) (i64.const 0x164)) + (func $const-f32 (type $out-f32) (f32.const 0xf32)) + (func $const-f64 (type $out-f64) (f64.const 0xf64)) + + (func $id-i32 (type $over-i32) (local.get 0)) + (func $id-i64 (type $over-i64) (local.get 0)) + (func $id-f32 (type $over-f32) (local.get 0)) + (func $id-f64 (type $over-f64) (local.get 0)) + + (func $i32-i64 (type $i32-i64) (local.get 1)) + (func $i64-f64 (type $i64-f64) (local.get 1)) + (func $f32-i32 (type $f32-i32) (local.get 1)) + (func $f64-f32 (type $f64-f32) (local.get 1)) + + (func $over-i32-duplicate (type $over-i32-duplicate) (local.get 0)) + (func $over-i64-duplicate (type $over-i64-duplicate) (local.get 0)) + (func $over-f32-duplicate (type $over-f32-duplicate) (local.get 0)) + (func $over-f64-duplicate (type $over-f64-duplicate) (local.get 0)) + + (table funcref + (elem + $const-i32 $const-i64 $const-f32 $const-f64 + $id-i32 $id-i64 $id-f32 $id-f64 + $f32-i32 $i32-i64 $f64-f32 $i64-f64 + $fac $fac-acc $even $odd + $over-i32-duplicate $over-i64-duplicate + $over-f32-duplicate $over-f64-duplicate + ) + ) + + ;; Syntax + + (func + (return_call_indirect (i32.const 0)) + (return_call_indirect (param i64) (i64.const 0) (i32.const 0)) + (return_call_indirect (param i64) (param) (param f64 i32 i64) + (i64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (i32.const 0) + ) + (return_call_indirect (result) (i32.const 0)) + ) + + (func (result i32) + (return_call_indirect (result i32) (i32.const 0)) + (return_call_indirect (result i32) (result) (i32.const 0)) + (return_call_indirect (param i64) (result i32) (i64.const 0) (i32.const 0)) + (return_call_indirect + (param) (param i64) (param) (param f64 i32 i64) (param) (param) + (result) (result i32) (result) (result) + (i64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (i32.const 0) + ) + ) + + (func (result i64) + (return_call_indirect (type $over-i64) (param i64) (result i64) + (i64.const 0) (i32.const 0) + ) + ) + + ;; Typing + + (func (export "type-i32") (result i32) + (return_call_indirect (type $out-i32) (i32.const 0)) + ) + (func (export "type-i64") (result i64) + (return_call_indirect (type $out-i64) (i32.const 1)) + ) + (func (export "type-f32") (result f32) + (return_call_indirect (type $out-f32) (i32.const 2)) + ) + (func (export "type-f64") (result f64) + (return_call_indirect (type $out-f64) (i32.const 3)) + ) + + (func (export "type-index") (result i64) + (return_call_indirect (type $over-i64) (i64.const 100) (i32.const 5)) + ) + + (func (export "type-first-i32") (result i32) + (return_call_indirect (type $over-i32) (i32.const 32) (i32.const 4)) + ) + (func (export "type-first-i64") (result i64) + (return_call_indirect (type $over-i64) (i64.const 64) (i32.const 5)) + ) + (func (export "type-first-f32") (result f32) + (return_call_indirect (type $over-f32) (f32.const 1.32) (i32.const 6)) + ) + (func (export "type-first-f64") (result f64) + (return_call_indirect (type $over-f64) (f64.const 1.64) (i32.const 7)) + ) + + (func (export "type-second-i32") (result i32) + (return_call_indirect (type $f32-i32) + (f32.const 32.1) (i32.const 32) (i32.const 8) + ) + ) + (func (export "type-second-i64") (result i64) + (return_call_indirect (type $i32-i64) + (i32.const 32) (i64.const 64) (i32.const 9) + ) + ) + (func (export "type-second-f32") (result f32) + (return_call_indirect (type $f64-f32) + (f64.const 64) (f32.const 32) (i32.const 10) + ) + ) + (func (export "type-second-f64") (result f64) + (return_call_indirect (type $i64-f64) + (i64.const 64) (f64.const 64.1) (i32.const 11) + ) + ) + + ;; Dispatch + + (func (export "dispatch") (param i32 i64) (result i64) + (return_call_indirect (type $over-i64) (local.get 1) (local.get 0)) + ) + + (func (export "dispatch-structural") (param i32) (result i64) + (return_call_indirect (type $over-i64-duplicate) + (i64.const 9) (local.get 0) + ) + ) + + ;; Multiple tables + + (table $tab2 funcref (elem $tab-f1)) + (table $tab3 funcref (elem $tab-f2)) + + (func $tab-f1 (result i32) (i32.const 0x133)) + (func $tab-f2 (result i32) (i32.const 0x134)) + + (func (export "call-tab") (param $i i32) (result i32) + (if (i32.eq (local.get $i) (i32.const 0)) + (then (return_call_indirect (type $out-i32) (i32.const 0))) + ) + (if (i32.eq (local.get $i) (i32.const 1)) + (then (return_call_indirect 1 (type $out-i32) (i32.const 0))) + ) + (if (i32.eq (local.get $i) (i32.const 2)) + (then (return_call_indirect $tab3 (type $out-i32) (i32.const 0))) + ) + (i32.const 0) + ) + + ;; Recursion + + (func $fac (export "fac") (type $over-i64) + (return_call_indirect (param i64 i64) (result i64) + (local.get 0) (i64.const 1) (i32.const 13) + ) + ) + + (func $fac-acc (param i64 i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (local.get 1)) + (else + (return_call_indirect (param i64 i64) (result i64) + (i64.sub (local.get 0) (i64.const 1)) + (i64.mul (local.get 0) (local.get 1)) + (i32.const 13) + ) + ) + ) + ) + + (func $even (export "even") (param i32) (result i32) + (if (result i32) (i32.eqz (local.get 0)) + (then (i32.const 44)) + (else + (return_call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 15) + ) + ) + ) + ) + (func $odd (export "odd") (param i32) (result i32) + (if (result i32) (i32.eqz (local.get 0)) + (then (i32.const 99)) + (else + (return_call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 14) + ) + ) + ) + ) +) + +(assert_return (invoke "type-i32") (i32.const 0x132)) +(assert_return (invoke "type-i64") (i64.const 0x164)) +(assert_return (invoke "type-f32") (f32.const 0xf32)) +(assert_return (invoke "type-f64") (f64.const 0xf64)) + +(assert_return (invoke "type-index") (i64.const 100)) + +(assert_return (invoke "type-first-i32") (i32.const 32)) +(assert_return (invoke "type-first-i64") (i64.const 64)) +(assert_return (invoke "type-first-f32") (f32.const 1.32)) +(assert_return (invoke "type-first-f64") (f64.const 1.64)) + +(assert_return (invoke "type-second-i32") (i32.const 32)) +(assert_return (invoke "type-second-i64") (i64.const 64)) +(assert_return (invoke "type-second-f32") (f32.const 32)) +(assert_return (invoke "type-second-f64") (f64.const 64.1)) + +(assert_return (invoke "dispatch" (i32.const 5) (i64.const 2)) (i64.const 2)) +(assert_return (invoke "dispatch" (i32.const 5) (i64.const 5)) (i64.const 5)) +(assert_return (invoke "dispatch" (i32.const 12) (i64.const 5)) (i64.const 120)) +(assert_return (invoke "dispatch" (i32.const 17) (i64.const 2)) (i64.const 2)) +(assert_trap (invoke "dispatch" (i32.const 0) (i64.const 2)) "indirect call type mismatch") +(assert_trap (invoke "dispatch" (i32.const 15) (i64.const 2)) "indirect call type mismatch") +(assert_trap (invoke "dispatch" (i32.const 20) (i64.const 2)) "undefined element") +(assert_trap (invoke "dispatch" (i32.const -1) (i64.const 2)) "undefined element") +(assert_trap (invoke "dispatch" (i32.const 1213432423) (i64.const 2)) "undefined element") + +(assert_return (invoke "dispatch-structural" (i32.const 5)) (i64.const 9)) +(assert_return (invoke "dispatch-structural" (i32.const 5)) (i64.const 9)) +(assert_return (invoke "dispatch-structural" (i32.const 12)) (i64.const 362880)) +(assert_return (invoke "dispatch-structural" (i32.const 17)) (i64.const 9)) +(assert_trap (invoke "dispatch-structural" (i32.const 11)) "indirect call type mismatch") +(assert_trap (invoke "dispatch-structural" (i32.const 16)) "indirect call type mismatch") + +(assert_return (invoke "call-tab" (i32.const 0)) (i32.const 0x132)) +(assert_return (invoke "call-tab" (i32.const 1)) (i32.const 0x133)) +(assert_return (invoke "call-tab" (i32.const 2)) (i32.const 0x134)) + +(assert_return (invoke "fac" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "fac" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac" (i64.const 5)) (i64.const 120)) +(assert_return (invoke "fac" (i64.const 25)) (i64.const 7034535277573963776)) + +(assert_return (invoke "even" (i32.const 0)) (i32.const 44)) +(assert_return (invoke "even" (i32.const 1)) (i32.const 99)) +(assert_return (invoke "even" (i32.const 100)) (i32.const 44)) +(assert_return (invoke "even" (i32.const 77)) (i32.const 99)) +(assert_return (invoke "even" (i32.const 100_000)) (i32.const 44)) +(assert_return (invoke "even" (i32.const 111_111)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 0)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 1)) (i32.const 44)) +(assert_return (invoke "odd" (i32.const 200)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 77)) (i32.const 44)) +(assert_return (invoke "odd" (i32.const 200_002)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 300_003)) (i32.const 44)) + + +;; Invalid syntax + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (type $sig) (result i32) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (param i32) (type $sig) (result i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (param i32) (result i32) (type $sig)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (result i32) (type $sig) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (result i32) (param i32) (type $sig)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (result i32) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) + +(assert_malformed + (module quote + "(table 0 funcref)" + "(func (return_call_indirect (param $x i32) (i32.const 0) (i32.const 0)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (type $sig) (result i32) (i32.const 0))" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (type $sig) (result i32) (i32.const 0))" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func" + " (return_call_indirect (type $sig) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (type $sig) (param i32) (result i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "inline function type" +) + +;; Invalid typing + +(assert_invalid + (module + (type (func)) + (func $no-table (return_call_indirect (type 0) (i32.const 0))) + ) + "unknown table" +) + +(assert_invalid + (module + (type (func)) + (table 0 funcref) + (func $type-void-vs-num (i32.eqz (return_call_indirect (type 0) (i32.const 0)))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (result i64))) + (table 0 funcref) + (func $type-num-vs-num (i32.eqz (return_call_indirect (type 0) (i32.const 0)))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type (func (param i32))) + (table 0 funcref) + (func $arity-0-vs-1 (return_call_indirect (type 0) (i32.const 0))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param f64 i32))) + (table 0 funcref) + (func $arity-0-vs-2 (return_call_indirect (type 0) (i32.const 0))) + ) + "type mismatch" +) + +(module + (type (func)) + (table 0 funcref) + (func $arity-1-vs-0 (return_call_indirect (type 0) (i32.const 1) (i32.const 0))) +) + +(module + (type (func)) + (table 0 funcref) + (func $arity-2-vs-0 + (return_call_indirect (type 0) (f64.const 2) (i32.const 1) (i32.const 0)) + ) +) + +(assert_invalid + (module + (type (func (param i32))) + (table 0 funcref) + (func $type-func-void-vs-i32 (return_call_indirect (type 0) (i32.const 1) (nop))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param i32))) + (table 0 funcref) + (func $type-func-num-vs-i32 (return_call_indirect (type 0) (i32.const 0) (i64.const 1))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type (func (param i32 i32))) + (table 0 funcref) + (func $type-first-void-vs-num + (return_call_indirect (type 0) (nop) (i32.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param i32 i32))) + (table 0 funcref) + (func $type-second-void-vs-num + (return_call_indirect (type 0) (i32.const 1) (nop) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param i32 f64))) + (table 0 funcref) + (func $type-first-num-vs-num + (return_call_indirect (type 0) (f64.const 1) (i32.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param f64 i32))) + (table 0 funcref) + (func $type-second-num-vs-num + (return_call_indirect (type 0) (i32.const 1) (f64.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) + + +;; Unbound type + +(assert_invalid + (module + (table 0 funcref) + (func $unbound-type (return_call_indirect (type 1) (i32.const 0))) + ) + "unknown type" +) +(assert_invalid + (module + (table 0 funcref) + (func $large-type (return_call_indirect (type 1012321300) (i32.const 0))) + ) + "unknown type" +) + + +;; Unbound function in table + +(assert_invalid + (module (table funcref (elem 0 0))) + "unknown function 0" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/threads/atomic.wast b/runtime/unc-vm/tests/wast/spec/proposals/threads/atomic.wast new file mode 100644 index 000000000..9f3cd65db --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/threads/atomic.wast @@ -0,0 +1,539 @@ +;; atomic operations + +(module + (memory 1 1 shared) + + (func (export "init") (param $value i64) (i64.store (i32.const 0) (local.get $value))) + + (func (export "i32.atomic.load") (param $addr i32) (result i32) (i32.atomic.load (local.get $addr))) + (func (export "i64.atomic.load") (param $addr i32) (result i64) (i64.atomic.load (local.get $addr))) + (func (export "i32.atomic.load8_u") (param $addr i32) (result i32) (i32.atomic.load8_u (local.get $addr))) + (func (export "i32.atomic.load16_u") (param $addr i32) (result i32) (i32.atomic.load16_u (local.get $addr))) + (func (export "i64.atomic.load8_u") (param $addr i32) (result i64) (i64.atomic.load8_u (local.get $addr))) + (func (export "i64.atomic.load16_u") (param $addr i32) (result i64) (i64.atomic.load16_u (local.get $addr))) + (func (export "i64.atomic.load32_u") (param $addr i32) (result i64) (i64.atomic.load32_u (local.get $addr))) + + (func (export "i32.atomic.store") (param $addr i32) (param $value i32) (i32.atomic.store (local.get $addr) (local.get $value))) + (func (export "i64.atomic.store") (param $addr i32) (param $value i64) (i64.atomic.store (local.get $addr) (local.get $value))) + (func (export "i32.atomic.store8") (param $addr i32) (param $value i32) (i32.atomic.store8 (local.get $addr) (local.get $value))) + (func (export "i32.atomic.store16") (param $addr i32) (param $value i32) (i32.atomic.store16 (local.get $addr) (local.get $value))) + (func (export "i64.atomic.store8") (param $addr i32) (param $value i64) (i64.atomic.store8 (local.get $addr) (local.get $value))) + (func (export "i64.atomic.store16") (param $addr i32) (param $value i64) (i64.atomic.store16 (local.get $addr) (local.get $value))) + (func (export "i64.atomic.store32") (param $addr i32) (param $value i64) (i64.atomic.store32 (local.get $addr) (local.get $value))) + + (func (export "i32.atomic.rmw.add") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw.add (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw.add") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw.add (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw8.add_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw8.add_u (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw16.add_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw16.add_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw8.add_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw8.add_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw16.add_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw16.add_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw32.add_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw32.add_u (local.get $addr) (local.get $value))) + + (func (export "i32.atomic.rmw.sub") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw.sub (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw.sub") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw.sub (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw8.sub_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw8.sub_u (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw16.sub_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw16.sub_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw8.sub_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw8.sub_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw16.sub_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw16.sub_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw32.sub_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw32.sub_u (local.get $addr) (local.get $value))) + + (func (export "i32.atomic.rmw.and") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw.and (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw.and") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw.and (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw8.and_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw8.and_u (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw16.and_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw16.and_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw8.and_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw8.and_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw16.and_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw16.and_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw32.and_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw32.and_u (local.get $addr) (local.get $value))) + + (func (export "i32.atomic.rmw.or") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw.or (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw.or") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw.or (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw8.or_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw8.or_u (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw16.or_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw16.or_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw8.or_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw8.or_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw16.or_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw16.or_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw32.or_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw32.or_u (local.get $addr) (local.get $value))) + + (func (export "i32.atomic.rmw.xor") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw.xor (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw.xor") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw.xor (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw8.xor_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw8.xor_u (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw16.xor_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw16.xor_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw8.xor_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw8.xor_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw16.xor_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw16.xor_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw32.xor_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw32.xor_u (local.get $addr) (local.get $value))) + + (func (export "i32.atomic.rmw.xchg") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw.xchg (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw.xchg") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw.xchg (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw8.xchg_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw8.xchg_u (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw16.xchg_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw16.xchg_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw8.xchg_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw8.xchg_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw16.xchg_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw16.xchg_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw32.xchg_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw32.xchg_u (local.get $addr) (local.get $value))) + + (func (export "i32.atomic.rmw.cmpxchg") (param $addr i32) (param $expected i32) (param $value i32) (result i32) (i32.atomic.rmw.cmpxchg (local.get $addr) (local.get $expected) (local.get $value))) + (func (export "i64.atomic.rmw.cmpxchg") (param $addr i32) (param $expected i64) (param $value i64) (result i64) (i64.atomic.rmw.cmpxchg (local.get $addr) (local.get $expected) (local.get $value))) + (func (export "i32.atomic.rmw8.cmpxchg_u") (param $addr i32) (param $expected i32) (param $value i32) (result i32) (i32.atomic.rmw8.cmpxchg_u (local.get $addr) (local.get $expected) (local.get $value))) + (func (export "i32.atomic.rmw16.cmpxchg_u") (param $addr i32) (param $expected i32) (param $value i32) (result i32) (i32.atomic.rmw16.cmpxchg_u (local.get $addr) (local.get $expected) (local.get $value))) + (func (export "i64.atomic.rmw8.cmpxchg_u") (param $addr i32) (param $expected i64) (param $value i64) (result i64) (i64.atomic.rmw8.cmpxchg_u (local.get $addr) (local.get $expected) (local.get $value))) + (func (export "i64.atomic.rmw16.cmpxchg_u") (param $addr i32) (param $expected i64) (param $value i64) (result i64) (i64.atomic.rmw16.cmpxchg_u (local.get $addr) (local.get $expected) (local.get $value))) + (func (export "i64.atomic.rmw32.cmpxchg_u") (param $addr i32) (param $expected i64) (param $value i64) (result i64) (i64.atomic.rmw32.cmpxchg_u (local.get $addr) (local.get $expected) (local.get $value))) + +) + +;; *.atomic.load* + +(invoke "init" (i64.const 0x0706050403020100)) + +(assert_return (invoke "i32.atomic.load" (i32.const 0)) (i32.const 0x03020100)) +(assert_return (invoke "i32.atomic.load" (i32.const 4)) (i32.const 0x07060504)) + +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0706050403020100)) + +(assert_return (invoke "i32.atomic.load8_u" (i32.const 0)) (i32.const 0x00)) +(assert_return (invoke "i32.atomic.load8_u" (i32.const 5)) (i32.const 0x05)) + +(assert_return (invoke "i32.atomic.load16_u" (i32.const 0)) (i32.const 0x0100)) +(assert_return (invoke "i32.atomic.load16_u" (i32.const 6)) (i32.const 0x0706)) + +(assert_return (invoke "i64.atomic.load8_u" (i32.const 0)) (i64.const 0x00)) +(assert_return (invoke "i64.atomic.load8_u" (i32.const 5)) (i64.const 0x05)) + +(assert_return (invoke "i64.atomic.load16_u" (i32.const 0)) (i64.const 0x0100)) +(assert_return (invoke "i64.atomic.load16_u" (i32.const 6)) (i64.const 0x0706)) + +(assert_return (invoke "i64.atomic.load32_u" (i32.const 0)) (i64.const 0x03020100)) +(assert_return (invoke "i64.atomic.load32_u" (i32.const 4)) (i64.const 0x07060504)) + +;; *.atomic.store* + +(invoke "init" (i64.const 0x0000000000000000)) + +(assert_return (invoke "i32.atomic.store" (i32.const 0) (i32.const 0xffeeddcc))) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x00000000ffeeddcc)) + +(assert_return (invoke "i64.atomic.store" (i32.const 0) (i64.const 0x0123456789abcdef))) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0123456789abcdef)) + +(assert_return (invoke "i32.atomic.store8" (i32.const 1) (i32.const 0x42))) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0123456789ab42ef)) + +(assert_return (invoke "i32.atomic.store16" (i32.const 4) (i32.const 0x8844))) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0123884489ab42ef)) + +(assert_return (invoke "i64.atomic.store8" (i32.const 1) (i64.const 0x99))) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0123884489ab99ef)) + +(assert_return (invoke "i64.atomic.store16" (i32.const 4) (i64.const 0xcafe))) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0123cafe89ab99ef)) + +(assert_return (invoke "i64.atomic.store32" (i32.const 4) (i64.const 0xdeadbeef))) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0xdeadbeef89ab99ef)) + +;; *.atomic.rmw*.add + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.add" (i32.const 0) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111123456789)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.add" (i32.const 0) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1212121213131313)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.add_u" (i32.const 0) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111111111de)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.add_u" (i32.const 0) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111dc0f)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.add_u" (i32.const 0) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111153)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.add_u" (i32.const 0) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111d000)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.add_u" (i32.const 0) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111dbccb7f6)) + +;; *.atomic.rmw*.sub + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.sub" (i32.const 0) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111fedcba99)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.sub" (i32.const 0) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x101010100f0f0f0f)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.sub_u" (i32.const 0) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111144)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.sub_u" (i32.const 0) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111114613)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.sub_u" (i32.const 0) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111111111cf)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.sub_u" (i32.const 0) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111115222)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.sub_u" (i32.const 0) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111146556a2c)) + +;; *.atomic.rmw*.and + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.and" (i32.const 0) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111110101010)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.and" (i32.const 0) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0101010100000000)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.and_u" (i32.const 0) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111101)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.and_u" (i32.const 0) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111110010)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.and_u" (i32.const 0) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111100)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.and_u" (i32.const 0) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111001)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.and_u" (i32.const 0) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111100110001)) + +;; *.atomic.rmw*.or + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.or" (i32.const 0) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111113355779)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.or" (i32.const 0) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111113131313)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.or_u" (i32.const 0) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111111111dd)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.or_u" (i32.const 0) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111dbff)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.or_u" (i32.const 0) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111153)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.or_u" (i32.const 0) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111bfff)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.or_u" (i32.const 0) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111dbbbb7f5)) + +;; *.atomic.rmw*.xor + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.xor" (i32.const 0) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111103254769)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.xor" (i32.const 0) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1010101013131313)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.xor_u" (i32.const 0) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111111111dc)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.xor_u" (i32.const 0) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111dbef)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.xor_u" (i32.const 0) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111153)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.xor_u" (i32.const 0) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111affe)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.xor_u" (i32.const 0) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111dbaab7f4)) + +;; *.atomic.rmw*.xchg + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.xchg" (i32.const 0) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111112345678)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.xchg" (i32.const 0) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0101010102020202)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.xchg_u" (i32.const 0) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111111111cd)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.xchg_u" (i32.const 0) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111cafe)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.xchg_u" (i32.const 0) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111142)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.xchg_u" (i32.const 0) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111beef)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.xchg_u" (i32.const 0) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111cabba6e5)) + +;; *.atomic.rmw*.cmpxchg (compare false) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.cmpxchg" (i32.const 0) (i32.const 0) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.cmpxchg" (i32.const 0) (i64.const 0) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.cmpxchg_u" (i32.const 0) (i32.const 0) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.cmpxchg_u" (i32.const 0) (i32.const 0) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.cmpxchg_u" (i32.const 0) (i64.const 0) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.cmpxchg_u" (i32.const 0) (i64.const 0) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.cmpxchg_u" (i32.const 0) (i64.const 0) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) + +;; *.atomic.rmw*.cmpxchg (compare true) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.cmpxchg" (i32.const 0) (i32.const 0x11111111) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111112345678)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.cmpxchg" (i32.const 0) (i64.const 0x1111111111111111) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0101010102020202)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.cmpxchg_u" (i32.const 0) (i32.const 0x11) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111111111cd)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.cmpxchg_u" (i32.const 0) (i32.const 0x1111) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111cafe)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.cmpxchg_u" (i32.const 0) (i64.const 0x11) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111142)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.cmpxchg_u" (i32.const 0) (i64.const 0x1111) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111beef)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.cmpxchg_u" (i32.const 0) (i64.const 0x11111111) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111cabba6e5)) + + +;; unaligned accesses + +(assert_trap (invoke "i32.atomic.load" (i32.const 1)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.load" (i32.const 1)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.load16_u" (i32.const 1)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.load16_u" (i32.const 1)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.load32_u" (i32.const 1)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.store" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.store" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.store16" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.store16" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.store32" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw.add" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw.add" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw16.add_u" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw16.add_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw32.add_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw.sub" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw.sub" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw16.sub_u" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw16.sub_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw32.sub_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw.and" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw.and" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw16.and_u" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw16.and_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw32.and_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw.or" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw.or" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw16.or_u" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw16.or_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw32.or_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw.xor" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw.xor" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw16.xor_u" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw16.xor_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw32.xor_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw.xchg" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw.xchg" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw16.xchg_u" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw16.xchg_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw32.xchg_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw.cmpxchg" (i32.const 1) (i32.const 0) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw.cmpxchg" (i32.const 1) (i64.const 0) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw16.cmpxchg_u" (i32.const 1) (i32.const 0) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw16.cmpxchg_u" (i32.const 1) (i64.const 0) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw32.cmpxchg_u" (i32.const 1) (i64.const 0) (i64.const 0)) "unaligned atomic") + +(module + (memory 1 1 shared) + + (func (export "init") (param $value i64) (i64.store (i32.const 0) (local.get $value))) + + (func (export "memory.atomic.notify") (param $addr i32) (param $count i32) (result i32) + (memory.atomic.notify (local.get 0) (local.get 1))) + (func (export "memory.atomic.wait32") (param $addr i32) (param $expected i32) (param $timeout i64) (result i32) + (memory.atomic.wait32 (local.get 0) (local.get 1) (local.get 2))) + (func (export "memory.atomic.wait64") (param $addr i32) (param $expected i64) (param $timeout i64) (result i32) + (memory.atomic.wait64 (local.get 0) (local.get 1) (local.get 2))) +) + +(invoke "init" (i64.const 0xffffffffffff)) +(assert_return (invoke "memory.atomic.wait32" (i32.const 0) (i32.const 0) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "memory.atomic.wait64" (i32.const 0) (i64.const 0) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "memory.atomic.notify" (i32.const 0) (i32.const 0)) (i32.const 0)) + +;; unshared memory is OK +(module + (memory 1 1) + (func (drop (memory.atomic.notify (i32.const 0) (i32.const 0)))) + (func (drop (memory.atomic.wait32 (i32.const 0) (i32.const 0) (i64.const 0)))) + (func (drop (memory.atomic.wait64 (i32.const 0) (i64.const 0) (i64.const 0)))) + (func (drop (i32.atomic.load (i32.const 0)))) + (func (drop (i64.atomic.load (i32.const 0)))) + (func (drop (i32.atomic.load16_u (i32.const 0)))) + (func (drop (i64.atomic.load16_u (i32.const 0)))) + (func (drop (i64.atomic.load32_u (i32.const 0)))) + (func (i32.atomic.store (i32.const 0) (i32.const 0))) + (func (i64.atomic.store (i32.const 0) (i64.const 0))) + (func (i32.atomic.store16 (i32.const 0) (i32.const 0))) + (func (i64.atomic.store16 (i32.const 0) (i64.const 0))) + (func (i64.atomic.store32 (i32.const 0) (i64.const 0))) + (func (drop (i32.atomic.rmw.add (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw.add (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw16.add_u (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw16.add_u (i32.const 0) (i64.const 0)))) + (func (drop (i64.atomic.rmw32.add_u (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw.sub (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw.sub (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw16.sub_u (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw16.sub_u (i32.const 0) (i64.const 0)))) + (func (drop (i64.atomic.rmw32.sub_u (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw.and (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw.and (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw16.and_u (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw16.and_u (i32.const 0) (i64.const 0)))) + (func (drop (i64.atomic.rmw32.and_u (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw.or (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw.or (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw16.or_u (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw16.or_u (i32.const 0) (i64.const 0)))) + (func (drop (i64.atomic.rmw32.or_u (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw.xor (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw.xor (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw16.xor_u (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw16.xor_u (i32.const 0) (i64.const 0)))) + (func (drop (i64.atomic.rmw32.xor_u (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw.xchg (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw.xchg (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw16.xchg_u (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw16.xchg_u (i32.const 0) (i64.const 0)))) + (func (drop (i64.atomic.rmw32.xchg_u (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw.cmpxchg (i32.const 0) (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw.cmpxchg (i32.const 0) (i64.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw16.cmpxchg_u (i32.const 0) (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw16.cmpxchg_u (i32.const 0) (i64.const 0) (i64.const 0)))) + (func (drop (i64.atomic.rmw32.cmpxchg_u (i32.const 0) (i64.const 0) (i64.const 0)))) +) + +;; Fails with no memory +(assert_invalid (module (func (drop (memory.atomic.notify (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (memory.atomic.wait32 (i32.const 0) (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (memory.atomic.wait64 (i32.const 0) (i64.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.load (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.load (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.load16_u (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.load16_u (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.load32_u (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (i32.atomic.store (i32.const 0) (i32.const 0)))) "unknown memory") +(assert_invalid (module (func (i64.atomic.store (i32.const 0) (i64.const 0)))) "unknown memory") +(assert_invalid (module (func (i32.atomic.store16 (i32.const 0) (i32.const 0)))) "unknown memory") +(assert_invalid (module (func (i64.atomic.store16 (i32.const 0) (i64.const 0)))) "unknown memory") +(assert_invalid (module (func (i64.atomic.store32 (i32.const 0) (i64.const 0)))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw.add (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw.add (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw16.add_u (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw16.add_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw32.add_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw.sub (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw.sub (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw16.sub_u (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw16.sub_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw32.sub_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw.and (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw.and (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw16.and_u (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw16.and_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw32.and_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw.or (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw.or (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw16.or_u (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw16.or_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw32.or_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw.xor (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw.xor (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw16.xor_u (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw16.xor_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw32.xor_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw.xchg (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw.xchg (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw16.xchg_u (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw16.xchg_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw32.xchg_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw.cmpxchg (i32.const 0) (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw.cmpxchg (i32.const 0) (i64.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw16.cmpxchg_u (i32.const 0) (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw16.cmpxchg_u (i32.const 0) (i64.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw32.cmpxchg_u (i32.const 0) (i64.const 0) (i64.const 0))))) "unknown memory") diff --git a/runtime/unc-vm/tests/wast/spec/proposals/threads/exports.wast b/runtime/unc-vm/tests/wast/spec/proposals/threads/exports.wast new file mode 100644 index 000000000..4a34734b0 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/threads/exports.wast @@ -0,0 +1,205 @@ +;; Functions + +(module (func) (export "a" (func 0))) +(module (func) (export "a" (func 0)) (export "b" (func 0))) +(module (func) (func) (export "a" (func 0)) (export "b" (func 1))) + +(module (func (export "a"))) +(module (func (export "a") (export "b") (export "c"))) +(module (func (export "a") (export "b") (param i32))) +(module (func) (export "a" (func 0))) +(module (func $a (export "a"))) +(module (func $a) (export "a" (func $a))) +(module (export "a" (func 0)) (func)) +(module (export "a" (func $a)) (func $a)) + +(module $Func + (export "e" (func $f)) + (func $f (param $n i32) (result i32) + (return (i32.add (local.get $n) (i32.const 1))) + ) +) +(assert_return (invoke "e" (i32.const 42)) (i32.const 43)) +(assert_return (invoke $Func "e" (i32.const 42)) (i32.const 43)) +(module) +(module $Other1) +(assert_return (invoke $Func "e" (i32.const 42)) (i32.const 43)) + +(assert_invalid + (module (func) (export "a" (func 1))) + "unknown function" +) +(assert_invalid + (module (func) (export "a" (func 0)) (export "a" (func 0))) + "duplicate export name" +) +(assert_invalid + (module (func) (func) (export "a" (func 0)) (export "a" (func 1))) + "duplicate export name" +) +(assert_invalid + (module (func) (global i32 (i32.const 0)) (export "a" (func 0)) (export "a" (global 0))) + "duplicate export name" +) +(assert_invalid + (module (func) (table 0 funcref) (export "a" (func 0)) (export "a" (table 0))) + "duplicate export name" +) +(assert_invalid + (module (func) (memory 0) (export "a" (func 0)) (export "a" (memory 0))) + "duplicate export name" +) + + +;; Globals + +(module (global i32 (i32.const 0)) (export "a" (global 0))) +(module (global i32 (i32.const 0)) (export "a" (global 0)) (export "b" (global 0))) +(module (global i32 (i32.const 0)) (global i32 (i32.const 0)) (export "a" (global 0)) (export "b" (global 1))) + +(module (global (export "a") i32 (i32.const 0))) +(module (global i32 (i32.const 0)) (export "a" (global 0))) +(module (global $a (export "a") i32 (i32.const 0))) +(module (global $a i32 (i32.const 0)) (export "a" (global $a))) +(module (export "a" (global 0)) (global i32 (i32.const 0))) +(module (export "a" (global $a)) (global $a i32 (i32.const 0))) + +(module $Global + (export "e" (global $g)) + (global $g i32 (i32.const 42)) +) +(assert_return (get "e") (i32.const 42)) +(assert_return (get $Global "e") (i32.const 42)) +(module) +(module $Other2) +(assert_return (get $Global "e") (i32.const 42)) + +(assert_invalid + (module (global i32 (i32.const 0)) (export "a" (global 1))) + "unknown global" +) +(assert_invalid + (module (global i32 (i32.const 0)) (export "a" (global 0)) (export "a" (global 0))) + "duplicate export name" +) +(assert_invalid + (module (global i32 (i32.const 0)) (global i32 (i32.const 0)) (export "a" (global 0)) (export "a" (global 1))) + "duplicate export name" +) +(assert_invalid + (module (global i32 (i32.const 0)) (func) (export "a" (global 0)) (export "a" (func 0))) + "duplicate export name" +) +(assert_invalid + (module (global i32 (i32.const 0)) (table 0 funcref) (export "a" (global 0)) (export "a" (table 0))) + "duplicate export name" +) +(assert_invalid + (module (global i32 (i32.const 0)) (memory 0) (export "a" (global 0)) (export "a" (memory 0))) + "duplicate export name" +) + + +;; Tables + +(module (table 0 funcref) (export "a" (table 0))) +(module (table 0 funcref) (export "a" (table 0)) (export "b" (table 0))) +;; No multiple tables yet. +;; (module (table 0 funcref) (table 0 funcref) (export "a" (table 0)) (export "b" (table 1))) + +(module (table (export "a") 0 funcref)) +(module (table (export "a") 0 1 funcref)) +(module (table 0 funcref) (export "a" (table 0))) +(module (table 0 1 funcref) (export "a" (table 0))) +(module (table $a (export "a") 0 funcref)) +(module (table $a (export "a") 0 1 funcref)) +(module (table $a 0 funcref) (export "a" (table $a))) +(module (table $a 0 1 funcref) (export "a" (table $a))) +(module (export "a" (table 0)) (table 0 funcref)) +(module (export "a" (table 0)) (table 0 1 funcref)) +(module (export "a" (table $a)) (table $a 0 funcref)) +(module (export "a" (table $a)) (table $a 0 1 funcref)) + +(; TODO: access table ;) + +(assert_invalid + (module (table 0 funcref) (export "a" (table 1))) + "unknown table" +) +(assert_invalid + (module (table 0 funcref) (export "a" (table 0)) (export "a" (table 0))) + "duplicate export name" +) +;; No multiple tables yet. +;; (assert_invalid +;; (module (table 0 funcref) (table 0 funcref) (export "a" (table 0)) (export "a" (table 1))) +;; "duplicate export name" +;; ) +(assert_invalid + (module (table 0 funcref) (func) (export "a" (table 0)) (export "a" (func 0))) + "duplicate export name" +) +(assert_invalid + (module (table 0 funcref) (global i32 (i32.const 0)) (export "a" (table 0)) (export "a" (global 0))) + "duplicate export name" +) +(assert_invalid + (module (table 0 funcref) (memory 0) (export "a" (table 0)) (export "a" (memory 0))) + "duplicate export name" +) + + +;; Memories + +(module (memory 0) (export "a" (memory 0))) +(module (memory 0) (export "a" (memory 0)) (export "b" (memory 0))) +;; No multiple memories yet. +;; (module (memory 0) (memory 0) (export "a" (memory 0)) (export "b" (memory 1))) + +(module (memory (export "a") 0)) +(module (memory (export "a") 0 1)) +(module (memory 0) (export "a" (memory 0))) +(module (memory 0 1) (export "a" (memory 0))) +(module (memory $a (export "a") 0)) +(module (memory $a (export "a") 0 1)) +(module (memory $a 0) (export "a" (memory $a))) +(module (memory $a 0 1) (export "a" (memory $a))) +(module (export "a" (memory 0)) (memory 0)) +(module (export "a" (memory 0)) (memory 0 1)) +(module (export "a" (memory $a)) (memory $a 0)) +(module (export "a" (memory $a)) (memory $a 0 1)) + +(module (memory (export "a") 0 1 shared)) +(module (memory 0 1 shared) (export "a" (memory 0))) +(module (memory $a (export "a") 0 1 shared)) +(module (memory $a 0 1 shared) (export "a" (memory $a))) +(module (export "a" (memory 0)) (memory 0 1 shared)) +(module (export "a" (memory $a)) (memory $a 0 1 shared)) + +(; TODO: access memory ;) + +(assert_invalid + (module (memory 0) (export "a" (memory 1))) + "unknown memory" +) +(assert_invalid + (module (memory 0) (export "a" (memory 0)) (export "a" (memory 0))) + "duplicate export name" +) +;; No multiple memories yet. +;; (assert_invalid +;; (module (memory 0) (memory 0) (export "a" (memory 0)) (export "a" (memory 1))) +;; "duplicate export name" +;; ) +(assert_invalid + (module (memory 0) (func) (export "a" (memory 0)) (export "a" (func 0))) + "duplicate export name" +) +(assert_invalid + (module (memory 0) (global i32 (i32.const 0)) (export "a" (memory 0)) (export "a" (global 0))) + "duplicate export name" +) +(assert_invalid + (module (memory 0) (table 0 funcref) (export "a" (memory 0)) (export "a" (table 0))) + "duplicate export name" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/threads/imports.wast b/runtime/unc-vm/tests/wast/spec/proposals/threads/imports.wast new file mode 100644 index 000000000..51dfbceaa --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/threads/imports.wast @@ -0,0 +1,605 @@ +;; Auxiliary module to import from + +(module + (func (export "func")) + (func (export "func-i32") (param i32)) + (func (export "func-f32") (param f32)) + (func (export "func->i32") (result i32) (i32.const 22)) + (func (export "func->f32") (result f32) (f32.const 11)) + (func (export "func-i32->i32") (param i32) (result i32) (local.get 0)) + (func (export "func-i64->i64") (param i64) (result i64) (local.get 0)) + (global (export "global-i32") i32 (i32.const 55)) + (global (export "global-f32") f32 (f32.const 44)) + (table (export "table-10-inf") 10 funcref) + ;; (table (export "table-10-20") 10 20 funcref) + (memory (export "memory-2-inf") 2) + ;; (memory (export "memory-2-4") 2 4) +) + +(register "test") + + +;; Functions + +(module + (type $func_i32 (func (param i32))) + (type $func_i64 (func (param i64))) + (type $func_f32 (func (param f32))) + (type $func_f64 (func (param f64))) + + (import "spectest" "print_i32" (func (param i32))) + ;; JavaScript can't handle i64 yet. + ;; (func (import "spectest" "print_i64") (param i64)) + (import "spectest" "print_i32" (func $print_i32 (param i32))) + ;; JavaScript can't handle i64 yet. + ;; (import "spectest" "print_i64" (func $print_i64 (param i64))) + (import "spectest" "print_f32" (func $print_f32 (param f32))) + (import "spectest" "print_f64" (func $print_f64 (param f64))) + (import "spectest" "print_i32_f32" (func $print_i32_f32 (param i32 f32))) + (import "spectest" "print_f64_f64" (func $print_f64_f64 (param f64 f64))) + (func $print_i32-2 (import "spectest" "print_i32") (param i32)) + (func $print_f64-2 (import "spectest" "print_f64") (param f64)) + (import "test" "func-i64->i64" (func $i64->i64 (param i64) (result i64))) + + (func (export "p1") (import "spectest" "print_i32") (param i32)) + (func $p (export "p2") (import "spectest" "print_i32") (param i32)) + (func (export "p3") (export "p4") (import "spectest" "print_i32") (param i32)) + (func (export "p5") (import "spectest" "print_i32") (type 0)) + (func (export "p6") (import "spectest" "print_i32") (type 0) (param i32) (result)) + + (import "spectest" "print_i32" (func (type $forward))) + (func (import "spectest" "print_i32") (type $forward)) + (type $forward (func (param i32))) + + (table funcref (elem $print_i32 $print_f64)) + + (func (export "print32") (param $i i32) + (local $x f32) + (local.set $x (f32.convert_i32_s (local.get $i))) + (call 0 (local.get $i)) + (call $print_i32_f32 + (i32.add (local.get $i) (i32.const 1)) + (f32.const 42) + ) + (call $print_i32 (local.get $i)) + (call $print_i32-2 (local.get $i)) + (call $print_f32 (local.get $x)) + (call_indirect (type $func_i32) (local.get $i) (i32.const 0)) + ) + + (func (export "print64") (param $i i64) + (local $x f64) + (local.set $x (f64.convert_i64_s (call $i64->i64 (local.get $i)))) + ;; JavaScript can't handle i64 yet. + ;; (call 1 (local.get $i)) + (call $print_f64_f64 + (f64.add (local.get $x) (f64.const 1)) + (f64.const 53) + ) + ;; JavaScript can't handle i64 yet. + ;; (call $print_i64 (local.get $i)) + (call $print_f64 (local.get $x)) + (call $print_f64-2 (local.get $x)) + (call_indirect (type $func_f64) (local.get $x) (i32.const 1)) + ) +) + +(assert_return (invoke "print32" (i32.const 13))) +(assert_return (invoke "print64" (i64.const 24))) + +(assert_invalid + (module + (type (func (result i32))) + (import "test" "func" (func (type 1))) + ) + "unknown type" +) + +(module (import "test" "func" (func))) +(module (import "test" "func-i32" (func (param i32)))) +(module (import "test" "func-f32" (func (param f32)))) +(module (import "test" "func->i32" (func (result i32)))) +(module (import "test" "func->f32" (func (result f32)))) +(module (import "test" "func-i32->i32" (func (param i32) (result i32)))) +(module (import "test" "func-i64->i64" (func (param i64) (result i64)))) + +(assert_unlinkable + (module (import "test" "unknown" (func))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (func))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "func" (func (param i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func" (func (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func" (func (param i32) (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (param f32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (param i64)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32" (func (param i32) (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (param i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (result f32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (result i64)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func->i32" (func (param i32) (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32->i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32->i32" (func (param i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "func-i32->i32" (func (result i32)))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "test" "global-i32" (func (result i32)))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "global_i32" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (func))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (func))) + "incompatible import type" +) + + +;; Globals + +(module + (import "spectest" "global_i32" (global i32)) + (global (import "spectest" "global_i32") i32) + + (import "spectest" "global_i32" (global $x i32)) + (global $y (import "spectest" "global_i32") i32) + + ;; JavaScript can't handle i64 yet. + ;; (import "spectest" "global_i64" (global i64)) + (import "spectest" "global_f32" (global f32)) + (import "spectest" "global_f64" (global f64)) + + (func (export "get-0") (result i32) (global.get 0)) + (func (export "get-1") (result i32) (global.get 1)) + (func (export "get-x") (result i32) (global.get $x)) + (func (export "get-y") (result i32) (global.get $y)) +) + +(assert_return (invoke "get-0") (i32.const 666)) +(assert_return (invoke "get-1") (i32.const 666)) +(assert_return (invoke "get-x") (i32.const 666)) +(assert_return (invoke "get-y") (i32.const 666)) + +(module (import "test" "global-i32" (global i32))) +(module (import "test" "global-f32" (global f32))) + +(assert_unlinkable + (module (import "test" "unknown" (global i32))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (global i32))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "func" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "print_i32" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (global i32))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (global i32))) + "incompatible import type" +) + + +;; Tables + +(module + (type (func (result i32))) + (import "spectest" "table" (table 10 20 funcref)) + (elem 0 (i32.const 1) $f $g) + + (func (export "call") (param i32) (result i32) + (call_indirect (type 0) (local.get 0)) + ) + (func $f (result i32) (i32.const 11)) + (func $g (result i32) (i32.const 22)) +) + +(assert_trap (invoke "call" (i32.const 0)) "uninitialized element") +(assert_return (invoke "call" (i32.const 1)) (i32.const 11)) +(assert_return (invoke "call" (i32.const 2)) (i32.const 22)) +(assert_trap (invoke "call" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "call" (i32.const 100)) "undefined element") + + +(module + (type (func (result i32))) + (table (import "spectest" "table") 10 20 funcref) + (elem 0 (i32.const 1) $f $g) + + (func (export "call") (param i32) (result i32) + (call_indirect (type 0) (local.get 0)) + ) + (func $f (result i32) (i32.const 11)) + (func $g (result i32) (i32.const 22)) +) + +(assert_trap (invoke "call" (i32.const 0)) "uninitialized element") +(assert_return (invoke "call" (i32.const 1)) (i32.const 11)) +(assert_return (invoke "call" (i32.const 2)) (i32.const 22)) +(assert_trap (invoke "call" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "call" (i32.const 100)) "undefined element") + + +(assert_invalid + (module (import "" "" (table 10 funcref)) (import "" "" (table 10 funcref))) + "multiple tables" +) +(assert_invalid + (module (import "" "" (table 10 funcref)) (table 10 funcref)) + "multiple tables" +) +(assert_invalid + (module (table 10 funcref) (table 10 funcref)) + "multiple tables" +) + +(module (import "test" "table-10-inf" (table 10 funcref))) +(module (import "test" "table-10-inf" (table 5 funcref))) +(module (import "test" "table-10-inf" (table 0 funcref))) +(module (import "spectest" "table" (table 10 funcref))) +(module (import "spectest" "table" (table 5 funcref))) +(module (import "spectest" "table" (table 0 funcref))) +(module (import "spectest" "table" (table 10 20 funcref))) +(module (import "spectest" "table" (table 5 20 funcref))) +(module (import "spectest" "table" (table 0 20 funcref))) +(module (import "spectest" "table" (table 10 25 funcref))) +(module (import "spectest" "table" (table 5 25 funcref))) + +(assert_unlinkable + (module (import "test" "unknown" (table 10 funcref))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (table 10 funcref))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "table-10-inf" (table 12 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (table 10 20 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (table 12 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (table 10 15 funcref))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "test" "func" (table 10 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-i32" (table 10 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (table 10 funcref))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "print_i32" (table 10 funcref))) + "incompatible import type" +) + + + +;; Memories + +(module + (import "spectest" "memory" (memory 1 2)) + (data 0 (i32.const 10) "\10") + + (func (export "load") (param i32) (result i32) (i32.load (local.get 0))) +) + +(assert_return (invoke "load" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load" (i32.const 10)) (i32.const 16)) +(assert_return (invoke "load" (i32.const 8)) (i32.const 0x100000)) +(assert_trap (invoke "load" (i32.const 1000000)) "out of bounds memory access") + +(module + (memory (import "spectest" "memory") 1 2) + (data 0 (i32.const 10) "\10") + + (func (export "load") (param i32) (result i32) (i32.load (local.get 0))) +) +(assert_return (invoke "load" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "load" (i32.const 10)) (i32.const 16)) +(assert_return (invoke "load" (i32.const 8)) (i32.const 0x100000)) +(assert_trap (invoke "load" (i32.const 1000000)) "out of bounds memory access") + +(assert_invalid + (module (import "" "" (memory 1)) (import "" "" (memory 1))) + "multiple memories" +) +(assert_invalid + (module (import "" "" (memory 1)) (memory 0)) + "multiple memories" +) +(assert_invalid + (module (memory 0) (memory 0)) + "multiple memories" +) + +(module (import "test" "memory-2-inf" (memory 2))) +(module (import "test" "memory-2-inf" (memory 1))) +(module (import "test" "memory-2-inf" (memory 0))) +(module (import "spectest" "memory" (memory 1))) +(module (import "spectest" "memory" (memory 0))) +(module (import "spectest" "memory" (memory 1 2))) +(module (import "spectest" "memory" (memory 0 2))) +(module (import "spectest" "memory" (memory 1 3))) +(module (import "spectest" "memory" (memory 0 3))) + +(assert_unlinkable + (module (import "test" "unknown" (memory 1))) + "unknown import" +) +(assert_unlinkable + (module (import "spectest" "unknown" (memory 1))) + "unknown import" +) + +(assert_unlinkable + (module (import "test" "memory-2-inf" (memory 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "memory-2-inf" (memory 2 3))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (memory 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (memory 1 1))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "test" "func-i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "global-i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "test" "table-10-inf" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "print_i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "global_i32" (memory 1))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "table" (memory 1))) + "incompatible import type" +) + +(assert_unlinkable + (module (import "spectest" "memory" (memory 2))) + "incompatible import type" +) +(assert_unlinkable + (module (import "spectest" "memory" (memory 1 1))) + "incompatible import type" +) + +(module + (import "spectest" "memory" (memory 0 3)) ;; actual has max size 2 + (func (export "grow") (param i32) (result i32) (memory.grow (local.get 0))) +) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 2)) + +;; Shared Memory + +(module (import "spectest" "shared_memory" (memory 1 2 shared))) + +(assert_unlinkable + (module (import "spectest" "shared_memory" (memory 1 2))) + "incompatible import type") + +(assert_unlinkable + (module (import "spectest" "memory" (memory 1 2 shared))) + "incompatible import type") + + +;; Syntax errors + +(assert_malformed + (module quote "(func) (import \"\" \"\" (func))") + "import after function" +) +(assert_malformed + (module quote "(func) (import \"\" \"\" (global i64))") + "import after function" +) +(assert_malformed + (module quote "(func) (import \"\" \"\" (table 0 funcref))") + "import after function" +) +(assert_malformed + (module quote "(func) (import \"\" \"\" (memory 0))") + "import after function" +) + +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (func))") + "import after global" +) +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (global f32))") + "import after global" +) +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (table 0 funcref))") + "import after global" +) +(assert_malformed + (module quote "(global i64 (i64.const 0)) (import \"\" \"\" (memory 0))") + "import after global" +) + +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (func))") + "import after table" +) +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (global i32))") + "import after table" +) +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (table 0 funcref))") + "import after table" +) +(assert_malformed + (module quote "(table 0 funcref) (import \"\" \"\" (memory 0))") + "import after table" +) + +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (func))") + "import after memory" +) +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (global i32))") + "import after memory" +) +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (table 1 3 funcref))") + "import after memory" +) +(assert_malformed + (module quote "(memory 0) (import \"\" \"\" (memory 1 2))") + "import after memory" +) + +;; This module is required to validate, regardless of whether it can be +;; linked. Overloading is not possible in wasm itself, but it is possible +;; in modules from which wasm can import. +(module) +(register "not wasm") +(assert_unlinkable + (module + (import "not wasm" "overloaded" (func)) + (import "not wasm" "overloaded" (func (param i32))) + (import "not wasm" "overloaded" (func (param i32 i32))) + (import "not wasm" "overloaded" (func (param i64))) + (import "not wasm" "overloaded" (func (param f32))) + (import "not wasm" "overloaded" (func (param f64))) + (import "not wasm" "overloaded" (func (result i32))) + (import "not wasm" "overloaded" (func (result i64))) + (import "not wasm" "overloaded" (func (result f32))) + (import "not wasm" "overloaded" (func (result f64))) + (import "not wasm" "overloaded" (global i32)) + (import "not wasm" "overloaded" (global i64)) + (import "not wasm" "overloaded" (global f32)) + (import "not wasm" "overloaded" (global f64)) + (import "not wasm" "overloaded" (table 0 funcref)) + (import "not wasm" "overloaded" (memory 0)) + ) + "unknown import" +) diff --git a/runtime/unc-vm/tests/wast/spec/proposals/threads/memory.wast b/runtime/unc-vm/tests/wast/spec/proposals/threads/memory.wast new file mode 100644 index 000000000..e10c3497a --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/proposals/threads/memory.wast @@ -0,0 +1,246 @@ +;; Test memory section structure + +(module (memory 0)) +(module (memory 1)) +(module (memory 0 0)) +(module (memory 0 1)) +(module (memory 1 256)) +(module (memory 0 65536)) +(module (memory 0 0 shared)) +(module (memory 1 2 shared)) + +(assert_invalid (module (memory 1 shared)) "shared memory must have maximum") + +(assert_invalid (module (memory 0) (memory 0)) "multiple memories") +(assert_invalid (module (memory (import "spectest" "memory") 0) (memory 0)) "multiple memories") + +(module (memory (data)) (func (export "memsize") (result i32) (memory.size))) +(assert_return (invoke "memsize") (i32.const 0)) +(module (memory (data "")) (func (export "memsize") (result i32) (memory.size))) +(assert_return (invoke "memsize") (i32.const 0)) +(module (memory (data "x")) (func (export "memsize") (result i32) (memory.size))) +(assert_return (invoke "memsize") (i32.const 1)) + +(assert_invalid (module (data (i32.const 0))) "unknown memory") +(assert_invalid (module (data (i32.const 0) "")) "unknown memory") +(assert_invalid (module (data (i32.const 0) "x")) "unknown memory") + +(assert_invalid + (module (func (drop (f32.load (i32.const 0))))) + "unknown memory" +) +(assert_invalid + (module (func (f32.store (i32.const 0) (f32.const 0)))) + "unknown memory" +) +(assert_invalid + (module (func (drop (i32.load8_s (i32.const 0))))) + "unknown memory" +) +(assert_invalid + (module (func (i32.store8 (i32.const 0) (i32.const 0)))) + "unknown memory" +) +(assert_invalid + (module (func (drop (memory.size)))) + "unknown memory" +) +(assert_invalid + (module (func (drop (memory.grow (i32.const 0))))) + "unknown memory" +) + + +(assert_invalid + (module (memory 1 0)) + "size minimum must not be greater than maximum" +) +(assert_invalid + (module (memory 65537)) + "memory size must be at most 65536 pages (4GiB)" +) +(assert_invalid + (module (memory 2147483648)) + "memory size must be at most 65536 pages (4GiB)" +) +(assert_invalid + (module (memory 4294967295)) + "memory size must be at most 65536 pages (4GiB)" +) +(assert_invalid + (module (memory 0 65537)) + "memory size must be at most 65536 pages (4GiB)" +) +(assert_invalid + (module (memory 0 2147483648)) + "memory size must be at most 65536 pages (4GiB)" +) +(assert_invalid + (module (memory 0 4294967295)) + "memory size must be at most 65536 pages (4GiB)" +) + +(assert_malformed + (module quote "(memory 0x1_0000_0000)") + "i32 constant out of range" +) +(assert_malformed + (module quote "(memory 0x1_0000_0000 0x1_0000_0000)") + "i32 constant out of range" +) +(assert_malformed + (module quote "(memory 0 0x1_0000_0000)") + "i32 constant out of range" +) + +(module + (memory 1) + (data (i32.const 0) "ABC\a7D") (data (i32.const 20) "WASM") + + ;; Data section + (func (export "data") (result i32) + (i32.and + (i32.and + (i32.and + (i32.eq (i32.load8_u (i32.const 0)) (i32.const 65)) + (i32.eq (i32.load8_u (i32.const 3)) (i32.const 167)) + ) + (i32.and + (i32.eq (i32.load8_u (i32.const 6)) (i32.const 0)) + (i32.eq (i32.load8_u (i32.const 19)) (i32.const 0)) + ) + ) + (i32.and + (i32.and + (i32.eq (i32.load8_u (i32.const 20)) (i32.const 87)) + (i32.eq (i32.load8_u (i32.const 23)) (i32.const 77)) + ) + (i32.and + (i32.eq (i32.load8_u (i32.const 24)) (i32.const 0)) + (i32.eq (i32.load8_u (i32.const 1023)) (i32.const 0)) + ) + ) + ) + ) + + ;; Memory cast + (func (export "cast") (result f64) + (i64.store (i32.const 8) (i64.const -12345)) + (if + (f64.eq + (f64.load (i32.const 8)) + (f64.reinterpret_i64 (i64.const -12345)) + ) + (then (return (f64.const 0))) + ) + (i64.store align=1 (i32.const 9) (i64.const 0)) + (i32.store16 align=1 (i32.const 15) (i32.const 16453)) + (f64.load align=1 (i32.const 9)) + ) + + ;; Sign and zero extending memory loads + (func (export "i32_load8_s") (param $i i32) (result i32) + (i32.store8 (i32.const 8) (local.get $i)) + (i32.load8_s (i32.const 8)) + ) + (func (export "i32_load8_u") (param $i i32) (result i32) + (i32.store8 (i32.const 8) (local.get $i)) + (i32.load8_u (i32.const 8)) + ) + (func (export "i32_load16_s") (param $i i32) (result i32) + (i32.store16 (i32.const 8) (local.get $i)) + (i32.load16_s (i32.const 8)) + ) + (func (export "i32_load16_u") (param $i i32) (result i32) + (i32.store16 (i32.const 8) (local.get $i)) + (i32.load16_u (i32.const 8)) + ) + (func (export "i64_load8_s") (param $i i64) (result i64) + (i64.store8 (i32.const 8) (local.get $i)) + (i64.load8_s (i32.const 8)) + ) + (func (export "i64_load8_u") (param $i i64) (result i64) + (i64.store8 (i32.const 8) (local.get $i)) + (i64.load8_u (i32.const 8)) + ) + (func (export "i64_load16_s") (param $i i64) (result i64) + (i64.store16 (i32.const 8) (local.get $i)) + (i64.load16_s (i32.const 8)) + ) + (func (export "i64_load16_u") (param $i i64) (result i64) + (i64.store16 (i32.const 8) (local.get $i)) + (i64.load16_u (i32.const 8)) + ) + (func (export "i64_load32_s") (param $i i64) (result i64) + (i64.store32 (i32.const 8) (local.get $i)) + (i64.load32_s (i32.const 8)) + ) + (func (export "i64_load32_u") (param $i i64) (result i64) + (i64.store32 (i32.const 8) (local.get $i)) + (i64.load32_u (i32.const 8)) + ) +) + +(assert_return (invoke "data") (i32.const 1)) +(assert_return (invoke "cast") (f64.const 42.0)) + +(assert_return (invoke "i32_load8_s" (i32.const -1)) (i32.const -1)) +(assert_return (invoke "i32_load8_u" (i32.const -1)) (i32.const 255)) +(assert_return (invoke "i32_load16_s" (i32.const -1)) (i32.const -1)) +(assert_return (invoke "i32_load16_u" (i32.const -1)) (i32.const 65535)) + +(assert_return (invoke "i32_load8_s" (i32.const 100)) (i32.const 100)) +(assert_return (invoke "i32_load8_u" (i32.const 200)) (i32.const 200)) +(assert_return (invoke "i32_load16_s" (i32.const 20000)) (i32.const 20000)) +(assert_return (invoke "i32_load16_u" (i32.const 40000)) (i32.const 40000)) + +(assert_return (invoke "i32_load8_s" (i32.const 0xfedc6543)) (i32.const 0x43)) +(assert_return (invoke "i32_load8_s" (i32.const 0x3456cdef)) (i32.const 0xffffffef)) +(assert_return (invoke "i32_load8_u" (i32.const 0xfedc6543)) (i32.const 0x43)) +(assert_return (invoke "i32_load8_u" (i32.const 0x3456cdef)) (i32.const 0xef)) +(assert_return (invoke "i32_load16_s" (i32.const 0xfedc6543)) (i32.const 0x6543)) +(assert_return (invoke "i32_load16_s" (i32.const 0x3456cdef)) (i32.const 0xffffcdef)) +(assert_return (invoke "i32_load16_u" (i32.const 0xfedc6543)) (i32.const 0x6543)) +(assert_return (invoke "i32_load16_u" (i32.const 0x3456cdef)) (i32.const 0xcdef)) + +(assert_return (invoke "i64_load8_s" (i64.const -1)) (i64.const -1)) +(assert_return (invoke "i64_load8_u" (i64.const -1)) (i64.const 255)) +(assert_return (invoke "i64_load16_s" (i64.const -1)) (i64.const -1)) +(assert_return (invoke "i64_load16_u" (i64.const -1)) (i64.const 65535)) +(assert_return (invoke "i64_load32_s" (i64.const -1)) (i64.const -1)) +(assert_return (invoke "i64_load32_u" (i64.const -1)) (i64.const 4294967295)) + +(assert_return (invoke "i64_load8_s" (i64.const 100)) (i64.const 100)) +(assert_return (invoke "i64_load8_u" (i64.const 200)) (i64.const 200)) +(assert_return (invoke "i64_load16_s" (i64.const 20000)) (i64.const 20000)) +(assert_return (invoke "i64_load16_u" (i64.const 40000)) (i64.const 40000)) +(assert_return (invoke "i64_load32_s" (i64.const 20000)) (i64.const 20000)) +(assert_return (invoke "i64_load32_u" (i64.const 40000)) (i64.const 40000)) + +(assert_return (invoke "i64_load8_s" (i64.const 0xfedcba9856346543)) (i64.const 0x43)) +(assert_return (invoke "i64_load8_s" (i64.const 0x3456436598bacdef)) (i64.const 0xffffffffffffffef)) +(assert_return (invoke "i64_load8_u" (i64.const 0xfedcba9856346543)) (i64.const 0x43)) +(assert_return (invoke "i64_load8_u" (i64.const 0x3456436598bacdef)) (i64.const 0xef)) +(assert_return (invoke "i64_load16_s" (i64.const 0xfedcba9856346543)) (i64.const 0x6543)) +(assert_return (invoke "i64_load16_s" (i64.const 0x3456436598bacdef)) (i64.const 0xffffffffffffcdef)) +(assert_return (invoke "i64_load16_u" (i64.const 0xfedcba9856346543)) (i64.const 0x6543)) +(assert_return (invoke "i64_load16_u" (i64.const 0x3456436598bacdef)) (i64.const 0xcdef)) +(assert_return (invoke "i64_load32_s" (i64.const 0xfedcba9856346543)) (i64.const 0x56346543)) +(assert_return (invoke "i64_load32_s" (i64.const 0x3456436598bacdef)) (i64.const 0xffffffff98bacdef)) +(assert_return (invoke "i64_load32_u" (i64.const 0xfedcba9856346543)) (i64.const 0x56346543)) +(assert_return (invoke "i64_load32_u" (i64.const 0x3456436598bacdef)) (i64.const 0x98bacdef)) + +;; Duplicate identifier errors + +(assert_malformed (module quote + "(memory $foo 1)" + "(memory $foo 1)") + "duplicate memory") +(assert_malformed (module quote + "(import \"\" \"\" (memory $foo 1))" + "(memory $foo 1)") + "duplicate memory") +(assert_malformed (module quote + "(import \"\" \"\" (memory $foo 1))" + "(import \"\" \"\" (memory $foo 1))") + "duplicate memory") diff --git a/runtime/unc-vm/tests/wast/spec/ref_func.wast b/runtime/unc-vm/tests/wast/spec/ref_func.wast new file mode 100644 index 000000000..adb5cb788 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/ref_func.wast @@ -0,0 +1,115 @@ +(module + (func (export "f") (param $x i32) (result i32) (local.get $x)) +) +(register "M") + +(module + (func $f (import "M" "f") (param i32) (result i32)) + (func $g (param $x i32) (result i32) + (i32.add (local.get $x) (i32.const 1)) + ) + + (global funcref (ref.func $f)) + (global funcref (ref.func $g)) + (global $v (mut funcref) (ref.func $f)) + + (global funcref (ref.func $gf1)) + (global funcref (ref.func $gf2)) + (func (drop (ref.func $ff1)) (drop (ref.func $ff2))) + (elem declare func $gf1 $ff1) + (elem declare funcref (ref.func $gf2) (ref.func $ff2)) + (func $gf1) + (func $gf2) + (func $ff1) + (func $ff2) + + (func (export "is_null-f") (result i32) + (ref.is_null (ref.func $f)) + ) + (func (export "is_null-g") (result i32) + (ref.is_null (ref.func $g)) + ) + (func (export "is_null-v") (result i32) + (ref.is_null (global.get $v)) + ) + + (func (export "set-f") (global.set $v (ref.func $f))) + (func (export "set-g") (global.set $v (ref.func $g))) + + (table $t 1 funcref) + (elem declare func $f $g) + + (func (export "call-f") (param $x i32) (result i32) + (table.set $t (i32.const 0) (ref.func $f)) + (call_indirect $t (param i32) (result i32) (local.get $x) (i32.const 0)) + ) + (func (export "call-g") (param $x i32) (result i32) + (table.set $t (i32.const 0) (ref.func $g)) + (call_indirect $t (param i32) (result i32) (local.get $x) (i32.const 0)) + ) + (func (export "call-v") (param $x i32) (result i32) + (table.set $t (i32.const 0) (global.get $v)) + (call_indirect $t (param i32) (result i32) (local.get $x) (i32.const 0)) + ) +) + +(assert_return (invoke "is_null-f") (i32.const 0)) +(assert_return (invoke "is_null-g") (i32.const 0)) +(assert_return (invoke "is_null-v") (i32.const 0)) + +(assert_return (invoke "call-f" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "call-g" (i32.const 4)) (i32.const 5)) +(assert_return (invoke "call-v" (i32.const 4)) (i32.const 4)) +(invoke "set-g") +(assert_return (invoke "call-v" (i32.const 4)) (i32.const 5)) +(invoke "set-f") +(assert_return (invoke "call-v" (i32.const 4)) (i32.const 4)) + +(assert_invalid + (module + (func $f (import "M" "f") (param i32) (result i32)) + (func $g (import "M" "g") (param i32) (result i32)) + (global funcref (ref.func 7)) + ) + "unknown function 7" +) + + +;; Reference declaration + +(module + (func $f1) + (func $f2) + (func $f3) + (func $f4) + (func $f5) + (func $f6) + + (table $t 1 funcref) + + (global funcref (ref.func $f1)) + (export "f" (func $f2)) + (elem (table $t) (i32.const 0) func $f3) + (elem (table $t) (i32.const 0) funcref (ref.func $f4)) + (elem func $f5) + (elem funcref (ref.func $f6)) + + (func + (ref.func $f1) + (ref.func $f2) + (ref.func $f3) + (ref.func $f4) + (ref.func $f5) + (ref.func $f6) + (return) + ) +) + +(assert_invalid + (module (func $f (drop (ref.func $f)))) + "undeclared function reference" +) +(assert_invalid + (module (start $f) (func $f (drop (ref.func $f)))) + "undeclared function reference" +) diff --git a/runtime/unc-vm/tests/wast/spec/ref_is_null.wast b/runtime/unc-vm/tests/wast/spec/ref_is_null.wast new file mode 100644 index 000000000..8396da4a7 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/ref_is_null.wast @@ -0,0 +1,58 @@ +(module + (func $f1 (export "funcref") (param $x funcref) (result i32) + (ref.is_null (local.get $x)) + ) + (func $f2 (export "externref") (param $x externref) (result i32) + (ref.is_null (local.get $x)) + ) + + (table $t1 2 funcref) + (table $t2 2 externref) + (elem (table $t1) (i32.const 1) func $dummy) + (func $dummy) + + (func (export "init") (param $r externref) + (table.set $t2 (i32.const 1) (local.get $r)) + ) + (func (export "deinit") + (table.set $t1 (i32.const 1) (ref.null func)) + (table.set $t2 (i32.const 1) (ref.null extern)) + ) + + (func (export "funcref-elem") (param $x i32) (result i32) + (call $f1 (table.get $t1 (local.get $x))) + ) + (func (export "externref-elem") (param $x i32) (result i32) + (call $f2 (table.get $t2 (local.get $x))) + ) +) + +(assert_return (invoke "funcref" (ref.null func)) (i32.const 1)) +(assert_return (invoke "externref" (ref.null extern)) (i32.const 1)) + +(assert_return (invoke "externref" (ref.extern 1)) (i32.const 0)) + +(invoke "init" (ref.extern 0)) + +(assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "externref-elem" (i32.const 0)) (i32.const 1)) + +(assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "externref-elem" (i32.const 1)) (i32.const 0)) + +(invoke "deinit") + +(assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "externref-elem" (i32.const 0)) (i32.const 1)) + +(assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "externref-elem" (i32.const 1)) (i32.const 1)) + +(assert_invalid + (module (func $ref-vs-num (param i32) (ref.is_null (local.get 0)))) + "type mismatch" +) +(assert_invalid + (module (func $ref-vs-empty (ref.is_null))) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/ref_null.wast b/runtime/unc-vm/tests/wast/spec/ref_null.wast new file mode 100644 index 000000000..b88b0888f --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/ref_null.wast @@ -0,0 +1,10 @@ +(module + (func (export "externref") (result externref) (ref.null extern)) + (func (export "funcref") (result funcref) (ref.null func)) + + (global externref (ref.null extern)) + (global funcref (ref.null func)) +) + +(assert_return (invoke "externref") (ref.null extern)) +(assert_return (invoke "funcref") (ref.null func)) diff --git a/runtime/unc-vm/tests/wast/spec/return.wast b/runtime/unc-vm/tests/wast/spec/return.wast new file mode 100644 index 000000000..7077f25b6 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/return.wast @@ -0,0 +1,479 @@ +;; Test `return` operator + +(module + ;; Auxiliary definition + (func $dummy) + + (func (export "type-i32") (drop (i32.ctz (return)))) + (func (export "type-i64") (drop (i64.ctz (return)))) + (func (export "type-f32") (drop (f32.neg (return)))) + (func (export "type-f64") (drop (f64.neg (return)))) + + (func (export "type-i32-value") (result i32) + (block (result i32) (i32.ctz (return (i32.const 1)))) + ) + (func (export "type-i64-value") (result i64) + (block (result i64) (i64.ctz (return (i64.const 2)))) + ) + (func (export "type-f32-value") (result f32) + (block (result f32) (f32.neg (return (f32.const 3)))) + ) + (func (export "type-f64-value") (result f64) + (block (result f64) (f64.neg (return (f64.const 4)))) + ) + + (func (export "nullary") (return)) + (func (export "unary") (result f64) (return (f64.const 3))) + + (func (export "as-func-first") (result i32) + (return (i32.const 1)) (i32.const 2) + ) + (func (export "as-func-mid") (result i32) + (call $dummy) (return (i32.const 2)) (i32.const 3) + ) + (func (export "as-func-last") + (nop) (call $dummy) (return) + ) + (func (export "as-func-value") (result i32) + (nop) (call $dummy) (return (i32.const 3)) + ) + + (func (export "as-block-first") + (block (return) (call $dummy)) + ) + (func (export "as-block-mid") + (block (call $dummy) (return) (call $dummy)) + ) + (func (export "as-block-last") + (block (nop) (call $dummy) (return)) + ) + (func (export "as-block-value") (result i32) + (block (result i32) (nop) (call $dummy) (return (i32.const 2))) + ) + + (func (export "as-loop-first") (result i32) + (loop (result i32) (return (i32.const 3)) (i32.const 2)) + ) + (func (export "as-loop-mid") (result i32) + (loop (result i32) (call $dummy) (return (i32.const 4)) (i32.const 2)) + ) + (func (export "as-loop-last") (result i32) + (loop (result i32) (nop) (call $dummy) (return (i32.const 5))) + ) + + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (return (i32.const 9)))) + ) + + (func (export "as-br_if-cond") + (block (br_if 0 (return))) + ) + (func (export "as-br_if-value") (result i32) + (block (result i32) + (drop (br_if 0 (return (i32.const 8)) (i32.const 1))) (i32.const 7) + ) + ) + (func (export "as-br_if-value-cond") (result i32) + (block (result i32) + (drop (br_if 0 (i32.const 6) (return (i32.const 9)))) (i32.const 7) + ) + ) + + (func (export "as-br_table-index") (result i64) + (block (br_table 0 0 0 (return (i64.const 9)))) (i64.const -1) + ) + (func (export "as-br_table-value") (result i32) + (block (result i32) + (br_table 0 0 0 (return (i32.const 10)) (i32.const 1)) (i32.const 7) + ) + ) + (func (export "as-br_table-value-index") (result i32) + (block (result i32) + (br_table 0 0 (i32.const 6) (return (i32.const 11))) (i32.const 7) + ) + ) + + (func (export "as-return-value") (result i64) + (return (return (i64.const 7))) + ) + + (func (export "as-if-cond") (result i32) + (if (result i32) + (return (i32.const 2)) (then (i32.const 0)) (else (i32.const 1)) + ) + ) + (func (export "as-if-then") (param i32 i32) (result i32) + (if (result i32) + (local.get 0) (then (return (i32.const 3))) (else (local.get 1)) + ) + ) + (func (export "as-if-else") (param i32 i32) (result i32) + (if (result i32) + (local.get 0) (then (local.get 1)) (else (return (i32.const 4))) + ) + ) + + (func (export "as-select-first") (param i32 i32) (result i32) + (select (return (i32.const 5)) (local.get 0) (local.get 1)) + ) + (func (export "as-select-second") (param i32 i32) (result i32) + (select (local.get 0) (return (i32.const 6)) (local.get 1)) + ) + (func (export "as-select-cond") (result i32) + (select (i32.const 0) (i32.const 1) (return (i32.const 7))) + ) + + (func $f (param i32 i32 i32) (result i32) (i32.const -1)) + (func (export "as-call-first") (result i32) + (call $f (return (i32.const 12)) (i32.const 2) (i32.const 3)) + ) + (func (export "as-call-mid") (result i32) + (call $f (i32.const 1) (return (i32.const 13)) (i32.const 3)) + ) + (func (export "as-call-last") (result i32) + (call $f (i32.const 1) (i32.const 2) (return (i32.const 14))) + ) + + (type $sig (func (param i32 i32 i32) (result i32))) + (table funcref (elem $f)) + (func (export "as-call_indirect-func") (result i32) + (call_indirect (type $sig) + (return (i32.const 20)) (i32.const 1) (i32.const 2) (i32.const 3) + ) + ) + (func (export "as-call_indirect-first") (result i32) + (call_indirect (type $sig) + (i32.const 0) (return (i32.const 21)) (i32.const 2) (i32.const 3) + ) + ) + (func (export "as-call_indirect-mid") (result i32) + (call_indirect (type $sig) + (i32.const 0) (i32.const 1) (return (i32.const 22)) (i32.const 3) + ) + ) + (func (export "as-call_indirect-last") (result i32) + (call_indirect (type $sig) + (i32.const 0) (i32.const 1) (i32.const 2) (return (i32.const 23)) + ) + ) + + (func (export "as-local.set-value") (result i32) (local f32) + (local.set 0 (return (i32.const 17))) (i32.const -1) + ) + (func (export "as-local.tee-value") (result i32) (local i32) + (local.tee 0 (return (i32.const 1))) + ) + (global $a (mut i32) (i32.const 0)) + (func (export "as-global.set-value") (result i32) + (global.set $a (return (i32.const 1))) + ) + + (memory 1) + (func (export "as-load-address") (result f32) + (f32.load (return (f32.const 1.7))) + ) + (func (export "as-loadN-address") (result i64) + (i64.load8_s (return (i64.const 30))) + ) + + (func (export "as-store-address") (result i32) + (f64.store (return (i32.const 30)) (f64.const 7)) (i32.const -1) + ) + (func (export "as-store-value") (result i32) + (i64.store (i32.const 2) (return (i32.const 31))) (i32.const -1) + ) + + (func (export "as-storeN-address") (result i32) + (i32.store8 (return (i32.const 32)) (i32.const 7)) (i32.const -1) + ) + (func (export "as-storeN-value") (result i32) + (i64.store16 (i32.const 2) (return (i32.const 33))) (i32.const -1) + ) + + (func (export "as-unary-operand") (result f32) + (f32.neg (return (f32.const 3.4))) + ) + + (func (export "as-binary-left") (result i32) + (i32.add (return (i32.const 3)) (i32.const 10)) + ) + (func (export "as-binary-right") (result i64) + (i64.sub (i64.const 10) (return (i64.const 45))) + ) + + (func (export "as-test-operand") (result i32) + (i32.eqz (return (i32.const 44))) + ) + + (func (export "as-compare-left") (result i32) + (f64.le (return (i32.const 43)) (f64.const 10)) + ) + (func (export "as-compare-right") (result i32) + (f32.ne (f32.const 10) (return (i32.const 42))) + ) + + (func (export "as-convert-operand") (result i32) + (i32.wrap_i64 (return (i32.const 41))) + ) + + (func (export "as-memory.grow-size") (result i32) + (memory.grow (return (i32.const 40))) + ) +) + +(assert_return (invoke "type-i32")) +(assert_return (invoke "type-i64")) +(assert_return (invoke "type-f32")) +(assert_return (invoke "type-f64")) + +(assert_return (invoke "type-i32-value") (i32.const 1)) +(assert_return (invoke "type-i64-value") (i64.const 2)) +(assert_return (invoke "type-f32-value") (f32.const 3)) +(assert_return (invoke "type-f64-value") (f64.const 4)) + +(assert_return (invoke "nullary")) +(assert_return (invoke "unary") (f64.const 3)) + +(assert_return (invoke "as-func-first") (i32.const 1)) +(assert_return (invoke "as-func-mid") (i32.const 2)) +(assert_return (invoke "as-func-last")) +(assert_return (invoke "as-func-value") (i32.const 3)) + +(assert_return (invoke "as-block-first")) +(assert_return (invoke "as-block-mid")) +(assert_return (invoke "as-block-last")) +(assert_return (invoke "as-block-value") (i32.const 2)) + +(assert_return (invoke "as-loop-first") (i32.const 3)) +(assert_return (invoke "as-loop-mid") (i32.const 4)) +(assert_return (invoke "as-loop-last") (i32.const 5)) + +(assert_return (invoke "as-br-value") (i32.const 9)) + +(assert_return (invoke "as-br_if-cond")) +(assert_return (invoke "as-br_if-value") (i32.const 8)) +(assert_return (invoke "as-br_if-value-cond") (i32.const 9)) + +(assert_return (invoke "as-br_table-index") (i64.const 9)) +(assert_return (invoke "as-br_table-value") (i32.const 10)) +(assert_return (invoke "as-br_table-value-index") (i32.const 11)) + +(assert_return (invoke "as-return-value") (i64.const 7)) + +(assert_return (invoke "as-if-cond") (i32.const 2)) +(assert_return (invoke "as-if-then" (i32.const 1) (i32.const 6)) (i32.const 3)) +(assert_return (invoke "as-if-then" (i32.const 0) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-if-else" (i32.const 0) (i32.const 6)) (i32.const 4)) +(assert_return (invoke "as-if-else" (i32.const 1) (i32.const 6)) (i32.const 6)) + +(assert_return (invoke "as-select-first" (i32.const 0) (i32.const 6)) (i32.const 5)) +(assert_return (invoke "as-select-first" (i32.const 1) (i32.const 6)) (i32.const 5)) +(assert_return (invoke "as-select-second" (i32.const 0) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-select-second" (i32.const 1) (i32.const 6)) (i32.const 6)) +(assert_return (invoke "as-select-cond") (i32.const 7)) + +(assert_return (invoke "as-call-first") (i32.const 12)) +(assert_return (invoke "as-call-mid") (i32.const 13)) +(assert_return (invoke "as-call-last") (i32.const 14)) + +(assert_return (invoke "as-call_indirect-func") (i32.const 20)) +(assert_return (invoke "as-call_indirect-first") (i32.const 21)) +(assert_return (invoke "as-call_indirect-mid") (i32.const 22)) +(assert_return (invoke "as-call_indirect-last") (i32.const 23)) + +(assert_return (invoke "as-local.set-value") (i32.const 17)) +(assert_return (invoke "as-local.tee-value") (i32.const 1)) +(assert_return (invoke "as-global.set-value") (i32.const 1)) + +(assert_return (invoke "as-load-address") (f32.const 1.7)) +(assert_return (invoke "as-loadN-address") (i64.const 30)) + +(assert_return (invoke "as-store-address") (i32.const 30)) +(assert_return (invoke "as-store-value") (i32.const 31)) +(assert_return (invoke "as-storeN-address") (i32.const 32)) +(assert_return (invoke "as-storeN-value") (i32.const 33)) + +(assert_return (invoke "as-unary-operand") (f32.const 3.4)) + +(assert_return (invoke "as-binary-left") (i32.const 3)) +(assert_return (invoke "as-binary-right") (i64.const 45)) + +(assert_return (invoke "as-test-operand") (i32.const 44)) + +(assert_return (invoke "as-compare-left") (i32.const 43)) +(assert_return (invoke "as-compare-right") (i32.const 42)) + +(assert_return (invoke "as-convert-operand") (i32.const 41)) + +(assert_return (invoke "as-memory.grow-size") (i32.const 40)) + +(assert_invalid + (module (func $type-value-empty-vs-num (result i32) (return))) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-vs-num-in-block (result i32) + (i32.const 0) + (block (return)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-vs-num-in-loop (result i32) + (i32.const 0) + (loop (return)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-vs-num-in-then (result i32) + (i32.const 0) (i32.const 0) + (if (then (return))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-vs-num-in-else (result i32) + (i32.const 0) (i32.const 0) + (if (result i32) (then (i32.const 0)) (else (return))) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-vs-num-in-br (result i32) + (i32.const 0) + (block (br 0 (return))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-vs-num-in-br_if (result i32) + (i32.const 0) + (block (br_if 0 (return) (i32.const 1))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-vs-num-in-br_table (result i32) + (i32.const 0) + (block (br_table 0 (return))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-vs-num-in-return (result i32) + (return (return)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-vs-num-in-select (result i32) + (select (return) (i32.const 1) (i32.const 2)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-vs-num-in-call (result i32) + (call 1 (return)) + ) + (func (param i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-value-empty-vs-num-in-call_indirect (result i32) + (block (result i32) + (call_indirect (type $sig) + (return) (i32.const 0) + ) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-vs-num-in-local.set (result i32) + (local i32) + (local.set 0 (return)) (local.get 0) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-value-empty-vs-num-in-local.tee (result i32) + (local i32) + (local.tee 0 (return)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (global $x (mut i32) (i32.const 0)) + (func $type-value-empty-vs-num-in-global.set (result i32) + (global.set $x (return)) (global.get $x) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-value-empty-vs-num-in-memory.grow (result i32) + (memory.grow (return)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 0) + (func $type-value-empty-vs-num-in-load (result i32) + (i32.load (return)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-value-empty-vs-num-in-store (result i32) + (i32.store (return) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module (func $type-value-void-vs-num (result f64) (return (nop)))) + "type mismatch" +) +(assert_invalid + (module (func $type-value-num-vs-num (result f64) (return (i64.const 1)))) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/select.wast b/runtime/unc-vm/tests/wast/spec/select.wast new file mode 100644 index 000000000..b343bf93c --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/select.wast @@ -0,0 +1,598 @@ +(module + ;; Auxiliary + (func $dummy) + (table $tab funcref (elem $dummy)) + (memory 1) + + (func (export "select-i32") (param i32 i32 i32) (result i32) + (select (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-i64") (param i64 i64 i32) (result i64) + (select (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-f32") (param f32 f32 i32) (result f32) + (select (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-f64") (param f64 f64 i32) (result f64) + (select (local.get 0) (local.get 1) (local.get 2)) + ) + + (func (export "select-i32-t") (param i32 i32 i32) (result i32) + (select (result i32) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-i64-t") (param i64 i64 i32) (result i64) + (select (result i64) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-f32-t") (param f32 f32 i32) (result f32) + (select (result f32) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-f64-t") (param f64 f64 i32) (result f64) + (select (result f64) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-funcref") (param funcref funcref i32) (result funcref) + (select (result funcref) (local.get 0) (local.get 1) (local.get 2)) + ) + (func (export "select-externref") (param externref externref i32) (result externref) + (select (result externref) (local.get 0) (local.get 1) (local.get 2)) + ) + + ;; Check that both sides of the select are evaluated + (func (export "select-trap-left") (param $cond i32) (result i32) + (select (unreachable) (i32.const 0) (local.get $cond)) + ) + (func (export "select-trap-right") (param $cond i32) (result i32) + (select (i32.const 0) (unreachable) (local.get $cond)) + ) + + (func (export "select-unreached") + (unreachable) (select) + (unreachable) (i32.const 0) (select) + (unreachable) (i32.const 0) (i32.const 0) (select) + (unreachable) (i32.const 0) (i32.const 0) (i32.const 0) (select) + (unreachable) (f32.const 0) (i32.const 0) (select) + (unreachable) + ) + + (func (export "select_unreached_result_1") (result i32) + (unreachable) (i32.add (select)) + ) + + (func (export "select_unreached_result_2") (result i64) + (unreachable) (i64.add (select (i64.const 0) (i32.const 0))) + ) + + ;; As the argument of control constructs and instructions + + (func (export "as-select-first") (param i32) (result i32) + (select (select (i32.const 0) (i32.const 1) (local.get 0)) (i32.const 2) (i32.const 3)) + ) + (func (export "as-select-mid") (param i32) (result i32) + (select (i32.const 2) (select (i32.const 0) (i32.const 1) (local.get 0)) (i32.const 3)) + ) + (func (export "as-select-last") (param i32) (result i32) + (select (i32.const 2) (i32.const 3) (select (i32.const 0) (i32.const 1) (local.get 0))) + ) + + (func (export "as-loop-first") (param i32) (result i32) + (loop (result i32) (select (i32.const 2) (i32.const 3) (local.get 0)) (call $dummy) (call $dummy)) + ) + (func (export "as-loop-mid") (param i32) (result i32) + (loop (result i32) (call $dummy) (select (i32.const 2) (i32.const 3) (local.get 0)) (call $dummy)) + ) + (func (export "as-loop-last") (param i32) (result i32) + (loop (result i32) (call $dummy) (call $dummy) (select (i32.const 2) (i32.const 3) (local.get 0))) + ) + + (func (export "as-if-condition") (param i32) + (select (i32.const 2) (i32.const 3) (local.get 0)) (if (then (call $dummy))) + ) + (func (export "as-if-then") (param i32) (result i32) + (if (result i32) (i32.const 1) (then (select (i32.const 2) (i32.const 3) (local.get 0))) (else (i32.const 4))) + ) + (func (export "as-if-else") (param i32) (result i32) + (if (result i32) (i32.const 0) (then (i32.const 2)) (else (select (i32.const 2) (i32.const 3) (local.get 0)))) + ) + + (func (export "as-br_if-first") (param i32) (result i32) + (block (result i32) (br_if 0 (select (i32.const 2) (i32.const 3) (local.get 0)) (i32.const 4))) + ) + (func (export "as-br_if-last") (param i32) (result i32) + (block (result i32) (br_if 0 (i32.const 2) (select (i32.const 2) (i32.const 3) (local.get 0)))) + ) + + (func (export "as-br_table-first") (param i32) (result i32) + (block (result i32) (select (i32.const 2) (i32.const 3) (local.get 0)) (i32.const 2) (br_table 0 0)) + ) + (func (export "as-br_table-last") (param i32) (result i32) + (block (result i32) (i32.const 2) (select (i32.const 2) (i32.const 3) (local.get 0)) (br_table 0 0)) + ) + + (func $func (param i32 i32) (result i32) (local.get 0)) + (type $check (func (param i32 i32) (result i32))) + (table $t funcref (elem $func)) + (func (export "as-call_indirect-first") (param i32) (result i32) + (block (result i32) + (call_indirect $t (type $check) + (select (i32.const 2) (i32.const 3) (local.get 0)) (i32.const 1) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-mid") (param i32) (result i32) + (block (result i32) + (call_indirect $t (type $check) + (i32.const 1) (select (i32.const 2) (i32.const 3) (local.get 0)) (i32.const 0) + ) + ) + ) + (func (export "as-call_indirect-last") (param i32) (result i32) + (block (result i32) + (call_indirect $t (type $check) + (i32.const 1) (i32.const 4) (select (i32.const 2) (i32.const 3) (local.get 0)) + ) + ) + ) + + (func (export "as-store-first") (param i32) + (select (i32.const 0) (i32.const 4) (local.get 0)) (i32.const 1) (i32.store) + ) + (func (export "as-store-last") (param i32) + (i32.const 8) (select (i32.const 1) (i32.const 2) (local.get 0)) (i32.store) + ) + + (func (export "as-memory.grow-value") (param i32) (result i32) + (memory.grow (select (i32.const 1) (i32.const 2) (local.get 0))) + ) + + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "as-call-value") (param i32) (result i32) + (call $f (select (i32.const 1) (i32.const 2) (local.get 0))) + ) + (func (export "as-return-value") (param i32) (result i32) + (select (i32.const 1) (i32.const 2) (local.get 0)) (return) + ) + (func (export "as-drop-operand") (param i32) + (drop (select (i32.const 1) (i32.const 2) (local.get 0))) + ) + (func (export "as-br-value") (param i32) (result i32) + (block (result i32) (br 0 (select (i32.const 1) (i32.const 2) (local.get 0)))) + ) + (func (export "as-local.set-value") (param i32) (result i32) + (local i32) (local.set 0 (select (i32.const 1) (i32.const 2) (local.get 0))) (local.get 0) + ) + (func (export "as-local.tee-value") (param i32) (result i32) + (local.tee 0 (select (i32.const 1) (i32.const 2) (local.get 0))) + ) + (global $a (mut i32) (i32.const 10)) + (func (export "as-global.set-value") (param i32) (result i32) + (global.set $a (select (i32.const 1) (i32.const 2) (local.get 0))) + (global.get $a) + ) + (func (export "as-load-operand") (param i32) (result i32) + (i32.load (select (i32.const 0) (i32.const 4) (local.get 0))) + ) + + (func (export "as-unary-operand") (param i32) (result i32) + (i32.eqz (select (i32.const 0) (i32.const 1) (local.get 0))) + ) + (func (export "as-binary-operand") (param i32) (result i32) + (i32.mul + (select (i32.const 1) (i32.const 2) (local.get 0)) + (select (i32.const 1) (i32.const 2) (local.get 0)) + ) + ) + (func (export "as-test-operand") (param i32) (result i32) + (block (result i32) + (i32.eqz (select (i32.const 0) (i32.const 1) (local.get 0))) + ) + ) + + (func (export "as-compare-left") (param i32) (result i32) + (block (result i32) + (i32.le_s (select (i32.const 1) (i32.const 2) (local.get 0)) (i32.const 1)) + ) + ) + (func (export "as-compare-right") (param i32) (result i32) + (block (result i32) + (i32.ne (i32.const 1) (select (i32.const 0) (i32.const 1) (local.get 0))) + ) + ) + + (func (export "as-convert-operand") (param i32) (result i32) + (block (result i32) + (i32.wrap_i64 (select (i64.const 1) (i64.const 0) (local.get 0))) + ) + ) + + (func (export "unreachable-num") + (unreachable) + (select) + (i32.eqz) + (drop) + ) + (func (export "unreachable-ref") + (unreachable) + (select) + (ref.is_null) + (drop) + ) +) + +(assert_return (invoke "select-i32" (i32.const 1) (i32.const 2) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "select-i64" (i64.const 2) (i64.const 1) (i32.const 1)) (i64.const 2)) +(assert_return (invoke "select-f32" (f32.const 1) (f32.const 2) (i32.const 1)) (f32.const 1)) +(assert_return (invoke "select-f64" (f64.const 1) (f64.const 2) (i32.const 1)) (f64.const 1)) + +(assert_return (invoke "select-i32" (i32.const 1) (i32.const 2) (i32.const 0)) (i32.const 2)) +(assert_return (invoke "select-i32" (i32.const 2) (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "select-i64" (i64.const 2) (i64.const 1) (i32.const -1)) (i64.const 2)) +(assert_return (invoke "select-i64" (i64.const 2) (i64.const 1) (i32.const 0xf0f0f0f0)) (i64.const 2)) + +(assert_return (invoke "select-f32" (f32.const nan) (f32.const 1) (i32.const 1)) (f32.const nan)) +(assert_return (invoke "select-f32" (f32.const nan:0x20304) (f32.const 1) (i32.const 1)) (f32.const nan:0x20304)) +(assert_return (invoke "select-f32" (f32.const nan) (f32.const 1) (i32.const 0)) (f32.const 1)) +(assert_return (invoke "select-f32" (f32.const nan:0x20304) (f32.const 1) (i32.const 0)) (f32.const 1)) +(assert_return (invoke "select-f32" (f32.const 2) (f32.const nan) (i32.const 1)) (f32.const 2)) +(assert_return (invoke "select-f32" (f32.const 2) (f32.const nan:0x20304) (i32.const 1)) (f32.const 2)) +(assert_return (invoke "select-f32" (f32.const 2) (f32.const nan) (i32.const 0)) (f32.const nan)) +(assert_return (invoke "select-f32" (f32.const 2) (f32.const nan:0x20304) (i32.const 0)) (f32.const nan:0x20304)) + +(assert_return (invoke "select-f64" (f64.const nan) (f64.const 1) (i32.const 1)) (f64.const nan)) +(assert_return (invoke "select-f64" (f64.const nan:0x20304) (f64.const 1) (i32.const 1)) (f64.const nan:0x20304)) +(assert_return (invoke "select-f64" (f64.const nan) (f64.const 1) (i32.const 0)) (f64.const 1)) +(assert_return (invoke "select-f64" (f64.const nan:0x20304) (f64.const 1) (i32.const 0)) (f64.const 1)) +(assert_return (invoke "select-f64" (f64.const 2) (f64.const nan) (i32.const 1)) (f64.const 2)) +(assert_return (invoke "select-f64" (f64.const 2) (f64.const nan:0x20304) (i32.const 1)) (f64.const 2)) +(assert_return (invoke "select-f64" (f64.const 2) (f64.const nan) (i32.const 0)) (f64.const nan)) +(assert_return (invoke "select-f64" (f64.const 2) (f64.const nan:0x20304) (i32.const 0)) (f64.const nan:0x20304)) + +(assert_return (invoke "select-i32-t" (i32.const 1) (i32.const 2) (i32.const 1)) (i32.const 1)) +(assert_return (invoke "select-i64-t" (i64.const 2) (i64.const 1) (i32.const 1)) (i64.const 2)) +(assert_return (invoke "select-f32-t" (f32.const 1) (f32.const 2) (i32.const 1)) (f32.const 1)) +(assert_return (invoke "select-f64-t" (f64.const 1) (f64.const 2) (i32.const 1)) (f64.const 1)) +(assert_return (invoke "select-funcref" (ref.null func) (ref.null func) (i32.const 1)) (ref.null func)) +(assert_return (invoke "select-externref" (ref.extern 1) (ref.extern 2) (i32.const 1)) (ref.extern 1)) + +(assert_return (invoke "select-i32-t" (i32.const 1) (i32.const 2) (i32.const 0)) (i32.const 2)) +(assert_return (invoke "select-i32-t" (i32.const 2) (i32.const 1) (i32.const 0)) (i32.const 1)) +(assert_return (invoke "select-i64-t" (i64.const 2) (i64.const 1) (i32.const -1)) (i64.const 2)) +(assert_return (invoke "select-i64-t" (i64.const 2) (i64.const 1) (i32.const 0xf0f0f0f0)) (i64.const 2)) +(assert_return (invoke "select-externref" (ref.extern 1) (ref.extern 2) (i32.const 0)) (ref.extern 2)) +(assert_return (invoke "select-externref" (ref.extern 2) (ref.extern 1) (i32.const 0)) (ref.extern 1)) + +(assert_return (invoke "select-f32-t" (f32.const nan) (f32.const 1) (i32.const 1)) (f32.const nan)) +(assert_return (invoke "select-f32-t" (f32.const nan:0x20304) (f32.const 1) (i32.const 1)) (f32.const nan:0x20304)) +(assert_return (invoke "select-f32-t" (f32.const nan) (f32.const 1) (i32.const 0)) (f32.const 1)) +(assert_return (invoke "select-f32-t" (f32.const nan:0x20304) (f32.const 1) (i32.const 0)) (f32.const 1)) +(assert_return (invoke "select-f32-t" (f32.const 2) (f32.const nan) (i32.const 1)) (f32.const 2)) +(assert_return (invoke "select-f32-t" (f32.const 2) (f32.const nan:0x20304) (i32.const 1)) (f32.const 2)) +(assert_return (invoke "select-f32-t" (f32.const 2) (f32.const nan) (i32.const 0)) (f32.const nan)) +(assert_return (invoke "select-f32-t" (f32.const 2) (f32.const nan:0x20304) (i32.const 0)) (f32.const nan:0x20304)) + +(assert_return (invoke "select-f64-t" (f64.const nan) (f64.const 1) (i32.const 1)) (f64.const nan)) +(assert_return (invoke "select-f64-t" (f64.const nan:0x20304) (f64.const 1) (i32.const 1)) (f64.const nan:0x20304)) +(assert_return (invoke "select-f64-t" (f64.const nan) (f64.const 1) (i32.const 0)) (f64.const 1)) +(assert_return (invoke "select-f64-t" (f64.const nan:0x20304) (f64.const 1) (i32.const 0)) (f64.const 1)) +(assert_return (invoke "select-f64-t" (f64.const 2) (f64.const nan) (i32.const 1)) (f64.const 2)) +(assert_return (invoke "select-f64-t" (f64.const 2) (f64.const nan:0x20304) (i32.const 1)) (f64.const 2)) +(assert_return (invoke "select-f64-t" (f64.const 2) (f64.const nan) (i32.const 0)) (f64.const nan)) +(assert_return (invoke "select-f64-t" (f64.const 2) (f64.const nan:0x20304) (i32.const 0)) (f64.const nan:0x20304)) + +(assert_trap (invoke "select-trap-left" (i32.const 1)) "unreachable") +(assert_trap (invoke "select-trap-left" (i32.const 0)) "unreachable") +(assert_trap (invoke "select-trap-right" (i32.const 1)) "unreachable") +(assert_trap (invoke "select-trap-right" (i32.const 0)) "unreachable") + +(assert_return (invoke "as-select-first" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-select-first" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "as-select-mid" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-select-mid" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-select-last" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-select-last" (i32.const 1)) (i32.const 3)) + +(assert_return (invoke "as-loop-first" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-loop-first" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-loop-mid" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-loop-mid" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-loop-last" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-loop-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-if-condition" (i32.const 0))) +(assert_return (invoke "as-if-condition" (i32.const 1))) +(assert_return (invoke "as-if-then" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-if-then" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-if-else" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-if-else" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-br_if-first" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-br_if-first" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-br_if-last" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-br_if-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-br_table-first" (i32.const 0)) (i32.const 3)) +(assert_return (invoke "as-br_table-first" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-br_table-last" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-br_table-last" (i32.const 1)) (i32.const 2)) + +(assert_return (invoke "as-call_indirect-first" (i32.const 0)) (i32.const 3)) +;;(assert_return (invoke "as-call_indirect-first" (i32.const 1)) (i32.const 2)) +(assert_return (invoke "as-call_indirect-mid" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-call_indirect-mid" (i32.const 1)) (i32.const 1)) +(assert_trap (invoke "as-call_indirect-last" (i32.const 0)) "undefined element") +(assert_trap (invoke "as-call_indirect-last" (i32.const 1)) "undefined element") + +(assert_return (invoke "as-store-first" (i32.const 0))) +(assert_return (invoke "as-store-first" (i32.const 1))) +(assert_return (invoke "as-store-last" (i32.const 0))) +(assert_return (invoke "as-store-last" (i32.const 1))) + +(assert_return (invoke "as-memory.grow-value" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-memory.grow-value" (i32.const 1)) (i32.const 3)) + +(assert_return (invoke "as-call-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-call-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-return-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-return-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-drop-operand" (i32.const 0))) +(assert_return (invoke "as-drop-operand" (i32.const 1))) +(assert_return (invoke "as-br-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-br-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-local.set-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-local.set-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-local.tee-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-local.tee-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-global.set-value" (i32.const 0)) (i32.const 2)) +(assert_return (invoke "as-global.set-value" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-load-operand" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "as-load-operand" (i32.const 1)) (i32.const 1)) + +(assert_return (invoke "as-unary-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-unary-operand" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-binary-operand" (i32.const 0)) (i32.const 4)) +(assert_return (invoke "as-binary-operand" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-test-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-test-operand" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-compare-left" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-compare-left" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-compare-right" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-compare-right" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "as-convert-operand" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "as-convert-operand" (i32.const 1)) (i32.const 1)) + +(assert_invalid + (module (func $arity-0-implicit (select (nop) (nop) (i32.const 1)))) + "type mismatch" +) +(assert_invalid + (module (func $arity-0 (select (result) (nop) (nop) (i32.const 1)))) + "invalid result arity" +) +(assert_invalid + (module (func $arity-2 (result i32 i32) + (select (result i32 i32) + (i32.const 0) (i32.const 0) + (i32.const 0) (i32.const 0) + (i32.const 1) + ) + )) + "invalid result arity" +) + + +(assert_invalid + (module (func $type-externref-implicit (param $r externref) + (drop (select (local.get $r) (local.get $r) (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-num-vs-num + (drop (select (i32.const 1) (i64.const 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-num-vs-num + (drop (select (i32.const 1) (f32.const 1.0) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-num-vs-num + (drop (select (i32.const 1) (f64.const 1.0) (i32.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-num-vs-num (select (i32.const 1) (i64.const 1) (i32.const 1)) (drop))) + "type mismatch" +) +(assert_invalid + (module (func $type-num-vs-num (select (i32.const 1) (f32.const 1.0) (i32.const 1)) (drop))) + "type mismatch" +) +(assert_invalid + (module (func $type-num-vs-num (select (i32.const 1) (i64.const 1) (i32.const 1)) (drop))) + "type mismatch" +) +(assert_invalid + (module (func $type-num-vs-num (select (i32.const 1) (f32.const 1.0) (i32.const 1)) (drop))) + "type mismatch" +) +(assert_invalid + (module (func $type-num-vs-num (select (i32.const 1) (f64.const 1.0) (i32.const 1)) (drop))) + "type mismatch" +) + + +(assert_invalid + (module + (func $type-1st-operand-empty + (select) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-2nd-operand-empty + (i32.const 0) (select) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-3rd-operand-empty + (i32.const 0) (i32.const 0) (select) (drop) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-1st-operand-empty-in-block + (i32.const 0) (i32.const 0) (i32.const 0) + (block (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-2nd-operand-empty-in-block + (i32.const 0) (i32.const 0) + (block (i32.const 0) (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-3rd-operand-empty-in-block + (i32.const 0) + (block (i32.const 0) (i32.const 0) (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-1st-operand-empty-in-loop + (i32.const 0) (i32.const 0) (i32.const 0) + (loop (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-2nd-operand-empty-in-loop + (i32.const 0) (i32.const 0) + (loop (i32.const 0) (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-3rd-operand-empty-in-loop + (i32.const 0) + (loop (i32.const 0) (i32.const 0) (select) (drop)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-1st-operand-empty-in-then + (i32.const 0) (i32.const 0) (i32.const 0) + (if (then (select) (drop))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-2nd-operand-empty-in-then + (i32.const 0) (i32.const 0) + (if (then (i32.const 0) (select) (drop))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-3rd-operand-empty-in-then + (i32.const 0) + (if (then (i32.const 0) (i32.const 0) (select) (drop))) + ) + ) + "type mismatch" +) + +;; Third operand must be i32 + +(assert_invalid + (module (func (select (i32.const 1) (i32.const 1) (i64.const 1)) (drop))) + "type mismatch" +) +(assert_invalid + (module (func (select (i32.const 1) (i32.const 1) (f32.const 1)) (drop))) + "type mismatch" +) +(assert_invalid + (module (func (select (i32.const 1) (i32.const 1) (f64.const 1)) (drop))) + "type mismatch" +) + +;; Result of select has type of first two operands + +(assert_invalid + (module (func (result i32) (select (i64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch" +) + +;; Validation after unreachable + +;; The first two operands should have the same type as each other +(assert_invalid + (module (func (unreachable) (select (i32.const 1) (i64.const 1) (i32.const 1)) (drop))) + "type mismatch" +) + +(assert_invalid + (module (func (unreachable) (select (i64.const 1) (i32.const 1) (i32.const 1)) (drop))) + "type mismatch" +) + +;; Third operand must be i32 +(assert_invalid + (module (func (unreachable) (select (i32.const 1) (i32.const 1) (i64.const 1)) (drop))) + "type mismatch" +) + +(assert_invalid + (module (func (unreachable) (select (i32.const 1) (i64.const 1)) (drop))) + "type mismatch" +) + +(assert_invalid + (module (func (unreachable) (select (i64.const 1)) (drop))) + "type mismatch" +) + +;; Result of select has type of first two operands (type of second operand when first one is omitted) +(assert_invalid + (module (func (result i32) (unreachable) (select (i64.const 1) (i32.const 1)))) + "type mismatch" +) + +;; select always has non-empty result +(assert_invalid + (module (func (unreachable) (select))) + "type mismatch" +) + diff --git a/runtime/unc-vm/tests/wast/spec/skip-stack-guard-page.wast b/runtime/unc-vm/tests/wast/spec/skip-stack-guard-page.wast new file mode 100644 index 000000000..a472e6814 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/skip-stack-guard-page.wast @@ -0,0 +1,2284 @@ +;; This tests that the stack overflow guard page can't be skipped by a function with more than a page of locals. +(module + (memory 1) + (export "test-guard-page-skip" (func $test-guard-page-skip)) + + (func $test-guard-page-skip + (param $depth i32) + (if (i32.eq (local.get $depth) (i32.const 0)) + (then (call $function-with-many-locals)) + (else (call $test-guard-page-skip (i32.sub (local.get $depth) (i32.const 1)))) + ) + ) + + (func $function-with-many-locals + + ;; 1056 i64 = 8448 bytes of locals + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x000-0x007 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x008-0x00f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x010-0x017 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x018-0x01f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x020-0x027 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x028-0x02f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x030-0x037 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x038-0x03f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x040-0x047 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x048-0x04f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x050-0x057 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x058-0x05f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x060-0x067 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x068-0x06f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x070-0x077 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x078-0x07f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x080-0x087 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x088-0x08f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x090-0x097 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x098-0x09f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x0a0-0x0a7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x0a8-0x0af + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x0b0-0x0b7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x0b8-0x0bf + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x0c0-0x0c7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x0c8-0x0cf + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x0d0-0x0d7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x0d8-0x0df + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x0e0-0x0e7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x0e8-0x0ef + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x0f0-0x0f7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x0f8-0x0ff + + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x100-0x107 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x108-0x10f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x110-0x117 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x118-0x11f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x120-0x127 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x128-0x12f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x130-0x137 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x138-0x13f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x140-0x147 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x148-0x14f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x150-0x157 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x158-0x15f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x160-0x167 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x168-0x16f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x170-0x177 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x178-0x17f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x180-0x187 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x188-0x18f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x190-0x197 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x198-0x19f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x1a0-0x1a7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x1a8-0x1af + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x1b0-0x1b7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x1b8-0x1bf + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x1c0-0x1c7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x1c8-0x1cf + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x1d0-0x1d7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x1d8-0x1df + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x1e0-0x1e7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x1e8-0x1ef + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x1f0-0x1f7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x1f8-0x1ff + + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x200-0x207 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x208-0x20f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x210-0x217 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x218-0x21f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x220-0x227 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x228-0x22f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x230-0x237 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x238-0x23f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x240-0x247 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x248-0x24f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x250-0x257 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x258-0x25f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x260-0x267 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x268-0x26f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x270-0x277 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x278-0x27f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x280-0x287 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x288-0x28f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x290-0x297 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x298-0x29f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x2a0-0x2a7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x2a8-0x2af + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x2b0-0x2b7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x2b8-0x2bf + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x2c0-0x2c7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x2c8-0x2cf + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x2d0-0x2d7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x2d8-0x2df + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x2e0-0x2e7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x2e8-0x2ef + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x2f0-0x2f7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x2f8-0x2ff + + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x300-0x307 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x308-0x30f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x310-0x317 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x318-0x31f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x320-0x327 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x328-0x32f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x330-0x337 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x338-0x33f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x340-0x347 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x348-0x34f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x350-0x357 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x358-0x35f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x360-0x367 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x368-0x36f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x370-0x377 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x378-0x37f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x380-0x387 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x388-0x38f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x390-0x397 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x398-0x39f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x3a0-0x3a7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x3a8-0x3af + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x3b0-0x3b7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x3b8-0x3bf + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x3c0-0x3c7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x3c8-0x3cf + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x3d0-0x3d7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x3d8-0x3df + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x3e0-0x3e7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x3e8-0x3ef + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x3f0-0x3f7 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x3f8-0x3ff + + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x400-0x407 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x408-0x40f + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x410-0x417 + (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) (local i64) ;; 0x418-0x41f + + ;; recurse first to try to make the callee access the stack below the space allocated for the locals before the locals themselves have been initialized. + (call $function-with-many-locals) + + ;; load from memory into the locals + (local.set 0x000 (i64.load offset=0x000 align=1 (i32.const 0))) + (local.set 0x001 (i64.load offset=0x001 align=1 (i32.const 0))) + (local.set 0x002 (i64.load offset=0x002 align=1 (i32.const 0))) + (local.set 0x003 (i64.load offset=0x003 align=1 (i32.const 0))) + (local.set 0x004 (i64.load offset=0x004 align=1 (i32.const 0))) + (local.set 0x005 (i64.load offset=0x005 align=1 (i32.const 0))) + (local.set 0x006 (i64.load offset=0x006 align=1 (i32.const 0))) + (local.set 0x007 (i64.load offset=0x007 align=1 (i32.const 0))) + (local.set 0x008 (i64.load offset=0x008 align=1 (i32.const 0))) + (local.set 0x009 (i64.load offset=0x009 align=1 (i32.const 0))) + (local.set 0x00a (i64.load offset=0x00a align=1 (i32.const 0))) + (local.set 0x00b (i64.load offset=0x00b align=1 (i32.const 0))) + (local.set 0x00c (i64.load offset=0x00c align=1 (i32.const 0))) + (local.set 0x00d (i64.load offset=0x00d align=1 (i32.const 0))) + (local.set 0x00e (i64.load offset=0x00e align=1 (i32.const 0))) + (local.set 0x00f (i64.load offset=0x00f align=1 (i32.const 0))) + (local.set 0x010 (i64.load offset=0x010 align=1 (i32.const 0))) + (local.set 0x011 (i64.load offset=0x011 align=1 (i32.const 0))) + (local.set 0x012 (i64.load offset=0x012 align=1 (i32.const 0))) + (local.set 0x013 (i64.load offset=0x013 align=1 (i32.const 0))) + (local.set 0x014 (i64.load offset=0x014 align=1 (i32.const 0))) + (local.set 0x015 (i64.load offset=0x015 align=1 (i32.const 0))) + (local.set 0x016 (i64.load offset=0x016 align=1 (i32.const 0))) + (local.set 0x017 (i64.load offset=0x017 align=1 (i32.const 0))) + (local.set 0x018 (i64.load offset=0x018 align=1 (i32.const 0))) + (local.set 0x019 (i64.load offset=0x019 align=1 (i32.const 0))) + (local.set 0x01a (i64.load offset=0x01a align=1 (i32.const 0))) + (local.set 0x01b (i64.load offset=0x01b align=1 (i32.const 0))) + (local.set 0x01c (i64.load offset=0x01c align=1 (i32.const 0))) + (local.set 0x01d (i64.load offset=0x01d align=1 (i32.const 0))) + (local.set 0x01e (i64.load offset=0x01e align=1 (i32.const 0))) + (local.set 0x01f (i64.load offset=0x01f align=1 (i32.const 0))) + (local.set 0x020 (i64.load offset=0x020 align=1 (i32.const 0))) + (local.set 0x021 (i64.load offset=0x021 align=1 (i32.const 0))) + (local.set 0x022 (i64.load offset=0x022 align=1 (i32.const 0))) + (local.set 0x023 (i64.load offset=0x023 align=1 (i32.const 0))) + (local.set 0x024 (i64.load offset=0x024 align=1 (i32.const 0))) + (local.set 0x025 (i64.load offset=0x025 align=1 (i32.const 0))) + (local.set 0x026 (i64.load offset=0x026 align=1 (i32.const 0))) + (local.set 0x027 (i64.load offset=0x027 align=1 (i32.const 0))) + (local.set 0x028 (i64.load offset=0x028 align=1 (i32.const 0))) + (local.set 0x029 (i64.load offset=0x029 align=1 (i32.const 0))) + (local.set 0x02a (i64.load offset=0x02a align=1 (i32.const 0))) + (local.set 0x02b (i64.load offset=0x02b align=1 (i32.const 0))) + (local.set 0x02c (i64.load offset=0x02c align=1 (i32.const 0))) + (local.set 0x02d (i64.load offset=0x02d align=1 (i32.const 0))) + (local.set 0x02e (i64.load offset=0x02e align=1 (i32.const 0))) + (local.set 0x02f (i64.load offset=0x02f align=1 (i32.const 0))) + (local.set 0x030 (i64.load offset=0x030 align=1 (i32.const 0))) + (local.set 0x031 (i64.load offset=0x031 align=1 (i32.const 0))) + (local.set 0x032 (i64.load offset=0x032 align=1 (i32.const 0))) + (local.set 0x033 (i64.load offset=0x033 align=1 (i32.const 0))) + (local.set 0x034 (i64.load offset=0x034 align=1 (i32.const 0))) + (local.set 0x035 (i64.load offset=0x035 align=1 (i32.const 0))) + (local.set 0x036 (i64.load offset=0x036 align=1 (i32.const 0))) + (local.set 0x037 (i64.load offset=0x037 align=1 (i32.const 0))) + (local.set 0x038 (i64.load offset=0x038 align=1 (i32.const 0))) + (local.set 0x039 (i64.load offset=0x039 align=1 (i32.const 0))) + (local.set 0x03a (i64.load offset=0x03a align=1 (i32.const 0))) + (local.set 0x03b (i64.load offset=0x03b align=1 (i32.const 0))) + (local.set 0x03c (i64.load offset=0x03c align=1 (i32.const 0))) + (local.set 0x03d (i64.load offset=0x03d align=1 (i32.const 0))) + (local.set 0x03e (i64.load offset=0x03e align=1 (i32.const 0))) + (local.set 0x03f (i64.load offset=0x03f align=1 (i32.const 0))) + (local.set 0x040 (i64.load offset=0x040 align=1 (i32.const 0))) + (local.set 0x041 (i64.load offset=0x041 align=1 (i32.const 0))) + (local.set 0x042 (i64.load offset=0x042 align=1 (i32.const 0))) + (local.set 0x043 (i64.load offset=0x043 align=1 (i32.const 0))) + (local.set 0x044 (i64.load offset=0x044 align=1 (i32.const 0))) + (local.set 0x045 (i64.load offset=0x045 align=1 (i32.const 0))) + (local.set 0x046 (i64.load offset=0x046 align=1 (i32.const 0))) + (local.set 0x047 (i64.load offset=0x047 align=1 (i32.const 0))) + (local.set 0x048 (i64.load offset=0x048 align=1 (i32.const 0))) + (local.set 0x049 (i64.load offset=0x049 align=1 (i32.const 0))) + (local.set 0x04a (i64.load offset=0x04a align=1 (i32.const 0))) + (local.set 0x04b (i64.load offset=0x04b align=1 (i32.const 0))) + (local.set 0x04c (i64.load offset=0x04c align=1 (i32.const 0))) + (local.set 0x04d (i64.load offset=0x04d align=1 (i32.const 0))) + (local.set 0x04e (i64.load offset=0x04e align=1 (i32.const 0))) + (local.set 0x04f (i64.load offset=0x04f align=1 (i32.const 0))) + (local.set 0x050 (i64.load offset=0x050 align=1 (i32.const 0))) + (local.set 0x051 (i64.load offset=0x051 align=1 (i32.const 0))) + (local.set 0x052 (i64.load offset=0x052 align=1 (i32.const 0))) + (local.set 0x053 (i64.load offset=0x053 align=1 (i32.const 0))) + (local.set 0x054 (i64.load offset=0x054 align=1 (i32.const 0))) + (local.set 0x055 (i64.load offset=0x055 align=1 (i32.const 0))) + (local.set 0x056 (i64.load offset=0x056 align=1 (i32.const 0))) + (local.set 0x057 (i64.load offset=0x057 align=1 (i32.const 0))) + (local.set 0x058 (i64.load offset=0x058 align=1 (i32.const 0))) + (local.set 0x059 (i64.load offset=0x059 align=1 (i32.const 0))) + (local.set 0x05a (i64.load offset=0x05a align=1 (i32.const 0))) + (local.set 0x05b (i64.load offset=0x05b align=1 (i32.const 0))) + (local.set 0x05c (i64.load offset=0x05c align=1 (i32.const 0))) + (local.set 0x05d (i64.load offset=0x05d align=1 (i32.const 0))) + (local.set 0x05e (i64.load offset=0x05e align=1 (i32.const 0))) + (local.set 0x05f (i64.load offset=0x05f align=1 (i32.const 0))) + (local.set 0x060 (i64.load offset=0x060 align=1 (i32.const 0))) + (local.set 0x061 (i64.load offset=0x061 align=1 (i32.const 0))) + (local.set 0x062 (i64.load offset=0x062 align=1 (i32.const 0))) + (local.set 0x063 (i64.load offset=0x063 align=1 (i32.const 0))) + (local.set 0x064 (i64.load offset=0x064 align=1 (i32.const 0))) + (local.set 0x065 (i64.load offset=0x065 align=1 (i32.const 0))) + (local.set 0x066 (i64.load offset=0x066 align=1 (i32.const 0))) + (local.set 0x067 (i64.load offset=0x067 align=1 (i32.const 0))) + (local.set 0x068 (i64.load offset=0x068 align=1 (i32.const 0))) + (local.set 0x069 (i64.load offset=0x069 align=1 (i32.const 0))) + (local.set 0x06a (i64.load offset=0x06a align=1 (i32.const 0))) + (local.set 0x06b (i64.load offset=0x06b align=1 (i32.const 0))) + (local.set 0x06c (i64.load offset=0x06c align=1 (i32.const 0))) + (local.set 0x06d (i64.load offset=0x06d align=1 (i32.const 0))) + (local.set 0x06e (i64.load offset=0x06e align=1 (i32.const 0))) + (local.set 0x06f (i64.load offset=0x06f align=1 (i32.const 0))) + (local.set 0x070 (i64.load offset=0x070 align=1 (i32.const 0))) + (local.set 0x071 (i64.load offset=0x071 align=1 (i32.const 0))) + (local.set 0x072 (i64.load offset=0x072 align=1 (i32.const 0))) + (local.set 0x073 (i64.load offset=0x073 align=1 (i32.const 0))) + (local.set 0x074 (i64.load offset=0x074 align=1 (i32.const 0))) + (local.set 0x075 (i64.load offset=0x075 align=1 (i32.const 0))) + (local.set 0x076 (i64.load offset=0x076 align=1 (i32.const 0))) + (local.set 0x077 (i64.load offset=0x077 align=1 (i32.const 0))) + (local.set 0x078 (i64.load offset=0x078 align=1 (i32.const 0))) + (local.set 0x079 (i64.load offset=0x079 align=1 (i32.const 0))) + (local.set 0x07a (i64.load offset=0x07a align=1 (i32.const 0))) + (local.set 0x07b (i64.load offset=0x07b align=1 (i32.const 0))) + (local.set 0x07c (i64.load offset=0x07c align=1 (i32.const 0))) + (local.set 0x07d (i64.load offset=0x07d align=1 (i32.const 0))) + (local.set 0x07e (i64.load offset=0x07e align=1 (i32.const 0))) + (local.set 0x07f (i64.load offset=0x07f align=1 (i32.const 0))) + (local.set 0x080 (i64.load offset=0x080 align=1 (i32.const 0))) + (local.set 0x081 (i64.load offset=0x081 align=1 (i32.const 0))) + (local.set 0x082 (i64.load offset=0x082 align=1 (i32.const 0))) + (local.set 0x083 (i64.load offset=0x083 align=1 (i32.const 0))) + (local.set 0x084 (i64.load offset=0x084 align=1 (i32.const 0))) + (local.set 0x085 (i64.load offset=0x085 align=1 (i32.const 0))) + (local.set 0x086 (i64.load offset=0x086 align=1 (i32.const 0))) + (local.set 0x087 (i64.load offset=0x087 align=1 (i32.const 0))) + (local.set 0x088 (i64.load offset=0x088 align=1 (i32.const 0))) + (local.set 0x089 (i64.load offset=0x089 align=1 (i32.const 0))) + (local.set 0x08a (i64.load offset=0x08a align=1 (i32.const 0))) + (local.set 0x08b (i64.load offset=0x08b align=1 (i32.const 0))) + (local.set 0x08c (i64.load offset=0x08c align=1 (i32.const 0))) + (local.set 0x08d (i64.load offset=0x08d align=1 (i32.const 0))) + (local.set 0x08e (i64.load offset=0x08e align=1 (i32.const 0))) + (local.set 0x08f (i64.load offset=0x08f align=1 (i32.const 0))) + (local.set 0x090 (i64.load offset=0x090 align=1 (i32.const 0))) + (local.set 0x091 (i64.load offset=0x091 align=1 (i32.const 0))) + (local.set 0x092 (i64.load offset=0x092 align=1 (i32.const 0))) + (local.set 0x093 (i64.load offset=0x093 align=1 (i32.const 0))) + (local.set 0x094 (i64.load offset=0x094 align=1 (i32.const 0))) + (local.set 0x095 (i64.load offset=0x095 align=1 (i32.const 0))) + (local.set 0x096 (i64.load offset=0x096 align=1 (i32.const 0))) + (local.set 0x097 (i64.load offset=0x097 align=1 (i32.const 0))) + (local.set 0x098 (i64.load offset=0x098 align=1 (i32.const 0))) + (local.set 0x099 (i64.load offset=0x099 align=1 (i32.const 0))) + (local.set 0x09a (i64.load offset=0x09a align=1 (i32.const 0))) + (local.set 0x09b (i64.load offset=0x09b align=1 (i32.const 0))) + (local.set 0x09c (i64.load offset=0x09c align=1 (i32.const 0))) + (local.set 0x09d (i64.load offset=0x09d align=1 (i32.const 0))) + (local.set 0x09e (i64.load offset=0x09e align=1 (i32.const 0))) + (local.set 0x09f (i64.load offset=0x09f align=1 (i32.const 0))) + (local.set 0x0a0 (i64.load offset=0x0a0 align=1 (i32.const 0))) + (local.set 0x0a1 (i64.load offset=0x0a1 align=1 (i32.const 0))) + (local.set 0x0a2 (i64.load offset=0x0a2 align=1 (i32.const 0))) + (local.set 0x0a3 (i64.load offset=0x0a3 align=1 (i32.const 0))) + (local.set 0x0a4 (i64.load offset=0x0a4 align=1 (i32.const 0))) + (local.set 0x0a5 (i64.load offset=0x0a5 align=1 (i32.const 0))) + (local.set 0x0a6 (i64.load offset=0x0a6 align=1 (i32.const 0))) + (local.set 0x0a7 (i64.load offset=0x0a7 align=1 (i32.const 0))) + (local.set 0x0a8 (i64.load offset=0x0a8 align=1 (i32.const 0))) + (local.set 0x0a9 (i64.load offset=0x0a9 align=1 (i32.const 0))) + (local.set 0x0aa (i64.load offset=0x0aa align=1 (i32.const 0))) + (local.set 0x0ab (i64.load offset=0x0ab align=1 (i32.const 0))) + (local.set 0x0ac (i64.load offset=0x0ac align=1 (i32.const 0))) + (local.set 0x0ad (i64.load offset=0x0ad align=1 (i32.const 0))) + (local.set 0x0ae (i64.load offset=0x0ae align=1 (i32.const 0))) + (local.set 0x0af (i64.load offset=0x0af align=1 (i32.const 0))) + (local.set 0x0b0 (i64.load offset=0x0b0 align=1 (i32.const 0))) + (local.set 0x0b1 (i64.load offset=0x0b1 align=1 (i32.const 0))) + (local.set 0x0b2 (i64.load offset=0x0b2 align=1 (i32.const 0))) + (local.set 0x0b3 (i64.load offset=0x0b3 align=1 (i32.const 0))) + (local.set 0x0b4 (i64.load offset=0x0b4 align=1 (i32.const 0))) + (local.set 0x0b5 (i64.load offset=0x0b5 align=1 (i32.const 0))) + (local.set 0x0b6 (i64.load offset=0x0b6 align=1 (i32.const 0))) + (local.set 0x0b7 (i64.load offset=0x0b7 align=1 (i32.const 0))) + (local.set 0x0b8 (i64.load offset=0x0b8 align=1 (i32.const 0))) + (local.set 0x0b9 (i64.load offset=0x0b9 align=1 (i32.const 0))) + (local.set 0x0ba (i64.load offset=0x0ba align=1 (i32.const 0))) + (local.set 0x0bb (i64.load offset=0x0bb align=1 (i32.const 0))) + (local.set 0x0bc (i64.load offset=0x0bc align=1 (i32.const 0))) + (local.set 0x0bd (i64.load offset=0x0bd align=1 (i32.const 0))) + (local.set 0x0be (i64.load offset=0x0be align=1 (i32.const 0))) + (local.set 0x0bf (i64.load offset=0x0bf align=1 (i32.const 0))) + (local.set 0x0c0 (i64.load offset=0x0c0 align=1 (i32.const 0))) + (local.set 0x0c1 (i64.load offset=0x0c1 align=1 (i32.const 0))) + (local.set 0x0c2 (i64.load offset=0x0c2 align=1 (i32.const 0))) + (local.set 0x0c3 (i64.load offset=0x0c3 align=1 (i32.const 0))) + (local.set 0x0c4 (i64.load offset=0x0c4 align=1 (i32.const 0))) + (local.set 0x0c5 (i64.load offset=0x0c5 align=1 (i32.const 0))) + (local.set 0x0c6 (i64.load offset=0x0c6 align=1 (i32.const 0))) + (local.set 0x0c7 (i64.load offset=0x0c7 align=1 (i32.const 0))) + (local.set 0x0c8 (i64.load offset=0x0c8 align=1 (i32.const 0))) + (local.set 0x0c9 (i64.load offset=0x0c9 align=1 (i32.const 0))) + (local.set 0x0ca (i64.load offset=0x0ca align=1 (i32.const 0))) + (local.set 0x0cb (i64.load offset=0x0cb align=1 (i32.const 0))) + (local.set 0x0cc (i64.load offset=0x0cc align=1 (i32.const 0))) + (local.set 0x0cd (i64.load offset=0x0cd align=1 (i32.const 0))) + (local.set 0x0ce (i64.load offset=0x0ce align=1 (i32.const 0))) + (local.set 0x0cf (i64.load offset=0x0cf align=1 (i32.const 0))) + (local.set 0x0d0 (i64.load offset=0x0d0 align=1 (i32.const 0))) + (local.set 0x0d1 (i64.load offset=0x0d1 align=1 (i32.const 0))) + (local.set 0x0d2 (i64.load offset=0x0d2 align=1 (i32.const 0))) + (local.set 0x0d3 (i64.load offset=0x0d3 align=1 (i32.const 0))) + (local.set 0x0d4 (i64.load offset=0x0d4 align=1 (i32.const 0))) + (local.set 0x0d5 (i64.load offset=0x0d5 align=1 (i32.const 0))) + (local.set 0x0d6 (i64.load offset=0x0d6 align=1 (i32.const 0))) + (local.set 0x0d7 (i64.load offset=0x0d7 align=1 (i32.const 0))) + (local.set 0x0d8 (i64.load offset=0x0d8 align=1 (i32.const 0))) + (local.set 0x0d9 (i64.load offset=0x0d9 align=1 (i32.const 0))) + (local.set 0x0da (i64.load offset=0x0da align=1 (i32.const 0))) + (local.set 0x0db (i64.load offset=0x0db align=1 (i32.const 0))) + (local.set 0x0dc (i64.load offset=0x0dc align=1 (i32.const 0))) + (local.set 0x0dd (i64.load offset=0x0dd align=1 (i32.const 0))) + (local.set 0x0de (i64.load offset=0x0de align=1 (i32.const 0))) + (local.set 0x0df (i64.load offset=0x0df align=1 (i32.const 0))) + (local.set 0x0e0 (i64.load offset=0x0e0 align=1 (i32.const 0))) + (local.set 0x0e1 (i64.load offset=0x0e1 align=1 (i32.const 0))) + (local.set 0x0e2 (i64.load offset=0x0e2 align=1 (i32.const 0))) + (local.set 0x0e3 (i64.load offset=0x0e3 align=1 (i32.const 0))) + (local.set 0x0e4 (i64.load offset=0x0e4 align=1 (i32.const 0))) + (local.set 0x0e5 (i64.load offset=0x0e5 align=1 (i32.const 0))) + (local.set 0x0e6 (i64.load offset=0x0e6 align=1 (i32.const 0))) + (local.set 0x0e7 (i64.load offset=0x0e7 align=1 (i32.const 0))) + (local.set 0x0e8 (i64.load offset=0x0e8 align=1 (i32.const 0))) + (local.set 0x0e9 (i64.load offset=0x0e9 align=1 (i32.const 0))) + (local.set 0x0ea (i64.load offset=0x0ea align=1 (i32.const 0))) + (local.set 0x0eb (i64.load offset=0x0eb align=1 (i32.const 0))) + (local.set 0x0ec (i64.load offset=0x0ec align=1 (i32.const 0))) + (local.set 0x0ed (i64.load offset=0x0ed align=1 (i32.const 0))) + (local.set 0x0ee (i64.load offset=0x0ee align=1 (i32.const 0))) + (local.set 0x0ef (i64.load offset=0x0ef align=1 (i32.const 0))) + (local.set 0x0f0 (i64.load offset=0x0f0 align=1 (i32.const 0))) + (local.set 0x0f1 (i64.load offset=0x0f1 align=1 (i32.const 0))) + (local.set 0x0f2 (i64.load offset=0x0f2 align=1 (i32.const 0))) + (local.set 0x0f3 (i64.load offset=0x0f3 align=1 (i32.const 0))) + (local.set 0x0f4 (i64.load offset=0x0f4 align=1 (i32.const 0))) + (local.set 0x0f5 (i64.load offset=0x0f5 align=1 (i32.const 0))) + (local.set 0x0f6 (i64.load offset=0x0f6 align=1 (i32.const 0))) + (local.set 0x0f7 (i64.load offset=0x0f7 align=1 (i32.const 0))) + (local.set 0x0f8 (i64.load offset=0x0f8 align=1 (i32.const 0))) + (local.set 0x0f9 (i64.load offset=0x0f9 align=1 (i32.const 0))) + (local.set 0x0fa (i64.load offset=0x0fa align=1 (i32.const 0))) + (local.set 0x0fb (i64.load offset=0x0fb align=1 (i32.const 0))) + (local.set 0x0fc (i64.load offset=0x0fc align=1 (i32.const 0))) + (local.set 0x0fd (i64.load offset=0x0fd align=1 (i32.const 0))) + (local.set 0x0fe (i64.load offset=0x0fe align=1 (i32.const 0))) + (local.set 0x0ff (i64.load offset=0x0ff align=1 (i32.const 0))) + (local.set 0x100 (i64.load offset=0x100 align=1 (i32.const 0))) + (local.set 0x101 (i64.load offset=0x101 align=1 (i32.const 0))) + (local.set 0x102 (i64.load offset=0x102 align=1 (i32.const 0))) + (local.set 0x103 (i64.load offset=0x103 align=1 (i32.const 0))) + (local.set 0x104 (i64.load offset=0x104 align=1 (i32.const 0))) + (local.set 0x105 (i64.load offset=0x105 align=1 (i32.const 0))) + (local.set 0x106 (i64.load offset=0x106 align=1 (i32.const 0))) + (local.set 0x107 (i64.load offset=0x107 align=1 (i32.const 0))) + (local.set 0x108 (i64.load offset=0x108 align=1 (i32.const 0))) + (local.set 0x109 (i64.load offset=0x109 align=1 (i32.const 0))) + (local.set 0x10a (i64.load offset=0x10a align=1 (i32.const 0))) + (local.set 0x10b (i64.load offset=0x10b align=1 (i32.const 0))) + (local.set 0x10c (i64.load offset=0x10c align=1 (i32.const 0))) + (local.set 0x10d (i64.load offset=0x10d align=1 (i32.const 0))) + (local.set 0x10e (i64.load offset=0x10e align=1 (i32.const 0))) + (local.set 0x10f (i64.load offset=0x10f align=1 (i32.const 0))) + (local.set 0x110 (i64.load offset=0x110 align=1 (i32.const 0))) + (local.set 0x111 (i64.load offset=0x111 align=1 (i32.const 0))) + (local.set 0x112 (i64.load offset=0x112 align=1 (i32.const 0))) + (local.set 0x113 (i64.load offset=0x113 align=1 (i32.const 0))) + (local.set 0x114 (i64.load offset=0x114 align=1 (i32.const 0))) + (local.set 0x115 (i64.load offset=0x115 align=1 (i32.const 0))) + (local.set 0x116 (i64.load offset=0x116 align=1 (i32.const 0))) + (local.set 0x117 (i64.load offset=0x117 align=1 (i32.const 0))) + (local.set 0x118 (i64.load offset=0x118 align=1 (i32.const 0))) + (local.set 0x119 (i64.load offset=0x119 align=1 (i32.const 0))) + (local.set 0x11a (i64.load offset=0x11a align=1 (i32.const 0))) + (local.set 0x11b (i64.load offset=0x11b align=1 (i32.const 0))) + (local.set 0x11c (i64.load offset=0x11c align=1 (i32.const 0))) + (local.set 0x11d (i64.load offset=0x11d align=1 (i32.const 0))) + (local.set 0x11e (i64.load offset=0x11e align=1 (i32.const 0))) + (local.set 0x11f (i64.load offset=0x11f align=1 (i32.const 0))) + (local.set 0x120 (i64.load offset=0x120 align=1 (i32.const 0))) + (local.set 0x121 (i64.load offset=0x121 align=1 (i32.const 0))) + (local.set 0x122 (i64.load offset=0x122 align=1 (i32.const 0))) + (local.set 0x123 (i64.load offset=0x123 align=1 (i32.const 0))) + (local.set 0x124 (i64.load offset=0x124 align=1 (i32.const 0))) + (local.set 0x125 (i64.load offset=0x125 align=1 (i32.const 0))) + (local.set 0x126 (i64.load offset=0x126 align=1 (i32.const 0))) + (local.set 0x127 (i64.load offset=0x127 align=1 (i32.const 0))) + (local.set 0x128 (i64.load offset=0x128 align=1 (i32.const 0))) + (local.set 0x129 (i64.load offset=0x129 align=1 (i32.const 0))) + (local.set 0x12a (i64.load offset=0x12a align=1 (i32.const 0))) + (local.set 0x12b (i64.load offset=0x12b align=1 (i32.const 0))) + (local.set 0x12c (i64.load offset=0x12c align=1 (i32.const 0))) + (local.set 0x12d (i64.load offset=0x12d align=1 (i32.const 0))) + (local.set 0x12e (i64.load offset=0x12e align=1 (i32.const 0))) + (local.set 0x12f (i64.load offset=0x12f align=1 (i32.const 0))) + (local.set 0x130 (i64.load offset=0x130 align=1 (i32.const 0))) + (local.set 0x131 (i64.load offset=0x131 align=1 (i32.const 0))) + (local.set 0x132 (i64.load offset=0x132 align=1 (i32.const 0))) + (local.set 0x133 (i64.load offset=0x133 align=1 (i32.const 0))) + (local.set 0x134 (i64.load offset=0x134 align=1 (i32.const 0))) + (local.set 0x135 (i64.load offset=0x135 align=1 (i32.const 0))) + (local.set 0x136 (i64.load offset=0x136 align=1 (i32.const 0))) + (local.set 0x137 (i64.load offset=0x137 align=1 (i32.const 0))) + (local.set 0x138 (i64.load offset=0x138 align=1 (i32.const 0))) + (local.set 0x139 (i64.load offset=0x139 align=1 (i32.const 0))) + (local.set 0x13a (i64.load offset=0x13a align=1 (i32.const 0))) + (local.set 0x13b (i64.load offset=0x13b align=1 (i32.const 0))) + (local.set 0x13c (i64.load offset=0x13c align=1 (i32.const 0))) + (local.set 0x13d (i64.load offset=0x13d align=1 (i32.const 0))) + (local.set 0x13e (i64.load offset=0x13e align=1 (i32.const 0))) + (local.set 0x13f (i64.load offset=0x13f align=1 (i32.const 0))) + (local.set 0x140 (i64.load offset=0x140 align=1 (i32.const 0))) + (local.set 0x141 (i64.load offset=0x141 align=1 (i32.const 0))) + (local.set 0x142 (i64.load offset=0x142 align=1 (i32.const 0))) + (local.set 0x143 (i64.load offset=0x143 align=1 (i32.const 0))) + (local.set 0x144 (i64.load offset=0x144 align=1 (i32.const 0))) + (local.set 0x145 (i64.load offset=0x145 align=1 (i32.const 0))) + (local.set 0x146 (i64.load offset=0x146 align=1 (i32.const 0))) + (local.set 0x147 (i64.load offset=0x147 align=1 (i32.const 0))) + (local.set 0x148 (i64.load offset=0x148 align=1 (i32.const 0))) + (local.set 0x149 (i64.load offset=0x149 align=1 (i32.const 0))) + (local.set 0x14a (i64.load offset=0x14a align=1 (i32.const 0))) + (local.set 0x14b (i64.load offset=0x14b align=1 (i32.const 0))) + (local.set 0x14c (i64.load offset=0x14c align=1 (i32.const 0))) + (local.set 0x14d (i64.load offset=0x14d align=1 (i32.const 0))) + (local.set 0x14e (i64.load offset=0x14e align=1 (i32.const 0))) + (local.set 0x14f (i64.load offset=0x14f align=1 (i32.const 0))) + (local.set 0x150 (i64.load offset=0x150 align=1 (i32.const 0))) + (local.set 0x151 (i64.load offset=0x151 align=1 (i32.const 0))) + (local.set 0x152 (i64.load offset=0x152 align=1 (i32.const 0))) + (local.set 0x153 (i64.load offset=0x153 align=1 (i32.const 0))) + (local.set 0x154 (i64.load offset=0x154 align=1 (i32.const 0))) + (local.set 0x155 (i64.load offset=0x155 align=1 (i32.const 0))) + (local.set 0x156 (i64.load offset=0x156 align=1 (i32.const 0))) + (local.set 0x157 (i64.load offset=0x157 align=1 (i32.const 0))) + (local.set 0x158 (i64.load offset=0x158 align=1 (i32.const 0))) + (local.set 0x159 (i64.load offset=0x159 align=1 (i32.const 0))) + (local.set 0x15a (i64.load offset=0x15a align=1 (i32.const 0))) + (local.set 0x15b (i64.load offset=0x15b align=1 (i32.const 0))) + (local.set 0x15c (i64.load offset=0x15c align=1 (i32.const 0))) + (local.set 0x15d (i64.load offset=0x15d align=1 (i32.const 0))) + (local.set 0x15e (i64.load offset=0x15e align=1 (i32.const 0))) + (local.set 0x15f (i64.load offset=0x15f align=1 (i32.const 0))) + (local.set 0x160 (i64.load offset=0x160 align=1 (i32.const 0))) + (local.set 0x161 (i64.load offset=0x161 align=1 (i32.const 0))) + (local.set 0x162 (i64.load offset=0x162 align=1 (i32.const 0))) + (local.set 0x163 (i64.load offset=0x163 align=1 (i32.const 0))) + (local.set 0x164 (i64.load offset=0x164 align=1 (i32.const 0))) + (local.set 0x165 (i64.load offset=0x165 align=1 (i32.const 0))) + (local.set 0x166 (i64.load offset=0x166 align=1 (i32.const 0))) + (local.set 0x167 (i64.load offset=0x167 align=1 (i32.const 0))) + (local.set 0x168 (i64.load offset=0x168 align=1 (i32.const 0))) + (local.set 0x169 (i64.load offset=0x169 align=1 (i32.const 0))) + (local.set 0x16a (i64.load offset=0x16a align=1 (i32.const 0))) + (local.set 0x16b (i64.load offset=0x16b align=1 (i32.const 0))) + (local.set 0x16c (i64.load offset=0x16c align=1 (i32.const 0))) + (local.set 0x16d (i64.load offset=0x16d align=1 (i32.const 0))) + (local.set 0x16e (i64.load offset=0x16e align=1 (i32.const 0))) + (local.set 0x16f (i64.load offset=0x16f align=1 (i32.const 0))) + (local.set 0x170 (i64.load offset=0x170 align=1 (i32.const 0))) + (local.set 0x171 (i64.load offset=0x171 align=1 (i32.const 0))) + (local.set 0x172 (i64.load offset=0x172 align=1 (i32.const 0))) + (local.set 0x173 (i64.load offset=0x173 align=1 (i32.const 0))) + (local.set 0x174 (i64.load offset=0x174 align=1 (i32.const 0))) + (local.set 0x175 (i64.load offset=0x175 align=1 (i32.const 0))) + (local.set 0x176 (i64.load offset=0x176 align=1 (i32.const 0))) + (local.set 0x177 (i64.load offset=0x177 align=1 (i32.const 0))) + (local.set 0x178 (i64.load offset=0x178 align=1 (i32.const 0))) + (local.set 0x179 (i64.load offset=0x179 align=1 (i32.const 0))) + (local.set 0x17a (i64.load offset=0x17a align=1 (i32.const 0))) + (local.set 0x17b (i64.load offset=0x17b align=1 (i32.const 0))) + (local.set 0x17c (i64.load offset=0x17c align=1 (i32.const 0))) + (local.set 0x17d (i64.load offset=0x17d align=1 (i32.const 0))) + (local.set 0x17e (i64.load offset=0x17e align=1 (i32.const 0))) + (local.set 0x17f (i64.load offset=0x17f align=1 (i32.const 0))) + (local.set 0x180 (i64.load offset=0x180 align=1 (i32.const 0))) + (local.set 0x181 (i64.load offset=0x181 align=1 (i32.const 0))) + (local.set 0x182 (i64.load offset=0x182 align=1 (i32.const 0))) + (local.set 0x183 (i64.load offset=0x183 align=1 (i32.const 0))) + (local.set 0x184 (i64.load offset=0x184 align=1 (i32.const 0))) + (local.set 0x185 (i64.load offset=0x185 align=1 (i32.const 0))) + (local.set 0x186 (i64.load offset=0x186 align=1 (i32.const 0))) + (local.set 0x187 (i64.load offset=0x187 align=1 (i32.const 0))) + (local.set 0x188 (i64.load offset=0x188 align=1 (i32.const 0))) + (local.set 0x189 (i64.load offset=0x189 align=1 (i32.const 0))) + (local.set 0x18a (i64.load offset=0x18a align=1 (i32.const 0))) + (local.set 0x18b (i64.load offset=0x18b align=1 (i32.const 0))) + (local.set 0x18c (i64.load offset=0x18c align=1 (i32.const 0))) + (local.set 0x18d (i64.load offset=0x18d align=1 (i32.const 0))) + (local.set 0x18e (i64.load offset=0x18e align=1 (i32.const 0))) + (local.set 0x18f (i64.load offset=0x18f align=1 (i32.const 0))) + (local.set 0x190 (i64.load offset=0x190 align=1 (i32.const 0))) + (local.set 0x191 (i64.load offset=0x191 align=1 (i32.const 0))) + (local.set 0x192 (i64.load offset=0x192 align=1 (i32.const 0))) + (local.set 0x193 (i64.load offset=0x193 align=1 (i32.const 0))) + (local.set 0x194 (i64.load offset=0x194 align=1 (i32.const 0))) + (local.set 0x195 (i64.load offset=0x195 align=1 (i32.const 0))) + (local.set 0x196 (i64.load offset=0x196 align=1 (i32.const 0))) + (local.set 0x197 (i64.load offset=0x197 align=1 (i32.const 0))) + (local.set 0x198 (i64.load offset=0x198 align=1 (i32.const 0))) + (local.set 0x199 (i64.load offset=0x199 align=1 (i32.const 0))) + (local.set 0x19a (i64.load offset=0x19a align=1 (i32.const 0))) + (local.set 0x19b (i64.load offset=0x19b align=1 (i32.const 0))) + (local.set 0x19c (i64.load offset=0x19c align=1 (i32.const 0))) + (local.set 0x19d (i64.load offset=0x19d align=1 (i32.const 0))) + (local.set 0x19e (i64.load offset=0x19e align=1 (i32.const 0))) + (local.set 0x19f (i64.load offset=0x19f align=1 (i32.const 0))) + (local.set 0x1a0 (i64.load offset=0x1a0 align=1 (i32.const 0))) + (local.set 0x1a1 (i64.load offset=0x1a1 align=1 (i32.const 0))) + (local.set 0x1a2 (i64.load offset=0x1a2 align=1 (i32.const 0))) + (local.set 0x1a3 (i64.load offset=0x1a3 align=1 (i32.const 0))) + (local.set 0x1a4 (i64.load offset=0x1a4 align=1 (i32.const 0))) + (local.set 0x1a5 (i64.load offset=0x1a5 align=1 (i32.const 0))) + (local.set 0x1a6 (i64.load offset=0x1a6 align=1 (i32.const 0))) + (local.set 0x1a7 (i64.load offset=0x1a7 align=1 (i32.const 0))) + (local.set 0x1a8 (i64.load offset=0x1a8 align=1 (i32.const 0))) + (local.set 0x1a9 (i64.load offset=0x1a9 align=1 (i32.const 0))) + (local.set 0x1aa (i64.load offset=0x1aa align=1 (i32.const 0))) + (local.set 0x1ab (i64.load offset=0x1ab align=1 (i32.const 0))) + (local.set 0x1ac (i64.load offset=0x1ac align=1 (i32.const 0))) + (local.set 0x1ad (i64.load offset=0x1ad align=1 (i32.const 0))) + (local.set 0x1ae (i64.load offset=0x1ae align=1 (i32.const 0))) + (local.set 0x1af (i64.load offset=0x1af align=1 (i32.const 0))) + (local.set 0x1b0 (i64.load offset=0x1b0 align=1 (i32.const 0))) + (local.set 0x1b1 (i64.load offset=0x1b1 align=1 (i32.const 0))) + (local.set 0x1b2 (i64.load offset=0x1b2 align=1 (i32.const 0))) + (local.set 0x1b3 (i64.load offset=0x1b3 align=1 (i32.const 0))) + (local.set 0x1b4 (i64.load offset=0x1b4 align=1 (i32.const 0))) + (local.set 0x1b5 (i64.load offset=0x1b5 align=1 (i32.const 0))) + (local.set 0x1b6 (i64.load offset=0x1b6 align=1 (i32.const 0))) + (local.set 0x1b7 (i64.load offset=0x1b7 align=1 (i32.const 0))) + (local.set 0x1b8 (i64.load offset=0x1b8 align=1 (i32.const 0))) + (local.set 0x1b9 (i64.load offset=0x1b9 align=1 (i32.const 0))) + (local.set 0x1ba (i64.load offset=0x1ba align=1 (i32.const 0))) + (local.set 0x1bb (i64.load offset=0x1bb align=1 (i32.const 0))) + (local.set 0x1bc (i64.load offset=0x1bc align=1 (i32.const 0))) + (local.set 0x1bd (i64.load offset=0x1bd align=1 (i32.const 0))) + (local.set 0x1be (i64.load offset=0x1be align=1 (i32.const 0))) + (local.set 0x1bf (i64.load offset=0x1bf align=1 (i32.const 0))) + (local.set 0x1c0 (i64.load offset=0x1c0 align=1 (i32.const 0))) + (local.set 0x1c1 (i64.load offset=0x1c1 align=1 (i32.const 0))) + (local.set 0x1c2 (i64.load offset=0x1c2 align=1 (i32.const 0))) + (local.set 0x1c3 (i64.load offset=0x1c3 align=1 (i32.const 0))) + (local.set 0x1c4 (i64.load offset=0x1c4 align=1 (i32.const 0))) + (local.set 0x1c5 (i64.load offset=0x1c5 align=1 (i32.const 0))) + (local.set 0x1c6 (i64.load offset=0x1c6 align=1 (i32.const 0))) + (local.set 0x1c7 (i64.load offset=0x1c7 align=1 (i32.const 0))) + (local.set 0x1c8 (i64.load offset=0x1c8 align=1 (i32.const 0))) + (local.set 0x1c9 (i64.load offset=0x1c9 align=1 (i32.const 0))) + (local.set 0x1ca (i64.load offset=0x1ca align=1 (i32.const 0))) + (local.set 0x1cb (i64.load offset=0x1cb align=1 (i32.const 0))) + (local.set 0x1cc (i64.load offset=0x1cc align=1 (i32.const 0))) + (local.set 0x1cd (i64.load offset=0x1cd align=1 (i32.const 0))) + (local.set 0x1ce (i64.load offset=0x1ce align=1 (i32.const 0))) + (local.set 0x1cf (i64.load offset=0x1cf align=1 (i32.const 0))) + (local.set 0x1d0 (i64.load offset=0x1d0 align=1 (i32.const 0))) + (local.set 0x1d1 (i64.load offset=0x1d1 align=1 (i32.const 0))) + (local.set 0x1d2 (i64.load offset=0x1d2 align=1 (i32.const 0))) + (local.set 0x1d3 (i64.load offset=0x1d3 align=1 (i32.const 0))) + (local.set 0x1d4 (i64.load offset=0x1d4 align=1 (i32.const 0))) + (local.set 0x1d5 (i64.load offset=0x1d5 align=1 (i32.const 0))) + (local.set 0x1d6 (i64.load offset=0x1d6 align=1 (i32.const 0))) + (local.set 0x1d7 (i64.load offset=0x1d7 align=1 (i32.const 0))) + (local.set 0x1d8 (i64.load offset=0x1d8 align=1 (i32.const 0))) + (local.set 0x1d9 (i64.load offset=0x1d9 align=1 (i32.const 0))) + (local.set 0x1da (i64.load offset=0x1da align=1 (i32.const 0))) + (local.set 0x1db (i64.load offset=0x1db align=1 (i32.const 0))) + (local.set 0x1dc (i64.load offset=0x1dc align=1 (i32.const 0))) + (local.set 0x1dd (i64.load offset=0x1dd align=1 (i32.const 0))) + (local.set 0x1de (i64.load offset=0x1de align=1 (i32.const 0))) + (local.set 0x1df (i64.load offset=0x1df align=1 (i32.const 0))) + (local.set 0x1e0 (i64.load offset=0x1e0 align=1 (i32.const 0))) + (local.set 0x1e1 (i64.load offset=0x1e1 align=1 (i32.const 0))) + (local.set 0x1e2 (i64.load offset=0x1e2 align=1 (i32.const 0))) + (local.set 0x1e3 (i64.load offset=0x1e3 align=1 (i32.const 0))) + (local.set 0x1e4 (i64.load offset=0x1e4 align=1 (i32.const 0))) + (local.set 0x1e5 (i64.load offset=0x1e5 align=1 (i32.const 0))) + (local.set 0x1e6 (i64.load offset=0x1e6 align=1 (i32.const 0))) + (local.set 0x1e7 (i64.load offset=0x1e7 align=1 (i32.const 0))) + (local.set 0x1e8 (i64.load offset=0x1e8 align=1 (i32.const 0))) + (local.set 0x1e9 (i64.load offset=0x1e9 align=1 (i32.const 0))) + (local.set 0x1ea (i64.load offset=0x1ea align=1 (i32.const 0))) + (local.set 0x1eb (i64.load offset=0x1eb align=1 (i32.const 0))) + (local.set 0x1ec (i64.load offset=0x1ec align=1 (i32.const 0))) + (local.set 0x1ed (i64.load offset=0x1ed align=1 (i32.const 0))) + (local.set 0x1ee (i64.load offset=0x1ee align=1 (i32.const 0))) + (local.set 0x1ef (i64.load offset=0x1ef align=1 (i32.const 0))) + (local.set 0x1f0 (i64.load offset=0x1f0 align=1 (i32.const 0))) + (local.set 0x1f1 (i64.load offset=0x1f1 align=1 (i32.const 0))) + (local.set 0x1f2 (i64.load offset=0x1f2 align=1 (i32.const 0))) + (local.set 0x1f3 (i64.load offset=0x1f3 align=1 (i32.const 0))) + (local.set 0x1f4 (i64.load offset=0x1f4 align=1 (i32.const 0))) + (local.set 0x1f5 (i64.load offset=0x1f5 align=1 (i32.const 0))) + (local.set 0x1f6 (i64.load offset=0x1f6 align=1 (i32.const 0))) + (local.set 0x1f7 (i64.load offset=0x1f7 align=1 (i32.const 0))) + (local.set 0x1f8 (i64.load offset=0x1f8 align=1 (i32.const 0))) + (local.set 0x1f9 (i64.load offset=0x1f9 align=1 (i32.const 0))) + (local.set 0x1fa (i64.load offset=0x1fa align=1 (i32.const 0))) + (local.set 0x1fb (i64.load offset=0x1fb align=1 (i32.const 0))) + (local.set 0x1fc (i64.load offset=0x1fc align=1 (i32.const 0))) + (local.set 0x1fd (i64.load offset=0x1fd align=1 (i32.const 0))) + (local.set 0x1fe (i64.load offset=0x1fe align=1 (i32.const 0))) + (local.set 0x1ff (i64.load offset=0x1ff align=1 (i32.const 0))) + (local.set 0x200 (i64.load offset=0x200 align=1 (i32.const 0))) + (local.set 0x201 (i64.load offset=0x201 align=1 (i32.const 0))) + (local.set 0x202 (i64.load offset=0x202 align=1 (i32.const 0))) + (local.set 0x203 (i64.load offset=0x203 align=1 (i32.const 0))) + (local.set 0x204 (i64.load offset=0x204 align=1 (i32.const 0))) + (local.set 0x205 (i64.load offset=0x205 align=1 (i32.const 0))) + (local.set 0x206 (i64.load offset=0x206 align=1 (i32.const 0))) + (local.set 0x207 (i64.load offset=0x207 align=1 (i32.const 0))) + (local.set 0x208 (i64.load offset=0x208 align=1 (i32.const 0))) + (local.set 0x209 (i64.load offset=0x209 align=1 (i32.const 0))) + (local.set 0x20a (i64.load offset=0x20a align=1 (i32.const 0))) + (local.set 0x20b (i64.load offset=0x20b align=1 (i32.const 0))) + (local.set 0x20c (i64.load offset=0x20c align=1 (i32.const 0))) + (local.set 0x20d (i64.load offset=0x20d align=1 (i32.const 0))) + (local.set 0x20e (i64.load offset=0x20e align=1 (i32.const 0))) + (local.set 0x20f (i64.load offset=0x20f align=1 (i32.const 0))) + (local.set 0x210 (i64.load offset=0x210 align=1 (i32.const 0))) + (local.set 0x211 (i64.load offset=0x211 align=1 (i32.const 0))) + (local.set 0x212 (i64.load offset=0x212 align=1 (i32.const 0))) + (local.set 0x213 (i64.load offset=0x213 align=1 (i32.const 0))) + (local.set 0x214 (i64.load offset=0x214 align=1 (i32.const 0))) + (local.set 0x215 (i64.load offset=0x215 align=1 (i32.const 0))) + (local.set 0x216 (i64.load offset=0x216 align=1 (i32.const 0))) + (local.set 0x217 (i64.load offset=0x217 align=1 (i32.const 0))) + (local.set 0x218 (i64.load offset=0x218 align=1 (i32.const 0))) + (local.set 0x219 (i64.load offset=0x219 align=1 (i32.const 0))) + (local.set 0x21a (i64.load offset=0x21a align=1 (i32.const 0))) + (local.set 0x21b (i64.load offset=0x21b align=1 (i32.const 0))) + (local.set 0x21c (i64.load offset=0x21c align=1 (i32.const 0))) + (local.set 0x21d (i64.load offset=0x21d align=1 (i32.const 0))) + (local.set 0x21e (i64.load offset=0x21e align=1 (i32.const 0))) + (local.set 0x21f (i64.load offset=0x21f align=1 (i32.const 0))) + (local.set 0x220 (i64.load offset=0x220 align=1 (i32.const 0))) + (local.set 0x221 (i64.load offset=0x221 align=1 (i32.const 0))) + (local.set 0x222 (i64.load offset=0x222 align=1 (i32.const 0))) + (local.set 0x223 (i64.load offset=0x223 align=1 (i32.const 0))) + (local.set 0x224 (i64.load offset=0x224 align=1 (i32.const 0))) + (local.set 0x225 (i64.load offset=0x225 align=1 (i32.const 0))) + (local.set 0x226 (i64.load offset=0x226 align=1 (i32.const 0))) + (local.set 0x227 (i64.load offset=0x227 align=1 (i32.const 0))) + (local.set 0x228 (i64.load offset=0x228 align=1 (i32.const 0))) + (local.set 0x229 (i64.load offset=0x229 align=1 (i32.const 0))) + (local.set 0x22a (i64.load offset=0x22a align=1 (i32.const 0))) + (local.set 0x22b (i64.load offset=0x22b align=1 (i32.const 0))) + (local.set 0x22c (i64.load offset=0x22c align=1 (i32.const 0))) + (local.set 0x22d (i64.load offset=0x22d align=1 (i32.const 0))) + (local.set 0x22e (i64.load offset=0x22e align=1 (i32.const 0))) + (local.set 0x22f (i64.load offset=0x22f align=1 (i32.const 0))) + (local.set 0x230 (i64.load offset=0x230 align=1 (i32.const 0))) + (local.set 0x231 (i64.load offset=0x231 align=1 (i32.const 0))) + (local.set 0x232 (i64.load offset=0x232 align=1 (i32.const 0))) + (local.set 0x233 (i64.load offset=0x233 align=1 (i32.const 0))) + (local.set 0x234 (i64.load offset=0x234 align=1 (i32.const 0))) + (local.set 0x235 (i64.load offset=0x235 align=1 (i32.const 0))) + (local.set 0x236 (i64.load offset=0x236 align=1 (i32.const 0))) + (local.set 0x237 (i64.load offset=0x237 align=1 (i32.const 0))) + (local.set 0x238 (i64.load offset=0x238 align=1 (i32.const 0))) + (local.set 0x239 (i64.load offset=0x239 align=1 (i32.const 0))) + (local.set 0x23a (i64.load offset=0x23a align=1 (i32.const 0))) + (local.set 0x23b (i64.load offset=0x23b align=1 (i32.const 0))) + (local.set 0x23c (i64.load offset=0x23c align=1 (i32.const 0))) + (local.set 0x23d (i64.load offset=0x23d align=1 (i32.const 0))) + (local.set 0x23e (i64.load offset=0x23e align=1 (i32.const 0))) + (local.set 0x23f (i64.load offset=0x23f align=1 (i32.const 0))) + (local.set 0x240 (i64.load offset=0x240 align=1 (i32.const 0))) + (local.set 0x241 (i64.load offset=0x241 align=1 (i32.const 0))) + (local.set 0x242 (i64.load offset=0x242 align=1 (i32.const 0))) + (local.set 0x243 (i64.load offset=0x243 align=1 (i32.const 0))) + (local.set 0x244 (i64.load offset=0x244 align=1 (i32.const 0))) + (local.set 0x245 (i64.load offset=0x245 align=1 (i32.const 0))) + (local.set 0x246 (i64.load offset=0x246 align=1 (i32.const 0))) + (local.set 0x247 (i64.load offset=0x247 align=1 (i32.const 0))) + (local.set 0x248 (i64.load offset=0x248 align=1 (i32.const 0))) + (local.set 0x249 (i64.load offset=0x249 align=1 (i32.const 0))) + (local.set 0x24a (i64.load offset=0x24a align=1 (i32.const 0))) + (local.set 0x24b (i64.load offset=0x24b align=1 (i32.const 0))) + (local.set 0x24c (i64.load offset=0x24c align=1 (i32.const 0))) + (local.set 0x24d (i64.load offset=0x24d align=1 (i32.const 0))) + (local.set 0x24e (i64.load offset=0x24e align=1 (i32.const 0))) + (local.set 0x24f (i64.load offset=0x24f align=1 (i32.const 0))) + (local.set 0x250 (i64.load offset=0x250 align=1 (i32.const 0))) + (local.set 0x251 (i64.load offset=0x251 align=1 (i32.const 0))) + (local.set 0x252 (i64.load offset=0x252 align=1 (i32.const 0))) + (local.set 0x253 (i64.load offset=0x253 align=1 (i32.const 0))) + (local.set 0x254 (i64.load offset=0x254 align=1 (i32.const 0))) + (local.set 0x255 (i64.load offset=0x255 align=1 (i32.const 0))) + (local.set 0x256 (i64.load offset=0x256 align=1 (i32.const 0))) + (local.set 0x257 (i64.load offset=0x257 align=1 (i32.const 0))) + (local.set 0x258 (i64.load offset=0x258 align=1 (i32.const 0))) + (local.set 0x259 (i64.load offset=0x259 align=1 (i32.const 0))) + (local.set 0x25a (i64.load offset=0x25a align=1 (i32.const 0))) + (local.set 0x25b (i64.load offset=0x25b align=1 (i32.const 0))) + (local.set 0x25c (i64.load offset=0x25c align=1 (i32.const 0))) + (local.set 0x25d (i64.load offset=0x25d align=1 (i32.const 0))) + (local.set 0x25e (i64.load offset=0x25e align=1 (i32.const 0))) + (local.set 0x25f (i64.load offset=0x25f align=1 (i32.const 0))) + (local.set 0x260 (i64.load offset=0x260 align=1 (i32.const 0))) + (local.set 0x261 (i64.load offset=0x261 align=1 (i32.const 0))) + (local.set 0x262 (i64.load offset=0x262 align=1 (i32.const 0))) + (local.set 0x263 (i64.load offset=0x263 align=1 (i32.const 0))) + (local.set 0x264 (i64.load offset=0x264 align=1 (i32.const 0))) + (local.set 0x265 (i64.load offset=0x265 align=1 (i32.const 0))) + (local.set 0x266 (i64.load offset=0x266 align=1 (i32.const 0))) + (local.set 0x267 (i64.load offset=0x267 align=1 (i32.const 0))) + (local.set 0x268 (i64.load offset=0x268 align=1 (i32.const 0))) + (local.set 0x269 (i64.load offset=0x269 align=1 (i32.const 0))) + (local.set 0x26a (i64.load offset=0x26a align=1 (i32.const 0))) + (local.set 0x26b (i64.load offset=0x26b align=1 (i32.const 0))) + (local.set 0x26c (i64.load offset=0x26c align=1 (i32.const 0))) + (local.set 0x26d (i64.load offset=0x26d align=1 (i32.const 0))) + (local.set 0x26e (i64.load offset=0x26e align=1 (i32.const 0))) + (local.set 0x26f (i64.load offset=0x26f align=1 (i32.const 0))) + (local.set 0x270 (i64.load offset=0x270 align=1 (i32.const 0))) + (local.set 0x271 (i64.load offset=0x271 align=1 (i32.const 0))) + (local.set 0x272 (i64.load offset=0x272 align=1 (i32.const 0))) + (local.set 0x273 (i64.load offset=0x273 align=1 (i32.const 0))) + (local.set 0x274 (i64.load offset=0x274 align=1 (i32.const 0))) + (local.set 0x275 (i64.load offset=0x275 align=1 (i32.const 0))) + (local.set 0x276 (i64.load offset=0x276 align=1 (i32.const 0))) + (local.set 0x277 (i64.load offset=0x277 align=1 (i32.const 0))) + (local.set 0x278 (i64.load offset=0x278 align=1 (i32.const 0))) + (local.set 0x279 (i64.load offset=0x279 align=1 (i32.const 0))) + (local.set 0x27a (i64.load offset=0x27a align=1 (i32.const 0))) + (local.set 0x27b (i64.load offset=0x27b align=1 (i32.const 0))) + (local.set 0x27c (i64.load offset=0x27c align=1 (i32.const 0))) + (local.set 0x27d (i64.load offset=0x27d align=1 (i32.const 0))) + (local.set 0x27e (i64.load offset=0x27e align=1 (i32.const 0))) + (local.set 0x27f (i64.load offset=0x27f align=1 (i32.const 0))) + (local.set 0x280 (i64.load offset=0x280 align=1 (i32.const 0))) + (local.set 0x281 (i64.load offset=0x281 align=1 (i32.const 0))) + (local.set 0x282 (i64.load offset=0x282 align=1 (i32.const 0))) + (local.set 0x283 (i64.load offset=0x283 align=1 (i32.const 0))) + (local.set 0x284 (i64.load offset=0x284 align=1 (i32.const 0))) + (local.set 0x285 (i64.load offset=0x285 align=1 (i32.const 0))) + (local.set 0x286 (i64.load offset=0x286 align=1 (i32.const 0))) + (local.set 0x287 (i64.load offset=0x287 align=1 (i32.const 0))) + (local.set 0x288 (i64.load offset=0x288 align=1 (i32.const 0))) + (local.set 0x289 (i64.load offset=0x289 align=1 (i32.const 0))) + (local.set 0x28a (i64.load offset=0x28a align=1 (i32.const 0))) + (local.set 0x28b (i64.load offset=0x28b align=1 (i32.const 0))) + (local.set 0x28c (i64.load offset=0x28c align=1 (i32.const 0))) + (local.set 0x28d (i64.load offset=0x28d align=1 (i32.const 0))) + (local.set 0x28e (i64.load offset=0x28e align=1 (i32.const 0))) + (local.set 0x28f (i64.load offset=0x28f align=1 (i32.const 0))) + (local.set 0x290 (i64.load offset=0x290 align=1 (i32.const 0))) + (local.set 0x291 (i64.load offset=0x291 align=1 (i32.const 0))) + (local.set 0x292 (i64.load offset=0x292 align=1 (i32.const 0))) + (local.set 0x293 (i64.load offset=0x293 align=1 (i32.const 0))) + (local.set 0x294 (i64.load offset=0x294 align=1 (i32.const 0))) + (local.set 0x295 (i64.load offset=0x295 align=1 (i32.const 0))) + (local.set 0x296 (i64.load offset=0x296 align=1 (i32.const 0))) + (local.set 0x297 (i64.load offset=0x297 align=1 (i32.const 0))) + (local.set 0x298 (i64.load offset=0x298 align=1 (i32.const 0))) + (local.set 0x299 (i64.load offset=0x299 align=1 (i32.const 0))) + (local.set 0x29a (i64.load offset=0x29a align=1 (i32.const 0))) + (local.set 0x29b (i64.load offset=0x29b align=1 (i32.const 0))) + (local.set 0x29c (i64.load offset=0x29c align=1 (i32.const 0))) + (local.set 0x29d (i64.load offset=0x29d align=1 (i32.const 0))) + (local.set 0x29e (i64.load offset=0x29e align=1 (i32.const 0))) + (local.set 0x29f (i64.load offset=0x29f align=1 (i32.const 0))) + (local.set 0x2a0 (i64.load offset=0x2a0 align=1 (i32.const 0))) + (local.set 0x2a1 (i64.load offset=0x2a1 align=1 (i32.const 0))) + (local.set 0x2a2 (i64.load offset=0x2a2 align=1 (i32.const 0))) + (local.set 0x2a3 (i64.load offset=0x2a3 align=1 (i32.const 0))) + (local.set 0x2a4 (i64.load offset=0x2a4 align=1 (i32.const 0))) + (local.set 0x2a5 (i64.load offset=0x2a5 align=1 (i32.const 0))) + (local.set 0x2a6 (i64.load offset=0x2a6 align=1 (i32.const 0))) + (local.set 0x2a7 (i64.load offset=0x2a7 align=1 (i32.const 0))) + (local.set 0x2a8 (i64.load offset=0x2a8 align=1 (i32.const 0))) + (local.set 0x2a9 (i64.load offset=0x2a9 align=1 (i32.const 0))) + (local.set 0x2aa (i64.load offset=0x2aa align=1 (i32.const 0))) + (local.set 0x2ab (i64.load offset=0x2ab align=1 (i32.const 0))) + (local.set 0x2ac (i64.load offset=0x2ac align=1 (i32.const 0))) + (local.set 0x2ad (i64.load offset=0x2ad align=1 (i32.const 0))) + (local.set 0x2ae (i64.load offset=0x2ae align=1 (i32.const 0))) + (local.set 0x2af (i64.load offset=0x2af align=1 (i32.const 0))) + (local.set 0x2b0 (i64.load offset=0x2b0 align=1 (i32.const 0))) + (local.set 0x2b1 (i64.load offset=0x2b1 align=1 (i32.const 0))) + (local.set 0x2b2 (i64.load offset=0x2b2 align=1 (i32.const 0))) + (local.set 0x2b3 (i64.load offset=0x2b3 align=1 (i32.const 0))) + (local.set 0x2b4 (i64.load offset=0x2b4 align=1 (i32.const 0))) + (local.set 0x2b5 (i64.load offset=0x2b5 align=1 (i32.const 0))) + (local.set 0x2b6 (i64.load offset=0x2b6 align=1 (i32.const 0))) + (local.set 0x2b7 (i64.load offset=0x2b7 align=1 (i32.const 0))) + (local.set 0x2b8 (i64.load offset=0x2b8 align=1 (i32.const 0))) + (local.set 0x2b9 (i64.load offset=0x2b9 align=1 (i32.const 0))) + (local.set 0x2ba (i64.load offset=0x2ba align=1 (i32.const 0))) + (local.set 0x2bb (i64.load offset=0x2bb align=1 (i32.const 0))) + (local.set 0x2bc (i64.load offset=0x2bc align=1 (i32.const 0))) + (local.set 0x2bd (i64.load offset=0x2bd align=1 (i32.const 0))) + (local.set 0x2be (i64.load offset=0x2be align=1 (i32.const 0))) + (local.set 0x2bf (i64.load offset=0x2bf align=1 (i32.const 0))) + (local.set 0x2c0 (i64.load offset=0x2c0 align=1 (i32.const 0))) + (local.set 0x2c1 (i64.load offset=0x2c1 align=1 (i32.const 0))) + (local.set 0x2c2 (i64.load offset=0x2c2 align=1 (i32.const 0))) + (local.set 0x2c3 (i64.load offset=0x2c3 align=1 (i32.const 0))) + (local.set 0x2c4 (i64.load offset=0x2c4 align=1 (i32.const 0))) + (local.set 0x2c5 (i64.load offset=0x2c5 align=1 (i32.const 0))) + (local.set 0x2c6 (i64.load offset=0x2c6 align=1 (i32.const 0))) + (local.set 0x2c7 (i64.load offset=0x2c7 align=1 (i32.const 0))) + (local.set 0x2c8 (i64.load offset=0x2c8 align=1 (i32.const 0))) + (local.set 0x2c9 (i64.load offset=0x2c9 align=1 (i32.const 0))) + (local.set 0x2ca (i64.load offset=0x2ca align=1 (i32.const 0))) + (local.set 0x2cb (i64.load offset=0x2cb align=1 (i32.const 0))) + (local.set 0x2cc (i64.load offset=0x2cc align=1 (i32.const 0))) + (local.set 0x2cd (i64.load offset=0x2cd align=1 (i32.const 0))) + (local.set 0x2ce (i64.load offset=0x2ce align=1 (i32.const 0))) + (local.set 0x2cf (i64.load offset=0x2cf align=1 (i32.const 0))) + (local.set 0x2d0 (i64.load offset=0x2d0 align=1 (i32.const 0))) + (local.set 0x2d1 (i64.load offset=0x2d1 align=1 (i32.const 0))) + (local.set 0x2d2 (i64.load offset=0x2d2 align=1 (i32.const 0))) + (local.set 0x2d3 (i64.load offset=0x2d3 align=1 (i32.const 0))) + (local.set 0x2d4 (i64.load offset=0x2d4 align=1 (i32.const 0))) + (local.set 0x2d5 (i64.load offset=0x2d5 align=1 (i32.const 0))) + (local.set 0x2d6 (i64.load offset=0x2d6 align=1 (i32.const 0))) + (local.set 0x2d7 (i64.load offset=0x2d7 align=1 (i32.const 0))) + (local.set 0x2d8 (i64.load offset=0x2d8 align=1 (i32.const 0))) + (local.set 0x2d9 (i64.load offset=0x2d9 align=1 (i32.const 0))) + (local.set 0x2da (i64.load offset=0x2da align=1 (i32.const 0))) + (local.set 0x2db (i64.load offset=0x2db align=1 (i32.const 0))) + (local.set 0x2dc (i64.load offset=0x2dc align=1 (i32.const 0))) + (local.set 0x2dd (i64.load offset=0x2dd align=1 (i32.const 0))) + (local.set 0x2de (i64.load offset=0x2de align=1 (i32.const 0))) + (local.set 0x2df (i64.load offset=0x2df align=1 (i32.const 0))) + (local.set 0x2e0 (i64.load offset=0x2e0 align=1 (i32.const 0))) + (local.set 0x2e1 (i64.load offset=0x2e1 align=1 (i32.const 0))) + (local.set 0x2e2 (i64.load offset=0x2e2 align=1 (i32.const 0))) + (local.set 0x2e3 (i64.load offset=0x2e3 align=1 (i32.const 0))) + (local.set 0x2e4 (i64.load offset=0x2e4 align=1 (i32.const 0))) + (local.set 0x2e5 (i64.load offset=0x2e5 align=1 (i32.const 0))) + (local.set 0x2e6 (i64.load offset=0x2e6 align=1 (i32.const 0))) + (local.set 0x2e7 (i64.load offset=0x2e7 align=1 (i32.const 0))) + (local.set 0x2e8 (i64.load offset=0x2e8 align=1 (i32.const 0))) + (local.set 0x2e9 (i64.load offset=0x2e9 align=1 (i32.const 0))) + (local.set 0x2ea (i64.load offset=0x2ea align=1 (i32.const 0))) + (local.set 0x2eb (i64.load offset=0x2eb align=1 (i32.const 0))) + (local.set 0x2ec (i64.load offset=0x2ec align=1 (i32.const 0))) + (local.set 0x2ed (i64.load offset=0x2ed align=1 (i32.const 0))) + (local.set 0x2ee (i64.load offset=0x2ee align=1 (i32.const 0))) + (local.set 0x2ef (i64.load offset=0x2ef align=1 (i32.const 0))) + (local.set 0x2f0 (i64.load offset=0x2f0 align=1 (i32.const 0))) + (local.set 0x2f1 (i64.load offset=0x2f1 align=1 (i32.const 0))) + (local.set 0x2f2 (i64.load offset=0x2f2 align=1 (i32.const 0))) + (local.set 0x2f3 (i64.load offset=0x2f3 align=1 (i32.const 0))) + (local.set 0x2f4 (i64.load offset=0x2f4 align=1 (i32.const 0))) + (local.set 0x2f5 (i64.load offset=0x2f5 align=1 (i32.const 0))) + (local.set 0x2f6 (i64.load offset=0x2f6 align=1 (i32.const 0))) + (local.set 0x2f7 (i64.load offset=0x2f7 align=1 (i32.const 0))) + (local.set 0x2f8 (i64.load offset=0x2f8 align=1 (i32.const 0))) + (local.set 0x2f9 (i64.load offset=0x2f9 align=1 (i32.const 0))) + (local.set 0x2fa (i64.load offset=0x2fa align=1 (i32.const 0))) + (local.set 0x2fb (i64.load offset=0x2fb align=1 (i32.const 0))) + (local.set 0x2fc (i64.load offset=0x2fc align=1 (i32.const 0))) + (local.set 0x2fd (i64.load offset=0x2fd align=1 (i32.const 0))) + (local.set 0x2fe (i64.load offset=0x2fe align=1 (i32.const 0))) + (local.set 0x2ff (i64.load offset=0x2ff align=1 (i32.const 0))) + (local.set 0x300 (i64.load offset=0x300 align=1 (i32.const 0))) + (local.set 0x301 (i64.load offset=0x301 align=1 (i32.const 0))) + (local.set 0x302 (i64.load offset=0x302 align=1 (i32.const 0))) + (local.set 0x303 (i64.load offset=0x303 align=1 (i32.const 0))) + (local.set 0x304 (i64.load offset=0x304 align=1 (i32.const 0))) + (local.set 0x305 (i64.load offset=0x305 align=1 (i32.const 0))) + (local.set 0x306 (i64.load offset=0x306 align=1 (i32.const 0))) + (local.set 0x307 (i64.load offset=0x307 align=1 (i32.const 0))) + (local.set 0x308 (i64.load offset=0x308 align=1 (i32.const 0))) + (local.set 0x309 (i64.load offset=0x309 align=1 (i32.const 0))) + (local.set 0x30a (i64.load offset=0x30a align=1 (i32.const 0))) + (local.set 0x30b (i64.load offset=0x30b align=1 (i32.const 0))) + (local.set 0x30c (i64.load offset=0x30c align=1 (i32.const 0))) + (local.set 0x30d (i64.load offset=0x30d align=1 (i32.const 0))) + (local.set 0x30e (i64.load offset=0x30e align=1 (i32.const 0))) + (local.set 0x30f (i64.load offset=0x30f align=1 (i32.const 0))) + (local.set 0x310 (i64.load offset=0x310 align=1 (i32.const 0))) + (local.set 0x311 (i64.load offset=0x311 align=1 (i32.const 0))) + (local.set 0x312 (i64.load offset=0x312 align=1 (i32.const 0))) + (local.set 0x313 (i64.load offset=0x313 align=1 (i32.const 0))) + (local.set 0x314 (i64.load offset=0x314 align=1 (i32.const 0))) + (local.set 0x315 (i64.load offset=0x315 align=1 (i32.const 0))) + (local.set 0x316 (i64.load offset=0x316 align=1 (i32.const 0))) + (local.set 0x317 (i64.load offset=0x317 align=1 (i32.const 0))) + (local.set 0x318 (i64.load offset=0x318 align=1 (i32.const 0))) + (local.set 0x319 (i64.load offset=0x319 align=1 (i32.const 0))) + (local.set 0x31a (i64.load offset=0x31a align=1 (i32.const 0))) + (local.set 0x31b (i64.load offset=0x31b align=1 (i32.const 0))) + (local.set 0x31c (i64.load offset=0x31c align=1 (i32.const 0))) + (local.set 0x31d (i64.load offset=0x31d align=1 (i32.const 0))) + (local.set 0x31e (i64.load offset=0x31e align=1 (i32.const 0))) + (local.set 0x31f (i64.load offset=0x31f align=1 (i32.const 0))) + (local.set 0x320 (i64.load offset=0x320 align=1 (i32.const 0))) + (local.set 0x321 (i64.load offset=0x321 align=1 (i32.const 0))) + (local.set 0x322 (i64.load offset=0x322 align=1 (i32.const 0))) + (local.set 0x323 (i64.load offset=0x323 align=1 (i32.const 0))) + (local.set 0x324 (i64.load offset=0x324 align=1 (i32.const 0))) + (local.set 0x325 (i64.load offset=0x325 align=1 (i32.const 0))) + (local.set 0x326 (i64.load offset=0x326 align=1 (i32.const 0))) + (local.set 0x327 (i64.load offset=0x327 align=1 (i32.const 0))) + (local.set 0x328 (i64.load offset=0x328 align=1 (i32.const 0))) + (local.set 0x329 (i64.load offset=0x329 align=1 (i32.const 0))) + (local.set 0x32a (i64.load offset=0x32a align=1 (i32.const 0))) + (local.set 0x32b (i64.load offset=0x32b align=1 (i32.const 0))) + (local.set 0x32c (i64.load offset=0x32c align=1 (i32.const 0))) + (local.set 0x32d (i64.load offset=0x32d align=1 (i32.const 0))) + (local.set 0x32e (i64.load offset=0x32e align=1 (i32.const 0))) + (local.set 0x32f (i64.load offset=0x32f align=1 (i32.const 0))) + (local.set 0x330 (i64.load offset=0x330 align=1 (i32.const 0))) + (local.set 0x331 (i64.load offset=0x331 align=1 (i32.const 0))) + (local.set 0x332 (i64.load offset=0x332 align=1 (i32.const 0))) + (local.set 0x333 (i64.load offset=0x333 align=1 (i32.const 0))) + (local.set 0x334 (i64.load offset=0x334 align=1 (i32.const 0))) + (local.set 0x335 (i64.load offset=0x335 align=1 (i32.const 0))) + (local.set 0x336 (i64.load offset=0x336 align=1 (i32.const 0))) + (local.set 0x337 (i64.load offset=0x337 align=1 (i32.const 0))) + (local.set 0x338 (i64.load offset=0x338 align=1 (i32.const 0))) + (local.set 0x339 (i64.load offset=0x339 align=1 (i32.const 0))) + (local.set 0x33a (i64.load offset=0x33a align=1 (i32.const 0))) + (local.set 0x33b (i64.load offset=0x33b align=1 (i32.const 0))) + (local.set 0x33c (i64.load offset=0x33c align=1 (i32.const 0))) + (local.set 0x33d (i64.load offset=0x33d align=1 (i32.const 0))) + (local.set 0x33e (i64.load offset=0x33e align=1 (i32.const 0))) + (local.set 0x33f (i64.load offset=0x33f align=1 (i32.const 0))) + (local.set 0x340 (i64.load offset=0x340 align=1 (i32.const 0))) + (local.set 0x341 (i64.load offset=0x341 align=1 (i32.const 0))) + (local.set 0x342 (i64.load offset=0x342 align=1 (i32.const 0))) + (local.set 0x343 (i64.load offset=0x343 align=1 (i32.const 0))) + (local.set 0x344 (i64.load offset=0x344 align=1 (i32.const 0))) + (local.set 0x345 (i64.load offset=0x345 align=1 (i32.const 0))) + (local.set 0x346 (i64.load offset=0x346 align=1 (i32.const 0))) + (local.set 0x347 (i64.load offset=0x347 align=1 (i32.const 0))) + (local.set 0x348 (i64.load offset=0x348 align=1 (i32.const 0))) + (local.set 0x349 (i64.load offset=0x349 align=1 (i32.const 0))) + (local.set 0x34a (i64.load offset=0x34a align=1 (i32.const 0))) + (local.set 0x34b (i64.load offset=0x34b align=1 (i32.const 0))) + (local.set 0x34c (i64.load offset=0x34c align=1 (i32.const 0))) + (local.set 0x34d (i64.load offset=0x34d align=1 (i32.const 0))) + (local.set 0x34e (i64.load offset=0x34e align=1 (i32.const 0))) + (local.set 0x34f (i64.load offset=0x34f align=1 (i32.const 0))) + (local.set 0x350 (i64.load offset=0x350 align=1 (i32.const 0))) + (local.set 0x351 (i64.load offset=0x351 align=1 (i32.const 0))) + (local.set 0x352 (i64.load offset=0x352 align=1 (i32.const 0))) + (local.set 0x353 (i64.load offset=0x353 align=1 (i32.const 0))) + (local.set 0x354 (i64.load offset=0x354 align=1 (i32.const 0))) + (local.set 0x355 (i64.load offset=0x355 align=1 (i32.const 0))) + (local.set 0x356 (i64.load offset=0x356 align=1 (i32.const 0))) + (local.set 0x357 (i64.load offset=0x357 align=1 (i32.const 0))) + (local.set 0x358 (i64.load offset=0x358 align=1 (i32.const 0))) + (local.set 0x359 (i64.load offset=0x359 align=1 (i32.const 0))) + (local.set 0x35a (i64.load offset=0x35a align=1 (i32.const 0))) + (local.set 0x35b (i64.load offset=0x35b align=1 (i32.const 0))) + (local.set 0x35c (i64.load offset=0x35c align=1 (i32.const 0))) + (local.set 0x35d (i64.load offset=0x35d align=1 (i32.const 0))) + (local.set 0x35e (i64.load offset=0x35e align=1 (i32.const 0))) + (local.set 0x35f (i64.load offset=0x35f align=1 (i32.const 0))) + (local.set 0x360 (i64.load offset=0x360 align=1 (i32.const 0))) + (local.set 0x361 (i64.load offset=0x361 align=1 (i32.const 0))) + (local.set 0x362 (i64.load offset=0x362 align=1 (i32.const 0))) + (local.set 0x363 (i64.load offset=0x363 align=1 (i32.const 0))) + (local.set 0x364 (i64.load offset=0x364 align=1 (i32.const 0))) + (local.set 0x365 (i64.load offset=0x365 align=1 (i32.const 0))) + (local.set 0x366 (i64.load offset=0x366 align=1 (i32.const 0))) + (local.set 0x367 (i64.load offset=0x367 align=1 (i32.const 0))) + (local.set 0x368 (i64.load offset=0x368 align=1 (i32.const 0))) + (local.set 0x369 (i64.load offset=0x369 align=1 (i32.const 0))) + (local.set 0x36a (i64.load offset=0x36a align=1 (i32.const 0))) + (local.set 0x36b (i64.load offset=0x36b align=1 (i32.const 0))) + (local.set 0x36c (i64.load offset=0x36c align=1 (i32.const 0))) + (local.set 0x36d (i64.load offset=0x36d align=1 (i32.const 0))) + (local.set 0x36e (i64.load offset=0x36e align=1 (i32.const 0))) + (local.set 0x36f (i64.load offset=0x36f align=1 (i32.const 0))) + (local.set 0x370 (i64.load offset=0x370 align=1 (i32.const 0))) + (local.set 0x371 (i64.load offset=0x371 align=1 (i32.const 0))) + (local.set 0x372 (i64.load offset=0x372 align=1 (i32.const 0))) + (local.set 0x373 (i64.load offset=0x373 align=1 (i32.const 0))) + (local.set 0x374 (i64.load offset=0x374 align=1 (i32.const 0))) + (local.set 0x375 (i64.load offset=0x375 align=1 (i32.const 0))) + (local.set 0x376 (i64.load offset=0x376 align=1 (i32.const 0))) + (local.set 0x377 (i64.load offset=0x377 align=1 (i32.const 0))) + (local.set 0x378 (i64.load offset=0x378 align=1 (i32.const 0))) + (local.set 0x379 (i64.load offset=0x379 align=1 (i32.const 0))) + (local.set 0x37a (i64.load offset=0x37a align=1 (i32.const 0))) + (local.set 0x37b (i64.load offset=0x37b align=1 (i32.const 0))) + (local.set 0x37c (i64.load offset=0x37c align=1 (i32.const 0))) + (local.set 0x37d (i64.load offset=0x37d align=1 (i32.const 0))) + (local.set 0x37e (i64.load offset=0x37e align=1 (i32.const 0))) + (local.set 0x37f (i64.load offset=0x37f align=1 (i32.const 0))) + (local.set 0x380 (i64.load offset=0x380 align=1 (i32.const 0))) + (local.set 0x381 (i64.load offset=0x381 align=1 (i32.const 0))) + (local.set 0x382 (i64.load offset=0x382 align=1 (i32.const 0))) + (local.set 0x383 (i64.load offset=0x383 align=1 (i32.const 0))) + (local.set 0x384 (i64.load offset=0x384 align=1 (i32.const 0))) + (local.set 0x385 (i64.load offset=0x385 align=1 (i32.const 0))) + (local.set 0x386 (i64.load offset=0x386 align=1 (i32.const 0))) + (local.set 0x387 (i64.load offset=0x387 align=1 (i32.const 0))) + (local.set 0x388 (i64.load offset=0x388 align=1 (i32.const 0))) + (local.set 0x389 (i64.load offset=0x389 align=1 (i32.const 0))) + (local.set 0x38a (i64.load offset=0x38a align=1 (i32.const 0))) + (local.set 0x38b (i64.load offset=0x38b align=1 (i32.const 0))) + (local.set 0x38c (i64.load offset=0x38c align=1 (i32.const 0))) + (local.set 0x38d (i64.load offset=0x38d align=1 (i32.const 0))) + (local.set 0x38e (i64.load offset=0x38e align=1 (i32.const 0))) + (local.set 0x38f (i64.load offset=0x38f align=1 (i32.const 0))) + (local.set 0x390 (i64.load offset=0x390 align=1 (i32.const 0))) + (local.set 0x391 (i64.load offset=0x391 align=1 (i32.const 0))) + (local.set 0x392 (i64.load offset=0x392 align=1 (i32.const 0))) + (local.set 0x393 (i64.load offset=0x393 align=1 (i32.const 0))) + (local.set 0x394 (i64.load offset=0x394 align=1 (i32.const 0))) + (local.set 0x395 (i64.load offset=0x395 align=1 (i32.const 0))) + (local.set 0x396 (i64.load offset=0x396 align=1 (i32.const 0))) + (local.set 0x397 (i64.load offset=0x397 align=1 (i32.const 0))) + (local.set 0x398 (i64.load offset=0x398 align=1 (i32.const 0))) + (local.set 0x399 (i64.load offset=0x399 align=1 (i32.const 0))) + (local.set 0x39a (i64.load offset=0x39a align=1 (i32.const 0))) + (local.set 0x39b (i64.load offset=0x39b align=1 (i32.const 0))) + (local.set 0x39c (i64.load offset=0x39c align=1 (i32.const 0))) + (local.set 0x39d (i64.load offset=0x39d align=1 (i32.const 0))) + (local.set 0x39e (i64.load offset=0x39e align=1 (i32.const 0))) + (local.set 0x39f (i64.load offset=0x39f align=1 (i32.const 0))) + (local.set 0x3a0 (i64.load offset=0x3a0 align=1 (i32.const 0))) + (local.set 0x3a1 (i64.load offset=0x3a1 align=1 (i32.const 0))) + (local.set 0x3a2 (i64.load offset=0x3a2 align=1 (i32.const 0))) + (local.set 0x3a3 (i64.load offset=0x3a3 align=1 (i32.const 0))) + (local.set 0x3a4 (i64.load offset=0x3a4 align=1 (i32.const 0))) + (local.set 0x3a5 (i64.load offset=0x3a5 align=1 (i32.const 0))) + (local.set 0x3a6 (i64.load offset=0x3a6 align=1 (i32.const 0))) + (local.set 0x3a7 (i64.load offset=0x3a7 align=1 (i32.const 0))) + (local.set 0x3a8 (i64.load offset=0x3a8 align=1 (i32.const 0))) + (local.set 0x3a9 (i64.load offset=0x3a9 align=1 (i32.const 0))) + (local.set 0x3aa (i64.load offset=0x3aa align=1 (i32.const 0))) + (local.set 0x3ab (i64.load offset=0x3ab align=1 (i32.const 0))) + (local.set 0x3ac (i64.load offset=0x3ac align=1 (i32.const 0))) + (local.set 0x3ad (i64.load offset=0x3ad align=1 (i32.const 0))) + (local.set 0x3ae (i64.load offset=0x3ae align=1 (i32.const 0))) + (local.set 0x3af (i64.load offset=0x3af align=1 (i32.const 0))) + (local.set 0x3b0 (i64.load offset=0x3b0 align=1 (i32.const 0))) + (local.set 0x3b1 (i64.load offset=0x3b1 align=1 (i32.const 0))) + (local.set 0x3b2 (i64.load offset=0x3b2 align=1 (i32.const 0))) + (local.set 0x3b3 (i64.load offset=0x3b3 align=1 (i32.const 0))) + (local.set 0x3b4 (i64.load offset=0x3b4 align=1 (i32.const 0))) + (local.set 0x3b5 (i64.load offset=0x3b5 align=1 (i32.const 0))) + (local.set 0x3b6 (i64.load offset=0x3b6 align=1 (i32.const 0))) + (local.set 0x3b7 (i64.load offset=0x3b7 align=1 (i32.const 0))) + (local.set 0x3b8 (i64.load offset=0x3b8 align=1 (i32.const 0))) + (local.set 0x3b9 (i64.load offset=0x3b9 align=1 (i32.const 0))) + (local.set 0x3ba (i64.load offset=0x3ba align=1 (i32.const 0))) + (local.set 0x3bb (i64.load offset=0x3bb align=1 (i32.const 0))) + (local.set 0x3bc (i64.load offset=0x3bc align=1 (i32.const 0))) + (local.set 0x3bd (i64.load offset=0x3bd align=1 (i32.const 0))) + (local.set 0x3be (i64.load offset=0x3be align=1 (i32.const 0))) + (local.set 0x3bf (i64.load offset=0x3bf align=1 (i32.const 0))) + (local.set 0x3c0 (i64.load offset=0x3c0 align=1 (i32.const 0))) + (local.set 0x3c1 (i64.load offset=0x3c1 align=1 (i32.const 0))) + (local.set 0x3c2 (i64.load offset=0x3c2 align=1 (i32.const 0))) + (local.set 0x3c3 (i64.load offset=0x3c3 align=1 (i32.const 0))) + (local.set 0x3c4 (i64.load offset=0x3c4 align=1 (i32.const 0))) + (local.set 0x3c5 (i64.load offset=0x3c5 align=1 (i32.const 0))) + (local.set 0x3c6 (i64.load offset=0x3c6 align=1 (i32.const 0))) + (local.set 0x3c7 (i64.load offset=0x3c7 align=1 (i32.const 0))) + (local.set 0x3c8 (i64.load offset=0x3c8 align=1 (i32.const 0))) + (local.set 0x3c9 (i64.load offset=0x3c9 align=1 (i32.const 0))) + (local.set 0x3ca (i64.load offset=0x3ca align=1 (i32.const 0))) + (local.set 0x3cb (i64.load offset=0x3cb align=1 (i32.const 0))) + (local.set 0x3cc (i64.load offset=0x3cc align=1 (i32.const 0))) + (local.set 0x3cd (i64.load offset=0x3cd align=1 (i32.const 0))) + (local.set 0x3ce (i64.load offset=0x3ce align=1 (i32.const 0))) + (local.set 0x3cf (i64.load offset=0x3cf align=1 (i32.const 0))) + (local.set 0x3d0 (i64.load offset=0x3d0 align=1 (i32.const 0))) + (local.set 0x3d1 (i64.load offset=0x3d1 align=1 (i32.const 0))) + (local.set 0x3d2 (i64.load offset=0x3d2 align=1 (i32.const 0))) + (local.set 0x3d3 (i64.load offset=0x3d3 align=1 (i32.const 0))) + (local.set 0x3d4 (i64.load offset=0x3d4 align=1 (i32.const 0))) + (local.set 0x3d5 (i64.load offset=0x3d5 align=1 (i32.const 0))) + (local.set 0x3d6 (i64.load offset=0x3d6 align=1 (i32.const 0))) + (local.set 0x3d7 (i64.load offset=0x3d7 align=1 (i32.const 0))) + (local.set 0x3d8 (i64.load offset=0x3d8 align=1 (i32.const 0))) + (local.set 0x3d9 (i64.load offset=0x3d9 align=1 (i32.const 0))) + (local.set 0x3da (i64.load offset=0x3da align=1 (i32.const 0))) + (local.set 0x3db (i64.load offset=0x3db align=1 (i32.const 0))) + (local.set 0x3dc (i64.load offset=0x3dc align=1 (i32.const 0))) + (local.set 0x3dd (i64.load offset=0x3dd align=1 (i32.const 0))) + (local.set 0x3de (i64.load offset=0x3de align=1 (i32.const 0))) + (local.set 0x3df (i64.load offset=0x3df align=1 (i32.const 0))) + (local.set 0x3e0 (i64.load offset=0x3e0 align=1 (i32.const 0))) + (local.set 0x3e1 (i64.load offset=0x3e1 align=1 (i32.const 0))) + (local.set 0x3e2 (i64.load offset=0x3e2 align=1 (i32.const 0))) + (local.set 0x3e3 (i64.load offset=0x3e3 align=1 (i32.const 0))) + (local.set 0x3e4 (i64.load offset=0x3e4 align=1 (i32.const 0))) + (local.set 0x3e5 (i64.load offset=0x3e5 align=1 (i32.const 0))) + (local.set 0x3e6 (i64.load offset=0x3e6 align=1 (i32.const 0))) + (local.set 0x3e7 (i64.load offset=0x3e7 align=1 (i32.const 0))) + (local.set 0x3e8 (i64.load offset=0x3e8 align=1 (i32.const 0))) + (local.set 0x3e9 (i64.load offset=0x3e9 align=1 (i32.const 0))) + (local.set 0x3ea (i64.load offset=0x3ea align=1 (i32.const 0))) + (local.set 0x3eb (i64.load offset=0x3eb align=1 (i32.const 0))) + (local.set 0x3ec (i64.load offset=0x3ec align=1 (i32.const 0))) + (local.set 0x3ed (i64.load offset=0x3ed align=1 (i32.const 0))) + (local.set 0x3ee (i64.load offset=0x3ee align=1 (i32.const 0))) + (local.set 0x3ef (i64.load offset=0x3ef align=1 (i32.const 0))) + (local.set 0x3f0 (i64.load offset=0x3f0 align=1 (i32.const 0))) + (local.set 0x3f1 (i64.load offset=0x3f1 align=1 (i32.const 0))) + (local.set 0x3f2 (i64.load offset=0x3f2 align=1 (i32.const 0))) + (local.set 0x3f3 (i64.load offset=0x3f3 align=1 (i32.const 0))) + (local.set 0x3f4 (i64.load offset=0x3f4 align=1 (i32.const 0))) + (local.set 0x3f5 (i64.load offset=0x3f5 align=1 (i32.const 0))) + (local.set 0x3f6 (i64.load offset=0x3f6 align=1 (i32.const 0))) + (local.set 0x3f7 (i64.load offset=0x3f7 align=1 (i32.const 0))) + (local.set 0x3f8 (i64.load offset=0x3f8 align=1 (i32.const 0))) + (local.set 0x3f9 (i64.load offset=0x3f9 align=1 (i32.const 0))) + (local.set 0x3fa (i64.load offset=0x3fa align=1 (i32.const 0))) + (local.set 0x3fb (i64.load offset=0x3fb align=1 (i32.const 0))) + (local.set 0x3fc (i64.load offset=0x3fc align=1 (i32.const 0))) + (local.set 0x3fd (i64.load offset=0x3fd align=1 (i32.const 0))) + (local.set 0x3fe (i64.load offset=0x3fe align=1 (i32.const 0))) + (local.set 0x3ff (i64.load offset=0x3ff align=1 (i32.const 0))) + (local.set 0x400 (i64.load offset=0x400 align=1 (i32.const 0))) + (local.set 0x401 (i64.load offset=0x401 align=1 (i32.const 0))) + (local.set 0x402 (i64.load offset=0x402 align=1 (i32.const 0))) + (local.set 0x403 (i64.load offset=0x403 align=1 (i32.const 0))) + (local.set 0x404 (i64.load offset=0x404 align=1 (i32.const 0))) + (local.set 0x405 (i64.load offset=0x405 align=1 (i32.const 0))) + (local.set 0x406 (i64.load offset=0x406 align=1 (i32.const 0))) + (local.set 0x407 (i64.load offset=0x407 align=1 (i32.const 0))) + (local.set 0x408 (i64.load offset=0x408 align=1 (i32.const 0))) + (local.set 0x409 (i64.load offset=0x409 align=1 (i32.const 0))) + (local.set 0x40a (i64.load offset=0x40a align=1 (i32.const 0))) + (local.set 0x40b (i64.load offset=0x40b align=1 (i32.const 0))) + (local.set 0x40c (i64.load offset=0x40c align=1 (i32.const 0))) + (local.set 0x40d (i64.load offset=0x40d align=1 (i32.const 0))) + (local.set 0x40e (i64.load offset=0x40e align=1 (i32.const 0))) + (local.set 0x40f (i64.load offset=0x40f align=1 (i32.const 0))) + (local.set 0x410 (i64.load offset=0x410 align=1 (i32.const 0))) + (local.set 0x411 (i64.load offset=0x411 align=1 (i32.const 0))) + (local.set 0x412 (i64.load offset=0x412 align=1 (i32.const 0))) + (local.set 0x413 (i64.load offset=0x413 align=1 (i32.const 0))) + (local.set 0x414 (i64.load offset=0x414 align=1 (i32.const 0))) + (local.set 0x415 (i64.load offset=0x415 align=1 (i32.const 0))) + (local.set 0x416 (i64.load offset=0x416 align=1 (i32.const 0))) + (local.set 0x417 (i64.load offset=0x417 align=1 (i32.const 0))) + (local.set 0x418 (i64.load offset=0x418 align=1 (i32.const 0))) + (local.set 0x419 (i64.load offset=0x419 align=1 (i32.const 0))) + (local.set 0x41a (i64.load offset=0x41a align=1 (i32.const 0))) + (local.set 0x41b (i64.load offset=0x41b align=1 (i32.const 0))) + (local.set 0x41c (i64.load offset=0x41c align=1 (i32.const 0))) + (local.set 0x41d (i64.load offset=0x41d align=1 (i32.const 0))) + (local.set 0x41e (i64.load offset=0x41e align=1 (i32.const 0))) + (local.set 0x41f (i64.load offset=0x41f align=1 (i32.const 0))) + + ;; store the locals back to memory + (i64.store offset=0x000 align=1 (i32.const 0) (local.get 0x000)) + (i64.store offset=0x001 align=1 (i32.const 0) (local.get 0x001)) + (i64.store offset=0x002 align=1 (i32.const 0) (local.get 0x002)) + (i64.store offset=0x003 align=1 (i32.const 0) (local.get 0x003)) + (i64.store offset=0x004 align=1 (i32.const 0) (local.get 0x004)) + (i64.store offset=0x005 align=1 (i32.const 0) (local.get 0x005)) + (i64.store offset=0x006 align=1 (i32.const 0) (local.get 0x006)) + (i64.store offset=0x007 align=1 (i32.const 0) (local.get 0x007)) + (i64.store offset=0x008 align=1 (i32.const 0) (local.get 0x008)) + (i64.store offset=0x009 align=1 (i32.const 0) (local.get 0x009)) + (i64.store offset=0x00a align=1 (i32.const 0) (local.get 0x00a)) + (i64.store offset=0x00b align=1 (i32.const 0) (local.get 0x00b)) + (i64.store offset=0x00c align=1 (i32.const 0) (local.get 0x00c)) + (i64.store offset=0x00d align=1 (i32.const 0) (local.get 0x00d)) + (i64.store offset=0x00e align=1 (i32.const 0) (local.get 0x00e)) + (i64.store offset=0x00f align=1 (i32.const 0) (local.get 0x00f)) + (i64.store offset=0x010 align=1 (i32.const 0) (local.get 0x010)) + (i64.store offset=0x011 align=1 (i32.const 0) (local.get 0x011)) + (i64.store offset=0x012 align=1 (i32.const 0) (local.get 0x012)) + (i64.store offset=0x013 align=1 (i32.const 0) (local.get 0x013)) + (i64.store offset=0x014 align=1 (i32.const 0) (local.get 0x014)) + (i64.store offset=0x015 align=1 (i32.const 0) (local.get 0x015)) + (i64.store offset=0x016 align=1 (i32.const 0) (local.get 0x016)) + (i64.store offset=0x017 align=1 (i32.const 0) (local.get 0x017)) + (i64.store offset=0x018 align=1 (i32.const 0) (local.get 0x018)) + (i64.store offset=0x019 align=1 (i32.const 0) (local.get 0x019)) + (i64.store offset=0x01a align=1 (i32.const 0) (local.get 0x01a)) + (i64.store offset=0x01b align=1 (i32.const 0) (local.get 0x01b)) + (i64.store offset=0x01c align=1 (i32.const 0) (local.get 0x01c)) + (i64.store offset=0x01d align=1 (i32.const 0) (local.get 0x01d)) + (i64.store offset=0x01e align=1 (i32.const 0) (local.get 0x01e)) + (i64.store offset=0x01f align=1 (i32.const 0) (local.get 0x01f)) + (i64.store offset=0x020 align=1 (i32.const 0) (local.get 0x020)) + (i64.store offset=0x021 align=1 (i32.const 0) (local.get 0x021)) + (i64.store offset=0x022 align=1 (i32.const 0) (local.get 0x022)) + (i64.store offset=0x023 align=1 (i32.const 0) (local.get 0x023)) + (i64.store offset=0x024 align=1 (i32.const 0) (local.get 0x024)) + (i64.store offset=0x025 align=1 (i32.const 0) (local.get 0x025)) + (i64.store offset=0x026 align=1 (i32.const 0) (local.get 0x026)) + (i64.store offset=0x027 align=1 (i32.const 0) (local.get 0x027)) + (i64.store offset=0x028 align=1 (i32.const 0) (local.get 0x028)) + (i64.store offset=0x029 align=1 (i32.const 0) (local.get 0x029)) + (i64.store offset=0x02a align=1 (i32.const 0) (local.get 0x02a)) + (i64.store offset=0x02b align=1 (i32.const 0) (local.get 0x02b)) + (i64.store offset=0x02c align=1 (i32.const 0) (local.get 0x02c)) + (i64.store offset=0x02d align=1 (i32.const 0) (local.get 0x02d)) + (i64.store offset=0x02e align=1 (i32.const 0) (local.get 0x02e)) + (i64.store offset=0x02f align=1 (i32.const 0) (local.get 0x02f)) + (i64.store offset=0x030 align=1 (i32.const 0) (local.get 0x030)) + (i64.store offset=0x031 align=1 (i32.const 0) (local.get 0x031)) + (i64.store offset=0x032 align=1 (i32.const 0) (local.get 0x032)) + (i64.store offset=0x033 align=1 (i32.const 0) (local.get 0x033)) + (i64.store offset=0x034 align=1 (i32.const 0) (local.get 0x034)) + (i64.store offset=0x035 align=1 (i32.const 0) (local.get 0x035)) + (i64.store offset=0x036 align=1 (i32.const 0) (local.get 0x036)) + (i64.store offset=0x037 align=1 (i32.const 0) (local.get 0x037)) + (i64.store offset=0x038 align=1 (i32.const 0) (local.get 0x038)) + (i64.store offset=0x039 align=1 (i32.const 0) (local.get 0x039)) + (i64.store offset=0x03a align=1 (i32.const 0) (local.get 0x03a)) + (i64.store offset=0x03b align=1 (i32.const 0) (local.get 0x03b)) + (i64.store offset=0x03c align=1 (i32.const 0) (local.get 0x03c)) + (i64.store offset=0x03d align=1 (i32.const 0) (local.get 0x03d)) + (i64.store offset=0x03e align=1 (i32.const 0) (local.get 0x03e)) + (i64.store offset=0x03f align=1 (i32.const 0) (local.get 0x03f)) + (i64.store offset=0x040 align=1 (i32.const 0) (local.get 0x040)) + (i64.store offset=0x041 align=1 (i32.const 0) (local.get 0x041)) + (i64.store offset=0x042 align=1 (i32.const 0) (local.get 0x042)) + (i64.store offset=0x043 align=1 (i32.const 0) (local.get 0x043)) + (i64.store offset=0x044 align=1 (i32.const 0) (local.get 0x044)) + (i64.store offset=0x045 align=1 (i32.const 0) (local.get 0x045)) + (i64.store offset=0x046 align=1 (i32.const 0) (local.get 0x046)) + (i64.store offset=0x047 align=1 (i32.const 0) (local.get 0x047)) + (i64.store offset=0x048 align=1 (i32.const 0) (local.get 0x048)) + (i64.store offset=0x049 align=1 (i32.const 0) (local.get 0x049)) + (i64.store offset=0x04a align=1 (i32.const 0) (local.get 0x04a)) + (i64.store offset=0x04b align=1 (i32.const 0) (local.get 0x04b)) + (i64.store offset=0x04c align=1 (i32.const 0) (local.get 0x04c)) + (i64.store offset=0x04d align=1 (i32.const 0) (local.get 0x04d)) + (i64.store offset=0x04e align=1 (i32.const 0) (local.get 0x04e)) + (i64.store offset=0x04f align=1 (i32.const 0) (local.get 0x04f)) + (i64.store offset=0x050 align=1 (i32.const 0) (local.get 0x050)) + (i64.store offset=0x051 align=1 (i32.const 0) (local.get 0x051)) + (i64.store offset=0x052 align=1 (i32.const 0) (local.get 0x052)) + (i64.store offset=0x053 align=1 (i32.const 0) (local.get 0x053)) + (i64.store offset=0x054 align=1 (i32.const 0) (local.get 0x054)) + (i64.store offset=0x055 align=1 (i32.const 0) (local.get 0x055)) + (i64.store offset=0x056 align=1 (i32.const 0) (local.get 0x056)) + (i64.store offset=0x057 align=1 (i32.const 0) (local.get 0x057)) + (i64.store offset=0x058 align=1 (i32.const 0) (local.get 0x058)) + (i64.store offset=0x059 align=1 (i32.const 0) (local.get 0x059)) + (i64.store offset=0x05a align=1 (i32.const 0) (local.get 0x05a)) + (i64.store offset=0x05b align=1 (i32.const 0) (local.get 0x05b)) + (i64.store offset=0x05c align=1 (i32.const 0) (local.get 0x05c)) + (i64.store offset=0x05d align=1 (i32.const 0) (local.get 0x05d)) + (i64.store offset=0x05e align=1 (i32.const 0) (local.get 0x05e)) + (i64.store offset=0x05f align=1 (i32.const 0) (local.get 0x05f)) + (i64.store offset=0x060 align=1 (i32.const 0) (local.get 0x060)) + (i64.store offset=0x061 align=1 (i32.const 0) (local.get 0x061)) + (i64.store offset=0x062 align=1 (i32.const 0) (local.get 0x062)) + (i64.store offset=0x063 align=1 (i32.const 0) (local.get 0x063)) + (i64.store offset=0x064 align=1 (i32.const 0) (local.get 0x064)) + (i64.store offset=0x065 align=1 (i32.const 0) (local.get 0x065)) + (i64.store offset=0x066 align=1 (i32.const 0) (local.get 0x066)) + (i64.store offset=0x067 align=1 (i32.const 0) (local.get 0x067)) + (i64.store offset=0x068 align=1 (i32.const 0) (local.get 0x068)) + (i64.store offset=0x069 align=1 (i32.const 0) (local.get 0x069)) + (i64.store offset=0x06a align=1 (i32.const 0) (local.get 0x06a)) + (i64.store offset=0x06b align=1 (i32.const 0) (local.get 0x06b)) + (i64.store offset=0x06c align=1 (i32.const 0) (local.get 0x06c)) + (i64.store offset=0x06d align=1 (i32.const 0) (local.get 0x06d)) + (i64.store offset=0x06e align=1 (i32.const 0) (local.get 0x06e)) + (i64.store offset=0x06f align=1 (i32.const 0) (local.get 0x06f)) + (i64.store offset=0x070 align=1 (i32.const 0) (local.get 0x070)) + (i64.store offset=0x071 align=1 (i32.const 0) (local.get 0x071)) + (i64.store offset=0x072 align=1 (i32.const 0) (local.get 0x072)) + (i64.store offset=0x073 align=1 (i32.const 0) (local.get 0x073)) + (i64.store offset=0x074 align=1 (i32.const 0) (local.get 0x074)) + (i64.store offset=0x075 align=1 (i32.const 0) (local.get 0x075)) + (i64.store offset=0x076 align=1 (i32.const 0) (local.get 0x076)) + (i64.store offset=0x077 align=1 (i32.const 0) (local.get 0x077)) + (i64.store offset=0x078 align=1 (i32.const 0) (local.get 0x078)) + (i64.store offset=0x079 align=1 (i32.const 0) (local.get 0x079)) + (i64.store offset=0x07a align=1 (i32.const 0) (local.get 0x07a)) + (i64.store offset=0x07b align=1 (i32.const 0) (local.get 0x07b)) + (i64.store offset=0x07c align=1 (i32.const 0) (local.get 0x07c)) + (i64.store offset=0x07d align=1 (i32.const 0) (local.get 0x07d)) + (i64.store offset=0x07e align=1 (i32.const 0) (local.get 0x07e)) + (i64.store offset=0x07f align=1 (i32.const 0) (local.get 0x07f)) + (i64.store offset=0x080 align=1 (i32.const 0) (local.get 0x080)) + (i64.store offset=0x081 align=1 (i32.const 0) (local.get 0x081)) + (i64.store offset=0x082 align=1 (i32.const 0) (local.get 0x082)) + (i64.store offset=0x083 align=1 (i32.const 0) (local.get 0x083)) + (i64.store offset=0x084 align=1 (i32.const 0) (local.get 0x084)) + (i64.store offset=0x085 align=1 (i32.const 0) (local.get 0x085)) + (i64.store offset=0x086 align=1 (i32.const 0) (local.get 0x086)) + (i64.store offset=0x087 align=1 (i32.const 0) (local.get 0x087)) + (i64.store offset=0x088 align=1 (i32.const 0) (local.get 0x088)) + (i64.store offset=0x089 align=1 (i32.const 0) (local.get 0x089)) + (i64.store offset=0x08a align=1 (i32.const 0) (local.get 0x08a)) + (i64.store offset=0x08b align=1 (i32.const 0) (local.get 0x08b)) + (i64.store offset=0x08c align=1 (i32.const 0) (local.get 0x08c)) + (i64.store offset=0x08d align=1 (i32.const 0) (local.get 0x08d)) + (i64.store offset=0x08e align=1 (i32.const 0) (local.get 0x08e)) + (i64.store offset=0x08f align=1 (i32.const 0) (local.get 0x08f)) + (i64.store offset=0x090 align=1 (i32.const 0) (local.get 0x090)) + (i64.store offset=0x091 align=1 (i32.const 0) (local.get 0x091)) + (i64.store offset=0x092 align=1 (i32.const 0) (local.get 0x092)) + (i64.store offset=0x093 align=1 (i32.const 0) (local.get 0x093)) + (i64.store offset=0x094 align=1 (i32.const 0) (local.get 0x094)) + (i64.store offset=0x095 align=1 (i32.const 0) (local.get 0x095)) + (i64.store offset=0x096 align=1 (i32.const 0) (local.get 0x096)) + (i64.store offset=0x097 align=1 (i32.const 0) (local.get 0x097)) + (i64.store offset=0x098 align=1 (i32.const 0) (local.get 0x098)) + (i64.store offset=0x099 align=1 (i32.const 0) (local.get 0x099)) + (i64.store offset=0x09a align=1 (i32.const 0) (local.get 0x09a)) + (i64.store offset=0x09b align=1 (i32.const 0) (local.get 0x09b)) + (i64.store offset=0x09c align=1 (i32.const 0) (local.get 0x09c)) + (i64.store offset=0x09d align=1 (i32.const 0) (local.get 0x09d)) + (i64.store offset=0x09e align=1 (i32.const 0) (local.get 0x09e)) + (i64.store offset=0x09f align=1 (i32.const 0) (local.get 0x09f)) + (i64.store offset=0x0a0 align=1 (i32.const 0) (local.get 0x0a0)) + (i64.store offset=0x0a1 align=1 (i32.const 0) (local.get 0x0a1)) + (i64.store offset=0x0a2 align=1 (i32.const 0) (local.get 0x0a2)) + (i64.store offset=0x0a3 align=1 (i32.const 0) (local.get 0x0a3)) + (i64.store offset=0x0a4 align=1 (i32.const 0) (local.get 0x0a4)) + (i64.store offset=0x0a5 align=1 (i32.const 0) (local.get 0x0a5)) + (i64.store offset=0x0a6 align=1 (i32.const 0) (local.get 0x0a6)) + (i64.store offset=0x0a7 align=1 (i32.const 0) (local.get 0x0a7)) + (i64.store offset=0x0a8 align=1 (i32.const 0) (local.get 0x0a8)) + (i64.store offset=0x0a9 align=1 (i32.const 0) (local.get 0x0a9)) + (i64.store offset=0x0aa align=1 (i32.const 0) (local.get 0x0aa)) + (i64.store offset=0x0ab align=1 (i32.const 0) (local.get 0x0ab)) + (i64.store offset=0x0ac align=1 (i32.const 0) (local.get 0x0ac)) + (i64.store offset=0x0ad align=1 (i32.const 0) (local.get 0x0ad)) + (i64.store offset=0x0ae align=1 (i32.const 0) (local.get 0x0ae)) + (i64.store offset=0x0af align=1 (i32.const 0) (local.get 0x0af)) + (i64.store offset=0x0b0 align=1 (i32.const 0) (local.get 0x0b0)) + (i64.store offset=0x0b1 align=1 (i32.const 0) (local.get 0x0b1)) + (i64.store offset=0x0b2 align=1 (i32.const 0) (local.get 0x0b2)) + (i64.store offset=0x0b3 align=1 (i32.const 0) (local.get 0x0b3)) + (i64.store offset=0x0b4 align=1 (i32.const 0) (local.get 0x0b4)) + (i64.store offset=0x0b5 align=1 (i32.const 0) (local.get 0x0b5)) + (i64.store offset=0x0b6 align=1 (i32.const 0) (local.get 0x0b6)) + (i64.store offset=0x0b7 align=1 (i32.const 0) (local.get 0x0b7)) + (i64.store offset=0x0b8 align=1 (i32.const 0) (local.get 0x0b8)) + (i64.store offset=0x0b9 align=1 (i32.const 0) (local.get 0x0b9)) + (i64.store offset=0x0ba align=1 (i32.const 0) (local.get 0x0ba)) + (i64.store offset=0x0bb align=1 (i32.const 0) (local.get 0x0bb)) + (i64.store offset=0x0bc align=1 (i32.const 0) (local.get 0x0bc)) + (i64.store offset=0x0bd align=1 (i32.const 0) (local.get 0x0bd)) + (i64.store offset=0x0be align=1 (i32.const 0) (local.get 0x0be)) + (i64.store offset=0x0bf align=1 (i32.const 0) (local.get 0x0bf)) + (i64.store offset=0x0c0 align=1 (i32.const 0) (local.get 0x0c0)) + (i64.store offset=0x0c1 align=1 (i32.const 0) (local.get 0x0c1)) + (i64.store offset=0x0c2 align=1 (i32.const 0) (local.get 0x0c2)) + (i64.store offset=0x0c3 align=1 (i32.const 0) (local.get 0x0c3)) + (i64.store offset=0x0c4 align=1 (i32.const 0) (local.get 0x0c4)) + (i64.store offset=0x0c5 align=1 (i32.const 0) (local.get 0x0c5)) + (i64.store offset=0x0c6 align=1 (i32.const 0) (local.get 0x0c6)) + (i64.store offset=0x0c7 align=1 (i32.const 0) (local.get 0x0c7)) + (i64.store offset=0x0c8 align=1 (i32.const 0) (local.get 0x0c8)) + (i64.store offset=0x0c9 align=1 (i32.const 0) (local.get 0x0c9)) + (i64.store offset=0x0ca align=1 (i32.const 0) (local.get 0x0ca)) + (i64.store offset=0x0cb align=1 (i32.const 0) (local.get 0x0cb)) + (i64.store offset=0x0cc align=1 (i32.const 0) (local.get 0x0cc)) + (i64.store offset=0x0cd align=1 (i32.const 0) (local.get 0x0cd)) + (i64.store offset=0x0ce align=1 (i32.const 0) (local.get 0x0ce)) + (i64.store offset=0x0cf align=1 (i32.const 0) (local.get 0x0cf)) + (i64.store offset=0x0d0 align=1 (i32.const 0) (local.get 0x0d0)) + (i64.store offset=0x0d1 align=1 (i32.const 0) (local.get 0x0d1)) + (i64.store offset=0x0d2 align=1 (i32.const 0) (local.get 0x0d2)) + (i64.store offset=0x0d3 align=1 (i32.const 0) (local.get 0x0d3)) + (i64.store offset=0x0d4 align=1 (i32.const 0) (local.get 0x0d4)) + (i64.store offset=0x0d5 align=1 (i32.const 0) (local.get 0x0d5)) + (i64.store offset=0x0d6 align=1 (i32.const 0) (local.get 0x0d6)) + (i64.store offset=0x0d7 align=1 (i32.const 0) (local.get 0x0d7)) + (i64.store offset=0x0d8 align=1 (i32.const 0) (local.get 0x0d8)) + (i64.store offset=0x0d9 align=1 (i32.const 0) (local.get 0x0d9)) + (i64.store offset=0x0da align=1 (i32.const 0) (local.get 0x0da)) + (i64.store offset=0x0db align=1 (i32.const 0) (local.get 0x0db)) + (i64.store offset=0x0dc align=1 (i32.const 0) (local.get 0x0dc)) + (i64.store offset=0x0dd align=1 (i32.const 0) (local.get 0x0dd)) + (i64.store offset=0x0de align=1 (i32.const 0) (local.get 0x0de)) + (i64.store offset=0x0df align=1 (i32.const 0) (local.get 0x0df)) + (i64.store offset=0x0e0 align=1 (i32.const 0) (local.get 0x0e0)) + (i64.store offset=0x0e1 align=1 (i32.const 0) (local.get 0x0e1)) + (i64.store offset=0x0e2 align=1 (i32.const 0) (local.get 0x0e2)) + (i64.store offset=0x0e3 align=1 (i32.const 0) (local.get 0x0e3)) + (i64.store offset=0x0e4 align=1 (i32.const 0) (local.get 0x0e4)) + (i64.store offset=0x0e5 align=1 (i32.const 0) (local.get 0x0e5)) + (i64.store offset=0x0e6 align=1 (i32.const 0) (local.get 0x0e6)) + (i64.store offset=0x0e7 align=1 (i32.const 0) (local.get 0x0e7)) + (i64.store offset=0x0e8 align=1 (i32.const 0) (local.get 0x0e8)) + (i64.store offset=0x0e9 align=1 (i32.const 0) (local.get 0x0e9)) + (i64.store offset=0x0ea align=1 (i32.const 0) (local.get 0x0ea)) + (i64.store offset=0x0eb align=1 (i32.const 0) (local.get 0x0eb)) + (i64.store offset=0x0ec align=1 (i32.const 0) (local.get 0x0ec)) + (i64.store offset=0x0ed align=1 (i32.const 0) (local.get 0x0ed)) + (i64.store offset=0x0ee align=1 (i32.const 0) (local.get 0x0ee)) + (i64.store offset=0x0ef align=1 (i32.const 0) (local.get 0x0ef)) + (i64.store offset=0x0f0 align=1 (i32.const 0) (local.get 0x0f0)) + (i64.store offset=0x0f1 align=1 (i32.const 0) (local.get 0x0f1)) + (i64.store offset=0x0f2 align=1 (i32.const 0) (local.get 0x0f2)) + (i64.store offset=0x0f3 align=1 (i32.const 0) (local.get 0x0f3)) + (i64.store offset=0x0f4 align=1 (i32.const 0) (local.get 0x0f4)) + (i64.store offset=0x0f5 align=1 (i32.const 0) (local.get 0x0f5)) + (i64.store offset=0x0f6 align=1 (i32.const 0) (local.get 0x0f6)) + (i64.store offset=0x0f7 align=1 (i32.const 0) (local.get 0x0f7)) + (i64.store offset=0x0f8 align=1 (i32.const 0) (local.get 0x0f8)) + (i64.store offset=0x0f9 align=1 (i32.const 0) (local.get 0x0f9)) + (i64.store offset=0x0fa align=1 (i32.const 0) (local.get 0x0fa)) + (i64.store offset=0x0fb align=1 (i32.const 0) (local.get 0x0fb)) + (i64.store offset=0x0fc align=1 (i32.const 0) (local.get 0x0fc)) + (i64.store offset=0x0fd align=1 (i32.const 0) (local.get 0x0fd)) + (i64.store offset=0x0fe align=1 (i32.const 0) (local.get 0x0fe)) + (i64.store offset=0x0ff align=1 (i32.const 0) (local.get 0x0ff)) + (i64.store offset=0x100 align=1 (i32.const 0) (local.get 0x100)) + (i64.store offset=0x101 align=1 (i32.const 0) (local.get 0x101)) + (i64.store offset=0x102 align=1 (i32.const 0) (local.get 0x102)) + (i64.store offset=0x103 align=1 (i32.const 0) (local.get 0x103)) + (i64.store offset=0x104 align=1 (i32.const 0) (local.get 0x104)) + (i64.store offset=0x105 align=1 (i32.const 0) (local.get 0x105)) + (i64.store offset=0x106 align=1 (i32.const 0) (local.get 0x106)) + (i64.store offset=0x107 align=1 (i32.const 0) (local.get 0x107)) + (i64.store offset=0x108 align=1 (i32.const 0) (local.get 0x108)) + (i64.store offset=0x109 align=1 (i32.const 0) (local.get 0x109)) + (i64.store offset=0x10a align=1 (i32.const 0) (local.get 0x10a)) + (i64.store offset=0x10b align=1 (i32.const 0) (local.get 0x10b)) + (i64.store offset=0x10c align=1 (i32.const 0) (local.get 0x10c)) + (i64.store offset=0x10d align=1 (i32.const 0) (local.get 0x10d)) + (i64.store offset=0x10e align=1 (i32.const 0) (local.get 0x10e)) + (i64.store offset=0x10f align=1 (i32.const 0) (local.get 0x10f)) + (i64.store offset=0x110 align=1 (i32.const 0) (local.get 0x110)) + (i64.store offset=0x111 align=1 (i32.const 0) (local.get 0x111)) + (i64.store offset=0x112 align=1 (i32.const 0) (local.get 0x112)) + (i64.store offset=0x113 align=1 (i32.const 0) (local.get 0x113)) + (i64.store offset=0x114 align=1 (i32.const 0) (local.get 0x114)) + (i64.store offset=0x115 align=1 (i32.const 0) (local.get 0x115)) + (i64.store offset=0x116 align=1 (i32.const 0) (local.get 0x116)) + (i64.store offset=0x117 align=1 (i32.const 0) (local.get 0x117)) + (i64.store offset=0x118 align=1 (i32.const 0) (local.get 0x118)) + (i64.store offset=0x119 align=1 (i32.const 0) (local.get 0x119)) + (i64.store offset=0x11a align=1 (i32.const 0) (local.get 0x11a)) + (i64.store offset=0x11b align=1 (i32.const 0) (local.get 0x11b)) + (i64.store offset=0x11c align=1 (i32.const 0) (local.get 0x11c)) + (i64.store offset=0x11d align=1 (i32.const 0) (local.get 0x11d)) + (i64.store offset=0x11e align=1 (i32.const 0) (local.get 0x11e)) + (i64.store offset=0x11f align=1 (i32.const 0) (local.get 0x11f)) + (i64.store offset=0x120 align=1 (i32.const 0) (local.get 0x120)) + (i64.store offset=0x121 align=1 (i32.const 0) (local.get 0x121)) + (i64.store offset=0x122 align=1 (i32.const 0) (local.get 0x122)) + (i64.store offset=0x123 align=1 (i32.const 0) (local.get 0x123)) + (i64.store offset=0x124 align=1 (i32.const 0) (local.get 0x124)) + (i64.store offset=0x125 align=1 (i32.const 0) (local.get 0x125)) + (i64.store offset=0x126 align=1 (i32.const 0) (local.get 0x126)) + (i64.store offset=0x127 align=1 (i32.const 0) (local.get 0x127)) + (i64.store offset=0x128 align=1 (i32.const 0) (local.get 0x128)) + (i64.store offset=0x129 align=1 (i32.const 0) (local.get 0x129)) + (i64.store offset=0x12a align=1 (i32.const 0) (local.get 0x12a)) + (i64.store offset=0x12b align=1 (i32.const 0) (local.get 0x12b)) + (i64.store offset=0x12c align=1 (i32.const 0) (local.get 0x12c)) + (i64.store offset=0x12d align=1 (i32.const 0) (local.get 0x12d)) + (i64.store offset=0x12e align=1 (i32.const 0) (local.get 0x12e)) + (i64.store offset=0x12f align=1 (i32.const 0) (local.get 0x12f)) + (i64.store offset=0x130 align=1 (i32.const 0) (local.get 0x130)) + (i64.store offset=0x131 align=1 (i32.const 0) (local.get 0x131)) + (i64.store offset=0x132 align=1 (i32.const 0) (local.get 0x132)) + (i64.store offset=0x133 align=1 (i32.const 0) (local.get 0x133)) + (i64.store offset=0x134 align=1 (i32.const 0) (local.get 0x134)) + (i64.store offset=0x135 align=1 (i32.const 0) (local.get 0x135)) + (i64.store offset=0x136 align=1 (i32.const 0) (local.get 0x136)) + (i64.store offset=0x137 align=1 (i32.const 0) (local.get 0x137)) + (i64.store offset=0x138 align=1 (i32.const 0) (local.get 0x138)) + (i64.store offset=0x139 align=1 (i32.const 0) (local.get 0x139)) + (i64.store offset=0x13a align=1 (i32.const 0) (local.get 0x13a)) + (i64.store offset=0x13b align=1 (i32.const 0) (local.get 0x13b)) + (i64.store offset=0x13c align=1 (i32.const 0) (local.get 0x13c)) + (i64.store offset=0x13d align=1 (i32.const 0) (local.get 0x13d)) + (i64.store offset=0x13e align=1 (i32.const 0) (local.get 0x13e)) + (i64.store offset=0x13f align=1 (i32.const 0) (local.get 0x13f)) + (i64.store offset=0x140 align=1 (i32.const 0) (local.get 0x140)) + (i64.store offset=0x141 align=1 (i32.const 0) (local.get 0x141)) + (i64.store offset=0x142 align=1 (i32.const 0) (local.get 0x142)) + (i64.store offset=0x143 align=1 (i32.const 0) (local.get 0x143)) + (i64.store offset=0x144 align=1 (i32.const 0) (local.get 0x144)) + (i64.store offset=0x145 align=1 (i32.const 0) (local.get 0x145)) + (i64.store offset=0x146 align=1 (i32.const 0) (local.get 0x146)) + (i64.store offset=0x147 align=1 (i32.const 0) (local.get 0x147)) + (i64.store offset=0x148 align=1 (i32.const 0) (local.get 0x148)) + (i64.store offset=0x149 align=1 (i32.const 0) (local.get 0x149)) + (i64.store offset=0x14a align=1 (i32.const 0) (local.get 0x14a)) + (i64.store offset=0x14b align=1 (i32.const 0) (local.get 0x14b)) + (i64.store offset=0x14c align=1 (i32.const 0) (local.get 0x14c)) + (i64.store offset=0x14d align=1 (i32.const 0) (local.get 0x14d)) + (i64.store offset=0x14e align=1 (i32.const 0) (local.get 0x14e)) + (i64.store offset=0x14f align=1 (i32.const 0) (local.get 0x14f)) + (i64.store offset=0x150 align=1 (i32.const 0) (local.get 0x150)) + (i64.store offset=0x151 align=1 (i32.const 0) (local.get 0x151)) + (i64.store offset=0x152 align=1 (i32.const 0) (local.get 0x152)) + (i64.store offset=0x153 align=1 (i32.const 0) (local.get 0x153)) + (i64.store offset=0x154 align=1 (i32.const 0) (local.get 0x154)) + (i64.store offset=0x155 align=1 (i32.const 0) (local.get 0x155)) + (i64.store offset=0x156 align=1 (i32.const 0) (local.get 0x156)) + (i64.store offset=0x157 align=1 (i32.const 0) (local.get 0x157)) + (i64.store offset=0x158 align=1 (i32.const 0) (local.get 0x158)) + (i64.store offset=0x159 align=1 (i32.const 0) (local.get 0x159)) + (i64.store offset=0x15a align=1 (i32.const 0) (local.get 0x15a)) + (i64.store offset=0x15b align=1 (i32.const 0) (local.get 0x15b)) + (i64.store offset=0x15c align=1 (i32.const 0) (local.get 0x15c)) + (i64.store offset=0x15d align=1 (i32.const 0) (local.get 0x15d)) + (i64.store offset=0x15e align=1 (i32.const 0) (local.get 0x15e)) + (i64.store offset=0x15f align=1 (i32.const 0) (local.get 0x15f)) + (i64.store offset=0x160 align=1 (i32.const 0) (local.get 0x160)) + (i64.store offset=0x161 align=1 (i32.const 0) (local.get 0x161)) + (i64.store offset=0x162 align=1 (i32.const 0) (local.get 0x162)) + (i64.store offset=0x163 align=1 (i32.const 0) (local.get 0x163)) + (i64.store offset=0x164 align=1 (i32.const 0) (local.get 0x164)) + (i64.store offset=0x165 align=1 (i32.const 0) (local.get 0x165)) + (i64.store offset=0x166 align=1 (i32.const 0) (local.get 0x166)) + (i64.store offset=0x167 align=1 (i32.const 0) (local.get 0x167)) + (i64.store offset=0x168 align=1 (i32.const 0) (local.get 0x168)) + (i64.store offset=0x169 align=1 (i32.const 0) (local.get 0x169)) + (i64.store offset=0x16a align=1 (i32.const 0) (local.get 0x16a)) + (i64.store offset=0x16b align=1 (i32.const 0) (local.get 0x16b)) + (i64.store offset=0x16c align=1 (i32.const 0) (local.get 0x16c)) + (i64.store offset=0x16d align=1 (i32.const 0) (local.get 0x16d)) + (i64.store offset=0x16e align=1 (i32.const 0) (local.get 0x16e)) + (i64.store offset=0x16f align=1 (i32.const 0) (local.get 0x16f)) + (i64.store offset=0x170 align=1 (i32.const 0) (local.get 0x170)) + (i64.store offset=0x171 align=1 (i32.const 0) (local.get 0x171)) + (i64.store offset=0x172 align=1 (i32.const 0) (local.get 0x172)) + (i64.store offset=0x173 align=1 (i32.const 0) (local.get 0x173)) + (i64.store offset=0x174 align=1 (i32.const 0) (local.get 0x174)) + (i64.store offset=0x175 align=1 (i32.const 0) (local.get 0x175)) + (i64.store offset=0x176 align=1 (i32.const 0) (local.get 0x176)) + (i64.store offset=0x177 align=1 (i32.const 0) (local.get 0x177)) + (i64.store offset=0x178 align=1 (i32.const 0) (local.get 0x178)) + (i64.store offset=0x179 align=1 (i32.const 0) (local.get 0x179)) + (i64.store offset=0x17a align=1 (i32.const 0) (local.get 0x17a)) + (i64.store offset=0x17b align=1 (i32.const 0) (local.get 0x17b)) + (i64.store offset=0x17c align=1 (i32.const 0) (local.get 0x17c)) + (i64.store offset=0x17d align=1 (i32.const 0) (local.get 0x17d)) + (i64.store offset=0x17e align=1 (i32.const 0) (local.get 0x17e)) + (i64.store offset=0x17f align=1 (i32.const 0) (local.get 0x17f)) + (i64.store offset=0x180 align=1 (i32.const 0) (local.get 0x180)) + (i64.store offset=0x181 align=1 (i32.const 0) (local.get 0x181)) + (i64.store offset=0x182 align=1 (i32.const 0) (local.get 0x182)) + (i64.store offset=0x183 align=1 (i32.const 0) (local.get 0x183)) + (i64.store offset=0x184 align=1 (i32.const 0) (local.get 0x184)) + (i64.store offset=0x185 align=1 (i32.const 0) (local.get 0x185)) + (i64.store offset=0x186 align=1 (i32.const 0) (local.get 0x186)) + (i64.store offset=0x187 align=1 (i32.const 0) (local.get 0x187)) + (i64.store offset=0x188 align=1 (i32.const 0) (local.get 0x188)) + (i64.store offset=0x189 align=1 (i32.const 0) (local.get 0x189)) + (i64.store offset=0x18a align=1 (i32.const 0) (local.get 0x18a)) + (i64.store offset=0x18b align=1 (i32.const 0) (local.get 0x18b)) + (i64.store offset=0x18c align=1 (i32.const 0) (local.get 0x18c)) + (i64.store offset=0x18d align=1 (i32.const 0) (local.get 0x18d)) + (i64.store offset=0x18e align=1 (i32.const 0) (local.get 0x18e)) + (i64.store offset=0x18f align=1 (i32.const 0) (local.get 0x18f)) + (i64.store offset=0x190 align=1 (i32.const 0) (local.get 0x190)) + (i64.store offset=0x191 align=1 (i32.const 0) (local.get 0x191)) + (i64.store offset=0x192 align=1 (i32.const 0) (local.get 0x192)) + (i64.store offset=0x193 align=1 (i32.const 0) (local.get 0x193)) + (i64.store offset=0x194 align=1 (i32.const 0) (local.get 0x194)) + (i64.store offset=0x195 align=1 (i32.const 0) (local.get 0x195)) + (i64.store offset=0x196 align=1 (i32.const 0) (local.get 0x196)) + (i64.store offset=0x197 align=1 (i32.const 0) (local.get 0x197)) + (i64.store offset=0x198 align=1 (i32.const 0) (local.get 0x198)) + (i64.store offset=0x199 align=1 (i32.const 0) (local.get 0x199)) + (i64.store offset=0x19a align=1 (i32.const 0) (local.get 0x19a)) + (i64.store offset=0x19b align=1 (i32.const 0) (local.get 0x19b)) + (i64.store offset=0x19c align=1 (i32.const 0) (local.get 0x19c)) + (i64.store offset=0x19d align=1 (i32.const 0) (local.get 0x19d)) + (i64.store offset=0x19e align=1 (i32.const 0) (local.get 0x19e)) + (i64.store offset=0x19f align=1 (i32.const 0) (local.get 0x19f)) + (i64.store offset=0x1a0 align=1 (i32.const 0) (local.get 0x1a0)) + (i64.store offset=0x1a1 align=1 (i32.const 0) (local.get 0x1a1)) + (i64.store offset=0x1a2 align=1 (i32.const 0) (local.get 0x1a2)) + (i64.store offset=0x1a3 align=1 (i32.const 0) (local.get 0x1a3)) + (i64.store offset=0x1a4 align=1 (i32.const 0) (local.get 0x1a4)) + (i64.store offset=0x1a5 align=1 (i32.const 0) (local.get 0x1a5)) + (i64.store offset=0x1a6 align=1 (i32.const 0) (local.get 0x1a6)) + (i64.store offset=0x1a7 align=1 (i32.const 0) (local.get 0x1a7)) + (i64.store offset=0x1a8 align=1 (i32.const 0) (local.get 0x1a8)) + (i64.store offset=0x1a9 align=1 (i32.const 0) (local.get 0x1a9)) + (i64.store offset=0x1aa align=1 (i32.const 0) (local.get 0x1aa)) + (i64.store offset=0x1ab align=1 (i32.const 0) (local.get 0x1ab)) + (i64.store offset=0x1ac align=1 (i32.const 0) (local.get 0x1ac)) + (i64.store offset=0x1ad align=1 (i32.const 0) (local.get 0x1ad)) + (i64.store offset=0x1ae align=1 (i32.const 0) (local.get 0x1ae)) + (i64.store offset=0x1af align=1 (i32.const 0) (local.get 0x1af)) + (i64.store offset=0x1b0 align=1 (i32.const 0) (local.get 0x1b0)) + (i64.store offset=0x1b1 align=1 (i32.const 0) (local.get 0x1b1)) + (i64.store offset=0x1b2 align=1 (i32.const 0) (local.get 0x1b2)) + (i64.store offset=0x1b3 align=1 (i32.const 0) (local.get 0x1b3)) + (i64.store offset=0x1b4 align=1 (i32.const 0) (local.get 0x1b4)) + (i64.store offset=0x1b5 align=1 (i32.const 0) (local.get 0x1b5)) + (i64.store offset=0x1b6 align=1 (i32.const 0) (local.get 0x1b6)) + (i64.store offset=0x1b7 align=1 (i32.const 0) (local.get 0x1b7)) + (i64.store offset=0x1b8 align=1 (i32.const 0) (local.get 0x1b8)) + (i64.store offset=0x1b9 align=1 (i32.const 0) (local.get 0x1b9)) + (i64.store offset=0x1ba align=1 (i32.const 0) (local.get 0x1ba)) + (i64.store offset=0x1bb align=1 (i32.const 0) (local.get 0x1bb)) + (i64.store offset=0x1bc align=1 (i32.const 0) (local.get 0x1bc)) + (i64.store offset=0x1bd align=1 (i32.const 0) (local.get 0x1bd)) + (i64.store offset=0x1be align=1 (i32.const 0) (local.get 0x1be)) + (i64.store offset=0x1bf align=1 (i32.const 0) (local.get 0x1bf)) + (i64.store offset=0x1c0 align=1 (i32.const 0) (local.get 0x1c0)) + (i64.store offset=0x1c1 align=1 (i32.const 0) (local.get 0x1c1)) + (i64.store offset=0x1c2 align=1 (i32.const 0) (local.get 0x1c2)) + (i64.store offset=0x1c3 align=1 (i32.const 0) (local.get 0x1c3)) + (i64.store offset=0x1c4 align=1 (i32.const 0) (local.get 0x1c4)) + (i64.store offset=0x1c5 align=1 (i32.const 0) (local.get 0x1c5)) + (i64.store offset=0x1c6 align=1 (i32.const 0) (local.get 0x1c6)) + (i64.store offset=0x1c7 align=1 (i32.const 0) (local.get 0x1c7)) + (i64.store offset=0x1c8 align=1 (i32.const 0) (local.get 0x1c8)) + (i64.store offset=0x1c9 align=1 (i32.const 0) (local.get 0x1c9)) + (i64.store offset=0x1ca align=1 (i32.const 0) (local.get 0x1ca)) + (i64.store offset=0x1cb align=1 (i32.const 0) (local.get 0x1cb)) + (i64.store offset=0x1cc align=1 (i32.const 0) (local.get 0x1cc)) + (i64.store offset=0x1cd align=1 (i32.const 0) (local.get 0x1cd)) + (i64.store offset=0x1ce align=1 (i32.const 0) (local.get 0x1ce)) + (i64.store offset=0x1cf align=1 (i32.const 0) (local.get 0x1cf)) + (i64.store offset=0x1d0 align=1 (i32.const 0) (local.get 0x1d0)) + (i64.store offset=0x1d1 align=1 (i32.const 0) (local.get 0x1d1)) + (i64.store offset=0x1d2 align=1 (i32.const 0) (local.get 0x1d2)) + (i64.store offset=0x1d3 align=1 (i32.const 0) (local.get 0x1d3)) + (i64.store offset=0x1d4 align=1 (i32.const 0) (local.get 0x1d4)) + (i64.store offset=0x1d5 align=1 (i32.const 0) (local.get 0x1d5)) + (i64.store offset=0x1d6 align=1 (i32.const 0) (local.get 0x1d6)) + (i64.store offset=0x1d7 align=1 (i32.const 0) (local.get 0x1d7)) + (i64.store offset=0x1d8 align=1 (i32.const 0) (local.get 0x1d8)) + (i64.store offset=0x1d9 align=1 (i32.const 0) (local.get 0x1d9)) + (i64.store offset=0x1da align=1 (i32.const 0) (local.get 0x1da)) + (i64.store offset=0x1db align=1 (i32.const 0) (local.get 0x1db)) + (i64.store offset=0x1dc align=1 (i32.const 0) (local.get 0x1dc)) + (i64.store offset=0x1dd align=1 (i32.const 0) (local.get 0x1dd)) + (i64.store offset=0x1de align=1 (i32.const 0) (local.get 0x1de)) + (i64.store offset=0x1df align=1 (i32.const 0) (local.get 0x1df)) + (i64.store offset=0x1e0 align=1 (i32.const 0) (local.get 0x1e0)) + (i64.store offset=0x1e1 align=1 (i32.const 0) (local.get 0x1e1)) + (i64.store offset=0x1e2 align=1 (i32.const 0) (local.get 0x1e2)) + (i64.store offset=0x1e3 align=1 (i32.const 0) (local.get 0x1e3)) + (i64.store offset=0x1e4 align=1 (i32.const 0) (local.get 0x1e4)) + (i64.store offset=0x1e5 align=1 (i32.const 0) (local.get 0x1e5)) + (i64.store offset=0x1e6 align=1 (i32.const 0) (local.get 0x1e6)) + (i64.store offset=0x1e7 align=1 (i32.const 0) (local.get 0x1e7)) + (i64.store offset=0x1e8 align=1 (i32.const 0) (local.get 0x1e8)) + (i64.store offset=0x1e9 align=1 (i32.const 0) (local.get 0x1e9)) + (i64.store offset=0x1ea align=1 (i32.const 0) (local.get 0x1ea)) + (i64.store offset=0x1eb align=1 (i32.const 0) (local.get 0x1eb)) + (i64.store offset=0x1ec align=1 (i32.const 0) (local.get 0x1ec)) + (i64.store offset=0x1ed align=1 (i32.const 0) (local.get 0x1ed)) + (i64.store offset=0x1ee align=1 (i32.const 0) (local.get 0x1ee)) + (i64.store offset=0x1ef align=1 (i32.const 0) (local.get 0x1ef)) + (i64.store offset=0x1f0 align=1 (i32.const 0) (local.get 0x1f0)) + (i64.store offset=0x1f1 align=1 (i32.const 0) (local.get 0x1f1)) + (i64.store offset=0x1f2 align=1 (i32.const 0) (local.get 0x1f2)) + (i64.store offset=0x1f3 align=1 (i32.const 0) (local.get 0x1f3)) + (i64.store offset=0x1f4 align=1 (i32.const 0) (local.get 0x1f4)) + (i64.store offset=0x1f5 align=1 (i32.const 0) (local.get 0x1f5)) + (i64.store offset=0x1f6 align=1 (i32.const 0) (local.get 0x1f6)) + (i64.store offset=0x1f7 align=1 (i32.const 0) (local.get 0x1f7)) + (i64.store offset=0x1f8 align=1 (i32.const 0) (local.get 0x1f8)) + (i64.store offset=0x1f9 align=1 (i32.const 0) (local.get 0x1f9)) + (i64.store offset=0x1fa align=1 (i32.const 0) (local.get 0x1fa)) + (i64.store offset=0x1fb align=1 (i32.const 0) (local.get 0x1fb)) + (i64.store offset=0x1fc align=1 (i32.const 0) (local.get 0x1fc)) + (i64.store offset=0x1fd align=1 (i32.const 0) (local.get 0x1fd)) + (i64.store offset=0x1fe align=1 (i32.const 0) (local.get 0x1fe)) + (i64.store offset=0x1ff align=1 (i32.const 0) (local.get 0x1ff)) + (i64.store offset=0x200 align=1 (i32.const 0) (local.get 0x200)) + (i64.store offset=0x201 align=1 (i32.const 0) (local.get 0x201)) + (i64.store offset=0x202 align=1 (i32.const 0) (local.get 0x202)) + (i64.store offset=0x203 align=1 (i32.const 0) (local.get 0x203)) + (i64.store offset=0x204 align=1 (i32.const 0) (local.get 0x204)) + (i64.store offset=0x205 align=1 (i32.const 0) (local.get 0x205)) + (i64.store offset=0x206 align=1 (i32.const 0) (local.get 0x206)) + (i64.store offset=0x207 align=1 (i32.const 0) (local.get 0x207)) + (i64.store offset=0x208 align=1 (i32.const 0) (local.get 0x208)) + (i64.store offset=0x209 align=1 (i32.const 0) (local.get 0x209)) + (i64.store offset=0x20a align=1 (i32.const 0) (local.get 0x20a)) + (i64.store offset=0x20b align=1 (i32.const 0) (local.get 0x20b)) + (i64.store offset=0x20c align=1 (i32.const 0) (local.get 0x20c)) + (i64.store offset=0x20d align=1 (i32.const 0) (local.get 0x20d)) + (i64.store offset=0x20e align=1 (i32.const 0) (local.get 0x20e)) + (i64.store offset=0x20f align=1 (i32.const 0) (local.get 0x20f)) + (i64.store offset=0x210 align=1 (i32.const 0) (local.get 0x210)) + (i64.store offset=0x211 align=1 (i32.const 0) (local.get 0x211)) + (i64.store offset=0x212 align=1 (i32.const 0) (local.get 0x212)) + (i64.store offset=0x213 align=1 (i32.const 0) (local.get 0x213)) + (i64.store offset=0x214 align=1 (i32.const 0) (local.get 0x214)) + (i64.store offset=0x215 align=1 (i32.const 0) (local.get 0x215)) + (i64.store offset=0x216 align=1 (i32.const 0) (local.get 0x216)) + (i64.store offset=0x217 align=1 (i32.const 0) (local.get 0x217)) + (i64.store offset=0x218 align=1 (i32.const 0) (local.get 0x218)) + (i64.store offset=0x219 align=1 (i32.const 0) (local.get 0x219)) + (i64.store offset=0x21a align=1 (i32.const 0) (local.get 0x21a)) + (i64.store offset=0x21b align=1 (i32.const 0) (local.get 0x21b)) + (i64.store offset=0x21c align=1 (i32.const 0) (local.get 0x21c)) + (i64.store offset=0x21d align=1 (i32.const 0) (local.get 0x21d)) + (i64.store offset=0x21e align=1 (i32.const 0) (local.get 0x21e)) + (i64.store offset=0x21f align=1 (i32.const 0) (local.get 0x21f)) + (i64.store offset=0x220 align=1 (i32.const 0) (local.get 0x220)) + (i64.store offset=0x221 align=1 (i32.const 0) (local.get 0x221)) + (i64.store offset=0x222 align=1 (i32.const 0) (local.get 0x222)) + (i64.store offset=0x223 align=1 (i32.const 0) (local.get 0x223)) + (i64.store offset=0x224 align=1 (i32.const 0) (local.get 0x224)) + (i64.store offset=0x225 align=1 (i32.const 0) (local.get 0x225)) + (i64.store offset=0x226 align=1 (i32.const 0) (local.get 0x226)) + (i64.store offset=0x227 align=1 (i32.const 0) (local.get 0x227)) + (i64.store offset=0x228 align=1 (i32.const 0) (local.get 0x228)) + (i64.store offset=0x229 align=1 (i32.const 0) (local.get 0x229)) + (i64.store offset=0x22a align=1 (i32.const 0) (local.get 0x22a)) + (i64.store offset=0x22b align=1 (i32.const 0) (local.get 0x22b)) + (i64.store offset=0x22c align=1 (i32.const 0) (local.get 0x22c)) + (i64.store offset=0x22d align=1 (i32.const 0) (local.get 0x22d)) + (i64.store offset=0x22e align=1 (i32.const 0) (local.get 0x22e)) + (i64.store offset=0x22f align=1 (i32.const 0) (local.get 0x22f)) + (i64.store offset=0x230 align=1 (i32.const 0) (local.get 0x230)) + (i64.store offset=0x231 align=1 (i32.const 0) (local.get 0x231)) + (i64.store offset=0x232 align=1 (i32.const 0) (local.get 0x232)) + (i64.store offset=0x233 align=1 (i32.const 0) (local.get 0x233)) + (i64.store offset=0x234 align=1 (i32.const 0) (local.get 0x234)) + (i64.store offset=0x235 align=1 (i32.const 0) (local.get 0x235)) + (i64.store offset=0x236 align=1 (i32.const 0) (local.get 0x236)) + (i64.store offset=0x237 align=1 (i32.const 0) (local.get 0x237)) + (i64.store offset=0x238 align=1 (i32.const 0) (local.get 0x238)) + (i64.store offset=0x239 align=1 (i32.const 0) (local.get 0x239)) + (i64.store offset=0x23a align=1 (i32.const 0) (local.get 0x23a)) + (i64.store offset=0x23b align=1 (i32.const 0) (local.get 0x23b)) + (i64.store offset=0x23c align=1 (i32.const 0) (local.get 0x23c)) + (i64.store offset=0x23d align=1 (i32.const 0) (local.get 0x23d)) + (i64.store offset=0x23e align=1 (i32.const 0) (local.get 0x23e)) + (i64.store offset=0x23f align=1 (i32.const 0) (local.get 0x23f)) + (i64.store offset=0x240 align=1 (i32.const 0) (local.get 0x240)) + (i64.store offset=0x241 align=1 (i32.const 0) (local.get 0x241)) + (i64.store offset=0x242 align=1 (i32.const 0) (local.get 0x242)) + (i64.store offset=0x243 align=1 (i32.const 0) (local.get 0x243)) + (i64.store offset=0x244 align=1 (i32.const 0) (local.get 0x244)) + (i64.store offset=0x245 align=1 (i32.const 0) (local.get 0x245)) + (i64.store offset=0x246 align=1 (i32.const 0) (local.get 0x246)) + (i64.store offset=0x247 align=1 (i32.const 0) (local.get 0x247)) + (i64.store offset=0x248 align=1 (i32.const 0) (local.get 0x248)) + (i64.store offset=0x249 align=1 (i32.const 0) (local.get 0x249)) + (i64.store offset=0x24a align=1 (i32.const 0) (local.get 0x24a)) + (i64.store offset=0x24b align=1 (i32.const 0) (local.get 0x24b)) + (i64.store offset=0x24c align=1 (i32.const 0) (local.get 0x24c)) + (i64.store offset=0x24d align=1 (i32.const 0) (local.get 0x24d)) + (i64.store offset=0x24e align=1 (i32.const 0) (local.get 0x24e)) + (i64.store offset=0x24f align=1 (i32.const 0) (local.get 0x24f)) + (i64.store offset=0x250 align=1 (i32.const 0) (local.get 0x250)) + (i64.store offset=0x251 align=1 (i32.const 0) (local.get 0x251)) + (i64.store offset=0x252 align=1 (i32.const 0) (local.get 0x252)) + (i64.store offset=0x253 align=1 (i32.const 0) (local.get 0x253)) + (i64.store offset=0x254 align=1 (i32.const 0) (local.get 0x254)) + (i64.store offset=0x255 align=1 (i32.const 0) (local.get 0x255)) + (i64.store offset=0x256 align=1 (i32.const 0) (local.get 0x256)) + (i64.store offset=0x257 align=1 (i32.const 0) (local.get 0x257)) + (i64.store offset=0x258 align=1 (i32.const 0) (local.get 0x258)) + (i64.store offset=0x259 align=1 (i32.const 0) (local.get 0x259)) + (i64.store offset=0x25a align=1 (i32.const 0) (local.get 0x25a)) + (i64.store offset=0x25b align=1 (i32.const 0) (local.get 0x25b)) + (i64.store offset=0x25c align=1 (i32.const 0) (local.get 0x25c)) + (i64.store offset=0x25d align=1 (i32.const 0) (local.get 0x25d)) + (i64.store offset=0x25e align=1 (i32.const 0) (local.get 0x25e)) + (i64.store offset=0x25f align=1 (i32.const 0) (local.get 0x25f)) + (i64.store offset=0x260 align=1 (i32.const 0) (local.get 0x260)) + (i64.store offset=0x261 align=1 (i32.const 0) (local.get 0x261)) + (i64.store offset=0x262 align=1 (i32.const 0) (local.get 0x262)) + (i64.store offset=0x263 align=1 (i32.const 0) (local.get 0x263)) + (i64.store offset=0x264 align=1 (i32.const 0) (local.get 0x264)) + (i64.store offset=0x265 align=1 (i32.const 0) (local.get 0x265)) + (i64.store offset=0x266 align=1 (i32.const 0) (local.get 0x266)) + (i64.store offset=0x267 align=1 (i32.const 0) (local.get 0x267)) + (i64.store offset=0x268 align=1 (i32.const 0) (local.get 0x268)) + (i64.store offset=0x269 align=1 (i32.const 0) (local.get 0x269)) + (i64.store offset=0x26a align=1 (i32.const 0) (local.get 0x26a)) + (i64.store offset=0x26b align=1 (i32.const 0) (local.get 0x26b)) + (i64.store offset=0x26c align=1 (i32.const 0) (local.get 0x26c)) + (i64.store offset=0x26d align=1 (i32.const 0) (local.get 0x26d)) + (i64.store offset=0x26e align=1 (i32.const 0) (local.get 0x26e)) + (i64.store offset=0x26f align=1 (i32.const 0) (local.get 0x26f)) + (i64.store offset=0x270 align=1 (i32.const 0) (local.get 0x270)) + (i64.store offset=0x271 align=1 (i32.const 0) (local.get 0x271)) + (i64.store offset=0x272 align=1 (i32.const 0) (local.get 0x272)) + (i64.store offset=0x273 align=1 (i32.const 0) (local.get 0x273)) + (i64.store offset=0x274 align=1 (i32.const 0) (local.get 0x274)) + (i64.store offset=0x275 align=1 (i32.const 0) (local.get 0x275)) + (i64.store offset=0x276 align=1 (i32.const 0) (local.get 0x276)) + (i64.store offset=0x277 align=1 (i32.const 0) (local.get 0x277)) + (i64.store offset=0x278 align=1 (i32.const 0) (local.get 0x278)) + (i64.store offset=0x279 align=1 (i32.const 0) (local.get 0x279)) + (i64.store offset=0x27a align=1 (i32.const 0) (local.get 0x27a)) + (i64.store offset=0x27b align=1 (i32.const 0) (local.get 0x27b)) + (i64.store offset=0x27c align=1 (i32.const 0) (local.get 0x27c)) + (i64.store offset=0x27d align=1 (i32.const 0) (local.get 0x27d)) + (i64.store offset=0x27e align=1 (i32.const 0) (local.get 0x27e)) + (i64.store offset=0x27f align=1 (i32.const 0) (local.get 0x27f)) + (i64.store offset=0x280 align=1 (i32.const 0) (local.get 0x280)) + (i64.store offset=0x281 align=1 (i32.const 0) (local.get 0x281)) + (i64.store offset=0x282 align=1 (i32.const 0) (local.get 0x282)) + (i64.store offset=0x283 align=1 (i32.const 0) (local.get 0x283)) + (i64.store offset=0x284 align=1 (i32.const 0) (local.get 0x284)) + (i64.store offset=0x285 align=1 (i32.const 0) (local.get 0x285)) + (i64.store offset=0x286 align=1 (i32.const 0) (local.get 0x286)) + (i64.store offset=0x287 align=1 (i32.const 0) (local.get 0x287)) + (i64.store offset=0x288 align=1 (i32.const 0) (local.get 0x288)) + (i64.store offset=0x289 align=1 (i32.const 0) (local.get 0x289)) + (i64.store offset=0x28a align=1 (i32.const 0) (local.get 0x28a)) + (i64.store offset=0x28b align=1 (i32.const 0) (local.get 0x28b)) + (i64.store offset=0x28c align=1 (i32.const 0) (local.get 0x28c)) + (i64.store offset=0x28d align=1 (i32.const 0) (local.get 0x28d)) + (i64.store offset=0x28e align=1 (i32.const 0) (local.get 0x28e)) + (i64.store offset=0x28f align=1 (i32.const 0) (local.get 0x28f)) + (i64.store offset=0x290 align=1 (i32.const 0) (local.get 0x290)) + (i64.store offset=0x291 align=1 (i32.const 0) (local.get 0x291)) + (i64.store offset=0x292 align=1 (i32.const 0) (local.get 0x292)) + (i64.store offset=0x293 align=1 (i32.const 0) (local.get 0x293)) + (i64.store offset=0x294 align=1 (i32.const 0) (local.get 0x294)) + (i64.store offset=0x295 align=1 (i32.const 0) (local.get 0x295)) + (i64.store offset=0x296 align=1 (i32.const 0) (local.get 0x296)) + (i64.store offset=0x297 align=1 (i32.const 0) (local.get 0x297)) + (i64.store offset=0x298 align=1 (i32.const 0) (local.get 0x298)) + (i64.store offset=0x299 align=1 (i32.const 0) (local.get 0x299)) + (i64.store offset=0x29a align=1 (i32.const 0) (local.get 0x29a)) + (i64.store offset=0x29b align=1 (i32.const 0) (local.get 0x29b)) + (i64.store offset=0x29c align=1 (i32.const 0) (local.get 0x29c)) + (i64.store offset=0x29d align=1 (i32.const 0) (local.get 0x29d)) + (i64.store offset=0x29e align=1 (i32.const 0) (local.get 0x29e)) + (i64.store offset=0x29f align=1 (i32.const 0) (local.get 0x29f)) + (i64.store offset=0x2a0 align=1 (i32.const 0) (local.get 0x2a0)) + (i64.store offset=0x2a1 align=1 (i32.const 0) (local.get 0x2a1)) + (i64.store offset=0x2a2 align=1 (i32.const 0) (local.get 0x2a2)) + (i64.store offset=0x2a3 align=1 (i32.const 0) (local.get 0x2a3)) + (i64.store offset=0x2a4 align=1 (i32.const 0) (local.get 0x2a4)) + (i64.store offset=0x2a5 align=1 (i32.const 0) (local.get 0x2a5)) + (i64.store offset=0x2a6 align=1 (i32.const 0) (local.get 0x2a6)) + (i64.store offset=0x2a7 align=1 (i32.const 0) (local.get 0x2a7)) + (i64.store offset=0x2a8 align=1 (i32.const 0) (local.get 0x2a8)) + (i64.store offset=0x2a9 align=1 (i32.const 0) (local.get 0x2a9)) + (i64.store offset=0x2aa align=1 (i32.const 0) (local.get 0x2aa)) + (i64.store offset=0x2ab align=1 (i32.const 0) (local.get 0x2ab)) + (i64.store offset=0x2ac align=1 (i32.const 0) (local.get 0x2ac)) + (i64.store offset=0x2ad align=1 (i32.const 0) (local.get 0x2ad)) + (i64.store offset=0x2ae align=1 (i32.const 0) (local.get 0x2ae)) + (i64.store offset=0x2af align=1 (i32.const 0) (local.get 0x2af)) + (i64.store offset=0x2b0 align=1 (i32.const 0) (local.get 0x2b0)) + (i64.store offset=0x2b1 align=1 (i32.const 0) (local.get 0x2b1)) + (i64.store offset=0x2b2 align=1 (i32.const 0) (local.get 0x2b2)) + (i64.store offset=0x2b3 align=1 (i32.const 0) (local.get 0x2b3)) + (i64.store offset=0x2b4 align=1 (i32.const 0) (local.get 0x2b4)) + (i64.store offset=0x2b5 align=1 (i32.const 0) (local.get 0x2b5)) + (i64.store offset=0x2b6 align=1 (i32.const 0) (local.get 0x2b6)) + (i64.store offset=0x2b7 align=1 (i32.const 0) (local.get 0x2b7)) + (i64.store offset=0x2b8 align=1 (i32.const 0) (local.get 0x2b8)) + (i64.store offset=0x2b9 align=1 (i32.const 0) (local.get 0x2b9)) + (i64.store offset=0x2ba align=1 (i32.const 0) (local.get 0x2ba)) + (i64.store offset=0x2bb align=1 (i32.const 0) (local.get 0x2bb)) + (i64.store offset=0x2bc align=1 (i32.const 0) (local.get 0x2bc)) + (i64.store offset=0x2bd align=1 (i32.const 0) (local.get 0x2bd)) + (i64.store offset=0x2be align=1 (i32.const 0) (local.get 0x2be)) + (i64.store offset=0x2bf align=1 (i32.const 0) (local.get 0x2bf)) + (i64.store offset=0x2c0 align=1 (i32.const 0) (local.get 0x2c0)) + (i64.store offset=0x2c1 align=1 (i32.const 0) (local.get 0x2c1)) + (i64.store offset=0x2c2 align=1 (i32.const 0) (local.get 0x2c2)) + (i64.store offset=0x2c3 align=1 (i32.const 0) (local.get 0x2c3)) + (i64.store offset=0x2c4 align=1 (i32.const 0) (local.get 0x2c4)) + (i64.store offset=0x2c5 align=1 (i32.const 0) (local.get 0x2c5)) + (i64.store offset=0x2c6 align=1 (i32.const 0) (local.get 0x2c6)) + (i64.store offset=0x2c7 align=1 (i32.const 0) (local.get 0x2c7)) + (i64.store offset=0x2c8 align=1 (i32.const 0) (local.get 0x2c8)) + (i64.store offset=0x2c9 align=1 (i32.const 0) (local.get 0x2c9)) + (i64.store offset=0x2ca align=1 (i32.const 0) (local.get 0x2ca)) + (i64.store offset=0x2cb align=1 (i32.const 0) (local.get 0x2cb)) + (i64.store offset=0x2cc align=1 (i32.const 0) (local.get 0x2cc)) + (i64.store offset=0x2cd align=1 (i32.const 0) (local.get 0x2cd)) + (i64.store offset=0x2ce align=1 (i32.const 0) (local.get 0x2ce)) + (i64.store offset=0x2cf align=1 (i32.const 0) (local.get 0x2cf)) + (i64.store offset=0x2d0 align=1 (i32.const 0) (local.get 0x2d0)) + (i64.store offset=0x2d1 align=1 (i32.const 0) (local.get 0x2d1)) + (i64.store offset=0x2d2 align=1 (i32.const 0) (local.get 0x2d2)) + (i64.store offset=0x2d3 align=1 (i32.const 0) (local.get 0x2d3)) + (i64.store offset=0x2d4 align=1 (i32.const 0) (local.get 0x2d4)) + (i64.store offset=0x2d5 align=1 (i32.const 0) (local.get 0x2d5)) + (i64.store offset=0x2d6 align=1 (i32.const 0) (local.get 0x2d6)) + (i64.store offset=0x2d7 align=1 (i32.const 0) (local.get 0x2d7)) + (i64.store offset=0x2d8 align=1 (i32.const 0) (local.get 0x2d8)) + (i64.store offset=0x2d9 align=1 (i32.const 0) (local.get 0x2d9)) + (i64.store offset=0x2da align=1 (i32.const 0) (local.get 0x2da)) + (i64.store offset=0x2db align=1 (i32.const 0) (local.get 0x2db)) + (i64.store offset=0x2dc align=1 (i32.const 0) (local.get 0x2dc)) + (i64.store offset=0x2dd align=1 (i32.const 0) (local.get 0x2dd)) + (i64.store offset=0x2de align=1 (i32.const 0) (local.get 0x2de)) + (i64.store offset=0x2df align=1 (i32.const 0) (local.get 0x2df)) + (i64.store offset=0x2e0 align=1 (i32.const 0) (local.get 0x2e0)) + (i64.store offset=0x2e1 align=1 (i32.const 0) (local.get 0x2e1)) + (i64.store offset=0x2e2 align=1 (i32.const 0) (local.get 0x2e2)) + (i64.store offset=0x2e3 align=1 (i32.const 0) (local.get 0x2e3)) + (i64.store offset=0x2e4 align=1 (i32.const 0) (local.get 0x2e4)) + (i64.store offset=0x2e5 align=1 (i32.const 0) (local.get 0x2e5)) + (i64.store offset=0x2e6 align=1 (i32.const 0) (local.get 0x2e6)) + (i64.store offset=0x2e7 align=1 (i32.const 0) (local.get 0x2e7)) + (i64.store offset=0x2e8 align=1 (i32.const 0) (local.get 0x2e8)) + (i64.store offset=0x2e9 align=1 (i32.const 0) (local.get 0x2e9)) + (i64.store offset=0x2ea align=1 (i32.const 0) (local.get 0x2ea)) + (i64.store offset=0x2eb align=1 (i32.const 0) (local.get 0x2eb)) + (i64.store offset=0x2ec align=1 (i32.const 0) (local.get 0x2ec)) + (i64.store offset=0x2ed align=1 (i32.const 0) (local.get 0x2ed)) + (i64.store offset=0x2ee align=1 (i32.const 0) (local.get 0x2ee)) + (i64.store offset=0x2ef align=1 (i32.const 0) (local.get 0x2ef)) + (i64.store offset=0x2f0 align=1 (i32.const 0) (local.get 0x2f0)) + (i64.store offset=0x2f1 align=1 (i32.const 0) (local.get 0x2f1)) + (i64.store offset=0x2f2 align=1 (i32.const 0) (local.get 0x2f2)) + (i64.store offset=0x2f3 align=1 (i32.const 0) (local.get 0x2f3)) + (i64.store offset=0x2f4 align=1 (i32.const 0) (local.get 0x2f4)) + (i64.store offset=0x2f5 align=1 (i32.const 0) (local.get 0x2f5)) + (i64.store offset=0x2f6 align=1 (i32.const 0) (local.get 0x2f6)) + (i64.store offset=0x2f7 align=1 (i32.const 0) (local.get 0x2f7)) + (i64.store offset=0x2f8 align=1 (i32.const 0) (local.get 0x2f8)) + (i64.store offset=0x2f9 align=1 (i32.const 0) (local.get 0x2f9)) + (i64.store offset=0x2fa align=1 (i32.const 0) (local.get 0x2fa)) + (i64.store offset=0x2fb align=1 (i32.const 0) (local.get 0x2fb)) + (i64.store offset=0x2fc align=1 (i32.const 0) (local.get 0x2fc)) + (i64.store offset=0x2fd align=1 (i32.const 0) (local.get 0x2fd)) + (i64.store offset=0x2fe align=1 (i32.const 0) (local.get 0x2fe)) + (i64.store offset=0x2ff align=1 (i32.const 0) (local.get 0x2ff)) + (i64.store offset=0x300 align=1 (i32.const 0) (local.get 0x300)) + (i64.store offset=0x301 align=1 (i32.const 0) (local.get 0x301)) + (i64.store offset=0x302 align=1 (i32.const 0) (local.get 0x302)) + (i64.store offset=0x303 align=1 (i32.const 0) (local.get 0x303)) + (i64.store offset=0x304 align=1 (i32.const 0) (local.get 0x304)) + (i64.store offset=0x305 align=1 (i32.const 0) (local.get 0x305)) + (i64.store offset=0x306 align=1 (i32.const 0) (local.get 0x306)) + (i64.store offset=0x307 align=1 (i32.const 0) (local.get 0x307)) + (i64.store offset=0x308 align=1 (i32.const 0) (local.get 0x308)) + (i64.store offset=0x309 align=1 (i32.const 0) (local.get 0x309)) + (i64.store offset=0x30a align=1 (i32.const 0) (local.get 0x30a)) + (i64.store offset=0x30b align=1 (i32.const 0) (local.get 0x30b)) + (i64.store offset=0x30c align=1 (i32.const 0) (local.get 0x30c)) + (i64.store offset=0x30d align=1 (i32.const 0) (local.get 0x30d)) + (i64.store offset=0x30e align=1 (i32.const 0) (local.get 0x30e)) + (i64.store offset=0x30f align=1 (i32.const 0) (local.get 0x30f)) + (i64.store offset=0x310 align=1 (i32.const 0) (local.get 0x310)) + (i64.store offset=0x311 align=1 (i32.const 0) (local.get 0x311)) + (i64.store offset=0x312 align=1 (i32.const 0) (local.get 0x312)) + (i64.store offset=0x313 align=1 (i32.const 0) (local.get 0x313)) + (i64.store offset=0x314 align=1 (i32.const 0) (local.get 0x314)) + (i64.store offset=0x315 align=1 (i32.const 0) (local.get 0x315)) + (i64.store offset=0x316 align=1 (i32.const 0) (local.get 0x316)) + (i64.store offset=0x317 align=1 (i32.const 0) (local.get 0x317)) + (i64.store offset=0x318 align=1 (i32.const 0) (local.get 0x318)) + (i64.store offset=0x319 align=1 (i32.const 0) (local.get 0x319)) + (i64.store offset=0x31a align=1 (i32.const 0) (local.get 0x31a)) + (i64.store offset=0x31b align=1 (i32.const 0) (local.get 0x31b)) + (i64.store offset=0x31c align=1 (i32.const 0) (local.get 0x31c)) + (i64.store offset=0x31d align=1 (i32.const 0) (local.get 0x31d)) + (i64.store offset=0x31e align=1 (i32.const 0) (local.get 0x31e)) + (i64.store offset=0x31f align=1 (i32.const 0) (local.get 0x31f)) + (i64.store offset=0x320 align=1 (i32.const 0) (local.get 0x320)) + (i64.store offset=0x321 align=1 (i32.const 0) (local.get 0x321)) + (i64.store offset=0x322 align=1 (i32.const 0) (local.get 0x322)) + (i64.store offset=0x323 align=1 (i32.const 0) (local.get 0x323)) + (i64.store offset=0x324 align=1 (i32.const 0) (local.get 0x324)) + (i64.store offset=0x325 align=1 (i32.const 0) (local.get 0x325)) + (i64.store offset=0x326 align=1 (i32.const 0) (local.get 0x326)) + (i64.store offset=0x327 align=1 (i32.const 0) (local.get 0x327)) + (i64.store offset=0x328 align=1 (i32.const 0) (local.get 0x328)) + (i64.store offset=0x329 align=1 (i32.const 0) (local.get 0x329)) + (i64.store offset=0x32a align=1 (i32.const 0) (local.get 0x32a)) + (i64.store offset=0x32b align=1 (i32.const 0) (local.get 0x32b)) + (i64.store offset=0x32c align=1 (i32.const 0) (local.get 0x32c)) + (i64.store offset=0x32d align=1 (i32.const 0) (local.get 0x32d)) + (i64.store offset=0x32e align=1 (i32.const 0) (local.get 0x32e)) + (i64.store offset=0x32f align=1 (i32.const 0) (local.get 0x32f)) + (i64.store offset=0x330 align=1 (i32.const 0) (local.get 0x330)) + (i64.store offset=0x331 align=1 (i32.const 0) (local.get 0x331)) + (i64.store offset=0x332 align=1 (i32.const 0) (local.get 0x332)) + (i64.store offset=0x333 align=1 (i32.const 0) (local.get 0x333)) + (i64.store offset=0x334 align=1 (i32.const 0) (local.get 0x334)) + (i64.store offset=0x335 align=1 (i32.const 0) (local.get 0x335)) + (i64.store offset=0x336 align=1 (i32.const 0) (local.get 0x336)) + (i64.store offset=0x337 align=1 (i32.const 0) (local.get 0x337)) + (i64.store offset=0x338 align=1 (i32.const 0) (local.get 0x338)) + (i64.store offset=0x339 align=1 (i32.const 0) (local.get 0x339)) + (i64.store offset=0x33a align=1 (i32.const 0) (local.get 0x33a)) + (i64.store offset=0x33b align=1 (i32.const 0) (local.get 0x33b)) + (i64.store offset=0x33c align=1 (i32.const 0) (local.get 0x33c)) + (i64.store offset=0x33d align=1 (i32.const 0) (local.get 0x33d)) + (i64.store offset=0x33e align=1 (i32.const 0) (local.get 0x33e)) + (i64.store offset=0x33f align=1 (i32.const 0) (local.get 0x33f)) + (i64.store offset=0x340 align=1 (i32.const 0) (local.get 0x340)) + (i64.store offset=0x341 align=1 (i32.const 0) (local.get 0x341)) + (i64.store offset=0x342 align=1 (i32.const 0) (local.get 0x342)) + (i64.store offset=0x343 align=1 (i32.const 0) (local.get 0x343)) + (i64.store offset=0x344 align=1 (i32.const 0) (local.get 0x344)) + (i64.store offset=0x345 align=1 (i32.const 0) (local.get 0x345)) + (i64.store offset=0x346 align=1 (i32.const 0) (local.get 0x346)) + (i64.store offset=0x347 align=1 (i32.const 0) (local.get 0x347)) + (i64.store offset=0x348 align=1 (i32.const 0) (local.get 0x348)) + (i64.store offset=0x349 align=1 (i32.const 0) (local.get 0x349)) + (i64.store offset=0x34a align=1 (i32.const 0) (local.get 0x34a)) + (i64.store offset=0x34b align=1 (i32.const 0) (local.get 0x34b)) + (i64.store offset=0x34c align=1 (i32.const 0) (local.get 0x34c)) + (i64.store offset=0x34d align=1 (i32.const 0) (local.get 0x34d)) + (i64.store offset=0x34e align=1 (i32.const 0) (local.get 0x34e)) + (i64.store offset=0x34f align=1 (i32.const 0) (local.get 0x34f)) + (i64.store offset=0x350 align=1 (i32.const 0) (local.get 0x350)) + (i64.store offset=0x351 align=1 (i32.const 0) (local.get 0x351)) + (i64.store offset=0x352 align=1 (i32.const 0) (local.get 0x352)) + (i64.store offset=0x353 align=1 (i32.const 0) (local.get 0x353)) + (i64.store offset=0x354 align=1 (i32.const 0) (local.get 0x354)) + (i64.store offset=0x355 align=1 (i32.const 0) (local.get 0x355)) + (i64.store offset=0x356 align=1 (i32.const 0) (local.get 0x356)) + (i64.store offset=0x357 align=1 (i32.const 0) (local.get 0x357)) + (i64.store offset=0x358 align=1 (i32.const 0) (local.get 0x358)) + (i64.store offset=0x359 align=1 (i32.const 0) (local.get 0x359)) + (i64.store offset=0x35a align=1 (i32.const 0) (local.get 0x35a)) + (i64.store offset=0x35b align=1 (i32.const 0) (local.get 0x35b)) + (i64.store offset=0x35c align=1 (i32.const 0) (local.get 0x35c)) + (i64.store offset=0x35d align=1 (i32.const 0) (local.get 0x35d)) + (i64.store offset=0x35e align=1 (i32.const 0) (local.get 0x35e)) + (i64.store offset=0x35f align=1 (i32.const 0) (local.get 0x35f)) + (i64.store offset=0x360 align=1 (i32.const 0) (local.get 0x360)) + (i64.store offset=0x361 align=1 (i32.const 0) (local.get 0x361)) + (i64.store offset=0x362 align=1 (i32.const 0) (local.get 0x362)) + (i64.store offset=0x363 align=1 (i32.const 0) (local.get 0x363)) + (i64.store offset=0x364 align=1 (i32.const 0) (local.get 0x364)) + (i64.store offset=0x365 align=1 (i32.const 0) (local.get 0x365)) + (i64.store offset=0x366 align=1 (i32.const 0) (local.get 0x366)) + (i64.store offset=0x367 align=1 (i32.const 0) (local.get 0x367)) + (i64.store offset=0x368 align=1 (i32.const 0) (local.get 0x368)) + (i64.store offset=0x369 align=1 (i32.const 0) (local.get 0x369)) + (i64.store offset=0x36a align=1 (i32.const 0) (local.get 0x36a)) + (i64.store offset=0x36b align=1 (i32.const 0) (local.get 0x36b)) + (i64.store offset=0x36c align=1 (i32.const 0) (local.get 0x36c)) + (i64.store offset=0x36d align=1 (i32.const 0) (local.get 0x36d)) + (i64.store offset=0x36e align=1 (i32.const 0) (local.get 0x36e)) + (i64.store offset=0x36f align=1 (i32.const 0) (local.get 0x36f)) + (i64.store offset=0x370 align=1 (i32.const 0) (local.get 0x370)) + (i64.store offset=0x371 align=1 (i32.const 0) (local.get 0x371)) + (i64.store offset=0x372 align=1 (i32.const 0) (local.get 0x372)) + (i64.store offset=0x373 align=1 (i32.const 0) (local.get 0x373)) + (i64.store offset=0x374 align=1 (i32.const 0) (local.get 0x374)) + (i64.store offset=0x375 align=1 (i32.const 0) (local.get 0x375)) + (i64.store offset=0x376 align=1 (i32.const 0) (local.get 0x376)) + (i64.store offset=0x377 align=1 (i32.const 0) (local.get 0x377)) + (i64.store offset=0x378 align=1 (i32.const 0) (local.get 0x378)) + (i64.store offset=0x379 align=1 (i32.const 0) (local.get 0x379)) + (i64.store offset=0x37a align=1 (i32.const 0) (local.get 0x37a)) + (i64.store offset=0x37b align=1 (i32.const 0) (local.get 0x37b)) + (i64.store offset=0x37c align=1 (i32.const 0) (local.get 0x37c)) + (i64.store offset=0x37d align=1 (i32.const 0) (local.get 0x37d)) + (i64.store offset=0x37e align=1 (i32.const 0) (local.get 0x37e)) + (i64.store offset=0x37f align=1 (i32.const 0) (local.get 0x37f)) + (i64.store offset=0x380 align=1 (i32.const 0) (local.get 0x380)) + (i64.store offset=0x381 align=1 (i32.const 0) (local.get 0x381)) + (i64.store offset=0x382 align=1 (i32.const 0) (local.get 0x382)) + (i64.store offset=0x383 align=1 (i32.const 0) (local.get 0x383)) + (i64.store offset=0x384 align=1 (i32.const 0) (local.get 0x384)) + (i64.store offset=0x385 align=1 (i32.const 0) (local.get 0x385)) + (i64.store offset=0x386 align=1 (i32.const 0) (local.get 0x386)) + (i64.store offset=0x387 align=1 (i32.const 0) (local.get 0x387)) + (i64.store offset=0x388 align=1 (i32.const 0) (local.get 0x388)) + (i64.store offset=0x389 align=1 (i32.const 0) (local.get 0x389)) + (i64.store offset=0x38a align=1 (i32.const 0) (local.get 0x38a)) + (i64.store offset=0x38b align=1 (i32.const 0) (local.get 0x38b)) + (i64.store offset=0x38c align=1 (i32.const 0) (local.get 0x38c)) + (i64.store offset=0x38d align=1 (i32.const 0) (local.get 0x38d)) + (i64.store offset=0x38e align=1 (i32.const 0) (local.get 0x38e)) + (i64.store offset=0x38f align=1 (i32.const 0) (local.get 0x38f)) + (i64.store offset=0x390 align=1 (i32.const 0) (local.get 0x390)) + (i64.store offset=0x391 align=1 (i32.const 0) (local.get 0x391)) + (i64.store offset=0x392 align=1 (i32.const 0) (local.get 0x392)) + (i64.store offset=0x393 align=1 (i32.const 0) (local.get 0x393)) + (i64.store offset=0x394 align=1 (i32.const 0) (local.get 0x394)) + (i64.store offset=0x395 align=1 (i32.const 0) (local.get 0x395)) + (i64.store offset=0x396 align=1 (i32.const 0) (local.get 0x396)) + (i64.store offset=0x397 align=1 (i32.const 0) (local.get 0x397)) + (i64.store offset=0x398 align=1 (i32.const 0) (local.get 0x398)) + (i64.store offset=0x399 align=1 (i32.const 0) (local.get 0x399)) + (i64.store offset=0x39a align=1 (i32.const 0) (local.get 0x39a)) + (i64.store offset=0x39b align=1 (i32.const 0) (local.get 0x39b)) + (i64.store offset=0x39c align=1 (i32.const 0) (local.get 0x39c)) + (i64.store offset=0x39d align=1 (i32.const 0) (local.get 0x39d)) + (i64.store offset=0x39e align=1 (i32.const 0) (local.get 0x39e)) + (i64.store offset=0x39f align=1 (i32.const 0) (local.get 0x39f)) + (i64.store offset=0x3a0 align=1 (i32.const 0) (local.get 0x3a0)) + (i64.store offset=0x3a1 align=1 (i32.const 0) (local.get 0x3a1)) + (i64.store offset=0x3a2 align=1 (i32.const 0) (local.get 0x3a2)) + (i64.store offset=0x3a3 align=1 (i32.const 0) (local.get 0x3a3)) + (i64.store offset=0x3a4 align=1 (i32.const 0) (local.get 0x3a4)) + (i64.store offset=0x3a5 align=1 (i32.const 0) (local.get 0x3a5)) + (i64.store offset=0x3a6 align=1 (i32.const 0) (local.get 0x3a6)) + (i64.store offset=0x3a7 align=1 (i32.const 0) (local.get 0x3a7)) + (i64.store offset=0x3a8 align=1 (i32.const 0) (local.get 0x3a8)) + (i64.store offset=0x3a9 align=1 (i32.const 0) (local.get 0x3a9)) + (i64.store offset=0x3aa align=1 (i32.const 0) (local.get 0x3aa)) + (i64.store offset=0x3ab align=1 (i32.const 0) (local.get 0x3ab)) + (i64.store offset=0x3ac align=1 (i32.const 0) (local.get 0x3ac)) + (i64.store offset=0x3ad align=1 (i32.const 0) (local.get 0x3ad)) + (i64.store offset=0x3ae align=1 (i32.const 0) (local.get 0x3ae)) + (i64.store offset=0x3af align=1 (i32.const 0) (local.get 0x3af)) + (i64.store offset=0x3b0 align=1 (i32.const 0) (local.get 0x3b0)) + (i64.store offset=0x3b1 align=1 (i32.const 0) (local.get 0x3b1)) + (i64.store offset=0x3b2 align=1 (i32.const 0) (local.get 0x3b2)) + (i64.store offset=0x3b3 align=1 (i32.const 0) (local.get 0x3b3)) + (i64.store offset=0x3b4 align=1 (i32.const 0) (local.get 0x3b4)) + (i64.store offset=0x3b5 align=1 (i32.const 0) (local.get 0x3b5)) + (i64.store offset=0x3b6 align=1 (i32.const 0) (local.get 0x3b6)) + (i64.store offset=0x3b7 align=1 (i32.const 0) (local.get 0x3b7)) + (i64.store offset=0x3b8 align=1 (i32.const 0) (local.get 0x3b8)) + (i64.store offset=0x3b9 align=1 (i32.const 0) (local.get 0x3b9)) + (i64.store offset=0x3ba align=1 (i32.const 0) (local.get 0x3ba)) + (i64.store offset=0x3bb align=1 (i32.const 0) (local.get 0x3bb)) + (i64.store offset=0x3bc align=1 (i32.const 0) (local.get 0x3bc)) + (i64.store offset=0x3bd align=1 (i32.const 0) (local.get 0x3bd)) + (i64.store offset=0x3be align=1 (i32.const 0) (local.get 0x3be)) + (i64.store offset=0x3bf align=1 (i32.const 0) (local.get 0x3bf)) + (i64.store offset=0x3c0 align=1 (i32.const 0) (local.get 0x3c0)) + (i64.store offset=0x3c1 align=1 (i32.const 0) (local.get 0x3c1)) + (i64.store offset=0x3c2 align=1 (i32.const 0) (local.get 0x3c2)) + (i64.store offset=0x3c3 align=1 (i32.const 0) (local.get 0x3c3)) + (i64.store offset=0x3c4 align=1 (i32.const 0) (local.get 0x3c4)) + (i64.store offset=0x3c5 align=1 (i32.const 0) (local.get 0x3c5)) + (i64.store offset=0x3c6 align=1 (i32.const 0) (local.get 0x3c6)) + (i64.store offset=0x3c7 align=1 (i32.const 0) (local.get 0x3c7)) + (i64.store offset=0x3c8 align=1 (i32.const 0) (local.get 0x3c8)) + (i64.store offset=0x3c9 align=1 (i32.const 0) (local.get 0x3c9)) + (i64.store offset=0x3ca align=1 (i32.const 0) (local.get 0x3ca)) + (i64.store offset=0x3cb align=1 (i32.const 0) (local.get 0x3cb)) + (i64.store offset=0x3cc align=1 (i32.const 0) (local.get 0x3cc)) + (i64.store offset=0x3cd align=1 (i32.const 0) (local.get 0x3cd)) + (i64.store offset=0x3ce align=1 (i32.const 0) (local.get 0x3ce)) + (i64.store offset=0x3cf align=1 (i32.const 0) (local.get 0x3cf)) + (i64.store offset=0x3d0 align=1 (i32.const 0) (local.get 0x3d0)) + (i64.store offset=0x3d1 align=1 (i32.const 0) (local.get 0x3d1)) + (i64.store offset=0x3d2 align=1 (i32.const 0) (local.get 0x3d2)) + (i64.store offset=0x3d3 align=1 (i32.const 0) (local.get 0x3d3)) + (i64.store offset=0x3d4 align=1 (i32.const 0) (local.get 0x3d4)) + (i64.store offset=0x3d5 align=1 (i32.const 0) (local.get 0x3d5)) + (i64.store offset=0x3d6 align=1 (i32.const 0) (local.get 0x3d6)) + (i64.store offset=0x3d7 align=1 (i32.const 0) (local.get 0x3d7)) + (i64.store offset=0x3d8 align=1 (i32.const 0) (local.get 0x3d8)) + (i64.store offset=0x3d9 align=1 (i32.const 0) (local.get 0x3d9)) + (i64.store offset=0x3da align=1 (i32.const 0) (local.get 0x3da)) + (i64.store offset=0x3db align=1 (i32.const 0) (local.get 0x3db)) + (i64.store offset=0x3dc align=1 (i32.const 0) (local.get 0x3dc)) + (i64.store offset=0x3dd align=1 (i32.const 0) (local.get 0x3dd)) + (i64.store offset=0x3de align=1 (i32.const 0) (local.get 0x3de)) + (i64.store offset=0x3df align=1 (i32.const 0) (local.get 0x3df)) + (i64.store offset=0x3e0 align=1 (i32.const 0) (local.get 0x3e0)) + (i64.store offset=0x3e1 align=1 (i32.const 0) (local.get 0x3e1)) + (i64.store offset=0x3e2 align=1 (i32.const 0) (local.get 0x3e2)) + (i64.store offset=0x3e3 align=1 (i32.const 0) (local.get 0x3e3)) + (i64.store offset=0x3e4 align=1 (i32.const 0) (local.get 0x3e4)) + (i64.store offset=0x3e5 align=1 (i32.const 0) (local.get 0x3e5)) + (i64.store offset=0x3e6 align=1 (i32.const 0) (local.get 0x3e6)) + (i64.store offset=0x3e7 align=1 (i32.const 0) (local.get 0x3e7)) + (i64.store offset=0x3e8 align=1 (i32.const 0) (local.get 0x3e8)) + (i64.store offset=0x3e9 align=1 (i32.const 0) (local.get 0x3e9)) + (i64.store offset=0x3ea align=1 (i32.const 0) (local.get 0x3ea)) + (i64.store offset=0x3eb align=1 (i32.const 0) (local.get 0x3eb)) + (i64.store offset=0x3ec align=1 (i32.const 0) (local.get 0x3ec)) + (i64.store offset=0x3ed align=1 (i32.const 0) (local.get 0x3ed)) + (i64.store offset=0x3ee align=1 (i32.const 0) (local.get 0x3ee)) + (i64.store offset=0x3ef align=1 (i32.const 0) (local.get 0x3ef)) + (i64.store offset=0x3f0 align=1 (i32.const 0) (local.get 0x3f0)) + (i64.store offset=0x3f1 align=1 (i32.const 0) (local.get 0x3f1)) + (i64.store offset=0x3f2 align=1 (i32.const 0) (local.get 0x3f2)) + (i64.store offset=0x3f3 align=1 (i32.const 0) (local.get 0x3f3)) + (i64.store offset=0x3f4 align=1 (i32.const 0) (local.get 0x3f4)) + (i64.store offset=0x3f5 align=1 (i32.const 0) (local.get 0x3f5)) + (i64.store offset=0x3f6 align=1 (i32.const 0) (local.get 0x3f6)) + (i64.store offset=0x3f7 align=1 (i32.const 0) (local.get 0x3f7)) + (i64.store offset=0x3f8 align=1 (i32.const 0) (local.get 0x3f8)) + (i64.store offset=0x3f9 align=1 (i32.const 0) (local.get 0x3f9)) + (i64.store offset=0x3fa align=1 (i32.const 0) (local.get 0x3fa)) + (i64.store offset=0x3fb align=1 (i32.const 0) (local.get 0x3fb)) + (i64.store offset=0x3fc align=1 (i32.const 0) (local.get 0x3fc)) + (i64.store offset=0x3fd align=1 (i32.const 0) (local.get 0x3fd)) + (i64.store offset=0x3fe align=1 (i32.const 0) (local.get 0x3fe)) + (i64.store offset=0x3ff align=1 (i32.const 0) (local.get 0x3ff)) + (i64.store offset=0x400 align=1 (i32.const 0) (local.get 0x400)) + (i64.store offset=0x401 align=1 (i32.const 0) (local.get 0x401)) + (i64.store offset=0x402 align=1 (i32.const 0) (local.get 0x402)) + (i64.store offset=0x403 align=1 (i32.const 0) (local.get 0x403)) + (i64.store offset=0x404 align=1 (i32.const 0) (local.get 0x404)) + (i64.store offset=0x405 align=1 (i32.const 0) (local.get 0x405)) + (i64.store offset=0x406 align=1 (i32.const 0) (local.get 0x406)) + (i64.store offset=0x407 align=1 (i32.const 0) (local.get 0x407)) + (i64.store offset=0x408 align=1 (i32.const 0) (local.get 0x408)) + (i64.store offset=0x409 align=1 (i32.const 0) (local.get 0x409)) + (i64.store offset=0x40a align=1 (i32.const 0) (local.get 0x40a)) + (i64.store offset=0x40b align=1 (i32.const 0) (local.get 0x40b)) + (i64.store offset=0x40c align=1 (i32.const 0) (local.get 0x40c)) + (i64.store offset=0x40d align=1 (i32.const 0) (local.get 0x40d)) + (i64.store offset=0x40e align=1 (i32.const 0) (local.get 0x40e)) + (i64.store offset=0x40f align=1 (i32.const 0) (local.get 0x40f)) + (i64.store offset=0x410 align=1 (i32.const 0) (local.get 0x410)) + (i64.store offset=0x411 align=1 (i32.const 0) (local.get 0x411)) + (i64.store offset=0x412 align=1 (i32.const 0) (local.get 0x412)) + (i64.store offset=0x413 align=1 (i32.const 0) (local.get 0x413)) + (i64.store offset=0x414 align=1 (i32.const 0) (local.get 0x414)) + (i64.store offset=0x415 align=1 (i32.const 0) (local.get 0x415)) + (i64.store offset=0x416 align=1 (i32.const 0) (local.get 0x416)) + (i64.store offset=0x417 align=1 (i32.const 0) (local.get 0x417)) + (i64.store offset=0x418 align=1 (i32.const 0) (local.get 0x418)) + (i64.store offset=0x419 align=1 (i32.const 0) (local.get 0x419)) + (i64.store offset=0x41a align=1 (i32.const 0) (local.get 0x41a)) + (i64.store offset=0x41b align=1 (i32.const 0) (local.get 0x41b)) + (i64.store offset=0x41c align=1 (i32.const 0) (local.get 0x41c)) + (i64.store offset=0x41d align=1 (i32.const 0) (local.get 0x41d)) + (i64.store offset=0x41e align=1 (i32.const 0) (local.get 0x41e)) + (i64.store offset=0x41f align=1 (i32.const 0) (local.get 0x41f)) + ) +) + +(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 0)) "call stack exhausted") +(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 100)) "call stack exhausted") +(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 200)) "call stack exhausted") +(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 300)) "call stack exhausted") +(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 400)) "call stack exhausted") +(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 500)) "call stack exhausted") +(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 600)) "call stack exhausted") +(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 700)) "call stack exhausted") +(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 800)) "call stack exhausted") +(assert_exhaustion (invoke "test-guard-page-skip" (i32.const 900)) "call stack exhausted") diff --git a/runtime/unc-vm/tests/wast/spec/stack.wast b/runtime/unc-vm/tests/wast/spec/stack.wast new file mode 100644 index 000000000..5da5043c8 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/stack.wast @@ -0,0 +1,239 @@ +(module + (func (export "fac-expr") (param $n i64) (result i64) + (local $i i64) + (local $res i64) + (local.set $i (local.get $n)) + (local.set $res (i64.const 1)) + (block $done + (loop $loop + (if + (i64.eq (local.get $i) (i64.const 0)) + (then (br $done)) + (else + (local.set $res (i64.mul (local.get $i) (local.get $res))) + (local.set $i (i64.sub (local.get $i) (i64.const 1))) + ) + ) + (br $loop) + ) + ) + (local.get $res) + ) + + (func (export "fac-stack") (param $n i64) (result i64) + (local $i i64) + (local $res i64) + (local.get $n) + (local.set $i) + (i64.const 1) + (local.set $res) + (block $done + (loop $loop + (local.get $i) + (i64.const 0) + (i64.eq) + (if + (then (br $done)) + (else + (local.get $i) + (local.get $res) + (i64.mul) + (local.set $res) + (local.get $i) + (i64.const 1) + (i64.sub) + (local.set $i) + ) + ) + (br $loop) + ) + ) + (local.get $res) + ) + + (func (export "fac-stack-raw") (param $n i64) (result i64) + (local $i i64) + (local $res i64) + local.get $n + local.set $i + i64.const 1 + local.set $res + block $done + loop $loop + local.get $i + i64.const 0 + i64.eq + if $body + br $done + else $body + local.get $i + local.get $res + i64.mul + local.set $res + local.get $i + i64.const 1 + i64.sub + local.set $i + end $body + br $loop + end $loop + end $done + local.get $res + ) + + (func (export "fac-mixed") (param $n i64) (result i64) + (local $i i64) + (local $res i64) + (local.set $i (local.get $n)) + (local.set $res (i64.const 1)) + (block $done + (loop $loop + (i64.eq (local.get $i) (i64.const 0)) + (if + (then (br $done)) + (else + (i64.mul (local.get $i) (local.get $res)) + (local.set $res) + (i64.sub (local.get $i) (i64.const 1)) + (local.set $i) + ) + ) + (br $loop) + ) + ) + (local.get $res) + ) + + (func (export "fac-mixed-raw") (param $n i64) (result i64) + (local $i i64) + (local $res i64) + (local.set $i (local.get $n)) + (local.set $res (i64.const 1)) + block $done + loop $loop + (i64.eq (local.get $i) (i64.const 0)) + if + br $done + else + (i64.mul (local.get $i) (local.get $res)) + local.set $res + (i64.sub (local.get $i) (i64.const 1)) + local.set $i + end + br $loop + end + end + local.get $res + ) + + (global $temp (mut i32) (i32.const 0)) + (func $add_one_to_global (result i32) + (local i32) + (global.set $temp (i32.add (i32.const 1) (global.get $temp))) + (global.get $temp) + ) + (func $add_one_to_global_and_drop + (drop (call $add_one_to_global)) + ) + (func (export "not-quite-a-tree") (result i32) + call $add_one_to_global + call $add_one_to_global + call $add_one_to_global_and_drop + i32.add + ) +) + +(assert_return (invoke "fac-expr" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-stack" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-mixed" (i64.const 25)) (i64.const 7034535277573963776)) + +(assert_return (invoke "not-quite-a-tree") (i32.const 3)) +(assert_return (invoke "not-quite-a-tree") (i32.const 9)) + + +;; Syntax of flat call_indirect + +(module + (type $proc (func)) + (table 1 funcref) + + (func + (block i32.const 0 call_indirect) + (loop i32.const 0 call_indirect) + (if (i32.const 0) (then i32.const 0 call_indirect)) + (if (i32.const 0) + (then i32.const 0 call_indirect) + (else i32.const 0 call_indirect) + ) + (block i32.const 0 call_indirect (type $proc)) + (loop i32.const 0 call_indirect (type $proc)) + (if (i32.const 0) (then i32.const 0 call_indirect (type $proc))) + (if (i32.const 0) + (then i32.const 0 call_indirect (type $proc)) + (else i32.const 0 call_indirect (type $proc)) + ) + (block i32.const 0 i32.const 0 call_indirect (param i32)) + (loop i32.const 0 i32.const 0 call_indirect (param i32)) + (if (i32.const 0) (then i32.const 0 i32.const 0 call_indirect (param i32))) + (if (i32.const 0) + (then i32.const 0 i32.const 0 call_indirect (param i32)) + (else i32.const 0 i32.const 0 call_indirect (param i32)) + ) + (block (result i32) i32.const 0 call_indirect (result i32)) (drop) + (loop (result i32) i32.const 0 call_indirect (result i32)) (drop) + (if (result i32) (i32.const 0) + (then i32.const 0 call_indirect (result i32)) + (else i32.const 0 call_indirect (result i32)) + ) (drop) + (block i32.const 0 call_indirect (type $proc) (param) (result)) + (loop i32.const 0 call_indirect (type $proc) (param) (result)) + (if (i32.const 0) + (then i32.const 0 call_indirect (type $proc) (param) (result)) + ) + (if (i32.const 0) + (then i32.const 0 call_indirect (type $proc) (param) (param) (result)) + (else i32.const 0 call_indirect (type $proc) (param) (result) (result)) + ) + + block i32.const 0 call_indirect end + loop i32.const 0 call_indirect end + i32.const 0 if i32.const 0 call_indirect end + i32.const 0 if i32.const 0 call_indirect else i32.const 0 call_indirect end + block i32.const 0 call_indirect (type $proc) end + loop i32.const 0 call_indirect (type $proc) end + i32.const 0 if i32.const 0 call_indirect (type $proc) end + i32.const 0 + if + i32.const 0 call_indirect (type $proc) + else + i32.const 0 call_indirect (type $proc) + end + block i32.const 0 i32.const 0 call_indirect (param i32) end + loop i32.const 0 i32.const 0 call_indirect (param i32) end + i32.const 0 if i32.const 0 i32.const 0 call_indirect (param i32) end + i32.const 0 + if + i32.const 0 i32.const 0 call_indirect (param i32) + else + i32.const 0 i32.const 0 call_indirect (param i32) + end + block (result i32) i32.const 0 call_indirect (result i32) end drop + loop (result i32) i32.const 0 call_indirect (result i32) end drop + i32.const 0 + if (result i32) + i32.const 0 call_indirect (result i32) + else + i32.const 0 call_indirect (result i32) + end drop + block i32.const 0 call_indirect (type $proc) (param) (result) end + loop i32.const 0 call_indirect (type $proc) (param) (result) end + i32.const 0 if i32.const 0 call_indirect (type $proc) (param) (result) end + i32.const 0 + if + i32.const 0 call_indirect (type $proc) (param) (result) + else + i32.const 0 call_indirect (type $proc) (param) (param) (result) (result) + end + i32.const 0 call_indirect + ) +) diff --git a/runtime/unc-vm/tests/wast/spec/start.wast b/runtime/unc-vm/tests/wast/spec/start.wast new file mode 100644 index 000000000..bf88a6a4a --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/start.wast @@ -0,0 +1,105 @@ +(assert_invalid + (module (func) (start 1)) + "unknown function" +) + +(assert_invalid + (module + (func $main (result i32) (return (i32.const 0))) + (start $main) + ) + "start function" +) +(assert_invalid + (module + (func $main (param $a i32)) + (start $main) + ) + "start function" +) + +(module + (memory (data "A")) + (func $inc + (i32.store8 + (i32.const 0) + (i32.add + (i32.load8_u (i32.const 0)) + (i32.const 1) + ) + ) + ) + (func $get (result i32) + (return (i32.load8_u (i32.const 0))) + ) + (func $main + (call $inc) + (call $inc) + (call $inc) + ) + + (start $main) + (export "inc" (func $inc)) + (export "get" (func $get)) +) +(assert_return (invoke "get") (i32.const 68)) +(invoke "inc") +(assert_return (invoke "get") (i32.const 69)) +(invoke "inc") +(assert_return (invoke "get") (i32.const 70)) + +(module + (memory (data "A")) + (func $inc + (i32.store8 + (i32.const 0) + (i32.add + (i32.load8_u (i32.const 0)) + (i32.const 1) + ) + ) + ) + (func $get (result i32) + (return (i32.load8_u (i32.const 0))) + ) + (func $main + (call $inc) + (call $inc) + (call $inc) + ) + (start 2) + (export "inc" (func $inc)) + (export "get" (func $get)) +) +(assert_return (invoke "get") (i32.const 68)) +(invoke "inc") +(assert_return (invoke "get") (i32.const 69)) +(invoke "inc") +(assert_return (invoke "get") (i32.const 70)) + +(module + (func $print_i32 (import "spectest" "print_i32") (param i32)) + (func $main (call $print_i32 (i32.const 1))) + (start 1) +) + +(module + (func $print_i32 (import "spectest" "print_i32") (param i32)) + (func $main (call $print_i32 (i32.const 2))) + (start $main) +) + +(module + (func $print (import "spectest" "print")) + (start $print) +) + +(assert_trap + (module (func $main (unreachable)) (start $main)) + "unreachable" +) + +(assert_malformed + (module quote "(module (func $a (unreachable)) (func $b (unreachable)) (start $a) (start $b))") + "multiple start sections" +) diff --git a/runtime/unc-vm/tests/wast/spec/store.wast b/runtime/unc-vm/tests/wast/spec/store.wast new file mode 100644 index 000000000..01c3a2dd6 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/store.wast @@ -0,0 +1,417 @@ +;; Store operator as the argument of control constructs and instructions + +(module + (memory 1) + + (func (export "as-block-value") + (block (i32.store (i32.const 0) (i32.const 1))) + ) + (func (export "as-loop-value") + (loop (i32.store (i32.const 0) (i32.const 1))) + ) + + (func (export "as-br-value") + (block (br 0 (i32.store (i32.const 0) (i32.const 1)))) + ) + (func (export "as-br_if-value") + (block + (br_if 0 (i32.store (i32.const 0) (i32.const 1)) (i32.const 1)) + ) + ) + (func (export "as-br_if-value-cond") + (block + (br_if 0 (i32.const 6) (i32.store (i32.const 0) (i32.const 1))) + ) + ) + (func (export "as-br_table-value") + (block + (br_table 0 (i32.store (i32.const 0) (i32.const 1)) (i32.const 1)) + ) + ) + + (func (export "as-return-value") + (return (i32.store (i32.const 0) (i32.const 1))) + ) + + (func (export "as-if-then") + (if (i32.const 1) (then (i32.store (i32.const 0) (i32.const 1)))) + ) + (func (export "as-if-else") + (if (i32.const 0) (then) (else (i32.store (i32.const 0) (i32.const 1)))) + ) +) + +(assert_return (invoke "as-block-value")) +(assert_return (invoke "as-loop-value")) + +(assert_return (invoke "as-br-value")) +(assert_return (invoke "as-br_if-value")) +(assert_return (invoke "as-br_if-value-cond")) +(assert_return (invoke "as-br_table-value")) + +(assert_return (invoke "as-return-value")) + +(assert_return (invoke "as-if-then")) +(assert_return (invoke "as-if-else")) + +(assert_malformed + (module quote + "(memory 1)" + "(func (param i32) (i32.store32 (local.get 0) (i32.const 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1)" + "(func (param i32) (i32.store64 (local.get 0) (i64.const 0)))" + ) + "unknown operator" +) + +(assert_malformed + (module quote + "(memory 1)" + "(func (param i32) (i64.store64 (local.get 0) (i64.const 0)))" + ) + "unknown operator" +) + +(assert_malformed + (module quote + "(memory 1)" + "(func (param i32) (f32.store32 (local.get 0) (f32.const 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1)" + "(func (param i32) (f32.store64 (local.get 0) (f64.const 0)))" + ) + "unknown operator" +) + +(assert_malformed + (module quote + "(memory 1)" + "(func (param i32) (f64.store32 (local.get 0) (f32.const 0)))" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(memory 1)" + "(func (param i32) (f64.store64 (local.get 0) (f64.const 0)))" + ) + "unknown operator" +) +;; store should have no retval + +(assert_invalid + (module (memory 1) (func (param i32) (result i32) (i32.store (i32.const 0) (i32.const 1)))) + "type mismatch" +) +(assert_invalid + (module (memory 1) (func (param i64) (result i64) (i64.store (i32.const 0) (i64.const 1)))) + "type mismatch" +) +(assert_invalid + (module (memory 1) (func (param f32) (result f32) (f32.store (i32.const 0) (f32.const 1)))) + "type mismatch" +) +(assert_invalid + (module (memory 1) (func (param f64) (result f64) (f64.store (i32.const 0) (f64.const 1)))) + "type mismatch" +) +(assert_invalid + (module (memory 1) (func (param i32) (result i32) (i32.store8 (i32.const 0) (i32.const 1)))) + "type mismatch" +) +(assert_invalid + (module (memory 1) (func (param i32) (result i32) (i32.store16 (i32.const 0) (i32.const 1)))) + "type mismatch" +) +(assert_invalid + (module (memory 1) (func (param i64) (result i64) (i64.store8 (i32.const 0) (i64.const 1)))) + "type mismatch" +) +(assert_invalid + (module (memory 1) (func (param i64) (result i64) (i64.store16 (i32.const 0) (i64.const 1)))) + "type mismatch" +) +(assert_invalid + (module (memory 1) (func (param i64) (result i64) (i64.store32 (i32.const 0) (i64.const 1)))) + "type mismatch" +) + + +(assert_invalid + (module + (memory 1) + (func $type-address-empty + (i32.store) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-value-empty + (i32.const 0) (i32.store) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-address-empty-in-block + (i32.const 0) (i32.const 0) + (block (i32.store)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-value-empty-in-block + (i32.const 0) + (block (i32.const 0) (i32.store)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-address-empty-in-loop + (i32.const 0) (i32.const 0) + (loop (i32.store)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-value-empty-in-loop + (i32.const 0) + (loop (i32.const 0) (i32.store)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-address-empty-in-then + (i32.const 0) (i32.const 0) + (if (then (i32.store))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-value-empty-in-then + (i32.const 0) + (if (then (i32.const 0) (i32.store))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-address-empty-in-else + (i32.const 0) (i32.const 0) + (if (result i32) (then (i32.const 0)) (else (i32.store))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-value-empty-in-else + (i32.const 0) + (if (result i32) (then (i32.const 0)) (else (i32.const 0) (i32.store))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-address-empty-in-br + (i32.const 0) (i32.const 0) + (block (br 0 (i32.store))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-value-empty-in-br + (i32.const 0) + (block (br 0 (i32.const 0) (i32.store))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-address-empty-in-br_if + (i32.const 0) (i32.const 0) + (block (br_if 0 (i32.store) (i32.const 1)) ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-value-empty-in-br_if + (i32.const 0) + (block (br_if 0 (i32.const 0) (i32.store) (i32.const 1)) ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-address-empty-in-br_table + (i32.const 0) (i32.const 0) + (block (br_table 0 (i32.store))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-value-empty-in-br_table + (i32.const 0) + (block (br_table 0 (i32.const 0) (i32.store))) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-address-empty-in-return + (return (i32.store)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-value-empty-in-return + (return (i32.const 0) (i32.store)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-address-empty-in-select + (select (i32.store) (i32.const 1) (i32.const 2)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-value-empty-in-select + (select (i32.const 0) (i32.store) (i32.const 1) (i32.const 2)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-address-empty-in-call + (call 1 (i32.store)) + ) + (func (param i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $type-value-empty-in-call + (call 1 (i32.const 0) (i32.store)) + ) + (func (param i32) (result i32) (local.get 0)) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-address-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (i32.store) (i32.const 0) + ) + ) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (memory 1) + (func $f (param i32) (result i32) (local.get 0)) + (type $sig (func (param i32) (result i32))) + (table funcref (elem $f)) + (func $type-value-empty-in-call_indirect + (block (result i32) + (call_indirect (type $sig) + (i32.const 0) (i32.store) (i32.const 0) + ) + ) + ) + ) + "type mismatch" +) + + +;; Type check + +(assert_invalid (module (memory 1) (func (i32.store (f32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (i32.store8 (f32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (i32.store16 (f32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (i64.store (f32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (i64.store8 (f32.const 0) (i64.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (i64.store16 (f32.const 0) (i64.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (i64.store32 (f32.const 0) (i64.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (f32.store (f32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (f64.store (f32.const 0) (f64.const 0)))) "type mismatch") + +(assert_invalid (module (memory 1) (func (i32.store (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (i32.store8 (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (i32.store16 (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (i64.store (i32.const 0) (f32.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (i64.store8 (i32.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (i64.store16 (i32.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (i64.store32 (i32.const 0) (f64.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (f32.store (i32.const 0) (i32.const 0)))) "type mismatch") +(assert_invalid (module (memory 1) (func (f64.store (i32.const 0) (i64.const 0)))) "type mismatch") diff --git a/runtime/unc-vm/tests/wast/spec/switch.wast b/runtime/unc-vm/tests/wast/spec/switch.wast new file mode 100644 index 000000000..e9ae24dc1 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/switch.wast @@ -0,0 +1,150 @@ +(module + ;; Statement switch + (func (export "stmt") (param $i i32) (result i32) + (local $j i32) + (local.set $j (i32.const 100)) + (block $switch + (block $7 + (block $default + (block $6 + (block $5 + (block $4 + (block $3 + (block $2 + (block $1 + (block $0 + (br_table $0 $1 $2 $3 $4 $5 $6 $7 $default + (local.get $i) + ) + ) ;; 0 + (return (local.get $i)) + ) ;; 1 + (nop) + ;; fallthrough + ) ;; 2 + ;; fallthrough + ) ;; 3 + (local.set $j (i32.sub (i32.const 0) (local.get $i))) + (br $switch) + ) ;; 4 + (br $switch) + ) ;; 5 + (local.set $j (i32.const 101)) + (br $switch) + ) ;; 6 + (local.set $j (i32.const 101)) + ;; fallthrough + ) ;; default + (local.set $j (i32.const 102)) + ) ;; 7 + ;; fallthrough + ) + (return (local.get $j)) + ) + + ;; Expression switch + (func (export "expr") (param $i i64) (result i64) + (local $j i64) + (local.set $j (i64.const 100)) + (return + (block $switch (result i64) + (block $7 + (block $default + (block $4 + (block $5 + (block $6 + (block $3 + (block $2 + (block $1 + (block $0 + (br_table $0 $1 $2 $3 $4 $5 $6 $7 $default + (i32.wrap_i64 (local.get $i)) + ) + ) ;; 0 + (return (local.get $i)) + ) ;; 1 + (nop) + ;; fallthrough + ) ;; 2 + ;; fallthrough + ) ;; 3 + (br $switch (i64.sub (i64.const 0) (local.get $i))) + ) ;; 6 + (local.set $j (i64.const 101)) + ;; fallthrough + ) ;; 4 + ;; fallthrough + ) ;; 5 + ;; fallthrough + ) ;; default + (br $switch (local.get $j)) + ) ;; 7 + (i64.const -5) + ) + ) + ) + + ;; Argument switch + (func (export "arg") (param $i i32) (result i32) + (return + (block $2 (result i32) + (i32.add (i32.const 10) + (block $1 (result i32) + (i32.add (i32.const 100) + (block $0 (result i32) + (i32.add (i32.const 1000) + (block $default (result i32) + (br_table $0 $1 $2 $default + (i32.mul (i32.const 2) (local.get $i)) + (i32.and (i32.const 3) (local.get $i)) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + + ;; Corner cases + (func (export "corner") (result i32) + (block + (br_table 0 (i32.const 0)) + ) + (i32.const 1) + ) +) + +(assert_return (invoke "stmt" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "stmt" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "stmt" (i32.const 2)) (i32.const -2)) +(assert_return (invoke "stmt" (i32.const 3)) (i32.const -3)) +(assert_return (invoke "stmt" (i32.const 4)) (i32.const 100)) +(assert_return (invoke "stmt" (i32.const 5)) (i32.const 101)) +(assert_return (invoke "stmt" (i32.const 6)) (i32.const 102)) +(assert_return (invoke "stmt" (i32.const 7)) (i32.const 100)) +(assert_return (invoke "stmt" (i32.const -10)) (i32.const 102)) + +(assert_return (invoke "expr" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "expr" (i64.const 1)) (i64.const -1)) +(assert_return (invoke "expr" (i64.const 2)) (i64.const -2)) +(assert_return (invoke "expr" (i64.const 3)) (i64.const -3)) +(assert_return (invoke "expr" (i64.const 6)) (i64.const 101)) +(assert_return (invoke "expr" (i64.const 7)) (i64.const -5)) +(assert_return (invoke "expr" (i64.const -10)) (i64.const 100)) + +(assert_return (invoke "arg" (i32.const 0)) (i32.const 110)) +(assert_return (invoke "arg" (i32.const 1)) (i32.const 12)) +(assert_return (invoke "arg" (i32.const 2)) (i32.const 4)) +(assert_return (invoke "arg" (i32.const 3)) (i32.const 1116)) +(assert_return (invoke "arg" (i32.const 4)) (i32.const 118)) +(assert_return (invoke "arg" (i32.const 5)) (i32.const 20)) +(assert_return (invoke "arg" (i32.const 6)) (i32.const 12)) +(assert_return (invoke "arg" (i32.const 7)) (i32.const 1124)) +(assert_return (invoke "arg" (i32.const 8)) (i32.const 126)) + +(assert_return (invoke "corner") (i32.const 1)) + +(assert_invalid (module (func (br_table 3 (i32.const 0)))) "unknown label") diff --git a/runtime/unc-vm/tests/wast/spec/table-sub.wast b/runtime/unc-vm/tests/wast/spec/table-sub.wast new file mode 100644 index 000000000..08787bddd --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/table-sub.wast @@ -0,0 +1,21 @@ +(assert_invalid + (module + (table $t1 10 funcref) + (table $t2 10 externref) + (func $f + (table.copy $t1 $t2 (i32.const 0) (i32.const 1) (i32.const 2)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 10 funcref) + (elem $el externref) + (func $f + (table.init $t $el (i32.const 0) (i32.const 1) (i32.const 2)) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/table.wast b/runtime/unc-vm/tests/wast/spec/table.wast new file mode 100644 index 000000000..0bd04f5cc --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/table.wast @@ -0,0 +1,54 @@ +;; Test table section structure + +(module (table 0 funcref)) +(module (table 1 funcref)) +(module (table 0 0 funcref)) +(module (table 0 1 funcref)) +(module (table 1 256 funcref)) +(module (table 0 65536 funcref)) +(module (table 0 0xffff_ffff funcref)) + +(module (table 0 funcref) (table 0 funcref)) +(module (table (import "spectest" "table") 0 funcref) (table 0 funcref)) + +(assert_invalid (module (elem (i32.const 0))) "unknown table") +(assert_invalid (module (elem (i32.const 0) $f) (func $f)) "unknown table") + + +(assert_invalid + (module (table 1 0 funcref)) + "size minimum must not be greater than maximum" +) +(assert_invalid + (module (table 0xffff_ffff 0 funcref)) + "size minimum must not be greater than maximum" +) + +(assert_malformed + (module quote "(table 0x1_0000_0000 funcref)") + "i32 constant out of range" +) +(assert_malformed + (module quote "(table 0x1_0000_0000 0x1_0000_0000 funcref)") + "i32 constant out of range" +) +(assert_malformed + (module quote "(table 0 0x1_0000_0000 funcref)") + "i32 constant out of range" +) + + +;; Duplicate table identifiers + +(assert_malformed (module quote + "(table $foo 1 funcref)" + "(table $foo 1 funcref)") + "duplicate table") +(assert_malformed (module quote + "(import \"\" \"\" (table $foo 1 funcref))" + "(table $foo 1 funcref)") + "duplicate table") +(assert_malformed (module quote + "(import \"\" \"\" (table $foo 1 funcref))" + "(import \"\" \"\" (table $foo 1 funcref))") + "duplicate table") diff --git a/runtime/unc-vm/tests/wast/spec/table_copy.wast b/runtime/unc-vm/tests/wast/spec/table_copy.wast new file mode 100644 index 000000000..380e84ee5 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/table_copy.wast @@ -0,0 +1,3082 @@ +;; +;; Generated by ../meta/generate_table_copy.js +;; DO NOT EDIT THIS FILE. CHANGE THE SOURCE AND REGENERATE. +;; + +(module + (func (export "ef0") (result i32) (i32.const 0)) + (func (export "ef1") (result i32) (i32.const 1)) + (func (export "ef2") (result i32) (i32.const 2)) + (func (export "ef3") (result i32) (i32.const 3)) + (func (export "ef4") (result i32) (i32.const 4)) +) +(register "a") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (nop)) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t0 (i32.const 13) (i32.const 2) (i32.const 3))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t0 (i32.const 25) (i32.const 15) (i32.const 2))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 25)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 26)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t0 (i32.const 13) (i32.const 25) (i32.const 3))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_trap (invoke "check_t0" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 15)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t0 (i32.const 20) (i32.const 22) (i32.const 4))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t0 (i32.const 25) (i32.const 1) (i32.const 3))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 26)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 27)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t0 (i32.const 10) (i32.const 12) (i32.const 7))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 10)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 11)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t0 (i32.const 12) (i32.const 10) (i32.const 7))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 13)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 17)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 18)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t1) (i32.const 3) func 1 3 1 4) + (elem (table $t1) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t0 (i32.const 10) (i32.const 0) (i32.const 20))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 4)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 1)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 22)) (i32.const 7)) +(assert_return (invoke "check_t1" (i32.const 23)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 24)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 25)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 26)) (i32.const 6)) +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (nop)) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t1 (i32.const 13) (i32.const 2) (i32.const 3))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t1 (i32.const 25) (i32.const 15) (i32.const 2))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 25)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 26)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t1 (i32.const 13) (i32.const 25) (i32.const 3))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_trap (invoke "check_t0" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 15)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t1 (i32.const 20) (i32.const 22) (i32.const 4))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t1 (i32.const 25) (i32.const 1) (i32.const 3))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 26)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 27)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t1 (i32.const 10) (i32.const 12) (i32.const 7))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 10)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 11)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t1 $t1 (i32.const 12) (i32.const 10) (i32.const 7))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 13)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 17)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 18)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 11)) (i32.const 6)) +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 7)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (elem (table $t0) (i32.const 3) func 1 3 1 4) + (elem (table $t0) (i32.const 11) func 6 3 2 5 7) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.copy $t0 $t1 (i32.const 10) (i32.const 0) (i32.const 20))) + (func (export "check_t0") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) + (func (export "check_t1") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check_t0" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t0" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check_t0" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check_t0" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t0" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check_t0" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check_t0" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check_t0" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check_t0" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check_t0" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t0" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 2)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 4)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 5)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 6)) (i32.const 4)) +(assert_trap (invoke "check_t1" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 12)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 13)) (i32.const 1)) +(assert_return (invoke "check_t1" (i32.const 14)) (i32.const 4)) +(assert_return (invoke "check_t1" (i32.const 15)) (i32.const 1)) +(assert_trap (invoke "check_t1" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 21)) "uninitialized element") +(assert_return (invoke "check_t1" (i32.const 22)) (i32.const 7)) +(assert_return (invoke "check_t1" (i32.const 23)) (i32.const 5)) +(assert_return (invoke "check_t1" (i32.const 24)) (i32.const 2)) +(assert_return (invoke "check_t1" (i32.const 25)) (i32.const 3)) +(assert_return (invoke "check_t1" (i32.const 26)) (i32.const 6)) +(assert_trap (invoke "check_t1" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check_t1" (i32.const 29)) "uninitialized element") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 28) (i32.const 1) (i32.const 3)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 0xFFFFFFFE) (i32.const 1) (i32.const 2)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 15) (i32.const 25) (i32.const 6)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 15) (i32.const 0xFFFFFFFE) (i32.const 2)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 15) (i32.const 25) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 30) (i32.const 15) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 31) (i32.const 15) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 15) (i32.const 30) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 15) (i32.const 31) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 30) (i32.const 30) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t0 $t0 (i32.const 31) (i32.const 31) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 28) (i32.const 1) (i32.const 3)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 0xFFFFFFFE) (i32.const 1) (i32.const 2)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 15) (i32.const 25) (i32.const 6)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 15) (i32.const 0xFFFFFFFE) (i32.const 2)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 15) (i32.const 25) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 30) (i32.const 15) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 31) (i32.const 15) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 15) (i32.const 30) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 15) (i32.const 31) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 30) (i32.const 30) (i32.const 0)) + )) + +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.copy $t1 $t0 (i32.const 31) (i32.const 31) (i32.const 0)) + )) + +(assert_trap (invoke "test") "out of bounds table access") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 0) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 24) (i32.const 0) (i32.const 16)) + "out of bounds table access") +(assert_return (invoke "test" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 7)) (i32.const 7)) +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 0) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 23) (i32.const 0) (i32.const 15)) + "out of bounds table access") +(assert_return (invoke "test" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 7)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 8)) (i32.const 8)) +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 24) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 24) (i32.const 16)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_return (invoke "test" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 7)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 23) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 23) (i32.const 15)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_return (invoke "test" (i32.const 23)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 24)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 8)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 11) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 24) (i32.const 11) (i32.const 16)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_return (invoke "test" (i32.const 11)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 12)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 13)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 14)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 15)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 16)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 17)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 18)) (i32.const 7)) +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 24) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 11) (i32.const 24) (i32.const 16)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_return (invoke "test" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 7)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 21) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 24) (i32.const 21) (i32.const 16)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_return (invoke "test" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 22)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 23)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 24)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 7)) +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 24) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 21) (i32.const 24) (i32.const 16)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_return (invoke "test" (i32.const 24)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 7)) + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem (i32.const 21) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $f10) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 21) (i32.const 21) (i32.const 16)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_return (invoke "test" (i32.const 21)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 22)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 23)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 24)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 25)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 26)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 27)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 28)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 29)) (i32.const 8)) +(assert_return (invoke "test" (i32.const 30)) (i32.const 9)) +(assert_return (invoke "test" (i32.const 31)) (i32.const 10)) + +(module + (type (func (result i32))) + (table 128 128 funcref) + (elem (i32.const 112) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $f10 $f11 $f12 $f13 $f14 $f15) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 0) (i32.const 112) (i32.const 4294967264)) + "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_return (invoke "test" (i32.const 112)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 113)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 114)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 115)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 116)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 117)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 118)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 119)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 120)) (i32.const 8)) +(assert_return (invoke "test" (i32.const 121)) (i32.const 9)) +(assert_return (invoke "test" (i32.const 122)) (i32.const 10)) +(assert_return (invoke "test" (i32.const 123)) (i32.const 11)) +(assert_return (invoke "test" (i32.const 124)) (i32.const 12)) +(assert_return (invoke "test" (i32.const 125)) (i32.const 13)) +(assert_return (invoke "test" (i32.const 126)) (i32.const 14)) +(assert_return (invoke "test" (i32.const 127)) (i32.const 15)) + +(module + (type (func (result i32))) + (table 128 128 funcref) + (elem (i32.const 0) + $f0 $f1 $f2 $f3 $f4 $f5 $f6 $f7 $f8 $f9 $f10 $f11 $f12 $f13 $f14 $f15) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $targetOffs i32) (param $srcOffs i32) (param $len i32) + (table.copy (local.get $targetOffs) (local.get $srcOffs) (local.get $len)))) + +(assert_trap (invoke "run" (i32.const 112) (i32.const 0) (i32.const 4294967264)) + "out of bounds table access") +(assert_return (invoke "test" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "test" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "test" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "test" (i32.const 3)) (i32.const 3)) +(assert_return (invoke "test" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "test" (i32.const 5)) (i32.const 5)) +(assert_return (invoke "test" (i32.const 6)) (i32.const 6)) +(assert_return (invoke "test" (i32.const 7)) (i32.const 7)) +(assert_return (invoke "test" (i32.const 8)) (i32.const 8)) +(assert_return (invoke "test" (i32.const 9)) (i32.const 9)) +(assert_return (invoke "test" (i32.const 10)) (i32.const 10)) +(assert_return (invoke "test" (i32.const 11)) (i32.const 11)) +(assert_return (invoke "test" (i32.const 12)) (i32.const 12)) +(assert_return (invoke "test" (i32.const 13)) (i32.const 13)) +(assert_return (invoke "test" (i32.const 14)) (i32.const 14)) +(assert_return (invoke "test" (i32.const 15)) (i32.const 15)) +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 112)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 113)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 114)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 115)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 116)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 117)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 118)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 119)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 120)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 121)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 122)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 123)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 124)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 125)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 126)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 127)) "uninitialized element") diff --git a/runtime/unc-vm/tests/wast/spec/table_fill.wast b/runtime/unc-vm/tests/wast/spec/table_fill.wast new file mode 100644 index 000000000..3df64da1a --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/table_fill.wast @@ -0,0 +1,153 @@ +(module + (table $t 10 externref) + + (func (export "fill") (param $i i32) (param $r externref) (param $n i32) + (table.fill $t (local.get $i) (local.get $r) (local.get $n)) + ) + + (func (export "get") (param $i i32) (result externref) + (table.get $t (local.get $i)) + ) +) + +(assert_return (invoke "get" (i32.const 1)) (ref.null extern)) +(assert_return (invoke "get" (i32.const 2)) (ref.null extern)) +(assert_return (invoke "get" (i32.const 3)) (ref.null extern)) +(assert_return (invoke "get" (i32.const 4)) (ref.null extern)) +(assert_return (invoke "get" (i32.const 5)) (ref.null extern)) + +(assert_return (invoke "fill" (i32.const 2) (ref.extern 1) (i32.const 3))) +(assert_return (invoke "get" (i32.const 1)) (ref.null extern)) +(assert_return (invoke "get" (i32.const 2)) (ref.extern 1)) +(assert_return (invoke "get" (i32.const 3)) (ref.extern 1)) +(assert_return (invoke "get" (i32.const 4)) (ref.extern 1)) +(assert_return (invoke "get" (i32.const 5)) (ref.null extern)) + +(assert_return (invoke "fill" (i32.const 4) (ref.extern 2) (i32.const 2))) +(assert_return (invoke "get" (i32.const 3)) (ref.extern 1)) +(assert_return (invoke "get" (i32.const 4)) (ref.extern 2)) +(assert_return (invoke "get" (i32.const 5)) (ref.extern 2)) +(assert_return (invoke "get" (i32.const 6)) (ref.null extern)) + +(assert_return (invoke "fill" (i32.const 4) (ref.extern 3) (i32.const 0))) +(assert_return (invoke "get" (i32.const 3)) (ref.extern 1)) +(assert_return (invoke "get" (i32.const 4)) (ref.extern 2)) +(assert_return (invoke "get" (i32.const 5)) (ref.extern 2)) + +(assert_return (invoke "fill" (i32.const 8) (ref.extern 4) (i32.const 2))) +(assert_return (invoke "get" (i32.const 7)) (ref.null extern)) +(assert_return (invoke "get" (i32.const 8)) (ref.extern 4)) +(assert_return (invoke "get" (i32.const 9)) (ref.extern 4)) + +(assert_return (invoke "fill" (i32.const 9) (ref.null extern) (i32.const 1))) +(assert_return (invoke "get" (i32.const 8)) (ref.extern 4)) +(assert_return (invoke "get" (i32.const 9)) (ref.null extern)) + +(assert_return (invoke "fill" (i32.const 10) (ref.extern 5) (i32.const 0))) +(assert_return (invoke "get" (i32.const 9)) (ref.null extern)) + +(assert_trap + (invoke "fill" (i32.const 8) (ref.extern 6) (i32.const 3)) + "out of bounds table access" +) +(assert_return (invoke "get" (i32.const 7)) (ref.null extern)) +(assert_return (invoke "get" (i32.const 8)) (ref.extern 4)) +(assert_return (invoke "get" (i32.const 9)) (ref.null extern)) + +(assert_trap + (invoke "fill" (i32.const 11) (ref.null extern) (i32.const 0)) + "out of bounds table access" +) + +(assert_trap + (invoke "fill" (i32.const 11) (ref.null extern) (i32.const 10)) + "out of bounds table access" +) + + +;; Type errors + +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-value-length-empty-vs-i32-i32 + (table.fill $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-empty-vs-i32 + (table.fill $t (ref.null extern) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-value-empty-vs + (table.fill $t (i32.const 1) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-length-empty-vs-i32 + (table.fill $t (i32.const 1) (ref.null extern)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 externref) + (func $type-index-f32-vs-i32 + (table.fill $t (f32.const 1) (ref.null extern) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 funcref) + (func $type-value-vs-funcref (param $r externref) + (table.fill $t (i32.const 1) (local.get $r) (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 externref) + (func $type-length-f32-vs-i32 + (table.fill $t (i32.const 1) (ref.null extern) (f32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t1 1 externref) + (table $t2 1 funcref) + (func $type-value-externref-vs-funcref-multi (param $r externref) + (table.fill $t2 (i32.const 0) (local.get $r) (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 1 externref) + (func $type-result-empty-vs-num (result i32) + (table.fill $t (i32.const 0) (ref.null extern) (i32.const 1)) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/table_get.wast b/runtime/unc-vm/tests/wast/spec/table_get.wast new file mode 100644 index 000000000..5d57c3198 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/table_get.wast @@ -0,0 +1,88 @@ +(module + (table $t2 2 externref) + (table $t3 3 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + + (func (export "init") (param $r externref) + (table.set $t2 (i32.const 1) (local.get $r)) + (table.set $t3 (i32.const 2) (table.get $t3 (i32.const 1))) + ) + + (func (export "get-externref") (param $i i32) (result externref) + (table.get $t2 (local.get $i)) + ) + (func $f3 (export "get-funcref") (param $i i32) (result funcref) + (table.get $t3 (local.get $i)) + ) + + (func (export "is_null-funcref") (param $i i32) (result i32) + (ref.is_null (call $f3 (local.get $i))) + ) +) + +(invoke "init" (ref.extern 1)) + +(assert_return (invoke "get-externref" (i32.const 0)) (ref.null extern)) +(assert_return (invoke "get-externref" (i32.const 1)) (ref.extern 1)) + +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func)) +(assert_return (invoke "is_null-funcref" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "is_null-funcref" (i32.const 2)) (i32.const 0)) + +(assert_trap (invoke "get-externref" (i32.const 2)) "out of bounds table access") +(assert_trap (invoke "get-funcref" (i32.const 3)) "out of bounds table access") +(assert_trap (invoke "get-externref" (i32.const -1)) "out of bounds table access") +(assert_trap (invoke "get-funcref" (i32.const -1)) "out of bounds table access") + + +;; Type errors + +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-empty-vs-i32 (result externref) + (table.get $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-f32-vs-i32 (result externref) + (table.get $t (f32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 10 externref) + (func $type-result-externref-vs-empty + (table.get $t (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-result-externref-vs-funcref (result funcref) + (table.get $t (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t1 1 funcref) + (table $t2 1 externref) + (func $type-result-externref-vs-funcref-multi (result funcref) + (table.get $t2 (i32.const 0)) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/table_grow.wast b/runtime/unc-vm/tests/wast/spec/table_grow.wast new file mode 100644 index 000000000..7d5b5630f --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/table_grow.wast @@ -0,0 +1,173 @@ +(module + (table $t 0 externref) + + (func (export "get") (param $i i32) (result externref) (table.get $t (local.get $i))) + (func (export "set") (param $i i32) (param $r externref) (table.set $t (local.get $i) (local.get $r))) + + (func (export "grow") (param $sz i32) (param $init externref) (result i32) + (table.grow $t (local.get $init) (local.get $sz)) + ) + (func (export "size") (result i32) (table.size $t)) +) + +(assert_return (invoke "size") (i32.const 0)) +(assert_trap (invoke "set" (i32.const 0) (ref.extern 2)) "out of bounds table access") +(assert_trap (invoke "get" (i32.const 0)) "out of bounds table access") + +(assert_return (invoke "grow" (i32.const 1) (ref.null extern)) (i32.const 0)) +(assert_return (invoke "size") (i32.const 1)) +(assert_return (invoke "get" (i32.const 0)) (ref.null extern)) +(assert_return (invoke "set" (i32.const 0) (ref.extern 2))) +(assert_return (invoke "get" (i32.const 0)) (ref.extern 2)) +(assert_trap (invoke "set" (i32.const 1) (ref.extern 2)) "out of bounds table access") +(assert_trap (invoke "get" (i32.const 1)) "out of bounds table access") + +(assert_return (invoke "grow" (i32.const 4) (ref.extern 3)) (i32.const 1)) +(assert_return (invoke "size") (i32.const 5)) +(assert_return (invoke "get" (i32.const 0)) (ref.extern 2)) +(assert_return (invoke "set" (i32.const 0) (ref.extern 2))) +(assert_return (invoke "get" (i32.const 0)) (ref.extern 2)) +(assert_return (invoke "get" (i32.const 1)) (ref.extern 3)) +(assert_return (invoke "get" (i32.const 4)) (ref.extern 3)) +(assert_return (invoke "set" (i32.const 4) (ref.extern 4))) +(assert_return (invoke "get" (i32.const 4)) (ref.extern 4)) +(assert_trap (invoke "set" (i32.const 5) (ref.extern 2)) "out of bounds table access") +(assert_trap (invoke "get" (i32.const 5)) "out of bounds table access") + + +;; Reject growing to size outside i32 value range +(module + (table $t 0x10 funcref) + (elem declare func $f) + (func $f (export "grow") (result i32) + (table.grow $t (ref.func $f) (i32.const 0xffff_fff0)) + ) +) + +(assert_return (invoke "grow") (i32.const -1)) + + +(module + (table $t 0 externref) + (func (export "grow") (param i32) (result i32) + (table.grow $t (ref.null extern) (local.get 0)) + ) +) + +(assert_return (invoke "grow" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 2)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 800)) (i32.const 3)) + + +(module + (table $t 0 10 externref) + (func (export "grow") (param i32) (result i32) + (table.grow $t (ref.null extern) (local.get 0)) + ) +) + +(assert_return (invoke "grow" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const 1)) +(assert_return (invoke "grow" (i32.const 2)) (i32.const 2)) +(assert_return (invoke "grow" (i32.const 6)) (i32.const 4)) +(assert_return (invoke "grow" (i32.const 0)) (i32.const 10)) +(assert_return (invoke "grow" (i32.const 1)) (i32.const -1)) +(assert_return (invoke "grow" (i32.const 0x10000)) (i32.const -1)) + + +(module + (table $t 10 funcref) + (func (export "grow") (param i32) (result i32) + (table.grow $t (ref.null func) (local.get 0)) + ) + (elem declare func 1) + (func (export "check-table-null") (param i32 i32) (result funcref) + (local funcref) + (local.set 2 (ref.func 1)) + (block + (loop + (local.set 2 (table.get $t (local.get 0))) + (br_if 1 (i32.eqz (ref.is_null (local.get 2)))) + (br_if 1 (i32.ge_u (local.get 0) (local.get 1))) + (local.set 0 (i32.add (local.get 0) (i32.const 1))) + (br_if 0 (i32.le_u (local.get 0) (local.get 1))) + ) + ) + (local.get 2) + ) +) + +(assert_return (invoke "check-table-null" (i32.const 0) (i32.const 9)) (ref.null func)) +(assert_return (invoke "grow" (i32.const 10)) (i32.const 10)) +(assert_return (invoke "check-table-null" (i32.const 0) (i32.const 19)) (ref.null func)) + + +;; Type errors + +(assert_invalid + (module + (table $t 0 externref) + (func $type-init-size-empty-vs-i32-externref (result i32) + (table.grow $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 externref) + (func $type-size-empty-vs-i32 (result i32) + (table.grow $t (ref.null extern)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 externref) + (func $type-init-empty-vs-externref (result i32) + (table.grow $t (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 externref) + (func $type-size-f32-vs-i32 (result i32) + (table.grow $t (ref.null extern) (f32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 0 funcref) + (func $type-init-externref-vs-funcref (param $r externref) (result i32) + (table.grow $t (local.get $r) (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 1 externref) + (func $type-result-i32-vs-empty + (table.grow $t (ref.null extern) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 1 externref) + (func $type-result-i32-vs-f32 (result f32) + (table.grow $t (ref.null extern) (i32.const 0)) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/table_init.wast b/runtime/unc-vm/tests/wast/spec/table_init.wast new file mode 100644 index 000000000..0b2d26f77 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/table_init.wast @@ -0,0 +1,2143 @@ +;; +;; Generated by ../meta/generate_table_init.js +;; DO NOT EDIT THIS FILE. CHANGE THE SOURCE AND REGENERATE. +;; + +(module + (func (export "ef0") (result i32) (i32.const 0)) + (func (export "ef1") (result i32) (i32.const 1)) + (func (export "ef2") (result i32) (i32.const 2)) + (func (export "ef3") (result i32) (i32.const 3)) + (func (export "ef4") (result i32) (i32.const 4)) +) +(register "a") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init $t0 1 (i32.const 7) (i32.const 0) (i32.const 4))) + (func (export "check") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_return (invoke "check" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 10)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init $t0 3 (i32.const 15) (i32.const 1) (i32.const 3))) + (func (export "check") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 9)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 17)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init $t0 1 (i32.const 7) (i32.const 0) (i32.const 4)) + (elem.drop 1) + (table.init $t0 3 (i32.const 15) (i32.const 1) (i32.const 3)) + (elem.drop 3) + (table.copy $t0 0 (i32.const 20) (i32.const 15) (i32.const 5)) + (table.copy $t0 0 (i32.const 21) (i32.const 29) (i32.const 1)) + (table.copy $t0 0 (i32.const 24) (i32.const 10) (i32.const 1)) + (table.copy $t0 0 (i32.const 13) (i32.const 11) (i32.const 4)) + (table.copy $t0 0 (i32.const 19) (i32.const 20) (i32.const 5))) + (func (export "check") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_return (invoke "check" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 10)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 13)) "uninitialized element") +(assert_return (invoke "check" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 17)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_return (invoke "check" (i32.const 19)) (i32.const 9)) +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_return (invoke "check" (i32.const 21)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_return (invoke "check" (i32.const 23)) (i32.const 8)) +(assert_return (invoke "check" (i32.const 24)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init $t1 1 (i32.const 7) (i32.const 0) (i32.const 4))) + (func (export "check") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_return (invoke "check" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 10)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 6)) +(assert_trap (invoke "check" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init $t1 3 (i32.const 15) (i32.const 1) (i32.const 3))) + (func (export "check") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 13)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 14)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 9)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 17)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init $t1 1 (i32.const 7) (i32.const 0) (i32.const 4)) + (elem.drop 1) + (table.init $t1 3 (i32.const 15) (i32.const 1) (i32.const 3)) + (elem.drop 3) + (table.copy $t1 1 (i32.const 20) (i32.const 15) (i32.const 5)) + (table.copy $t1 1 (i32.const 21) (i32.const 29) (i32.const 1)) + (table.copy $t1 1 (i32.const 24) (i32.const 10) (i32.const 1)) + (table.copy $t1 1 (i32.const 13) (i32.const 11) (i32.const 4)) + (table.copy $t1 1 (i32.const 19) (i32.const 20) (i32.const 5))) + (func (export "check") (param i32) (result i32) + (call_indirect $t1 (type 0) (local.get 0))) +) + +(invoke "test") +(assert_trap (invoke "check" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 1)) "uninitialized element") +(assert_return (invoke "check" (i32.const 2)) (i32.const 3)) +(assert_return (invoke "check" (i32.const 3)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 4)) (i32.const 4)) +(assert_return (invoke "check" (i32.const 5)) (i32.const 1)) +(assert_trap (invoke "check" (i32.const 6)) "uninitialized element") +(assert_return (invoke "check" (i32.const 7)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 8)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 9)) (i32.const 1)) +(assert_return (invoke "check" (i32.const 10)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 11)) "uninitialized element") +(assert_return (invoke "check" (i32.const 12)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 13)) "uninitialized element") +(assert_return (invoke "check" (i32.const 14)) (i32.const 7)) +(assert_return (invoke "check" (i32.const 15)) (i32.const 5)) +(assert_return (invoke "check" (i32.const 16)) (i32.const 2)) +(assert_return (invoke "check" (i32.const 17)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 18)) "uninitialized element") +(assert_return (invoke "check" (i32.const 19)) (i32.const 9)) +(assert_trap (invoke "check" (i32.const 20)) "uninitialized element") +(assert_return (invoke "check" (i32.const 21)) (i32.const 7)) +(assert_trap (invoke "check" (i32.const 22)) "uninitialized element") +(assert_return (invoke "check" (i32.const 23)) (i32.const 8)) +(assert_return (invoke "check" (i32.const 24)) (i32.const 8)) +(assert_trap (invoke "check" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "check" (i32.const 29)) "uninitialized element") +(assert_invalid + (module + (func (export "test") + (elem.drop 0))) + "unknown elem segment 0") + +(assert_invalid + (module + (func (export "test") + (table.init 0 (i32.const 12) (i32.const 1) (i32.const 1)))) + "unknown table 0") + +(assert_invalid + (module + (elem funcref (ref.func 0)) + (func (result i32) (i32.const 0)) + (func (export "test") + (elem.drop 4))) + "unknown elem segment 4") + +(assert_invalid + (module + (elem funcref (ref.func 0)) + (func (result i32) (i32.const 0)) + (func (export "test") + (table.init 4 (i32.const 12) (i32.const 1) (i32.const 1)))) + "unknown table 0") + + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (elem.drop 2) + )) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 2 (i32.const 12) (i32.const 1) (i32.const 1)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 1) (i32.const 1)) + (table.init 1 (i32.const 21) (i32.const 1) (i32.const 1)))) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (elem.drop 1) + (elem.drop 1))) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (elem.drop 1) + (table.init 1 (i32.const 12) (i32.const 1) (i32.const 1)))) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 0) (i32.const 5)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init 1 (i32.const 12) (i32.const 2) (i32.const 3)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t0 1 (i32.const 28) (i32.const 1) (i32.const 3)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t0 1 (i32.const 12) (i32.const 4) (i32.const 0)) + )) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t0 1 (i32.const 12) (i32.const 5) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t0 1 (i32.const 30) (i32.const 2) (i32.const 0)) + )) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t0 1 (i32.const 31) (i32.const 2) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t0 1 (i32.const 30) (i32.const 4) (i32.const 0)) + )) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t0 1 (i32.const 31) (i32.const 5) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t1 1 (i32.const 26) (i32.const 1) (i32.const 3)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t1 1 (i32.const 12) (i32.const 4) (i32.const 0)) + )) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t1 1 (i32.const 12) (i32.const 5) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t1 1 (i32.const 28) (i32.const 2) (i32.const 0)) + )) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t1 1 (i32.const 29) (i32.const 2) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t1 1 (i32.const 28) (i32.const 4) (i32.const 0)) + )) +(invoke "test") + +(module + (table $t0 30 30 funcref) + (table $t1 28 28 funcref) + (elem (table $t1) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t1) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 0)) + (func (result i32) (i32.const 1)) + (func (result i32) (i32.const 2)) + (func (result i32) (i32.const 3)) + (func (result i32) (i32.const 4)) + (func (result i32) (i32.const 5)) + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) + (func (export "test") + (table.init $t1 1 (i32.const 29) (i32.const 5) (i32.const 0)) + )) +(assert_trap (invoke "test") "out of bounds table access") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i32.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f32.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (i64.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f32.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (i64.const 1) (f64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (i32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (f32.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (i64.const 1)))) + "type mismatch") + +(assert_invalid + (module + (table 10 funcref) + (elem funcref (ref.func $f0) (ref.func $f0) (ref.func $f0)) + (func $f0) + (func (export "test") + (table.init 0 (f64.const 1) (f64.const 1) (f64.const 1)))) + "type mismatch") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 24) (i32.const 16)) "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 32 64 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 25) (i32.const 16)) "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") + +(module + (type (func (result i32))) + (table 160 320 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 96) (i32.const 32)) "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 112)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 113)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 114)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 115)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 116)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 117)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 118)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 119)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 120)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 121)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 122)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 123)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 124)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 125)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 126)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 127)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 128)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 129)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 130)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 131)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 132)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 133)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 134)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 135)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 136)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 137)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 138)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 139)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 140)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 141)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 142)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 143)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 144)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 145)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 146)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 147)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 148)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 149)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 150)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 151)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 152)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 153)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 154)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 155)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 156)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 157)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 158)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 159)) "uninitialized element") + +(module + (type (func (result i32))) + (table 160 320 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 97) (i32.const 31)) "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 64)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 65)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 66)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 67)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 68)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 69)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 70)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 71)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 72)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 73)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 74)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 75)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 76)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 77)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 78)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 79)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 80)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 81)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 82)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 83)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 84)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 85)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 86)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 87)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 88)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 89)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 90)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 91)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 92)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 93)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 94)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 95)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 96)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 97)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 98)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 99)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 100)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 101)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 102)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 103)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 104)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 105)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 106)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 107)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 108)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 109)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 110)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 111)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 112)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 113)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 114)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 115)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 116)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 117)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 118)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 119)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 120)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 121)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 122)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 123)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 124)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 125)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 126)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 127)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 128)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 129)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 130)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 131)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 132)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 133)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 134)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 135)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 136)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 137)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 138)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 139)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 140)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 141)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 142)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 143)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 144)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 145)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 146)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 147)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 148)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 149)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 150)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 151)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 152)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 153)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 154)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 155)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 156)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 157)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 158)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 159)) "uninitialized element") + +(module + (type (func (result i32))) + (table 64 64 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 0) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 48) (i32.const 4294967280)) "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 16)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 17)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 18)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 19)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 20)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 21)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 22)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 23)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 24)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 25)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 26)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 27)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 28)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 29)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 30)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 31)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 32)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 33)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 34)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 35)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 36)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 37)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 38)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 39)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 40)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 41)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 42)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 43)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 44)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 45)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 46)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 47)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 48)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 49)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 50)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 51)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 52)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 53)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 54)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 55)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 56)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 57)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 58)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 59)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 60)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 61)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 62)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 63)) "uninitialized element") + +(module + (type (func (result i32))) + (table 16 16 funcref) + (elem funcref + (ref.func $f0) (ref.func $f1) (ref.func $f2) (ref.func $f3) + (ref.func $f4) (ref.func $f5) (ref.func $f6) (ref.func $f7) + (ref.func $f8) (ref.func $f9) (ref.func $f10) (ref.func $f11) + (ref.func $f12) (ref.func $f13) (ref.func $f14) (ref.func $f15)) + (func $f0 (export "f0") (result i32) (i32.const 0)) + (func $f1 (export "f1") (result i32) (i32.const 1)) + (func $f2 (export "f2") (result i32) (i32.const 2)) + (func $f3 (export "f3") (result i32) (i32.const 3)) + (func $f4 (export "f4") (result i32) (i32.const 4)) + (func $f5 (export "f5") (result i32) (i32.const 5)) + (func $f6 (export "f6") (result i32) (i32.const 6)) + (func $f7 (export "f7") (result i32) (i32.const 7)) + (func $f8 (export "f8") (result i32) (i32.const 8)) + (func $f9 (export "f9") (result i32) (i32.const 9)) + (func $f10 (export "f10") (result i32) (i32.const 10)) + (func $f11 (export "f11") (result i32) (i32.const 11)) + (func $f12 (export "f12") (result i32) (i32.const 12)) + (func $f13 (export "f13") (result i32) (i32.const 13)) + (func $f14 (export "f14") (result i32) (i32.const 14)) + (func $f15 (export "f15") (result i32) (i32.const 15)) + (func (export "test") (param $n i32) (result i32) + (call_indirect (type 0) (local.get $n))) + (func (export "run") (param $offs i32) (param $len i32) + (table.init 0 (local.get $offs) (i32.const 8) (local.get $len)))) +(assert_trap (invoke "run" (i32.const 0) (i32.const 4294967292)) "out of bounds table access") +(assert_trap (invoke "test" (i32.const 0)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 1)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 2)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 3)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 4)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 5)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 6)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 7)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 8)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 9)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 10)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 11)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 12)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 13)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 14)) "uninitialized element") +(assert_trap (invoke "test" (i32.const 15)) "uninitialized element") + +(module + (table 1 funcref) + ;; 65 elem segments. 64 is the smallest positive number that is encoded + ;; differently as a signed LEB. + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) (elem funcref) (elem funcref) (elem funcref) + (elem funcref) + (func (table.init 64 (i32.const 0) (i32.const 0) (i32.const 0)))) diff --git a/runtime/unc-vm/tests/wast/spec/table_set.wast b/runtime/unc-vm/tests/wast/spec/table_set.wast new file mode 100644 index 000000000..5a9cfa371 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/table_set.wast @@ -0,0 +1,119 @@ +(module + (table $t2 1 externref) + (table $t3 2 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + + (func (export "get-externref") (param $i i32) (result externref) + (table.get $t2 (local.get $i)) + ) + (func $f3 (export "get-funcref") (param $i i32) (result funcref) + (table.get $t3 (local.get $i)) + ) + + (func (export "set-externref") (param $i i32) (param $r externref) + (table.set $t2 (local.get $i) (local.get $r)) + ) + (func (export "set-funcref") (param $i i32) (param $r funcref) + (table.set $t3 (local.get $i) (local.get $r)) + ) + (func (export "set-funcref-from") (param $i i32) (param $j i32) + (table.set $t3 (local.get $i) (table.get $t3 (local.get $j))) + ) + + (func (export "is_null-funcref") (param $i i32) (result i32) + (ref.is_null (call $f3 (local.get $i))) + ) +) + +(assert_return (invoke "get-externref" (i32.const 0)) (ref.null extern)) +(assert_return (invoke "set-externref" (i32.const 0) (ref.extern 1))) +(assert_return (invoke "get-externref" (i32.const 0)) (ref.extern 1)) +(assert_return (invoke "set-externref" (i32.const 0) (ref.null extern))) +(assert_return (invoke "get-externref" (i32.const 0)) (ref.null extern)) + +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func)) +(assert_return (invoke "set-funcref-from" (i32.const 0) (i32.const 1))) +(assert_return (invoke "is_null-funcref" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "set-funcref" (i32.const 0) (ref.null func))) +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func)) + +(assert_trap (invoke "set-externref" (i32.const 2) (ref.null extern)) "out of bounds table access") +(assert_trap (invoke "set-funcref" (i32.const 3) (ref.null func)) "out of bounds table access") +(assert_trap (invoke "set-externref" (i32.const -1) (ref.null extern)) "out of bounds table access") +(assert_trap (invoke "set-funcref" (i32.const -1) (ref.null func)) "out of bounds table access") + +(assert_trap (invoke "set-externref" (i32.const 2) (ref.extern 0)) "out of bounds table access") +(assert_trap (invoke "set-funcref-from" (i32.const 3) (i32.const 1)) "out of bounds table access") +(assert_trap (invoke "set-externref" (i32.const -1) (ref.extern 0)) "out of bounds table access") +(assert_trap (invoke "set-funcref-from" (i32.const -1) (i32.const 1)) "out of bounds table access") + + +;; Type errors + +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-value-empty-vs-i32-externref + (table.set $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-empty-vs-i32 + (table.set $t (ref.null extern)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-value-empty-vs-externref + (table.set $t (i32.const 1)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-size-f32-vs-i32 + (table.set $t (f32.const 1) (ref.null extern)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 funcref) + (func $type-value-externref-vs-funcref (param $r externref) + (table.set $t (i32.const 1) (local.get $r)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t1 1 externref) + (table $t2 1 funcref) + (func $type-value-externref-vs-funcref-multi (param $r externref) + (table.set $t2 (i32.const 0) (local.get $r)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 10 externref) + (func $type-result-empty-vs-num (result i32) + (table.set $t (i32.const 0) (ref.null extern)) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/table_size.wast b/runtime/unc-vm/tests/wast/spec/table_size.wast new file mode 100644 index 000000000..ad293b5ee --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/table_size.wast @@ -0,0 +1,86 @@ +(module + (table $t0 0 externref) + (table $t1 1 externref) + (table $t2 0 2 externref) + (table $t3 3 8 externref) + + (func (export "size-t0") (result i32) (table.size $t0)) + (func (export "size-t1") (result i32) (table.size $t1)) + (func (export "size-t2") (result i32) (table.size $t2)) + (func (export "size-t3") (result i32) (table.size $t3)) + + (func (export "grow-t0") (param $sz i32) + (drop (table.grow $t0 (ref.null extern) (local.get $sz))) + ) + (func (export "grow-t1") (param $sz i32) + (drop (table.grow $t1 (ref.null extern) (local.get $sz))) + ) + (func (export "grow-t2") (param $sz i32) + (drop (table.grow $t2 (ref.null extern) (local.get $sz))) + ) + (func (export "grow-t3") (param $sz i32) + (drop (table.grow $t3 (ref.null extern) (local.get $sz))) + ) +) + +(assert_return (invoke "size-t0") (i32.const 0)) +(assert_return (invoke "grow-t0" (i32.const 1))) +(assert_return (invoke "size-t0") (i32.const 1)) +(assert_return (invoke "grow-t0" (i32.const 4))) +(assert_return (invoke "size-t0") (i32.const 5)) +(assert_return (invoke "grow-t0" (i32.const 0))) +(assert_return (invoke "size-t0") (i32.const 5)) + +(assert_return (invoke "size-t1") (i32.const 1)) +(assert_return (invoke "grow-t1" (i32.const 1))) +(assert_return (invoke "size-t1") (i32.const 2)) +(assert_return (invoke "grow-t1" (i32.const 4))) +(assert_return (invoke "size-t1") (i32.const 6)) +(assert_return (invoke "grow-t1" (i32.const 0))) +(assert_return (invoke "size-t1") (i32.const 6)) + +(assert_return (invoke "size-t2") (i32.const 0)) +(assert_return (invoke "grow-t2" (i32.const 3))) +(assert_return (invoke "size-t2") (i32.const 0)) +(assert_return (invoke "grow-t2" (i32.const 1))) +(assert_return (invoke "size-t2") (i32.const 1)) +(assert_return (invoke "grow-t2" (i32.const 0))) +(assert_return (invoke "size-t2") (i32.const 1)) +(assert_return (invoke "grow-t2" (i32.const 4))) +(assert_return (invoke "size-t2") (i32.const 1)) +(assert_return (invoke "grow-t2" (i32.const 1))) +(assert_return (invoke "size-t2") (i32.const 2)) + +(assert_return (invoke "size-t3") (i32.const 3)) +(assert_return (invoke "grow-t3" (i32.const 1))) +(assert_return (invoke "size-t3") (i32.const 4)) +(assert_return (invoke "grow-t3" (i32.const 3))) +(assert_return (invoke "size-t3") (i32.const 7)) +(assert_return (invoke "grow-t3" (i32.const 0))) +(assert_return (invoke "size-t3") (i32.const 7)) +(assert_return (invoke "grow-t3" (i32.const 2))) +(assert_return (invoke "size-t3") (i32.const 7)) +(assert_return (invoke "grow-t3" (i32.const 1))) +(assert_return (invoke "size-t3") (i32.const 8)) + + +;; Type errors + +(assert_invalid + (module + (table $t 1 externref) + (func $type-result-i32-vs-empty + (table.size $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 1 externref) + (func $type-result-i32-vs-f32 (result f32) + (table.size $t) + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/token.wast b/runtime/unc-vm/tests/wast/spec/token.wast new file mode 100644 index 000000000..1dcd32e7f --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/token.wast @@ -0,0 +1,10 @@ +;; Test tokenization + +(assert_malformed + (module quote "(func (drop (i32.const0)))") + "unknown operator" +) +(assert_malformed + (module quote "(func br 0drop)") + "unknown operator" +) diff --git a/runtime/unc-vm/tests/wast/spec/traps.wast b/runtime/unc-vm/tests/wast/spec/traps.wast new file mode 100644 index 000000000..142fa22b2 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/traps.wast @@ -0,0 +1,91 @@ +;; Test that traps are preserved even in instructions which might otherwise +;; be dead-code-eliminated. These functions all perform an operation and +;; discard its return value. + +(module + (func (export "no_dce.i32.div_s") (param $x i32) (param $y i32) + (drop (i32.div_s (local.get $x) (local.get $y)))) + (func (export "no_dce.i32.div_u") (param $x i32) (param $y i32) + (drop (i32.div_u (local.get $x) (local.get $y)))) + (func (export "no_dce.i64.div_s") (param $x i64) (param $y i64) + (drop (i64.div_s (local.get $x) (local.get $y)))) + (func (export "no_dce.i64.div_u") (param $x i64) (param $y i64) + (drop (i64.div_u (local.get $x) (local.get $y)))) +) + +(assert_trap (invoke "no_dce.i32.div_s" (i32.const 1) (i32.const 0)) "integer divide by zero") +(assert_trap (invoke "no_dce.i32.div_u" (i32.const 1) (i32.const 0)) "integer divide by zero") +(assert_trap (invoke "no_dce.i64.div_s" (i64.const 1) (i64.const 0)) "integer divide by zero") +(assert_trap (invoke "no_dce.i64.div_u" (i64.const 1) (i64.const 0)) "integer divide by zero") +(assert_trap (invoke "no_dce.i32.div_s" (i32.const 0x80000000) (i32.const -1)) "integer overflow") +(assert_trap (invoke "no_dce.i64.div_s" (i64.const 0x8000000000000000) (i64.const -1)) "integer overflow") + +(module + (func (export "no_dce.i32.rem_s") (param $x i32) (param $y i32) + (drop (i32.rem_s (local.get $x) (local.get $y)))) + (func (export "no_dce.i32.rem_u") (param $x i32) (param $y i32) + (drop (i32.rem_u (local.get $x) (local.get $y)))) + (func (export "no_dce.i64.rem_s") (param $x i64) (param $y i64) + (drop (i64.rem_s (local.get $x) (local.get $y)))) + (func (export "no_dce.i64.rem_u") (param $x i64) (param $y i64) + (drop (i64.rem_u (local.get $x) (local.get $y)))) +) + +(assert_trap (invoke "no_dce.i32.rem_s" (i32.const 1) (i32.const 0)) "integer divide by zero") +(assert_trap (invoke "no_dce.i32.rem_u" (i32.const 1) (i32.const 0)) "integer divide by zero") +(assert_trap (invoke "no_dce.i64.rem_s" (i64.const 1) (i64.const 0)) "integer divide by zero") +(assert_trap (invoke "no_dce.i64.rem_u" (i64.const 1) (i64.const 0)) "integer divide by zero") + +(module + (func (export "no_dce.i32.trunc_f32_s") (param $x f32) (drop (i32.trunc_f32_s (local.get $x)))) + (func (export "no_dce.i32.trunc_f32_u") (param $x f32) (drop (i32.trunc_f32_u (local.get $x)))) + (func (export "no_dce.i32.trunc_f64_s") (param $x f64) (drop (i32.trunc_f64_s (local.get $x)))) + (func (export "no_dce.i32.trunc_f64_u") (param $x f64) (drop (i32.trunc_f64_u (local.get $x)))) + (func (export "no_dce.i64.trunc_f32_s") (param $x f32) (drop (i64.trunc_f32_s (local.get $x)))) + (func (export "no_dce.i64.trunc_f32_u") (param $x f32) (drop (i64.trunc_f32_u (local.get $x)))) + (func (export "no_dce.i64.trunc_f64_s") (param $x f64) (drop (i64.trunc_f64_s (local.get $x)))) + (func (export "no_dce.i64.trunc_f64_u") (param $x f64) (drop (i64.trunc_f64_u (local.get $x)))) +) + +(assert_trap (invoke "no_dce.i32.trunc_f32_s" (f32.const nan)) "invalid conversion to integer") +(assert_trap (invoke "no_dce.i32.trunc_f32_u" (f32.const nan)) "invalid conversion to integer") +(assert_trap (invoke "no_dce.i32.trunc_f64_s" (f64.const nan)) "invalid conversion to integer") +(assert_trap (invoke "no_dce.i32.trunc_f64_u" (f64.const nan)) "invalid conversion to integer") +(assert_trap (invoke "no_dce.i64.trunc_f32_s" (f32.const nan)) "invalid conversion to integer") +(assert_trap (invoke "no_dce.i64.trunc_f32_u" (f32.const nan)) "invalid conversion to integer") +(assert_trap (invoke "no_dce.i64.trunc_f64_s" (f64.const nan)) "invalid conversion to integer") +(assert_trap (invoke "no_dce.i64.trunc_f64_u" (f64.const nan)) "invalid conversion to integer") + +(module + (memory 1) + + (func (export "no_dce.i32.load") (param $i i32) (drop (i32.load (local.get $i)))) + (func (export "no_dce.i32.load16_s") (param $i i32) (drop (i32.load16_s (local.get $i)))) + (func (export "no_dce.i32.load16_u") (param $i i32) (drop (i32.load16_u (local.get $i)))) + (func (export "no_dce.i32.load8_s") (param $i i32) (drop (i32.load8_s (local.get $i)))) + (func (export "no_dce.i32.load8_u") (param $i i32) (drop (i32.load8_u (local.get $i)))) + (func (export "no_dce.i64.load") (param $i i32) (drop (i64.load (local.get $i)))) + (func (export "no_dce.i64.load32_s") (param $i i32) (drop (i64.load32_s (local.get $i)))) + (func (export "no_dce.i64.load32_u") (param $i i32) (drop (i64.load32_u (local.get $i)))) + (func (export "no_dce.i64.load16_s") (param $i i32) (drop (i64.load16_s (local.get $i)))) + (func (export "no_dce.i64.load16_u") (param $i i32) (drop (i64.load16_u (local.get $i)))) + (func (export "no_dce.i64.load8_s") (param $i i32) (drop (i64.load8_s (local.get $i)))) + (func (export "no_dce.i64.load8_u") (param $i i32) (drop (i64.load8_u (local.get $i)))) + (func (export "no_dce.f32.load") (param $i i32) (drop (f32.load (local.get $i)))) + (func (export "no_dce.f64.load") (param $i i32) (drop (f64.load (local.get $i)))) +) + +(assert_trap (invoke "no_dce.i32.load" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "no_dce.i32.load16_s" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "no_dce.i32.load16_u" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "no_dce.i32.load8_s" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "no_dce.i32.load8_u" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "no_dce.i64.load" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "no_dce.i64.load32_s" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "no_dce.i64.load32_u" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "no_dce.i64.load16_s" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "no_dce.i64.load16_u" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "no_dce.i64.load8_s" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "no_dce.i64.load8_u" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "no_dce.f32.load" (i32.const 65536)) "out of bounds memory access") +(assert_trap (invoke "no_dce.f64.load" (i32.const 65536)) "out of bounds memory access") diff --git a/runtime/unc-vm/tests/wast/spec/type.wast b/runtime/unc-vm/tests/wast/spec/type.wast new file mode 100644 index 000000000..b94063e6a --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/type.wast @@ -0,0 +1,50 @@ +;; Test type definitions + +(module + (type (func)) + (type $t (func)) + + (type (func (param i32))) + (type (func (param $x i32))) + (type (func (result i32))) + (type (func (param i32) (result i32))) + (type (func (param $x i32) (result i32))) + + (type (func (param f32 f64))) + (type (func (result i64 f32))) + (type (func (param i32 i64) (result f32 f64))) + + (type (func (param f32) (param f64))) + (type (func (param $x f32) (param f64))) + (type (func (param f32) (param $y f64))) + (type (func (param $x f32) (param $y f64))) + (type (func (result i64) (result f32))) + (type (func (param i32) (param i64) (result f32) (result f64))) + (type (func (param $x i32) (param $y i64) (result f32) (result f64))) + + (type (func (param f32 f64) (param $x i32) (param f64 i32 i32))) + (type (func (result i64 i64 f32) (result f32 i32))) + (type + (func (param i32 i32) (param i64 i32) (result f32 f64) (result f64 i32)) + ) + + (type (func (param) (param $x f32) (param) (param) (param f64 i32) (param))) + (type + (func (result) (result) (result i64 i64) (result) (result f32) (result)) + ) + (type + (func + (param i32 i32) (param i64 i32) (param) (param $x i32) (param) + (result) (result f32 f64) (result f64 i32) (result) + ) + ) +) + +(assert_malformed + (module quote "(type (func (result i32) (param i32)))") + "result before parameter" +) +(assert_malformed + (module quote "(type (func (result $x i32)))") + "unexpected token" +) diff --git a/runtime/unc-vm/tests/wast/spec/unreachable.wast b/runtime/unc-vm/tests/wast/spec/unreachable.wast new file mode 100644 index 000000000..3074847a3 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/unreachable.wast @@ -0,0 +1,304 @@ +;; Test `unreachable` operator + +(module + ;; Auxiliary definitions + (func $dummy) + (func $dummy3 (param i32 i32 i32)) + + (func (export "type-i32") (result i32) (unreachable)) + (func (export "type-i64") (result i32) (unreachable)) + (func (export "type-f32") (result f64) (unreachable)) + (func (export "type-f64") (result f64) (unreachable)) + + (func (export "as-func-first") (result i32) + (unreachable) (i32.const -1) + ) + (func (export "as-func-mid") (result i32) + (call $dummy) (unreachable) (i32.const -1) + ) + (func (export "as-func-last") + (call $dummy) (unreachable) + ) + (func (export "as-func-value") (result i32) + (call $dummy) (unreachable) + ) + + (func (export "as-block-first") (result i32) + (block (result i32) (unreachable) (i32.const 2)) + ) + (func (export "as-block-mid") (result i32) + (block (result i32) (call $dummy) (unreachable) (i32.const 2)) + ) + (func (export "as-block-last") + (block (nop) (call $dummy) (unreachable)) + ) + (func (export "as-block-value") (result i32) + (block (result i32) (nop) (call $dummy) (unreachable)) + ) + (func (export "as-block-broke") (result i32) + (block (result i32) (call $dummy) (br 0 (i32.const 1)) (unreachable)) + ) + + (func (export "as-loop-first") (result i32) + (loop (result i32) (unreachable) (i32.const 2)) + ) + (func (export "as-loop-mid") (result i32) + (loop (result i32) (call $dummy) (unreachable) (i32.const 2)) + ) + (func (export "as-loop-last") + (loop (nop) (call $dummy) (unreachable)) + ) + (func (export "as-loop-broke") (result i32) + (block (result i32) + (loop (result i32) (call $dummy) (br 1 (i32.const 1)) (unreachable)) + ) + ) + + (func (export "as-br-value") (result i32) + (block (result i32) (br 0 (unreachable))) + ) + + (func (export "as-br_if-cond") + (block (br_if 0 (unreachable))) + ) + (func (export "as-br_if-value") (result i32) + (block (result i32) + (drop (br_if 0 (unreachable) (i32.const 1))) (i32.const 7) + ) + ) + (func (export "as-br_if-value-cond") (result i32) + (block (result i32) + (drop (br_if 0 (i32.const 6) (unreachable))) (i32.const 7) + ) + ) + + (func (export "as-br_table-index") + (block (br_table 0 0 0 (unreachable))) + ) + (func (export "as-br_table-value") (result i32) + (block (result i32) + (br_table 0 0 0 (unreachable) (i32.const 1)) (i32.const 7) + ) + ) + (func (export "as-br_table-value-2") (result i32) + (block (result i32) + (block (result i32) (br_table 0 1 (unreachable) (i32.const 1))) + ) + ) + (func (export "as-br_table-value-index") (result i32) + (block (result i32) + (br_table 0 0 (i32.const 6) (unreachable)) (i32.const 7) + ) + ) + (func (export "as-br_table-value-and-index") (result i32) + (block (result i32) (br_table 0 0 (unreachable)) (i32.const 8)) + ) + + (func (export "as-return-value") (result i64) + (return (unreachable)) + ) + + (func (export "as-if-cond") (result i32) + (if (result i32) (unreachable) (then (i32.const 0)) (else (i32.const 1))) + ) + (func (export "as-if-then") (param i32 i32) (result i32) + (if (result i32) (local.get 0) (then (unreachable)) (else (local.get 1))) + ) + (func (export "as-if-else") (param i32 i32) (result i32) + (if (result i32) (local.get 0) (then (local.get 1)) (else (unreachable))) + ) + (func (export "as-if-then-no-else") (param i32 i32) (result i32) + (if (local.get 0) (then (unreachable))) (local.get 1) + ) + + (func (export "as-select-first") (param i32 i32) (result i32) + (select (unreachable) (local.get 0) (local.get 1)) + ) + (func (export "as-select-second") (param i32 i32) (result i32) + (select (local.get 0) (unreachable) (local.get 1)) + ) + (func (export "as-select-cond") (result i32) + (select (i32.const 0) (i32.const 1) (unreachable)) + ) + + (func (export "as-call-first") + (call $dummy3 (unreachable) (i32.const 2) (i32.const 3)) + ) + (func (export "as-call-mid") + (call $dummy3 (i32.const 1) (unreachable) (i32.const 3)) + ) + (func (export "as-call-last") + (call $dummy3 (i32.const 1) (i32.const 2) (unreachable)) + ) + + (type $sig (func (param i32 i32 i32))) + (table funcref (elem $dummy3)) + (func (export "as-call_indirect-func") + (call_indirect (type $sig) + (unreachable) (i32.const 1) (i32.const 2) (i32.const 3) + ) + ) + (func (export "as-call_indirect-first") + (call_indirect (type $sig) + (i32.const 0) (unreachable) (i32.const 2) (i32.const 3) + ) + ) + (func (export "as-call_indirect-mid") + (call_indirect (type $sig) + (i32.const 0) (i32.const 1) (unreachable) (i32.const 3) + ) + ) + (func (export "as-call_indirect-last") + (call_indirect (type $sig) + (i32.const 0) (i32.const 1) (i32.const 2) (unreachable) + ) + ) + + (func (export "as-local.set-value") (local f32) + (local.set 0 (unreachable)) + ) + (func (export "as-local.tee-value") (result f32) (local f32) + (local.tee 0 (unreachable)) + ) + (global $a (mut f32) (f32.const 0)) + (func (export "as-global.set-value") (result f32) + (global.set $a (unreachable)) + ) + + (memory 1) + (func (export "as-load-address") (result f32) + (f32.load (unreachable)) + ) + (func (export "as-loadN-address") (result i64) + (i64.load8_s (unreachable)) + ) + + (func (export "as-store-address") + (f64.store (unreachable) (f64.const 7)) + ) + (func (export "as-store-value") + (i64.store (i32.const 2) (unreachable)) + ) + + (func (export "as-storeN-address") + (i32.store8 (unreachable) (i32.const 7)) + ) + (func (export "as-storeN-value") + (i64.store16 (i32.const 2) (unreachable)) + ) + + (func (export "as-unary-operand") (result f32) + (f32.neg (unreachable)) + ) + + (func (export "as-binary-left") (result i32) + (i32.add (unreachable) (i32.const 10)) + ) + (func (export "as-binary-right") (result i64) + (i64.sub (i64.const 10) (unreachable)) + ) + + (func (export "as-test-operand") (result i32) + (i32.eqz (unreachable)) + ) + + (func (export "as-compare-left") (result i32) + (f64.le (unreachable) (f64.const 10)) + ) + (func (export "as-compare-right") (result i32) + (f32.ne (f32.const 10) (unreachable)) + ) + + (func (export "as-convert-operand") (result i32) + (i32.wrap_i64 (unreachable)) + ) + + (func (export "as-memory.grow-size") (result i32) + (memory.grow (unreachable)) + ) +) + +(assert_trap (invoke "type-i32") "unreachable") +(assert_trap (invoke "type-i64") "unreachable") +(assert_trap (invoke "type-f32") "unreachable") +(assert_trap (invoke "type-f64") "unreachable") + +(assert_trap (invoke "as-func-first") "unreachable") +(assert_trap (invoke "as-func-mid") "unreachable") +(assert_trap (invoke "as-func-last") "unreachable") +(assert_trap (invoke "as-func-value") "unreachable") + +(assert_trap (invoke "as-block-first") "unreachable") +(assert_trap (invoke "as-block-mid") "unreachable") +(assert_trap (invoke "as-block-last") "unreachable") +(assert_trap (invoke "as-block-value") "unreachable") +(assert_return (invoke "as-block-broke") (i32.const 1)) + +(assert_trap (invoke "as-loop-first") "unreachable") +(assert_trap (invoke "as-loop-mid") "unreachable") +(assert_trap (invoke "as-loop-last") "unreachable") +(assert_return (invoke "as-loop-broke") (i32.const 1)) + +(assert_trap (invoke "as-br-value") "unreachable") + +(assert_trap (invoke "as-br_if-cond") "unreachable") +(assert_trap (invoke "as-br_if-value") "unreachable") +(assert_trap (invoke "as-br_if-value-cond") "unreachable") + +(assert_trap (invoke "as-br_table-index") "unreachable") +(assert_trap (invoke "as-br_table-value") "unreachable") +(assert_trap (invoke "as-br_table-value-2") "unreachable") +(assert_trap (invoke "as-br_table-value-index") "unreachable") +(assert_trap (invoke "as-br_table-value-and-index") "unreachable") + +(assert_trap (invoke "as-return-value") "unreachable") + +(assert_trap (invoke "as-if-cond") "unreachable") +(assert_trap (invoke "as-if-then" (i32.const 1) (i32.const 6)) "unreachable") +(assert_return (invoke "as-if-then" (i32.const 0) (i32.const 6)) (i32.const 6)) +(assert_trap (invoke "as-if-else" (i32.const 0) (i32.const 6)) "unreachable") +(assert_return (invoke "as-if-else" (i32.const 1) (i32.const 6)) (i32.const 6)) +(assert_trap (invoke "as-if-then-no-else" (i32.const 1) (i32.const 6)) "unreachable") +(assert_return (invoke "as-if-then-no-else" (i32.const 0) (i32.const 6)) (i32.const 6)) + +(assert_trap (invoke "as-select-first" (i32.const 0) (i32.const 6)) "unreachable") +(assert_trap (invoke "as-select-first" (i32.const 1) (i32.const 6)) "unreachable") +(assert_trap (invoke "as-select-second" (i32.const 0) (i32.const 6)) "unreachable") +(assert_trap (invoke "as-select-second" (i32.const 1) (i32.const 6)) "unreachable") +(assert_trap (invoke "as-select-cond") "unreachable") + +(assert_trap (invoke "as-call-first") "unreachable") +(assert_trap (invoke "as-call-mid") "unreachable") +(assert_trap (invoke "as-call-last") "unreachable") + +(assert_trap (invoke "as-call_indirect-func") "unreachable") +(assert_trap (invoke "as-call_indirect-first") "unreachable") +(assert_trap (invoke "as-call_indirect-mid") "unreachable") +(assert_trap (invoke "as-call_indirect-last") "unreachable") + +(assert_trap (invoke "as-local.set-value") "unreachable") +(assert_trap (invoke "as-local.tee-value") "unreachable") +(assert_trap (invoke "as-global.set-value") "unreachable") + +(assert_trap (invoke "as-load-address") "unreachable") +(assert_trap (invoke "as-loadN-address") "unreachable") + +(assert_trap (invoke "as-store-address") "unreachable") +(assert_trap (invoke "as-store-value") "unreachable") +(assert_trap (invoke "as-storeN-address") "unreachable") +(assert_trap (invoke "as-storeN-value") "unreachable") + +(assert_trap (invoke "as-unary-operand") "unreachable") + +(assert_trap (invoke "as-binary-left") "unreachable") +(assert_trap (invoke "as-binary-right") "unreachable") + +(assert_trap (invoke "as-test-operand") "unreachable") + +(assert_trap (invoke "as-compare-left") "unreachable") +(assert_trap (invoke "as-compare-right") "unreachable") + +(assert_trap (invoke "as-convert-operand") "unreachable") + +(assert_trap (invoke "as-memory.grow-size") "unreachable") + diff --git a/runtime/unc-vm/tests/wast/spec/unreached-invalid.wast b/runtime/unc-vm/tests/wast/spec/unreached-invalid.wast new file mode 100644 index 000000000..3ddd77385 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/unreached-invalid.wast @@ -0,0 +1,695 @@ +;; Failures in unreachable code. + +(assert_invalid + (module (func $local-index (unreachable) (drop (local.get 0)))) + "unknown local" +) +(assert_invalid + (module (func $global-index (unreachable) (drop (global.get 0)))) + "unknown global" +) +(assert_invalid + (module (func $func-index (unreachable) (call 1))) + "unknown function" +) +(assert_invalid + (module (func $label-index (unreachable) (br 1))) + "unknown label" +) + +(assert_invalid + (module (func $type-num-vs-num + (unreachable) (drop (i64.eqz (i32.const 0)))) + ) + "type mismatch" +) +(assert_invalid + (module (func $type-poly-num-vs-num (result i32) + (unreachable) (i64.const 0) (i32.const 0) (select) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-poly-transitive-num-vs-num (result i32) + (unreachable) + (i64.const 0) (i32.const 0) (select) + (i32.const 0) (i32.const 0) (select) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unconsumed-const (unreachable) (i32.const 0))) + "type mismatch" +) +(assert_invalid + (module (func $type-unconsumed-result (unreachable) (i32.eqz))) + "type mismatch" +) +(assert_invalid + (module (func $type-unconsumed-result2 + (unreachable) (i32.const 0) (i32.add) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unconsumed-poly0 (unreachable) (select))) + "type mismatch" +) +(assert_invalid + (module (func $type-unconsumed-poly1 (unreachable) (i32.const 0) (select))) + "type mismatch" +) +(assert_invalid + (module (func $type-unconsumed-poly2 + (unreachable) (i32.const 0) (i32.const 0) (select) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-after-break + (block (br 0) (block (drop (i32.eqz (nop))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-after-break + (block (br 0) (drop (i32.eqz (f32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-after-break + (block (br 0) (block (drop (f32.eq (i32.const 1))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-after-break + (block (br 0) (drop (f32.eq (i32.const 1) (f32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-after-break + (block (br 0) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-after-break (result i32) + (block (result i32) (i32.const 1) (br 0) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-void-after-break + (block (loop (br 1) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-num-after-break (result i32) + (loop (result i32) (br 1 (i32.const 1)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-void-after-break + (br 0) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-num-after-break (result i32) + (br 0 (i32.const 1)) (f32.const 0) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-after-return + (return) (block (drop (i32.eqz (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-after-return + (return) (drop (i32.eqz (f32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-after-return + (return) (block (drop (f32.eq (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-after-return + (return) (drop (f32.eq (i32.const 1) (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-after-return + (block (return) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-after-return (result i32) + (block (result i32) (i32.const 1) (return (i32.const 0)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-void-after-return + (block (loop (return) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-num-after-return (result i32) + (loop (result i32) (return (i32.const 1)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-void-after-return + (return) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-num-after-return (result i32) + (return (i32.const 1)) (f32.const 0) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-after-unreachable + (unreachable) (block (drop (i32.eqz (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-void-in-loop-after-unreachable + (unreachable) (loop (drop (i32.eqz (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-void-in-i32-loop-after-unreachable + (unreachable) (loop (result i32) (i32.eqz (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-after-unreachable + (unreachable) (drop (i32.eqz (f32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-after-unreachable + (unreachable) (block (drop (f32.eq (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-after-unreachable + (unreachable) (drop (f32.eq (i32.const 1) (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-after-unreachable + (block (unreachable) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-after-unreachable (result i32) + (block (result i32) (i32.const 1) (unreachable) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-void-after-unreachable + (block (loop (unreachable) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-num-after-unreachable (result i32) + (loop (result i32) (unreachable) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-void-after-unreachable + (unreachable) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-num-after-unreachable (result i32) + (unreachable) (f32.const 0) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-void-in-if-after-unreachable + (unreachable) (if (i32.const 0) (then (drop (i32.eqz (nop))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-void-in-else-after-unreachable + (unreachable) (if (i32.const 0) (then (nop)) (else (drop (i32.eqz (nop))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-void-in-else-after-unreachable-if + (if (i32.const 0) (then (unreachable)) (else (drop (i32.eqz (nop))))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-after-nested-unreachable + (block (unreachable)) (block (drop (i32.eqz (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-after-nested-unreachable + (block (unreachable)) (drop (i32.eqz (f32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-after-nested-unreachable + (block (unreachable)) (block (drop (f32.eq (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-after-nested-unreachable + (block (unreachable)) (drop (f32.eq (i32.const 1) (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-after-nested-unreachable + (block (block (unreachable)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-after-nested-unreachable + (result i32) + (block (result i32) (i32.const 1) (block (unreachable)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-void-after-nested-unreachable + (block (loop (block (unreachable)) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-num-after-nested-unreachable + (result i32) + (loop (result i32) (block (unreachable)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-void-after-nested-unreachable + (block (unreachable)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-num-after-nested-unreachable + (result i32) + (block (unreachable)) (f32.const 0) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-after-infinite-loop + (loop (br 0)) (block (drop (i32.eqz (nop)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-after-infinite-loop + (loop (br 0)) (drop (i32.eqz (f32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-after-infinite-loop + (loop (br 0)) (block (drop (f32.eq (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-after-infinite-loop + (loop (br 0)) (drop (f32.eq (i32.const 1) (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-after-infinite-loop + (block (loop (br 0)) (i32.const 1)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-after-infinite-loop (result i32) + (block (result i32) (i32.const 1) (loop (br 0)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-void-after-infinite-loop + (block (loop (loop (br 0)) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-num-vs-num-after-infinite-loop (result i32) + (loop (result i32) (loop (br 0)) (f32.const 0)) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-void-after-infinite-loop + (loop (br 0)) (i32.const 1) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-func-value-num-vs-num-after-infinite-loop (result i32) + (loop (br 0)) (f32.const 0) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-unary-num-vs-void-in-dead-body + (if (i32.const 0) (then (drop (i32.eqz (nop))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-unary-num-vs-num-in-dead-body + (if (i32.const 0) (then (drop (i32.eqz (f32.const 1))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-void-in-dead-body + (if (i32.const 0) (then (drop (f32.eq (i32.const 1))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-binary-num-vs-num-in-dead-body + (if (i32.const 0) (then (drop (f32.eq (i32.const 1) (f32.const 0))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-if-value-num-vs-void-in-dead-body + (if (i32.const 0) (then (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-if-value-num-vs-num-in-dead-body (result i32) + (if (result i32) (i32.const 0) (then (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-in-dead-body + (if (i32.const 0) (then (block (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-in-dead-body (result i32) + (if (result i32) (i32.const 0) (then (block (result i32) (f32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-void-in-dead-body + (if (i32.const 0) (then (loop (i32.const 1)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-num-vs-num-in-dead-body (result i32) + (if (result i32) (i32.const 0) (then (loop (result i32) (f32.const 0)))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-return-second-num-vs-num (result i32) + (return (i32.const 1)) (return (f64.const 1)) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-br-second-num-vs-num (result i32) + (block (result i32) (br 0 (i32.const 1)) (br 0 (f64.const 1))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-br_if-cond-num-vs-num-after-unreachable + (block (br_if 0 (unreachable) (f32.const 0))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_if-num-vs-void-after-unreachable (result i32) + (block (result i32) + (block (unreachable) (br_if 1 (i32.const 0) (i32.const 0))) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_if-num-vs-num-after-unreachable (result i32) + (block (result i32) + (block (result f32) (unreachable) (br_if 1 (i32.const 0) (i32.const 0))) + (drop) (i32.const 0) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_if-num2-vs-num-after-unreachable (result i32) + (block (result i32) + (unreachable) (br_if 0 (i32.const 0) (i32.const 0)) (i32.const 0) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_table-num-vs-num-after-unreachable + (block (br_table 0 (unreachable) (f32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_table-label-num-vs-num-after-unreachable (result i32) + (block (result i32) (unreachable) (br_table 0 (f32.const 0) (i32.const 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-br_table-label-num-vs-label-void-after-unreachable + (block + (block (result f32) + (unreachable) + (br_table 0 1 0 (i32.const 1)) + ) + (drop) + ) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-block-value-nested-unreachable-num-vs-void + (block (i32.const 3) (block (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-unreachable-void-vs-num (result i32) + (block (block (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-unreachable-num-vs-num (result i32) + (block (result i64) (i64.const 0) (block (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-unreachable-num2-vs-void (result i32) + (block (i32.const 3) (block (i64.const 1) (unreachable))) (i32.const 9) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-block-value-nested-br-num-vs-void + (block (i32.const 3) (block (br 1))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-br-void-vs-num (result i32) + (block (result i32) (block (br 1 (i32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-br-num-vs-num (result i32) + (block (result i32) (i64.const 0) (block (br 1 (i32.const 0)))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-block-value-nested2-br-num-vs-void + (block (block (i32.const 3) (block (br 2)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested2-br-void-vs-num (result i32) + (block (result i32) (block (block (br 2 (i32.const 0))))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested2-br-num-vs-num (result i32) + (block (result i32) + (block (result i64) (i64.const 0) (block (br 2 (i32.const 0)))) + ) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested2-br-num2-vs-void (result i32) + (block (i32.const 3) (block (i64.const 1) (br 1))) (i32.const 9) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-block-value-nested-return-num-vs-void + (block (i32.const 3) (block (return))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-return-void-vs-num (result i32) + (block (block (return (i32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-return-num-vs-num (result i32) + (block (result i64) (i64.const 0) (block (return (i32.const 0)))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-block-value-nested-return-num2-vs-void (result i32) + (block (i32.const 3) (block (i64.const 1) (return (i32.const 0)))) + (i32.const 9) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-loop-value-nested-unreachable-num-vs-void + (loop (i32.const 3) (block (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-nested-unreachable-void-vs-num (result i32) + (loop (block (unreachable))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-loop-value-nested-unreachable-num-vs-num (result i32) + (loop (result i64) (i64.const 0) (block (unreachable))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $type-cont-last-void-vs-empty (result i32) + (loop (br 0 (nop))) + )) + "type mismatch" +) +(assert_invalid + (module (func $type-cont-last-num-vs-empty (result i32) + (loop (br 0 (i32.const 0))) + )) + "type mismatch" +) + +(assert_invalid + (module (func $tee-local-unreachable-value + (local i32) + (local.tee 0 (unreachable)) + )) + "type mismatch" +) +(assert_invalid + (module (func $br_if-unreachable (result i32) + (block (result i32) + (block + (br_if 1 (unreachable) (i32.const 0)) + ) + (i32.const 0) + ) + )) + "type mismatch" +) +(assert_invalid + (module + (func $type-br_if-after-unreachable (result i64) + unreachable + br_if 0 + i64.extend_i32_u + ) + ) + "type mismatch" +) diff --git a/runtime/unc-vm/tests/wast/spec/unwind.wast b/runtime/unc-vm/tests/wast/spec/unwind.wast new file mode 100644 index 000000000..85db60b59 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/unwind.wast @@ -0,0 +1,267 @@ +;; Test that control-flow transfer unwinds stack and it can be anything after. + +(module + (func (export "func-unwind-by-unreachable") + (i32.const 3) (i64.const 1) (unreachable) + ) + (func (export "func-unwind-by-br") + (i32.const 3) (i64.const 1) (br 0) + ) + (func (export "func-unwind-by-br-value") (result i32) + (i32.const 3) (i64.const 1) (br 0 (i32.const 9)) + ) + (func (export "func-unwind-by-br_if") + (i32.const 3) (i64.const 1) (drop (drop (br_if 0 (i32.const 1)))) + ) + (func (export "func-unwind-by-br_if-value") (result i32) + (i32.const 3) (i64.const 1) (drop (drop (br_if 0 (i32.const 9) (i32.const 1)))) + ) + (func (export "func-unwind-by-br_table") + (i32.const 3) (i64.const 1) (br_table 0 (i32.const 0)) + ) + (func (export "func-unwind-by-br_table-value") (result i32) + (i32.const 3) (i64.const 1) (br_table 0 (i32.const 9) (i32.const 0)) + ) + (func (export "func-unwind-by-return") (result i32) + (i32.const 3) (i64.const 1) (return (i32.const 9)) + ) + + (func (export "block-unwind-by-unreachable") + (block (i32.const 3) (i64.const 1) (unreachable)) + ) + (func (export "block-unwind-by-br") (result i32) + (block (i32.const 3) (i64.const 1) (br 0)) (i32.const 9) + ) + (func (export "block-unwind-by-br-value") (result i32) + (block (result i32) (i32.const 3) (i64.const 1) (br 0 (i32.const 9))) + ) + (func (export "block-unwind-by-br_if") (result i32) + (block (i32.const 3) (i64.const 1) (drop (drop (br_if 0 (i32.const 1))))) (i32.const 9) + ) + (func (export "block-unwind-by-br_if-value") (result i32) + (block (result i32) + (i32.const 3) (i64.const 1) (drop (drop (br_if 0 (i32.const 9) (i32.const 1)))) + ) + ) + (func (export "block-unwind-by-br_table") (result i32) + (block (i32.const 3) (i64.const 1) (br_table 0 (i32.const 0))) (i32.const 9) + ) + (func (export "block-unwind-by-br_table-value") (result i32) + (block (result i32) + (i32.const 3) (i64.const 1) (br_table 0 (i32.const 9) (i32.const 0)) + ) + ) + (func (export "block-unwind-by-return") (result i32) + (block (result i32) (i32.const 3) (i64.const 1) (return (i32.const 9))) + ) + + (func (export "block-nested-unwind-by-unreachable") (result i32) + (block (result i32) (i32.const 3) (block (i64.const 1) (unreachable))) + ) + (func (export "block-nested-unwind-by-br") (result i32) + (block (i32.const 3) (block (i64.const 1) (br 1)) (drop)) (i32.const 9) + ) + (func (export "block-nested-unwind-by-br-value") (result i32) + (block (result i32) + (i32.const 3) (block (i64.const 1) (br 1 (i32.const 9))) + ) + ) + (func (export "block-nested-unwind-by-br_if") (result i32) + (block (i32.const 3) (block (i64.const 1) (drop (br_if 1 (i32.const 1)))) (drop)) (i32.const 9) + ) + (func (export "block-nested-unwind-by-br_if-value") (result i32) + (block (result i32) + (i32.const 3) (block (i64.const 1) (drop (drop (br_if 1 (i32.const 9) (i32.const 1))))) + ) + ) + (func (export "block-nested-unwind-by-br_table") (result i32) + (block + (i32.const 3) (block (i64.const 1) (br_table 1 (i32.const 1))) + (drop) + ) + (i32.const 9) + ) + (func (export "block-nested-unwind-by-br_table-value") (result i32) + (block (result i32) + (i32.const 3) + (block (i64.const 1) (br_table 1 (i32.const 9) (i32.const 1))) + ) + ) + (func (export "block-nested-unwind-by-return") (result i32) + (block (result i32) + (i32.const 3) (block (i64.const 1) (return (i32.const 9))) + ) + ) + + (func (export "unary-after-unreachable") (result i32) + (f32.const 0) (unreachable) (i64.eqz) + ) + (func (export "unary-after-br") (result i32) + (block (result i32) (f32.const 0) (br 0 (i32.const 9)) (i64.eqz)) + ) + (func (export "unary-after-br_if") (result i32) + (block (result i32) + (i64.const 0) (drop (br_if 0 (i32.const 9) (i32.const 1))) (i64.eqz) + ) + ) + (func (export "unary-after-br_table") (result i32) + (block (result i32) + (f32.const 0) (br_table 0 0 (i32.const 9) (i32.const 0)) (i64.eqz) + ) + ) + (func (export "unary-after-return") (result i32) + (f32.const 0) (return (i32.const 9)) (i64.eqz) + ) + + (func (export "binary-after-unreachable") (result i32) + (f32.const 0) (f64.const 1) (unreachable) (i64.eq) + ) + (func (export "binary-after-br") (result i32) + (block (result i32) + (f32.const 0) (f64.const 1) (br 0 (i32.const 9)) (i64.eq) + ) + ) + (func (export "binary-after-br_if") (result i32) + (block (result i32) + (i64.const 0) (i64.const 1) (drop (br_if 0 (i32.const 9) (i32.const 1))) + (i64.eq) + ) + ) + (func (export "binary-after-br_table") (result i32) + (block (result i32) + (f32.const 0) (f64.const 1) (br_table 0 (i32.const 9) (i32.const 0)) + (i64.eq) + ) + ) + (func (export "binary-after-return") (result i32) + (f32.const 0) (f64.const 1) (return (i32.const 9)) (i64.eq) + ) + + (func (export "select-after-unreachable") (result i32) + (f32.const 0) (f64.const 1) (i64.const 0) (unreachable) (select) + ) + (func (export "select-after-br") (result i32) + (block (result i32) + (f32.const 0) (f64.const 1) (i64.const 0) (br 0 (i32.const 9)) (select) + ) + ) + (func (export "select-after-br_if") (result i32) + (block (result i32) + (i32.const 0) (i32.const 1) (i32.const 0) + (drop (br_if 0 (i32.const 9) (i32.const 1))) + (select) + ) + ) + (func (export "select-after-br_table") (result i32) + (block (result i32) + (f32.const 0) (f64.const 1) (i64.const 0) + (br_table 0 (i32.const 9) (i32.const 0)) + (select) + ) + ) + (func (export "select-after-return") (result i32) + (f32.const 0) (f64.const 1) (i64.const 1) (return (i32.const 9)) (select) + ) + + (func (export "block-value-after-unreachable") (result i32) + (block (result i32) (f32.const 0) (unreachable)) + ) + (func (export "block-value-after-br") (result i32) + (block (result i32) (f32.const 0) (br 0 (i32.const 9))) + ) + (func (export "block-value-after-br_if") (result i32) + (block (result i32) + (i32.const 0) (drop (br_if 0 (i32.const 9) (i32.const 1))) + ) + ) + (func (export "block-value-after-br_table") (result i32) + (block (result i32) + (f32.const 0) (br_table 0 0 (i32.const 9) (i32.const 0)) + ) + ) + (func (export "block-value-after-return") (result i32) + (block (result i32) (f32.const 0) (return (i32.const 9))) + ) + + (func (export "loop-value-after-unreachable") (result i32) + (loop (result i32) (f32.const 0) (unreachable)) + ) + (func (export "loop-value-after-br") (result i32) + (block (result i32) (loop (result i32) (f32.const 0) (br 1 (i32.const 9)))) + ) + (func (export "loop-value-after-br_if") (result i32) + (block (result i32) + (loop (result i32) + (i32.const 0) (drop (br_if 1 (i32.const 9) (i32.const 1))) + ) + ) + ) + + (func (export "loop-value-after-br_table") (result i32) + (block (result i32) + (loop (result i32) + (f32.const 0) (br_table 1 1 (i32.const 9) (i32.const 0)) + ) + ) + ) + (func (export "loop-value-after-return") (result i32) + (loop (result i32) (f32.const 0) (return (i32.const 9))) + ) +) + +(assert_trap (invoke "func-unwind-by-unreachable") "unreachable") +(assert_return (invoke "func-unwind-by-br")) +(assert_return (invoke "func-unwind-by-br-value") (i32.const 9)) +(assert_return (invoke "func-unwind-by-br_if")) +(assert_return (invoke "func-unwind-by-br_if-value") (i32.const 9)) +(assert_return (invoke "func-unwind-by-br_table")) +(assert_return (invoke "func-unwind-by-br_table-value") (i32.const 9)) +(assert_return (invoke "func-unwind-by-return") (i32.const 9)) + +(assert_trap (invoke "block-unwind-by-unreachable") "unreachable") +(assert_return (invoke "block-unwind-by-br") (i32.const 9)) +(assert_return (invoke "block-unwind-by-br-value") (i32.const 9)) +(assert_return (invoke "block-unwind-by-br_if") (i32.const 9)) +(assert_return (invoke "block-unwind-by-br_if-value") (i32.const 9)) +(assert_return (invoke "block-unwind-by-br_table") (i32.const 9)) +(assert_return (invoke "block-unwind-by-br_table-value") (i32.const 9)) +(assert_return (invoke "block-unwind-by-return") (i32.const 9)) + +(assert_trap (invoke "block-nested-unwind-by-unreachable") "unreachable") +(assert_return (invoke "block-nested-unwind-by-br") (i32.const 9)) +(assert_return (invoke "block-nested-unwind-by-br-value") (i32.const 9)) +(assert_return (invoke "block-nested-unwind-by-br_if") (i32.const 9)) +(assert_return (invoke "block-nested-unwind-by-br_if-value") (i32.const 9)) +(assert_return (invoke "block-nested-unwind-by-br_table") (i32.const 9)) +(assert_return (invoke "block-nested-unwind-by-br_table-value") (i32.const 9)) +(assert_return (invoke "block-nested-unwind-by-return") (i32.const 9)) + +(assert_trap (invoke "unary-after-unreachable") "unreachable") +(assert_return (invoke "unary-after-br") (i32.const 9)) +(assert_return (invoke "unary-after-br_if") (i32.const 9)) +(assert_return (invoke "unary-after-br_table") (i32.const 9)) +(assert_return (invoke "unary-after-return") (i32.const 9)) + +(assert_trap (invoke "binary-after-unreachable") "unreachable") +(assert_return (invoke "binary-after-br") (i32.const 9)) +(assert_return (invoke "binary-after-br_if") (i32.const 9)) +(assert_return (invoke "binary-after-br_table") (i32.const 9)) +(assert_return (invoke "binary-after-return") (i32.const 9)) + +(assert_trap (invoke "select-after-unreachable") "unreachable") +(assert_return (invoke "select-after-br") (i32.const 9)) +(assert_return (invoke "select-after-br_if") (i32.const 9)) +(assert_return (invoke "select-after-br_table") (i32.const 9)) +(assert_return (invoke "select-after-return") (i32.const 9)) + +(assert_trap (invoke "block-value-after-unreachable") "unreachable") +(assert_return (invoke "block-value-after-br") (i32.const 9)) +(assert_return (invoke "block-value-after-br_if") (i32.const 9)) +(assert_return (invoke "block-value-after-br_table") (i32.const 9)) +(assert_return (invoke "block-value-after-return") (i32.const 9)) + +(assert_trap (invoke "loop-value-after-unreachable") "unreachable") +(assert_return (invoke "loop-value-after-br") (i32.const 9)) +(assert_return (invoke "loop-value-after-br_if") (i32.const 9)) +(assert_return (invoke "loop-value-after-br_table") (i32.const 9)) +(assert_return (invoke "loop-value-after-return") (i32.const 9)) diff --git a/runtime/unc-vm/tests/wast/spec/update-testsuite.sh b/runtime/unc-vm/tests/wast/spec/update-testsuite.sh new file mode 100755 index 000000000..8f4a819a7 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/update-testsuite.sh @@ -0,0 +1,152 @@ +#!/bin/bash +# Update tests based on upstream repositories. +set -e +set -u +set -o pipefail + +repos=' + spec + threads + simd + exception-handling + gc + bulk-memory-operations + tail-call + nontrapping-float-to-int-conversions + multi-value + host-bindings + sign-extension-ops + reference-types + annotations +' + +log_and_run() { + echo ">>" $* + if ! $*; then + echo "sub-command failed: $*" + exit + fi +} + +try_log_and_run() { + echo ">>" $* + $* +} + +pushdir() { + pushd $1 >/dev/null || exit +} + +popdir() { + popd >/dev/null || exit +} + +update_repo() { + local repo=$1 + pushdir repos + if [ -d ${repo} ]; then + log_and_run git -C ${repo} fetch origin + log_and_run git -C ${repo} reset origin/master --hard + else + log_and_run git clone https://github.com/WebAssembly/${repo} + fi + + # Add upstream spec as "spec" remote. + if [ "${repo}" != "spec" ]; then + pushdir ${repo} + if ! git remote | grep spec >/dev/null; then + log_and_run git remote add spec https://github.com/WebAssembly/spec + fi + + log_and_run git fetch spec + popdir + fi + popdir +} + +merge_with_spec() { + local repo=$1 + + [ "${repo}" == "spec" ] && return + + pushdir repos/${repo} + # Create and checkout "try-merge" branch. + if ! git branch | grep try-merge >/dev/null; then + log_and_run git branch try-merge origin/master + fi + log_and_run git checkout try-merge + + # Attempt to merge with spec/master. + log_and_run git reset origin/master --hard + try_log_and_run git merge -q spec/master -m "merged" + if [ $? -ne 0 ]; then + # Ignore merge conflicts in non-test directories. + # We don't care about those changes. + try_log_and_run git checkout --ours document interpreter + try_log_and_run git add document interpreter + try_log_and_run git -c core.editor=true merge --continue + if [ $? -ne 0 ]; then + git merge --abort + popdir + return 1 + fi + fi + popdir + return 0 +} + + +echo -e "Update repos\n" > commit_message + +failed_repos= + +for repo in ${repos}; do + echo "++ updating ${repo}" + update_repo ${repo} + + if ! merge_with_spec ${repo}; then + echo -e "!! error merging ${repo}, skipping\n" + failed_repos="${failed_repos} ${repo}" + continue + fi + + if [ "${repo}" = "spec" ]; then + wast_dir=. + log_and_run cp $(find repos/${repo}/test/core -name \*.wast) ${wast_dir} + else + wast_dir=proposals/${repo} + mkdir -p ${wast_dir} + + # Don't add tests from propsoal that are the same as spec. + pushdir repos/${repo} + for new in $(find test/core -name \*.wast); do + old=../../repos/spec/${new} + if [[ ! -f ${old} ]] || ! diff ${old} ${new} >/dev/null; then + log_and_run cp ${new} ../../${wast_dir} + fi + done + popdir + fi + + # Check whether any files were updated. + if [ $(git status -s ${wast_dir}/*.wast | wc -l) -ne 0 ]; then + log_and_run git add ${wast_dir}/*.wast + + repo_sha=$(git -C repos/${repo} log --max-count=1 --oneline origin/master| sed -e 's/ .*//') + echo " ${repo}:" >> commit_message + echo " https://github.com/WebAssembly/${repo}/commit/${repo_sha}" >> commit_message + fi + + echo -e "-- ${repo}\n" +done + +echo "" >> commit_message +echo "This change was automatically generated by \`update-testsuite.sh\`" >> commit_message +git commit -a -F commit_message +# git push + +echo "done" + +if [ -n "${failed_repos}" ]; then + echo "!! failed to update repos: ${failed_repos}" +fi diff --git a/runtime/unc-vm/tests/wast/spec/utf8-custom-section-id.wast b/runtime/unc-vm/tests/wast/spec/utf8-custom-section-id.wast new file mode 100644 index 000000000..ee5fd7ccb --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/utf8-custom-section-id.wast @@ -0,0 +1,1792 @@ +;;;;;; Invalid UTF-8 custom section names + +;;;; Continuation bytes not preceded by prefixes + +;; encoding starts with (first) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\02" ;; custom section + "\01\80" ;; "\80" + ) + "malformed UTF-8 encoding" +) + +;; encoding starts with (0x8f) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\02" ;; custom section + "\01\8f" ;; "\8f" + ) + "malformed UTF-8 encoding" +) + +;; encoding starts with (0x90) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\02" ;; custom section + "\01\90" ;; "\90" + ) + "malformed UTF-8 encoding" +) + +;; encoding starts with (0x9f) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\02" ;; custom section + "\01\9f" ;; "\9f" + ) + "malformed UTF-8 encoding" +) + +;; encoding starts with (0xa0) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\02" ;; custom section + "\01\a0" ;; "\a0" + ) + "malformed UTF-8 encoding" +) + +;; encoding starts with (last) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\02" ;; custom section + "\01\bf" ;; "\bf" + ) + "malformed UTF-8 encoding" +) + +;;;; 2-byte sequences + +;; 2-byte sequence contains 3 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\c2\80\80" ;; "\c2\80\80" + ) + "malformed UTF-8 encoding" +) + +;; 2-byte sequence contains 1 byte at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\02" ;; custom section + "\01\c2" ;; "\c2" + ) + "malformed UTF-8 encoding" +) + +;; 2-byte sequence contains 1 byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\c2\2e" ;; "\c2." + ) + "malformed UTF-8 encoding" +) + +;;;; 2-byte sequence contents + +;; overlong encoding after 0xc0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\c0\80" ;; "\c0\80" + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xc0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\c0\bf" ;; "\c0\bf" + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xc1 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\c1\80" ;; "\c1\80" + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xc1 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\c1\bf" ;; "\c1\bf" + ) + "malformed UTF-8 encoding" +) + +;; byte after (first) 2-byte prefix not a contination byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\c2\00" ;; "\c2\00" + ) + "malformed UTF-8 encoding" +) + +;; byte after (first) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\c2\7f" ;; "\c2\7f" + ) + "malformed UTF-8 encoding" +) + +;; byte after (first) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\c2\c0" ;; "\c2\c0" + ) + "malformed UTF-8 encoding" +) + +;; byte after (first) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\c2\fd" ;; "\c2\fd" + ) + "malformed UTF-8 encoding" +) + +;; byte after (last) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\df\00" ;; "\df\00" + ) + "malformed UTF-8 encoding" +) + +;; byte after (last) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\df\7f" ;; "\df\7f" + ) + "malformed UTF-8 encoding" +) + +;; byte after (last) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\df\c0" ;; "\df\c0" + ) + "malformed UTF-8 encoding" +) + +;; byte after (last) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\df\fd" ;; "\df\fd" + ) + "malformed UTF-8 encoding" +) + +;;;; 3-byte sequences + +;; 3-byte sequence contains 4 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\e1\80\80\80" ;; "\e1\80\80\80" + ) + "malformed UTF-8 encoding" +) + +;; 3-byte sequence contains 2 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\e1\80" ;; "\e1\80" + ) + "malformed UTF-8 encoding" +) + +;; 3-byte sequence contains 2 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e1\80\2e" ;; "\e1\80." + ) + "malformed UTF-8 encoding" +) + +;; 3-byte sequence contains 1 byte at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\02" ;; custom section + "\01\e1" ;; "\e1" + ) + "malformed UTF-8 encoding" +) + +;; 3-byte sequence contains 1 byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\e1\2e" ;; "\e1." + ) + "malformed UTF-8 encoding" +) + +;;;; 3-byte sequence contents + +;; first byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e0\00\a0" ;; "\e0\00\a0" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e0\7f\a0" ;; "\e0\7f\a0" + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xe0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e0\80\80" ;; "\e0\80\80" + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xe0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e0\80\a0" ;; "\e0\80\a0" + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xe0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e0\9f\a0" ;; "\e0\9f\a0" + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xe0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e0\9f\bf" ;; "\e0\9f\bf" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e0\c0\a0" ;; "\e0\c0\a0" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e0\fd\a0" ;; "\e0\fd\a0" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e1\00\80" ;; "\e1\00\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e1\7f\80" ;; "\e1\7f\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e1\c0\80" ;; "\e1\c0\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e1\fd\80" ;; "\e1\fd\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ec\00\80" ;; "\ec\00\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ec\7f\80" ;; "\ec\7f\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ec\c0\80" ;; "\ec\c0\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ec\fd\80" ;; "\ec\fd\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ed\00\80" ;; "\ed\00\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ed\7f\80" ;; "\ed\7f\80" + ) + "malformed UTF-8 encoding" +) + +;; byte sequence reserved for UTF-16 surrogate half +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ed\a0\80" ;; "\ed\a0\80" + ) + "malformed UTF-8 encoding" +) + +;; byte sequence reserved for UTF-16 surrogate half +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ed\a0\bf" ;; "\ed\a0\bf" + ) + "malformed UTF-8 encoding" +) + +;; byte sequence reserved for UTF-16 surrogate half +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ed\bf\80" ;; "\ed\bf\80" + ) + "malformed UTF-8 encoding" +) + +;; byte sequence reserved for UTF-16 surrogate half +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ed\bf\bf" ;; "\ed\bf\bf" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ed\c0\80" ;; "\ed\c0\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ed\fd\80" ;; "\ed\fd\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ee\00\80" ;; "\ee\00\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ee\7f\80" ;; "\ee\7f\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ee\c0\80" ;; "\ee\c0\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ee\fd\80" ;; "\ee\fd\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ef\00\80" ;; "\ef\00\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ef\7f\80" ;; "\ef\7f\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ef\c0\80" ;; "\ef\c0\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ef\fd\80" ;; "\ef\fd\80" + ) + "malformed UTF-8 encoding" +) + +;;;; 3-byte sequence contents (third byte) + +;; second byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e0\a0\00" ;; "\e0\a0\00" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e0\a0\7f" ;; "\e0\a0\7f" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e0\a0\c0" ;; "\e0\a0\c0" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e0\a0\fd" ;; "\e0\a0\fd" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e1\80\00" ;; "\e1\80\00" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e1\80\7f" ;; "\e1\80\7f" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e1\80\c0" ;; "\e1\80\c0" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\e1\80\fd" ;; "\e1\80\fd" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ec\80\00" ;; "\ec\80\00" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ec\80\7f" ;; "\ec\80\7f" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ec\80\c0" ;; "\ec\80\c0" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ec\80\fd" ;; "\ec\80\fd" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ed\80\00" ;; "\ed\80\00" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ed\80\7f" ;; "\ed\80\7f" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ed\80\c0" ;; "\ed\80\c0" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ed\80\fd" ;; "\ed\80\fd" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ee\80\00" ;; "\ee\80\00" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ee\80\7f" ;; "\ee\80\7f" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ee\80\c0" ;; "\ee\80\c0" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ee\80\fd" ;; "\ee\80\fd" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ef\80\00" ;; "\ef\80\00" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ef\80\7f" ;; "\ef\80\7f" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ef\80\c0" ;; "\ef\80\c0" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\ef\80\fd" ;; "\ef\80\fd" + ) + "malformed UTF-8 encoding" +) + +;;;; 4-byte sequences + +;; 4-byte sequence contains 5 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\06" ;; custom section + "\05\f1\80\80\80\80" ;; "\f1\80\80\80\80" + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 3 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\f1\80\80" ;; "\f1\80\80" + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 3 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f1\80\80\23" ;; "\f1\80\80#" + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 2 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\f1\80" ;; "\f1\80" + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 2 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\f1\80\23" ;; "\f1\80#" + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 1 byte at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\02" ;; custom section + "\01\f1" ;; "\f1" + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 1 byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\f1\23" ;; "\f1#" + ) + "malformed UTF-8 encoding" +) + +;;;; 4-byte sequence contents + +;; first byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\00\90\90" ;; "\f0\00\90\90" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\7f\90\90" ;; "\f0\7f\90\90" + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xf0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\80\80\80" ;; "\f0\80\80\80" + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xf0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\80\90\90" ;; "\f0\80\90\90" + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xf0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\8f\90\90" ;; "\f0\8f\90\90" + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xf0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\8f\bf\bf" ;; "\f0\8f\bf\bf" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\c0\90\90" ;; "\f0\c0\90\90" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\fd\90\90" ;; "\f0\fd\90\90" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f1\00\80\80" ;; "\f1\00\80\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f1\7f\80\80" ;; "\f1\7f\80\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f1\c0\80\80" ;; "\f1\c0\80\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f1\fd\80\80" ;; "\f1\fd\80\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f3\00\80\80" ;; "\f3\00\80\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f3\7f\80\80" ;; "\f3\7f\80\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f3\c0\80\80" ;; "\f3\c0\80\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f3\fd\80\80" ;; "\f3\fd\80\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f4\00\80\80" ;; "\f4\00\80\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f4\7f\80\80" ;; "\f4\7f\80\80" + ) + "malformed UTF-8 encoding" +) + +;; (first) malformed code point +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f4\90\80\80" ;; "\f4\90\80\80" + ) + "malformed UTF-8 encoding" +) + +;; malformed code point +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f4\bf\80\80" ;; "\f4\bf\80\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f4\c0\80\80" ;; "\f4\c0\80\80" + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f4\fd\80\80" ;; "\f4\fd\80\80" + ) + "malformed UTF-8 encoding" +) + +;; (first) malformed 4-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f5\80\80\80" ;; "\f5\80\80\80" + ) + "malformed UTF-8 encoding" +) + +;; (last) malformed 4-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f7\80\80\80" ;; "\f7\80\80\80" + ) + "malformed UTF-8 encoding" +) + +;; (last) malformed 4-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f7\bf\bf\bf" ;; "\f7\bf\bf\bf" + ) + "malformed UTF-8 encoding" +) + +;;;; 4-byte sequence contents (third byte) + +;; second byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\90\00\90" ;; "\f0\90\00\90" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\90\7f\90" ;; "\f0\90\7f\90" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\90\c0\90" ;; "\f0\90\c0\90" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\90\fd\90" ;; "\f0\90\fd\90" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f1\80\00\80" ;; "\f1\80\00\80" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f1\80\7f\80" ;; "\f1\80\7f\80" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f1\80\c0\80" ;; "\f1\80\c0\80" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f1\80\fd\80" ;; "\f1\80\fd\80" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f3\80\00\80" ;; "\f3\80\00\80" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f3\80\7f\80" ;; "\f3\80\7f\80" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f3\80\c0\80" ;; "\f3\80\c0\80" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f3\80\fd\80" ;; "\f3\80\fd\80" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f4\80\00\80" ;; "\f4\80\00\80" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f4\80\7f\80" ;; "\f4\80\7f\80" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f4\80\c0\80" ;; "\f4\80\c0\80" + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f4\80\fd\80" ;; "\f4\80\fd\80" + ) + "malformed UTF-8 encoding" +) + +;;;; 4-byte sequence contents (fourth byte) + +;; third byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\90\90\00" ;; "\f0\90\90\00" + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\90\90\7f" ;; "\f0\90\90\7f" + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\90\90\c0" ;; "\f0\90\90\c0" + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f0\90\90\fd" ;; "\f0\90\90\fd" + ) + "malformed UTF-8 encoding" +) + +;; third byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f1\80\80\00" ;; "\f1\80\80\00" + ) + "malformed UTF-8 encoding" +) + +;; third byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f1\80\80\7f" ;; "\f1\80\80\7f" + ) + "malformed UTF-8 encoding" +) + +;; third byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f1\80\80\c0" ;; "\f1\80\80\c0" + ) + "malformed UTF-8 encoding" +) + +;; third byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f1\80\80\fd" ;; "\f1\80\80\fd" + ) + "malformed UTF-8 encoding" +) + +;; third byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f3\80\80\00" ;; "\f3\80\80\00" + ) + "malformed UTF-8 encoding" +) + +;; third byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f3\80\80\7f" ;; "\f3\80\80\7f" + ) + "malformed UTF-8 encoding" +) + +;; third byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f3\80\80\c0" ;; "\f3\80\80\c0" + ) + "malformed UTF-8 encoding" +) + +;; third byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f3\80\80\fd" ;; "\f3\80\80\fd" + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f4\80\80\00" ;; "\f4\80\80\00" + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f4\80\80\7f" ;; "\f4\80\80\7f" + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f4\80\80\c0" ;; "\f4\80\80\c0" + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f4\80\80\fd" ;; "\f4\80\80\fd" + ) + "malformed UTF-8 encoding" +) + +;;;; 5-byte sequences + +;; 5-byte sequence contains 6 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\07" ;; custom section + "\06\f8\80\80\80\80\80" ;; "\f8\80\80\80\80\80" + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 4 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f8\80\80\80" ;; "\f8\80\80\80" + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 4 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\06" ;; custom section + "\05\f8\80\80\80\23" ;; "\f8\80\80\80#" + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 3 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\f8\80\80" ;; "\f8\80\80" + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 3 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\f8\80\80\23" ;; "\f8\80\80#" + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 2 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\f8\80" ;; "\f8\80" + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 2 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\f8\80\23" ;; "\f8\80#" + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 1 byte at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\02" ;; custom section + "\01\f8" ;; "\f8" + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 1 byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\f8\23" ;; "\f8#" + ) + "malformed UTF-8 encoding" +) + +;;;; 5-byte sequence contents + +;; (first) malformed 5-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\06" ;; custom section + "\05\f8\80\80\80\80" ;; "\f8\80\80\80\80" + ) + "malformed UTF-8 encoding" +) + +;; (last) malformed 5-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\06" ;; custom section + "\05\fb\bf\bf\bf\bf" ;; "\fb\bf\bf\bf\bf" + ) + "malformed UTF-8 encoding" +) + +;;;; 6-byte sequences + +;; 6-byte sequence contains 7 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\08" ;; custom section + "\07\fc\80\80\80\80\80\80" ;; "\fc\80\80\80\80\80\80" + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 5 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\06" ;; custom section + "\05\fc\80\80\80\80" ;; "\fc\80\80\80\80" + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 5 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\07" ;; custom section + "\06\fc\80\80\80\80\23" ;; "\fc\80\80\80\80#" + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 4 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\fc\80\80\80" ;; "\fc\80\80\80" + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 4 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\06" ;; custom section + "\05\fc\80\80\80\23" ;; "\fc\80\80\80#" + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 3 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\fc\80\80" ;; "\fc\80\80" + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 3 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\fc\80\80\23" ;; "\fc\80\80#" + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 2 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\fc\80" ;; "\fc\80" + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 2 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\04" ;; custom section + "\03\fc\80\23" ;; "\fc\80#" + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 1 byte at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\02" ;; custom section + "\01\fc" ;; "\fc" + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 1 byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\fc\23" ;; "\fc#" + ) + "malformed UTF-8 encoding" +) + +;;;; 6-byte sequence contents + +;; (first) malformed 6-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\07" ;; custom section + "\06\fc\80\80\80\80\80" ;; "\fc\80\80\80\80\80" + ) + "malformed UTF-8 encoding" +) + +;; (last) malformed 6-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\07" ;; custom section + "\06\fd\bf\bf\bf\bf\bf" ;; "\fd\bf\bf\bf\bf\bf" + ) + "malformed UTF-8 encoding" +) + +;;;; Miscellaneous malformed bytes + +;; malformed byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\02" ;; custom section + "\01\fe" ;; "\fe" + ) + "malformed UTF-8 encoding" +) + +;; malformed byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\02" ;; custom section + "\01\ff" ;; "\ff" + ) + "malformed UTF-8 encoding" +) + +;; UTF-16BE BOM +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\fe\ff" ;; "\fe\ff" + ) + "malformed UTF-8 encoding" +) + +;; UTF-32BE BOM +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\00\00\fe\ff" ;; "\00\00\fe\ff" + ) + "malformed UTF-8 encoding" +) + +;; UTF-16LE BOM +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\03" ;; custom section + "\02\ff\fe" ;; "\ff\fe" + ) + "malformed UTF-8 encoding" +) + +;; UTF-32LE BOM +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\00\05" ;; custom section + "\04\ff\fe\00\00" ;; "\ff\fe\00\00" + ) + "malformed UTF-8 encoding" +) + diff --git a/runtime/unc-vm/tests/wast/spec/utf8-import-field.wast b/runtime/unc-vm/tests/wast/spec/utf8-import-field.wast new file mode 100644 index 000000000..0d030dabb --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/utf8-import-field.wast @@ -0,0 +1,2672 @@ +;;;;;; Invalid UTF-8 import field names + +;;;; Continuation bytes not preceded by prefixes + +;; encoding starts with (first) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\01\80" ;; "\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; encoding starts with (0x8f) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\01\8f" ;; "\8f" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; encoding starts with (0x90) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\01\90" ;; "\90" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; encoding starts with (0x9f) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\01\9f" ;; "\9f" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; encoding starts with (0xa0) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\01\a0" ;; "\a0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; encoding starts with (last) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\01\bf" ;; "\bf" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 2-byte sequences + +;; 2-byte sequence contains 3 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\c2\80\80" ;; "\c2\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 2-byte sequence contains 1 byte at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\01\c2" ;; "\c2" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 2-byte sequence contains 1 byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\c2\2e" ;; "\c2." + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 2-byte sequence contents + +;; overlong encoding after 0xc0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\c0\80" ;; "\c0\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xc0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\c0\bf" ;; "\c0\bf" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xc1 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\c1\80" ;; "\c1\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xc1 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\c1\bf" ;; "\c1\bf" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (first) 2-byte prefix not a contination byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\c2\00" ;; "\c2\00" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (first) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\c2\7f" ;; "\c2\7f" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (first) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\c2\c0" ;; "\c2\c0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (first) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\c2\fd" ;; "\c2\fd" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (last) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\df\00" ;; "\df\00" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (last) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\df\7f" ;; "\df\7f" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (last) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\df\c0" ;; "\df\c0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (last) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\df\fd" ;; "\df\fd" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 3-byte sequences + +;; 3-byte sequence contains 4 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\e1\80\80\80" ;; "\e1\80\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 3-byte sequence contains 2 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\e1\80" ;; "\e1\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 3-byte sequence contains 2 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e1\80\2e" ;; "\e1\80." + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 3-byte sequence contains 1 byte at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\01\e1" ;; "\e1" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 3-byte sequence contains 1 byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\e1\2e" ;; "\e1." + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 3-byte sequence contents + +;; first byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e0\00\a0" ;; "\e0\00\a0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e0\7f\a0" ;; "\e0\7f\a0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xe0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e0\80\80" ;; "\e0\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xe0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e0\80\a0" ;; "\e0\80\a0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xe0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e0\9f\a0" ;; "\e0\9f\a0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xe0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e0\9f\bf" ;; "\e0\9f\bf" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e0\c0\a0" ;; "\e0\c0\a0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e0\fd\a0" ;; "\e0\fd\a0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e1\00\80" ;; "\e1\00\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e1\7f\80" ;; "\e1\7f\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e1\c0\80" ;; "\e1\c0\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e1\fd\80" ;; "\e1\fd\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ec\00\80" ;; "\ec\00\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ec\7f\80" ;; "\ec\7f\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ec\c0\80" ;; "\ec\c0\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ec\fd\80" ;; "\ec\fd\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ed\00\80" ;; "\ed\00\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ed\7f\80" ;; "\ed\7f\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte sequence reserved for UTF-16 surrogate half +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ed\a0\80" ;; "\ed\a0\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte sequence reserved for UTF-16 surrogate half +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ed\a0\bf" ;; "\ed\a0\bf" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte sequence reserved for UTF-16 surrogate half +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ed\bf\80" ;; "\ed\bf\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte sequence reserved for UTF-16 surrogate half +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ed\bf\bf" ;; "\ed\bf\bf" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ed\c0\80" ;; "\ed\c0\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ed\fd\80" ;; "\ed\fd\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ee\00\80" ;; "\ee\00\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ee\7f\80" ;; "\ee\7f\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ee\c0\80" ;; "\ee\c0\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ee\fd\80" ;; "\ee\fd\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ef\00\80" ;; "\ef\00\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ef\7f\80" ;; "\ef\7f\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ef\c0\80" ;; "\ef\c0\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ef\fd\80" ;; "\ef\fd\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 3-byte sequence contents (third byte) + +;; second byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e0\a0\00" ;; "\e0\a0\00" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e0\a0\7f" ;; "\e0\a0\7f" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e0\a0\c0" ;; "\e0\a0\c0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e0\a0\fd" ;; "\e0\a0\fd" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e1\80\00" ;; "\e1\80\00" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e1\80\7f" ;; "\e1\80\7f" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e1\80\c0" ;; "\e1\80\c0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\e1\80\fd" ;; "\e1\80\fd" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ec\80\00" ;; "\ec\80\00" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ec\80\7f" ;; "\ec\80\7f" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ec\80\c0" ;; "\ec\80\c0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ec\80\fd" ;; "\ec\80\fd" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ed\80\00" ;; "\ed\80\00" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ed\80\7f" ;; "\ed\80\7f" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ed\80\c0" ;; "\ed\80\c0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ed\80\fd" ;; "\ed\80\fd" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ee\80\00" ;; "\ee\80\00" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ee\80\7f" ;; "\ee\80\7f" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ee\80\c0" ;; "\ee\80\c0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ee\80\fd" ;; "\ee\80\fd" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ef\80\00" ;; "\ef\80\00" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ef\80\7f" ;; "\ef\80\7f" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ef\80\c0" ;; "\ef\80\c0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\ef\80\fd" ;; "\ef\80\fd" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 4-byte sequences + +;; 4-byte sequence contains 5 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0f" ;; import section + "\01" ;; length 1 + "\05\f1\80\80\80\80" ;; "\f1\80\80\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 3 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\f1\80\80" ;; "\f1\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 3 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f1\80\80\23" ;; "\f1\80\80#" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 2 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\f1\80" ;; "\f1\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 2 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\f1\80\23" ;; "\f1\80#" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 1 byte at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\01\f1" ;; "\f1" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 1 byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\f1\23" ;; "\f1#" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 4-byte sequence contents + +;; first byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\00\90\90" ;; "\f0\00\90\90" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\7f\90\90" ;; "\f0\7f\90\90" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xf0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\80\80\80" ;; "\f0\80\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xf0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\80\90\90" ;; "\f0\80\90\90" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xf0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\8f\90\90" ;; "\f0\8f\90\90" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xf0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\8f\bf\bf" ;; "\f0\8f\bf\bf" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\c0\90\90" ;; "\f0\c0\90\90" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\fd\90\90" ;; "\f0\fd\90\90" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f1\00\80\80" ;; "\f1\00\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f1\7f\80\80" ;; "\f1\7f\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f1\c0\80\80" ;; "\f1\c0\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f1\fd\80\80" ;; "\f1\fd\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f3\00\80\80" ;; "\f3\00\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f3\7f\80\80" ;; "\f3\7f\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f3\c0\80\80" ;; "\f3\c0\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f3\fd\80\80" ;; "\f3\fd\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f4\00\80\80" ;; "\f4\00\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f4\7f\80\80" ;; "\f4\7f\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; (first) malformed code point +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f4\90\80\80" ;; "\f4\90\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; malformed code point +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f4\bf\80\80" ;; "\f4\bf\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f4\c0\80\80" ;; "\f4\c0\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f4\fd\80\80" ;; "\f4\fd\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; (first) malformed 4-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f5\80\80\80" ;; "\f5\80\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; (last) malformed 4-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f7\80\80\80" ;; "\f7\80\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; (last) malformed 4-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f7\bf\bf\bf" ;; "\f7\bf\bf\bf" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 4-byte sequence contents (third byte) + +;; second byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\90\00\90" ;; "\f0\90\00\90" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\90\7f\90" ;; "\f0\90\7f\90" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\90\c0\90" ;; "\f0\90\c0\90" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\90\fd\90" ;; "\f0\90\fd\90" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f1\80\00\80" ;; "\f1\80\00\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f1\80\7f\80" ;; "\f1\80\7f\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f1\80\c0\80" ;; "\f1\80\c0\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f1\80\fd\80" ;; "\f1\80\fd\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f3\80\00\80" ;; "\f3\80\00\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f3\80\7f\80" ;; "\f3\80\7f\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f3\80\c0\80" ;; "\f3\80\c0\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f3\80\fd\80" ;; "\f3\80\fd\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f4\80\00\80" ;; "\f4\80\00\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f4\80\7f\80" ;; "\f4\80\7f\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f4\80\c0\80" ;; "\f4\80\c0\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f4\80\fd\80" ;; "\f4\80\fd\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 4-byte sequence contents (fourth byte) + +;; third byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\90\90\00" ;; "\f0\90\90\00" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\90\90\7f" ;; "\f0\90\90\7f" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\90\90\c0" ;; "\f0\90\90\c0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f0\90\90\fd" ;; "\f0\90\90\fd" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f1\80\80\00" ;; "\f1\80\80\00" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f1\80\80\7f" ;; "\f1\80\80\7f" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f1\80\80\c0" ;; "\f1\80\80\c0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f1\80\80\fd" ;; "\f1\80\80\fd" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f3\80\80\00" ;; "\f3\80\80\00" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f3\80\80\7f" ;; "\f3\80\80\7f" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f3\80\80\c0" ;; "\f3\80\80\c0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f3\80\80\fd" ;; "\f3\80\80\fd" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f4\80\80\00" ;; "\f4\80\80\00" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f4\80\80\7f" ;; "\f4\80\80\7f" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f4\80\80\c0" ;; "\f4\80\80\c0" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f4\80\80\fd" ;; "\f4\80\80\fd" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 5-byte sequences + +;; 5-byte sequence contains 6 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\10" ;; import section + "\01" ;; length 1 + "\06\f8\80\80\80\80\80" ;; "\f8\80\80\80\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 4 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f8\80\80\80" ;; "\f8\80\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 4 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0f" ;; import section + "\01" ;; length 1 + "\05\f8\80\80\80\23" ;; "\f8\80\80\80#" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 3 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\f8\80\80" ;; "\f8\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 3 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\f8\80\80\23" ;; "\f8\80\80#" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 2 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\f8\80" ;; "\f8\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 2 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\f8\80\23" ;; "\f8\80#" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 1 byte at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\01\f8" ;; "\f8" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 1 byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\f8\23" ;; "\f8#" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 5-byte sequence contents + +;; (first) malformed 5-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0f" ;; import section + "\01" ;; length 1 + "\05\f8\80\80\80\80" ;; "\f8\80\80\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; (last) malformed 5-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0f" ;; import section + "\01" ;; length 1 + "\05\fb\bf\bf\bf\bf" ;; "\fb\bf\bf\bf\bf" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 6-byte sequences + +;; 6-byte sequence contains 7 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\11" ;; import section + "\01" ;; length 1 + "\07\fc\80\80\80\80\80\80" ;; "\fc\80\80\80\80\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 5 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0f" ;; import section + "\01" ;; length 1 + "\05\fc\80\80\80\80" ;; "\fc\80\80\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 5 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\10" ;; import section + "\01" ;; length 1 + "\06\fc\80\80\80\80\23" ;; "\fc\80\80\80\80#" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 4 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\fc\80\80\80" ;; "\fc\80\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 4 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0f" ;; import section + "\01" ;; length 1 + "\05\fc\80\80\80\23" ;; "\fc\80\80\80#" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 3 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\fc\80\80" ;; "\fc\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 3 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\fc\80\80\23" ;; "\fc\80\80#" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 2 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\fc\80" ;; "\fc\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 2 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\03\fc\80\23" ;; "\fc\80#" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 1 byte at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\01\fc" ;; "\fc" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 1 byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\fc\23" ;; "\fc#" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 6-byte sequence contents + +;; (first) malformed 6-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\10" ;; import section + "\01" ;; length 1 + "\06\fc\80\80\80\80\80" ;; "\fc\80\80\80\80\80" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; (last) malformed 6-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\10" ;; import section + "\01" ;; length 1 + "\06\fd\bf\bf\bf\bf\bf" ;; "\fd\bf\bf\bf\bf\bf" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; Miscellaneous malformed bytes + +;; malformed byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\01\fe" ;; "\fe" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; malformed byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\01\ff" ;; "\ff" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; UTF-16BE BOM +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\fe\ff" ;; "\fe\ff" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; UTF-32BE BOM +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\00\00\fe\ff" ;; "\00\00\fe\ff" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; UTF-16LE BOM +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\02\ff\fe" ;; "\ff\fe" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; UTF-32LE BOM +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\ff\fe\00\00" ;; "\ff\fe\00\00" + "\04\74\65\73\74" ;; "test" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + diff --git a/runtime/unc-vm/tests/wast/spec/utf8-import-module.wast b/runtime/unc-vm/tests/wast/spec/utf8-import-module.wast new file mode 100644 index 000000000..5a092da08 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/utf8-import-module.wast @@ -0,0 +1,2672 @@ +;;;;;; Invalid UTF-8 import module names + +;;;; Continuation bytes not preceded by prefixes + +;; encoding starts with (first) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\01\80" ;; "\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; encoding starts with (0x8f) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\01\8f" ;; "\8f" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; encoding starts with (0x90) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\01\90" ;; "\90" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; encoding starts with (0x9f) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\01\9f" ;; "\9f" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; encoding starts with (0xa0) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\01\a0" ;; "\a0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; encoding starts with (last) continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\01\bf" ;; "\bf" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 2-byte sequences + +;; 2-byte sequence contains 3 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\c2\80\80" ;; "\c2\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 2-byte sequence contains 1 byte at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\01\c2" ;; "\c2" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 2-byte sequence contains 1 byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\c2\2e" ;; "\c2." + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 2-byte sequence contents + +;; overlong encoding after 0xc0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\c0\80" ;; "\c0\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xc0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\c0\bf" ;; "\c0\bf" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xc1 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\c1\80" ;; "\c1\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xc1 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\c1\bf" ;; "\c1\bf" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (first) 2-byte prefix not a contination byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\c2\00" ;; "\c2\00" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (first) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\c2\7f" ;; "\c2\7f" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (first) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\c2\c0" ;; "\c2\c0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (first) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\c2\fd" ;; "\c2\fd" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (last) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\df\00" ;; "\df\00" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (last) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\df\7f" ;; "\df\7f" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (last) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\df\c0" ;; "\df\c0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte after (last) 2-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\df\fd" ;; "\df\fd" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 3-byte sequences + +;; 3-byte sequence contains 4 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\e1\80\80\80" ;; "\e1\80\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 3-byte sequence contains 2 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\e1\80" ;; "\e1\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 3-byte sequence contains 2 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e1\80\2e" ;; "\e1\80." + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 3-byte sequence contains 1 byte at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\01\e1" ;; "\e1" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 3-byte sequence contains 1 byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\e1\2e" ;; "\e1." + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 3-byte sequence contents + +;; first byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e0\00\a0" ;; "\e0\00\a0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e0\7f\a0" ;; "\e0\7f\a0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xe0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e0\80\80" ;; "\e0\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xe0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e0\80\a0" ;; "\e0\80\a0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xe0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e0\9f\a0" ;; "\e0\9f\a0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xe0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e0\9f\bf" ;; "\e0\9f\bf" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e0\c0\a0" ;; "\e0\c0\a0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e0\fd\a0" ;; "\e0\fd\a0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e1\00\80" ;; "\e1\00\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e1\7f\80" ;; "\e1\7f\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e1\c0\80" ;; "\e1\c0\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e1\fd\80" ;; "\e1\fd\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ec\00\80" ;; "\ec\00\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ec\7f\80" ;; "\ec\7f\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ec\c0\80" ;; "\ec\c0\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ec\fd\80" ;; "\ec\fd\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ed\00\80" ;; "\ed\00\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ed\7f\80" ;; "\ed\7f\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte sequence reserved for UTF-16 surrogate half +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ed\a0\80" ;; "\ed\a0\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte sequence reserved for UTF-16 surrogate half +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ed\a0\bf" ;; "\ed\a0\bf" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte sequence reserved for UTF-16 surrogate half +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ed\bf\80" ;; "\ed\bf\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; byte sequence reserved for UTF-16 surrogate half +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ed\bf\bf" ;; "\ed\bf\bf" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ed\c0\80" ;; "\ed\c0\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ed\fd\80" ;; "\ed\fd\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ee\00\80" ;; "\ee\00\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ee\7f\80" ;; "\ee\7f\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ee\c0\80" ;; "\ee\c0\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ee\fd\80" ;; "\ee\fd\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ef\00\80" ;; "\ef\00\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ef\7f\80" ;; "\ef\7f\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ef\c0\80" ;; "\ef\c0\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ef\fd\80" ;; "\ef\fd\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 3-byte sequence contents (third byte) + +;; second byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e0\a0\00" ;; "\e0\a0\00" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e0\a0\7f" ;; "\e0\a0\7f" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e0\a0\c0" ;; "\e0\a0\c0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xe0) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e0\a0\fd" ;; "\e0\a0\fd" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e1\80\00" ;; "\e1\80\00" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e1\80\7f" ;; "\e1\80\7f" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e1\80\c0" ;; "\e1\80\c0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\e1\80\fd" ;; "\e1\80\fd" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ec\80\00" ;; "\ec\80\00" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ec\80\7f" ;; "\ec\80\7f" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ec\80\c0" ;; "\ec\80\c0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ec\80\fd" ;; "\ec\80\fd" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ed\80\00" ;; "\ed\80\00" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ed\80\7f" ;; "\ed\80\7f" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ed\80\c0" ;; "\ed\80\c0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xed) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ed\80\fd" ;; "\ed\80\fd" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ee\80\00" ;; "\ee\80\00" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ee\80\7f" ;; "\ee\80\7f" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ee\80\c0" ;; "\ee\80\c0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ee\80\fd" ;; "\ee\80\fd" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ef\80\00" ;; "\ef\80\00" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ef\80\7f" ;; "\ef\80\7f" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ef\80\c0" ;; "\ef\80\c0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 3-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\ef\80\fd" ;; "\ef\80\fd" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 4-byte sequences + +;; 4-byte sequence contains 5 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0f" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\05\f1\80\80\80\80" ;; "\f1\80\80\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 3 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\f1\80\80" ;; "\f1\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 3 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f1\80\80\23" ;; "\f1\80\80#" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 2 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\f1\80" ;; "\f1\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 2 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\f1\80\23" ;; "\f1\80#" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 1 byte at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\01\f1" ;; "\f1" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 4-byte sequence contains 1 byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\f1\23" ;; "\f1#" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 4-byte sequence contents + +;; first byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\00\90\90" ;; "\f0\00\90\90" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\7f\90\90" ;; "\f0\7f\90\90" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xf0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\80\80\80" ;; "\f0\80\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xf0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\80\90\90" ;; "\f0\80\90\90" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xf0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\8f\90\90" ;; "\f0\8f\90\90" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; overlong encoding after 0xf0 prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\8f\bf\bf" ;; "\f0\8f\bf\bf" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\c0\90\90" ;; "\f0\c0\90\90" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\fd\90\90" ;; "\f0\fd\90\90" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f1\00\80\80" ;; "\f1\00\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f1\7f\80\80" ;; "\f1\7f\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f1\c0\80\80" ;; "\f1\c0\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f1\fd\80\80" ;; "\f1\fd\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f3\00\80\80" ;; "\f3\00\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f3\7f\80\80" ;; "\f3\7f\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f3\c0\80\80" ;; "\f3\c0\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f3\fd\80\80" ;; "\f3\fd\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f4\00\80\80" ;; "\f4\00\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f4\7f\80\80" ;; "\f4\7f\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; (first) malformed code point +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f4\90\80\80" ;; "\f4\90\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; malformed code point +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f4\bf\80\80" ;; "\f4\bf\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f4\c0\80\80" ;; "\f4\c0\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; first byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f4\fd\80\80" ;; "\f4\fd\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; (first) malformed 4-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f5\80\80\80" ;; "\f5\80\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; (last) malformed 4-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f7\80\80\80" ;; "\f7\80\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; (last) malformed 4-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f7\bf\bf\bf" ;; "\f7\bf\bf\bf" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 4-byte sequence contents (third byte) + +;; second byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\90\00\90" ;; "\f0\90\00\90" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\90\7f\90" ;; "\f0\90\7f\90" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\90\c0\90" ;; "\f0\90\c0\90" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\90\fd\90" ;; "\f0\90\fd\90" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f1\80\00\80" ;; "\f1\80\00\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f1\80\7f\80" ;; "\f1\80\7f\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f1\80\c0\80" ;; "\f1\80\c0\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f1\80\fd\80" ;; "\f1\80\fd\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f3\80\00\80" ;; "\f3\80\00\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f3\80\7f\80" ;; "\f3\80\7f\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f3\80\c0\80" ;; "\f3\80\c0\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f3\80\fd\80" ;; "\f3\80\fd\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f4\80\00\80" ;; "\f4\80\00\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f4\80\7f\80" ;; "\f4\80\7f\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f4\80\c0\80" ;; "\f4\80\c0\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; second byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f4\80\fd\80" ;; "\f4\80\fd\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 4-byte sequence contents (fourth byte) + +;; third byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\90\90\00" ;; "\f0\90\90\00" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\90\90\7f" ;; "\f0\90\90\7f" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\90\90\c0" ;; "\f0\90\90\c0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf0) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f0\90\90\fd" ;; "\f0\90\90\fd" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f1\80\80\00" ;; "\f1\80\80\00" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f1\80\80\7f" ;; "\f1\80\80\7f" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f1\80\80\c0" ;; "\f1\80\80\c0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (first normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f1\80\80\fd" ;; "\f1\80\80\fd" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f3\80\80\00" ;; "\f3\80\80\00" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f3\80\80\7f" ;; "\f3\80\80\7f" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f3\80\80\c0" ;; "\f3\80\80\c0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (last normal) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f3\80\80\fd" ;; "\f3\80\80\fd" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f4\80\80\00" ;; "\f4\80\80\00" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f4\80\80\7f" ;; "\f4\80\80\7f" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f4\80\80\c0" ;; "\f4\80\80\c0" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; third byte after (0xf4) 4-byte prefix not a continuation byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f4\80\80\fd" ;; "\f4\80\80\fd" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 5-byte sequences + +;; 5-byte sequence contains 6 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\10" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\06\f8\80\80\80\80\80" ;; "\f8\80\80\80\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 4 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f8\80\80\80" ;; "\f8\80\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 4 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0f" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\05\f8\80\80\80\23" ;; "\f8\80\80\80#" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 3 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\f8\80\80" ;; "\f8\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 3 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\f8\80\80\23" ;; "\f8\80\80#" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 2 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\f8\80" ;; "\f8\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 2 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\f8\80\23" ;; "\f8\80#" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 1 byte at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\01\f8" ;; "\f8" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 5-byte sequence contains 1 byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\f8\23" ;; "\f8#" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 5-byte sequence contents + +;; (first) malformed 5-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0f" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\05\f8\80\80\80\80" ;; "\f8\80\80\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; (last) malformed 5-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0f" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\05\fb\bf\bf\bf\bf" ;; "\fb\bf\bf\bf\bf" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 6-byte sequences + +;; 6-byte sequence contains 7 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\11" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\07\fc\80\80\80\80\80\80" ;; "\fc\80\80\80\80\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 5 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0f" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\05\fc\80\80\80\80" ;; "\fc\80\80\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 5 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\10" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\06\fc\80\80\80\80\23" ;; "\fc\80\80\80\80#" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 4 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\fc\80\80\80" ;; "\fc\80\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 4 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0f" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\05\fc\80\80\80\23" ;; "\fc\80\80\80#" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 3 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\fc\80\80" ;; "\fc\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 3 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\fc\80\80\23" ;; "\fc\80\80#" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 2 bytes at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\fc\80" ;; "\fc\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 2 bytes +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0d" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\03\fc\80\23" ;; "\fc\80#" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 1 byte at end of string +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\01\fc" ;; "\fc" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; 6-byte sequence contains 1 byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\fc\23" ;; "\fc#" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; 6-byte sequence contents + +;; (first) malformed 6-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\10" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\06\fc\80\80\80\80\80" ;; "\fc\80\80\80\80\80" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; (last) malformed 6-byte prefix +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\10" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\06\fd\bf\bf\bf\bf\bf" ;; "\fd\bf\bf\bf\bf\bf" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;;;; Miscellaneous malformed bytes + +;; malformed byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\01\fe" ;; "\fe" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; malformed byte +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0b" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\01\ff" ;; "\ff" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; UTF-16BE BOM +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\fe\ff" ;; "\fe\ff" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; UTF-32BE BOM +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\00\00\fe\ff" ;; "\00\00\fe\ff" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; UTF-16LE BOM +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0c" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\02\ff\fe" ;; "\ff\fe" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + +;; UTF-32LE BOM +(assert_malformed + (module binary + "\00asm" "\01\00\00\00" + "\02\0e" ;; import section + "\01" ;; length 1 + "\04\74\65\73\74" ;; "test" + "\04\ff\fe\00\00" ;; "\ff\fe\00\00" + "\03" ;; GlobalImport + "\7f" ;; i32 + "\00" ;; immutable + ) + "malformed UTF-8 encoding" +) + diff --git a/runtime/unc-vm/tests/wast/spec/utf8-invalid-encoding.wast b/runtime/unc-vm/tests/wast/spec/utf8-invalid-encoding.wast new file mode 100644 index 000000000..cfd596930 --- /dev/null +++ b/runtime/unc-vm/tests/wast/spec/utf8-invalid-encoding.wast @@ -0,0 +1,176 @@ +(assert_malformed (module quote "(func (export \"\\00\\00\\fe\\ff\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\8f\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\90\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\9f\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\a0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\bf\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\c0\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\c0\\bf\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\c1\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\c1\\bf\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\c2\\00\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\c2\\2e\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\c2\\7f\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\c2\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\c2\\c0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\c2\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\c2\\fd\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\df\\00\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\df\\7f\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\df\\c0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\df\\fd\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e0\\00\\a0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e0\\7f\\a0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e0\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e0\\80\\a0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e0\\9f\\a0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e0\\9f\\bf\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e0\\a0\\00\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e0\\a0\\7f\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e0\\a0\\c0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e0\\a0\\fd\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e0\\c0\\a0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e0\\fd\\a0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e1\\00\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e1\\2e\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e1\\7f\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e1\\80\\00\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e1\\80\\2e\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e1\\80\\7f\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e1\\80\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e1\\80\\c0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e1\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e1\\80\\fd\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e1\\c0\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e1\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\e1\\fd\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ec\\00\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ec\\7f\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ec\\80\\00\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ec\\80\\7f\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ec\\80\\c0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ec\\80\\fd\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ec\\c0\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ec\\fd\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ed\\00\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ed\\7f\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ed\\80\\00\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ed\\80\\7f\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ed\\80\\c0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ed\\80\\fd\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ed\\a0\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ed\\a0\\bf\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ed\\bf\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ed\\bf\\bf\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ed\\c0\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ed\\fd\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ee\\00\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ee\\7f\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ee\\80\\00\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ee\\80\\7f\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ee\\80\\c0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ee\\80\\fd\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ee\\c0\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ee\\fd\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ef\\00\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ef\\7f\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ef\\80\\00\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ef\\80\\7f\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ef\\80\\c0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ef\\80\\fd\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ef\\c0\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ef\\fd\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\00\\90\\90\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\7f\\90\\90\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\80\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\80\\90\\90\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\8f\\90\\90\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\8f\\bf\\bf\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\90\\00\\90\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\90\\7f\\90\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\90\\90\\00\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\90\\90\\7f\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\90\\90\\c0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\90\\90\\fd\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\90\\c0\\90\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\90\\fd\\90\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\c0\\90\\90\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f0\\fd\\90\\90\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\00\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\23\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\7f\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\80\\00\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\80\\23\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\80\\7f\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\80\\80\\00\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\80\\80\\23\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\80\\80\\7f\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\80\\80\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\80\\80\\c0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\80\\80\\fd\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\80\\c0\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\80\\fd\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\c0\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f1\\fd\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f3\\00\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f3\\7f\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f3\\80\\00\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f3\\80\\7f\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f3\\80\\80\\00\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f3\\80\\80\\7f\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f3\\80\\80\\c0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f3\\80\\80\\fd\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f3\\80\\c0\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f3\\80\\fd\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f3\\c0\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f3\\fd\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f4\\00\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f4\\7f\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f4\\80\\00\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f4\\80\\7f\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f4\\80\\80\\00\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f4\\80\\80\\7f\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f4\\80\\80\\c0\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f4\\80\\80\\fd\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f4\\80\\c0\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f4\\80\\fd\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f4\\90\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f4\\bf\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f4\\c0\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f4\\fd\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f5\\80\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f7\\80\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f7\\bf\\bf\\bf\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f8\\23\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f8\\80\\23\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f8\\80\\80\\23\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f8\\80\\80\\80\\23\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f8\\80\\80\\80\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f8\\80\\80\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f8\\80\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f8\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f8\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\f8\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fb\\bf\\bf\\bf\\bf\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fc\\23\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fc\\80\\23\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fc\\80\\80\\23\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fc\\80\\80\\80\\23\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fc\\80\\80\\80\\80\\23\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fc\\80\\80\\80\\80\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fc\\80\\80\\80\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fc\\80\\80\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fc\\80\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fc\\80\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fc\\80\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fc\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fd\\bf\\bf\\bf\\bf\\bf\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fe\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\fe\\ff\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ff\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ff\\fe\\00\\00\"))") "malformed UTF-8 encoding") +(assert_malformed (module quote "(func (export \"\\ff\\fe\"))") "malformed UTF-8 encoding") diff --git a/runtime/unc-vm/tests/wast/wasmer/README.md b/runtime/unc-vm/tests/wast/wasmer/README.md new file mode 100644 index 000000000..976146c24 --- /dev/null +++ b/runtime/unc-vm/tests/wast/wasmer/README.md @@ -0,0 +1,30 @@ +# Custom wast tests + +In this directory we have created wast tests for different cases +where we want to test other scenarios than the ones offered +by the standard WebAssembly spectests. + +## NaN canonicalization: `nan-canonicalization.wast` + +This is an extra set of tests that assure that operations with NaNs +are deterministic regarless of the environment/chipset where it executes in. + +## Call Indirect Spilled Stack: `call-indirect-spilledd-stack.wast` + +We had an issue occuring that was making singlepass not working properly +on the WebAssembly benchmark: https://00f.net/2019/10/22/updated-webassembly-benchmark/. + +This is a test case to ensure it doesn't reproduce again in the future. + +## Multiple Traps: `multiple-traps.wast` + +This is a test assuring functions that trap can be called multiple times. + +## Fac: `fac.wast` + +This is a simple factorial program. + +## Check that struct-return on the stack doesn't overflow: `stack-overflow-sret.wast` + +Stack space for a structure returning function call should be allocated once up +front, not once in each call. diff --git a/runtime/unc-vm/tests/wast/wasmer/call-indirect-spilled-stack.wast b/runtime/unc-vm/tests/wast/wasmer/call-indirect-spilled-stack.wast new file mode 100644 index 000000000..95df771f8 --- /dev/null +++ b/runtime/unc-vm/tests/wast/wasmer/call-indirect-spilled-stack.wast @@ -0,0 +1,35 @@ +;; Spilled stack tests +;; https://github.com/wasmerio/wasmer/pull/1191 + +(module + ;; Auxiliary definitions + (type $out-i32 (func (result i32))) + + (func $const-i32 (type $out-i32) (i32.const 0x132)) + + (table funcref + (elem + $const-i32 + ) + ) + + (memory 1) + + (func (export "call-indirect-from-spilled-stack") (result i32) + (i64.add (i64.const 0) (i64.const 0)) + (i64.add (i64.const 0) (i64.const 0)) + (i64.add (i64.const 0) (i64.const 0)) + (i64.add (i64.const 0) (i64.const 0)) + (i64.add (i64.const 0) (i64.const 0)) + (i64.add (i64.const 0) (i64.const 0)) + (i64.add (i64.const 0) (i64.const 0)) + (i64.add (i64.const 0) (i64.const 0)) + (i64.add (i64.const 0) (i64.const 0)) + (i64.add (i64.const 0x100000000) (i64.const 0)) + (i32.wrap_i64) + (call_indirect (type $out-i32)) + (return) + ) +) + +(assert_return (invoke "call-indirect-from-spilled-stack") (i32.const 0x132)) diff --git a/runtime/unc-vm/tests/wast/wasmer/fac.wast b/runtime/unc-vm/tests/wast/wasmer/fac.wast new file mode 100644 index 000000000..ef10991a8 --- /dev/null +++ b/runtime/unc-vm/tests/wast/wasmer/fac.wast @@ -0,0 +1,89 @@ +(module + ;; Recursive factorial + (func (export "fac-rec") (param i64) (result i64) + (if (result i64) (i64.eq (local.get 0) (i64.const 0)) + (then (i64.const 1)) + (else + (i64.mul (local.get 0) (call 0 (i64.sub (local.get 0) (i64.const 1)))) + ) + ) + ) + + ;; Recursive factorial named + (func $fac-rec-named (export "fac-rec-named") (param $n i64) (result i64) + (if (result i64) (i64.eq (local.get $n) (i64.const 0)) + (then (i64.const 1)) + (else + (i64.mul + (local.get $n) + (call $fac-rec-named (i64.sub (local.get $n) (i64.const 1))) + ) + ) + ) + ) + + ;; Iterative factorial + (func (export "fac-iter") (param i64) (result i64) + (local i64 i64) + (local.set 1 (local.get 0)) + (local.set 2 (i64.const 1)) + (block + (loop + (if + (i64.eq (local.get 1) (i64.const 0)) + (then (br 2)) + (else + (local.set 2 (i64.mul (local.get 1) (local.get 2))) + (local.set 1 (i64.sub (local.get 1) (i64.const 1))) + ) + ) + (br 0) + ) + ) + (local.get 2) + ) + + ;; Iterative factorial named + (func (export "fac-iter-named") (param $n i64) (result i64) + (local $i i64) + (local $res i64) + (local.set $i (local.get $n)) + (local.set $res (i64.const 1)) + (block $done + (loop $loop + (if + (i64.eq (local.get $i) (i64.const 0)) + (then (br $done)) + (else + (local.set $res (i64.mul (local.get $i) (local.get $res))) + (local.set $i (i64.sub (local.get $i) (i64.const 1))) + ) + ) + (br $loop) + ) + ) + (local.get $res) + ) + + ;; Optimized factorial. + (func (export "fac-opt") (param i64) (result i64) + (local i64) + (local.set 1 (i64.const 1)) + (block + (br_if 0 (i64.lt_s (local.get 0) (i64.const 2))) + (loop + (local.set 1 (i64.mul (local.get 1) (local.get 0))) + (local.set 0 (i64.add (local.get 0) (i64.const -1))) + (br_if 0 (i64.gt_s (local.get 0) (i64.const 1))) + ) + ) + (local.get 1) + ) +) + +(assert_return (invoke "fac-rec" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-iter" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-rec-named" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-iter-named" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_return (invoke "fac-opt" (i64.const 25)) (i64.const 7034535277573963776)) +(assert_exhaustion (invoke "fac-rec" (i64.const 1073741824)) "call stack exhausted") diff --git a/runtime/unc-vm/tests/wast/wasmer/int-extend-garbage.wast b/runtime/unc-vm/tests/wast/wasmer/int-extend-garbage.wast new file mode 100644 index 000000000..9b290d26f --- /dev/null +++ b/runtime/unc-vm/tests/wast/wasmer/int-extend-garbage.wast @@ -0,0 +1,35 @@ +;; https://github.com/wasmerio/wasmer/pull/1436 +;; +;; When doing an I64ExtendI32U or other integer extension operations, the +;; upper bits in the underlying storage must be cleared. +;; +;; On x86 sign extension is done with its own instruction, `movsx`, so here we only +;; test the unsigned extension case. + +(module + + (func (export "i64-extend-i32-u") (result i64) + ;; fill in stack slots allocated to registers + (i64.add (i64.const 0) (i64.const 0)) + (i64.add (i64.const 0) (i64.const 0)) + (i64.add (i64.const 0) (i64.const 0)) + (i64.add (i64.const 0) (i64.const 0)) + (i64.add (i64.const 0) (i64.const 0)) + (i64.add (i64.const 0) (i64.const 0)) + + ;; push an i64 to produce garbage on the higher 32 bits + (i64.add (i64.const -1) (i64.const 0)) + + ;; pop it + (drop) + + ;; push an i32 + (i32.add (i32.const 0) (i32.const 0)) + + ;; extend + (i64.extend_i32_u) + (return) + ) +) + +(assert_return (invoke "i64-extend-i32-u") (i64.const 0)) diff --git a/runtime/unc-vm/tests/wast/wasmer/max_locals.wast b/runtime/unc-vm/tests/wast/wasmer/max_locals.wast new file mode 100644 index 000000000..6fe0355d9 --- /dev/null +++ b/runtime/unc-vm/tests/wast/wasmer/max_locals.wast @@ -0,0 +1,37 @@ +;; As per https://www.w3.org/TR/wasm-core-1/#functions%E2%91%A0 functions may not specify more than +;; u32::MAX locals. Make sure this holds and does not break. + + +;; This is a validation failure +(assert_invalid + (module binary + "\00asm\01\00\00\00" ;; the header + "\01\04\01" ;; 4 byte type section with 1 element + "\60\00\00" ;; fn() -> () + "\03\02\01" ;; 2 byte func section with 1 element + "\00" ;; signature 0 + "\0a\0a\01" ;; 11 byte code section with 1 element + "\08" ;; 4 bytes for this function + "\01\ff\ff\ff\ff\0f\7f" ;; 1 local block containing 0xffff_ffff locals of type i32 + "\0b" ;; end + ) + "locals exceed maximum" +) + +;; Ensure that we don't hit any panics with > 0xFFFF_FFFF locals. +(assert_invalid + (module binary + "\00asm\01\00\00\00" ;; the header + "\01\04\01" ;; 4 byte type section with 1 element + "\60\00\00" ;; fn() -> () + "\03\02\01" ;; 2 byte func section with 1 element + "\00" ;; signature 0 + "\0a\0c\01" ;; 11 byte code section with 1 element + "\0a" ;; 11 bytes for this function + "\02" ;; 2 local blocks + "\ff\ff\ff\ff\0f\7f" ;; local block containing 0xffff_ffff locals of type i32 + "\7f\7f" ;; local block containing 0x7f locals of type i32 + "\0b" ;; end + ) + "locals exceed maximum" +) diff --git a/runtime/unc-vm/tests/wast/wasmer/max_size_of_memory.wast b/runtime/unc-vm/tests/wast/wasmer/max_size_of_memory.wast new file mode 100644 index 000000000..99116d1e7 --- /dev/null +++ b/runtime/unc-vm/tests/wast/wasmer/max_size_of_memory.wast @@ -0,0 +1 @@ +(module (memory 65536)) diff --git a/runtime/unc-vm/tests/wast/wasmer/multiple-traps.wast b/runtime/unc-vm/tests/wast/wasmer/multiple-traps.wast new file mode 100644 index 000000000..99b8e7ba2 --- /dev/null +++ b/runtime/unc-vm/tests/wast/wasmer/multiple-traps.wast @@ -0,0 +1,10 @@ +;; We assert that we can call a function that traps repeatedly + +(module + (func (export "throw_trap") + unreachable + )) + +(assert_trap (invoke "throw_trap") "unreachable") +(assert_trap (invoke "throw_trap") "unreachable") +(assert_trap (invoke "throw_trap") "unreachable") diff --git a/runtime/unc-vm/tests/wast/wasmer/nan-canonicalization-issue-2159.wast b/runtime/unc-vm/tests/wast/wasmer/nan-canonicalization-issue-2159.wast new file mode 100644 index 000000000..6a1253ce2 --- /dev/null +++ b/runtime/unc-vm/tests/wast/wasmer/nan-canonicalization-issue-2159.wast @@ -0,0 +1,10 @@ +;; https://github.com/wasmerio/wasmer/issues/2159 +(module + (func (export "_start") (result f64) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 (;=0;) + f64.div + f64.copysign + ) +) \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/wasmer/nan-canonicalization-issue-2347.wast b/runtime/unc-vm/tests/wast/wasmer/nan-canonicalization-issue-2347.wast new file mode 100644 index 000000000..575d33c65 --- /dev/null +++ b/runtime/unc-vm/tests/wast/wasmer/nan-canonicalization-issue-2347.wast @@ -0,0 +1,14 @@ +;; https://github.com/wasmerio/wasmer/issues/2347 +(module + (type (;0;) (func (param f64) (result i32))) + (func (;0;) (type 0) (param f64) (result i32) + unreachable) + (func (;1;) (type 0) (param f64) (result i32) + i32.const -16579585 + f64.convert_i32_s + f64.ceil + f64.ceil + local.get 0 + f64.copysign + unreachable)) + \ No newline at end of file diff --git a/runtime/unc-vm/tests/wast/wasmer/nan-canonicalization.wast b/runtime/unc-vm/tests/wast/wasmer/nan-canonicalization.wast new file mode 100644 index 000000000..748029bf5 --- /dev/null +++ b/runtime/unc-vm/tests/wast/wasmer/nan-canonicalization.wast @@ -0,0 +1,221 @@ +;; NaN canonicalization tests. +;; +;; Things that are covered by spectests canonicalization +;; (`fabs`, `fneg`, `fcopysign`, `reinterpret`, `const`) +;; won't be duplicated here. + +(module + ;; Auxiliary definitions + (type $f32-id (func (param f32) (result f32))) + (type $f64-id (func (param f64) (result f64))) + + (table funcref + (elem + $nan-canonicalization-f32-func-call-target + $nan-canonicalization-f64-func-call-target + ) + ) + + (memory 1) + + (func (export "nan-canonicalization-f32-add") (param i32) (result i32) + (i32.reinterpret_f32 (f32.add (f32.reinterpret_i32 (get_local 0)) (f32.const 0))) + ) + (func (export "nan-canonicalization-f32-sub") (param i32) (result i32) + (i32.reinterpret_f32 (f32.sub (f32.reinterpret_i32 (get_local 0)) (f32.const 0))) + ) + (func (export "nan-canonicalization-f32-mul") (param i32) (result i32) + (i32.reinterpret_f32 (f32.mul (f32.reinterpret_i32 (get_local 0)) (f32.const 0))) + ) + (func (export "nan-canonicalization-f32-div") (param i32) (result i32) + (i32.reinterpret_f32 (f32.div (f32.reinterpret_i32 (get_local 0)) (f32.const 1))) + ) + (func (export "nan-canonicalization-f32-max") (param i32) (result i32) + (i32.reinterpret_f32 (f32.max (f32.reinterpret_i32 (get_local 0)) (f32.const 1))) + ) + (func (export "nan-canonicalization-f32-min") (param i32) (result i32) + (i32.reinterpret_f32 (f32.min (f32.reinterpret_i32 (get_local 0)) (f32.const 1))) + ) + (func (export "nan-canonicalization-f32-nearest") (param i32) (result i32) + (i32.reinterpret_f32 (f32.nearest (f32.reinterpret_i32 (get_local 0)))) + ) + (func (export "nan-canonicalization-f32-floor") (param i32) (result i32) + (i32.reinterpret_f32 (f32.floor (f32.reinterpret_i32 (get_local 0)))) + ) + (func (export "nan-canonicalization-f32-ceil") (param i32) (result i32) + (i32.reinterpret_f32 (f32.ceil (f32.reinterpret_i32 (get_local 0)))) + ) + (func (export "nan-canonicalization-f32-trunc") (param i32) (result i32) + (i32.reinterpret_f32 (f32.trunc (f32.reinterpret_i32 (get_local 0)))) + ) + (func (export "nan-canonicalization-f32-sqrt") (param i32) (result i32) + (i32.reinterpret_f32 (f32.sqrt (f32.reinterpret_i32 (get_local 0)))) + ) + (func (export "nan-canonicalization-f32-mem") (param i32) (result i32) + (f32.store (i32.const 0) (f32.reinterpret_i32 (get_local 0))) + (i32.reinterpret_f32 (f32.load (i32.const 0))) + ) + (func (export "nan-canonicalization-f32-mem-cncl") (param i32) (result i32) + (f32.store (i32.const 0) (f32.add (f32.reinterpret_i32 (get_local 0)) (f32.const 0))) + (i32.reinterpret_f32 (f32.load (i32.const 0))) + ) + (func (export "nan-canonicalization-f32-local") (param i32) (result i32) + (local f32) + (set_local 1 (f32.reinterpret_i32 (get_local 0))) + (i32.reinterpret_f32 (get_local 1)) + ) + (func (export "nan-canonicalization-f32-local-cncl") (param i32) (result i32) + (local f32) + (set_local 1 (f32.add (f32.reinterpret_i32 (get_local 0)) (f32.const 0))) + (i32.reinterpret_f32 (get_local 1)) + ) + (func $nan-canonicalization-f32-func-call-target (param f32) (result f32) + (get_local 0) + ) + (func (export "nan-canonicalization-f32-func-call") (param i32) (result i32) + (i32.reinterpret_f32 (call $nan-canonicalization-f32-func-call-target (f32.reinterpret_i32 (get_local 0)))) + ) + (func (export "nan-canonicalization-f32-func-call-cncl") (param i32) (result i32) + (i32.reinterpret_f32 (call $nan-canonicalization-f32-func-call-target (f32.add (f32.reinterpret_i32 (get_local 0)) (f32.const 0)))) + ) + (func (export "nan-canonicalization-f32-func-call-indirect") (param i32) (result i32) + (i32.reinterpret_f32 (call_indirect (type $f32-id) (f32.reinterpret_i32 (get_local 0)) (i32.const 0))) + ) + (func (export "nan-canonicalization-f32-func-call-indirect-cncl") (param i32) (result i32) + (i32.reinterpret_f32 (call_indirect (type $f32-id) (f32.add (f32.reinterpret_i32 (get_local 0)) (f32.const 0)) (i32.const 0))) + ) + + (func (export "nan-canonicalization-f64-add") (param i64) (result i64) + (i64.reinterpret_f64 (f64.add (f64.reinterpret_i64 (get_local 0)) (f64.const 0))) + ) + (func (export "nan-canonicalization-f64-sub") (param i64) (result i64) + (i64.reinterpret_f64 (f64.sub (f64.reinterpret_i64 (get_local 0)) (f64.const 0))) + ) + (func (export "nan-canonicalization-f64-mul") (param i64) (result i64) + (i64.reinterpret_f64 (f64.mul (f64.reinterpret_i64 (get_local 0)) (f64.const 0))) + ) + (func (export "nan-canonicalization-f64-div") (param i64) (result i64) + (i64.reinterpret_f64 (f64.div (f64.reinterpret_i64 (get_local 0)) (f64.const 1))) + ) + (func (export "nan-canonicalization-f64-max") (param i64) (result i64) + (i64.reinterpret_f64 (f64.max (f64.reinterpret_i64 (get_local 0)) (f64.const 1))) + ) + (func (export "nan-canonicalization-f64-min") (param i64) (result i64) + (i64.reinterpret_f64 (f64.min (f64.reinterpret_i64 (get_local 0)) (f64.const 1))) + ) + (func (export "nan-canonicalization-f64-nearest") (param i64) (result i64) + (i64.reinterpret_f64 (f64.nearest (f64.reinterpret_i64 (get_local 0)))) + ) + (func (export "nan-canonicalization-f64-floor") (param i64) (result i64) + (i64.reinterpret_f64 (f64.floor (f64.reinterpret_i64 (get_local 0)))) + ) + (func (export "nan-canonicalization-f64-ceil") (param i64) (result i64) + (i64.reinterpret_f64 (f64.ceil (f64.reinterpret_i64 (get_local 0)))) + ) + (func (export "nan-canonicalization-f64-trunc") (param i64) (result i64) + (i64.reinterpret_f64 (f64.trunc (f64.reinterpret_i64 (get_local 0)))) + ) + (func (export "nan-canonicalization-f64-sqrt") (param i64) (result i64) + (i64.reinterpret_f64 (f64.sqrt (f64.reinterpret_i64 (get_local 0)))) + ) + (func (export "nan-canonicalization-f64-mem") (param i64) (result i64) + (f64.store (i32.const 0) (f64.reinterpret_i64 (get_local 0))) + (i64.reinterpret_f64 (f64.load (i32.const 0))) + ) + (func (export "nan-canonicalization-f64-mem-cncl") (param i64) (result i64) + (f64.store (i32.const 0) (f64.add (f64.reinterpret_i64 (get_local 0)) (f64.const 0))) + (i64.reinterpret_f64 (f64.load (i32.const 0))) + ) + (func (export "nan-canonicalization-f64-local") (param i64) (result i64) + (local f64) + (set_local 1 (f64.reinterpret_i64 (get_local 0))) + (i64.reinterpret_f64 (get_local 1)) + ) + (func (export "nan-canonicalization-f64-local-cncl") (param i64) (result i64) + (local f64) + (set_local 1 (f64.add (f64.reinterpret_i64 (get_local 0)) (f64.const 0))) + (i64.reinterpret_f64 (get_local 1)) + ) + (func $nan-canonicalization-f64-func-call-target (param f64) (result f64) + (get_local 0) + ) + (func (export "nan-canonicalization-f64-func-call") (param i64) (result i64) + (i64.reinterpret_f64 (call $nan-canonicalization-f64-func-call-target (f64.reinterpret_i64 (get_local 0)))) + ) + (func (export "nan-canonicalization-f64-func-call-cncl") (param i64) (result i64) + (i64.reinterpret_f64 (call $nan-canonicalization-f64-func-call-target (f64.add (f64.reinterpret_i64 (get_local 0)) (f64.const 0)))) + ) + (func (export "nan-canonicalization-f64-func-call-indirect") (param i64) (result i64) + (i64.reinterpret_f64 (call_indirect (type $f64-id) (f64.reinterpret_i64 (get_local 0)) (i32.const 1))) + ) + (func (export "nan-canonicalization-f64-func-call-indirect-cncl") (param i64) (result i64) + (i64.reinterpret_f64 (call_indirect (type $f64-id) (f64.add (f64.reinterpret_i64 (get_local 0)) (f64.const 0)) (i32.const 1))) + ) +) + +(assert_return (invoke "nan-canonicalization-f32-add" (i32.const 0x7fc00001)) (i32.const 0x7fc00000)) +(assert_return (invoke "nan-canonicalization-f32-sub" (i32.const 0x7fc00001)) (i32.const 0x7fc00000)) +(assert_return (invoke "nan-canonicalization-f32-mul" (i32.const 0x7fc00001)) (i32.const 0x7fc00000)) +(assert_return (invoke "nan-canonicalization-f32-div" (i32.const 0x7fc00001)) (i32.const 0x7fc00000)) +(assert_return (invoke "nan-canonicalization-f32-max" (i32.const 0x7fc00001)) (i32.const 0x7fc00000)) +(assert_return (invoke "nan-canonicalization-f32-min" (i32.const 0x7fc00001)) (i32.const 0x7fc00000)) +(assert_return (invoke "nan-canonicalization-f32-nearest" (i32.const 0x7fc00001)) (i32.const 0x7fc00000)) +(assert_return (invoke "nan-canonicalization-f32-floor" (i32.const 0x7fc00001)) (i32.const 0x7fc00000)) +(assert_return (invoke "nan-canonicalization-f32-ceil" (i32.const 0x7fc00001)) (i32.const 0x7fc00000)) +(assert_return (invoke "nan-canonicalization-f32-trunc" (i32.const 0x7fc00001)) (i32.const 0x7fc00000)) +(assert_return (invoke "nan-canonicalization-f32-sqrt" (i32.const 0x7fc00001)) (i32.const 0x7fc00000)) +(assert_return (invoke "nan-canonicalization-f32-mem" (i32.const 0x7fc00001)) (i32.const 0x7fc00001)) +(assert_return (invoke "nan-canonicalization-f32-mem-cncl" (i32.const 0x7fc00001)) (i32.const 0x7fc00000)) +(assert_return (invoke "nan-canonicalization-f32-local" (i32.const 0x7fc00001)) (i32.const 0x7fc00001)) +(assert_return (invoke "nan-canonicalization-f32-local-cncl" (i32.const 0x7fc00001)) (i32.const 0x7fc00000)) +(assert_return (invoke "nan-canonicalization-f32-func-call" (i32.const 0x7fc00001)) (i32.const 0x7fc00001)) +(assert_return (invoke "nan-canonicalization-f32-func-call-cncl" (i32.const 0x7fc00001)) (i32.const 0x7fc00000)) +(assert_return (invoke "nan-canonicalization-f32-func-call-indirect" (i32.const 0x7fc00001)) (i32.const 0x7fc00001)) +(assert_return (invoke "nan-canonicalization-f32-func-call-indirect-cncl" (i32.const 0x7fc00001)) (i32.const 0x7fc00000)) + +(assert_return (invoke "nan-canonicalization-f64-add" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "nan-canonicalization-f64-sub" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "nan-canonicalization-f64-mul" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "nan-canonicalization-f64-div" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "nan-canonicalization-f64-max" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "nan-canonicalization-f64-min" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "nan-canonicalization-f64-nearest" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "nan-canonicalization-f64-floor" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "nan-canonicalization-f64-ceil" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "nan-canonicalization-f64-trunc" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "nan-canonicalization-f64-sqrt" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "nan-canonicalization-f64-mem" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000001)) +(assert_return (invoke "nan-canonicalization-f64-mem-cncl" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "nan-canonicalization-f64-local" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000001)) +(assert_return (invoke "nan-canonicalization-f64-local-cncl" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "nan-canonicalization-f64-func-call" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000001)) +(assert_return (invoke "nan-canonicalization-f64-func-call-cncl" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000)) +(assert_return (invoke "nan-canonicalization-f64-func-call-indirect" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000001)) +(assert_return (invoke "nan-canonicalization-f64-func-call-indirect-cncl" (i64.const 0x7ff8000000000001)) (i64.const 0x7ff8000000000000)) + +;; Test canonicalization is done before branch in `else` operator. +(module + (func (;0;) + (local f64) + i32.const 1 + if (result f64) + local.get 0 + f64.const 0x1p+0 (;=1;) + f64.add + else + f64.const 0x0p+0 (;=0;) + end + return + ) +) + +;; Test canonicalization is done before branch in `return` operator. +(module + (func (;0;) (result f64) + (local f64) + f64.const 0x0p+0 + local.get 0 + f64.mul + return + ) +) diff --git a/runtime/unc-vm/tests/wast/wasmer/rotate-shift-overflow.wast b/runtime/unc-vm/tests/wast/wasmer/rotate-shift-overflow.wast new file mode 100644 index 000000000..456ea414f --- /dev/null +++ b/runtime/unc-vm/tests/wast/wasmer/rotate-shift-overflow.wast @@ -0,0 +1,334 @@ +;; Test that constant folding an operation that overflows doesn't produce an +;; undefined value. Changing these tests to move the constants to the +;; assert_return line hides the bug. + +(module + ;; shl i32 + (func (export "shl1_i32") (result i32) + i32.const 235 + i32.const 0 + i32.shl + ) + (func (export "shl2_i32") (result i32) + i32.const 235 + i32.const 32 + i32.shl + ) + (func (export "shl3_i32") (result i32) + i32.const 235 + i32.const 100 + i32.shl + ) + (func (export "shl4_i32") (result i32) + i32.const 235 + i32.const -32 + i32.shl + ) + (func (export "shl5_i32") (result i32) + i32.const 235 + i32.const -100 + i32.shl + ) + ;; shl i64 + (func (export "shl1_i64") (result i64) + i64.const 235 + i64.const 0 + i64.shl + ) + (func (export "shl2_i64") (result i64) + i64.const 235 + i64.const 64 + i64.shl + ) + (func (export "shl3_i64") (result i64) + i64.const 235 + i64.const 100 + i64.shl + ) + (func (export "shl4_i64") (result i64) + i64.const 235 + i64.const -64 + i64.shl + ) + (func (export "shl5_i64") (result i64) + i64.const 235 + i64.const -100 + i64.shl + ) + + ;; shr_u i32 + (func (export "shr_u1_i32") (result i32) + i32.const 235 + i32.const 0 + i32.shr_u + ) + (func (export "shr_u2_i32") (result i32) + i32.const 235 + i32.const 32 + i32.shr_u + ) + (func (export "shr_u3_i32") (result i32) + i32.const 235 + i32.const 100 + i32.shr_u + ) + (func (export "shr_u4_i32") (result i32) + i32.const 235 + i32.const -32 + i32.shr_u + ) + (func (export "shr_u5_i32") (result i32) + i32.const 235 + i32.const -100 + i32.shr_u + ) + + ;; shr_u i64 + (func (export "shr_u1_i64") (result i64) + i64.const 235 + i64.const 0 + i64.shr_u + ) + (func (export "shr_u2_i64") (result i64) + i64.const 235 + i64.const 64 + i64.shr_u + ) + (func (export "shr_u3_i64") (result i64) + i64.const 235 + i64.const 100 + i64.shr_u + ) + (func (export "shr_u4_i64") (result i64) + i64.const 235 + i64.const -64 + i64.shr_u + ) + (func (export "shr_u5_i64") (result i64) + i64.const 235 + i64.const -100 + i64.shr_u + ) + + ;; shr_s i32 + (func (export "shr_s1_i32") (result i32) + i32.const 235 + i32.const 0 + i32.shr_s + ) + (func (export "shr_s2_i32") (result i32) + i32.const 235 + i32.const 32 + i32.shr_s + ) + (func (export "shr_s3_i32") (result i32) + i32.const 235 + i32.const 100 + i32.shr_s + ) + (func (export "shr_s4_i32") (result i32) + i32.const 235 + i32.const -32 + i32.shr_s + ) + (func (export "shr_s5_i32") (result i32) + i32.const 235 + i32.const -100 + i32.shr_s + ) + + ;; shr_s i64 + (func (export "shr_s1_i64") (result i64) + i64.const 235 + i64.const 0 + i64.shr_s + ) + (func (export "shr_s2_i64") (result i64) + i64.const 235 + i64.const 64 + i64.shr_s + ) + (func (export "shr_s3_i64") (result i64) + i64.const 235 + i64.const 100 + i64.shr_s + ) + (func (export "shr_s4_i64") (result i64) + i64.const 235 + i64.const -64 + i64.shr_s + ) + (func (export "shr_s5_i64") (result i64) + i64.const 235 + i64.const -100 + i64.shr_s + ) + + ;; rotl i32 + (func (export "rotl1_i32") (result i32) + i32.const 235 + i32.const 0 + i32.rotl + ) + (func (export "rotl2_i32") (result i32) + i32.const 235 + i32.const 32 + i32.rotl + ) + (func (export "rotl3_i32") (result i32) + i32.const 235 + i32.const 100 + i32.rotl + ) + (func (export "rotl4_i32") (result i32) + i32.const 235 + i32.const -32 + i32.rotl + ) + (func (export "rotl5_i32") (result i32) + i32.const 235 + i32.const -100 + i32.rotl + ) + + ;; rotl i64 + (func (export "rotl1_i64") (result i64) + i64.const 235 + i64.const 0 + i64.rotl + ) + (func (export "rotl2_i64") (result i64) + i64.const 235 + i64.const 64 + i64.rotl + ) + (func (export "rotl3_i64") (result i64) + i64.const 235 + i64.const 100 + i64.rotl + ) + (func (export "rotl4_i64") (result i64) + i64.const 235 + i64.const -64 + i64.rotl + ) + (func (export "rotl5_i64") (result i64) + i64.const 235 + i64.const -100 + i64.rotl + ) + + ;; rotr i32 + (func (export "rotr1_i32") (result i32) + i32.const 235 + i32.const 0 + i32.rotr + ) + (func (export "rotr2_i32") (result i32) + i32.const 235 + i32.const 32 + i32.rotr + ) + (func (export "rotr3_i32") (result i32) + i32.const 235 + i32.const 100 + i32.rotr + ) + (func (export "rotr4_i32") (result i32) + i32.const 235 + i32.const -32 + i32.rotr + ) + (func (export "rotr5_i32") (result i32) + i32.const 235 + i32.const -100 + i32.rotr + ) + + ;; rotr i64 + (func (export "rotr1_i64") (result i64) + i64.const 235 + i64.const 0 + i64.rotr + ) + (func (export "rotr2_i64") (result i64) + i64.const 235 + i64.const 64 + i64.rotr + ) + (func (export "rotr3_i64") (result i64) + i64.const 235 + i64.const 100 + i64.rotr + ) + (func (export "rotr4_i64") (result i64) + i64.const 235 + i64.const -64 + i64.rotr + ) + (func (export "rotr5_i64") (result i64) + i64.const 235 + i64.const -100 + i64.rotr + ) +) + +(assert_return (invoke "shl1_i32") (i32.const 235)) +(assert_return (invoke "shl2_i32") (i32.const 235)) +(assert_return (invoke "shl3_i32") (i32.const 3760)) +(assert_return (invoke "shl4_i32") (i32.const 235)) +(assert_return (invoke "shl5_i32") (i32.const -1342177280)) + +(assert_return (invoke "shl1_i64") (i64.const 235)) +(assert_return (invoke "shl2_i64") (i64.const 235)) +(assert_return (invoke "shl3_i64") (i64.const 16149077032960)) +(assert_return (invoke "shl4_i64") (i64.const 235)) +(assert_return (invoke "shl5_i64") (i64.const 63082332160)) + +(assert_return (invoke "shr_u1_i32") (i32.const 235)) +(assert_return (invoke "shr_u2_i32") (i32.const 235)) +(assert_return (invoke "shr_u3_i32") (i32.const 14)) +(assert_return (invoke "shr_u4_i32") (i32.const 235)) +(assert_return (invoke "shr_u5_i32") (i32.const 0)) + +(assert_return (invoke "shr_u1_i64") (i64.const 235)) +(assert_return (invoke "shr_u2_i64") (i64.const 235)) +(assert_return (invoke "shr_u3_i64") (i64.const 0)) +(assert_return (invoke "shr_u4_i64") (i64.const 235)) +(assert_return (invoke "shr_u5_i64") (i64.const 0)) + +(assert_return (invoke "shr_s1_i32") (i32.const 235)) +(assert_return (invoke "shr_s2_i32") (i32.const 235)) +(assert_return (invoke "shr_s3_i32") (i32.const 14)) +(assert_return (invoke "shr_s4_i32") (i32.const 235)) +(assert_return (invoke "shr_s5_i32") (i32.const 0)) + +(assert_return (invoke "shr_s1_i64") (i64.const 235)) +(assert_return (invoke "shr_s2_i64") (i64.const 235)) +(assert_return (invoke "shr_s3_i64") (i64.const 0)) +(assert_return (invoke "shr_s4_i64") (i64.const 235)) +(assert_return (invoke "shr_s5_i64") (i64.const 0)) + +(assert_return (invoke "rotl1_i32") (i32.const 235)) +(assert_return (invoke "rotl2_i32") (i32.const 235)) +(assert_return (invoke "rotl3_i32") (i32.const 3760)) +(assert_return (invoke "rotl4_i32") (i32.const 235)) +(assert_return (invoke "rotl5_i32") (i32.const -1342177266)) + +(assert_return (invoke "rotl1_i64") (i64.const 235)) +(assert_return (invoke "rotl2_i64") (i64.const 235)) +(assert_return (invoke "rotl3_i64") (i64.const 16149077032960)) +(assert_return (invoke "rotl4_i64") (i64.const 235)) +(assert_return (invoke "rotl5_i64") (i64.const 63082332160)) + +(assert_return (invoke "rotr1_i32") (i32.const 235)) +(assert_return (invoke "rotr2_i32") (i32.const 235)) +(assert_return (invoke "rotr3_i32") (i32.const -1342177266)) +(assert_return (invoke "rotr4_i32") (i32.const 235)) +(assert_return (invoke "rotr5_i32") (i32.const 3760)) + +(assert_return (invoke "rotr1_i64") (i64.const 235)) +(assert_return (invoke "rotr2_i64") (i64.const 235)) +(assert_return (invoke "rotr3_i64") (i64.const 63082332160)) +(assert_return (invoke "rotr4_i64") (i64.const 235)) +(assert_return (invoke "rotr5_i64") (i64.const 16149077032960)) diff --git a/runtime/unc-vm/tests/wast/wasmer/stack-overflow-sret.wast b/runtime/unc-vm/tests/wast/wasmer/stack-overflow-sret.wast new file mode 100644 index 000000000..dc75d77ea --- /dev/null +++ b/runtime/unc-vm/tests/wast/wasmer/stack-overflow-sret.wast @@ -0,0 +1,47 @@ +(module + (func $sret + (result i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 + i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64) + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + i64.const 0 i64.const 0 i64.const 0 i64.const 0 + ) + + (func (export "long-loop") + (local i32) + i32.const 1000000 + local.set 0 + (loop + call $sret + drop drop drop drop drop drop drop drop drop drop drop drop drop drop drop drop + drop drop drop drop drop drop drop drop drop drop drop drop drop drop drop drop + drop drop drop drop drop drop drop drop drop drop drop drop drop drop drop drop + drop drop drop drop drop drop drop drop drop drop drop drop drop drop drop drop + local.get 0 + i32.const -1 + i32.add + local.tee 0 + br_if 0 + ) + ) +) + +(assert_return (invoke "long-loop")) diff --git a/runtime/unc-vm/tests/wast/wasmer/stack-overflow.wast b/runtime/unc-vm/tests/wast/wasmer/stack-overflow.wast new file mode 100644 index 000000000..fc11c085b --- /dev/null +++ b/runtime/unc-vm/tests/wast/wasmer/stack-overflow.wast @@ -0,0 +1,10 @@ +(module + (type (;0;) (func)) + (func (;0;) (type 0) + i32.const 0 + call_indirect (type 0)) + (table (;0;) 1 anyfunc) + (export "stack-overflow" (func 0)) + (elem (;0;) (i32.const 0) 0)) + +(assert_exhaustion (invoke "stack-overflow") "call stack exhausted") diff --git a/runtime/unc-vm/types/Cargo.toml b/runtime/unc-vm/types/Cargo.toml new file mode 100644 index 000000000..d7dd15140 --- /dev/null +++ b/runtime/unc-vm/types/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "unc-vm-types" +version.workspace = true +description = "unc VM Common Types" +categories = ["wasm", "data-structures"] +keywords = ["wasm", "webassembly", "types"] +authors = ["Wasmer Engineering Team ", "Hello Inc "] +repository.workspace = true +license = "MIT OR Apache-2.0 WITH LLVM-exception" +readme = "README.md" +edition = "2021" +publish = true + +[lints] +workspace = true + +[dependencies] +thiserror.workspace = true +indexmap.workspace = true +num-traits.workspace = true +rkyv.workspace = true + +[dev-dependencies] +bolero.workspace = true + +[[test]] +name = "partial-sum-map" diff --git a/runtime/unc-vm/types/LICENSE b/runtime/unc-vm/types/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/runtime/unc-vm/types/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/runtime/unc-vm/types/README.md b/runtime/unc-vm/types/README.md new file mode 100644 index 000000000..d67a5be84 --- /dev/null +++ b/runtime/unc-vm/types/README.md @@ -0,0 +1,30 @@ +# `unc-vm-types` + +This crate is a fork of `wasmer-types`. A significant number of things changed, but the documentation is not up-to-date yet. + +This library provides all the types and traits necessary to use +WebAssembly easily anywhere. + +Among other things, it defines the following _types_: + +* `units` like `Pages` or `Bytes` +* `types` and `values` like `I32`, `I64`, `F32`, `F64`, `ExternRef`, + `FuncRef`, `V128`, value conversions, `ExternType`, `FunctionType` + etc. +* `native` contains a set of trait and implementations to deal with + WebAssembly types that have a direct representation on the host, +* `memory_view`, an API to read/write memories when bytes are + interpreted as particular types (`i8`, `i16`, `i32` etc.) +* `indexes` contains all the possible WebAssembly module indexes for + various types +* `initializers` for tables, data etc. +* `features` to enable or disable some WebAssembly features inside the + Wasmer runtime + + +### Acknowledgments + +This project borrowed some of the code for the entity structure from [cranelift-entity](https://crates.io/crates/cranelift-entity). +We decided to move it here to help on serialization/deserialization and also to ease the integration with other tools like `loupe`. + +Please check [Wasmer ATTRIBUTIONS](https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md) to further see licenses and other attributions of the project. diff --git a/runtime/unc-vm/types/src/archives.rs b/runtime/unc-vm/types/src/archives.rs new file mode 100644 index 000000000..59257a375 --- /dev/null +++ b/runtime/unc-vm/types/src/archives.rs @@ -0,0 +1,39 @@ +use indexmap::IndexMap; +use rkyv::Archive; +use std::hash::Hash; + +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +/// See [`IndexMap`] +pub struct ArchivableIndexMap { + entries: Vec<(K, V)>, +} + +impl ArchivedArchivableIndexMap { + pub fn iter(&self) -> core::slice::Iter<'_, (K::Archived, V::Archived)> { + self.entries.iter() + } +} + +impl From> + for ArchivableIndexMap +{ + fn from(it: IndexMap) -> Self { + let mut r = Self { entries: Vec::new() }; + for (k, v) in it.into_iter() { + r.entries.push((k, v)); + } + r + } +} + +impl Into> + for ArchivableIndexMap +{ + fn into(self) -> IndexMap { + let mut r = IndexMap::new(); + for (k, v) in self.entries.into_iter() { + r.insert(k, v); + } + r + } +} diff --git a/runtime/unc-vm/types/src/entity/boxed_slice.rs b/runtime/unc-vm/types/src/entity/boxed_slice.rs new file mode 100644 index 000000000..c801f134d --- /dev/null +++ b/runtime/unc-vm/types/src/entity/boxed_slice.rs @@ -0,0 +1,316 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Boxed slices for `PrimaryMap`. + +use crate::entity::iter::{Iter, IterMut}; +use crate::entity::keys::Keys; +use crate::entity::EntityRef; +use crate::lib::std::boxed::Box; +use crate::lib::std::marker::PhantomData; +use crate::lib::std::ops::{Index, IndexMut}; +use crate::lib::std::slice; + +/// A slice mapping `K -> V` allocating dense entity references. +/// +/// The `BoxedSlice` data structure uses the dense index space to implement a map with a boxed +/// slice. +#[derive(Debug, Clone)] +pub struct BoxedSlice +where + K: EntityRef, +{ + elems: Box<[V]>, + unused: PhantomData, +} + +impl BoxedSlice +where + K: EntityRef, +{ + /// Create a new slice from a raw pointer. A safer way to create slices is + /// to use `PrimaryMap::into_boxed_slice()`. + /// + /// # Safety + /// + /// This relies on `raw` pointing to a valid slice of `V`s. + pub unsafe fn from_raw(raw: *mut [V]) -> Self { + Self { elems: Box::from_raw(raw), unused: PhantomData } + } + + /// Check if `k` is a valid key in the map. + pub fn is_valid(&self, k: K) -> bool { + k.index() < self.elems.len() + } + + /// Get the element at `k` if it exists. + pub fn get(&self, k: K) -> Option<&V> { + self.elems.get(k.index()) + } + + /// Get the element at `k` if it exists, mutable version. + pub fn get_mut(&mut self, k: K) -> Option<&mut V> { + self.elems.get_mut(k.index()) + } + + /// Is this map completely empty? + pub fn is_empty(&self) -> bool { + self.elems.is_empty() + } + + /// Get the total number of entity references created. + pub fn len(&self) -> usize { + self.elems.len() + } + + /// Iterate over all the keys in this map. + pub fn keys(&self) -> Keys { + Keys::with_len(self.elems.len()) + } + + /// Iterate over all the values in this map. + pub fn values(&self) -> slice::Iter { + self.elems.iter() + } + + /// Iterate over all the values in this map, mutable edition. + pub fn values_mut(&mut self) -> slice::IterMut { + self.elems.iter_mut() + } + + /// Iterate over all the keys and values in this map. + pub fn iter(&self) -> Iter { + Iter::new(self.elems.iter()) + } + + /// Iterate over all the keys and values in this map, mutable edition. + pub fn iter_mut(&mut self) -> IterMut { + IterMut::new(self.elems.iter_mut()) + } + + /// Returns the last element that was inserted in the map. + pub fn last(&self) -> Option<&V> { + self.elems.last() + } +} + +/// Immutable indexing into a `BoxedSlice`. +/// The indexed value must be in the map. +impl Index for BoxedSlice +where + K: EntityRef, +{ + type Output = V; + + fn index(&self, k: K) -> &V { + &self.elems[k.index()] + } +} + +/// Mutable indexing into a `BoxedSlice`. +impl IndexMut for BoxedSlice +where + K: EntityRef, +{ + fn index_mut(&mut self, k: K) -> &mut V { + &mut self.elems[k.index()] + } +} + +impl<'a, K, V> IntoIterator for &'a BoxedSlice +where + K: EntityRef, +{ + type Item = (K, &'a V); + type IntoIter = Iter<'a, K, V>; + + fn into_iter(self) -> Self::IntoIter { + Iter::new(self.elems.iter()) + } +} + +impl<'a, K, V> IntoIterator for &'a mut BoxedSlice +where + K: EntityRef, +{ + type Item = (K, &'a mut V); + type IntoIter = IterMut<'a, K, V>; + + fn into_iter(self) -> Self::IntoIter { + IterMut::new(self.elems.iter_mut()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::entity::PrimaryMap; + use crate::lib::std::vec::Vec; + + // `EntityRef` impl for testing. + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + struct E(u32); + + impl EntityRef for E { + fn new(i: usize) -> Self { + Self(i as u32) + } + fn index(self) -> usize { + self.0 as usize + } + } + + #[test] + fn basic() { + let r0 = E(0); + let r1 = E(1); + let p = PrimaryMap::::new(); + let m = p.into_boxed_slice(); + + let v: Vec = m.keys().collect(); + assert_eq!(v, []); + + assert!(!m.is_valid(r0)); + assert!(!m.is_valid(r1)); + } + + #[test] + fn iter() { + let mut p: PrimaryMap = PrimaryMap::new(); + p.push(12); + p.push(33); + let mut m = p.into_boxed_slice(); + + let mut i = 0; + for (key, value) in &m { + assert_eq!(key.index(), i); + match i { + 0 => assert_eq!(*value, 12), + 1 => assert_eq!(*value, 33), + _ => panic!(), + } + i += 1; + } + i = 0; + for (key_mut, value_mut) in m.iter_mut() { + assert_eq!(key_mut.index(), i); + match i { + 0 => assert_eq!(*value_mut, 12), + 1 => assert_eq!(*value_mut, 33), + _ => panic!(), + } + i += 1; + } + } + + #[test] + fn iter_rev() { + let mut p: PrimaryMap = PrimaryMap::new(); + p.push(12); + p.push(33); + let mut m = p.into_boxed_slice(); + + let mut i = 2; + for (key, value) in m.iter().rev() { + i -= 1; + assert_eq!(key.index(), i); + match i { + 0 => assert_eq!(*value, 12), + 1 => assert_eq!(*value, 33), + _ => panic!(), + } + } + + i = 2; + for (key, value) in m.iter_mut().rev() { + i -= 1; + assert_eq!(key.index(), i); + match i { + 0 => assert_eq!(*value, 12), + 1 => assert_eq!(*value, 33), + _ => panic!(), + } + } + } + #[test] + fn keys() { + let mut p: PrimaryMap = PrimaryMap::new(); + p.push(12); + p.push(33); + let m = p.into_boxed_slice(); + + let mut i = 0; + for key in m.keys() { + assert_eq!(key.index(), i); + i += 1; + } + } + + #[test] + fn keys_rev() { + let mut p: PrimaryMap = PrimaryMap::new(); + p.push(12); + p.push(33); + let m = p.into_boxed_slice(); + + let mut i = 2; + for key in m.keys().rev() { + i -= 1; + assert_eq!(key.index(), i); + } + } + + #[test] + fn values() { + let mut p: PrimaryMap = PrimaryMap::new(); + p.push(12); + p.push(33); + let mut m = p.into_boxed_slice(); + + let mut i = 0; + for value in m.values() { + match i { + 0 => assert_eq!(*value, 12), + 1 => assert_eq!(*value, 33), + _ => panic!(), + } + i += 1; + } + i = 0; + for value_mut in m.values_mut() { + match i { + 0 => assert_eq!(*value_mut, 12), + 1 => assert_eq!(*value_mut, 33), + _ => panic!(), + } + i += 1; + } + } + + #[test] + fn values_rev() { + let mut p: PrimaryMap = PrimaryMap::new(); + p.push(12); + p.push(33); + let mut m = p.into_boxed_slice(); + + let mut i = 2; + for value in m.values().rev() { + i -= 1; + match i { + 0 => assert_eq!(*value, 12), + 1 => assert_eq!(*value, 33), + _ => panic!(), + } + } + i = 2; + for value_mut in m.values_mut().rev() { + i -= 1; + match i { + 0 => assert_eq!(*value_mut, 12), + 1 => assert_eq!(*value_mut, 33), + _ => panic!(), + } + } + } +} diff --git a/runtime/unc-vm/types/src/entity/iter.rs b/runtime/unc-vm/types/src/entity/iter.rs new file mode 100644 index 000000000..1e770325c --- /dev/null +++ b/runtime/unc-vm/types/src/entity/iter.rs @@ -0,0 +1,118 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! A double-ended iterator over entity references and entities. + +use crate::entity::EntityRef; +use crate::lib::std::iter::Enumerate; +use crate::lib::std::marker::PhantomData; +use crate::lib::std::slice; +use crate::lib::std::vec; + +/// Iterate over all keys in order. +pub struct Iter<'a, K: EntityRef, V> +where + V: 'a, +{ + enumerate: Enumerate>, + unused: PhantomData, +} + +impl<'a, K: EntityRef, V> Iter<'a, K, V> { + /// Create an `Iter` iterator that visits the `PrimaryMap` keys and values + /// of `iter`. + pub fn new(iter: slice::Iter<'a, V>) -> Self { + Self { enumerate: iter.enumerate(), unused: PhantomData } + } +} + +impl<'a, K: EntityRef, V> Iterator for Iter<'a, K, V> { + type Item = (K, &'a V); + + fn next(&mut self) -> Option { + self.enumerate.next().map(|(i, v)| (K::new(i), v)) + } + + fn size_hint(&self) -> (usize, Option) { + self.enumerate.size_hint() + } +} + +impl<'a, K: EntityRef, V> DoubleEndedIterator for Iter<'a, K, V> { + fn next_back(&mut self) -> Option { + self.enumerate.next_back().map(|(i, v)| (K::new(i), v)) + } +} + +impl<'a, K: EntityRef, V> ExactSizeIterator for Iter<'a, K, V> {} + +/// Iterate over all keys in order. +pub struct IterMut<'a, K: EntityRef, V> +where + V: 'a, +{ + enumerate: Enumerate>, + unused: PhantomData, +} + +impl<'a, K: EntityRef, V> IterMut<'a, K, V> { + /// Create an `IterMut` iterator that visits the `PrimaryMap` keys and values + /// of `iter`. + pub fn new(iter: slice::IterMut<'a, V>) -> Self { + Self { enumerate: iter.enumerate(), unused: PhantomData } + } +} + +impl<'a, K: EntityRef, V> Iterator for IterMut<'a, K, V> { + type Item = (K, &'a mut V); + + fn next(&mut self) -> Option { + self.enumerate.next().map(|(i, v)| (K::new(i), v)) + } + + fn size_hint(&self) -> (usize, Option) { + self.enumerate.size_hint() + } +} + +impl<'a, K: EntityRef, V> DoubleEndedIterator for IterMut<'a, K, V> { + fn next_back(&mut self) -> Option { + self.enumerate.next_back().map(|(i, v)| (K::new(i), v)) + } +} + +impl<'a, K: EntityRef, V> ExactSizeIterator for IterMut<'a, K, V> {} + +/// Iterate over all keys in order. +pub struct IntoIter { + enumerate: Enumerate>, + unused: PhantomData, +} + +impl IntoIter { + /// Create an `IntoIter` iterator that visits the `PrimaryMap` keys and values + /// of `iter`. + pub fn new(iter: vec::IntoIter) -> Self { + Self { enumerate: iter.enumerate(), unused: PhantomData } + } +} + +impl Iterator for IntoIter { + type Item = (K, V); + + fn next(&mut self) -> Option { + self.enumerate.next().map(|(i, v)| (K::new(i), v)) + } + + fn size_hint(&self) -> (usize, Option) { + self.enumerate.size_hint() + } +} + +impl DoubleEndedIterator for IntoIter { + fn next_back(&mut self) -> Option { + self.enumerate.next_back().map(|(i, v)| (K::new(i), v)) + } +} + +impl ExactSizeIterator for IntoIter {} diff --git a/runtime/unc-vm/types/src/entity/keys.rs b/runtime/unc-vm/types/src/entity/keys.rs new file mode 100644 index 000000000..9e15157fe --- /dev/null +++ b/runtime/unc-vm/types/src/entity/keys.rs @@ -0,0 +1,57 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! A double-ended iterator over entity references. +//! +//! When `core::iter::Step` is stabilized, `Keys` could be implemented as a wrapper around +//! `core::ops::Range`, but for now, we implement it manually. + +use crate::entity::EntityRef; +use crate::lib::std::marker::PhantomData; + +/// Iterate over all keys in order. +pub struct Keys { + pos: usize, + rev_pos: usize, + unused: PhantomData, +} + +impl Keys { + /// Create a `Keys` iterator that visits `len` entities starting from 0. + pub fn with_len(len: usize) -> Self { + Self { pos: 0, rev_pos: len, unused: PhantomData } + } +} + +impl Iterator for Keys { + type Item = K; + + fn next(&mut self) -> Option { + if self.pos < self.rev_pos { + let k = K::new(self.pos); + self.pos += 1; + Some(k) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + let size = self.rev_pos - self.pos; + (size, Some(size)) + } +} + +impl DoubleEndedIterator for Keys { + fn next_back(&mut self) -> Option { + if self.rev_pos > self.pos { + let k = K::new(self.rev_pos - 1); + self.rev_pos -= 1; + Some(k) + } else { + None + } + } +} + +impl ExactSizeIterator for Keys {} diff --git a/runtime/unc-vm/types/src/entity/mod.rs b/runtime/unc-vm/types/src/entity/mod.rs new file mode 100644 index 000000000..9412fa76c --- /dev/null +++ b/runtime/unc-vm/types/src/entity/mod.rs @@ -0,0 +1,93 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +/// A type wrapping a small integer index should implement `EntityRef` so it can be used as the key +/// of an `SecondaryMap` or `SparseMap`. +pub trait EntityRef: Copy + Eq { + /// Create a new entity reference from a small integer. + /// This should crash if the requested index is not representable. + fn new(_: usize) -> Self; + + /// Get the index that was used to create this entity reference. + fn index(self) -> usize; +} + +/// Macro which provides the common implementation of a 32-bit entity reference. +#[macro_export] +macro_rules! entity_impl { + // Basic traits. + ($entity:ident) => { + impl $crate::entity::EntityRef for $entity { + fn new(index: usize) -> Self { + debug_assert!(index < ($crate::lib::std::u32::MAX as usize)); + $entity(index as u32) + } + + fn index(self) -> usize { + self.0 as usize + } + } + + impl $crate::entity::packed_option::ReservedValue for $entity { + fn reserved_value() -> $entity { + $entity($crate::lib::std::u32::MAX) + } + + fn is_reserved_value(&self) -> bool { + self.0 == $crate::lib::std::u32::MAX + } + } + + impl $entity { + /// Create a new instance from a `u32`. + pub fn from_u32(x: u32) -> Self { + debug_assert!(x < $crate::lib::std::u32::MAX); + $entity(x) + } + + /// Return the underlying index value as a `u32`. + pub fn as_u32(self) -> u32 { + self.0 + } + } + }; + + // Include basic `Display` impl using the given display prefix. + // Display a `Block` reference as "block12". + ($entity:ident, $display_prefix:expr) => { + entity_impl!($entity); + + impl $crate::lib::std::fmt::Display for $entity { + fn fmt( + &self, + f: &mut $crate::lib::std::fmt::Formatter, + ) -> $crate::lib::std::fmt::Result { + write!(f, concat!($display_prefix, "{}"), self.0) + } + } + + impl $crate::lib::std::fmt::Debug for $entity { + fn fmt( + &self, + f: &mut $crate::lib::std::fmt::Formatter, + ) -> $crate::lib::std::fmt::Result { + (self as &dyn $crate::lib::std::fmt::Display).fmt(f) + } + } + }; +} + +pub mod packed_option; + +mod boxed_slice; +mod iter; +mod keys; +mod primary_map; +mod secondary_map; + +pub use crate::entity_impl; +pub use boxed_slice::BoxedSlice; +pub use iter::{Iter, IterMut}; +pub use keys::Keys; +pub use primary_map::PrimaryMap; +pub use secondary_map::SecondaryMap; diff --git a/runtime/unc-vm/types/src/entity/packed_option.rs b/runtime/unc-vm/types/src/entity/packed_option.rs new file mode 100644 index 000000000..44db4ea4d --- /dev/null +++ b/runtime/unc-vm/types/src/entity/packed_option.rs @@ -0,0 +1,168 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Compact representation of `Option` for types with a reserved value. +//! +//! Small types are often used in tables and linked lists where an +//! `Option` is needed. Unfortunately, that would double the size of the tables +//! because `Option` is twice as big as `T`. +//! +//! This module provides a `PackedOption` for types that have a reserved value that can be used +//! to represent `None`. + +use crate::lib::std::fmt; +use crate::lib::std::mem; + +/// Types that have a reserved value which can't be created any other way. +pub trait ReservedValue { + /// Create an instance of the reserved value. + fn reserved_value() -> Self; + /// Checks whether value is the reserved one. + fn is_reserved_value(&self) -> bool; +} + +/// Packed representation of `Option`. +#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)] +pub struct PackedOption(T); + +impl PackedOption { + /// Returns `true` if the packed option is a `None` value. + pub fn is_none(&self) -> bool { + self.0.is_reserved_value() + } + + /// Returns `true` if the packed option is a `Some` value. + pub fn is_some(&self) -> bool { + !self.0.is_reserved_value() + } + + /// Expand the packed option into a normal `Option`. + pub fn expand(self) -> Option { + if self.is_none() { + None + } else { + Some(self.0) + } + } + + /// Maps a `PackedOption` to `Option` by applying a function to a contained value. + pub fn map(self, f: F) -> Option + where + F: FnOnce(T) -> U, + { + self.expand().map(f) + } + + /// Unwrap a packed `Some` value or panic. + pub fn unwrap(self) -> T { + self.expand().unwrap() + } + + /// Unwrap a packed `Some` value or panic. + pub fn expect(self, msg: &str) -> T { + self.expand().expect(msg) + } + + /// Takes the value out of the packed option, leaving a `None` in its place. + pub fn take(&mut self) -> Option { + mem::replace(self, None.into()).expand() + } +} + +impl Default for PackedOption { + /// Create a default packed option representing `None`. + fn default() -> Self { + Self(T::reserved_value()) + } +} + +impl From for PackedOption { + /// Convert `t` into a packed `Some(x)`. + fn from(t: T) -> Self { + debug_assert!(!t.is_reserved_value(), "Can't make a PackedOption from the reserved value."); + Self(t) + } +} + +impl From> for PackedOption { + /// Convert an option into its packed equivalent. + fn from(opt: Option) -> Self { + match opt { + None => Self::default(), + Some(t) => t.into(), + } + } +} + +impl Into> for PackedOption { + fn into(self) -> Option { + self.expand() + } +} + +impl fmt::Debug for PackedOption +where + T: ReservedValue + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.is_none() { + write!(f, "None") + } else { + write!(f, "Some({:?})", self.0) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // Dummy entity class, with no Copy or Clone. + #[derive(Debug, PartialEq, Eq)] + struct NoC(u32); + + impl ReservedValue for NoC { + fn reserved_value() -> Self { + Self(13) + } + + fn is_reserved_value(&self) -> bool { + self.0 == 13 + } + } + + #[test] + fn moves() { + let x = NoC(3); + let somex: PackedOption = x.into(); + assert!(!somex.is_none()); + assert_eq!(somex.expand(), Some(NoC(3))); + + let none: PackedOption = None.into(); + assert!(none.is_none()); + assert_eq!(none.expand(), None); + } + + // Dummy entity class, with Copy. + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + struct Ent(u32); + + impl ReservedValue for Ent { + fn reserved_value() -> Self { + Self(13) + } + + fn is_reserved_value(&self) -> bool { + self.0 == 13 + } + } + + #[test] + fn copies() { + let x = Ent(2); + let some: PackedOption = x.into(); + let some2: Option = x.into(); + assert_eq!(some.expand(), some2); + assert_eq!(some, x.into()); + } +} diff --git a/runtime/unc-vm/types/src/entity/primary_map.rs b/runtime/unc-vm/types/src/entity/primary_map.rs new file mode 100644 index 000000000..b617b3a2a --- /dev/null +++ b/runtime/unc-vm/types/src/entity/primary_map.rs @@ -0,0 +1,454 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Densely numbered entity references as mapping keys. +use rkyv::Archive; + +use crate::entity::boxed_slice::BoxedSlice; +use crate::entity::iter::{IntoIter, Iter, IterMut}; +use crate::entity::keys::Keys; +use crate::entity::EntityRef; +use crate::lib::std::boxed::Box; +use crate::lib::std::iter::FromIterator; +use crate::lib::std::marker::PhantomData; +use crate::lib::std::ops::{Index, IndexMut}; +use crate::lib::std::slice; +use crate::lib::std::vec::Vec; + +/// A primary mapping `K -> V` allocating dense entity references. +/// +/// The `PrimaryMap` data structure uses the dense index space to implement a map with a vector. +/// +/// A primary map contains the main definition of an entity, and it can be used to allocate new +/// entity references with the `push` method. +/// +/// There should only be a single `PrimaryMap` instance for a given `EntityRef` type, otherwise +/// conflicting references will be created. Using unknown keys for indexing will cause a panic. +/// +/// Note that `PrimaryMap` doesn't implement `Deref` or `DerefMut`, which would allow +/// `&PrimaryMap` to convert to `&[V]`. One of the main advantages of `PrimaryMap` is +/// that it only allows indexing with the distinct `EntityRef` key type, so converting to a +/// plain slice would make it easier to use incorrectly. To make a slice of a `PrimaryMap`, use +/// `into_boxed_slice`. +#[derive(Debug, Clone, Hash, PartialEq, Eq, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +pub struct PrimaryMap +where + K: EntityRef, +{ + pub(crate) elems: Vec, + pub(crate) unused: PhantomData, +} + +impl PrimaryMap +where + K: EntityRef, +{ + /// Create a new empty map. + pub fn new() -> Self { + Self { elems: Vec::new(), unused: PhantomData } + } + + /// Create a new empty map with the given capacity. + pub fn with_capacity(capacity: usize) -> Self { + Self { elems: Vec::with_capacity(capacity), unused: PhantomData } + } + + /// Check if `k` is a valid key in the map. + pub fn is_valid(&self, k: K) -> bool { + k.index() < self.elems.len() + } + + /// Get the element at `k` if it exists. + pub fn get(&self, k: K) -> Option<&V> { + self.elems.get(k.index()) + } + + /// Get the element at `k` if it exists, mutable version. + pub fn get_mut(&mut self, k: K) -> Option<&mut V> { + self.elems.get_mut(k.index()) + } + + /// Is this map completely empty? + pub fn is_empty(&self) -> bool { + self.elems.is_empty() + } + + /// Get the total number of entity references created. + pub fn len(&self) -> usize { + self.elems.len() + } + + /// Iterate over all the keys in this map. + pub fn keys(&self) -> Keys { + Keys::with_len(self.elems.len()) + } + + /// Iterate over all the values in this map. + pub fn values(&self) -> slice::Iter { + self.elems.iter() + } + + /// Iterate over all the values in this map, mutable edition. + pub fn values_mut(&mut self) -> slice::IterMut { + self.elems.iter_mut() + } + + /// Iterate over all the keys and values in this map. + pub fn iter(&self) -> Iter { + Iter::new(self.elems.iter()) + } + + /// Iterate over all the keys and values in this map, mutable edition. + pub fn iter_mut(&mut self) -> IterMut { + IterMut::new(self.elems.iter_mut()) + } + + /// Remove all entries from this map. + pub fn clear(&mut self) { + self.elems.clear() + } + + /// Get the key that will be assigned to the next pushed value. + pub fn next_key(&self) -> K { + K::new(self.elems.len()) + } + + /// Append `v` to the mapping, assigning a new key which is returned. + pub fn push(&mut self, v: V) -> K { + let k = self.next_key(); + self.elems.push(v); + k + } + + /// Returns the last element that was inserted in the map. + pub fn last(&self) -> Option<&V> { + self.elems.last() + } + + /// Reserves capacity for at least `additional` more elements to be inserted. + pub fn reserve(&mut self, additional: usize) { + self.elems.reserve(additional) + } + + /// Reserves the minimum capacity for exactly `additional` more elements to be inserted. + pub fn reserve_exact(&mut self, additional: usize) { + self.elems.reserve_exact(additional) + } + + /// Shrinks the capacity of the `PrimaryMap` as much as possible. + pub fn shrink_to_fit(&mut self) { + self.elems.shrink_to_fit() + } + + /// Consumes this `PrimaryMap` and produces a `BoxedSlice`. + pub fn into_boxed_slice(self) -> BoxedSlice { + unsafe { BoxedSlice::::from_raw(Box::<[V]>::into_raw(self.elems.into_boxed_slice())) } + } +} + +impl Default for PrimaryMap +where + K: EntityRef, +{ + fn default() -> Self { + Self::new() + } +} + +/// Immutable indexing into an `PrimaryMap`. +/// The indexed value must be in the map. +impl Index for PrimaryMap +where + K: EntityRef, +{ + type Output = V; + + fn index(&self, k: K) -> &V { + &self.elems[k.index()] + } +} + +/// Mutable indexing into an `PrimaryMap`. +impl IndexMut for PrimaryMap +where + K: EntityRef, +{ + fn index_mut(&mut self, k: K) -> &mut V { + &mut self.elems[k.index()] + } +} + +impl IntoIterator for PrimaryMap +where + K: EntityRef, +{ + type Item = (K, V); + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIter::new(self.elems.into_iter()) + } +} + +impl<'a, K, V> IntoIterator for &'a PrimaryMap +where + K: EntityRef, +{ + type Item = (K, &'a V); + type IntoIter = Iter<'a, K, V>; + + fn into_iter(self) -> Self::IntoIter { + Iter::new(self.elems.iter()) + } +} + +impl<'a, K, V> IntoIterator for &'a mut PrimaryMap +where + K: EntityRef, +{ + type Item = (K, &'a mut V); + type IntoIter = IterMut<'a, K, V>; + + fn into_iter(self) -> Self::IntoIter { + IterMut::new(self.elems.iter_mut()) + } +} + +impl FromIterator for PrimaryMap +where + K: EntityRef, +{ + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + Self { elems: Vec::from_iter(iter), unused: PhantomData } + } +} + +impl ArchivedPrimaryMap +where + K: Archive + EntityRef, + V: Archive, +{ + /// Get the total number of entity references created. + pub fn len(&self) -> usize { + self.elems.len() + } + + /// Iterate over all the values in this map. + pub fn values(&self) -> slice::Iter> { + self.elems.iter() + } + + /// Iterate over all the keys and values in this map. + pub fn iter(&self) -> Iter> { + Iter::new(self.elems.iter()) + } +} + +/// Immutable indexing into an `PrimaryMap`. +/// The indexed value must be in the map. +impl Index<&K::Archived> for ArchivedPrimaryMap +where + K: EntityRef + Archive, + K::Archived: EntityRef, + V: Archive, +{ + type Output = ::Archived; + + fn index(&self, k: &K::Archived) -> &Self::Output { + &self.elems[k.index()] + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // `EntityRef` impl for testing. + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + struct E(u32); + + impl EntityRef for E { + fn new(i: usize) -> Self { + Self(i as u32) + } + fn index(self) -> usize { + self.0 as usize + } + } + + #[test] + fn basic() { + let r0 = E(0); + let r1 = E(1); + let m = PrimaryMap::::new(); + + let v: Vec = m.keys().collect(); + assert_eq!(v, []); + + assert!(!m.is_valid(r0)); + assert!(!m.is_valid(r1)); + } + + #[test] + fn push() { + let mut m = PrimaryMap::new(); + let k0: E = m.push(12); + let k1 = m.push(33); + + assert_eq!(m[k0], 12); + assert_eq!(m[k1], 33); + + let v: Vec = m.keys().collect(); + assert_eq!(v, [k0, k1]); + } + + #[test] + fn iter() { + let mut m: PrimaryMap = PrimaryMap::new(); + m.push(12); + m.push(33); + + let mut i = 0; + for (key, value) in &m { + assert_eq!(key.index(), i); + match i { + 0 => assert_eq!(*value, 12), + 1 => assert_eq!(*value, 33), + _ => panic!(), + } + i += 1; + } + i = 0; + for (key_mut, value_mut) in m.iter_mut() { + assert_eq!(key_mut.index(), i); + match i { + 0 => assert_eq!(*value_mut, 12), + 1 => assert_eq!(*value_mut, 33), + _ => panic!(), + } + i += 1; + } + } + + #[test] + fn iter_rev() { + let mut m: PrimaryMap = PrimaryMap::new(); + m.push(12); + m.push(33); + + let mut i = 2; + for (key, value) in m.iter().rev() { + i -= 1; + assert_eq!(key.index(), i); + match i { + 0 => assert_eq!(*value, 12), + 1 => assert_eq!(*value, 33), + _ => panic!(), + } + } + + i = 2; + for (key, value) in m.iter_mut().rev() { + i -= 1; + assert_eq!(key.index(), i); + match i { + 0 => assert_eq!(*value, 12), + 1 => assert_eq!(*value, 33), + _ => panic!(), + } + } + } + #[test] + fn keys() { + let mut m: PrimaryMap = PrimaryMap::new(); + m.push(12); + m.push(33); + + let mut i = 0; + for key in m.keys() { + assert_eq!(key.index(), i); + i += 1; + } + } + + #[test] + fn keys_rev() { + let mut m: PrimaryMap = PrimaryMap::new(); + m.push(12); + m.push(33); + + let mut i = 2; + for key in m.keys().rev() { + i -= 1; + assert_eq!(key.index(), i); + } + } + + #[test] + fn values() { + let mut m: PrimaryMap = PrimaryMap::new(); + m.push(12); + m.push(33); + + let mut i = 0; + for value in m.values() { + match i { + 0 => assert_eq!(*value, 12), + 1 => assert_eq!(*value, 33), + _ => panic!(), + } + i += 1; + } + i = 0; + for value_mut in m.values_mut() { + match i { + 0 => assert_eq!(*value_mut, 12), + 1 => assert_eq!(*value_mut, 33), + _ => panic!(), + } + i += 1; + } + } + + #[test] + fn values_rev() { + let mut m: PrimaryMap = PrimaryMap::new(); + m.push(12); + m.push(33); + + let mut i = 2; + for value in m.values().rev() { + i -= 1; + match i { + 0 => assert_eq!(*value, 12), + 1 => assert_eq!(*value, 33), + _ => panic!(), + } + } + i = 2; + for value_mut in m.values_mut().rev() { + i -= 1; + match i { + 0 => assert_eq!(*value_mut, 12), + 1 => assert_eq!(*value_mut, 33), + _ => panic!(), + } + } + } + + #[test] + fn from_iter() { + let mut m: PrimaryMap = PrimaryMap::new(); + m.push(12); + m.push(33); + + let n = m.values().collect::>(); + assert!(m.len() == n.len()); + for (me, ne) in m.values().zip(n.values()) { + assert!(*me == **ne); + } + } +} diff --git a/runtime/unc-vm/types/src/entity/secondary_map.rs b/runtime/unc-vm/types/src/entity/secondary_map.rs new file mode 100644 index 000000000..288a85347 --- /dev/null +++ b/runtime/unc-vm/types/src/entity/secondary_map.rs @@ -0,0 +1,246 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Densely numbered entity references as mapping keys. + +use crate::entity::iter::{Iter, IterMut}; +use crate::entity::keys::Keys; +use crate::entity::EntityRef; +use crate::lib::std::cmp::min; +use crate::lib::std::marker::PhantomData; +use crate::lib::std::ops::{Index, IndexMut}; +use crate::lib::std::slice; +use crate::lib::std::vec::Vec; +use rkyv::Archive; + +/// A mapping `K -> V` for densely indexed entity references. +/// +/// The `SecondaryMap` data structure uses the dense index space to implement a map with a vector. +/// Unlike `PrimaryMap`, an `SecondaryMap` can't be used to allocate entity references. It is used +/// to associate secondary information with entities. +/// +/// The map does not track if an entry for a key has been inserted or not. Instead it behaves as if +/// all keys have a default entry from the beginning. +#[derive(Debug, Clone, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +pub struct SecondaryMap +where + K: EntityRef, + V: Clone, +{ + pub(crate) elems: Vec, + pub(crate) default: V, + pub(crate) unused: PhantomData, +} + +/// Shared `SecondaryMap` implementation for all value types. +impl SecondaryMap +where + K: EntityRef, + V: Clone, +{ + /// Create a new empty map. + pub fn new() -> Self + where + V: Default, + { + Self { elems: Vec::new(), default: Default::default(), unused: PhantomData } + } + + /// Create a new, empty map with the specified capacity. + /// + /// The map will be able to hold exactly `capacity` elements without reallocating. + pub fn with_capacity(capacity: usize) -> Self + where + V: Default, + { + Self { + elems: Vec::with_capacity(capacity), + default: Default::default(), + unused: PhantomData, + } + } + + /// Create a new empty map with a specified default value. + /// + /// This constructor does not require V to implement Default. + pub fn with_default(default: V) -> Self { + Self { elems: Vec::new(), default, unused: PhantomData } + } + + /// Returns the number of elements the map can hold without reallocating. + pub fn capacity(&self) -> usize { + self.elems.capacity() + } + + /// Get the element at `k` if it exists. + #[inline(always)] + pub fn get(&self, k: K) -> Option<&V> { + self.elems.get(k.index()) + } + + /// Is this map completely empty? + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.elems.is_empty() + } + + /// Remove all entries from this map. + #[inline(always)] + pub fn clear(&mut self) { + self.elems.clear() + } + + /// Iterate over all the keys and values in this map. + pub fn iter(&self) -> Iter { + Iter::new(self.elems.iter()) + } + + /// Iterate over all the keys and values in this map, mutable edition. + pub fn iter_mut(&mut self) -> IterMut { + IterMut::new(self.elems.iter_mut()) + } + + /// Iterate over all the keys in this map. + pub fn keys(&self) -> Keys { + Keys::with_len(self.elems.len()) + } + + /// Iterate over all the values in this map. + pub fn values(&self) -> slice::Iter { + self.elems.iter() + } + + /// Iterate over all the values in this map, mutable edition. + pub fn values_mut(&mut self) -> slice::IterMut { + self.elems.iter_mut() + } + + /// Resize the map to have `n` entries by adding default entries as needed. + pub fn resize(&mut self, n: usize) { + self.elems.resize(n, self.default.clone()); + } +} + +impl Default for SecondaryMap +where + K: EntityRef, + V: Clone + Default, +{ + fn default() -> Self { + Self::new() + } +} + +/// Immutable indexing into an `SecondaryMap`. +/// +/// All keys are permitted. Untouched entries have the default value. +impl Index for SecondaryMap +where + K: EntityRef, + V: Clone, +{ + type Output = V; + + #[inline(always)] + fn index(&self, k: K) -> &V { + self.elems.get(k.index()).unwrap_or(&self.default) + } +} + +/// Immutable indexing into an `SecondaryMap`. +/// +/// All keys are permitted. Untouched entries have the default value. +impl Index<&K::Archived> for ArchivedSecondaryMap +where + K: EntityRef + Archive, + K::Archived: EntityRef, + V: Archive + Clone, +{ + type Output = ::Archived; + + fn index(&self, k: &K::Archived) -> &Self::Output { + &self.elems.get(k.index()).unwrap_or(&self.default) + } +} + +/// Mutable indexing into an `SecondaryMap`. +/// +/// The map grows as needed to accommodate new keys. +impl IndexMut for SecondaryMap +where + K: EntityRef, + V: Clone, +{ + #[inline(always)] + fn index_mut(&mut self, k: K) -> &mut V { + let i = k.index(); + if i >= self.elems.len() { + self.elems.resize(i + 1, self.default.clone()); + } + &mut self.elems[i] + } +} + +impl PartialEq for SecondaryMap +where + K: EntityRef, + V: Clone + PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + let min_size = min(self.elems.len(), other.elems.len()); + self.default == other.default + && self.elems[..min_size] == other.elems[..min_size] + && self.elems[min_size..].iter().all(|e| *e == self.default) + && other.elems[min_size..].iter().all(|e| *e == other.default) + } +} + +impl Eq for SecondaryMap +where + K: EntityRef, + V: Clone + PartialEq + Eq, +{ +} + +#[cfg(test)] +mod tests { + use super::*; + + // `EntityRef` impl for testing. + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + struct E(u32); + + impl EntityRef for E { + fn new(i: usize) -> Self { + Self(i as u32) + } + fn index(self) -> usize { + self.0 as usize + } + } + + #[test] + fn basic() { + let r0 = E(0); + let r1 = E(1); + let r2 = E(2); + let mut m = SecondaryMap::new(); + + let v: Vec = m.keys().collect(); + assert_eq!(v, []); + + m[r2] = 3; + m[r1] = 5; + + assert_eq!(m[r1], 5); + assert_eq!(m[r2], 3); + + let v: Vec = m.keys().collect(); + assert_eq!(v, [r0, r1, r2]); + + let shared = &m; + assert_eq!(shared[r0], 0); + assert_eq!(shared[r1], 5); + assert_eq!(shared[r2], 3); + } +} diff --git a/runtime/unc-vm/types/src/extern_ref.rs b/runtime/unc-vm/types/src/extern_ref.rs new file mode 100644 index 000000000..9ba2cccc8 --- /dev/null +++ b/runtime/unc-vm/types/src/extern_ref.rs @@ -0,0 +1,266 @@ +use std::any::Any; +use std::ptr; +use std::sync::atomic; + +/// This type does not do reference counting automatically, reference counting can be done with +/// [`Self::ref_clone`] and [`Self::ref_drop`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct VMExternRef(*const VMExternRefInner); + +impl VMExternRef { + /// The maximum number of references allowed to this data. + const MAX_REFCOUNT: usize = std::usize::MAX - 1; + + /// Checks if the given ExternRef is null. + pub fn is_null(&self) -> bool { + self.0.is_null() + } + + /// New null extern ref + pub const fn null() -> Self { + Self(ptr::null()) + } + + /// Get a bit-level representation of an externref. + /// For internal use for packing / unpacking it for calling functions. + pub(crate) fn to_binary(self) -> i128 { + self.0 as i128 + } + + /// Create an externref from bit-level representation. + /// For internal use for packing / unpacking it for calling functions. + /// + /// # Safety + /// The pointer is assumed valid or null. Passing arbitrary data to this function will + /// result in undefined behavior. It is the caller's responsibility to verify that only + /// valid externref bit patterns are passed to this function. + pub(crate) unsafe fn from_binary(bits: i128) -> Self { + Self(bits as usize as *const _) + } + + /// Make a new extern reference + pub fn new(value: T) -> Self + where + T: Any + Send + Sync + 'static + Sized, + { + Self(Box::into_raw(Box::new(VMExternRefInner::new::(value)))) + } + + /// Try to downcast to the given value + pub fn downcast(&self) -> Option<&T> + where + T: Any + Send + Sync + 'static + Sized, + { + if self.is_null() { + return None; + } + unsafe { + let inner = &*self.0; + + inner.data.downcast_ref::() + } + } + + /// Panic if the ref count gets too high. + #[track_caller] + fn sanity_check_ref_count(old_size: usize, growth_amount: usize) { + // If we exceed 18_446_744_073_709_551_614 references on a 64bit system (or + // 2_147_483_646 references on a 32bit system) then we either live in a future with + // magic technology or we have a bug in our ref counting logic (i.e. a leak). + // Either way, the best course of action is to terminate the program and update + // some code on our side. + // + // Note to future readers: exceeding `usize` ref count is trivially provable as a + // bug on systems that can address `usize` sized memory blocks or smaller because + // the reference itself is at least `usize` in size and all virtual memory would be + // taken by references to the data leaving no room for the data itself. + if old_size.checked_add(growth_amount).map_or(true, |v| v > Self::MAX_REFCOUNT) { + panic!("Too many references to `ExternRef`"); + } + } + + /// A low-level function to increment the strong-count a given number of times. + /// + /// This is used as an optimization when implementing some low-level VM primitives. + /// If you're using this type directly for whatever reason, you probably want + /// [`Self::ref_clone`] instead. + pub fn ref_inc_by(&self, val: usize) { + if self.0.is_null() { + return; + } + + let old_size = unsafe { + let ref_inner = &*self.0; + ref_inner.increment_ref_count(val) + }; + + Self::sanity_check_ref_count(old_size, val); + } + + /// A deep copy of the reference, increments the strong count. + pub fn ref_clone(&self) -> Self { + if self.0.is_null() { + return Self(self.0); + } + + let old_size = unsafe { + let ref_inner = &*self.0; + ref_inner.increment_ref_count(1) + }; + + // See comments in [`Self::sanity_check_ref_count`] for more information. + if old_size > Self::MAX_REFCOUNT { + panic!("Too many references to `ExternRef`"); + } + + Self(self.0) + } + + /// Does an inner drop, decrementing the strong count + pub fn ref_drop(&mut self) { + if !self.0.is_null() { + unsafe { + let should_drop = { + let ref_inner: &VMExternRefInner = &*self.0; + ref_inner.decrement_and_drop() + }; + if should_drop { + let _ = Box::from_raw(self.0 as *mut VMExternRefInner); + } + } + } + } + + #[allow(dead_code)] + /// Get the number of strong references to this data. + fn strong_count(&self) -> usize { + if self.0.is_null() { + 0 + } else { + unsafe { (&*self.0).strong.load(atomic::Ordering::SeqCst) } + } + } +} + +#[derive(Debug)] +#[repr(C)] +pub(crate) struct VMExternRefInner { + strong: atomic::AtomicUsize, + /// Do something obviously correct to get started. This can "easily" be improved + /// to be an inline allocation later as the logic is fully encapsulated. + data: Box, +} + +impl VMExternRefInner { + fn new(value: T) -> Self + where + T: Any + Send + Sync + Sized + 'static, + { + Self { strong: atomic::AtomicUsize::new(1), data: Box::new(value) } + } + + /// Increments the reference count. + /// Returns the old value. + fn increment_ref_count(&self, val: usize) -> usize { + // Using a relaxed ordering is alright here, as knowledge of + // the original reference prevents other threads from + // erroneously deleting the object. + // + // As explained in the [Boost documentation][1]: + // + // > Increasing the reference counter can always be done with + // > `memory_order_relaxed`: New references to an object can + // > only be formed from an existing reference, and passing an + // > existing reference from one thread to another must already + // > provide any required synchronization. + // + // [1]: https://www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html + self.strong.fetch_add(val, atomic::Ordering::Relaxed) + } + + /// Decrement the count and drop the data if the count hits 0 + /// returns `true` if the containing allocation should be dropped + fn decrement_and_drop(&self) -> bool { + // Because `fetch_sub` is already atomic, we do not need to + // synchronize with other thread. + if self.strong.fetch_sub(1, atomic::Ordering::Release) != 1 { + return false; + } + + // This fence is needed to prevent reordering of use of the data and + // deletion of the data. Because it is marked `Release`, the decreasing + // of the reference count synchronizes with this `Acquire` fence. This + // means that use of the data happens before decreasing the reference + // count, which happens before this fence, which happens before the + // deletion of the data. + // + // As explained in the [Boost documentation][1]: + // + // > It is important to enforce any possible access to the object in one + // > thread (through an existing reference) to *happen before* deleting + // > the object in a different thread. This is achieved by a "release" + // > operation after dropping a reference (any access to the object + // > through this reference must obviously happened before), and an + // > "acquire" operation before deleting the object. + // + // [1]: https://www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html + atomic::fence(atomic::Ordering::Acquire); + + return true; + } +} + +#[derive(Debug, PartialEq, Eq)] +#[repr(transparent)] +/// An opaque reference to some data. This reference can be passed through Wasm. +pub struct ExternRef { + inner: VMExternRef, +} + +impl Clone for ExternRef { + fn clone(&self) -> Self { + Self { inner: self.inner.ref_clone() } + } +} + +impl Drop for ExternRef { + fn drop(&mut self) { + self.inner.ref_drop() + } +} + +impl ExternRef { + /// Checks if the given ExternRef is null. + pub fn is_null(&self) -> bool { + self.inner.is_null() + } + + /// New null extern ref + pub fn null() -> Self { + Self { inner: VMExternRef::null() } + } + + /// Make a new extern reference + pub fn new(value: T) -> Self + where + T: Any + Send + Sync + 'static + Sized, + { + Self { inner: VMExternRef::new(value) } + } +} + +impl From for ExternRef { + fn from(other: VMExternRef) -> Self { + Self { inner: other } + } +} + +impl From for VMExternRef { + fn from(other: ExternRef) -> Self { + let out = other.inner; + // We want to make this transformation without decrementing the count. + std::mem::forget(other); + out + } +} diff --git a/runtime/unc-vm/types/src/features.rs b/runtime/unc-vm/types/src/features.rs new file mode 100644 index 000000000..0261a07ba --- /dev/null +++ b/runtime/unc-vm/types/src/features.rs @@ -0,0 +1,308 @@ +/// Controls which experimental features will be enabled. +/// Features usually have a corresponding [WebAssembly proposal]. +/// +/// [WebAssembly proposal]: https://github.com/WebAssembly/proposals +#[derive(Clone, Debug, Eq, PartialEq, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +pub struct Features { + /// Threads proposal should be enabled + pub threads: bool, + /// Reference Types proposal should be enabled + pub reference_types: bool, + /// SIMD proposal should be enabled + pub simd: bool, + /// Bulk Memory proposal should be enabled + pub bulk_memory: bool, + /// Multi Value proposal should be enabled + pub multi_value: bool, + /// Tail call proposal should be enabled + pub tail_call: bool, + /// Multi Memory proposal should be enabled + pub multi_memory: bool, + /// 64-bit Memory proposal should be enabled + pub memory64: bool, + /// Wasm exceptions proposal should be enabled + pub exceptions: bool, + /// Mutable global proposal should be enabled + pub mutable_global: bool, + /// Non-trapping float-to-int proposal should be enabled + pub saturating_float_to_int: bool, + /// Sign-extension operators should be enabled + pub sign_extension: bool, +} + +impl Features { + /// Create a new feature + pub fn new() -> Self { + Self { + threads: false, + // Reference types should be on by default + reference_types: true, + // SIMD should be on by default + simd: true, + // Bulk Memory should be on by default + bulk_memory: true, + // Multivalue should be on by default + multi_value: true, + tail_call: false, + multi_memory: false, + memory64: false, + exceptions: false, + // these were once defaulting to true in wasmparser, we now set them to true here + mutable_global: true, + saturating_float_to_int: true, + sign_extension: true, + } + } + + /// Configures whether the WebAssembly threads proposal will be enabled. + /// + /// The [WebAssembly threads proposal][threads] is not currently fully + /// standardized and is undergoing development. Support for this feature can + /// be enabled through this method for appropriate WebAssembly modules. + /// + /// This feature gates items such as shared memories and atomic + /// instructions. + /// + /// This is `false` by default. + /// + /// [threads]: https://github.com/webassembly/threads + pub fn threads(&mut self, enable: bool) -> &mut Self { + self.threads = enable; + self + } + + /// Configures whether the WebAssembly reference types proposal will be + /// enabled. + /// + /// The [WebAssembly reference types proposal][proposal] is now + /// fully standardized and enabled by default. + /// + /// This feature gates items such as the `externref` type and multiple tables + /// being in a module. Note that enabling the reference types feature will + /// also enable the bulk memory feature. + /// + /// This is `true` by default. + /// + /// [proposal]: https://github.com/webassembly/reference-types + pub fn reference_types(&mut self, enable: bool) -> &mut Self { + self.reference_types = enable; + // The reference types proposal depends on the bulk memory proposal + if enable { + self.bulk_memory(true); + } + self + } + + /// Configures whether the WebAssembly SIMD proposal will be + /// enabled. + /// + /// The [WebAssembly SIMD proposal][proposal] is not currently + /// fully standardized and is undergoing development. Support for this + /// feature can be enabled through this method for appropriate WebAssembly + /// modules. + /// + /// This feature gates items such as the `v128` type and all of its + /// operators being in a module. + /// + /// This is `false` by default. + /// + /// [proposal]: https://github.com/webassembly/simd + pub fn simd(&mut self, enable: bool) -> &mut Self { + self.simd = enable; + self + } + + /// Configures whether the WebAssembly bulk memory operations proposal will + /// be enabled. + /// + /// The [WebAssembly bulk memory operations proposal][proposal] is now + /// fully standardized and enabled by default. + /// + /// This feature gates items such as the `memory.copy` instruction, passive + /// data/table segments, etc, being in a module. + /// + /// This is `true` by default. + /// + /// [proposal]: https://github.com/webassembly/bulk-memory-operations + pub fn bulk_memory(&mut self, enable: bool) -> &mut Self { + self.bulk_memory = enable; + // In case is false, we disable both threads and reference types + // since they both depend on bulk memory + if !enable { + self.reference_types(false); + } + self + } + + /// Configures whether the WebAssembly multi-value proposal will + /// be enabled. + /// + /// The [WebAssembly multi-value proposal][proposal] is now fully + /// standardized and enabled by default, except with the singlepass + /// compiler which does not support it. + /// + /// This feature gates functions and blocks returning multiple values in a + /// module, for example. + /// + /// This is `true` by default. + /// + /// [proposal]: https://github.com/webassembly/multi-value + pub fn multi_value(&mut self, enable: bool) -> &mut Self { + self.multi_value = enable; + self + } + + /// Configures whether the WebAssembly tail-call proposal will + /// be enabled. + /// + /// The [WebAssembly tail-call proposal][proposal] is not + /// currently fully standardized and is undergoing development. + /// Support for this feature can be enabled through this method for + /// appropriate WebAssembly modules. + /// + /// This feature gates tail-call functions in WebAssembly. + /// + /// This is `false` by default. + /// + /// [proposal]: https://github.com/webassembly/tail-call + pub fn tail_call(&mut self, enable: bool) -> &mut Self { + self.tail_call = enable; + self + } + + /// Configures whether the WebAssembly multi-memory proposal will + /// be enabled. + /// + /// The [WebAssembly multi-memory proposal][proposal] is not + /// currently fully standardized and is undergoing development. + /// Support for this feature can be enabled through this method for + /// appropriate WebAssembly modules. + /// + /// This feature adds the ability to use multiple memories within a + /// single Wasm module. + /// + /// This is `false` by default. + /// + /// [proposal]: https://github.com/WebAssembly/multi-memory + pub fn multi_memory(&mut self, enable: bool) -> &mut Self { + self.multi_memory = enable; + self + } + + /// Configures whether the WebAssembly 64-bit memory proposal will + /// be enabled. + /// + /// The [WebAssembly 64-bit memory proposal][proposal] is not + /// currently fully standardized and is undergoing development. + /// Support for this feature can be enabled through this method for + /// appropriate WebAssembly modules. + /// + /// This feature gates support for linear memory of sizes larger than + /// 2^32 bits. + /// + /// This is `false` by default. + /// + /// [proposal]: https://github.com/WebAssembly/memory64 + pub fn memory64(&mut self, enable: bool) -> &mut Self { + self.memory64 = enable; + self + } +} + +impl Default for Features { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod test_features { + use super::*; + #[test] + fn default_features() { + let default = Features::default(); + assert_eq!( + default, + Features { + threads: false, + reference_types: true, + simd: true, + bulk_memory: true, + multi_value: true, + tail_call: false, + multi_memory: false, + memory64: false, + exceptions: false, + mutable_global: true, + saturating_float_to_int: true, + sign_extension: true, + } + ); + } + + #[test] + fn enable_threads() { + let mut features = Features::new(); + features.bulk_memory(false).threads(true); + + assert!(features.threads); + } + + #[test] + fn enable_reference_types() { + let mut features = Features::new(); + features.bulk_memory(false).reference_types(true); + assert!(features.reference_types); + assert!(features.bulk_memory); + } + + #[test] + fn enable_simd() { + let mut features = Features::new(); + features.simd(true); + assert!(features.simd); + } + + #[test] + fn enable_multi_value() { + let mut features = Features::new(); + features.multi_value(true); + assert!(features.multi_value); + } + + #[test] + fn enable_bulk_memory() { + let mut features = Features::new(); + features.bulk_memory(true); + assert!(features.bulk_memory); + } + + #[test] + fn disable_bulk_memory() { + let mut features = Features::new(); + features.threads(true).reference_types(true).bulk_memory(false); + assert!(!features.bulk_memory); + assert!(!features.reference_types); + } + + #[test] + fn enable_tail_call() { + let mut features = Features::new(); + features.tail_call(true); + assert!(features.tail_call); + } + + #[test] + fn enable_multi_memory() { + let mut features = Features::new(); + features.multi_memory(true); + assert!(features.multi_memory); + } + + #[test] + fn enable_memory64() { + let mut features = Features::new(); + features.memory64(true); + assert!(features.memory64); + } +} diff --git a/runtime/unc-vm/types/src/indexes.rs b/runtime/unc-vm/types/src/indexes.rs new file mode 100644 index 000000000..09412dc80 --- /dev/null +++ b/runtime/unc-vm/types/src/indexes.rs @@ -0,0 +1,255 @@ +//! Helper functions and structures for the translation. +use crate::entity::entity_impl; +use core::u32; + +/// Index type of a function defined locally inside the WebAssembly module. +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + Debug, + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, +)] +#[archive(as = "Self")] +#[repr(transparent)] +pub struct LocalFunctionIndex(u32); +entity_impl!(LocalFunctionIndex); + +/// Index type of a table defined locally inside the WebAssembly module. +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +pub struct LocalTableIndex(u32); +entity_impl!(LocalTableIndex); + +/// Index type of a memory defined locally inside the WebAssembly module. +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +pub struct LocalMemoryIndex(u32); +entity_impl!(LocalMemoryIndex); + +/// Index type of a global defined locally inside the WebAssembly module. +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + Debug, + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, +)] +#[archive(as = "Self")] +#[repr(transparent)] +pub struct LocalGlobalIndex(u32); +entity_impl!(LocalGlobalIndex); + +/// Index type of a function (imported or local) inside the WebAssembly module. +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + Debug, + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, +)] +#[archive(as = "Self")] +#[repr(transparent)] +pub struct FunctionIndex(u32); +entity_impl!(FunctionIndex); + +/// Index type of a table (imported or local) inside the WebAssembly module. +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + Debug, + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, +)] +#[archive(as = "Self")] +#[repr(transparent)] +pub struct TableIndex(u32); +entity_impl!(TableIndex); + +/// Index type of a global variable (imported or local) inside the WebAssembly module. +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + Debug, + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, +)] +#[archive(as = "Self")] +#[repr(transparent)] +pub struct GlobalIndex(u32); +entity_impl!(GlobalIndex); + +/// Index type of a linear memory (imported or local) inside the WebAssembly module. +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + Debug, + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, +)] +#[archive(as = "Self")] +#[repr(transparent)] +pub struct MemoryIndex(u32); +entity_impl!(MemoryIndex); + +/// Index type of a signature (imported or local) inside the WebAssembly module. +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + Debug, + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, +)] +#[archive(as = "Self")] +#[repr(transparent)] +pub struct SignatureIndex(u32); +entity_impl!(SignatureIndex); + +/// Index type of a passive data segment inside the WebAssembly module. +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + Debug, + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, +)] +#[archive(as = "Self")] +#[repr(transparent)] +pub struct DataIndex(u32); +entity_impl!(DataIndex); + +/// Index type of a passive element segment inside the WebAssembly module. +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + Debug, + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, +)] +#[archive(as = "Self")] +#[repr(transparent)] +pub struct ElemIndex(u32); +entity_impl!(ElemIndex); + +/// Index type of a custom section inside a WebAssembly module. +#[derive( + Copy, + Clone, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + Debug, + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, +)] +#[archive(as = "Self")] +#[repr(transparent)] +pub struct CustomSectionIndex(u32); +entity_impl!(CustomSectionIndex); + +/// An entity to export. +#[derive( + Clone, + Debug, + Hash, + PartialEq, + Eq, + PartialOrd, + Ord, + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, +)] +#[archive(as = "Self")] +#[repr(u8)] +pub enum ExportIndex { + /// Function export. + Function(FunctionIndex), + /// Table export. + Table(TableIndex), + /// Memory export. + Memory(MemoryIndex), + /// Global export. + Global(GlobalIndex), +} + +/// An entity to import. +#[derive( + Clone, + Debug, + Hash, + PartialEq, + Eq, + PartialOrd, + Ord, + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, +)] +#[archive(as = "Self")] +#[repr(u8)] +pub enum ImportIndex { + /// Function import. + Function(FunctionIndex), + /// Table import. + Table(TableIndex), + /// Memory import. + Memory(MemoryIndex), + /// Global import. + Global(GlobalIndex), +} diff --git a/runtime/unc-vm/types/src/initializers.rs b/runtime/unc-vm/types/src/initializers.rs new file mode 100644 index 000000000..289871891 --- /dev/null +++ b/runtime/unc-vm/types/src/initializers.rs @@ -0,0 +1,79 @@ +use crate::indexes::{FunctionIndex, GlobalIndex, MemoryIndex, TableIndex}; +use crate::lib::std::boxed::Box; + +/// A WebAssembly table initializer. +#[derive(Clone, Debug, Hash, PartialEq, Eq, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +pub struct OwnedTableInitializer { + /// The index of a table to initialize. + pub table_index: TableIndex, + /// Optionally, a global variable giving a base index. + pub base: Option, + /// The offset to add to the base. + pub offset: usize, + /// The values to write into the table elements. + pub elements: Box<[FunctionIndex]>, +} + +/// A memory index and offset within that memory where a data initialization +/// should be performed. +#[derive(Clone, Debug, PartialEq, Eq, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +pub struct DataInitializerLocation { + /// The index of the memory to initialize. + pub memory_index: MemoryIndex, + + /// Optionally a Global variable base to initialize at. + pub base: Option, + + /// A constant offset to initialize at. + pub offset: usize, +} + +/// A data initializer for linear memory. +#[derive(Debug)] +pub struct DataInitializer<'data> { + /// The location where the initialization is to be performed. + pub location: DataInitializerLocation, + + /// The initialization data. + pub data: &'data [u8], +} + +/// As `DataInitializer` but owning the data rather than +/// holding a reference to it +#[derive(Debug, Clone, PartialEq, Eq, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +pub struct OwnedDataInitializer { + /// The location where the initialization is to be performed. + pub location: DataInitializerLocation, + + /// The initialization owned data. + pub data: Vec, +} + +impl OwnedDataInitializer { + /// Creates a new `OwnedDataInitializer` from a `DataInitializer`. + pub fn new(borrowed: &DataInitializer<'_>) -> Self { + Self { location: borrowed.location.clone(), data: borrowed.data.to_vec() } + } +} + +impl<'a> From<&'a OwnedDataInitializer> for DataInitializer<'a> { + fn from(init: &'a OwnedDataInitializer) -> Self { + DataInitializer { location: init.location.clone(), data: &*init.data } + } +} + +impl<'a> From<&'a ArchivedOwnedDataInitializer> for DataInitializer<'a> { + fn from(init: &'a ArchivedOwnedDataInitializer) -> Self { + DataInitializer { + location: rkyv::Deserialize::deserialize(&init.location, &mut rkyv::Infallible) + .expect("deserialization cannot fail"), + data: &*init.data, + } + } +} + +impl<'a> From> for OwnedDataInitializer { + fn from(init: DataInitializer<'a>) -> Self { + Self { location: init.location.clone(), data: init.data.to_vec() } + } +} diff --git a/runtime/unc-vm/types/src/lib.rs b/runtime/unc-vm/types/src/lib.rs new file mode 100644 index 000000000..4a00aa28c --- /dev/null +++ b/runtime/unc-vm/types/src/lib.rs @@ -0,0 +1,76 @@ +//! This are the common types and utility tools for using WebAssembly +//! in a Rust environment. +//! +//! This crate provides common structures such as `Type` or `Value`, type indexes +//! and native function wrappers with `Func`. + +#![deny(missing_docs, unused_extern_crates)] +#![warn(unused_import_braces)] +#![deny(unstable_features)] +#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] +#![cfg_attr( + feature = "cargo-clippy", + warn( + clippy::float_arithmetic, + clippy::mut_mut, + clippy::nonminimal_bool, + clippy::map_unwrap_or, + clippy::print_stdout, + clippy::unicode_not_nfc, + clippy::use_self + ) +)] + +/// The `lib` module defines a `std` module that is identical whether +/// the `core` or the `std` feature is enabled. +pub mod lib { + /// Custom `std` module. + pub mod std { + pub use std::{ + any, borrow, boxed, cell, cmp, convert, fmt, format, hash, iter, marker, mem, ops, ptr, + rc, slice, string, sync, u32, vec, + }; + } +} + +mod archives; +mod extern_ref; +mod features; +mod indexes; +mod initializers; +mod memory_view; +mod module; +mod native; +pub mod partial_sum_map; +mod types; +mod units; +mod values; + +/// The entity module, with common helpers for Rust structures +pub mod entity; +pub use crate::extern_ref::{ExternRef, VMExternRef}; +pub use crate::features::Features; +pub use crate::indexes::{ + CustomSectionIndex, DataIndex, ElemIndex, ExportIndex, FunctionIndex, GlobalIndex, ImportIndex, + LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, + SignatureIndex, TableIndex, +}; +pub use crate::initializers::{ + DataInitializer, DataInitializerLocation, OwnedDataInitializer, OwnedTableInitializer, +}; +pub use crate::memory_view::{Atomically, MemoryView}; +pub use crate::module::{ImportCounts, ModuleInfo}; +pub use crate::native::{NativeWasmType, ValueType}; +pub use crate::units::{ + Bytes, PageCountOutOfRange, Pages, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, +}; +pub use crate::values::{Value, WasmValueType}; +pub use types::{ + ExportType, ExternType, FastGasCounter, FunctionType, FunctionTypeRef, GlobalInit, GlobalType, + Import, InstanceConfig, MemoryType, Mutability, TableType, Type, V128, +}; + +pub use archives::ArchivableIndexMap; + +/// Version number of this crate. +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/runtime/unc-vm/types/src/memory_view.rs b/runtime/unc-vm/types/src/memory_view.rs new file mode 100644 index 000000000..d2f69bcb0 --- /dev/null +++ b/runtime/unc-vm/types/src/memory_view.rs @@ -0,0 +1,118 @@ +use crate::lib::std::cell::Cell; +use crate::lib::std::marker::PhantomData; +use crate::lib::std::ops::Deref; +// use crate::lib::std::ops::{Bound, RangeBounds}; +use crate::lib::std::slice; +use crate::lib::std::sync::atomic::{ + AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicU16, AtomicU32, AtomicU64, AtomicU8, +}; +use crate::native::ValueType; + +pub trait Atomic { + type Output; +} + +macro_rules! atomic { + ( $($for:ty => $output:ty),+ ) => { + $( + impl Atomic for $for { + type Output = $output; + } + )+ + } +} + +atomic!( + i8 => AtomicI8, + i16 => AtomicI16, + i32 => AtomicI32, + i64 => AtomicI64, + u8 => AtomicU8, + u16 => AtomicU16, + u32 => AtomicU32, + u64 => AtomicU64, + f32 => AtomicU32, + f64 => AtomicU64 +); + +/// A trait that represants an atomic type. +pub trait Atomicity {} + +/// Atomically. +pub struct Atomically; +impl Atomicity for Atomically {} + +/// Non-atomically. +pub struct NonAtomically; +impl Atomicity for NonAtomically {} + +/// A view into a memory. +pub struct MemoryView<'a, T: 'a, A = NonAtomically> { + ptr: *mut T, + // Note: the length is in the terms of `size::()`. + // The total length in memory is `size::() * length`. + length: usize, + _phantom: PhantomData<(&'a [Cell], A)>, +} + +impl<'a, T> MemoryView<'a, T, NonAtomically> +where + T: ValueType, +{ + /// Creates a new MemoryView given a `pointer` and `length`. + pub unsafe fn new(ptr: *mut T, length: u32) -> Self { + Self { ptr, length: length as usize, _phantom: PhantomData } + } + + /// Creates a subarray view from this `MemoryView`. + pub fn subarray(&self, start: u32, end: u32) -> Self { + assert!((start as usize) < self.length, "The range start is bigger than current length"); + assert!((end as usize) < self.length, "The range end is bigger than current length"); + + Self { + ptr: unsafe { self.ptr.add(start as usize) }, + length: (end - start) as usize, + _phantom: PhantomData, + } + } + + /// Copy the contents of the source slice into this `MemoryView`. + /// + /// This function will efficiently copy the memory from within the wasm + /// module’s own linear memory to this typed array. + /// + /// # Safety + /// + /// This method is unsafe because the caller will need to make sure + /// there are no data races when copying memory into the view. + pub unsafe fn copy_from(&self, src: &[T]) { + // We cap at a max length + let sliced_src = &src[..self.length]; + for (i, byte) in sliced_src.iter().enumerate() { + *self.ptr.offset(i as isize) = *byte; + } + } +} + +impl<'a, T: Atomic> MemoryView<'a, T> { + /// Get atomic access to a memory view. + pub fn atomically(&self) -> MemoryView<'a, T::Output, Atomically> { + MemoryView { ptr: self.ptr as *mut T::Output, length: self.length, _phantom: PhantomData } + } +} + +impl<'a, T> Deref for MemoryView<'a, T, NonAtomically> { + type Target = [Cell]; + fn deref(&self) -> &[Cell] { + let mut_slice: &mut [T] = unsafe { slice::from_raw_parts_mut(self.ptr, self.length) }; + let cell_slice: &Cell<[T]> = Cell::from_mut(mut_slice); + cell_slice.as_slice_of_cells() + } +} + +impl<'a, T> Deref for MemoryView<'a, T, Atomically> { + type Target = [T]; + fn deref(&self) -> &[T] { + unsafe { slice::from_raw_parts(self.ptr as *const T, self.length) } + } +} diff --git a/runtime/unc-vm/types/src/module.rs b/runtime/unc-vm/types/src/module.rs new file mode 100644 index 000000000..785da0c2f --- /dev/null +++ b/runtime/unc-vm/types/src/module.rs @@ -0,0 +1,437 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Data structure for representing WebAssembly modules in a +//! `wasmer::Module`. + +use crate::entity::{EntityRef, PrimaryMap}; +use crate::ArchivableIndexMap; +use crate::{ + CustomSectionIndex, DataIndex, ElemIndex, ExportIndex, FunctionIndex, FunctionType, + GlobalIndex, GlobalInit, GlobalType, ImportIndex, LocalFunctionIndex, LocalGlobalIndex, + LocalMemoryIndex, LocalTableIndex, MemoryIndex, MemoryType, OwnedTableInitializer, + SignatureIndex, TableIndex, TableType, +}; +use indexmap::IndexMap; +use rkyv::{ + de::SharedDeserializeRegistry, ser::ScratchSpace, ser::Serializer, + ser::SharedSerializeRegistry, Archive, Archived, Fallible, +}; +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::fmt; +use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; +use std::sync::Arc; + +#[derive(Debug, Clone, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +pub struct ModuleId { + id: usize, +} + +impl ModuleId { + pub fn id(&self) -> String { + format!("{}", &self.id) + } +} + +impl Default for ModuleId { + fn default() -> Self { + static NEXT_ID: AtomicUsize = AtomicUsize::new(0); + Self { id: NEXT_ID.fetch_add(1, SeqCst) } + } +} + +/// The counts of imported entities in a WebAssembly module. +#[derive( + Debug, Copy, Clone, Default, PartialEq, Eq, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, +)] +#[archive(as = "Self")] +pub struct ImportCounts { + /// Number of imported functions in the module. + pub functions: u32, + + /// Number of imported tables in the module. + pub tables: u32, + + /// Number of imported memories in the module. + pub memories: u32, + + /// Number of imported globals in the module. + pub globals: u32, +} + +impl ImportCounts { + fn make_local(idx: I, imports: u32) -> Result { + EntityRef::index(idx).checked_sub(imports as _).map(R::new).ok_or(idx) + } + + /// Convert the `FunctionIndex` to a `LocalFunctionIndex`. + pub fn local_function_index( + &self, + idx: FunctionIndex, + ) -> Result { + Self::make_local(idx, self.functions) + } + + /// Convert the `TableIndex` to a `LocalTableIndex`. + pub fn local_table_index(&self, idx: TableIndex) -> Result { + Self::make_local(idx, self.tables) + } + + /// Convert the `MemoryIndex` to a `LocalMemoryIndex`. + pub fn local_memory_index(&self, idx: MemoryIndex) -> Result { + Self::make_local(idx, self.memories) + } + + /// Convert the `GlobalIndex` to a `LocalGlobalIndex`. + pub fn local_global_index(&self, idx: GlobalIndex) -> Result { + Self::make_local(idx, self.globals) + } + + fn make_index(idx: I, imports: u32) -> R { + let imports = imports as usize; + R::new(idx.index() + imports) + } + + /// Convert the `LocalFunctionIndex` to a `FunctionIndex`. + pub fn function_index(&self, idx: LocalFunctionIndex) -> FunctionIndex { + Self::make_index(idx, self.functions) + } + + /// Convert the `LocalTableIndex` to a `TableIndex`. + pub fn table_index(&self, idx: LocalTableIndex) -> TableIndex { + Self::make_index(idx, self.tables) + } + + /// Convert the `LocalMemoryIndex` to a `MemoryIndex`. + pub fn memory_index(&self, idx: LocalMemoryIndex) -> MemoryIndex { + Self::make_index(idx, self.memories) + } + + /// Convert the `LocalGlobalIndex` to a `GlobalIndex`. + pub fn global_index(&self, idx: LocalGlobalIndex) -> GlobalIndex { + Self::make_index(idx, self.globals) + } +} + +/// A translated WebAssembly module, excluding the function bodies and +/// memory initializers. +#[derive(Debug, Clone, Default)] +pub struct ModuleInfo { + /// A unique identifier (within this process) for this module. + /// + /// We skip serialization/deserialization of this field, as it + /// should be computed by the process. + /// + /// It's not skipped in rkyv, but that is okay, because even though it's skipped in + /// bincode/serde it's still deserialized back as a garbage number, and later override from + /// computed by the process + pub id: ModuleId, + + /// The name of this wasm module, often found in the wasm file. + pub name: Option, + + /// Imported entities with the (module, field, index_of_the_import) + /// + /// Keeping the `index_of_the_import` is important, as there can be + /// two same references to the same import, and we don't want to confuse + /// them. + pub imports: IndexMap<(String, String, u32), ImportIndex>, + + /// Exported entities. + pub exports: IndexMap, + + /// The module "start" function, if present. + pub start_function: Option, + + /// WebAssembly table initializers. + pub table_initializers: Vec, + + /// WebAssembly passive elements. + pub passive_elements: BTreeMap>, + + /// WebAssembly passive data segments. + pub passive_data: BTreeMap>, + + /// WebAssembly global initializers. + pub global_initializers: PrimaryMap, + + /// WebAssembly function names. + pub function_names: HashMap, + + /// WebAssembly function signatures. + pub signatures: PrimaryMap, + + /// WebAssembly functions (imported and local). + pub functions: PrimaryMap, + + /// WebAssembly tables (imported and local). + pub tables: PrimaryMap, + + /// WebAssembly linear memories (imported and local). + pub memories: PrimaryMap, + + /// WebAssembly global variables (imported and local). + pub globals: PrimaryMap, + + /// Custom sections in the module. + pub custom_sections: IndexMap, + + /// The data for each CustomSection in the module. + pub custom_sections_data: PrimaryMap>, + + /// The counts of imported entities. + pub import_counts: ImportCounts, +} + +/// Mirror version of ModuleInfo that can derive rkyv traits +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +pub struct ArchivableModuleInfo { + pub name: Option, + pub imports: ArchivableIndexMap<(String, String, u32), ImportIndex>, + pub exports: ArchivableIndexMap, + pub start_function: Option, + pub table_initializers: Vec, + pub passive_elements: BTreeMap>, + pub passive_data: BTreeMap>, + pub global_initializers: PrimaryMap, + pub function_names: BTreeMap, + pub signatures: PrimaryMap, + pub functions: PrimaryMap, + pub tables: PrimaryMap, + pub memories: PrimaryMap, + pub globals: PrimaryMap, + pub custom_sections: ArchivableIndexMap, + pub custom_sections_data: PrimaryMap>, + pub import_counts: ImportCounts, +} + +impl From for ArchivableModuleInfo { + fn from(it: ModuleInfo) -> Self { + Self { + name: it.name, + imports: ArchivableIndexMap::from(it.imports), + exports: ArchivableIndexMap::from(it.exports), + start_function: it.start_function, + table_initializers: it.table_initializers, + passive_elements: it.passive_elements.into_iter().collect(), + passive_data: it.passive_data.into_iter().collect(), + global_initializers: it.global_initializers, + function_names: it.function_names.into_iter().collect(), + signatures: it.signatures, + functions: it.functions, + tables: it.tables, + memories: it.memories, + globals: it.globals, + custom_sections: ArchivableIndexMap::from(it.custom_sections), + custom_sections_data: it.custom_sections_data, + import_counts: it.import_counts, + } + } +} + +impl From for ModuleInfo { + fn from(it: ArchivableModuleInfo) -> Self { + Self { + id: Default::default(), + name: it.name, + imports: it.imports.into(), + exports: it.exports.into(), + start_function: it.start_function, + table_initializers: it.table_initializers, + passive_elements: it.passive_elements.into_iter().collect(), + passive_data: it.passive_data.into_iter().collect(), + global_initializers: it.global_initializers, + function_names: it.function_names.into_iter().collect(), + signatures: it.signatures, + functions: it.functions, + tables: it.tables, + memories: it.memories, + globals: it.globals, + custom_sections: it.custom_sections.into(), + custom_sections_data: it.custom_sections_data, + import_counts: it.import_counts, + } + } +} + +impl From<&ModuleInfo> for ArchivableModuleInfo { + fn from(it: &ModuleInfo) -> Self { + Self::from(it.clone()) + } +} + +impl Archive for ModuleInfo { + type Archived = ::Archived; + type Resolver = ::Resolver; + + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivableModuleInfo::from(self).resolve(pos, resolver, out) + } +} + +impl rkyv::Serialize + for ModuleInfo +{ + fn serialize(&self, serializer: &mut S) -> Result { + ArchivableModuleInfo::from(self).serialize(serializer) + } +} + +impl rkyv::Deserialize + for Archived +{ + fn deserialize(&self, deserializer: &mut D) -> Result { + let r: ArchivableModuleInfo = + rkyv::Deserialize::::deserialize(self, deserializer)?; + Ok(ModuleInfo::from(r)) + } +} + +// For test serialization correctness, everything except module id should be same +impl PartialEq for ModuleInfo { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + && self.imports == other.imports + && self.exports == other.exports + && self.start_function == other.start_function + && self.table_initializers == other.table_initializers + && self.passive_elements == other.passive_elements + && self.passive_data == other.passive_data + && self.global_initializers == other.global_initializers + && self.function_names == other.function_names + && self.signatures == other.signatures + && self.functions == other.functions + && self.tables == other.tables + && self.memories == other.memories + && self.globals == other.globals + && self.custom_sections == other.custom_sections + && self.custom_sections_data == other.custom_sections_data + && self.import_counts == other.import_counts + } +} + +impl Eq for ModuleInfo {} + +impl ModuleInfo { + /// Allocates the module data structures. + pub fn new() -> Self { + Default::default() + } + + /// Get the given passive element, if it exists. + pub fn get_passive_element(&self, index: ElemIndex) -> Option<&[FunctionIndex]> { + self.passive_elements.get(&index).map(|es| &**es) + } + + /// Get the exported signatures of the module + pub fn exported_signatures(&self) -> Vec { + self.exports + .iter() + .filter_map(|(_name, export_index)| match export_index { + ExportIndex::Function(i) => { + let signature = self.functions.get(*i).unwrap(); + let func_type = self.signatures.get(*signature).unwrap(); + Some(func_type.clone()) + } + _ => None, + }) + .collect::>() + } + + /// Get the custom sections of the module given a `name`. + pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator> + 'a { + self.custom_sections.iter().filter_map(move |(section_name, section_index)| { + if name != section_name { + return None; + } + Some(self.custom_sections_data[*section_index].clone()) + }) + } + + /// Convert a `LocalFunctionIndex` into a `FunctionIndex`. + pub fn func_index(&self, local_func: LocalFunctionIndex) -> FunctionIndex { + self.import_counts.function_index(local_func) + } + + /// Convert a `FunctionIndex` into a `LocalFunctionIndex`. Returns None if the + /// index is an imported function. + pub fn local_func_index(&self, func: FunctionIndex) -> Option { + self.import_counts.local_function_index(func).ok() + } + + /// Test whether the given function index is for an imported function. + pub fn is_imported_function(&self, index: FunctionIndex) -> bool { + self.local_func_index(index).is_none() + } + + /// Convert a `LocalTableIndex` into a `TableIndex`. + pub fn table_index(&self, local_table: LocalTableIndex) -> TableIndex { + self.import_counts.table_index(local_table) + } + + /// Convert a `TableIndex` into a `LocalTableIndex`. Returns None if the + /// index is an imported table. + pub fn local_table_index(&self, table: TableIndex) -> Option { + self.import_counts.local_table_index(table).ok() + } + + /// Test whether the given table index is for an imported table. + pub fn is_imported_table(&self, index: TableIndex) -> bool { + self.local_table_index(index).is_none() + } + + /// Convert a `LocalMemoryIndex` into a `MemoryIndex`. + pub fn memory_index(&self, local_memory: LocalMemoryIndex) -> MemoryIndex { + self.import_counts.memory_index(local_memory) + } + + /// Convert a `MemoryIndex` into a `LocalMemoryIndex`. Returns None if the + /// index is an imported memory. + pub fn local_memory_index(&self, memory: MemoryIndex) -> Option { + self.import_counts.local_memory_index(memory).ok() + } + + /// Test whether the given memory index is for an imported memory. + pub fn is_imported_memory(&self, index: MemoryIndex) -> bool { + self.local_memory_index(index).is_none() + } + + /// Convert a `LocalGlobalIndex` into a `GlobalIndex`. + pub fn global_index(&self, local_global: LocalGlobalIndex) -> GlobalIndex { + self.import_counts.global_index(local_global) + } + + /// Convert a `GlobalIndex` into a `LocalGlobalIndex`. Returns None if the + /// index is an imported global. + pub fn local_global_index(&self, global: GlobalIndex) -> Option { + self.import_counts.local_global_index(global).ok() + } + + /// Test whether the given global index is for an imported global. + pub fn is_imported_global(&self, index: GlobalIndex) -> bool { + self.local_global_index(index).is_none() + } + + /// Get the Module name + pub fn name(&self) -> String { + match self.name { + Some(ref name) => name.to_string(), + None => "".to_string(), + } + } + + /// Get the imported function types of the module. + pub fn imported_function_types<'a>(&'a self) -> impl Iterator + 'a { + self.functions + .values() + .take(self.import_counts.functions as usize) + .map(move |sig_index| self.signatures[*sig_index].clone()) + } +} + +impl fmt::Display for ModuleInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name()) + } +} diff --git a/runtime/unc-vm/types/src/native.rs b/runtime/unc-vm/types/src/native.rs new file mode 100644 index 000000000..0136cb083 --- /dev/null +++ b/runtime/unc-vm/types/src/native.rs @@ -0,0 +1,270 @@ +//! This module permits to create native functions +//! easily in Rust, thanks to its advanced typing system. + +use crate::extern_ref::VMExternRef; +use crate::lib::std::fmt; +use crate::types::Type; +use crate::values::{Value, WasmValueType}; + +/// `NativeWasmType` represents a Wasm type that has a direct +/// representation on the host (hence the “native” term). +/// +/// It uses the Rust Type system to automatically detect the +/// Wasm type associated with a native Rust type. +/// +/// ``` +/// use unc_vm_types::{NativeWasmType, Type}; +/// +/// let wasm_type = i32::WASM_TYPE; +/// assert_eq!(wasm_type, Type::I32); +/// ``` +/// +/// > Note: This strategy will be needed later to +/// > automatically detect the signature of a Rust function. +pub trait NativeWasmType: Sized { + /// The ABI for this type (i32, i64, f32, f64) + type Abi: Copy + fmt::Debug; + + /// Type for this `NativeWasmType`. + const WASM_TYPE: Type; + + #[doc(hidden)] + fn from_abi(abi: Self::Abi) -> Self; + + #[doc(hidden)] + fn into_abi(self) -> Self::Abi; + + /// Convert self to i128 binary representation. + fn to_binary(self) -> i128; + + /// Convert self to a `Value`. + fn to_value(self) -> Value { + let binary = self.to_binary(); + // we need a store, we're just hoping we don't actually use it via funcref + // TODO(reftypes): we need an actual solution here + let hack = 3; + + unsafe { Value::read_value_from(&hack, &binary, Self::WASM_TYPE) } + } + + /// Convert to self from i128 binary representation. + fn from_binary(binary: i128) -> Self; +} + +impl NativeWasmType for i32 { + const WASM_TYPE: Type = Type::I32; + type Abi = Self; + + #[inline] + fn from_abi(abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self) -> Self::Abi { + self + } + + #[inline] + fn to_binary(self) -> i128 { + self as _ + } + + #[inline] + fn from_binary(bits: i128) -> Self { + bits as _ + } +} + +impl NativeWasmType for i64 { + const WASM_TYPE: Type = Type::I64; + type Abi = Self; + + #[inline] + fn from_abi(abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self) -> Self::Abi { + self + } + + #[inline] + fn to_binary(self) -> i128 { + self as _ + } + + #[inline] + fn from_binary(bits: i128) -> Self { + bits as _ + } +} + +impl NativeWasmType for f32 { + const WASM_TYPE: Type = Type::F32; + type Abi = Self; + + #[inline] + fn from_abi(abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self) -> Self::Abi { + self + } + + #[inline] + fn to_binary(self) -> i128 { + self.to_bits() as _ + } + + #[inline] + fn from_binary(bits: i128) -> Self { + Self::from_bits(bits as _) + } +} + +impl NativeWasmType for f64 { + const WASM_TYPE: Type = Type::F64; + type Abi = Self; + + #[inline] + fn from_abi(abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self) -> Self::Abi { + self + } + + #[inline] + fn to_binary(self) -> i128 { + self.to_bits() as _ + } + + #[inline] + fn from_binary(bits: i128) -> Self { + Self::from_bits(bits as _) + } +} + +impl NativeWasmType for u128 { + const WASM_TYPE: Type = Type::V128; + type Abi = Self; + + #[inline] + fn from_abi(abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self) -> Self::Abi { + self + } + + #[inline] + fn to_binary(self) -> i128 { + self as _ + } + + #[inline] + fn from_binary(bits: i128) -> Self { + bits as _ + } +} + +impl NativeWasmType for VMExternRef { + const WASM_TYPE: Type = Type::ExternRef; + type Abi = Self; + + #[inline] + fn from_abi(abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self) -> Self::Abi { + self + } + + #[inline] + fn to_binary(self) -> i128 { + self.to_binary() + } + + #[inline] + fn from_binary(bits: i128) -> Self { + // TODO(reftypes): ensure that the safety invariants are actually upheld here + unsafe { Self::from_binary(bits) } + } +} + +#[cfg(test)] +mod test_native_type { + use super::*; + use crate::types::Type; + + #[test] + fn test_wasm_types() { + assert_eq!(i32::WASM_TYPE, Type::I32); + assert_eq!(i64::WASM_TYPE, Type::I64); + assert_eq!(f32::WASM_TYPE, Type::F32); + assert_eq!(f64::WASM_TYPE, Type::F64); + assert_eq!(u128::WASM_TYPE, Type::V128); + } + + #[test] + fn test_roundtrip() { + assert_eq!(i32::from_binary(42i32.to_binary()), 42i32); + assert_eq!(i64::from_binary(42i64.to_binary()), 42i64); + assert_eq!(f32::from_binary(42f32.to_binary()), 42f32); + assert_eq!(f64::from_binary(42f64.to_binary()), 42f64); + assert_eq!(u128::from_binary(42u128.to_binary()), 42u128); + } +} + +// pub trait IntegerAtomic +// where +// Self: Sized +// { +// type Primitive; + +// fn add(&self, other: Self::Primitive) -> Self::Primitive; +// fn sub(&self, other: Self::Primitive) -> Self::Primitive; +// fn and(&self, other: Self::Primitive) -> Self::Primitive; +// fn or(&self, other: Self::Primitive) -> Self::Primitive; +// fn xor(&self, other: Self::Primitive) -> Self::Primitive; +// fn load(&self) -> Self::Primitive; +// fn store(&self, other: Self::Primitive) -> Self::Primitive; +// fn compare_exchange(&self, expected: Self::Primitive, new: Self::Primitive) -> Self::Primitive; +// fn swap(&self, other: Self::Primitive) -> Self::Primitive; +// } + +/// Trait for a Value type. A Value type is a type that is always valid and may +/// be safely copied. +/// +/// That is, for all possible bit patterns a valid Value type can be constructed +/// from those bits. +/// +/// Concretely a `u32` is a Value type because every combination of 32 bits is +/// a valid `u32`. However a `bool` is _not_ a Value type because any bit patterns +/// other than `0` and `1` are invalid in Rust and may cause undefined behavior if +/// a `bool` is constructed from those bytes. +pub unsafe trait ValueType: Copy +where + Self: Sized, +{ +} + +macro_rules! impl_value_type_for { + ( $($type:ty),* ) => { + $( + unsafe impl ValueType for $type {} + )* + }; +} + +impl_value_type_for!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64); diff --git a/runtime/unc-vm/types/src/partial_sum_map.rs b/runtime/unc-vm/types/src/partial_sum_map.rs new file mode 100644 index 000000000..05902f0b3 --- /dev/null +++ b/runtime/unc-vm/types/src/partial_sum_map.rs @@ -0,0 +1,163 @@ +//! Partial sum maps +//! +//! These maps allow you to efficiently store repeating sequences of a value. An example of such +//! sequence could be the list of locals for a webassembly function. +//! +//! Considering the locals example above, it might be represented as a `u32` partial sum of the +//! local’s index. The locals between the index of the previous element and the current element +//! have the `WpType` type. So, given +//! +//! (0, u32), (10, u64), (15, f64) +//! +//! then 0th local would be a u32, locals `1..=10` – u64 and locals `11..=15` – f64. +//! +//! The type of a given index can be quickly found with a binary search over the partial sum +//! field. + +/// A Map from keys to values that is able to efficiently store repeating occurences of the value. +/// +/// This map can only be appended to. +#[derive(Debug)] +pub struct PartialSumMap { + /// Keys between ((keys[n-1] + 1) or 0) and keys[n] (both included) have value values[n] + keys: Vec, + values: Vec, + size: K, +} + +impl PartialSumMap { + /// Create a new `PartialSumMap`. + /// + /// Does not allocate. + pub fn new() -> Self { + Self { keys: vec![], values: vec![], size: K::zero() } + } + + /// Push `count` number of `value`s. + /// + /// `O(1)` amortized. + pub fn push(&mut self, count: K, value: V) -> Result<(), Error> { + if count != K::zero() { + self.size = self.size.checked_add(&count).ok_or(Error::Overflow)?; + self.keys.push(self.size.clone() - K::one()); + self.values.push(value); + } + Ok(()) + } + + /// Get the current maximum index that can be used with `find` for this map. + /// + /// Will return `None` if there are no elements in this map yet. + /// + /// `O(1)` + pub fn max_index(&self) -> Option { + self.keys.last().cloned() + } + + /// Get the current (virtual) size of this map. This is the sum of all `count` arguments passed to `push` until now. + /// + /// Note that the result can be greater than `usize::MAX` if eg. `K` is a BigInt type. Cast at your own risk. + /// + /// `O(1)` + pub fn size(&self) -> &K { + &self.size + } + + /// Find the value by the index. + /// + /// This is a `O(n log n)` operation. + pub fn find(&self, index: K) -> Option<&V> { + match self.keys.binary_search(&index) { + // If this index would be inserted at the end of the list, then the + // index is out of bounds and we return a None. + // + // If `Ok` is returned we found the index exactly, or if `Err` is + // returned the position is the one which is the least index + // greater than `idx`, which is still the type of `idx` according + // to our "compressed" representation. In both cases we access the + // list at index `i`. + Ok(i) | Err(i) => self.values.get(i), + } + } +} + +/// Errors that occur when using PartialSumMap. +#[derive(Debug, PartialEq, Eq)] +pub enum Error { + /// The partial sum has overflowed. + Overflow, +} + +impl std::error::Error for Error {} +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Self::Overflow => "partial sum overflow", + }) + } +} + +#[cfg(test)] +mod tests { + use super::{Error, PartialSumMap}; + + #[test] + fn empty_partial_map() { + let map = PartialSumMap::::new(); + assert_eq!(None, map.find(0)); + assert_eq!(0, *map.size()); + } + + #[test] + fn basic_function() { + let mut map = PartialSumMap::::new(); + assert_eq!(None, map.max_index()); + assert_eq!(0, *map.size()); + for i in 0..10 { + map.push(1, i).unwrap(); + assert_eq!(Some(i), map.max_index()); + assert_eq!(i + 1, *map.size()); + } + for i in 0..10 { + assert_eq!(Some(&i), map.find(i)); + } + assert_eq!(None, map.find(10)); + assert_eq!(None, map.find(0xFFFF_FFFF)); + } + + #[test] + fn zero_count() { + let mut map = PartialSumMap::::new(); + assert_eq!(Ok(()), map.push(0, 0)); + assert_eq!(None, map.max_index()); + assert_eq!(0, *map.size()); + assert_eq!(Ok(()), map.push(10, 42)); + assert_eq!(Some(9), map.max_index()); + assert_eq!(10, *map.size()); + assert_eq!(Ok(()), map.push(0, 43)); + assert_eq!(Some(9), map.max_index()); + assert_eq!(10, *map.size()); + } + + #[test] + fn close_to_limit() { + let mut map = PartialSumMap::::new(); + assert_eq!(Ok(()), map.push(0xFFFF_FFFE, 42)); // we added values 0..=0xFFFF_FFFD + assert_eq!(Some(&42), map.find(0xFFFF_FFFD)); + assert_eq!(None, map.find(0xFFFF_FFFE)); + + assert_eq!(Err(Error::Overflow), map.push(100, 93)); // overflowing does not change the map + assert_eq!(Some(&42), map.find(0xFFFF_FFFD)); + assert_eq!(None, map.find(0xFFFF_FFFE)); + + assert_eq!(Ok(()), map.push(1, 322)); // we added value at index 0xFFFF_FFFE (which is the 0xFFFF_FFFFth value) + assert_eq!(Some(&42), map.find(0xFFFF_FFFD)); + assert_eq!(Some(&322), map.find(0xFFFF_FFFE)); + assert_eq!(None, map.find(0xFFFF_FFFF)); + + assert_eq!(Err(Error::Overflow), map.push(1, 1234)); // can't add any more stuff... + assert_eq!(Some(&42), map.find(0xFFFF_FFFD)); + assert_eq!(Some(&322), map.find(0xFFFF_FFFE)); + assert_eq!(None, map.find(0xFFFF_FFFF)); + } +} diff --git a/runtime/unc-vm/types/src/types.rs b/runtime/unc-vm/types/src/types.rs new file mode 100644 index 000000000..3be25b410 --- /dev/null +++ b/runtime/unc-vm/types/src/types.rs @@ -0,0 +1,634 @@ +use crate::indexes::{FunctionIndex, GlobalIndex}; +use crate::lib::std::fmt; +use crate::lib::std::format; +use crate::lib::std::string::{String, ToString}; +use crate::lib::std::vec::Vec; +use crate::units::Pages; +use crate::values::{Value, WasmValueType}; +use std::cell::UnsafeCell; +use std::rc::Rc; +use std::sync::Arc; + +// Type Representations + +// Value Types + +/// A list of all possible value types in WebAssembly. +#[derive( + Copy, Debug, Clone, Eq, PartialEq, Hash, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, +)] +#[archive(as = "Self")] +pub enum Type { + /// Signed 32 bit integer. + I32, + /// Signed 64 bit integer. + I64, + /// Floating point 32 bit integer. + F32, + /// Floating point 64 bit integer. + F64, + /// A 128 bit number. + V128, + /// A reference to opaque data in the Wasm instance. + ExternRef, /* = 128 */ + /// A reference to a Wasm function. + FuncRef, +} + +impl Type { + /// Returns true if `Type` matches any of the numeric types. (e.g. `I32`, + /// `I64`, `F32`, `F64`, `V128`). + pub fn is_num(self) -> bool { + matches!(self, Self::I32 | Self::I64 | Self::F32 | Self::F64 | Self::V128) + } + + /// Returns true if `Type` matches either of the reference types. + pub fn is_ref(self) -> bool { + matches!(self, Self::ExternRef | Self::FuncRef) + } +} + +impl fmt::Display for Type { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +#[derive( + Copy, Clone, Debug, Eq, PartialEq, Hash, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, +)] +#[archive(as = "Self")] +/// The WebAssembly V128 type +pub struct V128(pub(crate) [u8; 16]); + +impl V128 { + /// Get the bytes corresponding to the V128 value + pub fn bytes(&self) -> &[u8; 16] { + &self.0 + } + /// Iterate over the bytes in the constant. + pub fn iter(&self) -> impl Iterator { + self.0.iter() + } + + /// Convert the immediate into a vector. + pub fn to_vec(self) -> Vec { + self.0.to_vec() + } + + /// Convert the immediate into a slice. + pub fn as_slice(&self) -> &[u8] { + &self.0[..] + } +} + +impl From<[u8; 16]> for V128 { + fn from(array: [u8; 16]) -> Self { + Self(array) + } +} + +impl From<&[u8]> for V128 { + fn from(slice: &[u8]) -> Self { + assert_eq!(slice.len(), 16); + let mut buffer = [0; 16]; + buffer.copy_from_slice(slice); + Self(buffer) + } +} + +// External Types + +/// A list of all possible types which can be externally referenced from a +/// WebAssembly module. +/// +/// This list can be found in [`ImportType`] or [`ExportType`], so these types +/// can either be imported or exported. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ExternType { + /// This external type is the type of a WebAssembly function. + Function(FunctionType), + /// This external type is the type of a WebAssembly global. + Global(GlobalType), + /// This external type is the type of a WebAssembly table. + Table(TableType), + /// This external type is the type of a WebAssembly memory. + Memory(MemoryType), +} + +macro_rules! accessors { + ($(($variant:ident($ty:ty) $get:ident $unwrap:ident))*) => ($( + /// Attempt to return the underlying type of this external type, + /// returning `None` if it is a different type. + pub fn $get(&self) -> Option<&$ty> { + if let Self::$variant(e) = self { + Some(e) + } else { + None + } + } + + /// Returns the underlying descriptor of this [`ExternType`], panicking + /// if it is a different type. + /// + /// # Panics + /// + /// Panics if `self` is not of the right type. + pub fn $unwrap(&self) -> &$ty { + self.$get().expect(concat!("expected ", stringify!($ty))) + } + )*) +} + +impl ExternType { + accessors! { + (Function(FunctionType) func unwrap_func) + (Global(GlobalType) global unwrap_global) + (Table(TableType) table unwrap_table) + (Memory(MemoryType) memory unwrap_memory) + } +} + +// TODO: `shrink_to_fit` these or change it to `Box<[Type]>` if not using +// Cow or something else +/// The signature of a function that is either implemented +/// in a Wasm module or exposed to Wasm by the host. +/// +/// WebAssembly functions can have 0 or more parameters and results. +#[derive(Debug, Clone, PartialEq, Eq, Hash, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +pub struct FunctionType { + /// The parameters of the function + params: Arc<[Type]>, + /// The return values of the function + results: Arc<[Type]>, +} + +impl FunctionType { + /// Creates a new Function Type with the given parameter and return types. + pub fn new(params: Params, returns: Returns) -> Self + where + Params: Into>, + Returns: Into>, + { + Self { params: params.into(), results: returns.into() } + } + + /// Parameter types. + pub fn params(&self) -> &[Type] { + &self.params + } + + /// Return types. + pub fn results(&self) -> &[Type] { + &self.results + } +} + +impl fmt::Display for FunctionType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let params = self.params.iter().map(|p| format!("{:?}", p)).collect::>().join(", "); + let results = + self.results.iter().map(|p| format!("{:?}", p)).collect::>().join(", "); + write!(f, "[{}] -> [{}]", params, results) + } +} + +// Macro needed until https://rust-lang.github.io/rfcs/2000-const-generics.html is stable. +// See https://users.rust-lang.org/t/how-to-implement-trait-for-fixed-size-array-of-any-size/31494 +macro_rules! implement_from_pair_to_functiontype { + ($($N:literal,$M:literal)+) => { + $( + impl From<([Type; $N], [Type; $M])> for FunctionType { + fn from(pair: ([Type; $N], [Type; $M])) -> Self { + Self::new(&pair.0[..], &pair.1[..]) + } + } + )+ + } +} + +implement_from_pair_to_functiontype! { + 0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9 + 1,0 1,1 1,2 1,3 1,4 1,5 1,6 1,7 1,8 1,9 + 2,0 2,1 2,2 2,3 2,4 2,5 2,6 2,7 2,8 2,9 + 3,0 3,1 3,2 3,3 3,4 3,5 3,6 3,7 3,8 3,9 + 4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 4,8 4,9 + 5,0 5,1 5,2 5,3 5,4 5,5 5,6 5,7 5,8 5,9 + 6,0 6,1 6,2 6,3 6,4 6,5 6,6 6,7 6,8 6,9 + 7,0 7,1 7,2 7,3 7,4 7,5 7,6 7,7 7,8 7,9 + 8,0 8,1 8,2 8,3 8,4 8,5 8,6 8,7 8,8 8,9 + 9,0 9,1 9,2 9,3 9,4 9,5 9,6 9,7 9,8 9,9 +} + +impl From<&Self> for FunctionType { + fn from(as_ref: &Self) -> Self { + as_ref.clone() + } +} + +/// Borrowed version of [`FunctionType`]. +pub struct FunctionTypeRef<'a> { + /// The parameters of the function + params: &'a [Type], + /// The return values of the function + results: &'a [Type], +} + +impl<'a> FunctionTypeRef<'a> { + /// Create a new temporary function type. + pub fn new(params: &'a [Type], results: &'a [Type]) -> Self { + Self { params, results } + } + + /// Parameter types. + pub fn params(&self) -> &[Type] { + self.params + } + + /// Return types. + pub fn results(&self) -> &[Type] { + self.results + } +} + +impl<'a> From<&'a FunctionType> for FunctionTypeRef<'a> { + fn from(FunctionType { params, results }: &'a FunctionType) -> Self { + Self { params, results } + } +} + +impl<'a> From<&'a ArchivedFunctionType> for FunctionTypeRef<'a> { + fn from(ArchivedFunctionType { params, results }: &'a ArchivedFunctionType) -> Self { + Self { params: &**params, results: &**results } + } +} + +/// Indicator of whether a global is mutable or not +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, +)] +#[archive(as = "Self")] +pub enum Mutability { + /// The global is constant and its value does not change + Const, + /// The value of the global can change over time + Var, +} + +impl Mutability { + /// Returns a boolean indicating if the enum is set to mutable. + pub fn is_mutable(self) -> bool { + match self { + Self::Const => false, + Self::Var => true, + } + } +} + +/// WebAssembly global. +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, +)] +#[archive(as = "Self")] +pub struct GlobalType { + /// The type of the value stored in the global. + pub ty: Type, + /// A flag indicating whether the value may change at runtime. + pub mutability: Mutability, +} + +// Global Types + +/// A WebAssembly global descriptor. +/// +/// This type describes an instance of a global in a WebAssembly +/// module. Globals are local to an `Instance` and are either +/// immutable or mutable. +impl GlobalType { + /// Create a new Global variable + /// # Usage: + /// ``` + /// use unc_vm_types::{GlobalType, Type, Mutability, Value}; + /// + /// // An I32 constant global + /// let global = GlobalType::new(Type::I32, Mutability::Const); + /// // An I64 mutable global + /// let global = GlobalType::new(Type::I64, Mutability::Var); + /// ``` + pub fn new(ty: Type, mutability: Mutability) -> Self { + Self { ty, mutability } + } +} + +impl fmt::Display for GlobalType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mutability = match self.mutability { + Mutability::Const => "constant", + Mutability::Var => "mutable", + }; + write!(f, "{} ({})", self.ty, mutability) + } +} + +/// Globals are initialized via the `const` operators or by referring to another import. +#[derive(Debug, Clone, Copy, PartialEq, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +#[archive(as = "Self")] +pub enum GlobalInit { + /// An `i32.const`. + I32Const(i32), + /// An `i64.const`. + I64Const(i64), + /// An `f32.const`. + F32Const(f32), + /// An `f64.const`. + F64Const(f64), + /// A `v128.const`. + V128Const(V128), + /// A `global.get` of another global. + GetGlobal(GlobalIndex), + // TODO(reftypes): `ref.null func` and `ref.null extern` seem to be 2 different + // things: we need to handle both. Perhaps this handled in context by the + // global knowing its own type? + /// A `ref.null`. + RefNullConst, + /// A `ref.func `. + RefFunc(FunctionIndex), +} + +impl Eq for GlobalInit {} + +impl GlobalInit { + /// Get the `GlobalInit` from a given `Value` + pub fn from_value(value: Value) -> Self { + match value { + Value::I32(i) => Self::I32Const(i), + Value::I64(i) => Self::I64Const(i), + Value::F32(f) => Self::F32Const(f), + Value::F64(f) => Self::F64Const(f), + _ => unimplemented!("GlobalInit from_value for {:?}", value), + } + } + /// Get the `Value` from the Global init value + pub fn to_value(&self) -> Value { + match self { + Self::I32Const(i) => Value::I32(*i), + Self::I64Const(i) => Value::I64(*i), + Self::F32Const(f) => Value::F32(*f), + Self::F64Const(f) => Value::F64(*f), + _ => unimplemented!("GlobalInit to_value for {:?}", self), + } + } +} + +// Table Types + +/// A descriptor for a table in a WebAssembly module. +/// +/// Tables are contiguous chunks of a specific element, typically a `funcref` or +/// an `externref`. The most common use for tables is a function table through +/// which `call_indirect` can invoke other functions. +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, +)] +pub struct TableType { + /// The type of data stored in elements of the table. + pub ty: Type, + /// The minimum number of elements in the table. + pub minimum: u32, + /// The maximum number of elements in the table. + pub maximum: Option, +} + +impl TableType { + /// Creates a new table descriptor which will contain the specified + /// `element` and have the `limits` applied to its length. + pub fn new(ty: Type, minimum: u32, maximum: Option) -> Self { + Self { ty, minimum, maximum } + } +} + +impl fmt::Display for TableType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(maximum) = self.maximum { + write!(f, "{} ({}..{})", self.ty, self.minimum, maximum) + } else { + write!(f, "{} ({}..)", self.ty, self.minimum) + } + } +} + +// Memory Types + +/// A descriptor for a WebAssembly memory type. +/// +/// Memories are described in units of pages (64KB) and represent contiguous +/// chunks of addressable memory. +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, +)] +pub struct MemoryType { + /// The minimum number of pages in the memory. + pub minimum: Pages, + /// The maximum number of pages in the memory. + pub maximum: Option, + /// Whether the memory may be shared between multiple threads. + pub shared: bool, +} + +impl MemoryType { + /// Creates a new descriptor for a WebAssembly memory given the specified + /// limits of the memory. + pub fn new(minimum: IntoPages, maximum: Option, shared: bool) -> Self + where + IntoPages: Into, + { + Self { minimum: minimum.into(), maximum: maximum.map(Into::into), shared } + } +} + +impl fmt::Display for MemoryType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let shared = if self.shared { "shared" } else { "not shared" }; + if let Some(maximum) = self.maximum { + write!(f, "{} ({:?}..{:?})", shared, self.minimum, maximum) + } else { + write!(f, "{} ({:?}..)", shared, self.minimum) + } + } +} + +// Import Types + +/// A descriptor for an imported value into a wasm module. +/// +/// This type is primarily accessed from the `Module::imports` +/// API. Each `ImportType` describes an import into the wasm module +/// with the module/name that it's imported from as well as the type +/// of item that's being imported. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Import { + module: S, + name: S, + index: u32, + ty: T, +} + +impl, T> Import { + /// Creates a new import descriptor which comes from `module` and `name` and + /// is of type `ty`. + pub fn new(module: S, name: S, index: u32, ty: T) -> Self { + Self { module, name, index, ty } + } + + /// Returns the module name that this import is expected to come from. + pub fn module(&self) -> &str { + self.module.as_ref() + } + + /// Returns the field name of the module that this import is expected to + /// come from. + pub fn name(&self) -> &str { + self.name.as_ref() + } + + /// The index of the import in the module. + pub fn index(&self) -> u32 { + self.index + } + + /// Returns the expected type of this import. + pub fn ty(&self) -> &T { + &self.ty + } +} + +// Export Types + +/// A descriptor for an exported WebAssembly value. +/// +/// This type is primarily accessed from the `Module::exports` +/// accessor and describes what names are exported from a wasm module +/// and the type of the item that is exported. +/// +/// The `` refefers to `ExternType`, however it can also refer to use +/// `MemoryType`, `TableType`, `FunctionType` and `GlobalType` for ease of +/// use. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ExportType { + name: String, + ty: T, +} + +impl ExportType { + /// Creates a new export which is exported with the given `name` and has the + /// given `ty`. + pub fn new(name: &str, ty: T) -> Self { + Self { name: name.to_string(), ty } + } + + /// Returns the name by which this export is known by. + pub fn name(&self) -> &str { + &self.name + } + + /// Returns the type of this export. + pub fn ty(&self) -> &T { + &self.ty + } +} + +/// Fast gas counter with very simple structure, could be exposed to compiled code in the VM. For +/// instance by intrinsifying host functions responsible for gas metering. + +#[repr(C)] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FastGasCounter { + /// The following three fields must be put next to another to make sure + /// generated gas counting code can use and adjust them. + /// We will share counter to ensure we never miss synchronization. + /// This could change and in such a case synchronization required between compiled WASM code + /// and the host code. + + /// The amount of gas that was irreversibly used for contract execution. + pub burnt_gas: u64, + /// Hard gas limit for execution + pub gas_limit: u64, +} + +impl FastGasCounter { + /// New fast gas counter. + pub fn new(limit: u64) -> Self { + Self { burnt_gas: 0, gas_limit: limit } + } + /// Amount of gas burnt, maybe load as atomic to avoid aliasing issues. + pub fn burnt(&self) -> u64 { + self.burnt_gas + } +} + +impl fmt::Display for FastGasCounter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "burnt: {} limit: {} ", self.burnt(), self.gas_limit,) + } +} + +/// External configuration of execution environment for Instance. +#[derive(Clone)] +pub struct InstanceConfig { + /// External gas counter pointer. + pub gas_counter: *mut FastGasCounter, + default_gas_counter: Option>>, + /// Stack limit, in 8-byte slots. + pub stack_limit: u32, +} + +impl InstanceConfig { + /// Create default instance configuration. + pub fn with_stack_limit(stack_limit: u32) -> Self { + let result = Rc::new(UnsafeCell::new(FastGasCounter { burnt_gas: 0, gas_limit: u64::MAX })); + Self { gas_counter: result.get(), default_gas_counter: Some(result), stack_limit } + } + + /// Create instance configuration with an external gas counter, unsafe as it creates + /// an alias on raw memory of gas_counter. This memory could be accessed until + /// instance configured with this `InstanceConfig` exists. + pub unsafe fn with_counter(mut self, gas_counter: *mut FastGasCounter) -> Self { + self.gas_counter = gas_counter; + self.default_gas_counter = None; + self + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const VOID_TO_VOID: ([Type; 0], [Type; 0]) = ([], []); + const I32_I32_TO_VOID: ([Type; 2], [Type; 0]) = ([Type::I32, Type::I32], []); + const V128_I64_TO_I32: ([Type; 2], [Type; 1]) = ([Type::V128, Type::I64], [Type::I32]); + const NINE_V128_TO_NINE_I32: ([Type; 9], [Type; 9]) = ([Type::V128; 9], [Type::I32; 9]); + + #[test] + fn convert_tuple_to_functiontype() { + let ty: FunctionType = VOID_TO_VOID.into(); + assert_eq!(ty.params().len(), 0); + assert_eq!(ty.results().len(), 0); + + let ty: FunctionType = I32_I32_TO_VOID.into(); + assert_eq!(ty.params().len(), 2); + assert_eq!(ty.params()[0], Type::I32); + assert_eq!(ty.params()[1], Type::I32); + assert_eq!(ty.results().len(), 0); + + let ty: FunctionType = V128_I64_TO_I32.into(); + assert_eq!(ty.params().len(), 2); + assert_eq!(ty.params()[0], Type::V128); + assert_eq!(ty.params()[1], Type::I64); + assert_eq!(ty.results().len(), 1); + assert_eq!(ty.results()[0], Type::I32); + + let ty: FunctionType = NINE_V128_TO_NINE_I32.into(); + assert_eq!(ty.params().len(), 9); + assert_eq!(ty.results().len(), 9); + } +} diff --git a/runtime/unc-vm/types/src/units.rs b/runtime/unc-vm/types/src/units.rs new file mode 100644 index 000000000..09d5741da --- /dev/null +++ b/runtime/unc-vm/types/src/units.rs @@ -0,0 +1,186 @@ +use crate::lib::std::convert::TryFrom; +use crate::lib::std::fmt; +use crate::lib::std::ops::{Add, Sub}; +use std::convert::TryInto; +use thiserror::Error; + +/// WebAssembly page sizes are fixed to be 64KiB. +/// Note: large page support may be added in an opt-in manner in the [future]. +/// +/// [future]: https://webassembly.org/docs/future-features/#large-page-support +pub const WASM_PAGE_SIZE: usize = 0x10000; + +/// The number of pages we can have before we run out of byte index space. +pub const WASM_MAX_PAGES: u32 = 0x10000; + +/// The minimum number of pages allowed. +pub const WASM_MIN_PAGES: u32 = 0x100; + +/// Units of WebAssembly pages (as specified to be 65,536 bytes). +#[derive( + Copy, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, +)] +#[archive(as = "Self")] +#[repr(transparent)] +pub struct Pages(pub u32); + +impl Pages { + /// Returns the largest value that can be represented by the Pages type. + /// + /// This is defined by the WebAssembly standard as 65,536 pages. + #[inline(always)] + pub const fn max_value() -> Self { + Self(WASM_MAX_PAGES) + } + + /// Checked addition. Computes `self + rhs`, + /// returning `None` if overflow occurred. + pub fn checked_add(self, rhs: Self) -> Option { + let added = (self.0 as usize) + (rhs.0 as usize); + if added <= (WASM_MAX_PAGES as usize) { + Some(Self(added as u32)) + } else { + None + } + } + + /// Calculate number of bytes from pages. + pub fn bytes(self) -> Bytes { + self.into() + } +} + +impl fmt::Debug for Pages { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} pages", self.0) + } +} + +impl From for Pages { + fn from(other: u32) -> Self { + Self(other) + } +} + +/// Units of WebAssembly memory in terms of 8-bit bytes. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Bytes(pub usize); + +impl fmt::Debug for Bytes { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} bytes", self.0) + } +} + +impl From for Bytes { + fn from(pages: Pages) -> Self { + Self((pages.0 as usize) * WASM_PAGE_SIZE) + } +} + +impl From for Bytes { + fn from(other: usize) -> Self { + Self(other) + } +} + +impl From for Bytes { + fn from(other: u32) -> Self { + Self(other.try_into().unwrap()) + } +} + +impl Sub for Pages +where + T: Into, +{ + type Output = Self; + fn sub(self, rhs: T) -> Self { + Self(self.0 - rhs.into().0) + } +} + +impl Add for Pages +where + T: Into, +{ + type Output = Self; + fn add(self, rhs: T) -> Self { + Self(self.0 + rhs.into().0) + } +} + +/// The only error that can happen when converting `Bytes` to `Pages` +#[derive(Debug, Clone, Copy, PartialEq, Error)] +#[error("Number of pages exceeds uint32 range")] +pub struct PageCountOutOfRange; + +impl TryFrom for Pages { + type Error = PageCountOutOfRange; + + fn try_from(bytes: Bytes) -> Result { + let pages: u32 = (bytes.0 / WASM_PAGE_SIZE).try_into().or(Err(PageCountOutOfRange))?; + Ok(Self(pages)) + } +} + +impl Sub for Bytes +where + T: Into, +{ + type Output = Self; + fn sub(self, rhs: T) -> Self { + Self(self.0 - rhs.into().0) + } +} + +impl Add for Bytes +where + T: Into, +{ + type Output = Self; + fn add(self, rhs: T) -> Self { + Self(self.0 + rhs.into().0) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn convert_bytes_to_pages() { + // rounds down + let pages = Pages::try_from(Bytes(0)).unwrap(); + assert_eq!(pages, Pages(0)); + let pages = Pages::try_from(Bytes(1)).unwrap(); + assert_eq!(pages, Pages(0)); + let pages = Pages::try_from(Bytes(WASM_PAGE_SIZE - 1)).unwrap(); + assert_eq!(pages, Pages(0)); + let pages = Pages::try_from(Bytes(WASM_PAGE_SIZE)).unwrap(); + assert_eq!(pages, Pages(1)); + let pages = Pages::try_from(Bytes(WASM_PAGE_SIZE + 1)).unwrap(); + assert_eq!(pages, Pages(1)); + let pages = Pages::try_from(Bytes(28 * WASM_PAGE_SIZE + 42)).unwrap(); + assert_eq!(pages, Pages(28)); + let pages = Pages::try_from(Bytes((u32::MAX as usize) * WASM_PAGE_SIZE)).unwrap(); + assert_eq!(pages, Pages(u32::MAX)); + let pages = Pages::try_from(Bytes((u32::MAX as usize) * WASM_PAGE_SIZE + 1)).unwrap(); + assert_eq!(pages, Pages(u32::MAX)); + + // Errors when page count cannot be represented as u32 + let result = Pages::try_from(Bytes((u32::MAX as usize + 1) * WASM_PAGE_SIZE)); + assert_eq!(result.unwrap_err(), PageCountOutOfRange); + let result = Pages::try_from(Bytes(usize::MAX)); + assert_eq!(result.unwrap_err(), PageCountOutOfRange); + } +} diff --git a/runtime/unc-vm/types/src/values.rs b/runtime/unc-vm/types/src/values.rs new file mode 100644 index 000000000..cef4bfe5d --- /dev/null +++ b/runtime/unc-vm/types/src/values.rs @@ -0,0 +1,476 @@ +use crate::extern_ref::ExternRef; +use crate::lib::std::convert::TryFrom; +use crate::lib::std::fmt; +use crate::lib::std::ptr; +use crate::lib::std::string::{String, ToString}; +use crate::types::Type; + +/// Possible runtime values that a WebAssembly module can either consume or +/// produce. +#[derive(Clone, PartialEq)] +pub enum Value { + /// A 32-bit integer. + /// + /// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned. + I32(i32), + + /// A 64-bit integer. + /// + /// In Wasm integers are sign-agnostic, i.e. this can either be signed or unsigned. + I64(i64), + + /// A 32-bit float. + F32(f32), + + /// A 64-bit float. + F64(f64), + + /// An `externref` value which can hold opaque data to the wasm instance itself. + /// + /// Note that this is a nullable value as well. + ExternRef(ExternRef), + + /// A first-class reference to a WebAssembly function. + FuncRef(Option), + + /// A 128-bit number + V128(u128), +} + +macro_rules! accessors { + ($bind:ident $(($variant:ident($ty:ty) $get:ident $unwrap:ident $cvt:expr))*) => ($( + /// Attempt to access the underlying value of this `Value`, returning + /// `None` if it is not the correct type. + pub fn $get(&self) -> Option<$ty> { + if let Self::$variant($bind) = self { + Some($cvt) + } else { + None + } + } + + /// Returns the underlying value of this `Value`, panicking if it's the + /// wrong type. + /// + /// # Panics + /// + /// Panics if `self` is not of the right type. + pub fn $unwrap(&self) -> $ty { + self.$get().expect(concat!("expected ", stringify!($ty))) + } + )*) +} + +/// Trait for reading and writing Wasm values into binary for use on the layer +/// between the API and the VM internals, specifically with `unc_vm_types::Value`. +pub trait WasmValueType: std::fmt::Debug + 'static { + /// Write the value + unsafe fn write_value_to(&self, p: *mut i128); + + /// read the value + // TODO(reftypes): passing the store as `dyn Any` is a hack to work around the + // structure of our crates. We need to talk about the store in the rest of the + // VM (for example where this method is used) but cannot do so. Fixing this + // may be non-trivial. + unsafe fn read_value_from(store: &dyn std::any::Any, p: *const i128) -> Self; +} + +impl WasmValueType for () { + unsafe fn write_value_to(&self, _p: *mut i128) {} + + unsafe fn read_value_from(_store: &dyn std::any::Any, _p: *const i128) -> Self { + () + } +} + +impl Value +where + T: WasmValueType, +{ + /// Returns a null `externref` value. + pub fn null() -> Self { + Self::ExternRef(ExternRef::null()) + } + + /// Returns the corresponding [`Type`] for this `Value`. + pub fn ty(&self) -> Type { + match self { + Self::I32(_) => Type::I32, + Self::I64(_) => Type::I64, + Self::F32(_) => Type::F32, + Self::F64(_) => Type::F64, + Self::ExternRef(_) => Type::ExternRef, + Self::FuncRef(_) => Type::FuncRef, + Self::V128(_) => Type::V128, + } + } + + /// Writes it's value to a given pointer + /// + /// # Safety + /// `p` must be: + /// - Sufficiently aligned for the Rust equivalent of the type in `self` + /// - Non-null and pointing to valid, mutable memory + pub unsafe fn write_value_to(&self, p: *mut i128) { + match self { + Self::I32(i) => ptr::write(p as *mut i32, *i), + Self::I64(i) => ptr::write(p as *mut i64, *i), + Self::F32(u) => ptr::write(p as *mut f32, *u), + Self::F64(u) => ptr::write(p as *mut f64, *u), + Self::V128(b) => ptr::write(p as *mut u128, *b), + Self::FuncRef(Some(b)) => T::write_value_to(b, p), + Self::FuncRef(None) => ptr::write(p as *mut usize, 0), + // TODO(reftypes): review clone here + Self::ExternRef(extern_ref) => ptr::write(p as *mut ExternRef, extern_ref.clone()), + } + } + + /// Gets a `Value` given a pointer and a `Type` + /// + /// # Safety + /// `p` must be: + /// - Properly aligned to the specified `ty`'s Rust equivalent + /// - Non-null and pointing to valid memory + pub unsafe fn read_value_from(store: &dyn std::any::Any, p: *const i128, ty: Type) -> Self { + match ty { + Type::I32 => Self::I32(ptr::read(p as *const i32)), + Type::I64 => Self::I64(ptr::read(p as *const i64)), + Type::F32 => Self::F32(ptr::read(p as *const f32)), + Type::F64 => Self::F64(ptr::read(p as *const f64)), + Type::V128 => Self::V128(ptr::read(p as *const u128)), + Type::FuncRef => { + // We do the null check ourselves + if (*(p as *const usize)) == 0 { + Self::FuncRef(None) + } else { + Self::FuncRef(Some(T::read_value_from(store, p))) + } + } + Type::ExternRef => { + let extern_ref = (&*(p as *const ExternRef)).clone(); + Self::ExternRef(extern_ref) + } + } + } + + accessors! { + e + (I32(i32) i32 unwrap_i32 *e) + (I64(i64) i64 unwrap_i64 *e) + (F32(f32) f32 unwrap_f32 *e) + (F64(f64) f64 unwrap_f64 *e) + (ExternRef(ExternRef) externref unwrap_externref e.clone()) + (FuncRef(&Option) funcref unwrap_funcref e) + (V128(u128) v128 unwrap_v128 *e) + } +} + +impl fmt::Debug for Value +where + T: WasmValueType, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::I32(v) => write!(f, "I32({:?})", v), + Self::I64(v) => write!(f, "I64({:?})", v), + Self::F32(v) => write!(f, "F32({:?})", v), + Self::F64(v) => write!(f, "F64({:?})", v), + Self::ExternRef(v) => write!(f, "ExternRef({:?})", v), + Self::FuncRef(None) => write!(f, "Null FuncRef"), + Self::FuncRef(Some(v)) => write!(f, "FuncRef({:?})", v), + Self::V128(v) => write!(f, "V128({:?})", v), + } + } +} + +impl ToString for Value +where + T: WasmValueType, +{ + fn to_string(&self) -> String { + match self { + Self::I32(v) => v.to_string(), + Self::I64(v) => v.to_string(), + Self::F32(v) => v.to_string(), + Self::F64(v) => v.to_string(), + Self::ExternRef(_) => "externref".to_string(), + Self::FuncRef(_) => "funcref".to_string(), + Self::V128(v) => v.to_string(), + } + } +} + +impl From for Value +where + T: WasmValueType, +{ + fn from(val: i32) -> Self { + Self::I32(val) + } +} + +impl From for Value +where + T: WasmValueType, +{ + fn from(val: u32) -> Self { + // In Wasm integers are sign-agnostic, so i32 is basically a 4 byte storage we can use for signed or unsigned 32-bit integers. + Self::I32(val as i32) + } +} + +impl From for Value +where + T: WasmValueType, +{ + fn from(val: i64) -> Self { + Self::I64(val) + } +} + +impl From for Value +where + T: WasmValueType, +{ + fn from(val: u64) -> Self { + // In Wasm integers are sign-agnostic, so i64 is basically an 8 byte storage we can use for signed or unsigned 64-bit integers. + Self::I64(val as i64) + } +} + +impl From for Value +where + T: WasmValueType, +{ + fn from(val: f32) -> Self { + Self::F32(val) + } +} + +impl From for Value +where + T: WasmValueType, +{ + fn from(val: f64) -> Self { + Self::F64(val) + } +} + +impl From for Value +where + T: WasmValueType, +{ + fn from(val: ExternRef) -> Self { + Self::ExternRef(val) + } +} + +// impl From for Value { +// fn from(val: T) -> Self { +// Self::FuncRef(val) +// } +// } + +const NOT_I32: &str = "Value is not of Wasm type i32"; +const NOT_I64: &str = "Value is not of Wasm type i64"; +const NOT_F32: &str = "Value is not of Wasm type f32"; +const NOT_F64: &str = "Value is not of Wasm type f64"; + +impl TryFrom> for i32 +where + T: WasmValueType, +{ + type Error = &'static str; + + fn try_from(value: Value) -> Result { + value.i32().ok_or(NOT_I32) + } +} + +impl TryFrom> for u32 +where + T: WasmValueType, +{ + type Error = &'static str; + + fn try_from(value: Value) -> Result { + value.i32().ok_or(NOT_I32).map(|int| int as Self) + } +} + +impl TryFrom> for i64 +where + T: WasmValueType, +{ + type Error = &'static str; + + fn try_from(value: Value) -> Result { + value.i64().ok_or(NOT_I64) + } +} + +impl TryFrom> for u64 +where + T: WasmValueType, +{ + type Error = &'static str; + + fn try_from(value: Value) -> Result { + value.i64().ok_or(NOT_I64).map(|int| int as Self) + } +} + +impl TryFrom> for f32 +where + T: WasmValueType, +{ + type Error = &'static str; + + fn try_from(value: Value) -> Result { + value.f32().ok_or(NOT_F32) + } +} + +impl TryFrom> for f64 +where + T: WasmValueType, +{ + type Error = &'static str; + + fn try_from(value: Value) -> Result { + value.f64().ok_or(NOT_F64) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_value_i32_from_u32() { + let bytes = [0x00, 0x00, 0x00, 0x00]; + let v = Value::<()>::from(u32::from_be_bytes(bytes)); + assert_eq!(v, Value::I32(i32::from_be_bytes(bytes))); + + let bytes = [0x00, 0x00, 0x00, 0x01]; + let v = Value::<()>::from(u32::from_be_bytes(bytes)); + assert_eq!(v, Value::I32(i32::from_be_bytes(bytes))); + + let bytes = [0xAA, 0xBB, 0xCC, 0xDD]; + let v = Value::<()>::from(u32::from_be_bytes(bytes)); + assert_eq!(v, Value::I32(i32::from_be_bytes(bytes))); + + let bytes = [0xFF, 0xFF, 0xFF, 0xFF]; + let v = Value::<()>::from(u32::from_be_bytes(bytes)); + assert_eq!(v, Value::I32(i32::from_be_bytes(bytes))); + } + + #[test] + fn test_value_i64_from_u64() { + let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + let v = Value::<()>::from(u64::from_be_bytes(bytes)); + assert_eq!(v, Value::I64(i64::from_be_bytes(bytes))); + + let bytes = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]; + let v = Value::<()>::from(u64::from_be_bytes(bytes)); + assert_eq!(v, Value::I64(i64::from_be_bytes(bytes))); + + let bytes = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11]; + let v = Value::<()>::from(u64::from_be_bytes(bytes)); + assert_eq!(v, Value::I64(i64::from_be_bytes(bytes))); + + let bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; + let v = Value::<()>::from(u64::from_be_bytes(bytes)); + assert_eq!(v, Value::I64(i64::from_be_bytes(bytes))); + } + + #[test] + fn convert_value_to_i32() { + let value = Value::<()>::I32(5678); + let result = i32::try_from(value); + assert_eq!(result.unwrap(), 5678); + + let value = Value::<()>::from(u32::MAX); + let result = i32::try_from(value); + assert_eq!(result.unwrap(), -1); + + let value = Value::<()>::V128(42); + let result = i32::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type i32"); + } + + #[test] + fn convert_value_to_u32() { + let value = Value::<()>::from(u32::MAX); + let result = u32::try_from(value); + assert_eq!(result.unwrap(), u32::MAX); + + let value = Value::<()>::I32(-1); + let result = u32::try_from(value); + assert_eq!(result.unwrap(), u32::MAX); + + let value = Value::<()>::V128(42); + let result = u32::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type i32"); + } + + #[test] + fn convert_value_to_i64() { + let value = Value::<()>::I64(5678); + let result = i64::try_from(value); + assert_eq!(result.unwrap(), 5678); + + let value = Value::<()>::from(u64::MAX); + let result = i64::try_from(value); + assert_eq!(result.unwrap(), -1); + + let value = Value::<()>::V128(42); + let result = i64::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type i64"); + } + + #[test] + fn convert_value_to_u64() { + let value = Value::<()>::from(u64::MAX); + let result = u64::try_from(value); + assert_eq!(result.unwrap(), u64::MAX); + + let value = Value::<()>::I64(-1); + let result = u64::try_from(value); + assert_eq!(result.unwrap(), u64::MAX); + + let value = Value::<()>::V128(42); + let result = u64::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type i64"); + } + + #[test] + fn convert_value_to_f32() { + let value = Value::<()>::F32(1.234); + let result = f32::try_from(value); + assert_eq!(result.unwrap(), 1.234); + + let value = Value::<()>::V128(42); + let result = f32::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type f32"); + + let value = Value::<()>::F64(1.234); + let result = f32::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type f32"); + } + + #[test] + fn convert_value_to_f64() { + let value = Value::<()>::F64(1.234); + let result = f64::try_from(value); + assert_eq!(result.unwrap(), 1.234); + + let value = Value::<()>::V128(42); + let result = f64::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type f64"); + + let value = Value::<()>::F32(1.234); + let result = f64::try_from(value); + assert_eq!(result.unwrap_err(), "Value is not of Wasm type f64"); + } +} diff --git a/runtime/unc-vm/types/tests/partial-sum-map/corpus/.gitkeep b/runtime/unc-vm/types/tests/partial-sum-map/corpus/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/runtime/unc-vm/types/tests/partial-sum-map/crashes/.gitkeep b/runtime/unc-vm/types/tests/partial-sum-map/crashes/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/runtime/unc-vm/types/tests/partial-sum-map/main.rs b/runtime/unc-vm/types/tests/partial-sum-map/main.rs new file mode 100644 index 000000000..38b6266db --- /dev/null +++ b/runtime/unc-vm/types/tests/partial-sum-map/main.rs @@ -0,0 +1,31 @@ +use unc_vm_types::partial_sum_map::{Error, PartialSumMap}; + +#[test] +fn partial_sum_map_fuzzer() { + bolero::check!().with_type::<(Vec<(u32, u32)>, Vec)>().for_each(|input| { + let adds = &input.0; + let tests = &input.1; + let mut psm = PartialSumMap::new(); + let mut v = Vec::new(); + let mut size: u32 = 0; + for a in adds { + let push_res = psm.push(a.0, a.1); + match size.checked_add(a.0) { + None => { + assert_eq!(push_res, Err(Error::Overflow)); + continue; + } + Some(new_size) => { + assert_eq!(push_res, Ok(())); + size = new_size; + } + } + v.push(((size - a.0)..size, a.1)); + } + for t in tests { + let psm_answer = psm.find(*t); + let iset_answer = v.iter().find(|(r, _)| r.contains(&t)).map(|(_, v)| v); + assert_eq!(psm_answer, iset_answer); + } + }); +} diff --git a/runtime/unc-vm/vm/Cargo.toml b/runtime/unc-vm/vm/Cargo.toml new file mode 100644 index 000000000..25b53cedb --- /dev/null +++ b/runtime/unc-vm/vm/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "unc-vm-vm" +version.workspace = true +description = "Runtime library support for Wasmer" +categories = ["wasm"] +keywords = ["wasm", "webassembly"] +authors = ["Wasmer Engineering Team ", "Hello Inc "] +repository.workspace = true +license = "MIT OR Apache-2.0 WITH LLVM-exception" +readme = "README.md" +edition = "2021" +publish = true + +[lints] +workspace = true + +[dependencies] +backtrace.workspace = true +cfg-if.workspace = true +finite-wasm.workspace = true +indexmap.workspace = true +libc.workspace = true +memoffset.workspace = true +more-asserts.workspace = true +region.workspace = true +rkyv.workspace = true +thiserror.workspace = true +tracing.workspace = true +unc-vm-types.workspace = true +wasmparser = "0.99.0" + +[target.'cfg(target_os = "windows")'.dependencies] +winapi.workspace = true + +[build-dependencies] +cc.workspace = true + +[badges] +maintenance = { status = "actively-developed" } + +[features] +default = [] diff --git a/runtime/unc-vm/vm/LICENSE b/runtime/unc-vm/vm/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/runtime/unc-vm/vm/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/runtime/unc-vm/vm/README.md b/runtime/unc-vm/vm/README.md new file mode 100644 index 000000000..1fab741d0 --- /dev/null +++ b/runtime/unc-vm/vm/README.md @@ -0,0 +1,28 @@ +# `unc-vm-vm` + +This crate is a fork of `wasmer-vm`. A significant number of things changed, but the documentation is not up-to-date yet. + +This crate contains the Wasmer VM runtime library, supporting the Wasm ABI used by any [`wasmer-engine`] implementation. + +The Wasmer runtime is modular by design, and provides several +libraries where each of them provides a specific set of features. This +`wasmer-vm` library contains the low-level foundation for the runtime +itself. + +It provides all the APIs the +[`wasmer-engine`](https://crates.io/crates/wasmer-engine) needs to operate, +from the `instance`, to `memory`, `probestack`, signature registry, `trap`, +`table`, `VMContext`, `libcalls` etc. + +It is very unlikely that a user will need to deal with `wasmer-vm` +directly. The `wasmer` crate provides types that embed types from +`wasmer-vm` with a higher-level API. + + +[`wasmer-engine`]: https://crates.io/crates/wasmer-engine + +### Acknowledgments + +This project borrowed some of the code for the VM structure and trapping from the [wasmtime-runtime](https://crates.io/crates/wasmtime-runtime). + +Please check [Wasmer ATTRIBUTIONS](https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md) to further see licenses and other attributions of the project. diff --git a/runtime/unc-vm/vm/build.rs b/runtime/unc-vm/vm/build.rs new file mode 100644 index 000000000..e38ad66a2 --- /dev/null +++ b/runtime/unc-vm/vm/build.rs @@ -0,0 +1,16 @@ +//! Runtime build script compiles C code using setjmp for trap handling. + +use std::env; + +fn main() { + println!("cargo:rerun-if-changed=src/trap/handlers.c"); + + cc::Build::new() + .warnings(true) + .define( + &format!("CFG_TARGET_OS_{}", env::var("CARGO_CFG_TARGET_OS").unwrap().to_uppercase()), + None, + ) + .file("src/trap/handlers.c") + .compile("handlers"); +} diff --git a/runtime/unc-vm/vm/src/artifact.rs b/runtime/unc-vm/vm/src/artifact.rs new file mode 100644 index 000000000..a877bb755 --- /dev/null +++ b/runtime/unc-vm/vm/src/artifact.rs @@ -0,0 +1,70 @@ +use crate::{InstanceHandle, Resolver, Tunables, VMLocalFunction, VMSharedSignatureIndex}; +use unc_vm_types::{ + entity::BoxedSlice, ElemIndex, FunctionIndex, GlobalInit, GlobalType, ImportCounts, + InstanceConfig, LocalFunctionIndex, OwnedDataInitializer, OwnedTableInitializer, +}; +use std::{any::Any, collections::BTreeMap, sync::Arc}; + +/// [`Artifact`]s that can be instantiated. +pub trait Instantiatable: Artifact { + /// The errors that can occur when instantiating. + type Error: std::error::Error + Send; + + /// Crate an `Instance` from this `Artifact`. + /// + /// # Safety + /// + /// See [`InstanceHandle::new`]. + unsafe fn instantiate( + self: Arc, + tunables: &dyn Tunables, + resolver: &dyn Resolver, + host_state: Box, + config: InstanceConfig, + ) -> Result; +} + +/// A predecesor of a full module Instance. +/// +/// This type represents parts of a compiled WASM module ([`Executable`](crate::Executable)) that +/// are pre-allocated in within some Engine's store. +/// +/// Some other operations such as linking, relocating and similar may also be performed during +/// constructon of the Artifact, making this type particularly well suited for caching in-memory. +pub trait Artifact: Send { + /// The information about offsets into the VM context table. + fn offsets(&self) -> &crate::VMOffsets; + + /// The count of imported entities. + fn import_counts(&self) -> &ImportCounts; + + /// The locally defined functions. + /// + /// These are published and ready to call. + fn functions(&self) -> &BoxedSlice; + + /// Passive table elements. + fn passive_elements(&self) -> &BTreeMap>; + + /// Table initializers. + fn element_segments(&self) -> &[OwnedTableInitializer]; + + /// Memory initializers. + /// TODO: consider making it an iterator of `DataInitializer`s instead? + fn data_segments(&self) -> &[OwnedDataInitializer]; + + /// Passive table elements. + fn globals(&self) -> &[(GlobalType, GlobalInit)]; + + /// The function index to the start function. + fn start_function(&self) -> Option; + + /// Function by export name. + fn export_field(&self, name: &str) -> Option; + + /// Mapping between module SignatureIndex and VMSharedSignatureIndex. + fn signatures(&self) -> &[VMSharedSignatureIndex]; + + /// Obtain the function signature for either the import or local definition. + fn function_signature(&self, index: FunctionIndex) -> Option; +} diff --git a/runtime/unc-vm/vm/src/export.rs b/runtime/unc-vm/vm/src/export.rs new file mode 100644 index 000000000..a4fe25e0f --- /dev/null +++ b/runtime/unc-vm/vm/src/export.rs @@ -0,0 +1,235 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +use crate::global::Global; +use crate::instance::WeakOrStrongInstanceRef; +use crate::memory::{Memory, MemoryStyle}; +use crate::table::{Table, TableStyle}; +use crate::vmcontext::{VMFunctionBody, VMFunctionEnvironment, VMFunctionKind, VMTrampoline}; +use crate::VMSharedSignatureIndex; +use unc_vm_types::{MemoryType, TableType}; +use std::sync::Arc; + +/// The value of an export passed from one instance to another. +#[derive(Debug)] +pub enum VMExtern { + /// A function export value. + Function(VMFunction), + + /// A table export value. + Table(VMTable), + + /// A memory export value. + Memory(VMMemory), + + /// A global export value. + Global(VMGlobal), +} + +/// A function export value. +#[derive(Clone, Debug, PartialEq)] +pub struct VMFunction { + /// The address of the native-code function. + pub address: *const VMFunctionBody, + + /// Pointer to the containing `VMContext`. + pub vmctx: VMFunctionEnvironment, + + /// The function type, used for compatibility checking. + pub signature: VMSharedSignatureIndex, + + /// The function kind (specifies the calling convention for the + /// function). + pub kind: VMFunctionKind, + + /// Address of the function call trampoline owned by the same + /// VMContext that owns the VMFunctionBody. + /// + /// May be `None` when the function is a host function (`FunctionType` + /// == `Dynamic` or `vmctx` == `nullptr`). + pub call_trampoline: Option, + + /// A “reference” to the instance through the + /// `InstanceRef`. `None` if it is a host function. + pub instance_ref: Option, +} + +impl VMFunction { + /// Converts the stored instance ref into a strong `InstanceRef` if it is weak. + /// Returns None if it cannot be upgraded. + pub fn upgrade_instance_ref(&mut self) -> Option<()> { + if let Some(ref mut ir) = self.instance_ref { + *ir = ir.upgrade()?; + } + Some(()) + } +} + +/// # Safety +/// There is no non-threadsafe logic directly in this type. Calling the function +/// may not be threadsafe. +unsafe impl Send for VMFunction {} +/// # Safety +/// The members of an VMFunction are immutable after construction. +unsafe impl Sync for VMFunction {} + +impl From for VMExtern { + fn from(func: VMFunction) -> Self { + Self::Function(func) + } +} + +/// A table export value. +#[derive(Clone, Debug)] +pub struct VMTable { + /// Pointer to the containing `Table`. + pub from: Arc, + + /// A “reference” to the instance through the + /// `InstanceRef`. `None` if it is a host table. + pub instance_ref: Option, +} + +/// # Safety +/// This is correct because there is no non-threadsafe logic directly in this type; +/// correct use of the raw table from multiple threads via `definition` requires `unsafe` +/// and is the responsibilty of the user of this type. +unsafe impl Send for VMTable {} + +/// # Safety +/// This is correct because the values directly in `definition` should be considered immutable +/// and the type is both `Send` and `Clone` (thus marking it `Sync` adds no new behavior, it +/// only makes this type easier to use) +unsafe impl Sync for VMTable {} + +impl VMTable { + /// Get the table type for this exported table + pub fn ty(&self) -> &TableType { + self.from.ty() + } + + /// Get the style for this exported table + pub fn style(&self) -> &TableStyle { + self.from.style() + } + + /// Returns whether or not the two `VMTable`s refer to the same Memory. + pub fn same(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.from, &other.from) + } + + /// Converts the stored instance ref into a strong `InstanceRef` if it is weak. + /// Returns None if it cannot be upgraded. + pub fn upgrade_instance_ref(&mut self) -> Option<()> { + if let Some(ref mut ir) = self.instance_ref { + *ir = ir.upgrade()?; + } + Some(()) + } +} + +impl From for VMExtern { + fn from(table: VMTable) -> Self { + Self::Table(table) + } +} + +/// A memory export value. +#[derive(Debug, Clone)] +pub struct VMMemory { + /// Pointer to the containing `Memory`. + pub from: Arc, + + /// A “reference” to the instance through the + /// `InstanceRef`. `None` if it is a host memory. + pub instance_ref: Option, +} + +/// # Safety +/// This is correct because there is no non-threadsafe logic directly in this type; +/// correct use of the raw memory from multiple threads via `definition` requires `unsafe` +/// and is the responsibilty of the user of this type. +unsafe impl Send for VMMemory {} + +/// # Safety +/// This is correct because the values directly in `definition` should be considered immutable +/// and the type is both `Send` and `Clone` (thus marking it `Sync` adds no new behavior, it +/// only makes this type easier to use) +unsafe impl Sync for VMMemory {} + +impl VMMemory { + /// Get the type for this exported memory + pub fn ty(&self) -> MemoryType { + self.from.ty() + } + + /// Get the style for this exported memory + pub fn style(&self) -> &MemoryStyle { + self.from.style() + } + + /// Returns whether or not the two `VMMemory`s refer to the same Memory. + pub fn same(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.from, &other.from) + } + + /// Converts the stored instance ref into a strong `InstanceRef` if it is weak. + /// Returns None if it cannot be upgraded. + pub fn upgrade_instance_ref(&mut self) -> Option<()> { + if let Some(ref mut ir) = self.instance_ref { + *ir = ir.upgrade()?; + } + Some(()) + } +} + +impl From for VMExtern { + fn from(memory: VMMemory) -> Self { + Self::Memory(memory) + } +} + +/// A global export value. +#[derive(Debug, Clone)] +pub struct VMGlobal { + /// The global declaration, used for compatibility checking. + pub from: Arc, + + /// A “reference” to the instance through the + /// `InstanceRef`. `None` if it is a host global. + pub instance_ref: Option, +} + +/// # Safety +/// This is correct because there is no non-threadsafe logic directly in this type; +/// correct use of the raw global from multiple threads via `definition` requires `unsafe` +/// and is the responsibilty of the user of this type. +unsafe impl Send for VMGlobal {} + +/// # Safety +/// This is correct because the values directly in `definition` should be considered immutable +/// from the perspective of users of this type and the type is both `Send` and `Clone` (thus +/// marking it `Sync` adds no new behavior, it only makes this type easier to use) +unsafe impl Sync for VMGlobal {} + +impl VMGlobal { + /// Returns whether or not the two `VMGlobal`s refer to the same Global. + pub fn same(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.from, &other.from) + } + + /// Converts the stored instance ref into a strong `InstanceRef` if it is weak. + /// Returns None if it cannot be upgraded. + pub fn upgrade_instance_ref(&mut self) -> Option<()> { + if let Some(ref mut ir) = self.instance_ref { + *ir = ir.upgrade()?; + } + Some(()) + } +} + +impl From for VMExtern { + fn from(global: VMGlobal) -> Self { + Self::Global(global) + } +} diff --git a/runtime/unc-vm/vm/src/func_data_registry.rs b/runtime/unc-vm/vm/src/func_data_registry.rs new file mode 100644 index 000000000..2a4405509 --- /dev/null +++ b/runtime/unc-vm/vm/src/func_data_registry.rs @@ -0,0 +1,114 @@ +//! A registry for `VMFuncRef`s. This allows us to deduplicate funcrefs so that +//! identical `VMCallerCheckedAnyfunc`s will give us identical funcrefs. +//! +//! This registry also helps ensure that the `VMFuncRef`s can stay valid for as +//! long as we need them to. + +use crate::vmcontext::VMCallerCheckedAnyfunc; +use std::collections::HashMap; +use std::sync::Mutex; + +/// The registry that holds the values that `VMFuncRef`s point to. +#[derive(Debug)] +pub struct FuncDataRegistry { + // This structure is stored in an `Engine` and is intended to be shared + // across many instances. Ideally instances can themselves be sent across + // threads, and ideally we can compile across many threads. As a result we + // use interior mutability here with a lock to avoid having callers to + // externally synchronize calls to compilation. + inner: Mutex, +} + +// We use raw pointers but the data never moves, so it's not a problem +unsafe impl Send for FuncDataRegistry {} +unsafe impl Sync for FuncDataRegistry {} + +/// A function reference. A single word that points to metadata about a function. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct VMFuncRef(pub(crate) *const VMCallerCheckedAnyfunc); + +impl unc_vm_types::NativeWasmType for VMFuncRef { + const WASM_TYPE: unc_vm_types::Type = unc_vm_types::Type::FuncRef; + type Abi = Self; + + #[inline] + fn from_abi(abi: Self::Abi) -> Self { + abi + } + + #[inline] + fn into_abi(self) -> Self::Abi { + self + } + + #[inline] + fn to_binary(self) -> i128 { + self.0 as _ + } + + #[inline] + fn from_binary(bits: i128) -> Self { + // TODO: ensure that the safety invariants are actually upheld here + Self(bits as _) + } +} + +impl VMFuncRef { + /// Check if the FuncRef is null + // TODO: make this const when `std::ptr::is_null` is const + pub fn is_null(&self) -> bool { + self.0.is_null() + } + + /// Create a new null FuncRef + pub const fn null() -> Self { + Self(std::ptr::null()) + } +} + +impl std::ops::Deref for VMFuncRef { + type Target = *const VMCallerCheckedAnyfunc; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for VMFuncRef { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +// We use raw pointers but the data never moves, so it's not a problem +// TODO: update docs +unsafe impl Send for VMFuncRef {} +unsafe impl Sync for VMFuncRef {} + +#[derive(Debug, Default)] +struct Inner { + func_data: Vec, + anyfunc_to_index: HashMap, +} + +impl FuncDataRegistry { + /// Create a new `FuncDataRegistry`. + pub fn new() -> Self { + Self { inner: Default::default() } + } + + /// Register a signature and return its unique index. + pub fn register(&self, anyfunc: VMCallerCheckedAnyfunc) -> VMFuncRef { + let mut inner = self.inner.lock().unwrap(); + let data = if let Some(&idx) = inner.anyfunc_to_index.get(&anyfunc) { + &inner.func_data[idx] + } else { + let idx = inner.func_data.len(); + inner.func_data.push(anyfunc); + inner.anyfunc_to_index.insert(anyfunc, idx); + &inner.func_data[idx] + }; + VMFuncRef(data) + } +} diff --git a/runtime/unc-vm/vm/src/global.rs b/runtime/unc-vm/vm/src/global.rs new file mode 100644 index 000000000..8a13fab23 --- /dev/null +++ b/runtime/unc-vm/vm/src/global.rs @@ -0,0 +1,136 @@ +use crate::vmcontext::VMGlobalDefinition; +use unc_vm_types::{GlobalType, Mutability, Type, Value, WasmValueType}; +use std::cell::UnsafeCell; +use std::ptr::NonNull; +use std::sync::Mutex; +use thiserror::Error; + +#[derive(Debug)] +/// A Global instance +pub struct Global { + ty: GlobalType, + // TODO: this box is unnecessary + vm_global_definition: Box>, + // used to synchronize gets/sets + lock: Mutex<()>, +} + +/// # Safety +/// This is safe to send between threads because there is no-thread specific logic. +/// TODO: look into other reasons that make something not `Send` +unsafe impl Send for Global {} +/// # Safety +/// This is safe to share between threads because it uses a `Mutex` internally. +unsafe impl Sync for Global {} + +/// Error type describing things that can go wrong when operating on Wasm Globals. +#[derive(Error, Debug, Clone, PartialEq, Hash)] +pub enum GlobalError { + /// The error returned when attempting to set an immutable global. + #[error("Attempted to set an immutable global")] + ImmutableGlobalCannotBeSet, + + /// The error returned when attempting to operate on a global as a specific type + /// that differs from the global's own type. + #[error("Attempted to operate on a global of type {expected} as a global of type {found}")] + IncorrectType { + /// The type that the global is. + expected: Type, + /// The type that we were asked to use it as. + found: Type, + }, +} + +impl Global { + /// Create a new, zero bit-pattern initialized global from a [`GlobalType`]. + pub fn new(global_type: GlobalType) -> Self { + Self { + ty: global_type, + vm_global_definition: Box::new(UnsafeCell::new(VMGlobalDefinition::new())), + lock: Mutex::new(()), + } + } + + /// Get the type of the global. + pub fn ty(&self) -> &GlobalType { + &self.ty + } + + /// Get a pointer to the underlying definition used by the generated code. + pub fn vmglobal(&self) -> NonNull { + let ptr = self.vm_global_definition.as_ref() as *const UnsafeCell + as *const VMGlobalDefinition as *mut VMGlobalDefinition; + unsafe { NonNull::new_unchecked(ptr) } + } + + /// Get a value from the global. + // TODO(reftypes): the `&dyn Any` here for `Store` is a work-around for the fact + // that `Store` is defined in `API` when we need it earlier. Ideally this should + // be removed. + pub fn get(&self, store: &dyn std::any::Any) -> Value { + let _global_guard = self.lock.lock().unwrap(); + unsafe { + let definition = &*self.vm_global_definition.get(); + match self.ty().ty { + Type::I32 => Value::I32(definition.to_i32()), + Type::I64 => Value::I64(definition.to_i64()), + Type::F32 => Value::F32(definition.to_f32()), + Type::F64 => Value::F64(definition.to_f64()), + Type::V128 => Value::V128(definition.to_u128()), + Type::ExternRef => Value::ExternRef(definition.to_externref().into()), + Type::FuncRef => { + let p = definition.to_u128() as i128; + if p as usize == 0 { + Value::FuncRef(None) + } else { + let v = T::read_value_from(store, &p); + Value::FuncRef(Some(v)) + } + } + } + } + } + + /// Set a value for the global. + /// + /// # Safety + /// The caller should check that the `val` comes from the same store as this global. + pub unsafe fn set(&self, val: Value) -> Result<(), GlobalError> { + let _global_guard = self.lock.lock().unwrap(); + if self.ty().mutability != Mutability::Var { + return Err(GlobalError::ImmutableGlobalCannotBeSet); + } + if val.ty() != self.ty().ty { + return Err(GlobalError::IncorrectType { expected: self.ty.ty, found: val.ty() }); + } + self.set_unchecked(val) + } + + /// Set a value from the global (unchecked) + /// + /// # Safety + /// The caller should check that the `val` comes from the same store as this global. + /// The caller should also ensure that this global is synchronized. Otherwise, use + /// `set` instead. + pub unsafe fn set_unchecked(&self, val: Value) -> Result<(), GlobalError> { + // ideally we'd use atomics for the global value rather than needing to lock it + let definition = &mut *self.vm_global_definition.get(); + match val { + Value::I32(i) => *definition.as_i32_mut() = i, + Value::I64(i) => *definition.as_i64_mut() = i, + Value::F32(f) => *definition.as_f32_mut() = f, + Value::F64(f) => *definition.as_f64_mut() = f, + Value::V128(x) => *definition.as_bytes_mut() = x.to_ne_bytes(), + Value::ExternRef(r) => { + let extern_ref = definition.as_externref_mut(); + extern_ref.ref_drop(); + *extern_ref = r.into() + } + Value::FuncRef(None) => *definition.as_u128_mut() = 0, + Value::FuncRef(Some(r)) => { + r.write_value_to(definition.as_u128_mut() as *mut u128 as *mut i128) + } + } + Ok(()) + } +} diff --git a/runtime/unc-vm/vm/src/imports.rs b/runtime/unc-vm/vm/src/imports.rs new file mode 100644 index 000000000..ef3e3f625 --- /dev/null +++ b/runtime/unc-vm/vm/src/imports.rs @@ -0,0 +1,108 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +use crate::instance::ImportFunctionEnv; +use crate::vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport}; +use crate::{VMSharedSignatureIndex, VMTrampoline}; +use unc_vm_types::entity::{BoxedSlice, PrimaryMap}; +use unc_vm_types::{FunctionIndex, GlobalIndex, MemoryIndex, TableIndex}; + +/// Type of the import. +pub enum VMImportType { + /// A function import. + Function { + /// Signature for the function import. + sig: VMSharedSignatureIndex, + /// Trampoline to use for functions that use [`VMFunctionKind::Static`]. + static_trampoline: VMTrampoline, + }, + /// A global. + Global(unc_vm_types::GlobalType), + /// A table. + Table(unc_vm_types::TableType), + /// Some memory. + Memory(unc_vm_types::MemoryType, crate::MemoryStyle), +} + +/// A module import. +pub struct VMImport { + /// This is passed to the `resolve` method. + /// + /// This index is shared between different import types. + pub import_no: u32, + /// The module name. + pub module: String, + /// The field name. + pub field: String, + /// Type of the import. + pub ty: VMImportType, +} + +/// Resolved import pointers. +#[derive(Clone)] +pub struct Imports { + /// Resolved addresses for imported functions. + pub functions: BoxedSlice, + + /// Initializers for host function environments. This is split out from `functions` + /// because the generated code never needs to touch this and the extra wasted + /// space may affect Wasm runtime performance due to increased cache pressure. + /// + /// We make it optional so that we can free the data after use. + /// + /// We move this data in `get_imported_function_envs` because there's + /// no value to keeping it around; host functions must be initialized + /// exactly once so we save some memory and improve correctness by + /// moving this data. + pub host_function_env_initializers: Option>, + + /// Resolved addresses for imported tables. + pub tables: BoxedSlice, + + /// Resolved addresses for imported memories. + pub memories: BoxedSlice, + + /// Resolved addresses for imported globals. + pub globals: BoxedSlice, +} + +impl Imports { + /// Construct a new `Imports` instance. + pub fn new( + function_imports: PrimaryMap, + host_function_env_initializers: PrimaryMap, + table_imports: PrimaryMap, + memory_imports: PrimaryMap, + global_imports: PrimaryMap, + ) -> Self { + Self { + functions: function_imports.into_boxed_slice(), + host_function_env_initializers: Some(host_function_env_initializers.into_boxed_slice()), + tables: table_imports.into_boxed_slice(), + memories: memory_imports.into_boxed_slice(), + globals: global_imports.into_boxed_slice(), + } + } + + /// Construct a new `Imports` instance with no imports. + pub fn none() -> Self { + Self { + functions: PrimaryMap::new().into_boxed_slice(), + host_function_env_initializers: None, + tables: PrimaryMap::new().into_boxed_slice(), + memories: PrimaryMap::new().into_boxed_slice(), + globals: PrimaryMap::new().into_boxed_slice(), + } + } + + /// Get the `WasmerEnv::init_with_instance` function pointers and the pointers + /// to the envs to call it on. + /// + /// This function can only be called once, it deletes the data it returns after + /// returning it to ensure that it's not called more than once. + pub fn get_imported_function_envs(&mut self) -> BoxedSlice { + self.host_function_env_initializers + .take() + .unwrap_or_else(|| PrimaryMap::new().into_boxed_slice()) + } +} diff --git a/runtime/unc-vm/vm/src/instance/allocator.rs b/runtime/unc-vm/vm/src/instance/allocator.rs new file mode 100644 index 000000000..73caafd80 --- /dev/null +++ b/runtime/unc-vm/vm/src/instance/allocator.rs @@ -0,0 +1,195 @@ +use super::{Instance, InstanceRef}; +use crate::vmcontext::{VMMemoryDefinition, VMTableDefinition}; +use crate::VMOffsets; +use unc_vm_types::entity::EntityRef; +use unc_vm_types::{LocalMemoryIndex, LocalTableIndex}; +use std::alloc::{self, Layout}; +use std::convert::TryFrom; +use std::mem; +use std::ptr::{self, NonNull}; + +/// This is an intermediate type that manages the raw allocation and +/// metadata when creating an [`Instance`]. +/// +/// This type will free the allocated memory if it's dropped before +/// being used. +/// +/// It is important to remind that [`Instance`] is dynamically-sized +/// based on `VMOffsets`: The `Instance.vmctx` field represents a +/// dynamically-sized array that extends beyond the nominal end of the +/// type. So in order to create an instance of it, we must: +/// +/// 1. Define the correct layout for `Instance` (size and alignment), +/// 2. Allocate it properly. +/// +/// The [`InstanceAllocator::instance_layout`] computes the correct +/// layout to represent the wanted [`Instance`]. +/// +/// Then we use this layout to allocate an empty `Instance` properly. +pub struct InstanceAllocator { + /// The buffer that will contain the [`Instance`] and dynamic fields. + instance_ptr: NonNull, + + /// The layout of the `instance_ptr` buffer. + instance_layout: Layout, + + /// Information about the offsets into the `instance_ptr` buffer for + /// the dynamic fields. + offsets: VMOffsets, + + /// Whether or not this type has transferred ownership of the + /// `instance_ptr` buffer. If it has not when being dropped, + /// the buffer should be freed. + consumed: bool, +} + +impl Drop for InstanceAllocator { + fn drop(&mut self) { + if !self.consumed { + // If `consumed` has not been set, then we still have ownership + // over the buffer and must free it. + let instance_ptr = self.instance_ptr.as_ptr(); + + unsafe { + std::alloc::dealloc(instance_ptr as *mut u8, self.instance_layout); + } + } + } +} + +impl InstanceAllocator { + /// Allocates instance data for use with [`InstanceHandle::new`]. + /// + /// Returns a wrapper type around the allocation and 2 vectors of + /// pointers into the allocated buffer. These lists of pointers + /// correspond to the location in memory for the local memories and + /// tables respectively. These pointers should be written to before + /// calling [`InstanceHandle::new`]. + /// + /// [`InstanceHandle::new`]: super::InstanceHandle::new + pub fn new( + offsets: VMOffsets, + ) -> (Self, Vec>, Vec>) { + let instance_layout = Self::instance_layout(&offsets); + + #[allow(clippy::cast_ptr_alignment)] + let instance_ptr = unsafe { alloc::alloc(instance_layout) as *mut Instance }; + + let instance_ptr = if let Some(ptr) = NonNull::new(instance_ptr) { + ptr + } else { + alloc::handle_alloc_error(instance_layout); + }; + + let allocator = Self { instance_ptr, instance_layout, offsets, consumed: false }; + + // # Safety + // Both of these calls are safe because we allocate the pointer + // above with the same `offsets` that these functions use. + // Thus there will be enough valid memory for both of them. + let memories = unsafe { allocator.memory_definition_locations() }; + let tables = unsafe { allocator.table_definition_locations() }; + + (allocator, memories, tables) + } + + /// Calculate the appropriate layout for the [`Instance`]. + fn instance_layout(offsets: &VMOffsets) -> Layout { + let vmctx_size = usize::try_from(offsets.size_of_vmctx()) + .expect("Failed to convert the size of `vmctx` to a `usize`"); + + let instance_vmctx_layout = + Layout::array::(vmctx_size).expect("Failed to create a layout for `VMContext`"); + + let (instance_layout, _offset) = Layout::new::() + .extend(instance_vmctx_layout) + .expect("Failed to extend to `Instance` layout to include `VMContext`"); + + instance_layout.pad_to_align() + } + + /// Get the locations of where the local [`VMMemoryDefinition`]s should be stored. + /// + /// This function lets us create `Memory` objects on the host with backing + /// memory in the VM. + /// + /// # Safety + /// + /// - `Self.instance_ptr` must point to enough memory that all of + /// the offsets in `Self.offsets` point to valid locations in + /// memory, i.e. `Self.instance_ptr` must have been allocated by + /// `Self::new`. + unsafe fn memory_definition_locations(&self) -> Vec> { + let num_memories = self.offsets.num_local_memories(); + let num_memories = usize::try_from(num_memories).unwrap(); + let mut out = Vec::with_capacity(num_memories); + + // We need to do some pointer arithmetic now. The unit is `u8`. + let ptr = self.instance_ptr.cast::().as_ptr(); + let base_ptr = ptr.add(mem::size_of::()); + + for i in 0..num_memories { + let mem_offset = self.offsets.vmctx_vmmemory_definition(LocalMemoryIndex::new(i)); + let mem_offset = usize::try_from(mem_offset).unwrap(); + + let new_ptr = NonNull::new_unchecked(base_ptr.add(mem_offset)); + + out.push(new_ptr.cast()); + } + + out + } + + /// Get the locations of where the [`VMTableDefinition`]s should be stored. + /// + /// This function lets us create [`Table`] objects on the host with backing + /// memory in the VM. + /// + /// # Safety + /// + /// - `Self.instance_ptr` must point to enough memory that all of + /// the offsets in `Self.offsets` point to valid locations in + /// memory, i.e. `Self.instance_ptr` must have been allocated by + /// `Self::new`. + unsafe fn table_definition_locations(&self) -> Vec> { + let num_tables = self.offsets.num_local_tables(); + let num_tables = usize::try_from(num_tables).unwrap(); + let mut out = Vec::with_capacity(num_tables); + + // We need to do some pointer arithmetic now. The unit is `u8`. + let ptr = self.instance_ptr.cast::().as_ptr(); + let base_ptr = ptr.add(std::mem::size_of::()); + + for i in 0..num_tables { + let table_offset = self.offsets.vmctx_vmtable_definition(LocalTableIndex::new(i)); + let table_offset = usize::try_from(table_offset).unwrap(); + + let new_ptr = NonNull::new_unchecked(base_ptr.add(table_offset)); + + out.push(new_ptr.cast()); + } + out + } + + /// Finish preparing by writing the [`Instance`] into memory, and + /// consume this `InstanceAllocator`. + pub(crate) fn write_instance(mut self, instance: Instance) -> InstanceRef { + // Prevent the old state's drop logic from being called as we + // transition into the new state. + self.consumed = true; + + unsafe { + // `instance` is moved at `Self.instance_ptr`. This + // pointer has been allocated by `Self::allocate_instance` + // (so by `InstanceRef::allocate_instance`). + ptr::write(self.instance_ptr.as_ptr(), instance); + // Now `instance_ptr` is correctly initialized! + } + let instance = self.instance_ptr; + let instance_layout = self.instance_layout; + + // This is correct because of the invariants of `Self` and + // because we write `Instance` to the pointer in this function. + unsafe { InstanceRef::new(instance, instance_layout) } + } +} diff --git a/runtime/unc-vm/vm/src/instance/mod.rs b/runtime/unc-vm/vm/src/instance/mod.rs new file mode 100644 index 000000000..1c037c652 --- /dev/null +++ b/runtime/unc-vm/vm/src/instance/mod.rs @@ -0,0 +1,1321 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! An `Instance` contains all the runtime state used by execution of +//! a WebAssembly module (except its callstack and register state). An +//! `InstanceRef` is a wrapper around `Instance` that manages +//! how it is allocated and deallocated. An `InstanceHandle` is a +//! wrapper around an `InstanceRef`. + +mod allocator; +mod r#ref; + +pub use allocator::InstanceAllocator; +pub use r#ref::{InstanceRef, WeakOrStrongInstanceRef}; + +use crate::func_data_registry::VMFuncRef; +use crate::global::Global; +use crate::imports::Imports; +use crate::memory::{Memory, MemoryError}; +use crate::sig_registry::VMSharedSignatureIndex; +use crate::table::{Table, TableElement}; +use crate::trap::traphandlers::get_trap_handler; +use crate::trap::{catch_traps, Trap, TrapCode}; +use crate::vmcontext::{ + VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, + VMFunctionEnvironment, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, + VMLocalFunction, VMMemoryDefinition, VMMemoryImport, VMTableDefinition, VMTableImport, +}; +use crate::{unc_vm_call_trampoline, Artifact, VMOffsets, VMTrampoline}; +use crate::{VMExtern, VMFunction, VMGlobal}; +use memoffset::offset_of; +use more_asserts::assert_lt; +use unc_vm_types::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, PrimaryMap}; +use unc_vm_types::{ + DataIndex, DataInitializer, ElemIndex, ExportIndex, FastGasCounter, FunctionIndex, GlobalIndex, + GlobalInit, InstanceConfig, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, + OwnedTableInitializer, Pages, TableIndex, +}; +use std::any::Any; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::convert::TryFrom; +use std::ffi; +use std::fmt; +use std::mem; +use std::ptr::{self, NonNull}; +use std::slice; +use std::sync::Arc; + +/// The function pointer to call with data and an [`Instance`] pointer to +/// finish initializing the host env. +pub type ImportInitializerFuncPtr = + fn(*mut ffi::c_void, *const ffi::c_void) -> Result<(), ResultErr>; + +/// A WebAssembly instance. +/// +/// The type is dynamically-sized. Indeed, the `vmctx` field can +/// contain various data. That's why the type has a C representation +/// to ensure that the `vmctx` field is last. See the documentation of +/// the `vmctx` field to learn more. +#[repr(C)] +pub struct Instance { + pub(crate) artifact: Arc, + + /// External configuration for instance. + config: InstanceConfig, + + /// WebAssembly linear memory data. + memories: BoxedSlice>, + + /// Table data... + tables: BoxedSlice>, + + /// WebAssembly global data. + globals: BoxedSlice>, + + /// Passive elements in this instantiation. As `elem.drop`s happen, these + /// entries get removed. + passive_elements: RefCell>>, + + /// Passive data segments from our module. As `data.drop`s happen, entries + /// get removed. A missing entry is considered equivalent to an empty slice. + passive_data: RefCell>>, + + /// Mapping of function indices to their func ref backing data. `VMFuncRef`s + /// will point to elements here for functions defined or imported by this + /// instance. + funcrefs: BoxedSlice, + + /// Hosts can store arbitrary per-instance information here. + host_state: Box, + + /// Functions to operate on host environments in the imports + /// and pointers to the environments. + /// + /// TODO: Be sure to test with serialize/deserialize and imported + /// functions from other Wasm modules. + imported_function_envs: BoxedSlice, + + /// Additional context used by compiled WebAssembly code. This + /// field is last, and represents a dynamically-sized array that + /// extends beyond the nominal end of the struct (similar to a + /// flexible array member). + vmctx: VMContext, +} + +/// A collection of data about host envs used by imported functions. +#[derive(Debug)] +pub enum ImportFunctionEnv { + /// The `vmctx` pointer does not refer to a host env, there is no + /// metadata about it. + NoEnv, + /// We're dealing with a user-defined host env. + /// + /// This host env may be either unwrapped (the user-supplied host env + /// directly) or wrapped. i.e. in the case of Dynamic functions, we + /// store our own extra data along with the user supplied env, + /// thus the `env` pointer here points to the outermost type. + Env { + /// The function environment. This is not always the user-supplied + /// env. + env: *mut ffi::c_void, + + /// A clone function for duplicating the env. + clone: fn(*mut ffi::c_void) -> *mut ffi::c_void, + /// This field is not always present. When it is present, it + /// should be set to `None` after use to prevent double + /// initialization. + initializer: Option, + /// The destructor to clean up the type in `env`. + /// + /// # Safety + /// - This function must be called ina synchronized way. For + /// example, in the `Drop` implementation of this type. + destructor: unsafe fn(*mut ffi::c_void), + }, +} + +impl Clone for ImportFunctionEnv { + fn clone(&self) -> Self { + match &self { + Self::NoEnv => Self::NoEnv, + Self::Env { env, clone, destructor, initializer } => { + let new_env = (*clone)(*env); + Self::Env { + env: new_env, + clone: *clone, + destructor: *destructor, + initializer: *initializer, + } + } + } + } +} + +impl Drop for ImportFunctionEnv { + fn drop(&mut self) { + match self { + Self::Env { env, destructor, .. } => { + // # Safety + // - This is correct because we know no other references + // to this data can exist if we're dropping it. + unsafe { + (destructor)(*env); + } + } + Self::NoEnv => (), + } + } +} + +impl fmt::Debug for Instance { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.debug_struct("Instance").finish() + } +} + +#[allow(clippy::cast_ptr_alignment)] +impl Instance { + /// Helper function to access various locations offset from our `*mut + /// VMContext` object. + unsafe fn vmctx_plus_offset(&self, offset: u32) -> *mut T { + (self.vmctx_ptr() as *mut u8).add(usize::try_from(offset).unwrap()).cast() + } + + /// Offsets in the `vmctx` region. + fn offsets(&self) -> &VMOffsets { + self.artifact.offsets() + } + + /// Return a pointer to the `VMSharedSignatureIndex`s. + fn signature_ids_ptr(&self) -> *mut VMSharedSignatureIndex { + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_signature_ids_begin()) } + } + + /// Return the indexed `VMFunctionImport`. + fn imported_function(&self, index: FunctionIndex) -> &VMFunctionImport { + let index = usize::try_from(index.as_u32()).unwrap(); + unsafe { &*self.imported_functions_ptr().add(index) } + } + + /// Return a pointer to the `VMFunctionImport`s. + fn imported_functions_ptr(&self) -> *mut VMFunctionImport { + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_imported_functions_begin()) } + } + + /// Return the index `VMTableImport`. + fn imported_table(&self, index: TableIndex) -> &VMTableImport { + let index = usize::try_from(index.as_u32()).unwrap(); + unsafe { &*self.imported_tables_ptr().add(index) } + } + + /// Return a pointer to the `VMTableImports`s. + fn imported_tables_ptr(&self) -> *mut VMTableImport { + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_imported_tables_begin()) } + } + + /// Return the indexed `VMMemoryImport`. + fn imported_memory(&self, index: MemoryIndex) -> &VMMemoryImport { + let index = usize::try_from(index.as_u32()).unwrap(); + let addr = unsafe { self.imported_memories_ptr().add(index) }; + let align = std::mem::align_of::(); + debug_assert!( + addr as usize % align == 0, + "VMMemoryImport addr is not aligned to {align}: {addr:p}" + ); + unsafe { &*addr } + } + + /// Return a pointer to the `VMMemoryImport`s. + fn imported_memories_ptr(&self) -> *mut VMMemoryImport { + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_imported_memories_begin()) } + } + + /// Return the indexed `VMGlobalImport`. + fn imported_global(&self, index: GlobalIndex) -> &VMGlobalImport { + let index = usize::try_from(index.as_u32()).unwrap(); + unsafe { &*self.imported_globals_ptr().add(index) } + } + + /// Return a pointer to the `VMGlobalImport`s. + fn imported_globals_ptr(&self) -> *mut VMGlobalImport { + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_imported_globals_begin()) } + } + + /// Return the indexed `VMTableDefinition`. + #[allow(unused)] + fn table(&self, index: LocalTableIndex) -> VMTableDefinition { + unsafe { *self.table_ptr(index).as_ref() } + } + + /// Updates the value for a defined table to `VMTableDefinition`. + #[allow(unused)] + fn set_table(&self, index: LocalTableIndex, table: &VMTableDefinition) { + unsafe { + *self.table_ptr(index).as_ptr() = *table; + } + } + + /// Return the indexed `VMTableDefinition`. + fn table_ptr(&self, index: LocalTableIndex) -> NonNull { + let index = usize::try_from(index.as_u32()).unwrap(); + NonNull::new(unsafe { self.tables_ptr().add(index) }).unwrap() + } + + /// Return a pointer to the `VMTableDefinition`s. + fn tables_ptr(&self) -> *mut VMTableDefinition { + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_tables_begin()) } + } + + /// Return the indexed `VMMemoryDefinition`. + fn memory_definition(&self, index: MemoryIndex) -> &VMMemoryDefinition { + match self.artifact.import_counts().local_memory_index(index) { + Ok(local) => unsafe { self.memory_ptr(local).as_ref() }, + Err(import) => unsafe { &self.imported_memory(import).from.vmmemory().as_ref() }, + } + } + + #[allow(dead_code)] + /// Set the indexed memory to `VMMemoryDefinition`. + fn set_memory(&self, index: LocalMemoryIndex, mem: &VMMemoryDefinition) { + unsafe { + *self.memory_ptr(index).as_ptr() = *mem; + } + } + + /// Return the indexed `VMMemoryDefinition`. + fn memory_ptr(&self, index: LocalMemoryIndex) -> NonNull { + let index = usize::try_from(index.as_u32()).unwrap(); + NonNull::new(unsafe { self.memories_ptr().add(index) }).unwrap() + } + + /// Return a pointer to the `VMMemoryDefinition`s. + fn memories_ptr(&self) -> *mut VMMemoryDefinition { + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_memories_begin()) } + } + + /// Return the indexed `VMGlobalDefinition`. + fn global(&self, index: GlobalIndex) -> &VMGlobalDefinition { + match self.artifact.import_counts().local_global_index(index) { + Ok(local) => unsafe { self.global_ptr(local).as_ref() }, + Err(import) => unsafe { self.imported_global(import).definition.as_ref() }, + } + } + + /// Set the indexed global to `VMGlobalDefinition`. + #[allow(dead_code)] + fn set_global(&self, index: LocalGlobalIndex, global: &VMGlobalDefinition) { + unsafe { + *self.global_ptr(index).as_ptr() = global.clone(); + } + } + + /// Return the indexed `VMGlobalDefinition`. + fn global_ptr(&self, index: LocalGlobalIndex) -> NonNull { + let index = usize::try_from(index.as_u32()).unwrap(); + // TODO: + NonNull::new(unsafe { *self.globals_ptr().add(index) }).unwrap() + } + + /// Return a pointer to the `VMGlobalDefinition`s. + fn globals_ptr(&self) -> *mut *mut VMGlobalDefinition { + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_globals_begin()) } + } + + /// Return a pointer to the `VMBuiltinFunctionsArray`. + fn builtin_functions_ptr(&self) -> *mut VMBuiltinFunctionsArray { + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_builtin_functions_begin()) } + } + + /// Return a reference to the vmctx used by compiled wasm code. + fn vmctx(&self) -> &VMContext { + &self.vmctx + } + + /// Return a raw pointer to the vmctx used by compiled wasm code. + fn vmctx_ptr(&self) -> *mut VMContext { + self.vmctx() as *const VMContext as *mut VMContext + } + + /// Return a reference to the custom state attached to this instance. + #[inline] + pub fn host_state(&self) -> &dyn Any { + &*self.host_state + } + + /// Return a pointer to the trap catcher. + fn trap_catcher_ptr(&self) -> *mut *const u8 { + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_trap_handler()) } + } + + /// Return a pointer to the gas limiter. + pub fn gas_counter_ptr(&self) -> *mut *const FastGasCounter { + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_gas_limiter_pointer()) } + } + + /// Return a pointer to initial stack limit. + pub fn stack_limit_initial_ptr(&self) -> *mut u32 { + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_stack_limit_initial_begin()) } + } + + /// Return a pointer to current stack limit. + pub fn stack_limit_ptr(&self) -> *mut u32 { + unsafe { self.vmctx_plus_offset(self.offsets().vmctx_stack_limit_begin()) } + } + + /// Invoke the WebAssembly start function of the instance, if one is present. + fn invoke_start_function(&self) -> Result<(), Trap> { + let start_index = match self.artifact.start_function() { + Some(idx) => idx, + None => return Ok(()), + }; + let start_funcref = self.funcrefs[start_index]; + // Make the call. + self.reset_stack_meter(); + let result = unsafe { + catch_traps(|| { + mem::transmute::<*const VMFunctionBody, unsafe extern "C" fn(VMFunctionEnvironment)>( + start_funcref.func_ptr, + )(start_funcref.vmctx) + }) + }; + result + } + + pub fn reset_stack_meter(&self) { + unsafe { + *(self.stack_limit_ptr()) = *(self.stack_limit_initial_ptr()); + } + } + + /// Return the offset from the vmctx pointer to its containing `Instance`. + #[inline] + pub(crate) fn vmctx_offset() -> isize { + offset_of!(Self, vmctx) as isize + } + + /// Return the table index for the given `VMTableDefinition`. + pub(crate) fn table_index(&self, table: &VMTableDefinition) -> LocalTableIndex { + let begin: *const VMTableDefinition = self.tables_ptr() as *const _; + let end: *const VMTableDefinition = table; + // TODO: Use `offset_from` once it stablizes. + let index = LocalTableIndex::new( + (end as usize - begin as usize) / mem::size_of::(), + ); + assert_lt!(index.index(), self.tables.len()); + index + } + + /// Return the memory index for the given `VMMemoryDefinition`. + pub(crate) fn memory_index(&self, memory: &VMMemoryDefinition) -> LocalMemoryIndex { + let begin: *const VMMemoryDefinition = self.memories_ptr() as *const _; + let end: *const VMMemoryDefinition = memory; + // TODO: Use `offset_from` once it stablizes. + let index = LocalMemoryIndex::new( + (end as usize - begin as usize) / mem::size_of::(), + ); + assert_lt!(index.index(), self.memories.len()); + index + } + + /// Grow memory by the specified amount of pages. + /// + /// Returns `None` if memory can't be grown by the specified amount + /// of pages. + pub(crate) fn memory_grow( + &self, + memory_index: LocalMemoryIndex, + delta: IntoPages, + ) -> Result + where + IntoPages: Into, + { + let mem = self + .memories + .get(memory_index) + .unwrap_or_else(|| panic!("no memory for index {}", memory_index.index())); + mem.grow(delta.into()) + } + + /// Grow imported memory by the specified amount of pages. + /// + /// Returns `None` if memory can't be grown by the specified amount + /// of pages. + /// + /// # Safety + /// This and `imported_memory_size` are currently unsafe because they + /// dereference the memory import's pointers. + pub(crate) unsafe fn imported_memory_grow( + &self, + memory_index: MemoryIndex, + delta: IntoPages, + ) -> Result + where + IntoPages: Into, + { + let import = self.imported_memory(memory_index); + import.from.grow(delta.into()) + } + + /// Returns the number of allocated wasm pages. + pub(crate) fn memory_size(&self, memory_index: LocalMemoryIndex) -> Pages { + self.memories + .get(memory_index) + .unwrap_or_else(|| panic!("no memory for index {}", memory_index.index())) + .size() + } + + /// Returns the number of allocated wasm pages in an imported memory. + /// + /// # Safety + /// This and `imported_memory_grow` are currently unsafe because they + /// dereference the memory import's pointers. + pub(crate) unsafe fn imported_memory_size(&self, memory_index: MemoryIndex) -> Pages { + self.imported_memory(memory_index).from.size() + } + + /// Returns the number of elements in a given table. + pub(crate) fn table_size(&self, table_index: LocalTableIndex) -> u32 { + self.tables[table_index].size() + } + + /// Returns the number of elements in a given imported table. + /// + /// # Safety + /// `table_index` must be a valid, imported table index. + pub(crate) unsafe fn imported_table_size(&self, table_index: TableIndex) -> u32 { + self.imported_table(table_index).from.size() + } + + /// Grow table by the specified amount of elements. + /// + /// Returns `None` if table can't be grown by the specified amount + /// of elements. + pub(crate) fn table_grow( + &self, + table_index: LocalTableIndex, + delta: u32, + init_value: TableElement, + ) -> Option { + let result = self + .tables + .get(table_index) + .unwrap_or_else(|| panic!("no table for index {}", table_index.index())) + .grow(delta, init_value); + + result + } + + /// Grow table by the specified amount of elements. + /// + /// # Safety + /// `table_index` must be a valid, imported table index. + pub(crate) unsafe fn imported_table_grow( + &self, + table_index: TableIndex, + delta: u32, + init_value: TableElement, + ) -> Option { + let import = self.imported_table(table_index); + import.from.grow(delta, init_value) + } + + /// Get table element by index. + pub(crate) fn table_get( + &self, + table_index: LocalTableIndex, + index: u32, + ) -> Option { + self.tables + .get(table_index) + .unwrap_or_else(|| panic!("no table for index {}", table_index.index())) + .get(index) + } + + /// Returns the element at the given index. + /// + /// # Safety + /// `table_index` must be a valid, imported table index. + pub(crate) unsafe fn imported_table_get( + &self, + table_index: TableIndex, + index: u32, + ) -> Option { + let import = self.imported_table(table_index); + import.from.get(index) + } + + /// Set table element by index. + pub(crate) fn table_set( + &self, + table_index: LocalTableIndex, + index: u32, + val: TableElement, + ) -> Result<(), Trap> { + self.tables + .get(table_index) + .unwrap_or_else(|| panic!("no table for index {}", table_index.index())) + .set(index, val) + } + + /// Set table element by index for an imported table. + /// + /// # Safety + /// `table_index` must be a valid, imported table index. + pub(crate) unsafe fn imported_table_set( + &self, + table_index: TableIndex, + index: u32, + val: TableElement, + ) -> Result<(), Trap> { + let import = self.imported_table(table_index); + import.from.set(index, val) + } + + pub(crate) fn func_ref(&self, function_index: FunctionIndex) -> Option { + Some(self.get_vm_funcref(function_index)) + } + + /// Get a `VMFuncRef` for the given `FunctionIndex`. + fn get_vm_funcref(&self, index: FunctionIndex) -> VMFuncRef { + if index == FunctionIndex::reserved_value() { + return VMFuncRef::null(); + } + VMFuncRef(&self.funcrefs[index]) + } + + /// The `table.init` operation: initializes a portion of a table with a + /// passive element. + /// + /// # Errors + /// + /// Returns a `Trap` error when the range within the table is out of bounds + /// or the range within the passive element is out of bounds. + pub(crate) fn table_init( + &self, + table_index: TableIndex, + elem_index: ElemIndex, + dst: u32, + src: u32, + len: u32, + ) -> Result<(), Trap> { + // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-init + + let table = self.get_table(table_index); + let passive_elements = self.passive_elements.borrow(); + let elem = passive_elements.get(&elem_index).map_or::<&[VMFuncRef], _>(&[], |e| &**e); + + if src.checked_add(len).map_or(true, |n| n as usize > elem.len()) + || dst.checked_add(len).map_or(true, |m| m > table.size()) + { + return Err(Trap::lib(TrapCode::TableAccessOutOfBounds)); + } + + for (dst, src) in (dst..dst + len).zip(src..src + len) { + table + .set(dst, TableElement::FuncRef(elem[src as usize])) + .expect("should never panic because we already did the bounds check above"); + } + + Ok(()) + } + + /// The `table.fill` operation: fills a portion of a table with a given value. + /// + /// # Errors + /// + /// Returns a `Trap` error when the range within the table is out of bounds + pub(crate) fn table_fill( + &self, + table_index: TableIndex, + start_index: u32, + item: TableElement, + len: u32, + ) -> Result<(), Trap> { + // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-init + + let table = self.get_table(table_index); + let table_size = table.size() as usize; + + if start_index.checked_add(len).map_or(true, |n| n as usize > table_size) { + return Err(Trap::lib(TrapCode::TableAccessOutOfBounds)); + } + + for i in start_index..(start_index + len) { + table + .set(i, item.clone()) + .expect("should never panic because we already did the bounds check above"); + } + + Ok(()) + } + + /// Drop an element. + pub(crate) fn elem_drop(&self, elem_index: ElemIndex) { + // https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-elem-drop + + let mut passive_elements = self.passive_elements.borrow_mut(); + passive_elements.remove(&elem_index); + // Note that we don't check that we actually removed an element because + // dropping a non-passive element is a no-op (not a trap). + } + + /// Do a `memory.copy` for a locally defined memory. + /// + /// # Errors + /// + /// Returns a `Trap` error when the source or destination ranges are out of + /// bounds. + pub(crate) fn local_memory_copy( + &self, + memory_index: LocalMemoryIndex, + dst: u32, + src: u32, + len: u32, + ) -> Result<(), Trap> { + // https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy + let memory = unsafe { self.memory_ptr(memory_index).as_ref() }; + // The following memory copy is not synchronized and is not atomic: + unsafe { memory.memory_copy(dst, src, len) } + } + + /// Perform a `memory.copy` on an imported memory. + pub(crate) fn imported_memory_copy( + &self, + memory_index: MemoryIndex, + dst: u32, + src: u32, + len: u32, + ) -> Result<(), Trap> { + let import = self.imported_memory(memory_index); + // The following memory copy is not synchronized and is not atomic: + unsafe { import.from.vmmemory().as_ref().memory_copy(dst, src, len) } + } + + /// Perform the `memory.fill` operation on a locally defined memory. + /// + /// # Errors + /// + /// Returns a `Trap` error if the memory range is out of bounds. + pub(crate) fn local_memory_fill( + &self, + memory_index: LocalMemoryIndex, + dst: u32, + val: u32, + len: u32, + ) -> Result<(), Trap> { + let memory = unsafe { self.memory_ptr(memory_index).as_ref() }; + // The following memory fill is not synchronized and is not atomic: + unsafe { memory.memory_fill(dst, val, len) } + } + + /// Perform the `memory.fill` operation on an imported memory. + /// + /// # Errors + /// + /// Returns a `Trap` error if the memory range is out of bounds. + pub(crate) fn imported_memory_fill( + &self, + memory_index: MemoryIndex, + dst: u32, + val: u32, + len: u32, + ) -> Result<(), Trap> { + let import = self.imported_memory(memory_index); + // The following memory fill is not synchronized and is not atomic: + unsafe { import.from.vmmemory().as_ref().memory_fill(dst, val, len) } + } + + /// Performs the `memory.init` operation. + /// + /// # Errors + /// + /// Returns a `Trap` error if the destination range is out of this module's + /// memory's bounds or if the source range is outside the data segment's + /// bounds. + pub(crate) fn memory_init( + &self, + memory_index: MemoryIndex, + data_index: DataIndex, + dst: u32, + src: u32, + len: u32, + ) -> Result<(), Trap> { + // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-memory-init + + let memory = self.memory_definition(memory_index); + let passive_data = self.passive_data.borrow(); + let data = passive_data.get(&data_index).map_or(&[][..], |d| &**d); + + let oob_access = src.checked_add(len).map_or(true, |n| n as usize > data.len()) + || dst + .checked_add(len) + .map_or(true, |m| usize::try_from(m).unwrap() > memory.current_length); + + if oob_access { + return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds)); + } + let src_slice = &data[src as usize..(src + len) as usize]; + unsafe { + let dst_start = memory.base.add(dst as usize); + let dst_slice = slice::from_raw_parts_mut(dst_start, len as usize); + dst_slice.copy_from_slice(src_slice); + } + Ok(()) + } + + /// Drop the given data segment, truncating its length to zero. + pub(crate) fn data_drop(&self, data_index: DataIndex) { + let mut passive_data = self.passive_data.borrow_mut(); + passive_data.remove(&data_index); + } + + /// Get a table by index regardless of whether it is locally-defined or an + /// imported, foreign table. + pub(crate) fn get_table(&self, table_index: TableIndex) -> &dyn Table { + match self.artifact.import_counts().local_table_index(table_index) { + Ok(local) => self.get_local_table(local), + Err(import) => self.get_foreign_table(import), + } + } + + /// Get a locally-defined table. + pub(crate) fn get_local_table(&self, index: LocalTableIndex) -> &dyn Table { + self.tables[index].as_ref() + } + + /// Get an imported, foreign table. + pub(crate) fn get_foreign_table(&self, index: TableIndex) -> &dyn Table { + let import = self.imported_table(index); + &*import.from + } +} + +/// A handle holding an `InstanceRef`, which holds an `Instance` +/// of a WebAssembly module. +/// +/// This is more or less a public facade of the private `Instance`, +/// providing useful higher-level API. +#[derive(Debug, PartialEq)] +pub struct InstanceHandle { + /// The [`InstanceRef`]. See its documentation to learn more. + instance: InstanceRef, +} + +impl InstanceHandle { + /// Create a new `InstanceHandle` pointing at a new [`InstanceRef`]. + /// + /// # Safety + /// + /// This method is not necessarily inherently unsafe to call, but in general + /// the APIs of an `Instance` are quite unsafe and have not been really + /// audited for safety that much. As a result the unsafety here on this + /// method is a low-overhead way of saying “this is an extremely unsafe type + /// to work with”. + /// + /// Extreme care must be taken when working with `InstanceHandle` and it's + /// recommended to have relatively intimate knowledge of how it works + /// internally if you'd like to do so. If possible it's recommended to use + /// the `unc_vm` crate API rather than this type since that is vetted for + /// safety. + /// + /// However the following must be taken care of before calling this function: + /// - The memory at `instance.tables_ptr()` must be initialized with data for + /// all the local tables. + /// - The memory at `instance.memories_ptr()` must be initialized with data for + /// all the local memories. + // FIXME: instances should just store a reference to an Artifact + #[allow(clippy::too_many_arguments)] + pub unsafe fn new( + artifact: Arc, + allocator: InstanceAllocator, + finished_memories: BoxedSlice>, + finished_tables: BoxedSlice>, + finished_globals: BoxedSlice>, + imports: Imports, + passive_data: BTreeMap>, + host_state: Box, + imported_function_envs: BoxedSlice, + instance_config: InstanceConfig, + ) -> Self { + let vmctx_globals = finished_globals + .values() + .map(|m| m.vmglobal()) + .collect::>() + .into_boxed_slice(); + let passive_data = RefCell::new(passive_data); + + let handle = { + // use dummy value to create an instance so we can get the vmctx pointer + let funcrefs = PrimaryMap::new().into_boxed_slice(); + // Create the `Instance`. The unique, the One. + let instance = Instance { + artifact, + config: instance_config.clone(), + memories: finished_memories, + tables: finished_tables, + globals: finished_globals, + passive_elements: Default::default(), + passive_data, + host_state, + funcrefs, + imported_function_envs, + vmctx: VMContext {}, + }; + + let mut instance_ref = allocator.write_instance(instance); + + // Set the funcrefs after we've built the instance + { + let instance = instance_ref.as_mut().unwrap(); + let vmctx_ptr = instance.vmctx_ptr(); + instance.funcrefs = build_funcrefs( + &imports, + instance.artifact.functions().iter().map(|(_, f)| f), + vmctx_ptr, + ); + *(instance.trap_catcher_ptr()) = get_trap_handler(); + *(instance.gas_counter_ptr()) = instance_config.gas_counter; + *(instance.stack_limit_ptr()) = instance_config.stack_limit; + *(instance.stack_limit_initial_ptr()) = instance_config.stack_limit; + } + + Self { instance: instance_ref } + }; + let instance = handle.instance().as_ref(); + + ptr::copy( + instance.artifact.signatures().as_ptr(), + instance.signature_ids_ptr() as *mut VMSharedSignatureIndex, + instance.artifact.signatures().len(), + ); + + ptr::copy( + imports.functions.values().as_slice().as_ptr(), + instance.imported_functions_ptr() as *mut VMFunctionImport, + imports.functions.len(), + ); + ptr::copy( + imports.tables.values().as_slice().as_ptr(), + instance.imported_tables_ptr() as *mut VMTableImport, + imports.tables.len(), + ); + ptr::copy( + imports.memories.values().as_slice().as_ptr(), + instance.imported_memories_ptr() as *mut VMMemoryImport, + imports.memories.len(), + ); + ptr::copy( + imports.globals.values().as_slice().as_ptr(), + instance.imported_globals_ptr() as *mut VMGlobalImport, + imports.globals.len(), + ); + // these should already be set, add asserts here? for: + // - instance.tables_ptr() as *mut VMTableDefinition + // - instance.memories_ptr() as *mut VMMemoryDefinition + ptr::copy( + vmctx_globals.values().as_slice().as_ptr(), + instance.globals_ptr() as *mut NonNull, + vmctx_globals.len(), + ); + ptr::write( + instance.builtin_functions_ptr() as *mut VMBuiltinFunctionsArray, + VMBuiltinFunctionsArray::initialized(), + ); + + // Perform infallible initialization in this constructor, while fallible + // initialization is deferred to the `initialize` method. + initialize_passive_elements(instance); + initialize_globals(instance); + handle + } + + /// Return a reference to the contained `Instance`. + pub fn instance(&self) -> &InstanceRef { + &self.instance + } + + /// Finishes the instantiation process started by `Instance::new_with_config`. + /// + /// # Safety + /// + /// Only safe to call immediately after instantiation. + pub unsafe fn finish_instantiation(&self) -> Result<(), Trap> { + let instance = self.instance().as_ref(); + + // Apply the initializers. + initialize_tables(instance)?; + initialize_memories(instance, instance.artifact.data_segments().iter().map(Into::into))?; + + // The WebAssembly spec specifies that the start function is + // invoked automatically at instantiation time. + instance.invoke_start_function()?; + Ok(()) + } + + /// See [`traphandlers::unc_vm_call_trampoline`]. + pub unsafe fn invoke_function( + &self, + vmctx: VMFunctionEnvironment, + trampoline: VMTrampoline, + callee: *const VMFunctionBody, + values_vec: *mut u8, + ) -> Result<(), Trap> { + // `vmctx` is always `*mut VMContext` here, as we call to WASM. + { + let instance = self.instance().as_ref(); + instance.reset_stack_meter(); + } + unc_vm_call_trampoline(vmctx, trampoline, callee, values_vec) + } + + /// Return a reference to the vmctx used by compiled wasm code. + pub fn vmctx(&self) -> &VMContext { + self.instance().as_ref().vmctx() + } + + /// Return a raw pointer to the vmctx used by compiled wasm code. + pub fn vmctx_ptr(&self) -> *mut VMContext { + self.instance().as_ref().vmctx_ptr() + } + + /// Return a reference to the `VMOffsets` to get offsets in the + /// `Self::vmctx_ptr` region. Be careful when doing pointer + /// arithmetic! + pub fn vmoffsets(&self) -> &VMOffsets { + self.instance().as_ref().offsets() + } + + /// Lookup an exported function with the specified function index. + pub fn function_by_index(&self, idx: FunctionIndex) -> Option { + let instance = self.instance.as_ref(); + + let (address, signature, vmctx, call_trampoline) = + match instance.artifact.import_counts().local_function_index(idx) { + Ok(local) => { + let func = instance.artifact.functions().get(local)?; + ( + *(func.body), + func.signature, + VMFunctionEnvironment { vmctx: instance.vmctx_ptr() }, + Some(func.trampoline), + ) + } + Err(import) => { + let import = instance.imported_function(import); + (*(import.body), import.signature, import.environment, import.trampoline) + } + }; + Some(VMFunction { + // Any function received is already static at this point as: + // 1. All locally defined functions in the Wasm have a static signature. + // 2. All the imported functions are already static (because + // they point to the trampolines rather than the dynamic addresses). + kind: VMFunctionKind::Static, + address, + signature, + vmctx, + call_trampoline, + instance_ref: Some(WeakOrStrongInstanceRef::Strong(self.instance().clone())), + }) + } + + /// Return the indexed `VMMemoryDefinition`. + fn memory_by_index(&self, index: MemoryIndex) -> Option { + let instance = self.instance.as_ref(); + let from = match instance.artifact.import_counts().local_memory_index(index) { + Ok(local) => Arc::clone(&instance.memories[local]), + Err(import) => Arc::clone(&instance.imported_memory(import).from), + }; + Some(crate::VMMemory { + from, + instance_ref: Some(WeakOrStrongInstanceRef::Strong(self.instance().clone())), + }) + } + + /// Return the indexed `VMMemoryDefinition`. + fn table_by_index(&self, index: TableIndex) -> Option { + let instance = self.instance.as_ref(); + let from = match instance.artifact.import_counts().local_table_index(index) { + Ok(local) => Arc::clone(&instance.tables[local]), + Err(import) => Arc::clone(&instance.imported_table(import).from), + }; + Some(crate::VMTable { + from, + instance_ref: Some(WeakOrStrongInstanceRef::Strong(self.instance().clone())), + }) + } + + /// Obtain a reference to a global entity by its index. + pub fn global_by_index(&self, index: GlobalIndex) -> Option { + let instance = self.instance.as_ref(); + let from = match instance.artifact.import_counts().local_global_index(index) { + Ok(local) => Arc::clone(&instance.globals[local]), + Err(import) => Arc::clone(&instance.imported_global(import).from), + }; + Some(crate::VMGlobal { + from, + instance_ref: Some(WeakOrStrongInstanceRef::Strong(self.instance().clone())), + }) + } + + /// Lookup an exported function with the given name. + pub fn lookup(&self, field: &str) -> Option { + let instance = self.instance.as_ref(); + Some(match instance.artifact.export_field(field)? { + ExportIndex::Function(idx) => VMExtern::Function(self.function_by_index(idx)?), + ExportIndex::Table(idx) => VMExtern::Table(self.table_by_index(idx)?), + ExportIndex::Global(idx) => VMExtern::Global(self.global_by_index(idx)?), + ExportIndex::Memory(idx) => VMExtern::Memory(self.memory_by_index(idx)?), + }) + } + + /// Return a reference to the custom state attached to this instance. + pub fn host_state(&self) -> &dyn Any { + self.instance().as_ref().host_state() + } + + /// Return the memory index for the given `VMMemoryDefinition` in this instance. + pub fn memory_index(&self, memory: &VMMemoryDefinition) -> LocalMemoryIndex { + self.instance().as_ref().memory_index(memory) + } + + /// Grow memory in this instance by the specified amount of pages. + /// + /// Returns `None` if memory can't be grown by the specified amount + /// of pages. + pub fn memory_grow( + &self, + memory_index: LocalMemoryIndex, + delta: IntoPages, + ) -> Result + where + IntoPages: Into, + { + self.instance().as_ref().memory_grow(memory_index, delta) + } + + /// Return the table index for the given `VMTableDefinition` in this instance. + pub fn table_index(&self, table: &VMTableDefinition) -> LocalTableIndex { + self.instance().as_ref().table_index(table) + } + + /// Grow table in this instance by the specified amount of pages. + /// + /// Returns `None` if memory can't be grown by the specified amount + /// of pages. + pub fn table_grow( + &self, + table_index: LocalTableIndex, + delta: u32, + init_value: TableElement, + ) -> Option { + self.instance().as_ref().table_grow(table_index, delta, init_value) + } + + /// Get table element reference. + /// + /// Returns `None` if index is out of bounds. + pub fn table_get(&self, table_index: LocalTableIndex, index: u32) -> Option { + self.instance().as_ref().table_get(table_index, index) + } + + /// Set table element reference. + /// + /// Returns an error if the index is out of bounds + pub fn table_set( + &self, + table_index: LocalTableIndex, + index: u32, + val: TableElement, + ) -> Result<(), Trap> { + self.instance().as_ref().table_set(table_index, index, val) + } + + /// Get a table defined locally within this module. + pub fn get_local_table(&self, index: LocalTableIndex) -> &dyn Table { + self.instance().as_ref().get_local_table(index) + } +} + +/// Initializes the host environments. +/// +/// # Safety +/// - This function must be called with the correct `Err` type parameter: the error type is not +/// visible to code in `unc_vm_vm`, so it's the caller's responsibility to ensure these +/// functions are called with the correct type. +/// - `instance_ptr` must point to a valid `unc_vm_test_api::Instance`. +#[tracing::instrument(target = "unc_vm", level = "trace", skip_all)] +pub unsafe fn initialize_host_envs( + handle: &std::sync::Mutex, + instance_ptr: *const ffi::c_void, +) -> Result<(), Err> { + let initializers = { + let mut instance_lock = handle.lock().unwrap(); + let instance_ref = instance_lock.instance.as_mut_unchecked(); + let mut initializers = vec![]; + for import_function_env in instance_ref.imported_function_envs.values_mut() { + match import_function_env { + ImportFunctionEnv::Env { env, ref mut initializer, .. } => { + if let Some(init) = initializer.take() { + initializers.push((init, *env)); + } + } + ImportFunctionEnv::NoEnv => (), + } + } + initializers + }; + for (init, env) in initializers { + let f = mem::transmute::<&ImportInitializerFuncPtr, &ImportInitializerFuncPtr>(&init); + f(env, instance_ptr)?; + } + Ok(()) +} + +/// Compute the offset for a memory data initializer. +fn get_memory_init_start(init: &DataInitializer<'_>, instance: &Instance) -> usize { + let mut start = init.location.offset; + if let Some(base) = init.location.base { + let val = instance.global(base).to_u32(); + start += usize::try_from(val).unwrap(); + } + start +} + +#[allow(clippy::mut_from_ref)] +/// Return a byte-slice view of a memory's data. +unsafe fn get_memory_slice<'instance>( + init: &DataInitializer<'_>, + instance: &'instance Instance, +) -> &'instance mut [u8] { + let memory = instance.memory_definition(init.location.memory_index); + slice::from_raw_parts_mut(memory.base, memory.current_length) +} + +/// Compute the offset for a table element initializer. +fn get_table_init_start(init: &OwnedTableInitializer, instance: &Instance) -> usize { + let mut start = init.offset; + if let Some(base) = init.base { + let val = instance.global(base).to_u32(); + start += usize::try_from(val).unwrap(); + } + start +} + +/// Initialize the table memory from the provided initializers. +fn initialize_tables(instance: &Instance) -> Result<(), Trap> { + for init in instance.artifact.element_segments() { + let start = get_table_init_start(init, instance); + let table = instance.get_table(init.table_index); + + if start.checked_add(init.elements.len()).map_or(true, |end| end > table.size() as usize) { + return Err(Trap::lib(TrapCode::TableAccessOutOfBounds)); + } + + for (i, func_idx) in init.elements.iter().enumerate() { + let anyfunc = instance.get_vm_funcref(*func_idx); + table.set(u32::try_from(start + i).unwrap(), TableElement::FuncRef(anyfunc)).unwrap(); + } + } + + Ok(()) +} + +/// Initialize the `Instance::passive_elements` map by resolving the +/// `ModuleInfo::passive_elements`'s `FunctionIndex`s into `VMCallerCheckedAnyfunc`s for +/// this instance. +fn initialize_passive_elements(instance: &Instance) { + let mut passive_elements = instance.passive_elements.borrow_mut(); + debug_assert!( + passive_elements.is_empty(), + "should only be called once, at initialization time" + ); + + passive_elements.extend( + instance + .artifact + .passive_elements() + .iter() + .filter(|(_, segments)| !segments.is_empty()) + .map(|(idx, segments)| { + (*idx, segments.iter().map(|s| instance.get_vm_funcref(*s)).collect()) + }), + ); +} + +/// Initialize the table memory from the provided initializers. +fn initialize_memories<'a>( + instance: &Instance, + data_initializers: impl Iterator>, +) -> Result<(), Trap> { + for init in data_initializers { + let memory = instance.memory_definition(init.location.memory_index); + + let start = get_memory_init_start(&init, instance); + if start.checked_add(init.data.len()).map_or(true, |end| end > memory.current_length) { + return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds)); + } + + unsafe { + let mem_slice = get_memory_slice(&init, instance); + let end = start + init.data.len(); + let to_init = &mut mem_slice[start..end]; + to_init.copy_from_slice(init.data); + } + } + + Ok(()) +} + +fn initialize_globals(instance: &Instance) { + for (index, (_, initializer)) in instance.artifact.globals().iter().enumerate() { + unsafe { + let to = instance.global_ptr(LocalGlobalIndex::new(index)).as_ptr(); + match initializer { + GlobalInit::I32Const(x) => *(*to).as_i32_mut() = *x, + GlobalInit::I64Const(x) => *(*to).as_i64_mut() = *x, + GlobalInit::F32Const(x) => *(*to).as_f32_mut() = *x, + GlobalInit::F64Const(x) => *(*to).as_f64_mut() = *x, + GlobalInit::V128Const(x) => *(*to).as_bytes_mut() = *x.bytes(), + GlobalInit::GetGlobal(x) => *to = instance.global(*x).clone(), + GlobalInit::RefNullConst => *(*to).as_funcref_mut() = VMFuncRef::null(), + GlobalInit::RefFunc(func_idx) => { + let funcref = instance.func_ref(*func_idx).unwrap(); + *(*to).as_funcref_mut() = funcref; + } + } + } + } +} + +/// Eagerly builds all the `VMFuncRef`s for imported and local functions so that all +/// future funcref operations are just looking up this data. +pub fn build_funcrefs<'a>( + imports: &Imports, + finished_functions: impl ExactSizeIterator, + // vmshared_signatures: &BoxedSlice, + vmctx_ptr: *mut VMContext, +) -> BoxedSlice { + let mut func_refs = + PrimaryMap::with_capacity(imports.functions.len() + finished_functions.len()); + for (_, import) in imports.functions.iter() { + let anyfunc = VMCallerCheckedAnyfunc { + func_ptr: *(import.body), + type_index: import.signature, + vmctx: import.environment, + }; + func_refs.push(anyfunc); + } + // local functions + for function in finished_functions { + let anyfunc = VMCallerCheckedAnyfunc { + func_ptr: *(function.body), + type_index: function.signature, + vmctx: VMFunctionEnvironment { vmctx: vmctx_ptr }, + }; + func_refs.push(anyfunc); + } + func_refs.into_boxed_slice() +} diff --git a/runtime/unc-vm/vm/src/instance/ref.rs b/runtime/unc-vm/vm/src/instance/ref.rs new file mode 100644 index 000000000..e5ea81bfa --- /dev/null +++ b/runtime/unc-vm/vm/src/instance/ref.rs @@ -0,0 +1,233 @@ +use super::Instance; +use std::alloc::Layout; +use std::convert::TryFrom; +use std::ptr::{self, NonNull}; +use std::sync::{Arc, Weak}; + +/// Dynamic instance allocation. +/// +/// This structure has a C representation because `Instance` is +/// dynamically-sized, and the `instance` field must be last. +/// +/// This `InstanceRef` must be freed with [`InstanceInner::deallocate_instance`] +/// if and only if it has been set correctly. The `Drop` implementation of +/// [`InstanceInner`] calls its `deallocate_instance` method without +/// checking if this property holds, only when `Self.strong` is equal to 1. +#[derive(Debug)] +#[repr(C)] +struct InstanceInner { + /// The layout of `Instance` (which can vary). + instance_layout: Layout, + + /// The `Instance` itself. It must be the last field of + /// `InstanceRef` since `Instance` is dyamically-sized. + /// + /// `Instance` must not be dropped manually by Rust, because it's + /// allocated manually with `alloc` and a specific layout (Rust + /// would be able to drop `Instance` itself but it will imply a + /// memory leak because of `alloc`). + /// + /// No one in the code has a copy of the `Instance`'s + /// pointer. `Self` is the only one. + instance: NonNull, +} + +impl InstanceInner { + /// Deallocate `Instance`. + /// + /// # Safety + /// + /// `Self.instance` must be correctly set and filled before being + /// dropped and deallocated. + unsafe fn deallocate_instance(&mut self) { + let instance_ptr = self.instance.as_ptr(); + + ptr::drop_in_place(instance_ptr); + std::alloc::dealloc(instance_ptr as *mut u8, self.instance_layout); + } + + /// Get a reference to the `Instance`. + #[inline] + pub(crate) fn as_ref(&self) -> &Instance { + // SAFETY: The pointer is properly aligned, it is + // “dereferencable”, it points to an initialized memory of + // `Instance`, and the reference has the lifetime `'a`. + unsafe { self.instance.as_ref() } + } + + #[inline] + pub(super) fn as_mut(&mut self) -> &mut Instance { + unsafe { self.instance.as_mut() } + } +} + +impl PartialEq for InstanceInner { + /// Two `InstanceInner` are equal if and only if + /// `Self.instance` points to the same location. + fn eq(&self, other: &Self) -> bool { + self.instance == other.instance + } +} + +impl Drop for InstanceInner { + /// Drop the `InstanceInner`. + fn drop(&mut self) { + unsafe { Self::deallocate_instance(self) }; + } +} + +// TODO: These implementations have been added to enable instances to contain `Arc`s, however it +// isn’t exactly clear that the `InstanceInner` contents are `Send` or `Sync`, thus effectively +// lying to the compiler. Fortunately in actual use this is not a big deal as the near runtime is +// single-threaded anyway (and `Arc` is only necessary to facilitate caching of the loaded +// modules IIRC; an attempt to remove this cache has been made in the past, but had to be +// restored.) +unsafe impl Send for InstanceInner {} +unsafe impl Sync for InstanceInner {} + +/// An `InstanceRef` is responsible to properly deallocate, +/// and to give access to an `Instance`, in such a way that `Instance` +/// is unique, can be shared, safely, across threads, without +/// duplicating the pointer in multiple locations. `InstanceRef` +/// must be the only “owner” of an `Instance`. +/// +/// Consequently, one must not share `Instance` but +/// `InstanceRef`. It acts like an Atomically Reference Counter +/// to `Instance`. In short, `InstanceRef` is roughly a +/// simplified version of `std::sync::Arc`. +/// +/// Note for the curious reader: [`InstanceAllocator::new`] +/// and [`InstanceHandle::new`] will respectively allocate a proper +/// `Instance` and will fill it correctly. +/// +/// A little bit of background: The initial goal was to be able to +/// share an [`Instance`] between an [`InstanceHandle`] and the module +/// exports, so that one can drop a [`InstanceHandle`] but still being +/// able to use the exports properly. +#[derive(Debug, PartialEq, Clone)] +pub struct InstanceRef(Arc); + +impl InstanceRef { + /// Create a new `InstanceRef`. It allocates nothing. It fills + /// nothing. The `Instance` must be already valid and + /// filled. + /// + /// # Safety + /// + /// `instance` must a non-null, non-dangling, properly aligned, + /// and correctly initialized pointer to `Instance`. See + /// [`InstanceAllocator`] for an example of how to correctly use + /// this API. + pub(super) unsafe fn new(instance: NonNull, instance_layout: Layout) -> Self { + Self(Arc::new(InstanceInner { instance_layout, instance })) + } + + /// Get a reference to the `Instance`. + #[inline] + pub fn as_ref(&self) -> &Instance { + (&*self.0).as_ref() + } + + /// Only succeeds if ref count is 1. + #[inline] + pub(super) fn as_mut(&mut self) -> Option<&mut Instance> { + Some(Arc::get_mut(&mut self.0)?.as_mut()) + } + + /// Like [`InstanceRef::as_mut`] but always succeeds. + /// May cause undefined behavior if used improperly. + /// + /// # Safety + /// It is the caller's responsibility to ensure exclusivity and synchronization of the + /// instance before calling this function. No other pointers to any Instance data + /// should be dereferenced for the lifetime of the returned `&mut Instance`. + #[inline] + pub(super) unsafe fn as_mut_unchecked(&mut self) -> &mut Instance { + let ptr: *mut InstanceInner = Arc::as_ptr(&self.0) as *mut _; + (&mut *ptr).as_mut() + } +} + +/// A weak instance ref. This type does not keep the underlying `Instance` alive +/// but can be converted into a full `InstanceRef` if the underlying `Instance` hasn't +/// been deallocated. +#[derive(Debug, Clone)] +pub struct WeakInstanceRef(Weak); + +impl PartialEq for WeakInstanceRef { + fn eq(&self, other: &Self) -> bool { + self.0.ptr_eq(&other.0) + } +} + +impl WeakInstanceRef { + /// Try to convert into a strong, `InstanceRef`. + pub fn upgrade(&self) -> Option { + let inner = self.0.upgrade()?; + Some(InstanceRef(inner)) + } +} + +/// An `InstanceRef` that may or may not be keeping the `Instance` alive. +/// +/// This type is useful for types that conditionally must keep / not keep the +/// underlying `Instance` alive. For example, to prevent cycles in `WasmerEnv`s. +#[derive(Debug, Clone, PartialEq)] +pub enum WeakOrStrongInstanceRef { + /// A weak instance ref. + Weak(WeakInstanceRef), + /// A strong instance ref. + Strong(InstanceRef), +} + +impl WeakOrStrongInstanceRef { + /// Tries to upgrade weak references to a strong reference, returning None + /// if it can't be done. + pub fn upgrade(&self) -> Option { + match self { + Self::Weak(weak) => weak.upgrade().map(Self::Strong), + Self::Strong(strong) => Some(Self::Strong(strong.clone())), + } + } + + /// Clones self into a weak reference. + pub fn downgrade(&self) -> Self { + match self { + Self::Weak(weak) => Self::Weak(weak.clone()), + Self::Strong(strong) => Self::Weak(WeakInstanceRef(Arc::downgrade(&strong.0))), + } + } +} + +impl TryFrom for InstanceRef { + type Error = &'static str; + fn try_from(value: WeakOrStrongInstanceRef) -> Result { + match value { + WeakOrStrongInstanceRef::Strong(strong) => Ok(strong), + WeakOrStrongInstanceRef::Weak(weak) => { + weak.upgrade().ok_or("Failed to upgrade weak reference") + } + } + } +} + +impl From for WeakInstanceRef { + fn from(value: WeakOrStrongInstanceRef) -> Self { + match value { + WeakOrStrongInstanceRef::Strong(strong) => Self(Arc::downgrade(&strong.0)), + WeakOrStrongInstanceRef::Weak(weak) => weak, + } + } +} + +impl From for WeakOrStrongInstanceRef { + fn from(value: WeakInstanceRef) -> Self { + Self::Weak(value) + } +} + +impl From for WeakOrStrongInstanceRef { + fn from(value: InstanceRef) -> Self { + Self::Strong(value) + } +} diff --git a/runtime/unc-vm/vm/src/lib.rs b/runtime/unc-vm/vm/src/lib.rs new file mode 100644 index 000000000..133dd5c4c --- /dev/null +++ b/runtime/unc-vm/vm/src/lib.rs @@ -0,0 +1,78 @@ +//! Runtime library support for Wasmer. + +#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] +#![deny(trivial_numeric_casts, unused_extern_crates)] +#![warn(unused_import_braces)] +#![cfg_attr( + feature = "cargo-clippy", + allow(clippy::new_without_default, clippy::vtable_address_comparisons) +)] +#![cfg_attr( + feature = "cargo-clippy", + warn( + clippy::float_arithmetic, + clippy::mut_mut, + clippy::nonminimal_bool, + clippy::map_unwrap_or, + clippy::print_stdout, + clippy::unicode_not_nfc, + clippy::use_self + ) +)] + +mod artifact; +mod export; +mod func_data_registry; +mod global; +mod imports; +mod instance; +mod memory; +mod mmap; +mod probestack; +mod resolver; +mod sig_registry; +mod table; +mod trap; +mod tunables; +mod vmcontext; +mod vmoffsets; + +pub mod libcalls; + +pub use crate::artifact::{Artifact, Instantiatable}; +pub use crate::export::*; +pub use crate::func_data_registry::{FuncDataRegistry, VMFuncRef}; +pub use crate::global::*; +pub use crate::imports::{Imports, VMImport, VMImportType}; +pub use crate::instance::{ + initialize_host_envs, ImportFunctionEnv, ImportInitializerFuncPtr, InstanceAllocator, + InstanceHandle, WeakOrStrongInstanceRef, +}; +pub use crate::memory::{LinearMemory, Memory, MemoryError, MemoryStyle}; +pub use crate::mmap::Mmap; +pub use crate::probestack::PROBESTACK; +pub use crate::resolver::{ + ChainableNamedResolver, Export, ExportFunction, ExportFunctionMetadata, NamedResolver, + NamedResolverChain, NullResolver, Resolver, +}; +pub use crate::sig_registry::{SignatureRegistry, VMSharedSignatureIndex}; +pub use crate::table::{LinearTable, Table, TableElement, TableStyle}; +pub use crate::trap::*; +pub use crate::tunables::{TestTunables, Tunables}; +pub use crate::vmcontext::{ + FunctionBodyPtr, FunctionExtent, SectionBodyPtr, VMBuiltinFunctionIndex, + VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionBody, + VMFunctionEnvironment, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, + VMLocalFunction, VMMemoryDefinition, VMMemoryImport, VMTableDefinition, VMTableImport, + VMTrampoline, +}; +pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMOffsets}; +#[deprecated( + since = "2.1.0", + note = "ModuleInfo, ExportsIterator, ImportsIterator should be imported from unc_vm_types." +)] +pub use unc_vm_types::ModuleInfo; +pub use unc_vm_types::VMExternRef; + +/// Version number of this crate. +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/runtime/unc-vm/vm/src/libcalls.rs b/runtime/unc-vm/vm/src/libcalls.rs new file mode 100644 index 000000000..21a674227 --- /dev/null +++ b/runtime/unc-vm/vm/src/libcalls.rs @@ -0,0 +1,850 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Runtime library calls. +//! +//! Note that Wasm compilers may sometimes perform these inline rather than +//! calling them, particularly when CPUs have special instructions which compute +//! them directly. +//! +//! These functions are called by compiled Wasm code, and therefore must take +//! certain care about some things: +//! +//! * They must always be `pub extern "C"` and should only contain basic, raw +//! i32/i64/f32/f64/pointer parameters that are safe to pass across the system +//! ABI! +//! +//! * If any nested function propagates an `Err(trap)` out to the library +//! function frame, we need to raise it. This involves some nasty and quite +//! unsafe code under the covers! Notable, after raising the trap, drops +//! **will not** be run for local variables! This can lead to things like +//! leaking `InstanceHandle`s which leads to never deallocating JIT code, +//! instances, and modules! Therefore, always use nested blocks to ensure +//! drops run before raising a trap: +//! +//! ```ignore +//! pub extern "C" fn my_lib_function(...) { +//! let result = { +//! // Do everything in here so drops run at the end of the block. +//! ... +//! }; +//! if let Err(trap) = result { +//! // Now we can safely raise the trap without leaking! +//! raise_lib_trap(trap); +//! } +//! } +//! ``` + +#![allow(missing_docs)] // For some reason lint fails saying that `LibCall` is not documented, when it actually is + +use crate::func_data_registry::VMFuncRef; +use crate::probestack::PROBESTACK; +use crate::table::{RawTableElement, TableElement}; +use crate::trap::{raise_lib_trap, Trap, TrapCode}; +use crate::vmcontext::VMContext; +use crate::VMExternRef; +use unc_vm_types::{ + DataIndex, ElemIndex, FunctionIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, + TableIndex, Type, +}; +use std::fmt; + +/// Implementation of f32.ceil +#[no_mangle] +pub extern "C" fn unc_vm_f32_ceil(x: f32) -> f32 { + x.ceil() +} + +/// Implementation of f32.floor +#[no_mangle] +pub extern "C" fn unc_vm_f32_floor(x: f32) -> f32 { + x.floor() +} + +/// Implementation of f32.trunc +#[no_mangle] +pub extern "C" fn unc_vm_f32_trunc(x: f32) -> f32 { + x.trunc() +} + +/// Implementation of f32.nearest +#[allow(clippy::float_arithmetic, clippy::float_cmp)] +#[no_mangle] +pub extern "C" fn unc_vm_f32_nearest(x: f32) -> f32 { + // Rust doesn't have a nearest function, so do it manually. + if x == 0.0 { + // Preserve the sign of zero. + x + } else { + // Nearest is either ceil or floor depending on which is nearest or even. + let u = x.ceil(); + let d = x.floor(); + let um = (x - u).abs(); + let dm = (x - d).abs(); + if um < dm + || (um == dm && { + let h = u / 2.; + h.floor() == h + }) + { + u + } else { + d + } + } +} + +/// Implementation of f64.ceil +#[no_mangle] +pub extern "C" fn unc_vm_f64_ceil(x: f64) -> f64 { + x.ceil() +} + +/// Implementation of f64.floor +#[no_mangle] +pub extern "C" fn unc_vm_f64_floor(x: f64) -> f64 { + x.floor() +} + +/// Implementation of f64.trunc +#[no_mangle] +pub extern "C" fn unc_vm_f64_trunc(x: f64) -> f64 { + x.trunc() +} + +/// Implementation of f64.nearest +#[allow(clippy::float_arithmetic, clippy::float_cmp)] +#[no_mangle] +pub extern "C" fn unc_vm_f64_nearest(x: f64) -> f64 { + // Rust doesn't have a nearest function, so do it manually. + if x == 0.0 { + // Preserve the sign of zero. + x + } else { + // Nearest is either ceil or floor depending on which is nearest or even. + let u = x.ceil(); + let d = x.floor(); + let um = (x - u).abs(); + let dm = (x - d).abs(); + if um < dm + || (um == dm && { + let h = u / 2.; + h.floor() == h + }) + { + u + } else { + d + } + } +} + +/// Implementation of memory.grow for locally-defined 32-bit memories. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_memory32_grow( + vmctx: *mut VMContext, + delta: u32, + memory_index: u32, +) -> u32 { + let instance = (&*vmctx).instance(); + let memory_index = LocalMemoryIndex::from_u32(memory_index); + + instance.memory_grow(memory_index, delta).map(|pages| pages.0).unwrap_or(u32::max_value()) +} + +/// Implementation of memory.grow for imported 32-bit memories. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_imported_memory32_grow( + vmctx: *mut VMContext, + delta: u32, + memory_index: u32, +) -> u32 { + let instance = (&*vmctx).instance(); + let memory_index = MemoryIndex::from_u32(memory_index); + + instance + .imported_memory_grow(memory_index, delta) + .map(|pages| pages.0) + .unwrap_or(u32::max_value()) +} + +/// Implementation of memory.size for locally-defined 32-bit memories. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_memory32_size(vmctx: *mut VMContext, memory_index: u32) -> u32 { + let instance = (&*vmctx).instance(); + let memory_index = LocalMemoryIndex::from_u32(memory_index); + + instance.memory_size(memory_index).0 +} + +/// Implementation of memory.size for imported 32-bit memories. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_imported_memory32_size( + vmctx: *mut VMContext, + memory_index: u32, +) -> u32 { + let instance = (&*vmctx).instance(); + let memory_index = MemoryIndex::from_u32(memory_index); + + instance.imported_memory_size(memory_index).0 +} + +/// Implementation of `table.copy`. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_table_copy( + vmctx: *mut VMContext, + dst_table_index: u32, + src_table_index: u32, + dst: u32, + src: u32, + len: u32, +) { + let result = { + let dst_table_index = TableIndex::from_u32(dst_table_index); + let src_table_index = TableIndex::from_u32(src_table_index); + let instance = (&*vmctx).instance(); + let dst_table = instance.get_table(dst_table_index); + let src_table = instance.get_table(src_table_index); + dst_table.copy(src_table, dst, src, len) + }; + if let Err(trap) = result { + raise_lib_trap(trap); + } +} + +/// Implementation of `table.init`. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_table_init( + vmctx: *mut VMContext, + table_index: u32, + elem_index: u32, + dst: u32, + src: u32, + len: u32, +) { + let result = { + let table_index = TableIndex::from_u32(table_index); + let elem_index = ElemIndex::from_u32(elem_index); + let instance = (&*vmctx).instance(); + instance.table_init(table_index, elem_index, dst, src, len) + }; + if let Err(trap) = result { + raise_lib_trap(trap); + } +} + +/// Implementation of `table.fill`. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_table_fill( + vmctx: *mut VMContext, + table_index: u32, + start_idx: u32, + item: RawTableElement, + len: u32, +) { + let result = { + let table_index = TableIndex::from_u32(table_index); + let instance = (&*vmctx).instance(); + let elem = match instance.get_table(table_index).ty().ty { + Type::ExternRef => TableElement::ExternRef(item.extern_ref.into()), + Type::FuncRef => TableElement::FuncRef(item.func_ref), + _ => panic!("Unrecognized table type: does not contain references"), + }; + + instance.table_fill(table_index, start_idx, elem, len) + }; + if let Err(trap) = result { + raise_lib_trap(trap); + } +} + +/// Implementation of `table.size`. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_table_size(vmctx: *mut VMContext, table_index: u32) -> u32 { + let instance = (&*vmctx).instance(); + let table_index = LocalTableIndex::from_u32(table_index); + + instance.table_size(table_index) +} + +/// Implementation of `table.size` for imported tables. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_imported_table_size( + vmctx: *mut VMContext, + table_index: u32, +) -> u32 { + let instance = (&*vmctx).instance(); + let table_index = TableIndex::from_u32(table_index); + + instance.imported_table_size(table_index) +} + +/// Implementation of `table.get`. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_table_get( + vmctx: *mut VMContext, + table_index: u32, + elem_index: u32, +) -> RawTableElement { + let instance = (&*vmctx).instance(); + let table_index = LocalTableIndex::from_u32(table_index); + + // TODO: type checking, maybe have specialized accessors + match instance.table_get(table_index, elem_index) { + Some(table_ref) => table_ref.into(), + None => raise_lib_trap(Trap::lib(TrapCode::TableAccessOutOfBounds)), + } +} + +/// Implementation of `table.get` for imported tables. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_imported_table_get( + vmctx: *mut VMContext, + table_index: u32, + elem_index: u32, +) -> RawTableElement { + let instance = (&*vmctx).instance(); + let table_index = TableIndex::from_u32(table_index); + + // TODO: type checking, maybe have specialized accessors + match instance.imported_table_get(table_index, elem_index) { + Some(table_ref) => table_ref.into(), + None => raise_lib_trap(Trap::lib(TrapCode::TableAccessOutOfBounds)), + } +} + +/// Implementation of `table.set`. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +/// +/// It is the caller's responsibility to increment the ref count of any ref counted +/// type before passing it to this function. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_table_set( + vmctx: *mut VMContext, + table_index: u32, + elem_index: u32, + value: RawTableElement, +) { + let instance = (&*vmctx).instance(); + let table_index = TableIndex::from_u32(table_index); + if let Ok(local_table) = instance.artifact.import_counts().local_table_index(table_index) { + let elem = match instance.get_local_table(local_table).ty().ty { + Type::ExternRef => TableElement::ExternRef(value.extern_ref.into()), + Type::FuncRef => TableElement::FuncRef(value.func_ref), + _ => panic!("Unrecognized table type: does not contain references"), + }; + // TODO: type checking, maybe have specialized accessors + let result = instance.table_set(local_table, elem_index, elem); + if let Err(trap) = result { + raise_lib_trap(trap); + } + } else { + panic!("unc_vm_imported_table_set should have been called"); + } +} + +/// Implementation of `table.set` for imported tables. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_imported_table_set( + vmctx: *mut VMContext, + table_index: u32, + elem_index: u32, + value: RawTableElement, +) { + let instance = (&*vmctx).instance(); + let table_index = TableIndex::from_u32(table_index); + let elem = match instance.get_foreign_table(table_index).ty().ty { + Type::ExternRef => TableElement::ExternRef(value.extern_ref.into()), + Type::FuncRef => TableElement::FuncRef(value.func_ref), + _ => panic!("Unrecognized table type: does not contain references"), + }; + let result = instance.imported_table_set(table_index, elem_index, elem); + if let Err(trap) = result { + raise_lib_trap(trap); + } +} + +/// Implementation of `table.grow` for locally-defined tables. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_table_grow( + vmctx: *mut VMContext, + init_value: RawTableElement, + delta: u32, + table_index: u32, +) -> u32 { + let instance = (&*vmctx).instance(); + let table_index = LocalTableIndex::from_u32(table_index); + let init_value = match instance.get_local_table(table_index).ty().ty { + Type::ExternRef => TableElement::ExternRef(init_value.extern_ref.into()), + Type::FuncRef => TableElement::FuncRef(init_value.func_ref), + _ => panic!("Unrecognized table type: does not contain references"), + }; + instance.table_grow(table_index, delta, init_value).unwrap_or(u32::max_value()) +} + +/// Implementation of `table.grow` for imported tables. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_imported_table_grow( + vmctx: *mut VMContext, + init_value: RawTableElement, + delta: u32, + table_index: u32, +) -> u32 { + let instance = (&*vmctx).instance(); + let table_index = TableIndex::from_u32(table_index); + let init_value = match instance.get_table(table_index).ty().ty { + Type::ExternRef => TableElement::ExternRef(init_value.extern_ref.into()), + Type::FuncRef => TableElement::FuncRef(init_value.func_ref), + _ => panic!("Unrecognized table type: does not contain references"), + }; + + instance.imported_table_grow(table_index, delta, init_value).unwrap_or(u32::max_value()) +} + +/// Implementation of `func.ref`. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_func_ref(vmctx: *mut VMContext, function_index: u32) -> VMFuncRef { + let instance = (&*vmctx).instance(); + let function_index = FunctionIndex::from_u32(function_index); + + instance.func_ref(function_index).unwrap() +} + +/// Implementation of externref increment +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +/// +/// This function must only be called at precise locations to prevent memory leaks. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_externref_inc(externref: VMExternRef) { + externref.ref_clone(); +} + +/// Implementation of externref decrement +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +/// +/// This function must only be called at precise locations, otherwise use-after-free +/// and other serious memory bugs may occur. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_externref_dec(mut externref: VMExternRef) { + externref.ref_drop() +} + +/// Implementation of `elem.drop`. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_elem_drop(vmctx: *mut VMContext, elem_index: u32) { + let elem_index = ElemIndex::from_u32(elem_index); + let instance = (&*vmctx).instance(); + instance.elem_drop(elem_index); +} + +/// Implementation of `memory.copy` for locally defined memories. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_memory32_copy( + vmctx: *mut VMContext, + memory_index: u32, + dst: u32, + src: u32, + len: u32, +) { + let result = { + let memory_index = LocalMemoryIndex::from_u32(memory_index); + let instance = (&*vmctx).instance(); + instance.local_memory_copy(memory_index, dst, src, len) + }; + if let Err(trap) = result { + raise_lib_trap(trap); + } +} + +/// Implementation of `memory.copy` for imported memories. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_imported_memory32_copy( + vmctx: *mut VMContext, + memory_index: u32, + dst: u32, + src: u32, + len: u32, +) { + let result = { + let memory_index = MemoryIndex::from_u32(memory_index); + let instance = (&*vmctx).instance(); + instance.imported_memory_copy(memory_index, dst, src, len) + }; + if let Err(trap) = result { + raise_lib_trap(trap); + } +} + +/// Implementation of `memory.fill` for locally defined memories. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_memory32_fill( + vmctx: *mut VMContext, + memory_index: u32, + dst: u32, + val: u32, + len: u32, +) { + let result = { + let memory_index = LocalMemoryIndex::from_u32(memory_index); + let instance = (&*vmctx).instance(); + instance.local_memory_fill(memory_index, dst, val, len) + }; + if let Err(trap) = result { + raise_lib_trap(trap); + } +} + +/// Implementation of `memory.fill` for imported memories. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_imported_memory32_fill( + vmctx: *mut VMContext, + memory_index: u32, + dst: u32, + val: u32, + len: u32, +) { + let result = { + let memory_index = MemoryIndex::from_u32(memory_index); + let instance = (&*vmctx).instance(); + instance.imported_memory_fill(memory_index, dst, val, len) + }; + if let Err(trap) = result { + raise_lib_trap(trap); + } +} + +/// Implementation of `memory.init`. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_memory32_init( + vmctx: *mut VMContext, + memory_index: u32, + data_index: u32, + dst: u32, + src: u32, + len: u32, +) { + let result = { + let memory_index = MemoryIndex::from_u32(memory_index); + let data_index = DataIndex::from_u32(data_index); + let instance = (&*vmctx).instance(); + instance.memory_init(memory_index, data_index, dst, src, len) + }; + if let Err(trap) = result { + raise_lib_trap(trap); + } +} + +/// Implementation of `data.drop`. +/// +/// # Safety +/// +/// `vmctx` must be dereferenceable. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_data_drop(vmctx: *mut VMContext, data_index: u32) { + let data_index = DataIndex::from_u32(data_index); + let instance = (&*vmctx).instance(); + instance.data_drop(data_index) +} + +/// Implementation for raising a trap +/// +/// # Safety +/// +/// Only safe to call when wasm code is on the stack, aka `unc_vm_call` or +/// `unc_vm_call_trampoline` must have been previously called. +#[no_mangle] +pub unsafe extern "C" fn unc_vm_raise_trap(trap_code: TrapCode) -> ! { + let trap = Trap::lib(trap_code); + raise_lib_trap(trap) +} + +/// Probestack check +/// +/// # Safety +/// +/// This function does not follow the standard function ABI, and is called as +/// part of the function prologue. +#[no_mangle] +pub static unc_vm_probestack: unsafe extern "C" fn() = PROBESTACK; + +/// The name of a runtime library routine. +/// +/// This list is likely to grow over time. +#[derive( + rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Copy, Clone, Debug, PartialEq, Eq, Hash, +)] +pub enum LibCall { + /// ceil.f32 + CeilF32, + + /// ceil.f64 + CeilF64, + + /// floor.f32 + FloorF32, + + /// floor.f64 + FloorF64, + + /// nearest.f32 + NearestF32, + + /// nearest.f64 + NearestF64, + + /// trunc.f32 + TruncF32, + + /// trunc.f64 + TruncF64, + + /// memory.size for local functions + Memory32Size, + + /// memory.size for imported functions + ImportedMemory32Size, + + /// table.copy + TableCopy, + + /// table.init + TableInit, + + /// table.fill + TableFill, + + /// table.size for local tables + TableSize, + + /// table.size for imported tables + ImportedTableSize, + + /// table.get for local tables + TableGet, + + /// table.get for imported tables + ImportedTableGet, + + /// table.set for local tables + TableSet, + + /// table.set for imported tables + ImportedTableSet, + + /// table.grow for local tables + TableGrow, + + /// table.grow for imported tables + ImportedTableGrow, + + /// ref.func + FuncRef, + + /// elem.drop + ElemDrop, + + /// memory.copy for local memories + Memory32Copy, + + /// memory.copy for imported memories + ImportedMemory32Copy, + + /// memory.fill for local memories + Memory32Fill, + + /// memory.fill for imported memories + ImportedMemory32Fill, + + /// memory.init + Memory32Init, + + /// data.drop + DataDrop, + + /// A custom trap + RaiseTrap, + + /// probe for stack overflow. These are emitted for functions which need + /// when the `enable_probestack` setting is true. + Probestack, +} + +impl LibCall { + /// The function pointer to a libcall + pub fn function_pointer(self) -> usize { + match self { + Self::CeilF32 => unc_vm_f32_ceil as usize, + Self::CeilF64 => unc_vm_f64_ceil as usize, + Self::FloorF32 => unc_vm_f32_floor as usize, + Self::FloorF64 => unc_vm_f64_floor as usize, + Self::NearestF32 => unc_vm_f32_nearest as usize, + Self::NearestF64 => unc_vm_f64_nearest as usize, + Self::TruncF32 => unc_vm_f32_trunc as usize, + Self::TruncF64 => unc_vm_f64_trunc as usize, + Self::Memory32Size => unc_vm_memory32_size as usize, + Self::ImportedMemory32Size => unc_vm_imported_memory32_size as usize, + Self::TableCopy => unc_vm_table_copy as usize, + Self::TableInit => unc_vm_table_init as usize, + Self::TableFill => unc_vm_table_fill as usize, + Self::TableSize => unc_vm_table_size as usize, + Self::ImportedTableSize => unc_vm_imported_table_size as usize, + Self::TableGet => unc_vm_table_get as usize, + Self::ImportedTableGet => unc_vm_imported_table_get as usize, + Self::TableSet => unc_vm_table_set as usize, + Self::ImportedTableSet => unc_vm_imported_table_set as usize, + Self::TableGrow => unc_vm_table_grow as usize, + Self::ImportedTableGrow => unc_vm_imported_table_grow as usize, + Self::FuncRef => unc_vm_func_ref as usize, + Self::ElemDrop => unc_vm_elem_drop as usize, + Self::Memory32Copy => unc_vm_memory32_copy as usize, + Self::ImportedMemory32Copy => unc_vm_imported_memory32_copy as usize, + Self::Memory32Fill => unc_vm_memory32_fill as usize, + Self::ImportedMemory32Fill => unc_vm_memory32_fill as usize, + Self::Memory32Init => unc_vm_memory32_init as usize, + Self::DataDrop => unc_vm_data_drop as usize, + Self::Probestack => unc_vm_probestack as usize, + Self::RaiseTrap => unc_vm_raise_trap as usize, + } + } + + /// Return the function name associated to the libcall. + pub fn to_function_name(&self) -> &str { + match self { + Self::CeilF32 => "unc_vm_f32_ceil", + Self::CeilF64 => "unc_vm_f64_ceil", + Self::FloorF32 => "unc_vm_f32_floor", + Self::FloorF64 => "unc_vm_f64_floor", + Self::NearestF32 => "unc_vm_f32_nearest", + Self::NearestF64 => "unc_vm_f64_nearest", + Self::TruncF32 => "unc_vm_f32_trunc", + Self::TruncF64 => "unc_vm_f64_trunc", + Self::Memory32Size => "unc_vm_memory32_size", + Self::ImportedMemory32Size => "unc_vm_imported_memory32_size", + Self::TableCopy => "unc_vm_table_copy", + Self::TableInit => "unc_vm_table_init", + Self::TableFill => "unc_vm_table_fill", + Self::TableSize => "unc_vm_table_size", + Self::ImportedTableSize => "unc_vm_imported_table_size", + Self::TableGet => "unc_vm_table_get", + Self::ImportedTableGet => "unc_vm_imported_table_get", + Self::TableSet => "unc_vm_table_set", + Self::ImportedTableSet => "unc_vm_imported_table_set", + Self::TableGrow => "unc_vm_table_grow", + Self::ImportedTableGrow => "unc_vm_imported_table_grow", + Self::FuncRef => "unc_vm_func_ref", + Self::ElemDrop => "unc_vm_elem_drop", + Self::Memory32Copy => "unc_vm_memory32_copy", + Self::ImportedMemory32Copy => "unc_vm_imported_memory32_copy", + Self::Memory32Fill => "unc_vm_memory32_fill", + Self::ImportedMemory32Fill => "unc_vm_imported_memory32_fill", + Self::Memory32Init => "unc_vm_memory32_init", + Self::DataDrop => "unc_vm_data_drop", + Self::RaiseTrap => "unc_vm_raise_trap", + // We have to do this because macOS requires a leading `_` and it's not + // a normal function, it's a static variable, so we have to do it manually. + #[cfg(target_vendor = "apple")] + Self::Probestack => "_unc_vm_probestack", + #[cfg(not(target_vendor = "apple"))] + Self::Probestack => "unc_vm_probestack", + } + } +} + +impl fmt::Display for LibCall { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} diff --git a/runtime/unc-vm/vm/src/memory.rs b/runtime/unc-vm/vm/src/memory.rs new file mode 100644 index 000000000..0ea0d9b42 --- /dev/null +++ b/runtime/unc-vm/vm/src/memory.rs @@ -0,0 +1,388 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Memory management for linear memories. +//! +//! `LinearMemory` is to WebAssembly linear memories what `Table` is to WebAssembly tables. + +use crate::mmap::Mmap; +use crate::vmcontext::VMMemoryDefinition; +use more_asserts::assert_ge; +use unc_vm_types::{Bytes, MemoryType, Pages}; +use std::borrow::BorrowMut; +use std::cell::UnsafeCell; +use std::convert::TryInto; +use std::fmt; +use std::ptr::NonNull; +use std::sync::Mutex; +use thiserror::Error; + +/// Error type describing things that can go wrong when operating on Wasm Memories. +#[derive(Error, Debug, Clone, PartialEq, Hash)] +pub enum MemoryError { + /// Low level error with mmap. + #[error("Error when allocating memory: {0}")] + Region(String), + /// The operation would cause the size of the memory to exceed the maximum or would cause + /// an overflow leading to unindexable memory. + #[error("The memory could not grow: current size {} pages, requested increase: {} pages", current.0, attempted_delta.0)] + CouldNotGrow { + /// The current size in pages. + current: Pages, + /// The attempted amount to grow by in pages. + attempted_delta: Pages, + }, + /// The operation would cause the size of the memory size exceed the maximum. + #[error("The memory is invalid because {}", reason)] + InvalidMemory { + /// The reason why the provided memory is invalid. + reason: String, + }, + /// Caller asked for more minimum memory than we can give them. + #[error("The minimum requested ({} pages) memory is greater than the maximum allowed memory ({} pages)", min_requested.0, max_allowed.0)] + MinimumMemoryTooLarge { + /// The number of pages requested as the minimum amount of memory. + min_requested: Pages, + /// The maximum amount of memory we can allocate. + max_allowed: Pages, + }, + /// Caller asked for a maximum memory greater than we can give them. + #[error("The maximum requested memory ({} pages) is greater than the maximum allowed memory ({} pages)", max_requested.0, max_allowed.0)] + MaximumMemoryTooLarge { + /// The number of pages requested as the maximum amount of memory. + max_requested: Pages, + /// The number of pages requested as the maximum amount of memory. + max_allowed: Pages, + }, + /// A user defined error value, used for error cases not listed above. + #[error("A user-defined error occurred: {0}")] + Generic(String), +} + +/// Implementation styles for WebAssembly linear memory. +#[derive(Debug, Clone, PartialEq, Eq, Hash, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +pub enum MemoryStyle { + /// The actual memory can be resized and moved. + Dynamic { + /// Our chosen offset-guard size. + /// + /// It represents the size in bytes of extra guard pages after the end + /// to optimize loads and stores with constant offsets. + offset_guard_size: u64, + }, + /// Address space is allocated up front. + Static { + /// The number of mapped and unmapped pages. + bound: Pages, + /// Our chosen offset-guard size. + /// + /// It represents the size in bytes of extra guard pages after the end + /// to optimize loads and stores with constant offsets. + offset_guard_size: u64, + }, +} + +impl MemoryStyle { + /// Returns the offset-guard size + pub fn offset_guard_size(&self) -> u64 { + match self { + Self::Dynamic { offset_guard_size } => *offset_guard_size, + Self::Static { offset_guard_size, .. } => *offset_guard_size, + } + } +} + +/// Trait for implementing Wasm Memory used by Wasmer. +pub trait Memory: fmt::Debug + Send + Sync { + /// Returns the memory type for this memory. + fn ty(&self) -> MemoryType; + + /// Returns the memory style for this memory. + fn style(&self) -> &MemoryStyle; + + /// Returns the number of allocated wasm pages. + fn size(&self) -> Pages; + + /// Grow memory by the specified amount of wasm pages. + fn grow(&self, delta: Pages) -> Result; + + /// Return a [`VMMemoryDefinition`] for exposing the memory to compiled wasm code. + /// + /// The pointer returned in [`VMMemoryDefinition`] must be valid for the lifetime of this memory. + fn vmmemory(&self) -> NonNull; +} + +/// A linear memory instance. +#[derive(Debug)] +pub struct LinearMemory { + // The underlying allocation. + mmap: Mutex, + + // The optional maximum size in wasm pages of this linear memory. + maximum: Option, + + /// The WebAssembly linear memory description. + memory: MemoryType, + + /// Our chosen implementation style. + style: MemoryStyle, + + // Size in bytes of extra guard pages after the end to optimize loads and stores with + // constant offsets. + offset_guard_size: usize, + + /// The owned memory definition used by the generated code + vm_memory_definition: VMMemoryDefinitionOwnership, +} + +/// A type to help manage who is responsible for the backing memory of them +/// `VMMemoryDefinition`. +#[derive(Debug)] +enum VMMemoryDefinitionOwnership { + /// The `VMMemoryDefinition` is owned by the `Instance` and we should use + /// its memory. This is how a local memory that's exported should be stored. + VMOwned(NonNull), + /// The `VMMemoryDefinition` is owned by the host and we should manage its + /// memory. This is how an imported memory that doesn't come from another + /// Wasm module should be stored. + HostOwned(Box>), +} + +/// We must implement this because of `VMMemoryDefinitionOwnership::VMOwned`. +/// This is correct because synchronization of memory accesses is controlled +/// by the VM. +// REVIEW: I don't believe ^; this probably shouldn't be `Send`... +// mutations from other threads into this data could be a problem, but we probably +// don't want to use atomics for this in the generated code. +// TODO: +unsafe impl Send for LinearMemory {} + +/// This is correct because all internal mutability is protected by a mutex. +unsafe impl Sync for LinearMemory {} + +#[derive(Debug)] +struct WasmMmap { + // Our OS allocation of mmap'd memory. + alloc: Mmap, + // The current logical size in wasm pages of this linear memory. + size: Pages, +} + +impl LinearMemory { + /// Create a new linear memory instance with specified minimum and maximum number of wasm pages. + /// + /// This creates a `LinearMemory` with owned metadata: this can be used to create a memory + /// that will be imported into Wasm modules. + pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result { + unsafe { Self::new_internal(memory, style, None) } + } + + /// Create a new linear memory instance with specified minimum and maximum number of wasm pages. + /// + /// This creates a `LinearMemory` with metadata owned by a VM, pointed to by + /// `vm_memory_location`: this can be used to create a local memory. + /// + /// # Safety + /// - `vm_memory_location` must point to a valid location in VM memory. + pub unsafe fn from_definition( + memory: &MemoryType, + style: &MemoryStyle, + vm_memory_location: NonNull, + ) -> Result { + Self::new_internal(memory, style, Some(vm_memory_location)) + } + + /// Build a `LinearMemory` with either self-owned or VM owned metadata. + unsafe fn new_internal( + memory: &MemoryType, + style: &MemoryStyle, + vm_memory_location: Option>, + ) -> Result { + if memory.minimum > Pages::max_value() { + return Err(MemoryError::MinimumMemoryTooLarge { + min_requested: memory.minimum, + max_allowed: Pages::max_value(), + }); + } + // `maximum` cannot be set to more than `65536` pages. + if let Some(max) = memory.maximum { + if max > Pages::max_value() { + return Err(MemoryError::MaximumMemoryTooLarge { + max_requested: max, + max_allowed: Pages::max_value(), + }); + } + if max < memory.minimum { + return Err(MemoryError::InvalidMemory { + reason: format!( + "the maximum ({} pages) is less than the minimum ({} pages)", + max.0, memory.minimum.0 + ), + }); + } + } + + let offset_guard_bytes = style.offset_guard_size() as usize; + + let minimum_pages = match style { + MemoryStyle::Dynamic { .. } => memory.minimum, + MemoryStyle::Static { bound, .. } => { + assert_ge!(*bound, memory.minimum); + *bound + } + }; + let minimum_bytes = minimum_pages.bytes().0; + let request_bytes = minimum_bytes.checked_add(offset_guard_bytes).unwrap(); + let mapped_pages = memory.minimum; + let mapped_bytes = mapped_pages.bytes(); + + let mut mmap = WasmMmap { + alloc: Mmap::accessible_reserved(mapped_bytes.0, request_bytes) + .map_err(MemoryError::Region)?, + size: memory.minimum, + }; + + let base_ptr = mmap.alloc.as_mut_ptr(); + let mem_length = memory.minimum.bytes().0; + Ok(Self { + mmap: Mutex::new(mmap), + maximum: memory.maximum, + offset_guard_size: offset_guard_bytes, + vm_memory_definition: if let Some(mem_loc) = vm_memory_location { + { + let mut ptr = mem_loc; + let md = ptr.as_mut(); + md.base = base_ptr; + md.current_length = mem_length; + } + VMMemoryDefinitionOwnership::VMOwned(mem_loc) + } else { + VMMemoryDefinitionOwnership::HostOwned(Box::new(UnsafeCell::new( + VMMemoryDefinition { base: base_ptr, current_length: mem_length }, + ))) + }, + memory: *memory, + style: style.clone(), + }) + } + + /// Get the `VMMemoryDefinition`. + /// + /// # Safety + /// - You must ensure that you have mutually exclusive access before calling + /// this function. You can get this by locking the `mmap` mutex. + unsafe fn get_vm_memory_definition(&self) -> NonNull { + match &self.vm_memory_definition { + VMMemoryDefinitionOwnership::VMOwned(ptr) => *ptr, + VMMemoryDefinitionOwnership::HostOwned(boxed_ptr) => { + NonNull::new_unchecked(boxed_ptr.get()) + } + } + } +} + +impl Memory for LinearMemory { + /// Returns the type for this memory. + fn ty(&self) -> MemoryType { + let minimum = self.size(); + let mut out = self.memory; + out.minimum = minimum; + + out + } + + /// Returns the memory style for this memory. + fn style(&self) -> &MemoryStyle { + &self.style + } + + /// Returns the number of allocated wasm pages. + fn size(&self) -> Pages { + // TODO: investigate this function for race conditions + unsafe { + let md_ptr = self.get_vm_memory_definition(); + let md = md_ptr.as_ref(); + Bytes::from(md.current_length).try_into().unwrap() + } + } + + /// Grow memory by the specified amount of wasm pages. + /// + /// Returns `None` if memory can't be grown by the specified amount + /// of wasm pages. + fn grow(&self, delta: Pages) -> Result { + let mut mmap_guard = self.mmap.lock().unwrap(); + let mmap = mmap_guard.borrow_mut(); + // Optimization of memory.grow 0 calls. + if delta.0 == 0 { + return Ok(mmap.size); + } + + let new_pages = mmap + .size + .checked_add(delta) + .ok_or(MemoryError::CouldNotGrow { current: mmap.size, attempted_delta: delta })?; + let prev_pages = mmap.size; + + if let Some(maximum) = self.maximum { + if new_pages > maximum { + return Err(MemoryError::CouldNotGrow { + current: mmap.size, + attempted_delta: delta, + }); + } + } + + // Wasm linear memories are never allowed to grow beyond what is + // indexable. If the memory has no maximum, enforce the greatest + // limit here. + if new_pages >= Pages::max_value() { + // Linear memory size would exceed the index range. + return Err(MemoryError::CouldNotGrow { current: mmap.size, attempted_delta: delta }); + } + + let delta_bytes = delta.bytes().0; + let prev_bytes = prev_pages.bytes().0; + let new_bytes = new_pages.bytes().0; + + if new_bytes > mmap.alloc.len() - self.offset_guard_size { + // If the new size is within the declared maximum, but needs more memory than we + // have on hand, it's a dynamic heap and it can move. + let guard_bytes = self.offset_guard_size; + let request_bytes = + new_bytes.checked_add(guard_bytes).ok_or_else(|| MemoryError::CouldNotGrow { + current: new_pages, + attempted_delta: Bytes(guard_bytes).try_into().unwrap(), + })?; + + let mut new_mmap = + Mmap::accessible_reserved(new_bytes, request_bytes).map_err(MemoryError::Region)?; + + let copy_len = mmap.alloc.len() - self.offset_guard_size; + new_mmap.as_mut_slice()[..copy_len].copy_from_slice(&mmap.alloc.as_slice()[..copy_len]); + + mmap.alloc = new_mmap; + } else if delta_bytes > 0 { + // Make the newly allocated pages accessible. + mmap.alloc.make_accessible(prev_bytes, delta_bytes).map_err(MemoryError::Region)?; + } + + mmap.size = new_pages; + + // update memory definition + unsafe { + let mut md_ptr = self.get_vm_memory_definition(); + let md = md_ptr.as_mut(); + md.current_length = new_pages.bytes().0; + md.base = mmap.alloc.as_mut_ptr() as _; + } + + Ok(prev_pages) + } + + /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. + fn vmmemory(&self) -> NonNull { + let _mmap_guard = self.mmap.lock().unwrap(); + unsafe { self.get_vm_memory_definition() } + } +} diff --git a/runtime/unc-vm/vm/src/mmap.rs b/runtime/unc-vm/vm/src/mmap.rs new file mode 100644 index 000000000..f89681c1d --- /dev/null +++ b/runtime/unc-vm/vm/src/mmap.rs @@ -0,0 +1,276 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Low-level abstraction for allocating and managing zero-filled pages +//! of memory. + +use more_asserts::assert_le; +use more_asserts::assert_lt; +use std::io; +use std::ptr; +use std::slice; + +/// Round `size` up to the nearest multiple of `page_size`. +fn round_up_to_page_size(size: usize, page_size: usize) -> usize { + (size + (page_size - 1)) & !(page_size - 1) +} + +/// A simple struct consisting of a page-aligned pointer to page-aligned +/// and initially-zeroed memory and a length. +#[derive(Debug)] +pub struct Mmap { + // Note that this is stored as a `usize` instead of a `*const` or `*mut` + // pointer to allow this structure to be natively `Send` and `Sync` without + // `unsafe impl`. This type is sendable across threads and shareable since + // the coordination all happens at the OS layer. + ptr: usize, + len: usize, +} + +impl Mmap { + /// Construct a new empty instance of `Mmap`. + pub fn new() -> Self { + // Rust's slices require non-null pointers, even when empty. `Vec` + // contains code to create a non-null dangling pointer value when + // constructed empty, so we reuse that here. + let empty = Vec::::new(); + Self { ptr: empty.as_ptr() as usize, len: 0 } + } + + /// Create a new `Mmap` pointing to at least `size` bytes of page-aligned accessible memory. + pub fn with_at_least(size: usize) -> Result { + let page_size = region::page::size(); + let rounded_size = round_up_to_page_size(size, page_size); + Self::accessible_reserved(rounded_size, rounded_size) + } + + /// Create a new `Mmap` pointing to `accessible_size` bytes of page-aligned accessible memory, + /// within a reserved mapping of `mapping_size` bytes. `accessible_size` and `mapping_size` + /// must be native page-size multiples. + #[cfg(not(target_os = "windows"))] + pub fn accessible_reserved( + accessible_size: usize, + mapping_size: usize, + ) -> Result { + let page_size = region::page::size(); + assert_le!(accessible_size, mapping_size); + assert_eq!(mapping_size & (page_size - 1), 0); + assert_eq!(accessible_size & (page_size - 1), 0); + + // Mmap may return EINVAL if the size is zero, so just + // special-case that. + if mapping_size == 0 { + return Ok(Self::new()); + } + + Ok(if accessible_size == mapping_size { + // Allocate a single read-write region at once. + let ptr = unsafe { + libc::mmap( + ptr::null_mut(), + mapping_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANON, + -1, + 0, + ) + }; + if ptr as isize == -1_isize { + return Err(io::Error::last_os_error().to_string()); + } + + Self { ptr: ptr as usize, len: mapping_size } + } else { + // Reserve the mapping size. + let ptr = unsafe { + libc::mmap( + ptr::null_mut(), + mapping_size, + libc::PROT_NONE, + libc::MAP_PRIVATE | libc::MAP_ANON, + -1, + 0, + ) + }; + if ptr as isize == -1_isize { + return Err(io::Error::last_os_error().to_string()); + } + + let mut result = Self { ptr: ptr as usize, len: mapping_size }; + + if accessible_size != 0 { + // Commit the accessible size. + result.make_accessible(0, accessible_size)?; + } + + result + }) + } + + /// Create a new `Mmap` pointing to `accessible_size` bytes of page-aligned accessible memory, + /// within a reserved mapping of `mapping_size` bytes. `accessible_size` and `mapping_size` + /// must be native page-size multiples. + #[cfg(target_os = "windows")] + pub fn accessible_reserved( + accessible_size: usize, + mapping_size: usize, + ) -> Result { + use winapi::um::memoryapi::VirtualAlloc; + use winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_NOACCESS, PAGE_READWRITE}; + + let page_size = region::page::size(); + assert_le!(accessible_size, mapping_size); + assert_eq!(mapping_size & (page_size - 1), 0); + assert_eq!(accessible_size & (page_size - 1), 0); + + // VirtualAlloc may return ERROR_INVALID_PARAMETER if the size is zero, + // so just special-case that. + if mapping_size == 0 { + return Ok(Self::new()); + } + + Ok(if accessible_size == mapping_size { + // Allocate a single read-write region at once. + let ptr = unsafe { + VirtualAlloc( + ptr::null_mut(), + mapping_size, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE, + ) + }; + if ptr.is_null() { + return Err(io::Error::last_os_error().to_string()); + } + + Self { ptr: ptr as usize, len: mapping_size } + } else { + // Reserve the mapping size. + let ptr = + unsafe { VirtualAlloc(ptr::null_mut(), mapping_size, MEM_RESERVE, PAGE_NOACCESS) }; + if ptr.is_null() { + return Err(io::Error::last_os_error().to_string()); + } + + let mut result = Self { ptr: ptr as usize, len: mapping_size }; + + if accessible_size != 0 { + // Commit the accessible size. + result.make_accessible(0, accessible_size)?; + } + + result + }) + } + + /// Make the memory starting at `start` and extending for `len` bytes accessible. + /// `start` and `len` must be native page-size multiples and describe a range within + /// `self`'s reserved memory. + #[cfg(not(target_os = "windows"))] + pub fn make_accessible(&mut self, start: usize, len: usize) -> Result<(), String> { + let page_size = region::page::size(); + assert_eq!(start & (page_size - 1), 0); + assert_eq!(len & (page_size - 1), 0); + assert_lt!(len, self.len); + assert_lt!(start, self.len - len); + + // Commit the accessible size. + unsafe { region::protect(self.as_ptr().add(start), len, region::Protection::READ_WRITE) } + .map_err(|e| e.to_string()) + } + + /// Make the memory starting at `start` and extending for `len` bytes accessible. + /// `start` and `len` must be native page-size multiples and describe a range within + /// `self`'s reserved memory. + #[cfg(target_os = "windows")] + pub fn make_accessible(&mut self, start: usize, len: usize) -> Result<(), String> { + use winapi::ctypes::c_void; + use winapi::um::memoryapi::VirtualAlloc; + use winapi::um::winnt::{MEM_COMMIT, PAGE_READWRITE}; + let page_size = region::page::size(); + assert_eq!(start & (page_size - 1), 0); + assert_eq!(len & (page_size - 1), 0); + assert_lt!(len, self.len); + assert_lt!(start, self.len - len); + + // Commit the accessible size. + if unsafe { + VirtualAlloc(self.as_ptr().add(start) as *mut c_void, len, MEM_COMMIT, PAGE_READWRITE) + } + .is_null() + { + return Err(io::Error::last_os_error().to_string()); + } + + Ok(()) + } + + /// Return the allocated memory as a slice of u8. + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.as_ptr(), self.len) } + } + + /// Return the allocated memory as a mutable slice of u8. + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) } + } + + /// Return the allocated memory as a pointer to u8. + pub fn as_ptr(&self) -> *const u8 { + self.ptr as *const u8 + } + + /// Return the allocated memory as a mutable pointer to u8. + pub fn as_mut_ptr(&mut self) -> *mut u8 { + self.ptr as *mut u8 + } + + /// Return the length of the allocated memory. + pub fn len(&self) -> usize { + self.len + } + + /// Return whether any memory has been allocated. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl Drop for Mmap { + #[cfg(not(target_os = "windows"))] + fn drop(&mut self) { + if self.len != 0 { + let r = unsafe { libc::munmap(self.ptr as *mut libc::c_void, self.len) }; + assert_eq!(r, 0, "munmap failed: {}", io::Error::last_os_error()); + } + } + + #[cfg(target_os = "windows")] + fn drop(&mut self) { + if self.len != 0 { + use winapi::ctypes::c_void; + use winapi::um::memoryapi::VirtualFree; + use winapi::um::winnt::MEM_RELEASE; + let r = unsafe { VirtualFree(self.ptr as *mut c_void, 0, MEM_RELEASE) }; + assert_ne!(r, 0); + } + } +} + +fn _assert() { + fn _assert_send_sync() {} + _assert_send_sync::(); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_round_up_to_page_size() { + assert_eq!(round_up_to_page_size(0, 4096), 0); + assert_eq!(round_up_to_page_size(1, 4096), 4096); + assert_eq!(round_up_to_page_size(4096, 4096), 4096); + assert_eq!(round_up_to_page_size(4097, 4096), 8192); + } +} diff --git a/runtime/unc-vm/vm/src/probestack.rs b/runtime/unc-vm/vm/src/probestack.rs new file mode 100644 index 000000000..cba4389d3 --- /dev/null +++ b/runtime/unc-vm/vm/src/probestack.rs @@ -0,0 +1,64 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! This section defines the `PROBESTACK` intrinsic which is used in the +//! implementation of "stack probes" on certain platforms. +//! +//! The purpose of a stack probe is to provide a static guarantee that if a +//! thread has a guard page then a stack overflow is guaranteed to hit that +//! guard page. If a function did not have a stack probe then there's a risk of +//! having a stack frame *larger* than the guard page, so a function call could +//! skip over the guard page entirely and then later hit maybe the heap or +//! another thread, possibly leading to security vulnerabilities such as [The +//! Stack Clash], for example. +//! +//! [The Stack Clash]: https://blog.qualys.com/securitylabs/2017/06/19/the-stack-clash + +// A declaration for the stack probe function in Rust's standard library, for +// catching callstack overflow. +cfg_if::cfg_if! { + if #[cfg(all( + target_os = "windows", + target_env = "msvc", + target_pointer_width = "64" + ))] { + extern "C" { + pub fn __chkstk(); + } + /// The probestack for 64bit Windows when compiled with MSVC (note the double underscore) + pub const PROBESTACK: unsafe extern "C" fn() = __chkstk; + } else if #[cfg(all( + target_os = "windows", + target_env = "msvc", + target_pointer_width = "32" + ))] { + extern "C" { + pub fn _chkstk(); + } + /// The probestack for 32bit Windows when compiled with MSVC (note the singular underscore) + pub const PROBESTACK: unsafe extern "C" fn() = _chkstk; + } else if #[cfg(all(target_os = "windows", target_env = "gnu"))] { + extern "C" { + // ___chkstk (note the triple underscore) is implemented in compiler-builtins/src/x86_64.rs + // by the Rust compiler for the MinGW target + #[cfg(all(target_os = "windows", target_env = "gnu"))] + pub fn ___chkstk(); + } + /// The probestack for Windows when compiled with GNU + pub const PROBESTACK: unsafe extern "C" fn() = ___chkstk; + } else if #[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))] { + // As per + // https://github.com/rust-lang/compiler-builtins/blob/cae3e6ea23739166504f9f9fb50ec070097979d4/src/probestack.rs#L39, + // LLVM only has stack-probe support on x86-64 and x86. Thus, on any other CPU + // architecture, we simply use an empty stack-probe function. + extern "C" fn empty_probestack() {} + /// A default probestack for other architectures + pub const PROBESTACK: unsafe extern "C" fn() = empty_probestack; + } else { + extern "C" { + pub fn __rust_probestack(); + } + /// The probestack based on the Rust probestack + pub static PROBESTACK: unsafe extern "C" fn() = __rust_probestack; + } +} diff --git a/runtime/unc-vm/vm/src/resolver.rs b/runtime/unc-vm/vm/src/resolver.rs new file mode 100644 index 000000000..c71a2ea55 --- /dev/null +++ b/runtime/unc-vm/vm/src/resolver.rs @@ -0,0 +1,321 @@ +use std::sync::Arc; + +use crate::{ImportInitializerFuncPtr, VMExtern, VMFunction, VMGlobal, VMMemory, VMTable}; + +/// The value of an export passed from one instance to another. +#[derive(Debug, Clone)] +pub enum Export { + /// A function export value. + Function(ExportFunction), + + /// A table export value. + Table(VMTable), + + /// A memory export value. + Memory(VMMemory), + + /// A global export value. + Global(VMGlobal), +} + +impl From for VMExtern { + fn from(other: Export) -> Self { + match other { + Export::Function(ExportFunction { vm_function, .. }) => Self::Function(vm_function), + Export::Memory(vm_memory) => Self::Memory(vm_memory), + Export::Table(vm_table) => Self::Table(vm_table), + Export::Global(vm_global) => Self::Global(vm_global), + } + } +} + +impl From for Export { + fn from(other: VMExtern) -> Self { + match other { + VMExtern::Function(vm_function) => { + Self::Function(ExportFunction { vm_function, metadata: None }) + } + VMExtern::Memory(vm_memory) => Self::Memory(vm_memory), + VMExtern::Table(vm_table) => Self::Table(vm_table), + VMExtern::Global(vm_global) => Self::Global(vm_global), + } + } +} + +/// Extra metadata about `ExportFunction`s. +/// +/// The metadata acts as a kind of manual virtual dispatch. We store the +/// user-supplied `WasmerEnv` as a void pointer and have methods on it +/// that have been adapted to accept a void pointer. +/// +/// This struct owns the original `host_env`, thus when it gets dropped +/// it calls the `drop` function on it. +#[derive(Debug, PartialEq)] +pub struct ExportFunctionMetadata { + /// This field is stored here to be accessible by `Drop`. + /// + /// At the time it was added, it's not accessed anywhere outside of + /// the `Drop` implementation. This field is the "master copy" of the env, + /// that is, the original env passed in by the user. Every time we create + /// an `Instance` we clone this with the `host_env_clone_fn` field. + /// + /// Thus, we only bother to store the master copy at all here so that + /// we can free it. + /// + /// See `unc_vm_vm::export::VMFunction::vmctx` for the version of + /// this pointer that is used by the VM when creating an `Instance`. + pub host_env: *mut std::ffi::c_void, + + /// Function pointer to `WasmerEnv::init_with_instance(&mut self, instance: &Instance)`. + /// + /// This function is called to finish setting up the environment after + /// we create the `api::Instance`. + // This one is optional for now because dynamic host envs need the rest + // of this without the init fn + pub import_init_function_ptr: Option, + + /// A function analogous to `Clone::clone` that returns a leaked `Box`. + pub host_env_clone_fn: fn(*mut std::ffi::c_void) -> *mut std::ffi::c_void, + + /// The destructor to free the host environment. + /// + /// # Safety + /// - This function should only be called in when properly synchronized. + /// For example, in the `Drop` implementation of this type. + pub host_env_drop_fn: unsafe fn(*mut std::ffi::c_void), +} + +/// This can be `Send` because `host_env` comes from `WasmerEnv` which is +/// `Send`. Therefore all operations should work on any thread. +unsafe impl Send for ExportFunctionMetadata {} +/// This data may be shared across threads, `drop` is an unsafe function +/// pointer, so care must be taken when calling it. +unsafe impl Sync for ExportFunctionMetadata {} + +impl ExportFunctionMetadata { + /// Create an `ExportFunctionMetadata` type with information about + /// the exported function. + /// + /// # Safety + /// - the `host_env` must be `Send`. + /// - all function pointers must work on any thread. + pub unsafe fn new( + host_env: *mut std::ffi::c_void, + import_init_function_ptr: Option, + host_env_clone_fn: fn(*mut std::ffi::c_void) -> *mut std::ffi::c_void, + host_env_drop_fn: fn(*mut std::ffi::c_void), + ) -> Self { + Self { host_env, import_init_function_ptr, host_env_clone_fn, host_env_drop_fn } + } +} + +// We have to free `host_env` here because we always clone it before using it +// so all the `host_env`s freed at the `Instance` level won't touch the original. +impl Drop for ExportFunctionMetadata { + fn drop(&mut self) { + if !self.host_env.is_null() { + // # Safety + // - This is correct because we know no other references + // to this data can exist if we're dropping it. + unsafe { + (self.host_env_drop_fn)(self.host_env); + } + } + } +} + +/// A function export value with an extra function pointer to initialize +/// host environments. +#[derive(Debug, Clone, PartialEq)] +pub struct ExportFunction { + /// The VM function, containing most of the data. + pub vm_function: VMFunction, + /// Contains functions necessary to create and initialize host envs + /// with each `Instance` as well as being responsible for the + /// underlying memory of the host env. + pub metadata: Option>, +} + +impl From for Export { + fn from(func: ExportFunction) -> Self { + Self::Function(func) + } +} + +impl From for Export { + fn from(table: VMTable) -> Self { + Self::Table(table) + } +} + +impl From for Export { + fn from(memory: VMMemory) -> Self { + Self::Memory(memory) + } +} + +impl From for Export { + fn from(global: VMGlobal) -> Self { + Self::Global(global) + } +} + +/// +/// Import resolver connects imports with available exported values. +pub trait Resolver { + /// Resolves an import a WebAssembly module to an export it's hooked up to. + /// + /// The `index` provided is the index of the import in the wasm module + /// that's being resolved. For example 1 means that it's the second import + /// listed in the wasm module. + /// + /// The `module` and `field` arguments provided are the module/field names + /// listed on the import itself. + /// + /// # Notes: + /// + /// The index is useful because some WebAssembly modules may rely on that + /// for resolving ambiguity in their imports. Such as: + /// ```ignore + /// (module + /// (import "" "" (func)) + /// (import "" "" (func (param i32) (result i32))) + /// ) + /// ``` + fn resolve(&self, _index: u32, module: &str, field: &str) -> Option; +} + +/// Import resolver connects imports with available exported values. +/// +/// This is a specific subtrait for [`Resolver`] for those users who don't +/// care about the `index`, but only about the `module` and `field` for +/// the resolution. +pub trait NamedResolver { + /// Resolves an import a WebAssembly module to an export it's hooked up to. + /// + /// It receives the `module` and `field` names and return the [`Export`] in + /// case it's found. + fn resolve_by_name(&self, module: &str, field: &str) -> Option; +} + +// All NamedResolvers should extend `Resolver`. +impl Resolver for T { + /// By default this method will be calling [`NamedResolver::resolve_by_name`], + /// dismissing the provided `index`. + fn resolve(&self, _index: u32, module: &str, field: &str) -> Option { + self.resolve_by_name(module, field) + } +} + +impl NamedResolver for &T { + fn resolve_by_name(&self, module: &str, field: &str) -> Option { + (**self).resolve_by_name(module, field) + } +} + +impl NamedResolver for Box { + fn resolve_by_name(&self, module: &str, field: &str) -> Option { + (**self).resolve_by_name(module, field) + } +} + +impl NamedResolver for () { + /// Always returns `None`. + fn resolve_by_name(&self, _module: &str, _field: &str) -> Option { + None + } +} + +/// `Resolver` implementation that always resolves to `None`. Equivalent to `()`. +pub struct NullResolver {} + +impl Resolver for NullResolver { + fn resolve(&self, _idx: u32, _module: &str, _field: &str) -> Option { + None + } +} + +/// A [`Resolver`] that links two resolvers together in a chain. +pub struct NamedResolverChain { + a: A, + b: B, +} + +/// A trait for chaining resolvers together. +/// +/// ``` +/// # use unc_vm_vm::{ChainableNamedResolver, NamedResolver}; +/// # fn chainable_test(imports1: A, imports2: B) +/// # where A: NamedResolver + Sized + Send + Sync, +/// # B: NamedResolver + Sized + Send + Sync, +/// # { +/// // override duplicates with imports from `imports2` +/// imports1.chain_front(imports2); +/// # } +/// ``` +pub trait ChainableNamedResolver: NamedResolver + Sized + Send + Sync { + /// Chain a resolver in front of the current resolver. + /// + /// This will cause the second resolver to override the first. + /// + /// ``` + /// # use unc_vm_vm::{ChainableNamedResolver, NamedResolver}; + /// # fn chainable_test(imports1: A, imports2: B) + /// # where A: NamedResolver + Sized + Send + Sync, + /// # B: NamedResolver + Sized + Send + Sync, + /// # { + /// // override duplicates with imports from `imports2` + /// imports1.chain_front(imports2); + /// # } + /// ``` + fn chain_front(self, other: U) -> NamedResolverChain + where + U: NamedResolver + Send + Sync, + { + NamedResolverChain { a: other, b: self } + } + + /// Chain a resolver behind the current resolver. + /// + /// This will cause the first resolver to override the second. + /// + /// ``` + /// # use unc_vm_vm::{ChainableNamedResolver, NamedResolver}; + /// # fn chainable_test(imports1: A, imports2: B) + /// # where A: NamedResolver + Sized + Send + Sync, + /// # B: NamedResolver + Sized + Send + Sync, + /// # { + /// // override duplicates with imports from `imports1` + /// imports1.chain_back(imports2); + /// # } + /// ``` + fn chain_back(self, other: U) -> NamedResolverChain + where + U: NamedResolver + Send + Sync, + { + NamedResolverChain { a: self, b: other } + } +} + +// We give these chain methods to all types implementing NamedResolver +impl ChainableNamedResolver for T {} + +impl NamedResolver for NamedResolverChain +where + A: NamedResolver + Send + Sync, + B: NamedResolver + Send + Sync, +{ + fn resolve_by_name(&self, module: &str, field: &str) -> Option { + self.a.resolve_by_name(module, field).or_else(|| self.b.resolve_by_name(module, field)) + } +} + +impl Clone for NamedResolverChain +where + A: NamedResolver + Clone + Send + Sync, + B: NamedResolver + Clone + Send + Sync, +{ + fn clone(&self) -> Self { + Self { a: self.a.clone(), b: self.b.clone() } + } +} diff --git a/runtime/unc-vm/vm/src/sig_registry.rs b/runtime/unc-vm/vm/src/sig_registry.rs new file mode 100644 index 000000000..f9e509327 --- /dev/null +++ b/runtime/unc-vm/vm/src/sig_registry.rs @@ -0,0 +1,72 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Implement a registry of function signatures, for fast indirect call +//! signature checking. + +use unc_vm_types::FunctionType; +use std::collections::{hash_map, HashMap}; +use std::convert::TryFrom; + +/// An index into the shared signature registry, usable for checking signatures +/// at indirect calls. +#[repr(C)] +#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] +pub struct VMSharedSignatureIndex(u32); + +impl VMSharedSignatureIndex { + /// Create a new `VMSharedSignatureIndex`. + pub fn new(value: u32) -> Self { + Self(value) + } +} + +/// WebAssembly requires that the caller and callee signatures in an indirect +/// call must match. To implement this efficiently, keep a registry of all +/// signatures, shared by all instances, so that call sites can just do an +/// index comparison. +#[derive(Debug)] +pub struct SignatureRegistry { + type_to_index: HashMap, + index_to_data: Vec, +} + +impl SignatureRegistry { + /// Create a new `SignatureRegistry`. + pub fn new() -> Self { + Self { type_to_index: HashMap::new(), index_to_data: Vec::new() } + } + + /// Register a signature and return its unique index. + pub fn register(&mut self, sig: FunctionType) -> VMSharedSignatureIndex { + let len = self.index_to_data.len(); + // TODO(0-copy): this. should. not. allocate. (and take FunctionTypeRef as a parameter) + // + // This is pretty hard to avoid, however. In order to implement bijective map, we'd want + // a `Rc`, but indexing into a map keyed by `Rc` with + // `FunctionTypeRef` is… not possible given the current API either. + // + // Consider `transmute` or `hashbrown`'s raw_entry. + match self.type_to_index.entry(sig.clone()) { + hash_map::Entry::Occupied(entry) => *entry.get(), + hash_map::Entry::Vacant(entry) => { + debug_assert!( + u32::try_from(len).is_ok(), + "invariant: can't have more than 2³²-1 signatures!" + ); + let sig_id = VMSharedSignatureIndex::new(u32::try_from(len).unwrap()); + entry.insert(sig_id); + self.index_to_data.push(sig); + sig_id + } + } + } + + /// Looks up a shared signature index within this registry. + /// + /// Note that for this operation to be semantically correct the `idx` must + /// have previously come from a call to `register` of this same object. + pub fn lookup(&self, idx: VMSharedSignatureIndex) -> Option<&FunctionType> { + self.index_to_data.get(idx.0 as usize) + } +} diff --git a/runtime/unc-vm/vm/src/table.rs b/runtime/unc-vm/vm/src/table.rs new file mode 100644 index 000000000..5db846f5f --- /dev/null +++ b/runtime/unc-vm/vm/src/table.rs @@ -0,0 +1,391 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Memory management for tables. +//! +//! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories. + +use crate::func_data_registry::VMFuncRef; +use crate::trap::{Trap, TrapCode}; +use crate::vmcontext::VMTableDefinition; +use crate::VMExternRef; +use unc_vm_types::{ExternRef, TableType, Type as ValType}; +use std::borrow::BorrowMut; +use std::cell::UnsafeCell; +use std::convert::TryFrom; +use std::fmt; +use std::ptr::NonNull; +use std::sync::Mutex; + +/// Implementation styles for WebAssembly tables. +#[derive(Debug, Clone, Hash, PartialEq, Eq, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] +pub enum TableStyle { + /// Signatures are stored in the table and checked in the caller. + CallerChecksSignature, +} + +/// Trait for implementing the interface of a Wasm table. +pub trait Table: fmt::Debug + Send + Sync { + /// Returns the style for this Table. + fn style(&self) -> &TableStyle; + + /// Returns the type for this Table. + fn ty(&self) -> &TableType; + + /// Returns the number of allocated elements. + fn size(&self) -> u32; + + /// Grow table by the specified amount of elements. + /// + /// Returns `None` if table can't be grown by the specified amount + /// of elements, otherwise returns the previous size of the table. + fn grow(&self, delta: u32, init_value: TableElement) -> Option; + + /// Get reference to the specified element. + /// + /// Returns `None` if the index is out of bounds. + fn get(&self, index: u32) -> Option; + + /// Set reference to the specified element. + /// + /// # Errors + /// + /// Returns an error if the index is out of bounds. + fn set(&self, index: u32, reference: TableElement) -> Result<(), Trap>; + + /// Return a `VMTableDefinition` for exposing the table to compiled wasm code. + fn vmtable(&self) -> NonNull; + + /// Copy `len` elements from `src_table[src_index..]` into `dst_table[dst_index..]`. + /// + /// # Errors + /// + /// Returns an error if the range is out of bounds of either the source or + /// destination tables. + fn copy( + &self, + src_table: &dyn Table, + dst_index: u32, + src_index: u32, + len: u32, + ) -> Result<(), Trap> { + // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-copy + + if src_index.checked_add(len).map_or(true, |n| n > src_table.size()) { + return Err(Trap::lib(TrapCode::TableAccessOutOfBounds)); + } + + if dst_index.checked_add(len).map_or(true, |m| m > self.size()) { + return Err(Trap::lib(TrapCode::TableAccessOutOfBounds)); + } + + let srcs = src_index..src_index + len; + let dsts = dst_index..dst_index + len; + + // Note on the unwraps: the bounds check above means that these will + // never panic. + // + // TODO: investigate replacing this get/set loop with a `memcpy`. + if dst_index <= src_index { + for (s, d) in (srcs).zip(dsts) { + self.set(d, src_table.get(s).unwrap())?; + } + } else { + for (s, d) in srcs.rev().zip(dsts.rev()) { + self.set(d, src_table.get(s).unwrap())?; + } + } + + Ok(()) + } +} + +/// A reference stored in a table. Can be either an externref or a funcref. +#[derive(Debug, Clone)] +pub enum TableElement { + /// Opaque pointer to arbitrary host data. + // Note: we use `ExternRef` instead of `VMExternRef` here to ensure that we don't + // leak by not dec-refing on failure types. + ExternRef(ExternRef), + /// Pointer to function: contains enough information to call it. + FuncRef(VMFuncRef), +} + +impl From for RawTableElement { + fn from(other: TableElement) -> Self { + match other { + TableElement::ExternRef(extern_ref) => Self { extern_ref: extern_ref.into() }, + TableElement::FuncRef(func_ref) => Self { func_ref }, + } + } +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union RawTableElement { + pub(crate) extern_ref: VMExternRef, + pub(crate) func_ref: VMFuncRef, +} + +#[cfg(test)] +#[test] +fn table_element_size_test() { + use std::mem::size_of; + assert_eq!(size_of::(), size_of::()); + assert_eq!(size_of::(), size_of::()); +} + +impl fmt::Debug for RawTableElement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("RawTableElement").finish() + } +} + +impl Default for RawTableElement { + fn default() -> Self { + Self { func_ref: VMFuncRef::null() } + } +} + +impl Default for TableElement { + fn default() -> Self { + Self::FuncRef(VMFuncRef::null()) + } +} + +/// A table instance. +#[derive(Debug)] +pub struct LinearTable { + // TODO: we can remove the mutex by using atomic swaps and preallocating the max table size + vec: Mutex>, + maximum: Option, + /// The WebAssembly table description. + table: TableType, + /// Our chosen implementation style. + style: TableStyle, + vm_table_definition: VMTableDefinitionOwnership, +} + +/// A type to help manage who is responsible for the backing table of the +/// `VMTableDefinition`. +#[derive(Debug)] +enum VMTableDefinitionOwnership { + /// The `VMTableDefinition` is owned by the `Instance` and we should use + /// its table. This is how a local table that's exported should be stored. + VMOwned(NonNull), + /// The `VMTableDefinition` is owned by the host and we should manage its + /// table. This is how an imported table that doesn't come from another + /// Wasm module should be stored. + HostOwned(Box>), +} + +/// This is correct because there is no thread-specific data tied to this type. +unsafe impl Send for LinearTable {} +/// This is correct because all internal mutability is protected by a mutex. +unsafe impl Sync for LinearTable {} + +impl LinearTable { + /// Create a new linear table instance with specified minimum and maximum number of elements. + /// + /// This creates a `LinearTable` with metadata owned by a VM, pointed to by + /// `vm_table_location`: this can be used to create a local table. + pub fn new(table: &TableType, style: &TableStyle) -> Result { + unsafe { Self::new_inner(table, style, None) } + } + + /// Create a new linear table instance with specified minimum and maximum number of elements. + /// + /// This creates a `LinearTable` with metadata owned by a VM, pointed to by + /// `vm_table_location`: this can be used to create a local table. + /// + /// # Safety + /// - `vm_table_location` must point to a valid location in VM memory. + pub unsafe fn from_definition( + table: &TableType, + style: &TableStyle, + vm_table_location: NonNull, + ) -> Result { + Self::new_inner(table, style, Some(vm_table_location)) + } + + /// Create a new `LinearTable` with either self-owned or VM owned metadata. + unsafe fn new_inner( + table: &TableType, + style: &TableStyle, + vm_table_location: Option>, + ) -> Result { + match table.ty { + ValType::FuncRef | ValType::ExternRef => (), + ty => return Err(format!("tables of types other than funcref or externref ({})", ty)), + }; + if let Some(max) = table.maximum { + if max < table.minimum { + return Err(format!( + "Table minimum ({}) is larger than maximum ({})!", + table.minimum, max + )); + } + } + let table_minimum = usize::try_from(table.minimum) + .map_err(|_| "Table minimum is bigger than usize".to_string())?; + let mut vec = vec![RawTableElement::default(); table_minimum]; + let base = vec.as_mut_ptr(); + match style { + TableStyle::CallerChecksSignature => Ok(Self { + vec: Mutex::new(vec), + maximum: table.maximum, + table: *table, + style: style.clone(), + vm_table_definition: if let Some(table_loc) = vm_table_location { + { + let mut ptr = table_loc; + let td = ptr.as_mut(); + td.base = base as _; + td.current_elements = table_minimum as _; + } + VMTableDefinitionOwnership::VMOwned(table_loc) + } else { + VMTableDefinitionOwnership::HostOwned(Box::new(UnsafeCell::new( + VMTableDefinition { base: base as _, current_elements: table_minimum as _ }, + ))) + }, + }), + } + } + + /// Get the `VMTableDefinition`. + /// + /// # Safety + /// - You must ensure that you have mutually exclusive access before calling + /// this function. You can get this by locking the `vec` mutex. + unsafe fn get_vm_table_definition(&self) -> NonNull { + match &self.vm_table_definition { + VMTableDefinitionOwnership::VMOwned(ptr) => *ptr, + VMTableDefinitionOwnership::HostOwned(boxed_ptr) => { + NonNull::new_unchecked(boxed_ptr.get()) + } + } + } +} + +impl Table for LinearTable { + /// Returns the type for this Table. + fn ty(&self) -> &TableType { + &self.table + } + + /// Returns the style for this Table. + fn style(&self) -> &TableStyle { + &self.style + } + + /// Returns the number of allocated elements. + fn size(&self) -> u32 { + // TODO: investigate this function for race conditions + unsafe { + let td_ptr = self.get_vm_table_definition(); + let td = td_ptr.as_ref(); + td.current_elements + } + } + + /// Grow table by the specified amount of elements. + /// + /// Returns `None` if table can't be grown by the specified amount + /// of elements, otherwise returns the previous size of the table. + fn grow(&self, delta: u32, init_value: TableElement) -> Option { + let mut vec_guard = self.vec.lock().unwrap(); + let vec = vec_guard.borrow_mut(); + let size = self.size(); + let new_len = size.checked_add(delta)?; + if self.maximum.map_or(false, |max| new_len > max) { + return None; + } + if new_len == size { + debug_assert_eq!(delta, 0); + return Some(size); + } + + // Update the ref count + let element = match init_value { + TableElement::ExternRef(extern_ref) => { + let extern_ref: VMExternRef = extern_ref.into(); + // We reduce the amount we increment by because `into` prevents + // dropping `init_value` (which is a caller-inc'd ref). + if let Some(val) = (new_len as usize).checked_sub(size as usize + 1) { + extern_ref.ref_inc_by(val); + } + RawTableElement { extern_ref } + } + TableElement::FuncRef(func_ref) => RawTableElement { func_ref }, + }; + + vec.resize(usize::try_from(new_len).unwrap(), element); + + // update table definition + unsafe { + let mut td_ptr = self.get_vm_table_definition(); + let td = td_ptr.as_mut(); + td.current_elements = new_len; + td.base = vec.as_mut_ptr() as _; + } + Some(size) + } + + /// Get reference to the specified element. + /// + /// Returns `None` if the index is out of bounds. + fn get(&self, index: u32) -> Option { + let vec_guard = self.vec.lock().unwrap(); + let raw_data = vec_guard.get(index as usize).cloned()?; + Some(match self.table.ty { + ValType::ExternRef => { + TableElement::ExternRef(unsafe { raw_data.extern_ref.ref_clone() }.into()) + } + ValType::FuncRef => TableElement::FuncRef(unsafe { raw_data.func_ref }), + _ => todo!("getting invalid type from table, handle this error"), + }) + } + + /// Set reference to the specified element. + /// + /// # Errors + /// + /// Returns an error if the index is out of bounds. + fn set(&self, index: u32, reference: TableElement) -> Result<(), Trap> { + let mut vec_guard = self.vec.lock().unwrap(); + let vec = vec_guard.borrow_mut(); + match vec.get_mut(index as usize) { + Some(slot) => { + match (self.table.ty, reference) { + (ValType::ExternRef, TableElement::ExternRef(extern_ref)) => { + let extern_ref = extern_ref.into(); + unsafe { + let elem = &mut *slot; + elem.extern_ref.ref_drop(); + elem.extern_ref = extern_ref + } + } + (ValType::FuncRef, r @ TableElement::FuncRef(_)) => { + let element_data = r.into(); + *slot = element_data; + } + // This path should never be hit by the generated code due to Wasm + // validation. + (ty, v) => { + panic!("Attempted to set a table of type {} with the value {:?}", ty, v) + } + }; + + Ok(()) + } + None => Err(Trap::lib(TrapCode::TableAccessOutOfBounds)), + } + } + + /// Return a `VMTableDefinition` for exposing the table to compiled wasm code. + fn vmtable(&self) -> NonNull { + let _vec_guard = self.vec.lock().unwrap(); + unsafe { self.get_vm_table_definition() } + } +} diff --git a/runtime/unc-vm/vm/src/trap/handlers.c b/runtime/unc-vm/vm/src/trap/handlers.c new file mode 100644 index 000000000..d3ecaa16e --- /dev/null +++ b/runtime/unc-vm/vm/src/trap/handlers.c @@ -0,0 +1,66 @@ +// This file contains partial code from other sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +#include +#include +#if defined(CFG_TARGET_OS_MACOS) +#include +#include +#include +#endif +// Note that `sigsetjmp` and `siglongjmp` are used here where possible to +// explicitly pass a 0 argument to `sigsetjmp` that we don't need to preserve +// the process signal mask. This should make this call a bit faster b/c it +// doesn't need to touch the kernel signal handling routines. +// In case of macOS, stackoverflow +#if defined(CFG_TARGET_OS_WINDOWS) +// On Windows, default setjmp/longjmp sequence will try to unwind the stack +// it's fine most of the time, but not for JIT'd code that may not respect stack ordring +// Using a special setjmp here, with NULL as second parameter to disable that behaviour +// and have a regular simple setjmp/longjmp sequence +#ifdef __MINGW32__ +// MINGW64 doesn't expose the __intrinsic_setjmp function, but a similar _setjump instead +#define platform_setjmp(buf) _setjmp(buf, NULL) +#else +#define platform_setjmp(buf) __intrinsic_setjmp(buf, NULL) +#endif +#define platform_longjmp(buf, arg) longjmp(buf, arg) +#define platform_jmp_buf jmp_buf +#elif defined(CFG_TARGET_OS_MACOS) +// TODO: This is not the most performant, since it adds overhead when calling functions +// https://github.com/wasmerio/wasmer/issues/2562 +#define platform_setjmp(buf) sigsetjmp(buf, 1) +#define platform_longjmp(buf, arg) siglongjmp(buf, arg) +#define platform_jmp_buf sigjmp_buf +#else +#define platform_setjmp(buf) sigsetjmp(buf, 0) +#define platform_longjmp(buf, arg) siglongjmp(buf, arg) +#define platform_jmp_buf sigjmp_buf +#endif + +int unc_vm_register_setjmp( + void **buf_storage, + void (*body)(void*), + void *payload) { + #if 0 && defined(CFG_TARGET_OS_MACOS) + // Enable this block to ba able to debug Segfault with lldb + // This will mask the EXC_BAD_ACCESS from lldb... + static int allow_bad_access = 0; + if(!allow_bad_access) { + allow_bad_access = 1; + task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, 0); + } + #endif + platform_jmp_buf buf; + if (platform_setjmp(buf) != 0) { + return 0; + } + *buf_storage = &buf; + body(payload); + return 1; +} + +void unc_vm_unwind(void *JmpBuf) { + platform_jmp_buf *buf = (platform_jmp_buf*) JmpBuf; + platform_longjmp(*buf, 1); +} diff --git a/runtime/unc-vm/vm/src/trap/mod.rs b/runtime/unc-vm/vm/src/trap/mod.rs new file mode 100644 index 000000000..042864650 --- /dev/null +++ b/runtime/unc-vm/vm/src/trap/mod.rs @@ -0,0 +1,14 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! This is the module that facilitates the usage of Traps +//! in Wasmer Runtime +mod trapcode; +pub mod traphandlers; + +pub use trapcode::TrapCode; +pub use traphandlers::resume_panic; +pub use traphandlers::{ + catch_traps, catch_traps_with_result, unc_vm_call_trampoline, raise_lib_trap, raise_user_trap, + TlsRestore, Trap, +}; diff --git a/runtime/unc-vm/vm/src/trap/trapcode.rs b/runtime/unc-vm/vm/src/trap/trapcode.rs new file mode 100644 index 000000000..5f929a14f --- /dev/null +++ b/runtime/unc-vm/vm/src/trap/trapcode.rs @@ -0,0 +1,172 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Trap codes describing the reason for a trap. + +use core::fmt::{self, Display, Formatter}; +use core::str::FromStr; +use thiserror::Error; + +/// A trap code describing the reason for a trap. +/// +/// All trap instructions have an explicit trap code. +#[derive( + Clone, + Copy, + PartialEq, + Eq, + Debug, + Hash, + Error, + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, +)] +#[repr(u32)] +pub enum TrapCode { + /// The current stack space was exhausted. + /// + /// On some platforms, a stack overflow may also be indicated by a segmentation fault from the + /// stack guard page. + StackOverflow = 0, + + /// A `heap_addr` instruction detected an out-of-bounds error. + /// + /// Note that not all out-of-bounds heap accesses are reported this way; + /// some are detected by a segmentation fault on the heap unmapped or + /// offset-guard pages. + HeapAccessOutOfBounds = 1, + + /// A `heap_addr` instruction was misaligned. + HeapMisaligned = 2, + + /// A `table_addr` instruction detected an out-of-bounds error. + TableAccessOutOfBounds = 3, + + /// Other bounds checking error. + OutOfBounds = 4, + + /// Indirect call to a null table entry. + IndirectCallToNull = 5, + + /// Signature mismatch on indirect call. + BadSignature = 6, + + /// An integer arithmetic operation caused an overflow. + IntegerOverflow = 7, + + /// An integer division by zero. + IntegerDivisionByZero = 8, + + /// Failed float-to-int conversion. + BadConversionToInteger = 9, + + /// Code that was supposed to have been unreachable was reached. + UnreachableCodeReached = 10, + + /// An atomic memory access was attempted with an unaligned pointer. + UnalignedAtomic = 11, + + /// Hit the gas limit. + GasExceeded = 12, +} + +impl TrapCode { + /// Gets the message for this trap code + pub fn message(&self) -> &str { + match self { + Self::StackOverflow => "call stack exhausted", + Self::HeapAccessOutOfBounds => "out of bounds memory access", + Self::HeapMisaligned => "misaligned heap", + Self::TableAccessOutOfBounds => "undefined element: out of bounds table access", + Self::OutOfBounds => "out of bounds", + Self::IndirectCallToNull => "uninitialized element", + Self::BadSignature => "indirect call type mismatch", + Self::IntegerOverflow => "integer overflow", + Self::IntegerDivisionByZero => "integer divide by zero", + Self::BadConversionToInteger => "invalid conversion to integer", + Self::UnreachableCodeReached => "unreachable", + Self::UnalignedAtomic => "unaligned atomic access", + Self::GasExceeded => "gas limit exceeded", + } + } +} + +impl Display for TrapCode { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let identifier = match *self { + Self::StackOverflow => "stk_ovf", + Self::HeapAccessOutOfBounds => "heap_get_oob", + Self::HeapMisaligned => "heap_misaligned", + Self::TableAccessOutOfBounds => "table_get_oob", + Self::OutOfBounds => "oob", + Self::IndirectCallToNull => "icall_null", + Self::BadSignature => "bad_sig", + Self::IntegerOverflow => "int_ovf", + Self::IntegerDivisionByZero => "int_divz", + Self::BadConversionToInteger => "bad_toint", + Self::UnreachableCodeReached => "unreachable", + Self::UnalignedAtomic => "unalign_atom", + Self::GasExceeded => "out_of_gas", + }; + f.write_str(identifier) + } +} + +impl FromStr for TrapCode { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "stk_ovf" => Ok(Self::StackOverflow), + "heap_get_oob" => Ok(Self::HeapAccessOutOfBounds), + "heap_misaligned" => Ok(Self::HeapMisaligned), + "table_get_oob" => Ok(Self::TableAccessOutOfBounds), + "oob" => Ok(Self::OutOfBounds), + "icall_null" => Ok(Self::IndirectCallToNull), + "bad_sig" => Ok(Self::BadSignature), + "int_ovf" => Ok(Self::IntegerOverflow), + "int_divz" => Ok(Self::IntegerDivisionByZero), + "bad_toint" => Ok(Self::BadConversionToInteger), + "unreachable" => Ok(Self::UnreachableCodeReached), + "unalign_atom" => Ok(Self::UnalignedAtomic), + _ => Err(()), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // Everything but user-defined codes. + const CODES: [TrapCode; 12] = [ + TrapCode::StackOverflow, + TrapCode::HeapAccessOutOfBounds, + TrapCode::HeapMisaligned, + TrapCode::TableAccessOutOfBounds, + TrapCode::OutOfBounds, + TrapCode::IndirectCallToNull, + TrapCode::BadSignature, + TrapCode::IntegerOverflow, + TrapCode::IntegerDivisionByZero, + TrapCode::BadConversionToInteger, + TrapCode::UnreachableCodeReached, + TrapCode::UnalignedAtomic, + ]; + + #[test] + fn display() { + for r in &CODES { + let tc = *r; + assert_eq!(tc.to_string().parse(), Ok(tc)); + } + assert_eq!("bogus".parse::(), Err(())); + + // assert_eq!(TrapCode::User(17).to_string(), "user17"); + // assert_eq!("user22".parse(), Ok(TrapCode::User(22))); + assert_eq!("user".parse::(), Err(())); + assert_eq!("user-1".parse::(), Err(())); + assert_eq!("users".parse::(), Err(())); + } +} diff --git a/runtime/unc-vm/vm/src/trap/traphandlers.rs b/runtime/unc-vm/vm/src/trap/traphandlers.rs new file mode 100644 index 000000000..034858317 --- /dev/null +++ b/runtime/unc-vm/vm/src/trap/traphandlers.rs @@ -0,0 +1,413 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! WebAssembly trap handling, which is built on top of the lower-level +//! signalhandling mechanisms. + +use super::trapcode::TrapCode; +use crate::vmcontext::{VMFunctionBody, VMFunctionEnvironment, VMTrampoline}; +use backtrace::Backtrace; +use std::any::Any; +use std::cell::{Cell, UnsafeCell}; +use std::error::Error; +use std::mem::{self, MaybeUninit}; +use std::ptr; +pub use tls::TlsRestore; + +extern "C" { + fn unc_vm_register_setjmp( + jmp_buf: *mut *const u8, + callback: extern "C" fn(*mut u8), + payload: *mut u8, + ) -> i32; + fn unc_vm_unwind(jmp_buf: *const u8) -> !; +} + +/// Raises a user-defined trap immediately. +/// +/// This function performs as-if a wasm trap was just executed, only the trap +/// has a dynamic payload associated with it which is user-provided. This trap +/// payload is then returned from `catch_traps` below. +/// +/// # Safety +/// +/// Only safe to call when wasm code is on the stack, aka `catch_traps` must +/// have been previous called and not yet returned. +/// Additionally no Rust destructors may be on the stack. +/// They will be skipped and not executed. +pub unsafe fn raise_user_trap(data: Box) -> ! { + tls::with(|info| info.unwrap().unwind_with(UnwindReason::UserTrap(data))) +} + +/// Raises a trap from inside library code immediately. +/// +/// This function performs as-if a wasm trap was just executed. This trap +/// payload is then returned from `catch_traps` below. +/// +/// # Safety +/// +/// Only safe to call when wasm code is on the stack, aka `catch_traps` must +/// have been previous called and not yet returned. +/// Additionally no Rust destructors may be on the stack. +/// They will be skipped and not executed. +pub unsafe fn raise_lib_trap(trap: Trap) -> ! { + tls::with(|info| info.unwrap().unwind_with(UnwindReason::LibTrap(trap))) +} + +/// Carries a Rust panic across wasm code and resumes the panic on the other +/// side. +/// +/// # Safety +/// +/// Only safe to call when wasm code is on the stack, aka `catch_traps` must +/// have been previously called and not returned. Additionally no Rust destructors may be on the +/// stack. They will be skipped and not executed. +pub unsafe fn resume_panic(payload: Box) -> ! { + tls::with(|info| info.unwrap().unwind_with(UnwindReason::Panic(payload))) +} + +/// Stores trace message with backtrace. +#[derive(Debug)] +pub enum Trap { + /// A user-raised trap through `raise_user_trap`. + User(Box), + + /// A trap raised from the Wasm generated code + /// + /// Note: this trap is deterministic (assuming a deterministic host implementation) + Wasm { + /// The program counter in generated code where this trap happened. + pc: usize, + /// Native stack backtrace at the time the trap occurred + backtrace: Backtrace, + /// Optional trapcode associated to the signal that caused the trap + signal_trap: Option, + }, + + /// A trap raised from a wasm libcall + /// + /// Note: this trap is deterministic (assuming a deterministic host implementation) + Lib { + /// Code of the trap. + trap_code: TrapCode, + /// Native stack backtrace at the time the trap occurred + backtrace: Backtrace, + }, + + /// A trap indicating that the runtime was unable to allocate sufficient memory. + /// + /// Note: this trap is nondeterministic, since it depends on the host system. + OOM { + /// Native stack backtrace at the time the OOM occurred + backtrace: Backtrace, + }, +} + +impl Trap { + /// Construct a new Wasm trap with the given source location and backtrace. + /// + /// Internally saves a backtrace when constructed. + pub fn wasm(pc: usize, backtrace: Backtrace, signal_trap: Option) -> Self { + Self::Wasm { pc, backtrace, signal_trap } + } + + /// Construct a new Wasm trap with the given trap code. + /// + /// Internally saves a backtrace when constructed. + pub fn lib(trap_code: TrapCode) -> Self { + let backtrace = Backtrace::new_unresolved(); + Self::Lib { trap_code, backtrace } + } + + /// Construct a new OOM trap with the given source location and trap code. + /// + /// Internally saves a backtrace when constructed. + pub fn oom() -> Self { + let backtrace = Backtrace::new_unresolved(); + Self::OOM { backtrace } + } +} + +/// Call the VM function pointed to by `callee`. +/// +/// * `callee_env` - the function environment +/// * `trampoline` - the jit-generated trampoline whose ABI takes 3 values, the +/// callee funcenv, the `callee` argument below, and then the `values_vec` argument. +/// * `callee` - the 2nd argument to the `trampoline` function +/// * `values_vec` - points to a buffer which holds the incoming arguments, and to +/// which the outgoing return values will be written. +/// +/// Prefer invoking this via `Instance::invoke_trampoline`. +/// +/// # Safety +/// +/// Wildly unsafe because it calls raw function pointers and reads/writes raw +/// function pointers. +pub unsafe fn unc_vm_call_trampoline( + callee_env: VMFunctionEnvironment, + trampoline: VMTrampoline, + callee: *const VMFunctionBody, + values_vec: *mut u8, +) -> Result<(), Trap> { + catch_traps(|| { + mem::transmute::<_, extern "C" fn(VMFunctionEnvironment, *const VMFunctionBody, *mut u8)>( + trampoline, + )(callee_env, callee, values_vec); + }) +} + +/// Catches any wasm traps that happen within the execution of `closure`, +/// returning them as a `Result`. +/// +/// # Safety +/// +/// Soundness must not depend on `closure` destructors being run. +pub unsafe fn catch_traps(mut closure: F) -> Result<(), Trap> +where + F: FnMut(), +{ + return CallThreadState::new().with(|cx| { + unc_vm_register_setjmp( + cx.jmp_buf.as_ptr(), + call_closure::, + &mut closure as *mut F as *mut u8, + ) + }); + + extern "C" fn call_closure(payload: *mut u8) + where + F: FnMut(), + { + unsafe { (*(payload as *mut F))() } + } +} + +/// Catches any wasm traps that happen within the execution of `closure`, +/// returning them as a `Result`, with the closure contents. +/// +/// The main difference from this method and `catch_traps`, is that is able +/// to return the results from the closure. +/// +/// # Safety +/// +/// Check [`catch_traps`]. +pub unsafe fn catch_traps_with_result(mut closure: F) -> Result +where + F: FnMut() -> R, +{ + let mut global_results = MaybeUninit::::uninit(); + catch_traps(|| { + global_results.as_mut_ptr().write(closure()); + })?; + Ok(global_results.assume_init()) +} + +/// Temporary state stored on the stack which is registered in the `tls` module +/// below for calls into wasm. +pub struct CallThreadState { + unwind: UnsafeCell>, + jmp_buf: Cell<*const u8>, + prev: Cell, +} + +enum UnwindReason { + /// A panic caused by the host + Panic(Box), + /// A custom error triggered by the user + UserTrap(Box), + /// A Trap triggered by a wasm libcall + LibTrap(Trap), + /// A trap caused by the Wasm generated code + WasmTrap { backtrace: Backtrace, pc: usize, signal_trap: Option }, +} + +impl<'a> CallThreadState { + #[inline] + fn new() -> Self { + Self { + unwind: UnsafeCell::new(MaybeUninit::uninit()), + jmp_buf: Cell::new(ptr::null()), + prev: Cell::new(ptr::null()), + } + } + + fn with(self, closure: impl FnOnce(&Self) -> i32) -> Result<(), Trap> { + let ret = tls::set(&self, || closure(&self))?; + if ret != 0 { + return Ok(()); + } + // We will only reach this path if ret == 0. And that will + // only happen if a trap did happen. As such, it's safe to + // assume that the `unwind` field is already initialized + // at this moment. + match unsafe { (*self.unwind.get()).as_ptr().read() } { + UnwindReason::UserTrap(data) => Err(Trap::User(data)), + UnwindReason::LibTrap(trap) => Err(trap), + UnwindReason::WasmTrap { backtrace, pc, signal_trap } => { + Err(Trap::wasm(pc, backtrace, signal_trap)) + } + UnwindReason::Panic(panic) => std::panic::resume_unwind(panic), + } + } + + fn unwind_with(&self, reason: UnwindReason) -> ! { + unsafe { + (*self.unwind.get()).as_mut_ptr().write(reason); + unc_vm_unwind(self.jmp_buf.get()); + } + } +} + +// A private inner module for managing the TLS state that we require across +// calls in wasm. The WebAssembly code is called from C++ and then a trap may +// happen which requires us to read some contextual state to figure out what to +// do with the trap. This `tls` module is used to persist that information from +// the caller to the trap site. +mod tls { + use super::CallThreadState; + use crate::Trap; + use std::mem; + use std::ptr; + + pub use raw::Ptr; + + // An even *more* inner module for dealing with TLS. This actually has the + // thread local variable and has functions to access the variable. + // + // Note that this is specially done to fully encapsulate that the accessors + // for tls must not be inlined. Wasmer's async support will employ stack + // switching which can resume execution on different OS threads. This means + // that borrows of our TLS pointer must never live across accesses because + // otherwise the access may be split across two threads and cause unsafety. + // + // This also means that extra care is taken by the runtime to save/restore + // these TLS values when the runtime may have crossed threads. + mod raw { + use super::CallThreadState; + use crate::Trap; + use std::cell::Cell; + use std::ptr; + + pub type Ptr = *const CallThreadState; + + // The first entry here is the `Ptr` which is what's used as part of the + // public interface of this module. The second entry is a boolean which + // allows the runtime to perform per-thread initialization if necessary + // for handling traps (e.g. setting up ports on macOS and sigaltstack on + // Unix). + thread_local!(static PTR: Cell = Cell::new(ptr::null())); + + #[inline(never)] // see module docs for why this is here + pub fn replace(val: Ptr) -> Result { + PTR.with(|p| { + // When a new value is configured that means that we may be + // entering WebAssembly so check to see if this thread has + // performed per-thread initialization for traps. + let prev = p.get(); + p.set(val); + Ok(prev) + }) + } + + #[inline(never)] // see module docs for why this is here + pub fn get() -> Ptr { + PTR.with(|p| p.get()) + } + } + + /// Opaque state used to help control TLS state across stack switches for + /// async support. + pub struct TlsRestore(raw::Ptr); + + impl TlsRestore { + /// Takes the TLS state that is currently configured and returns a + /// token that is used to replace it later. + /// + /// # Safety + /// + /// This is not a safe operation since it's intended to only be used + /// with stack switching found with fibers and async unc_vm. + pub unsafe fn take() -> Result { + // Our tls pointer must be set at this time, and it must not be + // null. We need to restore the previous pointer since we're + // removing ourselves from the call-stack, and in the process we + // null out our own previous field for safety in case it's + // accidentally used later. + let raw = raw::get(); + assert!(!raw.is_null()); + let prev = (*raw).prev.replace(ptr::null()); + raw::replace(prev)?; + Ok(Self(raw)) + } + + /// Restores a previous tls state back into this thread's TLS. + /// + /// # Safety + /// + /// This is unsafe because it's intended to only be used within the + /// context of stack switching within unc_vm. + pub unsafe fn replace(self) -> Result<(), super::Trap> { + // We need to configure our previous TLS pointer to whatever is in + // TLS at this time, and then we set the current state to ourselves. + let prev = raw::get(); + assert!((*self.0).prev.get().is_null()); + (*self.0).prev.set(prev); + raw::replace(self.0)?; + Ok(()) + } + } + + /// Configures thread local state such that for the duration of the + /// execution of `closure` any call to `with` will yield `ptr`, unless this + /// is recursively called again. + pub fn set(state: &CallThreadState, closure: impl FnOnce() -> R) -> Result { + struct Reset<'a>(&'a CallThreadState); + + impl Drop for Reset<'_> { + #[inline] + fn drop(&mut self) { + raw::replace(self.0.prev.replace(ptr::null())) + .expect("tls should be previously initialized"); + } + } + + // Note that this extension of the lifetime to `'static` should be + // safe because we only ever access it below with an anonymous + // lifetime, meaning `'static` never leaks out of this module. + let ptr = unsafe { mem::transmute::<*const CallThreadState, _>(state) }; + let prev = raw::replace(ptr)?; + state.prev.set(prev); + let _reset = Reset(state); + Ok(closure()) + } + + /// Returns the last pointer configured with `set` above. Panics if `set` + /// has not been previously called and not returned. + pub fn with(closure: impl FnOnce(Option<&CallThreadState>) -> R) -> R { + let p = raw::get(); + unsafe { closure(if p.is_null() { None } else { Some(&*p) }) } + } +} + +extern "C" fn signal_less_trap_handler(pc: *const u8, trap: TrapCode) { + let jmp_buf = tls::with(|info| { + let backtrace = Backtrace::new_unresolved(); + let info = info.unwrap(); + unsafe { + (*info.unwind.get()).as_mut_ptr().write(UnwindReason::WasmTrap { + backtrace, + signal_trap: Some(trap), + pc: pc as usize, + }); + info.jmp_buf.get() + } + }); + unsafe { + unc_vm_unwind(jmp_buf); + } +} + +/// Returns pointer to the trap handler used in VMContext. +pub fn get_trap_handler() -> *const u8 { + signal_less_trap_handler as *const u8 +} diff --git a/runtime/unc-vm/vm/src/tunables.rs b/runtime/unc-vm/vm/src/tunables.rs new file mode 100644 index 000000000..74904504b --- /dev/null +++ b/runtime/unc-vm/vm/src/tunables.rs @@ -0,0 +1,121 @@ +use crate::MemoryError; +use crate::{Memory, Table}; +use crate::{MemoryStyle, TableStyle}; +use crate::{VMMemoryDefinition, VMTableDefinition}; +use unc_vm_types::{MemoryType, TableType}; +use std::ptr::NonNull; +use std::sync::Arc; + +/// An engine delegates the creation of memories, tables, and globals +/// to a foreign implementor of this trait. +pub trait Tunables: Sync { + /// Construct a `MemoryStyle` for the provided `MemoryType` + fn memory_style(&self, memory: &MemoryType) -> MemoryStyle; + + /// Construct a `TableStyle` for the provided `TableType` + fn table_style(&self, table: &TableType) -> TableStyle; + + /// Create a memory owned by the host given a [`MemoryType`] and a [`MemoryStyle`]. + fn create_host_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + ) -> Result, MemoryError>; + + /// Create a memory owned by the VM given a [`MemoryType`] and a [`MemoryStyle`]. + /// + /// # Safety + /// - `vm_definition_location` must point to a valid location in VM memory. + unsafe fn create_vm_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + vm_definition_location: NonNull, + ) -> Result, MemoryError>; + + /// Create a table owned by the host given a [`TableType`] and a [`TableStyle`]. + fn create_host_table( + &self, + ty: &TableType, + style: &TableStyle, + ) -> Result, String>; + + /// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`]. + /// + /// # Safety + /// - `vm_definition_location` must point to a valid location in VM memory. + unsafe fn create_vm_table( + &self, + ty: &TableType, + style: &TableStyle, + vm_definition_location: NonNull, + ) -> Result, String>; + + /// Instrumentation configuration: stack limiter config + fn stack_limiter_cfg(&self) -> Box; + + /// Instrumentation configuration: gas accounting config + fn gas_cfg(&self) -> Box>; + + /// Cost for initializing a stack frame + fn stack_init_gas_cost(&self, frame_size: u64) -> u64; +} + +#[doc(hidden)] +pub struct TestTunables; + +impl Tunables for TestTunables { + fn memory_style(&self, _memory: &MemoryType) -> MemoryStyle { + unimplemented!() + } + + fn table_style(&self, _table: &TableType) -> TableStyle { + unimplemented!() + } + + fn create_host_memory( + &self, + _ty: &MemoryType, + _style: &MemoryStyle, + ) -> Result, MemoryError> { + unimplemented!() + } + + unsafe fn create_vm_memory( + &self, + _ty: &MemoryType, + _style: &MemoryStyle, + _vm_definition_location: NonNull, + ) -> Result, MemoryError> { + unimplemented!() + } + + fn create_host_table( + &self, + _ty: &TableType, + _style: &TableStyle, + ) -> Result, String> { + unimplemented!() + } + + unsafe fn create_vm_table( + &self, + _ty: &TableType, + _style: &TableStyle, + _vm_definition_location: NonNull, + ) -> Result, String> { + unimplemented!() + } + + fn stack_limiter_cfg(&self) -> Box { + unimplemented!() + } + + fn gas_cfg(&self) -> Box> { + unimplemented!() + } + + fn stack_init_gas_cost(&self, _frame_size: u64) -> u64 { + unimplemented!() + } +} diff --git a/runtime/unc-vm/vm/src/vmcontext.rs b/runtime/unc-vm/vm/src/vmcontext.rs new file mode 100644 index 000000000..38b32abaf --- /dev/null +++ b/runtime/unc-vm/vm/src/vmcontext.rs @@ -0,0 +1,1108 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! This file declares `VMContext` and several related structs which contain +//! fields that compiled wasm code accesses directly. + +use crate::func_data_registry::VMFuncRef; +use crate::global::Global; +use crate::instance::Instance; +use crate::memory::Memory; +use crate::sig_registry::VMSharedSignatureIndex; +use crate::table::Table; +use crate::trap::{Trap, TrapCode}; +use crate::VMExternRef; +use std::any::Any; +use std::convert::TryFrom; +use std::fmt; +use std::ptr::{self, NonNull}; +use std::sync::Arc; +use std::u32; + +/// Union representing the first parameter passed when calling a function. +/// +/// It may either be a pointer to the [`VMContext`] if it's a Wasm function +/// or a pointer to arbitrary data controlled by the host if it's a host function. +#[derive(Copy, Clone, Eq)] +pub union VMFunctionEnvironment { + /// Wasm functions take a pointer to [`VMContext`]. + pub vmctx: *mut VMContext, + /// Host functions can have custom environments. + pub host_env: *mut std::ffi::c_void, +} + +impl VMFunctionEnvironment { + /// Check whether the pointer stored is null or not. + pub fn is_null(&self) -> bool { + unsafe { self.host_env.is_null() } + } +} + +impl std::fmt::Debug for VMFunctionEnvironment { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("VMFunctionEnvironment") + .field("vmctx_or_hostenv", unsafe { &self.host_env }) + .finish() + } +} + +impl std::cmp::PartialEq for VMFunctionEnvironment { + fn eq(&self, rhs: &Self) -> bool { + unsafe { self.host_env as usize == rhs.host_env as usize } + } +} + +impl std::hash::Hash for VMFunctionEnvironment { + fn hash(&self, state: &mut H) { + unsafe { + self.vmctx.hash(state); + } + } +} + +/// Represents a continuous region of executable memory starting with a function +/// entry point. +#[derive(Debug)] +#[repr(C)] +pub struct FunctionExtent { + /// Entry point for normal entry of the function. All addresses in the + /// function lie after this address. + pub address: FunctionBodyPtr, + /// Length in bytes. + pub length: usize, +} + +/// An imported function. +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct VMFunctionImport { + /// A pointer to the imported function body. + pub body: FunctionBodyPtr, + + /// Function signature index within the source module. + pub signature: VMSharedSignatureIndex, + + /// Function call trampoline + pub trampoline: Option, + + /// A pointer to the `VMContext` that owns the function or host env data. + pub environment: VMFunctionEnvironment, +} + +#[cfg(test)] +mod test_vmfunction_import { + use super::VMFunctionImport; + use crate::VMOffsets; + use memoffset::offset_of; + use unc_vm_types::ModuleInfo; + use std::mem::size_of; + + #[test] + fn check_vmfunction_import_offsets() { + let module = ModuleInfo::new(); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); + assert_eq!(size_of::(), usize::from(offsets.size_of_vmfunction_import())); + assert_eq!( + offset_of!(VMFunctionImport, body), + usize::from(offsets.vmfunction_import_body()) + ); + assert_eq!( + offset_of!(VMFunctionImport, environment), + usize::from(offsets.vmfunction_import_vmctx()) + ); + } +} + +/// A locally defined function. +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct VMLocalFunction { + /// A pointer to the imported function body. + pub body: FunctionBodyPtr, + + /// Length of the function code + pub length: u32, + + /// Function signature + pub signature: VMSharedSignatureIndex, + + /// Trampoline for host->VM function calls. + pub trampoline: VMTrampoline, +} + +/// The `VMDynamicFunctionContext` is the context that dynamic +/// functions will receive when called (rather than `vmctx`). +/// A dynamic function is a function for which we don't know the signature +/// until runtime. +/// +/// As such, we need to expose the dynamic function `context` +/// containing the relevant context for running the function indicated +/// in `address`. +#[repr(C)] +pub struct VMDynamicFunctionContext { + /// The address of the inner dynamic function. + /// + /// Note: The function must be on the form of + /// `(*mut T, SignatureIndex, *mut i128)`. + pub address: *const VMFunctionBody, + + /// The context that the inner dynamic function will receive. + pub ctx: T, +} + +// The `ctx` itself must be `Send`, `address` can be passed between +// threads because all usage is `unsafe` and synchronized. +unsafe impl Send for VMDynamicFunctionContext {} +// The `ctx` itself must be `Sync`, `address` can be shared between +// threads because all usage is `unsafe` and synchronized. +unsafe impl Sync for VMDynamicFunctionContext {} + +impl Clone for VMDynamicFunctionContext { + fn clone(&self) -> Self { + Self { address: self.address, ctx: self.ctx.clone() } + } +} + +#[cfg(test)] +mod test_vmdynamicfunction_import_context { + use super::VMDynamicFunctionContext; + use crate::VMOffsets; + use memoffset::offset_of; + use unc_vm_types::ModuleInfo; + use std::mem::size_of; + + #[test] + fn check_vmdynamicfunction_import_context_offsets() { + let module = ModuleInfo::new(); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); + assert_eq!( + size_of::>(), + usize::from(offsets.size_of_vmdynamicfunction_import_context()) + ); + assert_eq!( + offset_of!(VMDynamicFunctionContext, address), + usize::from(offsets.vmdynamicfunction_import_context_address()) + ); + assert_eq!( + offset_of!(VMDynamicFunctionContext, ctx), + usize::from(offsets.vmdynamicfunction_import_context_ctx()) + ); + } +} + +/// A placeholder byte-sized type which is just used to provide some amount of type +/// safety when dealing with pointers to JIT-compiled function bodies. Note that it's +/// deliberately not Copy, as we shouldn't be carelessly copying function body bytes +/// around. +#[repr(C)] +pub struct VMFunctionBody(u8); + +#[cfg(test)] +mod test_vmfunction_body { + use super::VMFunctionBody; + use std::mem::size_of; + + #[test] + fn check_vmfunction_body_offsets() { + assert_eq!(size_of::(), 1); + } +} + +/// A pointer to the beginning of the function body. +#[derive(Clone, Copy, Debug)] +#[repr(transparent)] +pub struct FunctionBodyPtr(pub *const VMFunctionBody); + +impl std::ops::Deref for FunctionBodyPtr { + type Target = *const VMFunctionBody; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +// SAFETY: The VMFunctionBody that this points to is opaque, so there's no data to read or write +// through this pointer. This is essentially a usize. +unsafe impl Send for FunctionBodyPtr {} + +/// SAFETY: The VMFunctionBody that this points to is opaque, so there's no data to read or write +/// through this pointer. This is essentially a usize. +unsafe impl Sync for FunctionBodyPtr {} + +/// A function kind is a calling convention into and out of wasm code. +#[derive(Debug, Copy, Clone, PartialEq)] +#[repr(C)] +pub enum VMFunctionKind { + /// A static function has the native signature: + /// `extern "C" (vmctx, arg1, arg2...) -> (result1, result2, ...)`. + /// + /// This is the default for functions that are defined: + /// 1. In the Host, natively + /// 2. In the WebAssembly file + Static, + + /// A dynamic function has the native signature: + /// `extern "C" (ctx, &[Value]) -> Vec`. + /// + /// This is the default for functions that are defined: + /// 1. In the Host, dynamically + Dynamic, +} + +/// The fields compiled code needs to access to utilize a WebAssembly table +/// imported from another instance. +#[derive(Debug, Clone)] +#[repr(C)] +pub struct VMTableImport { + /// A pointer to the imported table description. + pub definition: NonNull, + + /// A pointer to the `Table` that owns the table description. + pub from: Arc, +} + +#[cfg(test)] +mod test_vmtable_import { + use super::VMTableImport; + use crate::VMOffsets; + use memoffset::offset_of; + use unc_vm_types::ModuleInfo; + use std::mem::size_of; + + #[test] + fn check_vmtable_import_offsets() { + let module = ModuleInfo::new(); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); + assert_eq!(size_of::(), usize::from(offsets.size_of_vmtable_import())); + assert_eq!( + offset_of!(VMTableImport, definition), + usize::from(offsets.vmtable_import_definition()) + ); + assert_eq!(offset_of!(VMTableImport, from), usize::from(offsets.vmtable_import_from())); + } +} + +/// The fields compiled code needs to access to utilize a WebAssembly linear +/// memory imported from another instance. +#[derive(Debug, Clone)] +#[repr(C)] +pub struct VMMemoryImport { + /// A pointer to the imported memory description. + pub definition: NonNull, + + /// A pointer to the `Memory` that owns the memory description. + pub from: Arc, +} + +#[cfg(test)] +mod test_vmmemory_import { + use super::VMMemoryImport; + use crate::VMOffsets; + use memoffset::offset_of; + use unc_vm_types::ModuleInfo; + use std::mem::size_of; + + #[test] + fn check_vmmemory_import_offsets() { + let module = ModuleInfo::new(); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); + assert_eq!(size_of::(), usize::from(offsets.size_of_vmmemory_import())); + assert_eq!( + offset_of!(VMMemoryImport, definition), + usize::from(offsets.vmmemory_import_definition()) + ); + assert_eq!(offset_of!(VMMemoryImport, from), usize::from(offsets.vmmemory_import_from())); + } +} + +/// The fields compiled code needs to access to utilize a WebAssembly global +/// variable imported from another instance. +#[derive(Debug, Clone)] +#[repr(C)] +pub struct VMGlobalImport { + /// A pointer to the imported global variable description. + pub definition: NonNull, + + /// A pointer to the `Global` that owns the global description. + pub from: Arc, +} + +/// # Safety +/// This data is safe to share between threads because it's plain data that +/// is the user's responsibility to synchronize. Additionally, all operations +/// on `from` are thread-safe through the use of a mutex in [`Global`]. +unsafe impl Send for VMGlobalImport {} +/// # Safety +/// This data is safe to share between threads because it's plain data that +/// is the user's responsibility to synchronize. And because it's `Clone`, there's +/// really no difference between passing it by reference or by value as far as +/// correctness in a multi-threaded context is concerned. +unsafe impl Sync for VMGlobalImport {} + +#[cfg(test)] +mod test_vmglobal_import { + use super::VMGlobalImport; + use crate::VMOffsets; + use memoffset::offset_of; + use unc_vm_types::ModuleInfo; + use std::mem::size_of; + + #[test] + fn check_vmglobal_import_offsets() { + let module = ModuleInfo::new(); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); + assert_eq!(size_of::(), usize::from(offsets.size_of_vmglobal_import())); + assert_eq!( + offset_of!(VMGlobalImport, definition), + usize::from(offsets.vmglobal_import_definition()) + ); + assert_eq!(offset_of!(VMGlobalImport, from), usize::from(offsets.vmglobal_import_from())); + } +} + +/// The fields compiled code needs to access to utilize a WebAssembly linear +/// memory defined within the instance, namely the start address and the +/// size in bytes. +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct VMMemoryDefinition { + /// The start address which is always valid, even if the memory grows. + pub base: *mut u8, + + /// The current logical size of this linear memory in bytes. + pub current_length: usize, +} + +/// # Safety +/// This data is safe to share between threads because it's plain data that +/// is the user's responsibility to synchronize. +unsafe impl Send for VMMemoryDefinition {} +/// # Safety +/// This data is safe to share between threads because it's plain data that +/// is the user's responsibility to synchronize. And it's `Copy` so there's +/// really no difference between passing it by reference or by value as far as +/// correctness in a multi-threaded context is concerned. +unsafe impl Sync for VMMemoryDefinition {} + +impl VMMemoryDefinition { + /// Do an unsynchronized, non-atomic `memory.copy` for the memory. + /// + /// # Errors + /// + /// Returns a `Trap` error when the source or destination ranges are out of + /// bounds. + /// + /// # Safety + /// + /// The memory is not copied atomically and is not synchronized: it's the + /// caller's responsibility to synchronize. + pub(crate) unsafe fn memory_copy(&self, dst: u32, src: u32, len: u32) -> Result<(), Trap> { + // https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy + if src.checked_add(len).map_or(true, |n| usize::try_from(n).unwrap() > self.current_length) + || dst + .checked_add(len) + .map_or(true, |m| usize::try_from(m).unwrap() > self.current_length) + { + return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds)); + } + + let dst = usize::try_from(dst).unwrap(); + let src = usize::try_from(src).unwrap(); + + // Bounds and casts are checked above, by this point we know that + // everything is safe. + let dst = self.base.add(dst); + let src = self.base.add(src); + ptr::copy(src, dst, len as usize); + + Ok(()) + } + + /// Perform the `memory.fill` operation for the memory in an unsynchronized, + /// non-atomic way. + /// + /// # Errors + /// + /// Returns a `Trap` error if the memory range is out of bounds. + /// + /// # Safety + /// The memory is not filled atomically and is not synchronized: it's the + /// caller's responsibility to synchronize. + pub(crate) unsafe fn memory_fill(&self, dst: u32, val: u32, len: u32) -> Result<(), Trap> { + if dst.checked_add(len).map_or(true, |m| usize::try_from(m).unwrap() > self.current_length) + { + return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds)); + } + + let dst = isize::try_from(dst).unwrap(); + let val = val as u8; + + // Bounds and casts are checked above, by this point we know that + // everything is safe. + let dst = self.base.offset(dst); + ptr::write_bytes(dst, val, len as usize); + + Ok(()) + } +} + +#[cfg(test)] +mod test_vmmemory_definition { + use super::VMMemoryDefinition; + use crate::VMOffsets; + use memoffset::offset_of; + use unc_vm_types::ModuleInfo; + use std::mem::size_of; + + #[test] + fn check_vmmemory_definition_offsets() { + let module = ModuleInfo::new(); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); + assert_eq!( + size_of::(), + usize::from(offsets.size_of_vmmemory_definition()) + ); + assert_eq!( + offset_of!(VMMemoryDefinition, base), + usize::from(offsets.vmmemory_definition_base()) + ); + assert_eq!( + offset_of!(VMMemoryDefinition, current_length), + usize::from(offsets.vmmemory_definition_current_length()) + ); + } +} + +/// The fields compiled code needs to access to utilize a WebAssembly table +/// defined within the instance. +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct VMTableDefinition { + /// Pointer to the table data. + pub base: *mut u8, + + /// The current number of elements in the table. + pub current_elements: u32, +} + +#[cfg(test)] +mod test_vmtable_definition { + use super::VMTableDefinition; + use crate::VMOffsets; + use memoffset::offset_of; + use unc_vm_types::ModuleInfo; + use std::mem::size_of; + + #[test] + fn check_vmtable_definition_offsets() { + let module = ModuleInfo::new(); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); + assert_eq!( + size_of::(), + usize::from(offsets.size_of_vmtable_definition()) + ); + assert_eq!( + offset_of!(VMTableDefinition, base), + usize::from(offsets.vmtable_definition_base()) + ); + assert_eq!( + offset_of!(VMTableDefinition, current_elements), + usize::from(offsets.vmtable_definition_current_elements()) + ); + } +} + +/// A typesafe wrapper around the storage for a global variables. +/// +/// # Safety +/// +/// Accessing the different members of this union is always safe because there +/// are no invalid values for any of the types and the whole object is +/// initialized by VMGlobalDefinition::new(). +#[derive(Clone, Copy)] +#[repr(C, align(16))] +pub union VMGlobalDefinitionStorage { + as_i32: i32, + as_u32: u32, + as_f32: f32, + as_i64: i64, + as_u64: u64, + as_f64: f64, + as_u128: u128, + as_funcref: VMFuncRef, + as_externref: VMExternRef, + bytes: [u8; 16], +} + +impl fmt::Debug for VMGlobalDefinitionStorage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("VMGlobalDefinitionStorage").field("bytes", unsafe { &self.bytes }).finish() + } +} + +/// The storage for a WebAssembly global defined within the instance. +/// +/// TODO: Pack the globals more densely, rather than using the same size +/// for every type. +#[derive(Debug, Clone)] +#[repr(C, align(16))] +pub struct VMGlobalDefinition { + storage: VMGlobalDefinitionStorage, + // If more elements are added here, remember to add offset_of tests below! +} + +#[cfg(test)] +mod test_vmglobal_definition { + use super::VMGlobalDefinition; + use crate::{VMFuncRef, VMOffsets}; + use more_asserts::assert_ge; + use unc_vm_types::ModuleInfo; + use std::mem::{align_of, size_of}; + + #[test] + fn check_vmglobal_definition_alignment() { + assert_ge!(align_of::(), align_of::()); + assert_ge!(align_of::(), align_of::()); + assert_ge!(align_of::(), align_of::()); + assert_ge!(align_of::(), align_of::()); + assert_ge!(align_of::(), align_of::()); + assert_ge!(align_of::(), align_of::<[u8; 16]>()); + } + + #[test] + fn check_vmglobal_definition_offsets() { + let module = ModuleInfo::new(); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); + assert_eq!( + size_of::<*const VMGlobalDefinition>(), + usize::from(offsets.size_of_vmglobal_local()) + ); + } + + #[test] + fn check_vmglobal_begins_aligned() { + let module = ModuleInfo::new(); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); + assert_eq!(offsets.vmctx_globals_begin() % 16, 0); + } +} + +impl VMGlobalDefinition { + /// Construct a `VMGlobalDefinition`. + pub fn new() -> Self { + Self { storage: VMGlobalDefinitionStorage { bytes: [0; 16] } } + } + + /// Return the value as an i32. + /// + /// If this is not an I32 typed global it is unspecified what value is returned. + pub fn to_i32(&self) -> i32 { + unsafe { self.storage.as_i32 } + } + + /// Return a mutable reference to the value as an i32. + /// + /// # Safety + /// + /// It is the callers responsibility to make sure the global has I32 type. + /// Until the returned borrow is dropped, reads and writes of this global + /// must be done exclusively through this borrow. That includes reads and + /// writes of globals inside wasm functions. + pub unsafe fn as_i32_mut(&mut self) -> &mut i32 { + &mut self.storage.as_i32 + } + + /// Return a reference to the value as an u32. + /// + /// If this is not an I32 typed global it is unspecified what value is returned. + pub fn to_u32(&self) -> u32 { + unsafe { self.storage.as_u32 } + } + + /// Return a mutable reference to the value as an u32. + /// + /// # Safety + /// + /// It is the callers responsibility to make sure the global has I32 type. + /// Until the returned borrow is dropped, reads and writes of this global + /// must be done exclusively through this borrow. That includes reads and + /// writes of globals inside wasm functions. + pub unsafe fn as_u32_mut(&mut self) -> &mut u32 { + &mut self.storage.as_u32 + } + + /// Return a reference to the value as an i64. + /// + /// If this is not an I64 typed global it is unspecified what value is returned. + pub fn to_i64(&self) -> i64 { + unsafe { self.storage.as_i64 } + } + + /// Return a mutable reference to the value as an i64. + /// + /// # Safety + /// + /// It is the callers responsibility to make sure the global has I32 type. + /// Until the returned borrow is dropped, reads and writes of this global + /// must be done exclusively through this borrow. That includes reads and + /// writes of globals inside wasm functions. + pub unsafe fn as_i64_mut(&mut self) -> &mut i64 { + &mut self.storage.as_i64 + } + + /// Return a reference to the value as an u64. + /// + /// If this is not an I64 typed global it is unspecified what value is returned. + pub fn to_u64(&self) -> u64 { + unsafe { self.storage.as_u64 } + } + + /// Return a mutable reference to the value as an u64. + /// + /// # Safety + /// + /// It is the callers responsibility to make sure the global has I64 type. + /// Until the returned borrow is dropped, reads and writes of this global + /// must be done exclusively through this borrow. That includes reads and + /// writes of globals inside wasm functions. + pub unsafe fn as_u64_mut(&mut self) -> &mut u64 { + &mut self.storage.as_u64 + } + + /// Return a reference to the value as an f32. + /// + /// If this is not an F32 typed global it is unspecified what value is returned. + pub fn to_f32(&self) -> f32 { + unsafe { self.storage.as_f32 } + } + + /// Return a mutable reference to the value as an f32. + /// + /// # Safety + /// + /// It is the callers responsibility to make sure the global has F32 type. + /// Until the returned borrow is dropped, reads and writes of this global + /// must be done exclusively through this borrow. That includes reads and + /// writes of globals inside wasm functions. + pub unsafe fn as_f32_mut(&mut self) -> &mut f32 { + &mut self.storage.as_f32 + } + + /// Return a reference to the value as an f64. + /// + /// If this is not an F64 typed global it is unspecified what value is returned. + pub fn to_f64(&self) -> f64 { + unsafe { self.storage.as_f64 } + } + + /// Return a mutable reference to the value as an f64. + /// + /// # Safety + /// + /// It is the callers responsibility to make sure the global has F64 type. + /// Until the returned borrow is dropped, reads and writes of this global + /// must be done exclusively through this borrow. That includes reads and + /// writes of globals inside wasm functions. + pub unsafe fn as_f64_mut(&mut self) -> &mut f64 { + &mut self.storage.as_f64 + } + + /// Return a reference to the value as a `VMFuncRef`. + /// + /// If this is not a `VMFuncRef` typed global it is unspecified what value is returned. + pub fn to_funcref(&self) -> VMFuncRef { + unsafe { self.storage.as_funcref } + } + + /// Return a mutable reference to the value as a `VMFuncRef`. + /// + /// # Safety + /// + /// It is the callers responsibility to make sure the global has `VMFuncRef` type. + /// Until the returned borrow is dropped, reads and writes of this global + /// must be done exclusively through this borrow. That includes reads and + /// writes of globals inside wasm functions. + pub unsafe fn as_funcref_mut(&mut self) -> &mut VMFuncRef { + &mut self.storage.as_funcref + } + + /// Return a mutable reference to the value as an `VMExternRef`. + /// + /// # Safety + /// + /// It is the callers responsibility to make sure the global has I32 type. + /// Until the returned borrow is dropped, reads and writes of this global + /// must be done exclusively through this borrow. That includes reads and + /// writes of globals inside wasm functions. + pub unsafe fn as_externref_mut(&mut self) -> &mut VMExternRef { + &mut self.storage.as_externref + } + + /// Return a reference to the value as an `VMExternRef`. + /// + /// If this is not an I64 typed global it is unspecified what value is returned. + pub fn to_externref(&self) -> VMExternRef { + unsafe { self.storage.as_externref } + } + + /// Return a reference to the value as an u128. + /// + /// If this is not an V128 typed global it is unspecified what value is returned. + pub fn to_u128(&self) -> u128 { + unsafe { self.storage.as_u128 } + } + + /// Return a mutable reference to the value as an u128. + /// + /// # Safety + /// + /// It is the callers responsibility to make sure the global has V128 type. + /// Until the returned borrow is dropped, reads and writes of this global + /// must be done exclusively through this borrow. That includes reads and + /// writes of globals inside wasm functions. + pub unsafe fn as_u128_mut(&mut self) -> &mut u128 { + &mut self.storage.as_u128 + } + + /// Return a reference to the value as bytes. + pub fn to_bytes(&self) -> [u8; 16] { + unsafe { self.storage.bytes } + } + + /// Return a mutable reference to the value as bytes. + /// + /// # Safety + /// + /// Until the returned borrow is dropped, reads and writes of this global + /// must be done exclusively through this borrow. That includes reads and + /// writes of globals inside wasm functions. + pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8; 16] { + &mut self.storage.bytes + } +} + +#[cfg(test)] +mod test_vmshared_signature_index { + use super::VMSharedSignatureIndex; + use crate::vmoffsets::{TargetSharedSignatureIndex, VMOffsets}; + use unc_vm_types::ModuleInfo; + use std::mem::size_of; + + #[test] + fn check_vmshared_signature_index() { + let module = ModuleInfo::new(); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); + assert_eq!( + size_of::(), + usize::from(offsets.size_of_vmshared_signature_index()) + ); + } + + #[test] + fn check_target_shared_signature_index() { + assert_eq!(size_of::(), size_of::()); + } +} + +/// The VM caller-checked "anyfunc" record, for caller-side signature checking. +/// It consists of the actual function pointer and a signature id to be checked +/// by the caller. +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[repr(C)] +pub struct VMCallerCheckedAnyfunc { + /// Function body. + pub func_ptr: *const VMFunctionBody, + /// Function signature id. + pub type_index: VMSharedSignatureIndex, + /// Function `VMContext` or host env. + pub vmctx: VMFunctionEnvironment, + // If more elements are added here, remember to add offset_of tests below! +} + +#[cfg(test)] +mod test_vmcaller_checked_anyfunc { + use super::VMCallerCheckedAnyfunc; + use crate::VMOffsets; + use memoffset::offset_of; + use unc_vm_types::ModuleInfo; + use std::mem::size_of; + + #[test] + fn check_vmcaller_checked_anyfunc_offsets() { + let module = ModuleInfo::new(); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8).with_module_info(&module); + assert_eq!( + size_of::(), + usize::from(offsets.size_of_vmcaller_checked_anyfunc()) + ); + assert_eq!( + offset_of!(VMCallerCheckedAnyfunc, func_ptr), + usize::from(offsets.vmcaller_checked_anyfunc_func_ptr()) + ); + assert_eq!( + offset_of!(VMCallerCheckedAnyfunc, type_index), + usize::from(offsets.vmcaller_checked_anyfunc_type_index()) + ); + assert_eq!( + offset_of!(VMCallerCheckedAnyfunc, vmctx), + usize::from(offsets.vmcaller_checked_anyfunc_vmctx()) + ); + } +} + +/// An index type for builtin functions. +#[derive(Copy, Clone, Debug)] +pub struct VMBuiltinFunctionIndex(u32); + +impl VMBuiltinFunctionIndex { + /// Returns an index for wasm's `memory.grow` builtin function. + pub const fn get_memory32_grow_index() -> Self { + Self(0) + } + /// Returns an index for wasm's imported `memory.grow` builtin function. + pub const fn get_imported_memory32_grow_index() -> Self { + Self(1) + } + /// Returns an index for wasm's `memory.size` builtin function. + pub const fn get_memory32_size_index() -> Self { + Self(2) + } + /// Returns an index for wasm's imported `memory.size` builtin function. + pub const fn get_imported_memory32_size_index() -> Self { + Self(3) + } + /// Returns an index for wasm's `table.copy` when both tables are locally + /// defined. + pub const fn get_table_copy_index() -> Self { + Self(4) + } + /// Returns an index for wasm's `table.init`. + pub const fn get_table_init_index() -> Self { + Self(5) + } + /// Returns an index for wasm's `elem.drop`. + pub const fn get_elem_drop_index() -> Self { + Self(6) + } + /// Returns an index for wasm's `memory.copy` for locally defined memories. + pub const fn get_memory_copy_index() -> Self { + Self(7) + } + /// Returns an index for wasm's `memory.copy` for imported memories. + pub const fn get_imported_memory_copy_index() -> Self { + Self(8) + } + /// Returns an index for wasm's `memory.fill` for locally defined memories. + pub const fn get_memory_fill_index() -> Self { + Self(9) + } + /// Returns an index for wasm's `memory.fill` for imported memories. + pub const fn get_imported_memory_fill_index() -> Self { + Self(10) + } + /// Returns an index for wasm's `memory.init` instruction. + pub const fn get_memory_init_index() -> Self { + Self(11) + } + /// Returns an index for wasm's `data.drop` instruction. + pub const fn get_data_drop_index() -> Self { + Self(12) + } + /// Returns an index for wasm's `raise_trap` instruction. + pub const fn get_raise_trap_index() -> Self { + Self(13) + } + /// Returns an index for wasm's `table.size` instruction for local tables. + pub const fn get_table_size_index() -> Self { + Self(14) + } + /// Returns an index for wasm's `table.size` instruction for imported tables. + pub const fn get_imported_table_size_index() -> Self { + Self(15) + } + /// Returns an index for wasm's `table.grow` instruction for local tables. + pub const fn get_table_grow_index() -> Self { + Self(16) + } + /// Returns an index for wasm's `table.grow` instruction for imported tables. + pub const fn get_imported_table_grow_index() -> Self { + Self(17) + } + /// Returns an index for wasm's `table.get` instruction for local tables. + pub const fn get_table_get_index() -> Self { + Self(18) + } + /// Returns an index for wasm's `table.get` instruction for imported tables. + pub const fn get_imported_table_get_index() -> Self { + Self(19) + } + /// Returns an index for wasm's `table.set` instruction for local tables. + pub const fn get_table_set_index() -> Self { + Self(20) + } + /// Returns an index for wasm's `table.set` instruction for imported tables. + pub const fn get_imported_table_set_index() -> Self { + Self(21) + } + /// Returns an index for wasm's `func.ref` instruction. + pub const fn get_func_ref_index() -> Self { + Self(22) + } + /// Returns an index for wasm's `table.fill` instruction for local tables. + pub const fn get_table_fill_index() -> Self { + Self(23) + } + /// Returns an index for a function to increment the externref count. + pub const fn get_externref_inc_index() -> Self { + Self(24) + } + /// Returns an index for a function to decrement the externref count. + pub const fn get_externref_dec_index() -> Self { + Self(25) + } + /// Returns the total number of builtin functions. + pub const fn builtin_functions_total_number() -> u32 { + 26 + } + + /// Return the index as an u32 number. + pub const fn index(self) -> u32 { + self.0 + } +} + +/// An array that stores addresses of builtin functions. We translate code +/// to use indirect calls. This way, we don't have to patch the code. +#[repr(C)] +pub struct VMBuiltinFunctionsArray { + ptrs: [usize; Self::len()], +} + +impl VMBuiltinFunctionsArray { + pub const fn len() -> usize { + VMBuiltinFunctionIndex::builtin_functions_total_number() as usize + } + + pub fn initialized() -> Self { + use crate::libcalls::*; + + let mut ptrs = [0; Self::len()]; + + ptrs[VMBuiltinFunctionIndex::get_memory32_grow_index().index() as usize] = + unc_vm_memory32_grow as usize; + ptrs[VMBuiltinFunctionIndex::get_imported_memory32_grow_index().index() as usize] = + unc_vm_imported_memory32_grow as usize; + + ptrs[VMBuiltinFunctionIndex::get_memory32_size_index().index() as usize] = + unc_vm_memory32_size as usize; + ptrs[VMBuiltinFunctionIndex::get_imported_memory32_size_index().index() as usize] = + unc_vm_imported_memory32_size as usize; + + ptrs[VMBuiltinFunctionIndex::get_table_copy_index().index() as usize] = + unc_vm_table_copy as usize; + + ptrs[VMBuiltinFunctionIndex::get_table_init_index().index() as usize] = + unc_vm_table_init as usize; + ptrs[VMBuiltinFunctionIndex::get_elem_drop_index().index() as usize] = + unc_vm_elem_drop as usize; + + ptrs[VMBuiltinFunctionIndex::get_memory_copy_index().index() as usize] = + unc_vm_memory32_copy as usize; + ptrs[VMBuiltinFunctionIndex::get_imported_memory_copy_index().index() as usize] = + unc_vm_imported_memory32_copy as usize; + ptrs[VMBuiltinFunctionIndex::get_memory_fill_index().index() as usize] = + unc_vm_memory32_fill as usize; + ptrs[VMBuiltinFunctionIndex::get_imported_memory_fill_index().index() as usize] = + unc_vm_imported_memory32_fill as usize; + ptrs[VMBuiltinFunctionIndex::get_memory_init_index().index() as usize] = + unc_vm_memory32_init as usize; + ptrs[VMBuiltinFunctionIndex::get_data_drop_index().index() as usize] = + unc_vm_data_drop as usize; + ptrs[VMBuiltinFunctionIndex::get_raise_trap_index().index() as usize] = + unc_vm_raise_trap as usize; + ptrs[VMBuiltinFunctionIndex::get_table_size_index().index() as usize] = + unc_vm_table_size as usize; + ptrs[VMBuiltinFunctionIndex::get_imported_table_size_index().index() as usize] = + unc_vm_imported_table_size as usize; + ptrs[VMBuiltinFunctionIndex::get_table_grow_index().index() as usize] = + unc_vm_table_grow as usize; + ptrs[VMBuiltinFunctionIndex::get_imported_table_grow_index().index() as usize] = + unc_vm_imported_table_grow as usize; + ptrs[VMBuiltinFunctionIndex::get_table_get_index().index() as usize] = + unc_vm_table_get as usize; + ptrs[VMBuiltinFunctionIndex::get_imported_table_get_index().index() as usize] = + unc_vm_imported_table_get as usize; + ptrs[VMBuiltinFunctionIndex::get_table_set_index().index() as usize] = + unc_vm_table_set as usize; + ptrs[VMBuiltinFunctionIndex::get_imported_table_set_index().index() as usize] = + unc_vm_imported_table_set as usize; + ptrs[VMBuiltinFunctionIndex::get_func_ref_index().index() as usize] = + unc_vm_func_ref as usize; + ptrs[VMBuiltinFunctionIndex::get_table_fill_index().index() as usize] = + unc_vm_table_fill as usize; + ptrs[VMBuiltinFunctionIndex::get_externref_inc_index().index() as usize] = + unc_vm_externref_inc as usize; + ptrs[VMBuiltinFunctionIndex::get_externref_dec_index().index() as usize] = + unc_vm_externref_dec as usize; + + debug_assert!(ptrs.iter().cloned().all(|p| p != 0)); + + Self { ptrs } + } +} + +/// The VM "context", which is pointed to by the `vmctx` arg in the compiler. +/// This has information about globals, memories, tables, and other runtime +/// state associated with the current instance. +/// +/// The struct here is empty, as the sizes of these fields are dynamic, and +/// we can't describe them in Rust's type system. Sufficient memory is +/// allocated at runtime. +/// +/// TODO: We could move the globals into the `vmctx` allocation too. +#[derive(Debug)] +#[repr(C, align(16))] // align 16 since globals are aligned to that and contained inside +pub struct VMContext {} + +impl VMContext { + /// Return a mutable reference to the associated `Instance`. + /// + /// # Safety + /// This is unsafe because it doesn't work on just any `VMContext`, it must + /// be a `VMContext` allocated as part of an `Instance`. + #[allow(clippy::cast_ptr_alignment)] + #[inline] + pub unsafe fn instance(&self) -> &Instance { + &*((self as *const Self as *mut u8).offset(-Instance::vmctx_offset()) as *const Instance) + } + + /// Return a reference to the host state associated with this `Instance`. + /// + /// # Safety + /// This is unsafe because it doesn't work on just any `VMContext`, it must + /// be a `VMContext` allocated as part of an `Instance`. + #[inline] + pub unsafe fn host_state(&self) -> &dyn Any { + self.instance().host_state() + } +} + +/// +pub type VMTrampoline = unsafe extern "C" fn( + *mut VMContext, // callee vmctx + *const VMFunctionBody, // function we're actually calling + *mut u128, // space for arguments and return values +); + +/// Pointers to section data. +#[derive(Clone, Copy, Debug)] +#[repr(transparent)] +pub struct SectionBodyPtr(pub *const u8); + +impl std::ops::Deref for SectionBodyPtr { + type Target = *const u8; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/runtime/unc-vm/vm/src/vmoffsets.rs b/runtime/unc-vm/vm/src/vmoffsets.rs new file mode 100644 index 000000000..4e7f19cf5 --- /dev/null +++ b/runtime/unc-vm/vm/src/vmoffsets.rs @@ -0,0 +1,785 @@ +// This file contains code from external sources. +// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md + +//! Offsets and sizes of various structs in unc_vm-vm's vmcontext +//! module. + +#![deny(rustdoc::broken_intra_doc_links)] + +use crate::VMBuiltinFunctionIndex; +use more_asserts::assert_lt; +use unc_vm_types::{ + FunctionIndex, GlobalIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, + ModuleInfo, SignatureIndex, TableIndex, +}; +use std::convert::TryFrom; + +#[cfg(target_pointer_width = "32")] +fn cast_to_u32(sz: usize) -> u32 { + u32::try_from(sz).unwrap() +} +#[cfg(target_pointer_width = "64")] +fn cast_to_u32(sz: usize) -> u32 { + u32::try_from(sz).expect("overflow in cast from usize to u32") +} + +/// Align an offset used in this module to a specific byte-width by rounding up +#[inline] +const fn align(offset: u32, width: u32) -> u32 { + (offset + (width - 1)) / width * width +} + +/// This class computes offsets to fields within [`VMContext`] and other +/// related structs that JIT code accesses directly. +/// +/// [`VMContext`]: crate::vmcontext::VMContext +// Invariant: the addresses always fit into an u32 without overflowing +#[derive(Clone, Debug)] +pub struct VMOffsets { + /// The size in bytes of a pointer on the target. + pointer_size: u8, + /// The number of signature declarations in the module. + num_signature_ids: u32, + /// The number of imported functions in the module. + num_imported_functions: u32, + /// The number of imported tables in the module. + num_imported_tables: u32, + /// The number of imported memories in the module. + num_imported_memories: u32, + /// The number of imported globals in the module. + num_imported_globals: u32, + /// The number of defined tables in the module. + num_local_tables: u32, + /// The number of defined memories in the module. + num_local_memories: u32, + /// The number of defined globals in the module. + num_local_globals: u32, + /// If the module has trap handler. + has_trap_handlers: bool, + + vmctx_signature_ids_begin: u32, + vmctx_imported_functions_begin: u32, + vmctx_imported_tables_begin: u32, + vmctx_imported_memories_begin: u32, + vmctx_imported_globals_begin: u32, + vmctx_tables_begin: u32, + vmctx_memories_begin: u32, + vmctx_globals_begin: u32, + vmctx_builtin_functions_begin: u32, + vmctx_trap_handler_begin: u32, + vmctx_gas_limiter_pointer: u32, + vmctx_stack_limit_begin: u32, + vmctx_stack_limit_initial_begin: u32, + size_of_vmctx: u32, +} + +impl VMOffsets { + /// Return a new `VMOffsets` instance, for a given pointer size. + /// + /// The returned `VMOffsets` has no entities. Add entities with other builder methods for this + /// type. + pub fn new(pointer_size: u8) -> Self { + Self { + pointer_size, + num_signature_ids: 0, + num_imported_functions: 0, + num_imported_tables: 0, + num_imported_memories: 0, + num_imported_globals: 0, + num_local_tables: 0, + num_local_memories: 0, + num_local_globals: 0, + has_trap_handlers: false, + vmctx_signature_ids_begin: 0, + vmctx_imported_functions_begin: 0, + vmctx_imported_tables_begin: 0, + vmctx_imported_memories_begin: 0, + vmctx_imported_globals_begin: 0, + vmctx_tables_begin: 0, + vmctx_memories_begin: 0, + vmctx_globals_begin: 0, + vmctx_builtin_functions_begin: 0, + vmctx_trap_handler_begin: 0, + vmctx_gas_limiter_pointer: 0, + vmctx_stack_limit_begin: 0, + vmctx_stack_limit_initial_begin: 0, + size_of_vmctx: 0, + } + } + + /// Return a new `VMOffsets` instance, for a host's pointer size. + /// + /// The returned `VMOffsets` has no entities. Add entities with other builder methods for this + /// type. + pub fn for_host() -> Self { + Self::new(std::mem::size_of::<*const u8>() as u8) + } + + /// Add imports and locals from the provided ModuleInfo. + #[tracing::instrument(target = "unc_vm", level = "trace", skip_all)] + pub fn with_module_info(mut self, module: &ModuleInfo) -> Self { + self.num_imported_functions = module.import_counts.functions; + self.num_imported_tables = module.import_counts.tables; + self.num_imported_memories = module.import_counts.memories; + self.num_imported_globals = module.import_counts.globals; + self.num_signature_ids = cast_to_u32(module.signatures.len()); + // FIXME = these should most likely be subtracting the corresponding imports!!? + self.num_local_tables = cast_to_u32(module.tables.len()); + self.num_local_memories = cast_to_u32(module.memories.len()); + self.num_local_globals = cast_to_u32(module.globals.len()); + self.has_trap_handlers = true; + self.precompute(); + self + } + + /// Add imports and locals from the provided ModuleInfo. + pub fn with_archived_module_info(mut self, module: &rkyv::Archived) -> Self { + self.num_imported_functions = module.import_counts.functions; + self.num_imported_tables = module.import_counts.tables; + self.num_imported_memories = module.import_counts.memories; + self.num_imported_globals = module.import_counts.globals; + self.num_signature_ids = cast_to_u32(module.signatures.len()); + // FIXME = these should most likely be subtracting the corresponding imports!!? + self.num_local_tables = cast_to_u32(module.tables.len()); + self.num_local_memories = cast_to_u32(module.memories.len()); + self.num_local_globals = cast_to_u32(module.globals.len()); + self.has_trap_handlers = true; + self.precompute(); + self + } + + /// Number of local tables defined in the module + pub fn num_local_tables(&self) -> u32 { + self.num_local_tables + } + + /// Number of local memories defined in the module + pub fn num_local_memories(&self) -> u32 { + self.num_local_memories + } + + fn precompute(&mut self) { + use std::mem::align_of; + + /// Offset base by num_items items of size item_size, panicking on overflow + fn offset_by( + base: u32, + num_items: u32, + prev_item_size: u32, + next_item_align: usize, + ) -> u32 { + align( + base.checked_add(num_items.checked_mul(prev_item_size).unwrap()).unwrap(), + next_item_align as u32, + ) + } + + self.vmctx_signature_ids_begin = 0; + self.vmctx_imported_functions_begin = offset_by( + self.vmctx_signature_ids_begin, + self.num_signature_ids, + u32::from(self.size_of_vmshared_signature_index()), + align_of::(), + ); + self.vmctx_imported_tables_begin = offset_by( + self.vmctx_imported_functions_begin, + self.num_imported_functions, + u32::from(self.size_of_vmfunction_import()), + align_of::(), + ); + self.vmctx_imported_memories_begin = offset_by( + self.vmctx_imported_tables_begin, + self.num_imported_tables, + u32::from(self.size_of_vmtable_import()), + align_of::(), + ); + self.vmctx_imported_globals_begin = offset_by( + self.vmctx_imported_memories_begin, + self.num_imported_memories, + u32::from(self.size_of_vmmemory_import()), + align_of::(), + ); + self.vmctx_tables_begin = offset_by( + self.vmctx_imported_globals_begin, + self.num_imported_globals, + u32::from(self.size_of_vmglobal_import()), + align_of::(), + ); + self.vmctx_memories_begin = offset_by( + self.vmctx_tables_begin, + self.num_local_tables, + u32::from(self.size_of_vmtable_definition()), + align_of::(), + ); + self.vmctx_globals_begin = offset_by( + self.vmctx_memories_begin, + self.num_local_memories, + u32::from(self.size_of_vmmemory_definition()), + align_of::(), + ); + self.vmctx_builtin_functions_begin = offset_by( + self.vmctx_globals_begin, + self.num_local_globals, + u32::from(self.size_of_vmglobal_local()), + align_of::(), + ); + self.vmctx_trap_handler_begin = offset_by( + self.vmctx_builtin_functions_begin, + VMBuiltinFunctionIndex::builtin_functions_total_number(), + u32::from(self.pointer_size), + align_of::(), + ); + self.vmctx_gas_limiter_pointer = offset_by( + self.vmctx_trap_handler_begin, + if self.has_trap_handlers { 1 } else { 0 }, + u32::from(self.pointer_size), + align_of::<*mut unc_vm_types::FastGasCounter>(), + ); + self.vmctx_stack_limit_begin = offset_by( + self.vmctx_gas_limiter_pointer, + 1, + u32::from(self.pointer_size), + align_of::(), + ); + self.vmctx_stack_limit_initial_begin = self.vmctx_stack_limit_begin.checked_add(4).unwrap(); + self.size_of_vmctx = self.vmctx_stack_limit_begin.checked_add(4).unwrap(); + } +} + +/// Offsets for [`VMFunctionImport`]. +/// +/// [`VMFunctionImport`]: crate::vmcontext::VMFunctionImport +impl VMOffsets { + /// The offset of the `body` field. + #[allow(clippy::erasing_op)] + pub const fn vmfunction_import_body(&self) -> u8 { + 0 * self.pointer_size + } + + /// The offset of the `vmctx` field. + #[allow(clippy::identity_op)] + pub const fn vmfunction_import_vmctx(&self) -> u8 { + 3 * self.pointer_size + } + + /// Return the size of [`VMFunctionImport`]. + /// + /// [`VMFunctionImport`]: crate::vmcontext::VMFunctionImport + pub const fn size_of_vmfunction_import(&self) -> u8 { + 4 * self.pointer_size + } +} + +/// Offsets for [`VMDynamicFunctionContext`]. +/// +/// [`VMDynamicFunctionContext`]: crate::vmcontext::VMDynamicFunctionContext +impl VMOffsets { + /// The offset of the `address` field. + #[allow(clippy::erasing_op)] + pub const fn vmdynamicfunction_import_context_address(&self) -> u8 { + 0 * self.pointer_size + } + + /// The offset of the `ctx` field. + #[allow(clippy::identity_op)] + pub const fn vmdynamicfunction_import_context_ctx(&self) -> u8 { + 1 * self.pointer_size + } + + /// Return the size of [`VMDynamicFunctionContext`]. + /// + /// [`VMDynamicFunctionContext`]: crate::vmcontext::VMDynamicFunctionContext + pub const fn size_of_vmdynamicfunction_import_context(&self) -> u8 { + 2 * self.pointer_size + } +} + +/// Offsets for `*const VMFunctionBody`. +impl VMOffsets { + /// The size of the `current_elements` field. + #[allow(clippy::identity_op)] + pub const fn size_of_vmfunction_body_ptr(&self) -> u8 { + 1 * self.pointer_size + } +} + +/// Offsets for [`VMTableImport`]. +/// +/// [`VMTableImport`]: crate::vmcontext::VMTableImport +impl VMOffsets { + /// The offset of the `definition` field. + #[allow(clippy::erasing_op)] + pub const fn vmtable_import_definition(&self) -> u8 { + 0 * self.pointer_size + } + + /// The offset of the `from` field. + #[allow(clippy::identity_op)] + pub const fn vmtable_import_from(&self) -> u8 { + 1 * self.pointer_size + } + + /// Return the size of [`VMTableImport`]. + /// + /// [`VMTableImport`]: crate::vmcontext::VMTableImport + pub const fn size_of_vmtable_import(&self) -> u8 { + 3 * self.pointer_size + } +} + +/// Offsets for [`VMTableDefinition`]. +/// +/// [`VMTableDefinition`]: crate::vmcontext::VMTableDefinition +impl VMOffsets { + /// The offset of the `base` field. + #[allow(clippy::erasing_op)] + pub const fn vmtable_definition_base(&self) -> u8 { + 0 * self.pointer_size + } + + /// The offset of the `current_elements` field. + #[allow(clippy::identity_op)] + pub const fn vmtable_definition_current_elements(&self) -> u8 { + 1 * self.pointer_size + } + + /// The size of the `current_elements` field. + pub const fn size_of_vmtable_definition_current_elements(&self) -> u8 { + 4 + } + + /// Return the size of [`VMTableDefinition`]. + /// + /// [`VMTableDefinition`]: crate::vmcontext::VMTableDefinition + pub const fn size_of_vmtable_definition(&self) -> u8 { + 2 * self.pointer_size + } +} + +/// Offsets for [`VMMemoryImport`]. +/// +/// [`VMMemoryImport`]: crate::vmcontext::VMMemoryImport +impl VMOffsets { + /// The offset of the `from` field. + #[allow(clippy::erasing_op)] + pub const fn vmmemory_import_definition(&self) -> u8 { + 0 * self.pointer_size + } + + /// The offset of the `from` field. + #[allow(clippy::identity_op)] + pub const fn vmmemory_import_from(&self) -> u8 { + 1 * self.pointer_size + } + + /// Return the size of [`VMMemoryImport`]. + /// + /// [`VMMemoryImport`]: crate::vmcontext::VMMemoryImport + pub const fn size_of_vmmemory_import(&self) -> u8 { + 3 * self.pointer_size + } +} + +/// Offsets for [`VMMemoryDefinition`]. +/// +/// [`VMMemoryDefinition`]: crate::vmcontext::VMMemoryDefinition +impl VMOffsets { + /// The offset of the `base` field. + #[allow(clippy::erasing_op)] + pub const fn vmmemory_definition_base(&self) -> u8 { + 0 * self.pointer_size + } + + /// The offset of the `current_length` field. + #[allow(clippy::identity_op)] + pub const fn vmmemory_definition_current_length(&self) -> u8 { + 1 * self.pointer_size + } + + /// The size of the `current_length` field. + pub const fn size_of_vmmemory_definition_current_length(&self) -> u8 { + 4 + } + + /// Return the size of [`VMMemoryDefinition`]. + /// + /// [`VMMemoryDefinition`]: crate::vmcontext::VMMemoryDefinition + pub const fn size_of_vmmemory_definition(&self) -> u8 { + 2 * self.pointer_size + } +} + +/// Offsets for [`VMGlobalImport`]. +/// +/// [`VMGlobalImport`]: crate::vmcontext::VMGlobalImport +impl VMOffsets { + /// The offset of the `definition` field. + #[allow(clippy::erasing_op)] + pub const fn vmglobal_import_definition(&self) -> u8 { + 0 * self.pointer_size + } + + /// The offset of the `from` field. + #[allow(clippy::identity_op)] + pub const fn vmglobal_import_from(&self) -> u8 { + 1 * self.pointer_size + } + + /// Return the size of [`VMGlobalImport`]. + /// + /// [`VMGlobalImport`]: crate::vmcontext::VMGlobalImport + #[allow(clippy::identity_op)] + pub const fn size_of_vmglobal_import(&self) -> u8 { + 2 * self.pointer_size + } +} + +/// Offsets for a non-null pointer to a [`VMGlobalDefinition`] used as a local global. +/// +/// [`VMGlobalDefinition`]: crate::vmcontext::VMGlobalDefinition +impl VMOffsets { + /// Return the size of a pointer to a [`VMGlobalDefinition`]; + /// + /// The underlying global itself is the size of the largest value type (i.e. a V128), + /// however the size of this type is just the size of a pointer. + /// + /// [`VMGlobalDefinition`]: crate::vmcontext::VMGlobalDefinition + pub const fn size_of_vmglobal_local(&self) -> u8 { + self.pointer_size + } +} + +/// Offsets for [`VMSharedSignatureIndex`]. +/// +/// [`VMSharedSignatureIndex`]: crate::vmcontext::VMSharedSignatureIndex +impl VMOffsets { + /// Return the size of [`VMSharedSignatureIndex`]. + /// + /// [`VMSharedSignatureIndex`]: crate::vmcontext::VMSharedSignatureIndex + pub const fn size_of_vmshared_signature_index(&self) -> u8 { + 4 + } +} + +/// Offsets for [`VMCallerCheckedAnyfunc`]. +/// +/// [`VMCallerCheckedAnyfunc`]: crate::vmcontext::VMCallerCheckedAnyfunc +impl VMOffsets { + /// The offset of the `func_ptr` field. + #[allow(clippy::erasing_op)] + pub const fn vmcaller_checked_anyfunc_func_ptr(&self) -> u8 { + 0 * self.pointer_size + } + + /// The offset of the `type_index` field. + #[allow(clippy::identity_op)] + pub const fn vmcaller_checked_anyfunc_type_index(&self) -> u8 { + 1 * self.pointer_size + } + + /// The offset of the `vmctx` field. + pub const fn vmcaller_checked_anyfunc_vmctx(&self) -> u8 { + 2 * self.pointer_size + } + + /// Return the size of [`VMCallerCheckedAnyfunc`]. + /// + /// [`VMCallerCheckedAnyfunc`]: crate::vmcontext::VMCallerCheckedAnyfunc + pub const fn size_of_vmcaller_checked_anyfunc(&self) -> u8 { + 3 * self.pointer_size + } +} + +/// Offsets for [`VMFuncRef`]. +/// +/// [`VMFuncRef`]: crate::func_data_registry::VMFuncRef +impl VMOffsets { + /// The offset to the pointer to the anyfunc inside the ref. + #[allow(clippy::erasing_op)] + pub const fn vm_funcref_anyfunc_ptr(&self) -> u8 { + 0 * self.pointer_size + } + + /// Return the size of [`VMFuncRef`]. + /// + /// [`VMFuncRef`]: crate::func_data_registry::VMFuncRef + pub const fn size_of_vm_funcref(&self) -> u8 { + self.pointer_size + } +} + +/// Offsets for [`VMContext`]. +/// +/// [`VMContext`]: crate::vmcontext::VMContext +impl VMOffsets { + /// The offset of the `signature_ids` array. + #[inline] + pub fn vmctx_signature_ids_begin(&self) -> u32 { + self.vmctx_signature_ids_begin + } + + /// The offset of the `tables` array. + pub fn vmctx_imported_functions_begin(&self) -> u32 { + self.vmctx_imported_functions_begin + } + + /// The offset of the `tables` array. + #[allow(clippy::identity_op)] + pub fn vmctx_imported_tables_begin(&self) -> u32 { + self.vmctx_imported_tables_begin + } + + /// The offset of the `memories` array. + pub fn vmctx_imported_memories_begin(&self) -> u32 { + self.vmctx_imported_memories_begin + } + + /// The offset of the `globals` array. + pub fn vmctx_imported_globals_begin(&self) -> u32 { + self.vmctx_imported_globals_begin + } + + /// The offset of the `tables` array. + pub fn vmctx_tables_begin(&self) -> u32 { + self.vmctx_tables_begin + } + + /// The offset of the `memories` array. + pub fn vmctx_memories_begin(&self) -> u32 { + self.vmctx_memories_begin + } + + /// The offset of the `globals` array. + pub fn vmctx_globals_begin(&self) -> u32 { + self.vmctx_globals_begin + } + + /// The offset of the builtin functions array. + pub fn vmctx_builtin_functions_begin(&self) -> u32 { + self.vmctx_builtin_functions_begin + } + + /// The offset of the trap handler. + pub fn vmctx_trap_handler_begin(&self) -> u32 { + self.vmctx_trap_handler_begin + } + + /// The offset of the gas limiter pointer. + pub fn vmctx_gas_limiter_pointer(&self) -> u32 { + self.vmctx_gas_limiter_pointer + } + + /// The offset of the current stack limit. + pub fn vmctx_stack_limit_begin(&self) -> u32 { + self.vmctx_stack_limit_begin + } + + /// The offset of the initial stack limit. + pub fn vmctx_stack_limit_initial_begin(&self) -> u32 { + self.vmctx_stack_limit_initial_begin + } + + /// Return the size of the [`VMContext`] allocation. + /// + /// [`VMContext`]: crate::vmcontext::VMContext + pub fn size_of_vmctx(&self) -> u32 { + self.size_of_vmctx + } + + /// Return the offset to [`VMSharedSignatureIndex`] index `index`. + /// + /// [`VMSharedSignatureIndex`]: crate::vmcontext::VMSharedSignatureIndex + // Remember updating precompute upon changes + pub fn vmctx_vmshared_signature_id(&self, index: SignatureIndex) -> u32 { + assert_lt!(index.as_u32(), self.num_signature_ids); + self.vmctx_signature_ids_begin + + index.as_u32() * u32::from(self.size_of_vmshared_signature_index()) + } + + /// Return the offset to [`VMFunctionImport`] index `index`. + /// + /// [`VMFunctionImport`]: crate::vmcontext::VMFunctionImport + // Remember updating precompute upon changes + pub fn vmctx_vmfunction_import(&self, index: FunctionIndex) -> u32 { + assert_lt!(index.as_u32(), self.num_imported_functions); + self.vmctx_imported_functions_begin + + index.as_u32() * u32::from(self.size_of_vmfunction_import()) + } + + /// Return the offset to [`VMTableImport`] index `index`. + /// + /// [`VMTableImport`]: crate::vmcontext::VMTableImport + // Remember updating precompute upon changes + pub fn vmctx_vmtable_import(&self, index: TableIndex) -> u32 { + assert_lt!(index.as_u32(), self.num_imported_tables); + self.vmctx_imported_tables_begin + index.as_u32() * u32::from(self.size_of_vmtable_import()) + } + + /// Return the offset to [`VMMemoryImport`] index `index`. + /// + /// [`VMMemoryImport`]: crate::vmcontext::VMMemoryImport + // Remember updating precompute upon changes + pub fn vmctx_vmmemory_import(&self, index: MemoryIndex) -> u32 { + assert_lt!(index.as_u32(), self.num_imported_memories); + self.vmctx_imported_memories_begin + + index.as_u32() * u32::from(self.size_of_vmmemory_import()) + } + + /// Return the offset to [`VMGlobalImport`] index `index`. + /// + /// [`VMGlobalImport`]: crate::vmcontext::VMGlobalImport + // Remember updating precompute upon changes + pub fn vmctx_vmglobal_import(&self, index: GlobalIndex) -> u32 { + assert_lt!(index.as_u32(), self.num_imported_globals); + self.vmctx_imported_globals_begin + + index.as_u32() * u32::from(self.size_of_vmglobal_import()) + } + + /// Return the offset to [`VMTableDefinition`] index `index`. + /// + /// [`VMTableDefinition`]: crate::vmcontext::VMTableDefinition + // Remember updating precompute upon changes + pub fn vmctx_vmtable_definition(&self, index: LocalTableIndex) -> u32 { + assert_lt!(index.as_u32(), self.num_local_tables); + self.vmctx_tables_begin + index.as_u32() * u32::from(self.size_of_vmtable_definition()) + } + + /// Return the offset to [`VMMemoryDefinition`] index `index`. + /// + /// [`VMMemoryDefinition`]: crate::vmcontext::VMMemoryDefinition + // Remember updating precompute upon changes + pub fn vmctx_vmmemory_definition(&self, index: LocalMemoryIndex) -> u32 { + assert_lt!(index.as_u32(), self.num_local_memories); + self.vmctx_memories_begin + index.as_u32() * u32::from(self.size_of_vmmemory_definition()) + } + + /// Return the offset to the [`VMGlobalDefinition`] index `index`. + /// + /// [`VMGlobalDefinition`]: crate::vmcontext::VMGlobalDefinition + // Remember updating precompute upon changes + pub fn vmctx_vmglobal_definition(&self, index: LocalGlobalIndex) -> u32 { + assert_lt!(index.as_u32(), self.num_local_globals); + self.vmctx_globals_begin + index.as_u32() * u32::from(self.size_of_vmglobal_local()) + } + + /// Return the offset to the `body` field in `*const VMFunctionBody` index `index`. + // Remember updating precompute upon changes + pub fn vmctx_vmfunction_import_body(&self, index: FunctionIndex) -> u32 { + self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_body()) + } + + /// Return the offset to the `vmctx` field in `*const VMFunctionBody` index `index`. + // Remember updating precompute upon changes + pub fn vmctx_vmfunction_import_vmctx(&self, index: FunctionIndex) -> u32 { + self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_vmctx()) + } + + /// Return the offset to the `definition` field in [`VMTableImport`] index `index`. + /// + /// [`VMTableImport`]: crate::vmcontext::VMTableImport + // Remember updating precompute upon changes + pub fn vmctx_vmtable_import_definition(&self, index: TableIndex) -> u32 { + self.vmctx_vmtable_import(index) + u32::from(self.vmtable_import_definition()) + } + + /// Return the offset to the `base` field in [`VMTableDefinition`] index `index`. + /// + /// [`VMTableDefinition`]: crate::vmcontext::VMTableDefinition + // Remember updating precompute upon changes + pub fn vmctx_vmtable_definition_base(&self, index: LocalTableIndex) -> u32 { + self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_base()) + } + + /// Return the offset to the `current_elements` field in [`VMTableDefinition`] index `index`. + /// + /// [`VMTableDefinition`]: crate::vmcontext::VMTableDefinition + // Remember updating precompute upon changes + pub fn vmctx_vmtable_definition_current_elements(&self, index: LocalTableIndex) -> u32 { + self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_current_elements()) + } + + /// Return the offset to the `from` field in [`VMMemoryImport`] index `index`. + /// + /// [`VMMemoryImport`]: crate::vmcontext::VMMemoryImport + // Remember updating precompute upon changes + pub fn vmctx_vmmemory_import_definition(&self, index: MemoryIndex) -> u32 { + self.vmctx_vmmemory_import(index) + u32::from(self.vmmemory_import_definition()) + } + + /// Return the offset to the `vmctx` field in [`VMMemoryImport`] index `index`. + /// + /// [`VMMemoryImport`]: crate::vmcontext::VMMemoryImport + // Remember updating precompute upon changes + pub fn vmctx_vmmemory_import_from(&self, index: MemoryIndex) -> u32 { + self.vmctx_vmmemory_import(index) + u32::from(self.vmmemory_import_from()) + } + + /// Return the offset to the `base` field in [`VMMemoryDefinition`] index `index`. + /// + /// [`VMMemoryDefinition`]: crate::vmcontext::VMMemoryDefinition + // Remember updating precompute upon changes + pub fn vmctx_vmmemory_definition_base(&self, index: LocalMemoryIndex) -> u32 { + self.vmctx_vmmemory_definition(index) + u32::from(self.vmmemory_definition_base()) + } + + /// Return the offset to the `current_length` field in [`VMMemoryDefinition`] index `index`. + /// + /// [`VMMemoryDefinition`]: crate::vmcontext::VMMemoryDefinition + // Remember updating precompute upon changes + pub fn vmctx_vmmemory_definition_current_length(&self, index: LocalMemoryIndex) -> u32 { + self.vmctx_vmmemory_definition(index) + u32::from(self.vmmemory_definition_current_length()) + } + + /// Return the offset to the `from` field in [`VMGlobalImport`] index `index`. + /// + /// [`VMGlobalImport`]: crate::vmcontext::VMGlobalImport + // Remember updating precompute upon changes + pub fn vmctx_vmglobal_import_definition(&self, index: GlobalIndex) -> u32 { + self.vmctx_vmglobal_import(index) + u32::from(self.vmglobal_import_definition()) + } + + /// Return the offset to builtin function in `VMBuiltinFunctionsArray` index `index`. + // Remember updating precompute upon changes + pub fn vmctx_builtin_function(&self, index: VMBuiltinFunctionIndex) -> u32 { + self.vmctx_builtin_functions_begin + index.index() * u32::from(self.pointer_size) + } + + /// Return the offset to the trap handler. + pub fn vmctx_trap_handler(&self) -> u32 { + // Ensure that we never ask for trap handler offset if it's not enabled. + assert!(self.has_trap_handlers); + self.vmctx_trap_handler_begin + } +} + +/// Target specific type for shared signature index. +#[derive(Debug, Copy, Clone)] +pub struct TargetSharedSignatureIndex(u32); + +impl TargetSharedSignatureIndex { + /// Constructs `TargetSharedSignatureIndex`. + pub const fn new(value: u32) -> Self { + Self(value) + } + + /// Returns index value. + pub const fn index(self) -> u32 { + self.0 + } +} + +#[cfg(test)] +mod tests { + use crate::vmoffsets::align; + + #[test] + fn alignment() { + fn is_aligned(x: u32) -> bool { + x % 16 == 0 + } + assert!(is_aligned(align(0, 16))); + assert!(is_aligned(align(32, 16))); + assert!(is_aligned(align(33, 16))); + assert!(is_aligned(align(31, 16))); + } +} diff --git a/runtime/unc-vm/wast/Cargo.toml b/runtime/unc-vm/wast/Cargo.toml new file mode 100644 index 000000000..fc29556cc --- /dev/null +++ b/runtime/unc-vm/wast/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "unc-vm-wast" +version.workspace = true +authors = ["Wasmer Engineering Team ", "Hello Inc "] +description = "wast testing support for wasmer" +license = "MIT OR Apache-2.0 WITH LLVM-exception" +categories = ["wasm"] +keywords = ["wasm", "webassembly"] +repository.workspace = true +readme = "README.md" +edition = "2021" +publish = false + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +unc-vm-test-api.workspace = true +wast.workspace = true +tempfile.workspace = true +thiserror.workspace = true + +[features] +default = ["wat"] +wat = ["unc-vm-test-api/wat"] + +[badges] +maintenance = { status = "actively-developed" } diff --git a/runtime/unc-vm/wast/README.md b/runtime/unc-vm/wast/README.md new file mode 100644 index 000000000..a4f673fee --- /dev/null +++ b/runtime/unc-vm/wast/README.md @@ -0,0 +1,7 @@ +This is the `wasmer-wast` crate, which contains an implementation of WebAssembly's +"wast" test scripting language, which is used in the +[WebAssembly spec testsuite], using wasmer for execution. + +[WebAssembly spec testsuite]: https://github.com/WebAssembly/testsuite + +> Note: this project started as a fork of [this crate](https://crates.io/crates/wasmtime-wast). diff --git a/runtime/unc-vm/wast/src/error.rs b/runtime/unc-vm/wast/src/error.rs new file mode 100644 index 000000000..53f0ab6d3 --- /dev/null +++ b/runtime/unc-vm/wast/src/error.rs @@ -0,0 +1,37 @@ +use std::fmt; +use thiserror::Error; + +/// A Directive Error +#[derive(Debug)] +pub struct DirectiveError { + /// The line where the directive is defined + pub line: usize, + /// The column where the directive is defined + pub col: usize, + /// The failing message received when running the directive + pub message: String, +} + +/// A structure holding the list of all executed directives +#[derive(Error, Debug)] +pub struct DirectiveErrors { + /// The filename where the error occured + pub filename: String, + /// The list of errors + pub errors: Vec, +} + +impl fmt::Display for DirectiveErrors { + // This trait requires `fmt` with this exact signature. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // Write strictly the first element into the supplied output + // stream: `f`. Returns `fmt::Result` which indicates whether the + // operation succeeded or failed. Note that `write!` uses syntax which + // is very similar to `println!`. + writeln!(f, "Failed directives on {}:", self.filename)?; + for error in self.errors.iter() { + writeln!(f, " • {} ({}:{})", error.message, error.line, error.col)?; + } + Ok(()) + } +} diff --git a/runtime/unc-vm/wast/src/lib.rs b/runtime/unc-vm/wast/src/lib.rs new file mode 100644 index 000000000..b265f8234 --- /dev/null +++ b/runtime/unc-vm/wast/src/lib.rs @@ -0,0 +1,29 @@ +//! Implementation of the WAST text format for wasmer. +#![cfg(target_arch = "x86_64")] +#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] +#![warn(unused_import_braces)] +#![deny(unstable_features)] +#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] +#![cfg_attr( + feature = "cargo-clippy", + warn( + clippy::float_arithmetic, + clippy::mut_mut, + clippy::nonminimal_bool, + clippy::map_unwrap_or, + clippy::print_stdout, + clippy::unicode_not_nfc, + clippy::use_self + ) +)] + +mod error; +mod spectest; +mod wast; + +pub use crate::error::{DirectiveError, DirectiveErrors}; +pub use crate::spectest::spectest_importobject; +pub use crate::wast::Wast; + +/// Version number of this crate. +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/runtime/unc-vm/wast/src/spectest.rs b/runtime/unc-vm/wast/src/spectest.rs new file mode 100644 index 000000000..ebd7b18ac --- /dev/null +++ b/runtime/unc-vm/wast/src/spectest.rs @@ -0,0 +1,49 @@ +use unc_vm_test_api::*; + +/// Return an instance implementing the "spectest" interface used in the +/// spec testsuite. +#[allow(clippy::print_stdout)] // used for tests only +pub fn spectest_importobject(store: &Store) -> ImportObject { + let print = Function::new_native(store, || {}); + let print_i32 = Function::new_native(store, |val: i32| println!("{}: i32", val)); + let print_i64 = Function::new_native(store, |val: i64| println!("{}: i64", val)); + let print_f32 = Function::new_native(store, |val: f32| println!("{}: f32", val)); + let print_f64 = Function::new_native(store, |val: f64| println!("{}: f64", val)); + let print_i32_f32 = Function::new_native(store, |i: i32, f: f32| { + println!("{}: i32", i); + println!("{}: f32", f); + }); + let print_f64_f64 = Function::new_native(store, |f1: f64, f2: f64| { + println!("{}: f64", f1); + println!("{}: f64", f2); + }); + + let global_i32 = Global::new(store, Val::I32(666)); + let global_i64 = Global::new(store, Val::I64(666)); + let global_f32 = Global::new(store, Val::F32(f32::from_bits(0x4426_8000))); + let global_f64 = Global::new(store, Val::F64(f64::from_bits(0x4084_d000_0000_0000))); + + let ty = TableType::new(ValType::FuncRef, 10, Some(20)); + let table = Table::new(store, ty, Val::FuncRef(None)).unwrap(); + + let ty = MemoryType::new(1, Some(2), false); + let memory = Memory::new(store, ty).unwrap(); + + imports! { + "spectest" => { + "print" => print, + "print_i32" => print_i32, + "print_i64" => print_i64, + "print_f32" => print_f32, + "print_f64" => print_f64, + "print_i32_f32" => print_i32_f32, + "print_f64_f64" => print_f64_f64, + "global_i32" => global_i32, + "global_i64" => global_i64, + "global_f32" => global_f32, + "global_f64" => global_f64, + "table" => table, + "memory" => memory, + }, + } +} diff --git a/runtime/unc-vm/wast/src/wast.rs b/runtime/unc-vm/wast/src/wast.rs new file mode 100644 index 000000000..db8db151a --- /dev/null +++ b/runtime/unc-vm/wast/src/wast.rs @@ -0,0 +1,604 @@ +use crate::error::{DirectiveError, DirectiveErrors}; +use crate::spectest::spectest_importobject; +use anyhow::{anyhow, bail, Result}; +use unc_vm_test_api::*; +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::path::Path; +use std::str; + +/// The wast test script language allows modules to be defined and actions +/// to be performed on them. +pub struct Wast { + /// Wast files have a concept of a "current" module, which is the most + /// recently defined. + current: Option, + /// The Import Object that all wast tests will have + import_object: ImportObject, + /// The instances in the test + instances: HashMap, + /// Allowed failures (ideally this should be empty) + allowed_instantiation_failures: HashSet, + /// If the (expected from .wast, actual) message pair is in this list, + /// treat the strings as matching. + match_trap_messages: HashMap, + /// If the current module was an allowed failure, we allow test to fail + current_is_allowed_failure: bool, + /// Extern-ref manager: used for testing extern refs: they're referred to by + /// number in WAST, so we map here. + extern_refs: BTreeMap, + /// The wasm Store + store: Store, + /// A flag indicating if Wast tests should stop as soon as one test fails. + pub fail_fast: bool, + /// A flag indicating that assert_trap and assert_exhaustion should be skipped. + /// See https://github.com/wasmerio/wasmer/issues/1550 for more info + disable_assert_trap_exhaustion: bool, +} + +impl Wast { + /// Construct a new instance of `Wast` with a given imports. + pub fn new(store: Store, import_object: ImportObject) -> Self { + Self { + current: None, + store, + import_object, + allowed_instantiation_failures: HashSet::new(), + match_trap_messages: HashMap::new(), + current_is_allowed_failure: false, + instances: HashMap::new(), + extern_refs: BTreeMap::new(), + fail_fast: true, + disable_assert_trap_exhaustion: false, + } + } + + /// A list of instantiation failures to allow. + pub fn allow_instantiation_failures(&mut self, failures: &[&str]) { + for &failure_str in failures.iter() { + self.allowed_instantiation_failures.insert(failure_str.to_string()); + } + } + + /// A list of alternative messages to permit for a trap failure. + pub fn allow_trap_message(&mut self, expected: &str, allowed: &str) { + self.match_trap_messages.insert(expected.into(), allowed.into()); + } + + /// Do not run any code in assert_trap or assert_exhaustion. + pub fn disable_assert_and_exhaustion(&mut self) { + self.disable_assert_trap_exhaustion = true; + } + + /// Construct a new instance of `Wast` with the spectests imports. + pub fn new_with_spectest(store: Store) -> Self { + let import_object = spectest_importobject(&store); + Self::new(store, import_object) + } + + fn get_instance(&self, instance_name: Option<&str>) -> Result { + match instance_name { + Some(name) => self + .instances + .get(name) + .cloned() + .ok_or_else(|| anyhow!("failed to find instance named `{}`", name)), + None => self.current.clone().ok_or_else(|| anyhow!("no previous instance found")), + } + } + + /// Perform the action portion of a command. + fn perform_execute(&mut self, exec: wast::WastExecute<'_>) -> Result> { + match exec { + wast::WastExecute::Invoke(invoke) => self.perform_invoke(invoke), + wast::WastExecute::Module(mut module) => { + let binary = module.encode()?; + let result = self.instantiate(&binary); + result.map(|_| Vec::new()) + } + wast::WastExecute::Get { module, global } => { + let instance_name = module.map(|i| i.name()); + let instance = self.get_instance(instance_name.as_deref())?; + let global = if let Some(Export::Global(global)) = instance.lookup(global) { + global + } else { + bail!("could not find the global: {}", global) + }; + Ok(vec![global.from.get(&self.store)]) + } + } + } + + fn perform_invoke(&mut self, exec: wast::WastInvoke<'_>) -> Result> { + let values = exec.args.iter().map(|a| self.runtime_value(a)).collect::>>()?; + self.invoke(exec.module.map(|i| i.name()), exec.name, &values) + } + + fn assert_return( + &self, + result: Result>, + results: &[wast::AssertExpression], + ) -> Result<()> { + let values = result?; + for (v, e) in values.iter().zip(results) { + if self.val_matches(v, e)? { + continue; + } + if let Val::V128(bits) = v { + if let wast::AssertExpression::V128(pattern) = e { + bail!( + "expected {:?}, got {:?} (v128 bits: {})", + e, + v128_format(*bits, pattern), + bits + ); + } + } + bail!("expected {:?}, got {:?}", e, v) + } + Ok(()) + } + + fn assert_trap(&self, result: Result>, expected: &str) -> Result<()> { + let actual = match result { + Ok(values) => bail!("expected trap, got {:?}", values), + Err(t) => format!("{}", t), + }; + if self.matches_message_assert_trap(expected, &actual) { + return Ok(()); + } + bail!("expected '{}', got '{}'", expected, actual) + } + + fn run_directive(&mut self, test: &Path, directive: wast::WastDirective) -> Result<()> { + use wast::WastDirective::*; + + match directive { + Module(mut module) => { + let binary = module.encode()?; + self.module(module.id.map(|s| s.name()), &binary)?; + } + Register { span: _, name, module } => { + self.register(module.map(|s| s.name()), name)?; + } + Invoke(i) => { + self.perform_invoke(i)?; + } + AssertReturn { span: _, exec, results } => { + let result = self.perform_execute(exec); + self.assert_return(result, &results)?; + } + AssertTrap { span: _, exec, message } => { + if !self.disable_assert_trap_exhaustion { + let result = self.perform_execute(exec); + self.assert_trap(result, message)?; + } + } + AssertExhaustion { span: _, call, message } => { + if !self.disable_assert_trap_exhaustion { + let result = self.perform_invoke(call); + self.assert_trap(result, message)?; + } + } + AssertInvalid { span: _, module, message } => { + let wasm = match module { + wast::QuoteModule::Module(mut m) => m.encode()?, + wast::QuoteModule::Quote(list) => self.parse_quote_module(test, &list)?, + }; + let err = match self.module(None, &wasm) { + Ok(()) => bail!("expected module to fail to build"), + Err(e) => e, + }; + let error_message = format!("{:?}", err); + if !Self::matches_message_assert_invalid(message, &error_message) { + bail!("assert_invalid: expected \"{}\", got \"{}\"", message, error_message) + } + } + QuoteModule { .. } => { + // Do nothing + } + AssertException { .. } => { + // Do nothing for now + } + AssertMalformed { module, span: _, message: _ } => { + let mut module = match module { + wast::QuoteModule::Module(m) => m, + // This is a `*.wat` parser test which we're not + // interested in. + wast::QuoteModule::Quote(_) => return Ok(()), + }; + let bytes = module.encode()?; + if self.module(None, &bytes).is_ok() { + bail!("expected malformed module to fail to instantiate"); + } + } + AssertUnlinkable { span: _, mut module, message } => { + let bytes = module.encode()?; + let err = match self.module(None, &bytes) { + Ok(()) => bail!("expected module to fail to link"), + Err(e) => e, + }; + let error_message = format!("{:?}", err); + if !Self::matches_message_assert_unlinkable(message, &error_message) { + bail!("assert_unlinkable: expected {}, got {}", message, error_message) + } + } + } + + Ok(()) + } + + /// Run a wast script from a byte buffer. + pub fn run_buffer(&mut self, test: &Path, wast: &[u8]) -> Result<()> { + let wast = str::from_utf8(wast)?; + let filename = test.to_str().unwrap(); + let adjust_wast = |mut err: wast::Error| { + err.set_path(filename.as_ref()); + err.set_text(wast); + err + }; + + let mut lexer = wast::lexer::Lexer::new(wast); + lexer.allow_confusing_unicode(true); + let buf = wast::parser::ParseBuffer::new_with_lexer(lexer).map_err(adjust_wast)?; + let ast = wast::parser::parse::(&buf).map_err(adjust_wast)?; + let mut errors = Vec::with_capacity(ast.directives.len()); + for directive in ast.directives { + let sp = directive.span(); + if let Err(e) = self.run_directive(test, directive) { + let message = format!("{}", e); + // If depends on an instance that doesn't exist + if message.contains("no previous instance found") { + continue; + } + // We don't compute it, comes from instantiating an instance + // that we expected to fail. + if self.current.is_none() && self.current_is_allowed_failure { + continue; + } + let (line, col) = sp.linecol_in(wast); + errors.push(DirectiveError { line: line + 1, col, message }); + if self.fail_fast { + break; + } + } + } + if !errors.is_empty() { + return Err(DirectiveErrors { filename: filename.to_string(), errors }.into()); + } + Ok(()) + } + + fn parse_quote_module(&self, test: &Path, source: &[&[u8]]) -> Result> { + let mut ret = String::new(); + for src in source { + match str::from_utf8(src) { + Ok(s) => ret.push_str(s), + Err(_) => bail!("malformed UTF-8 encoding"), + } + ret.push(' '); + } + let mut lexer = wast::lexer::Lexer::new(&ret); + lexer.allow_confusing_unicode(true); + let buf = wast::parser::ParseBuffer::new_with_lexer(lexer)?; + let mut wat = wast::parser::parse::(&buf)?; + + // TODO: when memory64 merges into the proper spec then this should be + // removed since it will presumably no longer be a text-format error but + // rather a validation error. Currently all non-memory64 proposals + // assert that this offset is a text-parser error, whereas with memory64 + // support that error is deferred until later. + if ret.contains("offset=4294967296") && !test.iter().any(|t| t == "memory64") { + bail!("i32 constant out of bounds"); + } + Ok(wat.module.encode()?) + } + + /// Run a wast script from a file. + pub fn run_file(&mut self, path: &Path) -> Result<()> { + let bytes = std::fs::read(path)?; + self.run_buffer(path, &bytes) + } +} + +// This is the implementation specific to the Runtime +impl Wast { + /// Define a module and register it. + fn module(&mut self, instance_name: Option<&str>, module: &[u8]) -> Result<()> { + let instance = match self.instantiate(module) { + Ok(i) => i, + Err(e) => { + // We set the current to None to allow running other + // spectests when `fail_fast` is `false`. + self.current = None; + let error_message = format!("{}", e); + self.current_is_allowed_failure = false; + for allowed_failure in self.allowed_instantiation_failures.iter() { + if error_message.contains(allowed_failure) { + self.current_is_allowed_failure = true; + break; + } + } + bail!("instantiation failed with: {}", e) + } + }; + if let Some(name) = instance_name { + self.instances.insert(name.to_string(), instance.clone()); + } + self.current = Some(instance); + self.current_is_allowed_failure = false; + Ok(()) + } + + fn instantiate(&self, module: &[u8]) -> Result { + let module = Module::new(&self.store, module)?; + let instance = + Instance::new_with_config(&module, InstanceConfig::with_stack_limit(1000000), &self)?; + Ok(instance) + } + + /// Register an instance to make it available for performing actions. + fn register(&mut self, name: Option<&str>, as_name: &str) -> Result<()> { + let instance = self.get_instance(name)?; + self.instances.insert(as_name.to_string(), instance); + Ok(()) + } + + /// Invoke an exported function from an instance. + fn invoke( + &mut self, + instance_name: Option<&str>, + field: &str, + args: &[Val], + ) -> Result> { + let instance = self.get_instance(instance_name.as_deref())?; + instance.handle().instance().as_ref().reset_stack_meter(); + let func: Function = instance.lookup_function(field).expect("should find the function"); + match func.call(args) { + Ok(result) => Ok(result.into()), + Err(e) => Err(e.into()), + } + } + + /// Translate from a `script::Value` to a `Val`. + fn runtime_value(&mut self, v: &wast::Expression<'_>) -> Result { + use wast::Instruction::*; + + if v.instrs.len() != 1 { + bail!("too many instructions in {:?}", v); + } + Ok(match &v.instrs[0] { + I32Const(x) => Val::I32(*x), + I64Const(x) => Val::I64(*x), + F32Const(x) => Val::F32(f32::from_bits(x.bits)), + F64Const(x) => Val::F64(f64::from_bits(x.bits)), + V128Const(x) => Val::V128(u128::from_le_bytes(x.to_le_bytes())), + RefNull(wast::HeapType::Func) => Val::FuncRef(None), + RefNull(wast::HeapType::Extern) => Val::null(), + RefExtern(number) => { + let extern_ref = + self.extern_refs.entry(*number).or_insert_with(|| ExternRef::new(*number)); + Val::ExternRef(extern_ref.clone()) + } + other => bail!("couldn't convert {:?} to a runtime value", other), + }) + } + + // Checks if the `assert_unlinkable` message matches the expected one + fn matches_message_assert_unlinkable(expected: &str, actual: &str) -> bool { + actual.contains(&expected) + } + + // Checks if the `assert_invalid` message matches the expected one + fn matches_message_assert_invalid(expected: &str, actual: &str) -> bool { + actual.contains(expected) + // Waiting on https://github.com/WebAssembly/bulk-memory-operations/pull/137 + // to propagate to WebAssembly/testsuite. + || (expected.contains("unknown table") && actual.contains("unknown elem")) + // wasmparser return the wrong message + || (expected.contains("unknown memory") && actual.contains("no linear memories are present")) + // `elem.wast` and `proposals/bulk-memory-operations/elem.wast` disagree + // on the expected error message for the same error. + || (expected.contains("out of bounds") && actual.contains("does not fit")) + // handle `unknown global $NUM` error messages that wasmparser doesn't return yet + || (expected.contains("unknown global") && actual.contains("unknown global")) + // handle `unknown memory $NUM` error messages that wasmparser doesn't return yet + || (expected.contains("unknown memory") && actual.contains("unknown memory")) + || (expected.contains("unknown memory") && actual.contains("Data segment extends past end of the data section")) + } + + // Checks if the `assert_trap` message matches the expected one + fn matches_message_assert_trap(&self, expected: &str, actual: &str) -> bool { + actual.contains(expected) + || self + .match_trap_messages + .get(expected) + .map_or(false, |alternative| actual.contains(alternative)) + } + + fn val_matches(&self, actual: &Val, expected: &wast::AssertExpression) -> Result { + Ok(match (actual, expected) { + (Val::I32(a), wast::AssertExpression::I32(b)) => a == b, + (Val::I64(a), wast::AssertExpression::I64(b)) => a == b, + // Note that these float comparisons are comparing bits, not float + // values, so we're testing for bit-for-bit equivalence + (Val::F32(a), wast::AssertExpression::F32(b)) => f32_matches(*a, b), + (Val::F64(a), wast::AssertExpression::F64(b)) => f64_matches(*a, b), + (Val::V128(a), wast::AssertExpression::V128(b)) => v128_matches(*a, b), + (Val::FuncRef(None), wast::AssertExpression::RefNull(Some(wast::HeapType::Func))) => { + true + } + (Val::FuncRef(Some(_)), wast::AssertExpression::RefNull(_)) => false, + (Val::FuncRef(None), wast::AssertExpression::RefFunc(None)) => true, + (Val::FuncRef(None), wast::AssertExpression::RefFunc(Some(_))) => false, + ( + Val::ExternRef(extern_ref), + wast::AssertExpression::RefNull(Some(wast::HeapType::Extern)), + ) if extern_ref.is_null() => true, + (Val::ExternRef(extern_ref), wast::AssertExpression::RefExtern(_)) + if extern_ref.is_null() => + { + false + } + + (Val::ExternRef(_), wast::AssertExpression::RefNull(_)) => false, + (Val::ExternRef(extern_ref), wast::AssertExpression::RefExtern(num)) => { + if let Some(stored_extern_ref) = self.extern_refs.get(num) { + extern_ref == stored_extern_ref + } else { + false + } + } + _ => bail!("don't know how to compare {:?} and {:?} yet", actual, expected), + }) + } +} + +impl NamedResolver for Wast { + fn resolve_by_name(&self, module: &str, field: &str) -> Option { + let imports = self.import_object.clone(); + + if imports.contains_namespace(module) { + imports.resolve_by_name(module, field) + } else { + let instance = self.instances.get(module)?; + instance.lookup(field) + } + } +} + +fn extract_lane_as_i8(bytes: u128, lane: usize) -> i8 { + (bytes >> (lane * 8)) as i8 +} + +fn extract_lane_as_i16(bytes: u128, lane: usize) -> i16 { + (bytes >> (lane * 16)) as i16 +} + +fn extract_lane_as_i32(bytes: u128, lane: usize) -> i32 { + (bytes >> (lane * 32)) as i32 +} + +fn extract_lane_as_i64(bytes: u128, lane: usize) -> i64 { + (bytes >> (lane * 64)) as i64 +} + +fn f32_matches(actual: f32, expected: &wast::NanPattern) -> bool { + match expected { + wast::NanPattern::CanonicalNan => actual.is_canonical_nan(), + wast::NanPattern::ArithmeticNan => actual.is_arithmetic_nan(), + wast::NanPattern::Value(expected_value) => actual.to_bits() == expected_value.bits, + } +} + +fn f64_matches(actual: f64, expected: &wast::NanPattern) -> bool { + match expected { + wast::NanPattern::CanonicalNan => actual.is_canonical_nan(), + wast::NanPattern::ArithmeticNan => actual.is_arithmetic_nan(), + wast::NanPattern::Value(expected_value) => actual.to_bits() == expected_value.bits, + } +} + +fn v128_matches(actual: u128, expected: &wast::V128Pattern) -> bool { + match expected { + wast::V128Pattern::I8x16(b) => { + b.iter().enumerate().all(|(i, b)| *b == extract_lane_as_i8(actual, i)) + } + wast::V128Pattern::I16x8(b) => { + b.iter().enumerate().all(|(i, b)| *b == extract_lane_as_i16(actual, i)) + } + wast::V128Pattern::I32x4(b) => { + b.iter().enumerate().all(|(i, b)| *b == extract_lane_as_i32(actual, i)) + } + wast::V128Pattern::I64x2(b) => { + b.iter().enumerate().all(|(i, b)| *b == extract_lane_as_i64(actual, i)) + } + wast::V128Pattern::F32x4(b) => b.iter().enumerate().all(|(i, b)| { + let a = extract_lane_as_i32(actual, i) as u32; + f32_matches(f32::from_bits(a), b) + }), + wast::V128Pattern::F64x2(b) => b.iter().enumerate().all(|(i, b)| { + let a = extract_lane_as_i64(actual, i) as u64; + f64_matches(f64::from_bits(a), b) + }), + } +} + +fn v128_format(actual: u128, expected: &wast::V128Pattern) -> wast::V128Pattern { + match expected { + wast::V128Pattern::I8x16(_) => wast::V128Pattern::I8x16([ + extract_lane_as_i8(actual, 0), + extract_lane_as_i8(actual, 1), + extract_lane_as_i8(actual, 2), + extract_lane_as_i8(actual, 3), + extract_lane_as_i8(actual, 4), + extract_lane_as_i8(actual, 5), + extract_lane_as_i8(actual, 6), + extract_lane_as_i8(actual, 7), + extract_lane_as_i8(actual, 8), + extract_lane_as_i8(actual, 9), + extract_lane_as_i8(actual, 10), + extract_lane_as_i8(actual, 11), + extract_lane_as_i8(actual, 12), + extract_lane_as_i8(actual, 13), + extract_lane_as_i8(actual, 14), + extract_lane_as_i8(actual, 15), + ]), + wast::V128Pattern::I16x8(_) => wast::V128Pattern::I16x8([ + extract_lane_as_i16(actual, 0), + extract_lane_as_i16(actual, 1), + extract_lane_as_i16(actual, 2), + extract_lane_as_i16(actual, 3), + extract_lane_as_i16(actual, 4), + extract_lane_as_i16(actual, 5), + extract_lane_as_i16(actual, 6), + extract_lane_as_i16(actual, 7), + ]), + wast::V128Pattern::I32x4(_) => wast::V128Pattern::I32x4([ + extract_lane_as_i32(actual, 0), + extract_lane_as_i32(actual, 1), + extract_lane_as_i32(actual, 2), + extract_lane_as_i32(actual, 3), + ]), + wast::V128Pattern::I64x2(_) => wast::V128Pattern::I64x2([ + extract_lane_as_i64(actual, 0), + extract_lane_as_i64(actual, 1), + ]), + wast::V128Pattern::F32x4(_) => wast::V128Pattern::F32x4([ + wast::NanPattern::Value(wast::Float32 { bits: extract_lane_as_i32(actual, 0) as _ }), + wast::NanPattern::Value(wast::Float32 { bits: extract_lane_as_i32(actual, 1) as _ }), + wast::NanPattern::Value(wast::Float32 { bits: extract_lane_as_i32(actual, 2) as _ }), + wast::NanPattern::Value(wast::Float32 { bits: extract_lane_as_i32(actual, 3) as _ }), + ]), + wast::V128Pattern::F64x2(_) => wast::V128Pattern::F64x2([ + wast::NanPattern::Value(wast::Float64 { bits: extract_lane_as_i64(actual, 0) as _ }), + wast::NanPattern::Value(wast::Float64 { bits: extract_lane_as_i64(actual, 1) as _ }), + ]), + } +} + +pub trait NaNCheck { + fn is_arithmetic_nan(&self) -> bool; + fn is_canonical_nan(&self) -> bool; +} + +impl NaNCheck for f32 { + fn is_arithmetic_nan(&self) -> bool { + const AF32_NAN: u32 = 0x0040_0000; + (self.to_bits() & AF32_NAN) == AF32_NAN + } + + fn is_canonical_nan(&self) -> bool { + (self.to_bits() & 0x7fff_ffff) == 0x7fc0_0000 + } +} + +impl NaNCheck for f64 { + fn is_arithmetic_nan(&self) -> bool { + const AF64_NAN: u64 = 0x0008_0000_0000_0000; + (self.to_bits() & AF64_NAN) == AF64_NAN + } + + fn is_canonical_nan(&self) -> bool { + (self.to_bits() & 0x7fff_ffff_ffff_ffff) == 0x7ff8_0000_0000_0000 + } +} diff --git a/runtime/unc-wallet-contract/Cargo.toml b/runtime/unc-wallet-contract/Cargo.toml new file mode 100644 index 000000000..26264e504 --- /dev/null +++ b/runtime/unc-wallet-contract/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "unc-wallet-contract" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +description = "Builds and exposes Wallet Contract code." +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +unc-vm-runner.workspace = true + +[dev-dependencies] +unc-primitives-core.workspace = true + +[build-dependencies] +anyhow.workspace = true + +[features] +nightly_protocol = [ + "unc-vm-runner/nightly_protocol", +] +nightly = [ + "nightly_protocol", + "unc-vm-runner/nightly", +] diff --git a/runtime/unc-wallet-contract/README.md b/runtime/unc-wallet-contract/README.md new file mode 100644 index 000000000..5db1cfb67 --- /dev/null +++ b/runtime/unc-wallet-contract/README.md @@ -0,0 +1,15 @@ +A temporary (placeholder) implementation of the `Wallet Contract`. + +See https://github.com/near/NEPs/issues/518. + +Must not use in production! + +Currently, the contract can only be used in nightly build. +The `build.rs` generates WASM file and saves it to the `./res` directory. + +If you want to use the contract from framework, add this crate as a dependency +to the Cargo.toml and use `unc_wallet_contract::wallet_contract()`. + +In order to review changes to the WASM file, rebuild the wallet contract locally +(remove it beforehand to make sure it was rebuilt later) and check the hashes +by running `check_wallet_contract` and `check_wallet_contract_magic_bytes` tests in `src/lib.rs`. diff --git a/runtime/unc-wallet-contract/build.rs b/runtime/unc-wallet-contract/build.rs new file mode 100644 index 000000000..ce1b1334d --- /dev/null +++ b/runtime/unc-wallet-contract/build.rs @@ -0,0 +1,67 @@ +/// This file is run as a part of `cargo build` process and it builds the `Wallet Contract`. +/// The generated WASM file is put to the `./res` directory. +use anyhow::{anyhow, Context, Ok, Result}; + +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn main() -> Result<()> { + if cfg!(not(feature = "nightly")) { + return Ok(()); + } + build_contract("./wallet-contract", &[], "wallet_contract") +} + +fn build_contract(dir: &str, args: &[&str], output: &str) -> Result<()> { + let target_dir: PathBuf = + std::env::var("OUT_DIR").context("Failed to read OUT_DIR environment variable")?.into(); + + // We place the build artifacts in `target_dir` (workspace's build directory). + let mut cmd = cargo_build_cmd(&target_dir); + cmd.args(args); + cmd.current_dir(dir); + run_checking_status(cmd)?; + + let build_artifact_path = + format!("wasm32-unknown-unknown/release/{}.wasm", dir.replace('-', "_")); + let src = target_dir.join(build_artifact_path); + let wasm_target_path = format!("./res/{}.wasm", output); + + std::fs::copy(&src, &wasm_target_path) + .with_context(|| format!("Failed to copy `{}` to `{}`", src.display(), wasm_target_path))?; + + println!("cargo:rerun-if-changed={}", dir); + Ok(()) +} + +/// Creates `cargo build` command to compile the WASM file. +/// Note that we are in `build.rs` file, so this will be called as a part +/// of the global `cargo build` process that already has some flags set. +/// `env_remove` invocations will remove these flags from the nested `cargo build` +/// process, to avoid unexpected behaviors due to the workspace configurations. +// TODO(eth-implicit) Change it to have a reproducible hash of the WASM file. +// see https://github.com/utnet-org/utility/pull/10269#discussion_r1430139987. +fn cargo_build_cmd(target_dir: &Path) -> Command { + let mut res = Command::new("cargo"); + + res.env_remove("CARGO_BUILD_RUSTFLAGS"); + res.env_remove("CARGO_ENCODED_RUSTFLAGS"); + res.env_remove("RUSTC_WORKSPACE_WRAPPER"); + + res.env("RUSTFLAGS", "-Dwarnings"); + res.env("CARGO_TARGET_DIR", target_dir); + + res.args(["build", "--target=wasm32-unknown-unknown", "--release"]); + + res +} + +fn run_checking_status(mut cmd: Command) -> Result<()> { + cmd.status().with_context(|| format!("Failed to run command `{cmd:?}`")).and_then(|status| { + if status.success() { + Ok(()) + } else { + Err(anyhow!("Command `{cmd:?}` exited with non-zero status: {status:?}")) + } + }) +} diff --git a/runtime/unc-wallet-contract/res/.gitignore b/runtime/unc-wallet-contract/res/.gitignore new file mode 100644 index 000000000..19e1bced9 --- /dev/null +++ b/runtime/unc-wallet-contract/res/.gitignore @@ -0,0 +1 @@ +*.wasm diff --git a/runtime/unc-wallet-contract/src/lib.rs b/runtime/unc-wallet-contract/src/lib.rs new file mode 100644 index 000000000..8ad1143b9 --- /dev/null +++ b/runtime/unc-wallet-contract/src/lib.rs @@ -0,0 +1,68 @@ +#![doc = include_str!("../README.md")] +use unc_vm_runner::ContractCode; +use std::sync::{Arc, OnceLock}; + +/// Temporary (placeholder) Wallet Contract. +pub fn wallet_contract() -> Arc { + static CONTRACT: OnceLock> = OnceLock::new(); + CONTRACT.get_or_init(|| Arc::new(read_contract())).clone() +} + +/// Include the WASM file content directly in the binary at compile time. +fn read_contract() -> ContractCode { + #[cfg(feature = "nightly")] + let code = include_bytes!("../res/wallet_contract.wasm"); + + #[cfg(not(feature = "nightly"))] + let code = &[]; + + ContractCode::new(code.to_vec(), None) +} + +/// near[wallet contract hash] +pub fn wallet_contract_magic_bytes() -> Arc { + static CONTRACT: OnceLock> = OnceLock::new(); + CONTRACT + .get_or_init(|| { + let wallet_contract_hash = *wallet_contract().hash(); + let magic_bytes = format!("near{}", wallet_contract_hash); + Arc::new(ContractCode::new(magic_bytes.into(), None)) + }) + .clone() +} + +#[cfg(feature = "nightly")] +#[cfg(test)] +mod tests { + use crate::{wallet_contract, wallet_contract_magic_bytes}; + use unc_primitives_core::hash::CryptoHash; + use std::str::FromStr; + + const WALLET_CONTRACT_HASH: &'static str = "5wJJ2YaCq75kVSfx8zoZpevg1uLAn4h7nqUd2njKUEXe"; + const MAGIC_BYTES_HASH: &'static str = "31PSU4diHE4cpWju91fb2zTqn5JSDRZ6xNGM2ub8Lgdg"; + + #[test] + #[ignore] + // TODO(eth-implicit) Do not ignore when Wallet Contract build becomes reproducible, + // see https://github.com/utnet-org/utility/pull/10269#discussion_r1430139987. + fn check_wallet_contract() { + assert!(!wallet_contract().code().is_empty()); + let expected_hash = + CryptoHash::from_str(WALLET_CONTRACT_HASH).expect("Failed to parse hash from string"); + assert_eq!(*wallet_contract().hash(), expected_hash); + } + + #[test] + #[ignore] + // TODO(eth-implicit) Do not ignore when Wallet Contract build becomes reproducible, + // see https://github.com/utnet-org/utility/pull/10269#discussion_r1430139987. + fn check_wallet_contract_magic_bytes() { + assert!(!wallet_contract_magic_bytes().code().is_empty()); + let expected_hash = + CryptoHash::from_str(MAGIC_BYTES_HASH).expect("Failed to parse hash from string"); + assert_eq!(*wallet_contract_magic_bytes().hash(), expected_hash); + + let expected_code = format!("near{}", WALLET_CONTRACT_HASH); + assert_eq!(wallet_contract_magic_bytes().code(), expected_code.as_bytes()); + } +} diff --git a/runtime/unc-wallet-contract/wallet-contract/Cargo.lock b/runtime/unc-wallet-contract/wallet-contract/Cargo.lock new file mode 100644 index 000000000..273b4c9ea --- /dev/null +++ b/runtime/unc-wallet-contract/wallet-contract/Cargo.lock @@ -0,0 +1,1529 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + +[[package]] +name = "ahash" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +dependencies = [ + "getrandom 0.2.11", + "once_cell", + "version_check", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bitvec" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" +dependencies = [ + "crypto-mac", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "borsh" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +dependencies = [ + "borsh-derive", + "hashbrown 0.11.2", +] + +[[package]] +name = "borsh-derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate 0.1.5", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytesize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" + +[[package]] +name = "c2-chacha" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27dae93fe7b1e0424dc57179ac396908c26b035a87234809f5c4dfd1b47dc80" +dependencies = [ + "cipher", + "ppv-lite86", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", +] + +[[package]] +name = "dyn-clone" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" + +[[package]] +name = "easy-ext" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53aff6fdc1b181225acdcb5b14c47106726fd8e486707315b1b138baed68ee31" + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "impl-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "unc-abi" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "885db39b08518fa700b73fa2214e8adbbfba316ba82dd510f50519173eadaf73" +dependencies = [ + "borsh", + "schemars", + "semver", + "serde", +] + +[[package]] +name = "unc-account-id" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d258582a1878e6db67400b0504a5099db85718d22c2e07f747fe1706ae7150" +dependencies = [ + "borsh", + "serde", +] + +[[package]] +name = "unc-crypto" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e75673d69fd7365508f3d32483669fe45b03bfb34e4d9363e90adae9dfb416c" +dependencies = [ + "arrayref", + "blake2", + "borsh", + "bs58", + "c2-chacha", + "curve25519-dalek", + "derive_more", + "ed25519-dalek", + "unc-account-id", + "once_cell", + "parity-secp256k1", + "primitive-types", + "rand 0.7.3", + "rand_core 0.5.1", + "serde", + "serde_json", + "subtle", + "thiserror", +] + +[[package]] +name = "unc-primitives" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad1a9a1640539c81f065425c31bffcfbf6b31ef1aeaade59ce905f5df6ac860" +dependencies = [ + "borsh", + "byteorder", + "bytesize", + "chrono", + "derive_more", + "easy-ext", + "hex", + "unc-crypto", + "unc-primitives-core", + "unc-rpc-error-macro", + "unc-vm-errors", + "num-rational", + "once_cell", + "primitive-types", + "rand 0.7.3", + "reed-solomon-erasure", + "serde", + "serde_json", + "smart-default", + "strum", + "thiserror", +] + +[[package]] +name = "unc-primitives-core" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d508f0fc340f6461e4e256417685720d3c4c00bb5a939b105160e49137caba" +dependencies = [ + "base64 0.11.0", + "borsh", + "bs58", + "derive_more", + "unc-account-id", + "num-rational", + "serde", + "sha2 0.10.8", + "strum", +] + +[[package]] +name = "unc-rpc-error-core" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ee0b41c75ef859c193a8ff1dadfa0c8207bc0ac447cc22259721ad769a1408" +dependencies = [ + "quote", + "serde", + "syn 1.0.109", +] + +[[package]] +name = "unc-rpc-error-macro" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e837bd4bacd807073ec5ceb85708da7f721b46a4c2a978de86027fb0034ce31" +dependencies = [ + "unc-rpc-error-core", + "serde", + "syn 1.0.109", +] + +[[package]] +name = "unc-sdk" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15eb3de2defe3626260cc209a6cdb985c6b27b0bd4619fad97dcfae002c3c5bd" +dependencies = [ + "base64 0.13.1", + "borsh", + "bs58", + "unc-abi", + "unc-crypto", + "unc-primitives", + "unc-primitives-core", + "unc-sdk-macros", + "unc-sys", + "unc-vm-logic", + "once_cell", + "schemars", + "serde", + "serde_json", + "wee_alloc", +] + +[[package]] +name = "unc-sdk-macros" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4907affc9f5ed559456509188ff0024f1f2099c0830e6bdb66eb61d5b75912c0" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "unc-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397688591acf8d3ebf2c2485ba32d4b24fc10aad5334e3ad8ec0b7179bfdf06b" + +[[package]] +name = "unc-vm-errors" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0da466a30f0446639cbd788c30865086fac3e8dcb07a79e51d2b0775ed4261e" +dependencies = [ + "borsh", + "unc-account-id", + "unc-rpc-error-macro", + "serde", +] + +[[package]] +name = "unc-vm-logic" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b534828419bacbf1f7b11ef7b00420f248c548c485d3f0cfda8bb6931152f2" +dependencies = [ + "base64 0.13.1", + "borsh", + "bs58", + "byteorder", + "unc-account-id", + "unc-crypto", + "unc-primitives", + "unc-primitives-core", + "unc-vm-errors", + "ripemd", + "serde", + "sha2 0.10.8", + "sha3", + "zeropool-bn", +] + +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parity-scale-codec" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" +dependencies = [ + "arrayvec 0.7.4", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parity-secp256k1" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fca4f82fccae37e8bbdaeb949a4a218a1bbc485d11598f193d2a908042e5fc1" +dependencies = [ + "arrayvec 0.5.2", + "cc", + "cfg-if 0.1.10", + "rand 0.7.3", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primitive-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.11", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "reed-solomon-erasure" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a415a013dd7c5d4221382329a5a3482566da675737494935cbbbcdec04662f9d" +dependencies = [ + "smallvec", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rlp" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1190dcc8c3a512f1eef5d09bb8c84c7f39e1054e174d1795482e18f5272f2e73" +dependencies = [ + "rustc-hex", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "schemars" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "smart-default" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wallet-contract" +version = "0.1.0" +dependencies = [ + "hex", + "unc-sdk", + "rlp", + "serde_json", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.5.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + +[[package]] +name = "zeroize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "zeropool-bn" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e61de68ede9ffdd69c01664f65a178c5188b73f78faa21f0936016a888ff7c" +dependencies = [ + "borsh", + "byteorder", + "crunchy", + "lazy_static", + "rand 0.8.5", + "rustc-hex", +] diff --git a/runtime/unc-wallet-contract/wallet-contract/Cargo.toml b/runtime/unc-wallet-contract/wallet-contract/Cargo.toml new file mode 100644 index 000000000..bbc0dca91 --- /dev/null +++ b/runtime/unc-wallet-contract/wallet-contract/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "wallet-contract" +version = "0.1.0" +publish = false +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +hex = "0.4.2" +serde_json = "1.0.68" +unc-sdk = "4.1.1" +rlp = "0.4.6" + +[profile.release] +codegen-units = 1 +# Tell `rustc` to optimize for small code size. +opt-level = "z" +strip = true +lto = true +debug = false +panic = "abort" +rpath = false +debug-assertions = false +incremental = false +overflow-checks = true + +[workspace] +members = [] diff --git a/runtime/unc-wallet-contract/wallet-contract/src/lib.rs b/runtime/unc-wallet-contract/wallet-contract/src/lib.rs new file mode 100644 index 000000000..71f41d936 --- /dev/null +++ b/runtime/unc-wallet-contract/wallet-contract/src/lib.rs @@ -0,0 +1,53 @@ +//! Temporary implementation of the Wallet Contract. +//! See https://github.com/near/NEPs/issues/518. +//! Must not use in production! +// TODO(eth-implicit) Change to a real Wallet Contract implementation. + +use hex; +use unc_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; +use unc_sdk::{env, unc_bindgen, AccountId, Promise}; +use rlp::Rlp; + +#[unc_bindgen] +#[derive(Default, BorshDeserialize, BorshSerialize)] +pub struct WalletContract {} + +#[unc_bindgen] +impl WalletContract { + /// For the sake of this placeholder implementation, we assume simplified version of the `rlp_transaction` + /// that only has 3 values: `To`, `Value`, and `PublicKey`. We assume this is a transfer transaction. + /// The real implementation would obtain the public key from `Signature`. + pub fn execute_rlp(&self, target: AccountId, rlp_transaction: Vec) { + let rlp = Rlp::new(&rlp_transaction); + + let to: String = match rlp.val_at(0) { + Ok(to) => to, + _ => env::panic_str("Missing `to` field in RLP-encoded transaction."), + }; + if target.to_string() != to { + env::panic_str("`target` not equal to transaction's `To` address."); + } + + let value_bytes: Vec = match rlp.val_at(1) { + Ok(value_bytes) => value_bytes, + _ => env::panic_str("Missing `value` field in RLP-encoded transaction."), + }; + let value = u128::from_be_bytes( + value_bytes.try_into().expect("Incorrect `value` field in RLP-encoded transaction."), + ); + + let signer_public_key_bytes: Vec = match rlp.val_at(2) { + Ok(signer_public_key_bytes) => signer_public_key_bytes, + _ => env::panic_str("Signature extraction failed for RLP-encoded transaction."), + }; + + let hash = env::keccak256(&signer_public_key_bytes); + let signer_address = format!("0x{}", hex::encode(&hash[12..32])); + + if signer_address != env::current_account_id().to_string() { + env::panic_str("Public key does not match the Wallet Contract address."); + } + + Promise::new(target).transfer(value); + } +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..7cb6ce9a3 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,7 @@ +[toolchain] +# This specifies the version of Rust we use to build. +# Individual crates in the workspace may support a lower version, as indicated by `rust-version` field in each crate's `Cargo.toml`. +# The version specified below, should be at least as high as the maximum `rust-version` within the workspace. +channel = "1.75.0" +components = [ "rustfmt", "clippy" ] +targets = [ "wasm32-unknown-unknown" ] diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..39c996ca8 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,7 @@ +use_small_heuristics = "Max" +reorder_imports = true +edition = "2021" +# This option will merge imports, however it's only available in +nightly. +# imports_granularity = "Module" +# fn_args_density = "Compressed" +# overflow_delimited_expr = "true" diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/scripts/binary_release.sh b/scripts/binary_release.sh new file mode 100755 index 000000000..7d981dec5 --- /dev/null +++ b/scripts/binary_release.sh @@ -0,0 +1,65 @@ +#!/bin/bash +set -xeo pipefail + +release="${1:-uncd-release}" + +case "$release" in + uncd-release|nightly-release|perf-release|assertions-release) + ;; + *) + echo "Unsupported release type '$release'. Please provide no argument for normal release or provide nightly-release for nightly." + exit 1 + ;; +esac + +BRANCH=$(git branch --show-current) +COMMIT=$(git rev-parse HEAD) + +os=$(uname) +arch=$(uname -m) +os_and_arch=${os}-${arch} + +function tar_binary { + mkdir -p $1/${os_and_arch} + cp target/release/$1 $1/${os_and_arch}/ + tar -C $1 -czvf $1.tar.gz ${os_and_arch} +} + +make $release + +function upload_binary { + if [ "$release" = "uncd-release" ] + then + tar_binary $1 + tar_file=$1.tar.gz + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os}/${BRANCH}/$1 + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os}/${BRANCH}/${COMMIT}/$1 + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os}/${BRANCH}/${COMMIT}/stable/$1 + + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os_and_arch}/${BRANCH}/$1 + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os_and_arch}/${BRANCH}/${COMMIT}/$1 + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os_and_arch}/${BRANCH}/${COMMIT}/stable/$1 + + aws s3 cp --acl public-read ${tar_file} s3://build.nearprotocol.com/framework/${os_and_arch}/${BRANCH}/${tar_file} + aws s3 cp --acl public-read ${tar_file} s3://build.nearprotocol.com/framework/${os_and_arch}/${BRANCH}/${COMMIT}/${tar_file} + aws s3 cp --acl public-read ${tar_file} s3://build.nearprotocol.com/framework/${os_and_arch}/${BRANCH}/${COMMIT}/stable/${tar_file} + + else + folder="${release%-release}" + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os}/${BRANCH}/${COMMIT}/${folder}/$1 + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os_and_arch}/${BRANCH}/${COMMIT}/${folder}/$1 + fi +} + +upload_binary uncd + +# disabled until we clarify why we need this binary in S3 +# if [ "$release" != "assertions-release" ] +# then +# upload_binary store-validator +# fi + +# if [ "$release" = "release" ] +# then +# upload_binary unc-sandbox +# fi diff --git a/scripts/check_nightly.py b/scripts/check_nightly.py new file mode 100644 index 000000000..d0aac17ac --- /dev/null +++ b/scripts/check_nightly.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +"""Checks whether all expensive tests are mentioned in NayDuck tests list + +Scans all Rust source files looking for expensive tests and than makes sure that +they are all referenced in NayDuck test list files (the nightly/*.txt files). +Returns with success if that's the case; with failure otherwise. + +An expensive test is one which is marked with expensive_tests feature as +follows: + + #[test] + #[cfg_attr(not(feature = "expensive_tests"), ignore)] + fn test_gc_random_large() { + test_gc_random_common(25); + } + +The `test` and `cfg_attr` annotations can be specified in whatever order but +note that the script isn’t too smart about parsing Rust files and using +something more complicated in the `cfg_attr` will confuse it. + +Expensive tests are not executed when running `cargo test` nor are they run in +CI and it’s the purpose of this script to make sure that they are listed for +NayDuck to run. +""" + +import os +import pathlib +import re +import sys +import typing + +import nayduck + +IGNORED_SUBDIRS = ('target', 'target_expensive', 'sandbox') + +EXPENSIVE_DIRECTIVE = '#[cfg_attr(not(feature = "expensive_tests"), ignore)]' +TEST_DIRECTIVE = '#[test]' + + +def expensive_tests_in_file(path: pathlib.Path) -> typing.Iterable[str]: + """Yields names of expensive tests found in given Rust file. + + An expensive test is a function annotated with `test` and a conditional + `ignore` attributes, specifically: + + #[test] + #[cfg_attr(not(feature = "expensive_tests"), ignore)] + fn test_slow() { + // ... + } + + Note that anything more complex in the `cfg_attr` will cause the function + not to recognise the test. + + Args: + path: Path to the Rust source file. + Yields: + Names of functions defining expensive tests (e.g. `test_slow` in example + above). + """ + with open(path) as rd: + is_expensive = False + is_test = False + for line in rd: + line = line.strip() + if not line: + pass + elif line.startswith('#'): + is_expensive = is_expensive or line == EXPENSIVE_DIRECTIVE + is_test = is_test or line == TEST_DIRECTIVE + elif is_expensive or is_test: + if is_expensive and is_test: + match = re.search(r'\bfn\s+([A-Za-z_][A-Za-z_0-9]*)\b', + line) + if match: + yield match.group(1) + is_expensive = False + is_test = False + + +def nightly_tests(repo_dir: pathlib.Path) -> typing.Iterable[str]: + """Yields expensive tests mentioned in the nightly configuration file.""" + for test in nayduck.read_tests_from_file(repo_dir / + nayduck.DEFAULT_TEST_FILE, + include_comments=True): + t = test.split() + try: + # It's okay to comment out a test intentionally. + if t[t[0] == '#'] in ('expensive', '#expensive'): + yield t[-1].split('::')[-1] + except IndexError: + pass + + +def main() -> typing.Optional[str]: + repo_dir = pathlib.Path(__file__).parent.parent + nightly_txt_tests = set(nightly_tests(repo_dir)) + for root, dirs, files in os.walk(repo_dir): + dirs[:] = [ + dirname for dirname in dirs if dirname not in IGNORED_SUBDIRS + ] + path = pathlib.Path(root) + for filename in files: + if filename.endswith('.rs'): + filepath = path / filename + print(f'checking file {filepath}') + for test in expensive_tests_in_file(filepath): + print(f' expensive test {test}') + if test not in nightly_txt_tests: + return f'error: file {filepath} test {test} not in nightly.txt' + print('all tests in nightly') + return None + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/scripts/check_pytests.py b/scripts/check_pytests.py new file mode 100644 index 000000000..656b9f80f --- /dev/null +++ b/scripts/check_pytests.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +"""Checks whether all pytest tests are mentioned in NayDuck or Buildkite + +Lists all Python scripts inside of pytest/tests directory and checks whether +they are referenced in NayDuck test list files (the nightly/*.txt files) or +Buildkite pipeline configuration (the .buildkite/pipeline.yml file). Returns +with success if that's the case; with failure otherwise. +""" +import fnmatch +import os +import pathlib +import random +import re +import sys +import typing + +import yaml + +import nayduck + +# List of globs of Python scripts in the pytest/tests directory which are not +# test but rather helper scripts and libraries. The entire mocknet/ directory +# is covered here as well since those tests are not run on NayDuck any more. +HELPER_SCRIPTS = [ + 'delete_remote_nodes.py', + 'loadtest/*', + 'mocknet/*', + 'shardnet/*', + 'stress/hundred_nodes/*', + 'loadtest/*', + 'replay/*', +] + +PYTEST_TESTS_DIRECTORY = pathlib.Path('pytest/tests') +NIGHTLY_TESTS_FILE = pathlib.Path(nayduck.DEFAULT_TEST_FILE) + +# TODO: this should read ci.yml and fetch the list of tests from there +# Currently, the list of tests is hardcoded, so if a test is removed/added to ci.yml then the list +# here should be updated too +GHA_TESTS = [ + "sanity/backward_compatible.py", + "sanity/db_migration.py", + "sanity/spin_up_cluster.py", + "sanity/upgradable.py", +] + +StrGenerator = typing.Generator[str, None, None] + + +def list_test_files(topdir: pathlib.Path) -> StrGenerator: + """Yields all *.py files in a given directory traversing it recursively. + + Args: + topdir: Path to the directory to traverse. Directory is traversed + recursively. + Yields: + Paths (as str objects) to all the Python source files in the directory + relative to the top directory. __init__.py files (and in fact any files + starting with __) are ignored. + """ + for dirname, _, filenames in os.walk(topdir): + dirpath = pathlib.Path(dirname).relative_to(topdir) + for filename in filenames: + if not filename.startswith('__') and filename.endswith('.py'): + yield str(dirpath / filename) + + +def read_nayduck_tests(path: pathlib.Path) -> StrGenerator: + """Reads NayDuck test file and yields all tests mentioned there. + + The NayDuck test list files are ones with .txt extension. Only pytest and + mocknet tests are taken into account and returned. Enabled tests as well as + those commented out with a corresponding TODO comment are considered. + + Args: + path: Path to the test set. + Yields: + pytest and mocknet tests mentioned in the file. May include duplicates. + """ + + def extract_name(line: str) -> StrGenerator: + tokens = line.split() + idx = 1 + (tokens[0] == '#') + while idx < len(tokens) and tokens[idx].startswith('--'): + idx += 1 + if idx < len(tokens): + yield tokens[idx] + + found_todo = False + for line in nayduck.read_tests_from_file(path, include_comments=True): + line = re.sub(r'\s+', ' ', line.strip()) + if re.search(r'^(?:pytest|mocknet) ', line): + found_todo = False + yield from extract_name(line) + elif found_todo and re.search(r'^# ?(?:pytest|mocknet) ', line): + yield from extract_name(line) + elif re.search('^# ?TODO.*#[0-9]{4,}', line): + found_todo = True + elif not line.strip().startswith('#'): + found_todo = False + + +def print_error(missing: typing.Collection[str]) -> None: + """Formats and outputs an error message listing missing tests.""" + this_file = os.path.relpath(__file__) + example = random.sample(tuple(missing), 1)[0] + if example.startswith('mocknet/'): + example = 'mocknet ' + example + else: + example = 'pytest ' + example + msg = '''\ +Found {count} pytest file{s} (i.e. Python file{s} in pytest/tests directory) +which are not included in any of the nightly/*.txt files: + +{missing} + +Add the test{s} to one of the lists in nightly/*.txt files. For example as: + + {example} + +If the test is temporarily disabled, add it to one of the files with an +appropriate {todo} comment. For example: + + # {todo}(#1234): Enable the test again once + # {example} + +Note that the {todo} comment must reference a GitHub issue (i.e. must +contain a # string). + +If the file is not a test but a helper library, consider moving it out +of the pytest/tests directory to pytest/lib or add it to HELPER_SCRIPTS +list at the top of {this_file} file.'''.format( + count=len(missing), + s='' if len(missing) == 1 else 's', + missing='\n'.join( + ' - pytest/tests/' + name for name in sorted(missing)), + example=example, + this_file=this_file, + todo='TO' + 'DO', + ) + print(msg, file=sys.stderr) + + +def main() -> int: + """Main function of the script; returns integer exit code.""" + missing = set(list_test_files(PYTEST_TESTS_DIRECTORY)) + count = len(missing) + missing.difference_update( + read_nayduck_tests(pathlib.Path(nayduck.DEFAULT_TEST_FILE))) + missing.difference_update(GHA_TESTS) + missing = set(filename for filename in missing if not any( + fnmatch.fnmatch(filename, pattern) for pattern in HELPER_SCRIPTS)) + if missing: + print_error(missing) + return 1 + print(f'All {count} tests included') + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/scripts/fix_nightly_feature_flags.py b/scripts/fix_nightly_feature_flags.py new file mode 100644 index 000000000..40b4026c9 --- /dev/null +++ b/scripts/fix_nightly_feature_flags.py @@ -0,0 +1,287 @@ +#!/bin/python3 + +# This script checks that nightly feature flags are declared consistently so +# we avoid nasty subtle bugs. See the individual checks below for details. + +import toml +import re +import difflib +import sys + + +class Crate: + + def __init__(self, dir): + self.dir = dir + self.height = None + self.load_toml() + + def load_toml(self): + self.toml = toml.load(self.dir + "/Cargo.toml") + self.name = self.toml["package"]["name"] + self.features = self.toml.get("features", {}) + + self.has_nightly_feature = "nightly" in self.features + self.has_nightly_protocol_feature = "nightly_protocol" in self.features + self.local_nightly_features = self.get_local_nightly_features() + self.local_nightly_protocol_features = self.get_local_nightly_protocol_features( + ) + self.dependency_nightly_features = self.get_dependency_nightly_features( + ) + self.dependency_nightly_protocol_features = self.get_dependency_nightly_protocol_features( + ) + + def get_local_nightly_features(self): + if not self.has_nightly_feature: + return [] + return [ + feature for feature in self.features["nightly"] + if '/' not in feature + ] + + def get_local_nightly_protocol_features(self): + if not self.has_nightly_protocol_feature: + return [] + return [ + feature for feature in self.features["nightly_protocol"] + if '/' not in feature + ] + + def get_dependency_nightly_features(self): + if not self.has_nightly_feature: + return [] + return [ + feature for feature in self.features["nightly"] if '/' in feature + ] + + def get_dependency_nightly_protocol_features(self): + if not self.has_nightly_protocol_feature: + return [] + return [ + feature for feature in self.features["nightly_protocol"] + if '/' in feature + ] + + def build_deps(self, crates_by_name): + self.deps = [ + crates_by_name[dep] + for dep in self.toml['dependencies'].keys() + if dep in crates_by_name + ] + + def build_transitive_deps(self): + visited = set() + result = [] + + def recursion_helper(crate): + result.append(crate) + for dep in crate.deps: + if dep.name not in visited: + visited.add(dep.name) + recursion_helper(dep) + + for dep in self.deps: + if dep.name not in visited: + visited.add(dep.name) + recursion_helper(dep) + self.transitive_deps = result + + def write_toml(self, apply_fix): + toml_file_name = self.dir + "/Cargo.toml" + existing_toml_text = open(toml_file_name, "r").read() + new_toml_text = existing_toml_text + + def ensure_features(): + nonlocal new_toml_text + if "[features]" not in new_toml_text: + new_toml_text += "\n[features]\nnightly = []\nnightly_protocol = []\n" + if "nightly =" not in new_toml_text: + new_toml_text = re.sub(r"\[features\]", + "[features]\nnightly = []", + new_toml_text) + if "nightly_protocol =" not in new_toml_text: + new_toml_text = re.sub(r"\[features\]", + "[features]\nnightly_protocol = []", + new_toml_text) + + def replace_nightly(deps): + nonlocal new_toml_text + desired_text = 'nightly = [\n' + for dep in deps: + desired_text += ' "{}",\n'.format(dep) + desired_text += ']' + regex = re.compile(r"nightly\s*=\s*\[.*?\]", re.DOTALL) + (new_toml_text, n) = re.subn(regex, desired_text, new_toml_text) + assert n == 1, "Substitution failed for crate {}".format(self.name) + + def replace_nightly_protocol(deps): + nonlocal new_toml_text + desired_text = 'nightly_protocol = [\n' + for dep in deps: + desired_text += ' "{}",\n'.format(dep) + desired_text += ']' + regex = re.compile(r"nightly_protocol\s*=\s*\[.*?\]", re.DOTALL) + (new_toml_text, n) = re.subn(regex, desired_text, new_toml_text) + assert n == 1, "Substitution failed for crate {}".format(self.name) + + if self.has_nightly_feature: + ensure_features() + replace_nightly( + list(sorted(self.local_nightly_features)) + + list(sorted(self.dependency_nightly_features))) + if self.has_nightly_protocol_feature: + ensure_features() + replace_nightly_protocol( + list(sorted(self.local_nightly_protocol_features)) + + list(sorted(self.dependency_nightly_protocol_features))) + + if apply_fix: + open(toml_file_name, "w").write(new_toml_text) + else: + if new_toml_text != existing_toml_text: + print("Nightly feature flags need updating:") + sys.stdout.writelines( + difflib.unified_diff( + existing_toml_text.splitlines(keepends=True), + new_toml_text.splitlines(keepends=True), + fromfile=toml_file_name, + tofile=toml_file_name)) + return False + return True + + +workspace_toml = toml.load("Cargo.toml") +crate_dirs = workspace_toml["workspace"]["members"] + +crates = [Crate(dir) for dir in crate_dirs] +crate_by_name = {crate.name: crate for crate in crates} +for crate in crates: + crate.build_deps(crate_by_name) +for crate in crates: + crate.build_transitive_deps() + +for crate in crates: + # check 1: we should either have neither feature, or both. + if crate.has_nightly_feature != crate.has_nightly_protocol_feature: + if crate.has_nightly_feature: + print( + "crate {} has nightly feature but not nightly_protocol. Adding nightly_protocol" + .format(crate.name)) + crate.has_nightly_protocol_feature = True + crate.local_nightly_features.append('nightly_protocol') + else: + print( + "crate {} has nightly_protocol feature but not nightly. Adding nightly" + .format(crate.name)) + crate.has_nightly_feature = True + crate.local_nightly_features.append('nightly_protocol') + + # check 2: nightly_protocol should not activate on local features. + if crate.local_nightly_protocol_features != []: + print( + "crate {} has local nightly_protocol features: {}, moving to nightly" + .format(crate.name, crate.local_nightly_protocol_features)) + crate.local_nightly_features += crate.local_nightly_protocol_features + crate.local_nightly_protocol_features = [] + + # check 3: nightly should activate local nightly_protocol. + if crate.has_nightly_feature: + if 'nightly_protocol' not in crate.local_nightly_features: + print( + "crate {} nightly feature does not include nightly_protocol. Adding nightly_protocol" + .format(crate.name)) + crate.local_nightly_features.append('nightly_protocol') + +while True: + fixed = False + for crate in crates: + # check 4: nightly should be present if there's a transitive dependency that has it and there's also a transitive dependent that + # has it. Without this, suppose A depends on B and B depends on C, but B does not declare a nightly feature. There would be no way + # for A's nightly feature to activate C's nightly feature, since C is not a direct dependency of A. So to avoid that kind of headache + # we require B to also declare nightly. + transitive_dependencies_with_nightly = [ + dep for dep in crate.transitive_deps if dep.has_nightly_feature + ] + dependents_with_nightly = [ + other for other in crates + if crate in other.deps and other.has_nightly_feature + ] + if transitive_dependencies_with_nightly != [] and dependents_with_nightly != [] and crate.has_nightly_feature == False: + print( + "crate {} has both dependencies (e.g. {}) with nightly and dependents (e.g. {}) with nightly but does not have nightly feature itself. Adding nightly feature" + .format(crate.name, + transitive_dependencies_with_nightly[0].name, + dependents_with_nightly[0].name)) + crate.has_nightly_feature = True + crate.has_nightly_protocol_feature = True + crate.local_nightly_features.append('nightly_protocol') + fixed = True + if not fixed: + break + +for crate in crates: + if not crate.has_nightly_feature: + continue + # check 5: nightly should activate precisely the nightly flags of all direct dependencies. + # Same with nightly_protocol. + dependencies_with_nightly = [ + dep for dep in crate.deps if dep.has_nightly_feature + ] + desired_dependency_features = [ + dep.name + '/nightly' for dep in dependencies_with_nightly + ] + if set(crate.dependency_nightly_features) != set( + desired_dependency_features): + print("crate {} has nightly dependencies {} but should be {}, fixing". + format(crate.name, crate.dependency_nightly_features, + desired_dependency_features)) + crate.dependency_nightly_features = desired_dependency_features + + dependencies_with_nightly_protocol = [ + dep for dep in crate.deps if dep.has_nightly_protocol_feature + ] + desired_dependency_features = [ + dep.name + '/nightly_protocol' + for dep in dependencies_with_nightly_protocol + ] + if set(crate.dependency_nightly_protocol_features) != set( + desired_dependency_features): + print( + "crate {} has nightly_protocol dependencies {} but should be {}, fixing" + .format(crate.name, crate.dependency_nightly_protocol_features, + desired_dependency_features)) + crate.dependency_nightly_protocol_features = desired_dependency_features + + # check 6: if any feature declared locally has a name that coincides with a nightly + # feature of a transitive dependency, that feature should also be included in the + # local nightly list. Otherwise, this is likely a mistake, because nightly would + # activate a feature in a dependency crate but not a dependent crate. + all_dependency_local_nightly_features = [] + for dep in crate.transitive_deps: + all_dependency_local_nightly_features += dep.local_nightly_features + for feature in crate.features.keys(): + if feature == 'nightly' or feature == 'nightly_protocol': + continue + if feature in all_dependency_local_nightly_features: + if feature not in crate.local_nightly_features: + print( + "crate {} has local feature {} that coincides with a dependency nightly feature. Adding to local nightly list" + .format(crate.name, feature)) + crate.local_nightly_features.append(feature) + + # TODO: there's a similar check we should do that checks if A depends on B and they + # both have the same nightly feature name, then that feature of A should activate + # that feature of B. + +apply_fix = len(sys.argv) == 2 and sys.argv[1] == 'fix' +if apply_fix: + for crate in crates: + crate.write_toml(True) +else: + all_correct = True + for crate in crates: + all_correct = all_correct and crate.write_toml(False) + if not all_correct: + print("Run the following to apply nightly feature flag fixes:") + print(" python3 scripts/fix_nightly_feature_flags.py fix") + sys.exit(1) diff --git a/scripts/flaky_test_check.py b/scripts/flaky_test_check.py new file mode 100644 index 000000000..b4338e196 --- /dev/null +++ b/scripts/flaky_test_check.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +import os +from testlib import clean_binary_tests, build_tests, test_binaries, run_test + +TIMES = 10 + +if __name__ == "__main__": + clean_binary_tests() + build_tests() + binaries = test_binaries(exclude=[r'test_regression-.*']) + print(f'========= collected {len(binaries)} test binaries:') + print('\n'.join(binaries)) + + print(f"Run all tests sequentially for {TIMES} times: ") + test_with_fails = {} + for binary in binaries: + fails = [] + for i in range(TIMES): + exitcode, stdout, stderr = run_test(binary, isolate=False) + print(f'Run {binary} {i+1} of {TIMES}, exit code {exitcode}') + if exitcode != 0: + fails.append(exitcode) + if fails: + test_with_fails[binary] = fails + + if test_with_fails: + print("Some tests failed: ") + for t, f in test_with_fails.items(): + print(f'{t} failed {len(f)} times') diff --git a/scripts/formatting b/scripts/formatting new file mode 100755 index 000000000..bd63fc7c2 --- /dev/null +++ b/scripts/formatting @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 + +import pathlib +import shlex +import subprocess +import sys +import typing + +try: + import colorama + RED = colorama.Fore.RED + GREEN = colorama.Fore.GREEN + RESET_ALL = colorama.Style.RESET_ALL +except ImportError: + RED = GREEN = RESET_ALL = '' + +SELF_FILE = pathlib.Path(__file__).resolve() +REPO_DIR = SELF_FILE.parent.parent +SELF_RELATIVE = SELF_FILE.relative_to(REPO_DIR) + + +def run(args: typing.Sequence[typing.Union[str, pathlib.Path]]) -> bool: + print('+ ' + ' '.join(shlex.quote(str(arg)) for arg in args), + file=sys.stderr) + return subprocess.run(args, + cwd=REPO_DIR, + stdin=subprocess.DEVNULL, + check=False).returncode == 0 + + +def run_tools(*, check: bool) -> str: + langs = [] + + try: + import yapf as _ + has_yapf = True + except ImportError: + has_yapf = run((sys.executable, '-m', 'pip', 'install', 'yapf', + '--no-warn-script-location')) + + flag = '-pdr' if check else '-pir' + args = ('pytest', 'scripts', 'debug_scripts', SELF_FILE) + if not has_yapf or not run((sys.executable, '-m', 'yapf', flag) + args): + langs.append('Python') + + return ' and '.join(langs) + + +def main(argv: typing.Sequence[str]) -> typing.Optional[str]: + if len(argv) != 2 or argv[1] not in ('--fix', '--check'): + return 'usage: {} (--check | --fix)'.format(shlex.quote(argv[0])) + + check = argv[1] == '--check' + what_broke = run_tools(check=check) + if not what_broke: + print(f'{GREEN}All {"good" if check else "fixed"}.{RESET_ALL}') + return None + elif check: + return f''' +{RED}Found formatting issues in {what_broke} code.{RESET_ALL} +Please fix by running: + ./{SELF_RELATIVE} --fix''' + else: + return f'\n{RED}Failed fixing {what_broke} code.{RESET_ALL}\n' + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/scripts/install_precommit.sh b/scripts/install_precommit.sh new file mode 100755 index 000000000..9ec8a8f05 --- /dev/null +++ b/scripts/install_precommit.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +echo "Copying rustfmt.toml into ~/.config/rustfmt/" +mkdir -p "${HOME}"/.config/rustfmt +cp $(dirname "${0}")/../rustfmt.toml "${HOME}"/.config/rustfmt/ + +if [[ ! -f "${HOME}/.local/bin/pre-commit" ]]; then + echo "installing pre-commit program" + if [[ ! -z "$(command -v pip3)" ]]; then + pip3 install pre-commit + elif [[ ! -z "$(command -v pip)" ]]; then + pip install pre-commit + else + echo "Please install pip3 or pip and rerun this script" + exit 1 + fi +fi + +"${HOME}"/.local/bin/pre-commit install diff --git a/scripts/mac-release.sh b/scripts/mac-release.sh new file mode 100755 index 000000000..ef5f43bff --- /dev/null +++ b/scripts/mac-release.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +set -eo pipefail + +release="release" +if [ ! -z "$1" ]; +then + release=$1 +fi + +if [ "$release" != "release" ] && [ "$release" != "nightly-release" ] && [ "$release" != "perf-release" ] +then + echo "Please provide no argument for normal release or provide nightly-release for nightly" + exit 1 +fi + +branch=${BUILDKITE_BRANCH:-${GITHUB_REF##*/}} +commit=${BUILDKITE_COMMIT:-${GITHUB_SHA}} +if [[ ${commit} == "HEAD" ]]; then + commit=$(git rev-parse HEAD) +fi +os=$(uname) +arch=$(uname -m) +os_and_arch=${os}-${arch} + +function tar_binary { + mkdir -p $1/${os_and_arch} + cp target/release/$1 $1/${os_and_arch}/ + tar -C $1 -czvf $1.tar.gz ${os_and_arch} +} + +make $release + +function upload_binary { + if [ "$release" == "release" ] + then + tar_binary $1 + tar_file=$1.tar.gz + + if [ "$arch" == "x86_64" ] + then + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os}/${branch}/$1 + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os}/${branch}/${commit}/$1 + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os}/${branch}/${commit}/stable/$1 + fi + + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os_and_arch}/${branch}/$1 + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os_and_arch}/${branch}/${commit}/$1 + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os_and_arch}/${branch}/${commit}/stable/$1 + + aws s3 cp --acl public-read ${tar_file} s3://build.nearprotocol.com/framework/${os_and_arch}/${branch}/${tar_file} + aws s3 cp --acl public-read ${tar_file} s3://build.nearprotocol.com/framework/${os_and_arch}/${branch}/${commit}/${tar_file} + aws s3 cp --acl public-read ${tar_file} s3://build.nearprotocol.com/framework/${os_and_arch}/${branch}/${commit}/stable/${tar_file} + elif [ "$release" == "perf-release" ] + then + if [ "$arch" == "x86_64" ] + then + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os}/${branch}/${commit}/perf/$1 + fi + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os_and_arch}/${branch}/${commit}/perf/$1 + else + if [ "$arch" == "x86_64" ] + then + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os}/${branch}/${commit}/nightly/$1 + fi + aws s3 cp --acl public-read target/release/$1 s3://build.nearprotocol.com/framework/${os_and_arch}/${branch}/${commit}/nightly/$1 + fi +} + +upload_binary uncd +upload_binary store-validator + +if [ "$release" == "release" ] +then + upload_binary unc-sandbox +fi diff --git a/scripts/nayduck.py b/scripts/nayduck.py new file mode 100755 index 000000000..dfb6848dd --- /dev/null +++ b/scripts/nayduck.py @@ -0,0 +1,368 @@ +#!/usr/bin/env python3 +"""Runs integration tests in the cloud on NayDuck. + +To request a new run, use the following command: + + python3 scripts/nayduck.py \ + --branch \ + --test-file .txt + +Scheduled runs can be seen at . + +See README.md in nightly directory for documentation of the test suite file +format. Note that you must be a member of the unc or unc Protocol +organisation on GitHub to authenticate (). + +The source code for NayDuck itself is at . +""" + +import getpass +import json +import os +import pathlib +import shlex +import subprocess +import sys +import typing + +REPO_DIR = pathlib.Path(__file__).resolve().parents[1] + +DEFAULT_TEST_FILE = 'nightly/nightly.txt' +NAYDUCK_BASE_HREF = 'https://nayduck.near.org' + + +def _parse_args(): + import argparse + + default_test_path = (pathlib.Path(__file__).parent.parent / + DEFAULT_TEST_FILE) + + parser = argparse.ArgumentParser(description='Run tests.') + parser.add_argument( + '--cancel', + '-c', + help='Cancel scheduled run. In progress tests cannot be stopped.') + parser.add_argument('--branch', + '-b', + help='Branch to test. By default gets current one.') + parser.add_argument( + '--sha', + '-s', + help= + 'Commit sha to test. By default gets current one. This is ignored if branch name is provided' + ) + group = parser.add_mutually_exclusive_group() + group.add_argument('--test-file', + '-t', + default=default_test_path, + help=f'Test set file; {DEFAULT_TEST_FILE} by default.') + group.add_argument('--stdin', + '-i', + action='store_true', + help='Read test set from standard input.') + parser.add_argument('--run-locally', + '-l', + action='store_true', + help='Run tests locally.') + parser.add_argument( + '--dry-run', + '-n', + action='store_true', + help='Prints list of tests to execute, without doing anything') + args = parser.parse_args() + + return args + + +def get_sha(branch: str): + try: + sha = subprocess.check_output(['git', 'rev-parse', branch], text=True) + + except subprocess.CalledProcessError as e: + remote_branch = 'remotes/origin/' + branch + print( + f"Couldn't find a local branch \'{branch}\'. Trying remote: {remote_branch}" + ) + sha = subprocess.check_output( + ['git', 'rev-parse', remote_branch], + text=True, + ) + + return sha + + +def get_branch(sha: str = ""): + if sha: + return subprocess.check_output(['git', 'name-rev', sha], + text=True).strip().split(' ')[-1] + else: + return subprocess.check_output( + ['git', 'rev-parse', '--abbrev-ref', "HEAD"], text=True).strip() + + +FileReader = typing.Callable[[pathlib.Path], str] + + +def read_tests_from_file( + path: pathlib.Path, + *, + include_comments: bool = False, + reader: FileReader = lambda path: path.read_text() +) -> typing.Iterable[str]: + """Reads lines from file handling `./` includes. + + Returns an iterable over lines in given file but also handles `./` + syntax for including other files in the output and optionally filters + commented out lines. + + A `./` syntax acts like C's #include directive, Rust's `include!` + macro or shell's `source` command. All lines are read from file at + as if they were directly in the source file. The `./` directives are + handled recursively up to three levels deep. + + If include_comments is True, `#./` lines are handled as well with all + included line commented out. This is useful to comment out a include a with + TODO comment included and have check_nightly.py and check_pytest.py scripts + still recognise those includes. Note that the line must start with `#./`, + i.e. there must be no space between hash and the dot. + + Args: + path: Path to the file to read. + include_comments: By default empty lines and lines whose first non-space + character is hash are ignored and not included in the output. With + this set to `True` such lines are included as well. + reader: A callback which reads text content of a given file. This is + used by NayDuck. + Returns: + An iterable over lines in the given file. All lines are stripped of + leading and trailing white space. + """ + return __read_tests(reader(path).splitlines(), + filename=path, + dirpath=path.parent, + include_comments=include_comments, + reader=reader) + + +def read_tests_from_stdin( + *, + include_comments: bool = False, + reader: FileReader = lambda path: path.read_text() +) -> typing.Iterable[str]: + """Reads lines from standard input handling `./` includes. + + Behaves like `read_tests_from_file` but rather than reading contents of + given file reads lines from standard input. `./` includes are + resolved relative to current working directory. + + Returns: + An iterable over lines in the given file. All lines are stripped of + leading and trailing white space. + """ + return __read_tests(sys.stdin, + filename='', + dirpath=pathlib.Path.cwd(), + include_comments=include_comments, + reader=reader) + + +def __read_tests( + lines: typing.Iterable[str], + *, + filename: typing.Union[str, pathlib.Path], + dirpath: pathlib.Path, + include_comments: bool = False, + reader: FileReader = lambda path: path.read_text() +) -> typing.Iterable[str]: + + def impl(lines: typing.Iterable[str], + filename: typing.Union[str, pathlib.Path], + dirpath: pathlib.Path, + depth: int = 1, + comment: bool = False) -> typing.Iterable[str]: + for lineno, line in enumerate(lines, start=1): + line = line.rstrip() + if line.startswith('./') or (include_comments and + line.startswith('#./')): + if depth == 3: + print(f'{filename}:{lineno}: ignoring {line}; ' + f'would exceed depth limit of {depth}') + else: + incpath = dirpath / line.lstrip('#') + yield from impl( + reader(incpath).splitlines(), incpath, incpath.parent, + depth + 1, comment or line.startswith('#')) + elif include_comments or (line and line[0] != '#'): + if comment and not line.startswith('#'): + line = '#' + line + yield line + + return impl(lines, filename, dirpath) + + +def github_auth(code_path: pathlib.Path): + print('Go to the following link in your browser:\n\n{}/login/cli\n'.format( + NAYDUCK_BASE_HREF)) + code = getpass.getpass('Enter authorisation code: ') + code_path.parent.mkdir(parents=True, exist_ok=True) + code_path.write_text(code) + return code + + +def _parse_timeout(timeout: typing.Optional[str]) -> typing.Optional[int]: + """Parses timeout interval and converts it into number of seconds. + + Args: + timeout: An integer with an optional ‘h’, ‘m’ or ‘s’ suffix which + multiply the integer by 3600, 60 and 1 respectively. + Returns: + Interval in seconds. + """ + if not timeout: + return None + mul_ary = {'h': 3600, 'm': 60, 's': 1} + mul = mul_ary.get(timeout[-1]) + if mul: + timeout = timeout[:-1] + else: + mul = 1 + return int(timeout) * mul + + +def run_locally(args, tests): + for test in tests: + # See nayduck specs at https://github.com/near/nayduck/blob/master/lib/testspec.py + fields = test.split() + + timeout = None + index = 1 + ignored = [] + while len(fields) > index and fields[index].startswith('--'): + if fields[index].startswith("--timeout="): + timeout = fields[index][10:] + elif fields[index] != '--skip-build': + ignored.append(fields[index]) + index += 1 + + del fields[1:index] + message = f'Running ‘{"".join(fields)}’' + if ignored: + message = f'{message} (ignoring flags ‘{" ".join(ignored)}`)' + if not args.dry_run: + print(message) + + if fields[0] == 'expensive': + # TODO --test doesn't work + cmd = [ + 'cargo', + 'test', + '-p', + fields[1], # '--test', fields[2], + '--features', + 'expensive_tests', + '--', + '--exact', + fields[3] + ] + cwd = REPO_DIR + elif fields[0] in ('pytest', 'mocknet'): + fields[0] = sys.executable + fields[1] = os.path.join('tests', fields[1]) + cmd = fields + cwd = REPO_DIR / 'pytest' + else: + print(f'Unrecognised test category ‘{fields[0]}’', file=sys.stderr) + continue + if args.dry_run: + print('( cd {} && {} )'.format( + shlex.quote(str(cwd)), + ' '.join(shlex.quote(str(arg)) for arg in cmd))) + continue + print(f"RUNNING COMMAND cwd = {cwd} cmd = {cmd}") + subprocess.check_call(cmd, cwd=cwd, timeout=_parse_timeout(timeout)) + + +def run_command(args, tests): + import requests + + try: + import colorama + styles = (colorama.Fore.RED, colorama.Fore.GREEN, + colorama.Style.RESET_ALL) + except ImportError: + styles = ('', '', '') + + code_path = pathlib.Path( + os.environ.get('XDG_CONFIG_HOME') or + pathlib.Path.home() / '.config') / 'nayduck-code' + if code_path.is_file(): + code = code_path.read_text().strip() + else: + code = github_auth(code_path) + + if args.dry_run: + for test in tests: + print(test) + return + + if args.branch: + test_branch = args.branch + test_sha = get_sha(test_branch).strip() + + else: + test_sha = args.sha or get_sha("HEAD").strip() + test_branch = get_branch(test_sha) + + post = {'branch': test_branch, 'sha': test_sha, 'tests': list(tests)} + + print("Scheduling tests for: \n" + f"branch - {post['branch']} \n" + f"commit hash - {post['sha']}") + + while True: + print('Sending request ...') + if args.cancel: + res = requests.post(NAYDUCK_BASE_HREF + + f'/api/run/{args.cancel}/cancel', + cookies={'nay-code': code}) + else: + res = requests.post(NAYDUCK_BASE_HREF + '/api/run/new', + json=post, + cookies={'nay-code': code}) + if res.status_code != 401: + break + print(f'{styles[0]}Unauthorised.{styles[2]}\n') + code = github_auth(code_path) + + if res.status_code == 200: + json_res = json.loads(res.text) + if args.cancel: + print(styles[1] + 'Cancelled ' + str(json_res) + ' test(s)' + + styles[2]) + else: + print(styles[json_res['code'] == 0] + json_res['response'] + + styles[2]) + else: + print(f'{styles[0]}Got status code {res.status_code}:{styles[2]}\n') + print(res.text) + code = res.cookies.get('nay-code') + if code: + code_path.write_text(code) + + +def main(): + args = _parse_args() + + if args.stdin: + tests = list(read_tests_from_stdin()) + else: + tests = list(read_tests_from_file(pathlib.Path(args.test_file))) + + if args.run_locally: + run_locally(args, tests) + else: + run_command(args, tests) + + +if __name__ == "__main__": + main() diff --git a/scripts/nodelib.py b/scripts/nodelib.py new file mode 100755 index 000000000..c371e4633 --- /dev/null +++ b/scripts/nodelib.py @@ -0,0 +1,449 @@ +#!/usr/bin/env python3 + +import json +import os +import subprocess +import urllib + +try: + input = raw_input +except NameError: + pass + +USER = str(os.getuid()) + ':' + str(os.getgid()) +"""Installs cargo/Rust.""" + + +def install_cargo(): + try: + subprocess.call([os.path.expanduser('~/.cargo/bin/cargo'), '--version']) + except OSError: + print("Installing Rust...") + subprocess.check_output('curl https://sh.rustup.rs -sSf | sh -s -- -y', + shell=True) + + +"""Inits the node configuration using docker.""" + + +def docker_init(image, home_dir, init_flags): + subprocess.check_output(['mkdir', '-p', home_dir]) + subprocess.check_output([ + 'docker', 'run', '-u', USER, '-v', + '%s:/srv/near' % home_dir, '-v', + os.path.abspath('near/res') + + ':/near/res', image, 'near', '--home=/srv/near', 'init' + ] + init_flags) + + +"""Inits the node configuration using local build.""" + + +def nodocker_init(home_dir, is_release, init_flags): + target = './target/%s/uncd' % ('release' if is_release else 'debug') + cmd = [target] + if home_dir: + cmd.extend(['--home', home_dir]) + cmd.extend(['init']) + subprocess.call(cmd + init_flags) + + +def get_chain_id_from_flags(flags): + """Retrieve requested chain id from the flags.""" + chain_id = None + flags_iter = iter(flags) + for flag in flags_iter: + if flag.startswith('--chain-id='): + chain_id = flag[len('--chain-id='):] + elif flag == '--chain-id': + chain_id = next(flags_iter, chain_id) + return chain_id + + +"""Compile given package using cargo""" + + +def compile_package(package_name, is_release): + flags = ['-p', package_name] + if is_release: + flags = ['--release'] + flags + code = subprocess.call([os.path.expanduser('cargo'), 'build'] + flags) + if code != 0: + print("Compilation failed, aborting") + exit(code) + + +"""Checks if there is already everything setup on this machine, otherwise sets up NEAR node.""" + + +def check_and_setup(nodocker, + is_release, + image, + home_dir, + init_flags, + no_gas_price=False): + if nodocker: + compile_package('uncd', is_release) + + chain_id = get_chain_id_from_flags(init_flags) + if os.path.exists(os.path.join(home_dir, 'config.json')): + with open(os.path.join(os.path.join(home_dir, 'genesis.json'))) as fd: + genesis_config = json.load(fd) + if chain_id and genesis_config['chain_id'] != chain_id: + if chain_id == 'testnet': + print( + "Folder %s already has network configuration for %s, which is not the official testnet.\n" + "Use ./scripts/start_localnet.py instead to keep running with existing configuration.\n" + "If you want to run a different network, either specify different --home or remove %s to start from scratch." + % (home_dir, genesis_config['chain_id'], home_dir)) + elif genesis_config['chain_id'] == 'testnet': + print( + "Folder %s already has network configuration for the official testnet.\n" + "Use ./scripts/start_testnet.py instead to keep running it.\n" + "If you want to run a different network, either specify different --home or remove %s to start from scratch" + % (home_dir, home_dir)) + elif chain_id != '': + print( + "Folder %s already has network configuration for %s. Use ./scripts/start_localnet.py to continue running it." + % (home_dir, genesis_config['chain_id'])) + exit(1) + print("Using existing node configuration from %s for %s" % + (home_dir, genesis_config['chain_id'])) + return + + print("Setting up network configuration.") + if len([x for x in init_flags if x.startswith('--account-id')]) == 0: + prompt = "Enter your account ID" + if chain_id: + prompt += " (leave empty if not going to be a validator): " + else: + prompt += ": " + account_id = input(prompt) + if account_id: + init_flags.append('--account-id=%s' % account_id) + + if chain_id == 'testnet': + testnet_genesis_hash = open('near/res/testnet_genesis_hash').read() + testnet_genesis_records = 'near/res/testnet_genesis_records_%s.json' % testnet_genesis_hash + if not os.path.exists(testnet_genesis_records): + print('Downloading testnet genesis records') + url = 'https://s3-us-west-1.amazonaws.com/testnet.nearprotocol.com/testnet_genesis_records_%s.json' % testnet_genesis_hash + urllib.urlretrieve(url, testnet_genesis_records) + init_flags.extend([ + '--genesis-config', 'near/res/testnet_genesis_config.json', + '--genesis-records', testnet_genesis_records, '--genesis-hash', + testnet_genesis_hash + ]) + + if nodocker: + nodocker_init(home_dir, is_release, init_flags) + else: + docker_init(image, home_dir, init_flags) + if no_gas_price: + filename = os.path.join(home_dir, 'genesis.json') + with open(filename) as fd: + genesis_config = json.load(fd) + genesis_config['gas_price'] = 0 + genesis_config['min_gas_price'] = 0 + json.dump(genesis_config, open(filename, 'w')) + + +def print_staking_key(home_dir): + key_path = os.path.join(home_dir, 'validator_key.json') + if not os.path.exists(key_path): + return + + with open(key_path) as fd: + key_file = json.load(fd) + if not key_file['account_id']: + print("Node is not staking. Re-run init to specify staking account.") + return + print("Stake for user '%s' with '%s'" % + (key_file['account_id'], key_file['public_key'])) + + +"""Stops and removes given docker container.""" + + +def docker_stop_if_exists(name): + try: + subprocess.Popen(['docker', 'stop', name], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate() + except subprocess.CalledProcessError: + pass + try: + subprocess.Popen(['docker', 'rm', name], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate() + except subprocess.CalledProcessError: + pass + + +"""Checks the ports saved in config.json""" + + +def get_port(home_dir, net): + config = json.load(open(os.path.join(home_dir, 'config.json'))) + p = config[net]['addr'][config[net]['addr'].find(':') + 1:] + return p + ":" + p + + +"""Runs NEAR core inside the docker container for isolation and easy update with Watchtower.""" + + +def run_docker(image, home_dir, boot_nodes, telemetry_url, verbose): + print("Starting NEAR client and Watchtower dockers...") + docker_stop_if_exists('watchtower') + docker_stop_if_exists('framework') + # Start framework container, mapping home folder and ports. + envs = [ + '-e', + 'BOOT_NODES=%s' % boot_nodes, '-e', + 'TELEMETRY_URL=%s' % telemetry_url + ] + rpc_port = get_port(home_dir, 'rpc') + network_port = get_port(home_dir, 'network') + if verbose: + envs.extend(['-e', 'VERBOSE=1']) + subprocess.check_output(['mkdir', '-p', home_dir]) + subprocess.check_output([ + 'docker', 'run', '-u', USER, '-d', '-p', rpc_port, '-p', network_port, + '-v', + '%s:/srv/near' % home_dir, '-v', '/tmp:/tmp', '--ulimit', 'core=-1', + '--name', 'framework', '--restart', 'unless-stopped' + ] + envs + [image]) + # Start Watchtower that will automatically update the framework container when new version appears. + subprocess.check_output([ + 'docker', 'run', '-u', USER, '-d', '--restart', 'unless-stopped', + '--name', 'watchtower', '-v', + '/var/run/docker.sock:/var/run/docker.sock', 'v2tec/watchtower', image + ]) + print( + "Node is running! \nTo check logs call: docker logs --follow framework") + + +"""Runs NEAR core outside of docker.""" + + +def run_nodocker(home_dir, is_release, boot_nodes, telemetry_url, verbose): + print("Starting NEAR client...") + print( + "Autoupdate is not supported at the moment for runs outside of docker") + cmd = ['./target/%s/uncd' % ('release' if is_release else 'debug')] + cmd.extend(['--home', home_dir]) + if verbose: + cmd += ['--verbose', ''] + cmd.append('run') + if telemetry_url: + cmd.append('--telemetry-url=%s' % telemetry_url) + if boot_nodes: + cmd.append('--boot-nodes=%s' % boot_nodes) + try: + subprocess.call(cmd) + except KeyboardInterrupt: + print("\nStopping NEARCore.") + + +def setup_and_run(nodocker, + is_release, + image, + home_dir, + init_flags, + boot_nodes, + telemetry_url, + verbose=False, + no_gas_price=False): + if nodocker: + install_cargo() + else: + try: + subprocess.check_output(['docker', 'pull', image]) + subprocess.check_output(['docker', 'pull', 'v2tec/watchtower']) + except subprocess.CalledProcessError as exc: + print("Failed to fetch docker containers: %s" % exc) + exit(1) + + check_and_setup(nodocker, is_release, image, home_dir, init_flags, + no_gas_price) + + print_staking_key(home_dir) + + if nodocker: + run_nodocker(home_dir, is_release, boot_nodes, telemetry_url, verbose) + else: + run_docker(image, home_dir, boot_nodes, telemetry_url, verbose) + + +"""Stops docker for Nearcore and watchtower if they are running.""" + + +def stop_docker(): + docker_stop_if_exists('watchtower') + docker_stop_if_exists('framework') + + +def generate_node_key(home, is_release, nodocker, image): + print("Generating node key...") + if nodocker: + cmd = [ + './target/%s/keypair-generator' % + ('release' if is_release else 'debug') + ] + cmd.extend(['--home', home]) + cmd.extend(['--generate-config']) + cmd.extend(['node-key']) + try: + subprocess.call(cmd) + except KeyboardInterrupt: + print("\nStopping NEARCore.") + else: + subprocess.check_output(['mkdir', '-p', home]) + subprocess.check_output([ + 'docker', 'run', '-u', USER, '-v', + '%s:/srv/keypair-generator' % home, image, 'keypair-generator', + '--home=/srv/keypair-generator', '--generate-config', 'node-key' + ]) + print("Node key generated") + + +def generate_validator_key(home, is_release, nodocker, image, account_id): + print("Generating validator key...") + if nodocker: + cmd = [ + './target/%s/keypair-generator' % + ('release' if is_release else 'debug') + ] + cmd.extend(['--home', home]) + cmd.extend(['--generate-config']) + cmd.extend(['--account-id', account_id]) + cmd.extend(['validator-key']) + try: + subprocess.call(cmd) + except KeyboardInterrupt: + print("\nStopping NEARCore.") + else: + subprocess.check_output(['mkdir', '-p', home]) + subprocess.check_output([ + 'docker', 'run', '-u', USER, '-v', + '%s:/srv/keypair-generator' % home, image, 'keypair-generator', + '--home=/srv/keypair-generator', '--generate-config', + '--account-id=%s' % account_id, 'validator-key' + ]) + print("Validator key generated") + + +def generate_signer_key(home, is_release, nodocker, image, account_id): + print("Generating signer keys...") + if nodocker: + cmd = [ + './target/%s/keypair-generator' % + ('release' if is_release else 'debug') + ] + cmd.extend(['--home', home]) + cmd.extend(['--generate-config']) + cmd.extend(['--account-id', account_id]) + cmd.extend(['signer-keys']) + try: + subprocess.call(cmd) + except KeyboardInterrupt: + print("\nStopping NEARCore.") + else: + subprocess.check_output(['mkdir', '-p', home]) + subprocess.check_output([ + 'docker', 'run', '-u', USER, '-v', + '%s:/srv/keypair-generator' % home, image, 'keypair-generator', + '--home=/srv/keypair-generator', '--generate-config', + '--account-id=%s' % account_id, 'signer-keys' + ]) + print("Signer keys generated") + + +def initialize_keys(home, is_release, nodocker, image, account_id, + generate_signer_keys): + if nodocker: + install_cargo() + compile_package('keypair-generator', is_release) + else: + try: + subprocess.check_output(['docker', 'pull', image]) + except subprocess.CalledProcessError as exc: + print("Failed to fetch docker containers: %s" % exc) + exit(1) + if generate_signer_keys: + generate_signer_key(home, is_release, nodocker, image, account_id) + generate_node_key(home, is_release, nodocker, image) + if account_id: + generate_validator_key(home, is_release, nodocker, image, account_id) + + +def create_genesis(home, is_release, nodocker, image, chain_id, tracked_shards): + if os.path.exists(os.path.join(home, 'genesis.json')): + print("Genesis already exists") + return + print("Creating genesis...") + if not os.path.exists(os.path.join(home, 'accounts.csv')): + raise Exception( + "Failed to generate genesis: accounts.csv does not exist") + if nodocker: + cmd = [ + './target/%s/genesis-csv-to-json' % + ('release' if is_release else 'debug') + ] + if home: + cmd.extend(['--home', home]) + if chain_id: + cmd.extend(['--chain-id', chain_id]) + if len(tracked_shards) > 0: + cmd.extend(['--tracked-shards', tracked_shards]) + try: + subprocess.call(cmd) + except KeyboardInterrupt: + print("\nStopping NEARCore.") + else: + subprocess.check_output(['mkdir', '-p', home]) + cmd = [ + 'docker', + 'run', + '-u', + USER, + '-v', + '%s:/srv/genesis-csv-to-json' % home, + image, + 'genesis-csv-to-json', + '--home=/srv/genesis-csv-to-json', + ] + if chain_id: + cmd.extend(['--chain-id', chain_id]) + if tracked_shards: + cmd.extend(['--tracked-shards', tracked_shards]) + subprocess.check_output(cmd) + print("Genesis created") + + +def start_stakewars(home, is_release, nodocker, image, telemetry_url, verbose, + tracked_shards): + if nodocker: + install_cargo() + compile_package('genesis-csv-to-json', is_release) + compile_package('uncd', is_release) + else: + try: + subprocess.check_output(['docker', 'pull', image]) + except subprocess.CalledProcessError as exc: + print("Failed to fetch docker containers: %s" % exc) + exit(1) + create_genesis(home, is_release, nodocker, image, 'stakewars', + tracked_shards) + if nodocker: + run_nodocker(home, + is_release, + boot_nodes='', + telemetry_url=telemetry_url, + verbose=verbose) + else: + run_docker(image, + home, + boot_nodes='', + telemetry_url=telemetry_url, + verbose=verbose) diff --git a/scripts/parallel_coverage.py b/scripts/parallel_coverage.py new file mode 100644 index 000000000..65fcd465d --- /dev/null +++ b/scripts/parallel_coverage.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +import os +from testlib import clean_binary_tests, build_tests, test_binaries, current_path +from concurrent.futures import ThreadPoolExecutor, as_completed +import subprocess +import glob +from itertools import zip_longest +from multiprocessing import cpu_count + + +def grouper(iterable, n, fillvalue=None): + args = [iter(iterable)] * n + return zip_longest(*args, fillvalue=fillvalue) + + +def coverage(test_binary): + """ Run a single test coverage by copying to docker, save exitcode, stdout and stderr """ + src_dir = os.path.abspath('.') + test_binary_basename = os.path.basename(test_binary) + coverage_output = f'target/cov0/{test_binary_basename}' + subprocess.check_output(f'mkdir -p {coverage_output}', shell=True) + coverage_output = os.path.abspath(coverage_output) + + if not os.path.isfile(test_binary): + return -1, '', f'{test_binary} does not exist' + + p = subprocess.Popen([ + 'docker', 'run', '--rm', '--security-opt', 'seccomp=unconfined', '-u', + f'{os.getuid()}:{os.getgid()}', '-v', f'{test_binary}:{test_binary}', + '-v', f'{src_dir}:{src_dir}', '-v', + f'{coverage_output}:{coverage_output}', + 'nearprotocol/unc-coverage-runtime', 'bash', '-c', + f'/usr/local/bin/kcov --include-pattern=framework --exclude-pattern=.so --verify {coverage_output} {test_binary}' + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + stdout, stderr = p.communicate() + return (p.returncode, stdout, stderr) + + +def clean_coverage(): + subprocess.check_output(f'rm -rf {current_path}/../target/cov*', shell=True) + subprocess.check_output(f'rm -rf {current_path}/../target/merged_coverage', + shell=True) + + +def coverage_dir(i): + return f'{current_path}/../target/cov{i}' + + +def merge_coverage(i, to_merge, j): + p = subprocess.Popen([ + 'kcov', '--merge', + os.path.join(coverage_dir(i + 1), str(j)), *to_merge + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + return (p.returncode, stdout, stderr) + + +if __name__ == "__main__": + clean_coverage() + clean_binary_tests() + build_tests() + binaries = test_binaries(exclude=[ + r'test_regression-.*', + r'unc-.*', + r'test_cases_runtime-.*', + r'test_cases_testnet_rpc-.*', + r'test_catchup-.*', + r'test_errors-.*', + r'test_rejoin-.*', + r'test_simple-.*', + r'test_tps_regression-.*', + ]) + errors = False + + # Run coverage + with ThreadPoolExecutor(max_workers=cpu_count()) as executor: + future_to_binary = { + executor.submit(coverage, binary): binary for binary in binaries + } + for future in as_completed(future_to_binary): + binary = future_to_binary[future] + result = future.result() + if result[0] != 0: + print(result[2]) + errors = True + print( + f'========= error: kcov {binary} fail, exit code {result[0]} cause coverage fail' + ) + else: + print(f'========= kcov {binary} done') + + # Merge coverage + i = 0 + j = 0 + with ThreadPoolExecutor(max_workers=cpu_count()) as executor: + while True: + covs = glob.glob(f'{coverage_dir(i)}/*') + if len(covs) == 1: + break + subprocess.check_output(f'mkdir -p {coverage_dir(i+1)}', shell=True) + + cov_to_merge = list(grouper(covs, 2)) + if cov_to_merge[-1][-1] is None: + # ensure the last to merge is not only one cov + cov_to_merge[-2] += (cov_to_merge[-1][0],) + del cov_to_merge[-1] + + futures = [] + for cov in cov_to_merge: + j += 1 + futures.append(executor.submit(merge_coverage, i, cov, j)) + + for f in as_completed(futures): + pass + + i += 1 + + merged_coverage = os.path.join(coverage_dir(i), str(j)) + print(f'========= coverage merged to {merged_coverage}') + subprocess.check_output( + ['mv', merged_coverage, f'{current_path}/../merged_coverage']) + subprocess.check_output(f'rm -rf {current_path}/../target/cov*', shell=True) + + if errors: + print( + f'========= some errors in running kcov, coverage maybe inaccurate') diff --git a/scripts/remote_diff/diff_remote_files.sh b/scripts/remote_diff/diff_remote_files.sh new file mode 100755 index 000000000..23ba41caa --- /dev/null +++ b/scripts/remote_diff/diff_remote_files.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# +# Shows diff of host1:file and host2:file +# First copies those files locally +# +# Usage: ./diff_remote_files.sh project file host1 host2 + +project=$1 +file=$2 +host1=$3 +host2=$4 + +local_file=$(echo $file | tr / _) +local_file1=$host1\_$local_file +local_file2=$host2\_$local_file + +zone1=$(gcloud compute instances list --project=$project | grep ^$host1 | awk -F ' ' '{{print $2}}') +zone2=$(gcloud compute instances list --project=$project | grep ^$host2 | awk -F ' ' '{{print $2}}') + +gcloud compute scp ubuntu@$host1:$file $local_file1 --project=$project --zone=$zone1 +gcloud compute scp ubuntu@$host2:$file $local_file2 --project=$project --zone=$zone2 + +diff $local_file1 $local_file2 diff --git a/scripts/remote_diff/show_config_hash.py b/scripts/remote_diff/show_config_hash.py new file mode 100755 index 000000000..d5812e842 --- /dev/null +++ b/scripts/remote_diff/show_config_hash.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +""" +Shows md5sum of /home/ubuntu/.near/config.json on provided google cloud machines. + +Usage: ./show_config_hash.py project host1 host2 host3 ... +Example for testnet canaries: + ./show_config_hash.py unc-core testnet-canary-rpc-01-europe-north1-a-1f3e1e97 \ + testnet-canary-rpc-02-europe-west2-a-031e15e8 testnet-canary-rpc-archive-01-asia-east2-a-b25465d1 \ + testnet-canary-validator-01-us-west1-a-f160e149 +""" +import sys +from utils import display_table, run_on_machine + + +def install_jq(project, host, user='ubuntu'): + run_on_machine("sudo apt-get install jq -y", user, host, project) + + +def get_canonical_md5sum(project, host, user='ubuntu'): + install_jq(project, host, user) + return run_on_machine("jq --sort-keys . ~/.near/config.json | md5sum", user, + host, project) + + +def display_hashes(names, hashes): + rows = sorted(zip(names, hashes), key=lambda x: x[1]) + display_table([("name", "hash")] + rows) + + +if __name__ == '__main__': + project = sys.argv[1] + hosts = sys.argv[2:] + md5sums = [get_canonical_md5sum(project, host) for host in hosts] + display_hashes(hosts, md5sums) diff --git a/scripts/remote_diff/show_neard_version.py b/scripts/remote_diff/show_neard_version.py new file mode 100755 index 000000000..344b8716d --- /dev/null +++ b/scripts/remote_diff/show_neard_version.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +""" +Shows results of '/home/ubuntu/uncd -V' on provided google cloud machines. +Usage: ./show_neard_version.py project host1 host2 host3 ... +Example for testnet canaries: + ./show_neard_version.py unc-core testnet-canary-rpc-01-europe-north1-a-1f3e1e97 \ + testnet-canary-rpc-02-europe-west2-a-031e15e8 testnet-canary-rpc-archive-01-asia-east2-a-b25465d1 \ + testnet-canary-validator-01-us-west1-a-f160e149 +""" +import sys +from utils import display_table, run_on_machine + + +def get_neard_info(project, host, user='ubuntu'): + return run_on_machine("./uncd -V", user, host, project) + + +def display_neard_info(hosts, neard_info, user='ubuntu'): + display_table([[host] + neard_info.split(' ')[1:] + for (host, neard_info) in zip(hosts, neard_infos)]) + + +if __name__ == '__main__': + project = sys.argv[1] + hosts = sys.argv[2:] + neard_infos = [get_neard_info(project, host) for host in hosts] + display_neard_info(hosts, neard_infos) diff --git a/scripts/remote_diff/utils.py b/scripts/remote_diff/utils.py new file mode 100644 index 000000000..908867a6d --- /dev/null +++ b/scripts/remote_diff/utils.py @@ -0,0 +1,38 @@ +import subprocess + + +def get_zone(project, host): + zone_call = subprocess.run( + [ + f"gcloud compute instances list \ + --project={project} | grep ^{host} | awk -F ' ' '{{print $2}}'" + ], + stdout=subprocess.PIPE, + check=True, + text=True, + shell=True, + ) + return zone_call.stdout.replace('\n', '') + + +def run_on_machine(command, user, host, project): + zone = get_zone(project, host) + call = subprocess.run( + [ + f"gcloud compute ssh {user}@{host} --command='{command}' --project {project} --zone {zone}" + ], + stdout=subprocess.PIPE, + check=True, + text=True, + shell=True, + ) + return call.stdout.replace('\n', '') + + +def display_table(rows): + widths = [ + max([len(str(row[i])) for row in rows]) for i in range(len(rows[0])) + ] + format_str = " | ".join([f"{{:<{w}}}" for w in widths]) + for row in rows: + print(format_str.format(*row)) diff --git a/scripts/requirements_check.sh b/scripts/requirements_check.sh new file mode 100755 index 000000000..b870a912a --- /dev/null +++ b/scripts/requirements_check.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -e + +npm --version +# if that's the issue use +# yarn config set ignore-engines true +npm current node -v +yarn --version +python3 --version +cargo --version + + +if [[ $(yarn --version) != "1."* ]]; then + echo "You version of yarn is too old $(yarn --version) < 1.0" + echo "Install with npm install --global yarn" + exit 1 +fi + diff --git a/scripts/run_clippy.sh b/scripts/run_clippy.sh new file mode 100755 index 000000000..b34310ba1 --- /dev/null +++ b/scripts/run_clippy.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +exec cargo nextest run -p style-tests clippy --no-capture diff --git a/scripts/run_docker.sh b/scripts/run_docker.sh new file mode 100755 index 000000000..93ca5b91a --- /dev/null +++ b/scripts/run_docker.sh @@ -0,0 +1,24 @@ +#!/bin/sh +set -e + +unc_HOME=${unc_HOME:-/srv/near} +export unc_HOME + +if [ -n "$INIT" ]; then + uncd init ${CHAIN_ID:+--chain-id="$CHAIN_ID"} \ + ${ACCOUNT_ID:+--account-id="$ACCOUNT_ID"} +fi + +if [ -n "$NODE_KEY" ]; then + cat << EOF > "$unc_HOME/node_key.json" +{"account_id": "", "public_key": "", "secret_key": "$NODE_KEY"} +EOF +fi + +ulimit -c unlimited + +echo "Telemetry: ${TELEMETRY_URL}" +echo "Bootnodes: ${BOOT_NODES}" + +exec uncd run ${TELEMETRY_URL:+--telemetry-url="$TELEMETRY_URL"} \ + ${BOOT_NODES:+--boot-nodes="$BOOT_NODES"} "$@" diff --git a/scripts/rustc-coverage-wrapper.sh b/scripts/rustc-coverage-wrapper.sh new file mode 100755 index 000000000..38d35be5d --- /dev/null +++ b/scripts/rustc-coverage-wrapper.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +## When collecting coverage we want to instrument only the crates in our workspace, and not the +## external dependencies (there is no value in doing so – we don’t run tests for our dependencies +## anyway.) Furthermore, instrumenting the crates like these run a risk of significant performance +## regressions, such as seen in #10201 + +RUSTC="$1"; shift +exec "$RUSTC" -Cinstrument-coverage --cfg=coverage "$@" diff --git a/scripts/setup_hooks.sh b/scripts/setup_hooks.sh new file mode 100755 index 000000000..ce314957c --- /dev/null +++ b/scripts/setup_hooks.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" +HOOK_DIR=$(git rev-parse --show-toplevel)/.git/hooks +ln -s -f ${DIR}/pre-commit ${HOOK_DIR} diff --git a/scripts/split_storage_migration.sh b/scripts/split_storage_migration.sh new file mode 100755 index 000000000..ef28c0b55 --- /dev/null +++ b/scripts/split_storage_migration.sh @@ -0,0 +1,202 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Should be started in detached mode +# to ensure an ssh disconnect will not interrupt the process: +# /split_storage_migration.sh (testnet|mainnnet) >> ./split_storage_migration.log & +# Takes one argument -- chain_id (mainnet or testnet) +# Prerequisites: +# - systemd uncd service +# - uncd home is in /home/ubuntu/.near +# - uncd binary is in /home/ubuntu/uncd +# - /home/ubuntu/.near/config.json is initialised +# - metrics are exported to 3030 port +# - aws s3 client +# First, prepares config files using jq. +# Restarts uncd and waits for cold head to sync with final head +# Downloads latest rpc db in hot-data +# Stops uncd, prepares rpc db to be used as hot db, changes config to look at hot db instead of archival db +# Restarts uncd +# After that node is running in split storage mode +# Legacy archival db can be removed + +unc_HOME=/home/ubuntu/.near +chain=$1 +service_name=uncd + +if [ "$chain" != "testnet" ] && [ "$chain" != "mainnet" ]; then + echo "Chain should be 'mainnet' or 'testnet', got '$chain'" + exit 1 +fi + +function restart_neard() { + echo "Restarting the systemd service '$service_name'" + sudo systemctl restart $service_name +} + +function stop_neard() { + echo "Stopping the systemd service '$service_name'" + sudo systemctl stop $service_name +} + +function intro() { + echo "This script is going to migrate this archival node to cold storage" + echo + echo "The script makes the following assumptions:" + echo " * You have about 10TB of disk space available. Recommended setup is to mount a cheap HDD to '~/.near/cold-data'." + echo " * The node can be controlled as a systemd service. The script will run 'sudo systemctl restart $service_name' when it needs to restart the node." + echo " * The machine has commands 'jq' and 'aws' available, or if those commands are not available, they can be installed using 'apt'." + echo " * It's ok to stop the node for several minutes." + echo " * You are not going to do any other maintenance on this node in the next 24 hours." + echo " * You have data backups in an unlikely case of something going wrong." + echo + read -p "Do you want to proceed? (Y/N): " choice + case "$choice" in + y | Y) + echo "Proceeding with migration..." + ;; + n | N) + echo "Aborting migration..." + exit 1 # Exit with status code 1 + ;; + *) + echo "Invalid choice, please enter Y or N" + intro # Re-run the intro function until a valid choice is made + ;; + esac +} + +function check_jq() { + if ! command -v jq &>/dev/null; then + echo "'jq' command not found. Installing 'jq' package using 'apt'..." + sudo apt update && sudo apt install jq + else + echo "'jq' command is already installed" + fi +} + +function check_aws() { + if ! command -v aws &>/dev/null; then + echo "'aws' command not found. Installing 'awscli' package using 'apt'..." + sudo apt update && sudo apt install awscli + else + echo "'aws' command is already installed" + fi +} + +# Prepare all configs +function prepare_configs { + # make archival config + jq '.archive = true | .save_trie_changes=true' <$unc_HOME/config.json >$unc_HOME/config.json.archival + + # create migration config that has cold_store part + jq '.cold_store = .store' <$unc_HOME/config.json.archival >$unc_HOME/config.json.migration + + # create split storage config that points to the right hot storage + jq '.store.path = "hot-data"' <$unc_HOME/config.json.migration >$unc_HOME/config.json.split +} + +# Run for 10 minutes with TrieChanges +function run_with_trie_changes { + echo "Starting an archival run with TrieChanges" + cp $unc_HOME/config.json.archival $unc_HOME/config.json + restart_neard + + echo "Waiting 10 minutes" + sleep 600 +} + +# Initialize cold storage +function init_cold_storage { + # Switch to migration mode + lcho "Starting initial migration run" + echo "Expect the migration to take a long time (>1 hour)." + echo "Do not interrupt. Any interruption will undo the migration process." + cp $unc_HOME/config.json.migration $unc_HOME/config.json + + restart_neard + + metrics_url="http://0.0.0.0:3030/metrics" + # Wait for the migration to complete + while true; do + # Get data from the metrics page of a localhost node that is assumed to listen on port 3030. + cold_head=$(curl "$metrics_url" --silent | grep 'cold_head_height' | tail -n1 | awk '{print $2}') + echo "Checking if cold storage cold head is ready. Cold head: '$cold_head'" + + if [[ -n "$cold_head" ]]; then + break + else + echo "Cold storage isn't initialised yet. Will check again in 2 minutes. Please don't interrupt." + # Wait for 2 minutes before trying again + sleep 120 + fi + done +} + +# Download latest rpc backup +function download_latest_rpc_backup { + echo "Starting rpc download" + aws s3 --no-sign-request cp "s3://unc-protocol-public/backups/$chain/rpc/latest" . + latest=$(cat latest) + aws s3 --no-sign-request cp --recursive "s3://unc-protocol-public/backups/$chain/rpc/$latest" $unc_HOME/hot-data +} + +# Finish migration to split storage +function finish_split_storage_migration { + + # Change hot store type + while true + do + stop_neard + echo "Trying to change RPC DB kind to Hot" + if /home/ubuntu/uncd cold-store prepare-hot --store-relative-path='hot-data' + then + echo "Successfully changed RPC DB kind to Hot" + break + else + echo "Failed to change RPC DB king to Hot. Check the error above." + echo "Assuming the error is cold head being behind rpc tail." + echo "Restarting the node with legacy archival db and waiting an hour for cold head to increase." + restart_neard + sleep 3600 + fi + done + + # Switch to split storage mode + echo "Starting split storage run" + cp $unc_HOME/config.json.split $unc_HOME/config.json + restart_neard + + echo "Waiting 5 minutes" + sleep 300 + + hot_db_kind=$( + curl \ + --silent \ + -H "Content-Type: application/json" \ + -X POST \ + --data '{"jsonrpc":"2.0","method":"EXPERIMENTAL_split_storage_info","params":[],"id":"dontcare"}' \ + 0.0.0.0:3030 | + jq '.result.hot_db_kind' + ) + + echo "Hot DbKind is '$hot_db_kind'" + if [ "$hot_db_kind" != '"Hot"' ]; then + echo "Hot DbKind is not 'Hot'" + exit 1 + else + echo "Hot DbKind is correct" + echo "Migration complete" + fi +} + +# Backup old config.json +intro +check_jq +check_aws +cp $unc_HOME/config.json $unc_HOME/config.json.init +prepare_configs +run_with_trie_changes +init_cold_storage +download_latest_rpc_backup +finish_split_storage_migration diff --git a/scripts/state/mega-migrate.py b/scripts/state/mega-migrate.py new file mode 100755 index 000000000..01e064182 --- /dev/null +++ b/scripts/state/mega-migrate.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 + +# Migrates from 0.4 to the latest version. +# When adding changes to the genesis config: +# - add a new version to the change log +# - implement the migration code using the following template: +# ``` +# if config_version == 5: +# # add migration code here +# pass +# # increment config version +# config_version = 6 +# ``` +# +# Config version change log: +# - #1: Replaces `runtime_config` to use defaults and introduces `config_version`. + +import json +import os + +filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), + '../../near/res/testnet.json') +with open(filename) as fd: + q = json.load(rd) + +config_version = q.get('config_version', 0) + +if config_version == 0: + num_sec_per_year = 31556952 + # The rest of `runtime_config` fields are default + q['runtime_config'] = { + 'poke_threshold': + 24 * 3600, + 'storage_cost_byte_per_block': + str(5 * 10**6), + 'account_length_baseline_cost_per_block': + str(10**24 * 3**8 // num_sec_per_year), + } + config_version = 1 + +# Add future migration code below, without removing the previous migration code. +# Use the following template: +# +# if config_version == 1: +# ... +# config_version = 2 + +# Update the config version in the testnet +q['config_version'] = config_version + +# We overwrite the file instead of creating a new one. +with open(filename, 'w') as fd: + json.dump(q, fd, indent=2, sort_keys=True) + +# Dump the config into a separate file for easier reviewing in the git. +# It's not used for the reading genesis. +del q['records'] +with open(filename + '.config', 'w') as fd: + json.dump(q, fd, indent=2, sort_keys=True) diff --git a/scripts/state/split-genesis.py b/scripts/state/split-genesis.py new file mode 100755 index 000000000..a0fdb1a9c --- /dev/null +++ b/scripts/state/split-genesis.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +# Splits given genesis.json into 2 files: +# - genesis_config.json - that contains all fields except for the records +# - genesis_records.json - that contains all records from the genesis.json +# It keeps key order in the genesis config file. + +import json +import os +import sys +from collections import OrderedDict + +filename = sys.argv[1] +with open(filename) as fd: + q = json.load(fd, object_pairs_hook=OrderedDict) + +records = q['records'] +q['records'] = [] + +dirname = os.path.dirname(filename) +with open(os.path.join(dirname, 'genesis_config.json'), 'w') as fd: + json.dump(q, fd, indent=2) +with open(os.path.join(dirname, '_genesis_records.json'), 'w') as fd: + json.dump(records, fd, indent=2) diff --git a/scripts/state/update_res.py b/scripts/state/update_res.py new file mode 100755 index 000000000..88226815d --- /dev/null +++ b/scripts/state/update_res.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +"""Checks or updates contents of framework/res/genesis_config.json file. + +* `update_res.py` updates framework/res/genesis_config.json to current + `near init` version without any records. + +* `update_res.py check` checks whether framework/res/genesis_config.json + file matches what `near init` generates. +""" + +import collections +import json +import os +import pathlib +import subprocess +import sys +import tempfile + + +def main(): + if len(sys.argv) == 1: + update_res() + elif len(sys.argv) == 2 and sys.argv[1] == 'check': + check_res() + else: + sys.exit(__doc__.rstrip()) + + +GENESIS_REPO_PATH = 'chain/jsonrpc/jsonrpc-tests/res/genesis_config.json' +REPO_FULL_PATH = pathlib.Path(__file__).resolve().parent.parent.parent +GENESIS_FULL_PATH = REPO_FULL_PATH / GENESIS_REPO_PATH +SCRIPT_REPO_PATH = pathlib.Path(__file__).resolve().relative_to(REPO_FULL_PATH) + + +def unc_init_genesis(): + with tempfile.TemporaryDirectory() as tempdir: + args = ['--home', tempdir, 'init', '--chain-id', 'sample'] + prebuilt_neard = os.environ.get("CURRENT_NEARD") + if prebuilt_neard is not None: + subprocess.check_call([prebuilt_neard] + args) + else: + subprocess.check_call( + ['cargo', 'run', '-p', 'uncd', '--bin', 'uncd', '--'] + args) + with open(os.path.join(tempdir, 'genesis.json')) as rd: + genesis = json.load(rd, object_pairs_hook=collections.OrderedDict) + genesis['records'] = [] + # To avoid the genesis config changing each time + genesis['genesis_time'] = '1970-01-01T00:00:00.000000000Z' + # Secret key is seeded from test.near + genesis['validators'][0][ + 'public_key'] = 'ed25519:9BmAFNRTa5mRRXpSAm6MxSEeqRASDGNh2FuuwZ4gyxTw' + return genesis + + +def update_res(): + genesis = unc_init_genesis() + with open(GENESIS_FULL_PATH, 'w') as wr: + json.dump(genesis, wr, indent=2) + print(f'{GENESIS_REPO_PATH} updated') + + +def check_res(): + want_genesis = unc_init_genesis() + with open(GENESIS_FULL_PATH) as rd: + got_genesis = json.load(rd) + if want_genesis != got_genesis: + sys.exit( + f'`{GENESIS_REPO_PATH}` does not match `near init` generated one\n' + f'Please update by running `{SCRIPT_REPO_PATH}` script') + + +if __name__ == '__main__': + main() diff --git a/scripts/testlib.py b/scripts/testlib.py new file mode 100644 index 000000000..a4415a056 --- /dev/null +++ b/scripts/testlib.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 + +import glob +import os +import subprocess +import fcntl +import re +import filecmp + +fcntl.fcntl(1, fcntl.F_SETFL, 0) + +current_path = os.path.dirname(os.path.abspath(__file__)) +target_debug = os.path.abspath(os.path.join(current_path, '../target/debug')) + + +def clean_binary_tests(): + if os.environ.get('RFCI_COMMIT'): + return + for f in glob.glob(f'{target_debug}/*'): + if os.path.isfile(f): + os.remove(f) + + +def build_tests(nightly=False): + command = ['cargo', 'test', '--workspace', '--no-run'] + if nightly: + command += ['--features', 'nightly'] + print("Building tests using command: ", ' '.join(command)) + p = subprocess.run(command) + if p.returncode != 0: + os._exit(p.returncode) + + +def run_doc_tests(nightly=False): + command = ['cargo', 'test', '--workspace', '--doc'] + if nightly: + command += ['--features', 'nightly'] + print("Building doc tests using command: ", ' '.join(command)) + p = subprocess.run(command) + if p.returncode != 0: + os._exit(p.returncode) + + +def test_binaries(exclude=None): + binaries = [] + for f in glob.glob(f'{target_debug}/deps/*'): + fname = os.path.basename(f) + ext = os.path.splitext(fname)[1] + is_unc_binary = filecmp.cmp(f, f'{target_debug}/near') or filecmp.cmp( + f, f'{target_debug}/uncd') + if os.path.isfile(f) and not is_unc_binary and ext == '': + if not exclude: + binaries.append(f) + elif not any(map(lambda e: re.match(e, fname), exclude)): + binaries.append(f) + else: + print(f'========= ignore {f}') + return binaries + + +def run_test(test_binary, isolate=True): + """ Run a single test, save exitcode, stdout and stderr """ + if isolate: + cmd = [ + 'docker', 'run', '--rm', '-u', f'{os.getuid()}:{os.getgid()}', '-v', + f'{test_binary}:{test_binary}', 'nearprotocol/unc-test-runtime', + 'bash', '-c', f'RUST_BACKTRACE=1 {test_binary}' + ] + else: + cmd = [test_binary] + print(f'========= run test {test_binary}') + if os.path.isfile(test_binary): + p = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + stdout, stderr = p.communicate() + return (p.returncode, stdout, stderr) + return -1, '', f'{test_binary} does not exist' diff --git a/scripts/upload-cf-r2.py b/scripts/upload-cf-r2.py new file mode 100755 index 000000000..09a0134a2 --- /dev/null +++ b/scripts/upload-cf-r2.py @@ -0,0 +1,28 @@ +import boto3 +import os +import subprocess + +def upload_directory(directory, bucket, s3_client): + for root, dirs, files in os.walk(directory): + for file in files: + file_path = os.path.join(root, file) + s3_key = os.path.relpath(file_path, directory) + with open(file_path, 'rb') as data: + s3_client.upload_fileobj(data, bucket, s3_key) + +def run_neard_command(command): + result = subprocess.run(command, capture_output=True, text=True) + print(result.stdout) + +s3 = boto3.client( + service_name="s3", + endpoint_url='https://ec9b597fa02615ca6a0e62b7ff35d0cc.r2.cloudflarestorage.com', + aws_access_key_id='2131355885fd8671f483c8721136972d', + aws_secret_access_key='05b1dc1f4fb3af390c9f10b905e5eb40e73f7b1aca34651be33fb034aae51e74', + region_name="auto", # Must be one of: wnam, enam, weur, eeur, apac, auto +) + +#run_neard_command(["/Users/es/utility/target/debug/uncd", "--home=/Users/es/.unc", "database", "make-snapshot", "--destination=/Users/es/snapshot"]) +#run_neard_command(["/Users/es/utility/target/debug/uncd", "--home=/Users/es/snapshot", "database", "compact-database"]) + +upload_directory('/Users/es/snapshot', 'near', s3) \ No newline at end of file diff --git a/test-utils/actix-test-utils/Cargo.toml b/test-utils/actix-test-utils/Cargo.toml new file mode 100644 index 000000000..91e1677d2 --- /dev/null +++ b/test-utils/actix-test-utils/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "unc-actix-test-utils" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix-rt.workspace = true +futures.workspace = true +once_cell.workspace = true + +unc-store.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-store/nightly", +] +nightly_protocol = [ + "unc-store/nightly_protocol", +] diff --git a/test-utils/actix-test-utils/src/lib.rs b/test-utils/actix-test-utils/src/lib.rs new file mode 100644 index 000000000..d9bb55c10 --- /dev/null +++ b/test-utils/actix-test-utils/src/lib.rs @@ -0,0 +1,91 @@ +use once_cell::sync::Lazy; +use std::sync::Mutex; + +pub struct ShutdownableThread { + pub join: Option>, + pub actix_system: actix_rt::System, +} + +impl ShutdownableThread { + pub fn start(_name: &'static str, f: F) -> ShutdownableThread + where + F: FnOnce() + Send + 'static, + { + let (tx, rx) = std::sync::mpsc::channel(); + let join = std::thread::spawn(move || { + run_actix(async move { + f(); + tx.send(actix_rt::System::current()).unwrap(); + }); + }); + + let actix_system = rx.recv().unwrap(); + ShutdownableThread { join: Some(join), actix_system } + } + + pub fn shutdown(&self) { + self.actix_system.stop(); + } +} + +impl Drop for ShutdownableThread { + fn drop(&mut self) { + self.shutdown(); + self.join.take().unwrap().join().unwrap(); + } +} + +#[inline] +pub fn spawn_interruptible( + f: F, +) -> actix_rt::task::JoinHandle { + actix_rt::spawn(f) +} + +// Number of actix instances that are currently running. +pub(crate) static ACTIX_INSTANCES_COUNTER: Lazy> = Lazy::new(|| (Mutex::new(0))); + +pub fn setup_actix() -> actix_rt::SystemRunner { + static SET_PANIC_HOOK: std::sync::Once = std::sync::Once::new(); + + // This is a workaround to make actix/tokio runtime stop when a task panics. + // See: https://github.com/actix/actix-net/issues/80 + SET_PANIC_HOOK.call_once(|| { + let default_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |info| { + if actix_rt::System::is_registered() { + actix_rt::System::current().stop_with_code(1); + } + default_hook(info); + })); + }); + + actix_rt::System::new() +} + +pub fn block_on_interruptible( + sys: &actix_rt::SystemRunner, + f: F, +) -> F::Output { + sys.block_on(f) +} + +pub fn run_actix(f: F) { + { + let mut value = ACTIX_INSTANCES_COUNTER.lock().unwrap(); + *value += 1; + } + + let sys = setup_actix(); + block_on_interruptible(&sys, f); + sys.run().unwrap(); + + { + let mut value = ACTIX_INSTANCES_COUNTER.lock().unwrap(); + *value -= 1; + if *value == 0 { + // If we're the last instance - make sure to wait for all RocksDB handles to be dropped. + unc_store::db::RocksDB::block_until_all_instances_are_dropped(); + } + } +} diff --git a/test-utils/logger/Cargo.toml b/test-utils/logger/Cargo.toml new file mode 100644 index 000000000..ed7083a83 --- /dev/null +++ b/test-utils/logger/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "unc-logger-utils" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[dependencies] +tracing.workspace = true +unc-o11y.workspace = true diff --git a/test-utils/runtime-tester/Cargo.toml b/test-utils/runtime-tester/Cargo.toml new file mode 100644 index 000000000..48c672802 --- /dev/null +++ b/test-utils/runtime-tester/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "runtime-tester" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +bolero.workspace = true +cpu-time.workspace = true +libfuzzer-sys.workspace = true +serde.workspace = true +serde_json.workspace = true +tempfile.workspace = true +tracing.workspace = true + +unc-chain.workspace = true +unc-chain-configs.workspace = true +unc-crypto.workspace = true +unc-client.workspace = true +unc-client-primitives.workspace = true +unc-epoch-manager.workspace = true +framework.workspace = true +unc-parameters.workspace = true +unc-primitives.workspace = true +unc-store.workspace = true +unc-o11y.workspace = true +unc-test-contracts.workspace = true + +[dev-dependencies] +testlib.workspace = true diff --git a/test-utils/runtime-tester/README.md b/test-utils/runtime-tester/README.md new file mode 100644 index 000000000..28e893da6 --- /dev/null +++ b/test-utils/runtime-tester/README.md @@ -0,0 +1,65 @@ +# Runtime test + +Framework for creating and executing runtime scenarios. You can +create [`Scenario`] in rust code or load it from a JSON file. + +[`fuzzing`] module provides [`libfuzzer_sys::arbitrary::Arbitrary`] +trait for [`Scenario`], thus enabling creating random scenarios. + +## Scenario + +Runtime test is described by a [`Scenario`] object. Currently, +scenario supports only one client, but you can specify number of +accounts through [`NetworkConfig`]. + +Scenario can be loaded from a json file or constructed in rust code. + +```ignore +pub fn from_file(path: &Path) -> io::Result; +``` + +[`Scenario::run`] tries to produce all the described blocks and if +succeeded returns [`run_test::RuntimeStats`] wrapped in +a [`run_test::ScenarioResult`]. + +```ignore +pub fn run(&self) -> ScenarioResult; +``` + +[`run_test::RuntimeStats`] contain stats for every produced block. +Currently, only block production time is supported. + +```ignore +#[derive(Serialize, Deserialize, Default, Debug)] +pub struct RuntimeStats { + pub blocks_stats: Vec, +} + +#[derive(Serialize, Deserialize, Default, Debug)] +pub struct BlockStats { + pub height: u64, + pub block_production_time: Duration, +} +``` + +[`run_test::ScenarioResult`] is a wrapper around a `Result` type which +adds a `homedir` field: + +```ignore +pub struct ScenarioResult { + pub result: std::result::Result, + pub homedir: Option, +} +``` + +The `homedir` is populated if scenario is configured to use on-disk +storage (i.e. if `use_in_memory_store` is `false`) and allows the +caller to locate the store. + +Be careful to remember, that block height should be positive and +ascending. + +## Scenario Builder + +To easily create new scenarios in rust code use [`ScenarioBuilder`]. +Usage example can be found in `src/scenario_builder.rs` file. diff --git a/test-utils/runtime-tester/fuzz/.gitignore b/test-utils/runtime-tester/fuzz/.gitignore new file mode 100644 index 000000000..572e03bdf --- /dev/null +++ b/test-utils/runtime-tester/fuzz/.gitignore @@ -0,0 +1,4 @@ + +target +corpus +artifacts diff --git a/test-utils/runtime-tester/fuzz/Cargo.toml b/test-utils/runtime-tester/fuzz/Cargo.toml new file mode 100644 index 000000000..a07c6892e --- /dev/null +++ b/test-utils/runtime-tester/fuzz/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "runtime-tester-fuzz" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys.workspace = true +serde_json.workspace = true +runtime-tester.workspace = true + +[[bin]] +name = "runtime_fuzzer" +path = "fuzz_targets_disabled/runtime_fuzzer.rs" +test = false +doc = false diff --git a/test-utils/runtime-tester/fuzz/README.md b/test-utils/runtime-tester/fuzz/README.md new file mode 100644 index 000000000..f2e8fe311 --- /dev/null +++ b/test-utils/runtime-tester/fuzz/README.md @@ -0,0 +1,77 @@ +# Installation with cargo + +You can install `cargo-fuzz`, with the following command. + +```bash +cargo install cargo-fuzz +``` + +# How to see if everything works + +You can see if it compiles by the following command. + +``` +cd test-utils/runtime-tester/fuzz/ +env RUSTC_BOOTSTRAP=1 'cargo' 'fuzz' 'run' 'runtime_fuzzer' '--' '-len_control=0' '-prefer_small=0' '-max_len=4000000' '-rss_limit_mb=10240' +``` + +# Runtime Fuzz + +Currently only one target is present -- runtime_fuzzer. +This target will create random scenarios using Arbitrary trait +and execute them. This will keep happening, until one scenario fails. + + +To run fuzz test: +```bash +RUSTC_BOOTSTRAP=1 cargo fuzz run runtime_fuzzer +``` +`max_len` here is to create bigger scenarios. + +Fuzzing starts from small inputs (Unstructured) and slowly goes to bigger. +To go to bigger input faster run fuzz with additional arguments: + +```bash +RUSTC_BOOTSTRAP=1 cargo fuzz run runtime_fuzzer -- -len_control=0 -prefer_small=0 +``` + +After finding the failed test, cargo fuzz will show failed Scenario (in debug format) +and also will write path to failing input and suggest further debug commands: + +```bash +Failing input: + + artifacts/runtime_fuzzer/ + +Output of `std::fmt::Debug`: + + ** full json for Scenario ** + +Reproduce with: + + cargo fuzz run runtime_fuzzer artifacts/runtime_fuzzer/ + +Minimize test case with: + + cargo fuzz tmin runtime_fuzzer artifacts/runtime_fuzzer/ +``` + +So, reproducing in this case will be: +```bash +RUSTC_BOOTSTRAP=1 cargo fuzz run runtime_fuzzer artifacts/runtime_fuzzer/ +``` + +To make a smaller scenario with the same error: +```bash +RUSTC_BOOTSTRAP=1 cargo fuzz tmin runtime_fuzzer artifacts/runtime_fuzzer/ +``` + +Writing Scenario to json: +```bash +RUSTC_BOOTSTRAP=1 cargo fuzz fmt runtime_fuzzer artifacts/runtime_fuzzer/ 2>&1 | sed '1,3d' | tee scenario.json +``` + +To run specific scenario.json use test from runtime-tester +```bash +cargo test test_scenario_json --release -- --ignored +``` diff --git a/test-utils/runtime-tester/fuzz/fuzz_targets_disabled/runtime_fuzzer.rs b/test-utils/runtime-tester/fuzz/fuzz_targets_disabled/runtime_fuzzer.rs new file mode 100644 index 000000000..e91479b35 --- /dev/null +++ b/test-utils/runtime-tester/fuzz/fuzz_targets_disabled/runtime_fuzzer.rs @@ -0,0 +1,28 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; +use runtime_tester::Scenario; +use std::fs::File; +use std::time::Duration; + +fn do_fuzz(scenario: &Scenario) -> Result<(), String> { + let stats = scenario.run().result.map_err(|e| e.to_string())?; + for block_stats in stats.blocks_stats { + if block_stats.block_production_time > Duration::from_secs(2) { + return Err(format!( + "block at height {} was produced in {:?}", + block_stats.height, block_stats.block_production_time + )); + } + } + Ok(()) +} + +fn fuzz(scenario: Scenario) { + if let Err(err) = do_fuzz(&scenario) { + let file = "failed_scenario.json"; + serde_json::to_writer(&File::create(file).unwrap(), &scenario).unwrap(); + panic!("Bad scenario: {}, {}", file, err); + } +} + +fuzz_target!(|scenario: Scenario| fuzz(scenario) ); diff --git a/test-utils/runtime-tester/src/fuzzing.rs b/test-utils/runtime-tester/src/fuzzing.rs new file mode 100644 index 000000000..691d353f9 --- /dev/null +++ b/test-utils/runtime-tester/src/fuzzing.rs @@ -0,0 +1,798 @@ +use crate::run_test::{BlockConfig, NetworkConfig, RuntimeConfig, Scenario, TransactionConfig}; +use unc_crypto::{InMemorySigner, KeyType, PublicKey}; +use unc_primitives::{ + account::{AccessKey, AccessKeyPermission, FunctionCallPermission}, + transaction::{ + Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, + DeployContractAction, FunctionCallAction, TransferAction, + }, + types::{AccountId, Balance, BlockHeight, Nonce}, +}; +use framework::config::{UNC_BASE, TESTING_INIT_BALANCE}; + +use libfuzzer_sys::arbitrary::{Arbitrary, Result, Unstructured}; + +use std::collections::{HashMap, HashSet}; +use std::str::FromStr; + +pub type ContractId = usize; + +pub const MAX_BLOCKS: usize = 250; +pub const MAX_TXS: usize = 50; +pub const MAX_TX_DIFF: usize = 10; +pub const MAX_ACCOUNTS: usize = 100; +pub const MAX_ACTIONS: usize = 100; + +const GAS_1: u64 = 300_000_000_000_000; + +impl Arbitrary<'_> for Scenario { + fn arbitrary(u: &mut Unstructured<'_>) -> Result { + let num_accounts = u.int_in_range(2..=MAX_ACCOUNTS)?; + + let seeds: Vec = (0..num_accounts).map(|i| format!("test{}", i)).collect(); + + let mut scope = Scope::from_seeds(&seeds); + + let network_config = NetworkConfig { seeds }; + let runtime_config = RuntimeConfig { + max_total_prepaid_gas: GAS_1 * 100, + gas_limit: (GAS_1 as f64 * *u.choose(&[0.01, 0.1, 1., 10., 100.])?) as u64, + epoch_length: *u.choose(&[5, 10, 100, 500])? as u64, + }; + + let mut blocks = vec![]; + + while blocks.len() < MAX_BLOCKS && u.len() > BlockConfig::size_hint(0).0 { + blocks.push(BlockConfig::arbitrary(u, &mut scope)?); + } + Ok(Scenario { + network_config, + runtime_config, + blocks, + use_in_memory_store: true, + is_fuzzing: true, + }) + } + + fn size_hint(_depth: usize) -> (usize, Option) { + (1, Some(MAX_BLOCKS * BlockConfig::size_hint(0).1.unwrap())) + } +} + +impl BlockConfig { + fn arbitrary(u: &mut Unstructured, scope: &mut Scope) -> Result { + scope.inc_height(); + let mut block_config = BlockConfig::at_height(scope.height()); + + let lower_bound = scope.last_tx_num.saturating_sub(MAX_TX_DIFF); + let upper_bound = scope.last_tx_num.saturating_add(MAX_TX_DIFF); + let max_tx_num = u.int_in_range(lower_bound..=std::cmp::min(MAX_TXS, upper_bound))?; + scope.last_tx_num = max_tx_num; + + while block_config.transactions.len() < max_tx_num + && u.len() > TransactionConfig::size_hint(0).0 + { + block_config.transactions.push(TransactionConfig::arbitrary(u, scope)?) + } + + Ok(block_config) + } + + fn size_hint(_depth: usize) -> (usize, Option) { + (1, Some((MAX_TXS + 1) * TransactionConfig::size_hint(0).1.unwrap())) + } +} + +impl TransactionConfig { + fn arbitrary(u: &mut Unstructured, scope: &mut Scope) -> Result { + let mut options: Vec Result> = + vec![]; + + scope.inc_nonce(); + + assert!(scope.alive_accounts.contains(&0), "The only validator account got deleted."); + + // Transfer + options.push(|u, scope| { + let signer_account = scope.random_account(u)?; + let receiver_account = { + if u.arbitrary::()? { + scope.new_account(u)? + } else { + scope.random_account(u)? + } + }; + let amount = u.int_in_range::(0..=signer_account.balance)?; + + let signer_idx = scope.usize_id(&signer_account); + let receiver_idx = scope.usize_id(&receiver_account); + scope.accounts[signer_idx].balance = + scope.accounts[signer_idx].balance.saturating_sub(amount); + scope.accounts[receiver_idx].balance = + scope.accounts[receiver_idx].balance.saturating_add(amount); + + Ok(TransactionConfig { + nonce: scope.nonce(), + signer_id: signer_account.id.clone(), + receiver_id: receiver_account.id, + signer: scope.full_access_signer(u, &signer_account)?, + actions: vec![Action::Transfer(TransferAction { deposit: amount })], + }) + }); + + /* This actually can create new block producers, and currently we only support one. + // Stake + options.push(|u, scope| { + let signer_account = scope.random_account(u)?; + let amount = u.int_in_range::(0..=signer_account.balance)?; + let signer = InMemorySigner::from_seed( + signer_account.id.clone(), + KeyType::ED25519, + signer_account.id.as_ref(), + ); + let public_key = signer.public_key.clone(); + + Ok(TransactionConfig { + nonce: scope.nonce(), + signer_id: signer_account.id.clone(), + receiver_id: signer_account.id, + signer, + actions: vec![Action::Stake(StakeAction { stake: amount, public_key })], + }) + }); + */ + + // Create Account + options.push(|u, scope| { + let signer_account = scope.random_account(u)?; + let new_account = scope.new_account(u)?; + + let signer = scope.full_access_signer(u, &signer_account)?; + let new_public_key = InMemorySigner::from_seed( + new_account.id.clone(), + KeyType::ED25519, + new_account.id.as_ref(), + ) + .public_key; + Ok(TransactionConfig { + nonce: scope.nonce(), + signer_id: signer_account.id, + receiver_id: new_account.id, + signer, + actions: vec![ + Action::CreateAccount(CreateAccountAction {}), + Action::AddKey(Box::new(AddKeyAction { + public_key: new_public_key, + access_key: AccessKey { + nonce: 0, + permission: AccessKeyPermission::FullAccess, + }, + })), + Action::Transfer(TransferAction { deposit: UNC_BASE }), + ], + }) + }); + + // Delete Account + if scope.num_alive_accounts() > 1 { + options.push(|u, scope| { + let signer_account = scope.random_non_zero_account(u)?; + let receiver_account = signer_account.clone(); + let beneficiary_id = { + if u.arbitrary::()? { + scope.new_account(u)? + } else { + scope.accounts[scope.random_alive_account_usize_id(u)?].clone() + } + }; + + let signer = scope.full_access_signer(u, &signer_account)?; + + scope.delete_account(scope.usize_id(&receiver_account)); + + Ok(TransactionConfig { + nonce: scope.nonce(), + signer_id: signer_account.id.clone(), + receiver_id: receiver_account.id, + signer, + actions: vec![Action::DeleteAccount(DeleteAccountAction { + beneficiary_id: beneficiary_id.id, + })], + }) + }); + } + + // Deploy Contract + options.push(|u, scope| { + let nonce = scope.nonce(); + + let signer_account = scope.random_account(u)?; + + let max_contract_id = scope.available_contracts.len() - 1; + let contract_id = u.int_in_range::(0..=max_contract_id)?; + + let signer = scope.full_access_signer(u, &signer_account)?; + + scope.deploy_contract(&signer_account, contract_id); + + Ok(TransactionConfig { + nonce, + signer_id: signer_account.id.clone(), + receiver_id: signer_account.id.clone(), + signer, + actions: vec![Action::DeployContract(DeployContractAction { + code: scope.available_contracts[contract_id].code.clone(), + })], + }) + }); + + // Multiple function calls + options.push(|u, scope| { + let nonce = scope.nonce(); + + let signer_account = scope.random_account(u)?; + let receiver_account = { + let mut possible_receiver_accounts = vec![]; + for account in &scope.accounts { + if account.deployed_contract != None { + possible_receiver_accounts.push(account); + } + } + if possible_receiver_accounts.is_empty() { + signer_account.clone() + } else { + (*u.choose(&possible_receiver_accounts)?).clone() + } + }; + + let signer = + scope.function_call_signer(u, &signer_account, receiver_account.id.as_str())?; + + let mut receiver_functions = vec![]; + if let Some(contract_id) = receiver_account.deployed_contract { + for function in &scope.available_contracts[contract_id].functions { + receiver_functions.push(function); + } + } + + if receiver_functions.is_empty() { + return Ok(TransactionConfig { + nonce, + signer_id: signer_account.id.clone(), + receiver_id: receiver_account.id.clone(), + signer, + actions: vec![], + }); + } + + let mut actions = vec![]; + let mut actions_num = u.int_in_range(0..=MAX_ACTIONS)?; + if u.int_in_range(0..=10)? == 0 { + actions_num = 1; + } + + while actions.len() < actions_num && u.len() > Function::size_hint(0).1.unwrap() { + let function = u.choose(&receiver_functions)?; + actions.push(Action::FunctionCall(Box::new(function.arbitrary(u)?))); + } + + Ok(TransactionConfig { + nonce, + signer_id: signer_account.id.clone(), + receiver_id: receiver_account.id.clone(), + signer, + actions, + }) + }); + + // Add key + options.push(|u, scope| { + let nonce = scope.nonce(); + + let signer_account = scope.random_account(u)?; + + let signer = scope.full_access_signer(u, &signer_account)?; + + Ok(TransactionConfig { + nonce, + signer_id: signer_account.id.clone(), + receiver_id: signer_account.id.clone(), + signer, + actions: vec![Action::AddKey(Box::new(scope.add_new_key( + u, + scope.usize_id(&signer_account), + nonce, + )?))], + }) + }); + + // Delete key + options.push(|u, scope| { + let nonce = scope.nonce(); + + let signer_account = scope.random_account(u)?; + let signer = scope.full_access_signer(u, &signer_account)?; + + if signer_account.keys.is_empty() { + return Ok(TransactionConfig { + nonce, + signer_id: signer_account.id.clone(), + receiver_id: signer_account.id.clone(), + signer, + actions: vec![], + }); + } + + let public_key = scope.delete_random_key(u, &signer_account)?; + + Ok(TransactionConfig { + nonce, + signer_id: signer_account.id.clone(), + receiver_id: signer_account.id.clone(), + signer, + actions: vec![Action::DeleteKey(Box::new(DeleteKeyAction { public_key }))], + }) + }); + + let f = u.choose(&options)?; + f(u, scope) + } + + fn size_hint(_depth: usize) -> (usize, Option) { + (7, Some(210)) + } +} + +#[derive(Clone)] +pub struct Scope { + accounts: Vec, + alive_accounts: HashSet, + nonce: Nonce, + height: BlockHeight, + available_contracts: Vec, + last_tx_num: usize, + account_id_to_idx: HashMap, +} + +#[derive(Clone)] +pub struct Account { + pub id: AccountId, + pub balance: Balance, + pub deployed_contract: Option, + pub keys: HashMap, +} + +#[derive(Clone)] +pub struct Key { + pub signer: InMemorySigner, + pub access_key: AccessKey, +} + +#[derive(Clone)] +pub struct Contract { + pub code: Vec, + pub functions: Vec, +} + +#[derive(Clone)] +pub enum Function { + // ################# + // # Test contract # + // ################# + StorageUsage, + BlockIndex, + BlockTimestamp, + PrepaidGas, + RandomSeed, + PredecessorAccountId, + SignerAccountPk, + SignerAccountId, + CurrentAccountId, + AccountBalance, + AttachedDeposit, + ValidatorTotalStake, + ExtSha256, + UsedGas, + WriteKeyValue, + WriteBlockHeight, + // ######################## + // # Contract for fuzzing # + // ######################## + SumOfNumbers, + DataReceipt, +} + +impl Scope { + fn from_seeds(seeds: &[String]) -> Self { + let accounts: Vec = seeds.iter().map(|id| Account::from_id(id.clone())).collect(); + let account_id_to_idx = accounts + .iter() + .enumerate() + .map(|(i, account)| (account.id.clone(), i)) + .collect::>(); + Scope { + accounts, + alive_accounts: HashSet::from_iter(0..seeds.len()), + nonce: 1_000_000, + height: 0, + available_contracts: Scope::construct_available_contracts(), + last_tx_num: MAX_TXS, + account_id_to_idx, + } + } + + fn construct_available_contracts() -> Vec { + vec![ + Contract { + code: unc_test_contracts::rs_contract().to_vec(), + functions: vec![ + Function::StorageUsage, + Function::BlockIndex, + Function::BlockTimestamp, + Function::PrepaidGas, + Function::RandomSeed, + Function::PredecessorAccountId, + Function::SignerAccountPk, + Function::SignerAccountId, + Function::CurrentAccountId, + Function::AccountBalance, + Function::AttachedDeposit, + Function::ValidatorTotalStake, + Function::ExtSha256, + Function::UsedGas, + Function::WriteKeyValue, + Function::WriteBlockHeight, + ], + }, + Contract { + code: unc_test_contracts::fuzzing_contract().to_vec(), + functions: vec![Function::SumOfNumbers, Function::DataReceipt], + }, + ] + } + + pub fn inc_height(&mut self) { + self.height += 1 + } + + pub fn height(&self) -> BlockHeight { + self.height + } + + pub fn inc_nonce(&mut self) { + self.nonce += 1; + } + + pub fn nonce(&self) -> Nonce { + self.nonce + } + + pub fn num_alive_accounts(&self) -> usize { + self.alive_accounts.len() + } + + pub fn random_non_zero_alive_account_usize_id(&self, u: &mut Unstructured) -> Result { + let mut accounts = self.alive_accounts.clone(); + accounts.remove(&0); + Ok(*u.choose(&accounts.iter().cloned().collect::>())?) + } + + pub fn random_alive_account_usize_id(&self, u: &mut Unstructured) -> Result { + Ok(*u.choose(&self.alive_accounts.iter().cloned().collect::>())?) + } + + pub fn random_account(&self, u: &mut Unstructured) -> Result { + Ok(self.accounts[self.random_alive_account_usize_id(u)?].clone()) + } + + pub fn random_non_zero_account(&self, u: &mut Unstructured) -> Result { + Ok(self.accounts[self.random_non_zero_alive_account_usize_id(u)?].clone()) + } + + pub fn usize_id(&self, account: &Account) -> usize { + self.account_id_to_idx[&account.id] + } + + fn new_test_account(&mut self) -> Account { + let new_id = format!("test{}", self.accounts.len()); + self.alive_accounts.insert(self.accounts.len()); + self.accounts.push(Account::from_id(new_id)); + self.accounts[self.accounts.len() - 1].clone() + } + + fn new_implicit_account(&mut self, u: &mut Unstructured) -> Result { + let mut new_id_vec = vec![]; + let mut chars = vec![]; + for x in b'a'..=b'f' { + chars.push(x); + } + for x in b'0'..=b'9' { + chars.push(x); + } + for _ in 0..64 { + new_id_vec.push(*u.choose(&chars)?); + } + let new_id = String::from_utf8(new_id_vec).unwrap(); + self.alive_accounts.insert(self.accounts.len()); + self.accounts.push(Account::from_id(new_id)); + Ok(self.accounts[self.accounts.len() - 1].clone()) + } + + pub fn new_account(&mut self, u: &mut Unstructured) -> Result { + let account = if u.arbitrary::()? { + self.new_implicit_account(u)? + } else { + self.new_test_account() + }; + self.account_id_to_idx.insert(account.id.clone(), self.accounts.len() - 1); + Ok(account) + } + + pub fn delete_account(&mut self, account_usize_id: usize) { + self.alive_accounts.remove(&account_usize_id); + } + + pub fn deploy_contract(&mut self, receiver_account: &Account, contract_id: usize) { + let acc_id = self.usize_id(receiver_account); + self.accounts[acc_id].deployed_contract = Some(contract_id); + } + + pub fn add_new_key( + &mut self, + u: &mut Unstructured, + account_id: usize, + nonce: Nonce, + ) -> Result { + let permission = { + if u.arbitrary::()? { + AccessKeyPermission::FullAccess + } else { + AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: None, + receiver_id: self.random_account(u)?.id.into(), + method_names: vec![], + }) + } + }; + let signer = InMemorySigner::from_seed( + self.accounts[account_id].id.clone(), + KeyType::ED25519, + format!("test{}.{}", account_id, nonce).as_str(), + ); + self.accounts[account_id].keys.insert( + nonce, + Key { + signer: signer.clone(), + access_key: AccessKey { nonce, permission: permission.clone() }, + }, + ); + Ok(AddKeyAction { + public_key: signer.public_key, + access_key: AccessKey { nonce, permission }, + }) + } + + pub fn full_access_signer( + &self, + u: &mut Unstructured, + account: &Account, + ) -> Result { + let account_idx = self.usize_id(account); + let possible_signers = self.accounts[account_idx].full_access_keys(); + if possible_signers.is_empty() { + // this transaction will be invalid + Ok(InMemorySigner::from_seed( + self.accounts[account_idx].id.clone(), + KeyType::ED25519, + self.accounts[account_idx].id.as_ref(), + )) + } else { + Ok(u.choose(&possible_signers)?.clone()) + } + } + + pub fn function_call_signer( + &self, + u: &mut Unstructured, + account: &Account, + receiver_id: &str, + ) -> Result { + let account_idx = self.usize_id(account); + let possible_signers = self.accounts[account_idx].function_call_keys(receiver_id); + if possible_signers.is_empty() { + // this transaction will be invalid + Ok(InMemorySigner::from_seed( + self.accounts[account_idx].id.clone(), + KeyType::ED25519, + self.accounts[account_idx].id.as_ref(), + )) + } else { + Ok(u.choose(&possible_signers)?.clone()) + } + } + + pub fn delete_random_key( + &mut self, + u: &mut Unstructured, + account: &Account, + ) -> Result { + let account_idx = self.usize_id(account); + let (nonce, key) = self.accounts[account_idx].random_key(u)?; + let public_key = key.signer.public_key; + self.accounts[account_idx].keys.remove(&nonce); + Ok(public_key) + } +} + +impl Account { + pub fn from_id(id: String) -> Self { + let mut keys = HashMap::new(); + keys.insert( + 0, + Key { + signer: InMemorySigner::from_seed( + AccountId::from_str(id.as_str()).expect("Invalid account_id"), + KeyType::ED25519, + id.as_ref(), + ), + access_key: AccessKey { nonce: 0, permission: AccessKeyPermission::FullAccess }, + }, + ); + Self { + id: AccountId::from_str(id.as_str()).expect("Invalid account_id"), + balance: TESTING_INIT_BALANCE, + deployed_contract: None, + keys, + } + } + + pub fn full_access_keys(&self) -> Vec { + let mut full_access_keys = vec![]; + for (_, key) in &self.keys { + if key.access_key.permission == AccessKeyPermission::FullAccess { + full_access_keys.push(key.signer.clone()); + } + } + full_access_keys + } + + pub fn function_call_keys(&self, receiver_id: &str) -> Vec { + let mut function_call_keys = vec![]; + for (_, key) in &self.keys { + match &key.access_key.permission { + AccessKeyPermission::FullAccess => function_call_keys.push(key.signer.clone()), + AccessKeyPermission::FunctionCall(function_call_permission) => { + if function_call_permission.receiver_id == receiver_id { + function_call_keys.push(key.signer.clone()) + } + } + } + } + function_call_keys + } + + pub fn random_key(&self, u: &mut Unstructured) -> Result<(Nonce, Key)> { + let (nonce, key) = *u.choose(&self.keys.iter().collect::>())?; + Ok((*nonce, key.clone())) + } +} + +impl Function { + pub fn arbitrary(&self, u: &mut Unstructured) -> Result { + let method_name; + let mut args = Vec::new(); + match self { + // ################# + // # Test contract # + // ################# + Function::StorageUsage => { + method_name = "ext_storage_usage"; + } + Function::BlockIndex => { + method_name = "ext_block_index"; + } + Function::BlockTimestamp => { + method_name = "ext_block_timestamp"; + } + Function::PrepaidGas => { + method_name = "ext_prepaid_gas"; + } + Function::RandomSeed => { + method_name = "ext_random_seed"; + } + Function::PredecessorAccountId => { + method_name = "ext_predecessor_account_id"; + } + Function::SignerAccountPk => { + method_name = "ext_signer_account_pk"; + } + Function::SignerAccountId => { + method_name = "ext_signer_account_id"; + } + Function::CurrentAccountId => { + method_name = "ext_current_account_id"; + } + Function::AccountBalance => { + method_name = "ext_account_balance"; + } + Function::AttachedDeposit => { + method_name = "ext_attached_deposit"; + } + Function::ValidatorTotalStake => { + method_name = "ext_validators_total_stake"; + } + Function::ExtSha256 => { + let len = u.int_in_range(0..=100)?; + method_name = "ext_sha256"; + args = u.bytes(len)?.to_vec(); + } + Function::UsedGas => { + method_name = "ext_used_gas"; + } + Function::WriteKeyValue => { + let key = u.int_in_range::(0..=1_000)?.to_le_bytes(); + let value = u.int_in_range::(0..=1_000)?.to_le_bytes(); + method_name = "write_key_value"; + args = [&key[..], &value[..]].concat(); + } + Function::WriteBlockHeight => { + method_name = "write_block_height"; + } + // ######################## + // # Contract for fuzzing # + // ######################## + Function::SumOfNumbers => { + method_name = "sum_of_numbers"; + args = u.int_in_range::(1..=10)?.to_le_bytes().to_vec(); + } + Function::DataReceipt => { + method_name = "data_receipt_with_size"; + args = u.choose(&[10u64, 100, 1000, 10000, 100000])?.to_le_bytes().to_vec(); + } + }; + Ok(FunctionCallAction { + method_name: method_name.to_string(), + args: args, + gas: GAS_1, + deposit: 0, + }) + } + + fn size_hint(_depth: usize) -> (usize, Option) { + (0, Some(20)) + } +} + +#[cfg(test)] +mod tests { + use crate::Scenario; + + fn do_fuzz(scenario: &Scenario) -> Result<(), String> { + let stats = scenario.run().result.map_err(|e| e.to_string())?; + for block_stats in stats.blocks_stats { + if block_stats.block_production_time > std::time::Duration::from_secs(2) { + return Err(format!( + "block at height {} was produced in {:?}", + block_stats.height, block_stats.block_production_time + )); + } + } + Ok(()) + } + + fn fuzz(scenario: &Scenario) { + if let Err(err) = do_fuzz(scenario) { + let file = "failed_scenario.json"; + serde_json::to_writer(&std::fs::File::create(file).unwrap(), &scenario).unwrap(); + panic!("Bad scenario: {}, {}", file, err); + } + } + + #[test] + fn scenario_fuzzer() { + bolero::check!() + .with_iterations(100) // Limit to 100 iterations, the default of 1000 would be too slow + .with_arbitrary::() + .for_each(fuzz) + } +} diff --git a/test-utils/runtime-tester/src/lib.rs b/test-utils/runtime-tester/src/lib.rs new file mode 100644 index 000000000..c5a7472e6 --- /dev/null +++ b/test-utils/runtime-tester/src/lib.rs @@ -0,0 +1,55 @@ +#![doc = include_str!("../README.md")] + +pub mod fuzzing; +pub mod run_test; +pub mod scenario_builder; + +pub use crate::run_test::{BlockConfig, NetworkConfig, RuntimeConfig, Scenario, TransactionConfig}; +pub use crate::scenario_builder::ScenarioBuilder; + +#[test] +// Use this test as a base for creating reproducers. +fn scenario_smoke_test() { + use unc_crypto::{InMemorySigner, KeyType}; + use unc_primitives::transaction::{Action, TransferAction}; + use unc_primitives::types::AccountId; + + let num_accounts = 5; + + let seeds: Vec = (0..num_accounts).map(|i| format!("test{}", i)).collect(); + let accounts: Vec = seeds.iter().map(|id| id.parse().unwrap()).collect(); + + let mut scenario = Scenario { + network_config: NetworkConfig { seeds }, + runtime_config: RuntimeConfig { + max_total_prepaid_gas: 300 * 10u64.pow(12), + gas_limit: 1_000_000_000_000_000, + epoch_length: 500, + }, + blocks: Vec::new(), + use_in_memory_store: true, + is_fuzzing: false, + }; + + for h in 1..5 { + let mut block = BlockConfig::at_height(h); + let transaction = { + let signer_id = accounts[h as usize].clone(); + let receiver_id = accounts[(h - 1) as usize].clone(); + let signer = + InMemorySigner::from_seed(signer_id.clone(), KeyType::ED25519, signer_id.as_ref()); + + TransactionConfig { + nonce: h, + signer_id, + receiver_id, + signer, + actions: vec![Action::Transfer(TransferAction { deposit: 10 })], + } + }; + block.transactions.push(transaction); + scenario.blocks.push(block) + } + + scenario.run().result.unwrap(); +} diff --git a/test-utils/runtime-tester/src/run_test.rs b/test-utils/runtime-tester/src/run_test.rs new file mode 100644 index 000000000..5ceda9fff --- /dev/null +++ b/test-utils/runtime-tester/src/run_test.rs @@ -0,0 +1,221 @@ +use unc_chain::{Block, ChainGenesis, Provenance}; +use unc_chain_configs::Genesis; +use unc_client::test_utils::TestEnv; +use unc_client::ProcessTxResponse; +use unc_client_primitives::types::Error; +use unc_crypto::InMemorySigner; +use unc_epoch_manager::EpochManager; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::hash::CryptoHash; +use unc_primitives::transaction::{Action, SignedTransaction}; +use unc_primitives::types::{AccountId, BlockHeight, BlockHeightDelta, Gas, Nonce}; +use unc_store::config::StateSnapshotType; +use unc_store::genesis::initialize_genesis_state; +use unc_store::test_utils::create_test_store; +use framework::{config::GenesisExt, NightshadeRuntime}; +use std::io; +use std::path::Path; +use std::time::Duration; + +pub struct ScenarioResult { + pub result: std::result::Result, + + /// If scenario was run with on-disk storage (i.e. `use_in_memory_store` was + /// `false`, the home directory of the node. + pub homedir: Option, + pub env: TestEnv, +} + +impl Scenario { + pub fn from_file(path: &Path) -> io::Result { + serde_json::from_str::(&std::fs::read_to_string(path)?).map_err(io::Error::from) + } + + pub fn run(&self) -> ScenarioResult { + let accounts: Vec = + self.network_config.seeds.iter().map(|x| x.parse().unwrap()).collect(); + let clients = vec![accounts[0].clone()]; + let mut genesis = Genesis::test(accounts, 1); + let mut runtime_config = unc_parameters::RuntimeConfig::test(); + runtime_config.wasm_config.limit_config.max_total_prepaid_gas = + self.runtime_config.max_total_prepaid_gas; + genesis.config.epoch_length = self.runtime_config.epoch_length; + genesis.config.gas_limit = self.runtime_config.gas_limit; + let runtime_config_store = RuntimeConfigStore::with_one_config(runtime_config); + + let (tempdir, store) = if self.use_in_memory_store { + (None, create_test_store()) + } else { + let (tempdir, opener) = unc_store::NodeStorage::test_opener(); + let store = opener.open().unwrap(); + (Some(tempdir), store.get_hot_store()) + }; + let home_dir = tempdir.as_ref().map(|d| d.path()); + initialize_genesis_state(store.clone(), &genesis, home_dir); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = NightshadeRuntime::test_with_runtime_config_store( + home_dir.unwrap_or(Path::new(".")), + store.clone(), + &genesis.config, + epoch_manager.clone(), + runtime_config_store, + StateSnapshotType::ForReshardingOnly, + ); + + let mut env = TestEnv::builder(ChainGenesis::new(&genesis)) + .clients(clients.clone()) + .validators(clients) + .stores(vec![store]) + .epoch_managers(vec![epoch_manager]) + .runtimes(vec![runtime]) + .build(); + + let result = self.process_blocks(&mut env); + ScenarioResult { result, homedir: tempdir, env } + } + + fn process_blocks(&self, env: &mut TestEnv) -> Result { + let mut last_block = env.clients[0].chain.get_block_by_height(0).unwrap(); + + let mut runtime_stats = RuntimeStats::default(); + + for block in &self.blocks { + let mut block_stats = BlockStats::at_height(block.height); + + for tx in &block.transactions { + let signed_tx = tx.to_signed_transaction(&last_block); + block_stats.tx_hashes.push(signed_tx.get_hash()); + if !self.is_fuzzing { + // fuzzing can generate invalid transactions + assert_eq!( + env.clients[0].process_tx(signed_tx, false, false), + ProcessTxResponse::ValidTx + ); + } + } + + let start_time = cpu_time::ProcessTime::now(); + + last_block = env.clients[0] + .produce_block(block.height)? + .ok_or_else(|| Error::Other(String::from("No block has been produced")))?; + env.process_block(0, last_block.clone(), Provenance::PRODUCED); + + block_stats.block_production_time = start_time.elapsed(); + + runtime_stats.blocks_stats.push(block_stats); + } + + Ok(runtime_stats) + } +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct Scenario { + pub network_config: NetworkConfig, + pub runtime_config: RuntimeConfig, + pub blocks: Vec, + pub use_in_memory_store: bool, + pub is_fuzzing: bool, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct NetworkConfig { + pub seeds: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct RuntimeConfig { + pub max_total_prepaid_gas: Gas, + pub gas_limit: Gas, + pub epoch_length: BlockHeightDelta, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct BlockConfig { + pub height: BlockHeight, + pub transactions: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct TransactionConfig { + pub nonce: Nonce, + pub signer_id: AccountId, + pub receiver_id: AccountId, + pub signer: InMemorySigner, + pub actions: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize, Default, Debug)] +pub struct RuntimeStats { + pub blocks_stats: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize, Default, Debug)] +pub struct BlockStats { + pub height: u64, + pub block_production_time: Duration, + pub tx_hashes: Vec, +} + +impl std::fmt::Debug for Scenario { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", serde_json::to_string_pretty(&self).unwrap()) + } +} + +impl BlockConfig { + pub fn at_height(height: BlockHeight) -> Self { + Self { height, transactions: vec![] } + } +} + +impl TransactionConfig { + fn to_signed_transaction(&self, last_block: &Block) -> SignedTransaction { + SignedTransaction::from_actions( + self.nonce, + self.signer_id.clone(), + self.receiver_id.clone(), + &self.signer, + self.actions.clone(), + *last_block.hash(), + ) + } +} + +impl BlockStats { + fn at_height(height: BlockHeight) -> Self { + Self { height, block_production_time: Duration::default(), tx_hashes: vec![] } + } +} + +#[cfg(test)] +mod test { + use super::*; + + use std::path::Path; + use std::time::{Duration, Instant}; + + use unc_o11y::testonly::init_test_logger; + use tracing::info; + + #[test] + #[ignore] + fn test_scenario_json() { + init_test_logger(); + let path = Path::new("./fuzz/scenario.json"); + + let scenario = Scenario::from_file(path).expect("Failed to deserialize the scenario file."); + let starting_time = Instant::now(); + let runtime_stats = scenario.run().result.expect("Error while running scenario"); + info!("Time to run: {:?}", starting_time.elapsed()); + for block_stats in runtime_stats.blocks_stats { + if block_stats.block_production_time > Duration::from_secs(1) { + info!( + "Time to produce block {} is {:?}", + block_stats.height, block_stats.block_production_time + ); + } + } + } +} diff --git a/test-utils/runtime-tester/src/scenario_builder.rs b/test-utils/runtime-tester/src/scenario_builder.rs new file mode 100644 index 000000000..cefcec866 --- /dev/null +++ b/test-utils/runtime-tester/src/scenario_builder.rs @@ -0,0 +1,145 @@ +use crate::run_test::{BlockConfig, NetworkConfig, RuntimeConfig, Scenario, TransactionConfig}; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_primitives::{ + transaction::Action, + types::{AccountId, BlockHeight, BlockHeightDelta, Gas, Nonce}, +}; + +use std::str::FromStr; + +pub struct ScenarioBuilder { + height: BlockHeight, + nonce: Nonce, + scenario: Scenario, +} + +/// # Example +/// # Produce three blocks. The first one deploys a contract to the second account, other two blocks are empty. +/// # Assert that production of all blocks took less than a second. +/// ``` +/// use runtime_tester::ScenarioBuilder; +/// use std::time::Duration; +/// use unc_primitives::transaction::{Action, DeployContractAction}; +/// +/// let mut builder = ScenarioBuilder::new(). +/// number_of_accounts(10). +/// in_memory_store(true); +/// +/// builder.add_block(); +/// builder.add_transaction(0, 9, +/// vec![Action::DeployContract(DeployContractAction { +/// code: unc_test_contracts::rs_contract().to_vec(), +/// })]); +/// +/// builder.add_block(); +/// builder.add_block(); +/// +/// let runtime_stats = builder.scenario().run().result.unwrap(); +/// +/// for block_stats in runtime_stats.blocks_stats { +/// assert!(block_stats.block_production_time < Duration::from_secs(1), +/// "Block at height {} was produced in {:?}", +/// block_stats.height, block_stats.block_production_time); +/// } +/// ``` +impl ScenarioBuilder { + /// Creates builder with an empty scenario with 4 accounts. + /// Default `use_in_memory_store` -- true. + pub fn new() -> Self { + let network_config = NetworkConfig { seeds: (0..4).map(id_to_seed).collect() }; + let runtime_config = RuntimeConfig { + max_total_prepaid_gas: 300 * 10u64.pow(12), + gas_limit: 1_000_000_000_000_000, + epoch_length: 500, + }; + + ScenarioBuilder { + height: 1, + nonce: 1, + scenario: Scenario { + network_config, + runtime_config, + blocks: vec![], + use_in_memory_store: true, + is_fuzzing: false, + }, + } + } + + /// Changes number of accounts to `num_accounts`. + pub fn number_of_accounts(mut self, num_accounts: usize) -> Self { + self.scenario.network_config = + NetworkConfig { seeds: (0..num_accounts).map(id_to_seed).collect() }; + self + } + + /// Changes max_total_prepaid_gas + pub fn max_total_prepaid_gas(mut self, max_total_prepaid_gas: Gas) -> Self { + self.scenario.runtime_config.max_total_prepaid_gas = max_total_prepaid_gas; + self + } + + /// Changes gas_limit + pub fn gas_limit(mut self, gas_limit: Gas) -> Self { + self.scenario.runtime_config.gas_limit = gas_limit; + self + } + + /// Changes epoch_length + pub fn epoch_length(mut self, epoch_length: BlockHeightDelta) -> Self { + self.scenario.runtime_config.epoch_length = epoch_length; + self + } + + /// Changes `use_in_memory_store`. + pub fn in_memory_store(mut self, in_memory_store: bool) -> Self { + self.scenario.use_in_memory_store = in_memory_store; + self + } + + /// Adds empty block to the scenario with the next height (starting from 1). + pub fn add_block(&mut self) { + self.scenario.blocks.push(BlockConfig::at_height(self.height)); + self.height += 1; + } + + /// Adds transaction to the last block in the scenario. + pub fn add_transaction( + &mut self, + signer_index: usize, + receiver_index: usize, + actions: Vec, + ) { + assert!(!self.scenario.blocks.is_empty()); + + let signer_id = AccountId::from_str(&id_to_seed(signer_index)).unwrap(); + let receiver_id = AccountId::from_str(&id_to_seed(receiver_index)).unwrap(); + + let signer = + InMemorySigner::from_seed(signer_id.clone(), KeyType::ED25519, signer_id.as_ref()); + + let block = { + let last_id = self.scenario.blocks.len() - 1; + &mut self.scenario.blocks[last_id] + }; + + (*block).transactions.push(TransactionConfig { + nonce: self.nonce, + signer_id: signer_id, + receiver_id: receiver_id, + signer, + actions, + }); + + self.nonce += 1 + } + + /// Returns a reference to the built scenario. + pub fn scenario(&self) -> &Scenario { + &self.scenario + } +} + +fn id_to_seed(id: usize) -> String { + format!("test{}", id) +} diff --git a/test-utils/store-validator/Cargo.toml b/test-utils/store-validator/Cargo.toml new file mode 100644 index 000000000..02e9e658e --- /dev/null +++ b/test-utils/store-validator/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "store-validator" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +clap.workspace = true +yansi.workspace = true + +unc-epoch-manager.workspace = true +unc-chain-configs.workspace = true +unc-primitives.workspace = true +unc-store.workspace = true +unc-o11y.workspace = true +unc-chain.workspace = true +framework.workspace = true + +[dev-dependencies] +testlib.workspace = true +serde_json.workspace = true +unc-client.workspace = true diff --git a/test-utils/store-validator/src/main.rs b/test-utils/store-validator/src/main.rs new file mode 100644 index 000000000..3ebfe0d1a --- /dev/null +++ b/test-utils/store-validator/src/main.rs @@ -0,0 +1,91 @@ +use clap::{Arg, Command}; +use unc_chain::store_validator::StoreValidator; +use unc_chain_configs::GenesisValidationMode; +use unc_epoch_manager::{ + shard_tracker::{ShardTracker, TrackedConfig}, + EpochManager, +}; +use unc_o11y::testonly::init_integration_logger; +use framework::{get_default_home, load_config}; +use std::path::PathBuf; +use std::process; +use yansi::Color::{Green, Red, White, Yellow}; + +fn main() { + init_integration_logger(); + + let matches = Command::new("store-validator") + .arg( + Arg::new("home") + .long("home") + .default_value(get_default_home().into_os_string()) + .value_parser(clap::value_parser!(PathBuf)) + .help("Directory for config and data (default \"~/.near\")") + .action(clap::ArgAction::Set), + ) + .subcommand(Command::new("validate")) + .get_matches(); + + let home_dir = matches.get_one::("home").unwrap(); + let unc_config = load_config(home_dir, GenesisValidationMode::Full) + .unwrap_or_else(|e| panic!("Error loading config: {:#}", e)); + + let store = unc_store::NodeStorage::opener( + home_dir, + unc_config.config.archive, + &unc_config.config.store, + None, + ) + .open() + .unwrap() + .get_hot_store(); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &unc_config.genesis.config); + let shard_tracker = ShardTracker::new( + TrackedConfig::from_config(&unc_config.client_config), + epoch_manager.clone(), + ); + let runtime = framework::NightshadeRuntime::from_config( + home_dir, + store.clone(), + &unc_config, + epoch_manager.clone(), + ); + + let mut store_validator = StoreValidator::new( + unc_config.validator_signer.as_ref().map(|x| x.validator_id().clone()), + unc_config.genesis.config, + epoch_manager, + shard_tracker, + runtime, + store, + false, + ); + store_validator.validate(); + + if store_validator.tests_done() == 0 { + println!("{}", Red.style().bold().paint("No conditions has been validated")); + process::exit(1); + } + println!( + "{} {}", + White.style().bold().paint("Conditions validated:"), + Green.style().bold().paint(store_validator.tests_done().to_string()) + ); + for error in store_validator.errors.iter() { + println!( + "{} {} {}", + Red.style().bold().paint(&error.col), + Yellow.style().bold().paint(&error.key), + error.err + ); + } + if store_validator.is_failed() { + println!( + "Errors found: {}", + Red.style().bold().paint(store_validator.num_failed().to_string()) + ); + process::exit(1); + } else { + println!("{}", Green.style().bold().paint("No errors found")); + } +} diff --git a/test-utils/style/Cargo.toml b/test-utils/style/Cargo.toml new file mode 100644 index 000000000..3510b49ae --- /dev/null +++ b/test-utils/style/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "style-tests" +version = "0.0.0" +edition = "2021" +publish = false +description = "An entry point for miscelaneous (stylistic) workspace-wide tests" +authors = ["Hello Inc "] + +[lints] +workspace = true + +[dependencies] diff --git a/test-utils/style/src/lib.rs b/test-utils/style/src/lib.rs new file mode 100644 index 000000000..d0ce3832e --- /dev/null +++ b/test-utils/style/src/lib.rs @@ -0,0 +1,79 @@ +#![cfg(test)] +use std::{ + ffi::{OsStr, OsString}, + path::PathBuf, + process::Command, +}; + +/// Add common cargo arguments for tests run by this code. +fn cargo_env(cmd: &mut Command, target_dir: Option<&str>) { + // Set the working directory to the project root, rather than using whatever default nextest + // gives us. + let style_root = std::env::var_os("CARGO_MANIFEST_DIR").unwrap_or(OsString::from("./")); + let wp_root: PathBuf = [&style_root, OsStr::new(".."), OsStr::new("..")].into_iter().collect(); + cmd.current_dir(&wp_root); + + if let Some(tgt_dir) = target_dir { + // Use a different target directory to avoid invalidating any cache after tests are run (so + // that running `cargo nextest` twice does not rebuild half of the workspace on the 2nd + // rebuild. Unfortunately cargo itself does not readily expose this information to us, so + // we have to guess a little as to where this directory might end up. + // + // NB: We aren't using a temporary directory proper here in order to *allow* keeping cache + // between individual `clippy` runs and such. + let target_dir: PathBuf = + [wp_root.as_os_str(), OsStr::new("target"), OsStr::new(tgt_dir)].into_iter().collect(); + cmd.env("CARGO_TARGET_DIR", target_dir.as_path()); + } +} + +/// Create a cargo command. +/// +/// You will want to set `target_dir` to some unique `Some` value whenever there’s a chance that +/// this invocation of `cargo` will build any project code. Setting unique values avoids lock +/// contention and unintentional cache invalidation. +fn cargo(target_dir: Option<&str>) -> Command { + let cargo = std::env::var_os("CARGO").unwrap_or(OsString::from("cargo")); + let mut cmd = Command::new(cargo); + cargo_env(&mut cmd, target_dir); + cmd +} + +fn ensure_success(mut cmd: std::process::Command) { + println!("Running {:?}", cmd); + match cmd.status() { + Err(e) => { + panic!("Could not spawn the command: {e}") + } + Ok(out) if !out.success() => panic!("exit code {:?}", out), + Ok(_) => {} + } +} + +#[test] +fn rustfmt() { + let mut cmd = cargo(None); + cmd.args(&["fmt", "--", "--check"]); + ensure_success(cmd); +} + +#[test] +fn clippy() { + let mut cmd = cargo(Some("style")); + cmd.args(&["clippy", "--all-targets", "--all-features", "--locked"]); + ensure_success(cmd); +} + +#[test] +fn deny() { + let mut cmd = cargo(None); + cmd.args(&["deny", "--all-features", "--locked", "check", "bans"]); + ensure_success(cmd); +} + +#[test] +fn themis() { + let mut cmd = cargo(Some("themis")); + cmd.args(&["run", "--locked", "-p", "themis"]); + ensure_success(cmd); +} diff --git a/test-utils/testlib/Cargo.toml b/test-utils/testlib/Cargo.toml new file mode 100644 index 000000000..f20b34de3 --- /dev/null +++ b/test-utils/testlib/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "testlib" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +once_cell.workspace = true + +unc-chain-configs.workspace = true +unc-chain.workspace = true +unc-crypto.workspace = true +unc-parameters.workspace = true +unc-primitives.workspace = true +unc-test-contracts.workspace = true +node-runtime.workspace = true + +[features] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-chain/nightly_protocol", + "unc-parameters/nightly_protocol", + "unc-primitives/nightly_protocol", + "node-runtime/nightly_protocol", +] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-chain/nightly", + "unc-parameters/nightly", + "unc-primitives/nightly", + "node-runtime/nightly", +] +default = [] diff --git a/test-utils/testlib/src/fees_utils.rs b/test-utils/testlib/src/fees_utils.rs new file mode 100644 index 000000000..16f67d732 --- /dev/null +++ b/test-utils/testlib/src/fees_utils.rs @@ -0,0 +1,195 @@ +//! Helper functions to compute the costs of certain actions assuming they succeed and the only +//! actions in the transaction batch. +use unc_parameters::{ActionCosts, RuntimeConfig, RuntimeFeesConfig}; +use unc_primitives::transaction::Action; +use unc_primitives::types::{AccountId, Balance, Gas}; + +pub struct FeeHelper { + pub rt_cfg: RuntimeConfig, + pub gas_price: Balance, +} + +impl FeeHelper { + pub fn new(rt_cfg: RuntimeConfig, gas_price: Balance) -> Self { + Self { rt_cfg, gas_price } + } + + pub fn cfg(&self) -> &RuntimeFeesConfig { + &self.rt_cfg.fees + } + + pub fn gas_to_balance_inflated(&self, gas: Gas) -> Balance { + gas as Balance + * (self.gas_price * (*self.cfg().pessimistic_gas_price_inflation_ratio.numer() as u128) + / (*self.cfg().pessimistic_gas_price_inflation_ratio.denom() as u128)) + } + + pub fn gas_to_balance(&self, gas: Gas) -> Balance { + gas as Balance * self.gas_price + } + + pub fn gas_burnt_to_reward(&self, gas_burnt: Gas) -> Balance { + let gas_reward = gas_burnt * *self.cfg().burnt_gas_reward.numer() as u64 + / *self.cfg().burnt_gas_reward.denom() as u64; + self.gas_to_balance(gas_reward) + } + + pub fn create_account_cost(&self) -> Balance { + let exec_gas = self.cfg().fee(ActionCosts::new_action_receipt).exec_fee() + + self.cfg().fee(ActionCosts::create_account).exec_fee(); + let send_gas = self.cfg().fee(ActionCosts::new_action_receipt).send_fee(false) + + self.cfg().fee(ActionCosts::create_account).send_fee(false); + self.gas_to_balance(exec_gas + send_gas) + } + + pub fn create_account_transfer_full_key_fee(&self) -> Gas { + let exec_gas = self.cfg().fee(ActionCosts::new_action_receipt).exec_fee() + + self.cfg().fee(ActionCosts::create_account).exec_fee() + + self.cfg().fee(ActionCosts::transfer).exec_fee() + + self.cfg().fee(ActionCosts::add_full_access_key).exec_fee(); + let send_gas = self.cfg().fee(ActionCosts::new_action_receipt).send_fee(false) + + self.cfg().fee(ActionCosts::create_account).send_fee(false) + + self.cfg().fee(ActionCosts::transfer).send_fee(false) + + self.cfg().fee(ActionCosts::add_full_access_key).send_fee(false); + exec_gas + send_gas + } + + pub fn create_account_transfer_fee(&self) -> Gas { + let exec_gas = self.cfg().fee(ActionCosts::new_action_receipt).exec_fee() + + self.cfg().fee(ActionCosts::create_account).exec_fee() + + self.cfg().fee(ActionCosts::transfer).exec_fee(); + let send_gas = self.cfg().fee(ActionCosts::new_action_receipt).send_fee(false) + + self.cfg().fee(ActionCosts::create_account).send_fee(false) + + self.cfg().fee(ActionCosts::transfer).send_fee(false); + exec_gas + send_gas + } + + pub fn create_account_transfer_full_key_cost(&self) -> Balance { + self.gas_to_balance(self.create_account_transfer_full_key_fee()) + } + + pub fn create_account_transfer_cost(&self) -> Balance { + self.gas_to_balance(self.create_account_transfer_fee()) + } + + pub fn create_account_transfer_full_key_cost_no_reward(&self) -> Balance { + let exec_gas = self.cfg().fee(ActionCosts::new_action_receipt).exec_fee() + + self.cfg().fee(ActionCosts::create_account).exec_fee() + + self.cfg().fee(ActionCosts::transfer).exec_fee() + + self.cfg().fee(ActionCosts::add_full_access_key).exec_fee(); + let send_gas = self.cfg().fee(ActionCosts::new_action_receipt).send_fee(false) + + self.cfg().fee(ActionCosts::create_account).send_fee(false) + + self.cfg().fee(ActionCosts::transfer).send_fee(false) + + self.cfg().fee(ActionCosts::add_full_access_key).send_fee(false); + self.gas_to_balance(send_gas) + self.gas_to_balance_inflated(exec_gas) + } + + pub fn create_account_transfer_full_key_cost_fail_on_create_account(&self) -> Balance { + let exec_gas = self.cfg().fee(ActionCosts::new_action_receipt).exec_fee() + + self.cfg().fee(ActionCosts::create_account).exec_fee(); + let send_gas = self.cfg().fee(ActionCosts::new_action_receipt).send_fee(false) + + self.cfg().fee(ActionCosts::create_account).send_fee(false) + + self.cfg().fee(ActionCosts::transfer).send_fee(false) + + self.cfg().fee(ActionCosts::add_full_access_key).send_fee(false); + self.gas_to_balance(exec_gas + send_gas) + } + + pub fn deploy_contract_cost(&self, num_bytes: u64) -> Balance { + let exec_gas = self.cfg().fee(ActionCosts::new_action_receipt).exec_fee() + + self.cfg().fee(ActionCosts::deploy_contract_base).exec_fee() + + num_bytes * self.cfg().fee(ActionCosts::deploy_contract_byte).exec_fee(); + let send_gas = self.cfg().fee(ActionCosts::new_action_receipt).send_fee(true) + + self.cfg().fee(ActionCosts::deploy_contract_base).send_fee(true) + + num_bytes * self.cfg().fee(ActionCosts::deploy_contract_byte).send_fee(true); + self.gas_to_balance(exec_gas + send_gas) + } + + pub fn function_call_exec_gas(&self, num_bytes: u64) -> Gas { + self.cfg().fee(ActionCosts::new_action_receipt).exec_fee() + + self.cfg().fee(ActionCosts::function_call_base).exec_fee() + + num_bytes * self.cfg().fee(ActionCosts::function_call_byte).exec_fee() + } + + pub fn function_call_cost(&self, num_bytes: u64, prepaid_gas: u64) -> Balance { + let exec_gas = self.function_call_exec_gas(num_bytes); + let send_gas = self.cfg().fee(ActionCosts::new_action_receipt).send_fee(false) + + self.cfg().fee(ActionCosts::function_call_base).send_fee(false) + + num_bytes * self.cfg().fee(ActionCosts::function_call_byte).send_fee(false); + self.gas_to_balance(exec_gas + send_gas + prepaid_gas) + } + + pub fn transfer_fee(&self) -> Gas { + let exec_gas = self.cfg().fee(ActionCosts::new_action_receipt).exec_fee() + + self.cfg().fee(ActionCosts::transfer).exec_fee(); + let send_gas = self.cfg().fee(ActionCosts::new_action_receipt).send_fee(false) + + self.cfg().fee(ActionCosts::transfer).send_fee(false); + exec_gas + send_gas + } + + pub fn transfer_cost(&self) -> Balance { + self.gas_to_balance(self.transfer_fee()) + } + + pub fn stake_cost(&self) -> Balance { + let exec_gas = self.cfg().fee(ActionCosts::new_action_receipt).exec_fee() + + self.cfg().fee(ActionCosts::stake).exec_fee(); + let send_gas = self.cfg().fee(ActionCosts::new_action_receipt).send_fee(true) + + self.cfg().fee(ActionCosts::stake).send_fee(true); + self.gas_to_balance(exec_gas + send_gas) + } + + pub fn add_key_cost(&self, num_bytes: u64) -> Balance { + let exec_gas = self.cfg().fee(ActionCosts::new_action_receipt).exec_fee() + + self.cfg().fee(ActionCosts::add_function_call_key_base).exec_fee() + + num_bytes * self.cfg().fee(ActionCosts::add_function_call_key_byte).exec_fee(); + let send_gas = self.cfg().fee(ActionCosts::new_action_receipt).send_fee(true) + + self.cfg().fee(ActionCosts::add_function_call_key_base).send_fee(true) + + num_bytes * self.cfg().fee(ActionCosts::add_function_call_key_byte).send_fee(true); + self.gas_to_balance(exec_gas + send_gas) + } + + pub fn add_key_full_cost(&self) -> Balance { + let exec_gas = self.cfg().fee(ActionCosts::new_action_receipt).exec_fee() + + self.cfg().fee(ActionCosts::add_full_access_key).exec_fee(); + let send_gas = self.cfg().fee(ActionCosts::new_action_receipt).send_fee(true) + + self.cfg().fee(ActionCosts::add_full_access_key).send_fee(true); + self.gas_to_balance(exec_gas + send_gas) + } + + pub fn delete_key_cost(&self) -> Balance { + let exec_gas = self.cfg().fee(ActionCosts::new_action_receipt).exec_fee() + + self.cfg().fee(ActionCosts::delete_key).exec_fee(); + let send_gas = self.cfg().fee(ActionCosts::new_action_receipt).send_fee(true) + + self.cfg().fee(ActionCosts::delete_key).send_fee(true); + self.gas_to_balance(exec_gas + send_gas) + } + + pub fn prepaid_delete_account_cost(&self) -> Balance { + let exec_gas = self.cfg().fee(ActionCosts::new_action_receipt).exec_fee() + + self.cfg().fee(ActionCosts::delete_account).exec_fee(); + let send_gas = self.cfg().fee(ActionCosts::new_action_receipt).send_fee(false) + + self.cfg().fee(ActionCosts::delete_account).send_fee(false); + + let total_fee = exec_gas + send_gas; + + self.gas_to_balance(total_fee) + } + + /// The additional cost to execute a list of actions in a meta transaction, + /// compared to executing them directly. + /// + /// The overhead consists of: + /// - The base cost of the delegate action (send and exec). + /// - The additional send cost for all inner actions. + pub fn meta_tx_overhead_cost(&self, actions: &[Action], receiver: &AccountId) -> Balance { + // for tests, we assume sender != receiver + let sir = false; + let base = self.cfg().fee(ActionCosts::delegate); + let receipt = self.cfg().fee(ActionCosts::new_action_receipt); + let total_gas = base.exec_fee() + + base.send_fee(sir) + + receipt.send_fee(sir) + + node_runtime::config::total_send_fees(&self.rt_cfg, sir, actions, receiver).unwrap(); + self.gas_to_balance(total_gas) + } +} diff --git a/test-utils/testlib/src/lib.rs b/test-utils/testlib/src/lib.rs new file mode 100644 index 000000000..563213e02 --- /dev/null +++ b/test-utils/testlib/src/lib.rs @@ -0,0 +1,3 @@ +pub mod fees_utils; +pub mod process_blocks; +pub mod runtime_utils; diff --git a/test-utils/testlib/src/process_blocks.rs b/test-utils/testlib/src/process_blocks.rs new file mode 100644 index 000000000..641e5898c --- /dev/null +++ b/test-utils/testlib/src/process_blocks.rs @@ -0,0 +1,72 @@ +use unc_chain::{Block, BlockHeader}; +use unc_primitives::test_utils::create_test_signer; +use std::sync::Arc; + +pub fn set_no_chunk_in_block(block: &mut Block, prev_block: &Block) { + let chunk_headers = vec![prev_block.chunks()[0].clone()]; + let mut balance_burnt = 0; + for chunk in block.chunks().iter() { + if chunk.height_included() == block.header().height() { + balance_burnt += chunk.prev_balance_burnt(); + } + } + block.set_chunks(chunk_headers.clone()); + let block_body_hash = block.compute_block_body_hash(); + match block.mut_header() { + BlockHeader::BlockHeaderV1(header) => { + let header = Arc::make_mut(header); + header.inner_rest.chunk_headers_root = + Block::compute_chunk_headers_root(&chunk_headers).0; + header.inner_rest.chunk_tx_root = Block::compute_chunk_tx_root(&chunk_headers); + header.inner_rest.prev_chunk_outgoing_receipts_root = + Block::compute_chunk_prev_outgoing_receipts_root(&chunk_headers); + header.inner_lite.prev_state_root = Block::compute_state_root(&chunk_headers); + header.inner_lite.prev_outcome_root = Block::compute_outcome_root(&chunk_headers); + header.inner_rest.chunk_mask = vec![false]; + header.inner_rest.next_gas_price = prev_block.header().next_gas_price(); + header.inner_rest.total_supply += balance_burnt; + } + BlockHeader::BlockHeaderV2(header) => { + let header = Arc::make_mut(header); + header.inner_rest.chunk_headers_root = + Block::compute_chunk_headers_root(&chunk_headers).0; + header.inner_rest.chunk_tx_root = Block::compute_chunk_tx_root(&chunk_headers); + header.inner_rest.prev_chunk_outgoing_receipts_root = + Block::compute_chunk_prev_outgoing_receipts_root(&chunk_headers); + header.inner_lite.prev_state_root = Block::compute_state_root(&chunk_headers); + header.inner_lite.prev_outcome_root = Block::compute_outcome_root(&chunk_headers); + header.inner_rest.chunk_mask = vec![false]; + header.inner_rest.next_gas_price = prev_block.header().next_gas_price(); + header.inner_rest.total_supply += balance_burnt; + } + BlockHeader::BlockHeaderV3(header) => { + let header = Arc::make_mut(header); + header.inner_rest.chunk_headers_root = + Block::compute_chunk_headers_root(&chunk_headers).0; + header.inner_rest.chunk_tx_root = Block::compute_chunk_tx_root(&chunk_headers); + header.inner_rest.prev_chunk_outgoing_receipts_root = + Block::compute_chunk_prev_outgoing_receipts_root(&chunk_headers); + header.inner_lite.prev_state_root = Block::compute_state_root(&chunk_headers); + header.inner_lite.prev_outcome_root = Block::compute_outcome_root(&chunk_headers); + header.inner_rest.chunk_mask = vec![false]; + header.inner_rest.next_gas_price = prev_block.header().next_gas_price(); + header.inner_rest.total_supply += balance_burnt; + } + BlockHeader::BlockHeaderV4(header) => { + let header = Arc::make_mut(header); + header.inner_rest.chunk_headers_root = + Block::compute_chunk_headers_root(&chunk_headers).0; + header.inner_rest.chunk_tx_root = Block::compute_chunk_tx_root(&chunk_headers); + header.inner_rest.prev_chunk_outgoing_receipts_root = + Block::compute_chunk_prev_outgoing_receipts_root(&chunk_headers); + header.inner_lite.prev_state_root = Block::compute_state_root(&chunk_headers); + header.inner_lite.prev_outcome_root = Block::compute_outcome_root(&chunk_headers); + header.inner_rest.chunk_mask = vec![false]; + header.inner_rest.next_gas_price = prev_block.header().next_gas_price(); + header.inner_rest.total_supply += balance_burnt; + header.inner_rest.block_body_hash = block_body_hash.unwrap(); + } + } + let validator_signer = create_test_signer("test0"); + block.mut_header().resign(&validator_signer); +} diff --git a/test-utils/testlib/src/runtime_utils.rs b/test-utils/testlib/src/runtime_utils.rs new file mode 100644 index 000000000..776be55e4 --- /dev/null +++ b/test-utils/testlib/src/runtime_utils.rs @@ -0,0 +1,69 @@ +use unc_chain_configs::Genesis; +use unc_crypto::PublicKey; +use unc_primitives::account::{AccessKey, Account}; +use unc_primitives::hash::hash; +use unc_primitives::state_record::StateRecord; +use unc_primitives::types::{AccountId, Balance}; + +pub fn alice_account() -> AccountId { + "alice.near".parse().unwrap() +} +pub fn bob_account() -> AccountId { + "bob.near".parse().unwrap() +} +pub fn carol_account() -> AccountId { + "carol.near".parse().unwrap() +} +pub fn eve_dot_alice_account() -> AccountId { + "eve.alice.near".parse().unwrap() +} + +pub fn x_dot_y_dot_alice_account() -> AccountId { + "x.y.alice.near".parse().unwrap() +} + +/// Pre-deploy in genesis the standard test contract for a given account. +/// +/// This contract contains various functions useful for testing and its code is available in +/// `/home/jakmeier/near/core-runtime/framework/runtime/unc-test-contracts/test-contract-rs/src/lib.rs` +pub fn add_test_contract(genesis: &mut Genesis, account_id: &AccountId) { + add_contract(genesis, account_id, unc_test_contracts::rs_contract().to_vec()) +} + +/// Pre-deploy in genesis any contract for a given account. +pub fn add_contract(genesis: &mut Genesis, account_id: &AccountId, code: Vec) { + let mut is_account_record_found = false; + let hash = hash(&code); + let records = genesis.force_read_records().as_mut(); + for record in records.iter_mut() { + if let StateRecord::Account { account_id: record_account_id, ref mut account } = record { + if record_account_id == account_id { + is_account_record_found = true; + account.set_code_hash(hash); + } + } + } + if !is_account_record_found { + records.push(StateRecord::Account { + account_id: account_id.clone(), + account: Account::new(0, 0,0, hash, 0), + }); + } + records.push(StateRecord::Contract { account_id: account_id.clone(), code }); +} + +/// Add an account with a specified access key & balance to the genesis state records. +pub fn add_account_with_access_key( + genesis: &mut Genesis, + account_id: AccountId, + balance: Balance, + public_key: PublicKey, + access_key: AccessKey, +) { + let records = genesis.force_read_records().as_mut(); + records.push(StateRecord::Account { + account_id: account_id.clone(), + account: Account::new(balance, 0, 0, Default::default(), 0), + }); + records.push(StateRecord::AccessKey { account_id, public_key, access_key }); +} diff --git a/tools/amend-genesis/Cargo.toml b/tools/amend-genesis/Cargo.toml new file mode 100644 index 000000000..b909ae89a --- /dev/null +++ b/tools/amend-genesis/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "unc-amend-genesis" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +borsh.workspace = true +clap.workspace = true +num-rational.workspace = true +serde.workspace = true +serde_json.workspace = true +tempfile.workspace = true +tracing.workspace = true + +unc-chain.workspace = true +unc-chain-configs.workspace = true +unc-crypto.workspace = true +unc-epoch-manager.workspace = true +unc-network.workspace = true +unc-primitives.workspace = true +unc-primitives-core.workspace = true +unc-store.workspace = true +unc-test-contracts.workspace = true +framework.workspace = true +node-runtime.workspace = true diff --git a/tools/amend-genesis/src/cli.rs b/tools/amend-genesis/src/cli.rs new file mode 100644 index 000000000..53f5b8e83 --- /dev/null +++ b/tools/amend-genesis/src/cli.rs @@ -0,0 +1,106 @@ +use unc_primitives::types::NumBlocks; +use unc_primitives::types::{BlockHeightDelta, NumSeats}; +use unc_primitives::version::ProtocolVersion; +use num_rational::Rational32; +use std::path::PathBuf; + +/// Amend a genesis/records file created by `dump-state`. +#[derive(clap::Parser)] +pub struct AmendGenesisCommand { + /// path to the input genesis file + #[clap(long)] + genesis_file_in: PathBuf, + /// path to the output genesis file + #[clap(long)] + genesis_file_out: PathBuf, + /// path to the input records file. Note that right now this must be provided, and + /// this command will not work with a genesis file that itself contains the records + #[clap(long)] + records_file_in: PathBuf, + /// path to the output records file + #[clap(long)] + records_file_out: PathBuf, + /// path to a JSON list of AccountInfos representing the validators to put in the + /// output genesis state. These are JSON maps of the form + /// { + /// "account_id": , + /// "public_key": , + /// "amount": , + /// } + #[clap(long)] + validators: PathBuf, + /// path to extra records to add to the output state. Right now only Accounts and AccessKey + /// records are supported, and any added accounts must have zero `code_hash` + #[clap(long)] + extra_records: Option, + /// chain ID to set on the output genesis + #[clap(long)] + chain_id: Option, + /// protocol version to set on the output genesis + #[clap(long)] + protocol_version: Option, + /// num_seats to set in the output genesis file + #[clap(long)] + num_seats: Option, + /// epoch length to set in the output genesis file + #[clap(long)] + epoch_length: Option, + /// transaction_validity_period to set in the output genesis file + #[clap(long)] + transaction_validity_period: Option, + /// block_producer_kickout_threshold to set in the output genesis file + #[clap(long)] + block_producer_kickout_threshold: Option, + /// chunk_producer_kickout_threshold to set in the output genesis file + #[clap(long)] + chunk_producer_kickout_threshold: Option, + /// protocol_reward_rate to set in the output genesis file. Give a ratio here (e.g. "1/10") + #[clap(long)] + protocol_reward_rate: Option, + /// optional file that should contain a JSON-serialized shard layout + #[clap(long)] + shard_layout_file: Option, + /// runtime fees config `num_bytes_account` value. Used to initialize the `storage_usage` field + /// on accounts in the output state + #[clap(long)] + num_bytes_account: Option, + /// runtime fees config `num_extra_bytes_record` value. Used to initialize the `storage_usage` field + /// on accounts in the output state + #[clap(long)] + num_extra_bytes_record: Option, + /// min_gas_price to set in the output genesis file + #[clap(long)] + min_gas_price: Option, + /// max_gas_price to set in the output genesis file + #[clap(long)] + max_gas_price: Option, +} + +impl AmendGenesisCommand { + pub fn run(self) -> anyhow::Result<()> { + let genesis_changes = crate::GenesisChanges { + chain_id: self.chain_id, + protocol_version: self.protocol_version, + num_seats: self.num_seats, + epoch_length: self.epoch_length, + transaction_validity_period: self.transaction_validity_period, + protocol_reward_rate: self.protocol_reward_rate, + block_producer_kickout_threshold: self.block_producer_kickout_threshold, + chunk_producer_kickout_threshold: self.chunk_producer_kickout_threshold, + min_gas_price: self.min_gas_price, + max_gas_price: self.max_gas_price, + }; + crate::amend_genesis( + &self.genesis_file_in, + &self.genesis_file_out, + &self.records_file_in, + &self.records_file_out, + self.extra_records.as_deref(), + &self.validators, + self.shard_layout_file.as_deref(), + &genesis_changes, + self.num_bytes_account.unwrap_or(100), + self.num_extra_bytes_record.unwrap_or(40), + ) + } +} diff --git a/tools/amend-genesis/src/lib.rs b/tools/amend-genesis/src/lib.rs new file mode 100644 index 000000000..fcbfcf42f --- /dev/null +++ b/tools/amend-genesis/src/lib.rs @@ -0,0 +1,1042 @@ +use anyhow::Context; + +use unc_chain_configs::{Genesis, GenesisValidationMode}; +use unc_crypto::PublicKey; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardLayout; +use unc_primitives::state_record::StateRecord; +use unc_primitives::types::{AccountId, AccountInfo}; +use unc_primitives::utils; +use unc_primitives::version::ProtocolVersion; +use unc_primitives_core::account::{AccessKey, Account}; +use unc_primitives_core::types::{Balance, BlockHeightDelta, NumBlocks, NumSeats, NumShards, Power}; +use num_rational::Rational32; +use serde::ser::{SerializeSeq, Serializer}; +use std::collections::{hash_map, HashMap}; +use std::fs::File; +use std::io::{BufReader, BufWriter}; +use std::path::Path; + +mod cli; + +pub use cli::AmendGenesisCommand; + +// while parsing the --extra-records file we will keep track of the records we see for each +// account here, and then at the end figure out what to put in the storage_usage field +#[derive(Debug, Default)] +struct AccountRecords { + account: Option, + // when we parse the validators file, we will set the balance in the account to 0 + // and set this to true so we remember later to set some default value, and if we + // end up seeing the account listed in the input records file, we'll use the total + // given there + amount_needed: bool, + keys: HashMap, + // code state records must appear after the account state record. So for accounts we're + // modifying/adding keys for, we will remember any code records (there really should only be one), + // and add them to the output only after we write the account record + extra_records: Vec, +} + +// set the total balance to what's in src, keeping the locked amount the same +fn set_total_balance(dst: &mut Account, src: &Account) { + let total = src.amount() + src.locked(); + if total > dst.locked() { + dst.set_amount(total - dst.locked()); + } +} + +impl AccountRecords { + fn new(amount: Balance, locked: Balance, power: Power, num_bytes_account: u64) -> Self { + let mut ret = Self::default(); + ret.set_account(amount, locked, power, num_bytes_account); + ret + } + + fn new_validator(amount: Balance, power: Power, frozen: Balance, num_bytes_account: u64) -> Self { + let mut ret = Self::default(); + ret.set_account(amount, frozen, power, num_bytes_account); + ret.amount_needed = true; + ret + } + + fn set_account(&mut self, amount: Balance, locked: Balance, power: Power, num_bytes_account: u64) { + assert!(self.account.is_none()); + let account = Account::new(amount, locked, power, CryptoHash::default(), num_bytes_account); + self.account = Some(account); + } + + fn update_from_existing(&mut self, existing: &Account) { + match &mut self.account { + Some(account) => { + // an account added in extra_records (or one of the validators) also exists in the original + // records. Set the storage usage to reflect whatever's in the original records, and at the + // end we will add to the storage usage with any extra keys added for this account + account.set_storage_usage(existing.storage_usage()); + account.set_code_hash(existing.code_hash()); + account.set_power(existing.power()); + if self.amount_needed { + set_total_balance(account, existing); + } + } + None => { + let mut account = existing.clone(); + account.set_amount(account.amount() + account.locked()); + account.set_locked(0); + account.set_power(0); + self.account = Some(account); + } + } + self.amount_needed = false; + } + + fn push_extra_record(&mut self, record: StateRecord) { + self.extra_records.push(record); + } + + fn write_out( + self, + account_id: AccountId, + seq: &mut S, + total_supply: &mut Balance, + num_extra_bytes_record: u64, + ) -> anyhow::Result<()> + where + ::Error: Send + Sync + 'static, + { + match self.account { + Some(mut account) => { + for (public_key, access_key) in self.keys { + let storage_usage = account.storage_usage() + + public_key.len() as u64 + + borsh::object_length(&access_key).unwrap() as u64 + + num_extra_bytes_record; + account.set_storage_usage(storage_usage); + + seq.serialize_element(&StateRecord::AccessKey { + account_id: account_id.clone(), + public_key, + access_key, + })?; + } + if self.amount_needed { + account.set_amount(10_000 * framework::config::UNC_BASE); + } + *total_supply += account.amount() + account.locked(); + seq.serialize_element(&StateRecord::Account { account_id, account })?; + for record in self.extra_records.iter() { + seq.serialize_element(record)?; + } + } + None => { + tracing::warn!("access keys for {} were included in --extra-records, but no Account record was found. Not adding them to the output", &account_id); + } + } + Ok(()) + } +} + +fn validator_records( + validators: &[AccountInfo], + num_bytes_account: u64, +) -> anyhow::Result> { + let mut records = HashMap::new(); + for AccountInfo { account_id, public_key, amount, power, locked } in validators.iter() { + let mut r = AccountRecords::new_validator(*amount, *power, *locked, num_bytes_account); + r.keys.insert(public_key.clone(), AccessKey::full_access()); + if records.insert(account_id.clone(), r).is_some() { + anyhow::bail!("validator {} specified twice", account_id); + } + } + Ok(records) +} + +fn parse_validators(path: &Path) -> anyhow::Result> { + let validators = std::fs::read_to_string(path) + .with_context(|| format!("failed reading from {}", path.display()))?; + let validators = serde_json::from_str(&validators) + .with_context(|| format!("failed deserializing from {}", path.display()))?; + Ok(validators) +} + +fn parse_extra_records( + records_file: &Path, + num_bytes_account: u64, +) -> anyhow::Result> { + let reader = + BufReader::new(File::open(records_file).with_context(|| { + format!("Failed opening validators file {}", records_file.display()) + })?); + let mut records = HashMap::new(); + + let mut result = Ok(()); + unc_chain_configs::stream_records_from_file(reader, |r| { + match r { + StateRecord::Account { account_id, account } => { + if account.code_hash() != CryptoHash::default() { + result = Err(anyhow::anyhow!( + "FIXME: accounts in --extra-records with code_hash set not supported" + )); + } + match records.entry(account_id.clone()) { + hash_map::Entry::Vacant(e) => { + let r = AccountRecords::new( + account.amount(), + account.locked(), + account.power(), + num_bytes_account, + ); + e.insert(r); + } + hash_map::Entry::Occupied(mut e) => { + let r = e.get_mut(); + + if r.account.is_some() { + result = Err(anyhow::anyhow!( + "account {} given twice in extra records", + &account_id + )); + } + r.set_account(account.amount(), account.locked(), account.power(), num_bytes_account); + } + } + } + StateRecord::AccessKey { account_id, public_key, access_key } => { + records.entry(account_id).or_default().keys.insert(public_key, access_key); + } + _ => { + result = Err(anyhow::anyhow!( + "FIXME: only Account and AccessKey records are supported in --extra-records" + )); + } + }; + }) + .context("Failed deserializing records from --extra-records")?; + + Ok(records) +} + +fn wanted_records( + validators: &[AccountInfo], + extra_records: Option<&Path>, + num_bytes_account: u64, +) -> anyhow::Result> { + let mut records = validator_records(validators, num_bytes_account)?; + + if let Some(path) = extra_records { + let extra = parse_extra_records(path, num_bytes_account)?; + + for (account_id, account_records) in extra { + match records.entry(account_id) { + hash_map::Entry::Occupied(mut e) => { + let validator_records = e.get_mut(); + + if let Some(account) = &account_records.account { + set_total_balance(validator_records.account.as_mut().unwrap(), account); + validator_records.amount_needed = false; + } + validator_records.keys.extend(account_records.keys); + } + hash_map::Entry::Vacant(e) => { + e.insert(account_records); + } + } + } + } + + Ok(records) +} + +#[derive(Default)] +pub struct GenesisChanges { + pub chain_id: Option, + pub protocol_version: Option, + pub num_seats: Option, + pub epoch_length: Option, + pub transaction_validity_period: Option, + pub protocol_reward_rate: Option, + pub block_producer_kickout_threshold: Option, + pub chunk_producer_kickout_threshold: Option, + pub min_gas_price: Option, + pub max_gas_price: Option, +} + +/// Amend a genesis/records file created by `dump-state`. +pub fn amend_genesis( + genesis_file_in: &Path, + genesis_file_out: &Path, + records_file_in: &Path, + records_file_out: &Path, + extra_records: Option<&Path>, + validators: &Path, + shard_layout_file: Option<&Path>, + genesis_changes: &GenesisChanges, + num_bytes_account: u64, + num_extra_bytes_record: u64, +) -> anyhow::Result<()> { + let mut genesis = Genesis::from_file(genesis_file_in, GenesisValidationMode::UnsafeFast)?; + + let shard_layout = if let Some(path) = shard_layout_file { + let s = std::fs::read_to_string(path) + .with_context(|| format!("failed reading shard layout file {}", path.display()))?; + Some( + serde_json::from_str::(&s) + .context("failed deserializing --shard-layout-file")?, + ) + } else { + None + }; + + let reader = BufReader::new(File::open(records_file_in).with_context(|| { + format!("Failed opening input records file {}", records_file_in.display()) + })?); + let records_out = BufWriter::new(File::create(records_file_out).with_context(|| { + format!("Failed opening output records file {}", records_file_out.display()) + })?); + let mut records_ser = serde_json::Serializer::new(records_out); + let mut records_seq = records_ser.serialize_seq(None).unwrap(); + + let validators = parse_validators(validators)?; + let mut wanted = wanted_records(&validators, extra_records, num_bytes_account)?; + let mut total_supply = 0; + + unc_chain_configs::stream_records_from_file(reader, |mut r| { + match &mut r { + StateRecord::AccessKey { account_id, public_key, access_key } => { + if let Some(a) = wanted.get_mut(account_id) { + if let Some(a) = a.keys.remove(public_key) { + *access_key = a; + } + } + records_seq.serialize_element(&r).unwrap(); + } + StateRecord::Account { account_id, account } => { + if let Some(acc) = wanted.get_mut(account_id) { + acc.update_from_existing(account); + } else { + if account.locked() != 0 { + account.set_amount(account.amount() + account.locked()); + account.set_locked(0); + } + total_supply += account.amount() + account.locked(); + records_seq.serialize_element(&r).unwrap(); + } + } + StateRecord::Contract { account_id, .. } => { + if let Some(records) = wanted.get_mut(account_id) { + records.push_extra_record(r); + } else { + records_seq.serialize_element(&r).unwrap(); + } + } + _ => { + records_seq.serialize_element(&r).unwrap(); + } + }; + })?; + + for (account_id, records) in wanted { + records.write_out( + account_id, + &mut records_seq, + &mut total_supply, + num_extra_bytes_record, + )?; + } + + genesis.config.total_supply = total_supply; + // TODO: give an option to set this + genesis.config.num_block_producer_seats = validators.len() as NumSeats; + // here we have already checked that there are no duplicate validators in wanted_records() + genesis.config.validators = validators; + if let Some(chain_id) = &genesis_changes.chain_id { + genesis.config.chain_id = chain_id.clone(); + } + if let Some(n) = genesis_changes.num_seats { + genesis.config.num_block_producer_seats = n; + } + if let Some(shard_layout) = shard_layout { + genesis.config.avg_hidden_validator_seats_per_shard = + shard_layout.shard_ids().into_iter().map(|_| 0).collect(); + genesis.config.num_block_producer_seats_per_shard = utils::get_num_seats_per_shard( + shard_layout.shard_ids().count() as NumShards, + genesis.config.num_block_producer_seats, + ); + genesis.config.shard_layout = shard_layout; + } + if let Some(v) = genesis_changes.protocol_version { + genesis.config.protocol_version = v; + } + if let Some(l) = genesis_changes.epoch_length { + genesis.config.epoch_length = l; + } + if let Some(t) = genesis_changes.transaction_validity_period { + genesis.config.transaction_validity_period = t; + } + if let Some(r) = genesis_changes.protocol_reward_rate { + genesis.config.protocol_reward_rate = r; + } + if let Some(t) = genesis_changes.block_producer_kickout_threshold { + genesis.config.block_producer_kickout_threshold = t; + } + if let Some(t) = genesis_changes.chunk_producer_kickout_threshold { + genesis.config.chunk_producer_kickout_threshold = t; + } + if let Some(p) = genesis_changes.min_gas_price { + genesis.config.min_gas_price = p; + } + if let Some(p) = genesis_changes.max_gas_price { + genesis.config.max_gas_price = p; + } + genesis.to_file(genesis_file_out); + records_seq.end()?; + Ok(()) +} + +#[cfg(test)] +mod test { + use anyhow::Context; + use unc_chain_configs::{get_initial_supply, Genesis, GenesisConfig}; + use unc_primitives::hash::CryptoHash; + use unc_primitives::shard_layout::ShardLayout; + use unc_primitives::state_record::StateRecord; + use unc_primitives::static_clock::StaticClock; + use unc_primitives::types::{AccountId, AccountInfo}; + use unc_primitives::utils; + use unc_primitives::version::PROTOCOL_VERSION; + use unc_primitives_core::account::{AccessKey, Account}; + use unc_primitives_core::types::{Balance, StorageUsage}; + use num_rational::Rational32; + use std::collections::{HashMap, HashSet}; + use std::str::FromStr; + use tempfile::NamedTempFile; + + // these (TestAccountInfo, TestStateRecord, and ParsedTestCase) are here so we can + // have all static data in the testcases below + struct TestAccountInfo { + account_id: &'static str, + public_key: &'static str, + amount: Balance, + } + + impl TestAccountInfo { + fn parse(&self) -> AccountInfo { + AccountInfo { + account_id: self.account_id.parse().unwrap(), + public_key: self.public_key.parse().unwrap(), + amount: self.amount, + } + } + } + + enum TestStateRecord { + Account { + account_id: &'static str, + amount: Balance, + locked: Balance, + /// Storage used by the given account, includes account id, this struct, access keys and other data. + storage_usage: StorageUsage, + }, + AccessKey { + account_id: &'static str, + public_key: &'static str, + }, + Contract { + account_id: &'static str, + }, + } + + impl TestStateRecord { + fn parse(&self) -> StateRecord { + match &self { + Self::Account { account_id, amount, locked, storage_usage } => { + let account = + Account::new(*amount, *locked, CryptoHash::default(), *storage_usage); + StateRecord::Account { account_id: account_id.parse().unwrap(), account } + } + Self::AccessKey { account_id, public_key } => StateRecord::AccessKey { + account_id: account_id.parse().unwrap(), + public_key: public_key.parse().unwrap(), + access_key: AccessKey::full_access(), + }, + Self::Contract { account_id } => StateRecord::Contract { + account_id: account_id.parse().unwrap(), + code: vec![123], + }, + } + } + } + + struct ParsedTestCase { + genesis: Genesis, + records_file_in: NamedTempFile, + validators_in: Vec, + extra_records: Vec, + wanted_records: Vec, + } + + struct TestCase { + // for convenience, the validators set in the initial genesis file, matching + // the accounts in records_in with nonzero `locked` + initial_validators: &'static [TestAccountInfo], + // records to put in the --records-file-in file + records_in: &'static [TestStateRecord], + // account infos to put in the --validators file + validators_in: &'static [TestAccountInfo], + // records to put in the --extra-records file + extra_records: &'static [TestStateRecord], + // the records we want to appear in the output + wanted_records: &'static [TestStateRecord], + } + + fn compare_records( + got_records: Vec, + wanted_records: Vec, + ) -> anyhow::Result<()> { + let mut got_accounts = HashMap::new(); + let mut got_keys = HashSet::new(); + let mut got_contracts = HashMap::::new(); + let mut wanted_accounts = HashMap::new(); + let mut wanted_keys = HashSet::new(); + let mut wanted_contracts = HashMap::::new(); + + for r in got_records { + match r { + StateRecord::Account { account_id, account } => { + if got_accounts + .insert( + account_id.clone(), + ( + account.amount(), + account.locked(), + account.code_hash(), + account.storage_usage(), + ), + ) + .is_some() + { + anyhow::bail!("two account records in the output for {}", &account_id); + } + } + StateRecord::AccessKey { account_id, public_key, access_key } => { + if !got_keys.insert((account_id.clone(), public_key.clone(), access_key)) { + anyhow::bail!( + "two access key records in the output for {}, {}", + &account_id, + &public_key + ); + } + } + StateRecord::Contract { account_id, .. } => { + if !got_accounts.contains_key(&account_id) { + anyhow::bail!( + "account {} has a code state record before the account state record", + &account_id + ); + } + *got_contracts.entry(account_id).or_default() += 1; + } + _ => anyhow::bail!("got an unexpected record in the output: {}", r), + }; + } + for r in wanted_records { + match r { + StateRecord::Account { account_id, account } => { + wanted_accounts.insert( + account_id, + ( + account.amount(), + account.locked(), + account.code_hash(), + account.storage_usage(), + ), + ); + } + StateRecord::AccessKey { account_id, public_key, access_key } => { + wanted_keys.insert((account_id, public_key, access_key)); + } + StateRecord::Contract { account_id, .. } => { + *wanted_contracts.entry(account_id).or_default() += 1; + } + _ => anyhow::bail!("got an unexpected record in the output: {}", r), + }; + } + + assert_eq!(got_accounts, wanted_accounts); + assert_eq!(got_keys, wanted_keys); + assert_eq!(got_contracts, wanted_contracts); + Ok(()) + } + + impl TestCase { + fn parse(&self) -> anyhow::Result { + let initial_validators = self.initial_validators.iter().map(|v| v.parse()).collect(); + let records_in: Vec<_> = self.records_in.iter().map(|r| r.parse()).collect(); + + let num_shards = 4; + let shards = ShardLayout::v1( + (0..num_shards - 1) + .map(|f| AccountId::from_str(format!("shard{}.test.near", f).as_str()).unwrap()) + .collect(), + None, + 1, + ); + + let genesis_config = GenesisConfig { + protocol_version: PROTOCOL_VERSION, + genesis_time: StaticClock::utc(), + chain_id: "rusttestnet".to_string(), + genesis_height: 0, + num_block_producer_seats: framework::config::NUM_BLOCK_PRODUCER_SEATS, + num_block_producer_seats_per_shard: utils::get_num_seats_per_shard( + num_shards, + framework::config::NUM_BLOCK_PRODUCER_SEATS, + ), + avg_hidden_validator_seats_per_shard: (0..num_shards).map(|_| 0).collect(), + dynamic_resharding: false, + protocol_upgrade_stake_threshold: + framework::config::PROTOCOL_UPGRADE_STAKE_THRESHOLD, + epoch_length: 1000, + gas_limit: framework::config::INITIAL_GAS_LIMIT, + gas_price_adjustment_rate: framework::config::GAS_PRICE_ADJUSTMENT_RATE, + block_producer_kickout_threshold: + framework::config::BLOCK_PRODUCER_KICKOUT_THRESHOLD, + chunk_producer_kickout_threshold: + framework::config::CHUNK_PRODUCER_KICKOUT_THRESHOLD, + online_max_threshold: Rational32::new(99, 100), + online_min_threshold: Rational32::new( + framework::config::BLOCK_PRODUCER_KICKOUT_THRESHOLD as i32, + 100, + ), + validators: initial_validators, + transaction_validity_period: framework::config::TRANSACTION_VALIDITY_PERIOD, + protocol_reward_rate: framework::config::PROTOCOL_REWARD_RATE, + max_inflation_rate: framework::config::MAX_INFLATION_RATE, + total_supply: get_initial_supply(&records_in), + num_blocks_per_year: framework::config::NUM_BLOCKS_PER_YEAR, + protocol_treasury_account: "treasury.near".parse().unwrap(), + fishermen_threshold: framework::config::FISHERMEN_THRESHOLD, + shard_layout: shards, + min_gas_price: framework::config::MIN_GAS_PRICE, + ..Default::default() + }; + + let mut records_file_in = + tempfile::NamedTempFile::new().context("failed creating tmp file")?; + serde_json::to_writer(&mut records_file_in, &records_in) + .context("failed writing to --records-file-in")?; + let genesis = Genesis::new_with_path(genesis_config, records_file_in.path())?; + + Ok(ParsedTestCase { + genesis, + records_file_in, + validators_in: self.validators_in.iter().map(|v| v.parse()).collect(), + extra_records: self.extra_records.iter().map(|r| r.parse()).collect(), + wanted_records: self.wanted_records.iter().map(|r| r.parse()).collect(), + }) + } + + // take the records in the test case and write them to temp files, and then call amend_genesis() and + // check that the resulting genesis and records files match what's in self.want_records + // right now we aren't testing that other kinds of records appearing in the input records file + // will make it into the output, but that part is pretty simple + fn run(&self) -> anyhow::Result<()> { + let ParsedTestCase { + genesis, + records_file_in, + validators_in, + extra_records, + wanted_records, + } = self.parse()?; + + let mut genesis_file_in = + tempfile::NamedTempFile::new().context("failed creating tmp file")?; + let mut validators_file = + tempfile::NamedTempFile::new().context("failed creating tmp file")?; + let mut extra_records_file = + tempfile::NamedTempFile::new().context("failed creating tmp file")?; + let genesis_file_out = + tempfile::NamedTempFile::new().context("failed creating tmp file")?; + let records_file_out = + tempfile::NamedTempFile::new().context("failed creating tmp file")?; + + serde_json::to_writer(&mut validators_file, &validators_in) + .context("failed writing to --validators")?; + serde_json::to_writer(&mut extra_records_file, &extra_records) + .context("failed writing to --extra-records")?; + serde_json::to_writer(&mut genesis_file_in, &genesis) + .context("failed writing to --genesis-file-in")?; + + crate::amend_genesis( + genesis_file_in.path(), + genesis_file_out.path(), + records_file_in.path(), + records_file_out.path(), + Some(extra_records_file.path()), + validators_file.path(), + None, + &crate::GenesisChanges::default(), + 100, + 40, + ) + .context("amend_genesis() failed")?; + + let got_records = std::fs::read_to_string(records_file_out.path()) + .context("failed reading from --records-file-out")?; + let got_records: Vec = serde_json::from_str(&got_records) + .context("failed deserializing --records-file-out")?; + + compare_records(got_records, wanted_records) + } + } + + static TEST_CASES: &[TestCase] = &[ + // first one adds one validator (foo2), bumps up another's balance (foo0), and adds an extra account (extra-account.near) + TestCase { + initial_validators: &[ + TestAccountInfo { + account_id: "foo0", + public_key: "ed25519:He7QeRuwizNEhBioYG3u4DZ8jWXyETiyNzFD3MkTjDMf", + amount: 1_000_000, + }, + TestAccountInfo { + account_id: "foo1", + public_key: "ed25519:FXXrTXiKWpXj1R6r5fBvMLpstd8gPyrBq3qMByqKVzKF", + amount: 2_000_000, + }, + ], + records_in: &[ + TestStateRecord::Account { + account_id: "foo0", + amount: 1_000_000, + locked: 1_000_000, + storage_usage: 182, + }, + TestStateRecord::AccessKey { + account_id: "foo0", + public_key: "ed25519:He7QeRuwizNEhBioYG3u4DZ8jWXyETiyNzFD3MkTjDMf", + }, + TestStateRecord::Account { + account_id: "foo1", + amount: 1_000_000, + locked: 2_000_000, + storage_usage: 182, + }, + TestStateRecord::AccessKey { + account_id: "foo1", + public_key: "ed25519:FXXrTXiKWpXj1R6r5fBvMLpstd8gPyrBq3qMByqKVzKF", + }, + TestStateRecord::Account { + account_id: "asdf.near", + amount: 1_234_000, + locked: 0, + storage_usage: 182, + }, + TestStateRecord::AccessKey { + account_id: "asdf.near", + public_key: "ed25519:5C66RSJgwK17Yb6VtTbgBCFHDRPzGUd6AAhFdXNvmJuo", + }, + ], + validators_in: &[ + TestAccountInfo { + account_id: "foo0", + public_key: "ed25519:He7QeRuwizNEhBioYG3u4DZ8jWXyETiyNzFD3MkTjDMf", + amount: 1_000_000, + }, + TestAccountInfo { + account_id: "foo1", + public_key: "ed25519:FXXrTXiKWpXj1R6r5fBvMLpstd8gPyrBq3qMByqKVzKF", + amount: 2_000_000, + }, + TestAccountInfo { + account_id: "foo2", + public_key: "ed25519:Eo9W44tRMwcYcoua11yM7Xfr1DjgR4EWQFM3RU27MEX8", + amount: 3_000_000, + }, + ], + extra_records: &[ + TestStateRecord::Account { + account_id: "foo0", + amount: 100_000_000, + locked: 50_000_000, + storage_usage: 0, + }, + TestStateRecord::Account { + account_id: "extra-account.near", + amount: 9_000_000, + locked: 0, + storage_usage: 0, + }, + TestStateRecord::AccessKey { + account_id: "extra-account.near", + public_key: "ed25519:BhnQV3oJa8iSQDKDc8gy36TsenaMFmv7qHvcnutuXj33", + }, + ], + wanted_records: &[ + TestStateRecord::Account { + account_id: "foo0", + amount: 149_000_000, + locked: 1_000_000, + storage_usage: 182, + }, + TestStateRecord::AccessKey { + account_id: "foo0", + public_key: "ed25519:He7QeRuwizNEhBioYG3u4DZ8jWXyETiyNzFD3MkTjDMf", + }, + TestStateRecord::Account { + account_id: "foo1", + amount: 1_000_000, + locked: 2_000_000, + storage_usage: 182, + }, + TestStateRecord::AccessKey { + account_id: "foo1", + public_key: "ed25519:FXXrTXiKWpXj1R6r5fBvMLpstd8gPyrBq3qMByqKVzKF", + }, + TestStateRecord::Account { + account_id: "foo2", + amount: 10_000 * framework::config::UNC_BASE, + locked: 3_000_000, + storage_usage: 182, + }, + TestStateRecord::AccessKey { + account_id: "foo2", + public_key: "ed25519:Eo9W44tRMwcYcoua11yM7Xfr1DjgR4EWQFM3RU27MEX8", + }, + TestStateRecord::Account { + account_id: "asdf.near", + amount: 1_234_000, + locked: 0, + storage_usage: 182, + }, + TestStateRecord::AccessKey { + account_id: "asdf.near", + public_key: "ed25519:5C66RSJgwK17Yb6VtTbgBCFHDRPzGUd6AAhFdXNvmJuo", + }, + TestStateRecord::Account { + account_id: "extra-account.near", + amount: 9_000_000, + locked: 0, + storage_usage: 182, + }, + TestStateRecord::AccessKey { + account_id: "extra-account.near", + public_key: "ed25519:BhnQV3oJa8iSQDKDc8gy36TsenaMFmv7qHvcnutuXj33", + }, + ], + }, + // this one changes the validator set completely, and adds an extra accounts and keys + TestCase { + initial_validators: &[ + TestAccountInfo { + account_id: "foo0", + public_key: "ed25519:He7QeRuwizNEhBioYG3u4DZ8jWXyETiyNzFD3MkTjDMf", + amount: 1_000_000, + }, + TestAccountInfo { + account_id: "foo1", + public_key: "ed25519:FXXrTXiKWpXj1R6r5fBvMLpstd8gPyrBq3qMByqKVzKF", + amount: 2_000_000, + }, + ], + validators_in: &[ + TestAccountInfo { + account_id: "foo2", + public_key: "ed25519:He7QeRuwizNEhBioYG3u4DZ8jWXyETiyNzFD3MkTjDMf", + amount: 1_000_000, + }, + TestAccountInfo { + account_id: "foo3", + public_key: "ed25519:FXXrTXiKWpXj1R6r5fBvMLpstd8gPyrBq3qMByqKVzKF", + amount: 2_000_000, + }, + ], + records_in: &[ + TestStateRecord::Account { + account_id: "foo0", + amount: 1_000_000, + locked: 1_000_000, + storage_usage: 182, + }, + TestStateRecord::AccessKey { + account_id: "foo0", + public_key: "ed25519:He7QeRuwizNEhBioYG3u4DZ8jWXyETiyNzFD3MkTjDMf", + }, + TestStateRecord::Account { + account_id: "foo1", + amount: 1_000_000, + locked: 2_000_000, + storage_usage: 182, + }, + TestStateRecord::AccessKey { + account_id: "foo1", + public_key: "ed25519:FXXrTXiKWpXj1R6r5fBvMLpstd8gPyrBq3qMByqKVzKF", + }, + TestStateRecord::Account { + account_id: "asdf.near", + amount: 1_234_000, + locked: 0, + storage_usage: 182, + }, + TestStateRecord::AccessKey { + account_id: "asdf.near", + public_key: "ed25519:5C66RSJgwK17Yb6VtTbgBCFHDRPzGUd6AAhFdXNvmJuo", + }, + ], + extra_records: &[ + TestStateRecord::Account { + account_id: "foo0", + amount: 100_000_000, + locked: 0, + storage_usage: 0, + }, + TestStateRecord::Account { + account_id: "foo2", + amount: 300_000_000, + locked: 0, + storage_usage: 0, + }, + TestStateRecord::AccessKey { + account_id: "foo0", + public_key: "ed25519:FXXrTXiKWpXj1R6r5fBvMLpstd8gPyrBq3qMByqKVzKF", + }, + TestStateRecord::AccessKey { + account_id: "foo1", + public_key: "ed25519:He7QeRuwizNEhBioYG3u4DZ8jWXyETiyNzFD3MkTjDMf", + }, + TestStateRecord::Account { + account_id: "extra-account.near", + amount: 9_000_000, + locked: 0, + storage_usage: 0, + }, + TestStateRecord::AccessKey { + account_id: "extra-account.near", + public_key: "ed25519:BhnQV3oJa8iSQDKDc8gy36TsenaMFmv7qHvcnutuXj33", + }, + ], + wanted_records: &[ + TestStateRecord::Account { + account_id: "foo0", + amount: 100_000_000, + locked: 0, + storage_usage: 264, + }, + TestStateRecord::AccessKey { + account_id: "foo0", + public_key: "ed25519:He7QeRuwizNEhBioYG3u4DZ8jWXyETiyNzFD3MkTjDMf", + }, + TestStateRecord::AccessKey { + account_id: "foo0", + public_key: "ed25519:FXXrTXiKWpXj1R6r5fBvMLpstd8gPyrBq3qMByqKVzKF", + }, + TestStateRecord::Account { + account_id: "foo1", + amount: 3_000_000, + locked: 0, + storage_usage: 264, + }, + TestStateRecord::AccessKey { + account_id: "foo1", + public_key: "ed25519:FXXrTXiKWpXj1R6r5fBvMLpstd8gPyrBq3qMByqKVzKF", + }, + TestStateRecord::AccessKey { + account_id: "foo1", + public_key: "ed25519:He7QeRuwizNEhBioYG3u4DZ8jWXyETiyNzFD3MkTjDMf", + }, + TestStateRecord::Account { + account_id: "foo2", + amount: 299_000_000, + locked: 1_000_000, + storage_usage: 182, + }, + TestStateRecord::AccessKey { + account_id: "foo2", + public_key: "ed25519:He7QeRuwizNEhBioYG3u4DZ8jWXyETiyNzFD3MkTjDMf", + }, + TestStateRecord::Account { + account_id: "foo3", + amount: 10_000 * framework::config::UNC_BASE, + locked: 2_000_000, + storage_usage: 182, + }, + TestStateRecord::AccessKey { + account_id: "foo3", + public_key: "ed25519:FXXrTXiKWpXj1R6r5fBvMLpstd8gPyrBq3qMByqKVzKF", + }, + TestStateRecord::Account { + account_id: "asdf.near", + amount: 1_234_000, + locked: 0, + storage_usage: 182, + }, + TestStateRecord::AccessKey { + account_id: "asdf.near", + public_key: "ed25519:5C66RSJgwK17Yb6VtTbgBCFHDRPzGUd6AAhFdXNvmJuo", + }, + TestStateRecord::Account { + account_id: "extra-account.near", + amount: 9_000_000, + locked: 0, + storage_usage: 182, + }, + TestStateRecord::AccessKey { + account_id: "extra-account.near", + public_key: "ed25519:BhnQV3oJa8iSQDKDc8gy36TsenaMFmv7qHvcnutuXj33", + }, + ], + }, + // this one tests that account records appear before code records + TestCase { + initial_validators: &[TestAccountInfo { + account_id: "foo0", + public_key: "ed25519:He7QeRuwizNEhBioYG3u4DZ8jWXyETiyNzFD3MkTjDMf", + amount: 1_000_000, + }], + validators_in: &[TestAccountInfo { + account_id: "foo0", + public_key: "ed25519:He7QeRuwizNEhBioYG3u4DZ8jWXyETiyNzFD3MkTjDMf", + amount: 1_000_000, + }], + records_in: &[ + TestStateRecord::Account { + account_id: "foo0", + amount: 1_000_000, + locked: 1_000_000, + storage_usage: 183, + }, + TestStateRecord::AccessKey { + account_id: "foo0", + public_key: "ed25519:He7QeRuwizNEhBioYG3u4DZ8jWXyETiyNzFD3MkTjDMf", + }, + TestStateRecord::Contract { account_id: "foo0" }, + ], + extra_records: &[TestStateRecord::Account { + account_id: "foo0", + amount: 100_000_000, + locked: 0, + storage_usage: 0, + }], + wanted_records: &[ + TestStateRecord::Account { + account_id: "foo0", + amount: 99_000_000, + locked: 1_000_000, + storage_usage: 183, + }, + TestStateRecord::AccessKey { + account_id: "foo0", + public_key: "ed25519:He7QeRuwizNEhBioYG3u4DZ8jWXyETiyNzFD3MkTjDMf", + }, + TestStateRecord::Contract { account_id: "foo0" }, + ], + }, + ]; + + #[test] + fn test_amend_genesis() { + for t in TEST_CASES.iter() { + t.run().unwrap(); + } + } +} diff --git a/tools/chainsync-loadtest/Cargo.toml b/tools/chainsync-loadtest/Cargo.toml new file mode 100644 index 000000000..b535270f7 --- /dev/null +++ b/tools/chainsync-loadtest/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "chainsync-loadtest" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[[bin]] +path = "src/main.rs" +name = "chainsync-loadtest" + +[dependencies] +actix.workspace = true +anyhow.workspace = true +async-trait.workspace = true +clap.workspace = true +dirs.workspace = true +futures.workspace = true +log.workspace = true +openssl-probe.workspace = true +parking_lot.workspace = true +rand.workspace = true +tokio.workspace = true + +unc-async.workspace = true +unc-chain-configs.workspace = true +unc-crypto.workspace = true +unc-primitives.workspace = true +unc-store.workspace = true +framework.workspace = true +unc-network.workspace = true +unc-o11y.workspace = true diff --git a/tools/chainsync-loadtest/README.md b/tools/chainsync-loadtest/README.md new file mode 100644 index 000000000..602ca6f59 --- /dev/null +++ b/tools/chainsync-loadtest/README.md @@ -0,0 +1,38 @@ +# Chain fetcher + +This binary takes a hash of a block as an input and then fetches +from the network the following: +1. All block headers up to the newest header (or until block-limit is reached). +1. All blocks for the headers fetched. +1. All fragments of all chunks for all blocks fetched. +The binary doesn't interpret the data it received (except for checking what it +should fetch next), but rather discards it immediately. This way it is able to +benchmark the raw throughput of the network from the point of view of a single node. + +Flags: +* chain-id - the name of the chain. + The binary fetches the config file of the chain automatically. + The binary doesn't use the genesis file at all (it has the genesis file hashes hardcoded instead) + TODO: add a flag for genesis file hash. +* start-block-hash - the Base58 encoded block hash. The binary will fetch everything starting + with this block up to the newest block (or until block-limit is reached). +* qps-limit - maximum number of requests per second that the binary is allowed to send. + This is a global limit (NOT per connection). The requests are distributed uniformly across + all the connections that the program establishes. Peer discovery works the same way as for uncd. +* block-limit - number of blocks to fetch + +## Example usage + +1. Go to [https://explorer.testnet.near.org/blocks]. +1. Select the block from which you would like to start fetching from. +1. Copy the hash of the block. +1. run + + cargo run -- --chain-id=testnet --qps-limit=200 --block-limit=2000 --start-block-hash= + +1. First you will see that the program is establishing connections. +1. Once there are enough connections it will start sending the requests. +1. Every few seconds you will see a log indicating the progress: + * how many requests have been sent, how many responses received + * how many headers/blocks/chunks are being fetched, how many have been successfully fetched. +1. Once everything is fetched, the program will print final stats, then "Fetch completed" and terminate. diff --git a/tools/chainsync-loadtest/src/concurrency/mod.rs b/tools/chainsync-loadtest/src/concurrency/mod.rs new file mode 100644 index 000000000..f8947695b --- /dev/null +++ b/tools/chainsync-loadtest/src/concurrency/mod.rs @@ -0,0 +1,7 @@ +mod once; +mod rate_limiter; +pub mod weak_map; + +pub use once::Once; +pub use rate_limiter::RateLimiter; +pub use weak_map::WeakMap; diff --git a/tools/chainsync-loadtest/src/concurrency/once.rs b/tools/chainsync-loadtest/src/concurrency/once.rs new file mode 100644 index 000000000..f14f5c743 --- /dev/null +++ b/tools/chainsync-loadtest/src/concurrency/once.rs @@ -0,0 +1,58 @@ +use parking_lot::RwLock; +use std::future::Future; + +fn is_send() {} +fn is_sync() {} + +#[allow(dead_code)] +fn test() { + is_send::>(); + is_sync::>(); +} + +// Once is a synchronization primitive, which stores a single value. +// This value can be set at most once, and multiple consumers are +// allowed to wait for that value. +pub struct Once { + value: RwLock>, + notify: tokio::sync::Notify, +} + +impl Once { + pub fn new() -> Once { + return Once { value: RwLock::new(None), notify: tokio::sync::Notify::new() }; + } + + // set() sets the value of Once to x. + // Returns x back to the caller, in case Once has already been set. + pub fn set(&self, x: T) -> Result<(), T> { + let mut v = self.value.write(); + if v.is_some() { + return Err(x); + } + *v = Some(x); + self.notify.notify_waiters(); + drop(v); + return Ok(()); + } + + // get() gets a clone of the value, or returns None if not set yet. + pub fn get(&self) -> Option { + self.value.read().clone() + } + + // wait() waits for Once to be set, then returns a clone of the value. + pub fn wait(&self) -> impl Future + Send + '_ { + let l = self.value.read(); + let v = (*l).clone(); + let n = self.notify.notified(); + drop(l); + async move { + if let Some(v) = v { + return v; + } + n.await; + return self.get().unwrap(); + } + } +} diff --git a/tools/chainsync-loadtest/src/concurrency/rate_limiter.rs b/tools/chainsync-loadtest/src/concurrency/rate_limiter.rs new file mode 100644 index 000000000..1e1059476 --- /dev/null +++ b/tools/chainsync-loadtest/src/concurrency/rate_limiter.rs @@ -0,0 +1,60 @@ +use unc_async::time; +use unc_network::concurrency::ctx; +use std::sync::Arc; + +struct Inner { + interval: time::Duration, + burst: u64, + tokens: u64, + ticks_processed: u64, + start: time::Instant, +} + +impl Inner { + fn ticks(&self, t: time::Instant) -> u64 { + return ((t - self.start).as_seconds_f64() / self.interval.as_seconds_f64()) as u64; + } + fn instant(&self, ticks: u64) -> time::Instant { + return self.start + self.interval * (ticks as f64); + } +} + +// RateLimiter is a Semaphore with periodically added permits. +// It allows to rate limit any async-based operations. +// It is parametrized by: +// - interval - the amount of time after which a new permit is added. +// - burst - the maximal number of permits in the semaphore. +pub struct RateLimiter(Arc>); + +impl RateLimiter { + pub fn new(interval: time::Duration, burst: u64) -> RateLimiter { + if interval.is_zero() { + panic!("interval has to be non-zero"); + } + return RateLimiter(Arc::new(tokio::sync::Mutex::new(Inner { + interval, + burst, + tokens: burst, + start: ctx::time::now(), + ticks_processed: 0, + }))); + } + + // See semantics of https://pkg.go.dev/golang.org/x/time/rate + pub async fn allow(&self) -> ctx::OrCanceled<()> { + let mut rl = ctx::wait(self.0.lock()).await?; + let ticks_now = rl.ticks(ctx::time::now()); + rl.tokens = std::cmp::min( + rl.burst, + rl.tokens.wrapping_add(ticks_now.wrapping_sub(rl.ticks_processed)), + ); + rl.ticks_processed = ticks_now; + if rl.tokens > 0 { + rl.tokens -= 1; + return Ok(()); + } + ctx::time::sleep_until(rl.instant(rl.ticks_processed + 1)).await?; + rl.ticks_processed += 1; + Ok(()) + } +} diff --git a/tools/chainsync-loadtest/src/concurrency/weak_map.rs b/tools/chainsync-loadtest/src/concurrency/weak_map.rs new file mode 100644 index 000000000..2bc7d8879 --- /dev/null +++ b/tools/chainsync-loadtest/src/concurrency/weak_map.rs @@ -0,0 +1,69 @@ +use std::cmp::Eq; +use std::collections::hash_map::Entry; +use std::collections::HashMap; +use std::hash::Hash; +use std::ops::Deref; +use std::sync::{Arc, Mutex, Weak}; + +// WeakMap is a collection of weak pointers. +// Once the last reference to an element of the map is dropped, +// the weak pointer is removed from the map. +pub struct WeakMap { + inner: Mutex>>>, +} + +// Ref is a wrapper of V, which provides a custom drop() +// to clean up the weak map entry once all references to the +// map element are dropped. +pub struct Ref { + key: K, + map: Arc>, + value: V, +} + +impl Deref for Ref { + type Target = V; + fn deref(&self) -> &V { + return &self.value; + } +} + +impl Drop for Ref { + fn drop(&mut self) { + // Be careful to not to drop an Arc here, + // because that might trigger this function + // recursively and cause a deadlock. + let mut m = self.map.inner.lock().unwrap(); + if let Entry::Occupied(e) = m.entry(self.key.clone()) { + if e.get().strong_count() == 0 { + e.remove_entry(); + } + } + } +} + +impl WeakMap { + pub fn new() -> Arc { + return Arc::new(Self { inner: Mutex::new(HashMap::new()) }); + } + + // get() returns a reference to map[key], or None if not present. + pub fn get(self: &Arc, key: &K) -> Option>> { + let m = self.inner.lock().unwrap(); + return m.get(key).map(|w| w.upgrade()).flatten(); + } + + // get() returns a reference to map[key]. + // Uses new_value to initialize the map entry if missing. + pub fn get_or_insert(self: &Arc, key: &K, new_value: impl Fn() -> V) -> Arc> { + let mut m = self.inner.lock().unwrap(); + if let Some(w) = m.get(key) { + if let Some(v) = w.upgrade() { + return v; + } + } + let p = Arc::new(Ref { key: key.clone(), map: self.clone(), value: new_value() }); + m.insert(key.clone(), Arc::downgrade(&p)); + return p; + } +} diff --git a/tools/chainsync-loadtest/src/fetch_chain.rs b/tools/chainsync-loadtest/src/fetch_chain.rs new file mode 100644 index 000000000..83f69c466 --- /dev/null +++ b/tools/chainsync-loadtest/src/fetch_chain.rs @@ -0,0 +1,79 @@ +use crate::network; +use anyhow::Context; +use log::info; +use unc_async::time; +use unc_network::concurrency::ctx; +use unc_network::concurrency::scope; +use unc_primitives::hash::CryptoHash; +use std::sync::atomic::Ordering; +use std::sync::Arc; + +// run() fetches the chain (headers,blocks and chunks) +// starting with block having hash = and +// ending with the current tip of the chain (snapshotted once +// at the start of the routine, so that the amount of work +// is bounded). +pub async fn run( + network: &Arc, + start_block_hash: CryptoHash, + block_limit: u64, +) -> anyhow::Result<()> { + info!("SYNC start"); + let peers = network.info().await?; + let target_height = peers.highest_height_peers[0].highest_block_height as i64; + info!("SYNC target_height = {}", target_height); + + let start_time = ctx::time::now(); + let res = scope::run!(|s| async move { + s.spawn_bg::<()>(async { + loop { + info!("stats = {:?}", network.stats); + ctx::time::sleep(time::Duration::seconds(2)).await?; + } + }); + let mut last_hash = start_block_hash; + let mut last_height = 0; + let mut blocks_count = 0; + while last_height < target_height { + // Fetch the next batch of headers. + let mut headers = network.fetch_block_headers(last_hash).await?; + headers.sort_by_key(|h| h.height()); + let last_header = headers.last().context("no headers")?; + last_hash = *last_header.hash(); + last_height = last_header.height() as i64; + info!( + "SYNC last_height = {}, {} headers left", + last_height, + target_height - last_height + ); + for h in headers { + blocks_count += 1; + if blocks_count == block_limit { + return anyhow::Ok(()); + } + let h = *h.hash(); + s.spawn(async move { + let block = network.fetch_block(h).await?; + for ch in block.chunks().iter() { + s.spawn(network.fetch_chunk(ch.clone())); + } + anyhow::Ok(()) + }); + } + } + anyhow::Ok(()) + }); + let stop_time = ctx::time::now(); + let total_time = stop_time - start_time; + let t = total_time.as_seconds_f64(); + let sent = network.stats.msgs_sent.load(Ordering::Relaxed); + let headers = network.stats.header_done.load(Ordering::Relaxed); + let blocks = network.stats.block_done.load(Ordering::Relaxed); + let chunks = network.stats.chunk_done.load(Ordering::Relaxed); + info!("running time: {:.2}s", t); + info!("average QPS: {:.2}", (sent as f64) / t); + info!("fetched {} header batches ({:.2} per second)", headers, headers as f64 / t); + info!("fetched {} blocks ({:.2} per second)", blocks, blocks as f64 / t); + info!("fetched {} chunks ({:.2} per second)", chunks, chunks as f64 / t); + return res; +} diff --git a/tools/chainsync-loadtest/src/main.rs b/tools/chainsync-loadtest/src/main.rs new file mode 100644 index 000000000..925313f89 --- /dev/null +++ b/tools/chainsync-loadtest/src/main.rs @@ -0,0 +1,151 @@ +mod concurrency; +mod fetch_chain; +mod network; + +use anyhow::{anyhow, Context}; +use unc_async::actix::AddrWithAutoSpanContextExt; +use unc_async::messaging::LateBoundSender; +use unc_async::messaging::Sender; +use unc_async::time; +use unc_chain_configs::Genesis; +use unc_network::concurrency::ctx; +use unc_network::concurrency::scope; +use unc_network::PeerManagerActor; +use unc_o11y::tracing::{error, info}; +use unc_primitives::block::GenesisId; +use unc_primitives::hash::CryptoHash; +use framework::config; +use framework::config::UncConfig; +use network::Network; +use openssl_probe; +use std::sync::Arc; + +fn genesis_hash(chain_id: &str) -> CryptoHash { + return match chain_id { + unc_primitives::chains::MAINNET => "EPnLgE7iEq9s7yTkos96M3cWymH5avBAPm3qx3NXqR8H", + unc_primitives::chains::TESTNET => "FWJ9kR6KFWoyMoNjpLXXGHeuiy7tEY6GmoFeCA5yuc6b", + _ => { + return Default::default(); + } + } + .parse() + .unwrap(); +} + +pub fn start_with_config(config: UncConfig, qps_limit: u32) -> anyhow::Result> { + let network_adapter = Arc::new(LateBoundSender::default()); + let network = Network::new(&config, network_adapter.clone().into(), qps_limit); + + let network_actor = PeerManagerActor::spawn( + time::Clock::real(), + unc_store::db::TestDB::new(), + config.network_config, + network.clone(), + Sender::noop(), + GenesisId { + chain_id: config.client_config.chain_id.clone(), + hash: genesis_hash(&config.client_config.chain_id), + }, + ) + .context("PeerManagerActor::spawn()")?; + network_adapter.bind(network_actor.with_auto_span_context()); + return Ok(network); +} + +fn download_configs(chain_id: &str, dir: &std::path::Path) -> anyhow::Result { + // Always fetch the config. + std::fs::create_dir_all(dir)?; + let url = config::get_config_url(chain_id); + let config_path = &dir.join(config::CONFIG_FILENAME); + config::download_config(&url, config_path)?; + let config = config::Config::from_file(config_path)?; + + // Generate node key. + let account_id = "node".parse().unwrap(); + let node_signer = + unc_crypto::InMemorySigner::from_random(account_id, unc_crypto::KeyType::ED25519); + let mut genesis = Genesis::default(); + genesis.config.chain_id = chain_id.to_string(); + UncConfig::new(config, genesis, (&node_signer).into(), None) +} + +#[derive(clap::Parser, Debug)] +struct Cmd { + #[clap(long)] + pub chain_id: String, + #[clap(long)] + pub start_block_hash: String, + #[clap(long, default_value = "200")] + pub qps_limit: u32, + #[clap(long, default_value = "2000")] + pub block_limit: u64, +} + +impl Cmd { + fn parse_and_run() -> anyhow::Result<()> { + let cmd: Self = clap::Parser::parse(); + let start_block_hash = + cmd.start_block_hash.parse::().map_err(|x| anyhow!(x.to_string()))?; + + let mut cache_dir = dirs::cache_dir().context("dirs::cache_dir() = None")?; + cache_dir.push("unc_configs"); + cache_dir.push(&cmd.chain_id); + + info!("downloading configs for chain {}", cmd.chain_id); + let home_dir = cache_dir.as_path(); + let unc_config = + download_configs(&cmd.chain_id, home_dir).context("Failed to initialize configs")?; + + info!("#boot nodes = {}", unc_config.network_config.peer_store.boot_nodes.len()); + // Dropping Runtime is blocking, while futures should never be blocking. + // Tokio has a runtime check which panics if you drop tokio Runtime from a future executed + // on another Tokio runtime. + // To avoid that, we create a runtime within the synchronous code and pass just an Arc + // inside of it. + let rt_ = Arc::new(tokio::runtime::Runtime::new()?); + let rt = rt_; + return actix::System::new().block_on(async move { + let network = + start_with_config(unc_config, cmd.qps_limit).context("start_with_config")?; + + // We execute the chain_sync on a totally separate set of system threads to minimize + // the interaction with actix. + rt.spawn(async move { + scope::run!(|s| async { + s.spawn_bg(async { + match ctx::wait(tokio::signal::ctrl_c()).await { + Err(ctx::ErrCanceled) => Ok(()), + Ok(res) => { + res?; + info!("Got CTRL+C, stopping..."); + Err(anyhow!("Got CTRL+C")) + } + } + }); + fetch_chain::run(&network, start_block_hash, cmd.block_limit).await?; + info!("Fetch completed"); + anyhow::Ok(()) + }) + }) + .await??; + return Ok(()); + }); + } +} + +fn main() { + let env_filter = unc_o11y::EnvFilterBuilder::from_env() + .finish() + .unwrap() + .add_directive(unc_o11y::tracing::Level::INFO.into()); + let _subscriber = unc_o11y::default_subscriber(env_filter, &Default::default()).global(); + let orig_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |panic_info| { + orig_hook(panic_info); + std::process::exit(1); + })); + openssl_probe::init_ssl_cert_env_vars(); + if let Err(e) = Cmd::parse_and_run() { + error!("Cmd::parse_and_run(): {:#}", e); + } +} diff --git a/tools/chainsync-loadtest/src/network.rs b/tools/chainsync-loadtest/src/network.rs new file mode 100644 index 000000000..aa9e91172 --- /dev/null +++ b/tools/chainsync-loadtest/src/network.rs @@ -0,0 +1,311 @@ +use crate::concurrency::{Once, RateLimiter, WeakMap}; +use log::info; +use unc_async::messaging::CanSend; +use unc_async::time; +use unc_network::concurrency::ctx; +use unc_network::concurrency::scope; +use unc_network::types::{ + AccountIdOrPeerTrackingShard, PartialEncodedChunkRequestMsg, PartialEncodedChunkResponseMsg, + ReasonForBan, StateResponseInfo, +}; +use unc_network::types::{ + FullPeerInfo, NetworkInfo, NetworkRequests, PeerManagerAdapter, PeerManagerMessageRequest, +}; +use unc_primitives::block::{Approval, Block, BlockHeader}; +use unc_primitives::challenge::Challenge; +use unc_primitives::chunk_validation::ChunkEndorsement; +use unc_primitives::chunk_validation::ChunkStateWitness; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::sharding::ChunkHash; +use unc_primitives::sharding::ShardChunkHeader; +use unc_primitives::transaction::SignedTransaction; +use unc_primitives::types::{AccountId, EpochId, ShardId}; +use unc_primitives::views::FinalExecutionOutcomeView; +use framework::config::UncConfig; +use rand::seq::SliceRandom; +use rand::thread_rng; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::{Arc, Mutex}; +use tokio::sync::oneshot; + +#[derive(Default, Debug)] +pub struct Stats { + pub msgs_sent: AtomicU64, + pub msgs_recv: AtomicU64, + + pub header_start: AtomicU64, + pub header_done: AtomicU64, + pub block_start: AtomicU64, + pub block_done: AtomicU64, + pub chunk_start: AtomicU64, + pub chunk_done: AtomicU64, +} + +// NetworkData contains the mutable private data of the Network struct. +// TODO: consider replacing the vector of oneshot Senders with a single +// Notify/Once. +struct NetworkData { + info_futures: Vec>>, + info_: Arc, +} + +// Network encapsulates PeerManager and exposes an async API for sending RPCs. +pub struct Network { + pub stats: Stats, + network_adapter: PeerManagerAdapter, + pub block_headers: Arc>>>, + pub blocks: Arc>>, + pub chunks: Arc>>, + data: Mutex, + + // client_config.min_num_peers + min_peers: usize, + // Currently it is equivalent to genesis_config.num_block_producer_seats, + // (see https://cs.github.com/utnet-org/utility/blob/dae9553670de13c279d3ebd55f17da13d94fa691/framework/src/runtime/mod.rs#L1114). + // AFAICT eventually it will change dynamically (I guess it will be provided in the Block). + parts_per_chunk: u64, + + request_timeout: time::Duration, + rate_limiter: RateLimiter, +} + +impl Network { + pub fn new( + config: &UncConfig, + network_adapter: PeerManagerAdapter, + qps_limit: u32, + ) -> Arc { + Arc::new(Network { + stats: Default::default(), + network_adapter, + data: Mutex::new(NetworkData { + info_: Arc::new(NetworkInfo { + connected_peers: vec![], + num_connected_peers: 0, + peer_max_count: 0, + highest_height_peers: vec![], + sent_bytes_per_sec: 0, + received_bytes_per_sec: 0, + known_producers: vec![], + tier1_connections: vec![], + tier1_accounts_keys: vec![], + tier1_accounts_data: vec![], + }), + info_futures: Default::default(), + }), + blocks: WeakMap::new(), + block_headers: WeakMap::new(), + chunks: WeakMap::new(), + + min_peers: config.client_config.min_num_peers, + parts_per_chunk: config.genesis.config.num_block_producer_seats, + rate_limiter: RateLimiter::new( + time::Duration::seconds(1) / qps_limit, + qps_limit as u64, + ), + request_timeout: time::Duration::seconds(2), + }) + } + + // keep_sending() sends periodically (every self.request_timeout) + // a NetworkRequest produced by in an infinite loop. + // The requests are distributed uniformly among all the available peers. + // - keep_sending() completes as soon as ctx expires. + // - keep_sending() respects the global rate limits, so the actual frequency + // of the sends may be lower than expected. + // - keep_sending() may pause if the number of connected peers is too small. + async fn keep_sending<'a>( + self: &'a Arc, + new_req: impl 'a + Fn(FullPeerInfo) -> NetworkRequests, + ) -> anyhow::Result<()> { + loop { + let mut peers = self.info().await?.connected_peers.clone(); + peers.shuffle(&mut thread_rng()); + for peer in peers { + // TODO: rate limit per peer. + self.rate_limiter.allow().await?; + self.network_adapter.send(PeerManagerMessageRequest::NetworkRequests(new_req( + peer.full_peer_info.clone(), + ))); + self.stats.msgs_sent.fetch_add(1, Ordering::Relaxed); + ctx::time::sleep(self.request_timeout).await?; + } + } + } + + // info() fetches the state of the newest available NetworkInfo. + // It blocks if the number of connected peers is too small. + pub async fn info(self: &Arc) -> anyhow::Result> { + let (send, recv) = oneshot::channel(); + { + let mut n = self.data.lock().unwrap(); + if n.info_.num_connected_peers >= self.min_peers { + let _ = send.send(n.info_.clone()); + } else { + n.info_futures.push(send); + } + } + anyhow::Ok(ctx::wait(recv).await??) + } + + // fetch_block_headers fetches a batch of headers, starting with the header + // AFTER the header with the given . The batch size is bounded by + // sync::MAX_BLOCK_HEADERS = (currently) 512 + // https://github.com/utnet-org/utility/blob/ad896e767b0efbce53060dd145836bbeda3d656b/chain/client/src/sync.rs#L38 + pub async fn fetch_block_headers( + self: &Arc, + hash: CryptoHash, + ) -> anyhow::Result> { + scope::run!(|s| async { + self.stats.header_start.fetch_add(1, Ordering::Relaxed); + let recv = self.block_headers.get_or_insert(&hash, || Once::new()); + s.spawn_bg(async { + self.keep_sending(|peer| NetworkRequests::BlockHeadersRequest { + hashes: vec![hash], + peer_id: peer.peer_info.id, + }) + .await + }); + let res = ctx::wait(recv.wait()).await; + self.stats.header_done.fetch_add(1, Ordering::Relaxed); + anyhow::Ok(res?) + }) + } + + // fetch_block() fetches a block with a given hash. + pub async fn fetch_block(self: &Arc, hash: CryptoHash) -> anyhow::Result { + scope::run!(|s| async { + self.stats.block_start.fetch_add(1, Ordering::Relaxed); + let recv = self.blocks.get_or_insert(&hash, || Once::new()); + s.spawn_bg(async { + self.keep_sending(|peer| NetworkRequests::BlockRequest { + hash, + peer_id: peer.peer_info.id, + }) + .await + }); + let res = ctx::wait(recv.wait()).await; + self.stats.block_done.fetch_add(1, Ordering::Relaxed); + anyhow::Ok(res?) + }) + } + + // fetch_chunk fetches a chunk for the given chunk header. + pub async fn fetch_chunk( + self: &Arc, + ch: ShardChunkHeader, + ) -> anyhow::Result { + scope::run!(|s| async { + let recv = self.chunks.get_or_insert(&ch.chunk_hash(), || Once::new()); + // TODO: consider converting wrapping these atomic counters into sth like a Span. + self.stats.chunk_start.fetch_add(1, Ordering::Relaxed); + s.spawn_bg(async { + self.keep_sending(|peer| NetworkRequests::PartialEncodedChunkRequest { + target: AccountIdOrPeerTrackingShard { + account_id: peer.peer_info.account_id, + prefer_peer: true, + shard_id: ch.shard_id(), + only_archival: false, + min_height: ch.height_included(), + }, + request: PartialEncodedChunkRequestMsg { + chunk_hash: ch.chunk_hash(), + part_ords: (0..self.parts_per_chunk).collect(), + tracking_shards: Default::default(), + }, + create_time: ctx::time::now(), + }) + .await + }); + let res = ctx::wait(recv.wait()).await; + self.stats.chunk_done.fetch_add(1, Ordering::Relaxed); + anyhow::Ok(res?) + }) + } +} + +#[async_trait::async_trait] +impl unc_network::client::Client for Network { + async fn tx_status_request( + &self, + _account_id: AccountId, + _tx_hash: CryptoHash, + ) -> Option> { + None + } + + async fn tx_status_response(&self, _tx_result: FinalExecutionOutcomeView) {} + + async fn state_request_header( + &self, + _shard_id: ShardId, + _sync_hash: CryptoHash, + ) -> Result, ReasonForBan> { + Ok(None) + } + + async fn state_request_part( + &self, + _shard_id: ShardId, + _sync_hash: CryptoHash, + _part_id: u64, + ) -> Result, ReasonForBan> { + Ok(None) + } + + async fn state_response(&self, _info: StateResponseInfo) {} + + async fn block_approval(&self, _approval: Approval, _peer_id: PeerId) {} + + async fn transaction(&self, _transaction: SignedTransaction, _is_forwarded: bool) {} + + async fn block_request(&self, _hash: CryptoHash) -> Option> { + None + } + + async fn block_headers_request(&self, _hashes: Vec) -> Option> { + None + } + + async fn block(&self, block: Block, _peer_id: PeerId, _was_requested: bool) { + self.blocks.get(&block.hash().clone()).map(|p| p.set(block)); + } + + async fn block_headers( + &self, + headers: Vec, + _peer_id: PeerId, + ) -> Result<(), ReasonForBan> { + if let Some(h) = headers.iter().min_by_key(|h| h.height()) { + let hash = *h.prev_hash(); + self.block_headers.get(&hash).map(|p| p.set(headers)); + } + Ok(()) + } + + async fn challenge(&self, _challenge: Challenge) {} + + async fn network_info(&self, info: NetworkInfo) { + let mut n = self.data.lock().unwrap(); + n.info_ = Arc::new(info); + if n.info_.num_connected_peers < self.min_peers { + info!("connected = {}/{}", n.info_.num_connected_peers, self.min_peers); + return; + } + for s in n.info_futures.split_off(0) { + s.send(n.info_.clone()).unwrap(); + } + } + + async fn announce_account( + &self, + accounts: Vec<(AnnounceAccount, Option)>, + ) -> Result, ReasonForBan> { + Ok(accounts.into_iter().map(|a| a.0).collect()) + } + + async fn chunk_state_witness(&self, _witness: ChunkStateWitness) {} + + async fn chunk_endorsement(&self, _endorsement: ChunkEndorsement) {} +} diff --git a/tools/cold-store/Cargo.toml b/tools/cold-store/Cargo.toml new file mode 100644 index 000000000..e8c0f3e70 --- /dev/null +++ b/tools/cold-store/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "cold-store-tool" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +borsh.workspace = true +clap.workspace = true +rand.workspace = true +strum.workspace = true +tracing.workspace = true + +framework.workspace = true +unc-chain-configs.workspace = true +unc-epoch-manager.workspace = true +unc-primitives.workspace = true +unc-store.workspace = true diff --git a/tools/cold-store/README.md b/tools/cold-store/README.md new file mode 100644 index 000000000..4e0920957 --- /dev/null +++ b/tools/cold-store/README.md @@ -0,0 +1,101 @@ +# Cold Storage testing tool + +## Workflow + +Start by trying something on localnet, +then move on to test you code on a dedicated machine +with real archival data. +Ideally, on archival machine we only need to do every step before experimenting once. +But accidents happen, and we should be mindful of the time it takes us +to recover from them. + +### Localnet + +#### Prepare data +- Run `uncd init` / `uncd localnet`. +- Change your config (`--home-dir`/config.json) +to not garbage collect larger number of epochs. +By default it is 5, +but you can change `gc_num_epochs_to_keep` to 1000, for example. +Epoch lasts 500 blocks on localnet (as specified in `--home-dir`/genesis.json). +That will give you sort of archival storage of max 500'0000 blocks. + +#### Produce blocks +Start localnet node as usual `uncd run`. +Run node WITHOUT `--archive` parameter. +In archive mode node will not save `TrieChanges` +and they are needed to copy blocks to cold. +You can monitor the current height and make sure that we didn't cross +`gc_num_epochs_to_keep` epoch bound. +Otherwise, garbage collection will start, +and we cannot copy the block that has been garbage collected. + +#### Migrate and experiment +Migration can be done block by block, +because we have `TrieChanges` from the very start. + +### Real archival data + +#### Prepare machine +**TODO** + +#### Prepare data +- Download snapshot of archival db to the machine. +- Create a patched binary that overrides `save_trie_changes: false` +for archival storage. + +#### Produce blocks +Run that binary for at least `gc_num_epochs_to_keep`. +Epochs are larger on testnet/mainnet, so just give it a few days. +You can use `sudo systemctl start uncd` to run `/home/ubuntu/uncd` +and `jornalctl -u uncd` to check logs. +Be careful with what binary is at `/home/ubuntu/uncd`. +**TODO** some kind of system to maintain a bunch of local binaries. + +#### Migrate +After archival storage is populated with enough of `TrieChanges`, +it should not be experimented with. If we need more block, we should +run a binary with that and only that one path -- saving of `TrieChanges`. +Archival storage should probably lie NOT in `/home/ubuntu/.uncd/data`. + +That means, that unlike in real life, hot storage will not be mutated +archival storage, but rather a brand new one, +originally populated by copying of archival storage. + +And we WILL need a semi-proper migration to start split storage. +By semi-proper I mean everything, but accurately changing State +column in hot. That is not needed to start experimenting. + +## Subcommands +Before every subcommand the `NodeStorage` is opened with `UncConfig` +from `home_dir`. + +### Open +Check that `NodeStorage::has_cold` is true. + +### Head +Print `HEAD` of cold storage, `FINAL_HEAD` from hot storage +and also `HEAD` for hot storage. +It is useful to check that some blocks has been copied/produced. + +### CopyNextBlocks +Does `num_of_blocks`(1 by default) iterations of +- Copy block at height "cold HEAD + 1" to cold storage. +- Update cold storage `HEAD`. + +### (TODO) CopyAllBlocks +Initial population of cold storage, where we copy all cold column +to cold storage, plus set misc data like genesis hash and head. + +### (TODO) GCHotSimpleAll +Initial garbage collection of hot storage, where we just delete +all the gc columns but `State` up to head of cold storage. + +### (TODO) GCState +Initial gc of `State` for hot storage + +### (TODO) CompareHotState +Takes hot storage and rpc storage, +performs some manipulation using `TrieChanges` +to makes their tail and head match, +compares State column (should be exactly the same). \ No newline at end of file diff --git a/tools/cold-store/src/cli.rs b/tools/cold-store/src/cli.rs new file mode 100644 index 000000000..3ae2ed03b --- /dev/null +++ b/tools/cold-store/src/cli.rs @@ -0,0 +1,653 @@ +use crate::cli::SubCommand::CheckStateRoot; +use anyhow; +use anyhow::Context; +use borsh::BorshDeserialize; +use clap; +use unc_epoch_manager::{EpochManager, EpochManagerAdapter, EpochManagerHandle}; +use unc_primitives::block::Tip; +use unc_primitives::epoch_manager::block_info::BlockInfo; +use unc_primitives::hash::CryptoHash; +use unc_store::cold_storage::{copy_all_data_to_cold, update_cold_db, update_cold_head}; +use unc_store::metadata::DbKind; +use unc_store::{DBCol, NodeStorage, Store, StoreOpener}; +use unc_store::{COLD_HEAD_KEY, FINAL_HEAD_KEY, HEAD_KEY, TAIL_KEY}; +use framework::UncConfig; +use rand::seq::SliceRandom; +use std::io::Result; +use std::path::Path; +use strum::IntoEnumIterator; + +#[derive(clap::Parser)] +pub struct ColdStoreCommand { + /// By default state viewer opens rocks DB in the read only mode, which allows it to run + /// multiple instances in parallel and be sure that no unintended changes get written to the DB. + #[clap(long, short = 'w')] + readwrite: bool, + #[clap(subcommand)] + subcmd: SubCommand, +} + +#[derive(clap::Parser)] +#[clap(subcommand_required = true, arg_required_else_help = true)] +enum SubCommand { + /// Open NodeStorage and check that is has cold storage. + Open, + /// Open NodeStorage and print cold head, hot head and hot final head. + Head, + /// Copy n blocks to cold storage and update cold HEAD. One by one. + /// Updating of HEAD happens in every iteration. + CopyNextBlocks(CopyNextBlocksCmd), + /// Copy all blocks to cold storage and update cold HEAD. + CopyAllBlocks(CopyAllBlocksCmd), + /// Prepare a hot db from a rpc db. This command will update the db kind in + /// the db and perform some sanity checks to make sure this db is suitable + /// for migration to split storage. + /// This command expects the following preconditions: + /// - config.store.path points to an existing database with kind Archive + /// - config.cold_store.path points to an existing database with kind Cold + /// - store_relative_path points to an existing database with kind Rpc + PrepareHot(PrepareHotCmd), + /// Traverse trie and check that every node is in cold db. + /// Can start from given state_root or compute previous roots for every chunk in provided block + /// and use them as starting point. + /// You can provide maximum depth and/or maximum number of vertices to traverse for each root. + /// Trie is traversed using DFS with randomly shuffled kids for every node. + CheckStateRoot(CheckStateRootCmd), +} + +impl ColdStoreCommand { + pub fn run(self, home_dir: &Path) -> anyhow::Result<()> { + let mode = + if self.readwrite { unc_store::Mode::ReadWrite } else { unc_store::Mode::ReadOnly }; + let mut unc_config = framework::config::load_config( + &home_dir, + unc_chain_configs::GenesisValidationMode::Full, + ) + .unwrap_or_else(|e| panic!("Error loading config: {:#}", e)); + + let opener = self.get_opener(home_dir, &mut unc_config); + + let storage = + opener.open_in_mode(mode).unwrap_or_else(|e| panic!("Error opening storage: {:#}", e)); + + let epoch_manager = + EpochManager::new_arc_handle(storage.get_hot_store(), &unc_config.genesis.config); + match self.subcmd { + SubCommand::Open => check_open(&storage), + SubCommand::Head => print_heads(&storage), + SubCommand::CopyNextBlocks(cmd) => { + for _ in 0..cmd.number_of_blocks { + copy_next_block(&storage, &unc_config, epoch_manager.as_ref()); + } + Ok(()) + } + SubCommand::CopyAllBlocks(cmd) => { + copy_all_blocks(&storage, cmd.batch_size, !cmd.no_check_after); + Ok(()) + } + SubCommand::PrepareHot(cmd) => cmd.run(&storage, &home_dir, &unc_config), + SubCommand::CheckStateRoot(cmd) => cmd.run(&storage), + } + } + + /// Returns opener suitable for subcommand. + /// If subcommand is CheckStateRoot, creates checkpoint for cold db + /// and modifies `unc_config.config.cold_store.path` to path to that checkpoint. + /// Then returns opener for dbs at `store.path` and `cold_store.path`. + pub fn get_opener<'a>( + &'a self, + home_dir: &Path, + unc_config: &'a mut UncConfig, + ) -> StoreOpener<'a> { + if !unc_config.config.archive { + tracing::warn!("Expected archive option in config to be set to true."); + } + + let opener = NodeStorage::opener( + home_dir, + unc_config.config.archive, + &unc_config.config.store, + unc_config.config.cold_store.as_ref(), + ); + + match self.subcmd { + CheckStateRoot(_) => { + let (hot_snapshot, cold_snapshot) = opener + .create_snapshots(unc_store::Mode::ReadOnly) + .expect("Failed to create snapshots"); + if let Some(_) = &hot_snapshot.0 { + hot_snapshot.remove().expect("Failed to remove unnecessary hot snapshot"); + } + if let Some(cold_store_config) = unc_config.config.cold_store.as_mut() { + cold_store_config.path = + Some(cold_snapshot.0.clone().expect("cold_snapshot should be Some")); + } + } + _ => {} + } + + NodeStorage::opener( + home_dir, + unc_config.config.archive, + &unc_config.config.store, + unc_config.config.cold_store.as_ref(), + ) + } +} + +#[derive(clap::Parser)] +struct CopyNextBlocksCmd { + #[clap(short, long, default_value_t = 1)] + number_of_blocks: usize, +} + +#[derive(clap::Parser)] +struct CopyAllBlocksCmd { + /// Threshold size of the write transaction. + #[clap(short = 'b', long, default_value_t = 500_000_000)] + batch_size: usize, + /// Flag to not check correctness of cold db after copying. + #[clap(long = "nc")] + no_check_after: bool, +} + +fn check_open(store: &NodeStorage) -> anyhow::Result<()> { + assert!(store.has_cold()); + Ok(()) +} + +fn print_heads(store: &NodeStorage) -> anyhow::Result<()> { + let hot_store = store.get_hot_store(); + let cold_store = store.get_cold_store(); + + // hot store + { + let kind = hot_store.get_db_kind()?; + let head = hot_store.get_ser::(DBCol::BlockMisc, HEAD_KEY)?; + let final_head = hot_store.get_ser::(DBCol::BlockMisc, FINAL_HEAD_KEY)?; + let cold_head = hot_store.get_ser::(DBCol::BlockMisc, COLD_HEAD_KEY)?; + println!("HOT STORE KIND is {:#?}", kind); + println!("HOT STORE HEAD is at {:#?}", head); + println!("HOT STORE FINAL_HEAD is at {:#?}", final_head); + println!("HOT STORE COLD_HEAD is at {:#?}", cold_head); + } + + // cold store + if let Some(cold_store) = cold_store { + let kind = cold_store.get_db_kind()?; + let head_in_cold = cold_store.get_ser::(DBCol::BlockMisc, HEAD_KEY)?; + println!("COLD STORE KIND is {:#?}", kind); + println!("COLD STORE HEAD is at {:#?}", head_in_cold); + } + Ok(()) +} + +fn copy_next_block(store: &NodeStorage, config: &UncConfig, epoch_manager: &EpochManagerHandle) { + // Cold HEAD can be not set in testing. + // It should be set before the copying of a block in prod, + // but we should default it to genesis height here. + let cold_head_height = store + .get_cold_store() + .unwrap() + .get_ser::(DBCol::BlockMisc, HEAD_KEY) + .unwrap_or_else(|e| panic!("Error reading cold HEAD: {:#}", e)) + .map_or(config.genesis.config.genesis_height, |t| t.height); + + // If FINAL_HEAD is not set for hot storage though, we default it to 0. + // And subsequently fail in assert!(next_height <= hot_final_height). + let hot_final_head = store + .get_hot_store() + .get_ser::(DBCol::BlockMisc, FINAL_HEAD_KEY) + .unwrap_or_else(|e| panic!("Error reading hot FINAL_HEAD: {:#}", e)) + .map(|t| t.height) + .unwrap_or(0); + + let next_height = cold_head_height + 1; + println!("Height: {}", next_height); + assert!(next_height <= hot_final_head, "Should not copy non final blocks"); + + // Here it should be sufficient to just read from hot storage. + // Because BlockHeight is never garbage collectable and is not even copied to cold. + let cold_head_hash = get_ser_from_store::( + &store.get_hot_store(), + DBCol::BlockHeight, + &cold_head_height.to_le_bytes(), + ) + .unwrap_or_else(|| panic!("No block hash in hot storage for height {}", cold_head_height)); + + // For copying block we need to have shard_layout. + // For that we need epoch_id. + // For that we might use prev_block_hash, and because next_hight = cold_head_height + 1, + // we use cold_head_hash. + update_cold_db( + &*store.cold_db().unwrap(), + &store.get_hot_store(), + &epoch_manager + .get_shard_layout(&epoch_manager.get_epoch_id_from_prev_block(&cold_head_hash).unwrap()) + .unwrap(), + &next_height, + ) + .unwrap_or_else(|_| panic!("Failed to copy block at height {} to cold db", next_height)); + + update_cold_head(&*store.cold_db().unwrap(), &store.get_hot_store(), &next_height) + .unwrap_or_else(|_| panic!("Failed to update cold HEAD to {}", next_height)); +} + +fn copy_all_blocks(storage: &NodeStorage, batch_size: usize, check: bool) { + // If FINAL_HEAD is not set for hot storage we default it to 0 + // not genesis_height, because hot db needs to contain genesis block for that + let hot_final_head = storage + .get_hot_store() + .get_ser::(DBCol::BlockMisc, FINAL_HEAD_KEY) + .unwrap_or_else(|e| panic!("Error reading hot FINAL_HEAD: {:#}", e)) + .map(|t| t.height) + .unwrap_or(0); + + let keep_going = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true)); + + copy_all_data_to_cold( + (*storage.cold_db().unwrap()).clone(), + &storage.get_hot_store(), + batch_size, + &keep_going, + ) + .expect("Failed to do migration to cold db"); + + // Setting cold head to hot_final_head captured BEFORE the start of initial migration. + // Doesn't really matter here, but very important in case of migration during `uncd run`. + update_cold_head(&*storage.cold_db().unwrap(), &storage.get_hot_store(), &hot_final_head) + .unwrap_or_else(|_| panic!("Failed to update cold HEAD to {}", hot_final_head)); + + if check { + for col in DBCol::iter() { + if col.is_cold() { + println!( + "Performed {} {:?} checks", + check_iter(&storage.get_hot_store(), &storage.get_cold_store().unwrap(), col), + col + ); + } + } + } +} + +fn check_key( + first_store: &unc_store::Store, + second_store: &unc_store::Store, + col: DBCol, + key: &[u8], +) { + let first_res = first_store.get(col, key); + let second_res = second_store.get(col, key); + + assert_eq!(first_res.unwrap(), second_res.unwrap()); +} + +/// Checks that `first_store`'s column `col` is fully included in `second_store` +/// with same values for every key. +/// Return number of checks performed == number of keys in column `col` of the `first_store`. +fn check_iter( + first_store: &unc_store::Store, + second_store: &unc_store::Store, + col: DBCol, +) -> u64 { + let mut num_checks = 0; + for (key, _value) in first_store.iter(col).map(Result::unwrap) { + check_key(first_store, second_store, col, &key); + num_checks += 1; + } + num_checks +} + +/// Calls get_ser on Store with provided temperature from provided NodeStorage. +/// Expects read to not result in errors. +fn get_ser_from_store( + store: &Store, + col: DBCol, + key: &[u8], +) -> Option { + store.get_ser(col, key).unwrap_or_else(|_| panic!("Error reading {} {:?} from store", col, key)) +} + +#[derive(clap::Parser)] +struct PrepareHotCmd { + /// The relative path to the rpc store that will be converted to a hot store. + /// The path should be relative to the home_dir e.g. hot_data + #[clap(short, long)] + store_relative_path: String, +} + +impl PrepareHotCmd { + pub fn run( + &self, + storage: &NodeStorage, + home_dir: &Path, + unc_config: &UncConfig, + ) -> anyhow::Result<()> { + let _span = tracing::info_span!(target: "prepare-hot", "run"); + + let path = Path::new(&self.store_relative_path); + tracing::info!(target : "prepare-hot", "Preparing a hot db from a rpc db at path {path:#?}."); + + tracing::info!(target : "prepare-hot", "Opening hot and cold."); + let hot_store = storage.get_hot_store(); + let cold_store = storage.get_cold_store(); + let cold_store = cold_store.ok_or(anyhow::anyhow!("The cold store is not configured!"))?; + + tracing::info!(target : "prepare-hot", "Opening rpc."); + // Open the rpc_storage using the unc_config with the path swapped. + let mut rpc_store_config = unc_config.config.store.clone(); + rpc_store_config.path = Some(path.to_path_buf()); + let rpc_opener = NodeStorage::opener(home_dir, false, &rpc_store_config, None); + let rpc_storage = rpc_opener.open()?; + let rpc_store = rpc_storage.get_hot_store(); + + tracing::info!(target : "prepare-hot", "Checking db kind"); + Self::check_db_kind(&hot_store, &cold_store, &rpc_store)?; + + tracing::info!(target : "prepare-hot", "Checking up to date"); + Self::check_up_to_date(&cold_store, &rpc_store)?; + + // TODO may be worth doing some simple sanity check that the rpc store + // and the cold store contain the same chain. Keep in mind that the + // responsibility of ensuring that the rpc backupd can be trusted lies + // with the node owner still. We don't want to do a full check here + // as it would take too long. + + tracing::info!(target : "prepare-hot", "The hot, cold and RPC stores are suitable for cold storage migration"); + tracing::info!(target : "prepare-hot", "Changing the DbKind of the RPC store to Hot"); + rpc_store.set_db_kind(DbKind::Hot)?; + + tracing::info!(target : "prepare-hot", "Successfully prepared the hot store for migration. You can now set the config.store.path in uncd config to {:#?}", path); + + Ok(()) + } + + /// Check that the DbKind of each of the stores is as expected. + fn check_db_kind( + hot_store: &Store, + cold_store: &Store, + rpc_store: &Store, + ) -> anyhow::Result<()> { + let hot_db_kind = hot_store.get_db_kind()?; + if hot_db_kind != Some(DbKind::Archive) { + return Err(anyhow::anyhow!( + "Unexpected hot_store DbKind, expected: {:?}, got: {:?}", + DbKind::Archive, + hot_db_kind, + )); + } + + let cold_db_kind = cold_store.get_db_kind()?; + if cold_db_kind != Some(DbKind::Cold) { + return Err(anyhow::anyhow!( + "Unexpected cold_store DbKind, expected: {:?}, got: {:?}", + DbKind::Cold, + cold_db_kind, + )); + } + + let rpc_db_kind = rpc_store.get_db_kind()?; + if rpc_db_kind != Some(DbKind::RPC) { + return Err(anyhow::anyhow!( + "Unexpected rpc_store DbKind, expected: {:?}, got: {:?}", + DbKind::RPC, + rpc_db_kind, + )); + } + + Ok(()) + } + + /// Check that the cold store and rpc store are sufficiently up to date. + fn check_up_to_date(cold_store: &Store, rpc_store: &Store) -> anyhow::Result<()> { + let rpc_head = rpc_store.get_ser::(DBCol::BlockMisc, HEAD_KEY)?; + let rpc_head = rpc_head.ok_or(anyhow::anyhow!("The rpc head is missing!"))?; + let rpc_tail = rpc_store.get_ser::(DBCol::BlockMisc, TAIL_KEY)?; + let rpc_tail = rpc_tail.ok_or(anyhow::anyhow!("The rpc tail is missing!"))?; + let cold_head = cold_store.get_ser::(DBCol::BlockMisc, HEAD_KEY)?; + let cold_head = cold_head.ok_or(anyhow::anyhow!("The cold head is missing"))?; + + // Ideally it should look like this: + // RPC T . . . . . . H + // COLD . . . ES . . H + + if cold_head.height < rpc_tail { + return Err(anyhow::anyhow!( + "The cold head is behind the rpc tail. cold head height: {} rpc tail height: {}", + cold_head.height, + rpc_tail + )); + } + + let cold_head_hash = cold_head.last_block_hash; + let cold_head_block_info = + rpc_store.get_ser::(DBCol::BlockInfo, cold_head_hash.as_ref())?; + let cold_head_block_info = + cold_head_block_info.ok_or(anyhow::anyhow!("Cold head block info is not in rpc db"))?; + let cold_epoch_first_block = *cold_head_block_info.epoch_first_block(); + let cold_epoch_first_block_info = + rpc_store.get_ser::(DBCol::BlockInfo, cold_epoch_first_block.as_ref())?; + + if cold_epoch_first_block_info.is_none() { + return Err(anyhow::anyhow!( + "The start of the latest epoch in cold db is behind the rpc tail.\ + cold head height: {}, rpc tail height: {}", + cold_head.height, + rpc_tail + )); + } + + // More likely the cold store head will actually be ahead of the rpc + // head, since rpc will be downloaded from S3 and a bit behind. + // It should be fine and we just end up copying a few blocks from hot to + // cold that already are present in cold. Just in case something doesn't + // work let's tell the user that we're in that state. + // RPC T . . . . . H + // COLD . . . . . . . . . H + + if cold_head.height > rpc_head.height { + tracing::warn!(target: "prepare-hot", + cold_head_height = cold_head.height, + rpc_head_height = rpc_head.height, + "The cold head is ahead of the RPC head. This should fix itself when the node catches up and becomes in sync" + ); + } + + Ok(()) + } +} + +/// The StateRootSelector is a subcommand that allows the user to select the state root either by block height or by the state root hash. +#[derive(clap::Subcommand)] +enum StateRootSelector { + Height { height: unc_primitives::types::BlockHeight }, + Hash { hash: CryptoHash }, +} + +impl StateRootSelector { + pub fn get_hashes( + &self, + storage: &NodeStorage, + cold_store: &Store, + ) -> anyhow::Result> { + match self { + // If height is provided, calculate previous state roots for this block's chunks. + StateRootSelector::Height { height } => { + let hash_key = { + let height_key = height.to_le_bytes(); + storage + .get_hot_store() + .get(DBCol::BlockHeight, &height_key)? + .ok_or(anyhow::anyhow!( + "Failed to find block hash for height {:?}", + height + ))? + .as_slice() + .to_vec() + }; + let block = cold_store + .get_ser::(DBCol::Block, &hash_key)? + .ok_or(anyhow::anyhow!("Failed to find Block: {:?}", hash_key))?; + let mut hashes = vec![]; + for chunk in block.chunks().iter() { + hashes.push( + cold_store + .get_ser::( + DBCol::Chunks, + chunk.chunk_hash().as_bytes(), + )? + .ok_or(anyhow::anyhow!( + "Failed to find Chunk: {:?}", + chunk.chunk_hash() + ))? + .take_header() + .prev_state_root(), + ); + } + Ok(hashes) + } + // If state root is provided, then just use it. + StateRootSelector::Hash { hash } => Ok(vec![*hash]), + } + } +} + +/// Struct that holds all conditions for node in Trie +/// to be checked by CheckStateRootCmd::check_trie. +#[derive(Debug)] +struct PruneCondition { + /// Maximum depth (measured in number of nodes, not trie key length). + max_depth: Option, + /// Maximum number of nodes checked for each state_root. + max_count: Option, +} + +/// Struct that holds data related to pruning of node in CheckStateRootCmd::check_trie. +#[derive(Debug)] +struct PruneState { + /// Depth of node in trie (measured in number of nodes, not trie key length). + depth: u64, + /// Number of already checked nodes. + count: u64, +} + +impl PruneState { + pub fn new() -> Self { + Self { depth: 0, count: 0 } + } + + /// Return `true` if node should be pruned. + pub fn should_prune(&self, condition: &PruneCondition) -> bool { + if let Some(md) = condition.max_depth { + if self.depth > md { + return true; + } + } + if let Some(mc) = condition.max_count { + if self.count > mc { + return true; + } + } + false + } + + /// Modify self to reflect going down a tree. + /// We increment node count, because we are visiting a new node. + pub fn down(&mut self) { + self.count += 1; + self.depth += 1; + } + + /// Modify self to reflect going up a tree. + /// We do not change node count, because we already visited parent node before. + pub fn up(&mut self) { + self.depth -= 1; + } +} + +#[derive(clap::Args)] +struct CheckStateRootCmd { + /// Maximum depth (measured in number of nodes, not trie key length) for checking trie. + #[clap(long)] + max_depth: Option, + /// Maximum number of nodes checked for each state_root. + #[clap(long)] + max_count: Option, + #[clap(subcommand)] + state_root_selector: StateRootSelector, +} + +impl CheckStateRootCmd { + pub fn run(self, storage: &NodeStorage) -> anyhow::Result<()> { + let cold_store = + storage.get_cold_store().ok_or(anyhow::anyhow!("Cold storage is not configured"))?; + + let hashes = self.state_root_selector.get_hashes(storage, &cold_store)?; + for hash in hashes.iter() { + Self::check_trie( + &cold_store, + &hash, + &mut PruneState::new(), + &PruneCondition { max_depth: self.max_depth, max_count: self.max_count }, + )?; + } + + Ok(()) + } + + /// Check that trie subtree of `hash` is fully present in `store`. + fn check_trie( + store: &Store, + hash: &CryptoHash, + prune_state: &mut PruneState, + prune_condition: &PruneCondition, + ) -> anyhow::Result<()> { + tracing::debug!(target: "check_trie", "Checking {:?} at {:?}", hash, prune_state); + if prune_state.should_prune(prune_condition) { + tracing::debug!(target: "check_trie", "Reached prune condition: {:?}", prune_condition); + return Ok(()); + } + + let bytes = Self::read_state(store, hash.as_ref()) + .with_context(|| format!("Failed to read raw bytes for hash {:?}", hash))? + .with_context(|| format!("Failed to find raw bytes for hash {:?}", hash))?; + let node = unc_store::RawTrieNodeWithSize::try_from_slice(&bytes)?; + match node.node { + unc_store::RawTrieNode::Leaf(..) => { + tracing::debug!(target: "check_trie", "Reached leaf node"); + return Ok(()); + } + unc_store::RawTrieNode::BranchNoValue(mut children) + | unc_store::RawTrieNode::BranchWithValue(_, mut children) => { + children.0.shuffle(&mut rand::thread_rng()); + for (_, child) in children.iter() { + // Record in prune state that we are visiting a child node + prune_state.down(); + // Visit a child node + Self::check_trie(store, child, prune_state, prune_condition)?; + // Record in prune state that we are returning from a child node + prune_state.up(); + } + } + unc_store::RawTrieNode::Extension(_, child) => { + // Record in prune state that we are visiting a child node + prune_state.down(); + // Visit a child node + Self::check_trie(store, &child, prune_state, prune_condition)?; + // Record in prune state that we are returning from a child node + prune_state.up(); + } + } + Ok(()) + } + + fn read_state<'a>( + store: &'a Store, + trie_key: &'a [u8], + ) -> std::io::Result>> { + // As cold db strips shard_uid at the beginning of State key, we can add any 8 u8s as prefix. + let cold_state_key = [&[1; 8], trie_key.as_ref()].concat(); + store.get(DBCol::State, &cold_state_key) + } +} diff --git a/tools/cold-store/src/lib.rs b/tools/cold-store/src/lib.rs new file mode 100644 index 000000000..7d392b8db --- /dev/null +++ b/tools/cold-store/src/lib.rs @@ -0,0 +1,2 @@ +pub mod cli; +pub use cli::ColdStoreCommand; diff --git a/tools/database/Cargo.toml b/tools/database/Cargo.toml new file mode 100644 index 000000000..11ca63a77 --- /dev/null +++ b/tools/database/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "unc-database-tool" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +borsh.workspace = true +clap.workspace = true +indicatif.workspace = true +rand.workspace = true +rayon.workspace = true +rocksdb.workspace = true +strum.workspace = true +tempfile.workspace = true + +framework.workspace = true +unc-epoch-manager.workspace = true +unc-chain.workspace = true +unc-chain-configs.workspace = true +unc-store.workspace = true +unc-primitives.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-chain/nightly", + "unc-epoch-manager/nightly", + "unc-primitives/nightly", + "unc-store/nightly", + "framework/nightly", +] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-chain/nightly_protocol", + "unc-epoch-manager/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", + "framework/nightly_protocol", +] diff --git a/tools/database/README.md b/tools/database/README.md new file mode 100644 index 000000000..dd5e28a48 --- /dev/null +++ b/tools/database/README.md @@ -0,0 +1,108 @@ +# Database + +A set of tools useful when working with the underlying database. + +## Analyse data size distribution + +The analyse database script provides an efficient way to assess the size distribution +of keys and values within RocksDB. + +### Usage + +To run the script, use the following example: +```bash +cargo run --bin uncd -- --home /home/ubuntu/.near database analyse-data-size-distribution --column State --top_k 50 +``` +The arguments are as follows: + + - `--home`: The path to the RocksDB directory. + - `--column`: The specific column to inspect. + - `--top_k`: The maximum number of counts to display (default is 100). + +The resulting output will show the following: + + - Total number of key-value pairs per column family + - Sizes of each column family + - Key and value size distribution + +### Tips for Handling Large Column Families +As this script is designed to read as many column families as possible at the start, +you may need to adjust the max_open_files limit on your operating system. + +For Ubuntu 18.04, you can check the current limit by using the command `ulimit -Hn`. +To adjust this limit, modify the `/etc/security/limits.conf` file and add the following +entry (adjust parameters to suit your needs): +``` +* soft nofile 100000 +* hard nofile 100000 +``` + +## Adjust-db tool +This is a tool that should only be used for testing purposes. +It is intended as a collection of commands that perform small db modifications. + + +### change-db-kind +Changes DbKind of a DB described in config (cold or hot). +Example usage: +```bash +cargo run --bin uncd -- --home /home/ubuntu/.near database change-db-kind --new-kind RPC change-cold +``` + +In this example we change DbKind of the cold db to RPC (for some reason). +Notice, that you cannot perform this exact command twice in a row, +because you will not be able to open cold db in the first place. +If you want to change DbKind of the cold db back, you would have to adjust your config: +- .store.path = `` +- .cold_store = null +- .archive = false + +This way uncd would try to open db at cold path as RPC db. +Then you can call +`uncd database change-db-kind --new-kind Cold change-hot`. +Notice that even though in your mind this db is cold, in your config this db hot, so you have to pass `change-hot`. + +## Compact database + +Run compaction on the SST files. Running this command might increase database read performance. +This is good use case when changing `block_size` and wishing to perform test on how the RocksDB performance has +changed. + +Example usage: +```bash +cargo run --bin uncd -- database compact-database +``` + + +## Make a DB Snapshot + +Makes a copy of a DB (hot store only) at a specified location. If the +destination is within the same filesystem, the copy will be made instantly and +take no additional disk space due to hardlinking all the files. + +Example usage: +```bash +cargo run --bin uncd -- --home /home/ubuntu/.near database make-snapshot --destination /home/ubuntu/.near/data/snapshot +``` + +In this example all `.sst` files from `/home/ubuntu/.near/data` will be also +available in `/home/ubuntu/.near/data/snapshot` + +This command can be helpful before attempting activities that can potentially +corrupt the database. + +### Run DB Migrations + +Opens the DB and runs migrations to bring it to the actual version expected by `uncd` +Example usage: +```bash +cargo run --bin uncd database run-migrations +``` + +For example, if the binary expects DB version `38`, but the DB is currently +version `36`, the command will open the DB, run migrations that bring the DB +from version `36` to version `38`, and then exits. + +## State read perf +A tool for performance testing hot storage RocksDB State column reads. +Use help to get more details: `uncd database state-perf --help` diff --git a/tools/database/src/adjust_database.rs b/tools/database/src/adjust_database.rs new file mode 100644 index 000000000..48b06427d --- /dev/null +++ b/tools/database/src/adjust_database.rs @@ -0,0 +1,48 @@ +use unc_store::metadata::DbKind; +use unc_store::NodeStorage; +use std::path::Path; + +/// This can potentially support db specified not in config, but in command line. +/// `ChangeRelative { path: Path, archive: bool }` +/// But it is a pain to implement, because of all the current storage possibilities. +/// So, I'll leave it as a TODO(posvyatokum): implement relative path DbSelector. +/// This can be useful workaround for config modification. +#[derive(clap::Subcommand)] +enum DbSelector { + ChangeHot, + ChangeCold, +} + +#[derive(clap::Args)] +pub(crate) struct ChangeDbKindCommand { + /// Desired DbKind. + #[clap(long)] + new_kind: DbKind, + /// Which db to change. + #[clap(subcommand)] + db_selector: DbSelector, +} + +impl ChangeDbKindCommand { + pub(crate) fn run(&self, home_dir: &Path) -> anyhow::Result<()> { + let unc_config = framework::config::load_config( + &home_dir, + unc_chain_configs::GenesisValidationMode::UnsafeFast, + )?; + let opener = NodeStorage::opener( + home_dir, + unc_config.config.archive, + &unc_config.config.store, + unc_config.config.cold_store.as_ref(), + ); + + let storage = opener.open()?; + let store = match self.db_selector { + DbSelector::ChangeHot => storage.get_hot_store(), + DbSelector::ChangeCold => { + storage.get_cold_store().ok_or(anyhow::anyhow!("No cold store"))? + } + }; + Ok(store.set_db_kind(self.new_kind)?) + } +} diff --git a/tools/database/src/analyse_data_size_distribution.rs b/tools/database/src/analyse_data_size_distribution.rs new file mode 100644 index 000000000..734744e22 --- /dev/null +++ b/tools/database/src/analyse_data_size_distribution.rs @@ -0,0 +1,201 @@ +use clap::Parser; +use unc_store::db::{Database, RocksDB}; +use unc_store::DBCol; +use rayon::prelude::*; +use std::collections::HashMap; +use std::path::PathBuf; +use std::sync::{Arc, Mutex}; +use std::{panic, println}; +use strum::IntoEnumIterator; + +use crate::utils::{open_rocksdb, resolve_column}; + +#[derive(Parser)] +pub(crate) struct AnalyseDataSizeDistributionCommand { + /// If specified only this column will be analysed + #[arg(short, long)] + column: Option, + + /// Number of count sizes to output + #[arg(short, long, default_value_t = 100)] + top_k: usize, +} + +#[derive(Clone)] +struct ColumnFamilyCountAndSize { + number_of_pairs: usize, + size: usize, +} + +struct DataSizeDistribution { + key_sizes: Vec<(usize, usize)>, + value_sizes: Vec<(usize, usize)>, + total_num_of_pairs: usize, + column_families_data: Vec<(String, ColumnFamilyCountAndSize)>, +} + +impl DataSizeDistribution { + fn new( + mut key_sizes: Vec<(usize, usize)>, + mut value_sizes: Vec<(usize, usize)>, + col_families_data: Vec<(String, ColumnFamilyCountAndSize)>, + ) -> Self { + // The reason we sort here is because we want to display sorted + // output that shows the most occurring sizes (the ones with the + // biggest count) in descending order, to have histogram like order + key_sizes.sort_by(|a, b| b.1.cmp(&a.1)); + value_sizes.sort_by(|a, b| b.1.cmp(&a.1)); + let total_num_of_pairs = key_sizes.iter().map(|(_, count)| count).sum::(); + + Self { + key_sizes: key_sizes, + value_sizes: value_sizes, + total_num_of_pairs: total_num_of_pairs, + column_families_data: col_families_data, + } + } + + fn print_results(&self, top_k: usize) { + self.print_column_family_data(); + self.print_sizes_count(&self.key_sizes, "Key", top_k); + self.print_sizes_count(&self.value_sizes, "Value", top_k); + } + + fn print_column_family_data(&self) { + for (column_family_name, column_family_data) in self.column_families_data.iter() { + println!( + "Column family {} has {} number of pairs and {} bytes size", + column_family_name, column_family_data.number_of_pairs, column_family_data.size + ); + } + } + + fn print_sizes_count( + &self, + sizes_count: &Vec<(usize, usize)>, + size_count_type: &str, + top_k: usize, + ) { + println!( + "Total number of pairs read {}\n", + sizes_count.into_iter().map(|(_, count)| count).sum::() + ); + + // Print out distributions + println!("{} Size Distribution:", size_count_type); + println!( + "Minimum size {}: {:?}", + size_count_type, + sizes_count.iter().map(|(size, _)| size).min().unwrap() + ); + println!( + "Maximum size {}: {:?}", + size_count_type, + sizes_count.iter().map(|(size, _)| size).max().unwrap() + ); + println!("Most occurring size {}: {:?}", size_count_type, sizes_count.first().unwrap()); + println!("Least occurring size {}: {:?}", size_count_type, sizes_count.last().unwrap()); + + let total_sizes_bytes_sum = sizes_count.iter().map(|a| a.0 * a.1).sum::(); + println!( + "Average size {}: {:?}", + size_count_type, + total_sizes_bytes_sum as f64 / self.total_num_of_pairs as f64 + ); + let mut size_bytes_median = 0; + let mut median_index = self.total_num_of_pairs / 2; + for (size, count) in sizes_count.iter().take(top_k) { + if median_index < *count { + size_bytes_median = *size; + break; + } else { + median_index -= count; + } + } + println!("Median size {} {}", size_count_type, size_bytes_median); + for (size, count) in sizes_count.iter().take(top_k) { + println!("Size: {}, Count: {}", size, count); + } + println!(""); + } +} + +fn read_all_pairs(db: &RocksDB, col_families: &Vec) -> DataSizeDistribution { + // Initialize counters + let key_sizes: Arc>> = Arc::new(Mutex::new(HashMap::new())); + let value_sizes: Arc>> = Arc::new(Mutex::new(HashMap::new())); + let column_families_data: Arc>> = + Arc::new(Mutex::new(HashMap::new())); + + // Iterate over key-value pairs + let update_map = |global_map: &Arc>>, + local_map: &HashMap| { + let mut global_sizes_guard = global_map.lock().unwrap(); + for (key, value) in local_map { + *global_sizes_guard.entry(*key).or_insert(0) += *value; + } + }; + col_families.par_iter().for_each(|col_family| { + let mut local_key_sizes: HashMap = HashMap::new(); + let mut local_value_sizes: HashMap = HashMap::new(); + + //let cf_handle = db.cf_handle(col_family).unwrap(); + for res in db.iter_raw_bytes(*col_family) { + match res { + Ok(tuple) => { + // Count key sizes + let key_len = tuple.0.len(); + *local_key_sizes.entry(key_len).or_insert(0) += 1; + + // Count value sizes + let value_len = tuple.1.len(); + *local_value_sizes.entry(value_len).or_insert(0) += 1; + } + Err(err) => { + panic!("Error occurred during iteration of {}: {}", col_family, err); + } + } + } + + { + let mut guard = column_families_data.lock().unwrap(); + let column_number_of_pairs = local_key_sizes.values().sum::(); + let column_size = + local_key_sizes.iter().map(|(&size, &count)| size * count).sum::(); + let column_family = ColumnFamilyCountAndSize { + number_of_pairs: column_number_of_pairs, + size: column_size, + }; + guard.insert(col_family.to_string(), column_family); + } + + update_map(&key_sizes, &local_key_sizes); + update_map(&value_sizes, &local_value_sizes); + }); + + let key_sizes: Vec<(usize, usize)> = key_sizes.lock().unwrap().clone().into_iter().collect(); + let value_sizes: Vec<(usize, usize)> = + value_sizes.lock().unwrap().clone().into_iter().collect(); + let column_families: Vec<(String, ColumnFamilyCountAndSize)> = + column_families_data.lock().unwrap().clone().into_iter().collect(); + + DataSizeDistribution::new(key_sizes, value_sizes, column_families) +} + +fn get_column_families(input_col: &Option) -> anyhow::Result> { + match input_col { + Some(column_name) => Ok(vec![resolve_column(column_name)?]), + None => Ok(DBCol::iter().collect()), + } +} + +impl AnalyseDataSizeDistributionCommand { + pub(crate) fn run(&self, home: &PathBuf) -> anyhow::Result<()> { + let db = open_rocksdb(home, unc_store::Mode::ReadOnly)?; + let column_families = get_column_families(&self.column)?; + let results = read_all_pairs(&db, &column_families); + results.print_results(self.top_k); + + Ok(()) + } +} diff --git a/tools/database/src/analyse_gas_usage.rs b/tools/database/src/analyse_gas_usage.rs new file mode 100644 index 000000000..3ec0c32ab --- /dev/null +++ b/tools/database/src/analyse_gas_usage.rs @@ -0,0 +1,589 @@ +use std::collections::{BTreeMap, BTreeSet}; +use std::path::PathBuf; +use std::rc::Rc; + +use clap::Parser; +use unc_chain::{Block, ChainStore}; +use unc_chain_configs::GenesisValidationMode; +use unc_epoch_manager::EpochManager; +use framework::config::load_config; + +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::{account_id_to_shard_id, ShardUId}; +use unc_primitives::types::{AccountId, BlockHeight}; + +use framework::open_storage; + +use crate::block_iterators::{ + make_block_iterator_from_command_args, CommandArgs, LastNBlocksIterator, +}; + +/// `Gas` is an u64, but it stil might overflow when analysing a large amount of blocks. +/// 1ms of compute is about 1TGas = 10^12 gas. One epoch takes 43200 seconds (43200000ms). +/// This means that the amount of gas consumed during a single epoch can reach 43200000 * 10^12 = 4.32 * 10^19 +/// 10^19 doesn't fit in u64, so we need to use u128 +/// To avoid overflows, let's use `BigGas` for storing gas amounts in the code. +type BigGas = u128; + +/// Display gas amount in a human-friendly way +fn display_gas(gas: BigGas) -> String { + let tera_gas = gas as f64 / 1e12; + format!("{:.2} TGas", tera_gas) +} + +#[derive(Parser)] +pub(crate) struct AnalyseGasUsageCommand { + /// Analyse the last N blocks in the blockchain + #[arg(long)] + last_blocks: Option, + + /// Analyse blocks from the given block height, inclusive + #[arg(long)] + from_block_height: Option, + + /// Analyse blocks up to the given block height, inclusive + #[arg(long)] + to_block_height: Option, +} + +impl AnalyseGasUsageCommand { + pub(crate) fn run(&self, home: &PathBuf) -> anyhow::Result<()> { + // Create a ChainStore and EpochManager that will be used to read blockchain data. + let mut unc_config = load_config(home, GenesisValidationMode::Full).unwrap(); + let node_storage = open_storage(&home, &mut unc_config).unwrap(); + let store = node_storage.get_split_store().unwrap_or_else(|| node_storage.get_hot_store()); + let chain_store = Rc::new(ChainStore::new( + store.clone(), + unc_config.genesis.config.genesis_height, + false, + )); + let epoch_manager = + EpochManager::new_from_genesis_config(store, &unc_config.genesis.config).unwrap(); + + // Create an iterator over the blocks that should be analysed + let blocks_iter_opt = make_block_iterator_from_command_args( + CommandArgs { + last_blocks: self.last_blocks, + from_block_height: self.from_block_height, + to_block_height: self.to_block_height, + }, + chain_store.clone(), + ); + + let blocks_iter = match blocks_iter_opt { + Some(iter) => iter, + None => { + println!("No arguments, defaulting to last 100 blocks"); + Box::new(LastNBlocksIterator::new(100, chain_store.clone())) + } + }; + + // Analyse + analyse_gas_usage(blocks_iter, &chain_store, &epoch_manager); + + Ok(()) + } +} + +#[derive(Clone, Debug, Default)] +struct GasUsageInShard { + pub used_gas_per_account: BTreeMap, +} + +/// A shard can be split into two halves. +/// This struct represents the result of splitting a shard at `boundary_account`. +#[derive(Debug, Clone, PartialEq, Eq)] +struct ShardSplit { + /// Account on which the shard would be split + pub boundary_account: AccountId, + /// Gas used by accounts < boundary_account + pub gas_left: BigGas, + /// Gas used by accounts => boundary_account + pub gas_right: BigGas, +} + +impl GasUsageInShard { + pub fn new() -> GasUsageInShard { + GasUsageInShard { used_gas_per_account: BTreeMap::new() } + } + + pub fn add_used_gas(&mut self, account: AccountId, used_gas: BigGas) { + let account_gas = self.used_gas_per_account.entry(account).or_insert(0); + *account_gas = account_gas.checked_add(used_gas).unwrap(); + } + + pub fn used_gas_total(&self) -> BigGas { + let mut result: BigGas = 0; + for used_gas in self.used_gas_per_account.values() { + result = result.checked_add(*used_gas).unwrap(); + } + result + } + + pub fn merge(&mut self, other: &GasUsageInShard) { + for (account_id, used_gas) in &other.used_gas_per_account { + self.add_used_gas(account_id.clone(), *used_gas); + } + } + + /// Calculate the optimal point at which this shard could be split into two halves with similar gas usage + pub fn calculate_split(&self) -> Option { + let total_gas = self.used_gas_total(); + if total_gas == 0 || self.used_gas_per_account.len() < 2 { + return None; + } + + // Find a split with the smallest difference between the two halves + let mut best_split: Option = None; + let mut best_difference: BigGas = total_gas; + + let mut gas_left: BigGas = 0; + let mut gas_right: BigGas = total_gas; + + for (account, used_gas) in &self.used_gas_per_account { + // We are now considering a split of (left < account) and (right >= account) + + let difference: BigGas = gas_left.abs_diff(gas_right); + if difference < best_difference { + best_difference = difference; + best_split = + Some(ShardSplit { boundary_account: account.clone(), gas_left, gas_right }); + } + + gas_left = gas_left.checked_add(*used_gas).unwrap(); + gas_right = gas_right.checked_sub(*used_gas).unwrap(); + } + best_split + } + + pub fn biggest_account(&self) -> Option<(AccountId, BigGas)> { + let mut result: Option<(AccountId, BigGas)> = None; + + for (account, used_gas) in &self.used_gas_per_account { + match &mut result { + None => result = Some((account.clone(), *used_gas)), + Some((best_account, best_gas)) => { + if *used_gas > *best_gas { + *best_account = account.clone(); + *best_gas = *used_gas + } + } + } + } + + result + } +} + +#[derive(Clone, Debug)] +struct GasUsageStats { + pub shards: BTreeMap, +} + +impl GasUsageStats { + pub fn new() -> GasUsageStats { + GasUsageStats { shards: BTreeMap::new() } + } + + pub fn add_gas_usage_in_shard(&mut self, shard_uid: ShardUId, shard_usage: GasUsageInShard) { + match self.shards.get_mut(&shard_uid) { + Some(existing_shard_usage) => existing_shard_usage.merge(&shard_usage), + None => { + let _ = self.shards.insert(shard_uid, shard_usage); + } + } + } + + pub fn used_gas_total(&self) -> BigGas { + let mut result: BigGas = 0; + for shard_usage in self.shards.values() { + result = result.checked_add(shard_usage.used_gas_total()).unwrap(); + } + result + } + + pub fn merge(&mut self, other: GasUsageStats) { + for (shard_uid, shard_usage) in other.shards { + self.add_gas_usage_in_shard(shard_uid, shard_usage); + } + } +} + +fn get_gas_usage_in_block( + block: &Block, + chain_store: &ChainStore, + epoch_manager: &EpochManager, +) -> GasUsageStats { + let block_info = epoch_manager.get_block_info(block.hash()).unwrap(); + let epoch_id = block_info.epoch_id(); + let shard_layout = epoch_manager.get_shard_layout(epoch_id).unwrap(); + + let mut result = GasUsageStats::new(); + + // Go over every chunk in this block and gather data + for chunk_header in block.chunks().iter() { + let shard_id = chunk_header.shard_id(); + let shard_uid = ShardUId::from_shard_id_and_layout(shard_id, &shard_layout); + + let mut gas_usage_in_shard = GasUsageInShard::new(); + + // The outcome of each transaction and receipt executed in this chunk is saved in the database as an ExecutionOutcome. + // Go through all ExecutionOutcomes from this chunk and record the gas usage. + let outcome_ids = + chain_store.get_outcomes_by_block_hash_and_shard_id(block.hash(), shard_id).unwrap(); + for outcome_id in outcome_ids { + let outcome = chain_store + .get_outcome_by_id_and_block_hash(&outcome_id, block.hash()) + .unwrap() + .unwrap() + .outcome; + + // Sanity check - make sure that the executor of this outcome belongs to this shard + let account_shard_id = account_id_to_shard_id(&outcome.executor_id, &shard_layout); + assert_eq!(account_shard_id, shard_id); + + gas_usage_in_shard.add_used_gas(outcome.executor_id, outcome.gas_burnt.into()); + } + + result.add_gas_usage_in_shard(shard_uid, gas_usage_in_shard); + } + + result +} + +/// A struct that can be used to find N biggest accounts by gas usage in an efficient manner. +struct BiggestAccountsFinder { + accounts: BTreeSet<(BigGas, AccountId)>, + accounts_num: usize, +} + +impl BiggestAccountsFinder { + pub fn new(accounts_num: usize) -> BiggestAccountsFinder { + BiggestAccountsFinder { accounts: BTreeSet::new(), accounts_num } + } + + pub fn add_account_stats(&mut self, account: AccountId, used_gas: BigGas) { + self.accounts.insert((used_gas, account)); + + // If there are more accounts than desired, remove the one with the smallest gas usage + while self.accounts.len() > self.accounts_num { + self.accounts.pop_first(); + } + } + + pub fn get_biggest_accounts(&self) -> impl Iterator + '_ { + self.accounts.iter().rev().map(|(gas, account)| (account.clone(), *gas)) + } +} + +// Calculates how much percent of `big` is `small` and returns it as a string. +// Example: as_percentage_of(10, 100) == "10.0%" +fn as_percentage_of(small: BigGas, big: BigGas) -> String { + if big > 0 { + format!("{:.1}%", small as f64 / big as f64 * 100.0) + } else { + format!("-") + } +} + +fn display_shard_split_stats<'a>( + accounts: impl Iterator, + total_shard_gas: BigGas, +) { + let mut accounts_num: u64 = 0; + let mut total_split_half_gas: BigGas = 0; + let mut top_3_finder = BiggestAccountsFinder::new(3); + + for (account, used_gas) in accounts { + accounts_num += 1; + total_split_half_gas = total_split_half_gas.checked_add(*used_gas).unwrap(); + top_3_finder.add_account_stats(account.clone(), *used_gas); + } + + let indent = " "; + println!( + "{}Gas: {} ({} of shard)", + indent, + display_gas(total_split_half_gas), + as_percentage_of(total_split_half_gas, total_shard_gas) + ); + println!("{}Accounts: {}", indent, accounts_num); + println!("{}Top 3 accounts:", indent); + for (i, (account, used_gas)) in top_3_finder.get_biggest_accounts().enumerate() { + println!("{} #{}: {}", indent, i + 1, account); + println!( + "{} Used gas: {} ({} of shard)", + indent, + display_gas(used_gas), + as_percentage_of(used_gas, total_shard_gas) + ) + } +} + +fn analyse_gas_usage( + blocks_iter: impl Iterator, + chain_store: &ChainStore, + epoch_manager: &EpochManager, +) { + // Gather statistics about gas usage in all of the blocks + let mut blocks_count: usize = 0; + let mut first_analysed_block: Option<(BlockHeight, CryptoHash)> = None; + let mut last_analysed_block: Option<(BlockHeight, CryptoHash)> = None; + + let mut gas_usage_stats = GasUsageStats::new(); + + for block in blocks_iter { + blocks_count += 1; + if first_analysed_block.is_none() { + first_analysed_block = Some((block.header().height(), *block.hash())); + } + last_analysed_block = Some((block.header().height(), *block.hash())); + + let gas_usage_in_block = get_gas_usage_in_block(&block, chain_store, epoch_manager); + gas_usage_stats.merge(gas_usage_in_block); + } + + // Print out the analysis + if blocks_count == 0 { + println!("No blocks to analyse!"); + return; + } + println!(""); + println!("Analysed {} blocks between:", blocks_count); + if let Some((block_height, block_hash)) = first_analysed_block { + println!("Block: height = {block_height}, hash = {block_hash}"); + } + if let Some((block_height, block_hash)) = last_analysed_block { + println!("Block: height = {block_height}, hash = {block_hash}"); + } + let total_gas = gas_usage_stats.used_gas_total(); + println!(""); + println!("Total gas used: {}", display_gas(total_gas)); + println!(""); + for (shard_uid, shard_usage) in &gas_usage_stats.shards { + println!("Shard: {}", shard_uid); + let shard_total_gas = shard_usage.used_gas_total(); + println!( + " Gas usage: {} ({} of total)", + display_gas(shard_usage.used_gas_total()), + as_percentage_of(shard_total_gas, total_gas) + ); + println!(" Number of accounts: {}", shard_usage.used_gas_per_account.len()); + if let Some((biggest_account, biggest_account_gas)) = shard_usage.biggest_account() { + println!(" Biggest account: {}", biggest_account); + println!( + " Biggest account gas: {} ({} of shard)", + display_gas(biggest_account_gas), + as_percentage_of(biggest_account_gas, shard_total_gas) + ); + } + match shard_usage.calculate_split() { + Some(shard_split) => { + println!(" Optimal split:"); + println!(" boundary_account: {}", shard_split.boundary_account); + let boundary_account_gas = + *shard_usage.used_gas_per_account.get(&shard_split.boundary_account).unwrap(); + println!(" gas(boundary_account): {}", display_gas(boundary_account_gas)); + println!( + " Gas distribution (left, boundary_acc, right): ({}, {}, {})", + as_percentage_of(shard_split.gas_left, shard_total_gas), + as_percentage_of(boundary_account_gas, shard_total_gas), + as_percentage_of( + shard_split.gas_right.saturating_sub(boundary_account_gas), + shard_total_gas + ) + ); + println!(" Left (account < boundary_account):"); + let left_accounts = + shard_usage.used_gas_per_account.range(..shard_split.boundary_account.clone()); + display_shard_split_stats(left_accounts, shard_total_gas); + println!(" Right (account >= boundary_account):"); + let right_accounts = + shard_usage.used_gas_per_account.range(shard_split.boundary_account..); + display_shard_split_stats(right_accounts, shard_total_gas); + } + None => println!(" No optimal split for this shard"), + } + println!(""); + } + + // Find 10 biggest accounts by gas usage + let mut biggest_accounts_finder = BiggestAccountsFinder::new(10); + for shard in gas_usage_stats.shards.values() { + for (account, used_gas) in &shard.used_gas_per_account { + biggest_accounts_finder.add_account_stats(account.clone(), *used_gas); + } + } + println!("10 biggest accounts by gas usage:"); + for (i, (account, gas_usage)) in biggest_accounts_finder.get_biggest_accounts().enumerate() { + println!("#{}: {}", i + 1, account); + println!( + " Used gas: {} ({} of total)", + display_gas(gas_usage), + as_percentage_of(gas_usage, total_gas) + ) + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use unc_primitives::types::AccountId; + + use super::{GasUsageInShard, ShardSplit}; + + fn account(name: &str) -> AccountId { + AccountId::from_str(&format!("{name}.near")).unwrap() + } + + // There is no optimal split for a shard with no accounts + #[test] + fn empty_shard_no_split() { + let empty_shard = GasUsageInShard::new(); + assert_eq!(empty_shard.calculate_split(), None); + } + + // There is no optimal split for a shard with a single account + #[test] + fn one_account_no_split() { + let mut shard_usage = GasUsageInShard::new(); + + shard_usage.add_used_gas(account("a"), 12345); + + assert_eq!(shard_usage.calculate_split(), None); + } + + // A shard with two equally sized accounts should be split in half + #[test] + fn two_accounts_equal_split() { + let mut shard_usage = GasUsageInShard::new(); + + shard_usage.add_used_gas(account("a"), 12345); + shard_usage.add_used_gas(account("b"), 12345); + + let optimal_split = + ShardSplit { boundary_account: account("b"), gas_left: 12345, gas_right: 12345 }; + assert_eq!(shard_usage.calculate_split(), Some(optimal_split)); + } + + // A shard with two accounts where the first is slightly smaller should be split in half + #[test] + fn two_accounts_first_smaller_split() { + let mut shard_usage = GasUsageInShard::new(); + + shard_usage.add_used_gas(account("a"), 123); + shard_usage.add_used_gas(account("b"), 12345); + + let optimal_split = + ShardSplit { boundary_account: account("b"), gas_left: 123, gas_right: 12345 }; + assert_eq!(shard_usage.calculate_split(), Some(optimal_split)); + } + + // A shard with two accounts where the second one is slightly smaller should be split in half + #[test] + fn two_accounts_second_smaller_split() { + let mut shard_usage = GasUsageInShard::new(); + + shard_usage.add_used_gas(account("a"), 12345); + shard_usage.add_used_gas(account("b"), 123); + + let optimal_split = + ShardSplit { boundary_account: account("b"), gas_left: 12345, gas_right: 123 }; + assert_eq!(shard_usage.calculate_split(), Some(optimal_split)); + } + + // A shard with multiple accounts where all of them use 0 gas has optimal split + #[test] + fn many_accounts_zero_no_split() { + let mut shard_usage = GasUsageInShard::new(); + + shard_usage.add_used_gas(account("a"), 0); + shard_usage.add_used_gas(account("b"), 0); + shard_usage.add_used_gas(account("c"), 0); + shard_usage.add_used_gas(account("d"), 0); + shard_usage.add_used_gas(account("e"), 0); + shard_usage.add_used_gas(account("f"), 0); + shard_usage.add_used_gas(account("g"), 0); + shard_usage.add_used_gas(account("h"), 0); + + assert_eq!(shard_usage.calculate_split(), None); + } + + // A shard with multiple accounts where only one is nonzero has no optimal split + #[test] + fn many_accounts_one_nonzero_no_split() { + let mut shard_usage = GasUsageInShard::new(); + + shard_usage.add_used_gas(account("a"), 0); + shard_usage.add_used_gas(account("b"), 0); + shard_usage.add_used_gas(account("c"), 0); + shard_usage.add_used_gas(account("d"), 0); + shard_usage.add_used_gas(account("e"), 12345); + shard_usage.add_used_gas(account("f"), 0); + shard_usage.add_used_gas(account("g"), 0); + shard_usage.add_used_gas(account("h"), 0); + + assert_eq!(shard_usage.calculate_split(), None); + } + + // An example set of accounts is split correctly + #[test] + fn many_accounts_split() { + let mut shard_usage = GasUsageInShard::new(); + + shard_usage.add_used_gas(account("a"), 1); + shard_usage.add_used_gas(account("b"), 3); + shard_usage.add_used_gas(account("c"), 5); + shard_usage.add_used_gas(account("d"), 2); + shard_usage.add_used_gas(account("e"), 8); + shard_usage.add_used_gas(account("f"), 1); + shard_usage.add_used_gas(account("g"), 2); + shard_usage.add_used_gas(account("h"), 8); + + // Optimal split: + // 1 + 3 + 5 + 2 = 11 + // 8 + 1 + 2 + 8 = 19 + let optimal_split = + ShardSplit { boundary_account: account("e"), gas_left: 11, gas_right: 19 }; + assert_eq!(shard_usage.calculate_split(), Some(optimal_split)); + } + + // The first account uses the most gas, it should be the only one in its half of the split + #[test] + fn first_heavy_split() { + let mut shard_usage = GasUsageInShard::new(); + + shard_usage.add_used_gas(account("a"), 10000); + shard_usage.add_used_gas(account("b"), 1); + shard_usage.add_used_gas(account("c"), 1); + shard_usage.add_used_gas(account("d"), 1); + shard_usage.add_used_gas(account("e"), 1); + shard_usage.add_used_gas(account("f"), 1); + shard_usage.add_used_gas(account("g"), 1); + shard_usage.add_used_gas(account("h"), 1); + + let optimal_split = + ShardSplit { boundary_account: account("b"), gas_left: 10000, gas_right: 7 }; + assert_eq!(shard_usage.calculate_split(), Some(optimal_split)); + } + + // The last account uses the most gas, it should be the only one in its half of the split + #[test] + fn last_heavy_split() { + let mut shard_usage = GasUsageInShard::new(); + + shard_usage.add_used_gas(account("a"), 1); + shard_usage.add_used_gas(account("b"), 1); + shard_usage.add_used_gas(account("c"), 1); + shard_usage.add_used_gas(account("d"), 1); + shard_usage.add_used_gas(account("e"), 1); + shard_usage.add_used_gas(account("f"), 1); + shard_usage.add_used_gas(account("g"), 1); + shard_usage.add_used_gas(account("h"), 10000); + + let optimal_split = + ShardSplit { boundary_account: account("h"), gas_left: 7, gas_right: 10000 }; + assert_eq!(shard_usage.calculate_split(), Some(optimal_split)); + } +} diff --git a/tools/database/src/block_iterators/height_range.rs b/tools/database/src/block_iterators/height_range.rs new file mode 100644 index 000000000..9fed7d889 --- /dev/null +++ b/tools/database/src/block_iterators/height_range.rs @@ -0,0 +1,86 @@ +use std::rc::Rc; + +use unc_chain::{Block, ChainStore, ChainStoreAccess, Error}; +use unc_primitives::{hash::CryptoHash, types::BlockHeight}; + +/// Iterate over blocks between two block heights. +/// `from_height` and `to_height` are inclusive +pub struct BlockHeightRangeIterator { + chain_store: Rc, + from_block_height: BlockHeight, + /// Hash of the block that will be returned when next() is called + current_block_hash: Option, +} + +impl BlockHeightRangeIterator { + /// Create an iterator which iterates over blocks between from_height and to_height. + /// `from_height` and `to_height` are inclusive. + /// Both arguments are optional, passing `None`` means that the limit is +- infinity. + pub fn new( + from_height_opt: Option, + to_height_opt: Option, + chain_store: Rc, + ) -> BlockHeightRangeIterator { + if let (Some(from), Some(to)) = (&from_height_opt, &to_height_opt) { + if *from > *to { + // Empty iterator + return BlockHeightRangeIterator { + chain_store, + from_block_height: 0, + current_block_hash: None, + }; + } + } + + let min_height = chain_store.get_genesis_height(); + let max_height = chain_store.head().unwrap().height; + + let from_height = from_height_opt.unwrap_or(min_height); + let mut to_height = to_height_opt.unwrap_or(max_height); + + // There is no point in going over nonexisting blocks past the highest height + if to_height > max_height { + to_height = max_height; + } + + // A block with height `to_height` might not exist. + // Go over the range in reverse and find the highest block that exists. + let mut current_block_hash: Option = None; + for height in (from_height..=to_height).rev() { + match chain_store.get_block_hash_by_height(height) { + Ok(hash) => { + current_block_hash = Some(hash); + break; + } + Err(Error::DBNotFoundErr(_)) => continue, + err => err.unwrap(), + }; + } + + BlockHeightRangeIterator { chain_store, from_block_height: from_height, current_block_hash } + } +} + +impl Iterator for BlockHeightRangeIterator { + type Item = Block; + + fn next(&mut self) -> Option { + let current_block_hash = match self.current_block_hash.take() { + Some(hash) => hash, + None => return None, + }; + let current_block = self.chain_store.get_block(¤t_block_hash).unwrap(); + + // Make sure that the block is within the from..=to range, stop iterating if it isn't + if current_block.header().height() >= self.from_block_height { + // Set the previous block as "current" one, as long as the current one isn't the genesis block + if current_block.header().height() != self.chain_store.get_genesis_height() { + self.current_block_hash = Some(*current_block.header().prev_hash()); + } + + return Some(current_block); + } + + None + } +} diff --git a/tools/database/src/block_iterators/last_blocks.rs b/tools/database/src/block_iterators/last_blocks.rs new file mode 100644 index 000000000..f20b72f6b --- /dev/null +++ b/tools/database/src/block_iterators/last_blocks.rs @@ -0,0 +1,43 @@ +use std::rc::Rc; + +use unc_chain::{Block, ChainStore, ChainStoreAccess}; +use unc_primitives::hash::CryptoHash; + +/// Iterate over the last N blocks in the blockchain +pub struct LastNBlocksIterator { + chain_store: Rc, + blocks_left: u64, + /// Hash of the block that will be returned when next() is called + current_block_hash: Option, +} + +impl LastNBlocksIterator { + pub fn new(blocks_num: u64, chain_store: Rc) -> LastNBlocksIterator { + let current_block_hash = Some(chain_store.head().unwrap().last_block_hash); + LastNBlocksIterator { chain_store, blocks_left: blocks_num, current_block_hash } + } +} + +impl Iterator for LastNBlocksIterator { + type Item = Block; + + fn next(&mut self) -> Option { + // Decrease the amount of blocks left to produce + match self.blocks_left.checked_sub(1) { + Some(new_blocks_left) => self.blocks_left = new_blocks_left, + None => return None, + }; + + if let Some(current_block_hash) = self.current_block_hash.take() { + let current_block = self.chain_store.get_block(¤t_block_hash).unwrap(); + + // Set the previous block as "current" one, as long as the current one isn't the genesis block + if current_block.header().height() != self.chain_store.get_genesis_height() { + self.current_block_hash = Some(*current_block.header().prev_hash()); + } + return Some(current_block); + } + + None + } +} diff --git a/tools/database/src/block_iterators/mod.rs b/tools/database/src/block_iterators/mod.rs new file mode 100644 index 000000000..9a838cee7 --- /dev/null +++ b/tools/database/src/block_iterators/mod.rs @@ -0,0 +1,61 @@ +//! This module contains iterators that can be used to iterate over blocks in the database + +mod height_range; +mod last_blocks; + +use std::rc::Rc; + +use unc_chain::{Block, ChainStore}; +use unc_primitives::types::BlockHeight; + +/// Iterate over blocks between two block heights. +/// `from_height` and `to_height` are inclusive +pub use height_range::BlockHeightRangeIterator; + +/// Iterate over the last N blocks in the blockchain +pub use last_blocks::LastNBlocksIterator; + +/// Arguments that user can pass to a command to choose some subset of blocks +pub struct CommandArgs { + /// Analyse the last N blocks + pub last_blocks: Option, + + /// Analyse blocks from the given block height, inclusive + pub from_block_height: Option, + + /// Analyse blocks up to the given block height, inclusive + pub to_block_height: Option, +} + +/// Produce to right iterator for a given set of command line arguments +pub fn make_block_iterator_from_command_args( + command_args: CommandArgs, + chain_store: Rc, +) -> Option>> { + // Make sure that only one type of argument is used (there is no mixing of last_blocks and from_block_height) + let mut arg_types_used: u64 = 0; + if command_args.last_blocks.is_some() { + arg_types_used += 1; + } + if command_args.from_block_height.is_some() || command_args.from_block_height.is_some() { + arg_types_used += 1; + } + + if arg_types_used > 1 { + panic!("It is illegal to mix multiple types of arguments specifying a subset of blocks"); + } + + if let Some(last_blocks) = command_args.last_blocks { + return Some(Box::new(LastNBlocksIterator::new(last_blocks, chain_store))); + } + + if command_args.from_block_height.is_some() || command_args.to_block_height.is_some() { + return Some(Box::new(BlockHeightRangeIterator::new( + command_args.from_block_height, + command_args.to_block_height, + chain_store, + ))); + } + + None +} diff --git a/tools/database/src/commands.rs b/tools/database/src/commands.rs new file mode 100644 index 000000000..637677c67 --- /dev/null +++ b/tools/database/src/commands.rs @@ -0,0 +1,79 @@ +use crate::adjust_database::ChangeDbKindCommand; +use crate::analyse_data_size_distribution::AnalyseDataSizeDistributionCommand; +use crate::analyse_gas_usage::AnalyseGasUsageCommand; +use crate::compact::RunCompactionCommand; +use crate::corrupt::CorruptStateSnapshotCommand; +use crate::make_snapshot::MakeSnapshotCommand; +use crate::memtrie::LoadMemTrieCommand; +use crate::run_migrations::RunMigrationsCommand; +use crate::state_perf::StatePerfCommand; +use clap::Parser; +use std::path::PathBuf; + +#[derive(Parser)] +pub struct DatabaseCommand { + #[clap(subcommand)] + subcmd: SubCommand, +} + +#[derive(Parser)] +#[clap(subcommand_required = true, arg_required_else_help = true)] +enum SubCommand { + /// Analyse data size distribution in RocksDB + AnalyseDataSizeDistribution(AnalyseDataSizeDistributionCommand), + + /// Analyse gas usage in a chosen sequnce of blocks + AnalyseGasUsage(AnalyseGasUsageCommand), + + /// Change DbKind of hot or cold db. + ChangeDbKind(ChangeDbKindCommand), + + /// Run SST file compaction on database + CompactDatabase(RunCompactionCommand), + + /// Corrupt the state snapshot. + CorruptStateSnapshot(CorruptStateSnapshotCommand), + + /// Make snapshot of the database + MakeSnapshot(MakeSnapshotCommand), + + /// Run migrations, + RunMigrations(RunMigrationsCommand), + + /// Run performance test for State column reads. + /// Uses RocksDB data specified via --home argument. + StatePerf(StatePerfCommand), + + /// Loads an in-memory trie for research purposes. + LoadMemTrie(LoadMemTrieCommand), +} + +impl DatabaseCommand { + pub fn run(&self, home: &PathBuf) -> anyhow::Result<()> { + match &self.subcmd { + SubCommand::AnalyseDataSizeDistribution(cmd) => cmd.run(home), + SubCommand::AnalyseGasUsage(cmd) => cmd.run(home), + SubCommand::ChangeDbKind(cmd) => cmd.run(home), + SubCommand::CompactDatabase(cmd) => cmd.run(home), + SubCommand::CorruptStateSnapshot(cmd) => cmd.run(home), + SubCommand::MakeSnapshot(cmd) => { + let unc_config = framework::config::load_config( + &home, + unc_chain_configs::GenesisValidationMode::UnsafeFast, + ) + .unwrap_or_else(|e| panic!("Error loading config: {:#}", e)); + cmd.run(home, unc_config.config.archive, &unc_config.config.store) + } + SubCommand::RunMigrations(cmd) => cmd.run(home), + SubCommand::StatePerf(cmd) => cmd.run(home), + SubCommand::LoadMemTrie(cmd) => { + let unc_config = framework::config::load_config( + &home, + unc_chain_configs::GenesisValidationMode::UnsafeFast, + ) + .unwrap_or_else(|e| panic!("Error loading config: {:#}", e)); + cmd.run(unc_config, home) + } + } + } +} diff --git a/tools/database/src/compact.rs b/tools/database/src/compact.rs new file mode 100644 index 000000000..97178ec30 --- /dev/null +++ b/tools/database/src/compact.rs @@ -0,0 +1,24 @@ +use crate::utils::{open_rocksdb, resolve_column}; +use clap::Parser; +use unc_store::db::Database; +use std::path::PathBuf; + +#[derive(Parser)] +pub(crate) struct RunCompactionCommand { + /// If specified only this column will compacted + #[arg(short, long)] + column: Option, +} + +impl RunCompactionCommand { + pub(crate) fn run(&self, home: &PathBuf) -> anyhow::Result<()> { + let db = open_rocksdb(home, unc_store::Mode::ReadWrite)?; + if let Some(col_name) = &self.column { + db.compact_column(resolve_column(col_name)?)?; + } else { + db.compact()?; + } + eprintln!("Compaction is finished!"); + Ok(()) + } +} diff --git a/tools/database/src/corrupt.rs b/tools/database/src/corrupt.rs new file mode 100644 index 000000000..76d9669d9 --- /dev/null +++ b/tools/database/src/corrupt.rs @@ -0,0 +1,52 @@ +use crate::utils::open_state_snapshot; +use anyhow::anyhow; +use clap::Parser; +use unc_primitives::shard_layout::{ShardLayout, ShardVersion}; +use unc_store::{flat::FlatStorageManager, ShardUId, StoreUpdate}; +use std::path::PathBuf; + +#[derive(Parser)] +pub(crate) struct CorruptStateSnapshotCommand { + #[clap(short, long)] + shard_layout_version: ShardVersion, +} + +impl CorruptStateSnapshotCommand { + pub(crate) fn run(&self, home: &PathBuf) -> anyhow::Result<()> { + let store = open_state_snapshot(home, unc_store::Mode::ReadWrite)?; + let flat_storage_manager = FlatStorageManager::new(store.clone()); + + let mut store_update = store.store_update(); + // TODO(resharding) automatically detect the shard version + let shard_layout = match self.shard_layout_version { + 0 => ShardLayout::v0(1, 0), + 1 => ShardLayout::v0(1, 0), + 2 => ShardLayout::v0(1, 0), + _ => { + return Err(anyhow!( + "Unsupported shard layout version! {}", + self.shard_layout_version + )) + } + }; + for shard_uid in shard_layout.shard_uids() { + corrupt(&mut store_update, &flat_storage_manager, shard_uid)?; + } + store_update.commit().unwrap(); + + println!("corrupted the state snapshot"); + + Ok(()) + } +} + +fn corrupt( + store_update: &mut StoreUpdate, + flat_storage_manager: &FlatStorageManager, + shard_uid: ShardUId, +) -> Result<(), anyhow::Error> { + flat_storage_manager.create_flat_storage_for_shard(shard_uid)?; + let result = flat_storage_manager.remove_flat_storage_for_shard(shard_uid, store_update)?; + println!("removed flat storage for shard {shard_uid:?} result is {result}"); + Ok(()) +} diff --git a/tools/database/src/lib.rs b/tools/database/src/lib.rs new file mode 100644 index 000000000..827b5da4a --- /dev/null +++ b/tools/database/src/lib.rs @@ -0,0 +1,12 @@ +mod adjust_database; +mod analyse_data_size_distribution; +mod analyse_gas_usage; +mod block_iterators; +pub mod commands; +mod compact; +mod corrupt; +mod make_snapshot; +mod memtrie; +mod run_migrations; +mod state_perf; +mod utils; diff --git a/tools/database/src/make_snapshot.rs b/tools/database/src/make_snapshot.rs new file mode 100644 index 000000000..03c9975b6 --- /dev/null +++ b/tools/database/src/make_snapshot.rs @@ -0,0 +1,83 @@ +use unc_store::{checkpoint_hot_storage_and_cleanup_columns, Mode, NodeStorage, StoreConfig}; +use std::path::{Path, PathBuf}; + +#[derive(clap::Args)] +pub(crate) struct MakeSnapshotCommand { + /// Destination directory. + #[clap(long)] + destination: PathBuf, +} + +impl MakeSnapshotCommand { + pub(crate) fn run( + &self, + home_dir: &Path, + archive: bool, + store_config: &StoreConfig, + ) -> anyhow::Result<()> { + let opener = NodeStorage::opener(home_dir, archive, store_config, None); + let node_storage = opener.open_in_mode(Mode::ReadWriteExisting)?; + checkpoint_hot_storage_and_cleanup_columns( + &node_storage.get_hot_store(), + &self.destination, + None, + )?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::make_snapshot::MakeSnapshotCommand; + use unc_store::{DBCol, Mode, NodeStorage, StoreConfig}; + + /// Populates a DB, makes a checkpoint, makes changes to the DB. + /// Checks that the checkpoint DB can be opened and doesn't contain the latest changes. + #[test] + fn test() { + let home_dir = tempfile::tempdir().unwrap(); + let store_config = StoreConfig::test_config(); + let opener = NodeStorage::opener(home_dir.path(), false, &store_config, None); + + let keys = vec![vec![0], vec![1], vec![2], vec![3]]; + + { + // Populate the DB. + let node_storage = opener.open().unwrap(); + let mut store_update = node_storage.get_hot_store().store_update(); + for key in &keys { + store_update.insert(DBCol::Block, key.clone(), vec![42]); + } + store_update.commit().unwrap(); + println!("Populated"); + // Drops node_storage, which unlocks the DB. + } + + let destination = home_dir.path().join("data").join("snapshot"); + let cmd = MakeSnapshotCommand { destination: destination.clone() }; + cmd.run(home_dir.path(), false, &store_config).unwrap(); + println!("Made a checkpoint"); + + { + // Make a change to the original DB. + let node_storage = opener.open().unwrap(); + let mut store_update = node_storage.get_hot_store().store_update(); + store_update.delete_all(DBCol::Block); + store_update.commit().unwrap(); + println!("Deleted"); + } + + let node_storage = opener.open_in_mode(Mode::ReadOnly).unwrap(); + let snapshot_node_storage = NodeStorage::opener(&destination, false, &store_config, None) + .open_in_mode(Mode::ReadOnly) + .unwrap(); + for key in keys { + let exists_original = node_storage.get_hot_store().exists(DBCol::Block, &key).unwrap(); + let exists_snapshot = + snapshot_node_storage.get_hot_store().exists(DBCol::Block, &key).unwrap(); + println!("{exists_original},{exists_snapshot},{key:?}"); + assert!(!exists_original); + assert!(exists_snapshot); + } + } +} diff --git a/tools/database/src/memtrie.rs b/tools/database/src/memtrie.rs new file mode 100644 index 000000000..bed2e966f --- /dev/null +++ b/tools/database/src/memtrie.rs @@ -0,0 +1,48 @@ +use crate::utils::{flat_head, flat_head_state_root, open_rocksdb}; +use unc_epoch_manager::EpochManager; +use unc_primitives::block::Tip; +use unc_primitives::block_header::BlockHeader; +use unc_primitives::types::ShardId; +use unc_store::trie::mem::loading::load_trie_from_flat_state; +use unc_store::{DBCol, ShardUId, HEAD_KEY}; +use framework::UncConfig; +use std::path::Path; +use std::sync::Arc; +use std::time::Duration; + +/// Command to load an in-memory trie for research purposes. +#[derive(clap::Parser)] +pub struct LoadMemTrieCommand { + #[clap(long)] + shard_id: ShardId, +} + +impl LoadMemTrieCommand { + pub fn run(&self, unc_config: UncConfig, home: &Path) -> anyhow::Result<()> { + let rocksdb = Arc::new(open_rocksdb(home, unc_store::Mode::ReadOnly)?); + let store = unc_store::NodeStorage::new(rocksdb).get_hot_store(); + let genesis_config = &unc_config.genesis.config; + // Note: this is not necessarily correct; it's just an estimate of the shard layout, + // so that users of this tool doesn't have to specify the full shard UID. + let head = + store.get_ser::(DBCol::BlockMisc, HEAD_KEY).unwrap().unwrap().last_block_hash; + let block_header = store + .get_ser::(DBCol::BlockHeader, &borsh::to_vec(&head).unwrap())? + .ok_or_else(|| anyhow::anyhow!("Block header not found"))?; + let epoch_manager = + EpochManager::new_from_genesis_config(store.clone(), &genesis_config).unwrap(); + let shard_layout = epoch_manager.get_shard_layout(block_header.epoch_id()).unwrap(); + + let shard_uid = ShardUId::from_shard_id_and_layout(self.shard_id, &shard_layout); + let state_root = flat_head_state_root(&store, &shard_uid); + let flat_head_height = flat_head(&store, &shard_uid).height; + + let _trie = load_trie_from_flat_state(&store, shard_uid, state_root, flat_head_height)?; + println!( + "Loaded trie for shard {} at height {}, press Ctrl-C to exit.", + self.shard_id, flat_head_height + ); + std::thread::sleep(Duration::from_secs(10000000000)); + Ok(()) + } +} diff --git a/tools/database/src/run_migrations.rs b/tools/database/src/run_migrations.rs new file mode 100644 index 000000000..bf0ba38a5 --- /dev/null +++ b/tools/database/src/run_migrations.rs @@ -0,0 +1,16 @@ +use std::path::Path; + +#[derive(clap::Args)] +pub(crate) struct RunMigrationsCommand {} + +impl RunMigrationsCommand { + pub(crate) fn run(&self, home_dir: &Path) -> anyhow::Result<()> { + let mut unc_config = framework::config::load_config( + &home_dir, + unc_chain_configs::GenesisValidationMode::UnsafeFast, + ) + .unwrap_or_else(|e| panic!("Error loading config: {:#}", e)); + framework::open_storage(home_dir, &mut unc_config)?; + Ok(()) + } +} diff --git a/tools/database/src/state_perf.rs b/tools/database/src/state_perf.rs new file mode 100644 index 000000000..832ae3807 --- /dev/null +++ b/tools/database/src/state_perf.rs @@ -0,0 +1,196 @@ +use clap::Parser; +use indicatif::{ProgressBar, ProgressIterator}; +use std::collections::BTreeMap; +use std::fmt::{Display, Write}; +use std::path::Path; +use std::sync::Arc; +use std::time::{Duration, Instant}; + +use unc_primitives::shard_layout::{ShardLayout, ShardUId}; +use unc_primitives::state::ValueRef; +use rand::rngs::StdRng; +use rand::seq::SliceRandom; +use rand::SeedableRng; + +use unc_store::flat::store_helper::iter_flat_state_entries; +use unc_store::{Store, TrieStorage}; + +use crate::utils::open_rocksdb; + +#[derive(Parser)] +pub(crate) struct StatePerfCommand { + /// Number of requests to use for the performance evaluation. + /// Increasing this value results in more precise measurements, but longer test execution. + #[arg(short, long, default_value_t = 10000)] + samples: usize, + + /// Number of requests to use for the database warmup. + /// Those requests will be excluded from the measurements. + #[arg(short, long, default_value_t = 1000)] + warmup_samples: usize, +} + +impl StatePerfCommand { + pub(crate) fn run(&self, home: &Path) -> anyhow::Result<()> { + let rocksdb = Arc::new(open_rocksdb(home, unc_store::Mode::ReadOnly)?); + let store = unc_store::NodeStorage::new(rocksdb).get_hot_store(); + eprintln!("Start State perf test"); + let mut perf_context = PerfContext::new(); + let total_samples = self.warmup_samples + self.samples; + for (sample_i, (shard_uid, value_ref)) in + generate_state_requests(store.clone(), total_samples).into_iter().enumerate().progress() + { + let trie_storage = unc_store::TrieDBStorage::new(store.clone(), shard_uid); + let include_sample = sample_i >= self.warmup_samples; + if include_sample { + perf_context.reset(); + } + trie_storage.retrieve_raw_bytes(&value_ref.hash).unwrap(); + if include_sample { + perf_context.record(); + } + } + eprintln!("Finished State perf test"); + println!("{}", perf_context.format()); + Ok(()) + } +} + +struct PerfContext { + rocksdb_context: rocksdb::perf::PerfContext, + start: Instant, + measurements_per_block_reads: BTreeMap, + measurements_overall: Measurements, +} + +#[derive(Default)] +struct Measurements { + samples: usize, + total_observed_latency: Duration, + total_read_block_latency: Duration, + samples_with_merge: usize, +} + +impl Measurements { + fn record( + &mut self, + observed_latency: Duration, + read_block_latency: Duration, + has_merge: bool, + ) { + self.samples += 1; + self.total_observed_latency += observed_latency; + self.total_read_block_latency += read_block_latency; + if has_merge { + self.samples_with_merge += 1; + } + } + + fn avg_observed_latency(&self) -> Duration { + self.total_observed_latency / (self.samples as u32) + } + + fn avg_read_block_latency(&self) -> Duration { + self.total_read_block_latency / (self.samples as u32) + } +} + +impl Display for Measurements { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "avg observed_latency: {:?}, block_read_time: {:?}, samples with merge: {}", + self.avg_observed_latency(), + self.avg_read_block_latency(), + format_samples(self.samples_with_merge, self.samples) + ) + } +} + +impl PerfContext { + fn new() -> Self { + rocksdb::perf::set_perf_stats(rocksdb::perf::PerfStatsLevel::EnableTime); + Self { + rocksdb_context: rocksdb::perf::PerfContext::default(), + start: Instant::now(), + measurements_per_block_reads: BTreeMap::new(), + measurements_overall: Measurements::default(), + } + } + + fn reset(&mut self) { + self.rocksdb_context.reset(); + self.start = Instant::now(); + } + + fn record(&mut self) { + let observed_latency = self.start.elapsed(); + let block_read_cnt = + self.rocksdb_context.metric(rocksdb::PerfMetric::BlockReadCount) as usize; + let read_block_latency = + Duration::from_nanos(self.rocksdb_context.metric(rocksdb::PerfMetric::BlockReadTime)); + assert!(observed_latency > read_block_latency); + // This is a hack to check if at least one merge operator was executed during this request, + // will be replaced by a proper metric after `internal_merge_point_lookup_count` is added to + // rust-rocksdb + let has_merge = + self.rocksdb_context.metric(rocksdb::PerfMetric::MergeOperatorTimeNanos) > 0; + self.measurements_per_block_reads.entry(block_read_cnt).or_default().record( + observed_latency, + read_block_latency, + has_merge, + ); + self.measurements_overall.record(observed_latency, read_block_latency, has_merge); + } + + fn format(&self) -> String { + let mut ret = String::new(); + writeln!(&mut ret, "overall | {}", self.measurements_overall).unwrap(); + for (&block_read_cnt, measurements) in &self.measurements_per_block_reads { + writeln!( + &mut ret, + "block_read_count: {block_read_cnt}, samples: {}: | {}", + format_samples(measurements.samples, self.measurements_overall.samples), + measurements + ) + .unwrap(); + } + ret + } +} + +fn generate_state_requests(store: Store, samples: usize) -> Vec<(ShardUId, ValueRef)> { + eprintln!("Generate {samples} requests to State"); + let shard_uids = ShardLayout::v0_single_shard().shard_uids().collect::>(); + let num_shards = shard_uids.len(); + let mut ret = Vec::new(); + let progress = ProgressBar::new(samples as u64); + for shard_uid in shard_uids { + let shard_samples = samples / num_shards; + let mut keys_read = std::collections::HashSet::new(); + for value_ref in iter_flat_state_entries(shard_uid, &store, None, None) + .flat_map(|res| res.map(|(_, value)| value.to_value_ref())) + { + if value_ref.length > 4096 || !keys_read.insert(value_ref.hash) { + continue; + } + ret.push((shard_uid, value_ref)); + progress.inc(1); + if keys_read.len() == shard_samples { + break; + } + } + } + progress.finish(); + // Shuffle to avoid clustering requests to the same shard + ret.shuffle(&mut StdRng::seed_from_u64(42)); + eprintln!("Finished requests generation"); + ret +} + +fn format_samples(positive: usize, total: usize) -> String { + format!( + "{positive} ({:.2}%)", + if total == 0 { 0.0 } else { 100.0 * positive as f64 / total as f64 } + ) +} diff --git a/tools/database/src/utils.rs b/tools/database/src/utils.rs new file mode 100644 index 000000000..2f774631e --- /dev/null +++ b/tools/database/src/utils.rs @@ -0,0 +1,72 @@ +use std::fs; +use std::path::Path; + +use anyhow::anyhow; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::get_block_shard_uid; +use unc_store::flat::{store_helper, BlockInfo}; +use unc_store::{DBCol, NodeStorage, ShardUId, Store}; +use strum::IntoEnumIterator; + +pub(crate) fn open_rocksdb( + home: &Path, + mode: unc_store::Mode, +) -> anyhow::Result { + let config = framework::config::Config::from_file_skip_validation( + &home.join(framework::config::CONFIG_FILENAME), + )?; + let store_config = &config.store; + let db_path = store_config.path.as_ref().cloned().unwrap_or_else(|| home.join("data")); + let rocksdb = + unc_store::db::RocksDB::open(&db_path, store_config, mode, unc_store::Temperature::Hot)?; + Ok(rocksdb) +} + +pub(crate) fn open_state_snapshot(home: &Path, mode: unc_store::Mode) -> anyhow::Result { + let config = framework::config::Config::from_file_skip_validation( + &home.join(framework::config::CONFIG_FILENAME), + )?; + let store_config = &config.store; + let db_path = store_config.path.as_ref().cloned().unwrap_or_else(|| home.join("data")); + + let state_snapshot_dir = db_path.join("state_snapshot"); + let snapshots: Result, _> = fs::read_dir(state_snapshot_dir)?.into_iter().collect(); + let snapshots = snapshots?; + let &[snapshot_dir] = &snapshots.as_slice() else { + return Err(anyhow!("found more than one snapshot")); + }; + + let path = snapshot_dir.path(); + println!("state snapshot path {path:?}"); + + let opener = NodeStorage::opener(&path, false, &store_config, None); + let storage = opener.open_in_mode(mode)?; + let store = storage.get_hot_store(); + + Ok(store) +} + +pub(crate) fn resolve_column(col_name: &str) -> anyhow::Result { + DBCol::iter() + .filter(|db_col| <&str>::from(db_col) == col_name) + .next() + .ok_or_else(|| anyhow!("column {col_name} does not exist")) +} + +pub fn flat_head_state_root(store: &Store, shard_uid: &ShardUId) -> CryptoHash { + let chunk: unc_primitives::types::chunk_extra::ChunkExtra = store + .get_ser( + DBCol::ChunkExtra, + &get_block_shard_uid(&flat_head(store, shard_uid).hash, shard_uid), + ) + .unwrap() + .unwrap(); + *chunk.state_root() +} + +pub fn flat_head(store: &Store, shard_uid: &ShardUId) -> BlockInfo { + match store_helper::get_flat_storage_status(store, *shard_uid).unwrap() { + unc_store::flat::FlatStorageStatus::Ready(status) => status.flat_head, + other => panic!("invalid flat storage status {other:?}"), + } +} diff --git a/tools/debug-ui/.dockerignore b/tools/debug-ui/.dockerignore new file mode 100644 index 000000000..b38db2f29 --- /dev/null +++ b/tools/debug-ui/.dockerignore @@ -0,0 +1,2 @@ +node_modules/ +build/ diff --git a/tools/debug-ui/.eslintrc.yml b/tools/debug-ui/.eslintrc.yml new file mode 100644 index 000000000..dde6650bd --- /dev/null +++ b/tools/debug-ui/.eslintrc.yml @@ -0,0 +1,34 @@ +env: + browser: true + es2021: true +extends: + - eslint:recommended + - plugin:react/recommended + - plugin:react/jsx-runtime + - plugin:@typescript-eslint/recommended + - plugin:import/recommended + - plugin:import/typescript + - prettier +overrides: [] +parser: '@typescript-eslint/parser' +parserOptions: + ecmaVersion: latest + sourceType: module +plugins: + - react + - '@typescript-eslint' +rules: + import/order: + - error + '@typescript-eslint/no-non-null-assertion': + - off + '@typescript-eslint/no-unused-vars': + - error + '@typescript-eslint/no-empty-function': + - off +settings: + import/resolver: + typescript: true + node: true + react: + version: detect diff --git a/tools/debug-ui/.gitignore b/tools/debug-ui/.gitignore new file mode 100644 index 000000000..4d29575de --- /dev/null +++ b/tools/debug-ui/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/tools/debug-ui/.prettierrc.json b/tools/debug-ui/.prettierrc.json new file mode 100644 index 000000000..908a5e756 --- /dev/null +++ b/tools/debug-ui/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "printWidth": 100, + "tabWidth": 4, + "singleQuote": true, + "bracketSameLine": true +} \ No newline at end of file diff --git a/tools/debug-ui/Dockerfile b/tools/debug-ui/Dockerfile new file mode 100644 index 000000000..8621553ba --- /dev/null +++ b/tools/debug-ui/Dockerfile @@ -0,0 +1,17 @@ +FROM node:19-alpine AS build + +WORKDIR /build +ADD package.json /build/package.json +ADD package-lock.json /build/package-lock.json +RUN npm install + +ADD src /build/src +ADD public /build/public +ADD tsconfig.json /build/tsconfig.json +RUN npm run build + +# Serving does not require npm; simple nginx is good enough; it's just some +# static files. +FROM nginx:1.25.3-alpine +COPY --from=build /build/build /var/www/html +COPY nginx.conf /etc/nginx/conf.d/default.conf diff --git a/tools/debug-ui/README.md b/tools/debug-ui/README.md new file mode 100644 index 000000000..b2aea7e59 --- /dev/null +++ b/tools/debug-ui/README.md @@ -0,0 +1,70 @@ +# Nearcore Debug UI + +## How to Use +Clone framework, go to this directory, run `npm install` (only needed for first time), and then +``` +npm start +``` + +This will serve the UI at localhost:3000. + +Go to `http://localhost:3000/` to look at the debug UI of a near node. + +The RPC address can be either IP:port, or just IP (which will default to port 3030). + +## How to deploy in production +Use the included Dockerfile, which will serve the UI on port 80 inside the container. + +For example: + +``` +cd tools/debug-ui +docker build -t framework-debug-ui . +docker run -p 8080:80 framework-debug-ui +``` + +## Development + +The code is written in TypeScript with the React framework. The one thing most unintuitive about +React is React Hooks (the useState, useMemo, useCallback, useEffect, etc.) Understanding how +hooks work is a **must**: https://reactjs.org/docs/hooks-intro.html + +A few less-well-known hooks that are used often in this codebase: + +* `useMemo(func, [deps])` (from core React): returns func(), but only recomputing func()if any deps change from the last invocation (by shallow equality of the each dep). + +* `useEffect(func, [deps])` (from core React): similar to useMemo, but instead of returning func(), +just executes it, and func() is allowed to have side effects by mutating state (calling setXXX (that +comes from `const [XXX, setXXX] = useState(...);`)). + +* `useQuery([keys], () => promise)` (from react-query): returns `{data, error, isLoading}` which +represents fetching some data using the given promise. This is used to render asynchronously fetched +data. While the data is loading, `isLoading` is true; if there is an error, `error` is truthy; and +finally when there is data, `data` is truthy. This can be used to then render each state +accordingly. The keys given to the query are used to memoize the query, so that queries with the +same keys are only fetched once. + +It's also helpful to understand at a high level how the react-router library works; this is used +to support deep-linking in the URL (e.g. `/127.0.0.1/cluster` leads to the cluster page), allowing +the UI to be served as a single application. + +### Linting & Formatting +The project is configured to use ESLint (error-checking) and Prettier (consistent formatting). + +Run `npm run lint` to check for linting & formatting errors, and `npm run fix` to fix those that +can be automatically fixed. + +## How to Use the TestLoop Log Visualizer +For framework tests written in the TestLoop framework (any tests that mention "TestLoop", or +more precisely, using TestLoop from core/async/src/test_loop.rs), the test can be visualized: + +1. First run the test from the Rust side and save the output, e.g. + ``` + cargo test -p unc-chunks test_multi -- --show-output > ~/log.txt + ``` +2. Go to the UI at `/logviz`, such as http://localhost:3000/logviz +3. Drag the log.txt file into the UI. + +Screenshots: +image +image diff --git a/tools/debug-ui/nginx.conf b/tools/debug-ui/nginx.conf new file mode 100644 index 000000000..c9edbaab5 --- /dev/null +++ b/tools/debug-ui/nginx.conf @@ -0,0 +1,12 @@ +server { + listen 80; + root /var/www/html; + + location /static { + alias /var/www/html/static; + } + + location / { + try_files $uri /index.html; + } +} diff --git a/tools/debug-ui/package-lock.json b/tools/debug-ui/package-lock.json new file mode 100644 index 000000000..70b37d876 --- /dev/null +++ b/tools/debug-ui/package-lock.json @@ -0,0 +1,31270 @@ +{ + "name": "debug-ui", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "debug-ui", + "version": "0.1.0", + "dependencies": { + "@patternfly/react-log-viewer": "^4.87.101", + "@types/node": "^16.18.64", + "@types/react": "^18.2.37", + "@types/react-dom": "^18.2.18", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-query": "^4.0.0", + "react-router": "^6.21.0", + "react-router-dom": "^6.20.1", + "react-scripts": "^5.0.1", + "react-tooltip": "^5.22.0", + "react-xarrows": "^2.0.2" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^5.52.0", + "@typescript-eslint/parser": "^5.52.0", + "eslint": "^8.34.0", + "eslint-config-prettier": "^8.6.0", + "eslint-import-resolver-typescript": "^3.5.3", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-n": "^15.6.1", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-react": "^7.32.2", + "prettier": "2.8.4", + "typescript": "^4.9.5", + "typescript-plugin-css-modules": "^4.2.2" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.2.tgz", + "integrity": "sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw==", + "dev": true + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", + "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz", + "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==", + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-module-transforms": "^7.20.2", + "@babel/helpers": "^7.20.5", + "@babel/parser": "^7.20.5", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/core/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/eslint-parser": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz", + "integrity": "sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==", + "dependencies": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.11.0", + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@babel/eslint-parser/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "dependencies": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", + "dependencies": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.5.tgz", + "integrity": "sha512-3RCdA/EmEaikrhayahwToF0fpweU/8o2p8vhc1c/1kftHOdTKuC65kik/TLc+qfbS8JKw4qqJbne4ovICDhmww==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.19.1", + "@babel/helper-split-export-declaration": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz", + "integrity": "sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.2.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "dependencies": { + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz", + "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", + "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dependencies": { + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "dependencies": { + "@babel/types": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "dependencies": { + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz", + "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==", + "dependencies": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.1.tgz", + "integrity": "sha512-Gh5rchzSwE4kC+o/6T8waD0WHEQIsDmjltY8WnWRXHUdH8axZhuH86Ov9M72YhJfDrZseQwuuWaaIT/TmePp3g==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.5.tgz", + "integrity": "sha512-Lac7PpRJXcC3s9cKsBfl+uc+DYXU5FD06BrTFunQO6QIQT+DwyzDPURAowI3bcvD1dZF/ank1Z5rstUJn3Hn4Q==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.19.1", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/plugin-syntax-decorators": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.2.tgz", + "integrity": "sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ==", + "dependencies": { + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.20.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz", + "integrity": "sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.19.0.tgz", + "integrity": "sha512-xaBZUEDntt4faL1yN8oIFlhfXeQAWJW7CLKYsHTUqriCUbj8xOra8bfxxKGi/UwExPFBuPdH4XfHc9rGQhrVkQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", + "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.5.tgz", + "integrity": "sha512-WvpEIW9Cbj9ApF3yJCjIEEf1EiNJLtXagOrL5LNWEZOo3jv8pmPoYTSNJQvqej8OavVlgOoOPw6/htGZro6IkA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.2.tgz", + "integrity": "sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.19.1", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.2.tgz", + "integrity": "sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz", + "integrity": "sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-flow": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", + "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", + "dependencies": { + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", + "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-simple-access": "^7.19.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", + "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", + "dependencies": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-identifier": "^7.19.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", + "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.5.tgz", + "integrity": "sha512-h7plkOmcndIUWXZFLgpbrh2+fXAi47zcUX7IrOQuZdLD0I0KvjJ6cvo3BEcAOsDOcZhVKGJqv07mkSqK0y2isQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.20.2.tgz", + "integrity": "sha512-KS/G8YI8uwMGKErLFOHS/ekhqdHhpEloxs43NecQHVgo2QuQSyJhGIY1fL8UGl9wy5ItVwwoUL4YxVqsplGq2g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", + "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz", + "integrity": "sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", + "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", + "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", + "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "regenerator-transform": "^0.15.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", + "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.2.tgz", + "integrity": "sha512-jvS+ngBfrnTUBfOQq8NfGnSbF9BrqlR6hjJ2yVxMkmO5nL/cdifNbI30EfjRlN4g5wYWNnMPyj5Sa6R1pbLeag==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.20.2", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", + "dependencies": { + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.20.0", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.20.2", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.20.1", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.19.0", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", + "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-react-display-name": "^7.18.6", + "@babel/plugin-transform-react-jsx": "^7.18.6", + "@babel/plugin-transform-react-jsx-development": "^7.18.6", + "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", + "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-typescript": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", + "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.6.tgz", + "integrity": "sha512-tqeujPiuEfcH067mx+7otTQWROVMKHXEaOQcAeNV5dDdbPWvPcFA8/W9LXw2NfjNmOetqLl03dfnG2WALPlsRQ==", + "dependencies": { + "core-js-pure": "^3.25.1", + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" + }, + "node_modules/@csstools/normalize.css": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", + "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==" + }, + "node_modules/@csstools/postcss-cascade-layers": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", + "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", + "dependencies": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-color-function/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-font-format-keywords/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-hwb-function/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-ic-unit/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-nested-calc/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-normalize-display-values/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-oklab-function/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-stepped-value-functions/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-text-decoration-shorthand/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-trigonometric-functions/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", + "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2", + "postcss-selector-parser": "^6.0.10" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.1.tgz", + "integrity": "sha512-LSqwPZkK3rYfD7GKoIeExXOyYx6Q1O4iqZWwIehDNuv3Dv425FIAE8PRwtAx1imEolFTHgBEcoFHm9MDnYgPCg==" + }, + "node_modules/@floating-ui/dom": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.1.tgz", + "integrity": "sha512-TpIO93+DIujg3g7SykEAGZMDtbJRrmnYRCNYSjJlvIbGhBjRSNTLVbNeDQBrzy9qDgUbiWdc7KA0uZHZ2tJmiw==", + "dependencies": { + "@floating-ui/core": "^1.1.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/console/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/console/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@jest/console/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/core/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@jest/core/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dependencies": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@jest/reporters/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "dependencies": { + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "dependencies": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/transform/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@jest/transform/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/types/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/types/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@jest/types/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dependencies": { + "eslint-scope": "5.1.1" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@patternfly/react-core": { + "version": "4.278.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.278.0.tgz", + "integrity": "sha512-w+0WgSa5O64yi50PpkYuuIt5N5S9svkJKxrhV4LQzOhAtSRTtbraOBRwEKodppT/oUeKMlAdDUWep1O3QluF3Q==", + "dependencies": { + "@patternfly/react-icons": "^4.93.7", + "@patternfly/react-styles": "^4.92.8", + "@patternfly/react-tokens": "^4.94.7", + "focus-trap": "6.9.2", + "react-dropzone": "9.0.0", + "tippy.js": "5.1.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17 || ^18", + "react-dom": "^16.8 || ^17 || ^18" + } + }, + "node_modules/@patternfly/react-icons": { + "version": "4.93.7", + "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.93.7.tgz", + "integrity": "sha512-3kr35dgba7Qz5CSzmfH0rIjSvBC5xkmiknf3SvVUVxaiVA7KRowID8viYHeZlf3v/Oa3sEewaH830Q0t+nWsZQ==", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18", + "react-dom": "^16.8 || ^17 || ^18" + } + }, + "node_modules/@patternfly/react-log-viewer": { + "version": "4.87.101", + "resolved": "https://registry.npmjs.org/@patternfly/react-log-viewer/-/react-log-viewer-4.87.101.tgz", + "integrity": "sha512-JSV1PEDIRZ3ffzfXGhz7x3wA8VlTcypnYGQZ7OEng7Kv91TiaThlVDr2j/uZ2LPaTam/PvH6bdxpFXXVslqqWw==", + "dependencies": { + "@patternfly/react-core": "^4.276.6", + "@patternfly/react-icons": "^4.93.6", + "@patternfly/react-styles": "^4.92.6", + "memoize-one": "^5.1.0", + "monaco-editor": "^0.33.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17 || ^18", + "react-dom": "^16.8 || ^17 || ^18" + } + }, + "node_modules/@patternfly/react-styles": { + "version": "4.92.8", + "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.92.8.tgz", + "integrity": "sha512-K4lUU8O4HiCX9NeuNUIrPgN3wlGERCxJVio+PAjd8hpJD/PKnjFfOJ9u6/Cii3qLy/5ZviWPRNHbGiwA/+YUhg==" + }, + "node_modules/@patternfly/react-tokens": { + "version": "4.94.7", + "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.94.7.tgz", + "integrity": "sha512-h+ducOLDMSxcuec3+YY3x+stM5ZUSnrl/lC/eVmjypil2El08NuE2MNEPMQWdhrod6VRRZFMNqZw/m82iv6U1A==" + }, + "node_modules/@pkgr/utils": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz", + "integrity": "sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "is-glob": "^4.0.3", + "open": "^8.4.0", + "picocolors": "^1.0.0", + "tiny-glob": "^0.2.9", + "tslib": "^2.4.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz", + "integrity": "sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==", + "dependencies": { + "ansi-html-community": "^0.0.8", + "common-path-prefix": "^3.0.0", + "core-js-pure": "^3.23.3", + "error-stack-parser": "^2.0.6", + "find-up": "^5.0.0", + "html-entities": "^2.1.0", + "loader-utils": "^2.0.4", + "schema-utils": "^3.0.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "@types/webpack": "4.x || 5.x", + "react-refresh": ">=0.10.0 <1.0.0", + "sockjs-client": "^1.4.0", + "type-fest": ">=0.17.0 <4.0.0", + "webpack": ">=4.43.0 <6.0.0", + "webpack-dev-server": "3.x || 4.x", + "webpack-hot-middleware": "2.x", + "webpack-plugin-serve": "0.x || 1.x" + }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + }, + "sockjs-client": { + "optional": true + }, + "type-fest": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + }, + "webpack-hot-middleware": { + "optional": true + }, + "webpack-plugin-serve": { + "optional": true + } + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@remix-run/router": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.13.1.tgz", + "integrity": "sha512-so+DHzZKsoOcoXrILB4rqDkMDy7NLMErRdOxvzvOKb507YINKUP4Di+shbTZDhSE/pBZ+vr7XGIpcOO0VLSA+Q==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "dependencies": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/pluginutils/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", + "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" + }, + "node_modules/@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "dependencies": { + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" + } + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", + "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", + "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", + "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", + "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", + "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", + "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", + "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", + "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", + "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", + "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", + "@svgr/babel-plugin-transform-svg-component": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", + "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", + "dependencies": { + "@svgr/plugin-jsx": "^5.5.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", + "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", + "dependencies": { + "@babel/types": "^7.12.6" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", + "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", + "dependencies": { + "@babel/core": "^7.12.3", + "@svgr/babel-preset": "^5.5.0", + "@svgr/hast-util-to-babel-ast": "^5.5.0", + "svg-parser": "^2.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-svgo": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", + "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", + "dependencies": { + "cosmiconfig": "^7.0.0", + "deepmerge": "^4.2.2", + "svgo": "^1.2.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/webpack": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", + "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/plugin-transform-react-constant-elements": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.5", + "@svgr/core": "^5.5.0", + "@svgr/plugin-jsx": "^5.5.0", + "@svgr/plugin-svgo": "^5.5.0", + "loader-utils": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/webpack/node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" + } + }, + "node_modules/@svgr/webpack/node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@svgr/webpack/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/@tanstack/query-core": { + "version": "4.36.1", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.36.1.tgz", + "integrity": "sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.1.20", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", + "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", + "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.4.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", + "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==" + }, + "node_modules/@types/express": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", + "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.31", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", + "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + }, + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + }, + "node_modules/@types/node": { + "version": "16.18.64", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.64.tgz", + "integrity": "sha512-TiY2gIDob8+QOPIcVpS0ZY+H1DVTfplBW6UgL2b4gOYbigIlKVIh6Lcv+7YDUciUTqhVLG91PrZBXW10IoBhtw==" + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "node_modules/@types/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-0VLab/pcLTLcfbxi6THSIMVYcw9hEUBGvjwwaGpW77mMgRXfGF+a76t7BxTGyLh1y68tBvrffp8UWnqvm76+yg==", + "dev": true, + "dependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/@types/postcss-modules-scope": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/postcss-modules-scope/-/postcss-modules-scope-3.0.1.tgz", + "integrity": "sha512-LNkp3c4ML9EQj2dgslp4i80Jxj72YK3HjYzrTn6ftUVylW1zaKFGqrMlNIyqBmPWmIhZ/Y5r0Y4T49Hk1IuDUg==", + "dev": true, + "dependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/@types/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "node_modules/@types/q": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", + "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "node_modules/@types/react": { + "version": "18.2.37", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.37.tgz", + "integrity": "sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.18", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", + "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "node_modules/@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==" + }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", + "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, + "node_modules/@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz", + "integrity": "sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw==", + "dependencies": { + "@typescript-eslint/scope-manager": "5.53.0", + "@typescript-eslint/type-utils": "5.53.0", + "@typescript-eslint/utils": "5.53.0", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz", + "integrity": "sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==", + "dependencies": { + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz", + "integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz", + "integrity": "sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==", + "dependencies": { + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.53.0.tgz", + "integrity": "sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.53.0", + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/typescript-estree": "5.53.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz", + "integrity": "sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==", + "dependencies": { + "@typescript-eslint/types": "5.53.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.45.1.tgz", + "integrity": "sha512-WlXwY9dbmc0Lzu6xQOZ3yN8u/ws/1R8zPC16O217LMZJCbV2hJezqkWMUB+jMwguOJW+cukCDe92vcwwf8zwjQ==", + "dependencies": { + "@typescript-eslint/utils": "5.45.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.53.0.tgz", + "integrity": "sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ==", + "dependencies": { + "@typescript-eslint/scope-manager": "5.53.0", + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/typescript-estree": "5.53.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz", + "integrity": "sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==", + "dependencies": { + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz", + "integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz", + "integrity": "sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==", + "dependencies": { + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz", + "integrity": "sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==", + "dependencies": { + "@typescript-eslint/types": "5.53.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/@typescript-eslint/parser/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.1.tgz", + "integrity": "sha512-D6fCileR6Iai7E35Eb4Kp+k0iW7F1wxXYrOhX/3dywsOJpJAQ20Fwgcf+P/TDtvQ7zcsWsrJaglaQWDhOMsspQ==", + "dependencies": { + "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/visitor-keys": "5.45.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.53.0.tgz", + "integrity": "sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw==", + "dependencies": { + "@typescript-eslint/typescript-estree": "5.53.0", + "@typescript-eslint/utils": "5.53.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz", + "integrity": "sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==", + "dependencies": { + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz", + "integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz", + "integrity": "sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==", + "dependencies": { + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.53.0.tgz", + "integrity": "sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.53.0", + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/typescript-estree": "5.53.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz", + "integrity": "sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==", + "dependencies": { + "@typescript-eslint/types": "5.53.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/@typescript-eslint/type-utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.1.tgz", + "integrity": "sha512-HEW3U0E5dLjUT+nk7b4lLbOherS1U4ap+b9pfu2oGsW3oPu7genRaY9dDv3nMczC1rbnRY2W/D7SN05wYoGImg==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.1.tgz", + "integrity": "sha512-76NZpmpCzWVrrb0XmYEpbwOz/FENBi+5W7ipVXAsG3OoFrQKJMiaqsBMbvGRyLtPotGqUfcY7Ur8j0dksDJDng==", + "dependencies": { + "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/visitor-keys": "5.45.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.1.tgz", + "integrity": "sha512-rlbC5VZz68+yjAzQBc4I7KDYVzWG2X/OrqoZrMahYq3u8FFtmQYc+9rovo/7wlJH5kugJ+jQXV5pJMnofGmPRw==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.45.1", + "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/typescript-estree": "5.45.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.1.tgz", + "integrity": "sha512-cy9ln+6rmthYWjH9fmx+5FU/JDpjQb586++x2FZlveq7GdGuLLW9a2Jcst2TGekH82bXpfmRNSwP9tyEs6RjvQ==", + "dependencies": { + "@typescript-eslint/types": "5.45.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/acorn-node/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/address": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.1.tgz", + "integrity": "sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "node_modules/array-includes": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.reduce": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", + "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", + "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.1.3" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "node_modules/ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/attr-accept": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz", + "integrity": "sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==", + "dependencies": { + "core-js": "^2.5.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/attr-accept/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true + }, + "node_modules/autoprefixer": { + "version": "10.4.13", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", + "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001426", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/autoprefixer/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/axe-core": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.2.tgz", + "integrity": "sha512-u2MVsXfew5HBvjsczCv+xlwdNnB1oQR9HlAcsejZttNjKKSkeDNVwB1vMThIUIFI9GoT57Vtk8iQLwqOfAkboA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==" + }, + "node_modules/babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "dependencies": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/babel-jest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/babel-jest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", + "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" + } + }, + "node_modules/babel-loader/node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/babel-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/babel-loader/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-loader/node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/babel-loader/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-named-asset-import": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", + "peerDependencies": { + "@babel/core": "^7.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "dependencies": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-react-app": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", + "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-transform-flow-strip-types": "^7.16.0", + "@babel/plugin-transform-react-display-name": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.16.0", + "@babel/runtime": "^7.16.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + }, + "node_modules/bfj": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", + "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", + "dependencies": { + "bluebird": "^3.5.5", + "check-types": "^11.1.1", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/bonjour-service": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz", + "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==", + "dependencies": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + }, + "node_modules/browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/builtins/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001436", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001436.tgz", + "integrity": "sha512-ZmWkKsnC2ifEPoWUvSAIGyOYwT+keAaaWPHiQ9DfMqS1t6tfuyFYoWR78TeZtznkEQ64+vGXH9cZrElwR2Mrxg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/check-types": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.2.tgz", + "integrity": "sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA==" + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz", + "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==" + }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, + "node_modules/clean-css": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", + "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dependencies": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/core-js": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.1.tgz", + "integrity": "sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.1.tgz", + "integrity": "sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A==", + "dependencies": { + "browserslist": "^4.21.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-pure": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.1.tgz", + "integrity": "sha512-VVXcDpp/xJ21KdULRq/lXdLzQAtX7+37LzpyfFM973il0tWSsDEoyzG38G14AjTpK9VTfiNM9jnFauq/CpaWGQ==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-blank-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-has-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-loader": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.2.tgz", + "integrity": "sha512-oqGbbVcBJkm8QwmnNzrFrWTnudnRZC+1eXikLJl0n4ljcfotgRifpg2a1lKy8jTrc4/d9A/ap1GFq1jDKG7J+Q==", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.18", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-loader/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/css-loader/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", + "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "dependencies": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "postcss": "^8.3.5", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "bin": { + "css-prefers-color-scheme": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssdb": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.2.0.tgz", + "integrity": "sha512-JYlIsE7eKHSi0UNuCyo96YuIDFqvhGgHw4Ck6lsN+DP0Tp8M64UTDT2trGbkMDqnCoEjks7CkS0XcjU0rkvBdg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "5.1.14", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.14.tgz", + "integrity": "sha512-Oou7ihiTocbKqi0J1bB+TRJIQX5RMR3JghA8hcWSw9mjBLQ5Y3RWqEDoYG3sRNlAbCIXpqMoZGbq5KDR3vdzgw==", + "dependencies": { + "cssnano-preset-default": "^5.2.13", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.13", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.13.tgz", + "integrity": "sha512-PX7sQ4Pb+UtOWuz8A1d+Rbi+WimBIxJTRyBdgGp1J75VU0r/HFQeLnMYgHiCAp6AR4rqrc7Y4R+1Rjk3KJz6DQ==", + "dependencies": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.3", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.1", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + }, + "node_modules/csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "node_modules/detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "dependencies": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "bin": { + "detect": "bin/detect-port", + "detect-port": "bin/detect-port" + }, + "engines": { + "node": ">= 4.2.1" + } + }, + "node_modules/detect-port-alt/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/detect-port-alt/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "dependencies": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" + }, + "node_modules/dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" + }, + "node_modules/emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", + "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-abstract": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", + "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", + "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", + "dependencies": { + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz", + "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-config-react-app": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", + "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/eslint-parser": "^7.16.3", + "@rushstack/eslint-patch": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.5.0", + "@typescript-eslint/parser": "^5.5.0", + "babel-preset-react-app": "^10.0.1", + "confusing-browser-globals": "^1.0.11", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-testing-library": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.3.tgz", + "integrity": "sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "enhanced-resolve": "^5.10.0", + "get-tsconfig": "^4.2.0", + "globby": "^13.1.2", + "is-core-module": "^2.10.0", + "is-glob": "^4.0.3", + "synckit": "^0.8.4" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*" + } + }, + "node_modules/eslint-import-resolver-typescript/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-typescript/node_modules/globby": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", + "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-import-resolver-typescript/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/eslint-import-resolver-typescript/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-es": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", + "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-es/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-flowtype": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", + "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "dependencies": { + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@babel/plugin-syntax-flow": "^7.14.5", + "@babel/plugin-transform-react-jsx": "^7.14.9", + "eslint": "^8.1.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", + "has": "^1.0.3", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "dependencies": { + "@typescript-eslint/experimental-utils": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", + "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", + "dependencies": { + "@babel/runtime": "^7.18.9", + "aria-query": "^4.2.2", + "array-includes": "^3.1.5", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.4.3", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.3.2", + "language-tags": "^1.0.5", + "minimatch": "^3.1.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-n": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.6.1.tgz", + "integrity": "sha512-R9xw9OtCRxxaxaszTQmQAlPgM+RdGjaL1akWuY/Fv9fRAi8Wj4CUKc6iYVG8QNRjRuo8/BqVYIpfqberJUEacA==", + "dev": true, + "dependencies": { + "builtins": "^5.0.1", + "eslint-plugin-es": "^4.1.0", + "eslint-utils": "^3.0.0", + "ignore": "^5.1.1", + "is-core-module": "^2.11.0", + "minimatch": "^3.1.2", + "resolve": "^1.22.1", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", + "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.8" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-testing-library": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.9.1.tgz", + "integrity": "sha512-6BQp3tmb79jLLasPHJmy8DnxREe+2Pgf7L+7o09TSWPfdqqtQfRZmZNetr5mOs3yqZk/MRNxpN3RUpJe0wB4LQ==", + "dependencies": { + "@typescript-eslint/utils": "^5.13.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0", + "npm": ">=6" + }, + "peerDependencies": { + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "dependencies": { + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "node_modules/fastq": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", + "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" + } + }, + "node_modules/file-loader/node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/file-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/file-selector": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.19.tgz", + "integrity": "sha512-kCWw3+Aai8Uox+5tHCNgMFaUdgidxvMnLWO6fM5sZ0hA2wlHP5/DHGF0ECe84BiB95qdJbKNEJhWKVDvMN+JDQ==", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", + "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-cache-dir/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-cache-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" + }, + "node_modules/focus-trap": { + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-6.9.2.tgz", + "integrity": "sha512-gBEuXOPNOKPrLdZpMFUSTyIo1eT2NSZRrwZ9r/0Jqw5tmT3Yvxfmu8KBHw8xW2XQkw6E/JoG+OlEq7UDtSUNgw==", + "dependencies": { + "tabbable": "^5.3.2" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", + "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", + "dependencies": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=10", + "yarn": ">=1.0.0" + }, + "peerDependencies": { + "eslint": ">= 6", + "typescript": ">= 2.7", + "vue-template-compiler": "*", + "webpack": ">= 4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dependencies": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.4.0.tgz", + "integrity": "sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/globalyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", + "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", + "dev": true + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "node_modules/harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==" + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "webpack": "^5.20.0" + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, + "node_modules/identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "dependencies": { + "harmony-reflect": "^1.4.6" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ignore": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", + "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immer": { + "version": "9.0.16", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", + "integrity": "sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "devOptional": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-report/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jake/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jake/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jake/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "dependencies": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "dependencies": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-circus/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-circus/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-circus/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "dependencies": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-cli/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-cli/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "dependencies": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-config/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-config/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-diff/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-each/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-each/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-each/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-jasmine2/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-jasmine2/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-jasmine2/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-jasmine2/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "dependencies": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-matcher-utils/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-matcher-utils/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-message-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-resolve/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-resolve/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runner/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-runner/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runtime/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runtime/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-runtime/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-snapshot/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-validate/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-validate/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", + "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", + "dependencies": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.0.0", + "jest-regex-util": "^28.0.0", + "jest-watcher": "^28.0.0", + "slash": "^4.0.0", + "string-length": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "jest": "^27.0.0 || ^28.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/console/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dependencies": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@types/yargs": { + "version": "17.0.16", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.16.tgz", + "integrity": "sha512-Mh3OP0oh8X7O7F9m5AplC+XHYLBWuPKNkGVD3gIZFLFebBnuFI2Nz5Sf8WLvwGxECJ8YjifQvFdh79ubODkdug==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-watch-typeahead/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-watch-typeahead/node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-message-util/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "dependencies": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/jest-watch-typeahead/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watch-typeahead/node_modules/string-length": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", + "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", + "dependencies": { + "char-regex": "^2.0.0", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watch-typeahead/node_modules/string-length/node_modules/char-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", + "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/jest-watch-typeahead/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "dependencies": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-watcher/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-watcher/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-watcher/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-sdsl": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", + "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", + "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "dependencies": { + "array-includes": "^3.1.5", + "object.assign": "^4.1.3" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" + }, + "node_modules/language-tags": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.6.tgz", + "integrity": "sha512-HNkaCgM8wZgE/BZACeotAAgpL9FUjEnhgF0FVQMIgH//zqTPreLYMb3rWYkYAqPoF75Jwuycp1da7uz66cfFQg==", + "dependencies": { + "language-subtag-registry": "^0.3.20" + } + }, + "node_modules/less": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.4.12", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.12.tgz", + "integrity": "sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw==", + "dependencies": { + "fs-monkey": "^1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.2.tgz", + "integrity": "sha512-EdlUizq13o0Pd+uCp+WO/JpkLvHRVGt97RqfeGhXqAcorYo1ypJSpkV+WDT0vY/kmh/p7wRdJNJtuyK540PXDw==", + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/monaco-editor": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.33.0.tgz", + "integrity": "sha512-VcRWPSLIUEgQJQIE0pVT8FcGBIgFoxz7jtqctE+IiCxWugD0DwgyQBcZBhdSrdMC84eumoqMZsGl2GTreOzwqw==" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" + }, + "node_modules/needle": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", + "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", + "dev": true, + "optional": true, + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nwsapi": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", + "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz", + "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==", + "dependencies": { + "array.prototype.reduce": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.hasown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", + "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", + "dependencies": { + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/postcss": { + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-browser-comments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", + "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "browserslist": ">=4", + "postcss": ">=8" + } + }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-calc/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=7.6.0" + }, + "peerDependencies": { + "postcss": "^8.4.6" + } + }, + "node_modules/postcss-clamp/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-color-functional-notation/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-hex-alpha/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-color-rebeccapurple/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-colormin/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-convert-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-custom-media/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-custom-properties": { + "version": "12.1.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", + "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-custom-properties/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-double-position-gradients/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-env-function/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-flexbugs-fixes": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", + "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", + "peerDependencies": { + "postcss": "^8.1.4" + } + }, + "node_modules/postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-image-set-function/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-import/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", + "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-lab-function/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-loader/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-longhand/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.3.tgz", + "integrity": "sha512-LbLd7uFC00vpOuMvyZop8+vvhnfRGpp2S+IMQKeuOZZapPRY4SMq5ErjQeHbHsjCUgJkRNrlU+LmxsKIqPKQlA==", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-minify-params": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "dependencies": { + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nested": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", + "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nesting": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", + "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-normalize": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", + "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", + "dependencies": { + "@csstools/normalize.css": "*", + "postcss-browser-comments": "^4", + "sanitize.css": "*" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "browserslist": ">= 4", + "postcss": ">= 8" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-opacity-percentage": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz", + "integrity": "sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w==", + "funding": [ + { + "type": "kofi", + "url": "https://ko-fi.com/mrcgrtz" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/mrcgrtz" + } + ], + "engines": { + "node": "^12 || ^14 || >=16" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-ordered-values/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-overflow-shorthand/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-place/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-preset-env": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz", + "integrity": "sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag==", + "dependencies": { + "@csstools/postcss-cascade-layers": "^1.1.1", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.13", + "browserslist": "^4.21.4", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^7.1.0", + "postcss-attribute-case-insensitive": "^5.0.2", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.4", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.1", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.10", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.1", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.2.0", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.4", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-preset-env/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.1.tgz", + "integrity": "sha512-//jeDqWcHPuXGZLoolFrUXBDyuEGbr9S2rMo19bkTIjBQ4PqkaO+oI8wua5BOUxpfi97i3PCoInsiFIEBfkm9w==", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", + "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/postcss-svgo/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/postcss-svgo/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "node_modules/postcss-svgo/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/postcss-svgo/node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", + "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/prop-types-extra/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-app-polyfill": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", + "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", + "dependencies": { + "core-js": "^3.19.2", + "object-assign": "^4.1.1", + "promise": "^8.1.0", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.9", + "whatwg-fetch": "^3.6.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "dependencies": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-dev-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/react-dev-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/react-dev-utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/react-dev-utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/react-dev-utils/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dev-utils/node_modules/loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/react-dev-utils/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-dropzone": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-9.0.0.tgz", + "integrity": "sha512-wZ2o9B2qkdE3RumWhfyZT9swgJYJPeU5qHEcMU8weYpmLex1eeWX0CC32/Y0VutB+BBi2D+iePV/YZIiB4kZGw==", + "dependencies": { + "attr-accept": "^1.1.3", + "file-selector": "^0.1.8", + "prop-types": "^15.6.2", + "prop-types-extra": "^1.1.0" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/react-query": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/react-query/-/react-query-4.0.0.tgz", + "integrity": "sha512-qiW+Yvbl+EK8iwPDJAj4qWAKceh+g8Up8jxoNxJbzhV3bNheeyHF3EyynnkDO3S+CYgSwtCUFaP8vOjB62j7xQ==", + "deprecated": "Please use @tanstack/react-query for v4+", + "dependencies": { + "@tanstack/query-core": "^4.0.0-beta.1", + "@types/use-sync-external-store": "^0.0.3", + "use-sync-external-store": "^1.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/react-refresh": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", + "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.0.tgz", + "integrity": "sha512-hGZ0HXbwz3zw52pLZV3j3+ec+m/PQ9cTpBvqjFQmy2XVUWGn5MD+31oXHb6dVTxYzmAeaiUBYjkoNz66n3RGCg==", + "dependencies": { + "@remix-run/router": "1.14.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.20.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.20.1.tgz", + "integrity": "sha512-npzfPWcxfQN35psS7rJgi/EW0Gx6EsNjfdJSAk73U/HqMEJZ2k/8puxfwHFgDQhBGmS3+sjnGbMdMSV45axPQw==", + "dependencies": { + "@remix-run/router": "1.13.1", + "react-router": "6.20.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-router-dom/node_modules/react-router": { + "version": "6.20.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.20.1.tgz", + "integrity": "sha512-ccvLrB4QeT5DlaxSFFYi/KR8UMQ4fcD8zBcR71Zp1kaYTC5oJKYAp1cbavzGrogwxca+ubjkd7XjFZKBW8CxPA==", + "dependencies": { + "@remix-run/router": "1.13.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router/node_modules/@remix-run/router": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.14.0.tgz", + "integrity": "sha512-WOHih+ClN7N8oHk9N4JUiMxQJmRVaOxcg8w7F/oHUXzJt920ekASLI/7cYX8XkntDWRhLZtsk6LbGrkgOAvi5A==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/react-scripts": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", + "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", + "dependencies": { + "@babel/core": "^7.16.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", + "@svgr/webpack": "^5.5.0", + "babel-jest": "^27.4.2", + "babel-loader": "^8.2.3", + "babel-plugin-named-asset-import": "^0.3.8", + "babel-preset-react-app": "^10.0.1", + "bfj": "^7.0.2", + "browserslist": "^4.18.1", + "camelcase": "^6.2.1", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "css-loader": "^6.5.1", + "css-minimizer-webpack-plugin": "^3.2.0", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "eslint": "^8.3.0", + "eslint-config-react-app": "^7.0.1", + "eslint-webpack-plugin": "^3.1.1", + "file-loader": "^6.2.0", + "fs-extra": "^10.0.0", + "html-webpack-plugin": "^5.5.0", + "identity-obj-proxy": "^3.0.0", + "jest": "^27.4.3", + "jest-resolve": "^27.4.2", + "jest-watch-typeahead": "^1.0.0", + "mini-css-extract-plugin": "^2.4.5", + "postcss": "^8.4.4", + "postcss-flexbugs-fixes": "^5.0.2", + "postcss-loader": "^6.2.1", + "postcss-normalize": "^10.0.1", + "postcss-preset-env": "^7.0.1", + "prompts": "^2.4.2", + "react-app-polyfill": "^3.0.0", + "react-dev-utils": "^12.0.1", + "react-refresh": "^0.11.0", + "resolve": "^1.20.0", + "resolve-url-loader": "^4.0.0", + "sass-loader": "^12.3.0", + "semver": "^7.3.5", + "source-map-loader": "^3.0.0", + "style-loader": "^3.3.1", + "tailwindcss": "^3.0.2", + "terser-webpack-plugin": "^5.2.5", + "webpack": "^5.64.4", + "webpack-dev-server": "^4.6.0", + "webpack-manifest-plugin": "^4.0.2", + "workbox-webpack-plugin": "^6.4.1" + }, + "bin": { + "react-scripts": "bin/react-scripts.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + }, + "peerDependencies": { + "react": ">= 16", + "typescript": "^3.2.1 || ^4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-tooltip": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.22.0.tgz", + "integrity": "sha512-xbJBRY1LyHYd7j00UeBOqZR9SH/1S47Pe+m8vM1a+ZXglkeSNnBt5YYoPttU/amjC/VZJAPQ8+2B9x8Fl8U1qA==", + "dependencies": { + "@floating-ui/dom": "^1.0.0", + "classnames": "^2.3.0" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/react-xarrows": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-xarrows/-/react-xarrows-2.0.2.tgz", + "integrity": "sha512-tDlAqaxHNmy0vegW/6NdhoWyXJq1LANX/WUAlHyzoHe9BwFVnJPPDghmDjYeVr7XWFmBrVTUrHsrW7GKYI6HtQ==", + "dependencies": { + "@types/prop-types": "^15.7.3", + "lodash": "^4.17.21", + "prop-types": "^15.7.2" + }, + "funding": { + "type": "individual", + "url": "https://www.paypal.com/donate?hosted_button_id=CRQ343F9VTRS8" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz", + "integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsgen": "^0.7.1", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", + "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==" + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/reserved-words": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/reserved-words/-/reserved-words-0.1.2.tgz", + "integrity": "sha512-0S5SrIUJ9LfpbVl4Yzij6VipUdafHrOTzvmfazSw/jeZrZtQK303OPZW+obtkaw7jQlTQppy0UvZWm9872PbRw==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", + "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^7.0.35", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=8.9" + }, + "peerDependencies": { + "rework": "1.0.1", + "rework-visit": "1.0.0" + }, + "peerDependenciesMeta": { + "rework": { + "optional": true + }, + "rework-visit": { + "optional": true + } + } + }, + "node_modules/resolve-url-loader/node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" + } + }, + "node_modules/resolve-url-loader/node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/resolve-url-loader/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/resolve-url-loader/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup-plugin-terser/node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sanitize.css": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", + "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" + }, + "node_modules/sass": { + "version": "1.58.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.3.tgz", + "integrity": "sha512-Q7RaEtYf6BflYrQ+buPudKR26/lH+10EmO9bBqbmPh/KeLqv8bjpTNqxe71ocONqXq+jYiCbpPUmQMS+JJPk4A==", + "devOptional": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" + }, + "node_modules/selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "dependencies": { + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz", + "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", + "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead" + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/spdy-transport/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/spdy/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/spdy/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", + "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.3", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-loader": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", + "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/stylus": { + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz", + "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "debug": "^4.3.2", + "glob": "^7.1.6", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://opencollective.com/stylus" + } + }, + "node_modules/stylus/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/stylus/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/stylus/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "node_modules/svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", + "dependencies": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/svgo/node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/svgo/node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/svgo/node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/svgo/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/svgo/node_modules/domutils/node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/svgo/node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/tabbable": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz", + "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==" + }, + "node_modules/tailwindcss": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz", + "integrity": "sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==", + "dependencies": { + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", + "detective": "^5.2.1", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "lilconfig": "^2.0.6", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.18", + "postcss-import": "^14.1.0", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", + "postcss-nested": "6.0.0", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", + "quick-lru": "^5.1.1", + "resolve": "^1.22.1" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/tailwindcss/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tailwindcss/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "dependencies": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", + "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.14", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.14.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "node_modules/throat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==" + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "node_modules/tiny-glob": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", + "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", + "dev": true, + "dependencies": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, + "node_modules/tippy.js": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-5.1.2.tgz", + "integrity": "sha512-Qtrv2wqbRbaKMUb6bWWBQWPayvcDKNrGlvihxtsyowhT7RLGEh1STWuy6EMXC6QLkfKPB2MLnf8W2mzql9VDAw==", + "dependencies": { + "popper.js": "^1.16.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" + }, + "node_modules/tsconfig-paths": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz", + "integrity": "sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/typescript-plugin-css-modules": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/typescript-plugin-css-modules/-/typescript-plugin-css-modules-4.2.2.tgz", + "integrity": "sha512-X5OYGkX96ENq2c7xFJO4tgtiMTlBkOMoRmVHQXH2H4CGFcVODKGieDqPU2B0IV0I+AyvKYDFdKh4ZKtKxAcAww==", + "dev": true, + "dependencies": { + "@types/postcss-modules-local-by-default": "^4.0.0", + "@types/postcss-modules-scope": "^3.0.1", + "dotenv": "^16.0.3", + "icss-utils": "^5.1.0", + "less": "^4.1.3", + "lodash.camelcase": "^4.3.0", + "postcss": "^8.4.21", + "postcss-load-config": "^3.1.4", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "reserved-words": "^0.1.2", + "sass": "^1.58.3", + "source-map-js": "^1.0.2", + "stylus": "^0.59.0", + "tsconfig-paths": "^4.1.2" + }, + "peerDependencies": { + "typescript": ">=3.9.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "engines": { + "node": ">=10.4" + } + }, + "node_modules/webpack": { + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz", + "integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==", + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webpack-manifest-plugin": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", + "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", + "dependencies": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "peerDependencies": { + "webpack": "^4.44.2 || ^5.47.0" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "dependencies": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workbox-background-sync": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz", + "integrity": "sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-broadcast-update": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.5.4.tgz", + "integrity": "sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-build": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.5.4.tgz", + "integrity": "sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==", + "dependencies": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.11.1", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-replace": "^2.4.1", + "@surma/rollup-plugin-off-main-thread": "^2.2.3", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^7.1.6", + "lodash": "^4.17.20", + "pretty-bytes": "^5.3.0", + "rollup": "^2.43.1", + "rollup-plugin-terser": "^7.0.0", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "6.5.4", + "workbox-broadcast-update": "6.5.4", + "workbox-cacheable-response": "6.5.4", + "workbox-core": "6.5.4", + "workbox-expiration": "6.5.4", + "workbox-google-analytics": "6.5.4", + "workbox-navigation-preload": "6.5.4", + "workbox-precaching": "6.5.4", + "workbox-range-requests": "6.5.4", + "workbox-recipes": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4", + "workbox-streams": "6.5.4", + "workbox-sw": "6.5.4", + "workbox-window": "6.5.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/workbox-build/node_modules/@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "dependencies": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "ajv": ">=8" + } + }, + "node_modules/workbox-build/node_modules/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/workbox-build/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/workbox-build/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/workbox-build/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workbox-build/node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/workbox-build/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "node_modules/workbox-build/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/workbox-cacheable-response": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.5.4.tgz", + "integrity": "sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-core": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.4.tgz", + "integrity": "sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==" + }, + "node_modules/workbox-expiration": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.5.4.tgz", + "integrity": "sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-google-analytics": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.5.4.tgz", + "integrity": "sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==", + "dependencies": { + "workbox-background-sync": "6.5.4", + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } + }, + "node_modules/workbox-navigation-preload": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.5.4.tgz", + "integrity": "sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-precaching": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.5.4.tgz", + "integrity": "sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==", + "dependencies": { + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } + }, + "node_modules/workbox-range-requests": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.5.4.tgz", + "integrity": "sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-recipes": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.5.4.tgz", + "integrity": "sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==", + "dependencies": { + "workbox-cacheable-response": "6.5.4", + "workbox-core": "6.5.4", + "workbox-expiration": "6.5.4", + "workbox-precaching": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } + }, + "node_modules/workbox-routing": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.4.tgz", + "integrity": "sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-strategies": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.4.tgz", + "integrity": "sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==", + "dependencies": { + "workbox-core": "6.5.4" + } + }, + "node_modules/workbox-streams": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.5.4.tgz", + "integrity": "sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==", + "dependencies": { + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4" + } + }, + "node_modules/workbox-sw": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.5.4.tgz", + "integrity": "sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==" + }, + "node_modules/workbox-webpack-plugin": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.4.tgz", + "integrity": "sha512-LmWm/zoaahe0EGmMTrSLUi+BjyR3cdGEfU3fS6PN1zKFYbqAKuQ+Oy/27e4VSXsyIwAw8+QDfk1XHNGtZu9nQg==", + "dependencies": { + "fast-json-stable-stringify": "^2.1.0", + "pretty-bytes": "^5.4.1", + "upath": "^1.2.0", + "webpack-sources": "^1.4.3", + "workbox-build": "6.5.4" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "webpack": "^4.4.0 || ^5.9.0" + } + }, + "node_modules/workbox-webpack-plugin/node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/workbox-window": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.5.4.tgz", + "integrity": "sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==", + "dependencies": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "6.5.4" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@adobe/css-tools": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.2.tgz", + "integrity": "sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw==", + "dev": true + }, + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "requires": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + } + }, + "@babel/compat-data": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", + "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==" + }, + "@babel/core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz", + "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==", + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-module-transforms": "^7.20.2", + "@babel/helpers": "^7.20.5", + "@babel/parser": "^7.20.5", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/eslint-parser": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz", + "integrity": "sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==", + "requires": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "requires": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "requires": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", + "requires": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.5.tgz", + "integrity": "sha512-3RCdA/EmEaikrhayahwToF0fpweU/8o2p8vhc1c/1kftHOdTKuC65kik/TLc+qfbS8JKw4qqJbne4ovICDhmww==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.19.1", + "@babel/helper-split-export-declaration": "^7.18.6" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz", + "integrity": "sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.2.1" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "requires": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "requires": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "requires": { + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz", + "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==", + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==" + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-replace-supers": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", + "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "requires": { + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "requires": { + "@babel/types": "^7.20.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" + }, + "@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==" + }, + "@babel/helper-wrap-function": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "requires": { + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" + } + }, + "@babel/helpers": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz", + "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==", + "requires": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" + } + }, + "@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "requires": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==" + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.1.tgz", + "integrity": "sha512-Gh5rchzSwE4kC+o/6T8waD0WHEQIsDmjltY8WnWRXHUdH8axZhuH86Ov9M72YhJfDrZseQwuuWaaIT/TmePp3g==", + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.5.tgz", + "integrity": "sha512-Lac7PpRJXcC3s9cKsBfl+uc+DYXU5FD06BrTFunQO6QIQT+DwyzDPURAowI3bcvD1dZF/ank1Z5rstUJn3Hn4Q==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.19.1", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/plugin-syntax-decorators": "^7.19.0" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.2.tgz", + "integrity": "sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ==", + "requires": { + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.20.1" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz", + "integrity": "sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.19.0.tgz", + "integrity": "sha512-xaBZUEDntt4faL1yN8oIFlhfXeQAWJW7CLKYsHTUqriCUbj8xOra8bfxxKGi/UwExPFBuPdH4XfHc9rGQhrVkQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-flow": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", + "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.5.tgz", + "integrity": "sha512-WvpEIW9Cbj9ApF3yJCjIEEf1EiNJLtXagOrL5LNWEZOo3jv8pmPoYTSNJQvqej8OavVlgOoOPw6/htGZro6IkA==", + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.2.tgz", + "integrity": "sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.19.1", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.2.tgz", + "integrity": "sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw==", + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-flow-strip-types": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz", + "integrity": "sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg==", + "requires": { + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-flow": "^7.18.6" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "requires": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", + "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", + "requires": { + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", + "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", + "requires": { + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-simple-access": "^7.19.4" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", + "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", + "requires": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-identifier": "^7.19.1" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", + "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.5.tgz", + "integrity": "sha512-h7plkOmcndIUWXZFLgpbrh2+fXAi47zcUX7IrOQuZdLD0I0KvjJ6cvo3BEcAOsDOcZhVKGJqv07mkSqK0y2isQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-react-constant-elements": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.20.2.tgz", + "integrity": "sha512-KS/G8YI8uwMGKErLFOHS/ekhqdHhpEloxs43NecQHVgo2QuQSyJhGIY1fL8UGl9wy5ItVwwoUL4YxVqsplGq2g==", + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", + "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz", + "integrity": "sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.19.0" + } + }, + "@babel/plugin-transform-react-jsx-development": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", + "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "requires": { + "@babel/plugin-transform-react-jsx": "^7.18.6" + } + }, + "@babel/plugin-transform-react-pure-annotations": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", + "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", + "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.20.2", + "regenerator-transform": "^0.15.1" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", + "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", + "requires": { + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.2.tgz", + "integrity": "sha512-jvS+ngBfrnTUBfOQq8NfGnSbF9BrqlR6hjJ2yVxMkmO5nL/cdifNbI30EfjRlN4g5wYWNnMPyj5Sa6R1pbLeag==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.20.2", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/preset-env": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", + "requires": { + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.20.0", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.20.2", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.20.1", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.19.0", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/preset-react": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", + "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-react-display-name": "^7.18.6", + "@babel/plugin-transform-react-jsx": "^7.18.6", + "@babel/plugin-transform-react-jsx-development": "^7.18.6", + "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + } + }, + "@babel/preset-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", + "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-typescript": "^7.18.6" + } + }, + "@babel/runtime": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", + "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, + "@babel/runtime-corejs3": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.20.6.tgz", + "integrity": "sha512-tqeujPiuEfcH067mx+7otTQWROVMKHXEaOQcAeNV5dDdbPWvPcFA8/W9LXw2NfjNmOetqLl03dfnG2WALPlsRQ==", + "requires": { + "core-js-pure": "^3.25.1", + "regenerator-runtime": "^0.13.11" + } + }, + "@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + } + }, + "@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "requires": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" + }, + "@csstools/normalize.css": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", + "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==" + }, + "@csstools/postcss-cascade-layers": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", + "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", + "requires": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + } + }, + "@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "requires": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + } + }, + "@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "requires": {} + }, + "@csstools/selector-specificity": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", + "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "requires": {} + }, + "@eslint/eslintrc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + } + } + }, + "@floating-ui/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.1.tgz", + "integrity": "sha512-LSqwPZkK3rYfD7GKoIeExXOyYx6Q1O4iqZWwIehDNuv3Dv425FIAE8PRwtAx1imEolFTHgBEcoFHm9MDnYgPCg==" + }, + "@floating-ui/dom": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.1.1.tgz", + "integrity": "sha512-TpIO93+DIujg3g7SykEAGZMDtbJRrmnYRCNYSjJlvIbGhBjRSNTLVbNeDQBrzy9qDgUbiWdc7KA0uZHZ2tJmiw==", + "requires": { + "@floating-ui/core": "^1.1.0" + } + }, + "@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==" + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==" + }, + "@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "requires": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "requires": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + } + }, + "@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "requires": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + } + }, + "@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "requires": { + "@sinclair/typebox": "^0.24.1" + } + }, + "@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "requires": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "requires": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + } + }, + "@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + }, + "@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "requires": { + "eslint-scope": "5.1.1" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + } + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@patternfly/react-core": { + "version": "4.278.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.278.0.tgz", + "integrity": "sha512-w+0WgSa5O64yi50PpkYuuIt5N5S9svkJKxrhV4LQzOhAtSRTtbraOBRwEKodppT/oUeKMlAdDUWep1O3QluF3Q==", + "requires": { + "@patternfly/react-icons": "^4.93.7", + "@patternfly/react-styles": "^4.92.8", + "@patternfly/react-tokens": "^4.94.7", + "focus-trap": "6.9.2", + "react-dropzone": "9.0.0", + "tippy.js": "5.1.2", + "tslib": "^2.0.0" + } + }, + "@patternfly/react-icons": { + "version": "4.93.7", + "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.93.7.tgz", + "integrity": "sha512-3kr35dgba7Qz5CSzmfH0rIjSvBC5xkmiknf3SvVUVxaiVA7KRowID8viYHeZlf3v/Oa3sEewaH830Q0t+nWsZQ==", + "requires": {} + }, + "@patternfly/react-log-viewer": { + "version": "4.87.101", + "resolved": "https://registry.npmjs.org/@patternfly/react-log-viewer/-/react-log-viewer-4.87.101.tgz", + "integrity": "sha512-JSV1PEDIRZ3ffzfXGhz7x3wA8VlTcypnYGQZ7OEng7Kv91TiaThlVDr2j/uZ2LPaTam/PvH6bdxpFXXVslqqWw==", + "requires": { + "@patternfly/react-core": "^4.276.6", + "@patternfly/react-icons": "^4.93.6", + "@patternfly/react-styles": "^4.92.6", + "memoize-one": "^5.1.0", + "monaco-editor": "^0.33.0" + } + }, + "@patternfly/react-styles": { + "version": "4.92.8", + "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.92.8.tgz", + "integrity": "sha512-K4lUU8O4HiCX9NeuNUIrPgN3wlGERCxJVio+PAjd8hpJD/PKnjFfOJ9u6/Cii3qLy/5ZviWPRNHbGiwA/+YUhg==" + }, + "@patternfly/react-tokens": { + "version": "4.94.7", + "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.94.7.tgz", + "integrity": "sha512-h+ducOLDMSxcuec3+YY3x+stM5ZUSnrl/lC/eVmjypil2El08NuE2MNEPMQWdhrod6VRRZFMNqZw/m82iv6U1A==" + }, + "@pkgr/utils": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz", + "integrity": "sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "is-glob": "^4.0.3", + "open": "^8.4.0", + "picocolors": "^1.0.0", + "tiny-glob": "^0.2.9", + "tslib": "^2.4.0" + } + }, + "@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz", + "integrity": "sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==", + "requires": { + "ansi-html-community": "^0.0.8", + "common-path-prefix": "^3.0.0", + "core-js-pure": "^3.23.3", + "error-stack-parser": "^2.0.6", + "find-up": "^5.0.0", + "html-entities": "^2.1.0", + "loader-utils": "^2.0.4", + "schema-utils": "^3.0.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" + } + } + }, + "@remix-run/router": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.13.1.tgz", + "integrity": "sha512-so+DHzZKsoOcoXrILB4rqDkMDy7NLMErRdOxvzvOKb507YINKUP4Di+shbTZDhSE/pBZ+vr7XGIpcOO0VLSA+Q==" + }, + "@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + } + }, + "@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "requires": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + } + }, + "@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "requires": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + } + }, + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "dependencies": { + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + } + } + }, + "@rushstack/eslint-patch": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", + "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" + }, + "@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + }, + "@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "requires": { + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" + } + }, + "@svgr/babel-plugin-add-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==" + }, + "@svgr/babel-plugin-remove-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==" + }, + "@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", + "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==" + }, + "@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", + "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==" + }, + "@svgr/babel-plugin-svg-dynamic-title": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", + "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==" + }, + "@svgr/babel-plugin-svg-em-dimensions": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", + "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==" + }, + "@svgr/babel-plugin-transform-react-native-svg": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", + "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==" + }, + "@svgr/babel-plugin-transform-svg-component": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", + "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==" + }, + "@svgr/babel-preset": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", + "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", + "requires": { + "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", + "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", + "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", + "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", + "@svgr/babel-plugin-transform-svg-component": "^5.5.0" + } + }, + "@svgr/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", + "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", + "requires": { + "@svgr/plugin-jsx": "^5.5.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.0" + } + }, + "@svgr/hast-util-to-babel-ast": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", + "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", + "requires": { + "@babel/types": "^7.12.6" + } + }, + "@svgr/plugin-jsx": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", + "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", + "requires": { + "@babel/core": "^7.12.3", + "@svgr/babel-preset": "^5.5.0", + "@svgr/hast-util-to-babel-ast": "^5.5.0", + "svg-parser": "^2.0.2" + } + }, + "@svgr/plugin-svgo": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", + "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", + "requires": { + "cosmiconfig": "^7.0.0", + "deepmerge": "^4.2.2", + "svgo": "^1.2.2" + } + }, + "@svgr/webpack": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", + "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", + "requires": { + "@babel/core": "^7.12.3", + "@babel/plugin-transform-react-constant-elements": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.5", + "@svgr/core": "^5.5.0", + "@svgr/plugin-jsx": "^5.5.0", + "@svgr/plugin-svgo": "^5.5.0", + "loader-utils": "^2.0.0" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "@tanstack/query-core": { + "version": "4.36.1", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.36.1.tgz", + "integrity": "sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==" + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" + }, + "@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" + }, + "@types/babel__core": { + "version": "7.1.20", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", + "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", + "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/eslint": { + "version": "8.4.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", + "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==" + }, + "@types/express": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", + "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.31", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", + "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "requires": { + "@types/node": "*" + } + }, + "@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + }, + "@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + }, + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + }, + "@types/node": { + "version": "16.18.64", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.64.tgz", + "integrity": "sha512-TiY2gIDob8+QOPIcVpS0ZY+H1DVTfplBW6UgL2b4gOYbigIlKVIh6Lcv+7YDUciUTqhVLG91PrZBXW10IoBhtw==" + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "@types/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-0VLab/pcLTLcfbxi6THSIMVYcw9hEUBGvjwwaGpW77mMgRXfGF+a76t7BxTGyLh1y68tBvrffp8UWnqvm76+yg==", + "dev": true, + "requires": { + "postcss": "^8.0.0" + } + }, + "@types/postcss-modules-scope": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/postcss-modules-scope/-/postcss-modules-scope-3.0.1.tgz", + "integrity": "sha512-LNkp3c4ML9EQj2dgslp4i80Jxj72YK3HjYzrTn6ftUVylW1zaKFGqrMlNIyqBmPWmIhZ/Y5r0Y4T49Hk1IuDUg==", + "dev": true, + "requires": { + "postcss": "^8.0.0" + } + }, + "@types/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==" + }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "@types/q": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", + "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "@types/react": { + "version": "18.2.37", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.37.tgz", + "integrity": "sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "18.2.18", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", + "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", + "requires": { + "@types/react": "*" + } + }, + "@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "requires": { + "@types/node": "*" + } + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==" + }, + "@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "requires": { + "@types/node": "*" + } + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" + }, + "@types/trusted-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", + "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" + }, + "@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, + "@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "requires": { + "@types/node": "*" + } + }, + "@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz", + "integrity": "sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw==", + "requires": { + "@typescript-eslint/scope-manager": "5.53.0", + "@typescript-eslint/type-utils": "5.53.0", + "@typescript-eslint/utils": "5.53.0", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz", + "integrity": "sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==", + "requires": { + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0" + } + }, + "@typescript-eslint/types": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz", + "integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==" + }, + "@typescript-eslint/typescript-estree": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz", + "integrity": "sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==", + "requires": { + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.53.0.tgz", + "integrity": "sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==", + "requires": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.53.0", + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/typescript-estree": "5.53.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz", + "integrity": "sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==", + "requires": { + "@typescript-eslint/types": "5.53.0", + "eslint-visitor-keys": "^3.3.0" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.45.1.tgz", + "integrity": "sha512-WlXwY9dbmc0Lzu6xQOZ3yN8u/ws/1R8zPC16O217LMZJCbV2hJezqkWMUB+jMwguOJW+cukCDe92vcwwf8zwjQ==", + "requires": { + "@typescript-eslint/utils": "5.45.1" + } + }, + "@typescript-eslint/parser": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.53.0.tgz", + "integrity": "sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ==", + "requires": { + "@typescript-eslint/scope-manager": "5.53.0", + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/typescript-estree": "5.53.0", + "debug": "^4.3.4" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz", + "integrity": "sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==", + "requires": { + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0" + } + }, + "@typescript-eslint/types": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz", + "integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==" + }, + "@typescript-eslint/typescript-estree": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz", + "integrity": "sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==", + "requires": { + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz", + "integrity": "sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==", + "requires": { + "@typescript-eslint/types": "5.53.0", + "eslint-visitor-keys": "^3.3.0" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.1.tgz", + "integrity": "sha512-D6fCileR6Iai7E35Eb4Kp+k0iW7F1wxXYrOhX/3dywsOJpJAQ20Fwgcf+P/TDtvQ7zcsWsrJaglaQWDhOMsspQ==", + "requires": { + "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/visitor-keys": "5.45.1" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.53.0.tgz", + "integrity": "sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw==", + "requires": { + "@typescript-eslint/typescript-estree": "5.53.0", + "@typescript-eslint/utils": "5.53.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz", + "integrity": "sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==", + "requires": { + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0" + } + }, + "@typescript-eslint/types": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz", + "integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==" + }, + "@typescript-eslint/typescript-estree": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz", + "integrity": "sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==", + "requires": { + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.53.0.tgz", + "integrity": "sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==", + "requires": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.53.0", + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/typescript-estree": "5.53.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz", + "integrity": "sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==", + "requires": { + "@typescript-eslint/types": "5.53.0", + "eslint-visitor-keys": "^3.3.0" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/types": { + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.1.tgz", + "integrity": "sha512-HEW3U0E5dLjUT+nk7b4lLbOherS1U4ap+b9pfu2oGsW3oPu7genRaY9dDv3nMczC1rbnRY2W/D7SN05wYoGImg==" + }, + "@typescript-eslint/typescript-estree": { + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.1.tgz", + "integrity": "sha512-76NZpmpCzWVrrb0XmYEpbwOz/FENBi+5W7ipVXAsG3OoFrQKJMiaqsBMbvGRyLtPotGqUfcY7Ur8j0dksDJDng==", + "requires": { + "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/visitor-keys": "5.45.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/utils": { + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.1.tgz", + "integrity": "sha512-rlbC5VZz68+yjAzQBc4I7KDYVzWG2X/OrqoZrMahYq3u8FFtmQYc+9rovo/7wlJH5kugJ+jQXV5pJMnofGmPRw==", + "requires": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.45.1", + "@typescript-eslint/types": "5.45.1", + "@typescript-eslint/typescript-estree": "5.45.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.45.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.1.tgz", + "integrity": "sha512-cy9ln+6rmthYWjH9fmx+5FU/JDpjQb586++x2FZlveq7GdGuLLW9a2Jcst2TGekH82bXpfmRNSwP9tyEs6RjvQ==", + "requires": { + "@typescript-eslint/types": "5.45.1", + "eslint-visitor-keys": "^3.3.0" + } + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + } + } + }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "requires": {} + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "requires": {} + }, + "acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + } + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" + }, + "address": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.1.tgz", + "integrity": "sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==" + }, + "adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "requires": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "requires": {} + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==" + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "array-includes": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, + "array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.reduce": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", + "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + } + }, + "array.prototype.tosorted": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", + "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.1.3" + } + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" + }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, + "attr-accept": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz", + "integrity": "sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==", + "requires": { + "core-js": "^2.5.0" + }, + "dependencies": { + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + } + } + }, + "autoprefixer": { + "version": "10.4.13", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", + "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "requires": { + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001426", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "axe-core": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.2.tgz", + "integrity": "sha512-u2MVsXfew5HBvjsczCv+xlwdNnB1oQR9HlAcsejZttNjKKSkeDNVwB1vMThIUIFI9GoT57Vtk8iQLwqOfAkboA==" + }, + "axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==" + }, + "babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "requires": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "babel-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", + "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "requires": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + } + }, + "babel-plugin-named-asset-import": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", + "requires": {} + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.3" + } + }, + "babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "requires": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "babel-preset-react-app": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", + "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "requires": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-transform-flow-strip-types": "^7.16.0", + "@babel/plugin-transform-react-display-name": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.16.0", + "@babel/runtime": "^7.16.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + }, + "bfj": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", + "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", + "requires": { + "bluebird": "^3.5.5", + "check-types": "^11.1.1", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "bonjour-service": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz", + "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==", + "requires": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==" + }, + "builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "requires": { + "semver": "^7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001436", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001436.tgz", + "integrity": "sha512-ZmWkKsnC2ifEPoWUvSAIGyOYwT+keAaaWPHiQ9DfMqS1t6tfuyFYoWR78TeZtznkEQ64+vGXH9cZrElwR2Mrxg==" + }, + "case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==" + }, + "check-types": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.2.tgz", + "integrity": "sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA==" + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" + }, + "ci-info": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz", + "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==" + }, + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==" + }, + "classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, + "clean-css": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", + "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", + "requires": { + "source-map": "~0.6.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==" + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + } + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + }, + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + }, + "common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" + }, + "common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" + }, + "connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==" + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "requires": { + "is-what": "^3.14.1" + } + }, + "core-js": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.1.tgz", + "integrity": "sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA==" + }, + "core-js-compat": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.1.tgz", + "integrity": "sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A==", + "requires": { + "browserslist": "^4.21.4" + } + }, + "core-js-pure": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.1.tgz", + "integrity": "sha512-VVXcDpp/xJ21KdULRq/lXdLzQAtX7+37LzpyfFM973il0tWSsDEoyzG38G14AjTpK9VTfiNM9jnFauq/CpaWGQ==" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + }, + "css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-declaration-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", + "requires": {} + }, + "css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-loader": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.2.tgz", + "integrity": "sha512-oqGbbVcBJkm8QwmnNzrFrWTnudnRZC+1eXikLJl0n4ljcfotgRifpg2a1lKy8jTrc4/d9A/ap1GFq1jDKG7J+Q==", + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.18", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "css-minimizer-webpack-plugin": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", + "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "requires": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "postcss": "^8.3.5", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "requires": {} + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" + }, + "cssdb": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.2.0.tgz", + "integrity": "sha512-JYlIsE7eKHSi0UNuCyo96YuIDFqvhGgHw4Ck6lsN+DP0Tp8M64UTDT2trGbkMDqnCoEjks7CkS0XcjU0rkvBdg==" + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "cssnano": { + "version": "5.1.14", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.14.tgz", + "integrity": "sha512-Oou7ihiTocbKqi0J1bB+TRJIQX5RMR3JghA8hcWSw9mjBLQ5Y3RWqEDoYG3sRNlAbCIXpqMoZGbq5KDR3vdzgw==", + "requires": { + "cssnano-preset-default": "^5.2.13", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "cssnano-preset-default": { + "version": "5.2.13", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.13.tgz", + "integrity": "sha512-PX7sQ4Pb+UtOWuz8A1d+Rbi+WimBIxJTRyBdgGp1J75VU0r/HFQeLnMYgHiCAp6AR4rqrc7Y4R+1Rjk3KJz6DQ==", + "requires": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.3", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.1", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + } + }, + "cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "requires": {} + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "requires": { + "css-tree": "^1.1.2" + }, + "dependencies": { + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + } + } + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + } + } + }, + "csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, + "damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" + }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "requires": { + "execa": "^5.0.0" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + }, + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==" + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "requires": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "requires": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + } + }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==" + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + } + }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" + }, + "dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "requires": { + "@leichtgewicht/ip-codec": "^2.0.1" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "requires": { + "utila": "~0.4" + } + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" + } + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "dev": true + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "requires": { + "jake": "^10.8.5" + } + }, + "electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" + }, + "emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==" + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "enhanced-resolve": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", + "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "requires": { + "stackframe": "^1.3.4" + } + }, + "es-abstract": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", + "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } + }, + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "requires": { + "has": "^1.0.3" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, + "eslint": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", + "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", + "requires": { + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "requires": { + "type-fest": "^0.20.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + } + } + }, + "eslint-config-prettier": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz", + "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==", + "dev": true, + "requires": {} + }, + "eslint-config-react-app": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", + "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "requires": { + "@babel/core": "^7.16.0", + "@babel/eslint-parser": "^7.16.3", + "@rushstack/eslint-patch": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.5.0", + "@typescript-eslint/parser": "^5.5.0", + "babel-preset-react-app": "^10.0.1", + "confusing-browser-globals": "^1.0.11", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-testing-library": "^5.0.1" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "requires": { + "debug": "^3.2.7", + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" + } + }, + "eslint-import-resolver-typescript": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.3.tgz", + "integrity": "sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "enhanced-resolve": "^5.10.0", + "get-tsconfig": "^4.2.0", + "globby": "^13.1.2", + "is-core-module": "^2.10.0", + "is-glob": "^4.0.3", + "synckit": "^0.8.4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globby": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", + "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "requires": { + "debug": "^3.2.7" + } + }, + "eslint-plugin-es": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", + "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", + "dev": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-plugin-flowtype": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", + "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "requires": { + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" + } + }, + "eslint-plugin-import": { + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "requires": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", + "has": "^1.0.3", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", + "tsconfig-paths": "^3.14.1" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "requires": { + "esutils": "^2.0.2" + } + }, + "json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "requires": { + "minimist": "^1.2.0" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + } + } + }, + "eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "requires": { + "@typescript-eslint/experimental-utils": "^5.0.0" + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", + "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", + "requires": { + "@babel/runtime": "^7.18.9", + "aria-query": "^4.2.2", + "array-includes": "^3.1.5", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.4.3", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.3.2", + "language-tags": "^1.0.5", + "minimatch": "^3.1.2", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "eslint-plugin-n": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.6.1.tgz", + "integrity": "sha512-R9xw9OtCRxxaxaszTQmQAlPgM+RdGjaL1akWuY/Fv9fRAi8Wj4CUKc6iYVG8QNRjRuo8/BqVYIpfqberJUEacA==", + "dev": true, + "requires": { + "builtins": "^5.0.1", + "eslint-plugin-es": "^4.1.0", + "eslint-utils": "^3.0.0", + "ignore": "^5.1.1", + "is-core-module": "^2.11.0", + "minimatch": "^3.1.2", + "resolve": "^1.22.1", + "semver": "^7.3.8" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "dev": true, + "requires": {} + }, + "eslint-plugin-react": { + "version": "7.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", + "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", + "requires": { + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.8" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "requires": { + "esutils": "^2.0.2" + } + }, + "resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "requires": {} + }, + "eslint-plugin-testing-library": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.9.1.tgz", + "integrity": "sha512-6BQp3tmb79jLLasPHJmy8DnxREe+2Pgf7L+7o09TSWPfdqqtQfRZmZNetr5mOs3yqZk/MRNxpN3RUpJe0wB4LQ==", + "requires": { + "@typescript-eslint/utils": "^5.13.0" + } + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==" + }, + "eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "requires": { + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==" + }, + "expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "requires": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + } + }, + "express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "fastq": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", + "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "requires": { + "bser": "2.1.1" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "file-selector": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.19.tgz", + "integrity": "sha512-kCWw3+Aai8Uox+5tHCNgMFaUdgidxvMnLWO6fM5sZ0hA2wlHP5/DHGF0ECe84BiB95qdJbKNEJhWKVDvMN+JDQ==", + "requires": { + "tslib": "^2.0.1" + } + }, + "filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "requires": { + "minimatch": "^5.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", + "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==" + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "dependencies": { + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" + }, + "focus-trap": { + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-6.9.2.tgz", + "integrity": "sha512-gBEuXOPNOKPrLdZpMFUSTyIo1eT2NSZRrwZ9r/0Jqw5tmT3Yvxfmu8KBHw8xW2XQkw6E/JoG+OlEq7UDtSUNgw==", + "requires": { + "tabbable": "^5.3.2" + } + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, + "fork-ts-checker-webpack-plugin": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", + "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + } + } + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==" + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "get-tsconfig": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.4.0.tgz", + "integrity": "sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "globalyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", + "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", + "dev": true + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" + }, + "gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "requires": { + "duplexer": "^0.1.2" + } + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==" + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + }, + "html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "requires": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + } + }, + "html-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "requires": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + } + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "requires": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "requires": {} + }, + "idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, + "identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "requires": { + "harmony-reflect": "^1.4.6" + } + }, + "ignore": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", + "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==" + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true + }, + "immer": { + "version": "9.0.16", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", + "integrity": "sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ==" + }, + "immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "devOptional": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + } + } + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" + }, + "is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==" + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==" + }, + "is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==" + }, + "istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "requires": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "requires": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + } + }, + "jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "requires": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + } + }, + "jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "requires": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "requires": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + } + }, + "jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + }, + "jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "requires": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "requires": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*" + } + }, + "jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "requires": {} + }, + "jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==" + }, + "jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "requires": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + } + }, + "jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "requires": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + } + }, + "jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "requires": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "requires": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-watch-typeahead": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", + "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", + "requires": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.0.0", + "jest-regex-util": "^28.0.0", + "jest-watcher": "^28.0.0", + "slash": "^4.0.0", + "string-length": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + } + } + }, + "@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "requires": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "requires": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.16", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.16.tgz", + "integrity": "sha512-Mh3OP0oh8X7O7F9m5AplC+XHYLBWuPKNkGVD3gIZFLFebBnuFI2Nz5Sf8WLvwGxECJ8YjifQvFdh79ubODkdug==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + } + } + }, + "jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==" + }, + "jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "requires": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "dependencies": { + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + } + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" + }, + "string-length": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", + "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", + "requires": { + "char-regex": "^2.0.0", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "char-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", + "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==" + } + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "requires": { + "ansi-regex": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + } + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "requires": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-sdsl": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", + "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "requires": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==" + }, + "jsx-ast-utils": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", + "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "requires": { + "array-includes": "^3.1.5", + "object.assign": "^4.1.3" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, + "klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==" + }, + "language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" + }, + "language-tags": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.6.tgz", + "integrity": "sha512-HNkaCgM8wZgE/BZACeotAAgpL9FUjEnhgF0FVQMIgH//zqTPreLYMb3rWYkYAqPoF75Jwuycp1da7uz66cfFQg==", + "requires": { + "language-subtag-registry": "^0.3.20" + } + }, + "less": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "dev": true, + "requires": { + "copy-anything": "^2.0.1", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "parse-node-version": "^1.0.1", + "source-map": "~0.6.0", + "tslib": "^2.3.0" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==" + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==" + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "requires": { + "tslib": "^2.0.3" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "requires": { + "sourcemap-codec": "^1.4.8" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "requires": { + "tmpl": "1.0.5" + } + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "memfs": { + "version": "3.4.12", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.12.tgz", + "integrity": "sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw==", + "requires": { + "fs-monkey": "^1.0.3" + } + }, + "memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mini-css-extract-plugin": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.2.tgz", + "integrity": "sha512-EdlUizq13o0Pd+uCp+WO/JpkLvHRVGt97RqfeGhXqAcorYo1ypJSpkV+WDT0vY/kmh/p7wRdJNJtuyK540PXDw==", + "requires": { + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, + "monaco-editor": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.33.0.tgz", + "integrity": "sha512-VcRWPSLIUEgQJQIE0pVT8FcGBIgFoxz7jtqctE+IiCxWugD0DwgyQBcZBhdSrdMC84eumoqMZsGl2GTreOzwqw==" + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "requires": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + } + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" + }, + "needle": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", + "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", + "dev": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + } + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "requires": { + "boolbase": "^1.0.0" + } + }, + "nwsapi": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", + "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "object.fromentries": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz", + "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==", + "requires": { + "array.prototype.reduce": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "object.hasown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", + "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", + "requires": { + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "requires": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==" + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" + } + } + }, + "popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" + }, + "postcss": { + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-browser-comments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", + "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", + "requires": {} + }, + "postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "requires": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-convert-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "requires": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-custom-properties": { + "version": "12.1.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", + "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "requires": {} + }, + "postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "requires": {} + }, + "postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "requires": {} + }, + "postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "requires": {} + }, + "postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-flexbugs-fixes": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", + "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", + "requires": {} + }, + "postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "requires": {} + }, + "postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "requires": {} + }, + "postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "requires": {} + }, + "postcss-js": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", + "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", + "requires": { + "camelcase-css": "^2.0.1" + } + }, + "postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "requires": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + } + }, + "postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "requires": {} + }, + "postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "requires": {} + }, + "postcss-merge-longhand": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "requires": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-merge-rules": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.3.tgz", + "integrity": "sha512-LbLd7uFC00vpOuMvyZop8+vvhnfRGpp2S+IMQKeuOZZapPRY4SMq5ErjQeHbHsjCUgJkRNrlU+LmxsKIqPKQlA==", + "requires": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "requires": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-minify-params": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "requires": { + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "requires": {} + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-nested": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", + "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-nesting": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", + "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", + "requires": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-normalize": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", + "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", + "requires": { + "@csstools/normalize.css": "*", + "postcss-browser-comments": "^4", + "sanitize.css": "*" + } + }, + "postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "requires": {} + }, + "postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-normalize-unicode": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "requires": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "requires": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-opacity-percentage": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz", + "integrity": "sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w==" + }, + "postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "requires": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "requires": {} + }, + "postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-preset-env": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz", + "integrity": "sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag==", + "requires": { + "@csstools/postcss-cascade-layers": "^1.1.1", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.13", + "browserslist": "^4.21.4", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^7.1.0", + "postcss-attribute-case-insensitive": "^5.0.2", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.4", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.1", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.10", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.1", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.2.0", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.4", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-reduce-initial": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.1.tgz", + "integrity": "sha512-//jeDqWcHPuXGZLoolFrUXBDyuEGbr9S2rMo19bkTIjBQ4PqkaO+oI8wua5BOUxpfi97i3PCoInsiFIEBfkm9w==", + "requires": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "requires": {} + }, + "postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-selector-parser": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", + "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "requires": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + } + } + } + }, + "postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" + }, + "prettier": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", + "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "dev": true + }, + "pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==" + }, + "pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "requires": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + } + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "requires": { + "asap": "~2.0.6" + } + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "requires": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + } + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "requires": { + "performance-now": "^2.1.0" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-app-polyfill": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", + "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", + "requires": { + "core-js": "^3.19.2", + "object-assign": "^4.1.1", + "promise": "^8.1.0", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.9", + "whatwg-fetch": "^3.6.2" + } + }, + "react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "requires": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, + "react-dropzone": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-9.0.0.tgz", + "integrity": "sha512-wZ2o9B2qkdE3RumWhfyZT9swgJYJPeU5qHEcMU8weYpmLex1eeWX0CC32/Y0VutB+BBi2D+iePV/YZIiB4kZGw==", + "requires": { + "attr-accept": "^1.1.3", + "file-selector": "^0.1.8", + "prop-types": "^15.6.2", + "prop-types-extra": "^1.1.0" + } + }, + "react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "react-query": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/react-query/-/react-query-4.0.0.tgz", + "integrity": "sha512-qiW+Yvbl+EK8iwPDJAj4qWAKceh+g8Up8jxoNxJbzhV3bNheeyHF3EyynnkDO3S+CYgSwtCUFaP8vOjB62j7xQ==", + "requires": { + "@tanstack/query-core": "^4.0.0-beta.1", + "@types/use-sync-external-store": "^0.0.3", + "use-sync-external-store": "^1.2.0" + } + }, + "react-refresh": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", + "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==" + }, + "react-router": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.0.tgz", + "integrity": "sha512-hGZ0HXbwz3zw52pLZV3j3+ec+m/PQ9cTpBvqjFQmy2XVUWGn5MD+31oXHb6dVTxYzmAeaiUBYjkoNz66n3RGCg==", + "requires": { + "@remix-run/router": "1.14.0" + }, + "dependencies": { + "@remix-run/router": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.14.0.tgz", + "integrity": "sha512-WOHih+ClN7N8oHk9N4JUiMxQJmRVaOxcg8w7F/oHUXzJt920ekASLI/7cYX8XkntDWRhLZtsk6LbGrkgOAvi5A==" + } + } + }, + "react-router-dom": { + "version": "6.20.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.20.1.tgz", + "integrity": "sha512-npzfPWcxfQN35psS7rJgi/EW0Gx6EsNjfdJSAk73U/HqMEJZ2k/8puxfwHFgDQhBGmS3+sjnGbMdMSV45axPQw==", + "requires": { + "@remix-run/router": "1.13.1", + "react-router": "6.20.1" + }, + "dependencies": { + "react-router": { + "version": "6.20.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.20.1.tgz", + "integrity": "sha512-ccvLrB4QeT5DlaxSFFYi/KR8UMQ4fcD8zBcR71Zp1kaYTC5oJKYAp1cbavzGrogwxca+ubjkd7XjFZKBW8CxPA==", + "requires": { + "@remix-run/router": "1.13.1" + } + } + } + }, + "react-scripts": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", + "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", + "requires": { + "@babel/core": "^7.16.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", + "@svgr/webpack": "^5.5.0", + "babel-jest": "^27.4.2", + "babel-loader": "^8.2.3", + "babel-plugin-named-asset-import": "^0.3.8", + "babel-preset-react-app": "^10.0.1", + "bfj": "^7.0.2", + "browserslist": "^4.18.1", + "camelcase": "^6.2.1", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "css-loader": "^6.5.1", + "css-minimizer-webpack-plugin": "^3.2.0", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "eslint": "^8.3.0", + "eslint-config-react-app": "^7.0.1", + "eslint-webpack-plugin": "^3.1.1", + "file-loader": "^6.2.0", + "fs-extra": "^10.0.0", + "fsevents": "^2.3.2", + "html-webpack-plugin": "^5.5.0", + "identity-obj-proxy": "^3.0.0", + "jest": "^27.4.3", + "jest-resolve": "^27.4.2", + "jest-watch-typeahead": "^1.0.0", + "mini-css-extract-plugin": "^2.4.5", + "postcss": "^8.4.4", + "postcss-flexbugs-fixes": "^5.0.2", + "postcss-loader": "^6.2.1", + "postcss-normalize": "^10.0.1", + "postcss-preset-env": "^7.0.1", + "prompts": "^2.4.2", + "react-app-polyfill": "^3.0.0", + "react-dev-utils": "^12.0.1", + "react-refresh": "^0.11.0", + "resolve": "^1.20.0", + "resolve-url-loader": "^4.0.0", + "sass-loader": "^12.3.0", + "semver": "^7.3.5", + "source-map-loader": "^3.0.0", + "style-loader": "^3.3.1", + "tailwindcss": "^3.0.2", + "terser-webpack-plugin": "^5.2.5", + "webpack": "^5.64.4", + "webpack-dev-server": "^4.6.0", + "webpack-manifest-plugin": "^4.0.2", + "workbox-webpack-plugin": "^6.4.1" + }, + "dependencies": { + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "react-tooltip": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.22.0.tgz", + "integrity": "sha512-xbJBRY1LyHYd7j00UeBOqZR9SH/1S47Pe+m8vM1a+ZXglkeSNnBt5YYoPttU/amjC/VZJAPQ8+2B9x8Fl8U1qA==", + "requires": { + "@floating-ui/dom": "^1.0.0", + "classnames": "^2.3.0" + } + }, + "react-xarrows": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-xarrows/-/react-xarrows-2.0.2.tgz", + "integrity": "sha512-tDlAqaxHNmy0vegW/6NdhoWyXJq1LANX/WUAlHyzoHe9BwFVnJPPDghmDjYeVr7XWFmBrVTUrHsrW7GKYI6HtQ==", + "requires": { + "@types/prop-types": "^15.7.3", + "lodash": "^4.17.21", + "prop-types": "^15.7.2" + } + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "requires": { + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "requires": { + "minimatch": "^3.0.5" + } + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "regenerator-transform": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" + }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" + }, + "regexpu-core": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz", + "integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==", + "requires": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsgen": "^0.7.1", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + } + }, + "regjsgen": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", + "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==" + }, + "regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" + }, + "renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "requires": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "reserved-words": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/reserved-words/-/reserved-words-0.1.2.tgz", + "integrity": "sha512-0S5SrIUJ9LfpbVl4Yzij6VipUdafHrOTzvmfazSw/jeZrZtQK303OPZW+obtkaw7jQlTQppy0UvZWm9872PbRw==", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + }, + "resolve-url-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", + "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", + "requires": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^7.0.35", + "source-map": "0.6.1" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + } + } + }, + "resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==" + }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "requires": { + "fsevents": "~2.3.2" + } + }, + "rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "requires": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sanitize.css": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", + "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" + }, + "sass": { + "version": "1.58.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.3.tgz", + "integrity": "sha512-Q7RaEtYf6BflYrQ+buPudKR26/lH+10EmO9bBqbmPh/KeLqv8bjpTNqxe71ocONqXq+jYiCbpPUmQMS+JJPk4A==", + "devOptional": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "requires": { + "xmlchars": "^2.2.0" + } + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" + }, + "selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "requires": { + "node-forge": "^1" + } + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "optional": true + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + } + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "shell-quote": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz", + "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==" + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, + "source-map-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", + "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", + "requires": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + } + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, + "stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" + } + } + }, + "stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + } + } + }, + "string.prototype.matchall": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", + "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.3", + "side-channel": "^1.0.4" + } + }, + "string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" + }, + "strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==" + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + }, + "style-loader": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", + "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", + "requires": {} + }, + "stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "requires": { + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" + } + }, + "stylus": { + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz", + "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==", + "dev": true, + "requires": { + "@adobe/css-tools": "^4.0.1", + "debug": "^4.3.2", + "glob": "^7.1.6", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "dependencies": { + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + }, + "dependencies": { + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + } + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + } + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "requires": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + } + }, + "tabbable": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz", + "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==" + }, + "tailwindcss": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz", + "integrity": "sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==", + "requires": { + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", + "detective": "^5.2.1", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "lilconfig": "^2.0.6", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.18", + "postcss-import": "^14.1.0", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", + "postcss-nested": "6.0.0", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", + "quick-lru": "^5.1.1", + "resolve": "^1.22.1" + }, + "dependencies": { + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "requires": { + "is-glob": "^4.0.3" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + } + } + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" + }, + "temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==" + }, + "tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "requires": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "dependencies": { + "type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==" + } + } + }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "terser": { + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", + "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", + "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + } + } + }, + "terser-webpack-plugin": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "requires": { + "@jridgewell/trace-mapping": "^0.3.14", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.14.1" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "throat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==" + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "tiny-glob": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", + "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", + "dev": true, + "requires": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, + "tippy.js": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-5.1.2.tgz", + "integrity": "sha512-Qtrv2wqbRbaKMUb6bWWBQWPayvcDKNrGlvihxtsyowhT7RLGEh1STWuy6EMXC6QLkfKPB2MLnf8W2mzql9VDAw==", + "requires": { + "popper.js": "^1.16.0" + } + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "tough-cookie": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "dependencies": { + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" + } + } + }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "requires": { + "punycode": "^2.1.1" + } + }, + "tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" + }, + "tsconfig-paths": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz", + "integrity": "sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw==", + "dev": true, + "requires": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" + }, + "typescript-plugin-css-modules": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/typescript-plugin-css-modules/-/typescript-plugin-css-modules-4.2.2.tgz", + "integrity": "sha512-X5OYGkX96ENq2c7xFJO4tgtiMTlBkOMoRmVHQXH2H4CGFcVODKGieDqPU2B0IV0I+AyvKYDFdKh4ZKtKxAcAww==", + "dev": true, + "requires": { + "@types/postcss-modules-local-by-default": "^4.0.0", + "@types/postcss-modules-scope": "^3.0.1", + "dotenv": "^16.0.3", + "icss-utils": "^5.1.0", + "less": "^4.1.3", + "lodash.camelcase": "^4.3.0", + "postcss": "^8.4.21", + "postcss-load-config": "^3.1.4", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "reserved-words": "^0.1.2", + "sass": "^1.58.3", + "source-map-js": "^1.0.2", + "stylus": "^0.59.0", + "tsconfig-paths": "^4.1.2" + } + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==" + }, + "unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" + }, + "update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" + } + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "requires": { + "makeerror": "1.0.12" + } + }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" + }, + "webpack": { + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + } + } + }, + "webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "webpack-dev-server": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz", + "integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==", + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "dependencies": { + "ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "requires": {} + } + } + }, + "webpack-manifest-plugin": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", + "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", + "requires": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "dependencies": { + "webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "requires": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + } + } + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "requires": { + "iconv-lite": "0.4.24" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "word-wrap": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==" + }, + "workbox-background-sync": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz", + "integrity": "sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==", + "requires": { + "idb": "^7.0.1", + "workbox-core": "6.5.4" + } + }, + "workbox-broadcast-update": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.5.4.tgz", + "integrity": "sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==", + "requires": { + "workbox-core": "6.5.4" + } + }, + "workbox-build": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.5.4.tgz", + "integrity": "sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==", + "requires": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.11.1", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-replace": "^2.4.1", + "@surma/rollup-plugin-off-main-thread": "^2.2.3", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^7.1.6", + "lodash": "^4.17.20", + "pretty-bytes": "^5.3.0", + "rollup": "^2.43.1", + "rollup-plugin-terser": "^7.0.0", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "6.5.4", + "workbox-broadcast-update": "6.5.4", + "workbox-cacheable-response": "6.5.4", + "workbox-core": "6.5.4", + "workbox-expiration": "6.5.4", + "workbox-google-analytics": "6.5.4", + "workbox-navigation-preload": "6.5.4", + "workbox-precaching": "6.5.4", + "workbox-range-requests": "6.5.4", + "workbox-recipes": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4", + "workbox-streams": "6.5.4", + "workbox-sw": "6.5.4", + "workbox-window": "6.5.4" + }, + "dependencies": { + "@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "requires": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + } + }, + "ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "requires": { + "whatwg-url": "^7.0.0" + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "requires": { + "punycode": "^2.1.0" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } + } + }, + "workbox-cacheable-response": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.5.4.tgz", + "integrity": "sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==", + "requires": { + "workbox-core": "6.5.4" + } + }, + "workbox-core": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.4.tgz", + "integrity": "sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==" + }, + "workbox-expiration": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.5.4.tgz", + "integrity": "sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==", + "requires": { + "idb": "^7.0.1", + "workbox-core": "6.5.4" + } + }, + "workbox-google-analytics": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.5.4.tgz", + "integrity": "sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==", + "requires": { + "workbox-background-sync": "6.5.4", + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } + }, + "workbox-navigation-preload": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.5.4.tgz", + "integrity": "sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==", + "requires": { + "workbox-core": "6.5.4" + } + }, + "workbox-precaching": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.5.4.tgz", + "integrity": "sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==", + "requires": { + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } + }, + "workbox-range-requests": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.5.4.tgz", + "integrity": "sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==", + "requires": { + "workbox-core": "6.5.4" + } + }, + "workbox-recipes": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.5.4.tgz", + "integrity": "sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==", + "requires": { + "workbox-cacheable-response": "6.5.4", + "workbox-core": "6.5.4", + "workbox-expiration": "6.5.4", + "workbox-precaching": "6.5.4", + "workbox-routing": "6.5.4", + "workbox-strategies": "6.5.4" + } + }, + "workbox-routing": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.4.tgz", + "integrity": "sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==", + "requires": { + "workbox-core": "6.5.4" + } + }, + "workbox-strategies": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.4.tgz", + "integrity": "sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==", + "requires": { + "workbox-core": "6.5.4" + } + }, + "workbox-streams": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.5.4.tgz", + "integrity": "sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==", + "requires": { + "workbox-core": "6.5.4", + "workbox-routing": "6.5.4" + } + }, + "workbox-sw": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.5.4.tgz", + "integrity": "sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==" + }, + "workbox-webpack-plugin": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.4.tgz", + "integrity": "sha512-LmWm/zoaahe0EGmMTrSLUi+BjyR3cdGEfU3fS6PN1zKFYbqAKuQ+Oy/27e4VSXsyIwAw8+QDfk1XHNGtZu9nQg==", + "requires": { + "fast-json-stable-stringify": "^2.1.0", + "pretty-bytes": "^5.4.1", + "upath": "^1.2.0", + "webpack-sources": "^1.4.3", + "workbox-build": "6.5.4" + }, + "dependencies": { + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + } + } + }, + "workbox-window": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.5.4.tgz", + "integrity": "sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==", + "requires": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "6.5.4" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "requires": {} + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + } + } +} diff --git a/tools/debug-ui/package.json b/tools/debug-ui/package.json new file mode 100644 index 000000000..5a4b60c80 --- /dev/null +++ b/tools/debug-ui/package.json @@ -0,0 +1,52 @@ +{ + "name": "debug-ui", + "version": "0.1.0", + "private": true, + "dependencies": { + "@patternfly/react-log-viewer": "^4.87.101", + "@types/node": "^16.18.64", + "@types/react": "^18.2.37", + "@types/react-dom": "^18.2.18", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-query": "^4.0.0", + "react-router": "^6.21.0", + "react-router-dom": "^6.20.1", + "react-scripts": "^5.0.1", + "react-tooltip": "^5.22.0", + "react-xarrows": "^2.0.2" + }, + "scripts": { + "start": "DISABLE_ESLINT_PLUGIN=true react-scripts start", + "build": "react-scripts build", + "eject": "react-scripts eject", + "lint": "eslint . && prettier --check src", + "fix": "eslint . --fix && prettier --write src" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^5.52.0", + "@typescript-eslint/parser": "^5.52.0", + "eslint": "^8.34.0", + "eslint-config-prettier": "^8.6.0", + "eslint-import-resolver-typescript": "^3.5.3", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-n": "^15.6.1", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-react": "^7.32.2", + "prettier": "2.8.4", + "typescript": "^4.9.5", + "typescript-plugin-css-modules": "^4.2.2" + } +} \ No newline at end of file diff --git a/tools/debug-ui/public/index.html b/tools/debug-ui/public/index.html new file mode 100644 index 000000000..90b2b4e89 --- /dev/null +++ b/tools/debug-ui/public/index.html @@ -0,0 +1,26 @@ + + + + + + + + React App + + + + +
+ + + + \ No newline at end of file diff --git a/tools/debug-ui/src/App.scss b/tools/debug-ui/src/App.scss new file mode 100644 index 000000000..14af5c9bc --- /dev/null +++ b/tools/debug-ui/src/App.scss @@ -0,0 +1,26 @@ +.navbar { + padding: 4px; + background-color: #eee; + border-bottom: 1px solid gray; + + .nav-link { + margin: -4px 4px; + padding: 4px 8px; + text-decoration: none; + color: black; + + &:first-child { + margin-left: -4px; + } + + &.active { + background-color: rgb(53, 53, 159); + color: white; + } + + &:hover { + background-color: rgb(93, 93, 217); + color: white; + } + } +} diff --git a/tools/debug-ui/src/App.tsx b/tools/debug-ui/src/App.tsx new file mode 100644 index 000000000..09ba61458 --- /dev/null +++ b/tools/debug-ui/src/App.tsx @@ -0,0 +1,69 @@ +import './App.scss'; +import { NavLink } from 'react-router-dom'; +import { Navigate, Route, Routes, useParams } from 'react-router'; +import { ChainAndChunkInfoView } from './ChainAndChunkInfoView'; +import { ClusterView } from './ClusterView'; +import { EpochInfoView } from './EpochInfoView'; +import { HeaderBar } from './HeaderBar'; +import { LatestBlocksView } from './LatestBlocksView'; +import { NetworkInfoView } from './NetworkInfoView'; +import { EntityDebugView } from './entity_debug/EntityDebugView'; + +function useNodeAddr(): string { + const params = useParams<{ addr: string }>(); + const addr = params.addr || '127.0.0.1'; + return addr.includes(':') ? addr : addr + ':3030'; +} + +export const App = () => { + const addr = useNodeAddr(); + return ( +
+ +
+ + Latest Blocks + + + Network Info + + + Epoch Info + + + Chain & Chunk Info + + + Sync Info + + + Validator Info + + + Cluster View + + + Entity Debug + +
+ + } /> + } /> + } /> + } /> + } + /> + TODO
} /> + TODO} /> + } /> + } /> + + + ); +}; + +function navLinkClassName({ isActive }: { isActive: boolean }) { + return isActive ? 'nav-link active' : 'nav-link'; +} diff --git a/tools/debug-ui/src/BlocksView.scss b/tools/debug-ui/src/BlocksView.scss new file mode 100644 index 000000000..d509577f4 --- /dev/null +++ b/tools/debug-ui/src/BlocksView.scss @@ -0,0 +1,34 @@ +.chain-and-chunk-blocks-view { + .error { + color: red; + } +} +.chain-and-chunk-blocks-table { + table { + width: 100%; + border-collapse: collapse; + } + + table, + th, + td { + border: 1px solid black; + } + + td { + text-align: left; + vertical-align: top; + padding: 8px; + } + + th { + text-align: center; + vertical-align: center; + padding: 8px; + background-color: lightgrey; + } + + tr.active { + background-color: #eff8bf; + } +} diff --git a/tools/debug-ui/src/BlocksView.tsx b/tools/debug-ui/src/BlocksView.tsx new file mode 100644 index 000000000..f6da3292c --- /dev/null +++ b/tools/debug-ui/src/BlocksView.tsx @@ -0,0 +1,164 @@ +import './BlocksView.scss'; +import { useQuery } from 'react-query'; +import { fetchChainProcessingStatus, BlockProcessingStatus, ChunkProcessingStatus } from './api'; + +type BlocksViewProps = { + addr: string; +}; + +function prettyTime(datetime: string): string { + const time = new Date(Date.parse(datetime)); + return String(time.getUTCHours()).concat( + ':', + String(time.getUTCMinutes()).padStart(2, '0'), + ':', + String(time.getUTCSeconds()).padStart(2, '0'), + '.', + String(time.getUTCMilliseconds()).padStart(3, '0') + ); +} + +function printTimeInMs(time: number | null): string { + if (time == null) { + return 'N/A'; + } else { + return time + 'ms'; + } +} + +function printDuration(start: string, end: string): string { + const duration = Date.parse(end) - Date.parse(start); + if (duration > 0) { + return `+${duration}ms`; + } else { + return `${duration}ms`; + } +} + +function getChunkStatusSymbol(chunkStatus: ChunkProcessingStatus): string { + switch (chunkStatus) { + case 'Completed': + return '✔'; + case 'Requested': + return '⬇'; + case 'NeedToRequest': + return '.'; + default: + return ''; + } +} + +function printBlockStatus(blockStatus: BlockProcessingStatus): string { + if (typeof blockStatus === 'string') { + return blockStatus; + } + if ('Error' in blockStatus) { + return `Error: ${blockStatus.Error}`; + } + return `Dropped: ${blockStatus.Dropped}`; +} + +export const BlocksView = ({ addr }: BlocksViewProps) => { + const { + data: chainProcessingInfo, + error: chainProcessingInfoError, + isLoading: chainProcessingInfoLoading, + } = useQuery(['chainProcessingInfo', addr], () => fetchChainProcessingStatus(addr)); + if (chainProcessingInfoLoading) { + return
Loading...
; + } else if (chainProcessingInfoError) { + return ( +
+
{(chainProcessingInfoError as Error).stack}
+
+ ); + } else if (!chainProcessingInfo) { + return ( +
+
No Data
+
+ ); + } + const numShards = + chainProcessingInfo!.status_response.ChainProcessingStatus.blocks_info[0].chunks_info! + .length; + const shardIndices = [...Array(numShards).keys()]; + return ( +
+
+ + + + + + + + + + {shardIndices.map((shardIndex) => { + return ; + })} + + + + {chainProcessingInfo!.status_response.ChainProcessingStatus.blocks_info.map( + (block) => { + return ( + + + + + + + + + {block.chunks_info!.map((chunk) => { + if (chunk) { + return ( + + ); + } else { + return ; + } + })} + + ); + } + )} + +
HeightHashReceivedStatusIn Progress forIn Orphan forMissing Chunks for Shard {shardIndex}
{block.height}{block.hash}{prettyTime(block.received_timestamp)}{printBlockStatus(block.block_status)}{printTimeInMs(block.in_progress_ms)}{printTimeInMs(block.orphaned_ms)}{printTimeInMs(block.missing_chunks_ms)} + {`${ + chunk.status + } ${getChunkStatusSymbol( + chunk.status + )}`} + {chunk.completed_timestamp && ( + <> +
Completed @ BR + {`${printDuration( + block.received_timestamp, + chunk.completed_timestamp + )}`} + + )} + {chunk.requested_timestamp && ( + <> +
Requested @ BR + {`${printDuration( + block.received_timestamp, + chunk.requested_timestamp + )}`} + + )} + {chunk.request_duration && ( + <> +
Duration + {`${printTimeInMs( + chunk.request_duration + )}`} + + )} +
No Chunk
+ + ); +}; diff --git a/tools/debug-ui/src/ChainAndChunkInfoView.scss b/tools/debug-ui/src/ChainAndChunkInfoView.scss new file mode 100644 index 000000000..5e30a4353 --- /dev/null +++ b/tools/debug-ui/src/ChainAndChunkInfoView.scss @@ -0,0 +1,33 @@ +.chain-and-chunk-info-view { + .error { + color: red; + } + + table { + width: 100%; + border-collapse: collapse; + } + + table, + th, + td { + border: 1px solid black; + } + + td { + text-align: left; + vertical-align: top; + padding: 8px; + } + + th { + text-align: center; + vertical-align: center; + padding: 8px; + background-color: lightgrey; + } + + tr.active { + background-color: #eff8bf; + } +} diff --git a/tools/debug-ui/src/ChainAndChunkInfoView.tsx b/tools/debug-ui/src/ChainAndChunkInfoView.tsx new file mode 100644 index 000000000..59086be4b --- /dev/null +++ b/tools/debug-ui/src/ChainAndChunkInfoView.tsx @@ -0,0 +1,37 @@ +import './ChainAndChunkInfoView.scss'; +import { NavLink, Navigate, Route, Routes } from 'react-router-dom'; +import { BlocksView } from './BlocksView'; +import { ChainInfoSummaryView } from './ChainInfoSummaryView'; +import { FloatingChunksView } from './FloatingChunksView'; + +type ChainAndChunkInfoViewProps = { + addr: string; +}; + +export const ChainAndChunkInfoView = ({ addr }: ChainAndChunkInfoViewProps) => { + return ( +
+
+ + Chain Info Summary + + + Floating Chunks + + + Blocks + +
+ + } /> + } /> + } /> + } /> + +
+ ); +}; + +function navLinkClassName({ isActive }: { isActive: boolean }) { + return isActive ? 'nav-link active' : 'nav-link'; +} diff --git a/tools/debug-ui/src/ChainInfoSummaryView.scss b/tools/debug-ui/src/ChainInfoSummaryView.scss new file mode 100644 index 000000000..ad9baade3 --- /dev/null +++ b/tools/debug-ui/src/ChainInfoSummaryView.scss @@ -0,0 +1,5 @@ +.chain-info-summary-view { + .error { + color: red; + } +} diff --git a/tools/debug-ui/src/ChainInfoSummaryView.tsx b/tools/debug-ui/src/ChainInfoSummaryView.tsx new file mode 100644 index 000000000..be002ce52 --- /dev/null +++ b/tools/debug-ui/src/ChainInfoSummaryView.tsx @@ -0,0 +1,91 @@ +import './ChainInfoSummaryView.scss'; +import { useMemo } from 'react'; +import { useQuery } from 'react-query'; +import { fetchChainProcessingStatus, fetchFullStatus } from './api'; + +type ChainInfoSummaryViewProps = { + addr: string; +}; + +export const ChainInfoSummaryView = ({ addr }: ChainInfoSummaryViewProps) => { + const { + data: fullStatus, + error: fullStatusError, + isLoading: fullStatusLoading, + } = useQuery(['fullStatus', addr], () => fetchFullStatus(addr)); + const { + data: chainProcessingInfo, + error: chainProcessingInfoError, + isLoading: chainProcessingInfoLoading, + } = useQuery(['chainProcessingInfo', addr], () => fetchChainProcessingStatus(addr)); + const { + chainInfoHead, + chainInfoHeaderHead, + numBlocksOrphanPool, + numBlocksMissingChunksPool, + numBlocksProcessing, + } = useMemo(() => { + let chainInfoHead = ''; + let chainInfoHeaderHead = ''; + let numBlocksOrphanPool = -1; + let numBlocksMissingChunksPool = -1; + let numBlocksProcessing = -1; + if (fullStatus && chainProcessingInfo) { + const head = fullStatus.detailed_debug_status!.current_head_status; + chainInfoHead += head.hash; + chainInfoHead += '@'; + chainInfoHead += head.height; + const headerHead = fullStatus.detailed_debug_status!.current_header_head_status; + chainInfoHeaderHead += headerHead.hash; + chainInfoHeaderHead += '@'; + chainInfoHeaderHead += headerHead.height; + const chainInfo = chainProcessingInfo.status_response.ChainProcessingStatus; + numBlocksOrphanPool = chainInfo.num_orphans; + numBlocksMissingChunksPool = chainInfo.num_blocks_missing_chunks; + numBlocksProcessing = chainInfo.num_blocks_in_processing; + } + return { + chainInfoHead, + chainInfoHeaderHead, + numBlocksOrphanPool, + numBlocksMissingChunksPool, + numBlocksProcessing, + }; + }, [fullStatus, chainProcessingInfo]); + if (fullStatusLoading || chainProcessingInfoLoading) { + return
Loading...
; + } else if (fullStatusError || chainProcessingInfoError) { + return ( +
+
+ {((fullStatusError || chainProcessingInfoError) as Error).stack} +
+
+ ); + } else if (!fullStatus || !chainProcessingInfo) { + return ( +
+
No Data
+
+ ); + } + return ( +
+

+ Current head: {`${chainInfoHead}`} +

+

+ Current header head: {`${chainInfoHeaderHead}`} +

+

+ Number of blocks in orphan pool: {`${numBlocksOrphanPool}`} +

+

+ Number of blocks in missing chunks pool: {`${numBlocksMissingChunksPool}`} +

+

+ Number of blocks in processing: {`${numBlocksProcessing}`} +

+
+ ); +}; diff --git a/tools/debug-ui/src/ClusterNodeView.scss b/tools/debug-ui/src/ClusterNodeView.scss new file mode 100644 index 000000000..a004c11a0 --- /dev/null +++ b/tools/debug-ui/src/ClusterNodeView.scss @@ -0,0 +1,49 @@ +.cluster-node-view { + display: flex; + align-items: center; + + &:hover { + background-color: lightgray; + } + + .addr { + width: 150px; + } + + .loading { + color: gray; + font-style: italic; + } + + .height { + width: 80px; + margin: 0 10px; + font-family: 'Courier New', Courier, monospace; + text-align: center; + } + + .account { + width: 300px; + margin: 0 10px; + overflow: hidden; + text-overflow: ellipsis; + } + + .error { + color: red; + } + + .old-height { + color: brown; + background-color: #fcc; + } + + a { + text-decoration: none; + } + + .sync-status { + width: 200px; + margin: 0 10px; + } +} diff --git a/tools/debug-ui/src/ClusterNodeView.tsx b/tools/debug-ui/src/ClusterNodeView.tsx new file mode 100644 index 000000000..194ae5df7 --- /dev/null +++ b/tools/debug-ui/src/ClusterNodeView.tsx @@ -0,0 +1,138 @@ +import { useEffect } from 'react'; +import { useQuery } from 'react-query'; +import { + SyncStatusResponse, + TrackedShardsResponse, + fetchBasicStatus, + fetchFullStatus, + fetchSyncStatus, + fetchTrackedShards, +} from './api'; +import './ClusterNodeView.scss'; + +interface Props { + addr: string; + highestHeight: number; + basicStatusChanged: (addr: string, name: string | null, height: number) => void; + newNodesDiscovered: (nodes: string[]) => void; +} + +export const ClusterNodeView = ({ + addr, + highestHeight, + basicStatusChanged, + newNodesDiscovered, +}: Props) => { + const status = useQuery(['status', addr], () => fetchBasicStatus(addr)); + const fullStatus = useQuery(['fullStatus', addr], () => fetchFullStatus(addr)); + const syncStatus = useQuery(['syncStatus', addr], () => fetchSyncStatus(addr)); + const trackedShards = useQuery(['trackedShards', addr], () => fetchTrackedShards(addr)); + + useEffect(() => { + if (status.data) { + basicStatusChanged( + addr, + status.data.validator_account_id, + status.data.sync_info.latest_block_height + ); + } + }, [addr, status.data, basicStatusChanged]); + + useEffect(() => { + const networkInfo = fullStatus.data?.detailed_debug_status?.network_info; + if (networkInfo) { + const newAddrs = []; + for (const peer of networkInfo.connected_peers) { + newAddrs.push(peer.addr); + } + if (networkInfo.tier1_connections) { + for (const peer of networkInfo.tier1_connections) { + newAddrs.push(peer.addr); + } + } + const newAddrsWithCorrectedPort = []; + for (const addr of newAddrs) { + newAddrsWithCorrectedPort.push(addr.split(':')[0] + ':3030'); + } + + newNodesDiscovered(newAddrsWithCorrectedPort); + } + }, [addr, fullStatus.data, newNodesDiscovered]); + + const anyError = status.error || fullStatus.error || syncStatus.error || trackedShards.error; + const anyLoading = + status.isLoading || fullStatus.isLoading || syncStatus.isLoading || trackedShards.isLoading; + return ( +
+ + {status.data && ( +
+ {status.data.sync_info.latest_block_height} +
+ )} + {status.data && status.data.validator_account_id && ( +
{status.data.validator_account_id}
+ )} + {syncStatus.data && ( +
{syncStatusToText(syncStatus.data)}
+ )} + {trackedShards.data &&
{trackedShardsToText(trackedShards.data)}
} + {!!anyError &&
{'' + anyError}
} + {anyLoading && !anyError &&
Loading...
} +
+ ); +}; + +function syncStatusToText(syncStatus: SyncStatusResponse): string { + const status = syncStatus.status_response.SyncStatus; + if (status == null) { + return 'No sync status??'; + } + if (status === 'AwaitingPeers') { + return 'Awaiting peers'; + } + if (status === 'NoSync') { + return ''; + } + if (status === 'StateSyncDone') { + return 'State sync done'; + } + if ('EpochSync' in status) { + return 'Epoch sync'; + } + if ('HeaderSync' in status) { + return 'Header sync'; + } + if ('StateSync' in status) { + return 'State sync'; + } + return `Body sync ${status.BodySync.start_height} -> ${status.BodySync.highest_height}`; +} + +function booleanArrayToIndexList(array: boolean[]): number[] { + const result = []; + for (let i = 0; i < array.length; i++) { + if (array[i]) { + result.push(i); + } + } + return result; +} + +function trackedShardsToText(trackedShards: TrackedShardsResponse): string { + const { shards_tracked_this_epoch, shards_tracked_next_epoch } = + trackedShards.status_response.TrackedShards; + const thisShards = booleanArrayToIndexList(shards_tracked_this_epoch).join(', '); + const nextShards = booleanArrayToIndexList(shards_tracked_next_epoch).join(', '); + return `[${thisShards}] next: [${nextShards}]`; +} diff --git a/tools/debug-ui/src/ClusterView.scss b/tools/debug-ui/src/ClusterView.scss new file mode 100644 index 000000000..13f42efeb --- /dev/null +++ b/tools/debug-ui/src/ClusterView.scss @@ -0,0 +1,8 @@ +.cluster-view { + padding: 10px; + + .title { + font-weight: bold; + margin-bottom: 10px; + } +} diff --git a/tools/debug-ui/src/ClusterView.tsx b/tools/debug-ui/src/ClusterView.tsx new file mode 100644 index 000000000..2a292ef93 --- /dev/null +++ b/tools/debug-ui/src/ClusterView.tsx @@ -0,0 +1,78 @@ +import { useCallback, useEffect, useRef, useState } from 'react'; +import { ClusterNodeView } from './ClusterNodeView'; +import './ClusterView.scss'; + +interface Props { + initialAddr: string; +} + +function sortingKeyForNode(addr: string, addrToName: Map): string { + if (addrToName.has(addr)) { + return '0' + addrToName.get(addr); + } else { + return '1' + addr; + } +} + +export class DiscoveryNodes { + nodes: Set = new Set(); + addrToName: Map = new Map(); + + reset(initialAddr: string) { + this.nodes.clear(); + this.addrToName.clear(); + this.nodes.add(initialAddr); + } + + sorted(): string[] { + return Array.from(this.nodes).sort((a, b) => { + return sortingKeyForNode(a, this.addrToName).localeCompare( + sortingKeyForNode(b, this.addrToName) + ); + }); + } +} + +export const ClusterView = ({ initialAddr }: Props) => { + const [sortedNodes, setSortedNodes] = useState([]); + const [highestHeight, setHighestHeight] = useState(0); + const nodes = useRef(new DiscoveryNodes()); + + useEffect(() => { + nodes.current.reset(initialAddr); + setHighestHeight(0); + setSortedNodes(nodes.current.sorted()); + }, [initialAddr]); + + const basicStatusCallback = useCallback((addr: string, name: string | null, height: number) => { + setHighestHeight((prev) => Math.max(prev, height)); + if (name) { + nodes.current.addrToName.set(addr, name); + } + setSortedNodes(nodes.current.sorted()); + }, []); + + const newNodesDiscoveredCallback = useCallback((newIps: string[]) => { + for (const addr of newIps) { + nodes.current.nodes.add(addr); + } + setSortedNodes(nodes.current.sorted()); + }, []); + + return ( +
+
Discovered {sortedNodes.length} nodes in cluster
+ {sortedNodes.map((addr) => { + return ( + + ); + })} +
+ ); +}; diff --git a/tools/debug-ui/src/ConnectionStorageView.scss b/tools/debug-ui/src/ConnectionStorageView.scss new file mode 100644 index 000000000..9980f1fc7 --- /dev/null +++ b/tools/debug-ui/src/ConnectionStorageView.scss @@ -0,0 +1,3 @@ +.connection-storage-table { + margin: 10px; +} diff --git a/tools/debug-ui/src/ConnectionStorageView.tsx b/tools/debug-ui/src/ConnectionStorageView.tsx new file mode 100644 index 000000000..4ca9f875d --- /dev/null +++ b/tools/debug-ui/src/ConnectionStorageView.tsx @@ -0,0 +1,54 @@ +import { useQuery } from 'react-query'; +import { toHumanTime } from './utils'; +import { fetchRecentOutboundConnections } from './api'; +import './ConnectionStorageView.scss'; + +type ConnectionStorageViewProps = { + addr: string; +}; + +export const ConnectionStorageView = ({ addr }: ConnectionStorageViewProps) => { + const { + data: connectionStore, + error, + isLoading, + } = useQuery(['connectionStore', addr], () => fetchRecentOutboundConnections(addr)); + + if (isLoading) { + return
Loading...
; + } else if (error) { + return
{(error as Error).stack}
; + } + return ( + + + + + + + + + {connectionStore!.status_response.RecentOutboundConnections.recent_outbound_connections.map( + (conn) => { + return ( + + + + + + + ); + } + )} + +
Peer IDPeer addressTime establishedTime connected until
{conn.peer_id}{conn.addr} + {toHumanTime( + Math.floor(Date.now() / 1000) - conn.time_established + )} + + {toHumanTime( + Math.floor(Date.now() / 1000) - conn.time_connected_until + )} +
+ ); +}; diff --git a/tools/debug-ui/src/CurrentPeersView.scss b/tools/debug-ui/src/CurrentPeersView.scss new file mode 100644 index 000000000..07a72cf3c --- /dev/null +++ b/tools/debug-ui/src/CurrentPeersView.scss @@ -0,0 +1,30 @@ +.current-peers-view { + padding: 10px; + + .peer_in_sync { + background-color: green; + color: white; + } + + .peer_ahead { + background-color: lightblue; + } + + .peer_ahead_alot { + background-color: blueviolet; + color: white; + } + + .peer_behind_a_little { + background-color: yellowgreen; + } + + .peer_behind { + background-color: yellow; + } + + .peer_far_behind { + background-color: red; + color: white; + } +} diff --git a/tools/debug-ui/src/CurrentPeersView.tsx b/tools/debug-ui/src/CurrentPeersView.tsx new file mode 100644 index 000000000..0d81eb725 --- /dev/null +++ b/tools/debug-ui/src/CurrentPeersView.tsx @@ -0,0 +1,331 @@ +import { MouseEvent, useCallback, useMemo, useState } from 'react'; +import { useQuery } from 'react-query'; +import { PeerInfoView, fetchEpochInfo, fetchFullStatus } from './api'; +import { addDebugPortLink, formatDurationInMillis, formatTraffic } from './utils'; +import './CurrentPeersView.scss'; + +type NetworkInfoViewProps = { + addr: string; +}; + +type PeerInfo = { + peer: PeerInfoView; + validator: string[]; + routedValidator: string[]; + statusClassName: string; +}; + +function peerClass(current_height: number, peer_height: number): string { + if (peer_height > current_height + 5) { + return 'peer_ahead_alot'; + } + if (peer_height > current_height + 2) { + return 'peer_ahead'; + } + + if (peer_height < current_height - 100) { + return 'peer_far_behind'; + } + if (peer_height < current_height - 10) { + return 'peer_behind'; + } + if (peer_height < current_height - 3) { + return 'peer_behind_a_little'; + } + return 'peer_in_sync'; +} + +function getIntersection(setA: Set, setB: Set): Set { + const intersection = new Set([...setA].filter((element) => setB.has(element))); + + return intersection; +} + +function getDifference(setA: Set, setB: Set): Set { + return new Set([...setA].filter((element) => !setB.has(element))); +} + +export const CurrentPeersView = ({ addr }: NetworkInfoViewProps) => { + const { + data: fullStatus, + error: fullStatusError, + isLoading: fullStatusLoading, + } = useQuery(['fullStatus', addr], () => fetchFullStatus(addr)); + const { + data: epochInfo, + error: epochInfoError, + isLoading: epochInfoLoading, + } = useQuery(['epochInfo', addr], () => fetchEpochInfo(addr)); + + const { blockProducers, chunkProducers, knownSet, reachableSet, numPeersByStatus, peers } = + useMemo(() => { + if (fullStatus && epochInfo) { + const epochId = fullStatus?.sync_info.epoch_id; + const networkInfo = fullStatus.detailed_debug_status!.network_info; + const knownSet = new Set(networkInfo.known_producers.map((p) => p.account_id)); + const reachableSet = new Set( + networkInfo.known_producers + .filter((p) => (p.next_hops?.length ?? 0) > 0) + .map((p) => p.account_id) + ); + let blockProducers = new Set(); + let chunkProducers = new Set(); + for (const oneEpoch of epochInfo.status_response.EpochInfo) { + if (oneEpoch.epoch_id === epochId) { + blockProducers = new Set( + oneEpoch.block_producers.map((bp) => bp.account_id) + ); + chunkProducers = new Set(oneEpoch.chunk_only_producers); + break; + } + } + + const currentHeight = fullStatus.sync_info.latest_block_height; + const numPeersByStatus = new Map(); + const peers = [] as PeerInfo[]; + for (const peer of networkInfo.connected_peers) { + const validator = []; + const routedValidator = []; + for (const element of networkInfo.known_producers) { + if ( + blockProducers.has(element.account_id) || + chunkProducers.has(element.account_id) + ) { + if (element.peer_id === peer.peer_id) { + // This means that the peer that we're connected to is a validator. + validator.push(element.account_id); + } else if (element.next_hops?.includes(peer.peer_id)) { + // This means that the peer that we're connected to is on the shortest path + // to this validator. + routedValidator.push(element.account_id); + } + } + } + const statusClassName = peerClass(currentHeight, peer.height || 0); + numPeersByStatus.set( + statusClassName, + (numPeersByStatus.get(statusClassName) || 0) + 1 + ); + peers.push({ + peer, + validator, + routedValidator, + statusClassName, + }); + } + return { + blockProducers, + chunkProducers, + knownSet, + reachableSet, + numPeersByStatus, + peers, + }; + } + return { + blockProducers: new Set(), + chunkProducers: new Set(), + knownSet: new Set(), + reachableSet: new Set(), + numPeersByStatus: new Map(), + peers: [], + }; + }, [fullStatus, epochInfo]); + + if (fullStatusLoading || epochInfoLoading) { + return
Loading...
; + } + if (fullStatusError || epochInfoError) { + return ( +
+
{((fullStatusError || epochInfoError) as Error).stack}
+
+ ); + } + if (!fullStatus || !epochInfo) { + return ( +
+
No Data
+
+ ); + } + + const detailedDebugStatus = fullStatus.detailed_debug_status!; + + return ( +
+

+ PeerId: {fullStatus.node_public_key} +

+

+ Current Sync Status: {detailedDebugStatus.sync_status} +

+

+ Number of peers: {detailedDebugStatus.network_info.num_connected_peers} /{' '} + {detailedDebugStatus.network_info.peer_max_count} +

+ +

+ Block producers: {blockProducers.size}{' '} +

    +
  • + Unknown:{' '} + {[...getDifference(blockProducers, knownSet)].join(', ') || '(none)'} +
  • +
  • + Known but not reachable:{' '} + {[ + ...getDifference( + getIntersection(blockProducers, knownSet), + reachableSet + ), + ].join(', ') || '(none)'} +
  • +
+

+ +

+ Chunk producers: {chunkProducers.size}{' '} +

    +
  • + Unknown:{' '} + {[...getDifference(chunkProducers, knownSet)].join(', ') || '(none)'} +
  • +
  • + Known but not reachable:{' '} + {[ + ...getDifference( + getIntersection(chunkProducers, knownSet), + reachableSet + ), + ].join(', ') || '(none)'} +
  • +
+

+ +

+ Unknown means that we didn't receive 'announce' + information about this validator (so we don't know on which peer it is). This + usually means that the validator didn't connect to the network during current + epoch.
+ Unreachable means, that we know the peer_id of this validator, but + we cannot find it in our routing table. This usually means that validator did + connect to the network in the past, but now it is gone for at least 1 hour. +

+ + + + {[ + ['peer_ahead_alot', 'Peer ahead a lot'], + ['peer_ahead', 'Peer ahead'], + ['peer_in_sync', 'Peer in sync'], + ['peer_behind_a_little', 'Peer behind a little'], + ['peer_behind', 'Peer behind'], + ['peer_far_behind', 'Peer far behind'], + ].map(([className, description], i) => ( + + ))} + +
+ {description} {numPeersByStatus.get(className) || 0} +
+ + + + + + + + + + + + + + + + + + + + + {peers.map(({ peer, validator, routedValidator, statusClassName }) => { + return ( + + + + {' '} + {/* strips prefix 'ed25519:' */} + + + + + + + + + + + + ); + })} + +
AddressValidator?Account IDLast pingHeightLast Block HashTracked ShardsArchivalConnection typeNonceFirst connectionTraffic (last minute)Route to validators
{addDebugPortLink(peer.addr)}{validator.join(', ')}{peer.peer_id.substring(8, 14)}... 60 * 1000 + ? 'peer_far_behind' + : '' + }> + {formatDurationInMillis(peer.last_time_received_message_millis)} + {peer.height}{peer.block_hash}{JSON.stringify(peer.tracked_shards)}{JSON.stringify(peer.archival)}{peer.is_outbound_peer ? 'OUT' : 'IN'} + {peer.nonce} +
+ {peer.nonce > 1660000000 + ? formatDurationInMillis(Date.now() - peer.nonce * 1000) + : 'old style nonce'} +
+ {formatDurationInMillis( + peer.connection_established_time_millis + )} + + {formatTraffic( + peer.received_bytes_per_sec, + peer.sent_bytes_per_sec + )} + + +
+
+ ); +}; + +const CollapsableValidatorList = ({ validators }: { validators: string[] }) => { + const [showAll, setShowAll] = useState(false); + const callback = useCallback((e: MouseEvent) => { + e.preventDefault(); + setShowAll((show) => !show); + }, []); + const MAX_TO_SHOW = 2; + if (validators.length <= MAX_TO_SHOW) { + return <>{validators.join(', ')}; + } + if (showAll) { + return ( + <> + {validators.join(', ')}{' '} + + Show fewer + + + ); + } + return ( + <> + {validators.slice(0, MAX_TO_SHOW).join(', ')}, ... +
+ + Show all {validators.length} + + + ); +}; diff --git a/tools/debug-ui/src/EpochInfoView.scss b/tools/debug-ui/src/EpochInfoView.scss new file mode 100644 index 000000000..b0642f791 --- /dev/null +++ b/tools/debug-ui/src/EpochInfoView.scss @@ -0,0 +1,32 @@ +.epoch-info-view { + .error { + color: red; + } + + table { + width: 100%; + border-collapse: collapse; + } + + th, + td { + border: 1px solid #444; + } + + td { + text-align: left; + vertical-align: top; + padding: 8px; + } + + th { + text-align: center; + vertical-align: center; + padding: 8px; + background-color: lightgrey; + } + + .content { + padding: 10px; + } +} diff --git a/tools/debug-ui/src/EpochInfoView.tsx b/tools/debug-ui/src/EpochInfoView.tsx new file mode 100644 index 000000000..2e60aac63 --- /dev/null +++ b/tools/debug-ui/src/EpochInfoView.tsx @@ -0,0 +1,40 @@ +import './EpochInfoView.scss'; +import { NavLink } from 'react-router-dom'; +import { Navigate, Route, Routes } from 'react-router'; +import { EpochShardsView } from './EpochShardsView'; +import { EpochValidatorsView } from './EpochValidatorsView'; +import { RecentEpochsView } from './RecentEpochsView'; + +type EpochInfoViewProps = { + addr: string; +}; + +export const EpochInfoView = ({ addr }: EpochInfoViewProps) => { + return ( +
+
+ + Recent Epochs + + + Validators + + + Shard Sizes + +
+
+ + } /> + } /> + } /> + } /> + +
+
+ ); +}; + +function navLinkClassName({ isActive }: { isActive: boolean }) { + return isActive ? 'nav-link active' : 'nav-link'; +} diff --git a/tools/debug-ui/src/EpochShardsView.scss b/tools/debug-ui/src/EpochShardsView.scss new file mode 100644 index 000000000..2b97f9dca --- /dev/null +++ b/tools/debug-ui/src/EpochShardsView.scss @@ -0,0 +1,70 @@ +.epoch-shards-table { + .shard-cell { + display: flex; + align-items: center; + flex-wrap: wrap; + + .shard-size-bar { + flex: 1; + display: flex; + align-items: center; + margin-right: 8px; + + .bar { + background-color: rgb(14, 14, 188); + height: 8px; + margin-right: 6px; + } + + .text { + font-size: 14px; + white-space: nowrap; + } + } + + .shard-parts { + font-size: 14px; + } + } + + th:first-child { + border: none; + background: none; + } + + thead tr:first-child th { + background: none; + } + + thead tr:first-child th:nth-child(3) { + border: none; + } + + $current-border: 4px solid #3bda26; + + thead tr:first-child th:nth-child(2) { + border-top: $current-border; + border-left: $current-border; + border-right: $current-border; + } + + thead tr:nth-child(2) th, + tbody td { + &:nth-child(2) { + border-left: $current-border; + border-right: $current-border; + } + } + + tbody td:first-child { + background-color: lightgray; + } + + tbody tr:last-child td:nth-child(2) { + border-bottom: $current-border; + } + + thead tr:nth-child(2) th:nth-child(2) { + background-color: #d6ffd0; + } +} diff --git a/tools/debug-ui/src/EpochShardsView.tsx b/tools/debug-ui/src/EpochShardsView.tsx new file mode 100644 index 000000000..544d40308 --- /dev/null +++ b/tools/debug-ui/src/EpochShardsView.tsx @@ -0,0 +1,107 @@ +import { useQuery } from 'react-query'; +import { fetchEpochInfo } from './api'; +import './EpochShardsView.scss'; + +function humanFileSize(bytes: number, si = false, dp = 1): string { + const thresh = si ? 1000 : 1024; + + if (Math.abs(bytes) < thresh) { + return bytes + ' B'; + } + + const units = si + ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']; + let u = -1; + const r = 10 ** dp; + + do { + bytes /= thresh; + ++u; + } while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1); + + return bytes.toFixed(dp) + ' ' + units[u]; +} + +type EpochShardsViewProps = { + addr: string; +}; + +export const EpochShardsView = ({ addr }: EpochShardsViewProps) => { + const { + data: epochData, + error: epochError, + isLoading: epochIsLoading, + } = useQuery(['epochInfo', addr], () => fetchEpochInfo(addr)); + + if (epochIsLoading) { + return
Loading...
; + } + if (epochError) { + return
{(epochError as Error).stack}
; + } + const epochs = epochData!.status_response.EpochInfo; + + let numShards = 0; + let maxShardSize = 0; + for (const epoch of epochs.slice(1)) { + numShards = Math.max(epoch.shards_size_and_parts.length, numShards); + for (const [size] of epoch.shards_size_and_parts) { + maxShardSize = Math.max(maxShardSize, size); + } + } + return ( + + + + + + + + + + {epochs.slice(1).map((epoch) => { + return ; + })} + + + + {[...Array(numShards).keys()].map((i) => { + return ( + + + {epochs.slice(1).map((epoch) => { + if (epoch.shards_size_and_parts.length <= i) { + return <>; + } + const [size, parts, requested] = epoch.shards_size_and_parts[i]; + return ( + + ); + })} + + ); + })} + +
Current EpochPast Epochs
{epoch.epoch_id.substring(0, 6)}...
Shard {i} +
+ {drawShardSizeBar(size, maxShardSize)} +
{parts} parts
+
+
+ ); +}; + +function drawShardSizeBar(size: number, maxSize: number): JSX.Element { + const width = (size / maxSize) * 100 + 5; + const text = humanFileSize(size); + return ( +
+
+
{text}
+
+ ); +} diff --git a/tools/debug-ui/src/EpochValidatorsView.scss b/tools/debug-ui/src/EpochValidatorsView.scss new file mode 100644 index 000000000..0c08bcf7b --- /dev/null +++ b/tools/debug-ui/src/EpochValidatorsView.scss @@ -0,0 +1,118 @@ +.epoch-validators-table { + td { + vertical-align: middle; + } + + .produced-and-expected-bar { + display: flex; + align-items: center; + + .produced { + background-color: green; + height: 6px; + } + + .missed { + background-color: red; + height: 6px; + } + + .produced-count { + color: green; + font-size: 12px; + margin-right: 4px; + } + + .missed-count { + color: red; + font-size: 12px; + margin-left: 4px; + } + } + + .stake-bar { + display: flex; + flex-wrap: wrap; + align-items: center; + + .bar { + background-color: rgb(14, 14, 188); + height: 6px; + margin-right: 4px; + } + + .text { + font-size: 12px; + } + } + + .kickout-reason { + color: red; + } + + $current-border: 4px solid #3bda26; + $next-border: 3px solid gray; + + th.small-text { + font-size: 12px; + } + + thead tr:first-child { + th { + background: none; + border: none; + } + + th:nth-child(3) { + border-left: $current-border; + border-top: $current-border; + border-right: $current-border; + } + + th:nth-child(2) { + border-left: $next-border; + } + } + + thead tr:nth-child(2) th, + tbody td { + &:nth-child(2) { + border-left: $next-border; + } + + &:nth-child(2), + &:nth-child(3), + &:nth-child(4), + &:nth-child(5) { + opacity: 0.5; + } + + &:nth-child(6) { + border-left: $current-border; + } + + &:nth-child(10) { + border-right: $current-border; + } + } + + thead tr:nth-child(2) th { + &:nth-child(6), + &:nth-child(7), + &:nth-child(8), + &:nth-child(9), + &:nth-child(10) { + background-color: #d6ffd0; + } + } + + tbody tr:last-child td { + &:nth-child(6), + &:nth-child(7), + &:nth-child(8), + &:nth-child(9), + &:nth-child(10) { + border-bottom: $current-border; + } + } +} diff --git a/tools/debug-ui/src/EpochValidatorsView.tsx b/tools/debug-ui/src/EpochValidatorsView.tsx new file mode 100644 index 000000000..6790d866e --- /dev/null +++ b/tools/debug-ui/src/EpochValidatorsView.tsx @@ -0,0 +1,342 @@ +import { useId } from 'react'; +import { useQuery } from 'react-query'; +import { Tooltip } from 'react-tooltip'; +import { ValidatorKickoutReason, fetchEpochInfo } from './api'; +import './EpochValidatorsView.scss'; + +interface ProducedAndExpected { + produced: number; + expected: number; +} + +type ValidatorRole = 'BlockProducer' | 'ChunkOnlyProducer' | 'None'; + +interface CurrentValidatorInfo { + stake: number; + shards: number[]; + blocks: ProducedAndExpected; + chunks: ProducedAndExpected; +} + +interface NextValidatorInfo { + stake: number; + shards: number[]; +} + +interface ValidatorInfo { + accountId: string; + current: CurrentValidatorInfo | null; + next: NextValidatorInfo | null; + proposalStake: number | null; + kickoutReason: ValidatorKickoutReason | null; + roles: ValidatorRole[]; +} + +class Validators { + validators: Map = new Map(); + + constructor(private numEpochs: number) {} + + validator(accountId: string): ValidatorInfo { + if (this.validators.has(accountId)) { + return this.validators.get(accountId)!; + } + const roles = [] as ValidatorRole[]; + for (let i = 0; i < this.numEpochs; i++) { + roles.push('None'); + } + this.validators.set(accountId, { + accountId, + current: null, + next: null, + proposalStake: null, + kickoutReason: null, + roles, + }); + return this.validators.get(accountId)!; + } + + setValidatorRole(accountId: string, epochIndex: number, role: ValidatorRole) { + const validator = this.validator(accountId); + validator.roles[epochIndex] = role; + } + + sorted(): ValidatorInfo[] { + const validators = [...this.validators.values()]; + function sortingKey(info: ValidatorInfo) { + if (info.current !== null) { + return [0, -info.current.stake]; + } + if (info.next !== null) { + return [1, -info.next.stake]; + } + if (info.proposalStake !== null) { + return [2, -info.proposalStake]; + } + return [3, 0]; + } + validators.sort((a, b) => { + const [ax, ay] = sortingKey(a); + const [bx, by] = sortingKey(b); + if (ax == bx) { + return ay - by; + } + return ax - bx; + }); + return validators; + } +} + +type EpochValidatorViewProps = { + addr: string; +}; + +export const EpochValidatorsView = ({ addr }: EpochValidatorViewProps) => { + const { + data: epochData, + error: epochError, + isLoading: epochIsLoading, + } = useQuery(['epochInfo', addr], () => fetchEpochInfo(addr)); + + if (epochIsLoading) { + return
Loading...
; + } + if (epochError) { + return
{(epochError as Error).stack}
; + } + let maxStake = 0, + totalStake = 0, + maxExpectedBlocks = 0, + maxExpectedChunks = 0; + const epochs = epochData!.status_response.EpochInfo; + const validators = new Validators(epochs.length); + const currentValidatorInfo = epochData!.status_response.EpochInfo[1].validator_info; + for (const validatorInfo of currentValidatorInfo.current_validators) { + const validator = validators.validator(validatorInfo.account_id); + const stake = parseFloat(validatorInfo.stake); + validator.current = { + stake, + shards: validatorInfo.shards, + blocks: { + produced: validatorInfo.num_produced_blocks, + expected: validatorInfo.num_expected_blocks, + }, + chunks: { + produced: validatorInfo.num_produced_chunks, + expected: validatorInfo.num_expected_chunks, + }, + }; + maxStake = Math.max(maxStake, stake); + totalStake += stake; + maxExpectedBlocks = Math.max(maxExpectedBlocks, validatorInfo.num_expected_blocks); + maxExpectedChunks = Math.max(maxExpectedChunks, validatorInfo.num_expected_chunks); + } + for (const validatorInfo of currentValidatorInfo.next_validators) { + const validator = validators.validator(validatorInfo.account_id); + validator.next = { + stake: parseFloat(validatorInfo.stake), + shards: validatorInfo.shards, + }; + } + for (const proposal of currentValidatorInfo.current_proposals) { + const validator = validators.validator(proposal.account_id); + validator.proposalStake = parseFloat(proposal.stake); + } + for (const kickout of currentValidatorInfo.prev_epoch_kickout) { + const validator = validators.validator(kickout.account_id); + validator.kickoutReason = kickout.reason; + } + epochs.forEach((epochInfo, index) => { + for (const chunkOnlyProducer of epochInfo.chunk_only_producers) { + validators.setValidatorRole(chunkOnlyProducer, index, 'ChunkOnlyProducer'); + } + for (const blockProducer of epochInfo.block_producers) { + validators.setValidatorRole(blockProducer.account_id, index, 'BlockProducer'); + } + }); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + {epochs.slice(2).map((epoch) => { + return ( + + ); + })} + + + + {validators.sorted().map((validator) => { + return ( + + + + + + + + + + + + + + + {validator.roles.slice(2).map((role, i) => { + return ; + })} + + ); + })} + +
Next EpochCurrent EpochPast Epochs
ValidatorRoleShardsStakeProposalRoleShardsStakeBlocksChunksKickout + {epoch.epoch_id.substring(0, 4)}... +
{validator.accountId}{renderRole(validator.roles[0])}{validator.next?.shards?.join(',') ?? ''} + {drawStakeBar(validator.next?.stake ?? null, maxStake, totalStake)} + {drawStakeBar(validator.proposalStake, maxStake, totalStake)}{renderRole(validator.roles[1])}{validator.current?.shards?.join(',') ?? ''} + {drawStakeBar( + validator.current?.stake ?? null, + maxStake, + totalStake + )} + + {drawProducedAndExpectedBar( + validator.current?.blocks ?? null, + maxExpectedBlocks + )} + + {drawProducedAndExpectedBar( + validator.current?.chunks ?? null, + maxExpectedChunks + )} + + + {renderRole(role)}
+ ); +}; + +function drawProducedAndExpectedBar( + producedAndExpected: ProducedAndExpected | null, + maxExpected: number +): JSX.Element { + if (producedAndExpected === null) { + return <>; + } + const { produced, expected } = producedAndExpected; + if (expected == 0) { + return
0
; + } + const expectedWidth = (expected / maxExpected) * 100 + 10; + let producedWidth = (expectedWidth * produced) / expected; + let missedWidth = (expectedWidth * (expected - produced)) / expected; + if (produced !== expected) { + if (producedWidth < 5) { + producedWidth = 5; + missedWidth = expectedWidth - producedWidth; + } + if (missedWidth < 5) { + missedWidth = 5; + producedWidth = expectedWidth - missedWidth; + } + } + return ( +
+
{produced}
+
+ {produced !== expected && ( + <> +
+
{expected - produced}
+ + )} +
+ ); +} + +function drawStakeBar(stake: number | null, maxStake: number, totalStake: number): JSX.Element { + if (stake === null) { + return <>; + } + const width = (stake / maxStake) * 100 + 5; + const stakeText = Math.floor(stake / 1e24).toLocaleString('en-US'); + const stakePercentage = ((100 * stake) / totalStake).toFixed(2) + '%'; + return ( +
+
+
+ {stakeText} ({stakePercentage}) +
+
+ ); +} + +function renderRole(role: ValidatorRole): JSX.Element { + switch (role) { + case 'BlockProducer': + return BP; + case 'ChunkOnlyProducer': + return CP; + default: + return <>; + } +} + +const KickoutReason = ({ reason }: { reason: ValidatorKickoutReason | null }) => { + const id = useId(); + if (reason === null) { + return <>; + } + let kickoutSummary = ''; + let kickoutReason = ''; + if (reason == 'Slashed') { + kickoutSummary = 'Slashed'; + kickoutReason = 'Validator was slashed'; + } else if (reason == 'Unstaked') { + kickoutSummary = 'Unstaked'; + kickoutReason = 'Validator unstaked'; + } else if (reason == 'DidNotGetASeat') { + kickoutSummary = 'Seat'; + kickoutReason = 'Validator did not get a seat'; + } else if ('NotEnoughBlocks' in reason) { + kickoutSummary = '#Blocks'; + kickoutReason = `Validator did not produce enough blocks: expected ${reason.NotEnoughBlocks.expected}, actually produced ${reason.NotEnoughBlocks.produced}`; + } else if ('NotEnoughChunks' in reason) { + kickoutSummary = '#Chunks'; + kickoutReason = `Validator did not produce enough chunks: expected ${reason.NotEnoughChunks.expected}, actually produced ${reason.NotEnoughChunks.produced}`; + } else if ('NotEnoughStake' in reason) { + kickoutSummary = 'LowStake'; + kickoutReason = `Validator did not have enough stake: minimum stake required was ${reason.NotEnoughStake.threshold}, but validator only had ${reason.NotEnoughStake.stake}`; + } else { + kickoutSummary = 'Other'; + kickoutReason = JSON.stringify(reason); + } + return ( + <> + + {kickoutSummary} + + + + ); +}; diff --git a/tools/debug-ui/src/FloatingChunksView.scss b/tools/debug-ui/src/FloatingChunksView.scss new file mode 100644 index 000000000..4d47c1499 --- /dev/null +++ b/tools/debug-ui/src/FloatingChunksView.scss @@ -0,0 +1,34 @@ +.floating-chunks-view { + .error { + color: red; + } +} +.floating-chunks-table { + table { + width: 100%; + border-collapse: collapse; + } + + table, + th, + td { + border: 1px solid black; + } + + td { + text-align: left; + vertical-align: top; + padding: 8px; + } + + th { + text-align: center; + vertical-align: center; + padding: 8px; + background-color: lightgrey; + } + + tr.active { + background-color: #eff8bf; + } +} diff --git a/tools/debug-ui/src/FloatingChunksView.tsx b/tools/debug-ui/src/FloatingChunksView.tsx new file mode 100644 index 000000000..de1b16329 --- /dev/null +++ b/tools/debug-ui/src/FloatingChunksView.tsx @@ -0,0 +1,64 @@ +import './FloatingChunksView.scss'; +import { useQuery } from 'react-query'; +import { fetchChainProcessingStatus } from './api'; + +type FloatingChunksViewProps = { + addr: string; +}; + +export const FloatingChunksView = ({ addr }: FloatingChunksViewProps) => { + const { + data: chainProcessingInfo, + error: chainProcessingInfoError, + isLoading: chainProcessingInfoLoading, + } = useQuery(['chainProcessingInfo', addr], () => fetchChainProcessingStatus(addr)); + if (chainProcessingInfoLoading) { + return
Loading...
; + } else if (chainProcessingInfoError) { + return ( +
+
{(chainProcessingInfoError as Error).stack}
+
+ ); + } else if (!chainProcessingInfo) { + return ( +
+
No Data
+
+ ); + } + return ( +
+

+ {' '} + Floating chunks are chunks that we are not yet sure which blocks they belong to.{' '} +

+ + + + + + + + + + + + {chainProcessingInfo!.status_response.ChainProcessingStatus.floating_chunks_info.map( + (chunk) => { + return ( + + + + + + + + ); + } + )} + +
HeightShardIdHashCreated byStatus
{chunk.height_created}{chunk.shard_id}{chunk.chunk_hash}{chunk.created_by}{chunk.status}
+
+ ); +}; diff --git a/tools/debug-ui/src/HeaderBar.scss b/tools/debug-ui/src/HeaderBar.scss new file mode 100644 index 000000000..02e5321ec --- /dev/null +++ b/tools/debug-ui/src/HeaderBar.scss @@ -0,0 +1,37 @@ +.header-bar { + width: 100%; + display: flex; + align-items: center; + background-color: #eee; + border-bottom: 1px solid gray; + padding: 4px; + box-sizing: border-box; + + .loading { + color: gray; + font-style: italic; + } + + .error { + color: red; + } + + .label { + margin: -4px 4px -4px 4px; + padding: 4px 8px; + border-left: 1px solid gray; + border-right: 1px solid gray; + background-color: #666; + color: white; + } + + .label:first-child { + border-left: none; + margin-left: -4px; + } + + .value { + margin-right: 20px; + margin-left: 4px; + } +} diff --git a/tools/debug-ui/src/HeaderBar.tsx b/tools/debug-ui/src/HeaderBar.tsx new file mode 100644 index 000000000..5513a9ce7 --- /dev/null +++ b/tools/debug-ui/src/HeaderBar.tsx @@ -0,0 +1,88 @@ +import { useQuery } from 'react-query'; +import { fetchBasicStatus } from './api'; +import './HeaderBar.scss'; + +type Props = { + addr: string; +}; + +export const HeaderBar = ({ addr }: Props) => { + const { + data: nodeStatus, + error, + isLoading, + } = useQuery(['status', addr], () => fetchBasicStatus(addr)); + if (isLoading) { + return ( +
+
Loading...
+
+ ); + } + if (error) { + return ( +
+
{'' + error}
+
+ ); + } + if (!nodeStatus) { + return ( +
+
No node status
+
+ ); + } + const version = nodeStatus.version; + return ( +
+
Chain
+
{nodeStatus.chain_id}
+ {nodeStatus.validator_account_id &&
Validator
} + {nodeStatus.validator_account_id && ( +
{nodeStatus.validator_account_id}
+ )} +
Protocol
+
{nodeStatus.protocol_version}
+
Build
+
+ {version.version === 'trunk' && 'Nightly build '} + {version.version === version.build && 'Release '} + {version.version === 'trunk' || version.version === version.build ? ( + + {version.build} + + ) : ( + `Release ${version.version} (build ${version.build})` + )} +
+
rustc
+
{version.rustc_version}
+
Uptime
+
{formatDurationInSec(nodeStatus.uptime_sec)}
+
Address
+
{addr}
+
+ ); +}; + +function formatDurationInSec(totalSeconds: number): string { + const seconds = totalSeconds % 60; + const total_minutes = (totalSeconds - seconds) / 60; + const minutes = total_minutes % 60; + const total_hours = (total_minutes - minutes) / 60; + const hours = total_hours % 24; + const days = (total_hours - hours) / 24; + return `${days}d ${addZeros(hours)}:${addZeros(minutes)}:${addZeros(seconds)}`; +} + +function addZeros(x: number): string { + if (x >= 10) { + return '' + x; + } else { + return '0' + x; + } +} diff --git a/tools/debug-ui/src/LandingPage.scss b/tools/debug-ui/src/LandingPage.scss new file mode 100644 index 000000000..d1d2a0824 --- /dev/null +++ b/tools/debug-ui/src/LandingPage.scss @@ -0,0 +1,33 @@ +.landing-page { + padding: 10px; + + input { + font-family: 'Courier New', Courier, monospace; + } + + button { + margin-left: 10px; + } + + form { + margin-top: 10px; + display: flex; + } + + h3 { + font-size: 1.5em; + font-weight: bold; + } + + .spacer { + height: 100px; + } + + ul { + list-style-type: initial; + + li { + margin-left: 20px; + } + } +} diff --git a/tools/debug-ui/src/LandingPage.tsx b/tools/debug-ui/src/LandingPage.tsx new file mode 100644 index 000000000..0396ece57 --- /dev/null +++ b/tools/debug-ui/src/LandingPage.tsx @@ -0,0 +1,35 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router'; +import './LandingPage.scss'; +import { Link } from 'react-router-dom'; + +export const LandingPage = () => { + const [addr, setAddr] = useState(''); + const navigate = useNavigate(); + return ( +
+

Debug UI for framework

+

Enter address of target node to debug:

+
+ setAddr(e.target.value)} + size={40} + placeholder="ex. 1.2.3.4:3030 (port optional)" + /> + +
+ +
+

Additional links

+
    +
  • + /logviz: Log visualizer for framework TestLoop tests +
  • +
+
+ ); +}; diff --git a/tools/debug-ui/src/LatestBlocksView.scss b/tools/debug-ui/src/LatestBlocksView.scss new file mode 100644 index 000000000..0681355b0 --- /dev/null +++ b/tools/debug-ui/src/LatestBlocksView.scss @@ -0,0 +1,126 @@ +.latest-blocks-view { + padding: 10px; + + .height-controller { + margin-bottom: 10px; + + .prompt { + font-weight: bold; + margin-right: 20px; + } + } + + .explanation { + color: gray; + } + + .missed-blocks, + .missed-chunks { + margin-top: 10px; + margin-bottom: 10px; + } + + button { + margin-right: 8px; + } + + table { + width: 100%; + border-collapse: collapse; + margin-top: 10px; + } + + table, + th, + td { + border: 1px solid black; + } + + td { + text-align: left; + padding: 8px; + vertical-align: middle; + } + + .graph-node-cell { + border: none; + text-align: center; + } + + th { + text-align: center; + vertical-align: middle; + padding: 8px; + background-color: lightgrey; + } + + .skipped-chunk { + background-color: lightgray; + } + + .hash-element { + font-family: monospace; + cursor: pointer; + } + + .validator-unavailable { + color: red; + } + + .error { + color: red; + white-space: pre; + } + + .not-on-canonical-chain { + color: gray; + } + + .missed-height { + color: gray; + } + + .hidden { + display: none; + } + + .graph-dot { + width: 12px; + height: 12px; + background-color: black; + border-radius: 100%; + display: inline-block; + } + + .graph-dot-col-1 { + margin-left: 24px; + } + + .graph-dot-col-0.graph-dot-total-2 { + margin-right: 24px; + } + + .not-on-canonical-chain .graph-dot { + background-color: gray; + } + + .head-label, + .header-head-label { + font-size: 10px; + font-family: Arial, Helvetica, sans-serif; + font-weight: bold; + color: white; + border-radius: 4px; + text-align: center; + padding: 4px; + margin-top: 2px; + } + + .head-label { + background-color: rgb(76, 208, 0); + } + + .header-head-label { + background-color: rgb(235, 215, 0); + } +} diff --git a/tools/debug-ui/src/LatestBlocksView.tsx b/tools/debug-ui/src/LatestBlocksView.tsx new file mode 100644 index 000000000..aeea9bfe5 --- /dev/null +++ b/tools/debug-ui/src/LatestBlocksView.tsx @@ -0,0 +1,428 @@ +import { Fragment, ReactElement, useCallback, useMemo, useState } from 'react'; +import { useQuery } from 'react-query'; +import Xarrow, { Xwrapper, useXarrow } from 'react-xarrows'; +import { DebugBlockStatus, MissedHeightInfo, fetchBlockStatus, fetchFullStatus } from './api'; +import './LatestBlocksView.scss'; + +function ellipsify(str: string, maxLen: number): string { + if (str.length > maxLen) { + return str.substring(0, maxLen - 3) + '...'; + } + return str; +} + +type HashElementProps = { + hashValue: string; + creator: string; + expandAll: boolean; + knownProducers: Set; +}; + +// Makes an element that when clicked, expands or ellipsifies the hash and creator. +const HashElement = ({ hashValue, creator, expandAll, knownProducers }: HashElementProps) => { + const [expanded, setExpanded] = useState(false); + const updateXarrow = useXarrow(); + return ( + { + setExpanded((value) => !value); + // xarrows need to be updated whenever graph dot positions may change. + updateXarrow(); + }}> + {expanded || expandAll + ? `${hashValue} ${creator}` + : `${ellipsify(hashValue, 8)} ${ellipsify(creator, 13)}`} + + ); +}; + +type BlockTableRowBlock = { + block: DebugBlockStatus; + parentIndex: number | null; // the index of the parent block, or null if parent not included in the data + graphColumn: number | null; // the column to display the graph node in + blockDelay: number | null; // number of seconds since parent's block timestamp, or null if parent not included in the data + chunkSkipped: boolean[]; // for each chunk, whether the chunk is the same as that chunk of parent block + isHead: boolean; + isHeaderHead: boolean; +}; +type BlockTableRow = BlockTableRowBlock | { missedHeight: MissedHeightInfo }; + +// Sorts the API response into easily displayable rows, and computes the graph layout. +function sortBlocksAndDetermineBlockGraphLayout( + blocks: DebugBlockStatus[], + missedHeights: MissedHeightInfo[], + head: string, + headerHead: string +): BlockTableRow[] { + const rows: BlockTableRow[] = []; + for (const block of blocks) { + rows.push({ + block, + parentIndex: null, + graphColumn: -1, + blockDelay: null, + chunkSkipped: block.chunks.map(() => false), + isHead: head === block.block_hash, + isHeaderHead: headerHead === block.block_hash, + }); + } + for (const missedHeight of missedHeights) { + rows.push({ missedHeight }); + } + + function sortingKey(row: BlockTableRow) { + if ('block' in row) { + // some lousy tie-breaking for same-height rows. + return row.block.block_height + ((row.block.block_timestamp / 1e12) % 1); + } else { + return row.missedHeight.block_height; + } + } + + rows.sort((a, b) => sortingKey(b) - sortingKey(a)); + + const rowIndexByHash = new Map(); + rows.forEach((row, rowIndex) => { + if ('block' in row) { + rowIndexByHash.set(row.block.block_hash, rowIndex); + } + }); + + let highestNodeOnFirstColumn = rows.length; + for (let i = rows.length - 1; i >= 0; i--) { + const row = rows[i]; + if ('missedHeight' in row) { + continue; + } + const block = row.block; + + // Look up parent index, and also compute things that depend on the parent block. + if (rowIndexByHash.has(block.prev_block_hash)) { + row.parentIndex = rowIndexByHash.get(block.prev_block_hash)!; + const parentBlock = (rows[row.parentIndex] as BlockTableRowBlock).block; + row.blockDelay = (block.block_timestamp - parentBlock.block_timestamp) / 1e9; + for (let j = 0; j < Math.min(block.chunks.length, parentBlock.chunks.length); j++) { + row.chunkSkipped[j] = + block.chunks[j].chunk_hash === parentBlock.chunks[j].chunk_hash; + } + } + // We'll use a two-column layout for the block graph. We traverse from bottom + // up (oldest block to latest), and for each row we pick the first column unless + // that would make us draw a line (from the parent to this node) through another + // node; in which case we would pick the second column. To do that we just need + // to keep track of the highest node we've seen so far for the first column. + // + // Not the best layout for a graph, but it's sufficient since we rarely have forks. + let column = 0; + if ( + row.parentIndex !== null && + (rows[row.parentIndex] as BlockTableRowBlock).graphColumn === 0 && + row.parentIndex > highestNodeOnFirstColumn + ) { + column = 1; + } else { + highestNodeOnFirstColumn = i; + } + row.graphColumn = column; + } + return rows; +} + +type BlocksTableProps = { + rows: BlockTableRow[]; + knownProducers: Set; + expandAll: boolean; + hideMissingHeights: boolean; +}; + +const BlocksTable = ({ rows, knownProducers, expandAll, hideMissingHeights }: BlocksTableProps) => { + let numGraphColumns = 1; // either 1 or 2; determines the width of leftmost td + let numShards = 0; + for (const row of rows) { + if ('block' in row) { + numGraphColumns = Math.max(numGraphColumns, (row.graphColumn || 0) + 1); + for (const chunk of row.block.chunks) { + numShards = Math.max(numShards, chunk.shard_id + 1); + } + } + } + const header = ( + + Chain + Height + {'Hash & creator'} + Processing Time (ms) + Block Delay (s) + Gas price ratio + {[...Array(numShards).keys()].map((i) => ( + + Shard {i} (hash/gas(Tgas)/time(ms)) + + ))} + + ); + + // One xarrow element per arrow (from block to block). + const graphArrows = [] as ReactElement[]; + + // One 'tr' element per row. + const tableRows = [] as ReactElement[]; + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + if ('missedHeight' in row) { + if (!hideMissingHeights) { + tableRows.push( + + + {row.missedHeight.block_height} + + {row.missedHeight.block_producer} missed block + + + ); + } + continue; + } + const block = row.block; + + const chunkCells = [] as ReactElement[]; + block.chunks.forEach((chunk, shardId) => { + chunkCells.push( + + + + + {(chunk.gas_used / (1024 * 1024 * 1024 * 1024)).toFixed(1)} + {chunk.processing_time_ms} + + ); + }); + + tableRows.push( + + +
+ + + {block.block_height} + {row.isHead &&
HEAD
} + {row.isHeaderHead &&
HEADER HEAD
} + + + + + {block.processing_time_ms} + {row.blockDelay ?? ''} + {block.gas_price_ratio} + {block.full_block_missing && header only} + {chunkCells} + + ); + if (row.parentIndex != null) { + graphArrows.push( + + ); + } + } + return ( +
+ {graphArrows} + + + {header} + {tableRows} + +
+
+ ); +}; + +type LatestBlockViewProps = { + addr: string; +}; + +export const LatestBlocksView = ({ addr }: LatestBlockViewProps) => { + const [height, setHeight] = useState(null); + const [heightInInput, setHeightInInput] = useState(''); + const [expandAll, setExpandAll] = useState(false); + const [hideMissingHeights, setHideMissingHeights] = useState(false); + const [showMissingChunksStats, setShowMissingChunksStats] = useState(false); + const updateXarrow = useXarrow(); + + const { data: status } = useQuery( + ['fullStatus', addr], + async () => await fetchFullStatus(addr) + ); + const { + data: blockData, + error, + isLoading, + } = useQuery(['latestBlocks', addr, height], async () => await fetchBlockStatus(addr, height)); + + const { rows, knownProducerSet } = useMemo(() => { + if (status && blockData) { + const knownProducerSet = new Set(); + for (const producer of status.detailed_debug_status!.network_info.known_producers) { + knownProducerSet.add(producer.account_id); + } + + const data = blockData.status_response.BlockStatus; + const rows = sortBlocksAndDetermineBlockGraphLayout( + data.blocks, + data.missed_heights, + data.head, + data.header_head + ); + return { rows, knownProducerSet }; + } + return { rows: [], knownProducerSet: new Set() }; + }, [status, blockData]); + + // Compute missing blocks and chunks statistics (whenever rows changes). + const { numCanonicalBlocks, canonicalHeightCount, numChunksSkipped } = useMemo(() => { + let firstCanonicalHeight = 0; + let lastCanonicalHeight = 0; + let numCanonicalBlocks = 0; + const numChunksSkipped = []; + for (const row of rows) { + if (!('block' in row)) { + continue; + } + const block = row.block; + if (!block.is_on_canonical_chain) { + continue; + } + if (firstCanonicalHeight === 0) { + firstCanonicalHeight = block.block_height; + } + lastCanonicalHeight = block.block_height; + numCanonicalBlocks++; + for (let i = 0; i < row.chunkSkipped.length; i++) { + while (numChunksSkipped.length < i + 1) { + numChunksSkipped.push(0); + } + if (row.chunkSkipped[i]) { + numChunksSkipped[i]++; + } + } + } + return { + numCanonicalBlocks, + canonicalHeightCount: firstCanonicalHeight - lastCanonicalHeight + 1, + numChunksSkipped, + }; + }, [rows]); + + const goToHeightCallback = useCallback(() => { + const height = parseInt(heightInInput); + setHeight(height); + }, [heightInInput]); + + return ( + +
+
+ + {height == null + ? 'Displaying most recent blocks' + : `Displaying blocks from height ${height}`} + + setHeightInInput(e.target.value)} + /> + + +
+
Skipped chunks have grey background.
+
+ Red text means that we don't know this producer (it's not present in + our announce account list). +
+ {!!error &&
{(error as Error).stack}
} +
+ Missing blocks: {canonicalHeightCount - numCanonicalBlocks} {} + Produced: {numCanonicalBlocks} {} + Missing Rate:{' '} + {( + ((canonicalHeightCount - numCanonicalBlocks) / canonicalHeightCount) * + 100 + ).toFixed(2)} + % +
+ + + + {showMissingChunksStats && ( +
+ {numChunksSkipped.map((numSkipped, shardId) => ( +
+ Shard {shardId}: Missing chunks: {numSkipped} {} + Produced: {numCanonicalBlocks - numSkipped} {} + Missing Rate: {((numSkipped / numCanonicalBlocks) * 100).toFixed(2)} + % +
+ ))} +
+ )} + {isLoading ? ( +
Loading...
+ ) : ( + + )} +
+
+ ); +}; diff --git a/tools/debug-ui/src/LogFileDrop.tsx b/tools/debug-ui/src/LogFileDrop.tsx new file mode 100644 index 000000000..465eb7400 --- /dev/null +++ b/tools/debug-ui/src/LogFileDrop.tsx @@ -0,0 +1,71 @@ +import React, { ChangeEvent, useState } from 'react'; + +interface LogFileDropProps { + onFileDrop: (lines: string[]) => void; +} + +// Renders a box to drop a file into, and an alternative button to upload. +// onFileDrop is called when a file is dropped or uploaded, with the +// contents of the file as an array of lines. +export const LogFileDrop = ({ onFileDrop }: LogFileDropProps) => { + const [drag, setDrag] = useState(false); + + const handleDrag = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + }; + + const handleDragIn = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setDrag(true); + }; + + const handleDragOut = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setDrag(false); + }; + + const handleDrop = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setDrag(false); + if (e.dataTransfer?.files.length > 0) { + handleFile(e.dataTransfer.files[0]); + } + }; + + const handleFile = (file: File) => { + const reader = new FileReader(); + reader.onload = (e) => { + const lines = (e.target!.result as string).split('\n'); + onFileDrop(lines); + }; + reader.readAsText(file); + }; + + const handleClick = (event: ChangeEvent) => { + const input: HTMLInputElement | null = event.currentTarget; + if (input && input.files) { + handleFile(input.files[0]); + } + }; + return ( +
+
+ Drag and drop a file here +
+ + +
+ ); +}; diff --git a/tools/debug-ui/src/NetworkInfoView.scss b/tools/debug-ui/src/NetworkInfoView.scss new file mode 100644 index 000000000..26727fc12 --- /dev/null +++ b/tools/debug-ui/src/NetworkInfoView.scss @@ -0,0 +1,29 @@ +.network-info-view { + .error { + color: red; + } + + table { + width: 100%; + border-collapse: collapse; + } + + table, + th, + td { + border: 1px solid black; + } + + td { + text-align: left; + vertical-align: top; + padding: 8px; + } + + th { + text-align: center; + vertical-align: center; + padding: 8px; + background-color: lightgrey; + } +} diff --git a/tools/debug-ui/src/NetworkInfoView.tsx b/tools/debug-ui/src/NetworkInfoView.tsx new file mode 100644 index 000000000..c95134e4c --- /dev/null +++ b/tools/debug-ui/src/NetworkInfoView.tsx @@ -0,0 +1,52 @@ +import './NetworkInfoView.scss'; +import { NavLink, Navigate, Route, Routes } from 'react-router-dom'; +import { CurrentPeersView } from './CurrentPeersView'; +import { PeerStorageView } from './PeerStorageView'; +import { ConnectionStorageView } from './ConnectionStorageView'; +import { Tier1View } from './Tier1View'; +import { RoutingTableView } from './RoutingTableView'; +import { SnapshotHostsView } from './SnapshotHostsView'; + +type NetworkInfoViewProps = { + addr: string; +}; + +export const NetworkInfoView = ({ addr }: NetworkInfoViewProps) => { + return ( +
+
+ + Current Peers + + + Detailed Peer Storage + + + Connection Storage + + + TIER1 + + + Routing Table + + + Snapshot Hosts + +
+ + } /> + } /> + } /> + } /> + } /> + } /> + } /> + +
+ ); +}; + +function navLinkClassName({ isActive }: { isActive: boolean }) { + return isActive ? 'nav-link active' : 'nav-link'; +} diff --git a/tools/debug-ui/src/PeerStorageView.scss b/tools/debug-ui/src/PeerStorageView.scss new file mode 100644 index 000000000..c0b78af57 --- /dev/null +++ b/tools/debug-ui/src/PeerStorageView.scss @@ -0,0 +1,3 @@ +.peer-storage-table { + margin: 10px; +} diff --git a/tools/debug-ui/src/PeerStorageView.tsx b/tools/debug-ui/src/PeerStorageView.tsx new file mode 100644 index 000000000..00bfd44ab --- /dev/null +++ b/tools/debug-ui/src/PeerStorageView.tsx @@ -0,0 +1,65 @@ +import { useQuery } from 'react-query'; +import { toHumanTime } from './utils'; +import { fetchPeerStore } from './api'; +import './PeerStorageView.scss'; + +type PeerStorageViewProps = { + addr: string; +}; + +export const PeerStorageView = ({ addr }: PeerStorageViewProps) => { + const { + data: peerStore, + error, + isLoading, + } = useQuery(['peerStore', addr], () => fetchPeerStore(addr)); + + if (isLoading) { + return
Loading...
; + } else if (error) { + return
{(error as Error).stack}
; + } + return ( + + + + + + + + + + + {peerStore!.status_response.PeerStore.peer_states.map((peer) => { + return ( + + + + + + {peer.last_attempt ? ( + <> + + + + ) : ( + <> + + + + )} + + ); + })} + +
Peer IDPeer addressFirst seenLast seenLast connection attemptStatus
{peer.peer_id}{peer.addr}{toHumanTime(Math.floor(Date.now() / 1000) - peer.first_seen)}{toHumanTime(Math.floor(Date.now() / 1000) - peer.last_seen)} + {toHumanTime( + Math.floor(Date.now() / 1000) - peer.last_attempt[0] + )} + + {peer.status} +
+ Last attempt: {peer.last_attempt[1]} +
{peer.status}
+ ); +}; diff --git a/tools/debug-ui/src/RecentEpochsView.scss b/tools/debug-ui/src/RecentEpochsView.scss new file mode 100644 index 000000000..47ee7a50b --- /dev/null +++ b/tools/debug-ui/src/RecentEpochsView.scss @@ -0,0 +1,27 @@ +.recent-epochs-table { + th:first-child { + border: none; + background-color: transparent; + } + + td:first-child { + border: none; + background-color: none; + text-align: right; + white-space: nowrap; + } + + tr.current-epoch { + td:not(:first-child) { + background-color: #c6f8bf; + } + + td:first-child { + color: #3bda26; + } + } + + tr.next-epoch { + color: gray; + } +} diff --git a/tools/debug-ui/src/RecentEpochsView.tsx b/tools/debug-ui/src/RecentEpochsView.tsx new file mode 100644 index 000000000..b70fa21ee --- /dev/null +++ b/tools/debug-ui/src/RecentEpochsView.tsx @@ -0,0 +1,93 @@ +import { useQuery } from 'react-query'; +import { fetchEpochInfo, fetchFullStatus } from './api'; +import { formatDurationInMillis } from './utils'; +import './RecentEpochsView.scss'; + +type RecentEpochsViewProps = { + addr: string; +}; + +export const RecentEpochsView = ({ addr }: RecentEpochsViewProps) => { + const { + data: epochData, + error: epochError, + isLoading: epochIsLoading, + } = useQuery(['epochInfo', addr], () => fetchEpochInfo(addr)); + const { + data: statusData, + error: statusError, + isLoading: statusIsLoading, + } = useQuery(['fullStatus', addr], () => fetchFullStatus(addr)); + + if (epochIsLoading || statusIsLoading) { + return
Loading...
; + } + if (epochError || statusError) { + return
{((epochError || statusError) as Error).stack}
; + } + const epochInfos = epochData!.status_response.EpochInfo; + const status = statusData!; + + return ( + + + + + + + + + + + + + + + {epochInfos.map((epochInfo, index) => { + let firstBlockColumn = ''; + let epochStartColumn = ''; + if (epochInfo.first_block === null) { + if (index == 0) { + const blocksRemaining = + epochInfo.height - status.sync_info.latest_block_height; + const millisecondsRemaining = + blocksRemaining * + status.detailed_debug_status!.block_production_delay_millis; + firstBlockColumn = `Next epoch - in ${blocksRemaining} blocks`; + epochStartColumn = `in ${formatDurationInMillis( + millisecondsRemaining + )}`; + } + } else { + firstBlockColumn = epochInfo.first_block[0]; + epochStartColumn = `${formatDurationInMillis( + Date.now() - Date.parse(epochInfo.first_block[1]) + )} ago`; + } + let rowClassName = ''; + let firstColumnText = ''; + if (index == 0) { + rowClassName = 'next-epoch'; + firstColumnText = 'Next ⮕'; + } else if (index == 1) { + rowClassName = 'current-epoch'; + firstColumnText = 'Current ⮕'; + } + + return ( + + + + + + + + + + + ); + })} + +
Epoch IDStart HeightProtocol VersionFirst BlockEpoch StartBlock ProducersChunk-only Producers
{firstColumnText}{epochInfo.epoch_id}{epochInfo.height}{epochInfo.protocol_version}{firstBlockColumn}{epochStartColumn}{epochInfo.block_producers.length}{epochInfo.chunk_only_producers.length}
+ ); +}; diff --git a/tools/debug-ui/src/RoutingTableView.scss b/tools/debug-ui/src/RoutingTableView.scss new file mode 100644 index 000000000..09ded78ed --- /dev/null +++ b/tools/debug-ui/src/RoutingTableView.scss @@ -0,0 +1,3 @@ +.routing-table-view { + margin: 10px; +} diff --git a/tools/debug-ui/src/RoutingTableView.tsx b/tools/debug-ui/src/RoutingTableView.tsx new file mode 100644 index 000000000..1554f3913 --- /dev/null +++ b/tools/debug-ui/src/RoutingTableView.tsx @@ -0,0 +1,117 @@ +import { useQuery } from 'react-query'; +import { toHumanTime, formatDurationInMillis } from './utils'; +import { fetchRoutingTable } from './api'; +import './RoutingTableView.scss'; + +type RoutingTableViewProps = { + addr: string; +}; + +export const RoutingTableView = ({ addr }: RoutingTableViewProps) => { + const { + data: routingTable, + error, + isLoading, + } = useQuery(['routingTable', addr], () => fetchRoutingTable(addr)); + + if (isLoading) { + return
Loading...
; + } else if (error) { + return
{(error as Error).stack}
; + } + + const routingInfo = routingTable!.status_response.Routes; + + const peerLabels = routingInfo.edge_cache.peer_labels; + + const routable_peers = Object.keys(routingInfo.my_distances); + routable_peers.sort((a, b) => peerLabels[a] > peerLabels[b] ? 1 : -1); + + const direct_peers: string[] = []; + const disconnected_peers: string[] = []; + + Object.entries(routingInfo.local_edges).map(([peer_id, edge]) => { + if (edge.nonce % 2 == 1) { + direct_peers.push(peer_id); + } else { + disconnected_peers.push(peer_id); + } + }); + + direct_peers.sort((a, b) => peerLabels[a] > peerLabels[b] ? 1 : -1); + disconnected_peers.sort((a, b) => peerLabels[a] > peerLabels[b] ? 1 : -1); + + return ( +
+

Routable Peers

+ + + + + + + + {routable_peers.map((peer_id) => { + const peer_label = peerLabels[peer_id]; + return ( + + + + + + ); + })} + +
Peer IDPeer LabelShortest Path Length (Hops)
{peer_id.substring(8, 14)}...{peer_label}{routingInfo.my_distances[peer_id]}
+
+

Direct Peers

+ + + + + + + + + {direct_peers.map((peer_id) => { + const peer_label = peerLabels[peer_id]; + + const peer_distances = routingInfo.peer_distances[peer_id]; + + return ( + + + + + + + ); + })} + +
Peer IDPeer LabelAdvertised DistancesMin Nonce
{peer_id.substring(8, 14)}...{peer_label}{peer_distances.distance.map((x) => x ?? '_').join(', ')}{peer_distances.min_nonce} ({formatDurationInMillis(Date.now() - peer_distances.min_nonce * 1000)})
+
+

Disconnected Peers

+ + + + + + + + {disconnected_peers.map((peer_id) => { + const peer_label = peerLabels[peer_id]; + const nonce = routingInfo.local_edges[peer_id].nonce; + + return ( + + + + + + ); + })} + +
Peer IDPeer LabelNonce
{peer_id.substring(8, 14)}...{peer_label}{nonce} ({formatDurationInMillis(Date.now() - nonce * 1000)})
+
+ ); +}; diff --git a/tools/debug-ui/src/SnapshotHostsView.scss b/tools/debug-ui/src/SnapshotHostsView.scss new file mode 100644 index 000000000..9448bd389 --- /dev/null +++ b/tools/debug-ui/src/SnapshotHostsView.scss @@ -0,0 +1,3 @@ +.snapshot-hosts-view { + margin: 10px; +} diff --git a/tools/debug-ui/src/SnapshotHostsView.tsx b/tools/debug-ui/src/SnapshotHostsView.tsx new file mode 100644 index 000000000..c647c75bd --- /dev/null +++ b/tools/debug-ui/src/SnapshotHostsView.tsx @@ -0,0 +1,51 @@ +import { useQuery } from 'react-query'; +import { toHumanTime } from './utils'; +import { fetchSnapshotHosts } from './api'; +import './SnapshotHostsView.scss'; + +type SnapshotHostsViewProps = { + addr: string; +}; + +export const SnapshotHostsView = ({ addr }: SnapshotHostsViewProps) => { + const { + data: snapshotHosts, + error, + isLoading, + } = useQuery(['snapshotHosts', addr], () => fetchSnapshotHosts(addr)); + + if (isLoading) { + return
Loading...
; + } else if (error) { + return
{(error as Error).stack}
; + } + + const snapshot_hosts = snapshotHosts!.status_response.SnapshotHosts; + + return ( +
+ + + + + + + + + {snapshot_hosts.hosts.map( + (host) => { + return ( + + + + + + + ); + } + )} + +
Peer IDShardsEpoch HeightSync Hash
{host.peer_id}{JSON.stringify(host.shards)}{host.epoch_height}{host.sync_hash}
+
+ ); +}; diff --git a/tools/debug-ui/src/Tier1View.scss b/tools/debug-ui/src/Tier1View.scss new file mode 100644 index 000000000..b7959cdf1 --- /dev/null +++ b/tools/debug-ui/src/Tier1View.scss @@ -0,0 +1,3 @@ +.tier1-peers-view { + padding: 10px; +} diff --git a/tools/debug-ui/src/Tier1View.tsx b/tools/debug-ui/src/Tier1View.tsx new file mode 100644 index 000000000..96b8a70ed --- /dev/null +++ b/tools/debug-ui/src/Tier1View.tsx @@ -0,0 +1,206 @@ +import { MouseEvent, useCallback, useState } from 'react'; +import { useQuery } from 'react-query'; +import { PeerAddr, fetchFullStatus } from './api'; +import { addDebugPortLink, formatDurationInMillis, formatTraffic } from './utils'; +import './Tier1View.scss'; + +type Tier1ViewProps = { + addr: string; +}; + +export const Tier1View = ({ addr }: Tier1ViewProps) => { + const { + data: fullStatus, + error: fullStatusError, + isLoading: fullStatusLoading, + } = useQuery(['fullStatus', addr], () => fetchFullStatus(addr)); + + if (fullStatusLoading) { + return
Loading...
; + } + if (fullStatusError) { + return ( +
+
{(fullStatusError as Error).stack}
+
+ ); + } + if (!fullStatus) { + return ( +
+
No Data
+
+ ); + } + + const networkInfo = fullStatus.detailed_debug_status!.network_info; + + const rendered = new Set(); + const rows = []; + + // tier1_connections contains TIER1 nodes we are currently connected to + for (const peer of networkInfo.tier1_connections!) { + let accountKey = ''; + let proxies: PeerAddr[] = []; + + networkInfo.tier1_accounts_data!.forEach((account) => { + if (account.peer_id == peer.peer_id) { + accountKey = account.account_key; + proxies = account.proxies; + } + }); + + rendered.add(accountKey); + + let accountId = ''; + networkInfo.known_producers.forEach((producer) => { + if (producer.peer_id == peer.peer_id) { + accountId = producer.account_id; + } + }); + + let lastPing = formatDurationInMillis(peer.last_time_received_message_millis); + let lastPingClass = ''; + if (fullStatus.node_public_key == accountKey) { + lastPing = 'n/a'; + } else if (peer.last_time_received_message_millis > 60 * 1000) { + lastPingClass = 'peer_far_behind'; + } + + rows.push( + + {addDebugPortLink(peer.addr)} + {accountKey.substring(8, 14)}... + {accountId} + + + + {peer.peer_id.substring(8, 14)}... + {lastPing} + {JSON.stringify(peer.tracked_shards)} + {JSON.stringify(peer.archival)} + {peer.is_outbound_peer ? 'OUT' : 'IN'} + {formatDurationInMillis(peer.connection_established_time_millis)} + {formatTraffic(peer.received_bytes_per_sec, peer.sent_bytes_per_sec)} + + ); + } + + // tier1_accounts_data contains data about TIER1 nodes we would like to connect to + for (const account of networkInfo.tier1_accounts_data!) { + const accountKey = account.account_key; + + if (rendered.has(accountKey)) { + continue; + } + rendered.add(accountKey); + + let accountId = ''; + networkInfo.known_producers.forEach((producer) => { + if (producer.peer_id == account.peer_id) { + accountId = producer.account_id; + } + }); + + rows.push( + + + {accountKey.substring(8, 14)}... + {accountId} + + + + {account.peer_id.substring(8, 14)}... + + + + + + + + ); + } + + // tier1_accounts_keys contains accounts whose data we would like to collect + for (const accountKey of networkInfo.tier1_accounts_keys!) { + if (rendered.has(accountKey)) { + continue; + } + rendered.add(accountKey); + + rows.push( + + + {accountKey.substring(8, 14)}... + + + + + + + + + + + ); + } + + return ( +
+ + + + + + + + + + + + + + + + + {rows} +
AddressAccountKeyAccount IDProxiesPeerIdLast pingTracked ShardsArchivalConnection typeFirst connectionTraffic (last minute)
+
+ ); +}; + +const CollapsibleProxyList = ({ proxies }: { proxies: PeerAddr[] }) => { + const [showAll, setShowAll] = useState(false); + const callback = useCallback((e: MouseEvent) => { + e.preventDefault(); + setShowAll((show) => !show); + }, []); + const MAX_TO_SHOW = 2; + + const formatted = proxies.map((proxy) => { + return proxy.peer_id.substring(8, 14) + '...@' + proxy.addr; + }); + + if (formatted.length <= MAX_TO_SHOW) { + return <>{formatted.join(', ')}; + } + if (showAll) { + return ( + <> + {formatted.join(', ')}{' '} + + Show fewer + + + ); + } + return ( + <> + {formatted.slice(0, MAX_TO_SHOW).join(', ')}, ... +
+ + Show all {formatted.length} + + + ); +}; diff --git a/tools/debug-ui/src/api.tsx b/tools/debug-ui/src/api.tsx new file mode 100644 index 000000000..3c8c2404e --- /dev/null +++ b/tools/debug-ui/src/api.tsx @@ -0,0 +1,494 @@ +import { EntityQuery } from './entity_debug/types'; + +export interface StatusResponse { + chain_id: string; + latest_protocol_version: number; + node_key: string; + node_public_key: string; + protocol_version: number; + rpc_addr?: string; + sync_info: StatusSyncInfo; + uptime_sec: number; + validator_account_id: string | null; + validator_public_key: string | null; + validators: ValidatorInfo[]; + version: BuildInfo; + detailed_debug_status: DetailedDebugStatus | null; +} + +export interface StatusSyncInfo { + earliest_block_hash: string; + earliest_block_height: number; + earliest_block_time: string; + epoch_id: string; + epoch_start_height: number; + latest_block_hash: string; + latest_block_height: number; + latest_block_time: string; + latest_state_root: string; + syncing: boolean; +} + +export interface ValidatorInfo { + account_id: string; + is_slashed: boolean; +} + +export interface BuildInfo { + build: string; + rustc_version: string; + version: string; +} + +export interface DetailedDebugStatus { + network_info: NetworkInfoView; + sync_status: string; + catchup_status: CatchupStatusView[]; + current_head_status: BlockStatusView; + current_header_head_status: BlockStatusView; + block_production_delay_millis: number; +} + +export interface NetworkInfoView { + peer_max_count: number; + num_connected_peers: number; + connected_peers: PeerInfoView[]; + known_producers: KnownProducerView[]; + tier1_accounts_keys?: string[]; + tier1_accounts_data?: AccountData[]; + tier1_connections?: PeerInfoView[]; +} + +export interface CatchupStatusView { + sync_block_hash: string; + sync_block_height: number; + shard_sync_status: { [shard_id: number]: string }; + blocks_to_catchup: BlockStatusView[]; +} + +export interface BlockStatusView { + height: number; + hash: string; +} + +export interface PeerInfoView { + addr: string; + account_id: string | null; + height: number | null; + block_hash: string | null; + is_highest_block_invalid: boolean; + tracked_shards: number[]; + archival: boolean; + peer_id: string; + received_bytes_per_sec: number; + sent_bytes_per_sec: number; + last_time_peer_requested_millis: number; + last_time_received_message_millis: number; + connection_established_time_millis: number; + is_outbound_peer: boolean; + nonce: number; +} + +export interface KnownProducerView { + account_id: string; + peer_id: string; + next_hops: string[] | null; +} + +export interface PeerAddr { + addr: string; + peer_id: string; +} + +export interface AccountData { + peer_id: string; + proxies: PeerAddr[]; + account_key: string; + version: number; + timestamp: string; +} + +export type SyncStatusView = + | 'AwaitingPeers' + | 'NoSync' + | { + EpochSync: { epoch_ord: number }; + } + | { + HeaderSync: { + start_height: number; + current_height: number; + highest_height: number; + }; + } + | { + StateSync: [string, { [shard_id: number]: ShardSyncDownloadView }]; + } + | 'StateSyncDone' + | { + BodySync: { + start_height: number; + current_height: number; + highest_height: number; + }; + }; + +export interface ShardSyncDownloadView { + downloads: { error: boolean; done: boolean }[]; + status: string; +} + +export interface DebugBlockStatusData { + blocks: DebugBlockStatus[]; + missed_heights: MissedHeightInfo[]; + head: string; + header_head: string; +} + +export interface DebugBlockStatus { + block_hash: string; + prev_block_hash: string; + block_height: number; + block_timestamp: number; + block_producer: string | null; + full_block_missing: boolean; + is_on_canonical_chain: boolean; + chunks: DebugChunkStatus[]; + processing_time_ms?: number; + gas_price_ratio: number; +} + +export interface MissedHeightInfo { + block_height: number; + block_producer: string | null; +} + +export interface DebugChunkStatus { + shard_id: number; + chunk_hash: string; + chunk_producer: string | null; + gas_used: number; + processing_time_ms?: number; +} + +export interface EpochInfoView { + epoch_id: string; + height: number; + first_block: null | [string, string]; + block_producers: ValidatorInfo[]; + chunk_only_producers: string[]; + validator_info: EpochValidatorInfo; + protocol_version: number; + shards_size_and_parts: [number, number, boolean][]; +} + +export interface EpochValidatorInfo { + current_validators: CurrentEpochValidatorInfo[]; + next_validators: NextEpochValidatorInfo[]; + current_fishermen: ValidatorStakeView[]; + next_fishermen: ValidatorStakeView[]; + current_proposals: ValidatorStakeView[]; + prev_epoch_kickout: ValidatorKickoutView[]; + epoch_start_height: number; + epoch_height: number; +} + +export interface CurrentEpochValidatorInfo { + account_id: string; + public_key: string; + is_slashed: boolean; + stake: string; + shards: number[]; + num_produced_blocks: number; + num_expected_blocks: number; + num_produced_chunks: number; + num_expected_chunks: number; +} + +export interface NextEpochValidatorInfo { + account_id: string; + public_key: string; + stake: string; + shards: number[]; +} + +export interface ValidatorStakeView { + account_id: string; + public_key: string; + stake: string; + validator_stake_struct_version: 'V1'; +} + +export interface ValidatorKickoutView { + account_id: string; + reason: ValidatorKickoutReason; +} + +export type ValidatorKickoutReason = + | 'Slashed' + | { NotEnoughBlocks: { produced: number; expected: number } } + | { NotEnoughChunks: { produced: number; expected: number } } + | 'Unstaked' + | { NotEnoughStake: { stake: string; threshold: string } } + | 'DidNotGetASeat'; + +export interface PeerStoreView { + peer_states: KnownPeerStateView[]; +} + +export interface KnownPeerStateView { + peer_id: string; + status: string; + addr: string; + first_seen: number; + last_seen: number; + last_attempt: [number, string] | null; +} + +export interface SyncStatusResponse { + status_response: { + SyncStatus: SyncStatusView; + }; +} + +export interface TrackedShardsResponse { + status_response: { + TrackedShards: { + shards_tracked_this_epoch: boolean[]; + shards_tracked_next_epoch: boolean[]; + }; + }; +} + +export interface BlockStatusResponse { + status_response: { + BlockStatus: DebugBlockStatusData; + }; +} + +export interface EpochInfoResponse { + status_response: { + EpochInfo: EpochInfoView[]; + }; +} + +export interface PeerStoreResponse { + status_response: { + PeerStore: PeerStoreView; + }; +} + +export interface ConnectionInfoView { + peer_id: string; + addr: string; + time_established: number; + time_connected_until: number; +} + +export interface RecentOutboundConnectionsView { + recent_outbound_connections: ConnectionInfoView[]; +} + +export interface RecentOutboundConnectionsResponse { + status_response: { + RecentOutboundConnections: RecentOutboundConnectionsView; + }; +} + +export interface EdgeView { + peer0: string; + peer1: string; + nonce: number; +}; + +export interface LabeledEdgeView { + peer0: number; + peer1: number; + nonce: number; +}; + +export interface EdgeCacheView { + peer_labels: { [peer_id: string]: number }; + spanning_trees: { [peer_label: number]: LabeledEdgeView[] }; +} + +export interface PeerRoutesView { + distance: number[]; + min_nonce: number; +} + +export interface RoutingTableView { + edge_cache: EdgeCacheView; + local_edges: { [peer_id: string]: EdgeView }; + peer_distances: { [peer_id: string]: PeerRoutesView }; + my_distances: { [peer_id: string]: number }; +} + +export interface RoutingTableResponse { + status_response: { + Routes: RoutingTableView; + }; +} + +export interface SnapshotHostInfoView { + peer_id: string, + sync_hash: string, + epoch_height: number, + shards: number[], +} + +export interface SnapshotHostsView { + hosts: SnapshotHostInfoView[], +} + +export interface SnapshotHostsResponse { + status_response: { + SnapshotHosts: SnapshotHostsView; + }; +} + +export type DroppedReason = 'HeightProcessed' | 'TooManyProcessingBlocks'; + +export type BlockProcessingStatus = + | 'Orphan' + | 'WaitingForChunks' + | 'InProcessing' + | 'Accepted' + | { Error: string } + | { Dropped: DroppedReason } + | 'Unknown'; + +export interface BlockProcessingInfo { + height: number; + hash: string; + received_timestamp: string; + in_progress_ms: number; + orphaned_ms: number | null; + missing_chunks_ms: number | null; + block_status: BlockProcessingStatus; + chunks_info: ChunkProcessingInfo[] | null; +} + +export interface PartCollectionInfo { + part_owner: string; + received_time: string | null; + forwarded_received_time: string | null; + chunk_received_time: string | null; +} + +export type ChunkProcessingStatus = 'NeedToRequest' | 'Requested' | 'Completed'; + +export interface ChunkProcessingInfo { + height_created: number; + shard_id: number; + chunk_hash: string; + prev_block_hash: string; + created_by: string | null; + status: ChunkProcessingStatus; + requested_timestamp: string | null; + completed_timestamp: string | null; + request_duration: number | null; + chunk_parts_collection: PartCollectionInfo[]; +} + +export interface ChainProcessingInfo { + num_blocks_in_processing: number; + num_orphans: number; + num_blocks_missing_chunks: number; + blocks_info: BlockProcessingInfo[]; + floating_chunks_info: ChunkProcessingInfo[]; +} + +export interface ChainProcessingStatusResponse { + status_response: { + ChainProcessingStatus: ChainProcessingInfo; + }; +} + +export async function fetchBasicStatus(addr: string): Promise { + const response = await fetch(`http://${addr}/status`); + return await response.json(); +} + +export async function fetchFullStatus(addr: string): Promise { + const response = await fetch(`http://${addr}/debug/api/status`); + return await response.json(); +} + +export async function fetchSyncStatus(addr: string): Promise { + const response = await fetch(`http://${addr}/debug/api/sync_status`); + return await response.json(); +} + +export async function fetchTrackedShards(addr: string): Promise { + const response = await fetch(`http://${addr}/debug/api/tracked_shards`); + return await response.json(); +} + +export async function fetchBlockStatus( + addr: string, + height: number | null +): Promise { + const trailing = height ? `/${height}` : ''; + const response = await fetch(`http://${addr}/debug/api/block_status${trailing}`); + return await response.json(); +} + +export async function fetchEpochInfo(addr: string): Promise { + const response = await fetch(`http://${addr}/debug/api/epoch_info`); + return await response.json(); +} + +export async function fetchPeerStore(addr: string): Promise { + const response = await fetch(`http://${addr}/debug/api/peer_store`); + return await response.json(); +} + +export async function fetchRecentOutboundConnections( + addr: string +): Promise { + const response = await fetch(`http://${addr}/debug/api/recent_outbound_connections`); + return await response.json(); +} + +export async function fetchRoutingTable( + addr: string +): Promise { + const response = await fetch(`http://${addr}/debug/api/network_routes`); + return await response.json(); +} + +export async function fetchSnapshotHosts( + addr: string +): Promise { + const response = await fetch(`http://${addr}/debug/api/snapshot_hosts`); + return await response.json(); +} + +export async function fetchChainProcessingStatus( + addr: string +): Promise { + const response = await fetch(`http://${addr}/debug/api/chain_processing_status`); + return await response.json(); +} + +export type ApiEntityDataEntryValue = string | ApiEntityData; +export type ApiEntityData = { entries: ApiEntityDataEntry[] }; +export type ApiEntityDataEntry = { name: string; value: ApiEntityDataEntryValue }; + +export async function fetchEntity( + addr: string, + request: EntityQuery +): Promise { + const response = await fetch(`http://${addr}/debug/api/entity`, { + body: JSON.stringify(request), + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + }); + if (response.status !== 200) { + throw await response.text(); + } + return response.json(); +} diff --git a/tools/debug-ui/src/entity_debug/AllQueriesDisplay.scss b/tools/debug-ui/src/entity_debug/AllQueriesDisplay.scss new file mode 100644 index 000000000..0f8c442d4 --- /dev/null +++ b/tools/debug-ui/src/entity_debug/AllQueriesDisplay.scss @@ -0,0 +1,87 @@ +.query-list { + display: flex; + flex-direction: column; + align-items: stretch; + min-height: 0; + flex: 1; + + .header { + font-size: 18px; + padding: 2px 6px; + border-bottom: 1px solid gray; + font-weight: bold; + font-variant: small-caps; + } + + .body { + flex: 1; + overflow: hidden; + overflow-y: auto; + } +} + +.one-query { + padding: 6px; + cursor: pointer; + display: flex; + align-items: center; + + &.one-query-selected { + background-color: #eee; + } + + .one-query-description { + flex: 1; + } + + .one-query-delete { + opacity: 0; + cursor: default; + background-color: gray; + color: white; + width: 12px; + height: 12px; + line-height: 13px; + text-align: center; + margin-left: 4px; + border-radius: 100%; + + &:hover { + background-color: rgb(176, 58, 58); + } + } + + .one-query-keys { + display: flex; + flex-direction: column; + padding-left: 16px; + + .one-query-key { + display: flex; + font-size: 0.8em; + + .one-query-key-name { + color: gray; + margin-right: 4px; + + &:after { + content: ':'; + color: gray; + } + } + + .one-query-key-value { + max-width: 300px; + word-wrap: break-word; + } + } + } + + &:hover { + background-color: #ddd; + + .one-query-delete { + opacity: 1; + } + } +} diff --git a/tools/debug-ui/src/entity_debug/AllQueriesDisplay.tsx b/tools/debug-ui/src/entity_debug/AllQueriesDisplay.tsx new file mode 100644 index 000000000..c77326541 --- /dev/null +++ b/tools/debug-ui/src/entity_debug/AllQueriesDisplay.tsx @@ -0,0 +1,59 @@ +import { useContext } from 'react'; +import { AllQueriesContext } from './all_queries'; +import { getQueryType } from './types'; +import './AllQueriesDisplay.scss'; + +export const AllQueriesDisplay = () => { + const { queries, selectedIndex, dispatch } = useContext(AllQueriesContext); + return ( +
+
Queries
+
+ {queries.map((query, i) => { + const queryType = getQueryType(query); + const queryKeys: Record | null | undefined = query[queryType]; + return ( +
dispatch({ type: 'select-query', index: i })}> +
+
{queryType}
+ {queryKeys && ( +
+ {Object.keys(queryKeys).map((key) => { + const value = queryKeys[key]; + return ( +
+
{key}
+
+ {'' + value} +
+
+ ); + })} +
+ )} +
+
{ + e.stopPropagation(); + dispatch({ type: 'remove-query', index: i }); + }}> + × +
+
+ ); + })} +
dispatch({ type: 'select-query', index: -1 })}> + + Add New Query +
+
+
+ ); +}; diff --git a/tools/debug-ui/src/entity_debug/EntityDataRootView.scss b/tools/debug-ui/src/entity_debug/EntityDataRootView.scss new file mode 100644 index 000000000..dfd865403 --- /dev/null +++ b/tools/debug-ui/src/entity_debug/EntityDataRootView.scss @@ -0,0 +1,44 @@ +.entity-root { + padding: 0 8px; + + .error { + color: red; + } + + .entity-root-header { + border-bottom: 1px solid gray; + margin-left: -8px; + margin-right: -8px; + padding-left: 8px; + padding-right: 8px; + background-color: #eee; + display: flex; + align-items: center; + + .entity-type { + font-weight: bold; + font-size: 0.8em; + cursor: default; + user-select: none; + } + + .entity-root-button { + display: none; + cursor: pointer; + width: 16px; + height: 16px; + line-height: 16px; + margin-left: 4px; + text-align: center; + user-select: none; + + &:hover { + background-color: #ddd; + } + } + + &:hover .entity-root-button { + display: block; + } + } +} diff --git a/tools/debug-ui/src/entity_debug/EntityDataRootView.tsx b/tools/debug-ui/src/entity_debug/EntityDataRootView.tsx new file mode 100644 index 000000000..132deba3f --- /dev/null +++ b/tools/debug-ui/src/entity_debug/EntityDataRootView.tsx @@ -0,0 +1,89 @@ +import { useCallback, useContext, useEffect, useState } from 'react'; +import { + EntityDataValueNode, + EntityDataRootNode, + entityQueryOutputType, + getQueryType, +} from './types'; +import { FetcherContext } from './fetcher'; +import { AllQueriesContext } from './all_queries'; +import './EntityDataRootView.scss'; +import { EntityDataValueView } from './EntityDataValueView'; + +export type EntityDataRootViewProps = { + node: EntityDataRootNode; + removalCallback: (() => void) | null; +}; + +export const EntityDataRootView = ({ node, removalCallback }: EntityDataRootViewProps) => { + const [entry, setEntry] = useState(null); + const [error, setError] = useState(null); + const [, setVersion] = useState(0); + useEffect(() => { + node.entry.then( + (result) => { + setEntry(result); + }, + (err) => { + setError('' + err); + } + ); + return () => { + setEntry(null); + setError(null); + }; + }, [node.entry]); + const { dispatch: allQueriesDispatch } = useContext(AllQueriesContext); + const fetcher = useContext(FetcherContext); + const extractCallback = useCallback(() => { + allQueriesDispatch({ type: 'add-query', query: node.query, node: node }); + removalCallback!(); + }, [allQueriesDispatch, node, removalCallback]); + const refreshCallback = useCallback(() => { + node.entry = fetcher!.fetch(node.query); + setVersion((v) => v + 1); + }, [node, fetcher]); + let content: JSX.Element; + if (error) { + const message = error.match(/String\("([^"]*)"\)/); + content =
{message ? message[1] : error}
; + } else if (entry === null) { + content =
Loading...
; + } else { + content = ( +
+ {typeof entry.value === 'string' ? ( + + ) : ( + entry.value.entries.map((entry) => ( + + )) + )} +
+ ); + } + return ( +
+
+
{entityQueryOutputType[getQueryType(node.query)]}
+ {removalCallback && ( +
+ ✕ +
+ )} + {removalCallback && ( +
+ ⧉ +
+ )} +
+ ⟳ +
+
+ {content} +
+ ); +}; diff --git a/tools/debug-ui/src/entity_debug/EntityDataValueView.scss b/tools/debug-ui/src/entity_debug/EntityDataValueView.scss new file mode 100644 index 000000000..b3ec28efa --- /dev/null +++ b/tools/debug-ui/src/entity_debug/EntityDataValueView.scss @@ -0,0 +1,147 @@ +.entity-entry { + .entity-entry-header { + display: flex; + gap: 4px; + align-items: flex-start; + flex-wrap: wrap; + + &:hover { + background-color: #eee; + } + } + + .entity-entry-name { + color: gray; + + &:after { + content: ':'; + color: gray; + } + } + + .entity-entry-value { + word-wrap: break-word; + max-width: 800px; + + .value-trie-path { + .shard-uid { + font-size: 0.8em; + + &:after { + content: '/'; + } + } + + .state-root { + font-size: 0.8em; + + &:after { + content: '/'; + } + } + } + + .nibbles { + font-family: monospace; + + .hex-chars { + background-color: rgb(255, 254, 193); + color: rgb(117, 104, 0); + font-size: 0.9em; + } + + .ascii-char { + background-color: rgb(171, 255, 171); + } + } + } + + .entity-entry-value-with-keys { + color: rgb(0, 0, 198); + } + + .entity-entry-expander { + margin-top: 4px; + width: 16px; + height: 16px; + line-height: 16px; + text-align: center; + border: 1px solid gray; + border-radius: 2px; + cursor: pointer; + + &:hover { + background-color: #eee; + } + } + + .entity-entry-children { + margin-left: 8px; + padding-left: 8px; + border-left: 1px dashed gray; + } + + .entity-entry-queried-children { + > * { + margin-left: 8px; + margin-top: 4px; + margin-bottom: 8px; + border: 1px solid gray; + } + } + + .entity-key-pin-button { + margin-top: 2.5px; + background-color: #ddd; + border-radius: 16px; + padding: 0 6px; + font-size: 0.8em; + cursor: pointer; + user-select: none; + + &.selected { + background-color: rgb(69, 125, 255); + color: white; + } + } + + .entity-query-button { + margin-top: 2.5px; + + button { + display: block; + background-color: #c8ffc5; + border: none; + border-radius: 16px; + padding: 0 6px; + font-size: 0.8em; + user-select: none; + + &[disabled] { + background-color: #ddd; + color: gray; + cursor: not-allowed; + } + } + } + + .entity-query-tooltip { + .entity-query-type { + font-weight: bold; + + &:before { + content: 'Query: '; + font-weight: normal; + } + } + + .entity-query-keys { + margin-left: 16px; + + .missing { + font-weight: bold; + color: rgb(255, 186, 186); + } + } + } +} diff --git a/tools/debug-ui/src/entity_debug/EntityDataValueView.tsx b/tools/debug-ui/src/entity_debug/EntityDataValueView.tsx new file mode 100644 index 000000000..4d43a3f6d --- /dev/null +++ b/tools/debug-ui/src/entity_debug/EntityDataValueView.tsx @@ -0,0 +1,274 @@ +import { useCallback, useContext, useState } from 'react'; +import { Tooltip } from 'react-tooltip'; +import { FetcherContext } from './fetcher'; +import { + EntityDataValueNode, + EntityDataRootNode, + EntityKey, + EntityKeyType, + EntityQuery, + EntityQueryType, + entityQueryKeyTypes, + entityQueryOutputType, + entityQueryTypes, +} from './types'; +import { PinnedKeysContext } from './pinned_keys'; +import { EntityDataRootView } from './EntityDataRootView'; +import './EntityDataValueView.scss'; + +export type EntityDataValueViewProps = { + entry: EntityDataValueNode; + hideName: boolean; +}; + +export const EntityDataValueView = ({ entry, hideName }: EntityDataValueViewProps) => { + const fetcher = useContext(FetcherContext); + const [expanded, setExpanded] = useState(false); + const [hovering, setHovering] = useState(false); + const [, setChildrenVersion] = useState(0); + const { keys: pinnedKeys, dispatch: pinnedKeysDispatch } = useContext(PinnedKeysContext); + + const addQuery = useCallback( + (query: EntityQuery) => { + if (fetcher == null) { + return; + } + entry.queriedChildren.push(new EntityDataRootNode(query, fetcher.fetch(query))); + setChildrenVersion((v) => v + 1); + }, + [fetcher, entry] + ); + + const onMouseEnter = useCallback(() => setHovering(true), []); + const onMouseLeave = useCallback(() => setHovering(false), []); + const toggleExpanded = useCallback(() => setExpanded((v) => !v), []); + + let entryValue = null; + if (typeof entry.value === 'string') { + entryValue = {entry.value}; + if (entry.semantic !== undefined && 'display' in entry.semantic) { + const display = entry.semantic.display!; + if (display === 'trie_path') { + const [shard_uid, state_root, nibbles] = entry.value.split('/'); + if (nibbles) { + entryValue = ( + + {shard_uid} + {state_root} + {visualizeNibbles(nibbles)} + + ); + } + } else if (display === 'nibbles') { + entryValue = {visualizeNibbles(entry.value)}; + } + } + } else if (entry.semantic?.titleKey !== undefined) { + const titleKey = entry.semantic.titleKey; + const titleEntry = entry.value.entries.find((e) => e.name === titleKey); + if (titleEntry !== undefined) { + entryValue = {titleEntry.value as string}; + } + } + const header = ( +
+ {typeof entry.value !== 'string' && ( +
+ {expanded ? '-' : '+'} +
+ )} + {!(hideName && typeof entry.value === 'string') && ( +
{entry.name}
+ )} +
0 ? ' entity-entry-value-with-keys' : '') + }> + {entryValue} +
+ {entry.keys.map((key) => { + const selected = pinnedKeys.some( + (pinnedKey) => + pinnedKey.type() === key.type() && pinnedKey.toString() === key.toString() + ); + if (selected || hovering) { + return ( +
+ e.currentTarget.classList.contains('selected') + ? pinnedKeysDispatch({ + type: 'remove-key', + keyType: key.type(), + }) + : pinnedKeysDispatch({ type: 'add-key', key }) + }> + {selected ? '☑' : '☐'} {key.type()} +
+ ); + } + return null; + })} + {hovering && + getAvailableQueries(entry.keys, pinnedKeys).map(({ queryType, keys, query }) => { + const entityType = entityQueryOutputType[queryType]; + return ( + <> +
+ +
+ +
+
{queryType}
+
+ {keys.map(({ keyType, key }) => ( +
+ {keyType}:{' '} + {key ? ( + {key.toString()} + ) : ( + + (missing, please pin a key) + + )} +
+ ))} +
+
+
+ + ); + })} +
+ ); + + return ( +
+ {header} + {expanded && typeof entry.value !== 'string' && ( +
+ {entry.value.entries.map((entry) => ( + + ))} +
+ )} + {entry.queriedChildren.length > 0 && ( +
+ {entry.queriedChildren.map((child, i) => ( + { + entry.queriedChildren.splice(i, 1); + setChildrenVersion((v) => v + 1); + }} + /> + ))} +
+ )} +
+ ); +}; + +type AvailableQueryKey = { + keyType: EntityKeyType; + key: EntityKey | null; +}; + +type AvailableQuery = { + queryType: EntityQueryType; + keys: AvailableQueryKey[]; + query: EntityQuery | null; +}; + +/// Calculates the queries that we should display on a given value, based on the keys that are +/// derived from that value, as well as the keys that are pinned. +/// +/// A query is available if: +/// - the query can be constructed from some of the keys in "keys" and "pinnedKeys"; and +/// - the query has at least one explicit key in "keys" +function getAvailableQueries(keys: EntityKey[], pinnedKeys: EntityKey[]): AvailableQuery[] { + const result: AvailableQuery[] = []; + const keyTypeToKey = new Map(); + const explicitKeyTypes = new Set(); + for (const key of keys) { + keyTypeToKey.set(key.type(), key); + explicitKeyTypes.add(key.type()); + } + for (const key of pinnedKeys) { + if (!keyTypeToKey.has(key.type())) { + keyTypeToKey.set(key.type(), key); + } + } + + for (const queryType of entityQueryTypes) { + let explicitKeyCount = 0; + let allQueryKeysFound = true; + const keys = entityQueryKeyTypes[queryType].map(({ keyType, implicitOnly }) => { + const key = keyTypeToKey.get(keyType) ?? null; + if (explicitKeyTypes.has(keyType) && !implicitOnly) { + explicitKeyCount++; + } + if (key === null) { + allQueryKeysFound = false; + } + return { keyType, key }; + }); + if (explicitKeyCount > 0) { + if (allQueryKeysFound) { + const query: EntityQuery = {}; + const args: { [_ in EntityKeyType]?: unknown } = {}; + query[queryType] = args as any; + for (const { keyType, key } of keys) { + if (key) { + args[keyType] = key.toJSON(); + } + } + result.push({ queryType, keys, query }); + } else { + result.push({ queryType, keys, query: null }); + } + } + } + return result; +} + +function visualizeNibbles(nibbles: string): JSX.Element { + const children = []; + for (let i = 0; i < Math.floor(nibbles.length / 2); i++) { + const byte = parseInt(nibbles.substring(i * 2, i * 2 + 2), 16); + if (byte >= 0x20 && byte <= 0x7e) { + children.push( + + {String.fromCharCode(byte)} + + ); + } else { + children.push( + + {nibbles.substring(i * 2, i * 2 + 2)} + + ); + } + } + if (nibbles.length % 2 == 1) { + children.push( + + {nibbles.substring(nibbles.length - 1)} + + ); + } + return <>{children}; +} diff --git a/tools/debug-ui/src/entity_debug/EntityDebugView.scss b/tools/debug-ui/src/entity_debug/EntityDebugView.scss new file mode 100644 index 000000000..56de01f5e --- /dev/null +++ b/tools/debug-ui/src/entity_debug/EntityDebugView.scss @@ -0,0 +1,41 @@ +.entity-debug-view { + display: flex; + height: calc(100vh - 66px); + align-items: stretch; + + .left-panel { + border-right: 1px solid gray; + display: flex; + flex-direction: column; + } + + .right-panel { + flex: 1; + display: flex; + flex-direction: column; + } + + .left-panel-query-list { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; + } + + .right-panel-entity-tree { + flex: 1; + overflow: hidden; + overflow-y: auto; + display: flex; + align-items: stretch; + + > * { + flex: 1; + } + } + + .right-panel-pinned-keys { + background-color: #eee; + padding: 2px 10px; + } +} diff --git a/tools/debug-ui/src/entity_debug/EntityDebugView.tsx b/tools/debug-ui/src/entity_debug/EntityDebugView.tsx new file mode 100644 index 000000000..42763183f --- /dev/null +++ b/tools/debug-ui/src/entity_debug/EntityDebugView.tsx @@ -0,0 +1,68 @@ +import { useMemo, useReducer } from 'react'; +import { Fetcher, FetcherContext } from './fetcher'; +import { AllQueriesContext, allQueriesReducer } from './all_queries'; +import { PinnedKeysContext, pinnedKeysReducer } from './pinned_keys'; +import { AllQueriesDisplay } from './AllQueriesDisplay'; +import { PinnedKeysView } from './PinnedKeysView'; +import { EntityDataRootView } from './EntityDataRootView'; +import { EntityQueryComposer } from './EntityQueryComposer'; +import './EntityDebugView.scss'; + +export type EntityDebugViewProps = { + addr: string; +}; + +export const EntityDebugView = ({ addr }: EntityDebugViewProps) => { + const fetcher = useMemo(() => new Fetcher(addr), [addr]); + const [allQueries, allQueriesDispatcher] = useReducer(allQueriesReducer, { + queries: [], + results: [], + selectedIndex: -1, + }); + const [pinnedKeys, pinnedKeysDispatcher] = useReducer(pinnedKeysReducer, []); + const selectedQueryResult = + allQueries.selectedIndex === -1 ? null : allQueries.results[allQueries.selectedIndex]; + + const render = ( +
+
+
+ +
+
+
+
+ {selectedQueryResult ? ( +
+ +
+ ) : ( + + )} +
+
+ +
+
+
+ ); + return ( + + + + {render} + + + + ); +}; diff --git a/tools/debug-ui/src/entity_debug/EntityQueryComposer.scss b/tools/debug-ui/src/entity_debug/EntityQueryComposer.scss new file mode 100644 index 000000000..3f93f22ba --- /dev/null +++ b/tools/debug-ui/src/entity_debug/EntityQueryComposer.scss @@ -0,0 +1,58 @@ +.query-composer-container { + display: flex; + align-items: stretch; + + .query-type-list { + display: flex; + flex-direction: column; + border-right: 1px solid gray; + + .list-header { + font-size: 18px; + font-weight: bold; + border-bottom: 1px solid gray; + font-variant: small-caps; + padding: 2px 6px; + } + + .list-body { + flex: 1; + overflow: hidden; + overflow-y: auto; + } + + .list-item { + padding: 6px; + cursor: pointer; + + &.list-item-selected { + background-color: #eee; + } + + &:hover { + background-color: #ddd; + } + } + } + + .query-composer { + padding: 10px; + + .query-composer-header { + font-weight: bold; + padding-bottom: 4px; + } + + .query-composer-keys { + padding-left: 20px; + margin-bottom: 6px; + display: flex; + flex-direction: column; + gap: 8px; + } + + button:disabled { + color: gray; + } + } +} diff --git a/tools/debug-ui/src/entity_debug/EntityQueryComposer.tsx b/tools/debug-ui/src/entity_debug/EntityQueryComposer.tsx new file mode 100644 index 000000000..feabfa9d8 --- /dev/null +++ b/tools/debug-ui/src/entity_debug/EntityQueryComposer.tsx @@ -0,0 +1,98 @@ +import { useCallback, useContext, useReducer } from 'react'; +import { + EntityDataRootNode, + EntityKeyType, + EntityQuery, + entityQueryKeyTypes, + entityQueryTypes, +} from './types'; +import { ComposingQueryContext, composingQueryReducer } from './composing_query'; +import { KeyInput } from './KeyInput'; +import { AllQueriesContext } from './all_queries'; +import { FetcherContext } from './fetcher'; +import './EntityQueryComposer.scss'; +import { PinnedKeysContext } from './pinned_keys'; + +export const EntityQueryComposer = () => { + const { keys: pinnedKeys } = useContext(PinnedKeysContext); + const [query, queryDispatch] = useReducer(composingQueryReducer, { + queryType: entityQueryTypes[0], + keys: pinnedKeys, + }); + const { dispatch: allQueriesDispatch } = useContext(AllQueriesContext); + const fetcher = useContext(FetcherContext); + + const constructedQuery: EntityQuery | null = (() => { + if (entityQueryKeyTypes[query.queryType].length == 0) { + return { [query.queryType]: null }; + } + const args: { [_ in EntityKeyType]?: unknown } = {}; + for (const { keyType } of entityQueryKeyTypes[query.queryType]) { + const key = query.keys.find((key) => key.type() === keyType); + if (!key) { + return null; + } + args[keyType] = key.toJSON(); + } + return { + [query.queryType]: args as any, + }; + })(); + + const submitQuery = useCallback(() => { + if (constructedQuery && fetcher) { + allQueriesDispatch({ + type: 'add-query', + query: constructedQuery, + node: new EntityDataRootNode(constructedQuery, fetcher.fetch(constructedQuery)), + }); + } + }, [constructedQuery, fetcher, allQueriesDispatch]); + + const selectionList = ( +
+
+ All Query Types +
+
+ {entityQueryTypes.map((queryType) => { + return ( +
queryDispatch({ type: 'set-type', queryType })}> + {queryType} +
+ ); + })} +
+
+ ); + const composer = ( +
+
+ {query.queryType} +
+
+ {entityQueryKeyTypes[query.queryType].map(({ keyType }) => ( + + ))} +
+
+ +
+
+ ); + return ( + +
+ {selectionList} + {composer} +
+
+ ); +}; diff --git a/tools/debug-ui/src/entity_debug/KeyInput.scss b/tools/debug-ui/src/entity_debug/KeyInput.scss new file mode 100644 index 000000000..42fe68f7b --- /dev/null +++ b/tools/debug-ui/src/entity_debug/KeyInput.scss @@ -0,0 +1,15 @@ +.key-editor { + display: flex; + gap: 6px; + align-items: center; + + .key-value { + width: 500px; + border: 1px solid gray; + + &.key-value-invalid { + border: 1px solid red; + background-color: #ffe6e6; + } + } +} diff --git a/tools/debug-ui/src/entity_debug/KeyInput.tsx b/tools/debug-ui/src/entity_debug/KeyInput.tsx new file mode 100644 index 000000000..8da60268a --- /dev/null +++ b/tools/debug-ui/src/entity_debug/KeyInput.tsx @@ -0,0 +1,42 @@ +import { FormEvent, useCallback, useContext, useState } from 'react'; +import { ComposingQueryContext } from './composing_query'; +import { EntityKeyType } from './types'; +import { parseEntityKey } from './keys'; +import './KeyInput.scss'; + +export type KeyInputProps = { + keyType: EntityKeyType; +}; + +/// A component to accept a string input for a specific type of EntityKey. +/// It works by adding or removing the key from the composing query (from +/// context). If the key is valid it is added, but if it's invalid it's +/// removed. +export const KeyInput = ({ keyType }: KeyInputProps) => { + const { composingQuery, dispatch } = useContext(ComposingQueryContext); + const key = composingQuery.keys.find((key) => key.type() === keyType); + const [text, setText] = useState(''); + const onInput = useCallback( + (e: FormEvent) => { + const value = e.currentTarget.value; + setText(value); + const parsedKey = parseEntityKey(keyType, value); + if (!parsedKey) { + dispatch({ type: 'remove-key', keyType }); + } else { + dispatch({ type: 'set-key', key: parsedKey }); + } + }, + [keyType, dispatch] + ); + return ( +
+
{keyType}
+ +
+ ); +}; diff --git a/tools/debug-ui/src/entity_debug/PinnedKeysView.scss b/tools/debug-ui/src/entity_debug/PinnedKeysView.scss new file mode 100644 index 000000000..c6f4089d4 --- /dev/null +++ b/tools/debug-ui/src/entity_debug/PinnedKeysView.scss @@ -0,0 +1,44 @@ +.pinned-keys { + display: flex; + flex-wrap: wrap; + gap: 8px; + font-size: 0.8em; + padding: 2px 0; + + .pinned-key { + display: flex; + align-items: center; + border-radius: 16px; + background-color: rgb(69, 125, 255); + color: white; + padding: 0 6px; + + .pinned-key-type { + font-weight: bold; + + &:after { + content: ':'; + margin-right: 3px; + } + } + + .pinned-key-delete { + width: 12px; + height: 12px; + line-height: 12px; + text-align: center; + border-radius: 100%; + background-color: rgb(16, 43, 106); + margin-left: 4px; + cursor: pointer; + + &:hover { + background-color: rgb(176, 58, 58); + } + } + } + + .pinned-keys-empty { + color: gray; + } +} diff --git a/tools/debug-ui/src/entity_debug/PinnedKeysView.tsx b/tools/debug-ui/src/entity_debug/PinnedKeysView.tsx new file mode 100644 index 000000000..2706cbbd6 --- /dev/null +++ b/tools/debug-ui/src/entity_debug/PinnedKeysView.tsx @@ -0,0 +1,26 @@ +import { useContext } from 'react'; +import { PinnedKeysContext } from './pinned_keys'; +import './PinnedKeysView.scss'; + +export const PinnedKeysView = () => { + const { keys, dispatch } = useContext(PinnedKeysContext); + return ( +
+
Pinned keys:
+ {keys.map((key) => ( +
+
{key.type()}
+
{key.toString()}
+
dispatch({ type: 'remove-key', keyType: key.type() })}> + × +
+
+ ))} + {keys.length == 0 && ( +
(Click an entity key to pin it)
+ )} +
+ ); +}; diff --git a/tools/debug-ui/src/entity_debug/all_queries.tsx b/tools/debug-ui/src/entity_debug/all_queries.tsx new file mode 100644 index 000000000..b8f7682dc --- /dev/null +++ b/tools/debug-ui/src/entity_debug/all_queries.tsx @@ -0,0 +1,72 @@ +/// A react data structure for a list of all queries displayed on the UI. +/// See https://react.dev/reference/react/useReducer to understand this pattern. + +import { createContext } from 'react'; +import { EntityDataRootNode, EntityQuery } from './types'; + +export type AllQueriesAction = + | { + type: 'add-query'; + query: EntityQuery; + node: EntityDataRootNode; + } + | { + type: 'remove-query'; + index: number; + } + | { + type: 'select-query'; + index: number; + }; + +export type AllQueriesState = { + /// parallel array of queries and results. + queries: EntityQuery[]; + results: EntityDataRootNode[]; + selectedIndex: number; // -1 means no selection +}; + +export function allQueriesReducer( + queries: AllQueriesState, + action: AllQueriesAction +): AllQueriesState { + switch (action.type) { + case 'add-query': + return { + queries: [...queries.queries, action.query], + results: [...queries.results, action.node], + selectedIndex: queries.queries.length, + }; + case 'remove-query': { + const newSelectedIndex = + queries.selectedIndex === action.index + ? -1 + : queries.selectedIndex > action.index + ? queries.selectedIndex - 1 + : queries.selectedIndex; + return { + queries: queries.queries.filter((_, i) => i !== action.index), + results: queries.results.filter((_, i) => i !== action.index), + selectedIndex: newSelectedIndex, + }; + } + case 'select-query': + return { + queries: queries.queries, + results: queries.results, + selectedIndex: action.index, + }; + } +} + +export const AllQueriesContext = createContext<{ + queries: EntityQuery[]; + results: EntityDataRootNode[]; + selectedIndex: number; + dispatch: React.Dispatch; +}>({ + queries: [], + results: [], + selectedIndex: -1, + dispatch: () => {}, +}); diff --git a/tools/debug-ui/src/entity_debug/composing_query.tsx b/tools/debug-ui/src/entity_debug/composing_query.tsx new file mode 100644 index 000000000..d94094f1c --- /dev/null +++ b/tools/debug-ui/src/entity_debug/composing_query.tsx @@ -0,0 +1,60 @@ +/// A react data structure for a simple query that is being composed via the UI. +/// See https://react.dev/reference/react/useReducer to understand this pattern. + +import { createContext } from 'react'; +import { EntityKey, EntityKeyType, EntityQueryType, entityQueryTypes } from './types'; + +export type ComposingQuery = { + queryType: EntityQueryType; + keys: EntityKey[]; +}; + +export type ComposingQueryAction = + | { + type: 'set-type'; + queryType: EntityQueryType; + } + | { + type: 'set-key'; + key: EntityKey; + } + | { + type: 'remove-key'; + keyType: EntityKeyType; + }; + +export function composingQueryReducer( + state: ComposingQuery, + action: ComposingQueryAction +): ComposingQuery { + switch (action.type) { + case 'set-type': + return { + ...state, + queryType: action.queryType, + }; + case 'set-key': { + const keys = state.keys.filter((key) => key.type() !== action.key.type()); + return { + ...state, + keys: [...keys, action.key], + }; + } + case 'remove-key': + return { + ...state, + keys: state.keys.filter((key) => key.type() !== action.keyType), + }; + } +} + +export const ComposingQueryContext = createContext<{ + composingQuery: ComposingQuery; + dispatch: React.Dispatch; +}>({ + composingQuery: { + queryType: entityQueryTypes[0], + keys: [], + }, + dispatch: () => {}, +}); diff --git a/tools/debug-ui/src/entity_debug/fetcher.tsx b/tools/debug-ui/src/entity_debug/fetcher.tsx new file mode 100644 index 000000000..2bfc69732 --- /dev/null +++ b/tools/debug-ui/src/entity_debug/fetcher.tsx @@ -0,0 +1,56 @@ +import { createContext } from 'react'; +import { ApiEntityDataEntry, fetchEntity } from '../api'; +import { + EntityDataValueNode, + EntityDataStructNode, + EntityQuery, + FieldSemantic, + entityQueryOutputType, +} from './types'; +import { fieldSemantics } from './fields'; + +/// A simple class to fetch entity data for a query. +export class Fetcher { + constructor(private addr: string) {} + + async fetch(query: EntityQuery): Promise { + const result = await fetchEntity(this.addr, query); + const queryType: keyof EntityQuery = Object.keys(query)[0] as keyof EntityQuery; + const entityType = entityQueryOutputType[queryType]; + const rootEntry = this._parseApiEntityDataValue(fieldSemantics[entityType], { + name: '_root', + value: result, + }); + return rootEntry; + } + + /// Parses the API EntityDataValue response into our representation of it. + _parseApiEntityDataValue( + fieldSemantic: FieldSemantic, + entry: ApiEntityDataEntry + ): EntityDataValueNode { + if (typeof entry.value === 'string') { + return new EntityDataValueNode(fieldSemantic, entry.name, entry.value); + } + const data = new EntityDataStructNode(); + for (const childEntry of entry.value.entries) { + const isIndex = /^\d+$/.test(childEntry.name); + let childSemantic; + if (isIndex) { + childSemantic = + fieldSemantic !== undefined && 'array' in fieldSemantic + ? fieldSemantic.array + : undefined; + } else { + childSemantic = + fieldSemantic !== undefined && 'struct' in fieldSemantic + ? fieldSemantic.struct![childEntry.name] + : undefined; + } + data.entries.push(this._parseApiEntityDataValue(childSemantic, childEntry)); + } + return new EntityDataValueNode(fieldSemantic, entry.name, data); + } +} + +export const FetcherContext = createContext(null); diff --git a/tools/debug-ui/src/entity_debug/fields.tsx b/tools/debug-ui/src/entity_debug/fields.tsx new file mode 100644 index 000000000..a62f8d8f3 --- /dev/null +++ b/tools/debug-ui/src/entity_debug/fields.tsx @@ -0,0 +1,237 @@ +// Defines the field semantics for specific entity types. + +import { NumericEntityKey, StringEntityKey } from './keys'; +import { EntityKey, EntityKeyType, EntityType, FieldSemantic } from './types'; + +function stringField(keyType: EntityKeyType): FieldSemantic { + return { + parser: (_, v) => [new StringEntityKey(keyType, v)], + }; +} + +function numericField(keyType: EntityKeyType): FieldSemantic { + return { + parser: (_, v) => { + return [new NumericEntityKey(keyType, parseInt(v))] as EntityKey[]; + }, + }; +} + +const blockHash = stringField('block_hash'); +const blockOrdinal = numericField('block_ordinal'); +const blockHeight = numericField('block_height'); +const chunkHash = stringField('chunk_hash'); +const epochId = stringField('epoch_id'); +const transactionHash = stringField('transaction_hash'); +const receiptId = stringField('receipt_id'); +const accountId = stringField('account_id'); +const shardId = numericField('shard_id'); +const shardUId: FieldSemantic = { + parser: (_, v) => { + const shardId = v.match(/^s(\d+)[.]v\d+$/); + const shardIdKey = shardId ? [new NumericEntityKey('shard_id', parseInt(shardId[1]))] : []; + return [new StringEntityKey('shard_uid', v), ...shardIdKey]; + }, +}; +const stateRoot = stringField('state_root'); +const triePath: FieldSemantic = { + parser: (_, v) => { + return [new StringEntityKey('trie_path', v)] as EntityKey[]; + }, + display: 'trie_path', +}; + +const nibbles: FieldSemantic = { + display: 'nibbles', +}; + +const trieKey: FieldSemantic = { + display: 'nibbles', + parser: (_, v) => { + return [new StringEntityKey('trie_key', v)] as EntityKey[]; + }, +}; + +const blockHeader = { + struct: { + height: blockHeight, + prev_height: blockHeight, + epoch_id: epochId, + next_epoch_id: epochId, + hash: blockHash, + prev_hash: blockHash, + block_ordinal: blockOrdinal, + last_final_block: blockHash, + last_ds_final_block: blockHash, + }, +}; + +const chunkHeader = { + struct: { + chunk_hash: chunkHash, + prev_block_hash: blockHash, + prev_state_root: stateRoot, + height_created: blockHeight, + height_included: blockHeight, + shard_id: shardId, + }, + titleKey: 'chunk_hash', +}; + +const transaction = { + struct: { + signer_id: accountId, + receiver_id: accountId, + hash: transactionHash, + }, + titleKey: 'hash', +}; + +const receipt = { + struct: { + predecessor_id: accountId, + receiver_id: accountId, + receipt_id: receiptId, + }, + titleKey: 'receipt_id', +}; + +const chunk = { + struct: { + author: accountId, + header: chunkHeader, + transactions: { array: transaction }, + receipts: { array: receipt }, + }, +}; + +const block = { + struct: { + author: accountId, + header: blockHeader, + chunks: { + array: { + ...chunkHeader, + parser: (k: string) => [new NumericEntityKey('shard_id', parseInt(k))], + }, + }, + }, +}; + +const validatorStake = { + titleKey: 'account_id', +}; + +const epochInfo = { + struct: { + V3: { + struct: { + validators: { + array: validatorStake, + }, + }, + }, + }, +}; + +const executionOutcome = { + struct: { + receipt_ids: { array: receiptId }, + executor_id: accountId, + }, +}; + +const trieNode = { + struct: { + path: nibbles, + leaf_path: trieKey, + extension: triePath, + // The following is a bit of a hack. The children of a trie node are + // returned with keys 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, , b, c, d, e, f; + // the first 10 are numeric so they get interpreted as array indices, + // the last 6 are not numeric so they get interpreted as struct fields. + a: triePath, + b: triePath, + c: triePath, + d: triePath, + e: triePath, + f: triePath, + }, + array: triePath, +}; + +const tip = { + struct: { + height: blockHeight, + last_block_hash: blockHash, + prev_block_hash: blockHash, + epoch_id: epochId, + next_epoch_id: epochId, + }, +}; + +const blockInfo = { + struct: { + hash: blockHash, + height: blockHeight, + prev_hash: blockHash, + }, +}; + +const flatStorageStatus = { + struct: { + Creation: { + struct: { + FetchingState: { + struct: { + block_hash: blockHash, + }, + }, + CatchingUp: blockHash, + }, + }, + Ready: { + struct: { + flat_head: blockInfo, + }, + }, + }, +}; + +const flatStateChangeView = { + struct: { + key: trieKey, + }, +}; + +const flatStateChanges = { + array: flatStateChangeView, +}; + +const flatStateDeltaMetadata = { + struct: { + block: blockInfo, + }, +}; + +export const fieldSemantics: Record = { + AllShards: { array: shardUId }, + Block: block, + BlockHash: blockHash, + BlockHeader: blockHeader, + Chunk: chunk, + EpochInfo: epochInfo, + ExecutionOutcome: executionOutcome, + FlatState: undefined, + FlatStateChanges: flatStateChanges, + FlatStateDeltaMetadata: flatStateDeltaMetadata, + FlatStorageStatus: flatStorageStatus, + Receipt: receipt, + ShardId: shardId, + ShardLayout: undefined, + ShardUId: shardUId, + Tip: tip, + Transaction: transaction, + TrieNode: trieNode, + TrieRoot: triePath, +}; diff --git a/tools/debug-ui/src/entity_debug/keys.tsx b/tools/debug-ui/src/entity_debug/keys.tsx new file mode 100644 index 000000000..f7bfb5afd --- /dev/null +++ b/tools/debug-ui/src/entity_debug/keys.tsx @@ -0,0 +1,76 @@ +// Concrete implementations for EntityKey. + +import { EntityKey, EntityKeyType } from './types'; + +export class StringEntityKey { + constructor(private _type: EntityKeyType, private value: string) {} + type(): EntityKeyType { + return this._type; + } + toString(): string { + return this.value; + } + toJSON(): unknown { + return this.value; + } +} + +export class NumericEntityKey { + constructor(private _type: EntityKeyType, private value: number) {} + type(): EntityKeyType { + return this._type; + } + toString(): string { + return this.value.toString(); + } + toJSON(): unknown { + return this.value; + } +} + +/// Parses an EntityKey from string. +export function parseEntityKey(keyType: EntityKeyType, input: string): EntityKey | null { + switch (keyType) { + case 'account_id': + if (input.length == 0) { + return null; + } + return new StringEntityKey(keyType, input); + case 'block_height': + case 'block_ordinal': + if (/^\d+$/.test(input)) { + return new NumericEntityKey(keyType, parseInt(input)); + } + return null; + case 'block_hash': + case 'chunk_hash': + case 'epoch_id': + case 'receipt_id': + case 'transaction_hash': + case 'state_root': + if (input.length != 44) { + return null; + } + return new StringEntityKey(keyType, input); + case 'shard_id': + if (/^\d+$/.test(input)) { + return new NumericEntityKey(keyType, parseInt(input)); + } + return null; + case 'shard_uid': + if (/^s\d+[.]v\d+$/.test(input)) { + return new StringEntityKey(keyType, input); + } + return null; + case 'trie_path': + if (/^s\d+[.]v\d+$\/[0-9A-Za-z]{44}\/[0-9a-f]*$/.test(input)) { + return new StringEntityKey(keyType, input); + } + return null; + case 'trie_key': + if (/^([0-9a-f][0-9a-f])*$/.test(input)) { + return new StringEntityKey(keyType, input); + } + return null; + } +} diff --git a/tools/debug-ui/src/entity_debug/pinned_keys.tsx b/tools/debug-ui/src/entity_debug/pinned_keys.tsx new file mode 100644 index 000000000..1cf0df6af --- /dev/null +++ b/tools/debug-ui/src/entity_debug/pinned_keys.tsx @@ -0,0 +1,32 @@ +/// A react data structure for a list of pinned entity keys. +/// See https://react.dev/reference/react/useReducer to understand this pattern. + +import { createContext } from 'react'; +import { EntityKey, EntityKeyType } from './types'; + +export type PinnedKeysAction = + | { + type: 'add-key'; + key: EntityKey; + } + | { type: 'remove-key'; keyType: EntityKeyType }; + +export function pinnedKeysReducer(keys: EntityKey[], action: PinnedKeysAction): EntityKey[] { + switch (action.type) { + case 'add-key': + if (keys.some((key) => key.type() === action.key.type())) { + return keys.map((key) => (key.type() === action.key.type() ? action.key : key)); + } + return [...keys, action.key]; + case 'remove-key': + return keys.filter((key) => key.type() !== action.keyType); + } +} + +export const PinnedKeysContext = createContext<{ + keys: EntityKey[]; + dispatch: React.Dispatch; +}>({ + keys: [], + dispatch: () => {}, +}); diff --git a/tools/debug-ui/src/entity_debug/types.tsx b/tools/debug-ui/src/entity_debug/types.tsx new file mode 100644 index 000000000..e9383643b --- /dev/null +++ b/tools/debug-ui/src/entity_debug/types.tsx @@ -0,0 +1,270 @@ +/// Each key type represents a unique kind of input to an entity debug query. +export type EntityKeyType = + | 'account_id' + | 'block_hash' + | 'block_height' + | 'block_ordinal' + | 'chunk_hash' + | 'epoch_id' + | 'receipt_id' + | 'shard_id' + | 'shard_uid' + | 'state_root' + | 'transaction_hash' + | 'trie_key' + | 'trie_path'; + +/// Each entity type represents a unique kind of output from an entity debug +/// query. +export type EntityType = + | 'AllShards' + | 'Block' + | 'BlockHash' + | 'BlockHeader' + | 'Chunk' + | 'EpochInfo' + | 'ExecutionOutcome' + | 'FlatState' + | 'FlatStateChanges' + | 'FlatStateDeltaMetadata' + | 'FlatStorageStatus' + | 'Receipt' + | 'ShardId' + | 'ShardLayout' + | 'ShardUId' + | 'Tip' + | 'Transaction' + | 'TrieNode' + | 'TrieRoot'; + +/// Interface for a concrete entity key. +export interface EntityKey { + type(): EntityKeyType; + + /// A string representation, for display. + toString(): string; + + /// JSON representation, for composing a query that takes this key as input. + toJSON(): unknown; +} + +/// Root node for any query. +export class EntityDataRootNode { + constructor( + /// The query that produces the data in this node. + public query: EntityQuery, + /// A promise that resolves to the result data. + public entry: Promise + ) {} +} + +/// A struct node. +export class EntityDataStructNode { + public entries: EntityDataValueNode[] = []; +} + +/// The semantics of a field that enhances its display. +export type FieldSemantic = + | { + /// Customizes how the field should be displayed. + display?: CustomFieldDisplay; + /// If present, this field represents one or more entity keys, + /// and the parser provides the logic for parsing these keys out + /// of the field name and value. Typically it's just one key. + parser?: (key: string, value: string) => EntityKey[]; + /// If present, this field should be a struct, and we specify the + /// semantic of each of its fields. Fields can be missing if it doesn't + /// have any customizations. + struct?: Record; + /// If present, this field should be an array, and we specify the + /// semantic for each element of the array. + array?: FieldSemantic; + /// For struct or array fields, when displaying a node for the struct, if + /// titleKey is present then display the value of that child field as the + /// title of the struct node. This is useful for visualizing arrays where + /// otherwise each element of the array would have to be separately expanded + /// to know which element that is (for example an array of ValidatorStake). + titleKey?: string; + } + /// Undefined means there's no special customization for this field. + | undefined; + +export type CustomFieldDisplay = + /// Displays the value as a trie path (shard_uid/state_root/nibble_hex). + | 'trie_path' + /// Displays the value as a nibbles hex string. + | 'nibbles'; + +/// Represents a value node in the entity data tree. +export class EntityDataValueNode { + /// Any keys derived from this node. + keys: EntityKey[] = []; + /// Any children queries the user spawned from this node. + queriedChildren: EntityDataRootNode[] = []; + + constructor( + public semantic: FieldSemantic, + public name: string, + public value: string | EntityDataStructNode + ) { + const valueUsedForKeyDerivation = typeof value === 'string' ? value : ''; + if (semantic !== undefined && semantic.parser) { + for (const key of semantic.parser(name, valueUsedForKeyDerivation)) { + this.keys.push(key); + } + } + } +} + +/// Mirrors the EntityQuery type from Rust side. +export type EntityQuery = { + AllShardsByEpochId?: { epoch_id: string }; + BlockByHash?: { block_hash: string }; + BlockHashByHeight?: { block_height: number }; + BlockHeaderByHash?: { block_hash: string }; + ChunkByHash?: { chunk_hash: string }; + EpochInfoByEpochId?: { epoch_id: string }; + FlatStateByTrieKey?: { trie_key: string }; + FlatStateChangesByBlockHash?: { block_hash: string }; + FlatStateDeltaMetadataByBlockHash?: { block_hash: string }; + FlatStorageStatusByShardUId?: { shard_uid: string }; + OutcomeByReceiptId?: { receipt_id: string }; + OutcomeByReceiptIdAndBlockHash?: { receipt_id: string; block_hash: string }; + OutcomeByTransactionHash?: { transaction_hash: string }; + OutcomeByTransactionHashAndBlockHash?: { transaction_hash: string; block_hash: string }; + ReceiptById?: { receipt_id: string }; + ShardIdByAccountId?: { account_id: string }; + ShardLayoutByEpochId?: { epoch_id: string }; + ShardUIdByShardId?: { shard_id: number; epoch_id: string }; + TipAtFinalHead?: null; + TipAtHead?: null; + TipAtHeaderHead?: null; + TransactionByHash?: { transaction_hash: string }; + TrieNode?: { trie_path: string }; + TrieRootByChunkHash?: { chunk_hash: string }; + TrieRootByStateRoot?: { state_root: string; shard_uid: string }; +}; + +export type EntityQueryType = keyof EntityQuery; + +export function getQueryType(query: EntityQuery): EntityQueryType { + return Object.keys(query)[0] as EntityQueryType; +} + +export const entityQueryTypes: EntityQueryType[] = [ + 'AllShardsByEpochId', + 'BlockByHash', + 'BlockHashByHeight', + 'BlockHeaderByHash', + 'ChunkByHash', + 'EpochInfoByEpochId', + 'FlatStateByTrieKey', + 'FlatStateChangesByBlockHash', + 'FlatStateDeltaMetadataByBlockHash', + 'FlatStorageStatusByShardUId', + 'OutcomeByReceiptId', + 'OutcomeByReceiptIdAndBlockHash', + 'OutcomeByTransactionHash', + 'OutcomeByTransactionHashAndBlockHash', + 'ReceiptById', + 'ShardIdByAccountId', + 'ShardLayoutByEpochId', + 'ShardUIdByShardId', + 'TipAtFinalHead', + 'TipAtHead', + 'TipAtHeaderHead', + 'TransactionByHash', + 'TrieNode', + 'TrieRootByChunkHash', + 'TrieRootByStateRoot', +]; + +/// See entityQueryKeyTypes. +export type EntityQueryKeySpec = { + keyType: EntityKeyType; + implicitOnly: boolean; +}; + +/// See entityQueryKeyTypes. +function queryKey(keyType: EntityKeyType): EntityQueryKeySpec { + return { keyType, implicitOnly: false }; +} + +/// See entityQueryKeyTypes. +function implicitQueryKey(keyType: EntityKeyType): EntityQueryKeySpec { + return { keyType, implicitOnly: true }; +} + +/// For each query, we specify here the key types it requires. +/// Each query's set of key types must be the same as the keys that the query +/// actually requires (as specified by the EntityQuery type). +/// +/// Additionally, each key is either explicit or implicit. If a key is explicit, +/// it means that the query will show up as a prompt if the user hovers over a +/// node that provides that key. This is not true if the key is implicit. +/// +/// For example, querying a ShardUIdByShardId requires a shard_id (explicit) and +/// an epoch_id (implicit). If the user hovers over a node that provides a shard_id, +/// this query would show up as a button, as that logically makes sense. But if the +/// user hovers over an epoch_id, this query would not show up. +/// +/// For queries that require multiple keys, additional keys are specified by pinning. +export const entityQueryKeyTypes: Record = { + AllShardsByEpochId: [queryKey('epoch_id')], + BlockByHash: [queryKey('block_hash')], + BlockHashByHeight: [queryKey('block_height')], + BlockHeaderByHash: [queryKey('block_hash')], + ChunkByHash: [queryKey('chunk_hash')], + EpochInfoByEpochId: [queryKey('epoch_id')], + FlatStateByTrieKey: [queryKey('trie_key'), implicitQueryKey('shard_uid')], + FlatStateChangesByBlockHash: [queryKey('block_hash'), implicitQueryKey('shard_uid')], + FlatStateDeltaMetadataByBlockHash: [queryKey('block_hash'), implicitQueryKey('shard_uid')], + FlatStorageStatusByShardUId: [queryKey('shard_uid')], + OutcomeByReceiptId: [queryKey('receipt_id')], + OutcomeByReceiptIdAndBlockHash: [queryKey('receipt_id'), implicitQueryKey('block_hash')], + OutcomeByTransactionHash: [queryKey('transaction_hash')], + OutcomeByTransactionHashAndBlockHash: [ + queryKey('transaction_hash'), + implicitQueryKey('block_hash'), + ], + ReceiptById: [queryKey('receipt_id')], + ShardIdByAccountId: [queryKey('account_id'), implicitQueryKey('epoch_id')], + ShardLayoutByEpochId: [queryKey('epoch_id')], + ShardUIdByShardId: [queryKey('shard_id'), implicitQueryKey('epoch_id')], + TipAtFinalHead: [], + TipAtHead: [], + TipAtHeaderHead: [], + TransactionByHash: [queryKey('transaction_hash')], + TrieNode: [queryKey('trie_path')], + TrieRootByChunkHash: [queryKey('chunk_hash')], + TrieRootByStateRoot: [queryKey('state_root'), implicitQueryKey('shard_uid')], +}; + +/// Specifies the expected output entity type for each query. +export const entityQueryOutputType: Record = { + AllShardsByEpochId: 'AllShards', + BlockByHash: 'Block', + BlockHashByHeight: 'BlockHash', + BlockHeaderByHash: 'BlockHeader', + ChunkByHash: 'Chunk', + EpochInfoByEpochId: 'EpochInfo', + FlatStateByTrieKey: 'FlatState', + FlatStateChangesByBlockHash: 'FlatStateChanges', + FlatStateDeltaMetadataByBlockHash: 'FlatStateDeltaMetadata', + FlatStorageStatusByShardUId: 'FlatStorageStatus', + OutcomeByReceiptId: 'ExecutionOutcome', + OutcomeByReceiptIdAndBlockHash: 'ExecutionOutcome', + OutcomeByTransactionHash: 'ExecutionOutcome', + OutcomeByTransactionHashAndBlockHash: 'ExecutionOutcome', + ReceiptById: 'Receipt', + ShardIdByAccountId: 'ShardId', + ShardLayoutByEpochId: 'ShardLayout', + ShardUIdByShardId: 'ShardUId', + TipAtFinalHead: 'Tip', + TipAtHead: 'Tip', + TipAtHeaderHead: 'Tip', + TransactionByHash: 'Transaction', + TrieNode: 'TrieNode', + TrieRootByChunkHash: 'TrieRoot', + TrieRootByStateRoot: 'TrieRoot', +}; diff --git a/tools/debug-ui/src/index.css b/tools/debug-ui/src/index.css new file mode 100644 index 000000000..d74d8add2 --- /dev/null +++ b/tools/debug-ui/src/index.css @@ -0,0 +1,11 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', + 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; +} diff --git a/tools/debug-ui/src/index.tsx b/tools/debug-ui/src/index.tsx new file mode 100644 index 000000000..2508b48ac --- /dev/null +++ b/tools/debug-ui/src/index.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom/client'; +import './index.css'; +import 'react-tooltip/dist/react-tooltip.css'; +import '@patternfly/react-core/dist/styles/base.css'; +import { createBrowserRouter, RouterProvider } from 'react-router-dom'; +import { QueryClient, QueryClientProvider } from 'react-query'; +import { App } from './App'; +import { LogVisualizer } from './log_visualizer/LogVisualizer'; +import { LandingPage } from './LandingPage'; + +const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); + +const queryClient = new QueryClient(); + +const router = createBrowserRouter([ + { + path: '/', + element: , + }, + { + path: '/logviz', + element: , + }, + { + path: '/:addr/*', + element: , + }, +]); + +root.render( + + + + + +); diff --git a/tools/debug-ui/src/log_visualizer/LogVisualizer.scss b/tools/debug-ui/src/log_visualizer/LogVisualizer.scss new file mode 100644 index 000000000..d5ab6b94e --- /dev/null +++ b/tools/debug-ui/src/log_visualizer/LogVisualizer.scss @@ -0,0 +1,162 @@ +.log-visualizer { + padding: 10px; + + .visualizer-content { + position: relative; + background-color: white; + + .timestamp-header { + position: absolute; + text-align: left; + border-top: 1px solid lightgray; + color: gray; + font-size: 20px; + height: 20px; + } + + .column-header { + position: absolute; + text-align: center; + } + + .column-background { + position: absolute; + border-left: 1px solid lightgray; + } + + .event { + position: absolute; + border: 1px solid gray; + box-sizing: border-box; + display: flex; + align-items: center; + box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.5); + background-color: #eee; + padding: 4px; + z-index: 10; + + &.selected { + border: 1px solid green; + box-shadow: 0 0 2px 0 rgba(0, 255, 0, 0.5); + background-color: #cfc; + } + + .content { + overflow: hidden; + + .title { + font-size: 10px; + color: gray; + } + + .subtitle { + font-size: 12px; + text-overflow: ellipsis; + overflow: hidden; + } + } + } + + .attached-event { + position: absolute; + margin-top: -9px; + margin-left: -7px; + width: 14px; + height: 14px; + border: 1px solid gray; + background-color: white; + box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.3); + transform: rotate(45deg); + z-index: 9; + + &.selected { + border: 1px solid green; + box-shadow: 0 0 2px 0 rgba(0, 255, 0, 0.5); + background-color: #cfc; + } + } + + .line { + position: absolute; + box-sizing: content-box; + margin-left: -0.5px; + margin-top: -0.5px; + padding: 0.5px; + background-color: gray; + z-index: 1; + + &.selected { + --selection-color: green; + } + &.child-selected { + --selection-color: blue; + } + + &.selected, + &.child-selected { + margin-left: -1px; + margin-top: -1px; + padding: 1px; + background-color: var(--selection-color); + z-index: 2; + } + + &.arrow { + &:after { + content: ''; + position: absolute; + top: -1.5px; + right: 0; + display: inline-block !important; + width: 0; + height: 0; + border-left: 4px solid gray; + border-top: 2px solid transparent; + border-bottom: 2px solid transparent; + vertical-align: middle; + } + + &.selected, + &.child-selected { + &:after { + border-left: 6px solid var(--selection-color); + border-top: 3px solid white; + border-bottom: 3px solid white; + top: -2px; + } + } + } + } + } + + .log-view { + position: fixed; + right: 0; + top: 0; + bottom: 0; + width: 800px; + height: 100vh; + display: flex; + flex-direction: column; + align-items: stretch; + z-index: 20; + + > * { + flex: 1; + font-size: 12px; + } + } + + .log-view-header { + padding: 8px; + padding-left: 16px; + background-color: black; + color: white; + border-bottom: 1px solid rgb(137, 137, 137); + font-weight: bold; + } +} + +:root { + --pf-global--FontSize--sm: 12px; +} diff --git a/tools/debug-ui/src/log_visualizer/LogVisualizer.tsx b/tools/debug-ui/src/log_visualizer/LogVisualizer.tsx new file mode 100644 index 000000000..adff8b971 --- /dev/null +++ b/tools/debug-ui/src/log_visualizer/LogVisualizer.tsx @@ -0,0 +1,248 @@ +import React, { useMemo, useState } from 'react'; +import { LogViewer } from '@patternfly/react-log-viewer'; +import { LogFileDrop } from '../LogFileDrop'; +import './LogVisualizer.scss'; +import { prettyPrint } from '../pretty-print'; +import { EventItemCollection } from './events'; +import { Layouts, SizesConfig } from './layout'; +import { ArrowColumn, ArrowGroup, makeOutgoingArrowsForItem } from './arrows'; + +const LAYOUT_SIZES: SizesConfig = { + arrowSpacing: 8, + horizontalMargin: 36, + verticalMargin: 24, + itemHeight: 40, + itemWidth: 200, + newTimestampMargin: 8, +}; + +// Visualizes the log output of a Rust test written in the TestLoop framework. +// See core/async/src/test_loop.rs about the framework itself. +export const LogVisualizer = () => { + const [logLines, setLogLines] = useState([]); + const [selectedEventId, setSelectedEventId] = useState(null); + + const { events, arrowGroups, layouts } = useMemo(() => { + const events = EventItemCollection.parseFromLogLines(logLines); + const layouts = new Layouts(LAYOUT_SIZES, events); + const arrowGroups = [] as ArrowGroup[]; + for (const event of events.getAllNonAttachedItems()) { + for (const arrowGroup of makeOutgoingArrowsForItem(event, events)) { + arrowGroups.push(arrowGroup); + } + } + layouts.layoutWithArrows(arrowGroups); + return { events, arrowGroups, layouts }; + }, [logLines]); + + return ( +
+ {logLines.length === 0 && } +
setSelectedEventId(null)} + style={{ + height: layouts.totalHeight, + // Leave an extra 800px blank space for the log viewer. + width: layouts.totalWidth + 800, + }}> + {/* Render a background div for each column. */} +
+ {layouts.columns.map((_, i) => { + return ( +
+ ); + })} +
+ {/* Render the column headers. */} + {layouts.columns.map((_, i) => { + return ( +
+ Node {i} +
+ ); + })} + {/* Render the row timestamp headers. */} + {layouts.rows.map((row, i) => { + if (row.isNewTimestamp) { + return ( +
+ {row.time} ms +
+ ); + } + })} + + {/* Render all the arrows. */} + {arrowGroups.map((arrowGroup, arrowGroupId) => { + // Highlight outgoing arrows of the selected event. + const selected = + selectedEventId === arrowGroup.sourceEventId || + (arrowGroup.throughAttachedEventId !== null && + selectedEventId === arrowGroup.throughAttachedEventId); + // Also highlight inbound arrows of the selected event. + const childSelected = + selectedEventId !== null && arrowGroup.targetEventIds.has(selectedEventId); + return ( + + {arrowGroup.horizontalParts.map((part, i) => { + const y = layouts.getArrowRowYOffset(part.row, arrowGroupId); + const x1 = layouts.getArrowColumnXOffset( + part.fromColumn, + arrowGroupId + ); + const x2 = part.isArrow + ? layouts.getItemXOffset(part.toColumn.gridColumn) + : layouts.getArrowColumnXOffset(part.toColumn, arrowGroupId); + return drawLine( + { x: x1, y }, + { x: x2, y }, + '' + i, + selected, + childSelected, + part.isArrow + ); + })} + {arrowGroup.verticalParts.map((part, i) => { + const x = layouts.getArrowColumnXOffset(part.column, arrowGroupId); + const y1 = layouts.getArrowRowYOffset(part.fromRow, arrowGroupId); + const y2 = layouts.getArrowRowYOffset(part.toRow, arrowGroupId); + return drawLine( + { x, y: y1 }, + { x, y: y2 }, + '' + i, + selected, + childSelected, + false + ); + })} + {arrowGroup.throughAttachedEventId !== null && + (() => { + // Draw attachment here, since their positioning depends on + // the arrow group. + const event = events.get(arrowGroup.throughAttachedEventId)!; + const parent = events.get(event.parentId!)!; + return ( +
{ + setSelectedEventId(event.id); + e.stopPropagation(); + }}>
+ ); + })()} +
+ ); + })} + + {/* Render all the events (other than attachments). */} + {events.getAllNonAttachedItems().map((event) => { + return ( +
{ + setSelectedEventId(event.id); + e.stopPropagation(); + }}> +
+
{event.title}
+ {event.subtitle &&
{event.subtitle}
} +
+
+ ); + })} +
+ + {/* Render the log viewer; this shows the log messages emitted during the handling + of the event, as well as the detailed event message dump. */} + {selectedEventId !== null && ( +
+ Logs for this event
} + /> + Event details
} + /> + + )} + + ); +}; + +function drawLine( + from: { x: number; y: number }, + to: { x: number; y: number }, + key: string, + selected: boolean, + childSelected: boolean, + arrow: boolean +): JSX.Element { + let className = 'line'; + if (selected) { + className += ' selected'; + } + if (childSelected) { + className += ' child-selected'; + } + if (arrow) { + className += ' arrow'; + } + return ( +
+ ); +} diff --git a/tools/debug-ui/src/log_visualizer/arrows.ts b/tools/debug-ui/src/log_visualizer/arrows.ts new file mode 100644 index 000000000..28c73168a --- /dev/null +++ b/tools/debug-ui/src/log_visualizer/arrows.ts @@ -0,0 +1,325 @@ +import { EventItem, EventItemCollection } from './events'; + +// Arrows in the log visualizer are axes-parallel. They are drawn in one of two +// fashions: +// +// +--------------+ +--------------+ +--------------+ +// | Source | | Source | | | +// +---| Event | | Event | | | +// | +--------------+ +--------------+ +--------------+ +// | | +// | +--------------+ +--------------+ +// | | | OR | +// | | | +--------------+ | +--------------+ +// | +--------------+ | | | | | +// | | | | | | +// | +--------------+ +--------------+ | +--------------+ +// +-->| Target | | +// | Event | +--------------+ | +--------------+ +// +--------------+ | | +------>| Target | +// | | | Event | +// +--------------+ +--------------+ +// +// The one on the left is used for events that spawn children events only in +// the same instance, whereas the one on the right is used for events that +// spawn events into different instances (such as a network message). +// +// Additionally, an arrow of the second fashion may be drawn out of an +// attachment, like this: +// +// +------------------+ +// | Parent Event | +// +------------------+ +// \/ <-- attachment +// | +// | +// +--------------...> +// +// Since the arrows we draw may overlap, for each horizontal and vertical +// segment that may overlap, we compute a different offset. For example, +// if two arrows would both draw a horizontal segment at the same vertical +// position, then we would use the interval assignment algorithm to assign +// each of these two horizontal segments a different layer, so that they are +// actually drawn with different vertical positions. This layer assignment is +// carried out independently for each unique (originally desired) vertical +// position and horizontal position. The result is that the arrows are +// guaranteed to not overlap, even though they may cross each other. For +// example, +// +// -----------------+ \ +// | | (ArrowRow 1) +// -----------------|---+ / +// | | +// | | +// | | +// | | +// +---|--------------> (arrow A) \ +// | | (ArrowRow 2) +// +-----------------> (arrow B) / +// +// \___/ +// (ArrowColumn 1) +// +// Suppose two arrows both want to draw the following: +// - horizontal line on (ArrowRow 1) from (ArrowColumn 0) to (ArrowColumn 1) +// - vertical line on (ArrowColumn 1) from (ArrowRow 1) to (ArrowRow 2) +// - horizontal line on (ArrowRow 2) frmo (ArrowColumn 1) to (ArrowColumn 2) +// +// then since both arrows draw horizontal lines on ArrowRow1 with overlapping +// intervals, we assign them a different offset (layer) so that they are in +// fact drawn at different vertical positions. Same goes with the other two +// segments. +// +// To do this, each arrow we draw is represented by a set of ArrowParts, called +// an ArrowGroup. For each unique vertical position, we collect the horizontal +// ArrowParts (assume at most one per ArrowGroup), and use the interval +// scheduling algorithm to assign layers to them (in fact, to each ArrowGroup). +// We do the same for vertical ArrowParts for each unique horizontal position. +// Then, when rendering, we would look up the layer for each ArrowPart and +// draw them with the appropriate offsets. +// +// A unique vertical position where a horizontal arrow may be drawn is called +// an ArrowRow. A unique horizontal position where a vertical arrow may be +// drawn is called an ArrowColumn. + +// All possible ArrowRow positions for each item (event) row. +// - 'above' means above the boxes rendered for the items in that row. +// - 'inbound' means 1/3 of the way down from the top of the item box. +// - 'outbound' means 2/3 of the way down from the top of the item box. +export type ArrowRowPosition = 'above' | 'inbound' | 'outbound'; +export const ARROW_ROW_POSITIONS: ArrowRowPosition[] = ['above', 'inbound', 'outbound']; + +export class ArrowRow { + constructor(public readonly gridRow: number, public readonly positioning: ArrowRowPosition) {} + + // Gets a unique key for this ArrowRow that is larger if it's more + // vertically down. + get key(): number { + return ( + this.gridRow * 3 + + (this.positioning == 'above' ? 0 : this.positioning == 'inbound' ? 1 : 2) + ); + } + + max(other: ArrowRow): ArrowRow { + return this.key > other.key ? this : other; + } + + min(other: ArrowRow): ArrowRow { + return this.key < other.key ? this : other; + } +} + +// All possible ArrowColumn positions for each item (event) column. +// - 'left' means left of the boxes rendered for the items in that column. +// - 'middle' means the vertical middle of the item box. +export type ArrowColumnPosition = 'left' | 'middle'; +export const ARROW_COLUMN_POSITIONS: ArrowColumnPosition[] = ['left', 'middle']; + +export class ArrowColumn { + constructor( + public readonly gridColumn: number, + public readonly positioning: ArrowColumnPosition + ) {} + + get key(): number { + return this.gridColumn * 2 + (this.positioning == 'left' ? 0 : 1); + } + + max(other: ArrowColumn): ArrowColumn { + return this.key > other.key ? this : other; + } + + min(other: ArrowColumn): ArrowColumn { + return this.key < other.key ? this : other; + } +} + +// A segment of an arrow that is horizontally drawn. +export class ArrowPartHorizontal { + constructor( + // The column where the arrow starts. + public fromColumn: ArrowColumn, + // The column where the arrow ends. + public toColumn: ArrowColumn, + // The row where the arrow is drawn. + public row: ArrowRow, + // If true, draw an arrow head at the right end of the segment. + public isArrow: boolean + ) { + if (fromColumn.key >= toColumn.key) { + throw new Error('fromColumn must be before toColumn'); + } + } + + // A horizontal arrow going into an item from the left. + public static intoItem(gridRow: number, gridColumn: number): ArrowPartHorizontal { + return new ArrowPartHorizontal( + new ArrowColumn(gridColumn, 'left'), + new ArrowColumn(gridColumn, 'middle'), + new ArrowRow(gridRow, 'inbound'), + true + ); + } + + // A horizontal segment going out of an item on the left. + public static outOfItem(gridRow: number, gridColumn: number): ArrowPartHorizontal { + return new ArrowPartHorizontal( + new ArrowColumn(gridColumn, 'left'), + new ArrowColumn(gridColumn, 'middle'), + new ArrowRow(gridRow, 'outbound'), + false + ); + } + + // A horizontal segment going across columns along the horizontal margin + // between items. + public static acrossColumns( + gridRow: number, + fromColumn: ArrowColumn, + toColumn: ArrowColumn + ): ArrowPartHorizontal { + return new ArrowPartHorizontal(fromColumn, toColumn, new ArrowRow(gridRow, 'above'), false); + } +} + +// A segment of an arrow that is vertically drawn. +export class ArrowPartVertical { + constructor( + // The column where the arrow is drawn. + public column: ArrowColumn, + // The row where the arrow starts. + public fromRow: ArrowRow, + // The row where the arrow ends. + public toRow: ArrowRow + ) { + if (fromRow.key >= toRow.key) { + throw new Error('fromRow must be before toRow'); + } + } + + // A vertical arrow going out of an item towards the bottom. + public static outOfItem(gridRow: number, gridColumn: number): ArrowPartVertical { + return new ArrowPartVertical( + new ArrowColumn(gridColumn, 'middle'), + new ArrowRow(gridRow, 'outbound'), + new ArrowRow(gridRow + 1, 'above') + ); + } + + // A vertical arrow going across rows along the vertical margin between + // items. + public static acrossRows(fromRow: ArrowRow, toRow: ArrowRow, gridColumn: number) { + return new ArrowPartVertical(new ArrowColumn(gridColumn, 'left'), fromRow, toRow); + } +} + +// A group of ArrowParts, representing a single set of arrows. +// These arrows may point to multiple targets, but we draw them together so +// that their layer assignments are consistent, e.g. the vertical line in +// the below example should be a single line, not 3 separate lines. +// +// source +// | +// +------> target1 +// | +// +------> target2 +// | +// +------> target3 +export type ArrowGroup = { + // The source event that the arrows originate from. + sourceEventId: number; + // If present, the attachment of the event the arrows come from. + throughAttachedEventId: number | null; + // The target events that the arrows point to. + targetEventIds: Set; + + horizontalParts: ArrowPartHorizontal[]; + verticalParts: ArrowPartVertical[]; +}; + +// Makes the outgoing arrows for an event. +export function makeOutgoingArrowsForItem( + item: EventItem, + items: EventItemCollection +): ArrowGroup[] { + const edges = items.getAllOutgoingEdges(item.id); + + // We'll draw one separate ArrowSet per attachment (or lack thereof). + // This is because the attachments are separately drawn, and we want the + // arrows going out of each attachment to visually come from that + // attachment. + const edgesByAttachment = new Map(); + for (const edge of edges) { + const list = edgesByAttachment.get(edge.throughAttachedId) ?? []; + list.push(edge.toId); + edgesByAttachment.set(edge.throughAttachedId, list); + } + + const arrowGroups = [] as ArrowGroup[]; + for (const [attachmentId, targetIds] of edgesByAttachment) { + // For each column we'll find the lowest point we need to draw towards. + // That way we can draw a single vertical line and multiple horizontal + // arrows. + const columnToMaxRow = new Map(); + for (const targetId of targetIds) { + const child = items.get(targetId)!; + columnToMaxRow.set( + child.column, + Math.max(columnToMaxRow.get(child.column) ?? 0, child.row) + ); + } + let minColumn = 100000, + maxColumn = 0; + for (const column of columnToMaxRow.keys()) { + minColumn = Math.min(minColumn, column); + maxColumn = Math.max(maxColumn, column); + } + const horizontalParts = [] as ArrowPartHorizontal[]; + const verticalParts = [] as ArrowPartVertical[]; + // If we only have children to the same instance, then draw them in the + // first fashion (see comment at beginning of file). + if (minColumn == maxColumn && minColumn == item.column && attachmentId === null) { + horizontalParts.push(ArrowPartHorizontal.outOfItem(item.row, item.column)); + verticalParts.push( + ArrowPartVertical.acrossRows( + new ArrowRow(item.row, 'outbound'), + new ArrowRow(columnToMaxRow.get(item.column)!, 'inbound'), + item.column + ) + ); + } else { + // Otherwise we draw them in the second fashion. + const verticalOut = ArrowPartVertical.outOfItem(item.row, item.column); + verticalParts.push(verticalOut); + horizontalParts.push( + ArrowPartHorizontal.acrossColumns( + item.row + 1, + verticalOut.column.min(new ArrowColumn(minColumn, 'left')), + verticalOut.column.max(new ArrowColumn(maxColumn, 'left')) + ) + ); + for (const column of columnToMaxRow.keys()) { + verticalParts.push( + ArrowPartVertical.acrossRows( + new ArrowRow(item.row + 1, 'above'), + new ArrowRow(columnToMaxRow.get(column)!, 'inbound'), + column + ) + ); + } + } + for (const targetId of targetIds) { + const child = items.get(targetId)!; + horizontalParts.push(ArrowPartHorizontal.intoItem(child.row, child.column)); + } + arrowGroups.push({ + horizontalParts, + verticalParts, + sourceEventId: item.id, + throughAttachedEventId: attachmentId, + targetEventIds: new Set(targetIds), + }); + } + return arrowGroups; +} diff --git a/tools/debug-ui/src/log_visualizer/events.ts b/tools/debug-ui/src/log_visualizer/events.ts new file mode 100644 index 000000000..acc03df6a --- /dev/null +++ b/tools/debug-ui/src/log_visualizer/events.ts @@ -0,0 +1,216 @@ +// A single event executed by the TestLoop framework on the Rust side. +// The events are parsed from log messages emitted by the framework. +// See core/async/src/test_loop.rs for the Rust side code. +export class EventItem { + // The ID of the event, a number that increments for each event created, + // emitted from the Rust side. + public readonly id: number; + // The virtual timestamp this event was executed at. Emitted from the Rust + // side. + public readonly time: number; + // Which instance this event occurred on; in a single instance test, this + // is zero; in a multi-instance test, this is the instance number. + // Emitted from the Rust side as part of the event dump (like "(0, Event)") + // and parsed here. + public readonly column: number; + // The title we display for the event. Parsed from the event dump after + // extracting column number; this is usually the enum variant name. + public readonly title: string; + // The subtitle we display after the title. Parsed from the event dump, + // after the column number and after the enum variant name. + public readonly subtitle: string | null; + // The full event dump; emitted from the Rust side. + public readonly data: string; + // The parent event whose handler spawned this event. See the parsing logic + // for how this is derived. + public readonly parentId: number | null = null; + + // The row of the event in the log visualizer. This is set during layout. + public row = 0; + // The children IDs of this event. Derived from parentId. + public readonly childIds: number[] = []; + // The log lines emitted by the Rust program while handling this event. + public readonly logRows: string[] = []; + + constructor(id: number, parentId: number | null, time: number, eventDump: string) { + this.id = id; + this.time = time; + this.parentId = parentId; + + // If the event dump is a tuple, the first element is the instance ID. + if (eventDump.startsWith('(')) { + const split = eventDump.indexOf(','); + this.column = parseInt(eventDump.substring(1, split)); + this.data = eventDump.substring(split + 1, eventDump.length - 1).trim(); + } else { + this.column = 0; + this.data = eventDump; + } + // Parse the title and subtitle; for example, if the event dump is + // "OutboundNetwork(NetworkRequests(...))"" + // then the title is "OutboundNetwork" and the subtitle is + // "NetworkRequests". + const firstParens = this.data.indexOf('('); + if (firstParens !== -1) { + this.title = this.data.substring(0, firstParens); + const afterFirstParens = this.data.substring(firstParens + 1, this.data.length - 1); + const secondBracket = /[({]/.exec(afterFirstParens); + if (secondBracket !== null) { + this.subtitle = afterFirstParens.substring(0, secondBracket.index); + } else { + this.subtitle = afterFirstParens; + } + } else { + this.title = this.data; + this.subtitle = null; + } + } + + // Attachment means that instead of rendering this event as a separate + // item on the visualizer UI, we render it as an attached item to its + // parent. This is useful for outgoing network messages that are simply + // forwarded to another node, so we don't have to draw an extra edge just + // for that. + // + // Attachment looks like this: + // + // +------------------+ + // | Parent Event | + // +------------------+ + // \/ \/ \/ <-- these are 3 attachments + // | | | + // | | | + // (arrows leading to the children of the attachments) + // + // This method just computes whether an event is desirable to be rendered + // as an attachment, but EventItemCollection.isAttachedToParent is the + // source of truth. + get isEligibleForAttachment(): boolean { + // Modify this as needed. + return this.subtitle === 'NetworkRequests'; + } +} + +// The collection of all events. +export class EventItemCollection { + private readonly items: EventItem[] = []; + private readonly idToItem: Map = new Map(); + + public get(id: number): EventItem | null { + return this.idToItem.get(id)!; + } + + // Returns whether the given event is attached to its parent. + public isAttachedToParent(id: number): boolean { + const item = this.get(id)!; + if (item.parentId === null || !item.isEligibleForAttachment) { + return false; + } + if (this.isAttachedToParent(item.parentId)) { + return false; + } + const parent = this.get(item.parentId)!; + if (parent.time != item.time) { + return false; + } + return true; + } + + // Returns all events, whether or not they are attached. + public getRawItemsIncludingAttachments(): EventItem[] { + return this.items; + } + + // Returns all events that are not attached. + public getAllNonAttachedItems(): EventItem[] { + return this.items.filter((item) => !this.isAttachedToParent(item.id)); + } + + // Returns all events to which we should draw an outgoing arrow from this + // event. This includes all children, as well as all children of any + // attachments. + public getAllOutgoingEdges(id: number): OutgoingEdge[] { + const children: OutgoingEdge[] = []; + for (const childId of this.get(id)!.childIds) { + if (this.isAttachedToParent(childId)) { + for (const grandchildId of this.get(childId)!.childIds) { + children.push({ + throughAttachedId: childId, + toId: grandchildId, + }); + } + } else { + children.push({ + throughAttachedId: null, + toId: childId, + }); + } + } + return children; + } + + private add(item: EventItem) { + this.items.push(item); + this.idToItem.set(item.id, item); + if (item.parentId !== null) { + this.idToItem.get(item.parentId)!.childIds.push(item.id); + } + } + + private lastEvent(): EventItem | null { + return this.items.length == 0 ? null : this.items[this.items.length - 1]; + } + + // Parses the events from a log file emitted from Rust. + public static parseFromLogLines(lines: string[]): EventItemCollection { + const items = new EventItemCollection(); + const parentIds = [] as (number | null)[]; + let totalEventCount = 0; + for (const line of lines) { + // This marker marks the beginning of handling a TestLoop event. + // Search for this string on the Rust side to see the logic that + // emitted these markers. Lines after this marker and before + // the next marker are log messages that are emitted while handling + // this event. + const marker = 'TEST_LOOP_EVENT_START '; + const index = line.indexOf(marker); + if (index == -1) { + items.lastEvent()?.logRows?.push(line); + continue; + } + type LogLineData = { + current_index: number; + total_events: number; + current_event: string; + current_time_ms: number; + }; + const parsed = JSON.parse(line.substring(index + marker.length)) as LogLineData; + // Any event IDs between the previously seen total_events count and + // the currently reported total_events count are events that were + // emitted by the handling of the previous event. Therefore, the + // previous event is the parent of all these new IDs. + for (let i = totalEventCount; i < parsed.total_events; i++) { + parentIds[i] = items.lastEvent()?.id ?? null; + } + totalEventCount = parsed.total_events; + const event = new EventItem( + parsed.current_index, + parentIds[parsed.current_index], + parsed.current_time_ms, + parsed.current_event + ); + items.add(event); + } + return items; + } +} + +// An edge from one event (implicit) to another, that we should render on the +// UI. +export type OutgoingEdge = { + // The attachment event that actually is the parent of the toId event, + // if present. + throughAttachedId: number | null; + // The child event. + toId: number; +}; diff --git a/tools/debug-ui/src/log_visualizer/interval.ts b/tools/debug-ui/src/log_visualizer/interval.ts new file mode 100644 index 000000000..f3a768048 --- /dev/null +++ b/tools/debug-ui/src/log_visualizer/interval.ts @@ -0,0 +1,39 @@ +export type Interval = { + // Start and end are arbitrary numbers, we only require start < end. + start: number; + end: number; + + // The layer assigned by assignLayerToOverlappingIntervals. + assignedLayer: number; + + // The ArrowGroup that this interval belongs to (user data). + arrowGroupId: number; +}; + +// Classic interval scheduling problem. Given a set of intervals, +// assign each interval to a layer such that no two intervals +// on the same layer overlap, and that the number of layers +// used is minimized. +// +// The layer is assigned to the assignedLayer field of each given +// interval. The number of total layers is returned. Two intervals are +// considered to overlap even if only their endpoints touch. +export function assignLayerToOverlappingIntervals(intervals: Interval[]): number { + intervals.sort((a, b) => a.start - b.start); + const activeIntervals = [] as (Interval | null)[]; + for (const interval of intervals) { + for (let i = 0; i < activeIntervals.length + 1; i++) { + if (i >= activeIntervals.length) { + interval.assignedLayer = i; + activeIntervals.push(interval); + break; + } + if (activeIntervals[i] === null || activeIntervals[i]!.end < interval.start) { + interval.assignedLayer = i; + activeIntervals[i] = interval; + break; + } + } + } + return activeIntervals.length; +} diff --git a/tools/debug-ui/src/log_visualizer/layout.ts b/tools/debug-ui/src/log_visualizer/layout.ts new file mode 100644 index 000000000..1b793ddc7 --- /dev/null +++ b/tools/debug-ui/src/log_visualizer/layout.ts @@ -0,0 +1,305 @@ +import { + ArrowColumn, + ArrowColumnPosition, + ArrowGroup, + ArrowRow, + ArrowRowPosition, + ARROW_COLUMN_POSITIONS, + ARROW_ROW_POSITIONS, +} from './arrows'; +import { EventItemCollection } from './events'; +import { assignLayerToOverlappingIntervals, Interval } from './interval'; + +// Layout logic for events and arrows. +// +// See arrows.ts file-level comment for how the arrows are laid out. +// +// For events, we display them in a grid: +// - Horizontally, each column represents an instance in a multi-instance test. +// - Vertically, the events are first ordered by virtual timestamp, and then +// by the order of execution. A new timestamp always starts a fresh new row +// across all columns, and we display a horizontal line before that, along +// with the timestamp. + +// The layout parameters for a single row of items. +// +// | | +// | | <-- startOffset +// | | +// ------------|-|------------ <-- arrowLayout['above'].baseOffset +// ------------|-+ arrowLayout['above'].numLayers == 3 +// +-------------- +// +// +--------------------+ <-- itemOffset +// | | +// -->| | <-- arrowLayout['inbound'].baseOffset +// | Event | +// ---| | <-- arrowLayout['outbound'].baseOffset +// | | +// +--------------------+ +class OneRowLayout { + public startOffset = 0; + public itemOffset = 0; + public isNewTimestamp = false; + + public arrowLayout = new Map(); + + constructor(public readonly time: number) {} +} + +// The layout parameters for a single column of items. +// +// startOffset +// | arrowLayout['left'].baseOffset (.numLayers == 2) +// | | itemOffset +// | | | arrowLayout['middle'].baseOffset (.numLayers == 2) +// | | | | itemEndOffset +// | | | | | +// V V V V V +// | | +// | | +// | | +// | | +-----------------+ +// | | | | +// +--|--->| | +// | | Event | +// | | | +// | | | +// | +-----------------+ +// | \ / \ / +// | V V +// | | | +// | | | +class OneColumnLayout { + public startOffset = 0; + public itemOffset = 0; + public itemEndOffset = 0; + + public arrowLayout = new Map(); +} + +// The computed layout parameters for a unique vertical position of horizontal +// segments, or a unique horizontal position of vertical segments. +class ArrowLayoutStrip { + baseOffset = 0; // offset of layer 0 segments + arrowSpacing = 0; // spacing between successive layers + numLayers = 0; // number of total layers in this strip + layers = new Map(); // Map from ArrowGroup ID to layer + + constructor(numLayers: number) { + this.numLayers = numLayers; + } + + // Computes the offset of the ArrowPart in the given ArrowGroup that + // is drawn within this strip. + offsetFor(arrowGroupId: number): number { + const layer = this.layers.get(arrowGroupId) ?? 0; + return this.baseOffset + (this.numLayers - layer - 0.5) * this.arrowSpacing; + } +} + +// Various widths and margins used for the layout. +export type SizesConfig = { + itemWidth: number; + itemHeight: number; + horizontalMargin: number; // margin between columns + verticalMargin: number; // margin between rows + arrowSpacing: number; + newTimestampMargin: number; // extra margin for rows with new timestamps +}; + +// All layout parameters. +export class Layouts { + rows: OneRowLayout[] = []; + columns: OneColumnLayout[] = []; + totalHeight = 0; + totalWidth = 0; + + constructor(public sizes: SizesConfig, items: EventItemCollection) { + this.layoutItemsInGrid(items); + } + + // Sorts the items into a grid. See the file-level comment for the layout. + // this also prepares this.rows and this.columns with placeholder entries. + private layoutItemsInGrid(items: EventItemCollection) { + const sortedItems = [...items.getAllNonAttachedItems()]; + const nextRowForColumn: number[] = []; + let currentTime = 0; + for (const item of sortedItems) { + while (this.columns.length <= item.column) { + this.columns.push(new OneColumnLayout()); + nextRowForColumn.push(0); + } + } + sortedItems.sort((a, b) => a.time - b.time); + for (const item of sortedItems) { + if (item.time > currentTime) { + currentTime = item.time; + for (let i = 0; i < this.columns.length; i++) { + nextRowForColumn[i] = this.rows.length; + } + } + item.row = nextRowForColumn[item.column]++; + if (item.row >= this.rows.length) { + this.rows.push(new OneRowLayout(item.time)); + } + } + } + + // Given the arrows, computes all the layout parameters for the entire UI. + public layoutWithArrows(arrowGroups: ArrowGroup[]) { + // First, sort the arrows into intervals, by each unique horizontal and + // vertical position (ArrowRow and ArrowColumn keys). + const horizontalArrowIntervals = new Map(); + const verticalArrowIntervals = new Map(); + for (let i = 0; i < this.rows.length; i++) { + for (const positioning of ARROW_ROW_POSITIONS) { + horizontalArrowIntervals.set(new ArrowRow(i, positioning).key, []); + } + } + for (let i = 0; i < this.columns.length; i++) { + for (const positioning of ARROW_COLUMN_POSITIONS) { + verticalArrowIntervals.set(new ArrowColumn(i, positioning).key, []); + } + } + for (let i = 0; i < arrowGroups.length; i++) { + const arrowGroup = arrowGroups[i]; + for (const part of arrowGroup.horizontalParts) { + horizontalArrowIntervals.get(part.row.key)!.push({ + arrowGroupId: i, + start: part.fromColumn.key, + end: part.toColumn.key, + assignedLayer: 0, + }); + } + for (const part of arrowGroup.verticalParts) { + verticalArrowIntervals.get(part.column.key)!.push({ + arrowGroupId: i, + start: part.fromRow.key, + end: part.toRow.key, + assignedLayer: 0, + }); + } + } + + // Now, visit each row and column in order, run interval scheduling + // for the arrow segments, and compute the cumulative offsets. + let rowOffset = 32; // start with some margin for column headings + let prevRowTime = -1; + for (let i = 0; i < this.rows.length; i++) { + const rowData = this.rows[i]; + rowData.startOffset = rowOffset; + rowData.isNewTimestamp = rowData.time !== prevRowTime; + if (rowData.isNewTimestamp) { + rowOffset += this.sizes.newTimestampMargin; + } + prevRowTime = rowData.time; + + for (const positioning of ARROW_ROW_POSITIONS) { + const row = new ArrowRow(i, positioning); + const intervals = horizontalArrowIntervals.get(row.key)!; + const numLayers = assignLayerToOverlappingIntervals(intervals); + const arrowLayout = new ArrowLayoutStrip(numLayers); + rowData.arrowLayout.set(positioning, arrowLayout); + for (const interval of intervals) { + arrowLayout.layers.set(interval.arrowGroupId, interval.assignedLayer); + } + arrowLayout.baseOffset = rowOffset; + switch (positioning) { + case 'above': + arrowLayout.arrowSpacing = this.sizes.arrowSpacing; + rowOffset += numLayers * this.sizes.arrowSpacing; + rowOffset += this.sizes.verticalMargin / 2; + rowData.itemOffset = rowOffset; + rowOffset += this.sizes.itemHeight / 3; + break; + case 'inbound': + arrowLayout.arrowSpacing = 0; + rowOffset += this.sizes.itemHeight / 3; + break; + case 'outbound': + arrowLayout.arrowSpacing = 0; + rowOffset += this.sizes.itemHeight / 3; + rowOffset += this.sizes.verticalMargin / 2; + break; + } + } + } + this.totalHeight = rowOffset; + + let columnOffset = 100; // leave space for row timestamp headings + for (let i = 0; i < this.columns.length; i++) { + const columnData = this.columns[i]; + columnData.startOffset = columnOffset; + for (const positioning of ARROW_COLUMN_POSITIONS) { + const column = new ArrowColumn(i, positioning); + const intervals = verticalArrowIntervals.get(column.key)!; + const numLayers = assignLayerToOverlappingIntervals(intervals); + const arrowLayout = new ArrowLayoutStrip(numLayers); + columnData.arrowLayout.set(positioning, arrowLayout); + for (const interval of intervals) { + arrowLayout.layers.set(interval.arrowGroupId, interval.assignedLayer); + } + switch (positioning) { + case 'left': + columnOffset += this.sizes.horizontalMargin / 3; + arrowLayout.baseOffset = columnOffset; + arrowLayout.arrowSpacing = this.sizes.arrowSpacing; + columnOffset += numLayers * arrowLayout.arrowSpacing; + columnOffset += this.sizes.horizontalMargin / 3; + columnData.itemOffset = columnOffset; + break; + case 'middle': { + arrowLayout.arrowSpacing = this.sizes.arrowSpacing * 2; + const totalArrowWidth = numLayers * arrowLayout.arrowSpacing; + const itemWidth = Math.max(this.sizes.itemWidth, totalArrowWidth); + arrowLayout.baseOffset = columnOffset + (itemWidth - totalArrowWidth) / 2; + columnData.itemEndOffset = columnOffset + itemWidth; + columnOffset += itemWidth; + columnOffset += this.sizes.horizontalMargin / 3; + break; + } + } + } + } + + this.totalWidth = columnOffset; + } + + getArrowRowYOffset(row: ArrowRow, arrowGroupId: number): number { + const rowData = this.rows[row.gridRow]; + return rowData.arrowLayout.get(row.positioning)!.offsetFor(arrowGroupId); + } + + getArrowColumnXOffset(column: ArrowColumn, arrowGroupId: number): number { + const columnData = this.columns[column.gridColumn]; + return columnData.arrowLayout.get(column.positioning)!.offsetFor(arrowGroupId); + } + + getItemXOffset(column: number): number { + return this.columns[column].itemOffset; + } + + getItemYOffset(row: number): number { + return this.rows[row].itemOffset; + } + + getItemWidth(column: number): number { + return this.columns[column].itemEndOffset - this.columns[column].itemOffset; + } + + getGridRowYOffset(row: number): number { + return this.rows[row].startOffset; + } + + getGridColumnXOffset(column: number): number { + return this.columns[column].startOffset; + } + + getGridColumnWidth(column: number): number { + if (column == this.columns.length - 1) { + return this.totalWidth - this.columns[column].startOffset; + } + return this.columns[column + 1].startOffset - this.columns[column].startOffset; + } +} diff --git a/tools/debug-ui/src/pretty-print.ts b/tools/debug-ui/src/pretty-print.ts new file mode 100644 index 000000000..56542428c --- /dev/null +++ b/tools/debug-ui/src/pretty-print.ts @@ -0,0 +1,59 @@ +class IndentPrinter { + result = ''; + indentation = 0; + isBeginningOfLine = true; + + push(c: string, newlineBefore: boolean, newlineAfter: boolean) { + if (c == '\n') { + this._nextLine(); + return; + } + if (newlineBefore && !this.isBeginningOfLine) { + this._nextLine(); + } + this._appendToLine(c); + if (newlineAfter) { + this._nextLine(); + } + } + indent() { + this.indentation++; + } + unindent() { + this.indentation--; + } + _nextLine() { + this.result += '\n'; + this.isBeginningOfLine = true; + } + _appendToLine(c: string) { + if (this.isBeginningOfLine) { + if (c == ' ') { + return; + } + this.result += ' '.repeat(this.indentation); + this.isBeginningOfLine = false; + } + this.result += c; + } +} + +export function prettyPrint(s: string): string { + const printer = new IndentPrinter(); + for (let i = 0; i < s.length; i++) { + const c = s[i]; + const isClose = c === '}' || c === ']' || c === ')'; + const isOpen = c === '{' || c === '[' || c === '('; + const isComma = c === ','; + if (isClose) { + printer.unindent(); + } + const newlineBefore = isClose; + const newlineAfter = isOpen || isComma; + printer.push(c, newlineBefore, newlineAfter); + if (isOpen) { + printer.indent(); + } + } + return printer.result; +} diff --git a/tools/debug-ui/src/react-app-env.d.ts b/tools/debug-ui/src/react-app-env.d.ts new file mode 100644 index 000000000..6431bc5fc --- /dev/null +++ b/tools/debug-ui/src/react-app-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tools/debug-ui/src/utils.tsx b/tools/debug-ui/src/utils.tsx new file mode 100644 index 000000000..1176f12aa --- /dev/null +++ b/tools/debug-ui/src/utils.tsx @@ -0,0 +1,86 @@ +import { ReactElement } from 'react'; + +export function formatDurationInMillis(millis: number): string { + if (millis == null) { + return '(null)'; + } + const total_seconds = Math.floor(millis / 1000); + const hours = Math.floor(total_seconds / 3600); + const minutes = Math.floor((total_seconds - hours * 3600) / 60); + const seconds = total_seconds - hours * 3600 - minutes * 60; + if (hours > 0) { + if (minutes > 0) { + return `${hours}h ${minutes}m ${seconds}s`; + } else { + return `${hours}h ${seconds}s`; + } + } + if (minutes > 0) { + return `${minutes}m ${seconds}s`; + } + return `${seconds}s`; +} + +function formatBytesPerSecond(bytes_per_second: number): string { + if (bytes_per_second == null) { + return '-'; + } + if (bytes_per_second < 3000) { + return `${bytes_per_second} bps`; + } + const kilobytes_per_second = bytes_per_second / 1024; + if (kilobytes_per_second < 3000) { + return `${kilobytes_per_second.toFixed(1)} Kbps`; + } + const megabytes_per_second = kilobytes_per_second / 1024; + return `${megabytes_per_second.toFixed(1)} Mbps`; +} + +export function formatTraffic(bytes_received: number, bytes_sent: number): ReactElement { + return ( +
+
{'⬇ ' + formatBytesPerSecond(bytes_received)}
+
{'⬆ ' + formatBytesPerSecond(bytes_sent)}
+
+ ); +} + +export function addDebugPortLink(peer_network_addr: string): ReactElement { + // Each node running in a machine is assigned ports 24567+peer_num and 3030+peer_num, whereby peer_num is a whole number + // peer_rpc_address is not shared between peer nodes. Hence, it cannot be programmatically fetched. + // https://github.com/utnet-org/utility/blob/700ec29270f72f2e78a17029b4799a8228926c07/chain/network/src/network_protocol/peer.rs#L13-L19 + const DEFAULT_RPC_PORT = 3030; + const DEFAULT_NETWORK_PORT = 24567; + const peer_network_addr_array = peer_network_addr.split(':'); + const peer_network_port = parseInt(peer_network_addr_array.pop() || '24567'); + const peer_network_ip = peer_network_addr_array.pop() || peer_network_addr; + let peer_num = 0; + if (peer_network_ip.includes('127.0.0.1')) { + peer_num = peer_network_port - DEFAULT_NETWORK_PORT; + } + const peer_rpc_port = DEFAULT_RPC_PORT + peer_num; + const peer_rpc_address = + peer_network_addr.replace(/:.*/, ':') + peer_rpc_port.toString() + '/debug'; + return {peer_network_addr}; +} + +export function toHumanTime(seconds: number): string { + let result = ''; + if (seconds >= 60) { + let minutes = Math.floor(seconds / 60); + seconds = seconds % 60; + if (minutes > 60) { + let hours = Math.floor(minutes / 60); + minutes = minutes % 60; + if (hours > 24) { + const days = Math.floor(hours / 24); + hours = hours % 24; + result += days + ' days '; + } + result += hours + ' h '; + } + result += minutes + ' m '; + } + result += seconds + ' s'; + return result; +} diff --git a/tools/debug-ui/tsconfig.json b/tools/debug-ui/tsconfig.json new file mode 100644 index 000000000..86556e57f --- /dev/null +++ b/tools/debug-ui/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2015", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve" + }, + "include": [ + "src" + ] +} \ No newline at end of file diff --git a/tools/epoch-sync/Cargo.toml b/tools/epoch-sync/Cargo.toml new file mode 100644 index 000000000..8c12d0a06 --- /dev/null +++ b/tools/epoch-sync/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "unc-epoch-sync-tool" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +clap.workspace = true +tracing.workspace = true + +framework = { workspace = true, features = ["new_epoch_sync"] } +unc-chain.workspace = true +unc-chain-configs.workspace = true +unc-epoch-manager.workspace = true +unc-primitives.workspace = true +unc-store.workspace = true diff --git a/tools/epoch-sync/src/cli.rs b/tools/epoch-sync/src/cli.rs new file mode 100644 index 000000000..2d0bb36ed --- /dev/null +++ b/tools/epoch-sync/src/cli.rs @@ -0,0 +1,343 @@ +use anyhow::Context; +use clap; +use unc_chain::{ChainStore, ChainStoreAccess, ChainUpdate, DoomslugThresholdMode}; +use unc_epoch_manager::shard_tracker::{ShardTracker, TrackedConfig}; +use unc_epoch_manager::EpochManager; +use unc_primitives::block::BlockHeader; +use unc_primitives::borsh::BorshDeserialize; +use unc_primitives::epoch_manager::block_info::BlockInfo; +use unc_primitives::epoch_manager::AGGREGATOR_KEY; +use unc_primitives::hash::CryptoHash; +use unc_store::{checkpoint_hot_storage_and_cleanup_columns, DBCol, NodeStorage}; +use framework::NightshadeRuntime; +use std::collections::{HashMap, HashSet}; +use std::path::{Path, PathBuf}; +use unc_primitives::epoch_manager::block_summary::{BlockSummary, BlockSummaryV1}; + +#[derive(clap::Parser)] +pub struct EpochSyncCommand { + #[clap(subcommand)] + subcmd: SubCommand, +} + +#[derive(clap::Parser)] +#[clap(subcommand_required = true, arg_required_else_help = true)] +enum SubCommand { + /// For every finished epoch construct `EpochSyncInfo` + /// and validate it the same way we would if we received it from a peer. + ValidateEpochSyncInfo(ValidateEpochSyncInfoCmd), +} + +impl EpochSyncCommand { + pub fn run(self, home_dir: &Path) -> anyhow::Result<()> { + let mut unc_config = Self::create_snapshot(home_dir)?; + let storage = framework::open_storage(&home_dir, &mut unc_config)?; + + match self.subcmd { + SubCommand::ValidateEpochSyncInfo(cmd) => cmd.run(&home_dir, &storage, &unc_config), + } + } + + fn create_snapshot(home_dir: &Path) -> anyhow::Result { + let mut unc_config = framework::config::load_config( + &home_dir, + unc_chain_configs::GenesisValidationMode::UnsafeFast, + ) + .unwrap_or_else(|e| panic!("Error loading config: {e:#}")); + + let store_path_addition = unc_config + .config + .store + .path + .clone() + .unwrap_or(PathBuf::from("data")) + .join("epoch-sync-snapshot"); + let snapshot_path = home_dir.join(store_path_addition.clone()); + + let storage = framework::open_storage(&home_dir, &mut unc_config)?; + + if snapshot_path.exists() && snapshot_path.is_dir() { + tracing::info!(?snapshot_path, "Found a DB snapshot"); + } else { + tracing::info!(destination = ?snapshot_path, "Creating snapshot of original DB"); + // checkpointing only hot storage, because cold storage will not be changed + checkpoint_hot_storage_and_cleanup_columns( + &storage.get_hot_store(), + &snapshot_path, + None, + )?; + } + + unc_config.config.store.path = Some(store_path_addition.join("data")); + + Ok(unc_config) + } +} + +#[derive(clap::Parser)] +struct ValidateEpochSyncInfoCmd { + /// If `archive` flag is specified, `BlockInfo` column is assumed to be full and is used for optimisation purposes. + /// Using `BlockInfo` (`--archive` flag) takes around 10 minutes. + /// Using `BlockHeader` takes around 1.5 hours. + #[clap(short, long)] + archive: bool, +} + +impl ValidateEpochSyncInfoCmd { + pub fn run( + &self, + home_dir: &Path, + storage: &NodeStorage, + config: &framework::config::UncConfig, + ) -> anyhow::Result<()> { + let store = storage.get_hot_store(); + + let hash_to_prev_hash = if self.archive { + get_hash_to_prev_hash_from_block_info(storage)? + } else { + get_hash_to_prev_hash_from_block_header(storage)? + }; + let epoch_ids = get_all_epoch_ids(storage)?; + + let mut chain_store = + ChainStore::new(store.clone(), config.genesis.config.genesis_height, false); + let header_head_hash = chain_store.header_head()?.last_block_hash; + let hash_to_next_hash = get_hash_to_next_hash(&hash_to_prev_hash, &header_head_hash)?; + + let epoch_manager = + EpochManager::new_arc_handle(storage.get_hot_store(), &config.genesis.config); + let shard_tracker = ShardTracker::new( + TrackedConfig::from_config(&config.client_config), + epoch_manager.clone(), + ); + let runtime = NightshadeRuntime::from_config( + home_dir, + storage.get_hot_store(), + &config, + epoch_manager.clone(), + ); + let chain_update = ChainUpdate::new( + &mut chain_store, + epoch_manager, + shard_tracker, + runtime, + DoomslugThresholdMode::TwoThirds, + config.genesis.config.transaction_validity_period, + ); + + let genesis_hash = store + .get_ser::( + DBCol::BlockHeight, + &config.genesis.config.genesis_height.to_le_bytes(), + )? + .expect("Expect genesis height to be present in BlockHeight column"); + + let mut cur_hash = header_head_hash; + + // Edge case if we exactly at the epoch boundary. + // In this case we cannot create `EpochSyncInfo` for this epoch yet, + // as there is no block header with `epoch_sync_data_hash` for that epoch. + if epoch_ids.contains(&cur_hash) { + cur_hash = hash_to_prev_hash[&cur_hash]; + } + + let mut num_errors = 0; + + while cur_hash != genesis_hash { + tracing::debug!(?cur_hash, "Big loop hash"); + + // epoch ids are the last hashes of some epochs + if epoch_ids.contains(&cur_hash) { + let last_header = store + .get_ser::(DBCol::BlockHeader, cur_hash.as_ref())? + .context("BlockHeader for cur_hash not found")?; + let last_finalized_height = + if *last_header.last_final_block() == CryptoHash::default() { + 0 + } else { + let last_finalized_header = store + .get_ser::( + DBCol::BlockHeader, + last_header.last_final_block().as_ref(), + )? + .context("BlockHeader for cur_hash.last_final_block not found")?; + last_finalized_header.height() + }; + + loop { + let prev_hash = hash_to_prev_hash[&cur_hash]; + if epoch_ids.contains(&prev_hash) { + // prev_hash is the end of previous epoch + // cur_hash is the start of current epoch + break; + } else { + // prev_hash is still in the current epoch + // we descent to it + cur_hash = prev_hash; + } + } + + let first_block_hash = cur_hash; + let BlockSummary::V1(BlockSummaryV1{ + random_value:_random_value, + validators, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + fishermen, + fishermen_to_index, + power_change, + frozen_change, + validator_reward, + seat_price, + minted_amount, + all_power_proposals, + all_frozen_proposals, + validator_kickout, + validator_mandates, .. + }) = BlockSummary::default(); + let mut last_block_info = BlockInfo::new( + *last_header.hash(), + last_header.height(), + last_finalized_height, + *last_header.last_final_block(), + *last_header.prev_hash(), + last_header.prev_validator_power_proposals().collect(), + last_header.prev_validator_frozen_proposals().collect(), + last_header.chunk_mask().to_vec(), + vec![], + last_header.total_supply(), + last_header.latest_protocol_version(), + last_header.raw_timestamp(), + // start customized by James Savechives + *last_header.random_value(), + validators, + validator_to_index, + block_producers_settlement, + chunk_producers_settlement, + fishermen, + fishermen_to_index, + power_change, + frozen_change, + validator_reward, + seat_price, + minted_amount, + all_power_proposals, + all_frozen_proposals, + validator_kickout, + validator_mandates + // end customized by James Savechives + ); + + *last_block_info.epoch_id_mut() = last_header.epoch_id().clone(); + *last_block_info.epoch_first_block_mut() = first_block_hash; + + let next_epoch_first_hash = hash_to_next_hash[last_header.hash()]; + tracing::debug!("Creating EpochSyncInfo from block {:?}", last_header); + + let epoch_sync_info = chain_update.create_epoch_sync_info( + &last_block_info, + &next_epoch_first_hash, + Some(&hash_to_prev_hash), + )?; + + let calculated_epoch_sync_data_hash_result = + epoch_sync_info.calculate_epoch_sync_data_hash(); + let canonical_epoch_sync_data_hash_result = + epoch_sync_info.get_epoch_sync_data_hash(); + + if let (Ok(calculated), Ok(Some(canonical))) = ( + &calculated_epoch_sync_data_hash_result, + &canonical_epoch_sync_data_hash_result, + ) { + if calculated == canonical { + tracing::info!( + "EpochSyncInfo for height {:?} OK", + epoch_sync_info.epoch_info.epoch_height() + ); + continue; + } + } + tracing::error!( + "EpochSyncInfo for height {:?} ERROR {:?} {:?}", + epoch_sync_info.epoch_info.epoch_height(), + calculated_epoch_sync_data_hash_result, + canonical_epoch_sync_data_hash_result + ); + num_errors += 1; + } else { + cur_hash = hash_to_prev_hash[&cur_hash]; + } + } + assert_eq!(num_errors, 0); + Ok(()) + } +} + +/// Creates mapping from `cur_hash` to `prev_hash` by iterating through `BlockInfo` column. +/// Mapping from `cur_hash` to `prev_hash` is unique, as there are no two blocks with the same hash. +/// This means there will not be key collision. Value collision may happen, but it is irrelevant for `HashMap`. +fn get_hash_to_prev_hash_from_block_info( + storage: &NodeStorage, +) -> anyhow::Result> { + let mut hash_to_prev_hash = HashMap::new(); + let store = storage.get_split_store().unwrap_or(storage.get_hot_store()); + for result in store.iter(DBCol::BlockInfo) { + let (_, value) = result?; + let block_info = + BlockInfo::try_from_slice(value.as_ref()).expect("Failed to deser BlockInfo"); + if block_info.hash() != block_info.prev_hash() { + hash_to_prev_hash.insert(*block_info.hash(), *block_info.prev_hash()); + } + } + Ok(hash_to_prev_hash) +} + +/// Creates mapping from `cur_hash` to `prev_hash` by iterating through `BlockHeader` column. +/// Mapping from `cur_hash` to `prev_hash` is unique, as there are no two blocks with the same hash. +/// This means there will not be key collision. Value collision may happen, but it is irrelevant for `HashMap`. +fn get_hash_to_prev_hash_from_block_header( + storage: &NodeStorage, +) -> anyhow::Result> { + let mut hash_to_prev_hash = HashMap::new(); + for result in storage.get_hot_store().iter(DBCol::BlockHeader) { + let (_, value) = result?; + let block_header = + BlockHeader::try_from_slice(value.as_ref()).expect("Failed to deser BlockHeader"); + if block_header.hash() != block_header.prev_hash() { + hash_to_prev_hash.insert(*block_header.hash(), *block_header.prev_hash()); + } + } + Ok(hash_to_prev_hash) +} + +/// Creates mapping from `cur_hash` to `next_hash` for the chain ending in `chain_head` +/// by descending through mapping from `cur_hash` to `prev_hash`. +/// Only builds mapping for one chain to avoid key collision due to forks. +fn get_hash_to_next_hash( + hash_to_prev_hash: &HashMap, + chain_head: &CryptoHash, +) -> anyhow::Result> { + let mut hash_to_next_hash = HashMap::new(); + let mut cur_head = *chain_head; + while let Some(prev_hash) = hash_to_prev_hash.get(&cur_head) { + hash_to_next_hash.insert(*prev_hash, cur_head); + cur_head = *prev_hash; + } + Ok(hash_to_next_hash) +} + +/// Get all `EpochId`s by iterating `EpochInfo` column and return them as `HashSet`. +/// This function is used to get hashes of all last epoch blocks as `EpochId` represents last hash of prev prev column. +fn get_all_epoch_ids(storage: &NodeStorage) -> anyhow::Result> { + let mut epoch_ids = HashSet::new(); + for result in storage.get_hot_store().iter(DBCol::EpochInfo) { + let (key, _) = result?; + if key.as_ref() == AGGREGATOR_KEY { + continue; + } + epoch_ids + .insert(CryptoHash::try_from_slice(key.as_ref()).expect("Failed to deser CryptoHash")); + } + Ok(epoch_ids) +} diff --git a/tools/epoch-sync/src/lib.rs b/tools/epoch-sync/src/lib.rs new file mode 100644 index 000000000..8a0197d94 --- /dev/null +++ b/tools/epoch-sync/src/lib.rs @@ -0,0 +1,2 @@ +pub mod cli; +pub use cli::EpochSyncCommand; diff --git a/tools/flat-storage/Cargo.toml b/tools/flat-storage/Cargo.toml new file mode 100644 index 000000000..3c53016bf --- /dev/null +++ b/tools/flat-storage/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "unc-flat-storage" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +borsh.workspace = true +clap.workspace = true +rayon.workspace = true +tqdm.workspace = true +tracing.workspace = true + +unc-chain.workspace = true +unc-chain-configs.workspace = true +unc-epoch-manager.workspace = true +unc-primitives.workspace = true +unc-store.workspace = true +framework.workspace = true diff --git a/tools/flat-storage/README.md b/tools/flat-storage/README.md new file mode 100644 index 000000000..3d484711e --- /dev/null +++ b/tools/flat-storage/README.md @@ -0,0 +1 @@ +Set of tools for debugging and experimenting with flat storage. \ No newline at end of file diff --git a/tools/flat-storage/src/commands.rs b/tools/flat-storage/src/commands.rs new file mode 100644 index 000000000..8dbfdc675 --- /dev/null +++ b/tools/flat-storage/src/commands.rs @@ -0,0 +1,452 @@ +/// Tools for modifying flat storage - should be used only for experimentation & debugging. +use borsh::BorshDeserialize; +use clap::Parser; +use unc_chain::flat_storage_creator::FlatStorageShardCreator; +use unc_chain::types::RuntimeAdapter; +use unc_chain::{ChainStore, ChainStoreAccess}; +use unc_chain_configs::GenesisValidationMode; +use unc_epoch_manager::{EpochManager, EpochManagerAdapter, EpochManagerHandle}; +use unc_primitives::shard_layout::ShardVersion; +use unc_primitives::types::{BlockHeight, ShardId}; +use unc_store::flat::{ + inline_flat_state_values, store_helper, FlatStateDelta, FlatStateDeltaMetadata, + FlatStorageManager, FlatStorageStatus, +}; +use unc_store::{DBCol, Mode, NodeStorage, ShardUId, Store, StoreOpener}; +use framework::{load_config, UncConfig, NightshadeRuntime}; +use std::sync::atomic::AtomicBool; +use std::{path::PathBuf, sync::Arc, time::Duration}; +use tqdm::tqdm; + +#[derive(Parser)] +pub struct FlatStorageCommand { + #[clap(subcommand)] + subcmd: SubCommand, +} + +#[derive(Parser)] +#[clap(subcommand_required = true, arg_required_else_help = true)] +enum SubCommand { + /// View the current state of flat storage + View(ViewCmd), + + /// Reset the flat storage state (remove all the contents) + Reset(ResetCmd), + + /// Init the flat storage state, by copying from trie + Init(InitCmd), + + /// Verify flat storage state (it can take up to couple hours if flat storage is very large) + Verify(VerifyCmd), + + /// Temporary command to set the store version (useful as long flat + /// storage is enabled only during nightly with separate DB version). + SetStoreVersion(SetStoreVersionCmd), + + /// Run FlatState value inininig migration + MigrateValueInlining(MigrateValueInliningCmd), + + /// Construct and store trie in a separate directory from flat storage state for a given shard. + /// The trie is constructed for the block height equal to flat_head + ConstructTrieFromFlat(ConstructTriedFromFlatCmd), + + /// Move flat head forward. + MoveFlatHead(MoveFlatHeadCmd), +} + +#[derive(Parser)] +pub struct ViewCmd { + #[clap(long)] + shard_id: Option, +} + +#[derive(Parser)] +pub struct ConstructTriedFromFlatCmd { + #[clap(long)] + shard_id: ShardId, + /// Path to directory where the constructed trie would be stored. Note that there shouldn't be an + /// existing DB in the path provided. + #[clap(long)] + write_store_path: PathBuf, +} + +#[derive(Parser)] +pub struct SetStoreVersionCmd { + version: u32, +} + +#[derive(Parser)] +pub struct ResetCmd { + shard_id: ShardId, +} + +#[derive(Parser)] +pub struct InitCmd { + shard_id: ShardId, + + #[clap(default_value = "3")] + num_threads: usize, +} + +#[derive(Parser)] +pub struct VerifyCmd { + shard_id: ShardId, +} + +#[derive(Parser)] +pub struct MigrateValueInliningCmd { + #[clap(default_value = "16")] + num_threads: usize, + + #[clap(default_value = "50000")] + batch_size: usize, +} + +#[derive(Parser)] +pub struct MoveFlatHeadCmd { + #[clap(long)] + shard_id: ShardId, + #[clap(long)] + version: ShardVersion, + #[clap(long)] + new_flat_head_height: BlockHeight, +} + +fn print_delta(store: &Store, shard_uid: ShardUId, metadata: FlatStateDeltaMetadata) { + let changes = + store_helper::get_delta_changes(store, shard_uid, metadata.block.hash).unwrap().unwrap(); + println!("{:?}", FlatStateDelta { metadata, changes }); +} + +fn print_deltas(store: &Store, shard_uid: ShardUId) { + let deltas_metadata = store_helper::get_all_deltas_metadata(store, shard_uid).unwrap(); + let num_deltas = deltas_metadata.len(); + println!("Deltas: {}", num_deltas); + + if num_deltas <= 10 { + for delta_metadata in deltas_metadata { + print_delta(store, shard_uid, delta_metadata); + } + } else { + let (first_deltas, last_deltas) = deltas_metadata.split_at(5); + + for delta_metadata in first_deltas { + print_delta(store, shard_uid, *delta_metadata); + } + println!("... skipped {} deltas ...", num_deltas - 10); + let (_, last_deltas) = last_deltas.split_at(last_deltas.len() - 5); + for delta_metadata in last_deltas { + print_delta(store, shard_uid, *delta_metadata); + } + } +} + +impl FlatStorageCommand { + fn get_db( + opener: &StoreOpener, + home_dir: &PathBuf, + unc_config: &UncConfig, + mode: Mode, + ) -> (NodeStorage, Arc, Arc, ChainStore, Store) { + let node_storage = opener.open_in_mode(mode).unwrap(); + let epoch_manager = + EpochManager::new_arc_handle(node_storage.get_hot_store(), &unc_config.genesis.config); + let hot_runtime = NightshadeRuntime::from_config( + home_dir, + node_storage.get_hot_store(), + &unc_config, + epoch_manager.clone(), + ); + let chain_store = ChainStore::new(node_storage.get_hot_store(), 0, false); + let hot_store = node_storage.get_hot_store(); + (node_storage, epoch_manager, hot_runtime, chain_store, hot_store) + } + + fn view( + &self, + cmd: &ViewCmd, + home_dir: &PathBuf, + unc_config: &UncConfig, + opener: StoreOpener, + ) -> anyhow::Result<()> { + let (.., hot_store) = + Self::get_db(&opener, home_dir, &unc_config, unc_store::Mode::ReadOnly); + println!("DB version: {:?}", hot_store.get_db_version()?); + for item in hot_store.iter(DBCol::FlatStorageStatus) { + let (bytes_shard_uid, status) = item?; + let shard_uid = ShardUId::try_from(bytes_shard_uid.as_ref()).unwrap(); + let status = FlatStorageStatus::try_from_slice(&status)?; + if let Some(shard_id) = cmd.shard_id { + if shard_id != shard_uid.shard_id as ShardId { + continue; + } + } + + match status { + FlatStorageStatus::Ready(ready_status) => { + println!( + "Shard: {shard_uid:?} - flat storage @{:?} ({})", + ready_status.flat_head.height, ready_status.flat_head.hash, + ); + print_deltas(&hot_store, shard_uid); + } + status => { + println!("Shard: {shard_uid:?} - no flat storage: {status:?}"); + } + } + } + Ok(()) + } + + fn set_store_version( + &self, + cmd: &SetStoreVersionCmd, + opener: StoreOpener, + ) -> anyhow::Result<()> { + let rw_storage = opener.open_in_mode(unc_store::Mode::ReadWriteExisting)?; + let rw_store = rw_storage.get_hot_store(); + println!("Setting storage DB version to: {:?}", cmd.version); + rw_store.set_db_version(cmd.version)?; + Ok(()) + } + + fn reset( + &self, + cmd: &ResetCmd, + home_dir: &PathBuf, + unc_config: &UncConfig, + opener: StoreOpener, + ) -> anyhow::Result<()> { + let (_, epoch_manager, rw_hot_runtime, rw_chain_store, store) = + Self::get_db(&opener, home_dir, &unc_config, unc_store::Mode::ReadWriteExisting); + let tip = rw_chain_store.final_head()?; + + // TODO: there should be a method that 'loads' the current flat storage state based on Storage. + let shard_uid = epoch_manager.shard_id_to_uid(cmd.shard_id, &tip.epoch_id)?; + let flat_storage_manager = rw_hot_runtime.get_flat_storage_manager(); + flat_storage_manager.create_flat_storage_for_shard(shard_uid)?; + let mut store_update = store.store_update(); + flat_storage_manager.remove_flat_storage_for_shard(shard_uid, &mut store_update)?; + store_update.commit()?; + Ok(()) + } + + fn init( + &self, + cmd: &InitCmd, + home_dir: &PathBuf, + unc_config: &UncConfig, + opener: StoreOpener, + ) -> anyhow::Result<()> { + let (_, epoch_manager, rw_hot_runtime, rw_chain_store, rw_hot_store) = + Self::get_db(&opener, home_dir, &unc_config, unc_store::Mode::ReadWriteExisting); + + let tip = rw_chain_store.final_head()?; + let shard_uid = epoch_manager.shard_id_to_uid(cmd.shard_id, &tip.epoch_id)?; + let mut creator = + FlatStorageShardCreator::new(shard_uid, tip.height - 1, epoch_manager, rw_hot_runtime); + let pool = rayon::ThreadPoolBuilder::new().num_threads(cmd.num_threads).build()?; + + loop { + let status = creator.update_status(&rw_chain_store, &pool)?; + if status { + break; + } + let current_status = store_helper::get_flat_storage_status(&rw_hot_store, shard_uid); + println!("Status: {:?}", current_status); + + std::thread::sleep(Duration::from_secs(1)); + } + + println!("Flat storage initialization finished."); + Ok(()) + } + + fn verify( + &self, + cmd: &VerifyCmd, + home_dir: &PathBuf, + unc_config: &UncConfig, + opener: StoreOpener, + ) -> anyhow::Result<()> { + let (_, epoch_manager, hot_runtime, chain_store, hot_store) = + Self::get_db(&opener, home_dir, &unc_config, unc_store::Mode::ReadOnly); + let tip = chain_store.final_head()?; + let shard_uid = epoch_manager.shard_id_to_uid(cmd.shard_id, &tip.epoch_id)?; + + let head_hash = match store_helper::get_flat_storage_status(&hot_store, shard_uid) + .expect("falied to read flat storage status") + { + FlatStorageStatus::Ready(ready_status) => ready_status.flat_head.hash, + status => { + panic!("Flat storage is not ready for shard {:?}: {status:?}", cmd.shard_id); + } + }; + let block_header = chain_store.get_block_header(&head_hash)?; + let shard_layout = epoch_manager.get_shard_layout(block_header.epoch_id())?; + + println!( + "Verifying flat storage for shard {:?} - flat head @{:?} ({:?})", + cmd.shard_id, + block_header.height(), + block_header.hash() + ); + let chunk_extra = chain_store.get_chunk_extra( + &head_hash, + &ShardUId::from_shard_id_and_layout(cmd.shard_id, &shard_layout), + )?; + + // The state root must be from AFTER applying the final block (that's why we're taking it from the chunk extra). + let state_root = chunk_extra.state_root(); + + println!("Verifying using the {:?} as state_root", state_root); + let tip = chain_store.final_head()?; + + let shard_uid = epoch_manager.shard_id_to_uid(cmd.shard_id, &tip.epoch_id)?; + hot_runtime.get_flat_storage_manager().create_flat_storage_for_shard(shard_uid)?; + + let trie = hot_runtime.get_view_trie_for_shard(cmd.shard_id, &head_hash, *state_root)?; + + let flat_state_entries_iter = + store_helper::iter_flat_state_entries(shard_uid, &hot_store, None, None); + + let trie_iter = trie.iter()?; + let mut verified = 0; + let mut success = true; + for (item_trie, item_flat) in tqdm(std::iter::zip(trie_iter, flat_state_entries_iter)) { + let item_flat = item_flat?; + let value_ref = item_flat.1.to_value_ref(); + verified += 1; + + let item_trie = item_trie?; + if item_trie.0 != *item_flat.0 { + println!( + "Different keys {:?} in trie, {:?} in flat storage. ", + item_trie.0, item_flat.0 + ); + success = false; + break; + } + if item_trie.1.len() != value_ref.length as usize { + println!( + "Different ValueRef::length for key: {:?} in trie: {:?} vs flat storage: {:?}", + item_trie.0, + item_trie.1.len(), + value_ref.length + ); + success = false; + break; + } + + if unc_primitives::hash::hash(&item_trie.1) != value_ref.hash { + println!( + "Different ValueRef::hash for key: {:?} in trie: {:?} vs flat storage: {:?}", + item_trie.0, + unc_primitives::hash::hash(&item_trie.1), + value_ref.hash + ); + success = false; + break; + } + } + if success { + println!("Success - verified {:?} nodes", verified); + } else { + println!("FAILED - on node {:?}", verified); + } + Ok(()) + } + + fn migrate_value_inlining( + &self, + cmd: &MigrateValueInliningCmd, + home_dir: &PathBuf, + unc_config: &UncConfig, + opener: StoreOpener, + ) -> anyhow::Result<()> { + let store = + Self::get_db(&opener, home_dir, &unc_config, unc_store::Mode::ReadWriteExisting).4; + let flat_storage_manager = FlatStorageManager::new(store.clone()); + inline_flat_state_values( + store, + &flat_storage_manager, + &AtomicBool::new(true), + cmd.num_threads, + cmd.batch_size, + ); + Ok(()) + } + + fn construct_trie_from_flat( + &self, + cmd: &ConstructTriedFromFlatCmd, + home_dir: &PathBuf, + unc_config: &UncConfig, + opener: StoreOpener, + ) -> anyhow::Result<()> { + let (_, epoch_manager, _, chain_store, store) = + Self::get_db(&opener, home_dir, &unc_config, unc_store::Mode::ReadWriteExisting); + + let write_opener = + NodeStorage::opener(&cmd.write_store_path, false, &unc_config.config.store, None); + let write_node_storage = write_opener.open_in_mode(Mode::Create)?; + let write_store = write_node_storage.get_hot_store(); + + let tip = chain_store.final_head()?; + let shard_uid = epoch_manager.shard_id_to_uid(cmd.shard_id, &tip.epoch_id)?; + + unc_store::trie::construct_trie_from_flat(store, write_store, shard_uid); + Ok(()) + } + + fn move_flat_head( + &self, + cmd: &MoveFlatHeadCmd, + home_dir: &PathBuf, + unc_config: &UncConfig, + opener: StoreOpener, + ) -> anyhow::Result<()> { + let (_, _, runtime, chain_store, _) = + Self::get_db(&opener, home_dir, &unc_config, unc_store::Mode::ReadWriteExisting); + + let shard_uid = ShardUId { version: cmd.version, shard_id: cmd.shard_id as u32 }; + let flat_storage_manager = runtime.get_flat_storage_manager(); + flat_storage_manager.create_flat_storage_for_shard(shard_uid)?; + let flat_storage = flat_storage_manager.get_flat_storage_for_shard(shard_uid).unwrap(); + let header = chain_store.get_block_header_by_height(cmd.new_flat_head_height)?; + println!("Header: {header:?}"); + flat_storage.update_flat_head(header.hash(), true)?; + Ok(()) + } + + pub fn run( + &self, + home_dir: &PathBuf, + genesis_validation: GenesisValidationMode, + ) -> anyhow::Result<()> { + let unc_config = load_config(home_dir, genesis_validation)?; + let opener = NodeStorage::opener( + home_dir, + unc_config.config.archive, + &unc_config.config.store, + None, + ); + + match &self.subcmd { + SubCommand::View(cmd) => self.view(cmd, home_dir, &unc_config, opener), + SubCommand::SetStoreVersion(cmd) => self.set_store_version(cmd, opener), + SubCommand::Reset(cmd) => self.reset(cmd, home_dir, &unc_config, opener), + SubCommand::Init(cmd) => self.init(cmd, home_dir, &unc_config, opener), + SubCommand::Verify(cmd) => self.verify(cmd, home_dir, &unc_config, opener), + SubCommand::MigrateValueInlining(cmd) => { + self.migrate_value_inlining(cmd, home_dir, &unc_config, opener) + } + SubCommand::ConstructTrieFromFlat(cmd) => { + self.construct_trie_from_flat(cmd, home_dir, &unc_config, opener) + } + SubCommand::MoveFlatHead(cmd) => { + self.move_flat_head(cmd, home_dir, &unc_config, opener) + } + } + } +} diff --git a/tools/flat-storage/src/lib.rs b/tools/flat-storage/src/lib.rs new file mode 100644 index 000000000..82b6da3c0 --- /dev/null +++ b/tools/flat-storage/src/lib.rs @@ -0,0 +1 @@ +pub mod commands; diff --git a/tools/fork-network/Cargo.toml b/tools/fork-network/Cargo.toml new file mode 100644 index 000000000..d53f650ed --- /dev/null +++ b/tools/fork-network/Cargo.toml @@ -0,0 +1,60 @@ +[package] +name = "unc-fork-network" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix.workspace = true +anyhow.workspace = true +chrono.workspace = true +clap.workspace = true +hex.workspace = true +rayon.workspace = true +serde.workspace = true +serde_json.workspace = true +strum.workspace = true +tracing.workspace = true + +unc-chain-configs.workspace = true +unc-chain.workspace = true +unc-crypto.workspace = true +unc-epoch-manager.workspace = true +unc-mirror.workspace = true +unc-o11y.workspace = true +unc-parameters.workspace = true +unc-primitives.workspace = true +unc-store.workspace = true +framework.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-chain/nightly", + "unc-epoch-manager/nightly", + "unc-mirror/nightly", + "unc-o11y/nightly", + "unc-parameters/nightly", + "unc-primitives/nightly", + "unc-store/nightly", + "framework/nightly", +] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-chain/nightly_protocol", + "unc-epoch-manager/nightly_protocol", + "unc-mirror/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-parameters/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", + "framework/nightly_protocol", +] diff --git a/tools/fork-network/src/cli.rs b/tools/fork-network/src/cli.rs new file mode 100644 index 000000000..4656210e8 --- /dev/null +++ b/tools/fork-network/src/cli.rs @@ -0,0 +1,879 @@ +use crate::single_shard_storage_mutator::SingleShardStorageMutator; +use crate::storage_mutator::StorageMutator; +use unc_chain::types::{RuntimeAdapter, Tip}; +use unc_chain::{ChainStore, ChainStoreAccess}; +use unc_chain_configs::{Genesis, GenesisConfig, GenesisValidationMode}; +use unc_crypto::PublicKey; +use unc_epoch_manager::{EpochManager, EpochManagerAdapter, EpochManagerHandle}; +use unc_mirror::key_mapping::{map_account, map_key}; +use unc_o11y::default_subscriber_with_opentelemetry; +use unc_o11y::env_filter::make_env_filter; +use unc_parameters::{RuntimeConfig, RuntimeConfigStore}; +use unc_primitives::account::id::AccountType; +use unc_primitives::account::{AccessKey, AccessKeyPermission, Account}; +use unc_primitives::borsh; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::Receipt; +use unc_primitives::serialize::dec_format; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::state::FlatStateValue; +use unc_primitives::state_record::StateRecord; +use unc_primitives::trie_key::col; +use unc_primitives::trie_key::trie_key_parsers::parse_account_id_from_account_key; +use unc_primitives::types::{ + AccountId, AccountInfo, Balance, BlockHeight, EpochId, NumBlocks, ShardId, StateRoot, Power, +}; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_store::db::RocksDB; +use unc_store::flat::{store_helper, BlockInfo, FlatStorageManager, FlatStorageStatus}; +use unc_store::{ + checkpoint_hot_storage_and_cleanup_columns, DBCol, Store, TrieDBStorage, TrieStorage, + FINAL_HEAD_KEY, +}; +use framework::{load_config, open_storage, UncConfig, NightshadeRuntime, UNC_BASE}; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use serde::Deserialize; +use std::collections::HashSet; +use std::fs::File; +use std::io::BufReader; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use strum::IntoEnumIterator; + +#[derive(clap::Parser)] +/// Use the following sub-commands: +/// * init +/// * amend-access-keys +/// * set-validators +/// * finalize +/// +/// If something goes wrong, use the sub-command reset and start over. +pub struct ForkNetworkCommand { + #[clap(subcommand)] + command: SubCommand, +} + +#[derive(clap::Subcommand)] +enum SubCommand { + /// Prepares the DB for doing the modifications: + /// * Makes a snapshot. + /// * Finds and persists state roots. + Init(InitCmd), + + /// Creates a DB snapshot, then + /// Updates the state to ensure every account has a full access key that is known to us. + AmendAccessKeys(AmendAccessKeysCmd), + + /// Creates a DB snapshot, then + /// Reads a list of validator accounts from a file + /// Adds validator accounts to the state + /// Creates a genesis file with the new validators. + SetValidators(SetValidatorsCmd), + + /// Drops unneeded columns. + Finalize(FinalizeCmd), + + /// Recovers from a snapshot. + /// Deletes the snapshot. + Reset(ResetCmd), +} + +#[derive(clap::Parser)] +struct InitCmd; + +#[derive(clap::Parser)] +struct FinalizeCmd; + +#[derive(clap::Parser)] +struct AmendAccessKeysCmd { + #[arg(short, long, default_value = "2000000")] + batch_size: u64, +} + +#[derive(clap::Parser)] +struct SetValidatorsCmd { + /// Path to the JSON list of [`Validator`] structs containing account id and public keys. + /// The path can be relative to `home_dir` or an absolute path. + /// Example of a valid file that sets one validator with 50k tokens: + /// [{ + /// "account_id": "validator0", + /// "public_key": "ed25519:7PGseFbWxvYVgZ89K1uTJKYoKetWs7BJtbyXDzfbAcqX", + /// "amount": 50000000000000000000000000000 + /// }] + #[arg(short, long)] + pub validators: PathBuf, + #[arg(short, long, default_value = "1000")] + pub epoch_length: NumBlocks, + #[arg(long, default_value = "-fork", allow_hyphen_values = true)] + pub chain_id_suffix: String, +} + +#[derive(clap::Parser)] +struct ResetCmd; + +#[derive(Deserialize)] +struct Validator { + account_id: AccountId, + public_key: PublicKey, + #[serde(with = "dec_format")] + amount: Option, + #[serde(with = "dec_format")] + power: Option, + #[serde(with = "dec_format")] + frozen: Option, +} + +type MakeSingleShardStorageMutatorFn = + Arc anyhow::Result + Send + Sync>; + +impl ForkNetworkCommand { + pub fn run( + self, + home_dir: &Path, + genesis_validation: GenesisValidationMode, + verbose_target: Option<&str>, + o11y_opts: &unc_o11y::Options, + ) -> anyhow::Result<()> { + // Load config and check flat storage param + let mut unc_config = load_config(home_dir, genesis_validation) + .unwrap_or_else(|e| panic!("Error loading config: {e:#}")); + + let sys = actix::System::new(); + sys.block_on(async move { + self.run_impl(&mut unc_config, verbose_target, o11y_opts, home_dir).await.unwrap(); + actix::System::current().stop(); + }); + sys.run().unwrap(); + tracing::info!("Waiting for RocksDB to gracefully shutdown"); + RocksDB::block_until_all_instances_are_dropped(); + tracing::info!("exit"); + Ok(()) + } + + async fn run_impl( + self, + unc_config: &mut UncConfig, + verbose_target: Option<&str>, + o11y_opts: &unc_o11y::Options, + home_dir: &Path, + ) -> anyhow::Result<()> { + // As we're running multiple threads, we need to initialize the logging + // system to handle logging from all threads. + let _subscriber_guard = default_subscriber_with_opentelemetry( + make_env_filter(verbose_target).unwrap(), + o11y_opts, + unc_config.client_config.chain_id.clone(), + unc_config.network_config.node_key.public_key().clone(), + None, + ) + .await + .global(); + + if !unc_config.config.store.flat_storage_creation_enabled { + panic!("Flat storage must be enabled"); + } + unc_config.config.store.state_snapshot_enabled = false; + + match &self.command { + SubCommand::Init(InitCmd) => { + self.init(unc_config, home_dir)?; + } + SubCommand::AmendAccessKeys(AmendAccessKeysCmd { batch_size }) => { + self.amend_access_keys(*batch_size, unc_config, home_dir)?; + } + SubCommand::SetValidators(SetValidatorsCmd { + validators, + epoch_length, + chain_id_suffix, + }) => { + self.set_validators( + validators, + *epoch_length, + chain_id_suffix, + unc_config, + home_dir, + )?; + } + SubCommand::Finalize(FinalizeCmd) => { + self.finalize(unc_config, home_dir)?; + } + SubCommand::Reset(ResetCmd) => { + self.reset(unc_config, home_dir)?; + } + }; + Ok(()) + } + + /// Checks if a DB snapshot exists. + /// If a snapshot doesn't exist, then creates it at `~/.near/data/fork-snapshot`. + fn snapshot_db( + &self, + store: Store, + unc_config: &UncConfig, + home_dir: &Path, + ) -> anyhow::Result { + let store_path = + home_dir.join(unc_config.config.store.path.clone().unwrap_or(PathBuf::from("data"))); + let fork_snapshot_path = store_path.join("fork-snapshot"); + + if fork_snapshot_path.exists() && fork_snapshot_path.is_dir() { + tracing::info!(?fork_snapshot_path, "Found a DB snapshot"); + Ok(false) + } else { + tracing::info!(destination = ?fork_snapshot_path, "Creating snapshot of original DB"); + // checkpointing only hot storage, because cold storage will not be changed + checkpoint_hot_storage_and_cleanup_columns(&store, &fork_snapshot_path, None)?; + Ok(true) + } + } + + // Snapshots the DB. + // Determines parameters that will be used to initialize the new chain. + // After this completes, almost every DB column can be removed, however this command doesn't delete anything itself. + fn init(&self, unc_config: &mut UncConfig, home_dir: &Path) -> anyhow::Result<()> { + // Open storage with migration + let storage = open_storage(&home_dir, unc_config).unwrap(); + let store = storage.get_hot_store(); + assert!(self.snapshot_db(store.clone(), unc_config, home_dir)?); + + let epoch_manager = + EpochManager::new_arc_handle(store.clone(), &unc_config.genesis.config); + let head = store.get_ser::(DBCol::BlockMisc, FINAL_HEAD_KEY)?.unwrap(); + let shard_layout = epoch_manager.get_shard_layout(&head.epoch_id)?; + let all_shard_uids: Vec<_> = shard_layout.shard_uids().collect(); + let num_shards = all_shard_uids.len(); + // Flat state can be at different heights for different shards. + // That is fine, we'll simply lookup state root for each . + let fork_heads = get_fork_heads(&all_shard_uids, store.clone())?; + tracing::info!(?fork_heads); + + let chain = + ChainStore::new(store.clone(), unc_config.genesis.config.genesis_height, false); + + // Move flat storage to the max height for consistency across shards. + let (block_height, desired_block_hash) = + fork_heads.iter().map(|head| (head.height, head.hash)).max().unwrap(); + + let desired_block_header = chain.get_block_header(&desired_block_hash)?; + let epoch_id = desired_block_header.epoch_id(); + let flat_storage_manager = FlatStorageManager::new(store.clone()); + + // Advance flat heads to the same (max) block height to ensure + // consistency of state across the shards. + let state_roots: Vec = (0..num_shards) + .map(|shard_id| { + let shard_uid = + epoch_manager.shard_id_to_uid(shard_id as ShardId, epoch_id).unwrap(); + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + let flat_storage = + flat_storage_manager.get_flat_storage_for_shard(shard_uid).unwrap(); + flat_storage.update_flat_head(&desired_block_hash, true).unwrap(); + let chunk_extra = chain.get_chunk_extra(&desired_block_hash, &shard_uid).unwrap(); + let state_root = chunk_extra.state_root(); + tracing::info!(?shard_id, ?epoch_id, ?state_root); + *state_root + }) + .collect(); + + // Increment height to represent that some changes were made to the original state. + tracing::info!( + block_height, + ?desired_block_hash, + ?state_roots, + ?epoch_id, + "Moved flat heads to a common block" + ); + let block_height = block_height + 1; + + let mut store_update = store.store_update(); + store_update.set_ser(DBCol::Misc, b"FORK_TOOL_EPOCH_ID", epoch_id)?; + store_update.set_ser(DBCol::Misc, b"FORK_TOOL_BLOCK_HASH", &desired_block_hash)?; + store_update.set(DBCol::Misc, b"FORK_TOOL_BLOCK_HEIGHT", &block_height.to_le_bytes()); + for (shard_id, state_root) in state_roots.iter().enumerate() { + store_update.set_ser( + DBCol::Misc, + format!("FORK_TOOL_SHARD_ID:{shard_id}").as_bytes(), + state_root, + )?; + } + store_update.commit()?; + Ok(()) + } + + /// Creates a DB snapshot, then + /// Updates the state to ensure every account has a full access key that is known to us. + fn amend_access_keys( + &self, + batch_size: u64, + unc_config: &mut UncConfig, + home_dir: &Path, + ) -> anyhow::Result> { + // Open storage with migration + unc_config.config.store.load_mem_tries_for_all_shards = true; + let storage = open_storage(&home_dir, unc_config).unwrap(); + let store = storage.get_hot_store(); + + let (prev_state_roots, prev_hash, epoch_id, block_height) = + self.get_state_roots_and_hash(store.clone())?; + tracing::info!(?prev_state_roots, ?epoch_id, ?prev_hash); + + let epoch_manager = + EpochManager::new_arc_handle(store.clone(), &unc_config.genesis.config); + let num_shards = prev_state_roots.len(); + let all_shard_uids: Vec = (0..num_shards) + .map(|shard_id| epoch_manager.shard_id_to_uid(shard_id as ShardId, &epoch_id).unwrap()) + .collect(); + let runtime = + NightshadeRuntime::from_config(home_dir, store.clone(), &unc_config, epoch_manager); + runtime.load_mem_tries_on_startup(&all_shard_uids).unwrap(); + + let make_storage_mutator: MakeSingleShardStorageMutatorFn = + Arc::new(move |prev_state_root| { + SingleShardStorageMutator::new(&runtime.clone(), prev_state_root) + }); + + let new_state_roots = self.prepare_state( + batch_size, + &all_shard_uids, + store, + &prev_state_roots, + block_height, + make_storage_mutator.clone(), + )?; + Ok(new_state_roots) + } + + /// Creates a DB snapshot, then + /// Reads a list of validator accounts from a file + /// Adds validator accounts to the state + /// Creates a genesis file with the new validators. + fn set_validators( + &self, + validators: &Path, + epoch_length: u64, + chain_id_suffix: &str, + unc_config: &mut UncConfig, + home_dir: &Path, + ) -> anyhow::Result<(Vec, Vec)> { + // Open storage with migration + let storage = open_storage(&home_dir, unc_config).unwrap(); + let store = storage.get_hot_store(); + + let (prev_state_roots, _prev_hash, epoch_id, block_height) = + self.get_state_roots_and_hash(store.clone())?; + + let epoch_manager = + EpochManager::new_arc_handle(store.clone(), &unc_config.genesis.config); + + let runtime = + NightshadeRuntime::from_config(home_dir, store, &unc_config, epoch_manager.clone()); + + let runtime_config_store = RuntimeConfigStore::new(None); + let runtime_config = runtime_config_store.get_config(PROTOCOL_VERSION); + + let storage_mutator = StorageMutator::new( + epoch_manager.clone(), + &runtime, + epoch_id.clone(), + prev_state_roots, + )?; + let (new_state_roots, new_validator_accounts) = + self.add_validator_accounts(validators, runtime_config, home_dir, storage_mutator)?; + + tracing::info!("Creating a new genesis"); + backup_genesis_file(home_dir, &unc_config)?; + self.make_and_write_genesis( + epoch_length, + block_height, + chain_id_suffix, + &epoch_id, + new_state_roots.clone(), + new_validator_accounts.clone(), + epoch_manager, + home_dir, + &unc_config, + )?; + + tracing::info!("All Done! Run the node normally to start the forked network."); + Ok((new_state_roots, new_validator_accounts)) + } + + /// Deletes DB columns that are not needed in the new chain. + fn finalize(&self, unc_config: &mut UncConfig, home_dir: &Path) -> anyhow::Result<()> { + // Open storage with migration + let storage = open_storage(&home_dir, unc_config).unwrap(); + let store = storage.get_hot_store(); + + tracing::info!("Delete unneeded columns in the original DB"); + let mut update = store.store_update(); + for col in DBCol::iter() { + match col { + DBCol::DbVersion | DBCol::Misc | DBCol::State | DBCol::FlatState => {} + _ => update.delete_all(col), + } + } + update.commit()?; + Ok(()) + } + + fn get_state_roots_and_hash( + &self, + store: Store, + ) -> anyhow::Result<(Vec, CryptoHash, EpochId, BlockHeight)> { + let epoch_id = EpochId(store.get_ser(DBCol::Misc, b"FORK_TOOL_EPOCH_ID")?.unwrap()); + let block_hash = store.get_ser(DBCol::Misc, b"FORK_TOOL_BLOCK_HASH")?.unwrap(); + let block_height = store.get(DBCol::Misc, b"FORK_TOOL_BLOCK_HEIGHT")?.unwrap(); + let block_height = u64::from_le_bytes(block_height.as_slice().try_into().unwrap()); + let mut state_roots = vec![]; + for (shard_id, item) in + store.iter_prefix(DBCol::Misc, "FORK_TOOL_SHARD_ID:".as_bytes()).enumerate() + { + let (key, value) = item?; + let key = String::from_utf8(key.to_vec())?; + let state_root = borsh::from_slice(&value)?; + assert_eq!(key, format!("FORK_TOOL_SHARD_ID:{shard_id}")); + state_roots.push(state_root); + } + tracing::info!(?state_roots, ?block_hash, ?epoch_id, block_height); + Ok((state_roots, block_hash, epoch_id, block_height)) + } + + /// Checks that `~/.near/data/fork-snapshot/data` exists. + /// Deletes files (not directories) in `~/.near/data` + /// Moves everything from `~/.near/data/fork-snapshot/data/` to `~/.near/data`. + /// Deletes `~/.near/data/fork-snapshot/data`. + /// Moves `~/.near/genesis.json.backup` to `~/.near/genesis.json`. + fn reset(self, unc_config: &UncConfig, home_dir: &Path) -> anyhow::Result<()> { + let store_path = + home_dir.join(unc_config.config.store.path.clone().unwrap_or(PathBuf::from("data"))); + // '/data' prefix comes from the use of `checkpoint_hot_storage_and_cleanup_columns` fn + let fork_snapshot_path = store_path.join("fork-snapshot/data"); + if !Path::new(&fork_snapshot_path).exists() { + panic!("Fork snapshot does not exist"); + } + tracing::info!("Removing all current data"); + for entry in std::fs::read_dir(&store_path)? { + let entry = entry?; + if entry.file_type()?.is_file() { + std::fs::remove_file(&entry.path())?; + } + } + tracing::info!("Restoring fork snapshot"); + for entry in std::fs::read_dir(&fork_snapshot_path)? { + let entry = entry?; + std::fs::rename(&entry.path(), &store_path.join(entry.file_name()))?; + } + std::fs::remove_dir(&fork_snapshot_path)?; + + tracing::info!("Restoring genesis file"); + restore_backup_genesis_file(home_dir, &unc_config)?; + tracing::info!("Reset complete"); + return Ok(()); + } + + fn prepare_shard_state( + &self, + batch_size: u64, + shard_uid: ShardUId, + store: Store, + prev_state_root: StateRoot, + block_height: BlockHeight, + make_storage_mutator: MakeSingleShardStorageMutatorFn, + ) -> anyhow::Result { + // Doesn't support secrets. + tracing::info!(?shard_uid); + let mut storage_mutator: SingleShardStorageMutator = make_storage_mutator(prev_state_root)?; + + // Keeps track of accounts that have a full access key. + let mut has_full_key = HashSet::new(); + // Lets us lookup large values in the `State` columns. + let trie_storage = TrieDBStorage::new(store.clone(), shard_uid); + + // Iterate over the whole flat storage and do the necessary changes to have access to all accounts. + let mut index_delayed_receipt = 0; + let mut ref_keys_retrieved = 0; + let mut records_not_parsed = 0; + let mut records_parsed = 0; + let mut access_keys_updated = 0; + let mut accounts_implicit_updated = 0; + let mut contract_data_updated = 0; + let mut contract_code_updated = 0; + let mut postponed_receipts_updated = 0; + let mut delayed_receipts_updated = 0; + let mut received_data_updated = 0; + let mut fake_block_height = block_height + 1; + for item in store_helper::iter_flat_state_entries(shard_uid, &store, None, None) { + let (key, value) = match item { + Ok((key, FlatStateValue::Ref(ref_value))) => { + ref_keys_retrieved += 1; + (key, trie_storage.retrieve_raw_bytes(&ref_value.hash)?.to_vec()) + } + Ok((key, FlatStateValue::Inlined(value))) => (key, value), + otherwise => panic!("Unexpected flat state value: {otherwise:?}"), + }; + if let Some(sr) = StateRecord::from_raw_key_value(key.clone(), value.clone()) { + match sr { + StateRecord::AccessKey { account_id, public_key, access_key } => { + // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. + if account_id.get_account_type() != AccountType::NearImplicitAccount + && access_key.permission == AccessKeyPermission::FullAccess + { + has_full_key.insert(account_id.clone()); + } + let new_account_id = map_account(&account_id, None); + let replacement = map_key(&public_key, None); + storage_mutator.delete_access_key(account_id, public_key)?; + storage_mutator.set_access_key( + new_account_id, + replacement.public_key(), + access_key.clone(), + )?; + access_keys_updated += 1; + } + + StateRecord::Account { account_id, account } => { + // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. + if account_id.get_account_type() == AccountType::NearImplicitAccount { + let new_account_id = map_account(&account_id, None); + storage_mutator.delete_account(account_id)?; + storage_mutator.set_account(new_account_id, account)?; + accounts_implicit_updated += 1; + } + } + StateRecord::Data { account_id, data_key, value } => { + // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. + if account_id.get_account_type() == AccountType::NearImplicitAccount { + let new_account_id = map_account(&account_id, None); + storage_mutator.delete_data(account_id, &data_key)?; + storage_mutator.set_data(new_account_id, &data_key, value)?; + contract_data_updated += 1; + } + } + StateRecord::Contract { account_id, code } => { + // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. + if account_id.get_account_type() == AccountType::NearImplicitAccount { + let new_account_id = map_account(&account_id, None); + storage_mutator.delete_code(account_id)?; + storage_mutator.set_code(new_account_id, code)?; + contract_code_updated += 1; + } + } + StateRecord::PostponedReceipt(receipt) => { + // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. + if receipt.predecessor_id.get_account_type() + == AccountType::NearImplicitAccount + || receipt.receiver_id.get_account_type() + == AccountType::NearImplicitAccount + { + let new_receipt = Receipt { + predecessor_id: map_account(&receipt.predecessor_id, None), + receiver_id: map_account(&receipt.receiver_id, None), + receipt_id: receipt.receipt_id, + receipt: receipt.receipt.clone(), + }; + storage_mutator.delete_postponed_receipt(receipt)?; + storage_mutator.set_postponed_receipt(&new_receipt)?; + postponed_receipts_updated += 1; + } + } + StateRecord::ReceivedData { account_id, data_id, data } => { + // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. + if account_id.get_account_type() == AccountType::NearImplicitAccount { + let new_account_id = map_account(&account_id, None); + storage_mutator.delete_received_data(account_id, data_id)?; + storage_mutator.set_received_data(new_account_id, data_id, &data)?; + received_data_updated += 1; + } + } + StateRecord::DelayedReceipt(receipt) => { + // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. + if receipt.predecessor_id.get_account_type() + == AccountType::NearImplicitAccount + || receipt.receiver_id.get_account_type() + == AccountType::NearImplicitAccount + { + let new_receipt = Receipt { + predecessor_id: map_account(&receipt.predecessor_id, None), + receiver_id: map_account(&receipt.receiver_id, None), + receipt_id: receipt.receipt_id, + receipt: receipt.receipt, + }; + storage_mutator.delete_delayed_receipt(index_delayed_receipt)?; + storage_mutator + .set_delayed_receipt(index_delayed_receipt, &new_receipt)?; + delayed_receipts_updated += 1; + } + index_delayed_receipt += 1; + } + } + records_parsed += 1; + } else { + records_not_parsed += 1; + } + if storage_mutator.should_commit(batch_size) { + tracing::info!( + ?shard_uid, + ref_keys_retrieved, + records_parsed, + updated = access_keys_updated + + accounts_implicit_updated + + contract_data_updated + + contract_code_updated + + postponed_receipts_updated + + delayed_receipts_updated + + received_data_updated, + ); + let state_root = storage_mutator.commit(&shard_uid, fake_block_height)?; + fake_block_height += 1; + storage_mutator = make_storage_mutator(state_root)?; + } + } + + tracing::info!( + ?shard_uid, + ref_keys_retrieved, + records_parsed, + records_not_parsed, + accounts_implicit_updated, + access_keys_updated, + contract_code_updated, + contract_data_updated, + postponed_receipts_updated, + delayed_receipts_updated, + received_data_updated, + num_has_full_key = has_full_key.len(), + "Pass 1 done" + ); + + // Now do another pass to ensure all accounts have full access keys. + // Remember that we kept track of accounts with full access keys in `has_full_key`. + // Iterating over the whole flat state is very fast compared to writing all the updates. + let mut num_added = 0; + let mut num_accounts = 0; + for item in store_helper::iter_flat_state_entries(shard_uid, &store, None, None) { + if let Ok((key, _)) = item { + if key[0] == col::ACCOUNT { + num_accounts += 1; + let account_id = match parse_account_id_from_account_key(&key) { + Ok(account_id) => account_id, + Err(err) => { + tracing::error!( + ?err, + "Failed to parse account id {}", + hex::encode(&key) + ); + continue; + } + }; + if has_full_key.contains(&account_id) { + continue; + } + storage_mutator.set_access_key( + account_id, + unc_mirror::key_mapping::EXTRA_KEY.public_key(), + AccessKey::full_access(), + )?; + num_added += 1; + if storage_mutator.should_commit(batch_size) { + let state_root = storage_mutator.commit(&shard_uid, fake_block_height)?; + fake_block_height += 1; + storage_mutator = make_storage_mutator(state_root)?; + } + } + } + } + tracing::info!(?shard_uid, num_accounts, num_added, "Pass 2 done"); + + let state_root = storage_mutator.commit(&shard_uid, fake_block_height)?; + + tracing::info!(?shard_uid, "Commit done"); + Ok(state_root) + } + + fn prepare_state( + &self, + batch_size: u64, + all_shard_uids: &[ShardUId], + store: Store, + prev_state_roots: &[StateRoot], + block_height: BlockHeight, + make_storage_mutator: MakeSingleShardStorageMutatorFn, + ) -> anyhow::Result> { + let state_roots = all_shard_uids + .into_par_iter() + .map(|shard_uid| { + let state_root = self + .prepare_shard_state( + batch_size, + *shard_uid, + store.clone(), + prev_state_roots[shard_uid.shard_id as usize], + block_height, + make_storage_mutator.clone(), + ) + .unwrap(); + state_root + }) + .collect(); + tracing::info!(?state_roots, "All done"); + Ok(state_roots) + } + + /// Reads the validators file (which is a path relative to the home dir), + /// and adds new accounts and new keys for the specified accounts. + fn add_validator_accounts( + &self, + validators: &Path, + runtime_config: &Arc, + home_dir: &Path, + mut storage_mutator: StorageMutator, + ) -> anyhow::Result<(Vec, Vec)> { + let mut new_validator_accounts = vec![]; + + let liquid_balance = 100_000_000 * UNC_BASE; + let storage_bytes = runtime_config.fees.storage_usage_config.num_bytes_account; + let validators_path = if validators.is_absolute() { + PathBuf::from(validators) + } else { + home_dir.join(&validators) + }; + let file = File::open(&validators_path) + .expect("Failed to open the validators JSON {validators_path:?}"); + let new_validators: Vec = serde_json::from_reader(BufReader::new(file)) + .expect("Failed to read validators JSON {validators_path:?}"); + for validator in new_validators.into_iter() { + let validator_account = AccountInfo { + account_id: validator.account_id, + amount: validator.amount.unwrap_or(50_000 * UNC_BASE), + power: validator.power.unwrap_or(5), + locked: validator.frozen.unwrap_or(5), + public_key: validator.public_key, + }; + new_validator_accounts.push(validator_account.clone()); + storage_mutator.set_account( + &validator_account.account_id, + Account::new( + liquid_balance, + validator_account.amount, + validator_account.power, + CryptoHash::default(), + storage_bytes, + ), + )?; + storage_mutator.set_access_key( + &validator_account.account_id, + validator_account.public_key, + AccessKey::full_access(), + )?; + } + let new_state_roots = storage_mutator.commit()?; + Ok((new_state_roots, new_validator_accounts)) + } + + /// Makes a new genesis and writes it to `~/.near/genesis.json`. + fn make_and_write_genesis( + &self, + epoch_length: u64, + height: BlockHeight, + chain_id_suffix: &str, + epoch_id: &EpochId, + new_state_roots: Vec, + new_validator_accounts: Vec, + epoch_manager: Arc, + home_dir: &Path, + unc_config: &UncConfig, + ) -> anyhow::Result<()> { + let epoch_config = epoch_manager.get_epoch_config(epoch_id)?; + let epoch_info = epoch_manager.get_epoch_info(epoch_id)?; + let original_config = unc_config.genesis.config.clone(); + + let new_config = GenesisConfig { + chain_id: original_config.chain_id.clone() + chain_id_suffix, + genesis_height: height, + genesis_time: chrono::Utc::now(), + epoch_length, + num_block_producer_seats: epoch_config.num_block_producer_seats, + num_block_producer_seats_per_shard: epoch_config.num_block_producer_seats_per_shard, + avg_hidden_validator_seats_per_shard: epoch_config.avg_hidden_validator_seats_per_shard, + block_producer_kickout_threshold: 0, + chunk_producer_kickout_threshold: 0, + max_kickout_stake_perc: 0, + online_min_threshold: epoch_config.online_min_threshold, + online_max_threshold: epoch_config.online_max_threshold, + fishermen_threshold: epoch_config.fishermen_threshold, + minimum_stake_divisor: epoch_config.minimum_stake_divisor, + protocol_upgrade_stake_threshold: epoch_config.protocol_upgrade_stake_threshold, + shard_layout: epoch_config.shard_layout.clone(), + num_chunk_only_producer_seats: epoch_config + .validator_selection_config + .num_chunk_only_producer_seats, + minimum_validators_per_shard: epoch_config + .validator_selection_config + .minimum_validators_per_shard, + minimum_stake_ratio: epoch_config.validator_selection_config.minimum_stake_ratio, + dynamic_resharding: false, + protocol_version: epoch_info.protocol_version(), + validators: new_validator_accounts, + gas_price_adjustment_rate: original_config.gas_price_adjustment_rate, + gas_limit: original_config.gas_limit, + max_gas_price: original_config.max_gas_price, + max_inflation_rate: original_config.max_inflation_rate, + min_gas_price: original_config.min_gas_price, + num_blocks_per_year: original_config.num_blocks_per_year, + protocol_reward_rate: original_config.protocol_reward_rate, + protocol_treasury_account: original_config.protocol_treasury_account.clone(), + total_supply: original_config.total_supply, + transaction_validity_period: original_config.transaction_validity_period, + use_production_config: original_config.use_production_config, + }; + + let genesis = Genesis::new_from_state_roots(new_config, new_state_roots); + let genesis_file = &unc_config.config.genesis_file; + let original_genesis_file = home_dir.join(&genesis_file); + + tracing::info!(?original_genesis_file, "Writing new genesis"); + genesis.to_file(&original_genesis_file); + + Ok(()) + } +} + +fn backup_genesis_file_path(home_dir: &Path, genesis_file: &str) -> PathBuf { + home_dir.join(format!("{}.backup", &genesis_file)) +} + +/// Returns hash of flat head. +/// Checks that all shards have flat storage. +/// Checks that flat heads of all shards match. +fn get_fork_heads(all_shard_uids: &[ShardUId], store: Store) -> anyhow::Result> { + // Iterate over each shard to check that flat storage is Ready. + let flat_heads :Vec = all_shard_uids.iter().map(|shard_uid|{ + let flat_storage_status = store + .get_ser::(DBCol::FlatStorageStatus, &shard_uid.to_bytes()).unwrap() + .unwrap(); + if let FlatStorageStatus::Ready(ready) = &flat_storage_status { + ready.flat_head + } else { + panic!("Flat storage is not ready for shard {shard_uid}: {flat_storage_status:?}. Please reset the fork, and run the node for longer"); + } + }).collect(); + Ok(flat_heads) +} + +fn backup_genesis_file(home_dir: &Path, unc_config: &UncConfig) -> anyhow::Result<()> { + let genesis_file = &unc_config.config.genesis_file; + let original_genesis_file = home_dir.join(&genesis_file); + let backup_genesis_file = backup_genesis_file_path(home_dir, &genesis_file); + tracing::info!(?original_genesis_file, ?backup_genesis_file, "Backing up old genesis."); + std::fs::rename(&original_genesis_file, &backup_genesis_file)?; + Ok(()) +} + +fn restore_backup_genesis_file(home_dir: &Path, unc_config: &UncConfig) -> anyhow::Result<()> { + let genesis_file = &unc_config.config.genesis_file; + let backup_genesis_file = backup_genesis_file_path(home_dir, &genesis_file); + let original_genesis_file = home_dir.join(&genesis_file); + tracing::info!(?backup_genesis_file, ?original_genesis_file, "Restoring genesis from a backup"); + std::fs::rename(&backup_genesis_file, &original_genesis_file)?; + Ok(()) +} diff --git a/tools/fork-network/src/lib.rs b/tools/fork-network/src/lib.rs new file mode 100644 index 000000000..efffeca2d --- /dev/null +++ b/tools/fork-network/src/lib.rs @@ -0,0 +1,3 @@ +pub mod cli; +mod single_shard_storage_mutator; +mod storage_mutator; diff --git a/tools/fork-network/src/single_shard_storage_mutator.rs b/tools/fork-network/src/single_shard_storage_mutator.rs new file mode 100644 index 000000000..a7fa2bdc1 --- /dev/null +++ b/tools/fork-network/src/single_shard_storage_mutator.rs @@ -0,0 +1,183 @@ +use unc_chain::types::RuntimeAdapter; +use unc_crypto::PublicKey; +use unc_primitives::account::{AccessKey, Account}; +use unc_primitives::borsh; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::Receipt; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::{AccountId, StateRoot}; +use unc_primitives::types::{StoreKey, StoreValue}; +use unc_store::{flat::FlatStateChanges, DBCol, ShardTries}; +use framework::NightshadeRuntime; + +/// Object that updates the existing state. Combines all changes, commits them +/// and returns new state roots. +pub(crate) struct SingleShardStorageMutator { + updates: Vec<(Vec, Option>)>, + state_root: StateRoot, + shard_tries: ShardTries, +} + +impl SingleShardStorageMutator { + pub(crate) fn new(runtime: &NightshadeRuntime, state_root: StateRoot) -> anyhow::Result { + Ok(Self { updates: Vec::new(), state_root, shard_tries: runtime.get_tries() }) + } + + fn set(&mut self, key: TrieKey, value: Vec) -> anyhow::Result<()> { + self.updates.push((key.to_vec(), Some(value))); + Ok(()) + } + + fn remove(&mut self, key: TrieKey) -> anyhow::Result<()> { + self.updates.push((key.to_vec(), None)); + Ok(()) + } + + pub(crate) fn set_account( + &mut self, + account_id: AccountId, + value: Account, + ) -> anyhow::Result<()> { + self.set(TrieKey::Account { account_id }, borsh::to_vec(&value)?) + } + + pub(crate) fn delete_account(&mut self, account_id: AccountId) -> anyhow::Result<()> { + self.remove(TrieKey::Account { account_id }) + } + + pub(crate) fn set_access_key( + &mut self, + account_id: AccountId, + public_key: PublicKey, + access_key: AccessKey, + ) -> anyhow::Result<()> { + self.set(TrieKey::AccessKey { account_id, public_key }, borsh::to_vec(&access_key)?) + } + + pub(crate) fn delete_access_key( + &mut self, + account_id: AccountId, + public_key: PublicKey, + ) -> anyhow::Result<()> { + self.remove(TrieKey::AccessKey { account_id, public_key }) + } + + pub(crate) fn set_data( + &mut self, + account_id: AccountId, + data_key: &StoreKey, + value: StoreValue, + ) -> anyhow::Result<()> { + self.set( + TrieKey::ContractData { account_id, key: data_key.to_vec() }, + borsh::to_vec(&value)?, + ) + } + + pub(crate) fn delete_data( + &mut self, + account_id: AccountId, + data_key: &StoreKey, + ) -> anyhow::Result<()> { + self.remove(TrieKey::ContractData { account_id, key: data_key.to_vec() }) + } + + pub(crate) fn set_code(&mut self, account_id: AccountId, value: Vec) -> anyhow::Result<()> { + self.set(TrieKey::ContractCode { account_id }, value) + } + + pub(crate) fn delete_code(&mut self, account_id: AccountId) -> anyhow::Result<()> { + self.remove(TrieKey::ContractCode { account_id }) + } + + pub(crate) fn set_postponed_receipt(&mut self, receipt: &Receipt) -> anyhow::Result<()> { + self.set( + TrieKey::PostponedReceipt { + receiver_id: receipt.receiver_id.clone(), + receipt_id: receipt.receipt_id, + }, + borsh::to_vec(&receipt)?, + ) + } + + pub(crate) fn delete_postponed_receipt(&mut self, receipt: Box) -> anyhow::Result<()> { + self.remove(TrieKey::PostponedReceipt { + receiver_id: receipt.receiver_id, + receipt_id: receipt.receipt_id, + }) + } + + pub(crate) fn set_received_data( + &mut self, + account_id: AccountId, + data_id: CryptoHash, + data: &Option>, + ) -> anyhow::Result<()> { + self.set(TrieKey::ReceivedData { receiver_id: account_id, data_id }, borsh::to_vec(data)?) + } + + pub(crate) fn delete_received_data( + &mut self, + account_id: AccountId, + data_id: CryptoHash, + ) -> anyhow::Result<()> { + self.remove(TrieKey::ReceivedData { receiver_id: account_id, data_id }) + } + + pub(crate) fn set_delayed_receipt( + &mut self, + index: u64, + receipt: &Receipt, + ) -> anyhow::Result<()> { + self.set(TrieKey::DelayedReceipt { index }, borsh::to_vec(receipt)?) + } + + pub(crate) fn delete_delayed_receipt(&mut self, index: u64) -> anyhow::Result<()> { + self.remove(TrieKey::DelayedReceipt { index }) + } + + pub(crate) fn should_commit(&self, batch_size: u64) -> bool { + self.updates.len() >= batch_size as usize + } + + /// The fake block height is used to allow memtries to garbage collect. + /// Otherwise it would take significantly more memory holding old nodes. + pub(crate) fn commit( + self, + shard_uid: &ShardUId, + fake_block_height: u64, + ) -> anyhow::Result { + let num_updates = self.updates.len(); + tracing::info!(?shard_uid, num_updates, "commit"); + let mut update = self.shard_tries.store_update(); + let flat_state_changes = FlatStateChanges::from_raw_key_value(&self.updates); + flat_state_changes.apply_to_flat_state(&mut update, *shard_uid); + + let trie_changes = self + .shard_tries + .get_trie_for_shard(*shard_uid, self.state_root) + .update(self.updates)?; + tracing::info!( + ?shard_uid, + num_trie_node_insertions = trie_changes.insertions().len(), + num_trie_node_deletions = trie_changes.deletions().len() + ); + let state_root = self.shard_tries.apply_all(&trie_changes, *shard_uid, &mut update); + self.shard_tries.apply_memtrie_changes(&trie_changes, *shard_uid, fake_block_height); + // We may not have loaded memtries (some commands don't need to), so check. + if let Some(mem_tries) = self.shard_tries.get_mem_tries(*shard_uid) { + mem_tries.write().unwrap().delete_until_height(fake_block_height - 1); + } + tracing::info!(?shard_uid, num_updates, "committing"); + update.set_ser( + DBCol::Misc, + format!("FORK_TOOL_SHARD_ID:{}", shard_uid.shard_id).as_bytes(), + &state_root, + )?; + + update.commit()?; + tracing::info!(?shard_uid, ?state_root, "Commit is done"); + Ok(state_root) + } +} diff --git a/tools/fork-network/src/storage_mutator.rs b/tools/fork-network/src/storage_mutator.rs new file mode 100644 index 000000000..f623365e3 --- /dev/null +++ b/tools/fork-network/src/storage_mutator.rs @@ -0,0 +1,69 @@ +use crate::single_shard_storage_mutator::SingleShardStorageMutator; +use unc_crypto::PublicKey; +use unc_epoch_manager::EpochManagerAdapter; +use unc_primitives::account::{AccessKey, Account}; +use unc_primitives::types::{AccountId, EpochId, StateRoot}; +use framework::NightshadeRuntime; +use std::sync::Arc; + +/// Object that updates the existing state. Combines all changes, commits them +/// and returns new state roots. +pub(crate) struct StorageMutator { + epoch_manager: Arc, + epoch_id: EpochId, + mutators: Vec, +} + +impl StorageMutator { + pub(crate) fn new( + epoch_manager: Arc, + runtime: &NightshadeRuntime, + epoch_id: EpochId, + state_roots: Vec, + ) -> anyhow::Result { + let shard_layout = epoch_manager.get_shard_layout(&epoch_id)?; + assert_eq!(shard_layout.shard_ids().count(), state_roots.len()); + + let mut mutators = vec![]; + for state_root in state_roots { + mutators.push(SingleShardStorageMutator::new(runtime, state_root)?); + } + Ok(Self { epoch_manager, epoch_id, mutators }) + } + + fn mutator( + &mut self, + account_id: &AccountId, + ) -> anyhow::Result<&mut SingleShardStorageMutator> { + let shard_id = self.epoch_manager.account_id_to_shard_id(&account_id, &self.epoch_id)?; + Ok(&mut self.mutators[shard_id as usize]) + } + + pub(crate) fn set_account( + &mut self, + account_id: &AccountId, + value: Account, + ) -> anyhow::Result<()> { + self.mutator(account_id)?.set_account(account_id.clone(), value) + } + + pub(crate) fn set_access_key( + &mut self, + account_id: &AccountId, + public_key: PublicKey, + access_key: AccessKey, + ) -> anyhow::Result<()> { + self.mutator(account_id)?.set_access_key(account_id.clone(), public_key, access_key) + } + + pub(crate) fn commit(self) -> anyhow::Result> { + let shard_layout = self.epoch_manager.get_shard_layout(&self.epoch_id)?; + let all_shard_uids = shard_layout.shard_uids(); + let mut state_roots = vec![]; + for (mutator, shard_uid) in self.mutators.into_iter().zip(all_shard_uids.into_iter()) { + let state_root = mutator.commit(&shard_uid, 0)?; + state_roots.push(state_root); + } + Ok(state_roots) + } +} diff --git a/tools/indexer/example/Cargo.toml b/tools/indexer/example/Cargo.toml new file mode 100644 index 000000000..f1da446c3 --- /dev/null +++ b/tools/indexer/example/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "indexer-example" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix.workspace = true +anyhow.workspace = true +clap.workspace = true +openssl-probe.workspace = true +serde_json.workspace = true +tokio.workspace = true +tracing.workspace = true + +unc-indexer.workspace = true +unc-o11y.workspace = true diff --git a/tools/indexer/example/README.md b/tools/indexer/example/README.md new file mode 100644 index 000000000..13894862d --- /dev/null +++ b/tools/indexer/example/README.md @@ -0,0 +1,6 @@ +NEAR Indexer Simple Logger Example +================================== + +This is an example project featuring [NEAR Indexer Framework](https://github.com/utnet-org/utility/tree/master/chain/indexer). This Indexer prints out all the blocks, chunks, transactions, receipts, execution outcomes, and state changes block by block immediately once it gets finalized in the network. + +Refer to the NEAR Indexer Framework README to learn how to run this example. diff --git a/tools/indexer/example/src/configs.rs b/tools/indexer/example/src/configs.rs new file mode 100644 index 000000000..265e6d2f6 --- /dev/null +++ b/tools/indexer/example/src/configs.rs @@ -0,0 +1,85 @@ +use unc_indexer::unc_primitives::types::Gas; + +/// NEAR Indexer Example +/// Watches for stream of blocks from the chain +#[derive(clap::Parser, Debug)] +#[clap(version = "0.1", author = "unc Inc. ")] +#[clap(subcommand_required = true, arg_required_else_help = true)] +pub(crate) struct Opts { + /// Sets a custom config dir. Defaults to ~/.near/ + #[clap(short, long)] + pub home_dir: Option, + #[clap(subcommand)] + pub subcmd: SubCommand, +} + +#[derive(clap::Parser, Debug)] +#[allow(clippy::large_enum_variant)] +pub(crate) enum SubCommand { + /// Run NEAR Indexer Example. Start observe the network + Run, + /// Initialize necessary configs + Init(InitConfigArgs), +} + +#[derive(clap::Parser, Debug)] +pub(crate) struct InitConfigArgs { + /// chain/network id (localnet, testnet, devnet, betanet) + #[clap(short, long)] + pub chain_id: Option, + /// Account ID for the validator key + #[clap(long)] + pub account_id: Option, + /// Specify private key generated from seed (TESTING ONLY) + #[clap(long)] + pub test_seed: Option, + /// Number of shards to initialize the chain with + #[clap(short, long, default_value = "1")] + pub num_shards: u64, + /// Makes block production fast (TESTING ONLY) + #[clap(short, long)] + pub fast: bool, + /// Genesis file to use when initialize testnet (including downloading) + #[clap(short, long)] + pub genesis: Option, + #[clap(long)] + /// Download the verified NEAR genesis file automatically. + pub download_genesis: bool, + /// Specify a custom download URL for the genesis-file. + #[clap(long)] + pub download_genesis_url: Option, + /// Specify a custom download URL for the records-file. + #[clap(long)] + pub download_records_url: Option, + #[clap(long)] + /// Download the verified NEAR config file automatically. + pub download_config: bool, + /// Specify a custom download URL for the config file. + #[clap(long)] + pub download_config_url: Option, + /// Specify the boot nodes to bootstrap the network + pub boot_nodes: Option, + /// Specify a custom max_gas_burnt_view limit. + #[clap(long)] + pub max_gas_burnt_view: Option, +} + +impl From for unc_indexer::InitConfigArgs { + fn from(config_args: InitConfigArgs) -> Self { + Self { + chain_id: config_args.chain_id, + account_id: config_args.account_id, + test_seed: config_args.test_seed, + num_shards: config_args.num_shards, + fast: config_args.fast, + genesis: config_args.genesis, + download_genesis: config_args.download_genesis, + download_genesis_url: config_args.download_genesis_url, + download_records_url: config_args.download_records_url, + download_config: config_args.download_config, + download_config_url: config_args.download_config_url, + boot_nodes: config_args.boot_nodes, + max_gas_burnt_view: config_args.max_gas_burnt_view, + } + } +} diff --git a/tools/indexer/example/src/main.rs b/tools/indexer/example/src/main.rs new file mode 100644 index 000000000..68d53588d --- /dev/null +++ b/tools/indexer/example/src/main.rs @@ -0,0 +1,291 @@ +use actix; + +use anyhow::Result; +use clap::Parser; +use tokio::sync::mpsc; +use tracing::info; + +use configs::{Opts, SubCommand}; +use unc_indexer; + +mod configs; + +async fn listen_blocks(mut stream: mpsc::Receiver) { + while let Some(streamer_message) = stream.recv().await { + // TODO: handle data as you need + // Example of `StreamerMessage` with all the data (the data is synthetic) + // + // Note that `outcomes` for a given transaction won't be included into the same block. + // Execution outcomes are included into the blocks after the transaction or receipt + // are recorded on a chain; in most cases, it is the next block after the one that has + // the transaction or receipt. + // + // StreamerMessage { + // block: BlockView { + // author: "test.near", + // header: BlockHeaderView { + // height: 63596, + // epoch_id: `Bk7pvZWUTfHRRZtfgTDjnQ6y5cV8yG2h3orCqJvUbiym`, + // next_epoch_id: `3JuBZ4Gz5Eauf7PzQegfqSEDyvws3eKJYPbfGHAYmeR5`, + // hash: `5X37niQWWcihDGQjsvDMHYKLCurNJyQLxCeLgneDb8mk`, + // prev_hash: `2vJNJca72pBiq2eETq2xvuoc6caKDaUkdRgtdefyutbA`, + // prev_state_root: `GkdxSBf4Kfq8V16N4Kqn3YdcThG1f5KG1KLBmXpMzP1k`, + // chunk_receipts_root: `9ETNjrt6MkwTgSVMMbpukfxRshSD1avBUUa4R4NuqwHv`, + // chunk_headers_root: `C7dVr9KdXYKt31yF2BkeAu115fpo79zYTqeU3FzqbFak`, + // chunk_tx_root: `7tkzFg8RHBmMw1ncRJZCCZAizgq4rwCftTKYLce8RU8t`, + // outcome_root: `7tkzFg8RHBmMw1ncRJZCCZAizgq4rwCftTKYLce8RU8t`, + // chunks_included: 1, + // challenges_root: `11111111111111111111111111111111`, + // timestamp: 1618558205803345000, + // timestamp_nanosec: 1618558205803345000, + // random_value: `3cAa93XmoLaKAJQgWz3K7SiKwnA3uaxi8MGgLM78HTNS`, + // validator_proposals: [], + // chunk_mask: [ + // true, + // ], + // gas_price: 1000000000, + // rent_paid: 0, + // validator_reward: 0, + // total_supply: 2050206401403887985811862247311434, + // challenges_result: [], + // last_final_block: `DCkMmXYHqibzcMjgFjRXJP7eckAMLrA4ijggSApMNwKu`, + // last_ds_final_block: `2vJNJca72pBiq2eETq2xvuoc6caKDaUkdRgtdefyutbA`, + // next_bp_hash: `4DJWnxRbUhRrsXK6EBkx4nFeXHKgJWqteDnJ7Hv4MZ6M`, + // block_merkle_root: `Bvn5K89fJ3uPNsj3324Ls9TXAGUVteHPpfKwKqL1La6W`, + // approvals: [ + // Some( + // ed25519:F816hgJod7nPfD2qQz5yhaKDMn1JXmvzj2iXegsJpsmPNnYYZpKYJXgyuVTVJ4TKQbcJ2Q3USCGZF6fX2TcwBBv, + // ), + // ], + // signature: ed25519:239NbE4BuJaxneQA3AEsPrsGY7v3wBgaezbgg56HER69zPrBoc3a4fbyVWPXeoKE3LvgGma1g6pSHk9QHkmETCZY, + // latest_protocol_version: 43, + // }, + // chunks: [ + // ChunkHeaderView { + // chunk_hash: `2M2oeNFBbUUnHfkU1UuBr8EKBCLMH9xr2vfsGRpyiBmA`, + // prev_block_hash: `2vJNJca72pBiq2eETq2xvuoc6caKDaUkdRgtdefyutbA`, + // outcome_root: `11111111111111111111111111111111`, + // prev_state_root: `3gZPPijaumgMRCvMuuZZM1Ab2LoHTSfYigMKwLqZ67m6`, + // encoded_merkle_root: `79Bt7ivt9Qhp3c6dJYnueaTyPVweYxZRpQHASRRAiyuy`, + // encoded_length: 8, + // height_created: 63596, + // height_included: 63596, + // shard_id: 0, + // gas_used: 0, + // gas_limit: 1000000000000000, + // rent_paid: 0, + // validator_reward: 0, + // balance_burnt: 0, + // outgoing_receipts_root: `H4Rd6SGeEBTbxkitsCdzfu9xL9HtZ2eHoPCQXUeZ6bW4`, + // tx_root: `11111111111111111111111111111111`, + // validator_proposals: [], + // signature: ed25519:2vWNayBzEoW5DRc7gTdhxdLbkKuK6ACQ78p3JGpKSAZZCarnLroeoALPAFwpr9ZNPxBqdVYh9QLBe7WHZebsS17Z, + // }, + // ], + // }, + // shards: [ + // IndexerShard { + // shard_id: 0, + // chunk: Some( + // IndexerChunkView { + // author: "test.near", + // header: ChunkHeaderView { + // chunk_hash: `2M2oeNFBbUUnHfkU1UuBr8EKBCLMH9xr2vfsGRpyiBmA`, + // prev_block_hash: `2vJNJca72pBiq2eETq2xvuoc6caKDaUkdRgtdefyutbA`, + // outcome_root: `11111111111111111111111111111111`, + // prev_state_root: `3gZPPijaumgMRCvMuuZZM1Ab2LoHTSfYigMKwLqZ67m6`, + // encoded_merkle_root: `79Bt7ivt9Qhp3c6dJYnueaTyPVweYxZRpQHASRRAiyuy`, + // encoded_length: 8, + // height_created: 63596, + // height_included: 0, + // shard_id: 0, + // gas_used: 0, + // gas_limit: 1000000000000000, + // rent_paid: 0, + // validator_reward: 0, + // balance_burnt: 0, + // outgoing_receipts_root: `H4Rd6SGeEBTbxkitsCdzfu9xL9HtZ2eHoPCQXUeZ6bW4`, + // tx_root: `11111111111111111111111111111111`, + // validator_proposals: [], + // signature: ed25519:2vWNayBzEoW5DRc7gTdhxdLbkKuK6ACQ78p3JGpKSAZZCarnLroeoALPAFwpr9ZNPxBqdVYh9QLBe7WHZebsS17Z, + // }, + // transactions: [ + // IndexerTransactionWithOutcome { + // transaction: SignedTransactionView { + // signer_id: "test.near", + // public_key: ed25519:8NA7mh6TAWzy2qz68bHp62QHTEQ6nJLfiYeKDRwEbU3X, + // nonce: 1, + // receiver_id: "some.test.near", + // actions: [ + // CreateAccount, + // Transfer { + // deposit: 40000000000000000000000000, + // }, + // AddKey { + // public_key: ed25519:2syGhqwJ8ba2nUGmP9tkZn9m1DYZPYYobpufiERVnug8, + // access_key: AccessKeyView { + // nonce: 0, + // permission: FullAccess, + // }, + // }, + // ], + // signature: ed25519:Qniuu7exnr6xbe6gKafV5vDhuwM1jt9Bn7sCTF6cHfPpYWVJ4Q6kq8RAxKSeLoxbCreVp1XzMMJmXt8YcUqmMYw, + // hash: `8dNv9S8rAFwso9fLwfDQXmw5yv5zscDjQpta96pMF6Bi`, + // }, + // outcome: IndexerExecutionOutcomeWithReceipt { + // execution_outcome: ExecutionOutcomeWithIdView { + // proof: [], + // block_hash: `G9v6Fsv94xaa7BRY2N5PFF5PJwT7ec6DPzQK73Yf3CZ6`, + // id: `8dNv9S8rAFwso9fLwfDQXmw5yv5zscDjQpta96pMF6Bi`, + // outcome: ExecutionOutcomeView { + // logs: [], + // receipt_ids: [ + // `CbWu7WYYbYbn3kThs5gcxANrxy7AKLcMcBLxLw8Zq1Fz`, + // ], + // gas_burnt: 424555062500, + // tokens_burnt: 424555062500000000000, + // executor_id: "test.near", + // status: SuccessReceiptId(CbWu7WYYbYbn3kThs5gcxANrxy7AKLcMcBLxLw8Zq1Fz), + // }, + // }, + // receipt: None, + // }, + // }, + // ], + // receipts: [ + // ReceiptView { + // predecessor_id: "test.near", + // receiver_id: "some.test.near", + // receipt_id: `CbWu7WYYbYbn3kThs5gcxANrxy7AKLcMcBLxLw8Zq1Fz`, + // receipt: Action { + // signer_id: "test.near", + // signer_public_key: ed25519:8NA7mh6TAWzy2qz68bHp62QHTEQ6nJLfiYeKDRwEbU3X, + // gas_price: 1030000000, + // output_data_receivers: [], + // input_data_ids: [], + // actions: [ + // CreateAccount, + // Transfer { + // deposit: 40000000000000000000000000, + // }, + // AddKey { + // public_key: ed25519:2syGhqwJ8ba2nUGmP9tkZn9m1DYZPYYobpufiERVnug8, + // access_key: AccessKeyView { + // nonce: 0, + // permission: FullAccess, + // }, + // }, + // ], + // }, + // }, + // ], + // }, + // ), + // receipt_execution_outcomes: [ + // IndexerExecutionOutcomeWithReceipt { + // execution_outcome: ExecutionOutcomeWithIdView { + // proof: [], + // block_hash: `BXPB6DQGmBrjARvcgYwS8qKLkyto6dk9NfawGSmfjE9Q`, + // id: `CbWu7WYYbYbn3kThs5gcxANrxy7AKLcMcBLxLw8Zq1Fz`, + // outcome: ExecutionOutcomeView { + // logs: [], + // receipt_ids: [ + // `8vJ1QWM4pffRDnW3c5CxFFV5cMx8wiqxsAqmZTitHvfh`, + // ], + // gas_burnt: 424555062500, + // tokens_burnt: 424555062500000000000, + // executor_id: "some.test.near", + // status: SuccessValue(``), + // }, + // }, + // receipt: ReceiptView { + // predecessor_id: "test.near", + // receiver_id: "some.test.near", + // receipt_id: `CbWu7WYYbYbn3kThs5gcxANrxy7AKLcMcBLxLw8Zq1Fz`, + // receipt: Action { + // signer_id: "test.near", + // signer_public_key: ed25519:8NA7mh6TAWzy2qz68bHp62QHTEQ6nJLfiYeKDRwEbU3X, + // gas_price: 1030000000, + // output_data_receivers: [], + // input_data_ids: [], + // actions: [ + // CreateAccount, + // Transfer { + // deposit: 40000000000000000000000000, + // }, + // AddKey { + // public_key: ed25519:2syGhqwJ8ba2nUGmP9tkZn9m1DYZPYYobpufiERVnug8, + // access_key: AccessKeyView { + // nonce: 0, + // permission: FullAccess, + // }, + // }, + // ], + // }, + // }, + // }, + // ], + // }, + // ], + // state_changes: [ + // StateChangeWithCauseView { + // cause: ValidatorAccountsUpdate, + // value: AccountUpdate { + // account_id: "test.near", + // account: AccountView { + // amount: 1000000000000000000000000000000000, + // locked: 50000000000000000000000000000000, + // code_hash: `11111111111111111111111111111111`, + // storage_usage: 182, + // storage_paid_at: 0, + // }, + // }, + // }, + // ], + // } + info!( + target: "indexer_example", + "#{} {} Shards: {}, Transactions: {}, Receipts: {}, ExecutionOutcomes: {}", + streamer_message.block.header.height, + streamer_message.block.header.hash, + streamer_message.shards.len(), + streamer_message.shards.iter().map(|shard| if let Some(chunk) = &shard.chunk { chunk.transactions.len() } else { 0usize }).sum::(), + streamer_message.shards.iter().map(|shard| if let Some(chunk) = &shard.chunk { chunk.receipts.len() } else { 0usize }).sum::(), + streamer_message.shards.iter().map(|shard| shard.receipt_execution_outcomes.len()).sum::(), + ); + } +} + +fn main() -> Result<()> { + // We use it to automatically search the for root certificates to perform HTTPS calls + // (sending telemetry and downloading genesis) + openssl_probe::init_ssl_cert_env_vars(); + let env_filter = unc_o11y::tracing_subscriber::EnvFilter::new( + "framework=info,indexer_example=info,tokio_reactor=info,near=info,\ + stats=info,telemetry=info,indexer=info,unc-performance-metrics=info", + ); + let _subscriber = unc_o11y::default_subscriber(env_filter, &Default::default()).global(); + let opts: Opts = Opts::parse(); + + let home_dir = opts.home_dir.unwrap_or(unc_indexer::get_default_home()); + + match opts.subcmd { + SubCommand::Run => { + let indexer_config = unc_indexer::IndexerConfig { + home_dir, + sync_mode: unc_indexer::SyncModeEnum::FromInterruption, + await_for_node_synced: unc_indexer::AwaitForNodeSyncedEnum::WaitForFullSync, + validate_genesis: true, + }; + let system = actix::System::new(); + system.block_on(async move { + let indexer = unc_indexer::Indexer::new(indexer_config).expect("Indexer::new()"); + let stream = indexer.streamer(); + actix::spawn(listen_blocks(stream)); + }); + system.run()?; + } + SubCommand::Init(config) => unc_indexer::indexer_init_configs(&home_dir, config.into())?, + } + Ok(()) +} diff --git a/tools/mirror/Cargo.toml b/tools/mirror/Cargo.toml new file mode 100644 index 000000000..48a9e25c2 --- /dev/null +++ b/tools/mirror/Cargo.toml @@ -0,0 +1,84 @@ +[package] +name = "unc-mirror" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix.workspace = true +anyhow.workspace = true +async-trait.workspace = true +borsh.workspace = true +bs58.workspace = true +clap.workspace = true +ed25519-dalek.workspace = true +hex.workspace = true +hkdf.workspace = true +once_cell.workspace = true +openssl-probe.workspace = true +rand_core.workspace = true +rocksdb.workspace = true +secp256k1.workspace = true +serde.workspace = true +serde_json.workspace = true +sha2.workspace = true +strum.workspace = true +thiserror.workspace = true +tokio.workspace = true +tracing.workspace = true + +framework.workspace = true +unc-chain-configs.workspace = true +unc-chain.workspace = true +unc-chain-primitives.workspace = true +unc-client.workspace = true +unc-client-primitives.workspace = true +unc-epoch-manager.workspace = true +unc-indexer-primitives.workspace = true +unc-indexer.workspace = true +unc-network.workspace = true +unc-primitives.workspace = true +unc-primitives-core.workspace = true +unc-o11y.workspace = true +unc-store.workspace = true +unc-crypto.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-chain/nightly", + "unc-client-primitives/nightly", + "unc-client/nightly", + "unc-epoch-manager/nightly", + "unc-indexer-primitives/nightly", + "unc-indexer/nightly", + "unc-network/nightly", + "unc-o11y/nightly", + "unc-primitives-core/nightly", + "unc-primitives/nightly", + "unc-store/nightly", + "framework/nightly", +] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-chain/nightly_protocol", + "unc-client-primitives/nightly_protocol", + "unc-client/nightly_protocol", + "unc-epoch-manager/nightly_protocol", + "unc-indexer-primitives/nightly_protocol", + "unc-indexer/nightly_protocol", + "unc-network/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-primitives-core/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", + "framework/nightly_protocol", +] diff --git a/tools/mirror/README.md b/tools/mirror/README.md new file mode 100644 index 000000000..d01614f79 --- /dev/null +++ b/tools/mirror/README.md @@ -0,0 +1,53 @@ +## Transaction Mirror + +This is some code that tries to help with the following: We have some +chain, let's call it the "source chain", producing blocks and chunks +with transactions as usual, and we have another chain, let's call it +the "target chain" that starts from state forked from the source +chain. Usually this would be done by using the `uncd view-state +dump-state` command, and using the resulting genesis and records file +as the start of the target chain. What we want is to then periodically +send the transactions appearing in the source chain after the fork +point to the target chain. Ideally, the traffic we see in the target +chain will be very similar to the traffic in the source chain. + +The first approach we might try is to just send the source chain +transactions byte-for-byte unaltered to the target chain. This almost +works, but not quite, because the `block_hash` field in the +transactions will be rejected. This means we have no choice but to +replace the accounts' public keys in the original forked state, so +that we can sign transactions with a valid `block_hash` field. So the +way we'll use this is that we'll generate the forked state from the +source chain using the usual `dump-state` command, and then run: + +``` +$ mirror prepare --records-file-in "~/.near/output/records.json" --records-file-out "~/.near/output/mapped-records.json" +``` + +This command will output a records file where the keys have been +replaced. And then the logic we end up with when running the +transaction generator is something like this: + +``` +loop { + sleep(ONE_SECOND); + source_block = fetch_block(source_chain_view_client, height); + for chunk in block: + for tx in chunk: + private_key = map_key(tx.public_key) + block_hash = fetch_head_hash(target_chain_view_client) + new_tx = sign_tx(private_key, tx.actions, block_hash) + send_tx(target_chain_client, new_tx) +} +``` + +So then the question is what does `map_key()` do?. If we don't care +about the security of these accounts in the target chain (for example +if the target chain is just some throwaway test chain that nobody +would have any incentive to mess with), we can just use the bytes of +the public key directly as the private key. If we do care somewhat +about security, then we pass a `--secret-key-file` argument to the +`prepare` command, and pass it as an argument to `map_key()`. Using +that makes things a little bit more delicate, since if the generated +secret is ever lost, then it will no longer be possible to mirror any +traffic to the target chain. diff --git a/tools/mirror/src/chain_tracker.rs b/tools/mirror/src/chain_tracker.rs new file mode 100644 index 000000000..67a3c6f0d --- /dev/null +++ b/tools/mirror/src/chain_tracker.rs @@ -0,0 +1,1210 @@ +use crate::{ + ChainAccess, ChainError, LatestTargetNonce, MappedBlock, MappedTx, MappedTxProvenance, + NonceUpdater, TargetChainTx, TargetNonce, TxRef, +}; +use actix::Addr; +use anyhow::Context; +use unc_client::ViewClientActor; +use unc_crypto::{PublicKey, SecretKey}; +use unc_indexer::StreamerMessage; +use unc_indexer_primitives::{IndexerExecutionOutcomeWithReceipt, IndexerTransactionWithOutcome}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::transaction::Transaction; +use unc_primitives::types::{AccountId, BlockHeight}; +use unc_primitives::views::{ActionView, ExecutionStatusView, ReceiptEnumView}; +use unc_primitives_core::types::{Gas, Nonce}; +use rocksdb::DB; +use std::cmp::Ordering; +use std::collections::hash_map; +use std::collections::HashMap; +use std::collections::{BTreeSet, HashSet, VecDeque}; +use std::fmt::Write; +use std::pin::Pin; +use std::time::{Duration, Instant}; + +// Information related to a single transaction that we sent in the past. +// We could just forget it and not save any of this, but keeping this info +// makes it easy to print out human-friendly info later on when we find this +// transaction on chain. +struct TxSendInfo { + sent_at: Instant, + source_height: Option, + provenance: MappedTxProvenance, + source_signer_id: AccountId, + source_receiver_id: AccountId, + target_signer_id: Option, + target_receiver_id: Option, + actions: Vec, + sent_at_target_height: BlockHeight, +} + +impl TxSendInfo { + fn new( + tx: &MappedTx, + source_height: Option, + target_height: BlockHeight, + now: Instant, + ) -> Self { + let target_signer_id = if &tx.source_signer_id != &tx.target_tx.transaction.signer_id { + Some(tx.target_tx.transaction.signer_id.clone()) + } else { + None + }; + let target_receiver_id = if &tx.source_receiver_id != &tx.target_tx.transaction.receiver_id + { + Some(tx.target_tx.transaction.receiver_id.clone()) + } else { + None + }; + Self { + source_height, + provenance: tx.provenance, + source_signer_id: tx.source_signer_id.clone(), + source_receiver_id: tx.source_receiver_id.clone(), + target_signer_id, + target_receiver_id, + sent_at: now, + sent_at_target_height: target_height, + actions: tx + .target_tx + .transaction + .actions + .iter() + .map(|a| a.as_ref().to_string()) + .collect::>(), + } + } +} + +#[derive(PartialEq, Eq, Debug)] +struct TxId { + hash: CryptoHash, + nonce: Nonce, +} + +impl PartialOrd for TxId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for TxId { + fn cmp(&self, other: &Self) -> Ordering { + self.nonce.cmp(&other.nonce).then_with(|| self.hash.cmp(&other.hash)) + } +} + +fn gas_pretty(gas: Gas) -> String { + if gas < 1000 { + format!("{} gas", gas) + } else if gas < 1_000_000 { + format!("{} Kgas", gas / 1000) + } else if gas < 1_000_000_000 { + format!("{} Mgas", gas / 1_000_000) + } else if gas < 1_000_000_000_000 { + format!("{} Ggas", gas / 1_000_000_000) + } else { + format!("{} Tgas", gas / 1_000_000_000_000) + } +} + +#[derive(Clone, Debug)] +struct NonceInfo { + target_nonce: TargetNonce, + // the last height we have queued that references this access key. After + // we send the txs at that height, we'll delete this from memory so that + // the amount of memory we're using for these doesn't keep growing as we run for a while + last_height: Option, + txs_awaiting_nonce: BTreeSet, + queued_txs: BTreeSet, +} + +pub(crate) enum SentBatch { + MappedBlock(MappedBlock), + ExtraTxs(Vec), +} + +// Keeps the queue of upcoming transactions and provides them in regular intervals via next_batch() +// Also keeps track of txs we've sent so far and looks for them on chain, for metrics/logging purposes. + +// TODO: the separation between what's in here and what's in the main file with struct TxMirror is not +// that clear and doesn't make that much sense. Should refactor +#[derive(Default)] +pub(crate) struct TxTracker { + sent_txs: HashMap, + txs_by_signer: HashMap<(AccountId, PublicKey), BTreeSet>, + queued_blocks: VecDeque, + // for each updater (a tx or receipt hash, or a queued transaction we haven't sent yet), keeps + // a set of access keys who might be updated by it + updater_to_keys: HashMap>, + nonces: HashMap<(AccountId, PublicKey), NonceInfo>, + next_heights: VecDeque, + height_queued: Option, + // the reason we have these (nonempty_height_queued, height_seen, etc) is so that we can + // exit after we receive the target block containing the txs we sent for the last source block. + // It's a minor thing, but otherwise if we just exit after sending the last source block's txs, + // we won't get to see the resulting txs on chain in the debug logs from log_target_block() + nonempty_height_queued: Option, + height_popped: Option, + height_seen: Option, + send_time: Option>>, + // Config value in the target chain, used to judge how long to wait before sending a new batch of txs + min_block_production_delay: Duration, + // timestamps in the target chain, used to judge how long to wait before sending a new batch of txs + recent_block_timestamps: VecDeque, + // last source block we'll be sending transactions for + stop_height: Option, +} + +impl TxTracker { + // `next_heights` should show the next several valid heights in the chain, starting from + // the first block we want to send txs for. Right now we are assuming this arg is not empty when + // we unwrap() self.height_queued() in Self::next_heights() + pub(crate) fn new<'a, I>( + min_block_production_delay: Duration, + next_heights: I, + stop_height: Option, + ) -> Self + where + I: IntoIterator, + { + let next_heights = next_heights.into_iter().map(Clone::clone).collect(); + Self { min_block_production_delay, next_heights, stop_height, ..Default::default() } + } + + pub(crate) async fn next_heights( + &mut self, + source_chain: &T, + ) -> anyhow::Result<(Option, Option)> { + while self.next_heights.len() <= crate::CREATE_ACCOUNT_DELTA { + // we unwrap() the height_queued because Self::new() should have been called with + // nonempty next_heights. + let h = self + .next_heights + .iter() + .next_back() + .cloned() + .unwrap_or_else(|| self.height_queued.unwrap()); + match source_chain.get_next_block_height(h).await { + Ok(h) => self.next_heights.push_back(h), + Err(ChainError::Unknown) => break, + Err(ChainError::Other(e)) => { + return Err(e) + .with_context(|| format!("failed fetching next height after {}", h)) + } + }; + } + let next_height = self.next_heights.get(0).cloned(); + let create_account_height = self.next_heights.get(crate::CREATE_ACCOUNT_DELTA).cloned(); + Ok((next_height, create_account_height)) + } + + pub(crate) fn has_stop_height(&self) -> bool { + self.stop_height.is_some() + } + + pub(crate) fn finished(&self) -> bool { + match self.stop_height { + Some(_) => { + self.height_popped >= self.stop_height + && self.height_seen >= self.nonempty_height_queued + } + None => false, + } + } + + pub(crate) fn num_blocks_queued(&self) -> usize { + self.queued_blocks.len() + } + + async fn initialize_target_nonce<'a>( + &'a mut self, + target_view_client: &Addr, + db: &DB, + access_key: &(AccountId, PublicKey), + source_height: Option, + ) -> anyhow::Result<()> { + let info = match crate::read_target_nonce(db, &access_key.0, &access_key.1)? { + Some(t) => NonceInfo { + target_nonce: TargetNonce { + nonce: t.nonce, + pending_outcomes: t + .pending_outcomes + .into_iter() + .map(NonceUpdater::ChainObjectId) + .collect(), + }, + last_height: source_height, + txs_awaiting_nonce: BTreeSet::new(), + queued_txs: BTreeSet::new(), + }, + None => { + let nonce = + crate::fetch_access_key_nonce(target_view_client, &access_key.0, &access_key.1) + .await?; + let t = LatestTargetNonce { nonce, pending_outcomes: HashSet::new() }; + crate::put_target_nonce(db, &access_key.0, &access_key.1, &t)?; + NonceInfo { + target_nonce: TargetNonce { nonce: t.nonce, pending_outcomes: HashSet::new() }, + last_height: source_height, + txs_awaiting_nonce: BTreeSet::new(), + queued_txs: BTreeSet::new(), + } + } + }; + self.nonces.insert(access_key.clone(), info); + Ok(()) + } + + async fn read_target_nonce<'a>( + &'a mut self, + target_view_client: &Addr, + db: &DB, + access_key: &(AccountId, PublicKey), + source_height: Option, + ) -> anyhow::Result<&'a mut NonceInfo> { + if !self.nonces.contains_key(access_key) { + self.initialize_target_nonce(target_view_client, db, access_key, source_height).await?; + } + Ok(self.nonces.get_mut(access_key).unwrap()) + } + + pub(crate) async fn next_nonce<'a>( + &'a mut self, + target_view_client: &Addr, + db: &DB, + signer_id: &AccountId, + public_key: &PublicKey, + source_height: BlockHeight, + ) -> anyhow::Result<&'a TargetNonce> { + let source_height = Some(source_height); + let info = self + .read_target_nonce( + target_view_client, + db, + &(signer_id.clone(), public_key.clone()), + source_height, + ) + .await?; + if source_height > info.last_height { + info.last_height = source_height; + } + if let Some(nonce) = &mut info.target_nonce.nonce { + *nonce += 1; + } + Ok(&info.target_nonce) + } + + // normally when we're adding txs, we're adding a tx that + // wants to be sent after all the previous ones for that signer that + // we've already prepared. So next_nonce() returns the biggest nonce + // we've used so far + 1. But if we want to add a tx at the beginning, + // we need to shift all the bigger nonces by one. + pub(crate) async fn insert_nonce( + &mut self, + target_view_client: &Addr, + db: &DB, + signer_id: &AccountId, + public_key: &PublicKey, + secret_key: &SecretKey, + ) -> anyhow::Result { + let access_key = (signer_id.clone(), public_key.clone()); + if !self.nonces.contains_key(&access_key) { + self.initialize_target_nonce(target_view_client, db, &access_key, None).await?; + let info = self.nonces.get_mut(&access_key).unwrap(); + if let Some(nonce) = &mut info.target_nonce.nonce { + *nonce += 1; + } + return Ok(info.target_nonce.clone()); + } + let mut first_nonce = None; + let txs = self.nonces.get(&access_key).unwrap().queued_txs.clone(); + for tx_ref in txs { + let tx = self.get_tx(&tx_ref); + if first_nonce.is_none() { + first_nonce = Some(tx.target_nonce()); + } + tx.inc_target_nonce(secret_key) + } + match first_nonce { + Some(n) => { + if let Some(nonce) = + &mut self.nonces.get_mut(&access_key).unwrap().target_nonce.nonce + { + *nonce += 1; + } + Ok(n) + } + None => { + tracing::warn!(target: "mirror", "info for access key {:?} was cached but there are no upcoming transactions queued for it", &access_key); + Ok(TargetNonce::default()) + } + } + } + + fn get_tx(&mut self, tx_ref: &TxRef) -> &mut TargetChainTx { + let block_idx = self + .queued_blocks + .binary_search_by(|b| b.source_height.cmp(&tx_ref.source_height)) + .unwrap(); + let block = &mut self.queued_blocks[block_idx]; + let chunk = block.chunks.iter_mut().find(|c| c.shard_id == tx_ref.shard_id).unwrap(); + &mut chunk.txs[tx_ref.tx_idx] + } + + async fn insert_access_key_updates( + &mut self, + target_view_client: &Addr, + db: &DB, + tx_ref: &TxRef, + nonce_updates: &HashSet<(AccountId, PublicKey)>, + source_height: BlockHeight, + ) -> anyhow::Result<()> { + let source_height = Some(source_height); + for access_key in nonce_updates.iter() { + let info = + self.read_target_nonce(target_view_client, db, access_key, source_height).await?; + + if info.last_height < source_height { + info.last_height = source_height; + } + info.target_nonce.pending_outcomes.insert(NonceUpdater::TxRef(tx_ref.clone())); + } + if !nonce_updates.is_empty() { + assert!(self + .updater_to_keys + .insert(NonceUpdater::TxRef(tx_ref.clone()), nonce_updates.clone()) + .is_none()); + } + Ok(()) + } + + pub(crate) async fn queue_block( + &mut self, + block: MappedBlock, + target_view_client: &Addr, + db: &DB, + ) -> anyhow::Result<()> { + self.height_queued = Some(block.source_height); + self.next_heights.pop_front().unwrap(); + + for c in block.chunks.iter() { + if !c.txs.is_empty() { + self.nonempty_height_queued = Some(block.source_height); + } + for (tx_idx, tx) in c.txs.iter().enumerate() { + let tx_ref = + TxRef { source_height: block.source_height, shard_id: c.shard_id, tx_idx }; + match tx { + crate::TargetChainTx::Ready(tx) => { + let info = self + .nonces + .get_mut(&( + tx.target_tx.transaction.signer_id.clone(), + tx.target_tx.transaction.public_key.clone(), + )) + .unwrap(); + info.queued_txs.insert(tx_ref.clone()); + self.insert_access_key_updates( + target_view_client, + db, + &tx_ref, + &tx.nonce_updates, + block.source_height, + ) + .await?; + } + crate::TargetChainTx::AwaitingNonce(tx) => { + let info = self + .nonces + .get_mut(&( + tx.target_tx.signer_id.clone(), + tx.target_tx.public_key.clone(), + )) + .unwrap(); + info.txs_awaiting_nonce.insert(tx_ref.clone()); + info.queued_txs.insert(tx_ref.clone()); + self.insert_access_key_updates( + target_view_client, + db, + &tx_ref, + &tx.nonce_updates, + block.source_height, + ) + .await?; + } + }; + } + } + self.queued_blocks.push_back(block); + Ok(()) + } + + pub(crate) fn next_batch_time(&self) -> Instant { + match &self.send_time { + Some(t) => t.as_ref().deadline().into_std(), + None => Instant::now(), + } + } + + pub(crate) async fn next_batch( + &mut self, + target_view_client: &Addr, + db: &DB, + ) -> anyhow::Result { + // sleep until 20 milliseconds before we want to send transactions before we check for nonces + // in the target chain. In the second or so between now and then, we might process another block + // that will set the nonces. + if let Some(s) = &self.send_time { + tokio::time::sleep_until(s.as_ref().deadline() - Duration::from_millis(20)).await; + } + let mut needed_access_keys = HashSet::new(); + for c in self.queued_blocks[0].chunks.iter_mut() { + for tx in c.txs.iter_mut() { + if let TargetChainTx::AwaitingNonce(t) = tx { + needed_access_keys + .insert((t.target_tx.signer_id.clone(), t.target_tx.public_key.clone())); + } + } + } + for access_key in needed_access_keys.iter() { + self.try_set_nonces(target_view_client, db, access_key, None).await?; + } + let block = &mut self.queued_blocks[0]; + self.height_popped = Some(block.source_height); + for c in block.chunks.iter_mut() { + for (tx_idx, tx) in c.txs.iter_mut().enumerate() { + match tx { + TargetChainTx::AwaitingNonce(_) => { + let tx_ref = TxRef { + source_height: block.source_height, + shard_id: c.shard_id, + tx_idx, + }; + tx.try_set_nonce(None); + match tx { + TargetChainTx::Ready(t) => { + tracing::debug!( + target: "mirror", "Prepared {} for ({}, {:?}) with nonce {} even though there are still pending outcomes that may affect the access key", + &t.provenance, &t.target_tx.transaction.signer_id, &t.target_tx.transaction.public_key, t.target_tx.transaction.nonce + ); + self.nonces + .get_mut(&( + t.target_tx.transaction.signer_id.clone(), + t.target_tx.transaction.public_key.clone(), + )) + .unwrap() + .txs_awaiting_nonce + .remove(&tx_ref); + } + TargetChainTx::AwaitingNonce(t) => { + tracing::warn!( + target: "mirror", "Could not prepare {} for ({}, {:?}). Nonce unknown", + &t.provenance, &t.target_tx.signer_id, &t.target_tx.public_key, + ); + self.nonces + .get_mut(&( + t.target_tx.signer_id.clone(), + t.target_tx.public_key.clone(), + )) + .unwrap() + .txs_awaiting_nonce + .remove(&tx_ref); + } + }; + } + TargetChainTx::Ready(_) => {} + }; + } + } + if let Some(sleep) = &mut self.send_time { + sleep.await; + } + Ok(self.queued_blocks.pop_front().unwrap()) + } + + fn remove_tx(&mut self, tx: &IndexerTransactionWithOutcome) { + let k = (tx.transaction.signer_id.clone(), tx.transaction.public_key.clone()); + match self.txs_by_signer.entry(k.clone()) { + hash_map::Entry::Occupied(mut e) => { + let txs = e.get_mut(); + if !txs.remove(&TxId { hash: tx.transaction.hash, nonce: tx.transaction.nonce }) { + tracing::warn!(target: "mirror", "tried to remove nonexistent tx {} from txs_by_signer", tx.transaction.hash); + } + // split off from hash: default() since that's the smallest hash, which will leave us with every tx with nonce + // greater than this one in txs_left. + let txs_left = txs.split_off(&TxId { + hash: CryptoHash::default(), + nonce: tx.transaction.nonce + 1, + }); + if !txs.is_empty() { + tracing::warn!( + target: "mirror", "{} Transactions for {:?} skipped by inclusion of tx with nonce {}: {:?}. These will never make it on chain.", + txs.len(), &k, tx.transaction.nonce, &txs + ); + for t in txs.iter() { + if self.sent_txs.remove(&t.hash).is_none() { + tracing::warn!( + target: "mirror", "tx with hash {} that we thought was skipped is not in the set of sent txs", + &t.hash, + ); + } + } + } + *txs = txs_left; + if txs.is_empty() { + self.txs_by_signer.remove(&k); + } + } + hash_map::Entry::Vacant(_) => { + tracing::warn!( + target: "mirror", "recently removed tx {}, but ({:?}, {:?}) not in txs_by_signer", + tx.transaction.hash, tx.transaction.signer_id, tx.transaction.public_key + ); + return; + } + }; + } + + fn record_block_timestamp(&mut self, msg: &StreamerMessage) { + self.recent_block_timestamps.push_back(msg.block.header.timestamp_nanosec); + if self.recent_block_timestamps.len() > 10 { + self.recent_block_timestamps.pop_front(); + } + } + + fn log_target_block(&self, msg: &StreamerMessage) { + // don't do any work here if we're definitely not gonna log it + if tracing::level_filters::LevelFilter::current() + > tracing::level_filters::LevelFilter::DEBUG + { + return; + } + + // right now we're just logging this, but it would be nice to collect/index this + // and have some HTTP debug page where you can see how close the target chain is + // to the source chain + let mut log_message = String::new(); + let now = Instant::now(); + + for s in msg.shards.iter() { + let mut other_txs = 0; + if let Some(c) = &s.chunk { + if c.header.height_included == msg.block.header.height { + write!( + log_message, + "-------- shard {} gas used: {} ---------\n", + s.shard_id, + gas_pretty(c.header.gas_used) + ) + .unwrap(); + for tx in c.transactions.iter() { + if let Some(info) = self.sent_txs.get(&tx.transaction.hash) { + write!( + log_message, + "{} signer: \"{}\"{} receiver: \"{}\"{} actions: <{}> sent {:?} ago @ target #{}\n", + info.provenance, + info.source_signer_id, + info.target_signer_id.as_ref().map_or(String::new(), |s| format!(" (mapped to \"{}\")", s)), + info.source_receiver_id, + info.target_receiver_id.as_ref().map_or(String::new(), |s| format!(" (mapped to \"{}\")", s)), + info.actions.join(", "), + now - info.sent_at, + info.sent_at_target_height, + ).unwrap(); + } else { + other_txs += 1; + } + } + } else { + write!( + log_message, + "-------- shard {} old chunk (#{}) ---------\n", + s.shard_id, c.header.height_included + ) + .unwrap(); + } + } else { + write!(log_message, "-------- shard {} chunk missing ---------\n", s.shard_id) + .unwrap(); + } + if other_txs > 0 { + write!(log_message, " ... \n").unwrap(); + write!( + log_message, + "{} other txs (not ours, or sent before a restart)\n", + other_txs + ) + .unwrap(); + write!(log_message, " ... \n").unwrap(); + } + } + tracing::debug!(target: "mirror", "received target block #{}:\n{}", msg.block.header.height, log_message); + } + + fn tx_to_receipt( + &mut self, + db: &DB, + tx_hash: &CryptoHash, + receipt_id: &CryptoHash, + access_keys: HashSet<(AccountId, PublicKey)>, + ) -> anyhow::Result<()> { + crate::delete_pending_outcome(db, tx_hash)?; + + let updater = NonceUpdater::ChainObjectId(*tx_hash); + if let Some(keys) = self.updater_to_keys.remove(&updater) { + assert!(access_keys == keys); + } + + let new_updater = NonceUpdater::ChainObjectId(*receipt_id); + + for access_key in access_keys.iter() { + let mut n = crate::read_target_nonce(db, &access_key.0, &access_key.1)?.unwrap(); + assert!(n.pending_outcomes.remove(tx_hash)); + n.pending_outcomes.insert(*receipt_id); + crate::put_target_nonce(db, &access_key.0, &access_key.1, &n)?; + + if let Some(info) = self.nonces.get(access_key) { + let txs_awaiting_nonce = info.txs_awaiting_nonce.clone(); + + for r in txs_awaiting_nonce.iter() { + let tx = self.get_tx(r); + + match tx { + TargetChainTx::AwaitingNonce(t) => { + assert!(t.target_nonce.pending_outcomes.remove(&updater)); + t.target_nonce.pending_outcomes.insert(new_updater.clone()); + } + TargetChainTx::Ready(_) => unreachable!(), + }; + } + + let info = self.nonces.get_mut(access_key).unwrap(); + assert!(info.target_nonce.pending_outcomes.remove(&updater)); + info.target_nonce.pending_outcomes.insert(new_updater.clone()); + } + } + crate::put_pending_outcome(db, *receipt_id, access_keys.clone())?; + if !access_keys.is_empty() { + self.updater_to_keys.insert(new_updater, access_keys); + } + Ok(()) + } + + async fn try_set_nonces( + &mut self, + target_view_client: &Addr, + db: &DB, + access_key: &(AccountId, PublicKey), + id: Option<&CryptoHash>, + ) -> anyhow::Result<()> { + let mut n = crate::read_target_nonce(db, &access_key.0, &access_key.1)?.unwrap(); + if let Some(id) = id { + n.pending_outcomes.remove(id); + } + let mut nonce = + crate::fetch_access_key_nonce(target_view_client, &access_key.0, &access_key.1).await?; + n.nonce = std::cmp::max(n.nonce, nonce); + + crate::put_target_nonce(db, &access_key.0, &access_key.1, &n)?; + + let updater = id.map(|id| NonceUpdater::ChainObjectId(*id)); + if let Some(info) = self.nonces.get_mut(access_key) { + if let Some(updater) = &updater { + info.target_nonce.pending_outcomes.remove(updater); + } + let txs_awaiting_nonce = info.txs_awaiting_nonce.clone(); + let mut to_remove = Vec::new(); + + for r in txs_awaiting_nonce.iter() { + let tx = self.get_tx(r); + + match tx { + TargetChainTx::AwaitingNonce(t) => { + if let Some(updater) = &updater { + t.target_nonce.pending_outcomes.remove(updater); + } + if let Some(nonce) = &mut nonce { + *nonce += 1; + } + + if t.target_nonce.pending_outcomes.is_empty() { + to_remove.push(r.clone()); + tx.try_set_nonce(nonce); + match tx { + TargetChainTx::Ready(t) => { + tracing::debug!(target: "mirror", "set nonce for {:?}'s {} to {}", access_key, r, t.target_tx.transaction.nonce); + } + _ => { + tracing::warn!(target: "mirror", "Couldn't set nonce for {:?}'s {}", access_key, r); + } + } + } else { + t.target_nonce.nonce = std::cmp::max(t.target_nonce.nonce, nonce); + } + } + TargetChainTx::Ready(_) => unreachable!(), + }; + } + + let info = self.nonces.get_mut(access_key).unwrap(); + for r in to_remove.iter() { + info.txs_awaiting_nonce.remove(r); + } + info.target_nonce.nonce = std::cmp::max(info.target_nonce.nonce, nonce); + } + Ok(()) + } + + async fn on_outcome_finished( + &mut self, + target_view_client: &Addr, + db: &DB, + id: &CryptoHash, + access_keys: HashSet<(AccountId, PublicKey)>, + ) -> anyhow::Result<()> { + let updater = NonceUpdater::ChainObjectId(*id); + if let Some(keys) = self.updater_to_keys.remove(&updater) { + assert!(access_keys == keys); + } + + for access_key in access_keys.iter() { + self.try_set_nonces(target_view_client, db, &access_key, Some(id)).await?; + } + crate::delete_pending_outcome(db, id) + } + + async fn on_target_block_tx( + &mut self, + target_view_client: &Addr, + db: &DB, + tx: IndexerTransactionWithOutcome, + ) -> anyhow::Result<()> { + if let Some(info) = self.sent_txs.remove(&tx.transaction.hash) { + crate::metrics::TRANSACTIONS_INCLUDED.inc(); + self.remove_tx(&tx); + if info.source_height > self.height_seen { + self.height_seen = info.source_height; + } + } + if let Some(access_keys) = crate::read_pending_outcome(db, &tx.transaction.hash)? { + match tx.outcome.execution_outcome.outcome.status { + ExecutionStatusView::SuccessReceiptId(receipt_id) => { + self.tx_to_receipt(db, &tx.transaction.hash, &receipt_id, access_keys)? + } + ExecutionStatusView::SuccessValue(_) | ExecutionStatusView::Unknown => { + unreachable!() + } + ExecutionStatusView::Failure(_) => { + self.on_outcome_finished( + target_view_client, + db, + &tx.transaction.hash, + access_keys, + ) + .await? + } + } + } + Ok(()) + } + + async fn on_target_block_applied_receipt( + &mut self, + target_view_client: &Addr, + db: &DB, + outcome: IndexerExecutionOutcomeWithReceipt, + staked_accounts: &mut HashMap<(AccountId, PublicKey), AccountId>, + ) -> anyhow::Result<()> { + let access_keys = match crate::read_pending_outcome(db, &outcome.execution_outcome.id)? { + Some(a) => a, + None => return Ok(()), + }; + + self.on_outcome_finished( + target_view_client, + db, + &outcome.execution_outcome.id, + access_keys, + ) + .await?; + for receipt_id in outcome.execution_outcome.outcome.receipt_ids { + // we don't carry over the access keys here, because we set pending access keys when we send a tx with + // an add key action, which should be applied after one receipt. Setting empty access keys here allows us + // to keep track of which receipts are descendants of our transactions, so that we can reverse any stake actions + crate::put_pending_outcome(db, receipt_id, HashSet::default())?; + } + if !crate::execution_status_good(&outcome.execution_outcome.outcome.status) { + return Ok(()); + } + match outcome.receipt.receipt { + ReceiptEnumView::Action { actions, .. } => { + // since this receipt was recorded in the DB, it means that this stake action + // resulted from one of our txs (not an external/manual stake action by the test operator), + // so we want to reverse it. + for a in actions { + if let ActionView::Stake { public_key, stake } = a { + if stake > 0 { + staked_accounts.insert( + (outcome.receipt.receiver_id.clone(), public_key), + outcome.receipt.predecessor_id.clone(), + ); + } + } + } + } + _ => {} + }; + Ok(()) + } + + // return value maps (receiver_id, staked public key) to the predecessor_id in the + // receipt for any receipts that contain stake actions (w/ nonzero stake) that were + // generated by our transactions. Then the caller will send extra stake transactions + // to reverse those. + pub(crate) async fn on_target_block( + &mut self, + target_view_client: &Addr, + db: &DB, + msg: StreamerMessage, + ) -> anyhow::Result> { + self.record_block_timestamp(&msg); + self.log_target_block(&msg); + + let mut staked_accounts = HashMap::new(); + for s in msg.shards { + if let Some(c) = s.chunk { + for tx in c.transactions { + self.on_target_block_tx(target_view_client, db, tx).await?; + } + for outcome in s.receipt_execution_outcomes { + self.on_target_block_applied_receipt( + target_view_client, + db, + outcome, + &mut staked_accounts, + ) + .await?; + } + } + } + Ok(staked_accounts) + } + + async fn on_tx_sent( + &mut self, + db: &DB, + tx_ref: Option, + tx: MappedTx, + target_height: BlockHeight, + now: Instant, + access_keys_to_remove: &mut HashSet<(AccountId, PublicKey)>, + ) -> anyhow::Result<()> { + let hash = tx.target_tx.get_hash(); + if self.sent_txs.contains_key(&hash) { + tracing::warn!(target: "mirror", "transaction sent twice: {}", &hash); + return Ok(()); + } + + if tx.provenance.is_add_key() + || tx.provenance.is_create_account() + || tx.provenance.is_unstake() + { + tracing::debug!( + target: "mirror", "Successfully sent transaction {} for {}: {:?}", + &hash, &tx.provenance, &tx.target_tx.transaction.actions, + ); + } + let access_key = ( + tx.target_tx.transaction.signer_id.clone(), + tx.target_tx.transaction.public_key.clone(), + ); + let source_height = tx_ref.as_ref().map(|t| t.source_height); + // TODO: don't keep adding txs if we're not ever finding them on chain, since we'll OOM eventually + // if that happens. + self.sent_txs.insert(hash, TxSendInfo::new(&tx, source_height, target_height, now)); + let txs = self.txs_by_signer.entry(access_key.clone()).or_default(); + + if let Some(highest_nonce) = txs.iter().next_back() { + if highest_nonce.nonce > tx.target_tx.transaction.nonce { + tracing::warn!( + target: "mirror", "transaction sent with out of order nonce: {}: {}. Sent so far: {:?}", + &hash, tx.target_tx.transaction.nonce, txs + ); + } + } + if !txs.insert(TxId { hash, nonce: tx.target_tx.transaction.nonce }) { + tracing::warn!(target: "mirror", "inserted tx {} twice into txs_by_signer", &hash); + } + + match &tx_ref { + Some(tx_ref) => { + let updater = NonceUpdater::TxRef(tx_ref.clone()); + let new_updater = NonceUpdater::ChainObjectId(hash); + assert!( + &tx.nonce_updates == &self.updater_to_keys.remove(&updater).unwrap_or_default() + ); + for access_key in tx.nonce_updates.iter() { + let mut t = + crate::read_target_nonce(db, &access_key.0, &access_key.1)?.unwrap(); + t.pending_outcomes.insert(hash); + crate::put_target_nonce(db, &access_key.0, &access_key.1, &t)?; + + let info = self.nonces.get_mut(access_key).unwrap(); + assert!(info.target_nonce.pending_outcomes.remove(&updater)); + info.target_nonce.pending_outcomes.insert(new_updater.clone()); + let txs_awaiting_nonce = info.txs_awaiting_nonce.clone(); + if info.last_height <= source_height { + access_keys_to_remove.insert(access_key.clone()); + } + + for r in txs_awaiting_nonce.iter() { + let t = self.get_tx(r); + + match t { + TargetChainTx::AwaitingNonce(t) => { + assert!(t.target_nonce.pending_outcomes.remove(&updater)); + t.target_nonce.pending_outcomes.insert(new_updater.clone()); + } + TargetChainTx::Ready(_) => unreachable!(), + }; + } + } + } + None => { + // if tx_ref is None, it was an extra tx we sent to unstake an unwanted validator, + // and there should be no access key updates + assert!(tx.nonce_updates.is_empty()); + } + } + + crate::put_pending_outcome(db, hash, tx.nonce_updates)?; + + let mut t = crate::read_target_nonce( + db, + &tx.target_tx.transaction.signer_id, + &tx.target_tx.transaction.public_key, + )? + .unwrap(); + t.nonce = std::cmp::max(t.nonce, Some(tx.target_tx.transaction.nonce)); + crate::put_target_nonce( + db, + &tx.target_tx.transaction.signer_id, + &tx.target_tx.transaction.public_key, + &t, + )?; + let info = self.nonces.get_mut(&access_key).unwrap(); + if info.last_height <= source_height { + access_keys_to_remove.insert(access_key); + } + if let Some(tx_ref) = tx_ref { + assert!(info.queued_txs.remove(&tx_ref)); + } + Ok(()) + } + + // among the last 10 blocks, what's the second longest time between their timestamps? + // probably there's a better heuristic to use than that but this will do for now. + // TODO: it's possible these tiimestamps are just increasing by one nanosecond each time + // if block producers' clocks are off. should handle that case + fn second_longest_recent_block_delay(&self) -> Option { + if self.recent_block_timestamps.len() < 5 { + return None; + } + let mut last = *self.recent_block_timestamps.front().unwrap(); + let mut longest = None; + let mut second_longest = None; + + for timestamp in self.recent_block_timestamps.iter().skip(1) { + let delay = timestamp - last; + + match longest { + Some(l) => match second_longest { + Some(s) => { + if delay > l { + second_longest = longest; + longest = Some(delay); + } else if delay > s { + second_longest = Some(delay); + } + } + None => { + if delay > l { + second_longest = longest; + longest = Some(delay); + } else { + second_longest = Some(delay); + } + } + }, + None => { + longest = Some(delay); + } + } + last = *timestamp; + } + let delay = Duration::from_nanos(second_longest.unwrap()); + if delay > 2 * self.min_block_production_delay { + tracing::warn!( + "Target chain blocks are taking longer than expected to be produced. Observing delays \ + of {:?} and {:?} vs min_block_production_delay of {:?} ", + delay, + Duration::from_nanos(longest.unwrap()), + self.min_block_production_delay, + ) + } + Some(delay) + } + + fn on_tx_skipped( + &mut self, + tx_ref: &Option, + tx: &Transaction, + nonce_updates: &HashSet<(AccountId, PublicKey)>, + access_keys_to_remove: &mut HashSet<(AccountId, PublicKey)>, + ) -> anyhow::Result<()> { + let tx_ref = match tx_ref { + Some(t) => t, + None => return Ok(()), + }; + let updater = NonceUpdater::TxRef(tx_ref.clone()); + if let Some(keys) = self.updater_to_keys.remove(&updater) { + assert!(&keys == nonce_updates); + + for access_key in keys { + if let Some(info) = self.nonces.get(&access_key) { + let txs_awaiting_nonce = info.txs_awaiting_nonce.clone(); + let mut to_remove = Vec::new(); + for r in txs_awaiting_nonce.iter() { + let target_tx = self.get_tx(r); + match target_tx { + TargetChainTx::AwaitingNonce(tx) => { + assert!(tx.target_nonce.pending_outcomes.remove(&updater)); + if tx.target_nonce.pending_outcomes.is_empty() { + target_tx.try_set_nonce(None); + match target_tx { + TargetChainTx::Ready(t) => { + tracing::debug!(target: "mirror", "After skipping {} setting nonce for {:?}'s {} to {}", tx_ref, &access_key, r, t.target_tx.transaction.nonce); + } + _ => { + tracing::warn!(target: "mirror", "After skipping {} could not set nonce for {:?}'s {}", tx_ref, &access_key, r); + } + } + to_remove.push(r.clone()); + } + } + TargetChainTx::Ready(_) => unreachable!(), + } + } + + let info = self.nonces.get_mut(&access_key).unwrap(); + for r in to_remove.iter() { + info.txs_awaiting_nonce.remove(r); + } + + if info.last_height <= Some(tx_ref.source_height) { + access_keys_to_remove.insert(access_key); + } + } + } + } + let access_key = (tx.signer_id.clone(), tx.public_key.clone()); + let info = self.nonces.get_mut(&access_key).unwrap(); + if info.last_height <= Some(tx_ref.source_height) { + access_keys_to_remove.insert(access_key); + } + assert!(info.queued_txs.remove(tx_ref)); + Ok(()) + } + + // We just successfully sent some transactions. Remember them so we can see if they really show up on chain. + pub(crate) async fn on_txs_sent( + &mut self, + db: &DB, + sent_batch: SentBatch, + target_height: BlockHeight, + ) -> anyhow::Result<()> { + let mut total_sent = 0; + let now = Instant::now(); + let mut access_keys_to_remove = HashSet::new(); + + let (txs_sent, provenance) = match sent_batch { + SentBatch::MappedBlock(b) => { + let block_delay = self + .second_longest_recent_block_delay() + .unwrap_or(self.min_block_production_delay + Duration::from_millis(100)); + match &mut self.send_time { + Some(t) => t.as_mut().reset(tokio::time::Instant::now() + block_delay), + None => { + self.send_time = Some(Box::pin(tokio::time::sleep(block_delay))); + } + } + crate::set_last_source_height(db, b.source_height)?; + let txs = b + .chunks + .into_iter() + .flat_map(|c| { + c.txs.into_iter().enumerate().map(move |(tx_idx, tx)| { + ( + Some(TxRef { + source_height: b.source_height, + shard_id: c.shard_id, + tx_idx, + }), + tx, + ) + }) + }) + .collect::>(); + (txs, format!("source #{}", b.source_height)) + } + SentBatch::ExtraTxs(txs) => ( + txs.into_iter().map(|tx| (None, tx)).collect::>(), + String::from("extra unstake transactions"), + ), + }; + for (tx_ref, tx) in txs_sent { + match tx { + crate::TargetChainTx::Ready(t) => { + if t.sent_successfully { + self.on_tx_sent( + db, + tx_ref, + t, + target_height, + now, + &mut access_keys_to_remove, + ) + .await?; + total_sent += 1; + } else { + self.on_tx_skipped( + &tx_ref, + &t.target_tx.transaction, + &t.nonce_updates, + &mut access_keys_to_remove, + )?; + } + } + crate::TargetChainTx::AwaitingNonce(t) => { + self.on_tx_skipped( + &tx_ref, + &t.target_tx, + &t.nonce_updates, + &mut access_keys_to_remove, + )?; + } + } + } + + for access_key in access_keys_to_remove { + assert!(self.nonces.remove(&access_key).is_some()); + } + tracing::info!( + target: "mirror", "Sent {} transactions from {} with target HEAD @ #{}", + total_sent, provenance, target_height + ); + + Ok(()) + } +} diff --git a/tools/mirror/src/cli.rs b/tools/mirror/src/cli.rs new file mode 100644 index 000000000..d4a208f9e --- /dev/null +++ b/tools/mirror/src/cli.rs @@ -0,0 +1,153 @@ +use anyhow::Context; +use unc_primitives::types::BlockHeight; +use std::cell::Cell; +use std::path::PathBuf; + +#[derive(clap::Parser)] +pub struct MirrorCommand { + #[clap(subcommand)] + subcmd: SubCommand, +} + +#[derive(clap::Parser)] +enum SubCommand { + Prepare(PrepareCmd), + Run(RunCmd), +} + +/// initialize a target chain with genesis records from the source chain, and +/// then try to mirror transactions from the source chain to the target chain. +#[derive(clap::Parser)] +struct RunCmd { + /// source chain home dir + #[clap(long)] + source_home: PathBuf, + /// target chain home dir + #[clap(long)] + target_home: PathBuf, + /// file containing an optional secret as generated by the + /// `prepare` command. Must be provided unless --no-secret is given + #[clap(long)] + secret_file: Option, + /// Equivalent to passing --secret-file where is a + /// config that indicates no secret should be used. If this is + /// given, and --secret-file is also given and points to a config + /// that does contain a secret, the mirror will refuse to start + #[clap(long)] + no_secret: bool, + /// Start a NEAR node for the source chain, instead of only using + /// whatever's currently stored in --source-home + #[clap(long)] + online_source: bool, + /// If provided, we will stop after sending transactions coming from + /// this height in the source chain + #[clap(long)] + stop_height: Option, +} + +impl RunCmd { + fn run(self) -> anyhow::Result<()> { + openssl_probe::init_ssl_cert_env_vars(); + let runtime = tokio::runtime::Runtime::new().context("failed to start tokio runtime")?; + + let secret = if let Some(secret_file) = &self.secret_file { + let secret = crate::secret::load(secret_file) + .with_context(|| format!("Failed to load secret from {:?}", secret_file))?; + if secret.is_some() && self.no_secret { + anyhow::bail!( + "--no-secret given with --secret-file indicating that a secret should be used" + ); + } + secret + } else { + if !self.no_secret { + anyhow::bail!("Please give either --secret-file or --no-secret"); + } + None + }; + + let system = new_actix_system(runtime); + system + .block_on(async move { + let _subscriber_guard = unc_o11y::default_subscriber( + unc_o11y::EnvFilterBuilder::from_env().finish().unwrap(), + &unc_o11y::Options::default(), + ) + .global(); + actix::spawn(crate::run( + self.source_home, + self.target_home, + secret, + self.stop_height, + self.online_source, + )) + .await + }) + .unwrap() + } +} + +/// Write a new genesis records file where the public keys have been +/// altered so that this binary can sign transactions when mirroring +/// them from the source chain to the target chain +#[derive(clap::Parser)] +struct PrepareCmd { + /// A genesis records file as output by `uncd view-state + /// dump-state --stream` + #[clap(long)] + records_file_in: PathBuf, + /// Path to the new records file with updated public keys + #[clap(long)] + records_file_out: PathBuf, + /// If this is provided, don't use a secret when mapping public + /// keys to new source chain private keys. This means that anyone + /// will be able to sign transactions for the accounts in the + /// target chain corresponding to accounts in the source chain. If + /// that is okay, then --no-secret will make the code run slightly + /// faster, and you won't have to take care to not lose the + /// secret. + #[clap(long)] + no_secret: bool, + /// Path to the secret. Note that if you don't pass --no-secret, + /// this secret is required to sign transactions for the accounts + /// in the target chain corresponding to accounts in the source + /// chain. This means that if you lose this secret, you will no + /// longer be able to mirror any traffic. + #[clap(long)] + secret_file_out: PathBuf, +} + +impl PrepareCmd { + fn run(self) -> anyhow::Result<()> { + crate::genesis::map_records( + &self.records_file_in, + &self.records_file_out, + self.no_secret, + &self.secret_file_out, + ) + } +} + +// copied from uncd/src/cli.rs +fn new_actix_system(runtime: tokio::runtime::Runtime) -> actix::SystemRunner { + // `with_tokio_rt()` accepts an `Fn()->Runtime`, however we know that this function is called exactly once. + // This makes it safe to move out of the captured variable `runtime`, which is done by a trick + // using a `swap` of `Cell>`s. + let runtime_cell = Cell::new(Some(runtime)); + actix::System::with_tokio_rt(|| { + let r = Cell::new(None); + runtime_cell.swap(&r); + r.into_inner().unwrap() + }) +} + +impl MirrorCommand { + pub fn run(self) -> anyhow::Result<()> { + tracing::warn!(target: "mirror", "the mirror command is not stable, and may be removed or changed arbitrarily at any time"); + + match self.subcmd { + SubCommand::Prepare(r) => r.run(), + SubCommand::Run(r) => r.run(), + } + } +} diff --git a/tools/mirror/src/genesis.rs b/tools/mirror/src/genesis.rs new file mode 100644 index 000000000..b9e1c6319 --- /dev/null +++ b/tools/mirror/src/genesis.rs @@ -0,0 +1,119 @@ +use unc_primitives::state_record::StateRecord; +use unc_primitives_core::account::id::AccountType; +use unc_primitives_core::account::{AccessKey, AccessKeyPermission}; +use serde::ser::{SerializeSeq, Serializer}; +use std::collections::HashSet; +use std::fs::File; +use std::io::{BufReader, BufWriter}; +use std::path::Path; + +/// Reads records, makes changes to them and writes them to a new file. +/// `records_file_in` must be different from `records_file_out`. +/// Writes a secret to `secret_file_out`. +pub fn map_records>( + records_file_in: P, + records_file_out: P, + no_secret: bool, + secret_file_out: P, +) -> anyhow::Result<()> { + let secret = if no_secret { + crate::secret::write_empty(secret_file_out)?; + None + } else { + Some(crate::secret::generate(secret_file_out)?) + }; + let reader = BufReader::new(File::open(records_file_in)?); + let records_out = BufWriter::new(File::create(records_file_out)?); + let mut records_ser = serde_json::Serializer::new(records_out); + let mut records_seq = records_ser.serialize_seq(None).unwrap(); + + let mut has_full_key = HashSet::new(); + let mut accounts = HashSet::new(); + + unc_chain_configs::stream_records_from_file(reader, |mut r| { + match &mut r { + StateRecord::AccessKey { account_id, public_key, access_key } => { + let replacement = crate::key_mapping::map_key(&public_key, secret.as_ref()); + let new_record = StateRecord::AccessKey { + account_id: crate::key_mapping::map_account(&account_id, secret.as_ref()), + public_key: replacement.public_key(), + access_key: access_key.clone(), + }; + // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. + if account_id.get_account_type() != AccountType::NearImplicitAccount + && access_key.permission == AccessKeyPermission::FullAccess + { + has_full_key.insert(account_id.clone()); + } + // TODO: would be nice for stream_records_from_file() to let you return early on error so + // we dont have to unwrap here + records_seq.serialize_element(&new_record).unwrap(); + } + StateRecord::Account { account_id, .. } => { + // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. + if account_id.get_account_type() == AccountType::NearImplicitAccount { + *account_id = crate::key_mapping::map_account(&account_id, secret.as_ref()); + } else { + accounts.insert(account_id.clone()); + } + records_seq.serialize_element(&r).unwrap(); + } + StateRecord::Data { account_id, .. } => { + // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. + if account_id.get_account_type() == AccountType::NearImplicitAccount { + *account_id = crate::key_mapping::map_account(&account_id, secret.as_ref()); + } + records_seq.serialize_element(&r).unwrap(); + } + StateRecord::Contract { account_id, .. } => { + // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. + if account_id.get_account_type() == AccountType::NearImplicitAccount { + *account_id = crate::key_mapping::map_account(&account_id, secret.as_ref()); + } + records_seq.serialize_element(&r).unwrap(); + } + StateRecord::PostponedReceipt(receipt) => { + // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. + if receipt.predecessor_id.get_account_type() == AccountType::NearImplicitAccount + || receipt.receiver_id.get_account_type() == AccountType::NearImplicitAccount + { + receipt.predecessor_id = + crate::key_mapping::map_account(&receipt.predecessor_id, secret.as_ref()); + receipt.receiver_id = + crate::key_mapping::map_account(&receipt.receiver_id, secret.as_ref()); + } + records_seq.serialize_element(&r).unwrap(); + } + StateRecord::ReceivedData { account_id, .. } => { + // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. + if account_id.get_account_type() == AccountType::NearImplicitAccount { + *account_id = crate::key_mapping::map_account(&account_id, secret.as_ref()); + } + records_seq.serialize_element(&r).unwrap(); + } + StateRecord::DelayedReceipt(receipt) => { + // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. + if receipt.predecessor_id.get_account_type() == AccountType::NearImplicitAccount + || receipt.receiver_id.get_account_type() == AccountType::NearImplicitAccount + { + receipt.predecessor_id = + crate::key_mapping::map_account(&receipt.predecessor_id, secret.as_ref()); + receipt.receiver_id = + crate::key_mapping::map_account(&receipt.receiver_id, secret.as_ref()); + } + records_seq.serialize_element(&r).unwrap(); + } + }; + })?; + for account_id in accounts { + if !has_full_key.contains(&account_id) { + records_seq.serialize_element(&StateRecord::AccessKey { + account_id, + public_key: crate::key_mapping::EXTRA_KEY.public_key(), + access_key: AccessKey::full_access(), + })?; + } + } + records_seq.end()?; + Ok(()) +} diff --git a/tools/mirror/src/key_mapping.rs b/tools/mirror/src/key_mapping.rs new file mode 100644 index 000000000..8e1e5f0ff --- /dev/null +++ b/tools/mirror/src/key_mapping.rs @@ -0,0 +1,119 @@ +use hkdf::Hkdf; +use unc_crypto::{ED25519PublicKey, ED25519SecretKey, PublicKey, Secp256K1PublicKey, SecretKey}; +use unc_primitives::types::AccountId; +use unc_primitives::utils::derive_unc_implicit_account_id; +use unc_primitives_core::account::id::AccountType; +use sha2::Sha256; + +// there is nothing special about this key, it's just some randomly generated one. +// We will ensure that every account in the target chain has at least one full access +// key by adding this one (when preparing the records file, or when sending a create account tx) +// if one doesn't exist +pub const EXTRA_KEY: SecretKey = SecretKey::ED25519(ED25519SecretKey([ + 213, 175, 27, 65, 239, 63, 64, 126, 187, 96, 90, 207, 42, 75, 1, 199, 109, 5, 0, 67, 207, 80, + 147, 19, 53, 126, 142, 30, 162, 168, 97, 155, 119, 161, 145, 134, 247, 30, 152, 37, 178, 129, + 174, 62, 225, 47, 43, 131, 212, 59, 200, 4, 158, 143, 3, 235, 237, 190, 51, 82, 253, 38, 36, + 145, +])); + +fn ed25519_map_secret( + buf: &mut [u8], + public: &ED25519PublicKey, + secret: Option<&[u8; crate::secret::SECRET_LEN]>, +) { + match secret { + Some(secret) => { + let hk = Hkdf::::new(None, secret); + hk.expand(&public.0, buf).unwrap(); + } + None => { + buf.copy_from_slice(&public.0); + } + }; +} + +fn map_ed25519( + public: &ED25519PublicKey, + secret: Option<&[u8; crate::secret::SECRET_LEN]>, +) -> ED25519SecretKey { + let mut buf = [0; ed25519_dalek::KEYPAIR_LENGTH]; + + ed25519_map_secret(&mut buf[..ed25519_dalek::SECRET_KEY_LENGTH], public, secret); + + let secret_key = ed25519_dalek::SigningKey::from_bytes( + <&[u8; ed25519_dalek::SECRET_KEY_LENGTH]>::try_from( + &buf[..ed25519_dalek::SECRET_KEY_LENGTH], + ) + .unwrap(), + ); + let public_key = ed25519_dalek::VerifyingKey::from(&secret_key); + + buf[ed25519_dalek::SECRET_KEY_LENGTH..].copy_from_slice(public_key.as_bytes()); + ED25519SecretKey(buf) +} + +fn secp256k1_from_slice(buf: &mut [u8], public: &Secp256K1PublicKey) -> secp256k1::SecretKey { + match secp256k1::SecretKey::from_slice(buf) { + Ok(s) => s, + Err(_) => { + tracing::warn!(target: "mirror", "Something super unlikely occurred! SECP256K1 key mapped from {:?} is too large. Flipping most significant bit.", public); + // If we got an error, it means that either `buf` is all zeros, or that when interpreted as a 256-bit + // int, it is larger than the order of the secp256k1 curve. Since the order of the curve starts with 0xFF, + // in either case flipping the first bit should work, and we can unwrap() below. + buf[0] ^= 0x80; + secp256k1::SecretKey::from_slice(buf).unwrap() + } + } +} + +fn map_secp256k1( + public: &Secp256K1PublicKey, + secret: Option<&[u8; crate::secret::SECRET_LEN]>, +) -> secp256k1::SecretKey { + let mut buf = [0; secp256k1::constants::SECRET_KEY_SIZE]; + + match secret { + Some(secret) => { + let hk = Hkdf::::new(None, secret); + hk.expand(public.as_ref(), &mut buf).unwrap(); + } + None => { + buf.copy_from_slice(&public.as_ref()[..secp256k1::constants::SECRET_KEY_SIZE]); + } + }; + + secp256k1_from_slice(&mut buf, public) +} + +// This maps the public key to a secret key so that we can sign +// transactions on the target chain. If secret is None, then we just +// use the bytes of the public key directly, otherwise we feed the +// public key to a key derivation function. +pub fn map_key(key: &PublicKey, secret: Option<&[u8; crate::secret::SECRET_LEN]>) -> SecretKey { + match key { + PublicKey::ED25519(k) => SecretKey::ED25519(map_ed25519(k, secret)), + PublicKey::SECP256K1(k) => SecretKey::SECP256K1(map_secp256k1(k, secret)), + PublicKey::RSA(_) => panic!("RSA keys not supported"), + } +} + +// If it's a NEAR-implicit account, interprets it as an ed25519 public key, +// maps that and then returns the resulting implicit account. Otherwise does nothing. +// We do this so that transactions creating an implicit account +// by sending money will generate an account that we can control. +pub fn map_account( + account_id: &AccountId, + secret: Option<&[u8; crate::secret::SECRET_LEN]>, +) -> AccountId { + match account_id.get_account_type() { + AccountType::NearImplicitAccount => { + let public_key = + PublicKey::from_unc_implicit_account(account_id).expect("must be implicit"); + let mapped_key = map_key(&public_key, secret); + derive_unc_implicit_account_id(&mapped_key.public_key().unwrap_as_ed25519()) + } + // TODO(eth-implicit) map to a new ETH address + AccountType::EthImplicitAccount => account_id.clone(), + AccountType::NamedAccount => account_id.clone(), + } +} diff --git a/tools/mirror/src/lib.rs b/tools/mirror/src/lib.rs new file mode 100644 index 000000000..fec92b124 --- /dev/null +++ b/tools/mirror/src/lib.rs @@ -0,0 +1,1848 @@ +use actix::Addr; +use anyhow::Context; +use async_trait::async_trait; +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_chain_configs::GenesisValidationMode; +use unc_chain_primitives::error::QueryError as RuntimeQueryError; +use unc_client::{ClientActor, ViewClientActor}; +use unc_client::{ProcessTxRequest, ProcessTxResponse}; +use unc_client_primitives::types::{ + GetBlock, GetBlockError, GetChunkError, GetExecutionOutcomeError, GetReceiptError, Query, + QueryError, Status, +}; +use unc_crypto::{PublicKey, SecretKey}; +use unc_indexer::{Indexer, StreamerMessage}; +use unc_o11y::WithSpanContextExt; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::{Receipt, ReceiptEnum}; +use unc_primitives::transaction::{ + Action, AddKeyAction, CreateAccountAction, DeleteKeyAction, SignedTransaction, StakeAction, + Transaction, +}; +use unc_primitives::types::{ + AccountId, BlockHeight, BlockReference, Finality, TransactionOrReceiptId, +}; +use unc_primitives::views::{ + ExecutionOutcomeWithIdView, ExecutionStatusView, QueryRequest, QueryResponseKind, + SignedTransactionView, +}; +use unc_primitives_core::account::id::AccountType; +use unc_primitives_core::account::{AccessKey, AccessKeyPermission}; +use unc_primitives_core::types::{Nonce, ShardId}; +use framework::config::UncConfig; +use rocksdb::DB; +use std::borrow::Cow; +use std::collections::{HashMap, HashSet}; +use std::path::Path; +use std::sync::Arc; +use std::time::{Duration, Instant}; +use strum::IntoEnumIterator; +use tokio::sync::mpsc; + +mod chain_tracker; +pub mod cli; +mod genesis; +pub mod key_mapping; +mod metrics; +mod offline; +mod online; +pub mod secret; + +pub use cli::MirrorCommand; + +#[derive(strum::EnumIter)] +enum DBCol { + Misc, + // This tracks nonces for Access Keys added by AddKey transactions + // or transfers to implicit accounts (not present in the genesis state). + // For a given (account ID, public key), if we're preparing a transaction + // and there's no entry in the DB, then the key was present in the genesis + // state. Otherwise, we map tx nonces according to the values in this column. + Nonces, + AccessKeyOutcomes, +} + +impl DBCol { + fn name(&self) -> &'static str { + match self { + Self::Misc => "miscellaneous", + Self::Nonces => "nonces", + Self::AccessKeyOutcomes => "access_key_outcomes", + } + } +} + +// TODO: maybe move these type defs to `mod types` or something + +// we want a reference to transactions in .queued_blocks that need to have nonces +// set later. To avoid having the struct be self referential we keep this struct +// with enough info to look it up later. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +struct TxRef { + source_height: BlockHeight, + shard_id: ShardId, + tx_idx: usize, +} + +impl std::fmt::Display for TxRef { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "source #{} shard {} tx {}", self.source_height, self.shard_id, self.tx_idx) + } +} + +impl PartialOrd for TxRef { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for TxRef { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.source_height + .cmp(&other.source_height) + .then_with(|| self.tx_idx.cmp(&other.tx_idx)) + .then_with(|| self.shard_id.cmp(&other.shard_id)) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +enum NonceUpdater { + TxRef(TxRef), + ChainObjectId(CryptoHash), +} + +// returns bytes that serve as the key corresponding to this pair in the Nonces column +fn nonce_col_key(account_id: &AccountId, public_key: &PublicKey) -> Vec { + borsh::to_vec(&(account_id.clone(), public_key.clone())).unwrap() +} + +// this serves a similar purpose to `LatestTargetNonce`. The difference is +// that this one keeps track of what's in memory. So for example if the last +// height we sent transactions for was 10, and we have a set of transactions +// queued up for height 12, one of which is an AddKey for ('foo.near', 'ed25519:...'), +// then we'lll want to remember that for txs of height > 12 that use that +// signer id and public key, but we don't want to store that on disk. The `LatestTargetNonce` +// on disk will only record the transactions/receipts updating the nonce that we actually sent +// or saw on chain + +#[derive(Clone, Debug, Default)] +struct TargetNonce { + nonce: Option, + pending_outcomes: HashSet, +} + +// For a given AddKey Action, records the starting nonces of the +// resulting Access Keys. We need this because when an AddKey receipt +// is processed, the nonce field of the AddKey action is actually +// ignored, and it's set to block_height*1000000, so to generate +// transactions with valid nonces, we need to map valid source chain +// nonces to valid target chain nonces. +#[derive(BorshDeserialize, BorshSerialize, Debug)] +struct LatestTargetNonce { + nonce: Option, + pending_outcomes: HashSet, +} + +// TODO: move DB related stuff to its own file and add a way to +// keep track of updates in memory and write them all in one transaction +fn read_target_nonce( + db: &DB, + account_id: &AccountId, + public_key: &PublicKey, +) -> anyhow::Result> { + let db_key = nonce_col_key(account_id, public_key); + Ok(db + .get_cf(db.cf_handle(DBCol::Nonces.name()).unwrap(), &db_key)? + .map(|v| LatestTargetNonce::try_from_slice(&v).unwrap())) +} + +fn put_target_nonce( + db: &DB, + account_id: &AccountId, + public_key: &PublicKey, + nonce: &LatestTargetNonce, +) -> anyhow::Result<()> { + tracing::debug!(target: "mirror", "storing {:?} in DB for ({}, {:?})", &nonce, account_id, public_key); + let db_key = nonce_col_key(account_id, public_key); + db.put_cf( + db.cf_handle(DBCol::Nonces.name()).unwrap(), + &db_key, + &borsh::to_vec(&nonce).unwrap(), + )?; + Ok(()) +} + +// we store a value (empty HashSet if it affects no access keys) in the database for any tx we send, +// as well as all generated descendant receipts. Once we see it on chain, we update any access keys +// that might be updated by it, and we insert any generated receipts in the DB. When we see an +// execution outcome that adds stake actions, the presence/absence of that receipt's ID in the DB +// will tell us whether it resulted from one of our txs that we mirrored from the source so that +// we can send a stake tx to reverse it + +fn read_pending_outcome( + db: &DB, + id: &CryptoHash, +) -> anyhow::Result>> { + Ok(db + .get_cf( + db.cf_handle(DBCol::AccessKeyOutcomes.name()).unwrap(), + &borsh::to_vec(&id).unwrap(), + )? + .map(|v| HashSet::try_from_slice(&v).unwrap())) +} + +fn put_pending_outcome( + db: &DB, + id: CryptoHash, + access_keys: HashSet<(AccountId, PublicKey)>, +) -> anyhow::Result<()> { + tracing::debug!(target: "mirror", "storing {:?} in DB for {:?}", &access_keys, &id); + Ok(db.put_cf( + db.cf_handle(DBCol::AccessKeyOutcomes.name()).unwrap(), + &borsh::to_vec(&id).unwrap(), + &borsh::to_vec(&access_keys).unwrap(), + )?) +} + +fn delete_pending_outcome(db: &DB, id: &CryptoHash) -> anyhow::Result<()> { + tracing::debug!(target: "mirror", "deleting {:?} from DB", &id); + Ok(db.delete_cf( + db.cf_handle(DBCol::AccessKeyOutcomes.name()).unwrap(), + &borsh::to_vec(&id).unwrap(), + )?) +} + +fn set_last_source_height(db: &DB, height: BlockHeight) -> anyhow::Result<()> { + // TODO: we should instead save something like the + // (block_height, shard_id, idx_in_chunk) of the last + // transaction sent. Currently we set last_source_height after + // sending all of the transactions in that chunk, so if we get + // SIGTERM or something in the middle of sending a batch of + // txs, we'll send some that we already sent next time we + // start. Not a giant problem but kind of unclean. + db.put_cf( + db.cf_handle(DBCol::Misc.name()).unwrap(), + "last_source_height", + borsh::to_vec(&height).unwrap(), + )?; + Ok(()) +} + +fn get_last_source_height(db: &DB) -> anyhow::Result> { + Ok(db + .get_cf(db.cf_handle(DBCol::Misc.name()).unwrap(), "last_source_height")? + .map(|v| BlockHeight::try_from_slice(&v).unwrap())) +} + +enum SourceTransaction { + Tx(SignedTransaction), + TxView(SignedTransactionView), +} + +impl From for SourceTransaction { + fn from(tx: SignedTransaction) -> Self { + Self::Tx(tx) + } +} + +impl From for SourceTransaction { + fn from(tx: SignedTransactionView) -> Self { + Self::TxView(tx) + } +} + +impl SourceTransaction { + fn hash(&self) -> CryptoHash { + match self { + Self::Tx(tx) => tx.get_hash(), + Self::TxView(tx) => tx.hash, + } + } + + fn public_key(&self) -> &PublicKey { + match self { + Self::Tx(tx) => &tx.transaction.public_key, + Self::TxView(tx) => &tx.public_key, + } + } + + fn signer_id(&self) -> &AccountId { + match self { + Self::Tx(tx) => &tx.transaction.signer_id, + Self::TxView(tx) => &tx.signer_id, + } + } + + fn receiver_id(&self) -> &AccountId { + match self { + Self::Tx(tx) => &tx.transaction.receiver_id, + Self::TxView(tx) => &tx.receiver_id, + } + } + + fn actions<'a>(&'a self) -> Cow<'a, [Action]> { + match self { + Self::Tx(tx) => Cow::Borrowed(&tx.transaction.actions), + Self::TxView(tx) => { + Cow::Owned(tx.actions.iter().map(|a| a.clone().try_into().unwrap()).collect()) + } + } + } + + fn is_view(&self) -> bool { + matches!(self, Self::TxView(_)) + } +} + +struct SourceChunk { + shard_id: ShardId, + transactions: Vec, + receipts: Vec, +} + +struct SourceBlock { + hash: CryptoHash, + chunks: Vec, +} + +#[derive(thiserror::Error, Debug)] +enum ChainError { + #[error("unknown")] + Unknown, + #[error(transparent)] + Other(#[from] anyhow::Error), +} + +impl ChainError { + fn other(error: E) -> Self { + Self::Other(anyhow::Error::from(error)) + } +} + +impl From for ChainError { + fn from(err: GetBlockError) -> Self { + match err { + GetBlockError::UnknownBlock { .. } => Self::Unknown, + _ => Self::other(err), + } + } +} + +impl From for ChainError { + fn from(err: GetChunkError) -> Self { + match err { + GetChunkError::UnknownBlock { .. } => Self::Unknown, + _ => Self::other(err), + } + } +} + +impl From for ChainError { + fn from(err: unc_chain_primitives::Error) -> Self { + match err { + unc_chain_primitives::Error::DBNotFoundErr(_) => Self::Unknown, + _ => Self::other(err), + } + } +} + +impl From for ChainError { + fn from(err: GetExecutionOutcomeError) -> Self { + match err { + GetExecutionOutcomeError::UnknownBlock { .. } + | GetExecutionOutcomeError::UnknownTransactionOrReceipt { .. } + | GetExecutionOutcomeError::NotConfirmed { .. } => Self::Unknown, + _ => Self::other(err), + } + } +} + +impl From for ChainError { + fn from(err: GetReceiptError) -> Self { + match err { + GetReceiptError::UnknownReceipt(_) => Self::Unknown, + _ => Self::other(err), + } + } +} + +impl From for ChainError { + fn from(err: QueryError) -> Self { + match err { + QueryError::UnavailableShard { .. } + | QueryError::UnknownAccount { .. } + | QueryError::NoContractCode { .. } + | QueryError::UnknownAccessKey { .. } + | QueryError::GarbageCollectedBlock { .. } + | QueryError::UnknownBlock { .. } => Self::Unknown, + _ => Self::other(err), + } + } +} + +impl From for ChainError { + fn from(err: RuntimeQueryError) -> Self { + match err { + RuntimeQueryError::UnknownAccount { .. } + | RuntimeQueryError::NoContractCode { .. } + | RuntimeQueryError::UnknownAccessKey { .. } => Self::Unknown, + _ => Self::other(err), + } + } +} + +#[async_trait(?Send)] +trait ChainAccess { + // this should return the first `num_initial_blocks` valid block heights in the + // chain after last_height. It should only return fewer than that if there are no + // more, which only happens if we're not starting a source node (--online-source is + // not given), and the last height we sent txs for is close to the HEAD of the chain + async fn init( + &self, + last_height: BlockHeight, + num_initial_blocks: usize, + ) -> anyhow::Result>; + + async fn block_height_to_hash(&self, height: BlockHeight) -> Result; + + async fn head_height(&self) -> Result; + + async fn get_txs( + &self, + height: BlockHeight, + shards: &[ShardId], + ) -> Result; + + async fn get_next_block_height(&self, height: BlockHeight) -> Result; + + async fn get_outcome( + &self, + id: TransactionOrReceiptId, + ) -> Result; + + // for transactions with the same signer and receiver, we + // want the receipt that results from applying it, if successful, + // since that receipt doesn't show up if you just look through + // the receipts in the next block. + // If the tx wasn't successful, just returns None + async fn get_tx_receipt_id( + &self, + tx_hash: &CryptoHash, + signer_id: &AccountId, + ) -> Result, ChainError> { + match self + .get_outcome(TransactionOrReceiptId::Transaction { + transaction_hash: *tx_hash, + sender_id: signer_id.clone(), + }) + .await? + .outcome + .status + { + ExecutionStatusView::SuccessReceiptId(id) => Ok(Some(id)), + ExecutionStatusView::Failure(_) | ExecutionStatusView::Unknown => Ok(None), + ExecutionStatusView::SuccessValue(_) => unreachable!(), + } + } + + async fn get_receipt(&self, id: &CryptoHash) -> Result, ChainError>; + + // returns all public keys with full permissions for the given account + async fn get_full_access_keys( + &self, + account_id: &AccountId, + block_hash: &CryptoHash, + ) -> Result, ChainError>; +} + +fn execution_status_good(status: &ExecutionStatusView) -> bool { + matches!( + status, + ExecutionStatusView::SuccessReceiptId(_) | ExecutionStatusView::SuccessValue(_) + ) +} + +const CREATE_ACCOUNT_DELTA: usize = 5; + +struct TxMirror { + target_stream: mpsc::Receiver, + source_chain_access: T, + // TODO: separate out the code that uses the target chain clients, and + // make it an option to send the transactions to some RPC node. + // that way it would be possible to run this code and send transactions with an + // old binary not caught up to the current protocol version, since the + // transactions we're constructing should stay valid. + target_view_client: Addr, + target_client: Addr, + db: DB, + target_genesis_height: BlockHeight, + target_min_block_production_delay: Duration, + tracked_shards: Vec, + secret: Option<[u8; crate::secret::SECRET_LEN]>, +} + +fn open_db>(home: P, config: &UncConfig) -> anyhow::Result { + let db_path = unc_store::NodeStorage::opener( + home.as_ref(), + config.config.archive, + &config.config.store, + None, + ) + .path() + .join("mirror"); + let mut options = rocksdb::Options::default(); + options.create_missing_column_families(true); + options.create_if_missing(true); + let cf_descriptors = DBCol::iter() + .map(|col| rocksdb::ColumnFamilyDescriptor::new(col.name(), options.clone())) + .collect::>(); + Ok(DB::open_cf_descriptors(&options, db_path, cf_descriptors)?) +} + +#[derive(Clone, Copy, Debug)] +enum MappedTxProvenance { + MappedSourceTx(BlockHeight, ShardId, usize), + TxAddKey(BlockHeight, ShardId, usize), + ReceiptAddKey(BlockHeight, ShardId, usize), + TxCreateAccount(BlockHeight, ShardId, usize), + ReceiptCreateAccount(BlockHeight, ShardId, usize), + Unstake(CryptoHash), +} + +impl MappedTxProvenance { + fn is_create_account(&self) -> bool { + matches!( + self, + MappedTxProvenance::TxCreateAccount(_, _, _) + | MappedTxProvenance::ReceiptCreateAccount(_, _, _) + ) + } + + fn is_add_key(&self) -> bool { + matches!( + self, + MappedTxProvenance::TxAddKey(_, _, _) | MappedTxProvenance::ReceiptAddKey(_, _, _) + ) + } + + fn is_unstake(&self) -> bool { + matches!(self, MappedTxProvenance::Unstake(_)) + } +} + +impl std::fmt::Display for MappedTxProvenance { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::MappedSourceTx(height, shard_id, idx) => { + write!(f, "source #{} shard {} tx #{}", height, shard_id, idx) + } + Self::TxAddKey(height, shard_id, idx) => { + write!(f, "extra AddKey for source #{} shard {} tx #{}", height, shard_id, idx) + } + Self::ReceiptAddKey(height, shard_id, idx) => { + write!(f, "extra AddKey for source #{} shard {} receipt #{}", height, shard_id, idx) + } + Self::TxCreateAccount(height, shard_id, idx) => write!( + f, + "extra CreateAccount for source #{} shard {} tx #{}", + height, shard_id, idx + ), + Self::ReceiptCreateAccount(height, shard_id, idx) => { + write!( + f, + "extra CreateAccount for source #{} shard {} receipt #{}", + height, shard_id, idx + ) + } + Self::Unstake(hash) => { + write!(f, "unstake after stake receipt in target block {}", hash,) + } + } + } +} + +// a transaction that's almost prepared, except that we don't yet know +// what nonce to use because the public key was added in an AddKey +// action that we haven't seen on chain yet. The target_tx field is complete +// except for the nonce field. +#[derive(Debug)] +struct TxAwaitingNonce { + source_signer_id: AccountId, + source_receiver_id: AccountId, + provenance: MappedTxProvenance, + target_secret_key: SecretKey, + target_tx: Transaction, + nonce_updates: HashSet<(AccountId, PublicKey)>, + target_nonce: TargetNonce, +} + +impl TxAwaitingNonce { + fn new( + source_signer_id: AccountId, + source_receiver_id: AccountId, + target_signer_id: AccountId, + target_receiver_id: AccountId, + target_secret_key: SecretKey, + target_public_key: PublicKey, + actions: Vec, + target_nonce: &TargetNonce, + ref_hash: &CryptoHash, + provenance: MappedTxProvenance, + nonce_updates: HashSet<(AccountId, PublicKey)>, + ) -> Self { + let mut target_tx = + Transaction::new(target_signer_id, target_public_key, target_receiver_id, 0, *ref_hash); + target_tx.actions = actions; + Self { + source_signer_id, + source_receiver_id, + provenance, + target_secret_key, + target_tx, + nonce_updates, + target_nonce: target_nonce.clone(), + } + } +} + +// A transaction meant for the target chain that is complete/ready to send. +// We keep some extra info about the transaction for the purposes of logging +// later on when we find it on chain. +#[derive(Debug)] +struct MappedTx { + source_signer_id: AccountId, + source_receiver_id: AccountId, + provenance: MappedTxProvenance, + target_tx: SignedTransaction, + nonce_updates: HashSet<(AccountId, PublicKey)>, + sent_successfully: bool, +} + +impl MappedTx { + fn new( + source_signer_id: AccountId, + source_receiver_id: AccountId, + target_signer_id: AccountId, + target_receiver_id: AccountId, + target_secret_key: &SecretKey, + target_public_key: PublicKey, + actions: Vec, + nonce: Nonce, + ref_hash: &CryptoHash, + provenance: MappedTxProvenance, + nonce_updates: HashSet<(AccountId, PublicKey)>, + ) -> Self { + let mut target_tx = Transaction::new( + target_signer_id, + target_public_key, + target_receiver_id, + nonce, + *ref_hash, + ); + target_tx.actions = actions; + let target_tx = SignedTransaction::new( + target_secret_key.sign(&target_tx.get_hash_and_size().0.as_ref()), + target_tx, + ); + Self { + source_signer_id, + source_receiver_id, + provenance, + target_tx, + nonce_updates, + sent_successfully: false, + } + } + + fn inc_nonce(&mut self, target_secret_key: &SecretKey) { + let mut tx = self.target_tx.transaction.clone(); + tx.nonce += 1; + self.target_tx = + SignedTransaction::new(target_secret_key.sign(&tx.get_hash_and_size().0.as_ref()), tx); + } +} + +#[derive(Debug)] +enum TargetChainTx { + Ready(MappedTx), + AwaitingNonce(TxAwaitingNonce), +} + +impl TargetChainTx { + fn set_nonce(&mut self, nonce: Nonce) { + match self { + Self::AwaitingNonce(t) => { + t.target_tx.nonce = nonce; + let target_tx = SignedTransaction::new( + t.target_secret_key.sign(&t.target_tx.get_hash_and_size().0.as_ref()), + t.target_tx.clone(), + ); + *self = Self::Ready(MappedTx { + source_signer_id: t.source_signer_id.clone(), + source_receiver_id: t.source_receiver_id.clone(), + provenance: t.provenance, + target_tx, + nonce_updates: t.nonce_updates.clone(), + sent_successfully: false, + }); + } + Self::Ready(_) => unreachable!(), + }; + } + + // For an AwaitingNonce(_), set the nonce and sign the transaction, changing self into Ready(_). + // must not be called if self is Ready(_) + fn try_set_nonce(&mut self, nonce: Option) { + let nonce = match self { + Self::AwaitingNonce(t) => match std::cmp::max(t.target_nonce.nonce, nonce) { + Some(n) => n, + None => return, + }, + Self::Ready(_) => unreachable!(), + }; + self.set_nonce(nonce); + } + + fn new_ready( + source_signer_id: AccountId, + source_receiver_id: AccountId, + target_signer_id: AccountId, + target_receiver_id: AccountId, + target_secret_key: &SecretKey, + target_public_key: PublicKey, + actions: Vec, + nonce: Nonce, + ref_hash: &CryptoHash, + provenance: MappedTxProvenance, + nonce_updates: HashSet<(AccountId, PublicKey)>, + ) -> Self { + Self::Ready(MappedTx::new( + source_signer_id, + source_receiver_id, + target_signer_id, + target_receiver_id, + target_secret_key, + target_public_key, + actions, + nonce, + ref_hash, + provenance, + nonce_updates, + )) + } + + fn new_awaiting_nonce( + source_signer_id: AccountId, + source_receiver_id: AccountId, + target_signer_id: AccountId, + target_receiver_id: AccountId, + target_secret_key: &SecretKey, + target_public_key: PublicKey, + actions: Vec, + target_nonce: &TargetNonce, + ref_hash: &CryptoHash, + provenance: MappedTxProvenance, + nonce_updates: HashSet<(AccountId, PublicKey)>, + ) -> Self { + Self::AwaitingNonce(TxAwaitingNonce::new( + source_signer_id, + source_receiver_id, + target_signer_id, + target_receiver_id, + target_secret_key.clone(), + target_public_key, + actions, + target_nonce, + &ref_hash, + provenance, + nonce_updates, + )) + } + + fn target_nonce(&self) -> TargetNonce { + match self { + Self::Ready(t) => TargetNonce { + nonce: Some(t.target_tx.transaction.nonce), + pending_outcomes: HashSet::new(), + }, + Self::AwaitingNonce(t) => t.target_nonce.clone(), + } + } + + fn inc_target_nonce(&mut self, target_secret_key: &SecretKey) { + match self { + Self::Ready(t) => t.inc_nonce(target_secret_key), + Self::AwaitingNonce(t) => { + if let Some(n) = &mut t.target_nonce.nonce { + *n += 1; + } + } + } + } +} + +#[derive(Debug)] +struct MappedChunk { + txs: Vec, + shard_id: ShardId, +} + +#[derive(Debug)] +struct MappedBlock { + source_height: BlockHeight, + source_hash: CryptoHash, + chunks: Vec, +} + +async fn account_exists( + view_client: &Addr, + account_id: &AccountId, +) -> anyhow::Result { + match view_client + .send( + Query::new( + BlockReference::Finality(Finality::None), + QueryRequest::ViewAccount { account_id: account_id.clone() }, + ) + .with_span_context(), + ) + .await? + { + Ok(res) => match res.kind { + QueryResponseKind::ViewAccount(_) => Ok(true), + other => { + panic!("Received unexpected QueryResponse after Querying Account: {:?}", other); + } + }, + Err(e) => match &e { + QueryError::UnknownAccount { .. } => Ok(false), + _ => Err(e.into()), + }, + } +} + +async fn fetch_access_key_nonce( + view_client: &Addr, + account_id: &AccountId, + public_key: &PublicKey, +) -> anyhow::Result> { + match view_client + .send( + Query::new( + BlockReference::Finality(Finality::None), + QueryRequest::ViewAccessKey { + account_id: account_id.clone(), + public_key: public_key.clone(), + }, + ) + .with_span_context(), + ) + .await + .unwrap() + { + Ok(res) => match res.kind { + QueryResponseKind::AccessKey(access_key) => Ok(Some(access_key.nonce)), + other => { + panic!("Received unexpected QueryResponse after Querying Access Key: {:?}", other); + } + }, + Err(e) => match &e { + QueryError::UnknownAccessKey { .. } => Ok(None), + _ => Err(e.into()), + }, + } +} + +impl TxMirror { + fn new>( + source_chain_access: T, + target_home: P, + secret: Option<[u8; crate::secret::SECRET_LEN]>, + ) -> anyhow::Result { + let target_config = + framework::config::load_config(target_home.as_ref(), GenesisValidationMode::UnsafeFast) + .with_context(|| { + format!("Error loading target config from {:?}", target_home.as_ref()) + })?; + if !target_config.client_config.archive { + // this is probably not going to come up, but we want to avoid a situation where + // we go offline for a long time and then come back online, and we state sync to + // the head of the target chain without looking for our outcomes that made it on + // chain right before we went offline + anyhow::bail!("config file in {} has archive: false, but archive must be set to true for the target chain", target_home.as_ref().display()); + } + let db = + open_db(target_home.as_ref(), &target_config).context("failed to open mirror DB")?; + let target_indexer = Indexer::new(unc_indexer::IndexerConfig { + home_dir: target_home.as_ref().to_path_buf(), + sync_mode: unc_indexer::SyncModeEnum::FromInterruption, + await_for_node_synced: unc_indexer::AwaitForNodeSyncedEnum::StreamWhileSyncing, + validate_genesis: false, + }) + .context("failed to start target chain indexer")?; + let (target_view_client, target_client) = target_indexer.client_actors(); + let target_stream = target_indexer.streamer(); + + Ok(Self { + source_chain_access, + target_client, + target_view_client, + target_stream, + db, + target_genesis_height: target_config.genesis.config.genesis_height, + target_min_block_production_delay: target_config + .client_config + .min_block_production_delay, + tracked_shards: target_config.config.tracked_shards, + secret, + }) + } + + async fn send_transactions<'a, I: Iterator>( + &mut self, + txs: I, + ) -> anyhow::Result<()> { + for tx in txs { + match tx { + TargetChainTx::Ready(tx) => { + match self + .target_client + .send( + ProcessTxRequest { + transaction: tx.target_tx.clone(), + is_forwarded: false, + check_only: false, + } + .with_span_context(), + ) + .await? + { + ProcessTxResponse::RequestRouted => { + crate::metrics::TRANSACTIONS_SENT.with_label_values(&["ok"]).inc(); + tx.sent_successfully = true; + } + ProcessTxResponse::InvalidTx(e) => { + // TODO: here if we're getting an error because the tx was already included, it is possible + // that some other instance of this code ran and made progress already. For now we can assume + // only once instance of this code will run, but this is the place to detect if that's not the case. + tracing::error!( + target: "mirror", "Tried to send an invalid tx for ({}, {:?}) from {}: {:?}", + &tx.target_tx.transaction.signer_id, &tx.target_tx.transaction.public_key, &tx.provenance, e + ); + crate::metrics::TRANSACTIONS_SENT.with_label_values(&["invalid"]).inc(); + } + r => { + tracing::error!( + target: "mirror", "Unexpected response sending tx from {}: {:?}. The transaction was not sent", + &tx.provenance, r + ); + crate::metrics::TRANSACTIONS_SENT + .with_label_values(&["internal_error"]) + .inc(); + } + } + } + TargetChainTx::AwaitingNonce(tx) => { + // TODO: here we should just save this transaction for later and send it when it's known + tracing::warn!( + target: "mirror", "skipped sending transaction for ({}, {:?}) because valid target chain nonce not known", + &tx.target_tx.signer_id, &tx.target_tx.public_key + ); + } + } + } + Ok(()) + } + + async fn map_actions( + &self, + tx: &SourceTransaction, + ) -> anyhow::Result<(Vec, HashSet<(AccountId, PublicKey)>)> { + let mut actions = Vec::new(); + let mut nonce_updates = HashSet::new(); + + let mut account_created = false; + let mut full_key_added = false; + let source_actions = tx.actions(); + for action in source_actions.iter() { + match &action { + Action::AddKey(add_key) => { + if add_key.access_key.permission == AccessKeyPermission::FullAccess { + full_key_added = true; + } + let public_key = + crate::key_mapping::map_key(&add_key.public_key, self.secret.as_ref()) + .public_key(); + let receiver_id = + crate::key_mapping::map_account(tx.receiver_id(), self.secret.as_ref()); + + nonce_updates.insert((receiver_id, public_key.clone())); + actions.push(Action::AddKey(Box::new(AddKeyAction { + public_key, + access_key: add_key.access_key.clone(), + }))); + } + Action::DeleteKey(delete_key) => { + let replacement = + crate::key_mapping::map_key(&delete_key.public_key, self.secret.as_ref()); + let public_key = replacement.public_key(); + + actions.push(Action::DeleteKey(Box::new(DeleteKeyAction { public_key }))); + } + Action::Transfer(_) => { + // TODO(eth-implicit) Change back to is_implicit() when ETH-implicit accounts are supported. + if tx.receiver_id().get_account_type() == AccountType::NearImplicitAccount + && source_actions.len() == 1 + { + let target_account = + crate::key_mapping::map_account(tx.receiver_id(), self.secret.as_ref()); + if !account_exists(&self.target_view_client, &target_account) + .await + .with_context(|| { + format!( + "failed checking existence for account {}", + tx.receiver_id() + ) + })? + { + if target_account.get_account_type() == AccountType::NearImplicitAccount + { + let public_key = + PublicKey::from_unc_implicit_account(&target_account) + .expect("must be unc-implicit"); + nonce_updates.insert((target_account, public_key)); + } + } + } + actions.push(action.clone()); + } + // We don't want to mess with the set of validators in the target chain + Action::Stake(_) => {} + Action::DeployContract(_) => { + // if we're getting transactions from a ViewClient instead of directly from the DB, + // DeployContract actions are silently mangled, so we can't recover the original contract code here + if !tx.is_view() { + actions.push(action.clone()); + } + } + Action::CreateAccount(_) => { + account_created = true; + actions.push(action.clone()); + } + _ => actions.push(action.clone()), + }; + } + if account_created && !full_key_added { + actions.push(Action::AddKey(Box::new(AddKeyAction { + public_key: crate::key_mapping::EXTRA_KEY.public_key(), + access_key: AccessKey::full_access(), + }))); + } + Ok((actions, nonce_updates)) + } + + async fn prepare_tx( + &self, + tracker: &mut crate::chain_tracker::TxTracker, + source_signer_id: AccountId, + source_receiver_id: AccountId, + target_signer_id: AccountId, + target_receiver_id: AccountId, + target_secret_key: &SecretKey, + actions: Vec, + ref_hash: &CryptoHash, + source_height: Option, + provenance: MappedTxProvenance, + nonce_updates: HashSet<(AccountId, PublicKey)>, + ) -> anyhow::Result { + let target_public_key = target_secret_key.public_key(); + let target_nonce = match source_height.as_ref() { + Some(_) => None, + None => Some( + tracker + .insert_nonce( + &self.target_view_client, + &self.db, + &target_signer_id, + &target_public_key, + target_secret_key, + ) + .await?, + ), + }; + let target_nonce = match source_height { + Some(source_height) => { + tracker + .next_nonce( + &self.target_view_client, + &self.db, + &target_signer_id, + &target_public_key, + source_height, + ) + .await? + } + None => target_nonce.as_ref().unwrap(), + }; + + if target_nonce.pending_outcomes.is_empty() && target_nonce.nonce.is_some() { + Ok(TargetChainTx::new_ready( + source_signer_id, + source_receiver_id, + target_signer_id, + target_receiver_id, + &target_secret_key, + target_public_key, + actions, + target_nonce.nonce.unwrap(), + &ref_hash, + provenance, + nonce_updates, + )) + } else { + Ok(TargetChainTx::new_awaiting_nonce( + source_signer_id, + source_receiver_id, + target_signer_id, + target_receiver_id, + target_secret_key, + target_public_key, + actions, + target_nonce, + &ref_hash, + provenance, + nonce_updates, + )) + } + } + + // add extra AddKey transactions that come from function call. If we don't do this, + // then the only keys we will have mapped are the ones added by regular AddKey transactions. + async fn push_extra_tx( + &self, + tracker: &mut crate::chain_tracker::TxTracker, + block_hash: CryptoHash, + txs: &mut Vec, + predecessor_id: AccountId, + receiver_id: AccountId, + actions: &[Action], + ref_hash: &CryptoHash, + provenance: MappedTxProvenance, + source_height: Option, + ) -> anyhow::Result<()> { + let target_signer_id = + crate::key_mapping::map_account(&predecessor_id, self.secret.as_ref()); + + let target_secret_key = match self + .source_chain_access + .get_full_access_keys(&predecessor_id, &block_hash) + .await + { + Ok(keys) => { + let mut key = None; + let mut first_key = None; + for k in keys.iter() { + let target_secret_key = crate::key_mapping::map_key(k, self.secret.as_ref()); + if fetch_access_key_nonce( + &self.target_view_client, + &target_signer_id, + &target_secret_key.public_key(), + ) + .await? + .is_some() + { + key = Some(target_secret_key); + break; + } + if first_key.is_none() { + first_key = Some(target_secret_key); + } + } + // here none of them have equivalents in the target chain. Just use the first one and hope that + // it will become available. shouldn't happen much in practice + if key.is_none() { + if let Some(k) = first_key { + tracing::warn!( + target: "mirror", "preparing a transaction for {} for signer {} with key {} even though it is not yet known in the target chain", + &provenance, &target_signer_id, &k.public_key(), + ); + key = Some(k); + } + } + match key { + Some(key) => key, + None => { + tracing::debug!( + target: "mirror", "trying to prepare a transaction with the default extra key for {} because no full access key for {} in the source chain is known at block {}", + &provenance, &target_signer_id, &block_hash, + ); + crate::key_mapping::EXTRA_KEY + } + } + } + Err(e) => { + return Err(e) + .with_context(|| format!("failed fetching access key for {}", &predecessor_id)) + } + }; + + let target_receiver_id = + crate::key_mapping::map_account(&receiver_id, self.secret.as_ref()); + + let mut nonce_updates = HashSet::new(); + let mut target_actions = Vec::new(); + let mut full_key_added = false; + let mut account_created = false; + + for a in actions { + match a { + Action::AddKey(a) => { + if a.access_key.permission == AccessKeyPermission::FullAccess { + full_key_added = true; + } + let target_public_key = + crate::key_mapping::map_key(&a.public_key, self.secret.as_ref()) + .public_key(); + + nonce_updates.insert((target_receiver_id.clone(), target_public_key.clone())); + target_actions.push(Action::AddKey(Box::new(AddKeyAction { + public_key: target_public_key, + access_key: a.access_key.clone(), + }))); + } + Action::CreateAccount(_) => { + account_created = true; + target_actions.push(Action::CreateAccount(CreateAccountAction {})) + } + Action::Transfer(_) => { + if provenance.is_create_account() { + target_actions.push(a.clone()) + } + } + Action::Stake(_) => { + if provenance.is_unstake() { + target_actions.push(a.clone()); + } + } + _ => {} + }; + } + if account_created && !full_key_added { + target_actions.push(Action::AddKey(Box::new(AddKeyAction { + public_key: crate::key_mapping::EXTRA_KEY.public_key(), + access_key: AccessKey::full_access(), + }))); + } + + tracing::debug!( + target: "mirror", "preparing {} for ({}, {}) with actions: {:?}", + &provenance, &target_signer_id, target_secret_key.public_key(), &target_actions, + ); + let target_tx = self + .prepare_tx( + tracker, + predecessor_id, + receiver_id, + target_signer_id, + target_receiver_id, + &target_secret_key, + target_actions, + ref_hash, + source_height, + provenance, + nonce_updates, + ) + .await?; + txs.push(target_tx); + Ok(()) + } + + async fn add_function_call_keys( + &self, + tracker: &mut crate::chain_tracker::TxTracker, + txs: &mut Vec, + receipt_id: &CryptoHash, + receiver_id: &AccountId, + ref_hash: &CryptoHash, + provenance: MappedTxProvenance, + source_height: BlockHeight, + ) -> anyhow::Result<()> { + let outcome = self + .source_chain_access + .get_outcome(TransactionOrReceiptId::Receipt { + receipt_id: *receipt_id, + receiver_id: receiver_id.clone(), + }) + .await + .with_context(|| format!("failed fetching outcome for receipt {}", receipt_id))?; + if !execution_status_good(&outcome.outcome.status) { + return Ok(()); + } + + for id in outcome.outcome.receipt_ids.iter() { + let receipt = match self.source_chain_access.get_receipt(id).await { + Ok(r) => r, + Err(ChainError::Unknown) => { + tracing::warn!( + target: "mirror", "receipt {} appears in the list output by receipt {}, but can't find it in the source chain", + id, receipt_id, + ); + continue; + } + Err(ChainError::Other(e)) => return Err(e), + }; + + if let ReceiptEnum::Action(r) = &receipt.receipt { + if (provenance.is_create_account() && receipt.predecessor_id == receipt.receiver_id) + || (!provenance.is_create_account() + && receipt.predecessor_id != receipt.receiver_id) + { + continue; + } + // TODO: should also take care of delete key actions, and deploy contract actions to + // implicit accounts, etc... + let mut key_added = false; + let mut account_created = false; + for a in r.actions.iter() { + match a { + Action::AddKey(_) => key_added = true, + Action::CreateAccount(_) => account_created = true, + _ => {} + }; + } + if provenance.is_create_account() { + if !account_created { + continue; + } + } else { + if !key_added { + continue; + } + if account_created { + tracing::warn!( + target: "mirror", "for receipt {} predecessor and receiver are the same but there's a create account in the actions: {:?}", + &receipt.receipt_id, &r.actions, + ); + } + } + let outcome = self + .source_chain_access + .get_outcome(TransactionOrReceiptId::Receipt { + receipt_id: receipt.receipt_id, + receiver_id: receipt.receiver_id.clone(), + }) + .await + .with_context(|| { + format!("failed fetching outcome for receipt {}", receipt.receipt_id) + })?; + if !execution_status_good(&outcome.outcome.status) { + continue; + } + + self.push_extra_tx( + tracker, + outcome.block_hash, + txs, + receipt.predecessor_id.clone(), + receipt.receiver_id.clone(), + &r.actions, + ref_hash, + provenance, + Some(source_height), + ) + .await?; + } + } + + Ok(()) + } + + async fn add_tx_function_call_keys( + &self, + tx: &SourceTransaction, + provenance: MappedTxProvenance, + source_height: BlockHeight, + ref_hash: &CryptoHash, + tracker: &mut crate::chain_tracker::TxTracker, + txs: &mut Vec, + ) -> anyhow::Result<()> { + // if signer and receiver are the same then the resulting local receipt + // is only logically included, and we won't see it in the receipts in any chunk, + // so handle that case here + if tx.signer_id() == tx.receiver_id() + && tx.actions().iter().any(|a| matches!(a, Action::FunctionCall(_))) + { + let tx_hash = tx.hash(); + if let Some(receipt_id) = self + .source_chain_access + .get_tx_receipt_id(&tx_hash, tx.signer_id()) + .await + .with_context(|| format!("failed fetching local receipt ID for tx {}", &tx_hash))? + { + self.add_function_call_keys( + tracker, + txs, + &receipt_id, + &tx.receiver_id(), + ref_hash, + provenance, + source_height, + ) + .await + } else { + Ok(()) + } + } else { + Ok(()) + } + } + + async fn add_receipt_function_call_keys( + &self, + receipt: &Receipt, + provenance: MappedTxProvenance, + source_height: BlockHeight, + ref_hash: &CryptoHash, + tracker: &mut crate::chain_tracker::TxTracker, + txs: &mut Vec, + ) -> anyhow::Result<()> { + if let ReceiptEnum::Action(r) = &receipt.receipt { + if r.actions.iter().any(|a| matches!(a, Action::FunctionCall(_))) { + self.add_function_call_keys( + tracker, + txs, + &receipt.receipt_id, + &receipt.receiver_id, + ref_hash, + provenance, + source_height, + ) + .await + } else { + Ok(()) + } + } else { + Ok(()) + } + } + + async fn add_create_account_txs( + &self, + create_account_height: BlockHeight, + ref_hash: CryptoHash, + tracker: &mut crate::chain_tracker::TxTracker, + chunks: &mut Vec, + ) -> anyhow::Result<()> { + let source_block = self + .source_chain_access + .get_txs(create_account_height, &self.tracked_shards) + .await + .with_context(|| { + format!("Failed fetching chunks for source chain #{}", create_account_height) + })?; + let mut added_chunk = false; + for ch in source_block.chunks { + let txs = match chunks.iter_mut().find(|c| c.shard_id == ch.shard_id) { + Some(c) => &mut c.txs, + None => { + // It doesnt really matter which one we put it in, since we're just going to send them all anyway + tracing::warn!( + "got unexpected source chunk shard id {} for #{}", + ch.shard_id, + create_account_height + ); + if chunks.is_empty() { + chunks.push(MappedChunk { shard_id: 0, txs: Vec::new() }); + added_chunk = true; + } + &mut chunks[0].txs + } + }; + for (idx, source_tx) in ch.transactions.into_iter().enumerate() { + self.add_tx_function_call_keys( + &source_tx, + MappedTxProvenance::TxCreateAccount(create_account_height, ch.shard_id, idx), + create_account_height, + &ref_hash, + tracker, + txs, + ) + .await?; + } + for (idx, r) in ch.receipts.iter().enumerate() { + // TODO: we're scanning the list of receipts for each block twice. Once here and then again + // when we queue that height's txs. Prob not a big deal but could fix that. + self.add_receipt_function_call_keys( + r, + MappedTxProvenance::ReceiptCreateAccount( + create_account_height, + ch.shard_id, + idx, + ), + create_account_height, + &ref_hash, + tracker, + txs, + ) + .await?; + } + } + if added_chunk && chunks[0].txs.is_empty() { + chunks.clear(); + } + Ok(()) + } + + // fetch the source chain block at `source_height`, and prepare a + // set of transactions that should be valid in the target chain + // from it. + async fn fetch_txs( + &self, + source_height: BlockHeight, + create_account_height: Option, + ref_hash: CryptoHash, + tracker: &mut crate::chain_tracker::TxTracker, + ) -> anyhow::Result { + let source_block = self + .source_chain_access + .get_txs(source_height, &self.tracked_shards) + .await + .with_context(|| { + format!("Failed fetching chunks for source chain #{}", source_height) + })?; + + let mut chunks = Vec::new(); + for ch in source_block.chunks { + let mut txs = Vec::new(); + + for (idx, source_tx) in ch.transactions.into_iter().enumerate() { + let (actions, nonce_updates) = self.map_actions(&source_tx).await?; + if actions.is_empty() { + // If this is a tx containing only stake actions, skip it. + continue; + } + let target_private_key = + crate::key_mapping::map_key(source_tx.public_key(), self.secret.as_ref()); + + let target_signer_id = + crate::key_mapping::map_account(source_tx.signer_id(), self.secret.as_ref()); + let target_receiver_id = + crate::key_mapping::map_account(source_tx.receiver_id(), self.secret.as_ref()); + + let target_tx = self + .prepare_tx( + tracker, + source_tx.signer_id().clone(), + source_tx.receiver_id().clone(), + target_signer_id, + target_receiver_id, + &target_private_key, + actions, + &ref_hash, + Some(source_height), + MappedTxProvenance::MappedSourceTx(source_height, ch.shard_id, idx), + nonce_updates, + ) + .await?; + txs.push(target_tx); + self.add_tx_function_call_keys( + &source_tx, + MappedTxProvenance::TxAddKey(source_height, ch.shard_id, idx), + source_height, + &ref_hash, + tracker, + &mut txs, + ) + .await?; + } + for (idx, r) in ch.receipts.iter().enumerate() { + self.add_receipt_function_call_keys( + r, + MappedTxProvenance::ReceiptAddKey(source_height, ch.shard_id, idx), + source_height, + &ref_hash, + tracker, + &mut txs, + ) + .await?; + } + tracing::debug!( + target: "mirror", "prepared {} transacations for source chain #{} shard {}", + txs.len(), source_height, ch.shard_id + ); + chunks.push(MappedChunk { txs, shard_id: ch.shard_id }); + } + if let Some(create_account_height) = create_account_height { + self.add_create_account_txs(create_account_height, ref_hash, tracker, &mut chunks) + .await?; + } + Ok(MappedBlock { source_height, source_hash: source_block.hash, chunks }) + } + + // Up to a certain capacity, prepare and queue up batches of + // transactions that we want to send to the target chain. + async fn queue_txs( + &mut self, + tracker: &mut crate::chain_tracker::TxTracker, + ref_hash: CryptoHash, + check_send_time: bool, + ) -> anyhow::Result<()> { + if tracker.num_blocks_queued() > 100 { + return Ok(()); + } + + let next_batch_time = tracker.next_batch_time(); + + loop { + let (next_height, create_account_height) = + tracker.next_heights(&self.source_chain_access).await?; + + let next_height = match next_height { + Some(h) => h, + None => return Ok(()), + }; + // if we have a stop height, just send the last few blocks without worrying about + // extra create account txs, otherwise wait until we get more blocks + if !tracker.has_stop_height() && create_account_height.is_none() { + return Ok(()); + } + let b = self + .fetch_txs(next_height, create_account_height, ref_hash, tracker) + .await + .with_context(|| format!("Can't fetch source #{} transactions", next_height))?; + tracker.queue_block(b, &self.target_view_client, &self.db).await?; + if tracker.num_blocks_queued() > 100 { + break; + } + + if check_send_time + && tracker.num_blocks_queued() > 0 + && Instant::now() + Duration::from_millis(20) > next_batch_time + { + break; + } + } + Ok(()) + } + + // send stake txs for zero stake for each of the stake actions we just saw in + // the last block's processed receipts. These would have come from function calls + // rather than normal stake txs, since we skip sending those. + // TODO: here we're just sending it and forgetting about it, but would be good to + // retry later if the tx got lost for some reason + async fn unstake( + &mut self, + tracker: &mut crate::chain_tracker::TxTracker, + stakes: HashMap<(AccountId, PublicKey), AccountId>, + source_hash: &CryptoHash, + target_hash: &CryptoHash, + target_height: BlockHeight, + ) -> anyhow::Result<()> { + let mut txs = Vec::new(); + for ((receiver_id, public_key), predecessor_id) in stakes { + self.push_extra_tx( + tracker, + *source_hash, + &mut txs, + predecessor_id, + receiver_id.clone(), + &[Action::Stake(Box::new(StakeAction { public_key, stake: 0 }))], + target_hash, + MappedTxProvenance::Unstake(*target_hash), + None, + ) + .await?; + } + if !txs.is_empty() { + self.send_transactions(txs.iter_mut()).await?; + tracker + .on_txs_sent( + &self.db, + crate::chain_tracker::SentBatch::ExtraTxs(txs), + target_height, + ) + .await?; + } + Ok(()) + } + + async fn main_loop( + &mut self, + mut tracker: crate::chain_tracker::TxTracker, + mut target_height: BlockHeight, + mut target_head: CryptoHash, + mut source_hash: CryptoHash, + ) -> anyhow::Result<()> { + loop { + tokio::select! { + // time to send a batch of transactions + mapped_block = tracker.next_batch(&self.target_view_client, &self.db), if tracker.num_blocks_queued() > 0 => { + let mut mapped_block = mapped_block?; + source_hash = mapped_block.source_hash; + self.send_transactions(mapped_block.chunks.iter_mut().flat_map(|c| c.txs.iter_mut())).await?; + tracker.on_txs_sent(&self.db, crate::chain_tracker::SentBatch::MappedBlock(mapped_block), target_height).await?; + + // now we have one second left until we need to send more transactions. In the + // meantime, we might as well prepare some more batches of transactions. + // TODO: continue in best effort fashion on error + self.queue_txs(&mut tracker, target_head, true).await?; + } + msg = self.target_stream.recv() => { + let msg = msg.unwrap(); + target_head = msg.block.header.hash; + target_height = msg.block.header.height; + let staked_accounts = tracker.on_target_block(&self.target_view_client, &self.db, msg).await?; + self.unstake(&mut tracker, staked_accounts, &source_hash, &target_head, target_height).await?; + } + // If we don't have any upcoming sets of transactions to send already built, we probably fell behind in the source + // chain and can't fetch the transactions. Check if we have them now here. + _ = tokio::time::sleep(Duration::from_millis(200)), if tracker.num_blocks_queued() == 0 => { + self.queue_txs(&mut tracker, target_head, true).await?; + } + }; + if tracker.finished() { + tracing::info!(target: "mirror", "finished sending all transactions"); + return Ok(()); + } + } + } + + async fn target_chain_syncing(&self) -> bool { + self.target_client + .send(Status { is_health_check: false, detailed: false }.with_span_context()) + .await + .unwrap() + .map(|s| s.sync_info.syncing) + .unwrap_or(true) + } + + async fn target_chain_head(&self) -> anyhow::Result<(BlockHeight, CryptoHash)> { + let header = self + .target_view_client + .send(GetBlock(BlockReference::Finality(Finality::Final)).with_span_context()) + .await + .unwrap() + .context("failed fetching target chain HEAD")? + .header; + Ok((header.height, header.hash)) + } + + // call tracker.on_target_block() on each target chain block until that client is synced + async fn index_target_chain( + &mut self, + tracker: &mut crate::chain_tracker::TxTracker, + ) -> anyhow::Result<(BlockHeight, CryptoHash)> { + let mut head = None; + + loop { + let msg = self.target_stream.recv().await.unwrap(); + let height = msg.block.header.height; + + tracker.on_target_block(&self.target_view_client, &self.db, msg).await?; + + match head { + Some((head_height, head_hash)) => { + if height >= head_height { + return Ok((head_height, head_hash)); + } + } + None => { + if !self.target_chain_syncing().await { + head = Some(self.target_chain_head().await?); + } + } + } + } + } + + async fn run(mut self, stop_height: Option) -> anyhow::Result<()> { + let last_stored_height = get_last_source_height(&self.db)?; + let last_height = last_stored_height.unwrap_or(self.target_genesis_height - 1); + + let next_heights = self.source_chain_access.init(last_height, CREATE_ACCOUNT_DELTA).await?; + + if next_heights.is_empty() { + anyhow::bail!("no new blocks after #{}", last_height); + } + if let Some(stop_height) = stop_height { + if next_heights[0] > stop_height { + anyhow::bail!( + "--stop-height was {} and the next height to send txs for is {}", + stop_height, + next_heights[0] + ); + } + } + let source_hash = self + .source_chain_access + .block_height_to_hash(next_heights[0]) + .await + .with_context(|| format!("error fetching hash of block #{}", next_heights[0]))?; + + tracing::debug!(target: "mirror", "source chain initialized with first heights: {:?}", &next_heights); + + let mut tracker = crate::chain_tracker::TxTracker::new( + self.target_min_block_production_delay, + next_heights.iter(), + stop_height, + ); + let (target_height, target_head) = self.index_target_chain(&mut tracker).await?; + if last_stored_height.is_none() { + // send any extra function call-initiated create accounts for the first few blocks right now + let chunks = self + .tracked_shards + .iter() + .map(|s| MappedChunk { shard_id: *s, txs: Vec::new() }) + .collect(); + // we set source_hash to 0 because we don't actually care about it here, and it doesn't even exist since these are + // not transactions corresponding to some actual block, but just extra txs create account actions in the first few blocks. + let mut block = MappedBlock { + source_hash: CryptoHash::default(), + source_height: last_height, + chunks, + }; + for h in next_heights { + self.add_create_account_txs(h, target_head, &mut tracker, &mut block.chunks) + .await?; + } + if block.chunks.iter().any(|c| !c.txs.is_empty()) { + tracing::debug!(target: "mirror", "sending extra create account transactions for the first {} blocks", CREATE_ACCOUNT_DELTA); + tracker.queue_block(block, &self.target_view_client, &self.db).await?; + let mut b = tracker.next_batch(&self.target_view_client, &self.db).await?; + self.send_transactions(b.chunks.iter_mut().flat_map(|c| c.txs.iter_mut())).await?; + tracker + .on_txs_sent( + &self.db, + crate::chain_tracker::SentBatch::MappedBlock(b), + target_height, + ) + .await?; + } + } + + self.queue_txs(&mut tracker, target_head, false).await?; + + self.main_loop(tracker, target_height, target_head, source_hash).await + } +} + +async fn run>( + source_home: P, + target_home: P, + secret: Option<[u8; crate::secret::SECRET_LEN]>, + stop_height: Option, + online_source: bool, +) -> anyhow::Result<()> { + if !online_source { + let source_chain_access = crate::offline::ChainAccess::new(source_home)?; + let stop_height = stop_height.unwrap_or( + source_chain_access.head_height().await.context("could not fetch source chain head")?, + ); + TxMirror::new(source_chain_access, target_home, secret)?.run(Some(stop_height)).await + } else { + tracing::warn!(target: "mirror", "FIXME: currently --online-source will skip DeployContract actions"); + TxMirror::new(crate::online::ChainAccess::new(source_home)?, target_home, secret)? + .run(stop_height) + .await + } +} diff --git a/tools/mirror/src/metrics.rs b/tools/mirror/src/metrics.rs new file mode 100644 index 000000000..87ef96d4f --- /dev/null +++ b/tools/mirror/src/metrics.rs @@ -0,0 +1,21 @@ +use unc_o11y::metrics::{ + try_create_int_counter, try_create_int_counter_vec, IntCounter, IntCounterVec, +}; +use once_cell::sync::Lazy; + +pub static TRANSACTIONS_SENT: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "unc_mirror_transactions_sent", + "Total number of transactions sent", + &["status"], + ) + .unwrap() +}); + +pub static TRANSACTIONS_INCLUDED: Lazy = Lazy::new(|| { + try_create_int_counter( + "unc_mirror_transactions_included", + "Total number of transactions sent that made it on-chain", + ) + .unwrap() +}); diff --git a/tools/mirror/src/offline.rs b/tools/mirror/src/offline.rs new file mode 100644 index 000000000..79abbe07b --- /dev/null +++ b/tools/mirror/src/offline.rs @@ -0,0 +1,222 @@ +use crate::{ChainError, SourceBlock, SourceChunk}; +use anyhow::Context; +use async_trait::async_trait; +use unc_chain::types::RuntimeAdapter; +use unc_chain::{ChainStore, ChainStoreAccess}; +use unc_chain_configs::GenesisValidationMode; +use unc_chain_primitives::error::EpochErrorResultToChainError; +use unc_crypto::PublicKey; +use unc_epoch_manager::{EpochManager, EpochManagerAdapter, EpochManagerHandle}; +use unc_primitives::block::BlockHeader; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::Receipt; +use unc_primitives::types::{AccountId, BlockHeight, TransactionOrReceiptId}; +use unc_primitives::views::{ + AccessKeyPermissionView, ExecutionOutcomeWithIdView, QueryRequest, QueryResponseKind, +}; +use unc_primitives_core::types::ShardId; +use framework::NightshadeRuntime; +use std::path::Path; +use std::sync::Arc; + +fn is_on_current_chain( + chain: &ChainStore, + header: &BlockHeader, +) -> Result { + let chain_header = chain.get_block_header_by_height(header.height())?; + Ok(chain_header.hash() == header.hash()) +} + +pub(crate) struct ChainAccess { + chain: ChainStore, + epoch_manager: Arc, + runtime: Arc, +} + +impl ChainAccess { + pub(crate) fn new>(home: P) -> anyhow::Result { + let mut config = + framework::config::load_config(home.as_ref(), GenesisValidationMode::UnsafeFast) + .with_context(|| format!("Error loading config from {:?}", home.as_ref()))?; + let node_storage = + framework::open_storage(home.as_ref(), &mut config).context("failed opening storage")?; + let store = node_storage.get_hot_store(); + let chain = ChainStore::new( + store.clone(), + config.genesis.config.genesis_height, + config.client_config.save_trie_changes, + ); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &config.genesis.config); + let runtime = + NightshadeRuntime::from_config(home.as_ref(), store, &config, epoch_manager.clone()); + Ok(Self { chain, epoch_manager, runtime }) + } +} + +#[async_trait(?Send)] +impl crate::ChainAccess for ChainAccess { + async fn init( + &self, + last_height: BlockHeight, + num_initial_blocks: usize, + ) -> anyhow::Result> { + let mut block_heights = Vec::with_capacity(num_initial_blocks); + let head = self.head_height().await?; + + let mut height = last_height + 1; + loop { + if height > head { + return Ok(block_heights); + } + match self.chain.get_block_hash_by_height(height) { + Ok(hash) => { + block_heights.push( + self.chain + .get_block_header(&hash) + .with_context(|| format!("failed fetching block header for {}", &hash))? + .height(), + ); + break; + } + Err(unc_chain_primitives::Error::DBNotFoundErr(_)) => { + height += 1; + } + Err(e) => { + return Err(e) + .with_context(|| format!("failed fetching block hash for #{}", height)) + } + }; + } + while block_heights.len() < num_initial_blocks { + let last_height = *block_heights.iter().next_back().unwrap(); + match self.get_next_block_height(last_height).await { + Ok(h) => block_heights.push(h), + Err(ChainError::Unknown) => break, + Err(ChainError::Other(e)) => { + return Err(e).with_context(|| { + format!("failed getting next block height after {}", last_height) + }) + } + }; + } + Ok(block_heights) + } + + async fn block_height_to_hash(&self, height: BlockHeight) -> Result { + Ok(self.chain.get_block_hash_by_height(height)?) + } + + async fn head_height(&self) -> Result { + Ok(self.chain.head()?.height) + } + + async fn get_txs( + &self, + height: BlockHeight, + shards: &[ShardId], + ) -> Result { + let block_hash = self.chain.get_block_hash_by_height(height)?; + let block = self + .chain + .get_block(&block_hash) + .with_context(|| format!("Can't get block {} at height {}", &block_hash, height))?; + + // of course simpler/faster to just have an array of bools but this is a one liner and who cares :) + let shards = shards.iter().collect::>(); + + let mut chunks = Vec::new(); + for chunk in block.chunks().iter() { + if !shards.contains(&chunk.shard_id()) { + continue; + } + let chunk = match self.chain.get_chunk(&chunk.chunk_hash()) { + Ok(c) => c, + Err(e) => { + tracing::error!( + "Can't fetch source chain shard {} chunk at height {}. Are we tracking all shards?: {:?}", + chunk.shard_id(), height, e + ); + continue; + } + }; + chunks.push(SourceChunk { + shard_id: chunk.shard_id(), + transactions: chunk.transactions().iter().map(|t| t.clone().into()).collect(), + receipts: chunk.prev_outgoing_receipts().iter().cloned().collect(), + }) + } + Ok(SourceBlock { hash: block_hash, chunks }) + } + + async fn get_next_block_height(&self, height: BlockHeight) -> Result { + let hash = self.chain.get_block_hash_by_height(height)?; + let hash = self.chain.get_next_block_hash(&hash)?; + Ok(self.chain.get_block_header(&hash)?.height()) + } + + async fn get_outcome( + &self, + id: TransactionOrReceiptId, + ) -> Result { + let id = match id { + TransactionOrReceiptId::Receipt { receipt_id, .. } => receipt_id, + TransactionOrReceiptId::Transaction { transaction_hash, .. } => transaction_hash, + }; + let outcomes = self.chain.get_outcomes_by_id(&id)?; + // this implements the same logic as in Chain::get_execution_outcome(). We will rewrite + // that here because it makes more sense for us to have just the ChainStore and not the Chain, + // since we're just reading data, not doing any protocol related stuff + outcomes + .into_iter() + .find(|outcome| match self.chain.get_block_header(&outcome.block_hash) { + Ok(header) => is_on_current_chain(&self.chain, &header).unwrap_or(false), + Err(_) => false, + }) + .map(Into::into) + .ok_or(ChainError::Unknown) + } + + async fn get_receipt(&self, id: &CryptoHash) -> Result, ChainError> { + self.chain.get_receipt(id)?.ok_or(ChainError::Unknown) + } + + async fn get_full_access_keys( + &self, + account_id: &AccountId, + block_hash: &CryptoHash, + ) -> Result, ChainError> { + let mut ret = Vec::new(); + let header = self.chain.get_block_header(block_hash)?; + let shard_id = self + .epoch_manager + .account_id_to_shard_id(account_id, header.epoch_id()) + .into_chain_error()?; + let shard_uid = + self.epoch_manager.shard_id_to_uid(shard_id, header.epoch_id()).into_chain_error()?; + let chunk_extra = self.chain.get_chunk_extra(header.hash(), &shard_uid)?; + match self + .runtime + .query( + shard_uid, + chunk_extra.state_root(), + header.height(), + header.raw_timestamp(), + header.prev_hash(), + header.hash(), + header.epoch_id(), + &QueryRequest::ViewAccessKeyList { account_id: account_id.clone() }, + )? + .kind + { + QueryResponseKind::AccessKeyList(l) => { + for k in l.keys { + if k.access_key.permission == AccessKeyPermissionView::FullAccess { + ret.push(k.public_key); + } + } + } + _ => unreachable!(), + } + Ok(ret) + } +} diff --git a/tools/mirror/src/online.rs b/tools/mirror/src/online.rs new file mode 100644 index 000000000..ce84f1e51 --- /dev/null +++ b/tools/mirror/src/online.rs @@ -0,0 +1,238 @@ +use crate::{ChainError, SourceBlock, SourceChunk}; +use actix::Addr; +use anyhow::Context; +use async_trait::async_trait; +use unc_chain_configs::GenesisValidationMode; +use unc_client::ViewClientActor; +use unc_client_primitives::types::{ + GetBlock, GetBlockError, GetChunk, GetChunkError, GetExecutionOutcome, GetReceipt, Query, +}; +use unc_crypto::PublicKey; +use unc_o11y::WithSpanContextExt; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::Receipt; +use unc_primitives::types::{ + AccountId, BlockHeight, BlockId, BlockReference, Finality, TransactionOrReceiptId, +}; +use unc_primitives::views::{ + AccessKeyPermissionView, ExecutionOutcomeWithIdView, QueryRequest, QueryResponseKind, +}; +use unc_primitives_core::types::ShardId; +use std::path::Path; +use std::sync::Arc; +use std::time::Duration; + +pub(crate) struct ChainAccess { + view_client: Addr, +} + +impl ChainAccess { + pub(crate) fn new>(home: P) -> anyhow::Result { + let config = + framework::config::load_config(home.as_ref(), GenesisValidationMode::UnsafeFast) + .with_context(|| format!("Error loading config from {:?}", home.as_ref()))?; + + let node = framework::start_with_config(home.as_ref(), config) + .context("failed to start NEAR node")?; + Ok(Self { view_client: node.view_client }) + } +} + +#[async_trait(?Send)] +impl crate::ChainAccess for ChainAccess { + async fn init( + &self, + last_height: BlockHeight, + num_initial_blocks: usize, + ) -> anyhow::Result> { + // first wait until HEAD moves. We don't really need it to be fully synced. + let mut first_height = None; + loop { + match self.head_height().await { + Ok(head) => match first_height { + Some(h) => { + if h != head { + break; + } + } + None => { + first_height = Some(head); + } + }, + Err(ChainError::Unknown) => {} + Err(ChainError::Other(e)) => return Err(e), + }; + + tokio::time::sleep(Duration::from_millis(500)).await; + } + + let mut block_heights = Vec::with_capacity(num_initial_blocks); + let mut height = last_height; + + loop { + // note that here we are using the fact that get_next_block_height() for this struct + // allows passing a height that doesn't exist in the chain. This is not true for the offline + // version + match self.get_next_block_height(height).await { + Ok(h) => { + block_heights.push(h); + height = h; + if block_heights.len() >= num_initial_blocks { + return Ok(block_heights); + } + } + Err(ChainError::Unknown) => { + tokio::time::sleep(Duration::from_millis(500)).await; + } + Err(ChainError::Other(e)) => { + return Err(e) + .with_context(|| format!("failed fetching next block after #{}", height)) + } + } + } + } + + async fn block_height_to_hash(&self, height: BlockHeight) -> Result { + Ok(self + .view_client + .send(GetBlock(BlockReference::BlockId(BlockId::Height(height))).with_span_context()) + .await + .unwrap()? + .header + .hash) + } + + async fn head_height(&self) -> Result { + Ok(self + .view_client + .send(GetBlock(BlockReference::Finality(Finality::Final)).with_span_context()) + .await + .unwrap()? + .header + .height) + } + + async fn get_txs( + &self, + height: BlockHeight, + shards: &[ShardId], + ) -> Result { + let block_hash = self.block_height_to_hash(height).await?; + let mut chunks = Vec::new(); + for shard_id in shards.iter() { + let chunk = match self + .view_client + .send(GetChunk::Height(height, *shard_id).with_span_context()) + .await + .unwrap() + { + Ok(c) => c, + Err(e) => match e { + GetChunkError::UnknownChunk { .. } => { + tracing::error!( + "Can't fetch source chain shard {} chunk at height {}. Are we tracking all shards?", + shard_id, height + ); + continue; + } + _ => return Err(e.into()), + }, + }; + if chunk.header.height_included == height { + chunks.push(SourceChunk { + shard_id: *shard_id, + transactions: chunk.transactions.into_iter().map(Into::into).collect(), + receipts: chunk.receipts.into_iter().map(|r| r.try_into().unwrap()).collect(), + }) + } + } + + Ok(SourceBlock { hash: block_hash, chunks }) + } + + async fn get_next_block_height( + &self, + mut height: BlockHeight, + ) -> Result { + let head = self.head_height().await?; + + if height >= head { + // let's only return finalized heights + Err(ChainError::Unknown) + } else if height + 1 == head { + Ok(head) + } else { + loop { + height += 1; + if height >= head { + break Err(ChainError::Unknown); + } + match self + .view_client + .send( + GetBlock(BlockReference::BlockId(BlockId::Height(height))) + .with_span_context(), + ) + .await + .unwrap() + { + Ok(b) => break Ok(b.header.height), + Err(GetBlockError::UnknownBlock { .. }) => {} + Err(e) => break Err(ChainError::other(e)), + } + } + } + } + + async fn get_outcome( + &self, + id: TransactionOrReceiptId, + ) -> Result { + Ok(self + .view_client + .send(GetExecutionOutcome { id }.with_span_context()) + .await + .unwrap()? + .outcome_proof) + } + + async fn get_receipt(&self, id: &CryptoHash) -> Result, ChainError> { + self.view_client + .send(GetReceipt { receipt_id: *id }.with_span_context()) + .await + .unwrap()? + .map(|r| Arc::new(r.try_into().unwrap())) + .ok_or(ChainError::Unknown) + } + + async fn get_full_access_keys( + &self, + account_id: &AccountId, + block_hash: &CryptoHash, + ) -> Result, ChainError> { + let mut ret = Vec::new(); + match self + .view_client + .send( + Query { + block_reference: BlockReference::BlockId(BlockId::Hash(*block_hash)), + request: QueryRequest::ViewAccessKeyList { account_id: account_id.clone() }, + } + .with_span_context(), + ) + .await + .unwrap()? + .kind + { + QueryResponseKind::AccessKeyList(l) => { + for k in l.keys { + if k.access_key.permission == AccessKeyPermissionView::FullAccess { + ret.push(k.public_key); + } + } + } + _ => unreachable!(), + }; + Ok(ret) + } +} diff --git a/tools/mirror/src/secret.rs b/tools/mirror/src/secret.rs new file mode 100644 index 000000000..718e2363a --- /dev/null +++ b/tools/mirror/src/secret.rs @@ -0,0 +1,84 @@ +use rand_core::{OsRng, RngCore}; +use std::fs::File; +use std::io::Write; +use std::path::Path; +use std::str::FromStr; + +pub const SECRET_LEN: usize = 64; +struct KeyMapSecret([u8; SECRET_LEN]); + +#[derive(serde::Serialize, serde::Deserialize)] +struct MirrorSecretConfig { + pub key_map_secret: Option, +} + +impl serde::Serialize for KeyMapSecret { + fn serialize( + &self, + serializer: S, + ) -> Result<::Ok, ::Error> + where + S: serde::Serializer, + { + let data = bs58::encode(&self.0[..]).into_string(); + serializer.serialize_str(&data) + } +} + +impl<'de> serde::Deserialize<'de> for KeyMapSecret { + fn deserialize(deserializer: D) -> Result>::Error> + where + D: serde::Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + Self::from_str(&s).map_err(|err| serde::de::Error::custom(format!("{:?}", err))) + } +} + +#[derive(thiserror::Error, Debug)] +pub(crate) enum ParseSecretError { + #[error("Base58 decode failure: `{1}`")] + BS58(#[source] bs58::decode::Error, String), + #[error("invalid decoded length (expected: 64, got: {0}: input: `{1}`)")] + BadLength(usize, String), +} + +impl FromStr for KeyMapSecret { + type Err = ParseSecretError; + + fn from_str(s: &str) -> Result { + let mut array = [0; SECRET_LEN]; + let length = bs58::decode(s) + .into(&mut array[..]) + .map_err(|err| Self::Err::BS58(err, s.to_owned()))?; + if length != SECRET_LEN { + return Err(Self::Err::BadLength(length, s.to_owned())); + } + Ok(Self(array)) + } +} + +pub(crate) fn generate>(secret_file_out: P) -> anyhow::Result<[u8; SECRET_LEN]> { + let mut secret = [0; SECRET_LEN]; + let mut out = File::create(secret_file_out)?; + + OsRng.fill_bytes(&mut secret); + let config = MirrorSecretConfig { key_map_secret: Some(KeyMapSecret(secret)) }; + let str = serde_json::to_string_pretty(&config)?; + out.write_all(str.as_bytes())?; + Ok(secret) +} + +pub(crate) fn write_empty>(secret_file_out: P) -> anyhow::Result<()> { + let mut out = File::create(secret_file_out)?; + let config = MirrorSecretConfig { key_map_secret: None }; + let str = serde_json::to_string_pretty(&config)?; + out.write_all(str.as_bytes())?; + Ok(()) +} + +pub fn load>(secret_file: P) -> anyhow::Result> { + let s = std::fs::read_to_string(secret_file)?; + let config: MirrorSecretConfig = serde_json::from_str(&s)?; + Ok(config.key_map_secret.map(|s| s.0)) +} diff --git a/tools/mock-node/Cargo.toml b/tools/mock-node/Cargo.toml new file mode 100644 index 000000000..a4264acba --- /dev/null +++ b/tools/mock-node/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "mock-node" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix-rt.workspace = true +actix.workspace = true +anyhow.workspace = true +clap.workspace = true +futures.workspace = true +pin-project.workspace = true +rand.workspace = true +rayon.workspace = true +serde.workspace = true +serde_json.workspace = true +tempfile.workspace = true +tokio.workspace = true +tracing.workspace = true + +unc-actix-test-utils.workspace = true +unc-async.workspace = true +unc-chain.workspace = true +unc-chain-configs.workspace = true +unc-client.workspace = true +unc-crypto.workspace = true +unc-chunks.workspace = true +unc-epoch-manager.workspace = true +unc-jsonrpc.workspace = true +unc-jsonrpc-client.workspace = true +unc-network.workspace = true +unc-store.workspace = true +unc-o11y.workspace = true +unc-telemetry.workspace = true +unc-performance-metrics.workspace = true +unc-primitives.workspace = true +framework.workspace = true + +[[bin]] +name = "mock-node" + +[features] +test_features = ["framework/test_features"] diff --git a/tools/mock-node/README.md b/tools/mock-node/README.md new file mode 100644 index 000000000..e86021199 --- /dev/null +++ b/tools/mock-node/README.md @@ -0,0 +1,90 @@ +# mock-node +This crate hosts libraries to start a test env for a single node by replacing the network module with a mock network environment. +The mock network environment simulates the interaction that the client will usually have with other nodes by +responding to the client's network messages and broadcasting new blocks. The mock network reads a pre-generated chain +history from storage. + +## Quick Start + +```console +$ cargo run --release -p mock-node -- ~/.near/localnet/node0 +``` + +where the `node0` directory contains some pre-generated chain history in storage. +You can find two examples in the ./benches directory. + +If you are running a mock node for mainnet or testnet on a GCP node, you want to place the new client home +dir on a SSD disk for optimal rocksdb performance. Note that the +default booting disk of GCP notes are HDD, so you need to mount a new SSD disk on +your node and put the mock node's home dir there. See https://cloud.google.com/compute/docs/disks/add-persistent-disk +for how to attach and mount a new disk to an existing GCP node. + +See `$ cargo run -p mock-node -- --help` for the list of available options and their documentation. + +## Examples + +#### Replay localnet history + +```console +$ cargo r -r -p mock-node -- ~/.near/localnet/node0 +``` +Here we take the home dir of an existing node in a localnet as chain history home dir, +so the mock network will reproduce the client catching up with the entire history of the localnet from genesis. + +#### Replay mainnet history from a certain height + +To replay mainnet or testnet history, in most use cases, we want to start replaying from a certain height, instead +of from genesis block. The following comment replays mainnet history from block height 60925880 to block height 60925900. + +```console +$ cargo r -r -p mock-node -- ~/.near ~/mock_node_home_dir --start_height 60925880 --target-height 60925900 +``` + +By providing a starting height, +the binary will set up the data dir before starting the client, by copying the state at the specified height +and other chain info necessary for processing the blocks afterwards (such as block headers and blocks). +This initial setup may take a long time (The exact depends on your +source dir, my experiment takes about an hour from a non-archival source dir. Copying from archival node source +dir may take longer as rocksdb is slower). So we suggest specifying a client dir (the `~/mock_node_home_dir` argument) +so you can reuse it again without having to copy the state again. + +Note that the start height must be the last block of an epoch. + +Once you have the source dir already set up, you can run the command without `--start_height`, + +```console +$ cargo r -r -p mock-node -- ~/.near ~/mock_node_home_dir --target-height 60926000 +``` +Without `--starting_height`, the binary will not modify the client home dir before starting the mock node. Therefore, +the mock node will start from the chain head stored in the client dir. + +## Mock Network Configuration + +Certain details around how the mock network behaves can be configured with the file `mock.json` in the chain history +home directory. Currently, the only supported configuration options tell how long to wait before replying to requests +(the same as the --network_delay flag), and how often to send unrequested blocks and chunk part requests. By default, +no such unrequested messages are sent, but the following config file will have the mock code produce unrequested +blocks every 100 milliseconds, and chunk part requests every 50 milliseconds. + +```json +{ + "response_delay": { + "secs": 0, + "nanos": 100000000 + }, + "incoming_requests": { + "block": { + "interval": { + "secs": 0, + "nanos": 100000000 + } + }, + "chunk_request": { + "interval": { + "secs": 0, + "nanos": 50000000 + } + } + } +} +``` diff --git a/tools/mock-node/src/lib.rs b/tools/mock-node/src/lib.rs new file mode 100644 index 000000000..8b6b11785 --- /dev/null +++ b/tools/mock-node/src/lib.rs @@ -0,0 +1,490 @@ +//! Implements `ChainHistoryAccess` and `MockPeerManagerActor`, which is the main +//! components of the mock network. + +use anyhow::{anyhow, Context as AnyhowContext}; +use unc_async::time; +use unc_chain::{Block, Chain, ChainStoreAccess, Error}; +use unc_client::sync::header::MAX_BLOCK_HEADERS; +use unc_crypto::SecretKey; +use unc_network::raw::{DirectMessage, Listener, Message, RoutedMessage}; +use unc_network::tcp; +use unc_network::types::{PartialEncodedChunkRequestMsg, PartialEncodedChunkResponseMsg}; +use unc_primitives::sharding::ChunkHash; +use unc_primitives::types::{BlockHeight, ShardId}; +use std::collections::{HashMap, HashSet, VecDeque}; +use std::future::Future; +use std::path::Path; +use std::pin::Pin; +use std::task::Poll; +use std::time::Duration; + +pub mod setup; + +// For now this is a simple struct with one field just to leave the door +// open for adding stuff and/or having different configs for different message types later. +#[derive(Clone, Debug, serde::Deserialize)] +pub struct MockIncomingRequestConfig { + // How long we wait between sending each incoming request + interval: Duration, +} + +#[derive(Clone, Debug, serde::Deserialize)] +pub struct MockIncomingRequestsConfig { + // Options for sending unrequested blocks + block: Option, + // Options for sending chunk part requests + chunk_request: Option, +} + +#[derive(Clone, Debug, serde::Deserialize)] +pub struct MockNetworkConfig { + #[serde(default = "default_delay")] + // How long we'll wait until sending replies to the client + pub response_delay: Duration, + pub incoming_requests: Option, +} + +impl MockNetworkConfig { + pub fn with_delay(response_delay: Duration) -> Self { + let mut ret = Self::default(); + ret.response_delay = response_delay; + ret + } + + pub fn from_file>(path: &P) -> anyhow::Result { + let s = std::fs::read_to_string(path)?; + Ok(serde_json::from_str(&s)?) + } +} + +pub const MOCK_DEFAULT_NETWORK_DELAY: Duration = Duration::from_millis(100); + +fn default_delay() -> Duration { + MOCK_DEFAULT_NETWORK_DELAY +} + +impl Default for MockNetworkConfig { + fn default() -> Self { + Self { response_delay: default_delay(), incoming_requests: None } + } +} + +// A request we want to spam the node under test with over and over +#[derive(Debug)] +struct PeriodicRequest { + interval: tokio::time::Interval, + message: Message, +} + +async fn next_request(r: Option<&mut PeriodicRequest>) -> Message { + match r { + Some(r) => { + r.interval.tick().await; + r.message.clone() + } + None => futures::future::pending().await, + } +} + +#[derive(Debug)] +// Info related to unrequested messages we'll send to the client +struct IncomingRequests { + block: Option, + chunk_request: Option, +} + +// get some chunk hash to serve as the source of unrequested incoming chunks. +// For now we find the first chunk hash we know about starting from the height the client will start at. +// The lower the height, the better, so that the client will actually do some work on these +// requests instead of just seeing that the chunk hash is unknown. +fn retrieve_starting_chunk_hash( + chain: &Chain, + head_height: BlockHeight, +) -> anyhow::Result { + let mut last_err = None; + for height in (chain.tail().context("failed fetching chain tail")? + 1..=head_height).rev() { + match chain + .chain_store() + .get_block_hash_by_height(height) + .and_then(|hash| chain.chain_store().get_block(&hash)) + .map(|block| block.chunks().iter().next().unwrap().chunk_hash()) + { + Ok(hash) => return Ok(hash), + Err(e) => { + last_err = Some(e); + } + } + } + match last_err { + Some(e) => { + Err(e).with_context(|| format!("Last error (retrieving chunk hash @ #{})", head_height)) + } + None => Err(anyhow!("given head_height is not after the chain tail?")), + } +} + +// get some block to serve as the source of unrequested incoming blocks. +fn retrieve_incoming_block(chain: &Chain, head_height: BlockHeight) -> anyhow::Result { + let mut last_err = None; + for height in (chain.tail().context("failed fetching chain tail")? + 1..=head_height).rev() { + match chain.get_block_by_height(height) { + Ok(b) => return Ok(b), + Err(e) => { + last_err = Some(e); + } + } + } + match last_err { + Some(e) => { + Err(e).with_context(|| format!("Last error (retrieving block #{})", head_height)) + } + None => Err(anyhow!("given head_height is not after the chain tail?")), + } +} + +impl IncomingRequests { + fn new( + config: &Option, + chain: &Chain, + head_height: BlockHeight, + ) -> Self { + let now = std::time::Instant::now(); + let mut block = None; + let mut chunk_request = None; + + if let Some(config) = config { + if let Some(block_config) = &config.block { + match retrieve_incoming_block(chain, head_height) { + Ok(b) => { + block = Some(PeriodicRequest { + interval: tokio::time::interval_at( + (now + block_config.interval).into(), + block_config.interval, + ), + message: Message::Direct(DirectMessage::Block(b)), + }); + } + Err(e) => { + tracing::error!("Can't retrieve block suitable for mock messages: {:?}", e); + } + }; + } + if let Some(chunk_request_config) = &config.chunk_request { + match retrieve_starting_chunk_hash(chain, head_height) { + Ok(chunk_hash) => { + chunk_request = Some(PeriodicRequest { + interval: tokio::time::interval_at( + (now + chunk_request_config.interval).into(), + chunk_request_config.interval, + ), + message: Message::Routed(RoutedMessage::PartialEncodedChunkRequest( + PartialEncodedChunkRequestMsg { + chunk_hash, + part_ords: vec![0], + tracking_shards: std::iter::once(0).collect::>(), + }, + )), + }); + } + Err(e) => { + tracing::error!( + "Can't construct chunk part request suitable for mock messages: {:?}", + e + ); + } + }; + } + } + + Self { block, chunk_request } + } + + // If the user told us to spam the node with incoming messages via the mock.json + // config file, this function will produce them at the rate specified there. + async fn next(&mut self) -> Message { + tokio::select! { + msg = next_request(self.block.as_mut()) => { + msg + } + msg = next_request(self.chunk_request.as_mut()) => { + msg + } + } + } +} + +struct InFlightMessage { + message: Message, + sent_at: tokio::time::Instant, +} + +// type that simulates network latency by waiting for `response_delay` +// before delivering queued up messages +#[pin_project::pin_project] +struct InFlightMessages { + #[pin] + next_delivery: tokio::time::Sleep, + messages: VecDeque, + response_delay: Duration, +} + +impl InFlightMessages { + fn new(response_delay: Duration) -> Self { + Self { + next_delivery: tokio::time::sleep(Duration::ZERO), + messages: VecDeque::new(), + response_delay, + } + } + + fn queue_message(self: Pin<&mut Self>, message: Message) { + let me = self.project(); + let now = tokio::time::Instant::now(); + if me.messages.is_empty() { + me.next_delivery.reset(now + *me.response_delay); + } + tracing::debug!( + "mock peer queueing up message {} to be delivered in {:?}", + &message, + me.response_delay + ); + me.messages.push_back(InFlightMessage { message, sent_at: now }); + } +} + +impl Future for InFlightMessages { + type Output = Message; + + fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { + if self.messages.is_empty() { + Poll::Pending + } else { + let mut me = self.project(); + match me.next_delivery.as_mut().poll(cx) { + Poll::Ready(()) => { + let msg = me.messages.pop_front().unwrap(); + if let Some(m) = me.messages.front() { + // if there's another message after the one we're returning here, reset + // the time til the next message gets delivered accordingly. + me.next_delivery.as_mut().reset(m.sent_at + *me.response_delay); + } + Poll::Ready(msg.message) + } + Poll::Pending => Poll::Pending, + } + } + } +} + +struct MockPeer { + listener: Listener, + chain: Chain, + current_height: BlockHeight, + network_config: MockNetworkConfig, + block_production: tokio::time::Interval, + incoming_requests: IncomingRequests, +} + +impl MockPeer { + async fn new( + chain: Chain, + secret_key: SecretKey, + listen_addr: tcp::ListenerAddr, + chain_id: String, + archival: bool, + block_production_delay: Duration, + num_shards: ShardId, + network_start_height: BlockHeight, + network_config: MockNetworkConfig, + ) -> anyhow::Result { + let listener = Listener::bind( + listen_addr, + secret_key, + &chain_id, + *chain.genesis().hash(), + network_start_height, + (0..num_shards).collect(), + archival, + 30 * time::Duration::SECOND, + ) + .await?; + let incoming_requests = + IncomingRequests::new(&network_config.incoming_requests, &chain, network_start_height); + // make sure we start at a height that actually exists, because we want self.produce_block() + // to give the first block immediately. Otherwise the node won't even try asking us for block headers + // until we give it a block. + let tail = chain.tail().context("failed getting chain tail")?; + let mut current_height = None; + for height in (tail..=network_start_height).rev() { + if chain.get_block_by_height(height).is_ok() { + current_height = Some(height); + break; + } + } + let current_height = match current_height { + Some(h) => h, + None => anyhow::bail!( + "No block found between tail {} and network start height {}", + tail, + network_start_height + ), + }; + Ok(Self { + listener, + chain, + current_height, + network_config, + block_production: tokio::time::interval(block_production_delay), + incoming_requests, + }) + } + + fn handle_message( + &self, + message: Message, + outbound: Pin<&mut InFlightMessages>, + ) -> anyhow::Result<()> { + tracing::debug!("mock peer received message: {}", &message); + match message { + Message::Direct(msg) => { + match msg { + DirectMessage::BlockHeadersRequest(hashes) => { + let headers = self + .chain + .retrieve_headers(hashes, MAX_BLOCK_HEADERS, Some(self.current_height)) + .with_context(|| { + format!( + "failed retrieving block headers up to {}", + self.current_height + ) + })?; + outbound + .queue_message(Message::Direct(DirectMessage::BlockHeaders(headers))); + } + DirectMessage::BlockRequest(hash) => { + let block = self + .chain + .get_block(&hash) + .with_context(|| format!("failed getting block {}", &hash))?; + outbound.queue_message(Message::Direct(DirectMessage::Block(block))); + } + _ => {} + }; + } + Message::Routed(r) => { + match r { + RoutedMessage::PartialEncodedChunkRequest(request) => { + let response = retrieve_partial_encoded_chunk(&self.chain, &request) + .with_context(|| { + format!( + "failed getting partial encoded chunk response for {:?}", + &request + ) + })?; + outbound.queue_message(Message::Routed( + RoutedMessage::PartialEncodedChunkResponse(response), + )); + } + // TODO: add state sync requests to possible request types so we can either + // respond or just exit, saying we don't know how to do that + _ => {} + } + } + }; + Ok(()) + } + + // simulate the normal block production of the network by sending out a + // "new" block at an interval set by the config's block_production_delay field + fn produce_block(&mut self) -> anyhow::Result> { + let height = self.current_height; + self.current_height += 1; + match self.chain.get_block_by_height(height) { + Ok(b) => Ok(Some(b)), + Err(unc_chain::Error::DBNotFoundErr(_)) => Ok(None), + Err(e) => Err(e.into()), + } + } + + // returns a message produced by this mock peer. Right now this includes a new block + // at a rate given by block_production_delay in the config, and extra chunk part requests + // and blocks as specified by the mock.json config + async fn incoming_message(&mut self, target_height: BlockHeight) -> anyhow::Result { + loop { + tokio::select! { + msg = self.incoming_requests.next() => { + return Ok(msg); + } + _ = self.block_production.tick(), if self.current_height <= target_height => { + if let Some(block) = self.produce_block()? { + return Ok(Message::Direct(DirectMessage::Block(block))); + } + } + } + } + } + + // listen on the addr passed to MockPeer::new() and wait til someone connects. + // Then respond to messages indefinitely until an error occurs + async fn run(mut self, target_height: BlockHeight) -> anyhow::Result<()> { + let mut conn = self.listener.accept().await?; + let messages = InFlightMessages::new(self.network_config.response_delay); + tokio::pin!(messages); + + loop { + tokio::select! { + res = conn.recv() => { + let (msg, _timestamp) = res.with_context(|| format!("failed receiving message from {:?}", &conn))?; + + self.handle_message(msg, messages.as_mut())?; + } + msg = &mut messages => { + tracing::debug!("mock peer sending message {}", &msg); + match msg { + Message::Direct(msg) => conn.send_message(msg).await?, + Message::Routed(msg) => conn.send_routed_message(msg, conn.peer_id().clone(), 100).await?, + }; + } + msg = self.incoming_message(target_height) => { + let msg = msg?; + messages.as_mut().queue_message(msg); + } + } + } + } +} + +// TODO: this is not currently correct if we're an archival node and we get +// asked about an old chunk. In that case it needs to be reconstructed like +// in ShardsManager::prepare_partial_encoded_chunk_response() +fn retrieve_partial_encoded_chunk( + chain: &Chain, + request: &PartialEncodedChunkRequestMsg, +) -> Result { + let num_total_parts = chain.epoch_manager.num_total_parts(); + let partial_chunk = chain.chain_store().get_partial_chunk(&request.chunk_hash)?; + let present_parts: HashMap = + partial_chunk.parts().iter().map(|part| (part.part_ord, part)).collect(); + assert_eq!( + present_parts.len(), + num_total_parts, + "chunk {:?} doesn't have all parts", + request.chunk_hash + ); + let parts: Vec<_> = request + .part_ords + .iter() + .map(|ord| present_parts.get(ord).cloned().cloned().unwrap()) + .collect(); + + // Same process for receipts as above for parts. + let present_receipts: HashMap = + partial_chunk.receipts().iter().map(|receipt| (receipt.1.to_shard_id, receipt)).collect(); + let receipts: Vec<_> = request + .tracking_shards + .iter() + .map(|shard_id| present_receipts.get(shard_id).cloned().cloned().unwrap()) + .collect(); + + Ok(PartialEncodedChunkResponseMsg { chunk_hash: request.chunk_hash.clone(), parts, receipts }) +} diff --git a/tools/mock-node/src/main.rs b/tools/mock-node/src/main.rs new file mode 100644 index 000000000..085a36efd --- /dev/null +++ b/tools/mock-node/src/main.rs @@ -0,0 +1,202 @@ +//! A binary that starts a mock testing environment for ClientActor. It +//! simulates the entire network by substituting PeerManagerActor with a mock +//! network, responding to the client's network requests by reading from a +//! pre-generated chain history in storage. + +use actix::System; +use anyhow::Context; +use mock_node::setup::{setup_mock_node, MockNode}; +use mock_node::MockNetworkConfig; +use unc_actix_test_utils::run_actix; +use unc_chain_configs::GenesisValidationMode; +use unc_crypto::{InMemorySigner, KeyType}; +use unc_jsonrpc_client::JsonRpcClient; +use unc_network::tcp; +use unc_o11y::testonly::init_integration_logger; +use unc_primitives::types::BlockHeight; +use std::net::SocketAddr; +use std::path::{Path, PathBuf}; +use std::time::{Duration, Instant}; + +/// Program to start a mock node, which runs a regular client in a mock network environment. +/// The mock network simulates the entire network by replaying a pre-generated chain history +/// from storage and responds to the client's network requests. +/// +/// There are two ways to replay the stored history: +/// * catchup: client is behind the network and applies the blocks as fast as possible +/// * normal block production: client accept "new" blocks as they are produced +/// (in reality, blocks are just fetched from the pre-generated store). +/// +/// This is controlled by two flags: +/// * `--client-height` specifies the height the client starts at. Defaults to 0. +/// * `--network-height` specifies the hight the rest of the (simulated) +/// network starts at. Defaults to the latest recorded height. +/// +/// As a shortcut, `--start-height` sets both. +/// +/// +/// Examples +/// +/// ```console +/// # Pure catchup from genesis height to the end of the recorded history. +/// $ mock-node ~/.near/localnet/node0 +/// +/// # Pure block production starting from block height 61. +/// $ mock-node ~/.near/localnet/node0 --start-height 61 +/// +/// # Mixed: client starts at genesis and tries to catch up with the network, which starts at height 20. +/// $ mock-node ~/.near/localnet/node0 --network-height 20 +/// ``` +#[derive(clap::Parser)] +struct Cli { + /// Existing home dir for the pre-generated chain history. For example, you can use + /// the home dir of a near node. + chain_history_home_dir: String, + /// Home dir for the new client that will be started. If not specified, the binary will + /// generate a temporary directory + client_home_dir: Option, + /// Simulated network delay (in ms) + #[clap(short = 'd', long)] + network_delay: Option, + /// If specified, the binary will set up client home dir before starting the + /// client node so head of the client chain will be the specified height + /// when the client starts. The given height must be the last block in an + /// epoch. + #[clap(long, default_value = "0")] + client_height: BlockHeight, + /// The height at which the mock network starts. The client would have to + /// catch up to this height before participating in new block production. + /// + /// Defaults to the largest height in history. + #[clap(long)] + network_height: Option, + /// Shortcut to set both `--client-height` and `--network-height`. + #[clap(long, conflicts_with_all(&["client-height", "network-height"]))] + start_height: Option, + /// Target height that the client should sync to before stopping. If not specified, + /// use the height of the last block in chain history + #[clap(long)] + target_height: Option, + /// If true, use in memory storage instead of rocksdb for the client + #[clap(short = 'i', long)] + in_memory_storage: bool, + /// port the mock node should listen on + #[clap(long)] + mock_port: Option, +} + +async fn target_height_reached(client: &JsonRpcClient, target_height: BlockHeight) -> bool { + let t = Instant::now(); + let status = client.status().await; + let latency = t.elapsed(); + if latency > Duration::from_millis(100) { + tracing::warn!( + target: "mock_node", latency = %format_args!("{latency:0.2?}"), + "client is unresponsive, took too long to handle status request" + ); + } + match status { + Ok(status) => status.sync_info.latest_block_height >= target_height, + Err(_) => false, + } +} + +fn main() -> anyhow::Result<()> { + init_integration_logger(); + let args: Cli = clap::Parser::parse(); + let home_dir = Path::new(&args.chain_history_home_dir); + let mut unc_config = framework::config::load_config(home_dir, GenesisValidationMode::Full) + .context("Error loading config")?; + unc_config.validator_signer = None; + unc_config.client_config.min_num_peers = 1; + let signer = InMemorySigner::from_random("mock_node".parse().unwrap(), KeyType::ED25519); + unc_config.network_config.node_key = signer.secret_key; + unc_config.client_config.tracked_shards = + unc_config.genesis.config.shard_layout.shard_ids().collect(); + if unc_config.rpc_config.is_none() { + unc_config.rpc_config = Some(unc_jsonrpc::RpcConfig::default()); + } + let tempdir; + let client_home_dir = match &args.client_home_dir { + Some(it) => it.as_path(), + None => { + tempdir = tempfile::Builder::new().prefix("mock_node").tempdir().unwrap(); + tempdir.path() + } + }; + + let mock_config_path = home_dir.join("mock.json"); + let mut network_config = if mock_config_path.exists() { + MockNetworkConfig::from_file(&mock_config_path).with_context(|| { + format!("Error loading mock config from {}", mock_config_path.display()) + })? + } else { + MockNetworkConfig::default() + }; + if let Some(delay) = args.network_delay { + network_config.response_delay = Duration::from_millis(delay); + } + + let client_height = args.start_height.unwrap_or(args.client_height); + let network_height = args.start_height.or(args.network_height); + let addr = tcp::ListenerAddr::new(SocketAddr::new( + "127.0.0.1".parse().unwrap(), + args.mock_port.unwrap_or(24566), + )); + + run_actix(async move { + let MockNode { target_height, mut mock_peer, rpc_client } = setup_mock_node( + Path::new(&client_home_dir), + home_dir, + unc_config, + &network_config, + client_height, + network_height, + args.target_height, + args.in_memory_storage, + addr, + ); + + // TODO: would be nice to be able to somehow quit right after the target block + // is applied rather than polling like this + let mut interval = tokio::time::interval(Duration::from_millis(100)); + interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); + + let start = Instant::now(); + // Let's set the timeout to 5 seconds per block - just in case we test on very full blocks. + let timeout = target_height * 5; + let timeout = u32::try_from(timeout).unwrap_or(u32::MAX) * Duration::from_secs(1); + + loop { + if start.elapsed() > timeout { + tracing::error!( + "node still hasn't made it to #{} after {:?}", + target_height, + timeout + ); + mock_peer.abort(); + break; + } + tokio::select! { + _ = interval.tick() => { + if target_height_reached(&rpc_client, target_height).await { + tracing::info!("node reached target height"); + mock_peer.abort(); + break; + } + } + result = &mut mock_peer => { + match result { + Ok(Ok(_)) => tracing::info!("mock peer exited"), + Ok(Err(e)) => tracing::error!("mock peer exited with error: {:?}", e), + Err(e) => tracing::error!("failed running mock peer task: {:?}", e), + }; + break; + } + } + } + + System::current().stop(); + }); + Ok(()) +} diff --git a/tools/mock-node/src/setup.rs b/tools/mock-node/src/setup.rs new file mode 100644 index 000000000..13e3a6bd5 --- /dev/null +++ b/tools/mock-node/src/setup.rs @@ -0,0 +1,461 @@ +//! Provides functions for setting up a mock network from configs and home dirs. + +use crate::{MockNetworkConfig, MockPeer}; +use anyhow::Context; +use unc_chain::types::RuntimeAdapter; +use unc_chain::ChainStoreUpdate; +use unc_chain::{Chain, ChainGenesis, ChainStore, ChainStoreAccess, DoomslugThresholdMode}; +use unc_crypto::{KeyType, SecretKey}; +use unc_epoch_manager::shard_tracker::{ShardTracker, TrackedConfig}; +use unc_epoch_manager::{EpochManager, EpochManagerAdapter, EpochManagerHandle}; +use unc_jsonrpc_client::JsonRpcClient; +use unc_network::tcp; +use unc_network::types::PeerInfo; +use unc_primitives::network::PeerId; +use unc_primitives::state_part::PartId; +use unc_primitives::state_sync::get_num_state_parts; +use unc_primitives::types::{BlockHeight, NumShards, ShardId}; +use unc_store::test_utils::create_test_store; +use framework::{UncConfig, NightshadeRuntime}; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use std::cmp::min; +use std::path::Path; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; + +fn setup_runtime( + home_dir: &Path, + config: &UncConfig, + in_memory_storage: bool, +) -> (Arc, ShardTracker, Arc) { + let store = if in_memory_storage { + create_test_store() + } else { + unc_store::NodeStorage::opener(home_dir, config.config.archive, &config.config.store, None) + .open() + .unwrap() + .get_hot_store() + }; + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &config.genesis.config); + let shard_tracker = + ShardTracker::new(TrackedConfig::from_config(&config.client_config), epoch_manager.clone()); + let runtime = NightshadeRuntime::from_config(home_dir, store, config, epoch_manager.clone()); + (epoch_manager, shard_tracker, runtime) +} + +fn setup_mock_peer( + chain: Chain, + config: &mut UncConfig, + network_start_height: Option, + network_config: MockNetworkConfig, + target_height: BlockHeight, + num_shards: ShardId, + mock_listen_addr: tcp::ListenerAddr, +) -> tokio::task::JoinHandle> { + let network_start_height = match network_start_height { + None => target_height, + Some(0) => chain.genesis_block().header().height(), + Some(it) => it, + }; + let secret_key = SecretKey::from_random(KeyType::ED25519); + config + .network_config + .peer_store + .boot_nodes + .push(PeerInfo::new(PeerId::new(secret_key.public_key()), *mock_listen_addr)); + let chain_id = config.genesis.config.chain_id.clone(); + let block_production_delay = config.client_config.min_block_production_delay; + let archival = config.client_config.archive; + actix::spawn(async move { + let mock = MockPeer::new( + chain, + secret_key, + mock_listen_addr, + chain_id, + archival, + block_production_delay, + num_shards, + network_start_height, + network_config, + ) + .await?; + mock.run(target_height).await + }) +} + +pub struct MockNode { + // target height actually available to sync to in the chain history database + pub target_height: BlockHeight, + pub mock_peer: tokio::task::JoinHandle>, + // client that allows making RPC requests to the node under test + pub rpc_client: JsonRpcClient, +} + +/// Setup up a mock node, including setting up +/// a MockPeerManagerActor and a ClientActor and a ViewClientActor +/// `client_home_dir`: home dir for the new client +/// `network_home_dir`: home dir that contains the pre-generated chain history, will be used +/// to construct `MockPeerManagerActor` +/// `config`: config for the new client +/// `network_delay`: delay for getting response from the simulated network +/// `client_start_height`: start height for client +/// `network_start_height`: height at which the simulated network starts producing blocks +/// `target_height`: height that the simulated peers will produce blocks until. If None, will +/// use the height from the chain head in storage +/// `in_memory_storage`: if true, make client use in memory storage instead of rocksdb +/// +/// Returns a struct representing the node under test +pub fn setup_mock_node( + client_home_dir: &Path, + network_home_dir: &Path, + mut config: UncConfig, + network_config: &MockNetworkConfig, + client_start_height: BlockHeight, + network_start_height: Option, + target_height: Option, + in_memory_storage: bool, + mock_listen_addr: tcp::ListenerAddr, +) -> MockNode { + let parent_span = tracing::debug_span!(target: "mock_node", "setup_mock_node").entered(); + let (mock_network_epoch_manager, mock_network_shard_tracker, mock_network_runtime) = + setup_runtime(network_home_dir, &config, false); + tracing::info!(target: "mock_node", ?network_home_dir, "Setup network runtime"); + + let chain_genesis = ChainGenesis::new(&config.genesis); + + // set up client dir to be ready to process blocks from client_start_height + if client_start_height > 0 { + tracing::info!(target: "mock_node", "Preparing client data dir to be able to start at the specified start height {}", client_start_height); + let (client_epoch_manager, _, client_runtime) = + setup_runtime(client_home_dir, &config, in_memory_storage); + tracing::info!(target: "mock_node", ?client_home_dir, "Setup client runtime"); + let mut chain_store = ChainStore::new( + client_runtime.store().clone(), + config.genesis.config.genesis_height, + config.client_config.save_trie_changes, + ); + let mut network_chain_store = ChainStore::new( + mock_network_runtime.store().clone(), + config.genesis.config.genesis_height, + config.client_config.save_trie_changes, + ); + + let network_tail_height = network_chain_store.tail().unwrap(); + let network_head_height = network_chain_store.head().unwrap().height; + tracing::info!(target: "mock_node", network_tail_height, network_head_height, "network data chain"); + assert!( + client_start_height <= network_head_height + && client_start_height >= network_tail_height, + "client start height {} is not within the network chain range [{}, {}]", + client_start_height, + network_tail_height, + network_head_height + ); + let hash = network_chain_store.get_block_hash_by_height(client_start_height).unwrap(); + tracing::info!(target: "mock_node", "Checking whether the given start height is the last block of an epoch."); + if !mock_network_epoch_manager.is_next_block_epoch_start(&hash).unwrap() { + let epoch_start_height = + mock_network_epoch_manager.get_epoch_start_height(&hash).unwrap(); + panic!( + "start height must be the last block of an epoch, try using {} instead.", + epoch_start_height - 1 + ); + } + + // copy chain info + let chain_store_update = ChainStoreUpdate::copy_chain_state_as_of_block( + &mut chain_store, + &hash, + mock_network_epoch_manager.as_ref(), + &mut network_chain_store, + ) + .unwrap(); + chain_store_update.commit().unwrap(); + tracing::info!(target: "mock_node", "Done preparing chain state"); + + client_epoch_manager + .write() + .copy_epoch_info_as_of_block(&hash, &mock_network_epoch_manager.read()) + .unwrap(); + tracing::info!(target: "mock_node", "Done preparing epoch info"); + + // copy state for all shards + let block = network_chain_store.get_block(&hash).unwrap(); + let prev_hash = *block.header().prev_hash(); + for (shard_id, chunk_header) in block.chunks().iter().enumerate() { + let shard_id = shard_id as u64; + let state_root = chunk_header.prev_state_root(); + let state_root_node = + mock_network_runtime.get_state_root_node(shard_id, &hash, &state_root).unwrap(); + let num_parts = get_num_state_parts(state_root_node.memory_usage); + let finished_parts_count = Arc::new(AtomicUsize::new(0)); + tracing::info!(target: "mock_node", ?shard_id, ?state_root, num_parts, "Preparing state for a shard"); + + (0..num_parts) + .into_par_iter() + .try_for_each(|part_id| -> anyhow::Result<()> { + let _span = tracing::debug_span!( + target: "mock_node", + parent: &parent_span, + "obtain_and_apply_state_part", + part_id, + shard_id) + .entered(); + + let state_part = mock_network_runtime + .obtain_state_part( + shard_id, + &prev_hash, + &state_root, + PartId::new(part_id, num_parts), + ) + .with_context(|| { + format!("Obtaining state part {} in shard {}", part_id, shard_id) + })?; + client_runtime + .apply_state_part( + shard_id, + &state_root, + PartId::new(part_id, num_parts), + &state_part, + &mock_network_epoch_manager.get_epoch_id_from_prev_block(&hash)?, + ) + .with_context(|| { + format!("Applying state part {} in shard {}", part_id, shard_id) + })?; + finished_parts_count.fetch_add(1, Ordering::SeqCst); + tracing::info!( + target: "mock_node", + "Done {}/{} parts for shard {}", + finished_parts_count.load(Ordering::SeqCst), + num_parts, + shard_id, + ); + Ok(()) + }) + .unwrap(); + } + } + + let chain = Chain::new_for_view_client( + mock_network_epoch_manager.clone(), + mock_network_shard_tracker, + mock_network_runtime, + &chain_genesis, + DoomslugThresholdMode::NoApprovals, + config.client_config.save_trie_changes, + ) + .unwrap(); + let head = chain.head().unwrap(); + let target_height = min(target_height.unwrap_or(head.height), head.height); + let num_shards = + mock_network_epoch_manager.shard_ids(&head.epoch_id).unwrap().len() as NumShards; + + config.network_config.peer_store.boot_nodes.clear(); + let mock_peer = setup_mock_peer( + chain, + &mut config, + network_start_height, + network_config.clone(), + target_height, + num_shards, + mock_listen_addr, + ); + + let rpc_client = unc_jsonrpc_client::new_client(&format!( + "http://{}", + &config.rpc_config.as_ref().expect("the JSON RPC config must be set").addr + )); + let _node = framework::start_with_config(client_home_dir, config).unwrap(); + + MockNode { target_height, mock_peer, rpc_client } +} + +#[cfg(test)] +mod tests { + use crate::setup::{setup_mock_node, MockNode}; + use crate::MockNetworkConfig; + use actix::{Actor, System}; + use futures::{future, FutureExt}; + use unc_actix_test_utils::{run_actix, spawn_interruptible}; + use unc_chain::{ChainStore, ChainStoreAccess}; + use unc_chain_configs::Genesis; + use unc_client::{GetBlock, ProcessTxRequest}; + use unc_crypto::{InMemorySigner, KeyType}; + use unc_epoch_manager::{EpochManager, EpochManagerAdapter}; + use unc_network::tcp; + use unc_network::test_utils::{wait_or_timeout, WaitOrTimeoutActor}; + use unc_o11y::testonly::init_integration_logger; + use unc_o11y::WithSpanContextExt; + use unc_primitives::transaction::SignedTransaction; + use unc_store::test_utils::gen_account_from_alphabet; + use framework::config::GenesisExt; + use framework::{load_test_config, start_with_config, UNC_BASE}; + use rand::thread_rng; + use std::ops::ControlFlow; + use std::sync::{Arc, RwLock}; + use std::time::Duration; + + // Test the basic mocknet setup. + // This test first starts a localnet with one validator node that generates 2 epochs of blocks + // to generate a chain history. + // Then start a mock network with this chain history and test that the client in the mock network can catch up these 2 epochs. + // The localnet needs to have state snapshots enabled. It copies state from + // one instance to another by using the state sync mechanism, which relies + // on the flat storage snapshots. + #[test] + fn test_mock_node_basic() { + init_integration_logger(); + + // first set up a network with only one validator and generate some blocks + let mut genesis = + Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + let epoch_length = 50; + genesis.config.epoch_length = epoch_length; + let mut unc_config = + load_test_config("test0", tcp::ListenerAddr::reserve_for_test(), genesis.clone()); + unc_config.client_config.min_num_peers = 0; + unc_config.config.store.state_snapshot_enabled = true; + unc_config.config.tracked_shards = vec![0]; // Track all shards. + + let dir = tempfile::Builder::new().prefix("test0").tempdir().unwrap(); + let path1 = dir.path(); + run_actix(async move { + let framework::NearNode { view_client, client, .. } = + start_with_config(path1, unc_config).expect("start_with_config"); + + let view_client1 = view_client; + let nonce = Arc::new(RwLock::new(10)); + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let nonce = nonce.clone(); + let client1 = client.clone(); + let actor = view_client1.send(GetBlock::latest().with_span_context()); + let actor = actor.then(move |res| { + if let Ok(Ok(block)) = res { + let next_nonce = *nonce.read().unwrap(); + if next_nonce < 100 { + WaitOrTimeoutActor::new( + Box::new(move |_ctx| { + let signer0 = InMemorySigner::from_seed( + "test1".parse().unwrap(), + KeyType::ED25519, + "test1", + ); + let mut rng = thread_rng(); + let transaction = SignedTransaction::create_account( + next_nonce, + "test1".parse().unwrap(), + gen_account_from_alphabet(&mut rng, b"abcdefghijklmn"), + 5 * UNC_BASE, + signer0.public_key.clone(), + &signer0, + block.header.hash, + ); + spawn_interruptible( + client1 + .send( + ProcessTxRequest { + transaction, + is_forwarded: false, + check_only: false, + } + .with_span_context(), + ) + .then(move |_res| future::ready(())), + ); + }), + 100, + 30000, + ) + .start(); + *nonce.write().unwrap() = next_nonce + 1; + } + + // This is the flaky part. + // The node needs to stop as late into an epoch as + // possible without going over into the next epoch. + let expected_height = epoch_length * 3 - 5; + if block.header.height >= expected_height { + tracing::info!( + block_height = block.header.height, + expected_height, + "Time to stop" + ); + System::current().stop() + } + } + future::ready(()) + }); + spawn_interruptible(actor); + }), + // Keep this number low to ensure the node is stopped late in + // the epoch without going into the next epoch. + 100, + 60000, + ) + .start(); + }); + + // start the mock network to simulate a new node "test1" to sync up + // start the client at height 10 (end of the first epoch) + let dir1 = tempfile::Builder::new().prefix("test1").tempdir().unwrap(); + let mut unc_config1 = load_test_config("", tcp::ListenerAddr::reserve_for_test(), genesis); + unc_config1.client_config.min_num_peers = 1; + unc_config1.client_config.tracked_shards = vec![0]; // Track all shards. + unc_config1.config.store.state_snapshot_enabled = true; + let network_config = MockNetworkConfig::with_delay(Duration::from_millis(10)); + + let client_start_height = { + tracing::info!(target: "mock_node", store_path = ?dir.path(), "Opening the created store to get client_start_height"); + let store = unc_store::NodeStorage::opener( + dir.path(), + unc_config1.config.archive, + &unc_config1.config.store, + None, + ) + .open() + .unwrap() + .get_hot_store(); + let epoch_manager = + EpochManager::new_arc_handle(store.clone(), &unc_config1.genesis.config); + let chain_store = ChainStore::new( + store, + unc_config1.genesis.config.genesis_height, + unc_config1.client_config.save_trie_changes, + ); + let network_head_hash = chain_store.head().unwrap().last_block_hash; + let last_epoch_start_height = + epoch_manager.get_epoch_start_height(&network_head_hash).unwrap(); + tracing::info!(target: "mock_node", ?network_head_hash, last_epoch_start_height); + // Needs to be the last block of an epoch. + last_epoch_start_height - 1 + }; + tracing::info!(target: "mock_node", client_start_height); + + run_actix(async { + let MockNode { rpc_client, .. } = setup_mock_node( + dir1.path(), + dir.path(), + unc_config1, + &network_config, + client_start_height, + None, + None, + false, + tcp::ListenerAddr::reserve_for_test(), + ); + wait_or_timeout(100, 60000, || async { + if let Ok(status) = rpc_client.status().await { + if status.sync_info.latest_block_height >= client_start_height { + System::current().stop(); + return ControlFlow::Break(()); + } + } + ControlFlow::Continue(()) + }) + .await + .unwrap(); + }) + } +} diff --git a/tools/ping/Cargo.toml b/tools/ping/Cargo.toml new file mode 100644 index 000000000..aa1348a0e --- /dev/null +++ b/tools/ping/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "unc-ping" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix-web.workspace = true +anyhow.workspace = true +chrono.workspace = true +clap.workspace = true +once_cell.workspace = true +prometheus.workspace = true +tokio.workspace = true +tracing.workspace = true + +unc-async.workspace = true +unc-jsonrpc.workspace = true +unc-network.workspace = true +unc-o11y.workspace = true +unc-primitives.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-async/nightly", + "unc-jsonrpc/nightly", + "unc-network/nightly", + "unc-o11y/nightly", + "unc-primitives/nightly", +] +nightly_protocol = [ + "unc-async/nightly_protocol", + "unc-jsonrpc/nightly_protocol", + "unc-network/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-primitives/nightly_protocol", +] diff --git a/tools/ping/src/cli.rs b/tools/ping/src/cli.rs new file mode 100644 index 000000000..ef8a247c2 --- /dev/null +++ b/tools/ping/src/cli.rs @@ -0,0 +1,218 @@ +use anyhow::Context; +use unc_network::types::PeerInfo; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::PeerId; +use unc_primitives::types::AccountId; +use std::collections::HashSet; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use std::time::Duration; + +#[derive(clap::Parser)] +pub struct PingCommand { + #[clap(long)] + chain_id: String, + #[clap(long)] + /// genesis hash to use in the Handshake we send. This must be provided if --chain-id + /// is not "mainnet" or "testnet" + genesis_hash: Option, + #[clap(long)] + /// head height to use in the Handshake we send. This must be provided if --chain-id + /// is not "mainnet" or "testnet" + head_height: Option, + /// Protocol version to advertise in our handshake + #[clap(long)] + protocol_version: Option, + /// node public key and socket address in the format {pub key}@{socket addr}. e.g.: + /// ed25519:7PGseFbWxvYVgZ89K1uTJKYoKetWs7BJtbyXDzfbAcqX@127.0.0.1:24567 + #[clap(long)] + peer: String, + /// ttl to set on our Routed messages + #[clap(long, default_value = "100")] + ttl: u8, + /// milliseconds to wait between sending pings + #[clap(long, default_value = "1000")] + ping_frequency_millis: u64, + /// line-separated list of accounts to filter on. + /// We will only try to send pings to these accounts + // TODO: add --track-validators or something, that will constantly keep track of + // which accounts are validators and ping those + #[clap(long)] + account_filter_file: Option, + /// filename to append CSV data to + #[clap(long)] + latencies_csv_file: Option, + /// number of seconds to wait for incoming data before timing out + #[clap(long)] + recv_timeout_seconds: Option, + /// Listen address for prometheus metrics. + #[clap(long, default_value = "0.0.0.0:9000")] + prometheus_addr: String, +} + +fn display_stats(stats: &mut [(crate::PeerIdentifier, crate::PingStats)], peer_id: &PeerId) { + let mut acc_width = "account".len(); + for (peer, _) in stats.iter() { + acc_width = std::cmp::max(acc_width, format!("{}", peer).len()); + } + // the ones that never responded should end up at the top with this sorting, which is a + // little weird, but we can fix it later. + stats.sort_by(|(_, left), (_, right)| left.average_latency.cmp(&right.average_latency)); + println!( + "{:>(filename: P) -> std::io::Result> { + let f = File::open(filename.as_ref())?; + let mut reader = BufReader::new(f); + let mut line = String::new(); + let mut line_num = 1; + let mut filter = HashSet::new(); + loop { + if reader.read_line(&mut line)? == 0 { + break; + } + let acc = line.trim().trim_matches('"'); + if acc.is_empty() { + continue; + } + match AccountId::from_str(acc) { + Ok(a) => { + filter.insert(a); + } + Err(e) => { + tracing::warn!(target: "ping", "Could not parse account {} on line {}: {:?}", &line, line_num, e); + } + } + line.clear(); + line_num += 1; + } + if filter.is_empty() { + tracing::warn!(target: "ping", "No accounts parsed from {:?}. Only sending direct pings.", filename.as_ref()); + } + Ok(filter) +} + +impl PingCommand { + pub fn run(&self) -> anyhow::Result<()> { + tracing::warn!(target: "ping", "the ping command is not stable, and may be removed or changed arbitrarily at any time"); + + let mut chain_info = None; + for info in CHAIN_INFO.iter() { + if &info.chain_id == &self.chain_id { + chain_info = Some(info); + break; + } + } + + let genesis_hash = if let Some(h) = &self.genesis_hash { + match CryptoHash::from_str(&h) { + Ok(h) => h, + Err(e) => { + anyhow::bail!("Could not parse --genesis-hash {}: {:?}", &h, e) + } + } + } else { + match chain_info { + Some(chain_info) => chain_info.genesis_hash, + None => anyhow::bail!( + "--genesis-hash not given, and genesis hash for --chain-id {} not known", + &self.chain_id + ), + } + }; + + let peer = match PeerInfo::from_str(&self.peer) { + Ok(p) => p, + Err(e) => anyhow::bail!("Could not parse --peer {}: {:?}", &self.peer, e), + }; + if peer.addr.is_none() { + anyhow::bail!("--peer should be in the form [public key]@[socket addr]"); + } + let filter = if let Some(filename) = &self.account_filter_file { + Some(parse_account_filter(filename)?) + } else { + None + }; + let csv = + if let Some(filename) = &self.latencies_csv_file { + Some(crate::csv::LatenciesCsv::open(filename).with_context(|| { + format!("Couldn't open latencies CSV file at {:?}", filename) + })?) + } else { + None + }; + let runtime = tokio::runtime::Runtime::new().unwrap(); + runtime.block_on(async move { + let mut stats = Vec::new(); + crate::ping_via_node( + &self.chain_id, + genesis_hash, + self.head_height.unwrap_or(0), + self.protocol_version, + peer.id.clone(), + peer.addr.unwrap(), + self.ttl, + self.ping_frequency_millis, + self.recv_timeout_seconds.unwrap_or(5), + filter, + csv, + &mut stats, + &self.prometheus_addr, + ) + .await?; + display_stats(&mut stats, &peer.id); + Ok(()) + }) + } +} diff --git a/tools/ping/src/csv.rs b/tools/ping/src/csv.rs new file mode 100644 index 000000000..f075eccad --- /dev/null +++ b/tools/ping/src/csv.rs @@ -0,0 +1,99 @@ +use unc_async::time; +use unc_primitives::network::PeerId; +use unc_primitives::types::AccountId; +use std::fs::{File, OpenOptions}; +use std::io; +use std::io::BufWriter; +use std::io::{Read, Seek, SeekFrom, Write}; +use std::path::Path; + +pub struct LatenciesCsv { + out: BufWriter, +} + +const HEADER: &'static str = "timestamp,ID,latency\n"; + +impl LatenciesCsv { + pub fn open>(filename: P) -> io::Result { + let mut f = OpenOptions::new().read(true).append(true).create(true).open(filename)?; + + let end = f.seek(SeekFrom::End(0))?; + let start = f.seek(SeekFrom::Start(0))?; + + if end >= start + HEADER.len() as u64 { + let mut buf = [0; HEADER.len()]; + f.read_exact(&mut buf)?; + + match std::str::from_utf8(&buf) { + Ok(s) => { + if s != HEADER { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Does not appear to be a latencies CSV file", + )); + } + } + Err(_) => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Does not appear to be a latencies CSV file. Contains non-UTF-8 data", + )) + } + } + } else if end != start { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Does not appear to be a latencies CSV file", + )); + } + + if end > start { + f.seek(SeekFrom::End(-1))?; + let mut buf = [0; 1]; + f.read_exact(&mut buf)?; + let write_newline = match std::str::from_utf8(&buf) { + Ok(s) => s != "\n", + Err(_) => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Does not appear to be a latencies CSV file. Contains non-UTF-8 data", + )) + } + }; + if write_newline { + f.write_all("\n".as_bytes())?; + } + } else { + f.write_all(HEADER.as_bytes())?; + } + + Ok(Self { out: BufWriter::new(f) }) + } + + pub fn write( + &mut self, + peer_id: &PeerId, + account_id: Option<&AccountId>, + latency: time::Duration, + ) -> io::Result<()> { + let id = account_id.map_or_else(|| format!("{}", peer_id), |a| format!("{}", a)); + write!( + self.out, + "{:?},{},{}\n", + chrono::offset::Utc::now(), + id, + latency.whole_microseconds() + )?; + Ok(()) + } + + pub fn write_timeout( + &mut self, + peer_id: &PeerId, + account_id: Option<&AccountId>, + ) -> io::Result<()> { + let id = account_id.map_or_else(|| format!("{}", peer_id), |a| format!("{}", a)); + write!(self.out, "{:?},{},TIMEOUT\n", chrono::offset::Utc::now(), id)?; + Ok(()) + } +} diff --git a/tools/ping/src/lib.rs b/tools/ping/src/lib.rs new file mode 100644 index 000000000..98a768c14 --- /dev/null +++ b/tools/ping/src/lib.rs @@ -0,0 +1,501 @@ +use actix_web::{web, App, HttpServer}; +use anyhow::Context; +pub use cli::PingCommand; +use unc_async::time; +use unc_network::raw::{ConnectError, Connection, DirectMessage, Message, RoutedMessage}; +use unc_network::types::HandshakeFailureReason; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::{AnnounceAccount, PeerId}; +use unc_primitives::types::{AccountId, BlockHeight}; +use unc_primitives::version::ProtocolVersion; +use std::cmp; +use std::collections::hash_map::Entry; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::net::SocketAddr; +use std::pin::Pin; + +pub mod cli; +mod csv; +mod metrics; + +// TODO: also log number of bytes/other messages (like Blocks) received? +#[derive(Debug, Default)] +struct PingStats { + pings_sent: usize, + pongs_received: usize, + // TODO: these latency stats could be separated into time to first byte + // + time to last byte, etc. + min_latency: time::Duration, + max_latency: time::Duration, + average_latency: time::Duration, +} + +impl PingStats { + fn pong_received(&mut self, latency: time::Duration) { + self.pongs_received += 1; + + if self.min_latency == time::Duration::ZERO || self.min_latency > latency { + self.min_latency = latency; + } + if self.max_latency < latency { + self.max_latency = latency; + } + let n = self.pongs_received as u32; + self.average_latency = ((n - 1) * self.average_latency + latency) / n; + } +} + +type Nonce = u64; + +#[derive(Debug, Eq, PartialEq)] +struct PingTarget { + peer_id: PeerId, + last_pinged: Option, +} + +impl PartialOrd for PingTarget { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for PingTarget { + fn cmp(&self, other: &Self) -> cmp::Ordering { + match &self.last_pinged { + Some(my_last_pinged) => match &other.last_pinged { + Some(their_last_pinged) => my_last_pinged + .cmp(their_last_pinged) + .then_with(|| self.peer_id.cmp(&other.peer_id)), + None => cmp::Ordering::Greater, + }, + None => match &other.last_pinged { + Some(_) => cmp::Ordering::Less, + None => self.peer_id.cmp(&other.peer_id), + }, + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +struct PingTimeout { + peer_id: PeerId, + nonce: u64, + timeout: time::Instant, +} + +impl PartialOrd for PingTimeout { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for PingTimeout { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.timeout.cmp(&other.timeout).then_with(|| { + self.nonce.cmp(&other.nonce).then_with(|| self.peer_id.cmp(&other.peer_id)) + }) + } +} + +fn peer_str(peer_id: &PeerId, account_id: Option<&AccountId>) -> String { + account_id.map_or_else(|| format!("{}", peer_id), |a| format!("{}", a)) +} + +const MAX_PINGS_IN_FLIGHT: usize = 10; +const PING_TIMEOUT: time::Duration = time::Duration::seconds(100); + +#[derive(Debug)] +struct PingState { + stats: PingStats, + last_pinged: Option, + account_id: Option, +} + +struct PingTimes { + sent_at: time::Instant, + timeout: time::Instant, +} + +struct AppInfo { + stats: HashMap, + // we will ping targets in round robin fashion. So this keeps a set of + // targets ordered by when we last pinged them. + requests: BTreeMap>, + timeouts: BTreeSet, + account_filter: Option>, + chain_id: String, +} + +impl AppInfo { + fn new(account_filter: Option>, chain_id: &str) -> Self { + Self { + stats: HashMap::new(), + requests: BTreeMap::new(), + timeouts: BTreeSet::new(), + account_filter, + chain_id: chain_id.to_owned(), + } + } + + fn pick_next_target(&self) -> Option { + for (target, pending_pings) in self.requests.iter() { + if pending_pings.len() < MAX_PINGS_IN_FLIGHT { + return Some(target.peer_id.clone()); + } + } + None + } + + fn ping_sent(&mut self, peer_id: &PeerId, nonce: u64, chain_id: &str) { + let timestamp = time::Instant::now(); + let timeout = timestamp + PING_TIMEOUT; + + let account_id = self.peer_id_to_account_id(&peer_id); + crate::metrics::PING_SENT + .with_label_values(&[&chain_id, &peer_str(peer_id, account_id)]) + .inc(); + + match self.stats.entry(peer_id.clone()) { + Entry::Occupied(mut e) => { + let state = e.get_mut(); + let mut pending_pings = self + .requests + .remove(&PingTarget { + peer_id: peer_id.clone(), + last_pinged: state.last_pinged, + }) + .unwrap(); + + println!( + "send ping --------------> {}", + peer_str(&peer_id, state.account_id.as_ref()) + ); + + state.stats.pings_sent += 1; + state.last_pinged = Some(timestamp); + + match pending_pings.entry(nonce) { + Entry::Occupied(_) => { + tracing::warn!( + target: "ping", "Internal error! Sent two pings with nonce {} to {}. \ + Latency stats will probably be wrong.", nonce, &peer_id + ); + } + Entry::Vacant(e) => { + e.insert(PingTimes { sent_at: timestamp, timeout }); + } + }; + self.requests.insert( + PingTarget { peer_id: peer_id.clone(), last_pinged: Some(timestamp) }, + pending_pings, + ); + self.timeouts.insert(PingTimeout { peer_id: peer_id.clone(), nonce, timeout }) + } + Entry::Vacant(_) => { + panic!("sent ping to {:?}, but not present in stats HashMap", peer_id) + } + }; + } + + fn pong_received( + &mut self, + peer_id: &PeerId, + nonce: u64, + received_at: time::Instant, + ) -> Option<(time::Duration, Option<&AccountId>)> { + match self.stats.get_mut(peer_id) { + Some(state) => { + let pending_pings = self + .requests + .get_mut(&PingTarget { + peer_id: peer_id.clone(), + last_pinged: state.last_pinged, + }) + .unwrap(); + + match pending_pings.remove(&nonce) { + Some(times) => { + let latency = received_at - times.sent_at; + state.stats.pong_received(latency); + assert!(self.timeouts.remove(&PingTimeout { + peer_id: peer_id.clone(), + nonce, + timeout: times.timeout, + })); + + let l: std::time::Duration = latency.try_into().unwrap(); + println!( + "recv pong <-------------- {} latency: {:?}", + peer_str(&peer_id, state.account_id.as_ref()), + l + ); + Some((latency, state.account_id.as_ref())) + } + None => { + tracing::warn!( + target: "ping", + "received pong with nonce {} from {}, after we probably treated it as timed out previously", + nonce, peer_str(&peer_id, state.account_id.as_ref()) + ); + None + } + } + } + None => { + tracing::warn!(target: "ping", "received pong from {:?}, but don't know of this peer", peer_id); + None + } + } + } + + fn pop_timeout(&mut self, t: &PingTimeout) { + assert!(self.timeouts.remove(&t)); + let state = self.stats.get(&t.peer_id).unwrap(); + + let pending_pings = self + .requests + .get_mut(&PingTarget { peer_id: t.peer_id.clone(), last_pinged: state.last_pinged }) + .unwrap(); + assert!(pending_pings.remove(&t.nonce).is_some()); + println!( + "{} timeout after {} ---------", + peer_str(&t.peer_id, state.account_id.as_ref()), + PING_TIMEOUT + ); + } + + fn add_peer(&mut self, peer_id: PeerId, account_id: Option) { + if let Some(filter) = self.account_filter.as_ref() { + if let Some(account_id) = account_id.as_ref() { + if !filter.contains(account_id) { + tracing::debug!(target: "ping", "skipping AnnounceAccount for {}", account_id); + return; + } + } + } + match self.stats.entry(peer_id.clone()) { + Entry::Occupied(mut e) => { + if let Some(account_id) = account_id { + let state = e.get_mut(); + if let Some(old) = state.account_id.as_ref() { + if old != &account_id { + // TODO: we should just keep track of all accounts that map + // to this peer id, since it's valid for there to be more than one. + // We only use the accounts in the account filter and when displaying + // ping targets, so theres no reason we cant keep track of all of them + tracing::warn!( + target: "ping", "Received Announce Account mapping {:?} to {:?}, but already \ + knew of account id {:?}. Keeping old value", + peer_id, account_id, old + ); + } + } else { + state.account_id = Some(account_id); + } + } + } + Entry::Vacant(e) => { + e.insert(PingState { account_id, last_pinged: None, stats: PingStats::default() }); + self.requests.insert(PingTarget { peer_id, last_pinged: None }, HashMap::new()); + } + } + } + + fn add_announce_accounts(&mut self, accounts: Vec) { + for AnnounceAccount { account_id, peer_id, .. } in accounts { + self.add_peer(peer_id, Some(account_id)); + } + } + + fn peer_id_to_account_id(&self, peer_id: &PeerId) -> Option<&AccountId> { + self.stats.get(peer_id).and_then(|s| s.account_id.as_ref()) + } +} + +fn handle_message( + app_info: &mut AppInfo, + msg: Message, + received_at: time::Instant, + latencies_csv: Option<&mut crate::csv::LatenciesCsv>, +) -> anyhow::Result<()> { + match msg { + Message::Routed(RoutedMessage::Pong { nonce, source }) => { + let chain_id = app_info.chain_id.clone(); // Avoid an immutable borrow during a mutable borrow. + if let Some((latency, account_id)) = app_info.pong_received(&source, nonce, received_at) + { + crate::metrics::PONG_RECEIVED + .with_label_values(&[&chain_id, &peer_str(&source, account_id)]) + .observe(latency.as_seconds_f64()); + if let Some(csv) = latencies_csv { + csv.write(&source, account_id, latency) + .context("Failed writing to CSV file")?; + } + } + } + Message::Direct(DirectMessage::AnnounceAccounts(a)) => { + app_info.add_announce_accounts(a); + } + _ => {} + }; + Ok(()) +} + +#[derive(Debug)] +struct PeerIdentifier { + account_id: Option, + peer_id: PeerId, +} + +impl std::fmt::Display for PeerIdentifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match &self.account_id { + Some(a) => a.fmt(f), + None => self.peer_id.fmt(f), + } + } +} + +fn collect_stats(app_info: AppInfo, ping_stats: &mut Vec<(PeerIdentifier, PingStats)>) { + for (peer_id, state) in app_info.stats { + let PingState { stats, account_id, .. } = state; + ping_stats.push((PeerIdentifier { peer_id, account_id }, stats)); + } +} + +fn prepare_timeout(sleep: Pin<&mut tokio::time::Sleep>, app_info: &AppInfo) -> Option { + if let Some(t) = app_info.timeouts.iter().next() { + sleep.reset(tokio::time::Instant::from_std(t.timeout.try_into().unwrap())); + Some(t.clone()) + } else { + None + } +} + +async fn ping_via_node( + chain_id: &str, + genesis_hash: CryptoHash, + head_height: BlockHeight, + protocol_version: Option, + peer_id: PeerId, + peer_addr: SocketAddr, + ttl: u8, + ping_frequency_millis: u64, + recv_timeout_seconds: u32, + account_filter: Option>, + mut latencies_csv: Option, + ping_stats: &mut Vec<(PeerIdentifier, PingStats)>, + prometheus_addr: &str, +) -> anyhow::Result<()> { + let mut app_info = AppInfo::new(account_filter, chain_id); + + app_info.add_peer(peer_id.clone(), None); + + let mut peer = match Connection::connect( + peer_addr, + peer_id, + protocol_version, + chain_id, + genesis_hash, + head_height, + vec![0], + time::Duration::seconds(recv_timeout_seconds.into())).await { + Ok(p) => p, + Err(ConnectError::HandshakeFailure(reason)) => { + match reason { + HandshakeFailureReason::ProtocolVersionMismatch { version, oldest_supported_version } => anyhow::bail!( + "Received Handshake Failure: {:?}. Try running again with --protocol-version between {} and {}", + reason, oldest_supported_version, version + ), + HandshakeFailureReason::GenesisMismatch(_) => anyhow::bail!( + "Received Handshake Failure: {:?}. Try running again with --chain-id and --genesis-hash set to these values.", + reason, + ), + HandshakeFailureReason::InvalidTarget => anyhow::bail!( + "Received Handshake Failure: {:?}. Is the public key given with --peer correct?", + reason, + ), + } + } + Err(e) => { + anyhow::bail!("Error connecting to {:?}: {}", peer_addr, e); + } + }; + + let mut result = Ok(()); + let mut nonce = 1; + let next_ping = tokio::time::sleep(std::time::Duration::ZERO); + tokio::pin!(next_ping); + let next_timeout = tokio::time::sleep(std::time::Duration::ZERO); + tokio::pin!(next_timeout); + + let server = HttpServer::new(move || { + App::new().service( + web::resource("/metrics").route(web::get().to(unc_jsonrpc::prometheus_handler)), + ) + }) + .bind(prometheus_addr) + .unwrap() + .workers(1) + .shutdown_timeout(3) + .disable_signals() + .run(); + tokio::spawn(server); + + loop { + let target = app_info.pick_next_target(); + let pending_timeout = prepare_timeout(next_timeout.as_mut(), &app_info); + + tokio::select! { + _ = &mut next_ping, if target.is_some() => { + let target = target.unwrap(); + result = peer.send_routed_message(RoutedMessage::Ping{nonce}, target.clone(), ttl) + .await.with_context(|| format!("Failed sending ping to {:?}", &target)); + if result.is_err() { + break; + } + app_info.ping_sent(&target, nonce, &chain_id); + nonce += 1; + next_ping.as_mut().reset(tokio::time::Instant::now() + std::time::Duration::from_millis(ping_frequency_millis)); + } + res = peer.recv() => { + let (msg, first_byte_time) = match res { + Ok(x) => x, + Err(e) => { + result = Err(e).context("Failed receiving messages"); + break; + } + }; + result = handle_message( + &mut app_info, + msg, + first_byte_time.try_into().unwrap(), + latencies_csv.as_mut() + ); + if result.is_err() { + break; + } + } + _ = &mut next_timeout, if pending_timeout.is_some() => { + let t = pending_timeout.unwrap(); + app_info.pop_timeout(&t); + let account_id = app_info.peer_id_to_account_id(&t.peer_id); + crate::metrics::PONG_TIMEOUTS.with_label_values(&[&chain_id, &peer_str(&t.peer_id, account_id)]).inc(); + if let Some(csv) = latencies_csv.as_mut() { + result = csv.write_timeout( + &t.peer_id, + account_id, + ) + .context("Failed writing to CSV file"); + if result.is_err() { + break; + } + } + } + _ = tokio::signal::ctrl_c() => { + break; + } + } + } + collect_stats(app_info, ping_stats); + result +} diff --git a/tools/ping/src/metrics.rs b/tools/ping/src/metrics.rs new file mode 100644 index 000000000..c6260b1a5 --- /dev/null +++ b/tools/ping/src/metrics.rs @@ -0,0 +1,33 @@ +use unc_o11y::metrics::{ + exponential_buckets, try_create_histogram_vec, try_create_int_counter_vec, HistogramVec, + IntCounterVec, +}; +use once_cell::sync::Lazy; + +pub(crate) static PONG_RECEIVED: Lazy = Lazy::new(|| { + try_create_histogram_vec( + "ping_pong_received", + "Round-trip time of ping-pong", + &["chain_id", "account_id"], + Some(exponential_buckets(0.00001, 1.6, 40).unwrap()), + ) + .unwrap() +}); + +pub(crate) static PONG_TIMEOUTS: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "ping_pong_timeout", + "Number of pongs that were not received", + &["chain_id", "account_id"], + ) + .unwrap() +}); + +pub(crate) static PING_SENT: Lazy = Lazy::new(|| { + try_create_int_counter_vec( + "ping_ping_sent", + "Number of pings sent", + &["chain_id", "account_id"], + ) + .unwrap() +}); diff --git a/tools/restaked/Cargo.toml b/tools/restaked/Cargo.toml new file mode 100644 index 000000000..c64648b17 --- /dev/null +++ b/tools/restaked/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "restaked" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +clap.workspace = true +tokio.workspace = true + +unc-crypto.workspace = true +unc-primitives.workspace = true +unc-jsonrpc-client.workspace = true +framework.workspace = true +unc-o11y.workspace = true + +integration-tests.workspace = true + +[features] +test_features = ["integration-tests/test_features"] diff --git a/tools/restaked/src/main.rs b/tools/restaked/src/main.rs new file mode 100644 index 000000000..51500faaa --- /dev/null +++ b/tools/restaked/src/main.rs @@ -0,0 +1,131 @@ +use clap::{Arg, Command}; +use unc_crypto::{InMemorySigner, KeyFile}; +use unc_o11y::tracing::{error, info}; +use unc_primitives::views::CurrentEpochValidatorInfo; +use framework::config::{Config, BLOCK_PRODUCER_KICKOUT_THRESHOLD, CONFIG_FILENAME}; +use framework::get_default_home; +use std::path::PathBuf; +use std::sync::Arc; +use std::time::Duration; +// TODO(1905): Move out RPC interface for transacting into separate production crate. +use integration_tests::user::{rpc_user::RpcUser, User}; + +const DEFAULT_WAIT_PERIOD_SEC: &str = "60"; +const DEFAULT_RPC_URL: &str = "http://localhost:3030"; + +/// Returns true if given validator might get kicked out. +fn maybe_kicked_out(validator_info: &CurrentEpochValidatorInfo) -> bool { + validator_info.num_produced_blocks * 100 + < validator_info.num_expected_blocks * u64::from(BLOCK_PRODUCER_KICKOUT_THRESHOLD) +} + +fn main() { + let env_filter = unc_o11y::EnvFilterBuilder::from_env().verbose(Some("")).finish().unwrap(); + let _subscriber = unc_o11y::default_subscriber(env_filter, &Default::default()).global(); + + let matches = Command::new("Key-pairs generator") + .about( + "Continuously checking the node and executes staking transaction if node is kicked out", + ) + .arg( + Arg::new("home") + .long("home") + .default_value(get_default_home().into_os_string()) + .value_parser(clap::value_parser!(PathBuf)) + .help("Directory for config and data (default \"~/.near\")") + .action(clap::ArgAction::Set), + ) + .arg( + Arg::new("wait-period") + .long("wait-period") + .default_value(DEFAULT_WAIT_PERIOD_SEC) + .help("Waiting period between checking if node is kicked out (in seconds)") + .action(clap::ArgAction::Set), + ) + .arg( + Arg::new("rpc-url") + .long("rpc-url") + .default_value(DEFAULT_RPC_URL) + .help("Url of RPC for the node to monitor") + .action(clap::ArgAction::Set), + ) + .arg( + Arg::new("stake-amount") + .long("stake-amount") + .default_value("0") + .help("Stake amount in NEAR, if 0 is used it restakes last seen staked amount") + .action(clap::ArgAction::Set), + ) + .get_matches(); + + let home_dir = matches.get_one::("home").unwrap(); + let wait_period = matches + .get_one::("wait-period") + .map(|s| s.parse().expect("Wait period must be a number")) + .unwrap(); + let rpc_url = matches.get_one::("rpc-url").unwrap(); + let stake_amount = matches + .get_one::("stake-amount") + .map(|s| s.parse().expect("Stake amount must be a number")) + .unwrap(); + + let config = Config::from_file(&home_dir.join(CONFIG_FILENAME)).expect("can't load config"); + + let key_path = home_dir.join(&config.validator_key_file); + let key_file = KeyFile::from_file(&key_path) + .unwrap_or_else(|e| panic!("Failed to open key file at {:?}: {:#}", &key_path, e)); + // Support configuring if there is another key. + let signer = InMemorySigner::from_file(&key_path).unwrap_or_else(|e| { + panic!("Failed to initialize signer from key file at {:?}: {:#}", key_path, e) + }); + let account_id = signer.account_id.clone(); + let mut last_stake_amount = stake_amount; + + assert_eq!( + signer.account_id, key_file.account_id, + "Only can stake for the same account as given signer key" + ); + + let user = RpcUser::new(rpc_url, account_id.clone(), Arc::new(signer)); + loop { + let validators = user.validators(None).unwrap(); + // Check: + // - don't already have a proposal + // - too many missing blocks in current validators + // - missing in next validators + if validators.current_frozen_proposals.iter().any(|proposal| proposal.account_id() == &account_id) + { + continue; + } + let mut restake = false; + validators + .current_validators + .iter() + .filter(|validator_info| validator_info.account_id == account_id) + .last() + .map(|validator_info| { + last_stake_amount = validator_info.frozen; + if maybe_kicked_out(validator_info) { + restake = true; + } + }); + restake |= !validators + .next_validators + .iter() + .any(|validator_info| validator_info.account_id == account_id); + if restake { + // Already kicked out or getting kicked out. + let amount = if stake_amount == 0 { last_stake_amount } else { stake_amount }; + info!( + target: "restaked", + "Sending staking transaction {} -> {}", key_file.account_id, amount + ); + if let Err(err) = + user.stake(key_file.account_id.clone(), key_file.public_key.clone(), amount) + { + error!(target: "restaked", "Failed to send staking transaction: {}", err); + } + } + std::thread::sleep(Duration::from_secs(wait_period)); + } +} diff --git a/tools/rpctypegen/core/Cargo.toml b/tools/rpctypegen/core/Cargo.toml new file mode 100644 index 000000000..e521ac31a --- /dev/null +++ b/tools/rpctypegen/core/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "unc-rpc-error-core" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "This crate generates schema for Rust structs which can be used by TypeScript." +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +quote.workspace = true +serde.workspace = true +syn.workspace = true + +[dev-dependencies] +serde_json = { workspace = true, features = ["preserve_order"] } + +[features] +test = [] +dump_errors_schema = [] diff --git a/tools/rpctypegen/core/src/lib.rs b/tools/rpctypegen/core/src/lib.rs new file mode 100644 index 000000000..f1b879814 --- /dev/null +++ b/tools/rpctypegen/core/src/lib.rs @@ -0,0 +1,295 @@ +use std::collections::BTreeMap; +use syn::{Data, DataEnum, DataStruct, DeriveInput, Fields, FieldsNamed, FieldsUnnamed}; + +#[derive(Default, Debug, serde::Deserialize, serde::Serialize)] +pub struct ErrorType { + /// A type name of the error + pub name: String, + /// Names of subtypes of the error + pub subtypes: Vec, + /// An error input name and a type + pub props: BTreeMap, +} + +fn parse_rpc_error_variant(input: &DeriveInput) -> String { + let type_name = input.ident.to_string(); + let type_kind: Vec<&str> = type_name.split("Kind").collect(); + type_kind[0].to_string() +} + +fn error_type_name(schema: &mut BTreeMap, name: String) -> &mut ErrorType { + let error_type = ErrorType { name: name.clone(), ..Default::default() }; + schema.entry(name).or_insert(error_type) +} + +pub fn parse_error_type(schema: &mut BTreeMap, input: &DeriveInput) { + let name = parse_rpc_error_variant(input); + match &input.data { + Data::Enum(DataEnum { ref variants, .. }) => { + // TODO: check for uniqueness + let error_type = error_type_name(schema, name); + let mut direct_error_types = vec![]; + for variant in variants { + error_type.subtypes.push(variant.ident.to_string()); + match &variant.fields { + Fields::Unnamed(FieldsUnnamed { ref unnamed, .. }) => { + // Subtype + if unnamed.iter().count() > 1 { + panic!( + "Error types doesn't support tuple variants with multiple fields" + ); + } + } + Fields::Named(FieldsNamed { ref named, .. }) => { + // If variant is Enum with a named fields - create a new type for each variant with named props + let mut error_type = + ErrorType { name: variant.ident.to_string(), ..Default::default() }; + for field in named { + error_type.props.insert( + field + .ident + .as_ref() + .expect("named fields must have ident") + .to_string(), + "".to_owned(), + ); + } + direct_error_types.push(error_type); + } + Fields::Unit => { + direct_error_types.push(ErrorType { + name: variant.ident.to_string(), + ..Default::default() + }); + } + } + } + for e in direct_error_types { + let error_type = error_type_name(schema, e.name.clone()); + error_type.name = e.name; + error_type.props = e.props; + } + } + Data::Struct(DataStruct { ref fields, .. }) => { + let error_type = error_type_name(schema, name); + match fields { + Fields::Named(FieldsNamed { ref named, .. }) => { + for field in named { + let field_name = + field.ident.as_ref().expect("named fields must have ident").to_string(); + if field_name == "kind" { + continue; + } + error_type.props.insert(field_name, "".to_owned()); // TODO: add prop type + } + } + _ => { + panic!("RpcError supports structs with the named fields only"); + } + } + } + Data::Union(_) => { + panic!("Unions are not supported"); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use quote::quote; + #[test] + fn should_merge_kind() { + let mut schema = BTreeMap::default(); + let error_type = syn::parse2(quote! { + pub struct ActionError { + pub index: Option, + pub kind: ActionErrorKind, + } + }) + .unwrap(); + parse_error_type(&mut schema, &error_type); + let expected: BTreeMap = serde_json::from_str( + r#" + { + "ActionError": { + "name": "ActionError", + "subtypes": [], + "props": { + "index": "" + } + } + } + "#, + ) + .unwrap(); + assert_eq!( + serde_json::to_string(&expected).unwrap(), + serde_json::to_string(&schema).unwrap() + ); + let error_type_kind: DeriveInput = syn::parse2(quote! { + pub enum ActionErrorKind { + AccountAlreadyExists { account_id: String }, + } + }) + .unwrap(); + let expected: BTreeMap = serde_json::from_str( + r#" + { + "ActionError": { + "name": "ActionError", + "subtypes": ["AccountAlreadyExists"], + "props": { + "index": "" + } + }, + "AccountAlreadyExists": { + "name": "AccountAlreadyExists", + "subtypes": [], + "props": { + "account_id": "" + } + } + } + "#, + ) + .unwrap(); + parse_error_type(&mut schema, &error_type_kind); + assert_eq!( + serde_json::to_string(&expected).unwrap(), + serde_json::to_string(&schema).unwrap() + ); + } + + #[test] + fn complex() { + let mut schema = BTreeMap::default(); + parse_error_type( + &mut schema, + &syn::parse2(quote! { + pub enum TxExecutionError { + ActionError(ActionError), + InvalidTxError(InvalidTxError), + } + }) + .unwrap(), + ); + parse_error_type( + &mut schema, + &syn::parse2(quote! { + pub enum InvalidTxError { + InvalidAccessKeyError(InvalidAccessKeyError), + InvalidSignerId { signer_id: AccountId }, + } + }) + .unwrap(), + ); + parse_error_type( + &mut schema, + &syn::parse2(quote! { + pub enum InvalidAccessKeyError { + /// The access key identified by the `public_key` doesn't exist for the account + AccessKeyNotFound { account_id: AccountId, public_key: PublicKey }, + } + }) + .unwrap(), + ); + parse_error_type( + &mut schema, + &syn::parse2(quote! { + pub struct ActionError { + pub index: Option, + pub kind: ActionErrorKind, + } + }) + .unwrap(), + ); + parse_error_type( + &mut schema, + &syn::parse2(quote! { + pub enum ActionErrorKind { + AccountAlreadyExists { account_id: String }, + } + }) + .unwrap(), + ); + let expected: BTreeMap = serde_json::from_str( + r#" + { + "AccessKeyNotFound": { + "name": "AccessKeyNotFound", + "subtypes": [], + "props": { + "account_id": "", + "public_key": "" + } + }, + "AccountAlreadyExists": { + "name": "AccountAlreadyExists", + "subtypes": [], + "props": { + "account_id": "" + } + }, + "ActionError": { + "name": "ActionError", + "subtypes": [ + "AccountAlreadyExists" + ], + "props": { + "index": "" + } + }, + "InvalidAccessKeyError": { + "name": "InvalidAccessKeyError", + "subtypes": [ + "AccessKeyNotFound" + ], + "props": {} + }, + "InvalidSignerId": { + "name": "InvalidSignerId", + "subtypes": [], + "props": { + "signer_id": "" + } + }, + "InvalidTxError": { + "name": "InvalidTxError", + "subtypes": [ + "InvalidAccessKeyError", + "InvalidSignerId" + ], + "props": {} + }, + "TxExecutionError": { + "name": "TxExecutionError", + "subtypes": [ + "ActionError", + "InvalidTxError" + ], + "props": {} + } + }"#, + ) + .unwrap(); + assert_eq!( + serde_json::to_string(&expected).unwrap(), + serde_json::to_string(&schema).unwrap() + ); + } + #[test] + #[should_panic] + fn should_not_accept_tuples() { + let mut schema = BTreeMap::default(); + parse_error_type( + &mut schema, + &syn::parse2(quote! { + pub enum ErrorWithATupleVariant { + Var(One, Two) + } + }) + .unwrap(), + ); + } +} diff --git a/tools/rpctypegen/macro/Cargo.toml b/tools/rpctypegen/macro/Cargo.toml new file mode 100644 index 000000000..7b537a271 --- /dev/null +++ b/tools/rpctypegen/macro/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "unc-rpc-error-macro" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "This crate generates schema for Rust structs which can be used by TypeScript." +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[lib] +proc-macro = true + +[dependencies] +serde.workspace = true +serde_json = { workspace = true, optional = true, features = ["preserve_order"] } +syn.workspace = true +fs2.workspace = true + +unc-rpc-error-core.workspace = true + +[features] +test = [] +dump_errors_schema = ["unc-rpc-error-core/dump_errors_schema", "serde_json"] diff --git a/tools/rpctypegen/macro/src/lib.rs b/tools/rpctypegen/macro/src/lib.rs new file mode 100644 index 000000000..62e3094ce --- /dev/null +++ b/tools/rpctypegen/macro/src/lib.rs @@ -0,0 +1,124 @@ +use std::cell::RefCell; +use std::collections::BTreeMap; + +use proc_macro::TokenStream; +use serde::{Deserialize, Serialize}; +#[cfg(feature = "dump_errors_schema")] +use serde_json::Value; +use syn::{parse_macro_input, DeriveInput}; + +use unc_rpc_error_core::{parse_error_type, ErrorType}; + +thread_local!(static SCHEMA: RefCell = RefCell::new(Schema::default())); + +#[derive(Default, Debug, Deserialize, Serialize)] +struct Schema { + pub schema: BTreeMap, +} + +#[cfg(feature = "dump_errors_schema")] +fn merge(a: &mut Value, b: &Value) { + match (a, b) { + (&mut Value::Object(ref mut a), &Value::Object(ref b)) => { + for (k, v) in b { + merge(a.entry(k.clone()).or_insert(Value::Null), v); + } + } + (a, b) => { + *a = b.clone(); + } + } +} + +#[cfg(feature = "dump_errors_schema")] +impl Drop for Schema { + /// `rpc_error` wants to collect **all** invocations of the macro across the + /// project and merge them into a single file. These kinds of macros are not + /// supported at all by Rust macro infrastructure, so we use gross hacks + /// here. + /// + /// Every macro invocation merges its results into the + /// rpc_errors_schema.json file, with the file playing the role of global + /// mutable state which can be accessed from different processes. To avoid + /// race conditions, we use file-locking. File locking isn't a very robust + /// thing, but it should ok be considering the level of hack here. + fn drop(&mut self) { + use fs2::FileExt; + use std::fs::File; + use std::io::{Read, Seek, SeekFrom, Write}; + + struct Guard { + file: File, + } + impl Guard { + fn new(path: &str) -> Self { + let file = File::options() + .read(true) + .write(true) + .create_new(true) + .open(path) + .or_else(|_| File::options().read(true).write(true).open(path)) + .unwrap_or_else(|err| panic!("can't open {path}: {err}")); + file.lock_exclusive().unwrap_or_else(|err| panic!("can't lock {path}: {err}")); + Guard { file } + } + } + impl Drop for Guard { + fn drop(&mut self) { + let _ = self.file.unlock(); + } + } + + let schema_json = serde_json::to_value(self).expect("Schema serialize failed"); + + // std::env::var("CARGO_TARGET_DIR") doesn't exists + let filename = "./target/rpc_errors_schema.json"; + let mut guard = Guard::new(filename); + + let existing_schema: Option = { + let mut buf = Vec::new(); + guard + .file + .read_to_end(&mut buf) + .unwrap_or_else(|err| panic!("can't read {filename}: {err}")); + if buf.is_empty() { + None + } else { + let json = serde_json::from_slice(&buf) + .unwrap_or_else(|err| panic!("can't deserialize {filename}: {err}")); + Some(json) + } + }; + + let new_schema_json = match existing_schema { + None => schema_json, + Some(mut existing_schema) => { + merge(&mut existing_schema, &schema_json); + existing_schema + } + }; + + let new_schema_json_string = serde_json::to_string_pretty(&new_schema_json) + .expect("error schema serialization failed"); + + guard.file.set_len(0).unwrap_or_else(|err| panic!("can't truncate {filename}: {err}")); + guard + .file + .seek(SeekFrom::Start(0)) + .unwrap_or_else(|err| panic!("can't seek {filename}: {err}")); + guard + .file + .write_all(new_schema_json_string.as_bytes()) + .unwrap_or_else(|err| panic!("can't write {filename}: {err}")); + } +} + +#[proc_macro_derive(RpcError)] +pub fn rpc_error(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + SCHEMA.with(|s| { + parse_error_type(&mut s.borrow_mut().schema, &input); + }); + TokenStream::new() +} diff --git a/tools/speedy_sync/Cargo.toml b/tools/speedy_sync/Cargo.toml new file mode 100644 index 000000000..fe97d3de4 --- /dev/null +++ b/tools/speedy_sync/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "speedy_sync" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +unc-store.workspace = true +unc-chain-primitives.workspace = true +unc-primitives.workspace = true +framework.workspace = true +unc-chain-configs.workspace = true +unc-chain.workspace = true +unc-epoch-manager.workspace = true + +borsh.workspace = true +serde.workspace = true +serde_json.workspace = true +clap.workspace = true diff --git a/tools/speedy_sync/README.md b/tools/speedy_sync/README.md new file mode 100644 index 000000000..35942bfc9 --- /dev/null +++ b/tools/speedy_sync/README.md @@ -0,0 +1,62 @@ +# Speedy sync (a.k.a PoorMan's EpochSync) + +The goal of the speedy sync is to allow people to cathup quickly with mainnet, before we have fully implemented the EpochSync feature. + +Currently, in order to catchup with mainnet there are two possible options: +* download a DB backup that Pagoda provides (around 200GB) +* sync from scrach - which can take couple days. + +With SpeedySync, you're able to catchup with mainnet in around 2-3 hours. + +# How does it work? + +With regular sync, your job needs to download all the headers from the genesis block until now (so around 60 million headers - as of May 2022). + +This of course will take a lot of time (possibly even days). The real fix, will come once we finish building EpochSync - which would require the system to load only a single block per epoch (therefore would limit number of blocks needed by a factor of 40k - to around 12k blocks). + +But as EpochSync is not there yet, you can use SpeedySync in the meantime. + +SpeedySync uses a small checkpoint (around 50kb), that contains the necessary information about the state of the chain at a given epoch. Therefore your job can continue syncing from that moment, rather than directly from genesis. + +## Is it safe? + +Yes, but with small caveat: If someone provides you with a fake checkpoint, your future block hashes will not match, that's why **You should verify the block headers after your job is synced, to make sure that they match other blocks on the mainnet**. + + +# How do I use it? + + +## Creating a checkpoint +To create a checkpoint, please run: + +``` +cargo build -p speedy_sync + +./speedy_sync create --home $PATH_TO_RUNNING_unc_NODE --destination-dir $PATH_TO_PLACE_WHERE_TO_PUT_CHECKPOINT +``` + +## Loading a checkpoint +If your new HOME dir doesn't have a node_key.json file, you can generate a random one using: +``` +cargo run -p keypair-generator -- --home /tmp/bar --generate-config node-key +``` + + +To load a checkpoint, please run: +``` +cargo build -p speedy_sync +./speedy_sync load --source-dir $PATH_TO_CHECKPOINT_DIR --target-home $PATH_TO_HOME_DIR_OF_A_NEW_NODE +``` + + +### After running speedy + +**Important:** After running the 'load' command, you must still copy the 'node_key.json' file into that directory, before running uncd. + +Please also check and verify the config.json file. + +Afterwards you can start the uncd with the new homedir and let it sync: + +``` +./uncd --home $PATH_TO_HOME_DIR_OF_A_NEW_NODE +``` diff --git a/tools/speedy_sync/src/main.rs b/tools/speedy_sync/src/main.rs new file mode 100644 index 000000000..040497441 --- /dev/null +++ b/tools/speedy_sync/src/main.rs @@ -0,0 +1,286 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use unc_chain::types::{ChainConfig, Tip}; +use unc_chain::{Chain, ChainGenesis, DoomslugThresholdMode}; +use unc_chain_configs::{GenesisValidationMode, MutableConfigValue, ReshardingConfig}; +use unc_epoch_manager::shard_tracker::{ShardTracker, TrackedConfig}; +use unc_epoch_manager::types::EpochInfoAggregator; +use unc_epoch_manager::EpochManager; +use unc_primitives::block::Block; +use unc_primitives::block_header::BlockHeader; +use unc_primitives::epoch_manager::block_info::BlockInfo; +use unc_primitives::epoch_manager::epoch_info::EpochInfo; +use unc_primitives::epoch_manager::AGGREGATOR_KEY; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::PartialMerkleTree; +use unc_primitives::types::EpochId; +use unc_primitives::utils::index_to_bytes; +use unc_store::HEADER_HEAD_KEY; +use unc_store::{DBCol, Mode, NodeStorage, Store, StoreUpdate}; +use framework::NightshadeRuntime; +use std::fs; +use std::path::Path; + +#[derive(serde::Serialize, BorshSerialize, BorshDeserialize)] +pub struct BlockCheckpoint { + pub header: BlockHeader, + pub info: BlockInfo, + pub merkle_tree: PartialMerkleTree, +} + +#[derive(serde::Serialize, BorshSerialize, BorshDeserialize)] +pub struct EpochCheckpoint { + pub id: EpochId, + pub info: EpochInfo, +} + +#[derive(serde::Serialize, BorshSerialize, BorshDeserialize)] +pub struct SpeedyCheckpoint { + pub prev_epoch: EpochCheckpoint, + pub current_epoch: EpochCheckpoint, + pub next_epoch: EpochCheckpoint, + + pub block: BlockCheckpoint, + pub prev_block: BlockCheckpoint, + pub final_block: BlockCheckpoint, + pub first_block: BlockCheckpoint, +} + +#[derive(clap::Parser)] +pub struct CreateCmd { + #[clap(long)] + home: String, + + #[clap(long)] + destination_dir: String, +} + +#[derive(clap::Parser)] +pub struct LoadCmd { + #[clap(long)] + target_home: String, + + #[clap(long)] + source_dir: String, +} + +#[derive(clap::Parser)] +enum CliSubcmd { + Create(CreateCmd), + Load(LoadCmd), +} + +#[derive(clap::Parser)] +#[clap(subcommand_required = true, arg_required_else_help = true)] +struct Cli { + #[clap(subcommand)] + subcmd: CliSubcmd, +} + +fn read_block_checkpoint(store: &Store, block_hash: &CryptoHash) -> BlockCheckpoint { + let block: Block = store + .get_ser(DBCol::Block, block_hash.as_ref()) + .unwrap_or_else(|_| panic!("DB error Block {:?}", block_hash)) + .unwrap_or_else(|| panic!("Key missing Block {}", block_hash)); + + let info: BlockInfo = store + .get_ser(DBCol::BlockInfo, block_hash.as_ref()) + .unwrap_or_else(|_| panic!("DB error BlockInfo {:?}", block_hash)) + .unwrap_or_else(|| panic!("Key missing BlockInfo {}", block_hash)); + + let merkle_tree: PartialMerkleTree = store + .get_ser(DBCol::BlockMerkleTree, block_hash.as_ref()) + .unwrap_or_else(|_| panic!("DB error BlockMerkleTree {:?}", block_hash)) + .unwrap_or_else(|| panic!("Key missing BlockMerkleTree {}", block_hash)); + + BlockCheckpoint { header: block.header().clone(), info, merkle_tree } +} + +fn write_block_checkpoint(store_update: &mut StoreUpdate, block_checkpoint: &BlockCheckpoint) { + let hash = block_checkpoint.header.hash(); + store_update + .set_ser(DBCol::BlockHeader, hash.as_ref(), &block_checkpoint.header) + .expect("Failed writing a header"); + + store_update + .insert_ser(DBCol::BlockInfo, hash.as_ref(), &block_checkpoint.info) + .expect("Failed writing a block info"); + + store_update + .set_ser(DBCol::BlockMerkleTree, hash.as_ref(), &block_checkpoint.merkle_tree) + .expect("Failed writing merkle tree"); + store_update + .set_ser( + DBCol::BlockHeight, + &index_to_bytes(block_checkpoint.header.height()), + block_checkpoint.header.hash(), + ) + .unwrap(); +} + +fn write_epoch_checkpoint(store_update: &mut StoreUpdate, epoch_checkpoint: &EpochCheckpoint) { + store_update + .set_ser(DBCol::EpochInfo, epoch_checkpoint.id.as_ref(), &epoch_checkpoint.info) + .expect("Failed to write epoch info"); +} + +fn create_snapshot(create_cmd: CreateCmd) { + let path = Path::new(&create_cmd.home); + let store = NodeStorage::opener(path, false, &Default::default(), None) + .open_in_mode(Mode::ReadOnly) + .unwrap() + .get_hot_store(); + + // Get epoch information: + let mut epochs = store + .iter(DBCol::EpochInfo) + .filter_map(|result| { + if let Ok((key, value)) = result { + if key.as_ref() == AGGREGATOR_KEY { + None + } else { + let info = EpochInfo::try_from_slice(value.as_ref()).unwrap(); + let id = EpochId::try_from_slice(key.as_ref()).unwrap(); + Some(EpochCheckpoint { id, info }) + } + } else { + None + } + }) + .collect::>(); + + assert!(epochs.len() > 4, "Number of epochs must be greater than 4."); + + epochs.sort_by(|a, b| a.info.epoch_height().partial_cmp(&b.info.epoch_height()).unwrap()); + // Take last two epochs + let next_epoch = epochs.pop().unwrap(); + let current_epoch = epochs.pop().unwrap(); + let prev_epoch = epochs.pop().unwrap(); + + // We need information about 4 blocks to start the chain: + // + // 'block' - we'll always pick the last block of a given epoch. + // 'prev_block' - its predecessor + // 'final_block' - the block with finality (usually 2 blocks behind) + // 'first_block' - the first block of this epoch (usualy epoch_length behind). + + let block_hash = next_epoch.id.0; + let block = read_block_checkpoint(&store, &block_hash); + let block_header = block.header.clone(); + let prev_block = read_block_checkpoint(&store, block_header.prev_hash()); + let final_block = read_block_checkpoint(&store, block_header.last_final_block()); + let first_block = read_block_checkpoint(&store, block.info.epoch_first_block()); + + let checkpoint = SpeedyCheckpoint { + prev_epoch, + current_epoch, + next_epoch, + block, + prev_block, + final_block, + first_block, + }; + + let serialized = serde_json::to_string(&checkpoint).unwrap(); + + fs::write(Path::new(&create_cmd.destination_dir).join("snapshot.json"), serialized) + .expect("Failed writing to destination file"); + + fs::write( + Path::new(&create_cmd.destination_dir).join("snapshot.borsh"), + borsh::to_vec(&checkpoint).unwrap(), + ) + .expect("Failed writing to destination file"); + + fs::copy( + Path::new(&create_cmd.home).join("genesis.json"), + Path::new(&create_cmd.destination_dir).join("genesis.json"), + ) + .unwrap(); + fs::copy( + Path::new(&create_cmd.home).join("config.json"), + Path::new(&create_cmd.destination_dir).join("config.json"), + ) + .unwrap(); +} + +fn load_snapshot(load_cmd: LoadCmd) { + let data = fs::read(Path::new(&load_cmd.source_dir).join("snapshot.borsh")) + .expect("Failed reading snapshot.borsh"); + + let snapshot: SpeedyCheckpoint = SpeedyCheckpoint::try_from_slice(data.as_ref()).unwrap(); + + let home_dir = Path::new(&load_cmd.target_home); + fs::copy( + Path::new(&load_cmd.source_dir).join("genesis.json"), + Path::new(&load_cmd.target_home).join("genesis.json"), + ) + .unwrap(); + fs::copy( + Path::new(&load_cmd.source_dir).join("config.json"), + Path::new(&load_cmd.target_home).join("config.json"), + ) + .unwrap(); + + let config = framework::config::load_config(&home_dir, GenesisValidationMode::UnsafeFast) + .unwrap_or_else(|e| panic!("Error loading config: {:#}", e)); + let store = NodeStorage::opener(home_dir, config.config.archive, &Default::default(), None) + .open() + .unwrap() + .get_hot_store(); + let chain_genesis = ChainGenesis::new(&config.genesis); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &config.genesis.config); + let shard_tracker = + ShardTracker::new(TrackedConfig::from_config(&config.client_config), epoch_manager.clone()); + let runtime = + NightshadeRuntime::from_config(home_dir, store.clone(), &config, epoch_manager.clone()); + // This will initialize the database (add genesis block etc) + let _chain = Chain::new( + epoch_manager, + shard_tracker, + runtime, + &chain_genesis, + DoomslugThresholdMode::TwoThirds, + ChainConfig { + save_trie_changes: config.client_config.save_trie_changes, + background_migration_threads: 1, + resharding_config: MutableConfigValue::new( + ReshardingConfig::default(), + "resharding_config", + ), + }, + None, + ) + .unwrap(); + + let mut store_update = store.store_update(); + // Store epoch information. + write_epoch_checkpoint(&mut store_update, &snapshot.current_epoch); + write_epoch_checkpoint(&mut store_update, &snapshot.prev_epoch); + write_epoch_checkpoint(&mut store_update, &snapshot.next_epoch); + + // Store blocks. + write_block_checkpoint(&mut store_update, &snapshot.block); + write_block_checkpoint(&mut store_update, &snapshot.prev_block); + write_block_checkpoint(&mut store_update, &snapshot.final_block); + write_block_checkpoint(&mut store_update, &snapshot.first_block); + + // Store the HEADER_KEY (used in header sync). + store_update + .set_ser(DBCol::BlockMisc, HEADER_HEAD_KEY, &Tip::from_header(&snapshot.block.header)) + .unwrap(); + + // TODO: confirm if this aggregator can be empty. + // If not - we'll have to compute one and put it in the checkpoint. + let aggregator = + EpochInfoAggregator::new(snapshot.prev_epoch.id, *snapshot.final_block.header.hash()); + store_update.set_ser(DBCol::EpochInfo, AGGREGATOR_KEY, &aggregator).unwrap(); + store_update.commit().unwrap(); +} + +fn main() { + let args: Cli = clap::Parser::parse(); + match args.subcmd { + CliSubcmd::Create(create_cmd) => create_snapshot(create_cmd), + CliSubcmd::Load(load_cmd) => load_snapshot(load_cmd), + } +} diff --git a/tools/state-parts-dump-check/Cargo.toml b/tools/state-parts-dump-check/Cargo.toml new file mode 100644 index 000000000..064d00090 --- /dev/null +++ b/tools/state-parts-dump-check/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "unc-state-parts-dump-check" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix-web.workspace = true +actix.workspace = true +anyhow.workspace = true +borsh.workspace = true +clap.workspace = true +cloud-storage.workspace = true +unc-primitives.workspace = true +framework.workspace = true +unc-store.workspace = true +unc-client.workspace = true +unc-jsonrpc.workspace = true +unc-primitives-core.workspace = true +unc-o11y.workspace = true +once_cell.workspace = true +reqwest.workspace = true +tokio.workspace = true +tracing.workspace = true +[features] +nightly = [ + "nightly_protocol", + "unc-client/nightly", + "unc-jsonrpc/nightly", + "unc-o11y/nightly", + "unc-primitives-core/nightly", + "unc-primitives/nightly", + "unc-store/nightly", + "framework/nightly", +] +nightly_protocol = [ + "unc-client/nightly_protocol", + "unc-jsonrpc/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-primitives-core/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", + "framework/nightly_protocol", +] diff --git a/tools/state-parts-dump-check/src/cli.rs b/tools/state-parts-dump-check/src/cli.rs new file mode 100644 index 000000000..789acd5c4 --- /dev/null +++ b/tools/state-parts-dump-check/src/cli.rs @@ -0,0 +1,692 @@ +use actix_web::{web, App, HttpServer}; +use anyhow::anyhow; +use borsh::BorshDeserialize; +use unc_client::sync::external::{create_bucket_readonly, ExternalConnection}; +use unc_client::sync::external::{ + external_storage_location, external_storage_location_directory, get_num_parts_from_filename, +}; +use unc_jsonrpc::client::{new_client, JsonRpcClient}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::state_part::PartId; +use unc_primitives::types::{ + BlockId, BlockReference, EpochId, EpochReference, Finality, ShardId, StateRoot, +}; +use unc_primitives::views::BlockView; +use unc_store::Trie; +use framework::state_sync::extract_part_id_from_part_file_name; +use std::collections::HashSet; +use std::path::PathBuf; +use std::sync::Arc; +use std::thread::sleep; +use std::time::{Duration, Instant}; +use tokio::time::timeout; + +// This isn't set to a huge number because if we keep retrying for too long, we will miss the next epoch's check +const MAX_RETRIES: u32 = 5; + +#[derive(clap::Parser)] +pub struct StatePartsDumpCheckCommand { + #[clap(long)] + chain_id: String, + // the root dir to use when retrieving state parts from local storage + #[clap(long, value_parser)] + root_dir: Option, + // the s3 bucket to use when retrieving state parts from S3 + #[clap(long)] + s3_bucket: Option, + // the s3 region to use when retrieving state parts from S3 + #[clap(long)] + s3_region: Option, + // the gcs bucket to use when retrieving state parts from GCP + #[clap(long)] + gcs_bucket: Option, + // this can be either loop-check or single-check + #[clap(subcommand)] + subcmd: StatePartsDumpCheckSubCommand, +} + +#[derive(clap::Subcommand)] +pub enum StatePartsDumpCheckSubCommand { + /// Download and validate the state parts given the epoch_id, epoch_height, state_root and shard_id + SingleCheck(SingleCheckCommand), + /// Runs an infinite loop to download and validate state parts of all 4 shards for each epoch when it becomes available + LoopCheck(LoopCheckCommand), +} + +#[derive(clap::Parser)] +pub struct LoopCheckCommand { + /// Listen address for prometheus metrics. + #[clap(long, default_value = "0.0.0.0:4040")] + prometheus_addr: String, + // address of RPC server to retrieve latest block, epoch information + #[clap(long)] + rpc_server_addr: Option, +} + +#[derive(clap::Parser)] +pub struct SingleCheckCommand { + #[clap(long)] + epoch_id: EpochId, + #[clap(long)] + epoch_height: u64, + #[clap(long)] + state_root: StateRoot, + #[clap(long)] + shard_id: ShardId, +} + +impl StatePartsDumpCheckCommand { + pub fn run(&self) -> anyhow::Result<()> { + self.subcmd.run( + self.chain_id.clone(), + self.root_dir.clone(), + self.s3_bucket.clone(), + self.s3_region.clone(), + self.gcs_bucket.clone(), + ) + } +} + +impl StatePartsDumpCheckSubCommand { + pub fn run( + &self, + chain_id: String, + root_dir: Option, + s3_bucket: Option, + s3_region: Option, + gcs_bucket: Option, + ) -> anyhow::Result<()> { + match self { + StatePartsDumpCheckSubCommand::SingleCheck(cmd) => { + cmd.run(chain_id, root_dir, s3_bucket, s3_region, gcs_bucket) + } + StatePartsDumpCheckSubCommand::LoopCheck(cmd) => { + cmd.run(chain_id, root_dir, s3_bucket, s3_region, gcs_bucket) + } + } + } +} + +impl SingleCheckCommand { + fn run( + &self, + chain_id: String, + root_dir: Option, + s3_bucket: Option, + s3_region: Option, + gcs_bucket: Option, + ) -> anyhow::Result<()> { + let sys = actix::System::new(); + sys.block_on(async move { + let _check_result = run_single_check_with_3_retries( + chain_id, + self.epoch_id.clone(), + self.epoch_height, + self.shard_id, + self.state_root, + root_dir, + s3_bucket, + s3_region, + gcs_bucket, + ) + .await; + }); + + Ok(()) + } +} + +impl LoopCheckCommand { + // Connect to an RPC server to request latest epoch information. + // Whenever an epoch is complete, use the location specified by root_dir/s3_bucket&s3_location/gcs_bucket to download parts and validate them. + // Metrics will be emitted for epoch_height and dumped/valid/invalid/total state parts of the shard. + fn run( + &self, + chain_id: String, + root_dir: Option, + s3_bucket: Option, + s3_region: Option, + gcs_bucket: Option, + ) -> anyhow::Result<()> { + let rpc_server_addr = match &self.rpc_server_addr { + None => { + if chain_id == "mainnet" || chain_id == "testnet" { + format!("http://rpc.{}.near.org", chain_id) + } else { + return Err(anyhow!("rpc_server_addr needs to be supplied if chain_id is not mainnet or testnet")); + } + } + Some(addr) => addr.to_string(), + }; + + let rpc_client = new_client(&rpc_server_addr); + + tracing::info!("started running LoopCheckCommand"); + + let result = run_loop_all_shards( + chain_id, + root_dir, + s3_bucket, + s3_region, + gcs_bucket, + &rpc_client, + &self.prometheus_addr, + ); + + tracing::info!("run_loop_all_shards finished"); + + match result { + Ok(_) => { + tracing::info!("run_loop_all_shards returned OK()"); + } + Err(err) => { + tracing::info!("run_loop_all_shards errored out with {}", err); + } + } + + Ok(()) + } +} + +#[derive(Clone)] +// whether the check for the specified epoch's state part dump is finished or waiting +enum StatePartsDumpCheckStatus { + // download and validation for dumped parts of the epoch is done + Done { epoch_height: u64 }, + // not all required parts are dumped for the epoch + WaitingForParts { epoch_height: u64 }, +} + +#[derive(Clone)] +struct DumpCheckIterInfo { + epoch_id: EpochId, + epoch_height: u64, + state_roots: Vec, +} + +fn create_external_connection( + root_dir: Option, + bucket: Option, + region: Option, + gcs_bucket: Option, +) -> ExternalConnection { + if let Some(root_dir) = root_dir { + ExternalConnection::Filesystem { root_dir } + } else if let (Some(bucket), Some(region)) = (bucket, region) { + let bucket = create_bucket_readonly(&bucket, ®ion, Duration::from_secs(5)) + .expect("Failed to create an S3 bucket"); + ExternalConnection::S3 { bucket: Arc::new(bucket) } + } else if let Some(bucket) = gcs_bucket { + ExternalConnection::GCS { + gcs_client: Arc::new(cloud_storage::Client::default()), + reqwest_client: Arc::new(reqwest::Client::default()), + bucket, + } + } else { + panic!( + "Please provide --root-dir, or both of --s3-bucket and --s3-region, or --gcs-bucket" + ); + } +} + +fn validate_state_part(state_root: &StateRoot, part_id: PartId, part: &[u8]) -> bool { + match BorshDeserialize::try_from_slice(part) { + Ok(trie_nodes) => { + match Trie::validate_state_part(state_root, part_id, trie_nodes) { + Ok(_) => true, + // Storage error should not happen + Err(err) => { + tracing::error!(target: "state-parts", ?err, "State part storage error"); + false + } + } + } + // Deserialization error means we've got the data from malicious peer + Err(err) => { + tracing::error!(target: "state-parts", ?err, "State part deserialization error"); + false + } + } +} + +// run an infinite loop to download and validate state parts for all shards whenever new epoch becomes available +fn run_loop_all_shards( + chain_id: String, + root_dir: Option, + s3_bucket: Option, + s3_region: Option, + gcs_bucket: Option, + rpc_client: &JsonRpcClient, + prometheus_addr: &str, +) -> anyhow::Result<()> { + let mut last_check_status_vec = vec![]; + for _ in 0..4 { + last_check_status_vec + .push(Ok(StatePartsDumpCheckStatus::WaitingForParts { epoch_height: 0 })); + } + + let mut is_prometheus_server_up: bool = false; + + let sys = actix::System::new(); + loop { + tracing::info!("running the loop inside run_loop_all_shards"); + let dump_check_iter_info_res = + sys.block_on(async move { get_processing_epoch_information(&rpc_client).await }); + if let Err(err) = dump_check_iter_info_res { + tracing::info!( + "get_processing_epoch_information errs out with {}. sleeping for 60s.", + err + ); + sleep(Duration::from_secs(60)); + continue; + } + let dump_check_iter_info = dump_check_iter_info_res?; + + for shard_id in 0..4 as usize { + tracing::info!(shard_id, "started check"); + let dump_check_iter_info = dump_check_iter_info.clone(); + match last_check_status_vec[shard_id] { + Ok(StatePartsDumpCheckStatus::Done { epoch_height }) => { + tracing::info!(epoch_height, "last one was done."); + if epoch_height >= dump_check_iter_info.epoch_height { + tracing::info!("current height was already checked. sleeping for 60s."); + sleep(Duration::from_secs(60)); + continue; + } + + tracing::info!("current height was not already checked, will start checking."); + if dump_check_iter_info.epoch_height > epoch_height + 1 { + tracing::info!("there is a skip between last done epoch at epoch height: {epoch_height}, and latest available epoch at {}", dump_check_iter_info.epoch_height); + crate::metrics::STATE_SYNC_DUMP_CHECK_HAS_SKIPPED_EPOCH + .with_label_values(&[&shard_id.to_string(), &chain_id.to_string()]) + .set(1); + } else { + crate::metrics::STATE_SYNC_DUMP_CHECK_HAS_SKIPPED_EPOCH + .with_label_values(&[&shard_id.to_string(), &chain_id.to_string()]) + .set(0); + } + reset_num_parts_metrics(&chain_id, shard_id as u64); + } + Ok(StatePartsDumpCheckStatus::WaitingForParts { epoch_height }) => { + tracing::info!(epoch_height, "last one was waiting."); + if dump_check_iter_info.epoch_height > epoch_height { + tracing::info!("last one was never finished. There is a skip between last waiting epoch at epoch height {epoch_height}, and latest available epoch at {}", dump_check_iter_info.epoch_height); + crate::metrics::STATE_SYNC_DUMP_CHECK_HAS_SKIPPED_EPOCH + .with_label_values(&[&shard_id.to_string(), &chain_id.to_string()]) + .set(1); + reset_num_parts_metrics(&chain_id, shard_id as u64); + } else { + // this check would be working on the same epoch as last check, so we don't reset the num parts metrics to 0 repeatedly + tracing::info!("last one was waiting. Latest epoch is the same as last one waiting. Will recheck the same epoch at epoch_height: {}", epoch_height); + } + } + Err(_) => { + tracing::info!("last one errored out, will start check from the latest epoch"); + reset_num_parts_metrics(&chain_id, shard_id as u64); + } + } + + let chain_id = chain_id.clone(); + let root_dir = root_dir.clone(); + let s3_bucket = s3_bucket.clone(); + let s3_region = s3_region.clone(); + let gcs_bucket = gcs_bucket.clone(); + last_check_status_vec[shard_id] = sys.block_on(async move { + if !is_prometheus_server_up { + let server = HttpServer::new(move || { + App::new().service( + web::resource("/metrics") + .route(web::get().to(unc_jsonrpc::prometheus_handler)), + ) + }) + .bind(prometheus_addr)? + .workers(1) + .shutdown_timeout(3) + .disable_signals() + .run(); + tokio::spawn(server); + } + + run_single_check_with_3_retries( + chain_id, + dump_check_iter_info.epoch_id, + dump_check_iter_info.epoch_height, + shard_id as u64, + dump_check_iter_info.state_roots[shard_id], + root_dir, + s3_bucket, + s3_region, + gcs_bucket, + ) + .await + }); + is_prometheus_server_up = true; + } + } +} + +fn reset_num_parts_metrics(chain_id: &str, shard_id: ShardId) -> () { + tracing::info!(shard_id, "Resetting num of parts metrics to 0."); + crate::metrics::STATE_SYNC_DUMP_CHECK_NUM_PARTS_VALID + .with_label_values(&[&shard_id.to_string(), chain_id]) + .set(0); + crate::metrics::STATE_SYNC_DUMP_CHECK_NUM_PARTS_INVALID + .with_label_values(&[&shard_id.to_string(), chain_id]) + .set(0); +} + +async fn run_single_check_with_3_retries( + chain_id: String, + epoch_id: EpochId, + epoch_height: u64, + shard_id: ShardId, + state_root: StateRoot, + root_dir: Option, + s3_bucket: Option, + s3_region: Option, + gcs_bucket: Option, +) -> anyhow::Result { + let mut retries = 0; + let mut res; + loop { + let chain_id = chain_id.clone(); + let root_dir = root_dir.clone(); + let s3_bucket = s3_bucket.clone(); + let s3_region = s3_region.clone(); + let gcs_bucket = gcs_bucket.clone(); + let epoch_id = epoch_id.clone(); + res = run_single_check( + chain_id, + epoch_id, + epoch_height, + shard_id, + state_root, + root_dir, + s3_bucket, + s3_region, + gcs_bucket, + ) + .await; + match res { + Ok(_) => { + tracing::info!(shard_id, epoch_height, "run_single_check returned OK.",); + break; + } + Err(_) if retries < MAX_RETRIES => { + tracing::info!(shard_id, epoch_height, "run_single_check failure. Will retry.",); + retries += 1; + tokio::time::sleep(Duration::from_secs(60)).await; + } + Err(_) => { + tracing::info!( + shard_id, + epoch_height, + "run_single_check failure. No more retries." + ); + break; + } + } + } + res +} + +// download and validate state parts for a single epoch and shard +async fn run_single_check( + chain_id: String, + epoch_id: EpochId, + epoch_height: u64, + shard_id: ShardId, + state_root: StateRoot, + root_dir: Option, + s3_bucket: Option, + s3_region: Option, + gcs_bucket: Option, +) -> anyhow::Result { + tracing::info!( + epoch_height, + %state_root, + "run_single_check for" + ); + crate::metrics::STATE_SYNC_DUMP_CHECK_EPOCH_HEIGHT + .with_label_values(&[&shard_id.to_string(), &chain_id.to_string()]) + .set(epoch_height as i64); + + crate::metrics::STATE_SYNC_DUMP_CHECK_PROCESS_IS_UP + .with_label_values(&[&shard_id.to_string(), &chain_id.to_string()]) + .set(1); + + let external = create_external_connection( + root_dir.clone(), + s3_bucket.clone(), + s3_region.clone(), + gcs_bucket.clone(), + ); + + let directory_path = + external_storage_location_directory(&chain_id, &epoch_id, epoch_height, shard_id); + tracing::info!(directory_path, "the storage location for the state parts being checked:"); + let part_file_names = external.list_state_parts(shard_id, &directory_path).await?; + if part_file_names.is_empty() { + return Ok(StatePartsDumpCheckStatus::WaitingForParts { epoch_height: epoch_height }); + } + let part_file_ids: HashSet<_> = part_file_names + .iter() + .map(|file_name| extract_part_id_from_part_file_name(file_name)) + .collect(); + let num_parts = part_file_ids.len() as u64; + let total_required_parts = part_file_names + .iter() + .map(|file_name| get_num_parts_from_filename(file_name).unwrap()) + .min() + .unwrap() as u64; + + tracing::info!( + epoch_height, + %state_root, + total_required_parts, + num_parts + ); + + crate::metrics::STATE_SYNC_DUMP_CHECK_NUM_PARTS_TOTAL + .with_label_values(&[&shard_id.to_string(), &chain_id.to_string()]) + .set(total_required_parts as i64); + crate::metrics::STATE_SYNC_DUMP_CHECK_NUM_PARTS_DUMPED + .with_label_values(&[&shard_id.to_string(), &chain_id.to_string()]) + .set(num_parts as i64); + + if num_parts < total_required_parts { + tracing::info!( + epoch_height, + shard_id, + total_required_parts, + num_parts, + "Waiting for all parts to be dumped." + ); + return Ok(StatePartsDumpCheckStatus::WaitingForParts { epoch_height: epoch_height }); + } else if num_parts > total_required_parts { + tracing::info!( + epoch_height, + shard_id, + total_required_parts, + num_parts, + "There are more dumped parts than total required, something is seriously wrong." + ); + return Ok(StatePartsDumpCheckStatus::Done { epoch_height: epoch_height }); + } + + tracing::info!( + shard_id, + epoch_height, + num_parts, + "Spawning threads to download and validate state parts." + ); + + let start = Instant::now(); + let mut handles = vec![]; + for part_id in 0..num_parts { + let chain_id = chain_id.clone(); + let epoch_id = epoch_id.clone(); + let external = external.clone(); + let handle = tokio::spawn(async move { + process_part_with_3_retries( + part_id, + chain_id, + epoch_id, + epoch_height, + shard_id, + state_root, + num_parts, + external, + ) + .await + }); + handles.push(handle); + } + + for handle in handles { + let _ = handle.await.unwrap(); + } + + let duration = start.elapsed(); + tracing::info!("Time elapsed in downloading and validating the parts is: {:?}", duration); + Ok(StatePartsDumpCheckStatus::Done { epoch_height: epoch_height }) +} + +async fn process_part_with_3_retries( + part_id: u64, + chain_id: String, + epoch_id: EpochId, + epoch_height: u64, + shard_id: ShardId, + state_root: StateRoot, + num_parts: u64, + external: ExternalConnection, +) -> anyhow::Result<()> { + let mut retries = 0; + let mut res; + loop { + let chain_id = chain_id.clone(); + let epoch_id = epoch_id.clone(); + let external = external.clone(); + // timeout is needed to deal with edge cases where process_part awaits forever, i.e. the get_part().await somehow waits forever + // this is set to a long duration because the timer for each task, i.e. process_part, starts when the task is started, i.e. tokio::spawn is called, + // and counts actual time instead of CPU time. + // The bottom line is this timeout * MAX_RETRIES > time it takes for all parts to be processed, which is typically < 30 mins on monitoring nodes + let timeout_duration = tokio::time::Duration::from_secs(600); + res = timeout( + timeout_duration, + process_part( + part_id, + chain_id, + epoch_id, + epoch_height, + shard_id, + state_root, + num_parts, + external, + ), + ) + .await; + match res { + Ok(Ok(_)) => { + tracing::info!(shard_id, epoch_height, part_id, "process_part success.",); + break; + } + _ if retries < MAX_RETRIES => { + tracing::info!(shard_id, epoch_height, part_id, "process_part failed. Will retry.",); + retries += 1; + tokio::time::sleep(Duration::from_secs(5)).await; + } + _ => { + tracing::info!( + shard_id, + epoch_height, + part_id, + "process_part failed. No more retries.", + ); + break; + } + } + } + res? +} + +async fn process_part( + part_id: u64, + chain_id: String, + epoch_id: EpochId, + epoch_height: u64, + shard_id: ShardId, + state_root: StateRoot, + num_parts: u64, + external: ExternalConnection, +) -> anyhow::Result<()> { + tracing::info!(part_id, "process_part started."); + let location = + external_storage_location(&chain_id, &epoch_id, epoch_height, shard_id, part_id, num_parts); + let part = external.get_part(shard_id, &location).await?; + let is_part_valid = validate_state_part(&state_root, PartId::new(part_id, num_parts), &part); + if is_part_valid { + crate::metrics::STATE_SYNC_DUMP_CHECK_NUM_PARTS_VALID + .with_label_values(&[&shard_id.to_string(), &chain_id.to_string()]) + .inc(); + tracing::info!("part {part_id} is valid."); + } else { + crate::metrics::STATE_SYNC_DUMP_CHECK_NUM_PARTS_INVALID + .with_label_values(&[&shard_id.to_string(), &chain_id.to_string()]) + .inc(); + tracing::info!("part {part_id} is invalid."); + } + Ok(()) +} + +// get epoch information of the latest epoch that's complete +async fn get_processing_epoch_information( + rpc_client: &JsonRpcClient, +) -> anyhow::Result { + let latest_block_response = rpc_client + .block(BlockReference::Finality(Finality::Final)) + .await + .or_else(|_| Err(anyhow!("get final block failed")))?; + let latest_epoch_id = latest_block_response.header.epoch_id; + let latest_epoch_response = rpc_client + .validators(Some(EpochReference::EpochId(EpochId(latest_epoch_id)))) + .await + .or_else(|_| Err(anyhow!("validators_by_epoch_id for latest_epoch_id failed")))?; + let latest_epoch_height = latest_epoch_response.epoch_height; + let prev_epoch_last_block_response = + get_previous_epoch_last_block_response(rpc_client, latest_epoch_id).await?; + let shard_ids: Vec = (0..4).collect(); + // state roots ordered by shard_id + let prev_epoch_state_roots: Vec = shard_ids + .iter() + .map(|&shard_id| prev_epoch_last_block_response.chunks[shard_id].prev_state_root) + .collect(); + + Ok(DumpCheckIterInfo { + epoch_id: EpochId(latest_epoch_id), + epoch_height: latest_epoch_height, + state_roots: prev_epoch_state_roots, + }) +} + +async fn get_previous_epoch_last_block_response( + rpc_client: &JsonRpcClient, + current_epoch_id: CryptoHash, +) -> anyhow::Result { + let current_epoch_response = rpc_client + .validators(Some(EpochReference::EpochId(EpochId(current_epoch_id)))) + .await + .or_else(|_| Err(anyhow!("validators_by_epoch_id for current_epoch_id failed")))?; + let current_epoch_first_block_height = current_epoch_response.epoch_start_height; + let current_epoch_first_block_response = rpc_client + .block_by_id(BlockId::Height(current_epoch_first_block_height)) + .await + .or_else(|_| Err(anyhow!("block_by_id failed for current_epoch_first_block_height")))?; + let prev_epoch_last_block_hash = current_epoch_first_block_response.header.prev_hash; + let prev_epoch_last_block_response = rpc_client + .block_by_id(BlockId::Hash(prev_epoch_last_block_hash)) + .await + .or_else(|_| Err(anyhow!("block_by_id failed for prev_epoch_last_block_hash")))?; + Ok(prev_epoch_last_block_response) +} diff --git a/tools/state-parts-dump-check/src/lib.rs b/tools/state-parts-dump-check/src/lib.rs new file mode 100644 index 000000000..323080775 --- /dev/null +++ b/tools/state-parts-dump-check/src/lib.rs @@ -0,0 +1,2 @@ +pub mod cli; +mod metrics; diff --git a/tools/state-parts-dump-check/src/metrics.rs b/tools/state-parts-dump-check/src/metrics.rs new file mode 100644 index 000000000..f437d6aa9 --- /dev/null +++ b/tools/state-parts-dump-check/src/metrics.rs @@ -0,0 +1,65 @@ +use unc_o11y::metrics::{try_create_int_gauge_vec, IntGaugeVec}; +use once_cell::sync::Lazy; + +pub(crate) static STATE_SYNC_DUMP_CHECK_NUM_PARTS_VALID: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_state_sync_dump_check_num_parts_valid", + "Number of valid state parts dumped for the epoch", + &["shard_id", "chain_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_DUMP_CHECK_NUM_PARTS_INVALID: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_state_sync_dump_check_num_parts_invalid", + "Number of invalid state parts dumped for the epoch", + &["shard_id", "chain_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_DUMP_CHECK_NUM_PARTS_DUMPED: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_state_sync_dump_check_num_parts_dumped", + "Number of total parts required for the epoch", + &["shard_id", "chain_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_DUMP_CHECK_NUM_PARTS_TOTAL: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_state_sync_dump_check_num_parts_total", + "Number of total parts required for the epoch", + &["shard_id", "chain_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_DUMP_CHECK_EPOCH_HEIGHT: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_state_sync_dump_check_epoch_height", + "epoch height of the current epoch being checked", + &["shard_id", "chain_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_DUMP_CHECK_HAS_SKIPPED_EPOCH: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_state_sync_dump_check_has_skipped_epoch", + "whether there has been a skip of epoch heights in this check compared with last one", + &["shard_id", "chain_id"], + ) + .unwrap() +}); + +pub(crate) static STATE_SYNC_DUMP_CHECK_PROCESS_IS_UP: Lazy = Lazy::new(|| { + try_create_int_gauge_vec( + "unc_state_sync_dump_check_process_is_up", + "whether the state sync dump check process is up and running", + &["shard_id", "chain_id"], + ) + .unwrap() +}); diff --git a/tools/state-parts/Cargo.toml b/tools/state-parts/Cargo.toml new file mode 100644 index 000000000..1cf6c01e7 --- /dev/null +++ b/tools/state-parts/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "unc-state-parts" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +chrono.workspace = true +clap.workspace = true +once_cell.workspace = true +sha2 = "0.10.6" +tokio.workspace = true +tracing.workspace = true + +unc-async.workspace = true +unc-jsonrpc.workspace = true +unc-network.workspace = true +unc-o11y.workspace = true +unc-ping.workspace = true +unc-primitives.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-async/nightly", + "unc-jsonrpc/nightly", + "unc-network/nightly", + "unc-o11y/nightly", + "unc-ping/nightly", + "unc-primitives/nightly", +] +nightly_protocol = [ + "unc-async/nightly_protocol", + "unc-jsonrpc/nightly_protocol", + "unc-network/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-ping/nightly_protocol", + "unc-primitives/nightly_protocol", +] diff --git a/tools/state-parts/src/cli.rs b/tools/state-parts/src/cli.rs new file mode 100644 index 000000000..5d429c97c --- /dev/null +++ b/tools/state-parts/src/cli.rs @@ -0,0 +1,120 @@ +use unc_network::types::PeerInfo; +use unc_ping::cli::CHAIN_INFO; +use unc_primitives::hash::CryptoHash; +use unc_primitives::types::ShardId; +use std::str::FromStr; + +#[derive(clap::Parser)] +pub struct StatePartsCommand { + /// The hash of the first block of an epoch of which we are requesting state. + #[clap(long)] + block_hash: CryptoHash, + + /// shard_id selecting the shard, state of which we are requesting. + #[clap(long)] + shard_id: ShardId, + + /// Chain id of the peer. + #[clap(long)] + chain_id: String, + + #[clap(long)] + /// genesis hash to use in the Handshake we send. This must be provided if --chain-id + /// is not "mainnet" or "testnet" + genesis_hash: Option, + + #[clap(long)] + /// head height to use in the Handshake we send. This must be provided if --chain-id + /// is not "mainnet" or "testnet" + head_height: Option, + + /// Protocol version to advertise in our handshake + #[clap(long)] + protocol_version: Option, + + /// node public key and socket address in the format {pub key}@{socket addr}. e.g.: + /// ed25519:7PGseFbWxvYVgZ89K1uTJKYoKetWs7BJtbyXDzfbAcqX@127.0.0.1:24567 + #[clap(long)] + peer: String, + + /// ttl to set on our Routed messages + #[clap(long, default_value = "100")] + ttl: u8, + + /// milliseconds to wait between sending state part requests + #[clap(long, default_value = "1000")] + request_frequency_millis: u64, + + /// number of seconds to wait for incoming data before timing out + #[clap(long)] + recv_timeout_seconds: Option, + + /// Starting part id for the state requests. + #[clap(long, default_value = "0")] + start_part_id: u64, + + /// Number of parts in the state of the shard. + /// Assuming the tool doesn't have a valid DB and can't determine the number automatically. + #[clap(long)] + num_parts: u64, +} + +impl StatePartsCommand { + pub fn run(&self) -> anyhow::Result<()> { + tracing::warn!(target: "state-parts", "the state-parts command is not stable, and may be removed or changed arbitrarily at any time"); + + let mut chain_info = None; + for info in CHAIN_INFO.iter() { + if &info.chain_id == &self.chain_id { + chain_info = Some(info); + break; + } + } + + let genesis_hash = if let Some(h) = &self.genesis_hash { + match CryptoHash::from_str(&h) { + Ok(h) => h, + Err(e) => { + anyhow::bail!("Could not parse --genesis-hash {}: {:?}", &h, e) + } + } + } else { + match chain_info { + Some(chain_info) => chain_info.genesis_hash, + None => anyhow::bail!( + "--genesis-hash not given, and genesis hash for --chain-id {} not known", + &self.chain_id + ), + } + }; + + let peer = match PeerInfo::from_str(&self.peer) { + Ok(p) => p, + Err(e) => anyhow::bail!("Could not parse --peer {}: {:?}", &self.peer, e), + }; + if peer.addr.is_none() { + anyhow::bail!("--peer should be in the form [public key]@[socket addr]"); + } + tracing::info!(target: "state-parts", ?genesis_hash, ?peer); + let runtime = tokio::runtime::Runtime::new().unwrap(); + runtime.block_on(async move { + crate::state_parts_from_node( + self.block_hash, + self.shard_id, + &self.chain_id, + genesis_hash, + self.head_height.unwrap_or(0), + self.protocol_version, + peer.id.clone(), + peer.addr.unwrap(), + self.ttl, + self.request_frequency_millis, + self.recv_timeout_seconds.unwrap_or(5), + self.start_part_id, + self.num_parts, + ) + .await?; + Ok(()) + }) + } +} diff --git a/tools/state-parts/src/lib.rs b/tools/state-parts/src/lib.rs new file mode 100644 index 000000000..58065ee59 --- /dev/null +++ b/tools/state-parts/src/lib.rs @@ -0,0 +1,181 @@ +use anyhow::Context; +use unc_async::time; +use unc_network::raw::{ConnectError, Connection, DirectMessage, Message}; +use unc_network::types::HandshakeFailureReason; +use unc_primitives::hash::CryptoHash; +use unc_primitives::network::PeerId; +use unc_primitives::types::{AccountId, BlockHeight, ShardId}; +use unc_primitives::version::ProtocolVersion; +use sha2::Digest; +use sha2::Sha256; +use std::collections::HashMap; +use std::fmt::Write; +use std::net::SocketAddr; + +pub mod cli; + +struct AppInfo { + pub requests_sent: HashMap, +} + +impl AppInfo { + fn new() -> Self { + Self { requests_sent: HashMap::new() } + } +} + +fn handle_message( + app_info: &mut AppInfo, + msg: &Message, + received_at: time::Instant, +) -> anyhow::Result<()> { + match &msg { + Message::Direct(DirectMessage::VersionedStateResponse(response)) => { + let shard_id = response.shard_id(); + let sync_hash = response.sync_hash(); + let state_response = response.clone().take_state_response(); + let cached_parts = state_response.cached_parts(); + let part_id = state_response.part_id(); + let duration = if let Some(part_id) = part_id { + let duration = app_info + .requests_sent + .get(&part_id) + .map(|sent| (sent.elapsed() - received_at.elapsed()).as_seconds_f64()); + app_info.requests_sent.remove(&part_id); + duration + } else { + None + }; + let part_hash = if let Some(part) = state_response.part() { + Sha256::digest(&part.1).iter().fold(String::new(), |mut v, byte| { + write!(&mut v, "{:02x}", byte).unwrap(); + v + }) + } else { + "No part".to_string() + }; + + tracing::info!( + shard_id, + ?sync_hash, + ?part_id, + ?duration, + ?part_hash, + ?cached_parts, + "Received VersionedStateResponse", + ); + } + _ => {} + }; + Ok(()) +} + +#[derive(Debug)] +struct PeerIdentifier { + account_id: Option, + peer_id: PeerId, +} + +impl std::fmt::Display for PeerIdentifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match &self.account_id { + Some(a) => a.fmt(f), + None => self.peer_id.fmt(f), + } + } +} + +async fn state_parts_from_node( + block_hash: CryptoHash, + shard_id: ShardId, + chain_id: &str, + genesis_hash: CryptoHash, + head_height: BlockHeight, + protocol_version: Option, + peer_id: PeerId, + peer_addr: SocketAddr, + ttl: u8, + request_frequency_millis: u64, + recv_timeout_seconds: u32, + start_part_id: u64, + num_parts: u64, +) -> anyhow::Result<()> { + assert!(start_part_id < num_parts && num_parts > 0, "{}/{}", start_part_id, num_parts); + let mut app_info = AppInfo::new(); + + let mut peer = match Connection::connect( + peer_addr, + peer_id.clone(), + protocol_version, + chain_id, + genesis_hash, + head_height, + vec![0], + time::Duration::seconds(recv_timeout_seconds.into())).await { + Ok(p) => p, + Err(ConnectError::HandshakeFailure(reason)) => { + match reason { + HandshakeFailureReason::ProtocolVersionMismatch { version, oldest_supported_version } => anyhow::bail!( + "Received Handshake Failure: {:?}. Try running again with --protocol-version between {} and {}", + reason, oldest_supported_version, version + ), + HandshakeFailureReason::GenesisMismatch(_) => anyhow::bail!( + "Received Handshake Failure: {:?}. Try running again with --chain-id and --genesis-hash set to these values.", + reason, + ), + HandshakeFailureReason::InvalidTarget => anyhow::bail!( + "Received Handshake Failure: {:?}. Is the public key given with --peer correct?", + reason, + ), + } + } + Err(e) => { + anyhow::bail!("Error connecting to {:?}: {}", peer_addr, e); + } + }; + tracing::info!(target: "state-parts", ?peer_addr, ?peer_id, "Connected to peer"); + + let next_request = tokio::time::sleep(std::time::Duration::ZERO); + tokio::pin!(next_request); + + let mut result = Ok(()); + let mut part_id = start_part_id; + loop { + tokio::select! { + _ = &mut next_request => { + let target = &peer_id; + let msg = DirectMessage::StateRequestPart(shard_id, block_hash, part_id); + tracing::info!(target: "state-parts", ?target, shard_id, ?block_hash, part_id, ttl, "Sending a request"); + result = peer.send_message(msg).await.with_context(|| format!("Failed sending State Part Request to {:?}", target)); + app_info.requests_sent.insert(part_id, time::Instant::now()); + tracing::info!(target: "state-parts", ?result); + if result.is_err() { + break; + } + next_request.as_mut().reset(tokio::time::Instant::now() + std::time::Duration::from_millis(request_frequency_millis)); + part_id = (part_id + 1) % num_parts; + } + res = peer.recv() => { + let (msg, first_byte_time) = match res { + Ok(x) => x, + Err(e) => { + result = Err(e).context("Failed receiving messages"); + break; + } + }; + result = handle_message( + &mut app_info, + &msg, + first_byte_time.try_into().unwrap(), + ); + if result.is_err() { + break; + } + } + _ = tokio::signal::ctrl_c() => { + break; + } + } + } + result +} diff --git a/tools/state-viewer/Cargo.toml b/tools/state-viewer/Cargo.toml new file mode 100644 index 000000000..678cb9926 --- /dev/null +++ b/tools/state-viewer/Cargo.toml @@ -0,0 +1,85 @@ +[package] +name = "state-viewer" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix.workspace = true +anyhow.workspace = true +borsh.workspace = true +bytesize.workspace = true +chrono.workspace = true +clap.workspace = true +cloud-storage.workspace = true +itertools.workspace = true +once_cell.workspace = true +rand.workspace = true +rayon.workspace = true +redis.workspace = true +regex.workspace = true +reqwest.workspace = true +rust-s3.workspace = true +serde.workspace = true +serde_json.workspace = true +strum.workspace = true +tempfile.workspace = true +thiserror.workspace = true +tracing.workspace = true +yansi.workspace = true + +unc-chain-configs.workspace = true +unc-chain.workspace = true +unc-client.workspace = true +unc-crypto.workspace = true +unc-epoch-manager.workspace = true +unc-network.workspace = true +unc-o11y.workspace = true +unc-primitives-core.workspace = true +unc-primitives.workspace = true +unc-store.workspace = true +unc-test-contracts.workspace = true +framework.workspace = true +node-runtime.workspace = true + +[dev-dependencies] +unc-client.workspace = true +testlib.workspace = true +insta.workspace = true + +[features] +sandbox = ["node-runtime/sandbox", "unc-chain/sandbox", "unc-client/sandbox"] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-chain/nightly", + "unc-client/nightly", + "unc-epoch-manager/nightly", + "unc-network/nightly", + "unc-o11y/nightly", + "unc-primitives-core/nightly", + "unc-primitives/nightly", + "unc-store/nightly", + "framework/nightly", + "node-runtime/nightly", +] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-chain/nightly_protocol", + "unc-client/nightly_protocol", + "unc-epoch-manager/nightly_protocol", + "unc-network/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-primitives-core/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", + "framework/nightly_protocol", + "node-runtime/nightly_protocol", +] diff --git a/tools/state-viewer/README.md b/tools/state-viewer/README.md new file mode 100644 index 000000000..9013b5a13 --- /dev/null +++ b/tools/state-viewer/README.md @@ -0,0 +1,206 @@ +# `uncd view_state` + +`uncd view_state` is a tool that helps you look into the state of the blockchain, which includes: + +* apply old blocks with a new version of the code or of the protocol +* generate a genesis file from the current state of the blockchain + +## Functions + +TODO: Fill out documentation for all available commands + +### `apply_range` + +Basic example: +```bash +make uncd +./target/release/uncd --home ~/.near/ view_state apply_range \ + --shard-id=0 --start-index=42376889 --end_index=423770101 \ + --verbose-output --csv-file=./apply_range.csv +``` + +This command will: +* build `uncd` with link-time optimizations +* open the blockchain state at the location provided by `--home` +* for each block with height between `--start-index` and `--end-index` + * Run `apply_transactions` function +* Print individual outcomes if `--verbose-output` is provided. Useful for finding and debugging differences in replaying +the history. +* Print a csv file if `--csv-file` is provided. The csv file contains per-block statistics such as, timestamp of the +block, gas per block, delayed receipts per block. Useful for debugging performance issues. Don't forget to sort your +data before making charts using this data. + +If you want to re-apply all the blocks in the available blockchain then omit both the `--start-index` and `--end-index` +flags. Missing `--start-index` means use chain state starting from the genesis. Missing `--end-index` means using blocks up to the latest block available in the blockchain. + +Enable debug output to print extra details such as individual outcomes: + +```bash +./target/release/uncd view_state apply_range --verbose ... +``` + +To make more precise time estimations, enable `--sequential` flag, which will also cause slowdown proportional to the +number of rayon threads. + +#### Running for the whole `mainnet` history + +As of today you need approximately 2TB of disk space for the whole history of `mainnet`, and the most practical way of +obtaining this whole history is the following: + +* Patch to define your own GCP instance in project `rpc-prod`. +* Make sure to change `machine-name` and `role` to something unique. +* Make a Pull Request and ask Mario (@mhalambek) or Sandi (@chefsale) for review. +* Ask Mario or Sandi to grant you permissions to the GCP project `rpc-prod`. +* Run `terraform init` and `terraform apply` to start an instance. This instance will have a running `uncd` systemd + service, with `/home/ubuntu/.near` as the home directory. Follow the `terraform` CLI + [installation guide](https://learn.hashicorp.com/tutorials/terraform/install-cli) if needed. +* SSH using `gcloud compute ssh " --project=rpc-prod`. Follow the `gcloud` CLI + [installation guide](https://cloud.google.com/sdk/docs/install) if needed. +* It is recommended to run all the following commands as user `ubuntu`: `sudo su ubuntu`. +* Install tools be able to compile `uncd`: + * Install development packages: + * Install Rust: + * Clone the git repository: `git clone http://github.com/utnet-org/utility` + * `make uncd` +* `sudo systemctl stop uncd`, because a running node has a LOCK over the database. +* Run `uncd view_state` as described above +* Enjoy + +#### Checking Predicates + +It's hard to know in advance which predicates will be of interest. If you want to check that none of function calls use +more than X gas, feel free to add the check yourself. + +### `view_chain` + +If called without arguments this command will print the block header of tip of the chain, and chunk extras for that +block. + +Flags: + +* `--height` gets the block header and chunk extras for a block at a certain height. +* `--block` displays contents of the block itself, such as timestamp, outcome_root, challenges, and many more. +* `--chunk` displays contents of the chunk, such as transactions and receipts. + +### `dump_state` + +Saves the current state of the network in a new genesis file. + +Flags: + +* `--height` takes state from the genesis up to and including the given height. By default, the tool dumps all available states. + +* `--account-ids`, if set, specifies the only accounts that will appear in the output genesis file, except for validators, who will always be included. + +Example: + +```shell +./target/release/uncd --home ~/.near/mainnet/ view_state dump_state --height 68874690 --account-ids near +``` + +### `dump_tx` + +Saves all transactions of a range of blocks [start, end] to a file. + +Flags: + +* `--start-height` specifies the start block by its height, inclusive. + +* `--end-height` specifies the end block by its height, inclusive. + +* `--account-ids` specifies the accounts as receivers of the transactions that need to be dumped. By default, all transactions will be dumped if this parameter is not set. + +Example: + +```shell +./target/release/uncd --home ~/.near/mainnet/ view_state dump_tx --start-height 68701890 --end-height 68701890 --account-ids near +``` + +### `rocksdb_stats` + +Tool for measuring statistics of the store for each column: +- number of entries +- column size +- total keys size +- total values size + +Before running, install `sst_dump` tool as follows: + +```shell +git clone https://github.com/facebook/rocksdb.git +cd rocksdb +make sst_dump +sudo cp sst_dump /usr/local/bin/ +``` + +Should take ~2m for RPC node and 45m for archival node as of 4 Jan 2022. + +#### Output + +List of statistics for each column sorted by column size. + +#### Running on macOS + +```bash +brew install --cask google-cloud-sdk +export PATH=/usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/bin:$PATH +gcloud beta compute ssh --zone "europe-west4-a" "" --project "rpc-prod" +``` + +Check running instances at to see the machine +name and datacenter. + +### contract-accounts + +List account names with contracts deployed and additional information about the +contracts. + +By default, the command only displays the names of all accounts that have a +contract deployed right now. This should be fairly quick. Using flags, you can +display more information but it will also slow down the process. + +To see a list of flags, run +```ignore +cargo run -p uncd -- view-state contract-accounts --help +``` + +#### Example + +The following command lists all (but the skipped) accounts with contracts +deployed with the additional information of how many times the account has been +the receiver of a receipt and how many times a receipt was sent by the contract. +(Note: outgoing counts only sent by the contract, not anything where the signed +was the same account id.) + +Additionally, the output contains a list of actions that were in the outgoing +receipts. This is particularly useful to find contracts that call certain +actions on-chain. + +```ignore +cargo run -p uncd -- view-state contract-accounts \ + --skip-accounts "aurora,relay.aurora,token.sweat,oracle.sweat,tge-lockup.near,sweat_welcome.near" \ + --receipts-in \ + --receipts-out \ + --actions \ + > output.log +``` + +And the output may look something like thi: +```ignore +ACCOUNT_ID RCPTS_IN RCPTS_OUT ACTIONS +0-0.near 37 14 Transfer +0-1.near 797 117 Transfer +0.app.hipodev.near 8 9 Transfer +0.app2.hipodev.near 4 5 Transfer +0.near 56 9 Transfer +00.near 29 5 Transfer +000.near 190 17 Transfer +0000.mintbase1.near 49 68 FunctionCall,Transfer +... +And 18858 errors: +failed loading outgoing receipt DpoPSrAHECYrpntTdYXrp2W2Ad3yEPyMyCav4mXi8kyh +failed loading outgoing receipt BoSv67f3CYsWu9LfLxPNkKGSEnwiH7jwPzEmYwL5c7rm +... +failed loading outgoing receipt D4AEcD6umuJKGjSNA2JEZ4EMxn3GK4Z8Ew1iAQpWYtPS +failed loading outgoing receipt AAht3HUDJeGRJ1N776ZKJ2vRiRBAD9GtsLabgbrdioAC +``` diff --git a/tools/state-viewer/src/apply_chain_range.rs b/tools/state-viewer/src/apply_chain_range.rs new file mode 100644 index 000000000..17a1253c3 --- /dev/null +++ b/tools/state-viewer/src/apply_chain_range.rs @@ -0,0 +1,649 @@ +use unc_chain::chain::collect_receipts_from_response; +use unc_chain::migrations::check_if_block_is_first_with_chunk_of_version; +use unc_chain::types::{ + ApplyChunkBlockContext, ApplyChunkResult, ApplyChunkShardContext, RuntimeAdapter, + RuntimeStorageConfig, +}; +use unc_chain::{ChainStore, ChainStoreAccess, ChainStoreUpdate}; +use unc_chain_configs::Genesis; +use unc_epoch_manager::{EpochManagerAdapter, EpochManagerHandle}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::DelayedReceiptIndices; +use unc_primitives::transaction::{Action, ExecutionOutcomeWithId, ExecutionOutcomeWithProof}; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::chunk_extra::ChunkExtra; +use unc_primitives::types::{BlockHeight, ShardId}; +use unc_store::{DBCol, Store}; +use framework::NightshadeRuntime; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use std::fs::File; +use std::io::Write; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::{Arc, Mutex}; + +fn timestamp_ms() -> u64 { + use std::time::{SystemTime, UNIX_EPOCH}; + SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as u64 +} +pub const TGAS: u64 = 1024 * 1024 * 1024 * 1024; + +struct ProgressReporter { + cnt: AtomicU64, + // Timestamp to make relative measurements of block processing speed (in ms) + ts: AtomicU64, + all: u64, + skipped: AtomicU64, + // Fields below get cleared after each print. + empty_blocks: AtomicU64, + non_empty_blocks: AtomicU64, + // Total gas burned (in TGas) + tgas_burned: AtomicU64, +} + +impl ProgressReporter { + pub fn inc_and_report_progress(&self, gas_burnt: u64) { + let ProgressReporter { cnt, ts, all, skipped, empty_blocks, non_empty_blocks, tgas_burned } = + self; + if gas_burnt == 0 { + empty_blocks.fetch_add(1, Ordering::Relaxed); + } else { + non_empty_blocks.fetch_add(1, Ordering::Relaxed); + tgas_burned.fetch_add(gas_burnt / TGAS, Ordering::Relaxed); + } + + const PRINT_PER: u64 = 100; + let prev = cnt.fetch_add(1, Ordering::Relaxed); + if (prev + 1) % PRINT_PER == 0 { + let prev_ts = ts.load(Ordering::Relaxed); + let new_ts = timestamp_ms(); + let per_second = (PRINT_PER as f64 / (new_ts - prev_ts) as f64) * 1000.0; + ts.store(new_ts, Ordering::Relaxed); + let secs_remaining = (all - prev) as f64 / per_second; + let avg_gas = if non_empty_blocks.load(Ordering::Relaxed) == 0 { + 0.0 + } else { + tgas_burned.load(Ordering::Relaxed) as f64 + / non_empty_blocks.load(Ordering::Relaxed) as f64 + }; + + println!( + "Processed {} blocks, {:.4} blocks per second ({} skipped), {:.2} secs remaining {} empty blocks {:.2} avg gas per non-empty block", + prev + 1, + per_second, + skipped.load(Ordering::Relaxed), + secs_remaining, + empty_blocks.load(Ordering::Relaxed), + avg_gas, + ); + empty_blocks.store(0, Ordering::Relaxed); + non_empty_blocks.store(0, Ordering::Relaxed); + tgas_burned.store(0, Ordering::Relaxed); + } + } +} + +fn old_outcomes( + store: Store, + new_outcomes: &[ExecutionOutcomeWithId], +) -> Vec { + new_outcomes + .iter() + .map(|outcome| { + let old_outcome = store + .iter_prefix_ser::( + DBCol::TransactionResultForBlock, + outcome.id.as_ref(), + ) + .next() + .unwrap() + .unwrap() + .1 + .outcome; + ExecutionOutcomeWithId { id: outcome.id, outcome: old_outcome } + }) + .collect() +} + +fn maybe_add_to_csv(csv_file_mutex: &Mutex>, s: &str) { + let mut csv_file = csv_file_mutex.lock().unwrap(); + if let Some(csv_file) = csv_file.as_mut() { + writeln!(csv_file, "{}", s).unwrap(); + } +} + +fn apply_block_from_range( + height: BlockHeight, + shard_id: ShardId, + store: Store, + genesis: &Genesis, + epoch_manager: &EpochManagerHandle, + runtime_adapter: Arc, + progress_reporter: &ProgressReporter, + verbose_output: bool, + csv_file_mutex: &Mutex>, + only_contracts: bool, + use_flat_storage: bool, +) { + // normally save_trie_changes depends on whether the node is + // archival, but here we don't care, and can just set it to false + // since we're not writing anything to the store anyway + let mut chain_store = ChainStore::new(store.clone(), genesis.config.genesis_height, false); + let block_hash = match chain_store.get_block_hash_by_height(height) { + Ok(block_hash) => block_hash, + Err(_) => { + // Skipping block because it's not available in ChainStore. + progress_reporter.inc_and_report_progress(0); + return; + } + }; + let block = chain_store.get_block(&block_hash).unwrap(); + let shard_uid = epoch_manager.shard_id_to_uid(shard_id, block.header().epoch_id()).unwrap(); + assert!(block.chunks().len() > 0); + let mut existing_chunk_extra = None; + let mut prev_chunk_extra = None; + let mut num_tx = 0; + let mut num_receipt = 0; + let chunk_present: bool; + + let block_author = epoch_manager + .get_block_producer(block.header().epoch_id(),block.header().height()) + .unwrap(); + + let apply_result = if *block.header().prev_hash() == CryptoHash::default() { + if verbose_output { + println!("Skipping the genesis block #{}.", height); + } + progress_reporter.inc_and_report_progress(0); + return; + } else if block.chunks()[shard_id as usize].height_included() == height { + chunk_present = true; + let res_existing_chunk_extra = chain_store.get_chunk_extra(&block_hash, &shard_uid); + assert!( + res_existing_chunk_extra.is_ok(), + "Can't get existing chunk extra for block #{}", + height + ); + existing_chunk_extra = Some(res_existing_chunk_extra.unwrap()); + let chunk_hash = block.chunks()[shard_id as usize].chunk_hash(); + let chunk = chain_store.get_chunk(&chunk_hash).unwrap_or_else(|error| { + panic!( + "Can't get chunk on height: {} chunk_hash: {:?} error: {}", + height, chunk_hash, error + ); + }); + + let prev_block = match chain_store.get_block(block.header().prev_hash()) { + Ok(prev_block) => prev_block, + Err(_) => { + if verbose_output { + println!("Skipping applying block #{} because the previous block is unavailable and I can't determine the gas_price to use.", height); + } + maybe_add_to_csv( + csv_file_mutex, + &format!( + "{},{},{},,,{},,{},,", + height, + block_hash, + block_author, + block.header().raw_timestamp(), + chunk_present + ), + ); + progress_reporter.inc_and_report_progress(0); + return; + } + }; + + let chain_store_update = ChainStoreUpdate::new(&mut chain_store); + let receipt_proof_response = chain_store_update + .get_incoming_receipts_for_shard( + epoch_manager, + shard_id, + block_hash, + prev_block.chunks()[shard_id as usize].height_included(), + ) + .unwrap(); + let receipts = collect_receipts_from_response(&receipt_proof_response); + + let chunk_inner = chunk.cloned_header().take_inner(); + let is_first_block_with_chunk_of_version = check_if_block_is_first_with_chunk_of_version( + &chain_store, + epoch_manager, + block.header().prev_hash(), + shard_id, + ) + .unwrap(); + + num_receipt = receipts.len(); + num_tx = chunk.transactions().len(); + if only_contracts { + let mut has_contracts = false; + for tx in chunk.transactions() { + for action in &tx.transaction.actions { + has_contracts = has_contracts + || matches!(action, Action::FunctionCall(_) | Action::DeployContract(_)); + } + } + if !has_contracts { + progress_reporter.skipped.fetch_add(1, Ordering::Relaxed); + return; + } + } + runtime_adapter + .apply_chunk( + RuntimeStorageConfig::new(*chunk_inner.prev_state_root(), use_flat_storage), + ApplyChunkShardContext { + shard_id, + last_validator_power_proposals: chunk_inner.prev_validator_power_proposals(), + last_validator_frozen_proposals: chunk_inner.prev_validator_frozen_proposals(), + gas_limit: chunk_inner.gas_limit(), + is_new_chunk: true, + is_first_block_with_chunk_of_version, + }, + ApplyChunkBlockContext::from_header( + block.header(), + prev_block.header().next_gas_price(), + ), + &receipts, + chunk.transactions(), + ) + .unwrap() + } else { + chunk_present = false; + let chunk_extra = + chain_store.get_chunk_extra(block.header().prev_hash(), &shard_uid).unwrap(); + prev_chunk_extra = Some(chunk_extra.clone()); + + runtime_adapter + .apply_chunk( + RuntimeStorageConfig::new(*chunk_extra.state_root(), use_flat_storage), + ApplyChunkShardContext { + shard_id, + last_validator_power_proposals: chunk_extra.validator_power_proposals(), + last_validator_frozen_proposals: chunk_extra.validator_frozen_proposals(), + gas_limit: chunk_extra.gas_limit(), + is_new_chunk: false, + is_first_block_with_chunk_of_version: false, + }, + ApplyChunkBlockContext::from_header( + block.header(), + block.header().next_gas_price(), + ), + &[], + &[], + ) + .unwrap() + }; + + let (outcome_root, _) = ApplyChunkResult::compute_outcomes_proof(&apply_result.outcomes); + let chunk_extra = ChunkExtra::new( + &apply_result.new_root, + outcome_root, + apply_result.validator_power_proposals, + apply_result.validator_frozen_proposals, + apply_result.total_gas_burnt, + genesis.config.gas_limit, + apply_result.total_balance_burnt, + ); + + let state_update = + runtime_adapter.get_tries().new_trie_update(shard_uid, *chunk_extra.state_root()); + let delayed_indices = + unc_store::get::(&state_update, &TrieKey::DelayedReceiptIndices); + + match existing_chunk_extra { + Some(existing_chunk_extra) => { + if verbose_output { + println!("block_height: {}, block_hash: {}\nchunk_extra: {:#?}\nexisting_chunk_extra: {:#?}\noutcomes: {:#?}", height, block_hash, chunk_extra, existing_chunk_extra, apply_result.outcomes); + } + if !smart_equals(&existing_chunk_extra, &chunk_extra) { + panic!("Got a different ChunkExtra:\nblock_height: {}, block_hash: {}\nchunk_extra: {:#?}\nexisting_chunk_extra: {:#?}\nnew outcomes: {:#?}\n\nold outcomes: {:#?}\n", height, block_hash, chunk_extra, existing_chunk_extra, apply_result.outcomes, old_outcomes(store, &apply_result.outcomes)); + } + } + None => { + assert!(prev_chunk_extra.is_some()); + assert!(apply_result.outcomes.is_empty()); + if verbose_output { + println!("block_height: {}, block_hash: {}\nchunk_extra: {:#?}\nprev_chunk_extra: {:#?}\noutcomes: {:#?}", height, block_hash, chunk_extra, prev_chunk_extra, apply_result.outcomes); + } + } + }; + maybe_add_to_csv( + csv_file_mutex, + &format!( + "{},{},{},{},{},{},{},{},{},{},{}", + height, + block_hash, + block_author, + num_tx, + num_receipt, + block.header().raw_timestamp(), + apply_result.total_gas_burnt, + chunk_present, + apply_result.processed_delayed_receipts.len(), + delayed_indices.unwrap_or(None).map_or(0, |d| d.next_available_index - d.first_index), + apply_result.trie_changes.state_changes().len(), + ), + ); + progress_reporter.inc_and_report_progress(apply_result.total_gas_burnt); +} + +pub fn apply_chain_range( + store: Store, + genesis: &Genesis, + start_height: Option, + end_height: Option, + shard_id: ShardId, + epoch_manager: &EpochManagerHandle, + runtime_adapter: Arc, + verbose_output: bool, + csv_file: Option<&mut File>, + only_contracts: bool, + sequential: bool, + use_flat_storage: bool, +) { + let parent_span = tracing::debug_span!( + target: "state_viewer", + "apply_chain_range", + ?start_height, + ?end_height, + %shard_id, + only_contracts, + sequential, + use_flat_storage) + .entered(); + let chain_store = ChainStore::new(store.clone(), genesis.config.genesis_height, false); + let end_height = end_height.unwrap_or_else(|| chain_store.head().unwrap().height); + let start_height = start_height.unwrap_or_else(|| chain_store.tail().unwrap()); + + println!( + "Applying chunks in the range {}..={} for shard_id {}", + start_height, end_height, shard_id + ); + + println!("Printing results including outcomes of applying receipts"); + let csv_file_mutex = Mutex::new(csv_file); + maybe_add_to_csv(&csv_file_mutex, "Height,Hash,Author,#Tx,#Receipt,Timestamp,GasUsed,ChunkPresent,#ProcessedDelayedReceipts,#DelayedReceipts,#StateChanges"); + + let range = start_height..=end_height; + let progress_reporter = ProgressReporter { + cnt: AtomicU64::new(0), + ts: AtomicU64::new(timestamp_ms()), + all: end_height - start_height, + skipped: AtomicU64::new(0), + empty_blocks: AtomicU64::new(0), + non_empty_blocks: AtomicU64::new(0), + tgas_burned: AtomicU64::new(0), + }; + let process_height = |height| { + apply_block_from_range( + height, + shard_id, + store.clone(), + genesis, + epoch_manager, + runtime_adapter.clone(), + &progress_reporter, + verbose_output, + &csv_file_mutex, + only_contracts, + use_flat_storage, + ); + }; + + if sequential { + range.into_iter().for_each(|height| { + let _span = tracing::debug_span!( + target: "state_viewer", + parent: &parent_span, + "process_block_in_order", + height) + .entered(); + process_height(height) + }); + } else { + range.into_par_iter().for_each(|height| { + let _span = tracing::debug_span!( + target: "mock_node", + parent: &parent_span, + "process_block_in_parallel", + height) + .entered(); + process_height(height) + }); + } + + println!( + "No differences found after applying chunks in the range {}..={} for shard_id {}", + start_height, end_height, shard_id + ); +} + +/** + * With the database migration we can get into the situation where there are different + * ChunkExtra versions in database and produced by `uncd` playback. Consider them equal as + * long as the content is equal. + */ +fn smart_equals(extra1: &ChunkExtra, extra2: &ChunkExtra) -> bool { + if (extra1.outcome_root() != extra2.outcome_root()) + || (extra1.state_root() != extra2.state_root()) + || (extra1.gas_limit() != extra2.gas_limit()) + || (extra1.gas_used() != extra2.gas_used()) + || (extra1.balance_burnt() != extra2.balance_burnt()) + { + return false; + } + let mut proposals1 = extra1.validator_power_proposals(); + let mut proposals2 = extra2.validator_power_proposals(); + if proposals1.len() != proposals2.len() { + return false; + } + for _ in 0..proposals1.len() { + let p1 = proposals1.next().unwrap(); + let p2 = proposals2.next().unwrap(); + + if p1.into_v1() != p2.into_v1() { + return false; + } + } + let mut proposals1 = extra1.validator_frozen_proposals(); + let mut proposals2 = extra2.validator_frozen_proposals(); + if proposals1.len() != proposals2.len() { + return false; + } + for _ in 0..proposals1.len() { + let p1 = proposals1.next().unwrap(); + let p2 = proposals2.next().unwrap(); + + if p1.into_v1() != p2.into_v1() { + return false; + } + } + true +} + +#[cfg(test)] +mod test { + use std::io::{Read, Seek, SeekFrom}; + use std::path::Path; + + use unc_chain::{ChainGenesis, Provenance}; + use unc_chain_configs::Genesis; + use unc_client::test_utils::TestEnv; + use unc_client::ProcessTxResponse; + use unc_crypto::{InMemorySigner, KeyType}; + use unc_epoch_manager::EpochManager; + use unc_primitives::transaction::SignedTransaction; + use unc_primitives::types::{BlockHeight, BlockHeightDelta, NumBlocks}; + use unc_store::genesis::initialize_genesis_state; + use unc_store::test_utils::create_test_store; + use unc_store::Store; + use framework::config::GenesisExt; + use framework::config::TESTING_INIT_STAKE; + use framework::NightshadeRuntime; + + use crate::apply_chain_range::apply_chain_range; + + fn setup(epoch_length: NumBlocks) -> (Store, Genesis, TestEnv) { + let mut genesis = + Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.num_block_producer_seats = 2; + genesis.config.num_block_producer_seats_per_shard = vec![2]; + genesis.config.epoch_length = epoch_length; + let store = create_test_store(); + initialize_genesis_state(store.clone(), &genesis, None); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let nightshade_runtime = NightshadeRuntime::test( + Path::new("."), + store.clone(), + &genesis.config, + epoch_manager.clone(), + ); + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + chain_genesis.gas_limit = genesis.config.gas_limit; + let env = TestEnv::builder(chain_genesis) + .validator_seats(2) + .stores(vec![store.clone()]) + .epoch_managers(vec![epoch_manager]) + .runtimes(vec![nightshade_runtime]) + .build(); + (store, genesis, env) + } + + /// Produces blocks, avoiding the potential failure where the client is not the + /// block producer for each subsequent height (this can happen when a new validator + /// is staked since they will also have heights where they should produce the block instead). + fn safe_produce_blocks( + env: &mut TestEnv, + initial_height: BlockHeight, + num_blocks: BlockHeightDelta, + block_without_chunks: Option, + ) { + let mut h = initial_height; + let mut blocks = vec![]; + for _ in 1..=num_blocks { + let mut block = None; + // `env.clients[0]` may not be the block producer at `h`, + // loop until we find a height env.clients[0] should produce. + while block.is_none() { + block = env.clients[0].produce_block(h).unwrap(); + h += 1; + } + let mut block = block.unwrap(); + if let Some(block_without_chunks) = block_without_chunks { + if block_without_chunks == h { + assert!(!blocks.is_empty()); + testlib::process_blocks::set_no_chunk_in_block( + &mut block, + blocks.last().unwrap(), + ) + } + } + blocks.push(block.clone()); + env.process_block(0, block, Provenance::PRODUCED); + } + } + + #[test] + fn test_apply_chain_range() { + let epoch_length = 4; + let (store, genesis, mut env) = setup(epoch_length); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let tx = SignedTransaction::stake( + 1, + "test1".parse().unwrap(), + &signer, + TESTING_INIT_STAKE, + signer.public_key.clone(), + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + + safe_produce_blocks(&mut env, 1, epoch_length * 2 + 1, None); + + initialize_genesis_state(store.clone(), &genesis, None); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = NightshadeRuntime::test( + Path::new("."), + store.clone(), + &genesis.config, + epoch_manager.clone(), + ); + apply_chain_range( + store, + &genesis, + None, + None, + 0, + epoch_manager.as_ref(), + runtime, + true, + None, + false, + false, + false, + ); + } + + #[test] + fn test_apply_chain_range_no_chunks() { + let epoch_length = 4; + let (store, genesis, mut env) = setup(epoch_length); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let tx = SignedTransaction::stake( + 1, + "test1".parse().unwrap(), + &signer, + TESTING_INIT_STAKE, + signer.public_key.clone(), + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + + safe_produce_blocks(&mut env, 1, epoch_length * 2 + 1, Some(5)); + + initialize_genesis_state(store.clone(), &genesis, None); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = NightshadeRuntime::test( + Path::new("."), + store.clone(), + &genesis.config, + epoch_manager.clone(), + ); + let mut file = tempfile::NamedTempFile::new().unwrap(); + apply_chain_range( + store, + &genesis, + None, + None, + 0, + epoch_manager.as_ref(), + runtime, + true, + Some(file.as_file_mut()), + false, + false, + false, + ); + let mut csv = String::new(); + file.as_file_mut().seek(SeekFrom::Start(0)).unwrap(); + file.as_file_mut().read_to_string(&mut csv).unwrap(); + let lines: Vec<&str> = csv.split("\n").collect(); + assert!(lines[0].contains("Height")); + let mut has_tx = 0; + let mut no_tx = 0; + for line in &lines { + if line.contains(",test0,1,0,") { + has_tx += 1; + } + if line.contains(",test0,0,0,") { + no_tx += 1; + } + } + assert_eq!(has_tx, 1, "{:#?}", lines); + assert_eq!(no_tx, 8, "{:#?}", lines); + } +} diff --git a/tools/state-viewer/src/apply_chunk.rs b/tools/state-viewer/src/apply_chunk.rs new file mode 100644 index 000000000..6f3da032c --- /dev/null +++ b/tools/state-viewer/src/apply_chunk.rs @@ -0,0 +1,764 @@ +use anyhow::{anyhow, Context}; +use borsh::BorshDeserialize; +use unc_chain::chain::collect_receipts_from_response; +use unc_chain::migrations::check_if_block_is_first_with_chunk_of_version; +use unc_chain::types::{ + ApplyChunkBlockContext, ApplyChunkResult, ApplyChunkShardContext, RuntimeAdapter, + RuntimeStorageConfig, +}; +use unc_chain::{ChainStore, ChainStoreAccess}; +use unc_epoch_manager::{EpochManagerAdapter, EpochManagerHandle}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::combine_hash; +use unc_primitives::receipt::Receipt; +use unc_primitives::shard_layout; +use unc_primitives::sharding::{ChunkHash, ReceiptProof}; +use unc_primitives::state_sync::ReceiptProofResponse; +use unc_primitives::types::{BlockHeight, ShardId}; +use unc_primitives_core::hash::hash; +use unc_primitives_core::types::Gas; +use unc_store::DBCol; +use unc_store::Store; +use rand::rngs::StdRng; +use rand::seq::SliceRandom; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; + +// like ChainStoreUpdate::get_incoming_receipts_for_shard(), but for the case when we don't +// know of a block containing the target chunk +fn get_incoming_receipts( + chain_store: &mut ChainStore, + epoch_manager: &EpochManagerHandle, + chunk_hash: &ChunkHash, + shard_id: u64, + target_height: u64, + prev_hash: &CryptoHash, + prev_height_included: u64, + rng: Option, +) -> anyhow::Result> { + let mut receipt_proofs = vec![]; + + let chunk_hashes = chain_store.get_all_chunk_hashes_by_height(target_height)?; + if !chunk_hashes.contains(chunk_hash) { + return Err(anyhow!( + "given chunk hash is not listed in DBCol::ChunkHashesByHeight[{}]", + target_height + )); + } + + let mut chunks = + chunk_hashes.iter().map(|h| chain_store.get_chunk(h).unwrap()).collect::>(); + chunks.sort_by_key(|chunk| chunk.shard_id()); + + for chunk in chunks { + if let Ok(partial_encoded_chunk) = chain_store.get_partial_chunk(&chunk.chunk_hash()) { + for receipt in partial_encoded_chunk.receipts().iter() { + let ReceiptProof(_, shard_proof) = receipt; + if shard_proof.to_shard_id == shard_id { + receipt_proofs.push(receipt.clone()); + } + } + } else { + tracing::error!(target: "state-viewer", chunk_hash = ?chunk.chunk_hash(), "Failed to get a partial chunk"); + } + } + + if let Some(mut rng) = rng { + // for testing purposes, shuffle the receipts the same way it's done normally so we can compare the state roots + receipt_proofs.shuffle(&mut rng); + } + let mut responses = vec![ReceiptProofResponse(CryptoHash::default(), Arc::new(receipt_proofs))]; + responses.extend_from_slice(&chain_store.store_update().get_incoming_receipts_for_shard( + epoch_manager, + shard_id, + *prev_hash, + prev_height_included, + )?); + Ok(collect_receipts_from_response(&responses)) +} + +// returns (apply_result, gas limit) +pub(crate) fn apply_chunk( + epoch_manager: &EpochManagerHandle, + runtime: &dyn RuntimeAdapter, + chain_store: &mut ChainStore, + chunk_hash: ChunkHash, + target_height: Option, + rng: Option, + use_flat_storage: bool, +) -> anyhow::Result<(ApplyChunkResult, Gas)> { + let chunk = chain_store.get_chunk(&chunk_hash)?; + let chunk_header = chunk.cloned_header(); + + let prev_block_hash = chunk_header.prev_block_hash(); + let shard_id = chunk.shard_id(); + let prev_state_root = chunk.prev_state_root(); + + let transactions = chunk.transactions(); + let prev_block = + chain_store.get_block(prev_block_hash).context("Failed getting chunk's prev block")?; + let prev_height_included = prev_block.chunks()[shard_id as usize].height_included(); + let prev_height = prev_block.header().height(); + let target_height = match target_height { + Some(h) => h, + None => prev_height + 1, + }; + let prev_timestamp = prev_block.header().raw_timestamp(); + let gas_price = prev_block.header().next_gas_price(); + let receipts = get_incoming_receipts( + chain_store, + epoch_manager, + &chunk_hash, + shard_id, + target_height, + prev_block_hash, + prev_height_included, + rng, + ) + .context("Failed collecting incoming receipts")?; + + if use_flat_storage { + let shard_uid = + epoch_manager.shard_id_to_uid(shard_id as u64, prev_block.header().epoch_id()).unwrap(); + runtime.get_flat_storage_manager().create_flat_storage_for_shard(shard_uid).unwrap(); + } + + let is_first_block_with_chunk_of_version = check_if_block_is_first_with_chunk_of_version( + chain_store, + epoch_manager, + prev_block_hash, + shard_id, + )?; + + Ok(( + runtime.apply_chunk( + RuntimeStorageConfig::new(prev_state_root, use_flat_storage), + ApplyChunkShardContext { + shard_id, + last_validator_power_proposals: chunk_header.prev_validator_power_proposals(), + last_validator_frozen_proposals: chunk_header.prev_validator_frozen_proposals(), + gas_limit: chunk_header.gas_limit(), + is_first_block_with_chunk_of_version, + is_new_chunk: true, + }, + ApplyChunkBlockContext { + height: target_height, + block_timestamp: prev_timestamp + 1_000_000_000, + challenges_result: vec![], + prev_block_hash: *prev_block_hash, + block_hash: combine_hash( + prev_block_hash, + &hash("nonsense block hash for testing purposes".as_ref()), + ), + gas_price, + random_seed: hash("random seed".as_ref()), + }, + &receipts, + transactions, + )?, + chunk_header.gas_limit(), + )) +} + +enum HashType { + Tx, + Receipt, +} + +fn find_tx_or_receipt( + hash: &CryptoHash, + block_hash: &CryptoHash, + epoch_manager: &EpochManagerHandle, + chain_store: &ChainStore, +) -> anyhow::Result> { + let block = chain_store.get_block(block_hash)?; + let chunk_hashes = block.chunks().iter().map(|c| c.chunk_hash()).collect::>(); + + for (shard_id, chunk_hash) in chunk_hashes.iter().enumerate() { + let chunk = + chain_store.get_chunk(chunk_hash).context("Failed looking up canditate chunk")?; + for tx in chunk.transactions() { + if &tx.get_hash() == hash { + return Ok(Some((HashType::Tx, shard_id as ShardId))); + } + } + for receipt in chunk.prev_outgoing_receipts() { + if &receipt.get_hash() == hash { + let shard_layout = + epoch_manager.get_shard_layout_from_prev_block(chunk.prev_block())?; + let to_shard = + shard_layout::account_id_to_shard_id(&receipt.receiver_id, &shard_layout); + return Ok(Some((HashType::Receipt, to_shard))); + } + } + } + Ok(None) +} + +fn apply_tx_in_block( + epoch_manager: &EpochManagerHandle, + runtime: &dyn RuntimeAdapter, + chain_store: &mut ChainStore, + tx_hash: &CryptoHash, + block_hash: CryptoHash, + use_flat_storage: bool, +) -> anyhow::Result { + match find_tx_or_receipt(tx_hash, &block_hash, epoch_manager, chain_store)? { + Some((hash_type, shard_id)) => { + match hash_type { + HashType::Tx => { + println!("Found tx in block {} shard {}. equivalent command:\nview_state apply --height {} --shard-id {}\n", + &block_hash, shard_id, chain_store.get_block_header(&block_hash)?.height(), shard_id); + let (block, apply_result) = crate::commands::apply_block(block_hash, shard_id, epoch_manager, runtime, chain_store, use_flat_storage); + crate::commands::check_apply_block_result(&block, &apply_result, epoch_manager, chain_store, shard_id)?; + Ok(apply_result) + }, + HashType::Receipt => { + Err(anyhow!("{} appears to be a Receipt ID, not a tx hash. Try running:\nview_state apply_receipt --hash {}", tx_hash, tx_hash)) + }, + } + }, + None => { + Err(anyhow!("Could not find tx with hash {} in block {}, even though `DBCol::TransactionResultForBlock` says it should be there", tx_hash, block_hash)) + } + } +} + +fn apply_tx_in_chunk( + epoch_manager: &EpochManagerHandle, + runtime: &dyn RuntimeAdapter, + store: Store, + chain_store: &mut ChainStore, + tx_hash: &CryptoHash, + use_flat_storage: bool, +) -> anyhow::Result> { + if chain_store.get_transaction(tx_hash)?.is_none() { + return Err(anyhow!("tx with hash {} not known", tx_hash)); + } + + println!("Transaction is known but doesn't seem to have been applied. Searching in chunks that haven't been applied..."); + + let head = chain_store.head()?.height; + let mut chunk_hashes = vec![]; + + for item in store.iter(DBCol::ChunkHashesByHeight) { + let (k, v) = item.context("scanning ChunkHashesByHeight column")?; + let height = BlockHeight::from_le_bytes(k[..].try_into().unwrap()); + if height > head { + let hashes = HashSet::::try_from_slice(&v).unwrap(); + for chunk_hash in hashes { + let chunk = match chain_store.get_chunk(&chunk_hash) { + Ok(c) => c, + Err(_) => { + tracing::warn!(target: "state-viewer", "chunk hash {:?} appears in DBCol::ChunkHashesByHeight but the chunk is not saved", &chunk_hash); + continue; + } + }; + for hash in chunk.transactions().iter().map(|tx| tx.get_hash()) { + if hash == *tx_hash { + chunk_hashes.push(chunk_hash); + break; + } + } + } + } + } + + if chunk_hashes.is_empty() { + return Err(anyhow!( + "Could not find tx with hash {} in any chunk that hasn't been applied yet", + tx_hash + )); + } + + let mut results = Vec::new(); + for chunk_hash in chunk_hashes { + println!("found tx in chunk {}. Equivalent command (which will run faster than apply_tx):\nview_state apply_chunk --chunk_hash {}\n", &chunk_hash.0, &chunk_hash.0); + let (apply_result, gas_limit) = apply_chunk( + epoch_manager, + runtime, + chain_store, + chunk_hash, + None, + None, + use_flat_storage, + )?; + println!( + "resulting chunk extra:\n{:?}", + crate::commands::resulting_chunk_extra(&apply_result, gas_limit) + ); + results.push(apply_result); + } + Ok(results) +} + +pub(crate) fn apply_tx( + genesis_height: BlockHeight, + epoch_manager: &EpochManagerHandle, + runtime: &dyn RuntimeAdapter, + store: Store, + tx_hash: CryptoHash, + use_flat_storage: bool, +) -> anyhow::Result> { + let mut chain_store = ChainStore::new(store.clone(), genesis_height, false); + let outcomes = chain_store.get_outcomes_by_id(&tx_hash)?; + + if let Some(outcome) = outcomes.first() { + Ok(vec![apply_tx_in_block( + epoch_manager, + runtime, + &mut chain_store, + &tx_hash, + outcome.block_hash, + use_flat_storage, + )?]) + } else { + apply_tx_in_chunk( + epoch_manager, + runtime, + store, + &mut chain_store, + &tx_hash, + use_flat_storage, + ) + } +} + +fn apply_receipt_in_block( + epoch_manager: &EpochManagerHandle, + runtime: &dyn RuntimeAdapter, + chain_store: &mut ChainStore, + id: &CryptoHash, + block_hash: CryptoHash, + use_flat_storage: bool, +) -> anyhow::Result { + match find_tx_or_receipt(id, &block_hash, epoch_manager, chain_store)? { + Some((hash_type, shard_id)) => { + match hash_type { + HashType::Tx => { + Err(anyhow!("{} appears to be a tx hash, not a Receipt ID. Try running:\nview_state apply_tx --hash {}", id, id)) + }, + HashType::Receipt => { + println!("Found receipt in block {}. Receiver is in shard {}. equivalent command:\nview_state apply --height {} --shard-id {}\n", + &block_hash, shard_id, chain_store.get_block_header(&block_hash)?.height(), shard_id); + let (block, apply_result) = crate::commands::apply_block(block_hash, shard_id, epoch_manager, runtime, chain_store, use_flat_storage); + crate::commands::check_apply_block_result(&block, &apply_result, epoch_manager, chain_store, shard_id)?; + Ok(apply_result) + }, + } + }, + None => { + // TODO: handle local/delayed receipts + Err(anyhow!("Could not find receipt with ID {} in block {}. Is it a local or delayed receipt?", id, block_hash)) + } + } +} + +fn apply_receipt_in_chunk( + epoch_manager: &EpochManagerHandle, + runtime: &dyn RuntimeAdapter, + store: Store, + chain_store: &mut ChainStore, + id: &CryptoHash, + use_flat_storage: bool, +) -> anyhow::Result> { + if chain_store.get_receipt(id)?.is_none() { + // TODO: handle local/delayed receipts + return Err(anyhow!("receipt with ID {} not known. Is it a local or delayed receipt?", id)); + } + + println!( + "Receipt is known but doesn't seem to have been applied. Searching in chunks that haven't been applied..." + ); + + let head = chain_store.head()?.height; + let mut to_apply = HashSet::new(); + let mut non_applied_chunks = HashMap::new(); + + for item in store.iter(DBCol::ChunkHashesByHeight) { + let (k, v) = item.context("scanning ChunkHashesByHeight column")?; + let height = BlockHeight::from_le_bytes(k[..].try_into().unwrap()); + if height > head { + let hashes = HashSet::::try_from_slice(&v).unwrap(); + for chunk_hash in hashes { + let chunk = match chain_store.get_chunk(&chunk_hash) { + Ok(c) => c, + Err(_) => { + tracing::warn!(target: "state-viewer", "chunk hash {:?} appears in DBCol::ChunkHashesByHeight but the chunk is not saved", &chunk_hash); + continue; + } + }; + non_applied_chunks.insert((height, chunk.shard_id()), chunk_hash.clone()); + + for receipt in chunk.prev_outgoing_receipts().iter() { + if receipt.get_hash() == *id { + let shard_layout = + epoch_manager.get_shard_layout_from_prev_block(chunk.prev_block())?; + let to_shard = shard_layout::account_id_to_shard_id( + &receipt.receiver_id, + &shard_layout, + ); + to_apply.insert((height, to_shard)); + println!( + "found receipt in chunk {}. Receiver is in shard {}", + &chunk_hash.0, to_shard + ); + break; + } + } + } + } + } + + if to_apply.is_empty() { + return Err(anyhow!( + "Could not find receipt with hash {} in any chunk that hasn't been applied yet", + id + )); + } + + let mut results = Vec::new(); + for (height, shard_id) in to_apply { + let chunk_hash = match non_applied_chunks.get(&(height, shard_id)) { + Some(h) => h, + None => { + eprintln!( + "Wanted to apply chunk in shard {} at height {}, but no such chunk was found.", + shard_id, height, + ); + continue; + } + }; + println!("Applying chunk at height {} in shard {}. Equivalent command (which will run faster than apply_receipt):\nview_state apply_chunk --chunk_hash {}\n", + height, shard_id, chunk_hash.0); + let (apply_result, gas_limit) = apply_chunk( + epoch_manager, + runtime, + chain_store, + chunk_hash.clone(), + None, + None, + use_flat_storage, + )?; + let chunk_extra = crate::commands::resulting_chunk_extra(&apply_result, gas_limit); + println!("resulting chunk extra:\n{:?}", chunk_extra); + results.push(apply_result); + } + Ok(results) +} + +pub(crate) fn apply_receipt( + genesis_height: BlockHeight, + epoch_manager: &EpochManagerHandle, + runtime: &dyn RuntimeAdapter, + store: Store, + id: CryptoHash, + use_flat_storage: bool, +) -> anyhow::Result> { + let mut chain_store = ChainStore::new(store.clone(), genesis_height, false); + let outcomes = chain_store.get_outcomes_by_id(&id)?; + if let Some(outcome) = outcomes.first() { + Ok(vec![apply_receipt_in_block( + epoch_manager, + runtime, + &mut chain_store, + &id, + outcome.block_hash, + use_flat_storage, + )?]) + } else { + apply_receipt_in_chunk( + epoch_manager, + runtime, + store, + &mut chain_store, + &id, + use_flat_storage, + ) + } +} + +#[cfg(test)] +mod test { + use unc_chain::{ChainGenesis, ChainStore, ChainStoreAccess, Provenance}; + use unc_chain_configs::Genesis; + use unc_client::test_utils::TestEnv; + use unc_client::ProcessTxResponse; + use unc_crypto::{InMemorySigner, KeyType}; + use unc_epoch_manager::{EpochManager, EpochManagerAdapter}; + use unc_primitives::hash::CryptoHash; + use unc_primitives::shard_layout; + use unc_primitives::transaction::SignedTransaction; + use unc_primitives::utils::get_num_seats_per_shard; + use unc_store::genesis::initialize_genesis_state; + use unc_store::test_utils::create_test_store; + use framework::config::GenesisExt; + use framework::NightshadeRuntime; + use rand::rngs::StdRng; + use rand::SeedableRng; + use std::path::Path; + + fn send_txs(env: &mut TestEnv, signers: &[InMemorySigner], height: u64, hash: CryptoHash) { + for (i, signer) in signers.iter().enumerate() { + let from = format!("test{}", i); + let to = format!("test{}", (i + 1) % signers.len()); + let tx = SignedTransaction::send_money( + height, + from.parse().unwrap(), + to.parse().unwrap(), + signer, + 100, + hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + } + } + + #[test] + fn test_apply_chunk() { + let genesis = Genesis::test_sharded( + vec![ + "test0".parse().unwrap(), + "test1".parse().unwrap(), + "test2".parse().unwrap(), + "test3".parse().unwrap(), + ], + 1, + get_num_seats_per_shard(4, 1), + ); + + let store = create_test_store(); + let mut chain_store = ChainStore::new(store.clone(), genesis.config.genesis_height, false); + + initialize_genesis_state(store.clone(), &genesis, None); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = NightshadeRuntime::test( + Path::new("."), + store.clone(), + &genesis.config, + epoch_manager.clone(), + ); + let chain_genesis = ChainGenesis::test(); + + let signers = (0..4) + .map(|i| { + let acc = format!("test{}", i); + InMemorySigner::from_seed(acc.parse().unwrap(), KeyType::ED25519, &acc) + }) + .collect::>(); + + let mut env = TestEnv::builder(chain_genesis) + .stores(vec![store]) + .epoch_managers(vec![epoch_manager.clone()]) + .track_all_shards() + .runtimes(vec![runtime.clone()]) + .build(); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + + for height in 1..10 { + send_txs(&mut env, &signers, height, genesis_hash); + + let block = env.clients[0].produce_block(height).unwrap().unwrap(); + + let hash = *block.hash(); + let chunk_hashes = block.chunks().iter().map(|c| c.chunk_hash()).collect::>(); + let epoch_id = block.header().epoch_id().clone(); + + env.process_block(0, block, Provenance::PRODUCED); + + let new_roots = (0..4) + .map(|i| { + let shard_uid = epoch_manager.shard_id_to_uid(i, &epoch_id).unwrap(); + *chain_store.get_chunk_extra(&hash, &shard_uid).unwrap().state_root() + }) + .collect::>(); + + if height >= 2 { + for shard in 0..4 { + // we will shuffle receipts the same as in production, otherwise the state roots don't match + let mut slice = [0u8; 32]; + slice.copy_from_slice(hash.as_ref()); + let rng: StdRng = SeedableRng::from_seed(slice); + + let chunk_hash = &chunk_hashes[shard]; + let new_root = new_roots[shard]; + + let (apply_result, _) = crate::apply_chunk::apply_chunk( + epoch_manager.as_ref(), + runtime.as_ref(), + &mut chain_store, + chunk_hash.clone(), + None, + Some(rng), + false, + ) + .unwrap(); + assert_eq!(apply_result.new_root, new_root); + } + } + } + } + + #[test] + fn test_apply_tx_apply_receipt() { + let genesis = Genesis::test_sharded( + vec![ + "test0".parse().unwrap(), + "test1".parse().unwrap(), + "test2".parse().unwrap(), + "test3".parse().unwrap(), + ], + 1, + get_num_seats_per_shard(4, 1), + ); + + let store = create_test_store(); + let chain_store = ChainStore::new(store.clone(), genesis.config.genesis_height, false); + + initialize_genesis_state(store.clone(), &genesis, None); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = NightshadeRuntime::test( + Path::new("."), + store.clone(), + &genesis.config, + epoch_manager.clone(), + ); + let mut chain_genesis = ChainGenesis::test(); + // receipts get delayed with the small ChainGenesis::test() limit + chain_genesis.gas_limit = genesis.config.gas_limit; + + let signers = (0..4) + .map(|i| { + let acc = format!("test{}", i); + InMemorySigner::from_seed(acc.parse().unwrap(), KeyType::ED25519, &acc) + }) + .collect::>(); + + let mut env = TestEnv::builder(chain_genesis) + .stores(vec![store.clone()]) + .epoch_managers(vec![epoch_manager.clone()]) + .track_all_shards() + .runtimes(vec![runtime.clone()]) + .build(); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + + // first check that applying txs and receipts works when the block exists + + for height in 1..5 { + send_txs(&mut env, &signers, height, genesis_hash); + + let block = env.clients[0].produce_block(height).unwrap().unwrap(); + + let hash = *block.hash(); + let prev_hash = *block.header().prev_hash(); + let chunk_hashes = block.chunks().iter().map(|c| c.chunk_hash()).collect::>(); + let epoch_id = block.header().epoch_id().clone(); + + env.process_block(0, block, Provenance::PRODUCED); + + let new_roots = (0..4) + .map(|i| { + let shard_uid = epoch_manager.shard_id_to_uid(i, &epoch_id).unwrap(); + *chain_store.get_chunk_extra(&hash, &shard_uid).unwrap().state_root() + }) + .collect::>(); + let shard_layout = epoch_manager.get_shard_layout_from_prev_block(&prev_hash).unwrap(); + + if height >= 2 { + for shard_id in 0..4 { + let chunk = chain_store.get_chunk(&chunk_hashes[shard_id]).unwrap(); + + for tx in chunk.transactions() { + let results = crate::apply_chunk::apply_tx( + genesis.config.genesis_height, + &epoch_manager, + runtime.as_ref(), + store.clone(), + tx.get_hash(), + false, + ) + .unwrap(); + assert_eq!(results.len(), 1); + assert_eq!(results[0].new_root, new_roots[shard_id as usize]); + } + + for receipt in chunk.prev_outgoing_receipts() { + let to_shard = shard_layout::account_id_to_shard_id( + &receipt.receiver_id, + &shard_layout, + ); + + let results = crate::apply_chunk::apply_receipt( + genesis.config.genesis_height, + &epoch_manager, + runtime.as_ref(), + store.clone(), + receipt.get_hash(), + false, + ) + .unwrap(); + assert_eq!(results.len(), 1); + assert_eq!(results[0].new_root, new_roots[to_shard as usize]); + } + } + } + } + + // then check what happens when the block doesn't exist + // it won't exist because the chunks for the last height + // in the loop above are produced by env.process_block() but + // there was no corresponding env.clients[0].produce_block() after + + let chunks = chain_store.get_all_chunk_hashes_by_height(5).unwrap(); + let blocks = chain_store.get_all_header_hashes_by_height(5).unwrap(); + assert_ne!(chunks.len(), 0); + assert_eq!(blocks.len(), 0); + + for chunk_hash in chunks { + let chunk = chain_store.get_chunk(&chunk_hash).unwrap(); + + for tx in chunk.transactions() { + let results = crate::apply_chunk::apply_tx( + genesis.config.genesis_height, + &epoch_manager, + runtime.as_ref(), + store.clone(), + tx.get_hash(), + false, + ) + .unwrap(); + for result in results { + let mut applied = false; + for outcome in result.outcomes { + if outcome.id == tx.get_hash() { + applied = true; + break; + } + } + assert!(applied); + } + } + for receipt in chunk.prev_outgoing_receipts() { + let results = crate::apply_chunk::apply_receipt( + genesis.config.genesis_height, + &epoch_manager, + runtime.as_ref(), + store.clone(), + receipt.get_hash(), + false, + ) + .unwrap(); + for result in results { + let mut applied = false; + for outcome in result.outcomes { + if outcome.id == receipt.get_hash() { + applied = true; + break; + } + } + assert!(applied); + } + } + } + } +} diff --git a/tools/state-viewer/src/cli.rs b/tools/state-viewer/src/cli.rs new file mode 100644 index 000000000..ec63ba50b --- /dev/null +++ b/tools/state-viewer/src/cli.rs @@ -0,0 +1,796 @@ +use crate::commands::*; +use crate::contract_accounts::ContractAccountFilter; +use crate::rocksdb_stats::get_rocksdb_stats; +use crate::trie_iteration_benchmark::TrieIterationBenchmarkCmd; + +use unc_chain_configs::{GenesisChangeConfig, GenesisValidationMode}; +use unc_primitives::account::id::AccountId; +use unc_primitives::hash::CryptoHash; +use unc_primitives::sharding::ChunkHash; +use unc_primitives::trie_key::col; +use unc_primitives::types::{BlockHeight, ShardId}; +use unc_store::{Mode, NodeStorage, Store, Temperature}; +use framework::{load_config, UncConfig}; +use std::path::{Path, PathBuf}; +use std::str::FromStr; + +#[derive(clap::Subcommand)] +#[clap(subcommand_required = true, arg_required_else_help = true)] +pub enum StateViewerSubCommand { + /// Apply block at some height for shard. + Apply(ApplyCmd), + /// Apply a chunk, even if it's not included in any block on disk + #[clap(alias = "apply_chunk")] + ApplyChunk(ApplyChunkCmd), + /// Apply blocks at a range of heights for a single shard. + #[clap(alias = "apply_range")] + ApplyRange(ApplyRangeCmd), + /// Apply a receipt if it occurs in some chunk we know about, + /// even if it's not included in any block on disk + #[clap(alias = "apply_receipt")] + ApplyReceipt(ApplyReceiptCmd), + /// Apply a transaction if it occurs in some chunk we know about, + /// even if it's not included in any block on disk + #[clap(alias = "apply_tx")] + ApplyTx(ApplyTxCmd), + /// Print chain from start_index to end_index. + Chain(ChainCmd), + /// Check whether the node has all the blocks up to its head. + #[clap(alias = "check_block")] + CheckBlock, + /// Looks up a certain chunk. + Chunks(ChunksCmd), + /// Clear recoverable data in CachedContractCode column. + #[clap(alias = "clear_cache")] + ClearCache, + /// List account names with contracts deployed. + #[clap(alias = "contract_accounts")] + ContractAccounts(ContractAccountsCmd), + /// Dump contract data in storage of given account to binary file. + #[clap(alias = "dump_account_storage")] + DumpAccountStorage(DumpAccountStorageCmd), + /// Dump deployed contract code of given account to wasm file. + #[clap(alias = "dump_code")] + DumpCode(DumpCodeCmd), + /// Generate a genesis file from the current state of the DB. + #[clap(alias = "dump_state")] + DumpState(DumpStateCmd), + /// Writes state to a remote redis server. + #[clap(alias = "dump_state_redis")] + DumpStateRedis(DumpStateRedisCmd), + /// Generate a file that contains all transactions from a block. + #[clap(alias = "dump_tx")] + DumpTx(DumpTxCmd), + /// Print `EpochInfo` of an epoch given by `--epoch_id` or by `--epoch_height`. + #[clap(alias = "epoch_info")] + EpochInfo(EpochInfoCmd), + /// Looks up a certain partial chunk. + #[clap(alias = "partial_chunks")] + PartialChunks(PartialChunksCmd), + /// Looks up a certain receipt. + Receipts(ReceiptsCmd), + /// Replay headers from chain. + Replay(ReplayCmd), + /// Dump stats for the RocksDB storage. + #[clap(name = "rocksdb-stats", alias = "rocksdb_stats")] + RocksDBStats(RocksDBStatsCmd), + /// Reads all rows of a DB column and deserializes keys and values and prints them. + ScanDbColumn(ScanDbColumnCmd), + /// Iterates over a trie and prints the StateRecords. + State, + /// Dumps or applies StateChanges. + /// Experimental tool for shard shadowing development. + StateChanges(StateChangesCmd), + /// Dump or apply state parts. + StateParts(StatePartsCmd), + /// Iterates over the Flat State and prints some statistics. + /// e.g. large accounts, total, average and median size, middle account + StateStats(StateStatsCmd), + /// Benchmark how long does it take to iterate the trie. + TrieIterationBenchmark(TrieIterationBenchmarkCmd), + /// View head of the storage. + #[clap(alias = "view_chain")] + ViewChain(ViewChainCmd), + /// View trie structure. + #[clap(alias = "view_trie")] + ViewTrie(ViewTrieCmd), +} + +impl StateViewerSubCommand { + pub fn run( + self, + home_dir: &Path, + genesis_validation: GenesisValidationMode, + mode: Mode, + temperature: Temperature, + ) { + let unc_config = load_config(home_dir, genesis_validation) + .unwrap_or_else(|e| panic!("Error loading config: {:#}", e)); + + let cold_config: Option<&unc_store::StoreConfig> = unc_config.config.cold_store.as_ref(); + let store_opener = NodeStorage::opener( + home_dir, + unc_config.config.archive, + &unc_config.config.store, + cold_config, + ); + + let storage = store_opener.open_in_mode(mode).unwrap(); + let store = match temperature { + Temperature::Hot => storage.get_hot_store(), + // Cold store on it's own is useless in majority of subcommands + Temperature::Cold => storage.get_split_store().unwrap(), + }; + + match self { + StateViewerSubCommand::Apply(cmd) => cmd.run(home_dir, unc_config, store), + StateViewerSubCommand::ApplyChunk(cmd) => cmd.run(home_dir, unc_config, store), + StateViewerSubCommand::ApplyRange(cmd) => cmd.run(home_dir, unc_config, store), + StateViewerSubCommand::ApplyReceipt(cmd) => cmd.run(home_dir, unc_config, store), + StateViewerSubCommand::ApplyTx(cmd) => cmd.run(home_dir, unc_config, store), + StateViewerSubCommand::Chain(cmd) => cmd.run(unc_config, store), + StateViewerSubCommand::CheckBlock => check_block_chunk_existence(unc_config, store), + StateViewerSubCommand::Chunks(cmd) => cmd.run(unc_config, store), + StateViewerSubCommand::ClearCache => clear_cache(store), + StateViewerSubCommand::ContractAccounts(cmd) => cmd.run(home_dir, unc_config, store), + StateViewerSubCommand::DumpAccountStorage(cmd) => cmd.run(home_dir, unc_config, store), + StateViewerSubCommand::DumpCode(cmd) => cmd.run(home_dir, unc_config, store), + StateViewerSubCommand::DumpState(cmd) => cmd.run(home_dir, unc_config, store), + StateViewerSubCommand::DumpStateRedis(cmd) => cmd.run(home_dir, unc_config, store), + StateViewerSubCommand::DumpTx(cmd) => cmd.run(home_dir, unc_config, store), + StateViewerSubCommand::EpochInfo(cmd) => cmd.run(unc_config, store), + StateViewerSubCommand::PartialChunks(cmd) => cmd.run(unc_config, store), + StateViewerSubCommand::Receipts(cmd) => cmd.run(unc_config, store), + StateViewerSubCommand::Replay(cmd) => cmd.run(unc_config, store), + StateViewerSubCommand::RocksDBStats(cmd) => cmd.run(store_opener.path()), + StateViewerSubCommand::ScanDbColumn(cmd) => cmd.run(store), + StateViewerSubCommand::State => state(home_dir, unc_config, store), + StateViewerSubCommand::StateChanges(cmd) => cmd.run(home_dir, unc_config, store), + StateViewerSubCommand::StateParts(cmd) => cmd.run(home_dir, unc_config, store), + StateViewerSubCommand::StateStats(cmd) => cmd.run(home_dir, unc_config, store), + StateViewerSubCommand::ViewChain(cmd) => cmd.run(unc_config, store), + StateViewerSubCommand::ViewTrie(cmd) => cmd.run(store), + StateViewerSubCommand::TrieIterationBenchmark(cmd) => cmd.run(unc_config, store), + } + } +} + +#[derive(clap::Parser)] +pub struct ApplyCmd { + #[clap(long)] + height: BlockHeight, + #[clap(long)] + shard_id: ShardId, + #[clap(long)] + use_flat_storage: bool, +} + +impl ApplyCmd { + pub fn run(self, home_dir: &Path, unc_config: UncConfig, store: Store) { + apply_block_at_height( + self.height, + self.shard_id, + self.use_flat_storage, + home_dir, + unc_config, + store, + ) + .unwrap(); + } +} + +#[derive(clap::Parser)] +pub struct ApplyChunkCmd { + #[clap(long)] + chunk_hash: String, + #[clap(long)] + target_height: Option, + #[clap(long)] + use_flat_storage: bool, +} + +impl ApplyChunkCmd { + pub fn run(self, home_dir: &Path, unc_config: UncConfig, store: Store) { + let hash = ChunkHash::from(CryptoHash::from_str(&self.chunk_hash).unwrap()); + apply_chunk(home_dir, unc_config, store, hash, self.target_height, self.use_flat_storage) + .unwrap() + } +} + +#[derive(clap::Parser)] +pub struct ApplyRangeCmd { + #[clap(long)] + start_index: Option, + #[clap(long)] + end_index: Option, + #[clap(long, default_value = "0")] + shard_id: ShardId, + #[clap(long)] + verbose_output: bool, + #[clap(long, value_parser)] + csv_file: Option, + #[clap(long)] + only_contracts: bool, + #[clap(long)] + sequential: bool, + #[clap(long)] + use_flat_storage: bool, +} + +impl ApplyRangeCmd { + pub fn run(self, home_dir: &Path, unc_config: UncConfig, store: Store) { + apply_range( + self.start_index, + self.end_index, + self.shard_id, + self.verbose_output, + self.csv_file, + home_dir, + unc_config, + store, + self.only_contracts, + self.sequential, + self.use_flat_storage, + ); + } +} + +#[derive(clap::Parser)] +pub struct ApplyReceiptCmd { + #[clap(long)] + hash: String, + #[clap(long)] + use_flat_storage: bool, +} + +impl ApplyReceiptCmd { + pub fn run(self, home_dir: &Path, unc_config: UncConfig, store: Store) { + let hash = CryptoHash::from_str(&self.hash).unwrap(); + apply_receipt(home_dir, unc_config, store, hash, self.use_flat_storage).unwrap(); + } +} + +#[derive(clap::Parser)] +pub struct ApplyTxCmd { + #[clap(long)] + hash: String, + #[clap(long)] + use_flat_storage: bool, +} + +impl ApplyTxCmd { + pub fn run(self, home_dir: &Path, unc_config: UncConfig, store: Store) { + let hash = CryptoHash::from_str(&self.hash).unwrap(); + apply_tx(home_dir, unc_config, store, hash, self.use_flat_storage).unwrap(); + } +} + +#[derive(clap::Parser)] +pub struct ChainCmd { + #[clap(long)] + start_index: BlockHeight, + #[clap(long)] + end_index: BlockHeight, + // If true, show the full hash (block hash and chunk hash) when printing. + // If false, show only first couple chars. + #[clap(long)] + show_full_hashes: bool, +} + +impl ChainCmd { + pub fn run(self, unc_config: UncConfig, store: Store) { + print_chain(self.start_index, self.end_index, unc_config, store, self.show_full_hashes); + } +} + +#[derive(clap::Parser)] +pub struct ChunksCmd { + #[clap(long)] + chunk_hash: String, +} + +impl ChunksCmd { + pub fn run(self, unc_config: UncConfig, store: Store) { + let chunk_hash = ChunkHash::from(CryptoHash::from_str(&self.chunk_hash).unwrap()); + get_chunk(chunk_hash, unc_config, store) + } +} + +#[derive(clap::Parser)] +pub struct ContractAccountsCmd { + #[clap(flatten)] + filter: ContractAccountFilter, +} + +impl ContractAccountsCmd { + pub fn run(self, home_dir: &Path, unc_config: UncConfig, store: Store) { + contract_accounts(home_dir, store, unc_config, self.filter).unwrap(); + } +} + +#[derive(clap::Parser)] +pub struct DumpAccountStorageCmd { + #[clap(long)] + account_id: String, + #[clap(long)] + storage_key: String, + #[clap(long, value_parser)] + output: PathBuf, + #[clap(long)] + block_height: String, +} + +impl DumpAccountStorageCmd { + pub fn run(self, home_dir: &Path, unc_config: UncConfig, store: Store) { + dump_account_storage( + self.account_id, + self.storage_key, + &self.output, + self.block_height, + home_dir, + unc_config, + store, + ); + } +} + +#[derive(clap::Parser)] +pub struct DumpCodeCmd { + #[clap(long)] + account_id: String, + #[clap(long, value_parser)] + output: PathBuf, +} + +impl DumpCodeCmd { + pub fn run(self, home_dir: &Path, unc_config: UncConfig, store: Store) { + dump_code(self.account_id, &self.output, home_dir, unc_config, store); + } +} + +#[derive(clap::Parser)] +pub struct DumpStateCmd { + /// Optionally, can specify at which height to dump state. + #[clap(long)] + height: Option, + /// Dumps state records and genesis config into separate files. + /// Has reasonable RAM requirements. + /// Use for chains with large state, such as mainnet and testnet. + /// If false - writes all information into a single file, which is useful for smaller networks, + /// such as betanet. + #[clap(long)] + stream: bool, + /// Location of the dumped state. + /// This is a directory if --stream is set, and a file otherwise. + #[clap(long, value_parser)] + file: Option, + /// List of account IDs to dump. + /// Note: validators will always be dumped. + /// If not set, all account IDs will be dumped. + #[clap(long)] + account_ids: Option>, + /// List of validators to remain validators. + /// All other validators will be kicked, but still dumped. + /// Their stake will be returned to balance. + #[clap(long)] + include_validators: Option>, +} + +impl DumpStateCmd { + pub fn run(self, home_dir: &Path, unc_config: UncConfig, store: Store) { + dump_state( + self.height, + self.stream, + self.file, + home_dir, + unc_config, + store, + &GenesisChangeConfig::default() + .with_select_account_ids(self.account_ids) + .with_whitelist_validators(self.include_validators), + ); + } +} + +#[derive(clap::Parser)] +pub struct DumpStateRedisCmd { + /// Optionally, can specify at which height to dump state. + #[clap(long)] + height: Option, +} + +impl DumpStateRedisCmd { + pub fn run(self, home_dir: &Path, unc_config: UncConfig, store: Store) { + dump_state_redis(self.height, home_dir, unc_config, store); + } +} + +#[derive(clap::Parser)] +pub struct DumpTxCmd { + /// Specify the start block by height to begin dumping transactions from, inclusive. + #[clap(long)] + start_height: BlockHeight, + /// Specify the end block by height to stop dumping transactions at, inclusive. + #[clap(long)] + end_height: BlockHeight, + /// List of account IDs to dump. + /// If not set, all account IDs will be dumped. + #[clap(long)] + account_ids: Option>, + /// Optionally, can specify the path of the output. + #[clap(long)] + output_path: Option, +} + +impl DumpTxCmd { + pub fn run(self, home_dir: &Path, unc_config: UncConfig, store: Store) { + dump_tx( + self.start_height, + self.end_height, + home_dir, + unc_config, + store, + self.account_ids.as_deref(), + self.output_path, + ) + .expect("Failed to dump transaction...") + } +} + +#[derive(clap::Args)] +pub struct EpochInfoCmd { + /// Which EpochInfos to process. + #[clap(subcommand)] + epoch_selection: crate::epoch_info::EpochSelection, + /// Displays kickouts of the given validator and expected and missed blocks and chunks produced. + #[clap(long)] + validator_account_id: Option, + /// Show only information about kickouts. + #[clap(long)] + kickouts_summary: bool, +} + +impl EpochInfoCmd { + pub fn run(self, unc_config: UncConfig, store: Store) { + print_epoch_info( + self.epoch_selection, + self.validator_account_id.map(|s| AccountId::from_str(&s).unwrap()), + self.kickouts_summary, + unc_config, + store, + ); + } +} + +#[derive(clap::Parser)] +pub struct PartialChunksCmd { + #[clap(long)] + partial_chunk_hash: String, +} + +impl PartialChunksCmd { + pub fn run(self, unc_config: UncConfig, store: Store) { + let partial_chunk_hash = + ChunkHash::from(CryptoHash::from_str(&self.partial_chunk_hash).unwrap()); + get_partial_chunk(partial_chunk_hash, unc_config, store) + } +} + +#[derive(clap::Parser)] +pub struct ReceiptsCmd { + #[clap(long)] + receipt_id: String, +} + +impl ReceiptsCmd { + pub fn run(self, unc_config: UncConfig, store: Store) { + get_receipt(CryptoHash::from_str(&self.receipt_id).unwrap(), unc_config, store) + } +} + +#[derive(clap::Parser)] +pub struct ReplayCmd { + #[clap(long)] + start_index: BlockHeight, + #[clap(long)] + end_index: BlockHeight, +} + +impl ReplayCmd { + pub fn run(self, unc_config: UncConfig, store: Store) { + replay_chain(self.start_index, self.end_index, unc_config, store); + } +} + +#[derive(clap::Parser)] +pub struct RocksDBStatsCmd { + /// Location of the dumped Rocks DB stats. + #[clap(long, value_parser)] + file: Option, +} + +impl RocksDBStatsCmd { + pub fn run(self, store_dir: &Path) { + get_rocksdb_stats(store_dir, self.file).expect("Couldn't get RocksDB stats"); + } +} + +#[derive(clap::Parser, Debug)] +pub struct ScanDbColumnCmd { + /// Column name, e.g. 'Block' or 'BlockHeader'. + #[clap(long)] + column: String, + #[clap(long)] + from: Option, + // List of comma-separated u8-values. + #[clap(long)] + from_bytes: Option, + #[clap(long)] + from_hash: Option, + #[clap(long)] + to: Option, + // List of comma-separated u8-values. + // For example, if a column key starts wth ShardUId and you want to scan starting from s2.v1 use `--from-bytes 1,0,0,0,2,0,0,0`. + // Note that the numbers are generally saved as low-endian. + #[clap(long)] + to_bytes: Option, + #[clap(long)] + to_hash: Option, + #[clap(long)] + max_keys: Option, + #[clap(long, default_value = "false")] + no_value: bool, +} + +impl ScanDbColumnCmd { + pub fn run(self, store: Store) { + let lower_bound = Self::prefix(self.from, self.from_bytes, self.from_hash); + let upper_bound = Self::prefix(self.to, self.to_bytes, self.to_hash); + crate::scan_db::scan_db_column( + &self.column, + lower_bound.as_deref().map(|v| v.as_ref()), + upper_bound.as_deref().map(|v| v.as_ref()), + self.max_keys, + self.no_value, + store, + ) + } + + fn prefix( + s: Option, + bytes: Option, + hash: Option, + ) -> Option> { + match (s, bytes, hash) { + (None, None, None) => None, + (Some(s), None, None) => Some(s.into_bytes()), + (None, Some(bytes), None) => { + Some(bytes.split(",").map(|s| s.parse::().unwrap()).collect::>()) + } + (None, None, Some(hash)) => Some(borsh::to_vec(&hash).unwrap()), + _ => panic!("Need to provide exactly one of bytes, str, or hash"), + } + } +} + +#[derive(clap::Parser)] +pub struct StateChangesCmd { + #[clap(subcommand)] + command: crate::state_changes::StateChangesSubCommand, +} + +impl StateChangesCmd { + pub fn run(self, home_dir: &Path, unc_config: UncConfig, store: Store) { + self.command.run(home_dir, unc_config, store) + } +} + +#[derive(clap::Parser)] +pub struct StatePartsCmd { + /// Shard id. + #[clap(long)] + shard_id: ShardId, + /// Location of serialized state parts. + #[clap(long)] + root_dir: Option, + /// Store state parts in an S3 bucket. + #[clap(long)] + s3_bucket: Option, + /// Store state parts in an S3 bucket. + #[clap(long)] + s3_region: Option, + /// Store state parts in an GCS bucket. + #[clap(long)] + gcs_bucket: Option, + /// Dump or Apply state parts. + #[clap(subcommand)] + command: crate::state_parts::StatePartsSubCommand, +} + +impl StatePartsCmd { + pub fn run(self, home_dir: &Path, unc_config: UncConfig, store: Store) { + self.command.run( + self.shard_id, + self.root_dir, + self.s3_bucket, + self.s3_region, + self.gcs_bucket, + home_dir, + unc_config, + store, + ); + } +} + +#[derive(clap::Parser)] +pub struct StateStatsCmd {} + +impl StateStatsCmd { + pub fn run(self, home_dir: &Path, unc_config: UncConfig, store: Store) { + print_state_stats(home_dir, store, unc_config); + } +} + +#[derive(clap::Parser)] +pub struct ViewChainCmd { + #[clap(long)] + height: Option, + #[clap(long)] + block: bool, + #[clap(long)] + chunk: bool, +} + +impl ViewChainCmd { + pub fn run(self, unc_config: UncConfig, store: Store) { + view_chain(self.height, self.block, self.chunk, unc_config, store); + } +} + +#[derive(Clone)] +pub enum ViewTrieFormat { + Full, + Pretty, +} + +impl clap::ValueEnum for ViewTrieFormat { + fn value_variants<'a>() -> &'a [Self] { + &[Self::Full, Self::Pretty] + } + + fn to_possible_value(&self) -> Option { + match self { + Self::Full => Some(clap::builder::PossibleValue::new("full")), + Self::Pretty => Some(clap::builder::PossibleValue::new("pretty")), + } + } +} + +/// Possible record types in a state trie. +#[derive(Clone)] +#[repr(u8)] +pub enum RecordType { + Account = col::ACCOUNT, + ContractCode = col::CONTRACT_CODE, + AccessKey = col::ACCESS_KEY, + ReceivedData = col::RECEIVED_DATA, + PostponedReceiptId = col::POSTPONED_RECEIPT_ID, + PendingDataCount = col::PENDING_DATA_COUNT, + PostponedReceipt = col::POSTPONED_RECEIPT, + DelayedReceiptOrIndices = col::DELAYED_RECEIPT_OR_INDICES, + ContractData = col::CONTRACT_DATA, +} + +impl clap::ValueEnum for RecordType { + fn value_variants<'a>() -> &'a [Self] { + &[ + Self::Account, + Self::ContractCode, + Self::AccessKey, + Self::ReceivedData, + Self::PostponedReceiptId, + Self::PendingDataCount, + Self::PostponedReceipt, + Self::DelayedReceiptOrIndices, + Self::ContractData, + ] + } + + fn to_possible_value(&self) -> Option { + match self { + Self::Account => Some(clap::builder::PossibleValue::new("account")), + Self::ContractCode => Some(clap::builder::PossibleValue::new("contract-code")), + Self::AccessKey => Some(clap::builder::PossibleValue::new("access-key")), + Self::ReceivedData => Some(clap::builder::PossibleValue::new("received-data")), + Self::PostponedReceiptId => { + Some(clap::builder::PossibleValue::new("postponed-receipt-id")) + } + Self::PendingDataCount => Some(clap::builder::PossibleValue::new("pending-data-count")), + Self::PostponedReceipt => Some(clap::builder::PossibleValue::new("postponed-receipt")), + Self::DelayedReceiptOrIndices => { + Some(clap::builder::PossibleValue::new("delayed-receipt-or-indices")) + } + Self::ContractData => Some(clap::builder::PossibleValue::new("contract-data")), + } + } +} + +#[derive(clap::Parser)] +pub struct ViewTrieCmd { + /// The format of the output. This can be either `full` or `pretty`. + /// The full format will print all the trie nodes and can be rooted anywhere in the trie. + /// The pretty format will only print leaf nodes and must be rooted in the state root but is more human friendly. + #[clap(long, default_value = "pretty")] + format: ViewTrieFormat, + /// The hash of the trie node. + /// For format=full this can be any node in the trie. + /// For format=pretty this must the state root node. + /// You can find the state root hash using the `view-state view-chain` command. + #[clap(long)] + hash: String, + /// The id of the shard, a number between [0-NUM_SHARDS). When looking for particular + /// account you will need to know on which shard it's located. + #[clap(long)] + shard_id: u32, + /// The current shard version based on the shard layout. + /// You can find the shard version by using the `view-state view-chain` command. + /// It's typically equal to 0 for single shard localnet or the most recent unc_primitives::shard_layout::ShardLayout for prod. + #[clap(long)] + shard_version: u32, + /// The max depth of trie iteration. It's recommended to keep that value small, + /// otherwise the output may be really large. + /// For format=full this measures depth in terms of number of trie nodes. + /// For format=pretty this measures depth in terms of key nibbles. + #[clap(long)] + max_depth: u32, + /// Limits how many entries are printed to the output. + #[clap(long)] + limit: Option, + /// Filters output to only show records of the given type. + #[clap(long)] + record_type: Option, + /// Skips nodes which AccountId is lexicographically less than `from` (except being a prefix of `from`). + #[clap(long)] + from: Option, + /// Skips nodes which AccountId is lexicographically greater than `to`. + #[clap(long)] + to: Option, +} + +impl ViewTrieCmd { + pub fn run(self, store: Store) { + let hash = CryptoHash::from_str(&self.hash).unwrap(); + let record_type = self.record_type.map(|c| c as u8); + + match self.format { + ViewTrieFormat::Full => { + view_trie( + store, + hash, + self.shard_id, + self.shard_version, + self.max_depth, + self.limit, + record_type, + self.from, + self.to, + ) + .unwrap(); + } + ViewTrieFormat::Pretty => { + view_trie_leaves( + store, + hash, + self.shard_id, + self.shard_version, + self.max_depth, + self.limit, + record_type, + self.from, + self.to, + ) + .unwrap(); + } + } + } +} diff --git a/tools/state-viewer/src/commands.rs b/tools/state-viewer/src/commands.rs new file mode 100644 index 000000000..6cf20c829 --- /dev/null +++ b/tools/state-viewer/src/commands.rs @@ -0,0 +1,1403 @@ +use crate::apply_chain_range::apply_chain_range; +use crate::contract_accounts::ContractAccount; +use crate::contract_accounts::ContractAccountFilter; +use crate::contract_accounts::Summary; +use crate::state_dump::state_dump; +use crate::state_dump::state_dump_redis; +use crate::tx_dump::dump_tx_from_block; +use crate::{apply_chunk, epoch_info}; +use bytesize::ByteSize; +use itertools::GroupBy; +use itertools::Itertools; +use unc_chain::chain::collect_receipts_from_response; +use unc_chain::migrations::check_if_block_is_first_with_chunk_of_version; +use unc_chain::types::ApplyChunkBlockContext; +use unc_chain::types::ApplyChunkResult; +use unc_chain::types::ApplyChunkShardContext; +use unc_chain::types::RuntimeAdapter; +use unc_chain::types::RuntimeStorageConfig; +use unc_chain::{ChainStore, ChainStoreAccess, ChainStoreUpdate, Error}; +use unc_chain_configs::GenesisChangeConfig; +use unc_epoch_manager::types::BlockHeaderInfo; +use unc_epoch_manager::EpochManagerHandle; +use unc_epoch_manager::{EpochManager, EpochManagerAdapter}; +use unc_primitives::account::id::AccountId; +use unc_primitives::block::{Block, BlockHeader}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::shard_layout::ShardLayout; +use unc_primitives::shard_layout::ShardUId; +use unc_primitives::sharding::ChunkHash; +use unc_primitives::state::FlatStateValue; +use unc_primitives::state_record::state_record_to_account_id; +use unc_primitives::state_record::StateRecord; +use unc_primitives::trie_key::col::NON_DELAYED_RECEIPT_COLUMNS; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::{chunk_extra::ChunkExtra, BlockHeight, ShardId, StateRoot}; +use unc_primitives_core::types::Gas; +use unc_store::flat::FlatStorageChunkView; +use unc_store::flat::FlatStorageManager; +use unc_store::test_utils::create_test_store; +use unc_store::TrieStorage; +use unc_store::{DBCol, Store, Trie, TrieCache, TrieCachingStorage, TrieConfig, TrieDBStorage}; +use framework::{UncConfig, NightshadeRuntime}; +use node_runtime::adapter::ViewRuntimeAdapter; +use serde_json::json; +use std::collections::BinaryHeap; +use std::collections::HashMap; +use std::fs::{self, File}; +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::rc::Rc; +use std::sync::Arc; +use yansi::Color::Red; + +pub(crate) fn apply_block( + block_hash: CryptoHash, + shard_id: ShardId, + epoch_manager: &dyn EpochManagerAdapter, + runtime: &dyn RuntimeAdapter, + chain_store: &mut ChainStore, + use_flat_storage: bool, +) -> (Block, ApplyChunkResult) { + let block = chain_store.get_block(&block_hash).unwrap(); + let height = block.header().height(); + let shard_uid = epoch_manager.shard_id_to_uid(shard_id, block.header().epoch_id()).unwrap(); + if use_flat_storage { + runtime.get_flat_storage_manager().create_flat_storage_for_shard(shard_uid).unwrap(); + } + let apply_result = if block.chunks()[shard_id as usize].height_included() == height { + let chunk = chain_store.get_chunk(&block.chunks()[shard_id as usize].chunk_hash()).unwrap(); + let prev_block = chain_store.get_block(block.header().prev_hash()).unwrap(); + let chain_store_update = ChainStoreUpdate::new(chain_store); + let receipt_proof_response = chain_store_update + .get_incoming_receipts_for_shard( + epoch_manager, + shard_id, + block_hash, + prev_block.chunks()[shard_id as usize].height_included(), + ) + .unwrap(); + let receipts = collect_receipts_from_response(&receipt_proof_response); + + let chunk_inner = chunk.cloned_header().take_inner(); + let is_first_block_with_chunk_of_version = check_if_block_is_first_with_chunk_of_version( + chain_store, + epoch_manager, + block.header().prev_hash(), + shard_id, + ) + .unwrap(); + + runtime + .apply_chunk( + RuntimeStorageConfig::new(*chunk_inner.prev_state_root(), use_flat_storage), + ApplyChunkShardContext { + shard_id, + last_validator_power_proposals: chunk_inner.prev_validator_power_proposals(), + last_validator_frozen_proposals: chunk_inner.prev_validator_frozen_proposals(), + gas_limit: chunk_inner.gas_limit(), + is_new_chunk: true, + is_first_block_with_chunk_of_version, + }, + ApplyChunkBlockContext::from_header( + block.header(), + prev_block.header().next_gas_price(), + ), + &receipts, + chunk.transactions(), + ) + .unwrap() + } else { + let chunk_extra = + chain_store.get_chunk_extra(block.header().prev_hash(), &shard_uid).unwrap(); + + runtime + .apply_chunk( + RuntimeStorageConfig::new(*chunk_extra.state_root(), use_flat_storage), + ApplyChunkShardContext { + shard_id, + last_validator_power_proposals: chunk_extra.validator_power_proposals(), + last_validator_frozen_proposals: chunk_extra.validator_frozen_proposals(), + gas_limit: chunk_extra.gas_limit(), + is_new_chunk: false, + is_first_block_with_chunk_of_version: false, + }, + ApplyChunkBlockContext::from_header( + block.header(), + block.header().next_gas_price(), + ), + &[], + &[], + ) + .unwrap() + }; + (block, apply_result) +} + +pub(crate) fn apply_block_at_height( + height: BlockHeight, + shard_id: ShardId, + use_flat_storage: bool, + home_dir: &Path, + unc_config: UncConfig, + store: Store, +) -> anyhow::Result<()> { + let mut chain_store = ChainStore::new( + store.clone(), + unc_config.genesis.config.genesis_height, + unc_config.client_config.save_trie_changes, + ); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &unc_config.genesis.config); + let runtime = + NightshadeRuntime::from_config(home_dir, store, &unc_config, epoch_manager.clone()); + let block_hash = chain_store.get_block_hash_by_height(height).unwrap(); + let (block, apply_result) = apply_block( + block_hash, + shard_id, + epoch_manager.as_ref(), + runtime.as_ref(), + &mut chain_store, + use_flat_storage, + ); + check_apply_block_result( + &block, + &apply_result, + epoch_manager.as_ref(), + &mut chain_store, + shard_id, + ) +} + +pub(crate) fn apply_chunk( + home_dir: &Path, + unc_config: UncConfig, + store: Store, + chunk_hash: ChunkHash, + target_height: Option, + use_flat_storage: bool, +) -> anyhow::Result<()> { + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &unc_config.genesis.config); + let runtime = NightshadeRuntime::from_config( + home_dir, + store.clone(), + &unc_config, + epoch_manager.clone(), + ); + let mut chain_store = ChainStore::new( + store, + unc_config.genesis.config.genesis_height, + unc_config.client_config.save_trie_changes, + ); + let (apply_result, gas_limit) = apply_chunk::apply_chunk( + epoch_manager.as_ref(), + runtime.as_ref(), + &mut chain_store, + chunk_hash, + target_height, + None, + use_flat_storage, + )?; + println!("resulting chunk extra:\n{:?}", resulting_chunk_extra(&apply_result, gas_limit)); + Ok(()) +} + +pub(crate) fn apply_range( + start_index: Option, + end_index: Option, + shard_id: ShardId, + verbose_output: bool, + csv_file: Option, + home_dir: &Path, + unc_config: UncConfig, + store: Store, + only_contracts: bool, + sequential: bool, + use_flat_storage: bool, +) { + let mut csv_file = csv_file.map(|filename| std::fs::File::create(filename).unwrap()); + + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &unc_config.genesis.config); + let runtime = NightshadeRuntime::from_config( + home_dir, + store.clone(), + &unc_config, + epoch_manager.clone(), + ); + apply_chain_range( + store, + &unc_config.genesis, + start_index, + end_index, + shard_id, + epoch_manager.as_ref(), + runtime, + verbose_output, + csv_file.as_mut(), + only_contracts, + sequential, + use_flat_storage, + ); +} + +pub(crate) fn apply_receipt( + home_dir: &Path, + unc_config: UncConfig, + store: Store, + hash: CryptoHash, + use_flat_storage: bool, +) -> anyhow::Result<()> { + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &unc_config.genesis.config); + let runtime = NightshadeRuntime::from_config( + home_dir, + store.clone(), + &unc_config, + epoch_manager.clone(), + ); + apply_chunk::apply_receipt( + unc_config.genesis.config.genesis_height, + epoch_manager.as_ref(), + runtime.as_ref(), + store, + hash, + use_flat_storage, + ) + .map(|_| ()) +} + +pub(crate) fn apply_tx( + home_dir: &Path, + unc_config: UncConfig, + store: Store, + hash: CryptoHash, + use_flat_storage: bool, +) -> anyhow::Result<()> { + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &unc_config.genesis.config); + let runtime = NightshadeRuntime::from_config( + home_dir, + store.clone(), + &unc_config, + epoch_manager.clone(), + ); + apply_chunk::apply_tx( + unc_config.genesis.config.genesis_height, + epoch_manager.as_ref(), + runtime.as_ref(), + store, + hash, + use_flat_storage, + ) + .map(|_| ()) +} + +pub(crate) fn dump_account_storage( + account_id: String, + storage_key: String, + output: &Path, + block_height: String, + home_dir: &Path, + unc_config: UncConfig, + store: Store, +) { + let block_height = if block_height == "latest" { + LoadTrieMode::Latest + } else if let Ok(height) = block_height.parse::() { + LoadTrieMode::Height(height) + } else { + panic!("block_height should be either number or \"latest\"") + }; + let (_, runtime, state_roots, header) = + load_trie_stop_at_height(store, home_dir, &unc_config, block_height); + for (shard_id, state_root) in state_roots.iter().enumerate() { + let trie = runtime + .get_trie_for_shard(shard_id as u64, header.prev_hash(), *state_root, false) + .unwrap(); + let key = TrieKey::ContractData { + account_id: account_id.parse().unwrap(), + key: storage_key.as_bytes().to_vec(), + }; + let item = trie.get(&key.to_vec()); + let value = item.unwrap(); + if let Some(value) = value { + let record = StateRecord::from_raw_key_value(key.to_vec(), value).unwrap(); + match record { + StateRecord::Data { account_id: _, data_key: _, value } => { + fs::write(output, value.as_slice()).unwrap(); + println!( + "Dump contract storage under key {} of account {} into file {}", + storage_key, + account_id, + output.display() + ); + std::process::exit(0); + } + _ => unreachable!(), + } + } + } + println!("Storage under key {} of account {} not found", storage_key, account_id); + std::process::exit(1); +} + +pub(crate) fn dump_code( + account_id: String, + output: &Path, + home_dir: &Path, + unc_config: UncConfig, + store: Store, +) { + let (epoch_manager, runtime, state_roots, header) = load_trie(store, home_dir, &unc_config); + let epoch_id = &epoch_manager.get_epoch_id(header.hash()).unwrap(); + + for (shard_id, state_root) in state_roots.iter().enumerate() { + let shard_uid = epoch_manager.shard_id_to_uid(shard_id as u64, epoch_id).unwrap(); + if let Ok(contract_code) = + runtime.view_contract_code(&shard_uid, *state_root, &account_id.parse().unwrap()) + { + let mut file = File::create(output).unwrap(); + file.write_all(contract_code.code()).unwrap(); + println!("Dump contract of account {} into file {}", account_id, output.display()); + + std::process::exit(0); + } + } + println!( + "Account {} does not exist or do not have contract deployed in all shards", + account_id + ); +} + +pub(crate) fn dump_state( + height: Option, + stream: bool, + file: Option, + home_dir: &Path, + unc_config: UncConfig, + store: Store, + change_config: &GenesisChangeConfig, +) { + let mode = match height { + Some(h) => LoadTrieMode::LastFinalFromHeight(h), + None => LoadTrieMode::Latest, + }; + let (epoch_manager, runtime, state_roots, header) = + load_trie_stop_at_height(store, home_dir, &unc_config, mode); + let height = header.height(); + let home_dir = PathBuf::from(&home_dir); + + if stream { + let output_dir = file.unwrap_or(home_dir.join("output")); + let records_path = output_dir.join("records.json"); + let new_unc_config = state_dump( + epoch_manager.as_ref(), + runtime, + &state_roots, + header, + &unc_config, + Some(&records_path), + change_config, + ); + println!("Saving state at {:?} @ {} into {}", state_roots, height, output_dir.display(),); + new_unc_config.save_to_dir(&output_dir); + } else { + let new_unc_config = state_dump( + epoch_manager.as_ref(), + runtime, + &state_roots, + header, + &unc_config, + None, + change_config, + ); + let output_file = file.unwrap_or(home_dir.join("output.json")); + println!("Saving state at {:?} @ {} into {}", state_roots, height, output_file.display(),); + new_unc_config.genesis.to_file(&output_file); + } +} + +pub(crate) fn dump_state_redis( + height: Option, + home_dir: &Path, + unc_config: UncConfig, + store: Store, +) { + let mode = match height { + Some(h) => LoadTrieMode::LastFinalFromHeight(h), + None => LoadTrieMode::Latest, + }; + let (_, runtime, state_roots, header) = + load_trie_stop_at_height(store, home_dir, &unc_config, mode); + + let res = state_dump_redis(runtime, &state_roots, header); + assert_eq!(res, Ok(())); +} + +pub(crate) fn dump_tx( + start_height: BlockHeight, + end_height: BlockHeight, + home_dir: &Path, + unc_config: UncConfig, + store: Store, + select_account_ids: Option<&[AccountId]>, + output_path: Option, +) -> Result<(), Error> { + let chain_store = ChainStore::new( + store, + unc_config.genesis.config.genesis_height, + unc_config.client_config.save_trie_changes, + ); + let mut txs = vec![]; + for height in start_height..=end_height { + let hash_result = chain_store.get_block_hash_by_height(height); + if let Ok(hash) = hash_result { + let block = chain_store.get_block(&hash)?; + txs.extend(dump_tx_from_block(&chain_store, &block, select_account_ids)); + } + } + let json_path = match output_path { + Some(path) => PathBuf::from(path), + None => PathBuf::from(&home_dir).join("tx.json"), + }; + println!("Saving tx (height {} to {}) into {}", start_height, end_height, json_path.display(),); + fs::write(json_path, json!(txs).to_string()) + .expect("Error writing the results to a json file."); + Ok(()) +} + +pub(crate) fn get_chunk(chunk_hash: ChunkHash, unc_config: UncConfig, store: Store) { + let chain_store = ChainStore::new( + store, + unc_config.genesis.config.genesis_height, + unc_config.client_config.save_trie_changes, + ); + let chunk = chain_store.get_chunk(&chunk_hash); + println!("Chunk: {:#?}", chunk); +} + +pub(crate) fn get_partial_chunk( + partial_chunk_hash: ChunkHash, + unc_config: UncConfig, + store: Store, +) { + let chain_store = ChainStore::new( + store, + unc_config.genesis.config.genesis_height, + unc_config.client_config.save_trie_changes, + ); + let partial_chunk = chain_store.get_partial_chunk(&partial_chunk_hash); + println!("Partial chunk: {:#?}", partial_chunk); +} + +pub(crate) fn get_receipt(receipt_id: CryptoHash, unc_config: UncConfig, store: Store) { + let chain_store = ChainStore::new( + store, + unc_config.genesis.config.genesis_height, + unc_config.client_config.save_trie_changes, + ); + let receipt = chain_store.get_receipt(&receipt_id); + println!("Receipt: {:#?}", receipt); +} + +fn chunk_extras_equal(l: &ChunkExtra, r: &ChunkExtra) -> bool { + // explicitly enumerate the versions in a match here first so that if a new version is + // added, we'll get a compile error here and be reminded to update it correctly. + match (l, r) { + (ChunkExtra::V1(l), ChunkExtra::V1(r)) => return l == r, + (ChunkExtra::V2(l), ChunkExtra::V2(r)) => return l == r, + (ChunkExtra::V1(_), ChunkExtra::V2(_)) | (ChunkExtra::V2(_), ChunkExtra::V1(_)) => {} + }; + if l.state_root() != r.state_root() { + return false; + } + if l.outcome_root() != r.outcome_root() { + return false; + } + if l.gas_used() != r.gas_used() { + return false; + } + if l.gas_limit() != r.gas_limit() { + return false; + } + if l.balance_burnt() != r.balance_burnt() { + return false; + } + l.validator_power_proposals().collect::>() == r.validator_power_proposals().collect::>()&& + l.validator_frozen_proposals().collect::>() == r.validator_frozen_proposals().collect::>() +} + +pub(crate) fn check_apply_block_result( + block: &Block, + apply_result: &ApplyChunkResult, + epoch_manager: &EpochManagerHandle, + chain_store: &ChainStore, + shard_id: ShardId, +) -> anyhow::Result<()> { + let height = block.header().height(); + let block_hash = block.header().hash(); + let new_chunk_extra = + resulting_chunk_extra(apply_result, block.chunks()[shard_id as usize].gas_limit()); + println!( + "apply chunk for shard {} at height {}, resulting chunk extra {:?}", + shard_id, height, &new_chunk_extra, + ); + let shard_uid = epoch_manager.shard_id_to_uid(shard_id, block.header().epoch_id()).unwrap(); + if block.chunks()[shard_id as usize].height_included() == height { + if let Ok(old_chunk_extra) = chain_store.get_chunk_extra(block_hash, &shard_uid) { + if chunk_extras_equal(&new_chunk_extra, old_chunk_extra.as_ref()) { + println!("new chunk extra matches old chunk extra"); + Ok(()) + } else { + Err(anyhow::anyhow!( + "mismatch in resulting chunk extra.\nold: {:?}\nnew: {:?}", + &old_chunk_extra, + &new_chunk_extra + )) + } + } else { + Err(anyhow::anyhow!("No existing chunk extra available")) + } + } else { + println!("No existing chunk extra available"); + Ok(()) + } +} + +pub(crate) fn print_chain( + start_height: BlockHeight, + end_height: BlockHeight, + unc_config: UncConfig, + store: Store, + show_full_hashes: bool, +) { + let chain_store = ChainStore::new( + store.clone(), + unc_config.genesis.config.genesis_height, + unc_config.client_config.save_trie_changes, + ); + let epoch_manager = EpochManager::new_arc_handle(store, &unc_config.genesis.config); + let mut account_id_to_blocks = HashMap::new(); + let mut cur_epoch_id = None; + // TODO: Split into smaller functions. + for height in start_height..=end_height { + if let Ok(block_hash) = chain_store.get_block_hash_by_height(height) { + let header = chain_store.get_block_header(&block_hash).unwrap().clone(); + if height == 0 { + println!( + "{: >3} {}", + header.height(), + format_hash(*header.hash(), show_full_hashes) + ); + } else { + let parent_header = + chain_store.get_block_header(header.prev_hash()).unwrap().clone(); + if let Ok(epoch_id) = epoch_manager.get_epoch_id_from_prev_block(header.prev_hash()) + { + cur_epoch_id = Some(epoch_id.clone()); + match epoch_manager.is_next_block_epoch_start(header.prev_hash()) { + Ok(true) => { + println!("{:?}", account_id_to_blocks); + account_id_to_blocks = HashMap::new(); + println!( + "Epoch {} Validators {:?}", + format_hash(epoch_id.0, show_full_hashes), + epoch_manager + .get_epoch_block_producers_ordered(&epoch_id, header.hash()) + ); + } + Err(err) => { + println!("Don't know if next block is epoch start: {err:?}"); + } + _ => {} + }; + let block_producer = epoch_manager + .get_block_producer(header.epoch_id(),header.height()) + .map(|account_id| account_id.to_string()) + .ok() + .unwrap_or("error".to_owned()); + account_id_to_blocks + .entry(block_producer.clone()) + .and_modify(|e| *e += 1) + .or_insert(1); + + let block = if let Ok(block) = chain_store.get_block(&block_hash) { + block.clone() + } else { + continue; + }; + + let mut chunk_debug_str: Vec = Vec::new(); + + for shard_id in 0..header.chunk_mask().len() { + let chunk_producer = epoch_manager + .get_chunk_producer(&epoch_id, header.height(), shard_id as u64) + .map(|account_id| account_id.to_string()) + .unwrap_or("CP Unknown".to_owned()); + if header.chunk_mask()[shard_id] { + let chunk_hash = &block.chunks()[shard_id].chunk_hash(); + if let Ok(chunk) = chain_store.get_chunk(chunk_hash) { + chunk_debug_str.push(format!( + "{}: {} {: >3} Tgas {: >10}", + shard_id, + format_hash(chunk_hash.0, show_full_hashes), + chunk.cloned_header().prev_gas_used() / (1_000_000_000_000), + chunk_producer + )); + } else { + chunk_debug_str.push(format!( + "{}: {} ChunkMissing", + shard_id, + format_hash(chunk_hash.0, show_full_hashes), + )); + } + } else { + chunk_debug_str + .push(format!("{}: MISSING {: >10}", shard_id, chunk_producer)); + } + } + + println!( + "{: >3} {} {} | {: >10} | parent: {: >3} {} | {} {}", + header.height(), + header.raw_timestamp(), + format_hash(*header.hash(), show_full_hashes), + block_producer, + parent_header.height(), + format_hash(*parent_header.hash(), show_full_hashes), + chunk_mask_to_str(header.chunk_mask()), + chunk_debug_str.join("|") + ); + } else { + println!("{height} MISSING {:?}", header.prev_hash()); + } + } + } else if let Some(epoch_id) = &cur_epoch_id { + let block_producer = epoch_manager + .get_block_producer(epoch_id,height) + .map(|account_id| account_id.to_string()) + .unwrap_or("error".to_owned()); + println!( + "{: >3} {} | {: >10}", + height, + Red.style().bold().paint("MISSING"), + block_producer + ); + } else { + println!("{: >3} {}", height, Red.style().bold().paint("MISSING")); + } + } +} + +pub(crate) fn replay_chain( + start_height: BlockHeight, + end_height: BlockHeight, + unc_config: UncConfig, + store: Store, +) { + let chain_store = ChainStore::new( + store, + unc_config.genesis.config.genesis_height, + unc_config.client_config.save_trie_changes, + ); + let new_store = create_test_store(); + let epoch_manager = EpochManager::new_arc_handle(new_store, &unc_config.genesis.config); + for height in start_height..=end_height { + if let Ok(block_hash) = chain_store.get_block_hash_by_height(height) { + let header = chain_store.get_block_header(&block_hash).unwrap().clone(); + println!("Height: {}, header: {:#?}", height, header); + epoch_manager + .add_validator_proposals(BlockHeaderInfo::new( + &header, + chain_store.get_block_height(header.last_final_block()).unwrap(), + )) + .unwrap() + .commit() + .unwrap(); + } + } +} + +pub(crate) fn resulting_chunk_extra(result: &ApplyChunkResult, gas_limit: Gas) -> ChunkExtra { + let (outcome_root, _) = ApplyChunkResult::compute_outcomes_proof(&result.outcomes); + ChunkExtra::new( + &result.new_root, + outcome_root, + result.validator_power_proposals.clone(), + result.validator_frozen_proposals.clone(), + result.total_gas_burnt, + gas_limit, + result.total_balance_burnt, + ) +} + +pub(crate) fn state(home_dir: &Path, unc_config: UncConfig, store: Store) { + let (_, runtime, state_roots, header) = load_trie(store, home_dir, &unc_config); + println!("Storage roots are {:?}, block height is {}", state_roots, header.height()); + for (shard_id, state_root) in state_roots.iter().enumerate() { + let trie = runtime + .get_trie_for_shard(shard_id as u64, header.prev_hash(), *state_root, false) + .unwrap(); + for item in trie.iter().unwrap() { + let (key, value) = item.unwrap(); + if let Some(state_record) = StateRecord::from_raw_key_value(key, value) { + println!("{}", state_record); + } + } + } +} + +pub(crate) fn view_chain( + height: Option, + view_block: bool, + view_chunks: bool, + unc_config: UncConfig, + store: Store, +) { + let chain_store = ChainStore::new( + store.clone(), + unc_config.genesis.config.genesis_height, + unc_config.client_config.save_trie_changes, + ); + let block = { + match height { + Some(h) => { + let block_hash = + chain_store.get_block_hash_by_height(h).expect("Block does not exist"); + chain_store.get_block(&block_hash).unwrap() + } + None => { + let head = chain_store.head().unwrap(); + chain_store.get_block(&head.last_block_hash).unwrap() + } + } + }; + let epoch_manager = EpochManager::new_from_genesis_config(store, &unc_config.genesis.config) + .expect("Failed to start Epoch Manager"); + let shard_layout = epoch_manager.get_shard_layout(block.header().epoch_id()).unwrap(); + + let mut chunk_extras = vec![]; + let mut chunks = vec![]; + for (i, chunk_header) in block.chunks().iter().enumerate() { + if chunk_header.height_included() == block.header().height() { + let shard_uid = ShardUId::from_shard_id_and_layout(i as ShardId, &shard_layout); + chunk_extras + .push((i, chain_store.get_chunk_extra(block.hash(), &shard_uid).ok().clone())); + chunks.push((i, chain_store.get_chunk(&chunk_header.chunk_hash()).ok().clone())); + } + } + let chunk_extras = block + .chunks() + .iter() + .enumerate() + .filter_map(|(i, chunk_header)| { + if chunk_header.height_included() == block.header().height() { + let shard_uid = ShardUId::from_shard_id_and_layout(i as ShardId, &shard_layout); + Some((i, chain_store.get_chunk_extra(block.hash(), &shard_uid).ok())) + } else { + None + } + }) + .collect::>(); + + if height.is_none() { + let head = chain_store.head().unwrap(); + println!("head: {:#?}", head); + } else { + println!("block height {}, hash {}", block.header().height(), block.hash()); + } + + println!("shard layout {:#?}", shard_layout); + + for (shard_id, chunk_extra) in chunk_extras { + println!("shard {}, chunk extra: {:#?}", shard_id, chunk_extra); + } + if view_block { + println!("last block: {:#?}", block); + } + if view_chunks { + for (shard_id, chunk) in chunks { + println!("shard {}, chunk: {:#?}", shard_id, chunk); + } + } +} + +pub(crate) fn check_block_chunk_existence(unc_config: UncConfig, store: Store) { + let genesis_height = unc_config.genesis.config.genesis_height; + let chain_store = + ChainStore::new(store, genesis_height, unc_config.client_config.save_trie_changes); + let head = chain_store.head().unwrap(); + let mut cur_block = chain_store.get_block(&head.last_block_hash).unwrap(); + while cur_block.header().height() > genesis_height { + for chunk_header in cur_block.chunks().iter() { + if chunk_header.height_included() == cur_block.header().height() + && chain_store.get_chunk(&chunk_header.chunk_hash()).is_err() + { + panic!( + "chunk {:?} cannot be found in storage, last block {:?}", + chunk_header, cur_block + ); + } + } + cur_block = match chain_store.get_block(cur_block.header().prev_hash()) { + Ok(b) => b.clone(), + Err(_) => { + panic!("last block is {:?}", cur_block); + } + } + } + println!("Block check succeed"); +} + +pub(crate) fn print_epoch_info( + epoch_selection: epoch_info::EpochSelection, + validator_account_id: Option, + kickouts_summary: bool, + unc_config: UncConfig, + store: Store, +) { + let genesis_height = unc_config.genesis.config.genesis_height; + let mut chain_store = + ChainStore::new(store.clone(), genesis_height, unc_config.client_config.save_trie_changes); + let epoch_manager = + EpochManager::new_from_genesis_config(store.clone(), &unc_config.genesis.config) + .expect("Failed to start Epoch Manager") + .into_handle(); + + epoch_info::print_epoch_info( + epoch_selection, + validator_account_id, + kickouts_summary, + store, + &mut chain_store, + &epoch_manager, + ); +} + +fn get_trie(store: Store, hash: CryptoHash, shard_id: u32, shard_version: u32) -> Trie { + let shard_uid = ShardUId { version: shard_version, shard_id }; + let trie_config: TrieConfig = Default::default(); + let shard_cache = TrieCache::new(&trie_config, shard_uid, true); + let trie_storage = TrieCachingStorage::new(store, shard_cache, shard_uid, true, None); + Trie::new(Rc::new(trie_storage), hash, None) +} + +pub(crate) fn view_trie( + store: Store, + hash: CryptoHash, + shard_id: u32, + shard_version: u32, + max_depth: u32, + limit: Option, + record_type: Option, + from: Option, + to: Option, +) -> anyhow::Result<()> { + let trie = get_trie(store, hash, shard_id, shard_version); + trie.print_recursive( + &mut std::io::stdout().lock(), + &hash, + max_depth, + limit, + record_type, + &from.as_ref(), + &to.as_ref(), + ); + Ok(()) +} + +pub(crate) fn view_trie_leaves( + store: Store, + state_root_hash: CryptoHash, + shard_id: u32, + shard_version: u32, + max_depth: u32, + limit: Option, + record_type: Option, + from: Option, + to: Option, +) -> anyhow::Result<()> { + let trie = get_trie(store, state_root_hash, shard_id, shard_version); + trie.print_recursive_leaves( + &mut std::io::stdout().lock(), + max_depth, + limit, + record_type, + &from.as_ref(), + &to.as_ref(), + ); + Ok(()) +} + +enum LoadTrieMode { + /// Load latest state + Latest, + /// Load prev state at some height + Height(BlockHeight), + /// Load the prev state of the last final block from some height + LastFinalFromHeight(BlockHeight), +} + +fn load_trie( + store: Store, + home_dir: &Path, + unc_config: &UncConfig, +) -> (Arc, Arc, Vec, BlockHeader) { + load_trie_stop_at_height(store, home_dir, unc_config, LoadTrieMode::Latest) +} + +fn load_trie_stop_at_height( + store: Store, + home_dir: &Path, + unc_config: &UncConfig, + mode: LoadTrieMode, +) -> (Arc, Arc, Vec, BlockHeader) { + let chain_store = ChainStore::new( + store.clone(), + unc_config.genesis.config.genesis_height, + unc_config.client_config.save_trie_changes, + ); + + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &unc_config.genesis.config); + let runtime = + NightshadeRuntime::from_config(home_dir, store, unc_config, epoch_manager.clone()); + let head = chain_store.head().unwrap(); + let last_block = match mode { + LoadTrieMode::LastFinalFromHeight(height) => { + // find the first final block whose height is at least `height`. + let mut cur_height = height + 1; + loop { + if cur_height >= head.height { + panic!("No final block with height >= {} exists", height); + } + let cur_block_hash = match chain_store.get_block_hash_by_height(cur_height) { + Ok(hash) => hash, + Err(_) => { + cur_height += 1; + continue; + } + }; + let last_final_block_hash = + *chain_store.get_block_header(&cur_block_hash).unwrap().last_final_block(); + let last_final_block = chain_store.get_block(&last_final_block_hash).unwrap(); + if last_final_block.header().height() >= height { + break last_final_block; + } else { + cur_height += 1; + continue; + } + } + } + LoadTrieMode::Height(height) => { + let block_hash = chain_store.get_block_hash_by_height(height).unwrap(); + chain_store.get_block(&block_hash).unwrap() + } + LoadTrieMode::Latest => chain_store.get_block(&head.last_block_hash).unwrap(), + }; + let shard_layout = epoch_manager.get_shard_layout(&last_block.header().epoch_id()).unwrap(); + let state_roots = last_block + .chunks() + .iter() + .map(|chunk| { + // ChunkExtra contains StateRoot after applying actions in the block. + let chunk_extra = chain_store + .get_chunk_extra( + &head.last_block_hash, + &ShardUId::from_shard_id_and_layout(chunk.shard_id(), &shard_layout), + ) + .unwrap(); + *chunk_extra.state_root() + }) + .collect(); + + (epoch_manager, runtime, state_roots, last_block.header().clone()) +} + +fn format_hash(h: CryptoHash, show_full_hashes: bool) -> String { + let mut hash = h.to_string(); + if !show_full_hashes { + hash.truncate(7); + } + hash +} + +pub fn chunk_mask_to_str(mask: &[bool]) -> String { + mask.iter().map(|f| if *f { '.' } else { 'X' }).collect() +} + +pub(crate) fn contract_accounts( + home_dir: &Path, + store: Store, + unc_config: UncConfig, + filter: ContractAccountFilter, +) -> anyhow::Result<()> { + let (_, _runtime, state_roots, _header) = load_trie(store.clone(), home_dir, &unc_config); + + let tries = state_roots.iter().enumerate().map(|(shard_id, &state_root)| { + // TODO: This assumes simple nightshade layout, it will need an update when we reshard. + let shard_uid = ShardUId::from_shard_id_and_layout( + shard_id as u64, + &ShardLayout::v0_single_shard(), + ); + // Use simple non-caching storage, we don't expect many duplicate lookups while iterating. + let storage = TrieDBStorage::new(store.clone(), shard_uid); + // We don't need flat state to traverse all accounts. + let flat_storage_chunk_view = None; + Trie::new(Rc::new(storage), state_root, flat_storage_chunk_view) + }); + + filter.write_header(&mut std::io::stdout().lock())?; + // Prefer streaming the results, to use less memory and provide + // a feedback more quickly. + if filter.can_stream() { + // Process account after account and display results immediately. + for (i, trie) in tries.enumerate() { + eprintln!("Starting shard {i}"); + let trie_iter = ContractAccount::in_trie(trie, filter.clone())?; + for contract in trie_iter { + match contract { + Ok(contract) => println!("{contract}"), + Err(err) => eprintln!("{err}"), + } + } + } + } else { + // Load all results into memory, which allows advanced lookups but also + // means we have to wait for everything to complete before output can be + // shown. + let tries_iterator = ContractAccount::in_tries(tries.collect(), &filter)?; + let result = tries_iterator.summary(&store, &filter); + println!("{result}"); + } + + Ok(()) +} + +pub(crate) fn clear_cache(store: Store) { + let mut store_update = store.store_update(); + store_update.delete_all(DBCol::CachedContractCode); + store_update.commit().unwrap(); +} + +/// Prints the state statistics for all shards. Please note that it relies on +/// the live flat storage and may break if the node is not stopped. +pub(crate) fn print_state_stats(home_dir: &Path, store: Store, unc_config: UncConfig) { + let (epoch_manager, runtime, _, block_header) = + load_trie(store.clone(), home_dir, &unc_config); + + let block_hash = *block_header.hash(); + let shard_layout = epoch_manager.get_shard_layout_from_prev_block(&block_hash).unwrap(); + + let flat_storage_manager = runtime.get_flat_storage_manager(); + for shard_uid in shard_layout.shard_uids() { + print_state_stats_for_shard_uid(&store, &flat_storage_manager, block_hash, shard_uid); + } +} + +/// Prints the state statistics for a single shard. +fn print_state_stats_for_shard_uid( + store: &Store, + flat_storage_manager: &FlatStorageManager, + block_hash: CryptoHash, + shard_uid: ShardUId, +) { + flat_storage_manager.create_flat_storage_for_shard(shard_uid).unwrap(); + let trie_storage = TrieDBStorage::new(store.clone(), shard_uid); + let chunk_view = flat_storage_manager.chunk_view(shard_uid, block_hash).unwrap(); + + let mut state_stats = StateStats::default(); + + // iteratate for the first time to get the size statistics + let group_by = get_state_stats_group_by(&chunk_view, &trie_storage); + let iter = get_state_stats_account_iter(&group_by); + for state_stats_account in iter { + state_stats.push(state_stats_account); + } + + // iterate for the second time to find the middle account + let group_by = get_state_stats_group_by(&chunk_view, &trie_storage); + let iter = get_state_stats_account_iter(&group_by); + let mut current_size = ByteSize::default(); + for state_stats_account in iter { + let new_size = current_size + state_stats_account.size; + if 2 * new_size.as_u64() > state_stats.total_size.as_u64() { + state_stats.middle_account = Some(state_stats_account); + state_stats.middle_account_leading_size = Some(current_size); + break; + } + current_size = new_size; + } + + tracing::info!(target: "state_viewer", "{shard_uid:?}"); + tracing::info!(target: "state_viewer", "{state_stats:#?}"); +} + +/// Gets the flat state iterator from the chunk view, rearranges it to be sorted +/// by the account id, rather than type, account id and finally groups the +/// records by account id while collecting aggregate statistics. +fn get_state_stats_group_by<'a>( + chunk_view: &'a FlatStorageChunkView, + trie_storage: &'a TrieDBStorage, +) -> GroupBy< + AccountId, + impl Iterator + 'a, + impl FnMut(&StateStatsStateRecord) -> AccountId, +> { + // The flat state iterator is sorted by type, account id. In order to + // rearrange it we get the iterators for each type and later merge them by + // the account id. + let type_iters = NON_DELAYED_RECEIPT_COLUMNS + .iter() + .map(|(type_byte, _)| { + chunk_view.iter_flat_state_entries(Some(&[*type_byte]), Some(&[*type_byte + 1])) + }) + .into_iter(); + + // Filter out any errors. + let type_iters = type_iters.map(|type_iter| type_iter.filter_map(|item| item.ok())).into_iter(); + + // Read the values from and convert items to StateStatsStateRecord. + let type_iters = type_iters + .map(move |type_iter| { + type_iter.filter_map(move |(key, value)| { + let value = read_flat_state_value(&trie_storage, value); + let key_size = key.len() as u64; + let value_size = value.len() as u64; + let size = ByteSize::b(key_size + value_size); + let state_record = StateRecord::from_raw_key_value(key, value); + state_record.map(|state_record| StateStatsStateRecord { + account_id: state_record_to_account_id(&state_record).clone(), + state_record, + size, + }) + }) + }) + .into_iter(); + + // Merge the iterators for different types. The StateStatsStateRecord + // implements the Ord and PartialOrd traits that compare items by their + // account ids. + let iter = type_iters.kmerge().into_iter(); + + // Finally, group by the account id. + iter.group_by(|state_record| state_record.account_id.clone()) +} + +/// Given the StateStatsStateRecords grouped by the account id returns an +/// iterator of StateStatsAccount. +fn get_state_stats_account_iter<'a>( + group_by: &'a GroupBy< + AccountId, + impl Iterator + 'a, + impl FnMut(&StateStatsStateRecord) -> AccountId, + >, +) -> impl Iterator + 'a { + // aggregate size for each account id group + group_by + .into_iter() + .map(|(account_id, group)| { + let mut size = ByteSize::b(0); + for state_stats_state_record in group { + size += state_stats_state_record.size; + } + StateStatsAccount { account_id, size } + }) + .into_iter() +} + +/// Helper function to read the value from flat storage. +/// It either returns the inlined value or reads ref value from the storage. +fn read_flat_state_value( + trie_storage: &TrieDBStorage, + flat_state_value: FlatStateValue, +) -> Vec { + match flat_state_value { + FlatStateValue::Ref(val) => trie_storage.retrieve_raw_bytes(&val.hash).unwrap().to_vec(), + FlatStateValue::Inlined(val) => val, + } +} + +/// StateStats is used for storing the state statistics of a single trie. +#[derive(Default)] +pub struct StateStats { + pub total_size: ByteSize, + pub total_count: usize, + + // The account that is in the middle of the state in respect to storage. + pub middle_account: Option, + // The total size of all accounts leading to the middle account. + // Can be used to determin how does the middle account split the state. + pub middle_account_leading_size: Option, + + pub top_accounts: BinaryHeap, +} + +impl core::fmt::Debug for StateStats { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let average_size = self + .total_size + .as_u64() + .checked_div(self.total_count as u64) + .map(ByteSize::b) + .unwrap_or_default(); + + let left_size = self.middle_account_leading_size.unwrap_or_default(); + let middle_size = self.middle_account.as_ref().map(|a| a.size).unwrap_or_default(); + let right_size = self.total_size.as_u64() - left_size.as_u64() - middle_size.as_u64(); + let right_size = ByteSize::b(right_size); + + let left_percent = 100 * left_size.as_u64() / self.total_size.as_u64(); + let middle_percent = 100 * middle_size.as_u64() / self.total_size.as_u64(); + let right_percent = 100 * right_size.as_u64() / self.total_size.as_u64(); + + f.debug_struct("StateStats") + .field("total_size", &self.total_size) + .field("total_count", &self.total_count) + .field("average_size", &average_size) + .field("middle_account", &self.middle_account.as_ref().unwrap()) + .field("split_size", &format!("{left_size:?} : {middle_size:?} : {right_size:?}")) + .field("split_percent", &format!("{left_percent}:{middle_percent}:{right_percent}")) + .field("top_accounts", &self.top_accounts) + .finish() + } +} + +impl StateStats { + pub fn push(&mut self, state_stats_account: StateStatsAccount) { + self.total_size += state_stats_account.size; + self.total_count += 1; + + self.top_accounts.push(state_stats_account); + if self.top_accounts.len() > 5 { + self.top_accounts.pop(); + } + } +} + +/// StateStatsStateRecord stores the state record and associated information. +/// It's used as a helper struct for merging state records from different record types. +#[derive(Eq, PartialEq)] +struct StateStatsStateRecord { + state_record: StateRecord, + account_id: AccountId, + size: ByteSize, +} + +impl Ord for StateStatsStateRecord { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.account_id.cmp(&other.account_id) + } +} + +impl PartialOrd for StateStatsStateRecord { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// StateStatsAccount stores aggregated information about an account. +/// It is the result of the grouping of state records belonging to the same account. +#[derive(PartialEq, Eq)] +pub struct StateStatsAccount { + pub account_id: AccountId, + pub size: ByteSize, +} + +impl Ord for StateStatsAccount { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.size.cmp(&other.size).reverse() + } +} + +impl PartialOrd for StateStatsAccount { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl std::fmt::Debug for StateStatsAccount { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("StateStatsAccount") + .field("account_id", &self.account_id.as_str()) + .field("size", &self.size) + .finish() + } +} + +#[cfg(test)] +mod tests { + use unc_chain::types::RuntimeAdapter; + use unc_chain::ChainGenesis; + use unc_chain_configs::Genesis; + use unc_client::test_utils::TestEnv; + use unc_crypto::{InMemorySigner, KeyFile, KeyType}; + use unc_epoch_manager::EpochManager; + use unc_primitives::shard_layout::ShardUId; + use unc_primitives::types::chunk_extra::ChunkExtra; + use unc_primitives::types::AccountId; + use unc_store::genesis::initialize_genesis_state; + use framework::config::Config; + use framework::config::GenesisExt; + use framework::{UncConfig, NightshadeRuntime}; + use std::sync::Arc; + + #[test] + /// Tests that getting the latest trie state actually gets the latest state. + /// Adds a transaction and waits for it to be included in a block. + /// Checks that the change of state caused by that transaction is visible to `load_trie()`. + fn test_latest_trie_state() { + unc_o11y::testonly::init_test_logger(); + let validators = vec!["test0".parse::().unwrap()]; + let genesis = Genesis::test_sharded_new_version(validators, 1, vec![1]); + let chain_genesis = ChainGenesis::test(); + + let tmp_dir = tempfile::tempdir().unwrap(); + let home_dir = tmp_dir.path(); + + let store = unc_store::test_utils::create_test_store(); + initialize_genesis_state(store.clone(), &genesis, Some(home_dir)); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = NightshadeRuntime::test( + home_dir, + store.clone(), + &genesis.config, + epoch_manager.clone(), + ) as Arc; + + let stores = vec![store.clone()]; + let epoch_managers = vec![epoch_manager]; + let runtimes = vec![runtime]; + + let mut env = TestEnv::builder(chain_genesis) + .stores(stores) + .epoch_managers(epoch_managers) + .runtimes(runtimes) + .build(); + + let signer = InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + assert_eq!(env.send_money(0), unc_client::ProcessTxResponse::ValidTx); + + // It takes 2 blocks to record a transaction on chain and apply the receipts. + env.produce_block(0, 1); + env.produce_block(0, 2); + + let chunk_extras: Vec> = (1..=2) + .map(|height| { + let block = env.clients[0].chain.get_block_by_height(height).unwrap(); + let hash = *block.hash(); + let chunk_extra = env.clients[0] + .chain + .get_chunk_extra(&hash, &ShardUId { version: 1, shard_id: 0 }) + .unwrap(); + chunk_extra + }) + .collect(); + + // Check that `send_money()` actually changed state. + assert_ne!(chunk_extras[0].state_root(), chunk_extras[1].state_root()); + + let unc_config = + UncConfig::new(Config::default(), genesis, KeyFile::from(&signer), None).unwrap(); + let (_epoch_manager, _runtime, state_roots, block_header) = + crate::commands::load_trie(store, home_dir, &unc_config); + assert_eq!(&state_roots[0], chunk_extras[1].state_root()); + assert_eq!(block_header.height(), 2); + } +} diff --git a/tools/state-viewer/src/contract_accounts.rs b/tools/state-viewer/src/contract_accounts.rs new file mode 100644 index 000000000..f9c2d20e6 --- /dev/null +++ b/tools/state-viewer/src/contract_accounts.rs @@ -0,0 +1,734 @@ +//! State viewer functions to list and filter accounts that have contracts +//! deployed. + +use borsh::BorshDeserialize; +use unc_primitives::hash::CryptoHash; +use unc_primitives::receipt::{Receipt, ReceiptEnum}; +use unc_primitives::transaction::{Action, ExecutionOutcomeWithProof}; +use unc_primitives::trie_key::trie_key_parsers::parse_account_id_from_contract_code_key; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::AccountId; +use unc_store::{DBCol, NibbleSlice, StorageError, Store, Trie, TrieTraversalItem}; +use std::collections::{BTreeMap, BTreeSet, VecDeque}; + +type Result = std::result::Result; + +/// Output type for a complete contract account query. +pub(crate) struct ContractAccountSummary { + pub(crate) contracts: BTreeMap, + pub(crate) errors: Vec, +} + +/// Output type containing info about a single account and its contract. +/// +/// This output can be observed in streaming fashion, with less memory overhead +/// than returning all results in one bulk in `ContractAccountSummary`. While +/// streaming, not all fields are available. +pub(crate) struct ContractAccount { + pub(crate) account_id: AccountId, + pub(crate) info: ContractInfo, +} + +/// Output type containing info about a single contract. +#[derive(Default)] +pub(crate) struct ContractInfo { + /// The WASM source code size in bytes. + /// + /// Available in iterator stream and in the summary. + pub(crate) code_size: Option, + /// Actions that have been observed to be triggered by the contract. + /// + /// Not available in iterator stream, only in the summary. + pub(crate) actions: Option>, + /// Count incoming receipts. + /// + /// Not available in iterator stream, only in the summary. + pub(crate) receipts_in: Option, + /// Count outgoing receipts. + /// + /// Not available in iterator stream, only in the summary. + pub(crate) receipts_out: Option, +} + +/// Describe the desired output of a `ContractAccountIterator`. +/// +/// The default filter displays nothing but the account names, for all accounts +/// in all shards. +/// +/// Selecting specific accounts or skipping accounts with a lot of traffic can +/// speed up the command drastically. +/// +/// By selecting only the required fields, the iterator can be more efficient. +/// For example, finding all invoked actions is very slow. +/// +/// The output can either be streamed or collected into a BTreeMap. In the +/// latter case, you also want to be more conscious of memory consumption and +/// perhaps not include the full contract source code. +#[derive(Default, clap::Parser, Clone)] +pub(crate) struct ContractAccountFilter { + /// Print the size of the source WASM. + #[clap(long)] + pub(crate) code_size: bool, + /// Print the actions invoked from within each contract. + /// + /// Note: This will not include actions from an original transaction. It + /// only looks at the actions inside receipts spawned by other receipts. + /// This allows to look at on-chain actions only, which are more susceptible + /// to gas cost changes. + #[clap(long)] + pub(crate) actions: bool, + /// Print the number of action receipts received. + #[clap(long)] + pub(crate) receipts_in: bool, + /// Print the number of action receipts sent. + #[clap(long)] + pub(crate) receipts_out: bool, + + /// Only produce output for the selected account Ids. + #[clap(long, use_value_delimiter = true)] + select_accounts: Option>, + /// Do not look up details for the accounts listed here. + /// + /// This can be useful to avoid spending a long time reading through + /// receipts that are for some of the largest accounts. + #[clap(long, use_value_delimiter = true)] + skip_accounts: Option>, +} + +pub(crate) struct ContractAccountIterator { + /// Trie nodes that point to the contracts. + contract_nodes: VecDeque, + /// Selects fields to look up. + filter: ContractAccountFilter, + trie: Trie, +} + +#[derive(Debug, thiserror::Error)] +pub(crate) enum ContractAccountError { + #[error("could not parse key {1:?}")] + InvalidKey(#[source] std::io::Error, Vec), + #[error("could not parse receipt value for receipt {1}")] + InvalidReceipt(#[source] std::io::Error, CryptoHash), + #[error("failed loading outgoing receipt {0}")] + MissingOutgoingReceipt(CryptoHash), + #[error("failed loading contract code for account {1}")] + NoCode(#[source] StorageError, AccountId), + #[error("failed parsing a value in col {1}")] + UnparsableValue(#[source] std::io::Error, DBCol), +} + +/// List of supported actions to filter for. +/// +/// When filtering for an action, only those contracts will be listed that have +/// executed that action from within a recorded function call. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +#[repr(u8)] +pub(crate) enum ActionType { + CreateAccount, + DeployContract, + FunctionCall, + Transfer, + Stake, + AddKey, + DeleteKey, + DeleteAccount, + DataReceipt, + Delegate, + RegisterRsa2048Keys, + CreateRsa2048Challenge, +} + +impl ContractAccount { + /// Iterate over all contracts stored in the given trie, in lexicographic + /// order of the account IDs. + pub(crate) fn in_trie( + trie: Trie, + filter: ContractAccountFilter, + ) -> anyhow::Result { + ContractAccountIterator::new(trie, filter) + } + + /// Iterate multiple tries at the same time. + /// + /// Returns an error if any of the tries fail to create an iterator. + pub(crate) fn in_tries( + tries: Vec, + filter: &ContractAccountFilter, + ) -> anyhow::Result>> { + let mut iters = vec![]; + for trie in tries { + let trie_iter = ContractAccountIterator::new(trie, filter.clone())?; + iters.push(trie_iter); + } + let chained_iters = iters.into_iter().flatten(); + Ok(chained_iters) + } + + /// Reads all the fields that can be retrieved efficiently in streaming manner. + /// + /// This will not populate fields, such as the actions field, that require + /// to iterate entire columns. Calling `summary` populates the remaining fields. + fn read_streaming_fields( + account_id: AccountId, + value_hash: CryptoHash, + trie: &Trie, + filter: &ContractAccountFilter, + ) -> Result { + let code = if filter.code_size { + Some( + trie.retrieve_value(&value_hash) + .map_err(|err| ContractAccountError::NoCode(err, account_id.clone()))?, + ) + } else { + None + }; + Ok(Self { + account_id, + info: ContractInfo { code_size: code.map(|bytes| bytes.len()), ..Default::default() }, + }) + } +} + +impl ContractAccountIterator { + pub(crate) fn new(trie: Trie, filter: ContractAccountFilter) -> anyhow::Result { + let mut trie_iter = trie.iter()?; + // TODO(#8376): Consider changing the interface to TrieKey to make this easier. + // `TrieKey::ContractCode` requires a valid `AccountId`, we use "xx" + let key = TrieKey::ContractCode { account_id: "xx".parse()? }.to_vec(); + let (prefix, suffix) = key.split_at(key.len() - 2); + assert_eq!(suffix, "xx".as_bytes()); + + // `visit_nodes_interval` wants nibbles stored in `Vec` as input + let nibbles_before: Vec = NibbleSlice::new(prefix).iter().collect(); + let nibbles_after = { + let mut tmp = nibbles_before.clone(); + *tmp.last_mut().unwrap() += 1; + tmp + }; + + // finally, use trie iterator to find all contract nodes + let vec_of_nodes = trie_iter.visit_nodes_interval(&nibbles_before, &nibbles_after)?; + let contract_nodes = VecDeque::from(vec_of_nodes); + Ok(Self { contract_nodes, filter, trie }) + } +} + +/// Helper trait for blanket implementation, making the iterator composable. +pub(crate) trait Summary { + fn summary(self, store: &Store, filter: &ContractAccountFilter) -> ContractAccountSummary; +} + +impl>> Summary for T { + /// Eagerly evaluate everything selected by the filter, including fields + /// that are not available in the streaming iterator. + fn summary(self, store: &Store, filter: &ContractAccountFilter) -> ContractAccountSummary { + let mut errors = vec![]; + let mut contracts = BTreeMap::new(); + + for result in self { + match result { + Ok(mut contract) => { + // initialize values, we want zero values in the output when nothing is found + contract.info.receipts_in = Some(0); + contract.info.receipts_out = Some(0); + contract.info.actions = Some(BTreeSet::new()); + contracts.insert(contract.account_id, contract.info); + } + Err(e) => { + // Print the error in stderr so that it is immediately + // visible in the terminal when something goes wrong but + // without spoiling stdout. + eprintln!("skipping contract due to {e}"); + // Also store the error in the summary. + errors.push(e); + } + } + } + + eprintln!("Done collecting {} contracts.", contracts.len()); + + let iterate_receipts = filter.actions || filter.receipts_in || filter.receipts_out; + if iterate_receipts { + eprintln!("Iterating all receipts in the database. This might take a while..."); + let mut receipts_done = 0; + for pair in store.iter(unc_store::DBCol::Receipts) { + if let Err(e) = + try_find_actions_spawned_by_receipt(pair, &mut contracts, store, filter) + { + eprintln!("skipping receipt due to {e}"); + errors.push(e); + } + receipts_done += 1; + // give some feedback on progress to the user + if receipts_done % 100_000 == 0 { + eprintln!("Processed {receipts_done} receipts"); + } + } + } + ContractAccountSummary { contracts, errors } + } +} + +/// Given a receipt, search for all actions in its outgoing receipts. +/// +/// Technically, this involves looking up the execution outcome(s) of the +/// receipt and subsequently looking up each receipt in the outgoing receipt id +/// list. Actions found in any of those receipts will be added to the accounts +/// list of actions. +/// +/// If any of the receipts are missing, this will return an error and stop +/// processing receipts that would come later. Changes made to the action set by +/// already processed receipts will not be reverted. +fn try_find_actions_spawned_by_receipt( + raw_kv_pair: std::io::Result<(Box<[u8]>, Box<[u8]>)>, + accounts: &mut BTreeMap, + store: &Store, + filter: &ContractAccountFilter, +) -> Result<()> { + let (raw_key, raw_value) = + raw_kv_pair.map_err(|e| ContractAccountError::UnparsableValue(e, DBCol::Receipts))?; + // key: receipt id (CryptoHash) + let key = CryptoHash::deserialize(&mut raw_value.as_ref()) + .map_err(|e| ContractAccountError::InvalidKey(e, raw_key.to_vec()))?; + // value: Receipt + let receipt = Receipt::deserialize(&mut raw_value.as_ref()) + .map_err(|e| ContractAccountError::InvalidReceipt(e, key))?; + + // Skip refunds. + if receipt.receiver_id.is_system() { + return Ok(()); + } + + // Note: We could use the entry API here to avoid the double hash, but we + // would have to clone the key string. It's unclear which is better, I will + // avoid the entry API because normal contains/get_mut seems simpler. + if accounts.contains_key(&receipt.receiver_id) { + // yes, this is a contract in our map (skip/select filtering has already been applied when constructing the map) + let entry = accounts.get_mut(&receipt.receiver_id).unwrap(); + if filter.receipts_in { + *entry.receipts_in.get_or_insert(0) += 1; + } + // next, check the execution results (one for each block in case of forks) + for pair in store.iter_prefix_ser::( + DBCol::TransactionResultForBlock, + &raw_key, + ) { + let (_key, outcome) = pair.map_err(|e| { + ContractAccountError::UnparsableValue(e, DBCol::TransactionResultForBlock) + })?; + if filter.receipts_out { + *entry.receipts_out.get_or_insert(0) += outcome.outcome.receipt_ids.len(); + } + if filter.actions { + for outgoing_receipt_id in &outcome.outcome.receipt_ids { + let maybe_outgoing_receipt: Option = store + .get_ser(unc_store::DBCol::Receipts, outgoing_receipt_id.as_bytes()) + .map_err(|e| ContractAccountError::UnparsableValue(e, DBCol::Receipts))?; + let outgoing_receipt = maybe_outgoing_receipt.ok_or_else(|| { + ContractAccountError::MissingOutgoingReceipt(*outgoing_receipt_id) + })?; + match outgoing_receipt.receipt { + ReceiptEnum::Action(action_receipt) => { + for action in &action_receipt.actions { + let action_type = match action { + Action::CreateAccount(_) => ActionType::CreateAccount, + Action::DeployContract(_) => ActionType::DeployContract, + Action::FunctionCall(_) => ActionType::FunctionCall, + Action::Transfer(_) => ActionType::Transfer, + Action::Stake(_) => ActionType::Stake, + Action::AddKey(_) => ActionType::AddKey, + Action::DeleteKey(_) => ActionType::DeleteKey, + Action::DeleteAccount(_) => ActionType::DeleteAccount, + Action::Delegate(_) => ActionType::Delegate, + Action::RegisterRsa2048Keys(_) => ActionType::RegisterRsa2048Keys, + Action::CreateRsa2048Challenge(_) => ActionType::CreateRsa2048Challenge, + }; + entry + .actions + .get_or_insert_with(Default::default) + .insert(action_type); + } + } + ReceiptEnum::Data(_) => { + entry + .actions + .get_or_insert_with(Default::default) + .insert(ActionType::DataReceipt); + } + } + } + } + } + } + Ok(()) +} + +impl Iterator for ContractAccountIterator { + type Item = Result; + + fn next(&mut self) -> Option { + while let Some(item) = self.contract_nodes.pop_front() { + // only look at nodes with a value, ignoring intermediate nodes + // without values + if let TrieTraversalItem { hash, key: Some(trie_key) } = item { + let account_id = parse_account_id_from_contract_code_key(&trie_key) + .map_err(|err| ContractAccountError::InvalidKey(err, trie_key.to_vec())); + let Ok(account_id) = account_id else { return Some(Err(account_id.unwrap_err())) }; + + if !self.filter.include_account(&account_id) { + continue; + } + + // Here we can only look at fields that can be efficiently computed in one go. + let contract = ContractAccount::read_streaming_fields( + account_id, + hash, + &self.trie, + &self.filter, + ); + return Some(contract); + } + } + None + } +} + +impl std::fmt::Display for ContractAccount { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmt_account_id_and_info(&self.account_id, &self.info, f) + } +} + +impl std::fmt::Display for ContractAccountSummary { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (account_id, info) in &self.contracts { + fmt_account_id_and_info(account_id, info, f)?; + writeln!(f)?; + } + let num_errors = self.errors.len(); + if num_errors > 0 { + writeln!(f, "And {num_errors} errors:")?; + for err in &self.errors { + writeln!(f, "{err}")?; + } + } else { + writeln!(f)?; + writeln!(f, "Finished without errors!")?; + } + + Ok(()) + } +} + +fn fmt_account_id_and_info( + account_id: &AccountId, + info: &ContractInfo, + f: &mut std::fmt::Formatter<'_>, +) -> std::fmt::Result { + write!(f, "{:<64}", account_id)?; + if let Some(size) = info.code_size { + write!(f, " {:>9}", size)?; + } + if let Some(receipt_in) = info.receipts_in { + write!(f, " {receipt_in:>10}")?; + } + if let Some(receipt_out) = info.receipts_out { + write!(f, " {receipt_out:>10}")?; + } + if let Some(action_set) = &info.actions { + write!(f, " ")?; + for (i, action) in action_set.iter().enumerate() { + if i != 0 { + write!(f, ",")?; + } + write!(f, "{action:?}")?; + } + } + Ok(()) +} + +impl ContractAccountFilter { + pub(crate) fn write_header(&self, out: &mut impl std::io::Write) -> std::io::Result<()> { + write!(out, "{:<64}", "ACCOUNT_ID",)?; + if self.code_size { + write!(out, " {:>9}", "SIZE[B]")?; + } + if self.receipts_in { + write!(out, " {:>10}", "RCPTS_IN",)?; + } + if self.receipts_out { + write!(out, " {:>10}", "RCPTS_OUT",)?; + } + if self.actions { + write!(out, " ACTIONS")?; + } + writeln!(out) + } + + fn include_account(&self, account: &AccountId) -> bool { + if let Some(include) = &self.select_accounts { + include.contains(account) + } else if let Some(exclude) = &self.skip_accounts { + !exclude.contains(account) + } else { + true + } + } + + // If any of the fields are no computable on-the-fly / streaming, then we + // cannot stream. + pub(crate) fn can_stream(&self) -> bool { + !(self.actions || self.receipts_in || self.receipts_out) + } +} + +#[cfg(test)] +mod tests { + use super::{ContractAccount, ContractAccountFilter, Summary}; + use borsh::BorshSerialize; + use unc_crypto::{InMemorySigner, Signer}; + use unc_primitives::hash::CryptoHash; + use unc_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum}; + use unc_primitives::transaction::{ + Action, CreateAccountAction, DeployContractAction, ExecutionMetadata, ExecutionOutcome, + ExecutionOutcomeWithProof, ExecutionStatus, FunctionCallAction, TransferAction, + }; + use unc_primitives::trie_key::TrieKey; + use unc_primitives::types::AccountId; + use unc_store::test_utils::{ + create_test_store, test_populate_store, test_populate_store_rc, test_populate_trie, + TestTriesBuilder, + }; + use unc_store::{DBCol, ShardUId, Store, Trie}; + use std::fmt::Write; + + #[test] + fn test_three_contract_sizes() { + let initial = vec![ + contract_tuple("caroline.near", 3), + contract_tuple("alice.near", 1), + contract_tuple("alice.nearx", 2), + // data right before contracts in trie order + account_tuple("xeno.near", 1), + // data right after contracts in trie order + access_key_tuple("alan.near", 1), + ]; + let trie = create_trie(initial); + + let filter = ContractAccountFilter { code_size: true, ..Default::default() }; + let contract_accounts: Vec<_> = + ContractAccount::in_trie(trie, filter).expect("failed creating iterator").collect(); + assert_eq!(3, contract_accounts.len(), "wrong number of contracts returned by iterator"); + + // expect reordering toe lexicographic order + let contract1 = contract_accounts[0].as_ref().expect("returned error instead of contract"); + let contract2 = contract_accounts[1].as_ref().expect("returned error instead of contract"); + let contract3 = contract_accounts[2].as_ref().expect("returned error instead of contract"); + assert_eq!(contract1.account_id.as_str(), "alice.near"); + assert_eq!(contract2.account_id.as_str(), "alice.nearx"); + assert_eq!(contract3.account_id.as_str(), "caroline.near"); + assert_eq!(contract1.info.code_size, Some(1)); + assert_eq!(contract2.info.code_size, Some(2)); + assert_eq!(contract3.info.code_size, Some(3)); + } + + /// Check basic summary output and make sure the output looks right. + #[test] + fn test_simple_summary() { + let trie_data = vec![contract_tuple("alice.near", 100), contract_tuple("bob.near", 200)]; + let (store, trie) = create_store_and_trie([].into_iter(), &[], trie_data); + + let filter = full_filter(); + let summary = ContractAccount::in_tries(vec![trie], &filter) + .expect("iterator creation") + .summary(&store, &filter); + + let mut buf = vec![]; + filter.write_header(&mut buf).unwrap(); + let mut output = String::from_utf8(buf).unwrap(); + write!(&mut output, "{summary}").unwrap(); + insta::assert_snapshot!(output, @r###" + ACCOUNT_ID SIZE[B] RCPTS_IN RCPTS_OUT ACTIONS + alice.near 100 0 0 + bob.near 200 0 0 + + Finished without errors! + "###); + } + + /// Check summary output that contains a "full" output. + #[test] + fn test_summary() { + // To have an interesting output we need a receipt, for which the + // execution outcome spawns another receipt which has actions in it. + + // This is our original receipt, converted from a transaction, calling + // bob.near::foo(). + let fn_call_receipt_id = CryptoHash::hash_borsh(1); + let fn_call_receipt = create_receipt_with_actions( + "alice.near", + "bob.near", + vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "foo".to_owned(), + args: vec![], + gas: 1000, + deposit: 0, + }))], + ); + + // This is the receipt spawned, with the actions triggered by the + // function call. It is sent by Bob and contains several actions. (And + // executed by Alice but this should not matter for the tests here.) + let outgoing_receipt_id = CryptoHash::hash_borsh(2); + let outgoing_receipt = create_receipt_with_actions( + "bob.near", + "alice.near", + vec![ + Action::Transfer(TransferAction { deposit: 20 }), + Action::CreateAccount(CreateAccountAction {}), + Action::DeployContract(DeployContractAction { code: vec![] }), + ], + ); + + // And the outcome that links the two receipt above together. + let fn_call_outcome = create_execution_outcome(vec![outgoing_receipt_id]); + + // Now prepare data to be inserted to DB, separating ref counted data. + let store_data = [store_tripple( + DBCol::TransactionResultForBlock, + &fn_call_receipt_id, + &fn_call_outcome, + )]; + let store_data_rc = [ + store_tripple(DBCol::Receipts, &fn_call_receipt_id, &fn_call_receipt), + store_tripple(DBCol::Receipts, &outgoing_receipt_id, &outgoing_receipt), + ]; + + let trie_data = vec![contract_tuple("alice.near", 100), contract_tuple("bob.near", 200)]; + let (store, trie) = + create_store_and_trie(store_data.into_iter(), &store_data_rc, trie_data); + + let filter = full_filter(); + let summary = ContractAccount::in_tries(vec![trie], &filter) + .expect("iterator creation") + .summary(&store, &filter); + + let mut buf = vec![]; + filter.write_header(&mut buf).unwrap(); + let mut output = String::from_utf8(buf).unwrap(); + write!(&mut output, "{summary}").unwrap(); + insta::assert_snapshot!(output, @r###" + ACCOUNT_ID SIZE[B] RCPTS_IN RCPTS_OUT ACTIONS + alice.near 100 1 0 + bob.near 200 1 1 CreateAccount,DeployContract,Transfer + + Finished without errors! + "###); + } + + /// Create an in-memory trie with the key-value pairs. + fn create_trie(initial: Vec<(Vec, Option>)>) -> Trie { + create_store_and_trie([].into_iter(), &[], initial).1 + } + + /// Create an in-memory store + trie with key-value pairs for each. + fn create_store_and_trie( + store_data: impl Iterator, Vec)>, + store_data_rc: &[(DBCol, Vec, Vec)], + trie_data: Vec<(Vec, Option>)>, + ) -> (Store, Trie) { + let store = create_test_store(); + test_populate_store(&store, store_data); + test_populate_store_rc(&store, store_data_rc); + let tries = TestTriesBuilder::new().with_store(store.clone()).build(); + let root = + test_populate_trie(&tries, &Trie::EMPTY_ROOT, ShardUId::single_shard(), trie_data); + let trie = tries.get_trie_for_shard(ShardUId::single_shard(), root); + (store, trie) + } + + /// Create a test contract key-value pair to insert in the test trie, with specified amount of bytes. + fn contract_tuple(account: &str, num_bytes: u8) -> (Vec, Option>) { + ( + TrieKey::ContractCode { account_id: account.parse().unwrap() }.to_vec(), + Some(vec![num_bytes; num_bytes as usize]), + ) + } + + /// Create a test account key-value pair to insert in the test trie. + fn account_tuple(account: &str, num: u8) -> (Vec, Option>) { + (TrieKey::Account { account_id: account.parse().unwrap() }.to_vec(), Some(vec![num, num])) + } + + /// Create a test access key key-value pair to insert in the test trie. + fn access_key_tuple(account: &str, num: u8) -> (Vec, Option>) { + ( + TrieKey::AccessKey { + account_id: account.parse().unwrap(), + public_key: unc_crypto::PublicKey::empty(unc_crypto::KeyType::ED25519), + } + .to_vec(), + Some(vec![num, num, num, num]), + ) + } + + /// Creates an `ExecutionOutcomeWithProof` with the given outgoing receipts. + /// + /// Note that details such as the proof are invalid. + fn create_execution_outcome(receipt_ids: Vec) -> ExecutionOutcomeWithProof { + ExecutionOutcomeWithProof { + proof: vec![], + outcome: ExecutionOutcome { + logs: vec![], + receipt_ids, + gas_burnt: 100, + compute_usage: Some(200), + tokens_burnt: 2000, + executor_id: "someone.near".parse().unwrap(), + status: ExecutionStatus::SuccessValue(vec![]), + metadata: ExecutionMetadata::default(), + }, + } + } + + /// Convenience fn to create a triple to insert to the store. + fn store_tripple( + col: DBCol, + key: &impl BorshSerialize, + value: &impl BorshSerialize, + ) -> (DBCol, Vec, Vec) { + (col, borsh::to_vec(key).unwrap(), borsh::to_vec(value).unwrap()) + } + + /// A filter that collects all data. + fn full_filter() -> ContractAccountFilter { + ContractAccountFilter { + code_size: true, + actions: true, + receipts_in: true, + receipts_out: true, + select_accounts: None, + skip_accounts: None, + } + } + + /// Create a test receipt from sender to receiver with the given actions. + fn create_receipt_with_actions(sender: &str, receiver: &str, actions: Vec) -> Receipt { + let sender_id: AccountId = sender.parse().unwrap(); + let signer = + InMemorySigner::from_seed(sender_id.clone(), unc_crypto::KeyType::ED25519, "seed"); + Receipt { + predecessor_id: sender_id.clone(), + receiver_id: receiver.parse().unwrap(), + receipt_id: CryptoHash::default(), + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: sender_id, + signer_public_key: signer.public_key(), + gas_price: 2, + output_data_receivers: vec![], + input_data_ids: vec![], + actions, + }), + } + } +} diff --git a/tools/state-viewer/src/epoch_info.rs b/tools/state-viewer/src/epoch_info.rs new file mode 100644 index 000000000..e7189733d --- /dev/null +++ b/tools/state-viewer/src/epoch_info.rs @@ -0,0 +1,305 @@ +use borsh::BorshDeserialize; +use core::ops::Range; +use itertools::Itertools; +use unc_chain::{ChainStore, ChainStoreAccess, Error}; +use unc_epoch_manager::{EpochManagerAdapter, EpochManagerHandle}; +use unc_primitives::account::id::AccountId; +use unc_primitives::epoch_manager::epoch_info::EpochInfo; +use unc_primitives::epoch_manager::AGGREGATOR_KEY; +use unc_primitives::hash::CryptoHash; +use unc_primitives::types::{BlockHeight, EpochHeight, EpochId, ProtocolVersion, ShardId}; +use unc_store::{DBCol, Store}; +use std::str::FromStr; +use std::sync::Arc; + +#[derive(clap::Subcommand, Debug, Clone)] +pub(crate) enum EpochSelection { + /// Current epoch. + Current, + /// All epochs. + All, + /// Fetch the given epoch. + EpochId { epoch_id: String }, + /// Fetch epochs at the given height. + EpochHeight { epoch_height: EpochHeight }, + /// Fetch an epoch containing the given block hash. + BlockHash { block_hash: String }, + /// Fetch an epoch containing the given block height. + BlockHeight { block_height: BlockHeight }, + /// Fetch all epochs with the given protocol version. + ProtocolVersion { protocol_version: ProtocolVersion }, +} + +pub(crate) fn print_epoch_info( + epoch_selection: EpochSelection, + validator_account_id: Option, + kickouts_summary: bool, + store: Store, + chain_store: &ChainStore, + epoch_manager: &EpochManagerHandle, +) { + let epoch_ids = get_epoch_ids(epoch_selection, store, chain_store, epoch_manager); + + let head_block_info = + epoch_manager.get_block_info(&chain_store.head().unwrap().last_block_hash).unwrap(); + let head_epoch_height = + epoch_manager.get_epoch_info(head_block_info.epoch_id()).unwrap().epoch_height(); + let mut epoch_infos: Vec<(EpochId, Arc)> = epoch_ids + .iter() + .map(|epoch_id| (epoch_id.clone(), epoch_manager.get_epoch_info(epoch_id).unwrap())) + .collect(); + // Sorted output is much easier to follow. + epoch_infos.sort_by_key(|(_, epoch_info)| epoch_info.epoch_height()); + + for (epoch_id, epoch_info) in &epoch_infos { + println!("-------------------------"); + println!("EpochId: {}", epoch_id.0); + if kickouts_summary { + display_kickouts(epoch_info); + } else { + if let Err(err) = display_epoch_info( + epoch_id, + epoch_info, + &validator_account_id, + &head_epoch_height, + chain_store, + epoch_manager, + ) { + println!("Can't display Epoch Info: {err:?}"); + continue; + } + println!("---"); + if let Err(err) = + display_block_and_chunk_producers(epoch_id, epoch_info, chain_store, epoch_manager) + { + println!("Can't display Epoch Info: {err:?}"); + continue; + } + } + } + println!("========================="); + println!("Found {} epochs", epoch_ids.len()); +} + +fn display_block_and_chunk_producers( + epoch_id: &EpochId, + epoch_info: &EpochInfo, + chain_store: &ChainStore, + epoch_manager: &EpochManagerHandle, +) -> anyhow::Result<()> { + let block_height_range: Range = + get_block_height_range(epoch_id, chain_store, epoch_manager)?; + let shard_ids = epoch_manager.shard_ids(epoch_id).unwrap(); + for block_height in block_height_range { + let bp = epoch_info.sample_block_producer(block_height); + let bp = epoch_info.get_validator(bp).account_id().clone(); + let cps: Vec = shard_ids + .iter() + .map(|&shard_id| { + let cp = epoch_info.sample_chunk_producer(block_height, shard_id); + let cp = epoch_info.get_validator(cp).account_id().clone(); + cp.as_str().to_string() + }) + .collect(); + println!("{block_height}: BP=\"{bp}\" CP={cps:?}"); + } + Ok(()) +} + +// Iterate over each epoch starting from the head. Find the requested epoch and its previous epoch +// and use that to determine the block range corresponding to the epoch. +fn get_block_height_range( + epoch_id: &EpochId, + chain_store: &ChainStore, + epoch_manager: &EpochManagerHandle, +) -> anyhow::Result> { + let head = chain_store.head()?; + let mut cur_block_info = epoch_manager.get_block_info(&head.last_block_hash)?; + loop { + if cur_block_info.epoch_id() == epoch_id { + // Found the requested epoch. + let epoch_start_height = epoch_manager.get_epoch_start_height(cur_block_info.hash())?; + if &head.epoch_id == epoch_id { + // This is the current epoch and we can't know when exactly it will end. + // Estimate that the epoch should end at this height. + let next_epoch_start_height = + epoch_start_height + epoch_manager.get_epoch_config(epoch_id)?.epoch_length; + return Ok( + epoch_start_height..std::cmp::max(head.height + 1, next_epoch_start_height) + ); + } + // Head is in a different epoch, therefore the requested epoch must be complete. + // Iterate over block heights to find the first block in a different epoch. + for height in epoch_start_height..=head.height { + match chain_store.get_block_hash_by_height(height) { + Ok(hash) => { + let header = chain_store.get_block_header(&hash)?; + if header.epoch_id() != epoch_id { + return Ok(epoch_start_height..height); + } + } + Err(Error::DBNotFoundErr(_)) => { + // Block is missing, skip. + continue; + } + Err(err) => { + panic!("Failed to get block hash at height {height}: {err:?}"); + } + }; + } + return Ok(epoch_start_height..head.height + 1); + } else { + // Go to the previous epoch. + let first_block_info = + epoch_manager.get_block_info(cur_block_info.epoch_first_block())?; + let prev_block_info = epoch_manager.get_block_info(first_block_info.prev_hash())?; + cur_block_info = prev_block_info; + } + } +} + +// Converts a bunch of optional filtering options into a vector of EpochIds. +fn get_epoch_ids( + epoch_selection: EpochSelection, + store: Store, + chain_store: &ChainStore, + epoch_manager: &EpochManagerHandle, +) -> Vec { + match epoch_selection { + EpochSelection::All => iterate_and_filter(store, |_| true), + EpochSelection::Current => { + let epoch_id = + epoch_manager.get_epoch_id(&chain_store.head().unwrap().last_block_hash).unwrap(); + vec![epoch_id] + } + EpochSelection::EpochId { epoch_id } => { + let epoch_id = EpochId(CryptoHash::from_str(&epoch_id).unwrap()); + vec![epoch_id] + } + EpochSelection::EpochHeight { epoch_height } => { + // Fetch epochs at the given height. + // There should only be one epoch at a given height. But this is a debug tool, let's check + // if there are multiple epochs at a given height. + iterate_and_filter(store, |epoch_info| epoch_info.epoch_height() == epoch_height) + } + EpochSelection::BlockHash { block_hash } => { + let block_hash = CryptoHash::from_str(&block_hash).unwrap(); + vec![epoch_manager.get_epoch_id(&block_hash).unwrap()] + } + EpochSelection::BlockHeight { block_height } => { + // Fetch an epoch containing the given block height. + let block_hash = chain_store.get_block_hash_by_height(block_height).unwrap(); + vec![epoch_manager.get_epoch_id(&block_hash).unwrap()] + } + EpochSelection::ProtocolVersion { protocol_version } => { + // Fetch the first epoch of the given protocol version. + iterate_and_filter(store, |epoch_info| { + epoch_info.protocol_version() == protocol_version + }) + } + } +} + +// Iterates over the DBCol::EpochInfo column, ignores AGGREGATOR_KEY and returns deserialized EpochId +// for EpochInfos that satisfy the given predicate. +pub(crate) fn iterate_and_filter( + store: Store, + predicate: impl Fn(EpochInfo) -> bool, +) -> Vec { + store + .iter(DBCol::EpochInfo) + .map(Result::unwrap) + .filter_map(|(key, value)| { + if key.as_ref() == AGGREGATOR_KEY { + None + } else { + let epoch_info = EpochInfo::try_from_slice(value.as_ref()).unwrap(); + if predicate(epoch_info) { + Some(EpochId::try_from_slice(key.as_ref()).unwrap()) + } else { + None + } + } + }) + .collect() +} + +fn display_kickouts(epoch_info: &EpochInfo) { + for (account_id, kickout_reason) in + epoch_info.validator_kickout().iter().sorted_by_key(|&(account_id, _)| account_id) + { + println!("{account_id:?}: {kickout_reason:?}"); + } +} + +fn display_epoch_info( + epoch_id: &EpochId, + epoch_info: &EpochInfo, + validator_account_id: &Option, + head_epoch_height: &EpochHeight, + chain_store: &ChainStore, + epoch_manager: &EpochManagerHandle, +) -> anyhow::Result<()> { + if epoch_info.epoch_height() >= *head_epoch_height { + println!("Epoch information for this epoch is not yet available, skipping."); + return Ok(()); + } + println!("Epoch Height: {}", epoch_info.epoch_height()); + println!("Protocol Version: {}", epoch_info.protocol_version()); + let block_height_range = get_block_height_range(epoch_id, chain_store, epoch_manager)?; + println!("Epoch Height Range: [{}..{})", block_height_range.start, block_height_range.end); + if let Some(account_id) = validator_account_id.clone() { + display_validator_info(epoch_id, epoch_info, account_id, chain_store, epoch_manager)?; + } + Ok(()) +} + +fn display_validator_info( + epoch_id: &EpochId, + epoch_info: &EpochInfo, + account_id: AccountId, + chain_store: &ChainStore, + epoch_manager: &EpochManagerHandle, +) -> anyhow::Result<()> { + if let Some(kickout) = epoch_info.validator_kickout().get(&account_id) { + println!("Validator {account_id} kickout: {kickout:#?}"); + } + if let Some(validator_id) = epoch_info.get_validator_id(&account_id) { + let block_height_range: Range = + get_block_height_range(epoch_id, chain_store, epoch_manager)?; + let bp_for_blocks: Vec = block_height_range + .clone() + .filter(|&block_height| epoch_info.sample_block_producer(block_height) == *validator_id) + .collect(); + println!("Block producer for {} blocks: {bp_for_blocks:?}", bp_for_blocks.len()); + + let shard_ids = epoch_manager.shard_ids(epoch_id).unwrap(); + let cp_for_chunks: Vec<(BlockHeight, ShardId)> = block_height_range + .flat_map(|block_height| { + shard_ids + .iter() + .map(|&shard_id| (block_height, shard_id)) + .filter(|&(block_height, shard_id)| { + epoch_info.sample_chunk_producer(block_height, shard_id) == *validator_id + }) + .collect::>() + }) + .collect(); + println!("Chunk producer for {} chunks: {cp_for_chunks:?}", cp_for_chunks.len()); + let mut missing_chunks = vec![]; + for (block_height, shard_id) in cp_for_chunks { + if let Ok(block_hash) = chain_store.get_block_hash_by_height(block_height) { + let block = chain_store.get_block(&block_hash).unwrap(); + if block.chunks()[shard_id as usize].height_included() != block_height { + missing_chunks.push((block_height, shard_id)); + } + } else { + missing_chunks.push((block_height, shard_id)); + } + } + println!("Missing {} chunks: {missing_chunks:?}", missing_chunks.len()); + } else { + println!("Validator {account_id} didn't validate in epoch #{}", epoch_info.epoch_height()); + } + Ok(()) +} diff --git a/tools/state-viewer/src/lib.rs b/tools/state-viewer/src/lib.rs new file mode 100644 index 000000000..36aa5519b --- /dev/null +++ b/tools/state-viewer/src/lib.rs @@ -0,0 +1,17 @@ +#![doc = include_str!("../README.md")] + +mod apply_chain_range; +mod apply_chunk; +pub mod cli; +mod commands; +mod contract_accounts; +mod epoch_info; +mod rocksdb_stats; +mod scan_db; +mod state_changes; +mod state_dump; +mod state_parts; +mod trie_iteration_benchmark; +mod tx_dump; + +pub use cli::StateViewerSubCommand; diff --git a/tools/state-viewer/src/rocksdb_stats.rs b/tools/state-viewer/src/rocksdb_stats.rs new file mode 100644 index 000000000..1cf29b797 --- /dev/null +++ b/tools/state-viewer/src/rocksdb_stats.rs @@ -0,0 +1,122 @@ +use std::collections::HashMap; +use std::path::{Path, PathBuf}; +use std::process::Command; + +#[derive(serde::Serialize, Debug)] +struct SSTFileData { + col: String, + entries: u64, + estimated_table_size: u64, + // Total size of all keys in bytes. + raw_key_size: u64, + // Total size of all values in bytes. + raw_value_size: u64, +} + +// SST file dump keys we use to collect statistics. +const SST_FILE_DUMP_LINES: [&str; 5] = + ["column family name", "# entries", "(estimated) table size", "raw key size", "raw value size"]; + +/// Statistics about a single SST file. +impl SSTFileData { + fn for_sst_file(file_path: &Path) -> anyhow::Result { + let mut file_arg = std::ffi::OsString::from("--file="); + file_arg.push(file_path); + + let mut cmd = Command::new("sst_dump"); + // For some reason, adding --command=none makes execution 20x faster. + cmd.arg(file_arg).arg("--show_properties").arg("--command=none"); + + let output = cmd.output()?; + anyhow::ensure!( + output.status.success(), + "failed to run sst_dump, {}, stderr: {}", + output.status, + String::from_utf8_lossy(&output.stderr) + ); + + Self::from_sst_file_dump(std::str::from_utf8(&output.stdout).unwrap()) + } + + fn from_sst_file_dump(output: &str) -> anyhow::Result { + // Mapping from SST file dump key to value. + let mut values: HashMap<&str, Option<&str>> = + SST_FILE_DUMP_LINES.iter().map(|key| (*key, None)).collect(); + + for line in output.lines() { + let (key, value) = match line.split_once(':') { + None => continue, + Some((key, value)) => (key.trim(), value.trim()), + }; + + if let Some(entry) = values.get_mut(key) { + if let Some(prev) = entry { + anyhow::bail!( + "Key {key} was presented twice and \ + contains values {prev} and {value}" + ); + } + let _ = entry.insert(value); + } + } + + let get_u64 = + |idx| values.get(SST_FILE_DUMP_LINES[idx]).unwrap().unwrap().parse::().unwrap(); + + Ok(SSTFileData { + col: values.get(SST_FILE_DUMP_LINES[0]).unwrap().unwrap().to_owned(), + entries: get_u64(1), + estimated_table_size: get_u64(2), + raw_key_size: get_u64(3), + raw_value_size: get_u64(4), + }) + } + + fn merge(&mut self, other: &Self) { + self.entries += other.entries; + self.estimated_table_size += other.estimated_table_size; + self.raw_key_size += other.raw_key_size; + self.raw_value_size += other.raw_value_size; + } +} + +/// Merged statistics for all columns. +struct ColumnsData(HashMap); + +impl ColumnsData { + fn new() -> Self { + Self(Default::default()) + } + + fn add_sst_data(&mut self, data: SSTFileData) { + self.0.entry(data.col.clone()).and_modify(|entry| entry.merge(&data)).or_insert(data); + } + + fn into_vec(self) -> Vec { + let mut values: Vec<_> = self.0.into_values().collect(); + values.sort_by_key(|data| std::cmp::Reverse(data.estimated_table_size)); + values + } +} + +pub fn get_rocksdb_stats(store_dir: &Path, file: Option) -> anyhow::Result<()> { + let mut data = ColumnsData::new(); + + for entry in std::fs::read_dir(store_dir)? { + let entry = entry?; + let file_name = std::path::PathBuf::from(entry.file_name()); + if file_name.extension().map_or(false, |ext| ext == "sst") { + let file_path = store_dir.join(file_name); + eprintln!("Processing ‘{}’...", file_path.display()); + data.add_sst_data(SSTFileData::for_sst_file(&file_path)?); + } + } + + eprintln!("Dumping stats ..."); + let result = serde_json::to_string_pretty(&data.into_vec()).unwrap(); + match file { + None => println!("{}", result), + Some(file) => std::fs::write(file, result)?, + } + Ok(()) +} diff --git a/tools/state-viewer/src/scan_db.rs b/tools/state-viewer/src/scan_db.rs new file mode 100644 index 000000000..3d8ebc6db --- /dev/null +++ b/tools/state-viewer/src/scan_db.rs @@ -0,0 +1,250 @@ +use borsh::BorshDeserialize; +use unc_chain::types::LatestKnown; +use unc_epoch_manager::types::EpochInfoAggregator; +use unc_primitives::block::{Block, BlockHeader, Tip}; +use unc_primitives::epoch_manager::block_info::BlockInfo; +use unc_primitives::epoch_manager::epoch_info::EpochInfo; +use unc_primitives::epoch_manager::AGGREGATOR_KEY; +use unc_primitives::receipt::Receipt; +use unc_primitives::shard_layout::{get_block_shard_uid_rev, ShardUId}; +use unc_primitives::sharding::{ChunkHash, ReceiptProof, ShardChunk, StateSyncInfo}; +use unc_primitives::state::FlatStateValue; +use unc_primitives::state_sync::{ + ShardStateSyncResponseHeader, StateHeaderKey, StatePartKey, StateSyncDumpProgress, +}; +use unc_primitives::transaction::{ExecutionOutcomeWithProof, SignedTransaction}; +use unc_primitives::types::chunk_extra::ChunkExtra; +use unc_primitives::types::{EpochId, StateRoot}; +use unc_primitives::utils::{get_block_shard_id_rev, get_outcome_id_block_hash_rev}; +use unc_primitives_core::hash::CryptoHash; +use unc_primitives_core::types::BlockHeight; +use unc_store::flat::delta::KeyForFlatStateDelta; +use unc_store::flat::{FlatStateChanges, FlatStateDeltaMetadata}; +use unc_store::{DBCol, RawTrieNodeWithSize, Store, TrieChanges}; +use std::collections::HashSet; +use std::fmt::Debug; +use strum::IntoEnumIterator; + +fn find_db_col(col: &str) -> DBCol { + for db_col in DBCol::iter() { + if format!("{}", db_col) == col { + return db_col; + } + } + panic!("Wrong columnn") +} + +/// Scans a DB column, deserializes keys and values and prints them. +/// Note that this implementation doesn't support all columns, and formatting is the best it can be. +/// Refcounting is hard, and is handled by lookup by keys during a scan. +pub(crate) fn scan_db_column( + col: &str, + lower_bound: Option<&[u8]>, + upper_bound: Option<&[u8]>, + max_keys: Option, + no_value: bool, + store: Store, +) { + let db_col: DBCol = find_db_col(col); + tracing::info!(target: "scan", ?db_col); + for item in + store.iter_range(db_col, lower_bound, upper_bound).take(max_keys.unwrap_or(usize::MAX)) + { + let (key, value) = item.unwrap(); + let (key_ser, value_ser) = + format_key_and_value(key.as_ref(), value.as_ref(), db_col, &store); + if no_value { + println!("{:?}", key_ser); + } else { + println!("{:?}: {:?}", key_ser, value_ser); + } + } +} + +// TODO: Support more columns. +fn format_key_and_value<'a>( + key: &'a [u8], + value: &'a [u8], + db_col: DBCol, + store: &'a Store, +) -> (Box, Box) { + match db_col { + DBCol::Block => ( + Box::new(CryptoHash::try_from(key).unwrap()), + Box::new(Block::try_from_slice(value).unwrap()), + ), + DBCol::BlockHeader => ( + Box::new(CryptoHash::try_from(key).unwrap()), + Box::new(BlockHeader::try_from_slice(value).unwrap()), + ), + DBCol::BlockHeight => ( + Box::new(BlockHeight::try_from_slice(key).unwrap()), + Box::new(CryptoHash::try_from(value).unwrap()), + ), + DBCol::BlockInfo => ( + Box::new(CryptoHash::try_from(key).unwrap()), + Box::new(BlockInfo::try_from_slice(value).unwrap()), + ), + DBCol::BlockMisc => ( + Box::new(String::from_utf8_lossy(key).to_string()), + format_block_misc_value(key, value), + ), + DBCol::BlockRefCount => ( + Box::new(CryptoHash::try_from(key).unwrap()), + Box::new(u64::try_from_slice(value).unwrap()), + ), + DBCol::BlocksToCatchup => ( + Box::new(CryptoHash::try_from(key).unwrap()), + Box::new(Vec::::try_from_slice(value).unwrap()), + ), + DBCol::ChunkExtra => ( + Box::new(get_block_shard_uid_rev(key).unwrap()), + Box::new(ChunkExtra::try_from_slice(value).unwrap()), + ), + DBCol::ChunkHashesByHeight => ( + // TODO: Fix + Box::new(BlockHeight::try_from_slice(key).unwrap()), + Box::new(CryptoHash::try_from(value).unwrap()), + ), + DBCol::Chunks => ( + Box::new(ChunkHash::try_from_slice(key).unwrap()), + Box::new(ShardChunk::try_from_slice(value).unwrap()), + ), + DBCol::DbVersion => ( + Box::new(String::from_utf8_lossy(key).to_string()), + Box::new(String::from_utf8_lossy(value).to_string()), + ), + DBCol::EpochInfo => { + if key != AGGREGATOR_KEY { + ( + Box::new(EpochId::try_from_slice(key).unwrap()), + Box::new(EpochInfo::try_from_slice(value).unwrap()), + ) + } else { + ( + Box::new(String::from_utf8_lossy(key).to_string()), + Box::new(EpochInfoAggregator::try_from_slice(value).unwrap()), + ) + } + } + DBCol::EpochStart => ( + // TODO: Fix + Box::new(EpochId::try_from_slice(key).unwrap()), + Box::new(BlockHeight::try_from_slice(value).unwrap()), + ), + DBCol::FlatState => { + let (shard_uid, key) = + unc_store::flat::store_helper::decode_flat_state_db_key(key).unwrap(); + (Box::new((shard_uid, key)), Box::new(FlatStateValue::try_from_slice(value).unwrap())) + } + DBCol::FlatStateChanges => ( + // TODO: Format keys as nibbles. + Box::new(KeyForFlatStateDelta::try_from_slice(key).unwrap()), + Box::new(FlatStateChanges::try_from_slice(value).unwrap()), + ), + DBCol::FlatStateDeltaMetadata => ( + // TODO: Format keys as nibbles. + Box::new(KeyForFlatStateDelta::try_from_slice(key).unwrap()), + Box::new(FlatStateDeltaMetadata::try_from_slice(value).unwrap()), + ), + DBCol::FlatStorageStatus => ( + // TODO: Format keys as nibbles. + Box::new(ShardUId::try_from_slice(key).unwrap()), + Box::new(unc_store::flat::FlatStorageStatus::try_from_slice(value).unwrap()), + ), + DBCol::HeaderHashesByHeight => ( + Box::new(BlockHeight::try_from_slice(key).unwrap()), + Box::new(HashSet::::try_from_slice(value).unwrap()), + ), + DBCol::IncomingReceipts => ( + Box::new(get_block_shard_id_rev(key).unwrap()), + Box::new(Vec::::try_from_slice(value).unwrap()), + ), + DBCol::OutgoingReceipts => ( + Box::new(get_block_shard_id_rev(key).unwrap()), + Box::new(Vec::::try_from_slice(value).unwrap()), + ), + DBCol::OutcomeIds => ( + Box::new(get_block_shard_id_rev(key).unwrap()), + Box::new(Vec::::try_from_slice(value).unwrap()), + ), + DBCol::Receipts => { + // Handle refcounting by querying the value. + let value = store.get(db_col, key).unwrap().unwrap(); + ( + Box::new(CryptoHash::try_from(key).unwrap()), + Box::new(Receipt::try_from_slice(&value).unwrap()), + ) + } + DBCol::State => { + // This logic is exactly the same as KeyForFlatStateDelta. + let s: ShardUId = ShardUId::try_from(&key[..8]).unwrap(); + let h: CryptoHash = CryptoHash::try_from_slice(&key[8..]).unwrap(); + // Handle refcounting by querying the value. + let value = store.get(db_col, key).unwrap().unwrap(); + let res = if let Ok(node) = RawTrieNodeWithSize::try_from_slice(&value) { + format!("Node: {node:?}") + } else { + format!("Value: {value:?}") + }; + (Box::new((s, h)), Box::new(res)) + } + DBCol::StateDlInfos => ( + Box::new(CryptoHash::try_from(key).unwrap()), + Box::new(StateSyncInfo::try_from_slice(value).unwrap()), + ), + DBCol::StateHeaders => ( + Box::new(StateHeaderKey::try_from_slice(key).unwrap()), + Box::new(ShardStateSyncResponseHeader::try_from_slice(value).unwrap()), + ), + DBCol::StateParts => ( + Box::new(StatePartKey::try_from_slice(key).unwrap()), + // TODO: Print the trie containing in the state part. + Box::new(value), + ), + DBCol::TransactionResultForBlock => ( + Box::new(get_outcome_id_block_hash_rev(key).unwrap()), + Box::new(ExecutionOutcomeWithProof::try_from_slice(value).unwrap()), + ), + DBCol::Transactions => { + // Handle refcounting by querying the value. + let value = store.get(db_col, key).unwrap().unwrap(); + ( + Box::new(CryptoHash::try_from(key).unwrap()), + Box::new(SignedTransaction::try_from_slice(&value).unwrap()), + ) + } + DBCol::TrieChanges => ( + Box::new(get_block_shard_uid_rev(key).unwrap()), + Box::new(TrieChanges::try_from_slice(value).unwrap()), + ), + _ => (Box::new(key), Box::new(value)), + } +} + +fn format_block_misc_value<'a>(key: &'a [u8], value: &'a [u8]) -> Box { + if key == unc_store::HEAD_KEY + || key == unc_store::HEADER_HEAD_KEY + || key == unc_store::FINAL_HEAD_KEY + || key == unc_store::COLD_HEAD_KEY + || key == b"SYNC_HEAD" + { + Box::new(Tip::try_from_slice(value).unwrap()) + } else if key == unc_store::TAIL_KEY + || key == unc_store::CHUNK_TAIL_KEY + || key == unc_store::FORK_TAIL_KEY + || key == unc_store::LARGEST_TARGET_HEIGHT_KEY + { + Box::new(BlockHeight::try_from_slice(value).unwrap()) + } else if key == unc_store::LATEST_KNOWN_KEY { + Box::new(LatestKnown::try_from_slice(value).unwrap()) + } else if key == unc_store::GENESIS_JSON_HASH_KEY { + Box::new(CryptoHash::try_from(value).unwrap()) + } else if key == unc_store::GENESIS_STATE_ROOTS_KEY { + Box::new(Vec::::try_from_slice(value).unwrap()) + } else if key.starts_with(unc_store::STATE_SYNC_DUMP_KEY) { + Box::new(StateSyncDumpProgress::try_from_slice(value).unwrap()) + } else { + Box::new(value) + } +} diff --git a/tools/state-viewer/src/state_changes.rs b/tools/state-viewer/src/state_changes.rs new file mode 100644 index 000000000..74a03297d --- /dev/null +++ b/tools/state-viewer/src/state_changes.rs @@ -0,0 +1,224 @@ +use borsh::BorshDeserialize; +use unc_chain::types::RuntimeAdapter; +use unc_chain::{ChainStore, ChainStoreAccess}; +use unc_epoch_manager::{EpochManager, EpochManagerAdapter}; +use unc_primitives::hash::CryptoHash; +use unc_primitives::trie_key::TrieKey; +use unc_primitives::types::{ + EpochId, ShardId, StateChangesForBlock, StateChangesForBlockRange, StateChangesForShard, + StateRoot, +}; +use unc_primitives_core::types::BlockHeight; +use unc_store::{KeyForStateChanges, Store, WrappedTrieChanges}; +use framework::{UncConfig, NightshadeRuntime}; +use std::path::{Path, PathBuf}; + +#[derive(clap::Subcommand, Debug, Clone)] +pub(crate) enum StateChangesSubCommand { + /// Applies StateChanges from a file. + /// Needs state roots because chunks state roots may not be known. + /// Needs BlockHeaders available for the corresponding blocks. + Apply { + /// Location of the input file. + /// The file must be generated by the Dump subcommand. + #[clap(value_parser)] + file: PathBuf, + /// Id of the shard to apply state changes to. + shard_id: ShardId, + /// State root of the shard at the height of the first block with state changes. + state_root: StateRoot, + }, + /// Dumps state changes for a range of blocks from --height-from to --height-to inclusive. + /// The dump will include all available state changes, i.e. of shards tracked at the time. + Dump { + /// --height-from defines the lower (inclusive) bound of a range of block heights to dump. + height_from: BlockHeight, + /// --height-to defines the upper (inclusive) bound of a range of block heights to dump. + height_to: BlockHeight, + /// Location of the output file. + #[clap(value_parser)] + file: PathBuf, + }, +} + +impl StateChangesSubCommand { + pub(crate) fn run(self, home_dir: &Path, unc_config: UncConfig, store: Store) { + match self { + StateChangesSubCommand::Apply { file, shard_id, state_root } => { + apply_state_changes(file, shard_id, state_root, home_dir, unc_config, store) + } + StateChangesSubCommand::Dump { height_from, height_to, file } => { + dump_state_changes(height_from, height_to, file, unc_config, store) + } + } + } +} + +/// Reads StateChanges from the DB for the specified range of blocks. +/// Writes these state changes to the specified file. +/// The data written is borsh-serialized StateChangesForBlockRange. +/// State changes in that file are expected to be ordered in the increasing order of height. +/// Changes are grouped by (block_hash, shard_id). +/// If a block or a shard has no changes, they can be omitted from the serialization. +/// Row key of the StateChanges column are really important because the logic relies that state +/// changes of kinds DelayedReceipt and DelayedReceiptIndices have shard id encoded in the row key. +fn dump_state_changes( + height_from: BlockHeight, + height_to: BlockHeight, + file: PathBuf, + unc_config: UncConfig, + store: Store, +) { + assert!(height_from <= height_to, "--height-from must be less than or equal to --height-to"); + + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &unc_config.genesis.config); + let chain_store = ChainStore::new( + store.clone(), + unc_config.genesis.config.genesis_height, + unc_config.client_config.save_trie_changes, + ); + + let blocks = (height_from..=height_to).filter_map(|block_height| { + let block_header = chain_store.get_block_header_by_height(block_height).unwrap(); + let block_hash = block_header.hash(); + let epoch_id = block_header.epoch_id(); + let key = KeyForStateChanges::for_block(block_header.hash()); + let mut state_changes_per_shard: Vec<_> = + epoch_manager.shard_ids(epoch_id).unwrap().into_iter().map(|_| vec![]).collect(); + + for row in key.find_rows_iter(&store) { + let (key, value) = row.unwrap(); + let shard_id = get_state_change_shard_id(key.as_ref(), &value.trie_key, block_hash, epoch_id, epoch_manager.as_ref()).unwrap(); + state_changes_per_shard[shard_id as usize].push(value); + } + + tracing::info!(target: "state-changes", block_height = block_header.height(), num_state_changes_per_shard = ?state_changes_per_shard.iter().map(|v|v.len()).collect::>()); + let state_changes : Vec = state_changes_per_shard.into_iter().enumerate().filter_map(|(shard_id,state_changes)|{ + if state_changes.is_empty() { + // Skip serializing state changes for a shard if no state changes were found for this shard in this block. + None + } else { + Some(StateChangesForShard{shard_id:shard_id as ShardId, state_changes}) + } + }).collect(); + + if state_changes.is_empty() { + // Skip serializing state changes for a block if no state changes were found for this block. + None + } else { + Some(StateChangesForBlock { block_hash: *block_hash, state_changes }) + } + }).collect(); + + let state_changes_for_block_range = StateChangesForBlockRange { blocks }; + + tracing::info!(target: "state-changes", ?file, "Writing state changes to a file"); + let data: Vec = borsh::to_vec(&state_changes_for_block_range).unwrap(); + std::fs::write(&file, data).unwrap(); +} + +/// Reads StateChanges from a file. Applies StateChanges in the order of increasing block height. +/// +/// The file is assumed to be created by `dump_state_changes`. Same assumptions apply. +/// Row key of the StateChanges column are really important because the logic relies that state +/// changes of kinds DelayedReceipt and DelayedReceiptIndices have shard id encoded in the row key. +/// +/// The operation needs state viewer to be run in read-write mode, use `--readwrite` flag. +/// +/// In case the DB contains state roots of chunks, the process compares resulting StateRoots with those known StateRoots. +fn apply_state_changes( + file: PathBuf, + shard_id: ShardId, + mut state_root: StateRoot, + home_dir: &Path, + unc_config: UncConfig, + store: Store, +) { + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &unc_config.genesis.config); + let runtime = NightshadeRuntime::from_config( + home_dir, + store.clone(), + &unc_config, + epoch_manager.clone(), + ); + let mut chain_store = ChainStore::new( + store, + unc_config.genesis.config.genesis_height, + unc_config.client_config.save_trie_changes, + ); + + let data = std::fs::read(&file).unwrap(); + let state_changes_for_block_range = StateChangesForBlockRange::try_from_slice(&data).unwrap(); + + for StateChangesForBlock { block_hash, state_changes } in state_changes_for_block_range.blocks { + let block_header = chain_store.get_block_header(&block_hash).unwrap(); + let block_hash = block_header.hash(); + let block_height = block_header.height(); + let epoch_id = block_header.epoch_id(); + let shard_uid = epoch_manager.shard_id_to_uid(shard_id, epoch_id).unwrap(); + + for StateChangesForShard { shard_id: state_change_shard_id, state_changes } in state_changes + { + if state_change_shard_id != shard_id { + continue; + } + + if let Ok(block) = chain_store.get_block(block_hash) { + let known_state_root = block.chunks()[shard_id as usize].prev_state_root(); + assert_eq!(known_state_root, state_root); + tracing::debug!(target: "state-changes", block_height, ?state_root, "Known StateRoot matches"); + } + + tracing::info!(target: "state-changes", block_height, ?block_hash, ?shard_uid, ?state_root, num_changes = state_changes.len(), "Applying state changes"); + let trie = runtime.get_trie_for_shard(shard_id, block_hash, state_root, false).unwrap(); + + let trie_update = trie + .update(state_changes.iter().map(|raw_state_changes_with_trie_key| { + tracing::debug!(target: "state-changes", ?raw_state_changes_with_trie_key); + let raw_key = raw_state_changes_with_trie_key.trie_key.to_vec(); + let data = raw_state_changes_with_trie_key.changes.last().unwrap().data.clone(); + (raw_key, data) + })) + .unwrap(); + + tracing::info!(target: "state-change", block_height, ?block_hash, ?shard_uid, old_state_root = ?trie_update.old_root, new_state_root = ?trie_update.new_root, "Applied state changes"); + state_root = trie_update.new_root; + + let wrapped_trie_changes = WrappedTrieChanges::new( + runtime.get_tries(), + shard_uid, + trie_update, + state_changes, + *block_hash, + block_height, + ); + let mut store_update = chain_store.store_update(); + store_update.save_trie_changes(wrapped_trie_changes); + store_update.commit().unwrap(); + } + } + + tracing::info!(target: "state-changes", ?file, ?shard_id, ?state_root, "Done applying changes"); +} + +/// Determines the shard id which produced the StateChange based the row key, +/// part of the value (TrieKey) and the block that resulted in this state change. +pub fn get_state_change_shard_id( + row_key: &[u8], + trie_key: &TrieKey, + block_hash: &CryptoHash, + epoch_id: &EpochId, + epoch_manager: &dyn EpochManagerAdapter, +) -> Result { + if let Some(account_id) = trie_key.get_account_id() { + let shard_id = epoch_manager.account_id_to_shard_id(&account_id, epoch_id)?; + Ok(shard_id) + } else { + let shard_uid = + KeyForStateChanges::delayed_receipt_key_decode_shard_uid(row_key, block_hash, trie_key) + .map_err(|err| { + unc_chain::unc_chain_primitives::error::Error::Other(err.to_string()) + })?; + Ok(shard_uid.shard_id as ShardId) + } +} diff --git a/tools/state-viewer/src/state_dump.rs b/tools/state-viewer/src/state_dump.rs new file mode 100644 index 000000000..168553903 --- /dev/null +++ b/tools/state-viewer/src/state_dump.rs @@ -0,0 +1,938 @@ +use chrono::Utc; +use unc_chain::types::RuntimeAdapter; +use unc_chain_configs::{Genesis, GenesisChangeConfig, GenesisConfig}; +use unc_crypto::PublicKey; +use unc_epoch_manager::EpochManagerAdapter; +use unc_epoch_manager::EpochManagerHandle; +use unc_primitives::account::id::AccountId; +use unc_primitives::block::BlockHeader; +use unc_primitives::state_record::state_record_to_account_id; +use unc_primitives::state_record::StateRecord; +use unc_primitives::types::{AccountInfo, Balance, StateRoot}; +use framework::config::UncConfig; +use framework::NightshadeRuntime; +use redis::Commands; +use serde::ser::{SerializeSeq, Serializer}; +use std::collections::HashMap; +use std::collections::HashSet; +use std::env; +use std::fs; +use std::fs::File; +use std::path::Path; +use std::sync::Arc; +use unc_primitives_core::types::Power; + +/// Returns a `UncConfig` with genesis records taken from the current state. +/// If `records_path` argument is provided, then records will be streamed into a separate file, +/// otherwise the returned `UncConfig` will contain all the records within itself. +pub fn state_dump( + epoch_manager: &EpochManagerHandle, + runtime: Arc, + state_roots: &[StateRoot], + last_block_header: BlockHeader, + unc_config: &UncConfig, + records_path: Option<&Path>, + change_config: &GenesisChangeConfig, +) -> UncConfig { + println!( + "Generating genesis from state data of #{} / {}", + last_block_header.height(), + last_block_header.hash() + ); + let genesis_height = last_block_header.height() + 1; + let block_producers = epoch_manager + .get_epoch_block_producers_ordered(last_block_header.epoch_id(), last_block_header.hash()) + .unwrap(); + let validators = block_producers + .into_iter() + .filter_map(|(info, is_slashed)| { + if !is_slashed { + let (account_id, public_key, power, frozen) = info.destructure(); + Some((account_id, (public_key, power, frozen))) + } else { + None + } + }) + .collect::>(); + + let mut unc_config = unc_config.clone(); + + let mut genesis_config = unc_config.genesis.config.clone(); + genesis_config.genesis_height = genesis_height; + genesis_config.genesis_time = Utc::now(); + genesis_config.validators = validators + .iter() + .map(|(account_id, (public_key, power,frozen))| AccountInfo { + account_id: account_id.clone(), + public_key: public_key.clone(), + amount: 0, + power: *power, + locked: *frozen, + }) + .collect(); + genesis_config.validators.sort_by_key(|account_info| account_info.account_id.clone()); + // Record the protocol version of the latest block. Otherwise, the state + // dump ignores the fact that the nodes can be running a newer protocol + // version than the protocol version of the genesis. + genesis_config.protocol_version = last_block_header.latest_protocol_version(); + let shard_config = epoch_manager.get_shard_config(last_block_header.epoch_id()).unwrap(); + genesis_config.shard_layout = shard_config.shard_layout; + genesis_config.num_block_producer_seats_per_shard = + shard_config.num_block_producer_seats_per_shard; + genesis_config.avg_hidden_validator_seats_per_shard = + shard_config.avg_hidden_validator_seats_per_shard; + // Record only the filename of the records file. + // Otherwise the absolute path is stored making it impossible to copy the dumped state to actually use it. + match records_path { + Some(records_path) => { + let mut records_path_dir = records_path.to_path_buf(); + records_path_dir.pop(); + fs::create_dir_all(&records_path_dir).unwrap_or_else(|_| { + panic!("Failed to create directory {}", records_path_dir.display()) + }); + let records_file = File::create(records_path).unwrap(); + let mut ser = serde_json::Serializer::new(records_file); + let mut seq = ser.serialize_seq(None).unwrap(); + let total_supply = iterate_over_records( + runtime, + state_roots, + last_block_header, + &validators, + &genesis_config.protocol_treasury_account, + &mut |sr| seq.serialize_element(&sr).unwrap(), + change_config, + ); + seq.end().unwrap(); + // `total_supply` is expected to change due to the natural processes of burning tokens and + // minting tokens every epoch. + genesis_config.total_supply = total_supply; + change_genesis_config(&mut genesis_config, change_config); + unc_config.genesis = Genesis::new_with_path(genesis_config, records_path).unwrap(); + unc_config.config.genesis_records_file = + Some(records_path.file_name().unwrap().to_str().unwrap().to_string()); + } + None => { + let mut records: Vec = vec![]; + let total_supply = iterate_over_records( + runtime, + state_roots, + last_block_header, + &validators, + &genesis_config.protocol_treasury_account, + &mut |sr| records.push(sr), + change_config, + ); + // `total_supply` is expected to change due to the natural processes of burning tokens and + // minting tokens every epoch. + genesis_config.total_supply = total_supply; + change_genesis_config(&mut genesis_config, change_config); + unc_config.genesis = Genesis::new(genesis_config, records.into()).unwrap(); + } + } + unc_config +} + +pub fn state_dump_redis( + runtime: Arc, + state_roots: &[StateRoot], + last_block_header: BlockHeader, +) -> redis::RedisResult<()> { + let redis_client = + redis::Client::open(env::var("REDIS_HOST").unwrap_or("redis://127.0.0.1/".to_string()))?; + let mut redis_connection = redis_client.get_connection()?; + + let block_height = last_block_header.height(); + let block_hash = last_block_header.hash(); + + for (shard_id, state_root) in state_roots.iter().enumerate() { + let trie = runtime + .get_trie_for_shard(shard_id as u64, last_block_header.prev_hash(), *state_root, false) + .unwrap(); + for item in trie.iter().unwrap() { + let (key, value) = item.unwrap(); + if let Some(sr) = StateRecord::from_raw_key_value(key, value) { + if let StateRecord::Account { account_id, account } = &sr { + println!("Account: {}", account_id); + let redis_key = account_id.as_bytes(); + redis_connection.zadd( + [b"account:", redis_key].concat(), + block_hash.as_ref(), + block_height, + )?; + let value = borsh::to_vec(&account).unwrap(); + redis_connection.set( + [b"account-data:", redis_key, b":", block_hash.as_ref()].concat(), + value, + )?; + println!("Account written: {}", account_id); + } + + if let StateRecord::Data { account_id, data_key, value } = &sr { + println!("Data: {}", account_id); + let redis_key = [account_id.as_bytes(), b":", data_key.as_ref()].concat(); + redis_connection.zadd( + [b"data:", redis_key.as_slice()].concat(), + block_hash.as_ref(), + block_height, + )?; + let value_vec: &[u8] = value.as_ref(); + redis_connection.set( + [b"data-value:", redis_key.as_slice(), b":", block_hash.as_ref()].concat(), + value_vec, + )?; + println!("Data written: {}", account_id); + } + + if let StateRecord::Contract { account_id, code } = &sr { + println!("Contract: {}", account_id); + let redis_key = [b"code:", account_id.as_bytes()].concat(); + redis_connection.zadd(redis_key.clone(), block_hash.as_ref(), block_height)?; + let value_vec: &[u8] = code.as_ref(); + redis_connection.set( + [redis_key.clone(), b":".to_vec(), block_hash.0.to_vec()].concat(), + value_vec, + )?; + println!("Contract written: {}", account_id); + } + } + } + } + + Ok(()) +} + +fn should_include_record( + record: &StateRecord, + account_allowlist: &Option>, +) -> bool { + match account_allowlist { + None => true, + Some(allowlist) => { + let current_account_id = state_record_to_account_id(record); + allowlist.contains(current_account_id) + } + } +} + +/// Iterates over the state, calling `callback` for every record that genesis needs to contain. +fn iterate_over_records( + runtime: Arc, + state_roots: &[StateRoot], + last_block_header: BlockHeader, + validators: &HashMap, + protocol_treasury_account: &AccountId, + mut callback: impl FnMut(StateRecord), + change_config: &GenesisChangeConfig, +) -> Balance { + let account_allowlist = match &change_config.select_account_ids { + None => None, + Some(select_account_id_list) => { + let mut result = validators.keys().collect::>(); + result.extend(select_account_id_list); + result.insert(protocol_treasury_account); + Some(result) + } + }; + let mut total_supply = 0; + for (shard_id, state_root) in state_roots.iter().enumerate() { + let trie = runtime + .get_trie_for_shard(shard_id as u64, last_block_header.prev_hash(), *state_root, false) + .unwrap(); + for item in trie.iter().unwrap() { + let (key, value) = item.unwrap(); + if let Some(mut sr) = StateRecord::from_raw_key_value(key, value) { + if !should_include_record(&sr, &account_allowlist) { + continue; + } + if let StateRecord::Account { account_id, account } = &mut sr { + if account.locked() > 0 { + let stake = *validators.get(account_id).map(|(_,_, s)| s).unwrap_or(&0); + if account.locked() > stake { + account.set_amount(account.amount() + account.locked() - stake); + } + account.set_locked(stake); + } + total_supply += account.amount() + account.locked(); + } + change_state_record(&mut sr, change_config); + callback(sr); + } + } + } + total_supply +} + +/// Change record according to genesis_change_config. +/// 1. Remove stake from non-whitelisted validators; +pub fn change_state_record(record: &mut StateRecord, genesis_change_config: &GenesisChangeConfig) { + // Kick validators outside of whitelist + if let Some(whitelist) = &genesis_change_config.whitelist_validators { + if let StateRecord::Account { account_id, account } = record { + if !whitelist.contains(account_id) { + account.set_amount(account.amount() + account.locked()); + account.set_locked(0); + } + } + } +} + +/// Change genesis_config according to genesis_change_config. +/// 1. Kick all the non-whitelisted validators; +pub fn change_genesis_config( + genesis_config: &mut GenesisConfig, + genesis_change_config: &GenesisChangeConfig, +) { + { + // Kick validators outside of whitelist + if let Some(whitelist) = &genesis_change_config.whitelist_validators { + genesis_config.validators.retain(|v| whitelist.contains(&v.account_id)); + } + } +} + +#[cfg(test)] +mod test { + use std::collections::{HashMap, HashSet}; + use std::path::Path; + use std::sync::Arc; + + use unc_chain::{ChainGenesis, ChainStoreAccess, Provenance}; + use unc_chain_configs::genesis_validate::validate_genesis; + use unc_chain_configs::{Genesis, GenesisChangeConfig}; + use unc_client::test_utils::TestEnv; + use unc_client::ProcessTxResponse; + use unc_crypto::{InMemorySigner, KeyFile, KeyType, PublicKey, SecretKey}; + use unc_epoch_manager::EpochManager; + use unc_primitives::account::id::AccountId; + use unc_primitives::state_record::StateRecord; + use unc_primitives::transaction::{Action, DeployContractAction, SignedTransaction}; + use unc_primitives::types::{ + Balance, BlockHeight, BlockHeightDelta, NumBlocks, ProtocolVersion, + }; + use unc_primitives::version::PROTOCOL_VERSION; + use unc_store::genesis::initialize_genesis_state; + use unc_store::test_utils::create_test_store; + use unc_store::Store; + use framework::config::GenesisExt; + use framework::config::TESTING_INIT_STAKE; + use framework::config::{Config, UncConfig}; + use framework::test_utils::TestEnvNightshadeSetupExt; + use framework::NightshadeRuntime; + + use crate::state_dump::state_dump; + use unc_primitives::hash::CryptoHash; + use unc_primitives::validator_signer::InMemoryValidatorSigner; + use unc_primitives_core::account::id::AccountIdRef; + + fn setup( + epoch_length: NumBlocks, + protocol_version: ProtocolVersion, + test_resharding: bool, + ) -> (Store, Genesis, TestEnv, UncConfig) { + let mut genesis = + Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.num_block_producer_seats = 2; + genesis.config.num_block_producer_seats_per_shard = vec![2]; + genesis.config.epoch_length = epoch_length; + genesis.config.protocol_version = protocol_version; + genesis.config.use_production_config = test_resharding; + + let env = if test_resharding { + TestEnv::builder(ChainGenesis::new(&genesis)) + .validator_seats(2) + .use_state_snapshots() + .real_stores() + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build() + } else { + TestEnv::builder(ChainGenesis::new(&genesis)) + .validator_seats(2) + .real_epoch_managers(&genesis.config) + .nightshade_runtimes(&genesis) + .build() + }; + + let unc_config = UncConfig::new( + Config::default(), + genesis.clone(), + KeyFile { + account_id: "test".parse().unwrap(), + public_key: PublicKey::empty(KeyType::ED25519), + secret_key: SecretKey::from_random(KeyType::ED25519), + }, + Some(Arc::new(InMemoryValidatorSigner::from_random( + "test".parse().unwrap(), + KeyType::ED25519, + ))), + ) + .unwrap(); + + let store = env.clients[0].chain.chain_store().store().clone(); + (store, genesis, env, unc_config) + } + + /// Produces blocks, avoiding the potential failure where the client is not the + /// block producer for each subsequent height (this can happen when a new validator + /// is staked since they will also have heights where they should produce the block instead). + fn safe_produce_blocks( + env: &mut TestEnv, + initial_height: BlockHeight, + num_blocks: BlockHeightDelta, + ) { + let mut h = initial_height; + for _ in 1..=num_blocks { + let mut block = None; + // `env.clients[0]` may not be the block producer at `h`, + // loop until we find a height env.clients[0] should produce. + while block.is_none() { + block = env.clients[0].produce_block(h).unwrap(); + h += 1; + } + env.process_block(0, block.unwrap(), Provenance::PRODUCED); + } + } + + /// Test that we preserve the validators from the epoch of the state dump. + #[test] + fn test_dump_state_preserve_validators() { + let epoch_length = 4; + let (store, genesis, mut env, unc_config) = setup(epoch_length, PROTOCOL_VERSION, false); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let tx = SignedTransaction::stake( + 1, + "test1".parse().unwrap(), + &signer, + TESTING_INIT_STAKE, + signer.public_key.clone(), + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + + safe_produce_blocks(&mut env, 1, epoch_length * 2 + 1); + + let head = env.clients[0].chain.head().unwrap(); + let last_block_hash = head.last_block_hash; + let cur_epoch_id = head.epoch_id; + let block_producers = env.clients[0] + .epoch_manager + .get_epoch_block_producers_ordered(&cur_epoch_id, &last_block_hash) + .unwrap(); + assert_eq!( + block_producers.into_iter().map(|(r, _)| r.take_account_id()).collect::>(), + HashSet::from_iter(vec!["test0".parse().unwrap(), "test1".parse().unwrap()]) + ); + let last_block = env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); + let state_roots: Vec = + last_block.chunks().iter().map(|chunk| chunk.prev_state_root()).collect(); + initialize_genesis_state(store.clone(), &genesis, None); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = + NightshadeRuntime::test(Path::new("."), store, &genesis.config, epoch_manager.clone()); + let records_file = tempfile::NamedTempFile::new().unwrap(); + let new_unc_config = state_dump( + epoch_manager.as_ref(), + runtime, + &state_roots, + last_block.header().clone(), + &unc_config, + Some(records_file.path()), + &GenesisChangeConfig::default(), + ); + let new_genesis = new_unc_config.genesis; + assert_eq!(new_genesis.config.validators.len(), 2); + validate_genesis(&new_genesis).unwrap(); + } + + /// Test that we respect the specified account ID list in dump_state. + #[test] + fn test_dump_state_respect_select_account_ids() { + let epoch_length = 4; + let (store, genesis, mut env, unc_config) = setup(epoch_length, PROTOCOL_VERSION, false); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + + let signer0 = + InMemorySigner::from_seed("test0".parse().unwrap(), KeyType::ED25519, "test0"); + let tx00 = SignedTransaction::from_actions( + 1, + "test0".parse().unwrap(), + "test0".parse().unwrap(), + &signer0, + vec![Action::DeployContract(DeployContractAction { + code: unc_test_contracts::backwards_compatible_rs_contract().to_vec(), + })], + genesis_hash, + ); + let tx01 = SignedTransaction::stake( + 1, + "test0".parse().unwrap(), + &signer0, + TESTING_INIT_STAKE, + signer0.public_key.clone(), + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx00, false, false), ProcessTxResponse::ValidTx); + assert_eq!(env.clients[0].process_tx(tx01, false, false), ProcessTxResponse::ValidTx); + + let signer1 = + InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let tx1 = SignedTransaction::stake( + 1, + "test1".parse().unwrap(), + &signer1, + TESTING_INIT_STAKE, + signer1.public_key.clone(), + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx1, false, false), ProcessTxResponse::ValidTx); + + safe_produce_blocks(&mut env, 1, epoch_length * 2 + 1); + + let head = env.clients[0].chain.head().unwrap(); + let last_block_hash = head.last_block_hash; + let cur_epoch_id = head.epoch_id; + let block_producers = env.clients[0] + .epoch_manager + .get_epoch_block_producers_ordered(&cur_epoch_id, &last_block_hash) + .unwrap(); + assert_eq!( + block_producers.into_iter().map(|(r, _)| r.take_account_id()).collect::>(), + HashSet::from_iter(vec!["test0".parse().unwrap(), "test1".parse().unwrap()]), + ); + let last_block = env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); + let state_roots: Vec = + last_block.chunks().iter().map(|chunk| chunk.prev_state_root()).collect(); + initialize_genesis_state(store.clone(), &genesis, None); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = + NightshadeRuntime::test(Path::new("."), store, &genesis.config, epoch_manager.clone()); + let select_account_ids = vec!["test0".parse().unwrap()]; + let new_unc_config = state_dump( + epoch_manager.as_ref(), + runtime, + &state_roots, + last_block.header().clone(), + &unc_config, + None, + &GenesisChangeConfig::default() + .with_select_account_ids(Some(select_account_ids.clone())), + ); + let new_genesis = new_unc_config.genesis; + let mut expected_accounts: HashSet = + new_genesis.config.validators.iter().map(|v| v.account_id.clone()).collect(); + expected_accounts.extend(select_account_ids); + expected_accounts.insert(new_genesis.config.protocol_treasury_account.clone()); + let mut actual_accounts: HashSet = HashSet::new(); + new_genesis.for_each_record(|record| { + if let StateRecord::Account { account_id, .. } = record { + actual_accounts.insert(account_id.clone()); + } + }); + assert_eq!(expected_accounts, actual_accounts); + validate_genesis(&new_genesis).unwrap(); + } + + /// Test that we preserve the validators from the epoch of the state dump. + #[test] + fn test_dump_state_preserve_validators_inmemory() { + let epoch_length = 4; + let (store, genesis, mut env, unc_config) = setup(epoch_length, PROTOCOL_VERSION, false); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let tx = SignedTransaction::stake( + 1, + "test1".parse().unwrap(), + &signer, + TESTING_INIT_STAKE, + signer.public_key.clone(), + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + + safe_produce_blocks(&mut env, 1, epoch_length * 2 + 1); + + let head = env.clients[0].chain.head().unwrap(); + let last_block_hash = head.last_block_hash; + let cur_epoch_id = head.epoch_id; + let block_producers = env.clients[0] + .epoch_manager + .get_epoch_block_producers_ordered(&cur_epoch_id, &last_block_hash) + .unwrap(); + assert_eq!( + block_producers.into_iter().map(|(r, _)| r.take_account_id()).collect::>(), + HashSet::from_iter(vec!["test0".parse().unwrap(), "test1".parse().unwrap()]) + ); + let last_block = env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); + let state_roots: Vec = + last_block.chunks().iter().map(|chunk| chunk.prev_state_root()).collect(); + initialize_genesis_state(store.clone(), &genesis, None); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = + NightshadeRuntime::test(Path::new("."), store, &genesis.config, epoch_manager.clone()); + let new_unc_config = state_dump( + epoch_manager.as_ref(), + runtime, + &state_roots, + last_block.header().clone(), + &unc_config, + None, + &GenesisChangeConfig::default(), + ); + let new_genesis = new_unc_config.genesis; + assert_eq!(new_genesis.config.validators.len(), 2); + validate_genesis(&new_genesis).unwrap(); + } + + /// Test that we return locked tokens for accounts that are not validators. + #[test] + fn test_dump_state_return_locked() { + let epoch_length = 4; + let (store, genesis, mut env, unc_config) = setup(epoch_length, PROTOCOL_VERSION, false); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let tx = SignedTransaction::stake( + 1, + "test1".parse().unwrap(), + &signer, + TESTING_INIT_STAKE, + signer.public_key.clone(), + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + for i in 1..=epoch_length + 1 { + env.produce_block(0, i); + } + + let head = env.clients[0].chain.head().unwrap(); + let last_block = env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); + let state_roots: Vec = + last_block.chunks().iter().map(|chunk| chunk.prev_state_root()).collect(); + initialize_genesis_state(store.clone(), &genesis, None); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = + NightshadeRuntime::test(Path::new("."), store, &genesis.config, epoch_manager.clone()); + + let records_file = tempfile::NamedTempFile::new().unwrap(); + let new_unc_config = state_dump( + epoch_manager.as_ref(), + runtime, + &state_roots, + last_block.header().clone(), + &unc_config, + Some(records_file.path()), + &GenesisChangeConfig::default(), + ); + let new_genesis = new_unc_config.genesis; + assert_eq!( + new_genesis + .config + .validators + .clone() + .into_iter() + .map(|r| r.account_id) + .collect::>(), + vec!["test0"] + ); + validate_genesis(&new_genesis).unwrap(); + } + + #[test] + fn test_dump_state_shard_upgrade() { + use unc_client::test_utils::run_catchup; + use unc_primitives::shard_layout::ShardLayout; + use unc_primitives::version::ProtocolFeature::SimpleNightshade; + + let epoch_length = 4; + let (store, genesis, mut env, unc_config) = + setup(epoch_length, SimpleNightshade.protocol_version() - 1, true); + for i in 1..=2 * epoch_length + 1 { + let mut block = env.clients[0].produce_block(i).unwrap().unwrap(); + block.mut_header().set_latest_protocol_version(SimpleNightshade.protocol_version()); + env.process_block(0, block, Provenance::PRODUCED); + run_catchup(&mut env.clients[0], &[]).unwrap(); + } + let head = env.clients[0].chain.head().unwrap(); + assert_eq!( + env.clients[0].epoch_manager.get_shard_layout(&head.epoch_id).unwrap(), + ShardLayout::get_simple_nightshade_layout(), + ); + let last_block = env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); + + let state_roots: Vec = + last_block.chunks().iter().map(|chunk| chunk.prev_state_root()).collect(); + initialize_genesis_state(store.clone(), &genesis, None); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = + NightshadeRuntime::test(Path::new("."), store, &genesis.config, epoch_manager.clone()); + let records_file = tempfile::NamedTempFile::new().unwrap(); + let new_unc_config = state_dump( + epoch_manager.as_ref(), + runtime, + &state_roots, + last_block.header().clone(), + &unc_config, + Some(records_file.path()), + &GenesisChangeConfig::default(), + ); + let new_genesis = new_unc_config.genesis; + + assert_eq!(new_genesis.config.shard_layout, ShardLayout::get_simple_nightshade_layout()); + assert_eq!(new_genesis.config.num_block_producer_seats_per_shard, vec![2; 4]); + assert_eq!(new_genesis.config.avg_hidden_validator_seats_per_shard, vec![0; 4]); + } + + /// If the node does not track a shard, state dump will not give the correct result. + #[test] + #[should_panic(expected = "MissingTrieValue")] + fn test_dump_state_not_track_shard() { + let epoch_length = 4; + let mut genesis = + Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.num_block_producer_seats = 2; + genesis.config.num_block_producer_seats_per_shard = vec![2]; + genesis.config.epoch_length = epoch_length; + let store1 = create_test_store(); + let store2 = create_test_store(); + initialize_genesis_state(store1.clone(), &genesis, None); + initialize_genesis_state(store2.clone(), &genesis, None); + let epoch_manager1 = EpochManager::new_arc_handle(store1.clone(), &genesis.config); + let epoch_manager2 = EpochManager::new_arc_handle(store2.clone(), &genesis.config); + let runtime1 = NightshadeRuntime::test( + Path::new("."), + store1.clone(), + &genesis.config, + epoch_manager1.clone(), + ); + let runtime2 = NightshadeRuntime::test( + Path::new("."), + store2.clone(), + &genesis.config, + epoch_manager2.clone(), + ); + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + chain_genesis.gas_limit = genesis.config.gas_limit; + let mut env = TestEnv::builder(chain_genesis) + .clients_count(2) + .stores(vec![store1, store2]) + .epoch_managers(vec![epoch_manager1, epoch_manager2.clone()]) + .runtimes(vec![runtime1, runtime2.clone()]) + .build(); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let tx = SignedTransaction::send_money( + 1, + "test1".parse().unwrap(), + "test0".parse().unwrap(), + &signer, + 1, + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + + let mut blocks = vec![]; + for i in 1..epoch_length { + let block = env.clients[0].produce_block(i).unwrap().unwrap(); + for j in 0..2 { + let provenance = if j == 0 { Provenance::PRODUCED } else { Provenance::NONE }; + env.process_block(j, block.clone(), provenance); + } + blocks.push(block); + } + + let unc_config = UncConfig::new( + Config::default(), + genesis, + KeyFile { + account_id: "test".parse().unwrap(), + public_key: PublicKey::empty(KeyType::ED25519), + secret_key: SecretKey::from_random(KeyType::ED25519), + }, + Some(Arc::new(InMemoryValidatorSigner::from_random( + "test".parse().unwrap(), + KeyType::ED25519, + ))), + ) + .unwrap(); + + let last_block = blocks.pop().unwrap(); + let state_roots = + last_block.chunks().iter().map(|chunk| chunk.prev_state_root()).collect::>(); + + let records_file = tempfile::NamedTempFile::new().unwrap(); + let _ = state_dump( + epoch_manager2.as_ref(), + runtime2, + &state_roots, + last_block.header().clone(), + &unc_config, + Some(records_file.path()), + &GenesisChangeConfig::default(), + ); + } + + #[test] + fn test_dump_state_with_delayed_receipt() { + let epoch_length = 4; + let mut genesis = + Genesis::test(vec!["test0".parse().unwrap(), "test1".parse().unwrap()], 1); + genesis.config.num_block_producer_seats = 2; + genesis.config.num_block_producer_seats_per_shard = vec![2]; + genesis.config.epoch_length = epoch_length; + let store = create_test_store(); + initialize_genesis_state(store.clone(), &genesis, None); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let nightshade_runtime = NightshadeRuntime::test( + Path::new("."), + store.clone(), + &genesis.config, + epoch_manager.clone(), + ); + let mut chain_genesis = ChainGenesis::test(); + chain_genesis.epoch_length = epoch_length; + let mut env = TestEnv::builder(chain_genesis) + .validator_seats(2) + .stores(vec![store.clone()]) + .epoch_managers(vec![epoch_manager]) + .runtimes(vec![nightshade_runtime]) + .build(); + let genesis_hash = *env.clients[0].chain.genesis().hash(); + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let tx = SignedTransaction::stake( + 1, + "test1".parse().unwrap(), + &signer, + TESTING_INIT_STAKE, + signer.public_key.clone(), + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + + safe_produce_blocks(&mut env, 1, epoch_length * 2 + 1); + + let unc_config = UncConfig::new( + Config::default(), + genesis.clone(), + KeyFile { + account_id: "test".parse().unwrap(), + public_key: PublicKey::empty(KeyType::ED25519), + secret_key: SecretKey::from_random(KeyType::ED25519), + }, + Some(Arc::new(InMemoryValidatorSigner::from_random( + "test".parse().unwrap(), + KeyType::ED25519, + ))), + ) + .unwrap(); + let head = env.clients[0].chain.head().unwrap(); + let last_block_hash = head.last_block_hash; + let cur_epoch_id = head.epoch_id; + let block_producers = env.clients[0] + .epoch_manager + .get_epoch_block_producers_ordered(&cur_epoch_id, &last_block_hash) + .unwrap(); + assert_eq!( + block_producers.into_iter().map(|(r, _)| r.take_account_id()).collect::>(), + HashSet::from_iter(vec!["test0".parse().unwrap(), "test1".parse().unwrap()]) + ); + let last_block = env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); + let state_roots: Vec = + last_block.chunks().iter().map(|chunk| chunk.prev_state_root()).collect(); + initialize_genesis_state(store.clone(), &genesis, None); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = + NightshadeRuntime::test(Path::new("."), store, &genesis.config, epoch_manager.clone()); + let records_file = tempfile::NamedTempFile::new().unwrap(); + let new_unc_config = state_dump( + epoch_manager.as_ref(), + runtime, + &state_roots, + last_block.header().clone(), + &unc_config, + Some(records_file.path()), + &GenesisChangeConfig::default(), + ); + let new_genesis = new_unc_config.genesis; + + assert_eq!(new_genesis.config.validators.len(), 2); + validate_genesis(&new_genesis).unwrap(); + } + + #[test] + fn test_dump_state_respect_select_whitelist_validators() { + let epoch_length = 4; + let (store, genesis, mut env, unc_config) = setup(epoch_length, PROTOCOL_VERSION, false); + + let genesis_hash = *env.clients[0].chain.genesis().hash(); + let signer = InMemorySigner::from_seed("test1".parse().unwrap(), KeyType::ED25519, "test1"); + let tx = SignedTransaction::stake( + 1, + "test1".parse().unwrap(), + &signer, + TESTING_INIT_STAKE, + signer.public_key.clone(), + genesis_hash, + ); + assert_eq!(env.clients[0].process_tx(tx, false, false), ProcessTxResponse::ValidTx); + + safe_produce_blocks(&mut env, 1, epoch_length * 2 + 1); + + let head = env.clients[0].chain.head().unwrap(); + let last_block_hash = head.last_block_hash; + let cur_epoch_id = head.epoch_id; + let block_producers = env.clients[0] + .epoch_manager + .get_epoch_block_producers_ordered(&cur_epoch_id, &last_block_hash) + .unwrap(); + assert_eq!( + block_producers.into_iter().map(|(r, _)| r.take_account_id()).collect::>(), + HashSet::from_iter(vec!["test0".parse().unwrap(), "test1".parse().unwrap()]), + ); + + let whitelist_validators = + vec!["test1".parse().unwrap(), "non_validator_account".parse().unwrap()]; + + let last_block = env.clients[0].chain.get_block(&head.last_block_hash).unwrap(); + let state_roots: Vec = + last_block.chunks().iter().map(|chunk| chunk.prev_state_root()).collect(); + initialize_genesis_state(store.clone(), &genesis, None); + let epoch_manager = EpochManager::new_arc_handle(store.clone(), &genesis.config); + let runtime = + NightshadeRuntime::test(Path::new("."), store, &genesis.config, epoch_manager.clone()); + let new_unc_config = state_dump( + epoch_manager.as_ref(), + runtime, + &state_roots, + last_block.header().clone(), + &unc_config, + None, + &GenesisChangeConfig::default().with_whitelist_validators(Some(whitelist_validators)), + ); + let new_genesis = new_unc_config.genesis; + + assert_eq!( + new_genesis + .config + .validators + .iter() + .map(|x| x.account_id.clone()) + .collect::>(), + vec!["test1"] + ); + + let mut stake = HashMap::::new(); + new_genesis.for_each_record(|record| { + if let StateRecord::Account { account_id, account } = record { + stake.insert(account_id.clone(), account.locked()); + } + }); + + assert_eq!( + stake.get(AccountIdRef::new_or_panic("test0")).unwrap_or(&(0 as Balance)), + &(0 as Balance) + ); + + validate_genesis(&new_genesis).unwrap(); + } +} diff --git a/tools/state-viewer/src/state_parts.rs b/tools/state-viewer/src/state_parts.rs new file mode 100644 index 000000000..a3c23e8c5 --- /dev/null +++ b/tools/state-viewer/src/state_parts.rs @@ -0,0 +1,529 @@ +use crate::epoch_info::iterate_and_filter; +use borsh::BorshDeserialize; +use unc_chain::{Chain, ChainGenesis, ChainStoreAccess, DoomslugThresholdMode}; +use unc_client::sync::external::{ + create_bucket_readonly, create_bucket_readwrite, external_storage_location, + external_storage_location_directory, get_num_parts_from_filename, ExternalConnection, +}; +use unc_client::sync::state::StateSync; +use unc_epoch_manager::shard_tracker::{ShardTracker, TrackedConfig}; +use unc_epoch_manager::EpochManager; +use unc_primitives::challenge::PartialState; +use unc_primitives::epoch_manager::epoch_info::EpochInfo; +use unc_primitives::state_part::PartId; +use unc_primitives::state_record::StateRecord; +use unc_primitives::types::{EpochId, StateRoot}; +use unc_primitives_core::hash::CryptoHash; +use unc_primitives_core::types::{BlockHeight, EpochHeight, ShardId}; +use unc_store::{PartialStorage, Store, Trie}; +use framework::{UncConfig, NightshadeRuntime}; +use std::ops::Range; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use std::sync::Arc; +use std::time::{Duration, Instant}; + +#[derive(clap::ValueEnum, Clone, Debug, Default)] +pub(crate) enum LoadAction { + #[default] + Apply, + Print, + Validate, +} + +#[derive(clap::Subcommand, Debug, Clone)] +pub(crate) enum StatePartsSubCommand { + /// Load all or a single state part of a shard and perform an action over those parts. + Load { + /// Apply, validate or print. + #[clap(value_enum, long)] + action: LoadAction, + /// If provided, this value will be used instead of looking it up in the headers. + /// Use if those headers or blocks are not available. + #[clap(long)] + state_root: Option, + /// If provided, this value will be used instead of looking it up in the headers. + /// Use if those headers or blocks are not available. + #[clap(long)] + sync_hash: Option, + /// Choose a single part id. + /// If None - affects all state parts. + #[clap(long)] + part_id: Option, + /// Select an epoch to work on. + #[clap(subcommand)] + epoch_selection: EpochSelection, + }, + /// Dump all or a single state part of a shard. + Dump { + /// Dump part ids starting from this part. + #[clap(long)] + part_from: Option, + /// Dump part ids up to this part (exclusive). + #[clap(long)] + part_to: Option, + /// Location of a file with write permissions to the bucket. + #[clap(long)] + credentials_file: Option, + /// Select an epoch to work on. + #[clap(subcommand)] + epoch_selection: EpochSelection, + }, + /// Read State Header from the DB + ReadStateHeader { + /// Select an epoch to work on. + #[clap(subcommand)] + epoch_selection: EpochSelection, + }, + /// Finalize state sync. + Finalize { + /// If provided, this value will be used instead of looking it up in the headers. + /// Use if those headers or blocks are not available. + #[clap(long)] + sync_hash: CryptoHash, + }, +} + +impl StatePartsSubCommand { + pub(crate) fn run( + self, + shard_id: ShardId, + root_dir: Option, + s3_bucket: Option, + s3_region: Option, + gcs_bucket: Option, + home_dir: &Path, + unc_config: UncConfig, + store: Store, + ) { + let epoch_manager = + EpochManager::new_arc_handle(store.clone(), &unc_config.genesis.config); + let shard_tracker = ShardTracker::new( + TrackedConfig::from_config(&unc_config.client_config), + epoch_manager.clone(), + ); + let runtime = NightshadeRuntime::from_config( + home_dir, + store.clone(), + &unc_config, + epoch_manager.clone(), + ); + let chain_genesis = ChainGenesis::new(&unc_config.genesis); + let mut chain = Chain::new_for_view_client( + epoch_manager, + shard_tracker, + runtime, + &chain_genesis, + DoomslugThresholdMode::TwoThirds, + false, + ) + .unwrap(); + let chain_id = &unc_config.genesis.config.chain_id; + let sys = actix::System::new(); + sys.block_on(async move { + match self { + StatePartsSubCommand::Load { + action, + state_root, + sync_hash, + part_id, + epoch_selection, + } => { + let external = create_external_connection( + root_dir, + s3_bucket, + s3_region, + gcs_bucket, + None, + Mode::Readonly, + ); + load_state_parts( + action, + epoch_selection, + shard_id, + part_id, + state_root, + sync_hash, + &mut chain, + chain_id, + store, + &external, + ) + .await + } + StatePartsSubCommand::Dump { + part_from, + part_to, + epoch_selection, + credentials_file, + } => { + let external = create_external_connection( + root_dir, + s3_bucket, + s3_region, + gcs_bucket, + credentials_file, + Mode::Readwrite, + ); + dump_state_parts( + epoch_selection, + shard_id, + part_from, + part_to, + &chain, + chain_id, + store, + &external, + ) + .await + } + StatePartsSubCommand::ReadStateHeader { epoch_selection } => { + read_state_header(epoch_selection, shard_id, &chain, store) + } + StatePartsSubCommand::Finalize { sync_hash } => { + finalize_state_sync(sync_hash, shard_id, &mut chain) + } + } + actix::System::current().stop(); + }); + sys.run().unwrap(); + } +} + +enum Mode { + Readonly, + Readwrite, +} + +fn create_external_connection( + root_dir: Option, + bucket: Option, + region: Option, + gcs_bucket: Option, + credentials_file: Option, + mode: Mode, +) -> ExternalConnection { + if let Some(root_dir) = root_dir { + ExternalConnection::Filesystem { root_dir } + } else if let (Some(bucket), Some(region)) = (bucket, region) { + let bucket = match mode { + Mode::Readonly => create_bucket_readonly(&bucket, ®ion, Duration::from_secs(5)), + Mode::Readwrite => { + create_bucket_readwrite(&bucket, ®ion, Duration::from_secs(5), credentials_file) + } + } + .expect("Failed to create an S3 bucket"); + ExternalConnection::S3 { bucket: Arc::new(bucket) } + } else if let Some(bucket) = gcs_bucket { + if let Some(credentials_file) = credentials_file { + std::env::set_var("SERVICE_ACCOUNT", &credentials_file); + } + ExternalConnection::GCS { + gcs_client: Arc::new(cloud_storage::Client::default()), + reqwest_client: Arc::new(reqwest::Client::default()), + bucket, + } + } else { + panic!( + "Please provide --root-dir, or both of --s3-bucket and --s3-region, or --gcs-bucket" + ); + } +} + +#[derive(clap::Subcommand, Debug, Clone)] +pub(crate) enum EpochSelection { + /// Current epoch. + Current, + /// Fetch the given epoch. + EpochId { epoch_id: String }, + /// Fetch epochs at the given height. + EpochHeight { epoch_height: EpochHeight }, + /// Fetch an epoch containing the given block hash. + BlockHash { block_hash: String }, + /// Fetch an epoch containing the given block height. + BlockHeight { block_height: BlockHeight }, +} + +impl EpochSelection { + fn to_epoch_id(&self, store: Store, chain: &Chain) -> EpochId { + match self { + EpochSelection::Current => { + chain.epoch_manager.get_epoch_id(&chain.head().unwrap().last_block_hash).unwrap() + } + EpochSelection::EpochId { epoch_id } => { + EpochId(CryptoHash::from_str(epoch_id).unwrap()) + } + EpochSelection::EpochHeight { epoch_height } => { + // Fetch epochs at the given height. + // There should only be one epoch at a given height. But this is a debug tool, let's check + // if there are multiple epochs at a given height. + let epoch_ids = iterate_and_filter(store, |epoch_info| { + epoch_info.epoch_height() == *epoch_height + }); + assert_eq!(epoch_ids.len(), 1, "{:#?}", epoch_ids); + epoch_ids[0].clone() + } + EpochSelection::BlockHash { block_hash } => { + let block_hash = CryptoHash::from_str(block_hash).unwrap(); + chain.epoch_manager.get_epoch_id(&block_hash).unwrap() + } + EpochSelection::BlockHeight { block_height } => { + // Fetch an epoch containing the given block height. + let block_hash = + chain.chain_store().get_block_hash_by_height(*block_height).unwrap(); + chain.epoch_manager.get_epoch_id(&block_hash).unwrap() + } + } + } +} + +/// Returns block hash of some block of the given `epoch_info` epoch. +fn get_any_block_hash_of_epoch(epoch_info: &EpochInfo, chain: &Chain) -> CryptoHash { + let head = chain.chain_store().head().unwrap(); + let mut cur_block_info = chain.epoch_manager.get_block_info(&head.last_block_hash).unwrap(); + // EpochManager doesn't have an API that maps EpochId to Blocks, and this function works + // around that limitation by iterating over the epochs. + // This workaround is acceptable because: + // 1) Extending EpochManager's API is a major change. + // 2) This use case is not critical at all. + loop { + let cur_epoch_info = chain.epoch_manager.get_epoch_info(cur_block_info.epoch_id()).unwrap(); + let cur_epoch_height = cur_epoch_info.epoch_height(); + assert!( + cur_epoch_height >= epoch_info.epoch_height(), + "cur_block_info: {:#?}, epoch_info.epoch_height: {}", + cur_block_info, + epoch_info.epoch_height() + ); + let epoch_first_block_info = + chain.epoch_manager.get_block_info(cur_block_info.epoch_first_block()).unwrap(); + let prev_epoch_last_block_info = + chain.epoch_manager.get_block_info(epoch_first_block_info.prev_hash()).unwrap(); + + if cur_epoch_height == epoch_info.epoch_height() { + return *cur_block_info.hash(); + } + + cur_block_info = prev_epoch_last_block_info; + } +} + +async fn load_state_parts( + action: LoadAction, + epoch_selection: EpochSelection, + shard_id: ShardId, + part_id: Option, + maybe_state_root: Option, + maybe_sync_hash: Option, + chain: &mut Chain, + chain_id: &str, + store: Store, + external: &ExternalConnection, +) { + let epoch_id = epoch_selection.to_epoch_id(store, chain); + let (state_root, epoch_height, epoch_id, sync_hash) = + if let (Some(state_root), Some(sync_hash), EpochSelection::EpochHeight { epoch_height }) = + (maybe_state_root, maybe_sync_hash, &epoch_selection) + { + (state_root, *epoch_height, epoch_id, sync_hash) + } else { + let epoch = chain.epoch_manager.get_epoch_info(&epoch_id).unwrap(); + + let sync_hash = get_any_block_hash_of_epoch(&epoch, chain); + let sync_hash = StateSync::get_epoch_start_sync_hash(chain, &sync_hash).unwrap(); + + let state_header = chain.get_state_response_header(shard_id, sync_hash).unwrap(); + let state_root = state_header.chunk_prev_state_root(); + + (state_root, epoch.epoch_height(), epoch_id, sync_hash) + }; + + let directory_path = + external_storage_location_directory(chain_id, &epoch_id, epoch_height, shard_id); + let part_file_names = external.list_state_parts(shard_id, &directory_path).await.unwrap(); + assert!(!part_file_names.is_empty()); + let num_parts = part_file_names.len() as u64; + assert_eq!(Some(num_parts), get_num_parts_from_filename(&part_file_names[0])); + let part_ids = get_part_ids(part_id, part_id.map(|x| x + 1), num_parts); + tracing::info!( + target: "state-parts", + epoch_height, + shard_id, + num_parts, + ?sync_hash, + ?part_ids, + "Loading state as seen at the beginning of the specified epoch.", + ); + + let timer = Instant::now(); + for part_id in part_ids { + let timer = Instant::now(); + assert!(part_id < num_parts, "part_id: {}, num_parts: {}", part_id, num_parts); + let location = external_storage_location( + chain_id, + &epoch_id, + epoch_height, + shard_id, + part_id, + num_parts, + ); + let part = external.get_part(shard_id, &location).await.unwrap(); + + match action { + LoadAction::Apply => { + chain + .set_state_part(shard_id, sync_hash, PartId::new(part_id, num_parts), &part) + .unwrap(); + chain + .runtime_adapter + .apply_state_part( + shard_id, + &state_root, + PartId::new(part_id, num_parts), + &part, + &epoch_id, + ) + .unwrap(); + tracing::info!(target: "state-parts", part_id, part_length = part.len(), elapsed_sec = timer.elapsed().as_secs_f64(), "Loaded a state part"); + } + LoadAction::Validate => { + assert!(chain.runtime_adapter.validate_state_part( + &state_root, + PartId::new(part_id, num_parts), + &part + )); + tracing::info!(target: "state-parts", part_id, part_length = part.len(), elapsed_sec = timer.elapsed().as_secs_f64(), "Validated a state part"); + } + LoadAction::Print => { + print_state_part(&state_root, PartId::new(part_id, num_parts), &part) + } + } + } + tracing::info!(target: "state-parts", total_elapsed_sec = timer.elapsed().as_secs_f64(), "Loaded all requested state parts"); +} + +fn print_state_part(state_root: &StateRoot, _part_id: PartId, data: &[u8]) { + let trie_nodes: PartialState = BorshDeserialize::try_from_slice(data).unwrap(); + let trie = + Trie::from_recorded_storage(PartialStorage { nodes: trie_nodes }, *state_root, false); + trie.print_recursive( + &mut std::io::stdout().lock(), + &state_root, + u32::MAX, + None, + None, + &None, + &None, + ); +} + +async fn dump_state_parts( + epoch_selection: EpochSelection, + shard_id: ShardId, + part_from: Option, + part_to: Option, + chain: &Chain, + chain_id: &str, + store: Store, + external: &ExternalConnection, +) { + let epoch_id = epoch_selection.to_epoch_id(store, chain); + let epoch = chain.epoch_manager.get_epoch_info(&epoch_id).unwrap(); + let sync_hash = get_any_block_hash_of_epoch(&epoch, chain); + let sync_hash = StateSync::get_epoch_start_sync_hash(chain, &sync_hash).unwrap(); + let sync_block_header = chain.get_block_header(&sync_hash).unwrap(); + let sync_prev_header = chain.get_previous_header(&sync_block_header).unwrap(); + let sync_prev_prev_hash = sync_prev_header.prev_hash(); + + let state_header = chain.compute_state_response_header(shard_id, sync_hash).unwrap(); + let state_root = state_header.chunk_prev_state_root(); + let num_parts = state_header.num_state_parts(); + let part_ids = get_part_ids(part_from, part_to, num_parts); + + tracing::info!( + target: "state-parts", + epoch_height = epoch.epoch_height(), + epoch_id = ?epoch_id.0, + shard_id, + num_parts, + ?sync_hash, + ?part_ids, + ?state_root, + "Dumping state as seen at the beginning of the specified epoch.", + ); + + let timer = Instant::now(); + for part_id in part_ids { + let timer = Instant::now(); + assert!(part_id < num_parts, "part_id: {}, num_parts: {}", part_id, num_parts); + let state_part = chain + .runtime_adapter + .obtain_state_part( + shard_id, + sync_prev_prev_hash, + &state_root, + PartId::new(part_id, num_parts), + ) + .unwrap(); + + let location = external_storage_location( + &chain_id, + &epoch_id, + epoch.epoch_height(), + shard_id, + part_id, + num_parts, + ); + external.put_state_part(&state_part, shard_id, &location).await.unwrap(); + // part_storage.write(&state_part, part_id, num_parts); + let elapsed_sec = timer.elapsed().as_secs_f64(); + let first_state_record = get_first_state_record(&state_root, &state_part); + tracing::info!( + target: "state-parts", + part_id, + part_length = state_part.len(), + elapsed_sec, + first_state_record = ?first_state_record.map(|sr| format!("{}", sr)), + "Wrote a state part"); + } + tracing::info!(target: "state-parts", total_elapsed_sec = timer.elapsed().as_secs_f64(), "Wrote all requested state parts"); +} + +/// Returns the first `StateRecord` encountered while iterating over a sub-trie in the state part. +fn get_first_state_record(state_root: &StateRoot, data: &[u8]) -> Option { + let trie_nodes = BorshDeserialize::try_from_slice(data).unwrap(); + let trie = + Trie::from_recorded_storage(PartialStorage { nodes: trie_nodes }, *state_root, false); + + for (key, value) in trie.iter().unwrap().flatten() { + if let Some(sr) = StateRecord::from_raw_key_value(key, value) { + return Some(sr); + } + } + None +} + +/// Reads `StateHeader` stored in the DB. +fn read_state_header( + epoch_selection: EpochSelection, + shard_id: ShardId, + chain: &Chain, + store: Store, +) { + let epoch_id = epoch_selection.to_epoch_id(store, chain); + let epoch = chain.epoch_manager.get_epoch_info(&epoch_id).unwrap(); + + let sync_hash = get_any_block_hash_of_epoch(&epoch, chain); + let sync_hash = StateSync::get_epoch_start_sync_hash(chain, &sync_hash).unwrap(); + + let state_header = chain.chain_store().get_state_header(shard_id, sync_hash); + tracing::info!(target: "state-parts", ?epoch_id, ?sync_hash, ?state_header); +} + +fn finalize_state_sync(sync_hash: CryptoHash, shard_id: ShardId, chain: &mut Chain) { + chain.set_state_finalize(shard_id, sync_hash, Ok(())).unwrap() +} + +fn get_part_ids(part_from: Option, part_to: Option, num_parts: u64) -> Range { + part_from.unwrap_or(0)..part_to.unwrap_or(num_parts) +} diff --git a/tools/state-viewer/src/trie_iteration_benchmark.rs b/tools/state-viewer/src/trie_iteration_benchmark.rs new file mode 100644 index 000000000..0648d8e78 --- /dev/null +++ b/tools/state-viewer/src/trie_iteration_benchmark.rs @@ -0,0 +1,306 @@ +use std::{cell::RefCell, collections::HashMap, rc::Rc, time::Instant}; + +use unc_chain::{ChainStore, ChainStoreAccess}; +use unc_epoch_manager::EpochManager; +use unc_primitives::shard_layout::ShardLayout; +use unc_primitives::sharding::ShardChunkHeader; +use unc_primitives::state_record::{state_record_to_account_id, StateRecord}; +use unc_primitives::trie_key::col; +use unc_primitives::trie_key::trie_key_parsers::{ + parse_account_id_from_access_key_key, parse_account_id_from_trie_key_with_separator, +}; +use unc_primitives_core::types::ShardId; +use unc_store::{ShardUId, Store, Trie, TrieDBStorage}; +use framework::UncConfig; + +#[derive(Clone)] +pub enum TrieIterationType { + Full, + Shallow, +} + +impl clap::ValueEnum for TrieIterationType { + fn value_variants<'a>() -> &'a [Self] { + &[Self::Full, Self::Shallow] + } + + fn to_possible_value(&self) -> Option { + match self { + Self::Full => Some(clap::builder::PossibleValue::new("full")), + Self::Shallow => Some(clap::builder::PossibleValue::new("shallow")), + } + } +} + +#[derive(Default)] +struct ColumnCountMap(HashMap); + +impl ColumnCountMap { + fn col_to_string(col: u8) -> &'static str { + match col { + col::ACCOUNT => "ACCOUNT", + col::CONTRACT_CODE => "CONTRACT_CODE", + col::DELAYED_RECEIPT_OR_INDICES => "DELAYED_RECEIPT_OR_INDICES", + col::ACCESS_KEY => "ACCESS_KEY", + col::CONTRACT_DATA => "CONTRACT_DATA", + col::RECEIVED_DATA => "RECEIVED_DATA", + col::POSTPONED_RECEIPT_ID => "POSTPONED_RECEIPT_ID", + col::PENDING_DATA_COUNT => "PENDING_DATA_COUNT", + col::POSTPONED_RECEIPT => "POSTPONED_RECEIPT", + _ => unreachable!(), + } + } +} + +impl std::fmt::Debug for ColumnCountMap { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut map = f.debug_map(); + for (col, count) in &self.0 { + map.entry(&Self::col_to_string(*col), &count); + } + map.finish() + } +} + +#[derive(Debug)] +pub struct TrieIterationBenchmarkStats { + visited_map: ColumnCountMap, + pruned_map: ColumnCountMap, +} + +impl TrieIterationBenchmarkStats { + pub fn new() -> Self { + Self { visited_map: ColumnCountMap::default(), pruned_map: ColumnCountMap::default() } + } + + pub fn bump_visited(&mut self, col: u8) { + let entry = self.visited_map.0.entry(col).or_insert(0); + *entry += 1; + } + + pub fn bump_pruned(&mut self, col: u8) { + let entry = self.pruned_map.0.entry(col).or_insert(0); + *entry += 1; + } +} + +#[derive(clap::Parser)] +pub struct TrieIterationBenchmarkCmd { + /// The type of trie iteration. + /// - Full will iterate over all trie keys. + /// - Shallow will only iterate until full account id prefix can be parsed + /// in the trie key. Most notably this will skip any keys or data + /// belonging to accounts. + #[clap(long, default_value = "full")] + iteration_type: TrieIterationType, + + /// Limit the number of trie nodes to be iterated. + #[clap(long)] + limit: Option, +} + +impl TrieIterationBenchmarkCmd { + pub fn run(self, unc_config: UncConfig, store: Store) { + let genesis_config = &unc_config.genesis.config; + let chain_store = ChainStore::new( + store.clone(), + genesis_config.genesis_height, + unc_config.client_config.save_trie_changes, + ); + let head = chain_store.head().unwrap(); + let block = chain_store.get_block(&head.last_block_hash).unwrap(); + let epoch_manager = + EpochManager::new_from_genesis_config(store.clone(), &genesis_config).unwrap(); + let shard_layout = epoch_manager.get_shard_layout(block.header().epoch_id()).unwrap(); + + for (shard_id, chunk_header) in block.chunks().iter().enumerate() { + if chunk_header.height_included() != block.header().height() { + println!("chunk for shard {shard_id} is missing and will be skipped"); + } + } + + for (shard_id, chunk_header) in block.chunks().iter().enumerate() { + let shard_id = shard_id as ShardId; + if chunk_header.height_included() != block.header().height() { + println!("chunk for shard {shard_id} is missing, skipping it"); + continue; + } + let trie = self.get_trie(shard_id, &shard_layout, &chunk_header, &store); + + println!("shard id {shard_id:#?} benchmark starting"); + self.iter_trie(&trie); + println!("shard id {shard_id:#?} benchmark finished"); + } + } + + fn get_trie( + &self, + shard_id: ShardId, + shard_layout: &ShardLayout, + chunk_header: &ShardChunkHeader, + store: &Store, + ) -> Trie { + let shard_uid = ShardUId::from_shard_id_and_layout(shard_id, shard_layout); + // Note: here we get the previous state root but the shard layout + // corresponds to the current epoch id. In practice shouldn't + // matter as the shard layout doesn't change. + let state_root = chunk_header.prev_state_root(); + let storage = TrieDBStorage::new(store.clone(), shard_uid); + let flat_storage_chunk_view = None; + Trie::new(Rc::new(storage), state_root, flat_storage_chunk_view) + } + + fn iter_trie(&self, trie: &Trie) { + let stats = Rc::new(RefCell::new(TrieIterationBenchmarkStats::new())); + let stats_clone = Rc::clone(&stats); + + let prune_condition: Option) -> bool>> = match &self.iteration_type { + TrieIterationType::Full => None, + TrieIterationType::Shallow => Some(Box::new(move |key_nibbles| -> bool { + Self::shallow_iter_prune_condition(key_nibbles, &stats_clone) + })), + }; + + let start = Instant::now(); + let mut node_count = 0; + let mut error_count = 0; + let iter = trie.iter_with_prune_condition(prune_condition); + let iter = match iter { + Ok(iter) => iter, + Err(err) => { + tracing::error!("iter error {err:#?}"); + return; + } + }; + for item in iter { + node_count += 1; + + let (key, value) = match item { + Ok((key, value)) => (key, value), + Err(err) => { + tracing::error!("Failed to iterate node with error: {err}."); + error_count += 1; + continue; + } + }; + + stats.borrow_mut().bump_visited(key[0]); + + let state_record = StateRecord::from_raw_key_value(key.clone(), value); + let state_record = match state_record { + Some(state_record) => state_record, + None => { + println!("Failed to parse state record."); + error_count += 1; + continue; + } + }; + tracing::trace!( + target: "trie-iteration-benchmark", + "visiting column {} account id {}", + &state_record.get_type_string(),state_record_to_account_id(&state_record) + ); + + if let Some(limit) = self.limit { + if limit <= node_count { + break; + } + } + } + let duration = start.elapsed(); + println!("node count {node_count}"); + println!("error count {error_count}"); + println!("time {duration:?}"); + println!("stats\n{:#?}", stats.borrow()); + } + + fn shallow_iter_prune_condition( + key_nibbles: &Vec, + stats: &Rc>, + ) -> bool { + // Need at least 2 nibbles for the column type byte. + if key_nibbles.len() < 2 { + return false; + } + + // The key method will drop the last nibble if there is an odd number of + // them. This is on purpose because the interesting keys have even length. + let key = Self::key(key_nibbles); + let col: u8 = key[0]; + let result = match col { + // key for account only contains account id, nothing to prune + col::ACCOUNT => false, + // key for contract code only contains account id, nothing to prune + col::CONTRACT_CODE => false, + // key for delayed receipt only contains account id, nothing to prune + // key for delayed receipt indices is a shard singleton, nothing to prune + col::DELAYED_RECEIPT_OR_INDICES => false, + + // Most columns use the ACCOUNT_DATA_SEPARATOR to indicate the end + // of the accound id in the trie key. For those columns the + // partial_parse_account_id method should be used. + // The only exception is the ACCESS_KEY and dedicated method + // partial_parse_account_id_from_access_key should be used. + col::ACCESS_KEY => Self::partial_parse_account_id_from_access_key(&key, "ACCESS KEY"), + col::CONTRACT_DATA => Self::partial_parse_account_id(col, &key, "CONTRACT DATA"), + col::RECEIVED_DATA => Self::partial_parse_account_id(col, &key, "RECEIVED DATA"), + col::POSTPONED_RECEIPT_ID => { + Self::partial_parse_account_id(col, &key, "POSTPONED RECEIPT ID") + } + col::PENDING_DATA_COUNT => { + Self::partial_parse_account_id(col, &key, "PENDING DATA COUNT") + } + col::POSTPONED_RECEIPT => { + Self::partial_parse_account_id(col, &key, "POSTPONED RECEIPT") + } + _ => unreachable!(), + }; + + if result { + stats.borrow_mut().bump_pruned(col); + } + + result + + // TODO - this can be optimized, we really only need to look at the last + // byte of the key and check if it is the separator. This only works + // when doing full iteration as seeking inside of the trie would break + // the invariant that parent node key was already checked. + } + + fn key(key_nibbles: &Vec) -> Vec { + // Intentionally ignoring the odd nibble at the end. + let mut result = >::with_capacity(key_nibbles.len() / 2); + for i in (1..key_nibbles.len()).step_by(2) { + result.push(key_nibbles[i - 1] * 16 + key_nibbles[i]); + } + result + } + + fn partial_parse_account_id(col: u8, key: &Vec, col_name: &str) -> bool { + match parse_account_id_from_trie_key_with_separator(col, &key, "") { + Ok(account_id) => { + tracing::trace!( + target: "trie-iteration-benchmark", + "pruning column {col_name} account id {account_id:?}" + ); + true + } + Err(_) => false, + } + } + + // returns true if the partial key contains full account id + fn partial_parse_account_id_from_access_key(key: &Vec, col_name: &str) -> bool { + match parse_account_id_from_access_key_key(&key) { + Ok(account_id) => { + tracing::trace!( + target: "trie-iteration-benchmark", + "pruning column {col_name} account id {account_id:?}" + ); + true + } + Err(_) => false, + } + } +} diff --git a/tools/state-viewer/src/tx_dump.rs b/tools/state-viewer/src/tx_dump.rs new file mode 100644 index 000000000..329891cd3 --- /dev/null +++ b/tools/state-viewer/src/tx_dump.rs @@ -0,0 +1,40 @@ +use unc_chain::ChainStore; +use unc_chain::ChainStoreAccess; +use unc_primitives::account::id::AccountId; +use unc_primitives::block::Block; +use unc_primitives::transaction::SignedTransaction; + +/// Returns a list of transactions found in the block. +pub fn dump_tx_from_block( + chain_store: &ChainStore, + block: &Block, + select_account_ids: Option<&[AccountId]>, +) -> Vec { + let chunks = block.chunks(); + let mut res = vec![]; + for (_, chunk_header) in chunks.iter().enumerate() { + res.extend( + chain_store + .get_chunk(&chunk_header.chunk_hash()) + .unwrap() + .transactions() + .iter() + .filter(|signed_transaction| { + should_include_signed_transaction(signed_transaction, select_account_ids) + }) + .cloned() + .collect::>(), + ); + } + res +} + +fn should_include_signed_transaction( + signed_transaction: &SignedTransaction, + select_account_ids: Option<&[AccountId]>, +) -> bool { + match select_account_ids { + None => true, + Some(specified_ids) => specified_ids.contains(&signed_transaction.transaction.receiver_id), + } +} diff --git a/tools/storage-usage-delta-calculator/Cargo.toml b/tools/storage-usage-delta-calculator/Cargo.toml new file mode 100644 index 000000000..d16febd45 --- /dev/null +++ b/tools/storage-usage-delta-calculator/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "storage-usage-delta-calculator" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +serde_json.workspace = true +tokio.workspace = true +tracing.workspace = true + +unc-chain-configs.workspace = true +unc-o11y.workspace = true +unc-parameters.workspace = true +unc-primitives.workspace = true +unc-store.workspace = true +node-runtime.workspace = true diff --git a/tools/storage-usage-delta-calculator/README.md b/tools/storage-usage-delta-calculator/README.md new file mode 100644 index 000000000..a151eb9f1 --- /dev/null +++ b/tools/storage-usage-delta-calculator/README.md @@ -0,0 +1,6 @@ +# Storage delta calculator + +A small tool to compare the actual storage use with the one saved within the state. +Useful as a sanity check if we do any updates (as storage has impact on the amount of tokens that we lock in people's accounts). + +WARNING: This was built as a one-time tool - to debug the issue during migration in May 2021 - and is no longer maintained. diff --git a/tools/storage-usage-delta-calculator/src/main.rs b/tools/storage-usage-delta-calculator/src/main.rs new file mode 100644 index 000000000..f6da6e355 --- /dev/null +++ b/tools/storage-usage-delta-calculator/src/main.rs @@ -0,0 +1,42 @@ +use unc_chain_configs::{Genesis, GenesisValidationMode}; +use unc_parameters::RuntimeConfigStore; +use unc_primitives::state_record::StateRecord; +use unc_primitives::version::PROTOCOL_VERSION; +use unc_store::genesis::compute_genesis_storage_usage; +use std::fs::File; +use tracing::debug; + +/// Calculates delta between actual storage usage and one saved in state +/// output.json should contain dump of current state, +/// run 'uncd --home ~/.near/mainnet/ view_state dump_state' +/// to get it +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let env_filter = unc_o11y::EnvFilterBuilder::from_env().verbose(Some("")).finish().unwrap(); + let _subscriber = unc_o11y::default_subscriber(env_filter, &Default::default()).global(); + debug!(target: "storage-calculator", "Start"); + + let genesis = Genesis::from_file("output.json", GenesisValidationMode::Full)?; + debug!(target: "storage-calculator", "Genesis read"); + + let config_store = RuntimeConfigStore::new(None); + let config = config_store.get_config(PROTOCOL_VERSION); + let storage_usage_config = &config.fees.storage_usage_config; + let storage_usage = compute_genesis_storage_usage(&genesis, storage_usage_config); + debug!(target: "storage-calculator", "Storage usage calculated"); + + let mut result = Vec::new(); + genesis.for_each_record(|record| { + if let StateRecord::Account { account_id, account } = record { + let actual_storage_usage = storage_usage.get(account_id).unwrap(); + let saved_storage_usage = account.storage_usage(); + let delta = actual_storage_usage - saved_storage_usage; + if delta != 0 { + debug!("{},{}", account_id, delta); + result.push((account_id.clone(), delta)); + } + } + }); + serde_json::to_writer_pretty(&File::create("storage_usage_delta.json")?, &result)?; + Ok(()) +} diff --git a/tools/themis/Cargo.toml b/tools/themis/Cargo.toml new file mode 100644 index 000000000..aa43cdf07 --- /dev/null +++ b/tools/themis/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "themis" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +description = "https://en.wikipedia.org/wiki/Themis" +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +toml.workspace = true +serde.workspace = true +anyhow.workspace = true +semver.workspace = true +cargo_metadata.workspace = true diff --git a/tools/themis/src/main.rs b/tools/themis/src/main.rs new file mode 100644 index 000000000..c02a85e70 --- /dev/null +++ b/tools/themis/src/main.rs @@ -0,0 +1,47 @@ +mod rules; +mod style; +mod types; +mod utils; + +fn main() -> anyhow::Result<()> { + let workspace = utils::parse_workspace()?; + + assert!(!workspace.members.is_empty(), "unexpected empty workspace"); + + let rules = [ + rules::is_unversioned, + rules::has_publish_spec, + rules::has_rust_version, + rules::has_lint_inheritance, + rules::rust_version_matches_toolchain, + rules::has_unified_rust_edition, + rules::author_is_hello, + rules::publishable_has_license, + rules::publishable_has_unified_license, + rules::publishable_has_license_file, + rules::publishable_has_description, + rules::publishable_has_unc_link, + rules::recursively_publishable, + ]; + + let _unused_rules = [ + // TODO: https://github.com/utnet-org/utility/issues/5849 + // TODO: activate this rule when all non-private crates are sufficiently documented + rules::publishable_has_readme, + ]; + + let mut failed = false; + + for rule in rules { + if let Err(err) = rule(&workspace) { + failed |= true; + err.downcast::()?.report(&workspace); + } + } + + if failed { + std::process::exit(1); + } + + Ok(()) +} diff --git a/tools/themis/src/rules.rs b/tools/themis/src/rules.rs new file mode 100644 index 000000000..0b7216ffd --- /dev/null +++ b/tools/themis/src/rules.rs @@ -0,0 +1,404 @@ +use super::types::{ComplianceError, Expected, Outlier, Workspace}; +use super::{style, utils}; +use crate::utils::read_toml; +use anyhow::bail; +use cargo_metadata::DependencyKind; +use std::collections::{BTreeMap, HashMap}; + +/// Ensure all crates have the `publish = ` specification +pub fn has_publish_spec(workspace: &Workspace) -> anyhow::Result<()> { + let outliers: Vec<_> = workspace + .members + .iter() + .filter(|pkg| pkg.manifest.read(&["package", "publish"]).is_none()) + .map(|pkg| Outlier { path: pkg.parsed.manifest_path.clone(), found: None, extra: None }) + .collect(); + + if !outliers.is_empty() { + bail!(ComplianceError { + msg: "These packages should have the `publish` specification".to_string(), + expected: Some(Expected { value: "publish = ".to_string(), reason: None }), + outliers, + }); + } + + Ok(()) +} + +/// Ensure all publishable crates do not both specify version and rust-version as being workspace-wide +pub fn has_rust_version(workspace: &Workspace) -> anyhow::Result<()> { + let outliers: Vec<_> = workspace + .members + .iter() + .filter(|pkg| { + pkg.manifest.read(&["package", "publish"]) == Some(&toml::Value::Boolean(true)) + && pkg.manifest.read(&["package", "version", "workspace"]) + == Some(&toml::Value::Boolean(true)) + && pkg.manifest.read(&["package", "rust-version", "workspace"]) + == Some(&toml::Value::Boolean(true)) + }) + .map(|pkg| Outlier { path: pkg.parsed.manifest_path.clone(), found: None, extra: None }) + .collect(); + + if !outliers.is_empty() { + bail!(ComplianceError { + msg: "These publishable packages have Minimum Supported Rust Version (MSRV) and version both set from workspace" + .to_string(), + expected: None, + outliers, + }); + } + + Ok(()) +} + +/// Ensure all crates inherit workspace-wide lint definitions +pub fn has_lint_inheritance(workspace: &Workspace) -> anyhow::Result<()> { + let outliers: Vec<_> = workspace + .members + .iter() + .filter(|pkg| match pkg.manifest.read(&["lints", "workspace"]) { + None | Some(&toml::Value::Boolean(false)) => { + pkg.manifest.read(&["workspace"]).is_some() + } + Some(_) => false, + }) + .map(|pkg| Outlier { path: pkg.parsed.manifest_path.clone(), found: None, extra: None }) + .collect(); + + if !outliers.is_empty() { + bail!(ComplianceError { + msg: "These packages should specify `lints.workspace = true`".to_string(), + expected: None, + outliers, + }); + } + + Ok(()) +} + +/// Ensure rust-version is the same in Cargo.toml and rust-toolchain.toml +pub fn rust_version_matches_toolchain(workspace: &Workspace) -> anyhow::Result<()> { + fn get<'a>(mut val: &'a toml::Value, indexes: &[&str]) -> anyhow::Result<&'a toml::Value> { + for &i in indexes { + val = val.get(i).ok_or_else(|| anyhow::format_err!("no `{i}` in {val}"))?; + } + Ok(val) + } + + let toolchain_file = match read_toml(&workspace.root.join("rust-toolchain.toml"))? { + Some(toolchain_file) => toolchain_file, + None => return Ok(()), + }; + let toolchain_version = get(&toolchain_file, &["toolchain", "channel"])?; + + let workspace_version = + workspace.manifest.read(&["workspace", "package", "rust-version"]).ok_or_else(|| { + anyhow::format_err!("no `workspace.package.rust-version` in the root Cargo.toml") + })?; + + if toolchain_version != workspace_version { + bail!(ComplianceError { + msg: "rust-version in rust-toolchain.toml and workspace Cargo.toml differ".to_string(), + expected: None, + outliers: Vec::new(), + }); + } + + Ok(()) +} + +/// Ensure all crates are versioned to v0.0.0 +pub fn is_unversioned(workspace: &Workspace) -> anyhow::Result<()> { + let outliers = workspace + .members + .iter() + .filter(|pkg| { + !pkg.parsed.metadata["workspaces"]["independent"].as_bool().unwrap_or(false) + && pkg.parsed.version != semver::Version::new(0, 0, 0) + }) + .map(|pkg| Outlier { + path: pkg.parsed.manifest_path.clone(), + found: Some(pkg.parsed.version.to_string()), + extra: None, + }) + .collect::>(); + + if !outliers.is_empty() { + bail!(ComplianceError { + msg: "These packages shouldn't be versioned".to_string(), + expected: Some(Expected { value: "0.0.0".to_string(), reason: None }), + outliers, + }); + } + + Ok(()) +} + +/// Ensure all crates share the same rust edition +pub fn has_unified_rust_edition(workspace: &Workspace) -> anyhow::Result<()> { + let mut edition_groups = HashMap::new(); + + for pkg in &workspace.members { + *edition_groups.entry(&pkg.parsed.edition).or_insert(0) += 1; + } + + let (most_common_edition, n_compliant) = + edition_groups.into_iter().reduce(|a, b| if a.1 > b.1 { a } else { b }).unwrap(); + + let outliers = workspace + .members + .iter() + .filter(|pkg| pkg.parsed.edition != *most_common_edition) + .map(|pkg| Outlier { + path: pkg.parsed.manifest_path.clone(), + found: Some(pkg.parsed.edition.clone()), + extra: None, + }) + .collect::>(); + + if !outliers.is_empty() { + bail!(ComplianceError { + msg: "These packages have an unexpected rust edition".to_string(), + expected: Some(Expected { + value: most_common_edition.clone(), + reason: Some(format!("used by {} other packages in the workspace", n_compliant)), + }), + outliers, + }); + } + + Ok(()) +} + +const EXPECTED_AUTHOR: &str = "Hello Inc "; + +/// Ensure all crates have the appropriate author, non-exclusively of course. +pub fn author_is_hello(workspace: &Workspace) -> anyhow::Result<()> { + let outliers = workspace + .members + .iter() + .filter(|pkg| !pkg.parsed.authors.iter().any(|author| author == EXPECTED_AUTHOR)) + .map(|pkg| Outlier { + path: pkg.parsed.manifest_path.clone(), + found: Some(format!("{:?}", pkg.parsed.authors)), + extra: None, + }) + .collect::>(); + + if !outliers.is_empty() { + bail!(ComplianceError { + msg: "These packages need to be correctly authored".to_string(), + expected: Some(Expected { value: EXPECTED_AUTHOR.to_string(), reason: None }), + outliers, + }); + } + + Ok(()) +} + +/// Ensure all non-private crates have a license +pub fn publishable_has_license(workspace: &Workspace) -> anyhow::Result<()> { + let outliers = workspace + .members + .iter() + .filter(|pkg| { + utils::is_publishable(pkg) + && !(pkg.parsed.license.is_some() || pkg.parsed.license_file.is_some()) + }) + .map(|pkg| Outlier { path: pkg.parsed.manifest_path.clone(), found: None, extra: None }) + .collect::>(); + + if !outliers.is_empty() { + bail!(ComplianceError { + msg: "These non-private packages should have a `license` specification".to_string(), + expected: None, + outliers, + }); + } + + Ok(()) +} + +/// Ensure all non-private crates have a license file +/// +/// Checks for either one LICENSE file, or two LICENSE files, one of which +/// is the Apache License 2.0 and the other is the MIT license. +pub fn publishable_has_license_file(workspace: &Workspace) -> anyhow::Result<()> { + let outliers = workspace + .members + .iter() + .filter(|pkg| { + utils::is_publishable(pkg) + && !(utils::exists(pkg, "LICENSE") + || (utils::exists(pkg, "LICENSE-APACHE") && utils::exists(pkg, "LICENSE-MIT")) + || matches!(pkg + .parsed + .license_file, Some(ref l) if utils::exists(pkg, l.as_str()))) + }) + .map(|pkg| Outlier { + path: pkg.parsed.manifest_path.clone(), + found: pkg.parsed.license_file.as_ref().map(ToString::to_string), + extra: None, + }) + .collect::>(); + + if !outliers.is_empty() { + bail!(ComplianceError { + msg: "These non-private packages should have a license file".to_string(), + expected: None, + outliers, + }); + } + + Ok(()) +} + +const EXPECTED_LICENSE: &str = "MIT OR Apache-2.0"; + +/// Ensure all non-private crates use the the same expected license +pub fn publishable_has_unified_license(workspace: &Workspace) -> anyhow::Result<()> { + let outliers = workspace + .members + .iter() + .filter(|pkg| { + utils::is_publishable(pkg) + && matches!(pkg.parsed.license, Some(ref l) if l != EXPECTED_LICENSE) + // unc-vm is a wasmer fork, so we don’t control the license + && !pkg.parsed.name.starts_with("unc-vm") + }) + .map(|pkg| Outlier { + path: pkg.parsed.manifest_path.clone(), + found: pkg.parsed.license.as_ref().map(ToString::to_string), + extra: None, + }) + .collect::>(); + + if !outliers.is_empty() { + bail!(ComplianceError { + msg: "These non-private packages have an unexpected license".to_string(), + expected: Some(Expected { value: EXPECTED_LICENSE.to_string(), reason: None }), + outliers, + }); + } + + Ok(()) +} + +/// Ensure all non-private crates have a description +pub fn publishable_has_description(workspace: &Workspace) -> anyhow::Result<()> { + let outliers = workspace + .members + .iter() + .filter(|pkg| utils::is_publishable(pkg) && pkg.parsed.description.is_none()) + .map(|pkg| Outlier { path: pkg.parsed.manifest_path.clone(), found: None, extra: None }) + .collect::>(); + + if !outliers.is_empty() { + bail!(ComplianceError { + msg: "These non-private packages should have a `description`".to_string(), + expected: None, + outliers, + }); + } + + Ok(()) +} + +/// Ensure all non-private crates have a README file +pub fn publishable_has_readme(workspace: &Workspace) -> anyhow::Result<()> { + let outliers = workspace + .members + .iter() + .filter(|pkg| { + utils::is_publishable(pkg) + && !(utils::exists(pkg, "README.md") + || matches!(pkg.parsed.readme, Some(ref r) if utils::exists(pkg, r.as_str()))) + }) + .map(|pkg| Outlier { + path: pkg.parsed.manifest_path.clone(), + found: pkg.parsed.readme.as_ref().map(ToString::to_string), + extra: None, + }) + .collect::>(); + + if !outliers.is_empty() { + bail!(ComplianceError { + msg: "These non-private packages should have a readme file".to_string(), + expected: None, + outliers, + }); + } + + Ok(()) +} + +const EXPECTED_LINK: &str = "https://github.com/utnet-org/utility"; + +/// Ensure all non-private crates have appropriate repository links +pub fn publishable_has_unc_link(workspace: &Workspace) -> anyhow::Result<()> { + let outliers = workspace + .members + .iter() + .filter(|pkg| { + utils::is_publishable(pkg) + && !matches!(pkg.parsed.repository, Some(ref r) if r == EXPECTED_LINK) + }) + .map(|pkg| Outlier { + path: pkg.parsed.manifest_path.clone(), + found: pkg.parsed.repository.as_ref().map(ToString::to_string), + extra: None, + }) + .collect::>(); + + if !outliers.is_empty() { + bail!(ComplianceError { + msg: "These non-private packages need to have the appropriate `repository` link" + .to_string(), + expected: Some(Expected { value: EXPECTED_LINK.to_string(), reason: None }), + outliers, + }); + } + + Ok(()) +} + +/// Ensure all publishable crates do not depend on private crates +pub fn recursively_publishable(workspace: &Workspace) -> anyhow::Result<()> { + let mut outliers = BTreeMap::new(); + for pkg in workspace.members.iter().filter(|pkg| utils::is_publishable(pkg)) { + for dep in &pkg.parsed.dependencies { + if matches!(dep.kind, DependencyKind::Normal | DependencyKind::Build) { + if let Some(dep) = workspace.members.iter().find(|p| p.parsed.name == dep.name) { + if !utils::is_publishable(dep) { + outliers + .entry(dep.parsed.manifest_path.clone()) + .or_insert_with(Vec::new) + .push(pkg.parsed.name.clone()) + } + } + } + } + } + if !outliers.is_empty() { + bail!(ComplianceError { + msg: "These private crates break publishable crates. Either make these private crates publishable or avoid using them in the publishable crates".to_string(), + expected: None, + outliers: outliers + .into_iter() + .map(|(path, found)| Outlier { + path, + found: None, + extra: Some(format!( + "depended on by {}", + utils::human_list(found.iter().map(|name| style::fg(style::Color::White) + + style::bold() + + name + + style::reset())) + )), + }) + .collect(), + }); + } + Ok(()) +} diff --git a/tools/themis/src/style.rs b/tools/themis/src/style.rs new file mode 100644 index 000000000..85b36b69f --- /dev/null +++ b/tools/themis/src/style.rs @@ -0,0 +1,53 @@ +/// ANSI Color Codes +/// +/// +#[allow(dead_code)] +#[derive(Eq, Copy, Debug, Clone, PartialEq)] +pub enum Color { + Black, + Red, + Green, + Yellow, + Blue, + Magenta, + Cyan, + White, + Gray { shade: u8 }, + Color256(u8), +} + +impl Color { + pub fn as_ansi(self, foreground: bool) -> String { + let color = match self { + Color::Gray { shade } => return Color::Color256(0xE8 + shade).as_ansi(foreground), + Color::Color256(n) => { + return format!("\x1b[{};5;{}m", if foreground { 38 } else { 48 }, n) + } + Color::Black => 0, + Color::Red => 1, + Color::Green => 2, + Color::Yellow => 3, + Color::Blue => 4, + Color::Magenta => 5, + Color::Cyan => 6, + Color::White => 7, + }; + format!("\x1b[{}m", color + if foreground { 30 } else { 40 }) + } +} + +pub fn reset() -> &'static str { + "\x1b[0m" +} + +pub fn bg(color: Color) -> String { + color.as_ansi(false) +} + +pub fn fg(color: Color) -> String { + color.as_ansi(true) +} + +pub fn bold() -> &'static str { + "\x1b[1m" +} diff --git a/tools/themis/src/types.rs b/tools/themis/src/types.rs new file mode 100644 index 000000000..eaabc3521 --- /dev/null +++ b/tools/themis/src/types.rs @@ -0,0 +1,253 @@ +use std::fmt; +use std::rc::Rc; + +use cargo_metadata::camino::Utf8PathBuf; + +use super::style; + +#[derive(Debug)] +pub struct Package { + pub parsed: cargo_metadata::Package, + pub manifest: Manifest, +} + +#[derive(Debug)] +pub struct Workspace { + pub root: Utf8PathBuf, + pub members: Vec, + pub manifest: Rc, +} + +#[derive(Debug)] +pub struct Manifest { + raw: toml::Value, + parent: Option>, +} + +impl Manifest { + pub fn new(this: toml::Value, parent: Option>) -> Self { + Self { raw: this, parent } + } + + pub fn read(&self, path: &[&str]) -> Option<&toml::Value> { + read_toml_with_potential_inheritance(&self.raw, self.parent.as_ref().map(|p| &p.raw), path) + } +} + +#[derive(Debug)] +pub struct Outlier { + pub path: Utf8PathBuf, + pub found: Option, + pub extra: Option, +} + +#[derive(Debug)] +pub struct Expected { + pub value: String, + pub reason: Option, +} + +#[derive(Debug)] +pub struct ComplianceError { + pub msg: String, + pub expected: Option, + pub outliers: Vec, +} + +impl fmt::Display for ComplianceError { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + unimplemented!() + } +} + +impl std::error::Error for ComplianceError {} + +impl ComplianceError { + pub fn report(&self, workspace: &Workspace) { + let mut report = format!( + "{c_heading}(i) {}:{c_none}{}", + self.msg, + match &self.expected { + None => "".to_string(), + Some(Expected { value, reason }) => format!( + " [expected: {c_expected}{}{c_none}{}]", + value, + reason.as_ref().map_or("".to_string(), |reason| format!(", {}", reason)), + c_expected = style::fg(style::Color::Color256(35)) + + &style::bg(style::Color::Gray { shade: 3 }) + + style::bold(), + c_none = style::reset() + ), + }, + c_heading = style::fg(style::Color::Color256(172)) + style::bold(), + c_none = style::reset() + ); + + for Outlier { path, found, extra: reason } in &self.outliers { + report.push_str(&format!( + "\n {c_path}\u{21b3} {}{c_none}{}{}", + path.strip_prefix(&workspace.root).unwrap(), + match found { + None => "".to_string(), + Some(found) => format!( + " (found: {c_found}{}{c_none})", + found, + c_found = style::fg(style::Color::White) + + &style::bg(style::Color::Red) + + style::bold(), + c_none = style::reset() + ), + }, + match reason { + None => "".to_string(), + Some(reason) => format!(" ({})", reason), + }, + c_path = style::fg(style::Color::Gray { shade: 12 }), + c_none = style::reset(), + )); + } + eprintln!("{}", report); + } +} + +fn read_toml_with_potential_inheritance<'a>( + this: &'a toml::Value, + parent: Option<&'a toml::Value>, + path: &[&str], +) -> Option<&'a toml::Value> { + let mut raw = this; + let mut cursor = 0; + + for (i, key) in path.iter().enumerate() { + if let Some(_raw) = raw.get(key) { + (raw, cursor) = (_raw, i); + } + } + + if let (toml::Value::Table(contents), Some(parent)) = (raw, parent) { + if let Some(toml::Value::Boolean(true)) = contents.get("workspace") { + return read_toml_with_potential_inheritance(&parent["workspace"], None, path); + } + } + + (cursor == path.len() - 1).then_some(raw) +} + +#[cfg(test)] +mod tests { + use super::*; + use toml::toml; + + #[test] + fn test_read_pkg() { + let pkg = toml! { + [package] + name = "pkg" + version = "0.1.0" + edition = "2021" + authors = ["Alice "] + + [dependencies] + syn = "2" + quote = { version = "0.6", optional = true } + + [dependencies.serde] + path = "../serde" + }; + + let workspace = toml! { + [workspace] + members = ["pkg"] + }; + + assert_eq!( + Some(&toml::Value::String("2021".to_owned())), + read_toml_with_potential_inheritance(&pkg, Some(&workspace), &["package", "edition"]) + ); + + assert_eq!( + Some(&toml::Value::String("2".to_owned())), + read_toml_with_potential_inheritance(&pkg, Some(&workspace), &["dependencies", "syn"]) + ); + + assert_eq!( + Some(&toml::Value::Boolean(true)), + read_toml_with_potential_inheritance( + &pkg, + Some(&workspace), + &["dependencies", "quote", "optional"] + ) + ); + + assert_eq!( + Some(&toml! { + "path" = "../serde" + }), + read_toml_with_potential_inheritance( + &pkg, + Some(&workspace), + &["dependencies", "serde"] + ) + ); + } + + #[test] + fn test_workspace_inheritance() { + let pkg = toml! { + [package] + name = "pkg" + version.workspace = true + edition.workspace = true + authors.workspace = true + + [dependencies] + syn.workspace = true + quote.workspace = true + serde = { workspace = true } + }; + + let workspace = toml! { + [workspace.package] + version = "0.1.0" + edition = "2021" + authors = ["Alice "] + + [workspace.dependencies] + syn = "2" + quote = { version = "0.6", optional = true } + + [workspace.dependencies.serde] + path = "../serde" + }; + + assert_eq!( + Some(&toml::Value::String("2021".to_owned())), + read_toml_with_potential_inheritance(&pkg, Some(&workspace), &["package", "edition"]) + ); + + assert_eq!( + Some(&toml::Value::String("2".to_owned())), + read_toml_with_potential_inheritance(&pkg, Some(&workspace), &["dependencies", "syn"]) + ); + + assert_eq!( + Some(&toml::Value::Boolean(true)), + read_toml_with_potential_inheritance( + &pkg, + Some(&workspace), + &["dependencies", "quote", "optional"] + ) + ); + + assert_eq!( + Some(&toml! { + "path" = "../serde" + }), + read_toml_with_potential_inheritance( + &pkg, + Some(&workspace), + &["dependencies", "serde"] + ) + ); + } +} diff --git a/tools/themis/src/utils.rs b/tools/themis/src/utils.rs new file mode 100644 index 000000000..8d075bfcb --- /dev/null +++ b/tools/themis/src/utils.rs @@ -0,0 +1,85 @@ +use std::{fs, io, rc::Rc}; + +use cargo_metadata::{camino::Utf8PathBuf, CargoOpt, MetadataCommand}; + +use super::types::{Manifest, Package, Workspace}; + +pub fn read_toml(path: &Utf8PathBuf) -> anyhow::Result> { + match fs::read(path) { + Ok(p) => Ok(Some(toml::from_slice(&p)?)), + Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None), + Err(e) => Err(e.into()), + } +} + +pub fn parse_workspace() -> anyhow::Result { + let mut cmd = MetadataCommand::new(); + + cmd.features(CargoOpt::AllFeatures); + cmd.no_deps(); + + let metadata = cmd.exec()?; + + let workspace_manifest = Rc::new(Manifest::new( + match read_toml(&metadata.workspace_root.join("Cargo.toml"))? { + Some(raw) => raw, + None => anyhow::bail!("workspace manifest not found"), + }, + None, + )); + + let members = metadata + .packages + .iter() + .filter(|package| metadata.workspace_members.contains(&package.id)) + .map(|package| { + Ok(Package { + manifest: Manifest::new( + match read_toml(&package.manifest_path)? { + Some(raw) => raw, + None => anyhow::bail!("package manifest `{}` not found", package.name), + }, + Some(workspace_manifest.clone()), + ), + parsed: package.clone(), + }) + }) + .collect::>()?; + + Ok(Workspace { root: metadata.workspace_root, members, manifest: workspace_manifest }) +} + +/// Checks if the crate specified is explicitly publishable +pub fn is_publishable(pkg: &Package) -> bool { + !matches!(pkg.manifest.read(&["package", "publish"]), Some(toml::Value::Boolean(false))) +} + +/// Checks if the file specified exists relative to the crate folder +pub fn exists(pkg: &Package, file: &str) -> bool { + pkg.parsed.manifest_path.parent().unwrap().join(file).exists() +} + +/// Prints a string-ish iterator as a human-readable list +/// +/// ``` +/// assert_eq!( +/// print_list(&["a", "b", "c"]), +/// "a, b and c" +/// ); +/// ``` +pub fn human_list(i: I) -> String +where + I: Iterator, + T: AsRef, +{ + let mut items = i.peekable(); + let mut s = match items.next() { + Some(s) => s.as_ref().to_owned(), + None => return String::new(), + }; + while let Some(i) = items.next() { + s += if items.peek().is_some() { ", " } else { " and " }; + s += i.as_ref(); + } + s +} diff --git a/tools/undo-block/Cargo.toml b/tools/undo-block/Cargo.toml new file mode 100644 index 000000000..e97ebfb10 --- /dev/null +++ b/tools/undo-block/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "unc-undo-block" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +clap.workspace = true +tracing.workspace = true +chrono.workspace = true + +unc-chain.workspace = true +unc-chain-configs.workspace = true +unc-epoch-manager.workspace = true +unc-store.workspace = true +framework.workspace = true +unc-primitives.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-chain/nightly", + "unc-epoch-manager/nightly", + "unc-primitives/nightly", + "unc-store/nightly", + "framework/nightly", +] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-chain/nightly_protocol", + "unc-epoch-manager/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-store/nightly_protocol", + "framework/nightly_protocol", +] diff --git a/tools/undo-block/src/cli.rs b/tools/undo-block/src/cli.rs new file mode 100644 index 000000000..718244593 --- /dev/null +++ b/tools/undo-block/src/cli.rs @@ -0,0 +1,49 @@ +use unc_chain::ChainStore; +use unc_chain_configs::GenesisValidationMode; +use unc_epoch_manager::EpochManager; +use unc_store::{Mode, NodeStorage}; +use framework::load_config; +use std::path::Path; + +#[derive(clap::Parser)] +pub struct UndoBlockCommand { + /// Only reset the block head to the tail block. Does not reset the header head. + #[arg(short, long)] + reset_only_body: bool, +} + +impl UndoBlockCommand { + pub fn run( + self, + home_dir: &Path, + genesis_validation: GenesisValidationMode, + ) -> anyhow::Result<()> { + let unc_config = load_config(home_dir, genesis_validation) + .unwrap_or_else(|e| panic!("Error loading config: {:#}", e)); + + let store_opener = NodeStorage::opener( + home_dir, + unc_config.config.archive, + &unc_config.config.store, + None, + ); + + let storage = store_opener.open_in_mode(Mode::ReadWrite).unwrap(); + let store = storage.get_hot_store(); + + let epoch_manager = + EpochManager::new_arc_handle(store.clone(), &unc_config.genesis.config); + + let mut chain_store = ChainStore::new( + store, + unc_config.genesis.config.genesis_height, + unc_config.client_config.save_trie_changes, + ); + + if self.reset_only_body { + crate::undo_only_block_head(&mut chain_store, &*epoch_manager) + } else { + crate::undo_block(&mut chain_store, &*epoch_manager) + } + } +} diff --git a/tools/undo-block/src/lib.rs b/tools/undo-block/src/lib.rs new file mode 100644 index 000000000..95a13a0b1 --- /dev/null +++ b/tools/undo-block/src/lib.rs @@ -0,0 +1,82 @@ +use chrono::Utc; +use unc_chain::types::{EpochManagerAdapter, LatestKnown}; +use unc_chain::{ChainStore, ChainStoreAccess, ChainStoreUpdate}; +use unc_primitives::block::Tip; +use unc_primitives::utils::to_timestamp; + +pub mod cli; + +pub fn undo_block( + chain_store: &mut ChainStore, + epoch_manager: &dyn EpochManagerAdapter, +) -> anyhow::Result<()> { + let current_head = chain_store.head()?; + let current_head_hash = current_head.last_block_hash; + let prev_block_hash = current_head.prev_block_hash; + let prev_header = chain_store.get_block_header(&prev_block_hash)?; + let prev_tip = Tip::from_header(&prev_header); + let current_head_height = current_head.height; + let prev_block_height = prev_tip.height; + + tracing::info!(target: "uncd", ?prev_block_hash, ?current_head_hash, ?prev_block_height, ?current_head_height, "Trying to update head"); + + // stop if it's already the final block + if chain_store.final_head()?.height >= current_head.height { + return Err(anyhow::anyhow!("Cannot revert past final block")); + } + + let mut chain_store_update = ChainStoreUpdate::new(chain_store); + + chain_store_update.clear_head_block_data(epoch_manager)?; + + chain_store_update.save_head(&prev_tip)?; + + chain_store_update.commit()?; + + chain_store.save_latest_known(LatestKnown { + height: prev_tip.height, + seen: to_timestamp(Utc::now()), + })?; + + let new_chain_store_head = chain_store.head()?; + let new_chain_store_header_head = chain_store.header_head()?; + let new_head_height = new_chain_store_head.height; + let new_header_height = new_chain_store_header_head.height; + + tracing::info!(target: "uncd", ?new_head_height, ?new_header_height, "The current chain store shows"); + Ok(()) +} + +pub fn undo_only_block_head( + chain_store: &mut ChainStore, + epoch_manager: &dyn EpochManagerAdapter, +) -> anyhow::Result<()> { + let current_head = chain_store.head()?; + let current_head_height = current_head.height; + let current_header_head = chain_store.header_head()?; + let current_header_height = current_header_head.height; + + let tail_height = chain_store.tail()?; + let tail_header = chain_store.get_block_header_by_height(tail_height)?; + let new_head = Tip::from_header(&tail_header); + + tracing::info!(target: "uncd", ?tail_height, ?current_head_height, ?current_header_height, "Trying to update head"); + + if current_head_height == tail_height { + tracing::info!(target: "uncd", "Body head is alreay at the oldest block."); + return Ok(()); + } + + let mut chain_store_update = ChainStoreUpdate::new(chain_store); + chain_store_update.clear_head_block_data(epoch_manager)?; + chain_store_update.save_body_head(&new_head)?; + chain_store_update.commit()?; + + let new_chain_store_head = chain_store.head()?; + let new_chain_store_header_head = chain_store.header_head()?; + let new_head_height = new_chain_store_head.height; + let new_header_height = new_chain_store_header_head.height; + + tracing::info!(target: "uncd", ?new_head_height, ?new_header_height, "The current chain store shows"); + Ok(()) +} diff --git a/uncd/Cargo.toml b/uncd/Cargo.toml new file mode 100644 index 000000000..b187989fc --- /dev/null +++ b/uncd/Cargo.toml @@ -0,0 +1,131 @@ +[package] +name = "uncd" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[[bin]] +path = "src/main.rs" +name = "uncd" + +[dependencies] +actix.workspace = true +anyhow.workspace = true +clap.workspace = true +futures.workspace = true +once_cell.workspace = true +openssl-probe.workspace = true +opentelemetry.workspace = true +rayon.workspace = true +rlimit.workspace = true +serde.workspace = true +serde_json.workspace = true +shell-escape.workspace = true +thiserror.workspace = true +tikv-jemallocator.workspace = true +tokio.workspace = true +tracing.workspace = true +yansi.workspace = true + +framework.workspace = true +unc-amend-genesis.workspace = true +unc-chain-configs.workspace = true +unc-client.workspace = true +unc-cold-store-tool.workspace = true +unc-config-utils.workspace = true +unc-crypto.workspace = true +unc-database-tool.workspace = true +unc-dyn-configs.workspace = true +unc-epoch-sync-tool = { workspace = true, optional = true } +unc-flat-storage.workspace = true +unc-fork-network.workspace = true +unc-jsonrpc-primitives.workspace = true +unc-mirror.workspace = true +unc-network.workspace = true +unc-o11y.workspace = true +unc-performance-metrics.workspace = true +unc-ping.workspace = true +unc-primitives.workspace = true +unc-state-parts.workspace = true +unc-state-parts-dump-check.workspace = true +unc-state-viewer.workspace = true +unc-store.workspace = true +unc-undo-block.workspace = true + +[build-dependencies] +anyhow.workspace = true +rustc_version = "0.4" + +[features] +default = ["json_rpc", "calimero_zero_storage"] + +performance_stats = ["framework/performance_stats"] +c_memory_stats = ["framework/c_memory_stats"] +test_features = ["framework/test_features"] +expensive_tests = ["framework/expensive_tests"] +no_cache = ["framework/no_cache"] +json_rpc = ["framework/json_rpc"] +protocol_feature_fix_staking_threshold = ["framework/protocol_feature_fix_staking_threshold"] +serialize_all_state_changes = ["framework/serialize_all_state_changes"] +new_epoch_sync = ["framework/new_epoch_sync", "dep:unc-epoch-sync-tool"] + +nightly = [ + "nightly_protocol", + "protocol_feature_fix_staking_threshold", + "serialize_all_state_changes", + "unc-chain-configs/nightly", + "unc-client/nightly", + "unc-database-tool/nightly", + "unc-dyn-configs/nightly", + "unc-fork-network/nightly", + "unc-jsonrpc-primitives/nightly", + "unc-mirror/nightly", + "unc-network/nightly", + "unc-o11y/nightly", + "unc-ping/nightly", + "unc-primitives/nightly", + "unc-state-parts-dump-check/nightly", + "unc-state-parts/nightly", + "unc-store/nightly", + "unc-undo-block/nightly", + "framework/nightly", +] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-client/nightly_protocol", + "unc-database-tool/nightly_protocol", + "unc-dyn-configs/nightly_protocol", + "unc-fork-network/nightly_protocol", + "unc-jsonrpc-primitives/nightly_protocol", + "unc-mirror/nightly_protocol", + "unc-network/nightly_protocol", + "unc-o11y/nightly_protocol", + "unc-ping/nightly_protocol", + "unc-primitives/nightly_protocol", + "unc-state-parts-dump-check/nightly_protocol", + "unc-state-parts/nightly_protocol", + "unc-store/nightly_protocol", + "unc-undo-block/nightly_protocol", + "framework/nightly_protocol", +] + +calimero_zero_storage = [ + "unc-primitives/calimero_zero_storage", +] + +# Compile with option to emit a detailed trace of IO operations and their +# results that can be replayed on the estimator. To produce the output, compile +# with this flag and then enable it at runtime with `--record-io-trace=path` option. +io_trace = ["unc-store/io_trace", "unc-o11y/io_trace", "framework/io_trace"] + +sandbox = ["framework/sandbox"] + +[package.metadata.workspaces] +independent = true diff --git a/uncd/build.rs b/uncd/build.rs new file mode 100644 index 000000000..86f68f733 --- /dev/null +++ b/uncd/build.rs @@ -0,0 +1,118 @@ +//! Generates uncd version information and stores it in environment variables. +//! +//! The build script sets `NEARD_VERSION` and `NEARD_BUILD` to be uncd version +//! and build respectively. Version is the official semver such as 1.24.1 if +//! the executable is built from a release branch or `trunk` if it’s built from +//! master. Build is a `git describe` of the commit the binary was built at +//! (for official releases it should be the same as version). + +use anyhow::{anyhow, Result}; + +use std::os::unix::ffi::OsStringExt; + +/// Returns value of given environment variable or error if missing. +/// +/// This also outputs necessary ‘cargo:rerun-if-env-changed’ tag to make sure +/// build script is rerun if the environment variable changes. +fn env(key: &str) -> Result { + println!("cargo:rerun-if-env-changed={}", key); + std::env::var_os(key).ok_or_else(|| anyhow!("missing ‘{}’ environment variable", key)) +} + +/// Calls program with given arguments and returns its standard output. If +/// calling the program fails or it exits with non-zero exit status returns an +/// error. +fn command(prog: &str, args: &[&str], cwd: Option) -> Result> { + println!("cargo:rerun-if-env-changed=PATH"); + let mut cmd = std::process::Command::new(prog); + cmd.args(args); + cmd.stderr(std::process::Stdio::inherit()); + if let Some(cwd) = cwd { + cmd.current_dir(cwd); + } + let out = cmd.output()?; + if out.status.success() { + let mut stdout = out.stdout; + if let Some(b'\n') = stdout.last() { + stdout.pop(); + if let Some(b'\r') = stdout.last() { + stdout.pop(); + } + } + Ok(stdout) + } else if let Some(code) = out.status.code() { + Err(anyhow!("{}: terminated with {}", prog, code)) + } else { + Err(anyhow!("{}: killed by signal", prog)) + } +} + +/// Returns version read from git repository or ‘unknown’ if could not be +/// determined. +/// +/// Uses `git describe --always --dirty=-modified` to get the version. For +/// builds on release tags this will return that tag. In other cases the +/// version will describe the commit by including its hash. If the working +/// directory isn’t clean, the version will include `-modified` suffix. +fn get_git_version() -> Result { + // Figure out git directory. Don’t just assume it’s ../.git because that + // doesn’t work with git worktrees so use `git rev-parse --git-dir` instead. + let pkg_dir = std::path::PathBuf::from(env("CARGO_MANIFEST_DIR")?); + let git_dir = command("git", &["rev-parse", "--git-dir"], Some(pkg_dir)); + let git_dir = match git_dir { + Ok(git_dir) => std::path::PathBuf::from(std::ffi::OsString::from_vec(git_dir)), + Err(msg) => { + // We’re probably not inside of a git repository so report git + // version as unknown. + println!("cargo:warning=unable to determine git version (not in git repository?)"); + println!("cargo:warning={}", msg); + return Ok("unknown".to_string()); + } + }; + + // Make Cargo rerun us if currently checked out commit or the state of the + // working tree changes. We try to accomplish that by looking at a few + // crucial git state files. This probably may result in some false + // negatives but it’s best we’ve got. + for subpath in ["HEAD", "logs/HEAD", "index"] { + let path = git_dir.join(subpath).canonicalize()?; + println!("cargo:rerun-if-changed={}", path.display()); + } + + // * --always → if there is no matching tag, use commit hash + // * --dirty=-modified → append ‘-modified’ if there are local changes + // * --tags → consider tags even if they are unnanotated + // * --match=[0-9]* → only consider tags starting with a digit; this + // prevents tags such as `crates-0.14.0` from being considered + let args = &["describe", "--always", "--dirty=-modified", "--tags", "--match=[0-9]*"]; + let out = command("git", args, None)?; + match String::from_utf8_lossy(&out) { + std::borrow::Cow::Borrowed(version) => Ok(version.trim().to_string()), + std::borrow::Cow::Owned(version) => Err(anyhow!("git: invalid output: {}", version)), + } +} + +fn main() { + if let Err(err) = try_main() { + eprintln!("{}", err); + std::process::exit(1); + } +} + +fn try_main() -> Result<()> { + let version = env("CARGO_PKG_VERSION")?; + let version = match version.to_string_lossy() { + std::borrow::Cow::Borrowed("0.0.0") => "trunk", + std::borrow::Cow::Borrowed(version) => version, + std::borrow::Cow::Owned(version) => { + anyhow::bail!("invalid ‘CARGO_PKG_VERSION’: {}", version) + } + }; + println!("cargo:rustc-env=NEARD_VERSION={}", version); + + println!("cargo:rustc-env=NEARD_BUILD={}", get_git_version()?); + + println!("cargo:rustc-env=NEARD_RUSTC_VERSION={}", rustc_version::version()?); + + Ok(()) +} diff --git a/uncd/res/invalid_proof.json b/uncd/res/invalid_proof.json new file mode 100644 index 000000000..339075ff8 --- /dev/null +++ b/uncd/res/invalid_proof.json @@ -0,0 +1,151 @@ +{ + "jsonrpc": "2.0", + "result": { + "block_header_lite": { + "inner_lite": { + "block_merkle_root": "CwePvyiYLFqGfruuMAP98HEYZva9g1R4CG8hWQ8cRc8x", + "epoch_id": "7CbfSgxDvmShTkWKg7vXPRKbNfWkTTpAQm32pJystEkw", + "height": 75984871, + "next_bp_hash": "C3qDzKDx99Sq38xkb3DRr4s3mEZvp12fAkGiw1sawFJR", + "next_epoch_id": "B179NnZqaiaTmqXA1S9WDPHfiZg2XJzGciQorJf9nLoE", + "outcome_root": "6xeVbqxTT5GMEVCyHUpnDkK5tHdZnoNWyidhpU3gWLvS", + "prev_state_root": "GM6ujpkBMXquLf42qqzZNSL2z9nJhfLcgyK4kuW7EgM3", + "timestamp": 1665478647333841203, + "timestamp_nanosec": "1665478647333841203" + }, + "inner_rest_hash": "72JN2ZLn6jQPagcFvPF6xkFviMzgKuGR8GkVFLnD6Cea", + "prev_block_hash": "BSdNKe62rKG9DJTB8tvswhTPRoVsbcKE8e3SEKyubVD2" + }, + "block_proof": [ + { + "direction": "Right", + "hash": "Fbbike2JLx7uNRfS4GsiStNveRxq1KiuVGB28cjWmMuW" + }, + { + "direction": "Right", + "hash": "AvmVuDZQji88mDodvRyunVnkj58zh72ZPXBK5ieYg1D3" + }, + { + "direction": "Left", + "hash": "9YQ2twmSeaZeqQC7f4pJVehEmKsSimZU1Yddeo5b8SAf" + }, + { + "direction": "Right", + "hash": "9RmQED83eAFqRHc5LwB7TUxdLeimyXzMxG3v2rTZ4RAs" + }, + { + "direction": "Right", + "hash": "84jBVDoehcRcW2n6phJDsVgaq4bUZcrLQAScsM7Ar4bz" + }, + { + "direction": "Right", + "hash": "83rP4hif64nhhoW5ho9kWBZLNjBPkf8hiqwfWeU5pSqD" + }, + { + "direction": "Left", + "hash": "Bie6ib3fmVBcMn44ioZoMEunSFjyZnsrBsH4vrs9UCCL" + }, + { + "direction": "Left", + "hash": "DANS18qakKBaM7iHQ3xzaWM9YF1DmLtQ6rcKdN8xcSkZ" + }, + { + "direction": "Left", + "hash": "ENtjwKNBrKd9nbR1amQ7erPnE8GhznE7RXWVCiDZpAvT" + }, + { + "direction": "Left", + "hash": "8hS18TKPKMaeS1t3n8zQMZdQZpar2sbzxT8SkgWz7YJm" + }, + { + "direction": "Left", + "hash": "CC4pm4eNYxd8VofwpAZnNgvPNFRhmWbRG65scrJKMMKY" + }, + { + "direction": "Left", + "hash": "6KL94JaXFbcJRE6QMWJXMzVpFvLRg2yWYNGZpjemGH1F" + }, + { + "direction": "Left", + "hash": "FSEj5etq1PNJcJ232o9Vx275gPKm7j9BzRXYsWo7EPRV" + }, + { + "direction": "Left", + "hash": "HStoStAo63gYvsChmoWVB1zQJyKe1MizNMQGEpLyauAq" + }, + { + "direction": "Left", + "hash": "FhFqLiwhtWNjJmiW1vgQsYbk28HTZuzTAT6PpgASjd9F" + }, + { + "direction": "Left", + "hash": "44VtmsZ5uvCEYXa8aKnSCQ4VSEG12WatAdP1iRZeFr9i" + }, + { + "direction": "Left", + "hash": "Cb5LzegmpJsy1hwgjzHZHcS6fD5UBMCWdXvQgAsosVAT" + }, + { + "direction": "Left", + "hash": "BYk65Mexkx71QChUVbXXVv12S9KP1jrosHPQmEctQ4Ri" + }, + { + "direction": "Left", + "hash": "9UVUdykWvaFvh3PnfSthmAbBWmSdmftvCfZdDdGUioq" + }, + { + "direction": "Left", + "hash": "59rbk8LwGtgMcZMB6PDnPkcBmicVK1ft9gitzcKpmPck" + }, + { + "direction": "Left", + "hash": "CFRnvW612Du9TfKRMxMyvwvghtN1SzQwKRRCAFWKwkL3" + } + ], + "outcome_proof": { + "block_hash": "4CVvjzoVdnK3WCuaKBRyApuNwmM3ieNrRJ9c4SFqD1jt", + "id": "ALRJ4gpVhbX36dYbVPLLPVX76Kw9j1dMiNxqR8uCkifh", + "outcome": { + "executor_id": "app.nearcrowd.near", + "gas_burnt": 2428325704054, + "logs": [], + "metadata": { + "gas_profile": null, + "version": 1 + }, + "receipt_ids": [ + "GRWZpMCdF4A6YgJ4LA3ZC3wHytc6qEkNo4mjHwGSLmTN" + ], + "status": { + "SuccessReceiptId": "GRWZpMCdF4A6YgJ4LA3ZC3wHytc6qEkNo4mjHwGSLmTN" + }, + "tokens_burnt": "242832570405400000000" + }, + "proof": [ + { + "direction": "Right", + "hash": "8osbeLw7rbTRZ2gv6ZsyaQSKUEnFdP76cJR7pJBm6YGB" + }, + { + "direction": "Right", + "hash": "8PLiETbZJFxMLa9LPrhvHPiUBEjyy6jAk2sbpvwLxU2R" + }, + { + "direction": "Right", + "hash": "EeDfo2Xzqk6yyY2SANKXc3AB1Lt3FNpA8hAqYRfcDPUX" + } + ] + }, + "outcome_root_proof": [ + { + "direction": "Right", + "hash": "7TfEex3WTm4QwiWYiXxtvL3tbuXEi1HTY8t9qrAEmpKj" + }, + { + "direction": "Left", + "hash": "kXAGbpnTnzVA6aCb8oYN3S9ppSkVgrve1FQqhTEars8" + } + ] + }, + "id": "dontcare" +} \ No newline at end of file diff --git a/uncd/res/proof_example.json b/uncd/res/proof_example.json new file mode 100644 index 000000000..06ec218b4 --- /dev/null +++ b/uncd/res/proof_example.json @@ -0,0 +1,87 @@ +{ + "jsonrpc": "2.0", + "result": { + "block_header_lite": { + "inner_lite": { + "block_merkle_root": "3ZGUAkHpwttPEXyShWYxsJiUagTULCJ1wLob5oa3StX4", + "epoch_id": "11111111111111111111111111111111", + "height": 38, + "next_bp_hash": "9ApDrWbBX94wDVNE1dgLEpxuvAsuujT11PqtrNCmiyPm", + "next_epoch_id": "5M93i2Jobe6xCaeo6UYxbYYsa3VikZzCnL2dttGvjUm", + "outcome_root": "55TttfAcnk4VxftNs6Dr7sxmXHc72H5qjP7uzyoDQjkY", + "prev_state_root": "A7GepR5GCkqJe6Ww6wUjA7GZm8UpyaZKLnoHHhmqeDkb", + "timestamp": 1665470154799724000, + "timestamp_nanosec": "1665470154799724000" + }, + "inner_rest_hash": "FmVxjEnGv7a3KWQao6dqUmTnoxrRJpYPmv2qjMFxU5HL", + "prev_block_hash": "E1C75Hm8mCwFZ9Z1ycmLNjZRBCqbHWMzao6oQ4fgHD6E" + }, + "block_proof": [ + { + "direction": "Right", + "hash": "6iN1ormxiiUCZShNXQBHSyRWiJNhn6TAgCE7eV9E6kSW" + }, + { + "direction": "Left", + "hash": "5PAXdfXYZEH3pWsWdoDiJqtQ98QyGu24RW2JbmcyMuDp" + }, + { + "direction": "Left", + "hash": "92wP1qQ15zgHt1eHRUemFTn7Db4AdSrk99sPtMCnWLvv" + }, + { + "direction": "Right", + "hash": "2g2BvzCWAZN9hnA68HdyXtQhF3jonNMdwmVDxnrm8HKu" + }, + { + "direction": "Right", + "hash": "7vW2aH6c1kvkSzV9Z4aQePEDUtQtFThPRLZkJJtnzU36" + }, + { + "direction": "Left", + "hash": "8sNnmzXz1Z875zBkYqPjBxjHdqqpAnwG9S5KXkjar1pK" + }, + { + "direction": "Right", + "hash": "EY1jbMTZeufN2Ajs3SEAqF7prQe91jTbvT7zRADyNVho" + } + ], + "outcome_proof": { + "block_hash": "HqZHDTHSqH6Az22SZgFUjodGFDtfC2qSt4v9uYFpLuFC", + "id": "BonLrZ2H6KXnp8nwk2k8oCkcYhAFCU4bA8kWsXVv7pSs", + "outcome": { + "executor_id": "node0", + "gas_burnt": 2427961246812, + "logs": [], + "metadata": { + "gas_profile": null, + "version": 1 + }, + "receipt_ids": [ + "Ehno8reYKoLuUgyL8zq8gY5h2T4gYAQ5qCMVUPJmrLhJ" + ], + "status": { + "SuccessReceiptId": "Ehno8reYKoLuUgyL8zq8gY5h2T4gYAQ5qCMVUPJmrLhJ" + }, + "tokens_burnt": "242796124681200000000" + }, + "proof": [ + { + "direction": "Left", + "hash": "EWBApRfd7rMKqACqs71dTPsPGX6hx4HBckBLG6rGDrAj" + } + ] + }, + "outcome_root_proof": [ + { + "direction": "Left", + "hash": "7tkzFg8RHBmMw1ncRJZCCZAizgq4rwCftTKYLce8RU8t" + }, + { + "direction": "Left", + "hash": "4A9zZ1umpi36rXiuaKYJZgAjhUH9WoTrnSBXtA3wMdV2" + } + ] + }, + "id": "dontcare" +} \ No newline at end of file diff --git a/uncd/src/cli.rs b/uncd/src/cli.rs new file mode 100644 index 000000000..47359d705 --- /dev/null +++ b/uncd/src/cli.rs @@ -0,0 +1,904 @@ +#[cfg(unix)] +use anyhow::Context; +use unc_amend_genesis::AmendGenesisCommand; +use unc_chain_configs::GenesisValidationMode; +use unc_client::ConfigUpdater; +use unc_cold_store_tool::ColdStoreCommand; +use unc_database_tool::commands::DatabaseCommand; +use unc_dyn_configs::{UpdateableConfigLoader, UpdateableConfigLoaderError, UpdateableConfigs}; +#[cfg(feature = "new_epoch_sync")] +use unc_epoch_sync_tool::EpochSyncCommand; +use unc_flat_storage::commands::FlatStorageCommand; +use unc_fork_network::cli::ForkNetworkCommand; +use unc_jsonrpc_primitives::types::light_client::RpcLightClientExecutionProofResponse; +use unc_mirror::MirrorCommand; +use unc_network::tcp; +use unc_o11y::tracing_subscriber::EnvFilter; +use unc_o11y::{ + default_subscriber, default_subscriber_with_opentelemetry, BuildEnvFilterError, + EnvFilterBuilder, +}; +use unc_ping::PingCommand; +use unc_primitives::hash::CryptoHash; +use unc_primitives::merkle::compute_root_from_path; +use unc_primitives::types::{Gas, NumSeats, NumShards}; +use unc_state_parts::cli::StatePartsCommand; +use unc_state_parts_dump_check::cli::StatePartsDumpCheckCommand; +use unc_state_viewer::StateViewerSubCommand; +use unc_store::db::RocksDB; +use unc_store::Mode; +use unc_undo_block::cli::UndoBlockCommand; +use serde_json::Value; +use std::fs::File; +use std::io::BufReader; +use std::net::SocketAddr; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use tokio::sync::broadcast; +use tokio::sync::broadcast::Receiver; +use tracing::{debug, error, info, warn}; + +/// NEAR Protocol Node +#[derive(clap::Parser)] +#[clap(version = crate::NEARD_VERSION_STRING.as_str())] +#[clap(subcommand_required = true, arg_required_else_help = true)] +pub(super) struct NeardCmd { + #[clap(flatten)] + opts: NeardOpts, + #[clap(subcommand)] + subcmd: NeardSubCommand, +} + +impl NeardCmd { + pub(super) fn parse_and_run() -> anyhow::Result<()> { + let neard_cmd: Self = clap::Parser::parse(); + + // Enable logging of the current thread. + let _subscriber_guard = default_subscriber( + make_env_filter(neard_cmd.opts.verbose_target())?, + &neard_cmd.opts.o11y, + ) + .local(); + + info!( + target: "uncd", + version = crate::NEARD_VERSION, + build = crate::NEARD_BUILD, + latest_protocol = unc_primitives::version::PROTOCOL_VERSION + ); + + #[cfg(feature = "test_features")] + { + error!("THIS IS A NODE COMPILED WITH ADVERSARIAL BEHAVIORS. DO NOT USE IN PRODUCTION."); + if std::env::var("ADVERSARY_CONSENT").unwrap_or_default() != "1" { + error!( + "To run a node with adversarial behavior enabled give your consent \ + by setting an environment variable:" + ); + error!("ADVERSARY_CONSENT=1"); + std::process::exit(1); + } + } + + let home_dir = neard_cmd.opts.home.clone(); + let genesis_validation = if neard_cmd.opts.unsafe_fast_startup { + GenesisValidationMode::UnsafeFast + } else { + GenesisValidationMode::Full + }; + + match neard_cmd.subcmd { + NeardSubCommand::Init(cmd) => cmd.run(&home_dir)?, + NeardSubCommand::Localnet(cmd) => cmd.run(&home_dir), + NeardSubCommand::Run(cmd) => cmd.run( + &home_dir, + genesis_validation, + neard_cmd.opts.verbose_target(), + &neard_cmd.opts.o11y, + ), + + NeardSubCommand::StateViewer(cmd) => { + let mode = if cmd.readwrite { Mode::ReadWrite } else { Mode::ReadOnly }; + cmd.subcmd.run(&home_dir, genesis_validation, mode, cmd.store_temperature); + } + + NeardSubCommand::RecompressStorage(cmd) => { + cmd.run(&home_dir); + } + NeardSubCommand::VerifyProof(cmd) => { + cmd.run(); + } + NeardSubCommand::Ping(cmd) => { + cmd.run()?; + } + NeardSubCommand::Mirror(cmd) => { + cmd.run()?; + } + NeardSubCommand::AmendGenesis(cmd) => { + cmd.run()?; + } + NeardSubCommand::ColdStore(cmd) => { + cmd.run(&home_dir)?; + } + NeardSubCommand::StateParts(cmd) => { + cmd.run()?; + } + NeardSubCommand::FlatStorage(cmd) => { + cmd.run(&home_dir, genesis_validation)?; + } + NeardSubCommand::ValidateConfig(cmd) => { + cmd.run(&home_dir)?; + } + NeardSubCommand::UndoBlock(cmd) => { + cmd.run(&home_dir, genesis_validation)?; + } + NeardSubCommand::Database(cmd) => { + cmd.run(&home_dir)?; + } + NeardSubCommand::ForkNetwork(cmd) => { + cmd.run( + &home_dir, + genesis_validation, + neard_cmd.opts.verbose_target(), + &neard_cmd.opts.o11y, + )?; + } + NeardSubCommand::StatePartsDumpCheck(cmd) => { + cmd.run()?; + } + #[cfg(feature = "new_epoch_sync")] + NeardSubCommand::EpochSync(cmd) => { + cmd.run(&home_dir)?; + } + }; + Ok(()) + } +} + +#[derive(clap::Parser)] +pub(super) struct StateViewerCommand { + /// By default state viewer opens rocks DB in the read only mode, which allows it to run + /// multiple instances in parallel and be sure that no unintended changes get written to the DB. + /// In case an operation needs to write to caches, a read-write mode may be needed. + #[clap(long, short = 'w')] + readwrite: bool, + /// What store temperature should the state viewer open. Allowed values are hot and cold but + /// cold is only available when cold_store is configured. + /// Cold temperature actually means the split store will be used. + #[clap(long, short = 't', default_value = "hot")] + store_temperature: unc_store::Temperature, + #[clap(subcommand)] + subcmd: StateViewerSubCommand, +} + +#[derive(clap::Parser, Debug)] +struct NeardOpts { + /// Sets verbose logging for the given target, or for all targets if no + /// target is given. + #[clap(long, name = "target")] + verbose: Option>, + /// Directory for config and data. + #[clap(long, value_parser, default_value_os = crate::DEFAULT_HOME.as_os_str())] + home: PathBuf, + /// Skips consistency checks of genesis.json (and records.json) upon startup. + /// Let's you start `uncd` slightly faster. + #[clap(long)] + unsafe_fast_startup: bool, + /// Enables export of span data using opentelemetry protocol. + #[clap(flatten)] + o11y: unc_o11y::Options, +} + +impl NeardOpts { + // TODO(nikurt): Delete in 1.38 or later. + pub fn verbose_target(&self) -> Option<&str> { + self.verbose.as_ref().map(|inner| { + tracing::error!(target: "uncd", "--verbose flag is deprecated, please use RUST_LOG or log_config.json instead."); + inner.as_ref().map_or("", String::as_str) + }) + } +} + +#[derive(clap::Parser)] +pub(super) enum NeardSubCommand { + /// Initializes NEAR configuration + Init(InitCmd), + /// Runs NEAR node + Run(RunCmd), + /// Sets up local configuration with all necessary files (validator key, node key, genesis and + /// config) + Localnet(LocalnetCmd), + /// View DB state. + #[clap(name = "view-state", alias = "view_state")] + StateViewer(StateViewerCommand), + /// Recompresses the entire storage. This is a slow operation which reads + /// all the data from the database and writes them down to a new copy of the + /// database. + /// + /// In 1.26 release the compression algorithm for the database has changed + /// to reduce storage size. Nodes don’t need to do anything for new data to + /// take advantage of better compression but existing data may take months + /// to be recompressed. This may be an issue for archival nodes which keep + /// hold of all the old data. + /// + /// This command makes it possible to force the recompression as a one-time + /// operation. Using it reduces the database even by up to 40% though that + /// is partially due to database ‘defragmentation’ (whose effects will wear + /// off in time). Still, reduction by about 20% even if that’s taken into + /// account can be expected. + /// + /// It’s important to remember however, that this command may take up to + /// a day to finish in which time the database cannot be used by the node. + /// + /// Furthermore, file system where output directory is located needs enough + /// free space to store the new copy of the database. It will be smaller + /// than the original but to be safe one should provision around the same + /// space as the size of the current `data` directory. + /// + /// Finally, because this command is meant only as a temporary migration + /// tool, it is planned to be removed by the end of 2022. + #[clap(alias = "recompress_storage")] + RecompressStorage(RecompressStorageSubCommand), + + /// Verify proofs + #[clap(alias = "verify_proof")] + VerifyProof(VerifyProofSubCommand), + + /// Connects to a NEAR node and sends ping messages to the accounts it sends + /// us after the handshake is completed, printing stats to stdout. + Ping(PingCommand), + + /// Mirror transactions from a source chain to a test chain with state forked + /// from it, reproducing traffic and state as closely as possible. + Mirror(MirrorCommand), + + /// Amend a genesis/records file created by `dump-state`. + AmendGenesis(AmendGenesisCommand), + + /// Testing tool for cold storage + ColdStore(ColdStoreCommand), + + /// Connects to a NEAR node and sends state parts requests after the handshake is completed. + StateParts(StatePartsCommand), + + /// Flat storage related tooling. + FlatStorage(FlatStorageCommand), + + /// validate config files including genesis.json and config.json + ValidateConfig(ValidateConfigCommand), + + /// reset the head of the chain locally to the prev block of current head + UndoBlock(UndoBlockCommand), + + /// Set of commands to run on database + Database(DatabaseCommand), + + /// Resets the network into a forked network at the given block height and state. + ForkNetwork(ForkNetworkCommand), + + /// Check completeness of dumped state parts of an epoch + StatePartsDumpCheck(StatePartsDumpCheckCommand), + + #[cfg(feature = "new_epoch_sync")] + /// Testing tool for epoch sync + EpochSync(EpochSyncCommand), +} + +#[derive(clap::Parser)] +pub(super) struct InitCmd { + /// Download the verified NEAR genesis file automatically. + #[clap(long)] + download_genesis: bool, + /// Download the verified NEAR config file automatically. + #[clap(long)] + download_config: bool, + /// Makes block production fast (TESTING ONLY). + #[clap(long)] + fast: bool, + /// Account ID for the validator key. + #[clap(long)] + account_id: Option, + /// Chain ID, by default creates new random. + #[clap(long, value_parser(clap::builder::NonEmptyStringValueParser::new()))] + chain_id: Option, + /// Specify a custom download URL for the genesis file. + #[clap(long)] + download_genesis_url: Option, + /// Specify a custom download URL for the records file. + #[clap(long)] + download_records_url: Option, + /// Specify a custom download URL for the config file. + #[clap(long)] + download_config_url: Option, + /// Genesis file to use when initializing testnet (including downloading). + #[clap(long)] + genesis: Option, + /// Initialize boots nodes in @ format seperated by commas + /// to bootstrap the network and store them in config.json + #[clap(long)] + boot_nodes: Option, + /// Number of shards to initialize the chain with. + #[clap(long, default_value = "1")] + num_shards: NumShards, + /// Specify private key generated from seed (TESTING ONLY). + #[clap(long)] + test_seed: Option, + /// Customize max_gas_burnt_view runtime limit. If not specified, value + /// from genesis configuration will be taken. + #[clap(long)] + max_gas_burnt_view: Option, +} + +/// Warns if unsupported build of the executable is used on mainnet or testnet. +/// +/// Verifies that when running on mainnet or testnet chain a uncd binary built +/// with `make release` command is used. That Makefile targets enable +/// optimisation options which aren’t enabled when building with different +/// methods and is the only officially supported method of building the binary +/// to run in production. +/// +/// The detection is done by checking that `unc_RELEASE_BUILD` environment +/// variable was set to `release` during compilation (which is what Makefile +/// sets) and that neither `nightly` nor `nightly_protocol` features are +/// enabled. +fn check_release_build(chain: &str) { + let is_release_build = option_env!("unc_RELEASE_BUILD") == Some("release") + && !cfg!(feature = "nightly") + && !cfg!(feature = "nightly_protocol"); + if !is_release_build + && [unc_primitives::chains::MAINNET, unc_primitives::chains::TESTNET].contains(&chain) + { + warn!( + target: "uncd", + "Running a uncd executable which wasn’t built with `make release` \ + command isn’t supported on {}.", + chain + ); + warn!( + target: "uncd", + "Note that `cargo build --release` builds lack optimisations which \ + may be needed to run properly on {}", + chain + ); + warn!( + target: "uncd", + "Consider recompiling the binary using `make release` command."); + } +} + +impl InitCmd { + pub(super) fn run(self, home_dir: &Path) -> anyhow::Result<()> { + // TODO: Check if `home` exists. If exists check what networks we already have there. + if (self.download_genesis || self.download_genesis_url.is_some()) && self.genesis.is_some() + { + anyhow::bail!("Please give either --genesis or --download-genesis, not both."); + } + + if let Some(chain) = self.chain_id.as_ref() { + check_release_build(chain) + } + + framework::init_configs( + home_dir, + self.chain_id, + self.account_id.and_then(|account_id| account_id.parse().ok()), + self.test_seed.as_deref(), + self.num_shards, + self.fast, + self.genesis.as_deref(), + self.download_genesis, + self.download_genesis_url.as_deref(), + self.download_records_url.as_deref(), + self.download_config, + self.download_config_url.as_deref(), + self.boot_nodes.as_deref(), + self.max_gas_burnt_view, + ) + .context("Failed to initialize configs") + } +} + +#[derive(clap::Parser)] +pub(super) struct RunCmd { + /// Configure node to run as archival node which prevents deletion of old + /// blocks. This is a persistent setting; once client is started as + /// archival node, it cannot be run in non-archival mode. + #[clap(long)] + archive: bool, + /// Set the boot nodes to bootstrap network from. + #[clap(long)] + boot_nodes: Option, + /// Whether to re-establish connections from the ConnectionStore on startup + #[clap(long)] + connect_to_reliable_peers_on_startup: Option, + /// Minimum number of peers to start syncing/producing blocks + #[clap(long)] + min_peers: Option, + /// Customize network listening address (useful for running multiple nodes on the same machine). + #[clap(long)] + network_addr: Option, + /// Set this to false to only produce blocks when there are txs or receipts (default true). + #[clap(long)] + produce_empty_blocks: Option, + /// Customize RPC listening address (useful for running multiple nodes on + /// the same machine). Ignored if ‘--disable-rpc’ is given. + #[cfg(feature = "json_rpc")] + #[clap(long)] + rpc_addr: Option, + /// Export prometheus metrics on an additional listening address, which is useful + /// for having separate access restrictions for the RPC and prometheus endpoints. + /// Ignored if RPC http server is disabled, see 'rpc_addr'. + #[cfg(feature = "json_rpc")] + #[clap(long)] + rpc_prometheus_addr: Option, + /// Disable the RPC endpoint. This is a no-op on builds which don’t support + /// RPC endpoint. + #[clap(long)] + #[allow(dead_code)] + disable_rpc: bool, + /// Customize telemetry url. + #[clap(long)] + telemetry_url: Option, + /// Customize max_gas_burnt_view runtime limit. If not specified, either + /// value given at ‘init’ (i.e. present in config.json) or one from genesis + /// configuration will be taken. + #[clap(long)] + max_gas_burnt_view: Option, +} + +impl RunCmd { + pub(super) fn run( + self, + home_dir: &Path, + genesis_validation: GenesisValidationMode, + verbose_target: Option<&str>, + o11y_opts: &unc_o11y::Options, + ) { + // Load configs from home. + let mut unc_config = framework::config::load_config(home_dir, genesis_validation) + .unwrap_or_else(|e| panic!("Error loading config: {:#}", e)); + + check_release_build(&unc_config.client_config.chain_id); + + // Set current version in client config. + unc_config.client_config.version = crate::neard_version(); + // Override some parameters from command line. + if let Some(produce_empty_blocks) = self.produce_empty_blocks { + unc_config.client_config.produce_empty_blocks = produce_empty_blocks; + } + if let Some(connect_to_reliable_peers_on_startup) = + self.connect_to_reliable_peers_on_startup + { + unc_config.network_config.connect_to_reliable_peers_on_startup = + connect_to_reliable_peers_on_startup; + } + if let Some(boot_nodes) = self.boot_nodes { + if !boot_nodes.is_empty() { + unc_config.network_config.peer_store.boot_nodes = boot_nodes + .split(',') + .map(|chunk| chunk.parse().expect("Failed to parse PeerInfo")) + .collect(); + } + } + if let Some(min_peers) = self.min_peers { + unc_config.client_config.min_num_peers = min_peers; + } + if let Some(network_addr) = self.network_addr { + unc_config.network_config.node_addr = + Some(unc_network::tcp::ListenerAddr::new(network_addr)); + } + #[cfg(feature = "json_rpc")] + if self.disable_rpc { + unc_config.rpc_config = None; + } else { + if let Some(rpc_addr) = self.rpc_addr { + unc_config.rpc_config.get_or_insert(Default::default()).addr = + tcp::ListenerAddr::new(rpc_addr.parse().unwrap()); + } + if let Some(rpc_prometheus_addr) = self.rpc_prometheus_addr { + unc_config.rpc_config.get_or_insert(Default::default()).prometheus_addr = + Some(rpc_prometheus_addr); + } + } + if let Some(telemetry_url) = self.telemetry_url { + if !telemetry_url.is_empty() { + unc_config.telemetry_config.endpoints.push(telemetry_url); + } + } + if self.archive { + unc_config.client_config.archive = true; + } + if self.max_gas_burnt_view.is_some() { + unc_config.client_config.max_gas_burnt_view = self.max_gas_burnt_view; + } + + #[cfg(feature = "sandbox")] + { + if unc_config.client_config.chain_id == unc_primitives::chains::MAINNET + || unc_config.client_config.chain_id == unc_primitives::chains::TESTNET + { + eprintln!( + "Sandbox node can only run dedicate localnet, cannot connect to a network" + ); + std::process::exit(1); + } + } + + let (tx_crash, mut rx_crash) = broadcast::channel::<()>(16); + let (tx_config_update, rx_config_update) = + broadcast::channel::>>(16); + let sys = actix::System::new(); + + sys.block_on(async move { + // Initialize the subscriber that takes care of both logging and tracing. + let _subscriber_guard = default_subscriber_with_opentelemetry( + make_env_filter(verbose_target).unwrap(), + o11y_opts, + unc_config.client_config.chain_id.clone(), + unc_config.network_config.node_key.public_key().clone(), + unc_config + .network_config + .validator + .as_ref() + .map(|validator| validator.account_id()), + ) + .await + .global(); + + let updateable_configs = framework::dyn_config::read_updateable_configs(home_dir) + .unwrap_or_else(|e| panic!("Error reading dynamic configs: {:#}", e)); + let mut updateable_config_loader = + UpdateableConfigLoader::new(updateable_configs.clone(), tx_config_update); + let config_updater = ConfigUpdater::new(rx_config_update); + + let framework::NearNode { + rpc_servers, + cold_store_loop_handle, + state_sync_dump_handle, + flat_state_migration_handle, + resharding_handle, + .. + } = framework::start_with_config_and_synchronization( + home_dir, + unc_config, + Some(tx_crash), + Some(config_updater), + ) + .expect("start_with_config"); + + let sig = loop { + let sig = wait_for_interrupt_signal(home_dir, &mut rx_crash).await; + if sig == "SIGHUP" { + let maybe_updateable_configs = + framework::dyn_config::read_updateable_configs(home_dir); + updateable_config_loader.reload(maybe_updateable_configs); + } else { + break sig; + } + }; + warn!(target: "uncd", "{}, stopping... this may take a few minutes.", sig); + if let Some(handle) = cold_store_loop_handle { + handle.stop() + } + if let Some(handle) = state_sync_dump_handle { + handle.stop() + } + resharding_handle.stop(); + flat_state_migration_handle.stop(); + futures::future::join_all(rpc_servers.iter().map(|(name, server)| async move { + server.stop(true).await; + debug!(target: "uncd", "{} server stopped", name); + })) + .await; + actix::System::current().stop(); + // Disable the subscriber to properly shutdown the tracer. + unc_o11y::reload(Some("error"), None, Some(unc_o11y::OpenTelemetryLevel::OFF)) + .unwrap(); + }); + sys.run().unwrap(); + info!(target: "uncd", "Waiting for RocksDB to gracefully shutdown"); + RocksDB::block_until_all_instances_are_dropped(); + } +} + +#[cfg(not(unix))] +async fn wait_for_interrupt_signal(_home_dir: &Path, mut _rx_crash: &Receiver<()>) -> &str { + // TODO(#6372): Support graceful shutdown on windows. + tokio::signal::ctrl_c().await.unwrap(); + "Ctrl+C" +} + +#[cfg(unix)] +async fn wait_for_interrupt_signal(_home_dir: &Path, rx_crash: &mut Receiver<()>) -> &'static str { + use tokio::signal::unix::{signal, SignalKind}; + let mut sigint = signal(SignalKind::interrupt()).unwrap(); + let mut sigterm = signal(SignalKind::terminate()).unwrap(); + let mut sighup = signal(SignalKind::hangup()).unwrap(); + + tokio::select! { + _ = sigint.recv() => "SIGINT", + _ = sigterm.recv() => "SIGTERM", + _ = sighup.recv() => "SIGHUP", + _ = rx_crash.recv() => "ClientActor died", + } +} + +#[derive(clap::Parser)] +pub(super) struct LocalnetCmd { + /// Number of non-validators to initialize the localnet with. + #[clap(short = 'n', long, alias = "n", default_value = "0")] + non_validators: NumSeats, + /// Prefix for the directory name for each node with (e.g. ‘node’ results in + /// ‘node0’, ‘node1’, ...) + #[clap(long, default_value = "node")] + prefix: String, + /// Number of shards to initialize the localnet with. + #[clap(short = 's', long, default_value = "1")] + shards: NumShards, + /// Number of validators to initialize the localnet with. + #[clap(short = 'v', long, alias = "v", default_value = "4")] + validators: NumSeats, + /// Whether to configure nodes as archival. + #[clap(long)] + archival_nodes: bool, + /// Comma separated list of shards to track, the word 'all' to track all shards or the word 'none' to track no shards. + #[clap(long, default_value = "all")] + tracked_shards: String, +} + +impl LocalnetCmd { + fn parse_tracked_shards(tracked_shards: &str, num_shards: NumShards) -> Vec { + if tracked_shards.to_lowercase() == "all" { + return (0..num_shards).collect(); + } + if tracked_shards.to_lowercase() == "none" { + return vec![]; + } + tracked_shards + .split(',') + .map(|shard_id| shard_id.parse::().expect("Shard id must be an integer")) + .collect() + } + + pub(super) fn run(self, home_dir: &Path) { + let tracked_shards = Self::parse_tracked_shards(&self.tracked_shards, self.shards); + + framework::config::init_testnet_configs( + home_dir, + self.shards, + self.validators, + self.non_validators, + &self.prefix, + true, + self.archival_nodes, + tracked_shards, + ); + } +} + +#[derive(clap::Args)] +#[clap(arg_required_else_help = true)] +pub(super) struct RecompressStorageSubCommand { + /// Directory where to save new storage. + #[clap(long)] + output_dir: PathBuf, + + /// Keep data in DBCol::PartialChunks column. Data in that column can be + /// reconstructed from DBCol::Chunks is not needed by archival nodes. This is + /// always true if node is not an archival node. + #[clap(long)] + keep_partial_chunks: bool, + + /// Keep data in DBCol::InvalidChunks column. Data in that column is only used + /// when receiving chunks and is not needed to serve archival requests. + /// This is always true if node is not an archival node. + #[clap(long)] + keep_invalid_chunks: bool, + + /// Keep data in DBCol::TrieChanges column. Data in that column is never used + /// by archival nodes. This is always true if node is not an archival node. + #[clap(long)] + keep_trie_changes: bool, +} + +impl RecompressStorageSubCommand { + pub(super) fn run(self, home_dir: &Path) { + warn!(target: "uncd", "Recompressing storage; note that this operation may take up to a day to finish."); + let opts = framework::RecompressOpts { + dest_dir: self.output_dir, + keep_partial_chunks: self.keep_partial_chunks, + keep_invalid_chunks: self.keep_invalid_chunks, + keep_trie_changes: self.keep_trie_changes, + }; + if let Err(err) = framework::recompress_storage(home_dir, opts) { + error!("{}", err); + std::process::exit(1); + } + } +} + +#[derive(thiserror::Error, Debug, PartialEq)] +pub enum VerifyProofError { + #[error("invalid outcome root proof")] + InvalidOutcomeRootProof, + #[error("invalid block hash proof")] + InvalidBlockHashProof, +} + +#[derive(clap::Parser)] +pub struct VerifyProofSubCommand { + #[clap(long)] + json_file_path: String, +} + +impl VerifyProofSubCommand { + /// Verifies light client transaction proof (result of the EXPERIMENTAL_light_client_proof RPC call). + /// Returns the Hash and height of the block that transaction belongs to, and root of the light block merkle tree. + pub fn run(self) -> ((CryptoHash, u64), CryptoHash) { + let file = File::open(Path::new(self.json_file_path.as_str())) + .with_context(|| "Could not open proof file.") + .unwrap(); + let reader = BufReader::new(file); + let light_client_rpc_response: Value = + serde_json::from_reader(reader).with_context(|| "Failed to deserialize JSON.").unwrap(); + Self::verify_json(light_client_rpc_response).unwrap() + } + + pub fn verify_json( + light_client_rpc_response: Value, + ) -> Result<((CryptoHash, u64), CryptoHash), VerifyProofError> { + let light_client_proof: RpcLightClientExecutionProofResponse = + serde_json::from_value(light_client_rpc_response["result"].clone()).unwrap(); + + println!( + "Verifying light client proof for txn id: {:?}", + light_client_proof.outcome_proof.id + ); + let outcome_hashes = light_client_proof.outcome_proof.to_hashes(); + println!("Hashes of the outcome are: {:?}", outcome_hashes); + + let outcome_hash = CryptoHash::hash_borsh(&outcome_hashes); + println!("Hash of the outcome is: {:?}", outcome_hash); + + let outcome_shard_root = + compute_root_from_path(&light_client_proof.outcome_proof.proof, outcome_hash); + println!("Shard outcome root is: {:?}", outcome_shard_root); + let block_outcome_root = compute_root_from_path( + &light_client_proof.outcome_root_proof, + CryptoHash::hash_borsh(outcome_shard_root), + ); + println!("Block outcome root is: {:?}", block_outcome_root); + + if light_client_proof.block_header_lite.inner_lite.outcome_root != block_outcome_root { + println!( + "{}", + yansi::Paint::default(format!( + "ERROR: computed outcome root: {:?} doesn't match the block one {:?}.", + block_outcome_root, + light_client_proof.block_header_lite.inner_lite.outcome_root + )) + .fg(yansi::Color::Red) + .bold() + ); + return Err(VerifyProofError::InvalidOutcomeRootProof); + } + let block_hash = light_client_proof.outcome_proof.block_hash; + + if light_client_proof.block_header_lite.hash() + != light_client_proof.outcome_proof.block_hash + { + println!( + "{}", + yansi::Paint::default(format!( + "ERROR: block hash from header lite {:?} doesn't match the one from outcome proof {:?}", + light_client_proof.block_header_lite.hash(), + light_client_proof.outcome_proof.block_hash + )).fg(yansi::Color::Red).bold() + ); + return Err(VerifyProofError::InvalidBlockHashProof); + } else { + println!( + "{}", + yansi::Paint::green(format!("Block hash matches {:?}", block_hash)).bold() + ); + } + + // And now check that block exists in the light client. + + let light_block_merkle_root = + compute_root_from_path(&light_client_proof.block_proof, block_hash); + + println!( + "Please verify that your light block has the following block merkle root: {:?}", + light_block_merkle_root + ); + println!( + "OR verify that block with this hash {:?} is in the chain at this height {:?}", + block_hash, light_client_proof.block_header_lite.inner_lite.height + ); + Ok(( + (block_hash, light_client_proof.block_header_lite.inner_lite.height), + light_block_merkle_root, + )) + } +} + +fn make_env_filter(verbose: Option<&str>) -> Result { + let env_filter = EnvFilterBuilder::from_env().verbose(verbose).finish()?; + // Sandbox node can log to sandbox logging target via sandbox_debug_log host function. + // This is hidden by default so we enable it for sandbox node. + let env_filter = if cfg!(feature = "sandbox") { + env_filter.add_directive("sandbox=debug".parse().unwrap()) + } else { + env_filter + }; + Ok(env_filter) +} + +#[derive(clap::Parser)] +pub(super) struct ValidateConfigCommand {} + +impl ValidateConfigCommand { + pub(super) fn run(&self, home_dir: &Path) -> anyhow::Result<()> { + framework::config::load_config(home_dir, GenesisValidationMode::Full)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::{CryptoHash, NeardCmd, NeardSubCommand, VerifyProofError, VerifyProofSubCommand}; + use clap::Parser; + use std::str::FromStr; + + #[test] + fn optional_values() { + let cmd = NeardCmd::parse_from(&["test", "init", "--chain-id=testid", "--fast"]); + if let NeardSubCommand::Init(scmd) = cmd.subcmd { + assert_eq!(scmd.chain_id, Some("testid".to_string())); + assert!(scmd.fast); + } else { + panic!("incorrect subcommand"); + } + } + + #[test] + fn equal_no_value_syntax() { + assert!(NeardCmd::try_parse_from(&[ + "test", + "init", + // * This line currently fails to be parsed (= without a value) + "--chain-id=", + "--test-seed=alice.near", + "--account-id=test.near", + "--fast" + ]) + .is_err()); + } + + #[test] + fn verify_proof_test() { + assert_eq!( + VerifyProofSubCommand::verify_json( + serde_json::from_slice(include_bytes!("../res/proof_example.json")).unwrap() + ) + .unwrap(), + ( + ( + CryptoHash::from_str("HqZHDTHSqH6Az22SZgFUjodGFDtfC2qSt4v9uYFpLuFC").unwrap(), + 38 as u64 + ), + CryptoHash::from_str("BWwZdhAhjAgKxZ5ycqn1CvXads5DjPMfj4kRdc1rWit8").unwrap() + ) + ); + + // Proof with a wrong outcome (as user specified wrong shard). + assert_eq!( + VerifyProofSubCommand::verify_json( + serde_json::from_slice(include_bytes!("../res/invalid_proof.json")).unwrap() + ) + .unwrap_err(), + VerifyProofError::InvalidOutcomeRootProof + ); + } +} diff --git a/uncd/src/main.rs b/uncd/src/main.rs new file mode 100644 index 000000000..497bab22a --- /dev/null +++ b/uncd/src/main.rs @@ -0,0 +1,68 @@ +mod cli; + +use self::cli::NeardCmd; +use anyhow::Context; +use unc_primitives::version::{Version, PROTOCOL_VERSION}; +use unc_store::metadata::DB_VERSION; +use framework::get_default_home; +use once_cell::sync::Lazy; +use std::env; +use std::path::PathBuf; +use std::time::Duration; + +static NEARD_VERSION: &str = env!("NEARD_VERSION"); +static NEARD_BUILD: &str = env!("NEARD_BUILD"); +static RUSTC_VERSION: &str = env!("NEARD_RUSTC_VERSION"); + +static NEARD_VERSION_STRING: Lazy = Lazy::new(|| { + format!( + "(release {}) (build {}) (rustc {}) (protocol {}) (db {})", + NEARD_VERSION, NEARD_BUILD, RUSTC_VERSION, PROTOCOL_VERSION, DB_VERSION + ) +}); + +fn neard_version() -> Version { + Version { + version: NEARD_VERSION.to_string(), + build: NEARD_BUILD.to_string(), + rustc_version: RUSTC_VERSION.to_string(), + } +} + +static DEFAULT_HOME: Lazy = Lazy::new(get_default_home); + +#[global_allocator] +static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; + +fn main() -> anyhow::Result<()> { + if env::var("RUST_BACKTRACE").is_err() { + // Enable backtraces on panics by default. + env::set_var("RUST_BACKTRACE", "1"); + } + + rayon::ThreadPoolBuilder::new() + .stack_size(8 * 1024 * 1024) + .build_global() + .context("failed to create the threadpool")?; + + // We use it to automatically search the for root certificates to perform HTTPS calls + // (sending telemetry and downloading genesis) + openssl_probe::init_ssl_cert_env_vars(); + unc_performance_metrics::process::schedule_printing_performance_stats(Duration::from_secs(60)); + + // The default FD soft limit in linux is 1024. + // We use more than that, for example we support up to 1000 TCP + // connections, using 5 FDs per each connection. + // We consider 65535 to be a reasonable limit for this binary, + // and we enforce it here. We also set the hard limit to the same value + // to prevent the inner logic from trying to bump it further: + // FD limit is a global variable, so it shouldn't be modified in an + // uncoordinated way. + const FD_LIMIT: u64 = 65535; + let (_, hard) = rlimit::Resource::NOFILE.get().context("rlimit::Resource::NOFILE::get()")?; + rlimit::Resource::NOFILE.set(FD_LIMIT, FD_LIMIT).context(format!( + "couldn't set the file descriptor limit to {FD_LIMIT}, hard limit = {hard}" + ))?; + + NeardCmd::parse_and_run() +} diff --git a/utils/config/Cargo.toml b/utils/config/Cargo.toml new file mode 100644 index 000000000..6bfd115d7 --- /dev/null +++ b/utils/config/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "unc-config-utils" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "This is an internal crate to provide utils for reading config files" +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +anyhow.workspace = true +json_comments.workspace = true +thiserror.workspace = true +tracing.workspace = true + diff --git a/utils/config/src/lib.rs b/utils/config/src/lib.rs new file mode 100644 index 000000000..a8f15c61c --- /dev/null +++ b/utils/config/src/lib.rs @@ -0,0 +1,136 @@ +use std::io::Read; + +use json_comments::StripComments; + +// strip comments from a JSON string with comments. +// the comment formats that are supported: //, /* */ and #. +// json-comments-rs is used +// check out more details: https://github.com/tmccombs/json-comments-rs/blob/main/src/lib.rs +pub fn strip_comments_from_json_str(json_str: &String) -> std::io::Result { + let mut content_without_comments = String::new(); + StripComments::new(json_str.as_bytes()).read_to_string(&mut content_without_comments)?; + Ok(content_without_comments) +} + +// strip comments from a JSON input with comments. +pub fn strip_comments_from_json_reader(reader: impl Read) -> impl Read { + StripComments::new(reader) +} + +/// errors that arise when loading config files or config semantic checks +/// config files here include: genesis.json, config.json, node_key.json, validator_key.json +#[derive(thiserror::Error, Debug)] +pub enum ValidationError { + #[error("config.json semantic issue: {error_message}")] + ConfigSemanticsError { error_message: String }, + #[error("genesis.json semantic issue: {error_message}")] + GenesisSemanticsError { error_message: String }, + #[error("config.json file issue: {error_message}")] + ConfigFileError { error_message: String }, + #[error("genesis.json file issue: {error_message}")] + GenesisFileError { error_message: String }, + #[error("node_key.json file issue: {error_message}")] + NodeKeyFileError { error_message: String }, + #[error("validator_key.json file issue: {error_message}")] + ValidatorKeyFileError { error_message: String }, + #[error("cross config files semantic issue: {error_message}")] + CrossFileSematicError { error_message: String }, +} + +/// Used to collect errors on the go. +pub struct ValidationErrors(Vec); + +impl ValidationErrors { + pub fn new() -> Self { + ValidationErrors(Vec::new()) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn push_errors(&mut self, error: ValidationError) { + self.0.push(error) + } + + pub fn push_config_semantics_error(&mut self, error_message: String) { + self.0.push(ValidationError::ConfigSemanticsError { error_message: error_message }) + } + + pub fn push_config_file_error(&mut self, error_message: String) { + self.0.push(ValidationError::ConfigFileError { error_message: error_message }) + } + + pub fn push_genesis_semantics_error(&mut self, error_message: String) { + self.0.push(ValidationError::GenesisSemanticsError { error_message: error_message }) + } + + pub fn push_genesis_file_error(&mut self, error_message: String) { + self.0.push(ValidationError::GenesisFileError { error_message: error_message }) + } + + pub fn push_node_key_file_error(&mut self, error_message: String) { + self.0.push(ValidationError::NodeKeyFileError { error_message: error_message }) + } + + pub fn push_validator_key_file_error(&mut self, error_message: String) { + self.0.push(ValidationError::ValidatorKeyFileError { error_message: error_message }) + } + + pub fn push_cross_file_semantics_error(&mut self, error_message: String) { + self.0.push(ValidationError::CrossFileSematicError { error_message: error_message }) + } + + /// only to be used in panic_if_errors() + fn generate_final_error_message(&self) -> Option { + if self.0.is_empty() { + None + } else { + let mut final_error_message = String::new(); + for error in &self.0 { + final_error_message += "\n"; + + match error { + ValidationError::ConfigSemanticsError { error_message } + | ValidationError::GenesisSemanticsError { error_message } => { + // the final error_message is concatenation of GenesisSemanticsError or ConfigSemanticsError's ever seen + // not including the whole error.to_string() makes the final error message less confusing to read + final_error_message += error_message + } + _ => final_error_message += &error.to_string(), + }; + } + Some(final_error_message) + } + } + + /// concatenate all errors of a certain type in one error message + /// to be used for error types that tend to appear in multiples, e.g. ConfigSemanticsError and GenesisSemanticsError + pub fn generate_error_message_per_type(&self) -> Option { + if self.0.is_empty() { + None + } else { + let mut final_error_message = String::new(); + for error in &self.0 { + final_error_message += "\n"; + final_error_message += &error.to_string(); + } + final_error_message += "\n"; + Some(final_error_message) + } + } + + /// only call this function when you want the program to return () or all errors so far + /// should only be used when you finished inserting all errors + pub fn return_ok_or_error(&self) -> anyhow::Result<()> { + if self.0.is_empty() { + tracing::info!(target: "config", "All validations have passed!"); + Ok(()) + } else { + Err(anyhow::Error::msg(format!( + "\nThe following config checks failed:{}\nPlease fix the config json files and validate again!", + self.generate_final_error_message().unwrap() + ))) + } + } +} diff --git a/utils/fmt/Cargo.toml b/utils/fmt/Cargo.toml new file mode 100644 index 000000000..c9d98a29f --- /dev/null +++ b/utils/fmt/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "unc-fmt" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "Helpers for pretty formatting." +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +unc-primitives-core.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-primitives-core/nightly", +] +nightly_protocol = [ + "unc-primitives-core/nightly_protocol", +] diff --git a/utils/fmt/README.md b/utils/fmt/README.md new file mode 100644 index 000000000..d9e4eac7d --- /dev/null +++ b/utils/fmt/README.md @@ -0,0 +1,8 @@ +# Custom formatting for the framework code base. + +This crate is a lightweight assembly of wrapper types that we use to pretty +print values. + +Keep dependencies to an absolute minimum, since this crate is a dependency of +`unc-primitives` which in turn is used by many near related crates inside and +outside the framework repository. diff --git a/utils/fmt/src/lib.rs b/utils/fmt/src/lib.rs new file mode 100644 index 000000000..e7f2f8212 --- /dev/null +++ b/utils/fmt/src/lib.rs @@ -0,0 +1,325 @@ +use std::ops::RangeInclusive; + +use unc_primitives_core::hash::CryptoHash; +use unc_primitives_core::serialize::{base64_display, from_base64}; +use std::str::FromStr; + +/// A wrapper for bytes slice which tries to guess best way to format it. +/// +/// If the slice contains printable ASCII characters only, it’s represented as +/// a string surrounded by single quotes (as a consequence, empty value is +/// converted to pair of single quotes). Otherwise, it converts the value into +/// base64. +/// +/// The intended usage for this type is when trying to format binary data whose +/// structure isn’t known to the caller. For example, when generating debugging +/// or tracing data at database layer where everything is just slices of bytes. +/// At higher levels of abstractions, if the structure of the data is known, +/// it’s usually better to format data in a way that makes sense for the given +/// type. +/// +/// The type can be used as with `tracing::info!` and similar calls. For +/// example: +/// +/// ```ignore +/// tracing::trace!(target: "state", +/// db_op = "insert", +/// key = %unc_fmt::Bytes(key), +/// size = value.len()) +/// ``` +/// +/// See also [`StorageKey`] which tries to guess if the data is not a crypto +/// hash. +pub struct Bytes<'a>(pub &'a [u8]); + +impl<'a> std::fmt::Display for Bytes<'a> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + bytes_format(self.0, fmt, false) + } +} + +impl<'a> std::fmt::Debug for Bytes<'a> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + bytes_format(self.0, fmt, false) + } +} + +impl<'a> Bytes<'a> { + /// Reverses `bytes_format` to allow decoding `Bytes` written with `Display`. + /// + /// This looks similar to `FromStr` but due to lifetime constraints on + /// input and output, the trait cannot be implemented. + /// + /// Error: Returns an error when the input does not look like an output from + /// `bytes_format`. + pub fn from_str(s: &str) -> Result, Box> { + if s.len() >= 2 && s.starts_with('`') && s.ends_with('`') { + // hash encoded as base58 + let hash = CryptoHash::from_str(&s[1..s.len().checked_sub(1).expect("s.len() >= 2 ")])?; + Ok(hash.as_bytes().to_vec()) + } else if s.len() >= 2 && s.starts_with('\'') && s.ends_with('\'') { + // plain string + Ok(s[1..s.len().checked_sub(1).expect("s.len() >= 2 ")].as_bytes().to_vec()) + } else { + // encoded with base64 + from_base64(s).map_err(|err| err.into()) + } + } +} + +/// A wrapper for bytes slice which tries to guess best way to format it +/// truncating the value if it’s too long. +/// +/// Behaves like [`Bytes`] but truncates the formatted string to around 128 +/// characters. If the value is longer then that, the length of the value in +/// bytes is included at the beginning and ellipsis is included at the end of +/// the value. +pub struct AbbrBytes(pub T); + +impl<'a> std::fmt::Debug for AbbrBytes<&'a [u8]> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + truncated_bytes_format(self.0, fmt) + } +} + +impl<'a> std::fmt::Debug for AbbrBytes<&'a Vec> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + AbbrBytes(self.0.as_slice()).fmt(fmt) + } +} + +impl<'a> std::fmt::Debug for AbbrBytes> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.0 { + None => fmt.write_str("None"), + Some(bytes) => truncated_bytes_format(bytes, fmt), + } + } +} + +impl<'a> std::fmt::Display for AbbrBytes<&'a [u8]> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + truncated_bytes_format(self.0, fmt) + } +} + +impl<'a> std::fmt::Display for AbbrBytes<&'a Vec> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + AbbrBytes(self.0.as_slice()).fmt(fmt) + } +} + +impl<'a> std::fmt::Display for AbbrBytes> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.0 { + None => fmt.write_str("None"), + Some(bytes) => truncated_bytes_format(bytes, fmt), + } + } +} + +/// A wrapper for bytes slice which tries to guess best way to format it. +/// +/// If the slice is exactly 32-byte long, it’s assumed to be a hash and is +/// converted into base58 and printed surrounded by backtics. Otherwise, +/// behaves like [`Bytes`] representing the data as string if it contains ASCII +/// printable bytes only or base64 otherwise. +/// +/// The motivation for such choices is that we only ever use base58 to format +/// hashes which are 32-byte long. It’s therefore not useful to use it for any +/// other types of keys. +/// +/// The intended usage for this type is when trying to format binary data whose +/// structure isn’t known to the caller. For example, when generating debugging +/// or tracing data at database layer where everything is just slices of bytes. +/// At higher levels of abstractions, if the structure of the data is known, +/// it’s usually better to format data in a way that makes sense for the given +/// type. +/// +/// The type can be used as with `tracing::info!` and similar calls. For +/// example: +/// +/// ```ignore +/// tracing::info!(target: "store", +/// op = "set", +/// col = %col, +/// key = %unc_fmt::StorageKey(key), +/// size = value.len()) +/// ``` +pub struct StorageKey<'a>(pub &'a [u8]); + +impl<'a> std::fmt::Display for StorageKey<'a> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + bytes_format(self.0, fmt, true) + } +} + +impl<'a> std::fmt::Debug for StorageKey<'a> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + bytes_format(self.0, fmt, true) + } +} + +/// A wrapper for slices which formats the slice limiting the length. +/// +/// If the slice has no more than five elements, it’s printed in full. +/// Otherwise, only the first two and last two elements are printed to limit the +/// length of the formatted value. +pub struct Slice<'a, T>(pub &'a [T]); + +impl<'a, T: std::fmt::Debug> std::fmt::Debug for Slice<'a, T> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let slice = self.0; + + struct Ellipsis; + + impl std::fmt::Debug for Ellipsis { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmt.write_str("…") + } + } + + if let [a, b, _c, .., _x, y, z] = slice { + write!(fmt, "({})", slice.len())?; + fmt.debug_list().entry(a).entry(b).entry(&Ellipsis).entry(y).entry(z).finish() + } else { + std::fmt::Debug::fmt(&slice, fmt) + } + } +} + +/// Implementation of [`Bytes`] and [`StorageKey`] formatting. +/// +/// If the `consider_hash` argument is false, formats bytes as described in +/// [`Bytes`]. If it’s true, formats the bytes as described in [`StorageKey`]. +fn bytes_format( + bytes: &[u8], + fmt: &mut std::fmt::Formatter<'_>, + consider_hash: bool, +) -> std::fmt::Result { + if consider_hash && bytes.len() == 32 { + write!(fmt, "`{}`", CryptoHash(bytes.try_into().unwrap())) + } else if bytes.iter().all(|ch| 0x20 <= *ch && *ch <= 0x7E) { + // SAFETY: We’ve just checked that the value contains ASCII + // characters only. + let value = unsafe { std::str::from_utf8_unchecked(bytes) }; + write!(fmt, "'{value}'") + } else { + std::fmt::Display::fmt(&base64_display(bytes), fmt) + } +} + +/// Implementation of [`AbbrBytes`]. +fn truncated_bytes_format(bytes: &[u8], fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + const PRINTABLE_ASCII: RangeInclusive = 0x20..=0x7E; + const OVERALL_LIMIT: usize = 128; + const DISPLAY_ASCII_FULL_LIMIT: usize = OVERALL_LIMIT - 2; + const DISPLAY_ASCII_PREFIX_LIMIT: usize = OVERALL_LIMIT - 9; + const DISPLAY_BASE64_FULL_LIMIT: usize = OVERALL_LIMIT / 4 * 3; + const DISPLAY_BASE64_PREFIX_LIMIT: usize = (OVERALL_LIMIT - 8) / 4 * 3; + let len = bytes.len(); + if bytes.iter().take(DISPLAY_ASCII_FULL_LIMIT).all(|ch| PRINTABLE_ASCII.contains(ch)) { + if len <= DISPLAY_ASCII_FULL_LIMIT { + // SAFETY: We’ve just checked that the value contains ASCII + // characters only. + let value = unsafe { std::str::from_utf8_unchecked(bytes) }; + write!(fmt, "'{value}'") + } else { + let bytes = &bytes[..DISPLAY_ASCII_PREFIX_LIMIT]; + let value = unsafe { std::str::from_utf8_unchecked(bytes) }; + write!(fmt, "({len})'{value}'…") + } + } else if bytes.len() <= DISPLAY_BASE64_FULL_LIMIT { + std::fmt::Display::fmt(&base64_display(bytes), fmt) + } else { + let bytes = &bytes[..DISPLAY_BASE64_PREFIX_LIMIT]; + let value = base64_display(bytes); + write!(fmt, "({len}){value}…") + } +} + +#[cfg(test)] +macro_rules! do_test_bytes_formatting { + ($type:ident, $consider_hash:expr, $truncate:expr) => {{ + #[track_caller] + fn test(want: &str, slice: &[u8]) { + assert_eq!(want, $type(slice).to_string(), "unexpected formatting"); + if !$truncate { + assert_eq!(&Bytes::from_str(want).expect("decode fail"), slice, "wrong decoding"); + } + } + + #[track_caller] + fn test2(cond: bool, want_true: &str, want_false: &str, slice: &[u8]) { + test(if cond { want_true } else { want_false }, slice); + } + + test("''", b""); + test("'foo'", b"foo"); + test("'foo bar'", b"foo bar"); + test("WsOzxYJ3", "Zółw".as_bytes()); + test("EGZvbyBiYXI=", b"\x10foo bar"); + test("f2ZvbyBiYXI=", b"\x7Ffoo bar"); + + test2( + $consider_hash, + "`11111111111111111111111111111111`", + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + &[0; 32], + ); + let hash = CryptoHash::hash_bytes(b"foo"); + test2( + $consider_hash, + "`3yMApqCuCjXDWPrbjfR5mjCPTHqFG8Pux1TxQrEM35jj`", + "LCa0a2j/xo/5m0U8HTBBNBNCLXBkg7+g+YpeiGJm564=", + hash.as_bytes(), + ); + + let long_str = "rabarbar".repeat(16); + test2( + $truncate, + &format!("(128)'{}'…", &long_str[..119]), + &format!("'{long_str}'"), + long_str.as_bytes(), + ); + test2( + $truncate, + &format!("(102){}…", &"deadbeef".repeat(15)), + &"deadbeef".repeat(17), + &b"u\xe6\x9dm\xe7\x9f".repeat(17), + ); + }}; +} + +#[test] +fn test_bytes() { + do_test_bytes_formatting!(Bytes, false, false); +} + +#[test] +fn test_truncated_bytes() { + do_test_bytes_formatting!(AbbrBytes, false, true); +} + +#[test] +fn test_storage_key() { + do_test_bytes_formatting!(StorageKey, true, false); +} + +#[test] +fn test_slice() { + macro_rules! test { + ($want:literal, $fmt:literal, $len:expr) => { + assert_eq!( + $want, + format!($fmt, Slice(&[0u8, 11, 22, 33, 44, 55, 66, 77, 88, 99][..$len])) + ) + }; + } + + test!("[]", "{:?}", 0); + test!("[0, 11, 22, 33]", "{:?}", 4); + test!("[0, b, 16, 21]", "{:x?}", 4); + test!("(10)[0, 11, …, 88, 99]", "{:?}", 10); + test!("(10)[0, b, …, 58, 63]", "{:x?}", 10); +} diff --git a/utils/mainnet-res/Cargo.toml b/utils/mainnet-res/Cargo.toml new file mode 100644 index 000000000..54e530370 --- /dev/null +++ b/utils/mainnet-res/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "unc-mainnet-res" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +serde_json.workspace = true + +unc-account-id.workspace = true +unc-chain-configs.workspace = true +unc-primitives.workspace = true + +[features] +nightly = [ + "nightly_protocol", + "unc-chain-configs/nightly", + "unc-primitives/nightly", +] +nightly_protocol = [ + "unc-chain-configs/nightly_protocol", + "unc-primitives/nightly_protocol", +] diff --git a/utils/mainnet-res/README.md b/utils/mainnet-res/README.md new file mode 100644 index 000000000..b2c7aed24 --- /dev/null +++ b/utils/mainnet-res/README.md @@ -0,0 +1,15 @@ +## Core Resource Files + +Stores resource data which is part of the protocol stable enough to be moved outside of the code. + +### `mainnet_genesis.json` + +Stores genesis of mainnet. + +### `mainnet_restored_receipts.json` + +Stores receipts restored after the fix of applying chunks. See [#4248](https://github.com/utnet-org/utility/pull/4248) for more details. + +### `storage_usage_delta.json` + +Stores difference of storage usage applied to mainnet after observed bug related to delete key action. See [#3824](https://github.com/utnet-org/utility/issues/3824) for more details. diff --git a/utils/mainnet-res/res/mainnet_genesis.json b/utils/mainnet-res/res/mainnet_genesis.json new file mode 100644 index 000000000..ded3caf58 --- /dev/null +++ b/utils/mainnet-res/res/mainnet_genesis.json @@ -0,0 +1,941 @@ +{ + "protocol_version": 29, + "genesis_time": "2020-07-21T16:55:51.591948000Z", + "chain_id": "mainnet", + "genesis_height": 9820210, + "num_block_producer_seats": 100, + "num_block_producer_seats_per_shard": [ + 100 + ], + "avg_hidden_validator_seats_per_shard": [ + 0 + ], + "dynamic_resharding": false, + "epoch_length": 43200, + "gas_limit": 1000000000000000, + "min_gas_price": "1000000000", + "block_producer_kickout_threshold": 90, + "chunk_producer_kickout_threshold": 90, + "online_min_threshold": [ + 90, + 100 + ], + "online_max_threshold": [ + 99, + 100 + ], + "gas_price_adjustment_rate": [ + 1, + 100 + ], + "runtime_config": { + "storage_amount_per_byte": "100000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 4697339419375, + "send_not_sir": 4697339419375, + "execution": 4697339419375 + }, + "cost_per_byte": { + "send_sir": 59357464, + "send_not_sir": 59357464, + "execution": 59357464 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 99607375000, + "send_not_sir": 99607375000, + "execution": 99607375000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 6812999 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400 + }, + "grow_mem_cost": 1, + "regular_op_cost": 3856371, + "limit_config": { + "max_gas_burnt": 200000000000000, + "max_stack_height": 16384, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_length_storage_key": 4194304, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } + }, + "validators": [ + { + "account_id": "nfvalidator1.near", + "public_key": "ed25519:14pWWRutZtGFKX4B8q89KVFaUWY1Cqu1JcqYXhCDeFh1", + "amount": "50000000000000000000000000000" + }, + { + "account_id": "nfvalidator2.near", + "public_key": "ed25519:BwZk4bkYJxo79P2vSRw2uk1nfiqEfVkHvr5p8eVsqASC", + "amount": "50000000000000000000000000000" + }, + { + "account_id": "nfvalidator3.near", + "public_key": "ed25519:DMz11tmPvhdqpi7CzP2JULeeSE8SxYRD8pys5nKke4FS", + "amount": "50000000000000000000000000000" + }, + { + "account_id": "nfvalidator4.near", + "public_key": "ed25519:Fi3CQDHJoviKazVR27YmfFzWcFnvmoPBKEDd9ouq5Tjx", + "amount": "50000000000000000000000000000" + } + ], + "transaction_validity_period": 86400, + "protocol_reward_rate": [ + 0, + 1 + ], + "max_inflation_rate": [ + 0, + 1 + ], + "total_supply": "999999999792372916156395166000000", + "num_blocks_per_year": 31536000, + "protocol_treasury_account": "treasury.near", + "fishermen_threshold": "340282366920938463463374607431768211455", + "records": [ + { + "Account": { + "account_id": "01.near", + "account": { + "amount": "49999999958035075000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 264 + } + } + }, + { + "Account": { + "account_id": "alex.near", + "account": { + "amount": "9999000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "bo.near", + "account": { + "amount": "50000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "bot.pulse.near", + "account": { + "amount": "791373397694044304600000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "bowen.near", + "account": { + "amount": "49999999506363398300200000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "contributors.near", + "account": { + "amount": "418000000000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "erik.near", + "account": { + "amount": "10000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "foundation.near", + "account": { + "amount": "581779979999999955363487500000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "illia.near", + "account": { + "amount": "9909124991408763970627200000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 321 + } + } + }, + { + "Account": { + "account_id": "kendall.near", + "account": { + "amount": "49998999710140992484400000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 462 + } + } + }, + { + "Account": { + "account_id": "ledger.vlad.near", + "account": { + "amount": "999999957937258742200000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 327 + } + } + }, + { + "Account": { + "account_id": "mike.near", + "account": { + "amount": "30999999915088987500000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "mikemikemikemikemikemikemikemike", + "account": { + "amount": "19000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "near", + "account": { + "amount": "8700003991476791004803600000", + "locked": "0", + "code_hash": "23tqXYRdbJVuvpLB14Pe9Su9bQBwfn3njKN6EBbKTQwh", + "storage_usage": 197868 + } + } + }, + { + "Account": { + "account_id": "nfvalidator1.near", + "account": { + "amount": "0", + "locked": "50000000000000000000000000000", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "nfvalidator2.near", + "account": { + "amount": "0", + "locked": "50000000000000000000000000000", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "nfvalidator3.near", + "account": { + "amount": "0", + "locked": "50000000000000000000000000000", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "nfvalidator4.near", + "account": { + "amount": "0", + "locked": "50000000000000000000000000000", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "patrick.near", + "account": { + "amount": "9998999875468925000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 263 + } + } + }, + { + "Account": { + "account_id": "peter.near", + "account": { + "amount": "1000874999955363487500000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "pulse.near", + "account": { + "amount": "48001118054588063403800000", + "locked": "0", + "code_hash": "2pMwiHggCBQAv3eFEPtJozDpbHpD8KkL3o3qRv6qs6DT", + "storage_usage": 26061 + } + } + }, + { + "Account": { + "account_id": "registrar", + "account": { + "amount": "10000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "treasury.near", + "account": { + "amount": "10000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Account": { + "account_id": "vlad.near", + "account": { + "amount": "8998999831159137500000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 346 + } + } + }, + { + "Account": { + "account_id": "wallet.pulse.near", + "account": { + "amount": "999899913398562500000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 264 + } + } + }, + { + "Account": { + "account_id": "yifang.near", + "account": { + "amount": "50000000000000000000000000", + "locked": "0", + "code_hash": "11111111111111111111111111111111", + "storage_usage": 182 + } + } + }, + { + "Contract": { + "account_id": "near", + "code": "AGFzbQEAAAAB8gNBYAJ/fwF/YAF/AGAAAX9gBX9+fn5+AGACf34AYAR/fn5+AX5gCH9+fn5+fn5+AGADf35+AGAKf35+fn5+fn5+fgBgBn9+fn5+fgF+YAF/AX5gBH9+fn4AYAN/fn4BfmACf34BfmACf38AYAN/f38Bf2ACfn4AYAF+AX5gAX4AYAABfmADfn5+AGAAAGAIfn5+fn5+fn4BfmAJfn5+fn5+fn5+AX5gAn5+AX5gA35+fgF+YAd+fn5+fn5+AGAEfn5+fgBgCX5+fn5+fn5+fgBgBX5+fn5+AX5gA39/fwBgAX8Bf2AEf39/fgBgBX9/f35/AGAFf39/f38AYAR/f39/AGAFf39/f38Bf2AEf39/fwF/YAV/fn5+fwBgBn9/f39/fwBgAn9/AX5gBX9/fn9/AGADf39+AGAJf35+fn5+fn5+AX5gCn9+fn5+fn5+fn4BfmAEf39+fgF/YAN/fn4Bf2AHf39/f39/fwBgA39/fwF+YAJ+fwF+YAN+f38AYAh+f39/f35+fgBgBH5+fn8AYAN+f34AYAh+f35+fn9/fwBgAn5/AGAHf39/f35+fgBgBH9/fn4AYAd/f39+fn9/AGAEf3x/fwF/YAN+f38Bf2AGf39/f39/AX9gBH5+f38Bf2AEf35+fwBgBn9+fn5+fwAC5QguA2Vudg1yZWFkX3JlZ2lzdGVyABADZW52DHJlZ2lzdGVyX2xlbgARA2VudhJjdXJyZW50X2FjY291bnRfaWQAEgNlbnYRc2lnbmVyX2FjY291bnRfaWQAEgNlbnYRc2lnbmVyX2FjY291bnRfcGsAEgNlbnYWcHJlZGVjZXNzb3JfYWNjb3VudF9pZAASA2VudgVpbnB1dAASA2VudgtibG9ja19pbmRleAATA2Vudg9ibG9ja190aW1lc3RhbXAAEwNlbnYMZXBvY2hfaGVpZ2h0ABMDZW52DXN0b3JhZ2VfdXNhZ2UAEwNlbnYPYWNjb3VudF9iYWxhbmNlABIDZW52FmFjY291bnRfbG9ja2VkX2JhbGFuY2UAEgNlbnYQYXR0YWNoZWRfZGVwb3NpdAASA2VudgtwcmVwYWlkX2dhcwATA2Vudgh1c2VkX2dhcwATA2VudgtyYW5kb21fc2VlZAASA2VudgZzaGEyNTYAFANlbnYJa2VjY2FrMjU2ABQDZW52CWtlY2NhazUxMgAUA2Vudgx2YWx1ZV9yZXR1cm4AEANlbnYFcGFuaWMAFQNlbnYKcGFuaWNfdXRmOAAQA2Vudghsb2dfdXRmOAAQA2Vudglsb2dfdXRmMTYAEANlbnYOcHJvbWlzZV9jcmVhdGUAFgNlbnYMcHJvbWlzZV90aGVuABcDZW52C3Byb21pc2VfYW5kABgDZW52FHByb21pc2VfYmF0Y2hfY3JlYXRlABgDZW52EnByb21pc2VfYmF0Y2hfdGhlbgAZA2VudiNwcm9taXNlX2JhdGNoX2FjdGlvbl9jcmVhdGVfYWNjb3VudAASA2VudiRwcm9taXNlX2JhdGNoX2FjdGlvbl9kZXBsb3lfY29udHJhY3QAFANlbnYicHJvbWlzZV9iYXRjaF9hY3Rpb25fZnVuY3Rpb25fY2FsbAAaA2Vudh1wcm9taXNlX2JhdGNoX2FjdGlvbl90cmFuc2ZlcgAQA2Vudhpwcm9taXNlX2JhdGNoX2FjdGlvbl9zdGFrZQAbA2Vudi1wcm9taXNlX2JhdGNoX2FjdGlvbl9hZGRfa2V5X3dpdGhfZnVsbF9hY2Nlc3MAGwNlbnYvcHJvbWlzZV9iYXRjaF9hY3Rpb25fYWRkX2tleV93aXRoX2Z1bmN0aW9uX2NhbGwAHANlbnYfcHJvbWlzZV9iYXRjaF9hY3Rpb25fZGVsZXRlX2tleQAUA2VudiNwcm9taXNlX2JhdGNoX2FjdGlvbl9kZWxldGVfYWNjb3VudAAUA2VudhVwcm9taXNlX3Jlc3VsdHNfY291bnQAEwNlbnYOcHJvbWlzZV9yZXN1bHQAGANlbnYOcHJvbWlzZV9yZXR1cm4AEgNlbnYNc3RvcmFnZV93cml0ZQAdA2VudgxzdG9yYWdlX3JlYWQAGQNlbnYOc3RvcmFnZV9yZW1vdmUAGQNlbnYPc3RvcmFnZV9oYXNfa2V5ABgDzgbMBh4OHg4eDh4OHg4eDg4fAQEBDg4BDg4OAA8OAQAAHwEOHx4BDiAOIQEhAQEhHiEfHw4hDx8fHyIBIgEBDg4ADh8OIw4OAQAAAAAAAAAAAAAAAAAAAAAADgEOAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ8kJSMiIw4BDiImIg4eDg4ODh4eDh4OHg4OHg8ADiUPDyUAHh4fAA4OHg4eDh4OHg4eDh4ODg4ODh8fDg4OAAAOJyMKHh4oKSMqKiojDg4eHiMOIx4qKioODh8fAQEOHh4eDg4OJycnJycnHgIAHiUABw0EBAQEBAoKCgoEBAQKCgQLCwsHAQcHByssDAwFBAsGBwMDCAsLCgwECQUFDAAAHh4jIy0uFRUVFRUVAB4lACMiHg4OIx4jIwEiHgAODgofAAAAAAAlHh4AAQEBAQEBAQEBAQEBAR4lAQ4BDi8eIyUeDg4BHh8AHg4ADx8eAAEODh4OHh4VJx4OAQEODw8ODg8OAB8fHh4eIwAeHx8ADg4eDh4eHwAeAB8fAR8fAQEfIwAfJA8jDgEOAA4ODgofDgABGAEfDgAoHg4AHg8eDw4ADgABHzAjCg4OACgeHg8oHgEfAR8wIx4PDgAOACgeDgAwIx4PHg8oHg4AAQ4BAQEAAAAOAR4ODgEjAA4BDg4BAQEBAQEBCgEBAQEBAQEBAQEBASUfDh8OHiIiIh4eHw4ODh4eDg4BJyMBDgEODhUEBAEBAQEBKAoxEjIzFDQ1Njc3EwQSDiUeAAEADh4OODkeOh4eAQ4ODh4BAgcHAA4eAAABASMiDicAAB4OIwAAHh4eFR4fHh4eJx8eDhUeCh4BAQ4OHgAAFR8OAAAADx4OIwAAAAEBASUlHgAeAA8OFR4eDgEOHiMeHh4VJwEOAQoKCgAAAAAAHgEBAQAfAAAPABUAAA4ACh8ODgAAHgAAIxUODg4BIw4ODg4OHwAAAAEOAQ4OFRUeDgABHh4ADg4PIwAOAA8jIyIjIgAjAA8AAAoAAB4fJB4AIgAfHwAAAB8PIx8PHx4PHzsAOwAADwAAADwAPSUADwAfHyMjDgAPAQ8AACIAHh4AAAAjHgAAAAAAAAAAPgAAAAAAAAAfHw4ADw8PPz8DA0AEBwFwAcsBywEFAwEAEQYZA38BQYCAwAALfwBBsPrCAAt/AEGw+sIACwecAQkGbWVtb3J5AgAEc2VuZADHAgVjbGFpbQDIAhhjcmVhdGVfYWNjb3VudF9hbmRfY2xhaW0AyQIOY3JlYXRlX2FjY291bnQAygISb25fYWNjb3VudF9jcmVhdGVkAMsCHm9uX2FjY291bnRfY3JlYXRlZF9hbmRfY2xhaW1lZADMAgpfX2RhdGFfZW5kAwELX19oZWFwX2Jhc2UDAgmTAwEAQQELygGQBYMBggGcBuQCgQGoBcQGswWNAe8FiwHoBp8BpAZYpAORAd4GjgF+mQF2mwF0ngF6iQF4jAF/nAF8mgGRApICkwKUApUClgKXApgCmQKaApsCnAKdAp4CnwKgAqECogKjAqQCpQKmAqcCqAKpAqoCqwKsAq0CrgKvArACsQKyArMCtAK1ArYCtwK4ArkCugK7ArwCvQK+AvsB+gGPAa4FtwOtBeICjwPlAvYC3wLgAuEC6wXeAuwF7QXuAowDiAOLA+wC9QLxBrwDwgPDA8QDlAT7BKIEzgPPA9ADzAPNA4oFiwWtBKcEowayBOUFswSxBIoG/ASsBJ0EnwSgBNMGhAWCBYUFgwWPBaMFpQWkBaIFoAXPBtoG5QbfBtQGpwW0BbcFvwW8Bb4FuAXkBuQF1wXzBdoF4QXfBeAF0gXcBeoF6AXpBdYF2AXiBdsF+AX5BdMF+gX7BdEF1QXUBYEG/gWABv8FqQagBpMGwgarBq8GsAaLBqIGsga9Br4G7Aa/BsAGwQbpBuoG6wYK9/QIzAY2AQF/I4CAgIAAQRBrIgMkgICAgAAgAyABIAIQp4OAgAAgACADEK+AgIAAIANBEGokgICAgAAL8wECAX8CfiOAgICAAEHAAGsiAiSAgICAACACQShqQQhqIAFBCGooAgA2AgAgAiABKQIANwMoIAJBCGogAkEoahCdg4CAACACQShqIAJBCGoQxICAgAACQAJAIAIoAihBAUcNACAAIAIoAiw2AgQgAEEBNgIADAELIAJBKGpBEGopAwAhAyACKQMwIQQgAiACQQhqELuAgIAAIgE2AigCQCABRQ0AIABBATYCACAAIAE2AgQMAQsgAkEoahC8gICAACAAQRBqIAM3AwAgAEEIaiAENwMAIABBADYCAAsgAkEUahC+gICAACACQcAAaiSAgICAAAs2AQF/I4CAgIAAQRBrIgMkgICAgAAgAyABIAIQp4OAgAAgACADELGAgIAAIANBEGokgICAgAALhAIBAX8jgICAgABBwABrIgIkgICAgAAgAkEwakEIaiABQQhqKAIANgIAIAIgASkCADcDMCACIAJBMGoQnYOAgAAgAkEwaiACEL+AgIAAAkACQAJAIAIoAjBBAUYNACACQSBqQQhqIAJBMGpBDGooAgA2AgAgAiACKQI0NwMgIAIgAhC7gICAACIBNgIwIAENASACQTBqELyAgIAAIAAgAikDIDcCBCAAQQA2AgAgAEEMaiACQShqKAIANgIADAILIAAgAigCNDYCBCAAQQE2AgAMAQsgAEEBNgIAIAAgATYCBCACQSBqEL6AgIAACyACQQxqEL6AgIAAIAJBwABqJICAgIAACzYBAX8jgICAgABBEGsiAySAgICAACADIAEgAhCng4CAACAAIAMQs4CAgAAgA0EQaiSAgICAAAuEAgEBfyOAgICAAEHAAGsiAiSAgICAACACQTBqQQhqIAFBCGooAgA2AgAgAiABKQIANwMwIAIgAkEwahCdg4CAACACQTBqIAIQwoCAgAACQAJAAkAgAigCMEEBRg0AIAJBIGpBCGogAkEwakEMaigCADYCACACIAIpAjQ3AyAgAiACELuAgIAAIgE2AjAgAQ0BIAJBMGoQvICAgAAgACACKQMgNwIEIABBADYCACAAQQxqIAJBKGooAgA2AgAMAgsgACACKAI0NgIEIABBATYCAAwBCyAAQQE2AgAgACABNgIEIAJBIGoQvoCAgAALIAJBDGoQvoCAgAAgAkHAAGokgICAgAALNgEBfyOAgICAAEEQayIDJICAgIAAIAMgASACEKeDgIAAIAAgAxC1gICAACADQRBqJICAgIAAC7UCAQJ/I4CAgIAAQeAAayICJICAgIAAIAJBwABqQQhqIAFBCGooAgA2AgAgAiABKQIANwNAIAJBCGogAkHAAGoQnYOAgAAgAkHAAGogAkEIahC6gICAAAJAAkACQCACKAJAQQFGDQAgAkE4aiIDIAJBwABqQRRqKQIANwMAIAJBKGpBCGogAkHAAGpBDGopAgA3AwAgAiACKQJENwMoIAIgAkEIahC7gICAACIBNgJAIAENASACQcAAahC8gICAACAAIAIpAyg3AgQgAEEANgIAIABBFGogAykDADcCACAAQQxqIAJBMGopAwA3AgAMAgsgACACKAJENgIEIABBATYCAAwBCyAAQQE2AgAgACABNgIEIAJBKGoQvYCAgAALIAJBFGoQvoCAgAAgAkHgAGokgICAgAALNgEBfyOAgICAAEEQayIDJICAgIAAIAMgASACEKeDgIAAIAAgAxC3gICAACADQRBqJICAgIAAC+ECAQN/I4CAgIAAQfAAayICJICAgIAAIAJByABqQQhqIAFBCGooAgA2AgAgAiABKQIANwNIIAJBCGogAkHIAGoQnYOAgAAgAkHIAGogAkEIahDDgICAAAJAAkACQCACKAJIQQFGDQAgAkEoakEYaiIDIAJByABqQSBqKQMANwMAIAJBKGpBEGoiBCACQcgAakEYaikDADcDACACQShqQQhqIAJByABqQRBqKQMANwMAIAIgAikDUDcDKCACIAJBCGoQu4CAgAAiATYCSCABDQEgAkHIAGoQvICAgAAgAEEIaiACKQMoNwMAIABBADYCACAAQSBqIAMpAwA3AwAgAEEYaiAEKQMANwMAIABBEGogAkEoakEIaikDADcDAAwCCyAAIAIoAkw2AgQgAEEBNgIADAELIABBATYCACAAIAE2AgQgBBC+gICAAAsgAkEUahC+gICAACACQfAAaiSAgICAAAs2AQF/I4CAgIAAQRBrIgMkgICAgAAgAyABIAIQp4OAgAAgACADELmAgIAAIANBEGokgICAgAALtQIBAn8jgICAgABB4ABrIgIkgICAgAAgAkHAAGpBCGogAUEIaigCADYCACACIAEpAgA3A0AgAkEIaiACQcAAahCdg4CAACACQcAAaiACQQhqEMCAgIAAAkACQAJAIAIoAkBBAUYNACACQThqIgMgAkHAAGpBFGopAgA3AwAgAkEoakEIaiACQcAAakEMaikCADcDACACIAIpAkQ3AyggAiACQQhqELuAgIAAIgE2AkAgAQ0BIAJBwABqELyAgIAAIAAgAikDKDcCBCAAQQA2AgAgAEEUaiADKQMANwIAIABBDGogAkEwaikDADcCAAwCCyAAIAIoAkQ2AgQgAEEBNgIADAELIABBATYCACAAIAE2AgQgAkEoahDBgICAAAsgAkEUahC+gICAACACQeAAaiSAgICAAAsUACAAIAEgACAAIAAgABCKgoCAAAt6AQJ/I4CAgIAAQSBrIgEkgICAgAAgAUEIaiAAEM2AgIAAAkACQCABLQAIQQFGDQBBACECAkAgAS0ACUEBRw0AIAFBEzYCECAAIAFBEGoQxYCAgAAhAgsgAUEIahDIgICAAAwBCyABKAIMIQILIAFBIGokgICAgAAgAgsVAAJAIAAoAgBFDQAgABDYgICAAAsLFQAgABC+gICAACAAQQxqEL6AgIAACxIAIAAQm4OAgAAgABCcg4CAAAsUACAAIAEgACAAIAAgABCIgoCAAAsUACAAIAEgACAAIAAgABCJgoCAAAsVACAAEL6AgIAAIABBDGoQvoCAgAALFAAgACABIAAgACAAIAAQhoKAgAALFAAgACABIAAgACAAIAAQhYKAgAALFAAgACABIAAgACAAIAAQh4KAgAALawECfyOAgICAAEEgayICJICAgIAAIAJBCGogABCjg4CAACACKAIMIQAgAigCCCEDIAJBEGpBCGogAUEIaigCADYCACACIAEpAgA3AxAgAkEQaiADIAAQooOAgAAhASACQSBqJICAgIAAIAEL4QEBAX8jgICAgABBMGsiAySAgICAACADIAEgAhCCg4CAACADKAIEIQIgAygCACEBAkACQANAAkAgAiABRw0AQQAhAQwDCyADQQhqIAAQx4CAgAACQCADLQAIQQFGDQACQCADLQAJQQFGDQBBBSECIANBEGohAQwDCwJAIAMtAAogAS0AAEYNAEEJIQIgA0EgaiEBDAMLIAFBAWohASADQQhqEMiAgIAADAELCyADKAIMIQEMAQsgASACNgIAIAAgARDJgICAACEBIANBCGoQyICAgAALIANBMGokgICAgAAgAQtNAQN/QQAhAgJAIAEoAggiAyABKAIETw0AIAEoAgAgA2otAAAhBEEBIQIgASADQQFqNgIICyAAIAI6AAEgAEEAOgAAIABBAmogBDoAAAsYAAJAIAAtAABFDQAgAEEEahDYgICAAAsLawECfyOAgICAAEEgayICJICAgIAAIAJBCGogABChg4CAACACKAIMIQAgAigCCCEDIAJBEGpBCGogAUEIaigCADYCACACIAEpAgA3AxAgAkEQaiADIAAQooOAgAAhASACQSBqJICAgIAAIAELZAEBfyOAgICAAEEQayICJICAgIAAAkACQCAAKAIMRQ0AIAAhAQwBCyACQQhqIABBCGooAgA2AgAgAiAAKQIANwMAIAEgAhDJgICAACEBIAAQ8oCAgAALIAJBEGokgICAgAAgAQulCgEGfyOAgICAAEHAAGsiASSAgICAACAAQQxqIgIQzICAgABBACEDAkACQAJAAkADQCABQSBqIAAQzYCAgAACQAJAIAEtACBBAUYNAAJAAkACQAJAAkACQAJAAkAgAS0AIUEBRw0AIAEtACIhBCABQSBqEMiAgIAAIARBIkYNBCAEQS1GDQMCQAJAIARB2wBGDQAgBEHmAEYNBCAEQe4ARg0BIARB9ABGDQMgBEH7AEYNACAEQVBqQf8BcUEKTw0HIAEgABDOgICAACIENgIwIAFBMGohBSAERQ0IDBALQQAhBSACIANB//8DcUEARyAGEM+AgIAAIAAQ0ICAgAAgBCEGDAgLIAAQ0ICAgAAgASAAQYeAwIAAQQMQxoCAgAAiBDYCMCABQTBqIQUgBA0ODAYLIAFBBTYCMCAAIAFBMGoQxYCAgAAhBCABQSBqEMiAgIAADA0LIAAQ0ICAgAAgASAAQYSAwIAAQQMQxoCAgAAiBDYCMCABQTBqIQUgBEUNBAwMCyAAENCAgIAAIAEgAEGAgMCAAEEEEMaAgIAAIgQ2AjAgAUEwaiEFIARFDQMMCwsgABDQgICAACABIAAQzoCAgAAiBDYCMCABQTBqIQUgBEUNAgwKCyAAENCAgIAAIAEgABCtg4CAACIENgIwIAFBMGohBSAERQ0BDAkLIAFBCjYCMCAAIAFBMGoQxYCAgAAhBAwICyAFELyAgIAAQQEhBSADQf//A3ENACABQRBqIAIQ0YCAgAAgAS0AEEEBcUUNBiABLQARIQYLAkADQCABQSBqIAAQzYCAgAAgAS0AIEEBRg0CAkACQAJAAkACQAJAAkACQAJAAkACQCABLQAhQQFGDQAgBkH/AXEiBkHbAEcNAUECIQQgAUEwaiEGDAgLIAEtACIiBEHdAEYNASAEQf0ARg0DIARBLEcNBCAFQQFxRQ0FIAAQ0ICAgAAMBQsgBkH7AEcNAUEDIQQgAUEwaiEGDAYLIAZB/wFxQdsARw0CDAYLQYqAwIAAQShB0IDAgAAQoYWAgAAACyAGQf8BcUH7AEYNBAsgBUEBcUUNACAGQf8BcSIGQdsARw0BQQchBCABQTBqIQYMAgsgAUEgahDIgICAAEEBIQMgBkH/AXFB+wBHDQggAUEYaiAAEM2AgIAAIAEtABhBAUYNBSABLQAZQQFGDQNBAyEEIAFBMGohBgwKCwJAIAZB+wBHDQBBCCEEIAFBMGohBgwBC0GKgMCAAEEoQdCAwIAAEKGFgIAAAAsgBiAENgIAIAAgBhDFgICAACEEIAFBIGoQyICAgAAMCgsgAUEgahDIgICAACAAENCAgIAAIAFBCGogAhDRgICAACABLQAIQQFxRQ0IIAEtAAkhBkEBIQUMAQsLAkAgAS0AGkEiRg0AQRAhBCABQSBqIQYMBgsgABDQgICAACABQRhqEMiAgIAAIAEgABCtg4CAACIENgIwIAQNByABQTBqELyAgIAAIAFBGGogABDNgICAACABLQAYQQFGDQACQCABLQAZQQFGDQBBAyEEIAFBMGohBgwFCyABLQAaQTpGDQJBBiEEIAFBIGohBgwECyABKAIcIQQMBgsgASgCJCEEDAULIAAQ0ICAgAAgAUEYahDIgICAAAwACwsgBiAENgIAIAAgBhDFgICAACEEIAFBGGoQyICAgAAMAgsgBiAENgIAIAAgBhDFgICAACEEIAFBGGoQyICAgAAMAQtBACEECyABQcAAaiSAgICAACAECwwAIABBABCUg4CAAAu4AQEDfyOAgICAAEEQayICJICAgIAAAkADQAJAIAEoAggiAyABKAIESQ0AQQAhASACQQA7AQgMAgsgAkGAAjsBCCACIAEoAgAgA2otAAAiAzoACgJAIANBd2oiBEEXSw0AQQEgBHRBk4CABHFFDQAgARDQgICAACACQQhqEMiAgIAADAELC0EBIQELIAAgAToAASAAQQA6AAAgAEECaiADOgAAIAJBCGoQyICAgAAgAkEQaiSAgICAAAvVAwEDfyOAgICAAEEgayIBJICAgIAAIAEgABDegICAAAJAAkACQAJAAkACQCABLQAAIgJBAUYNAAJAAkACQCABLQABIgNBMEYNACADQU9qQf8BcUEISw0EDAELIAFBCGogABDTgICAACABLQAIQQFGDQQgAUEIaiEDIAEtAAlBUGpB/wFxQQpPDQEgAUEMNgIQIAAgAUEQahDFgICAACEDIAFBCGoQ14CAgAAMBgsDQCABQRBqIAAQ04CAgAAgAS0AEEEBRg0FAkAgAS0AEUFQakH/AXFBCUsNACAAENCAgIAAIAFBEGoQ14CAgAAMAQsLIAFBEGohAwsgAxDXgICAACABENeAgIAAIAFBEGogABDTgICAAAJAAkACQCABLQAQQQFGDQAgAS0AESICQS5GDQECQCACQcUARg0AQQAhAyACQeUARw0DCyAAEN2AgIAAIQMMAgsgASgCFCEDDAcLIAAQ3ICAgAAhAwsgAUEQahDXgICAAAwFCyABKAIEIQMMBAsgAUEMNgIQIAAgAUEQahDJgICAACEDDAILIAEoAgwhAwwBCyABKAIUIQMgAkUNACABQQRyENiAgIAADAELIAEQ14CAgAALIAFBIGokgICAgAAgAwuEAQEDfyOAgICAAEEQayIDJICAgIAAIAAgARCNhYCAACAAEJuFgIAAIQQgACgCCCEFIAMgAjoACSADIAE6AAgCQANAIAMgA0EIahCwgYCAACADLQAAQQFxRQ0BIAQgBWogAy0AAToAACAFQQFqIQUMAAsLIAAgBTYCCCADQRBqJICAgIAACzQBAn8CQCAAKAIIIgFBAWoiAiABTw0AQbCCwIAAQRxB6InAgAAQjYaAgAAACyAAIAI2AggLbgECfyOAgICAAEEQayICJICAgIAAAkACQCABKAIIIgMNAEEAIQEMAQsgASADQX9qNgIIIAJBCGogARDPhYCAACACKAIIIAEoAghqLQAAIQNBASEBCyAAIAM6AAEgACABOgAAIAJBEGokgICAgAALjQMCAn8CfiOAgICAAEEgayIEJICAgIAAIARBCGogARDTgICAAAJAAkACQCAELQAIQQFGDQACQAJAAkACQCAELQAJIgVBLkYNACAFQcUARg0BIAVB5QBGDQFCASEGIAJFDQIgAyEHDAMLIARBEGogASACIANBABDUgICAACAEQRBqIQEgBCgCEEEBRg0EIAQpAxghByAEQRBqENWAgIAAQgAhBgwCCyAEQRBqIAEgAiADQQAQ1oCAgAAgBEEQaiEBIAQoAhBBAUYNAyAEKQMYIQcgBEEQahDVgICAAEIAIQYMAQtCACEGAkBCgICAgICAgICAf0IAIAN9IANCgICAgICAgICAf1EbIgdCAVkNAEICIQYMAQsgA7q9QoCAgICAgICAgH+FIQcLIABBADYCACAAQRBqIAc3AwAgAEEIaiAGNwMAIARBCGoQ14CAgAAMAgsgACAEKAIMNgIEIABBATYCAAwBCyAAQQE2AgAgACABKAIENgIEIARBCGoQ14CAgAALIARBIGokgICAgAALewECfyOAgICAAEEQayICJICAgIAAAkACQCABKAIIIgMgASgCBE8NACACQQE6AAkgAiABKAIAIANqLQAAIgE6AAoMAQtBACEBIAJBADoACQsgACABOgABIABBADoAACACQQA6AAggAkEIahDIgICAACACQRBqJICAgIAAC7cFAgV/AX4jgICAgABBMGsiBSSAgICAACABENCAgIAAQQAhBgJAAkACQAJAAkACQAJAAkACQAJAA0AgBUEQaiABENOAgIAAIAUtABAiB0EBRg0CIAUtABFBUGoiCEH/AXEiCUEKTw0BIAEQ0ICAgAACQAJAIANCmbPmzJmz5swZVA0AAkAgA0KZs+bMmbPmzBlSDQAgCUEGSQ0BCwNAIAVBIGogARDTgICAACAFLQAgQQFGDQYgBS0AIUFQakH/AXFBCUsNAiABENCAgIAAIAVBIGoQ14CAgAAMAAsLIANCCn4iCiAIrUL/AYN8IgMgClQNByAEQQBIIARBf0ogBEF/aiIEQX9KR3ENCCAFQRBqENeAgIAAQQEhBiAEIQQMAQsLIAVBIGoQ14CAgAAgBUEQahDXgICAAAwICyAFQRBqENeAgIAAIAZBAXENByABKAIIIgQgASgCBEkNAiAFQQA6AAlBBSEIIAVBIGohBAwDCyAAIAUoAhQ2AgQgAEEBNgIADAcLIAAgBSgCJDYCBCAAQQE2AgAgBw0EIAVBEGoQ14CAgAAMBgsgBUEBOgAJIAUgASgCACAEai0AADoACkEMIQggBUEQaiEECyAFQQA6AAggBCAINgIAIAEgBBDFgICAACEEIABBATYCACAAIAQ2AgQgBUEIahDIgICAAAwEC0GwgsCAAEEcQeSBwIAAEI2GgIAAAAtBwIHAgABBIUHMgsCAABCNhoCAAAALIAVBEGpBBHIQ2ICAgAAMAQsgBUEgaiABENOAgIAAAkAgBS0AIEEBRw0AIAAgBSgCJDYCBCAAQQE2AgAMAQsCQAJAIAUtACFBIHJB5QBGDQAgACABIAIgAyAEENmAgIAADAELIAAgASACIAMgBBDWgICAAAsgBUEgahDXgICAAAsgBUEwaiSAgICAAAsYAAJAIAAoAgBFDQAgAEEEahDYgICAAAsLwAUCBn8BfiOAgICAAEEgayIFJICAgIAAIAEQ0ICAgAAgBUEQaiABENOAgIAAQQEhBgJAAkAgBS0AEEEBRg0AAkAgBS0AEUFVaiIHQQJLDQACQAJAIAcOAwECAAELQQAhBgsgARDQgICAAAsgBUEQahDXgICAACAFQQhqIAEQx4CAgAACQCAFLQAIQQFGDQACQCAFLQAJQQFHDQAgBS0ACiEHIAVBCGoQyICAgAACQCAHQVBqQf8BcSIHQQpJDQAgBUEMNgIQIAEgBUEQahDJgICAACEBIABBATYCACAAIAE2AgQMBAsDQCAFQRBqIAEQ04CAgAACQAJAAkAgBS0AEEEBRg0AIAUtABFBUGpB/wFxIghBCUsNASABENCAgIAAIAdBy5mz5gBMDQICQCAHQcyZs+YARw0AIAhBB00NAwsgACABIAIgAyAGEN+AgIAAIAVBEGoQ14CAgAAMBwsgACAFKAIUNgIEIABBATYCAAwGCyAFQRBqENeAgIAAIAAgASACIANB/////wdBgICAgHggBCAHaiIJQQBIGyAJIARBf0oiCCAHQX9KIgpGIAggCUF/SkdxG0H/////B0GAgICAeCAEIAdrIgdBAEgbIAcgCCAKRyAIIAdBf0pHcRsgBhsQ2YCAgAAMBQsCQCAHrEIKfiILQiCIpyALpyIHQR91Rg0AQYCCwIAAQSFB7ILAgAAQjYaAgAAACwJAIAdBf0oiCSAIQX9KRiAJIAcgCGoiB0F/SkdxDQAgBUEQahDXgICAAAwBCwtBsILAgABBHEHsgsCAABCNhoCAAAALIAVBBTYCECABIAVBEGoQyYCAgAAhASAAQQE2AgAgACABNgIEIAVBCGoQyICAgAAMAgsgACAFKAIMNgIEIABBATYCAAwBCyAAIAUoAhQ2AgQgAEEBNgIACyAFQSBqJICAgIAACxgAAkAgAC0AAEUNACAAQQRqENiAgIAACwtZAQJ/AkAgACgCACIBKAIAIgJBAUsNAAJAAkAgAg4CAAEACyABQQhqKAIAIgJFDQEgASgCBCACQQEQzoKAgAAMAQsgAUEEahCNgYCAAAsgACgCABDygICAAAupAgQBfwF8AX8BfCOAgICAAEEQayIFJICAgIAAIAO6IQYCQAJAA0AgBCEHAkACQAJAAkACQCAEQX9KDQAgBEGAgICAeEYNAUEAIARrIQcLIAdBtQJJDQELIAZEAAAAAAAAAABhDQQgBEEASA0CIAVBDTYCACAAIAEgBRDJgICAADYCBAwBCyAHQQN0QeCjwIAAaisDACEIAkAgBEF/Sg0AIAYgCKMhBgwECyAGIAiiIga9Qv///////////wCDv0QAAAAAAADwf2INAyAFQQ02AgAgACABIAUQyYCAgAA2AgQLQQEhBAwDCyAGRKDI64XzzOF/oyEGIARBtAJqIQQMAAsLIABBCGogBiAGmiACGzkDAEEAIQQLIAAgBDYCACAFQRBqJICAgIAAC7IFBAJ/AX4BfwF+I4CAgIAAQSBrIgMkgICAgAAgA0EIaiABEMeAgIAAAkACQAJAAkACQAJAAkACQAJAAkAgAy0ACEEBRg0AIAMtAAlBAUcNASADLQAKIQQgA0EIahDIgICAAAJAAkAgBEH/AXFBMEYNACAEQU9qQf8BcUEJSQ0BIANBDDYCECABIANBEGoQyYCAgAAhASAAQQE2AgAgACABNgIEDAsLIANBCGogARDTgICAACADLQAIQQFGDQMCQAJAIAMtAAlBUGpB/wFxQQlLDQAgA0EMNgIQIAEgA0EQahDFgICAACEBIABBATYCACAAIAE2AgQMAQsgACABIAJCABDSgICAAAsgA0EIahDXgICAAAwKCyAEQVBqrUL/AYMhBQNAIANBCGogARDTgICAACADLQAIQQFGDQQgAy0ACUFQaiIEQf8BcSIGQQlLDQUgARDQgICAAAJAIAVCmLPmzJmz5swZWA0AAkAgBUKZs+bMmbPmzBlSDQAgBkEGSQ0BC0EBIQQgA0EQaiABIAIgBUEBENuAgIAAIAMoAhBBAUcNByAAIAMoAhQ2AgQMCAsgBUIKfiIHIAStQv8Bg3wiBSAHVA0IIANBCGoQ14CAgAAMAAsLIAAgAygCDDYCBCAAQQE2AgAMCAsgA0EFNgIQIAEgA0EQahDJgICAACEBIABBATYCACAAIAE2AgQgA0EIahDIgICAAAwHCyAAIAMoAgw2AgQgAEEBNgIADAYLIAAgAygCDDYCBCAAQQE2AgAMBQsgACABIAIgBRDSgICAAAwDCyAAQRBqIAMpAxg3AwAgAEEIakIANwMAIANBEGoQ1YCAgABBACEECyAAIAQ2AgAMAQtBsILAgABBHEHcgsCAABCNhoCAAAALIANBCGoQ14CAgAALIANBIGokgICAgAALjQIBAn8jgICAgABBEGsiBSSAgICAAAJAAkACQAJAAkADQCAFQQhqIAEQ04CAgAAgBS0ACEEBRg0BAkAgBS0ACSIGQVBqQf8BcUEJSw0AIAEQ0ICAgAAgBEF/SiIGIAYgBEEBaiIEQX9KR3ENBiAFQQhqENeAgIAADAELCyAGQS5GDQECQCAGQcUARg0AIAZB5QBGDQAgACABIAIgAyAEENmAgIAADAMLIAAgASACIAMgBBDWgICAAAwCCyAAIAUoAgw2AgQgAEEBNgIADAILIAAgASACIAMgBBDUgICAAAsgBUEIahDXgICAAAsgBUEQaiSAgICAAA8LQbCCwIAAQRxB/ILAgAAQjYaAgAAAC+UBAQJ/I4CAgIAAQRBrIgEkgICAgAAgABDQgICAAEEBIQICQAJAA0AgASAAENOAgIAAIAEtAABBAUYNAQJAIAEtAAFBUGpB/wFxQQpPDQAgABDQgICAACABENeAgIAAQQAhAgwBCwsgARDXgICAAAJAIAJBAXENACABIAAQ04CAgAAgAS0AAEEBRg0BAkACQCABLQABQSByQeUARg0AQQAhAAwBCyAAEN2AgIAAIQALIAEQ14CAgAAMAgsgAUEMNgIAIAAgARDFgICAACEADAELIAEoAgQhAAsgAUEQaiSAgICAACAAC7cCAQJ/I4CAgIAAQSBrIgEkgICAgAAgABDQgICAACABQRBqIAAQ04CAgAACQAJAIAEtABBBAUYNAAJAIAEtABFBVWoiAkECSw0AAkAgAg4DAAEAAAsgABDQgICAAAsgAUEQahDXgICAACABQQhqIAAQ3oCAgAACQAJAIAEtAAhBAUYNACABLQAJQVBqQf8BcUEJSw0BIAFBCGoQ14CAgAADQCABQRBqIAAQ04CAgAAgAS0AEEEBRg0DAkAgAS0AEUFQakH/AXFBCk8NACAAENCAgIAAIAFBEGoQ14CAgAAMAQsLIAFBEGoQ14CAgABBACEADAMLIAEoAgwhAAwCCyABQQw2AhAgACABQRBqEMmAgIAAIQAgAUEIahDXgICAAAwBCyABKAIUIQALIAFBIGokgICAgAAgAAt4AQF/I4CAgIAAQRBrIgIkgICAgAAgAkEIaiABEMeAgIAAQQEhAQJAAkAgAi0ACEEBRg0AQQAhASAAIAItAApBACACLQAJGzoAASACQQhqEMiAgIAADAELIABBBGogAigCDDYCAAsgACABOgAAIAJBEGokgICAgAAL0gEBAX8jgICAgABBEGsiBSSAgICAAAJAAkAgA1ANACAERQ0AIAVBDTYCACABIAUQyYCAgAAhASAAQQE2AgAgACABNgIEDAELAkADQCAFIAEQ04CAgAAgBS0AAEEBRg0BAkAgBS0AAUFQakH/AXFBCUsNACABENCAgIAAIAUQ14CAgAAMAQsLIAUQ14CAgAAgAEEANgIAIABBCGpEAAAAAAAAAABEAAAAAAAAAIAgAhs5AwAMAQsgACAFKAIENgIEIABBATYCAAsgBUEQaiSAgICAAAvxBQEDfyOAgICAAEEwayIDJICAgIAAIANBCGogABDTgICAAEEAIAMtAAkgAy0ACCIEQQFGGyEFAkAgBEUNACADQQhqENeAgIAACwJAAkACQAJAAkACQAJAAkAgBUH/AXEiBEEiRg0AAkACQCAEQS1GDQACQCAEQeYARg0AAkACQCAEQe4ARg0AIARB9ABGDQEgBEHbAEYNBiAEQfsARg0HIAVBUGpB/wFxQQpPDQogA0EIaiAAQQEQ2oCAgAAgAygCCEEBRg0EIANBKGogA0EYaikDADcDACADIAMpAxA3AyAgA0EgaiABIAIQnoOAgAAhBAwLCyAAENCAgIAAIAMgAEGHgMCAAEEDEMaAgIAAIgQ2AgggBA0LIANBCGoQvICAgAAgA0EHOgAIIANBCGogASACEJ+DgIAAIQQMCgsgABDQgICAACADIABBhIDAgABBAxDGgICAACIENgIIIAQNCiADQQhqELyAgIAAIANBgAI7AQggA0EIaiABIAIQn4OAgAAhBAwJCyAAENCAgIAAIAMgAEGAgMCAAEEEEMaAgIAAIgQ2AgggBA0JIANBCGoQvICAgAAgA0EAOwEIIANBCGogASACEJ+DgIAAIQQMCAsgABDQgICAACADQQhqIABBABDagICAACADKAIIQQFHDQQLIAMoAgwhBAwHCyAAENCAgIAAIABBDGoiBBDMgICAACADQSBqIAAgBBCpg4CAACADKAIgQQFHDQMgAygCJCEEDAYLIANBCjoACCADQQhqIAEgAhCfg4CAACEEDAQLIANBCzoACCADQQhqIAEgAhCfg4CAACEEDAMLIANBKGogA0EYaikDADcDACADIAMpAxA3AyAgA0EgaiABIAIQnoOAgAAhBAwCCyADIANBKGopAwA3AgwgA0EFOgAIIANBCGogASACEJ+DgIAAIQQMAQsgA0EKNgIIIAAgA0EIahDFgICAACEECyAEIAAQyoCAgAAhBAsgA0EwaiSAgICAACAEC6wBAQF/I4CAgIAAQSBrIgEkgICAgAAgAUEIaiAAEM2AgIAAAkACQAJAAkAgAS0ACEEBRg0AIAEtAAlBAUYNASABQQM2AhAgACABQRBqEMWAgIAAIQAMAgsgASgCDCEADAILAkAgAS0ACkE6Rw0AIAAQ0ICAgABBACEADAELIAFBBjYCECAAIAFBEGoQxYCAgAAhAAsgAUEIahDIgICAAAsgAUEgaiSAgICAACAAC9EBAQJ/I4CAgIAAQSBrIgEkgICAgAAgAUEIaiAAEM2AgIAAAkACQAJAAkAgAS0ACEEBRg0AIAEtAAlBAUYNASABQQM2AhAgACABQRBqEMWAgIAAIQAMAgsgASgCDCEADAILAkACQCABLQAKIgJB/QBGDQAgAkEsRw0BIAFBEjYCECAAIAFBEGoQxYCAgAAhAAwCCyAAENCAgIAAQQAhAAwBCyABQRM2AhAgACABQRBqEMWAgIAAIQALIAFBCGoQyICAgAALIAFBIGokgICAgAAgAAugAgEDfyOAgICAAEEwayIBJICAgIAAIAEgABDNgICAAAJAAkACQAJAIAEtAABBAUYNACABLQABQQFGDQEgAUECNgIgIAAgAUEgahDFgICAACEADAILIAEoAgQhAAwCCwJAIAEtAAIiAkHdAEYNAAJAIAJBLEcNACAAENCAgIAAIAFBCGogABDNgICAAAJAAkAgAS0ACA0AIAEtAAlFDQAgAS0ACkHdAEcNAEESIQMgAUEQaiECDAELQRMhAyABQSBqIQILIAIgAzYCACAAIAIQxYCAgAAhACABQQhqEMiAgIAADAILIAFBEzYCICAAIAFBIGoQxYCAgAAhAAwBCyAAENCAgIAAQQAhAAsgARDIgICAAAsgAUEwaiSAgICAACAAC6sBAQF/I4CAgIAAQRBrIgUkgICAgAAgASgCAEGNg8CAAEEBEJiFgIAAIAVBAzoACCAFQQhqEOWAgIAAIAVBCGogASAFIAMgBBDmgICAAAJAAkAgBS0ACEEDRw0AIAVBCGoQ5YCAgAAgASgCAEGNg8CAAEEBEJiFgIAAIAVBAzoACCAFQQhqEOWAgIAAIABBAzoAAAwBCyAAIAUpAwg3AgALIAVBEGokgICAgAALFwACQCAALQAAQQNGDQAgABCNgYCAAAsL+QYBDX8jgICAgABBMGsiBSSAgICAACAFQSBqIAMgAyAEahD/goCAACADQX9qIQYgBEF/cyEHIAUgBUEgahCag4CAAEEAIQggBSgCCCEJIAUoAgQhCiAFKAIAIQsDQCAKIAtrIQxBACENAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQANAIAwgDUYNASAJIA1qIg5BAWoiDyAOSQ0CIAsgDWohDiANQQFqIQ0gDi0AACIQQZy9wIAAai0AACIORQ0ACyAJIA1qIgxBf2oiESAITQ0EIAUgBDYCFCAFIAM2AhAgBSAINgIYIAUgETYCHCAIRQ0DIAggBEYNAyAIIARPDQUgAyAIaiwAAEG/f0oNAwwFCxCehYCAACAIIARGDQEgBSAENgIEIAUgAzYCACAFIAg2AhwgBSAENgIQAkACQCAIRQ0AIAggBE8NASADIAhqLAAAQb9/TA0BCyABKAIAIAMgCGogBCAIaxCYhYCAACAFQQM6ACAgBUEgahDlgICAAAwCCyAFIAVBEGo2AiggBSAFQRxqNgIkIAUgBTYCICAFQSBqEOeAgIAAAAtBsILAgABBHEHwh8CAABCNhoCAAAALIABBAzoAACAFQTBqJICAgIAADwsCQCAHIAlqIA1qRQ0AIBEgBE8NAiAGIAlqIA1qLAAAQb9/TA0CCyABKAIAIAMgCGogCSAIayANakF/ahCYhYCAACAFQQM6ACAgBUEgahDlgICAAAsgDkGSf2oiCUEHTQ0BIA5Bnn9qIglBBE0NAkGpg8CAACEJIA5BIkYNCiAOQdwARw0DQaeDwIAAIQkMCgsgBSAFQRxqNgIoIAUgBUEYajYCJCAFIAVBEGo2AiAgBUEgahDogICAAAALIAkOCAYBAQEFAQQDBgsgCQ4FAQAAAAYBC0GKgMCAAEEoQdCAwIAAEKGFgIAAAAtBpYPAgAAhCQwFCyAFQdzqwYEDNgAQIAUgEEEPcUGMvcCAAGotAAA6ABUgBSAQQQR2QYy9wIAAai0AADoAFCABKAIAIAVBEGpBBhCYhYCAAAwFC0Gdg8CAACEJDAMLQZ+DwIAAIQkMAgtBoYPAgAAhCQwBC0Gjg8CAACEJCyABKAIAIAlBAhCYhYCAAAsgBUEDOgAgIAsgDWohCyAFQSBqEOWAgIAAIA8hCSAMIQgMAAsLKgEBfyAAKAIAIgEoAgAgASgCBCAAKAIEKAIAIAAoAggoAgAQkoaAgAAACyoBAX8gACgCACIBKAIAIAEoAgQgACgCBCgCACAAKAIIKAIAEJKGgIAAAAuvAQEBfyOAgICAAEEgayICJICAgIAAIAJBCGpBgAEQ6oCAgAAgAiACQQhqNgIYIAIgASACQRhqEOuAgIAAIgE2AhwCQAJAIAENACACQRxqELyAgIAAIAJBADYCFCACQRRqELyAgIAAIABBDGogAkEQaigCADYCACAAIAIpAwg3AgQgAEEANgIADAELIABBATYCACAAIAE2AgQgAkEIahC+gICAAAsgAkEgaiSAgICAAAtGAgF/AX4jgICAgABBEGsiAiSAgICAACACQQhqIAFBABCWg4CAACACKQMIIQMgAEEANgIIIAAgAzcCACACQRBqJICAgIAAC7YBAQJ/I4CAgIAAQSBrIgIkgICAgAACQCAAKAIIDQAgAEEIaiIDQX8Q/4SAgAAaIABBDGpBAToAACADIAAoAghBAWoQ/4SAgAAaIAEoAgBBl4PAgABBBBCYhYCAACACQQM6ABAgAiACQRBqEO2AgIAAIgA2AgwCQCAADQAgAkEMahC8gICAAAsgAkEgaiSAgICAACAADwtBxITAgABBECACQRhqQciFwIAAQZiFwIAAEKqGgIAAAAvZAQEBfyOAgICAAEEgayICJICAgIAAIAJBgAEQ6oCAgAAgAkGOg8CAAEGSg8CAACABLQAAIgEbQQRBBSABGxCYhYCAACACQQM6ABggAiACQRhqEO2AgIAAIgE2AhQCQAJAIAENACACQRRqELyAgIAAIAJBADYCECACQRBqELyAgIAAIAJBADYCDCACQQxqELyAgIAAIABBDGogAkEIaigCADYCACAAIAIpAwA3AgQgAEEANgIADAELIABBATYCACAAIAE2AgQgAhC+gICAAAsgAkEgaiSAgICAAAtUAQF/I4CAgIAAQRBrIgEkgICAgAACQCAALQAAQQNGDQAgASAAKQIANwMIIAFBCGoQh4OAgAAhACABQRBqJICAgIAAIAAPCyABQRBqJICAgIAAQQALFAAgACABQYyDwIAAQQEQ74CAgAALGAAgASgCACACIAMQmIWAgAAgAEEDOgAACxQAIAAgAUGbg8CAAEEBEO+AgIAACxQAIAAgAUGrg8CAAEEBEO+AgIAACw4AIABBFEEEEM6CgIAAC4ABAQF/I4CAgIAAQTBrIgIkgICAgAAgAiABNgIMIAIgADYCCCACIAJBCGpBgYCAgAAQk4WAgAAgAkEkakEBNgIAIAJCAjcCFCACQayIwIAANgIQIAIgAikDADcDKCACIAJBKGo2AiAgAkEQahClg4CAACEAIAJBMGokgICAgAAgAAsMACABIAEQ9YCAgAALEgAgAUHsk8CAAEEMEMiGgIAACwwAIAEgARD3gICAAAsSACABQeyTwIAAQQwQyIaAgAALDAAgASABEPmAgIAACxIAIAFB7JPAgABBDBDIhoCAAAsMACABIAEQ+4CAgAALEgAgAUHsk8CAAEEMEMiGgIAACwwAIAEgARD9gICAAAsSACABQeyTwIAAQQwQyIaAgAALDAAgACABEKyFgIAACwwAIAEgARCAgYCAAAsSACABQeyTwIAAQQwQyIaAgAALDwAgACgCACABEOeGgIAACw8AIAAoAgAgARCehoCAAAsdACAAKAIAIgAoAgAgASAAKAIEKAIgEYCAgIAAAAuqAQEBfyOAgICAAEHAAGsiAiSAgICAACACIAE2AgwgAkEANgIYIAJCATcDECACQTRqQQE2AgAgAkIBNwIkIAJBrIPAgAA2AiAgAkGCgICAADYCPCACIAJBOGo2AjAgAiACQQxqNgI4IAJBEGogAkEgahDqgoCAABCFgYCAACACQRBqEJCDgIAAIABBCGogAigCGDYCACAAIAIpAxA3AgAgAkHAAGokgICAgAALSAEBfyOAgICAAEEQayIBJICAgIAAAkAgAEUNAEG0g8CAAEE3IAFBCGpBuIXAgABBtITAgAAQqoaAgAAACyABQRBqJICAgIAAC6oBAQF/I4CAgIAAQcAAayICJICAgIAAIAIgATYCDCACQQA2AhggAkIBNwMQIAJBNGpBATYCACACQgE3AiQgAkGsg8CAADYCICACQYOAgIAANgI8IAIgAkE4ajYCMCACIAJBDGo2AjggAkEQaiACQSBqEOqCgIAAEIWBgIAAIAJBEGoQkIOAgAAgAEEIaiACKAIYNgIAIAAgAikDEDcCACACQcAAaiSAgICAAAsYAAJAIAAoAgBFDQAgAEEEahDYgICAAAsLFQACQCAAKAIARQ0AIAAQvoCAgAALCwIACzwBAn8gACgCACAAKAIEKAIAEYGAgIAAAAJAIAAoAgQiASgCBCICRQ0AIAAoAgAgAiABKAIIEM6CgIAACwsCAAsCAAtgAQN/AkAgAC0AAEECSQ0AIABBBGooAgAiASgCACABKAIEKAIAEYGAgIAAAAJAIAEoAgQiAigCBCIDRQ0AIAEoAgAgAyACKAIIEM6CgIAACyAAKAIEQQxBBBDOgoCAAAsLAgALAgALGAACQCAAKAIARQ0AIABBBGoQjYGAgAALCwIACxgAAkAgACgCAEUNACAAQQRqEI2BgIAACwsVAAJAIAAoAgBFDQAgABDYgICAAAsLIwAgAEEwahC+gICAACAAQQhqEL6AgIAAIABBIGoQvoCAgAALFQACQCAAKAIARQ0AIAAQvoCAgAALCxgAAkAgACgCAEUNACAAQQRqENiAgIAACwsYAAJAIAAoAgBFDQAgAEEEahDYgICAAAsLLgEBfyAAEPaEgIAAIABBBGohAQJAIAAoAgANACABEKmEgIAADwsgARCqhICAAAsCAAsCAAsCAAsCAAsVAAJAIAAoAgBFDQAgABC+gICAAAsLAgALAgALJQACQCAAIAEgAhDGg4CAACIARQ0AIABBACABEPKGgIAAGgsgAAs9AQF/AkAgACAEIAMQxoOAgAAiBUUNACAFIAEgBCACIAIgBEsbEPOGgIAAGiAAIAEgAiADEMeDgIAACyAFCxMAIAAgASACIAMQ54KAgABBAXMLPQEBfyOAgICAAEEQayIEJICAgIAAIARBCGpBACADIAEgAhCkgYCAACAAIAQpAwg3AgAgBEEQaiSAgICAAAtAAAJAAkAgAiABSQ0AIAQgAk8NASACIAQQj4aAgAAACyABIAIQkIaAgAAACyAAIAIgAWs2AgQgACADIAFqNgIACz0BAX8jgICAgABBEGsiBCSAgICAACAEQQhqIAMgAiABIAIQpIGAgAAgACAEKQMINwIAIARBEGokgICAgAALOQACQAJAIAEoAghFDQAgACABQcAAEPOGgIAAGgwBCyAAEKeBgIAAIAEoAghFDQAgARCUgYCAAA8LCzIBAX8jgICAgABBEGsiASSAgICAACABEM2EgIAAIAAgARDggYCAACABQRBqJICAgIAACyYBAX8gAS8AACECIAFBADsAACAAIAJBCHY6AAEgACACQQFxOgAACzMAAkAgASgCAA0AIAIgAyAEEKiGgIAAAAsgACABKQIANwIAIABBCGogAUEIaigCADYCAAspAAJAIAGnDQBB5JXAgABBFSAEEKiGgIAAAAsgACACNwMAIAAgAzcDCAtpAQF/I4CAgIAAQRBrIgUkgICAgAACQCABKAIAQQFHDQAgBSABKAIENgIMIAIgAyAFQQxqQdiFwIAAIAQQqoaAgAAACyAAIAEpAgQ3AgAgAEEIaiABQQxqKAIANgIAIAVBEGokgICAgAALRAEBfyOAgICAAEEQayICJICAgIAAIAJBCGogAUEIaigCADYCACACIAEpAgA3AwAgACACEPiEgIAAIAJBEGokgICAgAALQAEBfyOAgICAAEEQayIDJICAgIAAIAMgASkDADcDCCACIANBCGpBCBCYhYCAACAAQQM6AAAgA0EQaiSAgICAAAvwAQICfwJ+I4CAgIAAQTBrIgIkgICAgAACQAJAAkAgASgCBCIDQQhJDQAgAkEQaiABKAIAIANBCBCjgYCAACACQRhqIAIoAhAgAigCFBCXhYCAACACLQAYQQFGDQIgAikAGSEEIAJBCGogASgCACABKAIEQQgQpYGAgAAgAikDCCEFIABBCGogBDcDACAAQQA2AgAgASAFNwIADAELIAJBGGpBC0GkhsCAAEEaENmCgIAAIABBATYCACAAIAIpAxg3AgQLIAJBMGokgICAgAAPC0HohcCAAEErIAJBKGpBlIbAgABBmIfAgAAQqoaAgAAAC0QBAX8jgICAgABBEGsiAiSAgICAACACQQhqIAFBCGooAgA2AgAgAiABKQIANwMAIAAgAhD5hICAACACQRBqJICAgIAAC0gBAX8jgICAgABBEGsiAiSAgICAACACQQhqIAEQqIGAgAAgAi0ACCEBIAAgAi0ACToAASAAIAFBAXE6AAAgAkEQaiSAgICAAAs8AAJAIAAoAgggACgCBEcNACAAQQEQjYWAgAALIAAQm4WAgAAgACgCCGogAToAACAAIAAoAghBAWo2AggLUAEBfyOAgICAAEEQayIDJICAgIAAIAMgAhDqgICAACADIAEgAhCYhYCAACAAQQhqIANBCGooAgA2AgAgACADKQMANwIAIANBEGokgICAgAAL8QEBAX8jgICAgABBoAFrIgMkgICAgAAgAyACNgIMIAMgATYCCCADQdAAaiADQQhqELSBgIAAAkACQCADKAJQQQFHDQAgAyADKQJUNwMQIANBmAFqIANBEGoQwIWAgAAgACADKQOYATcCBCAAQQE2AgAMAQsgA0EQaiADQdAAakEIakHAABDzhoCAABoCQCADKAIMDQAgAEEIaiADQRBqQcAAEPOGgIAAGiAAQQA2AgAMAQsgA0HQAGpBDEGIiMCAAEESENmCgIAAIABBATYCACAAIAMpA1A3AgQgA0EQahCUgYCAAAsgA0GgAWokgICAgAALjAEBAX8jgICAgABB4ABrIgIkgICAgAAgAkEIaiABEIKCgIAAAkACQCACKAIIQQFHDQAgAiACKQIMNwNYIAJB0ABqIAJB2ABqEMCFgIAAIAAgAikDUDcCBCAAQQE2AgAMAQsgAEEIaiACQQhqQQhqQcAAEPOGgIAAGiAAQQA2AgALIAJB4ABqJICAgIAAC6kDAgF/An4jgICAgABB0ABrIgMkgICAgAACQAJAAkACQAJAIAJBEEkNACADQRBqIAEgAkEQEKOBgIAAIANBMGogAygCECADKAIUEL+EgIAAIAMtADBBAUYNAiADQTlqKQAAIQQgAykAMSEFIANBCGogASACQRAQpYGAgAAgA0E8aiAENwIAIAMgBTcCNEEAIQEgAygCDCECDAELIANBMGpBC0GkhsCAAEEaENmCgIAAQQEhAQsgA0EkaiADQThqKQMANwIAIANBLGogA0EwakEQaigCADYCACADIAE2AhggAyADKQMwNwIcAkAgAUUNACADIAMpAhw3AzAgA0HIAGogA0EwahDAhYCAACAAIAMpA0g3AgQgAEEBNgIADAMLIAINASADQRhqQRBqKQMAIQQgAykDICEFIABBADYCACAAQRBqIAQ3AwAgAEEIaiAFNwMADAILQeiFwIAAQSsgA0HIAGpBlIbAgABBmIfAgAAQqoaAgAAACyADQRhqQQxBiIjAgABBEhDZgoCAACAAQQE2AgAgACADKQMYNwIECyADQdAAaiSAgICAAAvCAQIBfwF+I4CAgIAAQTBrIgIkgICAgAAgAkEIakGACBDqgICAACACQShqIAEgAkEIahC3gYCAACACIAIpAygiAzcDGAJAAkAgA6dB/wFxQQNHDQAgAkEYahDlgICAACAAIAIpAwg3AgQgAEEMaiACQRBqKAIANgIAQQAhAQwBCyACIAM3AyggAkEgaiACQShqEMCFgIAAIAAgAikDIDcCBCACQQhqEL6AgIAAQQEhAQsgACABNgIAIAJBMGokgICAgAALhAECAX8BfiOAgICAAEEgayIDJICAgIAAIANBGGogASACEP+BgIAAIAMgAykDGCIENwMIAkACQCAEp0H/AXFBA0cNACADQQhqEOWAgIAAIABBAzoAAAwBCyADIAQ3AxggA0EQaiADQRhqEMCFgIAAIAAgAykDEDcCAAsgA0EgaiSAgICAAAuNAQEBfyOAgICAAEEwayICJICAgIAAIAJBCGpBgAgQ6oCAgAAgAiABQQhqKQMANwMoIAIgASkDADcDICACQQhqIAJBIGpBEBCYhYCAACACQgM3AxggAkEYahDlgICAACAAQQxqIAJBCGpBCGooAgA2AgAgACACKQMINwIEIABBADYCACACQTBqJICAgIAAC8IBAgF/AX4jgICAgABBMGsiAiSAgICAACACQQhqQYAIEOqAgIAAIAJBKGogASACQQhqELqBgIAAIAIgAikDKCIDNwMYAkACQCADp0H/AXFBA0cNACACQRhqEOWAgIAAIAAgAikDCDcCBCAAQQxqIAJBEGooAgA2AgBBACEBDAELIAIgAzcDKCACQSBqIAJBKGoQwIWAgAAgACACKQMgNwIEIAJBCGoQvoCAgABBASEBCyAAIAE2AgAgAkEwaiSAgICAAAt5AQF/I4CAgIAAQRBrIgMkgICAgAAgAyABKAIINgIEIAIgA0EEakEEEJiFgIAAIANCAzcDCCADQQhqEOWAgIAAIAIgARCbhYCAACABKAIIEJiFgIAAIANCAzcDCCADQQhqEOWAgIAAIABBAzoAACADQRBqJICAgIAAC7MBAgF/AX4jgICAgABB0ABrIgMkgICAgAAgAyACNgIkIAMgATYCICADIAA2AhwgA0EQaiADQRxqQYSAgIAAEJGFgIAAIAMpAxAhBCADQQhqIANBIGpBhYCAgAAQ6IKAgAAgA0E8akECNgIAIAMgBDcDQCADQgI3AiwgA0HYiMCAADYCKCADIAMpAwg3A0ggAyADQcAAajYCOCADQShqEKWDgIAAIQAgA0HQAGokgICAgAAgAAuAAQEBfyOAgICAAEEwayICJICAgIAAIAIgATYCDCACIAA2AgggAiACQQhqQYGAgIAAEJOFgIAAIAJBJGpBATYCACACQgI3AhQgAkH8iMCAADYCECACIAIpAwA3AyggAiACQShqNgIgIAJBEGoQpYOAgAAhACACQTBqJICAgIAAIAAL7wMBBH8jgICAgABBMGsiAiSAgICAACACQQhqIAEQzYCAgAACQAJAAkACQAJAIAItAAhBAUYNACACLQAJQQFHDQEgAi0ACiEDIAJBCGoQyICAgAACQCADQSJGDQAgASACQShqQbCQwIAAEOCAgIAAIQMgAkEBNgIIIAIgAzYCDAwECyABENCAgIAAIAFBDGoiAxDMgICAACACQRhqIAEgAxCpg4CAACACKAIcIQMCQAJAAkAgAigCGEEBRg0AIAJBGGpBDGooAgAhBCACQSBqKAIAIQUgAw0BIAJBCGogBSAEEJmDgIAADAILIABBATYCACAAIAM2AgQMBAsgAkEIaiAFIAQQmYOAgAALQQEhAyACKAIIQQFGDQMgACACKQIMNwIEIABBDGogAkEIakEMaigCADYCAEEAIQEMBAsgACACKAIMNgIEIABBATYCAAwBCyACQQU2AhggASACQRhqEMWAgIAAIQEgAEEBNgIAIAAgATYCBCACQQhqEMiAgIAACyACQTBqJICAgIAADwsgACACKAIMIAEQyoCAgAA2AgRBASEBQQAhAwsgACABNgIAAkACQCACKAIIDQAgAUUNASACQQhqQQRyEL6AgIAADAELIANFDQAgAkEIakEEchDYgICAAAsgAkEwaiSAgICAAAvvAQEBfyOAgICAAEEgayIEJICAgIAAIAQgACABIAIQv4GAgAAiATYCGAJAAkAgAQ0AIARBGGoQvICAgAAgBEEYaiAAKAIAEPGAgIAAIAQgBEEYahDtgICAACIBNgIUIAENASAEQRRqELyAgIAAIAAoAgAhASAEQQhqIAMQz4WAgAAgBCABIAQoAgggBCgCDBDAgYCAACIBNgIYIAENASAEQRhqELyAgIAAIARBAzoAGCAEIARBGGoQ7YCAgAAiATYCFCABDQEgBEEUahC8gICAAEEAIQEMAQsgARCNg4CAACEBCyAEQSBqJICAgIAAIAELyAEBAn8jgICAgABBEGsiAySAgICAAAJAIAAtAARBAUYNACAAKAIAKAIAQZyDwIAAQQEQmIWAgAALIANBAzoACCADIANBCGoQ7YCAgAAiBDYCBAJAIAQNACADQQRqELyAgIAAIABBAjoABCADIAAoAgAgASACEMCBgIAAIgQ2AgggBA0AIANBCGoQvICAgAAgA0EDOgAIIAMgA0EIahDtgICAACIENgIEIAQNACADQQRqELyAgIAAQQAhBAsgA0EQaiSAgICAACAEC1kBAX8jgICAgABBEGsiAySAgICAACADQQhqIAAgACABIAIQ5ICAgAAgAyADQQhqEO2AgIAAIgA2AgQCQCAADQAgA0EEahC8gICAAAsgA0EQaiSAgICAACAAC9YBAQF/I4CAgIAAQRBrIgQkgICAgAAgBCAAIAEgAhC/gYCAACIBNgIIAkACQCABDQAgBEEIahC8gICAACAEQQhqIAAoAgAQ8YCAgAAgBCAEQQhqEO2AgIAAIgE2AgQgAQ0BIARBBGoQvICAgAAgBCADIAAoAgAQwoGAgAAiATYCCCABDQEgBEEIahC8gICAACAEQQM6AAggBCAEQQhqEO2AgIAAIgE2AgQgAQ0BIARBBGoQvICAgABBACEBDAELIAEQjYOAgAAhAQsgBEEQaiSAgICAACABC9gBAQF/I4CAgIAAQcAAayICJICAgIAAIAIgADYCDCACQQA2AhggAkIBNwMQIAJBNGpBATYCACACQgE3AiQgAkGsg8CAADYCICACQYaAgIAANgI8IAIgAkE4ajYCMCACIAJBDGo2AjggAkEQaiACQSBqEOqCgIAAEIWBgIAAIAJBEGoQkIOAgAAgAkEoaiACKAIYNgIAIAIgAikDEDcDICACIAJBIGoQz4WAgAAgASACKAIAIAIoAgQQwIGAgAAhASACQSBqEL6AgIAAIAJBwABqJICAgIAAIAELIgAgASACEPOAgIAAEI2DgIAAIQEgAEEBNgIAIAAgATYCBAsiACABIAIQ84CAgAAQjYOAgAAhASAAQQE2AgAgACABNgIECz4BAn8jgICAgABBEGsiASSAgICAACABIAAQiYaAgAAgARCmg4CAACECIAAQvoCAgAAgAUEQaiSAgICAACACC10BAn8jgICAgABBEGsiAiSAgICAACACQQhqIAAQz4WAgAAgAigCDCEAIAIoAgghAyACIAEQz4WAgAAgAyAAIAIoAgAgAigCBBCBg4CAACEBIAJBEGokgICAgAAgAQuXBAEDfyOAgICAAEHQAGsiAiSAgICAAAJAAkACQAJAIAEoAgQiA0EESQ0AIAJBGGogASgCACADQQQQo4GAgAAgAkEwaiACKAIYIAIoAhwQw4WAgAAgAi0AMEEBRg0CIAIoADEhAyACQRBqIAEoAgAgASgCBEEEEKWBgIAAIAEgAikDEDcCACACIAOtNwJEIAJBADYCQCACQcAAahCSgYCAACADRQ0BAkAgASgCBCIEIANJDQAgAkEIaiABKAIAIAQgAxCjgYCAACACQSBqIAIoAgggAigCDBCygYCAACACIAEoAgAgASgCBCADEKWBgIAAIAEgAikDADcCACACQcAAakEIaiIBIAJBIGpBCGooAgA2AgAgAiACKQMgNwNAIAJBMGogAkHAAGoQm4WAgAAgASgCACACKAJEEMiFgIAAIABBDGogAkEwakEIaigCADYCACAAIAIpAzA3AgQgAEEANgIADAQLIAJBwABqQQtBpIbAgABBGhDZgoCAACAAQQE2AgAgACACKQNANwIEDAMLIAJBMGpBC0GkhsCAAEEaENmCgIAAIAIgAikDMDcDMCACQSBqIAJBMGoQwIWAgAAgACACKQMgNwIEIABBATYCAAwCCyAAQoCAgIAQNwIAIABBCGpCADcCAAwBC0HohcCAAEErIAJBIGpBlIbAgABBmIfAgAAQqoaAgAAACyACQdAAaiSAgICAAAvWBQEDfyOAgICAAEEgayICJICAgIAAIAJBCGogASgCABDNgICAAAJAAkACQAJAAkACQCACLQAIQQFGDQACQCACLQAJQQFGDQAgASgCACEBIAJBAzYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAULAkACQAJAAkAgAi0ACiIDQSxGDQAgA0H9AEcNASAAQYAEOwEADAgLIAEtAAQNASABKAIAENCAgIAAIAJBEGogASgCABDNgICAACACLQAQQQFGDQQgAi0AEiEDIAItABEhBCACQRBqEMiAgIAAIAJBCGoQyICAgAAgBA0CIAEoAgAhASACQQU2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwICyABLQAEDQAgASgCACEBIAJBCDYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAYLIAFBADoABCACQQhqEMiAgIAACyADQf8BcSIDQSJGDQMgA0H9AEcNAiABKAIAIQEgAkESNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMBQsgAEEBOgAAIABBBGogAigCDDYCAAwECyAAQQE6AAAgAEEEaiACKAIUNgIADAILIAEoAgAhASACQRA2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwCCyABKAIAIgEQ0ICAgAAgAUEMaiIDEMyAgIAAIAJBEGogASADEKmDgIAAAkACQAJAAkAgAigCEEEBRw0AIAIgAigCFCIBNgIMIAJBAToACAwBCyACQQhqIAJBGGooAgAgAkEQakEMaigCABDJgYCAACACLQAIQQFHDQEgAigCDCEBCyAAQQRqIAE2AgBBASEBDAELIAAgAi0ACToAAUEAIQELIAAgAToAAAwBCyACQQhqEMiAgIAACyACQSBqJICAgIAACycAIAEgAkHmk8CAAEEGEIGDgIAAIQEgAEEAOgAAIAAgAUEBczoAAQvWBQEDfyOAgICAAEEgayICJICAgIAAIAJBCGogASgCABDNgICAAAJAAkACQAJAAkACQCACLQAIQQFGDQACQCACLQAJQQFGDQAgASgCACEBIAJBAzYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAULAkACQAJAAkAgAi0ACiIDQSxGDQAgA0H9AEcNASAAQYAGOwEADAgLIAEtAAQNASABKAIAENCAgIAAIAJBEGogASgCABDNgICAACACLQAQQQFGDQQgAi0AEiEDIAItABEhBCACQRBqEMiAgIAAIAJBCGoQyICAgAAgBA0CIAEoAgAhASACQQU2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwICyABLQAEDQAgASgCACEBIAJBCDYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAYLIAFBADoABCACQQhqEMiAgIAACyADQf8BcSIDQSJGDQMgA0H9AEcNAiABKAIAIQEgAkESNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMBQsgAEEBOgAAIABBBGogAigCDDYCAAwECyAAQQE6AAAgAEEEaiACKAIUNgIADAILIAEoAgAhASACQRA2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwCCyABKAIAIgEQ0ICAgAAgAUEMaiIDEMyAgIAAIAJBEGogASADEKmDgIAAAkACQAJAAkAgAigCEEEBRw0AIAIgAigCFCIBNgIMIAJBAToACAwBCyACQQhqIAJBGGooAgAgAkEQakEMaigCABDLgYCAACACLQAIQQFHDQEgAigCDCEBCyAAQQRqIAE2AgBBASEBDAELIAAgAi0ACToAAUEAIQELIAAgAToAAAwBCyACQQhqEMiAgIAACyACQSBqJICAgIAAC1UAAkACQCABIAJB3JvAgABBFhCBg4CAAEUNACAAQQA6AAEMAQsCQCABIAJB5pPAgABBBhCBg4CAAEUNACAAQQE6AAEMAQsgAEECOgABCyAAQQA6AAAL1gUBA38jgICAgABBIGsiAiSAgICAACACQQhqIAEoAgAQzYCAgAACQAJAAkACQAJAAkAgAi0ACEEBRg0AAkAgAi0ACUEBRg0AIAEoAgAhASACQQM2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwFCwJAAkACQAJAIAItAAoiA0EsRg0AIANB/QBHDQEgAEGABDsBAAwICyABLQAEDQEgASgCABDQgICAACACQRBqIAEoAgAQzYCAgAAgAi0AEEEBRg0EIAItABIhAyACLQARIQQgAkEQahDIgICAACACQQhqEMiAgIAAIAQNAiABKAIAIQEgAkEFNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMCAsgAS0ABA0AIAEoAgAhASACQQg2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwGCyABQQA6AAQgAkEIahDIgICAAAsgA0H/AXEiA0EiRg0DIANB/QBHDQIgASgCACEBIAJBEjYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAULIABBAToAACAAQQRqIAIoAgw2AgAMBAsgAEEBOgAAIABBBGogAigCFDYCAAwCCyABKAIAIQEgAkEQNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMAgsgASgCACIBENCAgIAAIAFBDGoiAxDMgICAACACQRBqIAEgAxCpg4CAAAJAAkACQAJAIAIoAhBBAUcNACACIAIoAhQiATYCDCACQQE6AAgMAQsgAkEIaiACQRhqKAIAIAJBEGpBDGooAgAQzYGAgAAgAi0ACEEBRw0BIAIoAgwhAQsgAEEEaiABNgIAQQEhAQwBCyAAIAItAAk6AAFBACEBCyAAIAE6AAAMAQsgAkEIahDIgICAAAsgAkEgaiSAgICAAAsnACABIAJB35rAgABBChCBg4CAACEBIABBADoAACAAIAFBAXM6AAEL1gUBA38jgICAgABBIGsiAiSAgICAACACQQhqIAEoAgAQzYCAgAACQAJAAkACQAJAAkAgAi0ACEEBRg0AAkAgAi0ACUEBRg0AIAEoAgAhASACQQM2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwFCwJAAkACQAJAIAItAAoiA0EsRg0AIANB/QBHDQEgAEGABDsBAAwICyABLQAEDQEgASgCABDQgICAACACQRBqIAEoAgAQzYCAgAAgAi0AEEEBRg0EIAItABIhAyACLQARIQQgAkEQahDIgICAACACQQhqEMiAgIAAIAQNAiABKAIAIQEgAkEFNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMCAsgAS0ABA0AIAEoAgAhASACQQg2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwGCyABQQA6AAQgAkEIahDIgICAAAsgA0H/AXEiA0EiRg0DIANB/QBHDQIgASgCACEBIAJBEjYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAULIABBAToAACAAQQRqIAIoAgw2AgAMBAsgAEEBOgAAIABBBGogAigCFDYCAAwCCyABKAIAIQEgAkEQNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMAgsgASgCACIBENCAgIAAIAFBDGoiAxDMgICAACACQRBqIAEgAxCpg4CAAAJAAkACQAJAIAIoAhBBAUcNACACIAIoAhQiATYCDCACQQE6AAgMAQsgAkEIaiACQRhqKAIAIAJBEGpBDGooAgAQz4GAgAAgAi0ACEEBRw0BIAIoAgwhAQsgAEEEaiABNgIAQQEhAQwBCyAAIAItAAk6AAFBACEBCyAAIAE6AAAMAQsgAkEIahDIgICAAAsgAkEgaiSAgICAAAsnACABIAJB3JPAgABBChCBg4CAACEBIABBADoAACAAIAFBAXM6AAEL1gUBA38jgICAgABBIGsiAiSAgICAACACQQhqIAEoAgAQzYCAgAACQAJAAkACQAJAAkAgAi0ACEEBRg0AAkAgAi0ACUEBRg0AIAEoAgAhASACQQM2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwFCwJAAkACQAJAIAItAAoiA0EsRg0AIANB/QBHDQEgAEGABjsBAAwICyABLQAEDQEgASgCABDQgICAACACQRBqIAEoAgAQzYCAgAAgAi0AEEEBRg0EIAItABIhAyACLQARIQQgAkEQahDIgICAACACQQhqEMiAgIAAIAQNAiABKAIAIQEgAkEFNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMCAsgAS0ABA0AIAEoAgAhASACQQg2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwGCyABQQA6AAQgAkEIahDIgICAAAsgA0H/AXEiA0EiRg0DIANB/QBHDQIgASgCACEBIAJBEjYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAULIABBAToAACAAQQRqIAIoAgw2AgAMBAsgAEEBOgAAIABBBGogAigCFDYCAAwCCyABKAIAIQEgAkEQNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMAgsgASgCACIBENCAgIAAIAFBDGoiAxDMgICAACACQRBqIAEgAxCpg4CAAAJAAkACQAJAIAIoAhBBAUcNACACIAIoAhQiATYCDCACQQE6AAgMAQsgAkEIaiACQRhqKAIAIAJBEGpBDGooAgAQ0YGAgAAgAi0ACEEBRw0BIAIoAgwhAQsgAEEEaiABNgIAQQEhAQwBCyAAIAItAAk6AAFBACEBCyAAIAE6AAAMAQsgAkEIahDIgICAAAsgAkEgaiSAgICAAAtVAAJAAkAgASACQZybwIAAQQ4QgYOAgABFDQAgAEEAOgABDAELAkAgASACQaqbwIAAQQ4QgYOAgABFDQAgAEEBOgABDAELIABBAjoAAQsgAEEAOgAAC9YFAQN/I4CAgIAAQSBrIgIkgICAgAAgAkEIaiABKAIAEM2AgIAAAkACQAJAAkACQAJAIAItAAhBAUYNAAJAIAItAAlBAUYNACABKAIAIQEgAkEDNgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMBQsCQAJAAkACQCACLQAKIgNBLEYNACADQf0ARw0BIABBgAY7AQAMCAsgAS0ABA0BIAEoAgAQ0ICAgAAgAkEQaiABKAIAEM2AgIAAIAItABBBAUYNBCACLQASIQMgAi0AESEEIAJBEGoQyICAgAAgAkEIahDIgICAACAEDQIgASgCACEBIAJBBTYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAgLIAEtAAQNACABKAIAIQEgAkEINgIQIAEgAkEQahDFgICAACEBIABBAToAACAAQQRqIAE2AgAMBgsgAUEAOgAEIAJBCGoQyICAgAALIANB/wFxIgNBIkYNAyADQf0ARw0CIAEoAgAhASACQRI2AhAgASACQRBqEMWAgIAAIQEgAEEBOgAAIABBBGogATYCAAwFCyAAQQE6AAAgAEEEaiACKAIMNgIADAQLIABBAToAACAAQQRqIAIoAhQ2AgAMAgsgASgCACEBIAJBEDYCECABIAJBEGoQxYCAgAAhASAAQQE6AAAgAEEEaiABNgIADAILIAEoAgAiARDQgICAACABQQxqIgMQzICAgAAgAkEQaiABIAMQqYOAgAACQAJAAkACQCACKAIQQQFHDQAgAiACKAIUIgE2AgwgAkEBOgAIDAELIAJBCGogAkEYaigCACACQRBqQQxqKAIAENOBgIAAIAItAAhBAUcNASACKAIMIQELIABBBGogATYCAEEBIQEMAQsgACACLQAJOgABQQAhAQsgACABOgAADAELIAJBCGoQyICAgAALIAJBIGokgICAgAALVQACQAJAIAEgAkGcm8CAAEEOEIGDgIAARQ0AIABBADoAAQwBCwJAIAEgAkGqm8CAAEEOEIGDgIAARQ0AIABBAToAAQwBCyAAQQI6AAELIABBADoAAAtkAQJ/I4CAgIAAQRBrIgIkgICAgAAgAiABKAIAEOGAgIAAIgM2AgwCQAJAIAMNACACQQxqELyAgIAAIAAgASgCABC9gYCAAAwBCyAAQQE2AgAgACADNgIECyACQRBqJICAgIAAC2QBAn8jgICAgABBEGsiAiSAgICAACACIAEoAgAQ4YCAgAAiAzYCDAJAAkAgAw0AIAJBDGoQvICAgAAgACABKAIAENaBgIAADAELIABBATYCACAAIAM2AgQLIAJBEGokgICAgAALmAICAn8BfiOAgICAAEHAAGsiAiSAgICAACACQRhqIAEQvYGAgABBASEBAkACQCACKAIYQQFHDQAgACACKAIcEI2DgIAANgIEDAELIAJBCGpBCGogAkEYakEEciIBQQhqKAIAIgM2AgAgAiABKQIAIgQ3AwggAkEwakEIaiADNgIAIAIgBDcDMCACQRhqIAJBMGoQr4GAgAACQCACKAIYQQFHDQAgAiACKQIcNwMoIAJBMGogAkEoahCEgYCAACACQTBqEMWBgIAAIQEgAkEoahCKgYCAACAAIAE2AgRBASEBDAELIAAgAikCHDcCBCAAQQxqIAJBGGpBDGooAgA2AgBBACEBCyAAIAE2AgAgAkHAAGokgICAgAALZAECfyOAgICAAEEQayICJICAgIAAIAIgASgCABDhgICAACIDNgIMAkACQCADDQAgAkEMahC8gICAACAAIAEoAgAQ2IGAgAAMAQsgAEEBNgIAIAAgAzYCBAsgAkEQaiSAgICAAAuFAwMBfwJ+AX8jgICAgABB4ABrIgIkgICAgAAgAkEwaiABEL2BgIAAAkACQCACKAIwQQFHDQAgAigCNBCNg4CAACEBIABBATYCACAAIAE2AgQMAQsgAkEIakEIaiACQTBqQQRyIgFBCGooAgA2AgAgAiABKQIANwMIIAIgAkEIahDPhYCAACACQTBqIAIoAgAgAigCBEEKENyGgIAAAkACQCACLQAwQQFGDQAgAkHAAGopAwAhAyACQTBqQQhqKQMAIQRBACEBDAELIAIgAi0AMToATyACQdAAaiACQc8AahCGgYCAACACQdAAahDFgYCAACEFQQEhAQsgAkEYakEQaiADNwMAIAIgBDcDICACIAU2AhwgAiABNgIYAkAgAUUNACAFEI2DgIAAIQEgAEEBNgIAIAAgATYCBCACQQhqEL6AgIAADAELIABBADYCACAAQRBqIAM3AwAgAEEIaiAENwMAIAJBCGoQvoCAgAAgAkEYahCHgYCAAAsgAkHgAGokgICAgAALUwECfyOAgICAAEEQayIBJICAgIAAIAEgACgCABDhgICAACICNgIMAkAgAg0AIAFBDGoQvICAgAAgACgCABDagYCAACECCyABQRBqJICAgIAAIAILQwEBfyOAgICAAEEQayIBJICAgIAAIAEgABDLgICAACIANgIMAkAgAA0AIAFBDGoQvICAgAALIAFBEGokgICAgAAgAAu+BAEDfyOAgICAAEEgayICJICAgIAAIAIgASgCABDNgICAAAJAAkACQAJAAkAgAi0AAEEBRg0AAkAgAi0AAUEBRg0AIAEoAgAhASACQQI2AgggASACQQhqEMWAgIAAIQEgAEEBNgIAIAAgATYCBAwECwJAAkACQAJAIAItAAIiA0EsRg0AIANB3QBHDQEgAEEANgIAIABBCGpCADcDAAwHCyABLQAEDQEgASgCABDQgICAACACQQhqIAEoAgAQzYCAgAAgAi0ACEEBRg0EIAItAAohAyACLQAJIQQgAkEIahDIgICAACACEMiAgIAAIAQNAiABKAIAIQEgAkEFNgIIIAEgAkEIahDFgICAACEBIABBATYCACAAIAE2AgQMBwsgAS0ABA0AIAEoAgAhASACQQc2AgggASACQQhqEMWAgIAAIQEgAEEBNgIAIAAgATYCBAwFCyABQQA6AAQgAhDIgICAAAsCQCADQf8BcUHdAEcNACABKAIAIQEgAkESNgIIIAEgAkEIahDFgICAACEBIABBATYCACAAIAE2AgQMBQsgAkEIaiABKAIAENiBgIAAIAIoAghBAUcNAiAAIAIoAgw2AgQgAEEBNgIADAQLIAAgAigCBDYCBCAAQQE2AgAMAwsgACACKAIMNgIEIABBATYCAAwBCyAAQQA2AgAgAEEQaiACKQMQNwMAIABBCGpCATcDACAAQRhqIAJBCGpBEGopAwA3AwAMAQsgAhDIgICAAAsgAkEgaiSAgICAAAuzBAEDfyOAgICAAEEgayICJICAgIAAIAJBCGogASgCABDNgICAAAJAAkACQAJAAkAgAi0ACEEBRg0AAkAgAi0ACUEBRg0AIAEoAgAhASACQQI2AhAgASACQRBqEMWAgIAAIQEgAEEBNgIAIAAgATYCBAwECwJAAkACQAJAIAItAAoiA0EsRg0AIANB3QBHDQEgAEIANwIADAcLIAEtAAQNASABKAIAENCAgIAAIAJBEGogASgCABDNgICAACACLQAQQQFGDQQgAi0AEiEDIAItABEhBCACQRBqEMiAgIAAIAJBCGoQyICAgAAgBA0CIAEoAgAhASACQQU2AhAgASACQRBqEMWAgIAAIQEgAEEBNgIAIAAgATYCBAwHCyABLQAEDQAgASgCACEBIAJBBzYCECABIAJBEGoQxYCAgAAhASAAQQE2AgAgACABNgIEDAULIAFBADoABCACQQhqEMiAgIAACyADQf8BcUHdAEcNAiABKAIAIQEgAkESNgIQIAEgAkEQahDFgICAACEBIABBATYCACAAIAE2AgQMBAsgACACKAIMNgIEIABBATYCAAwDCyAAIAIoAhQ2AgQgAEEBNgIADAELIAJBEGogASgCABDWgYCAAAJAIAIoAhBBAUcNACAAIAIoAhQ2AgQgAEEBNgIADAILIAAgAikCFDcCBCAAQQA2AgAgAEEMaiACQRBqQQxqKAIANgIADAELIAJBCGoQyICAgAALIAJBIGokgICAgAALswQBA38jgICAgABBIGsiAiSAgICAACACQQhqIAEoAgAQzYCAgAACQAJAAkACQAJAIAItAAhBAUYNAAJAIAItAAlBAUYNACABKAIAIQEgAkECNgIQIAEgAkEQahDFgICAACEBIABBATYCACAAIAE2AgQMBAsCQAJAAkACQCACLQAKIgNBLEYNACADQd0ARw0BIABCADcCAAwHCyABLQAEDQEgASgCABDQgICAACACQRBqIAEoAgAQzYCAgAAgAi0AEEEBRg0EIAItABIhAyACLQARIQQgAkEQahDIgICAACACQQhqEMiAgIAAIAQNAiABKAIAIQEgAkEFNgIQIAEgAkEQahDFgICAACEBIABBATYCACAAIAE2AgQMBwsgAS0ABA0AIAEoAgAhASACQQc2AhAgASACQRBqEMWAgIAAIQEgAEEBNgIAIAAgATYCBAwFCyABQQA6AAQgAkEIahDIgICAAAsgA0H/AXFB3QBHDQIgASgCACEBIAJBEjYCECABIAJBEGoQxYCAgAAhASAAQQE2AgAgACABNgIEDAQLIAAgAigCDDYCBCAAQQE2AgAMAwsgACACKAIUNgIEIABBATYCAAwBCyACQRBqIAEoAgAQvYGAgAACQCACKAIQQQFHDQAgACACKAIUNgIEIABBATYCAAwCCyAAIAIpAhQ3AgQgAEEANgIAIABBDGogAkEQakEMaigCADYCAAwBCyACQQhqEMiAgIAACyACQSBqJICAgIAACxQAIABB5pPAgABBBiABEMGBgIAAC2MBAX8jgICAgABBEGsiAiSAgICAAAJAAkAgAUH/AXFFDQAgAkEIaiAAEO6AgIAAIAIgAkEIahDtgICAACIBNgIEIAENASACQQRqELyAgIAAC0EAIQELIAJBEGokgICAgAAgAQv4AgEDfyOAgICAAEEwayICJICAgIAAAkACQAJAIAEoAggiA0EBaiIEIANJDQAgAiAEEOqAgIAAIAIgARDygYCAACACQekAELGBgIAAIAEoAggiA0EBaiIEIANJDQEgAkEQaiAEEOqAgIAAIAJBEGogARDygYCAACACQRBqQesAELGBgIAAIAEoAggiA0EBaiIEIANJDQIgAkEgaiAEEOqAgIAAIAJBIGogARDygYCAACACQSBqQfYAELGBgIAAIABCADcDACAAQgA3AxggAEE4aiACQQhqKAIANgIAIAAgAikDADcCMCAAIAIpAxA3AgggAEEQaiACQRBqQQhqKAIANgIAIABBIGogAikDIDcCACAAQShqIAJBIGpBCGooAgA2AgAgARC+gICAACACQTBqJICAgIAADwtBsILAgABBHEHIjMCAABCNhoCAAAALQbCCwIAAQRxB2IzAgAAQjYaAgAAAC0GwgsCAAEEcQeiMwIAAEI2GgIAAAAvcAgEBfyOAgICAAEHQAGsiBiSAgICAACAGQSBqIAEgAiADEOKBgIAAIAZBGGogBkEgahDPhYCAACAGQTBqIAYoAhggBigCHBDphICAAAJAAkAgBigCMA0AIAYgARDjgYCAADcDQCAGQQhqIAZBIGoQz4WAgAAgBigCCCAGKAIMIAZBwABqQQgQ6ISAgAAaIAEgAiADEOSBgIAAIAFBGGogBCAFEOWBgIAAIABBADYCAEEBIQEMAQsgBkHAAGpBCGogBkEwakEIaigCADYCACAGIAYpAzA3A0AgBkEQaiAGQcAAahDPhYCAACAAIAFBGGogBigCECAGKAIUEOaBgIAAIAQgBRDngYCAACAGQcAAahC+gICAAEEAIQELIAZBIGoQvoCAgAACQAJAIAYoAjBFDQAgAUUNASAGQTBqEL6AgIAADAELIAZBMGoQiIGAgAALIAZB0ABqJICAgIAAC6sBAQN/I4CAgIAAQSBrIgQkgICAgAACQCABQThqKAIAIgUgA2oiBiAFSQ0AIARBEGogBhDqgICAACAEQQhqIAFBMGoQz4WAgAAgBEEQaiAEKAIIIAQoAgwQmIWAgAAgBEEQaiACIAMQmIWAgAAgAEEIaiAEQRBqQQhqKAIANgIAIAAgBCkDEDcCACAEQSBqJICAgIAADwtBsILAgABBHEG4jMCAABCNhoCAAAALKQEBfgJAIAApAwAiASAAKQMYUg0AIAEPC0HsisCAAEHmABDPhICAAAALlQECAX8CfiOAgICAAEEgayIDJICAgIAAIANBEGogACAAKQMAEPeBgIAAAkAgACkDACIEQgF8IgUgBFQNACAAIAU3AwAgA0EIaiADQRBqEM+FgIAAIAMoAgggAygCDCABIAIQ6ISAgAAaIANBEGoQvoCAgAAgA0EgaiSAgICAAA8LQbCCwIAAQRxBzI7AgAAQjYaAgAAAC5UBAgF/An4jgICAgABBIGsiAySAgICAACADQRBqIAAgACkDABD2gYCAAAJAIAApAwAiBEIBfCIFIARUDQAgACAFNwMAIANBCGogA0EQahDPhYCAACADKAIIIAMoAgwgASACEOiEgIAAGiADQRBqEL6AgIAAIANBIGokgICAgAAPC0GwgsCAAEEcQcyOwIAAEI2GgIAAAAtDAgF/AX4jgICAgABBEGsiAiSAgICAACACQgA3AwggAkEIakEIIAAgARCOhYCAACACKQMIIQMgAkEQaiSAgICAACADC9QBAQF/I4CAgIAAQTBrIgUkgICAgAACQAJAAkAgASkDACACWA0AIAVBEGogASACEPaBgIAAIAVBCGogBUEQahDPhYCAACAFKAIIIAUoAgwgAyAEEOiEgIAARQ0BIAVBIGoQ64SAgAAgBSgCIEUNAiAAIAUpAyA3AgAgAEEIaiAFQSBqQQhqKAIANgIAIAVBEGoQvoCAgAAgBUEwaiSAgICAAA8LQfiMwIAAQRMQz4SAgAAAC0HsisCAAEHmABDPhICAAAALQeyKwIAAQeYAEM+EgIAAAAujBQIBfwJ+I4CAgIAAQZABayIEJICAgIAAIARBwABqIAEgAiADEOKBgIAAIARBOGogBEHAAGoQz4WAgAAgBEHQAGogBCgCOCAEKAI8EOmEgIAAAkACQAJAIAQoAlANACAAQQA2AgAgBEHAAGoQvoCAgAAMAQsgBEHgAGpBCGogBEHQAGpBCGooAgA2AgAgBCAEKQNQNwNgAkACQAJAIAEQ44GAgABCAVENAAJAIAEQ44GAgAAiBUJ/fCIGIAVWDQAgBEGAAWogASAGEOmBgIAAIAQoAoABRQ0CIARB8ABqQQhqIARBgAFqQQhqKAIANgIAIAQgBCkDgAE3A3AgBEEwaiAEQcAAahDPhYCAACAEKAIwIAQoAjQQ6oSAgAAaIARBKGogBEHwAGoQz4WAgAACQCAEKAIoIAQoAiwgAiADEKKBgIAARQ0AIARBIGogBEHwAGoQz4WAgAAgBEGAAWogASAEKAIgIAQoAiQQ4oGAgAAgBEEYaiAEQYABahDPhYCAACAEKAIcIQIgBCgCGCEDIARBEGogBEHgAGoQz4WAgAAgAyACIAQoAhAgBCgCFBDohICAABogBEGAAWoQvoCAgAALIARB8ABqEL6AgIAADAMLQcCBwIAAQSFB3IrAgAAQjYaAgAAACyAEQQhqIARBwABqEM+FgIAAIAQoAgggBCgCDBDqhICAABoMAQtB7IrAgABB5gAQz4SAgAAACyAEIARB4ABqEM+FgIAAIARBgAFqIAEgBCgCACAEKAIEEOaBgIAAIgUQ6oGAgAAgBEGAAWoQvoCAgAAgACABQRhqIAUQ64GAgAAgBEHgAGoQvoCAgAAgBCgCUCEBIARBwABqEL6AgIAAIAENAQsgBEHQAGoQiIGAgAALIARBkAFqJICAgIAAC68BAQF/I4CAgIAAQTBrIgMkgICAgAACQAJAAkAgASkDACACWA0AIANBEGogASACEPeBgIAAIANBCGogA0EQahDPhYCAACADQSBqIAMoAgggAygCDBDphICAACADKAIgDQFB7IrAgABB5gAQz4SAgAAACyAAQQA2AgAMAQsgACADKQMgNwIAIABBCGogA0EgakEIaigCADYCACADQRBqEL6AgIAACyADQTBqJICAgIAAC4EDAwF/AX4BfyOAgICAAEHAAGsiAySAgICAAAJAIAEpAwAiBCACWA0AAkACQAJAAkACQCACQgF8IARRDQAgA0EQaiABIAIQ94GAgAAgA0EwaiABEPiBgIAAIANBIGogA0EwakHyjcCAAEEpQZyOwIAAEKmBgIAAIANBCGogA0EQahDPhYCAACADKAIMIQEgAygCCCEFIAMgA0EgahDPhYCAACAFIAEgAygCACADKAIEEOiEgIAADQFB7IrAgABB5gAQz4SAgAAACyADQTBqIAEQ+IGAgAAgAygCMA0BQeyKwIAAQeYAEM+EgIAAAAsgA0EwahDrhICAACADKAIwDQFB7IrAgABB5gAQz4SAgAAACyAAIAMpAzA3AgAgAEEIaiADQTBqQQhqKAIANgIADAELIAAgAykDMDcCACAAQQhqIANBMGpBCGooAgA2AgAgA0EgahC+gICAACADQRBqEL6AgIAACyADQcAAaiSAgICAAA8LQfiMwIAAQRMQz4SAgAAAC4EDAwF/AX4BfyOAgICAAEHAAGsiAySAgICAAAJAIAEpAwAiBCACWA0AAkACQAJAAkACQCACQgF8IARRDQAgA0EQaiABIAIQ9oGAgAAgA0EwaiABEPmBgIAAIANBIGogA0EwakHyjcCAAEEpQZyOwIAAEKmBgIAAIANBCGogA0EQahDPhYCAACADKAIMIQEgAygCCCEFIAMgA0EgahDPhYCAACAFIAEgAygCACADKAIEEOiEgIAADQFB7IrAgABB5gAQz4SAgAAACyADQTBqIAEQ+YGAgAAgAygCMA0BQeyKwIAAQeYAEM+EgIAAAAsgA0EwahDrhICAACADKAIwDQFB7IrAgABB5gAQz4SAgAAACyAAIAMpAzA3AgAgAEEIaiADQTBqQQhqKAIANgIADAELIAAgAykDMDcCACAAQQhqIANBMGpBCGooAgA2AgAgA0EgahC+gICAACADQRBqEL6AgIAACyADQcAAaiSAgICAAA8LQfiMwIAAQRMQz4SAgAAAC+kBAgF/An4jgICAgABBwABrIgQkgICAgAAgBEEQaiABIAIgAxDigYCAACAEQQhqIARBEGoQz4WAgAAgBEEgaiAEKAIIIAQoAgwQ6YSAgAACQAJAAkAgBCgCIA0AQgAhBQwBCyAEQTBqQQhqIARBIGpBCGooAgA2AgAgBCAEKQMgNwMwIAQgBEEwahDPhYCAACAEKAIAIAQoAgQQ5oGAgAAhBiAEQTBqEL6AgIAAQgEhBSAEKAIgDQELIARBIGoQiIGAgAALIARBEGoQvoCAgAAgACAGNwMIIAAgBTcDACAEQcAAaiSAgICAAAtgAQF/I4CAgIAAQRBrIgIkgICAgAAgAiABELmBgIAAAkAgAigCAEEBRw0AQdKLwIAAQR8Qz4SAgAAACyAAIAIpAgQ3AgAgAEEIaiACQQxqKAIANgIAIAJBEGokgICAgAALYAEBfyOAgICAAEEQayICJICAgIAAIAIgARC4gYCAAAJAIAIoAgBBAUcNAEHxi8CAAEEhEM+EgIAAAAsgACACKQIENwIAIABBCGogAkEMaigCADYCACACQRBqJICAgIAAC2gCAX8BfiOAgICAAEEgayIDJICAgIAAIANBCGogASACELWBgIAAAkAgAygCCEEBRw0AQZKMwIAAQSMQz4SAgAAACyADKQMQIQQgACADQRhqKQMANwMIIAAgBDcDACADQSBqJICAgIAAC4gCAgF/A34jgICAgABB0ABrIgMkgICAgAAgA0EwaiACEO2BgIAAIANBGGogA0EwahDPhYCAACADQSBqIAEgAygCGCADKAIcEPGBgIAAAkACQAJAIAMoAiANAEIAIQQMAQsgA0HAAGpBCGogA0EgakEIaigCADYCACADIAMpAyA3A0AgA0EQaiADQcAAahDPhYCAACADIAMoAhAgAygCFBDvgYCAACADQQhqKQMAIQUgAykDACEGIANBwABqEL6AgIAAQgEhBCADKAIgDQELIANBIGoQiIGAgAALIANBMGoQvoCAgAAgAEEQaiAFNwMAIAAgBjcDCCAAIAQ3AwAgA0HQAGokgICAgAALkwEBAX8jgICAgABBIGsiBCSAgICAACAEIAEgAiADEOyBgIAAAkACQAJAIAQpAwCnDQAgAEEANgIADAELIARBEGogAUEYaiAEKQMIEPWBgIAAIAQoAhBFDQEgACAEKQMQNwIAIABBCGogBEEQakEIaigCADYCAAsgBEEgaiSAgICAAA8LQeyKwIAAQeYAEM+EgIAAAAs/AQF/I4CAgIAAQRBrIgIkgICAgAAgAkEIaiABEP6BgIAAIAAgAigCCCACKAIMEIyFgIAAIAJBEGokgICAgAALzgICAn8DfiOAgICAAEHwAGsiBCSAgICAACAEQcAAaiACEO2BgIAAIARBKGogBEHAAGoQz4WAgAAgBCgCLCECIAQoAighBSAEQdAAaiADEO6BgIAAIARBIGogBEHQAGoQz4WAgAAgBEEwaiABIAUgAiAEKAIgIAQoAiQQ4YGAgAACQAJAAkAgBCgCMA0AQgAhBgwBCyAEQeAAakEIaiAEQTBqQQhqKAIANgIAIAQgBCkDMDcDYCAEQRhqIARB4ABqEM+FgIAAIARBCGogBCgCGCAEKAIcEO+BgIAAIARBCGpBCGopAwAhByAEKQMIIQggBEHgAGoQvoCAgABCASEGIAQoAjANAQsgBEEwahCIgYCAAAsgBEHQAGoQvoCAgAAgBEHAAGoQvoCAgAAgAEEQaiAHNwMAIAAgCDcDCCAAIAY3AwAgBEHwAGokgICAgAALiAICAX8DfiOAgICAAEHQAGsiAySAgICAACADQTBqIAIQ7YGAgAAgA0EYaiADQTBqEM+FgIAAIANBIGogASADKAIYIAMoAhwQ6IGAgAACQAJAAkAgAygCIA0AQgAhBAwBCyADQcAAakEIaiADQSBqQQhqKAIANgIAIAMgAykDIDcDQCADQRBqIANBwABqEM+FgIAAIAMgAygCECADKAIUEO+BgIAAIANBCGopAwAhBSADKQMAIQYgA0HAAGoQvoCAgABCASEEIAMoAiANAQsgA0EgahCIgYCAAAsgA0EwahC+gICAACAAQRBqIAU3AwAgACAGNwMIIAAgBDcDACADQdAAaiSAgICAAAuvAQEBfyOAgICAAEEwayIDJICAgIAAAkACQAJAIAEpAwAgAlgNACADQRBqIAEgAhD2gYCAACADQQhqIANBEGoQz4WAgAAgA0EgaiADKAIIIAMoAgwQ6YSAgAAgAygCIA0BQeyKwIAAQeYAEM+EgIAAAAsgAEEANgIADAELIAAgAykDIDcCACAAQQhqIANBIGpBCGooAgA2AgAgA0EQahC+gICAAAsgA0EwaiSAgICAAAuyAQEDfyOAgICAAEEgayIDJICAgIAAAkAgAUEQaigCACIEQQhqIgUgBEkNACADQQhqIAUQ6oCAgAAgAyABQQhqEM+FgIAAIANBCGogAygCACADKAIEEJiFgIAAIAMgAjcDGCADQQhqIANBGGpBCBCYhYCAACAAQQhqIANBCGpBCGooAgA2AgAgACADKQMINwIAIANBIGokgICAgAAPC0GwgsCAAEEcQayOwIAAEI2GgIAAAAuyAQEDfyOAgICAAEEgayIDJICAgIAAAkAgAUEQaigCACIEQQhqIgUgBEkNACADQQhqIAUQ6oCAgAAgAyABQQhqEM+FgIAAIANBCGogAygCACADKAIEEJiFgIAAIAMgAjcDGCADQQhqIANBGGpBCBCYhYCAACAAQQhqIANBCGpBCGooAgA2AgAgACADKQMINwIAIANBIGokgICAgAAPC0GwgsCAAEEcQayOwIAAEI2GgIAAAAuEAgIBfwJ+I4CAgIAAQTBrIgIkgICAgAACQAJAAkACQAJAIAEpAwAiA0IAUg0AIABBADYCAAwBCyACQRBqIAEgA0J/fBD3gYCAACABKQMAIgNCf3wiBCADVg0BIAEgBDcDACACQQhqIAJBEGoQz4WAgAAgAigCCCACKAIMEOqEgIAARQ0CIAJBIGoQ64SAgAAgAigCIEUNAyAAIAIpAyA3AgAgAEEIaiACQSBqQQhqKAIANgIAIAJBEGoQvoCAgAALIAJBMGokgICAgAAPC0HAgcCAAEEhQbyOwIAAEI2GgIAAAAtB7IrAgABB5gAQz4SAgAAAC0HsisCAAEHmABDPhICAAAALhAICAX8CfiOAgICAAEEwayICJICAgIAAAkACQAJAAkACQCABKQMAIgNCAFINACAAQQA2AgAMAQsgAkEQaiABIANCf3wQ9oGAgAAgASkDACIDQn98IgQgA1YNASABIAQ3AwAgAkEIaiACQRBqEM+FgIAAIAIoAgggAigCDBDqhICAAEUNAiACQSBqEOuEgIAAIAIoAiBFDQMgACACKQMgNwIAIABBCGogAkEgakEIaigCADYCACACQRBqEL6AgIAACyACQTBqJICAgIAADwtBwIHAgABBIUG8jsCAABCNhoCAAAALQeyKwIAAQeYAEM+EgIAAAAtB7IrAgABB5gAQz4SAgAAACwQAQQALBABBAAv9AQEBfyOAgICAAEGAAWsiASSAgICAACABQRBqQdyOwIAAQQUQ6YSAgAACQAJAAkACQCABKAIQDQAgAEEANgIIDAELIAFBIGpBCGogAUEQakEIaigCADYCACABIAEpAxA3AyAgAUEIaiABQSBqEM+FgIAAIAFBMGogASgCCCABKAIMELOBgIAAIAEoAjBBAUYNAiAAIAFBMGpBCGpBwAAQ84aAgAAaIAFBIGoQvoCAgAAgASgCEA0BCyABQRBqEIiBgIAACyABQYABaiSAgICAAA8LIAEgASkCNDcDeEHhjsCAAEEmIAFB+ABqQaiFwIAAQeyPwIAAEKqGgIAAAAuyAQEBfyOAgICAAEEwayIBJICAgIAAIAFBIGogABC2gYCAAAJAIAEoAiBBAUcNACABIAEpAiQ3AxBB/I/AgABBJCABQRBqQaiFwIAAQaCQwIAAEKqGgIAAAAsgAUEYaiABQSxqKAIANgIAIAEgASkCJDcDECABQQhqIAFBEGoQz4WAgABB3I7AgABBBSABKAIIIAEoAgwQ6ISAgAAaIAFBEGoQvoCAgAAgAUEwaiSAgICAAAtKAQJ/I4CAgIAAQRBrIgIkgICAgAAgAkEIaiABEM+FgIAAIAIoAgwhASAAIAIoAggiAzYCACAAIAMgAWo2AgQgAkEQaiSAgICAAAu6AgIBfwF+I4CAgIAAQSBrIgMkgICAgAAgA0EYaiABQTBqIAIQuoGAgAAgAyADKQMYIgQ3AwgCQAJAAkACQCAEp0H/AXFBA0cNACADQQhqEOWAgIAAIANBGGogASACEICCgIAAIAMgAykDGCIENwMIIASnQf8BcUEDRw0BIANBCGoQ5YCAgAAgA0EYaiABQRhqIAIQgYKAgAAgAyADKQMYIgQ3AwggBKdB/wFxQQNHDQIgA0EIahDlgICAACAAQQM6AAAMAwsgAyAENwMYIANBEGogA0EYahDAhYCAACAAIAMpAxA3AgAMAgsgAyAENwMYIANBEGogA0EYahDAhYCAACAAIAMpAxA3AgAMAQsgAyAENwMYIANBEGogA0EYahDAhYCAACAAIAMpAxA3AgALIANBIGokgICAgAAL3wECAX8BfiOAgICAAEEgayIDJICAgIAAIANBGGogASACEK2BgIAAIAMgAykDGCIENwMIAkACQAJAIASnQf8BcUEDRw0AIANBCGoQ5YCAgAAgA0EYaiABQQhqIAIQuoGAgAAgAyADKQMYIgQ3AwggBKdB/wFxQQNHDQEgA0EIahDlgICAACAAQQM6AAAMAgsgAyAENwMYIANBEGogA0EYahDAhYCAACAAIAMpAxA3AgAMAQsgAyAENwMYIANBEGogA0EYahDAhYCAACAAIAMpAxA3AgALIANBIGokgICAgAAL3wECAX8BfiOAgICAAEEgayIDJICAgIAAIANBGGogASACEK2BgIAAIAMgAykDGCIENwMIAkACQAJAIASnQf8BcUEDRw0AIANBCGoQ5YCAgAAgA0EYaiABQQhqIAIQuoGAgAAgAyADKQMYIgQ3AwggBKdB/wFxQQNHDQEgA0EIahDlgICAACAAQQM6AAAMAgsgAyAENwMYIANBEGogA0EYahDAhYCAACAAIAMpAxA3AgAMAQsgAyAENwMYIANBEGogA0EYahDAhYCAACAAIAMpAxA3AgALIANBIGokgICAgAALlAUBA38jgICAgABB0AFrIgIkgICAgAAgAkHYAGogARDHgYCAAAJAAkACQAJAAkACQAJAIAIoAlhBAUYNACACQcgAakEIaiACQeQAaigCADYCACACIAIpAlw3A0ggAkGAAWogARCDgoCAACACQdgAakEEciEDIAIoAoABQQFGDQEgAkHoAGpBEGoiBCACQYABakEYaikDADcDACACQegAakEIaiACQYABakEQaikDADcDACACIAIpA4gBNwNoIAJBoAFqIAEQhIKAgAAgAigCoAFBAUcNAyACIAIpAqQBNwPIASACQcABaiACQcgBahDAhYCAACAAIAIpA8ABNwIEIABBATYCACACQfAAahC+gICAACACQcgAahC+gICAACACKAKAAQ0CDAQLIAIgAikCXDcDoAEgAkGAAWogAkGgAWoQwIWAgAAgACACKQOAATcCBCAAQQE2AgAMBQsgAiACKQKEATcDoAEgAkHIAWogAkGgAWoQwIWAgAAgACACKQPIATcCBCAAQQE2AgAgAkHIAGoQvoCAgAAgAigCWEUNBAwDCyACQYABakEEchCNgYCAAAwBCyACQcAAaiACQcgAakEIaigCADYCACACQQhqQQhqIAJB6ABqQQhqKQMANwMAIAJBCGpBEGogBCkDADcDACACQShqIAJBoAFqQRBqKQMANwMAIAJBMGogAkGgAWpBGGopAwA3AwAgAiACKQNINwM4IAIgAikDaDcDCCACIAIpA6gBNwMgIABBCGogAkEIakHAABDzhoCAABogAEEANgIAAkAgAigCgAFFDQAgAkGAAWpBBHIQjYGAgAALIAIoAlgNAQwCCyACKAJYRQ0BCyADEI2BgIAACyACQdABaiSAgICAAAv4AQIBfwF+I4CAgIAAQTBrIgIkgICAgAAgAiABEK6BgIAAAkACQCACKAIAQQFGDQAgAikDCCEDIAJBEGogARDHgYCAAAJAAkAgAigCEEEBRw0AIAIgAikCFDcDKCACQSBqIAJBKGoQwIWAgAAgACACKQMgNwIEIABBATYCAAwBCyAAQQA2AgAgAEEIaiADNwMAIABBEGogAkEQakEEciIBKQIANwIAIABBGGogAUEIaigCADYCAAsgAhCQgYCAAAwBCyACIAIpAgQ3AxAgAkEoaiACQRBqEMCFgIAAIAAgAikDKDcCBCAAQQE2AgALIAJBMGokgICAgAAL+AECAX8BfiOAgICAAEEwayICJICAgIAAIAIgARCugYCAAAJAAkAgAigCAEEBRg0AIAIpAwghAyACQRBqIAEQx4GAgAACQAJAIAIoAhBBAUcNACACIAIpAhQ3AyggAkEgaiACQShqEMCFgIAAIAAgAikDIDcCBCAAQQE2AgAMAQsgAEEANgIAIABBCGogAzcDACAAQRBqIAJBEGpBBHIiASkCADcCACAAQRhqIAFBCGooAgA2AgALIAIQkIGAgAAMAQsgAiACKQIENwMQIAJBKGogAkEQahDAhYCAACAAIAIpAyg3AgQgAEEBNgIACyACQTBqJICAgIAAC+IRBQN/AX4BfwJ+A38jgICAgABBoAFrIgYkgICAgAAgBkEIaiABEM2AgIAAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAGLQAIQQFGDQAgBi0ACUEBRw0BIAYtAAohByAGQQhqEMiAgIAAAkACQCAHQdsARg0AIAdB+wBGDQEgASAGQZgBakHAkMCAABDggICAACEHIAZBATYCCCAGIAc2AgwMEAsgAS0AGEF/aiIHQf8BcSIIIAdHDQQgASAHOgAYAkAgCA0AIAZBiAFqIQcMEQsgARDQgICAACAGIAE2AnAgBkEBOgB0IAZBwABqIAZB8ABqEN2BgIAAAkACQCAGKAJAQQFGDQAgBkGIAWpBCGoiByAGQcwAaigCADYCACAGIAYpAkQiCTcDiAEgCacNAUEAQdSbwIAAQYybwIAAELuBgIAAIQogBkGIAWoQlYGAgAAMDwsgBigCRCEKDA4LIAZB+ABqQQhqIAcoAgA2AgAgBiAGKQOIATcDeCAGQcAAaiAGQfAAahDbgYCAACAGKAJAQQFHDQsgBigCRCEKDAwLIAEtABhBf2oiB0H/AXEiCCAHRw0CIAEgBzoAGAJAIAgNACAGQcAAaiEHDBALIAEQ0ICAgAAgBkEBOgB0IAYgATYCcCAGQQA2AnggBkHAAGpBBHIhCEIAIQkgBkHQAGohCgJAA0AgBkGIAWogBkHwAGoQyoGAgAACQAJAIAYtAIgBQQFGDQAgBi0AiQEiB0EDRg0DAkAgB0EBSw0AAkACQCAHDgIAAQALAkAgBigCeA0AIAZBwABqIAZB8ABqENSBgIAAIAYoAkBBAUcNBAwNC0Hcm8CAAEEWELyBgIAAIQcMDQsgCUIBUQ0JIAZBwABqIAZB8ABqENeBgIAAIAYoAkBBAUYNCyAKKQMAIQsgBikDSCEMIAZBwABqEJaBgIAAQgEhCQwDCyAGIAZB8ABqENmBgIAAIgc2AkAgBw0LIAZBwABqEJOBgIAADAILIAYoAowBIQcMCgsgBkH4AGoQlYGAgAAgBkH4AGpBCGogCEEIaigCADYCACAGIAgpAgA3A3gMAAsLIAYoAngNBUHcm8CAAEEWEPOAgIAAIQcMBwsgACAGKAIMNgIEIABBATYCAAwPCyAGQQU2AkAgASAGQcAAahDFgICAACEBIABBATYCACAAIAE2AgQgBkEIahDIgICAAAwOC0HAgcCAAEEhQdCQwIAAEI2GgIAAAAtBwIHAgABBIUHQkMCAABCNhoCAAAALQeaTwIAAQQYQvIGAgAAhBwwCCyAGQYgBakEIaiIHIAZB+ABqQQhqKAIANgIAIAYgBikDeDcDiAECQCAJQgFSDQAgBkEwakEIaiAHKAIANgIAIAYgBikDiAE3AzBBACEKDAQLIAZBwABqQeaTwIAAQQYQw4GAgAACQCAGKAJAQQFGDQAgBkHQAGopAwAhCyAGKQNIIQwgBkHAAGoQloGAgAAgBkEwakEIaiAGQYgBakEIaigCADYCACAGIAYpA4gBNwMwQQAhCgJAIAYoAngNACAGQfgAahCVgYCAAAsMBAsgBigCRCEHIAZBiAFqEL6AgIAAQQAhCAwCCyAGKAJEIQcLQQEhCAsCQAJAAkAgBigCeEUNAEEBIQogCA0BDAILIAZB+ABqEJWBgIAAQQEhCgwBCyAGQfgAahC+gICAAAsLAkACQAJAIAEtABhBAWoiCEH/AXEgCEcNACABIAg6ABggARDigICAACEIIAZB0ABqIAs3AwAgBkHAAGpBCGoiDSAMNwMAIAZB2ABqIg4gBikDMDcDACAGQeAAaiAGQTBqQQhqKAIANgIAIAYgBzYCRCAGIAo2AkAgBiAINgJoIAZB6ABqIQ8CQCAKDQACQCAIDQAgBkEoaiANQRhqKQMANwMAIAZBCGpBGGogDUEQaikDADcDACAGQQhqQRBqIA1BCGopAwA3AwAgBkEANgIIIAYgDSkDADcDEAwECyAGQQE2AgggBiAINgIMIA4QvoCAgABBACEKIAYoAmghCAwCC0EBIQogBkEBNgIIIAYgBzYCDAwBC0GwgsCAAEEcQeCQwIAAEI2GgIAAAAsgCEUNACAKRQ0FIA8Q2ICAgAAMBQsgDxC8gICAAAwECwJAIAYpA0hCAVINACAGQdgAaikDACEJIAZB0ABqKQMAIQwgBkHAAGoQl4GAgAAgBkEwakEIaiAGQfgAakEIaigCADYCACAGIAYpA3g3AzBBACEIDAMLQQFB1JvAgABBjJvAgAAQu4GAgAAhCiAGQcAAahCXgYCAAAsgBkH4AGoQvoCAgAALQQEhCAsCQCABLQAYQQFqIgdB/wFxIAdGDQBBsILAgABBHEHgkMCAABCNhoCAAAALIAEgBzoAGCABEOOAgIAAIQcgBkHQAGogCTcDACAGQcAAakEIaiINIAw3AwAgBkHYAGoiDiAGKQMwNwMAIAZB4ABqIAZBMGpBCGooAgA2AgAgBiAKNgJEIAYgCDYCQCAGIAc2AmggBkHoAGohDwJAAkACQCAIDQACQCAHDQAgBkEoaiANQRhqKQMANwMAIAZBCGpBGGogDUEQaikDADcDACAGQQhqQRBqIA1BCGopAwA3AwAgBkEANgIIIAYgDSkDADcDEAwDCyAGQQE2AgggBiAHNgIMIA4QvoCAgABBACEIIAYoAmghBwwBC0EBIQggBkEBNgIIIAYgCjYCDAsgB0UNACAIRQ0BIA8Q2ICAgAAMAQsgDxC8gICAAAsCQAJAIAYoAghBAUcNACAAIAYoAgwgARDKgICAADYCBCAAQQE2AgAgBigCCA0BIAZBIGoQvoCAgAAMAQsgAEEANgIAIABBCGogBikDEDcDACAAQSBqIAZBCGpBIGopAwA3AwAgAEEYaiAGQQhqQRhqKQMANwMAIABBEGogBkEIakEQaikDADcDAAsgBkGgAWokgICAgAAPCyAHQRU2AgAgASAHEMWAgIAAIQEgAEEBNgIAIAAgATYCBAsgBkGgAWokgICAgAALvAsDA38BfgJ/I4CAgIAAQdAAayIGJICAgIAAIAYgARDNgICAAAJAAkACQAJAAkACQAJAAkACQAJAAkAgBi0AAEEBRg0AIAYtAAFBAUcNASAGLQACIQcgBhDIgICAAAJAAkAgB0HbAEYNACAHQfsARg0BIAEgBkHIAGpB8JDAgAAQ4ICAgAAhByAGQQE2AgAgBiAHNgIEDAoLIAEtABhBf2oiB0H/AXEiCCAHRw0EIAEgBzoAGAJAIAgNACAGQTBqIQcMCwsgARDQgICAACAGIAE2AkBBASEIIAZBAToARCAGQRBqIAZBwABqEN2BgIAAIAYoAhBBAUYNBSAGQThqIAZBHGooAgA2AgAgBiAGKQIUIgk3AzACQCAJpyIKDQBBAEGEm8CAAEGMm8CAABC7gYCAACEKIAZBMGoQlYGAgABBASEIDAkLQQAhCCAGKQI0IQkMCAsgAS0AGEF/aiIHQf8BcSIIIAdHDQIgASAHOgAYAkAgCA0AIAZBEGohBwwKCyABENCAgIAAIAZBAToALCAGIAE2AiggBkEANgIwIAZBEGpBBHIhCAJAA0AgBkHAAGogBkEoahDOgYCAAAJAAkAgBi0AQEEBRg0AIAYtAEEiB0ECRw0BIAYoAjAiBw0DQdyTwIAAQQoQ84CAgAAhBwwJCyAGKAJEIQcMCAsCQCAHQQFxRQ0AIAYgBkEoahDZgYCAACIHNgIQIAcNCCAGQRBqEJOBgIAADAELAkACQCAGKAIwDQAgBkEQaiAGQShqENSBgIAAIAYoAhBBAUcNASAGKAIUIQcMCQtB3JPAgABBChC8gYCAACEHDAgLIAZBMGoQlYGAgAAgBkEwakEIaiAIQQhqKAIANgIAIAYgCCkCADcDMAwACwtBACEKIAYpAjQhCQwGCyAAIAYoAgQ2AgQgAEEBNgIADAkLIAZBBTYCECABIAZBEGoQxYCAgAAhASAAQQE2AgAgACABNgIEIAYQyICAgAAMCAtBwIHAgABBIUHQkMCAABCNhoCAAAALQcCBwIAAQSFB0JDAgAAQjYaAgAAACyAGKAIUIQoMAgsgBkEwahCVgYCAAEEBIQoLAkACQAJAIAEtABhBAWoiCEH/AXEgCEcNACABIAg6ABggARDigICAACEIIAZBGGogCTcDACAGIAg2AiAgBiAHNgIUIAYgCjYCECAGQSBqIQsCQCAKDQACQCAIDQAgBkEMaiAGQRBqQQxqKAIANgIAIAYgBikCFDcCBCAGQQA2AgAMBAsgBkEBNgIAIAYgCDYCBCAGQRBqQQRyEL6AgIAAQQAhCiAGKAIgIQgMAgtBASEKIAZBATYCACAGIAc2AgQMAQtBsILAgABBHEHgkMCAABCNhoCAAAALIAhFDQAgCkUNAiALENiAgIAADAILIAsQvICAgAAMAQsCQAJAAkAgAS0AGEEBaiIHQf8BcSAHRw0AIAEgBzoAGCABEOOAgIAAIQcgBkEYaiAJNwMAIAYgBzYCICAGIAo2AhQgBiAINgIQIAZBIGohCwJAIAgNAAJAIAcNACAGQQxqIAZBEGpBDGooAgA2AgAgBiAGKQIUNwIEIAZBADYCAAwECyAGQQE2AgAgBiAHNgIEIAZBEGpBBHIQvoCAgABBACEIIAYoAiAhBwwCC0EBIQggBkEBNgIAIAYgCjYCBAwBC0GwgsCAAEEcQeCQwIAAEI2GgIAAAAsgB0UNACAIRQ0BIAsQ2ICAgAAMAQsgCxC8gICAAAsCQAJAIAYoAgBBAUcNACAAIAYoAgQgARDKgICAADYCBCAAQQE2AgAgBigCAA0BIAZBBHIQvoCAgAAMAQsgAEEANgIAIAAgBkEEciIBKQIANwIEIABBDGogAUEIaigCADYCAAsgBkHQAGokgICAgAAPCyAHQRU2AgAgASAHEMWAgIAAIQEgAEEBNgIAIAAgATYCBAsgBkHQAGokgICAgAALoQoEA38CfgJ/AX4jgICAgABBwABrIgYkgICAgAAgBkEwaiABEM2AgIAAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgBi0AMEEBRg0AIAYtADFBAUcNASAGLQAyIQcgBkEwahDIgICAAAJAAkAgB0HbAEYNACAHQfsARg0BIAEgBkE4akGAkcCAABDggICAACEHDBMLIAEtABhBf2oiB0H/AXEiCCAHRw0GIAEgBzoAGCAIRQ0IIAEQ0ICAgAAgBiABNgIwQQEhCCAGQQE6ADQgBkEIaiAGQTBqENuBgIAAAkACQAJAAkAgBigCCEEBRg0AIAYpAxBCAVINASAGQSBqKQMAIQkgBkEYaikDACEKIAZBCGoQl4GAgABBACEIDAMLIAYoAgwhBwwBC0EAQYSbwIAAQYybwIAAELuBgIAAIQcgBkEIahCXgYCAAEEBIQgLCyABLQAYQQFqIgtB/wFxIAtHDQcgASALOgAYIAEQ44CAgAAhCyAGQRhqIAk3AwAgBkEQaiAKNwMAIAYgCzYCICAGIAc2AgwgBiAINgIIIAZBIGohDCAIDQRBASEIIAtFDQMgCyEHDBELIAEtABhBf2oiB0H/AXEiCCAHRw0EIAEgBzoAGAJAIAhFDQAgARDQgICAACAGQQE6ACwgBiABNgIoQgAhDSAGQRhqIQgCQAJAA0AgBkEwaiAGQShqEMiBgIAAIAYtADBBAUYNASAGLQAxIgdBAkYNAgJAIAdBAXFFDQAgBiAGQShqENmBgIAAIgc2AgggBw0TIAZBCGoQk4GAgAAMAQsgDUIBUQ0MIAZBCGogBkEoahDXgYCAACAGKAIIQQFGDREgCCkDACEJIAYpAxAhCiAGQQhqEJaBgIAAQgEhDQwACwsgBigCNCEHDBALQQAhCCANQgFSDQ0MEAsgBkEVNgIIIAEgBkEIahDFgICAACEBIABBATYCACAAIAE2AgQMCwsgACAGKAI0NgIEIABBATYCAAwKCyAGQQU2AgggASAGQQhqEMWAgIAAIQEgAEEBNgIAIAAgATYCBCAGQTBqEMiAgIAADAkLQQAhCAwGCyALDQZBASEIDAULQcCBwIAAQSFB0JDAgAAQjYaAgAAAC0HAgcCAAEEhQdCQwIAAEI2GgIAAAAtBsILAgABBHEHgkMCAABCNhoCAAAALIAZBFTYCCCABIAZBCGoQxYCAgAAhASAAQQE2AgAgACABNgIEDAMLQeaTwIAAQQYQvIGAgAAhBwwFCyAMELyAgIAADAYLIAwQ2ICAgABBASEIDAULIAZBwABqJICAgIAADwsgBkEIakHmk8CAAEEGEMOBgIAAIAYoAghBAUYNACAGQRhqKQMAIQkgBikDECEKIAZBCGoQloGAgAAMAgsgBigCDCEHC0EBIQgLAkAgAS0AGEEBaiILQf8BcSALRg0AQbCCwIAAQRxB4JDAgAAQjYaAgAAACyABIAs6ABggARDigICAACELIAZBGGogCTcDACAGQRBqIAo3AwAgBiALNgIgIAYgBzYCDCAGIAg2AgggBkEgaiEMAkACQAJAIAgNAEEBIQgCQCALRQ0AIAshBwwEC0EAIQgMAQsgCw0BQQEhCAsgDBC8gICAAAwBCyAMENiAgIAAQQEhCAsgCA0AIABBEGogCTcDACAAQQhqIAo3AwBBACEBDAELIAAgByABEMqAgIAANgIEQQEhAQsgACABNgIAIAZBwABqJICAgIAAC4UMAwN/AX4CfyOAgICAAEHQAGsiBiSAgICAACAGIAEQzYCAgAACQAJAAkACQAJAAkACQAJAAkACQAJAIAYtAABBAUYNACAGLQABQQFHDQEgBi0AAiEHIAYQyICAgAACQAJAIAdB2wBGDQAgB0H7AEYNASABIAZByABqQZCRwIAAEOCAgIAAIQcgBkEBNgIAIAYgBzYCBAwKCyABLQAYQX9qIgdB/wFxIgggB0cNBCABIAc6ABgCQCAIDQAgBkEwaiEHDAsLIAEQ0ICAgAAgBiABNgJAQQEhCCAGQQE6AEQgBkEQaiAGQcAAahDcgYCAACAGKAIQQQFGDQUgBkE4aiAGQRxqKAIANgIAIAYgBikCFCIJNwMwAkAgCaciCg0AQQBBhJvAgABBjJvAgAAQu4GAgAAhCiAGQTBqEJ2BgIAAQQEhCAwJC0EAIQggBikCNCEJDAgLIAEtABhBf2oiB0H/AXEiCCAHRw0CIAEgBzoAGAJAIAgNACAGQRBqIQcMCgsgARDQgICAACAGQQE6ACwgBiABNgIoIAZBADYCMCAGQRBqQQRyIQgCQAJAA0AgBkHAAGogBkEoahDMgYCAAAJAAkACQAJAIAYtAEBBAUYNACAGLQBBIgdBAkcNASAGKAIwIgdFDQJBACEKIAYpAjQhCQwNCyAGKAJEIQcMCwsCQCAHQQFxRQ0AIAYgBkEoahDZgYCAACIHNgIQIAcNCyAGQRBqEJOBgIAADAMLAkAgBigCMA0AIAZBEGogBkEoahDVgYCAACAGKAIQQQFHDQIgBigCFCEHDAsLQd+awIAAQQoQvIGAgAAhBwwKCyAGQRBqQd+awIAAQQoQxIGAgAAgBigCFCEHIAYoAhBBAUYNCSAGQRhqKQMAIQkgBigCMEUNAiAGQTBqEL6AgIAADAMLIAZBMGoQnYGAgAAgBkEwakEIaiAIQQhqKAIANgIAIAYgCCkCADcDMAwACwsgBkEwahCdgYCAAAtBACEKDAYLIAAgBigCBDYCBCAAQQE2AgAMCQsgBkEFNgIQIAEgBkEQahDFgICAACEBIABBATYCACAAIAE2AgQgBhDIgICAAAwIC0HAgcCAAEEhQdCQwIAAEI2GgIAAAAtBwIHAgABBIUHQkMCAABCNhoCAAAALIAYoAhQhCgwCCyAGQTBqEJ2BgIAAQQEhCgsCQAJAAkAgAS0AGEEBaiIIQf8BcSAIRw0AIAEgCDoAGCABEOKAgIAAIQggBkEYaiAJNwMAIAYgCDYCICAGIAc2AhQgBiAKNgIQIAZBIGohCwJAIAoNAAJAIAgNACAGQQxqIAZBEGpBDGooAgA2AgAgBiAGKQIUNwIEIAZBADYCAAwECyAGQQE2AgAgBiAINgIEIAZBEGpBBHIQvoCAgABBACEKIAYoAiAhCAwCC0EBIQogBkEBNgIAIAYgBzYCBAwBC0GwgsCAAEEcQeCQwIAAEI2GgIAAAAsgCEUNACAKRQ0CIAsQ2ICAgAAMAgsgCxC8gICAAAwBCwJAAkACQCABLQAYQQFqIgdB/wFxIAdHDQAgASAHOgAYIAEQ44CAgAAhByAGQRhqIAk3AwAgBiAHNgIgIAYgCjYCFCAGIAg2AhAgBkEgaiELAkAgCA0AAkAgBw0AIAZBDGogBkEQakEMaigCADYCACAGIAYpAhQ3AgQgBkEANgIADAQLIAZBATYCACAGIAc2AgQgBkEQakEEchC+gICAAEEAIQggBigCICEHDAILQQEhCCAGQQE2AgAgBiAKNgIEDAELQbCCwIAAQRxB4JDAgAAQjYaAgAAACyAHRQ0AIAhFDQEgCxDYgICAAAwBCyALELyAgIAACwJAAkAgBigCAEEBRw0AIAAgBigCBCABEMqAgIAANgIEIABBATYCACAGKAIADQEgBkEEchC+gICAAAwBCyAAQQA2AgAgACAGQQRyIgEpAgA3AgQgAEEMaiABQQhqKAIANgIACyAGQdAAaiSAgICAAA8LIAdBFTYCACABIAcQxYCAgAAhASAAQQE2AgAgACABNgIECyAGQdAAaiSAgICAAAuREwMDfwF+A38jgICAgABB4AFrIgYkgICAgAAgBkEIaiABEM2AgIAAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAYtAAhBAUYNACAGLQAJQQFHDQEgBi0ACiEHIAZBCGoQyICAgAACQAJAIAdB2wBGDQAgB0H7AEYNASABIAZB2AFqQaCRwIAAEOCAgIAAIQcgBkEBNgIIIAYgBzYCDAwQCyABLQAYQX9qIgdB/wFxIgggB0cNBCABIAc6ABgCQCAIDQAgBkEoaiEHDA4LIAEQ0ICAgAAgBiABNgKAASAGQQE6AIQBIAZBwABqIAZBgAFqEN2BgIAAIAYoAkBBAUYNBSAGQbABakEIaiIHIAZBzABqKAIANgIAIAYgBikCRCIJNwOwAQJAIAmnDQBBAEHUm8CAAEGMm8CAABC7gYCAACEKIAZBsAFqEJWBgIAADAwLIAZBkAFqQQhqIAcoAgA2AgAgBiAGKQOwATcDkAEgBkHAAGogBkGAAWoQ3IGAgAACQAJAAkAgBigCQEEBRw0AIAYoAkQhCgwBCyAGQbABakEIaiIHIAZBzABqKAIANgIAIAYgBikCRCIJNwOwASAJpw0BQQFB1JvAgABBjJvAgAAQu4GAgAAhCiAGQbABahCdgYCAAAsgBkGQAWoQvoCAgAAMDAsgBkGgAWpBCGogBygCACIHNgIAIAZBKGpBCGogBikDsAE3AwAgBkE4aiAHNgIAIAYgBikClAE3AyggBigCkAEhCkEAIQcMDAsgAS0AGEF/aiIHQf8BcSIIIAdHDQIgASAHOgAYAkAgCA0AIAZBwABqIQcMDQsgARDQgICAACAGQQE6AHwgBiABNgJ4IAZBADYCgAEgBkEANgKQASAGQcAAakEEciEIAkADQCAGQbABaiAGQfgAahDSgYCAAAJAAkAgBi0AsAFBAUYNACAGLQCxASIHQQNGDQMCQCAHQQFLDQACQAJAIAcOAgABAAsCQCAGKAKAAQ0AIAZBwABqIAZB+ABqENSBgIAAIAYoAkBBAUcNBAwNC0Gcm8CAAEEOELyBgIAAIQcMDQsCQCAGKAKQAQ0AIAZBwABqIAZB+ABqENWBgIAAIAYoAkBBAUYNDCAGQZABahCdgYCAACAGQZABakEIaiAIQQhqKAIANgIAIAYgCCkCADcDkAEMBAtBqpvAgABBDhC8gYCAACEHDAwLIAYgBkH4AGoQ2YGAgAAiBzYCQCAHDQsgBkHAAGoQk4GAgAAMAgsgBigCtAEhBwwKCyAGQYABahCVgYCAACAGQYABakEIaiAIQQhqKAIANgIAIAYgCCkCADcDgAEMAAsLIAYoAoABDQVBnJvAgABBDhDzgICAACEHDAcLIAAgBigCDDYCBCAAQQE2AgAMDAsgBkEFNgJAIAEgBkHAAGoQxYCAgAAhASAAQQE2AgAgACABNgIEIAZBCGoQyICAgAAMCwtBwIHAgABBIUHQkMCAABCNhoCAAAALQcCBwIAAQSFB0JDAgAAQjYaAgAAACyAGKAJEIQoMBQsgBkGgAWpBCGogBkGAAWpBCGooAgA2AgAgBiAGKQOAATcDoAECQAJAAkACQAJAIAYoApABIggNACAGQcAAakGqm8CAAEEOEMSBgIAAQQEhCiAGKAJAQQFGDQIgBkG4AWogBkHMAGooAgA2AgAgBiAGKQJENwOwASAGKAKQASEIDAELIAZBsAFqQQhqIAZBkAFqQQhqKAIANgIAIAYgBikDkAE3A7ABQQAhCgsgBkEoakEIaiAGKQOwATcDACAGQThqIAZBsAFqQQhqKAIANgIAIAYgBikCpAE3AyggBigCoAEhByAIRQ0BIApFDQIgBkGQAWoQvoCAgAAMAgsgBigCRCEHIAZBoAFqEL6AgIAAQQAhCgwECyAGQZABahCdgYCAAAtBACEIIAYoAoABDQMgBkGAAWoQlYGAgAAMAwsgBigCRCEHC0EBIQoLIAZBkAFqEJ2BgIAAAkAgBigCgAFFDQBBASEIIApFDQEgBkGAAWoQvoCAgAAMAQsgBkGAAWoQlYGAgABBASEICwJAAkAgAS0AGEEBaiIKQf8BcSAKRw0AIAEgCjoAGCABEOKAgIAAIQogBkHAAGpBCGogBikDKDcDACAGQcAAakEQaiAGQShqQQhqKQMANwMAIAZB2ABqIAZBKGpBEGooAgA2AgAgBiAHNgJEIAYgCDYCQCAGIAo2AlwCQAJAAkACQCAIDQAgCkUNAkEAIQtBASEMDAELQQEhC0EAIQwgByEKCyAGQQE2AgggBiAKNgIMIAgNASAGQcAAakEEchDBgICAAAwDCyAGQQhqQRRqIAZBwABqQRRqKQIANwIAIAZBCGpBDGogBkHAAGpBDGopAgA3AgAgBiAGKQJENwIMIAZBADYCCEEBIQsMAgsgDEUNASAGQcAAakEEchDYgICAAAwBC0GwgsCAAEEcQeCQwIAAEI2GgIAAAAsgBkHcAGohBwJAIAYoAlwNACAHELyAgIAADAULIAtFDQQgBxDYgICAAAwEC0EBIQcLAkACQCABLQAYQQFqIghB/wFxIAhHDQAgASAIOgAYIAEQ44CAgAAhCCAGQcAAakEIaiAGKQMoNwMAIAZBwABqQRBqIAZBKGpBCGopAwA3AwAgBkHYAGogBkEoakEQaigCADYCACAGIAo2AkQgBiAHNgJAIAYgCDYCXAJAAkACQAJAIAcNACAIRQ0CQQAhC0EBIQwMAQtBASELQQAhDCAKIQgLIAZBATYCCCAGIAg2AgwgBw0BIAZBwABqQQRyEMGAgIAADAMLIAZBCGpBFGogBkHAAGpBFGopAgA3AgAgBkEIakEMaiAGQcAAakEMaikCADcCACAGIAYpAkQ3AgwgBkEANgIIQQEhCwwCCyAMRQ0BIAZBwABqQQRyENiAgIAADAELQbCCwIAAQRxB4JDAgAAQjYaAgAAACyAGQdwAaiEHAkAgBigCXA0AIAcQvICAgAAMAwsgC0UNAiAHENiAgIAADAILIAdBFTYCACABIAcQxYCAgAAhASAAQQE2AgAgACABNgIECyAGQeABaiSAgICAAA8LAkACQCAGKAIIQQFHDQAgACAGKAIMIAEQyoCAgAA2AgQgAEEBNgIAIAYoAggNASAGQQhqQQRyEMGAgIAADAELIABBADYCACAAIAZBCGpBBHIiASkCADcCBCAAQRRqIAFBEGopAgA3AgAgAEEMaiABQQhqKQIANwIACyAGQeABaiSAgICAAAuREwMDfwF+A38jgICAgABB4AFrIgYkgICAgAAgBkEIaiABEM2AgIAAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAYtAAhBAUYNACAGLQAJQQFHDQEgBi0ACiEHIAZBCGoQyICAgAACQAJAIAdB2wBGDQAgB0H7AEYNASABIAZB2AFqQbCRwIAAEOCAgIAAIQcgBkEBNgIIIAYgBzYCDAwQCyABLQAYQX9qIgdB/wFxIgggB0cNBCABIAc6ABgCQCAIDQAgBkEoaiEHDA4LIAEQ0ICAgAAgBiABNgKAASAGQQE6AIQBIAZBwABqIAZBgAFqEN2BgIAAIAYoAkBBAUYNBSAGQbABakEIaiIHIAZBzABqKAIANgIAIAYgBikCRCIJNwOwAQJAIAmnDQBBAEHUm8CAAEGMm8CAABC7gYCAACEKIAZBsAFqEJWBgIAADAwLIAZBkAFqQQhqIAcoAgA2AgAgBiAGKQOwATcDkAEgBkHAAGogBkGAAWoQ3IGAgAACQAJAAkAgBigCQEEBRw0AIAYoAkQhCgwBCyAGQbABakEIaiIHIAZBzABqKAIANgIAIAYgBikCRCIJNwOwASAJpw0BQQFB1JvAgABBjJvAgAAQu4GAgAAhCiAGQbABahCdgYCAAAsgBkGQAWoQvoCAgAAMDAsgBkGgAWpBCGogBygCACIHNgIAIAZBKGpBCGogBikDsAE3AwAgBkE4aiAHNgIAIAYgBikClAE3AyggBigCkAEhCkEAIQcMDAsgAS0AGEF/aiIHQf8BcSIIIAdHDQIgASAHOgAYAkAgCA0AIAZBwABqIQcMDQsgARDQgICAACAGQQE6AHwgBiABNgJ4IAZBADYCgAEgBkEANgKQASAGQcAAakEEciEIAkADQCAGQbABaiAGQfgAahDQgYCAAAJAAkAgBi0AsAFBAUYNACAGLQCxASIHQQNGDQMCQCAHQQFLDQACQAJAIAcOAgABAAsCQCAGKAKAAQ0AIAZBwABqIAZB+ABqENSBgIAAIAYoAkBBAUcNBAwNC0Gcm8CAAEEOELyBgIAAIQcMDQsCQCAGKAKQAQ0AIAZBwABqIAZB+ABqENWBgIAAIAYoAkBBAUYNDCAGQZABahCdgYCAACAGQZABakEIaiAIQQhqKAIANgIAIAYgCCkCADcDkAEMBAtBqpvAgABBDhC8gYCAACEHDAwLIAYgBkH4AGoQ2YGAgAAiBzYCQCAHDQsgBkHAAGoQk4GAgAAMAgsgBigCtAEhBwwKCyAGQYABahCVgYCAACAGQYABakEIaiAIQQhqKAIANgIAIAYgCCkCADcDgAEMAAsLIAYoAoABDQVBnJvAgABBDhDzgICAACEHDAcLIAAgBigCDDYCBCAAQQE2AgAMDAsgBkEFNgJAIAEgBkHAAGoQxYCAgAAhASAAQQE2AgAgACABNgIEIAZBCGoQyICAgAAMCwtBwIHAgABBIUHQkMCAABCNhoCAAAALQcCBwIAAQSFB0JDAgAAQjYaAgAAACyAGKAJEIQoMBQsgBkGgAWpBCGogBkGAAWpBCGooAgA2AgAgBiAGKQOAATcDoAECQAJAAkACQAJAIAYoApABIggNACAGQcAAakGqm8CAAEEOEMSBgIAAQQEhCiAGKAJAQQFGDQIgBkG4AWogBkHMAGooAgA2AgAgBiAGKQJENwOwASAGKAKQASEIDAELIAZBsAFqQQhqIAZBkAFqQQhqKAIANgIAIAYgBikDkAE3A7ABQQAhCgsgBkEoakEIaiAGKQOwATcDACAGQThqIAZBsAFqQQhqKAIANgIAIAYgBikCpAE3AyggBigCoAEhByAIRQ0BIApFDQIgBkGQAWoQvoCAgAAMAgsgBigCRCEHIAZBoAFqEL6AgIAAQQAhCgwECyAGQZABahCdgYCAAAtBACEIIAYoAoABDQMgBkGAAWoQlYGAgAAMAwsgBigCRCEHC0EBIQoLIAZBkAFqEJ2BgIAAAkAgBigCgAFFDQBBASEIIApFDQEgBkGAAWoQvoCAgAAMAQsgBkGAAWoQlYGAgABBASEICwJAAkAgAS0AGEEBaiIKQf8BcSAKRw0AIAEgCjoAGCABEOKAgIAAIQogBkHAAGpBCGogBikDKDcDACAGQcAAakEQaiAGQShqQQhqKQMANwMAIAZB2ABqIAZBKGpBEGooAgA2AgAgBiAHNgJEIAYgCDYCQCAGIAo2AlwCQAJAAkACQCAIDQAgCkUNAkEAIQtBASEMDAELQQEhC0EAIQwgByEKCyAGQQE2AgggBiAKNgIMIAgNASAGQcAAakEEchC9gICAAAwDCyAGQQhqQRRqIAZBwABqQRRqKQIANwIAIAZBCGpBDGogBkHAAGpBDGopAgA3AgAgBiAGKQJENwIMIAZBADYCCEEBIQsMAgsgDEUNASAGQcAAakEEchDYgICAAAwBC0GwgsCAAEEcQeCQwIAAEI2GgIAAAAsgBkHcAGohBwJAIAYoAlwNACAHELyAgIAADAULIAtFDQQgBxDYgICAAAwEC0EBIQcLAkACQCABLQAYQQFqIghB/wFxIAhHDQAgASAIOgAYIAEQ44CAgAAhCCAGQcAAakEIaiAGKQMoNwMAIAZBwABqQRBqIAZBKGpBCGopAwA3AwAgBkHYAGogBkEoakEQaigCADYCACAGIAo2AkQgBiAHNgJAIAYgCDYCXAJAAkACQAJAIAcNACAIRQ0CQQAhC0EBIQwMAQtBASELQQAhDCAKIQgLIAZBATYCCCAGIAg2AgwgBw0BIAZBwABqQQRyEL2AgIAADAMLIAZBCGpBFGogBkHAAGpBFGopAgA3AgAgBkEIakEMaiAGQcAAakEMaikCADcCACAGIAYpAkQ3AgwgBkEANgIIQQEhCwwCCyAMRQ0BIAZBwABqQQRyENiAgIAADAELQbCCwIAAQRxB4JDAgAAQjYaAgAAACyAGQdwAaiEHAkAgBigCXA0AIAcQvICAgAAMAwsgC0UNAiAHENiAgIAADAILIAdBFTYCACABIAcQxYCAgAAhASAAQQE2AgAgACABNgIECyAGQeABaiSAgICAAA8LAkACQCAGKAIIQQFHDQAgACAGKAIMIAEQyoCAgAA2AgQgAEEBNgIAIAYoAggNASAGQQhqQQRyEL2AgIAADAELIABBADYCACAAIAZBCGpBBHIiASkCADcCBCAAQRRqIAFBEGopAgA3AgAgAEEMaiABQQhqKQIANwIACyAGQeABaiSAgICAAAugAgEBfyOAgICAAEEQayIDJICAgIAAAkACQAJAAkAgAkUNACADQQhqIAEQ8ICAgAAgAyADQQhqEO2AgIAAIgI2AgQgAg0BIANBBGoQvICAgAAgAEEIakEBOgAAIAAgATYCBCAAQQA2AgAMAwsgA0EIaiABEPCAgIAAIAMgA0EIahDtgICAACICNgIEIAINASADQQRqELyAgIAAIANBCGogARDugICAACADIANBCGoQ7YCAgAAiAjYCBAJAIAINACADQQRqELyAgIAAIABBCGpBADoAACAAIAE2AgQgAEEANgIADAMLIABBATYCACAAIAI2AgQMAgsgAEEBNgIAIAAgAjYCBAwBCyAAQQE2AgAgACACNgIECyADQRBqJICAgIAAC9QCBAF/AX4BfwF+I4CAgIAAQfAAayIAJICAgIAAIAAQ5ISAgAAiATcDGAJAIAFCAVINACAAQdgAakIAEOWEgIAAAkAgACgCWCICQQFHDQAgAEHYAGpBBHIQvoCAgAALIABB8ABqJICAgIAAIAJBAUYPCyAAIABBGGo2AlAgAEGAiMCAADYCVCAAQdgAakEUakEANgIAIABBrIPAgAA2AmggAEIBNwJcIABBwJLAgAA2AlggAEEQaiAAQdAAakGHgICAABDpgoCAACAAKQMQIQEgAEEIaiAAQdQAakGHgICAABDpgoCAACAAKQMIIQMgACAAQdgAakGIgICAABCShYCAACAAQSBqQRRqQQM2AgAgACADNwNAIAAgATcDOCAAQgM3AiQgAEH8kcCAADYCICAAIAApAwA3A0ggACAAQThqNgIwIABBIGpB1JLAgAAQ5oWAgAAACxIAQfj5woAAIAAgARDGg4CAAAsUAEH4+cKAACAAIAEgAhDHg4CAAAsWAEH4+cKAACAAIAEgAiADEKGBgIAACxIAQfj5woAAIAAgARCggYCAAAsMACABIAIQgICAgAALCgAgARCBgICAAAsKACABEIKAgIAACwoAIAEQg4CAgAALCgAgARCEgICAAAsKACABEIWAgIAACwoAIAEQhoCAgAALCAAQh4CAgAALCAAQiICAgAALCAAQiYCAgAALCAAQioCAgAALCgAgARCLgICAAAsKACABEIyAgIAACwoAIAEQjYCAgAALCAAQjoCAgAALCAAQj4CAgAALCgAgARCQgICAAAsOACABIAIgAxCRgICAAAsOACABIAIgAxCSgICAAAsOACABIAIgAxCTgICAAAsMACABIAIQlICAgAALCAAQlYCAgAALDAAgASACEJaAgIAACwwAIAEgAhCXgICAAAsMACABIAIQmICAgAALGAAgASACIAMgBCAFIAYgByAIEJmAgIAACxoAIAEgAiADIAQgBSAGIAcgCCAJEJqAgIAACwwAIAEgAhCbgICAAAsMACABIAIQnICAgAALDgAgASACIAMQnYCAgAALCgAgARCegICAAAsOACABIAIgAxCfgICAAAsWACABIAIgAyAEIAUgBiAHEKCAgIAACwwAIAEgAhChgICAAAsQACABIAIgAyAEEKKAgIAACxAAIAEgAiADIAQQo4CAgAALGgAgASACIAMgBCAFIAYgByAIIAkQpICAgAALDgAgASACIAMQpYCAgAALDgAgASACIAMQpoCAgAALCAAQp4CAgAALDAAgASACEKiAgIAACwoAIAEQqYCAgAALEgAgASACIAMgBCAFEKqAgIAACw4AIAEgAiADEKuAgIAACw4AIAEgAiADEKyAgIAACwwAIAEgAhCtgICAAAvEAQEBfyOAgICAAEEgayICJICAgIAAIAJBEGogAUECEIuCgIAAAkACQCACKAIQQQFHDQAgAigCFCEBDAELIAIgAigCFDYCCCACIAJBGGotAAA6AAwgAiACQQhqQdyTwIAAQQogAEEQahC+gYCAACIBNgIQIAENACACQRBqELyAgIAAIAIgAkEIaiAAEN6BgIAAIgE2AhAgAQ0AIAJBEGoQvICAgAAgAigCCCACLQAMEN+BgIAAIQELIAJBIGokgICAgAAgAQuWAQEBfyOAgICAAEEgayICJICAgIAAIAJBEGogAUEBEIuCgIAAAkACQCACKAIQQQFHDQAgAigCFCEBDAELIAIgAigCFDYCCCACIAJBGGotAAA6AAwgAiACQQhqIAAQ3oGAgAAiATYCECABDQAgAkEQahC8gICAACACKAIIIAItAAwQ34GAgAAhAQsgAkEgaiSAgICAACABC6kEAwF/A34BfyOAgICAAEGgAWsiAySAgICAACADQcAAahDXhICAAAJAIAMpA0BCgYCA9d246+Q1VCADQcAAakEIaikDACIEQjZUIARCNlEbDQAgA0HgAGpBCGogAkEIaigCADYCACADIAIpAgA3A2AgA0HQAGogA0HgAGoQrIGAgAAgA0EoaiABIANB0ABqEPCBgIAAIAMoAighAiADKQMwIQQgA0E4aikDACEFIANBGGoQ14SAgAACQAJAIARCACACGyIEIAMpAxh8IgYgBFQiByAFQgAgAhsiBSADQRhqQQhqKQMAfCAHrXwiBCAFVCAEIAVRGw0AIAZCgICAi6LHlJtKfCIFIAZWIAQgBkKAgID13bjr5DVUrX1CSnwiBiAEViAGIARRGw0BIAMgBTcDYCADIAY3A2ggAyABIANB0ABqIANB4ABqEPOBgIAAIANBkAFqENOEgIAAIANB4ABqIANBkAFqEO2EgIAAIANB8ABqQQhqIANB0ABqQQhqKAIANgIAIAMgAykDUDcDcCADQYABahDThICAACADQZABakHklMCAAEEeELKBgIAAIAAgA0HgAGogA0HwAGpCgICA9d246+Q1QjYgA0GAAWogA0GQAWoQ84SAgAAgA0GgAWokgICAgAAPC0GwgsCAAEEcQcSUwIAAEI2GgIAAAAtBwIHAgABBIUHUlMCAABCNhoCAAAALQfiTwIAAQTpBtJTAgAAQoYWAgAAAC4sFAgF/An4jgICAgABBwAFrIgMkgICAgAAgA0HQAGoQ1YSAgAAgA0HgAGoQ04SAgAACQAJAIANB0ABqIANB4ABqEMaBgIAARQ0AIANB4ABqEL6AgIAAIANB0ABqEL6AgIAAIANBMGogAhDPhYCAACADKAIwIAMoAjQQ7ISAgAANAUHAlcCAAEESQdSVwIAAEKGFgIAAAAsgAyADQdAAajYCoAEgAyADQeAAajYCpAEgA0GoAWpBFGpBADYCACADQayDwIAANgK4ASADQgE3AqwBIANBqJXAgAA2AqgBIANByABqIANBoAFqQYmAgIAAEKaFgIAAIAMpA0ghBCADQcAAaiADQaQBakGJgICAABCmhYCAACADKQNAIQUgA0E4aiADQagBakGIgICAABCShYCAACADQfAAakEUakEDNgIAIAMgBTcDkAEgAyAENwOIASADQgM3AnQgA0H8kcCAADYCcCADIAMpAzg3A5gBIAMgA0GIAWo2AoABIANB8ABqQbCVwIAAEOaFgIAAAAsgA0GoAWoQ1ISAgAAgA0EYaiABIANBqAFqEPSBgIAAIANBCGogAykDGCADKQMgIANBKGopAwBB/JXAgAAQqoGAgAAgA0EIakEIaikDACEEIAMpAwghBSADQagBahC+gICAACADQYgBahDThICAACADQagBaiADQYgBahDthICAACADQfAAahDUhICAACADQYgBaiADQagBaiADQfAAahD0hICAACADQYgBahCYgYCAACADQYgBakEIaiACQQhqKAIANgIAIAMgAikCADcDiAEgA0GoAWogA0GIAWoQ7YSAgAAgACADQagBaiAFIAQQ8YSAgAAgA0HAAWokgICAgAALvQgCAX8CfiOAgICAAEHwAWsiBCSAgICAACAEQeABahDVhICAACAEQaABahDThICAAAJAIARB4AFqIARBoAFqEMaBgIAARQ0AIARBoAFqEL6AgIAAIARB4AFqEL6AgIAAIARBOGogAhDPhYCAAAJAIAQoAjggBCgCPBDshICAAEUNACAEQYgBahDUhICAACAEQSBqIAEgBEGIAWoQ9IGAgAAgBEEQaiAEKQMgIAQpAyggBEEwaikDAEHslsCAABCqgYCAACAEQRBqQQhqKQMAIQUgBCkDECEGIARBiAFqEL6AgIAAIARB8ABqQQhqIAJBCGooAgA2AgAgBCACKQIANwNwIARBiAFqIARB8ABqEO2EgIAAIARB8ABqIARBiAFqEO+EgIAAIARBiAFqQQhqIANBCGooAgA2AgAgBCADKQIANwOIASAEQdgAaiAEQYgBahCsgYCAACAEQYgBaiAEQfAAaiAEQdgAahDyhICAACAEQaABaiAEQYgBaiAGIAUQ8YSAgAAgBCAGIAUQ/YSAgAAgBEEIaikDACEFIAQpAwAhBiAEQbABahDThICAACAEIAU3A3ggBCAGNwNwIARB2ABqQYABEOqAgIAAIAQgBEHYAGo2AtABIAQgBEHwAGogBEHQAWoQwIKAgAAiAjYC4AECQAJAIAINACAEQeABahC8gICAAEEAIQIgBEEANgLAASAEQcABahC8gICAACAEQZQBaiAEQdgAakEIaigCADYCACAEIAQpA1g3AowBDAELIAQgAjYCjAEgBEHYAGoQvoCAgABBASECCyAEIAI2AogBIARBwAFqIARBiAFqQeSSwIAAQTdBnJPAgAAQq4GAgAAgBEHYAGogBEGwAWoQiYaAgAAgBEGIAWogBEHYAGoQ7YSAgAAgBEHQAWpBvpPAgABBHhCygYCAACAEQeABakEIaiAEQcABakEIaigCADYCACAEIAQpA8ABNwPgASAEQdgAaiAEQYgBaiAEQdABaiAEQeABakIAQgBCgIDpg7HeFhDwhICAACAAIARBoAFqIARB2ABqEPWEgIAAIARBsAFqEL6AgIAAIARB8AFqJICAgIAADwtBwJXAgABBEkHclsCAABChhYCAAAALIAQgBEHgAWo2AsABIAQgBEGgAWo2AtABIARBiAFqQRRqQQA2AgAgBEGsg8CAADYCmAEgBEIBNwKMASAEQcSWwIAANgKIASAEQdAAaiAEQcABakGJgICAABCmhYCAACAEKQNQIQUgBEHIAGogBEHQAWpBiYCAgAAQpoWAgAAgBCkDSCEGIARBwABqIARBiAFqQYiAgIAAEJKFgIAAIARB2ABqQRRqQQM2AgAgBCAGNwN4IAQgBTcDcCAEQgM3AlwgBEH8kcCAADYCWCAEIAQpA0A3A4ABIAQgBEHwAGo2AmggBEHYAGpBzJbAgAAQ5oWAgAAAC/8FAgF/An4jgICAgABB0AFrIgQkgICAgAAgBEEoaiACEM+FgIAAAkAgBCgCKCAEKAIsEOyEgIAARQ0AIARBGGoQ14SAgAAgBEEYakEIaikDACEFIAQpAxghBiAEQaABakEIaiACQQhqKAIANgIAIAQgAikCADcDoAEgBEHwAGogBEGgAWoQ7YSAgAAgBEGgAWogBEHwAGoQ74SAgAAgBEHwAGpBCGogA0EIaigCADYCACAEIAMpAgA3A3AgBEHAAGogBEHwAGoQrIGAgAAgBEHwAGogBEGgAWogBEHAAGoQ8oSAgAAgBEEwaiAEQfAAaiAGIAUQ8YSAgAAgBEHQAGoQ1YSAgAAgBEEIaiAGIAUQ/YSAgAAgBEEIakEIaikDACEFIAQpAwghBiAEQeAAahDThICAACAEQYgBaiAEQdAAakEIaigCADYCACAEIAU3A3ggBCAGNwNwIAQgBCkDUDcDgAEgBEHAAGpBgAEQ6oCAgAAgBCAEQcAAajYCsAEgBCAEQfAAaiAEQbABahC/goCAACICNgLAASAEQYABaiEDAkACQCACDQAgBEHAAWoQvICAgABBACECIARBADYCkAEgBEGQAWoQvICAgAAgBEGsAWogBEHAAGpBCGooAgA2AgAgBCAEKQNANwKkAQwBCyAEIAI2AqQBIARBwABqEL6AgIAAQQEhAgsgBCACNgKgASAEQZABaiAEQaABakHkksCAAEE3QZyTwIAAEKuBgIAAIARBwABqIARB4ABqEImGgIAAIARBoAFqIARBwABqEO2EgIAAIARBsAFqQayTwIAAQRIQsoGAgAAgBEHAAWpBCGogBEGQAWpBCGooAgA2AgAgBCAEKQOQATcDwAEgBEHAAGogBEGgAWogBEGwAWogBEHAAWpCAEIAQoCA6YOx3hYQ8ISAgAAgAxC+gICAACAAIARBMGogBEHAAGoQ9YSAgAAgBEHgAGoQvoCAgAAgBEHQAWokgICAgAAPC0HAlcCAAEESQfyWwIAAEKGFgIAAAAvgAwECfyOAgICAAEGgAWsiBCSAgICAACAEQTBqENWEgIAAIARBwABqENOEgIAAAkACQCAEQTBqIARBwABqEMaBgIAARQ0AIARBwABqEL6AgIAAIARBMGoQvoCAgAACQBCMgoCAACIFDQAgBEHoAGpBCGogAUEIaigCADYCACAEIAEpAgA3A2ggBEGIAWogBEHoAGoQ7YSAgAAgBEEIaiACIAMQ/oSAgAAgBEHoAGogBEGIAWogBCkDCCAEQQhqQQhqKQMAEPGEgIAAIARB6ABqEJiBgIAADAILIAEQvoCAgAAMAQsgBCAEQTBqNgKAASAEIARBwABqNgKEASAEQYgBakEUakEANgIAIARBrIPAgAA2ApgBIARCATcCjAEgBEG8l8CAADYCiAEgBEEoaiAEQYABakGJgICAABCmhYCAACAEKQMoIQIgBEEgaiAEQYQBakGJgICAABCmhYCAACAEKQMgIQMgBEEYaiAEQYgBakGIgICAABCShYCAACAEQdAAakEUakEDNgIAIAQgAzcDcCAEIAI3A2ggBEIDNwJUIARB/JHAgAA2AlAgBCAEKQMYNwN4IAQgBEHoAGo2AmAgBEHQAGpBxJfAgAAQ5oWAgAAACyAEQaABaiSAgICAACAFC5QEAQJ/I4CAgIAAQbABayIDJICAgIAAIANBwABqENWEgIAAIANB0ABqENOEgIAAAkAgA0HAAGogA0HQAGoQxoGAgABFDQAgA0HQAGoQvoCAgAAgA0HAAGoQvoCAgAACQAJAEIyCgIAAIgQNACADQfgAahDUhICAACADQRhqIAEgAhD+hICAACADIANBIGopAwA3A6ABIAMgAykDGDcDmAEgAyAAIANB+ABqIANBmAFqEPOBgIAAIANB+ABqEL6AgIAADAELIANB+ABqENOEgIAAIANBmAFqIANB+ABqEO2EgIAAIANB4ABqENSEgIAAIANB+ABqIANBmAFqIANB4ABqEPSEgIAAIANB+ABqEJiBgIAACyADQbABaiSAgICAACAEDwsgAyADQcAAajYCkAEgAyADQdAAajYClAEgA0GYAWpBFGpBADYCACADQayDwIAANgKoASADQgE3ApwBIANBvJfAgAA2ApgBIANBOGogA0GQAWpBiYCAgAAQpoWAgAAgAykDOCEBIANBMGogA0GUAWpBiYCAgAAQpoWAgAAgAykDMCECIANBKGogA0GYAWpBiICAgAAQkoWAgAAgA0HgAGpBFGpBAzYCACADIAI3A4ABIAMgATcDeCADQgM3AmQgA0H8kcCAADYCYCADIAMpAyg3A4gBIAMgA0H4AGo2AnAgA0HgAGpB1JfAgAAQ5oWAgAAAC9YDAwJ/AX4BfyOAgICAAEHAAWsiACSAgICAABDQhICAAEEBQeSXwIAAEM6EgIAAIABB4ABqENaEgIAAIABBIGogAEHgAGpBsJnAgABBKkHcmcCAABCpgYCAACAAQQhqIABBIGoQz4WAgAAgAEHgAGogACgCCCAAKAIMELCAgIAAAkAgACgCYEEBRw0AIAAgACgCZDYCoAFB7JnAgABBJiAAQaABakHYhcCAAEHcmcCAABCqhoCAAAALIABBoAFqQQhqIABB7ABqKAIAIgE2AgAgACAAKQJkIgI3A6ABIABBEGpBCGoiAyABNgIAIAAgAjcDECAAQSBqEL6AgIAAIABB4ABqEPyBgIAAIABBIGogAEHgAGoQpoGAgAAgAEHgAGpBCGogAygCADYCACAAIAApAxA3A2AgAEGgAWogAEEgaiAAQeAAahDBgoCAACAAQeAAaiAAQaABahDpgICAACAAQbABaiAAQeAAakGSmsCAAEEwQdyZwIAAEKuBgIAAIAAgAEGwAWoQz4WAgAAgACgCACAAKAIEEOeEgIAAIABBIGoQ/YGAgAAgAEGwAWoQvoCAgAAgAEGgAWoQmIGAgAAgAEEgahCUgYCAACAAQcABaiSAgICAAAuHBAMCfwF+AX8jgICAgABB0AFrIgAkgICAgAAQ0ISAgABBAUHkl8CAABDOhICAACAAQRBqENeEgIAAAkACQCAAKQMQIABBGGopAwCEQgBSDQAgAEHwAGoQ1oSAgAAgAEEwaiAAQfAAakGwmcCAAEEqQdyZwIAAEKmBgIAAIABBCGogAEEwahDPhYCAACAAQfAAaiAAKAIIIAAoAgwQsoCAgAAgACgCcEEBRg0BIABBsAFqQQhqIABB/ABqKAIAIgE2AgAgACAAKQJ0IgI3A7ABIABBIGpBCGoiAyABNgIAIAAgAjcDICAAQTBqEL6AgIAAIABB8ABqEPyBgIAAIABBMGogAEHwAGoQpoGAgAAgAEHwAGpBCGogAygCADYCACAAIAApAyA3A3AgAEGwAWogAEEwaiAAQfAAahDCgoCAACAAQfAAaiAAQbABahDpgICAACAAQcABaiAAQfAAakGSmsCAAEEwQdyZwIAAEKuBgIAAIAAgAEHAAWoQz4WAgAAgACgCACAAKAIEEOeEgIAAIABBMGoQ/YGAgAAgAEHAAWoQvoCAgAAgAEGwAWoQmIGAgAAgAEEwahCUgYCAACAAQdABaiSAgICAAA8LQcKawIAAQR0Qz4SAgAAACyAAIAAoAnQ2ArABQeyZwIAAQSYgAEGwAWpB2IXAgABB3JnAgAAQqoaAgAAAC+oEAwF/An4CfyOAgICAAEHgAWsiACSAgICAABDQhICAAEEBQeSXwIAAEM6EgIAAIABBEGoQ14SAgAACQAJAIAApAxAgAEEYaikDAIRCAFINACAAQYABahDWhICAACAAQcABaiAAQYABakGwmcCAAEEqQdyZwIAAEKmBgIAAIABBCGogAEHAAWoQz4WAgAAgAEGAAWogACgCCCAAKAIMELSAgIAAIAAoAoABQQFGDQEgAEHQAGogAEGAAWpBFGopAgA3AwAgAEHAAGpBCGogAEGMAWopAgAiATcDACAAIAApAoQBIgI3A0AgAEEgakEIaiIDIAE+AgAgACACNwMgIABBMGpBCGoiBCAAQcAAakEUaigCADYCACAAIAApAkw3AzAgAEHAAWoQvoCAgAAgAEGAAWoQ/IGAgAAgAEHAAGogAEGAAWoQpoGAgAAgAEHQAWpBCGogAygCADYCACAAIAApAyA3A9ABIABBgAFqQQhqIAQoAgA2AgAgACAAKQMwNwOAASAAQcABaiAAQcAAaiAAQdABaiAAQYABahDDgoCAACAAQYABaiAAQcABahDpgICAACAAQdABaiAAQYABakGSmsCAAEEwQdyZwIAAEKuBgIAAIAAgAEHQAWoQz4WAgAAgACgCACAAKAIEEOeEgIAAIABBwABqEP2BgIAAIABB0AFqEL6AgIAAIABBwAFqEJiBgIAAIABBwABqEJSBgIAAIABB4AFqJICAgIAADwtBwprAgABBHRDPhICAAAALIAAgACgChAE2AkBB7JnAgABBJiAAQcAAakHYhcCAAEHcmcCAABCqhoCAAAALqwQDAX8CfgJ/I4CAgIAAQdABayIAJICAgIAAENCEgIAAQQFB5JfAgAAQzoSAgAAgAEHwAGoQ1oSAgAAgAEGwAWogAEHwAGpBsJnAgABBKkHcmcCAABCpgYCAACAAQQhqIABBsAFqEM+FgIAAIABB8ABqIAAoAgggACgCDBC4gICAAAJAIAAoAnBBAUcNACAAIAAoAnQ2AjBB7JnAgABBJiAAQTBqQdiFwIAAQdyZwIAAEKqGgIAAAAsgAEHAAGogAEHwAGpBFGopAgA3AwAgAEEwakEIaiAAQfwAaikCACIBNwMAIAAgACkCdCICNwMwIABBEGpBCGoiAyABPgIAIAAgAjcDECAAQSBqQQhqIgQgAEEwakEUaigCADYCACAAIAApAjw3AyAgAEGwAWoQvoCAgAAgAEHwAGoQ/IGAgAAgAEEwaiAAQfAAahCmgYCAACAAQcABakEIaiADKAIANgIAIAAgACkDEDcDwAEgAEHwAGpBCGogBCgCADYCACAAIAApAyA3A3AgAEGwAWogACAAQcABaiAAQfAAahDEgoCAACAAQfAAaiAAQbABahDpgICAACAAQcABaiAAQfAAakGSmsCAAEEwQdyZwIAAEKuBgIAAIAAgAEHAAWoQz4WAgAAgACgCACAAKAIEEOeEgIAAIABBMGoQ/YGAgAAgAEHAAWoQvoCAgAAgAEGwAWoQmIGAgAAgAEEwahCUgYCAACAAQdABaiSAgICAAAubBAUCfwJ+AX8BfgF/I4CAgIAAQdABayIAJICAgIAAENCEgIAAQQFB5JfAgAAQzoSAgAAgAEEYahDXhICAAAJAAkAgACkDGCAAQSBqKQMAhEIAUg0AIABB+ABqENaEgIAAIABBOGogAEH4AGpBsJnAgABBKkHcmcCAABCpgYCAACAAQRBqIABBOGoQz4WAgAAgAEH4AGogACgCECAAKAIUELaAgIAAIAAoAnhBAUYNASAAQcABakEIaiAAQZgBaigCACIBNgIAIAAgAEGQAWopAwAiAjcDwAEgAEGIAWopAwAhAyAAQfgAakEIaiIEKQMAIQUgAEEoakEIaiIGIAE2AgAgACACNwMoIABBOGoQvoCAgAAgAEH4AGoQ/IGAgAAgAEE4aiAAQfgAahCmgYCAACAEIAYoAgA2AgAgACAAKQMoNwN4IAAgACAAQfgAaiAFIAMQxYKAgAA6AL8BIABB+ABqIABBvwFqEOyAgIAAIABBwAFqIABB+ABqQZKawIAAQTBB3JnAgAAQq4GAgAAgAEEIaiAAQcABahDPhYCAACAAKAIIIAAoAgwQ54SAgAAgAEE4ahD9gYCAACAAQcABahC+gICAACAAQThqEJSBgIAAIABB0AFqJICAgIAADwtBwprAgABBHRDPhICAAAALIAAgACgCfDYCwAFB7JnAgABBJiAAQcABakHYhcCAAEHcmcCAABCqhoCAAAALuwMCAX8CfiOAgICAAEHAAWsiACSAgICAABDQhICAAEEBQeSXwIAAEM6EgIAAIABBGGoQ14SAgAACQAJAIAApAxggAEEgaikDAIRCAFINACAAQegAahDWhICAACAAQShqIABB6ABqQbCZwIAAQSpB3JnAgAAQqYGAgAAgAEEQaiAAQShqEM+FgIAAIABB6ABqIAAoAhAgACgCFBCugICAACAAKAJoQQFGDQEgAEH4AGopAwAhASAAQfAAaikDACECIABBKGoQvoCAgAAgAEHoAGoQ/IGAgAAgAEEoaiAAQegAahCmgYCAACAAIABBKGogAiABEMaCgIAAOgCvASAAQegAaiAAQa8BahDsgICAACAAQbABaiAAQegAakGSmsCAAEEwQdyZwIAAEKuBgIAAIABBCGogAEGwAWoQz4WAgAAgACgCCCAAKAIMEOeEgIAAIABBKGoQ/YGAgAAgAEGwAWoQvoCAgAAgAEEoahCUgYCAACAAQcABaiSAgICAAA8LQcKawIAAQR0Qz4SAgAAACyAAIAAoAmw2ArABQeyZwIAAQSYgAEGwAWpB2IXAgABB3JnAgAAQqoaAgAAACxMBAX8gACABEI2CgIAAIQIgAg8LDwAgACABIAIQjoKAgAAPCxcBAX8gACABIAIgAxCPgoCAACEEIAQPCxMBAX8gACABEJCCgIAAIQIgAg8LTQEBfyOAgICAAEEQayIEJICAgIAAIARBCGogARCrhYCAACAEIAQoAgggBCgCDCACIAMQ0oKAgAAgACAEKQMANwIAIARBEGokgICAgAALygEBAX8jgICAgABBIGsiBSSAgICAACAFIAI2AgQgBSABNgIAIAUgAzYCCCAFIAQ2AgwCQCAEIANJDQACQCADRQ0AIAIgA0YNACACIANNDQEgASADaiwAAEG/f0wNAQsCQCAERQ0AIAIgBEYNACACIARNDQEgASAEaiwAAEG/f0wNAQsgACAEIANrNgIEIAAgASADajYCACAFQSBqJICAgIAADwsgBSAFQQxqNgIYIAUgBUEIajYCFCAFIAU2AhAgBUEQahD6goCAAAALZQECfyOAgICAAEEQayIDJICAgIAAIAAgAiABayICENSCgIAAIAAgACgCCCIEIAJqNgIIIANBCGogABDVgoCAACAEIAMoAghqIAMoAgwgBGsgASACENaCgIAAIANBEGokgICAgAALEQAgACAAKAIIIAEQlYOAgAALGQAgACABEKqFgIAANgIAIAAgASgCCDYCBAuTAgEBfyOAgICAAEHgAGsiBCSAgICAACAEIAE2AgggBCADNgIMAkAgASADRw0AIAAgAiABEPOGgIAAGiAEQeAAaiSAgICAAA8LIARBKGpBFGpBiICAgAA2AgAgBEE0akHVgICAADYCACAEQRBqQRRqQQM2AgAgBCAEQQhqNgJAIAQgBEEMajYCRCAEQcgAakEUakEANgIAIARCAzcCFCAEQbygwIAANgIQIARB1YCAgAA2AiwgBEGcnMCAADYCWCAEQgE3AkwgBEGIocCAADYCSCAEIARBKGo2AiAgBCAEQcgAajYCOCAEIARBxABqNgIwIAQgBEHAAGo2AiggBEEQakHwn8CAABCxhoCAABCUhoCAAAALSwEBfyOAgICAAEEQayIDJICAgIAAIANBCGogARCrhYCAACADIAMoAgggAygCDCACENiCgIAAIAAgAykDADcCACADQRBqJICAgIAAC54BAQF/I4CAgIAAQSBrIgQkgICAgAAgBCACNgIEIAQgATYCACAEIAM2AgggBCACNgIMAkAgA0UNACACIANGDQACQCACIANNDQAgASADaiwAAEG/f0oNAQsgBCAEQQxqNgIYIAQgBEEIajYCFCAEIAQ2AhAgBEEQahD8goCAAAALIAAgAiADazYCBCAAIAEgA2o2AgAgBEEgaiSAgICAAAtEAQF/I4CAgIAAQRBrIgQkgICAgAAgBEEIaiACIAMQ3IKAgAAgACABIAQoAghB9JvAgAAQ8YWAgAAgBEEQaiSAgICAAAtgAQN/AkAgAC0AAEECSQ0AIABBBGooAgAiASgCACABKAIEKAIAEYGAgIAAAAJAIAEoAgQiAigCBCIDRQ0AIAEoAgAgAyACKAIIEM6CgIAACyAAKAIEQQxBBBDOgoCAAAsLQAACQAJAIAIgAUkNACAEIAJPDQEgAiAEEI+GgIAAAAsgASACEJCGgIAAAAsgACACIAFrNgIEIAAgAyABajYCAAtiAQF/I4CAgIAAQRBrIgMkgICAgAAgAyABIAIQjoOAgABBDEEEEN2CgIAAIgFBCGogA0EIaigCADYCACABIAMpAwA3AgAgAEH0m8CAADYCBCAAIAE2AgAgA0EQaiSAgICAAAsuAQF/AkACQCAARQ0AIAAgARDNgoCAACICDQEgACABEIWGgIAAAAsgASECCyACCwkAIABBADYCAAsJACAAQQA2AgALDQBCz7iP7NPB+cS/fwsEAEEACw8AIAAoAgAgARDjgoCAAAvZAwACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAAKAIADhYBAgMEBQYHCAkKCwwNDg8QERITFBUAAQsgAUHlt8CAAEEYEMiGgIAADwsgASAAKAIEIABBCGooAgAQyIaAgAAPCyAAQQRqIAEQ54WAgAAPCyABQZW7wIAAQRgQyIaAgAAPCyABQfq6wIAAQRsQyIaAgAAPCyABQeC6wIAAQRoQyIaAgAAPCyABQce6wIAAQRkQyIaAgAAPCyABQbu6wIAAQQwQyIaAgAAPCyABQai6wIAAQRMQyIaAgAAPCyABQZW6wIAAQRMQyIaAgAAPCyABQYe6wIAAQQ4QyIaAgAAPCyABQfm5wIAAQQ4QyIaAgAAPCyABQeu5wIAAQQ4QyIaAgAAPCyABQd25wIAAQQ4QyIaAgAAPCyABQcq5wIAAQRMQyIaAgAAPCyABQbC5wIAAQRoQyIaAgAAPCyABQfK4wIAAQT4QyIaAgAAPCyABQd64wIAAQRQQyIaAgAAPCyABQbq4wIAAQSQQyIaAgAAPCyABQay4wIAAQQ4QyIaAgAAPCyABQZm4wIAAQRMQyIaAgAAPCyABQf23wIAAQRwQyIaAgAALFAAgACgCACAAKAIEIAEQr4WAgAALDwAgACgCACABEMSGgIAACxwAIAAoAgAgACgCBCABKAIAIAEoAgQQ54KAgAALMAEBf0EAIQQCQAJAIAEgA0cNACAAIAJHDQFBASEECyAEDwsgACACIAEQ9IaAgABFCxAAIAAgAjYCBCAAIAE2AgALEAAgACACNgIEIAAgATYCAAtxAQF/I4CAgIAAQSBrIgIkgICAgAAgAiAANgIEIAJBCGpBEGogAUEQaikCADcDACACQQhqQQhqIAFBCGopAgA3AwAgAiABKQIANwMIIAJBBGpBhJ7AgAAgAkEIahCfhoCAACEBIAJBIGokgICAgAAgAQs1AQF/I4CAgIAAQRBrIgEkgICAgAAgAUEIaiAAENWCgIAAIAAQ8oKAgAAgAUEQaiSAgICAAAsCAAtIAQF/AkAgACgCACIBQQFLDQACQAJAIAEOAgABAAsgAEEIaigCACIBRQ0BIAAoAgQgAUEBEM6CgIAADwsgAEEEahDagoCAAAsLAgALGAAgACgCABDtgoCAACAAKAIAEPCCgIAACw4AIABBFEEEEM6CgIAACxgAAkAgAC0AAEUNACAAQQRqEO+CgIAACwsgAQF/AkAgACgCBCIBRQ0AIAAoAgAgAUEBEM6CgIAACwsYAAJAIAAvAQBFDQAgAEEEahDvgoCAAAsLFQACQCAAKAIARQ0AIAAQ74KAgAALCwIACwoAIAAQ64KAgAALGAACQCAALQAARQ0AIABBBGoQ74KAgAALC98BAAJAAkACQAJAIAFBgAFJDQAgAUGAEEkNASABQYCABE8NAiACIAFBP3FBgAFyOgACIAIgAUEGdkE/cUGAAXI6AAEgAiABQQx2QQ9xQeABcjoAAEEDIQEMAwsgAiABOgAAQQEhAQwCCyACIAFBP3FBgAFyOgABIAIgAUEGdkEfcUHAAXI6AABBAiEBDAELIAIgAUE/cUGAAXI6AAMgAiABQRJ2QfABcjoAACACIAFBBnZBP3FBgAFyOgACIAIgAUEMdkE/cUGAAXI6AAFBBCEBCyAAIAE2AgQgACACNgIAC3IBAX8jgICAgABBIGsiBCSAgICAACAEIAM2AhQgBCACNgIQAkACQCABIANJDQAgBEEIaiAAIAEgAxCAg4CAACAEIAQpAwg3AxhBASEDIARBEGogBEEYahDmgoCAAA0BC0EAIQMLIARBIGokgICAgAAgAwsqAQF/IAAoAgAiASgCACABKAIEIAAoAgQoAgAgACgCCCgCABCShoCAAAALGgAgACgCACAAKAIEQQAgASgCABCShoCAAAALKgEBfyAAKAIAIgEoAgAgASgCBCAAKAIEKAIAIAAoAggoAgAQkoaAgAAAC1EBBH9BACECAkACQCABKAIAIgMgASgCBEkNAAwBCyADQQEQtoOAgABqIgQgA0kNACABKAIAIQUgASAENgIAQQEhAgsgACAFNgIEIAAgAjYCAAupBAEJfyOAgICAAEEQayIHJICAgIAAIAIgBWshCCABKAIYIQkCQAJAA0ACQCAJIAVrIgogA0kNAEEAIQsgAUEANgIYDAMLAkBCASACIApqMQAAQj+DhiABKQMAg1BFDQAgASAKNgIYIAohCSAGDQEgASAFNgIgIAohCQwBCyABKAIMIgwhCwJAIAYNACABKAIgIgsgDCAMIAtLGyELCyAIIAlqIQ0gC0F/aiELAkADQAJAIAtBf0cNACAHIAUgASgCICAGGzYCDCAHIAw2AggCQAJAA0AgByAHQQhqEP2CgIAAAkAgBygCAA0AIAEgASgCGCILIAVrIg42AhgCQCAGDQAgASAFNgIgCyAAIA42AgQgAEEIaiALNgIAQQEhCwwJCyAHKAIEIgsgBU8NASABKAIYIg8gBWsgC2oiDiADTw0CIAQgC2otAAAgAiAOai0AAEYNAAsgASAPIAEoAhAiC2siCTYCGCAGDQUgASALNgIgDAULQdCfwIAAIAsgBRCMhoCAAAALQeCfwIAAIA4gAxCMhoCAAAALIAsgBU8NASAKIAtqIANPDQMgDSALaiEOIAQgC2ohDyALQX9qIQsgDy0AACAOLQAARg0ACyABIAkgDGsgC2pBAWoiCTYCGCAGDQEgASAFNgIgDAELC0Gwn8CAACALIAUQjIaAgAAAC0HAn8CAACAJIAVrIAtqIAMQjIaAgAAACyAAIAs2AgAgB0EQaiSAgICAAAsXACAAQQA2AgggACACNgIEIAAgATYCAAs9AQF/I4CAgIAAQRBrIgQkgICAgAAgBEEIakEAIAMgASACENuCgIAAIAAgBCkDCDcCACAEQRBqJICAgIAACxAAIAAgASACIAMQ54KAgAALEwAgACABIAJqNgIEIAAgATYCAAtMAQF/IAEoAgQhAgJAIAEoAgBBAUcNACAAQQE2AgAgACACNgIEDwsgAEKAgICAEDcCACAAQQhqIAI2AgAgAEEMaiABQQhqKAIANgIAC0gBAX8gASgCBCECAkAgASgCAEEBRw0AIABBATYCACAAIAI2AgQPCyAAQgA3AgAgAEEIaiACNgIAIABBDGogAUEIaigCADYCAAtIAQF/I4CAgIAAQRBrIgEkgICAgAACQCAARQ0AQaScwIAAQTcgAUEIakGQocCAAEGkncCAABCqhoCAAAALIAFBEGokgICAgAALVwEBfyOAgICAAEEQayIDJICAgIAAAkAgAkUNACAAIAI2AgQgACABNgIAIANBEGokgICAgAAPC0GgocCAAEErIANBCGpBzKHAgABBhKPAgAAQqoaAgAAACywBAX4gACkCACEBQRRBBBDdgoCAACIAQgA3AgwgACABNwIEIABBATYCACAAC2sBAX8jgICAgABBEGsiAiSAgICAACAAKAIAIQACQAJAIAFBgAFJDQAgAkEANgIMIAIgASACQQxqEPiCgIAAIAAgAigCACACKAIEEImDgIAADAELIAAgARCKg4CAAAsgAkEQaiSAgICAAEEACxEAIAAgASABIAJqENOCgIAACzwAAkAgACgCCCAAKAIERw0AIABBARDUgoCAAAsgABCqhYCAACAAKAIIaiABOgAAIAAgACgCCEEBajYCCAtoAQF/I4CAgIAAQSBrIgIkgICAgAAgACgCACEAIAJBCGpBEGogAUEQaikCADcDACACQQhqQQhqIAFBCGopAgA3AwAgAiABKQIANwMIIAAgAkEIahDqgoCAACEBIAJBIGokgICAgAAgAQsTACAAKAIAIAEgAhCJg4CAAEEACwQAIAALZgEBfyOAgICAAEEgayIDJICAgIAAIANBCGogAkEAEJaDgIAAIANBADYCGCADIAMpAwg3AxAgA0EQaiABIAIQiYOAgAAgAEEIaiADKAIYNgIAIAAgAykDEDcCACADQSBqJICAgIAAC0MBAX8jgICAgABBEGsiAiSAgICAACACQQhqIAAQq4WAgAAgAigCCCACKAIMIAEQ0IaAgAAhASACQRBqJICAgIAAIAELIAEBfwJAIAAoAgQgACgCCCIBRg0AIAAgARCRg4CAAAsLgwEBAX8CQAJAAkACQCAAKAIEIgIgAUkNAAJAIAFFDQAgAiABRg0EIAAoAgAgAkEBIAEQz4KAgAAiAg0CIAFBARCFhoCAAAALIAAQ8oKAgAAgAEEBNgIAQQAhAQwCC0G8o8CAAEEkQayiwIAAEI2GgIAAAAsgACACNgIACyAAIAE2AgQLC0cBAX8jgICAgABBEGsiAiSAgICAACABEJCDgIAAIAJBCGogASgCACABKAIEEJODgIAAIAAgAikDCDcCACACQRBqJICAgIAAC0gBAX8jgICAgABBEGsiAySAgICAACADIAI2AgwgAyABNgIIIAAgA0EIahCqhYCAADYCACAAIAMoAgw2AgQgA0EQaiSAgICAAAtBAQF/I4CAgIAAQRBrIgIkgICAgAACQCAAKAIIIAFJDQAgAkEIaiAAENWCgIAAIAAgATYCCAsgAkEQaiSAgICAAAtqAQF/I4CAgIAAQRBrIgMkgICAgAAgAyAAIAEgAkEBQQEQmIOAgAACQAJAIAMoAgBBAUcNACADQQhqKAIARQ0BQZSjwIAAQShBrKLAgAAQjYaAgAAACyADQRBqJICAgIAADwsQhoaAgAAAC6QBAQN/I4CAgIAAQRBrIgMkgICAgAACQAJAIAFBf0wNAAJAAkAgAQ0AQQEhAgwBCyADQQhqIAFBARCGg4CAACADKAIMIQQgAygCCCEFAkACQCACDQAgBSAEEM2CgIAAIQIMAQsgBSAEENCCgIAAIQILIAJFDQILIAAgATYCBCAAIAI2AgAgA0EQaiSAgICAAA8LEJeDgIAAAAsgBSAEEIWGgIAAAAsJABCGhoCAAAALhQIBAn9BACEGAkAgASgCBCIHIAJrIANPDQAgAiADaiIDIAJJIQICQAJAAkACQCAFRQ0AIAJFDQEgACADNgIEIABBCGpBADYCAAwDCyACRQ0BIAAgAzYCBCAAQQhqQQA2AgAMAgsgB0EBdCICIAMgAiADSxshAwsCQCADQX9KDQAgAEEIakEANgIADAELAkACQCAHDQAgA0EBEM2CgIAAIQIMAQsgASgCACAHQQEgAxDPgoCAACECCwJAAkAgAg0AIARFDQEgA0EBEIWGgIAAAAsgASADNgIEIAEgAjYCAAwCCyAAIAM2AgRBASEGIABBCGpBATYCAAwBC0EBIQYLIAAgBjYCAAtNAQF/I4CAgIAAQRBrIgMkgICAgAAgAyABIAIQjoOAgAAgAEEANgIAIABBDGogA0EIaigCADYCACAAIAMpAwA3AgQgA0EQaiSAgICAAAscACAAIAEpAgA3AgAgAEEIaiABQQhqKAIANgIACy0BAX8jgICAgABBEGsiASSAgICAACABQQhqIAAQ1YKAgAAgAUEQaiSAgICAAAsKACAAEPKCgIAACzUAIABBgAE6ABggAEIBNwIMIAAgASkCADcCACAAQRRqQQA2AgAgAEEIaiABQQhqKAIANgIAC4wBAgF/AX4jgICAgABBEGsiAySAgICAAAJAAkACQAJAIAAoAgAOAwABAgALIAApAwghBCADQQM6AAAgAyAENwMIDAILIAApAwghBCADQQE6AAAgAyAENwMIDAELIAApAwghBCADQQI6AAAgAyAENwMICyADIAEgAhCfg4CAACEAIANBEGokgICAgAAgAAvhAQEBfyOAgICAAEEwayIDJICAgIAAIAMgAjYCBCADIAE2AgACQAJAIAAtAABBB0YNACADQSxqQYWAgIAANgIAIANBHGpBAjYCACADQgI3AgwgA0GUvMCAADYCCCADQdaAgIAANgIkIAMgADYCICADIANBIGo2AhggAyADNgIoIANBCGoQpYOAgAAhAAwBCyADQRxqQQE2AgAgA0IBNwIMIANBxLzAgAA2AgggA0GFgICAADYCJCADIANBIGo2AhggAyADNgIgIANBCGoQpYOAgAAhAAsgA0EwaiSAgICAACAAC00BA39BACECAkAgASgCCCIDIAEoAgRPDQAgASgCACADai0AACEEQQEhAiABIANBAWo2AggLIAAgAjoAASAAQQA6AAAgAEECaiAEOgAACzwBAX8jgICAgABBEGsiAiSAgICAACACQQhqIAEgASgCCBCog4CAACAAIAIpAwg3AgAgAkEQaiSAgICAAAs4AQF/QRRBBBDdgoCAACIDIAI2AhAgAyABNgIMIANBCGogAEEIaigCADYCACADIAApAgA3AgAgAwtwAQN/I4CAgIAAQRBrIgIkgICAgAACQCABKAIIIgNBAWoiBCADSQ0AIAJBCGogASAEIAEoAgQiAyADIARLGxCog4CAACAAIAIpAwg3AgAgAkEQaiSAgICAAA8LQcCdwIAAQRxB2MDAgAAQjYaAgAAAC88CAgF/AX4jgICAgABB0ABrIgIkgICAgAAgAiAAKAIAIgA2AjQgAkEANgIYIAJCATcDECACQThqQRRqQQE2AgAgAkIBNwI8IAJBnJzAgAA2AjggAkHXgICAADYCLCACIAJBKGo2AkggAiACQTRqNgIoIAJBEGogAkE4ahDqgoCAABCFg4CAACACQRBqEJCDgIAAIAJBMGogAigCGDYCACACIAIpAxA3AyggAkEIaiAAQQxqQYSAgIAAELiDgIAAIAIpAwghAyACIABBEGpBhICAgAAQuIOAgAAgAkEQakEUakEDNgIAIAIgAzcDQCACQdiAgIAANgI8IAJCBDcCFCACQdi7wIAANgIQIAIgAikDADcDSCACIAJBKGo2AjggAiACQThqNgIgIAEgAkEQahDJhoCAACEAIAJBKGoQ64KAgAAgAkHQAGokgICAgAAgAAu5AQEBfyOAgICAAEHAAGsiASSAgICAACABIAA2AgwgAUEANgIYIAFCATcDECABQTRqQQE2AgAgAUIBNwIkIAFBnJzAgAA2AiAgAUHZgICAADYCPCABIAFBOGo2AjAgASABQQxqNgI4IAFBEGogAUEgahDqgoCAABCFg4CAACABQRBqEJCDgIAAIAFBKGogASgCGDYCACABIAEpAxA3AyAgAUEgahCmg4CAACEAIAFBwABqJICAgIAAIAALgwsCCn8BfiOAgICAAEGgAWsiASSAgICAACABQcAAaiAAEKuFgIAAIAFB2ABqIAEoAkAgASgCREGtu8CAAEEJENWGgIAAAkACQAJAAkACQCABKAJYQQFGDQAgAUHlAGotAAAhAiABQeAAaigCACEDIAFBjAFqKAIAIQQgASgCiAEhBQJAA0AgASAENgJMIAEgBTYCSCABIAM2ApwBAkAgA0UNACAEIANGDQAgBCADTQ0FIAUgA2osAABBv39MDQULIAJB/wFxIQYCQAJAIANFDQAgBSADaiIHQX9qIgItAAAiCEEYdEEYdSIJQQBODQECQAJAIAUgAkcNAEEAIQgMAQsCQCAHQX5qIgItAAAiCEHAAXFBgAFGDQAgCEEfcSEIDAELAkACQCAFIAJHDQBBACECDAELAkAgB0F9aiIKLQAAIgJBwAFxQYABRg0AIAJBD3EhAgwBCwJAAkAgBSAKRw0AQQAhBwwBCyAHQXxqLQAAQQdxQQZ0IQcLIAcgAkE/cXIhAgsgAkEGdCAIQT9xciEICyAIQQZ0IAlBP3FyIQgMAQsQqYWAgABBgIDEACEICyAGRSECAkAgBg0AIAhBgIDEAEYNAkEBIQYCQCAIQYABSQ0AQQIhBiAIQYAQSQ0AQQNBBCAIQYCABEkbIQYLIAMgBmshAwwBCwsgAUHQAGogAzYCACABIAM2AmAgASACOgBlIAEgAzYCTCABQQE2AkgMAgsgASADNgJgIAEgAjoAZSABQQA2AkgMAQsgAUHgAGohAyABQZQBaigCACEIIAFBjAFqKAIAIQYgASgCkAEhBSABKAKIASEEAkAgAUH8AGooAgBBf0cNACABQcgAaiADIAQgBiAFIAhBARD+goCAAAwBCyABQcgAaiADIAQgBiAFIAhBABD+goCAAAtBACEGQQAhCCABKAJIRQ0CAkACQAJAAkAgASgCTCIFQQlqIgIgBUkNACAFQRFqIQMCQANAIAFBOGogACADQXhqIggQ14KAgAACQAJAIAEoAjxFDQAgASgCOC0AAEFQakH/AXFBCkkNAQsgAUEwaiAAIANBeGoiBBDXgoCAAEEAIQZBACEIIAEoAjAgASgCNEG2u8CAAEEIEPmCgIAADQIMCQsgCEEBaiAISQ0DIANBAWohAwwACwsgBEEIaiIJIARJDQICQANAIAFBKGogACADENeCgIAAAkACQCABKAIsRQ0AIAEoAigtAABBUGpB/wFxQQpJDQELQQAhBkEAIQggAyAAKAIISQ0JIAFBIGogACACIAQQ0YKAgAAgAUHYAGogASgCICABKAIkEN2GgIAAIAEtAFhBAUcNAgwICyADQQFqIgggA0kNBSAIIQMMAAsLIAEoAlwhCCABQRhqIAAgCSADENGCgIAAIAFB2ABqIAEoAhggASgCHBDdhoCAACABLQBYQQFGDQUgASgCXCEGIAAoAgggBUkNBiABQRBqIAAQq4WAgAACQAJAIAVFDQAgASgCFCIDIAVGDQAgAyAFTQ0BIAEoAhAgBWosAABBQEgNAQsgACAFEJSDgIAADAcLQdyhwIAAQTBBrKLAgAAQjYaAgAAAC0HAncCAAEEcQcy8wIAAEI2GgIAAAAtBwJ3AgABBHEHcvMCAABCNhoCAAAALQcCdwIAAQRxB7LzAgAAQjYaAgAAAC0HAncCAAEEcQfy8wIAAEI2GgIAAAAsgAUHIAGogAUGcAWoQ+4KAgAAAC0EAIQZBACEICyABQdgAakEIaiAAQQhqKAIANgIAIAEgACkCADcDWCABQQhqIAFB2ABqEJKDgIAAIAEpAwghC0EUQQQQ3YKAgAAiAyAGNgIQIAMgCDYCDCADIAs3AgQgA0EANgIAIAFBoAFqJICAgIAAIAMLFwAgAEEANgIIIAAgAjYCBCAAIAE2AgAL4AEBBX8jgICAgABBEGsiAySAgICAACADQQhqIAEoAgAgASgCBCACEICDgIAAIAMoAggiBCADKAIMaiEFQQEhBgJAAkADQCAFIARrIQdBACEBAkADQCAHIAFGDQQCQCAEIAFqIgItAABBCkYNACABQQFqIgIgAUkNAiACIQEMAQsLIAZBAWoiASAGSQ0CIAJBAWohBCABIQYMAQsLQcCdwIAAQRxB+L/AgAAQjYaAgAAAC0HAncCAAEEcQYjAwIAAEI2GgIAAAAsgACABNgIEIAAgBjYCACADQRBqJICAgIAAC/AEAQZ/I4CAgIAAQTBrIgMkgICAgAADQCABKAIEIQQgASgCCCIFIQYDQAJAAkACQCAGIARJDQBBACEHDAELIAEoAgAgBmotAABBqMHAgABqLQAARQ0BQQEhBwsCQAJAIAQgBkYNAAJAAkACQAJAIAdFDQACQAJAAkAgASgCACIHIAZqLQAAIghB3ABGDQAgCEEiRw0BIAIoAgghCCADQRBqIAUgBiAHIAQQ24KAgAAgAygCFCEGIAMoAhAhBAJAIAhFDQAgAiAEIAYQiYOAgAAgASgCCCIGQQFqIgQgBkkNByABIAQ2AgggA0EIaiACEKuFgIAAIANBIGogASADKAIIIAMoAgwQqoOAgAAgACADQSBqEIODgIAADAkLIAEoAggiAkEBaiIHIAJJDQUgASAHNgIIIANBIGogASAEIAYQqoOAgAAgACADQSBqEISDgIAADAgLIANBGGogBSAGIAcgBBDbgoCAACACIAMoAhggAygCHBCJg4CAACABKAIIIgZBAWoiBCAGSQ0DIAEgBDYCCCADIAEgAhCrg4CAACIGNgIgIAYNASADQSBqEPSCgIAADAoLIAEgBkEBajYCCCADQQ82AiAgACABIANBIGoQrIOAgAAMBgsgAEEBNgIAIAAgBjYCBAwFC0GYwMCAACAGIAQQjIaAgAAAC0HAncCAAEEcQajAwIAAEI2GgIAAAAtBwJ3AgABBHEG4wMCAABCNhoCAAAALQcCdwIAAQRxByMDAgAAQjYaAgAAACyADQQQ2AiAgACABIANBIGoQrIOAgAALIANBMGokgICAgAAPCyABIAZBAWoiBjYCCAwACwsLZQEBfyOAgICAAEEgayIEJICAgIAAIAQgAiADENeGgIAAAkACQCAEKAIAQQFGDQAgACAEKQIENwIEIABBADYCAAwBCyAEQQ42AhAgACABIARBEGoQtYOAgAALIARBIGokgICAgAALxAcBA38jgICAgABBIGsiAiSAgICAACACQRBqIAAQs4OAgAACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAItABBBAUYNACACLQARIQMgAkEQahD3goCAACADQZJ/aiIEQQdNDQQgA0Gef2oiBEEETQ0FIANBIkYNASADQS9GDQMgA0HcAEYNAgwMCyACKAIUIQAMDAsgAUEiEIqDgIAAQQAhAAwLCyABQdwAEIqDgIAAQQAhAAwKCyABQS8QioOAgABBACEADAkLIAQOCAQHBwcDBwIBBAsgBA4FBQYGBgQFCyACQQhqIAAQsIOAgAACQAJAAkACQAJAAkACQAJAIAIvAQhBAUYNAAJAAkACQCACLwEKIgRBgPgDcSIDQYCwA0YNACADQYC4A0cNASACQRE2AhAgACACQRBqEK+DgIAAIQAMCgsgAkEQaiAAELODgIAAIAItABBBAUYNByACLQARIQMgAkEQahD3goCAACADQdwARw0DIAJBEGogABCzg4CAACACLQAQQQFGDQcgAi0AESEDIAJBEGoQ94KAgAAgA0H1AEcNBiACQRBqIAAQsIOAgAAgAi8BEEEBRg0HIAIvARIhAyACQRBqEPOCgIAAIANBgPgDcUGAuANHDQggBEGA0HxqIgRB//8DcSAERw0EIANBgMh8aiIDQf//A3EgA0cNBQJAIARB//8DcUEKdCADQf//A3FyQYCABGoiBEH//8MASw0AIARBgPD/P3FBgLADRw0CCyACQQ42AhAgACACQRBqEK+DgIAAIQAMCQsgBEGA8ANxQYCwA0cNACACQQ42AhAgACACQRBqEK+DgIAAIQAMCAsgAkEIahDzgoCAAEEAIQAgAkEANgIQIAIgBCACQRBqEPiCgIAAIAEgAigCACACKAIEEImDgIAADA4LIAIoAgwhAAwNCyACQRQ2AhAgACACQRBqEK+DgIAAIQAMBQtB4J3AgABBIUGow8CAABCNhoCAAAALQeCdwIAAQSFBuMPAgAAQjYaAgAAACyACQRQ2AhAgACACQRBqEK+DgIAAIQAMAgsgAigCFCEADAELIAJBETYCECAAIAJBEGoQr4OAgAAhAAsgAkEIahDzgoCAAAwGCyABQQkQioOAgABBACEADAULIAFBDRCKg4CAAEEAIQAMBAsgAUEKEIqDgIAAQQAhAAwDCyABQQwQioOAgABBACEADAILIAFBCBCKg4CAAEEAIQAMAQsgAkELNgIQIAAgAkEQahCvg4CAACEACyACQSBqJICAgIAAIAALdwECfyOAgICAAEEgayIDJICAgIAAIANBCGogARChg4CAACADKAIMIQEgAygCCCEEIANBEGpBCGogAkEIaigCADYCACADIAIpAgA3AxAgA0EQaiAEIAEQooOAgAAhAiAAQQE2AgAgACACNgIEIANBIGokgICAgAALkwIBBH8jgICAgABBEGsiASSAgICAAAJAA0AgACgCBCECIAAoAgghAwJAA0ACQAJAAkAgAyACSQ0AQQAhBAwBCyAAKAIAIANqLQAAQajBwIAAai0AAEUNAUEBIQQLAkACQCACIANGDQAgBA0BQejAwIAAIAMgAhCMhoCAAAALIAFBBDYCAAwDCwJAIAAoAgAgA2otAAAiAkHcAEYNAAJAIAJBIkYNACABQQ82AgAMBAsgACADQQFqNgIIQQAhAwwFCyAAIANBAWo2AgggASAAEK6DgIAAIgM2AgAgAw0EIAEQ9IKAgAAMAwsgACADQQFqIgM2AggMAAsLCyAAIAEQr4OAgAAhAwsgAUEQaiSAgICAACADC9oFAQR/I4CAgIAAQSBrIgEkgICAgAAgAUEQaiAAELODgIAAAkACQAJAAkACQAJAIAEtABBBAUYNACABLQARIQIgAUEQahD3goCAAEEAIQMgAkGSf2oiBEEHTQ0BIAJBnn9qIgRBBE0NAiACQSJGDQUgAkEvRg0FIAJB3ABGDQUMBAsgASgCFCEDDAQLIAQOCAMCAgIDAgMBAwsgBA4FAgEBAQICCyABQQhqIAAQsIOAgAACQAJAAkACQAJAAkACQAJAIAEvAQhBAUYNAAJAAkAgAS8BCiIEQYD4A3EiAkGAsANGDQAgAkGAuANHDQEgAUERNgIQIAAgAUEQahCvg4CAACEDDAkLIAFBEGogABCzg4CAACABLQAQQQFGDQYgAS0AESECIAFBEGoQ94KAgAAgAkHcAEcNAiABQRBqIAAQs4OAgAAgAS0AEEEBRg0GIAEtABEhAiABQRBqEPeCgIAAIAJB9QBHDQUgAUEQaiAAELCDgIAAIAEvARBBAUYNBiABLwESIQIgAUEQahDzgoCAACACQYD4A3FBgLgDRw0HIARBgNB8aiIEQf//A3EgBEcNAyACQYDIfGoiAkH//wNxIAJHDQQgBEH//wNxQQp0IAJB//8DcXJBgIAEaiEECyABQQhqEPOCgIAAAkAgBEH//8MASw0AIARBgPD/P3FBgLADRw0KCyABQQ42AhAgACABQRBqEK+DgIAAIQMMCQsgASgCDCEDDAgLIAFBFDYCECAAIAFBEGoQr4OAgAAhAwwFC0HgncCAAEEhQcjDwIAAEI2GgIAAAAtB4J3AgABBIUHYw8CAABCNhoCAAAALIAFBFDYCECAAIAFBEGoQr4OAgAAhAwwCCyABKAIUIQMMAQsgAUERNgIQIAAgAUEQahCvg4CAACEDCyABQQhqEPOCgIAADAELIAFBCzYCECAAIAFBEGoQr4OAgAAhAwsgAUEgaiSAgICAACADC2sBAn8jgICAgABBIGsiAiSAgICAACACQQhqIAAQoYOAgAAgAigCDCEAIAIoAgghAyACQRBqQQhqIAFBCGooAgA2AgAgAiABKQIANwMQIAJBEGogAyAAEKKDgIAAIQEgAkEgaiSAgICAACABC8wCAQh/I4CAgIAAQSBrIgIkgICAgAACQAJAAkACQAJAIAEoAggiA0EEaiIEIANJDQAgBCABKAIEIgVLDQNBACEEQQAhBgNAAkAgBEEERw0AIABBADsBACAAIAY7AQIMBgsgAyAEaiIHIAVPDQIgAkEIaiABKAIAIANqIARqLQAAELGDgIAAIAIvAQohCCACLwEIIQkgASAHQQFqNgIIAkAgCQ0AIAJBCzYCECAAIAEgAkEQahCyg4CAAAwGCyAGQQR0QfD/A3EgCEH//wNxaiIGQf//A3EgBkcNAyAEQQFqIQQMAAsLQcCdwIAAQRxB+MDAgAAQjYaAgAAAC0GIwcCAACAHIAUQjIaAgAAAC0HAncCAAEEcQZjBwIAAEI2GgIAAAAsgASAFNgIIIAJBBDYCECAAIAEgAkEQahCyg4CAAAsgAkEgaiSAgICAAAskACAAIAFB/wFxQejDwIAAai0AACIBOwECIAAgAUH/AUc7AQALegECfyOAgICAAEEgayIDJICAgIAAIANBCGogARChg4CAACADKAIMIQEgAygCCCEEIANBEGpBCGogAkEIaigCADYCACADIAIpAgA3AxAgA0EQaiAEIAEQooOAgAAhAiAAQQE7AQAgAEEEaiACNgIAIANBIGokgICAgAALlwEBAX8jgICAgABBIGsiAiSAgICAACACQQhqIAEQoIOAgAACQAJAIAItAAhBAUYNAAJAAkAgAi0ACUEBRg0AIAJBBDYCECAAIAEgAkEQahC0g4CAAAwBCyAAIAItAAo6AAEgAEEAOgAACyACQQhqEPGCgIAADAELIABBAToAACAAQQRqIAIoAgw2AgALIAJBIGokgICAgAALegECfyOAgICAAEEgayIDJICAgIAAIANBCGogARChg4CAACADKAIMIQEgAygCCCEEIANBEGpBCGogAkEIaigCADYCACADIAIpAgA3AxAgA0EQaiAEIAEQooOAgAAhAiAAQQE6AAAgAEEEaiACNgIAIANBIGokgICAgAALdwECfyOAgICAAEEgayIDJICAgIAAIANBCGogARChg4CAACADKAIMIQEgAygCCCEEIANBEGpBCGogAkEIaigCADYCACADIAIpAgA3AxAgA0EQaiAEIAEQooOAgAAhAiAAQQE2AgAgACACNgIEIANBIGokgICAgAALBAAgAAtDACAAKAIAIQACQCABEMqGgIAADQACQCABEMuGgIAADQAgACABEJyGgIAADwsgACABEOGGgIAADwsgACABENuGgIAACxAAIAAgAjYCBCAAIAE2AgALUgEBfwJAAkAgACABaiICIABJDQAgAkF/aiIAIAJLDQEgACABbg8LQeDGwIAAQRxBxMbAgAAQjYaAgAAAC0GQx8CAAEEhQfzGwIAAEI2GgIAAAAsOACAAQYCABBC5g4CAAAsMACAAQQQQuYOAgAALAgALKwACQCAAQf//A3EgAEYNAEHQx8CAAEEhQbTHwIAAEI2GgIAAAAsgAEEQdAstAAJAIABB/////wNxIABGDQBB0MfAgABBIUG0x8CAABCNhoCAAAALIABBAnQLJQACQCAAQXhqIABLDQAPC0GQx8CAAEEhQZTIwIAAEI2GgIAAAAt5AQJ/AkAgACgCACIBQXxxIgJFDQAgAUECcQ0AIAIgAigCBEEDcSAAKAIEQXxxcjYCBAsCQCAAKAIEIgJBfHEiAUUNACABIAEoAgBBA3EgACgCAEF8cXI2AgAgACgCBCECCyAAIAJBA3E2AgQgACAAKAIAQQNxNgIACzYBAX8CQCAAKAIAQXxxIgEgAGtBeGoiACABSw0AIAAPC0GQx8CAAEEhQYDJwIAAEI2GgIAAAAuSAgEBfyACEL6DgIAAIQQCQAJAAkACQCADQcAAaiICIANJDQAgAkH/////AXEgAkcNASAEIAJBA3QiAiAEIAJLGyICQQhqIgMgAkkNAgJAAkAgAxC6g4CAACIDQAAiAkF/Rw0AQQEhAwwBCyACQf//A3EgAkcNBCADEL2DgIAAIgQQv4OAgAAgAkEQdCICQgA3AwBBACEDIAJBADYCCCACIAIgBGpBAnI2AgALIAAgAjYCBCAAIAM2AgAPC0HgxsCAAEEcQfTHwIAAEI2GgIAAAAtB0MfAgABBIUGEyMCAABCNhoCAAAALQeDGwIAAQRxB9MfAgAAQjYaAgAAAC0HQx8CAAEEhQdjKwIAAEI2GgIAAAAsEAEEQCwQAQQELpgUBCX8gAUF/aiIFQX9zIQYgBSABSyEHIAIoAgAhAUEAIQgCQANAIAFFDQECQANAAkAgASgCCCIJQQFxDQAgAUEIaiEJIAAQvoOAgAAhCiABEMGDgIAAIApJDQICQAJAAkACQAJAAkAgASgCAEF8cSILIAprIgwgC0sNACAHDQEgAyAAIAQoAhARgICAgAAAEL6DgIAAIQ0gCUEIaiIKIAlJDQIgCiANaiINIApJDQMCQAJAIA0gDCAGcSIKTQ0AIAUgCXENCiACIAEoAghBfHE2AgAgASEJDAELIApBeGoiCSAKSw0FIAsgCWsiAiALSw0GIAIQv4OAgAAgCUEANgIIIAlCADcCACAJIAEoAgBBfHE2AgACQCABKAIAIgpBfHEiAkUNACAKQQJxDQAgAiACKAIEQQNxIAlyNgIECyAJIAkoAgRBA3EgAXI2AgQgASABKAIIQX5xNgIIIAEgASgCAEEDcSAJciICNgIAIAJBAnFFDQAgASACQX1xNgIAIAkgCSgCAEECcjYCAAsgCSAJKAIAQQFyNgIAIAlBCGohCAwKC0GQx8CAAEEhQZDJwIAAEI2GgIAAAAtBkMfAgABBIUGgycCAABCNhoCAAAALQeDGwIAAQRxBsMnAgAAQjYaAgAAAC0HgxsCAAEEcQbDJwIAAEI2GgIAAAAtBkMfAgABBIUHAycCAABCNhoCAAAALQZDHwIAAQSFB0MnAgAAQjYaAgAAACyABIAlBfnE2AggCQAJAIAEoAgRBfHEiCQ0AQQAhCQwBC0EAIAkgCS0AAEEBcRshCQsgARDAg4CAAAJAIAEtAABBAnFFDQAgCSAJKAIAQQJyNgIACyACIAk2AgAgCRDBg4CAABogCSEBDAALCyACIAkoAgAiATYCAAwACwsgCAvMAQECfyOAgICAAEEQayIDJICAgIAAIAJBASACGyECAkACQCABRQ0AIAEQu4OAgAAhBCADIAAoAgA2AgwCQCAEIAIgA0EMakHgycCAAEHgycCAABDFg4CAACIBDQAgA0HgycCAACAEIAIQwoOAgABBACEBIAMoAgANACADKAIEIgEgAygCDDYCCCADIAE2AgwgBCACIANBDGpB4MnAgABB4MnAgAAQxYOAgAAhAQsgACADKAIMNgIADAELIAIhAQsgA0EQaiSAgICAACABC+cBAQN/AkAgAUUNACACRQ0AIAIQu4OAgAAaIAAoAgAhBCABQXhqIgIgAigCAEF+cTYCACACEMGDgIAAGiABQQA2AgACQAJAAkACQCABQXxqKAIAQXxxIgVFDQAgBS0AAEEBcUUNAQsgAigCACIFQXxxIgZFDQEgBUECcQ0BIAYtAABBAXENASABIAYoAghBfHE2AgAgBiACQQFyNgIIIAQhAgwCCyACEMCDgIAAAkAgAi0AAEECcUUNACAFIAUoAgBBAnI2AgALIAUQwYOAgAAaIAQhAgwBCyABIAQ2AgALIAAgAjYCAAsLUwECfyAAIAEoAgwgASgCCCICayIDEI2FgIAAIAAoAgggABCbhYCAAGogAiADEPOGgIAAGiABIAEoAgw2AgggACAAKAIIIANqNgIIIAEQyYOAgAALbgEBfyOAgICAAEEgayIBJICAgIAAIAEgADYCGANAIAFBEGogAUEYahDMhICAACABLQAQQQFxDQALIAFBCGogACgCACAAKAIEEMuFgIAAIAEgASkDCDcDGCABQRhqENCFgIAAIAFBIGokgICAgAALOgEBf0EMQQQQy4OAgAAiAkEIaiABQQhqKAIANgIAIAIgASkCADcCACAAQejKwIAANgIEIAAgAjYCAAsuAQF/AkACQCAARQ0AIAAgARDNgoCAACICDQEgACABEIWGgIAAAAsgASECCyACCxQAIABBKDYCBCAAQZDLwIAANgIACwkAIABBADYCAAsJACAAQQA2AgALDQBCor2vyfH32uXXAAsEAEEAC0oBAX8jgICAgABBEGsiAiSAgICAACACQQhqIAFBCGooAgA2AgAgAiABKQIANwMAIAAgAhDSg4CAABDTg4CAACACQRBqJICAgIAAC9gBAwF/AX4DfyOAgICAAEEQayICJICAgIAAAkACQCAAKAIAEYKAgIAAACIADQAQwYWAgABBASEBDAELIAE1AgQhAyABKAIIIQQgASgCACEFIAIgABCLhICAACACIAIoAgQ2AgwgAiACKAIAIgA2AghBACEBIABBACAAKAIAG0HY18CAABCMhICAACIAKAIEIQYgACgCACAFKQMAIAMgBCgCACIANQIIIAAQm4WAgACtIAYoApQBEYOAgIAAACACQQhqQQRyEI2EgIAACyACQRBqJICAgIAAIAELSQEBfyOAgICAAEEQayIBJICAgIAAAkAgAEUNAEG4y8CAAEHGACABQQhqQbzRwIAAQcjMwIAAEKqGgIAAAAsgAUEQaiSAgICAAAtMAQF/I4CAgIAAQRBrIgIkgICAgAACQCAApw0AIAJBEGokgICAgAAgAQ8LQbjLwIAAQcYAIAJBCGpBvNHAgABByMzAgAAQqoaAgAAACxAAIAAQ1oOAgAAQ04OAgAALogEBAn8jgICAgABBEGsiASSAgICAAAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAQQEhAAwBCyABIAAQi4SAgAAgASABKAIENgIMIAEgASgCACICNgIIQQAhACACQQAgAigCABtBiNbAgAAQjISAgAAiAigCAEIAIAIoAgQoAiQRhICAgAAAIAFBCGpBBHIQjYSAgAALIAFBEGokgICAgAAgAAsSACAAIAEQ2IOAgAAQ04OAgAALpgEBAn8jgICAgABBEGsiAiSAgICAAAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAQQEhAAwBCyACIAAQi4SAgAAgAiACKAIENgIMIAIgAigCACIDNgIIQQAhACADQQAgAygCABtBmNfAgAAQjISAgAAiAygCACABKQMAIAMoAgQoAoQBEYSAgIAAACACQQhqQQRyEI2EgIAACyACQRBqJICAgIAAIAALQgIBfwF+I4CAgIAAQRBrIgIkgICAgAAgAiAAIAEQ2oOAgAAgAikDACACKQMIENSDgIAAIQMgAkEQaiSAgICAACADC70BAgF/An4jgICAgABBEGsiAySAgICAAAJAAkAgASgCABGCgICAAAAiAQ0AEMGFgIAAQgEhBAwBCyADIAEQi4SAgAAgAyADKAIENgIMIAMgAygCACIBNgIIQgAhBCABQQAgASgCABtBsNrAgAAQjISAgAAiASgCACACNQIEIAI1AgBCACABKAIEKAK4ARGFgICAAAAhBSADQQhqQQRyEI2EgIAACyAAIAU3AwggACAENwMAIANBEGokgICAgAALYwEBfyOAgICAAEEgayICJICAgIAAIAJBCGpBEGogAUEQaigCADYCACACQQhqQQhqIAFBCGopAgA3AwAgAiABKQIANwMIIAAgAkEIahDcg4CAABDTg4CAACACQSBqJICAgIAAC+gBAwJ/AX4DfyOAgICAAEEQayICJICAgIAAAkACQCAAKAIAEYKAgIAAACIDDQAQwYWAgABBASEBDAELIAE1AgwhBCABKAIAIQUgASgCBCEAIAEoAgghBiABKAIQIQcgAiADEIuEgIAAIAIgAigCBDYCDCACIAIoAgAiAzYCCEEAIQEgA0EAIAMoAgAbQbjXwIAAEIyEgIAAIgMoAgAgBSkDACAANQIEIAA1AgAgBjUCBCAGNQIAIAQgBykDACADKAIEKAKMARGGgICAAAAgAkEIakEEchCNhICAAAsgAkEQaiSAgICAACABCxQAIAAgASACEN6DgIAAENODgIAAC6kBAQJ/I4CAgIAAQRBrIgMkgICAgAACQAJAIAAoAgARgoCAgAAAIgANABDBhYCAAEEBIQAMAQsgAyAAEIuEgIAAIAMgAygCBDYCDCADIAMoAgAiBDYCCEEAIQAgBEEAIAQoAgAbQcjXwIAAEIyEgIAAIgQoAgAgASkDACACrSAEKAIEKAKQARGHgICAAAAgA0EIakEEchCNhICAAAsgA0EQaiSAgICAACAACxQAIAAgASACEOCDgIAAENODgIAAC6gBAQF/I4CAgIAAQSBrIgMkgICAgAAgAyACNgIUIAMgATYCEAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAIANBEGoQkISAgABBASEBDAELIANBCGogABCOhICAACADIAMoAgw2AhwgAyADKAIIIgA2AhggABCRhICAACAAIAI2AgQgACABNgIAIANBGGpBBHIQj4SAgABBACEBCyADQSBqJICAgIAAIAELYwEBfyOAgICAAEEgayICJICAgIAAIAJBCGpBEGogAUEQaikCADcDACACQQhqQQhqIAFBCGopAgA3AwAgAiABKQIANwMIIAAgAkEIahDig4CAABDTg4CAACACQSBqJICAgIAAC7UCBAJ/AX4FfwR+I4CAgIAAQSBrIgIkgICAgAACQAJAIAAoAgARgoCAgAAAIgMNABDBhYCAAEEBIQEMAQsgATUCDCEEIAEoAhQhACABKAIQIQUgASgCCCEGIAEoAgQhByABKAIAIQggAkEQaiADEIuEgIAAIAIgAigCFDYCHCACIAIoAhAiAzYCGEEAIQEgA0EAIAMoAgAbQfjXwIAAEIyEgIAAIgMoAgAhCSADKAIEIQMgCCkDACEKIAcoAgAiBzUCCCELIAcQm4WAgAAhByAGKQMAIQwgBSgCACIFNQIIIQ0gAkEIaiAFEM+FgIAAIAkgCiALIAetIAwgBCANIAI1AgggADUCBCAANQIAIAMoApwBEYiAgIAAACACQRhqQQRyEI2EgIAACyACQSBqJICAgIAAIAELSgEBfyOAgICAAEEQayICJICAgIAAIAJBCGogAUEIaigCADYCACACIAEpAgA3AwAgACACEOSDgIAAENODgIAAIAJBEGokgICAgAAL1wEBBX8jgICAgABBEGsiAiSAgICAAAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAQQEhAQwBCyABKAIIIQMgASgCBCEEIAEoAgAhBSACIAAQi4SAgAAgAiACKAIENgIMIAIgAigCACIANgIIQQAhASAAQQAgACgCABtB6NfAgAAQjISAgAAiACgCBCEGIAAoAgAgBSkDACAEKAIAIgA1AgggABCbhYCAAK0gAykDACAGKAKYARGDgICAAAAgAkEIakEEchCNhICAAAsgAkEQaiSAgICAACABCxAAIAAQ5oOAgAAQ04OAgAALogEBAn8jgICAgABBEGsiASSAgICAAAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAQQEhAAwBCyABIAAQi4SAgAAgASABKAIENgIMIAEgASgCACICNgIIQQAhACACQQAgAigCABtB6NXAgAAQjISAgAAiAigCAEIAIAIoAgQoAiARhICAgAAAIAFBCGpBBHIQjYSAgAALIAFBEGokgICAgAAgAAtEAgF/AX4jgICAgABBEGsiAySAgICAACADIAAgASACEOiDgIAAIAMpAwAgAykDCBDUg4CAACEEIANBEGokgICAgAAgBAvHAQIBfwJ+I4CAgIAAQRBrIgQkgICAgAACQAJAIAEoAgARgoCAgAAAIgENABDBhYCAAEIBIQUMAQsgBCABEIuEgIAAIAQgBCgCBDYCDCAEIAQoAgAiATYCCCABQQAgASgCABtBgNrAgAAQjISAgAAiASgCACACNQIEIAI1AgAgAzUCBCADNQIAQn4gASgCBCgCtAERiYCAgAAAIQYgBEEIakEEchCNhICAAEIAIQULIAAgBjcDCCAAIAU3AwAgBEEQaiSAgICAAAtAAgF/AX4jgICAgABBEGsiASSAgICAACABIAAQ6oOAgAAgASkDACABKQMIENSDgIAAIQIgAUEQaiSAgICAACACC7EBAgF/An4jgICAgABBEGsiAiSAgICAAAJAAkAgASgCABGCgICAAAAiAQ0AEMGFgIAAQgEhAwwBCyACIAEQi4SAgAAgAiACKAIENgIMIAIgAigCACIBNgIIIAFBACABKAIAG0Go2MCAABCMhICAACIBKAIAIAEoAgQoAqgBEYqAgIAAACEEIAJBCGpBBHIQjYSAgABCACEDCyAAIAQ3AwggACADNwMAIAJBEGokgICAgAALEgAgACABEOyDgIAAENODgIAAC6MBAQJ/I4CAgIAAQRBrIgIkgICAgAACQAJAIAAoAgARgoCAgAAAIgANABDBhYCAAEEBIQAMAQsgAiAAEIuEgIAAIAIgAigCBDYCDCACIAIoAgAiAzYCCEEAIQAgA0EAIAMoAgAbQZjWwIAAEIyEgIAAIgMoAgAgAa0gAygCBCgCQBGEgICAAAAgAkEIakEEchCNhICAAAsgAkEQaiSAgICAACAAC0ICAX8BfiOAgICAAEEQayICJICAgIAAIAIgACABEO6DgIAAIAIpAwAgAikDCBDUg4CAACEDIAJBEGokgICAgAAgAwu9AQIBfwJ+I4CAgIAAQRBrIgMkgICAgAACQAJAIAEoAgARgoCAgAAAIgENABDBhYCAAEIBIQQMAQsgAyABEIuEgIAAIAMgAygCBDYCDCADIAMoAgAiATYCCCABQQAgASgCABtB0NrAgAAQjISAgAAiASgCACACNQIEIAI1AgBCfiABKAIEKAK8ARGFgICAAAAhBSADQQhqQQRyEI2EgIAAQgAhBAsgACAFNwMIIAAgBDcDACADQRBqJICAgIAACxQAIAAgASACEPCDgIAAENODgIAAC9cBAgN/An4jgICAgABBIGsiAySAgICAAAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAQQEhAAwBCyADQRBqIAAQi4SAgAAgAyADKAIUNgIcIAMgAygCECIENgIYQQAhACAEQQAgBCgCABtBmNjAgAAQjISAgAAiBCgCACEFIAQoAgQhBCABKQMAIQYgAigCACIBNQIIIQcgA0EIaiABEM+FgIAAIAUgBiAHIAM1AgggBCgCpAERi4CAgAAAIANBGGpBBHIQjYSAgAALIANBIGokgICAgAAgAAtCAgF/AX4jgICAgABBEGsiAiSAgICAACACIAAgARDyg4CAACACKQMAIAIpAwgQ1IOAgAAhAyACQRBqJICAgIAAIAMLuAECAX8CfiOAgICAAEEQayIDJICAgIAAAkACQCABKAIAEYKAgIAAACIBDQAQwYWAgABCASEEDAELIAMgARCLhICAACADIAMoAgQ2AgwgAyADKAIAIgE2AghCACEEIAFBACABKAIAG0Gg2cCAABCMhICAACIBKAIAIAIpAwBCACABKAIEKAKsARGMgICAAAAhBSADQQhqQQRyEI2EgIAACyAAIAU3AwggACAENwMAIANBEGokgICAgAALEAAgABD0g4CAABDTg4CAAAuiAQECfyOAgICAAEEQayIBJICAgIAAAkACQCAAKAIAEYKAgIAAACIADQAQwYWAgABBASEADAELIAEgABCLhICAACABIAEoAgQ2AgwgASABKAIAIgI2AghBACEAIAJBACACKAIAG0G41cCAABCMhICAACICKAIAQgAgAigCBCgCFBGEgICAAAAgAUEIakEEchCNhICAAAsgAUEQaiSAgICAACAACxAAIAAQ9oOAgAAQ04OAgAALogEBAn8jgICAgABBEGsiASSAgICAAAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAQQEhAAwBCyABIAAQi4SAgAAgASABKAIENgIMIAEgASgCACICNgIIQQAhACACQQAgAigCABtB2NXAgAAQjISAgAAiAigCAEIAIAIoAgQoAhwRhICAgAAAIAFBCGpBBHIQjYSAgAALIAFBEGokgICAgAAgAAtEAgF/AX4jgICAgABBEGsiAySAgICAACADIAAgASACEPiDgIAAIAMpAwAgAykDCBDUg4CAACEEIANBEGokgICAgAAgBAvGAQQBfwF+AX8BfiOAgICAAEEQayIEJICAgIAAAkACQCABKAIAEYKAgIAAACIBDQAQwYWAgABCASEFDAELIAQgARCLhICAACAEIAQoAgQ2AgwgBCAEKAIAIgE2AgggAUEAIAEoAgAbQejWwIAAEIyEgIAAIgEoAgQhBiABKAIAIAIQm4WAgACtIAM1AgQgBigCeBGMgICAAAAhByAEQQhqQQRyEI2EgIAAQgAhBQsgACAHNwMIIAAgBTcDACAEQRBqJICAgIAACxQAIAAgASACEPqDgIAAENODgIAAC7IBAQN/I4CAgIAAQRBrIgMkgICAgAACQAJAIAAoAgARgoCAgAAAIgANABDBhYCAAEEBIQAMAQsgAyAAEIuEgIAAIAMgAygCBDYCDCADIAMoAgAiBDYCCEEAIQAgBEEAIAQoAgAbQdTUwIAAEIyEgIAAIgQoAgQhBSAEKAIAIAEpAwAgAhCbhYCAAK0gBSgCDBGHgICAAAAgA0EIakEEchCNhICAAAsgA0EQaiSAgICAACAACxIAIAAgARD8g4CAABDTg4CAAAuqAQECfyOAgICAAEEQayICJICAgIAAAkACQCAAKAIAEYKAgIAAACIADQAQwYWAgABBASEADAELIAIgABCLhICAACACIAIoAgQ2AgwgAiACKAIAIgM2AghBACEAIANBACADKAIAG0HA2cCAABCMhICAACIDKAIAIAE1AgQgATUCACADKAIEKAJcEYeAgIAAACACQQhqQQRyEI2EgIAACyACQRBqJICAgIAAIAALEgAgACABEP6DgIAAENODgIAAC6oBAQJ/I4CAgIAAQRBrIgIkgICAgAACQAJAIAAoAgARgoCAgAAAIgANABDBhYCAAEEBIQAMAQsgAiAAEIuEgIAAIAIgAigCBDYCDCACIAIoAgAiAzYCCEEAIQAgA0EAIAMoAgAbQeDZwIAAEIyEgIAAIgMoAgAgATUCBCABNQIAIAMoAgQoAmQRh4CAgAAAIAJBCGpBBHIQjYSAgAALIAJBEGokgICAgAAgAAtCAgF/AX4jgICAgABBEGsiAiSAgICAACACIAAgARCAhICAACACKQMAIAIpAwgQ1IOAgAAhAyACQRBqJICAgIAAIAML3wEEAX8BfgF/AX4jgICAgABBIGsiAySAgICAAAJAAkAgASgCABGCgICAAAAiAQ0AEMGFgIAAQgEhBAwBCyADQRBqIAEQi4SAgAAgAyADKAIUNgIcIAMgAygCECIBNgIYIAFBACABKAIAG0H41sCAABCMhICAACIBKAIAIQUgASgCBCEBIAIoAgAiAjUCCCEEIANBCGogAhDPhYCAACAFIAQgAzUCCCABKAJ8EYyAgIAAACEGIANBGGpBBHIQjYSAgABCACEECyAAIAY3AwggACAENwMAIANBIGokgICAgAALEgAgACABEIKEgIAAENODgIAAC6YBAQJ/I4CAgIAAQRBrIgIkgICAgAACQAJAIAAoAgARgoCAgAAAIgANABDBhYCAAEEBIQAMAQsgAiAAEIuEgIAAIAIgAigCBDYCDCACIAIoAgAiAzYCCEEAIQAgA0EAIAMoAgAbQbDZwIAAEIyEgIAAIgMoAgAgASkDACADKAIEKAKwARGEgICAAAAgAkEIakEEchCNhICAAAsgAkEQaiSAgICAACAAC0QCAX8BfiOAgICAAEEQayIDJICAgIAAIAMgACABIAIQhISAgAAgAykDACADKQMIENSDgIAAIQQgA0EQaiSAgICAACAEC+kBBAF/AX4BfwF+I4CAgIAAQSBrIgQkgICAgAACQAJAIAEoAgARgoCAgAAAIgENABDBhYCAAEIBIQUMAQsgBEEQaiABEIuEgIAAIAQgBCgCFDYCHCAEIAQoAhAiATYCGCABQQAgASgCABtBiNfAgAAQjISAgAAiASgCACEGIAEoAgQhASACKQMAIQUgAygCACICNQIIIQcgBEEIaiACEM+FgIAAIAYgBSAHIAQ1AgggASgCgAERhYCAgAAAIQcgBEEYakEEchCNhICAAEIAIQULIAAgBzcDCCAAIAU3AwAgBEEgaiSAgICAAAsUACAAIAEgAhCGhICAABDTg4CAAAuwAQECfyOAgICAAEEQayIDJICAgIAAAkACQCAAKAIAEYKAgIAAACIADQAQwYWAgABBASEADAELIAMgABCLhICAACADIAMoAgQ2AgwgAyADKAIAIgQ2AghBACEAIARBACAEKAIAG0Go18CAABCMhICAACIEKAIAIAEpAwAgAjUCBCACNQIAIAQoAgQoAogBEYuAgIAAACADQQhqQQRyEI2EgIAACyADQRBqJICAgIAAIAALFAAgACABIAIQiISAgAAQ04OAgAALvQEBA38jgICAgABBEGsiAySAgICAAAJAAkAgACgCABGCgICAAAAiAA0AEMGFgIAAQQEhAAwBCyADIAAQi4SAgAAgAyADKAIENgIMIAMgAygCACIENgIIQQAhACAEQQAgBCgCABtBiNjAgAAQjISAgAAiBCgCBCEFIAQoAgAgASkDACACKAIAIgQ1AgggBBCbhYCAAK0gBSgCoAERi4CAgAAAIANBCGpBBHIQjYSAgAALIANBEGokgICAgAAgAAtCAgF/AX4jgICAgABBEGsiAiSAgICAACACIAAgARCKhICAACACKQMAIAIpAwgQ1IOAgAAhAyACQRBqJICAgIAAIAMLtQECAX8CfiOAgICAAEEQayIDJICAgIAAAkACQCABKAIAEYKAgIAAACIBDQAQwYWAgABCASEEDAELIAMgARCLhICAACADIAMoAgQ2AgwgAyADKAIAIgE2AgggAUEAIAEoAgAbQeTUwIAAEIyEgIAAIgEoAgAgAikDACABKAIEKAIQEY2AgIAAACEFIANBCGpBBHIQjYSAgABCACEECyAAIAU3AwggACAENwMAIANBEGokgICAgAALcgECfyOAgICAAEEQayICJICAgIAAIAIgARC4hICAACIDNgIEAkAgAw0AIAJBBGoQroSAgABB9NDAgABBGCACQQhqQazRwIAAQYzRwIAAEKqGgIAAAAsgACADNgIEIAAgAUEEajYCACACQRBqJICAgIAACxwAAkAgAA0AQdDTwIAAQR0gARCohoCAAAALIAALGAAgACgCACIAIAAoAgBBf2oQ/4SAgAAaC3IBAn8jgICAgABBEGsiAiSAgICAACACIAEQtoSAgAAiAzYCBAJAIAMNACACQQRqEKaEgIAAQZHQwIAAQRAgAkEIakGc0cCAAEHk0MCAABCqhoCAAAALIAAgAzYCBCAAIAFBBGo2AgAgAkEQaiSAgICAAAsYACAAKAIAIgAgACgCAEEBahD/hICAABoLPAECfyAAKAIAIAAoAgQoAgARgYCAgAAAAkAgACgCBCIBKAIEIgJFDQAgACgCACACIAEoAggQzoKAgAALCxUAAkAgACgCAEUNACAAEJCEgIAACwuhAQMBfwF+AX8jgICAgABBMGsiAiSAgICAACACIAERgYCAgAAAIAApAgQhAyAAIAIpAwA3AgQgAEEMaiIBKAIAIQQgASACQQhqKAIANgIAIAAoAgAhASAAQQE2AgAgAkEQakEMaiAENgIAIAIgATYCECACIAM3AhQgAEEEaiEAAkAgAUUNACACQRBqQQhqEJGEgIAACyACQTBqJICAgIAAIAALHwACQCAAKAIAQQFGDQAgACABEJKEgIAADwsgAEEEagsPACAAKAIAIAEQroaAgAALqgEBAX8jgICAgABBwABrIgIkgICAgAAgAiABNgIMIAJBADYCGCACQgE3AxAgAkE0akEBNgIAIAJCATcCJCACQdjMwIAANgIgIAJB7YCAgAA2AjwgAiACQThqNgIwIAIgAkEMajYCOCACQRBqIAJBIGoQtYWAgAAQloSAgAAgAkEQahDFhYCAACAAQQhqIAIoAhg2AgAgACACKQMQNwIAIAJBwABqJICAgIAAC0gBAX8jgICAgABBEGsiASSAgICAAAJAIABFDQBB4MzAgABBNyABQQhqQczRwIAAQeDNwIAAEKqGgIAAAAsgAUEQaiSAgICAAAu9AQEDfyOAgICAAEEwayIDJICAgIAAAkAgAS0ADA0AIAEoAgQhBCABKAIAIQUgAyACEJ2FgIAAIAAgBSAEIAMoAgAgAygCBCABKAIIEImFgIAAIANBMGokgICAgAAPCyADQQhqQYTPwIAAQYGAgIAAEJOFgIAAIANBJGpBATYCACADQgE3AhQgA0HUzsCAADYCECADIAMpAwg3AyggAyADQShqNgIgIANBEGpBrM/AgAAQsYaAgAAQlIaAgAAAC6QCAgR/AX4jgICAgABB0ABrIgIkgICAgABBACEDIAJBACABKAIEEIGFgIAAIAJBIGpBCGoiBCABQQhqKQIANwMAIAIgASkCADcDICACQRBqIAJBIGogAhCXhICAACACQTBqQQhqIgEgAkEIaigCADYCACACIAIpAwA3AzACQAJAIAIoAhBBAUYNACACKAIUIQUgBCABKAIANgIAIAIgAikDMDcDICACQSBqIAUQmYSAgAAgAkHAAGpBCGogBCgCACIBNgIAIAIgAikDICIGNwNAIABBDGogATYCACAAIAY3AgQMAQsgACACKQIUNwIEIABBDGogAkEQakEMaigCADYCACACQTBqEJqEgIAAQQEhAwsgACADNgIAIAJB0ABqJICAgIAAC0EBAX8jgICAgABBEGsiAiSAgICAAAJAIAAoAgggAUkNACACQQhqIAAQnYWAgAAgACABNgIICyACQRBqJICAgIAACxIAIAAQzoWAgAAgABDQhYCAAAs9AQF/I4CAgIAAQRBrIgQkgICAgAAgBEEIakEAIAMgASACELyEgIAAIAAgBCkDCDcCACAEQRBqJICAgIAACxwAIAAoAgAgACgCBCABKAIAIAEoAgQQuYWAgAALCwAgARCehICAAAALQwEBfyOAgICAAEEgayIBJICAgIAAIAFBEGogABCVhICAACABQQhqIAFBEGoQxoSAgAAgASgCCCABKAIMEM+EgIAAAAsLACABEJ6EgIAAAAsLACABEKGEgIAAAAsLACAAEJ6EgIAAAAsCAAsYACAAQRhqEKSEgIAAIABBKGoQpISAgAALLwEBfyAAEKiEgIAAGiAAQQRqIQECQCAAKAIADQAgARCphICAAA8LIAEQqoSAgAALFwACQCAAKAIADQAgAEEEahCahICAAAsLFQACQCAAKAIARQ0AIAAQj4SAgAALCwIAC84IBAZ/AX4EfwJ+I4CAgIAAQdAAayIBJICAgIAAAkACQAJAAkACQCAAKAIAQQFGDQAgAUEoaiAAKAIEIgJBCGoQt4SAgAAgASABKAIsNgJEIAEgASgCKCIDNgJAIAFBwABqQQRyIQQgAykDAEIBUQ0CIAEgAkE8ahC4hICAACIFNgJMIAVFDQEgASAFNgI0IAEgAkHAAGoiBTYCMCABQTBqQQRyIQYCQAJAIAUoAgBBAkcNACACQSBqENmEgIAAIQcMAQsgBRCohICAACACQSBqENqEgIAAIQcLIAYQjYSAgAAgASACQSxqELiEgIAAIgU2AkwgBUUNAyABIAU2AjQgAkE4aigCAEE4bCEGIAEgAkEwaiICNgIwIAIoAgAhCCABQTBqQQRyIQlBACECA0ACQCAGIAJHDQAgAyAHNwMIIANCATcDACAJEI2EgIAADAYLAkACQAJAAkACQAJAAkACQAJAAkAgCCACaiIFKAIADgkAAQIDBAUGBwgACyAHENuEgIAADAgLIAFBCGogBUEEahDPhYCAACAHIAEoAgggASgCDBDchICAAAwHCyABQRhqIAVBBGoQz4WAgAAgASgCHCEKIAEoAhghCyABQRBqIAVBEGoQz4WAgAAgByALIAogASgCECABKAIUIAVBIGopAwAgBUEoaikDACAFQTBqKQMAEN2EgIAADAYLIAcgBUEIaikDACAFQRBqKQMAEN6EgIAADAULIAcgBUEQaikDACAFQRhqKQMAIAVBBGoQ34SAgAAMBAsgByAFQQRqQgAQ4ISAgAAMAwsgBUEwaikDACEMIAVBKGopAwAhDSABQSBqIAVBHGoQz4WAgAAgByAFQQRqQgAgDSAMIAVBEGogASgCICABKAIkEOGEgIAADAILIAcgBUEEahDihICAAAwBCyAHIAVBBGoQ44SAgAALIAJBOGohAgwACwsgASAAKAIEIgVBCGoQt4SAgAAgASABKAIENgJEIAEgASgCACICNgJAIAFBwABqQQRyIQQCQCACKQMAQgFRDQAgBUEgahCohICAACEHIAEgBUEwahCohICAADcDOCABIAc3AzAgAiABQTBqQQIQ2ISAgAAiBzcDCCACQgE3AwAMBAsgAikDCCEHDAMLIAFBzABqEK6EgIAAQfTQwIAAQRggAUEwakGs0cCAAEGM0cCAABCqhoCAAAALIAMpAwghBwwBCyABQcwAahCuhICAAEH00MCAAEEYIAFBMGpBrNHAgABBjNHAgAAQqoaAgAAACyAEEI+EgIAAIAEgAEEIahC4hICAACICNgJAAkAgAkUNACABIAI2AjQgASAAQQxqIgI2AjAgAi0AACECIAFBMGpBBHIQjYSAgAACQCACRQ0AIAcQ5oSAgAALIAFB0ABqJICAgIAAIAcPCyABQcAAahCuhICAAEH00MCAAEEYIAFBMGpBrNHAgABBjNHAgAAQqoaAgAAAC3wBAn8gACgCACIBIAEoAgBBf2o2AgACQCAAKAIAIgEoAgANACABQSBqEJqEgIAAIAFBMGoiAhDLhICAACACEMiEgIAAIAFBwABqEKuEgIAAIAAoAgAiASABKAIEQX9qNgIEIAAoAgAiACgCBA0AIABB0ABBCBDOgoCAAAsLWwEBfyAAKAIAIgEgASgCAEF/ajYCAAJAIAAoAgAiASgCAA0AIAFBCGoQo4SAgAAgACgCACIBIAEoAgRBf2o2AgQgACgCACIAKAIEDQAgAEHAAEEIEM6CgIAACwsXAAJAIAAoAgBBAkYNACAAEKSEgIAACwsCAAsCAAsVAAJAIAAoAgBFDQAgABCNhICAAAsLGAACQCAAKAIARQ0AIABBBGoQsISAgAALCzwBAn8gACgCACAAKAIEKAIAEYGAgIAAAAJAIAAoAgQiASgCBCICRQ0AIAAoAgAgAiABKAIIEM6CgIAACwsKACAAEJqEgIAACwIACwIACyoBAX8gACgCACIBKAIAIAEoAgQgACgCBCgCACAAKAIIKAIAEJKGgIAAAAtNAQF/I4CAgIAAQRBrIgQkgICAgAAgBCABNgIEIAQgADYCACAEIAM2AgwgBCACNgIIIAQgBEEIahCchICAACECIARBEGokgICAgAAgAgsjAQF/QQAhAQJAIAAoAgANACAAQX8Q/4SAgAAaIAAhAQsgAQtyAQJ/I4CAgIAAQRBrIgIkgICAgAAgAiABELaEgIAAIgM2AgQCQCADDQAgAkEEahCmhICAAEGR0MCAAEEQIAJBCGpBnNHAgABB5NDAgAAQqoaAgAAACyAAIAM2AgQgACABQQhqNgIAIAJBEGokgICAgAALJgEBfwJAIAAoAgBBAWoiAUEBTg0AQQAPCyAAIAEQ/4SAgAAaIAALUQEEf0EAIQICQAJAIAEoAgAiAyABKAIESQ0ADAELIANBARCWhYCAAGoiBCADSQ0AIAEoAgAhBSABIAQ2AgBBASECCyAAIAU2AgQgACACNgIAC1gCAn8BfkEAIQMCQAJAIAEoAgAgASgCBCIBakF/akEAIAFrcSIErSACrX4iBUIgiKdFDQAQlIWAgAAMAQsgACAENgIIIAAgBac2AgAgASEDCyAAIAM2AgQLQAACQAJAIAIgAUkNACAEIAJPDQEgAiAEEI+GgIAAAAsgASACEJCGgIAAAAsgACACIAFrNgIEIAAgAyABajYCAAtAAAJAAkAgAiABSQ0AIAQgAk8NASACIAQQj4aAgAAACyABIAIQkIaAgAAACyAAIAIgAWs2AgQgACADIAFqNgIACzMAAkAgASgCAA0AIAIgAyAEEKiGgIAAAAsgACABKQIANwIAIABBCGogAUEIaigCADYCAAuNAQEBfyOAgICAAEEgayIDJICAgIAAAkAgASgCAEEBRw0AIANBGGogAUEUaigCADYCACADQRBqIAFBDGopAgA3AwAgAyABKQIENwMIQdzRwIAAQSsgA0EIakGI0sCAACACEKqGgIAAAAsgACABKQIENwIAIABBCGogAUEMaigCADYCACADQSBqJICAgIAACzcBAX9BASEDAkAgAkEQRw0AIAAgASkAADcAASAAQQlqIAFBCGopAAA3AABBACEDCyAAIAM6AAALLQEBf0HQAEEIEMuDgIAAIgFCgYCAgBA3AwAgAUEIaiAAQcgAEPOGgIAAGiABC0YCAX8BfiOAgICAAEEQayICJICAgIAAIAJBCGogAUEAEMeFgIAAIAIpAwghAyAAQQA2AgggACADNwIAIAJBEGokgICAgAALPAACQCAAKAIIIAAoAgRHDQAgAEEBEI2FgIAACyAAEJuFgIAAIAAoAghqIAE6AAAgACAAKAIIQQFqNgIICxEAIAAgACgCCCABEMSEgIAAC2oBAX8jgICAgABBEGsiAySAgICAACADIAAgASACQQFBARDJhICAAAJAAkAgAygCAEEBRw0AIANBCGooAgBFDQFBmNLAgABBKEG8z8CAABCNhoCAAAALIANBEGokgICAgAAPCxCGhoCAAAALUAEBfyOAgICAAEEQayIDJICAgIAAIAMgAhDBhICAACADIAEgAhCYhYCAACAAQQhqIANBCGooAgA2AgAgACADKQMANwIAIANBEGokgICAgAALNwEBfyOAgICAAEEQayICJICAgIAAIAJBCGogARDPhYCAACAAIAIpAwg3AgAgAkEQaiSAgICAAAukAQECfyOAgICAAEEgayICJICAgIAAIAJBCGogARDPhYCAACACQRBqIAIoAgggAigCDBDXhoCAAEEBIQMCQAJAIAIoAhBBAUYNACAAIAEpAgA3AgQgAEEMaiABQQhqKAIANgIAQQAhAwwBCyAAIAEpAgA3AgQgAEEQaiACKQIUNwIAIABBDGogAUEIaigCADYCAAsgACADNgIAIAJBIGokgICAgAALIwEBfwJAIAAoAgQiAUUNACAAKAIAIAFBOGxBCBDOgoCAAAsLoAMBAn8jgICAgABBwABrIgYkgICAgABBACEHAkAgASgCBCACayADTw0AAkACQAJAIAVFDQAgBkEoaiABIAIgAxDKhICAAEEBIQcgBigCLCEDIAYoAihBAUcNASAGQQhqIAMgBkEwaigCABCVhYCAACAAIAYpAwg3AgQMAwsgAiADaiIDIAJPDQAgBkEgaiADQQAQlYWAgAAgACAGKQMgNwIEDAELIAZCuICAgIABNwM4IAZBKGogBkE4aiADELqEgIAAIAYoAighAgJAIAYoAiwiBw0AIAZBEGogAkEAEJWFgIAAIAAgBikDEDcCBAwBCwJAIAJBf0oNACAGQRhqIAZBABCVhYCAACAAIAYpAxg3AgQMAQsCQAJAIAEoAgQiBQ0AIAIgBxDNgoCAACEFDAELIAEoAgAgBUE4bEEIIAIQz4KAgAAhBQsCQAJAIAUNACAERQ0BIAIgBxCFhoCAAAALIAEgAzYCBCABIAU2AgBBACEHDAILIAAgAjYCBCAAQQhqIAc2AgALQQEhBwsgACAHNgIAIAZBwABqJICAgIAAC4EBAQF/I4CAgIAAQRBrIgQkgICAgAACQAJAIAIgA2oiAyACTw0AIARBCGogA0EAEJWFgIAAIAQoAgghAiAAQQhqIAQoAgw2AgBBASEDDAELIAEoAgRBAXQiAiADIAIgA0sbIQJBACEDCyAAIAM2AgAgACACNgIEIARBEGokgICAgAAL2wEBAn8gACgCCEE4bCEBIAAoAgAhAAJAA0AgAUUNAQJAAkAgACgCACICQQdLDQACQAJAAkACQAJAAkAgAg4IBwABBwIDBAUHCyAAQQRqEJqEgIAADAYLIABBBGoQmoSAgAAgAEEQahCahICAAAwFCyAAQQRqEJqEgIAADAQLIABBBGoQmoSAgAAMAwsgAEEEahCahICAACAAQRBqEJqEgIAAIABBHGoQmoSAgAAMAgsgAEEEahCahICAAAwBCyAAQQRqEJqEgIAACyAAQThqIQAgAUFIaiEBDAALCwtHAQJ/AkACQCABKAIAIgEoAggiAiABKAIMRw0AQQAhAwwBC0EBIQMgASACQQFqNgIIIAItAAAhAQsgACABOgABIAAgAzoAAAtxAgF/An4jgICAgABBEGsiASSAgICAAAJAQQApA4D6woAAIgJCAXwiAyACVA0AQQAgAzcDgPrCgAAgASACNwMIIAAgAUEIakEIEMWEgIAAIAFBEGokgICAgAAPC0HQz8CAAEEcQaTTwIAAEI2GgIAAAAsSAEG008CAACAAIAEQ34OAgAALSQEBfyOAgICAAEEQayICJICAgIAAIAIgATYCDCACIAA2AghBtNPAgAAgAkEIahD9g4CAAEGY0sCAAEEoQdDZwIAAEKGFgIAAAAsQAEEBQbjTwIAAEPWFgIAAC5cBAQF/I4CAgIAAQTBrIgIkgICAgAAgAiABNwMYIAJBCGogARDShICAAAJAAkAgAikDCKcNABCehYCAACAAQQA2AgAMAQsgAkEgakEAIAIpAxCnEIGFgIAAQbTTwIAAIAJBGGogAkEgahD5g4CAACAAQQhqIAJBIGpBCGooAgA2AgAgACACKQMgNwIACyACQTBqJICAgIAAC0oBAX8jgICAgABBEGsiAiSAgICAACACIAE3AwggAEG008CAACACQQhqEImEgIAAIgE3AwggACABQn9SrTcDACACQRBqJICAgIAAC3sBAX8jgICAgABBMGsiASSAgICAAEG008CAABDzg4CAACABQQhqQgAQ0YSAgAAgAUEgaiABQQhqQfTUwIAAQcQAQbjVwIAAEL2EgIAAIAFBCGogAUEgahDHhICAACAAIAFBCGpByNXAgAAQvoSAgAAgAUEwaiSAgICAAAtPAQF/I4CAgIAAQRBrIgEkgICAgABBtNPAgAAQ9YOAgAAgAUIAENGEgIAAIAAgAUH01MCAAEHEAEHY1cCAABC9hICAACABQRBqJICAgIAAC3sBAX8jgICAgABBMGsiASSAgICAAEG008CAABDlg4CAACABQQhqQgAQ0YSAgAAgAUEgaiABQQhqQfTUwIAAQcQAQejVwIAAEL2EgIAAIAFBCGogAUEgahDHhICAACAAIAFBCGpB+NXAgAAQvoSAgAAgAUEwaiSAgICAAAsYAEG008CAABDVg4CAACAAQgAQ0YSAgAALWwICfwF+I4CAgIAAQRBrIgEkgICAgAAgAUEIaiICQgA3AwAgAUIANwMAQbTTwIAAIAEQ64OAgAAgASkDACEDIAAgAikDADcDCCAAIAM3AwAgAUEQaiSAgICAAAuYAwIEfwF+I4CAgIAAQdAAayICJICAgIAAIAIgATYCLCACIAA2AigCQAJAAkACQAJAIAFB/////wFxIAFHDQAgAkEwakEAIAFBA3QQgYWAgAAgAkEgakEAIAEQnIWAgAAgAiACKQMgNwNAA0AgAkEYaiACQcAAahC5hICAACACKAIYRQ0FIAIoAhwiA0H/////AXEgA0cNAiADQQFqIgRB/////wFxIARHDQMgAkEQaiACQTBqEJ2FgIAAIAJBCGogA0EDdCIFIARBA3QgAigCECACKAIUELyEgIAAIAMgAU8NBCACKAIMIQMgAigCCCEEIAIgACAFaikDADcDSCAEIAMgAkHIAGpBCBCOhYCAAAwACwtB8M/AgABBIUGo1sCAABCNhoCAAAALQfDPwIAAQSFBuNbAgAAQjYaAgAAAC0Hwz8CAAEEhQcjWwIAAEI2GgIAAAAtB2NbAgAAgAyABEIyGgIAAAAtBtNPAgAAgAkEwaiACQShqEPeDgIAAIQYgAkEwahCahICAACACQdAAaiSAgICAACAGCz4CAX8BfiOAgICAAEEQayIBJICAgIAAIAEgADYCDEG008CAACABQQxqEP+DgIAAIQIgAUEQaiSAgICAACACC0UBAX8jgICAgABBEGsiAiSAgICAACACIAE2AgwgAiAANwMAQbTTwIAAIAIgAkEMahCDhICAACEAIAJBEGokgICAgAAgAAs4AQF/I4CAgIAAQRBrIgEkgICAgAAgASAANwMIQbTTwIAAIAFBCGoQ14OAgAAgAUEQaiSAgICAAAtIAQF/I4CAgIAAQRBrIgMkgICAgAAgAyACNgIMIAMgATYCCCADIAA3AwBBtNPAgAAgAyADQQhqEIWEgIAAIANBEGokgICAgAALnQEBAX8jgICAgABB0ABrIggkgICAgAAgCCAGNwMoIAggBTcDICAIIAI2AhQgCCABNgIQIAggADcDCCAIIAQ2AhwgCCADNgIYIAggBzcDMCAIIAhBMGo2AkggCCAIQSBqNgJEIAggCEEYajYCQCAIIAhBEGo2AjwgCCAIQQhqNgI4QbTTwIAAIAhBOGoQ24OAgAAgCEHQAGokgICAgAALSwEBfyOAgICAAEEgayIDJICAgIAAIAMgAjcDGCADIAE3AxAgAyAANwMIQbTTwIAAIANBCGogA0EQahDdg4CAACADQSBqJICAgIAAC2gBAX8jgICAgABBMGsiBCSAgICAACAEIAI3AxAgBCABNwMIIAQgADcDACAEIAM2AhwgBCAEQRxqNgIoIAQgBEEIajYCJCAEIAQ2AiBBtNPAgAAgBEEgahDRg4CAACAEQTBqJICAgIAAC2QBAX8jgICAgABBMGsiAySAgICAACADIAE2AhQgAyAANwMIIAMgAjcDGCADIANBGGo2AiggAyADQRRqNgIkIAMgA0EIajYCIEG008CAACADQSBqEOODgIAAIANBMGokgICAgAALpAEBAX8jgICAgABB0ABrIggkgICAgAAgCCAENwMgIAggAzcDGCAIIAE2AgwgCCAANwMAIAggAjcDECAIIAU2AiwgCCAHNgI0IAggBjYCMCAIIAhBMGo2AkwgCCAIQSxqNgJIIAggCEEYajYCRCAIIAhBEGo2AkAgCCAIQQxqNgI8IAggCDYCOEG008CAACAIQThqEOGDgIAAIAhB0ABqJICAgIAAC0EBAX8jgICAgABBEGsiAiSAgICAACACIAE2AgwgAiAANwMAQbTTwIAAIAIgAkEMahCHhICAACACQRBqJICAgIAAC0EBAX8jgICAgABBEGsiAiSAgICAACACIAE2AgwgAiAANwMAQbTTwIAAIAIgAkEMahDvg4CAACACQRBqJICAgIAACw4AQbTTwIAAEOmDgIAAC8oBAQF/I4CAgIAAQTBrIgIkgICAgAAgAiABNwMIAkACQAJAAkACQEG008CAACACQQhqEPGDgIAAIgFCAlYNACABpw4DAQIDAQtBuNjAgABBF0HQ2MCAABChhYCAAAALIABBADYCAAwCCyACQSBqQgAQ0YSAgAAgAkEQaiACQSBqQeDYwIAAQTBBkNnAgAAQvYSAgAAgAEEMaiACQRhqKAIANgIAIAAgAikDEDcCBCAAQQE2AgAMAQsgAEECNgIACyACQTBqJICAgIAACzgBAX8jgICAgABBEGsiASSAgICAACABIAA3AwhBtNPAgAAgAUEIahCBhICAACABQRBqJICAgIAACz8BAX8jgICAgABBEGsiAiSAgICAACACIAE2AgwgAiAANgIIQbTTwIAAIAJBCGoQ+4OAgAAgAkEQaiSAgICAAAuIAQIBfwF+I4CAgIAAQRBrIgQkgICAgAAgBCABNgIEIAQgADYCACAEIAM2AgwgBCACNgIIAkACQAJAQbTTwIAAIAQgBEEIahDng4CAACIFQgFWDQBBACECIAWnDgICAQILQbjYwIAAQRdB8NnAgAAQoYWAgAAAC0EBIQILIARBEGokgICAgAAgAgujAQIBfwF+I4CAgIAAQSBrIgMkgICAgAAgAyACNgIMIAMgATYCCAJAAkACQAJAQbTTwIAAIANBCGoQ2YOAgAAiBEIBVg0AIASnDgIBAgELQbjYwIAAQRdBkNrAgAAQoYWAgAAACyAAQQA2AgAMAQsgA0EQakIAENGEgIAAIAAgA0EQakH01MCAAEHEAEGg2sCAABC9hICAAAsgA0EgaiSAgICAAAt4AgF/AX4jgICAgABBEGsiAiSAgICAACACIAE2AgwgAiAANgIIAkACQAJAQbTTwIAAIAJBCGoQ7YOAgAAiA0IBVg0AQQAhACADpw4CAgECC0G42MCAAEEXQcDawIAAEKGFgIAAAAtBASEACyACQRBqJICAgIAAIAALDAAgAEJ+ENGEgIAAC80BAQV/I4CAgIAAQRBrIgIkgICAgABBACEDAkAgAUF+akE+Sw0AIAJBCGogACABEJ+FgIAAIAIoAgwhBCACKAIIIQBBASEBA0AgASEFAkAgBCAARw0AIAVBAXMhAwwCC0EAIQNBACEBAkAgAC0AACIGQZ9/akH/AXFBGkkNAEEAIQEgBkFQakH/AXFBCkkNAEEBIQEgBkFTakECSQ0AIAZB3wBGDQBBACEDDAILIABBAWohACAFIAFxRQ0ACwsgAkEQaiSAgICAACADQQFxC5cBAQF/I4CAgIAAQdAAayICJICAgIAAIAJBNGpCADcCACACQShqIAFBCGooAgA2AgAgAkKAgICAIDcCPCACQoCAgICAATcCLCACQgA3AxAgAkEANgIIIAIgASkCADcDICACQQhqEMCEgIAAIQEgAEEMakEAOgAAIABBADYCCCAAIAE2AgQgAEEANgIAIAJB0ABqJICAgIAAC6oCAQR/I4CAgIAAQcAAayIDJICAgIAAAkACQCABKAIAQQFGDQAgAyABKAIEIgRBLGoQtoSAgAAiBTYCCCAFRQ0BIAMgBTYCBCADIARBMGoiBjYCACADQQhqIAJBOBDzhoCAABogA0EEciECAkAgBEE4aigCACIFIARBNGooAgBHDQAgBkEBEMOEgIAAIAQoAjghBQsgBCgCMCAFQThsaiADQQhqQTgQ84aAgAAaIAQgBCgCOEEBajYCOCACEI+EgIAAIABBCGogAUEIaikCADcCACAAIAEpAgA3AgAgA0HAAGokgICAgAAPC0Hg2sCAAEElQeTbwIAAEKGFgIAAAAsgA0EIahCmhICAAEGR0MCAAEEQIANBCGpBnNHAgABB5NDAgAAQqoaAgAAAC1gBAX8jgICAgABB0ABrIgIkgICAgAAgAkEIakEIaiABQQhqKQIANwMAIAIgASkCADcDCCACQQA2AhggACACQQhqIAJBGGoQ7oSAgAAgAkHQAGokgICAgAALpQEBAX8jgICAgABB0ABrIgckgICAgAAgB0EIakEIaiABQQhqKQIANwMAIAcgASkCADcDCCAHQcAAaiAFNwMAIAdBJGogAkEIaigCADYCACAHQTBqIANBCGooAgA2AgAgByAENwM4IAcgBjcDSCAHQQI2AhggByACKQIANwIcIAcgAykCADcDKCAAIAdBCGogB0EYahDuhICAACAHQdAAaiSAgICAAAtvAQF/I4CAgIAAQdAAayIEJICAgIAAIARBCGpBCGogAUEIaikCADcDACAEIAEpAgA3AwggBEEoaiADNwMAIARBGGpBCGogAjcDACAEQQM2AhggACAEQQhqIARBGGoQ7oSAgAAgBEHQAGokgICAgAALcgEBfyOAgICAAEHQAGsiAySAgICAACADQQhqQQhqIAFBCGopAgA3AwAgAyABKQIANwMIIANBJGogAkEIaigCADYCACADQQU2AhggAyACKQIANwIcIAAgA0EIaiADQRhqEO6EgIAAIANB0ABqJICAgIAAC7gBAQF/I4CAgIAAQdAAayIHJICAgIAAIAdBCGpBCGogAUEIaikCADcDACAHIAEpAgA3AwggB0HIAGogBDcDACAHQSRqIAJBCGooAgA2AgAgB0EwaiAFQQhqKAIANgIAIAdBPGogBkEIaigCADYCACAHIAM3A0AgB0EGNgIYIAcgAikCADcCHCAHIAUpAgA3AyggByAGKQIANwI0IAAgB0EIaiAHQRhqEO6EgIAAIAdB0ABqJICAgIAAC3IBAX8jgICAgABB0ABrIgMkgICAgAAgA0EIakEIaiABQQhqKQIANwMAIAMgASkCADcDCCADQSRqIAJBCGooAgA2AgAgA0EHNgIYIAMgAikCADcCHCAAIANBCGogA0EYahDuhICAACADQdAAaiSAgICAAAuFAgEDfyOAgICAAEEgayIDJICAgIAAAkACQCACKAIAQQFGDQAgA0EIaiIEIAFBCGopAgA3AwAgAyABKQIANwMAIAMgAigCBCIBQTxqELaEgIAAIgU2AhwgBUUNASADIAU2AhQgAyABQcAAaiIFNgIQIAUQq4SAgAAgAUHIAGogBCkDADcCACAFIAMpAwA3AgAgA0EQakEEchCPhICAACAAQQhqIAJBCGopAgA3AgAgACACKQIANwIAIANBIGokgICAgAAPC0H028CAAEEeQZTcwIAAEKGFgIAAAAsgA0EcahCmhICAAEGR0MCAAEEQIANBEGpBnNHAgABB5NDAgAAQqoaAgAAACwsAIAAQqISAgAAaC+cBAQN/I4CAgIAAQTBrIgIkgICAgAAgAkEYaiABEM+FgIAAIAJBIGogAigCGCACKAIcEIiGgIAAIAJBEGogAkEgahDPhYCAAAJAAkAgAigCECIDIAIoAhQiBEGk3MCAAEEHELWEgIAARQ0AQQAhAyAAQQA6AAEMAQsCQCADIARBq9zAgABBCRC1hICAAEUNACAAQQE6AAFBACEDDAELIAJBCGpBtNzAgABBEhDuhYCAACAAQQRqIAIpAwg3AgBBASEDCyAAIAM6AAAgAkEgahCahICAACABEJqEgIAAIAJBMGokgICAgAALHAAgACABKQIANwIAIABBCGogAUEIaigCADYCAAtHAQF/I4CAgIAAQRBrIgIkgICAgAAgAkEIaiABEM+FgIAAIAAgAigCCCACKAIMEPqEgIAAIAEQmoSAgAAgAkEQaiSAgICAAAu6CQIFfwF+I4CAgIAAQYABayIDJICAgIAAIANBOjYCSCADQSBqIANByABqQQRBARCbhICAACADQTo2AmggAyACNgJkIANBADYCYCADIAI2AlwgAyABNgJYIAMgAygCSDYCcCADIAMoAiQ2AmwgA0HwAGohBCACIQVBACEGIAIhBwJAAkACQAJAAkADQAJAAkACQAJAIAUgBkkNACAHIAVPDQELEJ6FgIAADAELIANBGGogAygCbCADQdgAampBF2otAAAgAygCWCAGaiAFIAZrELOGgIAAIAMoAhhBAUYNASADIAMoAmQ2AmALQQAhBkEAIQdBACEFDAILIAMgAygCHCADKAJgakEBaiIGNgJgAkACQCAGIAMoAmwiBUkNACADKAJcIAZJDQAgAygCWCEHIAMgBTYCLCADIAcgBiAFayIGajYCKCADQRBqQQAgBSAEQQQQu4SAgAAgAyADKQMQNwNIIANBKGogA0HIAGoQnISAgAANASADKAJgIQYLIAMoAmQhBSADKAJcIQcMAQsLAkAgBkUNACAGIAJGDQAgBiACTw0CIAEgBmosAABBv39MDQILIANB2ABqIAEgBhDKhYCAACADQcgAaiADQdgAahD3hICAAEEBIQUCQCADLQBIQQFGDQAgAy0ASSEHIAMgAiAGayIFNgIsIAMgASAGaiIGNgIoIANBATYCeCADIAU2AnwCQAJAIAVBAUsNACAFDgIFAQULIAYsAAFBv39MDQQLIAZBAWohASAFQX9qIQJBACEGQQAhBQwBCyADKAJMIgdBgH5xIQYgA0HQAGooAgAhAQsgA0HEAGogAjYCACADQThqQQhqIAE2AgAgAyAFNgI4IAMgBiAHQf8BcXIiBjYCPAJAAkAgBQ0AIANBOGoQr4SAgAAgA0EAOwFkIANB8M3AgAA2AmAgAyACNgJcIAMgATYCWCADQThqIANB2ABqEJiEgIAAIANBOGpBBHIhBiADKAI4QQFHDQEgA0HYAGpBCGogBkEIaigCADYCACADIAYpAgA3A1ggAyADQdgAahDKg4CAACADKAIAIQYgAEEIakHoysCAADYCACAAIAY2AgQgAEEBNgIAIANBOGoQpYSAgAAMBQsgACAGNgIEIABBATYCACAAQQhqIAE2AgAMBAsgA0EoakEIaiICIAZBCGooAgAiBTYCACADIAYpAgA3AyggBUHAAEEgIAdBAXEiBhsiB0cNAiADQcgAaiAHQQFyEMGEgIAAIANByABqIAYQwoSAgAAgA0E4akEIaiIFIAIoAgA2AgAgAyADKQMoNwM4IAMgA0E4ahCbhYCAACIGNgJgIAMgBjYCWCADIAMoAjw2AlwgAyAGIAUoAgBqNgJkIANByABqIANB2ABqEMiDgIAAIABBDGogA0HIAGpBCGooAgA2AgAgACADKQNINwIEIABBADYCAAwDCyABIAJBACAGEJKGgIAAAAsgAyADQfwAajYCYCADIANB+ABqNgJcIAMgA0EoajYCWCADQdgAahC0hICAAAALIANBCGpBxtzAgABBIBDuhYCAACADKQMIIQggAEEBNgIAIAAgCDcCBCADQShqEJqEgIAACyADQYABaiSAgICAAAsJACAAQgA3AgALFABBiPrCgABB7oCAgAAQk4SAgAALEAAgACACNwMIIAAgATcDAAsQACAAIAI3AwggACABNwMACxQBAX8gACgCACECIAAgATYCACACC0YCAX8BfiOAgICAAEEQayICJICAgIAAIAJBCGogAUEAEMeFgIAAIAIpAwghAyAAQQA2AgggACADNwIAIAJBEGokgICAgAALjgECAX8BfiOAgICAAEEgayIDJICAgIAAAkACQCABQf8BcUUNACADQRBqIAIQgIWAgAAgA0EQahCbhYCAACABIAIQ8oaAgAAaIABBCGogAjYCACAAIAMpAxA3AgAMAQsgA0EIaiACQQEQx4WAgAAgAykDCCEEIAAgAjYCCCAAIAQ3AgALIANBIGokgICAgAALQwAgACgCACEAAkAgARDKhoCAAA0AAkAgARDLhoCAAA0AIAAgARCchoCAAA8LIAAgARDhhoCAAA8LIAAgARDbhoCAAAsPACAAKAIAIAEQ04aAgAALAgALAgALPQEBfyOAgICAAEEQayIEJICAgIAAIARBCGpBACADIAEgAhCHhYCAACAAIAQpAwg3AgAgBEEQaiSAgICAAAtAAAJAAkAgAiABSQ0AIAQgAk8NASACIAQQj4aAgAAACyABIAIQkIaAgAAACyAAIAIgAWs2AgQgACADIAFqNgIAC2cBA38CQAJAIAEoAgAiAiABKAIERw0AQQAhAgwBCyABIAJBAWo2AgACQCABKAIIIgNBAWoiBCADTw0AQcDdwIAAQRxBsN3AgAAQjYaAgAAACyABIAQ2AggLIAAgAjYCBCAAIAM2AgALwAUBBX8jgICAgABBoAJrIgYkgICAgAAgBS0AACEHIAZBoAFqQf8BQYABEPKGgIAAGiAGQQA2AiggBiAFQTpqNgIkIAYgBTYCIANAIAZBGGogBkEgahCIhYCAAAJAIAYoAhwiBQ0AIAZBIGogBkGgAWpBgAEQ84aAgAAaIAZBADYCqAEgBiABNgKgASAGIAEgAmo2AqQBQQAhCAJAAkADQCAGQRBqIAZBoAFqEIiFgIAAAkAgBigCFCIFDQAgB0H/AXEhBQNAAkACQCACRQ0AIAEtAAAgBUYNAQsgBiADIAQgCBCGhYCAACAGKAIEIglBAXYhCiAJIAYoAgAiBWpBf2ohCQJAA0AgCkUNASAFLQAAIQQgBSAJLQAAOgAAIAkgBDoAACAKQX9qIQogBUEBaiEFIAlBf2ohCQwACwsgAEEANgIAIAAgCDYCBAwFCwJAIAggBEkNACAAQgE3AgAMBQsgAUEBaiEBIAMgCGpBADoAACACQX9qIQIgCEEBaiEIDAALCyAGKAIQIQkCQAJAAkAgBSwAACIFQQBIDQAgBkEgaiAFQf8BcSIKai0AACIFQf8BRg0BIAZBCGogAyAEIAgQhoWAgAAgBigCCCEJIAYoAgwhCgNAIApFDQMgCSAJLQAAQTpsIAVqIgU6AAAgCkF/aiEKIAlBAWohCSAFQQh2IQUMAAsLIABBAjYCBCAAQQhqIAk2AgAMAwsgAEEBNgIEIABBDGogCTYCACAAQQhqIAo2AgAMAgsDQCAFRQ0BAkAgCCAETw0AIAMgCGogBToAACAFQQh2IQUgCEEBaiEIDAELCwsgAEEANgIECyAAQQE2AgALIAZBoAJqJICAgIAADwsgBSwAACIFQf8BcSEJAkAgBUEASA0AIAZBoAFqIAlqIAYoAhg6AAAMAQsLQbTewIAAIAlBgAEQjIaAgAAAC9sCAQF/I4CAgIAAQcAAayICJICAgIAAAkACQAJAAkACQCAAKAIADgQBAgMAAQtBxN7AgABBKEHs3sCAABCNhoCAAAALIAJBPGpBADYCACACQdjgwIAANgI4IAJCATcCLCACQdDgwIAANgIoIAEgAkEoahDJhoCAACEADAILIAIgACgCBDYCDCACIABBCGooAgA2AiQgAkE8akECNgIAIAJBHGpBhICAgAA2AgAgAkICNwIsIAJB/N/AgAA2AiggAkGEgYCAADYCFCACIAJBEGo2AjggAiACQSRqNgIYIAIgAkEMajYCECABIAJBKGoQyYaAgAAhAAwBCyACIAAoAgQ2AiQgAkE8akEBNgIAIAJCATcCLCACQbzfwIAANgIoIAJBhICAgAA2AhQgAiACQRBqNgI4IAIgAkEkajYCECABIAJBKGoQyYaAgAAhAAsgAkHAAGokgICAgAAgAAvGAgEBfyOAgICAAEEgayICJICAgIAAAkACQAJAAkACQCAAKAIADgQBAgMAAQsgAkEQaiABQdjgwIAAQQ8QzYaAgAAgAkEQahC2hoCAACEADAMLIAJBEGogAUG84cCAAEEOEM2GgIAAIAJBEGoQtoaAgAAhAAwCCyACQRBqIAFBkOHAgABBEBDMhoCAACACIABBBGo2AgwgAkEQakGg4cCAAEEJIAJBDGpBrOHAgAAQp4aAgAAaIAIgAEEIajYCDCACQRBqQfjgwIAAQQUgAkEMakGA4cCAABCnhoCAABogAkEQahC0hoCAACEADAELIAJBEGogAUHn4MCAAEEREMyGgIAAIAIgAEEEajYCDCACQRBqQfjgwIAAQQUgAkEMakGA4cCAABCnhoCAABogAkEQahC0hoCAACEACyACQSBqJICAgIAAIAALNgEBfyAAIAIgAWsiAhCNhYCAACAAIAAoAggiAyACajYCCCADIAAoAgBqIAIgASACEI6FgIAACxEAIAAgACgCCCABEJmFgIAAC5MCAQF/I4CAgIAAQeAAayIEJICAgIAAIAQgATYCCCAEIAM2AgwCQCABIANHDQAgACACIAEQ84aAgAAaIARB4ABqJICAgIAADwsgBEEoakEUakGIgICAADYCACAEQTRqQYmBgIAANgIAIARBEGpBFGpBAzYCACAEIARBCGo2AkAgBCAEQQxqNgJEIARByABqQRRqQQA2AgAgBEIDNwIUIARBiOLAgAA2AhAgBEGJgYCAADYCLCAEQdziwIAANgJYIARCATcCTCAEQdTiwIAANgJIIAQgBEEoajYCICAEIARByABqNgI4IAQgBEHEAGo2AjAgBCAEQcAAajYCKCAEQRBqQajjwIAAELGGgIAAEJSGgIAAAAtDACAAKAIAIQACQCABEMqGgIAADQACQCABEMuGgIAADQAgACABEJyGgIAADwsgACABEOGGgIAADwsgACABENuGgIAACxQAIAAoAgAgACgCBCABENKGgIAACxAAIAAgAjYCBCAAIAE2AgALEAAgACACNgIEIAAgATYCAAsQACAAIAI2AgQgACABNgIACwIACxAAIAAgAjYCBCAAIAE2AgALBAAgAAsnAQF/QQEhAwJAIAJBCEcNACAAIAEpAAA3AAFBACEDCyAAIAM6AAALEQAgACABIAEgAmoQjIWAgAALagEBfyOAgICAAEEQayIDJICAgIAAIAMgACABIAJBAUEBEJqFgIAAAkACQCADKAIAQQFHDQAgA0EIaigCAEUNAUG448CAAEEoQYDkwIAAEI2GgIAAAAsgA0EQaiSAgICAAA8LEIaGgIAAAAuFAgECf0EAIQYCQCABKAIEIgcgAmsgA08NACACIANqIgMgAkkhAgJAAkACQAJAIAVFDQAgAkUNASAAIAM2AgQgAEEIakEANgIADAMLIAJFDQEgACADNgIEIABBCGpBADYCAAwCCyAHQQF0IgIgAyACIANLGyEDCwJAIANBf0oNACAAQQhqQQA2AgAMAQsCQAJAIAcNACADQQEQzYKAgAAhAgwBCyABKAIAIAdBASADEM+CgIAAIQILAkACQCACDQAgBEUNASADQQEQhYaAgAAACyABIAM2AgQgASACNgIADAILIAAgAzYCBEEBIQYgAEEIakEBNgIADAELQQEhBgsgACAGNgIACwcAIAAoAgALEAAgACACNgIEIAAgATYCAAsWACAAIAEoAgg2AgQgACABKAIANgIACwIACxMAIAAgASACajYCBCAAIAE2AgALDABC5K7ChZebpYgRCz8BAX8jgICAgABBEGsiAySAgICAACADIAE2AgwgAyAANgIIIANBCGpBkOTAgABBACACELGGgIAAEPeFgIAAAAsCAAsCAAslAAJAIAEoAgANABDyhYCAAAALIABBpOTAgAA2AgQgACABNgIAC2YBAn8gASgCACECIAFBADYCAAJAAkAgAkUNACABKAIEIQNBCEEEEM2CgIAAIgFFDQEgASADNgIEIAEgAjYCACAAQaTkwIAANgIEIAAgATYCAA8LEPKFgIAAAAtBCEEEEIWGgIAAAAsQACAAIAI2AgQgACABNgIACxQAIAAoAgAgACgCBCABENCGgIAAC0MAIAAoAgAhAAJAIAEQyoaAgAANAAJAIAEQy4aAgAANACAAIAEQ2oaAgAAPCyAAIAEQ44aAgAAPCyAAIAEQ4oaAgAALAgALBwAgACgCAAsWACAAIAEoAgg2AgQgACABKAIANgIACxIAIAFBtOTAgABBCBDIhoCAAAuKCgEBfyOAgICAAEEwayICJICAgIAAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgAC0AAA4SAAECAwQFBgcICQoLDA0ODxARAAsgAiAALQABOgAIIAJBLGpBATYCACACQgI3AhwgAkGE58CAADYCGCACQY+BgIAANgIUIAIgAkEQajYCKCACIAJBCGo2AhAgASACQRhqEMmGgIAAIQAMEQsgAiAAQQhqKQMANwMIIAJBLGpBATYCACACQgI3AhwgAkHo5sCAADYCGCACQZCBgIAANgIUIAIgAkEQajYCKCACIAJBCGo2AhAgASACQRhqEMmGgIAAIQAMEAsgAiAAQQhqKQMANwMIIAJBLGpBATYCACACQgI3AhwgAkHo5sCAADYCGCACQZGBgIAANgIUIAIgAkEQajYCKCACIAJBCGo2AhAgASACQRhqEMmGgIAAIQAMDwsgAiAAQQhqKQMANwMIIAJBLGpBATYCACACQgI3AhwgAkHM5sCAADYCGCACQZKBgIAANgIUIAIgAkEQajYCKCACIAJBCGo2AhAgASACQRhqEMmGgIAAIQAMDgsgAiAAQQRqKAIANgIIIAJBLGpBATYCACACQgI3AhwgAkGs5sCAADYCGCACQZOBgIAANgIUIAIgAkEQajYCKCACIAJBCGo2AhAgASACQRhqEMmGgIAAIQAMDQsgAiAAQQRqKQIANwMIIAJBLGpBATYCACACQgE3AhwgAkGY5sCAADYCGCACQZSBgIAANgIUIAIgAkEQajYCKCACIAJBCGo2AhAgASACQRhqEMmGgIAAIQAMDAsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJBiObAgAA2AhggASACQRhqEMmGgIAAIQAMCwsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJBgObAgAA2AhggASACQRhqEMmGgIAAIQAMCgsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJB7OXAgAA2AhggASACQRhqEMmGgIAAIQAMCQsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJB2OXAgAA2AhggASACQRhqEMmGgIAAIQAMCAsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJBwOXAgAA2AhggASACQRhqEMmGgIAAIQAMBwsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJBsOXAgAA2AhggASACQRhqEMmGgIAAIQAMBgsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJBpOXAgAA2AhggASACQRhqEMmGgIAAIQAMBQsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJBmOXAgAA2AhggASACQRhqEMmGgIAAIQAMBAsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJBhOXAgAA2AhggASACQRhqEMmGgIAAIQAMAwsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJB7OTAgAA2AhggASACQRhqEMmGgIAAIQAMAgsgAkEsakEANgIAIAJBtOTAgAA2AiggAkIBNwIcIAJB1OTAgAA2AhggASACQRhqEMmGgIAAIQAMAQsgASAAQQRqKAIAIABBCGooAgAQyIaAgAAhAAsgAkEwaiSAgICAACAACxQAIAEgACgCACAAKAIEEMiGgIAACxIAIAAgAiABKAIMEYCAgIAAAAs2AQF/IAAgAiABayICELGFgIAAIAAgACgCCCIDIAJqNgIIIAMgACgCAGogAiABIAIQsoWAgAALEQAgACAAKAIIIAEQyYWAgAALkwIBAX8jgICAgABB4ABrIgQkgICAgAAgBCABNgIIIAQgAzYCDAJAIAEgA0cNACAAIAIgARDzhoCAABogBEHgAGokgICAgAAPCyAEQShqQRRqQYiAgIAANgIAIARBNGpBlYGAgAA2AgAgBEEQakEUakEDNgIAIAQgBEEIajYCQCAEIARBDGo2AkQgBEHIAGpBFGpBADYCACAEQgM3AhQgBEHE6MCAADYCECAEQZWBgIAANgIsIARBlOfAgAA2AlggBEIBNwJMIARBkOnAgAA2AkggBCAEQShqNgIgIAQgBEHIAGo2AjggBCAEQcQAajYCMCAEIARBwABqNgIoIARBEGpB+OfAgAAQsYaAgAAQlIaAgAAACxkAIAAoAgAiACgCACAAKAIIIAEQ0IaAgAALQwAgACgCACEAAkAgARDKhoCAAA0AAkAgARDLhoCAAA0AIAAgARCchoCAAA8LIAAgARDhhoCAAA8LIAAgARDbhoCAAAtxAQF/I4CAgIAAQSBrIgIkgICAgAAgAiAANgIEIAJBCGpBEGogAUEQaikCADcDACACQQhqQQhqIAFBCGopAgA3AwAgAiABKQIANwMIIAJBBGpBlOfAgAAgAkEIahCfhoCAACEBIAJBIGokgICAgAAgAQsgAQF/AkAgACgCBCIBRQ0AIAAoAgAgAUEBEM6CgIAACwsCAAsCAAsQACAAIAEgAiADELqFgIAACzABAX9BACEEAkACQCABIANHDQAgACACRw0BQQEhBAsgBA8LIAAgAiABEPSGgIAARQtXAQF/I4CAgIAAQRBrIgMkgICAgAACQCACRQ0AIAAgAjYCBCAAIAE2AgAgA0EQaiSAgICAAA8LQZjpwIAAQSsgA0EIakHE6cCAAEHM6sCAABCqhoCAAAALzgIBAn8jgICAgABBEGsiAiSAgICAACAAKAIAIQACQAJAAkACQCABQYABSQ0AIAJBADYCDCABQYAQSQ0BIAJBDGohAwJAIAFBgIAETw0AIAIgAUE/cUGAAXI6AA4gAiABQQZ2QT9xQYABcjoADSACIAFBDHZBD3FB4AFyOgAMQQMhAQwDCyACIAFBP3FBgAFyOgAPIAIgAUESdkHwAXI6AAwgAiABQQZ2QT9xQYABcjoADiACIAFBDHZBP3FBgAFyOgANQQQhAQwCCwJAIAAoAggiAyAAKAIERw0AIABBARCxhYCAACAAKAIIIQMLIAAoAgAgA2ogAToAACAAIAAoAghBAWo2AggMAgsgAiABQT9xQYABcjoADSACIAFBBnZBH3FBwAFyOgAMIAJBDGohA0ECIQELIAAgAyABEL2FgIAACyACQRBqJICAgIAAQQALEQAgACABIAEgAmoQsIWAgAALaAEBfyOAgICAAEEgayICJICAgIAAIAAoAgAhACACQQhqQRBqIAFBEGopAgA3AwAgAkEIakEIaiABQQhqKQIANwMAIAIgASkCADcDCCAAIAJBCGoQtYWAgAAhASACQSBqJICAgIAAIAELEwAgACgCACABIAIQvYWAgABBAAsMACAAIAEpAgA3AgALAgALUAEBfyOAgICAAEEQayIDJICAgIAAIAMgAhDEhYCAACADIAEgAhC9hYCAACAAQQhqIANBCGooAgA2AgAgACADKQMANwIAIANBEGokgICAgAALJwEBf0EBIQMCQCACQQRHDQAgACABKAAANgABQQAhAwsgACADOgAAC0YCAX8BfiOAgICAAEEQayICJICAgIAAIAJBCGogAUEAEMeFgIAAIAIpAwghAyAAQQA2AgggACADNwIAIAJBEGokgICAgAALIAEBfwJAIAAoAgQgACgCCCIBRg0AIAAgARDGhYCAAAsLgwEBAX8CQAJAAkACQCAAKAIEIgIgAUkNAAJAIAFFDQAgAiABRg0EIAAoAgAgAkEBIAEQz4KAgAAiAg0CIAFBARCFhoCAAAALIAAQtoWAgAAgAEEBNgIAQQAhAQwCC0Hc6sCAAEEkQfTpwIAAEI2GgIAAAAsgACACNgIACyAAIAE2AgQLC6QBAQN/I4CAgIAAQRBrIgMkgICAgAACQAJAIAFBf0wNAAJAAkAgAQ0AQQEhAgwBCyADQQhqIAFBARC7hYCAACADKAIMIQQgAygCCCEFAkACQCACDQAgBSAEEM2CgIAAIQIMAQsgBSAEENCCgIAAIQILIAJFDQILIAAgATYCBCAAIAI2AgAgA0EQaiSAgICAAA8LEMyFgIAAAAsgBSAEEIWGgIAAAAsXACAAIAI2AgggACADNgIEIAAgATYCAAtqAQF/I4CAgIAAQRBrIgMkgICAgAAgAyAAIAEgAkEBQQEQzYWAgAACQAJAIAMoAgBBAUcNACADQQhqKAIARQ0BQYDrwIAAQShB9OnAgAAQjYaAgAAACyADQRBqJICAgIAADwsQhoaAgAAACw4AIAAgASACEMKFgIAACxAAIAAgAjYCBCAAIAE2AgALCQAQhoaAgAAAC4UCAQJ/QQAhBgJAIAEoAgQiByACayADTw0AIAIgA2oiAyACSSECAkACQAJAAkAgBUUNACACRQ0BIAAgAzYCBCAAQQhqQQA2AgAMAwsgAkUNASAAIAM2AgQgAEEIakEANgIADAILIAdBAXQiAiADIAIgA0sbIQMLAkAgA0F/Sg0AIABBCGpBADYCAAwBCwJAAkAgBw0AIANBARDNgoCAACECDAELIAEoAgAgB0EBIAMQz4KAgAAhAgsCQAJAIAINACAERQ0BIANBARCFhoCAAAALIAEgAzYCBCABIAI2AgAMAgsgACADNgIEQQEhBiAAQQhqQQE2AgAMAQtBASEGCyAAIAY2AgALAgALFgAgACABKAIINgIEIAAgASgCADYCAAsKACAAELaFgIAACwwAQuSuwoWXm6WIEQsMAEKY3Iamxs6tyQgLDQBCmfbtpaTq/5TFAAsdACAAKAIAIgAoAgAgASAAKAIEKAIkEYCAgIAAAAsPACAAKAIAIAEQ1oWAgAAL5QMBAX8jgICAgABBEGsiAiSAgICAAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAAtAAAOEgECAwQFBgcICQoLDA0ODxARAAELIAIgAUHA8cCAAEENEM2GgIAADBELIAIgAUGM88CAAEEIEM2GgIAADBALIAIgAUH88sCAAEEQEM2GgIAADA8LIAIgAUHr8sCAAEEREM2GgIAADA4LIAIgAUHc8sCAAEEPEM2GgIAADA0LIAIgAUHL8sCAAEEREM2GgIAADAwLIAIgAUG/8sCAAEEMEM2GgIAADAsLIAIgAUG28sCAAEEJEM2GgIAADAoLIAIgAUGm8sCAAEEQEM2GgIAADAkLIAIgAUGc8sCAAEEKEM2GgIAADAgLIAIgAUGP8sCAAEENEM2GgIAADAcLIAIgAUGF8sCAAEEKEM2GgIAADAYLIAIgAUH58cCAAEEMEM2GgIAADAULIAIgAUHu8cCAAEELEM2GgIAADAQLIAIgAUHm8cCAAEEIEM2GgIAADAMLIAIgAUHd8cCAAEEJEM2GgIAADAILIAIgAUHS8cCAAEELEM2GgIAADAELIAIgAUHN8cCAAEEFEM2GgIAACyACELaGgIAAIQEgAkEQaiSAgICAACABCxQAIAAoAgAgACgCBCABENKGgIAACzwAAkAgARDKhoCAAA0AAkAgARDLhoCAAA0AIAAgARDkhoCAAA8LIAAgARDhhoCAAA8LIAAgARDbhoCAAAs/AQF/I4CAgIAAQRBrIgMkgICAgAAgAyABNgIMIAMgADYCCCADQQhqQfDwwIAAQQAgAhCxhoCAABD3hYCAAAALAgALKgEBfwJAIAAoAgQiAUUNACAAQQhqKAIAIgBFDQAgASAAQQEQzoKAgAALCyMBAX8CQCAAQQRqKAIAIgFFDQAgACgCACABQQEQzoKAgAALCxwAAkAgAA0AQdDrwIAAQSsgARCNhoCAAAALIAALIAACQCAADQBB0OvAgABBK0G88MCAABCNhoCAAAALIAALgwUBBX8jgICAgABBEGsiAiSAgICAACAAKAIAIQACQAJAAkACQAJAAkACQCABQYABSQ0AIAJBADYCDCABQYAQSQ0BIAJBDGohAwJAIAFBgIAETw0AIAIgAUE/cUGAAXI6AA4gAiABQQZ2QT9xQYABcjoADSACIAFBDHZBD3FB4AFyOgAMQQMhAQwECyACIAFBP3FBgAFyOgAPIAIgAUESdkHwAXI6AAwgAiABQQZ2QT9xQYABcjoADiACIAFBDHZBP3FBgAFyOgANQQQhAQwDCwJAAkAgACgCCCIEIABBBGooAgBGDQAgACgCACEFDAELIARBAWoiBSAESQ0GIARBAXQiAyAFIAMgBUsbIgNBAEgNBgJAAkAgBA0AIANBARDNgoCAACEFDAELIAAoAgAgBEEBIAMQz4KAgAAhBQsgBUUNAiAAIAU2AgAgAEEEaiADNgIAIAAoAgghBAsgBSAEaiABOgAAIAAgACgCCEEBajYCCAwDCyACIAFBP3FBgAFyOgANIAIgAUEGdkEfcUHAAXI6AAwgAkEMaiEDQQIhAQwBCyADQQEQhYaAgAAACwJAAkAgAEEEaigCACIFIABBCGooAgAiBGsgAUkNACAAKAIAIQUMAQsgBCABaiIGIARJDQMgBUEBdCIEIAYgBCAGSxsiBEEASA0DAkACQCAFDQAgBEEBEM2CgIAAIQUMAQsgACgCACAFQQEgBBDPgoCAACEFCyAFRQ0CIAAgBTYCACAAQQRqIAQ2AgAgAEEIaigCACEECyAAQQhqIAQgAWo2AgAgBSAEaiADIAEQ84aAgAAaCyACQRBqJICAgIAAQQAPCyAEQQEQhYaAgAAACxCGhoCAAAALdAEBfyOAgICAAEEgayICJICAgIAAIAIgACgCADYCBCACQQhqQRBqIAFBEGopAgA3AwAgAkEIakEIaiABQQhqKQIANwMAIAIgASkCADcDCCACQQRqQajrwIAAIAJBCGoQn4aAgAAhASACQSBqJICAgIAAIAEL1wEBA38CQAJAAkACQCAAKAIAIgBBBGooAgAiAyAAQQhqKAIAIgRrIAJJDQAgACgCACEDDAELIAQgAmoiBSAESQ0CIANBAXQiBCAFIAQgBUsbIgRBAEgNAgJAAkAgAw0AIARBARDNgoCAACEDDAELIAAoAgAgA0EBIAQQz4KAgAAhAwsgA0UNASAAIAM2AgAgAEEEaiAENgIAIABBCGooAgAhBAsgAEEIaiAEIAJqNgIAIAMgBGogASACEPOGgIAAGkEADwsgBEEBEIWGgIAAAAsQhoaAgAAACxQAIAAoAgAgACgCCCABENCGgIAACwkAEIaGgIAAAAsUACAAKAIAIAAoAgggARDShoCAAAtEAQF/I4CAgIAAQRBrIgIkgICAgAAgAkEIaiABQfvrwIAAQQsQzIaAgAAgAkEIahC0hoCAACEBIAJBEGokgICAgAAgAQtIAQF/I4CAgIAAQRBrIgIkgICAgAAgAiABELGGgIAANgIMIAIgADYCCCACQcDrwIAANgIEIAJBwOvAgAA2AgAgAhD2hYCAAAAL6gUBA38jgICAgABBwABrIgIkgICAgAACQAJAAkACQAJAIAAtAAAOAwACAQALIAIgAEEEaigCADYCBEEUQQEQzYKAgAAiAEUNAyAAQRBqQQAoAKTzwIAANgAAIABBCGpBACkAnPPAgAA3AAAgAEEAKQCU88CAADcAACACQpSAgIDAAjcCDCACIAA2AgggAkEoakEUakECNgIAIAJBJGpBm4GAgAA2AgAgAkIDNwIsIAJBuO/AgAA2AiggAkGcgYCAADYCHCACIAJBGGo2AjggAiACQQRqNgIgIAIgAkEIajYCGCABIAJBKGoQyYaAgAAhACACKAIMIgFFDQIgAigCCCABQQEQzoKAgAAMAgsgAEEEaigCACIAKAIAIAEgACgCBCgCIBGAgICAAAAhAAwBC0Gw7MCAACEDQRYhBAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAAtAAEOEgABAgMEBQYHCAkKCwwNDg8QEgALQcnuwIAAIQNBECEEDBELQbjuwIAAIQNBESEEDBALQabuwIAAIQNBEiEEDA8LQZbuwIAAIQNBECEEDA4LQYTuwIAAIQNBEiEEDA0LQfftwIAAIQNBDSEEDAwLQentwIAAIQMMCgtB1O3AgAAhA0EVIQQMCgtBye3AgAAhA0ELIQQMCQtBtO3AgAAhA0EVIQQMCAtBn+3AgAAhA0EVIQQMBwtBiO3AgAAhA0EXIQQMBgtB/OzAgAAhA0EMIQQMBQtB8+zAgAAhA0EJIQQMBAtB6ezAgAAhA0EKIQQMAwtB1OzAgAAhA0EVIQQMAgtBxuzAgAAhAwtBDiEECyACQTxqQQE2AgAgAiAENgIcIAIgAzYCGCACQZ2BgIAANgIMIAJCATcCLCACQaTvwIAANgIoIAIgAkEYajYCCCACIAJBCGo2AjggASACQShqEMmGgIAAIQALIAJBwABqJICAgIAAIAAPC0EUQQEQhYaAgAAACw0AQs+4j+zTwfnEv38LBABBAAsJACAAQQA2AgALFgAgACABKAIINgIEIAAgASgCADYCAAsUACAAKAIAIAAoAgggARDShoCAAAsUACAAKAIAIAAoAgggARDQhoCAAAuVAQEBfwJAAkACQCACQX9MDQACQAJAIAINAEEBIQMMAQsgAkEBEM2CgIAAIgNFDQILIAMgASACEPOGgIAAIQNBDEEEEM2CgIAAIgFFDQIgASACNgIIIAEgAjYCBCABIAM2AgAgAEGI7MCAADYCBCAAIAE2AgAPCxDjhYCAAAALIAJBARCFhoCAAAALQQxBBBCFhoCAAAALDAAgACABEPCFgIAAC/gDAQF/I4CAgIAAQTBrIgIkgICAgAACQAJAAkACQAJAIAAtAAAOAwACAQALIAIgAEEEaigCADYCDCACQRBqIAFB8O7AgABBAhDMhoCAACACQRBqQfLuwIAAQQQgAkEMakH47sCAABCnhoCAACEAIAJBEDoAHyAAQYjvwIAAQQQgAkEfakHg7sCAABCnhoCAACEBQRRBARDNgoCAACIARQ0DIABBEGpBACgApPPAgAA2AAAgAEEIakEAKQCc88CAADcAACAAQQApAJTzwIAANwAAIAJClICAgMACNwIkIAIgADYCICABQYzvwIAAQQcgAkEgakGU78CAABCnhoCAABC0hoCAACEAIAIoAiQiAUUNAiACKAIgIAFBARDOgoCAAAwCCyAAQQRqKAIAIQAgAkEgaiABQZnxwIAAQQYQzIaAgAAgAiAAQQhqNgIQIAJBIGpBiO/AgABBBCACQRBqQaDxwIAAEKeGgIAAGiACIAA2AhAgAkEgakGU8cCAAEEFIAJBEGpBsPHAgAAQp4aAgAAaIAJBIGoQtIaAgAAhAAwBCyACIAAtAAE6ABAgAkEgaiABQdnuwIAAQQQQzYaAgAAgAkEgaiACQRBqQeDuwIAAELWGgIAAELaGgIAAIQALIAJBMGokgICAgAAgAA8LQRRBARCFhoCAAAALnAEBAn8jgICAgABBEGsiBCSAgICAAAJAQQxBBBDNgoCAACIFDQBBDEEEEIWGgIAAAAsgBSABOgAIIAUgAzYCBCAFIAI2AgAgBSAELwANOwAJIAVBC2ogBEENakECai0AADoAACAAQQI6AAAgACAELwAKOwABIABBA2ogBEEKakECai0AADoAACAAQQRqIAU2AgAgBEEQaiSAgICAAAsEAAAACwIACyUBAX8gACABQQAoApj6woAAIgJBnoGAgAAgAhsRjoCAgAAAAAALvwEBAX8CQAJAAkBBACgCqPrCgABBAUYNAEEAQgE3A6j6woAADAELQQAoAqz6woAADQELAkBBACgCnPrCgAANAEEAKAKk+sKAACECQQAgATYCpPrCgABBACgCoPrCgAAhAUEAIAA2AqD6woAAQQBBADYCnPrCgAACQCACRQ0AIAEgAigCABGBgICAAAAgAigCBCIARQ0AIAEgACACKAIIEM6CgIAACw8LAAALQdDvwIAAQTRBnPDAgAAQ2YWAgAAAC2IBA38jgICAgABBEGsiASSAgICAACAAEK2GgIAAQazwwIAAEN2FgIAAIQIgABCshoCAABDehYCAACEDIAFBADYCBCABIAM2AgAgAUHM8MCAACAAEKyGgIAAIAIQ94WAgAAAC58CAQJ/I4CAgIAAQSBrIgQkgICAgABBASEFAkACQAJAAkBBACgCqPrCgABBAUYNAEEAQoGAgIAQNwOo+sKAAAwBC0EAQQAoAqz6woAAQQFqIgU2Aqz6woAAIAVBAksNAQsgBCADNgIcIAQgAjYCGCAEQcDrwIAANgIUIARBwOvAgAA2AhBBACgCnPrCgAAiAkF/TA0AQQAgAkEBaiICNgKc+sKAAAJAQQAoAqT6woAAIgNFDQBBACgCoPrCgAAhAiAEQQhqIAAgASgCEBGOgICAAAAgBCAEKQMINwMQIAIgBEEQaiADKAIMEY6AgIAAAEEAKAKc+sKAACECC0EAIAJBf2o2Apz6woAAIAVBAU0NAQsAAAsgACABEPyFgIAAAAviAgEFfyOAgICAAEHAAGsiAiSAgICAAAJAIAEoAgQiAw0AIAFBBGohAyABKAIAIQQgAkEANgIgIAJCATcDGCACIAJBGGo2AiQgAkEoakEQaiAEQRBqKQIANwMAIAJBKGpBCGogBEEIaikCADcDACACIAQpAgA3AyggAkEkakGo68CAACACQShqEJ+GgIAAGiACQQhqQQhqIgQgAigCIDYCACACIAIpAxg3AwgCQCABKAIEIgVFDQAgAUEIaigCACIGRQ0AIAUgBkEBEM6CgIAACyADIAIpAwg3AgAgA0EIaiAEKAIANgIAIAMoAgAhAwsgAUEBNgIEIAFBDGooAgAhBCABQQhqIgEoAgAhBSABQgA3AgACQEEMQQQQzYKAgAAiAQ0AQQxBBBCFhoCAAAALIAEgBDYCCCABIAU2AgQgASADNgIAIABB4PDAgAA2AgQgACABNgIAIAJBwABqJICAgIAAC4QCAQR/I4CAgIAAQcAAayICJICAgIAAIAFBBGohAwJAIAEoAgQNACABKAIAIQQgAkEANgIgIAJCATcDGCACIAJBGGo2AiQgAkEoakEQaiAEQRBqKQIANwMAIAJBKGpBCGogBEEIaikCADcDACACIAQpAgA3AyggAkEkakGo68CAACACQShqEJ+GgIAAGiACQQhqQQhqIgQgAigCIDYCACACIAIpAxg3AwgCQCABKAIEIgVFDQAgAUEIaigCACIBRQ0AIAUgAUEBEM6CgIAACyADIAIpAwg3AgAgA0EIaiAEKAIANgIACyAAQeDwwIAANgIEIAAgAzYCACACQcAAaiSAgICAAAthAQJ/IAEoAgAhAiABQQA2AgACQAJAIAJFDQAgASgCBCEDQQhBBBDNgoCAACIBRQ0BIAEgAzYCBCABIAI2AgAgAEGE8cCAADYCBCAAIAE2AgAPCwAAC0EIQQQQhYaAgAAACyAAAkAgASgCAA0AAAALIABBhPHAgAA2AgQgACABNgIACzEBAX8jgICAgABBEGsiAiSAgICAACACIAE2AgwgAiAANgIIIAJBCGoQ/YWAgAAaAAALBAAAAAtDACAAKAIAIQACQCABEMqGgIAADQACQCABEMuGgIAADQAgACABENmGgIAADwsgACABEOCGgIAADwsgACABENaGgIAACw8AIAAoAgAgARDthoCAAAt/AQJ/I4CAgIAAQRBrIgIkgICAgAAgACgCACIAKAIIIQMgACgCACEAIAIgARDOhoCAAAJAIANFDQADQCACIAA2AgwgAiACQQxqQajzwIAAELiGgIAAGiAAQQFqIQAgA0F/aiIDDQALCyACELmGgIAAIQAgAkEQaiSAgICAACAACwIACxcAIAAoAgAgACgCBEEAIAEQkoaAgAAACyoBAX8gACgCACIBKAIAIAEoAgQgACgCBCgCACAAKAIIKAIAEJKGgIAAAAv6BAEFfyOAgICAAEEQayICJICAgIAAAkACQAJAAkACQAJAAkAgAUGAAUkNACACQQA2AgwgAUGAEEkNASACQQxqIQMCQCABQYCABE8NACACIAFBP3FBgAFyOgAOIAIgAUEGdkE/cUGAAXI6AA0gAiABQQx2QQ9xQeABcjoADEEDIQEMBAsgAiABQT9xQYABcjoADyACIAFBEnZB8AFyOgAMIAIgAUEGdkE/cUGAAXI6AA4gAiABQQx2QT9xQYABcjoADUEEIQEMAwsCQAJAIAAoAggiBCAAQQRqKAIARg0AIAAoAgAhBQwBCyAEQQFqIgUgBEkNBiAEQQF0IgMgBSADIAVLGyIDQQBIDQYCQAJAIAQNACADQQEQzYKAgAAhBQwBCyAAKAIAIARBASADEM+CgIAAIQULIAVFDQIgACAFNgIAIABBBGogAzYCACAAKAIIIQQLIAUgBGogAToAACAAIAAoAghBAWo2AggMAwsgAiABQT9xQYABcjoADSACIAFBBnZBH3FBwAFyOgAMIAJBDGohA0ECIQEMAQsgA0EBEIWGgIAAAAsCQAJAIABBBGooAgAiBSAAQQhqKAIAIgRrIAFJDQAgACgCACEFDAELIAQgAWoiBiAESQ0DIAVBAXQiBCAGIAQgBksbIgRBAEgNAwJAAkAgBQ0AIARBARDNgoCAACEFDAELIAAoAgAgBUEBIAQQz4KAgAAhBQsgBUUNAiAAIAU2AgAgAEEEaiAENgIAIABBCGooAgAhBAsgAEEIaiAEIAFqNgIAIAUgBGogAyABEPOGgIAAGgsgAkEQaiSAgICAAA8LIARBARCFhoCAAAALEIaGgIAAAAsNACAAIAEQ9IWAgAAACxcAQc/zwIAAQRFB4PPAgAAQjYaAgAAACwkAEIaGgIAAAAvADQELfyOAgICAAEEwayIDJICAgIAAAkACQAJAAkACQAJAAkAgAkF/TA0AAkACQCACDQBBASEEDAELIAJBARDNgoCAACIERQ0CCyADQQA2AgggAyACNgIEIAMgBDYCAAJAIAJFDQAgASACaiEFIAEhBkEAIQcDQCAGIQggBkEBaiEJAkACQAJAIAYsAAAiBEF/Sg0AAkACQCAJIAVHDQBBACEKIAUhCwwBCyAGLQABQT9xIQogBkECaiIJIQsLIARBH3EhDCAEQf8BcSIEQd8BSw0BIAogDEEGdHIhBCAJIQYMAgsgBEH/AXEhBCAJIQYMAQsCQAJAIAsgBUcNAEEAIQYgBSELDAELIAstAABBP3EhBiALQQFqIgkhCwsgBiAKQQZ0ciEKAkAgBEHwAU8NACAKIAxBDHRyIQQgCSEGDAELAkACQCALIAVHDQBBACEEIAkhBgwBCyALQQFqIQYgCy0AAEE/cSEECyAKQQZ0IAxBEnRBgIDwAHFyIARyIgRBgIDEAEYNAgsCQAJAAkACQCAEQaMHRg0AIARBgIDEAEYNBSADQSBqIAQQ8IaAgAAgAygCJCIJRQ0BIAMoAighBCADIAMoAiAQhIaAgAAgAyAJEISGgIAAIAQNAgwDCyADIAI2AiQgAyABNgIgAkAgB0UNACAHIAJGDQAgByACTw0IIAEgB2osAABBv39MDQgLIAEgB2ohBAJAAkADQCAEIAFGDQECQAJAIARBf2oiCy0AACIJQRh0QRh1IgpBAEgNACALIQQMAQsCQAJAIAsgAUcNAEEAIQkgASEEDAELAkAgBEF+aiILLQAAIglBwAFxQYABRg0AIAlBH3EhCSALIQQMAQsCQAJAIAsgAUcNAEEAIQsgASEEDAELAkAgBEF9aiIMLQAAIgtBwAFxQYABRg0AIAtBD3EhCyAMIQQMAQsCQAJAIAwgAUcNAEEAIQwgASEEDAELIARBfGoiBC0AAEEHcUEGdCEMCyAMIAtBP3FyIQsLIAtBBnQgCUE/cXIhCQsgCUEGdCAKQT9xciIJQYCAxABGDQILIAkQ7oaAgAANAAsgCRDvhoCAAEUNACADIAI2AhQgAyABNgIQIAMgB0ECaiIENgIYIAMgAjYCHAJAIARFDQAgBCACRg0AIAQgAk8NCyABIARqLAAAQb9/TA0LCyABIARqIQRBACELA0AgBCAFRg0CIARBAWohCgJAAkAgBCwAACIJQX9MDQAgCUH/AXEhCSAKIQQMAQsCQAJAIAogBUcNAEEAIQwgBSEKDAELIARBAmohCiAELQABQT9xIQwLIAlBH3EhDQJAIAlB/wFxIglB3wFLDQAgDCANQQZ0ciEJIAohBAwBCwJAAkAgCiAFRw0AQQAhCiAFIQQMAQsgCkEBaiEEIAotAABBP3EhCgsgCiAMQQZ0ciEKAkAgCUHwAU8NACAKIA1BDHRyIQkMAQsCQAJAIAQgBUcNAEEAIQkgBSEEDAELIAQtAABBP3EhCSAEQQFqIQQLIApBBnQgDUESdEGAgPAAcXIgCXIiCUGAgMQARg0DCwJAAkAgC0H/AXENACAJEO6GgIAARQ0AQYCAxAAhCUEAIQsMAQtBASELCyAJQYCAxABGDQALIAkQ74aAgABFDQELAkACQCADKAIEIgkgAygCCCIEa0ECSQ0AIAMoAgAhCQwBCyAEQQJqIgsgBEkNDSAJQQF0IgogCyAKIAtLGyILQQBIDQ0CQAJAIAkNACALQQEQzYKAgAAhCQwBCyADKAIAIAlBASALEM+CgIAAIQkLIAlFDQsgAyALNgIEIAMgCTYCAAsgAyAEQQJqNgIIIAkgBGpBz4cCOwAADAMLAkACQCADKAIEIgkgAygCCCIEa0ECSQ0AIAMoAgAhCQwBCyAEQQJqIgsgBEkNDCAJQQF0IgogCyAKIAtLGyILQQBIDQwCQAJAIAkNACALQQEQzYKAgAAhCQwBCyADKAIAIAlBASALEM+CgIAAIQkLIAlFDQsgAyALNgIEIAMgCTYCAAsgAyAEQQJqNgIIIAkgBGpBz4UCOwAADAILIAMoAiAhBAsgAyAEEISGgIAACyAHIAhrIAZqIQcgBSAGRw0ACwsgACADKQMANwIAIABBCGogA0EIaigCADYCACADQTBqJICAgIAADwsQh4aAgAAACyACQQEQhYaAgAAACyADQSBqIAcQgoaAgAAACyADIANBHGo2AiggAyADQRhqNgIkIAMgA0EQajYCICADQSBqEIOGgIAAAAsgC0EBEIWGgIAAAAsgC0EBEIWGgIAAAAsQhoaAgAAAC3IBAn8CQAJAIAEoAggiAkF/TA0AIAEoAgAhAQJAAkAgAg0AQQEhAwwBCyACQQEQzYKAgAAiA0UNAgsgAyABIAIQ84aAgAAhASAAIAI2AgggACACNgIEIAAgATYCAA8LEIeGgIAAAAsgAkEBEIWGgIAAAAuHAQEBfyOAgICAAEEQayICJICAgIAAIAIgAUHw88CAAEENEMyGgIAAIAIgADYCDCACQf3zwIAAQQUgAkEMakGE9MCAABCnhoCAABogAiAAQQxqNgIMIAJBlPTAgABBBSACQQxqQZz0wIAAEKeGgIAAGiACELSGgIAAIQAgAkEQaiSAgICAACAACwIAC4EBAQF/I4CAgIAAQTBrIgMkgICAgAAgAyACNgIEIAMgATYCACADQRxqQQI2AgAgA0EsakGEgICAADYCACADQgI3AgwgA0Hwj8GAADYCCCADQYSAgIAANgIkIAMgA0EgajYCGCADIAM2AiggAyADQQRqNgIgIANBCGogABCUhoCAAAALVAEBfyOAgICAAEEgayIDJICAgIAAIANBFGpBADYCACADQaz0wIAANgIQIANCATcCBCADIAE2AhwgAyAANgIYIAMgA0EYajYCACADIAIQlIaAgAAAC7IEAQd/AkAgAUH/CUsNACABQQV2IQICQAJAAkACQAJAAkACQCAAKAIAIgNFDQAgA0F/aiEEIAAgA0ECdGohBSAAIAMgAmpBAnRqIQMDQCAEQSdLDQIgAiAEaiIGQSdLDQMgAyAFKAIANgIAIAVBfGohBSADQXxqIQMgBEF/aiIEQX9HDQALCwJAIAJFDQAgAEEEaiEFIAJBAnQhA0EAIQQDQCAEQaABRg0EIAUgBGpBADYCACADIARBBGoiBEcNAAsLIAAoAgAiBCACaiEFAkAgAUEfcSIGDQAgACAFNgIAIAAPCyAFQX9qIgNBJ0sNAyAFIQcCQCAAIANBAnRqQQRqKAIAIgNBACABa0EfcSIBdiIIRQ0AIAVBJ0sNBSAAIAVBAnRqQQRqIAg2AgAgBUEBaiEHCwJAIAJBAWoiCCAFTw0AIAQgAmpBAnQgAGpBfGohBANAIAVBfmpBJ0sNByAEQQRqIAMgBnQgBCgCACIDIAF2cjYCACAEQXxqIQQgCCAFQX9qIgVJDQALCyAAIAJBAnRqQQRqIgQgBCgCACAGdDYCACAAIAc2AgAgAA8LQYilwYAAIARBKBCMhoCAAAALQYilwYAAIAZBKBCMhoCAAAALQYilwYAAQShBKBCMhoCAAAALQYilwYAAIANBKBCMhoCAAAALQYilwYAAIAVBKBCMhoCAAAALQYilwYAAIAVBfmpBKBCMhoCAAAALQbKlwYAAQR1BiKXBgAAQjYaAgAAAC4UBAQF/I4CAgIAAQTBrIgIkgICAgAAgAiABNgIEIAIgADYCACACQRxqQQI2AgAgAkEsakGEgICAADYCACACQgI3AgwgAkHok8GAADYCCCACQYSAgIAANgIkIAIgAkEgajYCGCACIAJBBGo2AiggAiACNgIgIAJBCGpB+JPBgAAQlIaAgAAAC4UBAQF/I4CAgIAAQTBrIgIkgICAgAAgAiABNgIEIAIgADYCACACQRxqQQI2AgAgAkEsakGEgICAADYCACACQgI3AgwgAkGslMGAADYCCCACQYSAgIAANgIkIAIgAkEgajYCGCACIAJBBGo2AiggAiACNgIgIAJBCGpBvJTBgAAQlIaAgAAAC6sHAQx/IABBEGooAgAhAwJAAkACQAJAIABBCGooAgAiBEEBRg0AIANBAUYNASAAKAIYIAEgAiAAQRxqKAIAKAIMEY+AgIAAACEDDAMLIANBAUcNAQsCQAJAIAINAEEAIQIMAQsgASACaiEFIABBFGooAgBBAWohBkEAIQcgASEDIAEhCANAIANBAWohCQJAAkACQCADLAAAIgpBf0oNAAJAAkAgCSAFRw0AQQAhCyAFIQMMAQsgAy0AAUE/cSELIANBAmoiCSEDCyAKQR9xIQwCQCAKQf8BcSIKQd8BSw0AIAsgDEEGdHIhCgwCCwJAAkAgAyAFRw0AQQAhDSAFIQ4MAQsgAy0AAEE/cSENIANBAWoiCSEOCyANIAtBBnRyIQsCQCAKQfABTw0AIAsgDEEMdHIhCgwCCwJAAkAgDiAFRw0AQQAhCiAJIQMMAQsgDkEBaiEDIA4tAABBP3EhCgsgC0EGdCAMQRJ0QYCA8ABxciAKciIKQYCAxABHDQIMBAsgCkH/AXEhCgsgCSEDCwJAIAZBf2oiBkUNACAHIAhrIANqIQcgAyEIIAUgA0cNAQwCCwsgCkGAgMQARg0AAkACQCAHRQ0AIAcgAkYNAEEAIQMgByACTw0BIAEgB2osAABBQEgNAQsgASEDCyAHIAIgAxshAiADIAEgAxshAQsgBEEBRg0AIAAoAhggASACIABBHGooAgAoAgwRj4CAgAAADwtBACEJAkAgAkUNACACIQogASEDA0AgCSADLQAAQcABcUGAAUZqIQkgA0EBaiEDIApBf2oiCg0ACwsCQCACIAlrIAAoAgwiBkkNACAAKAIYIAEgAiAAQRxqKAIAKAIMEY+AgIAAAA8LQQAhB0EAIQkCQCACRQ0AQQAhCSACIQogASEDA0AgCSADLQAAQcABcUGAAUZqIQkgA0EBaiEDIApBf2oiCg0ACwsgCSACayAGaiIJIQoCQAJAAkBBACAALQAgIgMgA0EDRhsOBAIBAAECCyAJQQF2IQcgCUEBakEBdiEKDAELQQAhCiAJIQcLIAdBAWohAwJAA0AgA0F/aiIDRQ0BIAAoAhggACgCBCAAKAIcKAIQEYCAgIAAAEUNAAtBAQ8LIAAoAgQhCUEBIQMgACgCGCABIAIgACgCHCgCDBGPgICAAAANACAKQQFqIQMgACgCHCEKIAAoAhghAANAAkAgA0F/aiIDDQBBAA8LIAAgCSAKKAIQEYCAgIAAAEUNAAtBAQ8LIAMLpgkBBn8jgICAgABB8ABrIgQkgICAgAAgBCADNgIMIAQgAjYCCEEBIQUgASEGAkAgAUGBAkkNAEEAIAFrIQdBgAIhCANAAkAgCCABTw0AIAAgCGosAABBv39MDQBBACEFIAghBgwCCyAIQX9qIQZBACEFIAhBAUYNASAHIAhqIQkgBiEIIAlBAUcNAAsLIAQgBjYCFCAEIAA2AhAgBEEAQQUgBRs2AhwgBEGs9MCAAEGul8GAACAFGzYCGAJAAkACQAJAIAIgAUsiCA0AIAMgAUsNACACIANLDQECQAJAIAJFDQAgASACRg0AIAEgAk0NASAAIAJqLAAAQUBIDQELIAMhAgsgBCACNgIgIAJFDQIgAiABRg0CIAFBAWohCQNAAkAgAiABTw0AIAAgAmosAABBQE4NBAsgAkF/aiEIIAJBAUYNBCAJIAJGIQYgCCECIAZFDQAMBAsLIAQgAiADIAgbNgIoIARBMGpBFGpBAzYCACAEQcgAakEUakG4gYCAADYCACAEQdQAakG4gYCAADYCACAEQgM3AjQgBEHUl8GAADYCMCAEQYSAgIAANgJMIAQgBEHIAGo2AkAgBCAEQRhqNgJYIAQgBEEQajYCUCAEIARBKGo2AkggBEEwakHsl8GAABCUhoCAAAALIARB5ABqQbiBgIAANgIAIARByABqQRRqQbiBgIAANgIAIARB1ABqQYSAgIAANgIAIARBMGpBFGpBBDYCACAEQgQ3AjQgBEGgmMGAADYCMCAEQYSAgIAANgJMIAQgBEHIAGo2AkAgBCAEQRhqNgJgIAQgBEEQajYCWCAEIARBDGo2AlAgBCAEQQhqNgJIIARBMGpBwJjBgAAQlIaAgAAACyACIQgLAkAgCCABRg0AQQEhBgJAAkACQAJAIAAgCGoiCSwAACICQX9KDQBBACEFIAAgAWoiBiEBAkAgCUEBaiAGRg0AIAlBAmohASAJLQABQT9xIQULIAJBH3EhCSACQf8BcUHfAUsNASAFIAlBBnRyIQEMAgsgBCACQf8BcTYCJCAEQShqIQIMAgtBACEAIAYhBwJAIAEgBkYNACABQQFqIQcgAS0AAEE/cSEACyAAIAVBBnRyIQECQCACQf8BcUHwAU8NACABIAlBDHRyIQEMAQtBACECAkAgByAGRg0AIActAABBP3EhAgsgAUEGdCAJQRJ0QYCA8ABxciACciIBQYCAxABGDQILIAQgATYCJEEBIQYgBEEoaiECIAFBgAFJDQBBAiEGIAFBgBBJDQBBA0EEIAFBgIAESRshBgsgBCAINgIoIAQgBiAIajYCLCAEQTBqQRRqQQU2AgAgBEHsAGpBuIGAgAA2AgAgBEHkAGpBuIGAgAA2AgAgBEHIAGpBFGpBuYGAgAA2AgAgBEHUAGpBhIGAgAA2AgAgBEIFNwI0IARBlJnBgAA2AjAgBCACNgJYIARBhICAgAA2AkwgBCAEQcgAajYCQCAEIARBGGo2AmggBCAEQRBqNgJgIAQgBEEkajYCUCAEIARBIGo2AkggBEEwakG8mcGAABCUhoCAAAALQamOwYAAQStB0JjBgAAQjYaAgAAAC+ECAgJ/AX4jgICAgABBgAFrIgIkgICAgAAgACgCACEAAkACQAJAAkACQCABKAIAIgNBEHENACAAKQMAIQQgA0EgcQ0BIARBASABEMOGgIAAIQAMAgsgACkDACEEQQAhAANAIAIgAGpB/wBqIASnQQ9xIgNBMHIgA0HXAGogA0EKSRs6AAAgAEF/aiEAIARCBIgiBEIAUg0ACyAAQYABaiIDQYEBTw0CIAFBAUHBkMGAAEECIAIgAGpBgAFqQQAgAGsQxYaAgAAhAAwBC0EAIQADQCACIABqQf8AaiAEp0EPcSIDQTByIANBN2ogA0EKSRs6AAAgAEF/aiEAIARCBIgiBEIAUg0ACyAAQYABaiIDQYEBTw0CIAFBAUHBkMGAAEECIAIgAGpBgAFqQQAgAGsQxYaAgAAhAAsgAkGAAWokgICAgAAgAA8LIANBgAEQkIaAgAAACyADQYABEJCGgIAAAAtCAQF/I4CAgIAAQRBrIgIkgICAgAAgAiABNgIMIAIgADYCCCACQfCOwYAANgIEIAJBrPTAgAA2AgAgAhD2hYCAAAALnQQEAn8BfgJ/AX4CQAJAAkACQAJAIAFBB3EiAkUNACAAKAIAIgNBKU8NAQJAAkAgAw0AQQAhAwwBCyACQQJ0Qdz1wIAAajUCACEEIAAgA0ECdGpBBGohBSADQQJ0IQYgAEEEaiECQgAhBwNAIAIgAjUCACAEfiAHfCIHPgIAIAJBBGohAiAHQiCIIQcgBkF8aiIGDQALIAenIgJFDQAgA0EnSw0DIAUgAjYCACADQQFqIQMLIAAgAzYCAAsCQCABQQhxRQ0AIAAoAgAiA0EpTw0DAkACQCADDQBBACEDDAELIAAgA0ECdCIGakEEaiEFIABBBGohAkIAIQcDQCACIAI1AgBCgMLXL34gB3wiBz4CACACQQRqIQIgB0IgiCEHIAZBfGoiBg0ACyAHpyICRQ0AIANBJ0sNBSAFIAI2AgAgA0EBaiEDCyAAIAM2AgALAkAgAUEQcUUNACAAQaz2wIAAQQIQloaAgAAaCwJAIAFBIHFFDQAgAEG09sCAAEEEEJaGgIAAGgsCQCABQcAAcUUNACAAQcT2wIAAQQcQloaAgAAaCwJAIAFBgAFxRQ0AIABB4PbAgABBDhCWhoCAABoLAkAgAUGAAnFFDQAgAEGY98CAAEEbEJaGgIAAGgsgAA8LIANBKBCPhoCAAAALQYilwYAAIANBKBCMhoCAAAALIANBKBCPhoCAAAALQYilwYAAIANBKBCMhoCAAAALoAYDDH8CfgF/I4CAgIAAQaABayIDJICAgIAAIANBAEGgARDyhoCAACEEAkACQCAAKAIAIgVBKU8NACAAQQRqIQYCQCAFIAJJDQAgASACQQJ0aiEHAkACQAJAIAVFDQAgBUEBaiEIIAVBAnQhAkEAIQlBACEKA0AgBCAJQQJ0aiELA0AgCSEMIAshAyABIAdGDQcgA0EEaiELIAxBAWohCSABKAIAIQ0gAUEEaiIOIQEgDUUNAAsgDa0hD0IAIRAgAiENIAwhASAGIQsDQCABQSdLDQMgAyAQIAM1AgB8IAs1AgAgD358IhA+AgAgEEIgiCEQIANBBGohAyABQQFqIQEgC0EEaiELIA1BfGoiDQ0ACyAFIQMCQCAQpyIBRQ0AIAwgBWoiA0EnSw0EIAQgA0ECdGogATYCACAIIQMLIAMgDGoiAyAKIAogA0kbIQogDiEBDAALC0EAIQpBACEDA0AgByABRg0FIANBAWohAyABKAIAIQsgAUEEaiIJIQEgC0UNACADQX9qIgEgCiAKIAFJGyEKIAkhAQwACwtBiKXBgAAgAUEoEIyGgIAAAAtBiKXBgAAgA0EoEIyGgIAAAAsgAkECdCEIIAJBAWohESAAIAVBAnRqQQRqIQ5BACEMIAYhC0EAIQoCQANAIAQgDEECdGohCQNAIAwhDSAJIQMgCyAORg0EIANBBGohCSANQQFqIQwgCygCACEHIAtBBGoiBSELIAdFDQALIAetIQ9CACEQIAghByANIQsgASEJAkADQCALQSdLDQEgAyAQIAM1AgB8IAk1AgAgD358IhA+AgAgEEIgiCEQIANBBGohAyALQQFqIQsgCUEEaiEJIAdBfGoiBw0ACyACIQMCQCAQpyILRQ0AIA0gAmoiA0EnSw0DIAQgA0ECdGogCzYCACARIQMLIAMgDWoiAyAKIAogA0kbIQogBSELDAELC0GIpcGAACALQSgQjIaAgAAAC0GIpcGAACADQSgQjIaAgAAACyAFQSgQj4aAgAAACyAGIARBoAEQ84aAgAAaIAAgCjYCACAEQaABaiSAgICAACAAC6ojAwF/BH4ZfyOAgICAAEHQCmsiBCSAgICAAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIAEpAwAiBVANACABKQMIIgZQDQEgASkDECIHUA0CIAUgB3wiCCAFVA0DIAUgBn0gBVYNBCADQRFJDQggASwAGiEJIAEuARghCkEAIQEgBEGoCWpBAEGgARDyhoCAABogCq1CMIZCMIcgCEJ/fHl9QsKawegEfkKAoc2gtAJ8QiCIp0EQdEEQdSELIARBqAlqIQwDQCABQShGDQYgDCAFPgIAIAxBBGohDCABQQFqIQEgBUIgiCIFUEUNAAsgBCABNgIQIARBEGpBBHIgBEGoCWpBoAEQ84aAgAAhDUEAIQEgBEGoCWpBAEGgARDyhoCAABogBEGoCWohDANAIAFBKEYNByAMIAY+AgAgDEEEaiEMIAFBAWohASAGQiCIIgZQRQ0ACyAEIAE2ArgBIARBuAFqQQRyIARBqAlqQaABEPOGgIAAGkEAIQEgBEGoCWpBAEGgARDyhoCAABogBEGoCWohDANAIAFBKEYNCCAMIAc+AgAgDEEEaiEMIAFBAWohASAHQiCIIgdQRQ0ACyAEIAE2AuACIARB4AJqQQRyIARBqAlqQaABEPOGgIAAGiAEQoGAgIAQNwOIBCAEQZAEakEAQZwBEPKGgIAAGgJAAkAgCkEASA0AIARBEGogChCOhoCAABogBEG4AWogChCOhoCAABogBEHgAmogChCOhoCAABoMAQsgBEGIBGpBACAKa0EQdEEQdRCOhoCAABoLAkACQCALQX9KDQAgBEEQakEAIAtrQRB0QRB1IgEQlYaAgAAaIARBuAFqIAEQlYaAgAAaIARB4AJqIAEQlYaAgAAaDAELIARBiARqIAsQlYaAgAAaCyAEIAQoAhAiDjYCqAkgBEGoCWpBBHIgDUGgARDzhoCAABoCQAJAAkAgDiAEKALgAiIPIA4gD0sbIhBBKEsNACAQDQFBACEQDAILIBBBKBCPhoCAAAALIARBqAlqQQRyIQEgBEHgAmpBBHIhDEEAIRFBACESA0AgASABKAIAIhMgDCgCAGoiCiARQQFxaiIRNgIAIAogE0kgESAKSXIhESABQQRqIQEgDEEEaiEMIBJBAWoiEiAQSQ0ACyARRQ0AIBBBJ0sNCiAEQagJaiAQQQJ0akEEakEBNgIAIBBBAWohEAsgBCAQNgKoCSAEKAKIBCIRIBAgESAQSxsiAUEpTw0KIAFBAnQhAQJAAkACQAJAA0ACQCABDQBBf0EAIAEbIQwMAgsgAUUNAiAEQYgEaiABaiEMIARBqAlqIAFqIQogAUF8aiEBQX8gDCgCACIMIAooAgAiCkcgDCAKSRsiDEUNAAsLIAwgCUgNAQsgDkEpTw0NAkACQCAODQBBACEODAELIARBEGogDkECdCIMakEEaiEKIARBEGpBBHIhAUIAIQUDQCABIAE1AgBCCn4gBXwiBT4CACABQQRqIQEgBUIgiCEFIAxBfGoiDA0ACyAFpyIBRQ0AIA5BJ0sNDyAKIAE2AgAgDkEBaiEOCyAEIA42AhAgBCgCuAEiCkEpTw0PAkACQCAKDQBBACEKDAELIARBuAFqIApBAnQiDGpBBGohEiAEQbgBakEEciEBQgAhBQNAIAEgATUCAEIKfiAFfCIFPgIAIAFBBGohASAFQiCIIQUgDEF8aiIMDQALIAWnIgFFDQAgCkEnSw0RIBIgATYCACAKQQFqIQoLIAQgCjYCuAEgD0EpTw0RAkAgDw0AIARBADYC4AIMAgsgBEHgAmogD0ECdCIMakEEaiEKIARB4AJqQQRyIQFCACEFA0AgASABNQIAQgp+IAV8IgU+AgAgAUEEaiEBIAVCIIghBSAMQXxqIgwNAAsCQCAFpyIBRQ0AIA9BJ0sNEyAKIAE2AgAgD0EBaiEPCyAEIA82AuACDAELIAtBAWohCwsgBCARNgKwBSAEQbAFakEEciAEQYgEakEEciIUQaABEPOGgIAAIRUgBEGwBWpBARCOhoCAABogBCAEKAKIBDYC2AYgBEHYBmpBBHIgFEGgARDzhoCAACEWIARB2AZqQQIQjoaAgAAaIAQgBCgCiAQ2AoAIIARBgAhqQQRyIBRBoAEQ84aAgAAhFyAEQYAIakEDEI6GgIAAGgJAAkACQAJAIAQoAhAiECAEKAKACCIYIBAgGEsbIg5BKEsNACAEQagJakEEciEZIARB4AJqQQRyIRogBEEQakEEciEbIARBuAFqQQRyIRxBACEdA0AgHSEeIA5BAnQhAQJAAkACQANAAkAgAQ0AQX9BACABGyEMDAILIAFFDQIgBEEQaiABaiEMIARBgAhqIAFqIQogAUF8aiEBQX8gDCgCACIMIAooAgAiCkcgDCAKSRsiDEUNAAsLQQAhHyAMQf8BcUEBSw0BCwJAIA5FDQBBASERIA4hEiAbIQEgFyEMA0AgASABKAIAIhMgDCgCAEF/c2oiCiARQQFxaiIRNgIAIAogE0kgESAKSXIhESABQQRqIQEgDEEEaiEMIBJBf2oiEg0ACyARRQ0YCyAEIA42AhBBCCEfIA4hEAsgECAEKALYBiIBIBAgAUsbIg5BKU8NFyAOQQJ0IQECQAJAAkADQAJAIAENAEF/QQAgARshDAwCCyABRQ0CIARBEGogAWohDCAEQdgGaiABaiEKIAFBfGohAUF/IAwoAgAiDCAKKAIAIgpHIAwgCkkbIgxFDQALCyAMQf8BcUEBTQ0AIBAhDgwBCwJAIA5FDQBBACESQQEhESAbIQEgFiEMA0AgASABKAIAIhMgDCgCAEF/c2oiCiARQQFxaiIRNgIAIAogE0kgESAKSXIhESABQQRqIQEgDEEEaiEMIBJBAWoiEiAOSQ0ACyARRQ0aCyAEIA42AhAgH0EEciEfCyAOIAQoArAFIgEgDiABSxsiD0EpTw0ZIA9BAnQhAQJAAkACQANAAkAgAQ0AQX9BACABGyEMDAILIAFFDQIgBEEQaiABaiEMIARBsAVqIAFqIQogAUF8aiEBQX8gDCgCACIMIAooAgAiCkcgDCAKSRsiDEUNAAsLIAxB/wFxQQFNDQAgDiEPDAELAkAgD0UNAEEAIRJBASERIBshASAVIQwDQCABIAEoAgAiEyAMKAIAQX9zaiIKIBFBAXFqIhE2AgAgCiATSSARIApJciERIAFBBGohASAMQQRqIQwgEkEBaiISIA9JDQALIBFFDRwLIAQgDzYCECAfQQJqIR8LIA8gBCgCiAQiICAPICBLGyIQQSlPDRsgEEECdCEBAkACQAJAA0ACQCABDQBBf0EAIAEbIQwMAgsgAUUNAiAEQRBqIAFqIQwgBEGIBGogAWohCiABQXxqIQFBfyAMKAIAIgwgCigCACIKRyAMIApJGyIMRQ0ACwsgDEH/AXFBAU0NACAPIRAMAQsCQCAQRQ0AQQAhEkEBIREgGyEBIBQhDANAIAEgASgCACITIAwoAgBBf3NqIgogEUEBcWoiETYCACAKIBNJIBEgCklyIREgAUEEaiEBIAxBBGohDCASQQFqIhIgEEkNAAsgEUUNHgsgBCAQNgIQIB9BAWohHwsgHiADRg0DIAIgHmogH0EwajoAACAQIAQoArgBIh8gECAfSxsiAUEpTw0dIB5BAWohHSABQQJ0IQECQANAAkAgAQ0AQX9BACABGyEPDAILAkAgAQ0AQQEhDwwCCyAEQRBqIAFqIQwgBEG4AWogAWohCiABQXxqIQFBfyAMKAIAIgwgCigCACIKRyAMIApJGyIPRQ0ACwsgBCAQNgKoCSAZIA1BoAEQ84aAgAAhAQJAAkACQCAQIAQoAuACIiEgECAhSxsiDkEoSw0AIA4NAUEAIQ4MAgsgDkEoEI+GgIAAAAtBACERIAEhASAaIQxBACESA0AgASABKAIAIhMgDCgCAGoiCiARQQFxaiIRNgIAIAogE0kgESAKSXIhESABQQRqIQEgDEEEaiEMIBJBAWoiEiAOSQ0ACyARRQ0AIA5BJ0sNHyAEQagJaiAOQQJ0akEEakEBNgIAIA5BAWohDgsgBCAONgKoCSAgIA4gICAOSxsiAUEpTw0fIAFBAnQhAQJAA0ACQCABDQBBf0EAIAEbIQwMAgsCQCABDQBBASEMDAILIARBiARqIAFqIQwgBEGoCWogAWohCiABQXxqIQFBfyAMKAIAIgwgCigCACIKRyAMIApJGyIMRQ0ACwsgDyAJSA0CIAwgCUgNAiAQQSlPDSACQAJAIBANAEEAIRAMAQsgBEEQaiAQQQJ0IgxqQQRqIQpCACEFIBshAQNAIAEgATUCAEIKfiAFfCIFPgIAIAFBBGohASAFQiCIIQUgDEF8aiIMDQALIAWnIgFFDQAgEEEnSw0iIAogATYCACAQQQFqIRALIAQgEDYCECAfQSlPDSICQAJAIB8NAEEAIR8MAQsgBEG4AWogH0ECdCIMakEEaiEKQgAhBSAcIQEDQCABIAE1AgBCCn4gBXwiBT4CACABQQRqIQEgBUIgiCEFIAxBfGoiDA0ACyAFpyIBRQ0AIB9BJ0sNJCAKIAE2AgAgH0EBaiEfCyAEIB82ArgBICFBKU8NJAJAAkAgIQ0AQQAhIQwBCyAEQeACaiAhQQJ0IgxqQQRqIQpCACEFIBohAQNAIAEgATUCAEIKfiAFfCIFPgIAIAFBBGohASAFQiCIIQUgDEF8aiIMDQALIAWnIgFFDQAgIUEnSw0mIAogATYCACAhQQFqISELIAQgITYC4AIgECAYIBAgGEsbIg5BKE0NAAsLIA5BKBCPhoCAAAALAkAgDCAJTg0AAkAgDyAJTg0AIARBEGpBARCOhoCAACgCACIBIAQoAogEIgwgASAMSxsiAUEpTw0lIAFBAnQhAQJAA0ACQCABDQBBf0EAIAEbIQwMAgsgAUUNAiAEQRBqIAFqIQwgBEGIBGogAWohCiABQXxqIQFBfyAMKAIAIgwgCigCACIKRyAMIApJGyIMRQ0ACwsgDEH/AXFBAUsNAQsgBEEIaiACIAMgHRCYhoCAACAELQAIQQFxRQ0AIB0gA08NAiACIB1qIAQtAAk6AAAgC0EBaiELIB5BAmohHQsgACALOwEEIAAgHTYCACAEQdAKaiSAgICAAA8LQYj7wIAAIAMgAxCMhoCAAAALQZj7wIAAIB0gAxCMhoCAAAALQa74wIAAQRxBzPjAgAAQjYaAgAAAC0Hc+MCAAEEdQfz4wIAAEI2GgIAAAAtBjPnAgABBHEGo+cCAABCNhoCAAAALQbj5wIAAQTZB8PnAgAAQjYaAgAAAC0GA+sCAAEE3Qbj6wIAAEI2GgIAAAAtBiKXBgABBKEEoEIyGgIAAAAtBiKXBgABBKEEoEIyGgIAAAAtBiKXBgABBKEEoEIyGgIAAAAtByPrAgABBLUH4+sCAABCNhoCAAAALQYilwYAAIBBBKBCMhoCAAAALIAFBKBCPhoCAAAALIA5BKBCPhoCAAAALQYilwYAAIA5BKBCMhoCAAAALIApBKBCPhoCAAAALQYilwYAAIApBKBCMhoCAAAALIA9BKBCPhoCAAAALQYilwYAAIA9BKBCMhoCAAAALQZilwYAAQRpBiKXBgAAQjYaAgAAACyAOQSgQj4aAgAAAC0GYpcGAAEEaQYilwYAAEI2GgIAAAAsgD0EoEI+GgIAAAAtBmKXBgABBGkGIpcGAABCNhoCAAAALIBBBKBCPhoCAAAALQZilwYAAQRpBiKXBgAAQjYaAgAAACyABQSgQj4aAgAAAC0GIpcGAACAOQSgQjIaAgAAACyABQSgQj4aAgAAACyAQQSgQj4aAgAAAC0GIpcGAACAQQSgQjIaAgAAACyAfQSgQj4aAgAAAC0GIpcGAACAfQSgQjIaAgAAACyAhQSgQj4aAgAAAC0GIpcGAACAhQSgQjIaAgAAACyABQSgQj4aAgAAAC+8CAQV/AkAgAiADSQ0AIAMgAWoiBEF/aiEFQQAhBgJAAkACQAJAAkACQANAIAMgBmpFDQEgBSAGaiEHIAZBf2oiCCEGIActAABBOUYNAAsgAyAIaiIGIAJPDQIgBCAIaiIHIActAABBAWo6AABBACEFAkAgBkEBaiADSQ0ADAILIAhBAWohBgNAIAMgBmoiByACTw0EIAQgBmpBMDoAACAGQQFqIgcgBkkhCCAHIQYgCEUNAAsMAQtBASEFAkAgAw0AQTEhBwwBCyACRQ0DIAFBMToAAEEwIQcgA0ECSQ0AQQEhBgNAIAIgBkYNBUEwIQcgASAGakEwOgAAQQEhBSADIAZBAWoiBkcNAAsLIAAgBzoAASAAIAU6AAAPC0GkisGAACAGIAIQjIaAgAAAC0G0isGAACAHIAIQjIaAgAAAC0HEisGAAEEAQQAQjIaAgAAAC0HUisGAACACIAIQjIaAgAAACyADIAIQj4aAgAAAC6kcAwF/A34TfyOAgICAAEHQBmsiBSSAgICAAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgASkDACIGUA0AIAEpAwgiB1ANASABKQMQIghQDQIgBiAIfCAGVA0DIAYgB30gBlYNBSABLgEYIQlBACEBIAVBqAVqQQBBoAEQ8oaAgAAaIAmtQjCGQjCHIAZCf3x5fULCmsHoBH5CgKHNoLQCfEIgiKdBEHRBEHUhCiAFQagFaiELA0AgAUEoRg0FIAsgBj4CACALQQRqIQsgAUEBaiEBIAZCIIgiBlBFDQALIAUgATYCCCAFQQhqQQRyIAVBqAVqQaABEPOGgIAAGiAFQoGAgIAQNwOwASAFQbgBakEAQZwBEPKGgIAAGiAFQbABaiAFQQhqIAlBAEgbIAkgCUEfdSIBaiABc0EQdEEQdRCOhoCAABogBUGwAWogBUEIaiAKQX9KGyAKIApBH3UiAWogAXNBEHRBEHUQlYaAgAAaIAUgBSgCsAEiATYCqAUgBUGoBWpBBHIgBUGwAWpBBHIiDEGgARDzhoCAABogAyEJAkAgA0EJTQ0AAkAgAUEoSw0AIAMhCQNAAkAgAUUNACABQQJ0IQFCACEGA0AgBUGoBWogAWoiCyAGQiCGIAs1AgCEIgZCgJTr3AOAIgc+AgAgBiAHQoCU69wDfn0hBiABQXxqIgENAAsLIAlBd2oiCUEKSQ0CIAUoAqgFIgFBKE0NAAsLIAFBKBCPhoCAAAALAkACQAJAIAlBAnRBhPbAgABqKAIAIgtFDQAgBSgCqAUiAUEpTw0JIAENAUEAIQEMAgtBz6XBgABBG0GIpcGAABCNhoCAAAALIAFBAnQhASALrSEGQgAhBwNAIAVBqAVqIAFqIgsgB0IghiALNQIAhCIHIAaAIgg+AgAgByAIIAZ+fSEHIAFBfGoiAQ0ACyAFKAKoBSEBCwJAAkACQCABIAUoAggiDSABIA1LGyIOQShLDQAgDg0BQQAhDgwCCyAOQSgQj4aAgAAACyAFQagFakEEciEBIAVBCGpBBHIhC0EAIQ9BACEQA0AgASABKAIAIhEgCygCAGoiCSAPQQFxaiIPNgIAIAkgEUkgDyAJSXIhDyABQQRqIQEgC0EEaiELIBBBAWoiECAOSQ0ACyAPRQ0AIA5BJ0sNCCAFQagFaiAOQQJ0akEEakEBNgIAIA5BAWohDgsgBSAONgKoBSAOIAUoArABIhIgDiASSxsiAUEpTw0IIAFBAnQhAQJAAkACQANAAkAgAQ0AQX9BACABGyELDAILIAFFDQIgBUGoBWogAWohCyAFQbABaiABaiEJIAFBfGohAUF/IAsoAgAiCyAJKAIAIglHIAsgCUkbIgtFDQALCyALQf8BcUECSQ0AIA1BKU8NCwJAIA0NACAFQQA2AggMAgsgBUEIaiANQQJ0IgtqQQRqIQkgBUEIakEEciEBQgAhBgNAIAEgATUCAEIKfiAGfCIGPgIAIAFBBGohASAGQiCIIQYgC0F8aiILDQALAkAgBqciAUUNACANQSdLDQ0gCSABNgIAIA1BAWohDQsgBSANNgIIDAELIApBAWohCgtBASEPAkACQAJAAkAgCkEQdEEQdSIBIARBEHRBEHUiC04NAEEAIRMMAQsCQCAKIARrQRB0QRB1IAMgASALayADSRsiEw0AQQAhEwwBCyAFIBI2AtgCIAVB2AJqQQRyIAxBoAEQ84aAgAAhFCAFQdgCakEBEI6GgIAAGiAFIAUoArABNgKABCAFQYAEakEEciAMQaABEPOGgIAAIRUgBUGABGpBAhCOhoCAABogBSAFKAKwATYCqAUgBUGoBWpBBHIgDEGgARDzhoCAACEWIAVBqAVqQQMQjoaAgAAaIAVBsAFqQQRyIRcgBUEIakEEciEYIAUoAgghD0EAIRkDQCAZIRogD0EpTw0PIBpBAWohGSAPQQJ0IQEgGCELA0AgAUUNHiABQXxqIQEgCygCACEJIAtBBGohCyAJRQ0ACyAPIAUoAqgFIgEgDyABSxsiDkEpTw0QIA5BAnQhAQJAAkACQANAAkAgAQ0AQX9BACABGyELDAILIAFFDQIgBUEIaiABaiELIAVBqAVqIAFqIQkgAUF8aiEBQX8gCygCACILIAkoAgAiCUcgCyAJSRsiC0UNAAsLQQAhGyALQf8BcUECTw0BCwJAIA5FDQBBACEQQQEhDyAYIQEgFiELA0AgASABKAIAIhEgCygCAEF/c2oiCSAPQQFxaiIPNgIAIAkgEUkgDyAJSXIhDyABQQRqIQEgC0EEaiELIBBBAWoiECAOSQ0ACyAPRQ0TCyAFIA42AghBCCEbIA4hDwsgDyAFKAKABCIBIA8gAUsbIg5BKU8NEiAOQQJ0IQECQAJAAkADQAJAIAENAEF/QQAgARshCwwCCyABRQ0CIAVBCGogAWohCyAFQYAEaiABaiEJIAFBfGohAUF/IAsoAgAiCyAJKAIAIglHIAsgCUkbIgtFDQALCyALQf8BcUEBTQ0AIA8hDgwBCwJAIA5FDQBBACEQQQEhDyAYIQEgFSELA0AgASABKAIAIhEgCygCAEF/c2oiCSAPQQFxaiIPNgIAIAkgEUkgDyAJSXIhDyABQQRqIQEgC0EEaiELIBBBAWoiECAOSQ0ACyAPRQ0VCyAFIA42AgggG0EEciEbCyAOIAUoAtgCIgEgDiABSxsiDUEpTw0UIA1BAnQhAQJAAkACQANAAkAgAQ0AQX9BACABGyELDAILIAFFDQIgBUEIaiABaiELIAVB2AJqIAFqIQkgAUF8aiEBQX8gCygCACILIAkoAgAiCUcgCyAJSRsiC0UNAAsLIAtB/wFxQQFNDQAgDiENDAELAkAgDUUNAEEAIRBBASEPIBghASAUIQsDQCABIAEoAgAiESALKAIAQX9zaiIJIA9BAXFqIg82AgAgCSARSSAPIAlJciEPIAFBBGohASALQQRqIQsgEEEBaiIQIA1JDQALIA9FDRcLIAUgDTYCCCAbQQJqIRsLIA0gBSgCsAEiEiANIBJLGyIPQSlPDRYgD0ECdCEBAkACQAJAA0ACQCABDQBBf0EAIAEbIQsMAgsgAUUNAiAFQQhqIAFqIQsgBUGwAWogAWohCSABQXxqIQFBfyALKAIAIgsgCSgCACIJRyALIAlJGyILRQ0ACwsgC0H/AXFBAU0NACANIQ8MAQsCQCAPRQ0AQQAhEUEBIRAgGCEBIBchCwNAIAEgASgCACIOIAsoAgBBf3NqIgkgEEEBcWoiEDYCACAJIA5JIBAgCUlyIRAgAUEEaiEBIAtBBGohCyARQQFqIhEgD0kNAAsgEEUNGQsgBSAPNgIIIBtBAWohGwsgGiADRg0CIAIgGmogG0EwajoAACAPQSlPDRgCQAJAIA8NAEEAIQ8MAQsgBUEIaiAPQQJ0IgtqQQRqIQlCACEGIBghAQNAIAEgATUCAEIKfiAGfCIGPgIAIAFBBGohASAGQiCIIQYgC0F8aiILDQALIAanIgFFDQAgD0EnSw0aIAkgATYCACAPQQFqIQ8LIAUgDzYCCCAZIBNHDQALQQAhDwsgEkEpTw0YAkACQCASDQBBACESDAELIAVBsAFqIBJBAnQiAWpBBGohC0IAIQYDQCAMIAw1AgBCBX4gBnwiBj4CACAMQQRqIQwgBkIgiCEGIAFBfGoiAQ0ACyAGpyIBRQ0AIBJBJ0sNGiALIAE2AgAgEkEBaiESCyAFIBI2ArABIAUoAggiASASIAEgEksbIgFBKU8NGiABQQJ0IQECQAJAA0AgAUUNASABRQ0CIAVBCGogAWohCyAFQbABaiABaiEJIAFBfGohAUF/IAsoAgAiCyAJKAIAIglHIAsgCUkbIgtFDQALIAtB/wFxQQFHDR4MAQsgAQ0dIA8NACATQX9qIgEgA08NAiACIAFqLQAAQQFxRQ0dCyAFIAIgAyATEJiGgIAAIAUtAABBAXFFDRwgCkEQdEGAgARqQRB1IgogBEEQdEEQdUwNHCATIANPDRwgAiATaiAFLQABOgAAIBNBAWohEwwcC0H4+8CAACADIAMQjIaAgAAAC0GI/MCAACABIAMQjIaAgAAAC0Gu+MCAAEEcQaj7wIAAEI2GgIAAAAtB3PjAgABBHUG4+8CAABCNhoCAAAALQYz5wIAAQRxByPvAgAAQjYaAgAAAC0G4+cCAAEE2Qdj7wIAAEI2GgIAAAAtBiKXBgABBKEEoEIyGgIAAAAtBgPrAgABBN0Ho+8CAABCNhoCAAAALIAFBKBCPhoCAAAALQYilwYAAIA5BKBCMhoCAAAALIAFBKBCPhoCAAAALIA1BKBCPhoCAAAALQYilwYAAIA1BKBCMhoCAAAALIA9BKBCPhoCAAAALIA5BKBCPhoCAAAALQZilwYAAQRpBiKXBgAAQjYaAgAAACyAOQSgQj4aAgAAAC0GYpcGAAEEaQYilwYAAEI2GgIAAAAsgDUEoEI+GgIAAAAtBmKXBgABBGkGIpcGAABCNhoCAAAALIA9BKBCPhoCAAAALQZilwYAAQRpBiKXBgAAQjYaAgAAACyAPQSgQj4aAgAAAC0GIpcGAACAPQSgQjIaAgAAACyASQSgQj4aAgAAAC0GIpcGAACASQSgQjIaAgAAACyABQSgQj4aAgAAACyATIBpJDQEgEyADSw0CIBMgGkYNACACIBpqQTAgEyAaaxDyhoCAABoLIAAgCjsBBCAAIBM2AgAgBUHQBmokgICAgAAPCyAaIBMQkIaAgAAACyATIAMQj4aAgAAAC/QSBgF/BH4CfxR+BX8BfiOAgICAAEHQAGsiBCSAgICAAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgASkDACIFUA0AIAEpAwgiBlANASABKQMQIgdQDQIgBSAHfCIHIAVUDQMgBSAGfSIIIAVWDQQgA0ERSQ0FIAdC//////////8fVg0KIAQgAS8BGCIBOwEQIAQgCDcDCCABIAFBYGogASAHQoCAgIAQVCIJGyIKQXBqIAogB0IghiAHIAkbIgdCgICAgICAwABUIgkbIgpBeGogCiAHQhCGIAcgCRsiB0KAgICAgICAgAFUIgkbIgpBfGogCiAHQgiGIAcgCRsiB0KAgICAgICAgBBUIgkbIgpBfmogCiAHQgSGIAcgCRsiB0KAgICAgICAgMAAVCIJGyAHQgKGIAcgCRsiC0I/h6dBf3NqIglrQRB0QRB1IgpBAEgNBiAEQn8gCq1CP4MiDIgiByAIgzcDKCAIIAdWDQcgBCABOwEQIAQgBTcDCCAEIAcgBYM3AyggBSAHVg0IQaB/IAlrQRB0QRB1QdAAbEGwpwVqQc4QbSIBQdEATw0JIAFBBHQiAUGY/MCAAGopAwAiB0L/////D4MiBiAFIAyGIgVCIIgiDX4iDkIgiCIPIAdCIIgiECANfnwgECAFQv////8PgyIFfiIHQiCIIhF8IRIgDkL/////D4MgBiAFfkIgiHwgB0L/////D4N8QoCAgIAIfEIgiCETQgFBACAJIAFBoPzAgABqLwEAamtBP3GtIgeGIg5Cf3whFCAGIAggDIYiBUIgiCIVfiIIQv////8PgyAGIAVC/////w+DIgV+QiCIfCAQIAV+IgVC/////w+DfEKAgICACHxCIIghFiAQIBV+IQwgBUIgiCEXIAhCIIghGCABQaL8wIAAai8BACEBAkACQAJAAkAgECALIAtCf4VCP4iGIgVCIIgiGX4iGiAGIBl+IghCIIgiG3wgECAFQv////8PgyIFfiILQiCIIhx8IAhC/////w+DIAYgBX5CIIh8IAtC/////w+DfEKAgICACHxCIIgiHXxCAXwiHiAHiKciCkGQzgBJDQAgCkHAhD1JDQEgCkGAwtcvSQ0CQQhBCSAKQYCU69wDSSIJGyEfQYDC1y9BgJTr3AMgCRshCQwDCwJAIApB5ABJDQBBAkEDIApB6AdJIgkbIR9B5ABB6AcgCRshCQwDC0EBQQogCkEKSRshCSAKQQlLIR8MAgtBBEEFIApBoI0GSSIJGyEfQZDOAEGgjQYgCRshCQwBC0EGQQcgCkGAreIESSIJGyEfQcCEPUGAreIEIAkbIQkLIBIgE3whEiAeIBSDIQUgHyABa0EBaiEgIB4gDCAYfCAXfCAWfH1CAXwiCyAUgyEIQQAhAQJAAkADQCAKIAluISEgAyABRg0BIAIgAWoiIiAhQTBqIiM6AAACQAJAAkAgCyAKICEgCWxrIgqtIAeGIgwgBXwiBlYNACAfIAFHDQIgAUEBaiEBQgEhBgNAIAYhCyAIIQwgASADTw0GIAtCCn4hBiACIAFqIAVCCn4iBSAHiKdBMGoiCToAACABQQFqIQEgDEIKfiIIIAUgFIMiBVgNAAsgBiAeIBJ9fiIHIAZ8IQ0gCCAFfSAOVCIKDRQgByAGfSIUIAVWDQEMFAsgAUEBaiEKIAEgA08NECALIAZ9Ig4gCa0gB4YiB1QhASAeIBJ9IghCAXwhJCAIQn98IhQgBlgNESAOIAdUDREgHCAbfCAdfCIIIBp8IBJ9IAwgBXx9IR4gBSAPfCARfCATfCAQIA0gGX1+fCAbfSAcfSAdfSAMfCEOIAggECAZIBV9fnwgF30gGH0gFn0gBSAHfCAMfH1CAnwhEEIAIQUDQAJAIAYgB3wiCCAUVA0AIB4gBXwgByAOfFoNAEEAIQEMEwsgIiAjQX9qIiM6AAAgECAFfCIMIAdUIQEgCCAUWg0TIA4gB3whDiAFIAd9IQUgCCEGIAwgB1QNEwwACwsgAiABakF/aiEhIAxCCn4gDiAFfH0hECAOIBJCCn4gGyAcfCAdfCAafEIKfn0gC358IR4gFCAFfSEZQgAhBwNAAkAgBSAOfCIGIBRUDQAgGSAHfCAeIAV8Wg0AQQAhCgwUCyAhIAlBf2oiCToAACAQIAd8IgwgDlQhCiAGIBRaDRQgByAOfSEHIAYhBSAMIA5UDRQMAAsLIAFBAWohASAJQQpJISEgCUEKbiEJICFFDQALQaCIwYAAQRlBhIjBgAAQjYaAgAAAC0G8iMGAACADIAMQjIaAgAAAC0HMiMGAACABIAMQjIaAgAAAC0Gu+MCAAEEcQeSGwYAAEI2GgIAAAAtB3PjAgABBHUH0hsGAABCNhoCAAAALQYz5wIAAQRxBhIfBgAAQjYaAgAAAC0G4+cCAAEE2QZSHwYAAEI2GgIAAAAtBgPrAgABBN0Gkh8GAABCNhoCAAAALQcj6wIAAQS1BtIfBgAAQjYaAgAAAC0Gt9MCAAEEdQej0wIAAEI2GgIAAAAsgBEEkakG6gYCAADYCACAEQcQAakECNgIAIARCAzcCNCAEQbT1wIAANgIwIARBuoGAgAA2AhwgBCAEQShqNgJIIAQgBEEYajYCQCAEIARBzABqNgIgIAQgBEHIAGo2AhggBCAEQQhqNgJMIARBMGpBzPXAgAAQlIaAgAAACyAEQSRqQbqBgIAANgIAIARBxABqQQI2AgAgBEIDNwI0IARBtPXAgAA2AjAgBEG6gYCAADYCHCAEIARBKGo2AkggBCAEQRhqNgJAIAQgBEHMAGo2AiAgBCAEQcgAajYCGCAEIARBCGo2AkwgBEEwakHM9cCAABCUhoCAAAALQdSGwYAAIAFB0QAQjIaAgAAAC0HEh8GAAEEtQfSHwYAAEI2GgIAAAAsgCiADEI+GgIAAAAsgBiEICwJAICQgCFgNACABDQBBACEJIAggB3wiBSAkVA0DICQgCH0gBSAkfVoNAwtBACEJIAhCAlQNAiAIIAtCfHxWDQIgACAKNgIEIABBCGogIDsBAEEBIQkMAgsgBSEGCwJAIA0gBlgNACAKDQBBACEJIAYgDnwiBSANVA0BIA0gBn0gBSANfVoNAQtBACEJIAtCFH4gBlYNACAGIAtCWH4gCHxWDQAgACABNgIEIABBCGogIDsBAEEBIQkLIAAgCTYCACAEQdAAaiSAgICAAAuODQoBfwF+An8EfgN/AX4CfwF+An8BfiOAgICAAEEQayIFJICAgIAAAkACQAJAAkACQAJAAkACQAJAIAEpAwAiBlANACAGQv//////////H1YNASADRQ0DQaB/IAEvARgiAUFgaiABIAZCgICAgBBUIgcbIgFBcGogASAGQiCGIAYgBxsiBkKAgICAgIDAAFQiBxsiAUF4aiABIAZCEIYgBiAHGyIGQoCAgICAgICAAVQiBxsiAUF8aiABIAZCCIYgBiAHGyIGQoCAgICAgICAEFQiBxsiAUF+aiABIAZCBIYgBiAHGyIGQoCAgICAgICAwABUIgcbIAZCAoYgBiAHGyIGQj+Hp0F/c2oiB2tBEHRBEHVB0ABsQbCnBWpBzhBtIgFB0QBPDQIgAUEEdCIBQaL8wIAAai8BACEIAkACQAJAAkAgAUGY/MCAAGopAwAiCUL/////D4MiCiAGIAZCf4VCP4iGIgZCIIgiC34iDEIgiCAJQiCIIgkgC358IAkgBkL/////D4MiBn4iCUIgiHwgDEL/////D4MgCiAGfkIgiHwgCUL/////D4N8QoCAgIAIfEIgiHwiBkFAIAcgAUGg/MCAAGovAQBqa0EQdEEQdSINQT9xrSIMiKciDkGQzgBJDQAgDkHAhD1JDQEgDkGAwtcvSQ0CQQhBCSAOQYCU69wDSSIBGyEPQYDC1y9BgJTr3AMgARshBwwDCwJAIA5B5ABJDQBBAkEDIA5B6AdJIgEbIQ9B5ABB6AcgARshBwwDC0EBQQogDkEKSRshByAOQQlLIQ8MAgtBBEEFIA5BoI0GSSIBGyEPQZDOAEGgjQYgARshBwwBC0EGQQcgDkGAreIESSIBGyEPQcCEPUGAreIEIAEbIQcLQgEgDIYhEAJAAkACQAJAAkAgDyAIayIRQRB0QYCABGpBEHUiEiAEQRB0QRB1IgFMDQAgBiAQQn98IhODIQYgEiAEa0EQdEEQdSADIBIgAWsgA0kbIhRBf2ohFUEAIQEDQCAOIAduIQggAyABRg0EIA4gCCAHbGshDiACIAFqIAhBMGo6AAAgFSABRg0CIA8gAUYNAyABQQFqIQEgB0EKSSEIIAdBCm4hByAIRQ0AC0GgiMGAAEEZQdSJwYAAEI2GgIAAAAsgBkIKgCEGQQAhCCAHrSAMhiIJIBBYDQwgCSAQfSAQWA0MAkAgCSAGfSAGWA0AIAkgBkIBhn0gEEIBhloNCQsgBiAQWA0MIAkgBiAQfSIGfSAGVg0MQQAhAQJAIBFBEHRBgIAIakEQdSIHIARBEHRBEHVMDQAgAkExOgAAQQEhAQsgACABNgIEIABBCGogBzsBAAwLC0EAIQggB60gDIYiCSAQWA0LIAkgEH0gEFgNCwJAIAkgDq0gDIYgBnwiBn0gBlgNACAJIAZCAYZ9IBBCAYZaDQkLIAYgEFgNCyAJIAYgEH0iBn0gBlYNCyAFIAIgAyAUEJiGgIAAQQEhCAJAIAUtAABBAXFFDQAgEUEQdEGAgAhqQRB1IhIgBEEQdEEQdUwNACAUIANPDQAgAiAUaiAFLQABOgAAIBRBAWohFAsgACAUNgIEIABBCGogEjsBAAwLCyABQQFqIQEgDUF/akE/ca0hFkIBIQkDQEEAIQggCSIKIBaIQgBSDQsgASADTw0CIApCCn4hCSAGQgp+IgsgE4MhBiACIAFqIAsgDIinQTBqOgAAIBQgAUEBaiIBRw0ACyAQIAlYDQogECAJfSAJWA0KAkAgECAGfSAGWA0AIBAgBkIBhn0gCkIUfloNCQsgBiAJWA0KIBAgBiAJfSIGfSAGVg0KIAVBCGogAiADIBQQmIaAgABBASEIAkAgBS0ACEEBcUUNACARQRB0QYCACGpBEHUiEiAEQRB0QRB1TA0AIBQgA08NACACIBRqIAUtAAk6AAAgFEEBaiEUCyAAIBQ2AgQgAEEIaiASOwEADAoLQeSJwYAAIAMgAxCMhoCAAAALQfSJwYAAIAEgAxCMhoCAAAALQa74wIAAQRxBgInBgAAQjYaAgAAAC0GQicGAAEEkQbSJwYAAEI2GgIAAAAtB1IbBgAAgAUHRABCMhoCAAAALQdyIwYAAQSFBxInBgAAQjYaAgAAACyAAQQA2AgQgAEEIaiASOwEADAILIAAgFDYCBCAAQQhqIBI7AQAMAQsgACAUNgIEIABBCGogEjsBAAtBASEICyAAIAg2AgAgBUEQaiSAgICAAAsRACAANQIAQQEgARDDhoCAAAv1BQQDfwJ+AX8CfiOAgICAAEGQAWsiBCSAgICAACAEIAM2AmwCQAJAAkACQAJAAkACQAJAAkAgA0F+akEiSw0AAkAgAg0AIABBADoAAQwICwJAIAEtAABBK0cNAEEBIQUgAkF/aiICRQ0CIAFBAWohAQsgA0ELSQ0CIARB4ABqIQZCACEHQgAhCANAIAJFDQcCQCABLQAAIglBUGoiBUEKSQ0AAkAgCUGff2pBGkkNACAJQb9/akEaTw0GIAlBSWohBQwBCyAJQal/aiEFCyAFIANPDQQgBEHIAGogCEIAIAOtIgpCABD3hoCAACAEQdgAaiAHQgAgCkIAEPeGgIAAIARBOGpCAEIAIAdCABD3hoCAACAEKQNQIAQpA0CEQgBSIAYpAwAiByAEKQNIIAQpAzh8fCIKIAdUckEBRg0FIAFBAWohASACQX9qIQIgBCkDWCILIAWtfCIHIAtUIgUgCiAFrXwiCCAKVCAHIAtaG0EBRw0ADAYLCyAEQYQBakEBNgIAIARCATcCdCAEQaCMwYAANgJwIARBhICAgAA2AowBIAQgBEGIAWo2AoABIAQgBEHsAGo2AogBIARB8ABqQcCMwYAAEJSGgIAAAAsgAEEAOgABDAYLIARBMGohCUIAIQdCACEIA0AgAkUNBCABLQAAQVBqIgVBCUsNASAFIANPDQEgBEEYaiAIQgAgA60iCkIAEPeGgIAAIARBKGogB0IAIApCABD3hoCAACAEQQhqQgBCACAHQgAQ94aAgAAgBCkDICAEKQMQhEIAUiAJKQMAIgcgBCkDGCAEKQMIfHwiCiAHVHINAiABQQFqIQEgAkF/aiECIAQpAygiCyAFrXwiByALVCIFIAogBa18IgggClQgByALWhtFDQAMAwsLQQEhBSAAQQE6AAEMBAsgAEECOgABDAILIABBAjoAAQwBCyAAQRBqIAg3AwAgAEEIaiAHNwMAQQAhBQwBC0EBIQULIAAgBToAACAEQZABaiSAgICAAAtvAQF/QdCMwYAAIQICQAJAAkACQAJAIAAtAAAOBQABAgMEAAsgAUHfjcGAAEEmEJGGgIAADwsgAUHCjcGAAEEdEJGGgIAADwsgAUGcjcGAAEEmEJGGgIAADwtB9ozBgAAhAgsgASACQSYQkYaAgAAL/gYBCn8jgICAgABBMGsiAySAgICAACADQSRqIAE2AgAgA0EDOgAoIANCgICAgIAENwMIIAMgADYCIEEAIQQgA0EANgIYIANBADYCEAJAAkACQAJAAkACQCACKAIIIgUNACACKAIAIQYgAigCBCIHIAJBFGooAgAiBSAFIAdLGyIIRQ0BIAIoAhAhBUEBIQkgACAGKAIAIAYoAgQgASgCDBGPgICAAAANBSAGQQxqIQJBASEEA0ACQCAFKAIAIANBCGogBUEEaigCABGAgICAAABFDQBBASEJDAcLIAQgCE8NAiACQXxqIQAgAigCACEBIAJBCGohAiAFQQhqIQVBASEJIARBAWohBCADKAIgIAAoAgAgASADKAIkKAIMEY+AgIAAAEUNAAwGCwsgAigCACEGIAIoAgQiByACQQxqKAIAIgkgCSAHSxsiCkUNACACQRRqKAIAIQggAigCECELQQEhCSAAIAYoAgAgBigCBCABKAIMEY+AgIAAAA0EIAZBDGohAkEBIQQDQCADIAVBBGooAgA2AgwgAyAFQRxqLQAAOgAoIAMgBUEIaigCADYCCCAFQRhqKAIAIQlBACEBQQAhAAJAAkACQCAFQRRqKAIADgMBAAIBCyAJIAhPDQUgCUEDdCEMQQAhACALIAxqIgwoAgRBu4GAgABHDQEgDCgCACgCACEJC0EBIQALIAMgCTYCFCADIAA2AhAgBUEQaigCACEJAkACQAJAIAVBDGooAgAOAwEAAgELIAkgCE8NBiAJQQN0IQAgCyAAaiIAKAIEQbuBgIAARw0BIAAoAgAoAgAhCQtBASEBCyADIAk2AhwgAyABNgIYIAUoAgAiCSAITw0CAkAgCyAJQQN0aiIJKAIAIANBCGogCSgCBBGAgICAAABFDQBBASEJDAYLIAQgCk8NASACQXxqIQAgAigCACEBIAJBCGohAiAFQSBqIQVBASEJIARBAWohBCADKAIgIAAoAgAgASADKAIkKAIMEY+AgIAAAEUNAAwFCwsCQCAHIARNDQBBASEJIAMoAiAgBiAEQQN0aiIFKAIAIAUoAgQgAygCJCgCDBGPgICAAAANBAtBACEJDAMLQbySwYAAIAkgCBCMhoCAAAALQcySwYAAIAkgCBCMhoCAAAALQcySwYAAIAkgCBCMhoCAAAALIANBMGokgICAgAAgCQuXAQEDfyOAgICAAEEgayICJICAgIAAAkAgACABEKGGgIAADQAgAUEcaigCACEDIAEoAhghBCACQRxqQQA2AgAgAkGs9MCAADYCGCACQgE3AgwgAkGIjsGAADYCCCAEIAMgAkEIahCfhoCAAA0AIABBBGogARChhoCAACEBIAJBIGokgICAgAAgAQ8LIAJBIGokgICAgABBAQvRAgEDfyOAgICAAEGAAWsiAiSAgICAAAJAAkACQAJAAkAgASgCACIDQRBxDQAgACgCACEEIANBIHENASAErUEBIAEQw4aAgAAhAAwCCyAAKAIAIQRBACEAA0AgAiAAakH/AGogBEEPcSIDQTByIANB1wBqIANBCkkbOgAAIABBf2ohACAEQQR2IgQNAAsgAEGAAWoiBEGBAU8NAiABQQFBwZDBgABBAiACIABqQYABakEAIABrEMWGgIAAIQAMAQtBACEAA0AgAiAAakH/AGogBEEPcSIDQTByIANBN2ogA0EKSRs6AAAgAEF/aiEAIARBBHYiBA0ACyAAQYABaiIEQYEBTw0CIAFBAUHBkMGAAEECIAIgAGpBgAFqQQAgAGsQxYaAgAAhAAsgAkGAAWokgICAgAAgAA8LIARBgAEQkIaAgAAACyAEQYABEJCGgIAAAAsMAEKY3Iamxs6tyQgLIQAgASgCGEGQjsGAAEELIAFBHGooAgAoAgwRj4CAgAAACyEAIAEoAhhBm47BgABBDiABQRxqKAIAKAIMEY+AgIAAAAv6AgIDfwF+QQIhAwJAAkACQAJAAkAgAUF3aiIEQR5NDQAgAUHcAEcNAQwCC0H0ACEFAkACQCAEDh8FAQICAAICAgICAgICAgICAgICAgICAgICAwICAgIDBQtB8gAhBQwEC0HuACEFDAMLAkAgAkUNACABQQp2IQUCQAJAAkACQCABQYDYB0kNAEEeIQMgBUGAB0cNBAwBCyAFQcC+wYAAai0AACIDQR5LDQELIANBBHQgAUEGdkEPcXJBu7/BgABqLQAAIgVBiwFPDQFBAyEDIAVBA3RBsMPBgABqKQMAQgEgAUE/ca2Gg1ANAiABQQFyZ0ECdkEHc61CgICAgNAAhCEGDAQLQcykwYAAIANBHxCMhoCAAAALQdykwYAAIAVBiwEQjIaAgAAACwJAIAEQpoaAgABFDQBBASEDDAILIAFBAXJnQQJ2QQdzrUKAgICA0ACEIQZBAyEDDAELCyABIQULIAAgBTYCBCAAIAM2AgAgAEEIaiAGNwIAC/AGAQd/AkACQAJAAkACQAJAAkACQAJAAkAgAEGAgARJDQAgAEGAgAhJDQFBACEBIABB4ot0akHijSxJDQggAEGfqHRqQZ8YSQ0IIABB3uJ0akEOSQ0IIABB/v//AHFBnvAKRg0IIABBqbJ1akEpSQ0IIABBy5F1akELSQ0IIABBkPxHakGP/AtLDwsgAEGA/gNxQQh2IQJB/JnBgAAhA0EAIQQgAEH/AXEhBQNAIANBAmohBiAEIAMtAAEiAWohBwJAIAMtAAAiAyACRg0AIAMgAksNCCAHIQQgBiEDIAZBzprBgABHDQEMCAsgByAESQ0CIAdBpQJLDQMgBEHOmsGAAGohAwJAA0AgAUUNASABQX9qIQEgAy0AACEEIANBAWohAyAEIAVHDQALQQAhAQwJCyAHIQQgBiEDIAZBzprBgABHDQAMBwsLIABBgP4DcUEIdiECQa2fwYAAIQNBACEEIABB/wFxIQUDQCADQQJqIQYgBCADLQABIgFqIQcCQCADLQAAIgMgAkYNACADIAJLDQYgByEEIAYhAyAGQfOfwYAARw0BDAYLIAcgBEkNAyAHQaYBSw0EIARB85/BgABqIQMCQANAIAFFDQEgAUF/aiEBIAMtAAAhBCADQQFqIQMgBCAFRw0AC0EAIQEMCAsgByEEIAYhAyAGQfOfwYAARw0ADAULCyAEIAcQkIaAgAAACyAHQaUCEI+GgIAAAAsgBCAHEJCGgIAAAAsgB0GmARCPhoCAAAALIABB//8DcSEFQZmhwYAAIQNBASEBAkADQCADQQFqIQACQAJAIAMtAAAiBEEYdEEYdSIHQQBIDQAgACEDDAELIABBsaTBgABGDQIgB0H/AHFBCHQgAy0AAXIhBCADQQJqIQMLIAUgBGsiBUEASA0DIAFBAXMhASADQbGkwYAARw0ADAMLC0GpjsGAAEErQeyZwYAAEI2GgIAAAAsgAEH//wNxIQVB85zBgAAhA0EBIQEDQCADQQFqIQACQAJAIAMtAAAiBEEYdEEYdSIHQQBIDQAgACEDDAELIABBrZ/BgABGDQMgB0H/AHFBCHQgAy0AAXIhBCADQQJqIQMLIAUgBGsiBUEASA0BIAFBAXMhASADQa2fwYAARw0ACwsgAUEBcQ8LQamOwYAAQStB7JnBgAAQjYaAgAAAC/IDAgR/An4jgICAgABBwABrIgUkgICAgABBASEGAkAgAC0ABA0AIAAtAAUhBwJAIAAoAgAiCC0AAEEEcQ0AQQEhBiAIKAIYQaGQwYAAQaOQwYAAIAdB/wFxIgcbQQJBAyAHGyAIQRxqKAIAKAIMEY+AgIAAAA0BQQEhBiAAKAIAIggoAhggASACIAhBHGooAgAoAgwRj4CAgAAADQFBASEGIAAoAgAiCCgCGEHcjsGAAEECIAhBHGooAgAoAgwRj4CAgAAADQEgAyAAKAIAIAQoAgwRgICAgAAAIQYMAQsCQCAHQf8BcQ0AQQEhBiAIKAIYQZyQwYAAQQMgCEEcaigCACgCDBGPgICAAAANASAAKAIAIQgLQQEhBiAFQQE6ABcgBUE0akGAkMGAADYCACAFIAgpAhg3AwggBSAFQRdqNgIQIAgpAgghCSAIKQIQIQogBSAILQAgOgA4IAUgCjcDKCAFIAk3AyAgBSAIKQIANwMYIAUgBUEIajYCMCAFQQhqIAEgAhCyhoCAAA0AIAVBCGpB3I7BgABBAhCyhoCAAA0AIAMgBUEYaiAEKAIMEYCAgIAAAA0AIAUoAjBBn5DBgABBAiAFKAI0KAIMEY+AgIAAACEGCyAAQQE6AAUgACAGOgAEIAVBwABqJICAgIAAIAALbAEBfyOAgICAAEEwayIDJICAgIAAIAMgATYCDCADIAA2AgggA0EkakEBNgIAIANCATcCFCADQdSOwYAANgIQIANBuIGAgAA2AiwgAyADQShqNgIgIAMgA0EIajYCKCADQRBqIAIQlIaAgAAACxQAIAEgACgCACAAKAIEEJGGgIAAC5MBAQF/I4CAgIAAQcAAayIFJICAgIAAIAUgATYCDCAFIAA2AgggBSADNgIUIAUgAjYCECAFQSxqQQI2AgAgBUE8akG8gYCAADYCACAFQgI3AhwgBUHgjsGAADYCGCAFQbiBgIAANgI0IAUgBUEwajYCKCAFIAVBEGo2AjggBSAFQQhqNgIwIAVBGGogBBCUhoCAAAALGAAgACgCACABIAAoAgQoAgwRgICAgAAACwcAIAAoAggLBwAgACgCDAvhAwEEfyOAgICAAEHAAGsiAiSAgICAAEEBIQMCQCABKAIYQYCPwYAAQQwgAUEcaigCACgCDBGPgICAAAANAAJAAkAgACgCCCIDDQAgACgCACIDIAAoAgQoAgwRioCAgAAAQuSuwoWXm6WIEVINASACIAM2AgwgAkG9gYCAADYCFCACIAJBDGo2AhAgASgCGCEEIAEoAhwhBUEBIQMgAkE8akEBNgIAIAJCAjcCLCACQZCPwYAANgIoIAIgAkEQajYCOCAEIAUgAkEoahCfhoCAAA0CDAELIAIgAzYCDCACQb6BgIAANgIUIAIgAkEMajYCECABKAIYIQQgASgCHCEFQQEhAyACQTxqQQE2AgAgAkICNwIsIAJBkI/BgAA2AiggAiACQRBqNgI4IAQgBSACQShqEJ+GgIAADQELIAAoAgwhAyACQRBqQRRqQYSAgIAANgIAIAJBEGpBDGpBhICAgAA2AgAgAiADQQxqNgIgIAIgA0EIajYCGCACQbiBgIAANgIUIAIgAzYCECABKAIYIQMgASgCHCEBIAJBKGpBFGpBAzYCACACQgM3AiwgAkGkj8GAADYCKCACIAJBEGo2AjggAyABIAJBKGoQn4aAgAAhAwsgAkHAAGokgICAgAAgAwsZACABIAAoAgAiACgCACAAKAIEEJGGgIAAC3kBA38jgICAgABBIGsiAiSAgICAACABQRxqKAIAIQMgASgCGCEEIAJBCGpBEGogACgCACIBQRBqKQIANwMAIAJBCGpBCGogAUEIaikCADcDACACIAEpAgA3AwggBCADIAJBCGoQn4aAgAAhASACQSBqJICAgIAAIAELBAAgAAvOBAEHfyOAgICAAEEwayIDJICAgIAAAkACQCACDQBBACEEDAELIANBKGohBQJAAkACQAJAA0ACQCAAKAIILQAARQ0AIAAoAgBBmJDBgABBBCAAKAIEKAIMEY+AgIAAAA0FCyADQQo2AiggA0KKgICAEDcDICADIAI2AhwgA0EANgIYIAMgAjYCFCADIAE2AhAgA0EIakEKIAEgAhCzhoCAAAJAAkACQAJAIAMoAghBAUcNACADKAIMIQQDQCADIAQgAygCGGpBAWoiBDYCGAJAAkAgBCADKAIkIgZPDQAgAygCFCEHDAELIAMoAhQiByAESQ0AIAZBBU8NByADKAIQIAQgBmsiCGoiCSAFRg0EIAkgBSAGEPSGgIAARQ0ECyADKAIcIgkgBEkNAiAHIAlJDQIgAyAGIANBEGpqQRdqLQAAIAMoAhAgBGogCSAEaxCzhoCAACADKAIEIQQgAygCAEEBRg0ACwsgAyADKAIcNgIYCyAAKAIIQQA6AAAgAiEEDAELIAAoAghBAToAACAIQQFqIQQLIAAoAgQhCSAAKAIAIQYCQCAERSACIARGciIHDQAgAiAETQ0DIAEgBGosAABBv39MDQMLIAYgASAEIAkoAgwRj4CAgAAADQQCQCAHDQAgAiAETQ0EIAEgBGosAABBv39MDQQLIAEgBGohASACIARrIgINAAtBACEEDAQLIAZBBBCPhoCAAAALIAEgAkEAIAQQkoaAgAAACyABIAIgBCACEJKGgIAAAAtBASEECyADQTBqJICAgIAAIAQL9wIBBn9BACEEAkACQCACQQNxIgVFDQBBBCAFayIFRQ0AIAMgBSAFIANLGyEEQQAhBSABQf8BcSEGA0AgBCAFRg0BIAIgBWohByAFQQFqIQUgBy0AACIHIAZHDQALQQEhAyAHIAFB/wFxRkEBakEBcSAFakF/aiEFDAELIAFB/wFxIQYCQAJAIANBCEkNACAEIANBeGoiCEsNACAGQYGChAhsIQUCQANAIAIgBGoiB0EEaigCACAFcyIJQX9zIAlB//37d2pxIAcoAgAgBXMiB0F/cyAHQf/9+3dqcXJBgIGChHhxDQEgBEEIaiIEIAhNDQALCyAEIANLDQELIAIgBGohCSADIARrIQJBACEDQQAhBQJAA0AgAiAFRg0BIAkgBWohByAFQQFqIQUgBy0AACIHIAZHDQALQQEhAyAHIAFB/wFxRkEBakEBcSAFakF/aiEFCyAFIARqIQUMAQsgBCADEJCGgIAAAAsgACAFNgIEIAAgAzYCAAuLAQEDfyAALQAEIQECQCAALQAFRQ0AIAFB/wFxIQJBASEBAkAgAg0AIAAoAgAiAUEcaigCACgCDCECIAEoAhghAwJAIAEtAABBBHENACADQaeQwYAAQQIgAhGPgICAAAAhAQwBCyADQaaQwYAAQQEgAhGPgICAAAAhAQsgACABOgAECyABQf8BcUEARwv4AgIEfwJ+I4CAgIAAQcAAayIDJICAgIAAQQEhBAJAIAAtAAgNACAAKAIEIQUCQCAAKAIAIgYtAABBBHENAEEBIQQgBigCGEGhkMGAAEGrkMGAACAFG0ECQQEgBRsgBkEcaigCACgCDBGPgICAAAANASABIAAoAgAgAigCDBGAgICAAAAhBAwBCwJAIAUNAEEBIQQgBigCGEGpkMGAAEECIAZBHGooAgAoAgwRj4CAgAAADQEgACgCACEGC0EBIQQgA0EBOgAXIANBNGpBgJDBgAA2AgAgAyAGKQIYNwMIIAMgA0EXajYCECAGKQIIIQcgBikCECEIIAMgBi0AIDoAOCADIAg3AyggAyAHNwMgIAMgBikCADcDGCADIANBCGo2AjAgASADQRhqIAIoAgwRgICAgAAADQAgAygCMEGfkMGAAEECIAMoAjQoAgwRj4CAgAAAIQQLIAAgBDoACCAAIAAoAgRBAWo2AgQgA0HAAGokgICAgAAgAAunAQEDfyAALQAIIQECQCAAKAIEIgJFDQAgAUH/AXEhA0EBIQECQCADDQACQCACQQFHDQAgAC0ACUUNACAAKAIAIgMtAABBBHENAEEBIQEgAygCGEGskMGAAEEBIANBHGooAgAoAgwRj4CAgAAADQELIAAoAgAiASgCGEGtkMGAAEEBIAFBHGooAgAoAgwRj4CAgAAAIQELIAAgAToACAsgAUH/AXFBAEcL9gICA38CfiOAgICAAEHAAGsiAySAgICAAEEBIQQCQCAALQAEDQAgAC0ABSEEAkAgACgCACIFLQAAQQRxDQACQCAEQf8BcUUNAEEBIQQgBSgCGEGhkMGAAEECIAVBHGooAgAoAgwRj4CAgAAADQIgACgCACEFCyABIAUgAigCDBGAgICAAAAhBAwBCwJAIARB/wFxDQBBASEEIAUoAhhBrpDBgABBASAFQRxqKAIAKAIMEY+AgIAAAA0BIAAoAgAhBQtBASEEIANBAToAFyADQTRqQYCQwYAANgIAIAMgBSkCGDcDCCADIANBF2o2AhAgBSkCCCEGIAUpAhAhByADIAUtACA6ADggAyAHNwMoIAMgBjcDICADIAUpAgA3AxggAyADQQhqNgIwIAEgA0EYaiACKAIMEYCAgIAAAA0AIAMoAjBBn5DBgABBAiADKAI0KAIMEY+AgIAAACEECyAAQQE6AAUgACAEOgAEIANBwABqJICAgIAACxAAIAAgASACELeGgIAAIAALOgEBf0EBIQECQCAALQAEDQAgACgCACIAKAIYQcCQwYAAQQEgAEEcaigCACgCDBGPgICAAAAhAQsgAQuaDAUBfwJ+AX8CfgR/I4CAgIAAQfAIayIEJICAgIAAIAG9IgVC/////////weDIgZCgICAgICAgAiEIAZCAYYgBUI0iKdB/w9xIgcbIghCAYMhCSAGUCEKQQIhCwJAAkACQAJAAkAgBUKAgICAgICA+P8AgyIGUEUNAEECQQMgChsOBAQBAgMECwJAIAZCgICAgICAgPj/AFINACAKDgQEAQIDBAtCgICAgICAgCAgCEIBhiAIQoCAgICAgIAIUSIMGyEIQgJCASAMGyEGIAmnQQFzIQtBy3dBzHcgDBsgB2ohDAwDC0EDIQsMAgtBBCELDAELIAdBzXdqIQwgCadBAXMhC0IBIQYLIARBkAhqQQRqIARBEGpBBGotAAAiCjoAACAEIAQoABAiBzYCkAggBEHvCGogCjoAACAEIAw7AegIIAQgBjcD4AggBEIBNwPYCCAEIAg3A9AIIAQgBzYA6wggBCALOgDqCAJAAkACQAJAAkAgC0F+aiILQQMgC0H/AXEiDUEDSRtB/wFxIgtBAksNAAJAAkAgCw4DAQIAAQtBrPTAgAAhCkEAIQcCQCACQf8BcQ4EBQAEAwULQaeLwYAAQaz0wIAAIAVCAFMbIQogBUI/iKchBwwECyAEQQM2ApgIIARBrIvBgAA2ApQIIARBAjsBkAhBASELQQAhB0Gs9MCAACEKDAQLQaeLwYAAQaz0wIAAIAVCAFMiBxtBp4vBgABBqIvBgAAgBxsgAkH/AXEiAkECSRshCkEBIQsgByACQQFLciEHAkAgDUECTQ0AAkACQAJAAkBBdEEFIAxBEHRBEHUiC0EASBsgC2xBBHZBFWoiC0GACEsNACAEQZAIaiAEQdAIaiAEQRBqIAtBACADa0GAgH4gA0GAgAJJGyIMEJuGgIAAIAxBEHRBEHUhAgJAAkAgBCgCkAhBAUYNACAEQQhqIARB0AhqIARBEGogCyACEJmGgIAAIAQvAQwhCyAEKAIIIQwMAQsgBEGYCGovAQAhCyAEKAKUCCEMCwJAIAtBEHRBEHUgAkwNACAMQYEITw0CIAxFDQMgBC0AEEExSQ0EAkACQCALQRB0QRB1IgJBAUgNAEECIQsgBEECOwGQCCAEIARBEGo2ApQIIAwgAk0NASAEQQI7AagIIARBATYCpAggBEGmi8GAADYCoAggBEECOwGcCCAEIAI2ApgIIAQgBEEQaiACajYCrAggBCAMIAJrIg02ArAIQQMhCyANIANPDQsgBEEAOwG0CCAEIAMgDGsgAmo2ArgIQQQhCwwLCyAEQQI7AagIIARBAjYCmAggBEGki8GAADYClAggBEECOwGQCCAEQQA7AZwIIARBACACayINNgKgCCAEIAw2ArAIIAQgBEEQajYCrAhBAyELIAwgA08NCiADIAxrIgwgDU0NCiAEQQA7AbQIIAQgDCACajYCuAhBBCELDAoLIARBADsBnAggBCAMNgKYCCAEIAIgDGs2AqAIIANFDQkgBCADNgK4CCAEQQA7AbQIIARBATYCsAggBEGmi8GAADYCrAggBEECOwGoCEEEIQsMCQtBAiELIARBAjsBkAgCQCADRQ0AIAQgAzYCoAggBEEAOwGcCCAEQQI2ApgIIARBpIvBgAA2ApQIDAkLQQEhCyAEQQE2ApgIIARBrPTAgAA2ApQIDAgLQa+LwYAAQSVB1IvBgAAQjYaAgAAACyAMQYAIEI+GgIAAAAtB3IjBgABBIUHkisGAABCNhoCAAAALQfSKwYAAQR9BlIvBgAAQjYaAgAAACyAEQQM2ApgIIARBqYvBgAA2ApQIIARBAjsBkAgMAwtBp4vBgABBqIvBgAAgBUIAUxshCkEBIQcMAQtBqIvBgAAhCkEBIQcLQQIhCyAEQQI7AZAIAkAgA0UNACAEIAM2AqAIIARBADsBnAggBEECNgKYCCAEQaSLwYAANgKUCAwBC0EBIQsgBEEBNgKYCCAEQaz0wIAANgKUCAsgBEHMCGogCzYCACAEIAc2AsQIIAQgCjYCwAggBCAEQZAIajYCyAggACAEQcAIahC7hoCAACELIARB8AhqJICAgIAAIAsLkQUBCX8jgICAgABBEGsiAiSAgICAAAJAAkAgACgCCEEBRg0AIAAgARDHhoCAACEDDAELIABBDGooAgAhBCACQQxqIAFBDGooAgAiBTYCACACIAFBCGooAgAiAzYCCCACIAFBBGooAgAiBjYCBCACIAEoAgAiATYCACAALQAgIQcgACgCBCEIAkACQAJAIAAtAABBCHENACAGIQkgByEKDAELIAAoAhggASAGIABBHGooAgAoAgwRj4CAgAAADQFBASEKIABBAToAICAAQTA2AgRBACEJIAJBADYCBCACQaz0wIAANgIAQQAgBCAGayIBIAEgBEsbIQQLAkAgBUUNACADIAVBDGxqIQYDQCADIgFBDGohAwJAAkACQAJAIAEvAQAOAwABAgALIAFBBGooAgAhAQwCCwJAIAFBAmovAQAiBUHoB0kNAEEEQQUgBUGQzgBJGyEBDAILQQEhASAFQQpJDQFBAkEDIAVB5ABJGyEBDAELIAFBCGooAgAhAQsgASAJaiEJIAYgA0cNAAsLAkACQAJAIAQgCU0NAEEAIQMgBCAJayIBIQkCQAJAAkAgCkEDcQ4EAgEAAQILIAFBAXYhAyABQQFqQQF2IQkMAQtBACEJIAEhAwsgA0EBaiEDA0AgA0F/aiIDRQ0CIAAoAhggACgCBCAAKAIcKAIQEYCAgIAAAA0EDAALCyAAIAIQx4aAgAAhAwwBCyAAKAIEIQEgACACEMeGgIAADQEgCUEBaiEDIAAoAhwhCSAAKAIYIQYDQAJAIANBf2oiAw0AQQAhAwwCCyAGIAEgCSgCEBGAgICAAABFDQALQQEhAwsgACAHOgAgIAAgCDYCBAwBC0EBIQMLIAJBEGokgICAgAAgAwuYCgUBfwJ+AX8CfgR/I4CAgIAAQYABayIEJICAgIAAIAG9IgVC/////////weDIgZCgICAgICAgAiEIAZCAYYgBUI0iKdB/w9xIgcbIghCAYMhCSAGUCEKQQIhCwJAAkACQAJAAkAgBUKAgICAgICA+P8AgyIGUEUNAEECQQMgChsOBAQBAgMECwJAIAZCgICAgICAgPj/AFINACAKDgQEAQIDBAtCgICAgICAgCAgCEIBhiAIQoCAgICAgIAIUSIMGyEIQgJCASAMGyEGIAmnQQFzIQtBy3dBzHcgDBsgB2ohDAwDC0EDIQsMAgtBBCELDAELIAdBzXdqIQwgCadBAXMhC0IBIQYLIARBCGpBBGogBEEgakEEai0AACIKOgAAIAQgBCgAICIHNgIIIARB/wBqIAo6AAAgBCAMOwF4IAQgBjcDcCAEQgE3A2ggBCAINwNgIAQgBzYAeyAEIAs6AHoCQAJAAkACQAJAIAtBfmoiC0EDIAtB/wFxIgdBA0kbQf8BcSILQQJLDQACQAJAIAsOAwECAAELQaz0wIAAIQxBACEKAkAgAkH/AXEOBAUABAMFC0Gni8GAAEGs9MCAACAFQgBTGyEMIAVCP4inIQoMBAsgBEEDNgIoIARBrIvBgAA2AiQgBEECOwEgQQEhC0EAIQpBrPTAgAAhDAwEC0Gni8GAAEGs9MCAACAFQgBTIgobQaeLwYAAQaiLwYAAIAobIAJB/wFxIgJBAkkbIQxBASELIAogAkEBS3IhCgJAIAdBAk0NACAEQSBqIARB4ABqIARBCGpBERCahoCAAAJAAkAgBCgCIEEBRg0AIAQgBEHgAGogBEEIakEREJeGgIAAIAQvAQQhCyAEKAIAIQcMAQsgBEEoai8BACELIAQoAiQhBwsCQAJAAkAgB0ESTw0AIAdFDQEgBC0ACEExSQ0CAkACQAJAIAtBEHRBEHUiAkEBSA0AQQIhCyAEQQI7ASAgBCAEQQhqNgIkIAcgAk0NASAEQQI7ATggBEEBNgI0IARBpovBgAA2AjAgBEECOwEsIAQgAjYCKCAEIARBCGogAmo2AjwgBCAHIAJrIg02AkBBAyELIA0gA08NCiAEQQA7AUQgBCADIAdrIAJqNgJIDAILIARBAjsBOCAEQQI2AiggBEGki8GAADYCJCAEQQI7ASAgBEEAOwEsIARBACACayINNgIwIAQgBzYCQCAEIARBCGo2AjxBAyELIAcgA08NCSADIAdrIgcgDU0NCSAEQQA7AUQgBCAHIAJqNgJIDAELIARBADsBLCAEIAc2AiggBCACIAdrNgIwIANFDQggBCADNgJIIARBADsBRCAEQQE2AkAgBEGmi8GAADYCPCAEQQI7ATgLQQQhCwwHCyAHQREQj4aAgAAAC0HciMGAAEEhQeSKwYAAEI2GgIAAAAtB9IrBgABBH0GUi8GAABCNhoCAAAALIARBAzYCKCAEQamLwYAANgIkIARBAjsBIAwDC0Gni8GAAEGoi8GAACAFQgBTGyEMQQEhCgwBC0Goi8GAACEMQQEhCgtBAiELIARBAjsBIAJAIANFDQAgBCADNgIwIARBADsBLCAEQQI2AiggBEGki8GAADYCJAwBC0EBIQsgBEEBNgIoIARBrPTAgAA2AiQLIARB3ABqIAs2AgAgBCAKNgJUIAQgDDYCUCAEIARBIGo2AlggACAEQdAAahC7hoCAACELIARBgAFqJICAgIAAIAsLmwIBAn8jgICAgABBEGsiAiSAgICAACACQQA2AgwCQAJAAkACQCABQYABSQ0AIAFBgBBJDQEgAkEMaiEDIAFBgIAETw0CIAIgAUE/cUGAAXI6AA4gAiABQQZ2QT9xQYABcjoADSACIAFBDHZBD3FB4AFyOgAMQQMhAQwDCyACIAE6AAwgAkEMaiEDQQEhAQwCCyACIAFBP3FBgAFyOgANIAIgAUEGdkEfcUHAAXI6AAwgAkEMaiEDQQIhAQwBCyACIAFBP3FBgAFyOgAPIAIgAUESdkHwAXI6AAwgAiABQQZ2QT9xQYABcjoADiACIAFBDHZBP3FBgAFyOgANQQQhAQsgACADIAEQsoaAgAAhASACQRBqJICAgIAAIAELcQEBfyOAgICAAEEgayICJICAgIAAIAIgADYCBCACQQhqQRBqIAFBEGopAgA3AwAgAkEIakEIaiABQQhqKQIANwMAIAIgASkCADcDCCACQQRqQYySwYAAIAJBCGoQn4aAgAAhASACQSBqJICAgIAAIAELEQAgACgCACABIAIQsoaAgAALDwAgACgCACABEL2GgIAAC3QBAX8jgICAgABBIGsiAiSAgICAACACIAAoAgA2AgQgAkEIakEQaiABQRBqKQIANwMAIAJBCGpBCGogAUEIaikCADcDACACIAEpAgA3AwggAkEEakGMksGAACACQQhqEJ+GgIAAIQEgAkEgaiSAgICAACABCxEAIAA1AgBBASABEMOGgIAAC+kCAwJ/AX4DfyOAgICAAEEwayIDJICAgIAAQSchBAJAAkAgAEKQzgBaDQAgACEFDAELQSchBANAIANBCWogBGoiBkF8aiAAIABCkM4AgCIFQpDOAH59pyIHQf//A3FB5ABuIghBAXRBw5DBgABqLwAAOwAAIAZBfmogByAIQeQAbGtB//8DcUEBdEHDkMGAAGovAAA7AAAgBEF8aiEEIABC/8HXL1YhBiAFIQAgBg0ACwsCQCAFpyIGQeMATA0AIANBCWogBEF+aiIEaiAFpyIGIAZB//8DcUHkAG4iBkHkAGxrQf//A3FBAXRBw5DBgABqLwAAOwAACwJAAkAgBkEKSA0AIANBCWogBEF+aiIEaiAGQQF0QcOQwYAAai8AADsAAAwBCyADQQlqIARBf2oiBGogBkEwajoAAAsgAiABQaz0wIAAQQAgA0EJaiAEakEnIARrEMWGgIAAIQQgA0EwaiSAgICAACAEC3QBAn8jgICAgABBIGsiAiSAgICAACABQRxqKAIAIQMgASgCGCEBIAJBCGpBEGogAEEQaikCADcDACACQQhqQQhqIABBCGopAgA3AwAgAiAAKQIANwMIIAEgAyACQQhqEJ+GgIAAIQAgAkEgaiSAgICAACAAC7EGAQZ/AkACQCABRQ0AQStBgIDEACAAKAIAIgZBAXEiARshByABIAVqIQgMAQsgBUEBaiEIIAAoAgAhBkEtIQcLAkACQCAGQQRxDQBBACECDAELQQAhCQJAIANFDQAgAyEKIAIhAQNAIAkgAS0AAEHAAXFBgAFGaiEJIAFBAWohASAKQX9qIgoNAAsLIAggA2ogCWshCAtBASEBAkACQCAAKAIIQQFGDQAgACAHIAIgAxDGhoCAAA0BIAAoAhggBCAFIABBHGooAgAoAgwRj4CAgAAADwsCQCAAQQxqKAIAIgkgCEsNACAAIAcgAiADEMaGgIAADQEgACgCGCAEIAUgAEEcaigCACgCDBGPgICAAAAPCwJAAkAgBkEIcQ0AQQAhASAJIAhrIgkhCAJAAkACQEEBIAAtACAiCiAKQQNGGw4EAgEAAQILIAlBAXYhASAJQQFqQQF2IQgMAQtBACEIIAkhAQsgAUEBaiEBA0AgAUF/aiIBRQ0CIAAoAhggACgCBCAAKAIcKAIQEYCAgIAAAEUNAAtBAQ8LIAAoAgQhBiAAQTA2AgQgAC0AICELQQEhASAAQQE6ACAgACAHIAIgAxDGhoCAAA0BQQAhASAJIAhrIgohAwJAAkACQEEBIAAtACAiCSAJQQNGGw4EAgEAAQILIApBAXYhASAKQQFqQQF2IQMMAQtBACEDIAohAQsgAUEBaiEBAkADQCABQX9qIgFFDQEgACgCGCAAKAIEIAAoAhwoAhARgICAgAAARQ0AC0EBDwsgACgCBCEKQQEhASAAKAIYIAQgBSAAKAIcKAIMEY+AgIAAAA0BIANBAWohCSAAKAIcIQMgACgCGCECAkADQCAJQX9qIglFDQFBASEBIAIgCiADKAIQEYCAgIAAAEUNAAwDCwsgACALOgAgIAAgBjYCBEEADwsgACgCBCEKQQEhASAAIAcgAiADEMaGgIAADQAgACgCGCAEIAUgACgCHCgCDBGPgICAAAANACAIQQFqIQkgACgCHCEDIAAoAhghAANAAkAgCUF/aiIJDQBBAA8LQQEhASAAIAogAygCEBGAgICAAABFDQALCyABC1wBAX8CQAJAIAFBgIDEAEYNAEEBIQQgACgCGCABIABBHGooAgAoAhARgICAgAAADQELAkAgAg0AQQAPCyAAKAIYIAIgAyAAQRxqKAIAKAIMEY+AgIAAACEECyAEC/wEAQh/I4CAgIAAQRBrIgIkgICAgAACQAJAIAEoAgQiA0UNAEEBIQQgACgCGCABKAIAIAMgAEEcaigCACgCDBGPgICAAAANAQsCQCABQQxqKAIAIgQNAEEAIQQMAQsgASgCCCIFIARBDGxqIQYgAkEIakF/aiEHIAJBCGpBBGohCANAAkACQAJAAkACQAJAAkACQAJAIAUvAQAOAwABAgALAkACQCAFKAIEIgFBwQBPDQAgAQ0BDAkLA0AgACgCGEHcksGAAEHAACAAKAIcKAIMEY+AgIAAAA0IIAFBQGoiAUHAAEsNAAsLQcAAIQQgACgCHCEDIAAoAhghCQJAIAFBwABGDQAgAUHcksGAAGosAABBv39MDQMgASEECyAJQdySwYAAIAQgAygCDBGPgICAAABFDQcMBgsgBS8BAiEBIAhBADoAACACQQA2AghBASEEAkACQAJAIAUvAQAOAwIAAQILAkAgBS8BAiIEQegHSQ0AQQRBBSAEQZDOAEkbIQkMBgtBASEJIARBCkkNBUECQQMgBEHkAEkbIQkMBQtBAiEECyAFIARBAnRqKAIAIglBBk8NAiAJDQNBACEJDAQLIAAoAhggBSgCBCAFKAIIIAAoAhwoAgwRj4CAgAAARQ0FDAQLQdySwYAAQcAAQQAgARCShoCAAAALIAlBBRCPhoCAAAALIAkhBANAIAcgBGogASABQf//A3FBCm4iA0EKbGtBMHI6AAAgAyEBIARBf2oiBA0ACwsgACgCGCACQQhqIAkgACgCHCgCDBGPgICAAABFDQELQQEhBAwCCyAGIAVBDGoiBUcNAAtBACEECyACQRBqJICAgIAAIAQLHQAgACgCGCABIAIgAEEcaigCACgCDBGPgICAAAALdAECfyOAgICAAEEgayICJICAgIAAIABBHGooAgAhAyAAKAIYIQAgAkEIakEQaiABQRBqKQIANwMAIAJBCGpBCGogAUEIaikCADcDACACIAEpAgA3AwggACADIAJBCGoQn4aAgAAhASACQSBqJICAgIAAIAELDQAgAC0AAEEQcUEEdgsNACAALQAAQSBxQQV2CzQAIAEoAhggAiADIAFBHGooAgAoAgwRj4CAgAAAIQIgAEEAOgAFIAAgAjoABCAAIAE2AgALOAAgACABKAIYIAIgAyABQRxqKAIAKAIMEY+AgIAAADoACCAAIAE2AgAgACADRToACSAAQQA2AgQLOgEBfyABKAIYQa+QwYAAQQEgAUEcaigCACgCDBGPgICAAAAhAiAAQQA6AAUgACACOgAEIAAgATYCAAstAAJAIAAtAAANACABQaCTwYAAQQUQkYaAgAAPCyABQZyTwYAAQQQQkYaAgAALvgkBDX8jgICAgABBMGsiAySAgICAAEEBIQQCQAJAIAIoAhhBIiACQRxqKAIAKAIQEYCAgIAAAA0AAkACQCABDQBBACEFDAELIAAgAWohBiAAIQdBACEFQQAhCAJAA0AgByEJIAdBAWohCgJAAkACQCAHLAAAIgtBf0oNAAJAAkAgCiAGRw0AQQAhDCAGIQcMAQsgBy0AAUE/cSEMIAdBAmoiCiEHCyALQR9xIQ0CQCALQf8BcSILQd8BSw0AIAwgDUEGdHIhCwwCCwJAAkAgByAGRw0AQQAhDiAGIQ8MAQsgBy0AAEE/cSEOIAdBAWoiCiEPCyAOIAxBBnRyIQwCQCALQfABTw0AIAwgDUEMdHIhCwwCCwJAAkAgDyAGRw0AQQAhCyAKIQcMAQsgD0EBaiEHIA8tAABBP3EhCwsgDEEGdCANQRJ0QYCA8ABxciALciILQYCAxABHDQIMBAsgC0H/AXEhCwsgCiEHCyADIAtBARClhoCAAAJAAkACQAJAIAMoAgAiCg4EAQIBAAELIAMoAgggAy0ADGpBAUYNAQsgAyABNgIUIAMgADYCECADIAU2AhggAyAINgIcAkACQCAIIAVJDQACQCAFRQ0AIAUgAUYNACAFIAFPDQEgACAFaiwAAEG/f0wNAQsCQCAIRQ0AIAggAUYNACAIIAFPDQEgACAIaiwAAEG/f0wNAQsgAigCGCAAIAVqIAggBWsgAigCHCgCDBGPgICAAABFDQEMAwsgAyADQRxqNgIoIAMgA0EYajYCJCADIANBEGo2AiAgA0EgahDRhoCAAAALIAMtAAwhDSADKAIIIQ8CQAJAIAMoAgQiDkGAgMQARw0AA0AgCiEFQQEhCkHcACEMAkACQCAFDgQEBAEABAsgDUH/AXEhBUEDIQpBBCENAkACQAJAAkAgBQ4GBwMCAQAEBwtBAyENQfUAIQxBAyEKDAMLQQIhDUH7ACEMDAILQQJBASAPGyENQYCAxAAgD0ECdEEccXZBD3FBMHIhDCAPQX9qQQAgDxshDwwBC0EAIQ1B/QAhDAsgAigCGCAMIAIoAhwoAhARgICAgAAADQQMAAsLA0AgCiEMQQEhCkHcACEFAkACQAJAAkAgDA4EBQEDAAULIA1B/wFxIQxBAyEKQQQhDQJAAkACQCAMDgYHAgEABAUHC0ECIQ1B+wAhBQwECyAOIA9BAnRBHHF2QQ9xIgVBMHIgBUHXAGogBUEKSRshBUECQQEgDxshDSAPQX9qQQAgDxshDwwDC0EAIQ1B/QAhBQwCC0EAIQogDiEFDAELQQMhDUH1ACEFQQMhCgsgAigCGCAFIAIoAhwoAhARgICAgAAADQMMAAsLQQEhBQJAIAtBgAFJDQBBAiEFIAtBgBBJDQBBA0EEIAtBgIAESRshBQsgBSAIaiEFCyAIIAlrIAdqIQggBiAHRw0BDAILC0EBIQQMAgsgBUUNACAFIAFGDQAgBSABTw0CIAAgBWosAABBv39MDQILIAIoAhggACAFaiABIAVrIAIoAhwoAgwRj4CAgAAADQAgAigCGEEiIAIoAhwoAhARgICAgAAAIQQLIANBMGokgICAgAAgBA8LIAAgASAFIAEQkoaAgAAACyoBAX8gACgCACIBKAIAIAEoAgQgACgCBCgCACAAKAIIKAIAEJKGgIAAAAsOACACIAAgARCRhoCAAAudBAEHfyOAgICAAEEQayICJICAgIAAQQEhAwJAIAEoAhhBJyABQRxqKAIAKAIQEYCAgIAAAA0AIAIgACgCAEEBEKWGgIAAIAJBDGotAAAhBCACQQhqKAIAIQUgAigCACEAAkACQCACKAIEIgZBgIDEAEYNAANAIAAhB0EBIQNB3AAhCEEBIQACQAJAAkACQCAHDgQGAQMABgsgBEH/AXEhB0EEIQRBAyEAAkACQAJAIAcOBggCAQAEBQgLQQIhBEH7ACEIDAQLIAYgBUECdEEccXZBD3EiCEEwciAIQdcAaiAIQQpJGyEIQQJBASAFGyEEIAVBf2pBACAFGyEFDAMLQQAhBEH9ACEIDAILQQAhACAGIQgMAQtBAyEAQfUAIQhBAyEECyABKAIYIAggASgCHCgCEBGAgICAAABFDQAMAwsLA0AgACEIQQEhA0HcACEHQQEhAAJAAkAgCA4EAwMBAAMLIARB/wFxIQhBBCEEQQMhAAJAAkACQAJAIAgOBgYDAgEABAYLQQMhAEH1ACEHQQMhBAwDC0ECIQRB+wAhBwwCC0ECQQEgBRshBEGAgMQAIAVBAnRBHHF2QQ9xQTByIQcgBUF/akEAIAUbIQUMAQtBACEEQf0AIQcLIAEoAhggByABKAIcKAIQEYCAgIAAAEUNAAwCCwsgASgCGEEnIAEoAhwoAhARgICAgAAAIQMLIAJBEGokgICAgAAgAwv0AgECfyOAgICAAEEQayICJICAgIAAAkACQAJAIAFBCGooAgBBAUYNACABQRBqKAIAQQFHDQELIAAoAgAhACACQQA2AgwCQAJAAkAgAEGAAUkNACAAQYAQSQ0BIAJBDGohAyAAQYCABE8NAiACIABBP3FBgAFyOgAOIAIgAEEGdkE/cUGAAXI6AA0gAiAAQQx2QQ9xQeABcjoADCABIANBAxCRhoCAACEBDAQLIAIgADoADCABIAJBDGpBARCRhoCAACEBDAMLIAIgAEE/cUGAAXI6AA0gAiAAQQZ2QR9xQcABcjoADCABIAJBDGpBAhCRhoCAACEBDAILIAIgAEE/cUGAAXI6AA8gAiAAQRJ2QfABcjoADCACIABBBnZBP3FBgAFyOgAOIAIgAEEMdkE/cUGAAXI6AA0gASADQQQQkYaAgAAhAQwBCyABKAIYIAAoAgAgAUEcaigCACgCEBGAgICAAAAhAQsgAkEQaiSAgICAACABC+UKAwl/AX4BfwJAAkACQAJAAkACQAJAAkAgBEEBSw0AQQAhBSAEIQYgBCEHQQAhCCAEDgICAQILQQEhCUEAIQVBASEKQQAhC0EBIQYDQCAKIQwCQAJAIAsgBWoiCiAETw0AAkAgAyAJai0AAEH/AXEiCSADIApqLQAAIgpJDQACQCAJIApGDQBBASEGIAxBAWohCkEAIQsgDCEFDAMLQQAgC0EBaiIKIAogBkYiCRshCyAKQQAgCRsgDGohCgwCCyAMIAtqQQFqIgogBWshBkEAIQsMAQtB6JTBgAAgCiAEEIyGgIAAAAsgCiALaiIJIARJDQALQQEhCUEAIQhBASEKQQAhC0EBIQcDQCAKIQwCQAJAIAsgCGoiCiAETw0AAkAgAyAJai0AAEH/AXEiCSADIApqLQAAIgpLDQACQCAJIApGDQBBASEHIAxBAWohCkEAIQsgDCEIDAMLQQAgC0EBaiIKIAogB0YiCRshCyAKQQAgCRsgDGohCgwCCyAMIAtqQQFqIgogCGshB0EAIQsMAQtB6JTBgAAgCiAEEIyGgIAAAAsgCiALaiIJIARJDQALCwJAAkACQCAFIAggBSAISyILGyINIARLDQAgBiAHIAsbIgogDWoiCyAKSQ0BIAsgBEsNAiAKRQ0EIAMgAyAKaiANEPSGgIAARQ0EIA0gBCANayILIA0gC0sbIQxCACEOIAQhCiADIQsDQEIBIAsxAABCP4OGIA6EIQ4gC0EBaiELIApBf2oiCg0ACyAMQQFqIQpBfyEMIA0hCUF/IQsMBQsgDSAEEI+GgIAAAAsgCiALEJCGgIAAAAsgCyAEEI+GgIAAAAsgACADNgI4IAAgATYCMCAAQgA3AwAgAEE8akEANgIAIABBNGogAjYCACAAQQxqQYECOwEAIABBCGogAjYCAA8LQQEhBUEAIQtBASEJQQAhBgJAA0AgCSIMIAtqIgcgBE8NASAEIAtrIAxBf3NqIgkgBE8NBSALQX9zIARqIAZrIgggBE8NBAJAAkACQCADIAlqLQAAQf8BcSIJIAMgCGotAAAiCEkNACAJIAhGDQEgDEEBaiEJQQAhC0EBIQUgDCEGDAILIAdBAWoiCSAGayEFQQAhCwwBC0EAIAtBAWoiCSAJIAVGIggbIQsgCUEAIAgbIAxqIQkLIAUgCkcNAAsLQQEhBUEAIQtBASEJQQAhBwJAAkACQAJAAkADQCAJIgwgC2oiDyAETw0BIAQgC2sgDEF/c2oiCSAETw0CIAtBf3MgBGogB2siCCAETw0DAkACQAJAIAMgCWotAABB/wFxIgkgAyAIai0AACIISw0AIAkgCEYNASAMQQFqIQlBACELQQEhBSAMIQcMAgsgD0EBaiIJIAdrIQVBACELDAELQQAgC0EBaiIJIAkgBUYiCBshCyAJQQAgCBsgDGohCQsgBSAKRw0ACwsgCiAESw0FIAQgBiAHIAYgB0sbayEJQgAhDiAKDQJBACEKQQAhDAwDC0H4lMGAACAJIAQQjIaAgAAAC0GIlcGAACAIIAQQjIaAgAAAC0EAIQxBACELA0BCASADIAtqMQAAQj+DhiAOhCEOIAogC0EBaiILRw0ACwsgBCELCyAAIAM2AjggACABNgIwIABBATYCACAAQTxqIAQ2AgAgAEE0aiACNgIAIABBKGogCzYCACAAQSRqIAw2AgAgAEEgaiACNgIAIABBHGpBADYCACAAQRhqIAo2AgAgAEEUaiAJNgIAIABBEGogDTYCACAAQQhqIA43AgAPCyAKIAQQj4aAgAAAC0GIlcGAACAIIAQQjIaAgAAAC0H4lMGAACAJIAQQjIaAgAAAC6YBAQN/I4CAgIAAQYABayICJICAgIAAIAAtAAAhA0EAIQADQCACIABqQf8AaiADQQ9xIgRBMHIgBEHXAGogBEEKSRs6AAAgAEF/aiEAIANBBHZBD3EiAw0ACwJAIABBgAFqIgNBgQFJDQAgA0GAARCQhoCAAAALIAFBAUHBkMGAAEECIAIgAGpBgAFqQQAgAGsQxYaAgAAhACACQYABaiSAgICAACAAC3gCAX8BfiOAgICAAEEQayIDJICAgIAAIANBCGogASACENiGgIAAAkACQCADKQMIIgRCgICAgPAfg0KAgICAIFENACAAIAQ3AgRBASEBDAELIAAgATYCBCAAQQhqIAI2AgBBACEBCyAAIAE2AgAgA0EQaiSAgICAAAulBwEGfwJAAkACQCACRQ0AQQAgAWtBACABQQNxGyEDIAJBeWpBACACQQdLGyEEQQAhBQNAAkACQAJAAkACQCABIAVqLQAAIgZBGHRBGHUiB0F/Sg0AAkACQAJAAkAgBkGulcGAAGotAABBfmoiCEECSw0AIAgOAwECAwELIABBgQI7AQQgACAFNgIADwsCQCAFQQFqIgYgAkkNACAAQQA6AAQgACAFNgIADwsgASAGai0AAEHAAXFBgAFGDQMgAEGBAjsBBCAAIAU2AgAPCwJAIAVBAWoiCCACSQ0AIABBADoABCAAIAU2AgAPCyABIAhqLQAAIQgCQAJAIAZBoH5qIgZBDUsNAAJAAkAgBg4OAAICAgICAgICAgICAgEACyAIQeABcUGgAUcNDAwCCyAIQRh0QRh1QX9KDQsgCEH/AXFBoAFJDQEMCwsCQCAHQR9qQf8BcUELSw0AIAhBGHRBGHVBf0oNCyAIQf8BcUHAAU8NCwwBCyAIQf8BcUG/AUsNCiAHQf4BcUHuAUcNCiAIQRh0QRh1QX9KDQoLAkAgBUECaiIGIAJJDQAgAEEAOgAEIAAgBTYCAA8LIAEgBmotAABBwAFxQYABRg0CIABBgQQ7AQQgACAFNgIADwsCQCAFQQFqIgggAkkNACAAQQA6AAQgACAFNgIADwsgASAIai0AACEIAkACQCAGQZB+aiIGQQRLDQACQAJAIAYOBQACAgIBAAsgCEHwAGpB/wFxQTBPDQoMAgsgCEEYdEEYdUF/Sg0JIAhB/wFxQZABSQ0BDAkLIAhB/wFxQb8BSw0IIAdBD2pB/wFxQQJLDQggCEEYdEEYdUF/Sg0ICwJAIAVBAmoiBiACSQ0AIABBADoABCAAIAU2AgAPCyABIAZqLQAAQcABcUGAAUcNAgJAIAVBA2oiBiACSQ0AIABBADoABCAAIAU2AgAPCyABIAZqLQAAQcABcUGAAUYNASAAQYEGOwEEIAAgBTYCAA8LIAMgBWtBA3ENAgJAIAUgBE8NAANAIAEgBWoiBkEEaigCACAGKAIAckGAgYKEeHENASAFQQhqIgUgBEkNAAsLIAUgAk8NAwNAIAEgBWosAABBAEgNBCACIAVBAWoiBUcNAAwGCwsgBkEBaiEFDAILIABBgQQ7AQQgACAFNgIADwsgBUEBaiEFCyAFIAJJDQALCyAAQQI6AAQPCyAAQYECOwEEIAAgBTYCAA8LIABBgQI7AQQgACAFNgIACxEAIAAxAABBASABEMOGgIAACxEAIAApAwBBASABEMOGgIAAC6MBAQN/I4CAgIAAQYABayICJICAgIAAIAAoAgAhA0EAIQADQCACIABqQf8AaiADQQ9xIgRBMHIgBEHXAGogBEEKSRs6AAAgAEF/aiEAIANBBHYiAw0ACwJAIABBgAFqIgNBgQFJDQAgA0GAARCQhoCAAAALIAFBAUHBkMGAAEECIAIgAGpBgAFqQQAgAGsQxYaAgAAhACACQYABaiSAgICAACAACxAAIAAgASACIAMQnYaAgAAL2wEDAn8BfgF/AkAgAg0AIABBADoAASAAQQE6AAAPCwJAAkAgAS0AAEErRw0AIAJBf2oiAkUNASABQQFqIQELQQAhAwJAAkACQANAIAJFDQMgAS0AAEFQaiIEQQlLDQEgA61CCn4iBUIgiKcNAiABQQFqIQEgAkF/aiECIAWnIgYgBGoiAyAGTw0ACyAAQQI6AAEgAEEBOgAADwsgAEEBOgABIABBAToAAA8LIABBAjoAASAAQQE6AAAPCyAAQQRqIAM2AgAgAEEAOgAADwsgAEEAOgABIABBAToAAAubAgECfyOAgICAAEEQayICJICAgIAAIAIgASgCGEGMpsGAAEERIAFBHGooAgAoAgwRj4CAgAAAOgAIIAIgATYCACACQQA6AAkgAkEANgIEIAIgADYCDCACIAJBDGpB/KXBgAAQtYaAgAAaIAItAAghAQJAIAIoAgQiA0UNACABQf8BcSEAQQEhAQJAIAANAAJAIANBAUcNACACLQAJQf8BcUUNACACKAIAIgAtAABBBHENAEEBIQEgACgCGEGskMGAAEEBIABBHGooAgAoAgwRj4CAgAAADQELIAIoAgAiASgCGEGtkMGAAEEBIAFBHGooAgAoAgwRj4CAgAAAIQELIAIgAToACAsgAkEQaiSAgICAACABQf8BcUEARwtKAgF/AXwgAS0AAEEBdEECcSECIAArAwAhAwJAIAEoAhBBAUYNACABIAMgAkEAELyGgIAADwsgASADIAIgAUEUaigCABC6hoCAAAulAQEDfyOAgICAAEGAAWsiAiSAgICAACAALQAAIQNBACEAA0AgAiAAakH/AGogA0EPcSIEQTByIARBN2ogBEEKSRs6AAAgAEF/aiEAIANBBHZBD3EiAw0ACwJAIABBgAFqIgNBgQFJDQAgA0GAARCQhoCAAAALIAFBAUHBkMGAAEECIAIgAGpBgAFqQQAgAGsQxYaAgAAhACACQYABaiSAgICAACAAC6IBAQN/I4CAgIAAQYABayICJICAgIAAIAAoAgAhA0EAIQADQCACIABqQf8AaiADQQ9xIgRBMHIgBEE3aiAEQQpJGzoAACAAQX9qIQAgA0EEdiIDDQALAkAgAEGAAWoiA0GBAUkNACADQYABEJCGgIAAAAsgAUEBQcGQwYAAQQIgAiAAakGAAWpBACAAaxDFhoCAACEAIAJBgAFqJICAgIAAIAALqwEDAX8BfgF/I4CAgIAAQYABayICJICAgIAAIAApAwAhA0EAIQADQCACIABqQf8AaiADp0EPcSIEQTByIARB1wBqIARBCkkbOgAAIABBf2ohACADQgSIIgNCAFINAAsCQCAAQYABaiIEQYEBSQ0AIARBgAEQkIaAgAAACyABQQFBwZDBgABBAiACIABqQYABakEAIABrEMWGgIAAIQAgAkGAAWokgICAgAAgAAuqAQMBfwF+AX8jgICAgABBgAFrIgIkgICAgAAgACkDACEDQQAhAANAIAIgAGpB/wBqIAOnQQ9xIgRBMHIgBEE3aiAEQQpJGzoAACAAQX9qIQAgA0IEiCIDQgBSDQALAkAgAEGAAWoiBEGBAUkNACAEQYABEJCGgIAAAAsgAUEBQcGQwYAAQQIgAiAAakGAAWpBACAAaxDFhoCAACEAIAJBgAFqJICAgIAAIAALKQEBfiAAKAIAIgCsIgIgAkI/hyICfCAChSAAQX9zQR92IAEQw4aAgAALIwECfiAAKQMAIgIgAkI/hyIDfCADhSACQn9VIAEQw4aAgAALwgMDBH8CfgJ/I4CAgIAAQdAAayIEJICAgIAAAkACQCAAQpDOAFRBACABUBtFDQAgAKchBUEnIQYMAQtBJyEGIARBIGohBwNAIARBGGogACABQpDOAEIAEPiGgIAAIARBCGogBCkDGCIIIAcpAwAiCUKQzgBCABD3hoCAACAEQSlqIAZqIgVBfGogACAEKQMIfaciCkH//wNxQeQAbiILQQF0QcOQwYAAai8AADsAACAFQX5qIAogC0HkAGxrQf//A3FBAXRBw5DBgABqLwAAOwAAIABC/8HXL1YhBSABQgBSIQogAVAhCyAGQXxqIQYgCCEAIAkhASAFIAogCxsNAAsgCKchBQsCQAJAIAVB4wBKDQAgBSEKDAELIARBKWogBkF+aiIGaiAFIAVB//8DcUHkAG4iCkHkAGxrQf//A3FBAXRBw5DBgABqLwAAOwAACwJAAkAgCkEKSA0AIARBKWogBkF+aiIGaiAKQQF0QcOQwYAAai8AADsAAAwBCyAEQSlqIAZBf2oiBmogCkEwajoAAAsgAyACQaz0wIAAQQAgBEEpaiAGakEnIAZrEMWGgIAAIQYgBEHQAGokgICAgAAgBgsZACAAKQMAIABBCGopAwBBASABEOaGgIAACyEAIAEoAhhBpabBgABBBSABQRxqKAIAKAIMEY+AgIAAAAsPACAAKAIAIAEQoYaAgAALEgAgAUGlk8GAAEECEJGGgIAAC9oCAQJ/I4CAgIAAQRBrIgIkgICAgAACQAJAIAAoAgAiAC0AAEEBRg0AIAEoAhhBoabBgABBBCABQRxqKAIAKAIMEY+AgIAAACEBDAELIAIgASgCGEGdpsGAAEEEIAFBHGooAgAoAgwRj4CAgAAAOgAIIAIgATYCACACQQA6AAkgAkEANgIEIAIgAEEBajYCDCACIAJBDGpBsJDBgAAQtYaAgAAaIAItAAghAQJAIAIoAgQiA0UNACABQf8BcSEAQQEhAQJAIAANAAJAIANBAUcNACACLQAJQf8BcUUNACACKAIAIgAtAABBBHENAEEBIQEgACgCGEGskMGAAEEBIABBHGooAgAoAgwRj4CAgAAADQELIAIoAgAiASgCGEGtkMGAAEEBIAFBHGooAgAoAgwRj4CAgAAAIQELIAIgAToACAsgAUH/AXFBAEchAQsgAkEQaiSAgICAACABC+ICAQN/I4CAgIAAQYABayICJICAgIAAIAAoAgAhAAJAAkACQAJAAkAgASgCACIDQRBxDQAgAC0AACEEIANBIHENASAErUL/AYNBASABEMOGgIAAIQAMAgsgAC0AACEEQQAhAANAIAIgAGpB/wBqIARBD3EiA0EwciADQdcAaiADQQpJGzoAACAAQX9qIQAgBEEEdkEPcSIEDQALIABBgAFqIgRBgQFPDQIgAUEBQcGQwYAAQQIgAiAAakGAAWpBACAAaxDFhoCAACEADAELQQAhAANAIAIgAGpB/wBqIARBD3EiA0EwciADQTdqIANBCkkbOgAAIABBf2ohACAEQQR2QQ9xIgQNAAsgAEGAAWoiBEGBAU8NAiABQQFBwZDBgABBAiACIABqQYABakEAIABrEMWGgIAAIQALIAJBgAFqJICAgIAAIAAPCyAEQYABEJCGgIAAAAsgBEGAARCQhoCAAAALpgIBAn8jgICAgABBEGsiAiSAgICAACABKAIYQaqmwYAAQQkgAUEcaigCACgCDBGPgICAAAAhAyACQQA6AAUgAiADOgAEIAIgATYCACACIAA2AgwgAkGzpsGAAEELIAJBDGpB7KXBgAAQp4aAgAAaIAIgAEEEajYCDCACQb6mwYAAQQkgAkEMakHIpsGAABCnhoCAABogAi0ABCEBAkAgAi0ABUUNACABQf8BcSEAQQEhAQJAIAANACACKAIAIgFBHGooAgAoAgwhACABKAIYIQMCQCABLQAAQQRxDQAgA0GnkMGAAEECIAARj4CAgAAAIQEMAQsgA0GmkMGAAEEBIAARj4CAgAAAIQELIAIgAToABAsgAkEQaiSAgICAACABQf8BcUEARwuiAQECfyAAQQp2IQECQAJAAkACQCAAQYDoB0kNAEEhIQIgAUGAB0YNAUEADwsgAUHYpsGAAGotAAAiAkEhSw0BCyACQQR0IABBBnZBD3FyQdWnwYAAai0AACIBQbMBSw0BIAFBA3RB+KvBgABqKQMAQgEgAEE/ca2Gg0IAUg8LQcykwYAAIAJBIhCMhoCAAAALQdykwYAAIAFBtAEQjIaAgAAAC6ABAQJ/IABBCnYhAQJAAkACQAJAIABBgNgHSQ0AQQYhAiABQfwARg0BQQAPCyABQZi3wYAAai0AACICQRJLDQELIAJBBHQgAEEGdkEPcXJBk7jBgABqLQAAIgFBPksNASABQQN0Qci6wYAAaikDAEIBIABBP3GthoNCAFIPC0HMpMGAACACQRMQjIaAgAAAC0HcpMGAACABQT8QjIaAgAAAC/YCAQF/AkBBAEG3BSABQew8SRsiAiACQdsCaiICIAJBBHRBiMzBgABqKAIAIAFLGyICIAJBrgFqIgIgAkEEdEGIzMGAAGooAgAgAUsbIgIgAkHXAGoiAiACQQR0QYjMwYAAaigCACABSxsiAiACQStqIgIgAkEEdEGIzMGAAGooAgAgAUsbIgIgAkEWaiICIAJBBHRBiMzBgABqKAIAIAFLGyICIAJBC2oiAiACQQR0QYjMwYAAaigCACABSxsiAiACQQVqIgIgAkEEdEGIzMGAAGooAgAgAUsbIgIgAkEDaiICIAJBBHRBiMzBgABqKAIAIAFLGyICIAJBAWoiAiACQQR0QYjMwYAAaigCACABSxsiAiACQQFqIgIgAkEEdEGIzMGAAGooAgAgAUsbQQR0IgJBiMzBgABqKAIAIAFGDQAgAEIANwIEIAAgATYCAA8LIABBCGogAkGUzMGAAGooAgA2AgAgACACQYzMwYAAaikCADcCAAuAAgECfyOAgICAAEEQayICJICAgIAAIAEoAhhB6PnCgABBCSABQRxqKAIAKAIMEY+AgIAAACEDIAJBADoABSACIAM6AAQgAiABNgIAIAIgADYCDCACQfH5woAAQQcgAkEMakH8pcGAABCnhoCAABogAi0ABCEBAkAgAi0ABUUNACABQf8BcSEAQQEhAQJAIAANACACKAIAIgFBHGooAgAoAgwhACABKAIYIQMCQCABLQAAQQRxDQAgA0GnkMGAAEECIAARj4CAgAAAIQEMAQsgA0GmkMGAAEEBIAARj4CAgAAAIQELIAIgAToABAsgAkEQaiSAgICAACABQf8BcUEARwssAQF/AkAgAkUNACAAIQMDQCADIAE6AAAgA0EBaiEDIAJBf2oiAg0ACwsgAAs2AQF/AkAgAkUNACAAIQMDQCADIAEtAAA6AAAgA0EBaiEDIAFBAWohASACQX9qIgINAAsLIAALSgEDf0EAIQMCQCACRQ0AAkADQCAALQAAIgQgAS0AACIFRw0BIABBAWohACABQQFqIQEgAkF/aiICRQ0CDAALCyAEIAVrIQMLIAMLVwEBfgJAAkAgA0HAAHENACADRQ0BIAFBACADa0E/ca2IIAIgA0E/ca0iBIaEIQIgASAEhiEBDAELIAEgA0E/ca2GIQJCACEBCyAAIAE3AwAgACACNwMIC1cBAX4CQAJAIANBwABxDQAgA0UNASABIANBP3GtIgSIIAJBACADa0E/ca2GhCEBIAIgBIghAgwBCyACIANBP3GtiCEBQgAhAgsgACABNwMAIAAgAjcDCAt1AQJ+IAAgA0IgiCIFIAFCIIgiBn4gAyACfnwgBCABfnwgA0L/////D4MiAyABQv////8PgyIBfiIEQiCIIAMgBn58IgNCIIh8IANC/////w+DIAUgAX58IgNCIIh8NwMIIAAgA0IghiAEQv////8Pg4Q3AwALTQEBfyOAgICAAEEQayIFJICAgIAAIAUgASACIAMgBEEAEPmGgIAAIAUpAwAhASAAIAVBCGopAwA3AwggACABNwMAIAVBEGokgICAgAAL9wUCA38GfiOAgICAAEEwayIGJICAgIAAAkACQAJAAkACQAJAAkACQAJAAkAgAlANACADUA0BIARQDQIgBHmnIAJ5p2siB0E/Sw0DQf8AIAdrIQggB0EBaiEHDAgLAkAgBFANACAFDQQMBgsCQAJAIAVFDQAgA0IAUQ0GIAVCADcDCCAFIAEgA4I3AwAMAQsgA0IAUQ0FCyABIAOAIQEMBgsgBFANAwJAAkACQCABUA0AIAR7QgFRDQEgBHmnIAJ5p2siB0E+Sw0CQf8AIAdrIQggB0EBaiEHDAkLAkAgBUUNACAFQgA3AwAgBSACIASCNwMICyACIASAIQEMBwsCQCAFRQ0AIAUgATcDACAFIARCf3wgAoM3AwgLIAIgBHpCP4OIIQEMBgsgBUUNBAwCCwJAIAN7QgFRDQBBv38gA3mnIAJ5p2siB2shCCAHQcEAaiEHDAYLAkAgBUUNACAFQgA3AwggBSADQn98IAGDNwMACyADQgFRDQYgBkEgaiABIAIgA3qnEPaGgIAAIAZBKGopAwAhAiAGKQMgIQEMBgsgBUUNAgsgBSABNwMAIAUgAjcDCEIAIQEMAgsAAAtCACEBC0IAIQIMAQsgBiABIAIgCEH/AHEQ9YaAgAAgBkEQaiABIAIgB0H/AHEQ9oaAgAAgBkEIaikDACECIAZBEGpBCGopAwAhCSAGKQMAIQEgBikDECEKAkACQCAHDQBCACELQgAhDAwBC0IAIQxCACENA0AgCUIBhiAKQj+IhCILIAtCf4UgBHwgCkIBhiACQj+IhCIKQn+FIgsgA3wgC1StfEI/hyILIASDfSAKIAsgA4MiDlStfSEJIAogDn0hCkIAIAJCAYYgAUI/iISEIQIgDSABQgGGhCEBIAtCAYMiCyENIAdBf2oiBw0ACwsCQCAFRQ0AIAUgCjcDACAFIAk3AwgLIAwgAkIBhiABQj+IhIQhAiALIAFCAYaEIQELIAAgATcDACAAIAI3AwggBkEwaiSAgICAAAsLw/oCAgBBgIDAAAv4+QJhbHNlcnVldWxsaW50ZXJuYWwgZXJyb3I6IGVudGVyZWQgdW5yZWFjaGFibGUgY29kZTw6OnN0ZDo6bWFjcm9zOjpwYW5pYyBtYWNyb3M+ADIAEAAdAAAAAgAAAAQAAAAvVXNlcnMvY3lwcmVzcy8uY2FyZ28vcmVnaXN0cnkvc3JjL2dpdGh1Yi5jb20tMWVjYzYyOTlkYjllYzgyMy9zZXJkZV9qc29uLTEuMC40OC9zcmMvZGUucnMAAAAAAABhdHRlbXB0IHRvIHN1YnRyYWN0IHdpdGggb3ZlcmZsb3cAAABgABAAWgAAAPYBAAAbAAAAAAAAAAAAAAAAAAAAYXR0ZW1wdCB0byBtdWx0aXBseSB3aXRoIG92ZXJmbG93AAAAAAAAAAAAAAAAAAAAYXR0ZW1wdCB0byBhZGQgd2l0aCBvdmVyZmxvd2AAEABaAAAA9wEAAA0AAABgABAAWgAAAKMBAAAjAAAAYAAQAFoAAAAyAgAAEwAAAGAAEABaAAAAuwEAABUAAAB9InRydWVmYWxzZW51bGx7LFx0XHJcblxmXGJcXFwiOqwBEAAAAAAAYSBEaXNwbGF5IGltcGxlbWVudGF0aW9uIHJldHVybmVkIGFuIGVycm9yIHVuZXhwZWN0ZWRseS9ydXN0Yy9jZDFlZjM5MGU3MzFlZDc3YjkwYjExYjFmNzdlMmM1Y2E2NDFiMjYxL3NyYy9saWJhbGxvYy9zdHJpbmcucnMAAADrARAARgAAAHwIAAAJAAAAYWxyZWFkeSBib3Jyb3dlZC9ydXN0Yy9jZDFlZjM5MGU3MzFlZDc3YjkwYjExYjFmNzdlMmM1Y2E2NDFiMjYxL3NyYy9saWJjb3JlL2NlbGwucnMAVAIQAEMAAABuAwAACQAAAAoAAAAIAAAABAAAAAsAAAAMAAAAAAAAAAEAAAANAAAADgAAAAAAAAABAAAADwAAABAAAAAEAAAABAAAABEAAABjYWxsZWQgYFJlc3VsdDo6dW53cmFwKClgIG9uIGFuIGBFcnJgIHZhbHVlABIAAAAAAAAAAQAAABMAAABVbmV4cGVjdGVkIGxlbmd0aCBvZiBpbnB1dC9Vc2Vycy9jeXByZXNzLy5jYXJnby9yZWdpc3RyeS9zcmMvZ2l0aHViLmNvbS0xZWNjNjI5OWRiOWVjODIzL2JvcnNoLTAuNi4xL3NyYy9kZS9tb2QucnMAAD4DEABYAAAASAAAADAAAAAvcnVzdGMvY2QxZWYzOTBlNzMxZWQ3N2I5MGIxMWIxZjc3ZTJjNWNhNjQxYjI2MS9zcmMvbGliY29yZS9vcHMvYXJpdGgucnOoAxAASAAAALsCAAAzAAAAAQAAAAAAAABOb3QgYWxsIGJ5dGVzIHJlYWRtaXNzaW5nIGZpZWxkIGBgAAAaBBAADwAAACkEEAABAAAAaW52YWxpZCBsZW5ndGggLCBleHBlY3RlZCAAADwEEAAPAAAASwQQAAsAAABkdXBsaWNhdGUgZmllbGQgYAAAAGgEEAARAAAAKQQQAAEAAAAvVXNlcnMvY3lwcmVzcy8uY2FyZ28vcmVnaXN0cnkvc3JjL2dpdGh1Yi5jb20tMWVjYzYyOTlkYjllYzgyMy9zZXJkZV9qc29uLTEuMC40OC9zcmMvcmVhZC5yc4wEEABcAAAA8wEAAAkAAAAvVXNlcnMvY3lwcmVzcy8uY2FyZ28vcmVnaXN0cnkvc3JjL2dpdGh1Yi5jb20tMWVjYzYyOTlkYjllYzgyMy9uZWFyLXNkay0wLjkuMi9zcmMvY29sbGVjdGlvbnMvbWFwLnJz+AQQAGQAAACAAAAAQAAAAFRoZSBjb2xsZWN0aW9uIGlzIGFuIGluY29uc2lzdGVudCBzdGF0ZS4gRGlkIHByZXZpb3VzIHNtYXJ0IGNvbnRyYWN0IGV4ZWN1dGlvbiB0ZXJtaW5hdGUgdW5leHBlY3RlZGx5P0Nhbm5vdCBzZXJpYWxpemUga2V5IHdpdGggQm9yc2hDYW5ub3Qgc2VyaWFsaXplIHZhbHVlIHdpdGggQm9yc2hDYW5ub3QgZGVzZXJpYWxpemUgdmFsdWUgd2l0aCBCb3JzaAAAAPgEEABkAAAARwAAACoAAAD4BBAAZAAAACkAAAA3AAAA+AQQAGQAAAAtAAAAMwAAAPgEEABkAAAAMQAAADUAAABJbmRleCBvdXQgb2YgYm91bmRzL1VzZXJzL2N5cHJlc3MvLmNhcmdvL3JlZ2lzdHJ5L3NyYy9naXRodWIuY29tLTFlY2M2Mjk5ZGI5ZWM4MjMvbmVhci1zZGstMC45LjIvc3JjL2NvbGxlY3Rpb25zL3ZlY3Rvci5yc2NoZWNrZWQgYGluZGV4IDwgbGVuYCBhYm92ZSwgc28gYGxlbiA+IDBgAIsGEABnAAAATAAAACIAAACLBhAAZwAAACkAAAAxAAAAiwYQAGcAAABnAAAADQAAAIsGEABnAAAAWwAAAAkAAABTVEFURUNhbm5vdCBkZXNlcmlhbGl6ZSB0aGUgY29udHJhY3Qgc3RhdGUuL1VzZXJzL2N5cHJlc3MvLmNhcmdvL3JlZ2lzdHJ5L3NyYy9naXRodWIuY29tLTFlY2M2Mjk5ZGI5ZWM4MjMvbmVhci1zZGstMC45LjIvc3JjL2Vudmlyb25tZW50L2Vudi5ycwCHBxAAZAAAANUCAAAVAAAAQ2Fubm90IHNlcmlhbGl6ZSB0aGUgY29udHJhY3Qgc3RhdGUuhwcQAGQAAADZAgAAEAAAABQAAAAAAAAAAQAAABUAAAAWAAAAAAAAAAEAAAAXAAAAYAAQAFoAAABBBAAAEwAAAGAAEABaAAAASgQAABMAAAAYAAAAAAAAAAEAAAAZAAAAGgAAAAAAAAABAAAAGwAAABwAAAAAAAAAAQAAAB0AAAAeAAAAAAAAAAEAAAAfAAAAIAAAAAAAAAABAAAAIQAAAGFzc2VydGlvbiBmYWlsZWQ6IGAobGVmdCA9PSByaWdodClgCiAgbGVmdDogYGAsCiByaWdodDogYGA6IMAIEAAtAAAA7QgQAAwAAAD5CBAAAwAAAENvbnRyYWN0IGV4cGVjdGVkIGEgcmVzdWx0IG9uIHRoZSBjYWxsYmFjawAAFAkQACoAAABzcmMvbGliLnJzAABICRAACgAAACQAAAAFAAAARmFpbGVkIHRvIHNlcmlhbGl6ZSB0aGUgY3Jvc3MgY29udHJhY3QgYXJncyB1c2luZyBKU09OLgBICRAACgAAABoAAAABAAAAb25fYWNjb3VudF9jcmVhdGVkb25fYWNjb3VudF9jcmVhdGVkX2FuZF9jbGFpbWVkYWNjb3VudF9pZGFtb3VudHN0cnVjdCBJbnB1dEF0dGFjaGVkIGRlcG9zaXQgbXVzdCBiZSBncmVhdGVyIHRoYW4gQUNDRVNTX0tFWV9BTExPV0FOQ0UAAEgJEAAKAAAANQAAAAkAAABICRAACgAAADkAAAAcAAAASAkQAAoAAAA5AAAAGwAAAGNsYWltLGNyZWF0ZV9hY2NvdW50X2FuZF9jbGFpbUNsYWltIG9ubHkgY2FuIGNvbWUgZnJvbSB0aGlzIGFjY291bnQAggoQACUAAABICRAACgAAAEQAAAAJAAAASW52YWxpZCBhY2NvdW50IGlkAABICRAACgAAAEkAAAAJAAAAVW5leHBlY3RlZCBwdWJsaWMga2V5AAAASAkQAAoAAABNAAAAFgAAAENyZWF0ZSBhY2NvdW50IGFuZCBjbGFpbSBvbmx5IGNhbiBjb21lIGZyb20gdGhpcyBhY2NvdW50DAsQADgAAABICRAACgAAAFsAAAAJAAAASAkQAAoAAABgAAAACQAAAEgJEAAKAAAAZAAAABYAAABICRAACgAAAHsAAAAJAAAAQ2FsbGJhY2sgY2FuIG9ubHkgYmUgY2FsbGVkIGZyb20gdGhlIGNvbnRyYWN0AAAAjAsQAC0AAABICRAACgAAAIkAAAAJAAAASAkQAAoAAACYAAAACQAAACIAAAAAAAAAAQAAACMAAAAkAAAAJQAAACYAAAAnAAAAKAAAACkAAAAqAAAAKwAAACwAAAAtAAAALgAAAC8AAAAwAAAAMQAAADIAAAAzAAAANAAAADUAAAA2AAAANwAAADgAAAA5AAAAOgAAADsAAAA8AAAAPQAAAD4AAAA/AAAAQAAAAEEAAABCAAAAQwAAAEQAAABFAAAARgAAAEcAAABIAAAASQAAAEoAAABLAAAATAAAAE0AAABOAAAATwAAAFAAAABRAAAAUgAAAEV4cGVjdGVkIGlucHV0IHNpbmNlIG1ldGhvZCBoYXMgYXJndW1lbnRzLgAASAkQAAoAAAAvAAAAAQAAAEZhaWxlZCB0byBkZXNlcmlhbGl6ZSBpbnB1dCBmcm9tIEpTT04uRmFpbGVkIHRvIHNlcmlhbGl6ZSB0aGUgcmV0dXJuIHZhbHVlIHVzaW5nIEpTT04uTWV0aG9kIGRvZXNuJ3QgYWNjZXB0IGRlcG9zaXRwdWJsaWNfa2V5c3RydWN0IElucHV0IHdpdGggMSBlbGVtZW50aQ0QABsAAABTAAAACAAAAAQAAABUAAAAbmV3X2FjY291bnRfaWRuZXdfcHVibGljX2tleXN0cnVjdCBJbnB1dCB3aXRoIDIgZWxlbWVudHO4DRAAHAAAAHByZWRlY2Vzc29yX2FjY291bnRfaWQAAFoAAAAMAAAABAAAAFsAAABcAAAAXQAAAF4AAABfAAAAYAAAAGEAAAAcDhAAAAAAAGEgRGlzcGxheSBpbXBsZW1lbnRhdGlvbiByZXR1cm5lZCBhbiBlcnJvciB1bmV4cGVjdGVkbHkvcnVzdGMvY2QxZWYzOTBlNzMxZWQ3N2I5MGIxMWIxZjc3ZTJjNWNhNjQxYjI2MS9zcmMvbGliYWxsb2Mvc3RyaW5nLnJzAAAAWw4QAEYAAAB8CAAACQAAAAAAAAAAAAAAAAAAAGF0dGVtcHQgdG8gYWRkIHdpdGggb3ZlcmZsb3cAAAAAYXR0ZW1wdCB0byBzdWJ0cmFjdCB3aXRoIG92ZXJmbG93AAAAYgAAAAQAAAAEAAAAYwAAAGQAAABlAAAAL3J1c3RjL2NkMWVmMzkwZTczMWVkNzdiOTBiMTFiMWY3N2UyYzVjYTY0MWIyNjEvc3JjL2xpYmNvcmUvbWFjcm9zL21vZC5ycy9ydXN0Yy9jZDFlZjM5MGU3MzFlZDc3YjkwYjExYjFmNzdlMmM1Y2E2NDFiMjYxL3NyYy9saWJjb3JlL3N0ci9wYXR0ZXJuLnJzAGUPEABKAAAA2QQAABQAAABlDxAASgAAANkEAAAhAAAAZQ8QAEoAAADlBAAAFAAAAGUPEABKAAAA5QQAACEAAAAcDxAASQAAABIAAAANAAAAYXNzZXJ0aW9uIGZhaWxlZDogYChsZWZ0ID09IHJpZ2h0KWAKICBsZWZ0OiBgYCwKIHJpZ2h0OiBgYDogABAQAC0AAAAtEBAADAAAADkQEAADAAAAZGVzdGluYXRpb24gYW5kIHNvdXJjZSBzbGljZXMgaGF2ZSBkaWZmZXJlbnQgbGVuZ3Roc1QQEAA0AAAAZgAAAAAAAAABAAAADQAAAGNhbGxlZCBgUmVzdWx0Ojp1bndyYXAoKWAgb24gYW4gYEVycmAgdmFsdWUAZwAAAAAAAAABAAAAaAAAAGFzc2VydGlvbiBmYWlsZWQ6IHNlbGYuaXNfY2hhcl9ib3VuZGFyeShuZXdfbGVuKTw6OmNvcmU6Om1hY3Jvczo6cGFuaWMgbWFjcm9zPgAADBEQAB4AAAACAAAAAgAAAC9ydXN0Yy9jZDFlZjM5MGU3MzFlZDc3YjkwYjExYjFmNzdlMmM1Y2E2NDFiMjYxL3NyYy9saWJhbGxvYy9yYXdfdmVjLnJzADwREABHAAAAVwAAAB4AAABpbnRlcm5hbCBlcnJvcjogZW50ZXJlZCB1bnJlYWNoYWJsZSBjb2RlVHJpZWQgdG8gc2hyaW5rIHRvIGEgbGFyZ2VyIGNhcGFjaXR5AAAAAAAA8D8AAAAAAAAkQAAAAAAAAFlAAAAAAABAj0AAAAAAAIjDQAAAAAAAavhAAAAAAICELkEAAAAA0BJjQQAAAACE15dBAAAAAGXNzUEAAAAgX6ACQgAAAOh2SDdCAAAAopQabUIAAEDlnDCiQgAAkB7EvNZCAAA0JvVrDEMAgOA3ecNBQwCg2IVXNHZDAMhOZ23Bq0MAPZFg5FjhQ0CMtXgdrxVEUO/i1uQaS0SS1U0Gz/CARPZK4ccCLbVEtJ3ZeUN46kSRAigsKosgRTUDMrf0rVRFAoT+5HHZiUWBEh8v5yfARSHX5vrgMfRF6oygOVk+KUYksAiI741fRhduBbW1uJNGnMlGIuOmyEYDfNjqm9D+RoJNx3JhQjNH4yB5z/kSaEcbaVdDuBeeR7GhFirTztJHHUqc9IeCB0ilXMPxKWM9SOcZGjf6XXJIYaDgxHj1pkh5yBj21rLcSEx9z1nG7xFJnlxD8LdrRknGM1TspQZ8SVygtLMnhLFJc8ihoDHl5UmPOsoIfl4bSppkfsUOG1FKwP3ddtJhhUowfZUUR7q6Sj5u3WxstPBKzskUiIfhJEtB/Blq6RlaS6k9UOIxUJBLE03kWj5kxEtXYJ3xTX35S224BG6h3C9MRPPC5OTpY0wVsPMdXuSYTBuccKV1Hc9MkWFmh2lyA031+T/pA084TXL4j+PEYm5NR/s5Drv9ok0ZesjRKb3XTZ+YOkZ0rA1OZJ/kq8iLQk49x93Wui53Tgw5lYxp+qxOp0Pd94Ec4k6RlNR1oqMWT7W5SROLTExPERQO7NavgU8WmRGnzBu2T1v/1dC/outPmb+F4rdFIVB/LyfbJZdVUF/78FHv/IpQG502kxXewFBiRAT4mhX1UHtVBbYBWypRbVXDEeF4YFHIKjRWGZeUUXo1wavfvMlRbMFYywsWAFLH8S6+jhs0Ujmuum1yImlSx1kpCQ9rn1Id2Lll6aLTUiROKL+jiwhTrWHyroyuPlMMfVftFy1zU09crehd+KdTY7PYYnX23VMecMddCboSVCVMObWLaEdULp+Hoq5CfVR9w5QlrUmyVFz0+W4Y3OZUc3G4ih6THFXoRrMW89tRVaIYYNzvUoZVyh5406vnu1U/Eytky3DxVQ7YNT3+zCVWEk6DzD1AW1bLENKfJgiRVv6UxkcwSsVWPTq4Wbyc+lZmJBO49aEwV4DtFyZzymRX4Oid7w/9mVeMscL1KT7QV+9dM3O0TQRYazUAkCFhOVjFQgD0ablvWLspgDji06NYKjSgxtrI2Fg1QUh4EfsOWcEoLevqXENZ8XL4pSU0eFmtj3YPL0GuWcwZqmm96OJZP6AUxOyiF1pPyBn1p4tNWjIdMPlId4JafiR8NxsVt1qeLVsFYtrsWoL8WEN9CCJbozsvlJyKVluMCju5Qy2MW5fmxFNKnMFbPSC26FwD9ltNqOMiNIQrXDBJzpWgMmFcfNtBu0h/lVxbUhLqGt/KXHlzS9JwywBdV1DeBk3+NF1t5JVI4D1qXcSuXS2sZqBddRq1OFeA1F0SYeIGbaAJXqt8TSREBEBe1ttgLVUFdF7MErl4qgapXn9X5xZVSN9er5ZQLjWNE19bvOR5gnBIX3LrXRijjH5fJ7M67+UXs1/xXwlr393nX+23y0VX1R1g9FKfi1alUmCxJ4curE6HYJ3xKDpXIr1gApdZhHY18mDD/G8l1MImYfT7yy6Jc1xheH0/vTXIkWHWXI8sQzrGYQw0s/fTyPthhwDQeoRdMWKpAISZ5bRlYtQA5f8eIptihCDvX1P10GKl6Oo3qDIFY8+i5UVSfzpjwYWva5OPcGMyZ5tGeLOkY/5AQlhW4Nljn2gp9zUsEGTGwvN0QzdEZHizMFIURXlkVuC8ZlmWr2Q2DDbg973jZEOPQ9h1rRhlFHNUTtPYTmXsx/QQhEeDZej5MRVlGbhlYXh+Wr4f7mU9C4/41tMiZgzOsrbMiFdmj4Ff5P9qjWb5sLvu32LCZjidauqX+/ZmhkQF5X26LGfUSiOvjvRhZ4kd7FqycZZn6ySn8R4OzGcTdwhX04gBaNeUyiwI6zVoDTr9N8pla2hIRP5inh+haFrVvfuFZ9VosUqtemfBCmmvTqys4LhAaVpi19cY53Rp8TrNDd8gqmnWRKBoi1TgaQxWyEKuaRRqj2t60xmESWpzBllIIOV/agikNy0077NqCo2FOAHr6GpM8KaGwSUfazBWKPSYd1Nru2syMX9ViGuqBn/93mq+aypkb17LAvNrNT0LNn7DJ2yCDI7DXbRdbNHHOJq6kJJsxvnGQOk0x2w3uPiQIwL9bCNzmzpWITJt609CyaupZm3m45K7FlScbXDOOzWOtNFtDMKKwrEhBm6Pci0zHqo7bpln/N9SSnFuf4H7l+ecpW7fYfp9IQTbbix9vO6U4hBvdpxrKjobRW+Ugwa1CGJ6bz0SJHFFfbBvzBZtzZac5G9/XMiAvMMZcM85fdBVGlBwQ4icROsghHBUqsMVJim5cOmUNJtvc+9wEd0AwSWoI3FWFEExL5JYcWtZkf26to5x49d63jQyw3HcjRkWwv73cVPxn5ty/i1y1PZDoQe/YnKJ9JSJyW6Xcqsx+ut7Ss1yC198c41OAnPNdlvQMOI2c4FUcgS9mmxz0HTHIrbgoXMEUnmr41jWc4amV5Yc7wt0FMj23XF1QXQYenRVztJ1dJ6Y0eqBR6t0Y//CMrEM4XQ8v3N/3U8VdQuvUN/Uo0p1Z22SC2WmgHXACHdO/s+0dfHKFOL9A+p11v5MrX5CIHaMPqBYHlNUdi9OyO7lZ4l2u2F6at/Bv3YVfYyiK9nzdlqcL4t2zyh3cIP7LVQDX3cmMr2cFGKTd7B+7MOZOsh3XJ7nNEBJ/nf5whAhyO0yeLjzVCk6qWd4pTCqs4iTnXhnXkpwNXzSeAH2XMxCGwd5gjN0fxPiPHkxoKgvTA1yeT3IkjufkKZ5TXp3Csc03HlwrIpm/KAReoxXLYA7CUZ6b604YIqLe3plbCN8Njexen9HLBsEheV6Xln3IUXmGnvblzo1689Qe9I9iQLmA4V7Ro0rg99EuntMOPuxC2vwe18Gep7OhSR89ocYRkKnWXz6VM9riQiQfDgqw8arCsR8x/RzuFYN+Xz48ZBmrFAvfTuXGsBrkmN9Cj0hsAZ3mH1MjClcyJTOfbD3mTn9HAN+nHUAiDzkN34DkwCqS91tfuJbQEpPqqJ+2nLQHONU136QjwTkGyoNf7rZgm5ROkJ/KZAjyuXIdn8zdKw8H3usf6DI64XzzOF/L1VzZXJzL2N5cHJlc3MvLmNhcmdvL3JlZ2lzdHJ5L3NyYy9naXRodWIuY29tLTFlY2M2Mjk5ZGI5ZWM4MjMvc2VyZGVfanNvbi0xLjAuNDgvc3JjL2Vycm9yLnJzcmVjdXJzaW9uIGxpbWl0IGV4Y2VlZGVkdW5leHBlY3RlZCBlbmQgb2YgaGV4IGVzY2FwZXRyYWlsaW5nIGNoYXJhY3RlcnN0cmFpbGluZyBjb21tYWxvbmUgbGVhZGluZyBzdXJyb2dhdGUgaW4gaGV4IGVzY2FwZWtleSBtdXN0IGJlIGEgc3RyaW5nY29udHJvbCBjaGFyYWN0ZXIgKFx1MDAwMC1cdTAwMUYpIGZvdW5kIHdoaWxlIHBhcnNpbmcgYSBzdHJpbmdpbnZhbGlkIHVuaWNvZGUgY29kZSBwb2ludG51bWJlciBvdXQgb2YgcmFuZ2VpbnZhbGlkIG51bWJlcmludmFsaWQgZXNjYXBlZXhwZWN0ZWQgdmFsdWVleHBlY3RlZCBpZGVudGV4cGVjdGVkIGAsYCBvciBgfWBleHBlY3RlZCBgLGAgb3IgYF1gZXhwZWN0ZWQgYDpgRU9GIHdoaWxlIHBhcnNpbmcgYSB2YWx1ZUVPRiB3aGlsZSBwYXJzaW5nIGEgc3RyaW5nRU9GIHdoaWxlIHBhcnNpbmcgYW4gb2JqZWN0RU9GIHdoaWxlIHBhcnNpbmcgYSBsaXN0IGF0IGxpbmUgIGNvbHVtbiBFcnJvcigsIGxpbmU6ICwgY29sdW1uOiApAL4dEAAGAAAAxB0QAAgAAADMHRAACgAAANYdEAABAAAAaW52YWxpZCB0eXBlOiAsIGV4cGVjdGVkIAAAAPgdEAAOAAAABh4QAAsAAABpbnZhbGlkIHR5cGU6IG51bGwsIGV4cGVjdGVkIAAAACQeEAAdAAAAiBsQAF0AAACUAQAAGQAAAIgbEABdAAAAlwEAAAkAAACIGxAAXQAAAJ8BAAAbAAAAiBsQAF0AAACiAQAACQAAADAxMjM0NTY3ODlhYmNkZWZ1dXV1dXV1dWJ0bnVmcnV1dXV1dXV1dXV1dXV1dXV1dQAAIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL1VzZXJzL2N5cHJlc3MvLmNhcmdvL3JlZ2lzdHJ5L3NyYy9naXRodWIuY29tLTFlY2M2Mjk5ZGI5ZWM4MjMvc2VyZGVfanNvbi0xLjAuNDgvc3JjL3JlYWQucnOcHxAAXAAAAJkBAAAVAAAAnB8QAFwAAACVAQAAFQAAAJwfEABcAAAAtwEAABMAAACcHxAAXAAAAMcBAAAVAAAAnB8QAFwAAAC9AQAAGQAAAJwfEABcAAAAwQEAABkAAACcHxAAXAAAAP0BAAA7AAAAnB8QAFwAAAAXAgAAEwAAAJwfEABcAAAAKAIAAAwAAACcHxAAXAAAAC8CAAAlAAAAnB8QAFwAAAA0AgAAGQAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcHxAAXAAAAPoCAAAfAAAAnB8QAFwAAAD6AgAAPQAAAJwfEABcAAAAMwMAABcAAACcHxAAXAAAADMDAAA1AAAA////////////////////////////////////////////////////////////////AAECAwQFBgcICf////////8KCwwNDg///////////////////////////////////woLDA0OD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////y9Vc2Vycy9jeXByZXNzLy5jYXJnby9yZWdpc3RyeS9zcmMvZ2l0aHViLmNvbS0xZWNjNjI5OWRiOWVjODIzL21lbW9yeV91bml0cy0wLjQuMC9zcmMvbGliLnJz6CIQAFwAAACPAAAABgAAAAAAAAAAAAAAAAAAAGF0dGVtcHQgdG8gYWRkIHdpdGggb3ZlcmZsb3foIhAAXAAAAI8AAAAFAAAAAAAAAGF0dGVtcHQgdG8gc3VidHJhY3Qgd2l0aCBvdmVyZmxvdwAAAOgiEABcAAAAFgAAABcAAAAAAAAAAAAAAAAAAABhdHRlbXB0IHRvIG11bHRpcGx5IHdpdGggb3ZlcmZsb3cAAADoIhAAXAAAACoAAAAXAAAA6CIQAFwAAAA8AAAAFwAAAOgiEABcAAAAMwAAABcAAAAvVXNlcnMvY3lwcmVzcy8uY2FyZ28vcmVnaXN0cnkvc3JjL2dpdGh1Yi5jb20tMWVjYzYyOTlkYjllYzgyMy93ZWVfYWxsb2MtMC40LjUvc3JjL2xpYi5ycwAAACQkEABZAAAAngEAAA8AAAAkJBAAWQAAABkCAAAhAAAAJCQQAFkAAAAZAgAANAAAACQkEABZAAAAHAIAAAwAAAAkJBAAWQAAAB0CAAAjAAAAJCQQAFkAAAAhAgAAGwAAAGkAAAAAAAAAAQAAAGoAAABrAAAAbAAAAC9Vc2Vycy9jeXByZXNzLy5jYXJnby9yZWdpc3RyeS9zcmMvZ2l0aHViLmNvbS0xZWNjNjI5OWRiOWVjODIzL3dlZV9hbGxvYy0wLjQuNS9zcmMvaW1wX3dhc20zMi5yc/gkEABgAAAADAAAABMAAABvAAAADAAAAAQAAABwAAAAcQAAAHIAAABzAAAAdAAAAHUAAAB2AAAAZGVzY3JpcHRpb24oKSBpcyBkZXByZWNhdGVkOyB1c2UgRGlzcGxheWNhbm5vdCBhY2Nlc3MgYSBUaHJlYWQgTG9jYWwgU3RvcmFnZSB2YWx1ZSBkdXJpbmcgb3IgYWZ0ZXIgZGVzdHJ1Y3Rpb24vcnVzdGMvY2QxZWYzOTBlNzMxZWQ3N2I5MGIxMWIxZjc3ZTJjNWNhNjQxYjI2MS9zcmMvbGlic3RkL3RocmVhZC9sb2NhbC5yc/4lEABKAAAA7wAAAAkAAABYJhAAAAAAAGEgRGlzcGxheSBpbXBsZW1lbnRhdGlvbiByZXR1cm5lZCBhbiBlcnJvciB1bmV4cGVjdGVkbHkvcnVzdGMvY2QxZWYzOTBlNzMxZWQ3N2I5MGIxMWIxZjc3ZTJjNWNhNjQxYjI2MS9zcmMvbGliYWxsb2Mvc3RyaW5nLnJzAAAAlyYQAEYAAAB8CAAACQAAADEyMzQ1Njc4OUFCQ0RFRkdISktMTU5QUVJTVFVWV1hZWmFiY2RlZmdoaWprbW5vcHFyc3R1dnd4eXppbnRlcm5hbCBlcnJvcjogZW50ZXJlZCB1bnJlYWNoYWJsZSBjb2RlOiAqJxAAKgAAAFRoaXMgZnVuY3Rpb24gcmVxdWlyZXMgJ2NoZWNrJyBmZWF0dXJlAABcJxAAJgAAADw6OmNvcmU6Om1hY3Jvczo6cGFuaWMgbWFjcm9zPgAAjCcQAB4AAAAFAAAAMgAAAIwnEAAeAAAAAgAAAAIAAAAAAAAAYXR0ZW1wdCB0byBhZGQgd2l0aCBvdmVyZmxvdwAAAABhdHRlbXB0IHRvIG11bHRpcGx5IHdpdGggb3ZlcmZsb3dhbHJlYWR5IGJvcnJvd2VkL3J1c3RjL2NkMWVmMzkwZTczMWVkNzdiOTBiMTFiMWY3N2UyYzVjYTY0MWIyNjEvc3JjL2xpYmNvcmUvY2VsbC5ycyEoEABDAAAAbgMAAAkAAABhbHJlYWR5IG11dGFibHkgYm9ycm93ZWQhKBAAQwAAAB4DAAAJAAAAdwAAAAAAAAABAAAADwAAAHgAAAAAAAAAAQAAAHkAAAB6AAAAAAAAAAEAAAB7AAAAfAAAAAAAAAABAAAADQAAAGNhbGxlZCBgUmVzdWx0Ojp1bndyYXAoKWAgb24gYW4gYEVycmAgdmFsdWUAfQAAABQAAAAEAAAAfgAAAGludGVybmFsIGVycm9yOiBlbnRlcmVkIHVucmVhY2hhYmxlIGNvZGUvVXNlcnMvY3lwcmVzcy8uY2FyZ28vcmVnaXN0cnkvc3JjL2dpdGh1Yi5jb20tMWVjYzYyOTlkYjllYzgyMy9uZWFyLXNkay0wLjkuMi9zcmMvY29sbGVjdGlvbnMvbW9kLnJzQCkQAGQAAAA5AAAACQAAAH8AAACAAAAAAAAAAAEAAACBAAAAggAAAIMAAABCbG9ja2NoYWluIGludGVyZmFjZSBub3Qgc2V0Li9Vc2Vycy9jeXByZXNzLy5jYXJnby9yZWdpc3RyeS9zcmMvZ2l0aHViLmNvbS0xZWNjNjI5OWRiOWVjODIzL25lYXItc2RrLTAuOS4yL3NyYy9lbnZpcm9ubWVudC9lbnYucnMAAADtKRAAZAAAAIEAAAAJAAAA7SkQAGQAAACLAAAACQAAAFJlZ2lzdGVyIHdhcyBleHBlY3RlZCB0byBoYXZlIGRhdGEgYmVjYXVzZSB3ZSBqdXN0IHdyb3RlIGl0IGludG8gaXQu7SkQAGQAAACZAAAAFwAAAO0pEABkAAAAmQAAAAUAAADtKRAAZAAAAKMAAAAFAAAA7SkQAGQAAACoAAAAFwAAAO0pEABkAAAAqAAAAAUAAADtKRAAZAAAAKwAAAAFAAAA7SkQAGQAAADwAAAADQAAAO0pEABkAAAAbgEAAB4AAADtKRAAZAAAAHABAAAOAAAA7SkQAGQAAABwAQAALQAAAO0pEABkAAAAcQEAAB8AAADtKRAAZAAAAHUBAAANAAAA7SkQAGQAAAB/AQAADQAAAO0pEABkAAAAiQEAAA0AAADtKRAAZAAAAJQBAAANAAAA7SkQAGQAAACeAQAADQAAAO0pEABkAAAAsgEAAA0AAADtKRAAZAAAAMQBAAANAAAA7SkQAGQAAADSAQAADQAAAO0pEABkAAAA4gEAAA0AAADtKRAAZAAAAPgBAAANAAAA7SkQAGQAAAAMAgAADQAAAO0pEABkAAAAHQIAAA0AAADtKRAAZAAAAC8CAAANAAAAVW5leHBlY3RlZCByZXR1cm4gY29kZS4A7SkQAGQAAABFAgAADgAAAFByb21pc2UgcmVzdWx0IHNob3VsZCd2ZSByZXR1cm5lZCBpbnRvIHJlZ2lzdGVyLu0pEABkAAAAQAIAABgAAADtKRAAZAAAADgCAAANAAAA7SkQAGQAAABNAgAADQAAAO0pEABkAAAAWQIAAA0AAADtKRAAZAAAAGoCAAAFAAAA7SkQAGQAAABkAgAADQAAAO0pEABkAAAAiwIAAA4AAADtKRAAZAAAAIACAAANAAAA7SkQAGQAAACbAgAADgAAAO0pEABkAAAAmgIAABMAAADtKRAAZAAAAJICAAANAAAA7SkQAGQAAACsAgAADgAAAO0pEABkAAAAowIAAA0AAABDYW5ub3QgYWRkIGFjdGlvbiB0byBhIGpvaW50IHByb21pc2UuL1VzZXJzL2N5cHJlc3MvLmNhcmdvL3JlZ2lzdHJ5L3NyYy9naXRodWIuY29tLTFlY2M2Mjk5ZGI5ZWM4MjMvbmVhci1zZGstMC45LjIvc3JjL3Byb21pc2UucnMAAACFLRAAXAAAAOAAAAApAAAAQ2Fubm90IGNhbGxiYWNrIGpvaW50IHByb21pc2UuAACFLRAAXAAAAE8BAAApAAAAZWQyNTUxOXNlY3AyNTZrMVVua25vd24gY3VydmUga2luZEludmFsaWQgbGVuZ3RoIG9mIHRoZSBwdWJsaWMga2V5L3J1c3RjL2NkMWVmMzkwZTczMWVkNzdiOTBiMTFiMWY3N2UyYzVjYTY0MWIyNjEvc3JjL2xpYmNvcmUvb3BzL2FyaXRoLnJzAABmLhAASAAAALsCAAAzAAAAYXR0ZW1wdCB0byBhZGQgd2l0aCBvdmVyZmxvdy9Vc2Vycy9jeXByZXNzLy5jYXJnby9yZWdpc3RyeS9zcmMvZ2l0aHViLmNvbS0xZWNjNjI5OWRiOWVjODIzL2JzNTgtMC4zLjAvc3JjL2RlY29kZS5ycwDcLhAAVwAAAMwAAAANAAAAaW50ZXJuYWwgZXJyb3I6IGVudGVyZWQgdW5yZWFjaGFibGUgY29kZdwuEABXAAAATQEAACcAAABwcm92aWRlZCBzdHJpbmcgY29udGFpbmVkIG5vbi1hc2NpaSBjaGFyYWN0ZXIgc3RhcnRpbmcgYXQgYnl0ZSAAfC8QAD8AAABwcm92aWRlZCBzdHJpbmcgY29udGFpbmVkIGludmFsaWQgY2hhcmFjdGVyICBhdCBieXRlIAAAAMQvEAAsAAAA8C8QAAkAAABidWZmZXIgcHJvdmlkZWQgdG8gZGVjb2RlIGJhc2U1OCBlbmNvZGVkIHN0cmluZyBpbnRvIHdhcyB0b28gc21hbGwAAAwwEABCAAAAX19Ob25FeGhhdXN0aXZlTm9uQXNjaWlDaGFyYWN0ZXJpbmRleAAAAIUAAAAEAAAABAAAAIYAAABJbnZhbGlkQ2hhcmFjdGVyY2hhcmFjdGVyAAAAhwAAAAQAAAAEAAAAiAAAAEJ1ZmZlclRvb1NtYWxsYXNzZXJ0aW9uIGZhaWxlZDogYChsZWZ0ID09IHJpZ2h0KWAKICBsZWZ0OiBgYCwKIHJpZ2h0OiBgYDogAADKMBAALQAAAPcwEAAMAAAAAzEQAAMAAABkZXN0aW5hdGlvbiBhbmQgc291cmNlIHNsaWNlcyBoYXZlIGRpZmZlcmVudCBsZW5ndGhzIDEQADQAAAAvcnVzdGMvY2QxZWYzOTBlNzMxZWQ3N2I5MGIxMWIxZjc3ZTJjNWNhNjQxYjI2MS9zcmMvbGliY29yZS9tYWNyb3MvbW9kLnJzAAAAXDEQAEkAAAASAAAADQAAAGludGVybmFsIGVycm9yOiBlbnRlcmVkIHVucmVhY2hhYmxlIGNvZGU8Ojpjb3JlOjptYWNyb3M6OnBhbmljIG1hY3Jvcz4AAOAxEAAeAAAAAgAAAAIAAACKAAAACAAAAAQAAACLAAAAjAAAAI0AAAAIAAAABAAAAI4AAABhIHN0cmluZ2J5dGUgYXJyYXlzdHJ1Y3QgdmFyaWFudEYyEAAOAAAAdHVwbGUgdmFyaWFudAAAAFwyEAANAAAAbmV3dHlwZSB2YXJpYW50AHQyEAAPAAAAdW5pdCB2YXJpYW50jDIQAAwAAABlbnVtoDIQAAQAAABtYXAArDIQAAMAAABzZXF1ZW5jZbgyEAAIAAAAbmV3dHlwZSBzdHJ1Y3QAAMgyEAAOAAAAT3B0aW9uIHZhbHVl4DIQAAwAAAB1bml0IHZhbHVlAAD0MhAACgAAADwyEAAKAAAAc3RyaW5nIAAQMxAABwAAAGNoYXJhY3RlciBgYCAzEAALAAAAKzMQAAEAAABmbG9hdGluZyBwb2ludCBgPDMQABAAAAArMxAAAQAAAGludGVnZXIgYAAAAFwzEAAJAAAAKzMQAAEAAABib29sZWFuIGAAAAB4MxAACQAAACszEAABAAAAlgAAAAQAAAAEAAAAlwAAAJgAAACZAAAAL3J1c3RjL2NkMWVmMzkwZTczMWVkNzdiOTBiMTFiMWY3N2UyYzVjYTY0MWIyNjEvc3JjL2xpYmNvcmUvbWFjcm9zL21vZC5ycwAAAKwzEABJAAAAEgAAAA0AAABhc3NlcnRpb24gZmFpbGVkOiBgKGxlZnQgPT0gcmlnaHQpYAogIGxlZnQ6IGBgLAogcmlnaHQ6IGBgOiAINBAALQAAADU0EAAMAAAAQTQQAAMAAABkZXN0aW5hdGlvbiBhbmQgc291cmNlIHNsaWNlcyBoYXZlIGRpZmZlcmVudCBsZW5ndGhzXDQQADQAAABjYWxsZWQgYFJlc3VsdDo6dW53cmFwKClgIG9uIGFuIGBFcnJgIHZhbHVlAJoAAAAAAAAAAQAAAGgAAAA8Ojpjb3JlOjptYWNyb3M6OnBhbmljIG1hY3Jvcz4AANQ0EAAeAAAAAgAAAAIAAAAvcnVzdGMvY2QxZWYzOTBlNzMxZWQ3N2I5MGIxMWIxZjc3ZTJjNWNhNjQxYjI2MS9zcmMvbGliYWxsb2MvcmF3X3ZlYy5ycwAENRAARwAAAFcAAAAeAAAAVHJpZWQgdG8gc2hyaW5rIHRvIGEgbGFyZ2VyIGNhcGFjaXR5aW50ZXJuYWwgZXJyb3I6IGVudGVyZWQgdW5yZWFjaGFibGUgY29kZZ8AAAAEAAAABAAAAKAAAAChAAAAogAAAJ8AAAAAAAAAAQAAAKMAAABjYWxsZWQgYE9wdGlvbjo6dW53cmFwKClgIG9uIGEgYE5vbmVgIHZhbHVlQWNjZXNzRXJyb3IAAKQAAAAMAAAABAAAAKUAAACmAAAApwAAAF4AAAClAAAAYAAAAGEAAAB1bmV4cGVjdGVkIGVuZCBvZiBmaWxlb3RoZXIgb3MgZXJyb3JvcGVyYXRpb24gaW50ZXJydXB0ZWR3cml0ZSB6ZXJvdGltZWQgb3V0aW52YWxpZCBkYXRhaW52YWxpZCBpbnB1dCBwYXJhbWV0ZXJvcGVyYXRpb24gd291bGQgYmxvY2tlbnRpdHkgYWxyZWFkeSBleGlzdHNicm9rZW4gcGlwZWFkZHJlc3Mgbm90IGF2YWlsYWJsZWFkZHJlc3MgaW4gdXNlbm90IGNvbm5lY3RlZGNvbm5lY3Rpb24gYWJvcnRlZGNvbm5lY3Rpb24gcmVzZXRjb25uZWN0aW9uIHJlZnVzZWRwZXJtaXNzaW9uIGRlbmllZGVudGl0eSBub3QgZm91bmRLaW5kAAAAnwAAAAEAAAABAAAAqAAAAE9zY29kZQAAnwAAAAQAAAAEAAAAqQAAAGtpbmRtZXNzYWdlAKQAAAAMAAAABAAAAKoAAADANRAAAAAAACAob3MgZXJyb3IgKcA1EAAAAAAArDcQAAsAAAC3NxAAAQAAAGNhbm5vdCBtb2RpZnkgdGhlIHBhbmljIGhvb2sgZnJvbSBhIHBhbmlja2luZyB0aHJlYWRzcmMvbGlic3RkL3Bhbmlja2luZy5ycwAEOBAAFwAAAHAAAAAJAAAABDgQABcAAAB6AQAADwAAAAQ4EAAXAAAAewEAAA8AAACrAAAAEAAAAAQAAACsAAAArQAAAKQAAAAMAAAABAAAAK4AAACfAAAACAAAAAQAAACvAAAAsAAAAJ8AAAAIAAAABAAAALEAAABlcnJvckN1c3RvbQCfAAAABAAAAAQAAACyAAAAnwAAAAQAAAAEAAAAswAAAFVuZXhwZWN0ZWRFb2ZPdGhlckludGVycnVwdGVkV3JpdGVaZXJvVGltZWRPdXRJbnZhbGlkRGF0YUludmFsaWRJbnB1dFdvdWxkQmxvY2tBbHJlYWR5RXhpc3RzQnJva2VuUGlwZUFkZHJOb3RBdmFpbGFibGVBZGRySW5Vc2VOb3RDb25uZWN0ZWRDb25uZWN0aW9uQWJvcnRlZENvbm5lY3Rpb25SZXNldENvbm5lY3Rpb25SZWZ1c2VkUGVybWlzc2lvbkRlbmllZE5vdEZvdW5kb3BlcmF0aW9uIHN1Y2Nlc3NmdWy0AAAABAAAAAQAAAC1AAAAc3JjL2xpYmFsbG9jL3Jhd192ZWMucnNjYXBhY2l0eSBvdmVyZmxvd7g5EAAXAAAA6gIAAAUAAABGcm9tVXRmOEVycm9yYnl0ZXMAALQAAAAEAAAABAAAALYAAABlcnJvcgAAALQAAAAEAAAABAAAALcAAAAwYXNzZXJ0aW9uIGZhaWxlZDogZWRlbHRhID49IDBzcmMvbGliY29yZS9udW0vZGl5X2Zsb2F0LnJzAABKOhAAHAAAAEwAAAAJAAAAYXNzZXJ0aW9uIGZhaWxlZDogYChsZWZ0ID09IHJpZ2h0KWAKICBsZWZ0OiBgYCwKIHJpZ2h0OiBgYAAAeDoQAC0AAAClOhAADAAAALE6EAABAAAASjoQABwAAABOAAAACQAAAAEAAAAKAAAAZAAAAOgDAAAQJwAAoIYBAEBCDwCAlpgAAOH1BQDKmjsCAAAAFAAAAMgAAADQBwAAIE4AAEANAwCAhB4AAC0xAQDC6wsAlDV3AADBb/KGIwAAAAAAge+shVtBbS3uBAAAAAAAAAAAAAABH2q/ZO04bu2Xp9r0+T/pA08YAAAAAAAAAAAAAAAAAAAAAAABPpUuCZnfA/04FQ8v5HQj7PXP0wjcBMTasM28GX8zpgMmH+lOAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfC6YW4fTvnKf2diHLxUSxlDea3BuSs8P2JXVbnGyJrBmxq0kNhUdWtNCPA5U/2PAc1XMF+/5ZfIovFX3x9yA3O1u9M7v3F/3UwUAc3JjL2xpYmNvcmUvbnVtL2ZsdDJkZWMvc3RyYXRlZ3kvZHJhZ29uLnJzYXNzZXJ0aW9uIGZhaWxlZDogZC5tYW50ID4gMAAABDwQACoAAABxAAAABQAAAGFzc2VydGlvbiBmYWlsZWQ6IGQubWludXMgPiAwAAAABDwQACoAAAByAAAABQAAAGFzc2VydGlvbiBmYWlsZWQ6IGQucGx1cyA+IDAEPBAAKgAAAHMAAAAFAAAAYXNzZXJ0aW9uIGZhaWxlZDogZC5tYW50LmNoZWNrZWRfYWRkKGQucGx1cykuaXNfc29tZSgpAAAEPBAAKgAAAHQAAAAFAAAAYXNzZXJ0aW9uIGZhaWxlZDogZC5tYW50LmNoZWNrZWRfc3ViKGQubWludXMpLmlzX3NvbWUoKQAEPBAAKgAAAHUAAAAFAAAAYXNzZXJ0aW9uIGZhaWxlZDogYnVmLmxlbigpID49IE1BWF9TSUdfRElHSVRTAAAABDwQACoAAAB2AAAABQAAAAQ8EAAqAAAAvQAAAAkAAAAEPBAAKgAAAPUAAAANAAAABDwQACoAAAAAAQAABQAAAAQ8EAAqAAAAAQEAAAUAAAAEPBAAKgAAAAIBAAAFAAAABDwQACoAAAADAQAABQAAAAQ8EAAqAAAABAEAAAUAAAAEPBAAKgAAAFoBAAANAAAABDwQACoAAABkAQAANgAAAN9FGj0DzxrmwfvM/gAAAADKxprHF/5wq9z71P4AAAAAT9y8vvyxd//2+9z+AAAAAAzWa0HvkVa+Efzk/gAAAAA8/H+QrR/QjSz87P4AAAAAg5pVMShcUdNG/PT+AAAAALXJpq2PrHGdYfz8/gAAAADLi+4jdyKc6nv8BP8AAAAAbVN4QJFJzK6W/Az/AAAAAFfOtl15EjyCsfwU/wAAAAA3VvtNNpQQwsv8HP8AAAAAT5hIOG/qlpDm/CT/AAAAAMc6giXLhXTXAP0s/wAAAAD0l7+Xzc+GoBv9NP8AAAAA5awqF5gKNO81/Tz/AAAAAI6yNSr7ZziyUP1E/wAAAAA7P8bS39TIhGv9TP8AAAAAus3TGidE3cWF/VT/AAAAAJbJJbvOn2uToP1c/wAAAACEpWJ9JGys27r9ZP8AAAAA9tpfDVhmq6PV/Wz/AAAAACbxw96T+OLz7/10/wAAAAC4gP+qqK21tQr+fP8AAAAAi0p8bAVfYocl/oT/AAAAAFMwwTRg/7zJP/6M/wAAAABVJrqRjIVOllr+lP8AAAAAvX4pcCR3+d90/pz/AAAAAI+45bifvd+mj/6k/wAAAACUfXSIz1+p+Kn+rP8AAAAAz5uoj5NwRLnE/rT/AAAAAGsVD7/48AiK3/68/wAAAAC2MTFlVSWwzfn+xP8AAAAArH970MbiP5kU/8z/AAAAAAY7KyrEEFzkLv/U/wAAAADTknNpmSQkqkn/3P8AAAAADsoAg/K1h/1j/+T/AAAAAOsaEZJkCOW8fv/s/wAAAADMiFBvCcy8jJn/9P8AAAAALGUZ4lgXt9Gz//z/AAAAAAAAAAAAAECczv8EAAAAAAAAAAAAEKXU6Oj/DAAAAAAAAABirMXreK0DABQAAAAAAIQJlPh4OT+BHgAcAAAAAACzFQfJe86XwDgAJAAAAAAAcFzqe84yfo9TACwAAAAAAGiA6aukONLVbQA0AAAAAABFIpoXJidPn4gAPAAAAAAAJ/vE1DGiY+2iAEQAAAAAAKityIw4Zd6wvQBMAAAAAADbZasajgjHg9gAVAAAAAAAmh1xQvkdXcTyAFwAAAAAAFjnG6YsaU2SDQFkAAAAAADqjXAaZO4B2icBbAAAAAAASnfvmpmjbaJCAXQAAAAAAIVrfbR7eAnyXAF8AAAAAAB3GN15oeRUtHcBhAAAAAAAwsWbW5KGW4aSAYwAAAAAAD1dlsjFUzXIrAGUAAAAAACzoJf6XLQqlccBnAAAAAAA41+gmb2fRt7hAaQAAAAAACWMOds0wpul/AGsAAAAAABcn5ijcprG9hYCtAAAAAAAzr7pVFO/3LcxArwAAAAAAOJBIvIX8/yITALEAAAAAACleFzTm84gzGYCzAAAAAAA31Mhe/NaFpiBAtQAAAAAADowH5fctaDimwLcAAAAAACWs+NcU9HZqLYC5AAAAAAAPESnpNl8m/vQAuwAAAAAABBEpKdMTHa76wL0AAAAAAAanEC2746riwYD/AAAAAAALIRXphDvH9AgAwQBAAAAACkxkenlpBCbOwMMAQAAAACdDJyh+5sQ51UDFAEAAAAAKfQ7YtkgKKxwAxwBAAAAAIXPp3peS0SAiwMkAQAAAAAt3awDQOQhv6UDLAEAAAAAj/9EXi+cZ47AAzQBAAAAAEG4jJydFzPU2gM8AQAAAACpG+O0ktsZnvUDRAEAAAAA2Xffum6/lusPBEwBAAAAAHNyYy9saWJjb3JlL251bS9mbHQyZGVjL3N0cmF0ZWd5L2dyaXN1LnJzAAAAKEMQACkAAAB8AAAAFQAAAChDEAApAAAAqAAAAAUAAAAoQxAAKQAAAKkAAAAFAAAAKEMQACkAAACqAAAABQAAAChDEAApAAAAqwAAAAUAAAAoQxAAKQAAAKwAAAAFAAAAKEMQACkAAACtAAAABQAAAGFzc2VydGlvbiBmYWlsZWQ6IGQubWFudCArIGQucGx1cyA8ICgxIDw8IDYxKQAAAChDEAApAAAArgAAAAUAAAAoQxAAKQAAAAoBAAARAAAAAAAAAAAAAAAAAAAAYXR0ZW1wdCB0byBkaXZpZGUgYnkgemVybwAAAChDEAApAAAADQEAAAkAAAAoQxAAKQAAADkBAAAJAAAAYXNzZXJ0aW9uIGZhaWxlZDogIWJ1Zi5pc19lbXB0eSgpAAAAKEMQACkAAADTAQAABQAAAGFzc2VydGlvbiBmYWlsZWQ6IGQubWFudCA8ICgxIDw8IDYxKShDEAApAAAA1AEAAAUAAAAoQxAAKQAAANUBAAAFAAAAKEMQACkAAAAWAgAAEQAAAChDEAApAAAAGQIAAAkAAAAoQxAAKQAAAEwCAAAJAAAAc3JjL2xpYmNvcmUvbnVtL2ZsdDJkZWMvbW9kLnJzAAAERRAAHgAAAJcAAAANAAAABEUQAB4AAACZAAAAEQAAAARFEAAeAAAAnwAAAA0AAAAERRAAHgAAAKEAAAARAAAABEUQAB4AAAAfAQAABQAAAGFzc2VydGlvbiBmYWlsZWQ6IGJ1ZlswXSA+IGInMCcABEUQAB4AAAAgAQAABQAAADAuLi0raW5mTmFOYXNzZXJ0aW9uIGZhaWxlZDogYnVmLmxlbigpID49IG1heGxlbgRFEAAeAAAAzAIAAA0AAABmcm9tX3N0cl9yYWRpeF9pbnQ6IG11c3QgbGllIGluIHRoZSByYW5nZSBgWzIsIDM2XWAgLSBmb3VuZCDkRRAAPAAAAHNyYy9saWJjb3JlL251bS9tb2QucnMAAChGEAAWAAAAPhMAAAUAAABudW1iZXIgd291bGQgYmUgemVybyBmb3Igbm9uLXplcm8gdHlwZW51bWJlciB0b28gc21hbGwgdG8gZml0IGluIHRhcmdldCB0eXBlbnVtYmVyIHRvbyBsYXJnZSB0byBmaXQgaW4gdGFyZ2V0IHR5cGVpbnZhbGlkIGRpZ2l0IGZvdW5kIGluIHN0cmluZ2Nhbm5vdCBwYXJzZSBpbnRlZ2VyIGZyb20gZW1wdHkgc3RyaW5nLi4ABUcQAAIAAABCb3Jyb3dFcnJvckJvcnJvd011dEVycm9yY2FsbGVkIGBPcHRpb246OnVud3JhcCgpYCBvbiBhIGBOb25lYCB2YWx1ZSw6EAAAAAAAOiAAACw6EAAAAAAAXEcQAAIAAAC/AAAAAAAAAAEAAADAAAAAcGFuaWNrZWQgYXQgJycsIIxHEAABAAAAjUcQAAMAAAA6AAAALDoQAAAAAACgRxAAAQAAAKBHEAABAAAAaW5kZXggb3V0IG9mIGJvdW5kczogdGhlIGxlbiBpcyAgYnV0IHRoZSBpbmRleCBpcyAAALxHEAAgAAAA3EcQABIAAAC/AAAADAAAAAQAAADBAAAAwgAAAMMAAAAgICAgIHsKLAosICB7IH0gfSgKKCwpClu/AAAABAAAAAQAAADEAAAAXTB4MDAwMTAyMDMwNDA1MDYwNzA4MDkxMDExMTIxMzE0MTUxNjE3MTgxOTIwMjEyMjIzMjQyNTI2MjcyODI5MzAzMTMyMzMzNDM1MzYzNzM4Mzk0MDQxNDI0MzQ0NDU0NjQ3NDg0OTUwNTE1MjUzNTQ1NTU2NTc1ODU5NjA2MTYyNjM2NDY1NjY2NzY4Njk3MDcxNzI3Mzc0NzU3Njc3Nzg3OTgwODE4MjgzODQ4NTg2ODc4ODg5OTA5MTkyOTM5NDk1OTY5Nzk4OTkAvwAAAAQAAAAEAAAAxQAAAMYAAADHAAAAc3JjL2xpYmNvcmUvZm10L21vZC5ycwAAJEkQABYAAABEBAAADQAAACRJEAAWAAAAUAQAACQAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwdHJ1ZWZhbHNlKClzcmMvbGliY29yZS9zbGljZS9tb2QucnNpbmRleCAgb3V0IG9mIHJhbmdlIGZvciBzbGljZSBvZiBsZW5ndGggAL9JEAAGAAAAxUkQACIAAACnSRAAGAAAAHIKAAAFAAAAc2xpY2UgaW5kZXggc3RhcnRzIGF0ICBidXQgZW5kcyBhdCAACEoQABYAAAAeShAADQAAAKdJEAAYAAAAeAoAAAUAAABzcmMvbGliY29yZS9zdHIvcGF0dGVybi5ycwAATEoQABoAAAAQBQAAFQAAAExKEAAaAAAAPgUAABUAAABMShAAGgAAAD8FAAAVAAAAc3JjL2xpYmNvcmUvc3RyL21vZC5ycwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwMDAwMDAwMDAwMDAwMDAwQEBAQEAAAAAAAAAAAAAABbLi4uXWJ5dGUgaW5kZXggIGlzIG91dCBvZiBib3VuZHMgb2YgYLNLEAALAAAAvksQABYAAACxOhAAAQAAAJhKEAAWAAAAUwgAAAkAAABiZWdpbiA8PSBlbmQgKCA8PSApIHdoZW4gc2xpY2luZyBgAAD8SxAADgAAAApMEAAEAAAADkwQABAAAACxOhAAAQAAAJhKEAAWAAAAVwgAAAUAAACYShAAFgAAAGgIAAAOAAAAIGlzIG5vdCBhIGNoYXIgYm91bmRhcnk7IGl0IGlzIGluc2lkZSAgKGJ5dGVzICkgb2YgYLNLEAALAAAAYEwQACYAAACGTBAACAAAAI5MEAAGAAAAsToQAAEAAACYShAAFgAAAGoIAAAFAAAAc3JjL2xpYmNvcmUvdW5pY29kZS9wcmludGFibGUucnPMTBAAIAAAABoAAAAoAAAAAAEDBQUGBgMHBggICREKHAsZDBQNEg4NDwQQAxISEwkWARcFGAIZAxoHHAIdAR8WIAMrBCwCLQsuATADMQIyAacCqQKqBKsI+gL7Bf0E/gP/Ca14eYuNojBXWIuMkBwd3Q4PS0z7/C4vP1xdX7XihI2OkZKpsbq7xcbJyt7k5f8ABBESKTE0Nzo7PUlKXYSOkqmxtLq7xsrOz+TlAAQNDhESKTE0OjtFRklKXmRlhJGbncnOzw0RKUVJV2RljZGptLq7xcnf5OXwBA0RRUlkZYCBhLK8vr/V1/Dxg4WLpKa+v8XHzs/a20iYvc3Gzs9JTk9XWV5fiY6Psba3v8HGx9cRFhdbXPb3/v+ADW1x3t8ODx9ubxwdX31+rq+7vPoWFx4fRkdOT1haXF5+f7XF1NXc8PH1cnOPdHWWly9fJi4vp6+3v8fP19+aQJeYMI8fwMHO/05PWlsHCA8QJy/u725vNz0/QkWQkf7/U2d1yMnQ0djZ5/7/ACBfIoLfBIJECBsEBhGBrA6AqzUeFYDgAxkIAQQvBDQEBwMBBwYHEQpQDxIHVQgCBBwKCQMIAwcDAgMDAwwEBQMLBgEOFQU6AxEHBgUQB1cHAgcVDVAEQwMtAwEEEQYPDDoEHSVfIG0EaiWAyAWCsAMaBoL9A1kHFQsXCRQMFAxqBgoGGgZZBysFRgosBAwEAQMxCywEGgYLA4CsBgoGH0FMBC0DdAg8Aw8DPAc4CCsFgv8RGAgvES0DIBAhD4CMBIKXGQsViJQFLwU7BwIOGAmAsDB0DIDWGgwFgP8FgLYFJAybxgrSMBCEjQM3CYFcFIC4CIDHMDUECgY4CEYIDAZ0Cx4DWgRZCYCDGBwKFglICICKBqukDBcEMaEEgdomBwwFBYClEYFtEHgoKgZMBICNBIC+AxsDDw0ABgEBAwEEAggICQIKBQsCEAERBBIFExEUAhUCFwIZBBwFHQgkAWoDawK8AtEC1AzVCdYC1wLaAeAF4QLoAu4g8AT5BvoCDCc7Pk5Pj56enwYHCTY9Plbz0NEEFBg2N1ZXvTXOz+ASh4mOngQNDhESKTE0OkVGSUpOT2RlWly2txscqKnY2Qk3kJGoBwo7PmZpj5JvX+7vWmKamycoVZ2goaOkp6iturzEBgsMFR06P0VRpqfMzaAHGRoiJT4/xcYEICMlJigzODpISkxQU1VWWFpcXmBjZWZrc3h9f4qkqq+wwNAMcqOky8xub14iewUDBC0DZQQBLy6Agh0DMQ8cBCQJHgUrBUQEDiqAqgYkBCQEKAg0CwGAkIE3CRYKCICYOQNjCAkwFgUhAxsFAUA4BEsFLwQKBwkHQCAnBAwJNgM6BRoHBAwHUEk3Mw0zBy4ICoEmH4CBKAgqgIYXCU4EHg9DDhkHCgZHCScJdQs/QSoGOwUKBlEGAQUQAwWAi2AgSAgKgKZeIkULCgYNEzkHCjYsBBCAwDxkUwwBgKBFG0gIUx05gQdGCh0DR0k3Aw4ICgY5BwqBNhmAxzINg5tmdQuAxIq8hC+P0YJHobmCOQcqBAJgJgpGCigFE4KwW2VLBDkHEUAEHJf4CILzpQ2BHzEDEQQIgYyJBGsFDQMJBxCTYID2CnMIbhdGgJoUDFcJGYCHgUcDhUIPFYVQK4DVLQMaBAKBcDoFAYUAgNcpTAQKBAKDEURMPYDCPAYBBFUFGzQCgQ4sBGQMVgoNA10DPTkdDSwECQcCDgaAmoPWCg0DCwV0DFkHDBQMBDgICgYoCB5SdwMxA4CmDBQEAwUDDQaFanNyYy9saWJjb3JlL3VuaWNvZGUvbW9kLnJzADFSEAAaAAAAOAAAAA8AAAAxUhAAGgAAADkAAAAQAAAAc3JjL2xpYmNvcmUvbnVtL2JpZ251bS5ycwAAAGxSEAAZAAAA4wEAAAEAAABhc3NlcnRpb24gZmFpbGVkOiBub2JvcnJvd2Fzc2VydGlvbiBmYWlsZWQ6IGRpZ2l0cyA8IDQwYXNzZXJ0aW9uIGZhaWxlZDogb3RoZXIgPiAwAAC/AAAABAAAAAQAAADIAAAAvwAAAAQAAAAEAAAAyQAAAFRyeUZyb21TbGljZUVycm9yU29tZU5vbmVFcnJvclV0ZjhFcnJvcnZhbGlkX3VwX3RvZXJyb3JfbGVuAL8AAAAEAAAABAAAAMoAAAAZDhUeHAQRFxYAABAbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEg0TAAAAAAAAAAAAAAAAAAAAAAAAAAMGCQAHCyAfGh0AAAAAABgAAAAAAAAAAAAAAAAFAgAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAAAoACAAUAAwAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkAAAAAAAAAAAAAAAAABYvNAAAAAAAAAAAAAAAACgAqwIAAAAAAAAAAAAAAABcWIYmAAAAAAAAAAAAAABeZgYAAAAAAAAAAAAATBoAkohPK3UAAAAAAAAAAJgAADoAAAAAAAAAAAAAAAClYUsAAAAAAAAAAAAAAACAAAAAMAByAAAAAAAAAKpEAAAHAAAAAAAAAAAAAD0AAAAAAAAAABcAAAAAABwADgAAAAAAAAAAAAAAAACFAAAAAA+gLVQzTgxtAAALAAAeoVojUABFrQ1RgQAAOQAAAAAAAAAAAAAAAAAAgwBVAJQAr0kAAAAAAAAAFAQ+AHYAAAAgmpEAfFlDVhkAAAAAAAAAAAAAAAAAAAA7AACWRhiEPGR6o2MALgBCPwAAAIcAAAAAAABKAAAAAEchALJ7U3iJeWJ5p5k3AxJIlSRSAAAAAAAAAAAAAAAAaIUAbq5psaYAAAAAAACbi2sAAAAAAAAAAAAAAAAAAABvMmoAAAAAAAAArLOzcAkAcQAAAAAAADGOIh8AAAAAAHQAKo0AAAAAAAAAAAAAAACMXSV3AAAAAAAAAAAALAAAnwBlAJ4KHQAAAABbAAAAAKI4mTZ9NQAbcxV+E2yQfwioKZcFAACdJ5wBZwBBAAAAqZOCEWBXjxCKAABAfV8AALCzAACzs7NNAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAwAAAAAAAAAEAAAAAAAAAAgAAAAAAAAADQAAAAAAAAAPAAAAAAAAABwAAAAAAAAAQAAAAAAAAACwAAAAAAAAAL8AAAAAAAAA+AMAAAAAAAAABwAAAAAAAP8HAAAAAAAA8A8AAAAAAAAAEAAAAAAAAAAeAAAAAAAAACAAAAAAAAABIAAAAAAAAEA/AAAAAAAAAHgAAAAAAADAfwAAAAAAAACAAAAAAAAAwP8BAAAAAACA/wMAAAAAAAAAIAAAAAAAAAAkAAAAAAAABFwAAAAAAAAAfwAAAAAAAACjAAAAAAAAAAACAAAAAAD8fwMAAAAAAACACQAAAAAAAAAOAAAAAIAAfg4AAAAAPwD/FwAAAAAAAP8fAAAAAGQgACAAAAAAQP6PIAAAAAABAAAwAAAAAAAAAEAAAAAAXAAAQAAAAAAAAAB+AAAAAAAAAMAAAAAAAAAA4AAAAAAAAADwAAAAAAAAAPgAAAAAAID//wAAAAAAAAAAAQAAAAAA8AwBAAAAAAAAQAEAAAD/////AwAAAAAAAAALAAAAHiAAAAwAAABAMAAADAAAAB4gQAAMAAAAwT1gAAwAAAAAAABgDwAAAAAAAABgAAAARAgAAGAAAAAAgAAAYAAAAAAAAADwAAAAYAAAAAACAAB////52wcAAAAAAID4BwAAAAAA4LwPAAAAAAAAICEAAAMAAAA8OwAA5w8AAAA8AAAAAMC//z0AAAAAAADAPwAAAADA//8/AAAA+AADkHwAAAAAAAAAgAAAAAAAAADwAAAQAAD4/v8AAP//CAD//wAA////////AAAAAAAAAAABAAEAAAAAAAEAAfj//wAAAQAAAAAAwP8BAAAA/////wEA/iH+AAwAAgAAAAAAAAADAAAAAAAAgAMAAAAAAECjAwAAAAAAAAAIAAAADAAAAAwABAAAAAD4DwC2AAAAAAAQAAAAAAAAABgAAAAcAAAAHAAAAADDAQAeAAAAAAAAAB8AAQAAAMAfHwAHAAAAgO8fAP//////HyAAhjkCAAAAIwACAAAgADBYAAAAAAAAfmYAAAD8///8bQAAAAAAAAB/AAAAAAAAKL8AAAAAAADwzwD//////wcAAQAAAAAAoZABAAAAAAAA/wEAAAADAACgAgAAAAAAAAADAAD3//0hEAMAAAAAgEAABP///////zAEAAAAAACA/wYAAAAAAADABwAAAAAAAPIHIAAAAAA8PggAAAAACAAADgAAAACHAQQOAAAAAAAAABACAAAAAAAAEAYAAAAAAAAQCBAAAAAAARAHAAAAAAAAFA8AAAAAANAXAwAAAAAAABgAAAAAAADyH9/g//7///8fAAAAAAAAACAAAAAAAPg/JAMAAAAAAHgmAAAAAAAAADAHAAAAAADIMwAAAAAAAAA/AAAAAAAAsD8AAAAAAAD/P4BAAAQAAABAHiAAAAwAAEAAAAAAAIDTQAPgAOAA4ABgAAAAAADg/WYAAAAAAAAAcP4HAAAAAPh5AwAAAAAAwH8AAAAAAAD+fwAAAACAAP9/AAAAAAAAAIB/AAAAAAAAgAAAAAAAgACAAAAAAN//AoAwAAAA//8DgAAAAAAAAPiFbvAAAAAAAIcCAAAAAAAAkAAAQH/lH/ifAAAAAAAAAKAAAAAAAAD4pwAAAAAAADywAAAAAAAAfrQAAAAAAAB/vwAAAAAAgPe/AAD+/////78RAAAAAAAAwAAAAAAAAJ3BAAAAAAAAAPj///////9/+P/////////7viEAAAwAAPwAAAAAAAAA/wIAAAAAAAD/AAAAAIAD+P8AAAAAAAD8/wAAAAAAAP//AIACAAAA//8AAAAAAPD//wAAAAD/////AgAAAP////8AAAD4/////wAA+P////////////////8NEgAADAAACQ4KAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQDAAAAAAAAAAAAAAAAAAAAAAAAAAECABAACAAACwAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAAAAAAAAAAABUIAAAAAAAAAAAAAAAAAAAqKz4AAAAAAAAAAAAADwoAMj46FAAAAAAAAAAAAD4AAAAAAAAAAAAAKiwEAAAAAAAAAAAAAAAAAD4BAAAAAAAAAAAAAAAAEBAAAAAAAAAAAAAAAAAAAB8APj4+AD4+Pj42GhsYAAAnDQAAAAAAAAAAAAAAAAAAMwsAAAAAAAAAAAAAAAAAADMgAAAAAAAAAAAAADMZABYTJT4+JD0+PhIMAB4xJgAdCQAiNAIAAAAAAAAAAAAuNz4RDgAAAAAAAAAAAAAAPgYqFwAAAAAAAAAAAAAAAD44ITwcOT4+Pj4wIygtLwU+Pjs+KTUDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAAAAAAGAAAAAAAAAD/AQAAAAAAAP8DAAAAAAAA9w8AAAAAAAD//wAAAAAAAH8A+AAAAAAA/v//BwAAAAAAAP8fAAAAAP///z8AAAAA/////wAAAAADAAAAHwAAAP//////AwAA/////78gAAD//////z8AAP///////wcA/////x94DAD//+//////AQAAAAAABCAEfAAAAAAAAAcAAAAA/v//B/7//wf+//8H//8P/////w/cH88P/x/cH////////z8/Pz//qv///z/////////fX7/n39////97AAAAAAAAAoAgAAAAAADPvP8B///////nvyD//////+f////fZN7/64T8Lz5QvR/y/f//9/////f/////////9///f////3//////////v/8AAAAAAADA////3////9///////////v8AAAAAAAD///////f/AP///wP///8D////f////3//////////f/////3////9////////P////0DX///7////AAAAAPz///8AAAAA/////+BDAAD///////9/AP///////z8//////////3/////////f//////9f/P3///////94////////A/z////////v//////////D///////////////////8EDxUbGQMSFxEAAA4WAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYTAAAAAAAAAAAAAAAAAAAAAAAAAAIHCgAIDB0cGBoAAAAAAAAAAAAAAAAAAAAAAAAFAQAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAsACQAUAA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8SAAAAAAAAAAAAAAAAAB8AAAAAAAAAAAAAAAAAAABJRmYdAAAAAAAAAAAAAAAAij4AAAAAAAAAAAAAAAAAS1MAAAAAAAAAAAAAAABnI0IAAAAAAAAAAAAAAAA9AAAAAAAjAAAAAAAAAAAAdQAALQAAAAAAAAAAAAAAAIJOPAAAAAAAAAAAAAAAAGMAAAAlAFoAAAAAAAAAgTYAAAMAAAAAAAAAAAAALwAAAAAAAAAAEAAAAAAAEwAIAAAAAAAAAAAAAAAAAEMAcgCJAAAAAAAAAAAAAAcAAAB9BRg/ADeHCUBkAAAhAAAAAAAAAAAAAAAAAAoAAEEAAAAAAAAAAAAAAAAMADAAXAAAABl3cQBgRzVELgAAdDkRZSxRXn9QAAAANDEAAABTAAAAAAAAOgAAAAA4GgCIXytraV1PXYSAKmgUOwAXAAAAAAAAAAAAAAAAAFUAAFcAAACDAAAAAAAAAABZAAAAAAAAJm4bFgAAAAAAbUocAAAAAAAAAAAAACQAAHwAUgB7BhUAAAAASAAAAAB+KHYnbCkAIlsOYQ1WcGIEhSB4AgAAeh55AVQAMwAAAIZzWABNRW8LagAAMmxMAACJigAAioqKPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAADQAAAAAAAAAcAAAAAAAAAEAAAAAAAAAAtgAAAAAAAAC/AAAAAAAAAPgDAAAAAAAA8AcAAAAAAAD/BwAAAAAAAAAQAAAAAAAAAB4AAAAAAAAAOAAAAAAAAAA/AAAAAAAAgH8AAAAAAAAAgAAAAAAAAMD/AQAAAAAAgP8DAAAAAAAAgAcAAAAAAAAAfwAAAAAAASCAAAAAAAAAAKMAAAAAAAD8fwMAAAAAAAAABgAAAAAAAP8HAAAAAAAAgAkAAAAAAAAADgAAAACAAH4OAAAAAGQgACAAAAAAQP4PIAAAAAABAAAwAAAAAAAAAEAAAAAAXAAAQAAAAAAAAABgAAAAAACEXIAAAAAAAAAAwAAAAAAAAADgAAAAAAAAAAABAAAAAADwDAEAAABEMGAADAAAAME9YAAMAAAAHiCAAAwAAAAeIMAADAAAAP4h/gAMAAAAAAAAACAAAAAAAAAAYAAAAEQIAABgAAAAAAAAAPAAAABgAAAAAAIAAH////nbBwAAAAAAgPgHAAAAAADgvA8AAAAAAAAgIQAAAwAAADw7AADnDwAAADwAAAAAwJ+fPQAAAADA++8+AAAAAAAAwD8AAAAAAAAA8AAAAAAAAAD8AAAQAAD4/v8AAP//AAD//wAA////////AAAA+P//AAABAAAAAADA/wEAAAD/////AQAAAAAAAAADAAAAAAAAgAMAAAAAAECjAwAAAAAAAAAIAAAADAAAAAwABAAAAAD4DwAAAAAAAAAYAAAAHAAAABwAAAAAwwEAHgAAAAAAAAAfAAEAgADAHx8ABwAAAIDvHwD//////x8gAIY5AgAAACMAAgAAAAAwQAAAAAAAAH5mAAAA/P///G0AAAAAAAAAfwAAAAAAACi/AAAAAAAA8M8AAAAAAwAAoAIAAPf//SEQAwMAAAAAAHgGAAAAAACA/wYAAAAAAADABwAAAAAAAPIHAAAAAIcBBA4GAAAAAAAAEAgQAAAAAAAQBwAAAAAAABQPAAAAAADwFwAAAAAAAPIf3+D//v///x8AAAAAAAAAIAAAAAAA+A8gBwAAAAAAyDMAAAAAAACwPwAAAAAAgPc/BAAAAAAAAEAeIIAADAAAQAAAAAAAgNNAAgAAAAAAAFADAAAAAAAAWAAAAAAA4P1m/gcAAAAA+HkDAAAAAADAfwAAAAAAAP5/AAAAAAAA/38AAAAAAAAAgH8AAAAAAACAMAAAAP//A4Bu8AAAAAAAhwIAAAAAAACQAABAf+Uf+J8AAAAAAAD5pQAAAAAAAPinAAAAAACAPLAAAAAAAAB+tAAAAAAAAH+/AAD+/////78RAAAAAAAAwAAAAAAAAJ3BAgAAAAAAANAAAAAAoMMH+P///////3/4//////////u+IQAADAAA/AAAAAAAAAD/AgAAAAAAAP8AAAIAAAD//wAA+P/7////AAAAAP///////////////0EAAABhAAAAAAAAAAAAAABCAAAAYgAAAAAAAAAAAAAAQwAAAGMAAAAAAAAAAAAAAEQAAABkAAAAAAAAAAAAAABFAAAAZQAAAAAAAAAAAAAARgAAAGYAAAAAAAAAAAAAAEcAAABnAAAAAAAAAAAAAABIAAAAaAAAAAAAAAAAAAAASQAAAGkAAAAAAAAAAAAAAEoAAABqAAAAAAAAAAAAAABLAAAAawAAAAAAAAAAAAAATAAAAGwAAAAAAAAAAAAAAE0AAABtAAAAAAAAAAAAAABOAAAAbgAAAAAAAAAAAAAATwAAAG8AAAAAAAAAAAAAAFAAAABwAAAAAAAAAAAAAABRAAAAcQAAAAAAAAAAAAAAUgAAAHIAAAAAAAAAAAAAAFMAAABzAAAAAAAAAAAAAABUAAAAdAAAAAAAAAAAAAAAVQAAAHUAAAAAAAAAAAAAAFYAAAB2AAAAAAAAAAAAAABXAAAAdwAAAAAAAAAAAAAAWAAAAHgAAAAAAAAAAAAAAFkAAAB5AAAAAAAAAAAAAABaAAAAegAAAAAAAAAAAAAAwAAAAOAAAAAAAAAAAAAAAMEAAADhAAAAAAAAAAAAAADCAAAA4gAAAAAAAAAAAAAAwwAAAOMAAAAAAAAAAAAAAMQAAADkAAAAAAAAAAAAAADFAAAA5QAAAAAAAAAAAAAAxgAAAOYAAAAAAAAAAAAAAMcAAADnAAAAAAAAAAAAAADIAAAA6AAAAAAAAAAAAAAAyQAAAOkAAAAAAAAAAAAAAMoAAADqAAAAAAAAAAAAAADLAAAA6wAAAAAAAAAAAAAAzAAAAOwAAAAAAAAAAAAAAM0AAADtAAAAAAAAAAAAAADOAAAA7gAAAAAAAAAAAAAAzwAAAO8AAAAAAAAAAAAAANAAAADwAAAAAAAAAAAAAADRAAAA8QAAAAAAAAAAAAAA0gAAAPIAAAAAAAAAAAAAANMAAADzAAAAAAAAAAAAAADUAAAA9AAAAAAAAAAAAAAA1QAAAPUAAAAAAAAAAAAAANYAAAD2AAAAAAAAAAAAAADYAAAA+AAAAAAAAAAAAAAA2QAAAPkAAAAAAAAAAAAAANoAAAD6AAAAAAAAAAAAAADbAAAA+wAAAAAAAAAAAAAA3AAAAPwAAAAAAAAAAAAAAN0AAAD9AAAAAAAAAAAAAADeAAAA/gAAAAAAAAAAAAAAAAEAAAEBAAAAAAAAAAAAAAIBAAADAQAAAAAAAAAAAAAEAQAABQEAAAAAAAAAAAAABgEAAAcBAAAAAAAAAAAAAAgBAAAJAQAAAAAAAAAAAAAKAQAACwEAAAAAAAAAAAAADAEAAA0BAAAAAAAAAAAAAA4BAAAPAQAAAAAAAAAAAAAQAQAAEQEAAAAAAAAAAAAAEgEAABMBAAAAAAAAAAAAABQBAAAVAQAAAAAAAAAAAAAWAQAAFwEAAAAAAAAAAAAAGAEAABkBAAAAAAAAAAAAABoBAAAbAQAAAAAAAAAAAAAcAQAAHQEAAAAAAAAAAAAAHgEAAB8BAAAAAAAAAAAAACABAAAhAQAAAAAAAAAAAAAiAQAAIwEAAAAAAAAAAAAAJAEAACUBAAAAAAAAAAAAACYBAAAnAQAAAAAAAAAAAAAoAQAAKQEAAAAAAAAAAAAAKgEAACsBAAAAAAAAAAAAACwBAAAtAQAAAAAAAAAAAAAuAQAALwEAAAAAAAAAAAAAMAEAAGkAAAAHAwAAAAAAADIBAAAzAQAAAAAAAAAAAAA0AQAANQEAAAAAAAAAAAAANgEAADcBAAAAAAAAAAAAADkBAAA6AQAAAAAAAAAAAAA7AQAAPAEAAAAAAAAAAAAAPQEAAD4BAAAAAAAAAAAAAD8BAABAAQAAAAAAAAAAAABBAQAAQgEAAAAAAAAAAAAAQwEAAEQBAAAAAAAAAAAAAEUBAABGAQAAAAAAAAAAAABHAQAASAEAAAAAAAAAAAAASgEAAEsBAAAAAAAAAAAAAEwBAABNAQAAAAAAAAAAAABOAQAATwEAAAAAAAAAAAAAUAEAAFEBAAAAAAAAAAAAAFIBAABTAQAAAAAAAAAAAABUAQAAVQEAAAAAAAAAAAAAVgEAAFcBAAAAAAAAAAAAAFgBAABZAQAAAAAAAAAAAABaAQAAWwEAAAAAAAAAAAAAXAEAAF0BAAAAAAAAAAAAAF4BAABfAQAAAAAAAAAAAABgAQAAYQEAAAAAAAAAAAAAYgEAAGMBAAAAAAAAAAAAAGQBAABlAQAAAAAAAAAAAABmAQAAZwEAAAAAAAAAAAAAaAEAAGkBAAAAAAAAAAAAAGoBAABrAQAAAAAAAAAAAABsAQAAbQEAAAAAAAAAAAAAbgEAAG8BAAAAAAAAAAAAAHABAABxAQAAAAAAAAAAAAByAQAAcwEAAAAAAAAAAAAAdAEAAHUBAAAAAAAAAAAAAHYBAAB3AQAAAAAAAAAAAAB4AQAA/wAAAAAAAAAAAAAAeQEAAHoBAAAAAAAAAAAAAHsBAAB8AQAAAAAAAAAAAAB9AQAAfgEAAAAAAAAAAAAAgQEAAFMCAAAAAAAAAAAAAIIBAACDAQAAAAAAAAAAAACEAQAAhQEAAAAAAAAAAAAAhgEAAFQCAAAAAAAAAAAAAIcBAACIAQAAAAAAAAAAAACJAQAAVgIAAAAAAAAAAAAAigEAAFcCAAAAAAAAAAAAAIsBAACMAQAAAAAAAAAAAACOAQAA3QEAAAAAAAAAAAAAjwEAAFkCAAAAAAAAAAAAAJABAABbAgAAAAAAAAAAAACRAQAAkgEAAAAAAAAAAAAAkwEAAGACAAAAAAAAAAAAAJQBAABjAgAAAAAAAAAAAACWAQAAaQIAAAAAAAAAAAAAlwEAAGgCAAAAAAAAAAAAAJgBAACZAQAAAAAAAAAAAACcAQAAbwIAAAAAAAAAAAAAnQEAAHICAAAAAAAAAAAAAJ8BAAB1AgAAAAAAAAAAAACgAQAAoQEAAAAAAAAAAAAAogEAAKMBAAAAAAAAAAAAAKQBAAClAQAAAAAAAAAAAACmAQAAgAIAAAAAAAAAAAAApwEAAKgBAAAAAAAAAAAAAKkBAACDAgAAAAAAAAAAAACsAQAArQEAAAAAAAAAAAAArgEAAIgCAAAAAAAAAAAAAK8BAACwAQAAAAAAAAAAAACxAQAAigIAAAAAAAAAAAAAsgEAAIsCAAAAAAAAAAAAALMBAAC0AQAAAAAAAAAAAAC1AQAAtgEAAAAAAAAAAAAAtwEAAJICAAAAAAAAAAAAALgBAAC5AQAAAAAAAAAAAAC8AQAAvQEAAAAAAAAAAAAAxAEAAMYBAAAAAAAAAAAAAMUBAADGAQAAAAAAAAAAAADHAQAAyQEAAAAAAAAAAAAAyAEAAMkBAAAAAAAAAAAAAMoBAADMAQAAAAAAAAAAAADLAQAAzAEAAAAAAAAAAAAAzQEAAM4BAAAAAAAAAAAAAM8BAADQAQAAAAAAAAAAAADRAQAA0gEAAAAAAAAAAAAA0wEAANQBAAAAAAAAAAAAANUBAADWAQAAAAAAAAAAAADXAQAA2AEAAAAAAAAAAAAA2QEAANoBAAAAAAAAAAAAANsBAADcAQAAAAAAAAAAAADeAQAA3wEAAAAAAAAAAAAA4AEAAOEBAAAAAAAAAAAAAOIBAADjAQAAAAAAAAAAAADkAQAA5QEAAAAAAAAAAAAA5gEAAOcBAAAAAAAAAAAAAOgBAADpAQAAAAAAAAAAAADqAQAA6wEAAAAAAAAAAAAA7AEAAO0BAAAAAAAAAAAAAO4BAADvAQAAAAAAAAAAAADxAQAA8wEAAAAAAAAAAAAA8gEAAPMBAAAAAAAAAAAAAPQBAAD1AQAAAAAAAAAAAAD2AQAAlQEAAAAAAAAAAAAA9wEAAL8BAAAAAAAAAAAAAPgBAAD5AQAAAAAAAAAAAAD6AQAA+wEAAAAAAAAAAAAA/AEAAP0BAAAAAAAAAAAAAP4BAAD/AQAAAAAAAAAAAAAAAgAAAQIAAAAAAAAAAAAAAgIAAAMCAAAAAAAAAAAAAAQCAAAFAgAAAAAAAAAAAAAGAgAABwIAAAAAAAAAAAAACAIAAAkCAAAAAAAAAAAAAAoCAAALAgAAAAAAAAAAAAAMAgAADQIAAAAAAAAAAAAADgIAAA8CAAAAAAAAAAAAABACAAARAgAAAAAAAAAAAAASAgAAEwIAAAAAAAAAAAAAFAIAABUCAAAAAAAAAAAAABYCAAAXAgAAAAAAAAAAAAAYAgAAGQIAAAAAAAAAAAAAGgIAABsCAAAAAAAAAAAAABwCAAAdAgAAAAAAAAAAAAAeAgAAHwIAAAAAAAAAAAAAIAIAAJ4BAAAAAAAAAAAAACICAAAjAgAAAAAAAAAAAAAkAgAAJQIAAAAAAAAAAAAAJgIAACcCAAAAAAAAAAAAACgCAAApAgAAAAAAAAAAAAAqAgAAKwIAAAAAAAAAAAAALAIAAC0CAAAAAAAAAAAAAC4CAAAvAgAAAAAAAAAAAAAwAgAAMQIAAAAAAAAAAAAAMgIAADMCAAAAAAAAAAAAADoCAABlLAAAAAAAAAAAAAA7AgAAPAIAAAAAAAAAAAAAPQIAAJoBAAAAAAAAAAAAAD4CAABmLAAAAAAAAAAAAABBAgAAQgIAAAAAAAAAAAAAQwIAAIABAAAAAAAAAAAAAEQCAACJAgAAAAAAAAAAAABFAgAAjAIAAAAAAAAAAAAARgIAAEcCAAAAAAAAAAAAAEgCAABJAgAAAAAAAAAAAABKAgAASwIAAAAAAAAAAAAATAIAAE0CAAAAAAAAAAAAAE4CAABPAgAAAAAAAAAAAABwAwAAcQMAAAAAAAAAAAAAcgMAAHMDAAAAAAAAAAAAAHYDAAB3AwAAAAAAAAAAAAB/AwAA8wMAAAAAAAAAAAAAhgMAAKwDAAAAAAAAAAAAAIgDAACtAwAAAAAAAAAAAACJAwAArgMAAAAAAAAAAAAAigMAAK8DAAAAAAAAAAAAAIwDAADMAwAAAAAAAAAAAACOAwAAzQMAAAAAAAAAAAAAjwMAAM4DAAAAAAAAAAAAAJEDAACxAwAAAAAAAAAAAACSAwAAsgMAAAAAAAAAAAAAkwMAALMDAAAAAAAAAAAAAJQDAAC0AwAAAAAAAAAAAACVAwAAtQMAAAAAAAAAAAAAlgMAALYDAAAAAAAAAAAAAJcDAAC3AwAAAAAAAAAAAACYAwAAuAMAAAAAAAAAAAAAmQMAALkDAAAAAAAAAAAAAJoDAAC6AwAAAAAAAAAAAACbAwAAuwMAAAAAAAAAAAAAnAMAALwDAAAAAAAAAAAAAJ0DAAC9AwAAAAAAAAAAAACeAwAAvgMAAAAAAAAAAAAAnwMAAL8DAAAAAAAAAAAAAKADAADAAwAAAAAAAAAAAAChAwAAwQMAAAAAAAAAAAAAowMAAMMDAAAAAAAAAAAAAKQDAADEAwAAAAAAAAAAAAClAwAAxQMAAAAAAAAAAAAApgMAAMYDAAAAAAAAAAAAAKcDAADHAwAAAAAAAAAAAACoAwAAyAMAAAAAAAAAAAAAqQMAAMkDAAAAAAAAAAAAAKoDAADKAwAAAAAAAAAAAACrAwAAywMAAAAAAAAAAAAAzwMAANcDAAAAAAAAAAAAANgDAADZAwAAAAAAAAAAAADaAwAA2wMAAAAAAAAAAAAA3AMAAN0DAAAAAAAAAAAAAN4DAADfAwAAAAAAAAAAAADgAwAA4QMAAAAAAAAAAAAA4gMAAOMDAAAAAAAAAAAAAOQDAADlAwAAAAAAAAAAAADmAwAA5wMAAAAAAAAAAAAA6AMAAOkDAAAAAAAAAAAAAOoDAADrAwAAAAAAAAAAAADsAwAA7QMAAAAAAAAAAAAA7gMAAO8DAAAAAAAAAAAAAPQDAAC4AwAAAAAAAAAAAAD3AwAA+AMAAAAAAAAAAAAA+QMAAPIDAAAAAAAAAAAAAPoDAAD7AwAAAAAAAAAAAAD9AwAAewMAAAAAAAAAAAAA/gMAAHwDAAAAAAAAAAAAAP8DAAB9AwAAAAAAAAAAAAAABAAAUAQAAAAAAAAAAAAAAQQAAFEEAAAAAAAAAAAAAAIEAABSBAAAAAAAAAAAAAADBAAAUwQAAAAAAAAAAAAABAQAAFQEAAAAAAAAAAAAAAUEAABVBAAAAAAAAAAAAAAGBAAAVgQAAAAAAAAAAAAABwQAAFcEAAAAAAAAAAAAAAgEAABYBAAAAAAAAAAAAAAJBAAAWQQAAAAAAAAAAAAACgQAAFoEAAAAAAAAAAAAAAsEAABbBAAAAAAAAAAAAAAMBAAAXAQAAAAAAAAAAAAADQQAAF0EAAAAAAAAAAAAAA4EAABeBAAAAAAAAAAAAAAPBAAAXwQAAAAAAAAAAAAAEAQAADAEAAAAAAAAAAAAABEEAAAxBAAAAAAAAAAAAAASBAAAMgQAAAAAAAAAAAAAEwQAADMEAAAAAAAAAAAAABQEAAA0BAAAAAAAAAAAAAAVBAAANQQAAAAAAAAAAAAAFgQAADYEAAAAAAAAAAAAABcEAAA3BAAAAAAAAAAAAAAYBAAAOAQAAAAAAAAAAAAAGQQAADkEAAAAAAAAAAAAABoEAAA6BAAAAAAAAAAAAAAbBAAAOwQAAAAAAAAAAAAAHAQAADwEAAAAAAAAAAAAAB0EAAA9BAAAAAAAAAAAAAAeBAAAPgQAAAAAAAAAAAAAHwQAAD8EAAAAAAAAAAAAACAEAABABAAAAAAAAAAAAAAhBAAAQQQAAAAAAAAAAAAAIgQAAEIEAAAAAAAAAAAAACMEAABDBAAAAAAAAAAAAAAkBAAARAQAAAAAAAAAAAAAJQQAAEUEAAAAAAAAAAAAACYEAABGBAAAAAAAAAAAAAAnBAAARwQAAAAAAAAAAAAAKAQAAEgEAAAAAAAAAAAAACkEAABJBAAAAAAAAAAAAAAqBAAASgQAAAAAAAAAAAAAKwQAAEsEAAAAAAAAAAAAACwEAABMBAAAAAAAAAAAAAAtBAAATQQAAAAAAAAAAAAALgQAAE4EAAAAAAAAAAAAAC8EAABPBAAAAAAAAAAAAABgBAAAYQQAAAAAAAAAAAAAYgQAAGMEAAAAAAAAAAAAAGQEAABlBAAAAAAAAAAAAABmBAAAZwQAAAAAAAAAAAAAaAQAAGkEAAAAAAAAAAAAAGoEAABrBAAAAAAAAAAAAABsBAAAbQQAAAAAAAAAAAAAbgQAAG8EAAAAAAAAAAAAAHAEAABxBAAAAAAAAAAAAAByBAAAcwQAAAAAAAAAAAAAdAQAAHUEAAAAAAAAAAAAAHYEAAB3BAAAAAAAAAAAAAB4BAAAeQQAAAAAAAAAAAAAegQAAHsEAAAAAAAAAAAAAHwEAAB9BAAAAAAAAAAAAAB+BAAAfwQAAAAAAAAAAAAAgAQAAIEEAAAAAAAAAAAAAIoEAACLBAAAAAAAAAAAAACMBAAAjQQAAAAAAAAAAAAAjgQAAI8EAAAAAAAAAAAAAJAEAACRBAAAAAAAAAAAAACSBAAAkwQAAAAAAAAAAAAAlAQAAJUEAAAAAAAAAAAAAJYEAACXBAAAAAAAAAAAAACYBAAAmQQAAAAAAAAAAAAAmgQAAJsEAAAAAAAAAAAAAJwEAACdBAAAAAAAAAAAAACeBAAAnwQAAAAAAAAAAAAAoAQAAKEEAAAAAAAAAAAAAKIEAACjBAAAAAAAAAAAAACkBAAApQQAAAAAAAAAAAAApgQAAKcEAAAAAAAAAAAAAKgEAACpBAAAAAAAAAAAAACqBAAAqwQAAAAAAAAAAAAArAQAAK0EAAAAAAAAAAAAAK4EAACvBAAAAAAAAAAAAACwBAAAsQQAAAAAAAAAAAAAsgQAALMEAAAAAAAAAAAAALQEAAC1BAAAAAAAAAAAAAC2BAAAtwQAAAAAAAAAAAAAuAQAALkEAAAAAAAAAAAAALoEAAC7BAAAAAAAAAAAAAC8BAAAvQQAAAAAAAAAAAAAvgQAAL8EAAAAAAAAAAAAAMAEAADPBAAAAAAAAAAAAADBBAAAwgQAAAAAAAAAAAAAwwQAAMQEAAAAAAAAAAAAAMUEAADGBAAAAAAAAAAAAADHBAAAyAQAAAAAAAAAAAAAyQQAAMoEAAAAAAAAAAAAAMsEAADMBAAAAAAAAAAAAADNBAAAzgQAAAAAAAAAAAAA0AQAANEEAAAAAAAAAAAAANIEAADTBAAAAAAAAAAAAADUBAAA1QQAAAAAAAAAAAAA1gQAANcEAAAAAAAAAAAAANgEAADZBAAAAAAAAAAAAADaBAAA2wQAAAAAAAAAAAAA3AQAAN0EAAAAAAAAAAAAAN4EAADfBAAAAAAAAAAAAADgBAAA4QQAAAAAAAAAAAAA4gQAAOMEAAAAAAAAAAAAAOQEAADlBAAAAAAAAAAAAADmBAAA5wQAAAAAAAAAAAAA6AQAAOkEAAAAAAAAAAAAAOoEAADrBAAAAAAAAAAAAADsBAAA7QQAAAAAAAAAAAAA7gQAAO8EAAAAAAAAAAAAAPAEAADxBAAAAAAAAAAAAADyBAAA8wQAAAAAAAAAAAAA9AQAAPUEAAAAAAAAAAAAAPYEAAD3BAAAAAAAAAAAAAD4BAAA+QQAAAAAAAAAAAAA+gQAAPsEAAAAAAAAAAAAAPwEAAD9BAAAAAAAAAAAAAD+BAAA/wQAAAAAAAAAAAAAAAUAAAEFAAAAAAAAAAAAAAIFAAADBQAAAAAAAAAAAAAEBQAABQUAAAAAAAAAAAAABgUAAAcFAAAAAAAAAAAAAAgFAAAJBQAAAAAAAAAAAAAKBQAACwUAAAAAAAAAAAAADAUAAA0FAAAAAAAAAAAAAA4FAAAPBQAAAAAAAAAAAAAQBQAAEQUAAAAAAAAAAAAAEgUAABMFAAAAAAAAAAAAABQFAAAVBQAAAAAAAAAAAAAWBQAAFwUAAAAAAAAAAAAAGAUAABkFAAAAAAAAAAAAABoFAAAbBQAAAAAAAAAAAAAcBQAAHQUAAAAAAAAAAAAAHgUAAB8FAAAAAAAAAAAAACAFAAAhBQAAAAAAAAAAAAAiBQAAIwUAAAAAAAAAAAAAJAUAACUFAAAAAAAAAAAAACYFAAAnBQAAAAAAAAAAAAAoBQAAKQUAAAAAAAAAAAAAKgUAACsFAAAAAAAAAAAAACwFAAAtBQAAAAAAAAAAAAAuBQAALwUAAAAAAAAAAAAAMQUAAGEFAAAAAAAAAAAAADIFAABiBQAAAAAAAAAAAAAzBQAAYwUAAAAAAAAAAAAANAUAAGQFAAAAAAAAAAAAADUFAABlBQAAAAAAAAAAAAA2BQAAZgUAAAAAAAAAAAAANwUAAGcFAAAAAAAAAAAAADgFAABoBQAAAAAAAAAAAAA5BQAAaQUAAAAAAAAAAAAAOgUAAGoFAAAAAAAAAAAAADsFAABrBQAAAAAAAAAAAAA8BQAAbAUAAAAAAAAAAAAAPQUAAG0FAAAAAAAAAAAAAD4FAABuBQAAAAAAAAAAAAA/BQAAbwUAAAAAAAAAAAAAQAUAAHAFAAAAAAAAAAAAAEEFAABxBQAAAAAAAAAAAABCBQAAcgUAAAAAAAAAAAAAQwUAAHMFAAAAAAAAAAAAAEQFAAB0BQAAAAAAAAAAAABFBQAAdQUAAAAAAAAAAAAARgUAAHYFAAAAAAAAAAAAAEcFAAB3BQAAAAAAAAAAAABIBQAAeAUAAAAAAAAAAAAASQUAAHkFAAAAAAAAAAAAAEoFAAB6BQAAAAAAAAAAAABLBQAAewUAAAAAAAAAAAAATAUAAHwFAAAAAAAAAAAAAE0FAAB9BQAAAAAAAAAAAABOBQAAfgUAAAAAAAAAAAAATwUAAH8FAAAAAAAAAAAAAFAFAACABQAAAAAAAAAAAABRBQAAgQUAAAAAAAAAAAAAUgUAAIIFAAAAAAAAAAAAAFMFAACDBQAAAAAAAAAAAABUBQAAhAUAAAAAAAAAAAAAVQUAAIUFAAAAAAAAAAAAAFYFAACGBQAAAAAAAAAAAACgEAAAAC0AAAAAAAAAAAAAoRAAAAEtAAAAAAAAAAAAAKIQAAACLQAAAAAAAAAAAACjEAAAAy0AAAAAAAAAAAAApBAAAAQtAAAAAAAAAAAAAKUQAAAFLQAAAAAAAAAAAACmEAAABi0AAAAAAAAAAAAApxAAAActAAAAAAAAAAAAAKgQAAAILQAAAAAAAAAAAACpEAAACS0AAAAAAAAAAAAAqhAAAAotAAAAAAAAAAAAAKsQAAALLQAAAAAAAAAAAACsEAAADC0AAAAAAAAAAAAArRAAAA0tAAAAAAAAAAAAAK4QAAAOLQAAAAAAAAAAAACvEAAADy0AAAAAAAAAAAAAsBAAABAtAAAAAAAAAAAAALEQAAARLQAAAAAAAAAAAACyEAAAEi0AAAAAAAAAAAAAsxAAABMtAAAAAAAAAAAAALQQAAAULQAAAAAAAAAAAAC1EAAAFS0AAAAAAAAAAAAAthAAABYtAAAAAAAAAAAAALcQAAAXLQAAAAAAAAAAAAC4EAAAGC0AAAAAAAAAAAAAuRAAABktAAAAAAAAAAAAALoQAAAaLQAAAAAAAAAAAAC7EAAAGy0AAAAAAAAAAAAAvBAAABwtAAAAAAAAAAAAAL0QAAAdLQAAAAAAAAAAAAC+EAAAHi0AAAAAAAAAAAAAvxAAAB8tAAAAAAAAAAAAAMAQAAAgLQAAAAAAAAAAAADBEAAAIS0AAAAAAAAAAAAAwhAAACItAAAAAAAAAAAAAMMQAAAjLQAAAAAAAAAAAADEEAAAJC0AAAAAAAAAAAAAxRAAACUtAAAAAAAAAAAAAMcQAAAnLQAAAAAAAAAAAADNEAAALS0AAAAAAAAAAAAAoBMAAHCrAAAAAAAAAAAAAKETAABxqwAAAAAAAAAAAACiEwAAcqsAAAAAAAAAAAAAoxMAAHOrAAAAAAAAAAAAAKQTAAB0qwAAAAAAAAAAAAClEwAAdasAAAAAAAAAAAAAphMAAHarAAAAAAAAAAAAAKcTAAB3qwAAAAAAAAAAAACoEwAAeKsAAAAAAAAAAAAAqRMAAHmrAAAAAAAAAAAAAKoTAAB6qwAAAAAAAAAAAACrEwAAe6sAAAAAAAAAAAAArBMAAHyrAAAAAAAAAAAAAK0TAAB9qwAAAAAAAAAAAACuEwAAfqsAAAAAAAAAAAAArxMAAH+rAAAAAAAAAAAAALATAACAqwAAAAAAAAAAAACxEwAAgasAAAAAAAAAAAAAshMAAIKrAAAAAAAAAAAAALMTAACDqwAAAAAAAAAAAAC0EwAAhKsAAAAAAAAAAAAAtRMAAIWrAAAAAAAAAAAAALYTAACGqwAAAAAAAAAAAAC3EwAAh6sAAAAAAAAAAAAAuBMAAIirAAAAAAAAAAAAALkTAACJqwAAAAAAAAAAAAC6EwAAiqsAAAAAAAAAAAAAuxMAAIurAAAAAAAAAAAAALwTAACMqwAAAAAAAAAAAAC9EwAAjasAAAAAAAAAAAAAvhMAAI6rAAAAAAAAAAAAAL8TAACPqwAAAAAAAAAAAADAEwAAkKsAAAAAAAAAAAAAwRMAAJGrAAAAAAAAAAAAAMITAACSqwAAAAAAAAAAAADDEwAAk6sAAAAAAAAAAAAAxBMAAJSrAAAAAAAAAAAAAMUTAACVqwAAAAAAAAAAAADGEwAAlqsAAAAAAAAAAAAAxxMAAJerAAAAAAAAAAAAAMgTAACYqwAAAAAAAAAAAADJEwAAmasAAAAAAAAAAAAAyhMAAJqrAAAAAAAAAAAAAMsTAACbqwAAAAAAAAAAAADMEwAAnKsAAAAAAAAAAAAAzRMAAJ2rAAAAAAAAAAAAAM4TAACeqwAAAAAAAAAAAADPEwAAn6sAAAAAAAAAAAAA0BMAAKCrAAAAAAAAAAAAANETAAChqwAAAAAAAAAAAADSEwAAoqsAAAAAAAAAAAAA0xMAAKOrAAAAAAAAAAAAANQTAACkqwAAAAAAAAAAAADVEwAApasAAAAAAAAAAAAA1hMAAKarAAAAAAAAAAAAANcTAACnqwAAAAAAAAAAAADYEwAAqKsAAAAAAAAAAAAA2RMAAKmrAAAAAAAAAAAAANoTAACqqwAAAAAAAAAAAADbEwAAq6sAAAAAAAAAAAAA3BMAAKyrAAAAAAAAAAAAAN0TAACtqwAAAAAAAAAAAADeEwAArqsAAAAAAAAAAAAA3xMAAK+rAAAAAAAAAAAAAOATAACwqwAAAAAAAAAAAADhEwAAsasAAAAAAAAAAAAA4hMAALKrAAAAAAAAAAAAAOMTAACzqwAAAAAAAAAAAADkEwAAtKsAAAAAAAAAAAAA5RMAALWrAAAAAAAAAAAAAOYTAAC2qwAAAAAAAAAAAADnEwAAt6sAAAAAAAAAAAAA6BMAALirAAAAAAAAAAAAAOkTAAC5qwAAAAAAAAAAAADqEwAAuqsAAAAAAAAAAAAA6xMAALurAAAAAAAAAAAAAOwTAAC8qwAAAAAAAAAAAADtEwAAvasAAAAAAAAAAAAA7hMAAL6rAAAAAAAAAAAAAO8TAAC/qwAAAAAAAAAAAADwEwAA+BMAAAAAAAAAAAAA8RMAAPkTAAAAAAAAAAAAAPITAAD6EwAAAAAAAAAAAADzEwAA+xMAAAAAAAAAAAAA9BMAAPwTAAAAAAAAAAAAAPUTAAD9EwAAAAAAAAAAAACQHAAA0BAAAAAAAAAAAAAAkRwAANEQAAAAAAAAAAAAAJIcAADSEAAAAAAAAAAAAACTHAAA0xAAAAAAAAAAAAAAlBwAANQQAAAAAAAAAAAAAJUcAADVEAAAAAAAAAAAAACWHAAA1hAAAAAAAAAAAAAAlxwAANcQAAAAAAAAAAAAAJgcAADYEAAAAAAAAAAAAACZHAAA2RAAAAAAAAAAAAAAmhwAANoQAAAAAAAAAAAAAJscAADbEAAAAAAAAAAAAACcHAAA3BAAAAAAAAAAAAAAnRwAAN0QAAAAAAAAAAAAAJ4cAADeEAAAAAAAAAAAAACfHAAA3xAAAAAAAAAAAAAAoBwAAOAQAAAAAAAAAAAAAKEcAADhEAAAAAAAAAAAAACiHAAA4hAAAAAAAAAAAAAAoxwAAOMQAAAAAAAAAAAAAKQcAADkEAAAAAAAAAAAAAClHAAA5RAAAAAAAAAAAAAAphwAAOYQAAAAAAAAAAAAAKccAADnEAAAAAAAAAAAAACoHAAA6BAAAAAAAAAAAAAAqRwAAOkQAAAAAAAAAAAAAKocAADqEAAAAAAAAAAAAACrHAAA6xAAAAAAAAAAAAAArBwAAOwQAAAAAAAAAAAAAK0cAADtEAAAAAAAAAAAAACuHAAA7hAAAAAAAAAAAAAArxwAAO8QAAAAAAAAAAAAALAcAADwEAAAAAAAAAAAAACxHAAA8RAAAAAAAAAAAAAAshwAAPIQAAAAAAAAAAAAALMcAADzEAAAAAAAAAAAAAC0HAAA9BAAAAAAAAAAAAAAtRwAAPUQAAAAAAAAAAAAALYcAAD2EAAAAAAAAAAAAAC3HAAA9xAAAAAAAAAAAAAAuBwAAPgQAAAAAAAAAAAAALkcAAD5EAAAAAAAAAAAAAC6HAAA+hAAAAAAAAAAAAAAvRwAAP0QAAAAAAAAAAAAAL4cAAD+EAAAAAAAAAAAAAC/HAAA/xAAAAAAAAAAAAAAAB4AAAEeAAAAAAAAAAAAAAIeAAADHgAAAAAAAAAAAAAEHgAABR4AAAAAAAAAAAAABh4AAAceAAAAAAAAAAAAAAgeAAAJHgAAAAAAAAAAAAAKHgAACx4AAAAAAAAAAAAADB4AAA0eAAAAAAAAAAAAAA4eAAAPHgAAAAAAAAAAAAAQHgAAER4AAAAAAAAAAAAAEh4AABMeAAAAAAAAAAAAABQeAAAVHgAAAAAAAAAAAAAWHgAAFx4AAAAAAAAAAAAAGB4AABkeAAAAAAAAAAAAABoeAAAbHgAAAAAAAAAAAAAcHgAAHR4AAAAAAAAAAAAAHh4AAB8eAAAAAAAAAAAAACAeAAAhHgAAAAAAAAAAAAAiHgAAIx4AAAAAAAAAAAAAJB4AACUeAAAAAAAAAAAAACYeAAAnHgAAAAAAAAAAAAAoHgAAKR4AAAAAAAAAAAAAKh4AACseAAAAAAAAAAAAACweAAAtHgAAAAAAAAAAAAAuHgAALx4AAAAAAAAAAAAAMB4AADEeAAAAAAAAAAAAADIeAAAzHgAAAAAAAAAAAAA0HgAANR4AAAAAAAAAAAAANh4AADceAAAAAAAAAAAAADgeAAA5HgAAAAAAAAAAAAA6HgAAOx4AAAAAAAAAAAAAPB4AAD0eAAAAAAAAAAAAAD4eAAA/HgAAAAAAAAAAAABAHgAAQR4AAAAAAAAAAAAAQh4AAEMeAAAAAAAAAAAAAEQeAABFHgAAAAAAAAAAAABGHgAARx4AAAAAAAAAAAAASB4AAEkeAAAAAAAAAAAAAEoeAABLHgAAAAAAAAAAAABMHgAATR4AAAAAAAAAAAAATh4AAE8eAAAAAAAAAAAAAFAeAABRHgAAAAAAAAAAAABSHgAAUx4AAAAAAAAAAAAAVB4AAFUeAAAAAAAAAAAAAFYeAABXHgAAAAAAAAAAAABYHgAAWR4AAAAAAAAAAAAAWh4AAFseAAAAAAAAAAAAAFweAABdHgAAAAAAAAAAAABeHgAAXx4AAAAAAAAAAAAAYB4AAGEeAAAAAAAAAAAAAGIeAABjHgAAAAAAAAAAAABkHgAAZR4AAAAAAAAAAAAAZh4AAGceAAAAAAAAAAAAAGgeAABpHgAAAAAAAAAAAABqHgAAax4AAAAAAAAAAAAAbB4AAG0eAAAAAAAAAAAAAG4eAABvHgAAAAAAAAAAAABwHgAAcR4AAAAAAAAAAAAAch4AAHMeAAAAAAAAAAAAAHQeAAB1HgAAAAAAAAAAAAB2HgAAdx4AAAAAAAAAAAAAeB4AAHkeAAAAAAAAAAAAAHoeAAB7HgAAAAAAAAAAAAB8HgAAfR4AAAAAAAAAAAAAfh4AAH8eAAAAAAAAAAAAAIAeAACBHgAAAAAAAAAAAACCHgAAgx4AAAAAAAAAAAAAhB4AAIUeAAAAAAAAAAAAAIYeAACHHgAAAAAAAAAAAACIHgAAiR4AAAAAAAAAAAAAih4AAIseAAAAAAAAAAAAAIweAACNHgAAAAAAAAAAAACOHgAAjx4AAAAAAAAAAAAAkB4AAJEeAAAAAAAAAAAAAJIeAACTHgAAAAAAAAAAAACUHgAAlR4AAAAAAAAAAAAAnh4AAN8AAAAAAAAAAAAAAKAeAAChHgAAAAAAAAAAAACiHgAAox4AAAAAAAAAAAAApB4AAKUeAAAAAAAAAAAAAKYeAACnHgAAAAAAAAAAAACoHgAAqR4AAAAAAAAAAAAAqh4AAKseAAAAAAAAAAAAAKweAACtHgAAAAAAAAAAAACuHgAArx4AAAAAAAAAAAAAsB4AALEeAAAAAAAAAAAAALIeAACzHgAAAAAAAAAAAAC0HgAAtR4AAAAAAAAAAAAAth4AALceAAAAAAAAAAAAALgeAAC5HgAAAAAAAAAAAAC6HgAAux4AAAAAAAAAAAAAvB4AAL0eAAAAAAAAAAAAAL4eAAC/HgAAAAAAAAAAAADAHgAAwR4AAAAAAAAAAAAAwh4AAMMeAAAAAAAAAAAAAMQeAADFHgAAAAAAAAAAAADGHgAAxx4AAAAAAAAAAAAAyB4AAMkeAAAAAAAAAAAAAMoeAADLHgAAAAAAAAAAAADMHgAAzR4AAAAAAAAAAAAAzh4AAM8eAAAAAAAAAAAAANAeAADRHgAAAAAAAAAAAADSHgAA0x4AAAAAAAAAAAAA1B4AANUeAAAAAAAAAAAAANYeAADXHgAAAAAAAAAAAADYHgAA2R4AAAAAAAAAAAAA2h4AANseAAAAAAAAAAAAANweAADdHgAAAAAAAAAAAADeHgAA3x4AAAAAAAAAAAAA4B4AAOEeAAAAAAAAAAAAAOIeAADjHgAAAAAAAAAAAADkHgAA5R4AAAAAAAAAAAAA5h4AAOceAAAAAAAAAAAAAOgeAADpHgAAAAAAAAAAAADqHgAA6x4AAAAAAAAAAAAA7B4AAO0eAAAAAAAAAAAAAO4eAADvHgAAAAAAAAAAAADwHgAA8R4AAAAAAAAAAAAA8h4AAPMeAAAAAAAAAAAAAPQeAAD1HgAAAAAAAAAAAAD2HgAA9x4AAAAAAAAAAAAA+B4AAPkeAAAAAAAAAAAAAPoeAAD7HgAAAAAAAAAAAAD8HgAA/R4AAAAAAAAAAAAA/h4AAP8eAAAAAAAAAAAAAAgfAAAAHwAAAAAAAAAAAAAJHwAAAR8AAAAAAAAAAAAACh8AAAIfAAAAAAAAAAAAAAsfAAADHwAAAAAAAAAAAAAMHwAABB8AAAAAAAAAAAAADR8AAAUfAAAAAAAAAAAAAA4fAAAGHwAAAAAAAAAAAAAPHwAABx8AAAAAAAAAAAAAGB8AABAfAAAAAAAAAAAAABkfAAARHwAAAAAAAAAAAAAaHwAAEh8AAAAAAAAAAAAAGx8AABMfAAAAAAAAAAAAABwfAAAUHwAAAAAAAAAAAAAdHwAAFR8AAAAAAAAAAAAAKB8AACAfAAAAAAAAAAAAACkfAAAhHwAAAAAAAAAAAAAqHwAAIh8AAAAAAAAAAAAAKx8AACMfAAAAAAAAAAAAACwfAAAkHwAAAAAAAAAAAAAtHwAAJR8AAAAAAAAAAAAALh8AACYfAAAAAAAAAAAAAC8fAAAnHwAAAAAAAAAAAAA4HwAAMB8AAAAAAAAAAAAAOR8AADEfAAAAAAAAAAAAADofAAAyHwAAAAAAAAAAAAA7HwAAMx8AAAAAAAAAAAAAPB8AADQfAAAAAAAAAAAAAD0fAAA1HwAAAAAAAAAAAAA+HwAANh8AAAAAAAAAAAAAPx8AADcfAAAAAAAAAAAAAEgfAABAHwAAAAAAAAAAAABJHwAAQR8AAAAAAAAAAAAASh8AAEIfAAAAAAAAAAAAAEsfAABDHwAAAAAAAAAAAABMHwAARB8AAAAAAAAAAAAATR8AAEUfAAAAAAAAAAAAAFkfAABRHwAAAAAAAAAAAABbHwAAUx8AAAAAAAAAAAAAXR8AAFUfAAAAAAAAAAAAAF8fAABXHwAAAAAAAAAAAABoHwAAYB8AAAAAAAAAAAAAaR8AAGEfAAAAAAAAAAAAAGofAABiHwAAAAAAAAAAAABrHwAAYx8AAAAAAAAAAAAAbB8AAGQfAAAAAAAAAAAAAG0fAABlHwAAAAAAAAAAAABuHwAAZh8AAAAAAAAAAAAAbx8AAGcfAAAAAAAAAAAAAIgfAACAHwAAAAAAAAAAAACJHwAAgR8AAAAAAAAAAAAAih8AAIIfAAAAAAAAAAAAAIsfAACDHwAAAAAAAAAAAACMHwAAhB8AAAAAAAAAAAAAjR8AAIUfAAAAAAAAAAAAAI4fAACGHwAAAAAAAAAAAACPHwAAhx8AAAAAAAAAAAAAmB8AAJAfAAAAAAAAAAAAAJkfAACRHwAAAAAAAAAAAACaHwAAkh8AAAAAAAAAAAAAmx8AAJMfAAAAAAAAAAAAAJwfAACUHwAAAAAAAAAAAACdHwAAlR8AAAAAAAAAAAAAnh8AAJYfAAAAAAAAAAAAAJ8fAACXHwAAAAAAAAAAAACoHwAAoB8AAAAAAAAAAAAAqR8AAKEfAAAAAAAAAAAAAKofAACiHwAAAAAAAAAAAACrHwAAox8AAAAAAAAAAAAArB8AAKQfAAAAAAAAAAAAAK0fAAClHwAAAAAAAAAAAACuHwAAph8AAAAAAAAAAAAArx8AAKcfAAAAAAAAAAAAALgfAACwHwAAAAAAAAAAAAC5HwAAsR8AAAAAAAAAAAAAuh8AAHAfAAAAAAAAAAAAALsfAABxHwAAAAAAAAAAAAC8HwAAsx8AAAAAAAAAAAAAyB8AAHIfAAAAAAAAAAAAAMkfAABzHwAAAAAAAAAAAADKHwAAdB8AAAAAAAAAAAAAyx8AAHUfAAAAAAAAAAAAAMwfAADDHwAAAAAAAAAAAADYHwAA0B8AAAAAAAAAAAAA2R8AANEfAAAAAAAAAAAAANofAAB2HwAAAAAAAAAAAADbHwAAdx8AAAAAAAAAAAAA6B8AAOAfAAAAAAAAAAAAAOkfAADhHwAAAAAAAAAAAADqHwAAeh8AAAAAAAAAAAAA6x8AAHsfAAAAAAAAAAAAAOwfAADlHwAAAAAAAAAAAAD4HwAAeB8AAAAAAAAAAAAA+R8AAHkfAAAAAAAAAAAAAPofAAB8HwAAAAAAAAAAAAD7HwAAfR8AAAAAAAAAAAAA/B8AAPMfAAAAAAAAAAAAACYhAADJAwAAAAAAAAAAAAAqIQAAawAAAAAAAAAAAAAAKyEAAOUAAAAAAAAAAAAAADIhAABOIQAAAAAAAAAAAABgIQAAcCEAAAAAAAAAAAAAYSEAAHEhAAAAAAAAAAAAAGIhAAByIQAAAAAAAAAAAABjIQAAcyEAAAAAAAAAAAAAZCEAAHQhAAAAAAAAAAAAAGUhAAB1IQAAAAAAAAAAAABmIQAAdiEAAAAAAAAAAAAAZyEAAHchAAAAAAAAAAAAAGghAAB4IQAAAAAAAAAAAABpIQAAeSEAAAAAAAAAAAAAaiEAAHohAAAAAAAAAAAAAGshAAB7IQAAAAAAAAAAAABsIQAAfCEAAAAAAAAAAAAAbSEAAH0hAAAAAAAAAAAAAG4hAAB+IQAAAAAAAAAAAABvIQAAfyEAAAAAAAAAAAAAgyEAAIQhAAAAAAAAAAAAALYkAADQJAAAAAAAAAAAAAC3JAAA0SQAAAAAAAAAAAAAuCQAANIkAAAAAAAAAAAAALkkAADTJAAAAAAAAAAAAAC6JAAA1CQAAAAAAAAAAAAAuyQAANUkAAAAAAAAAAAAALwkAADWJAAAAAAAAAAAAAC9JAAA1yQAAAAAAAAAAAAAviQAANgkAAAAAAAAAAAAAL8kAADZJAAAAAAAAAAAAADAJAAA2iQAAAAAAAAAAAAAwSQAANskAAAAAAAAAAAAAMIkAADcJAAAAAAAAAAAAADDJAAA3SQAAAAAAAAAAAAAxCQAAN4kAAAAAAAAAAAAAMUkAADfJAAAAAAAAAAAAADGJAAA4CQAAAAAAAAAAAAAxyQAAOEkAAAAAAAAAAAAAMgkAADiJAAAAAAAAAAAAADJJAAA4yQAAAAAAAAAAAAAyiQAAOQkAAAAAAAAAAAAAMskAADlJAAAAAAAAAAAAADMJAAA5iQAAAAAAAAAAAAAzSQAAOckAAAAAAAAAAAAAM4kAADoJAAAAAAAAAAAAADPJAAA6SQAAAAAAAAAAAAAACwAADAsAAAAAAAAAAAAAAEsAAAxLAAAAAAAAAAAAAACLAAAMiwAAAAAAAAAAAAAAywAADMsAAAAAAAAAAAAAAQsAAA0LAAAAAAAAAAAAAAFLAAANSwAAAAAAAAAAAAABiwAADYsAAAAAAAAAAAAAAcsAAA3LAAAAAAAAAAAAAAILAAAOCwAAAAAAAAAAAAACSwAADksAAAAAAAAAAAAAAosAAA6LAAAAAAAAAAAAAALLAAAOywAAAAAAAAAAAAADCwAADwsAAAAAAAAAAAAAA0sAAA9LAAAAAAAAAAAAAAOLAAAPiwAAAAAAAAAAAAADywAAD8sAAAAAAAAAAAAABAsAABALAAAAAAAAAAAAAARLAAAQSwAAAAAAAAAAAAAEiwAAEIsAAAAAAAAAAAAABMsAABDLAAAAAAAAAAAAAAULAAARCwAAAAAAAAAAAAAFSwAAEUsAAAAAAAAAAAAABYsAABGLAAAAAAAAAAAAAAXLAAARywAAAAAAAAAAAAAGCwAAEgsAAAAAAAAAAAAABksAABJLAAAAAAAAAAAAAAaLAAASiwAAAAAAAAAAAAAGywAAEssAAAAAAAAAAAAABwsAABMLAAAAAAAAAAAAAAdLAAATSwAAAAAAAAAAAAAHiwAAE4sAAAAAAAAAAAAAB8sAABPLAAAAAAAAAAAAAAgLAAAUCwAAAAAAAAAAAAAISwAAFEsAAAAAAAAAAAAACIsAABSLAAAAAAAAAAAAAAjLAAAUywAAAAAAAAAAAAAJCwAAFQsAAAAAAAAAAAAACUsAABVLAAAAAAAAAAAAAAmLAAAViwAAAAAAAAAAAAAJywAAFcsAAAAAAAAAAAAACgsAABYLAAAAAAAAAAAAAApLAAAWSwAAAAAAAAAAAAAKiwAAFosAAAAAAAAAAAAACssAABbLAAAAAAAAAAAAAAsLAAAXCwAAAAAAAAAAAAALSwAAF0sAAAAAAAAAAAAAC4sAABeLAAAAAAAAAAAAABgLAAAYSwAAAAAAAAAAAAAYiwAAGsCAAAAAAAAAAAAAGMsAAB9HQAAAAAAAAAAAABkLAAAfQIAAAAAAAAAAAAAZywAAGgsAAAAAAAAAAAAAGksAABqLAAAAAAAAAAAAABrLAAAbCwAAAAAAAAAAAAAbSwAAFECAAAAAAAAAAAAAG4sAABxAgAAAAAAAAAAAABvLAAAUAIAAAAAAAAAAAAAcCwAAFICAAAAAAAAAAAAAHIsAABzLAAAAAAAAAAAAAB1LAAAdiwAAAAAAAAAAAAAfiwAAD8CAAAAAAAAAAAAAH8sAABAAgAAAAAAAAAAAACALAAAgSwAAAAAAAAAAAAAgiwAAIMsAAAAAAAAAAAAAIQsAACFLAAAAAAAAAAAAACGLAAAhywAAAAAAAAAAAAAiCwAAIksAAAAAAAAAAAAAIosAACLLAAAAAAAAAAAAACMLAAAjSwAAAAAAAAAAAAAjiwAAI8sAAAAAAAAAAAAAJAsAACRLAAAAAAAAAAAAACSLAAAkywAAAAAAAAAAAAAlCwAAJUsAAAAAAAAAAAAAJYsAACXLAAAAAAAAAAAAACYLAAAmSwAAAAAAAAAAAAAmiwAAJssAAAAAAAAAAAAAJwsAACdLAAAAAAAAAAAAACeLAAAnywAAAAAAAAAAAAAoCwAAKEsAAAAAAAAAAAAAKIsAACjLAAAAAAAAAAAAACkLAAApSwAAAAAAAAAAAAApiwAAKcsAAAAAAAAAAAAAKgsAACpLAAAAAAAAAAAAACqLAAAqywAAAAAAAAAAAAArCwAAK0sAAAAAAAAAAAAAK4sAACvLAAAAAAAAAAAAACwLAAAsSwAAAAAAAAAAAAAsiwAALMsAAAAAAAAAAAAALQsAAC1LAAAAAAAAAAAAAC2LAAAtywAAAAAAAAAAAAAuCwAALksAAAAAAAAAAAAALosAAC7LAAAAAAAAAAAAAC8LAAAvSwAAAAAAAAAAAAAviwAAL8sAAAAAAAAAAAAAMAsAADBLAAAAAAAAAAAAADCLAAAwywAAAAAAAAAAAAAxCwAAMUsAAAAAAAAAAAAAMYsAADHLAAAAAAAAAAAAADILAAAySwAAAAAAAAAAAAAyiwAAMssAAAAAAAAAAAAAMwsAADNLAAAAAAAAAAAAADOLAAAzywAAAAAAAAAAAAA0CwAANEsAAAAAAAAAAAAANIsAADTLAAAAAAAAAAAAADULAAA1SwAAAAAAAAAAAAA1iwAANcsAAAAAAAAAAAAANgsAADZLAAAAAAAAAAAAADaLAAA2ywAAAAAAAAAAAAA3CwAAN0sAAAAAAAAAAAAAN4sAADfLAAAAAAAAAAAAADgLAAA4SwAAAAAAAAAAAAA4iwAAOMsAAAAAAAAAAAAAOssAADsLAAAAAAAAAAAAADtLAAA7iwAAAAAAAAAAAAA8iwAAPMsAAAAAAAAAAAAAECmAABBpgAAAAAAAAAAAABCpgAAQ6YAAAAAAAAAAAAARKYAAEWmAAAAAAAAAAAAAEamAABHpgAAAAAAAAAAAABIpgAASaYAAAAAAAAAAAAASqYAAEumAAAAAAAAAAAAAEymAABNpgAAAAAAAAAAAABOpgAAT6YAAAAAAAAAAAAAUKYAAFGmAAAAAAAAAAAAAFKmAABTpgAAAAAAAAAAAABUpgAAVaYAAAAAAAAAAAAAVqYAAFemAAAAAAAAAAAAAFimAABZpgAAAAAAAAAAAABapgAAW6YAAAAAAAAAAAAAXKYAAF2mAAAAAAAAAAAAAF6mAABfpgAAAAAAAAAAAABgpgAAYaYAAAAAAAAAAAAAYqYAAGOmAAAAAAAAAAAAAGSmAABlpgAAAAAAAAAAAABmpgAAZ6YAAAAAAAAAAAAAaKYAAGmmAAAAAAAAAAAAAGqmAABrpgAAAAAAAAAAAABspgAAbaYAAAAAAAAAAAAAgKYAAIGmAAAAAAAAAAAAAIKmAACDpgAAAAAAAAAAAACEpgAAhaYAAAAAAAAAAAAAhqYAAIemAAAAAAAAAAAAAIimAACJpgAAAAAAAAAAAACKpgAAi6YAAAAAAAAAAAAAjKYAAI2mAAAAAAAAAAAAAI6mAACPpgAAAAAAAAAAAACQpgAAkaYAAAAAAAAAAAAAkqYAAJOmAAAAAAAAAAAAAJSmAACVpgAAAAAAAAAAAACWpgAAl6YAAAAAAAAAAAAAmKYAAJmmAAAAAAAAAAAAAJqmAACbpgAAAAAAAAAAAAAipwAAI6cAAAAAAAAAAAAAJKcAACWnAAAAAAAAAAAAACanAAAnpwAAAAAAAAAAAAAopwAAKacAAAAAAAAAAAAAKqcAACunAAAAAAAAAAAAACynAAAtpwAAAAAAAAAAAAAupwAAL6cAAAAAAAAAAAAAMqcAADOnAAAAAAAAAAAAADSnAAA1pwAAAAAAAAAAAAA2pwAAN6cAAAAAAAAAAAAAOKcAADmnAAAAAAAAAAAAADqnAAA7pwAAAAAAAAAAAAA8pwAAPacAAAAAAAAAAAAAPqcAAD+nAAAAAAAAAAAAAECnAABBpwAAAAAAAAAAAABCpwAAQ6cAAAAAAAAAAAAARKcAAEWnAAAAAAAAAAAAAEanAABHpwAAAAAAAAAAAABIpwAASacAAAAAAAAAAAAASqcAAEunAAAAAAAAAAAAAEynAABNpwAAAAAAAAAAAABOpwAAT6cAAAAAAAAAAAAAUKcAAFGnAAAAAAAAAAAAAFKnAABTpwAAAAAAAAAAAABUpwAAVacAAAAAAAAAAAAAVqcAAFenAAAAAAAAAAAAAFinAABZpwAAAAAAAAAAAABapwAAW6cAAAAAAAAAAAAAXKcAAF2nAAAAAAAAAAAAAF6nAABfpwAAAAAAAAAAAABgpwAAYacAAAAAAAAAAAAAYqcAAGOnAAAAAAAAAAAAAGSnAABlpwAAAAAAAAAAAABmpwAAZ6cAAAAAAAAAAAAAaKcAAGmnAAAAAAAAAAAAAGqnAABrpwAAAAAAAAAAAABspwAAbacAAAAAAAAAAAAAbqcAAG+nAAAAAAAAAAAAAHmnAAB6pwAAAAAAAAAAAAB7pwAAfKcAAAAAAAAAAAAAfacAAHkdAAAAAAAAAAAAAH6nAAB/pwAAAAAAAAAAAACApwAAgacAAAAAAAAAAAAAgqcAAIOnAAAAAAAAAAAAAISnAACFpwAAAAAAAAAAAACGpwAAh6cAAAAAAAAAAAAAi6cAAIynAAAAAAAAAAAAAI2nAABlAgAAAAAAAAAAAACQpwAAkacAAAAAAAAAAAAAkqcAAJOnAAAAAAAAAAAAAJanAACXpwAAAAAAAAAAAACYpwAAmacAAAAAAAAAAAAAmqcAAJunAAAAAAAAAAAAAJynAACdpwAAAAAAAAAAAACepwAAn6cAAAAAAAAAAAAAoKcAAKGnAAAAAAAAAAAAAKKnAACjpwAAAAAAAAAAAACkpwAApacAAAAAAAAAAAAApqcAAKenAAAAAAAAAAAAAKinAACppwAAAAAAAAAAAACqpwAAZgIAAAAAAAAAAAAAq6cAAFwCAAAAAAAAAAAAAKynAABhAgAAAAAAAAAAAACtpwAAbAIAAAAAAAAAAAAArqcAAGoCAAAAAAAAAAAAALCnAACeAgAAAAAAAAAAAACxpwAAhwIAAAAAAAAAAAAAsqcAAJ0CAAAAAAAAAAAAALOnAABTqwAAAAAAAAAAAAC0pwAAtacAAAAAAAAAAAAAtqcAALenAAAAAAAAAAAAALinAAC5pwAAAAAAAAAAAAC6pwAAu6cAAAAAAAAAAAAAvKcAAL2nAAAAAAAAAAAAAL6nAAC/pwAAAAAAAAAAAADCpwAAw6cAAAAAAAAAAAAAxKcAAJSnAAAAAAAAAAAAAMWnAACCAgAAAAAAAAAAAADGpwAAjh0AAAAAAAAAAAAAIf8AAEH/AAAAAAAAAAAAACL/AABC/wAAAAAAAAAAAAAj/wAAQ/8AAAAAAAAAAAAAJP8AAET/AAAAAAAAAAAAACX/AABF/wAAAAAAAAAAAAAm/wAARv8AAAAAAAAAAAAAJ/8AAEf/AAAAAAAAAAAAACj/AABI/wAAAAAAAAAAAAAp/wAASf8AAAAAAAAAAAAAKv8AAEr/AAAAAAAAAAAAACv/AABL/wAAAAAAAAAAAAAs/wAATP8AAAAAAAAAAAAALf8AAE3/AAAAAAAAAAAAAC7/AABO/wAAAAAAAAAAAAAv/wAAT/8AAAAAAAAAAAAAMP8AAFD/AAAAAAAAAAAAADH/AABR/wAAAAAAAAAAAAAy/wAAUv8AAAAAAAAAAAAAM/8AAFP/AAAAAAAAAAAAADT/AABU/wAAAAAAAAAAAAA1/wAAVf8AAAAAAAAAAAAANv8AAFb/AAAAAAAAAAAAADf/AABX/wAAAAAAAAAAAAA4/wAAWP8AAAAAAAAAAAAAOf8AAFn/AAAAAAAAAAAAADr/AABa/wAAAAAAAAAAAAAABAEAKAQBAAAAAAAAAAAAAQQBACkEAQAAAAAAAAAAAAIEAQAqBAEAAAAAAAAAAAADBAEAKwQBAAAAAAAAAAAABAQBACwEAQAAAAAAAAAAAAUEAQAtBAEAAAAAAAAAAAAGBAEALgQBAAAAAAAAAAAABwQBAC8EAQAAAAAAAAAAAAgEAQAwBAEAAAAAAAAAAAAJBAEAMQQBAAAAAAAAAAAACgQBADIEAQAAAAAAAAAAAAsEAQAzBAEAAAAAAAAAAAAMBAEANAQBAAAAAAAAAAAADQQBADUEAQAAAAAAAAAAAA4EAQA2BAEAAAAAAAAAAAAPBAEANwQBAAAAAAAAAAAAEAQBADgEAQAAAAAAAAAAABEEAQA5BAEAAAAAAAAAAAASBAEAOgQBAAAAAAAAAAAAEwQBADsEAQAAAAAAAAAAABQEAQA8BAEAAAAAAAAAAAAVBAEAPQQBAAAAAAAAAAAAFgQBAD4EAQAAAAAAAAAAABcEAQA/BAEAAAAAAAAAAAAYBAEAQAQBAAAAAAAAAAAAGQQBAEEEAQAAAAAAAAAAABoEAQBCBAEAAAAAAAAAAAAbBAEAQwQBAAAAAAAAAAAAHAQBAEQEAQAAAAAAAAAAAB0EAQBFBAEAAAAAAAAAAAAeBAEARgQBAAAAAAAAAAAAHwQBAEcEAQAAAAAAAAAAACAEAQBIBAEAAAAAAAAAAAAhBAEASQQBAAAAAAAAAAAAIgQBAEoEAQAAAAAAAAAAACMEAQBLBAEAAAAAAAAAAAAkBAEATAQBAAAAAAAAAAAAJQQBAE0EAQAAAAAAAAAAACYEAQBOBAEAAAAAAAAAAAAnBAEATwQBAAAAAAAAAAAAsAQBANgEAQAAAAAAAAAAALEEAQDZBAEAAAAAAAAAAACyBAEA2gQBAAAAAAAAAAAAswQBANsEAQAAAAAAAAAAALQEAQDcBAEAAAAAAAAAAAC1BAEA3QQBAAAAAAAAAAAAtgQBAN4EAQAAAAAAAAAAALcEAQDfBAEAAAAAAAAAAAC4BAEA4AQBAAAAAAAAAAAAuQQBAOEEAQAAAAAAAAAAALoEAQDiBAEAAAAAAAAAAAC7BAEA4wQBAAAAAAAAAAAAvAQBAOQEAQAAAAAAAAAAAL0EAQDlBAEAAAAAAAAAAAC+BAEA5gQBAAAAAAAAAAAAvwQBAOcEAQAAAAAAAAAAAMAEAQDoBAEAAAAAAAAAAADBBAEA6QQBAAAAAAAAAAAAwgQBAOoEAQAAAAAAAAAAAMMEAQDrBAEAAAAAAAAAAADEBAEA7AQBAAAAAAAAAAAAxQQBAO0EAQAAAAAAAAAAAMYEAQDuBAEAAAAAAAAAAADHBAEA7wQBAAAAAAAAAAAAyAQBAPAEAQAAAAAAAAAAAMkEAQDxBAEAAAAAAAAAAADKBAEA8gQBAAAAAAAAAAAAywQBAPMEAQAAAAAAAAAAAMwEAQD0BAEAAAAAAAAAAADNBAEA9QQBAAAAAAAAAAAAzgQBAPYEAQAAAAAAAAAAAM8EAQD3BAEAAAAAAAAAAADQBAEA+AQBAAAAAAAAAAAA0QQBAPkEAQAAAAAAAAAAANIEAQD6BAEAAAAAAAAAAADTBAEA+wQBAAAAAAAAAAAAgAwBAMAMAQAAAAAAAAAAAIEMAQDBDAEAAAAAAAAAAACCDAEAwgwBAAAAAAAAAAAAgwwBAMMMAQAAAAAAAAAAAIQMAQDEDAEAAAAAAAAAAACFDAEAxQwBAAAAAAAAAAAAhgwBAMYMAQAAAAAAAAAAAIcMAQDHDAEAAAAAAAAAAACIDAEAyAwBAAAAAAAAAAAAiQwBAMkMAQAAAAAAAAAAAIoMAQDKDAEAAAAAAAAAAACLDAEAywwBAAAAAAAAAAAAjAwBAMwMAQAAAAAAAAAAAI0MAQDNDAEAAAAAAAAAAACODAEAzgwBAAAAAAAAAAAAjwwBAM8MAQAAAAAAAAAAAJAMAQDQDAEAAAAAAAAAAACRDAEA0QwBAAAAAAAAAAAAkgwBANIMAQAAAAAAAAAAAJMMAQDTDAEAAAAAAAAAAACUDAEA1AwBAAAAAAAAAAAAlQwBANUMAQAAAAAAAAAAAJYMAQDWDAEAAAAAAAAAAACXDAEA1wwBAAAAAAAAAAAAmAwBANgMAQAAAAAAAAAAAJkMAQDZDAEAAAAAAAAAAACaDAEA2gwBAAAAAAAAAAAAmwwBANsMAQAAAAAAAAAAAJwMAQDcDAEAAAAAAAAAAACdDAEA3QwBAAAAAAAAAAAAngwBAN4MAQAAAAAAAAAAAJ8MAQDfDAEAAAAAAAAAAACgDAEA4AwBAAAAAAAAAAAAoQwBAOEMAQAAAAAAAAAAAKIMAQDiDAEAAAAAAAAAAACjDAEA4wwBAAAAAAAAAAAApAwBAOQMAQAAAAAAAAAAAKUMAQDlDAEAAAAAAAAAAACmDAEA5gwBAAAAAAAAAAAApwwBAOcMAQAAAAAAAAAAAKgMAQDoDAEAAAAAAAAAAACpDAEA6QwBAAAAAAAAAAAAqgwBAOoMAQAAAAAAAAAAAKsMAQDrDAEAAAAAAAAAAACsDAEA7AwBAAAAAAAAAAAArQwBAO0MAQAAAAAAAAAAAK4MAQDuDAEAAAAAAAAAAACvDAEA7wwBAAAAAAAAAAAAsAwBAPAMAQAAAAAAAAAAALEMAQDxDAEAAAAAAAAAAACyDAEA8gwBAAAAAAAAAAAAoBgBAMAYAQAAAAAAAAAAAKEYAQDBGAEAAAAAAAAAAACiGAEAwhgBAAAAAAAAAAAAoxgBAMMYAQAAAAAAAAAAAKQYAQDEGAEAAAAAAAAAAAClGAEAxRgBAAAAAAAAAAAAphgBAMYYAQAAAAAAAAAAAKcYAQDHGAEAAAAAAAAAAACoGAEAyBgBAAAAAAAAAAAAqRgBAMkYAQAAAAAAAAAAAKoYAQDKGAEAAAAAAAAAAACrGAEAyxgBAAAAAAAAAAAArBgBAMwYAQAAAAAAAAAAAK0YAQDNGAEAAAAAAAAAAACuGAEAzhgBAAAAAAAAAAAArxgBAM8YAQAAAAAAAAAAALAYAQDQGAEAAAAAAAAAAACxGAEA0RgBAAAAAAAAAAAAshgBANIYAQAAAAAAAAAAALMYAQDTGAEAAAAAAAAAAAC0GAEA1BgBAAAAAAAAAAAAtRgBANUYAQAAAAAAAAAAALYYAQDWGAEAAAAAAAAAAAC3GAEA1xgBAAAAAAAAAAAAuBgBANgYAQAAAAAAAAAAALkYAQDZGAEAAAAAAAAAAAC6GAEA2hgBAAAAAAAAAAAAuxgBANsYAQAAAAAAAAAAALwYAQDcGAEAAAAAAAAAAAC9GAEA3RgBAAAAAAAAAAAAvhgBAN4YAQAAAAAAAAAAAL8YAQDfGAEAAAAAAAAAAABAbgEAYG4BAAAAAAAAAAAAQW4BAGFuAQAAAAAAAAAAAEJuAQBibgEAAAAAAAAAAABDbgEAY24BAAAAAAAAAAAARG4BAGRuAQAAAAAAAAAAAEVuAQBlbgEAAAAAAAAAAABGbgEAZm4BAAAAAAAAAAAAR24BAGduAQAAAAAAAAAAAEhuAQBobgEAAAAAAAAAAABJbgEAaW4BAAAAAAAAAAAASm4BAGpuAQAAAAAAAAAAAEtuAQBrbgEAAAAAAAAAAABMbgEAbG4BAAAAAAAAAAAATW4BAG1uAQAAAAAAAAAAAE5uAQBubgEAAAAAAAAAAABPbgEAb24BAAAAAAAAAAAAUG4BAHBuAQAAAAAAAAAAAFFuAQBxbgEAAAAAAAAAAABSbgEAcm4BAAAAAAAAAAAAU24BAHNuAQAAAAAAAAAAAFRuAQB0bgEAAAAAAAAAAABVbgEAdW4BAAAAAAAAAAAAVm4BAHZuAQAAAAAAAAAAAFduAQB3bgEAAAAAAAAAAABYbgEAeG4BAAAAAAAAAAAAWW4BAHluAQAAAAAAAAAAAFpuAQB6bgEAAAAAAAAAAABbbgEAe24BAAAAAAAAAAAAXG4BAHxuAQAAAAAAAAAAAF1uAQB9bgEAAAAAAAAAAABebgEAfm4BAAAAAAAAAAAAX24BAH9uAQAAAAAAAAAAAADpAQAi6QEAAAAAAAAAAAAB6QEAI+kBAAAAAAAAAAAAAukBACTpAQAAAAAAAAAAAAPpAQAl6QEAAAAAAAAAAAAE6QEAJukBAAAAAAAAAAAABekBACfpAQAAAAAAAAAAAAbpAQAo6QEAAAAAAAAAAAAH6QEAKekBAAAAAAAAAAAACOkBACrpAQAAAAAAAAAAAAnpAQAr6QEAAAAAAAAAAAAK6QEALOkBAAAAAAAAAAAAC+kBAC3pAQAAAAAAAAAAAAzpAQAu6QEAAAAAAAAAAAAN6QEAL+kBAAAAAAAAAAAADukBADDpAQAAAAAAAAAAAA/pAQAx6QEAAAAAAAAAAAAQ6QEAMukBAAAAAAAAAAAAEekBADPpAQAAAAAAAAAAABLpAQA06QEAAAAAAAAAAAAT6QEANekBAAAAAAAAAAAAFOkBADbpAQAAAAAAAAAAABXpAQA36QEAAAAAAAAAAAAW6QEAOOkBAAAAAAAAAAAAF+kBADnpAQAAAAAAAAAAABjpAQA66QEAAAAAAAAAAAAZ6QEAO+kBAAAAAAAAAAAAGukBADzpAQAAAAAAAAAAABvpAQA96QEAAAAAAAAAAAAc6QEAPukBAAAAAAAAAAAAHekBAD/pAQAAAAAAAAAAAB7pAQBA6QEAAAAAAAAAAAAf6QEAQekBAAAAAAAAAAAAIOkBAELpAQAAAAAAAAAAACHpAQBD6QEAAAAAAAAAAABMYXlvdXRFcnJwcml2YXRlAEH4+cIACzgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==" + } + }, + { + "Contract": { + "account_id": "pulse.near", + "code": "AGFzbQEAAAABbBNgAX8Bf2ACf38Bf2AAAGADf39/AGABfwBgA39/fwF/YAABf2ABfgF/YAJ/fwBgAX4AYAJ+fgBgAn9/AX5gAX4BfmAEf39/fwBgA39/fgBgA39+fwBgBH9/f38Bf2AFf39/f38Bf2ACf34BfwJbBgNlbnYFYWJvcnQADQNlbnYFaW5wdXQACQNlbnYMcmVnaXN0ZXJfbGVuAAwDZW52BXBhbmljAAIDZW52DXJlYWRfcmVnaXN0ZXIACgNlbnYIbG9nX3V0ZjgACgOXAZUBBAEAAAIAAgIAAAICAgICAAIAAgIAAQIDBAUBAAECAgIJDAIBCgEAAQABAAQAAQQAAQAGAAMDAQMBAQEAAQARAQUBAQgFAwMIAwEAAAUFBQUEAAUBBRAAAAEBAQEAAAQABAAABgEEAAEAAwAIAQADABIHDgAABggAAAMEAAYBAQEGAAsLCwADBw8HBwcAAQEBAAQJAgIEBAFwAAEFAwEAAQaoAjV/AUEAC38BQQALfwFBAAt/AEEgC38AQcAAC38AQeAAC38AQYABC34AQoCAgIAIC38AQaABC38AQeABC38AQaACC38BQQALfwBB4AILfwBBgAMLfwBBkAQLfgFCAAt+AUIAC34BQgALfgFCAAt+AUIAC34BQgALfgFCAAt/AUEAC38AQQgLfwBBCQt/AEEGC38BQQALfwBBIAt/AEHAAAt/AEHgAAt/AEGAAQt+AEKAgICACAt/AEGgAQt/AEHgAQt/AEGgAgt/AUEAC38AQeACC38AQYADC38AQcAEC38AQeAEC38AQYAFC38BQQALfwFBAAt/AUEAC38BQQALfwBBAAt/AUEAC38BQQALfwBBfwt/AEF/C38BQQALfwBB8B4LfwBB1CALBy4DFF9fc2V0QXJndW1lbnRzTGVuZ3RoAFYGbWVtb3J5AgAJaGVhcnRiZWF0AJkBCAKaAQkGAQBBAQsACrlmlQFTAQV/PwAhASABQRB0IQIgACACSwRAIAAgAmtB//8DakH//wNBf3NxQRB2IQMgASIEIAMiBSAEIAVKG0AAQQBIBEAgA0AAQQBIBEAACwsLIAAkAQtdAQV/IABB8P///wNLBEAACyMBQRBqIQIgAEEPakEPQX9zcSIDQRAiBCADIARLGyEFIAIgBWoQBiACQRBrIQYgBiAFNgIAIAZBATYCBCAGIAE2AgggBiAANgIMIAILBAAgAAsUACAARQRAQQBBAxAHEAghAAsgAAsZACM0QQ9qQQ9Bf3NxJAAjACQBQQAQCSQCCxQAIABFBEBBAEEEEAcQCCEACyAACwgAQQAQCyQLCwYAEAoQDAsUACAARQRAQQBBBxAHEAghAAsgAAsWACAARQRAQQBBBhAHEAghAAsgABAOCwgAQQAQDyQWCwQAEBALBAAQEQsGABANEBILBAAQEwsUACAARQRAQQBBDhAHEAghAAsgAAsIAEEAEBUkGgsUACAARQRAQQBBDxAHEAghAAsgAAsIAEEAEBckIwsGABAWEBgLDQAgAEEQaygCDEEBdgsaACABIAAQGk8EQEF/DwsgACABQQF0ai8BAAsmAEGgBUEAEBskKUHABUEAEBskKkHgBUEAEBskK0GABkEAEBskLAvUAwIGfwF+AkAgACEFIAEhBCACIQMgA0UEQAwBCyAFIAQ6AAAgBSADakEBayAEOgAAIANBAk0EQAwBCyAFQQFqIAQ6AAAgBUECaiAEOgAAIAUgA2pBAmsgBDoAACAFIANqQQNrIAQ6AAAgA0EGTQRADAELIAVBA2ogBDoAACAFIANqQQRrIAQ6AAAgA0EITQRADAELQQAgBWtBA3EhBiAFIAZqIQUgAyAGa0F8cSEDQX9B/wFuIARB/wFxbCEHIAUgBzYCACAFIANqQQRrIAc2AgAgA0EITQRADAELIAVBBGogBzYCACAFQQhqIAc2AgAgBSADakEMayAHNgIAIAUgA2pBCGsgBzYCACADQRhNBEAMAQsgBUEMaiAHNgIAIAVBEGogBzYCACAFQRRqIAc2AgAgBUEYaiAHNgIAIAUgA2pBHGsgBzYCACAFIANqQRhrIAc2AgAgBSADakEUayAHNgIAIAUgA2pBEGsgBzYCAEEYIAVBBHFqIQYgBSAGaiEFIAMgBmshAyAHrSAHrUIghoQhCQNAIANBIE8EQCAFIAk3AwAgBUEIaiAJNwMAIAVBEGogCTcDACAFQRhqIAk3AwAgA0EgayEDIAVBIGohBQwBCwsLCwMAAQuMAQEEfyABQfD///8DIAJ2SwRAQaAGQdAGQRdBOBAAAAsgASACdCIBQQAQByEDIANBACABEB0gAEUEQEEMQQIQBxAIIQALIABBADYCACAAQQA2AgQgAEEANgIIIAAiBCADIgUgBCgCACIGRwRAIAUQCCEFIAYQHgsgBTYCACAAIAM2AgQgACABNgIIIAALKgAgAAR/IAAFQRBBERAHEAgLIAFBAhAfIQAgAEEANgIMIAAgATYCDCAACx8AIABFBEBBBEEQEAcQCCEACyAAQQBBABAgNgIAIAALUAEDfyABEAghASAARQRAQQhBEhAHEAghAAsgAEEANgIAIABBADYCBCAAIgIgASIDIAIoAgAiBEcEQCADEAghAyAEEB4LIAM2AgAgARAeIAALEABBABAhJC5BACMuECIkLwsGABAcECMLBgAQGRAkCwYAIAAQAQsGACAAEAILBAAQAwsYACAABH8gAAVBDEEUEAcQCAsgAUEAEB8LCAAgACABEAQLNgAgARAIIQEgAEUEQEEMQRMQBxAIIQALIABBkAc2AgAgAEEANgIEIAAgARAINgIIIAEQHiAACwcAIAAoAggLJgAgASAAKAIITwRAQaAHQeAHQZgBQSwQAAALIAAoAgQgAWotAAALLQAgACgCBCgCBCAAKAIEKAIIECxOBEBBfw8LIAAoAgQoAgggACgCBCgCBBAtCygAIAFBCUYEf0EBBSABQQpGCwR/QQEFIAFBDUYLBH9BAQUgAUEgRgsLSgEBfyAAKAIEKAIEIAAoAgQoAggQLEhFBEBBoAhB4AhB8ABBCBAAAAsgACgCBCgCCCAAKAIEIAAoAgQoAgQiAUEBajYCBCABEC0LGQEBfwNAIAAgABAuEC8EQCAAEDAaDAELCwsUACAARQRAQQBBCxAHEAghAAsgAAsxAQF/IAFB8P///wNLBEBBoAZB0AZBNkEqEAAACyABQQAQByECIAJBACABEB0gAhAIC0sBAn8gACIBQQBBEBAzIAEoAgAQHjYCACAAQQRBAWs2AgQgACICQQBBMBAzIAIoAggQHjYCCCAAQQQ2AgwgAEEANgIQIABBADYCFAtCACAARQRAQRhBDBAHEAghAAsgAEEANgIAIABBADYCBCAAQQA2AgggAEEANgIMIABBADYCECAAQQA2AhQgABA0IAALKgAgAAR/IAAFQRBBDRAHEAgLIAFBAhAfIQAgAEEANgIMIAAgATYCDCAAC04BAn8gAAR/IAAFQQhBChAHEAgLEDIhACAAQQA2AgAgAEEANgIEIAAiAUEAEDUgASgCABAeNgIAIAAiAkEAQQAQNiACKAIEEB42AgQgAAsGAEEAEDcLBwAgACgCDAuHDwEEfwNAIAIEfyABQQNxBUEACwRAIAAiBkEBaiEAIAYgASIGQQFqIQEgBi0AADoAACACQQFrIQIMAQsLIABBA3FBAEYEQANAIAJBEE8EQCAAIAEoAgA2AgAgAEEEaiABQQRqKAIANgIAIABBCGogAUEIaigCADYCACAAQQxqIAFBDGooAgA2AgAgAUEQaiEBIABBEGohACACQRBrIQIMAQsLIAJBCHEEQCAAIAEoAgA2AgAgAEEEaiABQQRqKAIANgIAIABBCGohACABQQhqIQELIAJBBHEEQCAAIAEoAgA2AgAgAEEEaiEAIAFBBGohAQsgAkECcQRAIAAgAS8BADsBACAAQQJqIQAgAUECaiEBCyACQQFxBEAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAACw8LIAJBIE8EQAJAAkACQAJAIABBA3EhBSAFQQFGDQAgBUECRg0BIAVBA0YNAgwDCyABKAIAIQMgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgAkEDayECA0AgAkERTwRAIAFBAWooAgAhBCAAIANBGHYgBEEIdHI2AgAgAUEFaigCACEDIABBBGogBEEYdiADQQh0cjYCACABQQlqKAIAIQQgAEEIaiADQRh2IARBCHRyNgIAIAFBDWooAgAhAyAAQQxqIARBGHYgA0EIdHI2AgAgAUEQaiEBIABBEGohACACQRBrIQIMAQsLDAILIAEoAgAhAyAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAJBAmshAgNAIAJBEk8EQCABQQJqKAIAIQQgACADQRB2IARBEHRyNgIAIAFBBmooAgAhAyAAQQRqIARBEHYgA0EQdHI2AgAgAUEKaigCACEEIABBCGogA0EQdiAEQRB0cjYCACABQQ5qKAIAIQMgAEEMaiAEQRB2IANBEHRyNgIAIAFBEGohASAAQRBqIQAgAkEQayECDAELCwwBCyABKAIAIQMgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAJBAWshAgNAIAJBE08EQCABQQNqKAIAIQQgACADQQh2IARBGHRyNgIAIAFBB2ooAgAhAyAAQQRqIARBCHYgA0EYdHI2AgAgAUELaigCACEEIABBCGogA0EIdiAEQRh0cjYCACABQQ9qKAIAIQMgAEEMaiAEQQh2IANBGHRyNgIAIAFBEGohASAAQRBqIQAgAkEQayECDAELCwwACwsgAkEQcQRAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAAAsgAkEIcQRAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAALIAJBBHEEQCAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAAIAAiBUEBaiEAIAUgASIFQQFqIQEgBS0AADoAACAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAALIAJBAnEEQCAAIgVBAWohACAFIAEiBUEBaiEBIAUtAAA6AAAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAACyACQQFxBEAgACIFQQFqIQAgBSABIgVBAWohASAFLQAAOgAACwvmAgEFfwJAIAAhBSABIQQgAiEDIAUgBEYEQAwBCyAEIANqIAVNBH9BAQUgBSADaiAETQsEQCAFIAQgAxA6DAELIAUgBEkEQCAEQQdxIAVBB3FGBEADQCAFQQdxBEAgA0UEQAwFCyADQQFrIQMgBSIHQQFqIQUgByAEIgdBAWohBCAHLQAAOgAADAELCwNAIANBCE8EQCAFIAQpAwA3AwAgA0EIayEDIAVBCGohBSAEQQhqIQQMAQsLCwNAIAMEQCAFIgdBAWohBSAHIAQiB0EBaiEEIActAAA6AAAgA0EBayEDDAELCwUgBEEHcSAFQQdxRgRAA0AgBSADakEHcQRAIANFBEAMBQsgBSADQQFrIgNqIAQgA2otAAA6AAAMAQsLA0AgA0EITwRAIANBCGshAyAFIANqIAQgA2opAwA3AwAMAQsLCwNAIAMEQCAFIANBAWsiA2ogBCADai0AADoAAAwBCwsLCwvWAQEGfyAAQQBHBH8gAEEPcUUFQQALRQRAQQBBkApBK0ECEAAACyAAQRBrIQIgAigCACEDIAIoAgRBAUZFBEBBAEGQCkEuQQ0QAAALIAAgA2ojAUYhBCABQQ9qQQ9Bf3NxIQUgASADSwRAIAQEQCABQfD///8DSwRAAAsgACAFahAGIAIgBTYCAAUgBSIGIANBAXQiByAGIAdLGyACKAIIEAchBiAGIAAgAigCDBA7IAYiAEEQayECCwUgBARAIAAgBWokASACIAU2AgALCyACIAE2AgwgAAtzAQR/IAAoAgghAyABIAMgAnZLBEAgAUHw////AyACdksEQEGgBkHgCUEOQS8QAAALIAAoAgAhBCABIAJ0IQUgBCAFEDwhBiAGIANqQQAgBSADaxAdIAYgBEcEQCAAIAY2AgAgACAGNgIECyAAIAU2AggLCz8BA38gARAIIQEgACgCDCECIAJBAWohAyAAIANBAhA9IAAoAgQgAkECdGogARAINgIAIAAgAzYCDCADIAEQHgsSACAAKAIEIAFBAnRqKAIAEAgLPwEBfyABIAAoAgxPBEBBoAdB4AlB3QBBKRAAAAsgACABED8hAiACRQRAIAIQHkHACkHgCUHhAEEnEAAACyACCxMAIAAoAgAgACgCABA5QQFrEEALPwEDfyAAQRBrKAIIIQIjMyEDIAIgAygCAE0EQANAIAIgAUYEQEEBDwsgA0EEaiACQQhsaigCBCICDQALC0EAC1QBBH8gABAIIQBBxbvyiHghASAAQQBHBEBBACECIAAQGkEBdCEDA0AgAiADSQRAIAEgACACai0AAHNBk4OACGwhASACQQFqIQIMAQsLCyABIAAQHgu/AQEGfyAAEAghACACEAghAiAAIAFBAXRqIQUgAiADQQF0aiEGIARBBE8EfyAFQQdxIAZBB3FyRQVBAAsEQAJAA0AgBSkDACAGKQMAUgRADAILIAVBCGohBSAGQQhqIQYgBEEEayEEIARBBE8NAAsLCwNAIAQiB0EBayEEIAcEQCAFLwEAIQggBi8BACEJIAggCUcEQCAIIAlrIAAQHiACEB4PCyAFQQJqIQUgBkECaiEGDAELC0EAIAAQHiACEB4LcQECfyAAEAghACABEAghASAAIAFGBEBBASAAEB4gARAeDwsgAEEARgR/QQEFIAFBAEYLBEBBACAAEB4gARAeDwsgABAaIQMgAyABEBpHBEBBACAAEB4gARAeDwsgAEEAIAFBACADEERFIAAQHiABEB4LXwEDfyABEAghASAAKAIAIAIgACgCBHFBBGxqKAIAIQMDQCADBEAgAygCCEEBcUUEfyADKAIAIAEQRQVBAAsEQCADIAEQHg8LIAMoAghBAUF/c3EhAwwBCwtBACABEB4LKgECfyABEAghASAAIAECfyABEAghAiACEEMgAhAeDAALEEZBAEcgARAeCz8BA38gARAIIQEgACgCDCECIAJBAWohAyAAIANBAhA9IAAoAgQgAkECdGogARAINgIAIAAgAzYCDCADIAEQHgugAgEMfyABQQFqIQJBACACQQRsEDMhAyACQQhsQQNtIQRBACAEQQxsEDMhBSAAKAIIIQYgBiAAKAIQQQxsaiEHIAUhCANAIAYgB0cEQCAGIQogCigCCEEBcUUEQCAIIQsgCyAKKAIANgIAIAsgCigCBDYCBAJ/IAooAgAQCCEMIAwQQyAMEB4MAAsgAXEhDCADIAxBBGxqIQ0gCyANKAIANgIIIA0gCDYCACAIQQxqIQgLIAZBDGohBgwBCwsgACILIAMiDCALKAIAIglHBEAgDBAIIQwgCRAeCyAMNgIAIAAgATYCBCAAIg0gBSIJIA0oAggiC0cEQCAJEAghCSALEB4LIAk2AgggACAENgIMIAAgACgCFDYCECADEB4gBRAeC/0BAQR/IAEQCCEBIAIQCCECAn8gARAIIQMgAxBDIAMQHgwACyEFIAAgASAFEEYhBiAGBEAgBigCBCEDIAIgA0cEQCAGIAIQCDYCBCADEB4LBSAAKAIQIAAoAgxGBEAgACAAKAIUIAAoAgxBA2xBBG1IBH8gACgCBAUgACgCBEEBdEEBcgsQSQsgACgCCBAIIQMgAyAAIAAoAhAiBEEBajYCECAEQQxsaiEGIAYgARAINgIAIAYgAhAINgIEIAAgACgCFEEBajYCFCAAKAIAIAUgACgCBHFBBGxqIQQgBiAEKAIANgIIIAQgBjYCACADEB4LIAAQCCABEB4gAhAeCzoAIAEQCCEBIAIQCCECIAAoAgAgARBHRQRAIAAoAgQgARBIGgsgACgCACABIAIQShAeIAEQHiACEB4LHwAgARAIIQEgAhAIIQIgACABIAIQSyABEB4gAhAeDwsWACABEAghASAAKAIAIAEQPhogARAeC84BAQR/IAEQCCEBIAIQCCECIAEQGkEARgR/IAAoAgAQOUEARgVBAAsEQCAAKAIAIAIQPhogARAeIAIQHg8LIAAQQSIDIgRFBH9BAAUgBEEKEEILBEAgABBBIgQiBUEKEEIEfyAFBUEAQbALQcoAQQcQAAALIAEgAhBMIAQQHgUgABBBIgQiBUUEf0EABSAFQRUQQgsEQCAAEEEiBSIGQRUQQgR/IAYFQQBBsAtBzQBBERAAAAsgAhBNIAUQHgsgBBAeCyADEB4gARAeIAIQHgsqAQJ/IAEQCCEBEDghAiAAIAEgAhBOIAAoAgAgAhA+GkEBIAIQHiABEB4LDQAgACgCBCAAKAIAawsYAQF/IAAQCCEAIAAoAgAgABBQaiAAEB4L7gIBCn8gACEDIAAgAWohBCAEIANPRQRAQQBBsA5B3wVBBhAAAAsgAUEBdEEBEAchBSAFIQYCQANAIAMgBEkEQCADLQAAIQggA0EBaiEDIAhBgAFxRQRAIAIgCEVxBEAMBAsgBiAIOwEABSAEIANGBEAMBAsgAy0AAEE/cSEJIANBAWohAyAIQeABcUHAAUYEQCAGIAhBH3FBBnQgCXI7AQAFIAQgA0YEQAwFCyADLQAAQT9xIQogA0EBaiEDIAhB8AFxQeABRgRAIAhBD3FBDHQgCUEGdHIgCnIhCAUgBCADRgRADAYLIAhBB3FBEnQgCUEMdHIgCkEGdHIgAy0AAEE/cXIhCCADQQFqIQMLIAhBgIAESQRAIAYgCDsBAAUgCEGAgARrIQggCEEKdkGAsANyIQsgCEH/B3FBgLgDciEMIAYgCyAMQRB0cjYCACAGQQJqIQYLCwsgBkECaiEGDAELCwsgBSAGIAVrEDwQCAseAQF/IAAQCCEAIAAQUSABaiACIAFrQQAQUiAAEB4LEAAgACgCCCABIAJBAWsQUwslAAJAAkACQCMyQQFrDgIBAgALAAsgACgCBCECCyAAIAEgAhBUCwYAIAAkMgsHACAAKAIMC/UCAQp/IAIQCCECIAFBAWshAyADQQBIBEBBkAcgAhAeDwsgA0UEQCAAKAIAIgQEfyAEEAgFQZAHCyACEB4PC0EAIQVBACEGQQAhBANAIAQgAUgEQCAAIARBAnRqKAIAIgggBiIJRwRAIAgQCCEIIAkQHgsgCCEGIAZBAEcEQCAFIAYQGmohBQsgBEEBaiEEDAELC0EAIQogAhAaIQsgBSALIANsakEBdEEBEAchDEEAIQQDQCAEIANIBEAgACAEQQJ0aigCACIJIAYiCEcEQCAJEAghCSAIEB4LIAkhBiAGQQBHBEAgBhAaIQkgDCAKQQF0aiAGIAlBAXQQOyAKIAlqIQoLIAsEQCAMIApBAXRqIAIgC0EBdBA7IAogC2ohCgsgBEEBaiEEDAELCyAAIANBAnRqKAIAIgggBiIERwRAIAgQCCEIIAQQHgsgCCEGIAZBAEcEQCAMIApBAXRqIAYgBhAaQQF0EDsLIAwQCCACEB4gBhAeCx0BA38gARAIIQEgACgCBCAAKAIMIAEQWCABEB4PCxsBAX8gACABEAchAyACBEAgAyACIAAQOwsgAws9AQN/QRAgAhAHIQQgACABdCEFIAVBACADEFohBiAEIAYQCDYCACAEIAY2AgQgBCAFNgIIIAQgADYCDCAEC48BAQR/IAAQMCEBIAEjKWshAiACQQlKBEAgASMra0EKaiECIAJBCkgEf0EBBSACQQ9KCwRAIAEjLGtBCmohAgsLQQJBAkEWQQAQWxAIIQMgAygCBCEEIAQgATYCACAEIAI2AgQgAyACQQBOBH8gAkEQSAVBAAtFBEBBwBFB4AhBjQJBBBAAAAsgAiEDEB4gAwtxAQR/IABB///DAE1FBEBBAEGwDkEhQQQQAAALIABB//8DSiEBQQIgAXRBARAHIQIgAUUEQCACIAA7AQAFIABBgIAEayEAIABB/wdxQYC4A3IhAyAAQQp2QYCwA3IhBCACIAQgA0EQdHI2AgALIAIQCAstAQJ/IAFBAEohAkECIAJ0QQEQByEDIAMgADsBACACBEAgAyABOwECCyADEAgLIAACQAJAAkAjMkEBaw4CAQIACwALQX8hAQsgACABEF4LewEGfyABEAghASABQQBGBEBBgAUiAiABIgNHBEAgAhAIIQIgAxAeCyACIQELIAAQGkEBdCEEIAEQGkEBdCEFIAQgBWohBiAGQQBGBEBBkAcQCCABEB4PCyAGQQEQBxAIIQcgByAAIAQQOyAHIARqIAEgBRA7IAcgARAeCycBAX8gABAIIQAgARAIIQEgAEGABSAAQQBHGyABEGAgABAeIAEQHgvqAQEGfyAAEDAhASABQfAMQQAQG0YEQEHwDA8LIAFB4A5BABAbRgRAQeAODwsgAUGAD0EAEBtGBEBBgA8PCyABQaAPQQAQG0YEQEHADw8LIAFB4A9BABAbRgRAQYAQDwsgAUGgEEEAEBtGBEBBwBAPCyABQeAQQQAQG0YEQEGAEQ8LIAFBoBFBABAbRgRAIAAQXCAAEFwhAyAAEFwhBCAAEFwhBUGAIGwgA0GAAmxqIARBEGxqIAVqEF0PC0EARQRAQYASQQEkMiABQQAQXyIGEGEiBUHgCEH/AUEEEAAAC0GQByAGEB4gBRAeC/oBAQZ/IAAQMEHwDEEAEBtGRQRAQZANQeAIQcUBQQQQAAALIAAoAgQoAgQhAUEAQQAQNiECA0BBAQRAIAAQMCEEIARBIE5FBEBB4A1B4AhBywFBBhAAAAsgBEHwDEEAEBtGBEBBASQyIAAoAgQgAUEAEFUhBSACEFdBAEYEQCAFIAIQHg8LIAIgBRBIGiACQZAHEFkgBRAeIAIQHg8FIARB4A5BABAbRgRAIAAoAgQoAgQgAUEBakoEQCACQQEkMiAAKAIEIAFBABBVIgUQSBogBRAeCyACIAAQYiIFEEgaIAAoAgQoAgQhASAFEB4LCwwBCwtBkAcgAhAeCz8BAn8gABAxIAAoAgQiASAAEGMgASgCABAeNgIAIAAQMSAAEDBB0BJBABAbRkUEQEHwEkHgCEGfAUEEEAAACws9AQJ/IAAoAgwhASABQQFIBEBB4BNB4AlBoAJBFBAAAAsgACgCBCABQQFrIgFBAnRqKAIAEAggACABNgIMCxgAIAAoAgAQOUEBSgRAIAAoAgAQZRAeCwvAAQEDfyAAEC5BwAlBABAbRwRAQQAPCyAAKAIEKAIAEAghASAAKAIEIgJBkAcgAigCABAeNgIAIAAoAgAgARBPBEAgABAwGiAAEDFBASECA0AgABAuQYAMQQAQG0cEQCACRQRAIAAQMEGgDEEAEBtGRQRAQcAMQeAIQY4BQQoQAAALBUEAIQILIAAQZCAAEH4aDAELCyAAEDBBgAxBABAbRkUEQEGgE0HgCEGVAUEGEAAACwsgACgCABBmQQEgARAeCzUBAn8gAAR/IAAFQQRBFRAHEAgLEDIhACAAQQA2AgAgACIBQQBBABAgIAEoAgAQHjYCACAACwYAQQAQaAtCAQJ/IAEQCCEBEGkhAiAAKAIAEDlBAEYEQCAAKAIAIAIQPhoFIAAgASACEE4gACgCACACED4aC0EBIAIQHiABEB4LGAAgACgCABA5QQFKBEAgACgCABBlEB4LC7wBAQN/IAAQLkGQFEEAEBtHBEBBAA8LIAAoAgQoAgAQCCEBIAAoAgQiAkGQByACKAIAEB42AgAgACgCACABEGoEQCAAEDAaIAAQMUEBIQIDQCAAEC5BsBRBABAbRwRAIAJFBEAgABAwQaAMQQAQG0ZFBEBBwAxB4AhBsAFBChAAAAsFQQAhAgsgABB+GgwBCwsgABAwQbAUQQAQG0ZFBEBB0BRB4AhBtgFBBhAAAAsLIAAoAgAQa0EBIAEQHgsrACABEAghASAABH8gAAVBBEEXEAcQCAsQMiEAIAAgARAINgIAIAEQHiAACxQBAX8gABAIIQBBACAAEG0gABAeCyoBAX8gARAIIQEgAhAIIQIgAhBuIQMgACABIAMQTiADEB4gARAeIAIQHgsxAQJ/IAAQLkHwDEEAEBtHBEBBAA8LIAAoAgAgACgCBCgCACAAEGMiARBvQQEgARAeC1oBBH8gARAIIQFBACECA0AgAiABEBpIBEAgASACEBsgABAwRkUEQEGQFSABEGEiBEHAFRBhIgVB4AhBwQJBBhAAAAsgBBAeIAUQHiACQQFqIQIMAQsLIAEQHgsfACAABH8gAAVBAUEYEAcQCAsQMiEAIAAgAToAACAACwgAQQAgABByCyABAX8gARAIIQEgAhBzIQMgACABIAMQTiADEB4gARAeC1YAIAAQLkHgBEEAEBtGBEAgACMnEHEgACgCACAAKAIEKAIAQQAQdEEBDwsgABAuQcAEQQAQG0YEQCAAIyYQcSAAKAIAIAAoAgQoAgBBARB0QQEPC0EACx8AIAAEfyAABUEIQRkQBxAICxAyIQAgACABNwMAIAALCABBACAAEHYLIAEBfyABEAghASACEHchAyAAIAEgAxBOIAMQHiABEB4LggECA38CfkIAIQRCASEFIAAQLkHgFUEAEBtGBEBCfyEFIAAQMBoLQQAhAQNAIykgABAuTAR/IAAQLiMqTAVBAAsEQCAAEDAhAyAEQgp+IAMjKWusfCEEIAFBAWohAQwBCwsgAUEASgRAIAAoAgAgACgCBCgCACAEIAV+EHhBAQ8LQQALFAAgAAR/IAAFQQBBGhAHEAgLEDILBgBBABB6Cx4BAX8gARAIIQEQeyECIAAgASACEE4gAhAeIAEQHgsrACAAEC5BgAVBABAbRgRAIAAjKBBxIAAoAgAgACgCBCgCABB8QQEPC0EAC0wBAX8gABAxIAAQZyIBBH8gAQUgABBsCyIBBH8gAQUgABBwCyIBBH8gAQUgABB1CyIBBH8gAQUgABB5CyIBBH8gAQUgABB9CyAAEDELcAEDfyABEAghASACEAghAiACQQBHBEAgACIDIAIiBCADKAIEIgVHBEAgBBAIIQQgBRAeCyAENgIEBSAAIgVBACABECsgBSgCBBAeNgIECyAAEH5BAEdFBEBBgBZB4AhB5ABBBBAAAAsgARAeIAIQHgsfAQF/A0AgACgCABA5QQBKBEAgACgCABBlEB4MAQsLC08BBH8gABAIIQBBACEBIAAiAiABIgNHBEAgAhAIIQIgAxAeCyACIQEjLyABQQAQfyMvKAIAEEEiAhAIIy8oAgAQgAEgAhAeIAAQHiABEB4LXwIDfwF+QgAQJkIAECchAyADQv////8PUQRAECgLQQAgA6cQKSEAQgAgACgCBK0QKiAAEAghASABEIEBIAEQHiIBQQoQQgR/IAEFQQBBwBZBwABBFBAAAAsQCCAAEB4LHwEBfyAAEAghACABEAghASAAIAEQRUUgABAeIAEQHgtIAQN/IAEQCCEBIAAgAQJ/IAEQCCECIAIQQyACEB4MAAsQRiEEIARFBEAgARAeQeAXQaAYQe8AQRAQAAALIAQoAgQQCCABEB4LLgEBfyABEAghASAAKAIAIAEQR0UEQEEAEAggARAeDwsgACgCACABEIQBIAEQHgscAEEABH9BAQVBAAsEf0EBBUEACwR/QQEFQQALC5UBAQF/IABBgC1JBEAgAEGAAXJBoAFGIABBCWtBDUEJa01yDwsgAEGAwABrQQpNBEBBAQ8LAkACQAJAAkACQAJAAkACQCAAIQEgAUGALUYNACABQajAAEYNASABQanAAEYNAiABQa/AAEYNAyABQd/AAEYNBCABQYDgAEYNBSABQf/9A0YNBgwHCwsLCwsLC0EBDwtBAAvaAwIEfwN+IAAQCCEAIAAQGiECIAJFBEBCACAAEB4PCyAAIQMgAy8BACEEQgEhBwNAIAQQhwEEQCADQQJqIgMvAQAhBCACQQFrIQIMAQsLIARBLUYEQCACQQFrIgJFBEBCACAAEB4PCyADQQJqIgMvAQAhBEJ/IQcFIARBK0YEQCACQQFrIgJFBEBCACAAEB4PCyADQQJqIgMvAQAhBAsLIAFFBEAgBEEwRgR/IAJBAkoFQQALBEACQAJAAkACQAJAIANBAmovAQBBIHIhBSAFQeIARg0AIAVB7wBGDQEgBUH4AEYNAgwDCyADQQRqIQMgAkECayECQQIhAQwDCyADQQRqIQMgAkECayECQQghAQwCCyADQQRqIQMgAkECayECQRAhAQwBC0EKIQELBUEKIQELBSABQQJIBH9BAQUgAUEkSgsEQEIAIAAQHg8LC0IAIQgCQANAIAIiBUEBayECIAUEQCADLwEAIQQgBEEwa0EKSQRAIARBMGshBAUgBEHBAGtBGU0EQCAEQcEAQQprayEEBSAEQeEAa0EZTQRAIARB4QBBCmtrIQQFDAULCwsgBCABTwRADAMLIAggAax+IAStfCEIIANBAmohAwwBCwsLIAcgCH4gABAeCxUBAX4gABAIIQAgACABEIgBIAAQHguvAwIHfwJ+IAAQCCEAIAEQCCEBIAAQCCECQQAhAyACIgRFBH9BAAUgBEEKEEILBH8gAUGQBxCDAQVBAAsEQCACIgRBChBCBH8gBAVBAEHAFkGcAUEkEAAACxAIIQQgBCABEIUBIQUgBUEARgRAEIYBBH9BAAVBAAsEQCAAEB4gARAeIAIQHiADEB4gBBAeIAUQHkHQGEHwGBBhIgZBkBkQYSIHQcAWQaEBQQoQAAAFIAAQHiABEB4gAhAeIAMQHiAEEB4gBRAeQdAYQfAYEGEiB0GQGRBhIgZBwBZBpwFBCBAAAAsACyAFIgcgAyIGRwRAIAcQCCEHIAYQHgsgByEDIAQQHiAFEB4FIAIiBiADIgVHBEAgBhAIIQYgBRAeCyAGIQMLIAMiBkUEf0EABSAGQRcQQgtFBEBBwBkgARBhIgZB8BkQYSIFQfAYEGEiBEGgGhBhIgdBwBZBtwFBBhAAAAsgAyIIQRcQQgR/IAgFQQBBwBZBuAFBGxAAAAsoAgAQCCEIIAhBABCJASAGEB4gBRAeIAQQHiAHEB4gCBAeIAAQHiABEB4gAhAeIAMQHg8LeAEBfyAAQaCNBkkEQCAAQeQASQRAQQFBAiAAQQpJGw8FQQRBBSAAQZDOAEkbIQFBAyABIABB6AdJGw8LAAUgAEGAreIESQRAQQZBByAAQcCEPUkbDwVBCUEKIABBgJTr3ANJGyEBQQggASAAQYDC1y9JGw8LAAsAC+4BAgd/An4DQCABQZDOAE8EQCABQZDOAG4gAUGQzgBwIQUhASAFQeQAbiEGIAVB5ABwIQdB4BsgBkECdGo1AgAhCkHgGyAHQQJ0ajUCACELIAJBBGshAiAAIAJBAXRqIAogC0IghoQ3AwAMAQsLIAFB5ABPBEAgAUHkAG4gAUHkAHAhCCEBIAJBAmshAkHgGyAIQQJ0aigCACEJIAAgAkEBdGogCTYCAAsgAUEKTwRAIAJBAmshAkHgGyABQQJ0aigCACEJIAAgAkEBdGogCTYCAAUgAkEBayECQTAgAWohCSAAIAJBAXRqIAk7AQALC7MBAQF/IABCgICapuqv4wFUBEAgAEKAoJSljR1UBEBBC0EMIABCgNDbw/QCVBshAUEKIAEgAEKAyK+gJVQbDwVBDkEPIABCgIDpg7HeFlQbIQFBDSABIABCgMDK84SjAlQbDwsABSAAQoCAqOyFr9GxAVQEQEEQQREgAEKAgIT+pt7hEVQbDwVBE0EUIABCgICgz8jgyOOKf1QbIQFBEiABIABCgICQu7rWrfANVBsPCwALAAvaAQIIfwN+A0AgAUKAwtcvWgRAIAFCgMLXL4AhCyABIAtCgMLXL359pyEEIAshASAEQZDOAG4hBSAEQZDOAHAhBiAFQeQAbiEHIAVB5ABwIQggBkHkAG4hCSAGQeQAcCEKQeAbIAlBAnRqNQIAIQxB4BsgCkECdGo1AgAhDSACQQRrIQIgACACQQF0aiAMIA1CIIaENwMAQeAbIAdBAnRqNQIAIQxB4BsgCEECdGo1AgAhDSACQQRrIQIgACACQQF0aiAMIA1CIIaENwMADAELCyAAIAGnIAIQjAELXwIGfwF+IABCAFJFBEBBoAUPCyAAQv////8PWARAIACnIQIgAhCLASEDIANBAXRBARAHIQEgASACIAMQjAEFIAAQjQEhAyADQQF0QQEQByEBIAEgACADEI4BCyABEAgLCAAgABCPAQ8LBwAgABCQAQsGACAAEAgLuQEBBX8gABAIIQAgACECIAIgAEEQaygCDGohAyABQQBHIQQCQANAIAIgA0kEQCACLwEAIQYgBkGAAUkEQCABIAZFcQRADAQLIARBAWohBAUgBkGAEEkEQCAEQQJqIQQFIAZBgPgDcUGAsANGBH8gAkECaiADSQVBAAsEQCACLwECQYD4A3FBgLgDRgRAIARBBGohBCACQQRqIQIMBQsLIARBA2ohBAsLIAJBAmohAgwBCwsLIAQgABAeC7IDAQ1/IAAQCCEAIAAhAiAAIABBEGsoAgxqIQMgACABEJMBIQQgBEEAEAchBSAFIARqIAFBAEdrIQYgBSEHA0AgByAGSQRAIAIvAQAhCSAJQYABSQRAIAcgCToAACAHQQFqIQcFIAlBgBBJBEAgCUEGdkHAAXIhCiAJQT9xQYABciELIAcgC0EIdCAKcjsBACAHQQJqIQcFIAlBgPgDcUGAsANGBH8gAkECaiADSQVBAAsEQCACLwECIQsgC0GA+ANxQYC4A0YEQEGAgAQgCUH/B3FBCnRqIAtB/wdxciEJIAlBEnZB8AFyIQogCUEMdkE/cUGAAXIhDCAJQQZ2QT9xQYABciENIAlBP3FBgAFyIQ4gByAOQRh0IA1BEHRyIAxBCHRyIApyNgIAIAdBBGohByACQQRqIQIMBQsLIAlBDHZB4AFyIQsgCUEGdkE/cUGAAXIhDiAJQT9xQYABciENIAcgDkEIdCALcjsBACAHIA06AAIgB0EDaiEHCwsgAkECaiECDAELCyACIANNRQRAQQBBsA5BzwVBBhAAAAsgAQRAIAdBADoAAAsgBRAIIAAQHgsbAQJ/IAAQCCEAIAAgARCUASICIAIQHiAAEB4LNAEDfyAAEAghACAAQQEQkwFBAWshAUEAIAEQKSECIAIoAgQgAEEAEJUBIAEQOyACIAAQHgs6AQN/IAAQCCEAQQAhASAAEJIBIQIgAhCWASABEB4hASACEB4gASgCCKwgASgCBK0QBSABEB4gABAeCx0BAn9BsBsgABCRASIBEGEiAhCXASABEB4gAhAeCxgBAX8QggEhACAAQbAXEIoBEJgBIAAQHgsGABAUECULC/EeSQBBEAsYCAAAAAEAAAABAAAACAAAADoAbABlAG4AAEEwCxwMAAAAAQAAAAEAAAAMAAAAOgBmAHIAbwBuAHQAAEHQAAsaCgAAAAEAAAABAAAACgAAADoAYgBhAGMAawAAQfAACxQEAAAAAQAAAAEAAAAEAAAAOgA6AABBkAELOioAAAABAAAAAQAAACoAAABiAGwAbwBjAGsAXwBpAG4AZABlAHgAXwBzAGUAZQBkAGUAZABfAGEAdAAAQdABCzIiAAAAAQAAAAEAAAAiAAAAcgBhAG4AZABvAG0AXwBiAHUAZgBmAGUAcgBfAGsAZQB5AABBkAILPi4AAAABAAAAAQAAAC4AAAByAGEAbgBkAG8AbQBfAGIAdQBmAGYAZQByAF8AaQBuAGQAZQB4AF8AawBlAHkAAEHQAgsSAgAAAAEAAAABAAAAAgAAAD0AAEHwAguQAYAAAAABAAAAAQAAAIAAAABBAEIAQwBEAEUARgBHAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAYQBiAGMAZABlAGYAZwBoAGkAagBrAGwAbQBuAG8AcABxAHIAcwB0AHUAdgB3AHgAeQB6ADAAMQAyADMANAA1ADYANwA4ADkAKwAvAABBgAQLMCAAAAABAAAAAQAAACAAAAAwADEAMgAzADQANQA2ADcAOAA5AGEAYgBjAGQAZQBmAABBsAQLGAgAAAABAAAAAQAAAAgAAAB0AHIAdQBlAABB0AQLGgoAAAABAAAAAQAAAAoAAABmAGEAbABzAGUAAEHwBAsYCAAAAAEAAAABAAAACAAAAG4AdQBsAGwAAEGQBQsSAgAAAAEAAAABAAAAAgAAADAAAEGwBQsSAgAAAAEAAAABAAAAAgAAADkAAEHQBQsSAgAAAAEAAAABAAAAAgAAAEEAAEHwBQsSAgAAAAEAAAABAAAAAgAAAGEAAEGQBgssHAAAAAEAAAABAAAAHAAAAEkAbgB2AGEAbABpAGQAIABsAGUAbgBnAHQAaAAAQcAGCzYmAAAAAQAAAAEAAAAmAAAAfgBsAGkAYgAvAGEAcgByAGEAeQBiAHUAZgBmAGUAcgAuAHQAcwAAQYAHCxAAAAAAAQAAAAEAAAAAAAAAAEGQBws0JAAAAAEAAAABAAAAJAAAAEkAbgBkAGUAeAAgAG8AdQB0ACAAbwBmACAAcgBhAG4AZwBlAABB0AcLNCQAAAABAAAAAQAAACQAAAB+AGwAaQBiAC8AdAB5AHAAZQBkAGEAcgByAGEAeQAuAHQAcwAAQZAICzgoAAAAAQAAAAEAAAAoAAAAVQBuAGUAeABwAGUAYwB0AGUAZAAgAGkAbgBwAHUAdAAgAGUAbgBkAABB0AgLVkYAAAABAAAAAQAAAEYAAAB+AGwAaQBiAC8AYQBzAHMAZQBtAGIAbAB5AHMAYwByAGkAcAB0AC0AagBzAG8AbgAvAGQAZQBjAG8AZABlAHIALgB0AHMAAEGwCQsSAgAAAAEAAAABAAAAAgAAAHsAAEHQCQsqGgAAAAEAAAABAAAAGgAAAH4AbABpAGIALwBhAHIAcgBhAHkALgB0AHMAAEGACgsuHgAAAAEAAAABAAAAHgAAAH4AbABpAGIALwByAHQALwBzAHQAdQBiAC4AdABzAABBsAoLbl4AAAABAAAAAQAAAF4AAABFAGwAZQBtAGUAbgB0ACAAdAB5AHAAZQAgAG0AdQBzAHQAIABiAGUAIABuAHUAbABsAGEAYgBsAGUAIABpAGYAIABhAHIAcgBhAHkAIABpAHMAIABoAG8AbABlAHkAAEGgCwtQQAAAAAEAAAABAAAAQAAAAH4AbABpAGIALwBhAHMAcwBlAG0AYgBsAHkAcwBjAHIAaQBwAHQALQBqAHMAbwBuAC8ASgBTAE8ATgAuAHQAcwAAQfALCxICAAAAAQAAAAEAAAACAAAAfQAAQZAMCxICAAAAAQAAAAEAAAACAAAALAAAQbAMCygYAAAAAQAAAAEAAAAYAAAARQB4AHAAZQBjAHQAZQBkACAAJwAsACcAAEHgDAsSAgAAAAEAAAABAAAAAgAAACIAAEGADQtKOgAAAAEAAAABAAAAOgAAAEUAeABwAGUAYwB0AGUAZAAgAGQAbwB1AGIAbABlAC0AcQB1AG8AdABlAGQAIABzAHQAcgBpAG4AZwAAQdANC0g4AAAAAQAAAAEAAAA4AAAAVQBuAGUAeABwAGUAYwB0AGUAZAAgAGMAbwBuAHQAcgBvAGwAIABjAGgAYQByAGEAYwB0AGUAcgAAQaAOCywcAAAAAQAAAAEAAAAcAAAAfgBsAGkAYgAvAHMAdAByAGkAbgBnAC4AdABzAABB0A4LEgIAAAABAAAAAQAAAAIAAABcAABB8A4LEgIAAAABAAAAAQAAAAIAAAAvAABBkA8LEgIAAAABAAAAAQAAAAIAAABiAABBsA8LEgIAAAABAAAAAQAAAAIAAAAIAABB0A8LEgIAAAABAAAAAQAAAAIAAABuAABB8A8LEgIAAAABAAAAAQAAAAIAAAAKAABBkBALEgIAAAABAAAAAQAAAAIAAAByAABBsBALEgIAAAABAAAAAQAAAAIAAAANAABB0BALEgIAAAABAAAAAQAAAAIAAAB0AABB8BALEgIAAAABAAAAAQAAAAIAAAAJAABBkBELEgIAAAABAAAAAQAAAAIAAAB1AABBsBELNiYAAAABAAAAAQAAACYAAABVAG4AZQB4AHAAZQBjAHQAZQBkACAAXAB1ACAAZABpAGcAaQB0AABB8BELTDwAAAABAAAAAQAAADwAAABVAG4AZQB4AHAAZQBjAHQAZQBkACAAZQBzAGMAYQBwAGUAZAAgAGMAaABhAHIAYQBjAHQAZQByADoAIAAAQcASCxICAAAAAQAAAAEAAAACAAAAOgAAQeASCygYAAAAAQAAAAEAAAAYAAAARQB4AHAAZQBjAHQAZQBkACAAJwA6ACcAAEGQEwtAMAAAAAEAAAABAAAAMAAAAFUAbgBlAHgAcABlAGMAdABlAGQAIABlAG4AZAAgAG8AZgAgAG8AYgBqAGUAYwB0AABB0BMLLBwAAAABAAAAAQAAABwAAABBAHIAcgBhAHkAIABpAHMAIABlAG0AcAB0AHkAAEGAFAsSAgAAAAEAAAABAAAAAgAAAFsAAEGgFAsSAgAAAAEAAAABAAAAAgAAAF0AAEHAFAs+LgAAAAEAAAABAAAALgAAAFUAbgBlAHgAcABlAGMAdABlAGQAIABlAG4AZAAgAG8AZgAgAGEAcgByAGEAeQAAQYAVCyQUAAAAAQAAAAEAAAAUAAAARQB4AHAAZQBjAHQAZQBkACAAJwAAQbAVCxICAAAAAQAAAAEAAAACAAAAJwAAQdAVCxICAAAAAQAAAAEAAAACAAAALQAAQfAVCzIiAAAAAQAAAAEAAAAiAAAAQwBhAG4AbgBvAHQAIABwAGEAcgBzAGUAIABKAFMATwBOAABBsBYLaFgAAAABAAAAAQAAAFgAAABuAG8AZABlAF8AbQBvAGQAdQBsAGUAcwAvAG4AZQBhAHIALQBzAGQAawAtAGEAcwAvAGEAcwBzAGUAbQBiAGwAeQAvAGIAaQBuAGQAZwBlAG4ALgB0AHMAAEGgFwsiEgAAAAEAAAABAAAAEgAAAHQAaQBtAGUAcwB0AGEAbQBwAABB0BcLNCQAAAABAAAAAQAAACQAAABLAGUAeQAgAGQAbwBlAHMAIABuAG8AdAAgAGUAeABpAHMAdAAAQZAYCyYWAAAAAQAAAAEAAAAWAAAAfgBsAGkAYgAvAG0AYQBwAC4AdABzAABBwBgLGgoAAAABAAAAAQAAAAoAAAB0AHkAcABlACAAAEHgGAsWBgAAAAEAAAABAAAABgAAAHUANgA0AABBgBkLMCAAAAABAAAAAQAAACAAAAAgAGMAYQBuAG4AbwB0ACAAYgBlACAAbgB1AGwAbAAuAABBsBkLMCAAAAABAAAAAQAAACAAAABWAGEAbAB1AGUAIAB3AGkAdABoACAASwBlAHkAOgAgAABB4BkLJhYAAAABAAAAAQAAABYAAAAgAHcAaQB0AGgAIAB0AHkAcABlACAAAEGQGguOAX4AAAABAAAAAQAAAH4AAAAgAGkAcwAgAGEAbgAgADYANAAtAGIAaQB0ACAAaQBuAHQAZQBnAGUAcgAgAGEAbgBkACAAaQBzACAAZQB4AHAAZQBjAHQAZQBkACAAdABvACAAYgBlACAAZQBuAGMAbwBkAGUAZAAgAGEAcwAgAGEAIABzAHQAcgBpAG4AZwAAQaAbCyYWAAAAAQAAAAEAAAAWAAAAaABlAGEAcgB0AGIAZQBhAHQAOgAgAABB0BsLoAOQAQAAAQAAABsAAACQAQAAMAAwADAAMQAwADIAMAAzADAANAAwADUAMAA2ADAANwAwADgAMAA5ADEAMAAxADEAMQAyADEAMwAxADQAMQA1ADEANgAxADcAMQA4ADEAOQAyADAAMgAxADIAMgAyADMAMgA0ADIANQAyADYAMgA3ADIAOAAyADkAMwAwADMAMQAzADIAMwAzADMANAAzADUAMwA2ADMANwAzADgAMwA5ADQAMAA0ADEANAAyADQAMwA0ADQANAA1ADQANgA0ADcANAA4ADQAOQA1ADAANQAxADUAMgA1ADMANQA0ADUANQA1ADYANQA3ADUAOAA1ADkANgAwADYAMQA2ADIANgAzADYANAA2ADUANgA2ADYANwA2ADgANgA5ADcAMAA3ADEANwAyADcAMwA3ADQANwA1ADcANgA3ADcANwA4ADcAOQA4ADAAOAAxADgAMgA4ADMAOAA0ADgANQA4ADYAOAA3ADgAOAA4ADkAOQAwADkAMQA5ADIAOQAzADkANAA5ADUAOQA2ADkANwA5ADgAOQA5AABB8B4L5AEcAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAEAAAAAcAAAAQAAAAAAAAABAAAAAHAAAAEAAAAAcAAAAQAAAACwAAABAAAAAAAAAAmCBBAAAAAACTIAAAAgAAABAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAJMgAAACAAAAEAAAAAAAAAAQAAAAAAAAADEAAAACAAAAEAAAAAsAAACTBAAAAgAAABAAAAALAAAAEAAAAAsAAAAQAAAACwAAABAAAAALAAAAEAAAAAAAAAAAhT8EbmFtZQH9PpsBABN+bGliL2J1aWx0aW5zL2Fib3J0AT9ub2RlX21vZHVsZXMvbmVhci1zZGstYXMvYXNzZW1ibHkvcnVudGltZS9lbnYvaW1wb3J0cy9lbnYuaW5wdXQCRm5vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9ydW50aW1lL2Vudi9pbXBvcnRzL2Vudi5yZWdpc3Rlcl9sZW4DP25vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9ydW50aW1lL2Vudi9pbXBvcnRzL2Vudi5wYW5pYwRHbm9kZV9tb2R1bGVzL25lYXItc2RrLWFzL2Fzc2VtYmx5L3J1bnRpbWUvZW52L2ltcG9ydHMvZW52LnJlYWRfcmVnaXN0ZXIFMX5saWIvbmVhci1zZGstYXMvcnVudGltZS9lbnYvaW1wb3J0cy9lbnYubG9nX3V0ZjgGHH5saWIvcnQvc3R1Yi9tYXliZUdyb3dNZW1vcnkHFH5saWIvcnQvc3R1Yi9fX2FsbG9jCBV+bGliL3J0L3N0dWIvX19yZXRhaW4JNH5saWIvbmVhci1zZGstYXMvcnVudGltZS9zdG9yYWdlL1N0b3JhZ2UjY29uc3RydWN0b3IKJnN0YXJ0On5saWIvbmVhci1zZGstYXMvcnVudGltZS9zdG9yYWdlCzV+bGliL25lYXItc2RrLWFzL3J1bnRpbWUvY29udHJhY3QvQ29udGV4dCNjb25zdHJ1Y3Rvcgwnc3RhcnQ6fmxpYi9uZWFyLXNkay1hcy9ydW50aW1lL2NvbnRyYWN0DSRzdGFydDp+bGliL25lYXItc2RrLWFzL3J1bnRpbWUvaW5kZXgOMn5saWIvbmVhci1zZGstYXMvdm0vb3V0Y29tZS9SZXR1cm5EYXRhI2NvbnN0cnVjdG9yDyx+bGliL25lYXItc2RrLWFzL3ZtL291dGNvbWUvTm9uZSNjb25zdHJ1Y3RvchAhc3RhcnQ6fmxpYi9uZWFyLXNkay1hcy92bS9vdXRjb21lERxzdGFydDp+bGliL25lYXItc2RrLWFzL3ZtL3ZtEh9zdGFydDp+bGliL25lYXItc2RrLWFzL3ZtL2luZGV4ExxzdGFydDp+bGliL25lYXItc2RrLWFzL2luZGV4FBNzdGFydDphc3NlbWJseS9tYWluFUVub2RlX21vZHVsZXMvbmVhci1zZGstYXMvYXNzZW1ibHkvcnVudGltZS9zdG9yYWdlL1N0b3JhZ2UjY29uc3RydWN0b3IWN3N0YXJ0Om5vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9ydW50aW1lL3N0b3JhZ2UXRm5vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9ydW50aW1lL2NvbnRyYWN0L0NvbnRleHQjY29uc3RydWN0b3IYOHN0YXJ0Om5vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9ydW50aW1lL2NvbnRyYWN0GTVzdGFydDpub2RlX21vZHVsZXMvbmVhci1zZGstYXMvYXNzZW1ibHkvcnVudGltZS9pbmRleBodfmxpYi9zdHJpbmcvU3RyaW5nI2dldDpsZW5ndGgbHX5saWIvc3RyaW5nL1N0cmluZyNjaGFyQ29kZUF0HCZzdGFydDp+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vZGVjb2Rlch0XfmxpYi9tZW1vcnkvbWVtb3J5LmZpbGweFn5saWIvcnQvc3R1Yi9fX3JlbGVhc2UfLH5saWIvYXJyYXlidWZmZXIvQXJyYXlCdWZmZXJWaWV3I2NvbnN0cnVjdG9yIEZ+bGliL2FycmF5L0FycmF5PH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWU+I2NvbnN0cnVjdG9yITF+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyI2NvbnN0cnVjdG9yIl9+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vZGVjb2Rlci9KU09ORGVjb2Rlcjx+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyPiNjb25zdHJ1Y3RvciMjc3RhcnQ6fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04kJHN0YXJ0On5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9pbmRleCUvc3RhcnQ6bm9kZV9tb2R1bGVzL25lYXItc2RrLWFzL2Fzc2VtYmx5L2JpbmRnZW4mL25vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9iaW5kZ2VuL2lucHV0JzZub2RlX21vZHVsZXMvbmVhci1zZGstYXMvYXNzZW1ibHkvYmluZGdlbi9yZWdpc3Rlcl9sZW4oL25vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9iaW5kZ2VuL3BhbmljKSZ+bGliL3R5cGVkYXJyYXkvVWludDhBcnJheSNjb25zdHJ1Y3Rvcio3bm9kZV9tb2R1bGVzL25lYXItc2RrLWFzL2Fzc2VtYmx5L2JpbmRnZW4vcmVhZF9yZWdpc3Rlcis5fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvRGVjb2RlclN0YXRlI2NvbnN0cnVjdG9yLCV+bGliL3R5cGVkYXJyYXkvVWludDhBcnJheSNnZXQ6bGVuZ3RoLSB+bGliL3R5cGVkYXJyYXkvVWludDhBcnJheSNfX2dldC5cfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcGVla0NoYXIvYH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I2lzV2hpdGVzcGFjZTBcfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcmVhZENoYXIxYn5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I3NraXBXaGl0ZXNwYWNlMjR+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLlZhbHVlI2NvbnN0cnVjdG9yMyh+bGliL2FycmF5YnVmZmVyL0FycmF5QnVmZmVyI2NvbnN0cnVjdG9yNE9+bGliL21hcC9NYXA8fmxpYi9zdHJpbmcvU3RyaW5nLH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWU+I2NsZWFyNVV+bGliL21hcC9NYXA8fmxpYi9zdHJpbmcvU3RyaW5nLH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWU+I2NvbnN0cnVjdG9yNjB+bGliL2FycmF5L0FycmF5PH5saWIvc3RyaW5nL1N0cmluZz4jY29uc3RydWN0b3I3Mn5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uT2JqI2NvbnN0cnVjdG9yOC9+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLlZhbHVlLk9iamVjdDlFfmxpYi9hcnJheS9BcnJheTx+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLlZhbHVlPiNnZXQ6bGVuZ3RoOhd+bGliL3V0aWwvbWVtb3J5L21lbWNweTsXfmxpYi9tZW1vcnkvbWVtb3J5LmNvcHk8Fn5saWIvcnQvc3R1Yi9fX3JlYWxsb2M9FX5saWIvYXJyYXkvZW5zdXJlU2l6ZT4/fmxpYi9hcnJheS9BcnJheTx+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLlZhbHVlPiNwdXNoP0p+bGliL2FycmF5L0FycmF5PH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWU+I19fdW5jaGVja2VkX2dldEBAfmxpYi9hcnJheS9BcnJheTx+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLlZhbHVlPiNfX2dldEEufmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlciNnZXQ6cGVla0IUfmxpYi9ydC9fX2luc3RhbmNlb2ZDFn5saWIvdXRpbC9oYXNoL2hhc2hTdHJEHH5saWIvdXRpbC9zdHJpbmcvY29tcGFyZUltcGxFF35saWIvc3RyaW5nL1N0cmluZy5fX2VxRk5+bGliL21hcC9NYXA8fmxpYi9zdHJpbmcvU3RyaW5nLH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWU+I2ZpbmRHTX5saWIvbWFwL01hcDx+bGliL3N0cmluZy9TdHJpbmcsfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSlNPTi5WYWx1ZT4jaGFzSCl+bGliL2FycmF5L0FycmF5PH5saWIvc3RyaW5nL1N0cmluZz4jcHVzaElQfmxpYi9tYXAvTWFwPH5saWIvc3RyaW5nL1N0cmluZyx+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLlZhbHVlPiNyZWhhc2hKTX5saWIvbWFwL01hcDx+bGliL3N0cmluZy9TdHJpbmcsfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSlNPTi5WYWx1ZT4jc2V0Syt+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLk9iaiNfc2V0TFR+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLk9iaiNzZXQ8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSlNPTi5WYWx1ZT5NK35saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uQXJyI3B1c2hOLn5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXIjYWRkVmFsdWVPMH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXIjcHVzaE9iamVjdFAvfmxpYi9hcnJheWJ1ZmZlci9BcnJheUJ1ZmZlclZpZXcjZ2V0OmJ5dGVPZmZzZXRRNX5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi91dGlsL2luZGV4L0J1ZmZlci5nZXREYXRhUHRyUiR+bGliL3N0cmluZy9TdHJpbmcuVVRGOC5kZWNvZGVVbnNhZmVTNX5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi91dGlsL2luZGV4L0J1ZmZlci5yZWFkU3RyaW5nVDh+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vZGVjb2Rlci9EZWNvZGVyU3RhdGUjcmVhZFN0cmluZ1VDfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvRGVjb2RlclN0YXRlI3JlYWRTdHJpbmd8dHJhbXBvbGluZVYTfnNldEFyZ3VtZW50c0xlbmd0aFcvfmxpYi9hcnJheS9BcnJheTx+bGliL3N0cmluZy9TdHJpbmc+I2dldDpsZW5ndGhYIH5saWIvdXRpbC9zdHJpbmcvam9pblN0cmluZ0FycmF5WSl+bGliL2FycmF5L0FycmF5PH5saWIvc3RyaW5nL1N0cmluZz4jam9pbloVfmxpYi9ydC9fX2FsbG9jQnVmZmVyWxR+bGliL3J0L19fYWxsb2NBcnJheVxgfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcmVhZEhleERpZ2l0XSB+bGliL3N0cmluZy9TdHJpbmcuZnJvbUNvZGVQb2ludF4ffmxpYi9zdHJpbmcvU3RyaW5nLmZyb21DaGFyQ29kZV8qfmxpYi9zdHJpbmcvU3RyaW5nLmZyb21DaGFyQ29kZXx0cmFtcG9saW5lYBl+bGliL3N0cmluZy9TdHJpbmcjY29uY2F0YRt+bGliL3N0cmluZy9TdHJpbmcuX19jb25jYXRiY35saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I3JlYWRFc2NhcGVkQ2hhcmNefmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcmVhZFN0cmluZ2RcfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcGFyc2VLZXllPn5saWIvYXJyYXkvQXJyYXk8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSlNPTi5WYWx1ZT4jcG9wZi9+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyI3BvcE9iamVjdGdffmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcGFyc2VPYmplY3RoMn5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uQXJyI2NvbnN0cnVjdG9yaS5+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLlZhbHVlLkFycmF5ai9+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyI3B1c2hBcnJheWsufmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlciNwb3BBcnJheWxefmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcGFyc2VBcnJheW0yfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSlNPTi5TdHIjY29uc3RydWN0b3JuL35saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWUuU3RyaW5nby9+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyI3NldFN0cmluZ3BffmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL2RlY29kZXIvSlNPTkRlY29kZXI8fmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlcj4jcGFyc2VTdHJpbmdxYX5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I3JlYWRBbmRBc3NlcnRyM35saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uQm9vbCNjb25zdHJ1Y3RvcnMtfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSlNPTi5WYWx1ZS5Cb29sdDB+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyI3NldEJvb2xlYW51YH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I3BhcnNlQm9vbGVhbnYyfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSlNPTi5OdW0jY29uc3RydWN0b3J3L35saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWUuTnVtYmVyeDB+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyI3NldEludGVnZXJ5X35saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I3BhcnNlTnVtYmVyejN+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9KU09OLk51bGwjY29uc3RydWN0b3J7LX5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWUuTnVsbHwtfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlciNzZXROdWxsfV1+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vZGVjb2Rlci9KU09ORGVjb2Rlcjx+bGliL2Fzc2VtYmx5c2NyaXB0LWpzb24vSlNPTi9IYW5kbGVyPiNwYXJzZU51bGx+Xn5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I3BhcnNlVmFsdWV/X35saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9kZWNvZGVyL0pTT05EZWNvZGVyPH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0hhbmRsZXI+I2Rlc2VyaWFsaXplgAErfmxpYi9hc3NlbWJseXNjcmlwdC1qc29uL0pTT04vSGFuZGxlciNyZXNldIEBRX5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL19KU09OLnBhcnNlPH5saWIvdHlwZWRhcnJheS9VaW50OEFycmF5PoIBMm5vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9iaW5kZ2VuL2dldElucHV0gwEXfmxpYi9zdHJpbmcvU3RyaW5nLl9fbmWEAU1+bGliL21hcC9NYXA8fmxpYi9zdHJpbmcvU3RyaW5nLH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uVmFsdWU+I2dldIUBKn5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uT2JqI2dldIYBP25vZGVfbW9kdWxlcy9uZWFyLXNkay1hcy9hc3NlbWJseS9iaW5kZ2VuL2lzUmVhbGx5TnVsbGFibGU8dTY0PocBGH5saWIvdXRpbC9zdHJpbmcvaXNTcGFjZYgBHH5saWIvdXRpbC9zdHJpbmcvc3RydG9sPGk2ND6JARh+bGliL251bWJlci9VNjQucGFyc2VJbnSKAVxub2RlX21vZHVsZXMvbmVhci1zZGstYXMvYXNzZW1ibHkvYmluZGdlbi9kZWNvZGU8dTY0LH5saWIvYXNzZW1ibHlzY3JpcHQtanNvbi9KU09OL0pTT04uT2JqPosBH35saWIvdXRpbC9udW1iZXIvZGVjaW1hbENvdW50MzKMARt+bGliL3V0aWwvbnVtYmVyL3V0b2EzMl9sdXSNAR9+bGliL3V0aWwvbnVtYmVyL2RlY2ltYWxDb3VudDY0jgEbfmxpYi91dGlsL251bWJlci91dG9hNjRfbHV0jwEXfmxpYi91dGlsL251bWJlci91dG9hNjSQARp+bGliL3V0aWwvbnVtYmVyL2l0b2E8dTY0PpEBGH5saWIvbnVtYmVyL1U2NCN0b1N0cmluZ5IBG35saWIvc3RyaW5nL1N0cmluZyN0b1N0cmluZ5MBIn5saWIvc3RyaW5nL1N0cmluZy5VVEY4LmJ5dGVMZW5ndGiUAR5+bGliL3N0cmluZy9TdHJpbmcuVVRGOC5lbmNvZGWVASl+bGliL25lYXItc2RrLWFzL3J1bnRpbWUvdXRpbC91dGlsLnRvVVRGOJYBMH5saWIvbmVhci1zZGstYXMvcnVudGltZS91dGlsL3V0aWwuc3RyaW5nVG9CeXRlc5cBQH5saWIvbmVhci1zZGstYXMvcnVudGltZS9sb2dnaW5nL2xvZ2dpbmcubG9nPH5saWIvc3RyaW5nL1N0cmluZz6YARdhc3NlbWJseS9tYWluL2hlYXJ0YmVhdJkBIWFzc2VtYmx5L21haW4vX193cmFwcGVyX2hlYXJ0YmVhdJoBBn5zdGFydA==" + } + }, + { + "AccessKey": { + "account_id": "01.near", + "public_key": "ed25519:6GxYiNnRLoKkjGeKA68hrfyrJC9tYSamGND5d23aXqRx", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "01.near", + "public_key": "ed25519:E837NUYQLFgP9cLQou3nBSYzqFFhGffhYQLVzbwL5jtY", + "access_key": { + "nonce": 1, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "alex.near", + "public_key": "ed25519:8fohZQ3DwXgVUXKJSoU9vi6iPyXKUKKff1T7sw4xj4wW", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "bo.near", + "public_key": "ed25519:C5kXZP86M3DoWjPUwYr2QXkP7RoLj1hcF3kPFyoYcC4h", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "bot.pulse.near", + "public_key": "ed25519:9x5kkFynLRojfwoVGbuZPSoRHEP5urze5xAbkybXHFBS", + "access_key": { + "nonce": 422638, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "bowen.near", + "public_key": "ed25519:5LaQTGEqGZMrSQuypgR8zS3fQJRhVLgMtjFw7qBmWb8X", + "access_key": { + "nonce": 1, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "contributors.near", + "public_key": "ed25519:BCCMGbV9FzTMTcwS67QCW1TrTmjuwFR1SrFPiG744kio", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "erik.near", + "public_key": "ed25519:8fohZQ3DwXgVUXKJSoU9vi6iPyXKUKKff1T7sw4xj4wW", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "foundation.near", + "public_key": "ed25519:GmtTh6yhWz6BmkA9AfnoQESKanDbBJDGfWVpW5wq9Uz", + "access_key": { + "nonce": 1, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "illia.near", + "public_key": "ed25519:dQMV9776YrjHYLysrpQ7abkUmqiA5XLupvveVofYnvy", + "access_key": { + "nonce": 0, + "permission": { + "FunctionCall": { + "allowance": "0", + "receiver_id": "illia.near", + "method_names": [ + "__wallet__metadata" + ] + } + } + } + } + }, + { + "AccessKey": { + "account_id": "illia.near", + "public_key": "ed25519:8fohZQ3DwXgVUXKJSoU9vi6iPyXKUKKff1T7sw4xj4wW", + "access_key": { + "nonce": 11, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "kendall.near", + "public_key": "ed25519:3Puiccgti9iExBUucUxEdbVgeecRibK5FgENQVLHTg5t", + "access_key": { + "nonce": 0, + "permission": { + "FunctionCall": { + "allowance": "0", + "receiver_id": "kendall.near", + "method_names": [ + "__wallet__metadata" + ] + } + } + } + } + }, + { + "AccessKey": { + "account_id": "kendall.near", + "public_key": "ed25519:DvabrRhC1TKXG8hWTGG2U3Ra5E4YXAF1azHdwSc61fs9", + "access_key": { + "nonce": 5, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "kendall.near", + "public_key": "ed25519:J7PuMuFm34c19f324gFSQwMkaBG1DmwPaSEVZEbZw1nX", + "access_key": { + "nonce": 0, + "permission": { + "FunctionCall": { + "allowance": "0", + "receiver_id": "kendall.near", + "method_names": [ + "__wallet__metadata" + ] + } + } + } + } + }, + { + "AccessKey": { + "account_id": "ledger.vlad.near", + "public_key": "ed25519:8g7GvgccAaub68HeSrmp6Aw2vYAvRYbLQZdEa6hZiG9X", + "access_key": { + "nonce": 1, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "ledger.vlad.near", + "public_key": "ed25519:GgK5WqBhrhdwYDUgqsjKwDpnFWad4BgpJSNfH2VPs94v", + "access_key": { + "nonce": 0, + "permission": { + "FunctionCall": { + "allowance": "0", + "receiver_id": "ledger.vlad.near", + "method_names": [ + "__wallet__metadata" + ] + } + } + } + } + }, + { + "AccessKey": { + "account_id": "mike.near", + "public_key": "ed25519:AhiKooGnQsw8S8WZ2V2xRGvpbZDY3yHFcTp4iCHYP8jo", + "access_key": { + "nonce": 1, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "mikemikemikemikemikemikemikemike", + "public_key": "ed25519:AhiKooGnQsw8S8WZ2V2xRGvpbZDY3yHFcTp4iCHYP8jo", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "near", + "public_key": "ed25519:5zset1JX4qp4PcR3N9KDSY6ATdgkrbBW5wFBGWC4ZjnU", + "access_key": { + "nonce": 8, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "nfvalidator1.near", + "public_key": "ed25519:Fd2TW6TtTDL5hiY58pbTVYfTBSNyWLgHGxiD9mcHgQ92", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "nfvalidator2.near", + "public_key": "ed25519:4rg9rmbxuSM7bX8z8989LTmBiM6JNnE4w9LZ8KkuCcfq", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "nfvalidator3.near", + "public_key": "ed25519:EVyX7KE6e2KD3CzpoN1kvzJATsS5KxkjbMCCYHbM3vRr", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "nfvalidator4.near", + "public_key": "ed25519:CrLQzMvfSDWnTYzfbEzcJ3hdetnpYdsQnvbhuzwHBtAG", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "patrick.near", + "public_key": "ed25519:8MPLjkG12V5AQfCogZhjrWe5k6PoRzNtLUb2eD1r7fyU", + "access_key": { + "nonce": 3, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "patrick.near", + "public_key": "ed25519:BHTmjrvg2UWxBjzSwDyhkc2FYJseSduWVe7YXBS2Rms1", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "peter.near", + "public_key": "ed25519:HDybq3JWgmbaiCKtE27T75iYVkEoA8cH6rfnut77ZVY1", + "access_key": { + "nonce": 1, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "pulse.near", + "public_key": "ed25519:3BWDipnJmNfWT7YSBGZu63dkfMBoZDUqWJsctNGBDinE", + "access_key": { + "nonce": 3, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "registrar", + "public_key": "ed25519:Fm9g4GQeQrnwknCVexuPvn3owgrYvMbZhPRoXKpj2wX6", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "treasury.near", + "public_key": "ed25519:CzAXM8NcumuHPYJYnjq5tUX5v92GHdbYZfmfKFwDNzBZ", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "vlad.near", + "public_key": "ed25519:2nE29FtYYZrT2owygL3FN9CLVBs9wdUy1r6pdpuScazs", + "access_key": { + "nonce": 2, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "vlad.near", + "public_key": "ed25519:4GnS8L8hnCNWh4saWPPAVxto1VFtVdmY27mkrXLeSxgp", + "access_key": { + "nonce": 1, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "vlad.near", + "public_key": "ed25519:9xLURZGus8bU4Qnf9AC3jmJhHNBo7Ydh17w7nJAY2L78", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "wallet.pulse.near", + "public_key": "ed25519:9783PHB4mZXYFopqXcypm4TCv2LoAbAdmj24AA9YJ2C6", + "access_key": { + "nonce": 2, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "wallet.pulse.near", + "public_key": "ed25519:BJ3wDgNtiMa22d8iCKmzbGA7YTiSWv9J33NTftekUcoZ", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "AccessKey": { + "account_id": "yifang.near", + "public_key": "ed25519:CKUb9VneyN1XFMXcvEc55aKiDpirdDim8Dd4cAyzefF1", + "access_key": { + "nonce": 0, + "permission": "FullAccess" + } + } + }, + { + "Data": { + "account_id": "near", + "data_key": "U1RBVEU=", + "value": "CQAAAAAAAAAAAAAAaQAAAAAAAAAACQAAAAAAAAAAAAAAawAAAAAAAAAACQAAAAAAAAAAAAAAdg==" + } + } + ], + "protocol_upgrade_stake_threshold": [ + 4, + 5 + ], + "protocol_upgrade_num_epochs": 2, + "max_gas_price": "10000000000000000000000", + "minimum_stake_divisor": 10 +} diff --git a/utils/mainnet-res/res/mainnet_restored_receipts.json b/utils/mainnet-res/res/mainnet_restored_receipts.json new file mode 100644 index 000000000..7b60c52ed --- /dev/null +++ b/utils/mainnet-res/res/mainnet_restored_receipts.json @@ -0,0 +1,8749 @@ +{ + "0": [ + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "7WEzUsFtzkFpVSic4vRZcG8Y19p2Y8QpwCi3NxzBHDj8", + "receipt": { + "Action": { + "signer_id": "8e41bf0bd25afc7f1a433f8697144d043ed751369086304a95c2d18294306b5b", + "signer_public_key": "ed25519:AaK3HcMvzxDCTK36f313m3tFrL2e7MCzxmttGotJ7ytE", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "Xf9QBkyAfxMhjAeFkSDR2nW8YAeRPbRAZjXxgEk15H6", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI4ZTQxYmYwYmQyNWFmYzdmMWE0MzNmODY5NzE0NGQwNDNlZDc1MTM2OTA4NjMwNGE5NWMyZDE4Mjk0MzA2YjViIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "9A9j7Zc5rCaUfRUz9uiiyz1Eqhd2LTAWfsxYPxw1NxEr", + "receipt": { + "Action": { + "signer_id": "8e41bf0bd25afc7f1a433f8697144d043ed751369086304a95c2d18294306b5b", + "signer_public_key": "ed25519:AaK3HcMvzxDCTK36f313m3tFrL2e7MCzxmttGotJ7ytE", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "Xf9QBkyAfxMhjAeFkSDR2nW8YAeRPbRAZjXxgEk15H6" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI4ZTQxYmYwYmQyNWFmYzdmMWE0MzNmODY5NzE0NGQwNDNlZDc1MTM2OTA4NjMwNGE5NWMyZDE4Mjk0MzA2YjViIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "8e41bf0bd25afc7f1a433f8697144d043ed751369086304a95c2d18294306b5b", + "receipt_id": "GM1eVjC8U6krA6uN2xxGoYBKj5UCDSAg7nMPfx5UJrzb", + "receipt": { + "Action": { + "signer_id": "8e41bf0bd25afc7f1a433f8697144d043ed751369086304a95c2d18294306b5b", + "signer_public_key": "ed25519:AaK3HcMvzxDCTK36f313m3tFrL2e7MCzxmttGotJ7ytE", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5775133625563966418790" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "FLTGRxYW3VGJCaVvPLXLDSTpezGrHFqVHxPzuVgU22mv", + "receipt": { + "Action": { + "signer_id": "f259745440ddbe754409af393195e59654b3595731d1207eebf12c1835444145", + "signer_public_key": "ed25519:HK2n48XXnW4fD66HFemhY6noaeMwzauvnK9WHk3fBiGk", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "9KjZNxSZUJ3XLbVt8YyQFDVxvBxP5hwPKkdjFDEPJ7yA", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmMjU5NzQ1NDQwZGRiZTc1NDQwOWFmMzkzMTk1ZTU5NjU0YjM1OTU3MzFkMTIwN2VlYmYxMmMxODM1NDQ0MTQ1IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "3SCbhx5E5hiESF5wJ5Nvo7gUba2ghxzHHkxvnHPH7SWT", + "receipt": { + "Action": { + "signer_id": "f259745440ddbe754409af393195e59654b3595731d1207eebf12c1835444145", + "signer_public_key": "ed25519:HK2n48XXnW4fD66HFemhY6noaeMwzauvnK9WHk3fBiGk", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "9KjZNxSZUJ3XLbVt8YyQFDVxvBxP5hwPKkdjFDEPJ7yA" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmMjU5NzQ1NDQwZGRiZTc1NDQwOWFmMzkzMTk1ZTU5NjU0YjM1OTU3MzFkMTIwN2VlYmYxMmMxODM1NDQ0MTQ1IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f259745440ddbe754409af393195e59654b3595731d1207eebf12c1835444145", + "receipt_id": "4RhWYnSzVZCJSqEM4vg1E3Ae3nj8hsbPdTyztPtHGane", + "receipt": { + "Action": { + "signer_id": "f259745440ddbe754409af393195e59654b3595731d1207eebf12c1835444145", + "signer_public_key": "ed25519:HK2n48XXnW4fD66HFemhY6noaeMwzauvnK9WHk3fBiGk", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5874383348262772861770" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "CpsvnQxCkxGG9k3ELqHyioyxjvqM3LxYobGqHmSbgP4M", + "receipt": { + "Action": { + "signer_id": "665fda562084fe42cfbb797a2ff0eb47f3b9e57a4ed070abdd7b958353194089", + "signer_public_key": "ed25519:7tdMm7iw6iutM3LrikJzfqaNp1zcsFQmYBWMgGzNR4Pr", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "DmeqnUe81ZCrdwk1mF2PLJF5MBnXf5nwq6MpmnXod9XA", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI2NjVmZGE1NjIwODRmZTQyY2ZiYjc5N2EyZmYwZWI0N2YzYjllNTdhNGVkMDcwYWJkZDdiOTU4MzUzMTk0MDg5IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "GSXVxk2HtAPeR4xumXW26Jcy5MmedxKrg5dWAyo1ytaK", + "receipt": { + "Action": { + "signer_id": "665fda562084fe42cfbb797a2ff0eb47f3b9e57a4ed070abdd7b958353194089", + "signer_public_key": "ed25519:7tdMm7iw6iutM3LrikJzfqaNp1zcsFQmYBWMgGzNR4Pr", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "DmeqnUe81ZCrdwk1mF2PLJF5MBnXf5nwq6MpmnXod9XA" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI2NjVmZGE1NjIwODRmZTQyY2ZiYjc5N2EyZmYwZWI0N2YzYjllNTdhNGVkMDcwYWJkZDdiOTU4MzUzMTk0MDg5IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "665fda562084fe42cfbb797a2ff0eb47f3b9e57a4ed070abdd7b958353194089", + "receipt_id": "5NAQ4JUWExVtHHLCBC8p5R5evAzF5P9wakSeTkmrotdi", + "receipt": { + "Action": { + "signer_id": "665fda562084fe42cfbb797a2ff0eb47f3b9e57a4ed070abdd7b958353194089", + "signer_public_key": "ed25519:7tdMm7iw6iutM3LrikJzfqaNp1zcsFQmYBWMgGzNR4Pr", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5874383348262772861770" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "BHh2UEHg4qWrQkQCBAq6CtzBBjEWQGK4RLHvb2KzvmMs", + "receipt": { + "Action": { + "signer_id": "9e1cf7a1181ddb552c4d97baf33ba788245d90cfacdf53b7ce4a1385864d7c08", + "signer_public_key": "ed25519:BeD32zXm5SFfxUkaamJ6bRNDXnP7b59DdDEYVUJzdbaw", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "EVYg66Mnw4riTVejwJigP8VtQfKExiFxG3Jbr9M8mXvC", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI5ZTFjZjdhMTE4MWRkYjU1MmM0ZDk3YmFmMzNiYTc4ODI0NWQ5MGNmYWNkZjUzYjdjZTRhMTM4NTg2NGQ3YzA4IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "8dxnRTnXaY8SCZ4BDHEyMzLgKYYRpAZRrnnohyD2bds5", + "receipt": { + "Action": { + "signer_id": "9e1cf7a1181ddb552c4d97baf33ba788245d90cfacdf53b7ce4a1385864d7c08", + "signer_public_key": "ed25519:BeD32zXm5SFfxUkaamJ6bRNDXnP7b59DdDEYVUJzdbaw", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "EVYg66Mnw4riTVejwJigP8VtQfKExiFxG3Jbr9M8mXvC" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI5ZTFjZjdhMTE4MWRkYjU1MmM0ZDk3YmFmMzNiYTc4ODI0NWQ5MGNmYWNkZjUzYjdjZTRhMTM4NTg2NGQ3YzA4IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9e1cf7a1181ddb552c4d97baf33ba788245d90cfacdf53b7ce4a1385864d7c08", + "receipt_id": "5LwXXAevURHBKXZYbuMrtkPfxzWztHhYuzWLsVeZPhrs", + "receipt": { + "Action": { + "signer_id": "9e1cf7a1181ddb552c4d97baf33ba788245d90cfacdf53b7ce4a1385864d7c08", + "signer_public_key": "ed25519:BeD32zXm5SFfxUkaamJ6bRNDXnP7b59DdDEYVUJzdbaw", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "4HuEsXREgtUCJsbb5nMR7LSsgvmAmnrtkAz4nFp5dD29", + "receipt": { + "Action": { + "signer_id": "c91e7bb8ebec8c634b415413e723c492cb0c37bea4710c9817a4aa3f42437307", + "signer_public_key": "ed25519:EY5ubZkZzojiwemgP64S5PhhpVodd1QqVKkw1bCNwTdQ", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "AUjrjwtDZEH6TNwcwKyXx87pnofZJsxCWmwCq5AAZdEG", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJjOTFlN2JiOGViZWM4YzYzNGI0MTU0MTNlNzIzYzQ5MmNiMGMzN2JlYTQ3MTBjOTgxN2E0YWEzZjQyNDM3MzA3IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "7U4xv5UuKc4YR8GEStsYH3yHYPwfwe6xDVLb7d32GRM", + "receipt": { + "Action": { + "signer_id": "c91e7bb8ebec8c634b415413e723c492cb0c37bea4710c9817a4aa3f42437307", + "signer_public_key": "ed25519:EY5ubZkZzojiwemgP64S5PhhpVodd1QqVKkw1bCNwTdQ", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "AUjrjwtDZEH6TNwcwKyXx87pnofZJsxCWmwCq5AAZdEG" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJjOTFlN2JiOGViZWM4YzYzNGI0MTU0MTNlNzIzYzQ5MmNiMGMzN2JlYTQ3MTBjOTgxN2E0YWEzZjQyNDM3MzA3IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "c91e7bb8ebec8c634b415413e723c492cb0c37bea4710c9817a4aa3f42437307", + "receipt_id": "5w6bgotpzy179yUFVjWex8Rn7kVXKLgfPBU46dubetPF", + "receipt": { + "Action": { + "signer_id": "c91e7bb8ebec8c634b415413e723c492cb0c37bea4710c9817a4aa3f42437307", + "signer_public_key": "ed25519:EY5ubZkZzojiwemgP64S5PhhpVodd1QqVKkw1bCNwTdQ", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5874383348262772861770" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "7rhZCLKSmwyGD1uHgV5cF4xSjEt6xKSPHrCbAMvaUwK2", + "receipt": { + "Action": { + "signer_id": "96868d0db4b1bf344d9758117c3ff54c24794af7687d70e01c6ae3caf6438fc3", + "signer_public_key": "ed25519:B8b9q9GwrXmQKJYq6uX2LWgVUz6JgK2neEGrDQ281Zii", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "DhPBhqt67J1U3wVGn2QqESwcqnGrzZMQyhWTNZN9NPQm", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI5Njg2OGQwZGI0YjFiZjM0NGQ5NzU4MTE3YzNmZjU0YzI0Nzk0YWY3Njg3ZDcwZTAxYzZhZTNjYWY2NDM4ZmMzIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "Bt67WMrbX1FmTX7hF7RVzRr2cXmqLcB4MtL9b1ELNksB", + "receipt": { + "Action": { + "signer_id": "96868d0db4b1bf344d9758117c3ff54c24794af7687d70e01c6ae3caf6438fc3", + "signer_public_key": "ed25519:B8b9q9GwrXmQKJYq6uX2LWgVUz6JgK2neEGrDQ281Zii", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "DhPBhqt67J1U3wVGn2QqESwcqnGrzZMQyhWTNZN9NPQm" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI5Njg2OGQwZGI0YjFiZjM0NGQ5NzU4MTE3YzNmZjU0YzI0Nzk0YWY3Njg3ZDcwZTAxYzZhZTNjYWY2NDM4ZmMzIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "96868d0db4b1bf344d9758117c3ff54c24794af7687d70e01c6ae3caf6438fc3", + "receipt_id": "65yBB2XVcuzBQxBrNy3xXooAbAVWgJFnAzD1MWVnYm5w", + "receipt": { + "Action": { + "signer_id": "96868d0db4b1bf344d9758117c3ff54c24794af7687d70e01c6ae3caf6438fc3", + "signer_public_key": "ed25519:B8b9q9GwrXmQKJYq6uX2LWgVUz6JgK2neEGrDQ281Zii", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5877805752493766187390" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "3g1mtQDFMv6rRZJrPuroQH7A5Wqobornd1tsZUB3Mt3i", + "receipt": { + "Action": { + "signer_id": "d1b4bdebb50fcf3a29707620f5143d6567a1f3d8b1a6daccab9d70b6f0f0e1fd", + "signer_public_key": "ed25519:F7c4MfBUxdhfbL3Vuck1iDEF6L8FFQhpmWxM6Y6DeZet", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "4fyMQ1ghscrgNd3kiCuyHjrJRUeSdNKiqjFQhQTcjo4U", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJkMWI0YmRlYmI1MGZjZjNhMjk3MDc2MjBmNTE0M2Q2NTY3YTFmM2Q4YjFhNmRhY2NhYjlkNzBiNmYwZjBlMWZkIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "G8YWiNSJcQX7t3SYtqAWTZzWy5RmhkbNoKzv1FigwRU6", + "receipt": { + "Action": { + "signer_id": "d1b4bdebb50fcf3a29707620f5143d6567a1f3d8b1a6daccab9d70b6f0f0e1fd", + "signer_public_key": "ed25519:F7c4MfBUxdhfbL3Vuck1iDEF6L8FFQhpmWxM6Y6DeZet", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "4fyMQ1ghscrgNd3kiCuyHjrJRUeSdNKiqjFQhQTcjo4U" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJkMWI0YmRlYmI1MGZjZjNhMjk3MDc2MjBmNTE0M2Q2NTY3YTFmM2Q4YjFhNmRhY2NhYjlkNzBiNmYwZjBlMWZkIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d1b4bdebb50fcf3a29707620f5143d6567a1f3d8b1a6daccab9d70b6f0f0e1fd", + "receipt_id": "FrhzkJ4yQVcj4UmRZRqmiuzLXv4D3zrJWXPxBY7QB2Cs", + "receipt": { + "Action": { + "signer_id": "d1b4bdebb50fcf3a29707620f5143d6567a1f3d8b1a6daccab9d70b6f0f0e1fd", + "signer_public_key": "ed25519:F7c4MfBUxdhfbL3Vuck1iDEF6L8FFQhpmWxM6Y6DeZet", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "9B7ovtuQQYbWzCgguLZRUhL5EAP6MQmXwHHXSUbLByCe", + "receipt": { + "Action": { + "signer_id": "aded10ace49471f43ea1b70766a5500d0366155a3fae62352462443abd8d2e73", + "signer_public_key": "ed25519:ChwCDLZr4f91AcaajQpxH2oigwZRLHDtoHfhhaQrtzHg", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "8i7ytmYEHWFowyeMkDAvjZzskbqomGNt6rVjifBtA4cx", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJhZGVkMTBhY2U0OTQ3MWY0M2VhMWI3MDc2NmE1NTAwZDAzNjYxNTVhM2ZhZTYyMzUyNDYyNDQzYWJkOGQyZTczIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "3zUPNxLXpUSt9EBBdNSKe8UfYcqgtQoaqtvr7E1tNywR", + "receipt": { + "Action": { + "signer_id": "aded10ace49471f43ea1b70766a5500d0366155a3fae62352462443abd8d2e73", + "signer_public_key": "ed25519:ChwCDLZr4f91AcaajQpxH2oigwZRLHDtoHfhhaQrtzHg", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "8i7ytmYEHWFowyeMkDAvjZzskbqomGNt6rVjifBtA4cx" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJhZGVkMTBhY2U0OTQ3MWY0M2VhMWI3MDc2NmE1NTAwZDAzNjYxNTVhM2ZhZTYyMzUyNDYyNDQzYWJkOGQyZTczIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "aded10ace49471f43ea1b70766a5500d0366155a3fae62352462443abd8d2e73", + "receipt_id": "AU4GsYdN6H4b3rKyAfYYVRBfmXuvKg1jJqabM89eQqPe", + "receipt": { + "Action": { + "signer_id": "aded10ace49471f43ea1b70766a5500d0366155a3fae62352462443abd8d2e73", + "signer_public_key": "ed25519:ChwCDLZr4f91AcaajQpxH2oigwZRLHDtoHfhhaQrtzHg", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "AC3xXafUpjX8yNjPjgNV7kHeJzXjQPiT6QEgoDtpNrc8", + "receipt": { + "Action": { + "signer_id": "45e271a4672463996edbe970b4392e544c6475cbd614ba6ef970e372993391c2", + "signer_public_key": "ed25519:5hoQADcP2N4JNNDgG7pFePtMGF1UK889Baj7vHmfTpjs", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "CkyHw4HKN6mNYyNxtaxkpQrpCEeWSom3x5FAXEg92frJ", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI0NWUyNzFhNDY3MjQ2Mzk5NmVkYmU5NzBiNDM5MmU1NDRjNjQ3NWNiZDYxNGJhNmVmOTcwZTM3Mjk5MzM5MWMyIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "BDDm9G9hqWqpZyn6o3Nhvjz7uAnhcGtosFjAB9414LUG", + "receipt": { + "Action": { + "signer_id": "45e271a4672463996edbe970b4392e544c6475cbd614ba6ef970e372993391c2", + "signer_public_key": "ed25519:5hoQADcP2N4JNNDgG7pFePtMGF1UK889Baj7vHmfTpjs", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "CkyHw4HKN6mNYyNxtaxkpQrpCEeWSom3x5FAXEg92frJ" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI0NWUyNzFhNDY3MjQ2Mzk5NmVkYmU5NzBiNDM5MmU1NDRjNjQ3NWNiZDYxNGJhNmVmOTcwZTM3Mjk5MzM5MWMyIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "45e271a4672463996edbe970b4392e544c6475cbd614ba6ef970e372993391c2", + "receipt_id": "EhoE4Y2xTouoktMNfeUf4fXZUrEvq2GpA26Zvt1tsJeF", + "receipt": { + "Action": { + "signer_id": "45e271a4672463996edbe970b4392e544c6475cbd614ba6ef970e372993391c2", + "signer_public_key": "ed25519:5hoQADcP2N4JNNDgG7pFePtMGF1UK889Baj7vHmfTpjs", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "3wCj7wvNM7kn483Hz6fypRio8iPaPefnBFKKZadDgyU4", + "receipt": { + "Action": { + "signer_id": "024d0c2be073ee5c67261aee66612b12f99cab7de4c9444364db2eba18c03463", + "signer_public_key": "ed25519:9yxV7s83e85jr8kBns1SVKFmZ5bwNoSH7uy6q3xn5ML", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "8ATMMbPYVkPo2tYVUhr2fbk4JpVoUV7w3V19BRePaqDX", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiIwMjRkMGMyYmUwNzNlZTVjNjcyNjFhZWU2NjYxMmIxMmY5OWNhYjdkZTRjOTQ0NDM2NGRiMmViYTE4YzAzNDYzIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "ChJvLzqmGmtj6Y7SVPAMJyF37npQegySBmpDjahpnbV2", + "receipt": { + "Action": { + "signer_id": "024d0c2be073ee5c67261aee66612b12f99cab7de4c9444364db2eba18c03463", + "signer_public_key": "ed25519:9yxV7s83e85jr8kBns1SVKFmZ5bwNoSH7uy6q3xn5ML", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "8ATMMbPYVkPo2tYVUhr2fbk4JpVoUV7w3V19BRePaqDX" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiIwMjRkMGMyYmUwNzNlZTVjNjcyNjFhZWU2NjYxMmIxMmY5OWNhYjdkZTRjOTQ0NDM2NGRiMmViYTE4YzAzNDYzIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "024d0c2be073ee5c67261aee66612b12f99cab7de4c9444364db2eba18c03463", + "receipt_id": "GAF8NUgHZgsiDriRKTDbvpHyW8QTeGNmkS78eKZ1RnvA", + "receipt": { + "Action": { + "signer_id": "024d0c2be073ee5c67261aee66612b12f99cab7de4c9444364db2eba18c03463", + "signer_public_key": "ed25519:9yxV7s83e85jr8kBns1SVKFmZ5bwNoSH7uy6q3xn5ML", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "FkGVsfCw16FpuPmQeXrmGxNjh9ybwPWGNrkASCrsGHCP", + "receipt": { + "Action": { + "signer_id": "d7f233fc4d32717dfc6adc35dcd35b9566be1423b869b8336374c714baa48181", + "signer_public_key": "ed25519:FXxrvCFpraE6YupfxA9Gw21SWDJjMbPwjKWHXueoYLm6", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "AfZQQ3d24wjgkYm7JqtSDyfvoesvumQkCDEHeLcFatKZ", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJkN2YyMzNmYzRkMzI3MTdkZmM2YWRjMzVkY2QzNWI5NTY2YmUxNDIzYjg2OWI4MzM2Mzc0YzcxNGJhYTQ4MTgxIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "BcHYZhXM1QY4gcEkPN9bKywSPpSiNi1eU8hkTHRuoSKY", + "receipt": { + "Action": { + "signer_id": "d7f233fc4d32717dfc6adc35dcd35b9566be1423b869b8336374c714baa48181", + "signer_public_key": "ed25519:FXxrvCFpraE6YupfxA9Gw21SWDJjMbPwjKWHXueoYLm6", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "AfZQQ3d24wjgkYm7JqtSDyfvoesvumQkCDEHeLcFatKZ" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJkN2YyMzNmYzRkMzI3MTdkZmM2YWRjMzVkY2QzNWI5NTY2YmUxNDIzYjg2OWI4MzM2Mzc0YzcxNGJhYTQ4MTgxIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d7f233fc4d32717dfc6adc35dcd35b9566be1423b869b8336374c714baa48181", + "receipt_id": "F1WoYrzK6x4eEtto6oEGDLrbHf4hcw8M72dm34N3kbsw", + "receipt": { + "Action": { + "signer_id": "d7f233fc4d32717dfc6adc35dcd35b9566be1423b869b8336374c714baa48181", + "signer_public_key": "ed25519:FXxrvCFpraE6YupfxA9Gw21SWDJjMbPwjKWHXueoYLm6", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "126jLBWSDoHVAy43ZRnbjdYFgVn8qX21JQAegy3AJ9dG", + "receipt": { + "Action": { + "signer_id": "a8542246f9268abcdb8109d3215a260e70c8879c7c54acd57768948a9a6fa979", + "signer_public_key": "ed25519:CL5uJDABW5SCXRN51gyvPMccTA9a5QzDqD7igkyF1i7z", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "ESevef5UhAov5afjKQavMBJEnuHjoHCZmxhJurLa1v12", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJhODU0MjI0NmY5MjY4YWJjZGI4MTA5ZDMyMTVhMjYwZTcwYzg4NzljN2M1NGFjZDU3NzY4OTQ4YTlhNmZhOTc5IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "93PNwuWnLQSAfw5zx5YQUnbS4vddsJuBwNBSj5FJrq9g", + "receipt": { + "Action": { + "signer_id": "a8542246f9268abcdb8109d3215a260e70c8879c7c54acd57768948a9a6fa979", + "signer_public_key": "ed25519:CL5uJDABW5SCXRN51gyvPMccTA9a5QzDqD7igkyF1i7z", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "ESevef5UhAov5afjKQavMBJEnuHjoHCZmxhJurLa1v12" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJhODU0MjI0NmY5MjY4YWJjZGI4MTA5ZDMyMTVhMjYwZTcwYzg4NzljN2M1NGFjZDU3NzY4OTQ4YTlhNmZhOTc5IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "a8542246f9268abcdb8109d3215a260e70c8879c7c54acd57768948a9a6fa979", + "receipt_id": "4eChDED6dhdzFfZtq7Z2bV3EGK53uSPoRTvxBtxkiNg5", + "receipt": { + "Action": { + "signer_id": "a8542246f9268abcdb8109d3215a260e70c8879c7c54acd57768948a9a6fa979", + "signer_public_key": "ed25519:CL5uJDABW5SCXRN51gyvPMccTA9a5QzDqD7igkyF1i7z", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5877805752493766187390" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "BSPtFXuX9VWdem2YM3ktunSew5jHpZHwAKsKWXpPdjHr", + "receipt": { + "Action": { + "signer_id": "5b66e43149ae4c8a73baa693d2cca2b0e76b1beee9610e5f454a055eaf0f6540", + "signer_public_key": "ed25519:79o6hBaue6SjAsLCkpVz5Rk9rqFzhHUYSPxFrE8nKqyH", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "4Xm8ytqfQoUpb7MbmLaCV876JLE6Wrog9Bu19hykFJxs", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI1YjY2ZTQzMTQ5YWU0YzhhNzNiYWE2OTNkMmNjYTJiMGU3NmIxYmVlZTk2MTBlNWY0NTRhMDU1ZWFmMGY2NTQwIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "6zNuWyDqmYkaRTktT8iCWpbgUWjXcSvDcJbxq9N4ctX9", + "receipt": { + "Action": { + "signer_id": "5b66e43149ae4c8a73baa693d2cca2b0e76b1beee9610e5f454a055eaf0f6540", + "signer_public_key": "ed25519:79o6hBaue6SjAsLCkpVz5Rk9rqFzhHUYSPxFrE8nKqyH", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "4Xm8ytqfQoUpb7MbmLaCV876JLE6Wrog9Bu19hykFJxs" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI1YjY2ZTQzMTQ5YWU0YzhhNzNiYWE2OTNkMmNjYTJiMGU3NmIxYmVlZTk2MTBlNWY0NTRhMDU1ZWFmMGY2NTQwIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "5b66e43149ae4c8a73baa693d2cca2b0e76b1beee9610e5f454a055eaf0f6540", + "receipt_id": "8ei4t6sX4tJS7g4Jp8vAgqKQzgi4CdVLtWJGRuy9zSZk", + "receipt": { + "Action": { + "signer_id": "5b66e43149ae4c8a73baa693d2cca2b0e76b1beee9610e5f454a055eaf0f6540", + "signer_public_key": "ed25519:79o6hBaue6SjAsLCkpVz5Rk9rqFzhHUYSPxFrE8nKqyH", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5874383348262772861770" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "E6HvHMwE9QLt77o426s5DseCvzvWp5NkeADWQ4yE7hte", + "receipt": { + "Action": { + "signer_id": "12e7194d1f35c20a07c188a7881cf092c1ae184e690fc3a9763d4f1635231fca", + "signer_public_key": "ed25519:2GnixzB6x1osnonp8A9x6UkjnLXgqt48CRQYCgEzSu3o", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "BUq3Hv3cFx1Wq37nLwmnCE2ikrjjTE1kLMsxhTdauQqE", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiIxMmU3MTk0ZDFmMzVjMjBhMDdjMTg4YTc4ODFjZjA5MmMxYWUxODRlNjkwZmMzYTk3NjNkNGYxNjM1MjMxZmNhIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "FRcUo5JcqWRrUH6wfCE5KzJzyNyCtK1Rd9YXQrtu7MjN", + "receipt": { + "Action": { + "signer_id": "12e7194d1f35c20a07c188a7881cf092c1ae184e690fc3a9763d4f1635231fca", + "signer_public_key": "ed25519:2GnixzB6x1osnonp8A9x6UkjnLXgqt48CRQYCgEzSu3o", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "BUq3Hv3cFx1Wq37nLwmnCE2ikrjjTE1kLMsxhTdauQqE" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiIxMmU3MTk0ZDFmMzVjMjBhMDdjMTg4YTc4ODFjZjA5MmMxYWUxODRlNjkwZmMzYTk3NjNkNGYxNjM1MjMxZmNhIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "12e7194d1f35c20a07c188a7881cf092c1ae184e690fc3a9763d4f1635231fca", + "receipt_id": "BJDYRFMsZjZ5SVSf2JsPgMGC2ekmiwLURJwkgJPyP3xR", + "receipt": { + "Action": { + "signer_id": "12e7194d1f35c20a07c188a7881cf092c1ae184e690fc3a9763d4f1635231fca", + "signer_public_key": "ed25519:2GnixzB6x1osnonp8A9x6UkjnLXgqt48CRQYCgEzSu3o", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5874383348262772861770" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "GFATWJJjmwWczAXf6xw3yhdWh3yEvkx2cwpfoWd7h7b2", + "receipt": { + "Action": { + "signer_id": "27e1b99da5956b968f4e18eef758bd3dd4c51d0bb39487dce611c7efbab97622", + "signer_public_key": "ed25519:3ggXtfxibkZBCznykcqbSLAN6RhtK4QGKSX2BnwPLqv9", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "DL6dsiDcSAx6Rf9Lw7YZ6XYUFWHzBSCu7CHkxRqxyajm", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiIyN2UxYjk5ZGE1OTU2Yjk2OGY0ZTE4ZWVmNzU4YmQzZGQ0YzUxZDBiYjM5NDg3ZGNlNjExYzdlZmJhYjk3NjIyIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "8imDisv6DPXa8YWNztgYWXM4M3F9kkHBRipUui1t2zrz", + "receipt": { + "Action": { + "signer_id": "27e1b99da5956b968f4e18eef758bd3dd4c51d0bb39487dce611c7efbab97622", + "signer_public_key": "ed25519:3ggXtfxibkZBCznykcqbSLAN6RhtK4QGKSX2BnwPLqv9", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "DL6dsiDcSAx6Rf9Lw7YZ6XYUFWHzBSCu7CHkxRqxyajm" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiIyN2UxYjk5ZGE1OTU2Yjk2OGY0ZTE4ZWVmNzU4YmQzZGQ0YzUxZDBiYjM5NDg3ZGNlNjExYzdlZmJhYjk3NjIyIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "27e1b99da5956b968f4e18eef758bd3dd4c51d0bb39487dce611c7efbab97622", + "receipt_id": "FYRXR44CKmynPVBaMCGSGS2gYCswUBWjZ6QetF9KNDMJ", + "receipt": { + "Action": { + "signer_id": "27e1b99da5956b968f4e18eef758bd3dd4c51d0bb39487dce611c7efbab97622", + "signer_public_key": "ed25519:3ggXtfxibkZBCznykcqbSLAN6RhtK4QGKSX2BnwPLqv9", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5877805752493766187390" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "DdXiFK5asLygRwkGtE5E5YfChMnbArK8ozZh6DjX8AvQ", + "receipt": { + "Action": { + "signer_id": "e6964cf50afb3b7d7e6b0da93d780d9df6f13e77b7239d64fd8178ba5c3c04d2", + "signer_public_key": "ed25519:GX7hS5YZ81wqmu8kzqWuCuKmpa2pSeBwaS2AJUrT13gu", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "6ujmzShuCHfKUdyJ4rNcU2MVJkjpigRZfhiLftmv4Uug", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJlNjk2NGNmNTBhZmIzYjdkN2U2YjBkYTkzZDc4MGQ5ZGY2ZjEzZTc3YjcyMzlkNjRmZDgxNzhiYTVjM2MwNGQyIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "9rTpUwYqQyWQg6BXDAUabwuZqFTYoRY18gEMtL87AHJ", + "receipt": { + "Action": { + "signer_id": "e6964cf50afb3b7d7e6b0da93d780d9df6f13e77b7239d64fd8178ba5c3c04d2", + "signer_public_key": "ed25519:GX7hS5YZ81wqmu8kzqWuCuKmpa2pSeBwaS2AJUrT13gu", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "6ujmzShuCHfKUdyJ4rNcU2MVJkjpigRZfhiLftmv4Uug" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJlNjk2NGNmNTBhZmIzYjdkN2U2YjBkYTkzZDc4MGQ5ZGY2ZjEzZTc3YjcyMzlkNjRmZDgxNzhiYTVjM2MwNGQyIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "e6964cf50afb3b7d7e6b0da93d780d9df6f13e77b7239d64fd8178ba5c3c04d2", + "receipt_id": "2whRMGh4Bcj3PG1YQwTechaiM2qJRGeukq3ucUqjHvZ4", + "receipt": { + "Action": { + "signer_id": "e6964cf50afb3b7d7e6b0da93d780d9df6f13e77b7239d64fd8178ba5c3c04d2", + "signer_public_key": "ed25519:GX7hS5YZ81wqmu8kzqWuCuKmpa2pSeBwaS2AJUrT13gu", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "CABeaPods2v6d4wdGMMNQAWxLTXturMa8UYVw4Kb29a5", + "receipt": { + "Action": { + "signer_id": "e291748ca4f5f06fe6ada8d5bb72927dc53947ddf4714602a827f0839a1743c9", + "signer_public_key": "ed25519:GFRnJXk3XVqB21SexNAosuM9ESY5vvFK27PFPVR3HqiY", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "6NrL97N8kEBYWV359wt1qCkm7aKodNw9nFEaVEsxJJC2", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJlMjkxNzQ4Y2E0ZjVmMDZmZTZhZGE4ZDViYjcyOTI3ZGM1Mzk0N2RkZjQ3MTQ2MDJhODI3ZjA4MzlhMTc0M2M5IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "6Pbv7vTfnWc8hZHwXKPv3m2uGQyCK1qYKKUg6MrvCtgq", + "receipt": { + "Action": { + "signer_id": "e291748ca4f5f06fe6ada8d5bb72927dc53947ddf4714602a827f0839a1743c9", + "signer_public_key": "ed25519:GFRnJXk3XVqB21SexNAosuM9ESY5vvFK27PFPVR3HqiY", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "6NrL97N8kEBYWV359wt1qCkm7aKodNw9nFEaVEsxJJC2" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJlMjkxNzQ4Y2E0ZjVmMDZmZTZhZGE4ZDViYjcyOTI3ZGM1Mzk0N2RkZjQ3MTQ2MDJhODI3ZjA4MzlhMTc0M2M5IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "e291748ca4f5f06fe6ada8d5bb72927dc53947ddf4714602a827f0839a1743c9", + "receipt_id": "FFaP2L22Bz2vKvLzfxPjFHoFJaYTkZanLCsACZv4KQNL", + "receipt": { + "Action": { + "signer_id": "e291748ca4f5f06fe6ada8d5bb72927dc53947ddf4714602a827f0839a1743c9", + "signer_public_key": "ed25519:GFRnJXk3XVqB21SexNAosuM9ESY5vvFK27PFPVR3HqiY", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "68Te51F9SjFZYHLLEfFhC1r6pbpLT5CpVToWxvDtG8po", + "receipt": { + "Action": { + "signer_id": "7b1c881095bdd0e63be8d3120fbd2e12b36c7386366d46cb8766fa20aa5a691c", + "signer_public_key": "ed25519:9HaP46YrvzigHL7fRiLLsLJdieVrnhjcCMW6RxAmKrnP", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "pnPCQ1ZbLiyyiLGip6WTjMRkHJWsR1ajSPCXJyCevdd", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI3YjFjODgxMDk1YmRkMGU2M2JlOGQzMTIwZmJkMmUxMmIzNmM3Mzg2MzY2ZDQ2Y2I4NzY2ZmEyMGFhNWE2OTFjIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "k8AWZp6v3oy1MB2iAYWcXoUHKrdivaZTPdJnP7gt5YY", + "receipt": { + "Action": { + "signer_id": "7b1c881095bdd0e63be8d3120fbd2e12b36c7386366d46cb8766fa20aa5a691c", + "signer_public_key": "ed25519:9HaP46YrvzigHL7fRiLLsLJdieVrnhjcCMW6RxAmKrnP", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "pnPCQ1ZbLiyyiLGip6WTjMRkHJWsR1ajSPCXJyCevdd" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI3YjFjODgxMDk1YmRkMGU2M2JlOGQzMTIwZmJkMmUxMmIzNmM3Mzg2MzY2ZDQ2Y2I4NzY2ZmEyMGFhNWE2OTFjIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "7b1c881095bdd0e63be8d3120fbd2e12b36c7386366d46cb8766fa20aa5a691c", + "receipt_id": "7ynNksQwsWb888DnzxR7j43i62WQZT3GX6jWmsMHCyf9", + "receipt": { + "Action": { + "signer_id": "7b1c881095bdd0e63be8d3120fbd2e12b36c7386366d46cb8766fa20aa5a691c", + "signer_public_key": "ed25519:9HaP46YrvzigHL7fRiLLsLJdieVrnhjcCMW6RxAmKrnP", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5874383348262772861770" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "4yTQ6k4CdYLrcoCrBZuNiJgmfMyqyKcsRgz5kKTyRzqh", + "receipt": { + "Action": { + "signer_id": "f9d1087abb2e72f7e3899302915356832c918f4cac3344d39c9a0b54d3b19504", + "signer_public_key": "ed25519:HpBPRr2J1m7aUw92sLwMdCEztLsU64eVfiCaug1B22BM", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "55qrjmqCgmPxq8pSkbNHjmvfFPPdRj86TYBhNSN14BQG", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmOWQxMDg3YWJiMmU3MmY3ZTM4OTkzMDI5MTUzNTY4MzJjOTE4ZjRjYWMzMzQ0ZDM5YzlhMGI1NGQzYjE5NTA0IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "44PSDS2kSMyHRLpgQfMR8uNTEA8DrKPyH7LMCUdef5oK", + "receipt": { + "Action": { + "signer_id": "f9d1087abb2e72f7e3899302915356832c918f4cac3344d39c9a0b54d3b19504", + "signer_public_key": "ed25519:HpBPRr2J1m7aUw92sLwMdCEztLsU64eVfiCaug1B22BM", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "55qrjmqCgmPxq8pSkbNHjmvfFPPdRj86TYBhNSN14BQG" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmOWQxMDg3YWJiMmU3MmY3ZTM4OTkzMDI5MTUzNTY4MzJjOTE4ZjRjYWMzMzQ0ZDM5YzlhMGI1NGQzYjE5NTA0IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f9d1087abb2e72f7e3899302915356832c918f4cac3344d39c9a0b54d3b19504", + "receipt_id": "ENvf1J567ZVBbEZpYetH7wc1u5dGpRHFhCrMzpZE4b58", + "receipt": { + "Action": { + "signer_id": "f9d1087abb2e72f7e3899302915356832c918f4cac3344d39c9a0b54d3b19504", + "signer_public_key": "ed25519:HpBPRr2J1m7aUw92sLwMdCEztLsU64eVfiCaug1B22BM", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "z9bqpMMf6Z47jhfAYqMMZQrCgJJqYukgu4Wsu7NbKEh", + "receipt": { + "Action": { + "signer_id": "f8e98fefd6f7c06830f13ed768042f732a7030c39aa568e5eeb45162b63ed9bf", + "signer_public_key": "ed25519:Hkeg1v4JwgUKzoaAxRp4Qz2UybvhqhkrYyYPnJNTSu18", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "64hZTfY26N93V3QyG7CVaQkiY92ioYrftgSJSkKZk9hL", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmOGU5OGZlZmQ2ZjdjMDY4MzBmMTNlZDc2ODA0MmY3MzJhNzAzMGMzOWFhNTY4ZTVlZWI0NTE2MmI2M2VkOWJmIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "A4PpbYGxs2wAvXHjmDaQ55JfoBw6zUobMS8S2mJkWCxD", + "receipt": { + "Action": { + "signer_id": "f8e98fefd6f7c06830f13ed768042f732a7030c39aa568e5eeb45162b63ed9bf", + "signer_public_key": "ed25519:Hkeg1v4JwgUKzoaAxRp4Qz2UybvhqhkrYyYPnJNTSu18", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "64hZTfY26N93V3QyG7CVaQkiY92ioYrftgSJSkKZk9hL" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmOGU5OGZlZmQ2ZjdjMDY4MzBmMTNlZDc2ODA0MmY3MzJhNzAzMGMzOWFhNTY4ZTVlZWI0NTE2MmI2M2VkOWJmIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f8e98fefd6f7c06830f13ed768042f732a7030c39aa568e5eeb45162b63ed9bf", + "receipt_id": "7zBEEna1T9RNwF8uQjCgcBy5TBx5kVa8923aTeGHdYYq", + "receipt": { + "Action": { + "signer_id": "f8e98fefd6f7c06830f13ed768042f732a7030c39aa568e5eeb45162b63ed9bf", + "signer_public_key": "ed25519:Hkeg1v4JwgUKzoaAxRp4Qz2UybvhqhkrYyYPnJNTSu18", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "FnNNt3sZuEn28pCwnJ2crc2pFYaZDRoN4kpizKJQJJyz", + "receipt": { + "Action": { + "signer_id": "fe75433b6985ab37f6cfee452002cce76842304e938c4e4073652da3dfc22bc1", + "signer_public_key": "ed25519:J8JGFNK4YP6eFcb8ZsVSUizM2hqsNZPtVSt8SS6Aj2WC", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "5jsatDYVN5tFbcpbdPpETjAFKqgzG8xUq674tdwhuh1z", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmZTc1NDMzYjY5ODVhYjM3ZjZjZmVlNDUyMDAyY2NlNzY4NDIzMDRlOTM4YzRlNDA3MzY1MmRhM2RmYzIyYmMxIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "8hWafkKaMig2CFqfvEheLShSsKx471ZAjiB1VGJscGbQ", + "receipt": { + "Action": { + "signer_id": "fe75433b6985ab37f6cfee452002cce76842304e938c4e4073652da3dfc22bc1", + "signer_public_key": "ed25519:J8JGFNK4YP6eFcb8ZsVSUizM2hqsNZPtVSt8SS6Aj2WC", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "5jsatDYVN5tFbcpbdPpETjAFKqgzG8xUq674tdwhuh1z" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmZTc1NDMzYjY5ODVhYjM3ZjZjZmVlNDUyMDAyY2NlNzY4NDIzMDRlOTM4YzRlNDA3MzY1MmRhM2RmYzIyYmMxIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "fe75433b6985ab37f6cfee452002cce76842304e938c4e4073652da3dfc22bc1", + "receipt_id": "8aM3yh65g1VPxyqALVT68ygobsRw8SXDpDj6Kr8cnBru", + "receipt": { + "Action": { + "signer_id": "fe75433b6985ab37f6cfee452002cce76842304e938c4e4073652da3dfc22bc1", + "signer_public_key": "ed25519:J8JGFNK4YP6eFcb8ZsVSUizM2hqsNZPtVSt8SS6Aj2WC", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "CB6JJsDmEJCCyQ512tEaCk8BmStrDa9xSnCKaS2AEuBD", + "receipt": { + "Action": { + "signer_id": "da47ffc9d6490de39dece9519ff4a9c17851e94f4aaf7f1122fecbf025a2b712", + "signer_public_key": "ed25519:Fh5ZAVaqAWAuw6oWpqqnm2VP7nCTATFkDYYSjgBnX97P", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "6WPdFpumY5JB4HBJxtZEmTPtJuTyXd2jwP58wGNBbM9k", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJkYTQ3ZmZjOWQ2NDkwZGUzOWRlY2U5NTE5ZmY0YTljMTc4NTFlOTRmNGFhZjdmMTEyMmZlY2JmMDI1YTJiNzEyIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "24sdwPdfYLuWwCNEA7B3wkgZGPxtFH6gpSFAnSQHqsEe", + "receipt": { + "Action": { + "signer_id": "da47ffc9d6490de39dece9519ff4a9c17851e94f4aaf7f1122fecbf025a2b712", + "signer_public_key": "ed25519:Fh5ZAVaqAWAuw6oWpqqnm2VP7nCTATFkDYYSjgBnX97P", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "6WPdFpumY5JB4HBJxtZEmTPtJuTyXd2jwP58wGNBbM9k" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJkYTQ3ZmZjOWQ2NDkwZGUzOWRlY2U5NTE5ZmY0YTljMTc4NTFlOTRmNGFhZjdmMTEyMmZlY2JmMDI1YTJiNzEyIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "da47ffc9d6490de39dece9519ff4a9c17851e94f4aaf7f1122fecbf025a2b712", + "receipt_id": "AHDetUWD8sdXR9SCUQ5BFdEhP44MC6jXfiRe2VSmU3Kv", + "receipt": { + "Action": { + "signer_id": "da47ffc9d6490de39dece9519ff4a9c17851e94f4aaf7f1122fecbf025a2b712", + "signer_public_key": "ed25519:Fh5ZAVaqAWAuw6oWpqqnm2VP7nCTATFkDYYSjgBnX97P", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "F8b5k6z6aNJ5tn7YjgTmNhwWRpoTozeSvroQZXup1BFV", + "receipt": { + "Action": { + "signer_id": "4ce4deadc2705a5f4433bd12de089b27bf1b0c9d28ed86e66ffbab0049cb6474", + "signer_public_key": "ed25519:6BAQ89PW7i7PgLFfRjTAiqSSnTZqZzKe12NFP9tsw1Xy", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "8vw6vF7hdhx4dt1Li1BuuDhT3WvPjremqpKKsTAZcfiS", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI0Y2U0ZGVhZGMyNzA1YTVmNDQzM2JkMTJkZTA4OWIyN2JmMWIwYzlkMjhlZDg2ZTY2ZmZiYWIwMDQ5Y2I2NDc0IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "B53ZWDd84EMRASB53FRFrUT3R3Z7ucZkBLwfzts7myxz", + "receipt": { + "Action": { + "signer_id": "4ce4deadc2705a5f4433bd12de089b27bf1b0c9d28ed86e66ffbab0049cb6474", + "signer_public_key": "ed25519:6BAQ89PW7i7PgLFfRjTAiqSSnTZqZzKe12NFP9tsw1Xy", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "8vw6vF7hdhx4dt1Li1BuuDhT3WvPjremqpKKsTAZcfiS" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI0Y2U0ZGVhZGMyNzA1YTVmNDQzM2JkMTJkZTA4OWIyN2JmMWIwYzlkMjhlZDg2ZTY2ZmZiYWIwMDQ5Y2I2NDc0IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "4ce4deadc2705a5f4433bd12de089b27bf1b0c9d28ed86e66ffbab0049cb6474", + "receipt_id": "4BBzfo5sv7x9nvpkSqwh15tETS1zGfqALFbiAQN4qdu5", + "receipt": { + "Action": { + "signer_id": "4ce4deadc2705a5f4433bd12de089b27bf1b0c9d28ed86e66ffbab0049cb6474", + "signer_public_key": "ed25519:6BAQ89PW7i7PgLFfRjTAiqSSnTZqZzKe12NFP9tsw1Xy", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5874383348262772861770" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "29nccxmoMNXSVDUttPiDHdy6kdxVXiBWMUjqNv1uNSok", + "receipt": { + "Action": { + "signer_id": "68c4a3c5370fa876328343570bc44ca481444b100887978dde5bfb6ebbc42359", + "signer_public_key": "ed25519:83yJxxtS2KAGpzRqeECEqpscKrjG4bFG3nB5mieWq7bW", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "5F14zVDyxRFrcQ6DR2QJKEywfHBretqYQN2vXy3grajA", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI2OGM0YTNjNTM3MGZhODc2MzI4MzQzNTcwYmM0NGNhNDgxNDQ0YjEwMDg4Nzk3OGRkZTViZmI2ZWJiYzQyMzU5IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "4ZYG8PZUvp4ciwUJHvefshUpPcJeKkRwRyjWwjCtqxGr", + "receipt": { + "Action": { + "signer_id": "68c4a3c5370fa876328343570bc44ca481444b100887978dde5bfb6ebbc42359", + "signer_public_key": "ed25519:83yJxxtS2KAGpzRqeECEqpscKrjG4bFG3nB5mieWq7bW", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "5F14zVDyxRFrcQ6DR2QJKEywfHBretqYQN2vXy3grajA" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI2OGM0YTNjNTM3MGZhODc2MzI4MzQzNTcwYmM0NGNhNDgxNDQ0YjEwMDg4Nzk3OGRkZTViZmI2ZWJiYzQyMzU5IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "68c4a3c5370fa876328343570bc44ca481444b100887978dde5bfb6ebbc42359", + "receipt_id": "BoRmNLPHWk82Ru5gSGv5CKdt8rEyXmLXoeCGFfvLU6em", + "receipt": { + "Action": { + "signer_id": "68c4a3c5370fa876328343570bc44ca481444b100887978dde5bfb6ebbc42359", + "signer_public_key": "ed25519:83yJxxtS2KAGpzRqeECEqpscKrjG4bFG3nB5mieWq7bW", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "C5YaTXkWapPUKjLqu5KjXNn4uD8b8ztucHaNv5BpHztK", + "receipt": { + "Action": { + "signer_id": "b6600dae3575a2e71d8630ef81d4c42202a9dc732140f14e14d03177853002e2", + "signer_public_key": "ed25519:DGv9mSVPn1YF6Jt8bNaLHUbqbDsr7paiausXtDYdiRTP", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "36Xy6GXZrZqKQkk8gbumNgLQYF7rwD1k6tswCG3Dh1JL", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJiNjYwMGRhZTM1NzVhMmU3MWQ4NjMwZWY4MWQ0YzQyMjAyYTlkYzczMjE0MGYxNGUxNGQwMzE3Nzg1MzAwMmUyIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "HaDECLKDQYvtjnGkbAJjU1Mgrdvv141yRE6CGzDDi5B2", + "receipt": { + "Action": { + "signer_id": "b6600dae3575a2e71d8630ef81d4c42202a9dc732140f14e14d03177853002e2", + "signer_public_key": "ed25519:DGv9mSVPn1YF6Jt8bNaLHUbqbDsr7paiausXtDYdiRTP", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "36Xy6GXZrZqKQkk8gbumNgLQYF7rwD1k6tswCG3Dh1JL" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJiNjYwMGRhZTM1NzVhMmU3MWQ4NjMwZWY4MWQ0YzQyMjAyYTlkYzczMjE0MGYxNGUxNGQwMzE3Nzg1MzAwMmUyIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "b6600dae3575a2e71d8630ef81d4c42202a9dc732140f14e14d03177853002e2", + "receipt_id": "Ay3YiT8XebJSMqTWomaqLU1uwpiwLUqKDqMNepyBo9by", + "receipt": { + "Action": { + "signer_id": "b6600dae3575a2e71d8630ef81d4c42202a9dc732140f14e14d03177853002e2", + "signer_public_key": "ed25519:DGv9mSVPn1YF6Jt8bNaLHUbqbDsr7paiausXtDYdiRTP", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5874383348262772861770" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "3B2M96AW3QWKgrW2bvWjFKajbieALHb2hb5aKirTEnAJ", + "receipt": { + "Action": { + "signer_id": "ec960585471cbcb86a12339835d1e93007f567585ae8d46d29ceed5d576f61d8", + "signer_public_key": "ed25519:GvXtzqiutgqbkwhdW7xfHHqaawKwKCwTDhRKzzsHCVrP", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "9nb11V5f929WsZpviQobUJzbj9BKs5CTKy7qvbWPJp7X", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJlYzk2MDU4NTQ3MWNiY2I4NmExMjMzOTgzNWQxZTkzMDA3ZjU2NzU4NWFlOGQ0NmQyOWNlZWQ1ZDU3NmY2MWQ4IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "6jXkb2hcgFh9kF1a9eFBrkyfvW3uHXC7rTVFuNWgPz5m", + "receipt": { + "Action": { + "signer_id": "ec960585471cbcb86a12339835d1e93007f567585ae8d46d29ceed5d576f61d8", + "signer_public_key": "ed25519:GvXtzqiutgqbkwhdW7xfHHqaawKwKCwTDhRKzzsHCVrP", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "9nb11V5f929WsZpviQobUJzbj9BKs5CTKy7qvbWPJp7X" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJlYzk2MDU4NTQ3MWNiY2I4NmExMjMzOTgzNWQxZTkzMDA3ZjU2NzU4NWFlOGQ0NmQyOWNlZWQ1ZDU3NmY2MWQ4IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ec960585471cbcb86a12339835d1e93007f567585ae8d46d29ceed5d576f61d8", + "receipt_id": "BUbxMLvxBKCZR71NuEj2mnEjf77U4VitWoKHYrRxg1rh", + "receipt": { + "Action": { + "signer_id": "ec960585471cbcb86a12339835d1e93007f567585ae8d46d29ceed5d576f61d8", + "signer_public_key": "ed25519:GvXtzqiutgqbkwhdW7xfHHqaawKwKCwTDhRKzzsHCVrP", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "ErfDTtYv5Hb3qQ2EjxkygXiZ3AfF36E6AZdQpZBpTrus", + "receipt": { + "Action": { + "signer_id": "83057af2467f8abef59d55540e406bcd59194b9e4dbed5ac05bcc2c321286232", + "signer_public_key": "ed25519:9pTFoQsYqi6FAK6x8uGMbg5FLayj1hZmWryGnGzYzbjo", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "7L7JEoQG9vgZ6PNQcaos1MZ6XEZCNctf6jr1UP8i3W1F", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI4MzA1N2FmMjQ2N2Y4YWJlZjU5ZDU1NTQwZTQwNmJjZDU5MTk0YjllNGRiZWQ1YWMwNWJjYzJjMzIxMjg2MjMyIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "GkKaXeNhGHoB3bmMB6KxrWhhUmxPxRnmpajdRQSEVdMU", + "receipt": { + "Action": { + "signer_id": "83057af2467f8abef59d55540e406bcd59194b9e4dbed5ac05bcc2c321286232", + "signer_public_key": "ed25519:9pTFoQsYqi6FAK6x8uGMbg5FLayj1hZmWryGnGzYzbjo", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "7L7JEoQG9vgZ6PNQcaos1MZ6XEZCNctf6jr1UP8i3W1F" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI4MzA1N2FmMjQ2N2Y4YWJlZjU5ZDU1NTQwZTQwNmJjZDU5MTk0YjllNGRiZWQ1YWMwNWJjYzJjMzIxMjg2MjMyIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "83057af2467f8abef59d55540e406bcd59194b9e4dbed5ac05bcc2c321286232", + "receipt_id": "2yBSngaPJt16zFUQsLkygxWaQQhwCpQkoWfYzAJQkdXS", + "receipt": { + "Action": { + "signer_id": "83057af2467f8abef59d55540e406bcd59194b9e4dbed5ac05bcc2c321286232", + "signer_public_key": "ed25519:9pTFoQsYqi6FAK6x8uGMbg5FLayj1hZmWryGnGzYzbjo", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "BrF5x1qcjgZFABxk8HFPWXT2518eLCH9Q5JkrHsBhA3H", + "receipt": { + "Action": { + "signer_id": "d76de16fc5ae649a57730bed7b2f0facc8070196d3037fd361e7865edf886fc9", + "signer_public_key": "ed25519:FVwqNYDAywHhuc6yWDB2nqZQWyFv9PVdhgN9bfGCfddJ", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "BP3NhoV5x6sLxwvX8qKWmJfkRigGtCnLXBguQVTF6jeG", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJkNzZkZTE2ZmM1YWU2NDlhNTc3MzBiZWQ3YjJmMGZhY2M4MDcwMTk2ZDMwMzdmZDM2MWU3ODY1ZWRmODg2ZmM5IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "9WFY4HnxnNYFntLc4eGsDDzoPsHqUaKFeJmjJvBK2Ncr", + "receipt": { + "Action": { + "signer_id": "d76de16fc5ae649a57730bed7b2f0facc8070196d3037fd361e7865edf886fc9", + "signer_public_key": "ed25519:FVwqNYDAywHhuc6yWDB2nqZQWyFv9PVdhgN9bfGCfddJ", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "BP3NhoV5x6sLxwvX8qKWmJfkRigGtCnLXBguQVTF6jeG" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJkNzZkZTE2ZmM1YWU2NDlhNTc3MzBiZWQ3YjJmMGZhY2M4MDcwMTk2ZDMwMzdmZDM2MWU3ODY1ZWRmODg2ZmM5IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d76de16fc5ae649a57730bed7b2f0facc8070196d3037fd361e7865edf886fc9", + "receipt_id": "DoFpKxruwMZSj3jHiTAxY2a9RZJnFB6fqPCmHpBLgiud", + "receipt": { + "Action": { + "signer_id": "d76de16fc5ae649a57730bed7b2f0facc8070196d3037fd361e7865edf886fc9", + "signer_public_key": "ed25519:FVwqNYDAywHhuc6yWDB2nqZQWyFv9PVdhgN9bfGCfddJ", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "BB6enSF9u69KTXiqkQUtqikiiUPoXmh5gQfGtzr8Uivq", + "receipt": { + "Action": { + "signer_id": "9375fffe3685efb35859008a25960dedd5f8e04bd64c262a5ee9e22a54a6305f", + "signer_public_key": "ed25519:AvdHu5uEtk5wqoR51HtdFp4htSjhjKZMHp4UD5xVLGrJ", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "B7GuGL6FNPP8SMCSYW1fiscLm2jS5eNYn1rxS9CaNKGz", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI5Mzc1ZmZmZTM2ODVlZmIzNTg1OTAwOGEyNTk2MGRlZGQ1ZjhlMDRiZDY0YzI2MmE1ZWU5ZTIyYTU0YTYzMDVmIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "Fs4TWKMVNoWZ5roWAM4TC1bgX7NQrKqJ7k8qFNTfQedf", + "receipt": { + "Action": { + "signer_id": "9375fffe3685efb35859008a25960dedd5f8e04bd64c262a5ee9e22a54a6305f", + "signer_public_key": "ed25519:AvdHu5uEtk5wqoR51HtdFp4htSjhjKZMHp4UD5xVLGrJ", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "B7GuGL6FNPP8SMCSYW1fiscLm2jS5eNYn1rxS9CaNKGz" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI5Mzc1ZmZmZTM2ODVlZmIzNTg1OTAwOGEyNTk2MGRlZGQ1ZjhlMDRiZDY0YzI2MmE1ZWU5ZTIyYTU0YTYzMDVmIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9375fffe3685efb35859008a25960dedd5f8e04bd64c262a5ee9e22a54a6305f", + "receipt_id": "2hhZ2L8Pbumj6k6ZXmz59YqtKnnNZVfwtJqhiibQgbLC", + "receipt": { + "Action": { + "signer_id": "9375fffe3685efb35859008a25960dedd5f8e04bd64c262a5ee9e22a54a6305f", + "signer_public_key": "ed25519:AvdHu5uEtk5wqoR51HtdFp4htSjhjKZMHp4UD5xVLGrJ", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5874383348262772861770" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "2fcVd96b1h3utC6Q7UeBT5jcoYszZPUdqsXb5sx2vYjM", + "receipt": { + "Action": { + "signer_id": "f5ae69c59e056965e8294c05c2e2278848529a32bad867828237a21126af285d", + "signer_public_key": "ed25519:HY38zi9mbNGyw8XPcqMtFUmE6Wxbi5wSXo5PZg447wJk", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "6UugSXcVYACwXj9n9h4JX7XhV3syirHjGmfzqeP1w5YE", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmNWFlNjljNTllMDU2OTY1ZTgyOTRjMDVjMmUyMjc4ODQ4NTI5YTMyYmFkODY3ODI4MjM3YTIxMTI2YWYyODVkIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "E4BzkPXBLLvzCGD1DVGpzwjTUoXQSuRqcTZqCvAuz73m", + "receipt": { + "Action": { + "signer_id": "f5ae69c59e056965e8294c05c2e2278848529a32bad867828237a21126af285d", + "signer_public_key": "ed25519:HY38zi9mbNGyw8XPcqMtFUmE6Wxbi5wSXo5PZg447wJk", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "6UugSXcVYACwXj9n9h4JX7XhV3syirHjGmfzqeP1w5YE" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmNWFlNjljNTllMDU2OTY1ZTgyOTRjMDVjMmUyMjc4ODQ4NTI5YTMyYmFkODY3ODI4MjM3YTIxMTI2YWYyODVkIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f5ae69c59e056965e8294c05c2e2278848529a32bad867828237a21126af285d", + "receipt_id": "GntyaXCKKZCVNuYXp7XEQXHsWpUzPzd4voh2cYArB2H9", + "receipt": { + "Action": { + "signer_id": "f5ae69c59e056965e8294c05c2e2278848529a32bad867828237a21126af285d", + "signer_public_key": "ed25519:HY38zi9mbNGyw8XPcqMtFUmE6Wxbi5wSXo5PZg447wJk", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "9aqRtQBKK33aaVCBopDT84Lsh2NTjvDNQvfezWWFT9zq", + "receipt": { + "Action": { + "signer_id": "e458a95d4d2fb55511fd31ffadd87b7c6c5d401a4d26a8ceaaa5f855a4a49619", + "signer_public_key": "ed25519:GNNNLR4KeTY1U6AkJitumwgUj7nhbb2iXhjQtrwNci5e", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "3BNLEPBGgZoxGnUPeKGdiNYo7SmATKCMa2KXAMqmGceh", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJlNDU4YTk1ZDRkMmZiNTU1MTFmZDMxZmZhZGQ4N2I3YzZjNWQ0MDFhNGQyNmE4Y2VhYWE1Zjg1NWE0YTQ5NjE5IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "A55KHCTQyFNe6rZhEnK6V6H7k7c5XwHXvLNw9oKxTzHc", + "receipt": { + "Action": { + "signer_id": "e458a95d4d2fb55511fd31ffadd87b7c6c5d401a4d26a8ceaaa5f855a4a49619", + "signer_public_key": "ed25519:GNNNLR4KeTY1U6AkJitumwgUj7nhbb2iXhjQtrwNci5e", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "3BNLEPBGgZoxGnUPeKGdiNYo7SmATKCMa2KXAMqmGceh" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJlNDU4YTk1ZDRkMmZiNTU1MTFmZDMxZmZhZGQ4N2I3YzZjNWQ0MDFhNGQyNmE4Y2VhYWE1Zjg1NWE0YTQ5NjE5IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "e458a95d4d2fb55511fd31ffadd87b7c6c5d401a4d26a8ceaaa5f855a4a49619", + "receipt_id": "3SnwbJ9upeYwQBDzKNMBMaxgjFSKpNSQPLNTLBSycUW9", + "receipt": { + "Action": { + "signer_id": "e458a95d4d2fb55511fd31ffadd87b7c6c5d401a4d26a8ceaaa5f855a4a49619", + "signer_public_key": "ed25519:GNNNLR4KeTY1U6AkJitumwgUj7nhbb2iXhjQtrwNci5e", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5884650560955752838630" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "BctiKRTfyoiz1FDCAy34AByewCsDQJ2eNd2UY8iUrfWT", + "receipt": { + "Action": { + "signer_id": "b273c99335764062b4ec3d54b1bf67d2e353efbd33b4249af8992db6f4d34013", + "signer_public_key": "ed25519:D1bySjc1EjoYKBFeNhmeA47V81aPSgCfB8Dv9SiXMjQN", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "BxrftNueY1xfeF8RmQK9BPuueWBnmo4WXGuB4QcUZXX1", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJiMjczYzk5MzM1NzY0MDYyYjRlYzNkNTRiMWJmNjdkMmUzNTNlZmJkMzNiNDI0OWFmODk5MmRiNmY0ZDM0MDEzIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "3eLrWheRxChogkdUVbBsQfWrt3hGop3p4YEfpBnkXdY7", + "receipt": { + "Action": { + "signer_id": "b273c99335764062b4ec3d54b1bf67d2e353efbd33b4249af8992db6f4d34013", + "signer_public_key": "ed25519:D1bySjc1EjoYKBFeNhmeA47V81aPSgCfB8Dv9SiXMjQN", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "BxrftNueY1xfeF8RmQK9BPuueWBnmo4WXGuB4QcUZXX1" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJiMjczYzk5MzM1NzY0MDYyYjRlYzNkNTRiMWJmNjdkMmUzNTNlZmJkMzNiNDI0OWFmODk5MmRiNmY0ZDM0MDEzIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "b273c99335764062b4ec3d54b1bf67d2e353efbd33b4249af8992db6f4d34013", + "receipt_id": "Gx9Lhjpw7ierRTNdEj8mhDSuUkCDqieqTk5wTwEQmKCY", + "receipt": { + "Action": { + "signer_id": "b273c99335764062b4ec3d54b1bf67d2e353efbd33b4249af8992db6f4d34013", + "signer_public_key": "ed25519:D1bySjc1EjoYKBFeNhmeA47V81aPSgCfB8Dv9SiXMjQN", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5884650560955752838630" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "mdYvrFyLmUvq9HyX14d8QuLN5SWKt59ungatAjQUxxF", + "receipt": { + "Action": { + "signer_id": "502b972ac697a6c05316fb1e44da364a7ccb82a1f6f9c4d6b609a21c94571cdc", + "signer_public_key": "ed25519:6PxAiPm9gVjsZWJRLrPdTaTRBAtGfFvN6EC5NbKZ4ydV", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "AeVDeqE3s1DDmEHL3TmajDTec8GRDv41tSCLEruGmS4F", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI1MDJiOTcyYWM2OTdhNmMwNTMxNmZiMWU0NGRhMzY0YTdjY2I4MmExZjZmOWM0ZDZiNjA5YTIxYzk0NTcxY2RjIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "CLfnEkZZ3nafgg26Pr7XYKa2seZKG7Q9KvHKSdsNkyFb", + "receipt": { + "Action": { + "signer_id": "502b972ac697a6c05316fb1e44da364a7ccb82a1f6f9c4d6b609a21c94571cdc", + "signer_public_key": "ed25519:6PxAiPm9gVjsZWJRLrPdTaTRBAtGfFvN6EC5NbKZ4ydV", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "AeVDeqE3s1DDmEHL3TmajDTec8GRDv41tSCLEruGmS4F" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI1MDJiOTcyYWM2OTdhNmMwNTMxNmZiMWU0NGRhMzY0YTdjY2I4MmExZjZmOWM0ZDZiNjA5YTIxYzk0NTcxY2RjIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "502b972ac697a6c05316fb1e44da364a7ccb82a1f6f9c4d6b609a21c94571cdc", + "receipt_id": "2ktr1nUEtUP6jQre29KJ9ruQdArWamkhL1rkfbupHp9D", + "receipt": { + "Action": { + "signer_id": "502b972ac697a6c05316fb1e44da364a7ccb82a1f6f9c4d6b609a21c94571cdc", + "signer_public_key": "ed25519:6PxAiPm9gVjsZWJRLrPdTaTRBAtGfFvN6EC5NbKZ4ydV", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5877805752493766187390" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "J4Kt64rZwKmxXnfN1o46WNuh3nztXnbMupKzjsrpvMGr", + "receipt": { + "Action": { + "signer_id": "68409f027aeaa537ec26e409b7769e4804de91b14140d62518e63010b0364358", + "signer_public_key": "ed25519:81xZ1LPtcR1g8nMQH1VAGb5RwALiPVAW45oezatCXRqR", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "HqpjvmaGvvs5DMKVGSKVoAgFkXnRzdFmCCk56eLHY2Yy", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI2ODQwOWYwMjdhZWFhNTM3ZWMyNmU0MDliNzc2OWU0ODA0ZGU5MWIxNDE0MGQ2MjUxOGU2MzAxMGIwMzY0MzU4IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "FEi3W4tG6MfAVPnFWnEj9WDcTUJH5tDAurLgM8UG64K5", + "receipt": { + "Action": { + "signer_id": "68409f027aeaa537ec26e409b7769e4804de91b14140d62518e63010b0364358", + "signer_public_key": "ed25519:81xZ1LPtcR1g8nMQH1VAGb5RwALiPVAW45oezatCXRqR", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "HqpjvmaGvvs5DMKVGSKVoAgFkXnRzdFmCCk56eLHY2Yy" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI2ODQwOWYwMjdhZWFhNTM3ZWMyNmU0MDliNzc2OWU0ODA0ZGU5MWIxNDE0MGQ2MjUxOGU2MzAxMGIwMzY0MzU4IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "68409f027aeaa537ec26e409b7769e4804de91b14140d62518e63010b0364358", + "receipt_id": "6DWqbWdekZ4SZ2zdSnwXXvQJK6Aq6RgNnWQQxJfYRWwH", + "receipt": { + "Action": { + "signer_id": "68409f027aeaa537ec26e409b7769e4804de91b14140d62518e63010b0364358", + "signer_public_key": "ed25519:81xZ1LPtcR1g8nMQH1VAGb5RwALiPVAW45oezatCXRqR", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "HspNuP9xvL1p9FoYR9WmMdn9vW2krKWNxWHxpqPDr1Fa", + "receipt": { + "Action": { + "signer_id": "a6c5de8b0902861145ebcdc5e2d4ff7c130c7c7717c738f5ea26b81f090cd768", + "signer_public_key": "ed25519:CE1g7qxpYDShQGVXkqDKuBJHGPNZDBrKuHshssa8DNEK", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "DSghGdri1UPvZ5JfvuxcEXbh8mihChtYSUPUeNSnCND2", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJhNmM1ZGU4YjA5MDI4NjExNDVlYmNkYzVlMmQ0ZmY3YzEzMGM3Yzc3MTdjNzM4ZjVlYTI2YjgxZjA5MGNkNzY4IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "49RbmebsToNvfHxwBbfjESwyJaRwivz1HMVdCGBHkUtg", + "receipt": { + "Action": { + "signer_id": "a6c5de8b0902861145ebcdc5e2d4ff7c130c7c7717c738f5ea26b81f090cd768", + "signer_public_key": "ed25519:CE1g7qxpYDShQGVXkqDKuBJHGPNZDBrKuHshssa8DNEK", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "DSghGdri1UPvZ5JfvuxcEXbh8mihChtYSUPUeNSnCND2" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJhNmM1ZGU4YjA5MDI4NjExNDVlYmNkYzVlMmQ0ZmY3YzEzMGM3Yzc3MTdjNzM4ZjVlYTI2YjgxZjA5MGNkNzY4IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "a6c5de8b0902861145ebcdc5e2d4ff7c130c7c7717c738f5ea26b81f090cd768", + "receipt_id": "721KaibXNvTrFJytaab2X63ShQkmV8CvZmv3kS8HT5gC", + "receipt": { + "Action": { + "signer_id": "a6c5de8b0902861145ebcdc5e2d4ff7c130c7c7717c738f5ea26b81f090cd768", + "signer_public_key": "ed25519:CE1g7qxpYDShQGVXkqDKuBJHGPNZDBrKuHshssa8DNEK", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5877805752493766187390" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "5pmm6wrPmwWVLYrHFGiUZxKPNnWG95XWNhDLwuCjq6d1", + "receipt": { + "Action": { + "signer_id": "fbeb59b9e63b9ce058448b37f80dfc6ef6592f31100ed9cc1e4d5dcfef3f59d3", + "signer_public_key": "ed25519:HxPUgfL6XArzkFXwdsyT9DqbnfiNSsK5nDk4RvFEUb14", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "3gyvGLjLBXYYzu6vppQvQf5xeruwdwtQM18QQCcaoGz4", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmYmViNTliOWU2M2I5Y2UwNTg0NDhiMzdmODBkZmM2ZWY2NTkyZjMxMTAwZWQ5Y2MxZTRkNWRjZmVmM2Y1OWQzIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "7qCe9WqqSv4nuqzMnn3HteapyCQ2hcgcxydtGbqCTtez", + "receipt": { + "Action": { + "signer_id": "fbeb59b9e63b9ce058448b37f80dfc6ef6592f31100ed9cc1e4d5dcfef3f59d3", + "signer_public_key": "ed25519:HxPUgfL6XArzkFXwdsyT9DqbnfiNSsK5nDk4RvFEUb14", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "3gyvGLjLBXYYzu6vppQvQf5xeruwdwtQM18QQCcaoGz4" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmYmViNTliOWU2M2I5Y2UwNTg0NDhiMzdmODBkZmM2ZWY2NTkyZjMxMTAwZWQ5Y2MxZTRkNWRjZmVmM2Y1OWQzIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "fbeb59b9e63b9ce058448b37f80dfc6ef6592f31100ed9cc1e4d5dcfef3f59d3", + "receipt_id": "FTENhFaA79vTzPGVz22do3SLrzxLdQeG12Eujhv8axe1", + "receipt": { + "Action": { + "signer_id": "fbeb59b9e63b9ce058448b37f80dfc6ef6592f31100ed9cc1e4d5dcfef3f59d3", + "signer_public_key": "ed25519:HxPUgfL6XArzkFXwdsyT9DqbnfiNSsK5nDk4RvFEUb14", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "DR1qQ1PZeadQqzJFqJ7zySa9qL4oVmr8tvcKn3HtUMTg", + "receipt": { + "Action": { + "signer_id": "d53992179bfc33eda3d827897f5b3de1acafbbc4db26c3256be6c3844d54f852", + "signer_public_key": "ed25519:FMLkpE7UPjACMk5MVe4M3erXsdGmWkXzuHoA2ViubYrm", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "HWNsgkesjxBLyLp5nA5UvZZoKHu7gcVf63PaRypfuhNn", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJkNTM5OTIxNzliZmMzM2VkYTNkODI3ODk3ZjViM2RlMWFjYWZiYmM0ZGIyNmMzMjU2YmU2YzM4NDRkNTRmODUyIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "7qTxqgWY7EcNGsNdFAAY5xbwLks4UrfqgCDWtSbw3dm", + "receipt": { + "Action": { + "signer_id": "d53992179bfc33eda3d827897f5b3de1acafbbc4db26c3256be6c3844d54f852", + "signer_public_key": "ed25519:FMLkpE7UPjACMk5MVe4M3erXsdGmWkXzuHoA2ViubYrm", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "HWNsgkesjxBLyLp5nA5UvZZoKHu7gcVf63PaRypfuhNn" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJkNTM5OTIxNzliZmMzM2VkYTNkODI3ODk3ZjViM2RlMWFjYWZiYmM0ZGIyNmMzMjU2YmU2YzM4NDRkNTRmODUyIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d53992179bfc33eda3d827897f5b3de1acafbbc4db26c3256be6c3844d54f852", + "receipt_id": "HqmnrQoDnvp95t2RNnTYZFs7zMp4vBcT8PeVKby5M7Tc", + "receipt": { + "Action": { + "signer_id": "d53992179bfc33eda3d827897f5b3de1acafbbc4db26c3256be6c3844d54f852", + "signer_public_key": "ed25519:FMLkpE7UPjACMk5MVe4M3erXsdGmWkXzuHoA2ViubYrm", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "4FtN3SL2v7669BC5NHBZGoMT1epzkD1X7ENrF9qQ8xgj", + "receipt": { + "Action": { + "signer_id": "ad36afa78073407b3c1412a8dbc227b1cc7e3fba37523fafb932be9deca74600", + "signer_public_key": "ed25519:Cf9tzZRgUQBFidboWnMGV2eRDZvL9r4GR4zEPdT3Y2WK", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "6LFqfQFEDiFysJ2Pvr8hGE5ee5H46WjTkcCFmdEyFePg", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJhZDM2YWZhNzgwNzM0MDdiM2MxNDEyYThkYmMyMjdiMWNjN2UzZmJhMzc1MjNmYWZiOTMyYmU5ZGVjYTc0NjAwIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "BQViHrVcV7YA9jLzLtwFakF2iGSRkDsAxHYiX5SpwWeQ", + "receipt": { + "Action": { + "signer_id": "ad36afa78073407b3c1412a8dbc227b1cc7e3fba37523fafb932be9deca74600", + "signer_public_key": "ed25519:Cf9tzZRgUQBFidboWnMGV2eRDZvL9r4GR4zEPdT3Y2WK", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "6LFqfQFEDiFysJ2Pvr8hGE5ee5H46WjTkcCFmdEyFePg" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJhZDM2YWZhNzgwNzM0MDdiM2MxNDEyYThkYmMyMjdiMWNjN2UzZmJhMzc1MjNmYWZiOTMyYmU5ZGVjYTc0NjAwIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ad36afa78073407b3c1412a8dbc227b1cc7e3fba37523fafb932be9deca74600", + "receipt_id": "6bjTniWQyk3ycZ2qs4gMByt3QUYhoP9kHuYdTJNw2H6n", + "receipt": { + "Action": { + "signer_id": "ad36afa78073407b3c1412a8dbc227b1cc7e3fba37523fafb932be9deca74600", + "signer_public_key": "ed25519:Cf9tzZRgUQBFidboWnMGV2eRDZvL9r4GR4zEPdT3Y2WK", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5877805752493766187390" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "6KcadZ2EhqfuUDeTQkqPt9jHQKGzNxKWPmknYh9VQJVs", + "receipt": { + "Action": { + "signer_id": "2489e4be7e8df9e199b2f62feda07e479e615a44450eaa223d1f4d0a0c8b1ba1", + "signer_public_key": "ed25519:3TddbCwkcki3NBQMT2ENafwVLGcgkiSqd2Dc2ZgTCwSg", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "EUN1nVhuub98dvfK3Et8m8FBYWwx6K71ggbA3JEGzADu", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiIyNDg5ZTRiZTdlOGRmOWUxOTliMmY2MmZlZGEwN2U0NzllNjE1YTQ0NDUwZWFhMjIzZDFmNGQwYTBjOGIxYmExIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "HJ8v7EdZ5QVxpsr2j3B1hJrS6fu5e93aGcCVfbHQb7Sk", + "receipt": { + "Action": { + "signer_id": "2489e4be7e8df9e199b2f62feda07e479e615a44450eaa223d1f4d0a0c8b1ba1", + "signer_public_key": "ed25519:3TddbCwkcki3NBQMT2ENafwVLGcgkiSqd2Dc2ZgTCwSg", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "EUN1nVhuub98dvfK3Et8m8FBYWwx6K71ggbA3JEGzADu" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiIyNDg5ZTRiZTdlOGRmOWUxOTliMmY2MmZlZGEwN2U0NzllNjE1YTQ0NDUwZWFhMjIzZDFmNGQwYTBjOGIxYmExIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "2489e4be7e8df9e199b2f62feda07e479e615a44450eaa223d1f4d0a0c8b1ba1", + "receipt_id": "FBzcr8JUDytF9VGrXH8iSARZ2XnGbwhiEKGPmWZphSu9", + "receipt": { + "Action": { + "signer_id": "2489e4be7e8df9e199b2f62feda07e479e615a44450eaa223d1f4d0a0c8b1ba1", + "signer_public_key": "ed25519:3TddbCwkcki3NBQMT2ENafwVLGcgkiSqd2Dc2ZgTCwSg", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5877805752493766187390" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "3axkUvBtyBuhyRy6ex6D8asVCbtkSxAKYrum23MhH9Q7", + "receipt": { + "Action": { + "signer_id": "69a9624854e5a2f8592ac57abaa3ee399a838e784c9ec9457efd0850e345745f", + "signer_public_key": "ed25519:87TcWe6UK1uCWnxphbdMMSJbVCWJqmuVL6Sihfd7oq5g", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "54dSBq9ah9BXnhDUpXMYNNdQ9KZXj6xPkfDu8a8ZX28t", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI2OWE5NjI0ODU0ZTVhMmY4NTkyYWM1N2FiYWEzZWUzOTlhODM4ZTc4NGM5ZWM5NDU3ZWZkMDg1MGUzNDU3NDVmIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "7u5jVvybKFPvP6BJMD5ZctQNKqMkamt8VZVEGvJCdVVB", + "receipt": { + "Action": { + "signer_id": "69a9624854e5a2f8592ac57abaa3ee399a838e784c9ec9457efd0850e345745f", + "signer_public_key": "ed25519:87TcWe6UK1uCWnxphbdMMSJbVCWJqmuVL6Sihfd7oq5g", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "54dSBq9ah9BXnhDUpXMYNNdQ9KZXj6xPkfDu8a8ZX28t" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI2OWE5NjI0ODU0ZTVhMmY4NTkyYWM1N2FiYWEzZWUzOTlhODM4ZTc4NGM5ZWM5NDU3ZWZkMDg1MGUzNDU3NDVmIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "69a9624854e5a2f8592ac57abaa3ee399a838e784c9ec9457efd0850e345745f", + "receipt_id": "5msEbwbJ2MDoaVBjCDHTRzqfNm1ueWpZWg5wBnEy6Xeo", + "receipt": { + "Action": { + "signer_id": "69a9624854e5a2f8592ac57abaa3ee399a838e784c9ec9457efd0850e345745f", + "signer_public_key": "ed25519:87TcWe6UK1uCWnxphbdMMSJbVCWJqmuVL6Sihfd7oq5g", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "XkbEhMwg6RERNns8TJnCFdaSVWB6saoTBQ9KGkRcnfQ", + "receipt": { + "Action": { + "signer_id": "f611eba55a7df6a499e958041fa57baa9d6ec9d47783d3a1db2295fadd205b12", + "signer_public_key": "ed25519:HZZ9GnUjiQu8KRHUGtueVXdDFjJAMmen97eNXFofaHPF", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "FGmJMV8vFmMg2zbEc9Qju84cAQGbJVU896b3YMwLCWYg", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmNjExZWJhNTVhN2RmNmE0OTllOTU4MDQxZmE1N2JhYTlkNmVjOWQ0Nzc4M2QzYTFkYjIyOTVmYWRkMjA1YjEyIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "2CXM88aiSZgaMT6ikP9fTYj6fxxGX3uUwW5kgncwTdeF", + "receipt": { + "Action": { + "signer_id": "f611eba55a7df6a499e958041fa57baa9d6ec9d47783d3a1db2295fadd205b12", + "signer_public_key": "ed25519:HZZ9GnUjiQu8KRHUGtueVXdDFjJAMmen97eNXFofaHPF", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "FGmJMV8vFmMg2zbEc9Qju84cAQGbJVU896b3YMwLCWYg" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmNjExZWJhNTVhN2RmNmE0OTllOTU4MDQxZmE1N2JhYTlkNmVjOWQ0Nzc4M2QzYTFkYjIyOTVmYWRkMjA1YjEyIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f611eba55a7df6a499e958041fa57baa9d6ec9d47783d3a1db2295fadd205b12", + "receipt_id": "J75AAFA7CbekAShDZuujzE2ue3aKXTzxnQTch4XHHCZb", + "receipt": { + "Action": { + "signer_id": "f611eba55a7df6a499e958041fa57baa9d6ec9d47783d3a1db2295fadd205b12", + "signer_public_key": "ed25519:HZZ9GnUjiQu8KRHUGtueVXdDFjJAMmen97eNXFofaHPF", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5874383348262772861770" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "J3kUg3j6nFXczLfNtTyxxiwHxSyVvC9gNi1BnXiabPRJ", + "receipt": { + "Action": { + "signer_id": "eb46e034b19aa78afd13692816e781b17ccf1876c3088bf1c36b66c1824143a5", + "signer_public_key": "ed25519:GqRVXjUiMnoVYeYfLDWMvsEzgsvcN6hsRFVKS6oGqz44", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "AEqYroZMrGrwYXBHfFwvE7i27W7MfQaeVJneSm4Bv16A", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJlYjQ2ZTAzNGIxOWFhNzhhZmQxMzY5MjgxNmU3ODFiMTdjY2YxODc2YzMwODhiZjFjMzZiNjZjMTgyNDE0M2E1IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "8MFfyUY4YVzERq69UY5vV8NzaEugBG25LaTJNpye8HyL", + "receipt": { + "Action": { + "signer_id": "eb46e034b19aa78afd13692816e781b17ccf1876c3088bf1c36b66c1824143a5", + "signer_public_key": "ed25519:GqRVXjUiMnoVYeYfLDWMvsEzgsvcN6hsRFVKS6oGqz44", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "AEqYroZMrGrwYXBHfFwvE7i27W7MfQaeVJneSm4Bv16A" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJlYjQ2ZTAzNGIxOWFhNzhhZmQxMzY5MjgxNmU3ODFiMTdjY2YxODc2YzMwODhiZjFjMzZiNjZjMTgyNDE0M2E1IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "eb46e034b19aa78afd13692816e781b17ccf1876c3088bf1c36b66c1824143a5", + "receipt_id": "8vzpnZ3BTzUZVncHvcSpXLs1rqSYSNHq3i5vMDhAxneN", + "receipt": { + "Action": { + "signer_id": "eb46e034b19aa78afd13692816e781b17ccf1876c3088bf1c36b66c1824143a5", + "signer_public_key": "ed25519:GqRVXjUiMnoVYeYfLDWMvsEzgsvcN6hsRFVKS6oGqz44", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "GiRUYtJUhSBaQoZJfHRZxhdEgvUqzvqBohRHrUMxjyHD", + "receipt": { + "Action": { + "signer_id": "f963b51fc6512c65eee9c24f5fb696b0df6ca3dbddb68a78a98ca9e75c55c8ec", + "signer_public_key": "ed25519:HnWhX1QtwxJVA3BWhSJtgvhSgfgQHDm3ZvXd8jn6DwXy", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "6RaDMfkH2fMjizWintDrLi5oXNJgzpdgnF7Y329Q4zHM", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmOTYzYjUxZmM2NTEyYzY1ZWVlOWMyNGY1ZmI2OTZiMGRmNmNhM2RiZGRiNjhhNzhhOThjYTllNzVjNTVjOGVjIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "AV1FeCTf3NgBAYeqk4SR35DaudMbdYG8ktMFPycmi9FE", + "receipt": { + "Action": { + "signer_id": "f963b51fc6512c65eee9c24f5fb696b0df6ca3dbddb68a78a98ca9e75c55c8ec", + "signer_public_key": "ed25519:HnWhX1QtwxJVA3BWhSJtgvhSgfgQHDm3ZvXd8jn6DwXy", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "6RaDMfkH2fMjizWintDrLi5oXNJgzpdgnF7Y329Q4zHM" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJmOTYzYjUxZmM2NTEyYzY1ZWVlOWMyNGY1ZmI2OTZiMGRmNmNhM2RiZGRiNjhhNzhhOThjYTllNzVjNTVjOGVjIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f963b51fc6512c65eee9c24f5fb696b0df6ca3dbddb68a78a98ca9e75c55c8ec", + "receipt_id": "6uhjBPPas3od2GgtZmZsinztK1JuRyHsucC2EiZEC3yz", + "receipt": { + "Action": { + "signer_id": "f963b51fc6512c65eee9c24f5fb696b0df6ca3dbddb68a78a98ca9e75c55c8ec", + "signer_public_key": "ed25519:HnWhX1QtwxJVA3BWhSJtgvhSgfgQHDm3ZvXd8jn6DwXy", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5874383348262772861770" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "EzQhz6vhN1eYBCdVaSFXeonPy39huVmDPGJvqMw7dYJZ", + "receipt": { + "Action": { + "signer_id": "802efa65820935a2460d9d562595f9879e0219112e00dbf66611703996852054", + "signer_public_key": "ed25519:9dNjWnnmQe5hM2Rh2jbHGC7aM9v2KPRVk8bADKYe3E11", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "8EjAFwmw78XyF8RLhyCKuZSriupFV72iJWVBvtpe1ZBv", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI4MDJlZmE2NTgyMDkzNWEyNDYwZDlkNTYyNTk1Zjk4NzllMDIxOTExMmUwMGRiZjY2NjExNzAzOTk2ODUyMDU0IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "7eCy6LsyTn46Zw6zHgC4AEgD6KsuJFVPy7pGET1kgrvE", + "receipt": { + "Action": { + "signer_id": "802efa65820935a2460d9d562595f9879e0219112e00dbf66611703996852054", + "signer_public_key": "ed25519:9dNjWnnmQe5hM2Rh2jbHGC7aM9v2KPRVk8bADKYe3E11", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "8EjAFwmw78XyF8RLhyCKuZSriupFV72iJWVBvtpe1ZBv" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI4MDJlZmE2NTgyMDkzNWEyNDYwZDlkNTYyNTk1Zjk4NzllMDIxOTExMmUwMGRiZjY2NjExNzAzOTk2ODUyMDU0IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "802efa65820935a2460d9d562595f9879e0219112e00dbf66611703996852054", + "receipt_id": "4UumEMHMy67sMK3qHcWPL97CuY5uTK3jSVH2xjibjjUm", + "receipt": { + "Action": { + "signer_id": "802efa65820935a2460d9d562595f9879e0219112e00dbf66611703996852054", + "signer_public_key": "ed25519:9dNjWnnmQe5hM2Rh2jbHGC7aM9v2KPRVk8bADKYe3E11", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "CxLgbaqK2SY4gCGgEPbS1gNYVqY1Pi1HzGWVAsYi3nPW", + "receipt": { + "Action": { + "signer_id": "be1009a4018d330fddca34f9e870914e218dd7b44248fa5ef2129ea69ce1839d", + "signer_public_key": "ed25519:DnveVMrpZ7PUMkz5i4rbDvRBfuvgf5srbRmpADsyVAtk", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "8cpPbZqnXia5RSNqnTrM8tafZCTAgmAqdxrGYntJTvMq", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJiZTEwMDlhNDAxOGQzMzBmZGRjYTM0ZjllODcwOTE0ZTIxOGRkN2I0NDI0OGZhNWVmMjEyOWVhNjljZTE4MzlkIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "DAHX7gRMSyX7Ni9RQYARdfZegnCYXqGhHCKJRymC8v1F", + "receipt": { + "Action": { + "signer_id": "be1009a4018d330fddca34f9e870914e218dd7b44248fa5ef2129ea69ce1839d", + "signer_public_key": "ed25519:DnveVMrpZ7PUMkz5i4rbDvRBfuvgf5srbRmpADsyVAtk", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "8cpPbZqnXia5RSNqnTrM8tafZCTAgmAqdxrGYntJTvMq" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJiZTEwMDlhNDAxOGQzMzBmZGRjYTM0ZjllODcwOTE0ZTIxOGRkN2I0NDI0OGZhNWVmMjEyOWVhNjljZTE4MzlkIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "be1009a4018d330fddca34f9e870914e218dd7b44248fa5ef2129ea69ce1839d", + "receipt_id": "Ba1Pb3z18Cx2icqsaFWMqFLj1G4cdwBz5jbpgAstf6yH", + "receipt": { + "Action": { + "signer_id": "be1009a4018d330fddca34f9e870914e218dd7b44248fa5ef2129ea69ce1839d", + "signer_public_key": "ed25519:DnveVMrpZ7PUMkz5i4rbDvRBfuvgf5srbRmpADsyVAtk", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5874383348262772861770" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "4Qeb7ZTmSmothyUqsCokWkBRR5bNUvxY1bzW4C4MmCJa", + "receipt": { + "Action": { + "signer_id": "7f858468df4b938147ba238793457783d376f35ebed79eb54ab2489e6c173de6", + "signer_public_key": "ed25519:9anrwA6ULtN6HiHRvyY22SPKoUCvgZZrW7gobf4KHge9", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "6V1rE4neBhQ9kb9KZoRMnFkDsxx6X6tazoNc3ipxdG64", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI3Zjg1ODQ2OGRmNGI5MzgxNDdiYTIzODc5MzQ1Nzc4M2QzNzZmMzVlYmVkNzllYjU0YWIyNDg5ZTZjMTczZGU2IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "4bPpcLBgjt9iNwYAyWpSCFTgNH5CHLMvqJ1sLmKmi5mC", + "receipt": { + "Action": { + "signer_id": "7f858468df4b938147ba238793457783d376f35ebed79eb54ab2489e6c173de6", + "signer_public_key": "ed25519:9anrwA6ULtN6HiHRvyY22SPKoUCvgZZrW7gobf4KHge9", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "6V1rE4neBhQ9kb9KZoRMnFkDsxx6X6tazoNc3ipxdG64" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI3Zjg1ODQ2OGRmNGI5MzgxNDdiYTIzODc5MzQ1Nzc4M2QzNzZmMzVlYmVkNzllYjU0YWIyNDg5ZTZjMTczZGU2IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "7f858468df4b938147ba238793457783d376f35ebed79eb54ab2489e6c173de6", + "receipt_id": "FBiiYRJ5V7tybUuaWjKhmGCqb6jdLNqX7xF3Xs1BTsou", + "receipt": { + "Action": { + "signer_id": "7f858468df4b938147ba238793457783d376f35ebed79eb54ab2489e6c173de6", + "signer_public_key": "ed25519:9anrwA6ULtN6HiHRvyY22SPKoUCvgZZrW7gobf4KHge9", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "AtT9WZKF8X7R9FcVsEJsWH7uR1PrT6S1A9fuDRqLzEja", + "receipt": { + "Action": { + "signer_id": "5b661f5cb82735bce376359f46aec287fd182228e1117ada4d09003360817e33", + "signer_public_key": "ed25519:79nRFh8rDzw6EKn2H3BFvgTFPqtiC2zcapL4DojRJ5Mt", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "9JWhzwZNHZTt6iSb9PFuLhzQJjrUcMwTFqf1WDA8EM2L", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI1YjY2MWY1Y2I4MjczNWJjZTM3NjM1OWY0NmFlYzI4N2ZkMTgyMjI4ZTExMTdhZGE0ZDA5MDAzMzYwODE3ZTMzIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "8fcSTmLEj1XKpWAZfX9KiMTvjy1qYV74PDLUsXiJj77j", + "receipt": { + "Action": { + "signer_id": "5b661f5cb82735bce376359f46aec287fd182228e1117ada4d09003360817e33", + "signer_public_key": "ed25519:79nRFh8rDzw6EKn2H3BFvgTFPqtiC2zcapL4DojRJ5Mt", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "9JWhzwZNHZTt6iSb9PFuLhzQJjrUcMwTFqf1WDA8EM2L" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI1YjY2MWY1Y2I4MjczNWJjZTM3NjM1OWY0NmFlYzI4N2ZkMTgyMjI4ZTExMTdhZGE0ZDA5MDAzMzYwODE3ZTMzIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "5b661f5cb82735bce376359f46aec287fd182228e1117ada4d09003360817e33", + "receipt_id": "6kXRUBote14mjKY9NEMgVgDkMmcgAxehaQQTaykj59Rq", + "receipt": { + "Action": { + "signer_id": "5b661f5cb82735bce376359f46aec287fd182228e1117ada4d09003360817e33", + "signer_public_key": "ed25519:79nRFh8rDzw6EKn2H3BFvgTFPqtiC2zcapL4DojRJ5Mt", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5874383348262772861770" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "HQBZa1wQpRnGNRAKUZ9FEPuxwrF1Drt1JUiUxAZh32c7", + "receipt": { + "Action": { + "signer_id": "a36f94bd041dcde5f1ac46fc10840710d27cfbf4fc864aa15121fd4b3d047bb3", + "signer_public_key": "ed25519:Bzz8yhj2J3rcZTiohpg5V3rVKuT2ELyWrzapoZB6Xvgi", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "CovHSxc56D9iK7Z2b2yuKdZZRJq55izTvGTvMkKySU5h", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJhMzZmOTRiZDA0MWRjZGU1ZjFhYzQ2ZmMxMDg0MDcxMGQyN2NmYmY0ZmM4NjRhYTE1MTIxZmQ0YjNkMDQ3YmIzIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "3KQJRDQn6s7mw4dv9fEWsyKUeXYZmLw3xHvub2EF3kXV", + "receipt": { + "Action": { + "signer_id": "a36f94bd041dcde5f1ac46fc10840710d27cfbf4fc864aa15121fd4b3d047bb3", + "signer_public_key": "ed25519:Bzz8yhj2J3rcZTiohpg5V3rVKuT2ELyWrzapoZB6Xvgi", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "CovHSxc56D9iK7Z2b2yuKdZZRJq55izTvGTvMkKySU5h" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJhMzZmOTRiZDA0MWRjZGU1ZjFhYzQ2ZmMxMDg0MDcxMGQyN2NmYmY0ZmM4NjRhYTE1MTIxZmQ0YjNkMDQ3YmIzIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "a36f94bd041dcde5f1ac46fc10840710d27cfbf4fc864aa15121fd4b3d047bb3", + "receipt_id": "2evdvzWoANB6N8aRrTGysejcNqfr3GmtWXtiWdTdTeeC", + "receipt": { + "Action": { + "signer_id": "a36f94bd041dcde5f1ac46fc10840710d27cfbf4fc864aa15121fd4b3d047bb3", + "signer_public_key": "ed25519:Bzz8yhj2J3rcZTiohpg5V3rVKuT2ELyWrzapoZB6Xvgi", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5877805752493766187390" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "F9QzfycXo3YdjjLCJ2K5hbmC5vjbs9EbHygLcnoT4bfV", + "receipt": { + "Action": { + "signer_id": "ba97412f6eba6162312580649ad9edce8291d8bd2e922de0e824c7689b6f7c66", + "signer_public_key": "ed25519:DZNbvck6pbw5KWowfKXRB4XuKZ77u3PFwHhmBZG8Y7T3", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "GGHYtbQHrWG6G1qBw428KvmNRjgtcx5fc5ykm96xGvpN", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJiYTk3NDEyZjZlYmE2MTYyMzEyNTgwNjQ5YWQ5ZWRjZTgyOTFkOGJkMmU5MjJkZTBlODI0Yzc2ODliNmY3YzY2IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "4WAW8jWocqJdgjgNHQkvBmtmU5u4MSjDyMd3HvgvhPUJ", + "receipt": { + "Action": { + "signer_id": "ba97412f6eba6162312580649ad9edce8291d8bd2e922de0e824c7689b6f7c66", + "signer_public_key": "ed25519:DZNbvck6pbw5KWowfKXRB4XuKZ77u3PFwHhmBZG8Y7T3", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "GGHYtbQHrWG6G1qBw428KvmNRjgtcx5fc5ykm96xGvpN" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJiYTk3NDEyZjZlYmE2MTYyMzEyNTgwNjQ5YWQ5ZWRjZTgyOTFkOGJkMmU5MjJkZTBlODI0Yzc2ODliNmY3YzY2IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ba97412f6eba6162312580649ad9edce8291d8bd2e922de0e824c7689b6f7c66", + "receipt_id": "ChERjm92vGwZBiyeVNrm3dEcrbMLKCizF3hF4QB4FLRK", + "receipt": { + "Action": { + "signer_id": "ba97412f6eba6162312580649ad9edce8291d8bd2e922de0e824c7689b6f7c66", + "signer_public_key": "ed25519:DZNbvck6pbw5KWowfKXRB4XuKZ77u3PFwHhmBZG8Y7T3", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "CSjxyymjxfRiWQM57jFohhaZrUCCgcKdHuTyovSFCT7e", + "receipt": { + "Action": { + "signer_id": "b771dc9b98e630bbd946e033c728e53b1a3c8bdad1c0c76e9548cd464197b69d", + "signer_public_key": "ed25519:DM6Ju8H4CTquSTfzSZJzV5HJ7m9wVFY9pTsKPKts6Vnt", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "4ZazYfcX7AAGxk49pMUWqzdZb8fTt6nqQzkFBZXP8S8p", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJiNzcxZGM5Yjk4ZTYzMGJiZDk0NmUwMzNjNzI4ZTUzYjFhM2M4YmRhZDFjMGM3NmU5NTQ4Y2Q0NjQxOTdiNjlkIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "AsGiozZga53PsZ7PLDMjEcfCsNL1sjPxZpwK6K1tzyvh", + "receipt": { + "Action": { + "signer_id": "b771dc9b98e630bbd946e033c728e53b1a3c8bdad1c0c76e9548cd464197b69d", + "signer_public_key": "ed25519:DM6Ju8H4CTquSTfzSZJzV5HJ7m9wVFY9pTsKPKts6Vnt", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "4ZazYfcX7AAGxk49pMUWqzdZb8fTt6nqQzkFBZXP8S8p" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJiNzcxZGM5Yjk4ZTYzMGJiZDk0NmUwMzNjNzI4ZTUzYjFhM2M4YmRhZDFjMGM3NmU5NTQ4Y2Q0NjQxOTdiNjlkIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "b771dc9b98e630bbd946e033c728e53b1a3c8bdad1c0c76e9548cd464197b69d", + "receipt_id": "EE7wVXFcyky9yZbvF54wCCFECCB9aM5MP2yFNigNM3ww", + "receipt": { + "Action": { + "signer_id": "b771dc9b98e630bbd946e033c728e53b1a3c8bdad1c0c76e9548cd464197b69d", + "signer_public_key": "ed25519:DM6Ju8H4CTquSTfzSZJzV5HJ7m9wVFY9pTsKPKts6Vnt", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "4XjugAC2V7EhcMCqQ498E2UX6E1ZQ13zuMcyJqGT5QdX", + "receipt": { + "Action": { + "signer_id": "64622d70a15b99209a28f77e523050793c57a65bb67d876f3894b383603280e1", + "signer_public_key": "ed25519:7krbiCh972A89AK9xijAGvZrGBpBGdw3HpDvuXfjwR1r", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "9kNUMtXPgS8J7e8UwQgDxDgiwgoAWY9mnEduA3NWUSem", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI2NDYyMmQ3MGExNWI5OTIwOWEyOGY3N2U1MjMwNTA3OTNjNTdhNjViYjY3ZDg3NmYzODk0YjM4MzYwMzI4MGUxIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "2G3geRWVnRV9eAGgk5xssPBXXRyKvYgvGdghJRJhRTwV", + "receipt": { + "Action": { + "signer_id": "64622d70a15b99209a28f77e523050793c57a65bb67d876f3894b383603280e1", + "signer_public_key": "ed25519:7krbiCh972A89AK9xijAGvZrGBpBGdw3HpDvuXfjwR1r", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "9kNUMtXPgS8J7e8UwQgDxDgiwgoAWY9mnEduA3NWUSem" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI2NDYyMmQ3MGExNWI5OTIwOWEyOGY3N2U1MjMwNTA3OTNjNTdhNjViYjY3ZDg3NmYzODk0YjM4MzYwMzI4MGUxIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "64622d70a15b99209a28f77e523050793c57a65bb67d876f3894b383603280e1", + "receipt_id": "AYd29XfZgaWgsnrCFScMC5aUB9ekxXfdByd1Ttnqk1en", + "receipt": { + "Action": { + "signer_id": "64622d70a15b99209a28f77e523050793c57a65bb67d876f3894b383603280e1", + "signer_public_key": "ed25519:7krbiCh972A89AK9xijAGvZrGBpBGdw3HpDvuXfjwR1r", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "5CXp64sNkGYydpjxgj6Jvcc8d8YaBiMKd6UhTBh5w3ys", + "receipt": { + "Action": { + "signer_id": "756ad7319847864952e8bb311f0af834a8474c9556b41e94474feeba9dcdc105", + "signer_public_key": "ed25519:8uMC5YiMNPeFcUTHehqKnPh4Z2pXZFd2zoHmH8cCAbC8", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "7M8j3dFceUvb25ThPH3swUBJydYEpwSXRfxH93hXQFDF", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI3NTZhZDczMTk4NDc4NjQ5NTJlOGJiMzExZjBhZjgzNGE4NDc0Yzk1NTZiNDFlOTQ0NzRmZWViYTlkY2RjMTA1IiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "2gBBVqMZQE3Q2UnbXXPwicUCt8XvzzzpFvwvVk7CLCAM", + "receipt": { + "Action": { + "signer_id": "756ad7319847864952e8bb311f0af834a8474c9556b41e94474feeba9dcdc105", + "signer_public_key": "ed25519:8uMC5YiMNPeFcUTHehqKnPh4Z2pXZFd2zoHmH8cCAbC8", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "7M8j3dFceUvb25ThPH3swUBJydYEpwSXRfxH93hXQFDF" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI3NTZhZDczMTk4NDc4NjQ5NTJlOGJiMzExZjBhZjgzNGE4NDc0Yzk1NTZiNDFlOTQ0NzRmZWViYTlkY2RjMTA1IiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "756ad7319847864952e8bb311f0af834a8474c9556b41e94474feeba9dcdc105", + "receipt_id": "4trnDdEVMHkCcgtk2DnksCMkwKNdyfAwovJ4U5bgQji5", + "receipt": { + "Action": { + "signer_id": "756ad7319847864952e8bb311f0af834a8474c9556b41e94474feeba9dcdc105", + "signer_public_key": "ed25519:8uMC5YiMNPeFcUTHehqKnPh4Z2pXZFd2zoHmH8cCAbC8", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5884650560955752838630" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "EyBb2X6cVZVfEzYjJGt37j1P54G6J5PritfDqGzeQzaP", + "receipt": { + "Action": { + "signer_id": "d6175429043c812e387d2698a9584eebbd4624f283e0350b1b3e1c1aab7feaea", + "signer_public_key": "ed25519:FQit1Fr3gUXqGN4q9fYmXVtnEcipH7ZpgNexGgMuoYBT", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "Hyzx6fn9X9WdtGTSsAQ1VwUuohYPowd7a3Hzwtd52N1v", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiJkNjE3NTQyOTA0M2M4MTJlMzg3ZDI2OThhOTU4NGVlYmJkNDYyNGYyODNlMDM1MGIxYjNlMWMxYWFiN2ZlYWVhIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "7FD8fcXzsvoxPwfkLFHrLEiPLUKLoERCpdoPD61Va9rD", + "receipt": { + "Action": { + "signer_id": "d6175429043c812e387d2698a9584eebbd4624f283e0350b1b3e1c1aab7feaea", + "signer_public_key": "ed25519:FQit1Fr3gUXqGN4q9fYmXVtnEcipH7ZpgNexGgMuoYBT", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "Hyzx6fn9X9WdtGTSsAQ1VwUuohYPowd7a3Hzwtd52N1v" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiJkNjE3NTQyOTA0M2M4MTJlMzg3ZDI2OThhOTU4NGVlYmJkNDYyNGYyODNlMDM1MGIxYjNlMWMxYWFiN2ZlYWVhIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d6175429043c812e387d2698a9584eebbd4624f283e0350b1b3e1c1aab7feaea", + "receipt_id": "7cge1MGQRQCoJ1MNxCkXZDu4RmFca6XKazdaD9FpcEwY", + "receipt": { + "Action": { + "signer_id": "d6175429043c812e387d2698a9584eebbd4624f283e0350b1b3e1c1aab7feaea", + "signer_public_key": "ed25519:FQit1Fr3gUXqGN4q9fYmXVtnEcipH7ZpgNexGgMuoYBT", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5881228156724759513010" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "amm.counselor.near", + "receipt_id": "XUt4nVrFL7jdw34XLxyp1mYEuAyL9bpjQoUuKRcXbi2", + "receipt": { + "Action": { + "signer_id": "67bad843e9ecaab916f0f28ae100b8e0541f4ab3648a0958ac9b3a44eb5031cb", + "signer_public_key": "ed25519:7yvEtACLafq9JCii5SPWh4dercz5BkjTKXck1oSGiWFL", + "gas_price": "347841850", + "output_data_receivers": [ + { + "data_id": "G1Dy9q4a7YfyvEQbLZc4b8RDWoYgfwcmU3bxePtRXVJz", + "receiver_id": "wrap.near" + } + ], + "input_data_ids": [], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_on_transfer", + "args": "eyJzZW5kZXJfaWQiOiI2N2JhZDg0M2U5ZWNhYWI5MTZmMGYyOGFlMTAwYjhlMDU0MWY0YWIzNjQ4YTA5NThhYzliM2E0NGViNTAzMWNiIiwiYW1vdW50IjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsIm1zZyI6IntcImZ1bmN0aW9uXCI6XCJidXlcIixcImFyZ3NcIjp7XCJtYXJrZXRfaWRcIjpcIjM0XCIsXCJvdXRjb21lX3RhcmdldFwiOjAsXCJtaW5fc2hhcmVzX291dFwiOlwiMFwifX0ifQ==", + "gas": 170000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "wrap.near", + "receiver_id": "wrap.near", + "receipt_id": "3RgTMGUKLxZF6BJ6zhwTVgPM3MvykAoSiS9MPKWwEgeR", + "receipt": { + "Action": { + "signer_id": "67bad843e9ecaab916f0f28ae100b8e0541f4ab3648a0958ac9b3a44eb5031cb", + "signer_public_key": "ed25519:7yvEtACLafq9JCii5SPWh4dercz5BkjTKXck1oSGiWFL", + "gas_price": "347841850", + "output_data_receivers": [], + "input_data_ids": [ + "G1Dy9q4a7YfyvEQbLZc4b8RDWoYgfwcmU3bxePtRXVJz" + ], + "actions": [ + { + "FunctionCall": { + "method_name": "ft_resolve_transfer", + "args": "eyJzZW5kZXJfaWQiOiI2N2JhZDg0M2U5ZWNhYWI5MTZmMGYyOGFlMTAwYjhlMDU0MWY0YWIzNjQ4YTA5NThhYzliM2E0NGViNTAzMWNiIiwicmVjZWl2ZXJfaWQiOiJhbW0uY291bnNlbG9yLm5lYXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIn0=", + "gas": 5000000000000, + "deposit": "0" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "67bad843e9ecaab916f0f28ae100b8e0541f4ab3648a0958ac9b3a44eb5031cb", + "receipt_id": "2AZ3LZiJzdTkmuf5P7u4ksYDKhNa5WhH2PZu9BjFy7Do", + "receipt": { + "Action": { + "signer_id": "67bad843e9ecaab916f0f28ae100b8e0541f4ab3648a0958ac9b3a44eb5031cb", + "signer_public_key": "ed25519:7yvEtACLafq9JCii5SPWh4dercz5BkjTKXck1oSGiWFL", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "5884650560955752838630" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "e16f51c60e28107634badab485b5880e8fd0de5e21f8e44ee8f9f8730b1d590e", + "receipt_id": "JCc4dbfSqCzF8HvXd1TLUiPJnyWn3FxEe33zcSD6gJEg", + "receipt": { + "Action": { + "signer_id": "e16f51c60e28107634badab485b5880e8fd0de5e21f8e44ee8f9f8730b1d590e", + "signer_public_key": "ed25519:GB1BeAqXp9kyrWj69MjqQ5npUkbrAkMzRTdMLtzwNZEM", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2202721678614593074010" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "fdf3c0ba76b88752da7b3c2281d59d453f6bbc3ebbb9a90419a32c32a6e37b9e", + "receipt_id": "DFpxfN9y4vBGj5WgZRD2LM41iczCyoujVGZ4KRWboYKo", + "receipt": { + "Action": { + "signer_id": "fdf3c0ba76b88752da7b3c2281d59d453f6bbc3ebbb9a90419a32c32a6e37b9e", + "signer_public_key": "ed25519:J6KiypFaYysRYYnLusad15ntATNqXPfSgSRBtcPAqgmT", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "69391a26878dc60f48f4c6571ad696cd7e9954aff10eedb3a2074fff865f45bd", + "receipt_id": "3ciY175jWZN7EhfdGDHo5PdqVEw44RR79TAGFxJ8QUXw", + "receipt": { + "Action": { + "signer_id": "69391a26878dc60f48f4c6571ad696cd7e9954aff10eedb3a2074fff865f45bd", + "signer_public_key": "ed25519:85kJxpT9AegNntRVwGSBZ9oCHNj33kxWcJYYBuvihThN", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "2ea3a819b54d727eea5905b0b585eef7c95dab63141c0abb89897bda4669bb23", + "receipt_id": "4hjy4597msgd7GmCdCYfqoUaTk1a18GCM7MchadNiFoy", + "receipt": { + "Action": { + "signer_id": "2ea3a819b54d727eea5905b0b585eef7c95dab63141c0abb89897bda4669bb23", + "signer_public_key": "ed25519:494Vaz7VHVMvLtgx6WT1sBd6yDgZM2AR9BjLdSgtbPT8", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "e1c47b3a8a009791f20838d976954a902113dc2cf82eb88c9910557a2abf1938", + "receipt_id": "NKK1EuV9P26kYVrNGYPiJGxJoAgPdrUSPi2jteQo3f9", + "receipt": { + "Action": { + "signer_id": "e1c47b3a8a009791f20838d976954a902113dc2cf82eb88c9910557a2abf1938", + "signer_public_key": "ed25519:GCJW4jMQLos7WC4UwtyKUkYqUFw836hNh4Bge3cz4SyV", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "b4a0c9f6f783bfcde1b1d4b75c85341c1c0d2e075f58d7a231601193d14ff91a", + "receipt_id": "GG7LPZYzfa3uKxnpzvXCgkJEWcWzWxkydiCEWxykN3AT", + "receipt": { + "Action": { + "signer_id": "b4a0c9f6f783bfcde1b1d4b75c85341c1c0d2e075f58d7a231601193d14ff91a", + "signer_public_key": "ed25519:DA6b7Vc8DkKw3m3UjEGDCvN779ciCo5VMwnHf7sTsTw7", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "27ff5373c141a3c65de7f08351caa7cf32456734fb903542e11df6b3adff2cda", + "receipt_id": "4HGHodYW9FAGeCFWPgsHd88GCH2G4sxLBRCSXvFXV4sf", + "receipt": { + "Action": { + "signer_id": "27ff5373c141a3c65de7f08351caa7cf32456734fb903542e11df6b3adff2cda", + "signer_public_key": "ed25519:3h8iHXKaEybfkUAXzN9bH9QcPKaMwZMY4DYm1615td2R", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ccf4d8c5cb4c2b61caf4e2993c7be1fec7f604983174568c1371ce52c9687359", + "receipt_id": "AwcKwazpnyCa8f8BcS2ZGTd8jwDX84i5iTxDqz2UqJ8v", + "receipt": { + "Action": { + "signer_id": "ccf4d8c5cb4c2b61caf4e2993c7be1fec7f604983174568c1371ce52c9687359", + "signer_public_key": "ed25519:Eo4iR7tZ1SNSKZLfWqGijfy3YTs3LQTBsuRNiGp6bXNY", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "a4420bdf0dbf92834296100d7d3e878f97ef4325d55ef3be49c7d02cf0a86505", + "receipt_id": "D4bXqCdFy6Crq9D5A6Bh5kyNF75pf5f5ux4pLQmgmY3x", + "receipt": { + "Action": { + "signer_id": "a4420bdf0dbf92834296100d7d3e878f97ef4325d55ef3be49c7d02cf0a86505", + "signer_public_key": "ed25519:C4CGuLfz2dbNVmg3HxzwyScXQoCHyVH81vsRQkxB6jK2", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "62029ea73bde940bdca4ac7a37103174cc94333c67f095f00ba69efd4588373a", + "receipt_id": "AF2nML54ZcwLgbo7JFo64FMRNx1rJm1TiBP9HNW7T3iH", + "receipt": { + "Action": { + "signer_id": "62029ea73bde940bdca4ac7a37103174cc94333c67f095f00ba69efd4588373a", + "signer_public_key": "ed25519:7bbGjdBzpHuimsG4RJ7wu7f5umfCwtw1YD8wcZQspRLu", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "346c50b5e3601c1472072919b2d3c478ed8d89d37d6f17c0e280666e01b463fe", + "receipt_id": "Cu7Mc5PLzgZyqKEMEhBmPwzVxHxUJTDxgPSGmUGAjxye", + "receipt": { + "Action": { + "signer_id": "346c50b5e3601c1472072919b2d3c478ed8d89d37d6f17c0e280666e01b463fe", + "signer_public_key": "ed25519:4XdzhiZVcfJekmLYaBM5WA7iKvtjY7iAS4n2XU6f4D3b", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ada56dd31338b61688a1bbccf9136010c93e640cf76af38bf1fff5d89cdbe88c", + "receipt_id": "FYqAXm8KyR6Ptzjt7hHvrAAi9NWzusTy3VNus43EpFsa", + "receipt": { + "Action": { + "signer_id": "ada56dd31338b61688a1bbccf9136010c93e640cf76af38bf1fff5d89cdbe88c", + "signer_public_key": "ed25519:CgqqbuBmU7fz6i1tJdq2pi4rXejnVRCKKr7fpsgGkcxb", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "8850bfa0acaf43c0af7cfae7e63f95d1bbf5cc6456c6b037d763a4006e796edf", + "receipt_id": "36zZTnLGS31nwWyzy4WvtVEuUyUwCRVsXgpoKHYDownL", + "receipt": { + "Action": { + "signer_id": "8850bfa0acaf43c0af7cfae7e63f95d1bbf5cc6456c6b037d763a4006e796edf", + "signer_public_key": "ed25519:AB7rxUa2AEBZHZXZxToWprirKzy9JdPbvD78qk7xo1tr", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "38bbba4522fb45b4bef0c1454ee4bf0ac9ae210608a5a3500a69dacf58caa2bc", + "receipt_id": "CQA8seKhXNmba6npvnspWZJZafcpTmiuYTQE5eovsY9F", + "receipt": { + "Action": { + "signer_id": "38bbba4522fb45b4bef0c1454ee4bf0ac9ae210608a5a3500a69dacf58caa2bc", + "signer_public_key": "ed25519:4pTrnRi7bJy7Kwp4ysxTpoPdGgZz2FV4qdfcXJuGQkZh", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "a84f16490fcca438161789940634e7374c85f82f689967c76e149037f13ef759", + "receipt_id": "FSKqWBLLu7xTvrQuaRdjNr4WNPDzQFGqkwEr78EvpNMm", + "receipt": { + "Action": { + "signer_id": "a84f16490fcca438161789940634e7374c85f82f689967c76e149037f13ef759", + "signer_public_key": "ed25519:CL1SR9noD9sVY2JSPUbSwBRFz5qF9zzSyzgtyKN6JQsr", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9d43a591c1177f6db71c7396202be99ab0f2d6c24a21fd2eb7e2908058b8071d", + "receipt_id": "2pNf5Fi1pyCEGQWeRJHAqcMQb7bwZA75Wd8PA4zq8q2A", + "receipt": { + "Action": { + "signer_id": "9d43a591c1177f6db71c7396202be99ab0f2d6c24a21fd2eb7e2908058b8071d", + "signer_public_key": "ed25519:BatqUEYvk3cefSPdbDwBN6iPALdcNs1AhoUX1qNMKSwn", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "db8d152e3d6bebc828897d6f45c71f17add023b8330ce0b16f5228e8d73b94ac", + "receipt_id": "426jQKcXvVAxjTTajXYATfVZGxcZbuMwCZ88pCpxJafD", + "receipt": { + "Action": { + "signer_id": "db8d152e3d6bebc828897d6f45c71f17add023b8330ce0b16f5228e8d73b94ac", + "signer_public_key": "ed25519:Fn34VAiUZtnAvnRawewutABVMf8P7CgaLcAGWtQgjiKu", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "951471e0814756b302c809ed03e783c5f00807050e6e8573d9a576249d0b32c7", + "receipt_id": "9TwR46JMU9gvtv2rauQaWJJVn8QNfNvWGHMkpr8Y4vgz", + "receipt": { + "Action": { + "signer_id": "951471e0814756b302c809ed03e783c5f00807050e6e8573d9a576249d0b32c7", + "signer_public_key": "ed25519:B2wq41H57YSvkDDZfM55ZGofryR4ukCRykvNAAoTteVc", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "c5b41c0b7acf736d417f84d22cac0eed3921c1aeca5fc80d024e7c8e7cff9c51", + "receipt_id": "FspUdBvyjm1MBWhSv27AcZBeHn4NkDfNjQpJxuGjYGH6", + "receipt": { + "Action": { + "signer_id": "c5b41c0b7acf736d417f84d22cac0eed3921c1aeca5fc80d024e7c8e7cff9c51", + "signer_public_key": "ed25519:EJkcATFEhPzNuuDmTuSG4ihCyZKoMgW3J3nCz98FKtGp", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "e9bffb51db321c42a25d34282ee943cdb41f8fc0fdd7920f134ce2073115d188", + "receipt_id": "DtpDxZAnAvjQcRG8c7LTvHfq8ETTuuPfXWXEJuYSbQxV", + "receipt": { + "Action": { + "signer_id": "e9bffb51db321c42a25d34282ee943cdb41f8fc0fdd7920f134ce2073115d188", + "signer_public_key": "ed25519:GjTnRdN34nCf6AQA8sxLJjGbdQU62N5nE8rVG2XFWMX5", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "95bec7e57ef31eb8bd81993a678bdb1363e7d007b239ba65925af5af43653cc7", + "receipt_id": "5Us3Vejyorp2j21QftbsV8e2mfnELJoSk1ZJvLc8yEks", + "receipt": { + "Action": { + "signer_id": "95bec7e57ef31eb8bd81993a678bdb1363e7d007b239ba65925af5af43653cc7", + "signer_public_key": "ed25519:B5YUXGFxfqDeKhx2MSAize6eiYwGnfpkPDhq5XvhmqzW", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d482c54f3b9ab801baca5810dfcfb7160d5aec673570c1a23f70a80ff5eb663f", + "receipt_id": "4owQb8GWbZJ1a5G3dTZrhiyL2yr3m72w5URA7ecnfeLs", + "receipt": { + "Action": { + "signer_id": "d482c54f3b9ab801baca5810dfcfb7160d5aec673570c1a23f70a80ff5eb663f", + "signer_public_key": "ed25519:FJZ615hc3tDSHAhNNvxj3sTqastSkJPE4q1RaYGaitcE", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "adc2a42bc959c3b0596cf653a8b562ef421890bedbaf8ea9144ebb0557605c1e", + "receipt_id": "BaJZHTgm8siwTuKz3mKaz2arsxRgmjiCvNzCz2dPCeGk", + "receipt": { + "Action": { + "signer_id": "adc2a42bc959c3b0596cf653a8b562ef421890bedbaf8ea9144ebb0557605c1e", + "signer_public_key": "ed25519:ChHg4WweaL3zH9inBQiu47nyh7vL5bxFJwnK37PiG8TP", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f6964201d324203ea3657473a62873d917542c19cc5e522d54f5f13bbfd4054f", + "receipt_id": "2mMai51XuzFAYFAfEba1UtPAnmq3NjDFXJ7VaE4dkUNH", + "receipt": { + "Action": { + "signer_id": "f6964201d324203ea3657473a62873d917542c19cc5e522d54f5f13bbfd4054f", + "signer_public_key": "ed25519:HbaBakCuBcmHpXS2ekW91hHpeNGXmJQFrcnUrrzL8pTg", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "cb6c9942fe09078c116f42300fa4c41957d2cf74e0461dff3113fd82e82552b6", + "receipt_id": "CgxadAJvuFsywTWHXSmNc8seK4YtW13DxMp426sXWZyZ", + "receipt": { + "Action": { + "signer_id": "cb6c9942fe09078c116f42300fa4c41957d2cf74e0461dff3113fd82e82552b6", + "signer_public_key": "ed25519:Eh5orfc6AV8dQiiExNprFrz84fDghyDoXaNFfgfFMjwK", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "c75aed38d44b0402c2676bf78264f34dd0521405b54b51c01df13e002f88ffda", + "receipt_id": "2jLTX7bwkvJNupJ8qhzgu3ueuuMBxKZoqvm15JsM5r8o", + "receipt": { + "Action": { + "signer_id": "c75aed38d44b0402c2676bf78264f34dd0521405b54b51c01df13e002f88ffda", + "signer_public_key": "ed25519:ERCYmwoBzCY3VHJchMNx37PY65WTbM8q9BayjqfJKcnV", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "22b634bd3b9f6cf5dce2374b8445f59c7772b1bcfd452876a0118727c63c44a1", + "receipt_id": "8gwaXrriECf3tQmJJqG2a6bZp8GLqzLX1xiTxLgHFPR9", + "receipt": { + "Action": { + "signer_id": "22b634bd3b9f6cf5dce2374b8445f59c7772b1bcfd452876a0118727c63c44a1", + "signer_public_key": "ed25519:3LW1KzQNTbvQXHpzvrs2fYsbLbCGUjAg9jvdEXDx2T4t", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9e0d32c1e5e536acd1408f2109b992b63da4d402c717bb4a045faf82f84150b5", + "receipt_id": "8x6AJpX7pxSX77HTK2bwpN73kvQKWMKQbTyVnVZgMyzo", + "receipt": { + "Action": { + "signer_id": "9e0d32c1e5e536acd1408f2109b992b63da4d402c717bb4a045faf82f84150b5", + "signer_public_key": "ed25519:Bdy69wUEbDDQc1EvBQxJM8ntmrsQzUV4N2osdQodovdi", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "b692a421b9447690a6627ab67ddd477607a35493e78de0f875eee0994157e41d", + "receipt_id": "2fje43BUwNQ4Cxy2bPj7P7G7cQiQm7qsPNx6Z5AbyiNp", + "receipt": { + "Action": { + "signer_id": "b692a421b9447690a6627ab67ddd477607a35493e78de0f875eee0994157e41d", + "signer_public_key": "ed25519:DHgtgry1xXuMQt4CSvkHYc7d4pVDQZ12qPh2GoNZk3Xr", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "8c023e8a8aa769c51b3a9198ea28e2067a73455d109bd5475dd07df40fa8a1d3", + "receipt_id": "Fdcwzdr7NkAwnavU1JGQ2zvhNNVhaaMKzRYTDX9n6JGE", + "receipt": { + "Action": { + "signer_id": "8c023e8a8aa769c51b3a9198ea28e2067a73455d109bd5475dd07df40fa8a1d3", + "signer_public_key": "ed25519:ARY4dSrNrYE6ktcjhGVkNePoE2zSpq36mdhKN3fTWNE6", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "e528a122e6217e29e12162c0c04b3c0fac1a9d893e9b2854ac89a5da504b9be4", + "receipt_id": "CPwan3Nm66uLQier4gAcBdBznaVQTRzz8NVdLSa9Px56", + "receipt": { + "Action": { + "signer_id": "e528a122e6217e29e12162c0c04b3c0fac1a9d893e9b2854ac89a5da504b9be4", + "signer_public_key": "ed25519:GRYJ9dZLRFGzfERMdVAvkL4uF1DR84CT9X96Z5mCuMbH", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "bf10089507eab7423978cfa4dd83a8c5287476bce26609507929997cdd607c70", + "receipt_id": "7nccKAxbSmxRnqSyL4tPEyb5uvaN69QfxTHLV95WBRPf", + "receipt": { + "Action": { + "signer_id": "bf10089507eab7423978cfa4dd83a8c5287476bce26609507929997cdd607c70", + "signer_public_key": "ed25519:Drq3vZ6ndoVSywo6Not9GFUFJ3jSUUupdEN6f7NwkC1m", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "86316ea0b03e99303b8fb1868b223c5548976a3d250749793b25ec2872a1cb7e", + "receipt_id": "Hyxz66eW6s9Nun9FiZ6aAHfbyQgsSSe9rzS56B6Qafiw", + "receipt": { + "Action": { + "signer_id": "86316ea0b03e99303b8fb1868b223c5548976a3d250749793b25ec2872a1cb7e", + "signer_public_key": "ed25519:A2qMGqQxJuGYDPiqLQkXGmVNjxGagh6mJjdH9D4hfaRT", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9c599d6ab77500d535d9fbbe728b7a631b45d0a2249872f090b0c355b89477c5", + "receipt_id": "Gtptbztaxw6XM91fQNQMsfm5qcSn8Z46K1uQCTMgyisR", + "receipt": { + "Action": { + "signer_id": "9c599d6ab77500d535d9fbbe728b7a631b45d0a2249872f090b0c355b89477c5", + "signer_public_key": "ed25519:BXKrh3XCTaZzdd3Yt2n9VgEQY8YffnZBRpkGksqhhr1a", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2244371072686879729910" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9b3aed246ad3825a690fe37a534447359838530add978a26517bbc35b16da721", + "receipt_id": "6RPHF978X8escbNhb63qfmMVpb2exgE8745w9uBnGEtb", + "receipt": { + "Action": { + "signer_id": "9b3aed246ad3825a690fe37a534447359838530add978a26517bbc35b16da721", + "signer_public_key": "ed25519:BSxJrnnVQ6kjsg4tX99DdoTQcChP5y8BQAdxwetFsMRJ", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "7e275210db68d04783386a8b19992954a6fe027a8025e58210c179b28d1cf739", + "receipt_id": "3Bxr5BwVMXwxUuG7v9JtcFHnrWamGoAU3VmbL6ckvojG", + "receipt": { + "Action": { + "signer_id": "7e275210db68d04783386a8b19992954a6fe027a8025e58210c179b28d1cf739", + "signer_public_key": "ed25519:9VT9RYwcQXB14zDUnBSaC9uNksYG8o4N6vxzhpe9C95S", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ec20f015d77f523102b412458564a2ef54121863cfb7dad9f454d83c7412676c", + "receipt_id": "GBUS9x6LS63yLdWCuNK1KVaTFd7KuGm8pv63NqGpf2GT", + "receipt": { + "Action": { + "signer_id": "ec20f015d77f523102b412458564a2ef54121863cfb7dad9f454d83c7412676c", + "signer_public_key": "ed25519:GtkM8WFnLEgL9Ne6TUXgzKS54d54GuJfFfBs86YRFTNs", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d4db0497cfbad28e3b0dd724060f6bfb780dbe3263e8acf779c9ae8f16f63998", + "receipt_id": "ENyVpadta1KA1kXtDrh8JJSVjSZXA9XrpRtnssPVn6g7", + "receipt": { + "Action": { + "signer_id": "d4db0497cfbad28e3b0dd724060f6bfb780dbe3263e8acf779c9ae8f16f63998", + "signer_public_key": "ed25519:FKu8gjwkoURPNYPZQNKmM9Lrzewev27q4vKyVPcBjV1R", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "6c55a22ccf93c51a4ac7f4fdc04409d6bae8617f55643d13c6e7e2f5d7d73ada", + "receipt_id": "8y1sR6sGMf925Kb3kdNNNZpcEH8V9z3ryxmopUSdQ2pQ", + "receipt": { + "Action": { + "signer_id": "6c55a22ccf93c51a4ac7f4fdc04409d6bae8617f55643d13c6e7e2f5d7d73ada", + "signer_public_key": "ed25519:8HtmS1oMpHUzBpz1nvPpRhrtpu3XQUEdGro6ez2a34Mf", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "c2d6dd31401dd18bed6dd7b72a028518b079a4ee34c6573416720a2cc4c73125", + "receipt_id": "6DrinKb5U3dXCyCVhfrJaR6Vbap9cZUPmHBJxMPeTqVe", + "receipt": { + "Action": { + "signer_id": "c2d6dd31401dd18bed6dd7b72a028518b079a4ee34c6573416720a2cc4c73125", + "signer_public_key": "ed25519:E7a7yNXnXUhqMjGup1ZganQfFFbk7j1yd3tfNiUv8DZn", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "fe9ab0f4c8dc72b41ab2a55dbb3c9d5994110926b139f14aea65a98f2b4a8e9c", + "receipt_id": "69JURLNT5N73uUuQKP1AyXSxm1igSSUax9NUEkMHCfZK", + "receipt": { + "Action": { + "signer_id": "fe9ab0f4c8dc72b41ab2a55dbb3c9d5994110926b139f14aea65a98f2b4a8e9c", + "signer_public_key": "ed25519:J8sNAfvzXgj3sfbpDP8Etz2h8h18TUtQsM6jvuRmrTJs", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "79b6e05e6237effbb697fbd795e25cd89e1cf02fb46d842f4b82a025e42c57b7", + "receipt_id": "8DF2K9uTekwDfRtKP2SbM43f9BQn1qXz55NGaEDnU5qf", + "receipt": { + "Action": { + "signer_id": "79b6e05e6237effbb697fbd795e25cd89e1cf02fb46d842f4b82a025e42c57b7", + "signer_public_key": "ed25519:9C84xhDxEofzGfzXzjs3zYtUXPTEM5qK6yXKeEkzhajY", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ab848f5e55f34eac36139d0468fe3e4a8f20eb4413c04ad446b1c90adc72c9c8", + "receipt_id": "7B2KCziD8WZw54WRM4CpdQdLRia9gFH5jDGfmwHpXUzx", + "receipt": { + "Action": { + "signer_id": "ab848f5e55f34eac36139d0468fe3e4a8f20eb4413c04ad446b1c90adc72c9c8", + "signer_public_key": "ed25519:CYXxHAqfLjp9Gun6Xc9QRchrGP35uQJNrQfgyZnR69w5", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "b34b5ce808125be5fe0c7c2d2cebe34b0028b76f0dc09c191e5454e4a9d3fd2f", + "receipt_id": "8JJ5AGFAhr64UjFrm8nX1PPfmAor7XnUdUuu5DmbpxEN", + "receipt": { + "Action": { + "signer_id": "b34b5ce808125be5fe0c7c2d2cebe34b0028b76f0dc09c191e5454e4a9d3fd2f", + "signer_public_key": "ed25519:D4tdVn1s5QBenipizvKx1KbXUsTeFB7VDy4BQUgew5FL", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d86315f3bd9cc2169f224c8c46708c42e53316337fe4f73da028bddedd6ef5b1", + "receipt_id": "AJ2eNdux99dAECEkaGnBunv4B5YtPF8tidwRGUMSTYY", + "receipt": { + "Action": { + "signer_id": "d86315f3bd9cc2169f224c8c46708c42e53316337fe4f73da028bddedd6ef5b1", + "signer_public_key": "ed25519:FZghHpobchb9Xwq3LQQXsV1F2eax96dgbtDtUXKBmg32", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "cbd3eb3fdd1e649180493be73135f5b42ffb6dd3ddbe14a805e5ff7da55c33ac", + "receipt_id": "3g2zSqxzoUFYwyciv97mmHQ5CYZ2tQUKH8fBZfJ2RNNn", + "receipt": { + "Action": { + "signer_id": "cbd3eb3fdd1e649180493be73135f5b42ffb6dd3ddbe14a805e5ff7da55c33ac", + "signer_public_key": "ed25519:EifBinBUGCzUANwQ7HUirzqWSrpWbVPJJSi46XWfFMAb", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "97632a15a2e5a4e8b651af4fa7c09f201ce61c15396476a5ea980d10820a124b", + "receipt_id": "HQLtpHNkiZ5SSaCjXKTjVoJjdjnb6jNNbdwGxhBSpWhJ", + "receipt": { + "Action": { + "signer_id": "97632a15a2e5a4e8b651af4fa7c09f201ce61c15396476a5ea980d10820a124b", + "signer_public_key": "ed25519:BBxGJbz8e6LbZsf3qVLvhRVa5tc6x4REJohgeTrH1ENN", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "bf2e395fa8291c98822818a82e77085d1376625efe748ae6ba4f6fa49fa32644", + "receipt_id": "5e7k9kRPWjJS66ozemyh4jwbJVRXr755R2Zjc47eytjE", + "receipt": { + "Action": { + "signer_id": "bf2e395fa8291c98822818a82e77085d1376625efe748ae6ba4f6fa49fa32644", + "signer_public_key": "ed25519:DsHkZkWXaCED9TYpb8B4mkaC4QsB2SdVWtd1CkmMUJtT", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f65f8c1efd21b7f3e360cefda9b32d436d1324449bede9fe5e78fc6db9d3759d", + "receipt_id": "EnKrgNWjnyXYCbovP3StmZPWdcdQ67PXNjycTJdKtZGm", + "receipt": { + "Action": { + "signer_id": "f65f8c1efd21b7f3e360cefda9b32d436d1324449bede9fe5e78fc6db9d3759d", + "signer_public_key": "ed25519:HajoBSkUGuKGkvhk8yzLHrWwC8h1kfdARypceQrLKCPv", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9e33bfcb688a1d9eb9602423c4fa1be7ac1dfb8cf0c955670e2cc53dde09b514", + "receipt_id": "H1nezbFhmMraS4wcVqtzjWPxptFsysggV6UBDX8aACno", + "receipt": { + "Action": { + "signer_id": "9e33bfcb688a1d9eb9602423c4fa1be7ac1dfb8cf0c955670e2cc53dde09b514", + "signer_public_key": "ed25519:BeZBeHqXKqMLEfpVuDjKFakE9xqX1kmSGZ9k1QT25FBh", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "dcb2189f37930d19268031bb9ded48f601ac7fbedefe463e0bed182f12dcad18", + "receipt_id": "8y189UhNkGVhMt7a3kBUP65p1Eo1xmViueMVdHmiHkhB", + "receipt": { + "Action": { + "signer_id": "dcb2189f37930d19268031bb9ded48f601ac7fbedefe463e0bed182f12dcad18", + "signer_public_key": "ed25519:FrWCkndPFphk9dEQkozcwM8pjJmp3iNd7Ed25VTe1uUT", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "86cae95ab9610267315602b8e9c5deac35def217d3ac469d119a6f35537b4961", + "receipt_id": "3xSnPwoakkthFsH5DjDga4v9ohCDLsmevfdkbHnbHwYH", + "receipt": { + "Action": { + "signer_id": "86cae95ab9610267315602b8e9c5deac35def217d3ac469d119a6f35537b4961", + "signer_public_key": "ed25519:A5B65LMR4Te1vrvRb5khxX4aWXLuQbaWQv5CNVX4vjyJ", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d2d11c58348c0c6e7bab3ff46943166e4614bb767e452054d37f8ba2526296f1", + "receipt_id": "nPcAwL1A3cPAYCGR3GHFtCGeayE8tbwGmTtvta2bMyT", + "receipt": { + "Action": { + "signer_id": "d2d11c58348c0c6e7bab3ff46943166e4614bb767e452054d37f8ba2526296f1", + "signer_public_key": "ed25519:FBwZCPuzBG8q126sPMBSrBVKJwPPM48J2NmT8PHZjWBA", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "dc0395aaeb1563588c46b473457db5000fbc387b8bb0f0d7da95ad2bc38ff94a", + "receipt_id": "GcSfu3im2GoVaHjaNWfkN6pnNVEiLwKWG4H4Sp72E9ei", + "receipt": { + "Action": { + "signer_id": "dc0395aaeb1563588c46b473457db5000fbc387b8bb0f0d7da95ad2bc38ff94a", + "signer_public_key": "ed25519:Foqs6kxkxvU7ta8MFao1tmtrJmJK88FPqdNYnKvWXxXb", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "bd7b296ee5112130d3f8267132059c383018510e0104961d6eac0086949c0be9", + "receipt_id": "8ybECCyPSHapm6Y7GbpZTX6a8D8QaXiUDgepPn7QrTox", + "receipt": { + "Action": { + "signer_id": "bd7b296ee5112130d3f8267132059c383018510e0104961d6eac0086949c0be9", + "signer_public_key": "ed25519:DkeyqBXdoHyZyD8fUHkiiz7NGH2u6bzQ15MVbgXBFN8G", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "6110555fd56d62549f722d080162aedae5068dec1ba2b856e51326daaf1cbc19", + "receipt_id": "3Aut3qHpRXNjY1vJ17Q1eKVBVpjMB1FvoXGFYzFwnoQW", + "receipt": { + "Action": { + "signer_id": "6110555fd56d62549f722d080162aedae5068dec1ba2b856e51326daaf1cbc19", + "signer_public_key": "ed25519:7XtzYT3n5ogWVA3DyXWpDJCs3qh11jU4B9EBsbZJEF3N", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "85d120d634d75910b15f2a22532c840bf7a827499234bcf8dd9f8b582786edfa", + "receipt_id": "DpUpt6CxuLMFLEcw3iF73DBDAG9oK3uRVpT8XnDzZnWo", + "receipt": { + "Action": { + "signer_id": "85d120d634d75910b15f2a22532c840bf7a827499234bcf8dd9f8b582786edfa", + "signer_public_key": "ed25519:A1NBKU2YsTBxh3tRiq1JyLWsiDvGDCHuL6ZzjkbHa2WV", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "64eea8dbb351e58717fad21fb7fe7391fcdbd903e69408c9e10dc10868844af5", + "receipt_id": "2WA9wxECXFZKnkZJeN7zMBJeQcnXbtiesK8H2CBKFiJq", + "receipt": { + "Action": { + "signer_id": "64eea8dbb351e58717fad21fb7fe7391fcdbd903e69408c9e10dc10868844af5", + "signer_public_key": "ed25519:7nzqowDvv6iMuE1GwJAsr1p7MJ9GJqHiUDJwau7cZ7Me", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9e550695d89179b13b87d4d50aa4257aaa51ea414e31c67845cb3fe0b49fc33d", + "receipt_id": "D6Y6vC7LAznzng4VSkb8Nzy7d15LYjeX9fXCLEz5P7ny", + "receipt": { + "Action": { + "signer_id": "9e550695d89179b13b87d4d50aa4257aaa51ea414e31c67845cb3fe0b49fc33d", + "signer_public_key": "ed25519:Bf4caa5nHpniVajitqzn347G5Nu3B92vHYS17fLKitvg", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9af100e2c40fdd1b5f5481b6165949d6eb51ba79a744140667c7e30cf9a26c41", + "receipt_id": "EZNeqgx5PyPYEozbw2PzMmYhVVwRcBv9wZP1Y69KdVqq", + "receipt": { + "Action": { + "signer_id": "9af100e2c40fdd1b5f5481b6165949d6eb51ba79a744140667c7e30cf9a26c41", + "signer_public_key": "ed25519:BRpvwyvJYLCdMAVQANG3qAVZddihStjF4Sg8RXEWU8jS", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "a6a92ac66c4c1c843c5445eff294551306a72ec27b4d4cb9e940aa3808929666", + "receipt_id": "GQwxpfqiQwcciFABkBAMiaTeypk2kHLLYAPNdD44pjAJ", + "receipt": { + "Action": { + "signer_id": "a6a92ac66c4c1c843c5445eff294551306a72ec27b4d4cb9e940aa3808929666", + "signer_public_key": "ed25519:CDaHpmHaeLoY9MHi7Mv4UuhPtFuXxwXqmm7PaCCT4tXw", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "5ece99c2dad18bc360488c55f976c86953177b2d88c895cb1539b88972e2b665", + "receipt_id": "43hRVfQKceZsYu4dJn1GxVbCMoRjez9kc98eeUGNLihi", + "receipt": { + "Action": { + "signer_id": "5ece99c2dad18bc360488c55f976c86953177b2d88c895cb1539b88972e2b665", + "signer_public_key": "ed25519:7P63T5HcjqwZnJYnvL83WMNZWSvp3ghcfjrA1WAemZye", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "cec2438622e4c0cbaca15e76aa09158b8c41ba593509e8bf403e341d9f8cee67", + "receipt_id": "GVx3eCqARts9F3DuqGVpBQw7JE8js1KLnbhzKGp8LAJh", + "receipt": { + "Action": { + "signer_id": "cec2438622e4c0cbaca15e76aa09158b8c41ba593509e8bf403e341d9f8cee67", + "signer_public_key": "ed25519:Ev6o2fTf2gG6LimMc77BcZ1DCwu7GHcXa1oCcVVYjnMC", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "2e36db745cd6e52613cad698b4eeee0a1b460485ccd406b904f095752229df82", + "receipt_id": "Gax4rQgd1SWnEaxLfeCXKvn8diBpabLNUdqQn4cpkYJp", + "receipt": { + "Action": { + "signer_id": "2e36db745cd6e52613cad698b4eeee0a1b460485ccd406b904f095752229df82", + "signer_public_key": "ed25519:47QGfh3D6ZuB2Wo9XBeG9vqZW6LaYNQ9jC9xQuYWeyPo", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "398cb52fd6a20f216aff694fbaec4f318b38de2a15e803876c8938e14c10759d", + "receipt_id": "9vyx9R2QnAobbzq2HAdWtnWudAgzYorsE3J8LfjqN3DW", + "receipt": { + "Action": { + "signer_id": "398cb52fd6a20f216aff694fbaec4f318b38de2a15e803876c8938e14c10759d", + "signer_public_key": "ed25519:4segXL7WVV2BMEt6eMDKr3BJvdya7ZiU13Jf85igpF8g", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "b34ec17c714cd2a33a5eed2f331b479db90d89d0aa5d2c78d302330554a95674", + "receipt_id": "6Dgnbg4KPNqHCBS793KBRhkUb79KvFtMHQCq8qQB1hnU", + "receipt": { + "Action": { + "signer_id": "b34ec17c714cd2a33a5eed2f331b479db90d89d0aa5d2c78d302330554a95674", + "signer_public_key": "ed25519:D4wdY6PxzTQBBeH9VAKgvWvUtXHRGEq18m3SBi4vm8d1", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "b1b7c86ce1fb46a12003eb437fbcd2dfc8e7667046c9a9f24a7c7c633936f888", + "receipt_id": "DKN7YveuEoSEpmK12u4uq7DBAoH6XaeW2TggX9Ne9ngD", + "receipt": { + "Action": { + "signer_id": "b1b7c86ce1fb46a12003eb437fbcd2dfc8e7667046c9a9f24a7c7c633936f888", + "signer_public_key": "ed25519:CxjhfJkzM2AL9gxaptN67s1qKSPcBxwaTPCPHKSBDNXh", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "665f03be2d015e96a40c92f8bbf7d78a7afdf551d9fc46c12267035b5c2f848b", + "receipt_id": "7A1VCXp4ErpCskYhj3E4FUYs1bny4QieUNseeBJ1uqEE", + "receipt": { + "Action": { + "signer_id": "665f03be2d015e96a40c92f8bbf7d78a7afdf551d9fc46c12267035b5c2f848b", + "signer_public_key": "ed25519:7tccmBtofbVHCc2wYXZtUAuTVNHLof72csxBGMSdhXWa", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "2ba0d812e1849ba2274f6547c113b78b0cb7a63fdc951252bd07ec40d2a9c3ea", + "receipt_id": "6cCdFfGExsRfsCxyxVkh7RAXoQWj4NWFKdEHrqSv1jVt", + "receipt": { + "Action": { + "signer_id": "2ba0d812e1849ba2274f6547c113b78b0cb7a63fdc951252bd07ec40d2a9c3ea", + "signer_public_key": "ed25519:3wJnNcU22nWDpYNrMN2Kg47Hv6hrhKEiPa1WfyyLuuZB", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "e878fa4447575b4c3978bfd0d81015368a48e15dea0738ae3b158bf2c7c81789", + "receipt_id": "BYwJkwaJ2oweKPoYw3WmopC3WQdYjZbQYMDdhun167kx", + "receipt": { + "Action": { + "signer_id": "e878fa4447575b4c3978bfd0d81015368a48e15dea0738ae3b158bf2c7c81789", + "signer_public_key": "ed25519:GeUab3rjc9ifKukpJys7pbKtPHZ9g1YaMp7qvm7Mo6KJ", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "977d37d51ad52025f814e572a2ac749e76d2035912c9fdcf5012941b48fe9b71", + "receipt_id": "CtV5wtg2FqoyYMLij2yFmgi4eW7rk4ghqN2pyFzVK4vD", + "receipt": { + "Action": { + "signer_id": "977d37d51ad52025f814e572a2ac749e76d2035912c9fdcf5012941b48fe9b71", + "signer_public_key": "ed25519:BCMJjyvtTymrf4yYQQcxKjrCTFzj2NA1BdJaCtU5kpHW", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "4a02b292c00f310d845cd881433fd55f516d0b232f0ca4d5bbdd9651f578a94a", + "receipt_id": "J5qVd5qAjFRFZm49XBbjzrpA41jJNK9j4Phb24xMiqro", + "receipt": { + "Action": { + "signer_id": "4a02b292c00f310d845cd881433fd55f516d0b232f0ca4d5bbdd9651f578a94a", + "signer_public_key": "ed25519:5yuZDEwRvthNswPnHBVcso8m8w8eRcBAw26ZuPx9DzEM", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "c6b39564526aaad07bf30538916ec07d4a369cf048c12c9321c969ab598ce137", + "receipt_id": "GA4upJTLz3Qoa1QmRyN1Gye9oPFUgYGTyLpQAZeG7tdy", + "receipt": { + "Action": { + "signer_id": "c6b39564526aaad07bf30538916ec07d4a369cf048c12c9321c969ab598ce137", + "signer_public_key": "ed25519:ENeYq4VRZRii154ZU4ppryyTMCHjf9aMJe474CsKaJmL", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "efe1428295b1e114fa4cbf816e509a3edf52ddc8598d64d0a477311b95600e38", + "receipt_id": "HFVV4YoZqQQyPgnes6gKwjyK3rsDADVmtC4azo6yJhv2", + "receipt": { + "Action": { + "signer_id": "efe1428295b1e114fa4cbf816e509a3edf52ddc8598d64d0a477311b95600e38", + "signer_public_key": "ed25519:H9PfKXJDFEHs1sGfsHKxHNgtsNgSLW4BAtduDsjHjvZu", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "a015838bb5bf36a599767ee230bcc6aba759cdf5c913af47356da20a593fd9f4", + "receipt_id": "A7Czd4NhwFuPkp4ywbtKNHPsomWGjXN4JCfMBQ6h5DZ1", + "receipt": { + "Action": { + "signer_id": "a015838bb5bf36a599767ee230bcc6aba759cdf5c913af47356da20a593fd9f4", + "signer_public_key": "ed25519:BmuFzw2AwcFcHAV7wAvHTfA3oWxyEK2aQjDf4eHqsHmm", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f19767f7d037901a2bb744b1d083fb21208d41abfabf47bbe240013ea8cd246d", + "receipt_id": "5sEqrRnJw77xjUf4LUHvoEihKnJzqXjctuffJzVNqQ5J", + "receipt": { + "Action": { + "signer_id": "f19767f7d037901a2bb744b1d083fb21208d41abfabf47bbe240013ea8cd246d", + "signer_public_key": "ed25519:HG5AFaLuCQfu1LLy24LcxWnGqYYWSiaF43smaeU4HGuz", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "c91eb75e4c9866d7061125fda55aab4cf466706f3966fbd107081cbcabee45d5", + "receipt_id": "AoRCjYHAdg5JgYbjoHgdmzcnmgFpY5TkoJ3XPnuDmGN", + "receipt": { + "Action": { + "signer_id": "c91eb75e4c9866d7061125fda55aab4cf466706f3966fbd107081cbcabee45d5", + "signer_public_key": "ed25519:EY67YkS1G6Ag457mfJz7zahWmiBbc9pX2Ui5ccJpwTkp", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "bd096f601f98f22c4ebd977259628c6c79edf5aadcd52fbcc45a0fb729fe4a08", + "receipt_id": "Hz1rSK1FaPC7pjdJ8jUT2W8jWE6N9Te4yiW3AWcnfVYu", + "receipt": { + "Action": { + "signer_id": "bd096f601f98f22c4ebd977259628c6c79edf5aadcd52fbcc45a0fb729fe4a08", + "signer_public_key": "ed25519:DivQADhVUq6RbrfbAB3QL3srBDhrKk1hrxSjDUEKyQ1D", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9ff6ff2ad63f9183e64d0c7eae2f57ab4e42d311befaceb8802b5ec034293d5c", + "receipt_id": "FQkPBwwHv1iS17JjT5tMX3xN4vQUJoBLc7YWFsmhBJmZ", + "receipt": { + "Action": { + "signer_id": "9ff6ff2ad63f9183e64d0c7eae2f57ab4e42d311befaceb8802b5ec034293d5c", + "signer_public_key": "ed25519:BmSGcKTK1AbKCuBoWBwozSCerkPeMYaRFNbMBiA8mBbH", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "783bb90d52468e7faedb0141cfbe032d7138617cfbef1fee231b36ef34e53379", + "receipt_id": "G184YmDQmRR71uVBN17vtfuaGfbwnvAbF2YedVffhBeA", + "receipt": { + "Action": { + "signer_id": "783bb90d52468e7faedb0141cfbe032d7138617cfbef1fee231b36ef34e53379", + "signer_public_key": "ed25519:96Lk6DmS4uTKjjWxKgJZZUMasA35Ckqynau8GqeiLe1W", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "b957b9e970ebf7b3f49cb9458f7b0556ce3b275a7ff819c5adc687d2f76fefff", + "receipt_id": "5LcxPP25fxizdcGCq59GW8mgPoS6nJukjrcy2iCTXEqg", + "receipt": { + "Action": { + "signer_id": "b957b9e970ebf7b3f49cb9458f7b0556ce3b275a7ff819c5adc687d2f76fefff", + "signer_public_key": "ed25519:DUW1ZHgfJHxQnSsZD4MQc2L1cUbuw7KB2ZG6QSrFKkgE", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "8e972caaeaf483332ab5efd66a5348f8f7ff8c27ab0e1d3e517171d3bc27ba78", + "receipt_id": "BWRW2f2g9j97c2dVJKDNPTRtgQUFviq9uLtLuynv4jD2", + "receipt": { + "Action": { + "signer_id": "8e972caaeaf483332ab5efd66a5348f8f7ff8c27ab0e1d3e517171d3bc27ba78", + "signer_public_key": "ed25519:AbcbNNohYvM5HNic84YAjSsVA3v1Fz3bTUdrYV6FDufm", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "8f74f92c9ac8065fcc4befe44aa087df0c1e32f72912dfa4608decdd1d565827", + "receipt_id": "7vVp6jNnS68DfnVTz3qGs9t9dCaCp3hwQ3Y1PsKrZxFR", + "receipt": { + "Action": { + "signer_id": "8f74f92c9ac8065fcc4befe44aa087df0c1e32f72912dfa4608decdd1d565827", + "signer_public_key": "ed25519:AezkeiivjF9SKcm4JDmPDTtm3H1YSCDNYKCUhvcxvH1G", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "898134e580c7e05ec5b765a499cd046348ebba06ed9ad622adb7d32101ee4180", + "receipt_id": "2VS42MVPAgHUShH7XvVTLJEM8P6ffzxGNnFout37N3GK", + "receipt": { + "Action": { + "signer_id": "898134e580c7e05ec5b765a499cd046348ebba06ed9ad622adb7d32101ee4180", + "signer_public_key": "ed25519:AFm8HUx9ZaqyqAwSGAcW3iXf787iwjzTgnLDpksuRPuZ", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ac45f601af54cf7190dbdb36123a19c296217c698579098b4177a53b95e65388", + "receipt_id": "9dtTB7Fos32QNPpQ8oj9HDCKUhqWLVzQcfJ45oRR2sjQ", + "receipt": { + "Action": { + "signer_id": "ac45f601af54cf7190dbdb36123a19c296217c698579098b4177a53b95e65388", + "signer_public_key": "ed25519:CbUzskd7J6uToaEKsyxpyewPM7f2FuV3F3KyC5mCUVjm", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "e9ff82c731c68f900b5ac9cd437c1a603e75ddf913808224a27bac2d12f78e93", + "receipt_id": "6KTKN9HV8sgsnCGb9cKusGw5Fd4CfMgAR5WGwqeBBCnL", + "receipt": { + "Action": { + "signer_id": "e9ff82c731c68f900b5ac9cd437c1a603e75ddf913808224a27bac2d12f78e93", + "signer_public_key": "ed25519:GkRyBcMypeCkV5pncHisSAsuF9C99oCk9yBrdrJmiiHg", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "a4f83b79f0cd51ff5f0e944d8379aebe0910b6461f633d222635cf9406700c1e", + "receipt_id": "WyxCJQ8KqQvviZpmb23aNQvYEESLk3CtJ2vTHa3nokQ", + "receipt": { + "Action": { + "signer_id": "a4f83b79f0cd51ff5f0e944d8379aebe0910b6461f633d222635cf9406700c1e", + "signer_public_key": "ed25519:C6yQDphQyoEpVRcAFG4B3miCa3nRbP6U9dBhKjYhT6m3", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ca56d5327c525b64338555a43972c5a8982a878d86a0e12f6487552ccbfb6e4e", + "receipt_id": "CwMfMoyPKCSzmgWD6jU4v73i35tT8mo4xARtoyqF6jiv", + "receipt": { + "Action": { + "signer_id": "ca56d5327c525b64338555a43972c5a8982a878d86a0e12f6487552ccbfb6e4e", + "signer_public_key": "ed25519:Ecr9ifnbevjxw3SPBputeiPoXrb7EztkFvvcFRwBgsyX", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "5e02467f987a3a30bc2d47c8b5514e027d6fee93353df6f6e7848dca0129c1b7", + "receipt_id": "FbpjrU8qdnnhBteXVDhXhqB9uMKczUHm4opHGt7Masqq", + "receipt": { + "Action": { + "signer_id": "5e02467f987a3a30bc2d47c8b5514e027d6fee93353df6f6e7848dca0129c1b7", + "signer_public_key": "ed25519:7KyLV96KfRABw2xQL7M3pNMDtqntyLtfZX5pKdowpYuU", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "8430793009aa9e43b87f3c6d073121ed8ddbb2a6901c20fe88a065499a22a3aa", + "receipt_id": "BHsiGNVKybeAnum61bfR2xEDYfhNwoVZCSvuQJefQgVW", + "receipt": { + "Action": { + "signer_id": "8430793009aa9e43b87f3c6d073121ed8ddbb2a6901c20fe88a065499a22a3aa", + "signer_public_key": "ed25519:9u1goRuqXfGKafRqfikustoeD6XSv1xty7GpfKEtHpbB", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "1704536907b138a2cc7ce71a6662017aaef8947b4f54f1c80e484a34e8d78358", + "receipt_id": "ADwGRjEfFe7bTbFb1Dbsctsd7vqcoQE87fiwXXAnFn7P", + "receipt": { + "Action": { + "signer_id": "1704536907b138a2cc7ce71a6662017aaef8947b4f54f1c80e484a34e8d78358", + "signer_public_key": "ed25519:2YrCmJxSHVBEmQNJBb5vBECdSnJAa4PxgMdTeUYzkKJF", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "99e2894a2819a8c892a78ca4423e26f7dbfcc5ea2dad662b221e0db657b4bbad", + "receipt_id": "FiR5v2rA5BPcc3gXLBJMDe2YpYwxEEMkaXoo88cRio3d", + "receipt": { + "Action": { + "signer_id": "99e2894a2819a8c892a78ca4423e26f7dbfcc5ea2dad662b221e0db657b4bbad", + "signer_public_key": "ed25519:BMhjCeFp9mGQt9aXVVERTK4zByH6pWgcBjzrJm6odb8p", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "a25ed719f9a6afc27a18a3af0473ad4e6c8a377eddf1198fe4b5dc2f623bbfa7", + "receipt_id": "CioZVTnj3P94rWsaMS4xwZ7YtN8psyKvZhddw2oeCtBZ", + "receipt": { + "Action": { + "signer_id": "a25ed719f9a6afc27a18a3af0473ad4e6c8a377eddf1198fe4b5dc2f623bbfa7", + "signer_public_key": "ed25519:Bvpvc6fuGgZ1PxiHyJhbkswESp9Cc6uEiKNtkbYCfJFQ", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f2f29e305a59ec0514136d7fdc93821213df769a13d25cca0bd697e223aef1c9", + "receipt_id": "9B4mq6yw15ZXBHZN1iCPvT4oAjY5wUGQxrrarE6m7XVh", + "receipt": { + "Action": { + "signer_id": "f2f29e305a59ec0514136d7fdc93821213df769a13d25cca0bd697e223aef1c9", + "signer_public_key": "ed25519:HMNEepiPGWcZRsRbckrhhaWgVjNqWKFcwU2x3Pnq8dTW", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "41f21acf6c8de8a97292963c24e95981b516fab553540bd258af6abf79b2e105", + "receipt_id": "7pKVJTQxjn5oKrgfo98iJpFqb9uevLng9hoJLWpS873T", + "receipt": { + "Action": { + "signer_id": "41f21acf6c8de8a97292963c24e95981b516fab553540bd258af6abf79b2e105", + "signer_public_key": "ed25519:5SRcuKt9Aywyyr93D9dHwFMQtGUcrQikven6CpBEbmYp", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d644260f8558ef4e47c16b2aa2684924148052b78ca599123a6aeebc99c126d0", + "receipt_id": "JA6fz8BfAfrX3gj5Cfza6Ts766Ef4Z67tTZgEn5fWtHR", + "receipt": { + "Action": { + "signer_id": "d644260f8558ef4e47c16b2aa2684924148052b78ca599123a6aeebc99c126d0", + "signer_public_key": "ed25519:FRQX4k5UH7GzC8Jqn72ASxrNq2MVf5VvexeYViTxkbZZ", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2257564517856291433790" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "6d99a67e7fff68871bdd7b66a1fc9eaeef5f65127b772322b8e171b7a60e0b5b", + "receipt_id": "HLocyZ6Qx6rXbRYgVsLTw7hbgqgdSyzmZQaZNnHKgAjt", + "receipt": { + "Action": { + "signer_id": "6d99a67e7fff68871bdd7b66a1fc9eaeef5f65127b772322b8e171b7a60e0b5b", + "signer_public_key": "ed25519:8NqL38gcX4msSWtTVByNsgW3mXnjJtUepCk185ENMfzA", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f24c2c96377147ccdf762d3f277c591649d1ee8e8f10a7be98a96b1b6a24c8d5", + "receipt_id": "8TnNYVaVSdEXGdJ1ud5aQ1kDiSdicdXarkytJGzEYTBT", + "receipt": { + "Action": { + "signer_id": "f24c2c96377147ccdf762d3f277c591649d1ee8e8f10a7be98a96b1b6a24c8d5", + "signer_public_key": "ed25519:HJq2qZiVEv8RdRPNa72TAmHtwWLY1a5qNRvDyaTMAXsa", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "650aa91f367e93c4296db463626ebd561f282a641296a03ec0b8715bf57d3dbf", + "receipt_id": "12Skb2WEhMc78tJBVnaUQjsgKA3vArgCfK1sB27XWY1C", + "receipt": { + "Action": { + "signer_id": "650aa91f367e93c4296db463626ebd561f282a641296a03ec0b8715bf57d3dbf", + "signer_public_key": "ed25519:7oRc8tAAVJ26wqf2ACLa1u7GQj5M7jwn1HGzoaVexvRt", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "43b5867d7b4f8d4fafa4e6c943a7be85b5012b34e15db5a758b64fe5d266a826", + "receipt_id": "DJtSCTEKutA33K1Woak1dg4HtqgQFkbHh9bZdv1SqY8J", + "receipt": { + "Action": { + "signer_id": "43b5867d7b4f8d4fafa4e6c943a7be85b5012b34e15db5a758b64fe5d266a826", + "signer_public_key": "ed25519:5ZJrkGwo3XvGJVnw2KmsS3WrT1Zt21SwjRWJgv1Z24yb", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "8919b79b3169550ce520b9d475a7f4ccd258dec250c022dc1065d3b26688a9d1", + "receipt_id": "DaENK2zXEoe8tnVzZ2pvsSWt8qc8xPha8PspD5sZLSHB", + "receipt": { + "Action": { + "signer_id": "8919b79b3169550ce520b9d475a7f4ccd258dec250c022dc1065d3b26688a9d1", + "signer_public_key": "ed25519:AEBbk87tRtbBUNySqNF7eqydP8k8KhEZXcozUt9c4br8", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "83b026173d9ecc2afd0619b7408e9a0d4745f6d4c781b77b8994c6f542445b84", + "receipt_id": "7cBSLVtZXzkQqCBm8Qva5y2kjxFpsQZBqubf3qNf1bhm", + "receipt": { + "Action": { + "signer_id": "83b026173d9ecc2afd0619b7408e9a0d4745f6d4c781b77b8994c6f542445b84", + "signer_public_key": "ed25519:9s4CKxhr1fsXQL2ScPzcar12pDETgq5UefF16uz5LrwD", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f019135ebc5f67c67f4f0cde906c3fed9c4be66aa0fad6d7cbb08e6a73f33325", + "receipt_id": "86GAHpzHEHLfBYDTi6K68ZX1YafMitEiH5jmi6YP5dYa", + "receipt": { + "Action": { + "signer_id": "f019135ebc5f67c67f4f0cde906c3fed9c4be66aa0fad6d7cbb08e6a73f33325", + "signer_public_key": "ed25519:HAF2RSg5hYUffzuQsUtYVzSz9eZ9sp5jbqjHjs45mp1A", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "b828e1925025d7e12d1cf1268d6d9316c9972f85913bf302458a33516c3ebaef", + "receipt_id": "Afwpy7H5zh1mXcXyfSkxtnAQqBtxrgA9A9gjLj9xoakr", + "receipt": { + "Action": { + "signer_id": "b828e1925025d7e12d1cf1268d6d9316c9972f85913bf302458a33516c3ebaef", + "signer_public_key": "ed25519:DPtAyBmpJsk5SeyEgusWMC8nZ9T2PdGLRdfDLMgeL6zi", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f7f0ae525376a7ba7ee16ae67d1f1732245b651c0ed27bfaa998c26128ac60af", + "receipt_id": "BCFWg1X2HcTdAMaArUyPETpEd9HAjTE5FahgBhnwDjj2", + "receipt": { + "Action": { + "signer_id": "f7f0ae525376a7ba7ee16ae67d1f1732245b651c0ed27bfaa998c26128ac60af", + "signer_public_key": "ed25519:HgrZXWb4vF3KCLUNkcPrmMnA7nhzcBrn1ZF1ybEp9Mze", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "adba8c40365f1eb5485ee9978d04c7e1c1af93f1bdc0fb6cba251d9434e608e2", + "receipt_id": "76ozuT3h4EPqPkHYi1UHJTNfmT4a9sgimFXyvVvVXiKz", + "receipt": { + "Action": { + "signer_id": "adba8c40365f1eb5485ee9978d04c7e1c1af93f1bdc0fb6cba251d9434e608e2", + "signer_public_key": "ed25519:ChAWuR5ChxYJdaxPBw21DnnhWHmDTTt2HusFw8zrxSwT", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ae624fb0f178da614a1115aa4bbbf48778c8388fc816ad748ef95d0664373156", + "receipt_id": "DgZbtwwa2AEHuVvPryqB157S7HbYHUymaV33rhkKbX2C", + "receipt": { + "Action": { + "signer_id": "ae624fb0f178da614a1115aa4bbbf48778c8388fc816ad748ef95d0664373156", + "signer_public_key": "ed25519:CjitQub8x382XAaR8PgyXL2AQs8wmZvdcGnV1h2Zkczh", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "b505a1d751eb9a6858ed688b7df320fc0ed8908f2b26d575f8f09cd7952cc04e", + "receipt_id": "3fg4c1wzYmqp38jVDPm2mtj8HNFmLGhLVTkMyr3QMEdV", + "receipt": { + "Action": { + "signer_id": "b505a1d751eb9a6858ed688b7df320fc0ed8908f2b26d575f8f09cd7952cc04e", + "signer_public_key": "ed25519:DBdmvCKLReBF3fdiM4a7Xu3VpaT31dXqZsdVfnmycySD", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9478f3b6b893a5cc4dac88d3f4bf7af89f85f1eff3ad64c4b8408de55cb7fdcd", + "receipt_id": "7QUKtfyphbFL7haDjzrrxBKdUbELjCb4xvYUFhp64MTp", + "receipt": { + "Action": { + "signer_id": "9478f3b6b893a5cc4dac88d3f4bf7af89f85f1eff3ad64c4b8408de55cb7fdcd", + "signer_public_key": "ed25519:AzaJyHrW31EvaiGg79k2WFMv78chR9p4fsuhYT3SUmaU", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "cb14a54738d1c8baf2f2d04a66d02e1db15b187970df884263e5a2e9625cc6aa", + "receipt_id": "7iLmVgfEi2VA9h4phmBx4YqqccxD9yw7qoZPcxsxLHEa", + "receipt": { + "Action": { + "signer_id": "cb14a54738d1c8baf2f2d04a66d02e1db15b187970df884263e5a2e9625cc6aa", + "signer_public_key": "ed25519:Efk2G7iMWPpm7LiYQ5VBPRo9AGohMPtrk7ysK7kdLHX7", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "5328210abf0698282d511648365ad4cae758e1b9a137451c8fbd5ad88847b578", + "receipt_id": "GUo75UtbqYYJnmviQACDsv5vq2a3A2RSm1atVQDAhttm", + "receipt": { + "Action": { + "signer_id": "5328210abf0698282d511648365ad4cae758e1b9a137451c8fbd5ad88847b578", + "signer_public_key": "ed25519:6bcL6f1JPsQt3rpFXfuNA37nmuT9VyBdwuYz2y6UX4gw", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9bdbbf5ef01689923d442a17d28e9e97c89ba8454a666eaddd488fd9c54eedbe", + "receipt_id": "3USKx6XiK6cmaasZGzWiRjkFk1iSLo1qgaGk8HnAuRZ8", + "receipt": { + "Action": { + "signer_id": "9bdbbf5ef01689923d442a17d28e9e97c89ba8454a666eaddd488fd9c54eedbe", + "signer_public_key": "ed25519:BVQYGB6NwHUanz3qQmJt5r3xJjkpS5VgVY7W45BpzLRB", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "5ccaef9f129a4804777173e64340f7d44e25de71c31a8fe244d2b533e2714d64", + "receipt_id": "73XSyhuBv2DVJ8jC3ZQyM2cF4UXRNo8JErp7sHv6tDyW", + "receipt": { + "Action": { + "signer_id": "5ccaef9f129a4804777173e64340f7d44e25de71c31a8fe244d2b533e2714d64", + "signer_public_key": "ed25519:7FDzBNAqtEkXT6p3NDLJTLpwF55g68iBxkKXnboz31pX", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "cf465e343c378822fce95abd46380b5f5a1992e9e06a1d834e49d58ab300e2b4", + "receipt_id": "9wbP8knAi2SRhfoKBMj1raUB61Bob2Xv4QoKUxU9tLkT", + "receipt": { + "Action": { + "signer_id": "cf465e343c378822fce95abd46380b5f5a1992e9e06a1d834e49d58ab300e2b4", + "signer_public_key": "ed25519:Ex7dP2pkGpzED781G66TqMR9TFXokHcoirbF3eDrY1mM", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "0f314cd2020fad3179ba83a28122eda11d088d3ca9e6205e9e8936545b45e872", + "receipt_id": "8F1865aN3941uGQvuJr4aYHnn2gBvWH6RLsJVdrfV6uk", + "receipt": { + "Action": { + "signer_id": "0f314cd2020fad3179ba83a28122eda11d088d3ca9e6205e9e8936545b45e872", + "signer_public_key": "ed25519:22Jia14cj4u9iuqK8ZETdbZgdvB1F2Y9S5BcxnMdQQ1X", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d7220ec21162677c55ed56336a15d0a6c6e2bd14f0bbcdb99cb8e6d1629041f4", + "receipt_id": "CFEGBkTZEMAj7jfn8JUfeMMdHZvgD8kJSYGJfvGPBdJ6", + "receipt": { + "Action": { + "signer_id": "d7220ec21162677c55ed56336a15d0a6c6e2bd14f0bbcdb99cb8e6d1629041f4", + "signer_public_key": "ed25519:FUnmziBVJWWwnVou1U5nQ24GwsPgL22GMTkPwhFezkqH", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d51a71377efe909903134a3f9953291cb3fd68bb267812fe48a3517599d343fb", + "receipt_id": "9Ea9EJ7XgrEgNQRZh6goFQfmdQH9ALmnH1c3Zw2MAEQT", + "receipt": { + "Action": { + "signer_id": "d51a71377efe909903134a3f9953291cb3fd68bb267812fe48a3517599d343fb", + "signer_public_key": "ed25519:FLsE4rxm5cEPT5LK43DW4FoMqN6gKoTRu8unvzqfqHmc", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "82daf2e1fffa0b517473a316aaf011d1ea7513a893d49e5de70070cb5b44af4c", + "receipt_id": "EPHv635byx216STMNHagq9ucRykqTVH5GqLn5GwKCmxz", + "receipt": { + "Action": { + "signer_id": "82daf2e1fffa0b517473a316aaf011d1ea7513a893d49e5de70070cb5b44af4c", + "signer_public_key": "ed25519:9ooe8JoJePvzAqB47RfaJSmhfFuZ37ZLqMzX5RVu9s2B", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ba7befcf795d317f30a2e4c343c9f7fe571ef549b7266fcd61fa7b374d48bfef", + "receipt_id": "5mQuP2pTiGsCPtrS5khypyqCP9PkAaaqFPP2rVVsX8aS", + "receipt": { + "Action": { + "signer_id": "ba7befcf795d317f30a2e4c343c9f7fe571ef549b7266fcd61fa7b374d48bfef", + "signer_public_key": "ed25519:DYxSeAWTA772diav5w7ceskjUhuKy9m6SFBY9YaRPjy8", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "45fad66a48dba78d6aa2dc08ebcce6ed87778b5cf51b4d5249ef779235313169", + "receipt_id": "5B4U8FZnqjexyTbc2QEC9pMtaJYQTZJFh3V51uSLNJEy", + "receipt": { + "Action": { + "signer_id": "45fad66a48dba78d6aa2dc08ebcce6ed87778b5cf51b4d5249ef779235313169", + "signer_public_key": "ed25519:5iAySh8T7xuqCfY6PjcJbrdAiAibLewJjkBoAJMpRdZi", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9861cd896b9d56cd3a5b99c7d39446e17a126900fb7dd00de9fc2c1b7b7119b4", + "receipt_id": "CVaYtAqhg44zBdyi8a5u5DXTrpkiGfccBhmrjHfbvMKE", + "receipt": { + "Action": { + "signer_id": "9861cd896b9d56cd3a5b99c7d39446e17a126900fb7dd00de9fc2c1b7b7119b4", + "signer_public_key": "ed25519:BFqU7QRwnKAS6aVVZw69URCu7fMP5xZFoQHv7Jm2aeRR", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "da620418677abef89d7a5aeab0c00ea3de22f3b0698f3353f11b767810ae6718", + "receipt_id": "3T3zWLmdacE5vioEwZSzeWS81sYAeKQhiouaTUHq1M7S", + "receipt": { + "Action": { + "signer_id": "da620418677abef89d7a5aeab0c00ea3de22f3b0698f3353f11b767810ae6718", + "signer_public_key": "ed25519:FhUZi9mAXVC97DwVzD2hyZAuTnR8R6DmUhBahpgShuyh", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "40126a8f80593083ac59fd31f70251907a0c6867027d55d8a2a0f22b1cba4538", + "receipt_id": "7dSneVRswXuTsGAJ9M6wmRGqfWbhc3p5mUBwY7DeG2jm", + "receipt": { + "Action": { + "signer_id": "40126a8f80593083ac59fd31f70251907a0c6867027d55d8a2a0f22b1cba4538", + "signer_public_key": "ed25519:5K7P3brFn77AJk6ZDwxgdrdM31ZRkcg3cjVDfHJq4svP", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "3579746fb3d61a6a4e41d0a399cbad55f88b3c5f383604c6dd195736e581dac4", + "receipt_id": "b3QbGFf1GeVn3aQsKoAtfTisMWZat9KETJ8bBhf72W8", + "receipt": { + "Action": { + "signer_id": "3579746fb3d61a6a4e41d0a399cbad55f88b3c5f383604c6dd195736e581dac4", + "signer_public_key": "ed25519:4bk2MD1FTgY16SCWDH7ATvTPaRR5frE6SMBR38ZXb14f", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f6a8a0ba0d93cfc32e8f8ffb2440876622302108181c1e945907100a1659684a", + "receipt_id": "HRBYPHTkbhJvss2FXUpLfXBkYpZrYFUTzLT6wAuL7Nez", + "receipt": { + "Action": { + "signer_id": "f6a8a0ba0d93cfc32e8f8ffb2440876622302108181c1e945907100a1659684a", + "signer_public_key": "ed25519:HbrRt3CDBW3gWuFwG2KaApmtQgrSXixyniRfpr21mGz5", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "3a9898a042aa470c727bc58eb48d519995cf85c5a3b3721885a92b699daedc57", + "receipt_id": "CazaNhsfVHf7y9bSc5qUubEvjaEon2KdSrwrj4c7xAVJ", + "receipt": { + "Action": { + "signer_id": "3a9898a042aa470c727bc58eb48d519995cf85c5a3b3721885a92b699daedc57", + "signer_public_key": "ed25519:4wjbzZm5iPaYybJEHeeXmqUWMuGk8998KmAV6m1CPjbU", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "5393bed5a91c753755b82ca3107a921d56538e807503385139055f0551923e5c", + "receipt_id": "2vfXLWcnWq3JRw24D3Rf5ve8zJJiE6g435o5d1gKAW3e", + "receipt": { + "Action": { + "signer_id": "5393bed5a91c753755b82ca3107a921d56538e807503385139055f0551923e5c", + "signer_public_key": "ed25519:6dFWLJNyHaCCW5uYx138FiKJWa3dcHkxk8Pc92Leh4QB", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f748e2a05703f981a2669690ba780dda74c4119793caf89fc2ae6c47d0578a29", + "receipt_id": "7kRdZecobhRiaytFh5cnXYDneZFDxGJgMN1izuUTANxr", + "receipt": { + "Action": { + "signer_id": "f748e2a05703f981a2669690ba780dda74c4119793caf89fc2ae6c47d0578a29", + "signer_public_key": "ed25519:HeJAN5pBtkFsNawXKc3dzG2ZLwMD8TqsQN9NPrrHHDqz", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "804e58971dab4d508490a15dbb2aa8d0dd1eeb58faa1786bc86b142394a10f4e", + "receipt_id": "BM3yVYYYHcVKRFznZsiyPRY6Y3VTPBrQmYNHZPtvoUze", + "receipt": { + "Action": { + "signer_id": "804e58971dab4d508490a15dbb2aa8d0dd1eeb58faa1786bc86b142394a10f4e", + "signer_public_key": "ed25519:9drUYms3qjW8f3Q7NCk4Sy66ULvasqiYEsLbDt6GPEdK", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "c80af0ef70b7d66b8f2a50a7b80c7c621bb1557e7cdc812393403f99d636f67b", + "receipt_id": "C9vofCL7T4xcurA1KZgh8KJnDXGTLW7B7YN61G6fqot7", + "receipt": { + "Action": { + "signer_id": "c80af0ef70b7d66b8f2a50a7b80c7c621bb1557e7cdc812393403f99d636f67b", + "signer_public_key": "ed25519:ETtDXW9xmzqiWBJLofVW9xcHuSv53grX5MTfAM8RzYSn", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "e7c75ed6746951f3b6380f0f985d464d674ce144bbaac051534351da6dceb8c0", + "receipt_id": "CobQAKqz6Lj9QJsHd1VZTnA5b1rMxMNk6LU1boM34FcQ", + "receipt": { + "Action": { + "signer_id": "e7c75ed6746951f3b6380f0f985d464d674ce144bbaac051534351da6dceb8c0", + "signer_public_key": "ed25519:GbmW9AmgkJC1Kkz3zEgLG8YuQScxEWjHRZdHDcqMSgV5", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "046cc192abb561d4c7d1bb4eca257cace26406444b731f263c123b62616bf206", + "receipt_id": "DhrNdiw4GxuDUb7gEoWTBD27Kg4g8qbzdVYcEyJwUENg", + "receipt": { + "Action": { + "signer_id": "046cc192abb561d4c7d1bb4eca257cace26406444b731f263c123b62616bf206", + "signer_public_key": "ed25519:JGpHbCgmoG4bc76v8KQdLvAWKbzeyV5LqMhzFNfg1id", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "50824a4efc7cdcdd6df31f8946aecbb7a0f17e46fc76a7f537c22c9c23a29670", + "receipt_id": "8mUTQz2QNQpxJTcbEDBstYtA88PY9xxCwUBZHUvB5hgP", + "receipt": { + "Action": { + "signer_id": "50824a4efc7cdcdd6df31f8946aecbb7a0f17e46fc76a7f537c22c9c23a29670", + "signer_public_key": "ed25519:6RGr2EXWPnJn4cc2EgGH68TNPAL7YiY79tpTVZZckCm5", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "8d0cf384ebf5016c910aadc8f826e28af9811b8441eddbe8d0a0d973c890c724", + "receipt_id": "ACPW9XxHkBjmwu5QCM5ogxvyFuLHrvVLRJjSgPU4uv1Y", + "receipt": { + "Action": { + "signer_id": "8d0cf384ebf5016c910aadc8f826e28af9811b8441eddbe8d0a0d973c890c724", + "signer_public_key": "ed25519:AVbwVb8LnkmD7osi2JoA7XLNXwBGfdCVBUd3aEcK8t1Z", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "e5a3c4f7a02f974b47155a10c517a09b0911e1a8d562e1bb09057dcedff0210b", + "receipt_id": "6GTf35qiVU41g3GMZNJXKbmnWVNoaHYxWWQcuQhXe9Df", + "receipt": { + "Action": { + "signer_id": "e5a3c4f7a02f974b47155a10c517a09b0911e1a8d562e1bb09057dcedff0210b", + "signer_public_key": "ed25519:GTRCg6kGCb2AEXsVQL3qsNj9SEPMSsTwcAyBFuaUXuyC", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "3d61d0b2b47077b5c17442e07193578535d8a812108438eec5b171c751a8f857", + "receipt_id": "EJbXFS9G6XP8RT4MAJFLExurhFbyC6B8BhRm11sfPeby", + "receipt": { + "Action": { + "signer_id": "3d61d0b2b47077b5c17442e07193578535d8a812108438eec5b171c751a8f857", + "signer_public_key": "ed25519:58cNv5GbJScdH2VQJscoBG2rv44R2AprpgyrZeDYTkVt", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ae65726ae00429f9c2cccd94d9ba17da0a787d3a9be8336ebd213eb49afe1b45", + "receipt_id": "6URLaUoBfrDjB1MZW4uy3pnwb5VwSsmuXQm9LKL8Qad4", + "receipt": { + "Action": { + "signer_id": "ae65726ae00429f9c2cccd94d9ba17da0a787d3a9be8336ebd213eb49afe1b45", + "signer_public_key": "ed25519:CjmfFu9GqmZKXJf9pY6BwLP7MsLL2nyQsWSgrYxLHcTe", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "be178edf36f7b68d0f8d575d920018e4172f26a37314cb68b01536db43c0c225", + "receipt_id": "C7QtUJU73zw9CZgLJPoeZJ2HKc4yQuCudeQuH1vwxoq4", + "receipt": { + "Action": { + "signer_id": "be178edf36f7b68d0f8d575d920018e4172f26a37314cb68b01536db43c0c225", + "signer_public_key": "ed25519:Do3JFhM31AMyjPMg35CLCZAiGR8ndMSmwV2H8rf97RSk", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "0601f08a3878d4259e3299c45d55f449f7e53bac8de1ab4f05199505866929cb", + "receipt_id": "3V2fUgd5g2vBLVcVfCRPN8B3T5XJS8BzbzrNDtTDZ9ew", + "receipt": { + "Action": { + "signer_id": "0601f08a3878d4259e3299c45d55f449f7e53bac8de1ab4f05199505866929cb", + "signer_public_key": "ed25519:QTANaVu8EAyj3ANSuodsKRHzTbbspn8rwJrqMq3fjbY", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ab2489c08a9563a26e1b49cfac335fb136d6b94d3ddba14f3d894c47c589cdb3", + "receipt_id": "6Bvp1yAgCNN59D3DXBwUf8oLSTQurU1QgCPAJ14yZdq6", + "receipt": { + "Action": { + "signer_id": "ab2489c08a9563a26e1b49cfac335fb136d6b94d3ddba14f3d894c47c589cdb3", + "signer_public_key": "ed25519:CX52nb4pB7f5nZB1jrpc1hA7c5UbfYv63zpcd9k8PfML", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "74ec2434a1bc9f1425d8d71b9492e762c39cd0e2f105eea11335a0447bebc618", + "receipt_id": "8FjGfomw94f845EgryPYEtcQNmDphuF6v4HH2sRD8B3E", + "receipt": { + "Action": { + "signer_id": "74ec2434a1bc9f1425d8d71b9492e762c39cd0e2f105eea11335a0447bebc618", + "signer_public_key": "ed25519:8sR8ywcVaX8VBfJuHFhc7oU4a8YYj8cjxjAntWnELTN7", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "8fbe0803b676e10dc1ee7edf0cfe0ec8769b023fcaab7aa20562528f6bcc3faa", + "receipt_id": "9TXS7G4CwUMFEKtZQTuUgmbQMMCdMyGdn3hKWiNTeSAu", + "receipt": { + "Action": { + "signer_id": "8fbe0803b676e10dc1ee7edf0cfe0ec8769b023fcaab7aa20562528f6bcc3faa", + "signer_public_key": "ed25519:Ag7NCJwDT3iMttD9xTYpe3babKjz8Qwyfmv1gKTSxCth", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "e9bfe89b7e2d3567d35e52843e9bdd8205d0eddde0db3fa81c6762ffdb284793", + "receipt_id": "4H7fS4twdfijw4nDnXp2uMY2xe6q1jJRS8ickmvc4mSp", + "receipt": { + "Action": { + "signer_id": "e9bfe89b7e2d3567d35e52843e9bdd8205d0eddde0db3fa81c6762ffdb284793", + "signer_public_key": "ed25519:GjTigAD3vTRwfdmLKvFn1W3KamtwXsDiqQAyeVSRSRWS", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "c148c7cb592ab642a93c2e2192f4ac5c328687ad0e6ba9db2cbe72a5faa6d3e1", + "receipt_id": "3ukbzJGXbB64EMcAnTszaHQJsJ9e6CRLasWcmUfSakTT", + "receipt": { + "Action": { + "signer_id": "c148c7cb592ab642a93c2e2192f4ac5c328687ad0e6ba9db2cbe72a5faa6d3e1", + "signer_public_key": "ed25519:E1W45WPndeToURM25Xm6L9AYybzHtjBb8ZwPnQW1zi4g", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d33a6a5d352f66aae8f63722017635d39a00b9b1a8ce685d3fcb41f1b758f524", + "receipt_id": "12yZeoXM5AhqXz3XYREWd7CyRfWW68Hw2fUFctxMgrqW", + "receipt": { + "Action": { + "signer_id": "d33a6a5d352f66aae8f63722017635d39a00b9b1a8ce685d3fcb41f1b758f524", + "signer_public_key": "ed25519:FDYgrfmF5KSLcS8kNnLdPptqoWqzmtbc4agX27neXU6P", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9a72a5a389689f5c4fe8f6f03ecfbefa592cec37d8a068fdba2cbe3ba55e4f7f", + "receipt_id": "WTUYhJapBqgHqRjy9oozgq94zoQhubqT8eUgYreWS1D", + "receipt": { + "Action": { + "signer_id": "9a72a5a389689f5c4fe8f6f03ecfbefa592cec37d8a068fdba2cbe3ba55e4f7f", + "signer_public_key": "ed25519:BPuBS5AYiB6jeMCd3Sr9vDNb6CXSANkhTnmTZgr1UQ8N", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "6fe4754749dcabf83bbcc112e7cb94072ede5cdf56ccdcb14d63c7f1d0330908", + "receipt_id": "FdDtuktfX1sn9JCop6dkE36zLwBZeG9Gts6CTdrwcxA1", + "receipt": { + "Action": { + "signer_id": "6fe4754749dcabf83bbcc112e7cb94072ede5cdf56ccdcb14d63c7f1d0330908", + "signer_public_key": "ed25519:8XnJdZDVkRdHVn2dhuEwHyBRCcUrVzw1Wymn5edzBSjZ", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "tom.zest.near", + "receipt_id": "9sWSJxAYPxAHdJUSSx53pmBWueXbz4MGQBmsQoq8v8yu", + "receipt": { + "Action": { + "signer_id": "system", + "signer_public_key": "ed25519:11111111111111111111111111111111", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "10000000000000000000000" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "tom.zest.near", + "receipt_id": "McGDd5RDjomVirX8HrNSULc4NrpQUqXJBYmwDEq5ae5", + "receipt": { + "Action": { + "signer_id": "tom.zest.near", + "signer_public_key": "ed25519:BDyia5hPirkaD71vhy3sqzbxrj9cBmvfoAuZFxzn9nWe", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "22561702411076162935108" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "e39e344c76fe0db6821d142974c62724d128e7f8599d3675b6085832fc8ff844", + "receipt_id": "9dhgTCN5tTwxUigKoCQfbMoCQ5sbZw5oCoRnE8ZUNJ1Z", + "receipt": { + "Action": { + "signer_id": "e39e344c76fe0db6821d142974c62724d128e7f8599d3675b6085832fc8ff844", + "signer_public_key": "ed25519:GKXTv8WtoVk8QigSK4QWMvnS9Yh5JFfKxQZcUUboroWs", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "41b586a35151989826c1920677029dfe0c9a65a3e44d08437e7391c6d0479bf2", + "receipt_id": "EbAuUSid16kpNCfPLzX8umxgoX6QoJGMa6V4KzDBhXxS", + "receipt": { + "Action": { + "signer_id": "41b586a35151989826c1920677029dfe0c9a65a3e44d08437e7391c6d0479bf2", + "signer_public_key": "ed25519:5RW3V17mRgsQG8at4o4Q4Adv2vvwwbBf7bHxksfLHg17", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "93f20e34b49ebda676fe05e9b2b4bbfa6a52f62fa163b4d27a80209e62bfdd74", + "receipt_id": "52XRr5qbwbZzLqmEN8aRnipLyXfo9985t9GVuxnBgUQC", + "receipt": { + "Action": { + "signer_id": "93f20e34b49ebda676fe05e9b2b4bbfa6a52f62fa163b4d27a80209e62bfdd74", + "signer_public_key": "ed25519:AxX1PTLxrpebmo8Kb2gEc4TyXB5jMTB6wSBddA3FfCom", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "860377af1ab14a4fe7f89055f4ab924965fa6c7dd884393f8cd3db59d9552b01", + "receipt_id": "314mhMT1BTGt3dB65xKoSZGxER4WTbWw7waDkDxnGamD", + "receipt": { + "Action": { + "signer_id": "860377af1ab14a4fe7f89055f4ab924965fa6c7dd884393f8cd3db59d9552b01", + "signer_public_key": "ed25519:A28hVhwKnaExQDGHmL8x6WAvVen75cYYDaQ7x6M1LkHW", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d652ed738b83cc807e5449c16cd88a9264a1aa6ece4d6a39898f7d5b5aba037b", + "receipt_id": "HAeqj3A3ha3XeiZZJkA4iXbX2eKr5vDSZU4TuUepPf4c", + "receipt": { + "Action": { + "signer_id": "d652ed738b83cc807e5449c16cd88a9264a1aa6ece4d6a39898f7d5b5aba037b", + "signer_public_key": "ed25519:FRdb9vwpPQUHbT8bta7F3ctAM3qffk9F8nw9Kb5885LN", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "b0eef3081810a6d8d51f17fd62ad12130428fe52d1273237ddcf286282293ef3", + "receipt_id": "7Mb31cziUB5ztHNsqD4bgP2GBRbGgvgHWfnfi598eYww", + "receipt": { + "Action": { + "signer_id": "b0eef3081810a6d8d51f17fd62ad12130428fe52d1273237ddcf286282293ef3", + "signer_public_key": "ed25519:Cug5obKM6mMVLwfdhG77tMEzgNpKnfr6t21iN885z1Ez", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "31b35d3312d35ea9a36dd681f5e6b449a0df50a2ad6dc12c4f590a76e5d348cc", + "receipt_id": "4uP9r1CqzYoLxTUM3tqFk9vcEZE9QWDJ7anKC1pDhPWp", + "receipt": { + "Action": { + "signer_id": "31b35d3312d35ea9a36dd681f5e6b449a0df50a2ad6dc12c4f590a76e5d348cc", + "signer_public_key": "ed25519:4M1cFDdhVNYG9p2zb4i3nYFgVjnvT5qDoacV9q3QYPYf", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "69b87b8918f61518f3995bd74a12af92c59657eda27a5e10907ebcbcf085b1dc", + "receipt_id": "JU3EBuASV8VVXJ6iFsEnuquCwxrjWQc26Tra6Cy4Sk2", + "receipt": { + "Action": { + "signer_id": "69b87b8918f61518f3995bd74a12af92c59657eda27a5e10907ebcbcf085b1dc", + "signer_public_key": "ed25519:87gy1CdDHZZs4VBAUQ35pjVq3zEqnyX52dog2odNKtqq", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ccf03b9d26b4ace6f3091c9e88b6b2095104dd38efbbd1e30735bd98ee8a8ffd", + "receipt_id": "4XFHZ2seb45UCmGX3hw3p5XxsVJewsCbW8d9TtUUughd", + "receipt": { + "Action": { + "signer_id": "ccf03b9d26b4ace6f3091c9e88b6b2095104dd38efbbd1e30735bd98ee8a8ffd", + "signer_public_key": "ed25519:Enzdk86YpYwUDmQSwyM4u7GSVzpudJFnTGxUKEh9TvpL", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "786eda2110de51ea96024b0f0dfba7e64368ddeff66d73bd47533f286c7c909d", + "receipt_id": "Sr8zWtU7UQnQjY1R5Psd4jZmZJ49WBv5ur6de3QUkYS", + "receipt": { + "Action": { + "signer_id": "786eda2110de51ea96024b0f0dfba7e64368ddeff66d73bd47533f286c7c909d", + "signer_public_key": "ed25519:977xnhheAz8cRx2dt2M1R61mZKmCKy8dqgot32m7UnDz", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "beae2434ee7a81fa81be5c2a596d9dc44f9e300f92d1195f2dede786f8f95720", + "receipt_id": "GkPadWJVhDMb7duzDEA38DXoJ3TppLVBwQQdwK29VpzY", + "receipt": { + "Action": { + "signer_id": "beae2434ee7a81fa81be5c2a596d9dc44f9e300f92d1195f2dede786f8f95720", + "signer_public_key": "ed25519:DqLUW1VrcDWxaXrzLtfHrBPtwu9VntxJbPuBPgz38rxo", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "233a501077ecff96250b7d1a410fb96ba66b6cd42aff9d7c5f64b22d3a1b5363", + "receipt_id": "BqSQYiYdurdsFmCwmcJDfLKkKrPBYSytHrpBNCyKRA4m", + "receipt": { + "Action": { + "signer_id": "233a501077ecff96250b7d1a410fb96ba66b6cd42aff9d7c5f64b22d3a1b5363", + "signer_public_key": "ed25519:3NWqoraPpacVBqFknzQJe3QeADHE8kwGrGRTpW6E7LTt", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "765fa2a0c4590c52eff4c376cbecb2a0f7eac7b40a3904a797f8fbf3cbc3a238", + "receipt_id": "GHijeKKxoCuTaGEVTyhKg4TpidLAsVKBhux88iWzziAq", + "receipt": { + "Action": { + "signer_id": "765fa2a0c4590c52eff4c376cbecb2a0f7eac7b40a3904a797f8fbf3cbc3a238", + "signer_public_key": "ed25519:8y5gwcnosCECbz5UFWCyDwog7Pwirf7DAgdiW6mD5WGX", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "77d6c3ccc71e73cc99498f68c22901cc825334d2826fafb9250982567ef91ba4", + "receipt_id": "Ee6g4WS3mT7hzpJ694HrJbEeim6AyaZYpBzoxAMPA9Xg", + "receipt": { + "Action": { + "signer_id": "77d6c3ccc71e73cc99498f68c22901cc825334d2826fafb9250982567ef91ba4", + "signer_public_key": "ed25519:94oTQ8CBqsXajm8Gu6gymAYcsGexiUk5X9eET6c9wBYf", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "8945bf9b7b2b0335bd5555dbb3bd2383590f63c1475c8c0bcb5e31c262ac73c2", + "receipt_id": "8r5aqmWfkH7A6ULD5vRu338abccEf3jYKqsmRzEmfvhm", + "receipt": { + "Action": { + "signer_id": "8945bf9b7b2b0335bd5555dbb3bd2383590f63c1475c8c0bcb5e31c262ac73c2", + "signer_public_key": "ed25519:AErYMDAFwHn5WkTy4nx4Rx2oDK8F4S2xCW781M95z1Gu", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "b045b6310ebd62741dbebb67a1b3cbf7c5289413985b7eec0ac8f6d235dc801c", + "receipt_id": "AGLJDGr4SFeFLmHYT637QtyGbn9pbzcvUYiHB9J4gFVT", + "receipt": { + "Action": { + "signer_id": "b045b6310ebd62741dbebb67a1b3cbf7c5289413985b7eec0ac8f6d235dc801c", + "signer_public_key": "ed25519:Cs6Qg6kPKyjAYnKuLN17dTtGeem2AwT9GtgAkDy9z5H5", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ace6c1aee9ef7fb39ab85677d5ad1e28dfb905fb6fcbff1a284df19d5dfd30ac", + "receipt_id": "5fippwgs4EyWn9EzJ29PUpK6JvzPbCuY8CSYVcMmvpM5", + "receipt": { + "Action": { + "signer_id": "ace6c1aee9ef7fb39ab85677d5ad1e28dfb905fb6fcbff1a284df19d5dfd30ac", + "signer_public_key": "ed25519:CdwCxzaUop8XtD6HgsjA3oBc9yZkUvafNRwx2DjtmvBd", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d43b0af353f5dc8e5e3c06916d73049e072dd8003ac4cf4ef46d63eac5fa73c4", + "receipt_id": "7xGnQWDSsxjLYjYSXcTzFoW7RLGmyNvguPozHoyJr5TJ", + "receipt": { + "Action": { + "signer_id": "d43b0af353f5dc8e5e3c06916d73049e072dd8003ac4cf4ef46d63eac5fa73c4", + "signer_public_key": "ed25519:FHTegSY3AyWExDHTx9CPcYwj98vjFX3jhvR1FVXKuDvK", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "79ffdf8a5df4e590abcf0aead4747ea52a8df1624d8e3a2d9e8bb7180ee6afa5", + "receipt_id": "EoPfohtgX2avCTWH2fgtTeXT4WKxSrijC2Rd1hwBGhT7", + "receipt": { + "Action": { + "signer_id": "79ffdf8a5df4e590abcf0aead4747ea52a8df1624d8e3a2d9e8bb7180ee6afa5", + "signer_public_key": "ed25519:9DEdNC373Nrci8QEaYLERZyiBTCHy7X8RJY4wW6EVjvk", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "a962e3b8a07b5e21b00cb514635e9ab3e22a863eafe66efe7cd5e555ea038d4d", + "receipt_id": "CLjkYLwa22yrKHRib3p8BvMGRriLMZX7eXs2yTaQtVyU", + "receipt": { + "Action": { + "signer_id": "a962e3b8a07b5e21b00cb514635e9ab3e22a863eafe66efe7cd5e555ea038d4d", + "signer_public_key": "ed25519:CQDMqnJy8BjWEpH43YVSsLeq1Q7qSZFrYXorDMo6uYk4", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "cac9b6ac15c8c93dcad942da7efa2aca564c358cfcfcabe65dec5e4dbe61fd99", + "receipt_id": "FHxoYdEkPQJqStr4gJSncJzuk8NCFSxiMtSanzWfZ83D", + "receipt": { + "Action": { + "signer_id": "cac9b6ac15c8c93dcad942da7efa2aca564c358cfcfcabe65dec5e4dbe61fd99", + "signer_public_key": "ed25519:Eebkas5ZGEmvpm1LueZY6StQLD3uzjtB3VBDKL73f81E", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "6bda41b72c96860bfe7e419249a5ee6c8e8f91f88015971ec7f7e599772acafc", + "receipt_id": "7Y3La5h22JcfjDdX7XSWWbfStMBHM3NnyqwNMRTkxubQ", + "receipt": { + "Action": { + "signer_id": "6bda41b72c96860bfe7e419249a5ee6c8e8f91f88015971ec7f7e599772acafc", + "signer_public_key": "ed25519:8G1ekwe77ptMhBdVji1h8RXspKvrcvp5v6tiEkoSWyEK", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "37c5f722e06f31f49e970f550b9d2f61789e624d3b2259e2fb4b75b01ac87243", + "receipt_id": "AiRRmma5wVV1RfpZHM36iVFrVPXH55miXFdRFzdpZKob", + "receipt": { + "Action": { + "signer_id": "37c5f722e06f31f49e970f550b9d2f61789e624d3b2259e2fb4b75b01ac87243", + "signer_public_key": "ed25519:4kiWHgYND8zu7mZDNeQ8xK6zZk5ZNGpCDAgQtC8JvN6v", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "0c88bb18d0f084744126b7743e2e17acfd7d8d7a3f91f62763b1c08bf406534c", + "receipt_id": "5kJFdryqLkKB1XUt9HzWiHrmrj2dbCx3uL14QEoDazs4", + "receipt": { + "Action": { + "signer_id": "0c88bb18d0f084744126b7743e2e17acfd7d8d7a3f91f62763b1c08bf406534c", + "signer_public_key": "ed25519:qvpSB1E5vbsXDpVDBuAd4sDeK6V8J6TMqDUowt6rsgP", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "4dad427a64e0dfc03cf06bd51579dfd962dd4c014d4b982984742f7e89f33e57", + "receipt_id": "GQ9NLr63J8z18LZeFpVL5UGNmFj3kAN8UkMPVUCyZzek", + "receipt": { + "Action": { + "signer_id": "4dad427a64e0dfc03cf06bd51579dfd962dd4c014d4b982984742f7e89f33e57", + "signer_public_key": "ed25519:6EDdDi1LV5yKHfsskhwFXCqASJuAEA7x3JD7ahSzBNhL", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "6aa32e62a51081a7992ec7861998ccaaf3966aed5ff73f3eec0fdcfcf1565e01", + "receipt_id": "EU3sU9p2n82dUXMt3jwjL7ib1mZDW3j1a6b3dXAZkKaN", + "receipt": { + "Action": { + "signer_id": "6aa32e62a51081a7992ec7861998ccaaf3966aed5ff73f3eec0fdcfcf1565e01", + "signer_public_key": "ed25519:8BGXzAzyNWreWJz5JkqxJA6mLqoKrVBx34Z3z1DoQ8aL", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "329f8b8c756ba124f52b6e56b72f9820f3d3e7ac886ae71f1b4d2e8536c30833", + "receipt_id": "8gJ15EafieybLWZi77dtFP1YcBWnbYSm9vgpT7aJ5hbK", + "receipt": { + "Action": { + "signer_id": "329f8b8c756ba124f52b6e56b72f9820f3d3e7ac886ae71f1b4d2e8536c30833", + "signer_public_key": "ed25519:4QcVGbQ2UQg6vvRd4Ccqi1SEmdQrKqyNHHDTXTZPV2u8", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f2b31b1b70fbdeccc756f36e98fbee14aa1508cf9831f378ab036d4e5d9a3d18", + "receipt_id": "8vtQr7jFj54h9jobW2kEjvA6Lbuxu6dGqjSLHaXYGdQH", + "receipt": { + "Action": { + "signer_id": "f2b31b1b70fbdeccc756f36e98fbee14aa1508cf9831f378ab036d4e5d9a3d18", + "signer_public_key": "ed25519:HLQ4mhq1VdfbN1WCy2K3AQ4aWDzgtFfr9ABLcaYJuZFV", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "3e1c44a0110e2a4cc38441889ac1a4943cef82ed0d6fb7f2304e4454a0a664b2", + "receipt_id": "3DkGoyaAe9VuFhdLVEBBk4kmqp6iNBXu6b7Vs1aJeENW", + "receipt": { + "Action": { + "signer_id": "3e1c44a0110e2a4cc38441889ac1a4943cef82ed0d6fb7f2304e4454a0a664b2", + "signer_public_key": "ed25519:5BTH78MGWDjRvbCBKcngmG4omcwprYe6fU24f8tuZtam", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9510a1908068800991d10d3a0c3d31b86aa8a8f1c07ade3863edae82b5c7925b", + "receipt_id": "5iaJTMfESLGBMSKf1HexRPckfVSCVMJDYvS54ZftzpXB", + "receipt": { + "Action": { + "signer_id": "9510a1908068800991d10d3a0c3d31b86aa8a8f1c07ade3863edae82b5c7925b", + "signer_public_key": "ed25519:B2tTRf6HzjC2SbVPTTDX1SyS5iGsFkc4x9n72SqpDdMt", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "cfc5ec4484f862a593ba81b36d008dd16ba41ae4edaa0a6f5c0636c054e40596", + "receipt_id": "25jG2mBrXZRZdrzNa5Ww4W4hgsQEzTTFAN7X8z5LV2S7", + "receipt": { + "Action": { + "signer_id": "cfc5ec4484f862a593ba81b36d008dd16ba41ae4edaa0a6f5c0636c054e40596", + "signer_public_key": "ed25519:Ez4SNeoxnngGyWu2guuUV2cFGBT5ZBioYKAdcbZtQZZF", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "331d760083bde8e16f2b31b685272a6eddbdb5c587b4a2eaa63edcf1b83c6fbf", + "receipt_id": "9idSG8KoC8wyXkj6S8ZJpBXc5eN3i5ELfzidhyA99pRY", + "receipt": { + "Action": { + "signer_id": "331d760083bde8e16f2b31b685272a6eddbdb5c587b4a2eaa63edcf1b83c6fbf", + "signer_public_key": "ed25519:4SXrBf3mN5FXa2qg9wsEWuueDvC9fiXBWE91GwHRbmSN", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "53f9c92c36ec210adc6faf00b49edce491f3b92659e88da9588b356c9e11d42a", + "receipt_id": "BNA7Goz8o7uTLLfyfSeXEnwRHfiiVvoSK5yN8LicRJX6", + "receipt": { + "Action": { + "signer_id": "53f9c92c36ec210adc6faf00b49edce491f3b92659e88da9588b356c9e11d42a", + "signer_public_key": "ed25519:6eokYb6nFPWrVfwyQ2Reg6GB47qESAF75j6kTcMSe513", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "92d9bfda040034a7d455aa2b67dcac8f9361174705da9d526c52cb9c5de37122", + "receipt_id": "FMCBpN4u78v86ajM1NG5nhpyEsypWjpE9uvmyhj7PrZM", + "receipt": { + "Action": { + "signer_id": "92d9bfda040034a7d455aa2b67dcac8f9361174705da9d526c52cb9c5de37122", + "signer_public_key": "ed25519:AtF6x2YejaHQfPZTgker47UwLZ9s2KY2yU3QatEA44wK", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "c4e038663d095934d22fbca2344373c722655cc40ac87a5e49f572783be86460", + "receipt_id": "B9ByHspiEhGB18UJouGtr65bM4AzkPCkxWwxaSaJv6Hj", + "receipt": { + "Action": { + "signer_id": "c4e038663d095934d22fbca2344373c722655cc40ac87a5e49f572783be86460", + "signer_public_key": "ed25519:EFXDCasR5exRAxZjLCYJtKG8qm3pzm4qpVdkXvTUAPHH", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "7d5c998cbfdec988c967aae14b58fd20f546cdae1d80c481b308e2e962af415c", + "receipt_id": "HdnrhgCf1aJAizUj2DFyiTQw4Am7mBqN1dpLJt9SuzwH", + "receipt": { + "Action": { + "signer_id": "7d5c998cbfdec988c967aae14b58fd20f546cdae1d80c481b308e2e962af415c", + "signer_public_key": "ed25519:9SMrmAYVfNikbPEEcozHjjwT7Myp5hzpaLLXWmfksDz3", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "0332f7e0e5c49417abdcd5bc1ff1b74ff3645ef6197455d15df146907a606269", + "receipt_id": "FYhkP6Z54niHnE1vRre8LiWkKN5y5ZRtnngfv9rftk7M", + "receipt": { + "Action": { + "signer_id": "0332f7e0e5c49417abdcd5bc1ff1b74ff3645ef6197455d15df146907a606269", + "signer_public_key": "ed25519:DVJPAyuLLB4BBLAadwN6tHza2HusH4qNy2j5FsLNdPe", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "1b1bfbe28688ee3197ba97029ed372e976d75f0980a6ecc1b2b68b952f8af5af", + "receipt_id": "GZLdcQZsNDXGvJ8VTJc2NtpSjjZuKiYBBFXckUkyziJq", + "receipt": { + "Action": { + "signer_id": "1b1bfbe28688ee3197ba97029ed372e976d75f0980a6ecc1b2b68b952f8af5af", + "signer_public_key": "ed25519:2ppkuRwPCMjpMeLxTdEe6Y2D9ysoNNU1SJzafyLkTVzv", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "0964bc1ea99c4dafbf98666ff089ffcb851deee744bb4d2d01e06f0dddce479c", + "receipt_id": "3KmA8sAK7siZszJhognXo9fwdSkp7gz6Q3K3jFWhVyDT", + "receipt": { + "Action": { + "signer_id": "0964bc1ea99c4dafbf98666ff089ffcb851deee744bb4d2d01e06f0dddce479c", + "signer_public_key": "ed25519:dfm4amWHy3sbFE4p6H1qtYRLDevVyfnBRmD1S5K5YSP", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "2802cc286689adce1c300e24ed346cad4e9ed4878926f73fa093648b02ca17d9", + "receipt_id": "7yFACe9mufL3XwezuGTN5xXpnm1sGc8RsZJNib1yeKLa", + "receipt": { + "Action": { + "signer_id": "2802cc286689adce1c300e24ed346cad4e9ed4878926f73fa093648b02ca17d9", + "signer_public_key": "ed25519:3hBnMjg2RGfngAwBVWJUGmbWHGawdbd2H2CgN2F3gXyz", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "c4fb6092ab14b367d3d196444a042d3ec73cd9ae694213c795276d26e43d2895", + "receipt_id": "FapmJtoYKv2UjrUETq2xNLAXsHWxxUaVL45EwnB5u63J", + "receipt": { + "Action": { + "signer_id": "c4fb6092ab14b367d3d196444a042d3ec73cd9ae694213c795276d26e43d2895", + "signer_public_key": "ed25519:EFwEEDBQqkWLDeQLsAjeg78PVPaikMbJoMd7YwxNJTjJ", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "6950fc50c8fd1c6a11b6a3ecd781429fb60104ffc37b9fedf3328b05bd7286e6", + "receipt_id": "39zszAbJADk5fxhJ4P1cxbs4WiUFj2amWyCBYkArgR8U", + "receipt": { + "Action": { + "signer_id": "6950fc50c8fd1c6a11b6a3ecd781429fb60104ffc37b9fedf3328b05bd7286e6", + "signer_public_key": "ed25519:867S5R57HCKnb5uPwCEhhtj5s7GAkSZWvoiuJhCggJRj", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "968258c0caac15f600a0e443b813a1576dda1e58282d095052008b42e7cdb730", + "receipt_id": "FA4EJ4AL6qvnwKdSR38aNnZhqtrAxigUvFTkbyLgVmB", + "receipt": { + "Action": { + "signer_id": "968258c0caac15f600a0e443b813a1576dda1e58282d095052008b42e7cdb730", + "signer_public_key": "ed25519:B8XSAmjorghb8JLknsiP4dY9RpZZRBofFzrN5HHxvpkX", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "48274640cd365f86b774b7903a410cebdf15430d46e9d9ddf13208717fd16916", + "receipt_id": "C7LejtCNLGFnVygLcimVcPiVHyrmJPmk6N6N1Dj4csVz", + "receipt": { + "Action": { + "signer_id": "48274640cd365f86b774b7903a410cebdf15430d46e9d9ddf13208717fd16916", + "signer_public_key": "ed25519:5rf69XLkoHzreWyCMkwvr1PgL4GwyPV21MtWB8sR3YqB", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "9cc501e5df0403bd5f072c44fbd18187d62e5f2acbd404e9c6dcf7a98f95710e", + "receipt_id": "2N7tEGDfTsU3u7CrtEiLDHmED5TQ9kMX3xnoUdKTKEjV", + "receipt": { + "Action": { + "signer_id": "9cc501e5df0403bd5f072c44fbd18187d62e5f2acbd404e9c6dcf7a98f95710e", + "signer_public_key": "ed25519:BYxqSdkxYBaJe248bPjaQets6tjvsnrrJwqhKL4YXW8d", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "86d567d8c0461961f149a6d553a350dcde8859f4718b2a0dfa68da806a6fd721", + "receipt_id": "HpQXEt7KZrzLPVQtbN8QG81xfdE5skq6EPFVXLVCFxSg", + "receipt": { + "Action": { + "signer_id": "86d567d8c0461961f149a6d553a350dcde8859f4718b2a0dfa68da806a6fd721", + "signer_public_key": "ed25519:A5LNNmWqic3xsa8R3ZKUqPxdbmg4YtHraVU4fQm4X8Jk", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "8ffbbc8fb7d4974e428721a4886beec557dd3ed611466ec2d87c9494d29aad13", + "receipt_id": "2wEoQFKo6kqShGvebBTHZVv5RxfSQFMcEg9kFrf6W5CU", + "receipt": { + "Action": { + "signer_id": "8ffbbc8fb7d4974e428721a4886beec557dd3ed611466ec2d87c9494d29aad13", + "signer_public_key": "ed25519:Ah3wQ215sdRcpgED3AQnf8qqpyBuPFRs3T8CLKeLvhuL", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "b5c0b0d463776a8f4cefb86ab5ab61ebc1aacf5cfd218736835567eae48862ce", + "receipt_id": "9SPFJ7682H1uMTouYw1f5X6XujrVs1iLPCQC2XeVw19L", + "receipt": { + "Action": { + "signer_id": "b5c0b0d463776a8f4cefb86ab5ab61ebc1aacf5cfd218736835567eae48862ce", + "signer_public_key": "ed25519:DEVDBKFnmHSXNZzCGgdsipw3yUcMMbohs3WgWtpHGeow", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "7a69e426f02e62d2194872542588c316b953e50d0986ebc8265affe3bfb0e135", + "receipt_id": "6etnid3DbLRYhxfyrSamv6SkZPXstRLXcJAnimV9nyAP", + "receipt": { + "Action": { + "signer_id": "7a69e426f02e62d2194872542588c316b953e50d0986ebc8265affe3bfb0e135", + "signer_public_key": "ed25519:9ErPcUxGVe2WVCR68ms7MM59koaaLeiHpzMGhGcQH6Xe", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "5b44489a4315d4b57b852cd3a62312eebf103ba2e613c3d86e222fe165ef4aec", + "receipt_id": "2RfAcYA4p4jVo3oVanXhNLr4dbHuETte8Sd16bikWXwh", + "receipt": { + "Action": { + "signer_id": "5b44489a4315d4b57b852cd3a62312eebf103ba2e613c3d86e222fe165ef4aec", + "signer_public_key": "ed25519:79GVUG1C9KXPi5hMi7bF6Xcao11kLhhYwZ8eGG1pgQLT", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "0a9dbfe3c25c9e2c17fcad7f37923893b550aa3a6c9f949634f182f1d6fc93b3", + "receipt_id": "fmKsZtFVYvHWEHgm9Y4vA6dHM5E3fd35wyBpYyRr1Li", + "receipt": { + "Action": { + "signer_id": "0a9dbfe3c25c9e2c17fcad7f37923893b550aa3a6c9f949634f182f1d6fc93b3", + "signer_public_key": "ed25519:iSbJo1NfUHkp9V3rq7d82duGJKXA3pwt71kzoRukKF8", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "547a9e28aecc5120e6847db20c8eaf4c23f511a04880d45faadaad1f16f68609", + "receipt_id": "CkzFqZKbUW2fzcHBUDnv4UeZrk3kYijruUTco14P6dZ", + "receipt": { + "Action": { + "signer_id": "547a9e28aecc5120e6847db20c8eaf4c23f511a04880d45faadaad1f16f68609", + "signer_public_key": "ed25519:6gmh3ar6xEqtedEXqPHUTz3V6VGjHaiqJv5hGy4ZGucL", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "5b8f1e852b5345e3aaccf652de5991d01e2ef6a96ba71cd2dc0b796e464a97bc", + "receipt_id": "FSoSNXukUn5g4j4f3dAErHphbNfMRGVBxHwdJnPA5fu8", + "receipt": { + "Action": { + "signer_id": "5b8f1e852b5345e3aaccf652de5991d01e2ef6a96ba71cd2dc0b796e464a97bc", + "signer_public_key": "ed25519:7AQgCbJdH7vRbdhTp5ERo82TN6N1XJubdZt1SrJBs3f9", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "0e4a711554e0b460bdc177df274a0e4cbf1341a3b71ed1b76bc25864a2c31b99", + "receipt_id": "GegyPvRwRZxNZppXDjEwMKZteYsocp18Fcht9tJnfLWD", + "receipt": { + "Action": { + "signer_id": "0e4a711554e0b460bdc177df274a0e4cbf1341a3b71ed1b76bc25864a2c31b99", + "signer_public_key": "ed25519:xnYaQdA7CR7Yec6Bnpi54zdSpUYTptDcHjG8Tb9qXr8", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d3471fcd945f9144b00fa700a0836f045e0f3f5f8ed8dfd04dfbedd07fb2a810", + "receipt_id": "76a3iyzxiREYrvaHMGvXM4Kg26wkfh5hTYpjGXUpqgZj", + "receipt": { + "Action": { + "signer_id": "d3471fcd945f9144b00fa700a0836f045e0f3f5f8ed8dfd04dfbedd07fb2a810", + "signer_public_key": "ed25519:FDjvkx5A4WdQ5pG4GV3dHzKb4TvEnUaJ2YiA2aPgZZRq", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "32d881d9d7be14e83940c0ee6f4da0dd993f013dcd9c4df464c741785046e150", + "receipt_id": "EF8BNmuR2egPL3ctdjjg7R61kA7BYe3KkeThYU8fi7oi", + "receipt": { + "Action": { + "signer_id": "32d881d9d7be14e83940c0ee6f4da0dd993f013dcd9c4df464c741785046e150", + "signer_public_key": "ed25519:4RUsAnzUUkogYHcc9CTq4mbgi2GUQCKQ6BMsGKd3UH1y", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "8c2d76d58782dfbb4a3b3379dfa2edc8069009bfa53c2d4792bbfb748cfe511e", + "receipt_id": "D6MMtakqPKQKjjFY8CEP1xpQ1Jg3Z28cmmau5iUZ2hnn", + "receipt": { + "Action": { + "signer_id": "8c2d76d58782dfbb4a3b3379dfa2edc8069009bfa53c2d4792bbfb748cfe511e", + "signer_public_key": "ed25519:ASCHcdApK7wexCAqsEM6vHF3S9ocZ76mTGkyoyNNoLfw", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "5ba05f51ad4e9a5ed44adbd1c76dd97c55b853d2500197457f83392374c962c2", + "receipt_id": "7uAcX4vFfZpzRprPRCCNEZRwLmw1EL3ghB6P2kxAVJqu", + "receipt": { + "Action": { + "signer_id": "5ba05f51ad4e9a5ed44adbd1c76dd97c55b853d2500197457f83392374c962c2", + "signer_public_key": "ed25519:7AfwD214F69opA7knZa67NGdfQKPStJ6DBdhsbnRAGAM", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "260fe9ee8dca70033ac403f4b6008bc1d2870fc261277bfadc8694d6f0821ba6", + "receipt_id": "CRTiQDTXjSN3LAF6c5BhVx7yq5jjUCjxdzGVCBLiPA7Z", + "receipt": { + "Action": { + "signer_id": "260fe9ee8dca70033ac403f4b6008bc1d2870fc261277bfadc8694d6f0821ba6", + "signer_public_key": "ed25519:3ZaZsa6oTqjNUhEDPzvinCJMxgxLiT28AMNkcio2tQDj", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "b7a844992cae2f81a0ed2484d43c212efe52f0b148732afc76377cbbf5d33684", + "receipt_id": "BPy45d7FDPxZuRwt57Ypew2Dkc8KyQzH8ouaNAPXqyqG", + "receipt": { + "Action": { + "signer_id": "b7a844992cae2f81a0ed2484d43c212efe52f0b148732afc76377cbbf5d33684", + "signer_public_key": "ed25519:DMvRh9s9gp2XwjEbSKSQx72QdALcmQPppvsvHXErYgiw", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "d24b559e5878af34f7228bbc282c4f05880b375043861a08291d9eac457c1d03", + "receipt_id": "HWZBSXRA2N2dyaeU99TMC3M9qzVaN7E9gjhPSm38gb6J", + "receipt": { + "Action": { + "signer_id": "d24b559e5878af34f7228bbc282c4f05880b375043861a08291d9eac457c1d03", + "signer_public_key": "ed25519:F9uF5S7Do8Qr3ZcNn4HMUNod4zeg9UZXrepSri8CrA2E", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "535e7f73a679282ba6cd6136c79c52064c43ead255840370fc6959da31b8a035", + "receipt_id": "46MvG5LSHqFEqcBKM61RHsTPAzFPmeQzax7GAKPMvQf1", + "receipt": { + "Action": { + "signer_id": "535e7f73a679282ba6cd6136c79c52064c43ead255840370fc6959da31b8a035", + "signer_public_key": "ed25519:6cSQyLe4EF5RbUwDrDMhZpEu3A6Vimq7MJTkWakyuTrt", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "20fb238ac4436eb65aba3bdac4ebc782aafa5ed776111f40d1403532ebe63302", + "receipt_id": "6bbgZmK7vxWdP97KjxSJJG8ayftK1wS5i3kugVpjVf9N", + "receipt": { + "Action": { + "signer_id": "20fb238ac4436eb65aba3bdac4ebc782aafa5ed776111f40d1403532ebe63302", + "signer_public_key": "ed25519:3Dk9yi2otAziUXQpfjcgCdLcgxVcbkqynJsCdW3GDENV", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "0647e1dbe4b766f7be1014176ffaa2211c3ae5ca765f4dbc33eaace1fd8257b1", + "receipt_id": "DyKDpiz8y1ybzMGHsjcpdXbdquLpLNuKVnVRVdpjLzQt", + "receipt": { + "Action": { + "signer_id": "0647e1dbe4b766f7be1014176ffaa2211c3ae5ca765f4dbc33eaace1fd8257b1", + "signer_public_key": "ed25519:RX27fezTgfTjF8bQQj77raXV8s1hygLgDiE2SyZe86C", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "ceba7a6a35a185cdba8e0b605bcd639281c7dd9fd770236a01331f9183cbaead", + "receipt_id": "XgVmEsr572qXot25CLkZg2xyksddKKddf52sz7Xv9zY", + "receipt": { + "Action": { + "signer_id": "ceba7a6a35a185cdba8e0b605bcd639281c7dd9fd770236a01331f9183cbaead", + "signer_public_key": "ed25519:EuyufUcLK6NSbvksoen5wpRkunazScgdLzWaQGovfY1J", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "0ef15125fe98cc744fcd202aa2aed4244dd8f48f9aad427952e773451062ffe8", + "receipt_id": "CYZgYi1dAwZHX417MK1CGiMfKUNViTgeewhcdAjymvSC", + "receipt": { + "Action": { + "signer_id": "0ef15125fe98cc744fcd202aa2aed4244dd8f48f9aad427952e773451062ffe8", + "signer_public_key": "ed25519:21L8XRjrtFmZukGyhx3NtVoXm2xSorcYDRJye5ofreQw", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "226ca713802ebd9bc49fab56ae8942c04d609c4d07d42c4b228c1b517c9f5f27", + "receipt_id": "CeAk8omxCm1GSPmRp4WCEAys7GmzikzcApdxukYTaQQX", + "receipt": { + "Action": { + "signer_id": "226ca713802ebd9bc49fab56ae8942c04d609c4d07d42c4b228c1b517c9f5f27", + "signer_public_key": "ed25519:3KNxNWhfLwKzXVDjjdiBpp9LG9WgaZsh1JSPc4xq2w4S", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "778df90fa5ffb7132c0364b67f7cd6148b74e85502e739b546a7a5f8b15a3d73", + "receipt_id": "5Vefr9LpoMfDS2GwzeJgw84vYpgqD8t7UCAzttMnvJ4r", + "receipt": { + "Action": { + "signer_id": "778df90fa5ffb7132c0364b67f7cd6148b74e85502e739b546a7a5f8b15a3d73", + "signer_public_key": "ed25519:93h5Vz7bNK7pbpyLhfWmFLVTLEgc5Zxhhwvkpg9JGReA", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "f6593f8bf56c5f9f24aaf93f19b1dded7090dc51a04ef540ca248381b0186d0c", + "receipt_id": "7o1JHkAiRu1aSdLqmfqjTY7TCtnpN6NSs61398GjqmTU", + "receipt": { + "Action": { + "signer_id": "f6593f8bf56c5f9f24aaf93f19b1dded7090dc51a04ef540ca248381b0186d0c", + "signer_public_key": "ed25519:HaeE4hCKf4VYDscdVKWEg8MCDi9kn5PeKM3byeGySZ7h", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2283986818853206908920" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "lulukuang.near", + "receipt_id": "CacxqWcmYho7sJxXdbTCkGmGEKDFqPN6Veyz4JN8oeBm", + "receipt": { + "Action": { + "signer_id": "lulukuang.near", + "signer_public_key": "ed25519:D1ChSvJztywJnk3BEfjWHNjwCuKLrsVFSyduZegKhse1", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "963720295159511630376" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "dab3114eafc6352cb93149b838b8cf794e654446cc28a5b58de1e63b772ce026", + "receipt_id": "2qKRaMryYiUcdTiMqiFkscuUZBZb4EDHd5tbJUMMsmsF", + "receipt": { + "Action": { + "signer_id": "dab3114eafc6352cb93149b838b8cf794e654446cc28a5b58de1e63b772ce026", + "signer_public_key": "ed25519:FiiFHvo7yiMQMCK2JNmbw14JkiWrL3NJjADGpwsn6269", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2324385038795796748000" + } + } + ] + } + } + }, + { + "predecessor_id": "system", + "receiver_id": "e9ea8202f8d5a322948b455ab75b1e83ff29390ab61cc7d339c5dabaed5d119b", + "receipt_id": "3nbC8Vec2ZfJ2sscQcZbZ7mMk5KuKE46W4teeXJvz4NB", + "receipt": { + "Action": { + "signer_id": "e9ea8202f8d5a322948b455ab75b1e83ff29390ab61cc7d339c5dabaed5d119b", + "signer_public_key": "ed25519:Gk7PpnpjXJb2BoaiT9sx1ixfAdF5xj8MVav4UpxBqY3C", + "gas_price": "0", + "output_data_receivers": [], + "input_data_ids": [], + "actions": [ + { + "Transfer": { + "deposit": "2324385038795796748000" + } + } + ] + } + } + } + ] +} diff --git a/utils/mainnet-res/res/storage_usage_delta.json b/utils/mainnet-res/res/storage_usage_delta.json new file mode 100644 index 000000000..dd446025f --- /dev/null +++ b/utils/mainnet-res/res/storage_usage_delta.json @@ -0,0 +1,12450 @@ +[ + [ + "0.near", + 1 + ], + [ + "00.near", + 1 + ], + [ + "001.near", + 1 + ], + [ + "007.near", + 3 + ], + [ + "0110b3cbbb0eb096319f1e12460519e65d0429375b7258f377265979c119889c", + 3 + ], + [ + "0153b15fecd4ffd382ffad6882ee053e0a0fddefa7e1a99d8aa29eb07dc9bac2", + 2 + ], + [ + "0154af6f1023fdfd8d87906211c7bae2bc2efae1e5b3347683813f598a19d50e", + 1 + ], + [ + "037bf673cb864e5a14fec7c476fb60d0416e1a46b196b2f95b87599c1c73073e", + 3 + ], + [ + "04bbe8cdda12ab0081a3c4379c97623ffebf33ec4c1d7949acc4f2626cd638a9", + 4 + ], + [ + "0514dc4c3005dd273ed59ac5bab420f81008c698df31d72baa088f92c3340ed4", + 1 + ], + [ + "07.near", + 1 + ], + [ + "0a1eb8bbd90d8a187672baab254774424f4fd58bf11fd6025cb60f118f930005", + 1 + ], + [ + "0aab2c312b933402b75764488c5ffdf2e6ef2b7bcc4cdaab422873e17f9b7f6a", + 4 + ], + [ + "0c0dbe4fd0ec7b6f49388fcd80085fc2f58d12b5a353031a15ba3fa0939bc1c3", + 5 + ], + [ + "0c6c891f032d39d667356fb891a1083fa062cefc5aeae2b1786b84104158ae90", + 7 + ], + [ + "0d10552f8c8cc44b2438f086e989b053b0c604e69a1b9e3858f51a334fe3346b", + 1 + ], + [ + "0d5054e7ee907ca117d7660350bd4a023968d51650c7c4d99c4eb86349752f66", + 3 + ], + [ + "0d7b443198e08e34350371f65ace682f1ca9786d9bf54b2b52ae608fbd81c230", + 1 + ], + [ + "0de88eb5ab5894e784f454508f396a7fed67c56d302264becdfaa9029453a138", + 2 + ], + [ + "0e2b64a24d58fdeba14234372bb0b23a8b683033680acf59cc2c8833bb64dbe6", + 5 + ], + [ + "0e7a0c50b20f5037b40a220aef05fab39aff40b923be04006ff7a6062c424c8b", + 2 + ], + [ + "0ed97eb8b992487f7f03451a24b1fe40d53ca6658afc42b0ab8fe4639385439c", + 2 + ], + [ + "0fb8d555930d307a9d20d5ffb0ffdac5e869b31c17277bd1d800ba6d806dc477", + 1 + ], + [ + "0x.near", + 1 + ], + [ + "0x3bfc.near", + 1 + ], + [ + "1.near", + 1 + ], + [ + "10.near", + 2 + ], + [ + "100.near", + 1 + ], + [ + "100x.near", + 1 + ], + [ + "1015a0dadb90ca2d30035c15261204a7b3f4693177ab8a9771b62591af55d737", + 4 + ], + [ + "1024.near", + 1 + ], + [ + "10scarandres.near", + 1 + ], + [ + "11.near", + 1 + ], + [ + "11_8-bernaced-00_7.near", + 5 + ], + [ + "11november.near", + 1 + ], + [ + "123.near", + 1 + ], + [ + "1234.near", + 1 + ], + [ + "12ed01143181f20669a689d0e1ee202a7d8301fba4c4864274de4bdffdc6abe5", + 3 + ], + [ + "132b92af125cb0b94f6cc8767539bf6e6fa3a2761824ba92913e272a4646165a", + 2 + ], + [ + "13ba6764bee29e130a9cea6ff5545ba137ea48ccf23ab08d55001a388d1eed55", + 1 + ], + [ + "14161728bc6fd05a204a6a74c51c1256d1be6160c42b9dbfcb56de24dd24ba2c", + 5 + ], + [ + "14b1f401dc50576118a2472acd428f7c4c2c99e5ae21d1c01dd483593026bf9b", + 7 + ], + [ + "15.near", + 4 + ], + [ + "166a8baa5a06c12a89bd9fe53be82f4f3113aff7a5b8ad1c790f6fe215de69bb", + 5 + ], + [ + "18.near", + 1 + ], + [ + "19240275b389c3d4f37ad6d015d0e5552f353e17a55555f4b88d120e208b35f2", + 2 + ], + [ + "1a1b2e1105736ad5cf15377b239ad0060ff698b427d837047dd8c6cefc664821", + 3 + ], + [ + "1a313ebf78de0975aa0d2d06f064db497e76e82dfe59ae91c6bd11fd96240f1e", + 3 + ], + [ + "1a360b62fac5a1693883a50a8a629f23d54c3ad08f9b1411ba1de41931f6635b", + 1 + ], + [ + "1ab5994f049e3b19f1a9a00af5466184df51143299ff74fdc0ef55ad3c452f60", + 1 + ], + [ + "1e4703c615a76e489c8812485cc48103f05e28ef719c7e64c7f99abbcf3a8514", + 2 + ], + [ + "1e6221ca523a6e16e271dda75b8edeae5b35b30de5b3b666e2eb86a974cd435e", + 1 + ], + [ + "1earn1.near", + 1 + ], + [ + "1inch.near", + 1 + ], + [ + "1kk.near", + 3 + ], + [ + "2.near", + 1 + ], + [ + "2140a646e0a3c8c974fb0c6e318fbf62d086784a5f7419d80caae4fcb40a76d6", + 2 + ], + [ + "22.near", + 1 + ], + [ + "237dd62c081e1ccc75f4ba77b02dcea4e56fde024f16746727285c042dc3027a", + 2 + ], + [ + "23d810a933bbac6447aaada7ef03156eb324a124857a7528942819b47f81c1a3", + 1 + ], + [ + "273ee3cbd1f36fd54dc52a4f93302cc80fd3e7d64486ceba35f5b4d1f7ebea8d", + 3 + ], + [ + "27eef9392cbcd7ff73675755cdf8a930c5d829306f2653b404ca2ebf45821c53", + 3 + ], + [ + "28121990fg.near", + 2 + ], + [ + "294548b971fa95fb635aca80caf6ec25cf26b2082c11beee3ad23edd3c119fbd", + 3 + ], + [ + "29878949f636662ce3c2429e45a68469f799f08817cc2d1b57936ddff2e390e8", + 3 + ], + [ + "2a50c02c6c506d7745713986e4d5fe0fc9806a819dc10225d762d96236a5b986", + 1 + ], + [ + "2bb9146b50b937ae0a02be5407dab2104311c228cf72521d59e652c693927052", + 4 + ], + [ + "2c080e67e1696fce40dbd29f37c0cb222f69d051f3d820479dcf68b69b8b6040", + 1 + ], + [ + "2e8b636a1b4ebc65969e2509dfdae58fd93135d5d688bc6e57ff9443398b4cec", + 4 + ], + [ + "2f7b6346ccb5b22d9e18caf0c46412cdc03b94783d2ecec3f9d3cb62395460a7", + 4 + ], + [ + "2ge.near", + 1 + ], + [ + "2smart4.near", + 1 + ], + [ + "303c0fe14eeac48e62cfc6ea8a4b0ad5c314b56038dd9b618a3a7a68004e0976", + 1 + ], + [ + "31337.near", + 1 + ], + [ + "31536812dcdf087d2f58d2006273342bb8a07691b187e60fa95013c546e3c697", + 22 + ], + [ + "317c975115ab57b7cff17aede35e7313b61d94283653e98193b62f4f5ef84155", + 2 + ], + [ + "329794744.near", + 2 + ], + [ + "362.near", + 1 + ], + [ + "372ad2d5c7cbab331e985ad0b82aa1ef4d8a814114506b55166b46dfedcf3cb8", + 1 + ], + [ + "3803e3358a676111a436ff0c2199165c290200682cedc3cef42b97db3e69436e", + 1 + ], + [ + "3832040fea9bb71e7d5b9878028daa11205347bbc4397120fdd629ebffbb3ee7", + 5 + ], + [ + "3839632950.near", + 1 + ], + [ + "3b83b07cab54824a59c3d3f2e203a7cd913b7fcdc4439595983e2402c2cf791d", + 1 + ], + [ + "3bcec9ab1433c85ec3073271652cbb56970affa28fc7681ed1ee7edcffbb41c8", + 11 + ], + [ + "3dda717ab16163320d3cb4c301d458b747800a78a001a8c2e62d6c67f77e1621", + 1 + ], + [ + "3dee906ea97fae13af3aaeccb3d48b2c26333dbe2e1400e0806d03b366c02554", + 1 + ], + [ + "4.near", + 2 + ], + [ + "40fe3cb2aed51e9566bda6125a73542a4f8783b10cb121adcbd138090413c87c", + 4 + ], + [ + "417d3db9955230c26c5f20d8976238809b93b200b20a902d2026503b564bc590", + 1 + ], + [ + "41a91e65b740aa45f7e43a7beb3f7e7ea89b886ef866d463b649b2ce250c5ac8", + 1 + ], + [ + "42095658e8f68de946387fdb4b57aba8688c19accc0c7561d21a40750acbfc3d", + 3 + ], + [ + "427921797a22047e361bd436fc3046be9af45d9a2fdb889fee4578f7e15b476d", + 16 + ], + [ + "42d4333ddf86fa25f44e3958aaf7c47f8381ba288298913985321778c57b4f87", + 3 + ], + [ + "44.near", + 1 + ], + [ + "454864608.near", + 1 + ], + [ + "4840e45ddb1f88707a66adb4014aef69aac28f4e6bf888be9af9875254778049", + 1 + ], + [ + "489fcc38ffe24db6bf49b37391f16cf55aff87e9e74752206876c5f2bcd96550", + 5 + ], + [ + "49b215f4ce37d8635322a23164e2129832e12a3aad8909f11015682fe2001243", + 3 + ], + [ + "49caf8c545700980ac5cd883bfc3682feeb971083c5569fe6ef67cc93ca3f47f", + 3 + ], + [ + "4b84545328537c4c97fd353879bf016275ae42888a9a613320a0929b3b12be9e", + 1 + ], + [ + "4b919505a2e49a3b47d4b7327d3b3bf1faeb02c21d797e3364bdde605ac03947", + 1 + ], + [ + "4myson.near", + 1 + ], + [ + "4nts.are.near", + 1 + ], + [ + "5.near", + 1 + ], + [ + "501f05dc581cc48e6c49ee2519dd983dcc209fda7d42053adb5d496042b2931e", + 1 + ], + [ + "51.near", + 1 + ], + [ + "51407b06e1740af4f5bc0d6fadbda8ec749087167b965808ed5430e9181f522f", + 2 + ], + [ + "518d7a7700b37d96543682aa5db65ec74082021542a01c32007af800d29da550", + 1 + ], + [ + "51ffc319cb90baa612c62fdb1c36f333db2034e403be321b88f1b0d6f754cfde", + 3 + ], + [ + "520.near", + 1 + ], + [ + "52fd2108c386cf51aea902e6fcea63db21887e3ee6ca4cb01bdea670c9dcf731", + 1 + ], + [ + "55.near", + 1 + ], + [ + "5df4b64e6e427f91d59b802b2447180b914edf54aa9afa28f6c9d5b4b1f7d8ea", + 1 + ], + [ + "5ec98442007ea2f2789e1b18fda4cede7af01e933bd055fd64cfa59d0764070f", + 4 + ], + [ + "5fff20e450e7297c455c88c4deb96679fb4bd360cf029ab65fe6722b37b6ddb5", + 4 + ], + [ + "6.near", + 1 + ], + [ + "6119bc71a0004cd7da1d838c90e43534c15d7e0b7f1233f9a9b9871070d1d54b", + 3 + ], + [ + "632be5d6e0ac3bec82f8cde5cbc29535b3d17c2728332de224d500dd3f919cb4", + 4 + ], + [ + "6577af8fce464dff3bdfcda04c9d93b80eb9bd2d7600648bfb154ece709199ee", + 7 + ], + [ + "659f400d18b5585fbe993d94090a2a7720b3905671c16d939f3ca75dc8748388", + 1 + ], + [ + "66.near", + 1 + ], + [ + "666.near", + 1 + ], + [ + "6755a67d6fc7e0d00eccc19f5f4ac4c796b70a9d05c600213079efdb35929ed8", + 2 + ], + [ + "6a62e81e18f79c5aaf721cd8ec79394e02dacef82b881efebd0840398161fa7e", + 1 + ], + [ + "6a9f536eaaccb62fc3ccda6018aa4a856893a9418dd7c0f62040d49240c925d1", + 1 + ], + [ + "6fe52682536bd3f2195f58a9ace51c9ded130959da5550d936bfb9d000045af1", + 2 + ], + [ + "7013df963f4f3226fc7fe393e0f459c4710c5e4b9f1455b772fb033adcd0db9d", + 2 + ], + [ + "710ea433fd8f67300c78c43351f847b48dd4ac0ae6b958d44081d5efc2458f5f", + 5 + ], + [ + "72119254f1872216214542967b700deecfe12f679c4206bce96159a580827b9a", + 2 + ], + [ + "736d01a5f9e2a6b6cebff2b56eaab5ba86d9efa51f6a292294e7a0f4e4f304bc", + 3 + ], + [ + "75180a176bc103dd239f43271ab8e95da5782755dc9a210925a0e722cb5fccbf", + 6 + ], + [ + "75570b99df84e2e8b138dbb6d084bed628cbb2e8179d0c7ba6d941b445d4f556", + 2 + ], + [ + "7671144.near", + 2 + ], + [ + "77.near", + 2 + ], + [ + "77725bb78fcfe81cbd640fdaa25f28fd3050ceae281c5d3764b694cc32cd66d4", + 1 + ], + [ + "7ee2991cb2076c43e90e37543efd0f1bac32f5c10f9b1755907723725c055444", + 6 + ], + [ + "7inch_dick.near", + 1 + ], + [ + "7nda.near", + 1 + ], + [ + "8.near", + 1 + ], + [ + "810d9ece5ed546dd3f17aea5f22b6044de70477d85ad53307eb07c920fcab139", + 4 + ], + [ + "8378ceb0c3dce515575bc04985acd20ab3de5bfc932daee11db781c0bac2c929", + 1 + ], + [ + "858035a9c9cec30c9f07be24ddf7b1ea1a513b001ce8046e4f6c5eb96222c51f", + 2 + ], + [ + "86.near", + 1 + ], + [ + "86b1889b9876cdfbd40b0bab21b8efa60559e02115850a743cbbdd8063b31803", + 1 + ], + [ + "88.near", + 2 + ], + [ + "8819beaa2fa7cef4fe800f6a890c1293e3e90f21d23027293a2690540de986f7", + 1 + ], + [ + "884e0aa90212fb91dc0b718987c5c5fc140cf5694316645f6716d5aaa2ef0a9a", + 1 + ], + [ + "888.near", + 1 + ], + [ + "88888.near", + 1 + ], + [ + "8888888.near", + 1 + ], + [ + "8cfeba62ea751cac817c7c0a39bd8a594f0f5ba91e4caf4304260785045b2e04", + 2 + ], + [ + "8dc251953d089d322fcdf21853331e12b467285174268f9020834962b3c6eb67", + 3 + ], + [ + "8ee0eabdb51eb6984ceb018851b93cea431681b0f9e0816061a8e7fe23c4a544", + 1 + ], + [ + "90b952a3f8a44642c4c61689bee725a5ce3927ba4a520be031e1c6d23fadc28f", + 3 + ], + [ + "90d23a0362766761a59dad0afca2243d3635f17bd0091aff1f5fb2eb804cb32c", + 10 + ], + [ + "9306bf915f8a4948d1c6b16d3a68897338309be7bdd6ef4887b2a11656c39ee5", + 25 + ], + [ + "93947f0956ab5e947b04775a08a3588818adbacf852de1af10a53cd2fc0913d7", + 2 + ], + [ + "9434e48096b096ac4111a3488cf01c4376aa5ffe111e6be818eaa35ed5be0c1e", + 5 + ], + [ + "960390f1cfc00c5dab00e5a6b148d8105b999b796d7b94b948f588f42fbcd1f1", + 3 + ], + [ + "996.near", + 2 + ], + [ + "999.near", + 1 + ], + [ + "99999.near", + 1 + ], + [ + "9ba1203d33ae0e66f8b0d55a0cc0de4bdc7decbc14912c3192a5d5aacd3f48f7", + 4 + ], + [ + "9e227608367c2ee4fa0b5f4aa1f6a1edca390e30c3eba3c1b478d767873edfb3", + 11 + ], + [ + "9e233ab3b452fd8e3cf322d4285880a30999767e09dc248c95347c0a1bba9bfd", + 3 + ], + [ + "9f01a286fca244938bfb85e4f42513d582b91829ef60ad54e0bf70b39850b2dc", + 2 + ], + [ + "9fb0e708aa699867ddf68297704f5733e0a960c32e4a5a141ebfcb930965105a", + 2 + ], + [ + "a.near", + 1 + ], + [ + "a0244742a4cbe6eb5227022032180ccd97f9860a7d0b3caa23f2da40401cda4f", + 3 + ], + [ + "a03d8af59a1e193e40df0cc3f86cc71854373d757777bc9df1bb3688b8977482", + 2 + ], + [ + "a3d68485efea9f80611ff9a983ea006b24481ca5856da8b2d799e2f51c9b98ce", + 4 + ], + [ + "a4bf751682fb82bc61de4bfd5824ab3191259309d2c37a87c34da62310ccb954", + 4 + ], + [ + "a707181abad1a2679da6ff53c55d8247a636d769215a736b4cb4984e41e3195d", + 4 + ], + [ + "a_mote.near", + 2 + ], + [ + "aaaeeeggg.near", + 2 + ], + [ + "aac2aad7527581b68ca6ec86344f799afa4b75a0014dd0919e755ab8a041c0e9", + 6 + ], + [ + "aad.near", + 1 + ], + [ + "aaronlutze.near", + 1 + ], + [ + "ab1.near", + 2 + ], + [ + "ab2.near", + 1 + ], + [ + "abc.near", + 1 + ], + [ + "abin.near", + 1 + ], + [ + "abl.near", + 2 + ], + [ + "abl2.near", + 2 + ], + [ + "aborjin.near", + 1 + ], + [ + "abosiqi.near", + 3 + ], + [ + "abraham.near", + 1 + ], + [ + "abstractmonday1.near", + 1 + ], + [ + "abu.near", + 1 + ], + [ + "abzman.near", + 1 + ], + [ + "acdab2c10ef8a4aa3e895c90814f3d7a4ca50c5582a3bdbca091f4ec783d924d", + 5 + ], + [ + "ace.near", + 1 + ], + [ + "acelee668.near", + 2 + ], + [ + "acotelo.near", + 1 + ], + [ + "acoyne1.near", + 1 + ], + [ + "acoyne2.near", + 1 + ], + [ + "acp.near", + 2 + ], + [ + "acpearson.near", + 2 + ], + [ + "ad9be0b70a92c588c53934f92a4ba4429ac6173228d9ecb12341fddf449c425e", + 1 + ], + [ + "adamko.near", + 1 + ], + [ + "admin.near", + 2 + ], + [ + "adnan.near", + 1 + ], + [ + "adrianchase.near", + 1 + ], + [ + "adrianirimia.near", + 2 + ], + [ + "adrimrl.near", + 1 + ], + [ + "adrymothe.near", + 1 + ], + [ + "aeeeinvest.near", + 1 + ], + [ + "aeror.near", + 2 + ], + [ + "afqanich.near", + 2 + ], + [ + "age.near", + 2 + ], + [ + "ahe.near", + 2 + ], + [ + "ahmadsalman.near", + 1 + ], + [ + "ahmedjacob.near", + 2 + ], + [ + "ahsette.near", + 1 + ], + [ + "ai.near", + 2 + ], + [ + "aidar.near", + 1 + ], + [ + "airocket.near", + 1 + ], + [ + "aiym.near", + 1 + ], + [ + "ajaykumarp0900.near", + 2 + ], + [ + "ajcct22.near", + 1 + ], + [ + "ak.near", + 1 + ], + [ + "ak6.near", + 1 + ], + [ + "akagetbetter.near", + 1 + ], + [ + "akariko.near", + 2 + ], + [ + "akartik87.near", + 1 + ], + [ + "akil.near", + 2 + ], + [ + "akueitaiwan.near", + 2 + ], + [ + "akulov.near", + 1 + ], + [ + "alan.near", + 1 + ], + [ + "alan1.near", + 2 + ], + [ + "alan2.near", + 2 + ], + [ + "alan3.near", + 1 + ], + [ + "alannice.near", + 1 + ], + [ + "alanromero6695.near", + 1 + ], + [ + "alby.near", + 2 + ], + [ + "alejoy2k.near", + 2 + ], + [ + "aleksandr.near", + 1 + ], + [ + "aleksandr_bakanov.near", + 1 + ], + [ + "aleksey.near", + 1 + ], + [ + "aleph.near", + 1 + ], + [ + "alethea.near", + 1 + ], + [ + "alex.near", + 3 + ], + [ + "alex1.near", + 2 + ], + [ + "alex13.near", + 1 + ], + [ + "alex487.near", + 1 + ], + [ + "alexander.near", + 1 + ], + [ + "alexandermacgg.near", + 1 + ], + [ + "alexbys.near", + 2 + ], + [ + "alexde.near", + 1 + ], + [ + "alexey.near", + 1 + ], + [ + "alexeymarkov080.near", + 1 + ], + [ + "alexk.near", + 1 + ], + [ + "alexmonttoya.near", + 1 + ], + [ + "alextom.near", + 1 + ], + [ + "alexyang.near", + 1 + ], + [ + "ali.near", + 1 + ], + [ + "aliaksandrh.near", + 2 + ], + [ + "alibaba.near", + 1 + ], + [ + "alifromcairo.near", + 1 + ], + [ + "aliskhan.near", + 2 + ], + [ + "alive29.near", + 1 + ], + [ + "aljpra.near", + 2 + ], + [ + "alkan.near", + 1 + ], + [ + "alma.near", + 2 + ], + [ + "almutasemalhaj.near", + 1 + ], + [ + "alpcan.near", + 1 + ], + [ + "alpha25.near", + 1 + ], + [ + "alsharif.near", + 1 + ], + [ + "alvin0617.near", + 1 + ], + [ + "am0250.near", + 1 + ], + [ + "ama89.near", + 2 + ], + [ + "amani.near", + 1 + ], + [ + "amazon.near", + 1 + ], + [ + "amg.near", + 2 + ], + [ + "amir.near", + 1 + ], + [ + "amirmurad.near", + 2 + ], + [ + "amisare.near", + 1 + ], + [ + "amit.near", + 1 + ], + [ + "amos.near", + 1 + ], + [ + "ampl.near", + 1 + ], + [ + "ams.near", + 1 + ], + [ + "amster.near", + 1 + ], + [ + "amyk.near", + 1 + ], + [ + "an.near", + 1 + ], + [ + "analilia23.near", + 1 + ], + [ + "analilia2303.near", + 1 + ], + [ + "anandp1005.near", + 1 + ], + [ + "anastdubrovina.near", + 2 + ], + [ + "andre.near", + 1 + ], + [ + "andre20113.near", + 1 + ], + [ + "andreafortugno.near", + 1 + ], + [ + "andreasmueller.near", + 2 + ], + [ + "andrecronje.near", + 1 + ], + [ + "andrewseptember.near", + 1 + ], + [ + "andrey.near", + 2 + ], + [ + "andreyvelde.near", + 3 + ], + [ + "andrius.near", + 2 + ], + [ + "andy.near", + 2 + ], + [ + "andy12.near", + 1 + ], + [ + "andyf.near", + 1 + ], + [ + "angrykoreandad.near", + 1 + ], + [ + "anisha.near", + 1 + ], + [ + "anki.near", + 2 + ], + [ + "anna.near", + 1 + ], + [ + "annachu8.near", + 1 + ], + [ + "annaleoprowell.near", + 1 + ], + [ + "annastories.near", + 1 + ], + [ + "annet09.near", + 1 + ], + [ + "annita.near", + 1 + ], + [ + "annmarzak2020.near", + 1 + ], + [ + "anoane.near", + 2 + ], + [ + "anonimus_it.near", + 1 + ], + [ + "anonymwhale.near", + 1 + ], + [ + "ant.near", + 1 + ], + [ + "antoinevergne.near", + 1 + ], + [ + "anton.near", + 1 + ], + [ + "antonbvb.near", + 1 + ], + [ + "antonio.near", + 1 + ], + [ + "antsibirev.near", + 1 + ], + [ + "anumation.near", + 1 + ], + [ + "anxongdong.near", + 1 + ], + [ + "anyhowclick.near", + 1 + ], + [ + "anyog77.near", + 1 + ], + [ + "aoi.near", + 1 + ], + [ + "apok.near", + 1 + ], + [ + "apolinares.near", + 1 + ], + [ + "aptemuyc.near", + 2 + ], + [ + "aqua_chiang.near", + 1 + ], + [ + "aquila.near", + 1 + ], + [ + "aramak.near", + 1 + ], + [ + "aravind.near", + 2 + ], + [ + "aravind1456.near", + 1 + ], + [ + "archebald.near", + 1 + ], + [ + "arco.near", + 1 + ], + [ + "arcosivan13.near", + 1 + ], + [ + "arctek.near", + 1 + ], + [ + "are.near", + 2 + ], + [ + "areddy.near", + 1 + ], + [ + "ares.near", + 5 + ], + [ + "arfrol.near", + 1 + ], + [ + "arhimond.near", + 2 + ], + [ + "aris.near", + 1 + ], + [ + "arista.near", + 1 + ], + [ + "arman.near", + 1 + ], + [ + "armi.near", + 1 + ], + [ + "arr.near", + 1 + ], + [ + "art.near", + 2 + ], + [ + "artem.near", + 1 + ], + [ + "arthur.near", + 1 + ], + [ + "arthur36.near", + 1 + ], + [ + "arthurs.near", + 1 + ], + [ + "arthurs2.near", + 1 + ], + [ + "artolose.near", + 1 + ], + [ + "artur.near", + 1 + ], + [ + "aryan.near", + 1 + ], + [ + "ash.near", + 5 + ], + [ + "ashwincc.near", + 9 + ], + [ + "asif.near", + 1 + ], + [ + "asotirov.near", + 1 + ], + [ + "aspaceodyssey2001.near", + 1 + ], + [ + "assefreak.near", + 2 + ], + [ + "astamo.near", + 1 + ], + [ + "astan.near", + 1 + ], + [ + "astro.near", + 1 + ], + [ + "astromartian.near", + 2 + ], + [ + "atillaofcrypto.near", + 1 + ], + [ + "atrdenis.near", + 1 + ], + [ + "atskbook.near", + 2 + ], + [ + "auction.near", + 1 + ], + [ + "audaciousamit30.near", + 1 + ], + [ + "audit.near", + 1 + ], + [ + "auel.near", + 1 + ], + [ + "aurel.near", + 1 + ], + [ + "aurora.near", + 1 + ], + [ + "austin.near", + 4 + ], + [ + "autox.near", + 2 + ], + [ + "avantiurin.near", + 2 + ], + [ + "avh857.near", + 1 + ], + [ + "aviy17.near", + 2 + ], + [ + "avizar.near", + 1 + ], + [ + "avogadro31.near", + 1 + ], + [ + "avtomax.near", + 1 + ], + [ + "aw_khalifa.near", + 1 + ], + [ + "awesome.near", + 1 + ], + [ + "awulf.near", + 1 + ], + [ + "axe.near", + 1 + ], + [ + "axel.near", + 1 + ], + [ + "axl.near", + 1 + ], + [ + "axser.near", + 2 + ], + [ + "ay.near", + 1 + ], + [ + "ayotom4u.near", + 2 + ], + [ + "ayy.near", + 1 + ], + [ + "az58348118.near", + 1 + ], + [ + "az6ykawallet.near", + 1 + ], + [ + "azaticus.near", + 1 + ], + [ + "b03a405ec73abb57ce8379889b4e705b6a33e2178f67349ccda1a87ef87e7cff", + 2 + ], + [ + "b0d8134aef7b38217d53e2df574064a7d2020ecdea4fa7c875a4c6c0b3aa0405", + 4 + ], + [ + "b17088e2d35317c7e38f08689973c71349e9bdc11e08dd4eb8ade316d79e7bad", + 3 + ], + [ + "b4b6c1ba1db210d9dfbb6c242d5d6266988ffae9bdc24f4b2be0b1e700704fc4", + 2 + ], + [ + "b4e194dc7b583f7b95c4549c47206f4283f38b208f293045609cd9ba819db17b", + 4 + ], + [ + "b525bd7c8dda7c6db8dd9da09c8791b2ee2c732c1db10478885084540b600180", + 3 + ], + [ + "b6954715.near", + 1 + ], + [ + "b87d7996f47a8d9c5c838457883abf543918bf1dc915ad033dfbcb40c8062cad", + 1 + ], + [ + "baa52093ca28dbc224c9a699ee559bd3b08c84f2d2c119ad7955722102623500", + 3 + ], + [ + "baba.near", + 1 + ], + [ + "babachyke.near", + 1 + ], + [ + "babooha1.near", + 1 + ], + [ + "babykuteok23.near", + 1 + ], + [ + "back2thailand.near", + 2 + ], + [ + "badgermilk33.near", + 4 + ], + [ + "badick.near", + 1 + ], + [ + "baed60a83adf64b3d89eefcc4c2e9bd53b7e86f02c0afc1e986e5fcf2db418a7", + 1 + ], + [ + "baer.near", + 1 + ], + [ + "baf.near", + 1 + ], + [ + "baggy.near", + 1 + ], + [ + "baidu.near", + 1 + ], + [ + "baillokb.near", + 1 + ], + [ + "bakautm.near", + 1 + ], + [ + "bakulowski23.near", + 1 + ], + [ + "baltitus2020.near", + 5 + ], + [ + "bambaleo.near", + 1 + ], + [ + "bandele.near", + 1 + ], + [ + "bangbangwu.near", + 1 + ], + [ + "bank.near", + 1 + ], + [ + "barbear.near", + 1 + ], + [ + "bas.near", + 2 + ], + [ + "basando09gambler.near", + 1 + ], + [ + "basedserg.near", + 2 + ], + [ + "basov.near", + 1 + ], + [ + "batbh.near", + 1 + ], + [ + "battuta.near", + 1 + ], + [ + "bayswatermay65.near", + 1 + ], + [ + "bayusoe.near", + 1 + ], + [ + "bcd38694fae6dba4b2458d44f5b094096ffc3d2342641e9c4d662e666c05ca9c", + 6 + ], + [ + "bcfiekuo25656.near", + 1 + ], + [ + "bdetto2010.near", + 1 + ], + [ + "be.near", + 1 + ], + [ + "bear.near", + 1 + ], + [ + "beatguy99.near", + 1 + ], + [ + "bedivere007.near", + 1 + ], + [ + "beetlz.near", + 1 + ], + [ + "begimay.near", + 1 + ], + [ + "believemestalinspeakslikeyou.near", + 1 + ], + [ + "bellyom.near", + 1 + ], + [ + "belove.near", + 1 + ], + [ + "ben_1507.near", + 1 + ], + [ + "benhartney.near", + 1 + ], + [ + "benjake.near", + 1 + ], + [ + "benjamin.near", + 2 + ], + [ + "benjaminbalazs.near", + 2 + ], + [ + "benoit.near", + 3 + ], + [ + "bentien.near", + 2 + ], + [ + "beny.near", + 1 + ], + [ + "berkayboga.near", + 1 + ], + [ + "berlin.near", + 1 + ], + [ + "berry3425.near", + 2 + ], + [ + "besavage.near", + 1 + ], + [ + "bet.near", + 1 + ], + [ + "bf15b701370b4024a3b465669425e0c38dab3d11f5bf536c62826d3ca38397d9", + 1 + ], + [ + "bfa14bc3bbc363ace68a1bbce2c03400ad956174d8c0caeb61426484635ef8ac", + 4 + ], + [ + "bibo.near", + 1 + ], + [ + "bichenkk.near", + 1 + ], + [ + "bigcoin.near", + 1 + ], + [ + "bigfish.near", + 2 + ], + [ + "bigpapa.near", + 1 + ], + [ + "bigpapa2.near", + 1 + ], + [ + "bilaljivraj.near", + 2 + ], + [ + "billtr.near", + 2 + ], + [ + "binancelabs.near", + 1 + ], + [ + "bingowrt.near", + 2 + ], + [ + "biogkosm.near", + 1 + ], + [ + "bipbippp.near", + 1 + ], + [ + "birchmd.near", + 2 + ], + [ + "birishka.near", + 1 + ], + [ + "bitcoin.near", + 1 + ], + [ + "bitcoinaddict.near", + 1 + ], + [ + "bitcoinbarber.near", + 2 + ], + [ + "bitcoinrok.near", + 2 + ], + [ + "bitcoins.near", + 2 + ], + [ + "bitfinex.near", + 2 + ], + [ + "bithumb.near", + 1 + ], + [ + "bitinvestia.near", + 1 + ], + [ + "bitlion.near", + 1 + ], + [ + "bitmax.near", + 2 + ], + [ + "bitprop.near", + 1 + ], + [ + "bizi.near", + 1 + ], + [ + "bjorn.near", + 1 + ], + [ + "bkk920.near", + 1 + ], + [ + "bkvkrll.near", + 2 + ], + [ + "blackflag.near", + 1 + ], + [ + "blackheuk.near", + 2 + ], + [ + "blackrium.near", + 1 + ], + [ + "blake.near", + 1 + ], + [ + "blakeboe.near", + 2 + ], + [ + "blakenear.near", + 2 + ], + [ + "blanco-75.near", + 1 + ], + [ + "blasio.near", + 1 + ], + [ + "blasio02.near", + 1 + ], + [ + "blinks.near", + 1 + ], + [ + "blitz-123.near", + 2 + ], + [ + "blizzhard.near", + 1 + ], + [ + "block.near", + 1 + ], + [ + "blockchaincompanies.near", + 2 + ], + [ + "blockdaemon.near", + 1 + ], + [ + "blockstech.near", + 1 + ], + [ + "blok1919.near", + 2 + ], + [ + "bloomedemperor777.near", + 2 + ], + [ + "bluccoj.near", + 1 + ], + [ + "bluccox.near", + 1 + ], + [ + "blueblindmelon.near", + 1 + ], + [ + "blxpro.near", + 1 + ], + [ + "bmahony.near", + 1 + ], + [ + "bmahony2.near", + 1 + ], + [ + "bnb.near", + 1 + ], + [ + "bobek.near", + 1 + ], + [ + "bobjmiles.near", + 2 + ], + [ + "bodin2499.near", + 2 + ], + [ + "bodya.near", + 1 + ], + [ + "bogdan.near", + 1 + ], + [ + "bohdan.near", + 1 + ], + [ + "boiler1981.near", + 2 + ], + [ + "bojana.near", + 1 + ], + [ + "boka_sfc.near", + 4 + ], + [ + "bokhaled.near", + 2 + ], + [ + "bombaymillions.near", + 2 + ], + [ + "bomboleylo.near", + 1 + ], + [ + "bond_y8674.near", + 1 + ], + [ + "bonesdu.near", + 1 + ], + [ + "bonestoh.near", + 2 + ], + [ + "bonka.near", + 1 + ], + [ + "bonmale.near", + 1 + ], + [ + "boot1985.near", + 1 + ], + [ + "borbox14.near", + 1 + ], + [ + "borneodreams.near", + 1 + ], + [ + "boss.near", + 1 + ], + [ + "boss57.near", + 1 + ], + [ + "boulder.near", + 1 + ], + [ + "bowen1.near", + 2 + ], + [ + "br4h.near", + 1 + ], + [ + "braaaaam.near", + 2 + ], + [ + "brad.near", + 2 + ], + [ + "brambumbes.near", + 1 + ], + [ + "bratukhin.near", + 2 + ], + [ + "brianbencom315.near", + 1 + ], + [ + "bridgecraven.near", + 2 + ], + [ + "brightside.near", + 3 + ], + [ + "brka.near", + 1 + ], + [ + "bro.near", + 1 + ], + [ + "brossan.near", + 1 + ], + [ + "brunitob.near", + 2 + ], + [ + "bruno.near", + 1 + ], + [ + "bryan994.near", + 1 + ], + [ + "btc.near", + 1 + ], + [ + "btc123.near", + 1 + ], + [ + "btcd.near", + 1 + ], + [ + "buchi.near", + 1 + ], + [ + "bugzbtc.near", + 1 + ], + [ + "buivantai.near", + 2 + ], + [ + "bulldogs8tigers.near", + 1 + ], + [ + "bulldojkee.near", + 1 + ], + [ + "bunghi.near", + 1 + ], + [ + "bunnydoodle12.near", + 1 + ], + [ + "burgheleaico.near", + 1 + ], + [ + "bustups22.near", + 1 + ], + [ + "buy.near", + 1 + ], + [ + "buzi.near", + 4 + ], + [ + "bve73.near", + 2 + ], + [ + "bydaichi.near", + 1 + ], + [ + "bymetin79.near", + 1 + ], + [ + "c3de7f0ba6ee1cfca2e55541707bb91cb6d0c59a0cb8a489a7f00f5c14360b88", + 4 + ], + [ + "c3lestial.near", + 1 + ], + [ + "c4885c8ab855c95d905be6b160333f55a9294b89641035bd321ab7d051f7e882", + 4 + ], + [ + "c55e4140ef5ed72bfa54f560c795ab96a38cc4e9079c7d07a88433c5b19662fb", + 4 + ], + [ + "c711198b5065bd07dac5a3f2bdc97cd3700b0af7cba0f4b51d91a350513ec942", + 11 + ], + [ + "c77b49f39a3ae39c1d94d8e4186b3110a022a4118c49d7ef2a913809c4046c50", + 4 + ], + [ + "c7f2a4ae16c9514159386f5351bf0365032b8c76801341087bc3627221cdd25e", + 9 + ], + [ + "c80f0a2093b7075b3717ea94dcc3bd54b369e47bc9096d6920f2baf16d1693ec", + 4 + ], + [ + "cacossio.near", + 1 + ], + [ + "cadel.near", + 1 + ], + [ + "caesar0314.near", + 2 + ], + [ + "caizhenhao.near", + 1 + ], + [ + "cake.near", + 2 + ], + [ + "can.near", + 1 + ], + [ + "canahmett.near", + 1 + ], + [ + "captainwhale.near", + 1 + ], + [ + "carangel.near", + 1 + ], + [ + "carles.near", + 1 + ], + [ + "carlos.near", + 2 + ], + [ + "carolinanoriega.near", + 2 + ], + [ + "cashman.near", + 1 + ], + [ + "casino.near", + 1 + ], + [ + "caso234.near", + 1 + ], + [ + "caso234z.near", + 1 + ], + [ + "castleman.near", + 2 + ], + [ + "catalin.near", + 2 + ], + [ + "catfat.near", + 1 + ], + [ + "catoshi.near", + 1 + ], + [ + "cbbffb1ef77b7258ae161102d549251177205345c5318e0be9ab1c0adf95f132", + 2 + ], + [ + "cbret81.near", + 1 + ], + [ + "cc.near", + 1 + ], + [ + "cc3f289f216158c78a126d9394b9b7e50fd27d327e2fc630668bc068a6d07548", + 2 + ], + [ + "cd1ec14a5e33a4d922fffc6eece8dfad31964e7238f28a431550ce2919faad63", + 1 + ], + [ + "ce0c6c3f00cdc1495d78b222b933b7a4a39bfcfdc6b19ee95020ca1faee6ebba", + 1 + ], + [ + "cea7ac3268c4efe1639776501cfd3d486d0cb61e9a0e5888986635944b07d539", + 5 + ], + [ + "cedric.near", + 1 + ], + [ + "ceo.near", + 1 + ], + [ + "cestovatel.near", + 2 + ], + [ + "cesxy.near", + 1 + ], + [ + "ceyptoslash.near", + 1 + ], + [ + "cfzy8888.near", + 1 + ], + [ + "cgrvgha6bvjh2apkzbbcdxdouy3yapjj.near", + 1 + ], + [ + "chad.near", + 1 + ], + [ + "chagabee.near", + 1 + ], + [ + "chain_green.near", + 1 + ], + [ + "chainwang.near", + 2 + ], + [ + "champinemcfaddin.near", + 1 + ], + [ + "chance2dance.near", + 1 + ], + [ + "chandru.near", + 2 + ], + [ + "changning.near", + 1 + ], + [ + "charles.near", + 1 + ], + [ + "charliephy.near", + 2 + ], + [ + "charlotte.near", + 1 + ], + [ + "chcobesc.near", + 1 + ], + [ + "cheburechek.near", + 2 + ], + [ + "cheikhada.near", + 1 + ], + [ + "chen813.near", + 3 + ], + [ + "chenda.near", + 1 + ], + [ + "chengizkhann.near", + 1 + ], + [ + "chengizkhanns.near", + 1 + ], + [ + "chengxiaokris.near", + 1 + ], + [ + "cherouvim21.near", + 1 + ], + [ + "cheryl.near", + 1 + ], + [ + "cheslav.near", + 1 + ], + [ + "chestnut.near", + 2 + ], + [ + "chewie1.near", + 1 + ], + [ + "cheyne.near", + 1 + ], + [ + "chez77.near", + 1 + ], + [ + "chief.near", + 1 + ], + [ + "china.near", + 5 + ], + [ + "chipon46.near", + 1 + ], + [ + "chirag911.near", + 1 + ], + [ + "chiwairen.near", + 5 + ], + [ + "chouchou.near", + 1 + ], + [ + "chouhsinyo.near", + 4 + ], + [ + "chris.near", + 1 + ], + [ + "chris_ns.near", + 1 + ], + [ + "chrispaes.near", + 2 + ], + [ + "christiaan.near", + 2 + ], + [ + "christian.near", + 1 + ], + [ + "christianwolf.near", + 1 + ], + [ + "chrom175.near", + 1 + ], + [ + "chuanshao.near", + 1 + ], + [ + "chuckando.near", + 2 + ], + [ + "chulei.near", + 1 + ], + [ + "chun.near", + 1 + ], + [ + "chung.near", + 3 + ], + [ + "chuppi1108.near", + 2 + ], + [ + "ciprian.near", + 1 + ], + [ + "ciptim.near", + 1 + ], + [ + "ckb.near", + 4 + ], + [ + "ckhk5661.near", + 2 + ], + [ + "claim-tokens.near", + 1 + ], + [ + "clark.near", + 2 + ], + [ + "claudia.near", + 1 + ], + [ + "claudiu.near", + 1 + ], + [ + "claunas.near", + 1 + ], + [ + "clawmvp.near", + 1 + ], + [ + "cleaner.near", + 1 + ], + [ + "cliu.near", + 2 + ], + [ + "cm.near", + 1 + ], + [ + "cn.near", + 1 + ], + [ + "coachb.near", + 1 + ], + [ + "cocolin51.near", + 1 + ], + [ + "code.near", + 1 + ], + [ + "coffee.near", + 1 + ], + [ + "coinbank.near", + 1 + ], + [ + "coinbase.near", + 2 + ], + [ + "coindex.near", + 1 + ], + [ + "coinless.near", + 1 + ], + [ + "coinlist.near", + 1 + ], + [ + "coinmarketcap.near", + 1 + ], + [ + "coinmike86.near", + 1 + ], + [ + "coins.near", + 1 + ], + [ + "coldwinter.near", + 1 + ], + [ + "colli.near", + 1 + ], + [ + "combatant.near", + 1 + ], + [ + "comcmipi.near", + 1 + ], + [ + "commam7109.near", + 1 + ], + [ + "companya.near", + 1 + ], + [ + "compound.near", + 1 + ], + [ + "condes.near", + 1 + ], + [ + "contributors.near", + 1 + ], + [ + "coonolex.near", + 1 + ], + [ + "cooper.near", + 2 + ], + [ + "copperbrain.near", + 1 + ], + [ + "coreman.near", + 1 + ], + [ + "cosmopolite.near", + 1 + ], + [ + "cowaribit.near", + 1 + ], + [ + "cr.near", + 1 + ], + [ + "craig.near", + 1 + ], + [ + "craigwright.near", + 2 + ], + [ + "crasengover.near", + 2 + ], + [ + "crass.near", + 2 + ], + [ + "crazypiano.near", + 1 + ], + [ + "crc5.near", + 1 + ], + [ + "crespo58.near", + 1 + ], + [ + "criogen.near", + 1 + ], + [ + "crlwallet.near", + 1 + ], + [ + "crypted.near", + 1 + ], + [ + "cryptio.near", + 2 + ], + [ + "cryptnito.near", + 1 + ], + [ + "crypto80.near", + 1 + ], + [ + "crypto_bu.near", + 2 + ], + [ + "cryptobro.near", + 1 + ], + [ + "cryptochin.near", + 2 + ], + [ + "cryptocurrency.near", + 1 + ], + [ + "cryptodonny.near", + 2 + ], + [ + "cryptodruu.near", + 1 + ], + [ + "cryptodzh.near", + 1 + ], + [ + "cryptoearn.near", + 2 + ], + [ + "cryptoinvestingk.near", + 1 + ], + [ + "cryptojay.near", + 1 + ], + [ + "cryptojones.near", + 1 + ], + [ + "cryptoking.near", + 2 + ], + [ + "cryptolab.near", + 1 + ], + [ + "cryptolife.near", + 2 + ], + [ + "cryptolonewolf.near", + 2 + ], + [ + "cryptomearis.near", + 1 + ], + [ + "cryptomilion.near", + 1 + ], + [ + "cryptomoneymaker.near", + 1 + ], + [ + "crypton0ob.near", + 1 + ], + [ + "cryptonear.near", + 1 + ], + [ + "cryptosa.near", + 2 + ], + [ + "cryptosafu.near", + 1 + ], + [ + "cryptotiger.near", + 1 + ], + [ + "cryptovision.near", + 1 + ], + [ + "cryptowhale.near", + 1 + ], + [ + "cryptoyoda.near", + 1 + ], + [ + "crysto73.near", + 1 + ], + [ + "csk13925957545.near", + 1 + ], + [ + "ct.near", + 1 + ], + [ + "cxz.near", + 1 + ], + [ + "cypherhunter.near", + 1 + ], + [ + "cz.near", + 1 + ], + [ + "czech.near", + 2 + ], + [ + "czizzy.near", + 3 + ], + [ + "d075b44b14dbda71a31f05abd24aa4c76b1f3ef42898abf67073d38d8fbfe801", + 1 + ], + [ + "d1.near", + 1 + ], + [ + "d1eselforeva.near", + 1 + ], + [ + "d258ab4694321dcde0c5bcc7097e5dafff46523840cf3a7370d78565e96d9253", + 4 + ], + [ + "d31700.near", + 1 + ], + [ + "d4e5cc25b629236480a236553680f218a37972dc3a5eee1bca1aa31838c80541", + 1 + ], + [ + "d53f439c2377739a1e529f5438f2a44821c853ab871d38221f185ccfb7fc8784", + 34 + ], + [ + "d5617c05503cac78c0aae88ed9550932c104e4e734166fa874eefe93c23eacd2", + 1 + ], + [ + "d5f29321220d0829ca1565d04a1fc07aaff326ab655a299add8543aa41bd4f5f", + 4 + ], + [ + "d6e8bfda2d5b11da382de2edb7d4f5a05237a539847dd794cd9ee30ec859bae2", + 4 + ], + [ + "d7c9d5126ef6b118224e49cf44133df2adde499e053d044fa4f860bb97552bbd", + 2 + ], + [ + "daeho.near", + 1 + ], + [ + "daf.near", + 1 + ], + [ + "daflv.near", + 1 + ], + [ + "dahlberry.near", + 4 + ], + [ + "dai.near", + 1 + ], + [ + "daixinyi.near", + 1 + ], + [ + "daliut.near", + 2 + ], + [ + "damianguenther.near", + 1 + ], + [ + "dana888.near", + 1 + ], + [ + "danbiscuitbrown.near", + 1 + ], + [ + "danchr.near", + 1 + ], + [ + "dandoon.near", + 1 + ], + [ + "dane75.near", + 1 + ], + [ + "dangdh.near", + 1 + ], + [ + "danger25.near", + 1 + ], + [ + "dani.near", + 1 + ], + [ + "daniel.near", + 2 + ], + [ + "danielc.near", + 1 + ], + [ + "danielledelsing.near", + 1 + ], + [ + "danil.near", + 1 + ], + [ + "daniyar.near", + 2 + ], + [ + "danklasson.near", + 2 + ], + [ + "dannn14.near", + 1 + ], + [ + "dannyvanosdeman.near", + 1 + ], + [ + "danosn69191969.near", + 1 + ], + [ + "danyjj.near", + 1 + ], + [ + "dao.near", + 1 + ], + [ + "daoasset.near", + 1 + ], + [ + "daphunk.near", + 1 + ], + [ + "dar1.near", + 1 + ], + [ + "dariako13.near", + 1 + ], + [ + "darkec.near", + 1 + ], + [ + "darkodoog.near", + 3 + ], + [ + "darkodoog2.near", + 2 + ], + [ + "darksurfer.near", + 1 + ], + [ + "darlka.near", + 2 + ], + [ + "daunjuan.near", + 2 + ], + [ + "davidbksantos.near", + 1 + ], + [ + "daviddion.near", + 1 + ], + [ + "davideverda.near", + 2 + ], + [ + "davidistaken.near", + 1 + ], + [ + "davinci.near", + 3 + ], + [ + "davram.near", + 1 + ], + [ + "dawg.near", + 1 + ], + [ + "dawns.near", + 1 + ], + [ + "daz.near", + 1 + ], + [ + "dbswap.near", + 2 + ], + [ + "dcha16.near", + 1 + ], + [ + "ddanchev.near", + 1 + ], + [ + "dear.near", + 1 + ], + [ + "decentraliseme.near", + 2 + ], + [ + "deepakdinkan.near", + 2 + ], + [ + "def626f5e77305e783fc61be8ff390c6010bca1779d5a7a0d218dd76802a51c7", + 2 + ], + [ + "defi.near", + 2 + ], + [ + "defiance.near", + 1 + ], + [ + "defykonglong.near", + 1 + ], + [ + "deg.near", + 1 + ], + [ + "del_la.near", + 2 + ], + [ + "delboy.near", + 1 + ], + [ + "delpadschnick.near", + 1 + ], + [ + "deltarun1.near", + 2 + ], + [ + "demerald.near", + 1 + ], + [ + "demon7719.near", + 1 + ], + [ + "demos.near", + 1 + ], + [ + "den.near", + 37 + ], + [ + "den20102004.near", + 1 + ], + [ + "denis125.near", + 1 + ], + [ + "dennis.near", + 1 + ], + [ + "dennison.near", + 1 + ], + [ + "denturebur.near", + 2 + ], + [ + "deposit.near", + 1 + ], + [ + "derkaalbers.near", + 2 + ], + [ + "deskertw.near", + 2 + ], + [ + "destrla.near", + 1 + ], + [ + "desy.near", + 1 + ], + [ + "dev.near", + 3 + ], + [ + "devasterus.near", + 1 + ], + [ + "devdn3dsr63.near", + 1 + ], + [ + "deviate.near", + 1 + ], + [ + "dex.near", + 2 + ], + [ + "dexxa05.near", + 2 + ], + [ + "deyond2020.near", + 1 + ], + [ + "deysler.near", + 1 + ], + [ + "dfa635185b3392d5e5a0eed62eb1f58bb7d6392ce2c5ff07acf4922726744256", + 2 + ], + [ + "dfcb2aff667f14b9adc3522e2db4b2f898f6cc3bf42300175f98661eb89f19b6", + 1 + ], + [ + "dfe4dbf46adc0dfa56c5f612dabe2caf93f783f22f3242e07ee653d85ec421a7", + 3 + ], + [ + "dg.near", + 1 + ], + [ + "dgimmy.near", + 1 + ], + [ + "dharmadhikari.near", + 1 + ], + [ + "dhaval567.near", + 1 + ], + [ + "dhoick.near", + 2 + ], + [ + "diana.near", + 1 + ], + [ + "didi.near", + 1 + ], + [ + "diegordo.near", + 1 + ], + [ + "digimon3y.near", + 3 + ], + [ + "dii.near", + 2 + ], + [ + "dillsp88.near", + 1 + ], + [ + "dimaro.near", + 1 + ], + [ + "dimaro1.near", + 10 + ], + [ + "dimmao.near", + 2 + ], + [ + "dinhhongd.near", + 2 + ], + [ + "dinokidjs.near", + 1 + ], + [ + "dio.near", + 1 + ], + [ + "dipsyv.near", + 2 + ], + [ + "direzza.near", + 1 + ], + [ + "dissy.near", + 2 + ], + [ + "distinct.near", + 1 + ], + [ + "dj72crypt.near", + 1 + ], + [ + "django.near", + 1 + ], + [ + "djchahal.near", + 1 + ], + [ + "djoker.near", + 1 + ], + [ + "dk009.near", + 2 + ], + [ + "dkdk009.near", + 1 + ], + [ + "dlt.near", + 1 + ], + [ + "dlwodlwogks.near", + 1 + ], + [ + "dmark2729.near", + 1 + ], + [ + "dmitri.near", + 2 + ], + [ + "dmt.near", + 2 + ], + [ + "dnovy.near", + 1 + ], + [ + "dog.near", + 1 + ], + [ + "dojisama.near", + 1 + ], + [ + "dolphintwo.near", + 1 + ], + [ + "domains.near", + 1 + ], + [ + "dominic.near", + 2 + ], + [ + "don.near", + 9 + ], + [ + "donaldp.near", + 1 + ], + [ + "donan.near", + 1 + ], + [ + "dongding.near", + 1 + ], + [ + "dot.near", + 1 + ], + [ + "dozer.near", + 2 + ], + [ + "dpod52.near", + 1 + ], + [ + "dragonbran945.near", + 1 + ], + [ + "drchudi_near.near", + 1 + ], + [ + "drewx.near", + 2 + ], + [ + "drey.near", + 1 + ], + [ + "drt860109.near", + 1 + ], + [ + "drupa.near", + 1 + ], + [ + "dsalv.near", + 2 + ], + [ + "dscocco.near", + 1 + ], + [ + "dsgital.near", + 1 + ], + [ + "dsouza.near", + 2 + ], + [ + "dsrv.near", + 1 + ], + [ + "dss.near", + 1 + ], + [ + "du.near", + 1 + ], + [ + "duckhai.near", + 2 + ], + [ + "duongkta.near", + 1 + ], + [ + "duongphi0111.near", + 1 + ], + [ + "durga.near", + 1 + ], + [ + "duxin.near", + 1 + ], + [ + "duyhuanh.near", + 1 + ], + [ + "duytan.near", + 3 + ], + [ + "dxndaniel.near", + 1 + ], + [ + "dytackxh4.near", + 1 + ], + [ + "dz.near", + 1 + ], + [ + "dzlzv.near", + 1 + ], + [ + "e02df51342ae2b7193ac78c721ad12b1dbc684ef99be531ebf2939fa210140cd", + 5 + ], + [ + "e0x.near", + 1 + ], + [ + "e1862ef993daf5a4b7c34aaa8c8dfe018ed273b5e393e73385e5a6f0b9ca7df7", + 8 + ], + [ + "e23100a2e5a3478018364b876ceedc416162f52e6f1edd832f924276bce712e1", + 3 + ], + [ + "e5292fc55f5eee5acbb6de64b5da63b9168bec1741de0b19b88b85fe9fe92996", + 2 + ], + [ + "e597a6b44de31c79b6c15f2030e53ef701ad84efa0f2fa41b5621724132b29fd", + 7 + ], + [ + "e644521f6235ffe0943f6f00ab5081c39b9a32e22feaece47998115a3fa6a7da", + 1 + ], + [ + "e77dc00ba3ca284f3c3186a59dbffce7729e7f8f0dbba7b9bed45b2e5646b983", + 26 + ], + [ + "e7ed1672d8ffa2ba50634e619f0144c55fb44265e7ad979376efbaa8e8a55fec", + 1 + ], + [ + "e7ef040fba4e567e2543e6814387749709d6152329220111b6895f1a845d4ddc", + 3 + ], + [ + "e9bf7bbfe620284ddb7451b8aec7d40a475fd1cf5400f4fe2f2a9a883f0ba462", + 6 + ], + [ + "ea3843fde8e221f237b5cd6d269fcdca69d10b2fabd53e57bde6946dbf5548d4", + 5 + ], + [ + "eagle.near", + 1 + ], + [ + "eagle1024.near", + 2 + ], + [ + "ebc1599e57e840017d8120ba70d9762a21a0444924a29769a5b8fed2546c5dd9", + 3 + ], + [ + "eddienuta.near", + 1 + ], + [ + "ede294.near", + 2 + ], + [ + "eden.near", + 2 + ], + [ + "edgar.near", + 1 + ], + [ + "edgarvgz.near", + 2 + ], + [ + "edrush2004.near", + 1 + ], + [ + "eelco.near", + 2 + ], + [ + "ef46b5bc99d85aacc5858af3ffdb2cc9145eb9913ba3e33317cfa90288312264", + 2 + ], + [ + "ef6e3920b04ff7d203c79ea0271c4fbd3a0c00f699ba72f36b2e1cc2c8930fbe", + 1 + ], + [ + "effylou.near", + 1 + ], + [ + "egle.near", + 2 + ], + [ + "einar.near", + 2 + ], + [ + "einsteiger.near", + 1 + ], + [ + "eintracht1.near", + 6 + ], + [ + "eirjag.near", + 1 + ], + [ + "ek.near", + 5 + ], + [ + "elcapitan.near", + 3 + ], + [ + "elecnodo.near", + 1 + ], + [ + "electro.near", + 1 + ], + [ + "electroexts.near", + 1 + ], + [ + "elegant.near", + 1 + ], + [ + "elemond.near", + 2 + ], + [ + "elen.near", + 1 + ], + [ + "elena.near", + 1 + ], + [ + "elenabot.near", + 1 + ], + [ + "elenagonzalez.near", + 1 + ], + [ + "elianewells.near", + 2 + ], + [ + "ellyse11.near", + 1 + ], + [ + "ellyse12.near", + 1 + ], + [ + "elnikov.near", + 1 + ], + [ + "elorpar.near", + 2 + ], + [ + "elpav70.near", + 1 + ], + [ + "elrider71.near", + 1 + ], + [ + "elrulo.near", + 1 + ], + [ + "elvisy.near", + 1 + ], + [ + "emanueldiniz.near", + 1 + ], + [ + "embiei.near", + 1 + ], + [ + "embydextrous.near", + 2 + ], + [ + "emcs1974.near", + 1 + ], + [ + "emiel.near", + 1 + ], + [ + "emili2017.near", + 1 + ], + [ + "eminem.near", + 1 + ], + [ + "emmazhuxy.near", + 3 + ], + [ + "emptyvoid.near", + 1 + ], + [ + "emre3jw3.near", + 2 + ], + [ + "end.near", + 1 + ], + [ + "end.was.near", + 2 + ], + [ + "enguessanro.near", + 1 + ], + [ + "enzy.near", + 1 + ], + [ + "ep.near", + 1 + ], + [ + "equilibrium.near", + 1 + ], + [ + "erfan.near", + 1 + ], + [ + "erfan_isaac.near", + 1 + ], + [ + "ericyfhung.near", + 2 + ], + [ + "erik.near", + 2 + ], + [ + "ernest.near", + 2 + ], + [ + "ernesto.near", + 1 + ], + [ + "ernesto_cardenas_near.near", + 2 + ], + [ + "erwinfranken.near", + 1 + ], + [ + "esc.near", + 2 + ], + [ + "ese.near", + 2 + ], + [ + "est1908.near", + 1 + ], + [ + "eth.near", + 1 + ], + [ + "ethd.near", + 2 + ], + [ + "ethyhy.near", + 1 + ], + [ + "eti_eyen01.near", + 1 + ], + [ + "etsk.near", + 2 + ], + [ + "eugent.near", + 5 + ], + [ + "europe.near", + 1 + ], + [ + "euthepronto.near", + 1 + ], + [ + "everstake.near", + 1 + ], + [ + "everstake1.near", + 9 + ], + [ + "evg1987a.near", + 1 + ], + [ + "evgen.near", + 1 + ], + [ + "evgeny.near", + 1 + ], + [ + "evgor.near", + 1 + ], + [ + "evian.near", + 1 + ], + [ + "eviltwindc.near", + 2 + ], + [ + "evkim.near", + 2 + ], + [ + "exan.near", + 1 + ], + [ + "excalibur.near", + 1 + ], + [ + "exiart.near", + 1 + ], + [ + "ey.near", + 1 + ], + [ + "ezbruh.near", + 1 + ], + [ + "f.near", + 1 + ], + [ + "f1tz14.near", + 1 + ], + [ + "f2e7e054d0f96e01f1f90e7f2bd5a3fd55437ace0e32b67183f6f3e6eb18f2ad", + 1 + ], + [ + "f5912374fd229bd851f73250abb8839f7fee00b9f17df4915142edfa9ec819c8", + 2 + ], + [ + "fabi.near", + 1 + ], + [ + "fabiolaluna.near", + 2 + ], + [ + "facebook.near", + 2 + ], + [ + "fadiseva.near", + 1 + ], + [ + "fadyamr.near", + 2 + ], + [ + "faprinvest.near", + 2 + ], + [ + "far.near", + 1 + ], + [ + "farhod.near", + 3 + ], + [ + "faridrached.near", + 1 + ], + [ + "faruk.near", + 1 + ], + [ + "fathurohman.near", + 3 + ], + [ + "fauji25.near", + 1 + ], + [ + "fb4caef88752f052dd8a0e17ec18e2a8ba0d75db694dd5ec889de6d80908f21a", + 4 + ], + [ + "fbalwy.near", + 1 + ], + [ + "fc31f902de0ca48ef5def39c3a3d1540fc877b6197fb65c55f71fccf3350a40f", + 1 + ], + [ + "fckmyluck.near", + 2 + ], + [ + "fd04644aae2a2f84e3742269140f5bdc35c70fbed13ff3bd5642a78b0ac572e4", + 2 + ], + [ + "fdbd7d79985344fcf58d4f8a8d067549b907c85355d8d261db19b2f8e5978615", + 1 + ], + [ + "fe5ef5f84e9d40b4901fa8dc602e0acfc77b8b9cbb40508a3d379271287a8370", + 1 + ], + [ + "fedul.near", + 2 + ], + [ + "feng.near", + 1 + ], + [ + "fengkiej.near", + 5 + ], + [ + "ferhat89.near", + 1 + ], + [ + "ferlee.near", + 1 + ], + [ + "fern.near", + 2 + ], + [ + "fernando7.near", + 1 + ], + [ + "ff7247ce7fce8a6d4e615a0507bf0d0d47282fa4cd1dbf1ba5c4aa2e34f4ebca", + 1 + ], + [ + "ff8rinoaff8.near", + 2 + ], + [ + "fifi.near", + 1 + ], + [ + "filipe.near", + 1 + ], + [ + "fill.near", + 2 + ], + [ + "filtom.near", + 1 + ], + [ + "finance.near", + 1 + ], + [ + "finchen.near", + 1 + ], + [ + "finhumb.near", + 1 + ], + [ + "firefist.near", + 1 + ], + [ + "first.near", + 1 + ], + [ + "firstmajor.near", + 1 + ], + [ + "fjbase21.near", + 1 + ], + [ + "fjythomas.near", + 2 + ], + [ + "flashboys.near", + 2 + ], + [ + "flatron7.near", + 1 + ], + [ + "flavian.near", + 1 + ], + [ + "flo.near", + 3 + ], + [ + "florian.near", + 1 + ], + [ + "florinda_gomes.near", + 1 + ], + [ + "fluslie.near", + 1 + ], + [ + "fluxper.near", + 1 + ], + [ + "flyinglooper.near", + 1 + ], + [ + "fmiah.near", + 2 + ], + [ + "fog.near", + 1 + ], + [ + "fokusnik.near", + 1 + ], + [ + "fomo.near", + 1 + ], + [ + "foobar.near", + 2 + ], + [ + "fooc.near", + 1 + ], + [ + "forest.near", + 1 + ], + [ + "foundation.near", + 1 + ], + [ + "foxtraveller.near", + 1 + ], + [ + "fracto.near", + 1 + ], + [ + "francisagui.near", + 1 + ], + [ + "francisobasi.near", + 2 + ], + [ + "franco.near", + 2 + ], + [ + "francodiaz777.near", + 2 + ], + [ + "frani.near", + 2 + ], + [ + "frank.near", + 2 + ], + [ + "frankwhite.near", + 1 + ], + [ + "franky.near", + 1 + ], + [ + "franrodriguez.near", + 1 + ], + [ + "franyjosemaria.near", + 1 + ], + [ + "franziskus.near", + 1 + ], + [ + "fred.near", + 2 + ], + [ + "fred2.near", + 2 + ], + [ + "fredtupas.near", + 4 + ], + [ + "free.near", + 3 + ], + [ + "freemangoo.near", + 3 + ], + [ + "freestriker00.near", + 1 + ], + [ + "frog.near", + 1 + ], + [ + "fruitdealer.near", + 1 + ], + [ + "fruitjuice.near", + 1 + ], + [ + "frytos.near", + 1 + ], + [ + "fsv.near", + 1 + ], + [ + "ftor.near", + 2 + ], + [ + "fuck.near", + 1 + ], + [ + "fulushou7111.near", + 1 + ], + [ + "fundamentallabs.near", + 1 + ], + [ + "funktified.near", + 1 + ], + [ + "fusan.near", + 2 + ], + [ + "future.near", + 3 + ], + [ + "futureis.near", + 1 + ], + [ + "g30ff1rl.near", + 1 + ], + [ + "gabriel.near", + 1 + ], + [ + "gabriel85.near", + 2 + ], + [ + "gagdiez.near", + 1 + ], + [ + "galant.near", + 1 + ], + [ + "games.near", + 1 + ], + [ + "gamesdarker.near", + 1 + ], + [ + "gaming.near", + 1 + ], + [ + "gardel.near", + 2 + ], + [ + "gary.near", + 2 + ], + [ + "garyhagu.near", + 1 + ], + [ + "gaston.near", + 1 + ], + [ + "gate.near", + 1 + ], + [ + "gay.near", + 1 + ], + [ + "gazelle.near", + 1 + ], + [ + "gbali.near", + 1 + ], + [ + "ge9123.near", + 4 + ], + [ + "gelfw12.near", + 2 + ], + [ + "gem.near", + 1 + ], + [ + "gemini.near", + 1 + ], + [ + "gen9.near", + 1 + ], + [ + "genericname.near", + 1 + ], + [ + "genesis.near", + 1 + ], + [ + "genie.near", + 3 + ], + [ + "genius.near", + 1 + ], + [ + "geo.near", + 2 + ], + [ + "george.near", + 1 + ], + [ + "geovanus.near", + 1 + ], + [ + "gerard.near", + 1 + ], + [ + "gewgwe.near", + 1 + ], + [ + "gfflo.near", + 2 + ], + [ + "ggal.near", + 2 + ], + [ + "ggg.near", + 2 + ], + [ + "ghedley.near", + 1 + ], + [ + "giallorossi_10.near", + 1 + ], + [ + "giandmn.near", + 3 + ], + [ + "gibby.near", + 1 + ], + [ + "gidroplan.near", + 1 + ], + [ + "ginzy.near", + 1 + ], + [ + "gio3gio.near", + 1 + ], + [ + "girazoki.near", + 3 + ], + [ + "gktnr0418.near", + 1 + ], + [ + "glad.near", + 1 + ], + [ + "gleb.near", + 1 + ], + [ + "glenn.near", + 1 + ], + [ + "global1.near", + 1 + ], + [ + "gmaverickv.near", + 1 + ], + [ + "gmck.near", + 1 + ], + [ + "goatmine.near", + 1 + ], + [ + "gocrypto.near", + 1 + ], + [ + "goctienao.near", + 1 + ], + [ + "godisgreat.near", + 1 + ], + [ + "godwitt28.near", + 1 + ], + [ + "goli.near", + 1 + ], + [ + "gonchiferradas.near", + 1 + ], + [ + "gonefishing.near", + 2 + ], + [ + "goodwin.near", + 1 + ], + [ + "google.near", + 1 + ], + [ + "goraset.near", + 1 + ], + [ + "goraya.near", + 2 + ], + [ + "gorazd.near", + 2 + ], + [ + "gordonleesmith.near", + 1 + ], + [ + "gorochi.near", + 2 + ], + [ + "gps.near", + 1 + ], + [ + "gramatikov.near", + 1 + ], + [ + "gravelpit.near", + 1 + ], + [ + "great.near", + 1 + ], + [ + "greedywhale.near", + 1 + ], + [ + "greg.near", + 1 + ], + [ + "greg99.near", + 1 + ], + [ + "grehei.near", + 1 + ], + [ + "grek.near", + 1 + ], + [ + "grek_vlg.near", + 1 + ], + [ + "greso.near", + 1 + ], + [ + "gresya.near", + 1 + ], + [ + "grifflet.near", + 1 + ], + [ + "grr.near", + 1 + ], + [ + "guantanamoboy.near", + 1 + ], + [ + "gudu169.near", + 1 + ], + [ + "guillesker.near", + 2 + ], + [ + "gun.near", + 2 + ], + [ + "gurugraptor.near", + 1 + ], + [ + "gustav.near", + 1 + ], + [ + "gustavo.near", + 1 + ], + [ + "gwak.near", + 1 + ], + [ + "gwogwosachi.near", + 1 + ], + [ + "gxlitian.near", + 3 + ], + [ + "h1tthat.near", + 1 + ], + [ + "h4xxl.near", + 1 + ], + [ + "hahaha.near", + 1 + ], + [ + "haihv96.near", + 2 + ], + [ + "haiptmkt.near", + 2 + ], + [ + "hakathon13.near", + 1 + ], + [ + "halar_1099_kimchi.near", + 1 + ], + [ + "hamper.near", + 1 + ], + [ + "hamzanet01.near", + 1 + ], + [ + "hannahui.near", + 1 + ], + [ + "hansu4t.near", + 1 + ], + [ + "hansu4t2.near", + 1 + ], + [ + "hansvl.near", + 5 + ], + [ + "hantaejae.near", + 2 + ], + [ + "hao.near", + 1 + ], + [ + "hardy008.near", + 1 + ], + [ + "haroldbaks77.near", + 1 + ], + [ + "harris.near", + 1 + ], + [ + "haru.near", + 1 + ], + [ + "hash.near", + 1 + ], + [ + "hashboy.near", + 1 + ], + [ + "hashpark.near", + 1 + ], + [ + "hatatoshi.near", + 1 + ], + [ + "hatred09.near", + 1 + ], + [ + "hatropine7.near", + 2 + ], + [ + "haustein.near", + 1 + ], + [ + "hauteclairestassin.near", + 1 + ], + [ + "haydon.near", + 1 + ], + [ + "hb33.near", + 2 + ], + [ + "hcsyyk.near", + 2 + ], + [ + "hdt33.near", + 1 + ], + [ + "he.near", + 1 + ], + [ + "he090279kap.near", + 1 + ], + [ + "he4usa.near", + 1 + ], + [ + "health.near", + 2 + ], + [ + "heartchan.near", + 1 + ], + [ + "hector.near", + 1 + ], + [ + "heinrichschw.near", + 1 + ], + [ + "heisenberg39.near", + 1 + ], + [ + "hejunqiu.near", + 1 + ], + [ + "helen.near", + 1 + ], + [ + "hello.near", + 1 + ], + [ + "helloworld.near", + 1 + ], + [ + "help_is.near", + 1 + ], + [ + "hendrikvveen.near", + 2 + ], + [ + "hennyc.near", + 1 + ], + [ + "henrywang7.near", + 22 + ], + [ + "heraclitus.near", + 1 + ], + [ + "herberthenghan.near", + 2 + ], + [ + "heretikkk.near", + 1 + ], + [ + "hermann.near", + 1 + ], + [ + "hewig.near", + 1 + ], + [ + "hhmeen.near", + 2 + ], + [ + "hhomsi.near", + 1 + ], + [ + "hi.near", + 1 + ], + [ + "hideekin.near", + 2 + ], + [ + "hidesun22.near", + 2 + ], + [ + "hiimyims5111.near", + 2 + ], + [ + "hippaz.near", + 2 + ], + [ + "hirajyoti.near", + 2 + ], + [ + "hirama.near", + 1 + ], + [ + "hiro0911.near", + 1 + ], + [ + "hiroki.near", + 2 + ], + [ + "hisham.near", + 2 + ], + [ + "hitthebricks.near", + 1 + ], + [ + "hl.near", + 1 + ], + [ + "hlongalee.near", + 1 + ], + [ + "hnx.near", + 1 + ], + [ + "hobbit.near", + 1 + ], + [ + "hodarrenkm.near", + 2 + ], + [ + "hodl.near", + 2 + ], + [ + "hodlov.near", + 1 + ], + [ + "hodlsworth.near", + 1 + ], + [ + "holleroi.near", + 1 + ], + [ + "home.near", + 1 + ], + [ + "homejumper.near", + 2 + ], + [ + "hongchang.near", + 1 + ], + [ + "hor.near", + 4 + ], + [ + "hotarrowin.near", + 1 + ], + [ + "hrasta.near", + 1 + ], + [ + "hrock.near", + 4 + ], + [ + "hryshko_denys.near", + 3 + ], + [ + "hua.near", + 1 + ], + [ + "huajuan.near", + 1 + ], + [ + "huckour.near", + 2 + ], + [ + "huehner.near", + 1 + ], + [ + "huenkaileung.near", + 1 + ], + [ + "hughhill.near", + 1 + ], + [ + "hugomartinezrosa.near", + 1 + ], + [ + "hulahuub.near", + 2 + ], + [ + "hulk.near", + 1 + ], + [ + "huluwa.near", + 2 + ], + [ + "humaid475.near", + 2 + ], + [ + "human.near", + 2 + ], + [ + "hungaus811.near", + 1 + ], + [ + "hunnxolo.near", + 1 + ], + [ + "huuhoang4984.near", + 3 + ], + [ + "hyper_doge.near", + 1 + ], + [ + "hyugbin.near", + 1 + ], + [ + "ia.near", + 2 + ], + [ + "ia2jbk3fix7ttxiungh2het2mmxlmkj8.near", + 2 + ], + [ + "iadibr.near", + 6 + ], + [ + "iaecoin.near", + 1 + ], + [ + "ialtaf-choudhry.near", + 1 + ], + [ + "iamluieey.near", + 2 + ], + [ + "ian.near", + 2 + ], + [ + "ianabell.near", + 2 + ], + [ + "ianbonavente.near", + 2 + ], + [ + "iandillon.near", + 1 + ], + [ + "iasonas2019.near", + 1 + ], + [ + "ibardak.near", + 1 + ], + [ + "ibingbing.near", + 1 + ], + [ + "ibm.near", + 1 + ], + [ + "iborion.near", + 1 + ], + [ + "ican3701.near", + 4 + ], + [ + "ichongge.near", + 1 + ], + [ + "icodrops.near", + 1 + ], + [ + "icohiinvestbot.near", + 2 + ], + [ + "icon470.near", + 1 + ], + [ + "icovassabi.near", + 2 + ], + [ + "idnl.near", + 1 + ], + [ + "ig27.near", + 1 + ], + [ + "ig6duv66.near", + 1 + ], + [ + "ignacio1.near", + 2 + ], + [ + "ignacio2.near", + 2 + ], + [ + "igor.near", + 1 + ], + [ + "igor76.near", + 1 + ], + [ + "igorbobritckii.near", + 1 + ], + [ + "igorbunkov.near", + 1 + ], + [ + "igoreshka888.near", + 1 + ], + [ + "ihorhorb.near", + 2 + ], + [ + "iissii97.near", + 1 + ], + [ + "ijulia.near", + 1 + ], + [ + "ikbremen-1073.near", + 2 + ], + [ + "ikqsang.near", + 1 + ], + [ + "ilanedils.near", + 1 + ], + [ + "ilde.near", + 1 + ], + [ + "iliabitfinex.near", + 1 + ], + [ + "ilina.near", + 1 + ], + [ + "illia_k.near", + 1 + ], + [ + "illuminati.near", + 1 + ], + [ + "ilya.near", + 2 + ], + [ + "ilya123.near", + 2 + ], + [ + "ilya4756.near", + 2 + ], + [ + "imanel.near", + 2 + ], + [ + "imker.near", + 1 + ], + [ + "immoait.near", + 2 + ], + [ + "impe83.near", + 1 + ], + [ + "imrano.near", + 1 + ], + [ + "imyourfather.near", + 2 + ], + [ + "india.near", + 1 + ], + [ + "inferno.near", + 1 + ], + [ + "ini333.near", + 2 + ], + [ + "inna.near", + 1 + ], + [ + "inotel.near", + 1 + ], + [ + "insidenima.near", + 1 + ], + [ + "insider.near", + 1 + ], + [ + "inst.near", + 1 + ], + [ + "introbits.near", + 2 + ], + [ + "invest.near", + 1 + ], + [ + "invest115.near", + 1 + ], + [ + "investiq.near", + 1 + ], + [ + "invictus.near", + 1 + ], + [ + "ipanda.near", + 1 + ], + [ + "iren20113.near", + 1 + ], + [ + "irishstevemc.near", + 1 + ], + [ + "irobot.near", + 1 + ], + [ + "ironbear.near", + 1 + ], + [ + "is.near", + 3 + ], + [ + "iskerl.near", + 1 + ], + [ + "itachi7368.near", + 2 + ], + [ + "iterrible.near", + 1 + ], + [ + "ituniversum.near", + 1 + ], + [ + "ivan.near", + 1 + ], + [ + "ivanfurta.near", + 1 + ], + [ + "ivanl.near", + 1 + ], + [ + "ivanortiz96.near", + 2 + ], + [ + "ivanytt.near", + 2 + ], + [ + "ivbeb777.near", + 1 + ], + [ + "ivs19.near", + 1 + ], + [ + "iwan.near", + 1 + ], + [ + "ix3m.near", + 1 + ], + [ + "izixhidi.near", + 1 + ], + [ + "izztalbraqi.near", + 3 + ], + [ + "j.near", + 4 + ], + [ + "j03rg1.near", + 1 + ], + [ + "jack933.near", + 2 + ], + [ + "jackie.near", + 1 + ], + [ + "jacklin.near", + 1 + ], + [ + "jackma.near", + 1 + ], + [ + "jackrose0503.near", + 1 + ], + [ + "jackyu.near", + 2 + ], + [ + "jacobcheng.near", + 1 + ], + [ + "jaegu01.near", + 2 + ], + [ + "jaga.near", + 1 + ], + [ + "jaggz1066.near", + 1 + ], + [ + "jahandadkhan.near", + 1 + ], + [ + "jaihanuman.near", + 1 + ], + [ + "jake.near", + 1 + ], + [ + "jakes2020.near", + 2 + ], + [ + "jamadi5039.near", + 1 + ], + [ + "jamkira.near", + 1 + ], + [ + "jan.near", + 1 + ], + [ + "janoltvoort.near", + 1 + ], + [ + "jantapw.near", + 1 + ], + [ + "japan.near", + 1 + ], + [ + "jarekpiotrowski.near", + 1 + ], + [ + "jash.near", + 2 + ], + [ + "jason.near", + 1 + ], + [ + "jason300000.near", + 1 + ], + [ + "javabdalex.near", + 1 + ], + [ + "javiveloso1.near", + 2 + ], + [ + "jay.near", + 1 + ], + [ + "jaycrypto.near", + 3 + ], + [ + "jayjay888.near", + 1 + ], + [ + "jayyoo.near", + 2 + ], + [ + "jazael90.near", + 2 + ], + [ + "jazza09.near", + 1 + ], + [ + "jc.near", + 1 + ], + [ + "jcj.near", + 2 + ], + [ + "jcmi.near", + 1 + ], + [ + "jeanmart.near", + 1 + ], + [ + "jeedocrypto.near", + 2 + ], + [ + "jeedocrypto2.near", + 2 + ], + [ + "jeez07z.near", + 1 + ], + [ + "jeff0052.near", + 1 + ], + [ + "jegib.near", + 1 + ], + [ + "jehanchu.near", + 1 + ], + [ + "jele7ka.near", + 1 + ], + [ + "jelvin8.near", + 1 + ], + [ + "jen.near", + 7 + ], + [ + "jepped.near", + 1 + ], + [ + "jerrymoon.near", + 1 + ], + [ + "jerrys.near", + 1 + ], + [ + "jeschis.near", + 1 + ], + [ + "jessica.near", + 1 + ], + [ + "jestin.near", + 1 + ], + [ + "jgsp.near", + 1 + ], + [ + "jhuebner1.near", + 1 + ], + [ + "jiang.near", + 1 + ], + [ + "jiangbingquan.near", + 1 + ], + [ + "jianshu.near", + 1 + ], + [ + "jiayaoqi.near", + 1 + ], + [ + "jik_k.near", + 1 + ], + [ + "jikthapat.near", + 1 + ], + [ + "jim.near", + 1 + ], + [ + "jimbo.near", + 1 + ], + [ + "jimmyrhino20.near", + 1 + ], + [ + "jimvdberg24.near", + 3 + ], + [ + "jimy1111.near", + 1 + ], + [ + "jin.near", + 2 + ], + [ + "jinshawn.near", + 1 + ], + [ + "jinxuan.near", + 1 + ], + [ + "jiru.near", + 4 + ], + [ + "jisol8122.near", + 1 + ], + [ + "jjp.near", + 1 + ], + [ + "jk1151.near", + 1 + ], + [ + "jkimmich.near", + 1 + ], + [ + "jkprag78.near", + 2 + ], + [ + "jleang.near", + 2 + ], + [ + "jmdoyle.near", + 1 + ], + [ + "jmunb.near", + 2 + ], + [ + "jnear.near", + 1 + ], + [ + "jocellin.near", + 2 + ], + [ + "joel.near", + 1 + ], + [ + "joeljonas9597.near", + 2 + ], + [ + "joelwee.near", + 2 + ], + [ + "joelyd19.near", + 1 + ], + [ + "joemoms.near", + 1 + ], + [ + "johannesst.near", + 2 + ], + [ + "john.near", + 1 + ], + [ + "johncalvin46.near", + 1 + ], + [ + "johnh.near", + 1 + ], + [ + "johnhoffmann.near", + 2 + ], + [ + "johnny.near", + 2 + ], + [ + "johnryan.near", + 1 + ], + [ + "johnt80.near", + 2 + ], + [ + "jojorge26.near", + 2 + ], + [ + "jollymetalbastard.near", + 2 + ], + [ + "jollymetaltrader.near", + 1 + ], + [ + "jonathan.near", + 1 + ], + [ + "jonathan6620.near", + 2 + ], + [ + "jongkwang.near", + 1 + ], + [ + "jonju.near", + 2 + ], + [ + "joonian.near", + 2 + ], + [ + "jop.near", + 1 + ], + [ + "jordaai.near", + 2 + ], + [ + "jordyfiene.near", + 2 + ], + [ + "jorgellfagundes.near", + 1 + ], + [ + "joryivanoff.near", + 1 + ], + [ + "josebawakeboarder.near", + 8 + ], + [ + "josef.near", + 1 + ], + [ + "josemanuel2.near", + 1 + ], + [ + "josh.near", + 2 + ], + [ + "josh5.near", + 1 + ], + [ + "josh6.near", + 2 + ], + [ + "joshua.near", + 2 + ], + [ + "jpbytez.near", + 1 + ], + [ + "js974lin.near", + 1 + ], + [ + "jsc2.near", + 1 + ], + [ + "jsc3.near", + 1 + ], + [ + "jsc4.near", + 2 + ], + [ + "jsc5.near", + 1 + ], + [ + "jsc6.near", + 1 + ], + [ + "jsc7.near", + 1 + ], + [ + "jsc8.near", + 3 + ], + [ + "jt77.near", + 1 + ], + [ + "jubi.near", + 1 + ], + [ + "juergen311.near", + 3 + ], + [ + "julia.near", + 1 + ], + [ + "jumidamo.near", + 1 + ], + [ + "jun.near", + 3 + ], + [ + "juncuirina.near", + 2 + ], + [ + "junhyub11.near", + 1 + ], + [ + "jurij.near", + 2 + ], + [ + "jurquidiz.near", + 1 + ], + [ + "justcrucial.near", + 1 + ], + [ + "justin.near", + 2 + ], + [ + "jwallet1.near", + 1 + ], + [ + "jwbcdz.near", + 2 + ], + [ + "jy.near", + 2 + ], + [ + "k.near", + 1 + ], + [ + "k111sg.near", + 1 + ], + [ + "k5zr8d.near", + 1 + ], + [ + "kaan.near", + 2 + ], + [ + "kai.near", + 1 + ], + [ + "kairypto.near", + 1 + ], + [ + "kaiserreich.near", + 1 + ], + [ + "kaiyuan.near", + 1 + ], + [ + "kake.near", + 1 + ], + [ + "kalimbamba.near", + 1 + ], + [ + "kalockwo.near", + 1 + ], + [ + "kalom.near", + 2 + ], + [ + "kalyani.near", + 1 + ], + [ + "kamil7890.near", + 3 + ], + [ + "kammon.near", + 1 + ], + [ + "kamui02.near", + 2 + ], + [ + "kaneluo.near", + 1 + ], + [ + "kanemori.near", + 1 + ], + [ + "kanskey.near", + 2 + ], + [ + "kao.near", + 2 + ], + [ + "kar.near", + 1 + ], + [ + "karimfa.near", + 1 + ], + [ + "karimhassaneid.near", + 2 + ], + [ + "karl.near", + 1 + ], + [ + "karthik.near", + 1 + ], + [ + "karthvader.near", + 1 + ], + [ + "katman.near", + 1 + ], + [ + "kato.near", + 2 + ], + [ + "katrin.near", + 1 + ], + [ + "kazadar.near", + 1 + ], + [ + "kazantip.near", + 2 + ], + [ + "kazbek.near", + 1 + ], + [ + "kb1224.near", + 1 + ], + [ + "kcupac.near", + 2 + ], + [ + "kdimarik89.near", + 1 + ], + [ + "keatsol.near", + 1 + ], + [ + "keith.near", + 1 + ], + [ + "kek.near", + 1 + ], + [ + "kellybelly.near", + 1 + ], + [ + "kelvin.near", + 1 + ], + [ + "kelvinngzh.near", + 1 + ], + [ + "kemo.near", + 2 + ], + [ + "kemola.near", + 1 + ], + [ + "kemosabe.near", + 1 + ], + [ + "kendall.near", + 1 + ], + [ + "kenichi.near", + 1 + ], + [ + "kenny2020.near", + 1 + ], + [ + "kennzonki.near", + 1 + ], + [ + "kentoshi.near", + 1 + ], + [ + "kenttran.near", + 1 + ], + [ + "kevinburkhard.near", + 1 + ], + [ + "khaikhai1960.near", + 1 + ], + [ + "khalisy.near", + 2 + ], + [ + "khidir.near", + 1 + ], + [ + "khn.near", + 1 + ], + [ + "khorolets.near", + 1 + ], + [ + "khoruzhyi.near", + 1 + ], + [ + "khunchan.near", + 1 + ], + [ + "kiberg.near", + 1 + ], + [ + "kickbuttowski.near", + 2 + ], + [ + "kiddunstopped.near", + 1 + ], + [ + "killua.near", + 1 + ], + [ + "kimon.near", + 1 + ], + [ + "kinggogh.near", + 3 + ], + [ + "kingstatoshi.near", + 2 + ], + [ + "kiran.near", + 2 + ], + [ + "kircovich.near", + 1 + ], + [ + "kircovich2.near", + 2 + ], + [ + "kirill_ri.near", + 2 + ], + [ + "kishan748.near", + 2 + ], + [ + "kitty.near", + 1 + ], + [ + "kivo2004.near", + 2 + ], + [ + "kiwi092020.near", + 1 + ], + [ + "kjellnearwallet.near", + 1 + ], + [ + "kkund.near", + 1 + ], + [ + "klaustin.near", + 1 + ], + [ + "knyazev7aa.near", + 1 + ], + [ + "kobarisun.near", + 1 + ], + [ + "koenig.near", + 1 + ], + [ + "kokkieroy.near", + 1 + ], + [ + "koledg129g.near", + 2 + ], + [ + "komido815.near", + 1 + ], + [ + "kon.near", + 2 + ], + [ + "kondor.near", + 2 + ], + [ + "konsulat.near", + 2 + ], + [ + "koolkamzzy.near", + 1 + ], + [ + "korkius.near", + 1 + ], + [ + "koszula.near", + 1 + ], + [ + "kps289.near", + 1 + ], + [ + "kramerrold.near", + 1 + ], + [ + "kraze.near", + 1 + ], + [ + "krendelyok.near", + 1 + ], + [ + "kriptofinance.near", + 1 + ], + [ + "kris25.near", + 1 + ], + [ + "kriso444.near", + 2 + ], + [ + "krisszet.near", + 1 + ], + [ + "kristaps.near", + 1 + ], + [ + "kristapsd.near", + 1 + ], + [ + "kritsdapat.near", + 1 + ], + [ + "krobi.near", + 2 + ], + [ + "krol.near", + 2 + ], + [ + "kruggok.near", + 1 + ], + [ + "krypto_jan.near", + 1 + ], + [ + "krypto_jan2.near", + 1 + ], + [ + "ks.near", + 2 + ], + [ + "ksg79.near", + 1 + ], + [ + "kucoin.near", + 1 + ], + [ + "kuka.near", + 1 + ], + [ + "kukulabanze.near", + 1 + ], + [ + "kumosoukai.near", + 1 + ], + [ + "kuroko1949.near", + 1 + ], + [ + "kusola.near", + 1 + ], + [ + "kusols.near", + 1 + ], + [ + "kybik.near", + 1 + ], + [ + "kytzu.near", + 1 + ], + [ + "l3po.near", + 2 + ], + [ + "l7p4qbzj3z.near", + 1 + ], + [ + "lakond.near", + 1 + ], + [ + "lalo2.near", + 1 + ], + [ + "lamandderi.near", + 1 + ], + [ + "lamer.near", + 2 + ], + [ + "lanax.near", + 2 + ], + [ + "lance009.near", + 1 + ], + [ + "lancelot.near", + 2 + ], + [ + "land.near", + 1 + ], + [ + "landa1993.near", + 2 + ], + [ + "lara.near", + 1 + ], + [ + "larch.near", + 1 + ], + [ + "late.near", + 1 + ], + [ + "lauranuta.near", + 1 + ], + [ + "laurent.near", + 1 + ], + [ + "laurentmaillard.near", + 1 + ], + [ + "law.near", + 2 + ], + [ + "lazyhjq.near", + 2 + ], + [ + "lazza_l0ma.near", + 1 + ], + [ + "lbilskicrypto.near", + 1 + ], + [ + "ldn.near", + 1 + ], + [ + "ldn91ptit.near", + 4 + ], + [ + "ledat1612.near", + 1 + ], + [ + "ledger.near", + 1 + ], + [ + "legiojuve.near", + 1 + ], + [ + "lehadobriy.near", + 1 + ], + [ + "lehung.near", + 3 + ], + [ + "lei.near", + 2 + ], + [ + "leifythebeastie.near", + 1 + ], + [ + "lelouch888.near", + 1 + ], + [ + "lenall51.near", + 1 + ], + [ + "lenara.near", + 1 + ], + [ + "lennieyoo.near", + 2 + ], + [ + "lenny00001.near", + 3 + ], + [ + "lentv.near", + 2 + ], + [ + "leo.near", + 1 + ], + [ + "leong.near", + 1 + ], + [ + "leonidtmn.near", + 1 + ], + [ + "leonie.near", + 8 + ], + [ + "leonlauaus.near", + 1 + ], + [ + "leoyishaun.near", + 1 + ], + [ + "lesterrawks123.near", + 1 + ], + [ + "letitia.near", + 1 + ], + [ + "levanquan297.near", + 2 + ], + [ + "levin.near", + 1 + ], + [ + "lewinsuker.near", + 1 + ], + [ + "lewismelland.near", + 2 + ], + [ + "lewyatan.near", + 1 + ], + [ + "lex.near", + 1 + ], + [ + "lex20150720.near", + 2 + ], + [ + "lexlex.near", + 1 + ], + [ + "li.near", + 2 + ], + [ + "libby08.near", + 1 + ], + [ + "life.near", + 1 + ], + [ + "lightzealot.near", + 1 + ], + [ + "likewhite.near", + 2 + ], + [ + "liliherciu.near", + 3 + ], + [ + "lilin_888.near", + 1 + ], + [ + "lin.near", + 1 + ], + [ + "line.near", + 2 + ], + [ + "lingxxx.near", + 1 + ], + [ + "linhphieu.near", + 1 + ], + [ + "linkdrop.near", + 1 + ], + [ + "linusx58.near", + 1 + ], + [ + "lissargentina.near", + 1 + ], + [ + "lito.near", + 1 + ], + [ + "liu120626.near", + 1 + ], + [ + "liu890610.near", + 1 + ], + [ + "liver.near", + 1 + ], + [ + "lixoy.near", + 1 + ], + [ + "lj.near", + 1 + ], + [ + "lkk.near", + 1 + ], + [ + "lntranet.near", + 1 + ], + [ + "lobster_xie.near", + 1 + ], + [ + "locked.near", + 1 + ], + [ + "lockup.near", + 6 + ], + [ + "logic.near", + 2 + ], + [ + "loni.near", + 1 + ], + [ + "loomist.near", + 1 + ], + [ + "loperk.near", + 3 + ], + [ + "lordshillings.near", + 1 + ], + [ + "loris.near", + 1 + ], + [ + "lou135222.near", + 1 + ], + [ + "louanojax.near", + 1 + ], + [ + "louisnguyen.near", + 2 + ], + [ + "love.near", + 1 + ], + [ + "lovemansh84.near", + 1 + ], + [ + "lp.near", + 2 + ], + [ + "lpthanh.near", + 1 + ], + [ + "lrubiorod.near", + 1 + ], + [ + "lt1412.near", + 1 + ], + [ + "lu666.near", + 1 + ], + [ + "luana.near", + 1 + ], + [ + "luca.near", + 1 + ], + [ + "lucas.near", + 2 + ], + [ + "lucho1963.near", + 1 + ], + [ + "lucjah.near", + 1 + ], + [ + "luck.near", + 1 + ], + [ + "luedu2020.near", + 2 + ], + [ + "luffy.near", + 1 + ], + [ + "luhar.near", + 1 + ], + [ + "luisaoxd20.near", + 2 + ], + [ + "luisvillarreal.near", + 1 + ], + [ + "lukasso.near", + 1 + ], + [ + "lukasso2.near", + 1 + ], + [ + "lukecaan.near", + 1 + ], + [ + "lumpi-1714wpx.near", + 1 + ], + [ + "luna.near", + 1 + ], + [ + "luongbdhust.near", + 11 + ], + [ + "luuvu.near", + 1 + ], + [ + "lxbach.near", + 2 + ], + [ + "lyonkwek.near", + 1 + ], + [ + "lyybtc.near", + 2 + ], + [ + "m.near", + 2 + ], + [ + "m17.near", + 2 + ], + [ + "m2dream.near", + 1 + ], + [ + "m3.near", + 1 + ], + [ + "ma.near", + 1 + ], + [ + "maagueror.near", + 2 + ], + [ + "maanimo.near", + 2 + ], + [ + "macanga.near", + 2 + ], + [ + "machina.near", + 1 + ], + [ + "machoman.near", + 1 + ], + [ + "mackalicious.near", + 2 + ], + [ + "madbustazz.near", + 1 + ], + [ + "madcapslaugh.near", + 2 + ], + [ + "madgasm.near", + 1 + ], + [ + "madison.near", + 2 + ], + [ + "madvillain.near", + 1 + ], + [ + "madxista.near", + 1 + ], + [ + "magadan.near", + 1 + ], + [ + "maghrebcrypto.near", + 2 + ], + [ + "magic1go.near", + 1 + ], + [ + "magmza.near", + 2 + ], + [ + "magnetic.near", + 1 + ], + [ + "maher.near", + 1 + ], + [ + "mahmuttahaolcucu.near", + 2 + ], + [ + "maibei.near", + 2 + ], + [ + "majal.near", + 2 + ], + [ + "majest3.near", + 2 + ], + [ + "major.near", + 1 + ], + [ + "majosk.near", + 1 + ], + [ + "make023.near", + 1 + ], + [ + "makhova.near", + 1 + ], + [ + "makindea.near", + 1 + ], + [ + "malik.near", + 1 + ], + [ + "malykhin.near", + 2 + ], + [ + "man.near", + 1 + ], + [ + "manatwork.near", + 2 + ], + [ + "manfred90.near", + 2 + ], + [ + "manginobi.near", + 2 + ], + [ + "maninder.near", + 2 + ], + [ + "manish.near", + 1 + ], + [ + "manju.near", + 1 + ], + [ + "manos.near", + 1 + ], + [ + "manovitskiy-andrey.near", + 1 + ], + [ + "mansley.near", + 7 + ], + [ + "mantradao.near", + 1 + ], + [ + "maotai.near", + 1 + ], + [ + "maratxo.near", + 2 + ], + [ + "marcel1111.near", + 3 + ], + [ + "marcelo.near", + 1 + ], + [ + "margask.near", + 1 + ], + [ + "marianmelinte.near", + 1 + ], + [ + "mariano11.near", + 1 + ], + [ + "mariia33.near", + 2 + ], + [ + "mario.near", + 2 + ], + [ + "mario66.near", + 1 + ], + [ + "marionclaire.near", + 1 + ], + [ + "maritn.near", + 1 + ], + [ + "mark.near", + 1 + ], + [ + "markgenuine.near", + 2 + ], + [ + "marko.near", + 1 + ], + [ + "markwenink.near", + 1 + ], + [ + "markyllanos.near", + 13 + ], + [ + "marley.near", + 1 + ], + [ + "marrescia.near", + 2 + ], + [ + "mars.near", + 1 + ], + [ + "marshx136.near", + 1 + ], + [ + "martian.near", + 1 + ], + [ + "martiescribano.near", + 1 + ], + [ + "martin.near", + 2 + ], + [ + "martin_burgherr.near", + 1 + ], + [ + "martinescribano.near", + 1 + ], + [ + "maruman.near", + 1 + ], + [ + "matejtomazin.near", + 1 + ], + [ + "math.near", + 1 + ], + [ + "mathewvarghese2001.near", + 2 + ], + [ + "mathiaskuehl.near", + 1 + ], + [ + "mathtest.near", + 1 + ], + [ + "mathwallet.near", + 1 + ], + [ + "mathwallet1.near", + 1 + ], + [ + "matrainier.near", + 1 + ], + [ + "mats478.near", + 2 + ], + [ + "matsumoto.near", + 1 + ], + [ + "matt1.near", + 6 + ], + [ + "matt7.near", + 3 + ], + [ + "mattjbailey.near", + 1 + ], + [ + "mattmcl.near", + 1 + ], + [ + "mattmcl1.near", + 1 + ], + [ + "mattrobo3.near", + 1 + ], + [ + "matveynearwallet11.near", + 1 + ], + [ + "matyan5921.near", + 1 + ], + [ + "mav_rick.near", + 1 + ], + [ + "max2017.near", + 1 + ], + [ + "max250777.near", + 1 + ], + [ + "maxfil.near", + 1 + ], + [ + "maxgwei.near", + 2 + ], + [ + "maxib0r.near", + 2 + ], + [ + "maxim.near", + 3 + ], + [ + "maximise.near", + 1 + ], + [ + "maximkhoroshev.near", + 1 + ], + [ + "maximoff.near", + 1 + ], + [ + "maxjirawat.near", + 2 + ], + [ + "maxlev.near", + 9 + ], + [ + "maxyok.near", + 1 + ], + [ + "mazzystar.near", + 3 + ], + [ + "mbarbosa.near", + 2 + ], + [ + "mbtrilla.near", + 1 + ], + [ + "mcb.near", + 2 + ], + [ + "mcb2001.near", + 4 + ], + [ + "mcdcrypt.near", + 1 + ], + [ + "mchristiansen.near", + 1 + ], + [ + "mdzor.near", + 2 + ], + [ + "meadowlane1862.near", + 1 + ], + [ + "meatjuice.near", + 1 + ], + [ + "mecogrouppsy.near", + 1 + ], + [ + "medlucky.near", + 1 + ], + [ + "megacel.near", + 1 + ], + [ + "megachan.near", + 2 + ], + [ + "megagangsta.near", + 1 + ], + [ + "megapapka.near", + 1 + ], + [ + "mehdi.near", + 2 + ], + [ + "meike-9.near", + 1 + ], + [ + "meilin.near", + 1 + ], + [ + "mel_smog.near", + 2 + ], + [ + "meli13.near", + 1 + ], + [ + "mellandlewis.near", + 1 + ], + [ + "melu.near", + 1 + ], + [ + "melvin.near", + 2 + ], + [ + "meomeo.near", + 1 + ], + [ + "mercadobursatil2020.near", + 1 + ], + [ + "merkletree.near", + 1 + ], + [ + "messidou.near", + 2 + ], + [ + "metoo.near", + 1 + ], + [ + "mexite.near", + 1 + ], + [ + "mezoantropo.near", + 1 + ], + [ + "mf.near", + 1 + ], + [ + "mgazvoda.near", + 1 + ], + [ + "mgs.near", + 2 + ], + [ + "mh.near", + 2 + ], + [ + "mhmtozback.near", + 2 + ], + [ + "miaodacuo.near", + 2 + ], + [ + "miaxxx.near", + 1 + ], + [ + "micabytes.near", + 3 + ], + [ + "micdrabble.near", + 2 + ], + [ + "michael.near", + 2 + ], + [ + "michael0503.near", + 2 + ], + [ + "michaelcdeguzman.near", + 1 + ], + [ + "michaeldeguzman.near", + 1 + ], + [ + "michel.near", + 1 + ], + [ + "michen.near", + 1 + ], + [ + "michess.near", + 1 + ], + [ + "midnighter.near", + 2 + ], + [ + "mieszko.near", + 1 + ], + [ + "mighty44.near", + 2 + ], + [ + "migi.near", + 1 + ], + [ + "miguelangelo.near", + 1 + ], + [ + "mihai.near", + 2 + ], + [ + "mijnders.near", + 1 + ], + [ + "mike06.near", + 1 + ], + [ + "mike520.near", + 1 + ], + [ + "mikefluff.near", + 1 + ], + [ + "mikhail.near", + 1 + ], + [ + "mikhalytch.near", + 2 + ], + [ + "mikvol.near", + 1 + ], + [ + "mila.near", + 1 + ], + [ + "mila1.near", + 1 + ], + [ + "milen.near", + 4 + ], + [ + "milica.near", + 1 + ], + [ + "millioner2021-891301.near", + 1 + ], + [ + "millsy.near", + 1 + ], + [ + "mim.near", + 1 + ], + [ + "min.near", + 1 + ], + [ + "minhloc.near", + 1 + ], + [ + "minhtbk.near", + 2 + ], + [ + "minilim.near", + 2 + ], + [ + "miquel.near", + 1 + ], + [ + "miriam.near", + 2 + ], + [ + "mirki.near", + 2 + ], + [ + "miron.near", + 1 + ], + [ + "misch.near", + 1 + ], + [ + "misha.near", + 1 + ], + [ + "missdefi.near", + 2 + ], + [ + "missy-missy.near", + 1 + ], + [ + "mister95.near", + 1 + ], + [ + "mistervp.near", + 1 + ], + [ + "misu.near", + 1 + ], + [ + "mix.near", + 1 + ], + [ + "mjaeckel.near", + 2 + ], + [ + "mjoaquim.near", + 1 + ], + [ + "mlknbr.near", + 2 + ], + [ + "mlr.near", + 1 + ], + [ + "mm.near", + 1 + ], + [ + "mmasal.near", + 2 + ], + [ + "mmasal74.near", + 2 + ], + [ + "mmayer.near", + 1 + ], + [ + "mmladin.near", + 1 + ], + [ + "mmm.near", + 1 + ], + [ + "mmmax5075.near", + 1 + ], + [ + "mnemonik.near", + 1 + ], + [ + "mnnblck.near", + 1 + ], + [ + "mo.near", + 1 + ], + [ + "moi.near", + 2 + ], + [ + "moku.near", + 1 + ], + [ + "momomo.near", + 1 + ], + [ + "monce.near", + 1 + ], + [ + "money.near", + 1 + ], + [ + "moo9000.near", + 2 + ], + [ + "moogz.near", + 1 + ], + [ + "moonboi9001.near", + 1 + ], + [ + "moonhappyblock.near", + 1 + ], + [ + "moonhunter.near", + 1 + ], + [ + "moonshot.near", + 2 + ], + [ + "moontrader.near", + 7 + ], + [ + "mort.near", + 2 + ], + [ + "morten76.near", + 2 + ], + [ + "motao.near", + 1 + ], + [ + "motti.near", + 1 + ], + [ + "mouthface.near", + 1 + ], + [ + "moyatronik1.near", + 1 + ], + [ + "mpescoin94.near", + 1 + ], + [ + "mqamar1994.near", + 1 + ], + [ + "mrak.near", + 1 + ], + [ + "mrbin228.near", + 1 + ], + [ + "mrcrypt.near", + 1 + ], + [ + "mrfinely.near", + 2 + ], + [ + "mrkev.near", + 1 + ], + [ + "mrmirliflore.near", + 1 + ], + [ + "mrreddington2015.near", + 2 + ], + [ + "mrskill.near", + 1 + ], + [ + "mrt.near", + 2 + ], + [ + "mrtn.near", + 1 + ], + [ + "mrtung101291.near", + 1 + ], + [ + "mrwong.near", + 1 + ], + [ + "mrwrong.near", + 1 + ], + [ + "ms31.near", + 1 + ], + [ + "msflorentine.near", + 2 + ], + [ + "mtelegin.near", + 2 + ], + [ + "mueller.near", + 2 + ], + [ + "muffy.near", + 2 + ], + [ + "mula.near", + 2 + ], + [ + "mularkey.near", + 1 + ], + [ + "multiwhs.near", + 2 + ], + [ + "mummyhap.near", + 1 + ], + [ + "mumu.near", + 1 + ], + [ + "mumunana.near", + 1 + ], + [ + "munaf.near", + 1 + ], + [ + "mundocrypto.near", + 1 + ], + [ + "muralikrishnan.near", + 1 + ], + [ + "murderbear.near", + 1 + ], + [ + "musakatae.near", + 1 + ], + [ + "mushhz.near", + 1 + ], + [ + "musk.near", + 1 + ], + [ + "muzammil.near", + 1 + ], + [ + "mw.near", + 1 + ], + [ + "mw3ka2tcj_6pd5f7qoesz.near", + 2 + ], + [ + "mxc.near", + 6 + ], + [ + "my_wallet.near", + 2 + ], + [ + "myaccount.near", + 1 + ], + [ + "mykha2.near", + 2 + ], + [ + "mymali.near", + 1 + ], + [ + "mymind.near", + 1 + ], + [ + "mynear.near", + 1 + ], + [ + "mzidov.near", + 3 + ], + [ + "n3m0.near", + 2 + ], + [ + "n3r0n.near", + 1 + ], + [ + "n44bsa.near", + 1 + ], + [ + "n4o4oakos.near", + 4 + ], + [ + "na5.near", + 1 + ], + [ + "nabeel.near", + 1 + ], + [ + "nabetakato.near", + 1 + ], + [ + "nabulmar.near", + 1 + ], + [ + "nadeemkhan.near", + 1 + ], + [ + "nadezdapw.near", + 1 + ], + [ + "naeun.near", + 2 + ], + [ + "nafooch.near", + 1 + ], + [ + "naka.near", + 1 + ], + [ + "nakamoto.near", + 1 + ], + [ + "nallow.near", + 1 + ], + [ + "namantra.near", + 1 + ], + [ + "namarseklasno.near", + 2 + ], + [ + "naozhong.near", + 4 + ], + [ + "narasimha.near", + 1 + ], + [ + "narniec.near", + 1 + ], + [ + "naruemon.near", + 1 + ], + [ + "narutra2.near", + 1 + ], + [ + "nas.near", + 1 + ], + [ + "nata.near", + 1 + ], + [ + "natalia.near", + 1 + ], + [ + "naturalwarp.near", + 2 + ], + [ + "natyendo.near", + 2 + ], + [ + "navarich.near", + 1 + ], + [ + "nb.near", + 2 + ], + [ + "nbawyl.near", + 2 + ], + [ + "near", + 4196 + ], + [ + "near.near", + 1 + ], + [ + "near001.near", + 1 + ], + [ + "near12.near", + 2 + ], + [ + "near123.near", + 3 + ], + [ + "near24.near", + 2 + ], + [ + "near29holger.near", + 1 + ], + [ + "near34holger.near", + 1 + ], + [ + "unc_stepec_ico.near", + 2 + ], + [ + "nearest.near", + 2 + ], + [ + "nearfans.near", + 1 + ], + [ + "nearjapan.near", + 1 + ], + [ + "nearjt.near", + 1 + ], + [ + "nearlover.near", + 1 + ], + [ + "nearman.near", + 2 + ], + [ + "nearman99.near", + 1 + ], + [ + "nearmoon.near", + 2 + ], + [ + "nearorfar.near", + 1 + ], + [ + "nearpig.near", + 2 + ], + [ + "nearwallet.near", + 2 + ], + [ + "necron.near", + 1 + ], + [ + "nedski.near", + 1 + ], + [ + "neerajpoddar.near", + 3 + ], + [ + "negat1ve.near", + 1 + ], + [ + "nene.near", + 1 + ], + [ + "neonoinm14.near", + 2 + ], + [ + "neoxxx.near", + 1 + ], + [ + "nepal.near", + 2 + ], + [ + "nepta.near", + 5 + ], + [ + "nepytaev.near", + 4 + ], + [ + "nero.near", + 1 + ], + [ + "nesh34.near", + 1 + ], + [ + "netfatik.near", + 1 + ], + [ + "netzzimedia.near", + 1 + ], + [ + "nevermind.near", + 2 + ], + [ + "newbietomarket.near", + 2 + ], + [ + "newera.near", + 1 + ], + [ + "newinternet.near", + 1 + ], + [ + "newmancoin.near", + 1 + ], + [ + "newtestone.near", + 1 + ], + [ + "nexus.near", + 1 + ], + [ + "nexus2.near", + 1 + ], + [ + "ngocgiauktb.near", + 1 + ], + [ + "ngoctuyenpro88.near", + 3 + ], + [ + "ngoquanghieu01.near", + 1 + ], + [ + "ngoquanghieu02.near", + 1 + ], + [ + "nguyencongsau.near", + 4 + ], + [ + "nguyenhuuthang.near", + 2 + ], + [ + "nguyenkhang.near", + 2 + ], + [ + "nguyenmanhha.near", + 1 + ], + [ + "nguyenquockhank123321.near", + 1 + ], + [ + "nhanha1960.near", + 1 + ], + [ + "nhatnm.near", + 1 + ], + [ + "nhmh1011.near", + 3 + ], + [ + "nhuanhplc.near", + 2 + ], + [ + "nic.near", + 1 + ], + [ + "nicandro.near", + 1 + ], + [ + "nicolecano.near", + 1 + ], + [ + "nightingale.near", + 1 + ], + [ + "nightsailor.near", + 1 + ], + [ + "nijem79.near", + 2 + ], + [ + "nik.near", + 1 + ], + [ + "nikolai1.near", + 1 + ], + [ + "nikolajrosendal.near", + 1 + ], + [ + "nikolay.near", + 1 + ], + [ + "nikolenko.near", + 1 + ], + [ + "nil.near", + 2 + ], + [ + "ning.near", + 1 + ], + [ + "ninini222nini22.near", + 2 + ], + [ + "ninjazz.near", + 1 + ], + [ + "niocris.near", + 2 + ], + [ + "nitin666.near", + 2 + ], + [ + "nitup88.near", + 1 + ], + [ + "nk112.near", + 1 + ], + [ + "nke1675.near", + 4 + ], + [ + "nknm99.near", + 1 + ], + [ + "nntn61.near", + 1 + ], + [ + "nodeasy.near", + 1 + ], + [ + "nomaan.near", + 1 + ], + [ + "nomer1.near", + 1 + ], + [ + "nomer2.near", + 1 + ], + [ + "nomer3.near", + 1 + ], + [ + "nomer4.near", + 1 + ], + [ + "nomer5.near", + 1 + ], + [ + "nomer6.near", + 1 + ], + [ + "nonli.near", + 1 + ], + [ + "nonsense.near", + 3 + ], + [ + "noom.near", + 1 + ], + [ + "noreply.near", + 1 + ], + [ + "norvit.near", + 3 + ], + [ + "not.near", + 1 + ], + [ + "notfarbutreally.near", + 1 + ], + [ + "notime.near", + 1 + ], + [ + "notsatoshi.near", + 1 + ], + [ + "nottoo.near", + 1 + ], + [ + "nqlarch.near", + 1 + ], + [ + "nsretenovic.near", + 1 + ], + [ + "number9dream.near", + 1 + ], + [ + "nuneun.near", + 8 + ], + [ + "nurgazy.near", + 2 + ], + [ + "nurislan.near", + 1 + ], + [ + "nut.near", + 1 + ], + [ + "nwright.near", + 1 + ], + [ + "nyc.near", + 1 + ], + [ + "o.near", + 1 + ], + [ + "o309steffe.near", + 1 + ], + [ + "o6mopok.near", + 1 + ], + [ + "o_belo.near", + 2 + ], + [ + "od3.near", + 1 + ], + [ + "odin.near", + 1 + ], + [ + "odoovan.near", + 3 + ], + [ + "og.near", + 2 + ], + [ + "ograma.near", + 2 + ], + [ + "ohadbachner.near", + 1 + ], + [ + "ojosdepez.near", + 2 + ], + [ + "okinawa.near", + 1 + ], + [ + "oldripvanwinkle.near", + 1 + ], + [ + "oldschool.near", + 1 + ], + [ + "oleg.near", + 2 + ], + [ + "olegan.near", + 1 + ], + [ + "olegjan2008.near", + 1 + ], + [ + "oleksandrantipin.near", + 2 + ], + [ + "olesiasol1981.near", + 1 + ], + [ + "olha.near", + 2 + ], + [ + "oli.near", + 1 + ], + [ + "olibay.near", + 7 + ], + [ + "oliser.near", + 1 + ], + [ + "omanizen.near", + 1 + ], + [ + "omar.near", + 1 + ], + [ + "omeshock.near", + 2 + ], + [ + "omg.near", + 2 + ], + [ + "omgclayaiken.near", + 1 + ], + [ + "omgwhy.near", + 1 + ], + [ + "omoide365.near", + 1 + ], + [ + "onceberry.near", + 2 + ], + [ + "ondrishe.near", + 4 + ], + [ + "one.near", + 2 + ], + [ + "oni365.near", + 1 + ], + [ + "onslaught.near", + 1 + ], + [ + "ontask.near", + 3 + ], + [ + "onur.near", + 1 + ], + [ + "oops.near", + 1 + ], + [ + "openb0x.near", + 1 + ], + [ + "oracle.near", + 1 + ], + [ + "orange.near", + 1 + ], + [ + "orangelcub.near", + 1 + ], + [ + "ordian.near", + 1 + ], + [ + "ork.near", + 1 + ], + [ + "orlando.near", + 1 + ], + [ + "orlando_larias.near", + 1 + ], + [ + "ortonre.near", + 1 + ], + [ + "os.near", + 1 + ], + [ + "oscar.near", + 2 + ], + [ + "oscarola.near", + 2 + ], + [ + "oscartsui.near", + 2 + ], + [ + "osipoff.near", + 1 + ], + [ + "oskolkov.near", + 2 + ], + [ + "osoroshia.near", + 1 + ], + [ + "othylmann.near", + 1 + ], + [ + "otukarehitoiki.near", + 2 + ], + [ + "ourplay.near", + 1 + ], + [ + "outinside.near", + 1 + ], + [ + "over.near", + 1 + ], + [ + "ovi.near", + 1 + ], + [ + "owarsharif.near", + 1 + ], + [ + "ox5678.near", + 1 + ], + [ + "oxotnik1.near", + 1 + ], + [ + "oxotnik2.near", + 1 + ], + [ + "ozan.near", + 1 + ], + [ + "ozgur.near", + 1 + ], + [ + "ozymandius.near", + 1 + ], + [ + "p007.near", + 2 + ], + [ + "pa132.near", + 1 + ], + [ + "pablo1128.near", + 2 + ], + [ + "pablot-93.near", + 2 + ], + [ + "pacek.near", + 1 + ], + [ + "pacheckd.near", + 1 + ], + [ + "paco.near", + 2 + ], + [ + "pancer.near", + 1 + ], + [ + "panda8888.near", + 1 + ], + [ + "pandalucu66.near", + 1 + ], + [ + "pandeymikhil.near", + 1 + ], + [ + "pantera_capital.near", + 1 + ], + [ + "pantufl.near", + 2 + ], + [ + "paquci.near", + 2 + ], + [ + "paradigm.near", + 1 + ], + [ + "parisa.near", + 2 + ], + [ + "parviz.near", + 3 + ], + [ + "pastamasta.near", + 2 + ], + [ + "pathrock.near", + 1 + ], + [ + "patrick.near", + 6 + ], + [ + "patrick1.near", + 10 + ], + [ + "paul.near", + 1 + ], + [ + "paul_budd1.near", + 1 + ], + [ + "paulbarnard.near", + 1 + ], + [ + "paulgould.near", + 1 + ], + [ + "pauljin.near", + 1 + ], + [ + "paulli31.near", + 1 + ], + [ + "paulnear01.near", + 2 + ], + [ + "paulwhiteway.near", + 1 + ], + [ + "payments.near", + 1 + ], + [ + "paypal.near", + 1 + ], + [ + "pchela_tr.near", + 2 + ], + [ + "peenka.near", + 1 + ], + [ + "peeveee1.near", + 1 + ], + [ + "penatrader.near", + 1 + ], + [ + "pepip.near", + 1 + ], + [ + "peterz.near", + 1 + ], + [ + "petrov.near", + 1 + ], + [ + "petrpko.near", + 1 + ], + [ + "petyokamenov.near", + 1 + ], + [ + "phackrudin.near", + 2 + ], + [ + "phainesthai.near", + 1 + ], + [ + "phigna1022.near", + 1 + ], + [ + "philh.near", + 1 + ], + [ + "philomene57.near", + 2 + ], + [ + "phlippsenx.near", + 1 + ], + [ + "phuonganh.near", + 1 + ], + [ + "phuongdung224466.near", + 1 + ], + [ + "phuongnt.near", + 1 + ], + [ + "piahrey.near", + 1 + ], + [ + "pierandrea.near", + 1 + ], + [ + "pihun.near", + 5 + ], + [ + "pikanou.near", + 1 + ], + [ + "pinkmole.near", + 1 + ], + [ + "pintofmilk.near", + 2 + ], + [ + "pip_gazebo.near", + 2 + ], + [ + "pirosiki.near", + 2 + ], + [ + "pishikinaleks.near", + 4 + ], + [ + "pitafina.near", + 2 + ], + [ + "pizza.near", + 1 + ], + [ + "pjh.near", + 1 + ], + [ + "pkmelee337.near", + 1 + ], + [ + "plavi.near", + 2 + ], + [ + "plavi23.near", + 2 + ], + [ + "plus3crypto.near", + 2 + ], + [ + "pmerheb.near", + 1 + ], + [ + "poker.near", + 1 + ], + [ + "polaco.near", + 5 + ], + [ + "policej.near", + 1 + ], + [ + "politicalbadger.near", + 1 + ], + [ + "polyatskiy.near", + 1 + ], + [ + "pomalllka.near", + 1 + ], + [ + "pompomlewis.near", + 2 + ], + [ + "pongty.near", + 3 + ], + [ + "ponomarev1980.near", + 1 + ], + [ + "pooh.near", + 4 + ], + [ + "poojabarman1960.near", + 1 + ], + [ + "poopoopaper.near", + 1 + ], + [ + "power.near", + 6 + ], + [ + "ppp666.near", + 1 + ], + [ + "pradeep1231.near", + 2 + ], + [ + "pradeipnear.near", + 3 + ], + [ + "prague.near", + 1 + ], + [ + "prateek.near", + 1 + ], + [ + "prateekj.near", + 1 + ], + [ + "prateekjassal.near", + 1 + ], + [ + "praxr.near", + 2 + ], + [ + "preddy.near", + 1 + ], + [ + "preico.near", + 1 + ], + [ + "prhino.near", + 1 + ], + [ + "primerwallet.near", + 1 + ], + [ + "prinjangwanich.near", + 7 + ], + [ + "priyanka.near", + 2 + ], + [ + "proff23562356.near", + 1 + ], + [ + "proofofstack.near", + 6 + ], + [ + "protector23.near", + 1 + ], + [ + "protocol.near", + 2 + ], + [ + "pruvh.near", + 1 + ], + [ + "prymmer.near", + 1 + ], + [ + "ptrpollupuu.near", + 1 + ], + [ + "pucheta.near", + 1 + ], + [ + "puchicoins.near", + 2 + ], + [ + "puchojenso.near", + 1 + ], + [ + "pukutai.near", + 1 + ], + [ + "pushpraj.near", + 1 + ], + [ + "putin.near", + 1 + ], + [ + "pvkpgp.near", + 1 + ], + [ + "pycche.near", + 1 + ], + [ + "q9.near", + 1 + ], + [ + "qasemiah.near", + 1 + ], + [ + "qcs13.near", + 1 + ], + [ + "qianji.near", + 1 + ], + [ + "qmargo.near", + 2 + ], + [ + "qmargo1.near", + 2 + ], + [ + "qmargo2.near", + 2 + ], + [ + "qq.near", + 2 + ], + [ + "qs6.near", + 1 + ], + [ + "quangnvd.near", + 1 + ], + [ + "quantum.near", + 1 + ], + [ + "quecksilber.near", + 2 + ], + [ + "queen.near", + 1 + ], + [ + "quickbrownfox.near", + 1 + ], + [ + "quimby.near", + 1 + ], + [ + "quocdung.near", + 2 + ], + [ + "qwer947.near", + 2 + ], + [ + "r1chman.near", + 1 + ], + [ + "r4bb1t.near", + 2 + ], + [ + "r4bbit.near", + 1 + ], + [ + "r9ph9fe8fbrq3.near", + 1 + ], + [ + "rabadji.near", + 1 + ], + [ + "rabbidfly.near", + 1 + ], + [ + "rabbit5.near", + 2 + ], + [ + "radikkrs.near", + 1 + ], + [ + "rafa.near", + 3 + ], + [ + "raghul.near", + 1 + ], + [ + "raj.near", + 1 + ], + [ + "rakmon.near", + 1 + ], + [ + "ramacoin.near", + 1 + ], + [ + "ramen.near", + 1 + ], + [ + "rameshilu.near", + 1 + ], + [ + "ramikorhonen.near", + 1 + ], + [ + "ramil.near", + 3 + ], + [ + "ramonrov.near", + 1 + ], + [ + "ramonrov2.near", + 1 + ], + [ + "rani.near", + 1 + ], + [ + "rasec24ollirum.near", + 1 + ], + [ + "rash1k.near", + 1 + ], + [ + "rasher17.near", + 1 + ], + [ + "rastalandtv.near", + 1 + ], + [ + "ratatoeskr.near", + 3 + ], + [ + "rav.near", + 2 + ], + [ + "ravikan.near", + 1 + ], + [ + "raymaker.near", + 2 + ], + [ + "raz.near", + 1 + ], + [ + "rback.near", + 2 + ], + [ + "rbb.near", + 1 + ], + [ + "rdrkn.near", + 1 + ], + [ + "realfriend.near", + 1 + ], + [ + "rebel_121.near", + 1 + ], + [ + "red.near", + 1 + ], + [ + "redlof.near", + 1 + ], + [ + "redpacket.near", + 1 + ], + [ + "reef2020.near", + 1 + ], + [ + "reemo.near", + 1 + ], + [ + "rehash.near", + 1 + ], + [ + "reiss.near", + 1 + ], + [ + "remedj.near", + 1 + ], + [ + "renatov.near", + 1 + ], + [ + "renta.near", + 1 + ], + [ + "renuo.near", + 1 + ], + [ + "reon.near", + 1 + ], + [ + "rest.near", + 1 + ], + [ + "resta69.near", + 2 + ], + [ + "retsambew.near", + 1 + ], + [ + "revenge.near", + 1 + ], + [ + "revolution.near", + 1 + ], + [ + "rezzak.near", + 1 + ], + [ + "rflxvty.near", + 1 + ], + [ + "rhaoniaragao.near", + 1 + ], + [ + "rhrusch.near", + 1 + ], + [ + "rhunestel.near", + 1 + ], + [ + "riccardo.near", + 1 + ], + [ + "richarddixon.near", + 2 + ], + [ + "rick.near", + 1 + ], + [ + "rimednid.near", + 2 + ], + [ + "rimrim.near", + 3 + ], + [ + "rinisoemarwoto.near", + 1 + ], + [ + "ripzery.near", + 1 + ], + [ + "rishabhtushar.near", + 2 + ], + [ + "risinghigh.near", + 1 + ], + [ + "ritesh.near", + 1 + ], + [ + "ritsu.near", + 1 + ], + [ + "rivendell.near", + 1 + ], + [ + "rizkiutama.near", + 2 + ], + [ + "rkreinbihl.near", + 1 + ], + [ + "rl149.near", + 1 + ], + [ + "rmayav.near", + 3 + ], + [ + "rmoiseenkov.near", + 5 + ], + [ + "rnd.near", + 1 + ], + [ + "roadtobitcoin.near", + 1 + ], + [ + "robert-zaremba.near", + 1 + ], + [ + "robert.near", + 2 + ], + [ + "robertkirchner.near", + 2 + ], + [ + "robertlim01.near", + 2 + ], + [ + "roberto_carlos_1987.near", + 2 + ], + [ + "robfarg.near", + 1 + ], + [ + "robin.near", + 2 + ], + [ + "robin23tt.near", + 2 + ], + [ + "robotix.near", + 1 + ], + [ + "rocco19.near", + 1 + ], + [ + "rodnca.near", + 1 + ], + [ + "rogertje.near", + 1 + ], + [ + "rohitgarnaik.near", + 1 + ], + [ + "rojer.near", + 1 + ], + [ + "rok.near", + 1 + ], + [ + "rokmc1066k.near", + 1 + ], + [ + "rokradej.near", + 2 + ], + [ + "romacdmx2020.near", + 1 + ], + [ + "romacdmx20202.near", + 2 + ], + [ + "roman.near", + 1 + ], + [ + "romano.near", + 1 + ], + [ + "romario.near", + 2 + ], + [ + "romaxa92.near", + 1 + ], + [ + "romezzz.near", + 1 + ], + [ + "ronaldboege.near", + 1 + ], + [ + "ronmnm.near", + 1 + ], + [ + "rooat.near", + 2 + ], + [ + "rovve89.near", + 1 + ], + [ + "royalfly.near", + 1 + ], + [ + "royalfu.near", + 1 + ], + [ + "rpw30078.near", + 3 + ], + [ + "rromic.near", + 2 + ], + [ + "rsidhu.near", + 2 + ], + [ + "rsiva.near", + 1 + ], + [ + "rss335.near", + 2 + ], + [ + "rtd2.near", + 9 + ], + [ + "rthom.near", + 9 + ], + [ + "ru.near", + 4 + ], + [ + "rubin315.near", + 1 + ], + [ + "ruchitha.near", + 1 + ], + [ + "ruda.near", + 1 + ], + [ + "rudaneck.near", + 1 + ], + [ + "ruelas.near", + 3 + ], + [ + "ruoyanlee.near", + 4 + ], + [ + "rus.near", + 1 + ], + [ + "rusalimchik.near", + 1 + ], + [ + "ruslan.near", + 1 + ], + [ + "russia.near", + 1 + ], + [ + "ruviy.near", + 1 + ], + [ + "ruxpin1.near", + 1 + ], + [ + "ryancollison.near", + 2 + ], + [ + "rybardolaza.near", + 2 + ], + [ + "ryn5g3du.near", + 1 + ], + [ + "ryr.near", + 2 + ], + [ + "rzz.near", + 2 + ], + [ + "s.near", + 4 + ], + [ + "sacha.near", + 2 + ], + [ + "sachaeslahi.near", + 3 + ], + [ + "saechiis.near", + 1 + ], + [ + "saengsooday.near", + 1 + ], + [ + "saenzcalzada.near", + 2 + ], + [ + "safranov.near", + 1 + ], + [ + "saganata.near", + 1 + ], + [ + "sailinon.near", + 1 + ], + [ + "sajakhta.near", + 1 + ], + [ + "saker2811.near", + 2 + ], + [ + "sam.near", + 1 + ], + [ + "samakrutti.near", + 1 + ], + [ + "samanthabarretto.near", + 2 + ], + [ + "samdiacos.near", + 2 + ], + [ + "samer.near", + 3 + ], + [ + "samgold.near", + 1 + ], + [ + "sami1.near", + 1 + ], + [ + "sami2.near", + 1 + ], + [ + "samijomana.near", + 5 + ], + [ + "samime.near", + 1 + ], + [ + "sammy.near", + 1 + ], + [ + "samolower.near", + 1 + ], + [ + "sandeepsudagani.near", + 2 + ], + [ + "sander.near", + 1 + ], + [ + "sanders-z.near", + 3 + ], + [ + "sandy.near", + 1 + ], + [ + "sangoku.near", + 1 + ], + [ + "sanjay_mandhan.near", + 1 + ], + [ + "sanket.near", + 1 + ], + [ + "santucho1979.near", + 1 + ], + [ + "sanz.near", + 1 + ], + [ + "sarang.near", + 1 + ], + [ + "saravana.near", + 1 + ], + [ + "sas.near", + 1 + ], + [ + "sasha.near", + 1 + ], + [ + "sashareyes.near", + 1 + ], + [ + "satinevood.near", + 1 + ], + [ + "satoshi-nakamoto.near", + 1 + ], + [ + "satoshi.near", + 1 + ], + [ + "satoshi0124.near", + 2 + ], + [ + "satoshi_nakamoto.near", + 2 + ], + [ + "satoshi_near.near", + 1 + ], + [ + "sauceee.near", + 2 + ], + [ + "sauges.near", + 2 + ], + [ + "saulscotland19.near", + 2 + ], + [ + "sav.near", + 1 + ], + [ + "savelev.near", + 1 + ], + [ + "sayes.near", + 1 + ], + [ + "sbls0919.near", + 1 + ], + [ + "scale.near", + 2 + ], + [ + "scazie.near", + 1 + ], + [ + "schlaptop.near", + 3 + ], + [ + "schmidde83.near", + 1 + ], + [ + "schopenhauer.near", + 1 + ], + [ + "schwendenwein.near", + 1 + ], + [ + "scorind5.near", + 1 + ], + [ + "scouser.near", + 2 + ], + [ + "scout.near", + 2 + ], + [ + "scoutz52.near", + 2 + ], + [ + "scrappy.near", + 1 + ], + [ + "se.near", + 1 + ], + [ + "sean.near", + 2 + ], + [ + "seb.near", + 1 + ], + [ + "seba.near", + 1 + ], + [ + "sebo58.near", + 1 + ], + [ + "sed19.near", + 1 + ], + [ + "segundawallet.near", + 1 + ], + [ + "sei204.near", + 2 + ], + [ + "sejwa.near", + 1 + ], + [ + "selenaarh1.near", + 1 + ], + [ + "selvam22.near", + 1 + ], + [ + "semenivanov6677.near", + 1 + ], + [ + "send.near", + 1 + ], + [ + "seniores111.near", + 1 + ], + [ + "seoj.near", + 1 + ], + [ + "seokhyun.near", + 2 + ], + [ + "seomax.near", + 1 + ], + [ + "serg.near", + 2 + ], + [ + "sergeyostrovnoy.near", + 7 + ], + [ + "sergeyrozov.near", + 1 + ], + [ + "sergio.near", + 1 + ], + [ + "sergiofranco.near", + 1 + ], + [ + "serik.near", + 1 + ], + [ + "serjlug2020.near", + 1 + ], + [ + "serjsergsky.near", + 2 + ], + [ + "service.near", + 1 + ], + [ + "serzh32.near", + 8 + ], + [ + "sessileserrated.near", + 1 + ], + [ + "seven.near", + 1 + ], + [ + "sex.near", + 1 + ], + [ + "sfeinberg.near", + 1 + ], + [ + "sfeinberg818.near", + 1 + ], + [ + "sff.near", + 1 + ], + [ + "sgvtys.near", + 1 + ], + [ + "shadyrifles.near", + 1 + ], + [ + "shahnawaz.near", + 1 + ], + [ + "shakur.near", + 1 + ], + [ + "shanton.near", + 2 + ], + [ + "shar.near", + 1 + ], + [ + "sharding786.near", + 1 + ], + [ + "sharzone.near", + 1 + ], + [ + "shaurya11.near", + 1 + ], + [ + "shazib.near", + 1 + ], + [ + "shekhar.near", + 1 + ], + [ + "sheva_9.near", + 1 + ], + [ + "shikione.near", + 1 + ], + [ + "shinevillehome.near", + 5 + ], + [ + "shitcoinsenpai.near", + 1 + ], + [ + "shivam.near", + 1 + ], + [ + "shodan.near", + 2 + ], + [ + "shower.near", + 2 + ], + [ + "shreyas.near", + 1 + ], + [ + "shryi41.near", + 1 + ], + [ + "shubyhere.near", + 1 + ], + [ + "shuo.near", + 1 + ], + [ + "shushle04.near", + 1 + ], + [ + "silent.near", + 2 + ], + [ + "silvano.near", + 2 + ], + [ + "simbro.near", + 1 + ], + [ + "simon.near", + 1 + ], + [ + "simona.near", + 1 + ], + [ + "simonasta.near", + 1 + ], + [ + "simongotlib.near", + 1 + ], + [ + "simonyeung.near", + 1 + ], + [ + "simple_simon.near", + 1 + ], + [ + "siribo4134.near", + 1 + ], + [ + "sirius.near", + 1 + ], + [ + "sittipong.near", + 2 + ], + [ + "siva.near", + 1 + ], + [ + "sixi.near", + 1 + ], + [ + "sjsvvsd2.near", + 1 + ], + [ + "sjsvvsd3.near", + 1 + ], + [ + "sjteeves.near", + 1 + ], + [ + "skarabej.near", + 1 + ], + [ + "skeanparty.near", + 1 + ], + [ + "skippasteve.near", + 1 + ], + [ + "skiprippa.near", + 1 + ], + [ + "skkyy.near", + 1 + ], + [ + "skopintsev.near", + 4 + ], + [ + "skorikoff.near", + 2 + ], + [ + "skycastle.near", + 1 + ], + [ + "skydan.near", + 3 + ], + [ + "slattz83.near", + 4 + ], + [ + "slavon.near", + 2 + ], + [ + "slimchance.near", + 1 + ], + [ + "slnear.near", + 1 + ], + [ + "slowpok.near", + 1 + ], + [ + "smart.near", + 1 + ], + [ + "smartstake.near", + 1 + ], + [ + "smercer.near", + 2 + ], + [ + "smith.near", + 1 + ], + [ + "smj9065.near", + 1 + ], + [ + "smoke.near", + 1 + ], + [ + "smurfoleta.near", + 1 + ], + [ + "snakamoto.near", + 2 + ], + [ + "sneza.near", + 1 + ], + [ + "snowman.near", + 1 + ], + [ + "sokolenkoandr.near", + 2 + ], + [ + "solimir.near", + 1 + ], + [ + "sonic.near", + 1 + ], + [ + "sorokin87.near", + 1 + ], + [ + "sp1010.near", + 1 + ], + [ + "spacex.near", + 1 + ], + [ + "spaincity914.near", + 1 + ], + [ + "sparrow.near", + 1 + ], + [ + "spartacus.near", + 1 + ], + [ + "spasoman.near", + 1 + ], + [ + "specialnear.near", + 1 + ], + [ + "spedisci.near", + 1 + ], + [ + "sphinx.near", + 1 + ], + [ + "spoonmeon.near", + 2 + ], + [ + "sprigan.near", + 2 + ], + [ + "spritelemon36.near", + 1 + ], + [ + "sputnik.near", + 1 + ], + [ + "sqzz.near", + 1 + ], + [ + "sr-trading.near", + 1 + ], + [ + "srazajamal.near", + 2 + ], + [ + "sreeraj.near", + 1 + ], + [ + "sri.near", + 1 + ], + [ + "srikanthraju1979.near", + 1 + ], + [ + "srini.near", + 3 + ], + [ + "sromic2.near", + 3 + ], + [ + "srubarmichal.near", + 1 + ], + [ + "srv.near", + 1 + ], + [ + "ss.near", + 1 + ], + [ + "stack.near", + 1 + ], + [ + "stacks.near", + 1 + ], + [ + "stakery.near", + 1 + ], + [ + "stakeservice.near", + 2 + ], + [ + "staking4all-validator.near", + 1 + ], + [ + "staking4all.near", + 1 + ], + [ + "staking4all01.near", + 1 + ], + [ + "stan.near", + 1 + ], + [ + "stanchocorporation.near", + 1 + ], + [ + "standrews.near", + 2 + ], + [ + "stanislavmileshin.near", + 2 + ], + [ + "stankf.near", + 2 + ], + [ + "stanly.near", + 1 + ], + [ + "stanvtb.near", + 1 + ], + [ + "stanwang.near", + 2 + ], + [ + "stardust.near", + 6 + ], + [ + "startico2017.near", + 7 + ], + [ + "stbanhf.near", + 2 + ], + [ + "steefan.near", + 1 + ], + [ + "steeny.near", + 1 + ], + [ + "stefan.near", + 1 + ], + [ + "stefan1103.near", + 2 + ], + [ + "steffen.near", + 2 + ], + [ + "stela.near", + 1 + ], + [ + "stells.near", + 1 + ], + [ + "sten.near", + 2 + ], + [ + "stephenreid.near", + 1 + ], + [ + "sterx.near", + 1 + ], + [ + "stevanus.near", + 1 + ], + [ + "steve.near", + 1 + ], + [ + "steven1989.near", + 2 + ], + [ + "stevenlwk.near", + 1 + ], + [ + "stijnlohuis.near", + 4 + ], + [ + "stirzy.near", + 1 + ], + [ + "stone.near", + 1 + ], + [ + "straj7771.near", + 1 + ], + [ + "strikerjax.near", + 1 + ], + [ + "studeywebs.near", + 1 + ], + [ + "stumpydoo5.near", + 1 + ], + [ + "styxson.near", + 2 + ], + [ + "subaozi.near", + 1 + ], + [ + "subbu.near", + 1 + ], + [ + "suhanbae.near", + 2 + ], + [ + "suisiwen.near", + 1 + ], + [ + "sukrucildirr.near", + 1 + ], + [ + "sumit.near", + 2 + ], + [ + "sun.near", + 1 + ], + [ + "sung.near", + 1 + ], + [ + "sunliming.near", + 2 + ], + [ + "sunrise.near", + 1 + ], + [ + "sunshine.near", + 1 + ], + [ + "superlogico1983.near", + 2 + ], + [ + "superman.near", + 1 + ], + [ + "superpups.near", + 2 + ], + [ + "superzack1221.near", + 1 + ], + [ + "supi-tup.near", + 1 + ], + [ + "suppernho.near", + 5 + ], + [ + "suresh.near", + 1 + ], + [ + "surf.near", + 1 + ], + [ + "sushi.near", + 1 + ], + [ + "sushiswap.near", + 1 + ], + [ + "suyoda.near", + 2 + ], + [ + "suzu151091.near", + 1 + ], + [ + "svdv.near", + 1 + ], + [ + "svetlanabunkova.near", + 1 + ], + [ + "svetlanaby.near", + 1 + ], + [ + "swaggadan.near", + 2 + ], + [ + "swagomat.near", + 1 + ], + [ + "sweetpotatoz.near", + 1 + ], + [ + "swiss-staking.near", + 1 + ], + [ + "swx123.near", + 3 + ], + [ + "swys.near", + 1 + ], + [ + "sxguan666.near", + 1 + ], + [ + "sxguanok.near", + 2 + ], + [ + "symadrynnikov.near", + 1 + ], + [ + "syncnode.near", + 1 + ], + [ + "system_s.near", + 1 + ], + [ + "sz.near", + 2 + ], + [ + "t14ul1.near", + 1 + ], + [ + "tabtrading.near", + 1 + ], + [ + "taforyou.near", + 1 + ], + [ + "taganskii.near", + 1 + ], + [ + "taheer121.near", + 2 + ], + [ + "taiyab.near", + 2 + ], + [ + "takashi.near", + 2 + ], + [ + "taku.near", + 1 + ], + [ + "tameemwallet2.near", + 1 + ], + [ + "tanderug.near", + 1 + ], + [ + "taoros.near", + 3 + ], + [ + "tarun.near", + 1 + ], + [ + "taruntrav.near", + 2 + ], + [ + "tatelinpp.near", + 5 + ], + [ + "tatyana_igumenceva.near", + 1 + ], + [ + "taurolsan.near", + 1 + ], + [ + "taylorholland13.near", + 1 + ], + [ + "tayyab.near", + 1 + ], + [ + "tb.near", + 1 + ], + [ + "tchiu.near", + 2 + ], + [ + "tclshilf.near", + 1 + ], + [ + "tdmoor.near", + 5 + ], + [ + "techno.near", + 1 + ], + [ + "teleginate.near", + 1 + ], + [ + "telegins.near", + 1 + ], + [ + "teleginsv.near", + 1 + ], + [ + "temaspeak.near", + 2 + ], + [ + "temidayore.near", + 1 + ], + [ + "tententen2.near", + 3 + ], + [ + "terencehan.near", + 1 + ], + [ + "terentyev089.near", + 1 + ], + [ + "terror7777.near", + 1 + ], + [ + "terryow11.near", + 1 + ], + [ + "teuhcatl.near", + 1 + ], + [ + "teusevi.near", + 2 + ], + [ + "tevinl.near", + 2 + ], + [ + "texgeek.near", + 2 + ], + [ + "th3m477.near", + 1 + ], + [ + "thanhvinh10589.near", + 1 + ], + [ + "thatalex.near", + 1 + ], + [ + "the.near", + 1 + ], + [ + "the_lexa.near", + 1 + ], + [ + "the_liolik.near", + 1 + ], + [ + "thegoat.near", + 2 + ], + [ + "thehatter.near", + 1 + ], + [ + "themoon.near", + 1 + ], + [ + "theo.near", + 2 + ], + [ + "theobtl.near", + 1 + ], + [ + "theresa.near", + 1 + ], + [ + "thesky1234.near", + 2 + ], + [ + "thienlongtu.near", + 6 + ], + [ + "thisismynearwallet.near", + 1 + ], + [ + "thiyaneswaran.near", + 2 + ], + [ + "thomas.near", + 1 + ], + [ + "thomasbaak.near", + 2 + ], + [ + "thomasschneider110471.near", + 12 + ], + [ + "thsnz.near", + 1 + ], + [ + "ti3au.near", + 2 + ], + [ + "tichathegreat.near", + 1 + ], + [ + "tiger.near", + 1 + ], + [ + "till.near", + 1 + ], + [ + "timchuk_vladimir89.near", + 1 + ], + [ + "timo.near", + 2 + ], + [ + "timolei.near", + 1 + ], + [ + "timonthen3arkat.near", + 1 + ], + [ + "tinaing2019.near", + 2 + ], + [ + "tinanpha.near", + 1 + ], + [ + "tinotrung.near", + 1 + ], + [ + "tipazloy.near", + 2 + ], + [ + "tirreg9.near", + 1 + ], + [ + "titalunz.near", + 2 + ], + [ + "titikaka791.near", + 1 + ], + [ + "tj77.near", + 6 + ], + [ + "tlinder.near", + 1 + ], + [ + "tobeaj2mer.near", + 1 + ], + [ + "tobiofneptune.near", + 1 + ], + [ + "todamoon.near", + 1 + ], + [ + "todd.near", + 3 + ], + [ + "todeco.near", + 2 + ], + [ + "toebee.near", + 1 + ], + [ + "tof.near", + 1 + ], + [ + "tokenfundamentalist.near", + 1 + ], + [ + "tolzr.near", + 1 + ], + [ + "tom.near", + 2 + ], + [ + "tom68.near", + 1 + ], + [ + "tomas.near", + 1 + ], + [ + "tomas007ales.near", + 1 + ], + [ + "tomasz.near", + 2 + ], + [ + "tome.near", + 1 + ], + [ + "tommy.near", + 1 + ], + [ + "tommybananas.near", + 1 + ], + [ + "tommynara.near", + 2 + ], + [ + "tommyvo.near", + 1 + ], + [ + "tommzardoz.near", + 1 + ], + [ + "tompi_2014.near", + 1 + ], + [ + "tongcp1576.near", + 1 + ], + [ + "tonton.near", + 3 + ], + [ + "tony.near", + 1 + ], + [ + "toonni.near", + 2 + ], + [ + "torledo.near", + 1 + ], + [ + "toth3moon.near", + 1 + ], + [ + "tp493159.near", + 1 + ], + [ + "trachartem.near", + 2 + ], + [ + "tradesman.near", + 1 + ], + [ + "trananhkhoait.near", + 1 + ], + [ + "tranducnham.near", + 1 + ], + [ + "travellerfrank.near", + 1 + ], + [ + "treasury.near", + 1 + ], + [ + "trepidswampmountain.near", + 1 + ], + [ + "trevordagg.near", + 2 + ], + [ + "tribe.near", + 1 + ], + [ + "trick.near", + 1 + ], + [ + "trim.near", + 1 + ], + [ + "trojan.near", + 1 + ], + [ + "troks.near", + 2 + ], + [ + "trongtour.near", + 4 + ], + [ + "troy.near", + 1 + ], + [ + "true.near", + 2 + ], + [ + "truongpx.near", + 1 + ], + [ + "truongvanhoa19.near", + 1 + ], + [ + "tschimi.near", + 1 + ], + [ + "tso.near", + 1 + ], + [ + "tsyregma.near", + 1 + ], + [ + "tuanduongk37.near", + 1 + ], + [ + "tunghaolin.near", + 1 + ], + [ + "tuzem.near", + 1 + ], + [ + "twintbeast.near", + 2 + ], + [ + "twr.near", + 2 + ], + [ + "txentejmnz.near", + 2 + ], + [ + "txirrisklas.near", + 2 + ], + [ + "tyler.near", + 1 + ], + [ + "tymon.near", + 4 + ], + [ + "tyoo.near", + 1 + ], + [ + "tzouf.near", + 1 + ], + [ + "u.near", + 2 + ], + [ + "ubikcapital.near", + 1 + ], + [ + "uen.near", + 2 + ], + [ + "ujjwal.near", + 1 + ], + [ + "ul.near", + 1 + ], + [ + "ula.near", + 1 + ], + [ + "ultracyl.near", + 1 + ], + [ + "undefined.near", + 2 + ], + [ + "underminer.near", + 1 + ], + [ + "unicorn.near", + 1 + ], + [ + "uniswap.near", + 1 + ], + [ + "unit.near", + 1 + ], + [ + "unknow-person.near", + 1 + ], + [ + "unlimg.near", + 1 + ], + [ + "unternehmer_83.near", + 1 + ], + [ + "up.near", + 6 + ], + [ + "upepe3keyama.near", + 4 + ], + [ + "uptowneric.near", + 1 + ], + [ + "ural.near", + 1 + ], + [ + "urmas.near", + 1 + ], + [ + "us.near", + 2 + ], + [ + "usa.near", + 1 + ], + [ + "usd.near", + 1 + ], + [ + "usdt.near", + 1 + ], + [ + "username.near", + 4 + ], + [ + "userzuzer.near", + 2 + ], + [ + "utifom.near", + 1 + ], + [ + "vabsie.near", + 1 + ], + [ + "vaclavmaslov1975.near", + 1 + ], + [ + "vadim_0060708.near", + 2 + ], + [ + "vadimpigolenko.near", + 2 + ], + [ + "vaihen.near", + 1 + ], + [ + "valodja.near", + 2 + ], + [ + "vananh.near", + 1 + ], + [ + "vanminh122298.near", + 1 + ], + [ + "vantoanbk57.near", + 2 + ], + [ + "vanvinh.near", + 1 + ], + [ + "varenka.near", + 1 + ], + [ + "varlan.near", + 2 + ], + [ + "varun.near", + 1 + ], + [ + "vasay1990.near", + 1 + ], + [ + "vasilies.near", + 1 + ], + [ + "vasilij333.near", + 2 + ], + [ + "vavanu4.near", + 2 + ], + [ + "vbs.near", + 1 + ], + [ + "vcar.near", + 1 + ], + [ + "vdfrost.near", + 1 + ], + [ + "vdyz4t8u.near", + 1 + ], + [ + "vega.near", + 1 + ], + [ + "veitali.near", + 3 + ], + [ + "vencn.near", + 1 + ], + [ + "venediger.near", + 1 + ], + [ + "verosar.near", + 1 + ], + [ + "very.near", + 3 + ], + [ + "vgng.near", + 2 + ], + [ + "vicky.near", + 2 + ], + [ + "vickyrock30.near", + 1 + ], + [ + "vict.near", + 1 + ], + [ + "vietanh.near", + 1 + ], + [ + "vietanhle8888.near", + 1 + ], + [ + "vietdo.near", + 1 + ], + [ + "vigneshmadrista.near", + 1 + ], + [ + "vigneshpandian.near", + 1 + ], + [ + "vijayakumar.near", + 1 + ], + [ + "vikassood1.near", + 1 + ], + [ + "viktor.near", + 2 + ], + [ + "viktor_benner.near", + 1 + ], + [ + "viktorliu.near", + 1 + ], + [ + "viktorskf.near", + 1 + ], + [ + "vikusss89.near", + 1 + ], + [ + "vilija.near", + 2 + ], + [ + "vincent.near", + 1 + ], + [ + "vinilpc.near", + 1 + ], + [ + "vinmar.near", + 3 + ], + [ + "vip.near", + 1 + ], + [ + "vitalik.near", + 1 + ], + [ + "vitalik_buterin.near", + 1 + ], + [ + "vitaliy.near", + 1 + ], + [ + "vitaly.near", + 1 + ], + [ + "vitas86.near", + 1 + ], + [ + "vitkov.near", + 1 + ], + [ + "vitkov2.near", + 1 + ], + [ + "vivi1333.near", + 1 + ], + [ + "vk1118.near", + 1 + ], + [ + "vlad.near", + 1 + ], + [ + "vladimirg.near", + 1 + ], + [ + "vladis.near", + 1 + ], + [ + "vladsmd.near", + 2 + ], + [ + "voland04.near", + 1 + ], + [ + "vote4pedro.near", + 1 + ], + [ + "vovusik.near", + 1 + ], + [ + "vox.near", + 1 + ], + [ + "vy_vy_22.near", + 1 + ], + [ + "vytas.near", + 1 + ], + [ + "wang16.near", + 2 + ], + [ + "wangchao86.near", + 1 + ], + [ + "wanglei0095.near", + 1 + ], + [ + "wangsta.near", + 1 + ], + [ + "wanseob.near", + 1 + ], + [ + "warisyaqubi.near", + 2 + ], + [ + "warlock.near", + 1 + ], + [ + "warmcapital.near", + 1 + ], + [ + "was.near", + 3 + ], + [ + "waters.near", + 1 + ], + [ + "wearesatoshi.near", + 1 + ], + [ + "weariedatom.near", + 3 + ], + [ + "web3.near", + 1 + ], + [ + "webmel.near", + 2 + ], + [ + "weed.near", + 1 + ], + [ + "weiliangfung.near", + 2 + ], + [ + "wenjielu.near", + 2 + ], + [ + "wenxianjiang.near", + 1 + ], + [ + "wetez.near", + 1 + ], + [ + "whatever.near", + 1 + ], + [ + "whiletrue.near", + 2 + ], + [ + "white_house.near", + 3 + ], + [ + "whoismrbru.near", + 1 + ], + [ + "whoismrskill.near", + 1 + ], + [ + "wildalert.near", + 1 + ], + [ + "willymooth.near", + 1 + ], + [ + "wimscot123.near", + 2 + ], + [ + "win.near", + 1 + ], + [ + "winnerclubs.near", + 1 + ], + [ + "winnieyow.near", + 1 + ], + [ + "wistar.near", + 1 + ], + [ + "wladka666.near", + 3 + ], + [ + "wodikasha.near", + 1 + ], + [ + "wojak.near", + 1 + ], + [ + "wolf34.near", + 1 + ], + [ + "wompwomp.near", + 1 + ], + [ + "wonbo.near", + 1 + ], + [ + "wonderreturn.near", + 1 + ], + [ + "woodwam.near", + 1 + ], + [ + "wookiewarrior.near", + 2 + ], + [ + "wow.near", + 1 + ], + [ + "wowanjd.near", + 1 + ], + [ + "wr.near", + 1 + ], + [ + "wtldmrs.near", + 2 + ], + [ + "wu365.near", + 1 + ], + [ + "wu40.near", + 1 + ], + [ + "wudi.near", + 2 + ], + [ + "wuffle.near", + 1 + ], + [ + "wurstmensch.near", + 1 + ], + [ + "ww.near", + 1 + ], + [ + "wx.near", + 2 + ], + [ + "x.near", + 1 + ], + [ + "xafobia.near", + 2 + ], + [ + "xander.near", + 1 + ], + [ + "xanyar.near", + 2 + ], + [ + "xbadmax.near", + 2 + ], + [ + "xbleidx11.near", + 1 + ], + [ + "xedap.near", + 1 + ], + [ + "xianghong0912.near", + 1 + ], + [ + "xiaobo.near", + 1 + ], + [ + "xiaojun.near", + 1 + ], + [ + "xikunying.near", + 1 + ], + [ + "xoreth.near", + 8 + ], + [ + "xpaco.near", + 2 + ], + [ + "xpromt.near", + 1 + ], + [ + "xrahul.near", + 1 + ], + [ + "xtreme11.near", + 1 + ], + [ + "xtreme12.near", + 1 + ], + [ + "xuyogi.near", + 1 + ], + [ + "xvideos469.near", + 2 + ], + [ + "xx.near", + 1 + ], + [ + "xxx.near", + 1 + ], + [ + "xy.near", + 2 + ], + [ + "xyb.near", + 2 + ], + [ + "y0b.near", + 1 + ], + [ + "yajirushiworks2020.near", + 2 + ], + [ + "yakultza.near", + 2 + ], + [ + "yam.near", + 1 + ], + [ + "yams.near", + 4 + ], + [ + "yan.near", + 5 + ], + [ + "yangchj.near", + 1 + ], + [ + "yangshen.near", + 1 + ], + [ + "yangweizi911.near", + 1 + ], + [ + "yanmingguo.near", + 1 + ], + [ + "yanok1985.near", + 2 + ], + [ + "yaomengmeng1986.near", + 1 + ], + [ + "yarco.near", + 1 + ], + [ + "yashman.near", + 1 + ], + [ + "yasser.near", + 2 + ], + [ + "ybob.near", + 2 + ], + [ + "yc.near", + 2 + ], + [ + "ycl.near", + 2 + ], + [ + "yderdiki.near", + 1 + ], + [ + "yearn.near", + 1 + ], + [ + "yellowboy.near", + 1 + ], + [ + "yessin.near", + 2 + ], + [ + "yfi.near", + 1 + ], + [ + "yfledger.near", + 1 + ], + [ + "yicrson.near", + 1 + ], + [ + "yin123y.near", + 3 + ], + [ + "yinqa.near", + 1 + ], + [ + "ykhan60.near", + 1 + ], + [ + "ykvph.near", + 1 + ], + [ + "yl.near", + 1 + ], + [ + "yoda.near", + 3 + ], + [ + "yoda_uk.near", + 2 + ], + [ + "yodake.near", + 2 + ], + [ + "yolo_surf.near", + 1 + ], + [ + "yongming.near", + 5 + ], + [ + "yoonho00.near", + 1 + ], + [ + "york1ng99.near", + 1 + ], + [ + "youtube.near", + 2 + ], + [ + "yrl2000.near", + 1 + ], + [ + "ysxlin01.near", + 2 + ], + [ + "yt8686jh.near", + 1 + ], + [ + "yuchialiang.near", + 2 + ], + [ + "yuggalen.near", + 1 + ], + [ + "yujinbo.near", + 1 + ], + [ + "yulian.near", + 1 + ], + [ + "yuridiablo.near", + 2 + ], + [ + "yuristturist.near", + 1 + ], + [ + "yuriy.near", + 1 + ], + [ + "yury.near", + 1 + ], + [ + "yuta.near", + 1 + ], + [ + "yuther.near", + 1 + ], + [ + "yy.near", + 1 + ], + [ + "yylai.near", + 2 + ], + [ + "z.near", + 1 + ], + [ + "zachzwei.near", + 1 + ], + [ + "zaebumba.near", + 1 + ], + [ + "zamzam1.near", + 1 + ], + [ + "zamzam2.near", + 1 + ], + [ + "zano66.near", + 1 + ], + [ + "zaremba.near", + 1 + ], + [ + "zazoley.near", + 2 + ], + [ + "zealot.near", + 1 + ], + [ + "zeecity.near", + 1 + ], + [ + "zenithli.near", + 1 + ], + [ + "zeromax.near", + 1 + ], + [ + "zeroone.near", + 1 + ], + [ + "zeus7912.near", + 4 + ], + [ + "zeusone.near", + 1 + ], + [ + "zeze.near", + 2 + ], + [ + "zhartur89.near", + 1 + ], + [ + "zhelserhii09.near", + 1 + ], + [ + "zhuhao529440.near", + 1 + ], + [ + "zhuoyue.near", + 1 + ], + [ + "ziaaddils.near", + 1 + ], + [ + "zilj.near", + 1 + ], + [ + "zinur.near", + 1 + ], + [ + "zitko.near", + 2 + ], + [ + "ziyou.near", + 1 + ], + [ + "zjouraolian.near", + 1 + ], + [ + "zkp.near", + 2 + ], + [ + "zoek.near", + 1 + ], + [ + "zorro.near", + 1 + ], + [ + "zpool.near", + 4 + ], + [ + "zyzygy.near", + 1 + ], + [ + "zzangpk.near", + 1 + ], + [ + "zzf.near", + 1 + ], + [ + "zztom.near", + 1 + ] +] \ No newline at end of file diff --git a/utils/mainnet-res/src/lib.rs b/utils/mainnet-res/src/lib.rs new file mode 100644 index 000000000..fdf81ae90 --- /dev/null +++ b/utils/mainnet-res/src/lib.rs @@ -0,0 +1,19 @@ +use unc_account_id::AccountId; +use unc_chain_configs::Genesis; +use unc_primitives::receipt::ReceiptResult; + +pub fn mainnet_restored_receipts() -> ReceiptResult { + let data = include_bytes!("../res/mainnet_restored_receipts.json"); + serde_json::from_slice(data) + .expect("File with receipts restored after apply_chunks fix has to be correct") +} + +pub fn mainnet_storage_usage_delta() -> Vec<(AccountId, u64)> { + let data = include_bytes!("../res/storage_usage_delta.json"); + serde_json::from_slice(data).expect("File with storage usage delta has to be correct") +} + +pub fn mainnet_genesis() -> Genesis { + let data = include_bytes!("../res/mainnet_genesis.json"); + serde_json::from_slice(data).expect("Failed to deserialize mainnet genesis") +} diff --git a/utils/mainnet-res/tests/load_genesis.rs b/utils/mainnet-res/tests/load_genesis.rs new file mode 100644 index 000000000..392ca6b35 --- /dev/null +++ b/utils/mainnet-res/tests/load_genesis.rs @@ -0,0 +1,6 @@ +use unc_chain_configs::{Genesis, GenesisValidationMode}; + +#[test] +fn test_load_genesis() { + Genesis::from_file("res/mainnet_genesis.json", GenesisValidationMode::Full).unwrap(); +} diff --git a/utils/stdx/Cargo.toml b/utils/stdx/Cargo.toml new file mode 100644 index 000000000..563684228 --- /dev/null +++ b/utils/stdx/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "unc-stdx" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "This crate contains polyfills which should really be in std, but currently aren't for one reason or another." +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +# Absolutely must not depend on any crates from framework workspace, +# and should have as few dependencies as possible otherwise. diff --git a/utils/stdx/src/lib.rs b/utils/stdx/src/lib.rs new file mode 100644 index 000000000..a31eb9a75 --- /dev/null +++ b/utils/stdx/src/lib.rs @@ -0,0 +1,153 @@ +//! `stdx` crate contains polyfills which should really be in std, +//! but currently aren't for one reason or another. +#![deny(clippy::arithmetic_side_effects)] + +// TODO(mina86): Replace usage of the split functions by split_array_ref et al +// methods of array and slice types once those are stabilised. + +/// Splits `&[u8; L + R]` into `(&[u8; L], &[u8; R])`. +pub fn split_array( + xs: &[u8; N], +) -> (&[u8; L], &[u8; R]) { + #[allow(clippy::let_unit_value)] + let () = AssertEqSum::::OK; + + let (left, right) = xs.split_at(L); + (left.try_into().unwrap(), right.try_into().unwrap()) +} + +/// Splits `&mut [u8; L + R]` into `(&mut [u8; L], &mut [u8; R])`. +pub fn split_array_mut( + xs: &mut [u8; N], +) -> (&mut [u8; L], &mut [u8; R]) { + #[allow(clippy::let_unit_value)] + let () = AssertEqSum::::OK; + + let (left, right) = xs.split_at_mut(L); + (left.try_into().unwrap(), right.try_into().unwrap()) +} + +/// Splits `&[u8]` into `(&[u8; N], &[u8])`. **Panics** if slice is shorter +/// than `N`. +pub fn split_slice(slice: &[u8]) -> (&[u8; N], &[u8]) { + let (head, tail) = slice.split_at(N); + (head.try_into().unwrap(), tail) +} + +/// Splits `&[u8]` into `(&[u8], &[u8; N])`. **Panics** if slice is shorter +/// than `N`. +pub fn rsplit_slice(slice: &[u8]) -> (&[u8], &[u8; N]) { + let index = slice.len().checked_sub(N).expect("len to be ≥ N"); + let (head, tail) = slice.split_at(index); + (head, tail.try_into().unwrap()) +} + +/// Splits `&[u8]` into `(&[u8; N], &[u8])`. **Panics** if slice is shorter +/// than `N`. +pub fn split_slice_mut(slice: &mut [u8]) -> (&mut [u8; N], &mut [u8]) { + let (head, tail) = slice.split_at_mut(N); + (head.try_into().unwrap(), tail) +} + +/// Splits `&[u8]` into `(&[u8], &[u8; N])`. **Panics** if slice is shorter +/// than `N`. +pub fn rsplit_slice_mut(slice: &mut [u8]) -> (&mut [u8], &mut [u8; N]) { + let index = slice.len().checked_sub(N).expect("len to be ≥ N"); + let (head, tail) = slice.split_at_mut(index); + (head, tail.try_into().unwrap()) +} + +#[test] +fn test_split() { + assert_eq!((&[0, 1], &[2, 3, 4]), split_array(&[0, 1, 2, 3, 4])); + assert_eq!((&mut [0, 1], &mut [2, 3, 4]), split_array_mut(&mut [0, 1, 2, 3, 4])); + + assert_eq!((&[0, 1], &[2, 3, 4][..]), split_slice(&[0, 1, 2, 3, 4])); + assert_eq!((&[0, 1][..], &[2, 3, 4]), rsplit_slice(&[0, 1, 2, 3, 4])); + assert_eq!((&mut [0, 1], &mut [2, 3, 4][..]), split_slice_mut(&mut [0, 1, 2, 3, 4])); + assert_eq!((&mut [0, 1][..], &mut [2, 3, 4]), rsplit_slice_mut(&mut [0, 1, 2, 3, 4])); +} + +/// Joins `[u8; L]` and `[u8; R]` into `[u8; L + R]`. +pub fn join_array( + left: [u8; L], + right: [u8; R], +) -> [u8; N] { + #[allow(clippy::let_unit_value)] + let () = AssertEqSum::::OK; + + let mut res = [0; N]; + let (l, r) = res.split_at_mut(L); + l.copy_from_slice(&left); + r.copy_from_slice(&right); + res +} + +#[test] +fn test_join() { + assert_eq!([0, 1, 2, 3], join_array([0, 1], [2, 3])); +} + +/// Splits a slice into a slice of N-element arrays. +// TODO(mina86): Replace with [T]::as_chunks once that’s stabilised. +pub fn as_chunks(slice: &[T]) -> (&[[T; N]], &[T]) { + #[allow(clippy::let_unit_value)] + let () = AssertNonZero::::OK; + + let len = slice.len().checked_div(N).expect("static assert above ensures N ≠ 0"); + let (head, tail) = slice + .split_at(len.checked_mul(N).expect("len * N ≤ slice.len() hence can't overflow here")); + + // SAFETY: We cast a slice of `len * N` elements into a slice of `len` many + // `N` elements chunks. + let head = unsafe { std::slice::from_raw_parts(head.as_ptr().cast(), len) }; + (head, tail) +} + +#[derive(Debug, Eq, PartialEq)] +pub struct InexactChunkingError { + slice_len: usize, + chunk_size: usize, +} +impl std::error::Error for InexactChunkingError {} +impl std::fmt::Display for InexactChunkingError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "slice of size {} cannot be precisely split into chunks of size {}", + self.slice_len, self.chunk_size + ) + } +} + +/// Like `as_chunks` but returns an error if there’s a remainder. +pub fn as_chunks_exact(slice: &[T]) -> Result<&[[T; N]], InexactChunkingError> { + let (chunks, remainder) = as_chunks(slice); + if remainder.is_empty() { + Ok(chunks) + } else { + Err(InexactChunkingError { slice_len: slice.len(), chunk_size: N }) + } +} + +#[test] +fn test_as_chunks() { + assert_eq!((&[[0, 1], [2, 3]][..], &[4][..]), as_chunks::<2, _>(&[0, 1, 2, 3, 4])); + assert_eq!(Ok(&[[0, 1], [2, 3]][..]), as_chunks_exact::<2, _>(&[0, 1, 2, 3])); + assert_eq!( + Err(InexactChunkingError { slice_len: 5, chunk_size: 2 }), + as_chunks_exact::<2, _>(&[0, 1, 2, 3, 4]) + ); +} + +/// Asserts, at compile time, that `S == A + B`. +struct AssertEqSum; +impl AssertEqSum { + const OK: () = assert!(S == A + B); +} + +/// Asserts, at compile time, that `N` is non-zero. +struct AssertNonZero; +impl AssertNonZero { + const OK: () = assert!(N != 0); +} diff --git a/utils/unc-cache/Cargo.toml b/utils/unc-cache/Cargo.toml new file mode 100644 index 000000000..0e3df6202 --- /dev/null +++ b/utils/unc-cache/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "unc-cache" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "do not use this, new versions can stop being published at literally any time" +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true + +[dependencies] +lru.workspace = true + +[dev-dependencies] +bencher.workspace = true +rand.workspace = true + +[[bench]] +name = "cache" +harness = false diff --git a/utils/unc-cache/README.md b/utils/unc-cache/README.md new file mode 100644 index 000000000..5fcdefbe2 --- /dev/null +++ b/utils/unc-cache/README.md @@ -0,0 +1,3 @@ +# unc-cache + +Do not use this, new versions can stop being published on crates.io at literally any time diff --git a/utils/unc-cache/benches/cache.rs b/utils/unc-cache/benches/cache.rs new file mode 100644 index 000000000..767e450b5 --- /dev/null +++ b/utils/unc-cache/benches/cache.rs @@ -0,0 +1,42 @@ +#[macro_use] +extern crate bencher; + +use bencher::Bencher; +use lru::LruCache; +use unc_cache::SyncLruCache; + +fn bench_lru(bench: &mut Bencher) { + bench.iter(|| { + let mut cache = LruCache::new(10000); + for _x in 0..1000000 { + let a = rand::random::(); + let b = rand::random::(); + cache.put(a, b); + } + }); +} + +fn bench_lru_cache(bench: &mut Bencher) { + bench.iter(|| { + let cache = SyncLruCache::new(10000); + for _x in 0..1000000 { + let a = rand::random::(); + let b = rand::random::(); + cache.put(a, b); + } + }); +} + +benchmark_group!(benches, bench_lru, bench_lru_cache); + +benchmark_main!(benches); + +// test bench_sized_cache ... bench: 6,709,170 ns/iter (+/- 3,536,860) +// test bench_lru ... bench: 35,469,761 ns/iter (+/- 1,045,064) +// test bench_sized_cache ... bench: 47,299,971 ns/iter (+/- 1,446,543) + +// LruCache with `cached` +// test bench_lru_cache ... bench: 51,420,781 ns/iter (+/- 912,557) + +// LruCache with `lru` +// test bench_lru_cache ... bench: 40,837,052 ns/iter (+/- 747,426) diff --git a/utils/unc-cache/src/cell.rs b/utils/unc-cache/src/cell.rs new file mode 100644 index 000000000..d6f29bfe4 --- /dev/null +++ b/utils/unc-cache/src/cell.rs @@ -0,0 +1,103 @@ +use lru::LruCache; +use std::borrow::Borrow; +use std::cell::RefCell; +use std::convert::Infallible; +use std::hash::Hash; + +/// A wrapper around `LruCache` to provide shared `&` access to content. +pub struct CellLruCache { + inner: RefCell>, +} + +impl CellLruCache +where + K: Hash + Eq, + V: Clone, +{ + /// Creats a new `LRU` cache that holds at most `cap` items. + pub fn new(cap: usize) -> Self { + Self { inner: RefCell::new(LruCache::::new(cap)) } + } + + /// Returns the number of key-value pairs that are currently in the the cache. + pub fn len(&self) -> usize { + self.inner.borrow().len() + } + + /// Returns true if the cache is empty and false otherwise + pub fn is_empty(&self) -> bool { + self.inner.borrow().is_empty() + } + + /// Return the value of they key in the cache otherwise computes the value and inserts it into + /// the cache. If the key is already in the cache, they gets gets moved to the head of + /// the LRU list. + pub fn get_or_put(&self, key: K, f: F) -> V + where + V: Clone, + F: FnOnce(&K) -> V, + { + Result::<_, Infallible>::unwrap(self.get_or_try_put(key, |k| Ok(f(k)))) + } + + /// Returns the value of they key in the cache if present, otherwise + /// computes the value using the provided closure. + /// + /// If the key is already in the cache, it gets moved to the head of the LRU + /// list. + /// + /// If the provided closure fails, the error is returned and the cache is + /// not updated. + pub fn get_or_try_put(&self, key: K, f: F) -> Result + where + V: Clone, + F: FnOnce(&K) -> Result, + { + if let Some(result) = self.get(&key) { + return Ok(result); + } + let val = f(&key)?; + let val_clone = val.clone(); + self.inner.borrow_mut().put(key, val_clone); + Ok(val) + } + + /// Puts a key-value pair into cache. If the key already exists in the cache, + /// then it updates the key's value. + pub fn put(&self, key: K, value: V) { + self.inner.borrow_mut().put(key, value); + } + + pub fn pop(&self, key: &Q) -> Option + where + lru::KeyRef: Borrow, + Q: Hash + Eq + ?Sized, + { + self.inner.borrow_mut().pop(key) + } + + /// Returns the value of the key in the cache or None if it is not present in the cache. + /// Moves the key to the head of the LRU list if it exists. + pub fn get(&self, key: &Q) -> Option + where + lru::KeyRef: Borrow, + Q: Hash + Eq + ?Sized, + { + self.inner.borrow_mut().get(key).cloned() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_cache() { + let cache = CellLruCache::>::new(100); + + assert_eq!(cache.get(&0u64), None); + assert_eq!(cache.get_or_put(123u64, |key| vec![*key, 123]), vec![123u64, 123]); + assert_eq!(cache.get(&123u64), Some(vec![123u64, 123])); + assert_eq!(cache.get(&0u64), None); + } +} diff --git a/utils/unc-cache/src/lib.rs b/utils/unc-cache/src/lib.rs new file mode 100644 index 000000000..5b7060e9c --- /dev/null +++ b/utils/unc-cache/src/lib.rs @@ -0,0 +1,4 @@ +mod cell; +mod sync; + +pub use crate::{cell::CellLruCache, sync::SyncLruCache}; diff --git a/utils/unc-cache/src/sync.rs b/utils/unc-cache/src/sync.rs new file mode 100644 index 000000000..f3cb9d751 --- /dev/null +++ b/utils/unc-cache/src/sync.rs @@ -0,0 +1,91 @@ +use lru::LruCache; +use std::convert::Infallible; +use std::hash::Hash; +use std::sync::Mutex; + +/// A wrapper around `LruCache`. This struct is thread safe, doesn't return any references to any +/// elements inside. +pub struct SyncLruCache { + inner: Mutex>, +} + +impl SyncLruCache +where + K: Hash + Eq, + V: Clone, +{ + /// Creats a new `LRU` cache that holds at most `cap` items. + pub fn new(cap: usize) -> Self { + Self { inner: Mutex::new(LruCache::::new(cap)) } + } + + /// Returns the number of key-value pairs that are currently in the the cache. + pub fn len(&self) -> usize { + self.inner.lock().unwrap().len() + } + + /// Returns true if the cache is empty and false otherwise. + pub fn is_empty(&self) -> bool { + self.inner.lock().unwrap().is_empty() + } + + /// Return the value of they key in the cache otherwise computes the value and inserts it into + /// the cache. If the key is already in the cache, they gets gets moved to the head of + /// the LRU list. + pub fn get_or_put(&self, key: K, f: F) -> V + where + V: Clone, + F: FnOnce(&K) -> V, + { + Result::<_, Infallible>::unwrap(self.get_or_try_put(key, |k| Ok(f(k)))) + } + + /// Returns the value of they key in the cache if present, otherwise + /// computes the value using the provided closure. + /// + /// If the key is already in the cache, it gets moved to the head of the LRU + /// list. + /// + /// If the provided closure fails, the error is returned and the cache is + /// not updated. + pub fn get_or_try_put(&self, key: K, f: F) -> Result + where + V: Clone, + F: FnOnce(&K) -> Result, + { + if let Some(result) = self.get(&key) { + return Ok(result); + } + let val = f(&key)?; + let val_clone = val.clone(); + self.inner.lock().unwrap().put(key, val_clone); + Ok(val) + } + + /// Puts a key-value pair into cache. If the key already exists in the cache, + /// then it updates the key's value. + pub fn put(&self, key: K, value: V) { + self.inner.lock().unwrap().put(key, value); + } + + /// Returns the value of the key in the cache or None if it is not present in the cache. + /// Moves the key to the head of the LRU list if it exists. + pub fn get(&self, key: &K) -> Option { + self.inner.lock().unwrap().get(key).cloned() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_cache() { + let cache = SyncLruCache::>::new(100); + + assert_eq!(cache.get(&0u64), None); + assert_eq!(cache.get_or_put(123u64, |key| vec![*key, 123]), vec![123u64, 123]); + assert_eq!(cache.get(&123u64), Some(vec![123u64, 123])); + assert_eq!(cache.get(&0u64), None); + } +} diff --git a/utils/unc-performance-metrics-macros/Cargo.toml b/utils/unc-performance-metrics-macros/Cargo.toml new file mode 100644 index 000000000..7ac363410 --- /dev/null +++ b/utils/unc-performance-metrics-macros/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "unc-performance-metrics-macros" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +quote.workspace = true +syn.workspace = true + +[lib] +proc-macro = true diff --git a/utils/unc-performance-metrics-macros/src/lib.rs b/utils/unc-performance-metrics-macros/src/lib.rs new file mode 100644 index 000000000..c0655dbfd --- /dev/null +++ b/utils/unc-performance-metrics-macros/src/lib.rs @@ -0,0 +1,105 @@ +extern crate proc_macro; +extern crate syn; + +use proc_macro::TokenStream; +use quote::quote; + +/// Wrap the method call with unc_performance_metrics::stats::measure_performance function. +/// +/// This derive can be used to provide performance metrics to method calls with Actors. Currently +/// we print performance stats per thread every minute, and we print a warning whenever a function +/// call exceeds took more than given time limit. It should have no performance impact unless +/// `performance_stats` feature is enabled. +/// +/// This function assumes it wraps around a method with `&mut self, msg: NetworkClientMessages, +/// ctx: &mut Self::Context` as arguments. There is currently a requirement that the second +/// argument is called msg. +/// +/// # Examples +/// ```ignore +/// +/// pub enum MyMessage { +/// ExampleMessage() +/// } +/// +/// pub struct ExampleResponse {} +/// use actix::Context; +/// impl Handler for ClientActor { +/// type Result = ExampleResponse; +/// +/// #[perf] +/// fn handle(&mut self, msg: NetworkClientMessages, ctx: &mut Self::Context) -> Self::Result { +/// ExampleResponse{} +/// } +/// } +/// ``` +#[proc_macro_attribute] +pub fn perf(_attr: TokenStream, item: TokenStream) -> TokenStream { + perf_internal(_attr, item, false) +} + +/// Wrap the method call with unc_performance_metrics::stats::measure_performance_with_debug function. +/// +/// This derive can be used to provide performance metrics to method calls with Actors. Currently +/// we print performance stats per thread every minute, and we print a warning whenever a function +/// call exceeds took more than given time limit. It should have no performance impact unless +/// `performance_stats` feature is enabled. In addition to prints provided by `perf`, +/// `perf_with_debug` prints enum variant type of the message. +/// +/// This function assumes it wraps around a method with `&mut self, msg: NetworkClientMessages, +/// ctx: &mut Self::Context` as arguments. There is currently a requirement that the second +/// argument is called msg. There is an assumption that the argument called `msg` is an enum, which +/// has `#[derive(AsRefStr)]`. +/// +/// # Examples +/// ```ignore +/// #[derive(strum::AsRefStr)] +/// pub enum MyMessage { +/// ExampleMessage() +/// } +/// +/// pub struct ExampleResponse {} +/// impl Handler for ClientActor { +/// type Result = ExampleResponse; +/// +/// #[perf_with_debug] +/// fn handle(&mut self, msg: NetworkClientMessages, ctx: &mut Self::Context) -> Self::Result { +/// ExampleResponse{} +/// } +/// } +/// ``` +#[proc_macro_attribute] +pub fn perf_with_debug(_attr: TokenStream, item: TokenStream) -> TokenStream { + perf_internal(_attr, item, true) +} + +fn perf_internal(_attr: TokenStream, item: TokenStream, debug: bool) -> TokenStream { + let item: syn::Item = syn::parse(item).expect("failed to parse input"); + + if let syn::Item::Fn(mut func) = item { + let function_body = func.block; + + let new_body: TokenStream = if debug { + quote! ( + { + unc_performance_metrics::stats::measure_performance_with_debug(std::any::type_name::(), msg, move |msg| { + #function_body + }) + } + ).into() + } else { + quote! ( + { + unc_performance_metrics::stats::measure_performance(std::any::type_name::(), (), move |_| { + #function_body + }) + } + ).into() + }; + func.block = syn::parse::(new_body).expect("failed to parse input").into(); + + quote! ( #func ).into() + } else { + panic!("not a function"); + } +} diff --git a/utils/unc-performance-metrics/Cargo.toml b/utils/unc-performance-metrics/Cargo.toml new file mode 100644 index 000000000..d883dc516 --- /dev/null +++ b/utils/unc-performance-metrics/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "unc-performance-metrics" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true + +[dependencies] +actix.workspace = true +bitflags.workspace = true +bytes.workspace = true +bytesize = { workspace = true, optional = true } +futures.workspace = true +libc.workspace = true +once_cell.workspace = true +strum.workspace = true +tokio.workspace = true +tokio-util.workspace = true +tracing.workspace = true + +[features] +c_memory_stats = [] +performance_stats = ["bytesize"] diff --git a/utils/unc-performance-metrics/src/actix_disabled.rs b/utils/unc-performance-metrics/src/actix_disabled.rs new file mode 100644 index 000000000..f99b1a8df --- /dev/null +++ b/utils/unc-performance-metrics/src/actix_disabled.rs @@ -0,0 +1,17 @@ +use std::time::Duration; + +pub fn spawn(_class_name: &'static str, f: F) +where + F: futures::Future + 'static, +{ + actix::spawn(f); +} + +pub fn run_later(ctx: &mut B, dur: Duration, f: F) -> actix::SpawnHandle +where + B: actix::AsyncContext, + A: actix::Actor, + F: FnOnce(&mut A, &mut A::Context) + 'static, +{ + ctx.run_later(dur, f) +} diff --git a/utils/unc-performance-metrics/src/actix_enabled.rs b/utils/unc-performance-metrics/src/actix_enabled.rs new file mode 100644 index 000000000..93fc0027f --- /dev/null +++ b/utils/unc-performance-metrics/src/actix_enabled.rs @@ -0,0 +1,49 @@ +use crate::stats_enabled::{get_thread_stats_logger, MyFuture, REF_COUNTER, SLOW_CALL_THRESHOLD}; +use std::panic::Location; +use std::time::{Duration, Instant}; +use tracing::warn; + +#[track_caller] +pub fn spawn(class_name: &'static str, f: F) +where + F: futures::Future + 'static, +{ + let loc = Location::caller(); + *REF_COUNTER.lock().unwrap().entry((loc.file(), loc.line())).or_insert_with(|| 0) += 1; + actix::spawn(MyFuture { f, class_name, file: loc.file(), line: loc.line() }); +} + +#[track_caller] +pub fn run_later(ctx: &mut B, dur: Duration, f: F) -> actix::SpawnHandle +where + B: actix::AsyncContext, + A: actix::Actor, + F: FnOnce(&mut A, &mut A::Context) + 'static, +{ + let loc = Location::caller(); + *REF_COUNTER.lock().unwrap().entry((loc.file(), loc.line())).or_insert_with(|| 0) += 1; + + let f2 = move |a: &mut A, b: &mut A::Context| { + let stat = get_thread_stats_logger(); + let started = Instant::now(); + stat.lock().unwrap().pre_log(started); + + f(a, b); + + let ended = Instant::now(); + let took = ended.saturating_duration_since(started); + stat.lock().unwrap().log("run_later", loc.file(), loc.line(), took, ended, ""); + if took > SLOW_CALL_THRESHOLD { + warn!( + "Slow function call {}:{:?} {}:{} took: {}ms", + "run_later", + std::thread::current().id(), + loc.file(), + loc.line(), + took.as_millis() + ); + } + *REF_COUNTER.lock().unwrap().entry((loc.file(), loc.line())).or_insert_with(|| 0) -= 1; + }; + ctx.run_later(dur, f2) +} diff --git a/utils/unc-performance-metrics/src/lib.rs b/utils/unc-performance-metrics/src/lib.rs new file mode 100644 index 000000000..1aa156164 --- /dev/null +++ b/utils/unc-performance-metrics/src/lib.rs @@ -0,0 +1,16 @@ +pub mod actix_disabled; +#[cfg(feature = "performance_stats")] +pub mod actix_enabled; +pub mod process; +pub mod stats_disabled; +#[cfg(feature = "performance_stats")] +pub mod stats_enabled; + +#[cfg(not(feature = "performance_stats"))] +pub use actix_disabled as actix; +#[cfg(feature = "performance_stats")] +pub use actix_enabled as actix; +#[cfg(not(feature = "performance_stats"))] +pub use stats_disabled as stats; +#[cfg(feature = "performance_stats")] +pub use stats_enabled as stats; diff --git a/utils/unc-performance-metrics/src/process.rs b/utils/unc-performance-metrics/src/process.rs new file mode 100644 index 000000000..838530f34 --- /dev/null +++ b/utils/unc-performance-metrics/src/process.rs @@ -0,0 +1,23 @@ +use crate::stats::print_performance_stats; +use std::thread; +use std::time::Duration; +use tracing::{error, info}; + +pub fn schedule_printing_performance_stats(sleep_time: Duration) { + if cfg!(feature = "performance_stats") { + if sleep_time.is_zero() { + info!("print_performance_stats: disabled"); + return; + } + info!("print_performance_stats: enabled"); + + if let Err(err) = + thread::Builder::new().name("PerformanceMetrics".to_string()).spawn(move || loop { + print_performance_stats(sleep_time); + thread::sleep(sleep_time); + }) + { + error!("failed to spawn the thread: {}", err); + } + } +} diff --git a/utils/unc-performance-metrics/src/stats_disabled.rs b/utils/unc-performance-metrics/src/stats_disabled.rs new file mode 100644 index 000000000..31d8805aa --- /dev/null +++ b/utils/unc-performance-metrics/src/stats_disabled.rs @@ -0,0 +1,26 @@ +use std::time::Duration; + +pub fn measure_performance( + _class_name: &'static str, + msg: Message, + f: F, +) -> Result +where + F: FnOnce(Message) -> Result, +{ + f(msg) +} + +pub fn measure_performance_with_debug( + _class_name: &'static str, + msg: Message, + f: F, +) -> Result +where + F: FnOnce(Message) -> Result, + for<'a> &'a Message: Into<&'static str>, +{ + f(msg) +} + +pub fn print_performance_stats(_sleep_time: Duration) {} diff --git a/utils/unc-performance-metrics/src/stats_enabled.rs b/utils/unc-performance-metrics/src/stats_enabled.rs new file mode 100644 index 000000000..d897b61c0 --- /dev/null +++ b/utils/unc-performance-metrics/src/stats_enabled.rs @@ -0,0 +1,383 @@ +use bytesize::ByteSize; +use futures; +use futures::task::Context; +use once_cell::sync::Lazy; +use std::cmp::{max, min}; +use std::collections::{HashMap, HashSet}; +use std::pin::Pin; +use std::sync::atomic::AtomicUsize; +use std::sync::{Arc, Mutex}; +use std::task::Poll; +use std::time::{Duration, Instant}; +use tracing::{info, warn}; + +pub static NTHREADS: AtomicUsize = AtomicUsize::new(0); +pub(crate) const SLOW_CALL_THRESHOLD: Duration = Duration::from_millis(500); +const MIN_OCCUPANCY_RATIO_THRESHOLD: f64 = 0.02; + +pub(crate) static STATS: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(Stats::new()))); +pub(crate) static REF_COUNTER: Lazy>> = + Lazy::new(|| Mutex::new(HashMap::new())); + +pub fn get_thread_stats_logger() -> Arc> { + thread_local! { + static LOCAL_STATS: Arc> = { + let res = Arc::new(Mutex::new(ThreadStats::new())); + STATS.lock().unwrap().add_entry(&res); + res + } + } + + LOCAL_STATS.with(Arc::clone) +} + +#[derive(Default)] +struct Entry { + cnt: u128, + time: Duration, + max_time: Duration, +} + +pub struct ThreadStats { + stat: HashMap<(&'static str, u32, &'static str), Entry>, + cnt: u128, + time: Duration, + classes: HashSet<&'static str>, + in_progress_since: Option, + last_check: Instant, + c_mem: ByteSize, + + // Write buffer stats + write_buf_len: ByteSize, + write_buf_capacity: ByteSize, + write_buf_added: ByteSize, + write_buf_drained: ByteSize, +} + +impl ThreadStats { + fn new() -> Self { + Self { + stat: Default::default(), + cnt: Default::default(), + time: Duration::default(), + classes: Default::default(), + in_progress_since: Default::default(), + last_check: Instant::now(), + c_mem: Default::default(), + + write_buf_len: Default::default(), + write_buf_capacity: Default::default(), + write_buf_added: Default::default(), + write_buf_drained: Default::default(), + } + } + + pub fn log_add_write_buffer(&mut self, bytes: usize, buf_len: usize, buf_capacity: usize) { + self.write_buf_added += ByteSize::b(bytes as u64); + self.write_buf_len = ByteSize::b(buf_len as u64); + self.write_buf_capacity = ByteSize::b(buf_capacity as u64); + self.write_buf_capacity = ByteSize::b(buf_capacity as u64); + } + + pub fn log_drain_write_buffer(&mut self, bytes: usize, buf_len: usize, buf_capacity: usize) { + self.write_buf_drained += ByteSize::b(bytes as u64); + self.write_buf_len = ByteSize::b(buf_len as u64); + self.write_buf_capacity = ByteSize::b(buf_capacity as u64); + } + + pub fn pre_log(&mut self, now: Instant) { + self.in_progress_since = Some(now); + } + + pub fn log( + &mut self, + class_name: &'static str, + msg: &'static str, + line: u32, + took: Duration, + now: Instant, + msg_text: &'static str, + ) { + self.in_progress_since = None; + self.c_mem = get_c_memory_usage_cur_thread(); + + let took_since_last_check = min(took, now.saturating_duration_since(self.last_check)); + + let entry = self.stat.entry((msg, line, msg_text)).or_insert_with(|| Entry { + cnt: 0, + time: Duration::default(), + max_time: Duration::default(), + }); + entry.cnt += 1; + entry.time += took_since_last_check; + entry.max_time = max(took, entry.max_time); + + self.cnt += 1; + self.time += took_since_last_check; + self.classes.insert(class_name); + } + + fn print_stats_and_clear( + &mut self, + tid: std::thread::ThreadId, + sleep_time: Duration, + now: Instant, + ) -> (f64, f64) { + let mut ratio = self.time.as_nanos() as f64; + if let Some(in_progress_since) = self.in_progress_since { + let from = max(in_progress_since, self.last_check); + ratio += (now.saturating_duration_since(from)).as_nanos() as f64; + } + ratio /= sleep_time.as_nanos() as f64; + + let show_stats = ratio >= MIN_OCCUPANCY_RATIO_THRESHOLD; + + if show_stats { + let class_name = format!("{:?}", self.classes); + warn!( + " {:?}: ratio: {:.3} {}:{:?} C mem: {}", + tid, + ratio, + class_name, + std::thread::current().id(), + self.c_mem, + ); + if self.write_buf_added.as_u64() > 0 + || self.write_buf_capacity.as_u64() > 0 + || self.write_buf_added.as_u64() > 0 + || self.write_buf_drained.as_u64() > 0 + { + info!( + " Write_buffer len: {} cap: {} added: {} drained: {}", + self.write_buf_len, + self.write_buf_capacity, + self.write_buf_added, + self.write_buf_drained, + ); + } + self.write_buf_added = Default::default(); + self.write_buf_drained = Default::default(); + + let mut stat: Vec<_> = self.stat.iter().collect(); + stat.sort_by_key(|f| f.0); + + for entry in stat { + warn!( + " func {}:{}:{} cnt: {} total: {}ms max: {}ms", + (entry.0).0, + (entry.0).1, + (entry.0).2, + entry.1.cnt, + ((entry.1.time.as_millis()) as f64), + ((entry.1.max_time.as_millis()) as f64) + ); + } + } + self.last_check = now; + self.c_mem = ByteSize::b(0); + self.clear(); + + if show_stats { + (ratio, 0.0) + } else { + (ratio, ratio) + } + } + + fn clear(&mut self) { + self.time = Duration::default(); + self.cnt = 0; + self.stat.clear(); + } +} + +pub(crate) struct Stats { + stats: HashMap>>, +} + +#[cfg(all(target_os = "linux", feature = "c_memory_stats"))] +fn get_c_memory_usage_cur_thread() -> ByteSize { + // hack to get memory usage stats for c memory usage per thread + // This feature will only work if near is started with environment + // LD_PRELOAD=${PWD}/bins/unc-c-allocator-proxy.so nearup ... + // from https://github.com/near/unc-memory-tracker/blob/master/unc-dump-analyzer + unsafe { ByteSize::b(libc::malloc(usize::MAX - 1) as u64) } +} + +#[cfg(any(not(target_os = "linux"), not(feature = "c_memory_stats")))] +fn get_c_memory_usage_cur_thread() -> ByteSize { + Default::default() +} + +#[cfg(all(target_os = "linux", feature = "c_memory_stats"))] +fn get_c_memory_usage() -> ByteSize { + // hack to get memory usage stats for c memory usage + // This feature will only work if near is started with environment + // LD_PRELOAD=${PWD}/bins/unc-c-allocator-proxy.so nearup ... + // from https://github.com/near/unc-memory-tracker/blob/master/unc-dump-analyzer + unsafe { ByteSize::b(libc::malloc(usize::MAX) as u64) } +} + +#[cfg(any(not(target_os = "linux"), not(feature = "c_memory_stats")))] +fn get_c_memory_usage() -> ByteSize { + Default::default() +} + +impl Stats { + fn new() -> Self { + Self { stats: HashMap::new() } + } + + pub(crate) fn add_entry(&mut self, local_stats: &Arc>) { + let tid = std::thread::current().id(); + self.stats.entry(tid).or_insert_with(|| Arc::clone(local_stats)); + } + + fn print_stats(&mut self, sleep_time: Duration) { + info!( + "Performance stats {} threads (min ratio = {})", + self.stats.len(), + MIN_OCCUPANCY_RATIO_THRESHOLD + ); + let s: Vec<_> = self.stats.iter().collect(); + + let mut ratio = 0.0; + let mut other_ratio = 0.0; + let now = Instant::now(); + for entry in s { + let (tmp_ratio, tmp_other_ratio) = + entry.1.lock().unwrap().print_stats_and_clear(*entry.0, sleep_time, now); + ratio += tmp_ratio; + other_ratio += tmp_other_ratio; + } + info!(" Other threads ratio {:.3}", other_ratio); + let c_memory_usage = get_c_memory_usage(); + if c_memory_usage > ByteSize::default() { + info!(" C alloc total memory usage: {}", c_memory_usage); + } + info!("Total ratio = {:.3}", ratio); + } +} + +pub fn measure_performance( + class_name: &'static str, + msg: Message, + f: F, +) -> Result +where + F: FnOnce(Message) -> Result, +{ + measure_performance_internal(class_name, msg, f, None) +} + +pub fn measure_performance_with_debug( + class_name: &'static str, + msg: Message, + f: F, +) -> Result +where + F: FnOnce(Message) -> Result, + for<'a> &'a Message: Into<&'static str>, +{ + let msg_text = (&msg).into(); + measure_performance_internal(class_name, msg, f, Some(msg_text)) +} + +pub fn measure_performance_internal( + class_name: &'static str, + msg: Message, + f: F, + msg_text: Option<&'static str>, +) -> Result +where + F: FnOnce(Message) -> Result, +{ + let stat = get_thread_stats_logger(); + + let start = Instant::now(); + stat.lock().unwrap().pre_log(start); + let result = f(msg); + + let ended = Instant::now(); + let took = ended.saturating_duration_since(start); + + if took >= SLOW_CALL_THRESHOLD { + let text_field = msg_text.map(|x| format!(" msg: {}", x)).unwrap_or(format!("")); + warn!( + "Function exceeded time limit {}:{:?} {:?} took: {}ms {}", + class_name, + std::thread::current().id(), + std::any::type_name::(), + took.as_millis(), + text_field, + ); + } + stat.lock().unwrap().log( + class_name, + std::any::type_name::(), + 0, + took, + ended, + msg_text.unwrap_or_default(), + ); + result +} + +pub struct MyFuture +where + F: futures::Future + 'static, +{ + pub f: F, + pub class_name: &'static str, + pub file: &'static str, + pub line: u32, +} + +impl futures::Future for MyFuture +where + F: futures::Future + 'static, +{ + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = unsafe { self.get_unchecked_mut() }; + + let stat = get_thread_stats_logger(); + let start = Instant::now(); + stat.lock().unwrap().pre_log(start); + + let res = unsafe { Pin::new_unchecked(&mut this.f) }.poll(cx); + let ended = Instant::now(); + let took = ended.saturating_duration_since(start); + stat.lock().unwrap().log(this.class_name, this.file, this.line, took, ended, ""); + + if took > SLOW_CALL_THRESHOLD { + warn!( + "Function exceeded time limit {}:{:?} {}:{} took: {}ms", + this.class_name, + std::thread::current().id(), + this.file, + this.line, + took.as_millis() + ); + } + match res { + Poll::Ready(x) => { + *REF_COUNTER.lock().unwrap().entry((this.file, this.line)).or_insert_with(|| 0) -= + 1; + Poll::Ready(x) + } + Poll::Pending => Poll::Pending, + } + } +} + +pub fn print_performance_stats(sleep_time: Duration) { + STATS.lock().unwrap().print_stats(sleep_time); + info!("Futures waiting for completion"); + for entry in REF_COUNTER.lock().unwrap().iter() { + if *entry.1 > 0 { + info!(" future {}:{} {}", (entry.0).0, (entry.0).1, entry.1); + } + } +} diff --git a/utils/unc-stable-hasher/Cargo.toml b/utils/unc-stable-hasher/Cargo.toml new file mode 100644 index 000000000..b80350c23 --- /dev/null +++ b/utils/unc-stable-hasher/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "unc-stable-hasher" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "`unc-stable-hasher` is a library that is essentially a wrapper around, now deprecated, `std::hash::SipHasher`." +repository.workspace = true +license.workspace = true +publish = true + +[lints] +workspace = true diff --git a/utils/unc-stable-hasher/README.md b/utils/unc-stable-hasher/README.md new file mode 100644 index 000000000..25941940c --- /dev/null +++ b/utils/unc-stable-hasher/README.md @@ -0,0 +1,25 @@ +# `unc-stable-hasher` + +`unc-stable-hasher` is a library that is essentially a wrapper around, now deprecated, `std::hash::SipHasher`. +Its purpose is to provide a stable hash function, which doesn't change depending on `rust_version`, `architecture`, `platform`, +`time`, etc. + +In addition, note that `SipHasher` is deprecated since `Rust` `1.13.0`. +Eventually `SipHasher` will be removed from `Rust`. +We need to ensure, nothing breaks during this transition period. + +## Structs + +This crate provides only one struct. See `StableHasher`. + +### Example: + +```rust +fn test_stable_hasher() { + let mut sh = StableHasher::new(); + + sh.write(&[1, 2, 3, 4, 5]); + let finish = sh.finish(); + assert_eq!(finish, 12661990674860217757) +} +``` diff --git a/utils/unc-stable-hasher/src/lib.rs b/utils/unc-stable-hasher/src/lib.rs new file mode 100644 index 000000000..ad0ce3e57 --- /dev/null +++ b/utils/unc-stable-hasher/src/lib.rs @@ -0,0 +1,39 @@ +#[allow(deprecated)] +use std::hash::{Hasher, SipHasher}; + +/// We not use stable hasher as it could change with Rust releases, so rely on stable SIP hash. +#[allow(deprecated)] +#[derive(Default, Clone)] +pub struct StableHasher(pub SipHasher); + +impl StableHasher { + #[allow(deprecated)] + pub fn new() -> StableHasher { + StableHasher(SipHasher::new()) + } +} + +impl Hasher for StableHasher { + fn finish(&self) -> u64 { + self.0.finish() + } + fn write(&mut self, bytes: &[u8]) { + self.0.write(bytes) + } +} + +#[cfg(test)] +mod tests { + use crate::StableHasher; + use std::hash::Hasher; + + /// Make sure the stable hasher never changes + #[test] + fn test_stable_hasher() { + let mut sh = StableHasher::new(); + + sh.write(&[1, 2, 3, 4, 5]); + let finish = sh.finish(); + assert_eq!(finish, 12661990674860217757) + } +}